summaryrefslogtreecommitdiffstats
path: root/game/code
diff options
context:
space:
mode:
Diffstat (limited to 'game/code')
-rw-r--r--game/code/ai/actionbuttonhandler.cpp4932
-rw-r--r--game/code/ai/actionbuttonhandler.h1370
-rw-r--r--game/code/ai/actionbuttonmanager.cpp771
-rw-r--r--game/code/ai/actionbuttonmanager.h134
-rw-r--r--game/code/ai/actionlist.h86
-rw-r--r--game/code/ai/actionnames.h63
-rw-r--r--game/code/ai/actor/ActorAnimationUFO.cpp107
-rw-r--r--game/code/ai/actor/ActorAnimationUFO.h69
-rw-r--r--game/code/ai/actor/actor.cpp251
-rw-r--r--game/code/ai/actor/actor.h127
-rw-r--r--game/code/ai/actor/actoranimation.h72
-rw-r--r--game/code/ai/actor/actoranimationwasp.cpp183
-rw-r--r--game/code/ai/actor/actoranimationwasp.h97
-rw-r--r--game/code/ai/actor/actordsg.cpp585
-rw-r--r--game/code/ai/actor/actordsg.h128
-rw-r--r--game/code/ai/actor/actormanager.cpp769
-rw-r--r--game/code/ai/actor/actormanager.h144
-rw-r--r--game/code/ai/actor/allactor.cpp16
-rw-r--r--game/code/ai/actor/attackbehaviour.cpp403
-rw-r--r--game/code/ai/actor/attackbehaviour.h95
-rw-r--r--game/code/ai/actor/attractionbehaviour.cpp277
-rw-r--r--game/code/ai/actor/attractionbehaviour.h89
-rw-r--r--game/code/ai/actor/behaviour.h68
-rw-r--r--game/code/ai/actor/cutcambehaviour.cpp231
-rw-r--r--game/code/ai/actor/cutcambehaviour.h80
-rw-r--r--game/code/ai/actor/evasionbehaviour.cpp185
-rw-r--r--game/code/ai/actor/evasionbehaviour.h95
-rw-r--r--game/code/ai/actor/flyingactor.cpp747
-rw-r--r--game/code/ai/actor/flyingactor.h131
-rw-r--r--game/code/ai/actor/intersectionlist.cpp721
-rw-r--r--game/code/ai/actor/intersectionlist.h129
-rw-r--r--game/code/ai/actor/projectile.cpp327
-rw-r--r--game/code/ai/actor/projectile.h107
-rw-r--r--game/code/ai/actor/projectiledsg.cpp63
-rw-r--r--game/code/ai/actor/projectiledsg.h85
-rw-r--r--game/code/ai/actor/spawnpoint.cpp164
-rw-r--r--game/code/ai/actor/spawnpoint.h96
-rw-r--r--game/code/ai/actor/stunnedbehaviour.cpp28
-rw-r--r--game/code/ai/actor/stunnedbehaviour.h72
-rw-r--r--game/code/ai/actor/ufoattackbehaviour.cpp308
-rw-r--r--game/code/ai/actor/ufoattackbehaviour.h105
-rw-r--r--game/code/ai/actor/ufobeamalwaysonbehaviour.cpp97
-rw-r--r--game/code/ai/actor/ufobeamalwaysonbehaviour.h70
-rw-r--r--game/code/ai/actor/ufobehaviour.cpp377
-rw-r--r--game/code/ai/actor/ufobehaviour.h86
-rw-r--r--game/code/ai/allai.cpp6
-rw-r--r--game/code/ai/automaticdoor.cpp55
-rw-r--r--game/code/ai/automaticdoor.h85
-rw-r--r--game/code/ai/playanimonce.cpp161
-rw-r--r--game/code/ai/playanimonce.h81
-rw-r--r--game/code/ai/sequencer/action.cpp2557
-rw-r--r--game/code/ai/sequencer/action.h790
-rw-r--r--game/code/ai/sequencer/actioncontroller.cpp69
-rw-r--r--game/code/ai/sequencer/actioncontroller.h66
-rw-r--r--game/code/ai/sequencer/allsequencer.cpp4
-rw-r--r--game/code/ai/sequencer/sequencer.cpp315
-rw-r--r--game/code/ai/sequencer/sequencer.h115
-rw-r--r--game/code/ai/sequencer/task.cpp73
-rw-r--r--game/code/ai/sequencer/task.h57
-rw-r--r--game/code/ai/state.cpp1773
-rw-r--r--game/code/ai/state.h232
-rw-r--r--game/code/ai/statemanager.cpp107
-rw-r--r--game/code/ai/statemanager.h93
-rw-r--r--game/code/ai/vehicle/allaivehicle.cpp7
-rw-r--r--game/code/ai/vehicle/chaseai.cpp558
-rw-r--r--game/code/ai/vehicle/chaseai.h97
-rw-r--r--game/code/ai/vehicle/potentialfield.cpp374
-rw-r--r--game/code/ai/vehicle/potentialfield.h89
-rw-r--r--game/code/ai/vehicle/potentials.cpp87
-rw-r--r--game/code/ai/vehicle/potentials.h94
-rw-r--r--game/code/ai/vehicle/trafficai.cpp1591
-rw-r--r--game/code/ai/vehicle/trafficai.h264
-rw-r--r--game/code/ai/vehicle/vehicleai.cpp3123
-rw-r--r--game/code/ai/vehicle/vehicleai.h392
-rw-r--r--game/code/ai/vehicle/vehicleairender.cpp563
-rw-r--r--game/code/ai/vehicle/vehicleairender.h64
-rw-r--r--game/code/ai/vehicle/waypointai.cpp946
-rw-r--r--game/code/ai/vehicle/waypointai.h183
-rw-r--r--game/code/atc/allatc.cpp2
-rw-r--r--game/code/atc/atcloader.cpp129
-rw-r--r--game/code/atc/atcloader.h53
-rw-r--r--game/code/atc/atcmanager.cpp208
-rw-r--r--game/code/atc/atcmanager.h84
-rw-r--r--game/code/camera/allcamera.cpp23
-rw-r--r--game/code/camera/animatedcam.cpp746
-rw-r--r--game/code/camera/animatedcam.h89
-rw-r--r--game/code/camera/bumpercam.cpp293
-rw-r--r--game/code/camera/bumpercam.h172
-rw-r--r--game/code/camera/bumpercamdata.h237
-rw-r--r--game/code/camera/burnoutcam.cpp241
-rw-r--r--game/code/camera/burnoutcam.h162
-rw-r--r--game/code/camera/chasecam.cpp269
-rw-r--r--game/code/camera/chasecam.h169
-rw-r--r--game/code/camera/chasecamdata.h309
-rw-r--r--game/code/camera/conversationcam.cpp766
-rw-r--r--game/code/camera/conversationcam.h145
-rw-r--r--game/code/camera/conversationcamdata.h172
-rw-r--r--game/code/camera/debugcam.cpp246
-rw-r--r--game/code/camera/debugcam.h102
-rw-r--r--game/code/camera/firstpersoncam.cpp354
-rw-r--r--game/code/camera/firstpersoncam.h160
-rw-r--r--game/code/camera/followcam.cpp1269
-rw-r--r--game/code/camera/followcam.h342
-rw-r--r--game/code/camera/followcamdata.h662
-rw-r--r--game/code/camera/followcamdatachunk.h100
-rw-r--r--game/code/camera/frustrumdrawable.h175
-rw-r--r--game/code/camera/icamerashaker.h62
-rw-r--r--game/code/camera/isupercamtarget.h68
-rw-r--r--game/code/camera/kullcam.cpp198
-rw-r--r--game/code/camera/kullcam.h162
-rw-r--r--game/code/camera/pccam.cpp504
-rw-r--r--game/code/camera/pccam.h91
-rw-r--r--game/code/camera/railcam.cpp1260
-rw-r--r--game/code/camera/railcam.h730
-rw-r--r--game/code/camera/relativeanimatedcam.cpp204
-rw-r--r--game/code/camera/relativeanimatedcam.h66
-rw-r--r--game/code/camera/reversecam.cpp425
-rw-r--r--game/code/camera/reversecam.h202
-rw-r--r--game/code/camera/sinecosshaker.cpp226
-rw-r--r--game/code/camera/sinecosshaker.h252
-rw-r--r--game/code/camera/snapshotcam.cpp208
-rw-r--r--game/code/camera/snapshotcam.h156
-rw-r--r--game/code/camera/staticcam.cpp323
-rw-r--r--game/code/camera/staticcam.h268
-rw-r--r--game/code/camera/supercam.cpp1544
-rw-r--r--game/code/camera/supercam.h958
-rw-r--r--game/code/camera/supercamcentral.cpp2357
-rw-r--r--game/code/camera/supercamcentral.h388
-rw-r--r--game/code/camera/supercamconstants.h19
-rw-r--r--game/code/camera/supercamcontroller.cpp213
-rw-r--r--game/code/camera/supercamcontroller.h177
-rw-r--r--game/code/camera/supercammanager.cpp302
-rw-r--r--game/code/camera/supercammanager.h75
-rw-r--r--game/code/camera/supersprintcam.cpp405
-rw-r--r--game/code/camera/supersprintcam.h103
-rw-r--r--game/code/camera/surveillancecam.cpp239
-rw-r--r--game/code/camera/surveillancecam.h92
-rw-r--r--game/code/camera/trackercam.cpp224
-rw-r--r--game/code/camera/trackercam.h159
-rw-r--r--game/code/camera/trackercamdata.h168
-rw-r--r--game/code/camera/walkercam.cpp1519
-rw-r--r--game/code/camera/walkercam.h224
-rw-r--r--game/code/camera/walkercamdata.h638
-rw-r--r--game/code/camera/walkercamdatachunk.h53
-rw-r--r--game/code/camera/wrecklesscam.cpp344
-rw-r--r--game/code/camera/wrecklesscam.h162
-rw-r--r--game/code/camera/wrecklesseventlistener.cpp98
-rw-r--r--game/code/camera/wrecklesseventlistener.h91
-rw-r--r--game/code/cards/allcards.cpp5
-rw-r--r--game/code/cards/bonuscard.cpp72
-rw-r--r--game/code/cards/bonuscard.h63
-rw-r--r--game/code/cards/card.cpp98
-rw-r--r--game/code/cards/card.h130
-rw-r--r--game/code/cards/cardgallery.cpp639
-rw-r--r--game/code/cards/cardgallery.h131
-rw-r--r--game/code/cards/cards.h124
-rw-r--r--game/code/cards/cardsdb.cpp218
-rw-r--r--game/code/cards/cardsdb.h71
-rw-r--r--game/code/cards/collectorcard.cpp72
-rw-r--r--game/code/cards/collectorcard.h63
-rw-r--r--game/code/cheats/allcheats.cpp3
-rw-r--r--game/code/cheats/cheatinputhandler.cpp319
-rw-r--r--game/code/cheats/cheatinputhandler.h86
-rw-r--r--game/code/cheats/cheatinputs.h27
-rw-r--r--game/code/cheats/cheatinputsystem.cpp513
-rw-r--r--game/code/cheats/cheatinputsystem.h107
-rw-r--r--game/code/cheats/cheats.cpp435
-rw-r--r--game/code/cheats/cheats.h119
-rw-r--r--game/code/console/allconsole.cpp5
-rw-r--r--game/code/console/console.cpp2356
-rw-r--r--game/code/console/console.h291
-rw-r--r--game/code/console/debugconsolecallback.cpp380
-rw-r--r--game/code/console/debugconsolecallback.h74
-rw-r--r--game/code/console/fbstricmp.cpp124
-rw-r--r--game/code/console/fbstricmp.h56
-rw-r--r--game/code/console/nameinsensitive.cpp145
-rw-r--r--game/code/console/nameinsensitive.h57
-rw-r--r--game/code/console/upcase.cpp52
-rw-r--r--game/code/console/upcase.h38
-rw-r--r--game/code/constants/actorenum.h32
-rw-r--r--game/code/constants/blobshadownames.h76
-rw-r--r--game/code/constants/breakablesenum.h56
-rw-r--r--game/code/constants/characterenum.h24
-rw-r--r--game/code/constants/directionalarrowenum.h17
-rw-r--r--game/code/constants/maxnpccharacters.h19
-rw-r--r--game/code/constants/maxplayers.h19
-rw-r--r--game/code/constants/movienames.h102
-rw-r--r--game/code/constants/particleenum.h52
-rw-r--r--game/code/constants/physprop.h22
-rw-r--r--game/code/constants/srrchunks.h73
-rw-r--r--game/code/constants/statepropenum.h54
-rw-r--r--game/code/constants/vehicleenum.h176
-rw-r--r--game/code/contexts/allcontexts.cpp8
-rw-r--r--game/code/contexts/bootupcontext.cpp611
-rw-r--r--game/code/contexts/bootupcontext.h98
-rw-r--r--game/code/contexts/context.cpp197
-rw-r--r--game/code/contexts/context.h94
-rw-r--r--game/code/contexts/contextenum.h71
-rw-r--r--game/code/contexts/demo/alldemo.cpp2
-rw-r--r--game/code/contexts/demo/democontext.cpp563
-rw-r--r--game/code/contexts/demo/democontext.h75
-rw-r--r--game/code/contexts/demo/loadingdemocontext.cpp244
-rw-r--r--game/code/contexts/demo/loadingdemocontext.h71
-rw-r--r--game/code/contexts/entrycontext.cpp211
-rw-r--r--game/code/contexts/entrycontext.h72
-rw-r--r--game/code/contexts/exitcontext.cpp266
-rw-r--r--game/code/contexts/exitcontext.h69
-rw-r--r--game/code/contexts/frontendcontext.cpp326
-rw-r--r--game/code/contexts/frontendcontext.h75
-rw-r--r--game/code/contexts/gameplay/allgameplay.cpp2
-rw-r--r--game/code/contexts/gameplay/gameplaycontext.cpp616
-rw-r--r--game/code/contexts/gameplay/gameplaycontext.h77
-rw-r--r--game/code/contexts/gameplay/loadinggameplaycontext.cpp250
-rw-r--r--game/code/contexts/gameplay/loadinggameplaycontext.h72
-rw-r--r--game/code/contexts/loadingcontext.cpp270
-rw-r--r--game/code/contexts/loadingcontext.h66
-rw-r--r--game/code/contexts/pausecontext.cpp575
-rw-r--r--game/code/contexts/pausecontext.h77
-rw-r--r--game/code/contexts/playingcontext.cpp190
-rw-r--r--game/code/contexts/playingcontext.h59
-rw-r--r--game/code/contexts/supersprint/allsupersprintctx.cpp3
-rw-r--r--game/code/contexts/supersprint/loadingsupersprintcontext.cpp240
-rw-r--r--game/code/contexts/supersprint/loadingsupersprintcontext.h72
-rw-r--r--game/code/contexts/supersprint/supersprintcontext.cpp544
-rw-r--r--game/code/contexts/supersprint/supersprintcontext.h63
-rw-r--r--game/code/contexts/supersprint/supersprintfecontext.cpp247
-rw-r--r--game/code/contexts/supersprint/supersprintfecontext.h73
-rw-r--r--game/code/data/PersistentSectors.cpp87
-rw-r--r--game/code/data/PersistentWorldManager.cpp199
-rw-r--r--game/code/data/PersistentWorldManager.h50
-rw-r--r--game/code/data/alldata.cpp3
-rw-r--r--game/code/data/config/configstring.cpp240
-rw-r--r--game/code/data/config/configstring.h84
-rw-r--r--game/code/data/config/gameconfig.h41
-rw-r--r--game/code/data/config/gameconfigmanager.cpp206
-rw-r--r--game/code/data/config/gameconfigmanager.h85
-rw-r--r--game/code/data/gamedata.h62
-rw-r--r--game/code/data/gamedatamanager.cpp1285
-rw-r--r--game/code/data/gamedatamanager.h217
-rw-r--r--game/code/data/memcard/allmemcard.cpp1
-rw-r--r--game/code/data/memcard/memorycardmanager.cpp1333
-rw-r--r--game/code/data/memcard/memorycardmanager.h302
-rw-r--r--game/code/data/savegameinfo.cpp338
-rw-r--r--game/code/data/savegameinfo.h101
-rw-r--r--game/code/debug/alldebug.cpp3
-rw-r--r--game/code/debug/debuginfo.cpp782
-rw-r--r--game/code/debug/debuginfo.h204
-rw-r--r--game/code/debug/profiler.cpp753
-rw-r--r--game/code/debug/profiler.h206
-rw-r--r--game/code/debug/ps2perf.hpp263
-rw-r--r--game/code/debug/section.cpp236
-rw-r--r--game/code/debug/section.h108
-rw-r--r--game/code/events/allevents.cpp2
-rw-r--r--game/code/events/eventdata.h151
-rw-r--r--game/code/events/eventenum.h395
-rw-r--r--game/code/events/eventlistener.cpp77
-rw-r--r--game/code/events/eventlistener.h58
-rw-r--r--game/code/events/eventmanager.cpp365
-rw-r--r--game/code/events/eventmanager.h90
-rw-r--r--game/code/font/ADLIBN.TTFbin0 -> 51248 bytes
-rw-r--r--game/code/font/buildfont.bat9
-rw-r--r--game/code/font/defaultfont.h690
-rw-r--r--game/code/gameflow/allgameflow.cpp1
-rw-r--r--game/code/gameflow/gameflow.cpp377
-rw-r--r--game/code/gameflow/gameflow.h106
-rw-r--r--game/code/input/FEMouse.cpp338
-rw-r--r--game/code/input/FEMouse.h109
-rw-r--r--game/code/input/Gamepad.cpp567
-rw-r--r--game/code/input/Gamepad.h107
-rw-r--r--game/code/input/Keyboard.cpp215
-rw-r--r--game/code/input/Keyboard.h58
-rw-r--r--game/code/input/Mouse.cpp399
-rw-r--r--game/code/input/Mouse.h80
-rw-r--r--game/code/input/MouseCursor.cpp149
-rw-r--r--game/code/input/MouseCursor.h57
-rw-r--r--game/code/input/RealController.cpp210
-rw-r--r--game/code/input/RealController.h131
-rw-r--r--game/code/input/SteeringWheel.cpp170
-rw-r--r--game/code/input/SteeringWheel.h31
-rw-r--r--game/code/input/allinput.cpp20
-rw-r--r--game/code/input/basedamper.cpp185
-rw-r--r--game/code/input/basedamper.h59
-rw-r--r--game/code/input/button.cpp14
-rw-r--r--game/code/input/button.h71
-rw-r--r--game/code/input/constanteffect.cpp163
-rw-r--r--game/code/input/constanteffect.h59
-rw-r--r--game/code/input/controller.h100
-rw-r--r--game/code/input/forceeffect.cpp195
-rw-r--r--game/code/input/forceeffect.h104
-rw-r--r--game/code/input/inputPointList.h176
-rw-r--r--game/code/input/inputmanager.cpp731
-rw-r--r--game/code/input/inputmanager.h414
-rw-r--r--game/code/input/mappable.cpp251
-rw-r--r--game/code/input/mappable.h138
-rw-r--r--game/code/input/mapper.cpp39
-rw-r--r--game/code/input/mapper.h40
-rw-r--r--game/code/input/rumbleeffect.cpp464
-rw-r--r--game/code/input/rumbleeffect.h116
-rw-r--r--game/code/input/rumblegc.cpp49
-rw-r--r--game/code/input/rumbleps2.cpp44
-rw-r--r--game/code/input/rumblewin32.cpp68
-rw-r--r--game/code/input/rumblexbox.cpp51
-rw-r--r--game/code/input/steeringspring.cpp187
-rw-r--r--game/code/input/steeringspring.h59
-rw-r--r--game/code/input/usercontroller.cpp726
-rw-r--r--game/code/input/usercontroller.h139
-rw-r--r--game/code/input/usercontrollerWin32.cpp1443
-rw-r--r--game/code/input/usercontrollerWin32.h294
-rw-r--r--game/code/input/virtualinputs.cpp178
-rw-r--r--game/code/input/virtualinputs.hpp31
-rw-r--r--game/code/input/wheeldefines.h53
-rw-r--r--game/code/input/wheelrumble.cpp214
-rw-r--r--game/code/input/wheelrumble.h67
-rw-r--r--game/code/interiors/allinteriors.cpp1
-rw-r--r--game/code/interiors/interiormanager.cpp2409
-rw-r--r--game/code/interiors/interiormanager.h253
-rw-r--r--game/code/loading/allloadmanager.cpp15
-rw-r--r--game/code/loading/cameradataloader.cpp168
-rw-r--r--game/code/loading/cameradataloader.h77
-rw-r--r--game/code/loading/cementfilehandler.cpp147
-rw-r--r--game/code/loading/cementfilehandler.h76
-rw-r--r--game/code/loading/choreofilehandler.cpp238
-rw-r--r--game/code/loading/choreofilehandler.h85
-rw-r--r--game/code/loading/consolefilehandler.cpp237
-rw-r--r--game/code/loading/consolefilehandler.h78
-rw-r--r--game/code/loading/filehandler.h105
-rw-r--r--game/code/loading/filehandlerenum.h46
-rw-r--r--game/code/loading/filehandlerfactory.cpp173
-rw-r--r--game/code/loading/filehandlerfactory.h41
-rw-r--r--game/code/loading/iconfilehandler.cpp200
-rw-r--r--game/code/loading/iconfilehandler.h79
-rw-r--r--game/code/loading/intersectionloader.cpp157
-rw-r--r--game/code/loading/intersectionloader.h29
-rw-r--r--game/code/loading/loadingmanager.cpp648
-rw-r--r--game/code/loading/loadingmanager.h213
-rw-r--r--game/code/loading/locatorloader.cpp1118
-rw-r--r--game/code/loading/locatorloader.h85
-rw-r--r--game/code/loading/p3dfilehandler.cpp196
-rw-r--r--game/code/loading/p3dfilehandler.h81
-rw-r--r--game/code/loading/pathloader.cpp214
-rw-r--r--game/code/loading/pathloader.h70
-rw-r--r--game/code/loading/roaddatasegmentloader.cpp140
-rw-r--r--game/code/loading/roaddatasegmentloader.h53
-rw-r--r--game/code/loading/roadloader.cpp544
-rw-r--r--game/code/loading/roadloader.h87
-rw-r--r--game/code/loading/scroobyfilehandler.cpp172
-rw-r--r--game/code/loading/scroobyfilehandler.h70
-rw-r--r--game/code/loading/soundfilehandler.cpp132
-rw-r--r--game/code/loading/soundfilehandler.h66
-rw-r--r--game/code/main/allgcmain.cpp10
-rw-r--r--game/code/main/allps2main.cpp6
-rw-r--r--game/code/main/commandlineoptions.cpp403
-rw-r--r--game/code/main/commandlineoptions.h132
-rw-r--r--game/code/main/dvderrors.xlsbin0 -> 34816 bytes
-rw-r--r--game/code/main/errorsGC.h62
-rw-r--r--game/code/main/errorsPS2.h62
-rw-r--r--game/code/main/errorsWIN32.h31
-rw-r--r--game/code/main/errorsXBOX.h62
-rw-r--r--game/code/main/game.cpp709
-rw-r--r--game/code/main/game.h139
-rw-r--r--game/code/main/gamecube_extras/buildlicense.bat8
-rw-r--r--game/code/main/gamecube_extras/gcmanager.cpp711
-rw-r--r--game/code/main/gamecube_extras/gcmanager.h83
-rw-r--r--game/code/main/gamecube_extras/license.h963
-rw-r--r--game/code/main/gamecube_extras/screenshot.c345
-rw-r--r--game/code/main/gamecube_extras/screenshot.h43
-rw-r--r--game/code/main/gcmain.cpp221
-rw-r--r--game/code/main/gcplatform.cpp1758
-rw-r--r--game/code/main/gcplatform.h160
-rw-r--r--game/code/main/globaltypes.h30
-rw-r--r--game/code/main/pchsrr2.cpp15
-rw-r--r--game/code/main/pchsrr2.h23
-rw-r--r--game/code/main/platform.h104
-rw-r--r--game/code/main/ps2main.cpp295
-rw-r--r--game/code/main/ps2platform.cpp1847
-rw-r--r--game/code/main/ps2platform.h207
-rw-r--r--game/code/main/singletons.cpp333
-rw-r--r--game/code/main/singletons.h35
-rw-r--r--game/code/main/tuidunaligned.cpp266
-rw-r--r--game/code/main/tuidunaligned.h79
-rw-r--r--game/code/main/win32main.cpp237
-rw-r--r--game/code/main/win32platform.cpp2286
-rw-r--r--game/code/main/win32platform.h176
-rw-r--r--game/code/main/xboxmain.cpp236
-rw-r--r--game/code/main/xboxplatform.cpp1315
-rw-r--r--game/code/main/xboxplatform.h99
-rw-r--r--game/code/memory/allmemory.cpp7
-rw-r--r--game/code/memory/allocpool.h143
-rw-r--r--game/code/memory/classsizetracker.cpp318
-rw-r--r--game/code/memory/classsizetracker.h58
-rw-r--r--game/code/memory/createheap.cpp322
-rw-r--r--game/code/memory/createheap.h39
-rw-r--r--game/code/memory/leakdetection.cpp238
-rw-r--r--game/code/memory/leakdetection.h61
-rw-r--r--game/code/memory/map.h295
-rw-r--r--game/code/memory/memorypool.cpp341
-rw-r--r--game/code/memory/memorypool.h79
-rw-r--r--game/code/memory/memoryutilities.cpp614
-rw-r--r--game/code/memory/memoryutilities.h42
-rw-r--r--game/code/memory/propstats.cpp181
-rw-r--r--game/code/memory/propstats.h93
-rw-r--r--game/code/memory/srrmemory.cpp2083
-rw-r--r--game/code/memory/srrmemory.h314
-rw-r--r--game/code/memory/stlallocators.h106
-rw-r--r--game/code/meta/actioneventlocator.cpp335
-rw-r--r--game/code/meta/actioneventlocator.h248
-rw-r--r--game/code/meta/allmeta.cpp17
-rw-r--r--game/code/meta/carstartlocator.cpp69
-rw-r--r--game/code/meta/carstartlocator.h102
-rw-r--r--game/code/meta/directionallocator.cpp70
-rw-r--r--game/code/meta/directionallocator.h105
-rw-r--r--game/code/meta/eventlocator.cpp88
-rw-r--r--game/code/meta/eventlocator.h135
-rw-r--r--game/code/meta/fovlocator.cpp125
-rw-r--r--game/code/meta/fovlocator.h176
-rw-r--r--game/code/meta/interiorentrancelocator.cpp217
-rw-r--r--game/code/meta/interiorentrancelocator.h147
-rw-r--r--game/code/meta/locator.cpp109
-rw-r--r--game/code/meta/locator.h233
-rw-r--r--game/code/meta/locatorevents.h232
-rw-r--r--game/code/meta/locatortypes.h51
-rw-r--r--game/code/meta/occlusionlocator.cpp86
-rw-r--r--game/code/meta/occlusionlocator.h113
-rw-r--r--game/code/meta/pedgrouplocator.h91
-rw-r--r--game/code/meta/recttriggervolume.cpp461
-rw-r--r--game/code/meta/recttriggervolume.h214
-rw-r--r--game/code/meta/scriptlocator.cpp89
-rw-r--r--game/code/meta/scriptlocator.h96
-rw-r--r--game/code/meta/spheretriggervolume.cpp481
-rw-r--r--game/code/meta/spheretriggervolume.h103
-rw-r--r--game/code/meta/splinelocator.cpp260
-rw-r--r--game/code/meta/splinelocator.h168
-rw-r--r--game/code/meta/staticcamlocator.cpp300
-rw-r--r--game/code/meta/staticcamlocator.h170
-rw-r--r--game/code/meta/triggerlocator.cpp203
-rw-r--r--game/code/meta/triggerlocator.h163
-rw-r--r--game/code/meta/triggervolume.cpp284
-rw-r--r--game/code/meta/triggervolume.h359
-rw-r--r--game/code/meta/triggervolumetracker.cpp1329
-rw-r--r--game/code/meta/triggervolumetracker.h198
-rw-r--r--game/code/meta/zoneeventlocator.cpp330
-rw-r--r--game/code/meta/zoneeventlocator.h101
-rw-r--r--game/code/mission/allmission.cpp13
-rw-r--r--game/code/mission/animatedicon.cpp806
-rw-r--r--game/code/mission/animatedicon.h181
-rw-r--r--game/code/mission/bonusmissioninfo.cpp792
-rw-r--r--game/code/mission/bonusmissioninfo.h226
-rw-r--r--game/code/mission/bonusobjective.h202
-rw-r--r--game/code/mission/boss.h87
-rw-r--r--game/code/mission/charactersheet/allcharactersheet.cpp1
-rw-r--r--game/code/mission/charactersheet/charactersheet.h169
-rw-r--r--game/code/mission/charactersheet/charactersheetmanager.cpp1962
-rw-r--r--game/code/mission/charactersheet/charactersheetmanager.h239
-rw-r--r--game/code/mission/conditions/allconditions.cpp13
-rw-r--r--game/code/mission/conditions/damagecondition.cpp201
-rw-r--r--game/code/mission/conditions/damagecondition.h61
-rw-r--r--game/code/mission/conditions/followcondition.cpp248
-rw-r--r--game/code/mission/conditions/followcondition.h128
-rw-r--r--game/code/mission/conditions/getoutofcarcondition.cpp306
-rw-r--r--game/code/mission/conditions/getoutofcarcondition.h76
-rw-r--r--game/code/mission/conditions/keepbarrelcondition.cpp132
-rw-r--r--game/code/mission/conditions/keepbarrelcondition.h58
-rw-r--r--game/code/mission/conditions/leaveinteriorcondition.cpp153
-rw-r--r--game/code/mission/conditions/leaveinteriorcondition.h52
-rw-r--r--game/code/mission/conditions/missioncondition.cpp167
-rw-r--r--game/code/mission/conditions/missioncondition.h222
-rw-r--r--game/code/mission/conditions/notabductedcondition.cpp78
-rw-r--r--game/code/mission/conditions/notabductedcondition.h68
-rw-r--r--game/code/mission/conditions/outofboundscondition.cpp157
-rw-r--r--game/code/mission/conditions/outofboundscondition.h53
-rw-r--r--game/code/mission/conditions/positioncondition.cpp130
-rw-r--r--game/code/mission/conditions/positioncondition.h73
-rw-r--r--game/code/mission/conditions/racecondition.cpp181
-rw-r--r--game/code/mission/conditions/racecondition.h57
-rw-r--r--game/code/mission/conditions/timeoutcondition.cpp153
-rw-r--r--game/code/mission/conditions/timeoutcondition.h55
-rw-r--r--game/code/mission/conditions/vehiclecarryingstateprop.cpp94
-rw-r--r--game/code/mission/conditions/vehiclecarryingstateprop.h78
-rw-r--r--game/code/mission/conditions/vehiclecondition.cpp70
-rw-r--r--game/code/mission/conditions/vehiclecondition.h85
-rw-r--r--game/code/mission/gameplaymanager.cpp3360
-rw-r--r--game/code/mission/gameplaymanager.h840
-rw-r--r--game/code/mission/haspresentationinfo.cpp320
-rw-r--r--game/code/mission/haspresentationinfo.h77
-rw-r--r--game/code/mission/mission.cpp2001
-rw-r--r--game/code/mission/mission.h362
-rw-r--r--game/code/mission/missionmanager.cpp1126
-rw-r--r--game/code/mission/missionmanager.h130
-rw-r--r--game/code/mission/missionscriptloader.cpp5678
-rw-r--r--game/code/mission/missionscriptloader.h350
-rw-r--r--game/code/mission/missionstage.cpp2553
-rw-r--r--game/code/mission/missionstage.h745
-rw-r--r--game/code/mission/nocopbonusobjective.cpp174
-rw-r--r--game/code/mission/nocopbonusobjective.h77
-rw-r--r--game/code/mission/nodamagebonusobjective.cpp174
-rw-r--r--game/code/mission/nodamagebonusobjective.h78
-rw-r--r--game/code/mission/objectives/allobjectives.cpp22
-rw-r--r--game/code/mission/objectives/buycarobjective.cpp124
-rw-r--r--game/code/mission/objectives/buycarobjective.h56
-rw-r--r--game/code/mission/objectives/buyskinobjective.cpp126
-rw-r--r--game/code/mission/objectives/buyskinobjective.h56
-rw-r--r--game/code/mission/objectives/coinobjective.cpp204
-rw-r--r--game/code/mission/objectives/coinobjective.h64
-rw-r--r--game/code/mission/objectives/collectdumpedobjective.cpp781
-rw-r--r--game/code/mission/objectives/collectdumpedobjective.h109
-rw-r--r--game/code/mission/objectives/collectibleobjective.cpp824
-rw-r--r--game/code/mission/objectives/collectibleobjective.h257
-rw-r--r--game/code/mission/objectives/deliveryobjective.cpp463
-rw-r--r--game/code/mission/objectives/deliveryobjective.h78
-rw-r--r--game/code/mission/objectives/destroybossobjective.cpp105
-rw-r--r--game/code/mission/objectives/destroybossobjective.h59
-rw-r--r--game/code/mission/objectives/destroyobjective.cpp252
-rw-r--r--game/code/mission/objectives/destroyobjective.h60
-rw-r--r--game/code/mission/objectives/dialogueobjective.cpp512
-rw-r--r--game/code/mission/objectives/dialogueobjective.h94
-rw-r--r--game/code/mission/objectives/fmvobjective.cpp107
-rw-r--r--game/code/mission/objectives/fmvobjective.h62
-rw-r--r--game/code/mission/objectives/followobjective.cpp227
-rw-r--r--game/code/mission/objectives/followobjective.h61
-rw-r--r--game/code/mission/objectives/getinobjective.cpp305
-rw-r--r--game/code/mission/objectives/getinobjective.h65
-rw-r--r--game/code/mission/objectives/gooutsideobjective.cpp186
-rw-r--r--game/code/mission/objectives/gooutsideobjective.h48
-rw-r--r--game/code/mission/objectives/gotoobjective.cpp499
-rw-r--r--game/code/mission/objectives/gotoobjective.h82
-rw-r--r--game/code/mission/objectives/interiorobjective.cpp259
-rw-r--r--game/code/mission/objectives/interiorobjective.h70
-rw-r--r--game/code/mission/objectives/loadvehicleobjective.cpp141
-rw-r--r--game/code/mission/objectives/loadvehicleobjective.h102
-rw-r--r--game/code/mission/objectives/loseobjective.cpp202
-rw-r--r--game/code/mission/objectives/loseobjective.h62
-rw-r--r--game/code/mission/objectives/missionobjective.cpp1751
-rw-r--r--game/code/mission/objectives/missionobjective.h228
-rw-r--r--game/code/mission/objectives/pickupitemobjective.cpp176
-rw-r--r--game/code/mission/objectives/pickupitemobjective.h66
-rw-r--r--game/code/mission/objectives/raceobjective.cpp855
-rw-r--r--game/code/mission/objectives/raceobjective.h114
-rw-r--r--game/code/mission/objectives/talktoobjective.cpp406
-rw-r--r--game/code/mission/objectives/talktoobjective.h87
-rw-r--r--game/code/mission/objectives/timerobjective.cpp142
-rw-r--r--game/code/mission/objectives/timerobjective.h56
-rw-r--r--game/code/mission/racepositionbonusobjective.cpp179
-rw-r--r--game/code/mission/racepositionbonusobjective.h95
-rw-r--r--game/code/mission/respawnmanager/allrespawnmanager.cpp3
-rw-r--r--game/code/mission/respawnmanager/respawnentity.cpp87
-rw-r--r--game/code/mission/respawnmanager/respawnentity.h58
-rw-r--r--game/code/mission/respawnmanager/respawnmanager.cpp166
-rw-r--r--game/code/mission/respawnmanager/respawnmanager.h51
-rw-r--r--game/code/mission/rewards/allrewards.cpp3
-rw-r--r--game/code/mission/rewards/merchandise.cpp23
-rw-r--r--game/code/mission/rewards/merchandise.h59
-rw-r--r--game/code/mission/rewards/reward.cpp85
-rw-r--r--game/code/mission/rewards/reward.h125
-rw-r--r--game/code/mission/rewards/rewardsmanager.cpp748
-rw-r--r--game/code/mission/rewards/rewardsmanager.h181
-rw-r--r--game/code/mission/safezone/allsafezone.cpp1
-rw-r--r--game/code/mission/safezone/safezone.cpp90
-rw-r--r--game/code/mission/safezone/safezone.h55
-rw-r--r--game/code/mission/statepropcollectible.cpp322
-rw-r--r--game/code/mission/statepropcollectible.h122
-rw-r--r--game/code/mission/timeremainbonusobjective.cpp176
-rw-r--r--game/code/mission/timeremainbonusobjective.h95
-rw-r--r--game/code/mission/ufo/allufo.cpp3
-rw-r--r--game/code/mission/ufo/tractorbeam.cpp354
-rw-r--r--game/code/mission/ufo/tractorbeam.h101
-rw-r--r--game/code/mission/ufo/ufo.cpp338
-rw-r--r--game/code/mission/ufo/ufo.h165
-rw-r--r--game/code/mission/ufo/weapon.h88
-rw-r--r--game/code/pedpaths/allpedpaths.cpp3
-rw-r--r--game/code/pedpaths/path.cpp118
-rw-r--r--game/code/pedpaths/path.h91
-rw-r--r--game/code/pedpaths/pathmanager.cpp98
-rw-r--r--game/code/pedpaths/pathmanager.h72
-rw-r--r--game/code/pedpaths/pathsegment.cpp93
-rw-r--r--game/code/pedpaths/pathsegment.h136
-rw-r--r--game/code/presentation/allpresentation.cpp12
-rw-r--r--game/code/presentation/animplayer.cpp356
-rw-r--r--game/code/presentation/animplayer.h135
-rw-r--r--game/code/presentation/blinker.cpp243
-rw-r--r--game/code/presentation/blinker.h68
-rw-r--r--game/code/presentation/cameraplayer.cpp111
-rw-r--r--game/code/presentation/cameraplayer.h55
-rw-r--r--game/code/presentation/fmvplayer/allfmvplayergc.cpp2
-rw-r--r--game/code/presentation/fmvplayer/allfmvplayerps2.cpp2
-rw-r--r--game/code/presentation/fmvplayer/fmvplayer.cpp526
-rw-r--r--game/code/presentation/fmvplayer/fmvplayer.h108
-rw-r--r--game/code/presentation/fmvplayer/fmvuserinputhandler.cpp257
-rw-r--r--game/code/presentation/fmvplayer/fmvuserinputhandler.h91
-rw-r--r--game/code/presentation/gui/allgui.cpp13
-rw-r--r--game/code/presentation/gui/backend/allbackend.cpp4
-rw-r--r--game/code/presentation/gui/backend/guiloadingbar.h61
-rw-r--r--game/code/presentation/gui/backend/guimanagerbackend.cpp296
-rw-r--r--game/code/presentation/gui/backend/guimanagerbackend.h69
-rw-r--r--game/code/presentation/gui/backend/guiscreendemo.cpp210
-rw-r--r--game/code/presentation/gui/backend/guiscreendemo.h52
-rw-r--r--game/code/presentation/gui/backend/guiscreenloading.cpp624
-rw-r--r--game/code/presentation/gui/backend/guiscreenloading.h84
-rw-r--r--game/code/presentation/gui/backend/guiscreenloadingfe.cpp566
-rw-r--r--game/code/presentation/gui/backend/guiscreenloadingfe.h79
-rw-r--r--game/code/presentation/gui/bootup/allbootup.cpp5
-rw-r--r--game/code/presentation/gui/bootup/guimanagerbootup.cpp403
-rw-r--r--game/code/presentation/gui/bootup/guimanagerbootup.h66
-rw-r--r--game/code/presentation/gui/bootup/guimanagerlanguage.cpp287
-rw-r--r--game/code/presentation/gui/bootup/guimanagerlanguage.h60
-rw-r--r--game/code/presentation/gui/bootup/guiscreenbootupload.cpp200
-rw-r--r--game/code/presentation/gui/bootup/guiscreenbootupload.h52
-rw-r--r--game/code/presentation/gui/bootup/guiscreenlanguage.cpp224
-rw-r--r--game/code/presentation/gui/bootup/guiscreenlanguage.h55
-rw-r--r--game/code/presentation/gui/bootup/guiscreenlicense.cpp202
-rw-r--r--game/code/presentation/gui/bootup/guiscreenlicense.h48
-rw-r--r--game/code/presentation/gui/frontend/allfrontend.cpp19
-rw-r--r--game/code/presentation/gui/frontend/guimanagerfrontend.cpp829
-rw-r--r--game/code/presentation/gui/frontend/guimanagerfrontend.h82
-rw-r--r--game/code/presentation/gui/frontend/guiscreencardgallery.cpp767
-rw-r--r--game/code/presentation/gui/frontend/guiscreencardgallery.h104
-rw-r--r--game/code/presentation/gui/frontend/guiscreencontroller.cpp401
-rw-r--r--game/code/presentation/gui/frontend/guiscreencontroller.h65
-rw-r--r--game/code/presentation/gui/frontend/guiscreencontrollerWin32.cpp1200
-rw-r--r--game/code/presentation/gui/frontend/guiscreencontrollerWin32.h163
-rw-r--r--game/code/presentation/gui/frontend/guiscreencontrollerWin32old.cpp867
-rw-r--r--game/code/presentation/gui/frontend/guiscreencontrollerWin32old.h147
-rw-r--r--game/code/presentation/gui/frontend/guiscreendisplay.cpp346
-rw-r--r--game/code/presentation/gui/frontend/guiscreendisplay.h68
-rw-r--r--game/code/presentation/gui/frontend/guiscreenloadgame.cpp1034
-rw-r--r--game/code/presentation/gui/frontend/guiscreenloadgame.h92
-rw-r--r--game/code/presentation/gui/frontend/guiscreenmainmenu.cpp1649
-rw-r--r--game/code/presentation/gui/frontend/guiscreenmainmenu.h157
-rw-r--r--game/code/presentation/gui/frontend/guiscreenmissiongallery.cpp860
-rw-r--r--game/code/presentation/gui/frontend/guiscreenmissiongallery.h108
-rw-r--r--game/code/presentation/gui/frontend/guiscreenmultichoosechar.cpp349
-rw-r--r--game/code/presentation/gui/frontend/guiscreenmultichoosechar.h62
-rw-r--r--game/code/presentation/gui/frontend/guiscreenmultisetup.cpp235
-rw-r--r--game/code/presentation/gui/frontend/guiscreenmultisetup.h58
-rw-r--r--game/code/presentation/gui/frontend/guiscreenoptions.cpp609
-rw-r--r--game/code/presentation/gui/frontend/guiscreenoptions.h82
-rw-r--r--game/code/presentation/gui/frontend/guiscreenplaymovie.cpp460
-rw-r--r--game/code/presentation/gui/frontend/guiscreenplaymovie.h70
-rw-r--r--game/code/presentation/gui/frontend/guiscreenscrapbook.cpp266
-rw-r--r--game/code/presentation/gui/frontend/guiscreenscrapbook.h64
-rw-r--r--game/code/presentation/gui/frontend/guiscreenscrapbookcontents.cpp674
-rw-r--r--game/code/presentation/gui/frontend/guiscreenscrapbookcontents.h97
-rw-r--r--game/code/presentation/gui/frontend/guiscreenscrapbookstats.cpp284
-rw-r--r--game/code/presentation/gui/frontend/guiscreenscrapbookstats.h67
-rw-r--r--game/code/presentation/gui/frontend/guiscreenskingallery.cpp785
-rw-r--r--game/code/presentation/gui/frontend/guiscreenskingallery.h100
-rw-r--r--game/code/presentation/gui/frontend/guiscreensound.cpp541
-rw-r--r--game/code/presentation/gui/frontend/guiscreensound.h94
-rw-r--r--game/code/presentation/gui/frontend/guiscreensplash.cpp344
-rw-r--r--game/code/presentation/gui/frontend/guiscreensplash.h73
-rw-r--r--game/code/presentation/gui/frontend/guiscreenvehiclegallery.cpp837
-rw-r--r--game/code/presentation/gui/frontend/guiscreenvehiclegallery.h100
-rw-r--r--game/code/presentation/gui/frontend/guiscreenviewcredits.cpp503
-rw-r--r--game/code/presentation/gui/frontend/guiscreenviewcredits.h74
-rw-r--r--game/code/presentation/gui/frontend/guiscreenviewmovies.cpp287
-rw-r--r--game/code/presentation/gui/frontend/guiscreenviewmovies.h53
-rw-r--r--game/code/presentation/gui/guientity.cpp61
-rw-r--r--game/code/presentation/gui/guientity.h184
-rw-r--r--game/code/presentation/gui/guimanager.cpp650
-rw-r--r--game/code/presentation/gui/guimanager.h207
-rw-r--r--game/code/presentation/gui/guimenu.cpp1367
-rw-r--r--game/code/presentation/gui/guimenu.h251
-rw-r--r--game/code/presentation/gui/guimenuitem.cpp318
-rw-r--r--game/code/presentation/gui/guimenuitem.h164
-rw-r--r--game/code/presentation/gui/guiscreen.cpp1314
-rw-r--r--game/code/presentation/gui/guiscreen.h234
-rw-r--r--game/code/presentation/gui/guiscreenmemcardcheck.cpp518
-rw-r--r--game/code/presentation/gui/guiscreenmemcardcheck.h74
-rw-r--r--game/code/presentation/gui/guiscreenmemorycard.cpp920
-rw-r--r--game/code/presentation/gui/guiscreenmemorycard.h206
-rw-r--r--game/code/presentation/gui/guiscreenmessage.cpp629
-rw-r--r--game/code/presentation/gui/guiscreenmessage.h114
-rw-r--r--game/code/presentation/gui/guiscreenprompt.cpp498
-rw-r--r--game/code/presentation/gui/guiscreenprompt.h166
-rw-r--r--game/code/presentation/gui/guisystem.cpp1845
-rw-r--r--game/code/presentation/gui/guisystem.h320
-rw-r--r--game/code/presentation/gui/guitextbible.cpp89
-rw-r--r--game/code/presentation/gui/guitextbible.h86
-rw-r--r--game/code/presentation/gui/guiuserinputhandler.cpp938
-rw-r--r--game/code/presentation/gui/guiuserinputhandler.h162
-rw-r--r--game/code/presentation/gui/guiwindow.cpp241
-rw-r--r--game/code/presentation/gui/guiwindow.h188
-rw-r--r--game/code/presentation/gui/ingame/allingame.cpp27
-rw-r--r--game/code/presentation/gui/ingame/guihudtextbox.h41
-rw-r--r--game/code/presentation/gui/ingame/guimanageringame.cpp1646
-rw-r--r--game/code/presentation/gui/ingame/guimanageringame.h231
-rw-r--r--game/code/presentation/gui/ingame/guiscreencreditspostfmv.cpp267
-rw-r--r--game/code/presentation/gui/ingame/guiscreencreditspostfmv.h54
-rw-r--r--game/code/presentation/gui/ingame/guiscreenhastransitions.cpp213
-rw-r--r--game/code/presentation/gui/ingame/guiscreenhastransitions.h54
-rw-r--r--game/code/presentation/gui/ingame/guiscreenhud.cpp2106
-rw-r--r--game/code/presentation/gui/ingame/guiscreenhud.h271
-rw-r--r--game/code/presentation/gui/ingame/guiscreenhudmap.cpp279
-rw-r--r--game/code/presentation/gui/ingame/guiscreenhudmap.h59
-rw-r--r--game/code/presentation/gui/ingame/guiscreeniriswipe.cpp366
-rw-r--r--game/code/presentation/gui/ingame/guiscreeniriswipe.h70
-rw-r--r--game/code/presentation/gui/ingame/guiscreenletterbox.cpp688
-rw-r--r--game/code/presentation/gui/ingame/guiscreenletterbox.h101
-rw-r--r--game/code/presentation/gui/ingame/guiscreenlevelend.cpp202
-rw-r--r--game/code/presentation/gui/ingame/guiscreenlevelend.h48
-rw-r--r--game/code/presentation/gui/ingame/guiscreenlevelstats.cpp270
-rw-r--r--game/code/presentation/gui/ingame/guiscreenlevelstats.h71
-rw-r--r--game/code/presentation/gui/ingame/guiscreenmissionbase.cpp1419
-rw-r--r--game/code/presentation/gui/ingame/guiscreenmissionbase.h132
-rw-r--r--game/code/presentation/gui/ingame/guiscreenmissionload.cpp511
-rw-r--r--game/code/presentation/gui/ingame/guiscreenmissionload.h73
-rw-r--r--game/code/presentation/gui/ingame/guiscreenmissionover.cpp450
-rw-r--r--game/code/presentation/gui/ingame/guiscreenmissionover.h72
-rw-r--r--game/code/presentation/gui/ingame/guiscreenmissionselect.cpp493
-rw-r--r--game/code/presentation/gui/ingame/guiscreenmissionselect.h83
-rw-r--r--game/code/presentation/gui/ingame/guiscreenmissionsuccess.cpp211
-rw-r--r--game/code/presentation/gui/ingame/guiscreenmissionsuccess.h47
-rw-r--r--game/code/presentation/gui/ingame/guiscreenmultihud.cpp825
-rw-r--r--game/code/presentation/gui/ingame/guiscreenmultihud.h91
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpause.cpp646
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpause.h83
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpausecontroller.cpp224
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpausecontroller.h52
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpausedisplay.cpp164
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpausedisplay.h44
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpausemission.cpp366
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpausemission.h50
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpauseoptions.cpp315
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpauseoptions.h59
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpausesettings.cpp557
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpausesettings.h81
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpausesound.cpp173
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpausesound.h48
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpausesunday.cpp331
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpausesunday.h50
-rw-r--r--game/code/presentation/gui/ingame/guiscreenphonebooth.cpp1104
-rw-r--r--game/code/presentation/gui/ingame/guiscreenphonebooth.h94
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpurchaserewards.cpp1078
-rw-r--r--game/code/presentation/gui/ingame/guiscreenpurchaserewards.h87
-rw-r--r--game/code/presentation/gui/ingame/guiscreenrewards.cpp854
-rw-r--r--game/code/presentation/gui/ingame/guiscreenrewards.h171
-rw-r--r--game/code/presentation/gui/ingame/guiscreensavegame.cpp1011
-rw-r--r--game/code/presentation/gui/ingame/guiscreensavegame.h72
-rw-r--r--game/code/presentation/gui/ingame/guiscreentutorial.cpp373
-rw-r--r--game/code/presentation/gui/ingame/guiscreentutorial.h58
-rw-r--r--game/code/presentation/gui/ingame/guiscreenviewcards.cpp213
-rw-r--r--game/code/presentation/gui/ingame/guiscreenviewcards.h52
-rw-r--r--game/code/presentation/gui/ingame/hudevents/allhudevents.cpp9
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudcardcollected.cpp429
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudcardcollected.h100
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudcoincollected.cpp324
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudcoincollected.h84
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudcountdown.cpp233
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudcountdown.h69
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudeventhandler.cpp77
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudeventhandler.h69
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudhitnrun.cpp297
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudhitnrun.h81
-rw-r--r--game/code/presentation/gui/ingame/hudevents/huditemdropped.cpp217
-rw-r--r--game/code/presentation/gui/ingame/hudevents/huditemdropped.h61
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudmissionobjective.cpp255
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudmissionobjective.h84
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudmissionprogress.cpp83
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudmissionprogress.h50
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudwaspdestroyed.cpp184
-rw-r--r--game/code/presentation/gui/ingame/hudevents/hudwaspdestroyed.h72
-rw-r--r--game/code/presentation/gui/minigame/allminigame.cpp5
-rw-r--r--game/code/presentation/gui/minigame/guimanagerminigame.cpp491
-rw-r--r--game/code/presentation/gui/minigame/guimanagerminigame.h71
-rw-r--r--game/code/presentation/gui/minigame/guiscreenminihud.cpp217
-rw-r--r--game/code/presentation/gui/minigame/guiscreenminihud.h52
-rw-r--r--game/code/presentation/gui/minigame/guiscreenminimenu.cpp1633
-rw-r--r--game/code/presentation/gui/minigame/guiscreenminimenu.h210
-rw-r--r--game/code/presentation/gui/minigame/guiscreenminipause.cpp297
-rw-r--r--game/code/presentation/gui/minigame/guiscreenminipause.h66
-rw-r--r--game/code/presentation/gui/minigame/guiscreenminisummary.cpp539
-rw-r--r--game/code/presentation/gui/minigame/guiscreenminisummary.h87
-rw-r--r--game/code/presentation/gui/utility/allutility.cpp8
-rw-r--r--game/code/presentation/gui/utility/colourutility.h63
-rw-r--r--game/code/presentation/gui/utility/hudmap.cpp1914
-rw-r--r--game/code/presentation/gui/utility/hudmap.h240
-rw-r--r--game/code/presentation/gui/utility/hudmapcam.cpp113
-rw-r--r--game/code/presentation/gui/utility/hudmapcam.h40
-rw-r--r--game/code/presentation/gui/utility/numerictext.cpp27
-rw-r--r--game/code/presentation/gui/utility/numerictext.h197
-rw-r--r--game/code/presentation/gui/utility/scrollingtext.cpp280
-rw-r--r--game/code/presentation/gui/utility/scrollingtext.h110
-rw-r--r--game/code/presentation/gui/utility/slider.cpp246
-rw-r--r--game/code/presentation/gui/utility/slider.h94
-rw-r--r--game/code/presentation/gui/utility/specialfx.cpp401
-rw-r--r--game/code/presentation/gui/utility/specialfx.h166
-rw-r--r--game/code/presentation/gui/utility/teletypetext.cpp318
-rw-r--r--game/code/presentation/gui/utility/teletypetext.h120
-rw-r--r--game/code/presentation/gui/utility/transitions.cpp2911
-rw-r--r--game/code/presentation/gui/utility/transitions.h592
-rw-r--r--game/code/presentation/language.cpp181
-rw-r--r--game/code/presentation/language.h43
-rw-r--r--game/code/presentation/mouthflapper.cpp565
-rw-r--r--game/code/presentation/mouthflapper.h107
-rw-r--r--game/code/presentation/nisplayer.cpp163
-rw-r--r--game/code/presentation/nisplayer.h59
-rw-r--r--game/code/presentation/playerdrawable.cpp90
-rw-r--r--game/code/presentation/playerdrawable.h58
-rw-r--r--game/code/presentation/presentation.cpp1466
-rw-r--r--game/code/presentation/presentation.h217
-rw-r--r--game/code/presentation/presentationanimator.cpp367
-rw-r--r--game/code/presentation/presentationanimator.h70
-rw-r--r--game/code/presentation/presevents/allpresevents.cpp4
-rw-r--r--game/code/presentation/presevents/fmvevent.cpp74
-rw-r--r--game/code/presentation/presevents/fmvevent.h62
-rw-r--r--game/code/presentation/presevents/nisevent.cpp229
-rw-r--r--game/code/presentation/presevents/nisevent.h66
-rw-r--r--game/code/presentation/presevents/presentationevent.cpp180
-rw-r--r--game/code/presentation/presevents/presentationevent.h107
-rw-r--r--game/code/presentation/presevents/transevent.cpp77
-rw-r--r--game/code/presentation/presevents/transevent.h44
-rw-r--r--game/code/presentation/simpleanimationplayer.cpp300
-rw-r--r--game/code/presentation/simpleanimationplayer.h89
-rw-r--r--game/code/presentation/transitionplayer.cpp161
-rw-r--r--game/code/presentation/transitionplayer.h82
-rw-r--r--game/code/presentation/tutorialmanager.cpp745
-rw-r--r--game/code/presentation/tutorialmanager.h127
-rw-r--r--game/code/presentation/tutorialmode.h61
-rw-r--r--game/code/render/Culling/BlockCoord.h34
-rw-r--r--game/code/render/Culling/Bounds.h171
-rw-r--r--game/code/render/Culling/BoxPts.cpp370
-rw-r--r--game/code/render/Culling/BoxPts.h58
-rw-r--r--game/code/render/Culling/Cell.cpp209
-rw-r--r--game/code/render/Culling/Cell.h66
-rw-r--r--game/code/render/Culling/CellBlock.cpp317
-rw-r--r--game/code/render/Culling/CellBlock.h56
-rw-r--r--game/code/render/Culling/ContiguousBinNode.h313
-rw-r--r--game/code/render/Culling/CoordSubList.cpp640
-rw-r--r--game/code/render/Culling/CoordSubList.h96
-rw-r--r--game/code/render/Culling/CullData.cpp12
-rw-r--r--game/code/render/Culling/CullData.h46
-rw-r--r--game/code/render/Culling/FixedArray.h96
-rw-r--r--game/code/render/Culling/FloatFuncs.h40
-rw-r--r--game/code/render/Culling/HexahedronP.cpp142
-rw-r--r--game/code/render/Culling/HexahedronP.h65
-rw-r--r--game/code/render/Culling/ISpatialProxy.cpp4
-rw-r--r--game/code/render/Culling/ISpatialProxy.h191
-rw-r--r--game/code/render/Culling/Matrix3f.h20
-rw-r--r--game/code/render/Culling/NodeFLL.h98
-rw-r--r--game/code/render/Culling/OctTreeConst.h15
-rw-r--r--game/code/render/Culling/OctTreeData.cpp0
-rw-r--r--game/code/render/Culling/OctTreeData.h29
-rw-r--r--game/code/render/Culling/OctTreeNode.cpp246
-rw-r--r--game/code/render/Culling/OctTreeNode.h92
-rw-r--r--game/code/render/Culling/OctTreeParams.h31
-rw-r--r--game/code/render/Culling/Plane3f.h52
-rw-r--r--game/code/render/Culling/Point3f.h22
-rw-r--r--game/code/render/Culling/ReserveArray.h169
-rw-r--r--game/code/render/Culling/ScratchArray.h259
-rw-r--r--game/code/render/Culling/SpatialNode.h67
-rw-r--r--game/code/render/Culling/SpatialTree.cpp94
-rw-r--r--game/code/render/Culling/SpatialTree.h101
-rw-r--r--game/code/render/Culling/SpatialTreeFactory.h208
-rw-r--r--game/code/render/Culling/SpatialTreeIter.cpp645
-rw-r--r--game/code/render/Culling/SpatialTreeIter.h897
-rw-r--r--game/code/render/Culling/SphereSP.cpp252
-rw-r--r--game/code/render/Culling/SphereSP.h120
-rw-r--r--game/code/render/Culling/SwapArray.h386
-rw-r--r--game/code/render/Culling/UseArray.h119
-rw-r--r--game/code/render/Culling/Vector3f.h80
-rw-r--r--game/code/render/Culling/Vector3i.h160
-rw-r--r--game/code/render/Culling/VectorLib.cpp31
-rw-r--r--game/code/render/Culling/VectorLib.h29
-rw-r--r--game/code/render/Culling/WorldScene.cpp2380
-rw-r--r--game/code/render/Culling/WorldScene.h187
-rw-r--r--game/code/render/Culling/allculling.cpp14
-rw-r--r--game/code/render/Culling/srrRenderTypes.h24
-rw-r--r--game/code/render/DSG/DSGFactory.cpp194
-rw-r--r--game/code/render/DSG/DSGFactory.h64
-rw-r--r--game/code/render/DSG/DynaLoadListDSG.h102
-rw-r--r--game/code/render/DSG/DynaPhysDSG.cpp424
-rw-r--r--game/code/render/DSG/DynaPhysDSG.h105
-rw-r--r--game/code/render/DSG/FenceEntityDSG.cpp236
-rw-r--r--game/code/render/DSG/FenceEntityDSG.h94
-rw-r--r--game/code/render/DSG/IEntityDSG.cpp226
-rw-r--r--game/code/render/DSG/IEntityDSG.h139
-rw-r--r--game/code/render/DSG/InstAnimDynaPhysDSG.cpp557
-rw-r--r--game/code/render/DSG/InstAnimDynaPhysDSG.h163
-rw-r--r--game/code/render/DSG/InstDynaPhysDSG.cpp663
-rw-r--r--game/code/render/DSG/InstDynaPhysDSG.h124
-rw-r--r--game/code/render/DSG/InstStatEntityDSG.cpp487
-rw-r--r--game/code/render/DSG/InstStatEntityDSG.h82
-rw-r--r--game/code/render/DSG/InstStatPhysDSG.cpp391
-rw-r--r--game/code/render/DSG/InstStatPhysDSG.h84
-rw-r--r--game/code/render/DSG/IntersectDSG.cpp1000
-rw-r--r--game/code/render/DSG/IntersectDSG.h116
-rw-r--r--game/code/render/DSG/LensFlareDSG.cpp588
-rw-r--r--game/code/render/DSG/LensFlareDSG.h137
-rw-r--r--game/code/render/DSG/StatePropDSG.cpp1118
-rw-r--r--game/code/render/DSG/StatePropDSG.h211
-rw-r--r--game/code/render/DSG/StaticEntityDSG.cpp414
-rw-r--r--game/code/render/DSG/StaticEntityDSG.h94
-rw-r--r--game/code/render/DSG/StaticPhysDSG.cpp594
-rw-r--r--game/code/render/DSG/StaticPhysDSG.h96
-rw-r--r--game/code/render/DSG/TriStripDSG.cpp352
-rw-r--r--game/code/render/DSG/TriStripDSG.h146
-rw-r--r--game/code/render/DSG/WorldSphereDSG.cpp487
-rw-r--r--game/code/render/DSG/WorldSphereDSG.h89
-rw-r--r--game/code/render/DSG/alldsg.cpp18
-rw-r--r--game/code/render/DSG/animcollisionentitydsg.cpp595
-rw-r--r--game/code/render/DSG/animcollisionentitydsg.h183
-rw-r--r--game/code/render/DSG/animentitydsg.cpp570
-rw-r--r--game/code/render/DSG/animentitydsg.h186
-rw-r--r--game/code/render/DSG/breakableobjectdsg.cpp243
-rw-r--r--game/code/render/DSG/breakableobjectdsg.h94
-rw-r--r--game/code/render/DSG/collisionentitydsg.cpp609
-rw-r--r--game/code/render/DSG/collisionentitydsg.h166
-rw-r--r--game/code/render/Enums/RenderEnums.h142
-rw-r--r--game/code/render/IntersectManager/IntersectManager.cpp1895
-rw-r--r--game/code/render/IntersectManager/IntersectManager.h202
-rw-r--r--game/code/render/IntersectManager/allintersect.cpp1
-rw-r--r--game/code/render/Loaders/AllWrappers.cpp447
-rw-r--r--game/code/render/Loaders/AllWrappers.h102
-rw-r--r--game/code/render/Loaders/AnimCollLoader.cpp278
-rw-r--r--game/code/render/Loaders/AnimCollLoader.h71
-rw-r--r--game/code/render/Loaders/AnimDSGLoader.cpp355
-rw-r--r--game/code/render/Loaders/AnimDSGLoader.h89
-rw-r--r--game/code/render/Loaders/AnimDynaPhysLoader.cpp1074
-rw-r--r--game/code/render/Loaders/AnimDynaPhysLoader.h200
-rw-r--r--game/code/render/Loaders/BillboardWrappedLoader.cpp211
-rw-r--r--game/code/render/Loaders/BillboardWrappedLoader.h66
-rw-r--r--game/code/render/Loaders/ChunkListenerCallback.h44
-rw-r--r--game/code/render/Loaders/DynaPhysLoader.cpp438
-rw-r--r--game/code/render/Loaders/DynaPhysLoader.h64
-rw-r--r--game/code/render/Loaders/FenceLoader.cpp205
-rw-r--r--game/code/render/Loaders/FenceLoader.h61
-rw-r--r--game/code/render/Loaders/GeometryWrappedLoader.cpp158
-rw-r--r--game/code/render/Loaders/GeometryWrappedLoader.h62
-rw-r--r--game/code/render/Loaders/IWrappedLoader.h49
-rw-r--r--game/code/render/Loaders/InstStatEntityLoader.cpp244
-rw-r--r--game/code/render/Loaders/InstStatEntityLoader.h61
-rw-r--r--game/code/render/Loaders/InstStatPhysLoader.cpp391
-rw-r--r--game/code/render/Loaders/InstStatPhysLoader.h63
-rw-r--r--game/code/render/Loaders/IntersectLoader.cpp286
-rw-r--r--game/code/render/Loaders/IntersectLoader.h59
-rw-r--r--game/code/render/Loaders/LensFlareLoader.cpp291
-rw-r--r--game/code/render/Loaders/LensFlareLoader.h68
-rw-r--r--game/code/render/Loaders/StaticEntityLoader.cpp191
-rw-r--r--game/code/render/Loaders/StaticEntityLoader.h61
-rw-r--r--game/code/render/Loaders/StaticPhysLoader.cpp256
-rw-r--r--game/code/render/Loaders/StaticPhysLoader.h62
-rw-r--r--game/code/render/Loaders/TreeDSGLoader.cpp219
-rw-r--r--game/code/render/Loaders/TreeDSGLoader.h58
-rw-r--r--game/code/render/Loaders/WorldSphereLoader.cpp309
-rw-r--r--game/code/render/Loaders/WorldSphereLoader.h76
-rw-r--r--game/code/render/Loaders/allloaders.cpp18
-rw-r--r--game/code/render/Loaders/breakableobjectloader.cpp323
-rw-r--r--game/code/render/Loaders/breakableobjectloader.h104
-rw-r--r--game/code/render/Loaders/instparticlesystemloader.cpp229
-rw-r--r--game/code/render/Loaders/instparticlesystemloader.h84
-rw-r--r--game/code/render/Particles/allparticles.cpp3
-rw-r--r--game/code/render/Particles/particlemanager.cpp822
-rw-r--r--game/code/render/Particles/particlemanager.h204
-rw-r--r--game/code/render/Particles/particlesystemdsg.cpp356
-rw-r--r--game/code/render/Particles/particlesystemdsg.h99
-rw-r--r--game/code/render/Particles/vehicleparticleemitter.cpp306
-rw-r--r--game/code/render/Particles/vehicleparticleemitter.h130
-rw-r--r--game/code/render/RenderFlow/allrenderflow.cpp1
-rw-r--r--game/code/render/RenderFlow/renderflow.cpp384
-rw-r--r--game/code/render/RenderFlow/renderflow.h106
-rw-r--r--game/code/render/RenderManager/FrontEndRenderLayer.cpp319
-rw-r--r--game/code/render/RenderManager/FrontEndRenderLayer.h71
-rw-r--r--game/code/render/RenderManager/RenderLayer.cpp988
-rw-r--r--game/code/render/RenderManager/RenderLayer.h182
-rw-r--r--game/code/render/RenderManager/RenderManager.cpp2060
-rw-r--r--game/code/render/RenderManager/RenderManager.h228
-rw-r--r--game/code/render/RenderManager/WorldRenderLayer.cpp2080
-rw-r--r--game/code/render/RenderManager/WorldRenderLayer.h132
-rw-r--r--game/code/render/RenderManager/allrendermanager.cpp4
-rw-r--r--game/code/render/animentitydsgmanager/allanimentitydsgmanager.cpp1
-rw-r--r--game/code/render/animentitydsgmanager/animentitydsgmanager.cpp563
-rw-r--r--game/code/render/animentitydsgmanager/animentitydsgmanager.h120
-rw-r--r--game/code/render/breakables/allbreakables.cpp1
-rw-r--r--game/code/render/breakables/breakablesmanager.cpp649
-rw-r--r--game/code/render/breakables/breakablesmanager.h165
-rw-r--r--game/code/roads/allroads.cpp10
-rw-r--r--game/code/roads/geometry.cpp1024
-rw-r--r--game/code/roads/geometry.h448
-rw-r--r--game/code/roads/intersection.cpp1374
-rw-r--r--game/code/roads/intersection.h312
-rw-r--r--game/code/roads/lane.cpp187
-rw-r--r--game/code/roads/lane.h187
-rw-r--r--game/code/roads/road.cpp830
-rw-r--r--game/code/roads/road.h249
-rw-r--r--game/code/roads/roadmanager.cpp2565
-rw-r--r--game/code/roads/roadmanager.h318
-rw-r--r--game/code/roads/roadrender.cpp401
-rw-r--r--game/code/roads/roadrendertest.cpp271
-rw-r--r--game/code/roads/roadrendertest.h60
-rw-r--r--game/code/roads/roadsegment.cpp792
-rw-r--r--game/code/roads/roadsegment.h267
-rw-r--r--game/code/roads/roadsegmentdata.cpp211
-rw-r--r--game/code/roads/roadsegmentdata.h61
-rw-r--r--game/code/roads/trafficcontrol.cpp98
-rw-r--r--game/code/roads/trafficcontrol.h106
-rw-r--r--game/code/sound/allsound.cpp7
-rw-r--r--game/code/sound/avatar/allsoundavatar.cpp5
-rw-r--r--game/code/sound/avatar/avatarsoundplayer.cpp187
-rw-r--r--game/code/sound/avatar/avatarsoundplayer.h63
-rw-r--r--game/code/sound/avatar/carsoundparameters.cpp1184
-rw-r--r--game/code/sound/avatar/carsoundparameters.h302
-rw-r--r--game/code/sound/avatar/icarsoundparameters.h168
-rw-r--r--game/code/sound/avatar/soundavatar.cpp245
-rw-r--r--game/code/sound/avatar/soundavatar.h78
-rw-r--r--game/code/sound/avatar/vehiclesounddebugpage.cpp138
-rw-r--r--game/code/sound/avatar/vehiclesounddebugpage.h75
-rw-r--r--game/code/sound/avatar/vehiclesoundplayer.cpp2499
-rw-r--r--game/code/sound/avatar/vehiclesoundplayer.h162
-rw-r--r--game/code/sound/dialog/alldialog.cpp11
-rw-r--r--game/code/sound/dialog/conversation.cpp408
-rw-r--r--game/code/sound/dialog/conversation.h79
-rw-r--r--game/code/sound/dialog/conversationmatcher.cpp237
-rw-r--r--game/code/sound/dialog/conversationmatcher.h57
-rw-r--r--game/code/sound/dialog/dialogcoordinator.cpp971
-rw-r--r--game/code/sound/dialog/dialogcoordinator.h84
-rw-r--r--game/code/sound/dialog/dialogline.cpp1018
-rw-r--r--game/code/sound/dialog/dialogline.h172
-rw-r--r--game/code/sound/dialog/dialoglist.cpp1259
-rw-r--r--game/code/sound/dialog/dialoglist.h109
-rw-r--r--game/code/sound/dialog/dialogpriorityqueue.cpp536
-rw-r--r--game/code/sound/dialog/dialogpriorityqueue.h99
-rw-r--r--game/code/sound/dialog/dialogqueueelement.cpp951
-rw-r--r--game/code/sound/dialog/dialogqueueelement.h158
-rw-r--r--game/code/sound/dialog/dialogqueuetype.h24
-rw-r--r--game/code/sound/dialog/dialogselectiongroup.cpp376
-rw-r--r--game/code/sound/dialog/dialogselectiongroup.h91
-rw-r--r--game/code/sound/dialog/dialogsounddebugpage.cpp243
-rw-r--r--game/code/sound/dialog/dialogsounddebugpage.h89
-rw-r--r--game/code/sound/dialog/playabledialog.cpp87
-rw-r--r--game/code/sound/dialog/playabledialog.h48
-rw-r--r--game/code/sound/dialog/selectabledialog.cpp214
-rw-r--r--game/code/sound/dialog/selectabledialog.h118
-rw-r--r--game/code/sound/dialog/selectabledialoglist.h24
-rw-r--r--game/code/sound/listener.cpp236
-rw-r--r--game/code/sound/listener.h56
-rw-r--r--game/code/sound/movingpositional/actorplayer.cpp194
-rw-r--r--game/code/sound/movingpositional/actorplayer.h77
-rw-r--r--game/code/sound/movingpositional/aivehiclesoundplayer.cpp99
-rw-r--r--game/code/sound/movingpositional/aivehiclesoundplayer.h53
-rw-r--r--game/code/sound/movingpositional/allmovingposn.cpp9
-rw-r--r--game/code/sound/movingpositional/animobjsoundplayer.cpp202
-rw-r--r--game/code/sound/movingpositional/animobjsoundplayer.h70
-rw-r--r--game/code/sound/movingpositional/avatarvehicleposnplayer.cpp208
-rw-r--r--game/code/sound/movingpositional/avatarvehicleposnplayer.h58
-rw-r--r--game/code/sound/movingpositional/movingsoundmanager.cpp747
-rw-r--r--game/code/sound/movingpositional/movingsoundmanager.h108
-rw-r--r--game/code/sound/movingpositional/platformsoundplayer.cpp209
-rw-r--r--game/code/sound/movingpositional/platformsoundplayer.h74
-rw-r--r--game/code/sound/movingpositional/trafficsoundplayer.cpp458
-rw-r--r--game/code/sound/movingpositional/trafficsoundplayer.h83
-rw-r--r--game/code/sound/movingpositional/vehicleposnsoundplayer.cpp258
-rw-r--r--game/code/sound/movingpositional/vehicleposnsoundplayer.h81
-rw-r--r--game/code/sound/movingpositional/waspsoundplayer.cpp260
-rw-r--r--game/code/sound/movingpositional/waspsoundplayer.h71
-rw-r--r--game/code/sound/music/allmusic.cpp1
-rw-r--r--game/code/sound/music/musicplayer.cpp2284
-rw-r--r--game/code/sound/music/musicplayer.h344
-rw-r--r--game/code/sound/nis/allnissound.cpp1
-rw-r--r--game/code/sound/nis/nissoundplayer.cpp388
-rw-r--r--game/code/sound/nis/nissoundplayer.h115
-rw-r--r--game/code/sound/nisenum.h41
-rw-r--r--game/code/sound/positionalsoundplayer.cpp283
-rw-r--r--game/code/sound/positionalsoundplayer.h109
-rw-r--r--game/code/sound/simpsonssoundplayer.cpp539
-rw-r--r--game/code/sound/simpsonssoundplayer.h136
-rw-r--r--game/code/sound/soundcluster.cpp276
-rw-r--r--game/code/sound/soundcluster.h103
-rw-r--r--game/code/sound/soundclusternameenum.h51
-rw-r--r--game/code/sound/soundcollisiondata.h58
-rw-r--r--game/code/sound/sounddebug/allsounddebug.cpp2
-rw-r--r--game/code/sound/sounddebug/sounddebugdisplay.cpp438
-rw-r--r--game/code/sound/sounddebug/sounddebugdisplay.h86
-rw-r--r--game/code/sound/sounddebug/sounddebugpage.cpp161
-rw-r--r--game/code/sound/sounddebug/sounddebugpage.h67
-rw-r--r--game/code/sound/soundfx/allsoundfx.cpp14
-rw-r--r--game/code/sound/soundfx/gcreverbcontroller.cpp108
-rw-r--r--game/code/sound/soundfx/gcreverbcontroller.h54
-rw-r--r--game/code/sound/soundfx/ipositionalsoundsettings.h59
-rw-r--r--game/code/sound/soundfx/ireverbsettings.h73
-rw-r--r--game/code/sound/soundfx/positionalsoundsettings.cpp180
-rw-r--r--game/code/sound/soundfx/positionalsoundsettings.h80
-rw-r--r--game/code/sound/soundfx/ps2reverbcontroller.cpp124
-rw-r--r--game/code/sound/soundfx/ps2reverbcontroller.h54
-rw-r--r--game/code/sound/soundfx/reverbcontroller.cpp410
-rw-r--r--game/code/sound/soundfx/reverbcontroller.h83
-rw-r--r--game/code/sound/soundfx/reverbsettings.cpp133
-rw-r--r--game/code/sound/soundfx/reverbsettings.h158
-rw-r--r--game/code/sound/soundfx/soundeffectplayer.cpp249
-rw-r--r--game/code/sound/soundfx/soundeffectplayer.h122
-rw-r--r--game/code/sound/soundfx/soundfxfrontendlogic.cpp137
-rw-r--r--game/code/sound/soundfx/soundfxfrontendlogic.h50
-rw-r--r--game/code/sound/soundfx/soundfxgameplaylogic.cpp1212
-rw-r--r--game/code/sound/soundfx/soundfxgameplaylogic.h130
-rw-r--r--game/code/sound/soundfx/soundfxlogic.cpp322
-rw-r--r--game/code/sound/soundfx/soundfxlogic.h91
-rw-r--r--game/code/sound/soundfx/soundfxpauselogic.cpp98
-rw-r--r--game/code/sound/soundfx/soundfxpauselogic.h50
-rw-r--r--game/code/sound/soundfx/win32reverbcontroller.cpp127
-rw-r--r--game/code/sound/soundfx/win32reverbcontroller.h54
-rw-r--r--game/code/sound/soundfx/xboxreverbcontroller.cpp127
-rw-r--r--game/code/sound/soundfx/xboxreverbcontroller.h54
-rw-r--r--game/code/sound/soundloader.cpp601
-rw-r--r--game/code/sound/soundloader.h98
-rw-r--r--game/code/sound/soundmanager.cpp2193
-rw-r--r--game/code/sound/soundmanager.h377
-rw-r--r--game/code/sound/soundrenderer/allsoundrenderer.cpp14
-rw-r--r--game/code/sound/soundrenderer/dasoundgroup.h100
-rw-r--r--game/code/sound/soundrenderer/dasoundplayer.cpp1191
-rw-r--r--game/code/sound/soundrenderer/fader.cpp483
-rw-r--r--game/code/sound/soundrenderer/fader.h146
-rw-r--r--game/code/sound/soundrenderer/idasoundresource.h172
-rw-r--r--game/code/sound/soundrenderer/idasoundtuner.h205
-rw-r--r--game/code/sound/soundrenderer/musicsoundplayer.cpp300
-rw-r--r--game/code/sound/soundrenderer/musicsoundplayer.h112
-rw-r--r--game/code/sound/soundrenderer/playermanager.cpp989
-rw-r--r--game/code/sound/soundrenderer/playermanager.h185
-rw-r--r--game/code/sound/soundrenderer/soundallocatedresource.cpp429
-rw-r--r--game/code/sound/soundrenderer/soundallocatedresource.h249
-rw-r--r--game/code/sound/soundrenderer/soundconstants.h0
-rw-r--r--game/code/sound/soundrenderer/sounddynaload.cpp954
-rw-r--r--game/code/sound/soundrenderer/sounddynaload.h199
-rw-r--r--game/code/sound/soundrenderer/soundnucleus.cpp594
-rw-r--r--game/code/sound/soundrenderer/soundnucleus.hpp70
-rw-r--r--game/code/sound/soundrenderer/soundplayer.h468
-rw-r--r--game/code/sound/soundrenderer/soundrenderingmanager.cpp1545
-rw-r--r--game/code/sound/soundrenderer/soundrenderingmanager.h187
-rw-r--r--game/code/sound/soundrenderer/soundresource.cpp555
-rw-r--r--game/code/sound/soundrenderer/soundresource.h152
-rw-r--r--game/code/sound/soundrenderer/soundresourcemanager.cpp527
-rw-r--r--game/code/sound/soundrenderer/soundresourcemanager.h164
-rw-r--r--game/code/sound/soundrenderer/soundsystem.h118
-rw-r--r--game/code/sound/soundrenderer/soundtuner.cpp1241
-rw-r--r--game/code/sound/soundrenderer/soundtuner.h253
-rw-r--r--game/code/sound/soundrenderer/tunerdebugpage.cpp231
-rw-r--r--game/code/sound/soundrenderer/tunerdebugpage.h68
-rw-r--r--game/code/sound/soundrenderer/wireplayers.cpp1
-rw-r--r--game/code/sound/soundrenderer/wiresystem.cpp104
-rw-r--r--game/code/sound/soundrenderercallback.cpp181
-rw-r--r--game/code/sound/soundrenderercallback.h70
-rw-r--r--game/code/sound/tuning/allsoundtuning.cpp1
-rw-r--r--game/code/sound/tuning/globalsettings.cpp467
-rw-r--r--game/code/sound/tuning/globalsettings.h224
-rw-r--r--game/code/sound/tuning/iglobalsettings.h128
-rw-r--r--game/code/stateprop/allstateprop.cpp2
-rw-r--r--game/code/stateprop/stateprop.cpp361
-rw-r--r--game/code/stateprop/stateprop.hpp94
-rw-r--r--game/code/stateprop/statepropdata.cpp251
-rw-r--r--game/code/stateprop/statepropdata.hpp99
-rw-r--r--game/code/stateprop/statepropdatatypes.hpp50
-rw-r--r--game/code/supersprint/allsupersprint.cpp3
-rw-r--r--game/code/supersprint/supersprintdata.cpp94
-rw-r--r--game/code/supersprint/supersprintdata.h151
-rw-r--r--game/code/supersprint/supersprintdrawable.h524
-rw-r--r--game/code/supersprint/supersprintmanager.cpp2668
-rw-r--r--game/code/supersprint/supersprintmanager.h383
-rw-r--r--game/code/worldsim/allworldsim.cpp9
-rw-r--r--game/code/worldsim/avatar.cpp1101
-rw-r--r--game/code/worldsim/avatar.h158
-rw-r--r--game/code/worldsim/avatarmanager.cpp511
-rw-r--r--game/code/worldsim/avatarmanager.h87
-rw-r--r--game/code/worldsim/character/aicharactercontroller.cpp112
-rw-r--r--game/code/worldsim/character/aicharactercontroller.h27
-rw-r--r--game/code/worldsim/character/allcharacter.cpp7
-rw-r--r--game/code/worldsim/character/character.cpp5171
-rw-r--r--game/code/worldsim/character/character.h1428
-rw-r--r--game/code/worldsim/character/charactercontroller.cpp1668
-rw-r--r--game/code/worldsim/character/charactercontroller.h308
-rw-r--r--game/code/worldsim/character/charactermanager.cpp2488
-rw-r--r--game/code/worldsim/character/charactermanager.h240
-rw-r--r--game/code/worldsim/character/charactermappable.cpp281
-rw-r--r--game/code/worldsim/character/charactermappable.h126
-rw-r--r--game/code/worldsim/character/characterrenderable.cpp581
-rw-r--r--game/code/worldsim/character/characterrenderable.h100
-rw-r--r--game/code/worldsim/character/charactertarget.cpp312
-rw-r--r--game/code/worldsim/character/charactertarget.h72
-rw-r--r--game/code/worldsim/character/controllereventhandler.h54
-rw-r--r--game/code/worldsim/character/footprint/allfootprint.cpp1
-rw-r--r--game/code/worldsim/character/footprint/footprint.cpp49
-rw-r--r--game/code/worldsim/character/footprint/footprint.h73
-rw-r--r--game/code/worldsim/character/footprint/footprintmanager.cpp252
-rw-r--r--game/code/worldsim/character/footprint/footprintmanager.h105
-rw-r--r--game/code/worldsim/coins/allcoins.cpp2
-rw-r--r--game/code/worldsim/coins/coinmanager.cpp1132
-rw-r--r--game/code/worldsim/coins/coinmanager.h168
-rw-r--r--game/code/worldsim/coins/sparkle.cpp1222
-rw-r--r--game/code/worldsim/coins/sparkle.h174
-rw-r--r--game/code/worldsim/groundplanepool.cpp326
-rw-r--r--game/code/worldsim/groundplanepool.h71
-rw-r--r--game/code/worldsim/harass/allharass.cpp1
-rw-r--r--game/code/worldsim/harass/chasemanager.cpp911
-rw-r--r--game/code/worldsim/harass/chasemanager.h188
-rw-r--r--game/code/worldsim/hitnrunmanager.cpp1118
-rw-r--r--game/code/worldsim/hitnrunmanager.h150
-rw-r--r--game/code/worldsim/huskpool.cpp309
-rw-r--r--game/code/worldsim/huskpool.h56
-rw-r--r--game/code/worldsim/parkedcars/allparkedcars.cpp1
-rw-r--r--game/code/worldsim/parkedcars/parkedcarmanager.cpp943
-rw-r--r--game/code/worldsim/parkedcars/parkedcarmanager.h127
-rw-r--r--game/code/worldsim/ped/allped.cpp2
-rw-r--r--game/code/worldsim/ped/pedestrian.cpp448
-rw-r--r--game/code/worldsim/ped/pedestrian.h128
-rw-r--r--game/code/worldsim/ped/pedestrianmanager.cpp1593
-rw-r--r--game/code/worldsim/ped/pedestrianmanager.h241
-rw-r--r--game/code/worldsim/physicsairef.h51
-rw-r--r--game/code/worldsim/redbrick/allredbrick.cpp13
-rw-r--r--game/code/worldsim/redbrick/geometryvehicle.cpp3384
-rw-r--r--game/code/worldsim/redbrick/geometryvehicle.h322
-rw-r--r--game/code/worldsim/redbrick/physicslocomotion.cpp2069
-rw-r--r--game/code/worldsim/redbrick/physicslocomotion.h135
-rw-r--r--game/code/worldsim/redbrick/physicslocomotioncontrollerforces.cpp1686
-rw-r--r--game/code/worldsim/redbrick/redbrickcollisionsolveragent.cpp927
-rw-r--r--game/code/worldsim/redbrick/redbrickcollisionsolveragent.h57
-rw-r--r--game/code/worldsim/redbrick/rootmatrixdriver.cpp29
-rw-r--r--game/code/worldsim/redbrick/rootmatrixdriver.h77
-rw-r--r--game/code/worldsim/redbrick/suspensionjointdriver.cpp130
-rw-r--r--game/code/worldsim/redbrick/suspensionjointdriver.h71
-rw-r--r--game/code/worldsim/redbrick/trafficbodydrawable.cpp82
-rw-r--r--game/code/worldsim/redbrick/trafficbodydrawable.h39
-rw-r--r--game/code/worldsim/redbrick/trafficlocomotion.cpp1421
-rw-r--r--game/code/worldsim/redbrick/trafficlocomotion.h270
-rw-r--r--game/code/worldsim/redbrick/vehicle.cpp6483
-rw-r--r--game/code/worldsim/redbrick/vehicle.h989
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.cpp167
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.h40
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/allvehiclecontroller.cpp4
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.cpp807
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h78
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.cpp16
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.h49
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.cpp263
-rw-r--r--game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.h60
-rw-r--r--game/code/worldsim/redbrick/vehicleeventlistener.cpp304
-rw-r--r--game/code/worldsim/redbrick/vehicleeventlistener.h45
-rw-r--r--game/code/worldsim/redbrick/vehicleinit.cpp2309
-rw-r--r--game/code/worldsim/redbrick/vehiclelocomotion.cpp46
-rw-r--r--game/code/worldsim/redbrick/vehiclelocomotion.h41
-rw-r--r--game/code/worldsim/redbrick/wheel.cpp533
-rw-r--r--game/code/worldsim/redbrick/wheel.h107
-rw-r--r--game/code/worldsim/skidmarks/SkidMarkGenerator.cpp272
-rw-r--r--game/code/worldsim/skidmarks/SkidMarkGenerator.h187
-rw-r--r--game/code/worldsim/skidmarks/allskidmarks.cpp3
-rw-r--r--game/code/worldsim/skidmarks/skidmark.cpp442
-rw-r--r--game/code/worldsim/skidmarks/skidmark.h85
-rw-r--r--game/code/worldsim/skidmarks/skidmarkmanager.cpp178
-rw-r--r--game/code/worldsim/skidmarks/skidmarkmanager.h94
-rw-r--r--game/code/worldsim/spawn/allspawn.cpp1
-rw-r--r--game/code/worldsim/spawn/spawnmanager.cpp107
-rw-r--r--game/code/worldsim/spawn/spawnmanager.h154
-rw-r--r--game/code/worldsim/traffic/alltraffic.cpp1
-rw-r--r--game/code/worldsim/traffic/trafficmanager.cpp2169
-rw-r--r--game/code/worldsim/traffic/trafficmanager.h312
-rw-r--r--game/code/worldsim/traffic/trafficmodelgroup.h145
-rw-r--r--game/code/worldsim/traffic/trafficvehicle.h135
-rw-r--r--game/code/worldsim/vehiclecentral.cpp2278
-rw-r--r--game/code/worldsim/vehiclecentral.h205
-rw-r--r--game/code/worldsim/worldcollisionsolveragent.cpp365
-rw-r--r--game/code/worldsim/worldcollisionsolveragent.h80
-rw-r--r--game/code/worldsim/worldobject.cpp125
-rw-r--r--game/code/worldsim/worldobject.h60
-rw-r--r--game/code/worldsim/worldphysicsmanager.cpp2921
-rw-r--r--game/code/worldsim/worldphysicsmanager.h296
1260 files changed, 393574 insertions, 0 deletions
diff --git a/game/code/ai/actionbuttonhandler.cpp b/game/code/ai/actionbuttonhandler.cpp
new file mode 100644
index 0000000..c03abef
--- /dev/null
+++ b/game/code/ai/actionbuttonhandler.cpp
@@ -0,0 +1,4932 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implementation of class ActionButtonHandler
+//
+// History: 08/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+#include <poser/joint.hpp>
+#include <poser/poseengine.hpp>
+#include <poser/pose.hpp>
+
+#include <p3d/anim/pose.hpp>
+#include <p3d/anim/animate.hpp>
+#include <p3d/anim/poseanimation.hpp>
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/matrixstack.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <ai/actionbuttonhandler.h>
+#include <ai/actionbuttonmanager.h>
+#include <presentation/presentation.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactercontroller.h>
+#include <ai/statemanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/character/charactermanager.h>
+#include <events/eventmanager.h>
+#include <events/eventenum.h>
+#include <sound/soundmanager.h>
+
+#include <memory/srrmemory.h>
+#include <meta/eventlocator.h>
+#include <meta/actioneventlocator.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/triggervolumetracker.h>
+#include <meta/interiorentrancelocator.h>
+#include <meta/carstartlocator.h>
+#include <worldsim/parkedcars/parkedcarmanager.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/hitnrunmanager.h>
+
+#include <ai/sequencer/action.h>
+#include <ai/sequencer/actioncontroller.h>
+#include <ai/sequencer/sequencer.h>
+#include <ai/actionbuttonhandler.h>
+#ifdef ACTIONEVENTHANDLER_DEBUG
+#include <ai/actionnames.h>
+#endif // ACTIONEVENTHANDLER_DEBUG
+#include <cards/cardgallery.h>
+#include <cards/cardsdb.h>
+#include <cards/card.h>
+#include <camera/supercam.h>
+
+#include <render/DSG/animcollisionentitydsg.h>
+#include <render/DSG/InstDynaPhysDSG.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/RenderLayer.h>
+#include <render/particles/particlemanager.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiwindow.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreeniriswipe.h>
+#include <presentation/gui/ingame/guiscreenletterbox.h>
+
+#include <gameflow/gameflow.h>
+#include <mission/gameplaymanager.h>
+
+#include <mission/animatedicon.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/gameplaymanager.h>
+#include <mission/rewards/rewardsmanager.h>
+
+#include <render/loaders/allwrappers.h>
+#include <render/loaders/BillboardWrappedLoader.h>
+
+#include <interiors/interiormanager.h>
+#include <render/DSG/InstAnimDynaPhysDSG.h>
+
+#include <input/inputmanager.h>
+#ifdef RAD_WIN32
+#include <input/usercontrollerWin32.h>
+#else
+#include <input/usercontroller.h>
+#endif
+
+#include <contexts/contextenum.h>
+
+#include <camera/relativeanimatedcam.h>
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercam.h>
+
+#include <contexts/context.h>
+#include <render/DSG/StatePropDSG.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+static const tUID GIL = tEntity::MakeUID( "gil" );
+
+//
+// Oh yeah. Big pre-final hack.
+//
+struct CarPurchaseDialogueStuff
+{
+ radKey32 convName;
+ radInt64 sellerName;
+};
+static CarPurchaseDialogueStuff s_carPurchaseConvNames[] =
+{
+ { ::radMakeKey32( "plowking" ), tEntity::MakeUID( "reward_barney" ) },
+ { ::radMakeKey32( "son" ), tEntity::MakeUID( "reward_homer" ) },
+ { ::radMakeKey32( "bus" ), tEntity::MakeUID( "reward_otto" ) },
+ { ::radMakeKey32( "tractor" ), tEntity::MakeUID( "reward_willie" ) },
+ { ::radMakeKey32( "borrowing" ), tEntity::MakeUID( "reward_homer" ) },
+ { ::radMakeKey32( "swine" ), tEntity::MakeUID( "reward_kearney" ) },
+ { 0, tEntity::MakeUID( "zombie" ) } // No conversation for zombies
+};
+
+struct AnimSwitchSoundData
+{
+ radKey32 objKey;
+ const char* soundName;
+ const char* positionalSettings;
+ bool isMovingSound;
+};
+
+static const AnimSwitchSoundData ANIM_SWITCH_SOUND_TABLE[] =
+{
+ { ::radMakeKey32( "TD" ), "trapdoor", "platform_settings", false },
+ { ::radMakeKey32( "Splatform1Trans" ), "platform_02", "platform_settings", true },
+ { ::radMakeKey32( "Splatform4Trans" ), "platform_01", "platform_settings", true },
+ { ::radMakeKey32( "Splatform5Trans" ), "platform_01", "platform_settings", true },
+ { ::radMakeKey32( "Splatform6Trans" ), "platform_01", "platform_settings", true },
+ { ::radMakeKey32( "Aplatform6Trans" ), "aplatform6", "platform_settings", true },
+ { ::radMakeKey32( "Splatfrom7Trans" ), "platform_01", "platform_settings", true }, // (sic)
+ { ::radMakeKey32( "truck1" ), "duff_truck", "platform_settings", false },
+ { ::radMakeKey32( "hdoortrans1" ), "plantdoor", "platform_settings", false },
+ { ::radMakeKey32( "elevator" ), "elevator", "loud_elevator_settings", true },
+ { ::radMakeKey32( "library" ), "gag_library", "platform_settings", false }
+};
+
+static unsigned int ANIM_SWITCH_SOUND_TABLE_SIZE =
+ sizeof( ANIM_SWITCH_SOUND_TABLE ) / sizeof( AnimSwitchSoundData );
+
+namespace ActionButton
+{
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+/*
+==============================================================================
+AnimCollisionEntityDSGWrapper::AnimCollisionEntityDSGWrapper
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: AnimCollisionEntityDSGWrapper
+
+=============================================================================
+*/
+AnimCollisionEntityDSGWrapper::AnimCollisionEntityDSGWrapper( void )
+:
+mpGameObject( 0 )
+{
+}
+/*
+==============================================================================
+AnimCollisionEntityDSGWrapper::~AnimCollisionEntityDSGWrapper
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: AnimCollisionEntityDSGWrapper
+
+=============================================================================
+*/
+AnimCollisionEntityDSGWrapper::~AnimCollisionEntityDSGWrapper( void )
+{
+ // Do not release. See comment in AnimCollisionEntityDSGWrapper::SetGameObject().
+ //
+ // TBJ [8/14/2002]
+ //
+ //tRefCounted::Release( mpGameObject );
+ mpGameObject = 0;
+}
+/*
+==============================================================================
+AnimCollisionEntityDSGWrapper::UpdateVisibility
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void AnimCollisionEntityDSGWrapper::UpdateVisibility( void )
+{
+ mpGameObject->UpdateVisibility();
+}
+/*
+==============================================================================
+AnimCollisionEntityDSGWrapper::SetGameObject
+==============================================================================
+Description: Comment
+
+Parameters: ( AnimCollisionEntityDSG* pGameObject )
+
+Return: void
+
+=============================================================================
+*/
+void AnimCollisionEntityDSGWrapper::SetGameObject( AnimCollisionEntityDSG* pGameObject )
+{
+ // Do NOT addref. The "AnimCollisionEntityDSG" is dynamically loaded.
+ // If the dynaload system wants to release this object, we must let it go.
+ // We will assume that the 'AnimCollisionEntityDSG' will notify the AnimSwitch
+ // when it is deleted.
+ //
+ // TBJ [8/14/2002]
+ //
+ //tRefCounted::Assign( mpGameObject, pGameObject );
+ mpGameObject = pGameObject;
+}
+
+/*
+==============================================================================
+AnimCollisionEntityDSGWrapper::GetAnimationDirection
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+float& AnimCollisionEntityDSGWrapper::GetAnimationDirection( void )
+{
+ return mpGameObject->GetAnimationDirection();
+}
+/*
+==============================================================================
+AnimCollisionEntityDSGWrapper::SetAnimationDirection
+==============================================================================
+Description: Comment
+
+Parameters: ( float fDirection )
+
+Return: void
+
+=============================================================================
+*/
+void AnimCollisionEntityDSGWrapper::SetAnimationDirection( float fDirection )
+{
+ mpGameObject->SetAnimationDirection( fDirection );
+}
+/*
+==============================================================================
+AnimCollisionEntityDSGWrapper::GetAnimController
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: tMultiController
+
+=============================================================================
+*/
+tMultiController* AnimCollisionEntityDSGWrapper::GetAnimController( void ) const
+{
+ return mpGameObject->GetAnimController();
+}
+
+/*
+==============================================================================
+AnimCollisionEntityDSGWrapper::GetDrawable
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: tCompositeDrawable
+
+=============================================================================
+*/
+tCompositeDrawable* AnimCollisionEntityDSGWrapper::GetDrawable( void ) const
+{
+ return mpGameObject->GetDrawable();
+}
+/*
+==============================================================================
+AnimCollisionEntityDSGWrapper::Display
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void AnimCollisionEntityDSGWrapper::Display( void )
+{
+ rAssertMsg( false, "This object should not be drawn.\n" );
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+/*
+==============================================================================
+AnimEntityDSGWrapper::AnimEntityDSGWrapper
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: AnimEntityDSGWrapper
+
+=============================================================================
+*/
+AnimEntityDSGWrapper::AnimEntityDSGWrapper( void )
+:
+mpDrawable( 0 ),
+mpPose( 0 ),
+mpAnimController( 0 ),
+mbVisible( true )
+{
+}
+/*
+==============================================================================
+AnimEntityDSGWrapper::~AnimEntityDSGWrapper
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: AnimEntityDSGWrapper
+
+=============================================================================
+*/
+AnimEntityDSGWrapper::~AnimEntityDSGWrapper( void )
+{
+ tRefCounted::Release( mpDrawable );
+ tRefCounted::Release( mpPose );
+ tRefCounted::Release( mpAnimController );
+}
+/*
+==============================================================================
+AnimEntityDSGWrapper::UpdateVisibility
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void AnimEntityDSGWrapper::UpdateVisibility( void )
+{
+ // No physics here, so do nothing.
+ //
+}
+/*
+==============================================================================
+AnimEntityDSGWrapper::SetDrawable
+==============================================================================
+Description: Comment
+
+Parameters: ( tCompositeDrawable* pDrawable )
+
+Return: void
+
+=============================================================================
+*/
+void AnimEntityDSGWrapper::SetDrawable( tCompositeDrawable* pDrawable )
+{
+ tRefCounted::Assign( mpDrawable, pDrawable );
+}
+/*
+==============================================================================
+AnimEntityDSGWrapper::SetPose
+==============================================================================
+Description: Comment
+
+Parameters: ( tPose* pPose )
+
+Return: void
+
+=============================================================================
+*/
+void AnimEntityDSGWrapper::SetPose( tPose* pPose )
+{
+ tRefCounted::Assign( mpPose, pPose );
+}
+/*
+==============================================================================
+AnimEntityDSGWrapper::SetAnimController
+==============================================================================
+Description: Comment
+
+Parameters: ( tMultiController* pAnimController )
+
+Return: void
+
+=============================================================================
+*/
+void AnimEntityDSGWrapper::SetAnimController( tMultiController* pAnimController )
+{
+ tRefCounted::Assign( mpAnimController, pAnimController );
+}
+/*
+==============================================================================
+AnimEntityDSGWrapper::SetTransform
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Matrix& transform )
+
+Return: void
+
+=============================================================================
+*/
+void AnimEntityDSGWrapper::SetTransform( rmt::Matrix& transform )
+{
+ mTransform = transform;
+}
+/*
+==============================================================================
+AnimEntityDSGWrapper::GetAnimationDirection
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+float& AnimEntityDSGWrapper::GetAnimationDirection( void )
+{
+ return mfDirection;
+}
+/*
+==============================================================================
+AnimEntityDSGWrapper::SetAnimationDirection
+==============================================================================
+Description: Comment
+
+Parameters: ( float fDirection )
+
+Return: void
+
+=============================================================================
+*/
+void AnimEntityDSGWrapper::SetAnimationDirection( float fDirection )
+{
+ mfDirection = fDirection;
+}
+/*
+==============================================================================
+AnimEntityDSGWrapper::GetAnimController
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: tMultiController
+
+=============================================================================
+*/
+tMultiController* AnimEntityDSGWrapper::GetAnimController( void ) const
+{
+ // Why did I need to do this?
+ //
+ //mpAnimController->SetPose( mpPose );
+ return mpAnimController;
+}
+
+/*
+==============================================================================
+AnimEntityDSGWrapper::GetDrawable
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: tCompositeDrawable
+
+=============================================================================
+*/
+tCompositeDrawable* AnimEntityDSGWrapper::GetDrawable( void ) const
+{
+ return mpDrawable;
+}
+/*
+==============================================================================
+AnimEntityDSGWrapper::Display
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void AnimEntityDSGWrapper::Display( void )
+{
+ if ( IsVisible() )
+ {
+ p3d::stack->PushMultiply( mTransform );
+ {
+ mpDrawable->SetPose( mpPose );
+ mpDrawable->Display();
+ }
+ p3d::stack->Pop( );
+ }
+}
+
+//==============================================================================
+// ActionButtonHandler::ActionButtonHandler
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ButtonHandler::ButtonHandler( void )
+:
+mActionButton( CharacterController::DoAction )
+{
+}
+
+//==============================================================================
+// ActionButtonHandler::~ActionButtonHandler
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ButtonHandler::~ButtonHandler()
+{
+}
+
+/*
+==============================================================================
+ButtonHandler::ButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: bool
+
+=============================================================================
+*/
+bool ButtonHandler::ButtonPressed( Character* pCharacter )
+{
+ if ( IsActionButtonPressed( pCharacter ) )
+ {
+ pCharacter->GetActionController()->Clear();
+ Sequencer* pSeq = pCharacter->GetActionController()->GetNextSequencer();
+ if ( OnButtonPressed( pCharacter, pSeq ) )
+ {
+ if ( UsesActionButton() )
+ {
+// GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ return true;
+ }
+ }
+ return false;
+}
+/*
+==============================================================================
+ButtonHandler::Enter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void ButtonHandler::Enter( Character* pCharacter )
+{
+ OnEnter( pCharacter );
+}
+/*
+==============================================================================
+ButtonHandler::Exit
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void ButtonHandler::Exit( Character* pCharacter )
+{
+ OnExit( pCharacter );
+}
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+/*
+==============================================================================
+ButtonHandler::IsActionButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: bool
+
+=============================================================================
+*/
+bool ButtonHandler::IsActionButtonPressed( Character* pCharacter )
+{
+ CharacterController::eIntention theIntention = pCharacter->GetController()->GetIntention();
+ return ( theIntention == mActionButton );
+}
+
+/*
+==============================================================================
+PropHandler::PropHandler
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: PropHandler
+
+=============================================================================
+*/
+PropHandler::PropHandler( void )
+:
+mpProp( 0 )
+{
+}
+/*
+==============================================================================
+PropHandler::~PropHandler
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: PropHandler
+
+=============================================================================
+*/
+PropHandler::~PropHandler( void )
+{
+ tRefCounted::Release( mpProp );
+}
+/*
+==============================================================================
+PropHandler::SetProp
+==============================================================================
+Description: Comment
+
+Parameters: ( InstDynaPhysDSG* pProp )
+
+Return: void
+
+=============================================================================
+*/
+void PropHandler::SetProp( InstDynaPhysDSG* pProp )
+{
+ tRefCounted::Assign( mpProp, pProp );
+}
+/*
+==============================================================================
+PropHandler::GetProp
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: InstDynaPhysDSG
+
+=============================================================================
+*/
+InstDynaPhysDSG* PropHandler::GetProp( void ) const
+{
+ return mpProp;
+}
+/*
+==============================================================================
+AttachProp::OnButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, Sequencer* pSeq )
+
+Return: bool
+
+=============================================================================
+*/
+bool AttachProp::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ rAssert( GetProp( ) != 0 );
+ pCharacter->AttachProp( GetProp( ) );
+ return true;
+}
+/*
+==============================================================================
+EnterInterior::EnterInterior
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: EnterInterior
+
+=============================================================================
+*/
+EnterInterior::EnterInterior( InteriorEntranceLocator* pLocator )
+:
+mpLocator( 0 )
+{
+ SetLocator (pLocator);
+}
+/*
+==============================================================================
+EnterInterior::~EnterInterior
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: EnterInterior
+
+=============================================================================
+*/
+EnterInterior::~EnterInterior()
+{
+}
+
+
+void EnterInterior::SetLocator ( InteriorEntranceLocator* pLocator )
+{
+ mpLocator = pLocator;
+}
+
+/*
+==============================================================================
+EnterInterior::OnButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+bool EnterInterior::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ if(GetGameFlow()->GetNextContext() != CONTEXT_PAUSE)
+ {
+ GetInteriorManager()->Enter(mpLocator, pCharacter, pSeq);
+ }
+ return true;
+}
+/*
+==============================================================================
+GetInCar::GetInCar
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: GetInCar
+
+=============================================================================
+*/
+GetInCar::GetInCar( EventLocator* pEventLocator )
+:
+mpEventLocator( 0 ),
+mCharacter( NULL )
+{
+ SetEventLocator( pEventLocator );
+}
+/*
+==============================================================================
+GetInCar::~GetInCar
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: GetInCar
+
+=============================================================================
+*/
+GetInCar::~GetInCar()
+{
+ if ( mpEventLocator )
+ {
+ mpEventLocator->Release( );
+ mpEventLocator = 0;
+ }
+
+ if ( mCharacter )
+ {
+ mCharacter->Release();
+ mCharacter = NULL;
+ }
+}
+/*
+==============================================================================
+GetInCar::SetEventLocator
+==============================================================================
+Description: Comment
+
+Parameters: ( EventLocator* pEventLocator )
+
+Return: void
+
+=============================================================================
+*/
+void GetInCar::SetEventLocator( EventLocator* pEventLocator )
+{
+ tRefCounted::Assign( mpEventLocator, pEventLocator );
+}
+/*
+==============================================================================
+GetInCar::OnButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+bool GetInCar::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ CharacterController::eIntention theIntention = pCharacter->GetController()->GetIntention();
+
+ Vehicle* pVehicle = GetVehicleCentral()->GetVehicle( mVehicleId );
+ rAssert( pVehicle );
+
+ tRefCounted::Assign( mCharacter, pCharacter );
+
+ //
+ // Trigger an event for tutorial mode
+ //
+ pCharacter->SetTargetVehicle( pVehicle );
+
+ rAssertMsg( pVehicle->mVehicleType != VT_AI,
+ "Trying to get into AI car?? Don't! We shouldn't have even allowed "
+ "reaching this point!" );
+
+ if( pVehicle->mVehicleType == VT_TRAFFIC)
+ {
+ GetEventManager()->TriggerEvent( EVENT_ENTERING_TRAFFIC_CAR, (void*)pVehicle );
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_ENTERING_PLAYER_CAR, (void*)pVehicle );
+ }
+
+ pCharacter->GetStateManager()->SetState<CharacterAi::GetIn>();
+
+ return true;
+}
+
+/*
+==============================================================================
+ActionEventHandler::ActionEventHandler
+==============================================================================
+Description: Comment
+
+Parameters: ( ActionEventLocator* pActionEventLocator )
+
+Return: ActionEventHandler
+
+=============================================================================
+*/
+ActionEventHandler::ActionEventHandler( ActionEventLocator* pActionEventLocator )
+:
+mpActionEventLocator( 0 ),
+mIsEnabled( true )
+{
+ SetActionEventLocator( pActionEventLocator );
+}
+/*
+==============================================================================
+ActionEventHandler::~ActionEventHandler
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ActionEventHandler
+
+=============================================================================
+*/
+ActionEventHandler::~ActionEventHandler( void )
+{
+ tRefCounted::Release( mpActionEventLocator );
+ mpActionEventLocator = 0;
+}
+#ifdef ACTIONEVENTHANDLER_DEBUG
+void ActionEventHandler::Enter( Character* pCharacter )
+{
+ if ( mpActionEventLocator )
+ {
+ rReleasePrintf( "Object:\t%s\nJoint:\t%s\nAction:\t%s\nButton:\t%s\nShouldTransform:\t%d\n",
+ mpActionEventLocator->GetObjName(),
+ mpActionEventLocator->GetJointName(),
+ mpActionEventLocator->GetActionName(),
+ ButtonName[ mpActionEventLocator->GetButtonInput() ],
+ mpActionEventLocator->GetShouldTransform() ? 1 : 0 );
+ }
+ ButtonHandler::Enter( pCharacter );
+}
+#endif //ACTIONEVENTHANDLER_DEBUG
+/*
+==============================================================================
+ActionEventHandler::SetActionEventLocator
+==============================================================================
+Description: Comment
+
+Parameters: ( ActionEventLocator* pActionEventLocator )
+
+Return: void
+
+=============================================================================
+*/
+void ActionEventHandler::SetActionEventLocator( ActionEventLocator* pActionEventLocator )
+{
+ tRefCounted::Assign( mpActionEventLocator, pActionEventLocator );
+}
+/*
+==============================================================================
+AnimSwitch::AnimSwitch
+==============================================================================
+Description: Comment
+
+Parameters: ( ActionEventLocator* pActionEventLocator )
+
+Return: AnimSwitch
+
+=============================================================================
+*/
+AnimSwitch::AnimSwitch( ActionEventLocator* pActionEventLocator )
+:
+ActionEventHandler( pActionEventLocator ),
+mpJoint( 0 ),
+mpGameObject( 0 ),
+mbAttachToJoint( false )
+{
+ unsigned int i;
+ const char* objName = pActionEventLocator->GetObjName();
+ radKey32 objNameKey;
+
+ mSoundName = NULL;
+
+ if( objName != NULL )
+ {
+ //
+ // Trapdoor hack! This is ugly, but not quite as ugly as listing
+ // each trapdoor separately in the table, I think.
+ //
+ if( ( objName[0] == 'T' ) && ( objName[1] == 'D' ) )
+ {
+ mSoundName = ANIM_SWITCH_SOUND_TABLE[0].soundName;
+ mIsMovingSound = ANIM_SWITCH_SOUND_TABLE[0].isMovingSound;
+ mSettingsName = ANIM_SWITCH_SOUND_TABLE[0].positionalSettings;
+ }
+ else
+ {
+ objNameKey = ::radMakeKey32( objName );
+
+ //
+ // Set the sound key
+ //
+ for( i = 0; i < ANIM_SWITCH_SOUND_TABLE_SIZE; i++ )
+ {
+ if( objNameKey == ANIM_SWITCH_SOUND_TABLE[i].objKey )
+ {
+ mSoundName = ANIM_SWITCH_SOUND_TABLE[i].soundName;
+ mIsMovingSound = ANIM_SWITCH_SOUND_TABLE[i].isMovingSound;
+ mSettingsName = ANIM_SWITCH_SOUND_TABLE[i].positionalSettings;
+ break;
+ }
+ }
+ }
+ }
+}
+/*
+==============================================================================
+AnimSwitch::~AnimSwitch
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: AnimSwitch
+
+=============================================================================
+*/
+AnimSwitch::~AnimSwitch( void )
+{
+ mpJoint = 0;
+ tRefCounted::Release( mpGameObject );
+}
+
+/*
+==============================================================================
+AnimSwitch::Create
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+bool AnimSwitch::Create( tEntityStore* inStore )
+{
+ AnimCollisionEntityDSG* pAnimCollisionEntityDSG = p3d::find<AnimCollisionEntityDSG>( inStore, mpActionEventLocator->GetObjName() );
+ bool bCreated = false;
+ if ( pAnimCollisionEntityDSG )
+ {
+ tPose* p3dPose = pAnimCollisionEntityDSG->GetPoseEngine()->GetP3DPose( );
+ rAssert( p3dPose );
+
+ // Evaluate the pose to get the proper worldmatrix for the joints.
+ //
+ p3dPose->Evaluate();
+ int jointIndex = p3dPose->FindJointIndex( mpActionEventLocator->GetJointName() );
+
+ tPose::Joint* pJoint = pAnimCollisionEntityDSG->GetPoseJoint( jointIndex, mpActionEventLocator->GetShouldTransform() );
+
+ ActionButton::AnimCollisionEntityDSGWrapper* pGameObject = new(GMA_LEVEL_OTHER) ActionButton::AnimCollisionEntityDSGWrapper;
+ pGameObject->SetGameObject( pAnimCollisionEntityDSG );
+ // Set the start state of all animations requiring a trigger to off.
+ //
+ // This will stop the animation until Action executes.
+ //
+ pGameObject->SetAnimationDirection( 0.0f );
+ // Reset the animation to the initial frame.
+ //
+ pGameObject->GetAnimController( )->SetFrame( 0.0f );
+
+ // The AnimCollisionEntityDSG must store a pointer to the action handler
+ // so it can callback when it is dumped by the dynaloader.
+ //
+ // TBJ [8/14/2002]
+ //
+ pAnimCollisionEntityDSG->SetAction( this );
+ Init( pGameObject, pJoint, mpActionEventLocator->GetShouldTransform() );
+ bCreated = true;
+ }
+ return bCreated;
+}
+//=============================================================================
+// AnimSwitch::OnReset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void )
+//
+// Return: void
+//
+//=============================================================================
+void AnimSwitch::OnReset( void )
+{
+ // Set the start state of all animations requiring a trigger to off.
+ //
+ // This will stop the animation until Action executes.
+ //
+ mpGameObject->SetAnimationDirection( 0.0f );
+ // Reset the animation to the initial frame.
+ //
+ mpGameObject->GetAnimController( )->SetFrame( 0.0f );
+ mpGameObject->GetAnimController( )->Advance( 0.0f );
+ mpGameObject->UpdateVisibility( );
+}
+/*
+==============================================================================
+AnimSwitch::Init
+==============================================================================
+Description: Comment
+
+Parameters: ( AnimCollisionEntityDSG* pAnimCollisionEntityDSG, tPose::Joint* pJoint, bool bAttachToJoint )
+
+Return: void
+
+=============================================================================
+*/
+void AnimSwitch::Init( IGameObjectWrapper* pGameObject, tPose::Joint* pJoint, bool bAttachToJoint )
+{
+ mbAttachToJoint = bAttachToJoint;
+ mpJoint = pJoint;
+ rAssert( mpJoint );
+
+ tRefCounted::Assign( mpGameObject, pGameObject );
+
+ ActionEventLocator* pActionEventLocator = GetActionEventLocator();
+ rAssert( pActionEventLocator );
+ pActionEventLocator->GetLocation( &mStandPosition );
+ if ( mbAttachToJoint )
+ {
+ // If attach to joint, then transform the eventlocator into
+ // the object local space. Store it in mStandPosition.
+ // Then we will transform mStandPosition back to
+ // world space when we need the position.
+ //
+ rmt::Matrix invMat = mpJoint->worldMatrix;
+ invMat.InvertOrtho( );
+ mStandPosition.Transform( invMat );
+ }
+ OnInit( );
+}
+/*
+==============================================================================
+AnimSwitch::GetAnimationDirection
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+float& AnimSwitch::GetAnimationDirection( void )
+{
+ return mpGameObject->GetAnimationDirection();
+}
+/*
+==============================================================================
+AnimSwitch::SetAnimationDirection
+==============================================================================
+Description: Comment
+
+Parameters: ( float fDirection )
+
+Return: void
+
+=============================================================================
+*/
+void AnimSwitch::SetAnimationDirection( float fDirection )
+{
+ mpGameObject->SetAnimationDirection( fDirection );
+}
+/*
+==============================================================================
+AnimSwitch::GetAnimController
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: tMultiController
+
+=============================================================================
+*/
+tMultiController* AnimSwitch::GetAnimController( void ) const
+{
+ return mpGameObject->GetAnimController();
+}
+/*
+==============================================================================
+AnimSwitch::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void AnimSwitch::Update( float timeins )
+{
+ if ( mbAttachToJoint )
+ {
+ rAssert( mpJoint );
+ ActionEventLocator* pActionEventLocator = GetActionEventLocator( );
+ rAssert( pActionEventLocator );
+
+ rmt::Vector pos = mpJoint->worldMatrix.Row( 3 );
+
+ pActionEventLocator->GetTriggerVolume( 0 )->SetPosition( pos );
+ }
+ OnUpdate( timeins );
+}
+/*
+==============================================================================
+AnimSwitch::ReleaseGameObject
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void AnimSwitch::Destroy( void )
+{
+}
+/*
+==============================================================================
+AnimSwitch::ButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+bool AnimSwitch::ButtonPressed( Character* pCharacter )
+{
+ CharacterController::eIntention theIntention = pCharacter->GetController()->GetIntention();
+
+ if ( IsActionButtonPressed( pCharacter ) )
+ {
+ pCharacter->GetActionController()->Clear();
+ Sequencer* pSeq = pCharacter->GetActionController()->GetNextSequencer();
+ if ( OnButtonPressed( pCharacter, pSeq ) )
+ {
+ PositionCharacter( pCharacter, pSeq );
+ SetAnimation( pCharacter, pSeq );
+
+ if ( UsesActionButton() )
+ {
+// GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+
+ return true;
+ }
+
+ }
+ return false;
+}
+
+/*
+==============================================================================
+AnimSwitch::PositionCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void AnimSwitch::PositionCharacter( Character* pCharacter, Sequencer* pSeq )
+{
+
+ rmt::Vector standPosition = mStandPosition;
+ if ( mbAttachToJoint )
+ {
+ // If attach to joint, then the locator has been transformed into
+ // object local space. Here we need to transform the point back
+ // into world space, but maintain the object local space of the locator,
+ // so we transform a copy of the position.
+ //
+ rmt::Matrix mat = mpJoint->worldMatrix;
+ standPosition.Transform( mat );
+ }
+
+ Action* pAction = 0;
+
+ // Orient.
+ //
+ rAssert( mpJoint );
+
+ rmt::Vector direction = GetActionEventLocator()->GetMatrix().Row(2);
+
+ // If direction != 0, 0, 0, then rotate us to align with direction.
+ //
+ if ( direction.NormalizeSafe( ) )
+ {
+ pSeq->BeginSequence();
+ pAction = new Orient( pCharacter, direction );
+ pSeq->AddAction( pAction );
+ // SlaveLoco
+ //
+ pAction = 0;
+
+ pAction = pCharacter->GetWalkerLocomotionAction();
+
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+ }
+
+ // Play run_into_object.
+ //
+ pSeq->BeginSequence();
+ pAction = new Position( pCharacter, standPosition, 0.1f );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+}
+/*
+==============================================================================
+AnimSwitch::SequenceActions
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, Sequencer* pSeq )
+
+Return: void
+
+=============================================================================
+*/
+void AnimSwitch::SequenceActions( Character* pCharacter, Sequencer* pSeq )
+{
+}
+
+/*
+==============================================================================
+AnimSwitch::SetAnimation
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, Sequencer* pSeq )
+
+Return: void
+
+=============================================================================
+*/
+void AnimSwitch::SetAnimation( Character* pCharacter, Sequencer* pSeq )
+{
+ Action* pAction = 0;
+ pAction = new PlayAnimationAction( pCharacter, "hit_switch_quick" );
+ pSeq->AddActionToSequence( pAction );
+ Action* eventAction = new TriggerEventAction( EVENT_BIG_RED_SWITCH_PRESSED, (void*)0 );
+ pSeq->AddActionToSequence( 0.3f, -1.0f, eventAction );
+}
+/*
+==============================================================================
+ToggleAnim::ToggleAnim
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+ToggleAnim::ToggleAnim( ActionEventLocator* pActionEventLocator )
+:
+AnimSwitch( pActionEventLocator )
+{
+}
+/*
+==============================================================================
+ToggleAnim::~ToggleAnim
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+ToggleAnim::~ToggleAnim()
+{
+ //
+ // Make good and sure this thing isn't going to play sound anymore
+ //
+ GetEventManager()->TriggerEvent( EVENT_STOP_ANIMATION_SOUND, this );
+}
+
+/*
+==============================================================================
+ToggleAnim::OnButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, Sequencer* pSeq )
+
+Return: void
+
+=============================================================================
+*/
+bool ToggleAnim::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ Action* pAction = 0;
+ float fDirection = 0.0f;
+ if ( GetAnimationDirection( ) != 0.0f )
+ {
+ fDirection = 0.0f;
+ }
+ else
+ {
+ fDirection = 1.0f;
+ }
+ tMultiController* pAnimController = GetAnimController( );
+ pAnimController->SetCycleMode( FORCE_CYCLIC );
+
+ pSeq->BeginSequence();
+ pAction = new AssignValueToFloat( GetAnimationDirection(), fDirection );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+
+ if( mSoundName != NULL )
+ {
+ if( fDirection == 0.0f )
+ {
+ GetEventManager()->TriggerEvent( EVENT_STOP_ANIMATION_SOUND, this );
+ }
+ else
+ {
+ AnimSoundData data( mSoundName, mSettingsName );
+
+ if( mIsMovingSound )
+ {
+ tPoseAnimationController* controller;
+ tPose::Joint theJoint;
+
+ //
+ // Platform. Yank the joint out of the animation stuff so that the
+ // moving sound code can calculate a position for it.
+ //
+ rAssert( dynamic_cast<tPoseAnimationController*>( pAnimController->GetTrack( 0 ) ) );
+ controller = static_cast<tPoseAnimationController*>( pAnimController->GetTrack( 0 ) );
+
+ rAssert( controller->GetPose()->GetNumJoint() >= 2 );
+ data.animJoint = controller->GetPose()->GetJoint( 1 );
+
+ data.soundObject = this;
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_START_ANIMATION_SOUND, &data );
+ }
+ }
+
+ return true;
+}
+
+/*
+==============================================================================
+ReverseAnim::OnButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, Sequencer* pSeq )
+
+Return: void
+
+=============================================================================
+*/
+bool ReverseAnim::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ Action* pAction = 0;
+ float fDirection = 0.0f;
+ if ( GetAnimationDirection( ) == 1.0f )
+ {
+ fDirection = -1.0f;
+ }
+ else
+ {
+ fDirection = 1.0f;
+ }
+ tMultiController* pAnimController = GetAnimController( );
+ pAnimController->SetCycleMode( FORCE_CYCLIC );
+
+ pSeq->BeginSequence();
+ pAction = new AssignValueToFloat( GetAnimationDirection( ), fDirection );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+ return true;
+}
+
+/*
+==============================================================================
+PlayAnim::PlayAnim
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+PlayAnim::PlayAnim( ActionEventLocator* pActionEventLocator )
+:
+AnimSwitch( pActionEventLocator )
+{
+}
+/*
+==============================================================================
+PlayAnim::~PlayAnim
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: PlayAnim
+
+=============================================================================
+*/
+PlayAnim::~PlayAnim()
+{
+ //
+ // Make good and sure this thing isn't going to play sound anymore
+ //
+ GetEventManager()->TriggerEvent( EVENT_STOP_ANIMATION_SOUND, this );
+}
+
+
+/*
+==============================================================================
+PlayAnim::OnButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, Sequencer* pSeq )
+
+Return: void
+
+=============================================================================
+*/
+bool PlayAnim::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ tMultiController* pAnimController = GetAnimController( );
+ bool bRestart = true;
+ // If the animation has already been triggered.
+ //
+ if ( GetAnimationDirection( ) == 1.0f )
+ {
+ // Test to see if the animation has finished.
+ //
+ if ( !pAnimController->LastFrameReached() )
+ {
+ // Don't perform the action.
+ //
+ bRestart = false;
+ }
+ }
+ if ( bRestart )
+ {
+ // This will stop the animation until Action executes.
+ //
+ SetAnimationDirection( 0.0f );
+ // Reset the animation to the initial frame.
+ // This assumes that the animation first frame is equal to last frame.
+ //
+ pAnimController->SetFrame( 0.0f );
+
+ // Force the animation to be NOT cyclic.
+ //
+ pAnimController->SetCycleMode( FORCE_NON_CYCLIC );
+
+
+ // Action will assign this value which will start the animation.
+ //
+ Action* pAction = 0;
+ float fDirection = 1.0f;
+ pSeq->BeginSequence();
+ pAction = new AssignValueToFloat( GetAnimationDirection( ), fDirection );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+
+ if( mSoundName != NULL )
+ {
+ AnimSoundData data( mSoundName, mSettingsName );
+
+ if( mIsMovingSound )
+ {
+ tPoseAnimationController* controller;
+ tPose::Joint theJoint;
+
+ //
+ // Platform. Yank the joint out of the animation stuff so that the
+ // moving sound code can calculate a position for it.
+ //
+ rAssert( dynamic_cast<tPoseAnimationController*>( pAnimController->GetTrack( 0 ) ) );
+ controller = static_cast<tPoseAnimationController*>( pAnimController->GetTrack( 0 ) );
+
+ data.animJoint = controller->GetPose()->GetJoint( 4 );
+
+ data.soundObject = this;
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_START_ANIMATION_SOUND, &data );
+ }
+ }
+ return bRestart;
+}
+/*
+==============================================================================
+PlayAnimLoop::PlayAnimLoop
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+PlayAnimLoop::PlayAnimLoop( ActionEventLocator* pActionEventLocator )
+:
+AnimSwitch( pActionEventLocator )
+{
+}
+/*
+==============================================================================
+PlayAnimLoop::~PlayAnimLoop
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: PlayAnimLoop
+
+=============================================================================
+*/
+PlayAnimLoop::~PlayAnimLoop()
+{
+}
+
+
+/*
+==============================================================================
+PlayAnimLoop::OnButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, Sequencer* pSeq )
+
+Return: void
+
+=============================================================================
+*/
+bool PlayAnimLoop::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ // Only do this once.
+ //
+ if ( GetAnimationDirection( ) == 0.0f )
+ {
+
+ // Force the animation to be cyclic.
+ //
+ tMultiController* pAnimController = GetAnimController( );
+ pAnimController->SetCycleMode( FORCE_CYCLIC );
+
+ // Action will assign this value which will start the animation.
+ //
+ Action* pAction = 0;
+ float fDirection = 1.0f;
+ pSeq->BeginSequence();
+ pAction = new AssignValueToFloat( GetAnimationDirection( ), fDirection );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+ return true;
+ }
+ return false;
+}
+
+/*
+==============================================================================
+AutoPlayAnim::AutoPlayAnim
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+AutoPlayAnim::AutoPlayAnim( ActionEventLocator* pActionEventLocator )
+:
+AnimSwitch( pActionEventLocator ),
+mCharacterCount( 0 ),
+mbJustEmpty( false )
+{
+ SetActionButton( CharacterController::NONE );
+}
+/*
+==============================================================================
+AutoPlayAnim::~AutoPlayAnim
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: AutoPlayAnim
+
+=============================================================================
+*/
+AutoPlayAnim::~AutoPlayAnim()
+{
+ //
+ // Make good and sure this thing isn't going to play sound anymore
+ //
+ GetEventManager()->TriggerEvent( EVENT_STOP_ANIMATION_SOUND, this );
+}
+
+/*
+==============================================================================
+AutoPlayAnim::IsActionButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: bool
+
+=============================================================================
+*/
+bool AutoPlayAnim::IsActionButtonPressed( Character* pCharacter )
+{
+ // These are auto volumes. No buttons.
+ //
+ return false;
+}
+
+/*
+==============================================================================
+AutoPlayAnim::IsActionButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: bool
+
+=============================================================================
+*/
+bool AutoPlayAnim::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ return false;
+}
+/*
+==============================================================================
+AutoPlayAnim::OnEnter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void AutoPlayAnim::OnEnter( Character* pCharacter )
+{
+ if ( mCharacterCount == 0 )
+ {
+ tMultiController* pAnimController = GetAnimController( );
+ // First person in the volume.
+ //
+ // Reset the animation to the initial frame.
+ // This assumes that the animation first frame is equal to last frame.
+ //
+ pAnimController->SetFrame( 0.0f );
+
+ // This will start the animation.
+ //
+ SetAnimationDirection( 1.0f );
+
+ // Force the animation to be cyclic dependent on the class type.
+ // GetIsCyclic is virtual.
+ //
+ pAnimController->SetCycleMode( GetIsCyclic() ? FORCE_CYCLIC : FORCE_NON_CYCLIC );
+
+ if( mSoundName != NULL )
+ {
+ AnimSoundData data( mSoundName, mSettingsName );
+
+ if( mIsMovingSound )
+ {
+ tPoseAnimationController* controller;
+ tPose::Joint theJoint;
+
+ //
+ // Platform. Yank the joint out of the animation stuff so that the
+ // moving sound code can calculate a position for it.
+ //
+ rAssert( dynamic_cast<tPoseAnimationController*>( pAnimController->GetTrack( 0 ) ) );
+ controller = static_cast<tPoseAnimationController*>( pAnimController->GetTrack( 0 ) );
+
+ rAssert( controller->GetPose()->GetNumJoint() >= 2 );
+ data.animJoint = controller->GetPose()->GetJoint( 1 );
+
+ data.soundObject = this;
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_START_ANIMATION_SOUND, &data );
+ }
+ }
+ mCharacterCount++;
+}
+/*
+==============================================================================
+AutoPlayAnim::OnUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void AutoPlayAnim::OnUpdate( float timeins )
+{
+ if ( mbJustEmpty )
+ {
+ // Test to see if the animation has finished.
+ //
+ tMultiController* pAnimController = GetAnimController( );
+ if ( pAnimController->LastFrameReached() )
+ {
+ mbJustEmpty = false;
+ // The animation is finished. Reactivate the trigger volume.
+ //
+ mpActionEventLocator->SetFlag( Locator::ACTIVE, true );
+
+ GetEventManager()->TriggerEvent( EVENT_STOP_ANIMATION_SOUND, this );
+ }
+ }
+}
+/*
+==============================================================================
+AutoPlayAnim::OnExit
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void AutoPlayAnim::OnExit( Character* pCharacter )
+{
+ mCharacterCount--;
+ if ( mCharacterCount == 0 )
+ {
+ tMultiController* pAnimController = GetAnimController( );
+
+ // Force the animation to be NOT cyclic.
+ //
+ pAnimController->SetCycleMode( FORCE_NON_CYCLIC );
+
+ mbJustEmpty = true;
+
+ // Deactivate the trigger volume. Give the animation a chance to finish.
+ //
+ mpActionEventLocator->SetFlag( Locator::ACTIVE, false );
+ }
+ rAssert( mCharacterCount >= 0 );
+}
+
+/*
+==============================================================================
+AutoPlayAnim::PositionCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void AutoPlayAnim::PositionCharacter( Character* pCharacter, Sequencer* pSeq )
+{
+}
+/*
+==============================================================================
+AutoPlayAnim::SetAnimation
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void AutoPlayAnim::SetAnimation( Character* pCharacter, Sequencer* pSeq )
+{
+}
+/*
+==============================================================================
+AutoPlayAnimLoop::AutoPlayAnimLoop
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+AutoPlayAnimLoop::AutoPlayAnimLoop( ActionEventLocator* pActionEventLocator )
+:
+AutoPlayAnim( pActionEventLocator )
+{
+}
+/*
+==============================================================================
+AutoPlayAnimLoop::~AutoPlayAnimLoop
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: AutoPlayAnimLoop
+
+=============================================================================
+*/
+AutoPlayAnimLoop::~AutoPlayAnimLoop()
+{
+}
+
+/*
+==============================================================================
+AutoPlayAnimInOut::AutoPlayAnimInOut
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+AutoPlayAnimInOut::AutoPlayAnimInOut( ActionEventLocator* pActionEventLocator )
+:
+AutoPlayAnim( pActionEventLocator )
+{
+}
+/*
+==============================================================================
+AutoPlayAnimInOut::~AutoPlayAnimInOut
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: AutoPlayAnimInOut
+
+=============================================================================
+*/
+AutoPlayAnimInOut::~AutoPlayAnimInOut()
+{
+}
+
+/*
+==============================================================================
+AutoPlayAnimInOut::IsActionButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: bool
+
+=============================================================================
+*/
+bool AutoPlayAnimInOut::IsActionButtonPressed( Character* pCharacter )
+{
+ return false;
+}
+
+/*
+==============================================================================
+AutoPlayAnimInOut::IsActionButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: bool
+
+=============================================================================
+*/
+bool AutoPlayAnimInOut::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ return false;
+}
+
+/*
+==============================================================================
+AutoPlayAnimInOut::OnUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void AutoPlayAnimInOut::OnUpdate( float timeins )
+{
+ if ( mbJustEmpty )
+ {
+ // Test to see if the animation has finished.
+ //
+ tMultiController* pAnimController = GetAnimController( );
+ if ( pAnimController->LastFrameReached() )
+ {
+ // This will reverse the animation.
+ //
+ SetAnimationDirection( -1.0f );
+
+
+ }
+ else if ( pAnimController->GetFrame() == 0.0f )
+ {
+ // We have played back to the start.
+ //
+
+ mbJustEmpty = false;
+ // The animation is finished. Reactivate the trigger volume.
+ //
+ mpActionEventLocator->SetFlag( Locator::ACTIVE, true );
+ }
+ }
+}
+
+/*
+==============================================================================
+AutoPlayAnimInOut::OnExit
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void AutoPlayAnimInOut::OnExit( Character* pCharacter )
+{
+ mCharacterCount--;
+ if ( mCharacterCount == 0 )
+ {
+ mbJustEmpty = true;
+ // Deactivate the trigger volume. Give the animation a chance to finish.
+ //
+ mpActionEventLocator->SetFlag( Locator::ACTIVE, false );
+ }
+ rAssert( mCharacterCount >= 0 );
+}
+/*
+==============================================================================
+DestroyObject::DestroyObject
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+DestroyObject::DestroyObject( ActionEventLocator* pActionEventLocator )
+:
+AnimSwitch( pActionEventLocator ),
+mbDestroyed( false ),
+mpStatePropDSG( NULL )
+{
+}
+/*
+==============================================================================
+DestroyObject::~DestroyObject
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: DestroyObject
+
+=============================================================================
+*/
+DestroyObject::~DestroyObject()
+{
+ if ( mpStatePropDSG != NULL )
+ {
+ mpStatePropDSG->RemoveStatePropListener( this );
+ }
+}
+
+bool
+DestroyObject::Create( tEntityStore* inStore )
+{
+ bool success;
+ // Try and find the corresponding InstAnimDynaPhysDSG object in the inventory.
+ // InstAnimDynaPhysDSG* pDSG = p3d::find< InstAnimDynaPhysDSG > ( inStore, mpActionEventLocator->GetObjName() );
+ StatePropDSG* pDSG = p3d::find< StatePropDSG >( inStore, mpActionEventLocator->GetObjName() );
+
+ if ( pDSG != NULL )
+ {
+ mpStatePropDSG = pDSG;
+ mpStatePropDSG->AddStatePropListener( this );
+ mpStatePropDSG->SetState(0);
+
+ SetInstanceEnabled( false );
+
+ success = true;
+ }
+ else
+ {
+ rTuneWarning("ERROR : DestroyObject::Create() could not find DSG object in inventory");
+ success = false;
+ }
+
+ return success;
+}
+bool
+DestroyObject::NeedsUpdate( void ) const
+{
+ // Plain destroy objects need no per-frame updating
+ return false;
+}
+
+void DestroyObject::Enter( Character* pCharacter )
+{
+ // don't let the action handler get registered of object is destroyed
+ // TODO : should really save processing by removing trigger from trigger volume tracker
+ if(!mbDestroyed)
+ {
+ AnimSwitch::Enter(pCharacter);
+ }
+}
+
+//=============================================================================
+// DestroyObject::OnReset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void )
+//
+// Return: void
+//
+//=============================================================================
+void DestroyObject::OnReset( void )
+{
+ // We want to respawn the stateprops and reset their state.
+ // So even if the user stomped and kicked them out of existance before the
+ // mission started, they would have magically respawned.
+
+ if ( mpStatePropDSG != NULL )
+ {
+ mpStatePropDSG->SetState( 0 );
+ mpStatePropDSG->EnableCollisionVolume( true );
+ }
+ mbDestroyed = false;
+}
+/*
+==============================================================================
+DestroyObject::OnButtonPressed==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, Sequencer* pSeq )
+
+Return: void
+
+=============================================================================
+*/
+bool DestroyObject::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ // Button press
+ // Destroy the object immediately
+ // Play a particle effect
+ // Then remove it from the DSG
+ if ( mpStatePropDSG != NULL )
+ {
+ pCharacter->Kick();
+ }
+ return true;
+}
+
+/*
+==============================================================================
+DestroyObject::PositionCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void DestroyObject::PositionCharacter( Character* pCharacter, Sequencer* pSeq )
+{
+
+ if ( mbAttachToJoint )
+ {
+ ActionEventLocator* pActionEventLocator = GetActionEventLocator();
+ if ( pActionEventLocator )
+ {
+ static float sfX = 0.2f;
+ static float sfZ = -0.7f;
+ rmt::Vector loc( sfX, 0.0f, sfZ );
+ rmt::Matrix mat = mpJoint->worldMatrix;
+ rmt::Vector pos = mpJoint->worldMatrix.Row( 3 );
+ loc.Transform( mat );
+ pActionEventLocator->SetLocation( loc );
+ }
+ }
+ ActionEventLocator* pActionEventLocator = GetActionEventLocator( );
+ rAssert( pActionEventLocator );
+ rmt::Vector standPosition;
+ pCharacter->GetPosition( standPosition );
+ rmt::Vector direction;
+ pActionEventLocator->GetLocation( &direction );
+
+ Action* pAction = 0;
+ // Orient.
+ //
+ direction.Sub( standPosition );
+ // If direction != 0, 0, 0, then rotate us to align with direction.
+ //
+ if ( direction.NormalizeSafe( ) )
+ {
+ pSeq->BeginSequence();
+ pAction = new Orient( pCharacter, direction );
+ pSeq->AddAction( pAction );
+ // SlaveLoco
+ //
+ pAction = 0;
+
+ pAction = pCharacter->GetWalkerLocomotionAction();
+
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+ }
+}
+/*
+==============================================================================
+DestroyObject::SetAnimation
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void DestroyObject::SetAnimation( Character* pCharacter, Sequencer* pSeq )
+{
+ Action* pAction = 0;
+ pAction = new PlayAnimationAction( pCharacter, "break_quick" );
+ pSeq->AddActionToSequence( pAction );
+}
+
+/*
+==============================================================================
+DestroyObject::OnUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void DestroyObject::OnUpdate( float timeins )
+{
+
+}
+
+void DestroyObject::RecieveEvent( int callback , CStateProp* stateProp )
+{
+ const int REMOVE_FROM_WORLD_CALLBACK = 0;
+ const int OBJECT_DESTROYED_CALLBACK = 10;
+ if ( callback == REMOVE_FROM_WORLD_CALLBACK || callback == OBJECT_DESTROYED_CALLBACK )
+ {
+ rAssert( mpStatePropDSG != NULL );
+ mbDestroyed = true;
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_DESTROYED, mpActionEventLocator );
+ GetEventManager()->TriggerEvent( EVENT_BREAK_CAMERA_OR_BOX );
+ }
+}
+
+
+/*
+==============================================================================
+UseVendingMachine::UseVendingMachine
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+UseVendingMachine::UseVendingMachine( ActionEventLocator* pActionEventLocator )
+:
+PlayAnim( pActionEventLocator )
+{
+}
+/*
+==============================================================================
+UseVendingMachine::~UseVendingMachine
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: UseVendingMachine
+
+=============================================================================
+*/
+UseVendingMachine::~UseVendingMachine()
+{
+}
+
+
+/*
+==============================================================================
+UseVendingMachine::OnButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, Sequencer* pSeq )
+
+Return: void
+
+=============================================================================
+*/
+bool UseVendingMachine::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ if ( PlayAnim::OnButtonPressed( pCharacter, pSeq ) )
+ {
+ // used to set turbo meter here, is this class even neccesary any more
+ return true;
+ }
+ return false;
+}
+/*
+==============================================================================
+UseVendingMachine::SetAnimation
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void UseVendingMachine::SetAnimation( Character* pCharacter, Sequencer* pSeq )
+{
+ Action* pAction = 0;
+ pAction = new PlayAnimationAction( pCharacter, "hit_switch_quick" );
+ pSeq->AddActionToSequence( pAction );
+}
+/*
+==============================================================================
+PrankPhone::PrankPhone
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+PrankPhone::PrankPhone( ActionEventLocator* pActionEventLocator )
+:
+PlayAnim( pActionEventLocator )
+{
+}
+/*
+==============================================================================
+PrankPhone::~PrankPhone
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: PrankPhone
+
+=============================================================================
+*/
+PrankPhone::~PrankPhone()
+{
+}
+
+
+/*
+==============================================================================
+PrankPhone::OnButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, Sequencer* pSeq )
+
+Return: void
+
+=============================================================================
+*/
+bool PrankPhone::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ if ( PlayAnim::OnButtonPressed( pCharacter, pSeq ) )
+ {
+ // Setup the prank phone call.
+ //
+ return true;
+ }
+ return false;
+}
+/*
+==============================================================================
+PrankPhone::SetAnimation
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void PrankPhone::SetAnimation( Character* pCharacter, Sequencer* pSeq )
+{
+ Action* pAction = 0;
+ pAction = new PlayAnimationAction( pCharacter, "break_quick" );
+ pSeq->AddActionToSequence( pAction );
+}
+
+//=============================================================================
+// Doorbell::Create
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tEntityStore* inStore )
+//
+// Return: bool
+//
+//=============================================================================
+bool Doorbell::Create( tEntityStore* inStore )
+{
+ AnimCollisionEntityDSG* pAnimCollisionEntityDSG = p3d::find<AnimCollisionEntityDSG>( inStore, mpActionEventLocator->GetJointName() );
+ bool bCreated = false;
+ if ( pAnimCollisionEntityDSG )
+ {
+ tPose* p3dPose = pAnimCollisionEntityDSG->GetPoseEngine()->GetP3DPose( );
+ rAssert( p3dPose );
+
+ // Evaluate the pose to get the proper worldmatrix for the joints.
+ //
+ p3dPose->Evaluate();
+ int jointIndex = p3dPose->FindJointIndex( mpActionEventLocator->GetJointName() );
+
+ tPose::Joint* pJoint = pAnimCollisionEntityDSG->GetPoseJoint( jointIndex, mpActionEventLocator->GetShouldTransform() );
+
+ ActionButton::AnimCollisionEntityDSGWrapper* pGameObject = new(GMA_LEVEL_OTHER) ActionButton::AnimCollisionEntityDSGWrapper;
+ pGameObject->SetGameObject( pAnimCollisionEntityDSG );
+ // Set the start state of all animations requiring a trigger to off.
+ //
+ // This will stop the animation until Action executes.
+ //
+
+ pAnimCollisionEntityDSG->GetAnimController()->SetCycleMode( FORCE_CYCLIC );
+ pGameObject->SetAnimationDirection( 1.0f );
+
+ Init( pGameObject, pJoint, false );
+ bCreated = true;
+ }
+ return bCreated;
+}
+
+//=============================================================================
+// Doorbell::OnButtonPressed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter, Sequencer* pSeq )
+//
+// Return: bool
+//
+//=============================================================================
+bool Doorbell::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ //if ( PlayAnim::OnButtonPressed( pCharacter, pSeq ) )
+ {
+ //Use the object name as the name for the sound as well.
+ const char* charName = this->mpActionEventLocator->GetObjName();
+
+ //Send this to the sound system.
+ if( charName != NULL )
+ {
+ GetEventManager()->TriggerEvent( EVENT_DING_DONG, const_cast<char*>(&charName[3]) );
+ }
+
+ return true;
+ }
+ return false;
+}
+
+bool SummonVehiclePhone::sbEnabled = true;
+int SummonVehiclePhone::CarSelectionInfo::sNumUsedSlots = 0;
+//int SummonVehiclePhone::sSelectedVehicle = INVALID_VEHICLE;
+char SummonVehiclePhone::sSelectedVehicleName[ SummonVehiclePhone::MAX_VEHICLE_NAME_LENGTH ];
+SummonVehiclePhone::CarSelectionInfo SummonVehiclePhone::sCarSelectInfo[ NUM_CAR_SELECTION_SLOTS ];
+SummonVehiclePhoneStaticCallback SummonVehiclePhone::sCallback;
+/*
+==============================================================================
+SummonVehiclePhone::SummonVehiclePhone
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+SummonVehiclePhone::SummonVehiclePhone( ActionEventLocator* pActionEventLocator )
+: ActionEventHandler( pActionEventLocator ),
+ mHudMapIconID( -1 )
+{
+ rmt::Vector pos;
+ pActionEventLocator->GetLocation( &pos );
+ mPhoneIcon.Init( "phone_icon", pos );
+
+ // register phone booth icon on HUD map
+ //
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ mHudMapIconID = currentHud->GetHudMap( 0 )->RegisterIcon( HudMapIcon::ICON_PHONE_BOOTH, pos );
+ }
+
+}
+/*
+==============================================================================
+SummonVehiclePhone::~SummonVehiclePhone
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: SummonVehiclePhone
+
+=============================================================================
+*/
+SummonVehiclePhone::~SummonVehiclePhone()
+{
+ // un-register phone booth icon
+ //
+ if( mHudMapIconID != -1 )
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->GetHudMap( 0 )->UnregisterIcon( mHudMapIconID );
+ mHudMapIconID = -1;
+ }
+ }
+
+ // release event locator
+ //
+ if( sCallback.mpActionEventLocator != NULL )
+ {
+ sCallback.mpActionEventLocator->Release();
+ sCallback.mpActionEventLocator = NULL;
+ }
+}
+
+//=============================================================================
+// SummonVehiclePhone::Create
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tEntityStore* inStore /* = 0 */ )
+//
+// Return: bool
+//
+//=============================================================================
+bool SummonVehiclePhone::Create( tEntityStore* inStore /* = 0 */ )
+{
+ tUID nameUID = tEntity::MakeUID(mpActionEventLocator->GetObjName());
+ unsigned int i;
+ for ( i = 0; i < ActionButtonManager::MAX_ACTIONS; ++i )
+ {
+ if ( GetActionButtonManager()->GetActionByIndex( i ) )
+ {
+ ActionButton::ButtonHandler* bh = GetActionButtonManager()->GetActionByIndex( i );
+
+ if ( bh && bh->GetType() == ActionButton::ButtonHandler::SUMMON_PHONE )
+ {
+ rAssert( dynamic_cast<ActionButton::SummonVehiclePhone*>(bh) != NULL );
+ ActionButton::SummonVehiclePhone* svp = static_cast<ActionButton::SummonVehiclePhone*>(bh);
+ rAssert( svp );
+ if ( tEntity::MakeUID(svp->GetActionEventLocator()->GetObjName()) == nameUID )
+ {
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+bool
+SummonVehiclePhone::UsesActionButton() const
+{
+ bool isPhoneEnabled = GetGameplayManager()->QueryPhoneBoothsEnabled();
+ return isPhoneEnabled;
+}
+
+/*
+==============================================================================
+SummonVehiclePhone::DumpVehicle
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void SummonVehiclePhone::DumpVehicle( void )
+{
+ rAssertMsg( false, "*** DEPRECATED FUNCTION! ***" );
+/*
+ if ( INVALID_VEHICLE != GetSelectedVehicleIndex( ) )
+ {
+ // A vehicle is already loaded. We need to dump a vehicle.
+ //
+ CarSelectionInfo* pInfo = GetCarSelectInfo( GetSelectedVehicleIndex( ) );
+ rAssert( pInfo );
+ if ( pInfo )
+ {
+ // Dump the vehicle.
+ //
+ Vehicle* pVehicle = GetVehicleCentral()->GetVehicleByName( pInfo->GetVehicleName() );
+ if ( pVehicle )
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList( pVehicle );
+ }
+
+ pVehicle->ReleaseVerified();
+ // Dump the inventory associated with this vehicle.
+
+
+ p3d::inventory->RemoveSectionElements( pInfo->GetFileName() );
+ p3d::inventory->DeleteSection( pInfo->GetFileName() );
+ }
+ }
+*/
+}
+/*
+==============================================================================
+SummonVehiclePhone::OnButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, Sequencer* pSeq )
+
+Return: void
+
+=============================================================================
+*/
+bool SummonVehiclePhone::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ //chuck
+ //we cant enter the phonebooth if we are in the paused state due to mission fail
+ //since this will generate a stack overflow and game death
+ if (
+ GetGameFlow()->GetNextContext() == CONTEXT_PAUSE
+ ||
+ GetGameplayManager()->GetCurrentMission()->GetState() ==Mission::STATE_FAILED
+ )
+ {
+ return false;
+ }
+
+ if( GetGuiSystem()->GetInGameManager()->IsEnteringPauseMenu() )
+ {
+ // if about to enter pause menu, ignore button press
+ //
+ return false;
+ }
+
+ //check if we can use the phone booth.
+ if ( sbEnabled && GetGameplayManager()->QueryPhoneBoothsEnabled() )
+ {
+ // clear out any unneeded characters here (mainly to remove characters
+ // from completed bonus missions in case we need their car)
+ GetCharacterManager()->GarbageCollect(true);
+
+ // We do some of the work here, then we will trigger an event to do the menu.
+ // The menu will trigger the loading.
+ //
+
+ //Chuck: We update the users vehicle's Hit points in the Charactersheet
+ int CarIndex = -1;
+ char car_name [16];
+ car_name[0] = '\0';
+ strcpy(car_name,GetGameplayManager()->GetVehicleSlotVehicleName(GameplayManager::eDefaultCar));
+ CarIndex = GetCharacterSheetManager()->GetCarIndex(car_name);
+
+ //check if the players default car exists
+
+ if ( GetGameplayManager()->GetVehicleInSlot(GameplayManager::eDefaultCar) != NULL)
+ {
+ //update
+ float currentCarHitpoints = GetGameplayManager()->GetVehicleInSlot(GameplayManager::eDefaultCar)->mHitPoints;
+ GetCharacterSheetManager()->UpdateCarHealth(CarIndex,GetGameplayManager()->GetVehicleInSlot(GameplayManager::eDefaultCar)->GetVehicleLifePercentage(currentCarHitpoints));
+ }
+ else
+ {
+ if (GetGameplayManager()->GetVehicleInSlot(GameplayManager::eOtherCar) != NULL)
+ {
+ //we are in a forced car mission do nothing since player may not repair cars in a forced car mission.
+ }
+ }
+
+
+
+ tRefCounted::Assign( sCallback.mpActionEventLocator, mpActionEventLocator );
+
+ // From this point we should just trigger an event to throw up a
+ // menu of cars to choose from. Selecting from the menu will trigger the load.
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_PHONE_BOOTH );
+
+ const rmt::Matrix& matrix = mpActionEventLocator->GetMatrix();
+ SuperCam* sc = GetSuperCamManager()->GetSCC( 0 )->GetSuperCam( SuperCam::RELATIVE_ANIMATED_CAM );
+ RelativeAnimatedCam* rac = dynamic_cast< RelativeAnimatedCam* >( sc );
+ rmt::Matrix m2 = matrix;
+ rmt::Matrix rotate;
+ rotate.Identity();
+ rotate.FillRotateY( rmt::PI / 2 );
+ m2.Mult( rotate );
+ const rmt::Vector& translation = matrix.Row( 3 );
+ m2.FillTranslate( translation );
+ rac->SetOffsetMatrix( m2 );
+
+ // switch to pause context
+ //
+ GetGameFlow()->SetContext( CONTEXT_PAUSE );
+
+ return true;
+ }
+ else
+ {
+ // TC: [TODO] trigger 'busy signal' event
+ //
+ GetEventManager()->TriggerEvent( EVENT_PHONE_BOOTH_BUSY );
+
+ return false;
+ }
+}
+
+/*
+==============================================================================
+SummonVehiclePhone::SelectLoadedVehicle
+==============================================================================
+Description: Comment
+
+Parameters: ( int selectedVehicleIndex )
+
+Return: void
+
+=============================================================================
+*/
+/*
+void SummonVehiclePhone::SelectLoadedVehicle( int selectedVehicleIndex )
+{
+ //HACK: this a perpetuation of Trav's original Billboardwrappedloader hack.
+ //dyna loading duriong a car load will now be BAD. -dm
+ ((BillboardWrappedLoader*)GetAllWrappers()->mpLoader(AllWrappers::msBillboard))->OverrideLoader(true);
+ //user has confirmed vehicle switch, so we must dump cars
+ //DumpVehicle();
+ GetGameplayManager()->DumpCurrentCar();
+
+// sSelectedVehicle = selectedVehicleIndex;
+ CarSelectionInfo* pInfo = GetCarSelectInfo( GetSelectedVehicleIndex( ) );
+ rAssert( pInfo );
+ if ( pInfo )
+ {
+ //update the Gameplaymanager about new car getting loaded into the Other slot.
+ strcpy(GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].name,pInfo->GetVehicleName());
+ strcpy(GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].filename,pInfo->GetFileName());
+
+ sCallback.OnProcessRequestsComplete( NULL );
+ }
+}
+*/
+
+/*
+==============================================================================
+SummonVehiclePhone::LoadVehicle
+==============================================================================
+Description: Comment
+
+Parameters: ( const char* filename )
+
+Return: void
+
+=============================================================================
+*/
+void SummonVehiclePhone::LoadVehicle( const char* name, const char* filename, VehicleCentral::DriverInit driver)
+{
+ //HACK: this a perpetuation of Trav's original Billboardwrappedloader hack.
+ //dyna loading duriong a car load will now be BAD. -dm
+ ((BillboardWrappedLoader*)GetAllWrappers()->mpLoader(AllWrappers::msBillboard))->OverrideLoader(true);
+
+ //user has confirmed vehicle switch, so we must dump cars
+ //DumpVehicle();
+ GetGameplayManager()->DumpCurrentCar();
+ rmt::Vector position;
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( position );
+ GetPCM().RemoveFreeCarIfClose( position );
+ rmt::Sphere s;
+ s.centre = position;
+ s.radius = 10.0f;
+ TrafficManager::GetInstance()->ClearTrafficInSphere( s );
+
+ // new
+ // greg
+ // jan 4, 2003
+
+ // phone booth cars should overwrite the default slot
+
+ rAssert( name != NULL && filename != NULL );
+
+ strncpy( sSelectedVehicleName, name, sizeof( sSelectedVehicleName ) );
+
+ //update the Gameplaymanager about new car getting loaded into the Other slot.
+ strcpy(GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].name, name);
+ strcpy(GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].filename, filename);
+
+ sCallback.mDriver = driver;
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, filename, GMA_LEVEL_MISSION, filename, NULL, &sCallback );
+}
+
+/*
+==============================================================================
+SummonVehiclePhone::LoadVehicle
+==============================================================================
+Description: Comment
+
+Parameters: ( int selectedVehicleIndex )
+
+Return: void
+
+=============================================================================
+*/
+void SummonVehiclePhone::LoadVehicle( int selectedVehicleIndex )
+{
+// sSelectedVehicle = selectedVehicleIndex;
+
+ CarSelectionInfo* pInfo = GetCarSelectInfo( selectedVehicleIndex );
+ rAssert( pInfo );
+ SummonVehiclePhone::LoadVehicle( pInfo->GetVehicleName(), pInfo->GetFileName(), VehicleCentral::ALLOW_DRIVER );
+}
+
+/*
+==============================================================================
+SummonVehiclePhone::LoadDebugVehicle
+==============================================================================
+Description: Comment
+
+Parameters: ( const char* filename )
+
+Return: void
+
+=============================================================================
+*/
+void SummonVehiclePhone::LoadDebugVehicle( void )
+{
+ LoadVehicle( NUM_CAR_SELECTION_SLOTS - 1 );
+}
+
+//=============================================================================
+// SummonVehiclePhone::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float timeins )
+//
+// Return: void
+//
+//=============================================================================
+void SummonVehiclePhone::OnUpdate( float timeins )
+{
+ if ( GetGameplayManager()->QueryPhoneBoothsEnabled() )
+ {
+ mPhoneIcon.ShouldRender( true );
+ mPhoneIcon.Update( rmt::FtoL(timeins * 1000.0f) );
+ }
+ else
+ {
+ mPhoneIcon.ShouldRender( false );
+ }
+}
+
+/*
+==============================================================================
+SummonVehiclePhoneStaticCallback::~SummonVehiclePhoneStaticCallback
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: SummonVehiclePhoneStaticCallback
+
+=============================================================================
+*/
+SummonVehiclePhoneStaticCallback::~SummonVehiclePhoneStaticCallback( void )
+{
+ tRefCounted::Release( mpActionEventLocator );
+}
+/*
+==============================================================================
+SummonVehiclePhoneStaticCallback::OnProcessRequestsComplete
+==============================================================================
+Description: Comment
+
+Parameters: ( void* pUserData )
+
+Return: void
+
+=============================================================================
+*/
+void SummonVehiclePhoneStaticCallback::OnProcessRequestsComplete( void* pUserData )
+{
+ ((BillboardWrappedLoader*)GetAllWrappers()->mpLoader(AllWrappers::msBillboard))->OverrideLoader(false);
+ // So now the vehicle is loaded.
+ //
+// SummonVehiclePhone::CarSelectionInfo* pInfo = SummonVehiclePhone::GetCarSelectInfo( SummonVehiclePhone::GetSelectedVehicleIndex( ) );
+// rAssert( pInfo );
+ // You suck, non const mf.
+
+ //check to see if player car is being requested
+ bool bplayercar = true;
+
+ if (GetCharacterSheetManager()->GetCarIndex(SummonVehiclePhone::GetSelectedVehicleName() ) == -1 )
+ {
+ bool bplayercar = false;
+ }
+
+
+ Vehicle* pVehicle = GetVehicleCentral()->InitVehicle( const_cast<char*>( SummonVehiclePhone::GetSelectedVehicleName() ), true, NULL, VT_USER, mDriver,bplayercar );
+ rAssert( pVehicle );
+ if ( pVehicle )
+ {
+ if ( mpActionEventLocator )
+ {
+ CarStartLocator* pLocator = p3d::find<CarStartLocator>( mpActionEventLocator->GetObjName() );
+ if ( pLocator )
+ {
+
+ rmt::Vector position;
+
+ pLocator->GetLocation( &position );
+ float facing = pLocator->GetRotation();
+
+ // Set the vehicle.
+ //
+ //pVehicle->SetInitialPosition( &position );
+ pVehicle->SetInitialPositionGroundOffsetAutoAdjust( &position );
+ pVehicle->SetResetFacingInRadians( facing );
+
+ // This will activate the above settings.
+ //
+ pVehicle->Reset( false );
+ //applying the correct damage to the called vehicle
+/*
+ //check if car is in charactersheet a -1 from car index indicates cars is not owned by player
+ int carindex = -1;
+ carindex = GetCharacterSheetManager()->GetCarIndex(pVehicle->GetName());
+
+ //so if the car is owned by the player and not a husk then apply the recorded damage to it.
+ if ( (carindex != -1) && (GetCharacterSheetManager()->GetCarHealth(carindex) != 0.0f) )
+ {
+ float damage = 1.0f - GetCharacterSheetManager()->GetCarHealth(carindex);
+ damage *= pVehicle->mDesignerParams.mHitPoints;
+ pVehicle->TriggerDamage(damage);
+
+
+ }
+*/
+
+ //Chuck: Adding support of "current" vehicle
+ GetGameplayManager()->SetCurrentVehicle(pVehicle);
+
+ //Also adding support for continuity errors.
+ //If this car is the same as a car currently loaded according to the table, then
+ //we have a problem.
+ if ( GetGameplayManager()->TestForContinuityErrorWithCar( pVehicle, true ) )
+ {
+ Vehicle* errorCar = GetGameplayManager()->GetVehicleInSlot( GameplayManager::eAICar );
+ if ( errorCar )
+ {
+ //This is kinda hacky.
+ GetVehicleCentral()->RemoveVehicleFromActiveList( errorCar );
+ errorCar->DrawVehicle( false );
+ }
+ }
+
+ // new
+ // greg
+ // jan 4, 2003
+
+ // phone booth cars should overwrite the default slot
+
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].mp_vehicle = pVehicle;
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].mp_vehicle->AddRef();
+ }
+ else
+ {
+ rAssertMsg(0, "what now! - vehicle init'd and added to active list but not addref'd - see greg \n");
+ }
+ tRefCounted::Release( mpActionEventLocator );
+ }
+ else
+ {
+ rAssertMsg(0, "what now! - vehicle init'd and added to active list but not addref'd - see greg \n");
+ }
+
+ // tell GUI system to resume in-game
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_RESUME_INGAME );
+ }
+}
+
+/*
+==============================================================================
+SummonVehiclePhone::SetAnimation
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void SummonVehiclePhone::SetAnimation( Character* pCharacter, Sequencer* pSeq )
+{
+ Action* pAction = 0;
+ pAction = new PlayAnimationAction( pCharacter, "phone_hold" );
+ pSeq->AddActionToSequence( pAction );
+}
+
+/*
+==============================================================================
+Bounce::Bounce
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+Bounce::Bounce( ActionEventLocator* pActionEventLocator )
+:
+ActionEventHandler( pActionEventLocator )
+{
+}
+/*
+==============================================================================
+Bounce::~Bounce
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: Bounce
+
+=============================================================================
+*/
+Bounce::~Bounce()
+{
+}
+/*
+==============================================================================
+Bounce::Create
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+bool Bounce::Create( tEntityStore* inStore )
+{
+ EventLocator* pEventLocator = new (GMA_LEVEL_OTHER) EventLocator();
+ pEventLocator->SetName( mpActionEventLocator->GetObjName() );
+ pEventLocator->AddRef();
+ pEventLocator->SetEventType( LocatorEvent::BOUNCEPAD );
+ pEventLocator->SetNumTriggers( 1, GMA_LEVEL_OTHER );
+ rmt::Vector location;
+ mpActionEventLocator->GetLocation( &location );
+ pEventLocator->SetLocation( location );
+
+ TriggerVolume* pTriggerVolume = mpActionEventLocator->GetTriggerVolume( 0 );
+ pEventLocator->AddTriggerVolume( pTriggerVolume );
+ pTriggerVolume->SetLocator( pEventLocator );
+ pTriggerVolume->SetName( mpActionEventLocator->GetObjName() );
+
+ // Nothing can go wrong here.
+ //
+ return true;
+}
+/*
+==============================================================================
+Bounce::OnButtonPressed
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, Sequencer* pSeq )
+
+Return: void
+
+=============================================================================
+*/
+void Bounce::OnEnter( Character* pCharacter )
+{
+}
+
+/*
+==============================================================================
+Bounce::OnEnter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, Locator* pLocator )
+
+Return: void
+
+=============================================================================
+*/
+void Bounce::OnEnter( Character* pCharacter, Locator* pLocator )
+{
+ if(pCharacter->GetStateManager()->GetState() != CharacterAi::LOCO)
+ {
+ return;
+ }
+
+ // Do the bounce logic.
+ //
+ rmt::Vector mJumpTarget;
+ pLocator->GetLocation( &mJumpTarget );
+ rmt::Vector position;
+ pCharacter->GetPosition( position );
+ mJumpTarget.Sub( position );
+
+ pCharacter->GetActionController()->Clear();
+ pCharacter->GetJumpLocomotionAction()->Reset( mJumpTarget, false );
+ Sequencer* seq = pCharacter->GetActionController()->GetNextSequencer();
+ seq->BeginSequence();
+ seq->AddAction(pCharacter->GetJumpLocomotionAction());
+ seq->EndSequence();
+}
+
+static float sfCollectibleRespawnTime = 10.0f;
+/*
+==============================================================================
+Collectible::Collectible
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+Collectible::Collectible( ActionEventLocator* pActionEventLocator )
+:
+ActionEventHandler( pActionEventLocator ),
+mAnimatedIcon( NULL ),
+mbCollected( false )
+//mpGameObject( 0 )
+{
+}
+/*
+==============================================================================
+Collectible::~Collectible
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: Collectible
+
+=============================================================================
+*/
+Collectible::~Collectible()
+{
+ //tRefCounted::Release( mpGameObject );
+ if ( mAnimatedIcon )
+ {
+ delete mAnimatedIcon;
+ }
+}
+/*
+==============================================================================
+Collectible::Create
+==============================================================================
+Description: Comment
+
+Parameters: ( tEntityStore* inStore )
+
+Return: bool
+
+=============================================================================
+*/
+bool Collectible::Create( tEntityStore* inStore )
+{
+ bool bCreated = false;
+
+ return bCreated;
+/*
+ // We can use this same tCompositeDrawable pointer, because what is really unique
+ // is the tPose, not the tCompositeDrawable.
+ //
+ tCompositeDrawable* pDrawable = p3d::find<tCompositeDrawable>( mpActionEventLocator->GetObjName() );
+ if ( pDrawable )
+ {
+ tMultiController* pAnimController = p3d::find<tMultiController>( pDrawable->GetUID() );
+ if ( pAnimController )
+ {
+ ActionButton::AnimEntityDSGWrapper* pGameObject = new ActionButton::AnimEntityDSGWrapper;
+
+ rmt::Matrix transform;
+ rmt::Vector position;
+ mpActionEventLocator->GetLocation( &position );
+ transform.Identity( );
+ transform.FillTranslate( position );
+ pGameObject->SetDrawable( pDrawable );
+ pGameObject->SetPose( pDrawable->GetSkeleton()->NewPose() );
+ pGameObject->SetAnimController( pAnimController );
+ pGameObject->SetTransform( transform );
+
+ // Set the start state of all animations requiring a trigger to off.
+ //
+ // This will stop the animation until Action executes.
+ //
+ pGameObject->SetAnimationDirection( 0.0f );
+ // Reset the animation to the initial frame.
+ //
+ pGameObject->GetAnimController( )->SetFrame( 0.0f );
+
+ // Play the Collectible animation.
+ //
+ pGameObject->SetAnimationDirection( 1.0f );
+
+ tRefCounted::Assign( mpGameObject, pGameObject );
+ // Temporary.
+ //
+ GetRenderManager()->mpLayer(RenderEnums::LevelSlot)->AddGuts((tDrawable*)(pGameObject) );
+ bCreated = true;
+ }
+ else
+ {
+#ifdef RAD_DEBUG
+ char error[256];
+ sprintf( error, "Shouldn't %s have a properly named multicontroller?\n", mpActionEventLocator->GetObjName());
+ rAssert(false);
+#endif
+ }
+ }
+ return bCreated;
+*/
+}
+
+//=============================================================================
+// Collectible::OnReset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void )
+//
+// Return: void
+//
+//=============================================================================
+void Collectible::OnReset( void )
+{
+/*
+ // Set the start state of all animations requiring a trigger to off.
+ //
+ // This will stop the animation until Action executes.
+ //
+ mpGameObject->SetAnimationDirection( 0.0f );
+ // Reset the animation to the initial frame.
+ //
+ mpGameObject->GetAnimController( )->SetFrame( 0.0f );
+
+ // Play the Collectible animation.
+ //
+ mpGameObject->SetAnimationDirection( 1.0f );
+
+ mbCollected = false;
+ mpGameObject->SetVisible( true );
+ mpGameObject->UpdateVisibility( );
+*/
+ //Kinda slow, but guarantees that the object will only be in once.
+ mAnimatedIcon->ShouldRender( false );
+ mAnimatedIcon->ShouldRender( true );
+}
+
+
+//Chuck: added this to make wrenches respawn since I didnt see a method to set mbCollected to false;
+
+void Collectible::ResetCollectible()
+{
+ mbCollected = false;
+}
+
+
+/*
+
+
+
+
+==============================================================================
+Collectible::OnUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Collectible::OnUpdate( float timeins )
+{
+ if ( IsCollected() )
+ {
+ if ( ShouldRespawn() )
+ {
+ if ( IsRespawnTimeExpired( ) )
+ {
+ mbCollected = false;
+ SetRespawnTime( sfCollectibleRespawnTime );
+
+ // Reactivate the trigger volumes.
+ //
+ mpActionEventLocator->SetFlag( Locator::ACTIVE, true );
+
+ // Allow display.
+ //
+ //mpGameObject->SetVisible( true );
+ mAnimatedIcon->ShouldRender( true );
+ }
+ else
+ {
+ UpdateRespawnTime( timeins );
+ }
+ }
+ }
+ else
+ {
+ //mpGameObject->GetAnimController()->Advance( timeins * 1000.0f * mpGameObject->GetAnimationDirection() );
+ mAnimatedIcon->Update( rmt::FtoL(timeins * 1000.0f) );
+ }
+}
+/*
+==============================================================================
+Collectible::OnEnter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void Collectible::OnEnter( Character* pCharacter )
+{
+ if ( !IsCollected() )
+ {
+ // Make it disappear.
+ //
+ //mpGameObject->SetVisible( false );
+ mAnimatedIcon->ShouldRender( false );
+
+ // Deactivate the trigger volumes.
+ //
+ mpActionEventLocator->SetFlag( Locator::ACTIVE, false );
+ // Maybe play an animation?
+ //
+
+ // Maybe play a sound effect.
+ //
+
+ // We collected it.
+ //
+ mbCollected = true;
+
+ GetEventManager()->TriggerEvent( EVENT_COLLECT_OBJECT );
+ }
+}
+
+void Collectible::OnExit( Character* pCharacter )
+{
+ // Do nothing.
+ //
+}
+
+/*
+==============================================================================
+RespawnCollectible::RespawnCollectible
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+RespawnCollectible::RespawnCollectible( ActionEventLocator* pActionEventLocator )
+:
+Collectible( pActionEventLocator ),
+mfRespawnTime( sfCollectibleRespawnTime )
+{
+}
+/*
+==============================================================================
+RespawnCollectible::~RespawnCollectible
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: RespawnCollectible
+
+=============================================================================
+*/
+RespawnCollectible::~RespawnCollectible()
+{
+}
+/*
+==============================================================================
+CollectibleFood::CollectibleFood
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+float CollectibleFood::sfSmallTurboGain = 0.1f;
+float CollectibleFood::sfLargeTurboGain = 0.3f;
+CollectibleFood::CollectibleFood( ActionEventLocator* pActionEventLocator, float fTurboGain )
+:
+RespawnCollectible( pActionEventLocator ),
+mfTurboGain( fTurboGain )
+{
+}
+/*
+==============================================================================
+CollectibleFood::~CollectibleFood
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: CollectibleFood
+
+=============================================================================
+*/
+CollectibleFood::~CollectibleFood()
+{
+}
+/*
+==============================================================================
+CollectibleFood::OnEnter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void CollectibleFood::OnEnter( Character* pCharacter )
+{
+ if ( !IsCollected() )
+ {
+ // used to add turbo here, is this class even neccesary any more
+
+ // Maybe play an animation?
+ //
+
+ // Maybe play a sound effect.
+ //
+ }
+ Collectible::OnEnter( pCharacter );
+}
+
+AnimatedIcon* CollectibleCard::mAnimatedCollectionThing = NULL;
+unsigned int CollectibleCard::mCollectibleCardCount = 0;
+
+
+/*
+==============================================================================
+CollectibleCard::CollectibleCard
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+CollectibleCard::CollectibleCard( ActionEventLocator* pActionEventLocator )
+:
+Collectible( pActionEventLocator )
+{
+ mCollectibleCardCount++;
+}
+/*
+==============================================================================
+CollectibleCard::~CollectibleCard
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: CollectibleCard
+
+=============================================================================
+*/
+CollectibleCard::~CollectibleCard()
+{
+ mCollectibleCardCount--;
+
+ if ( mCollectibleCardCount == 0 )
+ {
+ delete mAnimatedCollectionThing;
+ mAnimatedCollectionThing = NULL;
+ }
+}
+
+//=============================================================================
+// CollectibleCard::Create
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tEntityStore* inStore )
+//
+// Return: bool
+//
+//=============================================================================
+bool CollectibleCard::Create( tEntityStore* inStore )
+{
+MEMTRACK_PUSH_GROUP( "Collectible - Cards" );
+ bool bCreated = false;
+
+ // We can use this same tCompositeDrawable pointer, because what is really unique
+ // is the tPose, not the tCompositeDrawable.
+ //
+ bool found = false;
+ tUID nameUID = tEntity::MakeUID(mpActionEventLocator->GetObjName());
+ unsigned int i;
+ for ( i = 0; i < ActionButtonManager::MAX_ACTIONS; ++i )
+ {
+ if ( GetActionButtonManager()->GetActionByIndex( i ) )
+ {
+ ActionButton::ButtonHandler* bh = GetActionButtonManager()->GetActionByIndex( i );
+
+ if ( bh && bh->GetType() == ActionButton::ButtonHandler::COLLECTOR_CARD )
+ {
+ rAssert( dynamic_cast<ActionButton::CollectibleCard*>(bh) != NULL );
+ ActionButton::CollectibleCard* cc = static_cast<ActionButton::CollectibleCard*>(bh);
+ rAssert( cc );
+ if ( tEntity::MakeUID(cc->GetActionEventLocator()->GetObjName()) == nameUID )
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+
+ //Test to make sure the player doesn't already have this one.
+ if ( !found &&
+ !GetCardGallery()->IsCardCollected( nameUID ) )
+ {
+ // make sure this card belongs in the current level
+ //
+ CardsDB* cardsDB = GetCardGallery()->GetCardsDB();
+ rAssert( cardsDB != NULL );
+ Card* card = cardsDB->GetCardByName( nameUID );
+ if( card != NULL &&
+ static_cast<int>( card->GetLevel() ) != (GetGameplayManager()->GetCurrentLevelIndex() + 1) )
+ {
+ rTunePrintf( "*** Invalid card found: %d-%d\n", card->GetLevel(), card->GetLevelID() );
+ rTuneAssertMsg( false, "This card does not belong in this level! Please go tell Sheik!" );
+ }
+
+ mAnimatedIcon = new(GMA_LEVEL_OTHER) AnimatedIcon();
+
+ rmt::Vector pos;
+ mpActionEventLocator->GetLocation( &pos );
+ mAnimatedIcon->Init( "card_idle", pos, true );
+
+ if ( mAnimatedCollectionThing == NULL )
+ {
+ mAnimatedCollectionThing = new(GMA_LEVEL_OTHER) AnimatedIcon();
+ mAnimatedCollectionThing->Init( "card_collect", rmt::Vector(0.0f, 0.0f, 0.0f), false, true );
+ }
+
+ bCreated = true;
+ }
+MEMTRACK_POP_GROUP( "Collectible - Cards" );
+
+ return bCreated;
+}
+
+/*
+==============================================================================
+CollectibleCard::OnEnter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void CollectibleCard::OnEnter( Character* pCharacter )
+{
+ if ( !IsCollected() )
+ {
+ // Talk to the collector card DB.
+ //
+ Card* collectedCard = GetCardGallery()->AddCollectedCardByName( tEntity::MakeUID(mpActionEventLocator->GetObjName()) );
+ if( collectedCard != NULL )
+ {
+ // Update collected cards in character sheet
+ //
+ GetCharacterSheetManager()->AddCard( static_cast<RenderEnums::LevelEnum>( collectedCard->GetLevel() - 1 ),
+ collectedCard->GetLevelID() - 1 );
+ }
+
+ // Maybe play an animation?
+ //
+ mAnimatedCollectionThing->Reset();
+ rmt::Vector pos;
+ mpActionEventLocator->GetLocation( &pos );
+ mAnimatedCollectionThing->ShouldRender( true );
+ mAnimatedCollectionThing->Move( pos );
+
+ // Maybe play a sound effect.
+ //
+ GetEventManager()->TriggerEvent( EVENT_CARD_COLLECTED, reinterpret_cast<void*>( collectedCard ) );
+
+ //Rumble the controller.
+ int playerID = mpActionEventLocator->GetPlayerID();
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( playerID );
+ if ( controllerID != -1 )
+ {
+ GetInputManager()->GetController( controllerID )->ApplyEffect( RumbleEffect::MEDIUM, 250 );
+ GetInputManager()->GetController( controllerID )->ApplyEffect( RumbleEffect::HARD2, 250 );
+ }
+ }
+ Collectible::OnEnter( pCharacter );
+}
+
+//=============================================================================
+// CollectibleCard::UpdateThing
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleCard::UpdateThing( unsigned int milliseconds )
+{
+ if ( mAnimatedCollectionThing )
+ {
+ mAnimatedCollectionThing->Update( milliseconds );
+ }
+}
+
+
+
+
+//Intializing static stuff for WrenchIcon
+
+unsigned int ActionButton::WrenchIcon::mWrenchCount = 0;
+AnimatedIcon* ActionButton::WrenchIcon::mAnimatedCollectionThing = NULL;
+
+
+
+/*
+==============================================================================
+WrenchIcon::WrenchIcon
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ToggleAnim
+
+=============================================================================
+*/
+WrenchIcon::WrenchIcon ( ActionEventLocator* pActionEventLocator ) :
+Collectible( pActionEventLocator ),
+RespawnEntity(RespawnEntity::eWrench)
+{
+ mWrenchCount++;
+}
+/*
+==============================================================================
+WrenchIcon::~WrenchIcon
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: WrenchIcon
+
+=============================================================================
+*/
+WrenchIcon::~WrenchIcon()
+{
+ mWrenchCount--;
+
+ if (mWrenchCount ==0)
+ {
+ delete mAnimatedCollectionThing;
+ mAnimatedCollectionThing = NULL;
+ }
+}
+
+//=============================================================================
+// WrenchIcon::Create
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tEntityStore* inStore )
+//
+// Return: bool
+//
+//=============================================================================
+bool WrenchIcon::Create( tEntityStore* inStore )
+{
+MEMTRACK_PUSH_GROUP( "Wrench - Icon" );
+ bool bCreated = false;
+ // We can use this same tCompositeDrawable pointer, because what is really unique
+ // is the tPose, not the tCompositeDrawable.
+ //
+ bool found = false;
+ tUID nameUID = tEntity::MakeUID(mpActionEventLocator->GetObjName());
+ unsigned int i;
+ for ( i = 0; i < ActionButtonManager::MAX_ACTIONS; ++i )
+ {
+ if ( GetActionButtonManager()->GetActionByIndex( i ) )
+ {
+ ActionButton::ButtonHandler* bh = GetActionButtonManager()->GetActionByIndex( i );
+
+ if ( bh && bh->GetType() == ActionButton::ButtonHandler::WRENCH_ICON )
+ {
+ rAssert( dynamic_cast<ActionButton::WrenchIcon*>(bh) != NULL );
+ ActionButton::WrenchIcon* pWrenchIcon = static_cast<ActionButton::WrenchIcon*>(bh);
+ rAssert( pWrenchIcon );
+ if ( tEntity::MakeUID(pWrenchIcon->GetActionEventLocator()->GetObjName()) == nameUID )
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+
+
+ mAnimatedIcon = new(GMA_LEVEL_OTHER) AnimatedIcon();
+
+ rmt::Vector pos;
+ mpActionEventLocator->GetLocation( &pos );
+ mAnimatedIcon->Init( "wrench", pos, true );
+ mAnimatedIcon->ShouldRender(true);
+
+ if ( mAnimatedCollectionThing == NULL )
+ {
+ mAnimatedCollectionThing = new(GMA_LEVEL_OTHER) AnimatedIcon();
+ mAnimatedCollectionThing->Init( "wrench_collect", rmt::Vector(0.0f, 0.0f, 0.0f), false, true );
+ }
+
+ bCreated = true;
+
+MEMTRACK_POP_GROUP("Wrench - Icon");
+
+ return bCreated;
+}
+
+/*
+==============================================================================
+WrenchIcon::OnEnter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void WrenchIcon::OnEnter( Character* pCharacter )
+{
+ if ( !IsCollected() )
+ {
+
+
+ // Maybe play an animation?
+ mAnimatedCollectionThing->Reset();
+ rmt::Vector pos;
+ mpActionEventLocator->GetLocation( &pos );
+ mAnimatedCollectionThing->ShouldRender( true );
+ mAnimatedCollectionThing->Move( pos );
+
+ // Maybe play a sound effect.
+ //
+
+ //check which context we're in. If we are in regular gameplay then trigger Repair Car Event.
+ if ( GetGameFlow()->GetCurrentContext() == CONTEXT_GAMEPLAY)
+ {
+ GetEventManager()->TriggerEvent(EVENT_REPAIR_CAR);
+ }
+ //we must be in super sprint mode then just repair the car since we arent going to summon the helper bee thingy
+ else
+ {
+ int playerid = mpActionEventLocator->GetPlayerID();
+
+ GetAvatarManager()->GetAvatarForPlayer(playerid)->GetVehicle()->ResetDamageState();
+ }
+ //Fire off a event for the sound manager
+ GetEventManager()->TriggerEvent(EVENT_COLLECTED_WRENCH);
+
+
+ }
+ Collectible::OnEnter( pCharacter );
+ //update the respawnentity
+ RespawnEntity::EntityCollected();
+}
+
+//=============================================================================
+// WrenchIcon::UpdateThing
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void WrenchIcon::UpdateThing( unsigned int milliseconds )
+{
+ if ( mAnimatedCollectionThing != NULL)
+ {
+ mAnimatedCollectionThing->Update( milliseconds );
+ }
+
+}
+
+
+void WrenchIcon::Update(float timeins)
+{
+ unsigned int timeinms=0;
+ timeinms = rmt::FtoL(timeins * 1000.0f);
+ mAnimatedIcon->Update(timeinms);
+ RespawnEntity::Update(timeinms);
+ //check if entity should respawn.
+ if (RespawnEntity::ShouldEntityRespawn ())
+ {
+ //reset the collectible class created by trav and cary .
+ Collectible::Reset();
+ Collectible::ResetCollectible();
+ Collectible::mpActionEventLocator->SetFlag(Locator::ACTIVE,true);
+ Collectible::mAnimatedIcon->ShouldRender(true);
+
+ }
+
+}
+
+
+
+
+//Intializing static stuff for NitroIcon
+unsigned int ActionButton::NitroIcon::mNitroCount = 0;
+AnimatedIcon* ActionButton::NitroIcon::mAnimatedCollectionThing = NULL;
+
+
+
+/*
+==============================================================================
+NitroIcon::NitroIcon
+==============================================================================
+Description: Create the Nitro
+
+Parameters:
+
+Return:
+
+=============================================================================
+*/
+NitroIcon::NitroIcon ( ActionEventLocator* pActionEventLocator ) :
+Collectible( pActionEventLocator ),
+RespawnEntity(RespawnEntity::eNitro)
+{
+ mNitroCount++;
+}
+/*
+==============================================================================
+NitroIcon::~NitroIcon
+==============================================================================
+Description: Destructor
+
+Parameters: ()
+
+Return:
+
+=============================================================================
+*/
+NitroIcon::~NitroIcon()
+{
+ mNitroCount--;
+
+ if (mNitroCount ==0) //if no more instances left deleted the static animated class
+ {
+ delete mAnimatedCollectionThing;
+ mAnimatedCollectionThing = NULL;
+ }
+}
+
+//=============================================================================
+// NitroIcon::Create
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tEntityStore* inStore )
+//
+// Return: bool
+//
+//=============================================================================
+bool NitroIcon::Create( tEntityStore* inStore )
+{
+MEMTRACK_PUSH_GROUP( "Nitro - Icon" );
+ bool bCreated = false;
+ // We can use this same tCompositeDrawable pointer, because what is really unique
+ // is the tPose, not the tCompositeDrawable.
+ //
+ bool found = false;
+ tUID nameUID = tEntity::MakeUID(mpActionEventLocator->GetObjName());
+ unsigned int i;
+ for ( i = 0; i < ActionButtonManager::MAX_ACTIONS; ++i )
+ {
+ if ( GetActionButtonManager()->GetActionByIndex( i ) )
+ {
+ ActionButton::ButtonHandler* bh = GetActionButtonManager()->GetActionByIndex( i );
+
+ if ( bh && bh->GetType() == ActionButton::ButtonHandler::NITRO_ICON )
+ {
+ rAssert( dynamic_cast<ActionButton::NitroIcon*>(bh) != NULL );
+ ActionButton::NitroIcon* pNitroIcon = static_cast<ActionButton::NitroIcon*>(bh);
+ rAssert( pNitroIcon );
+ if ( tEntity::MakeUID(pNitroIcon->GetActionEventLocator()->GetObjName()) == nameUID )
+ {
+ found = true;
+ break;
+ }
+ }
+ }
+ }
+
+
+ mAnimatedIcon = new(GMA_LEVEL_OTHER) AnimatedIcon();
+
+ rmt::Vector pos;
+ mpActionEventLocator->GetLocation( &pos );
+ mAnimatedIcon->Init( "nitro", pos, true ); //Hack!
+
+ if ( mAnimatedCollectionThing == NULL )
+ {
+ mAnimatedCollectionThing = new(GMA_LEVEL_OTHER) AnimatedIcon();
+ mAnimatedCollectionThing->Init( "wrench_collect", rmt::Vector(0.0f, 0.0f, 0.0f), false, true );
+ }
+
+ bCreated = true;
+
+MEMTRACK_POP_GROUP("Nitro - Icon");
+
+ return bCreated;
+}
+
+/*
+==============================================================================
+NitroIcon::OnEnter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void NitroIcon::OnEnter( Character* pCharacter )
+{
+ if ( !IsCollected() )
+ {
+ // Maybe play an animation?
+ mAnimatedCollectionThing->Reset();
+ rmt::Vector pos;
+ mpActionEventLocator->GetLocation( &pos );
+ mAnimatedCollectionThing->ShouldRender( true );
+ mAnimatedCollectionThing->Move( pos );
+
+ int playerid = mpActionEventLocator->GetPlayerID();
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer( playerid );
+
+ /*
+ // TODO:
+ // Maybe play a sound effect? Pass in the avatar pointer? Ask Esan
+ ::GetEventManager()->TriggerEvent( EVENT_COLLECTED_NITRO, player );
+ */
+ Vehicle* playerVehicle = player->GetVehicle();
+ if( playerVehicle )
+ {
+ // increase the nitro count...
+ playerVehicle->mNumTurbos++;
+ }
+ }
+ Collectible::OnEnter( pCharacter );
+ //update the respawnentity
+ RespawnEntity::EntityCollected();
+}
+
+//=============================================================================
+// NitroIcon::UpdateThing
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void NitroIcon::UpdateThing( unsigned int milliseconds )
+{
+ if ( mAnimatedCollectionThing != NULL)
+ {
+ mAnimatedCollectionThing->Update( milliseconds );
+ }
+
+}
+
+
+void NitroIcon::Update(float timeins)
+{
+ unsigned int timeinms=0;
+ timeinms = rmt::FtoL(timeins * 1000.0f);
+ mAnimatedIcon->Update(timeinms);
+ RespawnEntity::Update(timeinms);
+ //check if entity should respawn.
+ if (RespawnEntity::ShouldEntityRespawn ())
+ {
+ //reset the collectible class created by trav and cary .
+ Collectible::Reset();
+ Collectible::ResetCollectible();
+ Collectible::mpActionEventLocator->SetFlag(Locator::ACTIVE,true);
+ Collectible::mAnimatedIcon->ShouldRender(true);
+
+ }
+
+}
+
+//=============================================================================
+// GenericEventButtonHandler::GenericEventButtonHandler
+//=============================================================================
+// Description: Constructor
+//
+// Parameters: ( EventLocator* pEventLocator, EventEnum event )
+//
+// Return:
+//
+//=============================================================================
+GenericEventButtonHandler::GenericEventButtonHandler( EventLocator* pEventLocator, EventEnum event ) :
+ mpEventLocator( NULL ),
+ mEvent( event ),
+ mEventData( NULL )
+{
+ SetEventLocator( pEventLocator );
+}
+
+//=============================================================================
+// ::~GenericEventButtonHandler
+//=============================================================================
+// Description: Destructor
+//
+// Parameters: ()
+//
+// Return:
+//
+//=============================================================================
+GenericEventButtonHandler::~GenericEventButtonHandler()
+{
+ if ( mpEventLocator )
+ {
+ mpEventLocator->Release();
+ mpEventLocator = NULL;
+ }
+};
+
+//=============================================================================
+// GenericEventButtonHandler::NewAction
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventLocator* pEventLocator, EventEnum event, GameMemoryAllocator alloc )
+//
+// Return: ButtonHandler
+//
+//=============================================================================
+ButtonHandler* GenericEventButtonHandler::NewAction( EventLocator* pEventLocator, EventEnum event, GameMemoryAllocator alloc )
+{
+ return new (alloc) GenericEventButtonHandler( pEventLocator, event );
+}
+
+//=============================================================================
+// GenericEventButtonHandler::SetEventLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventLocator* pEventLocator )
+//
+// Return: void
+//
+//=============================================================================
+void GenericEventButtonHandler::SetEventLocator( EventLocator* pEventLocator )
+{
+ tRefCounted::Assign( mpEventLocator, pEventLocator );
+}
+
+//=============================================================================
+// GenericEventButtonHandler::OnButtonPressed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter, Sequencer* pSeq )
+//
+// Return: bool
+//
+//=============================================================================
+bool GenericEventButtonHandler::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ CGuiScreenHud* currentHUD = GetCurrentHud();
+ if( (currentHUD != NULL && !currentHUD->IsActive()) || GetHitnRunManager()->BustingPlayer())
+ {
+ // if HUD is not active, ignore button press
+ //
+ return false;
+ }
+
+ if ( pCharacter->GetController()->GetIntention() == CharacterController::DoAction )
+ {
+ GetEventManager()->TriggerEvent( mEvent, mEventData );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+//=============================================================================
+// TeleportAction::TeleportAction
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ActionEventLocator* pActionEventLocator )
+//
+// Return: TeleportAction
+//
+//=============================================================================
+TeleportAction::TeleportAction( ActionEventLocator* pActionEventLocator ) :
+ ActionEventHandler( pActionEventLocator )
+{
+}
+
+//=============================================================================
+// ::~TeleportAction
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: TeleportAction
+//
+//=============================================================================
+TeleportAction::~TeleportAction()
+{
+ GetEventManager()->RemoveListener( this, EVENT_GUI_IRIS_WIPE_CLOSED );
+}
+
+//=============================================================================
+// TeleportAction::Create
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tEntityStore* inStore = 0 )
+//
+// Return: bool
+//
+//=============================================================================
+bool TeleportAction::Create( tEntityStore* inStore )
+{
+ return true;
+}
+
+//=============================================================================
+// TeleportAction::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void TeleportAction::HandleEvent( EventEnum id, void* pEventData )
+{
+ if ( id == EVENT_GUI_IRIS_WIPE_CLOSED )
+ {
+ GetEventManager()->RemoveListener( this, EVENT_GUI_IRIS_WIPE_CLOSED );
+
+ //Send the teleporting event
+ GetEventManager()->TriggerEvent( EVENT_TAKING_TELEPORT, mpActionEventLocator );
+
+ //Teleport the player to the locator
+ rmt::Vector pos;
+ mpActionEventLocator->GetLocation( &pos );
+
+ rmt::Matrix mat = mpActionEventLocator->GetMatrix();
+ mat.IdentityTranslation();
+
+ rmt::Vector z( 1.0f, 0.0f, 0.0f );
+ z.Transform( mat );
+ float facing, magWaste;
+
+ rmt::CartesianToPolar( z.x, z.z, &magWaste, &facing );
+
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetCharacter()->RelocateAndReset( pos, facing, true );
+#ifdef RAD_WIN32
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( SuperCam::ON_FOOT_CAM, SuperCamCentral::CUT, 0 );
+#else
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( SuperCam::WALKER_CAM, SuperCamCentral::CUT, 0 );
+#endif
+ GetSuperCamManager()->GetSCC( 0 )->DoCameraCut();
+
+ //Wipe out when we are finished moving.
+ GetGuiSystem()->HandleMessage( GUI_MSG_START_IRIS_WIPE_OPEN );
+
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Resume();
+ }
+}
+
+//=============================================================================
+// TeleportAction::OnButtonPressed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter, Sequencer* pSeq )
+//
+// Return: bool
+//
+//=============================================================================
+bool TeleportAction::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ //Do an iris wipe in
+ GetGuiSystem()->HandleMessage( GUI_MSG_START_IRIS_WIPE_CLOSE );
+
+ //add listener
+ GetEventManager()->AddListener( this, EVENT_GUI_IRIS_WIPE_CLOSED );
+
+ //Pause gameplay
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Suspend();
+ return true;
+}
+
+//=============================================================================
+// TeleportAction::OnEnter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter )
+//
+// Return: void
+//
+//=============================================================================
+void TeleportAction::OnEnter( Character* pCharacter )
+{
+ GetEventManager()->TriggerEvent( EVENT_ENTERED_TELEPORT_PAD, mpActionEventLocator );
+}
+
+//=============================================================================
+// TeleportAction::OnExit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter )
+//
+// Return: void
+//
+//=============================================================================
+void TeleportAction::OnExit( Character* pCharacter )
+{
+ GetEventManager()->TriggerEvent( EVENT_EXITED_TELEPORT_PAD, mpActionEventLocator );
+}
+
+//===============================================================================
+
+bool PurchaseReward::sbEnabled = true;
+
+//=============================================================================
+// PurchaseReward::PurchaseReward
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ActionEventLocator* pActionEventLocator )
+//
+// Return: PurchaseReward
+//
+//=============================================================================
+PurchaseReward::PurchaseReward( ActionEventLocator* pActionEventLocator ) :
+ ActionEventHandler( pActionEventLocator ),
+ mAllPurchased( false ),
+ mIsActive( true ),
+ mHudMapIconID( -1 )
+{
+}
+
+//=============================================================================
+// ::~PurchaseReward
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: PurchaseReward
+//
+//=============================================================================
+PurchaseReward::~PurchaseReward()
+{
+ if( mHudMapIconID != -1 )
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->GetHudMap( 0 )->UnregisterIcon( mHudMapIconID );
+ mHudMapIconID = -1;
+ }
+ }
+}
+
+//=============================================================================
+// PurchaseReward::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float timeins )
+//
+// Return: void
+//
+//=============================================================================
+void PurchaseReward::OnUpdate( float timeins )
+{
+ if ( sbEnabled && mIsActive && !mAllPurchased && GetGameplayManager()->IsSundayDrive() )
+ {
+ mIcon.Update( rmt::FtoL(timeins * 1000.0f) );
+ }
+
+ if ( mIsActive && ( mAllPurchased || !sbEnabled || !GetGameplayManager()->IsSundayDrive() ) )
+ {
+ //Time to shut this one down.
+ mIcon.ShouldRender( false );
+ mIsActive = false;
+
+ mpActionEventLocator->SetFlag( Locator::ACTIVE, false );
+
+ if( mHudMapIconID != -1 )
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->GetHudMap( 0 )->UnregisterIcon( mHudMapIconID );
+ mHudMapIconID = -1;
+ }
+ }
+ }
+ else if ( !mIsActive && !mAllPurchased && sbEnabled && GetGameplayManager()->IsSundayDrive() )
+ {
+ mIcon.ShouldRender( true );
+ mIsActive = true;
+
+ mpActionEventLocator->SetFlag( Locator::ACTIVE, true );
+
+ rAssert( mHudMapIconID == -1 );
+ if( mHudMapIconID == -1 )
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ mHudMapIconID = currentHud->GetHudMap( 0 )->RegisterIcon( HudMapIcon::ICON_PURCHASE_CENTRE,
+ rmt::Vector( 0.0f, 0.0f, 0.0f ),
+ mpActionEventLocator );
+ rAssert( mHudMapIconID != -1 );
+ }
+ }
+ }
+}
+
+//=============================================================================
+// PurchaseReward::CanEnable
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool PurchaseReward::CanEnable() const
+{
+ return (!mAllPurchased && sbEnabled && GetGameplayManager()->IsSundayDrive() );
+}
+
+
+//=============================================================================
+// PurchaseCar::PurchaseCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ActionEventLocator* pActionEventLocator )
+//
+// Return: PurchaseCar
+//
+//=============================================================================
+PurchaseCar::PurchaseCar( ActionEventLocator* pActionEventLocator ) :
+ PurchaseReward( pActionEventLocator )
+{
+ rmt::Vector pos;
+ pActionEventLocator->GetLocation( &pos );
+ mIcon.Init( "dollar", pos ); //TODO: get the real icon in.
+
+ //
+ // Set up transitions
+ //
+ m_DisableInput.SetState( Input::ACTIVE_FRONTEND );
+ m_GotoLetterbox.SetScreen( CGuiWindow::GUI_SCREEN_ID_LETTER_BOX );
+ m_GotoLetterbox.SetWindowOptions( CLEAR_WINDOW_HISTORY );
+ m_ContextSwitch.SetContext( CONTEXT_PAUSE );
+ m_WaitForConversationDone.SetEvent( EVENT_CONVERSATION_DONE );
+ m_GotoHud.SetScreen( CGuiWindow::GUI_SCREEN_ID_HUD );
+ m_GotoHud.SetWindowOptions( FORCE_WINDOW_CHANGE_IMMEDIATE );
+ m_GotoScreen.SetScreen( CGuiWindow::GUI_SCREEN_ID_PURCHASE_REWARDS );
+ m_GotoScreen.SetWindowOptions( CLEAR_WINDOW_HISTORY );
+
+ m_GotoLetterbox.SetNextTransition( m_DisableInput );
+ m_DisableInput.SetNextTransition( m_WaitForConversationDone );
+ m_WaitForConversationDone.SetNextTransition( m_ContextSwitch );
+// m_GotoHud.SetNextTransition( m_ContextSwitch ); // TC: no need to go to the HUD
+ m_ContextSwitch.SetNextTransition( m_GotoScreen );
+ m_GotoScreen.SetNextTransition( NULL );
+}
+
+//=============================================================================
+// ::~PurchaseCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: PurchaseCar
+//
+//=============================================================================
+PurchaseCar::~PurchaseCar()
+{
+}
+
+//=============================================================================
+// PurchaseCar::Create
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tEntityStore* inStore )
+//
+// Return: bool
+//
+//=============================================================================
+bool PurchaseCar::Create( tEntityStore* inStore )
+{
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ mHudMapIconID = currentHud->GetHudMap( 0 )->RegisterIcon( HudMapIcon::ICON_PURCHASE_CENTRE,
+ rmt::Vector( 0.0f, 0.0f, 0.0f ),
+ mpActionEventLocator );
+ }
+
+ //Should findout and fill in all the Car selection data here.
+ //Tony?
+ return true;
+}
+
+//=============================================================================
+// PurchaseCar::OnButtonPressed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter )
+//
+// Return: bool
+//
+//=============================================================================
+bool PurchaseCar::OnButtonPressed( Character* pCharacter )
+{
+ if( GetGuiSystem()->GetInGameManager()->IsEnteringPauseMenu() )
+ {
+ // if about to enter pause menu, ignore button press
+ //
+ return false;
+ }
+
+ int level;
+ GameplayManager* gameplayMgr = NULL;
+
+ //Purchase and load the car.
+ //Tony?
+ // switch to pause context
+ //
+
+ if ( mpActionEventLocator->GetUID() == GIL )
+ {
+ m_ContextSwitch.Activate();
+ m_GotoScreen.SetParam1( Merchandise::SELLER_GIL );
+ m_GotoScreen.Activate();
+
+ //
+ // Make Gil say something
+ //
+ GetEventManager()->TriggerEvent( EVENT_HAGGLING_WITH_GIL, GetCharacterManager()->GetCharacterByName( "reward_gil" ) );
+ }
+ else
+ {
+ m_GotoScreen.SetParam1( Merchandise::SELLER_SIMPSON );
+ //
+ // Start a conversation
+ //
+ gameplayMgr = GetGameplayManager();
+ rAssert( gameplayMgr != NULL );
+
+ level = gameplayMgr->GetCurrentLevelIndex() - RenderEnums::L1;
+ rAssert( ( level >= 0 ) && ( level <= 7 ) );
+
+ if( s_carPurchaseConvNames[level].convName != 0 )
+ {
+ DialogEventData data;
+
+ data.dialogName = s_carPurchaseConvNames[level].convName;
+
+ rAssert( pCharacter != NULL );
+
+ data.charUID1 = s_carPurchaseConvNames[level].sellerName;
+ data.charUID2 = pCharacter->GetUID();
+
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_INIT_DIALOG, &data );
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_START );
+
+ //
+ // make the two characters face one another
+ //
+ Character* c0 = GetCharacterManager()->GetCharacterByName( data.charUID1 );
+ Character* c1 = GetCharacterManager()->GetCharacterByName( data.charUID2 );
+ rAssertMsg( c0 != NULL, "Character's name is incorrect" );
+ rAssertMsg( c1 != NULL, "Character's name is incorrect" );
+ GetPresentationManager()->MakeCharactersFaceEachOther( c0, c1 );
+
+ CGuiScreenLetterBox::SuppressAcceptCancelButtons();
+ CGuiScreenLetterBox::ForceOpen();
+ m_GotoLetterbox.Activate();
+ }
+ else
+ {
+ m_ContextSwitch.Activate();
+ m_GotoScreen.Activate();
+ }
+ }
+
+ //Trickyness to get the carstart locator.
+ // We do some of the work here, then we will trigger an event to do the menu.
+ // The menu will trigger the loading.
+ //
+ tRefCounted::Assign( SummonVehiclePhone::sCallback.mpActionEventLocator, mpActionEventLocator );
+
+
+ return true;
+}
+
+//=============================================================================
+// PurchaseCar::OnEnter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter )
+//
+// Return: void
+//
+//=============================================================================
+void PurchaseCar::OnEnter( Character* pCharacter )
+{
+}
+
+//=============================================================================
+// PurchaseCar::OnExit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter )
+//
+// Return: void
+//
+//=============================================================================
+void PurchaseCar::OnExit( Character* pCharacter )
+{
+}
+
+//=============================================================================
+// PurchaseCar::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float timeins )
+//
+// Return: void
+//
+//=============================================================================
+void PurchaseCar::OnUpdate( float timeins )
+{
+ RenderEnums::LevelEnum currLev = GetGameplayManager()->GetCurrentLevelIndex();
+ RewardsManager* rm = GetRewardsManager();
+
+ Merchandise::eSellerType st = Merchandise::SELLER_GIL;
+ if ( mpActionEventLocator->GetUID() != GIL )
+ {
+ st = Merchandise::SELLER_SIMPSON;
+ }
+
+ //Test to see if the cars are all sold.
+
+ Merchandise* m = rm->FindFirstMerchandise( currLev, st );
+
+ mAllPurchased = true;
+ while ( m != NULL )
+ {
+ if ( !m->RewardStatus() )
+ {
+ //If one isn't purchased, we're not all purchased.
+ mAllPurchased = false;
+ break;
+ }
+
+ m = rm->FindNextMerchandise( currLev, st );
+ }
+
+ if ( mIsActive )
+ {
+ //Follow the character around.
+ const char* charName = mpActionEventLocator->GetJointName();
+ Character* character = GetCharacterManager()->GetCharacterByName( tEntity::MakeUID( charName ) );
+ rAssert( character );
+
+ rmt::Vector pos;
+ character->GetPosition( &pos );
+ mIcon.Move( pos );
+
+ mpActionEventLocator->SetLocation( pos );
+ mpActionEventLocator->GetTriggerVolume( 0 )->SetPosition( pos );
+ }
+
+ PurchaseReward::OnUpdate( timeins );
+}
+
+//=============================================================================
+// PurchaseSkin::PurchaseSkin
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ActionEventLocator* pActionEventLocator )
+//
+// Return: PurchaseSkin
+//
+//=============================================================================
+PurchaseSkin::PurchaseSkin( ActionEventLocator* pActionEventLocator ) :
+ PurchaseReward( pActionEventLocator )
+{
+ rmt::Vector pos;
+ pActionEventLocator->GetLocation( &pos );
+ mIcon.Init( "shirtdollar", pos ); //TODO: get the real icon in.
+}
+
+//=============================================================================
+// ::~PurchaseSkin
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: PurchaseSkin
+//
+//=============================================================================
+PurchaseSkin::~PurchaseSkin()
+{
+}
+
+//=============================================================================
+// PurchaseSkin::Create
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tEntityStore* inStore )
+//
+// Return: bool
+//
+//=============================================================================
+bool PurchaseSkin::Create( tEntityStore* inStore )
+{
+ rmt::Vector pos;
+ rAssert( mpActionEventLocator != NULL );
+ mpActionEventLocator->GetPosition( &pos );
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ mHudMapIconID = currentHud->GetHudMap( 0 )->RegisterIcon( HudMapIcon::ICON_PURCHASE_CENTRE,
+ pos );
+ }
+
+ GetActionButtonManager()->LoadingIntoInterior(); //HACK
+
+ return true;
+}
+
+//=============================================================================
+// PurchaseSkin::OnButtonPressed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter )
+//
+// Return: bool
+//
+//=============================================================================
+bool PurchaseSkin::OnButtonPressed( Character* pCharacter )
+{
+ if( GetGuiSystem()->GetInGameManager()->IsEnteringPauseMenu() )
+ {
+ // if about to enter pause menu, ignore button press
+ //
+ return false;
+ }
+
+ // switch to pause context
+ //
+ GetGameFlow()->SetContext( CONTEXT_PAUSE );
+
+ GetGuiSystem()->GotoScreen( CGuiWindow::GUI_SCREEN_ID_PURCHASE_REWARDS, Merchandise::SELLER_INTERIOR );
+
+ return true;
+}
+
+//=============================================================================
+// PurchaseSkin::OnEnter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter )
+//
+// Return: void
+//
+//=============================================================================
+void PurchaseSkin::OnEnter( Character* pCharacter )
+{
+}
+
+//=============================================================================
+// PurchaseSkin::OnExit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter )
+//
+// Return: void
+//
+//=============================================================================
+void PurchaseSkin::OnExit( Character* pCharacter )
+{
+}
+
+//=============================================================================
+// PurchaseSkin::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float timeins )
+//
+// Return: void
+//
+//=============================================================================
+void PurchaseSkin::OnUpdate( float timeins )
+{
+ RenderEnums::LevelEnum currLev = GetGameplayManager()->GetCurrentLevelIndex();
+ RewardsManager* rm = GetRewardsManager();
+
+ //Test to see if the skins are all sold.
+/*
+ Merchandise* m = rm->FindFirstMerchandise( currLev, Merchandise::SELLER_INTERIOR );
+
+/* mAllPurchased = true;
+
+ while ( m != NULL )
+ {
+ if ( !m->RewardStatus() )
+ {
+ //If one isn't purchased, we're not all purchased.
+ mAllPurchased = false;
+ break;
+ }
+
+ m = rm->FindNextMerchandise( currLev, Merchandise::SELLER_INTERIOR );
+ }
+*/
+ PurchaseReward::OnUpdate( timeins );
+}
+
+}; // namespace ActionButton.
diff --git a/game/code/ai/actionbuttonhandler.h b/game/code/ai/actionbuttonhandler.h
new file mode 100644
index 0000000..8089ec7
--- /dev/null
+++ b/game/code/ai/actionbuttonhandler.h
@@ -0,0 +1,1370 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: actionbuttonhandler.h
+//
+// Description: Blahblahblah
+//
+// History: 08/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef ACTIONBUTTONHANDLER_H
+#define ACTIONBUTTONHANDLER_H
+
+//========================================
+// Nested Includes
+//========================================
+//#include <p3d/refcounted.hpp>
+// Just a tDrawable for now, when AnimEntity is created, we can make it a tRefCount.
+//
+#include <p3d/drawable.hpp>
+#include <p3d/anim/pose.hpp>
+#include <worldsim/character/charactercontroller.h>
+#include <worldsim/vehiclecentral.h>
+#include <memory/srrmemory.h>
+
+
+#include <loading/loadingmanager.h>
+#include <string.h>
+
+#include <mission/animatedIcon.h>
+#include <mission/respawnmanager/respawnentity.h>
+#include <presentation/gui/utility/transitions.h>
+
+#include <events/eventlistener.h>
+#include <events/eventenum.h>
+#include <stateprop/stateprop.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+class Character;
+class Locator;
+class EventLocator;
+class ActionEventLocator;
+class AnimCollisionEntityDSG;
+class Sequencer;
+class tMultiController;
+class tPoseAnimationController;
+class tAnimation;
+class tCompositeDrawable;
+class InteriorEntranceLocator;
+class tEntityStore;
+class InstDynaPhysDSG;
+class AnimatedIcon;
+class InstAnimDynaPhysDSG;
+class RespawnEntity;
+class StatePropDSG;
+
+namespace ActionButton
+{
+
+#define INTERFACE_GameObjectWrapper( terminal ) \
+public: \
+ virtual void UpdateVisibility( void ) ## terminal \
+ virtual float& GetAnimationDirection( void ) ## terminal \
+ virtual void SetAnimationDirection( float fDirection ) ## terminal \
+ virtual tMultiController* GetAnimController( void ) const ## terminal \
+ virtual tCompositeDrawable* GetDrawable( void ) const ## terminal \
+ \
+ virtual void Display( void ) ## terminal \
+ \
+ virtual bool IsManualUpdate( void ) const ## terminal
+
+#define BASE_GameObjectWrapper INTERFACE_GameObjectWrapper(= 0;)
+#define IMPLEMENTS_GameObjectWrapper INTERFACE_GameObjectWrapper(;)
+
+struct IGameObjectWrapper
+:
+// Just a tDrawable for now, when AnimEntity is created, we can make it a tRefCount.
+//
+public tDrawable
+{
+ BASE_GameObjectWrapper;
+};
+
+class AnimCollisionEntityDSGWrapper
+:
+public IGameObjectWrapper
+{
+ IMPLEMENTS_GameObjectWrapper;
+ AnimCollisionEntityDSGWrapper( void );
+ virtual ~AnimCollisionEntityDSGWrapper( void );
+ void SetGameObject( AnimCollisionEntityDSG* pGameObject );
+protected:
+ AnimCollisionEntityDSG* mpGameObject;
+};
+/*
+==============================================================================
+AnimCollisionEntityDSGWrapper::IsManualUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool AnimCollisionEntityDSGWrapper::IsManualUpdate( void ) const
+{
+ return false;
+}
+
+class AnimEntityDSGWrapper
+:
+public IGameObjectWrapper
+{
+ IMPLEMENTS_GameObjectWrapper;
+ AnimEntityDSGWrapper( void );
+ virtual ~AnimEntityDSGWrapper( void );
+
+ void SetDrawable( tCompositeDrawable* pDrawable );
+ void SetPose( tPose* pPose );
+ void SetAnimController( tMultiController* pAnimController );
+ void SetTransform( rmt::Matrix& transform );
+ void SetVisible( bool bVisible )
+ {
+ mbVisible = bVisible;
+ }
+ bool IsVisible( void ) const
+ {
+ return mbVisible;
+ }
+protected:
+ tCompositeDrawable* mpDrawable;
+ tPose* mpPose;
+ tMultiController* mpAnimController;
+ float mfDirection;
+ rmt::Matrix mTransform;
+ bool mbVisible : 1;
+};
+
+/*
+==============================================================================
+AnimEntityDSGWrapper::IsManualUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool AnimEntityDSGWrapper::IsManualUpdate( void ) const
+{
+ return true;
+}
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/24/2002]
+//////////////////////////////////////////////////////////////////////////
+class ButtonHandler
+:
+public tRefCounted
+{
+public:
+ enum Type { MISSION_OBJECTIVE, GET_IN_USER_CAR, SUMMON_PHONE, INTERIOR, GAG, PURCHASE_CAR, PURCHASE_SKIN, GET_IN_CAR, COLLECTOR_CARD, ALIEN_CAMERA, WRENCH_ICON, NITRO_ICON, TELEPORT, OTHER };
+
+ ButtonHandler( void );
+ virtual ~ButtonHandler();
+
+ virtual bool ButtonPressed( Character* pCharacter );
+
+ virtual void Update( float timeins )
+ {
+ OnUpdate( timeins );
+ }
+ virtual void Enter( Character* pCharacter );
+ virtual void Exit( Character* pCharacter );
+
+ virtual bool UsesActionButton( void ) const
+ {
+ return true;
+ }
+
+ virtual bool NeedsUpdate( void ) const
+ {
+ return false;
+ }
+ virtual void Reset( void )
+ {
+ OnReset( );
+ }
+
+
+ virtual Type GetType( void ) { return OTHER; };
+
+ virtual bool IsInstanceEnabled()const { return true; }
+
+protected:
+ virtual void OnReset( void )
+ {
+ }
+ virtual void OnUpdate( float timeins )
+ {
+ }
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+ {
+ return false;
+ }
+ virtual void OnEnter( Character* pCharacter )
+ {
+ }
+ virtual void OnExit( Character* pCharacter )
+ {
+ }
+ virtual bool IsActionButtonPressed( Character* pCharacter );
+ void SetActionButton( CharacterController::eIntention theActionButton )
+ {
+ mActionButton = theActionButton;
+ }
+ CharacterController::eIntention mActionButton;
+private:
+ ButtonHandler( const ButtonHandler& actionbuttonhandler );
+ ButtonHandler& operator=( const ButtonHandler& actionbuttonhandler );
+};
+
+class PropHandler
+:
+public ButtonHandler
+{
+public:
+ PropHandler( void );
+ ~PropHandler( void );
+
+ void SetProp( InstDynaPhysDSG* pProp );
+ InstDynaPhysDSG* GetProp( void ) const;
+protected:
+private:
+ InstDynaPhysDSG* mpProp;
+};
+
+class AttachProp
+:
+public PropHandler
+{
+protected:
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+};
+//////////////////////////////////////////////////////////////////////////
+//
+//
+// TBJ [8/12/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class EnterInterior
+:
+public ButtonHandler
+{
+public:
+ EnterInterior( InteriorEntranceLocator* pEventLocator );
+ virtual ~EnterInterior();
+ void SetLocator ( InteriorEntranceLocator* pLocator );
+
+ virtual Type GetType( void ) { return INTERIOR; };
+
+
+protected:
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+ InteriorEntranceLocator* mpLocator;
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/24/2002]
+//////////////////////////////////////////////////////////////////////////
+class GetInCar
+:
+public ButtonHandler
+{
+public:
+ GetInCar( EventLocator* pEventLocator );
+ virtual ~GetInCar();
+
+ static ButtonHandler* NewAction( EventLocator* pEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) GetInCar( pEventLocator );
+ }
+
+
+ void SetVehicleId( int vehicleId )
+ {
+ mVehicleId = vehicleId;
+ }
+ int GetVehicleId( void ) const
+ {
+ return mVehicleId;
+ }
+ void SetEventLocator( EventLocator* pEventLocator );
+ EventLocator* GetEventLocator( void ) const
+ {
+ return mpEventLocator;
+ }
+
+ virtual Type GetType() { return GET_IN_CAR; };
+
+protected:
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+ EventLocator* mpEventLocator;
+private:
+ int mVehicleId;
+ Character* mCharacter;
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [8/27/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+#ifndef RAD_RELEASE
+ #define ACTIONEVENTHANDLER_DEBUG
+#endif
+class ActionEventHandler
+:
+public ButtonHandler
+{
+public:
+ ActionEventHandler( ActionEventLocator* pActionEventLocator );
+ virtual ~ActionEventHandler();
+ virtual bool Create( tEntityStore* inStore = 0 )
+ {
+ return true;
+ }
+
+ virtual Type GetType( void ) { return OTHER; };
+
+#ifdef ACTIONEVENTHANDLER_DEBUG
+ virtual void Enter( Character* pCharacter );
+#endif //ACTIONEVENTHANDLER_DEBUG
+ void SetActionEventLocator( ActionEventLocator* pActionEventLocator );
+ ActionEventLocator* GetActionEventLocator( void ) const
+ {
+ return mpActionEventLocator;
+ }
+ void SetInstanceEnabled( bool enabled ) { mIsEnabled = enabled; }
+
+protected:
+ ActionEventLocator* mpActionEventLocator;
+ bool mIsEnabled : 1;
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/24/2002]
+//////////////////////////////////////////////////////////////////////////
+class AnimSwitch
+:
+public ActionEventHandler
+{
+public:
+ AnimSwitch( ActionEventLocator* pActionEventLocator );
+ virtual ~AnimSwitch();
+
+ void Init( IGameObjectWrapper* pAnimCollisionEntityDSG, tPose::Joint* pJoint, bool bAttachToJoint );
+ virtual bool ButtonPressed( Character* pCharacter );
+ virtual bool Create( tEntityStore* inStore = 0 );
+ float& GetAnimationDirection( void );
+ void SetAnimationDirection( float fDirection );
+
+ tMultiController* GetAnimController( void ) const;
+
+ void Update( float timeins );
+
+
+ void Destroy( void );
+ // Informs the animswitch that the game object (DSG) has been destroyed
+ // via a collision or other disaster
+ virtual void GameObjectDestroyed(){}
+
+
+ virtual bool UsesActionButton( void ) const
+ {
+ return mActionButton == CharacterController::DoAction;
+ }
+ virtual bool NeedsUpdate( void ) const
+ {
+ if ( mpGameObject )
+ {
+ return mpGameObject->IsManualUpdate( );
+ }
+
+ return false;
+ }
+
+ virtual Type GetType( void ) { return GAG; };
+
+protected:
+ virtual void OnReset( void );
+ virtual void OnUpdate( float timeins ) {}
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq ) = 0;
+ virtual void PositionCharacter( Character* pCharacter, Sequencer* pSeq );
+ virtual void SequenceActions( Character* pCharacter, Sequencer* pSeq );
+ virtual void SetAnimation( Character* pCharacter, Sequencer* pSeq );
+ virtual void OnInit( void ) {}
+ float mfDirection;
+ tPose::Joint* mpJoint;
+ IGameObjectWrapper* mpGameObject;
+ rmt::Vector mStandPosition;
+ const char* mSoundName;
+ const char* mSettingsName;
+ bool mIsMovingSound : 1;
+ bool mbAttachToJoint: 1;
+private:
+
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/24/2002]
+//////////////////////////////////////////////////////////////////////////
+// Toggle the animation on/off.
+//
+class ToggleAnim
+:
+public AnimSwitch
+{
+public:
+ ToggleAnim( ActionEventLocator* pActionEventLocator );
+ virtual ~ToggleAnim();
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) ToggleAnim( pActionEventLocator );
+ }
+protected:
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+private:
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/24/2002]
+//////////////////////////////////////////////////////////////////////////
+// Change the direction of the animation.
+//
+class ReverseAnim
+:
+public ToggleAnim
+{
+public:
+ ReverseAnim( ActionEventLocator* pActionEventLocator )
+ :
+ ToggleAnim( pActionEventLocator )
+ {
+ }
+ virtual ~ReverseAnim() {}
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) ReverseAnim( pActionEventLocator );
+ }
+protected:
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+private:
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/24/2002]
+// Start an animation.
+// Only play once.
+//
+//////////////////////////////////////////////////////////////////////////
+class PlayAnim
+:
+public AnimSwitch
+{
+public:
+ PlayAnim( ActionEventLocator* pActionEventLocator );
+ virtual ~PlayAnim();
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) PlayAnim( pActionEventLocator );
+ }
+protected:
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+private:
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/24/2002]
+// Start an animation, let it play.
+//////////////////////////////////////////////////////////////////////////
+class PlayAnimLoop
+:
+public AnimSwitch
+{
+public:
+ PlayAnimLoop( ActionEventLocator* pActionEventLocator );
+ virtual ~PlayAnimLoop();
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) PlayAnimLoop( pActionEventLocator );
+ }
+protected:
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+private:
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/24/2002]
+//////////////////////////////////////////////////////////////////////////
+class AutoPlayAnim
+:
+public AnimSwitch
+{
+public:
+ AutoPlayAnim( ActionEventLocator* pActionEventLocator );
+ virtual ~AutoPlayAnim();
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) AutoPlayAnim( pActionEventLocator );
+ }
+ virtual void OnEnter( Character* pCharacter );
+ virtual void OnExit( Character* pCharacter );
+protected:
+ virtual bool IsActionButtonPressed( Character* pCharacter );
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+ virtual void OnUpdate( float timeins );
+ virtual void PositionCharacter( Character* pCharacter, Sequencer* pSeq );
+ virtual void SetAnimation( Character* pCharacter, Sequencer* pSeq );
+ virtual bool GetIsCyclic( void ) const
+ {
+ return false;
+ }
+ int mCharacterCount;
+ bool mbJustEmpty : 1;
+private:
+
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/24/2002]
+//////////////////////////////////////////////////////////////////////////
+class AutoPlayAnimLoop
+:
+public AutoPlayAnim
+{
+public:
+ AutoPlayAnimLoop( ActionEventLocator* pActionEventLocator );
+ virtual ~AutoPlayAnimLoop();
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) AutoPlayAnimLoop( pActionEventLocator );
+ }
+protected:
+ virtual bool GetIsCyclic( void ) const
+ {
+ return true;
+ }
+private:
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [8/4/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class AutoPlayAnimInOut
+:
+public AutoPlayAnim
+{
+public:
+ AutoPlayAnimInOut( ActionEventLocator* pActionEventLocator );
+ virtual ~AutoPlayAnimInOut();
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) AutoPlayAnimInOut( pActionEventLocator );
+ }
+protected:
+ virtual bool IsActionButtonPressed( Character* pCharacter );
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+ virtual void OnUpdate( float timeins );
+ virtual void OnExit( Character* pCharacter );
+private:
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/30/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class DestroyObject
+:
+public AnimSwitch,
+public CStatePropListener
+{
+public:
+ DestroyObject( ActionEventLocator* pActionEventLocator );
+ virtual ~DestroyObject();
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) DestroyObject( pActionEventLocator );
+ }
+ // Override Create
+ virtual bool Create( tEntityStore* inStore = 0 );
+ virtual bool NeedsUpdate( void ) const;
+
+ virtual void Enter( Character* pCharacter );
+ virtual void RecieveEvent( int callback , CStateProp* stateProp );
+
+ virtual Type GetType( void ) { return MISSION_OBJECTIVE; };
+
+ bool IsInstanceEnabled()const { return mIsEnabled; }
+
+
+
+protected:
+ virtual void OnReset( void );
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+ virtual void PositionCharacter( Character* pCharacter, Sequencer* pSeq );
+ virtual void SetAnimation( Character* pCharacter, Sequencer* pSeq );
+ virtual void OnUpdate( float timeins );
+ bool mbDestroyed : 1;
+
+protected:
+ StatePropDSG* mpStatePropDSG;
+
+
+private:
+
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// MKR [10/25/2002]
+//
+// A power coupling for the power plants. When it breaks, it disappears and plays
+// Aryan's power coupling explosion effect via the ParticleManager
+//
+//////////////////////////////////////////////////////////////////////////
+
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/30/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class UseVendingMachine
+:
+public PlayAnim
+{
+public:
+ UseVendingMachine( ActionEventLocator* pActionEventLocator );
+ virtual ~UseVendingMachine();
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) UseVendingMachine( pActionEventLocator );
+ }
+protected:
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+ virtual void SetAnimation( Character* pCharacter, Sequencer* pSeq );
+private:
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/30/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class PrankPhone
+:
+public PlayAnim
+{
+public:
+ PrankPhone( ActionEventLocator* pActionEventLocator );
+ virtual ~PrankPhone();
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) PrankPhone( pActionEventLocator );
+ }
+protected:
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+ virtual void SetAnimation( Character* pCharacter, Sequencer* pSeq );
+private:
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/30/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class SummonVehiclePhoneStaticCallback
+:
+public LoadingManager::ProcessRequestsCallback
+{
+public:
+ SummonVehiclePhoneStaticCallback( void ) : mpActionEventLocator( 0 ) {}
+ ~SummonVehiclePhoneStaticCallback( void );
+ void OnProcessRequestsComplete( void* pUserData );
+ ActionEventLocator* mpActionEventLocator;
+ VehicleCentral::DriverInit mDriver;
+};
+
+class SummonVehiclePhone
+:
+public ActionEventHandler
+{
+public:
+ SummonVehiclePhone( ActionEventLocator* pActionEventLocator );
+ virtual ~SummonVehiclePhone();
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) SummonVehiclePhone( pActionEventLocator );
+ }
+
+ virtual bool Create( tEntityStore* inStore = 0 );
+
+ virtual Type GetType( void ) { return ButtonHandler::SUMMON_PHONE; };
+
+ virtual bool UsesActionButton() const;
+
+ class CarSelectionInfo
+ {
+ public:
+ void AddVehicleSelectionInfo( const char* filename, const char* vehiclename, const char* scroobyname, bool bIncrementSlots = true )
+ {
+ rAssert( ::strlen( filename ) < static_cast<int>( MAX_STRING ) );
+ ::strcpy( mFileName, filename );
+
+ rAssert( ::strlen( vehiclename ) < static_cast<int>( MAX_STRING ) );
+ ::strcpy( mVehicleName, vehiclename );
+
+ rAssert( ::strlen( scroobyname ) < static_cast<int>( MAX_STRING ) );
+ ::strcpy( mScroobyIconName, scroobyname );
+ if ( bIncrementSlots )
+ {
+ sNumUsedSlots++;
+ }
+ }
+ void AddDebugVehicleSelectionInfo( const char* filename, const char* vehiclename, const char* scroobyname )
+ {
+ AddVehicleSelectionInfo( filename, vehiclename, scroobyname, false );
+ }
+ static int GetNumUsedSlots( void )
+ {
+ return sNumUsedSlots;
+ }
+ static void Reset( void )
+ {
+ sNumUsedSlots = 0;
+ }
+ const char* GetFileName( void ) const
+ {
+ return mFileName;
+ }
+ const char* GetVehicleName( void ) const
+ {
+ return mVehicleName;
+ }
+ const char* GetScroobyIconName( void ) const
+ {
+ return mScroobyIconName;
+ }
+ private:
+ static const int MAX_STRING = 32;
+ char mFileName[MAX_STRING];
+ char mVehicleName[MAX_STRING];
+ char mScroobyIconName[MAX_STRING];
+
+ static int sNumUsedSlots;
+ };
+
+ static CarSelectionInfo* GetCarSelectInfo( int index )
+ {
+ if ( index < NUM_CAR_SELECTION_SLOTS )
+ {
+ return &sCarSelectInfo[ index ];
+ }
+ return 0;
+ }
+
+ static CarSelectionInfo* GetDebugCarSelectInfo( void )
+ {
+ return GetCarSelectInfo( NUM_CAR_SELECTION_SLOTS - 1 );
+ }
+
+ static void ClearCarSelectInfo( void )
+ {
+ int i;
+ for( i = 0; i < NUM_CAR_SELECTION_SLOTS; i++ )
+ {
+ sCarSelectInfo[ i ].AddVehicleSelectionInfo( "", "", "" );
+ }
+ CarSelectionInfo::Reset( );
+ }
+/*
+ static int GetSelectedVehicleIndex( void )
+ {
+ return sSelectedVehicle;
+ }
+*/
+ static const char* GetSelectedVehicleName()
+ {
+ return sSelectedVehicleName;
+ }
+
+// static void SelectLoadedVehicle( int selectedVehicleIndex );
+ static void LoadVehicle( int selectedVehicleIndex );
+ static void LoadVehicle( const char* name, const char* filename, VehicleCentral::DriverInit );
+ static void LoadDebugVehicle( void );
+
+ // Dumps the currently loaded PBC.
+ //
+ static void DumpVehicle( void );
+
+ // Enable/Disable the PBCs.
+ //
+ static void SetEnabled( bool bEnabled )
+ {
+ sbEnabled = bEnabled;
+ }
+ // Query the state of the PBCs.
+ //
+ static bool IsEnabled( void )
+ {
+ return sbEnabled;
+ }
+
+ static SummonVehiclePhoneStaticCallback sCallback;
+protected:
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+ virtual bool OnButtonPressed( Character* pCharacter )
+ {
+ return false;
+ }
+ virtual void OnEnter( Character* pCharacter )
+ {
+ }
+ virtual void OnExit( Character* pCharacter )
+ {
+ }
+ virtual void SetAnimation( Character* pCharacter, Sequencer* pSeq );
+ virtual void OnUpdate( float timeins );
+ virtual bool NeedsUpdate( void ) const
+ {
+ return true;
+ }
+
+private:
+ // + 1 for a debug menu.
+ //
+ static const int NUM_CAR_SELECTION_SLOTS = 3 + 1;
+ static CarSelectionInfo sCarSelectInfo[ NUM_CAR_SELECTION_SLOTS ];
+// static const int INVALID_VEHICLE = -1;
+// static int sSelectedVehicle;
+ static const int MAX_VEHICLE_NAME_LENGTH = 16;
+ static char sSelectedVehicleName[ MAX_VEHICLE_NAME_LENGTH ];
+ static bool sbEnabled;
+ AnimatedIcon mPhoneIcon;
+ int mHudMapIconID;
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/30/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class Bounce
+:
+public ActionEventHandler
+{
+public:
+ Bounce( ActionEventLocator* pActionEventLocator );
+ virtual ~Bounce();
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) Bounce( pActionEventLocator );
+ }
+
+ virtual bool Create( tEntityStore* inStore = 0 );
+
+ virtual bool UsesActionButton( void ) const
+ {
+ return false;
+ }
+ static void OnEnter( Character* pCharacter, Locator* pLocator );
+protected:
+ virtual void OnEnter( Character* pCharacter );
+private:
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/30/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class Doorbell
+:
+public PlayAnim
+{
+public:
+ Doorbell( ActionEventLocator* pActionEventLocator ) : PlayAnim( pActionEventLocator ) {};
+ virtual ~Doorbell() {};
+ bool Create( tEntityStore* store = 0 );
+
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) Doorbell( pActionEventLocator );
+ }
+
+protected:
+
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+private:
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/30/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class OpenDoor
+:
+public PlayAnim
+{
+public:
+ OpenDoor( ActionEventLocator* pActionEventLocator ) : PlayAnim( pActionEventLocator ) {};
+ virtual ~OpenDoor() {};
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) OpenDoor( pActionEventLocator );
+ }
+protected:
+private:
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/30/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class TalkFood
+:
+public PlayAnim
+{
+public:
+ TalkFood( ActionEventLocator* pActionEventLocator ) : PlayAnim( pActionEventLocator ) {};
+ virtual ~TalkFood() {};
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) TalkFood( pActionEventLocator );
+ }
+protected:
+private:
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/30/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class TalkCollectible
+:
+public PlayAnim
+{
+public:
+ TalkCollectible( ActionEventLocator* pActionEventLocator ) : PlayAnim( pActionEventLocator ) {};
+ virtual ~TalkCollectible() {};
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) TalkCollectible( pActionEventLocator );
+ }
+protected:
+private:
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/30/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class TalkDialog
+:
+public PlayAnim
+{
+public:
+ TalkDialog( ActionEventLocator* pActionEventLocator ) : PlayAnim( pActionEventLocator ) {}
+ virtual ~TalkDialog() {};
+ static ButtonHandler* NewAction( ActionEventLocator* pEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) TalkDialog( pEventLocator );
+ }
+protected:
+private:
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [7/30/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class TalkMission
+:
+public PlayAnim
+{
+public:
+ TalkMission( ActionEventLocator* pActionEventLocator ) : PlayAnim( pActionEventLocator ) {};
+ virtual ~TalkMission() {};
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) TalkMission( pActionEventLocator );
+ }
+protected:
+private:
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// TBJ [8/6/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+class Collectible
+:
+public ActionEventHandler
+{
+public:
+ Collectible( ActionEventLocator* pActionEventLocator );
+ virtual ~Collectible();
+
+ virtual bool Create( tEntityStore* inStore = 0 );
+ virtual void ResetCollectible (); //needed a method to reset the mbCollected member
+protected:
+ virtual void OnReset( void );
+ virtual bool ShouldRespawn( ) const
+ {
+ return false;
+ }
+ virtual bool IsRespawnTimeExpired( void ) const
+ {
+ return GetRespawnTime( ) <= 0.0f;
+ }
+ virtual float GetRespawnTime( void ) const
+ {
+ return 0.0f;
+ }
+ virtual void SetRespawnTime( float fTime ) {}
+ virtual void UpdateRespawnTime( float timeins ) {}
+ virtual void OnUpdate( float timeins );
+ virtual void OnEnter( Character* pCharacter );
+ virtual void OnExit( Character* pCharacter );
+ bool IsCollected( void ) const
+ {
+ return mbCollected;
+ }
+ virtual bool NeedsUpdate( void ) const
+ {
+ return true;
+ }
+protected:
+// AnimEntityDSGWrapper* mpGameObject;
+ AnimatedIcon* mAnimatedIcon;
+private:
+ bool mbCollected : 1;
+};
+
+class RespawnCollectible
+:
+public Collectible
+{
+public:
+ RespawnCollectible( ActionEventLocator* pActionEventLocator );
+ virtual ~RespawnCollectible();
+protected:
+ virtual bool ShouldRespawn( ) const
+ {
+ return true;
+ }
+ virtual float GetRespawnTime( void ) const
+ {
+ return mfRespawnTime;
+ }
+ virtual void SetRespawnTime( float fTime )
+ {
+ mfRespawnTime = fTime;
+ }
+ virtual void UpdateRespawnTime( float timeins )
+ {
+ mfRespawnTime -= timeins;
+ }
+private:
+ float mfRespawnTime;
+};
+class CollectibleFood
+:
+public RespawnCollectible
+{
+public:
+ CollectibleFood( ActionEventLocator* pActionEventLocator, float fTurboGain );
+ virtual ~CollectibleFood();
+ static ButtonHandler* NewFoodSmallAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) CollectibleFood( pActionEventLocator, sfSmallTurboGain );
+ }
+ static ButtonHandler* NewFoodLargeAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) CollectibleFood( pActionEventLocator, sfLargeTurboGain );
+ }
+protected:
+ virtual void OnEnter( Character* pCharacter );
+ float mfTurboGain;
+private:
+ static float sfSmallTurboGain;
+ static float sfLargeTurboGain;
+};
+
+class CollectibleCard
+:
+public Collectible
+{
+public:
+ CollectibleCard( ActionEventLocator* pActionEventLocator );
+ virtual ~CollectibleCard();
+
+ virtual bool Create( tEntityStore* inStore = 0 );
+
+ virtual Type GetType( void ) { return ButtonHandler::COLLECTOR_CARD; };
+
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) CollectibleCard( pActionEventLocator );
+ }
+
+ static void UpdateThing( unsigned int milliseconds );
+
+protected:
+ virtual void OnEnter( Character* pCharacter );
+private:
+ static AnimatedIcon* mAnimatedCollectionThing;
+ static unsigned int mCollectibleCardCount;
+};
+
+////////////////////////////////////////////////////////
+//wrench class used to fix the car
+///////////////////////////////////////////////////////
+
+
+class WrenchIcon :public Collectible, public RespawnEntity
+{
+public:
+ WrenchIcon( ActionEventLocator* pActionEventLocator );
+ virtual ~WrenchIcon();
+
+ virtual bool Create( tEntityStore* inStore = 0 );
+
+ virtual Type GetType( void ) { return ButtonHandler::WRENCH_ICON; };
+
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) WrenchIcon( pActionEventLocator );
+ }
+
+ static void UpdateThing( unsigned int milliseconds );
+
+ //overriding travis's action button handler virtuals
+ void Update(float timeins);
+ bool NeedsUpdate()
+ {
+ return true;
+ }
+
+
+protected:
+ virtual void OnEnter( Character* pCharacter );
+private:
+ static AnimatedIcon* mAnimatedCollectionThing;
+ static unsigned int mWrenchCount;
+};
+
+
+////////////////////////////////////////////////////////
+//Nitro class used in the supersprint
+///////////////////////////////////////////////////////
+
+
+class NitroIcon :public Collectible, public RespawnEntity
+{
+public:
+ NitroIcon( ActionEventLocator* pActionEventLocator );
+ virtual ~NitroIcon();
+
+ virtual bool Create( tEntityStore* inStore = 0 );
+
+ virtual Type GetType( void ) { return ButtonHandler::NITRO_ICON; };
+
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new (GMA_LEVEL_OTHER) NitroIcon( pActionEventLocator );
+ }
+
+ static void UpdateThing( unsigned int milliseconds );
+
+ //overriding travis's action button handler virtuals
+ void Update(float timeins);
+ bool NeedsUpdate()
+ {
+ return true;
+ }
+
+
+protected:
+ virtual void OnEnter( Character* pCharacter );
+private:
+ static AnimatedIcon* mAnimatedCollectionThing;
+ static unsigned int mNitroCount;
+};
+
+class GenericEventButtonHandler : public ButtonHandler
+{
+public:
+ GenericEventButtonHandler( EventLocator* pEventLocator, EventEnum event );
+ virtual ~GenericEventButtonHandler();
+
+ static ButtonHandler* NewAction( EventLocator* pEventLocator, EventEnum event, GameMemoryAllocator alloc = GMA_LEVEL_OTHER );
+ void SetEventLocator( EventLocator* pEventLocator );
+
+ EventLocator* GetEventLocator( void ) const
+ {
+ return mpEventLocator;
+ }
+
+ void SetEventData( void* data ) { mEventData = data; };
+ void* GetEventData() { return mEventData; };
+
+ virtual Type GetType( void ) { return MISSION_OBJECTIVE; };
+
+protected:
+ EventLocator* mpEventLocator;
+
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+
+private:
+ EventEnum mEvent;
+ void* mEventData;
+};
+
+class TeleportAction : public ActionEventHandler, public EventListener
+{
+public:
+ TeleportAction( ActionEventLocator* pActionEventLocator );
+ virtual ~TeleportAction();
+
+ static ButtonHandler* NewAction( ActionEventLocator* pEventLocator )
+ {
+ ButtonHandler* handler = NULL;
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ handler = new TeleportAction( pEventLocator );
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ return handler;
+ }
+
+ virtual bool Create( tEntityStore* inStore = 0 );
+ virtual Type GetType( void ) { return TELEPORT; };
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+protected:
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+
+ virtual void OnEnter( Character* pCharacter );
+ virtual void OnExit( Character* pCharacter );
+};
+
+class PurchaseReward : public ActionEventHandler
+{
+public:
+ PurchaseReward( ActionEventLocator* pActionEventLocator );
+ virtual ~PurchaseReward();
+
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator );
+ virtual bool Create( tEntityStore* inStore = 0 ) = 0;
+ virtual Type GetType( void ) = 0;
+ virtual bool UsesActionButton() const = 0;
+
+ static void SetEnabled( bool bEnabled )
+ {
+ sbEnabled = bEnabled;
+ }
+ static bool IsEnabled( void )
+ {
+ return sbEnabled;
+ }
+
+protected:
+ bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq ) { return OnButtonPressed( pCharacter ); };
+ virtual bool OnButtonPressed( Character* pCharacter ) = 0;
+ virtual void OnEnter( Character* pCharacter ) = 0;
+ virtual void OnExit( Character* pCharacter ) = 0;
+ virtual void OnUpdate( float timeins );
+ bool NeedsUpdate( void ) const { return true; };
+ bool CanEnable() const;
+
+ AnimatedIcon mIcon;
+ bool mAllPurchased : 1;
+ bool mIsActive : 1;
+ int mHudMapIconID;
+
+private:
+ static bool sbEnabled;
+
+};
+
+class PurchaseCar : public PurchaseReward
+{
+public:
+ PurchaseCar( ActionEventLocator* pActionEventLocator );
+ virtual ~PurchaseCar();
+
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ ButtonHandler* handler = NULL;
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ handler = new PurchaseCar( pActionEventLocator );
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ return handler;
+ }
+
+ virtual bool Create( tEntityStore* inStore = 0 );
+ virtual Type GetType( void ) { return ButtonHandler::PURCHASE_CAR; };
+ virtual bool UsesActionButton() const { return true; };
+
+protected:
+ virtual bool OnButtonPressed( Character* pCharacter );
+ virtual void OnEnter( Character* pCharacter );
+ virtual void OnExit( Character* pCharacter );
+ virtual void OnUpdate( float timeins );
+
+private:
+ GuiSFX::InputStateChange m_DisableInput;
+ GuiSFX::GotoScreen m_GotoLetterbox;
+ GuiSFX::SwitchContext m_ContextSwitch;
+ GuiSFX::RecieveEvent m_WaitForConversationDone;
+ GuiSFX::GotoScreen m_GotoHud;
+ GuiSFX::GotoScreen m_GotoScreen;
+};
+
+class PurchaseSkin : public PurchaseReward
+{
+public:
+ PurchaseSkin( ActionEventLocator* pActionEventLocator );
+ virtual ~PurchaseSkin();
+
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ ButtonHandler* handler = NULL;
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ handler = new PurchaseSkin( pActionEventLocator );
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ return handler;
+ }
+
+ virtual bool Create( tEntityStore* inStore = 0 );
+ virtual Type GetType( void ) { return ButtonHandler::PURCHASE_SKIN; };
+ virtual bool UsesActionButton() const { return true; };
+
+protected:
+ virtual bool OnButtonPressed( Character* pCharacter );
+ virtual void OnEnter( Character* pCharacter );
+ virtual void OnExit( Character* pCharacter );
+ virtual void OnUpdate( float timeins );
+};
+
+}; // namespace ActionButton
+#endif //ACTIONBUTTONHANDLER_H
diff --git a/game/code/ai/actionbuttonmanager.cpp b/game/code/ai/actionbuttonmanager.cpp
new file mode 100644
index 0000000..3a70544
--- /dev/null
+++ b/game/code/ai/actionbuttonmanager.cpp
@@ -0,0 +1,771 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implementation of class ActionButtonManager
+//
+// History: 18/07/2002 + Created -- TBJ
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <memory/srrmemory.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <worldsim/character/charactercontroller.h>
+
+#include <ai/actionbuttonmanager.h>
+#include <ai/actionbuttonhandler.h>
+#include <ai/actionlist.h>
+#include <ai/actionnames.h>
+
+#include <render/DSG/animcollisionentitydsg.h>
+
+
+#include <memory/srrmemory.h>
+#include <meta/actioneventlocator.h>
+#include <meta/eventlocator.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/triggervolumetracker.h>
+
+#include <console/console.h>
+
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/RenderLayer.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/loaders/billboardwrappedloader.h>
+#include <mission/gameplaymanager.h>
+
+#include <events/eventmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+ActionButtonManager* ActionButtonManager::spActionButtonManager = (ActionButtonManager*)0;
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ActionButtonManager::ActionButtonManager
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+static int s_currentID = 0;
+
+ActionButtonManager::ActionButtonManager()
+:
+mpCurrentToResolve( 0 ),
+mLoadingIntoInterior( false )
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIONS; i++ )
+ {
+ mActionButtonList[ i ].handler = NULL;
+ mActionButtonList[ i ].sectionName = 0;
+ mbActionButtonNeedsUpdate[ i ] = false;
+ }
+
+ // Sanity check.
+ //
+ rAssertMsg( ActionButton::ButtonNameListSize == CharacterController::NUM_INPUTS, "Button Name list size does not match enumeration size defined in .\\worldsim\\character\\charactercontroller.h\n" );
+ rAssertMsg( ActionButton::nameIndex == ActionButton::ActionNameSize, "Action Name list size does not match enumeration size!\n" );
+
+ Console* pConsole = GetConsole();
+ if ( pConsole )
+ {
+ pConsole->AddFunction( "AddVehicleSelectInfo", AddVehicleSelectInfo, "", 3, 3 ); //One entry in phone selectable vehicles.
+ pConsole->AddFunction( "ClearVehicleSelectInfo", ClearVehicleSelectInfo, "", 0, 0 ); //Clear all entries in phone selectable vehicles.
+ }
+
+ GetEventManager()->AddListener( this, EVENT_DUMP_DYNA_SECTION );
+}
+
+/*
+==============================================================================
+ActionButtonManager::FindHandler
+==============================================================================
+Description: Returns the ButtonHandler that is associated with the given
+ locator. NULL if none are found
+
+Parameters: const ActionEventLocator* locator
+
+Return: ActionButton::ButtonHandler*
+
+=============================================================================
+*/
+ActionButton::ActionEventHandler* ActionButtonManager::FindHandler( const ActionEventLocator* locator )const
+{
+ // Iterate through the ButtonHandlers and find the Handler that contains the given locator
+ for ( int i = 0 ; i < MAX_ACTIONS ; i++ )
+ {
+ // Expensive dynamic cast
+ ActionButton::ActionEventHandler* handler = dynamic_cast< ActionButton::ActionEventHandler* >( mActionButtonList[i].handler );
+ if ( handler )
+ {
+ // Is this the locator we are looking for
+ if ( locator == handler->GetActionEventLocator() )
+ {
+ return handler;
+ }
+ }
+ }
+ // No Handler found, return NULL
+ return NULL;
+}
+
+
+/*
+==============================================================================
+ActionButtonManager::AddVehicleSelectInfo
+==============================================================================
+Description: Scripter hook
+
+Parameters: ( int argc, char** argv )
+
+Return: void
+
+=============================================================================
+*/
+void ActionButtonManager::AddVehicleSelectInfo( int argc, char** argv )
+{
+ rWarningMsg( false, "ActionButtonManager::AddVehicleSelectInfo() function is deprecated!" );
+/*
+ int index = ActionButton::SummonVehiclePhone::CarSelectionInfo::GetNumUsedSlots();
+ ActionButton::SummonVehiclePhone::CarSelectionInfo* pInfo = ActionButton::SummonVehiclePhone::GetCarSelectInfo( index );
+ if ( pInfo )
+ {
+ pInfo->AddVehicleSelectionInfo( argv[1], argv[2], argv[3] );
+ }
+ else
+ {
+ rReleasePrintf( "Too many vehicle select info lines. Call ClearVehicleSelectInfo() first!." );
+ }
+*/
+}
+
+/*
+==============================================================================
+ActionButtonManager::ClearVehicleSelectInfo
+==============================================================================
+Description: Scripter hook
+
+Parameters: ( int argc, char** argv )
+
+Return: void
+
+=============================================================================
+*/
+void ActionButtonManager::ClearVehicleSelectInfo( int argc, char** argv )
+{
+ rWarningMsg( false, "ActionButtonManager::ClearVehicleSelectInfo() function is deprecated!" );
+/*
+ ActionButton::SummonVehiclePhone::ClearCarSelectInfo( );
+*/
+}
+
+//=============================================================================
+// ActionButtonManager::OnProcessRequestsComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void ActionButtonManager::OnProcessRequestsComplete( void* pUserData )
+{
+ BillboardWrappedLoader::OverrideLoader( false );
+ GetRenderManager()->pWorldRenderLayer()->DoPostDynaLoad();
+}
+
+
+//==============================================================================
+// ActionButtonManager::~ActionButtonManager
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ActionButtonManager::~ActionButtonManager()
+{
+ Destroy( );
+
+ GetEventManager()->RemoveAll( this );
+}
+/*
+==============================================================================
+ActionButtonManager::Destroy
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void ActionButtonManager::Destroy( void )
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIONS; i++ )
+ {
+ // Delete the actionbuttonhandler.
+ //
+ RemoveActionByArrayPos( i );
+ }
+
+ s_currentID = 0;
+}
+/*
+==============================================================================
+ActionButtonManager::CreateInstance
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ActionButtonManager
+
+=============================================================================
+*/
+ActionButtonManager* ActionButtonManager::CreateInstance( void )
+{
+ rAssertMsg( spActionButtonManager == 0, "ActionButtonManager already created.\n" );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+
+ spActionButtonManager = new ActionButtonManager;
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+
+ return( spActionButtonManager );
+}
+/*
+==============================================================================
+ActionButtonManager::GetInstance
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ActionButtonManager
+
+=============================================================================
+*/
+ActionButtonManager* ActionButtonManager::GetInstance( void )
+{
+ rAssertMsg( spActionButtonManager != 0, "ActionButtonManager has not been created yet.\n" );
+ return spActionButtonManager;
+}
+
+/*
+==============================================================================
+ActionButtonManager::DestroyInstance
+==============================================================================
+Description: Destroy the singleton.
+
+Parameters: ( void )
+
+Return: n/a
+
+=============================================================================
+*/
+void ActionButtonManager::DestroyInstance( void )
+{
+ rAssert( spActionButtonManager != NULL );
+
+ #ifdef RAD_GAMECUBE
+ delete( GMA_GC_VMM, spActionButtonManager );
+ #else
+ delete( GMA_PERSISTENT, spActionButtonManager );
+ #endif
+
+ spActionButtonManager = NULL;
+}
+
+/*
+==============================================================================
+ActionButtonManager::EnterGame
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void ActionButtonManager::EnterGame( void )
+{
+}
+
+/*
+==============================================================================
+ActionButtonManager::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void ActionButtonManager::Update( float timeins )
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIONS; i++ )
+ {
+ if ( mActionButtonList[ i ].handler != 0 && mActionButtonList[ i ].handler->NeedsUpdate( ) )
+ {
+ mActionButtonList[ i ].handler->Update( timeins );
+ }
+ }
+
+ unsigned int time = rmt::FtoL( timeins * 1000.0f );
+
+ //These are the collection effects. They are one shot and remove
+ //themselves from the world when they're done.
+ ActionButton::CollectibleCard::UpdateThing( time );
+ ActionButton::WrenchIcon::UpdateThing( time );
+ ActionButton::NitroIcon::UpdateThing( time );
+}
+
+/*
+==============================================================================
+ActionButtonManager::AddActionEventLocator
+==============================================================================
+Description: Comment
+
+Parameters: ( ActionEventLocator* pActionEventLocator )
+
+Return: bool
+
+=============================================================================
+*/
+bool ActionButtonManager::AddActionEventLocator( ActionEventLocator* pActionEventLocator, tEntityStore* inStore )
+{
+ rAssert( pActionEventLocator );
+ return LinkActionToLocator( pActionEventLocator, inStore );
+}
+
+/*
+==============================================================================
+ActionButtonManager::GetActionAt
+==============================================================================
+Description: Comment
+
+Parameters: ( int i )
+
+Return: ActionButtonHandler
+
+=============================================================================
+*/
+ActionButton::ButtonHandler* ActionButtonManager::GetActionByIndex( int i ) const
+{
+ i = Find(i);
+
+ if ( i < MAX_ACTIONS && i >= 0 )
+ {
+ return mActionButtonList[ i ].handler;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+/*
+==============================================================================
+ActionButtonManager::AddAction
+==============================================================================
+Description: Comment
+
+Parameters: ( ActionButtonHandler* pAction )
+
+Return: bool
+
+=============================================================================
+*/
+bool ActionButtonManager::AddAction( ActionButton::ButtonHandler* pAction, int& id, tUID section)
+{
+ rAssert( pAction );
+ int i;
+ for ( i = 0; i < MAX_ACTIONS; i++ )
+ {
+ if ( mActionButtonList[ i ].handler == (ActionButton::ButtonHandler*)0 )
+ {
+ break;
+ }
+ }
+ if ( i < MAX_ACTIONS )
+ {
+ mActionButtonList[ i ].handler = pAction;
+ mActionButtonList[ i ].handler->AddRef();
+ mActionButtonList[ i ].id = s_currentID++;
+ mActionButtonList[ i ].sectionName = section;
+ id = mActionButtonList[ i ].id;
+ return true;
+ }
+ id = -1;
+ return false;
+}
+
+/*
+==============================================================================
+ActionButtonManager::RemoveActionByIndex
+==============================================================================
+Description: Comment
+
+Parameters: ( int id )
+
+Return: bool
+
+=============================================================================
+*/
+bool ActionButtonManager::RemoveActionByIndex( int id )
+{
+ return RemoveActionByArrayPos(Find(id));
+}
+/*
+==============================================================================
+ActionButtonManager::RemoveAction
+==============================================================================
+Description: Comment
+
+Parameters: ( ActionButton::ButtonHandler* pAction )
+
+Return: int
+
+=============================================================================
+*/
+int ActionButtonManager::RemoveAction( ActionButton::ButtonHandler* pAction )
+{
+ rAssert( pAction );
+ int id = -1;
+ int i;
+ for ( i = 0; i < MAX_ACTIONS; i++ )
+ {
+ if ( mActionButtonList[ i ].handler == pAction )
+ {
+ break;
+ }
+ }
+ if ( i < MAX_ACTIONS )
+ {
+ RemoveActionByArrayPos( i );
+ id = i;
+ }
+ return id;
+}
+/*
+==============================================================================
+ActionButtonManager::CreateActionEventTrigger
+==============================================================================
+Description: Comment
+
+Parameters: ( const char* triggerName, rmt::Vector& pos, float r )
+
+Return: bool
+
+=============================================================================
+*/
+bool ActionButtonManager::CreateActionEventTrigger( const char* triggerName, rmt::Vector& pos, float r )
+{
+ return true;
+}
+/*
+==============================================================================
+ActionButtonManager::LinkActionToObjectJoint
+==============================================================================
+Description: Comment
+
+Parameters: ( const char* objectName, const char* jointName, const char* triggerName, const char* typeName, const char* buttonName )
+
+Return: bool
+
+=============================================================================
+*/
+bool ActionButtonManager::LinkActionToObjectJoint( const char* objectName, const char* jointName, const char* triggerName, const char* typeName, const char* buttonName )
+{
+ return LinkActionToObject( objectName, jointName, triggerName, typeName, buttonName, true );
+}
+/*
+==============================================================================
+ActionButtonManager::LinkActionToObject
+==============================================================================
+Description: Comment
+
+Parameters: ( const char* objectName, const char* triggerName, const char* typeName, const char* buttonName )
+
+Return: bool
+
+=============================================================================
+*/
+bool ActionButtonManager::LinkActionToObject( const char* objectName, const char* jointName, const char* triggerName, const char* typeName, const char* buttonName, bool attachToJoint )
+{
+ return true;
+}
+
+/*
+==============================================================================
+ActionButtonManager::LinkActionToObject
+==============================================================================
+Description: Comment
+
+Parameters: ( ActionEventLocator* pActionEventLocator )
+
+Return: bool
+
+=============================================================================
+*/
+bool ActionButtonManager::LinkActionToLocator( ActionEventLocator* pActionEventLocator, tEntityStore* inStore )
+{
+ ActionButton::ButtonHandler* pABHandler = this->NewActionButtonHandler( pActionEventLocator->GetActionName(), pActionEventLocator );
+ bool bAdded = false;
+
+ if ( pABHandler != 0 )
+ {
+ rAssert( dynamic_cast< ActionButton::ActionEventHandler*>( pABHandler ) != NULL );
+ ActionButton::ActionEventHandler* pActionHandler = static_cast<ActionButton::ActionEventHandler*>( pABHandler );
+ rAssert( pActionHandler );
+ pActionHandler->AddRef();
+
+
+ int id = -1;
+ bool bCreated = pActionHandler->Create( inStore );
+ if ( bCreated )
+ {
+ tUID section = 0;
+ if ( GetRenderManager()->pWorldRenderLayer()->GetCurrentState() == WorldRenderLayer::msLoad )
+ {
+ section = GetRenderManager()->pWorldRenderLayer()->GetCurSectionName().GetUID();
+ }
+
+ bAdded = this->AddAction( pActionHandler, id, section);
+ rAssert( bAdded );
+ pActionEventLocator->SetData( id );
+ }
+ else
+ {
+ rReleasePrintf( "****************** Failed to create %s, action type %s\n", pActionEventLocator->GetObjName( ), pActionEventLocator->GetActionName( ) );
+ }
+
+ //HACK
+ mLoadingIntoInterior = false;
+
+ // We are done with it at this level.
+ // If it was not added, then this will delete the object.
+ //
+ pActionHandler->Release( );
+ }
+ return bAdded;
+}
+
+/*
+==============================================================================
+ActionButtonManager::EnterActionTrigger
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, int index )
+
+Return: void
+
+=============================================================================
+*/
+void ActionButtonManager::EnterActionTrigger( Character* pCharacter, int index )
+{
+ index = Find(index);
+
+ rAssert( index < MAX_ACTIONS && index >= 0 );
+
+ if ( index < MAX_ACTIONS && index >= 0 )
+ {
+ rAssert( mActionButtonList[ index ].handler != 0 );
+ if ( mActionButtonList[ index ].handler != 0 )
+ {
+ mActionButtonList[ index ].handler->Enter( pCharacter );
+ }
+ }
+}
+/*
+==============================================================================
+ActionButtonManager::ExitActionTrigger
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, int index )
+
+Return: void
+
+=============================================================================
+*/
+void ActionButtonManager::ExitActionTrigger( Character* pCharacter, int index )
+{
+ index = Find(index);
+
+ rAssert( index < MAX_ACTIONS && index >= 0 );
+
+ if ( index < MAX_ACTIONS && index >= 0 )
+ {
+ rAssert( mActionButtonList[ index ].handler != 0 );
+ if ( mActionButtonList[ index ].handler != 0 )
+ {
+ mActionButtonList[ index ].handler->Exit( pCharacter );
+ }
+ }
+}
+/*
+==============================================================================
+ActionButtonManager::NewActionButtonHandler
+==============================================================================
+Description: Comment
+
+Parameters: ( const char* typeName, ActionEventLocator* pActionEventLocator )
+
+Return: ActionButtonHandler
+
+=============================================================================
+*/
+ActionButton::ButtonHandler* ActionButtonManager::NewActionButtonHandler( const char* typeName, ActionEventLocator* pActionEventLocator )
+{
+ ActionButton::ButtonHandler* pActionButtonHandler = 0;
+ int i;
+ for ( i = 0; i < ActionButton::ActionListSize; i++ )
+ {
+ if ( ActionButton::CompareActionType( typeName, ActionButton::theListOfActions[ i ].mActionKey ) )
+ {
+ pActionButtonHandler = ActionButton::theListOfActions[ i ].actionPtr( pActionEventLocator );
+ break;
+ }
+ }
+
+ if ( !pActionButtonHandler )
+ {
+ rDebugPrintf("Could not create action button handler type %s. Update your worldbuilder and reexport.", typeName );
+ }
+
+ return pActionButtonHandler;
+}
+/*
+==============================================================================
+ActionButtonManager::ResolveActionTrigger
+==============================================================================
+Description: Comment
+
+Parameters: ( AnimCollisionEntityDSG* pAnimObject )
+
+Return: bool, true if successfully resolved.
+
+=============================================================================
+*/
+bool ActionButtonManager::ResolveActionTrigger( AnimCollisionEntityDSG* pAnimObject, tEntityStore* inStore )
+{
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection("Default");
+ mpCurrentToResolve = pAnimObject;
+
+ ActionEventLocator* pLocator = p3d::find<ActionEventLocator>( pAnimObject->GetUID() );
+ bool bAdded = false;
+ if ( pLocator )
+ {
+ bAdded = LinkActionToLocator( pLocator, inStore );
+ rAssert( bAdded );
+ }
+ mpCurrentToResolve = 0;
+ p3d::inventory->PopSection();
+ return bAdded;
+}
+
+//=============================================================================
+// ActionButtonManager::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void ActionButtonManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ //When the event EVENT_DUMP_DYNA_SECTION is sent, the dyna load section UID is
+ //compared to the stored section of the action butons. Any buttons created during the
+ //specified load section are released.
+ unsigned int i;
+ for ( i = 0; i < MAX_ACTIONS; ++i )
+ {
+ if ( mActionButtonList[ i ].handler != NULL &&
+ mActionButtonList[ i ].sectionName == static_cast<tName*>(pEventData)->GetUID() )
+ {
+ //Remove this action button.
+ RemoveActionByArrayPos( i );
+ }
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+int ActionButtonManager::Find(int id) const
+{
+ unsigned int i;
+ for ( i = 0; i < MAX_ACTIONS; ++i )
+ {
+ if ( mActionButtonList[ i ].handler != NULL &&
+ mActionButtonList[ i ].id == id )
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+bool ActionButtonManager::RemoveActionByArrayPos(int id)
+{
+ if( id < MAX_ACTIONS && id >= 0 )
+ {
+ if ( mActionButtonList[ id ].handler != 0 )
+ {
+ mActionButtonList[ id ].handler->Release( );
+ mActionButtonList[ id ].handler = 0;
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/game/code/ai/actionbuttonmanager.h b/game/code/ai/actionbuttonmanager.h
new file mode 100644
index 0000000..88f8a16
--- /dev/null
+++ b/game/code/ai/actionbuttonmanager.h
@@ -0,0 +1,134 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: actionbuttonmanager.h
+//
+// Description: Blahblahblah
+//
+// History: 18/07/2002 + Created -- TBJ
+//
+//=============================================================================
+
+#ifndef ACTIONBUTTONMANAGER_H
+#define ACTIONBUTTONMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/p3dtypes.hpp>
+#include <radmath/radmath.hpp>
+#include <loading/loadingmanager.h>
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+namespace ActionButton
+{
+ class ButtonHandler;
+ class ActionEventHandler;
+};
+class Character;
+class ActionEventLocator;
+class AnimCollisionEntityDSG;
+class tCompositeDrawable;
+class tEntityStore;
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ActionButtonManager : public LoadingManager::ProcessRequestsCallback, public EventListener
+{
+public:
+ enum { MAX_ACTIONS = 128 };
+
+ ActionButtonManager();
+ ~ActionButtonManager();
+
+ static ActionButtonManager* GetInstance( );
+ static ActionButtonManager* CreateInstance( );
+ static void DestroyInstance();
+
+ void EnterGame( void );
+ void Destroy( void );
+
+ void Update( float timeins );
+
+ bool AddActionEventLocator( ActionEventLocator* pActionEventLocator, tEntityStore* inStore = 0 );
+
+ ActionButton::ButtonHandler* GetActionByIndex( int i ) const;
+ int GetIndexByAction( ActionButton::ButtonHandler* ) const;
+
+ bool AddAction( ActionButton::ButtonHandler* pAction, int& id, tUID section = 0);
+ bool RemoveActionByIndex( int id );
+ int RemoveAction( ActionButton::ButtonHandler* pAction );
+ ActionButton::ButtonHandler* NewActionButtonHandler( const char* typeName, ActionEventLocator* pActionEventLocator );
+
+ bool CreateActionEventTrigger( const char* triggerName, rmt::Vector& pos, float r );
+ bool LinkActionToObjectJoint( const char* objectName, const char* jointName, const char* triggerName, const char* typeName, const char* buttonName );
+ bool LinkActionToObject( const char* objectName, const char* jointName, const char* triggerName, const char* typeName, const char* buttonName, bool bAttachToJoint );
+
+ bool LinkActionToLocator( ActionEventLocator* pActionEventLocator, tEntityStore* inStore = 0 );
+
+ void EnterActionTrigger( Character* pCharacter, int index );
+ void ExitActionTrigger( Character* pCharacter, int index );
+
+ bool ResolveActionTrigger( AnimCollisionEntityDSG* pAnimObject, tEntityStore* inStore = 0 );
+
+ AnimCollisionEntityDSG* GetCurrentAnimObjectToResolve( void ) const
+ {
+ return mpCurrentToResolve;
+ }
+
+ // Returns the button handler that owns the given locator
+ // Returns NULL if none found
+ ActionButton::ActionEventHandler* FindHandler( const ActionEventLocator* locator )const;
+
+ static void AddVehicleSelectInfo( int argc, char** argv );
+ static void ClearVehicleSelectInfo( int argc, char** argv );
+
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ //HACK HACK HACK
+ void LoadingIntoInterior() { mLoadingIntoInterior = true; };
+
+
+private:
+ //Prevent wasteful constructor creation.
+ ActionButtonManager( const ActionButtonManager& actionbuttonmanager );
+ ActionButtonManager& operator=( const ActionButtonManager& actionbuttonmanager );
+
+ // Singleton.
+ //
+ static ActionButtonManager* spActionButtonManager;
+
+ struct HandlerData
+ {
+ HandlerData() : handler( NULL ), sectionName( 0 ) { };
+ ActionButton::ButtonHandler* handler;
+ tUID sectionName; //This is for knowing when to release these. The worldrenderlayer section UID when this is loaded is stored here.
+ int id;
+ };
+
+ HandlerData mActionButtonList[ MAX_ACTIONS ];
+ bool mbActionButtonNeedsUpdate[ MAX_ACTIONS ];
+
+ // Big huge hack here. But it'll work.
+ //
+ AnimCollisionEntityDSG* mpCurrentToResolve;
+
+ bool mLoadingIntoInterior : 1;
+
+ int Find(int id) const;
+ bool RemoveActionByArrayPos(int i);
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline ActionButtonManager* GetActionButtonManager() { return( ActionButtonManager::GetInstance() ); }
+
+
+#endif //ACTIONBUTTONMANAGER_H
diff --git a/game/code/ai/actionlist.h b/game/code/ai/actionlist.h
new file mode 100644
index 0000000..fdbf42a
--- /dev/null
+++ b/game/code/ai/actionlist.h
@@ -0,0 +1,86 @@
+#ifndef ACTION_LIST_H_
+#define ACTION_LIST_H_
+
+#include <ai/actionbuttonhandler.h>
+#include <ai/playanimonce.h>
+#include <ai/actionnames.h>
+#include <ai/automaticdoor.h>
+
+#include <radkey.hpp>
+
+class EventLocator;
+
+namespace ActionButton
+{
+ // Create a function ptr type for easy handling of new actions.
+ //
+ typedef ButtonHandler* (*newActionPtr)( ActionEventLocator* pActionEventLocator);
+
+
+ // Keep the names around in debug mode.
+ //
+ #ifdef RAD_DEBUG
+ #define MAKE_ACTION_KEY( s ) s
+ typedef const char* ACTION_KEY;
+ bool CompareActionType( const char* typeName, const char* actionName )
+ {
+ return ( ::radMakeCaseInsensitiveKey( typeName ) == ::radMakeCaseInsensitiveKey( actionName ) );
+ }
+ #else
+ #define MAKE_ACTION_KEY( s ) radMakeCaseInsensitiveKey( s )
+ typedef radInt64 ACTION_KEY;
+ bool CompareActionType( const char* typeName, tUID actionUID )
+ {
+ return ( ::radMakeCaseInsensitiveKey( typeName ) == actionUID );
+ }
+ #endif
+
+ // A key, function ptr pair.
+ //
+ struct ActionIndex
+ {
+ ACTION_KEY mActionKey;
+ newActionPtr actionPtr;
+ };
+
+ // Add entries to this table when you want to create new actions.
+ //
+ static int nameIndex = 0;
+ ActionIndex theListOfActions[ ] =
+ {
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), ToggleAnim::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), ReverseAnim::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), PlayAnim::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), PlayAnimLoop::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), AutoPlayAnim::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), AutoPlayAnimLoop::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), AutoPlayAnimInOut::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), DestroyObject::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), DestroyObject::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), UseVendingMachine::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), PrankPhone::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), SummonVehiclePhone::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), Doorbell::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), OpenDoor::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), TalkFood::NewAction },
+ // NB switch to TalkCollectible::NewAction
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), CollectibleCard::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), TalkDialog::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), TalkMission::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), CollectibleFood::NewFoodSmallAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), CollectibleFood::NewFoodLargeAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), CollectibleCard::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), DestroyObject::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), PlayAnimOnce::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), AutomaticDoor::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), WrenchIcon::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), TeleportAction::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), PurchaseCar::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), PurchaseSkin::NewAction },
+ { MAKE_ACTION_KEY( ActionName[ nameIndex++ ] ), NitroIcon::NewAction }
+
+ };
+
+ const int ActionListSize = ( sizeof(ActionButton::theListOfActions) / sizeof(ActionButton::theListOfActions[0]) );
+};
+#endif \ No newline at end of file
diff --git a/game/code/ai/actionnames.h b/game/code/ai/actionnames.h
new file mode 100644
index 0000000..922e14b
--- /dev/null
+++ b/game/code/ai/actionnames.h
@@ -0,0 +1,63 @@
+#ifndef ACTION_NAMES_H_
+#define ACTION_NAMES_H_
+
+namespace ActionButton
+{
+ const char* const ActionName[] =
+ {
+ "ToggleOnOff",
+ "ToggleReverse",
+ "PlayAnim",
+ "PlayAnimLoop",
+ "AutoPlayAnim",
+ "AutoPlayAnimLoop",
+ "AutoPlayAnimInOut",
+ "DestroyObject",
+ "PowerCoupling",
+ "UseVendingMachine",
+ "PrankPhone",
+ "SummonVehiclePhone",
+ "Doorbell",
+ "OpenDoor",
+ "TalkFood",
+ "TalkCollectible",
+ "TalkDialog",
+ "TalkMission",
+ "FoodSmall",
+ "FoodLarge",
+ "CollectorCard",
+ "AlienCamera",
+ "PlayOnce",
+ "AutomaticDoor",
+ "Wrench",
+ "Teleport",
+ "PurchaseCar",
+ "PurchaseSkin",
+ "Nitro"
+ };
+ const int ActionNameSize = ( sizeof(ActionButton::ActionName) / sizeof(ActionButton::ActionName[0]) );
+
+ const char* const ButtonName[] =
+ {
+ "NONE",
+ "LeftStickX",
+ "LeftStickY",
+ "Action",
+ "Jump",
+ "Dash",
+ "Attack",
+ "DPadUp",
+ "DPadDown",
+ "DPadLeft",
+ "DPadRight"
+#ifdef RAD_WIN32
+ ,"GetOutCar"
+ ,"MouseLookLeft"
+ ,"MouseLookRight"
+#endif
+ };
+
+ const int ButtonNameListSize = ( sizeof(ActionButton::ButtonName) / sizeof(ActionButton::ButtonName[0]) );
+};
+
+#endif // ACTION_NAMES_H_
diff --git a/game/code/ai/actor/ActorAnimationUFO.cpp b/game/code/ai/actor/ActorAnimationUFO.cpp
new file mode 100644
index 0000000..4864b91
--- /dev/null
+++ b/game/code/ai/actor/ActorAnimationUFO.cpp
@@ -0,0 +1,107 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Name of subsystem or component this class belongs to.
+//
+// Description: This file contains the implementation of...
+//
+// Authors: Name Here
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai/actor/actoranimationufo.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+const float DEGREES_PER_SECOND = 20.0f;
+const float RADIANS_PER_MILLISECOND = rmt::DegToRadian( DEGREES_PER_SECOND ) / 1000.0f;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// ActorAnimationUFO::ActorAnimationUFO
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+ActorAnimationUFO::ActorAnimationUFO():
+m_NumUpdates( 0 )
+{
+
+}
+
+//===========================================================================
+// ActorAnimationUFO::~ActorAnimationUFO
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+ActorAnimationUFO::~ActorAnimationUFO()
+{
+
+}
+
+//===========================================================================
+// ActorAnimationUFO::Update
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+bool ActorAnimationUFO::Update( const rmt::Matrix& currTransform,
+ rmt::Matrix* newTransform,
+ float deltaTime,
+ tCompositeDrawable* compDraw )
+{
+ float angle = deltaTime * RADIANS_PER_MILLISECOND;
+
+ // Rotate the ufo
+ rmt::Matrix rotationMatrix;
+ rotationMatrix.Identity();
+ rotationMatrix.FillRotateY( angle );
+ newTransform->Mult( rotationMatrix, currTransform );
+
+ if ( (m_NumUpdates % 100) == 0 )
+ {
+ rotationMatrix.OrthoNormal();
+ }
+
+ m_NumUpdates++;
+ // Matrix always updated
+ return true;
+}
+
+
diff --git a/game/code/ai/actor/ActorAnimationUFO.h b/game/code/ai/actor/ActorAnimationUFO.h
new file mode 100644
index 0000000..e08a59b
--- /dev/null
+++ b/game/code/ai/actor/ActorAnimationUFO.h
@@ -0,0 +1,69 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: actoranimationufo
+//
+// Description: Procedural animation for the UFO
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef ACTORANIMATIONUFO_H
+#define ACTORANIMATIONUFO_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai/actor/actoranimation.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+//
+//
+// Constraints:
+//
+//===========================================================================
+class ActorAnimationUFO : public ActorAnimation
+{
+ public:
+ ActorAnimationUFO();
+ virtual ~ActorAnimationUFO();
+
+ virtual void SetState( int state ){}
+ // Apply animation, return true/false indicating whether or not the returned animation is different
+ // from the given one
+ virtual bool Update( const rmt::Matrix& currTransform, rmt::Matrix* newTransform, float deltaTime, tCompositeDrawable* = NULL );
+
+ int m_NumUpdates;
+
+ protected:
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow ActorAnimationUFO from being copied and assigned.
+ ActorAnimationUFO( const ActorAnimationUFO& );
+ ActorAnimationUFO& operator=( const ActorAnimationUFO& );
+
+
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/actor.cpp b/game/code/ai/actor/actor.cpp
new file mode 100644
index 0000000..b6854f0
--- /dev/null
+++ b/game/code/ai/actor/actor.cpp
@@ -0,0 +1,251 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: actor
+//
+// Description: actor implementation
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai/actor/actor.h>
+#include <ai/actor/actordsg.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <render/intersectmanager/intersectmanager.h>
+#include <render/dsg/fenceentitydsg.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+
+Actor::Actor():
+mp_StateProp( NULL ),
+m_ShouldDespawn( true ),
+m_IsInDSG( false )
+{
+ // Lets init the m_IntersectionSphere variable to be at a ridiculous coordinate so
+ // that it will be filled on update
+ m_IntersectionSphere.centre = rmt::Vector( 0, 0, 0 );
+ m_IntersectionSphere.radius = 0.01f;
+}
+
+Actor::~Actor()
+{
+ tRefCounted::Release( mp_StateProp );
+}
+
+void Actor::SetPosition( const rmt::Vector& position )
+{
+ m_PreviousPosition = position;
+ mp_StateProp->SetPosition( position );
+}
+
+void Actor::GetPosition( rmt::Vector* pPosition )const
+{
+ mp_StateProp->GetPosition( pPosition );
+}
+
+
+void Actor::GetTransform( rmt::Matrix* pTransform )const
+{
+ mp_StateProp->GetTransform( pTransform );
+}
+
+const ActorDSG* Actor::GetDSG() const
+{
+ return mp_StateProp;
+}
+
+void Actor::SetTransform( const rmt::Matrix& transform )
+{
+ m_PreviousPosition = transform.Row(3);
+ mp_StateProp->SetTransform( transform );
+}
+
+
+
+void
+Actor::SetState( int state )
+{
+ mp_StateProp->SetState( state );
+}
+
+unsigned int
+Actor::GetState()const
+{
+ return mp_StateProp->GetState();
+}
+
+const char*
+Actor::GetName()const
+{
+ return mp_StateProp->GetName();
+}
+
+tUID
+Actor::GetUID()const
+{
+ return mp_StateProp->GetUID();
+}
+
+tUID
+Actor::GetStatePropUID()const
+{
+ return mp_StateProp->GetStatePropUID();
+}
+
+void
+Actor::SetUID( tUID uid )
+{
+ mp_StateProp->SetUID( uid );
+}
+
+void
+Actor::SetStatePropID( unsigned int id )
+{
+ mp_StateProp->SetID( id );
+}
+
+void
+Actor::AddToDSG()
+{
+ if ( m_IsInDSG == false )
+ {
+ WorldRenderLayer* wrl = static_cast< WorldRenderLayer* > ( GetRenderManager()->mpLayer( RenderEnums::LevelSlot ) );
+ wrl->pWorldScene()->Add( mp_StateProp );
+ m_IsInDSG = true;
+ }
+}
+
+void Actor::RemoveFromDSG()
+{
+ if ( m_IsInDSG )
+ {
+ WorldRenderLayer* wrl = static_cast< WorldRenderLayer* > ( GetRenderManager()->mpLayer( RenderEnums::LevelSlot ) );
+ wrl->pWorldScene()->Remove( mp_StateProp );
+ m_IsInDSG = false;
+ }
+}
+
+void Actor::LoadShield( const char* statePropName )
+{
+ if ( mp_StateProp != NULL )
+ {
+ mp_StateProp->LoadShield( statePropName );
+ }
+}
+
+void Actor::LoadTractorBeam( const char* statePropName )
+{
+ if ( mp_StateProp != NULL )
+ {
+ mp_StateProp->LoadTractorBeam( statePropName );
+ }
+}
+
+void Actor::ActivateTractorBeam()
+{
+ if ( mp_StateProp != NULL )
+ mp_StateProp->ActivateTractorBeam();
+}
+
+void Actor::DeactivateTractorBeam()
+{
+ if ( mp_StateProp != NULL )
+ mp_StateProp->DeactivateTractorBeam();
+}
+
+bool Actor::IsTractorBeamOn()const
+{
+ if ( mp_StateProp != NULL )
+ return mp_StateProp->IsTractorBeamOn();
+ else
+ return false;
+}
+
+void Actor::FillIntersectionList( const rmt::Vector& position, float radius )
+{
+ m_IntersectionList.Clear();
+ FillIntersectionListDynamics( position, radius );
+ FillIntersectionListStatics( position, radius );
+
+
+ m_IntersectionSphere.centre = position;
+ m_IntersectionSphere.radius = radius;
+}
+
+void Actor::FillIntersectionListDynamics( const rmt::Vector& position, float radius )
+{
+ m_IntersectionList.ClearDynamics();
+ // Iterate through the dynamics and grab them all
+ ReserveArray< DynaPhysDSG* > dpList(200);
+ GetIntersectManager()->FindDynaPhysElems( const_cast< rmt::Vector& >( position ), radius, dpList );
+ for ( int i = 0 ; i < dpList.mUseSize ; i++ )
+ {
+ DynaPhysDSG* object = dpList[i];
+ // Lets not add ourselves
+ if ( object->GetAIRef() == PhysicsAIRef::ActorStateProp )
+ {
+ const ActorDSG* actordsg = static_cast< const ActorDSG* >( object );
+ if ( actordsg == GetDSG() )
+ continue;
+
+ // Are the ids the same? Dont make them intersect if they are
+ if ( actordsg->GetID() == GetDSG()->GetID() )
+ continue;
+ }
+
+ sim::SimState* simState = object->GetSimState();
+ m_IntersectionList.AddDynamic( simState, object );
+ }
+}
+
+void Actor::FillIntersectionListStatics( const rmt::Vector& position, float radius )
+{
+ m_IntersectionList.ClearStatics();
+ // Iterate through the dynamics and grab them all
+ ReserveArray< StaticPhysDSG* > spList(200);
+ GetIntersectManager()->FindStaticPhysElems( const_cast< rmt::Vector& >( position ), radius, spList );
+ for ( int i = 0 ; i < spList.mUseSize ; i++ )
+ {
+ StaticPhysDSG* object = spList[i];
+ // Lets not add ourselves
+ if ( object != static_cast< const StaticPhysDSG* >( GetDSG() ) )
+ {
+ sim::SimState* simState = object->GetSimState();
+ m_IntersectionList.AddStatic( simState );
+ }
+ }
+ FillIntersectionListFence( position, radius );
+}
+
+void Actor::FillIntersectionListFence( const rmt::Vector& position, float radius )
+{
+ m_IntersectionList.ClearFencePieces();
+
+ ReserveArray< FenceEntityDSG* > feList(200);
+ GetIntersectManager()->FindFenceElems( const_cast< rmt::Vector& > (position), radius, feList );
+ for ( int i = 0 ; i < feList.mUseSize ; i++ )
+ {
+ FenceEntityDSG* object = feList[i];
+ m_IntersectionList.AddFencePiece( object );
+ }
+}
diff --git a/game/code/ai/actor/actor.h b/game/code/ai/actor/actor.h
new file mode 100644
index 0000000..d458f70
--- /dev/null
+++ b/game/code/ai/actor/actor.h
@@ -0,0 +1,127 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: actor
+//
+// Description: actor implementation
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef ACTOR_H
+#define ACTOR_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <p3d/refcounted.hpp>
+#include <render/DSG/StatePropDSG.h>
+#include <ai/actor/intersectionlist.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class Behaviour;
+class ActorDSG;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Interface class for AI entities. Namely bee cameras and the UFO boss
+//
+// Constraints:
+//
+//
+//===========================================================================
+class Actor : public tRefCounted
+{
+ public:
+ Actor();
+ virtual ~Actor();
+
+
+ virtual bool Init( const char* statePropname, const char* instanceName )= 0;
+ virtual void Update( unsigned int timeInMS )=0;
+ virtual void AddToDSG();
+ virtual void RemoveFromDSG();
+ virtual void AddBehaviour( Behaviour* ) = 0;
+ virtual void Activate(){}
+ virtual void DeactivateBehaviours(){}
+ virtual void SetPosition( const rmt::Vector& position );
+ virtual void GetPosition( rmt::Vector* pPosition )const;
+ virtual void GetTransform( rmt::Matrix* pTransform )const;
+ virtual const ActorDSG* GetDSG()const;
+ virtual void SetTransform( const rmt::Matrix& transform );
+ virtual void SetState( int state );
+ // Set the ID associated with every StatePropDSG, used to determine
+ // relationship with projectile/weapon so they won't hit each other
+ virtual void SetStatePropID( unsigned id );
+ virtual unsigned int GetState()const;
+ virtual const char* GetName()const;
+ virtual tUID GetUID()const;
+ virtual void SetUID( tUID );
+ virtual tUID GetStatePropUID()const;
+ virtual void LookAt( const rmt::Vector&, unsigned int timeInMS )=0;
+ virtual void SetRotationSpeed( float degreesPerSecond )=0;
+ virtual void ReleaseBehaviours()=0;
+ virtual void MoveTo( const rmt::Vector& destination, float speed )=0;
+ virtual bool IsMoving()const { return false; }
+ virtual void LoadShield( const char* statePropName );
+ virtual void LoadTractorBeam( const char* statePropName );
+ void ActivateTractorBeam();
+ void DeactivateTractorBeam();
+ bool IsTractorBeamOn()const;
+ // Sets whether or not this object should despawn on
+ // is they get to far away from the avatar. defaults to true
+ void SetShouldDespawn( bool despawn ){ m_ShouldDespawn = despawn; }
+ bool ShouldDespawn()const { return m_ShouldDespawn; }
+
+ // Queries all statics and dynamics that reside within the given position/radius
+ // and add them to the m_IntersectionList
+ void FillIntersectionList( const rmt::Vector& position, float radius );
+ // Queries all the dynamics around the given point and adds them to the m_IntersectinList
+ void FillIntersectionListDynamics( const rmt::Vector& position, float radius );
+ // Ditto for statics
+ void FillIntersectionListStatics( const rmt::Vector& position, float radius );
+ void FillIntersectionListFence( const rmt::Vector& position, float radius );
+
+ protected:
+
+ ActorDSG* mp_StateProp;
+ bool m_ShouldDespawn;
+
+ // Holds all the collision volumes encompassed by the m_IntersectionSphere
+ // Used for collision detection / movement
+ IntersectionList m_IntersectionList;
+ // Determines what the IntersectionList holds
+ rmt::Sphere m_IntersectionSphere;
+ rmt::Vector m_PreviousPosition;
+
+ bool m_IsInDSG;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow Actor from being copied and assigned.
+ Actor( const Actor& );
+ Actor& operator=( const Actor& );
+
+
+};
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/actoranimation.h b/game/code/ai/actor/actoranimation.h
new file mode 100644
index 0000000..6b26ff8
--- /dev/null
+++ b/game/code/ai/actor/actoranimation.h
@@ -0,0 +1,72 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ActorAnimation
+//
+// Description: Actor animation driver
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef ACTORANIMATION_H
+#define ACTORANIMATION_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// A class that Actors can use to have a hardcoded, procedural animation implementation
+// instead of a long memory-expensive one exported from Maya
+//
+// Constraints:
+//
+//
+//===========================================================================
+class ActorAnimation
+{
+ public:
+ ActorAnimation(){}
+ virtual ~ActorAnimation(){ }
+ virtual void SetState( int state )=0;
+ // Apply animation, return true/false indicating whether or not the returned animation is different
+ // from the given one
+ virtual bool Update( const rmt::Matrix& currTransform, rmt::Matrix* newTransform, float deltaTime, tCompositeDrawable* = NULL )=0;
+
+ protected:
+
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow ActorAnimation from being copied and assigned.
+};
+
+class StatePropDSGProcAnimator
+{
+public:
+ StatePropDSGProcAnimator() {};
+ virtual ~StatePropDSGProcAnimator() {};
+ virtual void Advance( float Deltams ) {};
+ virtual void UpdateForRender( tCompositeDrawable* Drawable ) {};
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/actoranimationwasp.cpp b/game/code/ai/actor/actoranimationwasp.cpp
new file mode 100644
index 0000000..2b60034
--- /dev/null
+++ b/game/code/ai/actor/actoranimationwasp.cpp
@@ -0,0 +1,183 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ActorAnimationWasp
+//
+// Description: Actor animation driver for wasps. Encodes the figure eight
+// pattern that they fly procedurally
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai/actor/actoranimationwasp.h>
+#include <radmath/trig.hpp>
+#include <radmath/vector.hpp>
+#include <radmath/matrix.hpp>
+#include <radmath/quaternion.hpp>
+#include <p3d/anim/compositedrawable.hpp>
+#include <radtime.hpp>
+#include <raddebug.hpp>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+ActorAnimationWasp::ActorAnimationWasp():
+m_CurrentState( 0 ),
+m_CurrentYOffset( 0 ),
+m_CurrentXOffset( 0,0,0 ),
+m_YBias( 0.5f ),
+m_XBias( 0.5f )
+{
+
+}
+
+ActorAnimationWasp::~ActorAnimationWasp()
+{
+
+}
+
+void ActorAnimationWasp::SetState( int state )
+{
+
+}
+
+bool ActorAnimationWasp::Update( const rmt::Matrix& currTransform, rmt::Matrix* newTransform, float deltaTime, tCompositeDrawable* compDraw )
+{
+
+ // Fix the magnitude of this thing so that the input angle to sin and cos is in a decent range (0 - 2PI)
+ unsigned int iCurrentTime = radTimeGetMilliseconds();
+
+ static float C = 317;
+ /*
+ {
+ static bool initedWatcher = false;
+ if ( inited == false )
+ {
+ radDbgWatchAddFloat( &C, "wasp updates ", "wasp", NULL, NULL, 100.0f, 600.0f );
+ inited = true;
+ }
+ }*/
+
+ unsigned int iClippedTime = iCurrentTime % (int)( C * rmt::PI_2 );
+ float fClampedTime = (float)iClippedTime / C;
+ rAssert( fClampedTime >= 0 && fClampedTime <= rmt::PI_2 );
+
+
+ float fClippedTime = static_cast< float > ( iClippedTime );
+ float fCurrentTime = static_cast< float > ( iCurrentTime );
+
+ *newTransform = currTransform;
+ float oldY = m_CurrentYOffset;
+
+ m_CurrentYOffset = rmt::Sin( fClampedTime * 2.0f ) * m_YBias;
+ float scalarXOffset = rmt::Cos( fClampedTime ) * m_XBias;
+
+ // Apply the height offset the given transform matrix
+ newTransform->m[3][1] += m_CurrentYOffset - oldY;
+
+ // Undo old transform
+ newTransform->Row(3) -= m_CurrentXOffset;
+ // Apply the newtransform
+ m_CurrentXOffset = scalarXOffset * newTransform->Row(0);
+ newTransform->Row(3) += m_CurrentXOffset;
+
+
+
+ return true;
+}
+
+WingAnimator::WingAnimator( float FrameRate ) :
+ mTranKeys( 0 ),
+ mRotKeys( 0 ),
+ mAnimSpeed( 0.03f / FrameRate ),
+ mTime( 0.0f ),
+ mFrame( 0.0f ),
+ mRWingIndex( -1 ),
+ mLWingIndex( -1 ),
+ mNumKeys( 2 )
+{
+ mTranKeys = new rmt::Vector[ mNumKeys ];
+ rAssert( mTranKeys );
+ mRotKeys = new rmt::Quaternion[ mNumKeys ];
+ rAssert( mRotKeys );
+ mTranKeys[ 0 ].Set( 0.0743f, -0.0114f, -0.0003f );
+ mTranKeys[ 1 ].Set( -0.1024f, 0.1154f, 0.1085f );
+ mRotKeys[ 0 ].x = -0.0017f;
+ mRotKeys[ 0 ].y = 0.0000f;
+ mRotKeys[ 0 ].z = -0.3592f;
+ mRotKeys[ 0 ].w = -0.9332f;
+ mRotKeys[ 1 ].x = 0.3550f;
+ mRotKeys[ 1 ].y = -0.2419f;
+ mRotKeys[ 1 ].z = 0.5907f;
+ mRotKeys[ 1 ].w = -0.6830f;
+}
+
+WingAnimator::~WingAnimator()
+{
+ delete [] mTranKeys;
+ delete [] mRotKeys;
+};
+
+void WingAnimator::Advance( float Deltams )
+{
+ const static float ANIM_TIME = 4.0f / 30.0f;
+ const static float FRAME_RATIO = 1.0f / ( ANIM_TIME / 2.0f );
+ mTime += ( Deltams * mAnimSpeed );
+ while( mTime >= ANIM_TIME )
+ {
+ mTime -= ANIM_TIME;
+ }
+ mFrame = mTime * FRAME_RATIO;
+ // Ping-pong the frame number.
+ if( mFrame > 1.0f )
+ {
+ mFrame = 2.0f - mFrame;
+ }
+}
+
+void WingAnimator::UpdateForRender( tCompositeDrawable* Drawable )
+{
+ tPose* pose = Drawable->GetPose();
+ if( mRWingIndex < 0 )
+ {
+ mRWingIndex = pose->FindJointIndex( "Wing_R" );
+ rAssert( mRWingIndex >= 0 );
+ }
+ if( mLWingIndex < 0 )
+ {
+ mLWingIndex = pose->FindJointIndex( "Wing_L" );
+ rAssert( mLWingIndex >= 0 );
+ }
+ rmt::Vector tran;
+ tran.Scale( 1.0f - mFrame, mTranKeys[ 0 ] );
+ tran.ScaleAdd( mFrame, mTranKeys[ 1 ] );
+ rmt::Quaternion rot;
+ rot.Slerp(mRotKeys[ 0 ], mRotKeys[ 1 ], mFrame );
+
+ tPose::Joint* joint;
+ joint = pose->GetJoint( mLWingIndex );
+ rot.ConvertToMatrix( &joint->objectMatrix );
+ joint->objectMatrix.FillTranslate( tran );
+ tran.x *= -1.0f;
+ rot.y *= -1.0f;
+ rot.z *= -1.0f;
+ joint = pose->GetJoint( mRWingIndex );
+ rot.ConvertToMatrix( &joint->objectMatrix );
+ joint->objectMatrix.FillTranslate( tran );
+ pose->SetPoseReady( false );
+} \ No newline at end of file
diff --git a/game/code/ai/actor/actoranimationwasp.h b/game/code/ai/actor/actoranimationwasp.h
new file mode 100644
index 0000000..4a94fe5
--- /dev/null
+++ b/game/code/ai/actor/actoranimationwasp.h
@@ -0,0 +1,97 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ActorAnimationWasp
+//
+// Description: Actor animation driver for wasps. Encodes the figure eight
+// pattern that they fly procedurally
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef ACTORANIMATIONWASP_H
+#define ACTORANIMATIONWASP_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai/actor/actoranimation.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class tCompositeDrawable;
+class rmt::Vector;
+class rmt::Quaternion;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// A class that Actors can use to have a hardcoded, procedural animation implementation
+// instead of a long memory-expensive one exported from Maya
+//
+// Constraints:
+//
+//
+//===========================================================================
+class ActorAnimationWasp : public ActorAnimation
+{
+ public:
+ ActorAnimationWasp();
+ virtual ~ActorAnimationWasp();
+ virtual void SetState( int state );
+ virtual bool Update( const rmt::Matrix& currTransform, rmt::Matrix* newTransform, float deltaTime, tCompositeDrawable* = NULL );
+
+ protected:
+
+ // Which state the stateprop is currently in
+ int m_CurrentState;
+
+
+ float m_CurrentYOffset;
+ rmt::Vector m_CurrentXOffset;
+ float m_YBias;
+ float m_XBias;
+
+
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow ActorAnimationWasp from being copied and assigned.
+};
+
+class WingAnimator : public StatePropDSGProcAnimator
+{
+public:
+ // Note that frame rate is in animation frame rate, not the game frame rate.
+ WingAnimator( float FrameRate = 30.0f );
+ virtual ~WingAnimator();
+ virtual void Advance( float Deltams );
+ virtual void UpdateForRender( tCompositeDrawable* Drawable );
+
+protected:
+ rmt::Vector* mTranKeys;
+ rmt::Quaternion* mRotKeys;
+ float mAnimSpeed;
+ float mTime;
+ float mFrame;
+ char mRWingIndex;
+ char mLWingIndex;
+ char mNumKeys;
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/actordsg.cpp b/game/code/ai/actor/actordsg.cpp
new file mode 100644
index 0000000..af93d74
--- /dev/null
+++ b/game/code/ai/actor/actordsg.cpp
@@ -0,0 +1,585 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ActorDSG
+//
+// Description: ActorDSG is a statepropdsg that has an id that will indicate the
+// relationship between an object and the objects that it can shoot
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai/actor/actordsg.h>
+#include <ai/actor/actormanager.h>
+#include <render/breakables/breakablesmanager.h>
+#include <stateprop/statepropdata.hpp>
+#include <p3d/matrixstack.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <simphysics/articulatedphysicsobject.hpp>
+#include <debug/profiler.h>
+#include <worldsim/coins/coinmanager.h>
+#include <render/intersectmanager/intersectmanager.h>
+#include <ai/actor/intersectionlist.h>
+#include <constants/actorenum.h>
+#include <p3d/view.hpp>
+#include <p3d/light.hpp>
+#include <raddebug.hpp>
+
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+unsigned int ActorDSG::s_IDCounter = 0;
+
+static const int REMOVE_FROM_WORLD = 0;
+static const int SPAWN_5_COINS = 1;
+static const int REMOVE_COLLISION_VOLUME = 2;
+static const int FIRE_ENERGY_BOLT = 3;
+static const int KILL_SPEED = 4;
+static const int SPAWN_10_COINS = 5;
+static const int SPAWN_15_COINS = 6;
+static const int SPAWN_20_COINS = 7;
+
+// States for the shield
+static const unsigned int SHIELD_STATE_POWER_UP = 0;
+static const unsigned int SHIELD_STATE_IDLE = 1;
+static const unsigned int SHIELD_STATE_HIT = 2;
+static const unsigned int SHIELD_STATE_DESTROYED = 3;
+
+// States for the tractor beam
+static const unsigned int TRACTOR_BEAM_ACTIVATE = 0;
+static const unsigned int TRACTOR_BEAM_ON = 1;
+static const unsigned int TRACTOR_BEAM_DEACTIVATE = 2;
+static const unsigned int TRACTOR_BEAM_OFF = 3;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+class CStateProp;
+static tUID s_UfoName = tName::MakeUID("spaceship");
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+
+
+//===========================================================================
+// Debugging / Watching
+//===========================================================================
+
+static float AMB_RED_SCALE = 2.0f;
+static float AMB_GREEN_SCALE = 2.0f;
+static float AMB_BLUE_SCALE = 2.0f;
+static float DIR_RED_SCALE = 1.0f;
+static float DIR_GREEN_SCALE = 1.0f;
+static float DIR_BLUE_SCALE = 1.0f;
+
+
+
+ActorDSG::ActorDSG():
+m_ID( ++s_IDCounter ),
+m_ShieldProp( NULL ),
+m_TractorBeamProp( NULL ),
+m_ShieldEnabled( false ),
+mPhysicsProperties( NULL )
+{
+}
+
+ActorDSG::~ActorDSG()
+{
+ if ( m_ShieldProp != NULL )
+ {
+ m_ShieldProp->Release();
+ m_ShieldProp = NULL;
+ }
+ if ( m_TractorBeamProp != NULL )
+ {
+ m_TractorBeamProp->ReleaseVerified();
+ m_TractorBeamProp = NULL;
+ }
+ if ( mPhysicsProperties != NULL )
+ {
+ mPhysicsProperties->Release();
+ }
+}
+
+sim::Solving_Answer
+ActorDSG::PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+{
+ // Check to see that we aren't going to collide with an object that has the same ID
+ // that we do
+ if ( pCollidedObj->mAIRefIndex == PhysicsAIRef::ActorStateProp )
+ {
+ rAssert( dynamic_cast< ActorDSG* >( static_cast< tRefCounted* >( pCollidedObj->mAIRefPointer) ) );
+ ActorDSG* pStatePropDSG = static_cast< ActorDSG* >( pCollidedObj->mAIRefPointer );
+ if ( pStatePropDSG->GetID() == GetID() )
+ {
+ return sim::Solving_Aborted;
+ }
+ }
+ HandleEvent( StatePropDSG::FEATHER_TOUCH );
+
+ return sim::Solving_Continue;
+}
+
+void
+ActorDSG::SetRank(rmt::Vector& irRefPosn, rmt::Vector& mViewVector)
+{
+ if ( mpStateProp->GetUID() != s_UfoName )
+ {
+ IEntityDSG::SetRank( irRefPosn, mViewVector );
+ }
+ else
+ {
+ // Final hack to make the UFO always draw last
+ // The UFO has a *huge* bounding sphere that makes it
+ // always drawn after the vehicle explosion, cutting it
+ // off nastily, lets fix it and force the thing to be drawn first
+ mRank = FLT_MAX;
+ }
+}
+
+
+sim::Solving_Answer
+ActorDSG::PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision)
+{
+ // Calculate impulse
+ float impulseMagSqr = impulse.MagnitudeSqr();
+ // If the shield is on, impulse has to be above an very high number to cause it be
+ // be destroyed
+
+ const float FORCE_REQUIRED_TO_BREAK_SHIELD = 1600000;
+ const float FORCE_REQUIRE_TO_DESTROY = 800000;
+
+ if ( m_ShieldEnabled )
+ {
+ if ( impulseMagSqr > FORCE_REQUIRED_TO_BREAK_SHIELD )
+ {
+ DestroyShield();
+ }
+ else
+ {
+ PlayShieldGettingHit();
+ }
+ }
+ else
+ {
+ if ( impulseMagSqr > FORCE_REQUIRE_TO_DESTROY )
+ {
+ HandleEvent( DESTROYED );
+ }
+ }
+
+ AddToSimulation();
+
+ return CollisionEntityDSG::PostReactToCollision(impulse, inCollision);
+}
+
+
+void ActorDSG::ApplyForce( const rmt::Vector& direction, float force )
+{
+ // Force over a certain
+ const float SHIELD_BREAKING_FORCE = 400.0f;
+ const float FORCE_REQUIRED_TO_DESTROY = 400.0f;
+ if ( m_ShieldEnabled )
+ {
+ if ( force < SHIELD_BREAKING_FORCE )
+ {
+ PlayShieldGettingHit();
+ }
+ else
+ {
+ DestroyShield();
+ }
+ }
+ else
+ {
+ if ( force >= FORCE_REQUIRED_TO_DESTROY )
+ {
+ DestroyShield();
+ HandleEvent( StatePropDSG::DESTROYED );
+ DynaPhysDSG::ApplyForce( direction, force );
+ }
+ }
+}
+
+
+void
+ActorDSG::RecieveEvent( int callback , CStateProp* stateProp )
+{
+ switch ( callback )
+ {
+ case FIRE_ENERGY_BOLT:
+ {
+ tName name = "waspray";
+ GetActorManager()->FireProjectile( name.GetUID(), mPosn, -mTransform.Row(2), m_ID );
+ }
+ break;
+ case REMOVE_FROM_WORLD:
+ GetActorManager()->RemoveActorByDSGPointer( this, true );
+ break;
+ default:
+ StatePropDSG::RecieveEvent( callback, stateProp );
+ break;
+ };
+}
+
+void
+ActorDSG::HandleEvent( StatePropDSG::Event event )
+{
+ // If a shield is enabled, then only destroyed events should get
+ StatePropDSG::HandleEvent( event );
+}
+
+
+bool
+ActorDSG::LoadShield( const char* statePropName )
+{
+ bool loadedOk = false;
+ CStatePropData* statePropData = p3d::find< CStatePropData > ( statePropName );
+ if ( statePropData != NULL )
+ {
+ CStateProp* stateprop = CStateProp::CreateStateProp( statePropData, 0 );
+ if ( stateprop != NULL )
+ {
+ m_ShieldEnabled = true;
+ tRefCounted::Assign( m_ShieldProp, stateprop);
+ loadedOk = true;
+ SetPhysicsProperties( 100.0f, 0.8f, 1.15f, 0.0f );
+ }
+ }
+ return loadedOk;
+}
+
+bool
+ActorDSG::LoadTractorBeam( const char* statePropName )
+{
+ bool loadedOk = false;
+ CStatePropData* statePropData = p3d::find< CStatePropData > ( statePropName );
+ if ( statePropData != NULL )
+ {
+ CStateProp* stateprop = CStateProp::CreateStateProp( statePropData, TRACTOR_BEAM_OFF );
+ if ( stateprop != NULL )
+ {
+ tRefCounted::Assign( m_TractorBeamProp, stateprop);
+ loadedOk = true;
+ }
+ }
+ return loadedOk;
+}
+
+void
+ActorDSG::ActivateTractorBeam()
+{
+ if ( m_TractorBeamProp != NULL )
+ {
+ if ( m_TractorBeamProp->GetState() == TRACTOR_BEAM_OFF )
+ {
+ m_TractorBeamProp->SetState( TRACTOR_BEAM_ACTIVATE );
+ }
+ }
+}
+
+void
+ActorDSG::DeactivateTractorBeam()
+{
+ if ( m_TractorBeamProp != NULL )
+ {
+ if ( m_TractorBeamProp->GetState() == TRACTOR_BEAM_ON ||
+ m_TractorBeamProp->GetState() == TRACTOR_BEAM_ACTIVATE )
+ {
+ m_TractorBeamProp->SetState( TRACTOR_BEAM_DEACTIVATE );
+ }
+ }
+}
+
+bool
+ActorDSG::IsTractorBeamOn()const
+{
+ if ( m_TractorBeamProp == NULL )
+ return false;
+
+ return m_TractorBeamProp->GetState() != TRACTOR_BEAM_OFF;
+}
+
+
+void
+ActorDSG::Display()
+{
+ // Have the rad debugging functions been attached yet?
+ // Check, and if not, add them once
+#ifndef RAD_RELEASE
+
+ static bool radattached = false;
+
+ if ( radattached == false )
+ {
+ radDbgWatchAddFloat( &AMB_RED_SCALE, "Amb Red Scale", "Wasp lighting", NULL, NULL, 0, 4.0f );
+ radDbgWatchAddFloat( &AMB_GREEN_SCALE, "Amb Green Scale", "Wasp lighting", NULL, NULL, 0, 4.0f );
+ radDbgWatchAddFloat( &AMB_BLUE_SCALE, "Amb Blue Scale", "Wasp lighting", NULL, NULL, 0, 4.0f );
+ radDbgWatchAddFloat( &DIR_RED_SCALE, "Dir Red Scale", "Wasp lighting", NULL, NULL, 0, 4.0f );
+ radDbgWatchAddFloat( &DIR_GREEN_SCALE, "Dir Green Scale", "Wasp lighting", NULL, NULL, 0, 4.0f );
+ radDbgWatchAddFloat( &DIR_BLUE_SCALE, "Dir Blue Scale", "Wasp lighting", NULL, NULL, 0, 4.0f );
+ radattached = true;
+ }
+
+
+#endif
+
+ // Aryan and Yayoi want the beecamera brightened artificially
+ // There doesnt appear to be a nice + quick solution
+ // Do a quick UID compare then brighten the level lights
+ // We will reset them after drawing is finished
+ static tUID beecameraUID = tName::MakeUID( "beecamera" );
+ tColour origColours[MAX_VIEW_LIGHTS];
+ tColour origAmbient;
+ if ( mpStateProp->GetUID() == beecameraUID )
+ {
+ tView* view = p3d::context->GetView();
+ for ( unsigned int i = 0 ; i < MAX_VIEW_LIGHTS ; i ++ )
+ {
+ tLight* light = view->GetLight( i );
+ if ( light )
+ {
+ origColours[i] = light->GetColour();
+
+ int r = (int)( (float) origColours[i].Red() * DIR_RED_SCALE );
+ int g = (int)( (float) origColours[i].Green() * DIR_GREEN_SCALE );
+ int b = (int)( (float) origColours[i].Blue() * DIR_BLUE_SCALE );
+
+ if ( r > 255 ) r = 255;
+ if ( g > 255 ) g = 255;
+ if ( b > 255 ) b = 255;
+
+ tColour newColour( r, g, b );
+ light->SetColour( newColour );
+ }
+ }
+ origAmbient = p3d::pddi->GetAmbientLight();
+
+ int r = (int)( (float) origAmbient.Red() * AMB_RED_SCALE );
+ int g = (int)( (float) origAmbient.Green() * AMB_GREEN_SCALE );
+ int b = (int)( (float) origAmbient.Blue() * AMB_BLUE_SCALE );
+
+ if ( r > 255 ) r = 255;
+ if ( g > 255 ) g = 255;
+ if ( b > 255 ) b = 255;
+
+ tColour scaledAmbient( r, g, b );
+ p3d::pddi->SetAmbientLight( scaledAmbient );
+ }
+
+
+#ifdef PROFILER_ENABLED
+ char profileName[] = " ActorDSG Display";
+#endif
+ DSG_BEGIN_PROFILE(profileName)
+ StatePropDSG::Display();
+ if ( m_ShieldProp != NULL )
+ {
+ // Draw the shield
+ p3d::stack->PushMultiply( mTransform );
+ m_ShieldProp->UpdateFrameControllersForRender();
+ m_ShieldProp->GetDrawable()->Display();
+ p3d::stack->Pop();
+ }
+ if ( m_TractorBeamProp != NULL )
+ {
+ // Draw the tractor beam
+ p3d::stack->PushMultiply( mTransform );
+ m_TractorBeamProp->Display();
+ p3d::stack->Pop();
+ }
+
+ // Reset the level lights for the beecamera
+ if ( mpStateProp->GetUID() == beecameraUID )
+ {
+ tView* view = p3d::context->GetView();
+ for ( unsigned int i = 0 ; i < MAX_VIEW_LIGHTS ; i ++ )
+ {
+ tLight* light = view->GetLight( i );
+ if ( light )
+ {
+ light->SetColour( origColours[i] );
+ }
+ }
+ // Reset the ambient
+ p3d::pddi->SetAmbientLight( origAmbient );
+ }
+
+
+ DSG_END_PROFILE(profileName)
+
+}
+
+void
+ActorDSG::AdvanceAnimation( float timeInMS )
+{
+ if ( m_ShieldProp )
+ {
+ m_ShieldProp->Update( timeInMS );
+ }
+ if ( m_TractorBeamProp )
+ {
+ m_TractorBeamProp->Update( timeInMS );
+ }
+
+
+ StatePropDSG::AdvanceAnimation( timeInMS );
+
+
+
+
+}
+
+void
+ActorDSG::DestroyShield()
+{
+ if ( m_ShieldProp != NULL )
+ {
+ m_ShieldEnabled = false;
+ m_ShieldProp->SetState( SHIELD_STATE_DESTROYED );
+ }
+}
+
+void
+ActorDSG::PlayShieldGettingHit()
+{
+ if ( m_ShieldProp != NULL )
+ {
+ if ( m_ShieldProp->GetState() != SHIELD_STATE_HIT )
+ {
+ m_ShieldProp->SetState( SHIELD_STATE_HIT );
+ }
+ }
+}
+
+void
+ActorDSG::RestoreShield()
+{
+ if ( m_ShieldProp )
+ {
+ m_ShieldEnabled = true;
+ m_ShieldProp->SetState( SHIELD_STATE_POWER_UP );
+ }
+}
+
+void
+ActorDSG::SetPhysicsProperties( float mass, float friction, float rest, float tang )
+{
+
+ if ( mPhysicsProperties == NULL )
+ return;
+
+ // We want to grab the volume so we can compute the density via the given mass
+ // and the simstate's volume
+ sim::SimStateArticulated* pSimStateArt = ( sim::SimStateArticulated* )( mpSimStateObj );
+ sim::ArticulatedPhysicsObject* physicsObject = static_cast< sim::ArticulatedPhysicsObject*>( pSimStateArt->GetSimulatedObject( -1 ));
+ float volume = physicsObject->GetVolume();
+
+ rAssert( volume != 0 );
+
+ float density = mass / volume;
+
+ mPhysicsProperties->SetRestCoeffCGS( rest );
+ mPhysicsProperties->SetFrictCoeffCGS( friction );
+ mPhysicsProperties->SetTangRestCoeffCGS( tang );
+ mPhysicsProperties->SetDensityCGS( density );
+
+ mpSimStateObj->SetPhysicsProperties( mPhysicsProperties );
+}
+
+void
+ActorDSG::LoadSetup( CStatePropData* statePropData,
+ int startState,
+ const rmt::Matrix& transform,
+ CollisionAttributes* ipCollAttr,
+ bool isMoveable,
+ bool isDynaLoaded,
+ tEntityStore* ipSearchStore )
+{
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ sim::PhysicsProperties* physicsProperties = new sim::PhysicsProperties;
+
+ StatePropDSG::LoadSetup( statePropData, startState, transform, ipCollAttr, isDynaLoaded, ipSearchStore );
+
+ tRefCounted::Assign( mPhysicsProperties, physicsProperties );
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+
+}
+
+void
+ActorDSG::GenerateCoins( int numCoins )
+{
+
+ // Generate coins, but take into account the fact that this object might be hovering
+ // over the ground.
+ // So generate coins at the top of the object below the stateprop
+ // First find the ground plane
+ bool foundPlane;
+ rmt::Vector deepestPosn, deepestNormal, groundPlaneNormal, groundPlanePosn;
+ rmt::Vector searchPosn = rPosition();
+ searchPosn.y += 100.0f;
+ GetIntersectManager()->FindIntersection( searchPosn, foundPlane, groundPlaneNormal, groundPlanePosn );
+ if ( foundPlane == false )
+ {
+ groundPlanePosn = rPosition();
+ groundPlanePosn.y -= 30.0f;
+ }
+ // Check to see if there any static objects directly underneath us
+ // find the midpoint between the current position and the ground position
+ rmt::Vector midpoint = ( groundPlanePosn + rPosition() ) * 0.5f;
+ float radius = (rPosition().y - groundPlanePosn.y );
+ ReserveArray< StaticPhysDSG* > spList(200);
+ GetIntersectManager()->FindStaticPhysElems( midpoint, radius , spList );
+ // Iterate through the static phys list and add them to an intersection list
+ IntersectionList staticList;
+ for ( int i = 0 ; i < spList.mUseSize; i++ )
+ {
+ staticList.AddStatic( spList[i]->GetSimState() );
+ }
+ // Now query our list using subcollision volume accuracy to pinpoint the precise location
+ // of intersection
+ rmt::Vector staticIntersection;
+ rmt::Vector start = rPosition();
+ rmt::Vector end = groundPlanePosn;
+ if ( staticList.TestIntersectionStatics( start, end, &staticIntersection ))
+ {
+ // There is a static in the way. spawn teh coins on top of this object
+ GetCoinManager()->SpawnCoins( numCoins, rPosition(), staticIntersection.y );
+ }
+ else
+ {
+ // The wasp is hovering above solid ground. Spawn the coins on the ground
+ GetCoinManager()->SpawnCoins( numCoins, rPosition(), groundPlanePosn.y );
+ }
+
+}
+
+int
+ActorDSG::CastsShadow()
+{
+ // Dont cast a shadow in the first (fade in) and last(exploded) states
+ int castsShadow;
+
+ unsigned int statepropState = GetState();
+ if ( statepropState == ActorEnum::eFadeIn || statepropState == ActorEnum::eDestroyed )
+ castsShadow = 0;
+ else
+ castsShadow = StaticPhysDSG::CastsShadow();
+
+ return castsShadow;
+}
+
+
+
+
diff --git a/game/code/ai/actor/actordsg.h b/game/code/ai/actor/actordsg.h
new file mode 100644
index 0000000..2662d1e
--- /dev/null
+++ b/game/code/ai/actor/actordsg.h
@@ -0,0 +1,128 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ActorDSG
+//
+// Description: ActorDSG is a statepropdsg that has an id that will indicate the
+// relationship between an object and the objects that it can shoot
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef ACTORDSG_H
+#define ACTORDSG_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <render/DSG/StatePropDSG.h>
+
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace sim
+{
+ class PhysicsProperties;
+};
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+//
+// Constraints:
+//
+//
+//===========================================================================
+class ActorDSG : public StatePropDSG
+{
+ public:
+ ActorDSG();
+ virtual ~ActorDSG();
+ virtual sim::Solving_Answer PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision );
+ virtual sim::Solving_Answer PostReactToCollision( rmt::Vector& impulse, sim::Collision& inCollision );
+ virtual void ApplyForce( const rmt::Vector& direction, float force );
+
+ // ID for each stateprop. Basic idea: every stateprop gets a unique
+ // integer identifier, except that projectiles that get fired have the same
+ // id as the object that launched them. In prereact, if the two collision
+ // objects have the same ID, solving is aborted.
+ void SetID( int id ) { m_ID = id; }
+ unsigned int GetID()const { return m_ID; }
+ virtual void RecieveEvent( int callback , CStateProp* stateProp );
+ void HandleEvent( StatePropDSG::Event );
+ virtual void Display();
+ virtual void AdvanceAnimation( float timeInMS );
+ virtual int GetAIRef() { return PhysicsAIRef::ActorStateProp; }
+
+ // Adding a shield to the prop
+ bool LoadShield( const char* statePropName );
+ void RestoreShield();
+ bool HasShield()const { return m_ShieldEnabled; }
+ // Tractor beams
+ bool LoadTractorBeam( const char* statePropName );
+ void ActivateTractorBeam();
+ void DeactivateTractorBeam();
+ bool IsTractorBeamOn()const;
+ virtual void SetRank(rmt::Vector& irRefPosn, rmt::Vector& mViewVector);
+
+ void SetPhysicsProperties( float mass, float friction, float rest, float tang );
+ virtual void LoadSetup( CStatePropData* statePropData,
+ int startState,
+ const rmt::Matrix& transform,
+ CollisionAttributes* ipCollAttr,
+ bool isMoveable,
+ bool isDynaLoaded,
+ tEntityStore* ipSearchStore = NULL );
+
+ // Dont use the stateprop shadow system, use the one provided
+ // by StaticPhysDSG
+ virtual int CastsShadow();
+ virtual void DisplaySimpleShadow() { StaticPhysDSG::DisplaySimpleShadow(); }
+
+ virtual void GenerateCoins( int numCoins );
+
+ protected:
+
+ void DestroyShield();
+ void PlayShieldGettingHit();
+
+ static unsigned int s_IDCounter;
+ unsigned int m_ID;
+
+ CStateProp* m_ShieldProp;
+ CStateProp* m_TractorBeamProp;
+ bool m_ShieldEnabled;
+ sim::PhysicsProperties* mPhysicsProperties;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow ActorDSG from being copied and assigned.
+ ActorDSG( const ActorDSG& );
+ ActorDSG& operator=( const ActorDSG& );
+
+
+};
+
+
+
+
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/actormanager.cpp b/game/code/ai/actor/actormanager.cpp
new file mode 100644
index 0000000..eaaf440
--- /dev/null
+++ b/game/code/ai/actor/actormanager.cpp
@@ -0,0 +1,769 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: actormanager
+//
+// Description: Actormanager class, for holding stateprops that have AI
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+#include <ai/actor/actormanager.h>
+#include <ai/actor/actor.h>
+#include <memory/srrmemory.h>
+#include <console/console.h>
+#include <ai/actor/flyingactor.h>
+#include <ai/actor/projectile.h>
+#include <ai/actor/attackbehaviour.h>
+#include <ai/actor/evasionbehaviour.h>
+#include <ai/actor/ufoattackbehaviour.h>
+#include <ai/actor/attractionbehaviour.h>
+#include <ai/actor/ufobeamalwaysonbehaviour.h>
+#include <worldsim/avatarmanager.h>
+#include <events/eventmanager.h>
+#include <p3d/utility.hpp>
+#include <raddebugwatch.hpp>
+#include <radmemorymonitor.hpp>
+#include <sstream>
+
+#include <stack>
+
+static const int MAX_NUM_ACTORS = 50;
+
+// Static variables
+float ActorManager::ActorRemovalRangeSqr = 65.0f * 65.0f;
+ActorManager* ActorManager::sp_Instance = 0;
+
+ActorManager* ActorManager::CreateInstance()
+{
+ rAssert( sp_Instance == 0 );
+
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ sp_Instance = new ActorManager();
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+
+ return sp_Instance;
+}
+
+ActorManager* ActorManager::GetInstance()
+{
+ return sp_Instance;
+}
+
+void ActorManager::DestroyInstance()
+{
+ delete sp_Instance;
+}
+
+ActorManager::ActorManager()
+{
+ m_ActorList.Allocate( MAX_NUM_ACTORS );
+ m_SpawnPointList.Allocate( 50 );
+ m_ActorBank.Allocate( MAX_NUM_ACTORS );
+ m_RemoveQueue.Allocate( MAX_NUM_ACTORS );
+ SetupConsoleFunctions();
+
+ GetEventManager()->AddListener( this, EVENT_STATEPROP_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_STATEPROP_ADDED_TO_SIM );
+
+}
+
+ActorManager::~ActorManager()
+{
+ for ( int i = 0 ; i < m_ActorList.mUseSize ; i++ )
+ {
+ m_ActorList[ i ]->Release();
+ }
+ m_ActorList.ClearUse();
+ for ( int i = 0 ; i < m_ActorBank.mUseSize ; i++ )
+ {
+ m_ActorBank[ i ]->Release();
+ }
+ m_ActorBank.ClearUse();
+ for ( int i = 0 ; i < m_RemoveQueue.mUseSize ; i++ )
+ {
+ m_RemoveQueue[i]->RemoveFromDSG();
+ m_RemoveQueue[ i ]->Release();
+ }
+ m_RemoveQueue.ClearUse();
+
+ GetEventManager()->RemoveListener( this, EVENT_STATEPROP_DESTROYED );
+ GetEventManager()->RemoveListener( this, EVENT_STATEPROP_ADDED_TO_SIM );
+}
+
+void
+ActorManager::AddActor( Actor* actor )
+{
+ actor->AddRef();
+ m_ActorList.Add( actor );
+}
+
+void
+ActorManager::AddActorToBank( Actor* actor )
+{
+ actor->AddRef();
+ m_ActorBank.Add( actor );
+}
+
+
+void
+ActorManager::RemoveActor( int index, bool removeFromDSG )
+{
+ Actor* actor = m_ActorList[ index ];
+ GetEventManager()->TriggerEvent( EVENT_ACTOR_REMOVED, actor );
+
+ // Turn off its behaviours
+ actor->DeactivateBehaviours();
+ m_ActorList.Remove( index );
+ m_RemoveQueue.Add( actor );
+}
+
+void
+ActorManager::RemoveActorByDSGPointer( ActorDSG* dsgObject, bool removeFromDSG )
+{
+ for ( int i = 0 ; i < m_ActorList.mUseSize ; i++ )
+ {
+ if ( dsgObject == m_ActorList[ i ]->GetDSG() )
+ {
+ RemoveActor( i, removeFromDSG );
+ break;
+ }
+ }
+}
+
+void
+ActorManager::RemoveAllActors()
+{
+ int i;
+ while ( m_ActorList.mUseSize > 0 )
+ {
+ RemoveActor( 0 );
+ }
+ for ( i = 0 ; i < m_ActorBank.mUseSize ; i++ )
+ {
+ m_ActorBank[i]->ReleaseVerified();
+ }
+ for ( i = 0 ; i < m_RemoveQueue.mUseSize ; i++ )
+ {
+ m_RemoveQueue[i]->RemoveFromDSG();
+ m_RemoveQueue[i]->ReleaseVerified();
+ }
+
+ m_ActorList.ClearUse();
+ m_ActorBank.ClearUse();
+ m_RemoveQueue.ClearUse();
+ //radMemoryMonitorSuspend();
+}
+
+
+void
+ActorManager::AddSpawnPoint( SpawnPoint* point )
+{
+ point->AddRef();
+ m_SpawnPointList.Add( point );
+}
+
+void
+ActorManager::FireProjectile( tUID typeName, const rmt::Vector& position, const rmt::Vector& direction, unsigned int ownerID )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ char instanceName[200];
+ static int count = 0;
+ count++;
+ sprintf( instanceName, "%s %d","wasp bolt", count);
+
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillHeading( direction, rmt::Vector(0,1,0) );
+ transform.FillTranslate( position );
+
+ tName p3dInstanceName( instanceName );
+ Actor* actor = CreateActor( typeName, p3dInstanceName.GetUID(), transform );
+ if ( actor != NULL )
+ {
+ Projectile* projectile = static_cast< Projectile* >( actor );
+ projectile->Fire();
+ projectile->CalculateIntersections();
+ actor->SetStatePropID( ownerID );
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+
+ GetEventManager()->TriggerEvent( EVENT_WASP_BULLET_FIRED );
+}
+
+
+
+void
+ActorManager::Update( unsigned int timeInMS )
+{
+ // Copy all the elements from the remove queue into the bank
+ int i;
+ while( m_RemoveQueue.mUseSize > 0 )
+ {
+ m_RemoveQueue[0]->RemoveFromDSG();
+ m_ActorBank.Add( m_RemoveQueue[0] );
+ m_RemoveQueue.Remove( 0 );
+ }
+
+ for ( i = m_ActorList.mUseSize - 1 ; i >=0 ; i--)
+ {
+ // Compare avatar position with actor position, if they
+ // exceed a spawn range, then quit
+
+ if ( WithinAliveRange( i ) || m_ActorList[i]->ShouldDespawn() == false )
+ {
+
+ m_ActorList[ i ]->Update( timeInMS );
+ }
+ else
+ {
+ RemoveActor( i );
+ }
+ }
+}
+
+
+void
+ActorManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_STATEPROP_DESTROYED:
+ {
+ StatePropDSG* pStatePropDSG = static_cast< StatePropDSG* >( pEventData );
+ // Find the actor that corresponds to the stateprop ( THIS SHOULD PROBABLY BE OPTIMIZED )
+ // WITH A MAP!
+ for ( int i = 0 ; i < m_ActorList.mUseSize ; i++ )
+ {
+ if ( m_ActorList[i]->GetUID() == pStatePropDSG->GetUID() )
+ {
+ // Do not remove it from the DSG. The Stateprop removes it itself automatically
+ RemoveActor( i, false );
+ break;
+ }
+ }
+ }
+ break;
+ case EVENT_STATEPROP_ADDED_TO_SIM:
+ {
+ StatePropDSG* pStatePropDSG = static_cast< StatePropDSG* >( pEventData );
+ }
+ break;
+ default:
+ break;
+ };
+}
+
+
+Actor*
+ActorManager::GetActorByName( const char* name )
+{
+ tUID uid = tName::MakeUID( name );
+ Actor* actor = NULL;
+ for ( int i = 0 ; i < m_ActorList.mUseSize ; i++ )
+ {
+ if ( m_ActorList[ i ]->GetUID() == uid )
+ {
+ actor = m_ActorList[ i ];
+ break;
+ }
+ }
+ return actor;
+}
+
+Actor*
+ActorManager::GetActorByUID( tUID uid )
+{
+ Actor* actor = NULL;
+ for ( int i = 0 ; i < m_ActorList.mUseSize ; i++ )
+ {
+ if ( m_ActorList[ i ]->GetUID() == uid )
+ {
+ actor = m_ActorList[ i ];
+ break;
+ }
+ }
+ return actor;
+}
+
+std::vector< Actor*, s2alloc< Actor* > >
+ActorManager::GetActorsByType( const char* cTypeName )
+{
+ std::vector< Actor*, s2alloc< Actor* > > actorList;
+ int i;
+
+ tName typeName = cTypeName;
+ tUID uid = typeName.GetUID();
+ actorList.reserve( m_ActorBank.mUseSize + m_ActorList.mUseSize );
+
+ for( i = 0 ; i < m_ActorList.mUseSize ; i++ )
+ {
+ if ( uid == m_ActorList[i]->GetStatePropUID() )
+ {
+ actorList.push_back( m_ActorList[i] );
+ }
+ }
+ for ( i = 0 ; i < m_ActorBank.mUseSize ; i++ )
+ {
+ if ( uid == m_ActorBank[i]->GetStatePropUID() )
+ {
+ actorList.push_back( m_ActorBank[i] );
+ }
+ }
+
+ return actorList;
+}
+
+
+void
+ActorManager::SetupConsoleFunctions()
+{
+ GetConsole()->AddFunction( "AddFlyingActor", AddFlyingActor, "",5,5); //Adds a new (flying actor) into the world
+ GetConsole()->AddFunction( "AddFlyingActorByLocator", AddFlyingActorByLocator, "",3,4); //Adds a new (flying actor) into the world
+ GetConsole()->AddFunction( "AddBehaviour", AddBehaviour, "",2,7); //adds a new behaviour pattern to an actor
+ GetConsole()->AddFunction( "SetCollisionAttributes", SetCollisionAttributes, "", 4, 4 ); //Sets friction/mass/elasticity values
+ GetConsole()->AddFunction( "AddSpawnPoint", AddSpawnPointScript, "", 8,8 ); //Creates a new spawnpoint
+ GetConsole()->AddFunction( "AddSpawnPointByLocatorScript", AddSpawnPointByLocatorScript, "", 6, 6 ); //Creates a new spawnpoint
+ GetConsole()->AddFunction( "SetProjectileStats", SetProjectileStats, "", 3, 3 ); //Sets stateprop name, speed and number of instances to allocate
+ GetConsole()->AddFunction( "PreallocateActors", PreallocateActors, "", 2, 2 ); //Creates N stateprops for later use
+ GetConsole()->AddFunction( "SetActorRotationSpeed", SetActorRotationSpeed, "", 2, 2 ); //Creates sets the rotation speed for all actors of the given type. Rotation speed is in degrees/second
+ GetConsole()->AddFunction( "AddShield", AddShield, "", 2, 2 ); //Creates a shield around the stateprop. First parameter = recharge time in seconds after being broken.
+}
+
+bool
+ActorManager::WithinAliveRange( int index )
+{
+ bool withinRange;
+
+ Actor* actor = m_ActorList[ index ];
+
+ rmt::Vector position;
+ actor->GetPosition( &position );
+ // Get the avatar position;
+ Avatar* currAvatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rmt::Vector currAvatarPos;
+ currAvatar->GetPosition( currAvatarPos );
+ // calc distance
+ float distSqr = (currAvatarPos - position).MagnitudeSqr();
+ if ( distSqr < ActorRemovalRangeSqr )
+ {
+ withinRange = true;
+ }
+ else
+ {
+ withinRange = false;
+ }
+ return withinRange;
+}
+
+void
+ActorManager::AddFlyingActor( int argc, char** argv )
+{
+ // First param - statepropname
+ // 2nd param - instancename
+ // 3rd param - position X
+ // 4th param - position Y
+ // 5th param - position Z
+
+ const char* statepropName = argv[1];
+ const char* instanceName = argv[2];
+ rmt::Vector position;
+ position.x = static_cast< float > ( atof( argv[3] ) );
+ position.y = static_cast< float > ( atof( argv[4] ) );
+ position.z = static_cast< float > ( atof( argv[5] ) );
+
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( position );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ // Create a new actor and immediately insert it into the world
+ Actor* actor = GetActorManager()->CreateActor( tName::MakeUID( statepropName ), tName::MakeUID( instanceName ), transform );
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+}
+
+void
+ActorManager::AddFlyingActorByLocator( int argc, char** argv )
+{
+ const char* statepropName = argv[1];
+ const char* instanceName = argv[2];
+ const char* locatorName = argv[3];
+ const char* isPermanent = argv[4];
+
+ Locator* locator = p3d::find< Locator >( locatorName );
+ if( locator != NULL )
+ {
+ rmt::Vector position;
+ locator->GetPosition( &position );
+
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( position );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ // Create a new actor and immediately insert it into the world
+ Actor* actor = GetActorManager()->CreateActor( tName::MakeUID( statepropName ), tName::MakeUID( instanceName ), transform );
+
+ // Should this thing despawn if out of range, or should it be permanent
+
+ if( argc > 3 )
+ {
+ if ( strcmp( isPermanent, "NEVER_DESPAWN" ) == 0 )
+ {
+ rAssert( actor != NULL );
+ if( actor != NULL )
+ {
+ actor->SetShouldDespawn( false );
+ }
+ }
+ }
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ }
+ else
+ {
+ rTunePrintf("AddFlyingActorByLocator - can't find locator %s\n", locatorName);
+ }
+}
+
+void
+ActorManager::AddSpawnPointScript( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ const char* spawnPointName = argv[1];
+ const char* statePropName = argv[2];
+ const char* instanceName = argv[3];
+ rmt::Vector position;
+ position.x = static_cast< float >( atof( argv[4] ) );
+ position.y = static_cast< float >( atof( argv[5] ) );
+ position.z = static_cast< float >( atof( argv[6] ) );
+ float radius = static_cast< float >( atof( argv[7] ) );
+ unsigned int timeOut = atoi( argv[8] );
+
+ rmt::Sphere sphere( position, radius );
+
+ SpawnPoint* pPoint = new SpawnPoint( spawnPointName, statePropName, sphere, timeOut );
+
+ GetActorManager()->AddSpawnPoint( pPoint );
+
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+}
+
+void
+ActorManager::AddSpawnPointByLocatorScript( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ const char* spawnPointName = argv[1];
+ const char* statePropName = argv[2];
+ const char* instanceName = argv[3];
+
+ const char* locatorName = argv[4];
+
+ Locator* locator = p3d::find< Locator >( locatorName );
+ if ( locator != NULL )
+ {
+ rmt::Vector position;
+ locator->GetPosition( &position );
+ float radius = static_cast< float >( atof( argv[5] ));
+ unsigned int timeOut = atoi( argv[6] );
+ rmt::Sphere sphere( position, radius );
+ SpawnPoint* pPoint = new SpawnPoint( spawnPointName, statePropName, sphere, timeOut );
+ GetActorManager()->AddSpawnPoint( pPoint );
+ }
+ else
+ {
+ rTunePrintf("AddSpawnPointByLocatorScript - could not find locator %s\n", locatorName);
+ }
+
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+}
+
+
+void
+ActorManager::SetProjectileStats( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ const char* typeName = argv[1];
+ float speed = static_cast< float >( atof( argv[2] ) );
+ int numInstances = static_cast< int >( atoi( argv[3] ) );
+ // Create
+ rAssert( numInstances < 20 );
+ for ( int i = 0 ; i < numInstances ; i++)
+ {
+ // Allocate memory and initialize a new projectile actor
+ Projectile* projectile = new Projectile();
+ projectile->Init( typeName, "not active" );
+ projectile->SetSpeed( speed );
+ // Add it to the bank of initialized but unplaced Actors
+ GetActorManager()->AddActorToBank( projectile );
+ }
+
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+}
+
+void
+ActorManager::PreallocateActors( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ const char* stateprop = argv[1];
+ int numInstances = atoi( argv[2] );
+ rAssert( numInstances >= 0 && numInstances < 20 );
+
+ for ( int i = 0 ; i < numInstances ; i++)
+ {
+ // Allocate memory and initialize a new projectile actor
+ Actor* actor = new FlyingActor();
+ if ( actor->Init( stateprop, "not active" ))
+ {
+ // Add it to the bank of initialized but unplaced Actors
+ GetActorManager()->AddActorToBank( actor );
+ }
+ else
+ {
+ actor->Release();
+ }
+ }
+
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+}
+
+void
+ActorManager::SetActorRotationSpeed( int argc, char** argv )
+{
+ const char* typeName = argv[1];
+ float rotationSpeed = static_cast< float > ( atof( argv[2] ) );
+ std::vector< Actor*, s2alloc< Actor*> > actorList = GetActorManager()->GetActorsByType( typeName );
+ for ( unsigned int i = 0 ; i < actorList.size() ; i++ )
+ {
+ actorList[ i ]->SetRotationSpeed( rotationSpeed );
+ }
+}
+
+void
+ActorManager::AddShield( int argc, char** argv )
+{
+ const char* name = argv[1];
+ const char* shieldStatePropName = argv[2];
+ std::vector< Actor*, s2alloc< Actor*> > actorList = GetActorManager()->GetActorsByType( name );
+ for ( unsigned int i = 0 ; i < actorList.size() ; i++ )
+ {
+ actorList[ i ]->LoadShield( shieldStatePropName );
+ }
+
+
+}
+
+
+SpawnPoint*
+ActorManager::GetSpawnPointByName( const char* name )
+{
+ SpawnPoint* spawnPoint = NULL;
+ tUID uid = tName( name ).GetUID();
+ for ( int i = 0 ; i < m_SpawnPointList.mUseSize ; i++ )
+ {
+ if ( m_SpawnPointList[ i ]->GetUID() == uid )
+ {
+ spawnPoint = m_SpawnPointList[ i ];
+ break;
+ }
+ }
+ return spawnPoint;
+}
+void
+ActorManager::RemoveAllSpawnPoints()
+{
+ for ( int i = 0 ; i < m_SpawnPointList.mUseSize ; i++ )
+ {
+ m_SpawnPointList[ i ]->Release();
+ }
+ m_SpawnPointList.ClearUse();
+}
+
+Actor*
+ActorManager::CreateActor( tUID typeName, tUID instanceName, const rmt::Matrix& transform )
+{
+ // Find an actor in the actorbank that has a stateprop called typeName.
+ Actor* pActor = NULL;
+ int i;
+ for ( i = 0 ; i < m_ActorBank.mUseSize ; i++ )
+ {
+ if ( m_ActorBank[ i ]->GetStatePropUID() == typeName )
+ {
+ pActor = m_ActorBank[i];
+ break;
+ }
+ }
+ if ( pActor != NULL && m_ActorBank.mUseSize > 0 )
+ {
+ // If found, move it from the bank to the active actor list
+ // give it instanceName
+ pActor->SetUID( instanceName );
+ AddActor( pActor );
+ // add it to the dsg
+ pActor->AddToDSG();
+
+ // Reset default behaviour
+ pActor->Activate();
+
+ GetEventManager()->TriggerEvent( EVENT_ACTOR_CREATED, pActor );
+ pActor->SetTransform( transform );
+ pActor->SetState( 0 );
+ // remove it from the actorbank list
+ m_ActorBank.Remove( i );
+ pActor->Release();
+ }
+
+ return pActor;
+}
+
+
+void
+ActorManager::AddBehaviour( int argc, char** argv )
+{
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+#endif
+
+ const char* targetObject = argv[1];
+ const char* behaviourName = argv[2];
+
+ // Can add behaviours to all sorts of things
+ // is the given name a spawn point?
+ SpawnPoint* spawnPoint = GetActorManager()->GetSpawnPointByName( targetObject );
+ // is it an actor's instance name?
+ Actor* actor = GetActorManager()->GetActorByName( targetObject );
+ // is it a stateprop type name?
+ std::vector< Actor*, s2alloc< Actor*> > actorList = GetActorManager()->GetActorsByType( targetObject );
+
+ int numBehavioursToAllocate = actorList.size();
+ if ( spawnPoint )
+ numBehavioursToAllocate++;
+ if ( actor )
+ numBehavioursToAllocate++;
+
+ std::stack< Behaviour* > behaviourList;
+
+ // Allocate numBehavioursToAllocate behaviours.
+ for ( int i = 0 ; i < numBehavioursToAllocate ; i++ )
+ {
+ Behaviour* behaviour = NULL;
+ if ( strcmp( behaviourName, "ATTACK_PLAYER" ) == 0 )
+ {
+ float maxFiringRange = static_cast< float >( atof( argv[3] ) );
+ float forwardFiringArc = static_cast< float >( atof( argv[4] ) );
+ AttackBehaviour* attackBehaviour = new AttackBehaviour( maxFiringRange, forwardFiringArc );
+ behaviour = attackBehaviour;
+ if ( argc > 5 )
+ {
+ float timeBeforeMoving = static_cast< float >( atof( argv[5] ) );
+ attackBehaviour->SetMovementIntervals( timeBeforeMoving );
+ }
+ }
+ else if ( strcmp( behaviourName, "EVADE_PLAYER" ) == 0 )
+ {
+ float minEvadeDistHoriz = static_cast< float >( atof( argv[ 3 ] ) );
+ float maxEvadeDistHoriz = static_cast< float >( atof( argv[ 4 ] ) );
+ float minEvadeDistVert = static_cast< float >( atof( argv[ 5 ] ) );
+ float maxEvadeDistVert = static_cast< float >( atof( argv[ 6 ] ) );
+ float speed = static_cast< float >( atof( argv[ 7 ] ) );
+ EvasionBehaviour* evasionBehaviour = new EvasionBehaviour( minEvadeDistHoriz, maxEvadeDistHoriz, minEvadeDistVert, maxEvadeDistVert, speed );
+ behaviour = evasionBehaviour;
+ }
+ else if ( strcmp( behaviourName, "UFO_ATTACK_ALL" ) == 0 )
+ {
+ const char* tractorbeamName = argv[3];
+ behaviour = new UFOAttackBehaviour( 0 );
+ // We want the actor to load up a tractor beam
+ if ( actor )
+ actor->LoadTractorBeam( tractorbeamName );
+ }
+ else if ( strcmp( behaviourName, "ATTRACTION" ) == 0 )
+ {
+ float minWatchDistance = static_cast< float >( atof( argv[3] ) );
+ float maxWatchDistance = static_cast< float >( atof( argv[4] ) );
+ float speed = static_cast< float >( atof( argv[5] ) );
+ behaviour = new AttractionBehaviour( minWatchDistance, maxWatchDistance, speed );
+ }
+ else if ( strcmp( behaviourName, "UFO_BEAM_ALWAYS_ON" ) == 0 )
+ {
+ // This is for level 7 mission 3, where the ufo is stationary and the beam is always on
+ const char* tractorbeamName = argv[3];
+ behaviour = new UFOBeamAlwaysOn();
+ if ( actor )
+ actor->LoadTractorBeam( tractorbeamName );
+ }
+ else
+ {
+ rTunePrintf( "Unknown behaviour %s!", behaviourName );
+ rAssert( false );
+ }
+
+ behaviourList.push( behaviour );
+ }
+
+ // We now have an array full of preallocated behaviours
+
+ // Attach them to the actorList
+ for ( unsigned int i = 0 ; i < actorList.size() ; i++ )
+ {
+ actorList[i]->AddBehaviour( behaviourList.top() );
+ behaviourList.pop();
+ }
+
+ // Attach it to any individual actors that matched the given name
+ if ( actor )
+ {
+ actor->AddBehaviour( behaviourList.top() );
+ behaviourList.pop();
+ }
+
+ // Attach it to any spawn points that we matched up with
+ if ( spawnPoint )
+ {
+ spawnPoint->AddBehaviour( behaviourList.top() );
+ behaviourList.pop();
+ }
+ // Any remaining behaviours left in this stack are probably mem leaks
+ rTuneAssert( behaviourList.empty() );
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PopHeap( GMA_LEVEL_MISSION );
+#endif
+}
+
+void
+ActorManager::AddRespawnBehaviourPosition( int argc, char** argv )
+{
+ rmt::Vector position;
+ position.x = static_cast< float > ( atof( argv[1] ) );
+ position.y = static_cast< float > ( atof( argv[2] ) );
+ position.z = static_cast< float > ( atof( argv[3] ) );
+
+ //RespawnBehaviour::AddRespawnPosition( position );
+}
+
+void
+ActorManager::SetCollisionAttributes( int argc, char** argv )
+{
+ const char* instanceName = argv[1];
+
+ Actor* actor = GetActorManager()->GetActorByName( instanceName );
+
+
+ float friction = static_cast< float >( atof( argv[2] ) );
+ float mass = static_cast< float >( atof( argv[3] ) );
+ float elasticity = static_cast< float >( atof( argv[4] ) );
+
+}
+
+
+
diff --git a/game/code/ai/actor/actormanager.h b/game/code/ai/actor/actormanager.h
new file mode 100644
index 0000000..da1c0ba
--- /dev/null
+++ b/game/code/ai/actor/actormanager.h
@@ -0,0 +1,144 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: actormanager
+//
+// Description: Actormanager class, for holding stateprops that have AI
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef ACTORMANAGER_H
+#define ACTORMANAGER_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+
+#include <render/culling/swaparray.h>
+#include <ai/actor/spawnpoint.h>
+#include <events/eventlistener.h>
+#include <memory/stlallocators.h>
+#include <vector>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class Actor;
+class ActorDSG;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Holds a list of actors and makes sure they get updated every frame
+//
+// Constraints:
+//
+//
+//===========================================================================
+class ActorManager : public EventListener
+{
+ public:
+ ActorManager();
+ ~ActorManager();
+
+ // Adds an actor into the world
+ void AddActor( Actor* );
+ // Adds an actor into the bank. Basically a list of inactive actors
+ // that can be brought up for use when the avatar enters a spawn point
+ // or when an object fires a projectile
+ void AddActorToBank( Actor* );
+
+ Actor* GetActorByName( const char* name );
+ Actor* GetActorByUID( tUID name );
+ std::vector< Actor*, s2alloc< Actor* > > GetActorsByType( const char* typeName );
+
+
+ void RemoveActor( int index, bool removeFromDSG = true );
+ void RemoveActorByDSGPointer( ActorDSG* dsgObject, bool removeFromDSG = true );
+ void RemoveAllActors();
+
+ // Spawn point functions
+ void AddSpawnPoint( SpawnPoint* );
+ void FireProjectile( tUID typeName, const rmt::Vector& position, const rmt::Vector& direction, unsigned int id );
+ SpawnPoint* GetSpawnPointByName( const char* name );
+ void RemoveAllSpawnPoints();
+
+ // Creates a new actor in the world
+ Actor* CreateActor( tUID typeName, tUID instanceName, const rmt::Matrix& transform );
+
+
+ // Update all actors
+ void Update( unsigned int timeInMS );
+
+ // Inherited from class EventListener
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ static ActorManager* CreateInstance();
+ static ActorManager* GetInstance();
+ static void DestroyInstance();
+
+ static float ActorRemovalRangeSqr;
+
+ protected:
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow ActorManager from being copied and assigned.
+ ActorManager( const ActorManager& );
+ ActorManager& operator=( const ActorManager& );
+
+ void SetupConsoleFunctions();
+ bool WithinAliveRange( int index );
+ // Console functions
+ static void AddFlyingActor( int argc, char** argv );
+ static void AddFlyingActorByLocator( int argc, char** argv );
+ static void AddBehaviour( int argc, char** argv );
+ static void AddRespawnBehaviourPosition( int argc, char** argv );
+ static void SetCollisionAttributes( int argc, char** argv );
+ static void AddSpawnPointScript( int argc, char** argv );
+ static void AddSpawnPointByLocatorScript( int argc, char** argv );
+ static void SetProjectileStats( int argc, char** argv );
+ static void PreallocateActors( int argc, char** argv );
+ static void SetActorRotationSpeed( int argc, char** argv );
+ static void AddShield( int argc, char** argv );
+private:
+ static ActorManager* sp_Instance;
+
+ SwapArray< Actor* > m_ActorList;
+ SwapArray< SpawnPoint* > m_SpawnPointList;
+
+ // List of allocated but unused actors
+ SwapArray< Actor* > m_ActorBank;
+ // List of actors that will be removed at the end of an Update cycle
+ // not a temporary variable because I want to get rid of dynamic allocations at runtime
+
+ // List of actors that are flagged for removal. They will not be updated
+ // and will not be available for reuse until next frame, when they will be moved into the
+ // bank
+ // The removequeue is equivalent to the breakablemanager::removequeue. When you want to
+ // remove an object, but can't because it still inside the physics system
+ SwapArray< Actor* > m_RemoveQueue;
+};
+
+inline ActorManager* GetActorManager()
+{
+ return ActorManager::GetInstance();
+}
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/allactor.cpp b/game/code/ai/actor/allactor.cpp
new file mode 100644
index 0000000..42b3d1e
--- /dev/null
+++ b/game/code/ai/actor/allactor.cpp
@@ -0,0 +1,16 @@
+#include <ai/actor/actormanager.cpp>
+#include <ai/actor/flyingactor.cpp>
+#include <ai/actor/actor.cpp>
+#include <ai/actor/attackbehaviour.cpp>
+#include <ai/actor/evasionbehaviour.cpp>
+#include <ai/actor/spawnpoint.cpp>
+#include <ai/actor/projectile.cpp>
+#include <ai/actor/actordsg.cpp>
+#include <ai/actor/ufoattackbehaviour.cpp>
+#include <ai/actor/actoranimationwasp.cpp>
+#include <ai/actor/actoranimationufo.cpp>
+#include <ai/actor/attractionbehaviour.cpp>
+#include <ai/actor/projectiledsg.cpp>
+#include <ai/actor/ufobeamalwaysonbehaviour.cpp>
+#include <ai/actor/ufobehaviour.cpp>
+#include <ai/actor/intersectionlist.cpp> \ No newline at end of file
diff --git a/game/code/ai/actor/attackbehaviour.cpp b/game/code/ai/actor/attackbehaviour.cpp
new file mode 100644
index 0000000..671313a
--- /dev/null
+++ b/game/code/ai/actor/attackbehaviour.cpp
@@ -0,0 +1,403 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component:
+//
+// Description:
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai/actor/attackbehaviour.h>
+#include <ai/actor/actor.h>
+#include <worldsim/avatarmanager.h>
+#include <math.h>
+#include <constants/actorenum.h>
+#include <render/IntersectManager/IntersectManager.h>
+#include <ai/actor/actordsg.h>
+#include <radtime.hpp>
+#include <worldsim/character/character.h>
+#include <main/game.h>
+#include <events/eventenum.h>
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+// Designer tuning variables
+
+const float ATTACK_BEHAVIOUR_MOVEMENT_INTERVALS = 3.0f;
+const float ATTACK_BEHAVIOUR_CAM_ANGLE = 0.7f;
+const float AVG_CHARACTER_HEIGHT = 1.2f;
+
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+rmt::Randomizer AttackBehaviour::s_Randomizer(0);
+bool AttackBehaviour::s_RandomizerSeeded = false;
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+AttackBehaviour::AttackBehaviour( float maxFiringRange, float firingArc ) :
+m_ActiveGagCount( 0 ),
+m_InConversation( false )
+{
+ const float DEFAULT_MOVE_SPEED = 20.0f;
+
+ SetMovementIntervals( ATTACK_BEHAVIOUR_MOVEMENT_INTERVALS );
+ SetFiringArc( firingArc );
+ SetMaxFiringRange( maxFiringRange );
+ SetActorMoveSpeed( DEFAULT_MOVE_SPEED );
+
+ if (!s_RandomizerSeeded)
+ {
+ s_Randomizer.Seed (Game::GetRandomSeed ());
+ s_RandomizerSeeded = true;
+ }
+
+}
+
+AttackBehaviour::~AttackBehaviour()
+{
+
+}
+
+void
+AttackBehaviour::Apply( Actor* actor, unsigned int timeInMS )
+{
+
+ if ( m_InConversation || m_ActiveGagCount > 0 )
+ return;
+
+
+
+ rmt::Vector targetPos;
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ Character* character = avatar->GetCharacter();
+ avatar->GetPosition( targetPos );
+ targetPos.y += AVG_CHARACTER_HEIGHT; // Lets not lock onto their feet
+
+ rmt::Vector actorPos;
+ actor->GetPosition( &actorPos );
+ int currentAnimationState = actor->GetState();
+ actor->LookAt( targetPos, timeInMS );
+ // Are we firing? Then just keep trained on the target and
+ // thats it
+ if ( currentAnimationState == ActorEnum::eAttacking )
+ return;
+
+ // Vlad wants the wasp to not use the characters current height if jumping
+ // Lets check for this and compensate by using our current height
+ if ( character->IsJumping() )
+ {
+ targetPos.y -= character->GetJumpHeight();
+ }
+
+
+ if ( actor->IsMoving() )
+ {
+ // Record that we are still moving
+ m_TimeOfLastMove = radTimeGetMilliseconds();
+ SetExclusive( true );
+ }
+ else
+ {
+ // Lets make the actor more jumpy
+ // occasionally switch positions,
+ // if the shield is on, test for line of sight.
+ // if there is line of sight, never move
+ //
+ unsigned int currentTime = radTimeGetMilliseconds();
+
+
+ if ( IsTooClose( actorPos, targetPos ) &&
+ character->IsJumping() == false &&
+ IsMovementDisabled() == false )
+ {
+ MoveAway( actor, targetPos );
+ return;
+ }
+
+ bool shouldMove;
+ if ( IsMovementDisabled() )
+ shouldMove = false;
+ else
+ shouldMove = m_TimeOfLastMove + m_MovementIntervals < currentTime;
+
+ if ( shouldMove )
+ {
+ MoveIntoAttackRange( actor, targetPos );
+ }
+ else
+ {
+ rmt::Matrix actorTransform;
+ actor->GetTransform( &actorTransform );
+
+
+ switch ( currentAnimationState )
+ {
+ case ActorEnum::eIdleReadyToAttack:
+ {
+ // Are we in range?
+ // Are we facing the target
+
+ bool withinRange = WithinFiringRange( actorPos, targetPos );
+ bool withinArc = WithinFiringArc( actorPos, actorTransform.Row(2), targetPos );
+
+ if ( withinRange == false &&
+ IsMovementDisabled() == false )
+ {
+ MoveIntoAttackRange( actor, targetPos );
+ }
+ if ( withinRange && withinArc )
+ {
+ // Joe wants it so that they never attack when the user is in a
+ // vehicle
+ // Test for this
+
+ if ( avatar->IsInCar() == false )
+ {
+ // Joe ALSO wants them to only fire when in the cameras FOV
+ // Sheesh, is this game supposed to be for toddlers or something?
+
+ SuperCam* cam = GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ tPointCamera* pointCamera = cam->GetCamera();
+ if ( pointCamera->PointVisible( actorPos ) )
+ {
+ actor->SetState( ActorEnum::eAttacking );
+ SetExclusive( true );
+ }
+ }
+ }
+
+ break;
+ // We are supposed to attack, can't do that if we are not in the proper animation
+ // state
+ }
+ case ActorEnum::eTransitionToReadyToAttack:
+ SetExclusive( false );
+ break;
+ case ActorEnum::eUnaggressive:
+ actor->SetState( ActorEnum::eTransitionToReadyToAttack );
+ SetExclusive( false );
+ break;
+
+ // We are currently attacking.
+ case ActorEnum::eAttacking:
+ // As long as we are attacking, don't do anything else
+ SetExclusive( true );
+ break;
+
+ default:
+ SetExclusive( false );
+ break;
+ };
+ }
+ }
+}
+
+void AttackBehaviour::SetMaxFiringRange( float meters )
+{
+ m_MaxFiringRange = meters;
+ m_MaxFiringRangeSqr = meters * meters;
+
+
+
+}
+
+void AttackBehaviour::SetFiringArc( float firingArcDegrees )
+{
+ float firingArcRadians = firingArcDegrees * 3.1415927f / 180.0f;
+ float halfArc = firingArcRadians / 2.0f;
+ m_FiringArc = static_cast< float > ( fabs ( cos( halfArc ) )) ;
+}
+
+void AttackBehaviour::SetActorMoveSpeed( float kph )
+{
+ // Convert from kph to meters per millisecond
+ // Precision loss here?
+ m_Speed = kph * 1000.0f / 3600000.0f;
+
+}
+
+void AttackBehaviour::SetMovementIntervals( float seconds )
+{
+
+ // Convert seconds to milliseconds and store it
+ m_MovementIntervals = seconds * 1000.0f;
+}
+
+bool AttackBehaviour::IsMovementDisabled()const
+{
+ // No movement if designers set movement interval to be < 0
+ return ( m_MovementIntervals < 0.0f );
+}
+
+
+// Enable this behaviour
+void AttackBehaviour::Activate()
+{
+ // Register for events
+ GetEventManager()->AddListener( this, EVENT_GAG_START );
+ GetEventManager()->AddListener( this, EVENT_GAG_END );
+
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_START );
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_DONE );
+
+ m_TimeOfLastMove = 0;
+ m_ActiveGagCount = 0;
+ m_InConversation = false;
+ SetExclusive( false );
+}
+// Disable this behaviour
+void AttackBehaviour::Deactivate()
+{
+ GetEventManager()->RemoveAll( this );
+
+ m_ActiveGagCount = 0;
+ m_InConversation = false;
+ SetExclusive( false );
+}
+
+
+bool AttackBehaviour::WithinFiringRange( const rmt::Vector& actorPos, const rmt::Vector& target )const
+{
+ bool withinRange;
+
+ rmt::Vector toTarget = target - actorPos;
+ float rangeSqr = toTarget.MagnitudeSqr();
+ if ( rangeSqr < m_MaxFiringRangeSqr )
+ {
+ withinRange = true;
+ }
+ else
+ {
+ withinRange = false;
+ }
+
+ return withinRange;
+}
+
+bool AttackBehaviour::WithinFiringArc( const rmt::Vector& actorPos, const rmt::Vector& actorFacing, const rmt::Vector& target ) const
+{
+ bool withinArc;
+
+ // Get the toTarget vector
+ rmt::Vector toTarget = target - actorPos;
+ toTarget.Normalize();
+ // Get the cos of the angle between the 2 vectors
+ float cosAngle = toTarget.Dot( actorFacing );
+ if ( cosAngle > m_FiringArc || cosAngle < -m_FiringArc )
+ {
+ withinArc = true;
+ }
+ else
+ {
+ withinArc = false;
+ }
+ return withinArc;
+}
+
+bool
+AttackBehaviour::IsTooClose( const rmt::Vector& actorPos, const rmt::Vector& target )const
+{
+ const float TOO_CLOSE_DIST = 2.0f;
+ const float TOO_CLOSE_DIST_SQR = TOO_CLOSE_DIST * TOO_CLOSE_DIST;
+
+ bool tooClose;
+ if (( actorPos - target ).MagnitudeSqr() < TOO_CLOSE_DIST_SQR )
+ tooClose = true;
+ else
+ tooClose = false;
+
+ return tooClose;
+}
+
+
+void AttackBehaviour::MoveIntoAttackRange( Actor* actor, const rmt::Vector& target )
+{
+ // We want it to be inside the camera frustum
+ //
+
+ SuperCam* camera = GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam();
+ if ( camera == NULL )
+ return;
+
+ rmt::Vector cameraHeading;
+ camera->GetHeading( &cameraHeading );
+ cameraHeading.y = 0;
+ cameraHeading.Normalize();
+
+ // Add a random twist to the camera along world Y axis
+ float rotationAngle = s_Randomizer.FloatSigned() * ATTACK_BEHAVIOUR_CAM_ANGLE;
+
+ rmt::Matrix randomCameraRotation;
+ randomCameraRotation.Identity();
+ randomCameraRotation.FillRotateY( rotationAngle );
+
+ cameraHeading.Rotate( randomCameraRotation );
+
+ rmt::Vector destination = target + cameraHeading * m_MaxFiringRange * 0.5f ;
+ destination.y += ( 1.5f + s_Randomizer.Float() * 0.5f );
+ rmt::Vector actorPosition;
+ actor->GetPosition( &actorPosition );
+ actor->MoveTo( destination, m_Speed );
+}
+
+void
+AttackBehaviour::MoveAway( Actor* actor, const rmt::Vector& target )
+{
+ rmt::Vector actorPosition;
+ actor->GetPosition( &actorPosition );
+ // Get the vector pointing from the target to the actor
+ rmt::Vector toActor = actorPosition - target;
+ toActor.Normalize();
+
+ const float EVADE_DIST = 5.0f;
+
+ rmt::Vector destination = actorPosition + toActor * EVADE_DIST;
+ destination.y = actorPosition.y;
+ // Move directly away from the target
+ actor->MoveTo( destination, m_Speed );
+}
+
+void
+AttackBehaviour::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_GAG_START:
+ m_ActiveGagCount++;
+ break;
+
+ case EVENT_GAG_END:
+ m_ActiveGagCount--;
+ break;
+
+
+ case EVENT_CONVERSATION_START:
+ m_InConversation = true;
+ break;
+ case EVENT_CONVERSATION_DONE:
+ m_InConversation = false;
+ break;
+ default:
+ break;
+
+ }
+
+} \ No newline at end of file
diff --git a/game/code/ai/actor/attackbehaviour.h b/game/code/ai/actor/attackbehaviour.h
new file mode 100644
index 0000000..39b9bf1
--- /dev/null
+++ b/game/code/ai/actor/attackbehaviour.h
@@ -0,0 +1,95 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: AttackBehaviour
+//
+// Description: Atta
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef ATTACKBEHAVIOUR_H
+#define ATTACKBEHAVIOUR_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai/actor/behaviour.h>
+#include <radmath/random.hpp>
+#include <events/eventlistener.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+
+class AttackBehaviour : public Behaviour, public EventListener
+{
+ public:
+ // maximum firing range in meters, forward firing arc in degrees
+ AttackBehaviour( float maxFiringRange, float firingArc );
+ virtual ~AttackBehaviour();
+ virtual void Apply( Actor*, unsigned int timeInMS );
+
+ void SetMaxFiringRange( float meters );
+ void SetFiringArc( float degrees );
+ void SetActorMoveSpeed( float kph );
+ void SetMovementIntervals( float seconds );
+ bool IsMovementDisabled()const;
+
+ // Enable this behaviour
+ virtual void Activate();
+ // Disable this behaviour
+ virtual void Deactivate();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ protected:
+
+ bool WithinFiringRange( const rmt::Vector& actorPos, const rmt::Vector& target )const;
+ bool WithinFiringArc( const rmt::Vector& actorPos, const rmt::Vector& actorFacing, const rmt::Vector& target )const;
+ bool IsTooClose( const rmt::Vector& actorPos, const rmt::Vector& target )const;
+
+ void MoveIntoAttackRange( Actor* actor, const rmt::Vector& target );
+ void MoveAway( Actor* actor, const rmt::Vector& target );
+
+ float m_MaxFiringRange; // in meters
+ float m_MaxFiringRangeSqr;
+ float m_FiringArc; // half-arc radius, units = cos(angle)
+ float m_Speed; // meters per milliseconds
+
+ float m_MovementIntervals;
+ unsigned int m_TimeOfLastMove;
+
+ int m_ActiveGagCount;
+ bool m_InConversation;
+
+ static rmt::Randomizer s_Randomizer;
+ static bool s_RandomizerSeeded;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow AttackBehaviour from being copied and assigned.
+ AttackBehaviour( const AttackBehaviour& );
+ AttackBehaviour& operator=( const AttackBehaviour& );
+};
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/attractionbehaviour.cpp b/game/code/ai/actor/attractionbehaviour.cpp
new file mode 100644
index 0000000..ef830fb
--- /dev/null
+++ b/game/code/ai/actor/attractionbehaviour.cpp
@@ -0,0 +1,277 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: AttractionBehaviour
+//
+// Description: This behaviour has two states. In the first state, it will
+// cause the actor to remain motionless until something of interest
+// happens. Then this happens, the actor will move down to near the avatar
+// and watch him until the avatar does something violent
+//
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai/actor/attractionbehaviour.h>
+#include <ai/actor/actordsg.h>
+#include <ai/actor/actor.h>
+#include <ai/actor/flyingactor.h>
+#include <events/eventmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <memory/srrmemory.h>
+#include <render/IntersectManager/IntersectManager.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+
+AttractionBehaviour::AttractionBehaviour( float minWatchDistance, float maxWatchDistance, float speedKPH ):
+m_CurrentState( eIdle ),
+m_SensoryRangeSqr( maxWatchDistance * maxWatchDistance ),
+m_MinWatchDistanceSqr( minWatchDistance * minWatchDistance ),
+m_MaxWatchDistanceSqr( maxWatchDistance * maxWatchDistance ),
+m_ForceFindNewWatchPosition( false ),
+m_ParentActor( NULL )
+{
+ m_Speed = speedKPH * 1000.0f / 3600000.0f;
+ m_WatchDistanceFromAvatar = ( minWatchDistance + maxWatchDistance ) / 2.0f;
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ SetExclusive( true );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_OBJECT_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_BIG_CRASH );
+ GetEventManager()->AddListener( this, EVENT_BIG_VEHICLE_CRASH );
+ GetEventManager()->AddListener( this, EVENT_BIG_AIR );
+ GetEventManager()->AddListener( this, EVENT_PEDESTRIAN_DODGE );
+ GetEventManager()->AddListener( this, EVENT_PEDESTRIAN_SMACKDOWN );
+ GetEventManager()->AddListener( this, EVENT_BREAK_CAMERA_OR_BOX );
+ GetEventManager()->AddListener( this, EVENT_GAG_END );
+ GetEventManager()->AddListener( this, EVENT_PC_NPC_COLLISION );
+ GetEventManager()->AddListener( this, EVENT_PLAYER_CAR_HIT_NPC );
+ GetEventManager()->AddListener( this, EVENT_HIT_BREAKABLE );
+ GetEventManager()->AddListener( this, EVENT_OBJECT_KICKED );
+ GetEventManager()->AddListener( this, EVENT_JUMP_LANDING );
+
+ GetEventManager()->AddListener( this, EVENT_KICK );
+ GetEventManager()->AddListener( this, EVENT_STOMP );
+ GetEventManager()->AddListener( this, EVENT_DOUBLEJUMP );
+ GetEventManager()->AddListener( this, EVENT_COLLECT_OBJECT );
+
+
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+}
+
+AttractionBehaviour::~AttractionBehaviour()
+{
+ GetEventManager()->RemoveAll( this );
+}
+
+void
+AttractionBehaviour::Apply( Actor* actor, unsigned int timeInMS )
+{
+ m_ParentActor = actor;
+
+ // Make the actor stay fixed
+ FlyingActor* flyingactor = static_cast< FlyingActor* >( actor );
+ flyingactor->SetDesiredHeightEnabled( false );
+
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ rAssert( avatar != NULL );
+
+ rmt::Vector avatarPosition;
+ avatar->GetPosition( avatarPosition );
+ rmt::Vector actorPosition;
+ actor->GetPosition( &actorPosition );
+
+ if ( m_ForceFindNewWatchPosition )
+ {
+ rmt::Vector newWatchPosition;
+
+ if ( FindNewWatchPosition( *m_ParentActor, avatarPosition, &newWatchPosition ) )
+ {
+ m_ParentActor->MoveTo( newWatchPosition, m_Speed );
+ m_ForceFindNewWatchPosition = false;
+ }
+ }
+ float distanceToTargetSqr = (actorPosition - avatarPosition).MagnitudeSqr();
+ const float waspRadius = 5.0f;
+ if( distanceToTargetSqr < waspRadius * waspRadius )
+ {
+ //
+ // Send a message about approaching a wasp
+ //
+ GetEventManager()->TriggerEvent( EVENT_WASP_APPROACHED );
+ }
+
+ if ( m_CurrentState == eIdle )
+ {
+ if ( distanceToTargetSqr < m_MinWatchDistanceSqr )
+ {
+ // The target is too close!
+ // Attraction go into watch mode
+ // SetExclusive( false );
+ }
+ }
+ else if ( m_CurrentState == eWatching )
+ {
+ // Basically, we want the actor to watch the avatar, follow him around while
+ // keeping his distance
+ // Lets first check the distance to the avatar
+
+
+ if ( distanceToTargetSqr < m_MinWatchDistanceSqr )
+ {
+ // The target is too close!
+ // Attraction behaviour is over, lets bail
+ SetExclusive( false );
+ }
+ else if ( IsMovementDisabled() == false && distanceToTargetSqr > m_MaxWatchDistanceSqr )
+ {
+ // The target is too far!
+ // Lets move close
+ rmt::Vector newWatchPosition;
+ if ( FindNewWatchPosition( *actor, avatarPosition, &newWatchPosition ) )
+ {
+ actor->MoveTo( newWatchPosition, m_Speed );
+ }
+ }
+ else
+ {
+ actor->LookAt( avatarPosition, timeInMS );
+ }
+ }
+}
+
+
+void
+AttractionBehaviour::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_COLLECT_OBJECT:
+ {
+ // Hostile action was taken
+ if( m_CurrentState == eWatching )
+ {
+ SetExclusive( false );
+ }
+ }
+ case EVENT_KICK:
+ case EVENT_STOMP:
+ case EVENT_DOUBLEJUMP:
+ case EVENT_VEHICLE_DESTROYED:
+ case EVENT_OBJECT_DESTROYED:
+ case EVENT_BIG_CRASH:
+ case EVENT_BIG_VEHICLE_CRASH:
+ case EVENT_BIG_AIR:
+ case EVENT_PEDESTRIAN_DODGE:
+ case EVENT_PEDESTRIAN_SMACKDOWN:
+ case EVENT_BREAK_CAMERA_OR_BOX:
+ case EVENT_GAG_END:
+ case EVENT_PC_NPC_COLLISION:
+ case EVENT_PLAYER_CAR_HIT_NPC:
+ case EVENT_HIT_BREAKABLE:
+ case EVENT_OBJECT_KICKED:
+ case EVENT_JUMP_LANDING:
+ {
+ if ( m_ParentActor != NULL )
+ {
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if ( WithinSensoryRange( *m_ParentActor, *avatar ) )
+ {
+ switch (m_CurrentState)
+ {
+ case eIdle:
+ {
+ // Make the object fly down to the actor
+ m_CurrentState = eWatching;
+ m_ForceFindNewWatchPosition = true;
+ }
+ break;
+ default:
+ break;
+ };
+ }
+ }
+ }
+ break;
+ default:
+ rAssert( false );
+ break;
+ };
+}
+
+void AttractionBehaviour::Activate()
+{
+ m_CurrentState = eWatching;
+ SetExclusive( true );
+}
+
+void AttractionBehaviour::Deactivate()
+{
+ m_CurrentState = eIdle;
+ m_ForceFindNewWatchPosition = false;
+ SetExclusive( false );
+
+ if ( m_ParentActor != NULL )
+ {
+ FlyingActor* flyingactor = static_cast< FlyingActor* >( m_ParentActor );
+ }
+}
+
+bool
+AttractionBehaviour::WithinSensoryRange( const Actor& actor, const Avatar& avatar )const
+{
+ bool withinRange;
+ rmt::Vector actorPosition;
+ actor.GetPosition( &actorPosition );
+
+ rmt::Vector avatarPosition;
+ avatar.GetPosition( avatarPosition );
+
+ if ( (avatarPosition - actorPosition).MagnitudeSqr() < m_SensoryRangeSqr )
+ {
+ withinRange = true;
+ }
+ else
+ {
+ withinRange = false;
+ }
+ return withinRange;
+}
+
+bool
+AttractionBehaviour::FindNewWatchPosition( const Actor& actor, const rmt::Vector& avatarPosition, rmt::Vector* newPosition )const
+{
+ // We want to find a new position halfway between min and max distance
+ // along the line of sight
+ rmt::Vector actorPosition;
+ actor.GetPosition( &actorPosition );
+
+ rmt::Vector toAvatar = avatarPosition - actorPosition;
+ toAvatar.Normalize();
+
+ *newPosition = actorPosition + toAvatar * m_WatchDistanceFromAvatar;
+
+ return true;
+}
+
diff --git a/game/code/ai/actor/attractionbehaviour.h b/game/code/ai/actor/attractionbehaviour.h
new file mode 100644
index 0000000..019839f
--- /dev/null
+++ b/game/code/ai/actor/attractionbehaviour.h
@@ -0,0 +1,89 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: AttractionBehaviour
+//
+// Description: This behaviour has two states. In the first state, it will
+// cause the actor to remain motionless until something of interest
+// happens. Then this happens, the actor will move down to near the avatar
+// and watch him until the avatar does something violent
+//
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef ATTRACTIONBEHAVIOUR_H
+#define ATTRACTIONBEHAVIOUR_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai/actor/behaviour.h>
+#include <radmath/vector.hpp>
+#include <events/eventlistener.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class Actor;
+class Avatar;
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+
+class AttractionBehaviour : public Behaviour, public EventListener
+{
+ public:
+ AttractionBehaviour( float minWatchDistance, float maxWatchDistance, float speedKPH );
+ virtual ~AttractionBehaviour();
+ virtual void Apply( Actor*, unsigned int timeInMS );
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ virtual void Activate();
+ virtual void Deactivate();
+
+ bool IsMovementDisabled()const { return m_Speed <= 0.0f; }
+
+ enum State
+ {
+ eIdle,
+ eWatching
+ };
+
+ protected:
+
+ State m_CurrentState;
+ float m_SensoryRangeSqr;
+ float m_MinWatchDistanceSqr;
+ float m_MaxWatchDistanceSqr;
+ float m_WatchDistanceFromAvatar;
+ float m_Speed;
+ bool m_ForceFindNewWatchPosition;
+ Actor* m_ParentActor;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow AttractionBehaviour from being copied and assigned.
+ AttractionBehaviour( const AttractionBehaviour& );
+ AttractionBehaviour& operator=( const AttractionBehaviour& );
+ private:
+
+ bool WithinSensoryRange( const Actor& actor, const Avatar& avatar )const;
+ bool FindNewWatchPosition( const Actor& actor, const rmt::Vector& avatarPosition, rmt::Vector* result )const;
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/behaviour.h b/game/code/ai/actor/behaviour.h
new file mode 100644
index 0000000..a9a710c
--- /dev/null
+++ b/game/code/ai/actor/behaviour.h
@@ -0,0 +1,68 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: none
+//
+// Description: Interface for actor behaviours
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef BEHAVIOUR_H
+#define BEHAVIOUR_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <p3d/refcounted.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class Actor;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class Behaviour : public tRefCounted
+{
+ public:
+ Behaviour():m_IsMutuallyExclusive( false ){ }
+ virtual ~Behaviour() { }
+
+ virtual void Apply( Actor*, unsigned int timeInMS )=0;
+
+ // Can this behaviour be deactivated? or are we busy
+ virtual bool IsExclusive()const { return m_IsMutuallyExclusive; }
+
+ // Enable this behaviour
+ virtual void Activate(){}
+ // Disable this behaviour
+ virtual void Deactivate(){}
+
+ protected:
+ virtual void SetExclusive( bool exclusive ) { m_IsMutuallyExclusive = exclusive; }
+ bool m_IsMutuallyExclusive;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow Behaviour from being copied and assigned.
+ Behaviour( const Behaviour& );
+ Behaviour& operator=( const Behaviour& );
+
+};
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/cutcambehaviour.cpp b/game/code/ai/actor/cutcambehaviour.cpp
new file mode 100644
index 0000000..efd0f5b
--- /dev/null
+++ b/game/code/ai/actor/cutcambehaviour.cpp
@@ -0,0 +1,231 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: cutcambehaviour
+//
+// Description: While inside a trigger volume, the actor will switch to its POV
+//
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai/actor/cutcambehaviour.h>
+#include <ai/actor/actor.h>
+#include <events/eventmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <camera/supercammanager.h>
+#include <camera/supercam.h>
+#include <camera/surveillancecam.h>
+#include <memory/srrmemory.h>
+
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+
+CutCamBehaviour::CutCamBehaviour( float radius ):
+m_TriggerRadiusSqr( radius * radius ),
+m_SwitchingEnabled( true ),
+m_CutCamViewActivated( false),
+m_SuperCamID( UINT_MAX ),
+m_Registered( false ),
+m_ParentActor( NULL )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ SetExclusive( true );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_OBJECT_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_BIG_CRASH );
+ GetEventManager()->AddListener( this, EVENT_BIG_VEHICLE_CRASH );
+ GetEventManager()->AddListener( this, EVENT_BIG_AIR );
+ GetEventManager()->AddListener( this, EVENT_PEDESTRIAN_DODGE );
+ GetEventManager()->AddListener( this, EVENT_PEDESTRIAN_SMACKDOWN );
+ GetEventManager()->AddListener( this, EVENT_BREAK_CAMERA_OR_BOX );
+ GetEventManager()->AddListener( this, EVENT_GAG_END );
+
+
+
+ // Allocate a camera
+ mpCamera = new SurveillanceCam;
+ mpCamera->AddRef();
+
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+}
+
+CutCamBehaviour::~CutCamBehaviour()
+{
+ GetEventManager()->RemoveListener( this, EVENT_VEHICLE_DESTROYED );
+ GetEventManager()->RemoveListener( this, EVENT_OBJECT_DESTROYED );
+ GetEventManager()->RemoveListener( this, EVENT_BIG_CRASH );
+ GetEventManager()->RemoveListener( this, EVENT_BIG_VEHICLE_CRASH );
+ GetEventManager()->RemoveListener( this, EVENT_BIG_AIR );
+ GetEventManager()->RemoveListener( this, EVENT_PEDESTRIAN_DODGE );
+ GetEventManager()->RemoveListener( this, EVENT_PEDESTRIAN_SMACKDOWN );
+ GetEventManager()->RemoveListener( this, EVENT_BREAK_CAMERA_OR_BOX );
+ GetEventManager()->RemoveListener( this, EVENT_GAG_END );
+ if ( m_SuperCamID < UINT_MAX )
+ {
+ rAssert( m_SuperCamID < UINT_MAX );
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC( 0 );
+ if ( scc != NULL )
+ {
+ scc->UnregisterSuperCam( m_SuperCamID );
+ }
+ }
+
+ mpCamera->ReleaseVerified();
+}
+
+void
+CutCamBehaviour::Apply( Actor* actor, unsigned int timeInMS )
+{
+ m_ParentActor = actor;
+
+ // If we can switch to the camera, we can't do anything else, set the
+ // exclusive flag accordingly (we can't change behaviours)
+ SetExclusive( m_SwitchingEnabled );
+ if ( m_SwitchingEnabled )
+ {
+ // Check the range to the actor, if within range. Switch to its POV
+ // Get Avatar position
+ Avatar* currAvatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rmt::Vector currAvatarPos;
+ currAvatar->GetPosition( currAvatarPos );
+ currAvatarPos.y += 0.5f;
+ actor->LookAt( currAvatarPos, timeInMS );
+ // Get Actor position
+ rmt::Vector actorPosition;
+ actor->GetPosition( &actorPosition );
+ // Compute distanceSqr
+ float distanceToAvatarSqr = (actorPosition - currAvatarPos).MagnitudeSqr();
+ if ( distanceToAvatarSqr < m_TriggerRadiusSqr )
+ {
+ if ( m_CutCamViewActivated == false )
+ {
+
+ SwitchToCutCam();
+ m_CutCamViewActivated = true;
+ }
+ else
+ {
+ // Turn off rendering of the object, we don't want to see it from the inside
+// actor->SetVisibility( false );
+ }
+ mpCamera->SetPosition( actorPosition );
+ }
+ else
+ {
+ if ( m_CutCamViewActivated )
+ {
+// actor->SetVisibility( true );
+ RevertCamera();
+ m_CutCamViewActivated = false;
+ }
+ }
+ }
+}
+
+
+void
+CutCamBehaviour::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_VEHICLE_DESTROYED:
+ case EVENT_OBJECT_DESTROYED:
+ case EVENT_BIG_CRASH:
+ case EVENT_BIG_VEHICLE_CRASH:
+ case EVENT_BIG_AIR:
+ case EVENT_PEDESTRIAN_DODGE:
+ case EVENT_PEDESTRIAN_SMACKDOWN:
+ case EVENT_BREAK_CAMERA_OR_BOX:
+ case EVENT_GAG_END:
+ // Kill this behaviour
+ m_SwitchingEnabled = false;
+ // Are we in cutcam mode, get out of it.
+ if ( m_CutCamViewActivated )
+ {
+ RevertCamera();
+ m_CutCamViewActivated = false;
+ }
+ break;
+ default:
+ rAssert( false );
+ break;
+ };
+}
+
+void CutCamBehaviour::Activate()
+{
+
+}
+
+void CutCamBehaviour::Deactivate()
+{
+ if ( m_CutCamViewActivated )
+ {
+ if ( m_ParentActor )
+ {
+// m_ParentActor->SetVisibility( true );
+ }
+ RevertCamera();
+ m_CutCamViewActivated = false;
+ }
+}
+
+
+void
+CutCamBehaviour::SwitchToCutCam()
+{
+ SuperCamCentral* superCamCentral = GetSuperCamManager()->GetSCC( 0 );
+ rAssert( superCamCentral );
+
+ if ( !m_Registered )
+ {
+ // Register the supercam with the supercamcentral and store the ID returned so
+ // we can reference it again
+ m_SuperCamID = superCamCentral->RegisterSuperCam( mpCamera );
+ m_Registered = true;
+ }
+
+ // Finally, switch to our new supercam
+ superCamCentral->SelectSuperCam( m_SuperCamID );
+}
+
+void
+CutCamBehaviour::RevertCamera()
+{
+ if ( m_Registered )
+ {
+ // Tell the SuperCamCentral that we don't need this camera anymore
+ SuperCamCentral* superCamCentral = GetSuperCamManager()->GetSCC( 0 );
+ rAssert( superCamCentral );
+
+ superCamCentral->UnregisterSuperCam( m_SuperCamID );
+ m_Registered = false;
+ }
+
+ // reset our current supercamcentral ID
+ m_SuperCamID = UINT_MAX;
+}
+
+
+
diff --git a/game/code/ai/actor/cutcambehaviour.h b/game/code/ai/actor/cutcambehaviour.h
new file mode 100644
index 0000000..0e2a2a1
--- /dev/null
+++ b/game/code/ai/actor/cutcambehaviour.h
@@ -0,0 +1,80 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: cutcambehaviour
+//
+// Description: While inside a trigger volume, the actor will switch to its POV
+//
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef CUTCAM_BEHAVIOUR_H
+#define CUTCAM_BEHAVIOUR_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai/actor/behaviour.h>
+#include <radmath/vector.hpp>
+#include <events/eventlistener.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class SurveillanceCam;
+class Actor;
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+
+class CutCamBehaviour : public Behaviour, public EventListener
+{
+ public:
+ CutCamBehaviour( float radius );
+ virtual ~CutCamBehaviour();
+ virtual void Apply( Actor*, unsigned int timeInMS );
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ virtual void Activate();
+ virtual void Deactivate();
+
+ protected:
+
+ float m_TriggerRadiusSqr;
+ bool m_SwitchingEnabled;
+
+ // Camera for use when the character gets really close to the camera
+ // and wants to switch to using it
+ SurveillanceCam* mpCamera;
+ bool m_CutCamViewActivated;
+ unsigned int m_SuperCamID;
+ bool m_Registered; // Is the camera registered with the supercam central
+ Actor* m_ParentActor;
+
+ void SwitchToCutCam();
+ void RevertCamera();
+
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow CutCamBehaviour from being copied and assigned.
+ CutCamBehaviour( const CutCamBehaviour& );
+ CutCamBehaviour& operator=( const CutCamBehaviour& );
+ private:
+
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/evasionbehaviour.cpp b/game/code/ai/actor/evasionbehaviour.cpp
new file mode 100644
index 0000000..ef496de
--- /dev/null
+++ b/game/code/ai/actor/evasionbehaviour.cpp
@@ -0,0 +1,185 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component:
+//
+// Description:
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai/actor/evasionbehaviour.h>
+#include <ai/actor/actor.h>
+#include <worldsim/avatarmanager.h>
+#include <render/IntersectManager/IntersectManager.h>
+#include <ai/actor/actordsg.h>
+#include <main/game.h>
+#include <radtime.hpp>
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+static const float MAX_EVADE_RANGE_SQR = 4.0f * 4.0f;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+rmt::Randomizer EvasionBehaviour::s_Randomizer(0);
+bool EvasionBehaviour::s_RandomizerSeeded = false;
+
+
+EvasionBehaviour::EvasionBehaviour( float minEvadeDistHoriz, float maxEvadeDistHoriz, float minEvadeDistVert, float maxEvadeDistVert, float speedInKPH ) :
+m_MinEvadeDistHoriz( minEvadeDistHoriz ),
+m_MaxEvadeDistHoriz( maxEvadeDistHoriz ),
+m_MinEvadeDistVert( minEvadeDistVert ),
+m_MaxEvadeDistVert( maxEvadeDistVert ),
+m_DeltaTPerMilliSecond( 0 ),
+m_T( 0 )
+{
+ SetSpeed( speedInKPH );
+
+ if (!s_RandomizerSeeded)
+ {
+ s_Randomizer.Seed (Game::GetRandomSeed ());
+ s_RandomizerSeeded = true;
+ }
+}
+
+
+EvasionBehaviour::~EvasionBehaviour()
+{
+
+}
+
+void
+EvasionBehaviour::Apply( Actor* actor, unsigned int timeInMS )
+{
+
+ Avatar* currAvatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rmt::Vector currAvatarPos;
+ currAvatar->GetPosition( currAvatarPos );
+
+ currAvatarPos.y += 0.5f;
+ actor->LookAt( currAvatarPos, timeInMS );
+
+ // Test that we are in range of the object
+ if ( actor->IsMoving() )
+ {
+ }
+ else
+ {
+ // Get distance to player
+ Avatar* currAvatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rmt::Vector currAvatarPos;
+ currAvatar->GetPosition( currAvatarPos );
+
+ rmt::Vector actorPosition;
+ actor->GetPosition( &actorPosition );
+
+ float distanceToPlayerSqr = (actorPosition - currAvatarPos).MagnitudeSqr();
+ if ( distanceToPlayerSqr < MAX_EVADE_RANGE_SQR )
+ {
+ rmt::Vector dest;
+ if ( FindEvasionDestination( actorPosition, &dest ) )
+ {
+ actor->MoveTo( dest, m_EvadeSpeed );
+ // If the wasp is moving, don't allow the actor to do anything else
+ SetExclusive( actor->IsMoving() );
+ }
+ else
+ {
+ SetExclusive( false );
+ }
+ }
+ else
+ {
+ // No reason to evade
+ SetExclusive( false );
+ }
+ }
+
+}
+
+// Enable this behaviour
+void EvasionBehaviour::Activate()
+{
+ SetExclusive( false );
+}
+// Disable this behaviour
+void EvasionBehaviour::Deactivate()
+{
+ SetExclusive( false );
+}
+
+void EvasionBehaviour::SetSpeed( float kph )
+{
+ // Convert from kph to meters per millisecond
+ // Precision loss here?
+ m_EvadeSpeed = kph * 1000.0f / 3600000.0f;
+}
+
+
+bool EvasionBehaviour::FindEvasionDestination( const rmt::Vector& actorPosition, rmt::Vector* dest )
+{
+ // Pick 2 random numbers in between the designer's given input ranges for horizontal and vertical distance
+ float distHoriz;
+ float distVert;
+ distHoriz = ( m_MaxEvadeDistHoriz - m_MinEvadeDistHoriz ) * s_Randomizer.Float() + m_MinEvadeDistHoriz;
+
+ // Direction vector
+ rmt::Vector distOffset( 0, 0, distHoriz );
+ rmt::Matrix rotation;
+ // Calc a random angle between 0 and 2PI
+ float angle = s_Randomizer.Float() * 2.0f * 3.1415927f;
+ rotation.Identity();
+ rotation.FillRotateY( angle );
+ rmt::Vector evasionDest;
+ rotation.Transform( distOffset, &evasionDest );
+ // Grab actorposition and apply it to get world position
+ evasionDest += actorPosition;
+ float groundHeight;
+ if ( FindGroundHeight( evasionDest.x, evasionDest.z, &groundHeight ) == false )
+ {
+ rDebugString( "Evasion - ground plane not found!" );
+ groundHeight = 0;
+ }
+
+ distVert = ( m_MaxEvadeDistVert - m_MinEvadeDistVert ) * s_Randomizer.Float() + m_MinEvadeDistVert;
+ evasionDest.y = groundHeight + distVert;
+
+ *dest = evasionDest;
+
+ return true;
+}
+bool
+EvasionBehaviour::FindGroundHeight( float x, float z, float* out_height )const
+{
+
+ const float INTERSECT_TEST_RADIUS = 1.0f;
+ rmt::Vector deepestIntersectPos, deepestIntersectNormal;
+ rmt::Vector position( x, 100.0f, z);
+ rmt::Vector groundNormal, groundPlaneIntersectionPoint;
+ bool foundPlane;
+
+ GetIntersectManager()->FindIntersection( position,
+ foundPlane,
+ groundNormal,
+ groundPlaneIntersectionPoint );
+
+ if ( foundPlane )
+ {
+ *out_height = groundPlaneIntersectionPoint.y;
+ }
+ return foundPlane;
+}
diff --git a/game/code/ai/actor/evasionbehaviour.h b/game/code/ai/actor/evasionbehaviour.h
new file mode 100644
index 0000000..6b1fe4f
--- /dev/null
+++ b/game/code/ai/actor/evasionbehaviour.h
@@ -0,0 +1,95 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: EvasionBehaviour
+//
+// Description: Evades the player, dodging away from it
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef EVASIONBEHAVIOUR_H
+#define EVASIONBEHAVIOUR_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai/actor/behaviour.h>
+#include <radmath/spline.hpp>
+#include <radmath/random.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+//
+// Constraints:
+//
+//===========================================================================
+class EvasionBehaviour : public Behaviour
+{
+ public:
+ EvasionBehaviour( float minEvadeDistHoriz, float maxEvadeDistHoriz, float minEvadeDistVert, float maxEvadeDistVert, float speedInKPH );
+ virtual ~EvasionBehaviour();
+ virtual void Apply( Actor*, unsigned int timeInMS );
+
+ virtual void Activate();
+ virtual void Deactivate();
+
+ void SetSpeed( float kph );
+
+ protected:
+
+ // Evasion speed in meters per milliseconds
+ float m_EvadeSpeed;
+ float m_MinEvadeDistHoriz;
+ float m_MaxEvadeDistHoriz;
+ float m_MinEvadeDistVert;
+ float m_MaxEvadeDistVert;
+
+ rmt::Spline m_MotionPathSpline;
+ // We update distance by deltaT along the spline.
+ float m_DeltaTPerMilliSecond;
+ // Current position in the range 0 to 1 along the spline
+ float m_T;
+
+ static rmt::Randomizer s_Randomizer;
+ static bool s_RandomizerSeeded;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow EvasionBehaviour from being copied and assigned.
+ EvasionBehaviour( const EvasionBehaviour& );
+ EvasionBehaviour& operator=( const EvasionBehaviour& );
+
+ private:
+
+ // Finds a new evasion point from the current actor position
+ // on returning success, spline, deltaT, and evasion destination are all updated
+ // if false, a point could not be found
+ bool FindEvasionDestination( const rmt::Vector& actorPosition, rmt::Vector* dest );
+ bool FindGroundHeight( float x, float z, float* out_height )const;
+};
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/flyingactor.cpp b/game/code/ai/actor/flyingactor.cpp
new file mode 100644
index 0000000..157b1a5
--- /dev/null
+++ b/game/code/ai/actor/flyingactor.cpp
@@ -0,0 +1,747 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: flyingactor
+//
+// Description: Flying Actor
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai/actor/flyingactor.h>
+#include <ai/actor/actordsg.h>
+#include <ai/actor/attackbehaviour.h>
+#include <ai/actor/cutcambehaviour.h>
+#include <ai/actor/actoranimation.h>
+#include <ai/actor/actoranimationwasp.h>
+#include <ai/actor/actoranimationufo.h>
+#include <ai/actor/attractionbehaviour.h>
+#include <ai/actor/evasionbehaviour.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <render/IntersectManager/IntersectManager.h>
+#include <stateprop/statepropdata.hpp>
+#include <constants/actorenum.h>
+#include <atc/atcmanager.h>
+#include <main/game.h>
+#include <memory/srrmemory.h>
+#include <render/dsg/staticentitydsg.h>
+#include <contexts/bootupcontext.h>
+#include <float.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/gameplaymanager.h>
+
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+// Rotation speed in degrees per second
+const float DEFAULT_ROTATION_SPEED = 40.0f;
+const float DEFAULT_FLYING_SPEED = 0.001f;
+const float INTERSECTION_LIST_RADIUS = 20.0f;
+
+const float RAYWIDTH_FOR_PATH_FINDING = 1.0f;
+
+const float FLYING_HEIGHT = 2.0f;
+const int NUM_MOVEMENT_RETRIES = 6;
+
+const float MIN_FILL_INTERSECTION_DIAMETER = 10.0f;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+rmt::Randomizer FlyingActor::s_Randomizer(0);
+bool FlyingActor::s_RandomizerSeeded = false;
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+
+
+FlyingActor::FlyingActor():
+m_CurrentBehaviour( NULL ),
+m_AttackBehaviour( NULL ),
+m_EvadeBehaviour( NULL ),
+m_AttractionBehaviour( NULL ),
+m_ActorAnimation( NULL ),
+m_DesiredHeight( 0 ),
+m_DesiredHeightEnabled( false ),
+m_IsMoving( false ),
+m_Speed( DEFAULT_FLYING_SPEED ),
+m_CurrentWaypoint( 0 ),
+m_GroundIntersectionHeight( FLT_MAX ),
+m_HighestIntersectHeight( FLT_MAX ),
+m_HighestIntersectNormal( 0,0,0 )
+{
+
+ SetRotationSpeed( DEFAULT_ROTATION_SPEED );
+
+ if (!s_RandomizerSeeded)
+ {
+ s_Randomizer.Seed (Game::GetRandomSeed ());
+ s_RandomizerSeeded = true;
+ }
+
+ m_Waypoints.reserve( NUM_MOVEMENT_RETRIES );
+
+}
+
+FlyingActor::~FlyingActor()
+{
+ if ( m_CurrentBehaviour != NULL )
+ {
+ m_CurrentBehaviour->Deactivate();
+ m_CurrentBehaviour->Release();
+ m_CurrentBehaviour = NULL;
+ }
+
+ if ( m_AttackBehaviour != NULL )
+ {
+ m_AttackBehaviour->Release();
+ m_AttackBehaviour = NULL;
+ }
+ if ( m_EvadeBehaviour != NULL )
+ {
+ m_EvadeBehaviour->Release();
+ m_EvadeBehaviour = NULL;
+ }
+ if ( m_AttractionBehaviour != NULL )
+ {
+ m_AttractionBehaviour->Release();
+ m_AttractionBehaviour = NULL;
+ }
+ if ( m_ActorAnimation != NULL )
+ {
+ delete m_ActorAnimation;
+ }
+
+ tRefCounted::Release(mp_StateProp);
+}
+
+// Initialize object with its own stateprop and give it a name
+// Return true if stateprop found and used to create a dsg object successfully
+bool FlyingActor::Init( const char* statePropName, const char* instanceName )
+{
+ bool success;
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ CStatePropData* pStatePropData = p3d::find< CStatePropData > ( statePropName );
+ if ( pStatePropData )
+ {
+ mp_StateProp = new ActorDSG();
+ mp_StateProp->AddRef();
+
+ CollisionAttributes* pCollAttr = GetATCManager()->CreateCollisionAttributes( PROP_MOVEABLE, 1, 2.2425f );
+
+ pCollAttr->AddRef();
+ rmt::Matrix transform;
+ transform.Identity();
+ mp_StateProp->LoadSetup( pStatePropData, 0, transform, pCollAttr, true, false );
+ mp_StateProp->SetName( instanceName );
+ pCollAttr->Release();
+
+
+ // Quickie test to make wing animations only available on the
+ // beecameras
+ // shields too
+ // and shadows
+ if ( strcmp( statePropName, "beecamera" ) == 0 )
+ {
+ mp_StateProp->SetProcAnimator( new WingAnimator() );
+ // Lets grab a shadow. For now we will use the absolute simplest shadow around
+ tDrawable* shadow = p3d::find< tDrawable >( "SimpleCircleShadowShape" );
+ if ( shadow != NULL )
+ {
+ mp_StateProp->SetShadow( shadow );
+ }
+
+ m_ActorAnimation = new ActorAnimationWasp();
+ }
+ else if ( strcmp( statePropName, "spaceship" )==0 )
+ {
+ m_ActorAnimation = new ActorAnimationUFO();
+ }
+
+ //
+ // Register to be notified of state prop state changes
+ //
+ mp_StateProp->AddStatePropListener( this );
+
+ success = true;
+ }
+ else
+ {
+ success = false;
+ }
+
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+
+ return success;
+}
+
+void FlyingActor::Update( unsigned int timeInMS )
+{
+ FindGroundIntersection( &m_HighestIntersectHeight, &m_HighestIntersectNormal );
+
+ rmt::Matrix currentTransform;
+ GetTransform( &currentTransform );
+/*
+ // Check the intersection list and refill it if it is invalid
+ if ( m_IntersectionSphere.Contains( currentTransform.Row(3) ) == false ||
+ m_IntersectionList.GetNumStatics() == 0 ) // Problem with these things being initialized without
+ // while terrain is still loading
+ {
+ FillIntersectionList( currentTransform.Row(3), INTERSECTION_LIST_RADIUS );
+ }
+ else
+ {
+ // Refill the intersection list dynamics every frame
+ // StaticPhys should not change on a frame to frame basis
+ FillIntersectionListDynamics( currentTransform.Row(3), INTERSECTION_LIST_RADIUS );
+ }
+ */
+ FillIntersectionList( currentTransform.Row(3), INTERSECTION_LIST_RADIUS );
+
+ UpdateMovement( timeInMS, &currentTransform.Row(3) );
+
+ // Update idle animation
+
+ rmt::Matrix newTransform;
+
+ if ( m_ActorAnimation )
+ {
+ if ( m_ActorAnimation->Update( currentTransform, &newTransform, static_cast< float >( timeInMS ) ) )
+ {
+ currentTransform = newTransform;
+ }
+ }
+
+ SetTransform( currentTransform );
+
+
+ if ( m_CurrentBehaviour != NULL )
+ {
+ if ( m_CurrentBehaviour->IsExclusive() == false )
+ {
+ float chance = s_Randomizer.Float();
+ if ( chance < 0.5f )
+ {
+ if ( m_AttackBehaviour )
+ {
+ ChangeBehaviour( m_AttackBehaviour );
+ }
+ }
+ else
+ {
+ if ( m_EvadeBehaviour )
+ {
+ ChangeBehaviour( m_EvadeBehaviour );
+ }
+ }
+ }
+ }
+
+
+ if ( m_CurrentBehaviour != NULL )
+ {
+ m_CurrentBehaviour->Apply( this, timeInMS );
+ }
+
+ // Update the previous position variable (Actor base class variable)
+ m_PreviousPosition = currentTransform.Row(3);
+ // Lets set the shadow position
+ mp_StateProp->RecomputeShadowPositionNoIntersect( m_HighestIntersectHeight, m_HighestIntersectNormal, 0.05f, 3.0f );
+}
+
+
+
+void
+FlyingActor::AddBehaviour( Behaviour* behaviour )
+{
+ if ( dynamic_cast< AttackBehaviour* >( behaviour ) != NULL )
+ {
+ tRefCounted::Assign( m_AttackBehaviour, behaviour );
+ if ( m_CurrentBehaviour == NULL )
+ {
+ ChangeBehaviour( behaviour );
+ }
+ }
+ else if ( dynamic_cast< EvasionBehaviour* >( behaviour ) != NULL )
+ {
+ tRefCounted::Assign( m_EvadeBehaviour, behaviour );
+ if ( m_CurrentBehaviour == NULL )
+ {
+ ChangeBehaviour( behaviour );
+ }
+
+ }
+ else if ( dynamic_cast< AttractionBehaviour* >( behaviour ) != NULL )
+ {
+ tRefCounted::Assign( m_AttractionBehaviour, behaviour );
+ // Lets make it so that when the designer adds this behaviour via
+ // a mission script, it automatically becomes the default
+ ChangeBehaviour( m_AttractionBehaviour );
+ }
+ else
+ {
+ tRefCounted::Assign( m_CurrentBehaviour, behaviour );
+ ChangeBehaviour( m_CurrentBehaviour );
+ }
+}
+
+void
+FlyingActor::Activate()
+{
+ if ( m_AttractionBehaviour )
+ {
+ ChangeBehaviour( m_AttractionBehaviour );
+ }
+
+ if ( m_CurrentBehaviour == NULL )
+ {
+ if ( m_AttractionBehaviour )
+ ChangeBehaviour( m_AttractionBehaviour );
+ else if ( m_AttackBehaviour )
+ ChangeBehaviour( m_AttackBehaviour );
+ }
+ m_IsMoving = false;
+ mp_StateProp->RestoreShield();
+ m_HighestIntersectHeight = FLT_MAX;
+}
+
+void
+FlyingActor::DeactivateBehaviours()
+{
+ ChangeBehaviour( NULL );
+}
+
+void
+FlyingActor::SetRotationSpeed( float rotationSpeed )
+{
+ // convert from degrees per second to radians per millisecond
+ m_RotationSpeed = rotationSpeed * 3.1415927f / ( 180.0f * 1000.0f );
+}
+
+void
+FlyingActor::LookAt( const rmt::Vector& target, unsigned int timeInMS )
+{
+ rmt::Matrix actorTransform;
+ GetTransform( &actorTransform );
+
+ rmt::Matrix newTransform;
+ rmt::Quaternion actorQ;
+ actorQ.BuildFromMatrix( actorTransform );
+
+ rmt::Quaternion targetQ;
+
+ rmt::Vector toTarget = actorTransform.Row(3) - target;
+
+ rmt::Matrix targetMat;
+ targetMat.Identity();
+ targetMat.FillHeading( toTarget, rmt::Vector( 0, 1.0f, 0 ) );
+ targetQ.BuildFromMatrix( targetMat );
+
+ // Find angle between vectors
+ float targetDotActor = targetMat.Row(2).DotProduct( actorTransform.Row(2) );
+ float angle;
+ // Be careful of the values that get fed to arc cosine from the dot product
+ // operation, if they are even slightly out of the range[0,1], NaN and other
+ // bad things will occur, so make sure to clamp them beforehand
+ if ( targetDotActor >= 1.0f )
+ {
+ angle = 0;
+ }
+ else if ( targetDotActor <= 0 )
+ {
+ angle = 3.1415927f / 2.0f;
+ }
+ else
+ {
+ angle = rmt::ACos( targetDotActor );
+ }
+ // SLERP!
+ float deltaAngle = m_RotationSpeed * static_cast< float >( timeInMS );
+
+ float t = deltaAngle / angle;
+ if ( t > 1.0f )
+ {
+ t = 1.0f;
+ }
+ rmt::Quaternion resultQ;
+ resultQ.Slerp( actorQ, targetQ, t );
+ resultQ.Normalize();
+
+ rmt::Matrix result;
+ result.Identity();
+ result.FillRotation( resultQ );
+ result.FillTranslate( actorTransform.Row(3) );
+ SetTransform( result );
+
+}
+
+void
+FlyingActor::ReleaseBehaviours()
+{
+ if ( m_AttackBehaviour != NULL )
+ {
+ m_AttackBehaviour->Release();
+ m_AttackBehaviour = NULL;
+ }
+ if ( m_EvadeBehaviour != NULL )
+ {
+ m_EvadeBehaviour->Release();
+ m_EvadeBehaviour = NULL;
+ }
+}
+
+void FlyingActor::FindWaypoint( const rmt::Vector& start, const rmt::Vector& end, int depth )
+{
+ rmt::Vector unused;
+
+ if ( depth <= 0 )
+ return;
+
+ // Determine if there is a clear LOS between start and end
+ rmt::Vector intersection;
+ bool hitObject = m_IntersectionList.TestIntersection( start, end, &intersection );
+ if ( hitObject == false )
+ {
+ m_Waypoints.push_back( end );
+ }
+ else
+ {
+ // Check for an intermediary position somewhere around the intersection point
+ const rmt::Vector up( 0, 1.0f, 0 );
+ rmt::Vector ray = end - start;
+ ray.Normalize();
+ rmt::Vector right;
+ right.CrossProduct( ray, up );
+
+ float offsetDist = 2.0f;
+ float testDir = 1.0f;
+
+ for ( int i = 0 ; i < NUM_MOVEMENT_RETRIES ; i++ )
+ {
+ rmt::Vector offset;
+ offset = right * testDir * offsetDist;
+
+ testDir *= -1.0f;
+ if ( testDir > 0 )
+ offsetDist += 2.5f;
+
+ rmt::Vector groundNormal, groundPlaneIntersectionPoint;
+ bool foundPlane;
+
+ rmt::Vector intermediary = offset + intersection;
+
+ GetIntersectManager()->FindIntersection( intermediary,
+ foundPlane,
+ groundNormal,
+ groundPlaneIntersectionPoint );
+
+ if ( foundPlane )
+ {
+ intermediary.y = groundPlaneIntersectionPoint.y + FLYING_HEIGHT + s_Randomizer.Float() * 2.0f;
+ }
+
+ if ( m_IntersectionList.LineOfSight( start, intermediary )&&
+ m_IntersectionList.LineOfSight( intermediary, end ) )
+ {
+ // Clear line of sight, add it to the waypoint list and recurse
+ m_Waypoints.push_back( intermediary );
+ FindWaypoint( intermediary, end, depth - 1 );
+ break;
+ }
+ }
+ }
+}
+
+void
+FlyingActor::MoveTo( const rmt::Vector& destination, float speed )
+{
+ if ( speed <= 0 )
+ speed = DEFAULT_FLYING_SPEED;
+
+ m_Speed = speed;
+
+ rmt::Vector position;
+ GetPosition( &position );
+
+ rmt::Vector midway = (destination + position )* 0.5f;
+ float diameter = (destination - position).Magnitude();
+ if ( diameter < MIN_FILL_INTERSECTION_DIAMETER )
+ diameter = MIN_FILL_INTERSECTION_DIAMETER;
+ FillIntersectionList( midway, diameter );
+
+
+ // Reset waypoint information
+ m_Waypoints.resize(0);
+ m_CurrentWaypoint = 0;
+
+
+ //
+ // First make sure that the destination is actually above ground
+ rmt::Vector intersectTestPosition = destination;
+ intersectTestPosition.y += 100.0f;
+ rmt::Vector groundAdjustedDest = destination;
+ rmt::Vector groundNormal, groundPlaneIntersectionPoint;
+ bool foundPlane;
+ GetIntersectManager()->FindIntersection( intersectTestPosition,
+ foundPlane,
+ groundNormal,
+ groundPlaneIntersectionPoint );
+
+ if ( foundPlane )
+ {
+ if ( groundAdjustedDest.y < groundPlaneIntersectionPoint.y + FLYING_HEIGHT )
+ {
+ groundAdjustedDest.y = groundPlaneIntersectionPoint.y + FLYING_HEIGHT;
+ }
+ }
+ // Start finding waypoints
+ // Note that this is a recursive function
+ FindWaypoint( position, groundAdjustedDest, m_Waypoints.capacity() );
+ // Did we create any waypoints?
+ // If so, start moving, and set the appropriate flags
+ if ( m_Waypoints.empty() == false )
+ m_IsMoving = true;
+ else
+ m_IsMoving = false;
+}
+
+
+// Change the current behaviour of the object.
+// Tell the current behaviour object to deactivate itself
+// The reassign the m_CurrentBehaviour pointer to the given
+// behaviour and tell it to activate itself
+bool
+FlyingActor::ChangeBehaviour( Behaviour* newBehaviour )
+{
+ bool changed;
+
+ if ( m_CurrentBehaviour == newBehaviour )
+ {
+ changed = false;
+ }
+ else
+ {
+ if ( m_CurrentBehaviour == NULL )
+ {
+ tRefCounted::Assign( m_CurrentBehaviour, newBehaviour );
+ m_CurrentBehaviour->Activate();
+ changed = true;
+ }
+ else
+ {
+ m_CurrentBehaviour->Deactivate();
+ tRefCounted::Assign( m_CurrentBehaviour, newBehaviour );
+ if ( m_CurrentBehaviour != NULL )
+ {
+ m_CurrentBehaviour->Activate();
+ }
+ changed = true;
+ }
+ }
+ return changed;
+}
+
+bool
+FlyingActor::UpdateMovement( unsigned int timeInMS, rmt::Vector* out_newPosition )
+{
+ rmt::Vector movementOffset(0,0,0);
+ rmt::Vector currPosition;
+ GetPosition( &currPosition );
+
+ // Sanity check
+ {
+ bool cleanMove = m_IntersectionList.LineOfSight( currPosition, m_PreviousPosition, RAYWIDTH_FOR_PATH_FINDING );
+ if ( cleanMove == false )
+ {
+ // We just moved through a wall or something crazy. What was physics doing?
+ // Abort current movement, snap back to the old position
+ currPosition = m_PreviousPosition;
+ m_IsMoving = false;
+ }
+ }
+
+
+ // Basically we want to follow waypoints until the final one is reached
+ // Check to see if it was reached and set the moving flag to false to disable it in
+ // the future
+ if ( m_CurrentWaypoint >= m_Waypoints.size() )
+ m_IsMoving = false;
+
+ bool positionUpdated; // return value, was the object actually moved?
+
+ if ( m_IsMoving )
+ {
+ const rmt::Vector& nextDest = m_Waypoints[ m_CurrentWaypoint ];
+ // We are moving, follow the waypoints
+ // Check that we still have a line of sight. If not, abort
+ if ( m_IntersectionList.LineOfSight( currPosition, nextDest ) == false )
+ {
+ m_IsMoving = false;
+ return false;
+ }
+
+ float distToWaypoint = ( nextDest - currPosition ).Magnitude();
+ float distToTravel = timeInMS * m_Speed;
+ // Create vector from current position to the waypoint
+
+ const float CLOSE_ENOUGH_TO_WAYPOINT_DIST = 0.4f;
+
+ if ( distToTravel > ( distToWaypoint - CLOSE_ENOUGH_TO_WAYPOINT_DIST ) )
+ {
+ // We are going to travel too far, clamp to the waypoint
+ *out_newPosition = nextDest;
+ m_CurrentWaypoint++;
+ }
+ else
+ {
+ rmt::Vector toWaypoint = ( nextDest - currPosition ) / distToWaypoint;
+
+ // Move to the next waypoint
+ *out_newPosition = currPosition + toWaypoint * m_Speed * static_cast< float >( timeInMS );
+ }
+ positionUpdated = true;
+ }
+ else
+ {
+ // Not moving
+ // Check current height versus desired height
+ if ( m_HighestIntersectHeight == FLT_MAX )
+ {
+ // Couldnt find an intersection. Set it now to be the current height - FLYING_HEIGHT
+ m_HighestIntersectHeight = currPosition.y - FLYING_HEIGHT;
+ }
+
+ if ( currPosition.y < m_HighestIntersectHeight + FLYING_HEIGHT )
+ {
+ // Move upwards at m_Speed
+ // Calc distance to the desired point
+ float distToMinHeight = m_HighestIntersectHeight + FLYING_HEIGHT - currPosition.y;
+ float distCanTravel = m_Speed * static_cast< float >( timeInMS );
+ if ( distToMinHeight < distCanTravel )
+ {
+ // Just clamp the position to desired
+ *out_newPosition = currPosition;
+ out_newPosition->y = m_HighestIntersectHeight + FLYING_HEIGHT;
+ }
+ else
+ {
+ // Move upwards
+ out_newPosition->y += distCanTravel;
+ }
+ positionUpdated = true;
+ }
+ else
+ {
+ positionUpdated = false;
+ }
+ }
+
+
+ return positionUpdated;
+}
+
+bool
+FlyingActor::FindGroundIntersection( float* out_height, rmt::Vector* normal )
+{
+
+ bool intersectionFound = false;
+
+ const float INTERSECT_TEST_RADIUS = 1.0f;
+ rmt::Vector deepestIntersectPos, deepestIntersectNormal;
+ rmt::Vector position;
+ GetPosition( &position );
+
+ rmt::Vector deepPosition = position;
+ deepPosition.y -= 50.0f;
+ rmt::Vector objectIntersection;
+ if ( m_IntersectionList.TestIntersectionStatics( position, deepPosition, &objectIntersection ) )
+ {
+ // Unfort we can't get normal information from colliding with a static or dynamic object.
+ *normal = rmt::Vector( 0,1,0 );
+ *out_height = objectIntersection.y;
+ intersectionFound = true;
+ }
+ else
+ {
+ rmt::Vector groundNormal, groundPlaneIntersectionPoint;
+ bool foundPlane;
+ GetIntersectManager()->FindIntersection( position,
+ foundPlane,
+ groundNormal,
+ groundPlaneIntersectionPoint );
+ if ( foundPlane )
+ {
+ *normal = groundNormal;
+ *out_height = groundPlaneIntersectionPoint.y;
+ intersectionFound = true;
+ }
+
+ }
+
+ if ( m_DesiredHeightEnabled && GetDesiredHeight() > *out_height )
+ {
+ *out_height = GetDesiredHeight();
+ intersectionFound = true;
+ }
+
+ return intersectionFound;
+}
+
+//=============================================================================
+// FlyingActor::RecieveEvent
+//=============================================================================
+// Description: Trap state prop state changes to notify sound system
+//
+// Parameters: callback - event type
+// stateProp - state prop undergoing state change
+//
+// Return: void
+//
+//=============================================================================
+void FlyingActor::RecieveEvent( int callback , CStateProp* stateProp )
+{
+ unsigned int newState;
+
+ if( callback == STATEPROP_CHANGE_STATE_EVENT )
+ {
+ newState = stateProp->GetState();
+
+ switch( newState )
+ {
+ case ActorEnum::eTransitionToReadyToAttack:
+ GetEventManager()->TriggerEvent( EVENT_WASP_CHARGING, this );
+ break;
+
+ case ActorEnum::eIdleReadyToAttack:
+ GetEventManager()->TriggerEvent( EVENT_WASP_CHARGED, this );
+ break;
+
+ case ActorEnum::eAttacking:
+ GetEventManager()->TriggerEvent( EVENT_WASP_ATTACKING, this );
+ break;
+
+ case ActorEnum::eDestroyed:
+ GetCharacterSheetManager()->IncNumWaspsDestroyed(GetGameplayManager()->GetCurrentLevelIndex());
+ GetEventManager()->TriggerEvent( EVENT_WASP_BLOWED_UP, this );
+ break;
+
+ default:
+ //
+ // Unknown state, do nothing
+ //
+ break;
+ }
+ }
+}
diff --git a/game/code/ai/actor/flyingactor.h b/game/code/ai/actor/flyingactor.h
new file mode 100644
index 0000000..cd18c8b
--- /dev/null
+++ b/game/code/ai/actor/flyingactor.h
@@ -0,0 +1,131 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Flying Actor
+//
+// Description: Flying actors - bee cameras
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef FLYING_ACTOR_H
+#define FLYING_ACTOR_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai/actor/actor.h>
+#include <render/culling/swaparray.h>
+#include <ai/actor/behaviour.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class StatePropDSG;
+class ActorAnimation;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+//
+// Constraints:
+//
+//===========================================================================
+class FlyingActor : public Actor,
+ public CStatePropListener
+{
+ public:
+ FlyingActor();
+ virtual ~FlyingActor();
+
+ virtual bool Init( const char* statePropname, const char* instanceName );
+ virtual void Update( unsigned int timeInMS );
+ virtual void AddBehaviour( Behaviour* );
+ virtual void Activate();
+ virtual void DeactivateBehaviours();
+
+ // Set speed in degrees per second
+ virtual void SetRotationSpeed( float rotationSpeed );
+ // Give it a look at target, the actor will
+ // automatically reorient itself to look at the given position
+ virtual void LookAt( const rmt::Vector& position, unsigned int timeInMS );
+ virtual void ReleaseBehaviours();
+
+ void SetDesiredHeight( float desiredheight ){ m_DesiredHeight = desiredheight; }
+ float GetDesiredHeight() const { return m_DesiredHeight; }
+ void SetDesiredHeightEnabled( bool enable ) { m_DesiredHeightEnabled = enable; }
+
+ virtual void MoveTo( const rmt::Vector& destination, float speed );
+ virtual bool IsMoving()const { return m_IsMoving; }
+
+ //
+ // Needed to trap state transitions for sound player -- Esan
+ //
+ void RecieveEvent( int callback , CStateProp* stateProp );
+
+ protected:
+
+ bool ChangeBehaviour( Behaviour* newBehaviour );
+ bool FindGroundHeight( float x, float z, float* out_height )const;
+ // If a movement destination was specified, updatemovement will move along that path
+ // returns true if a new position was computed
+ bool UpdateMovement( unsigned int timeInMS, rmt::Vector* out_newPosition );
+ bool FindGroundIntersection( float* height, rmt::Vector* normal );
+ void FindWaypoint( const rmt::Vector& start, const rmt::Vector& end, int depth );
+
+ // Randomizer
+ static rmt::Randomizer s_Randomizer;
+ static bool s_RandomizerSeeded;
+
+ // STATE information
+ // -----------------
+ Behaviour* m_CurrentBehaviour;
+ Behaviour* m_AttackBehaviour;
+ Behaviour* m_EvadeBehaviour;
+ Behaviour* m_AttractionBehaviour;
+
+ // Animation for updating the root position of the object
+ // (procedural idle animation)
+ ActorAnimation* m_ActorAnimation;
+
+ // Movement information
+
+ float m_DesiredHeight;
+ bool m_DesiredHeightEnabled;
+ bool m_IsMoving;
+ rmt::Vector m_MovementDestination;
+ float m_Speed;
+
+ std::vector< rmt::Vector, s2alloc<rmt::Vector> > m_Waypoints;
+ unsigned int m_CurrentWaypoint;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow FlyingActor from being copied and assigned.
+ FlyingActor( const FlyingActor& );
+ FlyingActor& operator=( const FlyingActor& );
+
+ private:
+
+
+ float m_RotationSpeed; // in radians per millisecond
+ float m_GroundIntersectionHeight; // ground intersect height
+ float m_HighestIntersectHeight; // ground + object intersect height
+ rmt::Vector m_HighestIntersectNormal; // normal of the highest intersect position
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/intersectionlist.cpp b/game/code/ai/actor/intersectionlist.cpp
new file mode 100644
index 0000000..ff6e7a8
--- /dev/null
+++ b/game/code/ai/actor/intersectionlist.cpp
@@ -0,0 +1,721 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: intersectionlist
+//
+// Description: Holds a list of pointers to sim collision bounding boxes
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai\actor\intersectionlist.h>
+#include <simcollision\proximitydetection.hpp>
+#include <simcollision\collisionvolume.hpp>
+#include <simcollision\collisionobject.hpp>
+#include <simcommon\simstate.hpp>
+#include <simcommon\simstatearticulated.hpp>
+#include <float.h>
+#include <render\DSG\DynaPhysDSG.h>
+#include <render\DSG\fenceentitydsg.h>
+#include <render\DSG\AnimCollisionEntityDSG.h>
+#include <render\intersectmanager\intersectmanager.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+const unsigned int SIZE_INTERSECTION_LIST_ARRAYS = 50;
+const unsigned int SIZE_FENCE_PIECE_ARRAY = 100;
+const float INTERSECTION_TEST_RAY_THICKNESS = 1.0f;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// IntersectionList::IntersectionList
+//===========================================================================
+// Description:
+// IntersectionList ctor - presizes internal arrays
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+IntersectionList::IntersectionList()
+{
+ m_StaticCollisionList.reserve( SIZE_INTERSECTION_LIST_ARRAYS );
+ m_DynamicCollisionList.reserve( SIZE_INTERSECTION_LIST_ARRAYS );
+ m_AnimPhysCollisionList.reserve( SIZE_INTERSECTION_LIST_ARRAYS );
+ // Allocate the fence list array
+ m_FenceList.reserve( SIZE_FENCE_PIECE_ARRAY );
+
+}
+
+
+//===========================================================================
+// IntersectionList::~IntersectionList
+//===========================================================================
+// Description:
+// IntersectionList dtor - removes all data from the list
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+IntersectionList::~IntersectionList()
+{
+ Clear();
+}
+
+void
+IntersectionList::Clear()
+{
+ ClearStatics();
+ ClearDynamics();
+ ClearAnimPhys();
+ ClearFencePieces();
+}
+
+void
+IntersectionList::ClearDynamics()
+{
+ DynaCollListIt it;
+ for ( it = m_DynamicCollisionList.begin() ; it != m_DynamicCollisionList.end() ; it++ )
+ {
+ it->first->Release();
+ if ( it->second )
+ it->second->Release();
+ }
+
+ m_DynamicCollisionList.clear();
+}
+
+void IntersectionList::ClearAnimPhys()
+{
+ for ( unsigned int i = 0 ; i < m_AnimPhysCollisionList.size() ; i++ )
+ {
+ m_AnimPhysCollisionList[i]->Release();
+ }
+ m_AnimPhysCollisionList.resize(0);
+}
+
+void
+IntersectionList::ClearStatics()
+{
+ for ( unsigned int i = 0 ; i < m_StaticCollisionList.size() ; i++ )
+ {
+ m_StaticCollisionList[i]->Release();
+ }
+ m_StaticCollisionList.resize(0);
+}
+
+void
+IntersectionList::ClearFencePieces()
+{
+ m_FenceList.resize(0);
+}
+
+bool
+IntersectionList::TestIntersectionStatics( const rmt::Vector& sourceOfRay, const rmt::Vector& endOfRay, rmt::Vector* intersection )
+{
+ rAssert( m_StaticCollisionList.capacity() == SIZE_INTERSECTION_LIST_ARRAYS );
+
+ bool foundIntersection = false;
+ // Lets iterate through the boxes and see if any hit
+
+ rmt::Vector sray;
+ sray.Sub(endOfRay, sourceOfRay);
+ rmt::Vector ray = sray;
+ float rayLen = ray.NormalizeSafe();
+
+ float closestDistance = FLT_MAX;
+ sim::RayIntersectionInfo info;
+ info.SetMethod( sim::RayIntersectionVolume );
+ info.sRayThickness = INTERSECTION_TEST_RAY_THICKNESS;
+
+ for ( unsigned int i = 0 ; i < m_StaticCollisionList.size() ; i++ )
+ {
+ sim::CollisionVolume* vol = m_StaticCollisionList[i]->GetCollisionVolume();
+ if ( vol )
+ {
+ info.mCollisionVolume = vol;
+ if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
+ {
+ if( info.mDistFromSource < closestDistance )
+ {
+ // This volume is the closest yet
+ // make not of it and keep going
+ foundIntersection = true;
+ closestDistance = info.mDistFromSource;
+ }
+ }
+ }
+ }
+
+
+ // Test fence pieces
+ LineSegment2D rayLineSeg;
+ rayLineSeg.p1.x = sourceOfRay.x;
+ rayLineSeg.p1.y = sourceOfRay.z;
+ rayLineSeg.p2.x = endOfRay.x;
+ rayLineSeg.p2.y = endOfRay.z;
+
+ for ( unsigned int i = 0 ; i < m_FenceList.size() ; i++ )
+ {
+ float t;
+ if ( LineSegmentIntersection( rayLineSeg, m_FenceList[i], &t ) )
+ {
+ LineSegment2D f = m_FenceList[i];
+ // Get the distance based upon t
+ rmt::Vector fenceIntersection = sourceOfRay + t * ( endOfRay - sourceOfRay );
+ float dist = ( sourceOfRay - fenceIntersection ).Magnitude();
+
+ if ( dist < closestDistance )
+ {
+ closestDistance = dist;
+ foundIntersection = true;
+ }
+ }
+ }
+
+
+ if ( foundIntersection )
+ {
+ // The intersection point is going to be
+ // sourceOfRay + ray * closestDistance
+ closestDistance += INTERSECTION_TEST_RAY_THICKNESS;
+ *intersection = sourceOfRay + ray * closestDistance;
+ }
+
+ return foundIntersection;
+}
+
+bool
+IntersectionList::TestIntersectionDynamics( const rmt::Vector& sourceOfRay,
+ const rmt::Vector& endOfRay,
+ rmt::Vector* intersection,
+ DynaPhysDSG** objectHit )
+{
+ rAssert( m_DynamicCollisionList.capacity() == SIZE_INTERSECTION_LIST_ARRAYS );
+
+ bool foundIntersection;
+
+ // Lets iterate through the boxes and see if any hit
+
+ rmt::Vector sray;
+ sray.Sub(endOfRay, sourceOfRay);
+ rmt::Vector ray = sray;
+ float rayLen = ray.NormalizeSafe();
+
+ float closestDistance = FLT_MAX;
+ sim::RayIntersectionInfo info;
+ info.SetMethod( sim::RayIntersectionVolume );
+ info.sRayThickness = INTERSECTION_TEST_RAY_THICKNESS;
+
+ DynaCollListIt it;
+
+ for ( it = m_DynamicCollisionList.begin() ; it != m_DynamicCollisionList.end() ; it++ )
+ {
+ info.mCollisionVolume = it->first->GetCollisionVolume();
+ if ( info.mCollisionVolume )
+ {
+ if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
+ {
+ if( info.mDistFromSource < closestDistance )
+ {
+ // This volume is the closest yet
+ // make not of it and keep going
+ closestDistance = info.mDistFromSource;
+ if ( objectHit )
+ *objectHit = it->second;
+ }
+ }
+ }
+ }
+ if ( closestDistance != FLT_MAX )
+ {
+ // The intersection point is going to be
+ // sourceOfRay + ray * closestDistance
+ closestDistance += INTERSECTION_TEST_RAY_THICKNESS;
+ *intersection = sourceOfRay + ray * closestDistance;
+ foundIntersection = true;
+ }
+ else
+ {
+ foundIntersection = false;
+ }
+
+ return foundIntersection;
+}
+
+bool
+IntersectionList::TestIntersectionAnimPhys( const rmt::Vector& sourceOfRay,
+ const rmt::Vector& endOfRay,
+ rmt::Vector* intersection )
+{
+ rAssert( m_AnimPhysCollisionList.capacity() == SIZE_INTERSECTION_LIST_ARRAYS );
+
+ bool foundIntersection = false;
+ // Lets iterate through the boxes and see if any hit
+
+ rmt::Vector sray;
+ sray.Sub(endOfRay, sourceOfRay);
+ rmt::Vector ray = sray;
+ float rayLen = ray.NormalizeSafe();
+
+ float closestDistance = FLT_MAX;
+ sim::RayIntersectionInfo info;
+ info.SetMethod( sim::RayIntersectionVolume );
+ info.sRayThickness = INTERSECTION_TEST_RAY_THICKNESS;
+
+ for ( unsigned int i = 0 ; i < m_AnimPhysCollisionList.size() ; i++ )
+ {
+ sim::CollisionVolume* vol = m_AnimPhysCollisionList[i]->GetCollisionVolume();
+ if ( vol )
+ {
+ info.mCollisionVolume = vol;
+ if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
+ {
+ if( info.mDistFromSource < closestDistance )
+ {
+ // This volume is the closest yet
+ // make not of it and keep going
+ foundIntersection = true;
+ closestDistance = info.mDistFromSource;
+ }
+ }
+ }
+ }
+
+
+ // Test fence pieces
+ LineSegment2D rayLineSeg;
+ rayLineSeg.p1.x = sourceOfRay.x;
+ rayLineSeg.p1.y = sourceOfRay.z;
+ rayLineSeg.p2.x = endOfRay.x;
+ rayLineSeg.p2.y = endOfRay.z;
+
+ for ( unsigned int i = 0 ; i < m_FenceList.size() ; i++ )
+ {
+ float t;
+ if ( LineSegmentIntersection( rayLineSeg, m_FenceList[i], &t ) )
+ {
+ LineSegment2D f = m_FenceList[i];
+ // Get the distance based upon t
+ rmt::Vector fenceIntersection = sourceOfRay + t * ( endOfRay - sourceOfRay );
+ float dist = ( sourceOfRay - fenceIntersection ).Magnitude();
+
+ if ( dist < closestDistance )
+ {
+ closestDistance = dist;
+ foundIntersection = true;
+ }
+ }
+ }
+
+
+ if ( foundIntersection )
+ {
+ // The intersection point is going to be
+ // sourceOfRay + ray * closestDistance
+ closestDistance += INTERSECTION_TEST_RAY_THICKNESS;
+ *intersection = sourceOfRay + ray * closestDistance;
+ }
+
+ return foundIntersection;
+}
+
+bool
+IntersectionList::TestIntersection( const rmt::Vector& sourceOfRay, const rmt::Vector& endOfRay, rmt::Vector* intersection, float rayWidthForFences )
+{
+ rAssert( m_StaticCollisionList.capacity() == SIZE_INTERSECTION_LIST_ARRAYS );
+ rAssert( m_DynamicCollisionList.capacity() == SIZE_INTERSECTION_LIST_ARRAYS );
+
+
+ bool foundIntersection;
+
+ // Lets iterate through the boxes and see if any hit
+ rmt::Vector sray;
+ sray.Sub(endOfRay, sourceOfRay);
+ rmt::Vector ray = sray;
+ float rayLen = ray.NormalizeSafe();
+
+ float closestDistance = FLT_MAX;
+ sim::RayIntersectionInfo info;
+ info.SetMethod( sim::RayIntersectionVolume );
+ info.sRayThickness = INTERSECTION_TEST_RAY_THICKNESS;
+
+ DynaCollListIt it;
+
+ for ( it = m_DynamicCollisionList.begin() ; it != m_DynamicCollisionList.end() ; it++ )
+ {
+ info.mCollisionVolume = it->first->GetCollisionVolume();
+ if ( info.mCollisionVolume )
+ {
+ if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
+ {
+ if( info.mDistFromSource < closestDistance )
+ {
+ // This volume is the closest yet
+ // make note of it and keep going
+ closestDistance = info.mDistFromSource;
+ }
+ }
+ }
+ }
+ for ( unsigned int i = 0 ; i < m_StaticCollisionList.size() ; i++ )
+ {
+ info.mCollisionVolume = m_StaticCollisionList[i]->GetCollisionVolume();
+ if ( info.mCollisionVolume )
+ {
+ if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
+ {
+ if( info.mDistFromSource < closestDistance )
+ {
+ // This volume is the closest yet
+ // make note of it and keep going
+ closestDistance = info.mDistFromSource;
+ }
+ }
+ }
+ }
+ LineSegment2D rayLineSeg;
+ rayLineSeg.p1.x = sourceOfRay.x;
+ rayLineSeg.p1.y = sourceOfRay.z;
+ rayLineSeg.p2.x = endOfRay.x;
+ rayLineSeg.p2.y = endOfRay.z;
+
+ for ( unsigned int i = 0 ; i < m_FenceList.size() ; i++ )
+ {
+ float t;
+ if ( LineSegmentIntersection( rayLineSeg, m_FenceList[i], &t ) )
+ {
+ // Get the distance based upon t
+ rmt::Vector fenceIntersection = sourceOfRay + t * ( endOfRay - sourceOfRay );
+ float dist = ( sourceOfRay - fenceIntersection ).Magnitude();
+
+ if ( dist < closestDistance )
+ {
+ closestDistance = dist - rayWidthForFences;
+ foundIntersection = true;
+ }
+ }
+ }
+
+ if ( closestDistance != FLT_MAX )
+ {
+ // The intersection point is going to be
+ // sourceOfRay + ray * closestDistance
+ closestDistance += INTERSECTION_TEST_RAY_THICKNESS;
+ *intersection = sourceOfRay + ray * closestDistance;
+ foundIntersection = true;
+ }
+ else
+ {
+ foundIntersection = false;
+ }
+
+ return foundIntersection;
+}
+
+
+bool
+IntersectionList::LineOfSight( const rmt::Vector& sourceOfRay, const rmt::Vector& endOfRay, float rayWidth, bool staticsOnly )
+{
+ rmt::Vector sray;
+ sray.Sub(endOfRay, sourceOfRay);
+ rmt::Vector ray = sray;
+ float rayLen = ray.NormalizeSafe();
+
+
+ unsigned int i;
+ sim::RayIntersectionInfo info;
+ info.SetMethod( sim::RayIntersectionVolume );
+ info.sRayThickness = rayWidth;
+
+
+ // Test the statics
+ for ( i = 0 ; i < m_StaticCollisionList.size() ; i++ )
+ {
+ info.mCollisionVolume = m_StaticCollisionList[i]->GetCollisionVolume();
+ if ( info.mCollisionVolume )
+ {
+ if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
+ {
+ // Hit
+ return false;
+ }
+ }
+ }
+
+ if ( !staticsOnly )
+ {
+ // Test the dynamics
+ DynaCollListIt it;
+ for ( it = m_DynamicCollisionList.begin() ; it != m_DynamicCollisionList.end() ; it++ )
+ {
+ info.mCollisionVolume = it->first->GetCollisionVolume();
+ if ( info.mCollisionVolume )
+ {
+ if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
+ {
+ // Hit
+ return false;
+ }
+ }
+ }
+
+ for ( i = 0 ; i < m_AnimPhysCollisionList.size() ; i++ )
+ {
+ info.mCollisionVolume = m_AnimPhysCollisionList[i]->GetCollisionVolume();
+ if ( info.mCollisionVolume )
+ {
+ if ( sim::RayIntersectVolume( sourceOfRay, ray, sray, rayLen, info ) )
+ {
+ // Hit
+ return false;
+ }
+ }
+ }
+ }
+
+ LineSegment2D rayLineSeg;
+ rayLineSeg.p1.x = sourceOfRay.x;
+ rayLineSeg.p1.y = sourceOfRay.z;
+ rayLineSeg.p2.x = endOfRay.x;
+ rayLineSeg.p2.y = endOfRay.z;
+
+ // Test the fence pieces
+ for ( unsigned int i = 0 ; i < m_FenceList.size() ; i++ )
+ {
+ float t;
+ if ( LineSegmentIntersection( rayLineSeg, m_FenceList[i], &t ) )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void
+IntersectionList::AddStatic( sim::SimState* simstate )
+{
+ if ( m_StaticCollisionList.size() >= SIZE_INTERSECTION_LIST_ARRAYS )
+ return;
+
+ if ( simstate )
+ {
+ sim::CollisionObject* collisionObject = simstate->GetCollisionObject();
+ if ( collisionObject )
+ {
+ collisionObject->AddRef();
+ m_StaticCollisionList.push_back( collisionObject );
+ }
+ }
+}
+
+void
+IntersectionList::AddDynamic( sim::SimState* simstate, DynaPhysDSG* object )
+{
+ if ( m_DynamicCollisionList.size() >= SIZE_INTERSECTION_LIST_ARRAYS )
+ return;
+
+
+ if ( simstate )
+ {
+ sim::CollisionObject* collisionObject = simstate->GetCollisionObject();
+ if ( collisionObject )
+ {
+ if(m_DynamicCollisionList.find(collisionObject) == m_DynamicCollisionList.end())
+ {
+ collisionObject->AddRef();
+
+ if ( object )
+ object->AddRef();
+
+ m_DynamicCollisionList.insert( collisionObject, object );
+ }
+ else
+ {
+// rAssert(0);
+ }
+ }
+ }
+}
+
+void
+IntersectionList::AddAnimPhys( sim::SimStateArticulated* simstate )
+{
+ if ( m_AnimPhysCollisionList.size() >= SIZE_INTERSECTION_LIST_ARRAYS )
+ return;
+
+ if ( simstate )
+ {
+ sim::CollisionObject* collisionObject = simstate->GetCollisionObject();
+ if ( collisionObject )
+ {
+ collisionObject->AddRef();
+ m_AnimPhysCollisionList.push_back( collisionObject );
+ }
+ }
+}
+
+
+void
+IntersectionList::AddFencePiece( FenceEntityDSG* fencePiece )
+{
+ static int maxcount = 0;
+ // Prevent any dynamic allocations because of the push_back contained in this function
+ if ( m_FenceList.size() >= SIZE_FENCE_PIECE_ARRAY )
+ {
+ return;
+ }
+ LineSegment2D lineSegment;
+
+ lineSegment.p1.x = fencePiece->mStartPoint.x;
+ lineSegment.p1.y = fencePiece->mStartPoint.z;
+
+ lineSegment.p2.x = fencePiece->mEndPoint.x;
+ lineSegment.p2.y = fencePiece->mEndPoint.z;
+
+ m_FenceList.push_back( lineSegment );
+
+}
+
+// Add all objects in the given sphere to the list
+int
+IntersectionList::FillIntersectionListStatics( const rmt::Vector& position, float radius )
+{
+ int numAdded = FillIntersectionListFence( position, radius );
+
+ ClearStatics();
+ // Iterate through the statics and grab them all
+ ReserveArray< StaticPhysDSG* > spList(200);
+ GetIntersectManager()->FindStaticPhysElems( const_cast< rmt::Vector& >( position ), radius, spList );
+ for ( int i = 0 ; i < spList.mUseSize ; i++ )
+ {
+ StaticPhysDSG* object = spList[i];
+ sim::SimState* simState = object->GetSimState();
+ AddStatic( simState );
+ }
+ numAdded += spList.mUseSize;
+
+ return numAdded;
+}
+
+int
+IntersectionList::FillIntersectionListFence( const rmt::Vector& position, float radius )
+{
+ ClearFencePieces();
+
+ ReserveArray< FenceEntityDSG* > feList(200);
+ // Perhaps intersectmanager should be changed to accept a const rmt::Vector& instead?
+ GetIntersectManager()->FindFenceElems( const_cast< rmt::Vector& > (position), radius, feList );
+ for ( int i = 0 ; i < feList.mUseSize ; i++ )
+ {
+ FenceEntityDSG* object = feList[i];
+ AddFencePiece( object );
+ }
+
+ return feList.mUseSize;
+}
+
+int IntersectionList::FillIntersectionListDynamics( const rmt::Vector& position, float radius, bool addDynaPhys, DynaPhysDSG* source )
+{
+ ClearDynamics();
+
+ ReserveArray< DynaPhysDSG* > dpList(200);
+ // Perhaps intersectmanager should be changed to accept a const rmt::Vector& instead?
+ GetIntersectManager()->FindDynaPhysElems( const_cast< rmt::Vector& > (position), radius, dpList );
+ for ( int i = 0 ; i < dpList.mUseSize ; i++ )
+ {
+ DynaPhysDSG* object = dpList[i];
+
+ if ( object != source )
+ {
+ sim::SimState* simState = object->GetSimState();
+ // [Dusit: June 16th, 2003]
+ // Don't seem to be using object anywhere, and there seemed to be an addref/release
+ // mismatch coming from this system on traffic vehicles (supercam uses this on the
+ // traffic)... So this is a quick hack to solve that little problem.
+ AddDynamic( simState, (addDynaPhys? object : NULL) );
+ }
+ }
+
+ return dpList.mUseSize;
+}
+
+// Add all objects in the given sphere to the list
+int
+IntersectionList::FillIntersectionListAnimPhys( const rmt::Vector& position, float radius )
+{
+ int numAdded = FillIntersectionListFence( position, radius );
+
+ ClearAnimPhys();
+ // Iterate through the statics and grab them all
+ ReserveArray< AnimCollisionEntityDSG* > spList(200);
+ GetIntersectManager()->FindAnimPhysElems( const_cast< rmt::Vector& >( position ), radius, spList );
+ for ( int i = 0 ; i < spList.mUseSize ; i++ )
+ {
+ AnimCollisionEntityDSG* object = spList[i];
+ sim::SimStateArticulated* simState = object->GetSimState();
+ AddAnimPhys( simState );
+ }
+ numAdded += spList.mUseSize;
+
+ return numAdded;
+}
+
+bool
+IntersectionList::LineSegmentIntersection( const LineSegment2D& line1, const LineSegment2D& line2, float* t )
+{
+
+
+ float x1 = line1.p1.x;
+ float x2 = line1.p2.x;
+ float x3 = line2.p1.x;
+ float x4 = line2.p2.x;
+ float y1 = line1.p1.y;
+ float y2 = line1.p2.y;
+ float y3 = line2.p1.y;
+ float y4 = line2.p2.y;
+
+ float a_denom = (y4-y3)*(x2-x1)-(x4-x3)*(y2-y1);
+ if ( rmt::Fabs( a_denom ) < 0.001f )
+ return false;
+
+ float ua = ( (x4 - x3) * ( y1 - y3 ) - ( y4 - y3 ) * ( x1 - x3 ) ) / a_denom ;
+ if ( ua < 0 || ua > 1.0f )
+ return false;
+
+ float b_denom = (y4-y3)*(x2-x1)-(x4-x3)*(y2-y1);
+ if ( rmt::Fabs( b_denom ) < 0.001f )
+ return false;
+
+ float ub = ( (x2 - x1) * ( y1 - y3 ) - ( y2 - y1 ) * ( x1 - x3 ) ) / b_denom;
+ if ( ub < 0 || ub > 1.0f )
+ return false;
+
+ *t = ua;
+
+ return true;
+}
diff --git a/game/code/ai/actor/intersectionlist.h b/game/code/ai/actor/intersectionlist.h
new file mode 100644
index 0000000..2209a08
--- /dev/null
+++ b/game/code/ai/actor/intersectionlist.h
@@ -0,0 +1,129 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: intersectionlist
+//
+// Description: Holds a list of pointers to sim collision bounding boxes
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef INTERSECTIONLIST_H
+#define INTERSECTIONLIST_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <vector>
+#include <memory\srrmemory.h> // Needed for my STL allocations to go on the right heap
+#include <memory\map.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace sim
+{
+ class SimState;
+ class SimStateArticulated;
+ class CollisionVolume;
+ class CollisionObject;
+ class OBBoxVolume;
+}
+
+class DynaPhysDSG;
+class FenceEntityDSG;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+//
+// Constraints:
+//
+//
+//===========================================================================
+class IntersectionList
+{
+ public:
+ IntersectionList();
+ virtual ~IntersectionList();
+
+ // Clear all data from the internal arrays and decrement their refcounts
+ void Clear();
+ void ClearStatics(); // clears only the statics list
+ void ClearDynamics(); // only clears the dynamics list
+ void ClearAnimPhys(); // only clear animated phys
+ void ClearFencePieces(); // only clears the fence list
+
+ // Does the given line segment intersect with a volume?
+ // percent indicates the % distance along the line segment of the intersection
+ bool TestIntersectionStatics( const rmt::Vector& start, const rmt::Vector& end, rmt::Vector* intersection );
+ bool TestIntersectionDynamics( const rmt::Vector& start, const rmt::Vector& end, rmt::Vector* intersection, DynaPhysDSG** objectHit = NULL );
+ bool TestIntersectionAnimPhys( const rmt::Vector& start, const rmt::Vector& end, rmt::Vector* intersection );
+
+ // Performs intersection against both dynamics and statics
+ bool TestIntersection( const rmt::Vector& start, const rmt::Vector& end, rmt::Vector* intersection, float rayWidthForFences = 0.0f );
+ // Perform a line of sight test against statics and dynamics. Faster than TestIntersection
+ bool LineOfSight( const rmt::Vector& start, const rmt::Vector& end, float rayWidth = 1.0f, bool staticsOnly = false );
+
+
+ // Add objects to the internal list
+ void AddStatic( sim::SimState* );
+ void AddDynamic( sim::SimState*, DynaPhysDSG* dsg = NULL );
+ void AddFencePiece( FenceEntityDSG* );
+ void AddAnimPhys( sim::SimStateArticulated* );
+
+ // Add all objects in the given sphere to the list
+ // Returns the number of objects added to the list
+ // Note that fences are considered statics by this class
+ // so FillIntersectionListStatics also calls FillIntersectionListFence
+ int FillIntersectionListStatics( const rmt::Vector& position, float radius );
+ int FillIntersectionListFence( const rmt::Vector& position, float radius );
+ // As above for dynamics, but adds a Source dynaphys, one that will never be
+ // added to the list. Useful for kicking where the character would be returned from a query
+ int FillIntersectionListDynamics( const rmt::Vector& position, float radius, bool addDynaPhys, DynaPhysDSG* source = NULL );
+ int FillIntersectionListAnimPhys( const rmt::Vector& position, float radius );
+
+ protected:
+
+ // Arrays of pointers to dynamics and static collision volumes that will
+ // be used for intersection testing
+ std::vector< sim::CollisionObject*, s2alloc<sim::CollisionObject*> > m_StaticCollisionList;
+ typedef Map< sim::CollisionObject*, DynaPhysDSG* > DynaCollList;
+ typedef DynaCollList::iterator DynaCollListIt;
+
+ DynaCollList m_DynamicCollisionList;
+
+ std::vector< sim::CollisionObject*, s2alloc<sim::CollisionObject*> > m_AnimPhysCollisionList;
+
+ struct LineSegment2D
+ {
+ rmt::Vector2 p1, p2;
+ };
+
+ std::vector< LineSegment2D, s2alloc< LineSegment2D > > m_FenceList;
+
+ bool LineSegmentIntersection( const LineSegment2D& line1, const LineSegment2D& line2, float* t );
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow IntersectionList from being copied and assigned.
+ IntersectionList( const IntersectionList& );
+ IntersectionList& operator=( const IntersectionList& );
+};
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/projectile.cpp b/game/code/ai/actor/projectile.cpp
new file mode 100644
index 0000000..291b927
--- /dev/null
+++ b/game/code/ai/actor/projectile.cpp
@@ -0,0 +1,327 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: projectile
+//
+// Description: a weapon actor
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai/actor/projectile.h>
+#include <stateprop/statepropdata.hpp>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <atc/atcmanager.h>
+#include <ai/actor/projectiledsg.h>
+#include <render/IntersectManager/IntersectManager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+const float VEHICLE_DAMAGE_ON_PROJECTILE_HIT = 0.1f;
+const int COINS_LOST_BY_PLAYER_ON_HIT = 5;
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+float Projectile::s_Speed = 20.0f;
+const float PROJECTILE_MAX_RANGE = 25.0f;
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+Projectile::Projectile():
+mpStateProp( NULL ),
+m_WillHitStatic( false )
+{
+}
+
+Projectile::~Projectile()
+{
+ if ( mpStateProp )
+ {
+ mpStateProp->Release();
+ mpStateProp = NULL;
+ }
+}
+
+void
+Projectile::SetSpeed( float kph )
+{
+ // convert to meters / millisecond
+ s_Speed = kph * 1000.0f / ( 3600.0f * 1000.0f );
+}
+
+bool Projectile::Init( const char* statePropName, const char* instanceName )
+{
+ bool success;
+
+ CStatePropData* pStatePropData = p3d::find< CStatePropData > ( statePropName );
+ if ( pStatePropData )
+ {
+ mp_StateProp = new ProjectileDSG();
+ mp_StateProp->AddRef();
+
+ CollisionAttributes* pCollAttr = GetATCManager()->CreateCollisionAttributes( PROP_MOVEABLE, 1, 2.2425f );
+ rmt::Matrix transform;
+ transform.Identity();
+ mp_StateProp->LoadSetup( pStatePropData, 0, transform, pCollAttr, true, false );
+ mp_StateProp->SetName( instanceName );
+ mp_StateProp->EnableCollisionVolume( false );
+
+ success = true;
+ }
+ else
+ {
+ success = false;
+ }
+ return success;
+}
+
+void
+Projectile::Update( unsigned int timeInMS )
+{
+ rmt::Matrix transform;
+ GetTransform( &transform );
+ m_CurrentPosition = transform.Row(3);
+
+
+ // Lets see if we would have hit anything
+ int currState = mp_StateProp->GetState();
+ if ( currState == 0 )
+ {
+ // Test dynamics
+ FillIntersectionListDynamics( m_CurrentPosition, 10.0f );
+ rmt::Vector dynamicsIntersectionPoint;
+ DynaPhysDSG* objectHit;
+ if ( m_IntersectionList.TestIntersectionDynamics( m_PreviousPosition, m_CurrentPosition, &dynamicsIntersectionPoint, &objectHit ) )
+ {
+ if ( IsJumpingPlayer( objectHit ) == false )
+ {
+ // we hit something, make it explode at the collisionpoint
+ m_Speed = 0;
+ rmt::Vector adjustedIntersection;
+ AdjustCollisionPosition( dynamicsIntersectionPoint, m_CurrentPosition, m_PreviousPosition, &adjustedIntersection );
+ SetPosition( adjustedIntersection );
+ mp_StateProp->SetState( 1 );
+ ApplyDamage( objectHit );
+ }
+ }
+
+
+
+ if ( m_WillHitStatic )
+ {
+ // Test to see if the projectile hit the static this frame
+ // Take the dot between (Intersection - Current) and ( Intersection - Previous )
+ // If negative, collision
+ rmt::Vector toPrev = m_PreviousPosition - m_StaticIntersectionPoint;
+ rmt::Vector toCurr = m_CurrentPosition - m_StaticIntersectionPoint;
+ if ( toCurr.Dot( toPrev ) < 0 )
+ {
+ // we hit something, make it explode at the collisionpoint
+ m_Speed = 0;
+ SetPosition( m_StaticIntersectionPoint );
+ rmt::Vector adjustedIntersection;
+ AdjustCollisionPosition( m_StaticIntersectionPoint, m_CurrentPosition, m_PreviousPosition, &adjustedIntersection );
+ SetPosition( adjustedIntersection );
+ mp_StateProp->SetState( 1 );
+ }
+ }
+ m_IntersectionList.ClearDynamics();
+
+ {
+ // Keep flying
+ rmt::Vector velocityVectory = transform.Row(2) * m_Speed;
+ rmt::Vector position;
+ GetPosition( &position );
+ position = position + velocityVectory * static_cast< float >( timeInMS );
+ SetPosition( position );
+ }
+ }
+
+
+ m_PreviousPosition = m_CurrentPosition;
+}
+
+
+
+
+// Can't see a use for projectile behaviours at this point
+void Projectile::AddBehaviour( Behaviour* )
+{
+
+}
+
+void Projectile::Fire()
+{
+ m_Speed = s_Speed;
+}
+
+
+void Projectile::CalculateIntersections()
+{
+
+ // Calculate the static intersection
+ rmt::Matrix transform;
+ GetTransform( &transform );
+ const rmt::Vector& position = transform.Row(3);
+
+ // Lets query all objects around the projectile and see if it will (eventually)
+ // hit a static object
+ FillIntersectionListStatics( position, PROJECTILE_MAX_RANGE );
+
+ rmt::Vector destination;
+ destination = position + PROJECTILE_MAX_RANGE * transform.Row(2);
+ m_WillHitStatic = m_IntersectionList.TestIntersectionStatics( position, destination, &m_StaticIntersectionPoint );
+
+ // No use for the static list anymore, ditch it
+ m_IntersectionList.ClearStatics();
+}
+
+
+void Projectile::AdjustCollisionPosition( const rmt::Vector& intersection,
+ const rmt::Vector& currPos,
+ const rmt::Vector& prevPos,
+ rmt::Vector* newIntersection )
+{
+ // ToIntersection
+ if( currPos != prevPos )
+ {
+ rmt::Vector toIntersection = currPos - prevPos;
+ toIntersection.Normalize();
+ *newIntersection = intersection + toIntersection * 0.75f;
+ }
+ else
+ {
+ *newIntersection = intersection;
+ }
+}
+
+void Projectile::ApplyDamage( DynaPhysDSG* object )
+{
+ // Determine the type of the object and perform an action depending
+ // upon the AI ref
+ switch (object->GetAIRef())
+ {
+ case PhysicsAIRef::redBrickVehicle:
+ {
+ Vehicle* v = (Vehicle*)object;
+ if ( v->mVehicleType == VT_TRAFFIC )
+ {
+ v->TriggerDamage( VEHICLE_DAMAGE_ON_PROJECTILE_HIT );
+ }
+ break;
+ }
+ case PhysicsAIRef::PlayerCharacter:
+ {
+ // Make the character get shocked
+ rAssert( dynamic_cast< Character* >( static_cast< tRefCounted* >( object ) ) );
+ Character* character = static_cast< Character* >( object );
+ rmt::Vector characterPosition;
+ character->GetPosition( &characterPosition );
+ GetCoinManager()->LoseCoins( COINS_LOST_BY_PLAYER_ON_HIT , &characterPosition );
+ character->Shock( 0.5f );
+ break;
+ }
+ case PhysicsAIRef::NPCharacter:
+ {
+ // Make the character get shocked
+ rAssert( dynamic_cast< Character* >( static_cast< tRefCounted* >( object ) ) );
+ Character* character = static_cast< Character* >( object );
+
+ // Lets try a little velocity change to get the peds moving a bit after they get
+ // shot
+ sim::SimState* pSimState = character->GetSimState();
+ // Modifying the velocity will be done using a non-const reference to
+ // the velocity vector
+ rmt::Vector& rVelocity = pSimState->GetLinearVelocity();
+ // Apply delta velocity
+ // Lets play with the direction the NPC will fly to
+
+ rmt::Vector direction( 0, 0.707f, 0.707f );
+ rmt::Matrix transform;
+ GetTransform( &transform );
+ transform.RotateVector( direction, &direction );
+
+ float deltaV = 10.0f;
+ rVelocity += (direction * deltaV);
+ // Make it interact with the world
+ character->AddToSimulation();
+
+ break;
+ }
+ case PhysicsAIRef::StateProp:
+ {
+ // Earl wants these things to ignore vending machines and crates
+ const char* ignore_list[] =
+ {
+ "l1_crate", "l2_crate", "l3_crate", "l4_crate", "l5_crate", "l6_crate", "l7_crate",
+ "l1_vending", "l2_vending", "l3_vending", "l4_vending", "l5_vending", "l6_vending", "l7_vending"
+ };
+ const int num_items_to_ignore = sizeof( ignore_list ) / sizeof( ignore_list[0] );
+
+ StatePropDSG* spdsg = static_cast< StatePropDSG* >( object );
+ tUID uid = spdsg->GetStatePropUID();
+
+
+ bool isSpecialObject = false;
+ for ( int i = 0 ; i < num_items_to_ignore ; i++ )
+ {
+ if( tName::MakeUID( ignore_list[i] ) == uid )
+ {
+ // A special object, do nothing
+ isSpecialObject = true;
+ break;
+ }
+ }
+ if ( isSpecialObject == false )
+ {
+ rmt::Vector direction( 0, 0.707f, 0.707f );
+ rmt::Matrix transform;
+ GetTransform( &transform );
+ transform.RotateVector( direction, &direction );
+ object->ApplyForce( direction, 400.0f );
+ }
+ break;
+ }
+ break;
+ default:
+ {
+ rmt::Vector direction( 0, 0.707f, 0.707f );
+ rmt::Matrix transform;
+ GetTransform( &transform );
+ transform.RotateVector( direction, &direction );
+ object->ApplyForce( direction, 400.0f );
+ }
+ break;
+ }
+}
+
+bool Projectile::IsJumpingPlayer( DynaPhysDSG* object )
+{
+ bool jumpingCharacter = false;
+ if ( object->GetAIRef() == PhysicsAIRef::PlayerCharacter )
+ {
+ Character* character = static_cast< Character* >( object );
+ if ( character->IsJumping() )
+ jumpingCharacter = true;
+ }
+ return jumpingCharacter;
+}
+
+
diff --git a/game/code/ai/actor/projectile.h b/game/code/ai/actor/projectile.h
new file mode 100644
index 0000000..474763e
--- /dev/null
+++ b/game/code/ai/actor/projectile.h
@@ -0,0 +1,107 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: projectile
+//
+// Description: a weapon actor
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef PROJECTILE_H
+#define PROJECTILE_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <p3d/refcounted.hpp>
+#include <render/DSG/StatePropDSG.h>
+#include <ai/actor/actor.h>
+#include <ai/actor/intersectionlist.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class Behaviour;
+class StatePropDSG;
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Projectiles - weapons such as energy bolts fired from wasps
+//
+// Constraints:
+//
+//
+//===========================================================================
+class Projectile : public Actor
+{
+ public:
+ Projectile();
+ virtual ~Projectile();
+
+ static void SetSpeed( float kph );
+
+ virtual bool Init( const char* statePropname, const char* instanceName );
+ virtual void Update( unsigned int timeInMS );
+ virtual void AddBehaviour( Behaviour* );
+ // Fixed path weapon, no turning in flight
+ virtual void LookAt( const rmt::Vector&, unsigned int timeInMS ){}
+ // Fixed path weapon, no turning in flight
+ virtual void SetRotationSpeed( float degreesPerSecond ) {}
+ virtual void ReleaseBehaviours(){}
+ virtual void MoveTo( const rmt::Vector& destination, float speed ){}
+ void Fire();
+ void CalculateIntersections();
+
+ protected:
+
+ // Virtually all collision volumes stick out over the actual geometry
+ // this function pushes a given intersection into the object more
+ void AdjustCollisionPosition( const rmt::Vector& intersection,
+ const rmt::Vector& currPos,
+ const rmt::Vector& prevPos,
+ rmt::Vector* newIntersection );
+
+
+ void ApplyDamage( DynaPhysDSG* object );
+ // Joes test, is the given object a player thats currently jumping??
+ bool IsJumpingPlayer( DynaPhysDSG* object );
+
+ CStateProp* mpStateProp;
+
+ static float s_Speed;
+ float m_Speed;
+ rmt::Vector m_CurrentPosition;
+
+
+ bool m_WillHitStatic;
+ rmt::Vector m_StaticIntersectionPoint;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow Actor from being copied and assigned.
+ Projectile( const Projectile& );
+ Projectile& operator=( const Projectile& );
+
+
+};
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/projectiledsg.cpp b/game/code/ai/actor/projectiledsg.cpp
new file mode 100644
index 0000000..bfd2632
--- /dev/null
+++ b/game/code/ai/actor/projectiledsg.cpp
@@ -0,0 +1,63 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ProjectileDSG
+//
+// Description: ProjectileDSG is a statepropdsg with one minor difference
+// upon contact with an object in prereact, it destroys the other
+// object
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai/actor/projectiledsg.h>
+
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+ProjectileDSG::ProjectileDSG():
+mHasHit( false )
+{
+
+}
+
+ProjectileDSG::~ProjectileDSG()
+{
+
+}
+
+
+
+sim::Solving_Answer
+ProjectileDSG::PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+{
+
+ return sim::Solving_Aborted;
+}
+
+
+
+sim::Solving_Answer
+ProjectileDSG::PostReactToCollision( rmt::Vector& impulse, sim::Collision& inCollision )
+{
+ // No point in computing expensive sim calculations, abort immediately
+ return sim::Solving_Aborted;
+} \ No newline at end of file
diff --git a/game/code/ai/actor/projectiledsg.h b/game/code/ai/actor/projectiledsg.h
new file mode 100644
index 0000000..f762ed2
--- /dev/null
+++ b/game/code/ai/actor/projectiledsg.h
@@ -0,0 +1,85 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ProjectileDSG
+//
+// Description: ProjectileDSG is a statepropdsg with one minor difference
+// upon contact with an object in prereact, it destroys the other
+// object
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef PROJECTILEDSG_H
+#define PROJECTILEDSG_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai/actor/actordsg.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Exactly the same as a StatePropDSG, but with the ability to
+// destroy the object it touches in PreReactToCollision
+//
+// Constraints:
+//
+//
+//===========================================================================
+class ProjectileDSG : public ActorDSG
+{
+ public:
+ ProjectileDSG();
+ virtual ~ProjectileDSG();
+ virtual sim::Solving_Answer PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision );
+ virtual sim::Solving_Answer PostReactToCollision( rmt::Vector& impulse, sim::Collision& inCollision );
+
+ bool WasHit()const{ return mHasHit; }
+ void SetWasHit( bool wasHit ){ mHasHit = wasHit; }
+
+
+
+ protected:
+
+ bool mHasHit : 1;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow XxxClassName from being copied and assigned.
+ ProjectileDSG( const ProjectileDSG& );
+ ProjectileDSG& operator=( const ProjectileDSG& );
+
+
+};
+
+
+
+
+
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/spawnpoint.cpp b/game/code/ai/actor/spawnpoint.cpp
new file mode 100644
index 0000000..2760e04
--- /dev/null
+++ b/game/code/ai/actor/spawnpoint.cpp
@@ -0,0 +1,164 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Spawnpoint
+//
+// Description:
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai/actor/spawnpoint.h>
+#include <memory/srrmemory.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/triggervolumetracker.h>
+#include <ai/actor/actormanager.h>
+#include <ai/actor/flyingactor.h>
+#include <memory/srrmemory.h>
+#include <events/eventmanager.h>
+#include <data/PersistentWorldManager.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+SpawnPoint::SpawnPoint( const char* spawnPointName , const char* statePropName, const rmt::Sphere& sphere, unsigned int timeOut )
+: m_TimeActorDestroyed( 0 ),
+m_TimeOutPeriod( timeOut * 1000 ),
+m_WasDestroyed( false )
+{
+ m_Behaviours.Allocate( 6 );
+
+ mPersistentObjectID = GetPersistentWorldManager()->GetPersistentObjectID();
+ if( mPersistentObjectID < -1 )
+ {
+ m_WasDestroyed = true;
+ return;
+ }
+
+
+ m_StatePropName = tName::MakeUID( statePropName );
+ m_SpawnPointName = tName::MakeUID( spawnPointName );
+ m_Sphere = sphere;
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ // Create a new triggervolume and add it to the tracker
+ m_TriggerVolume = new SphereTriggerVolume( sphere.centre, sphere.radius );
+
+ SetNumTriggers( 1, HeapMgr()->GetCurrentAllocator() );
+
+ m_TriggerVolume->SetLocator( this );
+
+ // Make it active
+ GetTriggerVolumeTracker()->AddTrigger( m_TriggerVolume );
+ AddTriggerVolume( m_TriggerVolume );
+
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+ GetEventManager()->AddListener( this, EVENT_WASP_BLOWED_UP );
+}
+
+SpawnPoint::~SpawnPoint()
+{
+ if ( m_TriggerVolume != NULL )
+ {
+ GetTriggerVolumeTracker()->RemoveTrigger( m_TriggerVolume );
+ m_TriggerVolume = NULL;
+ }
+ for ( int i = 0 ; i < m_Behaviours.mUseSize ; i++ )
+ {
+ m_Behaviours[ i ]->Release();
+ }
+
+ if ( m_Behaviours.mUseSize > 0 )
+ m_Behaviours.ClearUse();
+
+ GetEventManager()->RemoveListener( this, EVENT_WASP_BLOWED_UP );
+}
+
+
+void
+SpawnPoint::AddBehaviour( Behaviour* behaviour )
+{
+ behaviour->AddRef();
+ m_Behaviours.Add( behaviour );
+}
+
+void
+SpawnPoint::OnTrigger( unsigned int playerID )
+{
+ // Create a new actor with the behaviours attached to this spawn point and add it to the actormanager
+ if ( GetActorManager()->GetActorByUID( m_SpawnPointName ) == NULL
+ && CanRespawn() )
+ {
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( m_Sphere.centre );
+ Actor* actor = GetActorManager()->CreateActor( m_StatePropName, m_SpawnPointName, transform );
+ // Copy behaviours over
+
+ if ( actor != NULL )
+ {
+ for ( int i = 0 ; i < m_Behaviours.mUseSize ; i++ )
+ {
+ actor->AddBehaviour( m_Behaviours[ i ] );
+ }
+ actor->Activate();
+ }
+
+
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+ }
+}
+
+bool
+SpawnPoint::CanRespawn()const
+{
+ return m_WasDestroyed == false;
+}
+
+
+void
+SpawnPoint::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_WASP_BLOWED_UP:
+ {
+ // Check to see if the wasp that was destroyed was the
+ // wasp associated with this spawn point
+ Actor* actor = reinterpret_cast< FlyingActor* >( pEventData );
+ if ( actor->GetUID() == m_SpawnPointName )
+ {
+ // The actor associated with this spawn point was destroyed
+ // remember the current time as the start point for our timeout counter
+ m_TimeActorDestroyed = radTimeGetMilliseconds();
+ m_WasDestroyed = true;
+ GetPersistentWorldManager()->OnObjectBreak( mPersistentObjectID );
+ }
+
+ }
+ break;
+ default:
+ break;
+ }
+}
+
diff --git a/game/code/ai/actor/spawnpoint.h b/game/code/ai/actor/spawnpoint.h
new file mode 100644
index 0000000..aa1b81e
--- /dev/null
+++ b/game/code/ai/actor/spawnpoint.h
@@ -0,0 +1,96 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Spawnpoint
+//
+// Description: Describes a place where actors can get spawned
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef SPAWNPOINT_H
+#define SPAWNPOINT_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <meta/triggerlocator.h>
+#include <render/culling/swaparray.h>
+#include <events/eventlistener.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class Behaviour;
+class TriggerVolume;
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Represents a spawn point in the world, essentially a trigger volume
+// that when entered, spawns a new actor with the given properties
+//
+// Constraints:
+// Each spawn point will only ever spawn one actor at a time
+//
+//===========================================================================
+class SpawnPoint : public TriggerLocator, public EventListener
+{
+ public:
+ SpawnPoint( const char* spawnPointName, const char* statePropName, const rmt::Sphere& sphere, unsigned int timeOut );
+ ~SpawnPoint();
+
+ tUID GetUID()const { return m_SpawnPointName; }
+ void AddBehaviour( Behaviour* );
+
+ virtual LocatorType::Type GetDataType() const { return LocatorType::SPAWN_POINT; };
+
+ // Can the spawn point respawn a new actor? Or has there
+ // not been enough time since the last respawn
+ bool CanRespawn()const;
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ protected:
+
+ rmt::Sphere m_Sphere;
+ unsigned int m_SpawnTimeout;
+ TriggerVolume* m_TriggerVolume;
+ tUID m_StatePropName;
+ tUID m_SpawnPointName;
+
+ unsigned int m_TimeActorDestroyed;
+ unsigned int m_TimeOutPeriod;
+ bool m_WasDestroyed;
+ short mPersistentObjectID;
+
+ virtual void OnTrigger( unsigned int playerID );
+
+ // List of behaviours that can be attached to a spawned object
+
+
+ private:
+
+ SwapArray< Behaviour* > m_Behaviours;
+
+};
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/stunnedbehaviour.cpp b/game/code/ai/actor/stunnedbehaviour.cpp
new file mode 100644
index 0000000..c223bda
--- /dev/null
+++ b/game/code/ai/actor/stunnedbehaviour.cpp
@@ -0,0 +1,28 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: StunnedBehaviour
+//
+// Description: Handles being stunned
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+#include <ai\actor\stunnedbehaviour.h>
+
+StunnedBehaviour::StunnedBehaviour( float stunTime )
+{
+ m_StunTime = stunTime / 1000.0f;
+}
+
+StunnedBehaviour::~StunnedBehaviour()
+{
+
+}
+
+void
+StunnedBehaviour::Apply( Actor* actor, unsigned int timeInMS )
+{
+
+} \ No newline at end of file
diff --git a/game/code/ai/actor/stunnedbehaviour.h b/game/code/ai/actor/stunnedbehaviour.h
new file mode 100644
index 0000000..684339f
--- /dev/null
+++ b/game/code/ai/actor/stunnedbehaviour.h
@@ -0,0 +1,72 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: StunnedBehaviour
+//
+// Description: Handles being stunned
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef STUNNEDBEHAVIOUR_H
+#define STUNNEDBEHAVIOUR_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai/actor/behaviour.h>
+
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+//
+// Constraints:
+//
+//===========================================================================
+class StunnedBehaviour : public Behaviour
+{
+ public:
+ // Ctor - stun time in seconds
+ StunnedBehaviour( float stunTime );
+ virtual ~StunnedBehaviour();
+ virtual void Apply( Actor*, unsigned int timeInMS );
+
+ protected:
+
+ // Time to be stunned in milliseconds
+ float m_StunTime;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow StunnedBehaviour from being copied and assigned.
+ StunnedBehaviour( const StunnedBehaviour& );
+ StunnedBehaviour& operator=( const StunnedBehaviour& );
+
+ private:
+
+
+};
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/ufoattackbehaviour.cpp b/game/code/ai/actor/ufoattackbehaviour.cpp
new file mode 100644
index 0000000..047e1c1
--- /dev/null
+++ b/game/code/ai/actor/ufoattackbehaviour.cpp
@@ -0,0 +1,308 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component:
+//
+// Description:
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai/actor/UFOAttackBehaviour.h>
+#include <ai/actor/actor.h>
+#include <worldsim/avatarmanager.h>
+#include <math.h>
+#include <ai/actor/actormanager.h>
+#include <constants/actorenum.h>
+#include <render/IntersectManager/IntersectManager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/vehiclecentral.h>
+#include <ai/actor/actordsg.h>
+#include <p3d/entity.hpp>
+#include <ai/actor/flyingactor.h>
+#include <worldsim/character/charactermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <render/rendermanager/rendermanager.h>
+#include <main/game.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+// Designer tuning variables
+
+#ifndef RAD_RELEASE
+ static float s_DesignerTunableMaxUFOFiringRange = 20.0f;
+#endif
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+rmt::Randomizer UFOAttackBehaviour::s_Randomizer(0);
+bool UFOAttackBehaviour::s_RandomizerSeeded = false;
+
+
+const float UFO_MOVEMENT_SPEED = 0.005f;
+const float UFO_HEIGHT_ABOVE_GROUND = 20.0f;
+const float UFO_CRUISE_HEIGHT = 25.0f;
+
+const int TRACTOR_BEAM_STATE_ACTIVATE = 0;
+const int TRACTOR_BEAM_STATE_DEACTIVATE = 1;
+const int TRACTOR_BEAM_STATE_IDLE_ON = 2;
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+UFOAttackBehaviour::UFOAttackBehaviour( float maxFiringRange ):
+m_TargetList( 50 ),
+m_CurrentTarget( 0 ),
+m_CurrentState( eSearching )
+{
+ const float DEFAULT_MOVE_SPEED = 20.0f;
+
+ SetMaxFiringRange( maxFiringRange );
+ SetActorMoveSpeed( DEFAULT_MOVE_SPEED );
+ // Try and find the stateprop in the inventory section
+
+ if (!s_RandomizerSeeded)
+ {
+ s_Randomizer.Seed (Game::GetRandomSeed ());
+ s_RandomizerSeeded = true;
+ }
+}
+
+UFOAttackBehaviour::~UFOAttackBehaviour()
+{
+}
+
+void
+UFOAttackBehaviour::Apply( Actor* actor, unsigned int timeInMS )
+{
+ // Force update of designer tunable parameters
+#ifndef RAD_RELEASE
+
+ static bool addedDesignerVariablesToWatcher = false;
+ if ( addedDesignerVariablesToWatcher == false )
+ {
+ radDbgWatchAddFloat( &s_DesignerTunableMaxUFOFiringRange, "Max firing range (meters)", "Actor", NULL,NULL,1, 75 );
+ addedDesignerVariablesToWatcher = true;
+ }
+ SetMaxFiringRange( s_DesignerTunableMaxUFOFiringRange );
+#endif
+
+ // We know that the ufo is a flying actor
+ rAssert( dynamic_cast< FlyingActor* >( actor ) != NULL );
+ FlyingActor* flyingActor = static_cast< FlyingActor* >( actor );
+ flyingActor->SetDesiredHeight( UFO_HEIGHT_ABOVE_GROUND );
+ flyingActor->SetDesiredHeightEnabled( true );
+
+ rmt::Vector actorPosition;
+ actor->GetPosition( &actorPosition );
+
+ if ( actor->IsTractorBeamOn() )
+ {
+
+ // Tractor beam is on,
+ // anything under the beam is toast.
+ // fetch a list of all the targets under the beam
+ SwapArray< DynaPhysDSG* > dynaPhysList( 200 );
+ GetTargetsInTractorRange( actorPosition, UFO_HEIGHT_ABOVE_GROUND * 1.5f, &dynaPhysList );
+ // Iterate through the list and destroy them
+ int objectsLeft = 0;
+ for ( int i = 0 ; i < dynaPhysList.mUseSize ; i++ )
+ {
+ if (SuckIntoUFO( actorPosition, dynaPhysList[i], static_cast< float >( timeInMS ) ) )
+ {
+ objectsLeft++;
+ }
+ if ( ReachedTopOfTractorBeam( actorPosition, dynaPhysList[i] ) )
+ {
+ // Destroy the object, and test to see if it damaged the UFO as well
+ if ( DestroyObject( dynaPhysList[i] ) )
+ actor->SetState( 1 );
+
+ objectsLeft--;
+ }
+ }
+
+ if ( objectsLeft <= 0 )
+ {
+ actor->DeactivateTractorBeam();
+ }
+ }
+ else
+ {
+ // Do we have a target
+ // if not, get one, then start moving toward it
+ if ( m_CurrentTarget == NULL )
+ {
+ DynaPhysDSG* target = FindTarget( actorPosition, actor->GetDSG() );
+ if ( target != NULL )
+ {
+ // Lets move
+ rmt::Vector targetPosition;
+ target->GetPosition( &targetPosition );
+ targetPosition.y += UFO_HEIGHT_ABOVE_GROUND;
+
+ actor->MoveTo( targetPosition, UFO_MOVEMENT_SPEED );
+ m_CurrentTarget = target;
+ }
+ }
+ else
+ {
+ // if we do have a target
+ // check to see if we are in range
+
+ if ( WithinTractorBeamRange( actorPosition, m_CurrentTarget ) )
+ {
+ // We are within range, suck it up
+ actor->ActivateTractorBeam();
+ m_CurrentTarget = NULL;
+ }
+ else
+ {
+ rmt::Vector targetPosition;
+ m_CurrentTarget->GetPosition( &targetPosition );
+ actor->MoveTo( targetPosition, UFO_MOVEMENT_SPEED );
+ }
+
+ }
+ }
+
+
+
+}
+
+void UFOAttackBehaviour::SetMaxFiringRange( float meters )
+{
+ m_MaxFiringRange = meters;
+ m_MaxFiringRangeSqr = meters * meters;
+
+#ifndef RAD_RELEASE
+ s_DesignerTunableMaxUFOFiringRange = meters;
+#endif
+
+}
+
+
+void UFOAttackBehaviour::SetActorMoveSpeed( float kph )
+{
+ // Convert from kph to meters per millisecond
+ // Precision loss here?
+ m_Speed = kph * 1000.0f / 3600000.0f;
+
+}
+
+// Enable this behaviour
+void UFOAttackBehaviour::Activate()
+{
+ SetExclusive( false );
+}
+// Disable this behaviour
+void UFOAttackBehaviour::Deactivate()
+{
+ SetExclusive( false );
+}
+
+
+
+
+
+bool UFOAttackBehaviour::FindPositionInAttackRange( const rmt::Vector& actorPos, const rmt::Vector& targetPos, rmt::Vector* out_destination )
+{
+ bool success;
+ // Find a position close to the target, lets fudge the data a bit
+ // make it always 1 meter in ZY from the target
+
+ *out_destination = targetPos;
+ out_destination->y += 2.0f;
+ out_destination->z += 2.0f;
+ success = true;
+ return success;
+}
+
+
+void UFOAttackBehaviour::MoveIntoAttackRange( const Actor& actor, const rmt::Vector& target )
+{
+
+
+}
+
+DynaPhysDSG*
+UFOAttackBehaviour::FindTarget( const rmt::Vector& actorPosition, const ActorDSG* dsgObject )
+{
+ // Iterate through each pedestrian in the charactermanager and find the closest
+ // one
+ int i;
+ DynaPhysDSG* target = NULL;
+ float closestDistance = 50.0f * 50.0f;
+ /*
+ for ( i = 0 ; i < GetCharacterManager()->GetMaxCharacters() ; i++ )
+ {
+ Character* character = GetCharacterManager()->GetCharacter( i );
+ if ( character != NULL )
+ {
+ rmt::Vector characterPosition;
+ character->GetPosition( &characterPosition );
+ float distSqr = (characterPosition - actorPosition).MagnitudeSqr();
+ if ( distSqr < closestDistance )
+ {
+ // We want to see if there is a clear line of sight, between the UFO
+ // and the target character.
+ // LineOfSight will think that the targetposition is occulded by the
+ // target itself, so set the lineofsight target position to
+ // be raised above the character
+ rmt::Vector pointAboveCharacter = characterPosition;
+ pointAboveCharacter.y += 2.5f;
+
+ if ( GetIntersectManager()->LineOfSight( actorPosition, pointAboveCharacter, dsgObject ) )
+ {
+ closestDistance = distSqr;
+ target = character;
+ }
+ }
+ }
+ }*/
+ // Iterate through each vehicle and find a target
+ // As a test, lets just grab the players vehicle
+ int numActiveVehicles;
+ Vehicle** activeVehicleList;
+ GetVehicleCentral()->GetActiveVehicleList( activeVehicleList, numActiveVehicles );
+ for ( i = 0 ; i < numActiveVehicles ; i++ )
+ {
+ if ( activeVehicleList[i] != NULL )
+ {
+ Vehicle* activeVehicle = activeVehicleList[i];
+
+ if ( activeVehicle->IsVehicleDestroyed() == false )
+ {
+ rmt::Vector vehiclePosition;
+ activeVehicle->GetPosition( &vehiclePosition );
+ float distToVehicleSqr = ( vehiclePosition - actorPosition ).MagnitudeSqr();
+ if ( distToVehicleSqr < closestDistance )
+ {
+ closestDistance = distToVehicleSqr;
+ target = activeVehicle;
+ }
+ }
+ }
+ }
+
+
+ return target;
+}
+
+
+
+
+
diff --git a/game/code/ai/actor/ufoattackbehaviour.h b/game/code/ai/actor/ufoattackbehaviour.h
new file mode 100644
index 0000000..744a6ab
--- /dev/null
+++ b/game/code/ai/actor/ufoattackbehaviour.h
@@ -0,0 +1,105 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: UFOAttackBehaviour
+//
+// Description: Atta
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef UFOATTACKBEHAVIOUR_H
+#define UFOATTACKBEHAVIOUR_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai/actor/ufobehaviour.h>
+#include <radmath/random.hpp>
+#include <render/culling/SwapArray.h>
+#include <stateprop/stateprop.hpp>
+#include <render/DSG/DynaPhysDSG.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class ActorDSG;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+
+class UFOAttackBehaviour : public UFOBehaviour
+{
+ public:
+ // maximum firing range in meters
+ UFOAttackBehaviour( float maxFiringRange );
+ virtual ~UFOAttackBehaviour();
+ virtual void Apply( Actor*, unsigned int timeInMS );
+
+ void SetMaxFiringRange( float meters );
+ void SetActorMoveSpeed( float kph );
+
+ // Enable this behaviour
+ virtual void Activate();
+ // Disable this behaviour
+ virtual void Deactivate();
+
+ enum State
+ {
+ eSearching,
+ eAttacking,
+ eUsingTractorBeam,
+ eDamaged,
+ eDestroyed
+ };
+
+
+ protected:
+
+ bool FindPositionInAttackRange( const rmt::Vector& actorPos, const rmt::Vector& targetPos, rmt::Vector* out_destination );
+ bool WithinFiringRange( const rmt::Vector& actorPos, const rmt::Vector& target )const;
+ void MoveIntoAttackRange( const Actor& actor, const rmt::Vector& target );
+ // Searches the DSG for available targets
+ int ScanForTargets( const rmt::Vector& actorPosition, float maxRangeSqr, SwapArray< DynaPhysDSG* >* pTargetList );
+
+ // Finds a new target (currently just peds) thats closet to the
+ // given actor position
+ DynaPhysDSG* FindTarget( const rmt::Vector& actorPosition, const ActorDSG* dsg );
+
+
+
+
+ float m_MaxFiringRange; // in meters
+ float m_MaxFiringRangeSqr;
+ float m_Speed; // meters per milliseconds
+
+ SwapArray< DynaPhysDSG* > m_TargetList;
+ DynaPhysDSG* m_CurrentTarget;
+ State m_CurrentState;
+
+ static rmt::Randomizer s_Randomizer;
+ static bool s_RandomizerSeeded;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow UFOAttackBehaviour from being copied and assigned.
+ UFOAttackBehaviour( const UFOAttackBehaviour& );
+ UFOAttackBehaviour& operator=( const UFOAttackBehaviour& );
+};
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/ufobeamalwaysonbehaviour.cpp b/game/code/ai/actor/ufobeamalwaysonbehaviour.cpp
new file mode 100644
index 0000000..e745448
--- /dev/null
+++ b/game/code/ai/actor/ufobeamalwaysonbehaviour.cpp
@@ -0,0 +1,97 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ufobeamalwayson
+//
+// Description: code for the ai (if you can call it that for l7m3)
+// UFO stays in one spot and its beam is always on
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai\actor\ufobeamalwaysonbehaviour.h>
+#include <ai\actor\actor.h>
+#include <ai\actor\flyingactor.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+const float UFO_ROT_SPEED = 0.001f;
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+
+UFOBeamAlwaysOn::UFOBeamAlwaysOn()
+{
+
+}
+
+UFOBeamAlwaysOn::~UFOBeamAlwaysOn()
+{
+
+}
+
+void
+UFOBeamAlwaysOn::Apply( Actor* actor, unsigned int timeInMS )
+{
+ if ( actor->IsTractorBeamOn() == false )
+ {
+ actor->ActivateTractorBeam();
+ }
+ FlyingActor* flyingActor = static_cast< FlyingActor* >( actor );
+ flyingActor->SetDesiredHeightEnabled( true );
+
+
+ m_Rotation += static_cast< float >( timeInMS ) * UFO_ROT_SPEED;
+ rmt::Matrix transform;
+ actor->GetTransform( &transform );
+ transform.FillRotateY( m_Rotation );
+ actor->SetTransform( transform );
+
+ const float UFO_HEIGHT_ABOVE_GROUND = 20.0f;
+ flyingActor->SetDesiredHeight( UFO_HEIGHT_ABOVE_GROUND );
+
+ if ( actor->IsTractorBeamOn() )
+ {
+
+ // Tractor beam is on,
+ // anything under the beam is toast.
+ // fetch a list of all the targets under the beam
+ SwapArray< DynaPhysDSG* > dynaPhysList( 20 );
+ rmt::Vector actorPosition;
+ actor->GetPosition( &actorPosition );
+ GetTargetsInTractorRange( actorPosition, UFO_HEIGHT_ABOVE_GROUND * 1.5f, &dynaPhysList );
+ // Iterate through the list and destroy them
+ int objectsLeft = 0;
+ for ( int i = 0 ; i < dynaPhysList.mUseSize ; i++ )
+ {
+ if (SuckIntoUFO( actorPosition, dynaPhysList[i], static_cast< float >( timeInMS ) ) )
+ {
+ objectsLeft++;
+ }
+ if ( ReachedTopOfTractorBeam( actorPosition, dynaPhysList[i] ) )
+ {
+ // Destroy the object, and test to see if it damaged the UFO as well
+ if ( DestroyObject( dynaPhysList[i] ) )
+ actor->SetState( 1 );
+
+ objectsLeft--;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/game/code/ai/actor/ufobeamalwaysonbehaviour.h b/game/code/ai/actor/ufobeamalwaysonbehaviour.h
new file mode 100644
index 0000000..5e41469
--- /dev/null
+++ b/game/code/ai/actor/ufobeamalwaysonbehaviour.h
@@ -0,0 +1,70 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ufobeamalwayson
+//
+// Description: code for the ai (if you can call it that for l7m3)
+// UFO stays in one spot and its beam is always on
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef UFOBEAMONBEHAVIOUR_H
+#define UFOBEAMONBEHAVIOUR_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai\actor\ufobehaviour.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class Actor;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Actor never moves. Beam is on
+//
+// Constraints:
+//
+//
+//===========================================================================
+class UFOBeamAlwaysOn : public UFOBehaviour
+{
+ public:
+ UFOBeamAlwaysOn();
+ virtual ~UFOBeamAlwaysOn();
+
+ virtual void Apply( Actor*, unsigned int timeInMS );
+
+ protected:
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow UFOBeamAlwaysOn from being copied and assigned.
+ UFOBeamAlwaysOn( const UFOBeamAlwaysOn& );
+ UFOBeamAlwaysOn& operator=( const UFOBeamAlwaysOn& );
+
+
+};
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/actor/ufobehaviour.cpp b/game/code/ai/actor/ufobehaviour.cpp
new file mode 100644
index 0000000..ef8defa
--- /dev/null
+++ b/game/code/ai/actor/ufobehaviour.cpp
@@ -0,0 +1,377 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ufobehaviour
+//
+// Description:
+//
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <ai\actor\ufobehaviour.h>
+#include <ai\actor\actor.h>
+#include <ai\actor\flyingactor.h>
+#include <worldsim\redbrick\vehicle.h>
+#include <worldsim\vehiclecentral.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <render/rendermanager/rendermanager.h>
+#include <meta/eventlocator.h>
+#include <worldsim/character/character.h>
+#include <render/breakables/breakablesmanager.h>
+#include <radtime.hpp>
+#include <worldsim/avatarmanager.h>
+#include <mission/statepropcollectible.h>
+#include <camera/supercammanager.h>
+#include <mission/gameplaymanager.h>
+#include <camera/supercamcentral.h>
+#include <ai/state.h>
+#include <ai/statemanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactercontroller.h>
+#include <worldsim/character/charactermanager.h>
+#include <ai/sequencer/action.h>
+#include <ai/sequencer/actioncontroller.h>
+#include <ai/sequencer/sequencer.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+// How fast objects move up the beam
+const float TRACTOR_BEAM_SUCK_SPEED = 0.003f;
+// How close an object being sucked up has to be to the center before it gets sucked up forever
+const float UFO_TRACTOR_BEAM_DISAPPEAR_RADIUS_SQR = 25.0f;
+
+const float UFO_TRACTOR_BEAM_RADIUS = 7.5f;
+const float UFO_TRACTOR_BEAM_RADIUS_SQR = UFO_TRACTOR_BEAM_RADIUS * UFO_TRACTOR_BEAM_RADIUS;
+
+// The cosine of the angle of the tractor beam
+const float TRACTOR_BEAM_ANGLE_COS = 0.94f;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+
+UFOBehaviour::UFOBehaviour() :
+m_Rotation( 0 )
+{
+ // Lets just make a default respawn position until the designers script one in.
+ m_RespawnLocator = new EventLocator();
+ m_RespawnLocator->AddRef();
+}
+
+UFOBehaviour::~UFOBehaviour()
+{
+ if ( m_RespawnLocator != NULL )
+ {
+ m_RespawnLocator->Release();
+ m_RespawnLocator = NULL;
+ }
+}
+
+void
+UFOBehaviour::Apply( Actor* actor, unsigned int timeInMS )
+{
+
+}
+
+void
+UFOBehaviour::SetCharacterRespawnPosition( EventLocator* locator )
+{
+ rmt::Vector respawnLocation;
+ locator->GetLocation( &respawnLocation );
+ rmt::Matrix matrix;
+ m_RespawnLocator->SetMatrix( locator->GetMatrix() );
+ m_RespawnLocator->SetLocation( respawnLocation );
+ m_RespawnLocator->SetEventType( LocatorEvent::DEATH );
+}
+
+bool
+UFOBehaviour::WithinTractorBeamRange( const rmt::Vector& position, IEntityDSG* dsg )const
+{
+ if ( dsg == NULL )
+ return false;
+
+ bool withinRange;
+
+ rmt::Vector targetPos;
+ dsg->GetPosition( &targetPos );
+
+ // Compare distance in x and z only
+ float deltaX = targetPos.x - position.x;
+ float deltaZ = targetPos.z - position.z;
+ float distSqr = (deltaX*deltaX) + (deltaZ*deltaZ);
+ if ( distSqr < UFO_TRACTOR_BEAM_RADIUS_SQR )
+ {
+ withinRange = true;
+ }
+ else
+ {
+ withinRange = false;
+ }
+ return withinRange;
+}
+
+
+bool
+UFOBehaviour::SuckIntoUFO( const rmt::Vector& actorPosition, DynaPhysDSG* pDSG, float timeInMS )
+{
+ bool wasSucked;
+
+
+
+ sim::SimState* simstate = pDSG->GetSimState();
+ if ( simstate == NULL )
+ return false;
+
+ const rmt::Matrix& transform = simstate->GetTransform();
+ const rmt::Vector& objectPos = transform.Row(3);
+ rmt::Vector toTopOfBeam = actorPosition - objectPos;
+ toTopOfBeam.Normalize();
+
+ // Move position upwards
+ rmt::Vector velocityVector = timeInMS * TRACTOR_BEAM_SUCK_SPEED * toTopOfBeam;
+
+
+ // Apply a rotation
+ // rmt::Vector rotationAxis = GetPseudoRandomRotationAxis( reinterpret_cast< int >( pDSG ) );
+//
+ // rmt::Matrix rotationMatrix;
+ // rotationMatrix.Identity();
+// rotationMatrix.FillRotation( rotationAxis, timeInMS * 0.001f );
+
+ //rmt::Matrix translation;
+ //translation.Identity();
+ // translation.FillTranslate( velocityVector );
+
+ rmt::Matrix newTransform = transform;
+ newTransform.Row(3) += velocityVector;
+ // newTransform.Mult( rotationMatrix );
+
+ int aiRef = pDSG->GetAIRef();
+
+ switch (aiRef)
+ {
+ case PhysicsAIRef::redBrickVehicle:
+ {
+ rAssert( dynamic_cast< Vehicle* >( pDSG ) != NULL );
+ Vehicle* vehicle = static_cast< Vehicle* >( pDSG );
+ // Don't let the chase cars get sucked up (JeffP)
+ if ( vehicle->mVehicleID != VehicleEnum::CBLBART && vehicle->IsVehicleDestroyed() == false
+ && vehicle->mVehicleID != VehicleEnum::HUSKA )
+ {
+ pDSG->GetSimState()->ResetVelocities();
+ vehicle->SetTransform( newTransform );
+ vehicle->SetSimpleShadow( false );
+ wasSucked = true;
+ // If we are in bumper cam, we need to force out.
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC(0);
+ if(scc->GetActiveSuperCam()->GetType() == SuperCam::BUMPER_CAM)
+ {
+ // Need to switch out of bumper cam.
+ scc->SelectSuperCam( SuperCam::FAR_FOLLOW_CAM, SuperCamCentral::FORCE | SuperCamCentral::CUT, 0 );
+ }
+ if(vehicle->IsUserDrivingCar())
+ {
+ Character* c = GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter();
+ if(c)
+ {
+ c->GetActionController()->Clear();
+ c->GetStateManager()->SetState<CharacterAi::GetOut>();
+ }
+ }
+ }
+ else
+ {
+ wasSucked = false;
+ }
+
+ }
+ break;
+ default:
+ wasSucked = false;
+ break;
+ }
+
+ return wasSucked;
+}
+
+
+bool
+UFOBehaviour::ReachedTopOfTractorBeam( const rmt::Vector& actorPosition, DynaPhysDSG* pDSG )
+{
+ bool atTop;
+ rmt::Vector objectPos;
+ pDSG->GetPosition( &objectPos );
+ float distToTop = ( actorPosition - objectPos ).MagnitudeSqr();
+ if ( distToTop < UFO_TRACTOR_BEAM_DISAPPEAR_RADIUS_SQR )
+ {
+ atTop = true;
+ }
+ else
+ {
+ atTop = false;
+ }
+ return atTop;
+}
+
+bool
+UFOBehaviour::DestroyObject( DynaPhysDSG* object )
+{
+ bool damagedUFO = false;
+ // We need to destroy the object. Perform any cleanup activities required for specific vehicle
+ // type
+ int airef = object->GetAIRef();
+ switch ( airef )
+ {
+ case PhysicsAIRef::ActorStateProp:
+ break;
+ case PhysicsAIRef::PlayerCharacter:
+ {
+//- GetEventManager()->TriggerEvent( (EventEnum)(EVENT_LOCATOR + LocatorEvent::DEATH) , NULL );
+ assert(dynamic_cast<Character*>(object));
+ GetEventManager()->TriggerEvent( EVENT_ABDUCTED, static_cast<Character*>(object) );
+ if ( !GetAvatarManager()->IsAvatarGettingInOrOutOfCar( 0 ) )
+ {
+ // Lets set a default spawn position since the designers aren't here
+ SetCharacterRespawnPosition( p3d::find< EventLocator >( "Z3D1" ) );
+ // Character* character = GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter();
+ // rAssert( character!= NULL );
+ // GetAvatarManager()->PutCharacterOnGround(character, vehicle);
+ m_RespawnLocator->Trigger( 0, true );
+ }
+ }
+ break;
+ case PhysicsAIRef::redBrickPhizMoveable:
+ case PhysicsAIRef::StateProp:
+ case PhysicsAIRef::redBrickPhizMoveableAnim:
+ // Remove the dynaphys object from the worldsim
+ {
+
+
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( RenderEnums::LevelSlot ));
+ // Sanity check
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Remove( object );
+ }
+ break;
+ case PhysicsAIRef::redBrickVehicle:
+ {
+ rAssert( dynamic_cast< Vehicle* >( object ) != NULL );
+ Vehicle* vehicle = static_cast< Vehicle* >( object );
+ // Ok, there are two cases. One is that the vehicle is a player vehicle
+ // the other is that it is not.
+ // If the object is a player vehicle
+
+ bool characterAbducted = false;
+ if ( vehicle->IsUserDrivingCar() && vehicle->GetDriver() != NULL && !GetAvatarManager()->IsAvatarGettingInOrOutOfCar( 0 ))
+ {
+ // Player got sucked up, better tell, someone. ( NotAbductedCondition uses this )
+ GetEventManager()->TriggerEvent( EVENT_ABDUCTED, vehicle->GetDriver() );
+ characterAbducted = true;
+ // Lets set a default spawn position since the designers aren't here
+ SetCharacterRespawnPosition( p3d::find< EventLocator >( "Z3D1" ) );
+ Character* character = GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter();
+ rAssert( character!= NULL );
+ GetAvatarManager()->PutCharacterOnGround(character, vehicle);
+ m_RespawnLocator->Trigger( 0, true );
+ }
+ else
+ {
+ vehicle->TriggerDamage( FLT_MAX, false );
+ }
+
+ StatePropCollectible* collectible = vehicle->GetAttachedCollectible();
+ if ( collectible != NULL )
+ {
+ vehicle->DetachCollectible(rmt::Vector(0,0,0), false );
+ collectible->RemoveFromDSG();
+ // The vehicle is carrying a collectible.
+ // Well, there is only one type of collectible in the game
+ // i.e. the exploding barrels
+ // so trigger a boss damaged event
+ // later we may want to switch on collectible type
+ GetEventManager()->TriggerEvent( EVENT_BOSS_DAMAGED, NULL );
+ // Note that the FMVs show the vehicle just disappearing, not being blown
+ // up, so lets just make it go away via RemoveVehicleFromActiveList
+ GetVehicleCentral()->RemoveVehicleFromActiveList( vehicle );
+ damagedUFO = true;
+ }
+ else
+ {
+ // The vehicle is not carrying anything
+ // So the UFO ate the player's vehicle
+ // signal the destroybossobjective object that
+ // the mission has failed
+ }
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( object->rPosition() );
+ GetBreakablesManager()->Play( BreakablesEnum::eCarExplosion, transform );
+ GetEventManager()->TriggerEvent( EVENT_BIG_BOOM_SOUND, vehicle );
+
+ GetEventManager()->TriggerEvent( EVENT_BIG_BOOM_SOUND, vehicle );
+ }
+ break;
+ default:
+ break;
+ }
+ return damagedUFO;
+}
+
+
+void
+UFOBehaviour::GetTargetsInTractorRange( rmt::Vector& actorPosition, float maxRange, SwapArray< DynaPhysDSG* >* targetList )
+{
+ targetList->ClearUse();
+ ReserveArray< DynaPhysDSG* > dynaPhysList(20);
+ GetIntersectManager()->FindDynaPhysElems( actorPosition, maxRange, dynaPhysList );
+ // Copy all the characters into the array
+ for ( int i = 0 ; i < dynaPhysList.mUseSize ; i++ )
+ {
+ // Do an angle check to see that its inside the cone formed by the tractor beam
+ rmt::Vector targetPosition;
+ dynaPhysList[ i ]->GetPosition( &targetPosition );
+ rmt::Vector toTarget = targetPosition - actorPosition;
+ toTarget.Normalize();
+ // Vector straight down, which is the direction that the tractor beam
+ // will be facing
+ rmt::Vector down( 0, -1.0f, 0 );
+ float distanceToObject = toTarget.MagnitudeSqr();
+ if ( toTarget.Dot( down ) > TRACTOR_BEAM_ANGLE_COS )
+ {
+ targetList->Add( dynaPhysList[i] );
+ }
+ }
+}
+
+
+rmt::Vector
+UFOBehaviour::GetPseudoRandomRotationAxis( int hash )const
+{
+ unsigned int timeInSec = radTimeGetSeconds();
+ rmt::Vector axis;
+ axis.x = static_cast< float >( timeInSec & 0xF0 );
+ axis.y = static_cast< float >( timeInSec & 0xF00 );
+ axis.z = static_cast< float >( timeInSec & 0xF000 );
+
+ axis.x -= 8.0f;
+ axis.y -= 8.0f;
+ axis.z -= 8.0f;
+
+ axis.Normalize();
+ return axis;
+}
diff --git a/game/code/ai/actor/ufobehaviour.h b/game/code/ai/actor/ufobehaviour.h
new file mode 100644
index 0000000..c2f1428
--- /dev/null
+++ b/game/code/ai/actor/ufobehaviour.h
@@ -0,0 +1,86 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ufobeamalwayson
+//
+// Description: Generic ufo behaviours for tractor beam, target searching, etc
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef UFOBEHAVIOUR_H
+#define UFOBEHAVIOUR_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai\actor\behaviour.h>
+#include <render\DSG\DynaPhysDSG.h>
+#include <memory\map.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class EventLocator;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Actor never moves. Beam is on
+//
+// Constraints:
+//
+//
+//===========================================================================
+class UFOBehaviour : public Behaviour
+{
+ public:
+ UFOBehaviour();
+ virtual ~UFOBehaviour()=0;
+ virtual void Apply( Actor*, unsigned int timeInMS );
+
+ void SetCharacterRespawnPosition( EventLocator* locator );
+
+
+ protected:
+
+ bool WithinTractorBeamRange( const rmt::Vector& position, IEntityDSG* dsg )const;
+ // Suck an object into the UFO. returns true if object is successfully sucked
+ bool SuckIntoUFO( const rmt::Vector& actorPosition, DynaPhysDSG*, float timeInMS );
+ bool ReachedTopOfTractorBeam( const rmt::Vector& actorPosition, DynaPhysDSG* );
+ // Destroy an object in the tractor beam, returns true if the object damaged the UFO
+ bool DestroyObject( DynaPhysDSG* );
+ void GetTargetsInTractorRange( rmt::Vector& actorPosition, float maxRange, SwapArray< DynaPhysDSG* >* targetList );
+
+ // Used for getting an axis of rotation to spin
+ // an object thats being sucked up
+ rmt::Vector GetPseudoRandomRotationAxis( int hash )const;
+
+ protected:
+
+ EventLocator* m_RespawnLocator;
+ float m_Rotation;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow UFOBehaviour from being copied and assigned.
+ UFOBehaviour( const UFOBehaviour& );
+ UFOBehaviour& operator=( const UFOBehaviour& );
+};
+
+
+#endif \ No newline at end of file
diff --git a/game/code/ai/allai.cpp b/game/code/ai/allai.cpp
new file mode 100644
index 0000000..ea209fc
--- /dev/null
+++ b/game/code/ai/allai.cpp
@@ -0,0 +1,6 @@
+#include <ai/actionbuttonhandler.cpp>
+#include <ai/actionbuttonmanager.cpp>
+#include <ai/state.cpp>
+#include <ai/statemanager.cpp>
+#include <ai/playanimonce.cpp>
+#include <ai/automaticdoor.cpp> \ No newline at end of file
diff --git a/game/code/ai/automaticdoor.cpp b/game/code/ai/automaticdoor.cpp
new file mode 100644
index 0000000..3a2cf00
--- /dev/null
+++ b/game/code/ai/automaticdoor.cpp
@@ -0,0 +1,55 @@
+#include <ai/automaticdoor.h>
+#include <events/eventmanager.h>
+#include <events/eventdata.h>
+#include <p3d/anim/multicontroller.hpp>
+
+ActionButton::AutomaticDoor::AutomaticDoor( ActionEventLocator* pActionEventLocator )
+: AnimSwitch( pActionEventLocator ),
+mCharacterCount( 0 )
+{
+ SetActionButton( CharacterController::NONE );
+}
+
+ActionButton::AutomaticDoor::~AutomaticDoor()
+{
+ //
+ // Make good and sure this thing isn't going to play sound anymore
+ //
+ GetEventManager()->TriggerEvent( EVENT_STOP_ANIMATION_SOUND, this );
+}
+
+void ActionButton::AutomaticDoor::OnEnter( Character* pCharacter )
+{
+ if ( mCharacterCount == 0 )
+ {
+ tMultiController* pAnimController = GetAnimController( );
+
+ // This will start the animation.
+ //
+ SetAnimationDirection( 1.0f );
+
+ // Bit of a sanity check. Automatic doors shouldn't door crazy and start looping
+ pAnimController->SetCycleMode( FORCE_NON_CYCLIC );
+
+ if( mSoundName != NULL )
+ {
+ AnimSoundData data( mSoundName, mSettingsName );
+ GetEventManager()->TriggerEvent( EVENT_START_ANIMATION_SOUND, &data );
+ }
+ }
+ mCharacterCount++;
+}
+
+void ActionButton::AutomaticDoor::OnExit( Character* pCharacter )
+{
+ mCharacterCount--;
+ if ( mCharacterCount == 0 )
+ {
+ tMultiController* pAnimController = GetAnimController( );
+ // The last person has left the trigger volume, shut the door by reversing the
+ // animation
+ SetAnimationDirection( -1.0f );
+ }
+}
+
+
diff --git a/game/code/ai/automaticdoor.h b/game/code/ai/automaticdoor.h
new file mode 100644
index 0000000..ab5b874
--- /dev/null
+++ b/game/code/ai/automaticdoor.h
@@ -0,0 +1,85 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: AutomaticDoor
+//
+// Description: AutomaticDoor is an AnimSwitch (See actionbuttonhandler) where,
+// upon entering the associated trigger volume, the animation will
+// play forward until the end. On exiting the trigger volume, the
+// animation will play in the reverse direction
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef AUTOMATIC_H
+#define AUTOMATIC_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai/actionbuttonhandler.h>
+
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Behaves like a sliding door would
+//
+// Constraints:
+//
+//===========================================================================
+
+namespace ActionButton
+{
+
+class AutomaticDoor : public AnimSwitch
+{
+ public:
+ AutomaticDoor( ActionEventLocator* pActionEventLocator );
+ virtual ~AutomaticDoor();
+
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new AutomaticDoor( pActionEventLocator );
+ }
+
+ protected:
+
+ virtual void OnEnter( Character* pCharacter );
+ virtual void OnExit( Character* pCharacter );
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq ) { return false; }
+
+ protected:
+ // Number of characters currently residing in the trigger volume
+ int mCharacterCount;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow AutomaticDoor from being copied and assigned.
+ AutomaticDoor( const AutomaticDoor& );
+ AutomaticDoor& operator=( const AutomaticDoor& );
+
+};
+
+};
+#endif \ No newline at end of file
diff --git a/game/code/ai/playanimonce.cpp b/game/code/ai/playanimonce.cpp
new file mode 100644
index 0000000..8069f9e
--- /dev/null
+++ b/game/code/ai/playanimonce.cpp
@@ -0,0 +1,161 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: PlayAnimOnce
+//
+// Description: PlayAnimOnce is an AnimSwitch (See actionbuttonhandler) where, button
+// button press, the associated animation (AnimCollDSG object) is played
+// once, then the animation stops and the button disappears
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+#include <p3d/anim/multicontroller.hpp>
+#include <ai/playanimonce.h>
+#include <ai/sequencer/action.h>
+#include <ai/sequencer/sequencer.h>
+#include <ai/actionbuttonmanager.h>
+#include <meta/actioneventlocator.h>
+#include <meta/triggervolumetracker.h>
+#include <events/eventmanager.h>
+#include <events/eventdata.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// PlayAnimOnce::PlayAnimOnce
+//===========================================================================
+// Description:
+// PlayAnimOnce - ctor
+//
+// Constraints:
+// Needs certain things in the inventory
+//
+// Parameters:
+// ActionEventLocator, can't be NULL
+//
+// Return:
+//
+//
+//===========================================================================
+
+using namespace ActionButton;
+
+PlayAnimOnce::PlayAnimOnce( ActionEventLocator* pActionEventLocator )
+: PlayAnim( pActionEventLocator ),
+mWasPressed( false )
+{
+
+}
+
+//===========================================================================
+// PlayAnimOnce::~PlayAnimOnce
+//===========================================================================
+// Description:
+// PlayAnimOnce - dtor
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//
+//===========================================================================
+PlayAnimOnce::~PlayAnimOnce()
+{
+ //
+ // Make good and sure this thing isn't going to play sound anymore
+ //
+ GetEventManager()->TriggerEvent( EVENT_STOP_ANIMATION_SOUND, this );
+}
+
+bool PlayAnimOnce::OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+{
+ bool success;
+ if ( mWasPressed == false )
+ {
+ // Don't think, play the animation, then remove this actionbuttonhandler from the manager
+
+ // Force the animation to be NOT cyclic.
+ //
+ tMultiController* pAnimController = GetAnimController();
+
+ pAnimController->SetCycleMode( FORCE_NON_CYCLIC );
+ pAnimController->SetFrame( 0.0f );
+
+ // Action will assign this value which will start the animation.
+ //
+ Action* pAction = 0;
+ float fDirection = 1.0f;
+ pSeq->BeginSequence();
+ pAction = new AssignValueToFloat( GetAnimationDirection( ), fDirection );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+
+ // Disallow the pressing of this button ever again
+ mWasPressed = true;
+ for (unsigned int i = 0 ; i < GetActionEventLocator()->GetNumTriggers() ; i++)
+ {
+ TriggerVolume* pTriggerVolume = GetActionEventLocator()->GetTriggerVolume( i );
+ GetTriggerVolumeTracker()->RemoveTrigger( pTriggerVolume );
+ }
+ if( mSoundName != NULL )
+ {
+ AnimSoundData data( mSoundName, mSettingsName );
+
+ GetEventManager()->TriggerEvent( EVENT_START_ANIMATION_SOUND, &data );
+ }
+ success = true;
+ }
+ else
+ {
+ success = false;
+ }
+ return success;
+}
+
+//===========================================================================
+// PlayAnimOnce::PositionCharacter
+//===========================================================================
+// Description:
+// PositionCharacter
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//
+//===========================================================================
+
+void
+PlayAnimOnce::PositionCharacter( Character* pCharacter, Sequencer* pSeq )
+{
+ if ( mWasPressed == false )
+ {
+ AnimSwitch::PositionCharacter( pCharacter, pSeq );
+ }
+}
+
+
+
diff --git a/game/code/ai/playanimonce.h b/game/code/ai/playanimonce.h
new file mode 100644
index 0000000..e5d5134
--- /dev/null
+++ b/game/code/ai/playanimonce.h
@@ -0,0 +1,81 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: PlayAnimOnce
+//
+// Description: PlayAnimOnce is an AnimSwitch (See actionbuttonhandler) where, button
+// button press, the associated animation (AnimCollDSG object) is played
+// once, then the animation stops and the button disappears
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef PLAYANIMONCE_H
+#define PLAYANIMONCE_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <ai/actionbuttonhandler.h>
+
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Nearly identical to PlayAnim, PlayAnimOnce will remove the actionbuttonhandler
+// after button press, so that the user can't press it again
+//
+// Constraints:
+//
+//===========================================================================
+
+namespace ActionButton
+{
+
+class PlayAnimOnce : public PlayAnim
+{
+ public:
+ PlayAnimOnce( ActionEventLocator* pActionEventLocator );
+ virtual ~PlayAnimOnce();
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq );
+ virtual void PositionCharacter( Character* pCharacter, Sequencer* pSeq );
+
+ static ButtonHandler* NewAction( ActionEventLocator* pActionEventLocator )
+ {
+ return new PlayAnimOnce( pActionEventLocator );
+ }
+
+ protected:
+
+ bool mWasPressed : 1;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow PlayAnimOnce from being copied and assigned.
+ PlayAnimOnce( const PlayAnimOnce& );
+ PlayAnimOnce& operator=( const PlayAnimOnce& );
+
+};
+
+};
+#endif \ No newline at end of file
diff --git a/game/code/ai/sequencer/action.cpp b/game/code/ai/sequencer/action.cpp
new file mode 100644
index 0000000..eb3f5a8
--- /dev/null
+++ b/game/code/ai/sequencer/action.cpp
@@ -0,0 +1,2557 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// action.cpp
+//
+// Description: An atomic unit of execution in the Sequencer, the Action class
+// is the base class for all logical actions that operate on
+// a character for movement and animation.
+//
+// Modification History:
+// + Created Aug 14, 2001 -- Gary Keong
+// - Snapshot from Hair Club (rev 4) Owner: Laurent Ancessi
+//-----------------------------------------------------------------------------
+
+#include <ai/sequencer/action.h>
+
+#include <ai/sequencer/actioncontroller.h>
+
+// Needed for particle animation to be played upon impact after jumping
+#include <render/particles/particlemanager.h>
+
+#include <events/eventmanager.h>
+#include <presentation/presentation.h>
+#include <main/game.h>
+#include <radtime.hpp>
+
+#include <input/inputmanager.h>
+#ifdef RAD_WIN32
+#include <input/usercontrollerWin32.h>
+#else
+#include <input/usercontroller.h>
+#endif
+
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/coins/sparkle.h>
+
+#include <sound/soundmanager.h>
+
+#include <worldsim/character/character.h>
+#include <choreo/utility.hpp>
+#include <choreo/puppet.hpp>
+#include <radmath/radmath.hpp>
+#include <interiors/interiormanager.h>
+
+inline bool AnyStickMovement(Character* character, float tol = 0.0f)
+{
+#ifdef RAD_WIN32
+ float mright = character->GetController()->GetValue(CharacterController::MouseLookRight);
+ float mleft = character->GetController()->GetValue(CharacterController::MouseLookLeft);
+ float mouselook = ( mright > mleft ) ? mright : -mleft;
+#endif
+
+ return (rmt::Abs(character->GetController()->GetValue(CharacterController::LeftStickX)) > tol) ||
+ (rmt::Abs(character->GetController()->GetValue(CharacterController::LeftStickY)) > tol) ||
+#ifdef RAD_WIN32
+ (
+ GetInputManager()->GetController(0)->IsMouseLookOn() &&
+ rmt::Abs(mouselook) > tol
+ ) ||
+#endif
+ (character->GetController()->GetValue(CharacterController::DPadUp)) ||
+ (character->GetController()->GetValue(CharacterController::DPadDown)) ||
+ (character->GetController()->GetValue(CharacterController::DPadLeft)) ||
+ (character->GetController()->GetValue(CharacterController::DPadRight));
+}
+
+//---------------------------------------------------------------------
+// class Action
+//---------------------------------------------------------------------
+const int MAX_CLASS_SIZE = 128; // bytes.
+const float JUMP_ACTION_SPEED_CLAMP = 25.0f;
+FBMemoryPool Action::sMemoryPool( MAX_CLASS_SIZE, 32, GMA_LEVEL_OTHER );
+
+Action::~Action()
+{
+}
+
+bool Action::IsSlave() const
+{
+ // by default, actions are NOT slaves
+ return false;
+}
+
+void Action::WakeUp(float time)
+{
+}
+
+void Action::DoSimulation(float time)
+{
+}
+
+void Action::Update(float time)
+{
+}
+
+void Action::Clear()
+{
+}
+
+//---------------------------------------------------------------------
+// class CSlaveAction
+//---------------------------------------------------------------------
+
+bool SlaveAction::IsSlave() const
+{
+ // descendents of CSlaveAction are slaves
+ return true;
+}
+
+
+/*
+==============================================================================
+WalkerLocomotionAction::WalkerLocomotionAction
+==============================================================================
+Description: Comment
+
+Parameters: (Character* pCharacter, float duration)
+
+Return: WalkerLocomotionAction
+
+=============================================================================
+*/
+rmt::Randomizer WalkerLocomotionAction::sRandom(0);
+bool WalkerLocomotionAction::sRandomSeeded = false;
+
+WalkerLocomotionAction::WalkerLocomotionAction( Character* pCharacter ) :
+mfDesiredSpeed( 0.0f ),
+mIdleTime(0.0f),
+mNextIdle(15.0f),
+mAllowIdle(false),
+mpCharacter(pCharacter),
+mpLocomotion( 0 ),
+mpLocomotionDriver( 0 )
+{
+ mNextIdle = (sRandom.Float() * 10.0f) + 10.0f;
+
+ SwitchLocomotion();
+
+ if (!sRandomSeeded)
+ {
+ sRandom.Seed (Game::GetRandomSeed ());
+ sRandomSeeded = true;
+ }
+}
+
+WalkerLocomotionAction::~WalkerLocomotionAction( )
+{
+ tRefCounted::Release( mpLocomotion );
+ tRefCounted::Release( mpLocomotionDriver );
+}
+
+void WalkerLocomotionAction::PlayDriver( void )
+{
+ mpCharacter->GetPuppet()->PlayDriver( mpLocomotionDriver, -1.0f, false );
+}
+
+void WalkerLocomotionAction::StopDriver( void )
+{
+ mpCharacter->GetPuppet()->StopDriver( mpLocomotionDriver);
+}
+
+void WalkerLocomotionAction::SwitchLocomotion( void )
+{
+ tRefCounted::Assign(mpLocomotion, choreo::find<choreo::Locomotion>( mpCharacter->GetPuppet()->GetBank(), tEntity::MakeUID("walkerLoco")));
+ rAssert(mpLocomotion );
+
+ tRefCounted::Assign(mpLocomotionDriver, mpLocomotion->NewLocomotionDriver());
+ rAssert(mpLocomotionDriver);
+}
+
+/*
+==============================================================================
+WalkerLocomotionAction::SolveActualDir
+==============================================================================
+Description: Comment
+
+Parameters: ( float fDesiredDir, float timeins )
+
+Return: float
+
+=============================================================================
+*/
+float WalkerLocomotionAction::SolveActualDir( float fDesiredDir, float timeins )
+{
+ // Damping function.
+ //
+ float rotateDelta = choreo::GetSmallestArc( mpCharacter->GetFacingDir( ), fDesiredDir );
+
+ float newRatio;
+ if ( mpCharacter->IsTurbo() )
+ {
+ newRatio = CharacterTune::sfTurboRotateRate * timeins;
+ }
+ else
+ {
+ newRatio = CharacterTune::sfLocoRotateRate * timeins;
+ }
+ float oldRatio = 1.0f - newRatio;
+ float newDir = ( mpCharacter->GetFacingDir( ) * oldRatio ) + ( ( mpCharacter->GetFacingDir( ) + rotateDelta ) * newRatio );
+
+ return newDir;
+}
+
+/*
+==============================================================================
+WalkerLocomotionAction::SolveActualSpeed
+==============================================================================
+Description: Comment
+
+Parameters: ( float fDesiredSpeed, float timeins )
+
+Return: float
+
+=============================================================================
+*/
+float WalkerLocomotionAction::SolveActualSpeed( float fInputSpeed, float timeins )
+{
+ float v = 0.0f;
+ if ( fInputSpeed == mfDesiredSpeed )
+ {
+ return fInputSpeed;
+ }
+ // Damping function.
+ //
+ else if ( fInputSpeed > mfDesiredSpeed )
+ {
+ v = mfDesiredSpeed + CharacterTune::sfLocoAcceleration * timeins;
+ return v > fInputSpeed ? fInputSpeed : v;
+ }
+ else
+ {
+ v = mfDesiredSpeed + CharacterTune::sfLocoDecceleration * timeins;
+ return v < fInputSpeed ? fInputSpeed : v;
+ }
+}
+/*
+==============================================================================
+WalkerLocomotionAction::WakeUp
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void WalkerLocomotionAction::WakeUp( float timeins )
+{
+}
+/*
+==============================================================================
+WalkerLocomotionAction::DoSimulation
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void WalkerLocomotionAction::DoSimulation( float timeins)
+{
+ choreo::Puppet* pPuppet = mpCharacter->GetPuppet();
+ rAssert( pPuppet );
+ float fSpeed = 0.0f;
+ rmt::Vector dir;
+ mpCharacter->GetDesiredFacing( dir );
+
+ // Only update the direction and speed if the
+ // controller has a direction input.
+ //
+ const float NO_DIRECTION = 0.0000001f;
+ if ( dir.MagnitudeSqr( ) > NO_DIRECTION )
+ {
+ float fDesiredDir = choreo::GetWorldAngle( dir.x, dir.z );
+ float fDir = SolveActualDir( fDesiredDir, timeins );
+ mpCharacter->SetFacingDir( fDir );
+
+ float fInputSpeed = mpCharacter->GetDesiredSpeed();
+
+ if ( mpCharacter->IsTurbo() && fInputSpeed > 0.0f )
+ {
+ mfDesiredSpeed = mpCharacter->GetMaxSpeed();
+ }
+ else
+ {
+ mfDesiredSpeed = SolveActualSpeed( fInputSpeed, timeins );
+
+ // Snap to MaxSpeed. Don't decel out of turbo.
+ //
+ if ( mfDesiredSpeed > mpCharacter->GetMaxSpeed() )
+ {
+ mfDesiredSpeed = mpCharacter->GetMaxSpeed();
+ }
+ }
+ }
+ // Method A the motion direction matches the facing direction.
+ // Quick turns at full speed result in a small U turn.
+ //
+ // Method B the motion direction matches the desired direction.
+ // Smoother 180 turns, but left right motion is jerkier.
+ // [6/21/2002]
+ //
+ if ( CharacterTune::bLocoTest || mpCharacter->IsTurbo() )
+ {
+ mpLocomotionDriver->SetDesiredMotionAngle( mpCharacter->GetFacingDir( ) );
+ mpLocomotionDriver->SetDesiredFacingAngle( mpCharacter->GetFacingDir( ) );
+ mpLocomotionDriver->SetDesiredVelocity( mfDesiredSpeed );
+ }
+ else
+ {
+ mpLocomotionDriver->SetDesiredMotionAngle( mpCharacter->GetDesiredDir( ) );
+ mpLocomotionDriver->SetDesiredFacingAngle( mpCharacter->GetFacingDir( ) );
+ mpLocomotionDriver->SetDesiredVelocity( mfDesiredSpeed );
+ }
+}
+/*
+==============================================================================
+WalkerLocomotionAction::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void WalkerLocomotionAction::Update( float timeins)
+{
+ rmt::Vector outnorm;
+ rmt::Vector outposition;
+
+ if( (mpCharacter->GetLocoVelocity().Magnitude() > 0.0f) || !mAllowIdle)
+ {
+ mIdleTime = 0.0f;
+ }
+ else
+ {
+ mIdleTime += timeins;
+ }
+
+ if(PresentationManager::GetInstance()->InConversation())
+ {
+ mIdleTime = 0.0f;
+ }
+
+ if((mIdleTime > mNextIdle) && (mpCharacter == GetCharacterManager()->GetCharacter(0)))
+ {
+ static int s_idleProbabilityTable[10] = { 0,0,0,0,0,1,1,1,2,2 };
+ static int s_lastIdle = 0;
+ int idle = s_idleProbabilityTable[sRandom.IntRanged(9)];
+
+ if((s_lastIdle != 0) && (s_lastIdle == idle))
+ {
+ idle = 0;
+ }
+ s_lastIdle = idle;
+
+ if(!(GetInteriorManager()->IsEntering() || GetInteriorManager()->IsExiting()))
+ {
+ char animName[64];
+ sprintf(animName, "%s_idle%d", GetCharacterManager()->GetModelName(mpCharacter), idle);
+
+ Sequencer* seq = mpCharacter->GetActionController()->GetNextSequencer();
+ seq->BeginSequence();
+ PlayIdleAnimationAction* a = new PlayIdleAnimationAction(mpCharacter,animName);
+ a->AbortWhenMovementOccurs(true);
+ seq->AddAction(a);
+ seq->EndSequence();
+ }
+
+ mIdleTime = 0.0f;
+ mNextIdle = (sRandom.Float() * 10.0f) + 10.0f;
+
+ GetEventManager()->TriggerEvent( EVENT_PLAY_IDLE_MUSIC );
+ }
+
+ CharacterController* c = mpCharacter->GetController();
+ rmt::Vector v;
+ mpCharacter->GetVelocity(v);
+ float velocity = v.Magnitude();
+ if(c && mpCharacter->mbSurfing &&
+ (c->GetIntention() == CharacterController::NONE) &&
+ !AnyStickMovement(mpCharacter) &&
+ (mpCharacter->GetLocoVelocity().Magnitude() < 0.1f) &&
+ (velocity > 10.0f))
+ {
+ mpCharacter->GetActionController()->SequenceSingleAction(new PlayAnimationAction(mpCharacter, "surf_in"));
+ mpCharacter->GetActionController()->SequenceSingleAction(new SurfAction(mpCharacter));
+ mpCharacter->GetActionController()->SequenceSingleAction(new PlayAnimationAction(mpCharacter, "surf_out"));
+ }
+}
+
+/*
+==============================================================================
+InCarAction::InCarAction
+==============================================================================
+Description: Comment
+
+Parameters: (Character* pCharacter, float duration)
+
+Return: WalkerAction
+
+=============================================================================
+*/
+InCarAction::InCarAction( Character* pCharacter )
+:
+mpCharacter( pCharacter ),
+mpAnimationDriver (NULL),
+rockinIsMyBusiness (false),
+timeBetweenBeats(0.0f),
+timeSinceLastBeat(0.0f),
+lastBeatValue(0.0f),
+mIdleTime(0.0f)
+{
+}
+
+InCarAction::~InCarAction( )
+{
+ tRefCounted::Release(mpAnimationDriver);
+}
+
+
+/*
+==============================================================================
+InCarAction::WakeUp
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void InCarAction::WakeUp( float timeins )
+{
+ mIsDriver = (mpCharacter->GetTargetVehicle()->mpDriver == mpCharacter) || (mpCharacter->GetTargetVehicle()->mpDriver == NULL);
+ mIdleTime = 0.0f;
+ IWannaRock(false);
+}
+
+void InCarAction::IWannaRock(bool whattaYouWannaDoWithYourLife)
+{
+ rockinIsMyBusiness = whattaYouWannaDoWithYourLife;
+
+ if(mpAnimationDriver)
+ {
+ mpCharacter->GetPuppet()->StopDriver( mpAnimationDriver );
+ tRefCounted::Release(mpAnimationDriver);
+ }
+
+ choreo::Puppet* pPuppet = mpCharacter->GetPuppet();
+ rAssert( pPuppet );
+
+ choreo::Animation* anim;
+
+ if(rockinIsMyBusiness)
+ {
+ anim = choreo::find<choreo::Animation>( pPuppet->GetBank(), mIsDriver ? "in_car_victory_small_driver" : "in_car_victory_small" );
+ }
+ else
+ {
+ anim = choreo::find<choreo::Animation>( pPuppet->GetBank(), mIsDriver ? "in_car_idle_driver" : "in_car_idle" );
+ }
+
+ if (anim == 0)
+ {
+ Done();
+ return;
+ }
+
+ choreo::AnimationDriver* animDriver = anim->NewAnimationDriver();
+ tRefCounted::Assign(mpAnimationDriver, animDriver);
+
+ animDriver->SetWeight(1.0f);
+ animDriver->SetPriority(0);
+ animDriver->SetIsCyclic(true);
+ if(rockinIsMyBusiness && (timeBetweenBeats != 0.0f))
+ {
+ animDriver->SetSpeed((46.0f / 30.0f) / timeBetweenBeats);
+ float biasedBeat = (lastBeatValue + 1.0f) - (12.0f / 46.0f);
+ float frame = ((biasedBeat) - (float)(int)(biasedBeat)) * 46.0f;
+ animDriver->SetFrame(frame);
+ }
+ else
+ {
+ animDriver->SetSpeed(1.0f);
+ }
+
+
+ pPuppet->PlayDriver( animDriver, -1.0f );
+}
+
+/*
+==============================================================================
+InCarAction::DoSimulation
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void InCarAction::DoSimulation( float timeins)
+{
+ mpCharacter->SetDesiredDir( rmt::PI );
+ mpCharacter->SetFacingDir( rmt::PI );
+ mpCharacter->SetDesiredSpeed( 0.0f );
+ mpCharacter->SetSpeed( 0.0f );
+}
+/*
+==============================================================================
+InCarAction::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void InCarAction::Update( float timeins)
+{
+ timeSinceLastBeat += timeins;
+
+ float beat;
+
+ if( CommandLineOptions::Get( CLO_MUTE ) )
+ beat = 0.0f;
+ else
+ beat = GetSoundManager()->GetBeatValue();
+
+ if(int(beat) != int(lastBeatValue))
+ {
+ timeBetweenBeats = timeSinceLastBeat;
+ timeSinceLastBeat = 0.0f;
+ if(rockinIsMyBusiness)
+ {
+ mpAnimationDriver->SetFrame(12.0f);
+ }
+ }
+
+ lastBeatValue = beat;
+
+ if((mIdleTime < 3.0f) && ((mIdleTime + timeins) > 3.0f))
+ {
+ if(mpCharacter->GetRockinIdle())
+ {
+ IWannaRock(true);
+ }
+ }
+
+ mIdleTime += timeins;
+}
+
+void InCarAction::Clear()
+{
+ mpCharacter->GetPuppet()->StopDriver( mpAnimationDriver );
+ tRefCounted::Release(mpAnimationDriver);
+}
+
+
+/*
+==============================================================================
+HoldAnimationAction
+
+A animation that hold on a specified frame untul the condition in
+"ShouldRelease" is met.
+==============================================================================
+*/
+
+HoldAnimationAction::HoldAnimationAction( Character* pCharacter, const char* animName, float hold)
+:
+mpCharacter( pCharacter ),
+mpAnimationDriver(NULL),
+mHoldFrame(hold)
+{
+ mAnimUID = tEntity::MakeUID(animName);
+
+}
+
+HoldAnimationAction::~HoldAnimationAction( )
+{
+ tRefCounted::Release(mpAnimationDriver);
+}
+
+void HoldAnimationAction::WakeUp( float timeins )
+{
+ choreo::Puppet* pPuppet = mpCharacter->GetPuppet();
+ rAssert( pPuppet );
+
+ choreo::Animation* anim = choreo::find<choreo::Animation>( pPuppet->GetBank(), mAnimUID );
+
+ if (anim == 0)
+ {
+ Done();
+ return;
+ }
+
+ choreo::AnimationDriver* animDriver = anim->NewAnimationDriver();
+ tRefCounted::Assign(mpAnimationDriver, animDriver);
+
+ animDriver->SetWeight(1.0f);
+ animDriver->SetPriority(0);
+ animDriver->SetIsCyclic(false);
+ mOrigFrames = animDriver->GetEndFrame();
+ animDriver->SetEndFrame(mHoldFrame);
+ animDriver->SetHoldEndFrame(true);
+
+ pPuppet->PlayDriver( mpAnimationDriver, -1.0f );
+}
+
+void HoldAnimationAction::DoSimulation( float timeins)
+{
+ mpCharacter->SetDesiredDir( rmt::PI );
+ mpCharacter->SetFacingDir( rmt::PI );
+ mpCharacter->SetDesiredSpeed( 0.0f );
+ mpCharacter->SetSpeed( 0.0f );
+}
+
+void HoldAnimationAction::Update( float timeins)
+{
+ if(ShouldRelease())
+ {
+ mpAnimationDriver->SetEndFrame(mOrigFrames);
+ mpAnimationDriver->SetHoldEndFrame(false);
+ }
+
+
+ if(mpAnimationDriver->IsFinished())
+ {
+ Done();
+ }
+}
+
+void HoldAnimationAction::Clear()
+{
+ mpCharacter->GetPuppet()->StopDriver( mpAnimationDriver );
+ tRefCounted::Release(mpAnimationDriver);
+}
+
+SteerAction::SteerAction( Character* pCharacter, const char* anim, float frame) :
+ HoldAnimationAction(pCharacter, anim, frame)
+{
+}
+
+bool SteerAction::ShouldRelease(void)
+{
+ if(!mpCharacter->GetTargetVehicle())
+ {
+ return true;
+ }
+
+ float steer = GetCharacterManager()->GetCharacter(0)->GetController()->GetValue(CharacterController::LeftStickX);
+ return (rmt::Abs(steer) < 0.1f) || (mpCharacter->GetTargetVehicle()->IsInReverse());
+;
+}
+
+ReverseAction::ReverseAction( Character* pCharacter, const char* anim, float frame) :
+ HoldAnimationAction(pCharacter, anim, frame)
+{
+ mRelease = 0.0f;
+}
+
+bool ReverseAction::ShouldRelease(void)
+{
+ if(!mpCharacter->GetTargetVehicle())
+ {
+ return true;
+ }
+
+ mRelease = (mRelease * 0.9f) + (mpCharacter->GetTargetVehicle()->IsInReverse() ? 0.0f : 0.1f);
+ return mRelease > 0.9f;
+}
+
+AccelAction::AccelAction( Character* pCharacter, const char* anim, float frame) :
+ HoldAnimationAction(pCharacter, anim, frame)
+{
+}
+
+bool AccelAction::ShouldRelease(void)
+{
+ if(!mpCharacter->GetTargetVehicle())
+ {
+ return true;
+ }
+
+ return mpCharacter->GetTargetVehicle()->GetAccelMss() < 7.0f;
+}
+
+DecelAction::DecelAction( Character* pCharacter, const char* anim, float frame) :
+ HoldAnimationAction(pCharacter, anim, frame)
+{
+}
+
+bool DecelAction::ShouldRelease(void)
+{
+ if(!mpCharacter->GetTargetVehicle())
+ {
+ return true;
+ }
+
+ return (mpCharacter->GetTargetVehicle()->GetAccelMss() > -9.0f) ||
+ (mpCharacter->GetTargetVehicle()->GetSpeedKmh() < 1.0f) ||
+ (mpCharacter->GetTargetVehicle()->IsInReverse());
+}
+
+/*
+==============================================================================
+Arrive::Arrive
+==============================================================================
+Description: Comment
+
+ Parameters: ( Character* pCharacter, rmt::Vector& destination )
+
+Return: Arrive
+
+=============================================================================
+*/
+
+Arrive::Arrive( Character* pCharacter, rmt::Vector& destination, bool strict )
+:
+mpCharacter( pCharacter ),
+mDestination( destination ),
+mfDecelTime( 0.1f ),
+mfMaxSpeed( 1.0f ),
+mbPrevLocoMode( CharacterTune::bLocoTest ),
+mStrict(strict)
+{
+ // work in 2d.
+ //
+ mDestination.y = 0.0f;
+ CharacterTune::bLocoTest = false;
+};
+
+/*
+==============================================================================
+Arrive::~Arrive
+==============================================================================
+Description: Comment
+
+ Parameters: ( void )
+
+Return: Arrive
+
+=============================================================================
+*/
+Arrive::~Arrive( void )
+{
+}
+/*
+==============================================================================
+Arrive::WakeUp
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Arrive::WakeUp( float timeins )
+{
+ rmt::Vector myPos;
+ mpCharacter->GetPosition( myPos );
+ myPos.y = 0.0f;
+ rmt::Vector vectorBetween;
+ vectorBetween.Sub( mDestination, myPos );
+ float dist = vectorBetween.Magnitude();
+
+ // figure out a resonable (conservative) time for how long it should take us to get there
+ mEstTime = ((dist / CharacterTune::sfMaxSpeed) * 1.5f) + timeins;
+ mElapsedTime = 0;
+
+ mbSolveState = mpCharacter->GetSolveCollisions();
+// mpCharacter->SetSolveCollisions( false );
+ mpCharacter->SetTurbo(false);
+}
+/*
+==============================================================================
+Arrive::DoSimulation
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Arrive::DoSimulation( float timeins )
+{
+ mElapsedTime += timeins;
+
+ if(mElapsedTime > mEstTime)
+ {
+ // taking too long, we probably got stuck, teleport
+ rmt::Vector position;
+ mpCharacter->GetPosition( position );
+ mDestination.y = position.y;
+ mpCharacter->SetPosition( mDestination );
+ mpCharacter->SetDesiredSpeed( 0.0f );
+ mpCharacter->ResetSpeed( );
+ Done( );
+ return;
+ }
+
+ rmt::Vector desiredVelocity( 0.0f, 0.0f, 0.0f );
+
+ rmt::Vector myPos;
+ mpCharacter->GetPosition( myPos );
+ myPos.y = 0.0f;
+ rmt::Vector vectorBetween;
+ vectorBetween.Sub( mDestination, myPos );
+ float dist = vectorBetween.Magnitude();
+ if ( dist > 0.05f )
+ {
+ float speed = dist / mfDecelTime;
+
+ float clipped_speed = rmt::Min( speed, CharacterTune::sfMaxSpeed );
+ desiredVelocity.Sub( mDestination, myPos );
+ desiredVelocity.Normalize();
+ desiredVelocity.Scale( clipped_speed );
+
+ //rmt::Vector velocity;
+ //mpCharacter->GetVelocity( velocity );
+ //desiredVelocity.Sub( velocity );
+
+ // desiredVelocity is in m/s
+ mpCharacter->SetDesiredSpeed( desiredVelocity.Magnitude( ));
+ mpCharacter->SetDesiredDir( choreo::GetWorldAngle( desiredVelocity.x, desiredVelocity.z ) );
+ }
+ else
+ {
+ if(mStrict)
+ {
+ rmt::Vector position;
+ mpCharacter->GetPosition( position );
+ mDestination.y = position.y;
+ mpCharacter->SetPosition( mDestination );
+ }
+
+ mpCharacter->SetDesiredSpeed( 0.0f );
+ mpCharacter->ResetSpeed( );
+ Done( );
+ }
+}
+
+/*
+==============================================================================
+Arrive::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Arrive::Update( float timeins )
+{
+}
+
+void Arrive::Clear( void )
+{
+ CharacterTune::bLocoTest = mbPrevLocoMode;
+ mpCharacter->SetSolveCollisions( mbSolveState );
+}
+/*
+==============================================================================
+Orient::Orient
+==============================================================================
+Description: Comment
+
+ Parameters: ( Character* pCharacter, rmt::Vector& facing )
+
+Return: Orient
+
+=============================================================================
+*/
+Orient::Orient( Character* pCharacter, rmt::Vector& facing )
+:
+mpCharacter( pCharacter )
+{
+ rmt::Matrix worldToLocal = mpCharacter->GetInverseParentTransform();
+ worldToLocal.RotateVector( facing, &facing );
+ mfTarget = choreo::GetWorldAngle( facing.x, facing.z );
+}
+
+
+/*
+==============================================================================
+Orient::~Orient
+==============================================================================
+Description: Comment
+
+ Parameters: ( void )
+
+Return: Orient
+
+=============================================================================
+*/
+Orient::~Orient( void )
+{
+}
+
+void Orient::WakeUp( float timeins )
+{
+ mpCharacter->SetDesiredDir( mfTarget );
+}
+/*
+==============================================================================
+Orient::DoSimulation
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Orient::DoSimulation( float timeins )
+{
+ mfTarget = mpCharacter->GetDesiredDir();
+}
+
+/*
+==============================================================================
+Orient::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Orient::Update( float timeins )
+{
+ float fTolerance = 0.1f;
+ float fFacingDir = mpCharacter->GetFacingDir();
+
+ // We must do two compares because if the value of mfTarget is
+ // slightly less than 2PI and the actual facing dir is close to 0.0f
+ // the first compare will return false.
+ //
+ if ( rmt::Epsilon( fFacingDir, mfTarget, fTolerance )
+ || rmt::Epsilon( fFacingDir, rmt::Fabs( mfTarget - rmt::PI_2 ), fTolerance ) )
+ {
+ // Close enough, now fake it.
+ //
+ mpCharacter->SetFacingDir( mfTarget );
+ Done( );
+ }
+}
+
+/*
+==============================================================================
+Position::Position
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, rmt::Vector& position, float fDuration )
+
+Return: Position
+
+=============================================================================
+*/
+Position::Position( Character* pCharacter, rmt::Vector& position, float fDuration, bool local )
+:
+mpCharacter( pCharacter ),
+mDestination( position ),
+mfDuration( fDuration ),
+mfTimeLeft( fDuration ),
+mLocal(local)
+{
+}
+
+/*
+==============================================================================
+Position::~Position
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: Position
+
+=============================================================================
+*/
+Position::~Position( void )
+{
+}
+
+/*
+==============================================================================
+Position::WakeUp
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Position::WakeUp( float timeins )
+{
+}
+/*
+==============================================================================
+Position::DoSimulation
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Position::DoSimulation( float timeins )
+{
+ mfTimeLeft -= timeins;
+
+ if ( mfTimeLeft > 0.0f )
+ {
+ rmt::Vector position;
+ if(mLocal)
+ {
+ position = mpCharacter->GetPuppet()->GetPosition();
+ }
+ else
+ {
+ mpCharacter->GetPosition( position );
+ }
+
+ rmt::Vector delta = mDestination - position;
+ delta.Scale(rmt::Min(timeins / mfTimeLeft, 1.0f));
+ position.Add(delta);
+
+ if(mLocal)
+ {
+ mpCharacter->GetPuppet()->SetPosition( position);
+ }
+ else
+ {
+ mpCharacter->SetPosition( position );
+ }
+ }
+ else
+ {
+ if(mLocal)
+ {
+ mpCharacter->GetPuppet()->SetPosition( mDestination );
+ }
+ else
+ {
+ mpCharacter->SetPosition( mDestination );
+ }
+ Done();
+ }
+}
+/*
+==============================================================================
+Position::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Position::Update( float timeins )
+{
+}
+
+void GroundSnap::WakeUp( float timeins )
+{
+ static float highOffset = 1.0f;
+ static float lowOffset = 0.4f;
+
+ mpCharacter->GetPuppet()->StopAllDrivers();
+
+ bool high = false;
+
+ if(mpCharacter->GetTargetVehicle())
+ {
+ high = mpCharacter->GetTargetVehicle()->GetDriverLocation().y > CharacterTune::sGetInHeightThreshold;
+ }
+
+ rmt::Vector position = mpCharacter->GetPuppet()->GetPosition();
+ position.y -= high ? highOffset : lowOffset;
+ mpCharacter->GetPuppet()->SetPosition(position);
+ Done();
+}
+
+#include <choreo/animation.hpp>
+/*
+==============================================================================
+PlayAnimationAction::PlayAnimationAction
+==============================================================================
+Description: Comment
+
+Parameters: (Character* pCharacter, const char* anim, bool preCollideWithWorld, bool postCollideWithWorld )
+
+Return: PlayAnimationAction
+
+=============================================================================
+*/
+PlayAnimationAction::PlayAnimationAction(Character* pCharacter, const char* anim, float fFrameRate /* = 30.0f */ )
+:
+mpCharacter( pCharacter ),
+mfFrameRate( fFrameRate ),
+mpAnimationDriver( 0 ),
+mbAbortWhenMovementOccurs( false ),
+loop(false)
+{
+ mAnimUID = tEntity::MakeUID( anim );
+}
+
+PlayAnimationAction::PlayAnimationAction( Character* pCharacter, const tName& anim, float fFrameRate )
+:
+mpCharacter( pCharacter ),
+mfFrameRate( fFrameRate ),
+mpAnimationDriver( 0 ),
+loop(false)
+{
+ mAnimUID = anim.GetUID();
+}
+
+
+//=============================================================================
+//PlayAnimationAction::AbortWhenMovementOccurs
+//=============================================================================
+//Description: do we want to abort this action if the character moves? y/n
+//
+//Parameters: abort - do we want to?
+//
+//Return: void
+//
+//=============================================================================
+void PlayAnimationAction::AbortWhenMovementOccurs( bool abort )
+{
+ mbAbortWhenMovementOccurs = abort;
+}
+
+/*
+==============================================================================
+PlayAnimationAction::WakeUp
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void PlayAnimationAction::WakeUp( float timeins )
+{
+ choreo::Puppet* pPuppet = mpCharacter->GetPuppet();
+ rAssert( pPuppet );
+
+ choreo::Animation* anim = choreo::find<choreo::Animation>( pPuppet->GetBank(), mAnimUID );
+ if (anim == 0)
+ return;
+
+ choreo::AnimationDriver* animDriver = anim->NewAnimationDriver();
+ animDriver->AddRef();
+
+ animDriver->SetWeight(1.0f);
+ animDriver->SetPriority(0);
+ animDriver->SetSpeed( mfFrameRate / animDriver->GetFramesPerSecond() );
+
+ if(loop)
+ {
+ animDriver->SetIsCyclic(true);
+ }
+
+ if ( pPuppet->PlayDriver( animDriver, loop ? -1.0f : animDriver->GetDuration() ) )
+ {
+ mpAnimationDriver = animDriver;
+ animDriver->Release();
+ }
+ else
+ {
+ animDriver->Release();
+ }
+}
+/*
+==============================================================================
+PlayAnimationAction::DoSimulation
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void PlayAnimationAction::DoSimulation( float timeins )
+{
+}
+/*
+==============================================================================
+PlayAnimationAction::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void PlayAnimationAction::Update( float timeins )
+{
+ //
+ // Abort the action if there was any movement
+ //
+ if( ShouldAbort() )
+ {
+ Done();
+ return;
+ }
+
+ if( mpCharacter->GetPuppet()->IsDriverFinished( mpAnimationDriver ) == true )
+ {
+ Done();
+ }
+
+}
+
+bool PlayAnimationAction::ShouldAbort(void)
+{
+ if(mbAbortWhenMovementOccurs)
+ {
+ CharacterController* c = mpCharacter->GetController();
+ if((c->GetIntention() != CharacterController::NONE) || AnyStickMovement(mpCharacter))
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/*
+==============================================================================
+PlayAnimationAction::Clear
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: void
+
+=============================================================================
+*/
+void PlayAnimationAction::Clear()
+{
+ mpCharacter->SetDesiredDir(mpCharacter->GetFacingDir());
+ mpCharacter->GetPuppet()->StopDriver( mpAnimationDriver );
+}
+
+
+
+
+/*
+==============================================================================
+PlayIdleAnimationAction::PlayIdleAnimationAction
+==============================================================================
+Description: Comment
+
+Parameters: (Character* pCharacter, const char* anim, bool preCollideWithWorld, bool postCollideWithWorld )
+
+Return: PlayIdleAnimationAction
+
+=============================================================================
+*/
+PlayIdleAnimationAction::PlayIdleAnimationAction(Character* pCharacter, const char* anim, float fFrameRate /* = 30.0f */ )
+:
+PlayAnimationAction( pCharacter, anim, fFrameRate )
+{
+}
+
+PlayIdleAnimationAction::PlayIdleAnimationAction( Character* pCharacter, const tName& anim, float fFrameRate )
+:
+PlayAnimationAction( pCharacter, anim, fFrameRate )
+{
+}
+
+/*
+==============================================================================
+PlayIdleAnimationAction::WakeUp
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void PlayIdleAnimationAction::WakeUp( float timeins )
+{
+ choreo::Puppet* pPuppet = mpCharacter->GetPuppet();
+ rAssert( pPuppet );
+
+ choreo::Animation* anim = choreo::find<choreo::Animation>( pPuppet->GetBank(), mAnimUID );
+ if (anim == 0)
+ return;
+
+ mpCharacter->mbIsPlayingIdleAnim = true;
+
+ PlayAnimationAction::WakeUp( timeins );
+}
+
+/*
+==============================================================================
+PlayIdleAnimationAction::Clear
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: void
+
+=============================================================================
+*/
+void PlayIdleAnimationAction::Clear()
+{
+ mpCharacter->mbIsPlayingIdleAnim = false;
+ PlayAnimationAction::Clear();
+}
+
+/*
+==============================================================================
+PlayIdleAnimationAction::Abort
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: void
+
+=============================================================================
+*/
+void PlayIdleAnimationAction::Abort()
+{
+ mpCharacter->mbIsPlayingIdleAnim = false;
+}
+
+
+
+/*
+==============================================================================
+CarDoorAction
+==============================================================================
+*/
+
+CarDoorAction::CarDoorAction(Vehicle* pVehicle, Vehicle::DoorAction action, Vehicle::Door door, float delay, float time, Character* c, Sequencer* s)
+:
+mpVehicle( pVehicle),
+mAction(action),
+mDoor(door),
+mTime(time),
+mDelay(delay),
+mCurrentTime(0.0f),
+character(c),
+sequencer(s)
+{
+}
+
+
+void CarDoorAction::WakeUp( float timeins )
+{
+ //HACK : abort the action if we have a pending movement on the character controller
+ // this needs to be handled more gracefully
+ if(sequencer && character)
+ {
+ if(AnyStickMovement(character, 0.5f)) Done();
+ }
+}
+
+void CarDoorAction::DoSimulation( float timeins )
+{
+}
+
+void CarDoorAction::Update( float timeins )
+{
+ mCurrentTime += timeins;
+
+ // make sure delay has elapsed
+ if(mCurrentTime <= mDelay)
+ {
+ return;
+ }
+
+ // calcualte how far along in our action the door is
+ float t = (mCurrentTime - mDelay) / mTime;
+
+ // check for completion
+ if(t > 1.0f)
+ {
+ t = 1.0f;
+ Done();
+ }
+
+ // if we are closing the door, we go backwards
+ if(mAction == Vehicle::DOORACTION_CLOSE)
+ {
+ t = 1.0f - t;
+ }
+
+ // move that sucker
+ mpVehicle->MoveDoor(mDoor, mAction, t);
+}
+
+void CarDoorAction::Clear()
+{
+}
+
+void ReleaseDoorsAction::Update( float timeins )
+{
+ mVehicle->ReleaseDoors();
+ Done();
+}
+
+/*
+==============================================================================
+JumpAction::JumpAction
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter, const char* name, float fUpVelocity )
+
+Return: JumpAction
+
+=============================================================================
+*/
+#include <worldsim/character/charactercontroller.h>
+#include <choreo/root.hpp>
+#include <sound/soundmanager.h>
+
+JumpAction::JumpAction( Character* pCharacter, const char* name, float fUpVelocity )
+:
+mpCharacter( pCharacter ),
+mJumpState( InitJump ),
+mAnimUid( 0 ),
+mfTime( 0.0f ),
+mfGravity( CharacterTune::sfAirGravity ),
+mpAnimationDriver( 0 ),
+mpRootController( 0 ),
+mpRootDriver( 0 ),
+mbBoost( false ),
+mbFalling( false )
+
+{
+ mVelocity.Set( 0.0f, fUpVelocity, 0.0f );
+ mAnimUid = tEntity::MakeUID( name );
+
+ mpRootController = new choreo::RootTransformController;
+ mpRootController->AddRef();
+
+ // weight & priority
+ mpRootController->SetRootPriority( 0 );
+ mpRootController->SetRootWeight( 1.0f );
+
+ // blend times
+ mpRootController->SetRootBlendInTime( 0.0f );
+ mpRootController->SetRootBlendOutTime( 0.0f );
+
+ mpRootDriver = new choreo::RootDriver(mpRootController);
+ mpRootDriver->AddRef();
+}
+
+/*
+==============================================================================
+JumpAction::~JumpAction
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: JumpAction
+
+=============================================================================
+*/
+JumpAction::~JumpAction( void )
+{
+ tRefCounted::Release(mpRootDriver);
+ tRefCounted::Release(mpRootController);
+ tRefCounted::Release(mpAnimationDriver);
+}
+
+
+/*
+==============================================================================
+JumpAction::WakeUp
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void JumpAction::SetRootTransform( void )
+{
+ //poser::Transform rootTransform;
+ //mpCharacter->GetRootTransform( rootTransform );
+ //mpRootController->SetRootTransform( rootTransform );
+ choreo::Puppet* pPuppet = mpCharacter->GetPuppet();
+ rAssert( pPuppet );
+ mpRootController->SetRootTransform( pPuppet->GetRootTransform( ) );
+}
+/*
+==============================================================================
+JumpAction::SetRootPosition
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& position )
+
+Return: void
+
+=============================================================================
+*/
+void JumpAction::SetRootPosition( const rmt::Vector& position )
+{
+ mpRootController->SetPosition( position );
+}
+
+/*
+==============================================================================
+JumpAction::Reset
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& jumpTarget, bool bFalling )
+
+Return: void
+
+=============================================================================
+*/
+void JumpAction::Reset( const rmt::Vector& jumpTarget, bool bFalling /* = false */)
+{
+ float t = Reset( jumpTarget.y, bFalling );
+ mpCharacter->ResetSpeed();
+ rmt::Vector velocityXZ;
+ velocityXZ = jumpTarget;
+ velocityXZ.y = 0.0f;
+ velocityXZ.Scale( 1.0f / t );
+ mVelocity.x = velocityXZ.x;
+ mVelocity.z = velocityXZ.z;
+ mfMaxSpeed = velocityXZ.Magnitude( );
+
+ // Clamp max speed
+ if ( mfMaxSpeed > JUMP_ACTION_SPEED_CLAMP )
+ {
+ mfMaxSpeed = JUMP_ACTION_SPEED_CLAMP;
+ velocityXZ.Normalize();
+ velocityXZ.Scale( JUMP_ACTION_SPEED_CLAMP );
+ }
+
+ // Make the character smoothly rotate to face the direction of travel.
+ //
+ mpCharacter->SetDesiredDir( choreo::GetWorldAngle( mVelocity.x, mVelocity.z ) );
+
+ mbPreSlam = mbSlam = false;
+}
+/*
+==============================================================================
+JumpAction::Reset
+==============================================================================
+Description: Comment
+
+Parameters: ( float fJumpHeight, bool bFalling )
+
+Return: void
+
+=============================================================================
+*/
+static rmt::Vector startPosition;
+static rmt::Vector endPosition;
+static float sfJumpHeightHighWater;
+
+float JumpAction::Reset( float fJumpHeight, bool bFalling )
+{
+ //rTuneAssertMsg( fJumpHeight >= 0, "JumpAction::Reset called with a negative height. Tell the designer who place the bouncepad to make sure the target is ABOVE the pad - MikeR" );
+
+ // Use a safe value
+ if ( fJumpHeight < 0.0f )
+ {
+ fJumpHeight = 0.2f;
+ }
+ mfTime = 0.0f;
+ mbBoost = false;
+ mbFalling = bFalling;
+ mfGravity = CharacterTune::sfAirGravity;
+ mVelocity.Clear( );
+ rmt::Vector position;
+ mpCharacter->GetPosition( position );
+ mfStartHeight = position.y;
+
+ if(mpAnimationDriver)
+ {
+ mpCharacter->GetPuppet()->StopDriver(mpAnimationDriver);
+ tRefCounted::Release(mpAnimationDriver);
+ }
+
+ mbJumpAgain = false;
+ // Overrides all animation translations.
+ //
+ mpRootController->SetRootPriority( -1 );
+
+ choreo::Puppet* pPuppet = mpCharacter->GetPuppet();
+ rAssert( pPuppet );
+
+ // Init the position of the root controller.
+ //
+ SetRootTransform( );
+ startPosition = mpRootController->GetPosition( );
+ sfJumpHeightHighWater = 0.0f;
+
+ mfMaxSpeed = rmt::Max( mpCharacter->GetMaxSpeed(), mpCharacter->GetSpeed( ) );
+
+ rmt::Vector velocity;
+ mpCharacter->GetVelocity(velocity);
+ velocity.y = 0;
+
+ // We care about the XZ velocity only, so we take the magnitude before we set the .y
+ //
+ float fActualVelocity = mVelocity.Magnitude( );
+
+ // We don't want to use the XZ velocity via physics when we are colliding.
+ // The impulse from the collision detection creates XZ velocity in the
+ // simstate when there is no visual velocity.
+ // TBJ [7/24/2002]
+ //
+ if ( mpCharacter->IsInCollision( ) )
+ {
+ velocity.x = velocity.z = 0.0f;
+ }
+
+ // Note, the mVelocity.y gets set in ::SetJumpVelocity
+ // called below.
+ //
+ mVelocity.x = velocity.x;
+ mVelocity.z = velocity.z;
+
+ // Do the calculations.
+ //
+ float g = mfGravity;
+ float d = fJumpHeight;
+ // How long does it take to fall d metres, given gravity and v = 0.
+ //
+ float t = rmt::Fabs( .5000000000f/-g*(-2.f*rmt::Sqrt(2.f*-g*d)) );
+ float v = 0.0f;
+ // OR we are in a bounce pad.
+ //
+ if ( !bFalling || fJumpHeight > 0.0f )
+ {
+ v = -.5000000000f*(g*t*t-2.f*d)/t;
+ }
+ // If we are on a platform, and running in the same direction as motion,
+ // our speed can be gt mfMaxSpeed.
+ // TBJ [7/24/2002]
+ //
+ mfMaxSpeed = rmt::Max( mfMaxSpeed, fActualVelocity );
+
+ if(mpCharacter->IsTurbo())
+ {
+ mfMaxSpeed -= CharacterTune::sfDashBurstMax;
+ }
+
+ // If we are on a bounce pad.
+ //
+ if ( bFalling && fJumpHeight > 0.0f )
+ {
+ velocity.y = 0.0f;
+ }
+ SetJumpVelocity( velocity.y + v );
+
+ SetStatus(SLEEPING);
+
+ mbPreSlam = mbSlam = false;
+ mbInJumpKick = mbDoJumpKick = false;
+
+ return t;
+}
+
+void JumpAction::WakeUp( float timeins )
+{
+ mpCharacter->SetJumping( true );
+
+ // We care about the XZ velocity only, so we take the magnitude before we set the .y
+ //
+ float fActualVelocity = mVelocity.Magnitude( );
+
+ if ( mbFalling
+ || mpCharacter->GetDesiredSpeed() != 0.0f
+ || mpCharacter->GetSpeed() != 0.0f
+ || fActualVelocity != 0 )
+ {
+ mJumpState = PreJump;
+ }
+ else
+ {
+ mJumpState = InitJump;
+ }
+
+ mpCharacter->GetPuppet()->PlayDriver(mpRootDriver, -1.0f);
+}
+/*
+==============================================================================
+JumpAction::DoSimulation
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void JumpAction::DoSimulation( float timeins )
+{
+ // Do not update the control is the PostTurboJumpRecover state.
+ // If the user wants to move, the action will transition to the
+ // Loco action. If we block that transition (turbo jump recovery)
+ // then we don't want the character to slide around.
+ // TBJ [7/24/2002]
+ //
+ if ( (AllowTranslate & mJumpState) && !mbPreSlam)
+ {
+ // Air control
+ //
+ rmt::Vector dir;
+ mpCharacter->GetDesiredFacing( dir );
+
+ if(!mbSlam)
+ {
+ float desiredSpeed = mpCharacter->GetDesiredSpeed();
+ rmt::Vector accel = dir;
+ accel.Scale( desiredSpeed * CharacterTune::sfAirAccelScale );
+ // v = v0 + at;
+ mVelocity.Add( accel );
+ }
+
+ float up = mVelocity.y;
+ mVelocity.y = 0.0f;
+ float speed = mVelocity.Magnitude();
+ if ( speed >= mfMaxSpeed )
+ {
+ mVelocity.Normalize();
+ mVelocity.Scale( mfMaxSpeed );
+ }
+ mVelocity.y = up;
+
+ if ( dir.MagnitudeSqr( ) > 0.0000001 && !mbSlam)
+ {
+ float fDesiredDir = choreo::GetWorldAngle( dir.x, dir.z );
+ float fDir = SolveActualDir( fDesiredDir, timeins );
+ mpRootController->SetOrientation( fDir );
+ }
+
+ rmt::Vector velocity;
+ velocity = mVelocity;
+ if ( Jump != mJumpState )
+ {
+ velocity.y = 0.0f;
+ }
+
+ if(mbSlam)
+ {
+ velocity.x = velocity.z = 0;
+ }
+
+ rmt::Vector position;
+ position = mpRootController->GetPosition( );
+
+ velocity.Scale( timeins );
+
+ // The root controller doesn't know about the parent transform,
+ // so we have to take care of resolving the actual position updates
+ // here.
+ //
+ rmt::Matrix localToWorldRotation = mpCharacter->GetParentTransform();
+ localToWorldRotation.IdentityTranslation( );
+ velocity.Transform( localToWorldRotation );
+
+ velocity.Add(position);
+ SetRootPosition( velocity );
+
+ float gravityDelta;
+ gravityDelta = mfGravity;
+ gravityDelta *= timeins;
+ mVelocity.y += gravityDelta * (mbSlam ? CharacterTune::sfStompGravityScale : 1.0f);
+ }
+}
+/*
+==============================================================================
+JumpAction::SolveActualDir
+==============================================================================
+Description: Comment
+
+Parameters: ( float fDesiredDir, float timeins )
+
+Return: float
+
+=============================================================================
+*/
+float JumpAction::SolveActualDir( float fDesiredDir, float timeins )
+{
+ // Damping function.
+ //
+ float rotateDelta = choreo::GetSmallestArc( mpCharacter->GetFacingDir( ), fDesiredDir );
+ float newRatio = CharacterTune::sfAirRotateRate * timeins;
+ float oldRatio = 1.0f - newRatio;
+ float newDir = ( mpCharacter->GetFacingDir( ) * oldRatio ) + ( ( mpCharacter->GetFacingDir( ) + rotateDelta ) * newRatio );
+
+ return newDir;
+}
+
+/*
+==============================================================================
+JumpAction::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void JumpAction::Update( float timeins )
+{
+ mpCharacter->DoGroundIntersect(true);
+
+ choreo::Puppet* pPuppet = mpCharacter->GetPuppet();
+ rAssert( pPuppet );
+
+ rmt::Vector outPos;
+ rmt::Vector outNormal;
+
+ rmt::Vector newPos = mpCharacter->LocalToWorld( mpRootController->GetPosition( ) );
+ mpCharacter->GetTerrainIntersect( outPos, outNormal );
+
+ switch ( mJumpState )
+ {
+ case InitJump:
+ {
+ tRefCounted::Assign(mpAnimationDriver,mpCharacter->GetPuppet()->PlayAnimation( "jump_idle_launch" ));
+ // SetIsDriverIndefinite( true ) as we will manually control the stop
+ // of the animation. Length can change based on terrain/collision objects.
+ //
+ pPuppet->SetIsDriverIndefinite( mpAnimationDriver, false );
+ mJumpState = IdleJump;
+ break;
+ }
+ // We have two states here because they have different translate flags.
+ //
+ case IdleJump:
+ case PreJump:
+ {
+ if ( !mpAnimationDriver || pPuppet->IsDriverFinished( mpAnimationDriver ) )
+ {
+ // if we are falling, not jumping.
+ // don't play a sound.
+ // Otherwise this is a jump, and we are beginning our vertical ascent.
+ // Play a sound.
+ //
+ if ( !mbFalling )
+ {
+ GetEventManager()->TriggerEvent( EVENT_JUMP_TAKEOFF );
+ }
+//animation "stomp_in_air"
+//animation "stomp_land"
+//animation "jump_kick"
+
+ pPuppet->StopDriver( mpAnimationDriver );
+ if ( mbJumpAgain )
+ {
+ tRefCounted::Assign(mpAnimationDriver,mpCharacter->GetPuppet()->PlayAnimation( "jump_dash_in_air" ));
+ }
+ else
+ {
+ tRefCounted::Assign(mpAnimationDriver,mpCharacter->GetPuppet()->PlayAnimation( "jump_idle" ));
+ }
+ // SetIsDriverIndefinite( true ) as we will manually control the stop
+ // of the animation. Length can change based on terrain/collision objects.
+ //
+ pPuppet->SetIsDriverIndefinite( mpAnimationDriver, true );
+
+ mJumpState = Jump;
+ }
+ break;
+ }
+ case Jump:
+ {
+ TestForJumpAgain( newPos, mVelocity.y, outPos );
+ float landingStr = 0.0f;
+ if(mbPreSlam && ( !mpAnimationDriver || pPuppet->IsDriverFinished( mpAnimationDriver )))
+ {
+ mbPreSlam = false;
+ tRefCounted::Assign(mpAnimationDriver,pPuppet->PlayAnimation( "stomp_in_air" ));
+ }
+
+ if ( mpCharacter->IsStanding() )
+ {
+ rmt::Vector pos;
+ mpCharacter->GetPosition(pos);
+ mpRootController->SetPosition( mpCharacter->WorldToLocal( pos ) );
+ mVelocity.y = 0.0f;
+ pPuppet->StopDriver( mpAnimationDriver );
+ eJumpState nextState = PostJump;
+
+ landingStr = 0.5f;
+ if( mbJumpAgain )
+ {
+ landingStr = 0.7f;
+ }
+ if ( mbSlam )
+ {
+ mpCharacter->ResetSpeed( );
+ mVelocity.Clear( );
+ tRefCounted::Assign(mpAnimationDriver,pPuppet->PlayAnimation( "stomp_land" ));
+
+ // SetIsDriverIndefinite( true ) as we will manually control the stop
+ // of the animation. Length can change based on terrain/collision objects.
+ //
+ pPuppet->SetIsDriverIndefinite( mpAnimationDriver, false );
+
+ landingStr = 1.0f;
+
+ mpCharacter->Slam();
+
+ } else if ( mpCharacter->GetDesiredSpeed() == 0.0f )
+ {
+ mpCharacter->ResetSpeed( );
+ mVelocity.Clear( );
+ tRefCounted::Assign(mpAnimationDriver, pPuppet->PlayAnimation( "jump_idle_land" ));
+ // SetIsDriverIndefinite( true ) as we will manually control the stop
+ // of the animation. Length can change based on terrain/collision objects.
+ //
+ pPuppet->SetIsDriverIndefinite( mpAnimationDriver, false );
+ }
+ else
+ {
+ tRefCounted::Release(mpAnimationDriver);
+ }
+ if( ( landingStr > 0.0f ) && !mpCharacter->IsNPC() )
+ {
+ rmt::Vector p, n;
+ mpCharacter->GetTerrainIntersect( p, n );
+ GetSparkleManager()->AddLanding( p, landingStr );
+ if( landingStr >= 1.0f )
+ {
+ GetSparkleManager()->AddShockRing( p, landingStr );
+ }
+ }
+ GetEventManager()->TriggerEvent( EVENT_JUMP_LANDING );
+
+ mJumpState = nextState;
+ }
+ else
+ {
+ if ( mpAnimationDriver )
+ {
+ if(mbInJumpKick)
+ {
+ if(mbDoJumpKick && (mpAnimationDriver->GetFrame() > 6))
+ {
+ mpCharacter->Kick();
+ mbDoJumpKick = false;
+ }
+ }
+ else
+ {
+ // Thanks Maple!
+ //
+ float g = -mfGravity;
+ float v = mVelocity.y;
+ float d = newPos.y - outPos.y;
+ //float t0 = .5000000000f/g*(-2.f*v+2.f*rmt::Sqrt((v*v)+2.f*g*d));
+ float t1 = rmt::Fabs( .5000000000f/g*(-2.f*v-2.f*rmt::Sqrt((v*v)+2.f*g*d)) );
+ // t1 seems to be a better root for us.
+ // Why?
+ //
+ float airTime = t1;
+ float animTime = mpAnimationDriver->GetTimeRemaining( );
+ float speedScale = animTime / airTime;
+ if ( speedScale < 1.0f )
+ {
+ mpAnimationDriver->SetSpeed( mpAnimationDriver->GetSpeed( ) * speedScale );
+ }
+ }
+ }
+ }
+ break;
+ }
+ case PostJump:
+ {
+ // bQuit is true when the user has pressed a direction.
+ // Or pressed "Jump".
+ // Allows the skip of the land animation and continue into Loco.
+ //
+ bool bQuit = (mpCharacter->GetDesiredSpeed() != 0.0f) && !mbSlam;
+ bQuit |= ( CharacterController::Jump == mpCharacter->GetController()->GetIntention( ) );
+ //mpCharacter->GetController()->IsButtonDown( CharacterController::Jump ) );
+
+ mpRootController->SetPosition( mpCharacter->WorldToLocal( outPos ) );
+ if ( bQuit || !mpAnimationDriver || pPuppet->IsDriverFinished( mpAnimationDriver ) )
+ {
+ Done( );
+ return;
+ }
+ break;
+ }
+ case PostTurboJumpRecover:
+ {
+ mpRootController->SetPosition( mpCharacter->WorldToLocal( outPos ) );
+ if ( !mpAnimationDriver || pPuppet->IsDriverFinished( mpAnimationDriver ) )
+ {
+ Done( );
+ return;
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ {
+ rmt::Vector position = mpRootController->GetPosition( );
+ float fHeight = position.y - startPosition.y;
+ if ( fHeight > sfJumpHeightHighWater )
+ {
+ sfJumpHeightHighWater = fHeight;
+ }
+ }
+ mfTime += timeins;
+}
+
+/*
+==============================================================================
+JumpAction::TestForJumpAgain
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& currentPos, float fUpVelocity, rmt::Vector& groundPos )
+
+Return: void
+
+=============================================================================
+*/
+void JumpAction::TestForJumpAgain( const rmt::Vector& currentPos, float fUpVelocity, rmt::Vector& groundPos )
+{
+ if(mbPreSlam || mbSlam || mbInJumpKick || mbDoJumpKick || mbFalling)
+ return;
+
+ CharacterController::eIntention theIntention = mpCharacter->GetController()->GetIntention();
+ if ( theIntention == CharacterController::Jump && !mbJumpAgain)
+ {
+ mpCharacter->GetController()->ClearIntention();
+
+ rmt::Vector outnorm;
+ mpCharacter->GetTerrainIntersect( groundPos, outnorm );
+
+ static float allowJump = 4.0f;
+
+ float fHeightAboveGround = currentPos.y - groundPos.y;
+ bool doJump = (fUpVelocity > 0) ?
+ (fUpVelocity <= CharacterTune::sfDoubleJumpAllowUp) :
+ (rmt::Abs(fUpVelocity) <= CharacterTune::sfDoubleJumpAllowDown);
+ if ( doJump )
+ {
+ if(mpAnimationDriver)
+ {
+ mpCharacter->GetPuppet()->StopDriver(mpAnimationDriver);
+ }
+ Reset(CharacterTune::sfDoubleJumpHeight);
+ mbJumpAgain = true;
+ mJumpState = PreJump;
+ GetEventManager()->TriggerEvent( EVENT_DOUBLEJUMP, mpCharacter );
+ }
+ else
+ {
+ // No chaining of actions.
+ //
+ mpCharacter->GetController()->SetIntention( CharacterController::NONE );
+ }
+ }
+ else if ( theIntention == CharacterController::Attack )
+ {
+ if(mbJumpAgain)
+ {
+ if(mpAnimationDriver )
+ {
+ mpCharacter->GetPuppet()->StopDriver( mpAnimationDriver );
+ }
+
+ tRefCounted::Assign(mpAnimationDriver,mpCharacter->GetPuppet()->PlayAnimation( "stomp_antic" ));
+// mpAnimationDriver = mpCharacter->GetPuppet()->PlayAnimation( "stomp_in_air" );
+ mbPreSlam = true;
+ mbSlam = true;
+ }
+ else
+ {
+ if(mpAnimationDriver )
+ {
+ mpCharacter->GetPuppet()->StopDriver( mpAnimationDriver );
+ }
+
+ tRefCounted::Assign(mpAnimationDriver,mpCharacter->GetPuppet()->PlayAnimation( "jump_kick" ));
+ mbInJumpKick = mbDoJumpKick = true;
+ mpCharacter->DoKickwave();
+ }
+ }
+}
+
+/*
+==============================================================================
+JumpAction::Clear
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: void
+
+=============================================================================
+*/
+void JumpAction::Clear()
+{
+ choreo::Puppet* pPuppet = mpCharacter->GetPuppet();
+ mpCharacter->SetJumping( false );
+ mJumpState = JumpDone;
+ rAssert( pPuppet );
+ pPuppet->StopDriver ( mpAnimationDriver );
+ pPuppet->StopDriver( mpRootDriver );
+ mpCharacter->SetTurbo( false );
+ endPosition = mpRootController->GetPosition( );
+ startPosition.y = endPosition.y = 0.0f;
+ startPosition.Sub( endPosition );
+ rDebugPrintf( "Distance: %.2f\n", startPosition.Magnitude( ) );
+ rDebugPrintf( "Height: %.2f\n", sfJumpHeightHighWater );
+ rDebugPrintf( "Time: %.2f\n", mfTime );
+ rDebugPrintf( "-----------------\n" );
+}
+
+//
+//=========================== DODGE ACTION ===============================
+//
+
+DodgeAction::DodgeAction( Character* pCharacter )
+:
+mpAnimationDriver( NULL ), // Set when we start playing the dodge animation and need to store its handle
+mpCharacter(pCharacter)
+{
+ rAssert( mpCharacter != NULL );
+
+}
+
+void DodgeAction::WakeUp( float timeins )
+{
+ rAssert( mpAnimationDriver == NULL );
+ mpAnimationDriver = mpCharacter->GetPuppet()->PlayAnimation( "dodge" );
+ rAssert( mpAnimationDriver != NULL );
+
+ mpCharacter->GetPuppet()->SetIsDriverIndefinite( mpAnimationDriver, false );
+ mpCharacter->SetBusy( true );
+}
+
+void DodgeAction::DoSimulation( float timeins)
+{
+ rAssert( mpCharacter != NULL );
+
+ // check that the character has a puppet... cuz that's where we're getting
+ // all our facing information.
+ choreo::Puppet* pPuppet = mpCharacter->GetPuppet();
+ rAssert( pPuppet );
+
+ rmt::Vector dir;
+ mpCharacter->GetDesiredFacing( dir );
+ float fDesiredDir = choreo::GetWorldAngle( dir.x, dir.z );
+ float fActualDir = SolveActualDir( fDesiredDir, timeins );
+ mpCharacter->SetFacingDir( fActualDir );
+}
+
+void DodgeAction::Update( float timeins)
+{
+ rAssert( mpAnimationDriver != NULL );
+ if( mpCharacter->GetPuppet()->IsDriverFinished( mpAnimationDriver ) )
+ {
+ Done();
+ }
+}
+
+void DodgeAction::Clear()
+{
+ mpCharacter->SetBusy( false );
+ mpCharacter->GetPuppet()->StopDriver( mpAnimationDriver );
+ mpAnimationDriver = NULL;
+}
+
+float DodgeAction::SolveActualDir( float fDesiredDir, float timeins )
+{
+ float rotateDelta = choreo::GetSmallestArc( mpCharacter->GetFacingDir( ), fDesiredDir );
+ float newDir = mpCharacter->GetFacingDir( ) + rotateDelta;
+ return newDir;
+}
+
+float DodgeAction::SolveActualSpeed( float fInputSpeed, float timeins )
+{
+ return fInputSpeed;
+}
+
+
+
+//
+//=========================== CRINGE ACTION ===============================
+//
+
+
+CringeAction::CringeAction( Character* pCharacter )
+:
+mpAnimationDriver( NULL ), // Set when we start playing the animation and need to store its handle
+mpCharacter(pCharacter)
+{
+ rAssert( mpCharacter != NULL );
+}
+
+void CringeAction::WakeUp( float timeins )
+{
+ rAssert( mpAnimationDriver == NULL );
+ mpAnimationDriver = mpCharacter->GetPuppet()->PlayAnimation( "cringe" );
+ rAssert( mpAnimationDriver != NULL );
+
+ mpCharacter->GetPuppet()->SetIsDriverIndefinite( mpAnimationDriver, false );
+}
+
+void CringeAction::DoSimulation( float timeins)
+{
+}
+
+void CringeAction::Update( float timeins)
+{
+ rAssert( mpAnimationDriver != NULL );
+ if( mpCharacter->GetPuppet()->IsDriverFinished( mpAnimationDriver ) )
+ {
+ Done();
+ }
+}
+
+void CringeAction::Clear()
+{
+ mpCharacter->GetPuppet()->StopDriver( mpAnimationDriver );
+ mpAnimationDriver = NULL;
+}
+
+float CringeAction::SolveActualDir( float fDesiredDir, float timeins )
+{
+ float rotateDelta = choreo::GetSmallestArc( mpCharacter->GetFacingDir( ), fDesiredDir );
+ float newDir = mpCharacter->GetFacingDir( ) + rotateDelta;
+ return newDir;
+}
+
+float CringeAction::SolveActualSpeed( float fInputSpeed, float timeins )
+{
+ return fInputSpeed;
+}
+
+//
+//=========================== FLAIL ACTION ===============================
+//
+FlailAction::FlailAction( Character* pCharacter )
+:
+mpAnimationDriver( NULL ), // Set when we start playing the animation and need to store its handle
+mpCharacter(pCharacter)
+{
+ rAssert( mpCharacter != NULL );
+
+}
+
+void FlailAction::WakeUp( float timeins )
+{
+ // The animation driver should modify the puppet's position while it's
+ // playing...
+ rAssert( mpAnimationDriver == NULL );
+ mpAnimationDriver = mpCharacter->GetPuppet()->PlayAnimation( "flail" );
+ rAssert( mpAnimationDriver != NULL );
+
+ mpAnimationDriver->SetIsCyclic( true );
+ //mpAnimationDriver->SetPriority( 0 );
+ mpCharacter->GetPuppet()->SetIsDriverIndefinite( mpAnimationDriver, true );
+
+ mpCharacter->SetBusy( true );
+}
+
+void FlailAction::DoSimulation( float timeins)
+{
+ // Something else is changing my motion root for me, so I do nothing
+}
+
+void FlailAction::Update( float timeins)
+{
+ rAssert( mpAnimationDriver != NULL );
+
+ if( mpCharacter->GetSimState()->GetControl() == sim::simAICtrl )
+ {
+ Done();
+ }
+}
+
+float FlailAction::SolveActualDir( float fDesiredDir, float timeins )
+{
+ float rotateDelta = choreo::GetSmallestArc( mpCharacter->GetFacingDir( ), fDesiredDir );
+ float newDir = mpCharacter->GetFacingDir( ) + rotateDelta;
+ return newDir;
+}
+
+float FlailAction::SolveActualSpeed( float fInputSpeed, float timeins )
+{
+ return fInputSpeed;
+}
+
+void FlailAction::Clear()
+{
+ mpCharacter->GetPuppet()->StopDriver( mpAnimationDriver );
+ mpAnimationDriver = NULL;
+ mpCharacter->SetBusy( false );
+}
+
+
+//
+//=========================== GETUP ACTION ===============================
+//
+GetUpAction::GetUpAction( Character* pCharacter ) :
+mpAnimationDriver( NULL ) // Set when we start playing the animation and need to store its handle
+{
+ mpCharacter = pCharacter;
+ rAssert( mpCharacter != NULL );
+
+}
+
+void GetUpAction::WakeUp( float timeins )
+{
+ /*
+ static const float angle = 0.93969262078590838405410927732473f; // cos(20)
+ static const rmt::Vector upVector(0.0f, 1.0f, 0.0f);
+ if(mpCharacter->GetLean().Dot(upVector) > angle)
+ {
+ Done();
+ return;
+ }
+ */
+
+ rAssert( mpAnimationDriver == NULL );
+ mpAnimationDriver = mpCharacter->GetPuppet()->PlayAnimation( "get_up" );
+ rAssert( mpAnimationDriver != NULL );
+
+ mpCharacter->GetPuppet()->SetIsDriverIndefinite( mpAnimationDriver, false );
+
+ rmt::Vector up, right, forward;
+ //right = mpCharacter->mPrevSimTransform.Row(0);
+ up = mpCharacter->mPrevSimTransform.Row(1);
+ //forward = mpCharacter->mPrevSimTransform.Row(2);
+
+ // NOTE:
+ // Setting mDirection using SetDirection as done below doesn't
+ // have any effect if speed is 0.0f. When character queries
+ // this controller for direction, it returns (0.0f,0.0f,0.0f)
+ // because it has been scaled by zero velocity.
+ //SetDirection( up );
+
+ // Use the "up" vector because the character comes out of flailing
+ // when it stops "rolling" on the ground. So the "up" when it was on
+ // the ground should be the facing direction of the get-up anim.
+ float dir = choreo::GetWorldAngle( up.x, up.z );
+ mpCharacter->SetDesiredDir( dir );
+ mpCharacter->SetFacingDir( dir );
+}
+
+void GetUpAction::DoSimulation( float timeins)
+{
+}
+
+void GetUpAction::Update( float timeins)
+{
+ if( mpAnimationDriver == NULL )
+ {
+ return;
+ }
+
+ if( mpCharacter->GetPuppet()->IsDriverFinished( mpAnimationDriver ) )
+ {
+ Done();
+ }
+
+ // if we've gone back into simulation, abort, and cue another flail and get up
+ if( mpCharacter->GetSimState()->GetControl() == sim::simSimulationCtrl)
+ {
+ // Sequence in the flail and get-up actions
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new FlailAction( mpCharacter ) );
+ pSeq->EndSequence();
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new GetUpAction( mpCharacter ) );
+ pSeq->EndSequence();
+
+ Done();
+ }
+}
+
+void GetUpAction::Clear()
+{
+ if(mpAnimationDriver)
+ {
+ mpCharacter->GetPuppet()->StopDriver( mpAnimationDriver );
+ mpAnimationDriver = NULL;
+ }
+}
+
+float GetUpAction::SolveActualDir( float fDesiredDir, float timeins )
+{
+ float rotateDelta = choreo::GetSmallestArc( mpCharacter->GetFacingDir( ), fDesiredDir );
+ float newDir = mpCharacter->GetFacingDir( ) + rotateDelta;
+ return newDir;
+}
+
+float GetUpAction::SolveActualSpeed( float fInputSpeed, float timeins )
+{
+ return fInputSpeed;
+}
+
+//
+//=========================== CHANGELOCOMOTION ACTION ===============================
+//
+void ChangeLocomotion::Update( float timeins )
+{
+ if(mType == WALKING)
+ {
+ mpCharacter->SetInCar( false );
+ mpCharacter->UpdateTransformToLoco();
+ Done( );
+ }
+ else if(mType == INCAR)
+ {
+ mpCharacter->SetInCar( true );
+ mpCharacter->UpdateTransformToInCar();
+ Done( );
+ }
+}
+
+
+//
+//=========================== CHANGENPCCONTROLLERSTATE ACTION ===============================
+//
+
+void ChangeNPCControllerState::Update( float timeins )
+{
+ Abort();
+ Done();
+}
+
+void ChangeNPCControllerState::Abort()
+{
+ // NOTE:
+ // We put the actual call to TransitToState in Abort() because
+ // we want to guarantee that this is done. The problem is that
+ // we get into situations where a sequenced action (like this one)
+ // can get prematurely cleared by one thing or another. Since this
+ // particular action is necessary for synching between the controller
+ // state and the action performed, we ALWAYS want to do it...
+
+ rAssert( mpCharacter->IsNPC() );
+ rAssert( dynamic_cast<NPCController*>(mpCharacter->GetController()) );
+ ((NPCController*)mpCharacter->GetController())->TransitToState(mState);
+ Action::Abort();
+}
+
+//
+//=========================== KICK ACTION ===============================
+//
+void KickAction::Update( float timeins )
+{
+ mTime += timeins;
+ if(mTime > mContact)
+ {
+ mpCharacter->Kick();
+ Done();
+ }
+}
+
+//
+//=========================== SURF ACTION ===============================
+//
+SurfAction::SurfAction(Character* c) : PlayAnimationAction(c, "surf_cycle"), mLowCount(0)
+{
+ SetLoop(true);
+ AbortWhenMovementOccurs(true);
+}
+
+bool SurfAction::ShouldAbort(void)
+{
+ return PlayAnimationAction::ShouldAbort() || (mLowCount > 16);
+}
+
+void SurfAction::Update(float t)
+{
+ rmt::Vector v;
+ mpCharacter->GetVelocity(v);
+ if(v.MagnitudeSqr() < 100.0f)
+ {
+ mLowCount += 1;
+ }
+ else
+ {
+ mLowCount = 0;
+ }
+
+ PlayAnimationAction::Update(t);
+
+ CharacterController::eIntention theIntention = mpCharacter->GetController()->GetIntention();
+ if ( theIntention == CharacterController::Jump )
+ {
+ Done();
+ JumpAction* pAction = mpCharacter->GetJumpLocomotionAction();
+ pAction->Reset(CharacterTune::sfJumpHeight, false);
+ mpCharacter->GetActionController()->SequenceSingleAction(pAction);
+ mpCharacter->GetController()->ClearIntention();
+ }
+}
diff --git a/game/code/ai/sequencer/action.h b/game/code/ai/sequencer/action.h
new file mode 100644
index 0000000..3d586e3
--- /dev/null
+++ b/game/code/ai/sequencer/action.h
@@ -0,0 +1,790 @@
+#ifndef _SEQUENCER_ACTION_HPP
+#define _SEQUENCER_ACTION_HPP
+//-----------------------------------------------------------------------------
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// action.hpp
+//
+// Description: An atomic unit of execution in the Sequencer, the Action class
+// is the base class for all logical actions that operate on
+// a character for movement and animation.
+//
+// Modification History:
+// + Created Aug 14, 2001 -- Gary Keong
+// - Snapshot from Hair Club (rev 1) Owner: Laurent Ancessi
+//-----------------------------------------------------------------------------
+
+//---------------------------------------------------------------------
+// Includes
+//---------------------------------------------------------------------
+
+//========================================
+// Nested Includes
+//========================================
+#include <memory/memorypool.h>
+#include <p3d/memory.hpp>
+
+#include <ai/sequencer/task.h>
+#include <ai/statemanager.h>
+#include <p3d/p3dtypes.hpp>
+#include <events/eventenum.h>
+#include <events/eventmanager.h>
+
+#include <worldsim/character/charactercontroller.h>
+#include <radmath/radmath.hpp>
+#include <worldsim/redbrick/vehicle.h>
+
+class tName;
+
+class Action: public Task
+{
+public:
+
+ virtual ~Action();
+
+ virtual bool IsSlave() const;
+
+ virtual void WakeUp(float time);
+ virtual void DoSimulation(float time);
+ virtual void Update(float time);
+ virtual void Clear();
+ virtual void Abort() {};
+
+ // Pool allocators.
+ //
+ static void* operator new( size_t size )
+ {
+ void *memPtr = sMemoryPool.Allocate( size );
+#ifdef RADLOAD_HEAP_DEBUGGING
+ radLoadHeapDebugAddAddress( memPtr );
+#endif
+ return memPtr;
+ }
+ static void operator delete( void* deadObject, size_t size )
+ {
+ sMemoryPool.Free( deadObject, size );
+//This is done in the radObject...
+//#ifdef RADLOAD_HEAP_DEBUGGING
+// radLoadHeapDebugRemoveAddress( deadObject );
+//#endif
+ }
+
+ // Declared but not defined to prevent access.
+ static void* operator new[]( size_t size );
+ static void operator delete[]( void* pMemory );
+
+private:
+ static FBMemoryPool sMemoryPool;
+};
+
+class SlaveAction: public Action
+{
+public:
+
+ virtual bool IsSlave() const;
+};
+namespace choreo
+{
+ class AnimationDriver;
+ class RootTransformController;
+ class RootDriver;
+ class Locomotion;
+ class LocomotionDriver;
+}
+
+class TriggerEventAction
+:
+public Action
+{
+public:
+ TriggerEventAction( EventEnum eventId, void* pUserData )
+ :
+ mEventId( eventId ),
+ mpUserData( pUserData )
+ {
+ }
+ virtual void WakeUp( float timeins )
+ {
+ GetEventManager()->TriggerEvent( mEventId, mpUserData );
+ Done( );
+ }
+private:
+ EventEnum mEventId;
+ void* mpUserData;
+};
+
+
+class DelayAction
+:
+public Action
+{
+public:
+ DelayAction( float timeins ) : mTime( timeins) { }
+
+ virtual void Update( float timeins )
+ {
+ mTime -= timeins;
+ if(mTime <= 0.0f)
+ {
+ Done( );
+ }
+ }
+
+private:
+ float mTime;
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// class WalkerLocomotionAction
+//
+//
+//////////////////////////////////////////////////////////////////////////
+class WalkerLocomotionAction : public SlaveAction
+{
+public:
+ WalkerLocomotionAction( Character* pCharacter );
+ ~WalkerLocomotionAction( );
+
+ virtual void WakeUp( float timeins );
+ virtual void DoSimulation( float timeins);
+ virtual void Update( float timeins);
+ void PlayDriver(void);
+ void StopDriver(void);
+
+ void SetDesiredSpeed( float fSpeed )
+ {
+ mfDesiredSpeed = fSpeed;
+ }
+
+ void SwitchLocomotion(void);
+
+ choreo::LocomotionDriver* GetDriver(void) { return mpLocomotionDriver;}
+
+ void AllowIdle(bool b) { mAllowIdle = b; }
+
+protected:
+ static rmt::Randomizer sRandom;
+ static bool sRandomSeeded;
+
+ float SolveActualDir( float fDesiredDir, float timeins );
+ float SolveActualSpeed( float fDesiredSpeed, float timeins );
+
+ float mfDesiredSpeed;
+ float mIdleTime;
+ float mNextIdle;
+ bool mAllowIdle : 1;
+
+ Character* mpCharacter;
+
+ choreo::Locomotion* mpLocomotion;
+ choreo::LocomotionDriver* mpLocomotionDriver;
+};
+
+//////////////////////////////////////////////////////////////////////////
+class InCarAction : public Action
+{
+public:
+ InCarAction( Character* pCharacter );
+ ~InCarAction( );
+
+ virtual void WakeUp( float timeins );
+ virtual void DoSimulation( float timeins);
+ virtual void Update( float timeins);
+ virtual void Clear( void );
+
+protected:
+ void IWannaRock(bool);
+
+ Character* mpCharacter;
+ choreo::AnimationDriver* mpAnimationDriver;
+ bool mIsDriver : 1;
+ bool rockinIsMyBusiness : 1;
+ float timeBetweenBeats;
+ float timeSinceLastBeat;
+ float lastBeatValue;
+
+ float mIdleTime;
+};
+
+//////////////////////////////////////////////////////////////////////////
+class HoldAnimationAction : public Action
+{
+public:
+ HoldAnimationAction( Character* pCharacter, const char* animName, float holdFrame);
+ ~HoldAnimationAction( );
+
+ virtual void WakeUp( float timeins );
+ virtual void DoSimulation( float timeins);
+ virtual void Update( float timeins);
+ virtual void Clear( void );
+
+protected:
+ virtual bool ShouldRelease(void) = 0;
+
+ Character* mpCharacter;
+ tUID mAnimUID;
+ choreo::AnimationDriver* mpAnimationDriver;
+ float mHoldFrame;
+ float mOrigFrames;
+};
+
+class SteerAction : public HoldAnimationAction
+{
+public:
+
+ SteerAction( Character* pCharacter, const char* anim, float frame);
+
+protected:
+ virtual bool ShouldRelease(void);
+
+};
+
+class ReverseAction : public HoldAnimationAction
+{
+public:
+ ReverseAction( Character* pCharacter, const char* anim, float frame);
+
+protected:
+ virtual bool ShouldRelease(void);
+ float mRelease;
+
+};
+
+class AccelAction : public HoldAnimationAction
+{
+public:
+ AccelAction( Character* pCharacter, const char* anim, float frame);
+
+protected:
+ virtual bool ShouldRelease(void);
+
+};
+
+class DecelAction : public HoldAnimationAction
+{
+public:
+ DecelAction( Character* pCharacter, const char* anim, float frame);
+
+protected:
+ virtual bool ShouldRelease(void);
+
+};
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// class JumpAction
+//
+//
+//////////////////////////////////////////////////////////////////////////
+class JumpAction
+:
+public Action
+{
+public:
+ JumpAction( Character* pCharacter, const char* name, float fUpVelocity );
+ virtual ~JumpAction( void );
+ enum eJumpState
+ {
+ NoTranslate = 1 << 16,
+ InitJump,
+ PostTurboJumpRecover,
+ IdleJump,
+
+ AllowTranslate = 1 << 17,
+ PreJump,
+ Jump,
+ PostJump,
+ JumpDone
+ };
+ virtual void WakeUp( float timeins );
+ float Reset( float fJumpHeight, bool bFalling = false );
+ void Reset( const rmt::Vector& jumpTarget, bool bFalling = false );
+ virtual void DoSimulation( float timeins );
+ virtual void Update( float timeins );
+ void TestForJumpAgain( const rmt::Vector& currentPos, float fUpVelocity, rmt::Vector& groundPos );
+ virtual void Clear( );
+
+ void SetRootTransform( void );
+ void SetRootPosition( const rmt::Vector& position );
+ bool IsInJump( void )
+ {
+ return mJumpState == Jump
+ // if we are in the PreJump state with no animation,
+ // then we are as good as jumping.
+ // had to do it for the launching platforms.
+ // Bounce.
+ //
+ || ( mJumpState == PreJump && mpAnimationDriver == 0 )
+ || ( mJumpState == PreJump && mbJumpAgain );
+ }
+
+ bool IsJumpState( eJumpState state )
+ {
+ return ( state == mJumpState );
+ }
+ const rmt::Vector& GetJumpVelocity( void ) const
+ {
+ return mVelocity;
+ }
+
+ float GetGravity( void ) const
+ {
+ return mfGravity;
+ }
+
+protected:
+ void SetJumpVelocity( float fVelocity )
+ {
+ mVelocity.y = fVelocity;
+ }
+ float SolveActualDir( float fDesiredDir, float timeins );
+ float SolveActualSpeed( float fDesiredSpeed, float timeins );
+private:
+
+ Character* mpCharacter;
+ eJumpState mJumpState;
+ rmt::Vector mVelocity;
+ tUID mAnimUid;
+ float mfTime;
+ float mfGravity;
+ float mfStartHeight;
+ float mfJumpHeight;
+ float mfMaxSpeed;
+ choreo::AnimationDriver* mpAnimationDriver;
+ choreo::RootTransformController* mpRootController;
+ choreo::RootDriver* mpRootDriver;
+ bool mbBoost : 1;
+ bool mbFalling : 1;
+ bool mbJumpAgain : 1;
+ bool mbTurboJump : 1;
+ bool mbInJumpKick : 1;
+ bool mbDoJumpKick : 1;
+ bool mbSlam : 1;
+ bool mbPreSlam : 1;
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// class DodgeAction
+//
+//
+//////////////////////////////////////////////////////////////////////////
+
+class DodgeAction
+:
+public Action
+{
+
+public:
+ DodgeAction( Character* pCharacter );
+
+ virtual void WakeUp( float timeins );
+ virtual void DoSimulation( float timeins);
+ virtual void Update( float timeins);
+ virtual void Clear();
+
+protected:
+ float SolveActualDir( float fDesiredDir, float timeins );
+ float SolveActualSpeed( float fDesiredSpeed, float timeins );
+
+ choreo::AnimationDriver* mpAnimationDriver; // stores handle for dodge animation
+ Character* mpCharacter;
+};
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// class CringeAction
+//
+//
+//////////////////////////////////////////////////////////////////////////
+
+class CringeAction
+:
+public Action
+{
+
+public:
+ CringeAction( Character* pCharacter );
+
+ virtual void WakeUp( float timeins );
+ virtual void DoSimulation( float timeins);
+ virtual void Update( float timeins);
+ virtual void Clear();
+
+protected:
+ float SolveActualDir( float fDesiredDir, float timeins );
+ float SolveActualSpeed( float fDesiredSpeed, float timeins );
+ choreo::AnimationDriver* mpAnimationDriver; // stores handle for dodge animation
+ Character* mpCharacter;
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// class FlailAction
+//
+//
+//////////////////////////////////////////////////////////////////////////
+
+class FlailAction
+:
+public Action
+{
+
+public:
+ FlailAction( Character* pCharacter );
+
+ virtual void WakeUp( float timeins );
+ virtual void DoSimulation( float timeins);
+ virtual void Update( float timeins);
+ virtual void Clear();
+
+
+protected:
+ float SolveActualDir( float fDesiredDir, float timeins );
+ float SolveActualSpeed( float fDesiredSpeed, float timeins );
+
+ choreo::AnimationDriver* mpAnimationDriver; // stores handle for dodge animation
+ Character* mpCharacter;
+};
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// class GetUpAction
+//
+//
+//////////////////////////////////////////////////////////////////////////
+
+class GetUpAction
+:
+public Action
+{
+
+public:
+ GetUpAction( Character* pCharacter );
+
+ virtual void WakeUp( float timeins );
+ virtual void DoSimulation( float timeins);
+ virtual void Update( float timeins);
+ virtual void Clear();
+
+protected:
+ float SolveActualDir( float fDesiredDir, float timeins );
+ float SolveActualSpeed( float fDesiredSpeed, float timeins );
+ choreo::AnimationDriver* mpAnimationDriver; // stores handle for dodge animation
+ Character* mpCharacter;
+};
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// class PlayAnimationAction
+//
+//
+//////////////////////////////////////////////////////////////////////////
+
+class PlayAnimationAction: public Action
+{
+public:
+ PlayAnimationAction( Character* pCharacter, const char* anim, float fFrameRate = 30.0f );
+ PlayAnimationAction( Character* pCharacter, const tName& anim, float fFrameRate = 30.0f );
+
+ virtual void WakeUp( float timeins );
+ virtual void DoSimulation( float timeins );
+ virtual void Update( float timeins );
+ virtual void Clear( void );
+ void AbortWhenMovementOccurs( bool abort );
+ void SetLoop(bool b) { loop = b;}
+
+private:
+ PlayAnimationAction( void );
+
+protected:
+ virtual bool ShouldAbort(void);
+
+ Character* mpCharacter;
+ tUID mAnimUID;
+ float mfFrameRate;
+ choreo::AnimationDriver* mpAnimationDriver;
+ bool mbAbortWhenMovementOccurs : 1;
+ bool loop : 1;
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// class PlayIdleAnimationAction
+//
+//
+//////////////////////////////////////////////////////////////////////////
+
+class PlayIdleAnimationAction: public PlayAnimationAction
+{
+public:
+ PlayIdleAnimationAction( Character* pCharacter, const char* anim, float fFrameRate = 30.0f );
+ PlayIdleAnimationAction( Character* pCharacter, const tName& anim, float fFrameRate = 30.0f );
+
+ virtual void WakeUp( float timeins );
+ virtual void Clear( void );
+ virtual void Abort( void );
+
+private:
+ PlayIdleAnimationAction( void );
+
+protected:
+};
+
+
+
+//////////////////////////////////////////////////////////////////////////
+//
+// class CarDoorAction
+//
+// Action for opening and closing car doors
+//
+//////////////////////////////////////////////////////////////////////////
+
+class Sequencer;
+
+class CarDoorAction: public Action
+{
+public:
+ CarDoorAction( Vehicle* vehicle, Vehicle::DoorAction, Vehicle::Door, float delay, float timeins, Character* character = NULL, Sequencer* seq = NULL);
+
+ virtual void WakeUp( float timeins );
+ virtual void DoSimulation( float timeins );
+ virtual void Update( float timeins );
+ virtual void Clear( void );
+
+private:
+ CarDoorAction( void );
+
+ Vehicle* mpVehicle;
+ Vehicle::DoorAction mAction;
+ Vehicle::Door mDoor;
+ float mTime;
+ float mDelay;
+ float mCurrentTime;
+
+ Character* character;
+ Sequencer* sequencer;
+};
+
+class ReleaseDoorsAction: public Action
+{
+public:
+ ReleaseDoorsAction( Vehicle* v ) : mVehicle(v) {};
+ virtual void Update( float timeins );
+
+protected:
+ Vehicle* mVehicle;
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// class Arrive
+//
+//
+//////////////////////////////////////////////////////////////////////////
+class Arrive
+:
+public Action
+{
+public:
+ Arrive( Character* pCharacter, rmt::Vector& destination, bool strict = false );
+ virtual ~Arrive( void );
+ virtual void WakeUp( float timeins );
+ virtual void DoSimulation( float timeins );
+ virtual void Update( float timeins );
+ virtual void Clear( );
+protected:
+private:
+ Character* mpCharacter;
+ rmt::Vector mDestination;
+ float mfDecelTime;
+ float mfMaxSpeed;
+ float mEstTime;
+ float mElapsedTime;
+ bool mbSolveState : 1;
+ bool mbPrevLocoMode : 1;
+ bool mStrict : 1;
+};
+
+class FragileArrive : public Arrive
+{
+public:
+ FragileArrive( Character* pCharacter, rmt::Vector& destination, bool strict = false ) :
+ Arrive( pCharacter, destination, strict ) {}
+ bool IsSlave() const { return true; }
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// class Orient
+//
+//
+//////////////////////////////////////////////////////////////////////////
+class Orient
+:
+public Action
+{
+public:
+ Orient( Character* pCharacter, rmt::Vector& facing );
+ virtual ~Orient( void );
+
+ virtual void WakeUp( float timeins );
+ virtual void DoSimulation( float timeins );
+ virtual void Update( float timeins );
+protected:
+private:
+ Character* mpCharacter;
+ float mfTarget;
+};
+//////////////////////////////////////////////////////////////////////////
+//
+// class Position
+//
+//
+//////////////////////////////////////////////////////////////////////////
+class Position
+:
+public Action
+{
+public:
+ Position( Character* pCharacter, rmt::Vector& position, float fDuration, bool local = false );
+ virtual ~Position( void );
+
+ virtual void WakeUp( float timeins );
+ virtual void DoSimulation( float timeins );
+ virtual void Update( float timeins );
+protected:
+private:
+ Character* mpCharacter;
+ rmt::Vector mDestination;
+ float mfDuration;
+ float mfTimeLeft;
+ bool mLocal : 1;
+};
+
+class GroundSnap
+:
+public Action
+{
+public:
+ GroundSnap( Character* pCharacter) : mpCharacter( pCharacter ) {}
+ virtual ~GroundSnap( void ) {}
+
+ virtual void WakeUp( float timeins );
+ virtual void DoSimulation( float timeins ) {}
+ virtual void Update( float timeins ) {}
+protected:
+private:
+ Character* mpCharacter;
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+// class AssignValueToFloat
+//
+//
+//////////////////////////////////////////////////////////////////////////
+class AssignValueToFloat
+:
+public Action
+{
+public:
+ AssignValueToFloat( float& refFloat, float fValue )
+ :
+ mRefFloat( refFloat ),
+ mfValue( fValue )
+ {
+ }
+ virtual void WakeUp( float timeins )
+ {
+ mRefFloat = mfValue;
+ Done( );
+ }
+private:
+ float& mRefFloat;
+ float mfValue;
+};
+
+
+class ChangeLocomotion
+:
+public Action
+{
+public:
+ enum LocoType { INCAR, WALKING };
+
+ ChangeLocomotion( Character* c, LocoType type) : mpCharacter(c), mType(type) { }
+
+ virtual void Update( float timeins );
+
+private:
+ Character* mpCharacter;
+ LocoType mType;
+};
+
+
+class ChangeNPCControllerState
+:
+public Action
+{
+public:
+ ChangeNPCControllerState( Character* c, NPCController::State state) : mpCharacter(c), mState(state) { }
+ virtual void Update( float timeins );
+ virtual void Abort( void );
+private:
+ Character* mpCharacter;
+ NPCController::State mState;
+};
+
+
+class KickAction
+:
+public SlaveAction
+{
+public:
+ KickAction( Character* c, float contact) : mpCharacter(c), mTime(0.0f), mContact(contact) { }
+
+ virtual void Update( float timeins );
+
+private:
+ Character* mpCharacter;
+ float mTime;
+ float mContact;
+};
+
+class SurfAction : public PlayAnimationAction
+{
+public:
+ SurfAction(Character*);
+
+protected:
+ unsigned mLowCount;
+ bool ShouldAbort(void);
+ void Update(float);
+};
+
+template <class STATE> class ChangeState
+:
+public Action
+{
+public:
+ ChangeState( Character* c) : mpCharacter(c) { }
+
+ virtual void Update( float timeins )
+ {
+ CharacterAi::SetState<STATE>( mpCharacter->GetStateManager() );
+ Done();
+ }
+
+private:
+ Character* mpCharacter;
+};
+
+
+#endif // HC_SEQUENCER_ACTION_HPP
diff --git a/game/code/ai/sequencer/actioncontroller.cpp b/game/code/ai/sequencer/actioncontroller.cpp
new file mode 100644
index 0000000..29f93a7
--- /dev/null
+++ b/game/code/ai/sequencer/actioncontroller.cpp
@@ -0,0 +1,69 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// actioncontroller.cpp
+//
+// Description: This class contains two sequencers, and handles
+// retrieval and swapping of these sequencers transparently.
+//
+// Modification History:
+// + Created Aug 14, 2001 -- Gary Keong
+// - Snapshot from Hair Club (rev 1) Owner: Bryan Brandt
+//-----------------------------------------------------------------------------
+
+#include <ai\sequencer\actioncontroller.h>
+
+//---------------------------------------------------------------------
+// ActionController class
+//---------------------------------------------------------------------
+
+ActionController::ActionController():
+ m_pCurrentSequencer(m_Sequencers + 1),
+ m_pNextSequencer(m_Sequencers)
+{
+}
+
+ActionController::~ActionController()
+{
+}
+
+void ActionController::Clear()
+{
+ m_Sequencers[0].Clear();
+ m_Sequencers[1].Clear();
+}
+
+void ActionController::WakeUp(float time)
+{
+ if (!m_pCurrentSequencer->IsBusy() && m_pNextSequencer->IsBusy())
+ {
+ // swap sequencers
+ m_pNextSequencer = m_pCurrentSequencer;
+
+ if (m_pNextSequencer == m_Sequencers)
+ {
+ m_pCurrentSequencer = (m_Sequencers + 1);
+ }
+ else
+ {
+ m_pCurrentSequencer = m_Sequencers;
+ }
+
+ // clear old sequencer so it's ready for new input
+ m_pNextSequencer->Clear();
+ }
+
+ m_pCurrentSequencer->WakeUp(time);
+}
+
+void ActionController::DoSimulation(float time)
+{
+ m_pCurrentSequencer->DoSimulation(time);
+}
+
+void ActionController::Update(float time)
+{
+ m_pCurrentSequencer->Update(time);
+}
+
+// End of file.
diff --git a/game/code/ai/sequencer/actioncontroller.h b/game/code/ai/sequencer/actioncontroller.h
new file mode 100644
index 0000000..b5341a3
--- /dev/null
+++ b/game/code/ai/sequencer/actioncontroller.h
@@ -0,0 +1,66 @@
+#ifndef _SEQUENCER_ACTIONCONTROLLER_HPP
+#define _SEQUENCER_ACTIONCONTROLLER_HPP
+//-----------------------------------------------------------------------------
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// actioncontroller.hpp
+//
+// Description: This class contains two sequencers, and handles
+// retrieval and swapping of these sequencers transparently.
+//
+// Modification History:
+// + Created Aug 14, 2001 -- Gary Keong
+// - Snapshot from Hair Club (rev 2) Owner: Bryan Brandt
+//-----------------------------------------------------------------------------
+
+//---------------------------------------------------------------------
+// Includes
+//---------------------------------------------------------------------
+
+// CLOSER #include "architecture/ctype.hpp"
+#include <ai\sequencer\sequencer.h>
+
+//---------------------------------------------------------------------
+// Structure and class
+//---------------------------------------------------------------------
+
+class ActionController
+{
+public:
+
+ ActionController();
+ virtual ~ActionController();
+
+ Sequencer* GetCurrentSequencer() const
+ { return m_pCurrentSequencer; }
+ Sequencer* GetNextSequencer() const
+ { return m_pNextSequencer; }
+
+ // clears ALL sequencers
+ void Clear();
+
+ bool IsBusy() const
+ { return (m_pCurrentSequencer->IsBusy() || m_pNextSequencer->IsBusy()); }
+
+ // main entries
+ void WakeUp(float time);
+ void DoSimulation(float time);
+ void Update(float time);
+
+ template <class T> T* SequenceSingleAction(T* t)
+ {
+ Sequencer* pSeq = GetNextSequencer();
+ pSeq->BeginSequence();
+ pSeq->AddAction(t);
+ pSeq->EndSequence();
+ return t;
+ }
+
+private:
+
+ Sequencer* m_pCurrentSequencer;
+ Sequencer* m_pNextSequencer;
+ Sequencer m_Sequencers[2];
+};
+
+#endif // HC_SEQUENCER_ACTIONCONTROLLER_HPP
diff --git a/game/code/ai/sequencer/allsequencer.cpp b/game/code/ai/sequencer/allsequencer.cpp
new file mode 100644
index 0000000..d11c67b
--- /dev/null
+++ b/game/code/ai/sequencer/allsequencer.cpp
@@ -0,0 +1,4 @@
+#include <ai/sequencer/action.cpp>
+#include <ai/sequencer/actioncontroller.cpp>
+#include <ai/sequencer/sequencer.cpp>
+#include <ai/sequencer/task.cpp>
diff --git a/game/code/ai/sequencer/sequencer.cpp b/game/code/ai/sequencer/sequencer.cpp
new file mode 100644
index 0000000..924b5f5
--- /dev/null
+++ b/game/code/ai/sequencer/sequencer.cpp
@@ -0,0 +1,315 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// sequencer.cpp
+//
+// Description: This class queues the actions of an actor.
+// The sequencer places actions into multiple queues,
+// named tracks, which can be grouped into larger composite
+// actions named sequences, which will be executed in turn.
+//
+// Modification History:
+// + Created Aug 14, 2001 -- Gary Keong
+// - Snapshot from Hair Club (rev 3) Owner: Bryan Brandt
+//-----------------------------------------------------------------------------
+
+#include <raddebug.hpp>
+#include <ai\sequencer\sequencer.h>
+#include <ai\sequencer\action.h>
+
+static bool s_StopClear = false;
+
+//---------------------------------------------------------------------
+// Sequencer class
+//---------------------------------------------------------------------
+
+Sequencer::Sequencer():
+ m_SequenceCount(0),
+ m_State(STATE_NONE),
+ m_TimeElapsed(0.0f)
+{
+}
+
+Sequencer::~Sequencer()
+{
+ Clear();
+}
+
+void Sequencer::ClearSequence(sSequence* seq)
+{
+ sAction* curAction = seq->actions;
+
+ // iterate over all actions of current sequence
+ for (unsigned i = 0; i < seq->actionCount; ++i)
+ {
+ if ( curAction->action )
+ {
+ if ( curAction->action->IsRunning() )
+ {
+ curAction->action->Clear();
+ }
+ // [Dusit: June 14th, 2003]
+ // always gets called to abort safely whether
+ // the action's running or not.
+ curAction->action->Abort();
+ }
+ tRefCounted::Release( curAction->action );
+ curAction->action = 0;
+
+ ++curAction;
+ }
+
+ seq->actionCount = 0;
+}
+
+void Sequencer::Clear()
+{
+ rAssert(!s_StopClear);
+ // empty all sequences
+ for (unsigned i = 0; i < m_SequenceCount; ++i)
+ {
+ ClearSequence(m_Sequences + i);
+ }
+
+ m_SequenceCount = 0;
+ m_State = STATE_NONE;
+ m_TimeElapsed = 0.0f;
+}
+
+void Sequencer::BeginSequence()
+{
+ rAssert(m_State == STATE_NONE);
+ rAssert(m_SequenceCount < MAX_SEQUENCES);
+
+ // add new sequence, set current sequence index to it,
+ // and reset current track to zero
+ m_pCurrentSequence = (m_Sequences + m_SequenceCount);
+ ++m_SequenceCount;
+
+ // reset current sequence
+ m_pCurrentSequence->actionCount = 0;
+
+ // indicate we can now add tracks/timed actions
+ m_State = STATE_SEQUENCE;
+}
+
+void Sequencer::EndSequence()
+{
+ rAssert(m_State == STATE_SEQUENCE);
+ m_State = STATE_NONE;
+}
+
+void Sequencer::AddAction(float t_begin, float t_duration, Action* action)
+{
+ // we need a sequence to add an action
+ rAssert(m_State != STATE_NONE);
+ rAssert(action != 0);
+
+ // allocate a new track for the action
+ sAction* curAction = (m_pCurrentSequence->actions + m_pCurrentSequence->actionCount);
+ ++(m_pCurrentSequence->actionCount);
+
+ // action runs from t_begin for t_duration seconds
+ curAction->timeBegin = t_begin;
+ curAction->timeDuration = t_duration;
+ curAction->action = action;
+ curAction->action->AddRef( );
+}
+
+void Sequencer::AddActionToSequence(float t_begin, float t_duration, Action* action)
+{
+ // we assume a sequence already exists
+ rAssert(m_State == STATE_NONE);
+ rAssert(m_SequenceCount > 0);
+ rAssert(action != 0);
+
+ // allocate a new track for the action
+ sAction* curAction = (m_pCurrentSequence->actions + m_pCurrentSequence->actionCount);
+ ++(m_pCurrentSequence->actionCount);
+
+ // we need to offset our times from NOW
+ curAction->timeBegin = (m_TimeElapsed + t_begin);
+ curAction->timeDuration = t_duration;
+ curAction->action = action;
+ curAction->action->AddRef( );
+}
+
+bool Sequencer::IsBusy() const
+{
+ if (m_SequenceCount <= 0)
+ return false;
+
+ unsigned actionIdx = 0;
+ const sAction* curAction = m_Sequences->actions;
+
+ // iterate over all tracks of current sequence
+ while (actionIdx < m_Sequences->actionCount)
+ {
+ // is track active?
+ if (curAction->action != 0)
+ return true;
+
+ ++actionIdx;
+ ++curAction;
+ }
+
+ // no tracks had actions in queue
+ return false;
+}
+
+void Sequencer::WakeUp(float time)
+{
+ if (m_SequenceCount <= 0)
+ return;
+
+// CLOSER??? m_TimeElapsed += SIMULATION_TIME;
+ m_TimeElapsed += time;
+
+ unsigned actionIdx = 0;
+ sAction* curAction = m_Sequences->actions;
+
+ // iterate over all tracks of current sequence
+ while (actionIdx < m_Sequences->actionCount)
+ {
+ // is track active?
+ if (curAction->action != 0)
+ {
+ // check to see if action should be running yet
+ if (curAction->timeBegin <= m_TimeElapsed)
+ {
+ Action* action = curAction->action;
+
+ // Is the current action sleeping?
+ if (action->IsSleeping())
+ {
+ // Wake-up the action.
+ action->WakeUp(time);
+ // Start the action only if wakeup didn't kill it
+ if (!action->IsDone())
+ {
+ action->Run();
+ }
+ }
+ }
+ }
+
+ ++actionIdx;
+ ++curAction;
+ }
+}
+
+void Sequencer::DoSimulation(float time)
+{
+ if (m_SequenceCount <= 0)
+ return;
+
+ unsigned actionIdx = 0;
+ sAction* curAction = m_Sequences->actions;
+
+ // iterate over all tracks of current sequence
+ while (actionIdx < m_Sequences->actionCount)
+ {
+ // is track active?
+ if (curAction->action != 0)
+ {
+ // check to see if action should be running yet
+ if (curAction->timeBegin <= m_TimeElapsed)
+ {
+ Action* action = curAction->action;
+
+ // Is the current action running? Yes, then do a simulation.
+ if (action->IsRunning())
+ {
+ action->DoSimulation(time);
+ }
+
+ // if action is non-indefinite, age it
+ if (curAction->timeDuration >= 0.0f)
+ {
+ // age the action
+ curAction->timeDuration -= time;
+
+ // if the action time has elapsed, finish it
+ if (curAction->timeDuration <= 0.0f)
+ {
+ action->Done();
+ }
+ }
+ }
+ }
+
+ ++actionIdx;
+ ++curAction;
+ }
+}
+
+void Sequencer::Update(float time)
+{
+ if (m_SequenceCount <= 0)
+ return;
+
+ unsigned masterCount = 0;
+
+ unsigned actionIdx = 0;
+ sAction* curAction = m_Sequences->actions;
+
+ // iterate over all tracks
+ while (actionIdx < m_Sequences->actionCount)
+ {
+ Action* action = curAction->action;
+
+ // does track have an action?
+ if (action != 0)
+ {
+ // Is the current action running? Yes, then get the new status.
+ if (action->IsRunning())
+ {
+ s_StopClear = true;
+ action->Update(time);
+ s_StopClear = false;
+ }
+
+ // Is the current action done or failed?
+ // Yes, then clear action
+ if (action->IsDone())
+ {
+ action->Clear();
+ action->Release( );
+ curAction->action = 0;
+ }
+ else
+ {
+ // accumulate master action count
+ if (!action->IsSlave())
+ {
+ ++masterCount;
+ }
+ }
+ }
+
+ ++actionIdx;
+ ++curAction;
+ }
+
+ if (masterCount <= 0)
+ {
+ // we have no master actions, so move on to
+ // the next sequence
+ ClearSequence(m_Sequences);
+
+ for (unsigned i = 1; i < m_SequenceCount; ++i)
+ {
+ m_Sequences[i-1] = m_Sequences[i];
+ }
+
+ if(m_SequenceCount)
+ {
+ --(m_SequenceCount);
+ }
+
+ // reset time origin for next sequence
+ m_TimeElapsed = 0.0f;
+ }
+}
+
+// End of file.
diff --git a/game/code/ai/sequencer/sequencer.h b/game/code/ai/sequencer/sequencer.h
new file mode 100644
index 0000000..e66dff7
--- /dev/null
+++ b/game/code/ai/sequencer/sequencer.h
@@ -0,0 +1,115 @@
+#ifndef _SEQUENCER_SEQUENCER_HPP
+#define _SEQUENCER_SEQUENCER_HPP
+
+//-----------------------------------------------------------------------------
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// sequencer.hpp
+//
+// Description: This class queues the actions of an actor.
+// The sequencer places actions into multiple queues,
+// named tracks, which can be grouped into larger composite
+// actions named sequences, which will be executed in turn.
+//
+// Modification History:
+// + Created Aug 14, 2001 -- Gary Keong
+// - Snapshot from Hair Club (rev 2) Owner: Bryan Brandt
+//-----------------------------------------------------------------------------
+
+//---------------------------------------------------------------------
+// Structure and class
+//---------------------------------------------------------------------
+
+class Action;
+
+class Sequencer
+{
+public:
+
+ enum { MAX_SEQUENCES = 16 };
+ enum { MAX_ACTIONS = 6 };
+
+ Sequencer();
+ virtual ~Sequencer();
+
+ bool IsBusy() const;
+ void Clear();
+
+ void BeginSequence();
+ void EndSequence();
+
+ // Dependent actions; calling these methods to add actions
+ // will queue each subsequent action up to occur after the
+ // previous action completes.
+ void AddAction(Action* action)
+ { AddAction(0.0f, -1.0f, action); }
+
+ // Set t_duration <= 0.0f to make action indefinite.
+ void AddAction(float t_duration, Action* action)
+ { AddAction(0.0f, t_duration, action); }
+
+ // Explicitly timed actions; these actions have an explicit
+ // start time independent of other actions' completion.
+ // Set t_duration <= 0.0f to make action indefinite.
+ void AddAction(float t_begin, float t_duration, Action* action);
+
+ // As above, but these can be called at any time, assuming
+ // there is a sequence currently running.
+ // This allows adding actions to the currently running sequencer
+ // on the fly. As such, it makes the most sense for these actions
+ // to be slaves.
+ void AddActionToSequence(Action* action)
+ { AddActionToSequence(0.0f, -1.0f, action); }
+ void AddActionToSequence(float t_duration, Action* action)
+ { AddActionToSequence(0.0f, t_duration, action); }
+ void AddActionToSequence(float t_begin, float t_duration, Action* action);
+
+ // main entries
+ void WakeUp(float time);
+ void DoSimulation(float time);
+ void Update(float time);
+
+ // get time elapsed
+ float GetTimeElapsed() const
+ { return m_TimeElapsed; }
+
+private:
+
+ // sAction - contains timing information
+ // and action itself
+ struct sAction
+ {
+ float timeBegin;
+ float timeDuration;
+ Action* action;
+ };
+
+ // sSequence - stores multiple parallel actions
+ struct sSequence
+ {
+ unsigned actionCount;
+ sAction actions[MAX_ACTIONS];
+ };
+
+ // queue of sequences, executed in order
+ unsigned m_SequenceCount;
+ sSequence m_Sequences[MAX_SEQUENCES];
+
+ // edit state - reference when adding sequences/tracks/actions
+ enum State
+ {
+ STATE_NONE,
+ STATE_SEQUENCE
+ };
+
+ State m_State;
+ sSequence* m_pCurrentSequence;
+
+ // amount of time passed for current sequence
+ float m_TimeElapsed;
+
+ void ClearSequence(sSequence* seq);
+};
+
+
+#endif // HC_SEQUENCER_SEQUENCER_HPP
diff --git a/game/code/ai/sequencer/task.cpp b/game/code/ai/sequencer/task.cpp
new file mode 100644
index 0000000..aabfb6c
--- /dev/null
+++ b/game/code/ai/sequencer/task.cpp
@@ -0,0 +1,73 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// task.hpp
+//
+// Description: A Sequencer unit of scheduling having a notion of execution
+// states.
+//
+// Modification History:
+// + Created Aug 14, 2001 -- Gary Keong
+// - Snapshot from Hair Club (rev 2) Owner: Laurent Ancessi
+//-----------------------------------------------------------------------------
+
+
+#include <ai\sequencer\task.h>
+
+//---------------------------------------------------------------------
+// Main functions
+//---------------------------------------------------------------------
+
+Task::Task(void)
+{
+ // Reset the class
+ m_status = SLEEPING;
+}
+
+Task::~Task(void)
+{
+}
+
+Status Task::GetStatus(void)
+{
+ // Return the status of the task
+ return m_status;
+}
+
+void Task::SetStatus(Status status)
+{
+ // Set the status of the task
+ m_status = status;
+}
+
+bool Task::IsSleeping(void)
+{
+ // Is the status set to SLEEPING
+ return m_status == SLEEPING;
+}
+
+void Task::Run(void)
+{
+ // Set the status to RUNNING
+ m_status = RUNNING;
+}
+
+bool Task::IsRunning(void)
+{
+ // Is the status set to RUNNING
+ return m_status == RUNNING;
+}
+
+void Task::Done(void)
+{
+ // Set the status to DONE
+ m_status = DONE;
+}
+
+bool Task::IsDone(void)
+{
+ // Is the status set to DONE
+ return m_status == DONE;
+}
+
+// End of file.
diff --git a/game/code/ai/sequencer/task.h b/game/code/ai/sequencer/task.h
new file mode 100644
index 0000000..14c4c7b
--- /dev/null
+++ b/game/code/ai/sequencer/task.h
@@ -0,0 +1,57 @@
+#ifndef _SEQUENCER_TASK_HPP
+#define _SEQUENCER_TASK_HPP
+//-----------------------------------------------------------------------------
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// task.hpp
+//
+// Description: A Sequencer unit of scheduling having a notion of execution
+// states.
+//
+// Modification History:
+// + Created Aug 14, 2001 -- Gary Keong
+// - Snapshot from Hair Club (rev 2) Owner: Laurent Ancessi
+//-----------------------------------------------------------------------------
+
+//---------------------------------------------------------------------
+// Includes
+//---------------------------------------------------------------------
+
+// CLOSER #include "architecture/ctype.hpp"
+
+//---------------------------------------------------------------------
+// Constant
+//---------------------------------------------------------------------
+#include <p3d/refcounted.hpp>
+
+enum Status
+{
+ DONE,
+ FAILED,
+ RUNNING,
+ SLEEPING
+};
+
+//---------------------------------------------------------------------
+// Structure and class
+//---------------------------------------------------------------------
+
+class Task
+:
+public tRefCounted
+{
+ public:
+ Task(void);
+ virtual ~Task(void);
+ Status GetStatus(void);
+ void SetStatus(Status status);
+ bool IsSleeping(void);
+ void Run(void);
+ bool IsRunning(void);
+ void Done(void);
+ bool IsDone(void);
+ private:
+ Status m_status;
+};
+
+#endif // HC_SEQUENCER_TASK_HPP
diff --git a/game/code/ai/state.cpp b/game/code/ai/state.cpp
new file mode 100644
index 0000000..355bf47
--- /dev/null
+++ b/game/code/ai/state.cpp
@@ -0,0 +1,1773 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: state.cpp
+//
+// Description: Implementation of class State
+//
+// History: 6/12/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <ai/state.h>
+#include <ai/statemanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactercontroller.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/hitnrunmanager.h>
+
+#include <ai/sequencer/action.h>
+#include <ai/sequencer/actioncontroller.h>
+#include <ai/sequencer/sequencer.h>
+#include <ai/actionbuttonhandler.h>
+
+#include <events/eventmanager.h>
+#include <events/eventenum.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h>
+#include <worldsim/redbrick/vehiclecontroller/vehiclemappable.h>
+#include <worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <simcollision/collisionmanager.hpp>
+
+#include <worldsim/avatarmanager.h>
+
+#include <interiors/interiormanager.h>
+
+#include <gameflow/gameflow.h>
+#include <main/game.h>
+
+#include <meta/locator.h>
+
+#include <cheats/cheatinputsystem.h>
+#include <cheats/cheats.h>
+#include <gameflow/gameflow.h>
+#include <contexts/contextenum.h>
+
+#include <camera/relativeanimatedcam.h>
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercam.h>
+
+#include <contexts/context.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreeniriswipe.h>
+
+
+namespace CharacterAi
+{
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+const int MAX_CLASS_SIZE = 32; // bytes.
+FBMemoryPool State::sMemoryPool( MAX_CLASS_SIZE, 32, GMA_LEVEL_OTHER );
+
+static const char* sGetOutPassenger[] =
+{
+ "get_out_of_car_open_door",
+ "get_out_of_car",
+ "get_out_of_car_close_door"
+};
+
+static const char* sGetOutDriver[] =
+{
+ "get_out_of_car_open_door_driver",
+ "get_out_of_car_driver",
+ "get_out_of_car_close_door_driver"
+};
+
+static const char* sGetOutHighPassenger[] =
+{
+ "get_out_of_car_open_door",
+ "get_out_of_car_high",
+ "get_out_of_car_close_door_high"
+};
+
+static const char* sGetOutHighDriver[] =
+{
+ "get_out_of_car_open_door_driver",
+ "get_out_of_car_high_driver",
+ "get_out_of_car_close_door_high_driver"
+};
+
+
+static const char* sGetInPassenger[] =
+{
+ "get_into_car_open_door",
+ "get_into_car",
+ "get_into_car_close_door"
+};
+
+static const char* sGetInDriver[] =
+{
+ "get_into_car_open_door_driver",
+ "get_into_car_driver",
+ "get_into_car_close_door_driver"
+};
+
+static const char* sGetInHighPassenger[] =
+{
+ "get_into_car_open_door_high",
+ "get_into_car_high",
+ "get_into_car_close_door"
+};
+
+static const char* sGetInHighDriver[] =
+{
+ "get_into_car_open_door_high_driver",
+ "get_into_car_high_driver",
+ "get_into_car_close_door_driver"
+};
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// State::State
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+State::State( Character* pCharacter )
+:
+mpCharacter( pCharacter )
+{
+}
+
+//==============================================================================
+// State::~State
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+State::~State()
+{
+ mpCharacter = 0;
+}
+
+/*
+==============================================================================
+InCar::InCar
+==============================================================================
+Description: Comment
+
+ Parameters:( Character* pCharacter )
+
+Return: InCar
+
+=============================================================================
+*/
+InCar::InCar( Character* pCharacter )
+:
+State( pCharacter )
+{
+ m_InCarAction = new InCarAction(pCharacter);
+ m_InCarAction->AddRef();
+
+ m_GetOutState = GETOUT_NONE;
+}
+
+InCar::~InCar( void )
+{
+ m_InCarAction->Release();
+}
+
+
+void InCar::Enter( void )
+{
+ if(!mpCharacter->GetTargetVehicle())
+ {
+ return;
+ }
+
+ mpCharacter->DoGroundIntersect(false);
+
+ m_GetOutState = GETOUT_NONE;
+
+ if(mpCharacter == GetCharacterManager()->GetCharacter(0))
+ {
+ GetVehicleCentral()->ActivateVehicleTriggers(false);
+ }
+
+ // if we did a git in, in car shoudl already be true, if not, need to force a little
+ if(!mpCharacter->IsInCar())
+ {
+ mpCharacter->GetActionController()->Clear();
+ mpCharacter->SetInCar( true );
+ mpCharacter->UpdateTransformToInCar();
+ }
+
+ mIsDriver = (mpCharacter->GetTargetVehicle()->mpDriver == mpCharacter) ||
+ (!mpCharacter->GetTargetVehicle()->HasDriver());
+
+ rmt::Vector seat = mIsDriver ?
+ mpCharacter->GetTargetVehicle()->GetDriverLocation() :
+ mpCharacter->GetTargetVehicle()->GetPassengerLocation();
+
+ bool busy = mpCharacter->GetActionController()->IsBusy();
+
+ if(!m_InCarAction->IsRunning())
+ {
+ m_InCarAction->SetStatus(SLEEPING);
+ }
+
+ // add the in car action (not very interesting) to the character sequencer
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+ pSeq->BeginSequence();
+ pSeq->AddAction( new Position(mpCharacter, seat, 0.0f, true));
+ if(!busy)
+ {
+ pSeq->AddAction( m_InCarAction);
+ }
+ pSeq->EndSequence();
+
+ mpCharacter->SetInCar( true );
+
+ if(!mpCharacter->GetTargetVehicle()->mVisibleCharacters)
+ {
+ mpCharacter->RemoveFromWorldScene();
+ }
+ else
+ {
+ mpCharacter->SetScale(mpCharacter->GetTargetVehicle()->mCharacterScale);
+ }
+
+ mpCharacter->RemoveFromPhysics();
+}
+
+void InCar::Exit( void )
+{
+ if(mpCharacter == GetCharacterManager()->GetCharacter(0))
+ {
+ Vehicle::sDoBounce = false;
+ }
+
+ this->m_InCarAction->Done();
+ this->m_InCarAction->Clear();
+
+ mpCharacter->AddToPhysics();
+
+ if( mpCharacter->GetTargetVehicle() != NULL )
+ {
+ if( (mpCharacter->GetTargetVehicle()->GetDriver() == mpCharacter) && (mpCharacter->GetRole() == Character::ROLE_PEDESTRIAN))
+ {
+ mpCharacter->GetTargetVehicle()->SetDriver(NULL);
+ }
+ }
+}
+
+void InCar::SequenceAction( void )
+{
+ Vehicle* pVehicle = mpCharacter->GetTargetVehicle( );
+
+ if(!pVehicle)
+ {
+ return;
+ }
+
+ rAssert(pVehicle);
+
+ if(!m_InCarAction->IsRunning())
+ {
+ m_InCarAction->SetStatus(SLEEPING);
+ }
+
+ // add the in car action (not very interesting) to the character sequencer
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+ pSeq->BeginSequence();
+ pSeq->AddAction( m_InCarAction);
+ pSeq->EndSequence();
+
+
+ rmt::Vector localPos = mpCharacter->GetPuppet()->GetPosition();
+ rmt::Vector seatPos;
+ if(mIsDriver)
+ {
+ seatPos = pVehicle->GetDriverLocation();
+ }
+ else
+ {
+ seatPos = pVehicle->GetPassengerLocation();
+ }
+ seatPos.y = localPos.y;
+ mpCharacter->GetPuppet()->SetPosition(seatPos);
+
+ if(mpCharacter == GetCharacterManager()->GetCharacter(0))
+ {
+ Vehicle::sDoBounce = true;
+ }
+}
+
+void InCar::Update( float timeins )
+{
+ Vehicle* pVehicle = mpCharacter->GetTargetVehicle( );
+
+ if((!pVehicle) ||
+ ((pVehicle->mVehicleDestroyed) &&
+ (GetGameFlow()->GetCurrentContext() != CONTEXT_DEMO) &&
+ (GetGameFlow()->GetCurrentContext() != CONTEXT_SUPERSPRINT)))
+ {
+ mpCharacter->GetActionController()->Clear();
+ mpCharacter->GetStateManager()->SetState<GetOut>();
+ return;
+ }
+
+ CharacterController::eIntention theIntention = CharacterController::NONE;
+ bool actionDown = false;
+ if(!GetHitnRunManager()->IsWaitingForReset())
+ {
+ theIntention = mpCharacter->GetController()->GetIntention();
+#ifdef RAD_WIN32
+ actionDown = mpCharacter->GetController()->IsButtonDown(CharacterController::GetOutCar);
+#else
+ actionDown = mpCharacter->GetController()->IsButtonDown(CharacterController::DoAction);
+#endif
+ }
+ bool brake = false;
+
+ if( GetGameFlow()->GetCurrentContext() != CONTEXT_SUPERSPRINT )
+ {
+
+ switch(m_GetOutState)
+ {
+ case GETOUT_NONE:
+ {
+#ifdef RAD_WIN32
+ if (theIntention == CharacterController::GetOutCar)
+#else
+ if (theIntention == CharacterController::DoAction)
+#endif
+ {
+ m_GetOutState = (pVehicle->GetSpeedKmh() < 30.0f) ? GETOUT_COMITTED : GETOUT_TRYING;
+ mpCharacter->GetTargetVehicle()->mGeometryVehicle->ShowBrakeLights();
+ }
+ }
+ break;
+
+ case GETOUT_TRYING :
+ {
+ if(!actionDown)
+ {
+ m_GetOutState = GETOUT_NONE;
+ mpCharacter->GetTargetVehicle()->mGeometryVehicle->HideBrakeLights();
+
+ VehicleController* controller = GetVehicleCentral()->GetVehicleController(GetVehicleCentral()->GetVehicleId(pVehicle));
+ if(controller)
+ {
+ rAssert(dynamic_cast<HumanVehicleController*>(controller));
+ static_cast<HumanVehicleController*>(controller)->GetMappable()->GetButton(VehicleMappable::Brake)->SetValue(0.0f);
+ static_cast<HumanVehicleController*>(controller)->GetMappable()->GetButton(VehicleMappable::HandBrake)->SetValue(0.0f);
+ }
+ break;
+ }
+
+ if ( pVehicle->GetSpeedKmh() < 30.0f &&
+ !pVehicle->IsUnstable() &&
+ !pVehicle->IsAirborn() )
+ {
+ m_GetOutState = GETOUT_COMITTED;
+ }
+
+ brake = true;
+ }
+ break;
+
+ case GETOUT_COMITTED :
+ {
+ if ( pVehicle->GetSpeedKmh() < 3.0f )
+ {
+ mpCharacter->GetActionController()->Clear();
+
+ VehicleController* controller = GetVehicleCentral()->GetVehicleController(GetVehicleCentral()->GetVehicleId(pVehicle));
+
+ if(controller)
+ {
+ rAssert(dynamic_cast<HumanVehicleController*>(controller));
+ static_cast<HumanVehicleController*>(controller)->GetMappable()->GetButton(VehicleMappable::Gas)->SetValue(0.0f);
+ static_cast<HumanVehicleController*>(controller)->GetMappable()->GetButton(VehicleMappable::Brake)->SetValue(0.0f);
+ static_cast<HumanVehicleController*>(controller)->GetMappable()->GetButton(VehicleMappable::HandBrake)->SetValue(1.0f);
+ }
+
+ mpCharacter->GetTargetVehicle()->mGeometryVehicle->HideBrakeLights();
+
+ mpCharacter->GetStateManager()->SetState<CharacterAi::GetOut>();
+ return;
+ }
+ else
+ {
+
+ VehicleController* v = GetVehicleCentral()->GetVehicleController(GetVehicleCentral()->GetVehicleId(pVehicle));
+ if(v && (v->GetGas() > 0.0f))
+ {
+ m_GetOutState = GETOUT_NONE;
+ mpCharacter->GetTargetVehicle()->mGeometryVehicle->HideBrakeLights();
+ VehicleController* controller = GetVehicleCentral()->GetVehicleController(GetVehicleCentral()->GetVehicleId(pVehicle));
+ static_cast<HumanVehicleController*>(controller)->GetMappable()->GetButton(VehicleMappable::HandBrake)->SetValue(0.0f);
+ }
+ else
+ {
+ brake = true;
+ }
+ }
+ }
+ break;
+ break;
+ }
+ }
+
+ if(brake)
+ {
+ VehicleController* controller = GetVehicleCentral()->GetVehicleController(GetVehicleCentral()->GetVehicleId(pVehicle));
+ if(controller)
+ {
+ rAssert(dynamic_cast<HumanVehicleController*>(controller));
+ static_cast<HumanVehicleController*>(controller)->GetMappable()->GetButton(VehicleMappable::Gas)->SetValue(0.0f);
+ static_cast<HumanVehicleController*>(controller)->GetMappable()->GetButton(VehicleMappable::HandBrake)->SetValue(1.0f);
+ }
+ }
+
+ if(m_InCarAction->IsRunning())
+ {
+ /*
+ if(mpCharacter->GetController()->IsButtonDown(CharacterController::Attack) && (m_GetOutState == GETOUT_NONE) )
+ {
+ m_InCarAction->IWannaRock(true);
+ }
+ */
+
+ VehicleController* v = GetVehicleCentral()->GetVehicleController(GetVehicleCentral()->GetVehicleId(mpCharacter->GetTargetVehicle(), false));
+ if(v)
+ {
+ bool duh;
+ float steer = v->GetSteering(duh);
+
+ Action* action = NULL;
+
+ float speed = mpCharacter->GetTargetVehicle()->GetSpeedKmh();
+
+ if(mIsDriver)
+ {
+ if((rmt::Abs(steer) > 0.5) && (speed > 5.0f))
+ {
+ action = new SteerAction(mpCharacter, (steer < 0) ? "turn_left_driver" : "turn_right_driver", 10.0f);
+ }
+ else if (mpCharacter->GetTargetVehicle()->IsInReverse())
+ {
+ action = new ReverseAction(mpCharacter, "look_back_driver", 25.0f);
+ }
+ else if ((mpCharacter->GetTargetVehicle()->GetAccelMss() > 8.0f) && (speed < 20.0f))
+ {
+ action = new AccelAction(mpCharacter, "accelerate_driver", 15.0f);
+ }
+ else if((mpCharacter->GetTargetVehicle()->GetAccelMss() < -10.0f) && (speed > 50.0f))
+ {
+ action = new DecelAction(mpCharacter, "decelerate_driver", 15.0f);
+ }
+ else if(mpCharacter->GetTargetVehicle()->GetAccelMss() < -25.0f)
+ {
+ if(mpCharacter->GetTargetVehicle()->mVelocityCM.DotProduct(mpCharacter->GetTargetVehicle()->mVehicleFacing) > 0.0f)
+ {
+ action = new PlayAnimationAction(mpCharacter, "crash_driver");
+ }
+ }
+ }
+ else // passenger
+ {
+ if((rmt::Abs(steer) > 0.5) && (speed > 50.0f))
+ {
+ action = new SteerAction(mpCharacter, (steer < 0) ? "sway_right" : "sway_left", 20.0f);
+ }
+ }
+
+ if(action)
+ {
+ m_InCarAction->Done();
+ m_InCarAction->Clear();
+ mpCharacter->GetActionController()->SequenceSingleAction(action);
+ }
+ }
+ }
+
+ if( theIntention == CharacterController::CelebrateSmall )
+ {
+ m_InCarAction->Done();
+ m_InCarAction->Clear();
+
+ mpCharacter->GetActionController()->SequenceSingleAction( new PlayAnimationAction( mpCharacter, mIsDriver ? "in_car_victory_small_driver" : "in_car_victory_small"));
+ return;
+ }
+
+ if( theIntention == CharacterController::CelebrateBig )
+ {
+ m_InCarAction->Done();
+ m_InCarAction->Clear();
+
+ mpCharacter->GetActionController()->SequenceSingleAction(new PlayAnimationAction( mpCharacter, mIsDriver ? "in_car_victory_large_driver" : "in_car_victory_large"));
+ return;
+ }
+
+ if( theIntention == CharacterController::WaveHello)
+ {
+ m_InCarAction->Done();
+ m_InCarAction->Clear();
+
+ mpCharacter->GetActionController()->SequenceSingleAction( new PlayAnimationAction( mpCharacter, "wave_driver"));
+ return;
+ }
+
+ if( theIntention == CharacterController::WaveGoodbye)
+ {
+ m_InCarAction->Done();
+ m_InCarAction->Clear();
+
+ mpCharacter->GetActionController()->SequenceSingleAction( new PlayAnimationAction( mpCharacter, "wave_goodbye_driver"));
+ return;
+ }
+}
+
+/*
+==============================================================================
+Loco::Loco
+==============================================================================
+Description: Comment
+
+Parameters( Character* pCharacter )
+
+Returns:
+
+=============================================================================
+*/
+Loco::Loco( Character* pCharacter )
+:
+State( pCharacter ),
+mLastActionFrame(0)
+{
+}
+
+Loco::~Loco( void )
+{
+}
+
+void Loco::Enter( void )
+{
+ if( mpCharacter->GetRole() != Character::ROLE_PEDESTRIAN )
+ {
+ mpCharacter->AddToWorldScene();
+ }
+
+ mpCharacter->SetScale(1.0f);
+
+ mpCharacter->UpdateTransformToLoco();
+
+ mpCharacter->DoGroundIntersect(true);
+
+ mpCharacter->SetInCar( false );
+
+ mpCharacter->SetTargetVehicle( NULL );
+
+ mpCharacter->GetWalkerLocomotionAction()->PlayDriver();
+
+ if(mpCharacter == GetCharacterManager()->GetCharacter(0))
+ {
+ GetVehicleCentral()->ActivateVehicleTriggers(true);
+ }
+
+ if(!mpCharacter->GetActionController()->IsBusy())
+ {
+ SequenceAction();
+ }
+}
+
+void Loco::Exit( void )
+{
+ mpCharacter->GetWalkerLocomotionAction()->StopDriver();
+ mpCharacter->GetWalkerLocomotionAction()->Done();;
+ mpCharacter->GetWalkerLocomotionAction()->AllowIdle(false);
+ mpCharacter->SetSimpleLoco(false);
+}
+
+void Loco::SequenceAction( void )
+{
+ mpCharacter->SetSimpleLoco(false);
+ mpCharacter->GetWalkerLocomotionAction()->AllowIdle(false);
+
+ CharacterController::eIntention theIntention = mpCharacter->GetController()->GetIntention();
+
+ if((theIntention == CharacterController::Attack) && GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_KICK_TOGGLES_CHARACTER_MODEL))
+ {
+ mpCharacter->GetController()->SetIntention(CharacterController::NONE);
+ GetCharacterManager()->NextCheatModel();
+ return;
+ }
+
+ // if we are doing a kick, and in the action range of a power coupling,
+ // turn the kick into an aciton
+ // Wow, is this ever a hack.
+ if(( theIntention == CharacterController::Attack) && mpCharacter->GetActionButtonHandler( ))
+ {
+ if(dynamic_cast<ActionButton::DestroyObject*>(mpCharacter->GetActionButtonHandler( )))
+ {
+ theIntention = CharacterController::DoAction;
+ mpCharacter->GetController()->SetIntention(theIntention);
+ }
+ }
+
+ // give the actionbutton handler (if any) a crack at things
+ if ( (mpCharacter->GetActionButtonHandler( ) != (ActionButton::ButtonHandler*)0) && ((mLastActionFrame + 3) < GetGame()->GetFrameCount()) )
+ {
+ if ( mpCharacter->GetActionButtonHandler()->ButtonPressed( mpCharacter ) )
+ {
+ mLastActionFrame = GetGame()->GetFrameCount();
+ return;
+ }
+ }
+
+ mpCharacter->GetController()->ClearIntention();
+
+ // Sequence in the Dodge animation
+ if( theIntention == CharacterController::Dodge )
+ {
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+
+ if( mpCharacter->IsNPC() ) // change NPC controller state when dodging is done
+ {
+ pSeq->BeginSequence();
+ pSeq->AddAction(new ChangeNPCControllerState(mpCharacter, NPCController::DODGING));
+ pSeq->EndSequence();
+ }
+
+ pSeq->BeginSequence();
+ Action* pAction = new DodgeAction( mpCharacter );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence();
+
+ if( mpCharacter->IsNPC() ) // change NPC controller state when dodging is done
+ {
+ pSeq->BeginSequence();
+ pSeq->AddAction(new ChangeNPCControllerState(mpCharacter, NPCController::FOLLOWING_PATH));
+ pSeq->EndSequence();
+ }
+ mpCharacter->GetWalkerLocomotionAction()->StopDriver();
+ return;
+ }
+
+ // Sequence in the jump animation
+ if ( theIntention == CharacterController::Jump && !GetInteriorManager()->IsInside())
+ {
+ if( !mpCharacter->IsNPC() &&
+ mpCharacter->GetSimState()->GetControl() == sim::simSimulationCtrl )
+ {
+ // if player character is in simulation control and jump button was pressed,
+ // we transit him back to AI control
+ mpCharacter->GetSimState()->SetControl( sim::simAICtrl );
+ mpCharacter->GetSimState()->ResetVelocities();
+ mpCharacter->OnTransitToAICtrl();
+ }
+ else
+ {
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+
+ pSeq->BeginSequence();
+ JumpAction* pAction = mpCharacter->GetJumpLocomotionAction();
+ pAction->Reset(CharacterTune::sfJumpHeight, false);
+ pSeq->AddAction( pAction );
+
+ pSeq->EndSequence();
+ mpCharacter->GetWalkerLocomotionAction()->StopDriver();
+ return;
+ }
+ }
+
+ // Sequence in the Cringe animation
+ if( theIntention == CharacterController::Cringe )
+ {
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+
+ if( mpCharacter->IsNPC() )
+ {
+ pSeq->BeginSequence();
+ pSeq->AddAction(new ChangeNPCControllerState(mpCharacter, NPCController::CRINGING));
+ pSeq->EndSequence();
+ }
+
+ pSeq->BeginSequence();
+ Action* pAction = new CringeAction( mpCharacter );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence();
+
+ if( mpCharacter->IsNPC() )
+ {
+ pSeq->BeginSequence();
+ pSeq->AddAction(new ChangeNPCControllerState(mpCharacter, NPCController::FOLLOWING_PATH));
+ pSeq->EndSequence();
+ }
+ mpCharacter->GetWalkerLocomotionAction()->StopDriver();
+ return;
+ }
+
+
+
+ // Sequence in the TurnRight anim
+ if( theIntention == CharacterController::TurnRight )
+ {
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+
+ pSeq->BeginSequence();
+ Action* pAction = new PlayAnimationAction( mpCharacter, "turn60_CW" ); //, 50.0f );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence();
+
+ mpCharacter->GetWalkerLocomotionAction()->StopDriver();
+ return;
+ }
+
+ // Sequence in the TurnLeft anim
+ if( theIntention == CharacterController::TurnLeft )
+ {
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+
+ pSeq->BeginSequence();
+ Action* pAction = new PlayAnimationAction( mpCharacter, "turn60_CCW" );//, 50.0f );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence();
+
+ mpCharacter->GetWalkerLocomotionAction()->StopDriver();
+ return;
+ }
+
+ // Sequence in the attack anim
+ if( theIntention == CharacterController::Attack )
+ {
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new PlayAnimationAction( mpCharacter, "break_quick" ));
+ pSeq->AddAction( new KickAction( mpCharacter, 0.25f ));
+ pSeq->EndSequence();
+ mpCharacter->GetWalkerLocomotionAction()->StopDriver();
+ return;
+ }
+
+ if( theIntention == CharacterController::CelebrateSmall )
+ {
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+
+ pSeq->BeginSequence();
+ PlayAnimationAction* action = new PlayAnimationAction( mpCharacter, "victory_small" );
+ action->AbortWhenMovementOccurs(true);
+ pSeq->AddAction(action);
+ pSeq->EndSequence();
+ mpCharacter->GetWalkerLocomotionAction()->StopDriver();
+ return;
+ }
+
+ if( theIntention == CharacterController::CelebrateBig )
+ {
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new PlayAnimationAction( mpCharacter, "victory_large" ));
+ pSeq->EndSequence();
+ mpCharacter->GetWalkerLocomotionAction()->StopDriver();
+ return;
+ }
+
+ // Only do the test for turbo when we are not jumping.
+ //
+ if ( mpCharacter->GetController()->IsButtonDown( CharacterController::Dash ) && !GetInteriorManager()->IsInside())
+ {
+ mpCharacter->SetTurbo( true );
+ }
+ else
+ {
+ mpCharacter->SetTurbo( false );
+ }
+
+ if ( mpCharacter->IsInCollision() )
+ {
+
+ // Only play the animation if the character is running fast enough.
+ //
+ if ( false ) //mpCharacter->IsTurbo() && mpCharacter->GetDesiredSpeed() > 0.0f )
+ {
+ // Speed is good, now check the hit angle.
+ // [6/26/2002]
+ //
+ if ( mpCharacter->CanStaggerCollision( ) )
+ {
+ mpCharacter->GetActionController()->Clear();
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+ Action* pAction = 0;
+ // Play run_into_object.
+ //
+ pSeq->BeginSequence();
+ pAction = new PlayAnimationAction( mpCharacter, "run_into_object" );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+ mpCharacter->ResetSpeed( );
+ mpCharacter->GetWalkerLocomotionAction()->StopDriver();
+ return;
+ }
+ }
+ }
+
+ // nothing else got sequenced, do the default
+ if(mpCharacter->GetWalkerLocomotionAction()->GetStatus() != RUNNING)
+ {
+ mpCharacter->GetWalkerLocomotionAction()->SetStatus(SLEEPING);
+ }
+
+ mpCharacter->GetWalkerLocomotionAction()->AllowIdle(true);
+
+ mpCharacter->GetWalkerLocomotionAction()->PlayDriver();
+
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+ pSeq->BeginSequence();
+ pSeq->AddAction( mpCharacter->GetWalkerLocomotionAction());
+ pSeq->EndSequence();
+
+ mpCharacter->SetSimpleLoco(true);
+}
+
+void Loco::Update( float timeins )
+{
+}
+
+
+/*
+==============================================================================
+GetIn
+==============================================================================
+*/
+GetIn::GetIn( Character* pCharacter )
+:
+State( pCharacter ),
+mObstructed(false),
+mFirst(true),
+mCollisionFailure(4)
+{
+}
+
+GetIn::~GetIn( void )
+{
+}
+
+void GetIn::Enter( void )
+{
+
+ if(mFirst)
+ {
+ GetEventManager()->TriggerEvent( EVENT_GETINTOVEHICLE_START, mpCharacter );
+ mFirst = false;
+ }
+
+ if(!mpCharacter->GetTargetVehicle()->mIrisTransition && !mObstructed && (mpCharacter->GetTargetVehicle()->GetSpeedKmh() <= 1.0f))
+ {
+ if(mpCharacter->GetWalkerLocomotionAction()->GetStatus() != RUNNING)
+ {
+ mpCharacter->GetWalkerLocomotionAction()->SetStatus(SLEEPING);
+ }
+
+ mpCharacter->GetWalkerLocomotionAction()->PlayDriver();
+
+ if(mpCharacter == GetCharacterManager()->GetCharacter(0))
+ {
+ GetVehicleCentral()->ActivateVehicleTriggers(false);
+ }
+
+ // figure out how to get into the car
+ int nWaypoints;
+ rmt::Vector waypoints[4];
+ bool slide;
+ bool jump;
+
+ CalcGetInPath(&nWaypoints, waypoints, &slide, &jump);
+
+ if(jump)
+ {
+ mObstructed = true;
+ mpCharacter->GetActionController()->Clear();
+ Enter();
+ /*
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+ pSeq->BeginSequence();
+ pSeq->AddAction( new FragileArrive( mpCharacter, waypoints[0]) );
+ mpCharacter->GetJumpLocomotionAction()->Reset(1.9f);
+ pSeq->AddAction(mpCharacter->GetJumpLocomotionAction());
+ pSeq->EndSequence( );
+ */
+ return;
+ }
+
+ rmt::Vector getinPosition = waypoints[nWaypoints - 1];
+
+ if( ( nWaypoints >= 3 ) && ( mpCharacter->GetTargetVehicle()->mpDriver != NULL ) )
+ {
+ GetEventManager()->TriggerEvent( EVENT_WRONG_SIDE_DUMBASS,
+ static_cast<Vehicle*>(mpCharacter->GetTargetVehicle()) );
+ }
+
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+ bool isDriver = mpCharacter->GetTargetVehicle()->HasDriver() ? false : true;
+ Vehicle::Door door = (isDriver && !slide) ? Vehicle::DOOR_DRIVER : Vehicle::DOOR_PASSENGER;
+ Action* pAction = 0;
+
+ mpCharacter->GetTargetVehicle()->UpdateDoorState();
+
+ bool high = false;
+ if(isDriver)
+ {
+ high = mpCharacter->GetTargetVehicle()->GetDriverLocation().y > CharacterTune::sGetInHeightThreshold;
+ }
+ else
+ {
+ high = mpCharacter->GetTargetVehicle()->GetPassengerLocation().y > CharacterTune::sGetInHeightThreshold;
+ }
+
+ const char** anims = NULL;
+ if(high)
+ {
+ if(isDriver && !slide)
+ {
+ anims = sGetInHighDriver;
+ }
+ else
+ {
+ anims = sGetInHighPassenger;
+ }
+ }
+ else
+ {
+ if(isDriver && !slide)
+ {
+ anims = sGetInDriver;
+ }
+ else
+ {
+ anims = sGetInPassenger;
+ }
+ }
+
+ // add actions for all the get in waypoints
+ for(int i = 0; i < nWaypoints; i++)
+ {
+ pSeq->BeginSequence();
+
+ pAction = new Arrive( mpCharacter, waypoints[i], i == nWaypoints - 1 );
+ pSeq->AddAction( pAction );
+ // SlaveLoco
+ //
+ pAction = 0;
+
+ pAction = mpCharacter->GetWalkerLocomotionAction();
+
+ pSeq->AddAction( mpCharacter->GetWalkerLocomotionAction() );
+ pSeq->EndSequence( );
+ }
+
+ // Orient
+ //
+ pSeq->BeginSequence();
+ rmt::Matrix mat = mpCharacter->GetTargetVehicle( )->GetTransform();
+ pAction = new Orient( mpCharacter, mat.Row( 2 ) );
+ pSeq->AddAction( pAction );
+ // SlaveLoco
+ //
+ pAction = 0;
+
+ pAction = mpCharacter->GetWalkerLocomotionAction();
+
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+
+ // Open Door Anim
+ //
+ if(mpCharacter->GetTargetVehicle()->NeedToOpenDoor(door))
+ {
+ float delay = CharacterTune::sGetInOpenDelay;
+ float time = CharacterTune::sGetInOpenSpeed;
+ float scale = 30.0f / CharacterTune::sfGetInOutOfCarAnimSpeed;
+
+ pSeq->BeginSequence();
+ pAction = new PlayAnimationAction( mpCharacter, anims[ 0 ], CharacterTune::sfGetInOutOfCarAnimSpeed);
+ pSeq->AddAction( pAction );
+ pSeq->AddAction( new CarDoorAction(mpCharacter->GetTargetVehicle(), Vehicle::DOORACTION_OPEN, door, delay * scale, time * scale));
+ pSeq->EndSequence( );
+ }
+
+ // Get In Car Anim
+ //
+ pSeq->BeginSequence();
+ pAction = new PlayAnimationAction( mpCharacter, anims[ 1 ], CharacterTune::sfGetInOutOfCarAnimSpeed);
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+
+ rmt::Vector seat;
+ if(isDriver && !slide)
+ {
+ seat = mpCharacter->GetTargetVehicle()->GetDriverLocation();
+ }
+ else
+ {
+ seat = mpCharacter->GetTargetVehicle()->GetPassengerLocation();
+ }
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new ChangeLocomotion(mpCharacter, ChangeLocomotion::INCAR) );
+ pSeq->AddAction( new ChangeState<InCar>(mpCharacter));
+ pSeq->AddAction( new TriggerEventAction(EVENT_GETINTOVEHICLE_END, mpCharacter));
+ pSeq->EndSequence( );
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new DelayAction(0.05f));
+ pSeq->EndSequence( );
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new Position(mpCharacter, seat, 0.0f, true));
+ pSeq->AddAction( new PlayAnimationAction( mpCharacter, "in_car_idle", 300.0f));
+ pSeq->EndSequence( );
+
+ // Close Door Anim
+ //
+ if(mpCharacter->GetTargetVehicle()->HasActiveDoor(door) || mpCharacter->GetTargetVehicle()->NeedToCloseDoor(door))
+ {
+ float delay = CharacterTune::sGetInCloseDelay;
+ float time = CharacterTune::sGetInCloseSpeed;
+ float scale = 30.0f / CharacterTune::sfGetInOutOfCarAnimSpeed;
+
+ pSeq->BeginSequence();
+ pAction = new PlayAnimationAction( mpCharacter, anims[ 2 ], CharacterTune::sfGetInOutOfCarAnimSpeed);
+ pSeq->AddAction( new CarDoorAction(mpCharacter->GetTargetVehicle(), Vehicle::DOORACTION_CLOSE, door, delay * scale, time * scale));
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+ }
+
+ if(slide)
+ {
+ rmt::Vector seatDist = mpCharacter->GetTargetVehicle()->GetPassengerLocation() -
+ mpCharacter->GetTargetVehicle()->GetDriverLocation();
+ bool seatClose = seatDist.Magnitude() < 0.1f;
+
+ rmt::Vector v = mpCharacter->GetTargetVehicle()->GetPassengerLocation();
+ pSeq->BeginSequence();
+ pSeq->AddAction( new Position(mpCharacter, v, 0.0f, true));
+ pSeq->EndSequence( );
+
+ v = mpCharacter->GetTargetVehicle()->GetDriverLocation();
+ pSeq->BeginSequence();
+ pSeq->AddAction( new Position(mpCharacter, v, 0.5f, true));
+ if(!seatClose)
+ {
+ pSeq->AddAction( new PlayAnimationAction(mpCharacter, "seatmove"));
+ }
+ pSeq->EndSequence( );
+
+ if(mpCharacter->GetTargetVehicle()->NeedToCloseDoor(Vehicle::DOOR_DRIVER))
+ {
+ float delay = CharacterTune::sGetInCloseDelay;
+ float time = CharacterTune::sGetInCloseSpeed;
+ float scale = 30.0f / CharacterTune::sfGetInOutOfCarAnimSpeed;
+
+ pSeq->BeginSequence();
+ pAction = new PlayAnimationAction( mpCharacter, "get_into_car_close_door_driver", CharacterTune::sfGetInOutOfCarAnimSpeed);
+ pSeq->AddAction( new CarDoorAction(mpCharacter->GetTargetVehicle(), Vehicle::DOORACTION_CLOSE, Vehicle::DOOR_DRIVER, delay * scale, time * scale));
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+ }
+ }
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new ReleaseDoorsAction(mpCharacter->GetTargetVehicle()));
+ pSeq->EndSequence( );
+ }
+ else
+ {
+ //Do an iris wipe in
+ GetGuiSystem()->HandleMessage( GUI_MSG_START_IRIS_WIPE_CLOSE );
+ //Set the speed of the wipe
+ CGuiScreenIrisWipe* iw = static_cast<CGuiScreenIrisWipe*>(GetGuiSystem()->GetInGameManager()->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_IRIS_WIPE ));
+ const float speed = 1.0f;
+ iw->SetRelativeSpeed( speed );
+
+ //add listener
+ GetEventManager()->AddListener( this, EVENT_GUI_IRIS_WIPE_CLOSED );
+
+ //Pause gameplay
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Suspend();
+ }
+}
+
+void GetIn::HandleEvent( EventEnum id, void* pUserData )
+{
+ if ( id == EVENT_GUI_IRIS_WIPE_CLOSED )
+ {
+ mpCharacter->RemoveFromPhysics();
+
+ GetEventManager()->RemoveListener( this, EVENT_GUI_IRIS_WIPE_CLOSED );
+
+ Vehicle* pVehicle = mpCharacter->GetTargetVehicle();
+ rAssert( pVehicle );
+
+ rmt::Vector pos;
+ pVehicle->GetPosition( &pos );
+
+ GetAvatarManager()->PutCharacterInCar( mpCharacter, pVehicle );
+
+ if(!pVehicle->mVisibleCharacters)
+ {
+ mpCharacter->RemoveFromWorldScene();
+ }
+
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( SuperCam::FOLLOW_CAM, SuperCamCentral::CUT, 0 ); //Override the auto one
+
+ //TODO: SWITCH MODELS
+
+ //Wipe out when we are finished moving.
+ GetGuiSystem()->HandleMessage( GUI_MSG_START_IRIS_WIPE_OPEN );
+
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Resume();
+
+ GetEventManager()->TriggerEvent( EVENT_GETINTOVEHICLE_END, mpCharacter );
+
+ pVehicle->MoveDoor(Vehicle::DOOR_DRIVER, Vehicle::DOORACTION_CLOSE, 0.0f);
+ pVehicle->MoveDoor(Vehicle::DOOR_PASSENGER, Vehicle::DOORACTION_CLOSE, 0.0f);
+ }
+}
+
+// figure out how to get into the car from an arbitrary location
+void GetIn::CalcGetInPath(int* nWaypoints, rmt::Vector* waypoints, bool* doingSlide, bool* jump)
+{
+ const float origedge = 0.75f; // how far want to be from the car at the nav points ( TODO : make this tunable?)
+ const float edge = origedge / 1.414f; // x & z need to be sacled by sqrt(2) to make the diagonal distance right ;
+
+ rmt::Vector box = mpCharacter->GetTargetVehicle()->GetExtents();
+
+ bool isDriver = mpCharacter->GetTargetVehicle()->HasDriver() ? false : true;
+
+ // grab matrices to get from car space to world space (we do all our work in car space)
+ rmt::Matrix carToWorld = mpCharacter->GetTargetVehicle( )->GetTransform();
+ rmt::Matrix worldToCar = mpCharacter->GetTargetVehicle( )->GetTransform();
+ worldToCar.Invert();
+
+ // get character position in car space
+ rmt::Vector characterPosition;
+ mpCharacter->GetPosition(characterPosition);
+ characterPosition.Transform(worldToCar);
+
+ *doingSlide = false;
+ *jump = false;
+ *nWaypoints = 0;
+
+ if((characterPosition.y > (-box.y / 2)) &&
+ (characterPosition.x < box.x) &&
+ (characterPosition.x > -box.x) &&
+ (characterPosition.z < box.z) &&
+ (characterPosition.z > -box.z))
+ {
+ // get in position (car space)
+ rmt::Vector getinPosition = CharacterTune::sGetInPosition;
+ getinPosition = -getinPosition;
+ getinPosition.Add(mpCharacter->GetTargetVehicle( )->GetDriverLocation());
+
+ // swap get in poistion for driver
+ characterPosition.x = getinPosition.x - 0.5f;
+
+ // add final get in position to waypoints
+ characterPosition.Transform( carToWorld );
+ waypoints[(*nWaypoints)++] = characterPosition;
+ *jump = true;
+ return;
+ }
+
+ if(isDriver && mpCharacter->GetTargetVehicle()->mAllowSlide)
+ {
+ rmt::Vector driverPosition = CharacterTune::sGetInPosition;
+ driverPosition.x = -driverPosition .x;
+ driverPosition.Add(mpCharacter->GetTargetVehicle( )->GetDriverLocation());
+
+ rmt::Vector passPosition = CharacterTune::sGetInPosition;
+ passPosition.Add(mpCharacter->GetTargetVehicle( )->GetPassengerLocation());
+
+ driverPosition.Sub(characterPosition);
+ passPosition.Sub(characterPosition);
+
+ if(characterPosition.x > (box.x - 0.5f))
+ {
+ isDriver = false;
+ *doingSlide = true;
+ }
+ }
+
+ // these are the edges of the car from a walking perspective
+ float carFront = box.z + (edge);
+ float carRear = -box.z - (edge);;
+ float carNearSide = (box.x + edge) * (isDriver ? -1 : 1);
+ float carFarSide = (box.x + edge) * (isDriver ? 1 : -1);
+
+ // get in position (car space)
+ rmt::Vector getinPosition = CharacterTune::sGetInPosition;
+
+ // swap get in poistion for driver
+ if ( isDriver )
+ {
+ // mirror over x.
+ getinPosition.x = -getinPosition.x;
+ getinPosition.Add(mpCharacter->GetTargetVehicle( )->GetDriverLocation());
+ }
+ else
+ {
+ getinPosition.Add(mpCharacter->GetTargetVehicle( )->GetPassengerLocation());
+ }
+
+ // are we in fron of the car or behind
+ bool front = characterPosition.z > 0;
+
+ bool onside = true;
+
+ if(((characterPosition.z > box.z) || (characterPosition.z < -box.z)) &&
+ (isDriver ? (characterPosition.x > -(box.x - (edge / 2))) : (characterPosition.x < (box.x - (edge / 2))) ))
+ {
+ onside = false;
+ }
+
+ if(((characterPosition.z < box.z) && (characterPosition.z > -box.z)) &&
+ (isDriver ? (characterPosition.x > 0) : (characterPosition.x < 0)))
+ {
+ onside = false;
+ }
+
+ if(!onside)
+ {
+ // we are offside, need some more nav points
+
+ // are we on the other side of the car
+ if((characterPosition.z < box.z) && (characterPosition.z > -box.z))
+ {
+ // add far corner to waypoints
+ rmt::Vector tmpPosition(0,0,0);
+ tmpPosition.z += front ? carFront : carRear;
+ tmpPosition.x += carFarSide;
+
+ tmpPosition.Transform( carToWorld );
+ waypoints[(*nWaypoints)++] = tmpPosition;
+ }
+
+ // add near corner to waypoints
+ rmt::Vector tmpPosition(0,0,0);
+ tmpPosition.z += front ? carFront : carRear;
+ tmpPosition.x += carNearSide;
+
+ tmpPosition.Transform( carToWorld );
+ waypoints[(*nWaypoints)++] = tmpPosition;
+ }
+
+ // add final get in position to waypoints
+ getinPosition.Transform( carToWorld );
+ waypoints[(*nWaypoints)++] = getinPosition;
+}
+
+void GetIn::SequenceAction( void )
+{
+ Enter();
+}
+
+void GetIn::Update( float timeins )
+{
+ if(mpCharacter->CollidedThisFrame() && (mCollisionFailure == 0) && !mpCharacter->GetJumpLocomotionAction()->IsInJump())
+ {
+ mObstructed = true;
+ mpCharacter->GetActionController()->Clear();
+ Enter();
+ }
+
+ if(mCollisionFailure)
+ {
+ mCollisionFailure--;
+ }
+}
+
+void GetIn::Exit( void )
+{
+ if(!mpCharacter->GetTargetVehicle()->mVisibleCharacters)
+ {
+ mpCharacter->RemoveFromWorldScene();
+ }
+
+ mpCharacter->GetWalkerLocomotionAction()->StopDriver();
+ mpCharacter->GetWalkerLocomotionAction()->Done();;
+}
+
+
+/*
+==============================================================================
+GetOut
+==============================================================================
+*/
+GetOut::GetOut( Character* pCharacter )
+:
+State( pCharacter ),
+mObstructed(false),
+mFirst(true),
+mPanic(false)
+{
+}
+
+GetOut::~GetOut( void )
+{
+}
+
+void GetOut::Enter( void )
+{
+ bool destroyed = !mpCharacter->GetTargetVehicle() || mpCharacter->GetTargetVehicle()->mVehicleDestroyed;
+ bool iris = !destroyed && (mObstructed || mpCharacter->GetTargetVehicle()->mIrisTransition);
+
+ if(mFirst)
+ {
+ mFirst = false;
+ GetEventManager()->TriggerEvent( EVENT_GETOUTOFVEHICLE_START, mpCharacter );
+ }
+
+ if ( !iris || mpCharacter != GetCharacterManager()->GetCharacter(0))
+ {
+ mpCharacter->AddToWorldScene();
+ mpCharacter->SetScale(1.0f);
+ DoGetOut();
+ }
+ else
+ {
+ //Do an iris wipe in
+ GetGuiSystem()->HandleMessage( GUI_MSG_START_IRIS_WIPE_CLOSE );
+ //Set the speed of the wipe
+ CGuiScreenIrisWipe* iw = static_cast<CGuiScreenIrisWipe*>(GetGuiSystem()->GetInGameManager()->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_IRIS_WIPE ));
+ const float speed = 1.0f;
+ iw->SetRelativeSpeed( speed );
+
+ //add listener
+ GetEventManager()->AddListener( this, EVENT_GUI_IRIS_WIPE_CLOSED );
+
+ //Pause gameplay
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Suspend();
+
+ if(mpCharacter->GetTargetVehicle())
+ {
+ mpCharacter->GetTargetVehicle()->SetOutOfCarSimState();
+ }
+ }
+}
+
+static sim::TArray< sim::RayIntersectionInfo > sIntersectInfo( 64 );
+
+void GetOut::HandleEvent( EventEnum id, void* pUserData )
+{
+ if ( id == EVENT_GUI_IRIS_WIPE_CLOSED )
+ {
+ GetEventManager()->RemoveListener( this, EVENT_GUI_IRIS_WIPE_CLOSED );
+
+ Character* character = GetAvatarManager()->GetAvatarForPlayer( 0 )->GetCharacter();
+ Vehicle* car = GetAvatarManager()->GetAvatarForPlayer( 0 )->GetVehicle();
+
+ GetEventManager()->TriggerEvent( EVENT_GETOUTOFVEHICLE_END, character );
+
+ car->MoveDoor(Vehicle::DOOR_DRIVER, Vehicle::DOORACTION_CLOSE, 0.0f);
+ car->MoveDoor(Vehicle::DOOR_PASSENGER, Vehicle::DOORACTION_CLOSE, 0.0f);
+
+ character->SetInCar( false );
+ character->SetTargetVehicle( NULL );
+ character->AddToWorldScene();
+ mpCharacter->SetScale(1.0f);
+
+ // get in position (car space)
+ rmt::Vector getOutPosition = CharacterTune::sGetInPosition;
+
+ int isDriver = car->HasDriver() ? 0 : 1;
+ if(!mObstructed)
+ {
+ // swap get in poistion for driver
+ if ( isDriver )
+ {
+ // mirror over x.
+ getOutPosition.x = -getOutPosition.x;
+ getOutPosition.Add( car->GetDriverLocation() );
+ }
+ else
+ {
+ getOutPosition.Add( car->GetPassengerLocation() );
+ }
+
+ getOutPosition.Transform( car->GetTransform() );
+ rmt::Vector carPos;
+
+ car->GetPosition(&carPos);
+ getOutPosition.y = carPos.y - car->GetExtents().y;
+
+ // check if point we want to teleport to is objstructed
+ sIntersectInfo.Clear();
+ float fOldRayThickness = sim::RayIntersectionInfo::sRayThickness;
+ static float sfCharacterRayThickness = 0.3f;
+ sim::RayIntersectionInfo::sRayThickness = sfCharacterRayThickness;
+ bool bFoundIntersect = false;
+
+ float outDist = VERY_LARGE;
+ float currentDist = outDist;
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+
+ // adds in the list the collision object interfering with the ray and ordered according to their distance to the source.
+ // use sim::RayIntersectionInfo::SetMethod(method) to set the method
+ // use sim::RayIntersectionInfo::SetReturnClosestOnly(true/false) if you need only the closest object
+ // nb. if SetReturnClosestOnly(true) is used, the previous returned list can be used as a cache.
+ sim::RayIntersectionInfo::SetReturnClosestOnly( false );
+ sim::RayIntersectionInfo::SetMethod( sim::RayIntersectionVolume );
+ rmt::Vector rayOrg;
+ mpCharacter->GetPosition(rayOrg);
+ rmt::Vector rayEnd = getOutPosition;
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ // Test ray against remaining collision objects.
+ //
+ GetWorldPhysicsManager()->mCollisionManager->DetectRayIntersection(sIntersectInfo, rayOrg, rayEnd, false, car->mCollisionAreaIndex );
+
+ for ( int i = 0; i < sIntersectInfo.GetSize( ); i++ )
+ {
+ if ( sIntersectInfo[ i ].mCollisionVolume &&
+ (sIntersectInfo[ i ].mCollisionVolume->GetCollisionObject()->GetSimState()->mAIRefPointer != mpCharacter ) &&
+ (sIntersectInfo[ i ].mCollisionVolume->GetCollisionObject()->GetSimState()->mAIRefPointer != car ))
+ {
+ mObstructed = true;
+ break;
+ }
+ }
+ sim::RayIntersectionInfo::sRayThickness = fOldRayThickness;
+ }
+
+ if(mObstructed)
+ {
+ car->GetPosition(&getOutPosition);
+ getOutPosition.y += car->GetExtents().y + 0.2f;
+ }
+
+ rmt::Vector facing = car->GetTransform().Row(0);
+ if(isDriver)
+ {
+ facing.Scale(-1.0f);
+ }
+
+ GetAvatarManager()->PutCharacterOnGround( character, car );
+ character->RelocateAndReset( getOutPosition, choreo::GetWorldAngle(facing.x, facing.z), true, true );
+ character->SnapToGround();
+
+ rmt::Vector newPos;
+ character->GetPosition(&newPos);
+
+ float dist = mObstructed ? 3.0f : 1.0f;
+ if(rmt::Abs(getOutPosition.y - newPos.y) > dist)
+ {
+ car->GetPosition(&getOutPosition);
+ getOutPosition.y += car->GetExtents().y + 0.2f;
+ character->RelocateAndReset( getOutPosition, choreo::GetWorldAngle(facing.x, facing.z), true, true );
+ }
+
+ GetVehicleCentral()->ActivateVehicleTriggers(true);
+
+#ifdef RAD_WIN32
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( SuperCam::ON_FOOT_CAM, SuperCamCentral::CUT, 0 );
+#else
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( SuperCam::WALKER_CAM, SuperCamCentral::CUT, 0 );
+#endif
+ //Wipe out when we are finished moving.
+ GetGuiSystem()->HandleMessage( GUI_MSG_START_IRIS_WIPE_OPEN );
+
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Resume();
+ }
+}
+
+void GetOut::Exit( void )
+{
+}
+
+void GetOut::SequenceAction( void )
+{
+ Enter();
+}
+
+void GetOut::Update( float timeins )
+{
+ if(mpCharacter->CollidedThisFrame() && !mPanic)
+ {
+ mObstructed = true;
+ mpCharacter->GetActionController()->Clear();
+ Enter();
+ }
+}
+
+void GetOut::DoGetOut( void )
+{
+ mPanic = !mpCharacter->GetTargetVehicle() || mpCharacter->GetTargetVehicle()->mVehicleDestroyed;
+ bool mIsDriver;
+
+ if(mpCharacter->GetTargetVehicle())
+ {
+ mIsDriver = (mpCharacter->GetTargetVehicle()->mpDriver == mpCharacter) ||
+ ((mpCharacter->GetTargetVehicle()->mVehicleType != VT_TRAFFIC) &&
+ (mpCharacter->GetTargetVehicle()->mpDriver == NULL));
+ }
+ else
+ {
+ mIsDriver = false;
+ }
+
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+
+ if(mPanic)
+ {
+ mpCharacter->AddToWorldScene();
+
+ if(mpCharacter->GetRole() == Character::ROLE_DRIVER)
+ {
+ GetCharacterManager()->SetGarbage(mpCharacter, true);
+ }
+
+ mpCharacter->SetFacingDir( mIsDriver ? (rmt::PI / 2) : -(rmt::PI / 2));
+ mpCharacter->SetDesiredDir( mIsDriver ? (rmt::PI / 2) : -(rmt::PI / 2));
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new DelayAction(0.5f));
+ pSeq->EndSequence( );
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new GroundSnap(mpCharacter));
+ pSeq->EndSequence( );
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new ChangeLocomotion(mpCharacter, ChangeLocomotion::WALKING) );
+ pSeq->EndSequence( );
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new DodgeAction(mpCharacter) );
+ pSeq->EndSequence( );
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new ChangeState<Loco>(mpCharacter));
+ pSeq->AddAction( new TriggerEventAction(EVENT_GETOUTOFVEHICLE_END, mpCharacter));
+ pSeq->EndSequence( );
+ }
+ else
+ {
+ int isDriver = mpCharacter->GetTargetVehicle()->HasDriver() ? 0 : 1;
+ Vehicle::Door door = isDriver ? Vehicle::DOOR_DRIVER : Vehicle::DOOR_PASSENGER;
+ Action* pAction = 0;
+
+ mpCharacter->GetTargetVehicle()->UpdateDoorState();
+
+ bool high = true;
+ rmt::Vector seat;
+
+ if(isDriver)
+ {
+ high = mpCharacter->GetTargetVehicle()->GetDriverLocation().y > CharacterTune::sGetInHeightThreshold;
+ seat = mpCharacter->GetTargetVehicle()->GetDriverLocation();
+ }
+ else
+ {
+ high = mpCharacter->GetTargetVehicle()->GetPassengerLocation().y > CharacterTune::sGetInHeightThreshold;
+ seat = mpCharacter->GetTargetVehicle()->GetPassengerLocation();
+ }
+
+ const char** anims = NULL;
+ if(high)
+ {
+ if(isDriver)
+ {
+ anims = sGetOutHighDriver;
+ }
+ else
+ {
+ anims = sGetOutHighPassenger;
+ }
+ }
+ else
+ {
+ if(isDriver)
+ {
+ anims = sGetOutDriver;
+ }
+ else
+ {
+ anims = sGetOutPassenger;
+ }
+ }
+
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new DelayAction(0.1f));
+ pSeq->EndSequence( );
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new Position(mpCharacter, seat, 0.0f, true));
+ pSeq->EndSequence( );
+
+ // Play get_out_of_car_open_door
+ if(mpCharacter->GetTargetVehicle()->NeedToOpenDoor(door))
+ {
+ float delay = CharacterTune::sGetOutOpenDelay;
+ float time = CharacterTune::sGetOutOpenSpeed;
+ float scale = 30.0f / CharacterTune::sfGetInOutOfCarAnimSpeed;
+
+ pSeq->BeginSequence();
+ pAction = new PlayAnimationAction( mpCharacter, anims[ 0 ], CharacterTune::sfGetInOutOfCarAnimSpeed );
+ pSeq->AddAction( new CarDoorAction(mpCharacter->GetTargetVehicle(), Vehicle::DOORACTION_OPEN, door, delay * scale, time * scale));
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+ }
+
+ if(0)
+ {
+ pSeq->BeginSequence();
+ pSeq->AddAction( new DelayAction(2.0f));
+ pSeq->EndSequence( );
+ }
+
+ // Play get_out_of_car
+ //
+ pSeq->BeginSequence();
+ pSeq->AddAction( new ChangeLocomotion(mpCharacter, ChangeLocomotion::WALKING) );
+ pSeq->EndSequence( );
+
+ if(0)
+ {
+ pSeq->BeginSequence();
+ pSeq->AddAction( new DelayAction(0.5f));
+ pSeq->EndSequence( );
+ }
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new GroundSnap(mpCharacter));
+ pAction = new PlayAnimationAction( mpCharacter, anims[ 1 ], CharacterTune::sfGetInOutOfCarAnimSpeed);
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+
+ if(0)
+ {
+ pSeq->BeginSequence();
+ pSeq->AddAction( new DelayAction(2.0f));
+ pSeq->EndSequence( );
+ }
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new ChangeState<Loco>(mpCharacter));
+ pSeq->AddAction( new TriggerEventAction(EVENT_GETOUTOFVEHICLE_END, mpCharacter));
+
+ // Play get_out_of_car_close_door
+ if(mpCharacter->GetTargetVehicle()->HasActiveDoor(door))
+ {
+ float delay = CharacterTune::sGetOutCloseDelay;
+ float time = CharacterTune::sGetOutCloseSpeed;
+ float scale = 30.0f / CharacterTune::sfGetInOutOfCarAnimSpeed;
+
+ PlayAnimationAction* pAction = new PlayAnimationAction( mpCharacter, anims [ 2 ], CharacterTune::sfGetInOutOfCarAnimSpeed);
+ pAction->AbortWhenMovementOccurs(true);
+ pSeq->AddAction( new CarDoorAction(mpCharacter->GetTargetVehicle(), Vehicle::DOORACTION_CLOSE, door, delay * scale, time * scale, mpCharacter, pSeq));
+ pSeq->AddAction( pAction );
+ }
+ pSeq->EndSequence( );
+ }
+
+}
+
+
+/*
+==============================================================================
+InSim::InSim
+==============================================================================
+Description: Comment
+
+Parameters( Character* pCharacter )
+
+Returns:
+
+=============================================================================
+*/
+InSim::InSim( Character* pCharacter )
+:
+State( pCharacter )
+{
+}
+
+InSim::~InSim( void )
+{
+}
+
+void InSim::Enter( void )
+{
+ // clear previous actions
+ mpCharacter->GetActionController()->Clear();
+
+ // Sequence in the flail and get-up actions
+ Sequencer* pSeq = mpCharacter->GetActionController()->GetNextSequencer();
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new FlailAction( mpCharacter ) );
+ pSeq->EndSequence();
+
+ pSeq->BeginSequence();
+ pSeq->AddAction( new GetUpAction( mpCharacter ) );
+ pSeq->EndSequence();
+
+ /*
+ pSeq->BeginSequence();
+ pSeq->AddAction( new ChangeState<Loco>( mpCharacter ) );
+ pSeq->EndSequence();
+ */
+
+ return;
+}
+
+void InSim::Exit( void )
+{
+}
+
+void InSim::SequenceAction( void )
+{
+ // SequenceAction gets called when no other actions are left to do.
+ // In this state, it means that we're just done "getting up"...
+ // so time to transit back to Loco state...
+ mpCharacter->GetStateManager()->SetState<Loco>();
+}
+
+void InSim::Update( float timeins )
+{
+}
+
+
+
+};
diff --git a/game/code/ai/state.h b/game/code/ai/state.h
new file mode 100644
index 0000000..3c27b33
--- /dev/null
+++ b/game/code/ai/state.h
@@ -0,0 +1,232 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: state.h
+//
+// Description: Blahblahblah
+//
+// History: 6/12/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef STATE_H
+#define STATE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <memory/memorypool.h>
+#include <radmath/radmath.hpp>
+#include <events/eventenum.h>
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+class Character;
+class InCarAction;
+
+namespace CharacterAi
+{
+
+enum CharacterState
+{
+ NOSTATE,
+ LOCO,
+ INCAR,
+ GET_IN,
+ GET_OUT,
+ INSIM
+};
+
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class State
+{
+public:
+ State( Character* pCharacter );
+ virtual ~State( void );
+
+ virtual void Enter( void ) = 0;
+ virtual void Exit( void ) = 0;
+
+ virtual void SequenceAction( void ) = 0;
+ virtual void Update( float timeins ) = 0;
+
+ virtual CharacterState GetStateID( void ) const = 0;
+
+ static void* operator new( size_t size ) { return( sMemoryPool.Allocate( size ) ); }
+ static void operator delete( void* deadObject, size_t size ) { sMemoryPool.Free( deadObject, size ); }
+
+ // Declared but not defined to prevent access.
+ static void* operator new[]( size_t size );
+ static void operator delete[]( void* pMemory );
+protected:
+
+ Character* mpCharacter;
+
+private:
+ //Prevent wasteful constructor creation.
+ State();
+ State( const State& state );
+ State& operator=( const State& state );
+
+ static FBMemoryPool sMemoryPool;
+};
+
+class NoState
+:
+public State
+{
+public:
+ NoState(Character* c) : State(c) {}
+ virtual ~NoState( void ) {}
+
+ static CharacterState StateID( void ) { return NOSTATE; }
+ virtual CharacterState GetStateID( void ) const { return StateID(); }
+
+protected:
+ virtual void Enter( void ) {}
+ virtual void Exit( void ) {}
+
+ virtual void SequenceAction( void ) {}
+ virtual void Update( float timeins ) {}
+};
+
+class InCar
+:
+public State
+{
+public:
+
+ InCar( Character* pCharacter );
+ virtual ~InCar( void );
+
+ static CharacterState StateID( void ) { return INCAR; }
+ virtual CharacterState GetStateID( void ) const { return StateID(); }
+
+protected:
+
+ virtual void Enter( void );
+ virtual void Exit( void );
+
+ virtual void SequenceAction( void );
+ virtual void Update( float timeins );
+
+private:
+ InCarAction* m_InCarAction;
+
+ enum GetOutState
+ {
+ GETOUT_NONE,
+ GETOUT_TRYING,
+ GETOUT_COMITTED
+ };
+
+ bool mIsDriver : 1;
+ GetOutState m_GetOutState;
+};
+
+class Loco
+:
+public State
+{
+public:
+
+ Loco( Character* pCharacter );
+ virtual ~Loco( void );
+
+ static CharacterState StateID( void ) { return LOCO; }
+ virtual CharacterState GetStateID( void ) const { return StateID(); }
+
+protected:
+ unsigned mLastActionFrame;
+
+ virtual void Enter( void );
+ virtual void Exit( void );
+
+ virtual void SequenceAction( void );
+ virtual void Update( float timeins );
+};
+
+class InSim : public State
+{
+public:
+
+ InSim( Character* pCharacter );
+ virtual ~InSim( void );
+
+ static CharacterState StateID( void ) { return INSIM; }
+ virtual CharacterState GetStateID( void ) const { return StateID(); }
+
+protected:
+
+ virtual void Enter( void );
+ virtual void Exit( void );
+
+ virtual void SequenceAction( void );
+ virtual void Update( float timeins );
+};
+
+
+class GetIn : public State, public EventListener
+{
+public:
+
+ GetIn( Character* pCharacter );
+ virtual ~GetIn( void );
+
+ static CharacterState StateID( void ) { return GET_IN; }
+ virtual CharacterState GetStateID( void ) const { return StateID(); }
+
+protected:
+
+ virtual void Enter( void );
+ virtual void Exit( void );
+
+ virtual void SequenceAction( void );
+ virtual void Update( float timeins );
+
+ void HandleEvent( EventEnum id, void* pUserData );
+
+private:
+ void CalcGetInPath(int* nWaypoints, rmt::Vector* waypoints, bool* doingSlide, bool* jump);
+
+ bool mObstructed : 1;
+ bool mFirst : 1;
+ int mCollisionFailure : 3;
+};
+
+class GetOut : public State, public EventListener
+{
+public:
+
+ GetOut( Character* pCharacter );
+ virtual ~GetOut( void );
+
+ static CharacterState StateID( void ) { return GET_OUT; }
+ virtual CharacterState GetStateID( void ) const { return StateID(); }
+
+protected:
+ void DoGetOut(void);
+
+ virtual void Enter( void );
+ virtual void Exit( void );
+
+ virtual void SequenceAction( void );
+ virtual void Update( float timeins );
+
+ void HandleEvent( EventEnum id, void* pUserData );
+
+ bool mObstructed : 1;
+ bool mFirst : 1;
+ bool mPanic : 1;
+};
+
+}; // end of namespace
+#endif //STATE_H
diff --git a/game/code/ai/statemanager.cpp b/game/code/ai/statemanager.cpp
new file mode 100644
index 0000000..d31148a
--- /dev/null
+++ b/game/code/ai/statemanager.cpp
@@ -0,0 +1,107 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: statemanager.cpp
+//
+// Description: Implementation of class StateManager
+//
+// History: 6/12/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <ai/statemanager.h>
+#include <ai/state.h>
+#include <worldsim/character/character.h>
+#include <ai/sequencer/actioncontroller.h>
+
+namespace CharacterAi
+{
+
+//==============================================================================
+StateManager::StateManager( Character* pCharacter )
+:
+mpCharacter(pCharacter),
+mpCurrentState( new NoState(pCharacter) ),
+mpNextState( NULL )
+{
+}
+
+//==============================================================================
+StateManager::~StateManager()
+{
+ delete mpCurrentState;
+ delete mpNextState;
+}
+
+//==============================================================================
+Character* StateManager::GetCharacter()
+{
+ return mpCharacter;
+}
+//==============================================================================
+CharacterState StateManager::GetState( void ) const
+{
+ return mpCurrentState->GetStateID();
+}
+
+//==============================================================================
+void StateManager::SetState( State* state)
+{
+ if(mpNextState)
+ {
+ delete mpNextState;
+ }
+
+ mpNextState = state;
+}
+
+//==============================================================================
+void StateManager::ResetState( void )
+{
+ if(mpNextState)
+ {
+ delete mpNextState;
+ mpNextState = NULL;
+ }
+
+ mpCurrentState->Exit( );
+ mpCurrentState->Enter( );
+}
+
+//==============================================================================
+void StateManager::Update( float timeins )
+{
+ if(mpNextState)
+ {
+ mpCurrentState->Exit( );
+ delete mpCurrentState;
+ mpCurrentState = mpNextState;
+ mpNextState = NULL;
+ mpCurrentState->Enter( );
+ }
+
+ mpCurrentState->Update( timeins );
+
+ // sequence actions, if we can
+ if ( !mpCharacter->GetActionController()->IsBusy())
+ {
+ mpCurrentState->SequenceAction();
+ }
+}
+
+//==============================================================================
+const State* StateManager::GetCurrentState( void ) const
+{
+ return mpCurrentState;
+}
+
+}; // namespace CharacterAi \ No newline at end of file
diff --git a/game/code/ai/statemanager.h b/game/code/ai/statemanager.h
new file mode 100644
index 0000000..2b4b812
--- /dev/null
+++ b/game/code/ai/statemanager.h
@@ -0,0 +1,93 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: statemanager.h
+//
+// Description: Blahblahblah
+//
+// History: 6/12/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef STATEMANAGER_H
+#define STATEMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <ai/state.h>
+
+//========================================
+// Forward References
+//========================================
+class Character;
+
+namespace CharacterAi
+{
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class StateManager
+{
+public:
+
+ StateManager( Character* pCharacter );
+ ~StateManager();
+
+ const Character* GetCharacter() const;
+ Character* GetCharacter();
+ const State* GetCurrentState( void ) const;
+ const State* GetNextState( void ) const { return mpNextState;};
+
+ CharacterState GetState( void ) const;
+
+ template <class STATE> void SetState(void)
+ {
+ // Do nothing if same as old state.
+ if ( (!mpNextState && (mpCurrentState->GetStateID() == STATE::StateID())) ||
+ (mpNextState && (mpNextState->GetStateID() == STATE::StateID())) )
+ {
+ return;
+ }
+
+ SetState(new STATE(mpCharacter));
+ }
+
+ void SetState( State* pState );
+ void ResetState( void );
+
+ void Update( float timeins );
+
+private:
+ //Prevent wasteful constructor creation.
+ StateManager( const StateManager& statemanager );
+ StateManager& operator=( const StateManager& statemanager );
+
+ Character* mpCharacter;
+ State* mpCurrentState;
+ State* mpNextState;
+};
+
+template <class STATE>
+void SetState( StateManager* sm )
+{
+ // Do nothing if same as old state.
+ const State* currentState = sm->GetCurrentState();
+ const State* nextState = sm->GetNextState();
+ if ( (!nextState && (currentState->GetStateID() == STATE::StateID())) ||
+ (nextState && (nextState->GetStateID() == STATE::StateID())) )
+ {
+ return;
+ }
+ {
+ Character* character = sm->GetCharacter();
+ sm->SetState( new STATE( character ) );
+ }
+}
+
+};
+#endif //STATEMANAGER_H
diff --git a/game/code/ai/vehicle/allaivehicle.cpp b/game/code/ai/vehicle/allaivehicle.cpp
new file mode 100644
index 0000000..48ab986
--- /dev/null
+++ b/game/code/ai/vehicle/allaivehicle.cpp
@@ -0,0 +1,7 @@
+#include <ai/vehicle/chaseai.cpp>
+#include <ai/vehicle/potentialfield.cpp>
+#include <ai/vehicle/potentials.cpp>
+#include <ai/vehicle/trafficai.cpp>
+#include <ai/vehicle/vehicleai.cpp>
+#include <ai/vehicle/waypointai.cpp>
+#include <ai/vehicle/vehicleairender.cpp> \ No newline at end of file
diff --git a/game/code/ai/vehicle/chaseai.cpp b/game/code/ai/vehicle/chaseai.cpp
new file mode 100644
index 0000000..891a7d2
--- /dev/null
+++ b/game/code/ai/vehicle/chaseai.cpp
@@ -0,0 +1,558 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement ChaseAI
+//
+// History: 10/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <ai/vehicle/chaseai.h>
+
+#include <mission/gameplaymanager.h>
+
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#include <roads/roadsegment.h>
+#include <roads/geometry.h>
+#include <roads/roadmanager.h>
+#include <roads/road.h>
+
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/harass/chasemanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+ChaseAI::ChaseAI( Vehicle* pVehicle, float beelineDist ) :
+
+ VehicleAI(
+ pVehicle,
+ AI_CHASE,
+ true,
+ VehicleAI::DEFAULT_MIN_SHORTCUT_SKILL,
+ VehicleAI::DEFAULT_MAX_SHORTCUT_SKILL,
+ false ),
+
+ mpTarget( NULL ),
+ mTargetHasMovedToAnotherPathElement( true ),
+ mTargetRoadSegment( NULL ),
+ mTargetRoadSegmentT( 0.0f ),
+ mTargetRoadT( 0.0f ),
+ mBeelineDist( beelineDist )
+{
+ mTargetPathElement.elem = NULL;
+ rAssert( beelineDist >= 0.0f );
+}
+
+ChaseAI::~ChaseAI()
+{
+}
+
+void ChaseAI::Initialize()
+{
+ // VehicleAI::Initialize will call our reset,
+ // our reset will call VehicleAI::Reset, so it's all good
+ VehicleAI::Initialize();
+}
+
+void ChaseAI::Reset()
+{
+ mpTarget = NULL;
+ mTargetHasMovedToAnotherPathElement = true;
+ mTargetPathElement.elem = NULL;
+ mTargetRoadSegment = NULL;
+ mTargetRoadSegmentT = 0.0f;
+ mTargetRoadT = 0.0f;
+
+ VehicleAI::Reset();
+}
+
+void ChaseAI::Update( float timeins )
+{
+ if ( GetState() == STATE_LIMBO)
+ {
+ return;
+ }
+
+ UpdateSelf();
+ UpdateTarget();
+ UpdateSegments();
+
+ if( mpTarget )
+ {
+ if( GetState() == STATE_WAITING && mTargetPathElement.elem != NULL )
+ {
+ SetState( STATE_ACCEL );
+ }
+ rmt::Vector targetPos;
+ mpTarget->GetPosition( targetPos );
+
+ rmt::Vector mypos;
+ GetVehicle()->GetPosition( &mypos );
+
+ rmt::Vector nextDestination;
+ GetNextDestination( nextDestination );
+
+ rmt::Vector vec2next;
+ vec2next.Sub( nextDestination, mypos );
+
+ rmt::Vector vec2target;
+ vec2target.Sub( targetPos, mypos );
+
+ // Not using this currently
+ //
+ //float dp = vec2next.DotProduct( vec2target );
+
+ float dist2target = vec2target.Magnitude(); // *** SQUARE ROOT! ***
+
+ // If we're not close enough to player, don't beeline...
+ if( dist2target >= mBeelineDist )
+ {
+ FollowRoad();
+ }
+ else
+ {
+ // If we're close enough to beeline, we still need
+ // to see if there are things in the way...
+ //
+ // First we construct a line between us and target
+ // If this line crosses over a segment's flanking edges,
+ // then we don't beeline..
+ //
+ rmt::Vector start, end;
+ GetPosition( &start );
+ mpTarget->GetPosition( end );
+
+ // we only need to worry if start & end aren't the same points
+ if( !start.Equals( end, 0.001f ) )
+ {
+ bool beeline = true;
+
+ #define CHASEAI_CHECK_STATICS
+ #ifdef CHASEAI_CHECK_STATICS
+ // grab list of statics
+ WorldPhysicsManager::StaticsInCollisionDetection** statics =
+ ::GetWorldPhysicsManager()->mCurrentStatics;
+ WorldPhysicsManager::StaticsInCollisionDetection* myStatics =
+ statics[ GetVehicle()->mCollisionAreaIndex ];
+ int maxStatics = ::GetWorldPhysicsManager()->mMaxStatics;
+
+ // iterate through list of statics
+ for( int j=0; j<maxStatics; j++ )
+ {
+ //rAssert( myStatics[j].clean );
+ StaticPhysDSG* obj = myStatics[j].mStaticPhysDSG;
+ if( !obj )
+ {
+ continue;
+ }
+ sim::SimState* simState = obj->GetSimState();
+ if( !simState )
+ {
+ continue;
+ }
+ sim::CollisionObject* colObj = simState->GetCollisionObject();
+ if( !colObj )
+ {
+ continue;
+ }
+ sim::CollisionVolume* colVol = colObj->GetCollisionVolume();
+ if( !colVol )
+ {
+ continue;
+ }
+
+ beeline = !TestIntersectBBox( start, end, colVol );
+ if( !beeline )
+ {
+ //rDebugPrintf( "Blocked by %s\n", colVol->GetCollisionObject()->GetName() );
+ }
+ /*
+ switch( colVol->Type() )
+ {
+ case sim::CollisionVolumeType:
+ rAssert( false );
+ break;
+ case sim::BBoxVolumeType:
+ beeline = TestIntersectBBox( start, end, colVol );
+ break;
+ case sim::SphereVolumeType:
+ beeline = TestIntersectSphere( start, end, colVol );
+ break;
+ case sim::CylinderVolumeType:
+ beeline = TestIntersectCylinder( start, end, colVol );
+ break;
+ case sim::OBBoxVolumeType:
+ beeline = TestIntersectOBBox( start, end, colVol );
+ break;
+ case sim::WallVolumeType:
+ beeline = TestIntersectWall( start, end, colVol );
+ break;
+ }
+ */
+ }
+ #endif
+
+ // Beeline here...
+ if( !beeline )
+ {
+ //rDebugPrintf( "**** FOLLOWING ROAD! ****\n" );
+ FollowRoad();
+ }
+ else
+ {
+ //rDebugPrintf( "**** BEELINING ****\n" );
+ Beeline( dist2target );
+ }
+ }
+ else
+ {
+ Beeline( dist2target );
+ }
+ }
+
+ // if we just hit another vehicle (not the other way around), transit to stunned,
+ // then reverse and try hitting again...
+ if( GetVehicle()->mCollidedWithVehicle && !GetVehicle()->mWasHitByVehicle )
+ {
+ mSecondsStunned = 0.0f;
+ SetState( STATE_STUNNED );
+ }
+ }
+ else // no one to chase!
+ {
+ //rDebugPrintf( "*** CHASE AI: No one to chase! ***\n" );
+ SetState( STATE_WAITING );
+ FollowRoad();
+ }
+
+ DoSteering();
+
+ // TODO:
+ // Evade traffic or static?
+
+ CheckState( timeins );
+
+}//end update
+
+
+void ChaseAI::DoCatchUp( float timeins )
+{
+ // Nothing for now
+ mDesiredSpeedKmh = GetVehicle()->mDesignerParams.mDpTopSpeedKmh;
+ mShortcutSkillMod = 0;
+}
+
+
+
+bool ChaseAI::MustRepopulateSegments()
+{
+ // get target's position.
+ if( mpTarget == NULL )
+ {
+ // if no target, don't repopulate segments
+ return false;
+ }
+ return mTargetHasMovedToAnotherPathElement;
+}
+
+//
+// given a line segment from point "start" to point "end", determine if any point on
+// this line is close enough to the current target waypoint
+//
+bool ChaseAI::TestReachedTarget( const rmt::Vector& start, const rmt::Vector& end )
+{
+ if( mpTarget )
+ {
+ rmt::Vector targetPos;
+ mpTarget->GetPosition( targetPos );
+
+ rmt::Vector closestPos;
+ FindClosestPointOnLine( start, end, targetPos, closestPos );
+
+ float distSqr = (closestPos - targetPos).MagnitudeSqr();
+
+ // NOTE:
+ // We don't use mBeelineDist here because we want to still
+ // know how to get closer to the target if, for example,
+ // we fail to beeline because we detected some statics are in
+ // the way...
+ const float REACHED_TARGET_DIST_SQR = 100.0f;
+ return( distSqr <= REACHED_TARGET_DIST_SQR ); //(mBeelineDist*mBeelineDist) );
+ }
+ // if no target, then no waypoint, then we don't need to reach any waypoint...
+ return true;
+}
+
+void ChaseAI::GetClosestPathElementToTarget(
+ rmt::Vector& targetPos,
+ RoadManager::PathElement& elem,
+ RoadSegment*& seg,
+ float& segT,
+ float& roadT )
+{
+ elem.elem = NULL;
+ seg = NULL;
+ segT = 0.0f;
+ roadT = 0.0f;
+
+ if( mpTarget )
+ {
+ mpTarget->GetPosition( targetPos );
+ elem = mTargetPathElement;
+ seg = (RoadSegment*) mTargetRoadSegment;
+ segT = mTargetRoadSegmentT;
+ roadT = mTargetRoadT;
+ }
+}
+
+
+void ChaseAI::UpdateTarget()
+{
+ mTargetHasMovedToAnotherPathElement = false;
+
+ mpTarget = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ if( mpTarget )
+ {
+ // the Avatar keeps a notion of its nearest road/intersection entities
+
+ RoadManager::PathElement elem;
+ elem.elem = NULL;
+ RoadSegment* seg = NULL;
+ float segT = 0.0f;
+ float roadT = 0.0f;
+
+ mpTarget->GetLastPathInfo( elem, seg, segT, roadT );
+
+ if( elem.elem != NULL )
+ {
+ // update old values only if the new values are good..
+ if( mTargetPathElement != elem )
+ {
+ mTargetHasMovedToAnotherPathElement = true;
+ mTargetPathElement = elem;
+ }
+ if( seg )
+ {
+ if( mTargetRoadSegment != seg )
+ {
+ mTargetRoadSegment = seg;
+ }
+ mTargetRoadSegmentT = segT;
+ mTargetRoadT = roadT;
+ }
+ }
+ if( mTargetPathElement.elem == NULL ) // no target, so ... ya...
+ {
+ SetState(STATE_WAITING);
+ }
+ else
+ {
+ if( GetState() == STATE_WAITING )
+ {
+ SetState(STATE_ACCEL);
+ }
+ }
+ }
+}
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+void ChaseAI::Beeline( float dist2target )
+{
+ if( mpTarget != NULL )
+ {
+ // we want to re-pathfind after we come out of beeline
+ // so we do this..
+ ResetSegments();
+
+ rmt::Vector targetPos;
+ mpTarget->GetPosition( targetPos );
+
+ rmt::Vector vel;
+
+ float speed = GetVehicle()->mSpeed;
+ float timetoimpact = 1000.0f;
+ if( speed > 0.0f )
+ {
+ timetoimpact = dist2target / speed;
+ }
+ rAssert( !rmt::IsNan( timetoimpact ) );
+
+ mpTarget->GetVelocity( vel ) ;
+ speed = mpTarget->GetSpeedMps();
+ float scale = 3.0f;
+ if( speed > 0.0f )
+ {
+ scale = timetoimpact / speed;
+ }
+ rAssert( !rmt::IsNan( scale ) );
+
+ vel.Scale( scale, scale, scale );
+
+ rmt::Vector nexttargetpos;
+ nexttargetpos.Add( targetPos, vel );
+
+ SetDestination( nexttargetpos );
+ SetNextDestination( nexttargetpos );
+ }
+}
+
+bool ChaseAI::TestIntersectBBox( rmt::Vector start, rmt::Vector end, sim::CollisionVolume* colVol )
+{
+ rAssert( colVol != NULL );
+ //rAssert( colVol->Type() == sim::BBoxVolumeType );
+
+ // work in 2D
+ start.y = 0.0f;
+ end.y = 0.0f;
+
+ sim::CollisionVolume* bbox = colVol;
+ //sim::BBoxVolume* bbox = (sim::BBoxVolume*) colVol;
+ rmt::Vector pos = bbox->mPosition; // center
+ rmt::Vector size = bbox->mBoxSize / 2.0f; // vector to one of corners
+
+ // obtain the vertices and test that they're all on same side of
+ // the line (from us to target). Since volume is axis-aligned,
+ // and since we're working only in 2D, only need the top 4 vertices
+ // of the box.
+ rmt::Vector tmp;
+ rmt::Vector vec0,vec1;
+
+ tmp = size * 2.0f;
+
+ // first point
+ vec0 = pos + size;
+ vec0.y = 0.0f;
+
+ // second point
+ vec1 = vec0;
+ vec1.x -= tmp.x;
+
+ if( !PointsOnSameSideOfLine( vec0, vec1, start, end ) )
+ {
+ return true;
+ }
+
+ // third point
+ vec1.z -= tmp.z;
+ if( !PointsOnSameSideOfLine( vec0, vec1, start, end ) )
+ {
+ return true;
+ }
+
+ // fourth point
+ vec1.x += tmp.x;
+ if( !PointsOnSameSideOfLine( vec0, vec1, start, end ) )
+ {
+ return true;
+ }
+ return false;
+}
+
+/*** HOPE I DON"T HAVE TO IMPLEMENT THESE JUST YET
+
+bool ChaseAI::TestIntersectOBBox( rmt::Vector start, rmt::Vector end, sim::CollisionVolume* colVol )
+{
+ rAssert( colVol != NULL );
+ rAssert( colVol->Type() == sim::OBBoxVolumeType );
+
+ sim::OBBoxVolume* obbox = (sim::OBBoxVolume*)colVol;
+
+ rmt::Vector a, p1, p2;
+ for (int i=0; i<4; i++)
+ {
+ }
+
+ return false;
+}
+bool ChaseAI::TestIntersectSphere( rmt::Vector start, rmt::Vector end, sim::CollisionVolume* colVol )
+{
+ rAssert( colVol != NULL );
+ rAssert( colVol->Type() == sim::SphereVolumeType );
+
+ sim::SphereVolume* sphere = (sim::SphereVolume*)colVol;
+
+ rmt::Sphere s( sphere->mPosition, sphere->mSphereRadius );
+
+ return TestIntersectLineSphere( start, end, sphere );
+}
+
+bool ChaseAI::TestIntersectCylinder( rmt::Vector start, rmt::Vector end, sim::CollisionVolume* colVol )
+{
+ rAssert( colVol != NULL );
+ rAssert( colVol->Type() == sim::CylinderVolumeType );
+
+ sim::CylinderVolume* cyl = (sim::CylinderVolume*)colVol;
+ return false;
+}
+
+bool ChaseAI::TestIntersectWall( rmt::Vector start, rmt::Vector end, sim::CollisionVolume* colVol )
+{
+ rAssert( colVol != NULL );
+ rAssert( colVol->Type() == sim::WallVolumeType );
+
+ sim::WallVolume* wall = (sim::WallVolume*)colVol;
+ return false;
+}
+
+****/
+
+int ChaseAI::RegisterHudMapIcon()
+{
+ int iconID = -1;
+
+ rmt::Vector initialLoc;
+ GetVehicle()->GetPosition( &initialLoc );
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ ChaseManager* chaseManager = GetGameplayManager()->GetChaseManager( 0 );
+ rAssert( chaseManager != NULL );
+ if( chaseManager->IsChaseVehicle( this->GetVehicle() ) )
+ {
+ iconID = currentHud->GetHudMap( 0 )->RegisterIcon( HudMapIcon::ICON_AI_HIT_N_RUN, initialLoc, this );
+ }
+ else
+ {
+ iconID = currentHud->GetHudMap( 0 )->RegisterIcon( HudMapIcon::ICON_AI_CHASE, initialLoc, this );
+ }
+ }
+
+ return iconID;
+}
+
diff --git a/game/code/ai/vehicle/chaseai.h b/game/code/ai/vehicle/chaseai.h
new file mode 100644
index 0000000..407cbce
--- /dev/null
+++ b/game/code/ai/vehicle/chaseai.h
@@ -0,0 +1,97 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: chaseai.h
+//
+// Description: Blahblahblah
+//
+// History: 10/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef CHASEAI_H
+#define CHASEAI_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <ai/vehicle/vehicleai.h>
+#include <simcollision/collisionvolume.hpp>
+//========================================
+// Forward References
+//========================================
+//class Vehicle;
+class Avatar;
+class CollisionVolume;
+
+
+static const float TAIL_BEELINE_DIST = 20.0f;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ChaseAI : public VehicleAI
+{
+public:
+ ChaseAI( Vehicle* pVehicle, float beelineDist=TAIL_BEELINE_DIST );
+ virtual ~ChaseAI();
+
+ virtual void Reset();
+ virtual void Initialize();
+ virtual void Update( float timeins );
+
+protected:
+ virtual bool MustRepopulateSegments();
+
+ virtual bool TestReachedTarget( const rmt::Vector& start, const rmt::Vector& end );
+
+ virtual void GetClosestPathElementToTarget(
+ rmt::Vector& targetPos,
+ RoadManager::PathElement& elem,
+ RoadSegment*& seg,
+ float& segT,
+ float& roadT );
+
+ virtual void DoCatchUp( float timeins );
+
+ void UpdateTarget();
+
+ void Beeline( float dist2target );
+
+ // All return true if line segment defined by start & end go through the volume colVol
+ bool TestIntersectBBox( rmt::Vector start, rmt::Vector end, sim::CollisionVolume* colVol );
+
+ /*** HOPE I DON'T HAVE TO IMPLEMENT THESE JUST YET
+ bool TestIntersectOBBox( rmt::Vector start, rmt::Vector end, sim::CollisionVolume* colVol );
+ bool TestIntersectSphere( rmt::Vector start, rmt::Vector end, sim::CollisionVolume* colVol );
+ bool TestIntersectCylinder( rmt::Vector start, rmt::Vector end, sim::CollisionVolume* colVol );
+ bool TestIntersectWall( rmt::Vector start, rmt::Vector end, sim::CollisionVolume* colVol );
+ ***/
+
+private:
+ virtual int RegisterHudMapIcon();
+
+ Avatar* mpTarget;
+
+ bool mTargetHasMovedToAnotherPathElement : 1;
+
+ RoadManager::PathElement mTargetPathElement;
+ RoadSegment* mTargetRoadSegment;
+ float mTargetRoadSegmentT;
+ float mTargetRoadT;
+
+ float mBeelineDist;
+
+private:
+ //Prevent wasteful constructor creation.
+ ChaseAI( const ChaseAI& chaseai );
+ ChaseAI& operator=( const ChaseAI& chaseai );
+
+};
+
+
+#endif //CHASEAI_H
diff --git a/game/code/ai/vehicle/potentialfield.cpp b/game/code/ai/vehicle/potentialfield.cpp
new file mode 100644
index 0000000..6872768
--- /dev/null
+++ b/game/code/ai/vehicle/potentialfield.cpp
@@ -0,0 +1,374 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement PotentialField
+//
+// History: 22/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <ai/vehicle/potentialfield.h>
+#include <ai/vehicle/potentials.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+static const float Z_ADJUST = 5.0f;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// PotentialField::PotentialField
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PotentialField::PotentialField()
+{
+}
+
+//==============================================================================
+// PotentialField::~PotentialField
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PotentialField::~PotentialField()
+{
+}
+
+//=============================================================================
+// PotentialField::Clear
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PotentialField::Clear( const float value )
+{
+ for( int i = 0; i < MAX_FIELD_POTENTIALS; i++ )
+ {
+ mPotentials[ i ].Clear( value );
+ }
+}
+
+//=============================================================================
+// PotentialField::Initialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const float xdim, const float ydim, const rmt::Vector& pos, const rmt::Vector& heading )
+//
+// Return: void
+//
+//=============================================================================
+void PotentialField::Initialize( const float xdim, const float ydim, const rmt::Vector& pos, const rmt::Vector& heading )
+{
+ mXscale = xdim / Potentials::MAX_POTENTIALS;
+ mZscale = ydim / MAX_FIELD_POTENTIALS;
+
+ mZaxis = heading;
+ mXaxis.CrossProduct( mZaxis, rmt::Vector( 0.0f, 1.0f, 0.0f ) );//, mZaxis );
+
+ //
+ // Adjust the origin by half the length of the xdim to move the
+ // origin from the bottom-centre of the field to the bottom-left
+ //
+ rmt::Vector tmp = mXaxis;
+ tmp.Scale( static_cast<float>( -xdim / 2 ));
+
+ mOrigin = pos;
+ mOrigin.Add( tmp );
+
+ //
+ // Move the origin forward on the z axis
+ //
+ tmp = mZaxis;
+ tmp.Scale( Z_ADJUST, Z_ADJUST, Z_ADJUST );
+ mOrigin.Add( tmp );
+}
+
+//=============================================================================
+// PotentialField::IndexToPos
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const int x, const int y, rmt::Vector& pos )
+//
+// Return: void
+//
+//=============================================================================
+void PotentialField::IndexToPos( const int x, const int y, rmt::Vector& pos )
+{
+ float xpos = static_cast<float>( x ) * mXscale;
+ float zpos = static_cast<float>( y ) * mZscale;
+
+ pos = mOrigin;
+
+ rmt::Vector tmp = mXaxis;
+ tmp.Scale( xpos, xpos, xpos );
+ pos.Add( tmp );
+
+ tmp = mZaxis;
+ tmp.Scale( zpos, zpos, zpos );
+ pos.Add( tmp );
+}
+
+//=============================================================================
+// PotentialField::PosToIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector& pos, int& x, int& y )
+//
+// Return: void
+//
+//=============================================================================
+void PotentialField::PosToIndex( rmt::Vector& pos, int& x, int& y )
+{
+ rmt::Vector relpos;
+ relpos.Sub( pos, mOrigin );
+
+ float xpos = mXaxis.DotProduct( relpos ) / mXscale;
+ float zpos = mZaxis.DotProduct( relpos ) / mZscale;
+
+ x = static_cast<int>( xpos );
+ y = static_cast<int>( zpos );
+}
+
+//=============================================================================
+// PotentialField::SetPotential
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const int x, const int y, const float value )
+//
+// Return: void
+//
+//=============================================================================
+void PotentialField::SetPotential( const int x, const int y, const float value )
+{
+ rAssert( y < MAX_FIELD_POTENTIALS );
+ mPotentials[ y ].SetPotential( x, value );
+}
+
+//=============================================================================
+// PotentialField::GetPotential
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const int x, const int y )
+//
+// Return: float
+//
+//=============================================================================
+float PotentialField::GetPotential( const int x, const int y ) const
+{
+ rAssert( y < MAX_FIELD_POTENTIALS );
+ return mPotentials[ y ].GetPotential( x );
+}
+
+//=============================================================================
+// PotentialField::AddPotential
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector& pos, const float value, const float falloff )
+//
+// Return: void
+//
+//=============================================================================
+bool PotentialField::AddPotential( rmt::Vector& pos, const float value, const float radius, const float falloff )
+{
+ int xpos;
+ int zpos;
+
+ PosToIndex( pos, xpos, zpos );
+
+ //
+ // Calc the distance is metres until the potential has no influence
+ //
+ float disttozero = radius + rmt::Fabs( value / falloff );
+ float disttozerosqr = disttozero * disttozero;
+
+ int xsize = 2 * static_cast<int>( disttozero / mXscale );
+ int zsize = 2 * static_cast<int>( disttozero / mZscale );
+
+ int xstart = xpos - xsize / 2;
+ int zstart = zpos - zsize / 2;
+
+ if( xstart < 0 )
+ {
+ //
+ // xstart is -ve, so by adding it to xsize we decrease
+ // xsize by the right amount
+ //
+ xsize += xstart;
+ xstart = 0;
+ }
+ if( xstart + xsize >= Potentials::MAX_POTENTIALS )
+ {
+ xsize = Potentials::MAX_POTENTIALS - xstart - 1;
+ }
+
+ if( zstart < 0 )
+ {
+ zsize -= zstart;
+ zstart = 0;
+ }
+ if( zstart + zsize >= MAX_FIELD_POTENTIALS )
+ {
+ zsize = Potentials::MAX_POTENTIALS - zstart - 1;
+ }
+
+ if( xsize <= 0 || zsize <= 0
+ || xstart >= Potentials::MAX_POTENTIALS
+ || zstart >= MAX_FIELD_POTENTIALS )
+ {
+ return false;
+ }
+
+ //
+ // Sign the falloff to make for easier math down below
+ //
+ float signedfalloff;
+ if( value < 0.0f )
+ {
+ signedfalloff = -falloff;
+ }
+ else
+ {
+ signedfalloff = falloff;
+ }
+
+ rmt::Vector newpos;
+ newpos.y = 0.0f;
+
+ for( int z = zstart; z < zstart + zsize; z++ )
+ {
+ for( int x = xstart; x < xstart + xsize; x++ )
+ {
+ //
+ // Convert the location in field-space to worldspace
+ //
+ newpos.x = ( x - xpos ) * mXscale;
+ newpos.z = ( z - zpos ) * mZscale;
+
+ //
+ // Only do the sqrt when necessary
+ //
+ float dist = newpos.MagnitudeSqr();
+
+ if( dist <= disttozerosqr )
+ {
+ dist = rmt::Sqrt( dist );
+
+ float newvalue;
+ if( dist > radius )
+ {
+ newvalue = value - (dist - radius) * signedfalloff;
+ }
+ else
+ {
+ newvalue = value;
+ }
+
+ //
+ // Accumulate potential, or else new potentials cancel out the old
+ // ones
+ //
+ float oldvalue = GetPotential( x, z );
+
+ SetPotential( x, z, oldvalue + newvalue );
+ }
+ }
+ }
+
+ return true;
+}
+
+//=============================================================================
+// PotentialField::FindBestPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector& pos )
+//
+// Return: bool
+//
+//=============================================================================
+bool PotentialField::FindBestPosition( rmt::Vector& pos, const float targetdist )
+{
+ bool bFound = false;
+ float bestvalue = -100.0f;
+ int bestx = 0;
+ int besty = 0;
+
+ rmt::Vector tmp = mZaxis;
+ float d = targetdist - Z_ADJUST;
+ if( d < 0.0f )
+ {
+ d = 0.0f;
+ }
+ tmp.Scale( d, d, d );
+ tmp.Add( mOrigin );
+
+ int tx, y;
+ PosToIndex( tmp, tx, y );
+
+ for( int x = 0; x < Potentials::MAX_POTENTIALS; x++ )
+ {
+ float value = mPotentials[ y ].GetPotential( x );
+ if( value > bestvalue )
+ {
+ bFound = true;
+
+ bestvalue = value;
+ bestx = x;
+ besty = y;
+ }
+ }
+
+ if( bFound )
+ {
+ IndexToPos( bestx, besty, pos );
+ }
+
+ return bFound;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/ai/vehicle/potentialfield.h b/game/code/ai/vehicle/potentialfield.h
new file mode 100644
index 0000000..08517b9
--- /dev/null
+++ b/game/code/ai/vehicle/potentialfield.h
@@ -0,0 +1,89 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: potentialfield.h
+//
+// Description: Blahblahblah
+//
+// History: 22/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef POTENTIALFIELD_H
+#define POTENTIALFIELD_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <ai/vehicle/potentials.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class PotentialField
+{
+public:
+ PotentialField();
+ virtual ~PotentialField();
+
+ static const int MAX_FIELD_POTENTIALS = 50;
+
+ void Clear( const float value = -1.0f );
+
+ //
+ // xdim is the distance in metres to the left and right of the field
+ // ydim is the distance along the heading vector of the field
+ // pos is the position of the vehicle (becomes the origin)
+ // heading is the direction of the vehicle (forms the z axis of the field)
+ //
+ void Initialize( const float xdim, const float ydim, const rmt::Vector& pos, const rmt::Vector& heading );
+
+ void IndexToPos( const int x, const int y, rmt::Vector& pos );
+ void PosToIndex( rmt::Vector& pos, int& x, int& y );
+
+ void SetPotential( const int x, const int y, const float value );
+ float GetPotential( const int x, const int y ) const;
+
+ //
+ // pos is the location in world space of the potential
+ // value is the potential to be added
+ // falloff is how much, per metre, the potential fades
+ //
+ bool AddPotential( rmt::Vector& pos, const float value,
+ const float radius, const float falloff = 0.1f );
+
+ //
+ // returns in pos the location in world space of the highest potential
+ // at distance targetdist.
+ // targetdist is the distance along the field's z axis (the heading vector)
+ // at which the highest potential will be found
+ //
+ bool FindBestPosition( rmt::Vector& pos, const float targetdist );
+
+private:
+
+ //Prevent wasteful constructor creation.
+ PotentialField( const PotentialField& potentialfield );
+ PotentialField& operator=( const PotentialField& potentialfield );
+
+ Potentials mPotentials[ MAX_FIELD_POTENTIALS ];
+
+ float mXdim;
+ float mZdim;
+ float mXscale;
+ float mZscale;
+ rmt::Vector mXaxis;
+ rmt::Vector mZaxis;
+ rmt::Vector mOrigin;
+};
+
+
+#endif //POTENTIALFIELD_H
diff --git a/game/code/ai/vehicle/potentials.cpp b/game/code/ai/vehicle/potentials.cpp
new file mode 100644
index 0000000..683dbca
--- /dev/null
+++ b/game/code/ai/vehicle/potentials.cpp
@@ -0,0 +1,87 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement Potentials
+//
+// History: 22/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <ai/vehicle/potentials.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Potentials::Potentials
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Potentials::Potentials()
+{
+}
+
+//==============================================================================
+// Potentials::~Potentials
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Potentials::~Potentials()
+{
+}
+
+//=============================================================================
+// Potentials::Clear
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Potentials::Clear( const float value )
+{
+ for( int i = 0; i < MAX_POTENTIALS; i++ )
+ {
+ //mValues[ i ] = -rmt::Fabs(static_cast<float>( i - MAX_POTENTIALS / 2 ) / static_cast<float>( MAX_POTENTIALS ));
+ mValues[ i ] = value;
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/ai/vehicle/potentials.h b/game/code/ai/vehicle/potentials.h
new file mode 100644
index 0000000..8dd950d
--- /dev/null
+++ b/game/code/ai/vehicle/potentials.h
@@ -0,0 +1,94 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: potentials.h
+//
+// Description: Blahblahblah
+//
+// History: 22/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef POTENTIALS_H
+#define POTENTIALS_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <radmath/radmath.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class Potentials
+{
+public:
+ Potentials();
+ virtual ~Potentials();
+
+ static const int MAX_POTENTIALS = 11;
+
+ void SetPotential( const int index, float value );
+ float GetPotential( const int index ) const;
+
+ void Clear( const float value );
+
+private:
+
+ //Prevent wasteful constructor creation.
+ Potentials( const Potentials& potentials );
+ Potentials& operator=( const Potentials& potentials );
+
+ float mValues[ MAX_POTENTIALS ];
+};
+
+//=============================================================================
+// Potentials::SetPotential
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const int index )
+//
+// Return: void
+//
+//=============================================================================
+inline void Potentials::SetPotential( const int index, float value )
+{
+ rAssert( index >= 0 && index < MAX_POTENTIALS - 1 );
+
+/* if( value > 1.0f )
+ {
+ value = 1.0f;
+ }
+ if( value < -1.0f )
+ {
+ value = -1.0f;
+ }*/
+ mValues[ index ] = value;
+}
+
+//=============================================================================
+// Potentials::GetPotential
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const int index )
+//
+// Return: float
+//
+//=============================================================================
+inline float Potentials::GetPotential( const int index ) const
+{
+ rAssert( index >= 0 && index < MAX_POTENTIALS );
+ return mValues[ index ];
+}
+
+#endif //POTENTIALS_H
diff --git a/game/code/ai/vehicle/trafficai.cpp b/game/code/ai/vehicle/trafficai.cpp
new file mode 100644
index 0000000..dd40cc0
--- /dev/null
+++ b/game/code/ai/vehicle/trafficai.cpp
@@ -0,0 +1,1591 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: TrafficAI.cpp
+//
+// Description: Implement TrafficAI
+//
+// History: 09/09/2002 + Accel/Decel behavior - Dusit Eakkachaichanvet
+// 06/08/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <stdlib.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <ai/vehicle/TrafficAI.h>
+#include <ai/vehicle/vehicleairender.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <roads/lane.h>
+#include <roads/intersection.h>
+#include <roads/road.h>
+#include <roads/roadsegment.h>
+#include <roads/roadsegmentdata.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/redbrick/trafficlocomotion.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/character/character.h>
+#include <render/culling/swaparray.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+const char* TRAFFICAI_NAMESPACE = "Traffic\\TrafficAI";
+
+//const float TrafficAI::LANE_CHANGE_DIST = 15.0f;
+const float LANE_CHANGE_SECONDS = 1.0f;
+const float MIN_LANE_CHANGE_DIST = 15.0f;
+
+const float TrafficAI::SECONDS_LOOKAHEAD = 1.5f;
+const float TrafficAI::LOOKAHEAD_MIN = 15.0f;
+//const float FRUSTRUM_DIST = 15.0f;
+const float DECEL_LO = -10.0f; // We don't want to decel any more slowly than this
+const float ACCEL = 3.5f;
+
+const float CAR_SPAN = 1.5f;
+const float CAR_LENGTH = 2.5f;
+const float CHAR_SPAN = 0.5f;
+const float CHAR_LENGTH = 0.5f;
+
+const float MIN_SECONDS_BETW_LANE_CHANGES = 5.0f;
+
+const float SECONDS_SWERVING = 0.8f;
+
+const float SWERVE_HEADLIGHT_SCALE = 1.0f;
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// TrafficAI::TrafficAI
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: none
+//
+// Return: N/A.
+//
+//=============================================================================
+TrafficAI::TrafficAI( Vehicle* vehicle ) :
+ AiVehicleController( vehicle )
+{
+ Init();
+}
+
+void TrafficAI::RegisterAI()
+{
+#ifdef DEBUGWATCH
+ mRenderHandle = VehicleAIRender::GetVehicleAIRender()->RegisterAI( this, VehicleAIRender::TYPE_TRAFFIC_AI );
+ rAssert( mRenderHandle != -1 );
+#endif
+}
+
+void TrafficAI::UnregisterAI()
+{
+#ifdef DEBUGWATCH
+ if( mRenderHandle >= 0 )
+ {
+ VehicleAIRender::GetVehicleAIRender()->UnregisterAI( mRenderHandle );
+ mRenderHandle = -1;
+ }
+#endif
+}
+
+TrafficAI::~TrafficAI()
+{
+ UnregisterDebugInfo();
+#ifdef DEBUGWATCH
+ UnregisterAI();
+#endif
+}
+
+//=============================================================================
+// TrafficAI::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficAI::Init()
+{
+ // BEHAVIOR
+ mDirection = LEFT; //STRAIGHT; //RIGHT;
+
+ // AFFILIATIONS
+ mLane = NULL;
+ mPrevLane = NULL;
+ mLaneIndex = 0;
+ mLaneLength = 0.0f;
+ mSegment = NULL;
+ mSegmentIndex = 0;
+ mT = 0.0f;
+ mAISpeed = 0.0f;
+ mPrevIntersection = NULL;
+
+ // STATES
+ mNeedToSuddenlyStop = false;
+ mIsActive = false;
+ mIsInIntersection = false;
+ if( mState == SWERVING )
+ {
+ StopSwerving();
+ }
+ mState = DRIVING;
+ mPrevState = DRIVING;
+ mSecondsDriving = 0.0f;
+
+ // CONSTANTS
+ mStopForSomethingDecel = 0.0f;
+
+ RegisterDebugInfo();
+ mRenderHandle = -1;
+
+ mSecondsSinceLastTriggeredImpedence = 0.0f;
+ mLastThingThatImpededMe = NULL;
+
+ mSecondsSinceLaneChange = 0.0f;
+
+ mOriginalMaxWheelTurnAngle = GetVehicle()->mDesignerParams.mDpMaxWheelTurnAngle;
+ mSwervingLeft = false;
+
+ mSwerveHighBeamOn = false;
+ mOriginalHeadlightScale = GetVehicle()->mGeometryVehicle->GetHeadlightScale();
+ mSecondsSwerveHighBeam = 0.0f;
+
+ mEndOfRoadPos.Set( 0.0f, 0.0f, 0.0f );
+}
+
+void TrafficAI::Init( Vehicle* vehicle,
+ Lane* lane,
+ unsigned int laneIndex,
+ RoadSegment* segment,
+ unsigned int segmentIndex,
+ float t,
+ float mps )
+{
+ rAssert( vehicle );
+ rAssert( lane );
+ rAssert( segment );
+
+ // BEHAVIOR
+ mDirection = LEFT;
+
+ // AFFILIATIONS
+ mLane = lane;
+ mPrevLane = lane;
+ mLaneIndex = laneIndex;
+ mSegment = segment;
+ mSegmentIndex = segmentIndex;
+ mT = t;
+ SetAISpeed( mps );
+ mPrevIntersection = NULL;
+
+ // STATES
+ mNeedToSuddenlyStop = false;
+ mState = DRIVING;
+ mPrevState = DRIVING;
+ mIsInIntersection = false;
+ mIsActive = false;
+ mSecondsDriving = 0.0f;
+
+ // CONSTANTS
+ mStopForSomethingDecel = 0.0f;
+
+ RegisterDebugInfo();
+ mRenderHandle = -1;
+
+ mSecondsSinceLastTriggeredImpedence = 0.0f;
+ mLastThingThatImpededMe = NULL;
+
+ mSecondsSinceLaneChange = 0.0f;
+
+ mOriginalMaxWheelTurnAngle = vehicle->mDesignerParams.mDpMaxWheelTurnAngle;
+ mSwervingLeft = false;
+
+ mSwerveHighBeamOn = false;
+ mOriginalHeadlightScale = GetVehicle()->mGeometryVehicle->GetHeadlightScale();
+ mSecondsSwerveHighBeam = 0.0f;
+
+ mEndOfRoadPos.Set( 0.0f, 0.0f, 0.0f );
+}
+
+//=============================================================================
+// TrafficAI::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficAI::Update( float seconds )
+{
+ // reset VehicleController values till we need them
+ mGas.SetValue( 0.0f );
+ mBrake.SetValue( 0.0f );
+ mReverse.SetValue( 0.0f );
+
+ //NOTE: we COULD keep handbrake on (so we don't drift to a stop)
+ // but we'll make traffic more an obstacle when you hit them.
+ mHandBrake.SetValue( 0.0f );
+
+ mSteering.SetValue( 0.0f );
+ mHorn.SetValue( 0.0f );
+
+ if( GetVehicle() == NULL )
+ {
+ return;
+ }
+ if( !mIsActive && mState != SWERVING )
+ {
+ return;
+ }
+
+ // ok, we're not dead... take handbrake off so we can drive normally
+ mHandBrake.SetValue( 0.0f );
+
+ mSecondsSinceLastTriggeredImpedence += seconds;
+ mSecondsSinceLaneChange += seconds;
+
+ BEGIN_PROFILE( "Traffic AI Update" );
+
+ switch ( mState )
+ {
+ case DEAD:
+ {
+ SetAISpeed( 0.0f );
+ }
+ break;
+
+ //case SPLINING: // fall thru
+ case DRIVING: // fall thru
+ {
+ mSecondsDriving += seconds;
+
+ ObstacleType foundSOMETHING = OT_NOTHING;
+ float distFromSOMETHINGSqr = 10000.0f;
+ void* SOMETHING = NULL;
+ bool SOMETHINGOnMyRight = false;
+ CheckForObstacles( foundSOMETHING, distFromSOMETHINGSqr, SOMETHING, SOMETHINGOnMyRight );
+
+ // ==========================================
+ // Determining & setting new speed/velocity
+ // ===========================================
+
+ switch( foundSOMETHING )
+ {
+
+ case OT_NOTHING:
+ {
+ // now there's really nothing in front of us... Yet inspite of
+ // this, we still want to lanechange on a whim some percentage of
+ // the time...
+ if( mSecondsSinceLaneChange >= MIN_SECONDS_BETW_LANE_CHANGES )
+ {
+ mSecondsSinceLaneChange = 0.0f;
+ int coinflip = rand() % 2;
+ if( coinflip == 0 )
+ {
+ AttemptLaneChange( foundSOMETHING, distFromSOMETHINGSqr, SOMETHING );
+ }
+ }
+ MaintainSpeed( seconds );
+ }
+ break;
+ case OT_PLAYERVEHICLE:
+ {
+ // ok, so whatever it was that impeded us is still impeding us...
+ PerhapsTriggerImpedence( foundSOMETHING, distFromSOMETHINGSqr, SOMETHING );
+
+ /*
+ // don't slow down for the player, but we can lanechange out of the way?
+ if( !AttemptLaneChange( foundSOMETHING, distFromSOMETHINGSqr, SOMETHING ) )
+ {
+ */
+
+ bool gonnaSwerve = false;
+
+ //////////////////////////////////////
+ // SWERVING LOGIC
+ //
+ // ok, so lanechange failed...
+ // if player is coming straight at us, swerve and flash headlights!
+ // else maintain speed
+ //
+ // If a new thing is impeding us, decide if we should swerve.
+ if( mLastThingThatImpededMe != SOMETHING )
+ {
+ int coinflip = 0; //rand() % 4;
+ if( coinflip == 0 ) // one in n chance to swerve
+ {
+ Vehicle* playerVehicle = GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle();
+ rAssert( playerVehicle );
+
+ Vehicle* myVehicle = GetVehicle();
+ rAssert( myVehicle );
+
+ const float MY_MIN_SPEED_THRESHOLD = 8.333f;
+ const float PLAYER_MIN_SPEED_THRESHOLD = 11.111f;
+ if( playerVehicle->mSpeed > PLAYER_MIN_SPEED_THRESHOLD &&
+ GetVehicle()->mTrafficLocomotion->mActualSpeed > MY_MIN_SPEED_THRESHOLD )
+ {
+ /*
+ rmt::Vector playerDir;
+ playerVehicle->GetHeading( &playerDir );
+ rAssert( rmt::Epsilon( playerDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ rmt::Vector myDir;
+ myVehicle->GetHeading( &myDir );
+ rAssert( rmt::Epsilon( myDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ const float INCOMING_COS_ALPHA = -0.9396926f;
+ if( myDir.Dot( playerDir ) < INCOMING_COS_ALPHA )
+ {
+ // ok, he's heading "right at us", so swerve!
+ StartSwerving();
+ gonnaSwerve = true;
+ }
+ */
+
+ // see if he's also headed right at us
+ rmt::Vector playerPos, playerDir, playerRight, playerUp;
+
+ playerVehicle->GetPosition( &playerPos );
+ playerVehicle->GetVelocity( &playerDir );
+
+ playerDir.Normalize(); // *** SQUARE ROOT! ***
+ rAssert( rmt::Epsilon( playerDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ playerUp.Set( 0.0f, 1.0f, 0.0f );
+
+ playerRight.CrossProduct( playerUp, playerDir );
+ playerRight.Normalize(); // *** SQUARE ROOT! ***
+ rAssert( rmt::Epsilon( playerRight.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ float span = CAR_SPAN + CAR_SPAN;
+ float lookAhead = GetLookAheadDistance();
+
+ rmt::Vector myPos;
+ myVehicle->GetPosition( &myPos );
+
+ bool onPlayersRight = false;
+ if( WillCollide( playerPos, playerDir, playerRight,
+ span, lookAhead, myPos, onPlayersRight ) )
+ {
+ // ok, he's heading more or less "right at us"...
+ //
+ // Test if we can safely swerve....
+ // From the recent WillCollide test, we know if we're on the right
+ // or the left side of the player's heading...
+ // From CheckForObstacles, we know which side of OUR heading,
+ // the player is on...
+ // So...
+ // If we're on his right and he's on our right, or if we're on his
+ // left and he's on our left, we can safely swerve. Otherwise,
+ // it's not clear which way we should swerve, so don't do it.
+
+ if( (onPlayersRight && SOMETHINGOnMyRight) ||
+ (!onPlayersRight && !SOMETHINGOnMyRight) )
+ {
+ StartSwerving( !onPlayersRight );
+ gonnaSwerve = true;
+ }
+ }
+ }
+ }
+ }
+ if( !gonnaSwerve )
+ {
+ StopForSomething( seconds, foundSOMETHING, distFromSOMETHINGSqr, SOMETHING );
+ }
+ else
+ {
+ MaintainSpeed( seconds );
+ }
+ }
+ break;
+ case OT_PLAYERCHARACTER:
+ {
+ PerhapsTriggerImpedence( foundSOMETHING, distFromSOMETHINGSqr, SOMETHING );
+ StopForSomething( seconds, foundSOMETHING, distFromSOMETHINGSqr, SOMETHING );
+ }
+ break;
+ case OT_NONPLAYERVEHICLE: // fall thru
+ case OT_NONPLAYERCHARACTER: // fall thru
+ {
+ // determine if we want to and can lane-change...
+ // if so, then do the appropriate actions...
+ if( AttemptLaneChange( foundSOMETHING, distFromSOMETHINGSqr, SOMETHING ) )
+ {
+ MaintainSpeed( seconds );
+ }
+ else
+ {
+ StopForSomething( seconds, foundSOMETHING, distFromSOMETHINGSqr, SOMETHING );
+ }
+ }
+ break;
+ case OT_ENDOFROAD:
+ {
+ if( mState != WAITING_AT_INTERSECTION )
+ {
+ mPrevIntersection = (Intersection*)SOMETHING;
+
+ // transit to WAITING AT INTERSECTION
+ mSecondsDriving = 0.0f;
+ SetState( WAITING_AT_INTERSECTION );
+
+ // set the lane's waiting vehicle to be this one!
+ mLane->mWaitingVehicle = GetVehicle()->mTrafficVehicle;
+
+ Road* myRoad = (Road*)mLane->GetRoad();
+
+ bool found = false;
+ SwapArray<Road*>* array = &(((Intersection*)SOMETHING)->mWaitingRoads);
+ for( int i=0; i<array->mUseSize; i++ )
+ {
+ if( myRoad == (*array)[i] )
+ {
+ found = true;
+ break;
+ }
+ }
+ if( !found )
+ {
+ array->Add( (Road*)myRoad );
+ }
+ }
+ StopForSomething( seconds, foundSOMETHING, distFromSOMETHINGSqr, SOMETHING );
+ }
+ break;
+ default:
+ {
+ rAssertMsg( false, "What's this type??" );
+ }
+ break;
+ } // end of switch( foundSOMETHING )
+ }
+ break;
+
+ case WAITING_AT_INTERSECTION:
+ {
+ // Get my pos
+ rmt::Vector myPos;
+ GetVehicle()->GetPosition( &myPos );
+
+ // Fake "found end of road obstacle"
+ ObstacleType foundSOMETHING = OT_ENDOFROAD;
+ float distFromSOMETHINGSqr = (mEndOfRoadPos - myPos).MagnitudeSqr();
+ void* SOMETHING = NULL;
+
+ StopForSomething( seconds, foundSOMETHING, distFromSOMETHINGSqr, SOMETHING );
+
+ // we gotta slow down or remain at rest
+ GetVehicle()->mGeometryVehicle->ShowBrakeLights();
+ }
+ break;
+
+ case WAITING_FOR_FREE_LANE:
+ {
+ // NOTE: Don't need to decel or anything. We just stop suddenly.
+ this->GetVehicle()->mGeometryVehicle->ShowBrakeLights();
+ SetAISpeed( 0.0f );
+ }
+ break;
+ case SPLINING: // fall thru
+ case LANE_CHANGING:
+ {
+ MaintainSpeed( seconds );
+ }
+ break;
+ case SWERVING:
+ {
+ // fiddle with the headlights
+ mSecondsSwerveHighBeam += seconds;
+
+ const float SECONDS_HIGH_BEAM_FLICKER_INTERVAL = 0.2f;
+ if( mSecondsSwerveHighBeam > SECONDS_HIGH_BEAM_FLICKER_INTERVAL )
+ {
+ if( mSwerveHighBeamOn )
+ {
+ // on long enough, turn it off
+ GetVehicle()->mGeometryVehicle->SetHeadlightScale( mOriginalHeadlightScale );
+ mSwerveHighBeamOn = false;
+ mSecondsSwerveHighBeam = 0.0f;
+ }
+ else
+ {
+ // off long enough, turn it on again
+ GetVehicle()->mGeometryVehicle->SetHeadlightScale( SWERVE_HEADLIGHT_SCALE );
+ mSwerveHighBeamOn = true;
+ mSecondsSwerveHighBeam = 0.0f;
+ }
+ }
+
+ mSecondsSwerving += seconds;
+ if( mSecondsSwerving > SECONDS_SWERVING )
+ {
+ StopSwerving();
+ }
+ else
+ {
+ // apply mGas and mSteering in one direction
+ // and maybe flash headlights too!
+ Swerve();
+ }
+ }
+ break;
+ default:
+ {
+ rAssert( false );
+ }
+ }
+
+ rAssert( !rmt::IsNan( mAISpeed ) );
+
+ END_PROFILE( "Traffic AI Update" );
+
+}
+
+void TrafficAI::StartSwerving( bool swerveRight )
+{
+ // transit to physics locomotion ===> vehicle->SetLocomotion( VL_PHYSICS )
+ // (this should already remove us from TrafficManager)
+ // start timer: mSecondsSwerving = 0.0f
+ Vehicle* v = GetVehicle();
+ rAssert( v );
+
+ rAssert( v->GetLocomotionType() == VL_TRAFFIC );
+
+ v->SetLocomotion( VL_PHYSICS );
+ mSecondsSwerving = 0.0f;
+
+ // turn on highbeam
+ mSwerveHighBeamOn = true;
+ v->mGeometryVehicle->SetHeadlightScale( SWERVE_HEADLIGHT_SCALE );
+ mSecondsSwerveHighBeam = 0.0f;
+
+ // hack turning so it's more sensitive
+ const float SWERVE_MAX_WHEEL_TURN_ANGLE = 120.0f;
+ v->mDesignerParams.mDpMaxWheelTurnAngle = SWERVE_MAX_WHEEL_TURN_ANGLE;
+
+ // figure out which direction we should be swerving
+
+ /*
+ // TODO:
+ // Do we go by playerPos being on OUR right, or our pos being
+ // on player's right? We should probably do the latter instead
+ // because our dir isn't all that accurate... and the player is
+ // probably going faster than we are!
+ // If we DO decide to go with testing our pos against player's
+ // velocity (to see if we're on his left or on his right), then
+ // we can just pass in the value from the caller to this function.
+ //
+
+ rmt::Vector myPos, myRight;
+ v->GetPosition( &myPos );
+ myRight = v->mVehicleTransverse;
+ rAssert( rmt::Epsilon( myRight.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( player );
+ rmt::Vector playerPos;
+ player->GetPosition( playerPos );
+
+ rmt::Vector toPlayer = playerPos - myPos;
+
+ if( toPlayer.Dot( myRight ) > 0.0f )
+ {
+ // player is on my right, so go left!
+ mSwervingLeft = true;
+ }
+ else
+ {
+ // player is on my left, so go right!
+ mSwervingLeft = false;
+ }
+ */
+ mSwervingLeft = !swerveRight;
+
+ SetState( SWERVING );
+}
+
+void TrafficAI::StopSwerving()
+{
+ Vehicle* v = GetVehicle();
+ rAssert( v );
+ rAssert( v->GetLocomotionType() == VL_PHYSICS );
+
+ mHandBrake.SetValue( 1.0f );
+
+ v->mDesignerParams.mDpMaxWheelTurnAngle = mOriginalMaxWheelTurnAngle;
+
+ // turn off any highbeaming
+ mSwerveHighBeamOn = false;
+ v->mGeometryVehicle->SetHeadlightScale( mOriginalHeadlightScale );
+ mSecondsSwerveHighBeam = 0.0f;
+
+ // nothing to do here really... hmm..
+ SetState( DEAD );
+}
+
+void TrafficAI::Swerve()
+{
+ // apply mGas and mSteering in one direction
+ // and maybe flash headlights too!
+
+ Vehicle* v = GetVehicle();
+ rAssert( v );
+ rAssert( v->GetLocomotionType() == VL_PHYSICS );
+
+ mGas.SetValue( 1.0f );
+ float swerveValue = mSwervingLeft? -1.0f : 1.0f;
+ mSteering.SetValue( swerveValue );
+
+
+
+ //mHorn.SetValue( 1.0f );
+ //mHandBrake.SetValue( 1.0f );
+}
+
+
+void TrafficAI::PerhapsTriggerImpedence( ObstacleType foundSOMETHING, float distSqr, void* SOMETHING )
+{
+ // not close enough to trigger
+ rAssert( foundSOMETHING == OT_PLAYERCHARACTER ||
+ foundSOMETHING == OT_PLAYERVEHICLE );
+
+ const float CAR_DIST_TRIGGER_IMPEDENCE_SQR = 100.0f;
+ const float CHAR_DIST_TRIGGER_IMPEDENCE_SQR = 64.0f;
+
+ const float SECONDS_BETW_TRIGGER_IMPEDENCE = 5.0f;
+
+ float testDistSqr = (foundSOMETHING == OT_PLAYERCHARACTER)?
+ CHAR_DIST_TRIGGER_IMPEDENCE_SQR :
+ CAR_DIST_TRIGGER_IMPEDENCE_SQR;
+
+ if( distSqr > testDistSqr )
+ {
+ return;
+ }
+
+ if( SOMETHING == mLastThingThatImpededMe )
+ {
+ // You again! Wait for awhile, then honk again!
+ if( mSecondsSinceLastTriggeredImpedence > SECONDS_BETW_TRIGGER_IMPEDENCE )
+ {
+ mSecondsSinceLastTriggeredImpedence = 0.0f;
+ ::GetEventManager()->TriggerEvent( EVENT_TRAFFIC_IMPEDED, GetVehicle() );
+ }
+ }
+ else // honk for an entirely new thing
+ {
+ mLastThingThatImpededMe = SOMETHING;
+ mSecondsSinceLastTriggeredImpedence = 0.0f;
+ ::GetEventManager()->TriggerEvent( EVENT_TRAFFIC_IMPEDED, GetVehicle() );
+ }
+}
+
+
+//=============================================================================
+// TrafficAI::GetDirection
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: TrafficAI
+//
+//=============================================================================
+TrafficAI::Direction TrafficAI::DecideTurn()
+{
+ int coinflip = 0;
+
+ // if rightmost lane, only go right or straight...
+ if( mLaneIndex == 0 )
+ {
+ coinflip = rand() % 2;
+ mDirection = (coinflip == 0)? STRAIGHT : RIGHT;
+ }
+ // if leftmost lane, only go left or straight...
+ else if( mLaneIndex == (mSegment->GetNumLanes()-1) )
+ {
+ coinflip = rand() % 2;
+ mDirection = (coinflip == 0)? STRAIGHT: LEFT;
+ }
+ // middle lanes only allowed to go straight...
+ else
+ {
+ mDirection = STRAIGHT;
+ }
+ return mDirection;
+}
+
+//=============================================================================
+// TrafficAI::SetSegmentIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index )
+//
+// Return: void
+//
+//=============================================================================
+void TrafficAI::SetSegmentIndex( unsigned int index )
+{
+ mSegmentIndex = index;
+
+ mSegment = mLane->GetRoad()->GetRoadSegment( mSegmentIndex );
+ rAssert( mSegment );
+
+ mLaneLength = mSegment->GetLaneLength( mLaneIndex );
+}
+
+//=============================================================================
+// TrafficAI::RegisterDebugInfo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficAI::RegisterDebugInfo()
+{
+}
+
+//=============================================================================
+// TrafficAI::UnregisterDebugInfo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficAI::UnregisterDebugInfo()
+{
+}
+
+
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+
+// returns the vertices of a triangle frustrum...
+void TrafficAI::GetFrustrum( const rmt::Vector& pos,
+ const rmt::Vector& dir,
+ rmt::Vector& leftFrustrumVertex,
+ rmt::Vector& rightFrustrumVertex )
+{
+ // our simple frustrum is a triangular PLANE that
+ // pans out from player's center of mass for a specified
+ // distance, whose base spans a width of 1 vehicle diameter
+
+ // TODO:
+ // We should really start the frustrum at vehicle's
+ // front, not vehicle's center. If our vehicle is a long
+ // truck, we could get in trouble (end up sensing no cars
+ // because the frustrum lies completely within the vehicle)
+ //
+ // For now, a triangular plane will do.
+ //
+ rmt::Vector end;
+ end = pos + dir * 15.0f;
+
+ rmt::Vector leftVec, rightVec;
+ leftVec.Set( -1 * dir.z, dir.y, dir.x );
+ rightVec.Set( dir.z, dir.y, -1 * dir.x );
+
+ // HACK:
+ // Hardcode the pan value (normally 1 car radius, but it seems to be larger
+ // than expected).
+ float pan = CAR_SPAN;
+
+ leftFrustrumVertex = end + leftVec * pan;
+ rightFrustrumVertex = end + rightVec * pan;
+}
+
+
+
+float TrafficAI::GetGoSpeedMps()
+{
+ // TODO:
+ // Vary speed according to angle of turn in intersection (sharper turns
+ // == slower speed)
+ static const float N_WAY_INTERSECTION_SPEED_MPS = 9.0f;
+ static const float NO_STOP_INTERSECTION_SPEED_MPS = 12.0f;
+ if( mIsInIntersection )
+ {
+ const Intersection* intersection = mLane->GetRoad()->GetSourceIntersection();
+ if( intersection->GetType() == Intersection::N_WAY )
+ {
+ return N_WAY_INTERSECTION_SPEED_MPS;
+ }
+ else if( intersection->GetType() == Intersection::NO_STOP )
+ {
+ return NO_STOP_INTERSECTION_SPEED_MPS;
+ }
+ }
+
+ // HACK:
+ // This is a temp hack to force speed limit, just trying it out
+ // If we want speed limit to be 60kph (they are currently defaulted to 50.0f
+ // in the data), then we'll have to change it in the world data.
+ //float speed = mLane->GetSpeedLimit();
+ float speed = TrafficManager::GetInstance()->GetDesiredTrafficSpeed();
+
+ return speed;
+}
+
+
+bool TrafficAI::AttemptLaneChange( ObstacleType foundSOMETHING, float distFromSOMETHINGSqr, void* SOMETHING )
+{
+ // some sanity checking
+ rAssert( mLane != NULL );
+ rAssert( GetVehicle()->mTrafficVehicle != NULL );
+ rAssert( GetVehicle()->mTrafficVehicle->GetIsActive() );
+ rAssert( mLane == GetVehicle()->mTrafficVehicle->GetLane() );
+
+ if( mState != TrafficAI::DRIVING )
+ {
+ return false;
+ }
+
+ if( mIsInIntersection )
+ {
+ //rDebugPrintf( "*** ABORTED LANECHANGE: Currently in an intersection ***\n" );
+ return false;
+ }
+
+ if( mSegment->GetRoad()->GetNumLanes() <= 1 )
+ {
+ return false;
+ }
+
+ // check if a car or a character is in front...
+ if( foundSOMETHING != OT_NONPLAYERVEHICLE &&
+ foundSOMETHING != OT_PLAYERVEHICLE &&
+ foundSOMETHING != OT_PLAYERCHARACTER &&
+ foundSOMETHING != OT_NONPLAYERCHARACTER &&
+ foundSOMETHING != OT_NOTHING )
+ {
+ //rDebugPrintf( "*** ABORTED LANECHANGE: Not a car or character in front of us ***\n" );
+ return false;
+ }
+
+ rmt::Vector myVel;
+ GetVehicle()->GetVelocity( &myVel );
+ if( rmt::Epsilon( myVel.MagnitudeSqr(), 0.0f, 0.0001f ) )
+ {
+ // if I have no speed, I shouldn't be lane-changing
+ return false;
+ }
+
+ /*
+ // TODO:
+ // This isn't working quite right. Set up a test case and test it more properly...
+ // check if he's moving faster than us (in the same direction as us)
+ // don't need to lane change if he is...
+
+ rmt::Vector velOfSOMETHING;
+ if( foundSOMETHING == OT_NONPLAYERVEHICLE ||
+ foundSOMETHING == OT_PLAYERVEHICLE ) // car
+ {
+ ((Vehicle*)SOMETHING)->GetVelocity(&velOfSOMETHING);
+ }
+ else if( foundSOMETHING == OT_PLAYERCHARACTER ||
+ foundSOMETHING == OT_NONPLAYERCHARACTER ) // character
+ {
+ ((Character*)SOMETHING)->GetVelocity(velOfSOMETHING);
+ }
+
+ if( !rmt::Epsilon( velOfSOMETHING.MagnitudeSqr(), 0.0f, 0.0001f ) )
+ {
+ rmt::Vector hisVel = GetProjectionVector( velOfSOMETHING, myVel );
+ rmt::Vector diff = hisVel - myVel;
+
+ // if dp is +ve, the difference vector points in my direction,
+ // therefore he is moving in the same direction as I am, but faster,
+ // so don't lane change...
+ float dp = diff.Dot( myVel );
+ if( dp >= 0.0f )
+ {
+ rDebugPrintf( "*** ABORTED LANECHANGE: Object in front is moving away at faster velocity ***\n" );
+ return false;
+ }
+ }
+ */
+
+ float laneChangeDist = MIN_LANE_CHANGE_DIST;
+ if( foundSOMETHING != OT_NOTHING )
+ {
+ //
+ // Don't determine lanechangedist based on the number of seconds.
+ // it's not accurate anyway.
+ // We want to grab the distance from the object, subtract the
+ // objects' spans (roughly 5 meters altogether), account for some
+ // slippage due to position and heading histories, and use this
+ // as the lanechangedist
+ //
+ float distFromSOMETHING = rmt::Sqrt(distFromSOMETHINGSqr);
+ const float APPROX_SPANS = 6.0f;
+ laneChangeDist = distFromSOMETHING - APPROX_SPANS;
+ }
+
+ // need at least this much room for lane change to look realistic
+ if( laneChangeDist < MIN_LANE_CHANGE_DIST )
+ {
+ //rDebugPrintf( "*** ABORTED LANECHANGE: Lanechange distance (%.2f m) at this speed is too small ***\n", laneChangeDist );
+ return false;
+ }
+
+ /*
+ // Check how close we are to the object...
+ // if it's closer than lane change dist, don't lane change
+ // since we won't clear the object
+ float minDistFromObj = laneChangeDist + 0.5f;
+ if( distFromSOMETHINGSqr < (minDistFromObj*minDistFromObj) )
+ {
+ float dist = rmt::Sqrt(distFromSOMETHINGSqr);
+ rDebugPrintf( "*** ABORTED LANECHANGE: Object (%f m) closer than the %f m required to lanechange ***\n",
+ dist, minDistFromObj);
+ return false;
+ }
+ */
+
+ float BUFFER = laneChangeDist;
+ float safeDistSqr = (laneChangeDist + BUFFER) * (laneChangeDist + BUFFER);
+
+ const Road* road = mSegment->GetRoad();
+ rAssert( road != NULL );
+
+ rmt::Vector myPos;
+ rAssert( GetVehicle() != NULL );
+ GetVehicle()->GetPosition(&myPos);
+
+ /* THIS CHECK IS MADE INSTEAD IN BUILDLANECHANGECURVE
+
+ // Check how close we are to the edge of the last segment
+ // If it's too close to the intersection, don't lane change
+ // since we won't have time...
+
+ rmt::Vector endPos, endFacing;
+
+ RoadSegment* seg = road->GetRoadSegment(
+ mSegment->GetRoad()->GetNumRoadSegments() - 1 );
+ rAssert( seg != NULL );
+ seg->GetLaneLocation( 1.0f, mLaneIndex, endPos, endFacing );
+
+ float distSqr = (endPos - myPos).MagnitudeSqr();
+ if( distSqr < safeDistSqr )
+ {
+ return false;
+ }
+ */
+
+ // Tally a list of possible lanes ...
+ unsigned int lanes[2];
+ int count = 0;
+
+ unsigned int laneInd;
+
+ int leftOrRight = rand() % 2;
+ if( leftOrRight == 0 ) // check left lane first
+ {
+ laneInd = mLaneIndex + 1;
+ if( laneInd < road->GetNumLanes() )
+ {
+ lanes[count] = laneInd;
+ count++;
+ }
+ if( mLaneIndex > 0 )
+ {
+ laneInd = mLaneIndex - 1;
+ if( laneInd >= 0 )
+ {
+ lanes[count] = laneInd;
+ count++;
+ }
+ }
+ }
+ else // check right lane first
+ {
+ if( mLaneIndex > 0 )
+ {
+ laneInd = mLaneIndex - 1;
+ if( laneInd >= 0 )
+ {
+ lanes[count] = laneInd;
+ count++;
+ }
+ }
+ laneInd = mLaneIndex + 1;
+ if( laneInd < road->GetNumLanes() )
+ {
+ lanes[count] = laneInd;
+ count++;
+ }
+ }
+
+ // no lane available, forget it...
+ if( count == 0 )
+ {
+ //rDebugPrintf( "*** ABORTED LANECHANGE: No lane available ***\n" );
+ return false;
+ }
+
+ // Now we have a list of Lanes...
+ // Check if prospective lane's current traffic is > n meters away from me
+ // Check if prospective lane has enough density to hold my car
+
+ Lane* targetLane = mLane;
+ unsigned int targetLaneIndex = mLaneIndex;
+
+ for( int i=0; i<count; i++ )
+ {
+ Lane* lane = road->GetLane( lanes[i] );
+ int numTraffic = lane->mTrafficVehicles.mUseSize;
+ if( numTraffic < (int)(lane->GetDensity()) )
+ {
+ bool clearToGo = true;
+
+ for( int j=0; j<numTraffic; j++ )
+ {
+ TrafficVehicle* tv = lane->mTrafficVehicles[j];
+
+ rmt::Vector vPos;
+ tv->GetVehicle()->GetPosition( &vPos );
+
+ float distSqr = (vPos - myPos).MagnitudeSqr();
+ if( distSqr < safeDistSqr )
+ {
+ clearToGo = false;
+ break;
+ }
+ }
+
+ if( clearToGo )
+ {
+ targetLane = lane;
+ targetLaneIndex = lanes[i];
+ }
+ }
+ }
+
+ // At this point, we have target lane determined
+ rAssert( targetLane != NULL );
+ rAssert( targetLaneIndex >= 0 );
+
+ if( mLane != targetLane )
+ {
+ bool succeeded = GetVehicle()->mTrafficLocomotion->BuildLaneChangeCurve(
+ mSegment, mT, mLaneIndex, targetLaneIndex, laneChangeDist );
+ if( !succeeded )
+ {
+ //rDebugPrintf( "*** ABORTED LANECHANGE: Couldn't build lane change curve (too few segments ahead) ***\n" );
+ return false;
+ }
+
+ // remove from old lane, add to new lane, update everything...
+ GetVehicle()->mTrafficLocomotion->UpdateLanes
+ ( GetVehicle()->mTrafficVehicle, mLane, targetLane );
+
+ // update state...
+ GetVehicle()->mTrafficLocomotion->mLaneChangeProgress = 0.0f;
+ GetVehicle()->mTrafficLocomotion->mLaneChangeDist = laneChangeDist;
+ GetVehicle()->mTrafficLocomotion->mLaneChangingFrom = mLaneIndex;
+ SetState( LANE_CHANGING );
+
+ mPrevLane = mLane;
+ mLane = targetLane;
+ mLaneIndex = targetLaneIndex;
+ mLaneLength = mSegment->GetLaneLength( mLaneIndex );
+
+ //rDebugPrintf( " >>>>>>>>>>>>>>>>> LANECHANGE! <<<<<<<<<<<<<<<<<<\n" );
+ mSecondsSinceLaneChange = 0.0f;
+ return true;
+ }
+ //rDebugPrintf( "*** ABORTED LANECHANGE: All adjacent lanes either is full (max density) or has a car too close by ***\n" );
+ return false;
+}
+
+void TrafficAI::SetAISpeed( float mps )
+{
+ // Keep new speed within bounds (ensure we don't ever reverse this way)
+ if( mps < 0.0f )
+ {
+ mps = 0.0f;
+ }
+ mAISpeed = mps;
+}
+
+float TrafficAI::GetAISpeed() const
+{
+ return mAISpeed;
+}
+
+void TrafficAI::MaintainSpeed( float seconds )
+{
+ float newSpeed = 0.0f;
+ float desiredSpeed = GetGoSpeedMps();
+ if( mAISpeed > desiredSpeed )
+ {
+ newSpeed = mAISpeed + DECEL_LO * seconds;
+ // NOTE: DOn't show brakelights for tiny adjustments in speed...
+ if( (mAISpeed - desiredSpeed) > 1.0f )
+ {
+ this->GetVehicle()->mGeometryVehicle->ShowBrakeLights();
+ }
+ }
+ else
+ {
+ newSpeed = mAISpeed + ACCEL * seconds;
+ this->GetVehicle()->mGeometryVehicle->HideBrakeLights();
+ }
+
+ SetAISpeed( newSpeed );
+ mNeedToSuddenlyStop = false;
+
+}
+
+
+void TrafficAI::StopForSomething( float seconds, ObstacleType ID, float distSqr, void* obj )
+{
+ // show brakelights when we're slowing down for something
+ this->GetVehicle()->mGeometryVehicle->ShowBrakeLights();
+
+ float speed = GetVehicle()->mTrafficLocomotion->mActualSpeed;
+
+ float EPSILON = 0.001f; // this is to prevent divide-by-zero in normal slowing
+ float BUFFER = CAR_LENGTH; // BUFFER is the minimal separation dist required to NOT interpenetrate
+ switch( ID ) // now account for the length of SOMETHING
+ {
+ case OT_NOTHING:
+ return;
+ case OT_PLAYERVEHICLE: // fall thru
+ case OT_NONPLAYERVEHICLE:
+ {
+ // another vehicle
+ BUFFER += CAR_LENGTH;
+ }
+ break;
+ case OT_PLAYERCHARACTER: // fall thru
+ case OT_NONPLAYERCHARACTER:
+ {
+ // a character
+ BUFFER += CHAR_LENGTH;
+ }
+ break;
+ case OT_ENDOFROAD:
+ // no need to modify "buffer"
+ break;
+ default:
+ rAssert( false );
+ }
+
+ ///////////////////////////
+ // Determine if we should be super-slowing...
+ // We do normal slowing when we're far away. If this isn't enough
+ // we super-slow when we're getting too close. If this is STILL not
+ // enough and if a non-submitting entity (for now, just traffic)
+ // is ahead of us, we suddenly stop so we don't collide with it.
+
+ // This is as close as we want to get to any car
+ float distDoSuddenStop = 1.0f; // this or less means we need to stop suddenly
+
+ /////////// Figure out distance needed to do super braking /////////////
+
+ float numFramesInHistory = TrafficLocomotion::ON_ROAD_HISTORY_SIZE;
+ float secondsHistoryDelay = numFramesInHistory * TrafficLocomotion::SECONDS_BETW_HISTORY_UPDATES;
+ float superSlowDriftDist = secondsHistoryDelay * speed;
+
+ // this is the distance the car will drift if we set speed to zero, thus
+ // populating all of POS_HISTORY one frame at a time (more or less) with the same pos.
+ float distDoSuperSlow = superSlowDriftDist + distDoSuddenStop;
+
+ /////////// Figure out distance we have to work with ////////////
+ float stopDist = rmt::Sqrt( distSqr ) - BUFFER; // *** SQUARE ROOT! ***
+
+ /////////// Figure out if we need to stop suddenly /////////////
+ /*
+ // NOTE: Disable need to suddenly stop. Traffic cars are submitting now
+ mNeedToSuddenlyStop = false;
+ if( stopDist <= distDoSuddenStop )
+ {
+ if( ID == OT_NONPLAYERVEHICLE )
+ {
+ Vehicle* npv = (Vehicle*) obj;
+ if( npv->GetLocomotionType() == VL_TRAFFIC &&
+ npv->mVehicleType == VT_TRAFFIC )
+ {
+ mNeedToSuddenlyStop = true;
+ mStopForSomethingDecel = -10000.0f;
+ SetAISpeed( 0.0f );
+ }
+ }
+ }
+ */
+
+ /////////// Figure out if we're doing super slow or normal slow /////////////
+ if( stopDist <= distDoSuperSlow )
+ {
+ mStopForSomethingDecel = -10000.0f;
+ SetAISpeed( 0.0f );
+ }
+ else
+ {
+ // here stopDist is greater than distDoSuperSlow,
+ // which hopefully means it is > zero
+ rAssert( stopDist > EPSILON );
+
+ mStopForSomethingDecel = -1*speed*speed / (2*stopDist);
+
+ /*
+ // HACK:
+ // The normal slow just ain't doing enough...
+ // Due to the fact that we use POS_HISTORY, the AISpeed we're setting will
+ // add only one new POS_HISTORY item amidst 20 items; then the average of
+ // these items is taken to determine new position. It's a very small
+ // contribution and skews our braking capabilities
+ //
+ const float FUDGEFUDGEFUDGE = 1.5f;
+ mStopForSomethingDecel *= FUDGEFUDGEFUDGE;
+ */
+
+ float newAISpeed = GetAISpeed() + mStopForSomethingDecel * seconds;
+ SetAISpeed( newAISpeed );
+ }
+
+}
+
+float TrafficAI::GetLookAheadDistance()
+{
+ return GetVehicle()->mTrafficLocomotion->mActualSpeed * SECONDS_LOOKAHEAD;
+}
+
+void TrafficAI::CheckForObstacles( ObstacleType& objID, float& distFromObjSqr, void*& obj, bool& objOnMyRight )
+{
+ float lookAhead = GetLookAheadDistance();
+ if( lookAhead < LOOKAHEAD_MIN )
+ {
+ lookAhead = LOOKAHEAD_MIN;
+ }
+
+ // Get information about this Traffic Vehicle
+ rmt::Vector playerPos;
+ Avatar* avatar = ::GetAvatarManager()->GetAvatarForPlayer( 0 );
+ avatar->GetPosition( playerPos );
+ Vehicle* playerVehicle = avatar->GetVehicle();
+
+ rmt::Vector pPos, pDir, pRightSide;
+ GetVehicle()->GetPosition( &pPos );
+
+ // *** WARNING ***
+ // GetHeading actually returns a normalized FACING value, meaning
+ // that a vehicle going in reverse will still have the same "heading"
+ // as the vehicle going forward.
+ //
+ // But as far as traffic's concerned, we never go in reverse, so
+ // this doesn't matter. Should we ever go into reverse, this will be
+ // a problem.
+ //
+ // we should use the velocity instead, but it's not accurate because
+ // we're not in physicslocomotion (it's probably stil 0, though we're moving)
+ //
+ if( !mIsInIntersection )
+ {
+ // TODO:
+ // Should one day change this so that it deals effectively with corners...
+ // right now the direction is just straight ahead of the car
+ // blegh..
+ GetVehicle()->GetHeading( &pDir );
+ }
+ // special lookahead dir for being in intersection, cuz we'll turn weird
+ else
+ {
+ rmt::Vector* ways;
+ int npts, currWay;
+ GetVehicle()->mTrafficLocomotion->GetSplineCurve( ways, npts, currWay );
+
+ if( currWay < (npts-1) ) // currway not the last point
+ {
+ const int NUM_WAYS_AHEAD_TO_LOOK = 20;
+ int lookAheadIndex = currWay + NUM_WAYS_AHEAD_TO_LOOK;
+ if( lookAheadIndex >= npts )
+ {
+ lookAheadIndex = npts-1;
+ }
+ pDir = ways[lookAheadIndex] - pPos;
+ pDir.NormalizeSafe();
+ }
+ else // if currway is last point, then just get heading normally
+ {
+ GetVehicle()->GetHeading( &pDir );
+ }
+ }
+ rAssert( rmt::Epsilon( pDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ mLookAheadPt = pPos + pDir * lookAhead;
+
+ // up x forward = right!
+ rmt::Vector pUp( 0.0f, 1.0f, 0.0f );
+ pRightSide.CrossProduct( pUp, pDir );//GetVehicle()->mVehicleTransverse;
+ pRightSide.NormalizeSafe();
+ rAssert( rmt::Epsilon( pRightSide.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ rmt::Sphere pSphere;
+ GetVehicle()->GetBoundingSphere( &pSphere );
+
+ float span = 0.0f;
+
+ // Get the vehicle/player character "q" that is:
+ // a) in front of us, AND
+ // b) closest to this vehicle "p"
+ // *** FORGET Item B because we have uniform deceleration
+ // just start slowing down if ANYONE's in front of us ***
+ // Must take into account all active vehicles:
+ // - other traffic vehicles,
+ // - player's vehicle,
+ // - other AI vehicles
+ // And all characters:
+ // - peds (actually, if they don't cross the street, we don't have to check)
+ // - player char
+ // - NPCs (actually, if they aren't placed in the street, don't have to check)
+
+ rmt::Vector qPos;
+
+ int nActiveVehicles = 0;
+ Vehicle** activeVehicles = NULL;
+ VehicleCentral* vc = ::GetVehicleCentral();
+ vc->GetActiveVehicleList( activeVehicles, nActiveVehicles );
+
+ // make sure there's at least 1 active vehicle because
+ // WE ARE SUPPOSED TO BE THAT ONE.
+ rAssert( nActiveVehicles >= 1 );
+
+ ObstacleType foundSOMETHING = OT_NOTHING;
+ float distFromSOMETHINGSqr = 100000.0f;
+ void* SOMETHING = NULL;
+ bool SOMETHINGOnMyRight = false;
+
+ float distSqr = 100000.0f;
+
+ Vehicle* aCar = NULL;
+ int aCount = 0;
+ int i;
+ for( i=0; i<vc->GetMaxActiveVehicles(); i++ )
+ {
+ if( aCount >= nActiveVehicles )
+ {
+ break;
+ }
+ aCar = activeVehicles[i];
+ if( aCar == NULL )
+ {
+ continue;
+ }
+
+ // Because ActiveList is a sparse list,
+ // we track how many we've seen till we've reached
+ // nActiveVehicles (to save some work at the end)
+ aCount++;
+
+ // if it's our own vehicle, forget it
+ if( aCar == GetVehicle() )
+ {
+ continue;
+ }
+
+ // Skip the vehicle if it's traffic and in different lane
+ if( aCar->GetLocomotionType() == VL_TRAFFIC )
+ {
+ Lane* hisLane = NULL;
+ Lane* myLane = mLane;
+
+ // Remember.. we care if he's ahead of us
+ if( mIsInIntersection )
+ {
+ // I'm in an intersection, so if he's ahead of us:
+ // if he's in intersection with us, he'll have same mLane (OUT lane)
+ // if he's not in intersection, he'll already be in mLane
+ hisLane = aCar->mTrafficLocomotion->GetAILane();
+ }
+ else
+ {
+ // I'm not in an intersection, so if he's ahead of us:
+ // if he's in an intersection, get his previous lane, as it could be our lane
+ // if he's NOT in an intersection, make sure he's in the same lane as we are.
+ if( aCar->mTrafficLocomotion->IsInIntersection() )
+ {
+ hisLane = aCar->mTrafficLocomotion->GetAIPrevLane();
+ }
+ else
+ {
+ hisLane = aCar->mTrafficLocomotion->GetAILane();
+ }
+ }
+ if( hisLane != myLane )
+ {
+ continue;
+ }
+ }
+
+ //
+ // Get info about the other car
+ //
+ aCar->GetPosition( &qPos );
+
+ span = CAR_SPAN + CAR_SPAN;
+ bool qPosLiesOnMyRight;
+ if( WillCollide( pPos, pDir, pRightSide, span, lookAhead, qPos, qPosLiesOnMyRight ) )
+ {
+ distSqr = (pPos - qPos).MagnitudeSqr();
+ if( distSqr < distFromSOMETHINGSqr )
+ {
+ if( aCar == playerVehicle )
+ {
+ foundSOMETHING = OT_PLAYERVEHICLE;
+ }
+ else
+ {
+ foundSOMETHING = OT_NONPLAYERVEHICLE;
+ }
+ distFromSOMETHINGSqr = distSqr;
+ SOMETHING = aCar;
+ SOMETHINGOnMyRight = qPosLiesOnMyRight;
+ }
+ }
+ }
+
+
+ // Not so fast! Must check to see if we'll be running over the player character!
+ span = CAR_SPAN + CHAR_SPAN;
+ bool charPosLiesOnMyRight;
+ if( !avatar->IsInCar() && WillCollide( pPos, pDir, pRightSide, span, lookAhead, playerPos, charPosLiesOnMyRight ) )
+ {
+ distSqr = (pPos - playerPos).MagnitudeSqr();
+ if( distSqr < distFromSOMETHINGSqr )
+ {
+ foundSOMETHING = OT_PLAYERCHARACTER;
+ distFromSOMETHINGSqr = distSqr;
+ SOMETHING = avatar->GetCharacter();
+ SOMETHINGOnMyRight = charPosLiesOnMyRight;
+ }
+ }
+
+ // Not so fast! Check if we're going to collide with any other characters
+ // we're supposed to avoid!
+ int charCount = 0;
+ int numChars = TrafficManager::GetInstance()->GetNumCharsToStopFor();
+ for( int i=0; (i<TrafficManager::MAX_CHARS_TO_STOP_FOR) && (charCount<numChars); i++ )
+ {
+ Character* charToStopFor = TrafficManager::GetInstance()->GetCharacterToStopFor(i);
+ if( charToStopFor != NULL )
+ {
+ rmt::Vector charPos;
+ charToStopFor->GetPosition( charPos );
+
+ charCount++;
+ if( WillCollide( pPos, pDir, pRightSide, span, lookAhead, charPos, charPosLiesOnMyRight ) )
+ {
+ distSqr = (pPos - charPos).MagnitudeSqr();
+ if( distSqr < distFromSOMETHINGSqr )
+ {
+ foundSOMETHING = OT_NONPLAYERCHARACTER;
+ distFromSOMETHINGSqr = distSqr;
+ SOMETHING = charToStopFor;
+ SOMETHINGOnMyRight = charPosLiesOnMyRight;
+ }
+ }
+ }
+ }
+
+ // Not so fast! If we are not already in an intersection, must check to see
+ // if we need to stop at this intersection
+ if( !mIsInIntersection && mState != LANE_CHANGING )
+ {
+ // If the destination intersection is of type N_WAY, we check
+ // to see if it's inside our frustrum...
+ //
+ const Intersection* intersection = mLane->GetRoad()->GetDestinationIntersection();
+ if( intersection->GetType() == Intersection::N_WAY &&
+ intersection != mPrevIntersection )
+ {
+ //
+ // if we haven't been driving long enough after we just got free from
+ // an intersection, don't transit to WAITING_AT_INTERSECTION
+ // (or we'll never go anywhere).
+ //
+ float lull = (float) NWayStop::NWAY_TURN_MILLISECONDS / 1000.0f;
+ if( (mPrevState != WAITING_AT_INTERSECTION) ||
+ (mSecondsDriving > lull && mPrevState == WAITING_AT_INTERSECTION) )
+ {
+ //
+ // Gotta sense if we are nearing an intersection by seeing if the
+ // lane location at t = 1.0f of the last segment on this
+ // road is inside our frustrum.
+
+ const Road* myRoad = mLane->GetRoad();
+ rAssert( myRoad != NULL );
+ unsigned int nSegments = myRoad->GetNumRoadSegments();
+
+ rmt::Vector endOfRoadPos, dummy;
+ myRoad->GetRoadSegment( nSegments - 1 )->GetLaneLocation
+ ( 1.0f, mLaneIndex, endOfRoadPos, dummy );
+
+ span = CAR_SPAN;
+ bool endOfRoadPosLiesOnMyRight;
+ if( WillCollide( pPos, pDir, pRightSide, span, lookAhead, endOfRoadPos, endOfRoadPosLiesOnMyRight ) )
+ {
+ float distToIntersectionSqr = (endOfRoadPos - pPos).MagnitudeSqr();
+ if( distToIntersectionSqr < distFromSOMETHINGSqr )
+ {
+ foundSOMETHING = OT_ENDOFROAD;
+ distFromSOMETHINGSqr = distToIntersectionSqr;
+ SOMETHING = (Intersection*)intersection;
+ SOMETHINGOnMyRight = endOfRoadPosLiesOnMyRight;
+
+ mEndOfRoadPos = endOfRoadPos;
+
+ }
+ }
+ }
+ }
+ }
+
+ objID = foundSOMETHING;
+ distFromObjSqr = distFromSOMETHINGSqr;
+ obj = SOMETHING;
+ objOnMyRight = SOMETHINGOnMyRight;
+
+}
+
diff --git a/game/code/ai/vehicle/trafficai.h b/game/code/ai/vehicle/trafficai.h
new file mode 100644
index 0000000..4a6242e
--- /dev/null
+++ b/game/code/ai/vehicle/trafficai.h
@@ -0,0 +1,264 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trafficai.h
+//
+// Description: Blahblahblah
+//
+// History: 09/09/2002 + Accel/Decel behavior -- Dusit Eakkachaichanvet
+// 06/08/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef TRAFFICAI_H
+#define TRAFFICAI_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <roads/intersection.h>
+#include <roads/lane.h>
+#include <roads/road.h>
+#include <worldsim/redbrick/vehiclecontroller/aivehiclecontroller.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+
+class TrafficAI : public AiVehicleController
+{
+public: // METHODS
+
+ static const float SECONDS_LOOKAHEAD;
+ static const float LOOKAHEAD_MIN;
+
+ //What is the traffic AI up to?
+ enum State
+ {
+ DEAD,
+ DRIVING,
+ WAITING_AT_INTERSECTION,
+ WAITING_FOR_FREE_LANE,
+ LANE_CHANGING,
+ SPLINING,
+ SWERVING,
+
+ NUM_STATES
+ };
+
+ //Which way does he want to turn?
+ enum Direction
+ {
+ LEFT,
+ RIGHT,
+ STRAIGHT,
+
+ NUM_DIRECTIONS
+ };
+
+ //static const float LANE_CHANGE_DIST;
+
+ TrafficAI( Vehicle* vehicle );
+ virtual ~TrafficAI();
+
+ void Init();
+
+ void Init( Vehicle* vehicle,
+ Lane* lane,
+ unsigned int laneIndex,
+ RoadSegment* segment,
+ unsigned int segmentIndex,
+ float t,
+ float mps );
+
+ void Update( float seconds );
+
+ State GetState() const;
+ void SetState(State state);
+
+ void SetLane( Lane* lane );
+ Lane* GetLane();
+
+ void SetLaneIndex( unsigned int index );
+ unsigned int GetLaneIndex() const;
+
+ float GetLaneLength() const;
+
+ void SetSegment( RoadSegment* segment );
+ RoadSegment* GetSegment() const;
+
+ void SetSegmentIndex( unsigned int index );
+ unsigned int GetSegmentIndex() const;
+
+ void SetLanePosition( float t );
+ float GetLanePosition();
+
+ void SetAISpeed( float mps );
+ float GetAISpeed() const;
+
+ Direction DecideTurn();
+
+ void RegisterDebugInfo();
+ void UnregisterDebugInfo();
+ void RegisterAI();
+ void UnregisterAI();
+
+ void StartSwerving( bool swerveRight );
+
+ enum ObstacleType
+ {
+ OT_NOTHING = 0,
+ OT_NONPLAYERVEHICLE = 1,
+ OT_NONPLAYERCHARACTER = 2,
+ OT_PLAYERCHARACTER = 3,
+ OT_PLAYERVEHICLE = 4,
+ OT_ENDOFROAD = 99
+ };
+ void CheckForObstacles( ObstacleType& objID, float& distFromObjSqr, void*& obj, bool& objOnMyRight );
+
+
+public: // MEMBERS
+
+ // Breaking architecture a bit. TrafficLocomotion is the one that
+ // needs to detect when we're inside/outside an intersection. So we allow it
+ // to set a TrafficAI flag in this instance. But how to ensure that only
+ // TrafficLocomotion is allowed to call this function? Hence a small
+ // breakage.
+ void SetIsInIntersection( bool value );
+ Lane* mPrevLane; // for when we're in an intersection, it's the IN lane
+ bool mIsActive : 1;
+ bool mNeedToSuddenlyStop : 1;
+ rmt::Vector mLookAheadPt;
+
+
+private: // MEMBERS
+
+ State mState;
+ State mPrevState;
+ Direction mDirection;
+
+ Lane* mLane; // curr lane when not in intersection, OUT lane when in intersection
+ unsigned int mLaneIndex;
+ float mLaneLength;
+
+ RoadSegment* mSegment;
+ unsigned int mSegmentIndex;
+
+ float mT; //t
+ float mAISpeed; //mps
+
+ float mSecondsDriving;
+ const Intersection* mPrevIntersection;
+ float mStopForSomethingDecel;
+
+ int mRenderHandle;
+
+ float mSecondsSinceLastTriggeredImpedence;
+ void* mLastThingThatImpededMe;
+
+ float mSecondsSinceLaneChange;
+
+ float mSecondsSwerving;
+ float mOriginalMaxWheelTurnAngle;
+ bool mIsInIntersection : 1;
+ bool mSwervingLeft : 1;
+ bool mSwerveHighBeamOn : 1;
+ float mOriginalHeadlightScale;
+ float mSecondsSwerveHighBeam;
+
+ // used for waiting at intersection state
+ rmt::Vector mEndOfRoadPos;
+
+private: // METHODS
+
+ // returns triangular vertices of frustrum
+ void GetFrustrum( const rmt::Vector& pos,
+ const rmt::Vector& dir,
+ rmt::Vector& leftFrustrumVertex,
+ rmt::Vector& rightFrustrumVertex );
+
+ float GetGoSpeedMps();
+
+
+ bool AttemptLaneChange( ObstacleType foundSOMETHING, float distFromSOMETHINGSqr, void* SOMETHING );
+ void MaintainSpeed( float seconds );
+ void StopForSomething( float seconds, ObstacleType ID, float distSqr, void* obj );
+
+ void PerhapsTriggerImpedence( ObstacleType foundSOMETHING, float distSqr, void* SOMETHING );
+
+ void StopSwerving();
+ void Swerve();
+
+ float GetLookAheadDistance();
+
+ //Prevent wasteful constructor creation.
+ TrafficAI( const TrafficAI& trafficai );
+ TrafficAI& operator=( const TrafficAI& trafficai );
+};
+
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+inline void TrafficAI::SetIsInIntersection( bool value )
+{
+ mIsInIntersection = value;
+}
+inline TrafficAI::State TrafficAI::GetState() const
+{
+ return mState;
+}
+
+inline void TrafficAI::SetLane( Lane* lane )
+{
+ mPrevLane = mLane;
+ mLane = lane;
+}
+inline Lane* TrafficAI::GetLane()
+{
+ return mLane;
+}
+inline float TrafficAI::GetLaneLength() const
+{
+ return mLaneLength;
+}
+inline void TrafficAI::SetLaneIndex( unsigned int index )
+{
+ mLaneIndex = index;
+}
+inline unsigned int TrafficAI::GetLaneIndex() const
+{
+ return mLaneIndex;
+}
+inline void TrafficAI::SetSegment( RoadSegment* segment )
+{
+ mSegment = segment;
+}
+inline RoadSegment* TrafficAI::GetSegment() const
+{
+ return mSegment;
+}
+inline unsigned int TrafficAI::GetSegmentIndex() const
+{
+ return mSegmentIndex;
+}
+inline void TrafficAI::SetLanePosition( float t )
+{
+ mT = t;
+}
+inline float TrafficAI::GetLanePosition()
+{
+ return mT;
+}
+inline void TrafficAI::SetState(State state)
+{
+ mPrevState = mState;
+ mState = state;
+ //rDebugPrintf( "TrafficAI: Statechange: %d -> %d\n", mPrevState, mState );
+}
+
+#endif //TRAFFICAI_H
diff --git a/game/code/ai/vehicle/vehicleai.cpp b/game/code/ai/vehicle/vehicleai.cpp
new file mode 100644
index 0000000..b4723b6
--- /dev/null
+++ b/game/code/ai/vehicle/vehicleai.cpp
@@ -0,0 +1,3123 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehicleai.cpp
+//
+// Description: Implement VehicleAI
+//
+// History: 27/06/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radtime.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <ai/vehicle/vehicleai.h>
+#include <ai/vehicle/vehicleairender.h>
+#include <memory/classsizetracker.h>
+#include <meta/locator.h>
+
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#include <roads/intersection.h>
+#include <roads/road.h>
+#include <roads/roadmanager.h>
+#include <roads/roadsegment.h>
+#include <roads/geometry.h>
+
+#include <simcollision/collisionmanager.hpp>
+//#include <simcollision/collisionvolume.hpp>
+
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/worldphysicsmanager.h>
+
+#include <mission/missionmanager.h>
+#include <mission/mission.h>
+#include <mission/objectives/missionobjective.h>
+#include <mission/objectives/destroyobjective.h>
+#include <mission/objectives/collectdumpedobjective.h>
+
+#include <debug/profiler.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+const bool EVADE_VEHICLES = true;
+const bool EVADE_STATICS = false;
+
+// how long going forward at minspeed do we allow before reversing
+const unsigned int FIRST_STUCK_TIME = 700;//500;
+
+// how long after reversing, going forward at speed <= minspeed,
+// do we allow before reversing again...
+const unsigned int NEXT_STUCK_TIME = 1500;//2500;
+
+// number of milliseconds we spend applying reverse
+const float REVERSE_BACKOFF = 200.0f;//1000;
+const float DEFAULT_REVERSE_TIME = 500.0f - REVERSE_BACKOFF;//1000;
+const float MAX_REVERSE_TIME = 1600.0f; // after this, just reset..
+const float STILL_STUCK_SPEED = 14.0f;
+
+//
+// Distances the AI looks ahead for steering
+const float LOOK_AHEAD_CLOSE_SECONDS = 0.8f;//0.7f;
+const float LOOK_AHEAD_FAR_SECONDS = 1.0f;//1.0f;
+const float MIN_LOOK_AHEAD_CLOSE = 5.0f;//2.0f;
+const float MAX_LOOK_AHEAD_CLOSE = 1000.0f;//20.0f;
+const float MIN_LOOK_AHEAD_FAR = 10.0f;//5.0f;
+const float MAX_LOOK_AHEAD_FAR = 1000.0f;//40.0f;
+
+//
+// A low number favours the far look-ahead, a high number
+// favours the close look-ahead
+const float DEFAULT_STEERING_RATIO = 1.0f;//0.5f;
+const float AVOID_STEERING_RATIO = 1.0f;//0.55f;
+
+// A low number makes the gas more dependent on the steering,
+// a high number makes it less.
+const float GAS_STEERING_RATIO = 0.50f;
+
+// A low number favours the turn of the wheels, a high number favours
+// the heading-to-destination
+const float STEER_HEADING_RATIO = 0.50f;
+
+// Default shortcut skills
+const int VehicleAI::DEFAULT_MIN_SHORTCUT_SKILL = 15;
+const int VehicleAI::DEFAULT_MAX_SHORTCUT_SKILL = 25;
+
+// Shortcut & Speed, Catch-up values
+
+//const float VehicleAI::CATCHUP_MAX_SPEED_MOD = 20.0f;
+const float VehicleAI::CATCHUP_NORMAL_DRIVING_PERCENTAGE_OF_TOPSPEED = 0.7f;
+const int VehicleAI::CATCHUP_MAX_SHORTCUTSKILL_MOD = 20;
+
+// number of seconds to start counting down before we repopulate
+// giving us a chance to get back on path
+const float SECONDS_REPOPULATE_DELAY = 3.0f;
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// VehicleAI::VehicleAI
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+VehicleAI::VehicleAI(
+ Vehicle* pVehicle,
+ VehicleAITypeEnum type,
+ bool enableSegmentOptimization,
+ int minShortcutSkill,
+ int maxShortcutSkill,
+ bool useMultiplier ) :
+
+AiVehicleController( pVehicle ),
+mNumSegments( 0 ),
+mSecondsStunned( 0.0f ),
+mSecondsOutOfControl( 0.0f ),
+
+mCurrPathElement( -1 ),
+
+mLastRoadSegment( NULL ),
+mLastRoadSegmentT( 0.0f ),
+mLastRoadT( 0.0f ),
+
+mRaceRoadSegment( NULL ),
+mRaceRoadSegmentT( 0.0f ),
+mRaceRoadT( 0.0f ),
+
+mSecondsSinceLastDoCatchUp( 0.0f ),
+
+/*** DEPRECATED: TO BE REMOVED ***
+mDestIndex( -1 ),
+mpRoad( NULL ),
+mpSegment( NULL ),
+*********************************/
+
+mStartStuckTime( 0 ),
+mNextStuckTime( FIRST_STUCK_TIME ),
+mSteeringRatio( DEFAULT_STEERING_RATIO ),
+mState( STATE_WAITING ),
+mLimboPushedState( STATE_WAITING ),
+mType( type ),
+mHudIndex( -1 ),
+mSecondsBeforeCorner( 0.0f ),
+mRenderHandle( -1 ),
+mSecondsLeftToGetBackOnPath( SECONDS_REPOPULATE_DELAY ),
+mReverseTime(DEFAULT_REVERSE_TIME),
+mEvadeVehicles( EVADE_VEHICLES ),
+mEvadeStatics( EVADE_STATICS ),
+mEvading( false ),
+mEnableSegmentOptimization( enableSegmentOptimization ),
+mUseMultiplier( useMultiplier )
+{
+ CLASSTRACKER_CREATE( VehicleAI );
+ rAssert( minShortcutSkill >= 0 );
+ rAssert( maxShortcutSkill >= 0 );
+ rAssert( minShortcutSkill <= maxShortcutSkill );
+ if( minShortcutSkill > maxShortcutSkill )
+ {
+ mMinShortcutSkill = maxShortcutSkill;
+ mMaxShortcutSkill = minShortcutSkill;
+ }
+ else
+ {
+ mMinShortcutSkill = minShortcutSkill;
+ mMaxShortcutSkill = maxShortcutSkill;
+ }
+
+
+ mDestination.Set( 0.0f, 0.0f, 0.0f );
+ mNextDestination.Set( 0.0f, 0.0f, 0.0f );
+
+ int maxPathElems = RoadManager::GetInstance()->GetMaxPathElements();
+ mPathElements.Allocate( maxPathElems );
+
+ mLastPathElement.elem = NULL;
+ mRacePathElement.elem = NULL;
+
+ ////////////////////////////////////////
+ // Initialize catchup params with defaults, ONCE on construction
+
+ mCatchupParams.Race.DistMaxCatchup = 80.0f;
+ mCatchupParams.Race.FractionPlayerSpeedMinCatchup = 0.5f;
+ mCatchupParams.Race.FractionPlayerSpeedMidCatchup = 1.1f;
+ mCatchupParams.Race.FractionPlayerSpeedMaxCatchup = 1.7f;
+
+ mCatchupParams.Evade.DistPlayerTooNear = 20.0f;
+ mCatchupParams.Evade.DistPlayerFarEnough = 70.0f;
+
+ mCatchupParams.Target.DistPlayerNearEnough = 20.0f;
+ mCatchupParams.Target.DistPlayerTooFar = 70.0f;
+
+
+ //////////////////////////////////////////
+ // Reset temp catchup params
+ ResetCatchUpParams();
+}
+
+//==============================================================================
+// VehicleAI::~VehicleAI
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+VehicleAI::~VehicleAI()
+{
+ CLASSTRACKER_DESTROY( VehicleAI );
+ mPathElements.Clear();
+ Finalize();
+}
+
+
+void VehicleAI::SetRaceCatchupParams( const RaceCatchupParams& raceParams )
+{
+ rTuneAssertMsg( raceParams.DistMaxCatchup > 0.0f,
+ "Race Catchup: Dist Max Catchup must be > zero" );
+
+ mCatchupParams.Race = raceParams;
+}
+void VehicleAI::SetEvadeCatchupParams( const EvadeCatchupParams& evadeParams )
+{
+ rTuneAssertMsg( evadeParams.DistPlayerFarEnough > 0.0f,
+ "Evade Cathcup: DistPlayerFarEnough must be > zero" );
+ rTuneAssertMsg( evadeParams.DistPlayerTooNear > 0.0f,
+ "Evade Catchup: DistPlayerTooNear must be > zero" );
+ rTuneAssertMsg( evadeParams.DistPlayerFarEnough > evadeParams.DistPlayerTooNear,
+ "Evade Catchup: DistPlayerFarEnough must be > DistPlayerTooNear" );
+
+ mCatchupParams.Evade = evadeParams;
+}
+void VehicleAI::SetTargetCatchupParams( const TargetCatchupParams& targetParams )
+{
+ rTuneAssertMsg( targetParams.DistPlayerTooFar > 0.0f,
+ "Evade Cathcup: DistPlayerTooFar must be > zero" );
+ rTuneAssertMsg( targetParams.DistPlayerNearEnough > 0.0f,
+ "Evade Catchup: DistPlayerNearEnough must be > zero" );
+ rTuneAssertMsg( targetParams.DistPlayerTooFar > targetParams.DistPlayerNearEnough,
+ "Evade Catchup: DistPlayerTooFar must be > DistPlayerNearEnough" );
+
+ mCatchupParams.Target = targetParams;
+}
+
+
+
+void VehicleAI::ResetControllerValues()
+{
+ // reset the controller values we use.
+ mGas.SetValue( 0.0f );
+ mBrake.SetValue( 0.0f );
+ mHandBrake.SetValue( 0.0f );
+ mSteering.SetValue( 0.0f );
+ mReverse.SetValue( 0.0f );
+ mHorn.SetValue( 0.0f );
+}
+
+void VehicleAI::Reset()
+{
+ mSecondsStunned = 0.0f;
+ mSecondsOutOfControl = 0.0f;
+
+ mNumSegments = 0;
+ mPathElements.ClearUse();
+
+ mCurrPathElement = -1;
+
+ mLastPathElement.elem = NULL;
+ mLastRoadSegment = NULL;
+ mLastRoadSegmentT = 0.0f;
+ mLastRoadT = 0.0f;
+
+ mRacePathElement.elem = NULL;
+ mRaceRoadSegment = NULL;
+ mRaceRoadSegmentT = 0.0f;
+ mRaceRoadT = 0.0f;
+
+
+ // updates mLastPathElement, mLastRoadSegment, mLastRoadSegmentT, mLastRoadT
+ UpdateSelf();
+ //
+ // Place a check here to see which vehicle wasn't started properly on a
+ // locator. This also enforces that whoever initializes this AI has
+ // already placed down the vehicle at its proper location before calling
+ // Initialize()
+ //
+ rTuneAssertMsg( (mLastPathElement.elem != NULL && mLastRoadSegment != NULL),
+ "Initialize() called on an AI car that was not on a road segment!\n" );
+
+ mSecondsSinceLastDoCatchUp = 0.0f;
+
+ mStartStuckTime = 0;
+ mNextStuckTime = FIRST_STUCK_TIME;
+ mSteeringRatio = DEFAULT_STEERING_RATIO;
+
+ mSecondsBeforeCorner = 0.0f;
+ mSecondsLeftToGetBackOnPath = SECONDS_REPOPULATE_DELAY;
+ mEvading = false;
+
+ /////////////////////////////////////////////////////
+ mDestination.Set( 0.0f, 0.0f, 0.0f );
+ mNextDestination.Set( 0.0f, 0.0f, 0.0f );
+ ResetCatchUpParams();
+
+ ResetControllerValues();
+}
+
+// Before calling Initialize, the vehicle's location should be
+// properly set on a road segment!
+void VehicleAI::Initialize()
+{
+ Reset();
+
+ mState = STATE_WAITING;
+ mLimboPushedState = STATE_WAITING;
+
+#ifdef DEBUGWATCH
+ mRenderHandle = VehicleAIRender::GetVehicleAIRender()->RegisterAI( this );
+ rAssert( mRenderHandle != -1 );
+#else
+ mRenderHandle = -1;
+#endif
+}
+
+
+void VehicleAI::Finalize()
+{
+ SetActive( false );
+#ifdef DEBUGWATCH
+ if( mRenderHandle >= 0 )
+ {
+ VehicleAIRender::GetVehicleAIRender()->UnregisterAI( mRenderHandle );
+ }
+#endif
+}
+
+//=============================================================================
+// VehicleAI::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float timeins )
+//
+// Return: void
+//
+//=============================================================================
+void VehicleAI::Update( float timeins )
+{
+BEGIN_PROFILE( "VehicleAI Update" );
+ if( GetState() == STATE_LIMBO )
+ {
+ return;
+ }
+ UpdateSelf();
+ UpdateSegments();
+ FollowRoad();
+ DoSteering();
+ DoCatchUp( timeins );
+ CheckState( timeins );
+END_PROFILE( "VehicleAI Update" );
+}
+
+void VehicleAI::SetActive( bool bIsActive )
+{
+ if( bIsActive )
+ {
+ mState = STATE_ACCEL;
+ //mpRoad = NULL;
+
+ if( mHudIndex == -1 )
+ {
+/*
+ rmt::Vector initialLoc;
+ GetVehicle()->GetPosition( &initialLoc );
+
+ CGuiScreenMultiHud* currentHud = GetCurrentHud();
+ if( currentHud )
+ {
+ HudMapIcon::eIconType iconType = mType == AI_CHASE ? HudMapIcon::ICON_AI_HARASS_CAR : HudMapIcon::ICON_AI_CAR;
+ mHudIndex = currentHud->GetHudMap( 0 )->RegisterIcon( iconType, initialLoc, this );
+ }
+*/
+ mHudIndex = this->RegisterHudMapIcon();
+ }
+ }
+ else
+ {
+ if( mHudIndex >= 0 )
+ {
+ CGuiScreenMultiHud* currentHud = GetCurrentHud();
+ if( currentHud )
+ {
+ currentHud->GetHudMap( 0 )->UnregisterIcon( mHudIndex );
+ }
+ mHudIndex = -1;
+ }
+
+ SetState( STATE_WAITING );
+ }
+}
+
+void VehicleAI::GetPosition( rmt::Vector* currentLoc )
+{
+ GetVehicle()->GetPosition( currentLoc );
+}
+
+void VehicleAI::GetHeading( rmt::Vector* heading )
+{
+ GetVehicle()->GetHeading( heading );
+}
+
+void VehicleAI::SetMaxShortcutSkill( int skill )
+{
+ mMaxShortcutSkill = skill;
+}
+int VehicleAI::GetMaxShortcutSkill()
+{
+ return mMaxShortcutSkill;
+}
+void VehicleAI::SetMinShortcutSkill( int skill )
+{
+ mMinShortcutSkill = skill;
+}
+int VehicleAI::GetMinShortcutSkill()
+{
+ return mMinShortcutSkill;
+}
+int VehicleAI::GetShortcutSkillMod()
+{
+ return mShortcutSkillMod;
+}
+void VehicleAI::EnterLimbo()
+{
+ if( mState != STATE_LIMBO )
+ {
+ //makes the AI goto a limbo state
+ mLimboPushedState = mState;
+ SetState(STATE_LIMBO);
+
+ // clear all inputs
+ ResetControllerValues();
+
+ // fudge handbrake, so we don't drift..
+ mHandBrake.SetValue( 1.0f );
+ }
+}
+void VehicleAI::ExitLimbo()
+{
+ if( mState == STATE_LIMBO )
+ {
+ //get out of limbo and enter waiting state
+ SetState(mLimboPushedState);
+ }
+}
+
+
+int VehicleAI::DetermineShortcutSkill()
+{
+ if( mMaxShortcutSkill < mMinShortcutSkill )
+ {
+ int temp = mMaxShortcutSkill;
+ mMaxShortcutSkill = mMinShortcutSkill;
+ mMinShortcutSkill = temp;
+ }
+
+ // roll a number between min skill and max skill
+ int diff = mMaxShortcutSkill - mMinShortcutSkill;
+ int roll = (rand() % diff) + mMinShortcutSkill;
+
+ return (roll + mShortcutSkillMod);
+}
+
+void VehicleAI::ResetCatchUpParams()
+{
+ rAssert( GetVehicle() );
+ mDesiredSpeedKmh = GetVehicle()->mDesignerParams.mDpTopSpeedKmh *
+ CATCHUP_NORMAL_DRIVING_PERCENTAGE_OF_TOPSPEED;
+ mShortcutSkillMod = 0;
+}
+
+void VehicleAI::FillPathElements()
+{
+ rAssert( mPathElements.IsSetUp() );
+
+ ////////////////////////////////////////////////////////////////////////
+ // clear the list to be refilled
+ mPathElements.ClearUse();
+
+ if( mLastPathElement.elem == NULL )
+ {
+ // TODO:
+ // This VehicleAI was not created on a road segment or in an intersection.
+ // This case happens when a new AI is put in to replace some old AI of some
+ // mission vehicle, at the time when the old AI was not on a road segment
+ // or in an intersection.
+ // Since pathfinding requires it's on some sort of path element, this car
+ // is going nowhere fast.
+ // More eloquent solution pending. Perhaps use IntersectionManager::FindClosestRoad?
+ return;
+ }
+
+ rmt::Vector sourcePos;
+ GetPosition( &sourcePos );
+
+ /////////////////////////////////////////////////////////////////////
+ // find our target element
+ rmt::Vector targetPos;
+ RoadManager::PathElement targetElem;
+ RoadSegment* targetSeg = NULL;
+ float targetSegT = 0.0f;
+ float targetRoadT = 0.0f;
+
+ GetClosestPathElementToTarget(
+ targetPos, targetElem, targetSeg, targetSegT, targetRoadT );
+
+ if( targetElem.elem == NULL ) // target element can be NULL
+ {
+ return;
+ }
+
+ //////////////////////////////////////////////////////////////////////
+ // invoke the pathfinder
+ RoadManager::GetInstance()->FindPathElementsBetween(
+ mUseMultiplier,
+ mLastPathElement, mLastRoadT, sourcePos,
+ targetElem, targetRoadT, targetPos,
+ mPathElements );
+}
+
+void VehicleAI::UpdateSelf()
+{
+ rmt::Vector myPos;
+ GetVehicle()->GetPosition( &myPos );
+
+ // find our source element
+ RoadManager::PathElement elem;
+ RoadSegment* seg = NULL;
+ float segT = 0.0f;
+ float roadT = 0.0f;
+
+ /*
+ RoadManager::PathfindingOptions options = RoadManager::PO_SEARCH_SHORTCUTS;
+ RoadManager::GetInstance()->FindClosestPathElement(
+ myPos, 100.0f, options, elem, seg, segT, roadT );
+ */
+
+ // Special initial case:
+ // If my last path info is completely unset (started off the road)
+ // then we fetch the closest path element using Devin's thing
+ if( mLastPathElement.elem == NULL && mLastRoadSegment == NULL )
+ {
+ RoadSegment* closestSeg = NULL;
+ float dummy;
+ GetIntersectManager()->FindClosestAnyRoad( myPos, 100.0f, closestSeg, dummy );
+
+ seg = (RoadSegment*) closestSeg;
+ segT = RoadManager::DetermineSegmentT( myPos, seg );
+ roadT = RoadManager::DetermineRoadT( seg, segT );
+ elem.elem = seg->GetRoad();
+ elem.type = RoadManager::ET_NORMALROAD;
+ }
+ else
+ {
+ FindClosestPathElement( myPos, elem, seg, segT, roadT, true );
+ }
+
+ if( elem.elem == NULL )
+ {
+ return;
+ }
+
+ rAssert( elem.elem != NULL );
+
+ // update race stuff..
+ mRacePathElement = elem;
+ if( seg )
+ {
+ mRaceRoadSegment = seg;
+ mRaceRoadSegmentT = segT;
+ mRaceRoadT = roadT;
+ }
+
+
+ /////////////////////////////////////////////////////////////
+ // THE RULES (assuming last element and curr element are different)
+ // -----------------------
+ // | LAST =======> CURRENT |
+ // -----------------------
+ //
+ // OK means we don't ignore this new pathelement
+ // Not OK means we ignore the new pathelement and continue using last element
+ //
+ // shortcut =====> intersection
+ // This is OK as long as intersection is the destination
+ // intersection of that shortcut road.
+ //
+ // shortcut =====> normalroad
+ // This is not OK
+ //
+ // shortcut =====> shortcut
+ // This is not OK
+ //
+ // normalroad ===> intersection
+ // This is OK as long as intersection is either source or dest
+ // intersection of this non-shortcut road.
+ //
+ // normalroad ===> normalroad
+ // This is OK because cars transit from road to road all
+ // the time at corners, skipping over intervening intersection
+ //
+ // normalroad ===> shortcut
+ // This is not OK
+ //
+ // intersection => intersection
+ // This is not OK
+ //
+ // intersection => normalroad
+ // This is OK as long as the road is connected to the intersection
+ //
+ // intersection => shortcut
+ // This is OK as long as the shortcut is an OUT road from the
+ // intersection---i.e. the shortcut road's source intersection
+ // is this intersection.
+ //
+ // NULL ========> anything else
+ // This is OK
+ //
+
+ //
+ // true means not to ignore the path element we just found
+ // default is true because we could have lastpathelement == elem
+ // but we could be on different segments...
+ //
+ bool OK = true;
+ //bool recomputePath = false;
+
+ if( mLastPathElement != elem )
+ {
+ if( mLastPathElement.elem == NULL )
+ {
+ OK = true;
+ }
+ else
+ {
+ Road* lastRoad = NULL;
+ Intersection* lastInt = NULL;
+ Road* currRoad = NULL;
+ Intersection* currInt = NULL;
+
+ // temporary type: 0 = intersection, 1 = road, 2 = shortcut
+
+ // Figure out what type is LAST
+ int lastElemType = 0;
+ if( mLastPathElement.type == RoadManager::ET_NORMALROAD )
+ {
+ lastRoad = (Road*) mLastPathElement.elem;
+ lastElemType++;
+
+ if( lastRoad->GetShortCut() )
+ {
+ lastElemType++;
+ }
+ }
+ else
+ {
+ lastInt = (Intersection*) mLastPathElement.elem;
+ }
+
+ // Figure out what type is CURR
+ int currElemType = 0;
+ if( elem.type == RoadManager::ET_NORMALROAD )
+ {
+ currRoad = (Road*) elem.elem;
+ currElemType++;
+
+ if( currRoad->GetShortCut() )
+ {
+ currElemType++;
+ }
+ }
+ else
+ {
+ currInt = (Intersection*) elem.elem;
+ }
+
+ //
+ // Start resolving ...
+ ///////////////
+ // LAST: Intersection
+ if( lastElemType == 0 )
+ {
+ rAssert( lastInt );
+ if( currElemType == 2 )// CURR: shortcut
+ {
+ rAssert( currRoad );
+ if( lastInt == (Intersection*) currRoad->GetSourceIntersection() ||
+ lastInt == (Intersection*) currRoad->GetDestinationIntersection() )
+ {
+ OK = true;
+ }
+ else
+ {
+ OK = false;
+ }
+ }
+ else
+ {
+ OK = true;
+ }
+ /*
+ if( currElemType == 0 ) // CURR: intersection
+ {
+ rAssert( currInt );
+ OK = false;
+ }
+ else if( currElemType == 1 ) // CURR: normal road
+ {
+ rAssert( currRoad );
+ if( lastInt == (Intersection*) currRoad->GetSourceIntersection() ||
+ lastInt == (Intersection*) currRoad->GetDestinationIntersection() )
+ {
+ OK = true;
+ }
+ else
+ {
+ OK = false;
+ }
+ }
+ else // CURR: shortcut
+ {
+ rAssert( currRoad );
+ if( lastInt == (Intersection*) currRoad->GetSourceIntersection() )
+ {
+ OK = true;
+ }
+ else
+ {
+ OK = false;
+ }
+ }
+ */
+ }
+ /////////////////
+ // LAST: Normal road
+ else if( lastElemType == 1 )
+ {
+ rAssert( lastRoad );
+
+ if( currElemType == 0 ) // CURR: intersection
+ {
+ rAssert( currInt );
+ if( currInt == (Intersection*)lastRoad->GetSourceIntersection() ||
+ currInt == (Intersection*)lastRoad->GetDestinationIntersection() )
+ {
+ OK = true;
+ }
+ else
+ {
+ OK = false;
+ }
+ }
+ else if( currElemType == 1 ) // CURR: normal road
+ {
+ rAssert( currRoad );
+ OK = true;
+ /*
+ if( lastRoad->GetSourceIntersection() == currRoad->GetDestinationIntersection() &&
+ lastRoad->GetDestinationIntersection() == currRoad->GetSourceIntersection() )
+ {
+ OK = false;
+ }
+ else
+ {
+ OK = true;
+ }
+ */
+ }
+ else // CURR: shortcut
+ {
+ rAssert( currRoad );
+ OK = false;
+ }
+ }
+ ////////////////
+ // LAST: Shortcut
+ else
+ {
+ rAssert( lastRoad );
+
+ if( currElemType == 0 ) // CURR: intersection
+ {
+ rAssert( currInt );
+
+ // NOTE:
+ // This is the fix to Level 3 Mission 7 where the limo drives over the where the shortcuts
+ // and normal road segments merge near the squidport.... This was where its last good element
+ // was found... It then transitted onto the intersection, but rejected it because back then
+ // we only tested for the destination intersection... So it was stuck there for a long time
+ // but didn't realize it until it hit the next waypoint (wayy... over by the dam) and decided
+ // it needed to turn back.
+ if( currInt == (Intersection*)lastRoad->GetDestinationIntersection() ||
+ currInt == (Intersection*)lastRoad->GetSourceIntersection() )
+ {
+ OK = true;
+
+ // TODO:
+ // Tell AI to recompute?
+ // recomputePath = true;
+ }
+ else
+ {
+ OK = false;
+ }
+ }
+ else if( currElemType == 1 ) // CURR: normal road
+ {
+ rAssert( currRoad );
+ OK = false;
+ }
+ else // CURR: shortcut
+ {
+ rAssert( currRoad );
+ OK = false;
+ }
+ }
+ }
+ }
+
+ if( OK )
+ {
+ mLastPathElement = elem;
+ if( seg )
+ {
+ if( mLastRoadSegment != seg )
+ {
+ mLastRoadSegment = seg;
+ }
+ mLastRoadSegmentT = segT;
+ mLastRoadT = roadT;
+ }
+ //if( recomputePath )
+ //{
+ // mNumSegments = 0;
+ // FillPathElements();
+ //}
+ }
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+void VehicleAI::FollowRoad()
+{
+ // Traverse mSegments for some lookaheaddistance meters, if
+ // FillSegments was successful...
+ if( mNumSegments > 0 )
+ {
+ rmt::Vector myPos;
+ GetVehicle()->GetPosition( &myPos );
+
+ // determine our progress along the first Segment...
+ rmt::Vector toMyPos, segForward;
+ segForward = mSegments[0].mEnd - mSegments[0].mStart;
+ toMyPos = myPos - mSegments[0].mStart;
+
+ float scale = segForward.Dot(toMyPos) / segForward.Dot(segForward);
+ rmt::Vector proj = segForward * scale;
+
+ float t = 0.0f;
+ if( scale >= 0.0f )
+ {
+ // projection is going in same direction as segment,
+ // so we can safely produce a "t" value here...
+ t = proj.Length() / mSegments[0].mLength; // *** SQUARE ROOT! ***
+ }
+ //rAssert( 0.0f <= t && t <= 1.0f );
+
+ //
+ // determine how far we want to look ahead for first destination
+ //
+ float lookAheadCloseDist;
+
+ if(GetVehicle()->IsInReverse())
+ {
+ lookAheadCloseDist = MIN_LOOK_AHEAD_CLOSE;
+ }
+ else
+ {
+ lookAheadCloseDist = rmt::Clamp( GetVehicle()->mSpeed * LOOK_AHEAD_CLOSE_SECONDS,
+ MIN_LOOK_AHEAD_CLOSE, MAX_LOOK_AHEAD_CLOSE );
+
+ }
+
+ rmt::Vector lookAheadClose;
+ GetPosAheadAlongRoad( t, lookAheadCloseDist, 0, &lookAheadClose );
+ SetDestination( lookAheadClose );
+
+
+ //
+ // determine how far we want to look ahead for second destination
+ //
+ float lookAheadFarDist = rmt::Clamp(
+ GetVehicle()->mSpeed * LOOK_AHEAD_FAR_SECONDS,
+ MIN_LOOK_AHEAD_FAR, MAX_LOOK_AHEAD_FAR );
+
+ rmt::Vector lookAheadFar;
+ GetPosAheadAlongRoad( t, lookAheadFarDist, 0, &lookAheadFar );
+ SetNextDestination( lookAheadFar );
+
+ rAssert( !rmt::IsNan(t) );
+ }
+ return;
+}
+
+void VehicleAI::FindClosestSegment( const rmt::Vector& pos,
+ int& closestIndex,
+ float& closestDistSqr,
+ rmt::Vector& closestPt )
+{
+ rAssert( 0 <= mNumSegments && mNumSegments <= MAX_SEGMENTS );
+
+ closestIndex = -1;
+ closestDistSqr = 100.0f; // this is the max distance we'll allow to be considered "near enough"
+ // to a segment. If we never find something at least this close,
+ // caller to this method should attempt to repopulate the segments list
+
+ for( int i=0; i<mNumSegments; i++ )
+ {
+ rmt::Vector closestSegPos;
+ FindClosestPointOnLine( mSegments[i].mStart, mSegments[i].mEnd, pos, closestSegPos );
+
+ rmt::Vector toPos = pos - closestSegPos;
+ float distToPosSqr = toPos.MagnitudeSqr();
+ if( distToPosSqr <= closestDistSqr )
+ {
+ closestPt = closestSegPos;
+ closestDistSqr = distToPosSqr;
+ closestIndex = i;
+ }
+ }
+ /*
+ rDebugPrintf( " *** closest segment == %d, distance = %f, pos = (%f,%f,%f), segStart = (%f,%f,%f)\n",
+ closestIndex, rmt::Sqrt(closestDistSqr), pos.x, pos.y, pos.z,
+ mSegments[closestIndex].mStart.x, mSegments[closestIndex].mStart.y, mSegments[closestIndex].mStart.z);
+ */
+}
+
+// this resets the segments
+void VehicleAI::ResetSegments()
+{
+ mNumSegments = 0;
+}
+
+
+//
+// Replace element at index "first" with element at index "first+numShifts"
+// and element at index "first+1" with element at index "first+numShifts+1"
+// and so forth till "first+numShifts+i" == mNumSegments-1
+void VehicleAI::ShiftSegments( int numShifts, int first )
+{
+ rAssert( 0 <= first && first < mNumSegments );
+ rAssert( 0 <= (first+numShifts) && (first+numShifts) < mNumSegments );
+
+ if( numShifts == 0 )
+ {
+ return;
+ }
+
+ mNumSegments -= numShifts;
+ for( int i=first; i<mNumSegments; i++ )
+ {
+ rAssert( 0 <= i && i < mNumSegments );
+ mSegments[i] = mSegments[i+numShifts];
+ }
+}
+
+void VehicleAI::FillSegments()
+{
+ rmt::Vector myPos;
+ GetPosition( &myPos );
+
+ ////////////////////////////// METHOD 1: ITERATING OVER PATHELEMENTS /////////////////////////
+ //
+ // If we need to repopulate for any reasons:
+ // - we reached the target (waypoint) or target (chase) has moved to another pathelement
+ // - we ran out of segments
+ // - we ran out of path elements
+ // - we are > 10 meters from all of the segments in our segments arrray
+ //
+ // Then recompute path and refill segments from scratch
+ //
+ if( mNumSegments == 0 || mCurrPathElement == mPathElements.mUseSize )
+ {
+ mNumSegments = 0;
+
+ /*
+ // re-search for our nearest road only if we're not a chase AI
+ // (cuz chase AI repopulates too often, this will take too long)
+ if( mType == AI_WAYPOINT )
+ {
+ mLastPathElement.elem = NULL;
+ mLastRoadSegment = NULL;
+ UpdateSelf();
+ }
+ */
+
+ // Redo pathfinding
+ FillPathElements();
+ if( mPathElements.mUseSize <= 0 )
+ {
+ mCurrPathElement = -1;
+ return;
+ }
+ mCurrPathElement = 0;
+ }
+
+ //rDebugPrintf( "*** Starting to fill segments: mNumSegments = %d ***\n", mNumSegments );
+
+ ///////////////////////////////////////////////////
+ // Grab some target information
+ //
+ rmt::Vector targetPos;
+ RoadManager::PathElement targetElem;
+ RoadSegment* targetSeg = NULL;
+ float targetSegT = 0.0f;
+ float targetRoadT = 0.0f;
+
+ // can return NULL targetElem if the chase target, for example,
+ // gets out of his vehicle...
+ GetClosestPathElementToTarget( targetPos, targetElem, targetSeg, targetSegT, targetRoadT );
+ if( targetElem.elem == NULL )
+ {
+ return;
+ }
+
+ unsigned int targetSegIndex = 0;
+ if( targetElem.type == RoadManager::ET_NORMALROAD )
+ {
+ rAssert( targetSeg );
+ rAssert( targetSeg->GetRoad() == (Road*) targetElem.elem );
+ targetSegIndex = targetSeg->GetSegmentIndex();
+ }
+
+
+ //////////////////////////////////////////////////////////////////
+ // Loop over all the path elements, filling up mSegments array
+ //
+ while( mCurrPathElement < mPathElements.mUseSize )
+ {
+ int i = mCurrPathElement;
+ if( mNumSegments >= MAX_SEGMENTS )
+ {
+ break;
+ }
+
+ // the ever-useful... last segment (if one exists)
+ Segment* last = (mNumSegments == 0)? NULL : &(mSegments[mNumSegments-1]);
+ if( last )
+ {
+ if( TestReachedTarget( last->mStart, last->mEnd ) )
+ {
+ return; // don't populate beyond the current waypoint
+ }
+ }
+ RoadManager::PathElement* currElem = &(mPathElements[i]);
+ RoadManager::PathElement* nextElem = NULL;
+ if( (i+1) < mPathElements.mUseSize )
+ {
+ nextElem = &(mPathElements[i+1]);
+ }
+
+ if( currElem->type == RoadManager::ET_INTERSECTION )
+ {
+ //rDebugPrintf( "*** FILLSEGMENTS: CurrElem is intersection, mCurrPathElement = %d\n", mCurrPathElement );
+
+
+ Intersection* currInt = (Intersection*) currElem->elem;
+ rmt::Vector currIntPos;
+ currInt->GetLocation( currIntPos );
+
+ // SPECIAL CASE:
+ // if no next element and we are in same intersection as target...
+ // then beeline at the target
+ if( nextElem == NULL )
+ {
+ rAssert( targetElem == (*currElem) );
+
+ // TODO:
+ // Use currIntPos or myPos here... Hmm....
+ rmt::Vector startPos = (last)? last->mEnd : myPos;
+
+ rAssert( !targetPos.Equals( startPos, 0.001f ) );
+
+ mSegments[mNumSegments].mStart = startPos;
+ mSegments[mNumSegments].mEnd = targetPos;
+ mSegments[mNumSegments].mType = 1; // irrelevant... 1 or 2
+ mSegments[mNumSegments].mLength = (
+ mSegments[mNumSegments].mStart -
+ mSegments[mNumSegments].mEnd).Magnitude(); // *** SQUARE ROOT! ***
+ mSegments[mNumSegments].mpSegment = mLastRoadSegment;
+ mSegments[mNumSegments].SelfVerify();
+
+ mNumSegments++;
+
+ // done.. Don't increment mCurrPathElement
+ // we reached target. Yay.
+ return;
+ }
+ // if there is a next element, it has to be a road.
+ // We create tweening segments to that road
+ else
+ {
+ rAssert( targetElem != (*currElem) );
+ rAssert( nextElem->type == RoadManager::ET_NORMALROAD );
+
+ ///////////////// SHORTCUT LOGIC ////////////////////////
+ // Decide whether or not to take shortcut:
+ // will not take if shortcut road is not an OUT road
+ // will not take if shortcut's dest int is not in the list from mCurrPathElement+1 forward
+ // will not take if shortcut skill is higher than my skill (adjusted by catch-up logic)
+ // If we take the shortcut, we have to do some dissecting such that
+ // the PathElements skipped over by the shortcut (which we are thankfully
+ // not yet visiting) are removed from mPathElements array (do removeKeepOrder)
+ //
+ // NOTE:
+ // We are assuming here that the shortcut is actually SHORTER
+ // than these elements we're replacing... It'd better be!
+ // If we can't assume this then we'll have to add up the
+ // lengths of the elements we're replacing and compare with
+ // length of this shortcut road... *sigh*
+ //
+ int mySkill = DetermineShortcutSkill();
+
+ int shortcutDestIntElemIndex = -1;
+ Road* shortcutRoad = NULL;
+
+ //
+ // process ALL shortcuts (which are by definition OUT roads...
+ // Note that direction matters... sometimes you can't take
+ // a shortcut in the opposite direction... e.g. a jump)
+ //
+ for( int k=0; k<currInt->mOutgoingShortcuts.mUseSize; k++ )
+ {
+ Road* candidate = currInt->mOutgoingShortcuts[k];
+ rAssert( candidate );
+ rAssert( candidate->GetShortCut() );
+
+ // NOTE: We are limited here by the assumption that the shortcut
+ // is only one road long!
+
+ // check if the destination intersection for this shortcut
+ // exists in my list of path elements!
+ Intersection* destInt = (Intersection*) candidate->GetDestinationIntersection();
+
+ for( int m=mCurrPathElement+2; m<mPathElements.mUseSize; m+=2 )
+ {
+ RoadManager::PathElement* intElem = &(mPathElements[m]);
+ rAssert( intElem->type == RoadManager::ET_INTERSECTION );
+
+ if( (Intersection*)intElem->elem == destInt )
+ {
+ // found the destination intersection! Now see if we are
+ // skilled enough to take this shortcut
+ if( mySkill >= (int)(candidate->GetDifficulty()) )
+ {
+ // ok, we're skilled enough... but does this shortcut
+ // take us further than all the other shortcuts in
+ // this intersection?
+ if( m > shortcutDestIntElemIndex )
+ {
+ shortcutDestIntElemIndex = m;
+ shortcutRoad = candidate;
+ }
+ }
+ break; // break here cuz we found the intersection
+ }
+ }
+ }
+
+ // if we didn't find anything, that's ok. But if we did, it'd
+ // better be within expected bounds!
+ rAssert( shortcutDestIntElemIndex == -1 ||
+ (mCurrPathElement+2 <= shortcutDestIntElemIndex &&
+ shortcutDestIntElemIndex < mPathElements.mUseSize) );
+
+ // ok, found a shortcut that we want to take...
+ if( shortcutRoad )
+ {
+ rAssert( mCurrPathElement+2 <= shortcutDestIntElemIndex &&
+ shortcutDestIntElemIndex < mPathElements.mUseSize );
+
+ // Do some cut&paste:
+ // replace elems between mCurrElements and shortcutDestIntElemIndex
+ // (exclusive) with the shortcut road element...
+
+ mPathElements[mCurrPathElement+1].type = RoadManager::ET_NORMALROAD;
+ mPathElements[mCurrPathElement+1].elem = shortcutRoad;
+
+ // If necessary, shift elements to get rid of stuff in between:
+ // ...,curr+1,curr+2,curr+3,...,m,m+1,... ==> ...,curr+1,m,m+1,...
+ int numShifts = shortcutDestIntElemIndex - (mCurrPathElement+2);
+ if( numShifts > 0 )
+ {
+ int newUseSize = mPathElements.mUseSize - numShifts;
+ for( int n=mCurrPathElement+2; n<newUseSize; n++ )
+ {
+ mPathElements[n] = mPathElements[n+numShifts];
+ }
+ mPathElements.mUseSize = newUseSize;
+ }
+
+ // make sure connectivity is correct...
+ rAssert( (Intersection*)shortcutRoad->GetSourceIntersection() == currInt );
+ rAssert( (Intersection*)shortcutRoad->GetDestinationIntersection() ==
+ (Intersection*)(mPathElements[mCurrPathElement+2].elem) );
+ }
+ /////////////////////////////////////////
+
+
+
+ //////////////////////////////////////////////////////
+ // Determine an appropriate OUT road segment, OUT road, and join-point
+ // (for tweening). Recall that we can traverse a road in either
+ // directions.. so make sure we get the correct segments and join-points
+
+ RoadSegment* outSeg = NULL;
+ rmt::Vector outPosStart, outPosEnd;
+
+ bool forward = true;
+ Road* outRoad = (Road*) nextElem->elem;
+ if( currInt == (Intersection*) outRoad->GetSourceIntersection() )
+ {
+ outSeg = outRoad->GetRoadSegment( 0 );
+ }
+ else
+ {
+ forward = false;
+ outSeg = outRoad->GetRoadSegment( outRoad->GetNumRoadSegments()-1 );
+ }
+ rAssert( outSeg );
+
+ rmt::Vector vec0,vec1,vec2,vec3;
+ rmt::Vector outSegStart, outSegEnd;
+
+ outSeg->GetCorner( 0, vec0 );
+ outSeg->GetCorner( 1, vec1 );
+ outSeg->GetCorner( 2, vec2 );
+ outSeg->GetCorner( 3, vec3 );
+ outSegStart = (vec0 + vec3) * 0.5f;
+ outSegEnd = (vec1 + vec2) * 0.5f;
+
+ if( forward )
+ {
+ outPosStart = outSegStart;
+ outPosEnd = outSegEnd;
+ }
+ else
+ {
+ outPosStart = outSegEnd;
+ outPosEnd = outSegStart;
+ }
+
+ ////////////////////////////////////////////////
+ // Build tween segments to that OUT road
+
+ float distToOutPos = 0.0f;
+ bool needFirstTween = true;
+ bool needLastTween = true;
+ if( last )
+ {
+ distToOutPos = (last->mEnd - outPosStart).Magnitude();
+ if( distToOutPos <= 0.001f )
+ {
+ // so close they're actually the same point!
+ // no tweening necessary since last->mEnd is already at outPosStart...
+ needFirstTween = false;
+ needLastTween = false;
+ }
+ if( distToOutPos <= 1.0f )
+ {
+ // we're so close... don't need the first tween, but need last tween
+ needFirstTween = false;
+ }
+ }
+ else // no last segment available --> no first tween necessary, last tween ok
+ {
+ needFirstTween = false;
+ }
+
+ // TODO:
+ // Use currIntPos or myPos here? Hmmm...
+ rmt::Vector intermediatePos = (last)? last->mEnd : currIntPos;
+
+ if( needFirstTween )
+ {
+ // we never do first tween without second tween
+ rAssert( needLastTween );
+
+ // make sure we can support adding both tweens at once
+ // plus the actual out segment (later)...
+ if( mNumSegments >= (MAX_SEGMENTS-2) )
+ {
+ // by returning, we keep mCurrPathElement on the same
+ // element. Hopefully we'll have more room next time.
+ return;
+ }
+ rAssert( distToOutPos > 1.0f );
+ float tweenDist = distToOutPos * 0.4f;
+
+ rmt::Vector lastSegDir = (last->mEnd - last->mStart) / last->mLength;
+ intermediatePos = last->mEnd + lastSegDir * tweenDist;
+
+ mSegments[mNumSegments].mStart = last->mEnd;
+ mSegments[mNumSegments].mEnd = intermediatePos;
+ mSegments[mNumSegments].mType = 1;
+ mSegments[mNumSegments].mLength = (
+ mSegments[mNumSegments].mStart -
+ mSegments[mNumSegments].mEnd).Magnitude(); // *** SQUARE ROOT! ***
+ mSegments[mNumSegments].mpSegment = outSeg;
+ mSegments[mNumSegments].SelfVerify();
+
+ mNumSegments++;
+
+ rAssert( 1 <= mNumSegments && mNumSegments < MAX_SEGMENTS );
+ last = &(mSegments[mNumSegments]);
+ }
+
+ if( needLastTween )
+ {
+ // make sure we can support adding at least one of the tweens
+ // plus the actual out segment...
+ if( mNumSegments >= (MAX_SEGMENTS-1) )
+ {
+ // by returning, we keep mCurrPathElement on the same
+ // element. Hopefully we'll have more room next time.
+ return;
+ }
+
+ // Second tween: join intermediate pos with out pos.
+ // The intermediate pos at this point can be one of:
+ // - intersection center ... if there was no last segment to continue from
+ // - last segment's end pos ... if we skipped doing the first tween
+ // - intermediate pos ... if we just did a first tween
+ //
+ mSegments[mNumSegments].mStart = intermediatePos;
+ mSegments[mNumSegments].mEnd = outPosStart;
+ mSegments[mNumSegments].mType = 2;
+ mSegments[mNumSegments].mLength = (
+ mSegments[mNumSegments].mStart -
+ mSegments[mNumSegments].mEnd).Magnitude(); // *** SQUARE ROOT! ***
+ mSegments[mNumSegments].mpSegment = outSeg;
+ mSegments[mNumSegments].SelfVerify();
+
+ mNumSegments++;
+ }
+
+ // Add the actual segment
+ mSegments[mNumSegments].mStart = outPosStart;
+ mSegments[mNumSegments].mEnd = outPosEnd;
+ mSegments[mNumSegments].mType = 0;
+ mSegments[mNumSegments].mLength = outSeg->GetSegmentLength();
+ mSegments[mNumSegments].mpSegment = outSeg;
+ mSegments[mNumSegments].SelfVerify();
+
+ mNumSegments++;
+
+ // OK done with currElem... go on to next one...
+ mCurrPathElement++;
+ }
+ }
+ else if( currElem->type == RoadManager::ET_NORMALROAD )
+ {
+ //rDebugPrintf( "*** FILLSEGMENTS: CurrElem is road, mCurrPathElement = %d\n", mCurrPathElement );
+ Road* currRoad = (Road*) currElem->elem;
+
+ unsigned int numRoadSegs = currRoad->GetNumRoadSegments();
+ rAssert( numRoadSegs > 0 );
+
+ /*** ATTEMPT 1***/
+ while( mNumSegments < MAX_SEGMENTS )
+ {
+ int j = mNumSegments; // numsegs should increment as j increments
+ last = (j <= 0)? NULL : &(mSegments[j-1]);
+
+ // don't populate beyond the current waypoint
+ if( last )
+ {
+ if( TestReachedTarget( last->mStart, last->mEnd ) )
+ {
+ return;
+ }
+ }
+ RoadSegment* seg = NULL;
+ float segT = 0.0f;
+ unsigned int segIndex = 0;
+ rmt::Vector segStart, segEnd;
+
+ // if last doesn't exist then we're populating this
+ // for the first time... presumably our stored last road segment
+ // reflects the current road element.. use this as our starting point
+ if( last == NULL )
+ {
+ rAssert( mLastRoadSegment );
+ // if last road segment isn't even on the current road, just
+ // use the current road rather than the last road segment.
+ if( mLastRoadSegment->GetRoad() != currRoad )
+ {
+ rmt::Vector closePos;
+ float closeDist = NEAR_INFINITY;
+ int ind = -1;
+
+ RoadManager::FindClosestPointOnRoad( currRoad, myPos, closePos, closeDist, ind );
+ rAssert( ind != -1 );
+
+ segIndex = (unsigned int)ind;
+ seg = currRoad->GetRoadSegment( segIndex );
+ segT = RoadManager::DetermineSegmentT( myPos, (RoadSegment*)seg );
+
+ }
+ else
+ {
+ seg = mLastRoadSegment;
+ segT = mLastRoadSegmentT;
+ segIndex = seg->GetSegmentIndex();
+ }
+
+ rmt::Vector vec0,vec1,vec2,vec3;
+ seg->GetCorner( 0, vec0 );
+ seg->GetCorner( 1, vec1 );
+ seg->GetCorner( 2, vec2 );
+ seg->GetCorner( 3, vec3 );
+ segStart = (vec0+vec3) * 0.5f;
+ segEnd = (vec1+vec2) * 0.5f;
+
+ // if no next element exists, we're near target
+ bool useSegStart = true;
+ if( nextElem == NULL ) // there is no next element
+ {
+ rAssert( targetElem == (*currElem) );
+ rAssert( targetSeg->GetRoad() == currRoad );
+
+ // if mPathElements has only one element, then
+ // we are on the same road as our target...
+ //
+ // if target is further along road..
+ if( targetSegIndex > segIndex )
+ {
+ useSegStart = true;
+ }
+ // target lies in the other direction, go against traffic!
+ else if( targetSegIndex < segIndex )
+ {
+ useSegStart = false;
+ }
+ // target is on the same road segment
+ else
+ {
+ if( targetSegT >= segT ) // target ahead of us
+ {
+ useSegStart = true;
+ }
+ else
+ {
+ useSegStart = false;
+ }
+ }
+ }
+ else // next elem is NOT null
+ {
+ rAssert( targetElem != (*currElem) );
+
+ // we got more than 1 element, we now know the direction
+ // the first segment should be heading simply by checking
+ // the next mPathElement (an intersection) and finding out
+ // whether that is the current PathElement (road)'s source
+ // or dest intersection
+
+ rAssert( nextElem->type == RoadManager::ET_INTERSECTION );
+ Intersection* nextInt = (Intersection*) nextElem->elem;
+
+ // next intersection is this road's destination intersection,
+ // so the road goes forward
+ if( (Intersection*) currRoad->GetDestinationIntersection() == nextInt )
+ {
+ useSegStart = true;
+ }
+ // otherwise the road goes the other way
+ else
+ {
+ rAssert( (Intersection*) currRoad->GetSourceIntersection() == nextInt );
+ useSegStart = false;
+ }
+ }
+
+ // time to actually do some segment population
+ if( useSegStart )
+ {
+ mSegments[j].mStart = segStart;
+ mSegments[j].mEnd = segEnd;
+ }
+ else
+ {
+ mSegments[j].mStart = segEnd;
+ mSegments[j].mEnd = segStart;
+ }
+ mSegments[j].mType = 0;
+ mSegments[j].mLength = seg->GetSegmentLength();
+ mSegments[j].mpSegment = seg;
+ mSegments[j].SelfVerify();
+
+ mNumSegments++;
+ }
+ else // if last is not NULL, we have to worry about join point
+ {
+ RoadSegment* lastSeg = last->mpSegment;
+ rAssert( lastSeg );
+
+ // TODO:
+ // if it's not even on our road, should we just join it with
+ // the first or the last road segment on this road?
+ // or does this situation never arise? Assert for now.
+ rAssert( lastSeg->GetRoad() == currRoad );
+
+ unsigned int lastSegIndex = lastSeg->GetSegmentIndex();
+
+ bool useSegStart = false;
+
+ if( nextElem == NULL )
+ {
+ // we are on the same road as our target...
+ rAssert( targetElem == (*currElem) );
+ rAssert( targetSeg->GetRoad() == currRoad );
+
+ // if target is further along road, we want to begin this
+ // segment at segStart and end it at segEnd
+ if( targetSegIndex > lastSegIndex )
+ {
+ useSegStart = true;
+
+ // if target can have index > our index, next segment exists!
+ rAssert( lastSegIndex < (numRoadSegs-1) );
+
+ segIndex = lastSegIndex+1;
+ seg = currRoad->GetRoadSegment( segIndex );
+ }
+ // target lies in the other direction, use seg's end pos...
+ else if( targetSegIndex < lastSegIndex )
+ {
+ useSegStart = false;
+
+ // if target can have index < our index, prev segment exists!
+ rAssert( lastSegIndex > 0 );
+
+ segIndex = lastSegIndex-1;
+ seg = currRoad->GetRoadSegment( segIndex );
+ }
+ else // target is on the same road segment
+ {
+ segIndex = lastSegIndex;
+ seg = lastSeg;
+
+ // We should really stop filling segments after this one...
+ // We hope that the segment we're about to make takes
+ // us close enough to the target to yield a TRUE value
+ // when TestReachedTarget is called
+ if( targetSegT >= segT ) // target ahead of us
+ {
+ useSegStart = true;
+ }
+ else
+ {
+ useSegStart = false;
+ }
+ }
+ }
+ else // if nextElem is not NULL
+ {
+ rAssert( targetElem != (*currElem) );
+ rAssert( nextElem->type == RoadManager::ET_INTERSECTION );
+
+ Intersection* nextInt = (Intersection*) nextElem->elem;
+
+ // if we want to traverse the road in the forward direction...
+ if( nextInt == (Intersection*) currRoad->GetDestinationIntersection() )
+ {
+ useSegStart = true;
+
+ if( lastSegIndex < (numRoadSegs-1) )
+ {
+ segIndex = lastSegIndex+1;
+ seg = currRoad->GetRoadSegment( segIndex );
+ }
+ else
+ {
+ //seg = lastSeg;
+ // break out of the current WHILE loop over mSegments
+ // we've reached one end of the road and are thus
+ // done with the current element (ready for next one)
+ mCurrPathElement++;
+ break;
+ }
+ }
+ else // traverse in the "backward" direction
+ {
+ rAssert( nextInt == (Intersection*) currRoad->GetSourceIntersection() );
+ useSegStart = false;
+ if( lastSegIndex > 0 )
+ {
+ segIndex = lastSegIndex-1;
+ seg = currRoad->GetRoadSegment( segIndex );
+ }
+ else
+ {
+ //seg = lastSeg;
+ // break out of the current WHILE loop over mSegments
+ // we've reached one end of the road and are thus
+ // done with the current element (ready for next one)
+ mCurrPathElement++;
+ break;
+ }
+ }
+ }
+
+ rAssert( seg );
+ rmt::Vector vec0,vec1,vec2,vec3;
+ seg->GetCorner( 0, vec0 );
+ seg->GetCorner( 1, vec1 );
+ seg->GetCorner( 2, vec2 );
+ seg->GetCorner( 3, vec3 );
+ segStart = (vec0+vec3) * 0.5f;
+ segEnd = (vec1+vec2) * 0.5f;
+
+ // figure out which endpoint of the segment this is...
+ // 0 = at segStart, 1 = at segEnd, 2 = somewhere else
+ // (the last scenario can occur if we do Optimization 1
+ // in UpdateSegments)
+ int joinPointLocation = -1;
+
+ if( last->mEnd.Equals( segEnd, 0.001f ) )
+ {
+ joinPointLocation = 1;
+ }
+ else if( last->mEnd.Equals( segStart, 0.001f ) )
+ {
+ joinPointLocation = 0;
+ }
+ else
+ {
+ // if this is the case... then we shall (later)
+ // need to build a tween segment from joinpoint to
+ // one of the segment's endpoints (segStart or segEnd)
+ // whichever one we want to use as the starting point
+ // for the next segment
+ joinPointLocation = 2;
+ }
+
+ // now actually do the segment building
+ if( useSegStart ) // want to build segment from segStart to segEnd
+ {
+ if( joinPointLocation == 0 ) // joinpoint at segStart
+ {
+ mSegments[j].mStart = segStart;
+ mSegments[j].mEnd = segEnd;
+ mSegments[j].mType = 0;
+ }
+ else if( joinPointLocation == 1 ) // already at segEnd
+ {
+ // skip adding this particular segment
+ // try adding the next one... (we have to add
+ // SOMETHING else we'd be stuck here...)
+ if( segIndex < (numRoadSegs-1) )
+ {
+ segIndex++;
+ seg = currRoad->GetRoadSegment( segIndex );
+ seg->GetCorner( 0, vec0 );
+ seg->GetCorner( 1, vec1 );
+ seg->GetCorner( 2, vec2 );
+ seg->GetCorner( 3, vec3 );
+ segStart = (vec0+vec3) * 0.5f;
+ segStart = (vec1+vec2) * 0.5f;
+ mSegments[j].mStart = segStart;
+ mSegments[j].mEnd = segEnd;
+ mSegments[j].mType = 0;
+ }
+ else
+ {
+ // break out of this inner FOR loop because we're done
+ // with the current path element (reached end of the road)
+ mCurrPathElement++;
+ break;
+ }
+ }
+ else // joinpoint is somewhere else entirely...
+ {
+ // add a tween segment from joinpoint right to segEnd
+ mSegments[j].mStart = last->mEnd;
+ mSegments[j].mEnd = segEnd;
+ mSegments[j].mType = 2;
+ }
+ }
+ else // want to build a segment from segEnd to segStart
+ {
+ if( joinPointLocation == 0 ) // already at segStart
+ {
+ // skip adding this particular segment
+ // try adding the next one... (we have to add
+ // SOMETHING else we'd be stuck here...)
+ if( segIndex > 0 )
+ {
+ segIndex--;
+ seg = currRoad->GetRoadSegment( segIndex );
+ seg->GetCorner( 0, vec0 );
+ seg->GetCorner( 1, vec1 );
+ seg->GetCorner( 2, vec2 );
+ seg->GetCorner( 3, vec3 );
+ segStart = (vec0+vec3) * 0.5f;
+ segStart = (vec1+vec2) * 0.5f;
+ mSegments[j].mStart = segEnd;
+ mSegments[j].mEnd = segStart;
+ mSegments[j].mType = 0;
+ }
+ else
+ {
+ // break out of this inner FOR loop because we're done
+ // with the current path element (reached end of the road)
+ mCurrPathElement++;
+ break;
+ }
+ }
+ else if( joinPointLocation == 1 ) // at segEnd
+ {
+ mSegments[j].mStart = segEnd;
+ mSegments[j].mEnd = segStart;
+ mSegments[j].mType = 0;
+ }
+ else // joinpoint is somewhere else entirely...
+ {
+ // add a tween segment from joinpoint right to segStart
+ mSegments[j].mStart = last->mEnd;
+ mSegments[j].mEnd = segStart;
+ mSegments[j].mType = 2;
+ }
+ }
+ mSegments[j].mpSegment = seg;
+ mSegments[j].mLength = (mSegments[j].mType == 0)?
+ seg->GetSegmentLength() :
+ (mSegments[j].mStart - mSegments[j].mEnd).Magnitude(); // *** SQUARE ROOT! ***
+ mSegments[j].SelfVerify();
+
+ mNumSegments++;
+
+ }// end of else (last is not NULL)
+ }// end of WHILE loop over mSegments
+ } // end of if currElem is type ET_NORMALROAD
+ else
+ {
+ rAssertMsg( false, "Uh-oh... I never anticipated this type... something new?\n" );
+ }
+ } // end of WHILE loop over mPathElements
+
+}
+
+bool VehicleAI::MustRepopulateSegments()
+{
+ return false;
+}
+
+void VehicleAI::GetPosAheadAlongRoad( float t, float lookAheadDist, int i, rmt::Vector* lookAheadPos )
+{
+ rAssert( 0 <= i && i < mNumSegments );
+
+ //rDebugPrintf( "Looking ahead %f meters\n", lookAheadDist );
+
+ float origT = t;
+ int origI = i;
+
+ rmt::Vector segDir = mSegments[i].mEnd - mSegments[i].mStart;
+ rmt::Vector start = mSegments[i].mStart + segDir * t;
+
+ // augment t with our new distance and figure out which
+ // segment this falls on and what the exact position is...
+ t += lookAheadDist / mSegments[i].mLength;
+ while( t > 1.0f && i < (mNumSegments-1) )
+ {
+ i++;
+ rAssert( 1 <= i && i < mNumSegments );
+
+ t -= 1.0f;
+ t *= mSegments[i-1].mLength / mSegments[i].mLength;
+ rAssert( !rmt::IsNan(t) );
+ }
+
+ // last segment, we don't want to go behind this.
+ t = rmt::Clamp( t, 0.0f, 1.0f );
+
+ // now we have the segment we want... find the position along this segment
+ segDir = mSegments[i].mEnd - mSegments[i].mStart;
+ (*lookAheadPos) = mSegments[i].mStart + segDir * t;
+
+ rAssert( !rmt::IsNan((*lookAheadPos).x) && !rmt::IsNan( (*lookAheadPos).z) );
+
+}
+
+void VehicleAI::UpdateSegments()
+{
+ // THE APPROACH
+ // ============
+ // - get my position "p"
+ // - find out which Segment "s" in mSegments I'm closest to
+ // - TODO: if my distance to "s" is greater than some tolerance, then we're too far off course...
+ // looks like we need to do some sort of pathfinding back. We should be able to insert
+ // new segments into mSegments
+ // - shift mSegments till "s" is at mSegments[0], adding new segments to the end as necessary...
+ // (when adding a new segment, look at the last segment "l"...
+ // if "l" is a normal roadsegment that's not the last segment of that road
+ // add the next roadsegment
+ // else if "l" is the last segment of that road,
+ // add the first tweening segment from the IN segment's trailing edge to the intersection center
+ // Decide which OUT road to take and store its first segment in pSegment field
+ // else if "l" is the first tweening intersection segment,
+ // add the second tweening segment from intersection center to leading edge of the OUT segment
+ // that was stored in "l"'s pSegment field, store the OUT segment in pSegment field
+ // else if "l" is the second tweening intersection segment,
+ // add the OUT road segment stored in "l"'s pSegment field.)
+ //
+ // - find out my projected "t" along this segment "s" based on my position "p"...
+ // - calc my lookahead distance based on my current speed and lookahead time (minimum = 5.0f meters)
+ // - t += lookaheaddist / segmentlen
+ // - while( t > 1.0f && while we're not out of segments in mSegments ) {
+ // t -= 1.0f
+ // go to next segment in mSegments
+ // }
+ // - Finally, we have t < 1.0f... get the position "q" at t along this segment...
+ // - we use "q" as a guidance for steering...
+
+ rmt::Vector myPos;
+ GetVehicle()->GetPosition( &myPos );
+
+ //
+ // If waypoint's nearest segment has changed (won't for waypointAI, will for chaseAI)
+ // and the new segment doesn't already exist in our list, repopulate mSegments
+ // by setting numsegments to 0
+ //
+ bool mustRepopulate = MustRepopulateSegments();
+ if( mustRepopulate )
+ {
+ ResetSegments();
+ mSecondsLeftToGetBackOnPath = SECONDS_REPOPULATE_DELAY;
+ }
+
+ // Attempt to fill up mSegments again
+ FillSegments();
+ // We try to shift the segments and optimize them here
+ //
+ // NOTE: Since we never populate segments beyond the current target
+ // waypoint, we don't need to worry about shifting too far (past
+ // the current waypoint)
+
+ //
+ // Find out which Segment in mSegments I'm closest to & shift this to
+ // the front of the segment queue
+ //
+ int segmentIndex;
+ float distToSegmentSqr;
+ rmt::Vector closestPt;
+ FindClosestSegment( myPos, segmentIndex, distToSegmentSqr, closestPt );
+ if( 0 < segmentIndex && segmentIndex < mNumSegments )
+ {
+ // If we found a segment, make this the first in our
+ // mSegments array (shift off the other segments)
+ ShiftSegments( segmentIndex );
+ mSecondsLeftToGetBackOnPath = SECONDS_REPOPULATE_DELAY;
+ }
+ else if( segmentIndex == -1 || segmentIndex >= mNumSegments )
+ {
+ // If we never found a close segment, repopulate our array?
+ // Only if we have been this way for some time...
+ if( mSecondsLeftToGetBackOnPath < 0.0f )
+ {
+ ResetSegments(); // repopulate now...
+ mSecondsLeftToGetBackOnPath = SECONDS_REPOPULATE_DELAY;
+ }
+ }
+ else // segmentIndex == 0
+ {
+ mSecondsLeftToGetBackOnPath = SECONDS_REPOPULATE_DELAY;
+ }
+
+ //
+ // Optimize the remaining segments...
+ //
+
+ if( mEnableSegmentOptimization )
+ {
+ // Optimization 1
+ // ==============
+ // Eliminate unnecessary segments:
+ // Segments that lie at least 3 segments away from our closest segment
+ // (segment zero... because we did a Shift earlier) should be distance
+ // tested ... if close enough, we tween so we eliminate the segments
+ // in between us. Has to be at least 3 because we don't want to tween
+ // through the two tweening segments in an intersection!
+ //
+ if( mNumSegments > 3 && (0 <= segmentIndex && segmentIndex < mNumSegments) )
+ {
+ rAssert( mSegments[0].mpSegment );
+ float segmentWidth = mSegments[0].mpSegment->GetRoadWidth();
+ float closeEnoughRadius = segmentWidth + 1.0f;
+
+ float SEGMENT_BEELINE_RADIUS_SQR = closeEnoughRadius * closeEnoughRadius;
+ for( int i=mNumSegments-1; i>2; i-- )
+ {
+ rAssert( 1 < i && i < mNumSegments );
+
+ // skip if this is a tweening segment (we shouldn't eliminate these since
+ // they guide us through the intersection)
+ if( mSegments[i].mType == 1 || mSegments[i].mType == 2 )
+ {
+ continue;
+ }
+
+ rmt::Vector closestPtToMySeg;
+ FindClosestPointOnLine( mSegments[i].mStart, mSegments[i].mEnd,
+ closestPt, closestPtToMySeg );
+ float distSqr = (closestPt - closestPtToMySeg).MagnitudeSqr();
+
+ // If this segment is close enough to our current segment,
+ // build a tween segment directly to it!
+ if( distSqr < SEGMENT_BEELINE_RADIUS_SQR )
+ {
+ // what if these two points are identical?
+ if( closestPt.Equals( closestPtToMySeg, 0.001f ) )
+ {
+ // Just skip the thing...
+ continue;
+ }
+ else
+ {
+ // Shift so that 0,1,2,..,i,i+1 becomes 0,1,i,i+1,...
+ // if( closestPt at t=0 and closestPtToMySeg at t=1 )
+ // 1 segment
+ // else if( closestPt at t=0 and closestPtToMySeg not at t=1 )
+ // 2 segments
+ // else if( closestPt not at t=0 and closestPtToMySeg at t=1 )
+ // 2 segments
+ // else
+ // 3 segments
+ bool closestPtAt0 = false;
+ bool closestPtToMySegAt1 = false;
+ if( closestPt.Equals( mSegments[0].mStart, 0.001f ) )
+ {
+ closestPtAt0 = true;
+ }
+ if( closestPtToMySeg.Equals( mSegments[i].mEnd, 0.001f ) )
+ {
+ closestPtToMySegAt1 = true;
+ }
+
+ if( closestPtAt0 && closestPtToMySegAt1 )
+ {
+ // modify i to start where seg0 starts (change) and
+ // end where segi already ends (no change)
+ mSegments[i].mStart = mSegments[0].mStart;
+ mSegments[i].mLength = (mSegments[i].mEnd - mSegments[i].mStart).Magnitude();
+ mSegments[i].SelfVerify();
+
+ // shift so 0,1,2,..,i,i+1 ===> i,i+1,i+2
+ ShiftSegments( i, 0 );
+ }
+ else if( closestPtAt0 && !closestPtToMySegAt1 )
+ {
+ // we'll have 2 segments
+ // modify seg0 to end at closestPtToMySeg, segi to start at closestPtToMySeg
+ mSegments[0].mEnd = closestPtToMySeg;
+ mSegments[0].mLength = (mSegments[0].mEnd - mSegments[0].mStart).Magnitude();
+ mSegments[0].SelfVerify();
+
+ mSegments[i].mStart = closestPtToMySeg;
+ mSegments[i].mLength = (mSegments[i].mEnd - mSegments[i].mStart).Magnitude();
+ mSegments[i].SelfVerify();
+
+ // shift so 0,1,2,..,i,i+1 ===> 0,i,i+1
+ ShiftSegments( i-1, 1 );
+ }
+ else if( !closestPtAt0 && closestPtToMySegAt1 )
+ {
+ // we'll have 2 segments
+ // modify seg0 to end at closestPt, segi to start at closestPt
+ mSegments[0].mEnd = closestPt;
+ mSegments[0].mLength = (mSegments[0].mEnd - mSegments[0].mStart).Magnitude();
+ mSegments[0].SelfVerify();
+
+ mSegments[i].mStart = closestPt;
+ mSegments[i].mLength = (mSegments[i].mEnd - mSegments[i].mStart).Magnitude();
+ mSegments[i].SelfVerify();
+
+ // shift so 0,1,2,..,i,i+1 ===> 0,i,i+1
+ ShiftSegments( i-1, 1 );
+ }
+ else
+ {
+ // we'll have 3 segments
+ // seg0 ends at closestPt, tween between closestPt and closestPtToMySeg,
+ // segi starts at closestPtToMySeg
+ mSegments[0].mEnd = closestPt;
+ mSegments[0].mLength = (mSegments[0].mEnd - mSegments[0].mStart).Magnitude();
+ mSegments[0].SelfVerify();
+
+ // modify seg 1 to be our tween
+ mSegments[1].mType = 1;
+ mSegments[1].mpSegment = mSegments[i].mpSegment;
+ mSegments[1].mStart = closestPt;
+ mSegments[1].mEnd = closestPtToMySeg;
+ mSegments[1].mLength = (mSegments[1].mEnd - mSegments[1].mStart).Magnitude();
+ mSegments[1].SelfVerify();
+
+ mSegments[i].mStart = closestPtToMySeg;
+ mSegments[i].mLength = (mSegments[i].mEnd - mSegments[i].mStart).Magnitude();
+ mSegments[i].SelfVerify();
+
+ // shift so 0,1,2,..,i,i+1 ===> 0,1,i,i+1
+ ShiftSegments( i-2, 2 );
+ }
+ }
+ break;
+ }
+ }
+ }
+ /*
+ // Optimization 2
+ // ==============
+ // Do the 12B12 ==> 12 optimization (when B is a segment no
+ // longer than.. say.. 15 meters)
+ static const float MAX_LENGTH_OF_MIDDLE_SEGMENT = 20.0f;
+ if( mNumSegments >= 5 )
+ {
+ // Loop through segments detecting a 1-2-0-1-2 segment type pattern
+ // keeping track by seenUpTo counter 1 2 3 4 5 according to above
+ int seenUpTo = 0; // have seen nothing...
+ for( int i=0; i<mNumSegments; i++ )
+ {
+ int type = mSegments[i].mType;
+ switch( seenUpTo )
+ {
+ case 0: // seen nothing
+ if( type == 1 )
+ seenUpTo = 1;
+ break;
+ case 1: // seen 1
+ if( type == 2 )
+ seenUpTo = 2;
+ else if( type == 1 )
+ seenUpTo = 1;
+ else
+ seenUpTo = 0;
+ break;
+ case 2: // seen 1-2
+ if( type == 0 )
+ {
+ // test if length of this segment is short enough
+ if( mSegments[i].mLength <= MAX_LENGTH_OF_MIDDLE_SEGMENT )
+ seenUpTo = 3;
+ else
+ seenUpTo = 0;
+ }
+ else if( type == 1 )
+ seenUpTo = 1;
+ else
+ seenUpTo = 0;
+ break;
+ case 3: // seen 1-2-0
+ if( type == 1 )
+ seenUpTo = 4;
+ else
+ seenUpTo = 0;
+ break;
+ case 4: // seen 1-2-0-1
+ if( type == 2 )
+ seenUpTo = 5;
+ else if( type == 1 )
+ seenUpTo = 1;
+ else
+ seenUpTo = 0;
+ break;
+ }
+
+ // if we got to the end, it means we saw the complete string
+ // and the middle segment is short enough to bypass
+ if( seenUpTo == 5 )
+ {
+ // now we convert segments
+ // i-4,i-3,i-2,i-1,i ===> i-4,i-3
+ // The two new segments shall go from the start of i-4 to
+ // the midpoint of i-2, and then from there to the end of
+ // segment i.
+
+ // build the new segments
+ int tween1 = i-4;
+ int tween2 = i-3;
+ int mid = i-2;
+ rAssert( 0 <= tween1 && tween1 < mNumSegments );
+ rAssert( 0 <= tween2 && tween2 < mNumSegments );
+ rAssert( 0 <= mid && mid < mNumSegments );
+
+ RoadSegment* seg = mSegments[i].mpSegment;
+ rmt::Vector midPt = (mSegments[mid].mStart + mSegments[mid].mEnd) / 2.0f;
+
+ mSegments[tween1].mEnd = midPt;
+ mSegments[tween1].mLength = (mSegments[tween1].mStart - mSegments[tween1].mEnd).Magnitude(); // *** SQUARE ROOT! ***
+ mSegments[tween1].mpSegment = seg;
+ mSegments[tween1].mType = 1;
+ mSegments[tween1].SelfVerify();
+
+ mSegments[tween2].mStart = midPt;
+ mSegments[tween2].mEnd = mSegments[i].mEnd;
+ mSegments[tween2].mLength = (mSegments[tween2].mStart - mSegments[tween2].mEnd).Magnitude(); // *** SQUARE ROOT! ***
+ mSegments[tween2].mpSegment = seg;
+ mSegments[tween2].mType = 2;
+ mSegments[tween2].SelfVerify();
+
+ // As a result, we reduce numsegments by 3 and should end up with at least 2 segments
+ mNumSegments -= 3;
+ rAssert( 2 <= mNumSegments && mNumSegments < VehicleAI::MAX_SEGMENTS );
+
+ }
+ }// end of for-loop
+ }// end of if numsegments >= 5 (and end of Optimization 2)
+ */
+ }
+}
+
+
+//=============================================================================
+// VehicleAI::EvadeTraffic
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleAI::EvadeTraffic( Vehicle* exceptThisOne )
+{
+ const float STEER_AROUND = 3.0f; // distance to the side of something to go around
+ const float MAX_DIST = 50.0f; // dont worry about anything farther than this
+ const float IGNORE_COS = -0.707f; // don't worry about stuff in this angle behind you
+
+ mEvading = false;
+
+ if(!EVADE_VEHICLES)
+ {
+ return;
+ }
+
+ if( mState == STATE_REVERSE )
+ {
+ return;
+ }
+
+ rmt::Vector mypos;
+ GetVehicle()->GetPosition( &mypos );
+
+ float closestdistsqr = MAX_DIST * MAX_DIST;
+ Vehicle* closestVehicle = NULL;
+ rmt::Vector closestpos;
+
+ rmt::Vector heading;
+ GetVehicle()->GetHeading( &heading );
+
+ for( int i = 0; i < GetVehicleCentral()->GetNumVehicles(); i++ )
+ {
+ Vehicle* vehicle = GetVehicleCentral()->GetVehicle( i );
+ if( vehicle && (vehicle != GetVehicle()) && (vehicle != exceptThisOne) )
+ {
+ rmt::Vector pos;
+ vehicle->GetPosition( &pos );
+
+ rmt::Vector vec2car;
+ vec2car.Sub( pos, mypos );
+
+ float dp = vec2car.DotProduct( heading );
+
+ if( dp > IGNORE_COS )
+ {
+ float distsqr = vec2car.MagnitudeSqr();
+
+ if( distsqr < closestdistsqr )
+ {
+ closestdistsqr = distsqr;
+ closestVehicle = vehicle;
+ closestpos = pos;
+ }
+ }
+
+ }
+ }
+
+ if( closestVehicle != NULL )
+ {
+ mEvading = true;
+
+ // Project pos into local happy car space
+ rmt::Vector vUp;
+ GetVehicle()->GetVUP( &vUp );
+
+ rmt::Vector side;
+ side.CrossProduct( heading, vUp );
+
+ rmt::Vector relpos = closestpos;
+ relpos.Sub( mypos );
+
+ rmt::Vector vec2relpos;
+
+ vec2relpos.x = side.DotProduct( relpos );
+ vec2relpos.y = 0.0f;
+ vec2relpos.z = heading.DotProduct( relpos );
+
+
+ // if it's in front, steer around
+ if( vec2relpos.z > 0.0f )
+ {
+ rmt::Vector normvec = relpos;
+ normvec.Normalize();
+ float dx = heading.DotProduct( normvec ) * STEER_AROUND * rmt::Sign(vec2relpos.x);
+
+ side.Scale( dx, dx, dx );
+
+ rmt::Vector newdest = mDestination;
+ newdest.Sub( side );
+
+ SetDestination( newdest );
+ }
+ }
+}
+
+/*
+void VehicleAI::EvadeTraffic( Vehicle* exceptThisOne )
+{
+ if( mState == STATE_REVERSE || mState == STATE_WAITING || mState == STATE_STUNNED )
+ {
+ return;
+ }
+
+ rmt::Vector mypos;
+ GetVehicle()->GetPosition( &mypos );
+
+ rmt::Vector myheading;
+ GetVehicle()->GetHeading( &myheading );
+ rAssert( rmt::Epsilon( myheading.MagnitudeSqr(), 1.0f, 0.00001f ) );
+
+ bool bCheck = false;
+
+
+ // Initialize potential field
+
+ mPotentialField.Initialize( 40.0f, 20.0f, mypos, myheading );
+ mPotentialField.Clear( -1.0f );
+
+ float closestcar = 10000.0f;
+
+ float radius, value, falloff;
+
+ float lookAhead = (LOOK_AHEAD_CLOSE_SECONDS * GetVehicle()->mSpeed);
+
+ // Populate field with statics
+ if( mEvadeStatics )
+ {
+
+ int collindex = GetVehicle()->mCollisionAreaIndex;
+
+ sim::TList<sim::CollisionObject*>* list = GetWorldPhysicsManager()->
+ mCollisionManager->GetCollisionObjectList( collindex );
+
+ for( int i = 0; i < list->GetSize(); i++ )
+ {
+ sim::CollisionObject* obj = list->GetAt( i );
+ if( !obj->IsStatic() )
+ {
+ continue;
+ }
+
+ sim::CollisionVolume* volume = obj->GetCollisionVolume();
+ rAssert( volume != NULL );
+
+ rmt::Vector pos;
+ pos = volume->mPosition;
+
+ rmt::Vector vec2obj;
+ vec2obj.Sub( pos, mypos );
+
+ float distsqr = vec2obj.MagnitudeSqr();
+
+ if( distsqr > 10.0f && distsqr < (lookAhead * lookAhead))
+ {
+ vec2obj.Normalize();
+
+ float dp = vec2obj.DotProduct( myheading );
+
+ // if the object lies within 120 frustrum (60 on either side of heading),
+ // then we want to add it to the potential field
+ if( dp > 0.5f ) //cos60=0.5
+ {
+ radius = 0.0;
+ value = -1.0f;
+ falloff = 1.0f; // the bigger the value, the less influence this potential exerts over distance
+
+ mPotentialField.AddPotential( pos, value, radius, falloff );
+ bCheck = true;
+ }
+ }
+ }
+ }
+
+ // Populate field with vehicles
+ if( mEvadeVehicles )
+ {
+
+ // DUSIT: Must loop through, max, not num vehicles, because active list is sparse
+ //for( int i = 0; i < GetVehicleCentral()->GetNumVehicles(); i++ )
+ for( int i = 0; i < GetVehicleCentral()->GetMaxActiveVehicles(); i++ )
+ {
+ Vehicle* vehicle = GetVehicleCentral()->GetVehicle( i );
+ if( vehicle != NULL && vehicle != GetVehicle() && vehicle != exceptThisOne )
+ {
+ rmt::Vector pos;
+ vehicle->GetPosition( &pos );
+
+ rmt::Vector vec2car;
+ vec2car.Sub( pos, mypos );
+
+ if( vec2car.MagnitudeSqr() < (lookAhead * lookAhead))
+ {
+ vec2car.Normalize();
+
+ float dp = vec2car.DotProduct( myheading );
+
+ if( dp > 0.5f )
+ {
+ for( int wheel = 0; wheel < 4; wheel++ )
+ {
+ if( mPotentialField.AddPotential( vehicle->mSuspensionWorldSpacePoints[ wheel ], -1.0f, 0.0f ))
+ {
+ bCheck = true;
+ }
+ }
+ if( mPotentialField.AddPotential( pos, -1.0f, 0.0f ))
+ {
+ bCheck = true;
+ }
+
+ if( vec2car.MagnitudeSqr() < closestcar )
+ {
+ closestcar = vec2car.MagnitudeSqr();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ mPotentialField.AddPotential( mDestination, 2.0f, 2.0f );
+
+ mSteeringRatio = DEFAULT_STEERING_RATIO;
+
+ if( bCheck )
+ {
+ float aheaddist;
+
+ // DUSIT:
+ // Instead of using a fixed distance, calculate a distance based on
+ // the vehicle's present speed & seconds of look-ahead
+ //float aheaddist = LOOK_AHEAD_CLOSE;
+ //aheaddist = LOOK_AHEAD_CLOSE;
+ aheaddist = (GetVehicle()->GetSpeedKmh() * KPH_2_MPS) * LOOK_AHEAD_CLOSE_SECONDS;
+ if( aheaddist > lookAhead )
+ {
+ aheaddist = lookAhead;
+ }
+ else if( aheaddist < 1.0f )
+ {
+ aheaddist = 1.0f;
+ }
+
+
+ rmt::Vector pos;
+ if( mPotentialField.FindBestPosition( pos, aheaddist ))
+ {
+ pos.y = 0.0f;
+ SetDestination( pos );
+
+ //mState = STATE_CORNER_PREPARE;
+ mSteeringRatio = AVOID_STEERING_RATIO;
+ }
+ }
+}
+*/
+
+void VehicleAI::DoCatchUp( float timeins )
+{
+}
+
+
+void VehicleAI::DoSteering()
+{
+ float steering = 0.0f;
+ float nextsteering = 0.0f;
+ float dist = 0.0f;
+ float nextdist = 0.0f;
+
+ //
+ // Calculates the steering necessary to move towards
+ // both the current dest and the next. Creates
+ // smoother driving.
+ //
+ DriveTowards( &mDestination, dist, steering );
+ DriveTowards( &mNextDestination, nextdist, nextsteering );
+
+ float combinedsteering = mSteeringRatio * steering
+ + ( 1.0f - mSteeringRatio ) * nextsteering;
+
+ if((mState == STATE_REVERSE) || (mState == STATE_STOP))
+ {
+ combinedsteering = rmt::Clamp(combinedsteering * 1.25f, -1.0f, 1.0f);
+ mSteering.SetValue( -combinedsteering );
+ }
+ else
+ {
+ mSteering.SetValue( combinedsteering );
+ }
+}
+
+
+
+void VehicleAI::CheckState( float timeins )
+{
+ // Determine if we should transit to being OUT OF CONTROL
+ static const float MAX_SECONDS_STUNNED = 0.5f;
+ static const float MAX_SECONDS_OUT_OF_CONTROL = 1.5f;
+
+ // steering will be this many times more sensitive when out of control...
+ static const float OUT_OF_CONTROL_STEERING_MODIFIER = 1.5f;
+ static const float NORMALIZED_SWERVE_THRESHOLD = 0.020f;
+
+ mSteeringRatio = DEFAULT_STEERING_RATIO;
+
+ if( mState != STATE_WAITING &&
+ GetVehicle()->mWasHitByVehicle &&
+ //GetVehicle()->mWasHitByVehicleType == VT_USER &&
+ GetVehicle()->mNormalizedMagnitudeOfVehicleHit > NORMALIZED_SWERVE_THRESHOLD )
+ {
+ GetVehicle()->mOutOfControl = true;
+ GetVehicle()->mVehicleState = VS_SLIP;
+ mOutOfControlNormal = GetVehicle()->mSwerveNormal;
+ mSecondsOutOfControl = 0.0f;
+ mState = STATE_OUT_OF_CONTROL;
+ }
+
+ // if this reaches zero or lower, we repopulate the paths because
+ // we have been too far from prescribed segment too long...
+ // UpdateSegment is supposed to check this variable
+ mSecondsLeftToGetBackOnPath -= timeins;
+
+ static const float SECONDS_BEFORE_CORNER = 0.0f;//0.25f;
+ static const float CORNER_COSALPHA = 0.7071067f;//0.7f; //cos45
+ static const float HANDBRAKE_COSALPHA = 0.0f; // cos90
+
+ VehicleAIState newState = mState;
+
+ mBrake.SetValue( 0.0f );
+ mGas.SetValue( 0.0f );
+ mHandBrake.SetValue( 0.0f );
+ mReverse.SetValue( 0.0f );
+
+ //
+ // Calc the vector from my position to the destination
+ //
+ rmt::Vector mypos;
+ rmt::Vector myheading;
+ GetVehicle()->GetPosition( &mypos );
+ GetVehicle()->GetHeading( &myheading );
+ myheading.y = 0.0f;
+ myheading.NormalizeSafe(); // *** SQUARE ROOT! ***
+
+ rmt::Vector vec2dest;
+ vec2dest.Sub( mDestination, mypos );
+ vec2dest.y = 0.0f;
+ vec2dest.Normalize(); // *** SQUARE ROOT! ***
+
+ float dp2heading = vec2dest.DotProduct( myheading );
+
+ //
+ // Calc the vector from the dest to the next dest
+ //
+ rmt::Vector dest2next;
+ dest2next.Sub( mNextDestination, mDestination );
+ dest2next.y = 0.0f;
+ dest2next.Normalize(); // *** SQUARE ROOT! ***
+
+ //
+ // Find the angle between them
+ //
+ float dp2next = dest2next.DotProduct( myheading );
+
+ /***
+ //
+ // Accelerate less as we're cornering
+ //
+ float steer = STEER_HEADING_RATIO * dp2heading
+ + ( 1.0f - STEER_HEADING_RATIO ) * ( 1.0f - rmt::Fabs( mSteering.GetValue() ));
+ ***/
+ float steer = rmt::Fabs(mSteering.GetValue());
+
+ Vehicle* vehicle = GetVehicle();
+ float skidding = vehicle->GetSkidLevel();
+
+ /*
+ float givergas;
+ if( skidding > 0.75f )
+ {
+ givergas = 1.0f;//0.0f;
+ }
+ else
+ {
+ // givergas = 1.0f - (GAS_STEERING_RATIO * steer);
+ givergas = 1.0f;
+ }
+ */
+
+ const float DESIRED_SPEED_BUFFER = 5.0f;
+
+ float givergas = 1.0f; // TODO: Is this a good value?
+ float speedKmh = GetVehicle()->GetSpeedKmh();
+ if( speedKmh > (mDesiredSpeedKmh + DESIRED_SPEED_BUFFER) )
+ {
+ givergas = 0.0f;
+ }
+ else if( speedKmh < (mDesiredSpeedKmh - DESIRED_SPEED_BUFFER) )
+ {
+ givergas = 1.0f;
+ }
+
+
+ static const float MIN_SPEED = 1.0f;
+ switch( mState )
+ {
+ case STATE_WAITING:
+ case STATE_WAITING_FOR_PLAYER:
+ {
+ mHandBrake.SetValue( 1.0f );
+ break;
+ }
+ case STATE_ACCEL:
+ {
+ //rDebugPrintf( "============ ACCELERATING ==============\n" );
+
+ float speed = GetVehicle()->mSpeed;
+
+ if( (speed > STILL_STUCK_SPEED) && (mReverseTime > DEFAULT_REVERSE_TIME))
+ {
+ mReverseTime = DEFAULT_REVERSE_TIME;
+ }
+
+ if( speed <= MIN_SPEED )
+ {
+ //
+ // We're going way too slow, so we might be stuck...
+ // Start a timer...
+ //
+ unsigned int currentTime = radTimeGetMilliseconds();
+ if( mStartStuckTime == 0 )
+ {
+ mStartStuckTime = currentTime;
+ }
+ else if( (currentTime - mStartStuckTime) > mNextStuckTime )
+ {
+ // If chase AI gets stuck, it's screwed anyway, not
+ // doing back off might help it get free a little
+ // faster if it isn't very stuck
+ if(mType == AI_CHASE)
+ {
+ mReverseTime = DEFAULT_REVERSE_TIME + REVERSE_BACKOFF;
+ }
+ else
+ {
+ mReverseTime += REVERSE_BACKOFF;
+ }
+
+ // we've been iteratively reversing too long...
+ // time to just reset...
+ if( mReverseTime > MAX_REVERSE_TIME )
+ {
+ mStartStuckTime = 0;
+ mNextStuckTime = NEXT_STUCK_TIME;
+ mReverseTime = DEFAULT_REVERSE_TIME;
+
+ ////////
+ // see if we should reset on the spot...
+ // don't do this for:
+ // - chase vehicles (they don't do iterative reverse anyway
+ // - destroy objectives
+ // - dump objectives where you need to bump it
+ //
+
+ bool needsReset = true;
+
+ if( GetType() == AI_CHASE )
+ {
+ needsReset = false;
+ }
+ else
+ {
+ Mission* m = GetGameplayManager()->GetCurrentMission();
+ if( m )
+ {
+ MissionStage* ms = m->GetCurrentStage();
+ if( ms )
+ {
+ MissionObjective* mobj = ms->GetObjective();
+ if( mobj )
+ {
+ MissionObjective::ObjectiveTypeEnum type = mobj->GetObjectiveType();
+ if( type == MissionObjective::OBJ_DESTROY )
+ {
+ DestroyObjective* dobj = (DestroyObjective*) mobj;
+ if( dobj->GetTargetVehicle() == GetVehicle() )
+ {
+ needsReset = false;
+ }
+ }
+ else if( type == MissionObjective::OBJ_DUMP )
+ {
+ CollectDumpedObjective* cdobj = (CollectDumpedObjective*) mobj;
+ if( cdobj->IsBumperCars() && cdobj->GetDumpVehicle() )
+ {
+ needsReset = false;
+ }
+ }
+ }
+ }
+ }
+ }
+ if( needsReset )
+ {
+ GetVehicle()->ResetOnSpot( false );
+ }
+ }
+ else
+ {
+ //
+ // We've been stuck long enough to do something about it
+ //
+ //rDebugPrintf( "ACCEL: Stuck longer than mNextStuckTime = %d. Going to REVERSE\n", mNextStuckTime );
+ newState = STATE_REVERSE;
+ mStartStuckTime = currentTime;
+ }
+ break;
+ }
+ }
+ else if( mNextStuckTime == NEXT_STUCK_TIME )
+ {
+ mNextStuckTime = FIRST_STUCK_TIME;
+ }
+ else
+ {
+ // if angle between heading and DestinationToNextdestination
+ // is great enough, we are going to have to take a corner...
+ if(( dp2next < CORNER_COSALPHA ) && ( speed > MIN_SPEED * 2.0f ))
+ {
+ newState = STATE_CORNER_PREPARE;
+
+ mSecondsBeforeCorner = 0.0f; // prepare counter
+ }
+ }
+
+ mGas.SetValue( givergas );
+
+ break;
+ }
+ case STATE_CORNER_PREPARE:
+ {
+ float speed = GetVehicle()->mSpeed;
+
+ // steer to far point for smoother conering, unless evading
+ mSteeringRatio = 0.0f;
+
+ if ( speed > MIN_SPEED )
+ {
+ static const float BRAKE_SPEED = 50.0f;//24.0f;// speed above which to apply brake (strongest)
+ static const float OFF_GAS_SPEED = 40.0f;//21.5f;// speed above which to let off gas (do nothing)
+ static const float POWERSLIDE_HEADING = 0.85f; // cos of angle to try to powerslide at
+ static const float POWERSLIDE_MINSPEED = 6.0f; // minimum speed needed to powerslide
+
+ //rDebugPrintf( "\n =========== CORNERING (%f mps) =============\n", speed );
+
+ // we are getting in danger of not making the turn, powerslide
+ if((dp2heading < POWERSLIDE_HEADING) && (speed > POWERSLIDE_MINSPEED))
+ {
+ mHandBrake.SetValue( 1.0f );
+ mGas.SetValue( 0.0f );
+ break;
+ }
+ if( dp2next > CORNER_COSALPHA )
+ {
+ mGas.SetValue( 1.0f );
+ mSteeringRatio = DEFAULT_STEERING_RATIO;
+ newState = STATE_ACCEL;
+ break;
+ }
+
+ if( speed > BRAKE_SPEED )
+ {
+ //rDebugPrintf( " Using normal brake!\n" );
+ mBrake.SetValue(1.0f);
+ }
+ else if( speed > OFF_GAS_SPEED )
+ {
+ //rDebugPrintf( " Letting off gas\n" );
+ mGas.SetValue(0.0f);
+ }
+ else
+ {
+ //rDebugPrintf( " Speed too low, accelerating!!\n" );
+ mGas.SetValue(givergas);
+ // TODO:
+ // Transit here to Accelerate? Maybe.... or maybe we need
+ // a new criterion for determining when we're out of a cornering
+ // state... hmmm..
+ //newState = STATE_ACCEL;
+ }
+
+ }
+ else
+ {
+ // We're probably stuck at this point, so do something
+ newState = STATE_ACCEL;
+ mSteeringRatio = DEFAULT_STEERING_RATIO;
+ mGas.SetValue( 1.0f );
+ break;
+ }
+ break;
+ }
+ case STATE_REVERSE:
+ {
+ mReverse.SetValue( 1.0f );
+
+ unsigned int currentTime = radTimeGetMilliseconds();
+ if( (currentTime - mStartStuckTime) > mReverseTime )
+ {
+ //rDebugPrintf( "REVERSE: Been reversing for longer than DEFAULT_REVERSE_TIME, going to STOP\n" );
+ newState = STATE_STOP;
+ mNextStuckTime = NEXT_STUCK_TIME;
+ mStartStuckTime = 0;
+ }
+ break;
+ }
+ case STATE_STOP:
+ {
+ // NOTE:
+ // Don't
+ //mBrake.SetValue( 1.0f );
+ mHandBrake.SetValue( 1.0f );
+
+ //float dp = GetVehicle()->mVehicleFacing.DotProduct( GetVehicle()->mVelocityCM );
+
+ float speed = GetVehicle()->mSpeed;
+ if(( speed < 1.0f ))//< 0.15f ))/* && ( dp > 0.0f ))*/
+ {
+ //rDebugPrintf( "STOPPED: speed = %f, which is < 0.15f, so I'll ACCEL\n", speed );
+ newState = STATE_ACCEL;
+ }
+
+ break;
+ }
+ case STATE_STUNNED:
+ {
+ mSecondsStunned += timeins;
+ mHandBrake.SetValue( 1.0f );
+ if( mSecondsStunned >= MAX_SECONDS_STUNNED )
+ {
+ //rDebugPrintf( "STUNNED: Been stunned for %f seconds, so I'll REVERSE\n", mSecondsStunned );
+ mStartStuckTime = radTimeGetMilliseconds();
+ newState = STATE_REVERSE;
+ }
+ break;
+ }
+ case STATE_OUT_OF_CONTROL:
+ {
+ rAssert( GetVehicle()->mOutOfControl );
+ rAssert( GetVehicle()->mVehicleState == VS_SLIP );
+
+ mGas.SetValue( givergas );
+
+ mSecondsOutOfControl += timeins;
+ if( mSecondsOutOfControl >= MAX_SECONDS_OUT_OF_CONTROL )
+ {
+ // if been out of control long enough, transit out...
+ GetVehicle()->mOutOfControl = false;
+ newState = STATE_ACCEL;
+ }
+ else
+ {
+ // do stuff while we're out of control...
+ //GetVehicle()->mVehicleState = VS_SLIP;
+
+ float steering = mSteering.GetValue();
+ steering *= OUT_OF_CONTROL_STEERING_MODIFIER;
+ mSteering.SetValue( steering );
+ }
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ break;
+ }
+ }
+
+ if( newState != mState )
+ {
+ mState = newState;
+ }
+}
+
+//=============================================================================
+// VehicleAI::DriveTowards
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleAI::DriveTowards( rmt::Vector* dest, float &distToTarget,
+ float &steering )
+{
+ // Return a steering value clamped between -1 and 1, analogous to
+ // the steering stick on the game controller. Value of 1 means we'll
+ // be turning the wheel to MaxWheelAngle (tunable parameter in .con file)
+ // to the Right side; -1 is to the left side.
+
+ // Remember: CxB=A, BxA=C, AxC=B
+ //
+ // C
+ // | B
+ // | /
+ // |/
+ // \
+ // \
+ // A
+ steering = 0.0f;
+
+ Vehicle* myVehicle = GetVehicle();
+ rAssert( myVehicle != NULL );
+
+ rmt::Vector vec2dest, myPos;
+ myVehicle->GetPosition( &myPos );
+
+ myPos.y = dest->y; // get rid of y values.. we'll work solely in xz plane
+
+ if( (*dest).Equals( myPos, 0.001f ) )
+ {
+ return;
+ }
+
+ vec2dest.Sub( *dest, myPos );
+
+ distToTarget = vec2dest.Magnitude();
+
+ // If we're not doing evasive manoeuver, we go about business as usual
+ vec2dest.Scale( 1.0f / distToTarget );
+
+ rmt::Vector myHeading;
+ myVehicle->GetHeading( &myHeading );
+ myHeading.y = 0.0f;
+ myHeading.NormalizeSafe();
+ rAssert( rmt::Epsilon( myHeading.MagnitudeSqr(), 1.0f, 0.00001f ) );
+
+ rmt::Vector myUp, myRightSide;
+ /*
+ // find out which way we have to turn by getting the vector
+ // representing our right side & dotting with our vec2dest.
+ myVehicle->GetVUP( &myUp );
+ myRightSide = myVehicle->mVehicleTransverse;
+ */
+ myUp.Set( 0.0f, 1.0f, 0.0f );
+ myRightSide.CrossProduct( myUp, myHeading );
+
+ float rightDot = myRightSide.DotProduct( vec2dest );
+ float turnDir = 0.0f;
+ if( rightDot < 0.0f )
+ {
+ // dest on my left
+ turnDir = -1.0f;
+ }
+ else
+ {
+ // dest on my right or exactly behind me
+ turnDir = 1.0f;
+ }
+
+ // Need to determine the steering required to get to destination.
+ //
+
+
+ // first, find the angle between my heading and my destination vector.
+ // TODO:
+ // The destination vector should be projected onto the plane of my vehicle
+ // as described by my heading and my rightside vector before we figure out
+ // the angle, but we'll ignore this for now because it involves too many
+ // float ops to do projection matrix calcs.
+ // For now, it's ok to clamp down all y values to 0 and assume xz-plane flatness
+
+ /*
+ myHeading.y = 0.0f;
+ myHeading.NormalizeSafe();
+
+ vec2dest.y = 0.0f;
+ vec2dest.NormalizeSafe();
+ */
+
+ float dotprod = myHeading.DotProduct( vec2dest );
+
+ // need to place these checks in to make sure we're not going out of bounds
+ // when we feed the value into ACos
+ if(dotprod > 0.99999f)
+ {
+ dotprod = 0.99999f;
+ }
+ else if( dotprod < -0.9999f )
+ {
+ dotprod = -0.99999f;
+ }
+
+
+ float alphaInRadians = rmt::ACos( dotprod ); //*** ACK! ArcCosine.. unavoidable ***
+
+ float maxWheelTurnInRadians = rmt::DegToRadian(myVehicle->mDesignerParams.mDpMaxWheelTurnAngle);
+ rAssert( maxWheelTurnInRadians > 0.0f );
+
+ steering = alphaInRadians / maxWheelTurnInRadians;
+ if( steering > 1.0f )
+ {
+ steering = 1.0f;
+ }
+ steering *= turnDir;
+
+ rAssert( !rmt::IsNan( steering ) );
+}
+
+void VehicleAI::SetDestination( rmt::Vector& pos )
+{
+ rAssert( !rmt::IsNan(pos.x) && !rmt::IsNan(pos.y) && !rmt::IsNan(pos.z) );
+ mDestination = pos;
+}
+void VehicleAI::SetNextDestination( rmt::Vector& pos )
+{
+ rAssert( !rmt::IsNan(pos.x) && !rmt::IsNan(pos.y) && !rmt::IsNan(pos.z) );
+ mNextDestination = pos;
+}
+
+
+/*
+bool VehicleAI::FindNextRoad( const Road** pRoad, rmt::Vector& nextDestination )
+{
+ rAssert( pRoad != NULL );
+ rAssert( (*pRoad) != NULL );
+
+ const Intersection* currentInt = (*pRoad)->GetDestinationIntersection();
+ rAssert( currentInt != NULL );
+
+ rmt::Vector mypos;
+ GetVehicle()->GetPosition( &mypos );
+ const Road* nextRoad = NULL;
+ const Road* closestRoad = (*pRoad);
+
+ float closestDist = 1000000.0f;
+ rmt::Vector closestPos;
+ rmt::Vector vec2pos;
+ float dist;
+ int segmentIndex;
+
+ for( int i = 0; i < MAX_ROADS; i++ )
+ {
+ nextRoad = currentInt->GetRoadOut( i );
+ if( nextRoad != NULL )
+ {
+ if( RoadManager::FindClosestPointOnRoad( nextRoad, nextDestination,
+ closestPos, dist, segmentIndex ))
+ {
+ if( dist < closestDist )
+ {
+ closestDist = dist;
+ closestRoad = nextRoad;
+ }
+ }
+ }
+ }
+
+ (*pRoad) = closestRoad;
+
+ return true;
+}
+*/
+
+
+
+bool VehicleAI::FindClosestPathElement(
+ rmt::Vector& pos,
+ RoadManager::PathElement& elem,
+ RoadSegment*& seg,
+ float& segT,
+ float& roadT,
+ bool considerShortcuts )
+{
+ elem.elem = NULL;
+ seg = NULL;
+ segT = 0.0f;
+ roadT = 0.0f;
+
+
+ RoadManager* rm = RoadManager::GetInstance();
+ rAssert( rm != NULL );
+
+ const Road* road = NULL;
+ RoadSegment* roadseg = NULL;
+ int segIndex = -1;
+ float in = 0.0f;
+ float lateral = 0.0f;
+
+ // The last argument we pass in forces the find query to
+ // IGNORE shortcut roads
+ rm->FindRoad( pos, &road, &roadseg, segIndex, in, lateral, considerShortcuts );
+ if( road == NULL )
+ {
+ // so if we are not on a road, see if we're in an intersection
+ Intersection* startInt = rm->FindIntersection( pos );
+ if( startInt == NULL )
+ {
+ return false;
+ }
+ else
+ {
+ elem.type = RoadManager::ET_INTERSECTION;
+ elem.elem = startInt;
+ }
+ }
+ else
+ {
+ rAssert( roadseg != NULL );
+ rAssert( roadseg->GetRoad() == road );
+ rAssert( 0 <= segIndex && segIndex < (int)(road->GetNumRoadSegments()) );
+ rAssert( roadseg->GetSegmentIndex() == (unsigned int) segIndex );
+ //rAssert( !road->GetShortCut() );
+
+ seg = (RoadSegment*) roadseg;
+
+ segT = RoadManager::DetermineSegmentT( pos, seg );
+ roadT = RoadManager::DetermineRoadT( seg, segT );
+
+ elem.type = RoadManager::ET_NORMALROAD;
+ elem.elem = (Road*)road;
+
+ }
+ return true;
+
+}
+
+void VehicleAI::GetLastPathInfo(
+ RoadManager::PathElement& elem,
+ RoadSegment*& seg,
+ float& segT,
+ float& roadT )
+{
+ elem = mLastPathElement;
+ seg = (RoadSegment*) mLastRoadSegment;
+ segT = mLastRoadSegmentT;
+ roadT = mLastRoadT;
+}
+
+void VehicleAI::GetRacePathInfo(
+ RoadManager::PathElement& elem,
+ RoadSegment*& seg,
+ float& segT,
+ float& roadT )
+{
+ elem = mRacePathElement;
+ seg = (RoadSegment*) mRaceRoadSegment;
+ segT = mRaceRoadSegmentT;
+ roadT = mRaceRoadT;
+}
+
diff --git a/game/code/ai/vehicle/vehicleai.h b/game/code/ai/vehicle/vehicleai.h
new file mode 100644
index 0000000..47f314e
--- /dev/null
+++ b/game/code/ai/vehicle/vehicleai.h
@@ -0,0 +1,392 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehicleai.h
+//
+// Description: Blahblahblah
+//
+// History: 27/06/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef VEHICLEAI_H
+#define VEHICLEAI_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <roads/roadmanager.h>
+#include <roads/roadsegment.h>
+#include <radmath/radmath.hpp>
+#include <ai/vehicle/potentialfield.h>
+#include <presentation/gui/utility/hudmap.h>
+#include <worldsim/redbrick/vehiclecontroller/aivehiclecontroller.h>
+
+//========================================
+// Forward References
+//========================================
+
+class Intersection;
+class Road;
+
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class VehicleAI : public AiVehicleController,
+ public IHudMapIconLocator
+{
+public:
+ enum VehicleAITypeEnum
+ {
+ AI_WAYPOINT,
+ AI_CHASE,
+ NUM_AI_TYPES
+ };
+ enum VehicleAIState
+ {
+ STATE_WAITING,
+ STATE_WAITING_FOR_PLAYER,
+ STATE_ACCEL,
+ STATE_BRAKE,
+ STATE_CORNER_PREPARE,
+ STATE_REVERSE,
+ STATE_STOP,
+ STATE_EVADE,
+ STATE_LIMBO,
+ STATE_STUNNED,
+ STATE_OUT_OF_CONTROL,
+ NUM_STATES
+ };
+
+ static const int DEFAULT_MIN_SHORTCUT_SKILL;
+ static const int DEFAULT_MAX_SHORTCUT_SKILL;
+
+ // TUNABLE catch-up values
+ //static const float CATCHUP_MAX_SPEED_MOD;
+ static const float CATCHUP_NORMAL_DRIVING_PERCENTAGE_OF_TOPSPEED;
+ static const int CATCHUP_MAX_SHORTCUTSKILL_MOD;
+
+
+ VehicleAI(
+ Vehicle* pVehicle,
+ VehicleAITypeEnum type,
+ bool enableSegmentOptimization=true,
+ int minShortcutSkill=DEFAULT_MIN_SHORTCUT_SKILL,
+ int maxShortcutSkill=DEFAULT_MAX_SHORTCUT_SKILL,
+ bool useMultiplier=true );
+
+ virtual ~VehicleAI();
+
+ //
+ // Call Init once at the beginning of the mission
+ // and Finalize once at the end. Reset when you need to just reset states
+ //
+ virtual void Initialize();
+ virtual void Finalize();
+ virtual void Reset();
+
+ void ResetControllerValues();
+
+ void SetMinShortcutSkill( int skill );
+ int GetMinShortcutSkill();
+ void SetMaxShortcutSkill( int skill );
+ int GetMaxShortcutSkill();
+
+ int GetShortcutSkillMod();
+
+ void SetActive( bool bIsActive );
+
+ //
+ // Call this bad mofo every frame
+ //
+ virtual void Update( float timeins );
+
+ VehicleAITypeEnum GetType();
+
+ virtual void GetPosition( rmt::Vector* currentLoc );
+ virtual void GetHeading( rmt::Vector* heading );
+ virtual void EnterLimbo ();
+ virtual void ExitLimbo ();
+ VehicleAIState GetState();
+
+ int GetHUDIndex() const { return mHudIndex; };
+
+ void GetDestination( rmt::Vector& pos );
+ void GetNextDestination( rmt::Vector& pos );
+
+ static bool FindClosestPathElement(
+ rmt::Vector& pos,
+ RoadManager::PathElement& elem,
+ RoadSegment*& seg,
+ float& segT,
+ float& roadT,
+ bool considerShortcuts );
+
+ void GetLastPathInfo(
+ RoadManager::PathElement& elem,
+ RoadSegment*& seg,
+ float& segT,
+ float& roadT );
+
+ void GetRacePathInfo(
+ RoadManager::PathElement& elem,
+ RoadSegment*& seg,
+ float& segT,
+ float& roadT );
+
+ float GetDesiredSpeedKmh();
+
+ void SetUseMultiplier( bool use );
+
+
+ struct RaceCatchupParams
+ {
+ float DistMaxCatchup; // dist at which catchup is 1
+ float FractionPlayerSpeedMinCatchup; // fraction of player vehicle's speed to use as target speed when catchup is -1
+ float FractionPlayerSpeedMidCatchup; // fraction of player vehicle's speed to use as target speed when catchup is 0
+ float FractionPlayerSpeedMaxCatchup; // fraction of player vehicle's speed to use as target speed when catchup is 1
+ };
+ struct EvadeCatchupParams
+ {
+ float DistPlayerTooNear; // dist at which catchup is 1
+ float DistPlayerFarEnough; // dist at which catchup is 0
+ };
+ struct TargetCatchupParams
+ {
+ float DistPlayerNearEnough; // dist at which catchup is 0
+ float DistPlayerTooFar; // dist at which catchup is -1
+ };
+ struct CatchupParams
+ {
+ RaceCatchupParams Race;
+ EvadeCatchupParams Evade;
+ TargetCatchupParams Target;
+ };
+
+ void SetRaceCatchupParams( const RaceCatchupParams& raceParams );
+ void SetEvadeCatchupParams( const EvadeCatchupParams& evadeParams );
+ void SetTargetCatchupParams( const TargetCatchupParams& targetParams );
+
+
+
+public:
+ class Segment
+ {
+ public: // MEMBERS
+ rmt::Vector mStart;
+ rmt::Vector mEnd;
+ float mLength;
+ RoadSegment* mpSegment;
+ int mType; // 0 = normal roadsegment,
+ // 1 = first tweening segment,
+ // 2 = second tweening segment
+ public: // METHODS
+
+ void InitZero()
+ {
+ mStart.Set( 0.0f, 0.0f, 0.0f );
+ mEnd.Set( 0.0f, 0.0f, 0.0f );
+ mLength = 0.0f;
+ mpSegment = NULL;
+ mType = 0;
+ }
+
+ Segment()
+ {
+ InitZero();
+ }
+
+ Segment& operator=( const Segment& src )
+ {
+ mStart = src.mStart;
+ mEnd = src.mEnd;
+ mLength = src.mLength;
+ mpSegment = src.mpSegment;
+ mType = src.mType;
+ return (*this);
+ }
+
+ void SelfVerify()
+ {
+ #ifdef RAD_DEBUG
+ rAssert( !rmt::IsNan(mStart.x) && !rmt::IsNan(mStart.y) && !rmt::IsNan(mStart.y) );
+ rAssert( !rmt::IsNan(mEnd.x) && !rmt::IsNan(mEnd.y) && !rmt::IsNan(mEnd.y) );
+ rAssert( !rmt::IsNan(mLength) );
+ rAssert( !rmt::Epsilon( mLength, 0.0f, 0.001f ) );
+ rAssert( mpSegment != NULL );
+ rAssert( mType == 0 || mType == 1 || mType == 2 );
+ #endif
+ }
+
+ };
+
+ // NOTE:
+ // This array needs to be large enough to account for at least 2 roads
+ // for optimization purposes..
+ static const int MAX_SEGMENTS = 20;
+ int mNumSegments;
+ Segment mSegments[ MAX_SEGMENTS ];
+
+
+protected:
+
+
+ void SetState( VehicleAIState state );
+
+ void DriveTowards( rmt::Vector* dest, float &distToTarget,
+ float& steering );
+
+ void SetDestination( rmt::Vector& pos );
+ void SetNextDestination( rmt::Vector& pos );
+
+ void CheckState( float timeins );
+ virtual void DoCatchUp( float timeins );
+ void DoSteering();
+ void FollowRoad();
+ void EvadeTraffic( Vehicle* exceptThisOne );
+
+ // TODO:
+ // Remove this after the new pathfinding stuff goes in (no need for it anymore)
+ //virtual bool DetermineNextRoad( const Road** pRoad ) = 0;
+ //bool FindNextRoad( const Road** pRoad, rmt::Vector& nextDestination );
+
+ virtual bool MustRepopulateSegments();
+ virtual bool TestReachedTarget( const rmt::Vector& start, const rmt::Vector& end ) = 0;
+
+ void UpdateSegments();
+ void FindClosestSegment( const rmt::Vector& pos, int& closestIndex, float& closestDistSqr, rmt::Vector& closestPt );
+ void ResetSegments();
+ void ShiftSegments( int numShifts, int first=0 );
+ void FillSegments();
+ void GetPosAheadAlongRoad( float t, float lookAheadDist, int i, rmt::Vector* lookAheadPos );
+
+ int DetermineShortcutSkill();
+
+ virtual void UpdateSelf();
+ virtual void GetClosestPathElementToTarget(
+ rmt::Vector& targetPos,
+ RoadManager::PathElement& elem,
+ RoadSegment*& seg,
+ float& segT,
+ float& roadT ) = 0;
+
+ void FillPathElements();
+
+ void ResetCatchUpParams();
+
+
+protected:
+ float mSecondsStunned;
+ float mSecondsOutOfControl;
+ rmt::Vector mOutOfControlNormal;
+
+ SwapArray<RoadManager::PathElement> mPathElements;
+ int mCurrPathElement;
+
+ // path info stuff
+ RoadManager::PathElement mLastPathElement;
+ RoadSegment* mLastRoadSegment; // the last (or current) road we're on
+ float mLastRoadSegmentT;
+ float mLastRoadT;
+
+ // race position stuff
+ RoadManager::PathElement mRacePathElement;
+ RoadSegment* mRaceRoadSegment;
+ float mRaceRoadSegmentT;
+ float mRaceRoadT;
+
+
+ // catch-up logic stuff
+ int mShortcutSkillMod;
+ float mDesiredSpeedKmh;
+ float mSecondsSinceLastDoCatchUp;
+
+ CatchupParams mCatchupParams; // persistent catch-up tunables
+
+private:
+
+ //Prevent wasteful constructor creation.
+ VehicleAI( const VehicleAI& vehicleai );
+ VehicleAI& operator=( const VehicleAI& vehicleai );
+
+ virtual int RegisterHudMapIcon() = 0;
+
+ rmt::Vector mDestination;
+ rmt::Vector mNextDestination;
+
+ unsigned int mStartStuckTime;
+ unsigned int mNextStuckTime;
+
+ float mSteeringRatio;
+
+ VehicleAIState mState;
+ VehicleAIState mLimboPushedState;
+
+ PotentialField mPotentialField;
+
+ VehicleAITypeEnum mType;
+
+ int mHudIndex;
+
+ float mSecondsBeforeCorner;
+
+ int mRenderHandle;
+
+ int mMinShortcutSkill;
+ int mMaxShortcutSkill;
+
+ float mSecondsLeftToGetBackOnPath;
+ float mReverseTime;
+
+ bool mEvadeVehicles : 1;
+ bool mEvadeStatics : 1;
+ bool mEvading : 1;
+ bool mEnableSegmentOptimization : 1;
+
+ // to use AGAINST_TRAFFIC_COST_MULTIPLIER in pathfinding or to not use it...
+ // if we use it, we pretty much guarantee we won't drive on the wrong side
+ // of the road (good for avoiding traffic). There are some cases where we
+ // may not want to use it, such as for ChaseAI, or for when it's a street race.
+ // We should expose the ability to turn it on/off at will...
+ bool mUseMultiplier : 1;
+};
+
+inline float VehicleAI::GetDesiredSpeedKmh()
+{
+ return mDesiredSpeedKmh;
+}
+
+inline VehicleAI::VehicleAIState VehicleAI::GetState()
+{
+ return mState;
+}
+
+inline void VehicleAI::SetState( VehicleAIState state )
+{
+ mState = state;
+}
+
+inline void VehicleAI::GetDestination( rmt::Vector& pos )
+{
+ pos = mDestination;
+}
+inline void VehicleAI::GetNextDestination( rmt::Vector& pos )
+{
+ pos = mNextDestination;
+}
+
+inline VehicleAI::VehicleAITypeEnum VehicleAI::GetType()
+{
+ return mType;
+}
+inline void VehicleAI::SetUseMultiplier( bool use )
+{
+ mUseMultiplier = use;
+}
+
+#endif //VEHICLEAI_H
diff --git a/game/code/ai/vehicle/vehicleairender.cpp b/game/code/ai/vehicle/vehicleairender.cpp
new file mode 100644
index 0000000..4c4d92e
--- /dev/null
+++ b/game/code/ai/vehicle/vehicleairender.cpp
@@ -0,0 +1,563 @@
+
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehicleairender.h
+//
+// Description: Blahblahblah
+//
+// History: 04/02/2003 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+#include <raddebugwatch.hpp>
+
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/renderlayer.h>
+#include <ai/vehicle/vehicleairender.h>
+#include <ai/vehicle/vehicleai.h>
+#include <ai/vehicle/trafficai.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/trafficlocomotion.h>
+
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <roads/roadmanager.h>
+
+#include <ai/vehicle/vehicleai.h>
+
+#include <p3d/debugdraw.hpp>
+
+#include <memory/srrmemory.h>
+
+static int rendercounter = 1;
+VehicleAIRender* VehicleAIRender::spInstance = NULL;
+
+VehicleAIRender* VehicleAIRender::GetVehicleAIRender()
+{
+ if( spInstance == NULL )
+ {
+ spInstance = new VehicleAIRender();
+ }
+ return spInstance;
+}
+
+void VehicleAIRender::Destroy()
+{
+ if( spInstance != NULL )
+ {
+ delete spInstance;
+ spInstance = NULL;
+ }
+}
+
+
+VehicleAIRender::VehicleAIRender( )
+{
+ rAssert( MAX_REGISTRATIONS <= DListArray::MAX_ELEMS );
+
+ mAIShader = new tShader("simple");
+ mAIShader->AddRef();
+
+ mFreeSlots.Clear();
+ mRegistered.Clear();
+
+ for( int i=0; i<MAX_REGISTRATIONS; i++ )
+ {
+ mAIs[i].ai = NULL;
+ mAIs[i].index = mFreeSlots.AddLast( &(mAIs[i]) );
+ mAIs[i].show = false;
+
+ rAssert( mAIs[i].index != -1 );
+ }
+
+ radDbgWatchAddFunction( "Drop Path Point", &DropPathPoint, NULL, "Vehicle AI Debug\\Path Finding");
+}
+
+VehicleAIRender::~VehicleAIRender()
+{
+
+ for( int i=0; i<MAX_REGISTRATIONS; i++ )
+ {
+ if( mAIs[i].ai != NULL )
+ {
+ UnregisterAI( mAIs[i].index );
+ }
+ }
+ if( mAIShader != NULL )
+ {
+ mAIShader->ReleaseVerified();
+ mAIShader = NULL;
+ }
+
+ radDbgWatchDelete( &DropPathPoint );
+}
+
+int VehicleAIRender::RegisterAI( void* ai, int type )
+{
+ if( ai == NULL )
+ {
+ return -1;
+ }
+
+ // make sure it's not already in the list
+ for( int i=0; i<MAX_REGISTRATIONS; i++ )
+ {
+ if( mAIs[i].ai == ai )
+ {
+ return mAIs[i].index;
+ }
+ }
+
+ // grab a free AI slot & set its values
+ RegisteredAI* ra = (RegisteredAI*) mFreeSlots.GetLast();
+ if( ra == NULL )
+ {
+ return -1;
+ }
+ ra->ai = ai;
+ ra->show = false;
+ ra->type = type;
+
+ // remove from free list (should succeed cuz we just grabbed
+ // it from the free list!)
+ bool succeeded = mFreeSlots.Remove( ra->index );
+ rAssert( succeeded );
+
+ // add to registered list (should succeed because its
+ // existence in the free list implies that the registered list
+ // isn't full.
+ ra->index = mRegistered.AddLast( ra );
+ rAssert( 0 <= ra->index && ra->index < MAX_REGISTRATIONS );
+
+ // add the debug watch
+ rendercounter = rendercounter + 1;
+ char name[64];
+ if( ra->type == TYPE_TRAFFIC_AI )
+ {
+ sprintf( name, "Vehicle AI Debug\\traffic (%d)", rendercounter );
+ }
+ else if( ra->type == TYPE_VEHICLE_AI )
+ {
+ sprintf( name, "Vehicle AI Debug\\%s (%d)", ((VehicleAI*)(ra->ai))->GetVehicle()->GetNameDangerous(), rendercounter);
+ }
+ else
+ {
+ rAssert( false );
+ }
+ radDbgWatchAddBoolean( &(ra->show), "Display", name );
+
+
+ return ra->index;
+}
+
+void VehicleAIRender::UnregisterAI( int handle )
+{
+ rAssert( 0 <= handle && handle < MAX_REGISTRATIONS );
+ if( handle < 0 || handle >= MAX_REGISTRATIONS )
+ {
+ return;
+ }
+
+ RegisteredAI* ra = (RegisteredAI*) mRegistered.GetDataAt(handle);
+ if( ra == NULL )
+ {
+ return;
+ }
+
+ ra->ai = NULL;
+ radDbgWatchDelete( &(ra->show) );
+ ra->show = false;
+
+ bool succeeded = mRegistered.Remove( ra->index );
+ rAssert( succeeded );
+
+ ra->index = mFreeSlots.AddLast( ra );
+ rAssert( ra->index != -1 );
+
+}
+
+static bool sWhichPathEndpoint = 0;
+static rmt::Vector sPathEndPoints[2];
+static unsigned sNumPathPoints = 0;
+static rmt::Vector sPathPoints[64];
+
+void VehicleAIRender::Display()
+{
+ tColour blue, red, green, black, magenta, cyan;
+ blue.Set( 0, 0, 255 );
+ red.Set( 255, 0, 0 );
+ green.Set( 0, 255, 0 );
+ black.Set( 0, 0, 0 );
+ cyan.Set( 0, 255, 255 );
+ magenta.Set( 255, 0, 255 );
+
+ pddiPrimStream* stream = NULL;
+
+ int i = mRegistered.GetHead();
+ while( 0 <= i && i < DListArray::MAX_ELEMS )
+ {
+ RegisteredAI* ra = (RegisteredAI*) mRegistered.GetDataAt(i);
+ rAssert( ra != NULL );
+
+ if( !ra->show )
+ {
+ i = mRegistered.GetNextOf(i);
+ continue;
+ }
+ if( ra->ai == NULL )
+ {
+ i = mRegistered.GetNextOf(i);
+ continue;
+ }
+
+ if( ra->type == TYPE_VEHICLE_AI )
+ {
+ VehicleAI* ai = (VehicleAI*) ra->ai;
+ // get vehicle position.
+ rmt::Vector pos;
+ ai->GetPosition( &pos );
+
+ rmt::Vector dest, nextDest;
+ ai->GetDestination( dest );
+ ai->GetNextDestination( nextDest );
+ /*
+ dest.y = pos.y;
+ nextDest.y = pos.y;
+ */
+
+ // draw lines to first and next destinations
+ stream = p3d::pddi->BeginPrims( mAIShader->GetShader(), PDDI_PRIM_LINES, PDDI_V_C, 2 );
+ if( stream )
+ {
+ stream->Vertex( ((pddiVector*)(&(pos))), blue );
+ stream->Vertex( ((pddiVector*)(&(dest))), blue );
+ }
+ p3d::pddi->EndPrims( stream );
+
+ stream = p3d::pddi->BeginPrims( mAIShader->GetShader(), PDDI_PRIM_LINES, PDDI_V_C, 2 );
+ if( stream )
+ {
+ stream->Vertex( ((pddiVector*)(&(pos))), red );
+ stream->Vertex( ((pddiVector*)(&(nextDest))), red );
+ }
+ p3d::pddi->EndPrims( stream );
+
+
+ RoadManager::PathElement lastElem;
+ RoadSegment* lastSeg;
+ float lastSegT;
+ float lastRoadT;
+
+ ai->GetLastPathInfo( lastElem, lastSeg, lastSegT, lastRoadT );
+
+ rmt::Vector lastElemPos;
+
+ if( lastElem.elem )
+ {
+ if( lastElem.type == RoadManager::ET_INTERSECTION )
+ {
+ ((Intersection*)lastElem.elem)->GetLocation( lastElemPos );
+
+#ifndef RAD_RELEASE
+ P3DDrawSphere(1.0f, lastElemPos, *mAIShader, magenta);
+#endif
+ }
+ else
+ {
+ Road* lastRoad = (Road*) lastElem.elem;
+
+ rmt::Vector vec0, vec1, vec2, vec3, start, end;
+ lastSeg->GetCorner(0, vec0);
+ lastSeg->GetCorner(1, vec1);
+ lastSeg->GetCorner(2, vec2);
+ lastSeg->GetCorner(3, vec3);
+
+ start = (vec0+vec3)*0.5f;
+ end = (vec1+vec2)*0.5f;
+
+ rmt::Vector segDir = end - start;
+
+ lastElemPos = start + segDir * lastSegT;
+
+
+ tColour useThisColor = (lastRoad->GetShortCut())? cyan : magenta;
+ stream = p3d::pddi->BeginPrims( mAIShader->GetShader(), PDDI_PRIM_LINESTRIP, PDDI_V_C, 4 );
+ if( stream )
+ {
+ stream->Vertex( ((pddiVector*)(&(vec0))), useThisColor );
+ stream->Vertex( ((pddiVector*)(&(vec1))), useThisColor );
+ stream->Vertex( ((pddiVector*)(&(vec2))), useThisColor );
+ stream->Vertex( ((pddiVector*)(&(vec3))), useThisColor );
+ }
+ p3d::pddi->EndPrims( stream );
+ }
+ }
+
+ // draw line from us to last element
+ stream = p3d::pddi->BeginPrims( mAIShader->GetShader(), PDDI_PRIM_LINES, PDDI_V_C, 2 );
+ if( stream )
+ {
+ stream->Vertex( ((pddiVector*)(&(pos))), black );
+ stream->Vertex( ((pddiVector*)(&(lastElemPos))), black );
+ }
+ p3d::pddi->EndPrims( stream );
+
+
+
+ // display the segments array!
+ int numSegs = ai->mNumSegments;
+
+ if( numSegs > 0 )
+ {
+ VehicleAI::Segment* segs = ai->mSegments;
+ stream = p3d::pddi->BeginPrims( mAIShader->GetShader(), PDDI_PRIM_LINESTRIP, PDDI_V_C, numSegs+1 );
+ if( stream )
+ {
+ stream->Vertex( ((pddiVector*)(&(segs[0].mStart))), green );
+ for( int j=0; j<numSegs; j++ )
+ {
+ stream->Vertex( ((pddiVector*)(&(segs[j].mEnd))), green );
+ }
+ }
+ p3d::pddi->EndPrims( stream );
+ }
+
+
+
+ // output states by projecting text from x-y coordinates onto screen coordinates
+ char speed[128];
+ char params[128];
+ char state[64];
+ char shortcut[128];
+ sprintf( speed, "%.1f (%.1f) km/h", ai->GetVehicle()->GetSpeedKmh(), ai->GetDesiredSpeedKmh() );
+ sprintf( params, "g : %.1f b : %.1f e : %.1f", ai->GetGas(), ai->GetBrake(), ai->GetHandBrake() );
+ sprintf( state, "state : %d", ai->GetState() );
+ sprintf( shortcut, "SC MIN : %d MAX : %d MOD : %d",
+ ai->GetMinShortcutSkill(), ai->GetMaxShortcutSkill(), ai->GetShortcutSkillMod() );
+
+ rmt::Vector pos2 = ai->GetVehicle()->GetPosition();
+ pos2.y += 3;
+
+ p3d::context->WorldToDevice(pos2, &pos2);
+
+ p3d::pddi->DrawString(speed, (int)pos2.x+1, (int)pos2.y+1, tColour(0,0,0));
+ p3d::pddi->DrawString(speed, (int)pos2.x, (int)pos2.y, tColour(255,255,255));
+ p3d::pddi->DrawString(params, (int)pos2.x+1, (int)pos2.y+16, tColour(0,0,0));
+ p3d::pddi->DrawString(params, (int)pos2.x, (int)pos2.y+15, tColour(255,255,255));
+ p3d::pddi->DrawString(state, (int)pos2.x+1, (int)pos2.y+31, tColour(0,0,0));
+ p3d::pddi->DrawString(state, (int)pos2.x, (int)pos2.y+30, tColour(255,255,255));
+ p3d::pddi->DrawString(shortcut, (int)pos2.x+1, (int)pos2.y+46, tColour(0,0,0));
+ p3d::pddi->DrawString(shortcut, (int)pos2.x, (int)pos2.y+45, tColour(255,255,255));
+
+ }
+ else if( ra->type == TYPE_TRAFFIC_AI )
+ {
+ TrafficAI* ai = (TrafficAI*) ra->ai;
+ if( ai->GetVehicle() == NULL )
+ {
+ i = mRegistered.GetNextOf(i);
+ continue;
+ }
+
+ if( !ai->mIsActive )
+ {
+ i = mRegistered.GetNextOf(i);
+ continue;
+ }
+
+ rmt::Vector aiPos;
+ ai->GetVehicle()->GetPosition( &aiPos );
+
+ // draw the lookahead position
+ stream = p3d::pddi->BeginPrims( mAIShader->GetShader(), PDDI_PRIM_LINES, PDDI_V_C, 2 );
+ if( stream )
+ {
+ stream->Vertex( ((pddiVector*)(&(aiPos))), blue );
+ stream->Vertex( ((pddiVector*)(&(ai->mLookAheadPt))), blue );
+ }
+ p3d::pddi->EndPrims( stream );
+
+
+ TrafficLocomotion* traffLoco = ai->GetVehicle()->mTrafficLocomotion;
+ rAssert( traffLoco );
+
+ // draw the segment position
+ rmt::Vector segmentPos( 0.0f, 0.0f, 0.0f );
+ rmt::Vector segmentDir( 0.0f, 1.0f, 0.0f );
+ if( traffLoco->IsInIntersection() ||
+ ai->GetState() == TrafficAI::SWERVING ||
+ ai->GetState() == TrafficAI::LANE_CHANGING )
+ {
+ rmt::Vector* ways;
+ int nWays;
+ int currWay;
+
+ traffLoco->GetSplineCurve( ways, nWays, currWay );
+
+ if( currWay < (nWays-1) )
+ {
+ segmentDir.Sub( ways[ currWay+1 ], ways[ currWay ] );
+ segmentPos = ways[ currWay ] + segmentDir * traffLoco->mCurrPathLocation;
+ }
+ }
+ else
+ {
+ ai->GetSegment()->GetLaneLocation( ai->GetLanePosition(),
+ ai->GetLaneIndex(), segmentPos, segmentDir );
+ }
+ segmentPos.y += 0.5f;
+ #ifndef RAD_RELEASE
+ P3DDrawSphere(0.5f, segmentPos, *mAIShader, tColour(0,255,255));
+ #endif
+
+ if( ai->GetState() == TrafficAI::LANE_CHANGING ||
+ ai->GetState() == TrafficAI::SPLINING )
+ {
+ // if lane changing, render the lane change path
+ TrafficLocomotion* tl = ai->GetVehicle()->mTrafficLocomotion;
+ rAssert( tl );
+
+ rmt::Vector* ways;
+ int nWays, currWay;
+ tl->GetSplineCurve( ways, nWays, currWay );
+
+ if( nWays > 0 )
+ {
+ stream = p3d::pddi->BeginPrims( mAIShader->GetShader(), PDDI_PRIM_LINESTRIP, PDDI_V_C, nWays );
+ if( stream )
+ {
+ for( int j=0; j<nWays; j++ )
+ {
+ if( j == currWay || ((j-1) == currWay) )
+ {
+ stream->Vertex( ((pddiVector*)(&(ways[j]))), red );
+ }
+ else
+ {
+ stream->Vertex( ((pddiVector*)(&(ways[j]))), green );
+ }
+ }
+ }
+ p3d::pddi->EndPrims( stream );
+ }
+ }
+
+ // output states by projecting text from x-y coordinates onto screen coordinates
+ char speed[128];
+ char state[64];
+ sprintf( speed, "%.1f (%.1f) km/h", ai->GetVehicle()->GetSpeedKmh(), ai->GetAISpeed()*3.6f );
+ sprintf( state, "state : %d", ai->GetState() );
+
+ rmt::Vector pos2 = ai->GetVehicle()->GetPosition();
+ pos2.y += 3;
+
+ p3d::context->WorldToDevice(pos2, &pos2);
+
+ p3d::pddi->DrawString(speed, (int)pos2.x+1, (int)pos2.y+1, tColour(0,0,0));
+ p3d::pddi->DrawString(speed, (int)pos2.x, (int)pos2.y, tColour(255,255,255));
+ p3d::pddi->DrawString(state, (int)pos2.x+1, (int)pos2.y+31, tColour(0,0,0));
+ p3d::pddi->DrawString(state, (int)pos2.x, (int)pos2.y+30, tColour(255,255,255));
+
+ }
+ i = mRegistered.GetNextOf(i);
+ }
+
+ #ifndef RAD_RELEASE
+ if(sNumPathPoints)
+ {
+ for(unsigned i = 0; i < sNumPathPoints - 1; i++)
+ {
+ P3DDrawSphere(1.0f, sPathPoints[i], *mAIShader, tColour(0,255,255));
+ P3DDrawCylinder( 0.3f, sPathPoints[i], sPathPoints[i+1], *mAIShader, tColour(255,0,255));
+ }
+ P3DDrawSphere(1.0f, sPathPoints[sNumPathPoints - 1], *mAIShader, tColour(0,255,255));
+ }
+ #endif
+}
+
+void VehicleAIRender::DropPathPoint(void*)
+{
+ /*
+ GetCharacterManager()->GetCharacter(0)->GetPosition(sPathPoints[sNumPathPoints++]);
+ */
+
+ /*
+ GetCharacterManager()->GetCharacter(0)->GetPosition(sPathEndPoints[sWhichPathEndpoint]);
+
+ if(sWhichPathEndpoint == 0)
+ {
+ sNumPathPoints = 0;
+ sWhichPathEndpoint = 1;
+ }
+ else
+ {
+ sWhichPathEndpoint = 0;
+ sNumPathPoints = 2;
+ sPathPoints[0] = sPathEndPoints[0];
+ sPathPoints[1] = sPathEndPoints[1];
+ sPathPoints[0].y += 3.0f;
+ sPathPoints[1].y += 3.0f;
+ }*/
+
+ GetCharacterManager()->GetCharacter(0)->GetPosition(sPathEndPoints[sWhichPathEndpoint]);
+
+ if(sWhichPathEndpoint == 0)
+ {
+ sNumPathPoints = 0;
+ sWhichPathEndpoint = 1;
+ }
+ else
+ {
+ sWhichPathEndpoint = 0;
+
+
+ RoadManager::PathElement sourceElem;
+ RoadManager::PathElement targetElem;
+ float sourceT = 0.0f;
+ float targetT = 0.0f;
+ float sourceRoadT = 0.0f;
+ float targetRoadT = 0.0f;
+ RoadSegment* sourceSeg = NULL;
+ RoadSegment* targetSeg = NULL;
+
+ SwapArray<RoadManager::PathElement> path;
+ path.Reserve(64);
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+ path.Allocate();
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ bool ok = true;
+ ok &= VehicleAI::FindClosestPathElement( sPathEndPoints[0], sourceElem, sourceSeg, sourceT, sourceRoadT, true );
+ ok &= VehicleAI::FindClosestPathElement( sPathEndPoints[1], targetElem, targetSeg, targetT, targetRoadT, true );
+
+ assert(ok);
+
+ RoadManager::GetInstance()->FindPathElementsBetween(
+ false,
+ sourceElem, sourceRoadT, sPathEndPoints[0],
+ targetElem, targetRoadT, sPathEndPoints[1],
+ path);
+
+ for(int i = 0; i < path.mUseSize; i++)
+ {
+ switch(path[i].type)
+ {
+ case RoadManager::ET_INTERSECTION :
+ {
+ ((Intersection*)(path[i].elem))->GetLocation(sPathPoints[sNumPathPoints]);
+ sPathPoints[sNumPathPoints++].y += 3.0f;
+ }
+ break;
+ case RoadManager::ET_NORMALROAD :
+ {
+ }
+ break;
+ }
+ }
+
+ }
+
+
+}
diff --git a/game/code/ai/vehicle/vehicleairender.h b/game/code/ai/vehicle/vehicleairender.h
new file mode 100644
index 0000000..4a00b50
--- /dev/null
+++ b/game/code/ai/vehicle/vehicleairender.h
@@ -0,0 +1,64 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehicleairender.h
+//
+// Description: Blahblahblah
+//
+// History: 04/02/2003 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#ifndef VEHICLEAIRENDER_H
+#define VEHICLEAIRENDER_H
+
+#include <roads/geometry.h>
+
+#include <p3d/drawable.hpp>
+
+class VehicleAIRender
+{
+public:
+ static VehicleAIRender* GetVehicleAIRender();
+ static void Destroy();
+
+ enum
+ {
+ TYPE_VEHICLE_AI,
+ TYPE_TRAFFIC_AI,
+ NUM_TYPES
+ };
+
+ void Display();
+ int RegisterAI( void* ai, int type=TYPE_VEHICLE_AI );
+ void UnregisterAI( int handle );
+
+private:
+ static VehicleAIRender* spInstance;
+ static const int MAX_REGISTRATIONS = 20;
+
+ struct RegisteredAI
+ {
+ void* ai;
+ int index;
+ int type;
+ bool show; //not a bitfield because of watcher
+ };
+ RegisteredAI mAIs[MAX_REGISTRATIONS];
+ DListArray mFreeSlots;
+ DListArray mRegistered;
+
+ tShader* mAIShader;
+
+ static void DropPathPoint(void*);
+
+ // Singletons don't expose these...
+ VehicleAIRender();
+ virtual ~VehicleAIRender();
+
+ //Prevent wasteful constructor creation.
+ VehicleAIRender( const VehicleAIRender& render );
+ VehicleAIRender& operator=( const VehicleAIRender& render );
+};
+
+#endif //VEHICLEAIRENDER_H \ No newline at end of file
diff --git a/game/code/ai/vehicle/waypointai.cpp b/game/code/ai/vehicle/waypointai.cpp
new file mode 100644
index 0000000..b393cb9
--- /dev/null
+++ b/game/code/ai/vehicle/waypointai.cpp
@@ -0,0 +1,946 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement WaypointAI
+//
+// History: 10/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <ai/vehicle/waypointai.h>
+
+#include <events/eventmanager.h>
+
+#include <meta/eventlocator.h>
+
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#include <roads/roadsegment.h>
+#include <roads/intersection.h>
+#include <roads/road.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/avatarmanager.h>
+
+#include <debug/profiler.h>
+
+#include <gameflow/gameflow.h>
+
+#include <mission/gameplaymanager.h>
+#include <mission/mission.h>
+#include <mission/missionstage.h>
+#include <mission/objectives/missionobjective.h>
+#include <mission/objectives/raceobjective.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+const float SECONDS_BEFORE_RESET_ON_SPOT = 1.0f;
+const float WaypointAI::DEFAULT_TRIGGER_RADIUS = 10.0f; // how close we have to be to waypoint to say we've reached it
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// WaypointAI::WaypointAI
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WaypointAI::WaypointAI(
+ Vehicle* pVehicle,
+ bool enableSegmentOptimization,
+ float triggerRadius,
+ bool autoResetOnDestroyed ) :
+
+ VehicleAI(
+ pVehicle,
+ AI_WAYPOINT,
+ enableSegmentOptimization,
+ VehicleAI::DEFAULT_MIN_SHORTCUT_SKILL,
+ VehicleAI::DEFAULT_MAX_SHORTCUT_SKILL,
+ true),
+ miNumWayPoints( 0 ),
+ miCurrentWayPoint( -1 ),
+ miNextWayPoint( 0 ),
+
+ mTriggerRadius( triggerRadius ),
+
+ mDistToCurrentCollectible( 0.0f ),
+ miCurrentCollectible( -1 ),
+ miNumLapsCompleted( 0 ),
+ mCurrWayPointHasMoved( true ),
+ mNeedsResetOnSpot( false ),
+ mAutoResetOnDestroyed( autoResetOnDestroyed ),
+ mSecondsTillResetOnSpot( 0.0f ),
+
+ mWaypointAIType( RACE ),
+
+ mSecondsSinceTurboUse( 0.0f ),
+
+ mSecondsWaitingForPlayer( 0.0f )
+{
+}
+
+//==============================================================================
+// WaypointAI::~WaypointAI
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WaypointAI::~WaypointAI()
+{
+}
+
+//=============================================================================
+// WaypointAI::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float timeins )
+//
+// Return: void
+//
+//=============================================================================
+void WaypointAI::Update( float timeins )
+{
+BEGIN_PROFILE( "WaypointAI Update" );
+
+ if( GetState() == STATE_LIMBO )
+ {
+ END_PROFILE( "WaypointAI Update" );
+ return;
+ }
+
+ if( miNumWayPoints <= 0 )
+ {
+ END_PROFILE( "WaypointAI Update" );
+ return;
+ }
+
+ rAssert( 0 < miNumWayPoints && miNumWayPoints < WaypointAI::MAX_WAYPOINTS );
+
+
+ if( miCurrentWayPoint < 0 )
+ {
+ SetCurrentWayPoint( 0 );
+ }
+
+ rAssert( 0 <= miCurrentWayPoint && miCurrentWayPoint < miNumWayPoints );
+ rAssert( 0 < miNextWayPoint && miNextWayPoint <= miNumWayPoints );
+
+
+ UpdateNeedToWaitForPlayer( timeins );
+ UpdateSelf();
+ UpdateNeedsResetOnSpot( timeins );
+ FollowWaypoints();
+ UpdateSegments();
+ FollowRoad();
+ EvadeTraffic( NULL );
+ DoSteering();
+ DoCatchUp( timeins );
+ CheckState( timeins );
+
+END_PROFILE( "WaypointAI Update" );
+}
+
+void WaypointAI::Initialize()
+{
+ miNumWayPoints = 0;
+ miCurrentWayPoint = -1;
+ miNextWayPoint = 0;
+
+ mDistToCurrentCollectible = 0.0f;
+ miCurrentCollectible = -1;
+ miNumLapsCompleted = 0;
+
+ mSecondsSinceTurboUse = 0.0f;
+
+ mSecondsWaitingForPlayer = 0.0f;
+
+ // VehicleAI::Initialize will call our reset,
+ // our reset will call VehicleAI::Reset, so it's all good
+ VehicleAI::Initialize();
+}
+
+void WaypointAI::Reset()
+{
+ mCurrWayPointHasMoved = true;
+
+ mNeedsResetOnSpot = false;
+ mSecondsTillResetOnSpot = 0.0f;
+
+ mSecondsWaitingForPlayer = 0.0f;
+
+ VehicleAI::Reset();
+}
+
+
+void WaypointAI::UpdateNeedToWaitForPlayer( float timeins )
+{
+ /*
+ I think we must finally deal with one design limitation... In a mission, the
+ AI generally needs to drive quite far. We only have adjacent zones loaded, so
+ since we start from the middle zone, we only have 1 zone to work with before
+ the AI gets too far ahead or behind the player and no world is loaded. Track
+ data alone is insufficient: the AI WILL get stuck, hit fences, get caught in
+ all sorts of different ballyhoos, etc., etc., etc..
+
+ Since this will have to be a last minute hack, I propose dealing with
+ the different objective types differently. In each update the AI will have
+ different treatments for different objectives:
+
+ OBJ_FOLLOW ==> Do nothing. The crow-fly distance test is part of failure
+ condition for the mission. Wouldn't hurt to do dist test.
+
+ OBJ_LOSETAIL ==> Do nothing. The crow-fly distance test is part of success
+ condition for the mission. Wouldn't hurt to do dist test.
+
+ OBJ_DESTROY ==> If crow-fly distance from player too far (300 meters),
+ temporarily disable. This is so they don't drive around
+ destroying themselves.
+
+ OBJ_DUMP ==> Same as above. Justification: gameplay requires them to
+ stay in your view.
+
+ OBJ_RACE ==> Trickier. If race pos is ahead of player's race pos AND if
+ crow-fly dist is too far (300 meters), then temporarily
+ disable (till player gets closer). We can't just do crow-fly
+ test because if it's possible at all for an AI that has been
+ "left in the dust" to catch up with the player, then we
+ should allow it to do so.
+ */
+ const float DIST_SQR_TOO_FAR_FROM_PLAYER = 90000.0f;
+
+ bool needToDisable = false;
+
+ Mission* m = GetGameplayManager()->GetCurrentMission();
+ if( m && !m->mIsStreetRace1Or2 ) //ignore streetraces cuz they're do loops in small areas
+ {
+ MissionStage* ms = m->GetCurrentStage();
+ if( ms )
+ {
+ MissionObjective* mo = ms->GetObjective();
+ if( mo )
+ {
+ MissionObjective::ObjectiveTypeEnum type = mo->GetObjectiveType();
+ if( type == MissionObjective::OBJ_RACE )
+ {
+ float myDistToCheckPt = GetDistToCurrentCollectible();
+ int myCheckPt = GetCurrentCollectible();
+ int myLap = GetCurrentLap();
+
+
+ float playerDistToCheckPt;
+ int playerCheckPt, playerLap;
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ player->GetRaceInfo( playerDistToCheckPt, playerCheckPt, playerLap );
+
+ bool aheadOfPlayer = false;
+
+ if( myLap > playerLap )
+ {
+ aheadOfPlayer = true;
+ }
+ else if( myLap == playerLap )
+ {
+ if( myCheckPt > playerCheckPt )
+ {
+ aheadOfPlayer = true;
+ }
+ else if( myCheckPt == playerCheckPt )
+ {
+ if( myDistToCheckPt < playerDistToCheckPt )
+ {
+ aheadOfPlayer = true;
+ }
+ }
+ }
+ if( aheadOfPlayer )
+ {
+ // ok, I'm ahead of the player (by assumption that
+ // if I have the same checkpoint as the player,
+ // I will be within 300 meters of the player and
+ // thus won't need to worry about disabling self.)
+
+ rmt::Vector myPos, playerPos;
+ GetPosition( &myPos );
+ player->GetPosition( playerPos );
+
+ if( (myPos - playerPos).MagnitudeSqr() >= DIST_SQR_TOO_FAR_FROM_PLAYER )
+ {
+ needToDisable = true;
+ }
+ }
+ }
+ // Other objective types: go through the same treatment.
+ else
+ {
+ rmt::Vector myPos, playerPos;
+ GetPosition( &myPos );
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( playerPos );
+
+ if( (myPos - playerPos).MagnitudeSqr() >= DIST_SQR_TOO_FAR_FROM_PLAYER )
+ {
+ needToDisable = true;
+ }
+ }
+ }
+ }
+ }
+ if( needToDisable )
+ {
+ if( GetState() != STATE_WAITING_FOR_PLAYER )
+ {
+ //mSecondsWaitingForPlayer = 0.0f;
+ SetState( STATE_WAITING_FOR_PLAYER );
+ }
+ }
+ else
+ {
+ if( GetState() == STATE_WAITING_FOR_PLAYER )
+ {
+ // get back into accel state? Or do we need some sort of push state
+ SetState( STATE_ACCEL );
+ }
+ }
+
+
+ if( GetState() == STATE_WAITING_FOR_PLAYER )
+ {
+ const float MAX_WAIT_SECONDS = 20.0f;
+
+ mSecondsWaitingForPlayer += timeins;
+ if( mSecondsWaitingForPlayer > MAX_WAIT_SECONDS )
+ {
+ // fudge sending this event to fail the mission
+ GetEventManager()->TriggerEvent( EVENT_WAYAI_AT_DESTINATION, GetVehicle() );
+ mSecondsWaitingForPlayer = 0.0f; // reset so we don't continually send this event
+ }
+ }
+}
+
+
+void WaypointAI::PossiblyUseTurbo()
+{
+ rAssert( GetGameFlow()->GetCurrentContext() == CONTEXT_SUPERSPRINT );
+
+ // in the supersprint context, we want to use a turbo:
+ // - if it's been long enough since the last time we used one; and
+ // XXX NAH XXX - if we're behind (in race position); and
+ // XXX NAH XXX - if we haven't used one this lap; and
+ // - if we have any turbo left
+ // - if vector to mDestination and current vehicle facing "line up"; and
+ // XXX NAH XXX - if current vehicle velocity & facing "line up"; and
+ // XXX NAH XXX - 50% chance when there's no one in our way or
+ // 50% chance when there's some one in our way; and
+ //
+
+ const float MIN_SECONDS_BETW_TURBOS = 10.0f;
+ if( mSecondsSinceTurboUse < MIN_SECONDS_BETW_TURBOS )
+ {
+ return;
+ }
+ if( GetVehicle()->mNumTurbos <= 0 )
+ {
+ return;
+ }
+
+ // grab my facing value
+ rmt::Vector myDir;
+ GetHeading( &myDir );
+ rAssert( rmt::Epsilon( myDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // grab the lookahead
+ rmt::Vector myPos;
+ GetPosition( &myPos );
+ rmt::Vector destPt;
+ GetDestination( destPt );
+
+ rmt::Vector toDest = destPt - myPos;
+ toDest.NormalizeSafe();
+ rAssert( rmt::Epsilon( toDest.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // if lookahead not close enough to facing, we don't
+ // have a straightaway, abort
+ const float LOOKAHEAD_FACING_COSALPHA = 0.9848077f; // cos10
+ if( myDir.Dot( toDest ) < LOOKAHEAD_FACING_COSALPHA )
+ {
+ return;
+ }
+
+ /* Don't need to do this
+ The turbo will basically shoot it forward...
+
+ // test the velocity (if speed is sufficient)
+ const float MIN_SPEED_FOR_TURBO = 1.0f;
+ if( GetVehicle()->mSpeed > MIN_SPEED_FOR_TURBO )
+ {
+ rmt::Vector myVel;
+ GetVehicle()->GetVelocity( myVel );
+ myVel /= mSpeed;
+ rAssert( rmt::Epsilon( toDest.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ const float VELOCITY_FACING_COSALPHA = ????
+ if( myDir.Dot( myVel ) < VELOCITY_FACING_COSALPHA )
+ {
+ return;
+ }
+ }
+ */
+ UseTurbo();
+
+}
+
+void WaypointAI::UseTurbo()
+{
+ // Ok, all conditions are a go.. use Turbo...
+ GetVehicle()->TurboOnHorn();
+ mSecondsSinceTurboUse = 0.0f;
+}
+
+void WaypointAI::DoCatchUp( float timeins )
+{
+ mSecondsSinceTurboUse += timeins;
+
+ const float SECONDS_BETW_CATCHUPS = 2.0f;
+ mSecondsSinceLastDoCatchUp += timeins;
+ if( mSecondsSinceLastDoCatchUp < SECONDS_BETW_CATCHUPS )
+ {
+ return;
+ }
+
+ mSecondsSinceLastDoCatchUp = 0.0f;
+
+ ///////////////////////////////////////////////////////
+ // info about myself & player
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer(0);
+ rAssert( player );
+
+ Vehicle* myVehicle = GetVehicle();
+ rAssert( myVehicle );
+
+ // reset old values
+ ResetCatchUpParams();
+
+ Vehicle* playerVehicle = player->GetVehicle();
+ if( playerVehicle == NULL )
+ {
+ return;
+ }
+
+ float mySpeedKmh = myVehicle->mSpeedKmh;
+ float playerSpeedKmh = playerVehicle->mSpeedKmh;
+
+ // figure rough distance to player
+ rmt::Vector myPos, playerPos;
+ GetPosition( &myPos );
+ playerVehicle->GetPosition( &playerPos );
+
+ float roughDistToPlayerSqr = (playerPos - myPos).MagnitudeSqr();
+ //float roughDistToPlayer = rmt::Sqrt( roughDistToPlayerSqr );
+
+ // Set the following values in the upcoming logic
+ float catchUpFactor = 0.0f; // ranges from -1.0f to 1.0f
+ float minDesiredSpeedKmh = 0.0f;
+ float midDesiredSpeedKmh = 0.0f;
+ float maxDesiredSpeedKmh = 0.0f;
+
+ // perform type-specific catch-up logic
+ switch( mWaypointAIType )
+ {
+ case RACE:
+ {
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_SUPERSPRINT )
+ {
+ PossiblyUseTurbo();
+ }
+
+ // Tunable values specific for Race situation
+
+ // If displacement distance lies within this value,
+ // we give the AI maximum capabilities to keep up with player
+ const float DIST_KEEP_UP_WITH_PLAYER = 25.0f;
+
+ // If it ever comes down to comparing distances to a
+ // collectible, this value is used to determine degree of catch-up
+ const float DIST_MAX_CATCHUP = mCatchupParams.Race.DistMaxCatchup;
+
+ const float MIN_FACTOR = mCatchupParams.Race.FractionPlayerSpeedMinCatchup; //0.5f;
+ const float MID_FACTOR = mCatchupParams.Race.FractionPlayerSpeedMidCatchup; //1.1f;
+ const float MAX_FACTOR = mCatchupParams.Race.FractionPlayerSpeedMaxCatchup; //1.7f;
+
+ const float ABSOLUTE_MIN_DESIRED_SPEED_KPH =
+ playerVehicle->mDesignerParams.mDpTopSpeedKmh * MIN_FACTOR;
+ const float ABSOLUTE_MAX_DESIRED_SPEED_KPH =
+ playerVehicle->mDesignerParams.mDpTopSpeedKmh * MAX_FACTOR;
+
+ // figure out the min (when catchup == -1.0f)
+ float tmpMinDesiredSpeed = playerVehicle->mSpeedKmh * MIN_FACTOR;
+ minDesiredSpeedKmh = rmt::Clamp( tmpMinDesiredSpeed,
+ ABSOLUTE_MIN_DESIRED_SPEED_KPH,
+ ABSOLUTE_MAX_DESIRED_SPEED_KPH );
+
+ // figure out the middle point (when catchup == 0.0f)
+ float tmpMidDesiredSpeed = playerVehicle->mSpeedKmh * MID_FACTOR;
+ midDesiredSpeedKmh = rmt::Clamp( tmpMidDesiredSpeed,
+ ABSOLUTE_MIN_DESIRED_SPEED_KPH,
+ ABSOLUTE_MAX_DESIRED_SPEED_KPH );
+
+ // figure out the max range (when catchup == 1.0f)
+ float tmpMaxDesiredSpeed = playerVehicle->mSpeedKmh * MAX_FACTOR;
+ maxDesiredSpeedKmh = rmt::Clamp( tmpMaxDesiredSpeed,
+ ABSOLUTE_MIN_DESIRED_SPEED_KPH,
+ ABSOLUTE_MAX_DESIRED_SPEED_KPH );
+
+
+ // if within range of player, regardless of race position,
+ // try to keep up with the player... but not too successfully...
+ if( roughDistToPlayerSqr <= rmt::Sqr( DIST_KEEP_UP_WITH_PLAYER ) )
+ {
+ // NOTE:
+ // Figure out if it's better to do nothing here, or do what's below
+ //catchUpFactor = 1.0f;
+ }
+ // ok, out of range... check race position
+ else
+ {
+ // get the avatar's race info
+ float playerDistToCheckpoint = -1.0f;
+ int playerCurrCheckpoint = -1;
+ int playerLapsCompleted = -1;
+ player->GetRaceInfo( playerDistToCheckpoint,
+ playerCurrCheckpoint, playerLapsCompleted );
+
+ // check laps first
+ if( miNumLapsCompleted > playerLapsCompleted )
+ {
+ catchUpFactor = -1.0f;
+ }
+ else if( miNumLapsCompleted < playerLapsCompleted )
+ {
+ catchUpFactor = 1.0f;
+ }
+ else
+ {
+ // Ok, now use race checkpoints
+ if( miCurrentCollectible > playerCurrCheckpoint )
+ {
+ catchUpFactor = -1.0f;
+ }
+ else if( miCurrentCollectible < playerCurrCheckpoint )
+ {
+ catchUpFactor = 1.0f;
+ }
+ else
+ {
+ // now use dist to curr checkpoint
+
+ // what's the difference between my dist and player dist?
+ float distDiff = rmt::Fabs( playerDistToCheckpoint - mDistToCurrentCollectible );
+
+ //catchUpFactor = ( distDiff - DIST_KEEP_UP_WITH_PLAYER ) / DIST_DELTA;
+ catchUpFactor = distDiff / DIST_MAX_CATCHUP;
+ catchUpFactor = rmt::Clamp( catchUpFactor, 0.0f, 1.0f );
+ if( mDistToCurrentCollectible < playerDistToCheckpoint )
+ {
+ catchUpFactor *= -1.0f;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case EVADE:
+ {
+ // Don't need the min range b/c catchup in this case ranges from 0 to 1
+ // We never want to slow down for the player. At worst, we match his
+ // speed. When he comes close, we aim for 1.5x his speed
+
+ float myTopSpeedKmh = GetVehicle()->mDesignerParams.mDpTopSpeedKmh;
+ rAssert( myTopSpeedKmh >= mDesiredSpeedKmh );
+ minDesiredSpeedKmh = mDesiredSpeedKmh;
+ midDesiredSpeedKmh = mDesiredSpeedKmh;
+ maxDesiredSpeedKmh = myTopSpeedKmh;
+
+ // Tunable catch-up values used only in EVADE situation
+ const float DIST_PLAYER_TOO_CLOSE_FOR_COMFORT = mCatchupParams.Evade.DistPlayerTooNear;
+ const float DIST_PLAYER_NICE_AND_FAR = mCatchupParams.Evade.DistPlayerFarEnough;
+
+ // Law & Order must be maintained!
+ rAssert( DIST_PLAYER_NICE_AND_FAR > DIST_PLAYER_TOO_CLOSE_FOR_COMFORT );
+ const float DIST_DELTA = DIST_PLAYER_NICE_AND_FAR -
+ DIST_PLAYER_TOO_CLOSE_FOR_COMFORT;
+
+ float roughDistToPlayer = rmt::Sqrt( roughDistToPlayerSqr );
+
+ // The closer the player is, the better the AI should perform
+ catchUpFactor = (DIST_PLAYER_NICE_AND_FAR - roughDistToPlayer) / DIST_DELTA;
+ catchUpFactor = rmt::Clamp( catchUpFactor, 0.0f, 1.0f );
+ }
+ break;
+ case TARGET:
+ {
+ // wanna make it easy for the player to hit us...
+ // At our best, we never want to exceed the player's speed..
+ // If getting too far from player, speed should approach near zero
+
+ const float ABSOLUTE_MIN_DESIRED_SPEED_KPH =
+ playerVehicle->mDesignerParams.mDpTopSpeedKmh * 0.2f;
+ const float ABSOLUTE_MAX_DESIRED_SPEED_KPH =
+ playerVehicle->mDesignerParams.mDpTopSpeedKmh;
+
+ minDesiredSpeedKmh = ABSOLUTE_MIN_DESIRED_SPEED_KPH;
+ midDesiredSpeedKmh = ABSOLUTE_MAX_DESIRED_SPEED_KPH;
+ maxDesiredSpeedKmh = ABSOLUTE_MAX_DESIRED_SPEED_KPH;
+
+ // TUNABLE catch-up params used only in TARGET situation
+ const float DIST_PLAYER_CLOSE_ENOUGH = mCatchupParams.Target.DistPlayerNearEnough; // dist at which catchup is 0.0f
+ const float DIST_PLAYER_TOO_FAR = mCatchupParams.Target.DistPlayerTooFar; // dist at which catchup is -1.0f
+
+ // Police our own values
+ rAssert( DIST_PLAYER_TOO_FAR > DIST_PLAYER_CLOSE_ENOUGH );
+ const float DIST_DELTA = DIST_PLAYER_TOO_FAR - DIST_PLAYER_CLOSE_ENOUGH;
+
+ float roughDistToPlayer = rmt::Sqrt( roughDistToPlayerSqr );
+
+ // the further the player is, the worse the AI should perform
+ catchUpFactor = (DIST_PLAYER_CLOSE_ENOUGH - roughDistToPlayer) / DIST_DELTA;
+ catchUpFactor = rmt::Clamp( catchUpFactor, -1.0f, 0.0f );
+ }
+ break;
+ default:
+ {
+ rAssert( false );
+ }
+ break;
+ }
+
+ rAssert( minDesiredSpeedKmh <= midDesiredSpeedKmh &&
+ midDesiredSpeedKmh <= maxDesiredSpeedKmh );
+
+ mDesiredSpeedKmh = midDesiredSpeedKmh + catchUpFactor * ((catchUpFactor > 0.0f)?
+ (maxDesiredSpeedKmh - midDesiredSpeedKmh) :
+ (midDesiredSpeedKmh - minDesiredSpeedKmh) );
+
+ mShortcutSkillMod = (int)(catchUpFactor * VehicleAI::CATCHUP_MAX_SHORTCUTSKILL_MOD);
+}
+
+void WaypointAI::UpdateNeedsResetOnSpot( float timeins )
+{
+ Vehicle* v = GetVehicle();
+ if( v != NULL )
+ {
+ rmt::Vector up;
+ rmt::Vector testUp( 0.0f, -1.0f, 0.0f );
+ v->GetVUP( &up );
+ float ep = -0.258819f;//cos100
+ float alpha = up.Dot(testUp);
+
+ if( !mNeedsResetOnSpot ) // if not reset is scheduled, see if we need to
+ {
+ if( v->mVehicleDestroyed && this->mAutoResetOnDestroyed )
+ {
+ mNeedsResetOnSpot = true;
+ mSecondsTillResetOnSpot = SECONDS_BEFORE_RESET_ON_SPOT;
+ }
+ else
+ {
+ // Test to make sure that we're not flipped over.
+ // If so, schedule a reset...
+ if( alpha >= ep )
+ {
+ // this means our up vector is quite close to the
+ // upside-down vector. Reset!
+ mNeedsResetOnSpot = true;
+ mSecondsTillResetOnSpot = SECONDS_BEFORE_RESET_ON_SPOT;
+ }
+ }
+ }
+ else // here we're counting down till reset
+ {
+ if( !v->mVehicleDestroyed || !mAutoResetOnDestroyed )
+ {
+ // if we're not resetting cuz we were destroyed
+ // and the vector corrected itself, cancel the reset
+ if( alpha < ep )
+ {
+ mNeedsResetOnSpot = false;
+ }
+ }
+
+ // here we still need to reset, continue with reset countdown
+ if( mNeedsResetOnSpot )
+ {
+ if( mSecondsTillResetOnSpot <= 0.0f )
+ {
+ // countdown finished.. Do reset
+ // Figure out if we need to reset damage states
+ bool resetDamageStates =
+ (v->mVehicleDestroyed && mAutoResetOnDestroyed)? true : false;
+
+ v->ResetOnSpot( resetDamageStates );
+ mNeedsResetOnSpot = false;
+ }
+ else
+ {
+ // countdown not complete... keep goin
+ mSecondsTillResetOnSpot -= timeins;
+ }
+ }
+ }
+ }
+}
+
+
+void WaypointAI::ClearWaypoints()
+{
+ miNumWayPoints = 0;
+ miNextWayPoint = 0;
+ miCurrentWayPoint = -1;
+}
+
+void WaypointAI::AddWaypoint( Locator* loc )
+{
+ rAssert( miNumWayPoints < MAX_WAYPOINTS );
+ rAssert( loc != NULL );
+
+ rmt::Vector locPos;
+ loc->GetLocation( &locPos );
+
+ float dummy;
+ RoadSegment* closestSeg = NULL;
+ GetIntersectManager()->FindClosestAnyRoad( locPos, 100.0f, closestSeg, dummy );
+
+#if defined( RAD_DEBUG ) || defined( RAD_TUNE )
+ char errMsg[512];
+ sprintf( errMsg,
+ "Waypoint at %.1f, %.1f, %.1f is not within %.1f meters of a road segment! "
+ "Please contact Darren Evenson or Dusit for a fix!\n",
+ locPos.x, locPos.y, locPos.z, mTriggerRadius );
+ rTuneAssertMsg( dummy < mTriggerRadius*mTriggerRadius, errMsg );
+#endif
+
+ RoadSegment* tmpSeg = (RoadSegment*) closestSeg;
+ float tmpSegT = RoadManager::DetermineSegmentT( locPos, tmpSeg );
+ float tmpRoadT = RoadManager::DetermineRoadT( tmpSeg, tmpSegT );
+
+ mpWayPoints[ miNumWayPoints ].loc = loc;
+ mpWayPoints[ miNumWayPoints ].seg = tmpSeg;
+ mpWayPoints[ miNumWayPoints ].segT = tmpSegT;
+ mpWayPoints[ miNumWayPoints ].roadT = tmpRoadT;
+ mpWayPoints[ miNumWayPoints ].elem.elem = tmpSeg->GetRoad();
+ mpWayPoints[ miNumWayPoints ].elem.type = RoadManager::ET_NORMALROAD;
+
+ miNumWayPoints += 1;
+}
+
+bool WaypointAI::MustRepopulateSegments()
+{
+ return mCurrWayPointHasMoved;
+
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+void WaypointAI::SetCurrentWayPoint( int index )
+{
+ miCurrentWayPoint = index;
+
+ if(( miCurrentWayPoint >= 0 ) && ( miCurrentWayPoint < miNumWayPoints ))
+ {
+ mCurrWayPointHasMoved = true;
+ int next = miCurrentWayPoint + 1;
+ miNextWayPoint = next;
+ }
+}
+
+void WaypointAI::FollowWaypoints()
+{
+ mCurrWayPointHasMoved = false;
+ if( miNumWayPoints <= 0 )
+ {
+ return;
+ }
+
+ if( miCurrentWayPoint < 0 )
+ {
+ SetCurrentWayPoint( 0 );
+ }
+
+ if ( miCurrentWayPoint < miNumWayPoints )
+ {
+ if ( TestWaypoint( miCurrentWayPoint ) )
+ {
+ //Im at the currentWaypoint. Time to choose another or stop if this
+ //is the end of the line.
+ GetEventManager()->TriggerEvent( EVENT_WAYAI_HIT_WAYPOINT, NULL );
+
+ if( miCurrentWayPoint >= miNumWayPoints-1 )
+ {
+ // loop again! whee!
+
+ // broadcast to folks that I just reached my last waypoint..
+ // it will be a repeat of the last normal HIT_WAYPOINT event
+ GetEventManager()->TriggerEvent( EVENT_WAYAI_HIT_LAST_WAYPOINT, GetVehicle() );
+
+ // TODO:
+ // We might want to expose control over looping here.
+ // For example, introduce an mLooping variable that is set
+ // upon construction, exposed to the designers via mission script
+ // command. If mLooping is true, we go to beginning... if not
+ // then SetActive( false ).
+ // For now, default to YES! We are looping! The governing race
+ // logic should do the calling of SetActive( false ) when we've
+ // reached a certain waypoint if they want us to stop.
+ miNextWayPoint = 0;
+ }
+ SetCurrentWayPoint( miNextWayPoint );
+ }
+ }
+}
+
+
+bool WaypointAI::TestReachedTarget( const rmt::Vector& start, const rmt::Vector& end )
+{
+ if( miNumWayPoints > 0 )//miCurrentWayPoint >= 0 )
+ {
+ rAssert( miCurrentWayPoint >= 0 );
+
+ int point = miCurrentWayPoint;
+ if ( point >= miNumWayPoints )
+ {
+ point = 0;
+ }
+ rAssert( mpWayPoints[ point ].loc );
+
+ rmt::Vector wayLoc;
+ mpWayPoints[ point ].loc->GetLocation( &wayLoc );
+
+ rmt::Vector closestPt;
+ FindClosestPointOnLine( start, end, wayLoc, closestPt );
+
+ float distSqr = (closestPt - wayLoc).MagnitudeSqr();
+ return ( distSqr < (mTriggerRadius*mTriggerRadius) );
+ }
+
+ // if no current waypoint, dont' go anywhere, we're already here...
+ return true;
+
+}
+
+void WaypointAI::GetClosestPathElementToTarget(
+ rmt::Vector& targetPos,
+ RoadManager::PathElement& elem,
+ RoadSegment*& seg,
+ float& segT,
+ float& roadT )
+{
+ elem.elem = NULL;
+ seg = NULL;
+ segT = 0.0f;
+ roadT = 0.0f;
+
+ if( miNumWayPoints > 0 )
+ {
+ rAssert( miCurrentWayPoint >= 0 );
+
+ int point = miCurrentWayPoint;
+ if ( point >= miNumWayPoints )
+ {
+ point = 0;
+ }
+ rAssert( mpWayPoints[ point ].loc );
+
+ mpWayPoints[ point ].loc->GetLocation( &targetPos );
+ elem = mpWayPoints[ point ].elem;
+ seg = mpWayPoints[ point ].seg;
+ segT = mpWayPoints[ point ].segT;
+ roadT = mpWayPoints[ point ].roadT;
+ }
+}
+
+int WaypointAI::RegisterHudMapIcon()
+{
+ int iconID = -1;
+
+ rmt::Vector initialLoc;
+ GetVehicle()->GetPosition( &initialLoc );
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ switch( mWaypointAIType )
+ {
+ case RACE:
+ {
+ iconID = currentHud->GetHudMap( 0 )->RegisterIcon( HudMapIcon::ICON_AI_RACE, initialLoc, this );
+
+ break;
+ }
+ case EVADE:
+ {
+ iconID = currentHud->GetHudMap( 0 )->RegisterIcon( HudMapIcon::ICON_AI_EVADE, initialLoc, this );
+
+ break;
+ }
+ case TARGET:
+ {
+ iconID = currentHud->GetHudMap( 0 )->RegisterIcon( HudMapIcon::ICON_AI_TARGET, initialLoc, this );
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Invalid waypoint AI type!" );
+
+ break;
+ }
+ }
+ }
+
+ return iconID;
+}
+
+bool WaypointAI::TestWaypoint( int waypoint )
+{
+ rmt::Vector mypos;
+ GetVehicle()->GetPosition( &mypos );
+
+ rmt::Vector vec2dest;
+ mpWayPoints[ waypoint ].loc->GetLocation( &vec2dest );
+ vec2dest.Sub( mypos );
+ float dist = vec2dest.MagnitudeSqr();
+ return ( dist < mTriggerRadius*mTriggerRadius );
+}
diff --git a/game/code/ai/vehicle/waypointai.h b/game/code/ai/vehicle/waypointai.h
new file mode 100644
index 0000000..08b60c4
--- /dev/null
+++ b/game/code/ai/vehicle/waypointai.h
@@ -0,0 +1,183 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: waypointai.h
+//
+// Description: Blahblahblah
+//
+// History: 10/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef WAYPOINTAI_H
+#define WAYPOINTAI_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <ai/vehicle/vehicleai.h>
+
+//========================================
+// Forward References
+//========================================
+
+class Locator;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class WaypointAI : public VehicleAI
+{
+public:
+
+ static const float DEFAULT_TRIGGER_RADIUS;
+
+ WaypointAI(
+ Vehicle* pVehicle,
+ bool enableSegmentOptimization=true,
+ float triggerRadius=DEFAULT_TRIGGER_RADIUS,
+ bool autoResetOnDestroyed=false );
+
+ virtual ~WaypointAI();
+
+ void ClearWaypoints();
+ void AddWaypoint( Locator* loc );
+
+ virtual void Update( float timeins );
+ virtual void Initialize();
+ virtual void Reset();
+
+ int GetCurrentWayPoint() const { return miCurrentWayPoint; };
+
+ // get/set collectible from mission objective
+ void SetCurrentCollectible( int collectible );
+ int GetCurrentCollectible() const;
+
+ // get/set current lap
+ void SetCurrentLap( int lap );
+ int GetCurrentLap() const;
+
+ // get/set dist to current collectible
+ float GetDistToCurrentCollectible() const;
+ void SetDistToCurrentCollectible( float dist );
+
+ static const int MAX_WAYPOINTS = 32;
+
+ enum WaypointAIType
+ {
+ RACE,
+ EVADE,
+ TARGET
+ };
+
+ void SetAIType( WaypointAIType type );
+
+ void UseTurbo();
+
+protected:
+ void FollowWaypoints();
+ void SetCurrentWayPoint( int index );
+ virtual bool MustRepopulateSegments();
+ virtual bool TestReachedTarget( const rmt::Vector& start, const rmt::Vector& end );
+
+ virtual void GetClosestPathElementToTarget(
+ rmt::Vector& targetPos,
+ RoadManager::PathElement& elem,
+ RoadSegment*& seg,
+ float& segT,
+ float& roadT );
+
+ virtual void DoCatchUp( float timeins );
+
+ void UpdateNeedsResetOnSpot( float timeins );
+
+ void UpdateNeedToWaitForPlayer( float timeins );
+
+ void PossiblyUseTurbo();
+
+private:
+ virtual int RegisterHudMapIcon();
+
+ bool TestWaypoint( int waypoint );
+
+ //Prevent wasteful constructor creation.
+ WaypointAI( const WaypointAI& waypointai );
+ WaypointAI& operator=( const WaypointAI& waypointai );
+
+
+private:
+ struct WayPoint
+ {
+ Locator* loc;
+ RoadManager::PathElement elem;
+ float segT;
+ RoadSegment* seg;
+ float roadT;
+ };
+ WayPoint mpWayPoints[ MAX_WAYPOINTS ];
+ int miNumWayPoints;
+ int miCurrentWayPoint;
+ int miNextWayPoint;
+
+ // distance at and closer than which we consider a waypoint reached
+ // (and move along to next waypoint)
+ float mTriggerRadius;
+
+
+ ///////////////// RACE DATA ///////////////////
+ // we keep pieces of the race data in this class
+ // because catch-up logic will need to use them
+ float mDistToCurrentCollectible;
+ int miCurrentCollectible;
+ int miNumLapsCompleted;
+
+ bool mCurrWayPointHasMoved : 1;
+ ///////////////// Auto-resetting stuff ///////////
+ bool mNeedsResetOnSpot : 1;
+ bool mAutoResetOnDestroyed : 1;
+ float mSecondsTillResetOnSpot;
+ WaypointAIType mWaypointAIType;
+
+ ////////////// Turbo logic stuff ///////////////
+ float mSecondsSinceTurboUse;
+
+
+ float mSecondsWaitingForPlayer;
+
+};
+
+inline void WaypointAI::SetCurrentCollectible( int collectible )
+{
+ miCurrentCollectible = collectible;
+}
+inline int WaypointAI::GetCurrentCollectible() const
+{
+ return miCurrentCollectible;
+}
+inline void WaypointAI::SetCurrentLap( int lap )
+{
+ miNumLapsCompleted = lap;
+}
+inline int WaypointAI::GetCurrentLap() const
+{
+ return miNumLapsCompleted;
+}
+inline void WaypointAI::SetDistToCurrentCollectible( float dist )
+{
+ rAssert( dist >= 0.0f );
+ mDistToCurrentCollectible = dist;
+}
+inline float WaypointAI::GetDistToCurrentCollectible() const
+{
+ return mDistToCurrentCollectible;
+}
+inline void WaypointAI::SetAIType( WaypointAIType type )
+{
+ mWaypointAIType = type;
+}
+
+#endif //WAYPOINTAI_H
diff --git a/game/code/atc/allatc.cpp b/game/code/atc/allatc.cpp
new file mode 100644
index 0000000..53306bc
--- /dev/null
+++ b/game/code/atc/allatc.cpp
@@ -0,0 +1,2 @@
+#include <atc/atcmanager.cpp>
+#include <atc/atcloader.cpp>
diff --git a/game/code/atc/atcloader.cpp b/game/code/atc/atcloader.cpp
new file mode 100644
index 0000000..a9236db
--- /dev/null
+++ b/game/code/atc/atcloader.cpp
@@ -0,0 +1,129 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ATCLoader.cpp
+//
+// Description: Implements ATCLoader, to process the ATC Chunk.
+//
+// History: 03/08/2002 + Created -- Chuck Chow
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/chunkfile.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <atc/atcloader.h>
+#include <memory/srrmemory.h>
+#include <atc/atcmanager.h>
+#include <render/DSG/collisionentitydsg.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ATCLoader::ATCLoader
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: ChunkID of The ATTRIBUTE_TABLE
+//
+// Return: N/A.
+//
+//==============================================================================
+ATCLoader::ATCLoader():tSimpleChunkHandler( SRR2::ChunkID::ATTRIBUTE_TABLE )
+{
+
+
+}
+
+//==============================================================================
+// ATCLoader::~ATCLoader
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ATCLoader::~ATCLoader()
+{
+}
+
+//=============================================================================
+// ATCLoader::LoadObject
+//=============================================================================
+// Description: Process the ATC chunk, creates the ATC table an sets mp_ATCTable in the ATCManager.
+//
+// Parameters: (tChunkFile* f, tEntityStore* store)
+//
+// Return: NULL, This chunk does not get added to the inventory
+//
+//=============================================================================
+
+
+//Loads the ATC Chunk
+tEntity* ATCLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ ATCManager* p_atcmanager =NULL;
+ AttributeRow* p_AttributeRow = NULL;
+ unsigned long numrows = f->GetUInt();
+
+ //creating ATCTable
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+
+ p_AttributeRow = new AttributeRow [numrows];
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+
+ //populating ATCTable
+ //read in the array row until there is no more rows
+ for(unsigned int i=0;i<numrows;i++)
+ {
+ f->GetString(p_AttributeRow[i].mSound);
+ f->GetString(p_AttributeRow[i].mParticle);
+ f->GetString(p_AttributeRow[i].mAnimation);
+ p_AttributeRow[i].mFriction=f->GetFloat();
+ p_AttributeRow[i].mMass=f->GetFloat();
+ p_AttributeRow[i].mElasticity=f->GetFloat();
+ }
+
+ //setting mp_ATCTable in the ATCManager
+ p_atcmanager=ATCManager::GetInstance();
+ p_atcmanager->SetATCTable(p_AttributeRow,numrows);
+
+ return NULL;
+}
+
+
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/atc/atcloader.h b/game/code/atc/atcloader.h
new file mode 100644
index 0000000..5e166f8
--- /dev/null
+++ b/game/code/atc/atcloader.h
@@ -0,0 +1,53 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: atcloader.h
+//
+// Description: Loads the ATC, creates the AttributreTable, and sets the ATCManager's mp_ATCTable.
+//
+// History: 03/08/2002 + Created -- Chuck Chow
+//
+//=============================================================================
+
+#ifndef ATCLOADER_H
+#define ATCLOADER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <constants/srrchunks.h>
+#include <p3d/loadmanager.hpp>
+
+
+//========================================
+// Forward References
+//========================================
+
+
+
+//=============================================================================
+//
+// Synopsis:Blah Blah
+//
+//=============================================================================
+
+class ATCLoader: public tSimpleChunkHandler
+{
+public:
+ ATCLoader();
+ virtual ~ATCLoader();
+
+private:
+
+
+ //Prevent wasteful constructor creation.
+ ATCLoader( const ATCLoader& atcloader);
+ ATCLoader& operator=( const ATCLoader& atc );
+
+ // P3D chunk loader.
+ virtual tEntity* LoadObject(tChunkFile* f, tEntityStore* store);
+
+};
+
+
+#endif //ATCLOADER_H
diff --git a/game/code/atc/atcmanager.cpp b/game/code/atc/atcmanager.cpp
new file mode 100644
index 0000000..9adaae1
--- /dev/null
+++ b/game/code/atc/atcmanager.cpp
@@ -0,0 +1,208 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: atcmanager.cpp
+//
+// Description: Implementation for the ATCManager class, Stolen from Darwin's EventManager .
+//
+// History: + Created -- Chuck Chow
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <atc/atcmanager.h>
+#include <memory/srrmemory.h>
+#include <render/DSG/collisionentitydsg.h>
+#include <loading/loadingmanager.h>
+#include <constants/physprop.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+
+ATCManager* ATCManager::spInstance = NULL;
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ATCManager::CreateInstance
+//==============================================================================
+//
+// Description: Creates the ATCManager.
+//
+// Parameters: None.
+//
+// Return: Pointer to the ATCManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+ATCManager* ATCManager::CreateInstance()
+{
+ rAssert( spInstance == NULL );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+
+ spInstance = new ATCManager;
+ rAssert( spInstance );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+
+ return spInstance;
+}
+
+//==============================================================================
+// ATCManager::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the ATCManager singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the ATCManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+ATCManager* ATCManager::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// ATCManager::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the ATCManager.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void ATCManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete spInstance;
+ spInstance = NULL;
+}
+
+
+// Call this method to Create a CollisionAttribute Class
+// Input is the classtypeid and the physprop from the OTC Chunk found in a DSG object in a PURE3D file.
+
+
+// NOTE this Method is incomplete since we dont have a particle system manager, I'm creating the particle pointer set to NULL
+//
+// volume is for moveable things to properly calculate the density for the physics properties
+// it defaults to 0.0f, in which case it's ignored
+CollisionAttributes* ATCManager::CreateCollisionAttributes(unsigned int classtypeid,unsigned int physpropid, float volume)
+{
+
+ //checking that physpropid is in the valid range
+ rAssert( (physpropid >= 0) && (physpropid < mNumRows) );
+
+ //Creates a CollisionAttributes Class and returns a pointer to it.
+ CollisionAttributes* p_collisionattributes = new (GMA_LEVEL_OTHER) CollisionAttributes (mp_ATCTable[physpropid].mSound,mp_ATCTable[physpropid].mParticle,mp_ATCTable[physpropid].mAnimation,
+ mp_ATCTable[physpropid].mFriction,mp_ATCTable[physpropid].mMass,mp_ATCTable[physpropid].mElasticity,
+ classtypeid, volume);
+ return p_collisionattributes;
+
+
+}
+
+
+//Initializes the Manager's ATCTable
+//by asking LoadingManager to load ATC chunk
+void ATCManager::Init (void)
+{
+ GetLoadingManager()->AddRequest(FILEHANDLER_PURE3D, "art\\atc\\atc.p3d", GMA_PERSISTENT, "Default");
+}
+
+//Sets the internal pointer to a Vaild ATC Table. and number of rows in the table
+void ATCManager::SetATCTable(AttributeRow* p_attributerow,unsigned int rows)
+{
+ mp_ATCTable=p_attributerow;
+ mNumRows =rows;
+
+ strcpy(mp_ATCTable[0].mAnimation,"eNull");
+ strcpy(mp_ATCTable[0].mSound,"smash");
+ strcpy(mp_ATCTable[0].mParticle,"eNull");
+ mp_ATCTable[0].mMass=100.0f;
+ mp_ATCTable[0].mFriction=1.0f;
+ mp_ATCTable[0].mElasticity=1.0f;
+
+}
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// ATCManager::ATCManager
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+ATCManager::ATCManager()
+{
+ mp_ATCTable =NULL;
+ mNumRows=0;
+}
+
+
+//==============================================================================
+// ATCManager::~ATCManager
+//==============================================================================
+//
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+ATCManager::~ATCManager()
+{
+ delete [] mp_ATCTable;
+}
+
diff --git a/game/code/atc/atcmanager.h b/game/code/atc/atcmanager.h
new file mode 100644
index 0000000..c346e42
--- /dev/null
+++ b/game/code/atc/atcmanager.h
@@ -0,0 +1,84 @@
+//=============================================================================
+
+#ifndef ATCMANAGER_H
+#define ATCMANAGER_H
+
+//========================================
+// System Includes
+//========================================
+
+
+//========================================
+// Project Includes
+//========================================
+
+
+//========================================
+// Forward References
+//========================================
+class CollisionAttributes;
+
+const unsigned int MAX_CHAR_LENGTH = 64;
+
+struct AttributeRow
+{
+ char mSound [MAX_CHAR_LENGTH];
+ char mAnimation [MAX_CHAR_LENGTH];
+ char mParticle [MAX_CHAR_LENGTH];
+ float mMass;
+ float mFriction;
+ float mElasticity;
+};
+
+
+
+
+
+
+//==============================================================================
+//
+// Synopsis: Create the ATC table and create CollosionAttribute objects
+//
+//==============================================================================
+class ATCManager
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static ATCManager* CreateInstance();
+ static ATCManager* GetInstance();
+ static void DestroyInstance();
+
+
+ // Methods
+ void Init();
+ CollisionAttributes* CreateCollisionAttributes(unsigned int classtypeid, unsigned int physpropid, float volume = 0.0f);
+ void SetATCTable(AttributeRow* p_attributerow,unsigned int rows);
+
+
+
+ private:
+
+ // No public access to these, use singleton interface.
+ ATCManager();
+ ~ATCManager();
+
+ // Declared but not defined to prevent copying and assignment.
+ ATCManager( const ATCManager& );
+ ATCManager& operator=( const ATCManager& );
+
+ // Pointer the Master AtrributeTableChunk
+ AttributeRow* mp_ATCTable;
+ unsigned int mNumRows;
+ // Pointer to the one and only instance of this singleton.
+ static ATCManager* spInstance;
+
+};
+
+
+// A little syntactic sugar for getting at this singleton.
+inline ATCManager* GetATCManager() { return( ATCManager::GetInstance() ); }
+
+
+#endif // ATCMANAGER_H
+
diff --git a/game/code/camera/allcamera.cpp b/game/code/camera/allcamera.cpp
new file mode 100644
index 0000000..2f7e6bc
--- /dev/null
+++ b/game/code/camera/allcamera.cpp
@@ -0,0 +1,23 @@
+#include <camera/animatedcam.cpp>
+#include <camera/bumpercam.cpp>
+#include <camera/chasecam.cpp>
+#include <camera/conversationcam.cpp>
+#include <camera/debugcam.cpp>
+#include <camera/followcam.cpp>
+#include <camera/kullcam.cpp>
+#include <camera/relativeanimatedcam.cpp>
+#include <camera/railcam.cpp>
+#include <camera/sinecosshaker.cpp>
+#include <camera/supercam.cpp>
+#include <camera/supercamcentral.cpp>
+#include <camera/supercamcontroller.cpp>
+#include <camera/supercammanager.cpp>
+#include <camera/trackercam.cpp>
+#include <camera/walkercam.cpp>
+#include <camera/wrecklesscam.cpp>
+#include <camera/wrecklesseventlistener.cpp>
+#include <camera/staticcam.cpp>
+#include <camera/reversecam.cpp>
+#include <camera/surveillancecam.cpp>
+#include <camera/supersprintcam.cpp>
+//#include <camera/firstpersoncam.cpp> \ No newline at end of file
diff --git a/game/code/camera/animatedcam.cpp b/game/code/camera/animatedcam.cpp
new file mode 100644
index 0000000..10425a7
--- /dev/null
+++ b/game/code/camera/animatedcam.cpp
@@ -0,0 +1,746 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: AnimatedCam.cpp
+//
+// Description: Implement AnimatedCam
+//
+// History: 24/04/2002 + Created -- Cary Brisebois, based from the Bumper Camera
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/animatedcam.h>
+#include <camera/supercammanager.h>
+#include <events/eventmanager.h>
+#include <input/inputmanager.h>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/utility.hpp>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenletterbox.h>
+#include <presentation/gui/ingame/guiscreenmultihud.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiwindow.h>
+#include <interiors/interiormanager.h>
+
+//========================================
+// Definitions
+//========================================
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+#define SKIPPING_DEFAULT false
+static tName g_CameraName;
+static tName g_MulticontrollerName;
+static tName g_MissionStartCameraName;
+static tName g_MissionStartMulticontrollerName;
+static tCamera* g_Camera = NULL;
+static tMultiController* g_Multicontroller = NULL;
+static bool g_CameraSwitchPending = false;
+static bool g_TriggeredNextCamera = false;
+static int g_InstanceCount = 0;
+static int g_CameraTransitionFlags = 0;
+static bool g_AllowSkipping = SKIPPING_DEFAULT;
+static bool g_SurpressNextLetterbox = false;
+
+const int DEFAULT_TRANSITION_FLAGS = SuperCamCentral::QUICK | SuperCamCentral::FORCE;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+
+void AnimatedCam::Abort()
+{
+ if( g_Multicontroller != NULL )
+ {
+ int flags = DEFAULT_TRANSITION_FLAGS;
+
+ if ( 0 != g_CameraTransitionFlags )
+ {
+ //Override the flags.
+ flags = g_CameraTransitionFlags;
+ }
+
+ rAssert( m_NextCameraType != NUM_TYPES );
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( m_NextCameraType, flags, 3000 );
+ g_TriggeredNextCamera = true;
+
+ //Reset the flags override.
+ g_CameraTransitionFlags = 0;
+
+ tRefCounted::Release( g_Multicontroller );
+ tRefCounted::Release( g_Camera );
+ AllowSkipping( SKIPPING_DEFAULT );
+ }
+}
+
+
+//==============================================================================
+// AnimatedCam::AnimatedCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AnimatedCam::AnimatedCam():
+ m_NextCameraType( INVALID )
+{
+ ++g_InstanceCount;
+}
+
+//==============================================================================
+// AnimatedCam::~AnimatedCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AnimatedCam::~AnimatedCam()
+{
+ --g_InstanceCount;
+ if( g_InstanceCount == 0 )
+ {
+
+ tRefCounted::Release( g_Multicontroller );
+ tRefCounted::Release( g_Camera );
+ g_CameraName.SetText(NULL);
+ g_MulticontrollerName.SetText(NULL);
+ g_MissionStartCameraName.SetText(NULL);
+ g_MissionStartMulticontrollerName.SetText(NULL);
+ g_TriggeredNextCamera = false;
+ }
+}
+
+//==============================================================================
+// AnimatedCam::AllowSkipping
+//==============================================================================
+// Description: should the animated camera allow the user to skip it with a
+// button press
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void AnimatedCam::AllowSkipping( const bool skippingAllowed )
+{
+ g_AllowSkipping = skippingAllowed;
+}
+
+//==============================================================================
+// AnimatedCam::CameraSwitchPending
+//==============================================================================
+// Description: is there a camera switch pending?
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+bool AnimatedCam::CameraSwitchPending()
+{
+ return g_CameraSwitchPending;
+}
+
+//==============================================================================
+// AnimatedCam::CheckPendingCameraSwitch
+//==============================================================================
+// Description: checks if we've queued up a camera switch to the animated camera
+// because the camera system isn't always set up in time.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void AnimatedCam::CheckPendingCameraSwitch()
+{
+ if( CameraSwitchPending() )
+ {
+ LookupCamera();
+ LookupMulticontroller();
+ SuperCamManager* scm = GetSuperCamManager();
+ SuperCamCentral* scc = scm->GetSCC( 0 );
+ SuperCam* sc = scc->GetActiveSuperCam();
+ if( sc == NULL )
+ {
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( ANIMATED_CAM );
+ }
+ else
+ {
+ SuperCam::Type type = sc->GetType();
+ if( type != ANIMATED_CAM )
+ {
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( ANIMATED_CAM );
+ }
+ }
+ SetCameraSwitchPending( false );
+ }
+}
+
+//=============================================================================
+// AnimatedCam::ClearCamera
+//=============================================================================
+// Description: clears out the animated camera
+//
+// Parameters: none
+//
+// Return: none
+//
+//=============================================================================
+void AnimatedCam::ClearCamera()
+{
+ tCamera* pCamera = NULL;
+ tMultiController* pMultiController = NULL;
+
+ tRefCounted::Assign( g_Camera, pCamera);
+ tRefCounted::Assign( g_Multicontroller,pMultiController );
+
+
+}
+
+//=============================================================================
+// AnimatedCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+const char* const AnimatedCam::GetName() const
+{
+ return "ANIMATED_CAM";
+}
+
+//=============================================================================
+// AnimatedCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+SuperCam::Type AnimatedCam::GetType()
+{
+ return ANIMATED_CAM;
+}
+
+//=============================================================================
+// AnimatedCam::GetWatcherName
+//=============================================================================
+// Description: the name of the class for the watcher or other debug purposes
+//
+// Parameters: NONE
+//
+// Return: const char* - the name of the class
+//
+//=============================================================================
+#ifdef DEBUGWATCH
+const char* AnimatedCam::GetWatcherName() const
+{
+ return "AnimatedCam";
+}
+#endif
+
+
+//=============================================================================
+// AnimatedCam::LetterBoxStart
+//=============================================================================
+// Description: start the letterbox for this camera
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+void AnimatedCam::LetterBoxStart()
+{
+ if( !g_SurpressNextLetterbox )
+ {
+ CGuiManager* gm = GetGuiSystem()->GetCurrentManager();
+ CGuiWindow::eGuiWindowID currentScreenId = gm->GetCurrentScreen();
+ CGuiWindow* currentWindow = gm->FindWindowByID( currentScreenId );
+ currentWindow->ForceClearTransitions();
+ CGuiScreenLetterBox::SuppressAcceptCancelButtons();
+ if( g_AllowSkipping )
+ {
+ CGuiScreenLetterBox::UnSurpressSkipButton();
+ }
+ else
+ {
+ CGuiScreenLetterBox::SurpressSkipButton();
+ }
+ GetGuiSystem()->GotoScreen( CGuiWindow::GUI_SCREEN_ID_LETTER_BOX, 0x00, 0x00, CLEAR_WINDOW_HISTORY );
+
+ CGuiManagerInGame* guiIngameManager = GetGuiSystem()->GetInGameManager();
+ if( guiIngameManager != NULL )
+ {
+ CGuiWindow* window = guiIngameManager->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_LETTER_BOX );
+ CGuiScreenLetterBox* letterBox = dynamic_cast< CGuiScreenLetterBox* >( window );
+ letterBox->CheckIfScreenShouldBeBlank();
+
+ window = guiIngameManager->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_MULTI_HUD );
+ CGuiScreenMultiHud* multiHud = dynamic_cast< CGuiScreenMultiHud* >( window );
+ if( multiHud != NULL )
+ {
+ multiHud->ShowLetterBox();
+ }
+ }
+ }
+}
+
+//=============================================================================
+// AnimatedCam::LetterBoxStop
+//=============================================================================
+// Description: stop the letterbox for this camera
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+void AnimatedCam::LetterBoxStop()
+{
+ if( !g_SurpressNextLetterbox )
+ {
+ CGuiScreenLetterBox::ForceOpen();
+ CGuiScreenLetterBox::SuppressAcceptCancelButtons( false );
+ GetGuiSystem()->GotoScreen( CGuiWindow::GUI_SCREEN_ID_HUD, 0x00, 0x00, CLEAR_WINDOW_HISTORY );
+ }
+ else
+ {
+ g_SurpressNextLetterbox = false;
+ }
+}
+
+//=============================================================================
+// AnimatedCam::LookupCamera
+//=============================================================================
+// Description: sets up the camera pointer for this animated camera
+//
+// Parameters: name - tName of the camera to find in the inventory
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::LookupCamera()
+{
+ if( g_Camera == NULL )
+ {
+ if( g_CameraName.GetUID() != static_cast< tUID >( 0 ) )
+ {
+ tCamera* found = p3d::find< tCamera >( g_CameraName.GetUID() );
+ if( found != NULL )
+ {
+ tRefCounted::Assign( g_Camera, found );
+ }
+ }
+ }
+}
+
+//=============================================================================
+// AnimatedCam::LookupMulticontroller
+//=============================================================================
+// Description: finds the multicontroller in the inventory
+//
+// Parameters: none
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::LookupMulticontroller()
+{
+ if( g_Multicontroller == NULL )
+ {
+ if( g_MulticontrollerName.GetUID() != static_cast< tUID >( 0 ) )
+ {
+ tMultiController* found = p3d::find< tMultiController >( g_MulticontrollerName.GetUID() );
+ if( found != NULL )
+ {
+ tRefCounted::Assign( g_Multicontroller, found );
+ }
+ Reset();
+ }
+ }
+}
+
+//=============================================================================
+// AnimatedCam::OnInit
+//=============================================================================
+// Description: called when the camera is started
+//
+// Parameters: NONE
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::OnInit()
+{
+ LetterBoxStart();
+ InitMyController();
+ InputManager::GetInstance()->SetGameState( Input::ACTIVE_ANIM_CAM );
+}
+
+//=============================================================================
+// AnimatedCam::OnShutdown
+//=============================================================================
+// Description: called when the camera is deactivated
+//
+// Parameters: NONE
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::OnShutdown()
+{
+ LetterBoxStop();
+ InputManager::GetInstance()->SetGameState( Input::DEACTIVE_ANIM_CAM );
+ GetEventManager()->TriggerEvent( EVENT_ANIMATED_CAM_SHUTDOWN );
+ m_NextCameraType = INVALID;
+}
+
+//=============================================================================
+// AnimatedCam::Reset
+//=============================================================================
+// Description: resets the camera to the start of the animation
+//
+// Parameters: NONE
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::Reset()
+{
+ if( g_Multicontroller != NULL )
+ {
+ g_Multicontroller->Reset();
+ }
+ g_TriggeredNextCamera = false;
+}
+
+//=============================================================================
+// AnimatedCam::SetCamera
+//=============================================================================
+// Description: sets up the camera pointer for this animated camera
+//
+// Parameters: name - tName of the camera to find in the inventory
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::SetCamera( tName name )
+{
+ // tRefCounted::Release( g_Camera );
+ g_CameraName = name;
+ if( g_CameraName.GetUID() == static_cast< tUID >( 0 ) )
+ {
+ SetCameraSwitchPending( false );
+ }
+ else
+ {
+ SetCameraSwitchPending( true );
+ }
+}
+
+//=============================================================================
+// AnimatedCam::SetMulticontroller
+//=============================================================================
+// Description: sets up the multicontroller pointer for this animated camera
+//
+// Parameters: name - tName of the camera to find in the inventory
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::SetMulticontroller( tName name )
+{
+ //tRefCounted::Release( g_Multicontroller );
+ g_MulticontrollerName = name;
+ if( g_MulticontrollerName.GetUID() == static_cast< tUID >( 0 ) )
+ {
+ SetCameraSwitchPending( false );
+ }
+ else
+ {
+ SetCameraSwitchPending( true );
+ }
+}
+
+//=============================================================================
+// AnimatedCam::SetMissionStartCamera
+//=============================================================================
+// Description: sets up the camera pointer for this animated camera
+//
+// Parameters: name - tName of the camera to find in the inventory
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::SetMissionStartCamera( tName name )
+{
+ g_MissionStartCameraName = name;
+}
+
+//=============================================================================
+// AnimatedCam::SetMissionStartMulticontroller
+//=============================================================================
+// Description: sets up the multicontroller pointer for this animated camera
+//
+// Parameters: name - tName of the camera to find in the inventory
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::SetMissionStartMulticontroller( tName name )
+{
+ g_MissionStartMulticontrollerName = name;
+}
+
+
+//=============================================================================
+// AnimatedCam::SetNextCameraType
+//=============================================================================
+// Description: sets up the camera that will get used after this camera is done
+//
+// Parameters: type - what kind of camera should we use
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::SetNextCameraType( const SuperCam::Type type )
+{
+ rAssert( type != ANIMATED_CAM );
+ m_NextCameraType = type;
+ //m_NextCameraType = SuperCam::DEFAULT_CAM;
+}
+
+//=============================================================================
+// AnimatedCam::SetTarget
+//=============================================================================
+// Description: sets the target (actually sets it for the next camera in the
+// chain
+//
+// Parameters: target - the target for the camera to look at
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::SetTarget( ISuperCamTarget* target )
+{
+ //nothing
+}
+
+//=============================================================================
+// AnimatedCam::Skip
+//=============================================================================
+// Description: skips the animated camera
+//
+// Parameters: NONE
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::Skip()
+{
+ if( g_AllowSkipping )
+ {
+ if( g_Multicontroller != NULL )
+ {
+ g_Multicontroller->Advance( 20000.0f );
+ }
+ AllowSkipping( SKIPPING_DEFAULT );
+ }
+}
+
+//=============================================================================
+// AnimatedCam::TriggerMissionStartCamera
+//=============================================================================
+// Description: switches to the mission start camera
+//
+// Parameters: NONE
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::TriggerMissionStartCamera()
+{
+ SetCamera( g_MissionStartCameraName );
+ SetMulticontroller( g_MissionStartMulticontrollerName );
+ g_MissionStartCameraName = "";
+ g_MissionStartMulticontrollerName = "";
+}
+
+//=============================================================================
+// AnimatedCam::SetCameraSwitchPending
+//=============================================================================
+// Description: changes the setting on the camera switch flag
+//
+// Parameters: pending - the new state of the flag
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::SetCameraSwitchPending( const bool pending )
+{
+ g_CameraSwitchPending = pending;
+}
+
+//=============================================================================
+// AnimatedCam::SetCameraTransitionFlags
+//=============================================================================
+// Description: Overrides the default camera transition flags
+//
+// Parameters: ( int flags )
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::SetCameraTransitionFlags( int flags )
+{
+ g_CameraTransitionFlags = flags;
+}
+
+//=============================================================================
+// AnimatedCam::SupressNextLetterbox
+//=============================================================================
+// Description: won't trigger the letterbox next time
+//
+// Parameters:
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::SupressNextLetterbox()
+{
+ g_SurpressNextLetterbox = true;
+}
+
+//=============================================================================
+// AnimatedCam::Update
+//=============================================================================
+// Description: called to animate the camera
+//
+// Parameters: ( unsigned int milliseconds
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedCam::Update( unsigned int milliseconds )
+{
+ if( g_TriggeredNextCamera )
+ {
+ return;
+ }
+ if( g_Multicontroller != NULL )
+ {
+ g_Multicontroller->Advance( static_cast< float >( milliseconds ) );
+
+ InteriorManager* im = GetInteriorManager();
+
+ //This is a last chance test to make sure that we do not play cameras
+ //in interiors.
+ if( g_Multicontroller->LastFrameReached() ||
+ (im && ( im->IsEntering() || im->IsInside() || im->IsExiting() ) ) )
+ {
+ //
+ // The animation has looped
+ //
+ if( g_TriggeredNextCamera == false )
+ {
+ int flags = DEFAULT_TRANSITION_FLAGS;
+
+ if ( 0 != g_CameraTransitionFlags )
+ {
+ //Override the flags.
+ flags = g_CameraTransitionFlags;
+ }
+
+ rAssert( m_NextCameraType != NUM_TYPES );
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( m_NextCameraType, flags, 3000 );
+ g_TriggeredNextCamera = true;
+
+ //Reset the flags override.
+ g_CameraTransitionFlags = 0;
+
+ tRefCounted::Release( g_Multicontroller );
+ tRefCounted::Release( g_Camera );
+ AllowSkipping( SKIPPING_DEFAULT );
+ }
+ }
+ else
+ {
+ //
+ // Update the camera values
+ //
+ rmt::Vector position;
+ rmt::Vector target;
+ g_Camera->GetWorldPosition( &position );
+ g_Camera->GetWorldLookAtDirection( &target );
+
+ static rmt::Vector oldPosition = position;
+ static float maxDistance = 30;
+
+ target *= 10.0f;
+ target += position;
+ SetCameraValues( milliseconds, position, target);
+ }
+ }
+ else
+ {
+ //
+ // We never found the multicontroller in the inventory
+ //
+ int flags = DEFAULT_TRANSITION_FLAGS;
+
+ if ( 0 != g_CameraTransitionFlags )
+ {
+ //Override the flags.
+ flags = g_CameraTransitionFlags;
+ }
+
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( SuperCam::DEFAULT_CAM, flags, 0 );
+ g_TriggeredNextCamera = true;
+
+ //Reset the flags override.
+ g_CameraTransitionFlags = 0;
+
+ tRefCounted::Release( g_Multicontroller );
+ tRefCounted::Release( g_Camera );
+
+ //
+ // Since our camera system doesn't use regular p3d cameras, i have to fake
+ // the following information
+ //
+ if( g_Camera == NULL )
+ {
+ //this is a bad bad case to ever get into
+ rWarningMsg( false, "Camera could not be loaded" );
+ //SetCameraValues( milliseconds, rmt::Vector( 0, 0, 0 ), rmt::Vector( 1, 0, 0 ) );
+ }
+
+ }
+}
diff --git a/game/code/camera/animatedcam.h b/game/code/camera/animatedcam.h
new file mode 100644
index 0000000..a714e25
--- /dev/null
+++ b/game/code/camera/animatedcam.h
@@ -0,0 +1,89 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: animated.h
+//
+// Description: camers used for animated camera transitions
+//
+// History: 21/01/2003 + Created -- Ian Gipson
+//
+//=============================================================================
+
+#ifndef ANIMATEDCAMERA_H
+#define ANIMATEDCAMERA_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+#include <events/eventlistener.h>
+#include <input/mappable.h>
+
+//========================================
+// Forward References
+//========================================
+class ISuperCamTarget;
+class tMultiController;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class AnimatedCam :
+ public SuperCam
+{
+public:
+ AnimatedCam();
+ virtual ~AnimatedCam();
+
+ //Update: Called when you want the super cam to update its state.
+ void Update( unsigned int milliseconds );
+ void Abort();
+ static void AllowSkipping( const bool skippingAllowed );
+ static void CheckPendingCameraSwitch();
+ static void ClearCamera();
+ const char* const GetName() const;
+ Type GetType();
+ virtual void OnInit();
+ virtual void OnShutdown();
+ static void Reset();
+ static void SetCamera( tName name );
+ static void SetCameraTransitionFlags( int flags );
+ static void SetMulticontroller( tName name );
+ static void SetMissionStartCamera( tName name );
+ static void SetMissionStartMulticontroller( tName name );
+ void SetNextCameraType( const SuperCam::Type type );
+ void SetTarget( ISuperCamTarget* target );
+ static void Skip();
+ static void SupressNextLetterbox();
+ static void TriggerMissionStartCamera();
+
+ #ifdef DEBUGWATCH
+ virtual const char* GetWatcherName() const;
+ #endif
+
+
+protected:
+ static bool CameraSwitchPending();
+ static void LookupCamera();
+ static void LookupMulticontroller();
+ virtual void LetterBoxStart();
+ virtual void LetterBoxStop();
+ static void SetCameraSwitchPending( const bool pending );
+
+ SuperCam::Type m_NextCameraType;
+
+ //Prevent wasteful constructor creation.
+ AnimatedCam( const AnimatedCam& AnimatedCam );
+ AnimatedCam& operator=( const AnimatedCam& AnimatedCam );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //ANIMATEDCAMERA_H
diff --git a/game/code/camera/bumpercam.cpp b/game/code/camera/bumpercam.cpp
new file mode 100644
index 0000000..01344bb
--- /dev/null
+++ b/game/code/camera/bumpercam.cpp
@@ -0,0 +1,293 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: bumpercam.cpp
+//
+// Description: Implement BumperCam
+//
+// History: 24/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+//These are included in the precompiled headers on XBOX and GC
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <radmath/radmath.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/BumperCam.h>
+#include <camera/isupercamtarget.h>
+
+#include <worldsim/redbrick/vehicle.h>
+
+#include <cheats/cheatinputsystem.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//That;
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// BumperCam::BumperCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+BumperCam::BumperCam() :
+ mTarget( NULL )
+{
+ mGroundOffset.Set( 0.0f, 0.0f, 0.0f );
+}
+
+//==============================================================================
+// BumperCam::~BumperCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+BumperCam::~BumperCam()
+{
+}
+
+
+//=============================================================================
+// BumperCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void BumperCam::Update( unsigned int milliseconds )
+{
+ rAssertMsg( mTarget, "The BumperCam needs a target!" );
+
+ if ( !mTarget->IsCar() )
+ {
+ return;
+ }
+
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ SetFlag( (Flag)CUT, false );
+ }
+
+ //Reset the FOV.
+ if ( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_SPEED_CAM ) )
+ {
+ SetFOV( 1.608495f );
+ }
+ else
+ {
+ SetFOV( mData.GetFOV() );
+ }
+
+ if ( GetFlag((Flag)FIRST_TIME ) )
+ {
+ rAssert( mTarget->IsCar() );
+
+ rAssert( dynamic_cast< Vehicle* > ( mTarget ) != NULL );
+ Vehicle* targV = static_cast<Vehicle*>(mTarget);
+ rAssert( targV );
+
+ float halfWheelBase = targV->mWheelBase * 0.5f;
+ float top = targV->GetExtents().y;
+ float front = halfWheelBase - GetNearPlane();
+
+ top += 0.3f;
+
+ mData.SetFrontPosition( rmt::Vector( 0.0f, top, front ) );
+ mData.SetFrontTarget( rmt::Vector( 0.0f, top, front + 2.0f ) );
+
+ float back = -halfWheelBase + GetNearPlane();
+
+ top -= 0.3f;
+
+ mData.SetBackPosition( rmt::Vector( 0.0f, top, back ) );
+ mData.SetBackTarget( rmt::Vector( 0.0f, top, back - 2.0f ) );
+
+ SetFlag( (Flag)FIRST_TIME, false );
+ }
+
+ bool lookBack = GetFlag( (Flag)LOOK_BACK );
+
+ if ( lookBack )
+ {
+ SetFlag( (Flag)LOOK_BACK, false );
+ }
+
+ //--------- Buid a rod for the camera
+
+ //This is where the camera is looking, not it's position.
+ rmt::Vector desiredTarget;
+
+ if ( lookBack )
+ {
+ //Use the back view.
+ mData.GetBackTarget( &desiredTarget );
+ }
+ else
+ {
+ //Use the front view.
+ mData.GetFrontTarget( &desiredTarget );
+ }
+
+ rmt::Matrix mat;
+ rmt::Vector targetHeading, targetVUP;
+ mTarget->GetHeading( &targetHeading );
+ mTarget->GetVUP( &targetVUP );
+ mat.Identity();
+ mat.FillHeading( targetHeading, targetVUP );
+
+ //Put the desiredTarget in the target space
+ desiredTarget.Transform( mat );
+
+ rmt::Vector desiredPosition;
+ if ( lookBack )
+ {
+ mData.GetBackPosition( &desiredPosition );
+ }
+ else
+ {
+ mData.GetFrontPosition( &desiredPosition );
+ }
+
+ //Put desiredPosition in the space of the target.
+ desiredPosition.Transform( mat );
+
+ //--------- Set the position and target of the camera
+
+ rmt::Vector targetPosition;
+ mTarget->GetPosition( &targetPosition );
+
+ desiredPosition.Add( targetPosition );
+ desiredTarget.Add( targetPosition );
+
+ SetCameraValues( milliseconds, desiredPosition, desiredTarget, &targetVUP );
+}
+
+//=============================================================================
+// BumperCam::UpdateForPhysics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void BumperCam::UpdateForPhysics( unsigned int milliseconds )
+{
+ rmt::Vector position, target, vup;
+ GetPosition( &position );
+ GetTarget( &target );
+ mTarget->GetVUP( &vup );
+
+ // Add ground collision.
+ position.Add( mGroundOffset );
+
+ SetCameraValues( 0, position, target, &vup ); //No extra transition
+}
+
+//=============================================================================
+// BumperCam::LoadSettings
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned char* settings )
+//
+// Return: void
+//
+//=============================================================================
+void BumperCam::LoadSettings( unsigned char* settings )
+{
+}
+
+//=============================================================================
+// BumperCam::SetCollisionOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
+//
+// Return: void
+//
+//=============================================================================
+void BumperCam::SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
+{
+ //Only colliding with ground.
+ mGroundOffset.y = groundOffset.y;
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// BumperCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void BumperCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\Bumper", GetPlayerID() );
+
+ radDbgWatchAddVector( &mData.mFrontPos.x, "Front Position", nameSpace, NULL, NULL, -10.0f, 10.0f );
+ radDbgWatchAddVector( &mData.mFrontTarg.x, "Front Target", nameSpace, NULL, NULL, -10.0f, 10.0f );
+ radDbgWatchAddVector( &mData.mBackPos.x, "Back Position", nameSpace, NULL, NULL, -10.0f, 10.0f );
+ radDbgWatchAddVector( &mData.mBackTarg.x, "Back Target", nameSpace, NULL, NULL, -10.0f, 10.0f );
+#endif
+}
+
+//=============================================================================
+// BumperCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void BumperCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mData.mFrontPos.x );
+ radDbgWatchDelete( &mData.mFrontTarg.x );
+ radDbgWatchDelete( &mData.mBackPos.x );
+ radDbgWatchDelete( &mData.mBackTarg.x );
+#endif
+}
diff --git a/game/code/camera/bumpercam.h b/game/code/camera/bumpercam.h
new file mode 100644
index 0000000..febf608
--- /dev/null
+++ b/game/code/camera/bumpercam.h
@@ -0,0 +1,172 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: bumpercam.h
+//
+// Description: Blahblahblah
+//
+// History: 24/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef BUMPERCAM_H
+#define BUMPERCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+#include <camera/bumpercamdata.h>
+
+//========================================
+// Forward References
+//========================================
+class ISuperCamTarget;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class BumperCam : public SuperCam
+{
+public:
+
+ enum BumperFlag
+ {
+ READY = SUPERCAM_END //This is to carry on from the supercam flags
+ };
+
+ BumperCam();
+ virtual ~BumperCam();
+
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds );
+ virtual void UpdateForPhysics( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ //This loads the off-line created settings for the camera.
+ //It is passed in as a byte stream of some data of known size.
+ virtual void LoadSettings( unsigned char* settings );
+
+ virtual Type GetType();
+
+ //These are for favourable support of this command
+ virtual void SetTarget( ISuperCamTarget* target );
+ virtual void AddTarget( ISuperCamTarget* target );
+
+ unsigned int GetNumTargets() const;
+
+ //Support for colliding with the world.
+ void SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset );
+ float GetCollisionRadius() const { return GetNearPlane(); };
+
+private:
+
+ //These functions are to allow real-time control of the settings of
+ //the supercam.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+ BumperCamData mData;
+ ISuperCamTarget* mTarget;
+
+ rmt::Vector mGroundOffset;
+
+ //Prevent wasteful constructor creation.
+ BumperCam( const BumperCam& bumpercam );
+ BumperCam& operator=( const BumperCam& bumpercam );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// BumperCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+inline const char* const BumperCam::GetName() const
+{
+ return "BUMPER_CAM";
+}
+
+//=============================================================================
+// BumperCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type BumperCam::GetType()
+{
+ return BUMPER_CAM;
+}
+
+//=============================================================================
+// BumperCam::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void BumperCam::SetTarget( ISuperCamTarget* target )
+{
+ mTarget = target;
+}
+
+//=============================================================================
+// BumperCam::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void BumperCam::AddTarget( ISuperCamTarget* target )
+{
+ rAssertMsg( false, "Only call SetTarget on the BumperCam" );
+}
+
+//=============================================================================
+// BumperCam::GetNumTargets
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int BumperCam::GetNumTargets() const
+{
+ if ( mTarget )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+
+#endif //BUMPERCAM_H
diff --git a/game/code/camera/bumpercamdata.h b/game/code/camera/bumpercamdata.h
new file mode 100644
index 0000000..324683e
--- /dev/null
+++ b/game/code/camera/bumpercamdata.h
@@ -0,0 +1,237 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: bumpercamdata.h
+//
+// Description: Blahblahblah
+//
+// History: 24/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef BUMPERCAMDATA_H
+#define BUMPERCAMDATA_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class BumperCamData
+{
+public:
+ BumperCamData();
+ virtual ~BumperCamData() {};
+
+ void GetFrontPosition( rmt::Vector* front );
+ void SetFrontPosition( rmt::Vector front );
+ void GetFrontTarget( rmt::Vector* front );
+ void SetFrontTarget( rmt::Vector front );
+
+ void GetBackPosition( rmt::Vector* back );
+ void SetBackPosition( rmt::Vector back );
+ void GetBackTarget( rmt::Vector* back );
+ void SetBackTarget( rmt::Vector back );
+
+ float GetFOV() const;
+ void SetFOV( float fov );
+
+ rmt::Vector mFrontPos;
+ rmt::Vector mFrontTarg;
+ rmt::Vector mBackPos;
+ rmt::Vector mBackTarg;
+
+ float mFOV;
+
+private:
+ //Prevent wasteful constructor creation.
+ BumperCamData( const BumperCamData& bumpercamdata );
+ BumperCamData& operator=( const BumperCamData& bumpercamdata );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// BumperCamData::BumperCamData
+//=============================================================================
+// Description: Constructor
+//
+// Parameters: ()
+//
+// Return: BumperCamData
+//
+//=============================================================================
+inline BumperCamData::BumperCamData() :
+ mFOV( 1.5707f )
+{
+ mFrontPos.Set( 0.0f, 0.0f, 2.3f );
+ mFrontTarg.Set( 0.0f, 0.0f, 2.8f );
+ mBackPos.Set( 0.0f, 0.0f, -2.22f );
+ mBackTarg.Set( 0.0f, 0.0f, -2.8f );
+}
+
+//=============================================================================
+// BumperCamData::GetFrontPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* front )
+//
+// Return: void
+//
+//=============================================================================
+inline void BumperCamData::GetFrontPosition( rmt::Vector* front )
+{
+ *front = mFrontPos;
+}
+
+//=============================================================================
+// BumperCamData::SetFrontPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector front )
+//
+// Return: void
+//
+//=============================================================================
+inline void BumperCamData::SetFrontPosition( rmt::Vector front )
+{
+ mFrontPos = front;
+}
+
+//=============================================================================
+// BumperCamData::GetFrontTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* front )
+//
+// Return: void
+//
+//=============================================================================
+inline void BumperCamData::GetFrontTarget( rmt::Vector* front )
+{
+ *front = mFrontTarg;
+}
+
+//=============================================================================
+// BumperCamData::SetFrontTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector front )
+//
+// Return: void
+//
+//=============================================================================
+inline void BumperCamData::SetFrontTarget( rmt::Vector front )
+{
+ mFrontTarg = front;
+}
+
+//=============================================================================
+// BumperCamData::GetBackPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* back )
+//
+// Return: void
+//
+//=============================================================================
+inline void BumperCamData::GetBackPosition( rmt::Vector* back )
+{
+ *back = mBackPos;
+}
+
+//=============================================================================
+// BumperCamData::SetBackPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector back )
+//
+// Return: void
+//
+//=============================================================================
+inline void BumperCamData::SetBackPosition( rmt::Vector back )
+{
+ mBackPos = back;
+}
+
+//=============================================================================
+// BumperCamData::GetBackTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* back )
+//
+// Return: void
+//
+//=============================================================================
+inline void BumperCamData::GetBackTarget( rmt::Vector* back )
+{
+ *back = mBackTarg;
+}
+
+//=============================================================================
+// BumperCamData::SetBackTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector back )
+//
+// Return: void
+//
+//=============================================================================
+inline void BumperCamData::SetBackTarget( rmt::Vector back )
+{
+ mBackTarg = back;
+}
+
+//=============================================================================
+// BumperCamData::GetFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float BumperCamData::GetFOV() const
+{
+ return mFOV;
+}
+
+//=============================================================================
+// BumperCamData::SetFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float fov )
+//
+// Return: void
+//
+//=============================================================================
+inline void BumperCamData::SetFOV( float fov )
+{
+ mFOV = fov;
+}
+
+#endif //BUMPERCAMDATA_H
diff --git a/game/code/camera/burnoutcam.cpp b/game/code/camera/burnoutcam.cpp
new file mode 100644
index 0000000..b2bef56
--- /dev/null
+++ b/game/code/camera/burnoutcam.cpp
@@ -0,0 +1,241 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: BurnoutCam.cpp
+//
+// Description: Implement BurnoutCam
+//
+// History: 10/7/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <radmath/radmath.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/BurnoutCam.h>
+#include <camera/supercamconstants.h>
+#include <input/inputmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+#ifndef DEBUGWATCH
+const float BURNOUT_CAM_INCREMENT = 0.0174f; //One degree
+//const float BURNOUT_CAM_DIST = 1.0f;
+const float BURNOUT_CAM_DIST = 0.1f;
+#else
+float BURNOUT_CAM_INCREMENT = 0.0174f;
+//float BURNOUT_CAM_DIST = 1.0f;
+float BURNOUT_CAM_DIST = 0.1f;
+#endif
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// BurnoutCam::BurnoutCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+BurnoutCam::BurnoutCam() :
+ mRotation( -1.061069f ), //Along the side
+ mElevation( 1.29794f ),
+ mMagnitude( 5.7266f )
+{
+ mIm = InputManager::GetInstance();
+
+ mTargetOffset.Set( 0.0f, 0.0f, 0.0f );
+}
+
+//==============================================================================
+// BurnoutCam::~BurnoutCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+BurnoutCam::~BurnoutCam()
+{
+}
+
+//=============================================================================
+// BurnoutCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void BurnoutCam::Update( unsigned int milliseconds )
+{
+ rAssert( mTarget );
+
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ //Reset the FOV.
+ SetFOV( SUPERCAM_FOV );//DEFAULT_FOV );
+ SetFlag( (Flag)CUT, false );
+ }
+
+ //This is to adjust interpolation when we're running substeps.
+ float timeMod = 1.0f;
+
+ timeMod = (float)milliseconds / 16.0f;
+
+ float xAxis = mIm->GetValue( s_secondaryControllerID, InputManager::LeftStickX );
+ float yAxis = mIm->GetValue( s_secondaryControllerID, InputManager::LeftStickY );
+ float zAxis = mIm->GetValue( s_secondaryControllerID, InputManager::LeftStickY );
+ float zToggle = mIm->GetValue( s_secondaryControllerID, InputManager::AnalogR1 );
+
+ if ( rmt::Fabs( zToggle ) > STICK_DEAD_ZONE && rmt::Fabs( zToggle ) <= 1.0f )
+ {
+ //zToggled
+ yAxis = 0.0f;
+ }
+ else
+ {
+ zAxis = 0.0f;
+ }
+
+
+ mRotation += ( xAxis * BURNOUT_CAM_INCREMENT * timeMod );
+ mElevation -= ( yAxis * BURNOUT_CAM_INCREMENT * timeMod );
+ mMagnitude -= ( zAxis * BURNOUT_CAM_DIST * timeMod );
+
+ if ( mElevation < 0.001f )
+ {
+ mElevation = 0.001f;
+ }
+ else if ( mElevation > rmt::PI - 0.05f )
+ {
+ mElevation = rmt::PI - 0.05f;
+ }
+
+ if ( mMagnitude < 2.0f )
+ {
+ mMagnitude = 2.0f;
+ }
+
+ //This positions itself always relative to the target.
+ rmt::Vector rod;
+ rmt::SphericalToCartesian( mMagnitude, mRotation, mElevation, &rod.x, &rod.z, &rod.y );
+
+ //This is the position of the target.
+ rmt::Vector targetPos;
+ mTarget->GetPosition( &targetPos );
+
+ rmt::Vector desiredPos, desiredTarget;
+
+ //Put this local to the targets heading.
+ rmt::Vector heading;
+ mTarget->GetHeading( &heading );
+
+ rmt::Vector vup;
+ mTarget->GetVUP( &vup );
+
+ rmt::Matrix mat;
+ mat.Identity();
+
+ mat.FillHeading( heading, vup );
+
+ rod.Transform( mat );
+
+ desiredPos.Add( rod, targetPos );
+
+ //Calculate the target of the camera.
+ if ( GetFlag( (Flag)FIRST_TIME ) )
+ {
+ //Okay, if this is the first time we're going in, let's figure out where the camera target is
+ //relative to the position of the target object.
+ rmt::Vector camTargetPos;
+ GetTarget( &camTargetPos );
+
+ mTargetOffset.Sub( camTargetPos, targetPos );
+
+ rmt::Matrix invMat;
+ invMat = mat;
+ invMat.Invert();
+
+ mTargetOffset.Transform( invMat );
+
+ SetFlag( (Flag)FIRST_TIME, false );
+ }
+
+ desiredTarget = mTargetOffset;
+ desiredTarget.Transform( mat );
+ desiredTarget.Add( targetPos );
+
+ SetCameraValues( milliseconds, desiredPos, desiredTarget );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// BurnoutCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void BurnoutCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\Burnout", GetPlayerID() );
+
+ radDbgWatchAddFloat( &mElevation, "Elevation", nameSpace, NULL, NULL, 0.001f, rmt::PI - 0.05f );
+ radDbgWatchAddFloat( &mRotation, "Rotation", nameSpace, NULL, NULL, 0.0f, rmt::PI_2 );
+ radDbgWatchAddFloat( &mMagnitude, "Magnitude", nameSpace, NULL, NULL, 1.0f, 10.0f );
+#endif
+}
+
+//=============================================================================
+// BurnoutCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void BurnoutCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mElevation );
+ radDbgWatchDelete( &mRotation );
+ radDbgWatchDelete( &mMagnitude );
+#endif
+} \ No newline at end of file
diff --git a/game/code/camera/burnoutcam.h b/game/code/camera/burnoutcam.h
new file mode 100644
index 0000000..3cf0711
--- /dev/null
+++ b/game/code/camera/burnoutcam.h
@@ -0,0 +1,162 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: burnoutcam.h
+//
+// Description: Blahblahblah
+//
+// History: 10/7/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef BURNOUTCAM_H
+#define BURNOUTCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+#include <camera/isupercamtarget.h>
+
+//========================================
+// Forward References
+//========================================
+class InputManager;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class BurnoutCam : public SuperCam
+{
+public:
+ BurnoutCam();
+ virtual ~BurnoutCam();
+
+ //Update: Called when you want the super cam to update its state.
+ void Update( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ const char* const GetName() const;
+
+ Type GetType();
+
+ //These are for favourable support of this command
+ void SetTarget( ISuperCamTarget* target );
+ void AddTarget( ISuperCamTarget* target );
+
+ unsigned int GetNumTargets() const;
+
+protected:
+
+ float mRotation;
+ float mElevation;
+ float mMagnitude;
+
+private:
+
+ ISuperCamTarget* mTarget;
+
+ InputManager* mIm;
+
+ rmt::Vector mTargetOffset;
+
+ //These functions are to allow real-time control of the settings of
+ //the supercam.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+ //Prevent wasteful constructor creation.
+ BurnoutCam( const BurnoutCam& burnoutcam );
+ BurnoutCam& operator=( const BurnoutCam& burnoutcam );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+//=============================================================================
+// BurnoutCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+inline const char* const BurnoutCam::GetName() const
+{
+ return "BURNOUT_CAM";
+}
+
+//=============================================================================
+// DebugCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type BurnoutCam::GetType()
+{
+ return BURNOUT_CAM;
+}
+
+//=============================================================================
+// BurnoutCam::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void BurnoutCam::SetTarget( ISuperCamTarget* target )
+{
+ mTarget = target;
+}
+
+//=============================================================================
+// BurnoutCam::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void BurnoutCam::AddTarget( ISuperCamTarget* target )
+{
+ rAssertMsg( false, "Only call SetTarget on the BurnoutCam" );
+}
+
+//=============================================================================
+// BurnoutCam::GetNumTargets
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int BurnoutCam::GetNumTargets() const
+{
+ if ( mTarget )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif //BURNOUTCAM_H
diff --git a/game/code/camera/chasecam.cpp b/game/code/camera/chasecam.cpp
new file mode 100644
index 0000000..6e6b23b
--- /dev/null
+++ b/game/code/camera/chasecam.cpp
@@ -0,0 +1,269 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: chasecam.cpp
+//
+// Description: Implement ChaseCam
+//
+// History: 24/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/ChaseCam.h>
+#include <camera/isupercamtarget.h>
+#include <camera/supercamconstants.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ChaseCam::ChaseCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ChaseCam::ChaseCam() :
+ mTarget( NULL ),
+ mFOVDelta( 0.0f )
+{
+ mPosition.Set( 0.0f, 0.0f, 0.0f );
+ mPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+ mTargetPos.Set( 0.0f, 0.0f, 0.0f );
+ mTargetDelta.Set( 0.0f, 0.0f, 0.0f );
+}
+
+//==============================================================================
+// ChaseCam::~ChaseCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ChaseCam::~ChaseCam()
+{
+}
+
+//=============================================================================
+// ChaseCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void ChaseCam::Update( unsigned int milliseconds )
+{
+ rAssertMsg( mTarget, "The ChaseCam needs a target!" );
+
+ //This is to adjust interpolation when we're running substeps.
+ float timeMod = (float)milliseconds / EXPECTED_FRAME_RATE;
+
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ DoCameraCut();
+ }
+
+ rmt::Vector oldCamPos;
+ GetPosition( &oldCamPos );
+
+ //--------- Buid a rod for the camera
+
+ rmt::Vector rod;
+
+ mData.GetRod( &rod );
+
+ //This makes the camera act more like a hellicopter camera
+ rmt::Matrix mat;
+ rmt::Vector targetHeading;
+ mTarget->GetHeading( &targetHeading );
+ mat.Identity();
+ mat.FillHeadingXZ( targetHeading );
+
+ rod.Transform( mat );
+
+ rmt::Vector targetPosition;
+ mTarget->GetPosition( &targetPosition );
+
+ rod.Add( targetPosition );
+
+
+ //--------- Set the position and target of the camera
+
+ rmt::Vector desiredPosition = rod;
+ rmt::Vector desiredTarget = targetPosition;
+
+ float posLag = mData.GetPositionLag() * timeMod;
+ CLAMP_TO_ONE(posLag);
+
+ MotionCubic( &mPosition.x, &mPositionDelta.x, desiredPosition.x, posLag );
+ MotionCubic( &mPosition.y, &mPositionDelta.y, desiredPosition.y, posLag );
+ MotionCubic( &mPosition.z, &mPositionDelta.z, desiredPosition.z, posLag );
+
+ float targLag = mData.GetTargetLag() * timeMod;
+ CLAMP_TO_ONE(targLag);
+
+ MotionCubic( &mTargetPos.x, &mTargetDelta.x, desiredTarget.x, targLag );
+ MotionCubic( &mTargetPos.y, &mTargetDelta.y, desiredTarget.y, targLag );
+ MotionCubic( &mTargetPos.z, &mTargetDelta.z, desiredTarget.z, targLag );
+
+ rmt::Vector diff;
+ diff.Sub( mPosition, oldCamPos );
+ float speed = diff.Magnitude();
+
+ //Let's goof with the field of view.
+ float diffFOV = mData.GetMaxFOV() - mData.GetMinFOV();
+ float maxSpeed = mData.GetMaxSpeed();
+
+ if ( speed > maxSpeed )
+ {
+ speed = maxSpeed;
+ }
+
+ //The closer we get to the max speed, the wider the FOV.
+ float desiredFOV = mData.GetMinFOV() + (diffFOV * speed / maxSpeed);
+
+ float FOV = GetFOV();
+
+ float fovLag = mData.GetFOVLag() * timeMod;
+ CLAMP_TO_ONE(fovLag);
+
+ MotionCubic( &FOV, &mFOVDelta, desiredFOV, fovLag );
+
+ SetFOV( FOV );
+
+ SetCameraValues( milliseconds, mPosition, mTargetPos );
+}
+
+//=============================================================================
+// ChaseCam::LoadSettings
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned char* settings )
+//
+// Return: void
+//
+//=============================================================================
+void ChaseCam::LoadSettings( unsigned char* settings )
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// ChaseCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ChaseCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\Chase", GetPlayerID() );
+
+ radDbgWatchAddVector( &mData.mRod.x, "Rod", nameSpace, NULL, NULL, -100.0f, 100.0f );
+ radDbgWatchAddFloat( &mData.mPositionLag, "Position Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &mData.mTargetLag, "Target Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &mData.mMinFOV, "Min FOV", nameSpace, NULL, NULL, 0.0f, rmt::PI );
+ radDbgWatchAddFloat( &mData.mMaxFOV, "Max FOV", nameSpace, NULL, NULL, 0.0f, rmt::PI );
+ radDbgWatchAddFloat( &mData.mMaxSpeed, "Max Speed", nameSpace, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &mData.mFOVLag, "FOV Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+#endif
+}
+
+//=============================================================================
+// ChaseCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ChaseCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mData.mRod.x );
+ radDbgWatchDelete( &mData.mPositionLag );
+ radDbgWatchDelete( &mData.mTargetLag );
+ radDbgWatchDelete( &mData.mMinFOV );
+ radDbgWatchDelete( &mData.mMaxFOV );
+ radDbgWatchDelete( &mData.mMaxSpeed );
+ radDbgWatchDelete( &mData.mFOVLag );
+#endif
+}
+
+//=============================================================================
+// ChaseCam::DoCameraCut
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ChaseCam::DoCameraCut()
+{
+ rmt::Vector rod;
+
+ mData.GetRod( &rod );
+
+ rmt::Vector targetPosition;
+ mTarget->GetPosition( &targetPosition );
+
+ rmt::Matrix mat;
+ rmt::Vector targetHeading;
+ mTarget->GetHeading( &targetHeading );
+ mat.Identity();
+ mat.FillHeadingXZ( targetHeading );
+
+ rod.Transform( mat );
+
+ mPosition.Add( rod, targetPosition );
+ mPositionDelta = mPosition;
+
+ SetFOV( SUPERCAM_FOV );//DEFAULT_FOV );
+ mFOVDelta = 0.0f;
+
+ SetFlag( (Flag)CUT, false );
+}
diff --git a/game/code/camera/chasecam.h b/game/code/camera/chasecam.h
new file mode 100644
index 0000000..f70e8e8
--- /dev/null
+++ b/game/code/camera/chasecam.h
@@ -0,0 +1,169 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: chasecam.h
+//
+// Description: Blahblahblah
+//
+// History: 24/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef CHASECAM_H
+#define CHASECAM_H
+
+//========================================
+// Nested Includes
+//========================================
+//These are included in the precompiled headers on XBOX and GC
+#include <radmath/radmath.hpp>
+
+#include <camera/supercam.h>
+#include <camera/ChaseCamData.h>
+
+//========================================
+// Forward References
+//========================================
+class ISuperCamTarget;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ChaseCam : public SuperCam
+{
+public:
+ ChaseCam();
+ virtual ~ChaseCam();
+
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ //This loads the off-line created settings for the camera.
+ //It is passed in as a byte stream of some data of known size.
+ virtual void LoadSettings( unsigned char* settings );
+
+ virtual Type GetType();
+
+ //These are for favourable support of this command
+ virtual void SetTarget( ISuperCamTarget* target );
+ virtual void AddTarget( ISuperCamTarget* target );
+
+ unsigned int GetNumTargets() const;
+
+private:
+ ISuperCamTarget* mTarget;
+ ChaseCamData mData;
+
+ rmt::Vector mPosition;
+ rmt::Vector mPositionDelta;
+ rmt::Vector mTargetPos;
+ rmt::Vector mTargetDelta;
+
+ float mFOVDelta;
+
+ //These functions are to allow real-time control of the settings of
+ //the supercam.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+ void DoCameraCut();
+
+ //Prevent wasteful constructor creation.
+ ChaseCam( const ChaseCam& chasecam );
+ ChaseCam& operator=( const ChaseCam& chasecam );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ChaseCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+inline const char* const ChaseCam::GetName() const
+{
+ return "CHASE_CAM";
+}
+
+//=============================================================================
+// ChaseCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type ChaseCam::GetType()
+{
+ return CHASE_CAM;
+}
+
+//=============================================================================
+// ChaseCam::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void ChaseCam::SetTarget( ISuperCamTarget* target )
+{
+ mTarget = target;
+}
+
+//=============================================================================
+// ChaseCam::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void ChaseCam::AddTarget( ISuperCamTarget* target )
+{
+ rAssertMsg( false, "Only call SetTarget on the ChaseCam" );
+}
+
+//=============================================================================
+// ChaseCam::GetNumTargets
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int ChaseCam::GetNumTargets() const
+{
+ if ( mTarget )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif //CHASECAM_H
diff --git a/game/code/camera/chasecamdata.h b/game/code/camera/chasecamdata.h
new file mode 100644
index 0000000..91c2168
--- /dev/null
+++ b/game/code/camera/chasecamdata.h
@@ -0,0 +1,309 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: chasecamdata.h
+//
+// Description: Blahblahblah
+//
+// History: 24/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef CHASECAMDATA_H
+#define CHASECAMDATA_H
+
+//========================================
+// Nested Includes
+//========================================
+//These are included in the precompiled headers on XBOX and GC
+#include <radmath/radmath.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ChaseCamData
+{
+public:
+ ChaseCamData();
+ virtual ~ChaseCamData() {};
+
+ void SetRod( rmt::Vector rod );
+ void GetRod( rmt::Vector* rod ) const;
+
+ void SetPositionLag( float lag );
+ float GetPositionLag() const;
+
+ void SetTargetLag( float lag );
+ float GetTargetLag() const;
+
+ void SetMinFOV( float min );
+ float GetMinFOV() const;
+
+ void SetMaxFOV( float max );
+ float GetMaxFOV() const;
+
+ void SetMaxSpeed( float speed );
+ float GetMaxSpeed() const;
+
+ void SetFOVLag( float lag );
+ float GetFOVLag() const;
+
+ rmt::Vector mRod;
+ float mPositionLag;
+ float mTargetLag;
+ float mMinFOV;
+ float mMaxFOV;
+ float mMaxSpeed;
+ float mFOVLag;
+
+private:
+ //Prevent wasteful constructor creation.
+ ChaseCamData( const ChaseCamData& chasecamdata );
+ ChaseCamData& operator=( const ChaseCamData& chasecamdata );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ChaseCamData::ChaseCamData
+//=============================================================================
+// Description: Constructor
+//
+// Parameters: ()
+//
+// Return: nothing
+//
+//=============================================================================
+inline ChaseCamData::ChaseCamData() :
+ mPositionLag( 0.01f ),
+ mTargetLag( 0.089f ),
+ mMinFOV( 0.3487f ), //Fudge...
+ mMaxFOV( 1.75079f ), //Fudge...
+ mMaxSpeed( 0.233f ),
+ mFOVLag( 0.05f )
+{
+ mRod.Set( 0.0f, 50.0f, -15.0f );
+}
+
+//=============================================================================
+// ChaseCamData::SetRod
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector rod )
+//
+// Return: void
+//
+//=============================================================================
+inline void ChaseCamData::SetRod( rmt::Vector rod )
+{
+ mRod = rod;
+}
+
+//=============================================================================
+// ChaseCamData::GetRod
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* rod )
+//
+// Return: void
+//
+//=============================================================================
+inline void ChaseCamData::GetRod( rmt::Vector* rod ) const
+{
+ *rod = mRod;
+}
+
+//=============================================================================
+// ChaseCamData::SetPositionLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void ChaseCamData::SetPositionLag( float lag )
+{
+ mPositionLag = lag;
+}
+
+//=============================================================================
+// ChaseCamData::GetPositionLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float ChaseCamData::GetPositionLag() const
+{
+ return mPositionLag;
+}
+
+//=============================================================================
+// ChaseCamData::SetTargetLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void ChaseCamData::SetTargetLag( float lag )
+{
+ mTargetLag = lag;
+}
+
+//=============================================================================
+// ChaseCamData::GetTargetLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float ChaseCamData::GetTargetLag() const
+{
+ return mTargetLag;
+}
+
+//=============================================================================
+// ChaseCamData::SetMinFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float min )
+//
+// Return: void
+//
+//=============================================================================
+inline void ChaseCamData::SetMinFOV( float min )
+{
+ mMinFOV = min;
+}
+
+//=============================================================================
+// ChaseCamData::GetMinFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float ChaseCamData::GetMinFOV() const
+{
+ return mMinFOV;
+}
+
+//=============================================================================
+// ChaseCamData::SetMaxFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float max )
+//
+// Return: void
+//
+//=============================================================================
+inline void ChaseCamData::SetMaxFOV( float max )
+{
+ mMaxFOV = max;
+}
+
+//=============================================================================
+// ChaseCamData::GetMaxFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float ChaseCamData::GetMaxFOV() const
+{
+ return mMaxFOV;
+}
+
+//=============================================================================
+// ChaseCamData::SetMaxSpeed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float speed )
+//
+// Return: void
+//
+//=============================================================================
+inline void ChaseCamData::SetMaxSpeed( float speed )
+{
+ mMaxSpeed = speed;
+}
+
+//=============================================================================
+// ChaseCamData::GetMaxSpeed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float ChaseCamData::GetMaxSpeed() const
+{
+ return mMaxSpeed;
+}
+
+//=============================================================================
+// ChaseCamData::SetFOVLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void ChaseCamData::SetFOVLag( float lag )
+{
+ mFOVLag = lag;
+}
+
+//=============================================================================
+// ChaseCamData::GetFOVLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float ChaseCamData::GetFOVLag() const
+{
+ return mFOVLag;
+}
+
+#endif //CHASECAMDATA_H
diff --git a/game/code/camera/conversationcam.cpp b/game/code/camera/conversationcam.cpp
new file mode 100644
index 0000000..5dc89e6
--- /dev/null
+++ b/game/code/camera/conversationcam.cpp
@@ -0,0 +1,766 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ConversationCam.cpp
+//
+// Description: Implement ConversationCam
+//
+// History: 24/04/2002 + Created -- Cary Brisebois, based from the Bumper Camera
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <algorithm>
+#include <meta/locator.h>
+#include <p3d/utility.hpp>
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <radmath/trig.hpp>
+#include <stdlib.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/ConversationCam.h>
+#include <camera/isupercamtarget.h>
+#include <meta/triggervolumetracker.h>
+#include <presentation/presentation.h>
+#include <worldsim/character/character.h>
+
+//========================================
+// Definitions
+//========================================
+static unsigned int FindConversationCam( const tName& name );
+static void InitializeCameraNames();
+static void CleanupCameraNames();
+int ConversationCam::sInstCount = 0;
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+static float sDistance = 5.0f;
+static float sAngle = 0.0f;
+static float sElevation = 2.0f;
+static float sFov = rmt::PI / 180.0f * 50.00f;
+static float sXOffset = 0.0f;
+static float sYOffset = 1.3f;
+static float sHeightAdjustment = 0.0f;
+static float sXOffsetPC = 0.0f;
+static float sXOffsetNPC = 0.0f;
+const float childHeightAdjustment = -0.4f;
+static rmt::Vector sPcOffset( 0.0f, 0.0f, 0.0f );
+static rmt::Vector sNpcOffset( 0.0f, 0.0f, 0.0f );
+
+static const unsigned int sMaxCameras = 8;
+static float sDistances [ sMaxCameras ] = { 5.00f, 5.00f, 6.20f, 6.20f };
+static float sElevations[ sMaxCameras ] = { 2.11f, 2.11f, 2.20f, 2.20f };
+static float sXOffsets [ sMaxCameras ] = { 0.00f, 0.00f, 1.06f, -1.06f };
+static float sYOffsets [ sMaxCameras ] = { 1.03f, 1.03f, 1.25f, 1.25f };
+static bool sPcFocus [ sMaxCameras ] = { false, false, false, false };
+static float sFovs [ sMaxCameras ] =
+{
+ 1.00f,
+ 1.00f,
+ 0.49f,
+ 0.49f,
+};
+static float sAngles [ sMaxCameras ] =
+{
+ 0.55f,
+ -0.55f,
+ 1.40f,
+ -1.40f,
+};
+static rmt::Vector sPcOffsets[ sMaxCameras ] =
+{
+ rmt::Vector( 0.00f, 0.00f, 0.00f ),
+ rmt::Vector( 0.00f, 0.00f, 0.00f ),
+ rmt::Vector( 0.00f, 0.00f, 0.00f ),
+ rmt::Vector( 0.00f, 0.00f, 0.00f )
+};
+static rmt::Vector sNpcOffsets[ sMaxCameras ] =
+{
+ rmt::Vector( 0.00f, 0.00f, 0.00f ),
+ rmt::Vector( 0.00f, 0.00f, 0.00f ),
+ rmt::Vector( -1.80f, 0.00f, -0.21f ),
+ rmt::Vector( 0.00f, 0.00f, -0.00f )
+};
+static tName* sCamNames;
+static unsigned int sCamPc = 999; //these numbers are overridden by the mission
+static unsigned int sCamNpc = 999;
+static bool sNpcIsChild;
+static bool sPcIsChild;
+static tName bestSideLocatorName;
+static Locator* bestSideLocator = NULL;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ConversationCam::ConversationCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ConversationCam::ConversationCam() :
+ mPosition( 0.0f, 0.0f, 0.0f ),
+ mTarget ( 0.0f, 0.0f, 0.0f ),
+ mTargetHeight( 1.0f ),
+ mPositionHeight( 1.5 )
+{
+ if( sCamNames == NULL )
+ {
+ InitializeCameraNames();
+ }
+
+ //Only have 2 targets.
+ mTargets[ 0 ] = NULL;
+ mTargets[ 1 ] = NULL;
+
+ sInstCount++;
+}
+
+//==============================================================================
+// ConversationCam::~ConversationCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ConversationCam::~ConversationCam()
+{
+ sInstCount--;
+ if (sInstCount < 1)
+ {
+ CleanupCameraNames ();
+ }
+
+ bestSideLocatorName.SetText( NULL );
+ bestSideLocator = NULL;
+}
+
+//=============================================================================
+// ConversationCam::Update
+//=============================================================================
+// Description: called to animate the camera
+//
+// Parameters: ( unsigned int milliseconds
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::Update( unsigned int milliseconds)
+{
+ rAssertMsg( mTargets[ 0 ], "The ConversationCam needs a target!" );
+ rAssertMsg( mTargets[ 1 ], "The ConversationCam needs a secondary target!" );
+
+ {
+ rmt::Vector pos;
+ rmt::Vector candidates[ 2 ];
+ rmt::Vector targPos[ 2 ];
+
+ targPos[ 0 ] = mPositionOriginal;
+ targPos[ 1 ] = mTargetOriginal;
+
+ //
+ // Make characters face one another
+ //
+ GetPresentationManager()->MakeCharactersFaceEachOther( mCharacters[ 0 ], mCharacters[ 1 ] );
+
+ //
+ // Update the characters' position
+ //
+ rmt::Vector averagePosition = ( mPositionOriginal + mTargetOriginal ) / 2;
+ rmt::Vector offset = mPositionOriginal - mTargetOriginal;
+ rmt::Vector pos1 = mPositionOriginal;
+ rmt::Vector pos2 = mTargetOriginal;
+ float distanceApart = offset.Magnitude();
+ offset.Normalize();
+ const float minimumSeparation = 1.65f;
+ if( distanceApart < minimumSeparation )
+ {
+ float amountToCorrectDistance = minimumSeparation - distanceApart;
+ pos1 += offset * amountToCorrectDistance / 2;
+ pos2 -= offset * amountToCorrectDistance / 2;
+ }
+ rmt::Vector rightvector = offset;
+ const rmt::Vector y = rmt::Vector( 0.0f, 1.0f, 0.0f );
+ rightvector.CrossProduct( y );
+
+ //
+ // figure out if we should invert the left and right to showcase a
+ // particular side of the conversation
+ //
+ bool swapSides = false;
+ if( bestSideLocator != NULL )
+ {
+ rmt::Vector bestSide;
+ bestSideLocator->GetPosition( &bestSide );
+ rmt::Vector leftSide = pos1 - rightvector;
+ rmt::Vector rightSide = pos1 + rightvector;
+ float leftDistance = ( bestSide - leftSide ).MagnitudeSqr();
+ float rightDistance = ( bestSide - rightSide ).MagnitudeSqr();
+ if( leftDistance > rightDistance )
+ {
+ swapSides = true;
+ rightvector = -rightvector;
+ }
+ }
+
+ pos1 += sPcOffset.x * offset + sPcOffset.y * y + sPcOffset.z * rightvector;
+ pos2 += sNpcOffset.x * offset + sNpcOffset.y * y + sNpcOffset.z * rightvector;
+ float rotation = choreo::GetWorldAngle( offset.x, offset.z );
+ mCharacters[ 1 ]->RelocateAndReset( pos2, rotation, false );
+ offset.Scale( -1.0f );
+ rotation = choreo::GetWorldAngle( offset.x, offset.z );
+ mCharacters[ 0 ]->RelocateAndReset( pos1, rotation, false );
+
+
+
+
+ rmt::Vector towardsNPC = targPos[ 1 ] - targPos[ 0 ];
+ rmt::Vector t0Tot1 = towardsNPC;
+
+ rmt::Vector normals[2];
+
+ normals[ 0 ] = t0Tot1;
+ normals[ 1 ] = t0Tot1;
+
+ float normalScale = 5.0f;
+
+ normals[ 0 ].CrossProduct( rmt::Vector( 0.0f, 1.0f, 0.0f ) );
+ normals[ 0 ].Normalize();
+ normals[ 0 ].Scale( normalScale );
+ normals[ 1 ].CrossProduct( rmt::Vector( 0.0f, -1.0f, 0.0f ) );
+ normals[ 1 ].Normalize();
+ normals[ 1 ].Scale( normalScale );
+
+ rmt::Vector& right = normals[ 0 ];
+ if( swapSides )
+ {
+ right *= -1.0f;
+ }
+
+ t0Tot1.Scale( 0.5f );
+
+ candidates[ 0 ].Add( targPos[ 0 ], t0Tot1 );
+ candidates[ 1 ].Add( targPos[ 0 ], t0Tot1 );
+
+ candidates[ 0 ].Add( normals[ 0 ] );
+ candidates[ 1 ].Add( normals[ 1 ] );
+
+ //Should test to make sure that one of the candidates is not passing into a wall,
+ //find the closest for now.
+ rmt::Vector camPos;
+ GetPosition( &camPos );
+
+ rmt::Vector test[2];
+ test[ 0 ].Sub( candidates[ 0 ], camPos );
+ test[ 1 ].Sub( candidates[ 1 ], camPos );
+
+ if ( test[ 0 ].MagnitudeSqr() < test[ 1 ].MagnitudeSqr() )
+ {
+ mPosition = candidates[ 0 ];
+ }
+ else
+ {
+ mPosition = candidates[ 1 ];
+ }
+
+ float theta = sAngle;
+ towardsNPC.Normalize();
+ right.Normalize();
+ mPosition = towardsNPC * rmt::Sin( theta ) + right * rmt::Cos( theta );
+ mPosition *= sDistance;
+ mPosition += ( targPos[ 0 ] + targPos[ 1 ] ) / 2.0f;
+ mPosition.y += sElevation + sHeightAdjustment; //adjust height
+ mTarget = ( targPos[ 0 ] + targPos[ 1 ] ) / 2.0f;
+ mTarget.y += sYOffset + sHeightAdjustment;
+ mTarget += towardsNPC * sXOffset; //adjust the x position for the target
+ SetFlag( (Flag)FIRST_TIME, false );
+
+ //Reset the FOV.
+ SetFOV( sFov );
+ }
+
+ //--------- Buid a rod for the camera
+
+ SetCameraValues( milliseconds, mPosition, mTarget);
+}
+
+//=============================================================================
+// ConversationCam::LoadSettings
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned char* settings )
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::LoadSettings( unsigned char* settings )
+{
+}
+
+//=============================================================================
+// ConversationCam::LockCharacterPositions
+//=============================================================================
+// Description: Grabs the positionss of the characters and records them for
+// later use
+//
+// Parameters: NONE
+//
+// Return: NONE
+//
+//=============================================================================
+void ConversationCam::LockCharacterPositions()
+{
+ //
+ // First reset the characters to their minimum separating distance
+ //
+ mCharacters[ 0 ]->GetPosition( &mPositionOriginal );
+ mCharacters[ 1 ]->GetPosition( &mTargetOriginal );
+}
+
+//=============================================================================
+// ConversationCam::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::SetTarget( ISuperCamTarget* target )
+{
+ mTargets[ 0 ] = target;
+}
+
+//=============================================================================
+// ConversationCam::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::AddTarget( ISuperCamTarget* target )
+{
+ mTargets[ 1 ] = target;
+}
+
+//=============================================================================
+// ConversationCam::GetNumTargets
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+unsigned int ConversationCam::GetNumTargets() const
+{
+ unsigned int count = 0;
+
+ if ( mTargets[ 0 ] )
+ {
+ count++;
+ }
+
+ if ( mTargets[ 1 ] )
+ {
+ count++;
+ }
+
+ return count;
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// ConversationCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\Conversation", GetPlayerID() );
+
+ radDbgWatchAddFloat( &sDistance, "Distance from center", nameSpace, NULL, NULL, 0.0f, 20.0f );
+ radDbgWatchAddFloat( &sAngle, "Angle", nameSpace, NULL, NULL, -rmt::PI_2, rmt::PI_2 );
+ radDbgWatchAddFloat( &sElevation, "Elevation", nameSpace, NULL, NULL, 0.0f, 10.0f );
+ radDbgWatchAddFloat( &sFov, "FOV", nameSpace, NULL, NULL, 0.0f, rmt::PI );
+ radDbgWatchAddFloat( &sXOffset, "XOffset", nameSpace, NULL, NULL, -5.0f, 5.0f );
+ radDbgWatchAddFloat( &sYOffset, "YOffset", nameSpace, NULL, NULL, 0.0f, 5.0f );
+ radDbgWatchAddVector( reinterpret_cast< float* >( &sPcOffset ), "PcOffset", nameSpace, NULL, NULL, -5.0f, 5.0f );
+ radDbgWatchAddVector( reinterpret_cast< float* >( &sNpcOffset ), "NpcOffset", nameSpace, NULL, NULL, -5.0f, 5.0f );
+#endif
+}
+
+//=============================================================================
+// ConversationCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &sDistance );
+ radDbgWatchDelete( &sAngle );
+ radDbgWatchDelete( &sFov );
+ radDbgWatchDelete( &sElevation );
+ radDbgWatchDelete( &sXOffset );
+ radDbgWatchDelete( &sYOffset );
+ radDbgWatchDelete( &sPcOffset );
+ radDbgWatchDelete( &sNpcOffset );
+#endif
+}
+
+//=============================================================================
+// InitializeCameraNames
+//=============================================================================
+// Description: sets up the names of the conversation cameras for scripting
+//
+// Parameters: NONE
+//
+// Return: void
+//
+//=============================================================================
+static void InitializeCameraNames()
+{
+ sCamNames = new tName[ sMaxCameras ];
+ int count = -1;
+ sCamNames[ ++count ] = "pc_far";
+ sCamNames[ ++count ] = "npc_far";
+ sCamNames[ ++count ] = "pc_near";
+ sCamNames[ ++count ] = "npc_near";
+}
+
+//=============================================================================
+// CleanupCameraNames
+//=============================================================================
+// Description: Cleans up the allocations made in InitializeCameraNames
+//
+// Parameters: NONE
+//
+// Return: void
+//
+//=============================================================================
+static void CleanupCameraNames()
+{
+ if (sCamNames)
+ {
+ delete[] sCamNames;
+ sCamNames = 0;
+ }
+}
+
+//=============================================================================
+// FindConversationCam
+//=============================================================================
+// Description: finds the index of a specific conversation camera by name
+//
+// Parameters: name - the name of the camera that we're looking for
+//
+// Return: void
+//
+//=============================================================================
+static unsigned int FindConversationCam( const tName& name )
+{
+ if( sCamNames == NULL )
+ {
+ InitializeCameraNames();
+ }
+ const tName* foundname = std::find( &sCamNames[ 0 ], &sCamNames[ sMaxCameras ], name );
+ const unsigned int offset = reinterpret_cast< unsigned int >( foundname ) - reinterpret_cast< unsigned int >( &sCamNames[ 0 ] );
+ const unsigned int index = offset / sizeof( tName );
+#ifdef RAD_DEBUG
+ if( index >= sMaxCameras )
+ {
+ rDebugPrintf( "Could Not Find conversation camera %s\n", name.GetText() );
+ }
+#endif
+ rAssertMsg( index < sMaxCameras, "Conversation camera could not be found" );
+ return index;
+}
+
+//=============================================================================
+// SetCameraByIndex
+//=============================================================================
+// Description: finds the index of a specific conversation camera by name
+//
+// Parameters: name - the name of the camera that we're looking for
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::SetCameraByIndex( const unsigned int index )
+{
+ sDistance = sDistances [ index ];
+ sAngle = sAngles [ index ];
+ sElevation = sElevations [ index ];
+ sFov = sFovs [ index ];
+ sXOffset = sXOffsets [ index ];
+ sYOffset = sYOffsets [ index ];
+ sPcOffset = sPcOffsets [ index ];
+ sNpcOffset = sNpcOffsets [ index ];
+ if( sPcFocus[ index ] == true )
+ {
+ if( sPcIsChild )
+ {
+ sHeightAdjustment = childHeightAdjustment;
+ }
+ else
+ {
+ sHeightAdjustment = 0.0f;
+ }
+ }
+ if( sPcFocus[ index ] == false )
+ {
+ if( sNpcIsChild )
+ {
+ sHeightAdjustment = childHeightAdjustment;
+ }
+ else
+ {
+ sHeightAdjustment = 0.0f;
+ }
+ }
+}
+
+//=============================================================================
+// SetCameraByName
+//=============================================================================
+// Description: finds the index of a specific conversation camera by name
+//
+// Parameters: name - the name of the camera that we're looking for
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::SetCameraByName( const tName& name )
+{
+ unsigned int index = FindConversationCam( name );
+ SetCameraByIndex( index );
+}
+//=============================================================================
+// SetPcCameraByName
+//=============================================================================
+// Description: sets the camera used when the PC is talking
+//
+// Parameters: name - the name of the camera that we're using
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::SetCameraDistanceByName( const tName& name, const float distance )
+{
+ const unsigned int index = FindConversationCam( name );
+ SetCameraDistanceByIndex( index, distance );
+}
+
+//=============================================================================
+// SetCamBestSide
+//=============================================================================
+// Description: gets the locator from the name, and uses its location to
+//
+// Parameters: index - the index of the camera we're using
+// distance - the distance to the focus point
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::SetCamBestSide( const tName& name )
+{
+ bestSideLocatorName = name;
+ bestSideLocator = p3d::find< Locator >( bestSideLocatorName.GetUID() );
+}
+
+//=============================================================================
+// SetCameraDistanceByIndex
+//=============================================================================
+// Description: sets the distance from the camera to the focus point by camera
+// index
+//
+// Parameters: index - the index of the camera we're using
+// distance - the distance to the focus point
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::SetCameraDistanceByIndex( const unsigned int index, const float distance )
+{
+ rAssert( index < sMaxCameras );
+ sDistances[ index ] = distance;
+}
+
+//=============================================================================
+// ConversationCam::SetCharacter
+//=============================================================================
+// Description: sets the actual characters we're going to look at
+//
+// Parameters: index - 0,1 is this the player or the NPC
+// character - pointer to the character
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::SetCharacter( const int index, Character* character )
+{
+ rAssert( index < CONV_CAM_MAX_CHARACTERS );
+ mCharacters[ index ] = character;
+}
+
+//=============================================================================
+// SetPcCameraByName
+//=============================================================================
+// Description: sets the camera used when the PC is talking
+//
+// Parameters: name - the name of the camera that we're using
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::SetPcCameraByName( const tName& name )
+{
+ unsigned int index = FindConversationCam( name );
+ sCamPc = index;
+}
+//=============================================================================
+// SetNpcCamerByName
+//=============================================================================
+// Description: sets the camera used when the NPC is talking
+//
+// Parameters: name - the name of the camera that we're using
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::SetNpcCameraByName( const tName& name )
+{
+ unsigned int index = FindConversationCam( name );
+ sCamNpc = index;
+}
+//=============================================================================
+// SetNpcIsChild
+//=============================================================================
+// Description: tells the converstaion camera that its target is a child
+//
+// Parameters: name - the name of the camera that we're using
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::SetNpcIsChild( const bool isChild )
+{
+ sNpcIsChild = isChild;
+}
+//=============================================================================
+// ConversationCam::SetPcIsChild
+//=============================================================================
+// Description: tells the converstaion camera that its target is a child
+//
+// Parameters: name - the name of the camera that we're using
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::SetPcIsChild( const bool isChild )
+{
+ sPcIsChild = isChild;
+}
+//=============================================================================
+// UsePcCam
+//=============================================================================
+// Description: sets the camera used when the NPC is talking
+//
+// Parameters: name - the name of the camera that we're using
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::UsePcCam()
+{
+ SetCameraByIndex( sCamPc );
+}
+//=============================================================================
+// SetNpcCamerByName
+//=============================================================================
+// Description: sets the camera used when the NPC is talking
+//
+// Parameters: name - the name of the camera that we're using
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::UseNpcCam()
+{
+ SetCameraByIndex( sCamNpc );
+}
+
+//=============================================================================
+// OnInit
+//=============================================================================
+// Description: gets called when we're transitioning from another camera type
+//
+// Parameters: none
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::OnInit()
+{
+ TriggerVolumeTracker::GetInstance()->IgnoreTriggers( true );
+}
+
+//=============================================================================
+// OnShutdown
+//=============================================================================
+// Description: gets called when we're transitioning to another camera type
+//
+// Parameters: none
+//
+// Return: void
+//
+//=============================================================================
+void ConversationCam::OnShutdown()
+{
+ TriggerVolumeTracker::GetInstance()->IgnoreTriggers( false );
+ bestSideLocatorName = "";
+}
diff --git a/game/code/camera/conversationcam.h b/game/code/camera/conversationcam.h
new file mode 100644
index 0000000..3fe071e
--- /dev/null
+++ b/game/code/camera/conversationcam.h
@@ -0,0 +1,145 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ConversationCam.h
+//
+// Description: Camera used for outdoor conversation. Based of Cary's Bumper Cam
+//
+// History: 24/04/2002 + Created -- Cary Brisebois, Chuck Chow
+//
+//=============================================================================
+
+#ifndef ConversationCam_H
+#define ConversationCam_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+#include <camera/ConversationCamdata.h>
+
+//========================================
+// Forward References
+//========================================
+class Character;
+class ISuperCamTarget;
+
+#define CONV_CAM_MAX_CHARACTERS 2
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ConversationCam : public SuperCam
+{
+public:
+
+ enum ConversationFlag
+ {
+ READY = SUPERCAM_END //This is to carry on from the supercam flags
+ };
+
+ ConversationCam();
+ virtual ~ConversationCam();
+
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds);
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ //This loads the off-line created settings for the camera.
+ //It is passed in as a byte stream of some data of known size.
+ virtual void LoadSettings( unsigned char* settings );
+
+ virtual Type GetType();
+
+ //These are for favourable support of this command
+ virtual void SetTarget( ISuperCamTarget* target );
+ virtual void AddTarget( ISuperCamTarget* target );
+ void LockCharacterPositions();
+ void SetCharacter( const int index, Character* character );
+ virtual void OnInit();
+ virtual void OnShutdown();
+
+ unsigned int GetNumTargets() const;
+ static void SetCameraByName( const tName& name );
+ static void SetCameraDistanceByName( const tName& name, const float distance );
+ static void SetPcCameraByName( const tName& name );
+ static void SetNpcCameraByName( const tName& name );
+ static void UsePcCam();
+ static void UseNpcCam();
+ static void SetNpcIsChild( const bool isChild );
+ static void SetPcIsChild( const bool isChild );
+ static void SetCamBestSide( const tName& name );
+
+private:
+ static void SetCameraByIndex( const unsigned int index );
+ static void SetCameraDistanceByIndex( const unsigned int index, const float distance );
+
+ //These functions are to allow real-time control of the settings of
+ //the supercam.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+ void CopyDefaultsToCameraPerLineOfDialog();
+
+ ConversationCamData mData;
+ ISuperCamTarget* mTargets[2];
+
+ rmt::Vector mPositionOriginal;
+ rmt::Vector mTargetOriginal;
+ rmt::Vector mPosition;
+ rmt::Vector mTarget;
+
+ float mTargetHeight;
+ float mPositionHeight;
+
+ Character* mCharacters[ CONV_CAM_MAX_CHARACTERS ];
+
+ static int sInstCount;
+
+ //Prevent wasteful constructor creation.
+ ConversationCam( const ConversationCam& ConversationCam );
+ ConversationCam& operator=( const ConversationCam& ConversationCam );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ConversationCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+inline const char* const ConversationCam::GetName() const
+{
+ return "CONVERSATION_CAM";
+}
+
+//=============================================================================
+// ConversationCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type ConversationCam::GetType()
+{
+ return CONVERSATION_CAM;
+}
+
+#endif //ConversationCam_H
diff --git a/game/code/camera/conversationcamdata.h b/game/code/camera/conversationcamdata.h
new file mode 100644
index 0000000..99acd4c
--- /dev/null
+++ b/game/code/camera/conversationcamdata.h
@@ -0,0 +1,172 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ConversationCamdata.h
+//
+// Description: Blahblahblah
+//
+// History: 24/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef ConversationCamDATA_H
+#define ConversationCamDATA_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Data for the Camera
+//
+//=============================================================================
+
+class ConversationCamData
+{
+public:
+ ConversationCamData();
+ virtual ~ConversationCamData() {};
+
+ void GetFrontPosition( rmt::Vector* front );
+ void SetFrontPosition( rmt::Vector front );
+ void GetFrontTarget( rmt::Vector* front );
+ void SetFrontTarget( rmt::Vector front );
+
+
+
+ float GetFOV() const;
+ void SetFOV( float fov );
+
+ rmt::Vector mFrontPos;
+ rmt::Vector mFrontTarg;
+
+ float mFOV;
+
+private:
+ //Prevent wasteful constructor creation.
+ ConversationCamData( const ConversationCamData& ConversationCamdata );
+ ConversationCamData& operator=( const ConversationCamData& ConversationCamdata );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ConversationCamData::ConversationCamData
+//=============================================================================
+// Description: Constructor
+//
+// Parameters: ()
+//
+// Return: ConversationCamData
+//
+//=============================================================================
+inline ConversationCamData::ConversationCamData() :
+ mFOV( 0.75f )
+{
+ mFrontPos.Set( 0.0f, 1.5f, -2.8f );
+ mFrontTarg.Set( 0.0f, 1.5f, 2.3f );
+
+}
+
+//=============================================================================
+// ConversationCamData::GetFrontPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* front )
+//
+// Return: void
+//
+//=============================================================================
+inline void ConversationCamData::GetFrontPosition( rmt::Vector* front )
+{
+ *front = mFrontPos;
+}
+
+//=============================================================================
+// ConversationCamData::SetFrontPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector front )
+//
+// Return: void
+//
+//=============================================================================
+inline void ConversationCamData::SetFrontPosition( rmt::Vector front )
+{
+ mFrontPos = front;
+}
+
+//=============================================================================
+// ConversationCamData::GetFrontTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* front )
+//
+// Return: void
+//
+//=============================================================================
+inline void ConversationCamData::GetFrontTarget( rmt::Vector* front )
+{
+ *front = mFrontTarg;
+}
+
+//=============================================================================
+// ConversationCamData::SetFrontTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector front )
+//
+// Return: void
+//
+//=============================================================================
+inline void ConversationCamData::SetFrontTarget( rmt::Vector front )
+{
+ mFrontTarg = front;
+}
+
+
+//=============================================================================
+// ConversationCamData::GetFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float ConversationCamData::GetFOV() const
+{
+ return mFOV;
+}
+
+//=============================================================================
+// ConversationCamData::SetFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float fov )
+//
+// Return: void
+//
+//=============================================================================
+inline void ConversationCamData::SetFOV( float fov )
+{
+ mFOV = fov;
+}
+
+#endif //ConversationCamDATA_H
diff --git a/game/code/camera/debugcam.cpp b/game/code/camera/debugcam.cpp
new file mode 100644
index 0000000..352dba3
--- /dev/null
+++ b/game/code/camera/debugcam.cpp
@@ -0,0 +1,246 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: debugcam.cpp
+//
+// Description: Implement DebugCam
+//
+// History: 24/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+//These are included in the precompiled headers on XBOX and GC
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/DebugCam.h>
+#include <camera/supercamconstants.h>
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+const float MAGNITUDE = 1.0f;
+
+#ifndef DEBUGWATCH
+const float INCREMENT = 0.0174f; //One degree
+const float DIST = 1.0f;
+#else
+float INCREMENT = 0.0174f;
+float DIST = 1.0f;
+#endif
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// DebugCam::DebugCam
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+DebugCam::DebugCam() :
+ mRotationAngleXZ( 0.0f ),
+ mRotationAngleY( 2.13f )
+{
+ mIm = InputManager::GetInstance();
+}
+
+//=============================================================================
+// DebugCam::~DebugCam
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+DebugCam::~DebugCam()
+{
+}
+
+//=============================================================================
+// DebugCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void DebugCam::Update( unsigned int milliseconds )
+{
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ //Reset the FOV.
+ SetFOV( SUPERCAM_FOV );//DEFAULT_FOV );
+
+ SetFlag( (Flag)CUT, false );
+ }
+
+ //This is to adjust interpolation when we're running substeps.
+ float timeMod = 1.0f;
+
+ timeMod = (float)milliseconds / (float)16;
+
+ bool cameraRelative = false;
+
+#ifdef RAD_WIN32
+ float left = mIm->GetValue( 0, InputManager::CameraLeft );
+ float right = mIm->GetValue( 0, InputManager::CameraRight );
+ float up = mIm->GetValue( 0, InputManager::CameraMoveIn );
+ float down = mIm->GetValue( 0, InputManager::CameraMoveOut );
+
+ float xAxis = ( right > left ) ? right : -left;
+ float yAxis = ( up > down ) ? up : -down;
+ float zAxis = yAxis;
+ float zToggle = mIm->GetValue( 0, InputManager::CameraLookUp );
+ float rightTrigger = mIm->GetValue( 0, InputManager::CameraZoom );
+#else
+ float xAxis = mIm->GetValue( s_secondaryControllerID, InputManager::LeftStickX );
+ float yAxis = mIm->GetValue( s_secondaryControllerID, InputManager::LeftStickY );
+ float zAxis = mIm->GetValue( s_secondaryControllerID, InputManager::LeftStickY );
+ float zToggle = mIm->GetValue( s_secondaryControllerID, InputManager::L3 );
+ float rightTrigger = mIm->GetValue( s_secondaryControllerID, InputManager::AnalogR1 );
+#endif
+
+ if ( rmt::Fabs( zToggle ) > STICK_DEAD_ZONE && rmt::Fabs( zToggle ) <= 1.0f )
+ {
+ //zToggled
+ yAxis = 0.0f;
+ }
+ else
+ {
+ zAxis = 0.0f;
+ }
+
+ if ( rmt::Fabs( rightTrigger ) > STICK_DEAD_ZONE && rmt::Fabs( rightTrigger ) <= 1.0f )
+ {
+ //TODO: Make camera relative movement work.
+ cameraRelative = true;
+ }
+
+ mRotationAngleXZ -= ( xAxis * INCREMENT * timeMod );
+ mRotationAngleY -= ( yAxis * INCREMENT * timeMod );
+
+ float x, y, z;
+ rmt::SphericalToCartesian( MAGNITUDE, mRotationAngleXZ, mRotationAngleY,
+ &x, &z, &y );
+
+ rmt::Vector camPos, camTarget, diff;
+ GetPosition( &camPos );
+
+ camTarget.Add( camPos, rmt::Vector( x, y, z ) );
+
+ diff.Sub( camTarget, camPos );
+ diff.NormalizeSafe();
+
+ diff.Scale( DIST * zAxis * timeMod );
+
+ camPos.Add( diff );
+ camTarget.Add( diff );
+
+ SetCameraValues( milliseconds, camPos, camTarget );
+}
+
+//=============================================================================
+// DebugCam::EnableShake
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DebugCam::EnableShake()
+{
+ SetShaker( &mSineCosShaker );
+
+ SuperCam::EnableShake();
+}
+
+//=============================================================================
+// DebugCam::DisableShake
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DebugCam::DisableShake()
+{
+ SuperCam::DisableShake();
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// DebugCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DebugCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\Debug", GetPlayerID() );
+
+ radDbgWatchAddFloat( &mRotationAngleXZ, "Rotation Angle XZ", nameSpace, NULL, NULL, 0.0f, rmt::PI_2 );
+ radDbgWatchAddFloat( &mRotationAngleY, "Rotation Angle Y", nameSpace, NULL, NULL, 0.0f, rmt::PI_2 );
+ radDbgWatchAddFloat( &INCREMENT, "Rotation Increment", nameSpace, NULL, NULL, 0.0f, rmt::PI_2 );
+ radDbgWatchAddFloat( &DIST, "Move Distance", nameSpace, NULL, NULL, 0.0f, 100.0f );
+#endif
+}
+
+//=============================================================================
+// DebugCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DebugCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mRotationAngleXZ );
+ radDbgWatchDelete( &mRotationAngleY );
+ radDbgWatchDelete( &INCREMENT );
+ radDbgWatchDelete( &DIST );
+#endif
+}
diff --git a/game/code/camera/debugcam.h b/game/code/camera/debugcam.h
new file mode 100644
index 0000000..f3dcffb
--- /dev/null
+++ b/game/code/camera/debugcam.h
@@ -0,0 +1,102 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: debugcam.h
+//
+// Description: Blahblahblah
+//
+// History: 24/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef DEBUGCAM_H
+#define DEBUGCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+
+//========================================
+// Forward References
+//========================================
+class InputManager;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class DebugCam : public SuperCam
+{
+public:
+ DebugCam();
+ virtual ~DebugCam();
+
+ //Update: Called when you want the super cam to update its state.
+ void Update( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ const char* const GetName() const;
+
+ Type GetType();
+
+ void EnableShake();
+ void DisableShake();
+
+private:
+
+ float mRotationAngleXZ;
+ float mRotationAngleY;
+
+ InputManager* mIm;
+
+ //These functions are to allow real-time control of the settings of
+ //the supercam.
+ void OnRegisterDebugControls();
+ void OnUnregisterDebugControls();
+
+ //Prevent wasteful constructor creation.
+ DebugCam( const DebugCam& debugcam );
+ DebugCam& operator=( const DebugCam& debugcam );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// DebugCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+inline const char* const DebugCam::GetName() const
+{
+ return "DEBUG_CAM";
+}
+
+//=============================================================================
+// DebugCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type DebugCam::GetType()
+{
+ return DEBUG_CAM;
+}
+
+#endif //DEBUGCAM_H
diff --git a/game/code/camera/firstpersoncam.cpp b/game/code/camera/firstpersoncam.cpp
new file mode 100644
index 0000000..ba12895
--- /dev/null
+++ b/game/code/camera/firstpersoncam.cpp
@@ -0,0 +1,354 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: firstpersoncam.cpp
+//
+// Description: Implement FirstPersonCam
+//
+// History: 2/21/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radmath/radmath.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/firstpersoncam.h>
+#include <camera/isupercamtarget.h>
+#include <camera/supercamcontroller.h>
+#include <camera/supercamconstants.h>
+#include <camera/supercammanager.h>
+
+#include <input/inputmanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+#define DEFAULT_MAGNITUDE 5.0f
+#define DEFAULT_ROTATION rmt::PI_BY2
+#define DEFAULT_ELEVATION rmt::PI_BY2
+
+#define MAX_ROT_ANGLE rmt::DegToRadian( 80.0f )
+#define MAX_ELEV_ANGLE rmt::DegToRadian( 80.0f )
+
+#ifdef DEBUGWATCH
+float FIRST_PERSON_CAM_MIN_FOV = SUPERCAM_DEFAULT_MIN_FOV;
+float FIRST_PERSON_CAM_MAX_FOV = SUPERCAM_DEFAULT_MAX_FOV;
+float FIRST_PERSON_CAM_LOOK_LAG = 0.05f;
+#else
+const float FIRST_PERSON_CAM_MIN_FOV = SUPERCAM_DEFAULT_MIN_FOV;
+const float FIRST_PERSON_CAM_MAX_FOV = SUPERCAM_DEFAULT_MAX_FOV;
+const float FIRST_PERSON_CAM_LOOK_LAG = SUPERCAM_DEFAULT_FOV_LAG;
+#endif
+
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// FirstPersonCam::FirstPersonCam
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+FirstPersonCam::FirstPersonCam() :
+ mTarget( NULL ),
+ mTargetDirty( false ),
+ mRotation( DEFAULT_ROTATION ),
+ mElevation( DEFAULT_ELEVATION ),
+ mRotationDelta( 0.0f ),
+ mElevationDelta( 0.0f ),
+ mFOVDelta( 0.0f ),
+ mCollisionOffset( NULL ),
+ mNumCollisions( 0 )
+{
+ mTargetPositionOffset.Set( 0.0f, 0.0f, 0.0f );
+}
+
+//=============================================================================
+// FirstPersonCam::~FirstPersonCam
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+FirstPersonCam::~FirstPersonCam()
+{
+}
+
+//=============================================================================
+// FirstPersonCam::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FirstPersonCam::OnInit()
+{
+ InitMyController();
+}
+
+//=============================================================================
+// FirstPersonCam::OnShutdown
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FirstPersonCam::OnShutdown()
+{
+ //Reset the controller state if we're not in a new state since we went in.
+ //Switching to pause when in first person will cause this to fail.
+ if ( GetInputManager()->GetGameState() == Input::ACTIVE_FIRST_PERSON )
+ {
+ GetInputManager()->SetGameState( Input::ACTIVE_GAMEPLAY );
+ }
+}
+
+//=============================================================================
+// FirstPersonCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void FirstPersonCam::Update( unsigned int milliseconds )
+{
+ rAssert( mTarget );
+
+ if ( mTargetDirty )
+ {
+ //Update the position offset since we just got a new target.
+ mTarget->GetFirstPersonPosition( &mTargetPositionOffset );
+ mTargetDirty = false;
+ }
+
+ if ( GetInputManager()->GetGameState() == Input::ACTIVE_GAMEPLAY || GetInputManager()->GetGameState() == Input::ACTIVE_ALL )
+ {
+ GetInputManager()->SetGameState( Input::ACTIVE_FIRST_PERSON );
+ }
+
+ if ( GetFlag((Flag)FIRST_TIME) || GetFlag((Flag)CUT) )
+ {
+ mRotation = DEFAULT_ROTATION;
+ mElevation = DEFAULT_ELEVATION;
+ mRotationDelta = 0.0f;
+ mElevationDelta = 0.0f;
+ mFOVDelta = 0.0f;
+
+ SetFlag( (Flag)FIRST_TIME, false );
+ SetFlag( (Flag)CUT, false );
+ }
+
+ //--------- Calculate positino and target
+
+ float timeMod = milliseconds / 16.0f;
+
+ //place the target at the position and deal with controller input.
+
+ rmt::Vector position, target;
+ mTarget->GetPosition( &position );
+ position.Add( mTargetPositionOffset );
+
+ //Take controller values and calculate desired rotation and position.
+ float desiredRot, desiredElev;
+ desiredRot = mRotation + ( MAX_ROT_ANGLE * -(mController->GetAxisValue( SuperCamController::stickX )) );
+
+ float invert = -1.0f;
+ if ( GetSuperCamManager()->GetSCC( GetPlayerID() )->IsInvertedCameraEnabled() )
+ {
+ invert = 1.0f;
+ }
+
+ desiredElev = MAX_ELEV_ANGLE * ( invert * mController->GetAxisValue( SuperCamController::stickY ) ) + DEFAULT_ELEVATION;
+
+ float lag = FIRST_PERSON_CAM_LOOK_LAG * timeMod;
+ CLAMP_TO_ONE( lag );
+
+ MotionCubic( &mRotation, &mRotationDelta, desiredRot, lag );
+ MotionCubic( &mElevation, &mElevationDelta, desiredElev, lag );
+
+ rmt::SphericalToCartesian( DEFAULT_MAGNITUDE, mRotation, mElevation, &target.x, &target.z, &target.y );
+
+ rmt::Vector targetHeading;
+ mTarget->GetHeading( &targetHeading );
+ rmt::Vector targetVUP;
+ mTarget->GetVUP( &targetVUP );
+
+ rmt::Matrix mat;
+ mat.Identity();
+ mat.FillHeading( targetHeading, targetVUP );
+
+ target.Transform( mat );
+ target.Add( position );
+
+ //--------- Goofin' with the FOV
+
+ float zoom = mController->GetValue( SuperCamController::zToggle );
+
+ float FOV = GetFOV();
+ FilterFov( zoom, FIRST_PERSON_CAM_MIN_FOV, FIRST_PERSON_CAM_MAX_FOV, FOV, mFOVDelta, SUPERCAM_DEFAULT_FOV_LAG, timeMod );
+
+ SetFOV( FOV );
+
+ //--------- Set values
+
+ SetCameraValues( milliseconds, position, target );
+}
+
+//=============================================================================
+// FirstPersonCam::UpdateForPhysics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void FirstPersonCam::UpdateForPhysics( unsigned int milliseconds )
+{
+ if ( mNumCollisions )
+ {
+ rmt::Vector offset( 0.0f, 0.0f, 0.0f );
+
+ unsigned int i;
+ for ( i = 0; i < mNumCollisions; ++i )
+ {
+ offset += mCollisionOffset[ i ];
+ }
+
+ //offset.x /= mNumCollisions;
+ //offset.y /= mNumCollisions;
+ //offset.z /= mNumCollisions;
+
+ rmt::Vector camPos;
+ GetPosition( &camPos );
+
+ rmt::Vector camTarg;
+ GetTarget( &camTarg );
+
+ camPos += offset;
+ camTarg += offset;
+
+ SetCameraValues( 0, camPos, camTarg );
+ }
+
+ if ( mTarget->IsUnstable() )
+ {
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( GetPlayerID() );
+ GetSuperCamManager()->GetSCC( 0 )->ToggleFirstPerson( controllerID );
+ }
+}
+
+//=============================================================================
+// FirstPersonCam::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+void FirstPersonCam::SetTarget( ISuperCamTarget* target )
+{
+ mTarget = target;
+ mTargetDirty = true;
+}
+
+//=============================================================================
+// FirstPersonCam::OverrideOldState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Input::ActiveState state )
+//
+// Return: void
+//
+//=============================================================================
+void FirstPersonCam::OverrideOldState( Input::ActiveState state )
+{
+ mOldState = state;
+}
+
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// FirstPersonCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FirstPersonCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\1st Person", GetPlayerID() );
+
+ radDbgWatchAddFloat( &FIRST_PERSON_CAM_MIN_FOV, "Min FOV", nameSpace, NULL, NULL, 0.0f, rmt::PI );
+ radDbgWatchAddFloat( &FIRST_PERSON_CAM_MAX_FOV, "Max FOV", nameSpace, NULL, NULL, 0.0f, rmt::PI );
+ radDbgWatchAddFloat( &FIRST_PERSON_CAM_LOOK_LAG, "Look Lag", nameSpace, NULL, NULL, 0.0f, rmt::PI );
+#endif
+}
+
+//=============================================================================
+// FirstPersonCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FirstPersonCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &FIRST_PERSON_CAM_MIN_FOV );
+ radDbgWatchDelete( &FIRST_PERSON_CAM_MAX_FOV );
+ radDbgWatchDelete( &FIRST_PERSON_CAM_LOOK_LAG );
+#endif
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/camera/firstpersoncam.h b/game/code/camera/firstpersoncam.h
new file mode 100644
index 0000000..7068e7e
--- /dev/null
+++ b/game/code/camera/firstpersoncam.h
@@ -0,0 +1,160 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: firstpersoncam.h
+//
+// Description: Blahblahblah
+//
+// History: 2/21/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef FIRSTPERSONCAM_H
+#define FIRSTPERSONCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+#include <camera/supercam.h>
+#include <input/controller.h>
+
+
+//========================================
+// Forward References
+//========================================
+class ISuperCamTarget;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class FirstPersonCam : public SuperCam
+{
+public:
+ FirstPersonCam();
+ virtual ~FirstPersonCam();
+
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds );
+ virtual void UpdateForPhysics( unsigned int milliseconds );
+
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ virtual Type GetType();
+
+ virtual void SetTarget( ISuperCamTarget* target );
+
+ virtual unsigned int GetNumTargets() const;
+
+ //Hack
+ //This is so if we change views from firstperson in the pause menu we don't screw the controller
+ void OverrideOldState( Input::ActiveState state );
+
+ //Support for colliding with the world.
+ void SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset );
+ float GetCollisionRadius() const { return GetNearPlane() / 2.0f; };
+
+protected:
+ //Override this if you want to get initted.
+ virtual void OnInit();
+ virtual void OnShutdown();
+
+ //You'll need to overload these if you want debug watcher or other debug controls.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+private:
+ ISuperCamTarget* mTarget;
+ bool mTargetDirty : 1;
+ rmt::Vector mTargetPositionOffset;
+
+ float mRotation;
+ float mElevation;
+ float mRotationDelta;
+ float mElevationDelta;
+
+ Input::ActiveState mOldState;
+
+ float mFOVDelta;
+
+ const rmt::Vector* mCollisionOffset;
+ unsigned int mNumCollisions;
+
+ //Prevent wasteful constructor creation.
+ FirstPersonCam( const FirstPersonCam& firstpersoncam );
+ FirstPersonCam& operator=( const FirstPersonCam& firstpersoncam );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+//=============================================================================
+// FirstPersonCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type FirstPersonCam::GetType()
+{
+ return SuperCam::FIRST_PERSON_CAM;
+}
+
+//=============================================================================
+// FirstPersonCam::GetNumTargets
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int FirstPersonCam::GetNumTargets() const
+{
+ return 1;
+}
+
+//=============================================================================
+// FirstPersonCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+inline const char* const FirstPersonCam::GetName() const
+{
+ return "FIRST_PERSON_CAM";
+}
+
+//=============================================================================
+// FirstPersonCam::SetCollisionOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
+//
+// Return: void
+//
+//=============================================================================
+inline void FirstPersonCam::SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
+{
+ mCollisionOffset = offset;
+ mNumCollisions = numCollisions;
+}
+#endif //FIRSTPERSONCAM_H
diff --git a/game/code/camera/followcam.cpp b/game/code/camera/followcam.cpp
new file mode 100644
index 0000000..1eb20a8
--- /dev/null
+++ b/game/code/camera/followcam.cpp
@@ -0,0 +1,1269 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: followcam.cpp
+//
+// Description: Implement FollowCam
+// This is the main target following camera mode. It maintains a rod
+// that orients itself in 3D according to rules on the 3-axis.
+// This mode can handle one main target and possibly 2-others in a
+// limited fashion.
+//
+// History: 22/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+//These are included in the precompiled headers on XBOX and GC
+#include <raddebug.hpp>
+#include <radmath/radmath.hpp>
+#include <raddebugwatch.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/FollowCam.h>
+#include <camera/FollowCamDataChunk.h>
+#include <camera/isupercamtarget.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercamcontroller.h>
+#include <camera/supercammanager.h>
+
+#include <main/game.h>
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <cheats/cheatinputsystem.h>
+#include <ai/actor/intersectionlist.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+void SetDirtyCB( void* userData )
+{
+ FollowCam* fc = (FollowCam*)userData;
+
+ fc->SetDirty();
+}
+
+static bool reverseTest = false;
+
+#ifdef DEBUGWATCH
+float MIN_REVERSE_VELOCITY = 45.0f;
+static float CREEPY_TWIST = 6.107f;
+float LOOK_OFFSET_DIST = 5.0f;
+float LOOK_OFFSET_HEIGHT = 1.0f;
+float LOOK_OFFSET_BACK = 0.0f;
+float LOOK_ROT = rmt::PI_BY2;
+#else
+const float MIN_REVERSE_VELOCITY = 45.0f;
+static const float CREEPY_TWIST = 6.107f;
+const float LOOK_OFFSET_DIST = 5.0f;
+const float LOOK_OFFSET_HEIGHT = 1.0f;
+const float LOOK_OFFSET_BACK = 0.0f;
+const float LOOK_ROT = rmt::PI_BY2;
+#endif
+
+const unsigned int FOLLOW_ID_OFFSET = 256;
+
+//Only uncomment ONE of these.
+//#define TURN_LOOK
+//#define CUT_LOOK
+#define EXTRA_ROT
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// FollowCam::FollowCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+FollowCam::FollowCam( FollowType type ) :
+ mNumTargets( 0 ),
+ mActiveTarget( 0 ),
+ mUnstableDelayTimeLeft( 0 ),
+ mQuickTurnTimeLeft( 0 ),
+ mFollowType( type ),
+ mXAxis( 0.0f )
+{
+ unsigned int i;
+ for ( i = 0; i < MAX_TARGETS; ++i )
+ {
+ mTargets[ i ] = NULL;
+ }
+
+ mUnstablePosition.Set( 0.0f, 0.0f, 0.0f );
+ mUnstableTarget.Set( 0.0f, 0.0f, 0.0f );
+
+ mRotationAngleXZ = mData.GetRotationXZ();
+ mRotationAngleY = mData.GetRotationY();
+ mRotationAngleXZDelta = 0.0f;
+ mRotationAngleYDelta = 0.0f;
+ mMagnitude = mData.GetMagnitude();
+ mMagnitudeDelta = 0.0f;
+
+ mTargetPosition.Set( 0.0f, 0.0f, 0.0f );
+ mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+
+ mGroundOffset.Set( 0.0f, 0.0f, 0.0f );
+}
+
+//==============================================================================
+// FollowCam::~FollowCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+FollowCam::~FollowCam()
+{
+}
+
+//=============================================================================
+// FollowCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::Update( unsigned int milliseconds )
+{
+ BEGIN_PROFILE("FollowCam::Update")
+ rAssert( mTargets[ mActiveTarget ] );
+
+ //This is to adjust interpolation when we're running substeps.
+ float timeMod = (float)milliseconds / EXPECTED_FRAME_RATE;
+
+ rmt::Vector rod;
+ ISuperCamTarget* target = mTargets[ mActiveTarget ];
+
+#ifdef CUT_LOOK
+#if defined(RAD_GAMECUBE) || defined(RAD_XBOX)
+ float leftRight = mController->GetValue( SuperCamController::stickX );
+#elif defined(RAD_WIN32)
+ float left = mController->GetValue( SuperCamController::carLookLeft );
+ float right = mController->GetValue( SuperCamController::carLookRight );
+ float leftRight = ( right > left ) ? right : -left;
+#else //This is PS2
+ float leftRight = mController->GetValue( SuperCamController::r2 ) - mController->GetValue( SuperCamController::l2 );
+#endif
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ if ( mController->IsWheel() )
+ {
+ //This is a wheel. No left right on wheels.
+ leftRight = 0.0f;
+ }
+#endif
+
+ if ( rmt::Fabs(leftRight) > 0.2f && !GetFlag( (Flag)UNSTABLE ) )
+ {
+ SetFlag( (Flag)LOOKING_SIDEWAYS, true );
+ SetFlag( (Flag)CUT, true );
+ }
+ else if ( rmt::Fabs(leftRight) <= 0.2f && GetFlag( (Flag)LOOKING_SIDEWAYS ) )
+ {
+ SetFlag( (Flag)LOOKING_SIDEWAYS, false );
+ SetFlag( (Flag)CUT, true );
+ }
+ else
+#endif
+
+ if ( GetFlag( (Flag)LOOK_BACK ) &&
+ !GetFlag( (Flag)LOOKING_BACK ) &&
+ !GetFlag( (Flag)UNSTABLE ) )
+ {
+ //Turn on
+ SetFlag( (Flag)LOOKING_BACK, true );
+ SetFlag( (Flag)CUT, true );
+ }
+ else if ( GetFlag( (Flag)LOOKING_BACK ) &&
+ !GetFlag( (Flag)LOOK_BACK ) &&
+ !GetFlag( (Flag)UNSTABLE ) )
+ {
+ //Turn off.
+ SetFlag( (Flag)LOOKING_BACK, false );
+ SetFlag( (Flag)CUT, true );
+ }
+
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ DoCameraCut();
+ }
+
+ if ( GetFlag( (Flag)UNSTABLE ) )
+ {
+ UpdateUnstable( milliseconds );
+ }
+
+ //--------- Test to see if the target is still unstable
+
+ if ( !GetFlag((Flag)FIRST_TIME) &&
+ !GetFlag((Flag)CUT) &&
+ ( target->IsUnstable() || target->IsQuickTurn() || target->IsAirborn() ) &&
+ !GetSuperCamManager()->GetSCC( GetPlayerID() )->IsCutCam()
+ )
+ {
+ if ( target->IsQuickTurn() )
+ {
+ //Say that going unstable is due to quick turn,
+ //so when we come out of unstable, speed up interpolation.
+ SetFlag( (Flag)QUICK_TURN_ALERT, true );
+ }
+
+ if ( mUnstableDelayTimeLeft == 0 )
+ {
+ //Going unstable anew
+ InitUnstable();
+ }
+
+ //Reset the unstable clock.
+ mUnstableDelayTimeLeft = mData.GetUnstableDelay();
+ SetFlag((Flag)UNSTABLE, true );
+ }
+
+ BEGIN_PROFILE("Cam::Rod")
+ //--------- Figure out the new rod thing.
+ if ( GetFlag( (Flag)UNSTABLE ) )
+ {
+ //Get the unstable rod.
+ rod = mUnstablePosition;
+
+ //Deal with collision
+ //rAssert( mNumCollisions == 0 );
+ }
+ else
+ {
+ CalculateRod( &rod, milliseconds, timeMod );
+ }
+ END_PROFILE("Cam::Rod")
+
+ rmt::Vector desiredPosition = rod;
+
+ //--------- Get the position in space of the target to apply to desired values.
+ rmt::Vector targetPos;
+ target->GetPosition( &targetPos );
+
+ //--------- Set the desired position of the camera
+ desiredPosition.Add( targetPos );
+
+ BEGIN_PROFILE("FollowCam::Target")
+ //--------- Set the desired position of the camera target
+ rmt::Vector desiredTarget;
+ GetTargetPosition( &desiredTarget );
+
+ if ( GetFlag( (Flag)UNSTABLE ) )
+ {
+ desiredTarget.Add( targetPos, mUnstableTarget );
+ }
+ else
+ {
+ CalculateTarget( &desiredTarget, milliseconds, timeMod );
+ }
+
+ //TODO: Connect this camera to the physics system.
+ END_PROFILE("FollowCam::Target")
+
+ if ( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_SPEED_CAM ) )
+ {
+ SetFOV( 1.608495f );
+ }
+ else
+ {
+ SetFOV( mData.GetFOV() );
+ }
+
+ bool stabilizingToggle = false;
+
+ rmt::Vector lookFrom = targetPos;
+ rmt::Vector lookTo = desiredPosition;
+
+ IntersectionList& iList = GetSuperCamManager()->GetSCC( GetPlayerID() )->GetIntersectionList();
+
+ if ( !iList.LineOfSight( lookFrom, lookTo, 0.01f, true ) )
+ {
+ bool badSituation = false;
+
+ if ( GetFlag( (Flag)LOOK_BACK ) )
+ {
+ //Go somewhere else.
+ rmt::Vector intersection;
+ if ( iList.TestIntersectionStatics( lookFrom, lookTo, &intersection ) )
+ {
+ desiredPosition = intersection;
+
+ rmt::Vector localPos = desiredPosition;
+ localPos.Sub( targetPos );
+
+ rmt::CartesianToSpherical( localPos.x, localPos.z, localPos.y,
+ &mMagnitude, &mRotationAngleXZ, &mRotationAngleY );
+
+ mRotationAngleXZDelta = 0.0f;
+ mRotationAngleYDelta = 0.0f;
+ mMagnitudeDelta = 0.0f;
+
+ lookFrom = targetPos;
+ lookTo = desiredPosition;
+
+ if ( !iList.LineOfSight( lookFrom, lookTo, 0.01f, true ) )
+ {
+ badSituation = true;
+ }
+ }
+ }
+ else
+ {
+ SetFlag( (Flag)CUT, true );
+ DoCameraCut();
+ CalculateRod( &rod, milliseconds, timeMod );
+ desiredPosition = rod;
+ desiredPosition.Add( targetPos );
+
+ lookFrom = targetPos;
+ lookTo = desiredPosition;
+
+ if ( !iList.LineOfSight( lookFrom, lookTo, 0.01f, true ) )
+ {
+ badSituation = true;
+ }
+
+ SetFlag( (Flag)LOS_CORRECTED, true );
+ }
+
+ if ( badSituation )
+ {
+ iList.TestIntersectionStatics( lookTo, lookFrom, &desiredPosition );
+
+ rmt::Vector distVec;
+ distVec.Sub( targetPos, desiredPosition );
+ float minDist = mData.GetMagnitude() - 2.0f; //Arbitrary?
+ if ( distVec.MagnitudeSqr() < ( minDist * minDist ) )
+ {
+ desiredPosition = targetPos;
+ desiredPosition.Add( rmt::Vector( 0.0f, 6.0f, 0.0f ) ); //BAD!
+
+/* rmt::Vector heading;
+ mTargets[ 0 ]->GetHeading( &heading );
+
+ if ( GetFlag( (Flag)LOOKING_BACK ) )
+ {
+ heading.x *= -1.0f;
+ heading.z *= -1.0f;
+ }
+
+ heading.Scale( 2.0f );
+
+ desiredTarget.Add( desiredPosition, heading );
+*/
+ }
+ }
+
+ //Better clear the unstable flag, just in case.
+ //Clear the unstable flag.
+ SetFlag( (Flag)UNSTABLE, false );
+ stabilizingToggle = true;
+ mUnstableDelayTimeLeft = 0;
+ }
+
+ //--------- Set the new camera values
+ BEGIN_PROFILE("FollowCam::SetCameraValues")
+ SetCameraValues( milliseconds, desiredPosition, desiredTarget );
+ END_PROFILE("FollowCam::SetCameraValues")
+
+ SetFlag( (Flag)FIRST_TIME, false );
+ SetFlag( (Flag)LOOK_BACK, false );
+ SetFlag( (Flag)STABILIZING, stabilizingToggle );
+ SetFlag( (Flag)CUT, stabilizingToggle );
+ //SetFlag( (Flag)LOOKING_SIDEWAYS, false );
+ END_PROFILE("FollowCam::Update")
+
+ //Save the old position of the car in case we go unstable next update.
+ GetTargetPosition( &mOldTargetPos, false );
+}
+
+//=============================================================================
+// FollowCam::UpdateForPhysics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::UpdateForPhysics( unsigned int milliseconds )
+{
+ bool collision = false;
+
+ //This is to correct for collisions.
+ //Adjust for collision;
+ rmt::Vector currentPosition, desiredPosition, delta;
+ GetPosition( &currentPosition );
+
+ desiredPosition = currentPosition;
+ delta.Set( 0.0f, 0.0f, 0.0f );
+
+ unsigned int i;
+ for ( i = 0; i < mNumCollisions; ++i )
+ {
+ rmt::Vector camHeading;
+ GetHeading( &camHeading );
+ camHeading.NormalizeSafe();
+ if ( camHeading.DotProduct( mCollisionOffset[ i ] ) >= -0.7f )
+ {
+ desiredPosition.Add( mCollisionOffset[ i ] );
+ }
+ else if ( !collision )
+ {
+ rmt::Vector fakedCollision = camHeading;
+ fakedCollision *= GetNearPlane() * 1.5f;
+ desiredPosition.Add( fakedCollision );
+ }
+
+ collision = true;
+ }
+
+ float lag = mData.GetCollisionLag();
+ lag *= milliseconds / 16.0f;
+ CLAMP_TO_ONE( lag );
+
+ MotionCubic( &currentPosition.x, &delta.x, desiredPosition.x, lag );
+ MotionCubic( &currentPosition.y, &delta.y, desiredPosition.y, lag );
+ MotionCubic( &currentPosition.z, &delta.z, desiredPosition.z, lag );
+
+ //------- Check to see if we've intersected anything
+ if ( mGroundOffset.MagnitudeSqr() > 0.001f )
+ {
+ currentPosition.Add( mGroundOffset );
+ collision = true;
+ }
+
+ rmt::Vector targetPos;
+ GetTarget( &targetPos );
+
+ SetCameraValues( 0, currentPosition, targetPos ); //0.0f, no extra transition please.
+
+ SetFlag((Flag)COLLIDING, collision );
+
+ if ( collision )
+ {
+ mXAxis = mController->GetAxisValue( SuperCamController::stickX );
+ }
+}
+
+
+//=============================================================================
+// FollowCam::LoadSettings
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned char* settings )
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::LoadSettings( unsigned char* settings )
+{
+}
+
+//=============================================================================
+// FollowCam::CopyToData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::CopyToData()
+{
+ // dlong: it's fun to misuse other people's code
+ mData.SetAspect( GetAspect() );
+}
+
+//=============================================================================
+// FollowCam::SetTarget
+//=============================================================================
+// Description: This selects the current active target.
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::SetTarget( ISuperCamTarget* target )
+{
+ rAssert( target );
+
+ mTargets[ 0 ] = target;
+ mNumTargets = 1;
+
+ mActiveTarget = 0;//Set it to the first one.
+
+ FollowCamDataChunk* fcD = SuperCamCentral::FindFCD( target->GetID() + static_cast<unsigned int>(mFollowType) * FOLLOW_ID_OFFSET );
+
+ if ( fcD )
+ {
+ //Load the data.
+ //mData.SetRotationXZ( fcD->mRotation );
+ mData.SetRotationY( fcD->mElevation );
+ mData.SetMagnitude( fcD->mMagnitude );
+ mData.SetTargetOffset( fcD->mTargetOffset );
+
+ return;
+ }
+
+ rDebugString( "There should have been camera data loaded!" );
+}
+
+//=============================================================================
+// FollowCam::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::AddTarget( ISuperCamTarget* target )
+{
+ rAssert( mNumTargets < MAX_TARGETS );
+ rAssert( target );
+
+ if ( mNumTargets < MAX_TARGETS )
+ {
+ //Add the target
+ mTargets[ mNumTargets ] = target;
+
+ ++mNumTargets;
+ }
+}
+
+//=============================================================================
+// FollowCam::EnableShake
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::EnableShake()
+{
+ SetShaker( &mSineCosShaker );
+ SuperCam::EnableShake();
+}
+
+//=============================================================================
+// FollowCam::DisableShake
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::DisableShake()
+{
+ SuperCam::DisableShake();
+}
+
+//=============================================================================
+// FollowCam::ShouldReverse
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool FollowCam::ShouldReverse() const
+{
+ rAssert( mTargets[0] != NULL );
+ rmt::Vector v;
+ mTargets[0]->GetVelocity( &v );
+
+ return !mTargets[0]->IsAirborn() &&
+ !mTargets[0]->IsUnstable() &&
+ mTargets[0]->IsInReverse() &&
+ ((v.MagnitudeSqr() > MIN_REVERSE_VELOCITY ) || mNumCollisions > 0 );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// FollowCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\Follow", GetPlayerID() );
+
+ radDbgWatchAddFloat( &mData.mRotationXZ, "Rotation XZ", nameSpace, &SetDirtyCB, this, 0.0f, rmt::PI_2 );
+ radDbgWatchAddFloat( &mData.mRotationY, "Rotation Y", nameSpace, &SetDirtyCB, this, 0.001f, rmt::PI_BY2 );
+ radDbgWatchAddFloat( &mData.mMagnitude, "Magnitude", nameSpace, &SetDirtyCB, this, 2.0f, 50.0f );
+
+ radDbgWatchAddFloat( &mData.mCameraLagXZ, "Camera Lag XZ", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &mData.mCameraLagY, "Camera Lag Y", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &mData.mMagnitudeLag, "Magnitude Lag", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &mData.mCollisionLag, "Collision Lag", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f );
+
+ radDbgWatchAddVector( &mData.mTargetOffset.x, "Target Offset", nameSpace, &SetDirtyCB, this, 0.0f, 100.0f );
+
+ radDbgWatchAddFloat( &mData.mTargetLagXZ, "Target Lag XZ", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &mData.mTargetLagY, "Target Lag Y", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f );
+
+ radDbgWatchAddUnsignedInt( &mData.mUnstableDelay, "Unstable Delay", nameSpace, &SetDirtyCB, this, 0, 1000 );
+
+ radDbgWatchAddUnsignedInt( &mData.mQuickTurnDelay, "Quick-turn Delay", nameSpace, &SetDirtyCB, this, 0, 1000 );
+ radDbgWatchAddFloat( &mData.mQuickTurnModifier, "Quick-turn Modifier", nameSpace, &SetDirtyCB, this, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat( &MIN_REVERSE_VELOCITY, "Min reverse v", nameSpace, NULL, this, 10.0f, 300.0f );
+
+ radDbgWatchAddFloat( &CREEPY_TWIST, "Twist", nameSpace, NULL, NULL, 0.0f, rmt::PI_2 );
+
+#if defined(LOOK_TURN) || defined(CUT_LOOK)
+ radDbgWatchAddFloat( &LOOK_OFFSET_DIST, "Side/Up Look Dist", nameSpace, NULL, NULL, 0.0f, 20.0f );
+ radDbgWatchAddFloat( &LOOK_OFFSET_HEIGHT, "Side/Up Look height", nameSpace, NULL, NULL, 0.0f, 5.0f );
+ radDbgWatchAddFloat( &LOOK_OFFSET_BACK, "Side/Up Look pos Z", nameSpace, NULL, NULL, 0.0f, -5.0f );
+#endif
+
+#ifdef EXTRA_ROT
+ radDbgWatchAddFloat( &LOOK_ROT, "Side Look Rot", nameSpace, NULL, NULL, 0.01f, rmt::PI_BY2 );
+#endif
+
+#endif
+}
+
+//=============================================================================
+// FollowCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mData.mRotationXZ );
+ radDbgWatchDelete( &mData.mRotationY );
+ radDbgWatchDelete( &mData.mMagnitude );
+
+ radDbgWatchDelete( &mData.mCameraLagXZ );
+ radDbgWatchDelete( &mData.mCameraLagY );
+ radDbgWatchDelete( &mData.mMagnitudeLag );
+ radDbgWatchDelete( &mData.mCollisionLag );
+
+ radDbgWatchDelete( &mData.mTargetOffset.x );
+
+ radDbgWatchDelete( &mData.mTargetLagXZ );
+ radDbgWatchDelete( &mData.mTargetLagY );
+
+ radDbgWatchDelete( &mData.mUnstableDelay );
+
+ radDbgWatchDelete( &mData.mQuickTurnDelay );
+ radDbgWatchDelete( &mData.mQuickTurnModifier );
+
+ radDbgWatchDelete( &MIN_REVERSE_VELOCITY );
+
+ radDbgWatchDelete( &CREEPY_TWIST );
+
+#if defined(LOOK_TURN) || defined(CUT_LOOK)
+ radDbgWatchDelete( &LOOK_OFFSET_DIST );
+ radDbgWatchDelete( &LOOK_OFFSET_HEIGHT );
+ radDbgWatchDelete( &LOOK_OFFSET_BACK );
+#endif
+
+#ifdef EXTRA_ROT
+ radDbgWatchDelete( &LOOK_ROT );
+#endif
+#endif
+}
+
+//=============================================================================
+// FollowCam::DoCameraCut
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::DoCameraCut()
+{
+ SetFlag( (Flag)CUT, true );
+
+ //We're doing a camera cut so let's set the position to the default.
+ rmt::Matrix mat;
+ rmt::Vector targetHeading, targetVUP;
+ mTargets[ mActiveTarget ]->GetHeading( &targetHeading );
+ mTargets[ mActiveTarget ]->GetVUP( &targetVUP );
+ mat.Identity();
+ mat.FillHeading( targetHeading, targetVUP );
+
+ rmt::Vector desiredRod;
+ GetDesiredRod( &desiredRod );
+
+ //Get the rod into the target's space
+ desiredRod.Transform( mat );
+
+ rmt::CartesianToSpherical( desiredRod.x, desiredRod.z, desiredRod.y,
+ &mMagnitude, &mRotationAngleXZ, &mRotationAngleY );
+
+ //Also, reset the deltas.
+ mRotationAngleXZDelta = 0.0f;
+ mRotationAngleYDelta = 0.0f;
+ mMagnitudeDelta = 0.0f;
+
+#ifdef CUT_LOOK
+ if ( GetFlag( (Flag)LOOKING_SIDEWAYS ) )
+ {
+ GetTargetPosition( &mTargetPosition, false );
+ mTargetPosition.Add( rmt::Vector( 0.0f, LOOK_OFFSET_HEIGHT, 0.0f ) );
+ }
+ else
+ {
+ //mTargetPosition.Set( 0.0f, 0.0f, 0.0f );
+ GetTargetPosition( &mTargetPosition );
+ }
+#else
+ //mTargetPosition.Set( 0.0f, 0.0f, 0.0f );
+ GetTargetPosition( &mTargetPosition );
+#endif
+
+ mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+
+ //Reset the FOV and aspect ratio for this camera.
+ SetFOV( mData.GetFOV() );
+ SetAspect( mData.GetAspect() );
+
+ SetFlag( (Flag)UNSTABLE, false );
+}
+
+//=============================================================================
+// FollowCam::InitUnstable
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::InitUnstable()
+{
+ //First time unstable since recovered.
+ //Set up the unstable rod
+
+ rmt::Vector targetPosition;
+ //GetTargetPosition( &targetPosition, false );
+ targetPosition = mOldTargetPos;
+
+ rmt::Vector camPosition;
+ GetPosition( &camPosition );
+
+ mUnstablePosition.Sub( camPosition, targetPosition );
+
+ rmt::Vector camTarget;
+ GetTarget( &camTarget );
+
+ mUnstableTarget.Sub( camTarget, targetPosition );
+
+ //Set the unstable flag
+ SetFlag( (Flag)UNSTABLE, true );
+}
+
+//=============================================================================
+// FollowCam::UpdateUnstable
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::UpdateUnstable( unsigned int milliseconds )
+{
+ mUnstableDelayTimeLeft -= milliseconds;
+
+ if ( mUnstableDelayTimeLeft <= 0 )
+ {
+ //Finished being unstable. Let the camera drift back to it's normal
+ //position. If this is a speed up camera situation, we should
+ //accelerate the rotation.
+
+ mUnstableDelayTimeLeft = 0;
+
+ mUnstablePosition.Set( 0.0f, 0.0f, 0.0f );
+ mUnstableTarget.Set( 0.0f, 0.0f, 0.0f );
+
+ mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+
+ //Clear the unstable flag.
+ SetFlag( (Flag)UNSTABLE, false );
+
+ if ( GetFlag( (Flag)QUICK_TURN_ALERT ) )
+ {
+ SetFlag( (Flag)QUICK_TURN_ALERT, false );
+ SetFlag( (Flag)QUICK_TURN, true );
+
+ InitQuickTurn();
+ }
+
+ //This is to internally reset the state of the follow cam. By doing
+ //The unstable stuff we've hosed the "memory" of the rotations.
+ SetFlag( (Flag)STABILIZING, true );
+ }
+}
+
+//=============================================================================
+// FollowCam::InitQuickTurn
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::InitQuickTurn()
+{
+ mQuickTurnTimeLeft = mData.GetQuickTurnDelay();
+}
+
+//=============================================================================
+// FollowCam::UpdateQuickTurn
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::UpdateQuickTurn( unsigned int milliseconds )
+{
+ mQuickTurnTimeLeft -= milliseconds;
+
+ if ( mQuickTurnTimeLeft <= 0 )
+ {
+ //Done the quick turn, back to normal speed now.
+ SetFlag( (Flag)QUICK_TURN, false );
+ mQuickTurnTimeLeft = 0;
+ }
+}
+
+//=============================================================================
+// FollowCam::GetTargetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* position, bool withOffset )
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::GetTargetPosition( rmt::Vector* position,
+ bool withOffset )
+{
+ mTargets[ mActiveTarget ]->GetPosition( position );
+
+ if ( withOffset )
+ {
+ rmt::Vector offset;
+ mData.GetTargetOffset( &offset );
+
+ if ( GetFlag( (Flag)LOOK_BACK ) )
+ {
+ offset.x *= -1.0f;
+ offset.z *= -1.0f;
+ }
+
+ //Add offset according to controller input.
+ float lookUp = 0.0f;
+
+#ifdef TURN_LOOK
+ float lookLeftRight = 0.0f;
+
+#if defined(RAD_GAMECUBE) || defined(RAD_XBOX)
+ lookUp = mController->GetValue( SuperCamController::stickY );
+ lookLeftRight = mController->GetValue( SuperCamController::stickX );
+#elif defined(RAD_WIN32)
+ lookUp = mController->GetValue( SuperCamController::carLookUp );
+ float left = mController->GetValue( SuperCamController::carLookLeft );
+ float right = mController->GetValue( SuperCamController::carLookRight );
+ lookLeftRight = ( right > left ) ? right : -left;
+#else //This is PS2
+ lookUp = mController->GetValue( SuperCamController::lookToggle );
+ lookLeftRight = mController->GetValue( SuperCamController::r2 ) - mController->GetValue( SuperCamController::l2 );
+#endif
+#else
+#if defined(RAD_GAMECUBE) || defined(RAD_XBOX)
+ lookUp = mController->GetValue( SuperCamController::stickY );
+#elif defined(RAD_WIN32)
+ lookUp = mController->GetValue( SuperCamController::carLookUp );
+#else //This is PS2
+ lookUp = mController->GetValue( SuperCamController::lookToggle );
+#endif
+#endif
+
+#ifdef TURN_LOOK
+ if ( GetCharacterSheetManager()->QueryInvertedCameraSetting() )
+ {
+ //Invert this!
+ lookLeftRight *= -1.0f;
+ }
+
+ offset.x += ( LOOK_OFFSET_DIST * lookLeftRight );
+#endif
+
+ if ( lookUp > 0.2f )
+ {
+ offset.y += ( LOOK_OFFSET_DIST * 1.0f ); //Make it digital.
+ }
+
+ //Now put the offset in the target's space
+ rmt::Matrix mat;
+ rmt::Vector targetHeading, targetVUP;
+ mTargets[ mActiveTarget ]->GetHeading( &targetHeading );
+ mTargets[ mActiveTarget ]->GetVUP( &targetVUP );
+ mat.Identity();
+ mat.FillHeading( targetHeading, targetVUP );
+
+ offset.Transform( mat );
+
+ (*position).Add( offset );
+ }
+
+}
+
+
+//=============================================================================
+// FollowCam::CalculateRod
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* rod, unsigned int milliseconds, float timeMod )
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::CalculateRod( rmt::Vector* rod,
+ unsigned int milliseconds,
+ float timeMod )
+{
+ //Where is the camera in spherical coordinates?
+
+ //This is the targets matrix and inverse matrix for rotation.
+ rmt::Matrix mat;
+ rmt::Vector targetHeading, targetVUP;
+ mTargets[ mActiveTarget ]->GetHeading( &targetHeading );
+ mTargets[ mActiveTarget ]->GetVUP( &targetVUP );
+
+ //Never go upside down.
+ targetVUP.y = rmt::Fabs( targetVUP.y );
+
+ mat.Identity();
+ mat.FillHeading( targetHeading, targetVUP );
+
+ rmt::Vector desiredRod;
+
+ GetDesiredRod( &desiredRod );
+ desiredRod.Transform( mat );
+
+ //--------- Do the look back
+ if( GetFlag( (Flag)LOOK_BACK ) )
+ {
+ //reverse the rod.
+ desiredRod.x = -desiredRod.x;
+ desiredRod.z = -desiredRod.z;
+ }
+
+
+ //This puts us in spherical space, also note the z and y switch.
+ float desiredRotXZ, desiredRotY, desiredMag;
+ rmt::CartesianToSpherical( desiredRod.x, desiredRod.z, desiredRod.y,
+ &desiredMag, &desiredRotXZ, &desiredRotY );
+
+
+#ifdef EXTRA_ROT
+ float invertMod = GetSuperCamManager()->GetSCC( GetPlayerID() )->IsInvertedCameraEnabled() ? -1.0f : 1.0f;
+#if defined(RAD_GAMECUBE) || defined(RAD_XBOX)
+ float leftRight = -mController->GetAxisValue( SuperCamController::stickX );
+#elif defined(RAD_WIN32)
+ float left = mController->GetValue( SuperCamController::carLookLeft );
+ float right = mController->GetValue( SuperCamController::carLookRight );
+ float leftRight = ( right > left ) ? -right : left;
+#else //This is PS2
+ float leftRight = mController->GetValue( SuperCamController::l2 ) - mController->GetValue( SuperCamController::r2 );
+#endif
+
+ if ( GetFlag( (Flag)LOS_CORRECTED ) && IsPushingStick() )
+ {
+ leftRight = 0.0f;
+ }
+ else
+ {
+ SetFlag( (Flag)LOS_CORRECTED, false );
+ }
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ if ( mController->IsWheel() )
+ {
+ //This is a wheel. No left right on wheels.
+ //Or we are stopping the user from continuing to force the stick into a wall.
+ leftRight = 0.0f;
+ }
+#endif
+
+ if ( !GetFlag( (Flag)LOOK_BACK ) )
+ {
+#ifdef RAD_WIN32 // this retarded move is thanks to vs.net optimization.
+ desiredRotXZ += ( invertMod * leftRight * rmt::PI_BY2 );
+#else
+ desiredRotXZ += ( invertMod * leftRight * LOOK_ROT );
+#endif
+ }
+#endif
+
+
+ //--------- Setup the desired angle and the current angle.
+
+ if ( !GetFlag((Flag)CUT) &&
+ (GetFlag( (Flag)FIRST_TIME ) || GetFlag( (Flag)STABILIZING ) || GetFlag((Flag)COLLIDING )) )
+ {
+ //Let's try to interpolate from it's current position.
+ rmt::Vector position;
+ GetPosition( &position );
+
+ rmt::Vector target;
+ if ( GetFlag( (Flag)FIRST_TIME ) )
+ {
+ mTargets[ mActiveTarget]->GetPosition( &target );
+ }
+ else
+ {
+ //Use the old position since we're stabilizing from badness.
+ target = mOldTargetPos;
+ }
+
+ rmt::Vector targToPos;
+ targToPos.Sub( position, target );
+
+ rmt::CartesianToSpherical( targToPos.x, targToPos.z, targToPos.y, &mMagnitude, &mRotationAngleXZ, &mRotationAngleY );
+ mMagnitudeDelta = 0.0f;
+ mRotationAngleXZDelta = 0.0f;
+ mRotationAngleYDelta = 0.0f;
+ }
+
+ //We only want to interpolate to a rotation via the fewest number of degrees.
+ AdjustAngles( &desiredRotXZ, &mRotationAngleXZ, &mRotationAngleXZDelta );
+ AdjustAngles( &desiredRotY, &mRotationAngleY, &mRotationAngleYDelta );
+
+ rmt::Clamp( desiredRotY, rmt::PI + rmt::PI_BY2, rmt::PI_BY2 );
+
+ //--------- Interpolate to the desired position and target
+
+ //This is the normal interpolation stage.
+ float cameraLagXZ = mData.GetCameraLagXZ() * timeMod;
+ float cameraLagY = mData.GetCameraLagY() * timeMod;
+ float magnitudeLag = mData.GetMagnitudeLag() * timeMod;
+
+ UpdateQuickTurn( milliseconds );
+
+ if ( GetFlag( (Flag)QUICK_TURN ) )
+ {
+ //Change the speed of interpolation to the adjusted value.
+ cameraLagXZ *= mData.GetQuickTurnModifier();
+ CLAMP_TO_ONE(cameraLagXZ);
+ }
+
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ cameraLagXZ = 1.0f;
+ cameraLagY = 1.0f;
+ magnitudeLag = 1.0f;
+ }
+ else
+ {
+ CLAMP_TO_ONE( cameraLagXZ );
+ CLAMP_TO_ONE( cameraLagY );
+ CLAMP_TO_ONE( magnitudeLag );
+ }
+
+ MotionCubic( &mRotationAngleXZ, &mRotationAngleXZDelta, desiredRotXZ, cameraLagXZ );
+ MotionCubic( &mRotationAngleY, &mRotationAngleYDelta, desiredRotY, cameraLagY );
+ MotionCubic( &mMagnitude, &mMagnitudeDelta, desiredMag, magnitudeLag );
+
+ float x, y, z;
+ rmt::SphericalToCartesian( mMagnitude, mRotationAngleXZ, mRotationAngleY,
+ &x, &z, &y );
+ rod->Set( x, y, z );
+}
+
+//=============================================================================
+// FollowCam::CalculateTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* desiredTarget,
+// unsigned int milliseconds,
+// float timeMod )
+//
+// Return: void
+//
+//=============================================================================
+void FollowCam::CalculateTarget( rmt::Vector* desiredTarget,
+ unsigned int milliseconds,
+ float timeMod )
+{
+#ifdef CUT_LOOK
+ if ( GetFlag( (Flag)LOOKING_SIDEWAYS ) )
+ {
+ GetTargetPosition( &mTargetPosition, false );
+ mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+ *desiredTarget = mTargetPosition;
+ desiredTarget->Add( rmt::Vector( 0.0f, LOOK_OFFSET_HEIGHT, 0.0f ) );
+ return;
+ }
+#endif
+
+ if ( !GetFlag((Flag)CUT) &&
+ (GetFlag( (Flag)FIRST_TIME ) ||
+ (GetFlag( (Flag)STABILIZING ))) )
+ {
+ GetTarget( &mTargetPosition );
+ mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+ }
+
+ GetTargetPosition( desiredTarget );
+
+ //Here's the target position and interpolation.
+ float targetLagXZ = mData.GetTargetLagXZ() * timeMod;
+ CLAMP_TO_ONE(targetLagXZ);
+
+ float targetLagY = mData.GetTargetLagY() * timeMod;
+ CLAMP_TO_ONE(targetLagY);
+
+ MotionCubic( &mTargetPosition.x, &mTargetPositionDelta.x, desiredTarget->x, targetLagXZ );
+ MotionCubic( &mTargetPosition.y, &mTargetPositionDelta.y, desiredTarget->y, targetLagY );
+ MotionCubic( &mTargetPosition.z, &mTargetPositionDelta.z, desiredTarget->z, targetLagXZ );
+
+
+ //Set the target position
+ *desiredTarget = mTargetPosition;
+}
+
+//=============================================================================
+// FollowCam::GetDesiredRod
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* rod)
+//
+// Return: bool - this says whether the rod is in world space or not.
+//
+//=============================================================================
+bool FollowCam::GetDesiredRod( rmt::Vector* rod)
+{
+#ifdef CUT_LOOK
+
+ if ( !GetFlag( (Flag)LOOKING_SIDEWAYS ) )
+ {
+ //No movement.
+ mData.GetRod( rod );
+ return false;
+ }
+ else
+ {
+ float invertMod = GetSuperCamManager()->GetSCC( GetPlayerID() )->IsInvertedCameraEnabled() ? -1.0f : 1.0f;
+#if defined(RAD_GAMECUBE) || defined(RAD_XBOX)
+ float leftRight = mController->GetValue( SuperCamController::stickX );
+#elif defined(RAD_WIN32)
+ float left = mController->GetValue( SuperCamController::carLookLeft );
+ float right = mController->GetValue( SuperCamController::carLookRight );
+ float leftRight = ( right > left ) ? right : -left;
+#else //This is PS2
+ float leftRight = mController->GetValue( SuperCamController::r2 ) - mController->GetValue( SuperCamController::l2 );
+#endif
+
+ float dir = rmt::Sign( leftRight );
+ //Look right so move left (I like it inverted here.)
+ rod->Set( dir * invertMod * LOOK_OFFSET_DIST, LOOK_OFFSET_HEIGHT, LOOK_OFFSET_BACK );
+ return false;
+ }
+
+#endif
+ mData.GetRod( rod );
+ return false;
+}
+
+//=============================================================================
+// FollowCam::IsPushingStick
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool FollowCam::IsPushingStick()
+{
+#if defined(RAD_XBOX) || defined(RAD_GAMECUBE)
+ float xAxis = mController->GetValue( SuperCamController::stickX );
+#elif defined(RAD_WIN32)
+ float left = mController->GetValue( SuperCamController::carLookLeft );
+ float right = mController->GetValue( SuperCamController::carLookRight );
+ float xAxis = ( right > left ) ? right : -left;
+#else
+ float xAxis = mController->GetValue( SuperCamController::l2 ) - mController->GetValue( SuperCamController::r2 );
+#endif
+
+ return ( !rmt::Epsilon( xAxis, 0.0f, 0.001f ) &&
+ rmt::Sign( xAxis ) == rmt::Sign( mXAxis ) );
+}
+
diff --git a/game/code/camera/followcam.h b/game/code/camera/followcam.h
new file mode 100644
index 0000000..0eecb70
--- /dev/null
+++ b/game/code/camera/followcam.h
@@ -0,0 +1,342 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: followcam.h
+//
+// Description: The follow cam is the main camera mode for in-car gameplay.
+// It works though interpolating the angles of a spherical
+// co-ordinate system localized to the target.
+//
+// The spherical co-ordinate system uses two angles and a
+// magnitude to describe a "rod" in space. The two angles are:
+// 1. An angle representing rotation in the XZ plane; and
+// 2. An angle representing rotation in the Y plane.
+//
+// The magnitude is simply the length of the "rod" going outward
+// from the target.
+//
+// Interpolation of the angles is calculated through knowing what
+// the current angle is and what the desired angle should be. A
+// "lag" factor is introduced that controls the interval of
+// interpolation. Essentially, the value represents the
+// parametric position after interpolation between the current
+// and desired angles.
+//
+// The values of the "lag" are between 0 and 1, where 0 results
+// in no interpolation and 1 is a "snap" to the desired angle ( a
+// value of one is also no interpolation, it is more like an
+// assignment of position )
+//
+// An internal value of rotation delta or "rate" holds the
+// intermediate value of the interpolation so that further
+// interpolation to a new desired angle is continuous.
+//
+// If the current angle is overridden by a camera cut, or other
+// means, the rotation delta must be reset to the same value or
+// offset by the same amount as the current angle was changed.
+// Otherwise, the interpolation will attept to interpolate to
+// some whacky asmyptote or possibly 0 or infinity...
+//
+// History: 22/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef FOLLOWCAM_H
+#define FOLLOWCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+//These are included in the precompiled headers on XBOX and GC
+#include <radmath/radmath.hpp>
+
+#include <camera/supercam.h>
+#include <camera/FollowCamData.h>
+
+//========================================
+// Forward References
+//========================================
+
+class ISuperCamTarget;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class FollowCam : public SuperCam
+{
+public:
+ enum
+ {
+ MAX_TARGETS = 3
+ };
+
+ enum Follow_Flag
+ {
+ FOLLOW = SUPERCAM_END, //This is to carry on from the supercam flags
+ UNSTABLE, //When the car is unstable this is true
+ STABILIZING,
+ QUICK_TURN_ALERT, //We have been in a quick turn situation
+ QUICK_TURN, //We are out of UNSTABLE, into QUICK_TURN
+ LOOKING_BACK,
+ LOOKING_SIDEWAYS,
+ COLLIDING,
+ LOS_CORRECTED //This is when we've corrected due to line of sight.
+ };
+
+ enum FollowType
+ {
+ FOLLOW_NEAR,
+ FOLLOW_FAR
+ };
+
+ FollowCam( FollowType type = FOLLOW_NEAR );
+ virtual ~FollowCam();
+
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds );
+ virtual void UpdateForPhysics( unsigned int milliseconds );
+
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ //This loads the off-line created settings for the camera.
+ //It is passed in as a byte stream of some data of known size.
+ virtual void LoadSettings( unsigned char* settings );
+
+ // copies the FollowCam's members into the FollowCamData structure
+ void CopyToData();
+
+ virtual Type GetType();
+
+ //These are for favourable support of this command
+ virtual void SetTarget( ISuperCamTarget* target );
+ virtual void AddTarget( ISuperCamTarget* target );
+
+ unsigned int GetNumTargets() const;
+
+ void SetDirty();
+
+ void EnableShake();
+ void DisableShake();
+
+ //Support for colliding with the world.
+ void SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset );
+ float GetCollisionRadius() const { return GetNearPlane() * 2.0f; };
+
+ virtual float GetIntersectionRadius() const { return mData.GetMagnitude(); };
+
+
+ bool ShouldReverse() const;
+
+protected:
+ virtual void OnInit();
+
+private:
+
+ ISuperCamTarget* mTargets[ MAX_TARGETS ];
+ unsigned int mNumTargets;
+ unsigned int mActiveTarget;
+
+ //Camera brains
+ float mRotationAngleXZ;
+ float mRotationAngleY;
+ float mMagnitude;
+
+ float mRotationAngleXZDelta;
+ float mRotationAngleYDelta;
+ float mMagnitudeDelta;
+
+ rmt::Vector mTargetPosition;
+ rmt::Vector mTargetPositionDelta;
+
+ rmt::Vector mUnstablePosition;
+ rmt::Vector mUnstableTarget;
+ rmt::Vector mOldTargetPos;
+
+ FollowCamData mData;
+
+ //Unstable time counter
+ int mUnstableDelayTimeLeft;
+
+ //Quick turn time counter
+ int mQuickTurnTimeLeft;
+
+ const rmt::Vector* mCollisionOffset;
+ unsigned int mNumCollisions;
+ rmt::Vector mGroundOffset;
+
+ FollowType mFollowType;
+
+ float mXAxis;
+
+ //These functions are to allow real-time control of the settings of
+ //the supercam.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+ void DoCameraCut();
+ void InitUnstable();
+ void UpdateUnstable( unsigned int milliseconds );
+
+ void InitQuickTurn();
+ void UpdateQuickTurn( unsigned int milliseconds );
+
+ void GetTargetPosition( rmt::Vector* position, bool withOffset = true );
+
+ void CalculateRod( rmt::Vector* rod,
+ unsigned int milliseconds,
+ float timeMod );
+
+ void CalculateTarget( rmt::Vector* desiredTarget,
+ unsigned int milliseconds,
+ float timeMod );
+
+ bool GetDesiredRod( rmt::Vector* rod );
+
+ bool IsPushingStick();
+
+ //Prevent wasteful constructor creation.
+ FollowCam( const FollowCam& followcam );
+ FollowCam& operator=( const FollowCam& followcam );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// FollowCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+
+inline const char* const FollowCam::GetName() const
+{
+ if ( mFollowType == FollowCam::FOLLOW_NEAR )
+ {
+ return "NEAR_FOLLOW_CAM";
+ }
+ else
+ {
+ return "FAR_FOLLOW_CAM";
+ }
+}
+
+//=============================================================================
+// unsigned int FollowCam::GetNumTargets const
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int FollowCam::GetNumTargets() const
+{
+ return mNumTargets;
+}
+
+//=============================================================================
+// FollowCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type FollowCam::GetType()
+{
+ return static_cast<SuperCam::Type>(NEAR_FOLLOW_CAM + mFollowType);
+}
+
+//=============================================================================
+// FollowCam::SetDirty
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCam::SetDirty()
+{
+ mData.SetDirty();
+}
+
+//=============================================================================
+// FollowCam::SetCollisionOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCam::SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
+{
+ mCollisionOffset = offset;
+ mNumCollisions = numCollisions;
+ mGroundOffset = groundOffset;
+}
+
+//*****************************************************************************
+//
+// Inline Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// FollowCam::OnInit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCam::OnInit()
+{
+ InitMyController();
+ mUnstableDelayTimeLeft = 0;
+}
+
+#endif //FOLLOWCAM_H
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/game/code/camera/followcamdata.h b/game/code/camera/followcamdata.h
new file mode 100644
index 0000000..0bbd59e
--- /dev/null
+++ b/game/code/camera/followcamdata.h
@@ -0,0 +1,662 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: followcamdata.h
+//
+// Description: Blahblahblah
+//
+// History: 22/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef FOLLOWCAMDATA_H
+#define FOLLOWCAMDATA_H
+
+//========================================
+// Nested Includes
+//========================================
+//These are included in the precompiled headers on XBOX and GC
+#include <radmath/radmath.hpp>
+#include <camera/supercamconstants.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class FollowCamData
+{
+public:
+ FollowCamData();
+ virtual ~FollowCamData() {};
+
+ //These are the spherical coordinates for the camera position
+ float GetRotationXZ() const;
+ void SetRotationXZ( float rotXZ );
+
+ float GetRotationY() const;
+ void SetRotationY( float rotY );
+
+ float GetMagnitude() const;
+ void SetMagnitude( float magnitude );
+
+ //Cartesian version of the above
+ void GetRod( rmt::Vector* rod );
+
+ //This is the offset from the target
+ void GetTargetOffset( rmt::Vector* offset ) const;
+ void SetTargetOffset( rmt::Vector offset );
+
+ //These set the interpolation speed (lag) of the positions.
+ float GetCameraLagXZ() const;
+ void SetCameraLagXZ( float lag );
+
+ float GetCameraLagY() const;
+ void SetCameraLagY( float lag );
+
+ float GetTargetLagXZ() const;
+ void SetTargetLagXZ( float lag );
+
+ float GetTargetLagY() const;
+ void SetTargetLagY( float lag );
+
+ float GetMagnitudeLag() const;
+ void SetMagnitudeLag( float lag );
+
+ float GetCollisionLag() const;
+ void SetCollisionLag( float lag );
+
+ unsigned int GetUnstableDelay() const;
+ void SetUnstableDelay( unsigned int delay );
+
+ unsigned int GetQuickTurnDelay() const;
+ void SetQuickTurnDelay( unsigned int delay );
+
+ float GetQuickTurnModifier() const;
+ void SetQuickTurnModifier( float modifier );
+
+ float GetAspect() const;
+ void SetAspect( float aspect );
+
+ float GetFOV() const;
+ void SetFOV( float fov );
+
+ void SetDirty();
+
+ //This is the default camera position.
+ float mRotationXZ;
+ float mRotationY;
+ float mMagnitude;
+
+ //This is the target offset.
+ rmt::Vector mTargetOffset;
+
+ //These are the rotation (XZ) and height (Y) damping values for camera
+ //movement
+ float mCameraLagXZ;
+ float mCameraLagY;
+
+ //Target lag could be broken into X, and Y in a 2D projection.
+ //or as before.
+ float mTargetLagXZ;
+ float mTargetLagY;
+
+ float mMagnitudeLag;
+
+ float mCollisionLag;
+
+ //This is how long the camera will stay unstable without additional
+ //unstable stimulus
+ unsigned int mUnstableDelay;
+
+ //This is how long the camera will stay in a quick turn state without\
+ //additional stimulus
+ unsigned int mQuickTurnDelay;
+ float mQuickTurnModifier;
+
+ float mAspect;
+ float mFOV;
+
+private:
+ //This is the rod built by the stuff above. (cartesian version)
+ rmt::Vector mRod;
+
+ bool mIsDirty : 1;
+
+ //Prevent wasteful constructor creation.
+ FollowCamData( const FollowCamData& followcamdata );
+ FollowCamData& operator=( const FollowCamData& followcamdata );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+inline FollowCamData::FollowCamData() :
+ mRotationXZ( 4.71238f ),
+ mRotationY( 1.2568370f ),
+ mMagnitude( 7.8559999f ),
+ //mCameraLagXZ( 0.044f ),
+ mCameraLagXZ( 0.044f ),
+ mCameraLagY( 0.028f ),
+ //mTargetLagXZ( 1.0f ),
+ mTargetLagXZ( 0.1f ),
+ mTargetLagY( 0.1f ),
+ mMagnitudeLag( 0.05f ),
+ mCollisionLag( 0.2f ),
+ mUnstableDelay( 200 ),
+ mQuickTurnDelay( 0 ),
+ mQuickTurnModifier( 0.0f ),
+ mAspect( SUPERCAM_ASPECT ),//4.0f / 3.0f ),
+#ifdef RAD_XBOX
+ mFOV( 1.45f ),
+#else
+ mFOV( 1.363451f ),
+#endif
+ mIsDirty( true )
+{
+ mTargetOffset.Set( 0.0f, 0.5f, 1.2f );
+}
+
+//=============================================================================
+// FollowCamData::GetRotationXZ
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FollowCamData::GetRotationXZ() const
+{
+ return mRotationXZ;
+}
+
+//=============================================================================
+// FollowCamData::SetRotationXZ
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float rotXZ )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetRotationXZ( float rotXZ )
+{
+ mRotationXZ = rotXZ;
+ mIsDirty = true;
+}
+
+//=============================================================================
+// FollowCamData::GetRotationY
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FollowCamData::GetRotationY() const
+{
+ return mRotationY;
+}
+
+//=============================================================================
+// FollowCamData::SetRotationY
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float rotY )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetRotationY( float rotY )
+{
+ mRotationY = rotY;
+ mIsDirty = true;
+}
+
+//=============================================================================
+// FollowCamData::GetMagnitude
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FollowCamData::GetMagnitude() const
+{
+ return mMagnitude;
+}
+
+//=============================================================================
+// FollowCamData::SetMagnitude
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetMagnitude( float magnitude )
+{
+ mMagnitude = magnitude;
+ mIsDirty = true;
+}
+
+//=============================================================================
+// FollowCamData::GetRod
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* rod )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::GetRod( rmt::Vector* rod )
+{
+ if ( mIsDirty )
+ {
+ //Update the mRod since new values for rotations and / or magnitude
+ //have been added.
+
+ rmt::SphericalToCartesian( mMagnitude, mRotationXZ, mRotationY,
+ &mRod.x, &mRod.z, &mRod.y );
+
+ mIsDirty = false;
+ }
+
+ *rod = mRod;
+}
+
+//=============================================================================
+// FollowCamData::GetTargetOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* offset )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::GetTargetOffset( rmt::Vector* offset ) const
+{
+ *offset = mTargetOffset;
+}
+
+//=============================================================================
+// FollowCamData::SetTargetOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector offset )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetTargetOffset( rmt::Vector offset )
+{
+ mTargetOffset = offset;
+}
+
+
+//=============================================================================
+// FollowCamData::GetCameraLagXZ
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FollowCamData::GetCameraLagXZ() const
+{
+ return mCameraLagXZ;
+}
+
+//=============================================================================
+// FollowCamData::SetCameraLagXZ
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetCameraLagXZ( float lag )
+{
+ mCameraLagXZ = lag;
+}
+
+//=============================================================================
+// FollowCamData::GetCameraLagY
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FollowCamData::GetCameraLagY() const
+{
+ return mCameraLagY;
+}
+
+//=============================================================================
+// FollowCamData::SetCameraLagY
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetCameraLagY( float lag )
+{
+ mCameraLagY = lag;
+}
+
+//=============================================================================
+// FollowCamData::GetTargetLagXZ
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FollowCamData::GetTargetLagXZ() const
+{
+ return mTargetLagXZ;
+}
+
+//=============================================================================
+// FollowCamData::SetTargetLagXZ
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetTargetLagXZ( float lag )
+{
+ mTargetLagXZ = lag;
+}
+
+//=============================================================================
+// FollowCamData::GetTargetLagY
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FollowCamData::GetTargetLagY() const
+{
+ return mTargetLagY;
+}
+
+//=============================================================================
+// FollowCamData::SetTargetLagY
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetTargetLagY( float lag )
+{
+ mTargetLagY = lag;
+}
+
+//=============================================================================
+// FollowCamData::GetMagnitudeLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FollowCamData::GetMagnitudeLag() const
+{
+ return mMagnitudeLag;
+}
+
+//=============================================================================
+// FollowCamData::SetMagnitudeLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetMagnitudeLag( float lag )
+{
+ mMagnitudeLag = lag;
+}
+
+//=============================================================================
+// FollowCamData::GetCollisionLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FollowCamData::GetCollisionLag() const
+{
+ return mCollisionLag;
+}
+
+//=============================================================================
+// FollowCamData::SetCollisionLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetCollisionLag( float lag )
+{
+ mCollisionLag = lag;
+}
+//=============================================================================
+// FollowCamData::GetUnstableDelay
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline unsigned int FollowCamData::GetUnstableDelay() const
+{
+ return mUnstableDelay;
+}
+
+//=============================================================================
+// FollowCamData::SetUnstableDelay
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float delay )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetUnstableDelay( unsigned int delay )
+{
+ mUnstableDelay = delay;
+}
+
+//=============================================================================
+// FollowCamData::GetQuickTurnDelay
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int FollowCamData::GetQuickTurnDelay() const
+{
+ return mQuickTurnDelay;
+}
+
+//=============================================================================
+// FollowCamData::SetQuickTurnDelay
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int delay )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetQuickTurnDelay( unsigned int delay )
+{
+ mQuickTurnDelay = delay;
+}
+
+//=============================================================================
+// FollowCamData::GetQuickTurnModifier
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FollowCamData::GetQuickTurnModifier() const
+{
+ return mQuickTurnModifier;
+}
+
+//=============================================================================
+// FollowCamData::SetQuickTurnModifier
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float modifier )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetQuickTurnModifier( float modifier )
+{
+ mQuickTurnModifier = modifier;
+}
+
+//=============================================================================
+// FollowCamData::GetAspect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FollowCamData::GetAspect() const
+{
+ return mAspect;
+}
+
+//=============================================================================
+// FollowCamData::SetAspect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float aspect )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetAspect( float aspect )
+{
+ mAspect = aspect;
+}
+
+//=============================================================================
+// FollowCamData::GetFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FollowCamData::GetFOV() const
+{
+ return mFOV;
+}
+
+//=============================================================================
+// FollowCamData::SetFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float fov )
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetFOV( float fov )
+{
+ mFOV = fov;
+}
+
+//=============================================================================
+// FollowCamData::SetDirty
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void FollowCamData::SetDirty()
+{
+ mIsDirty = true;
+}
+
+#endif //FOLLOWCAMDATA_H
diff --git a/game/code/camera/followcamdatachunk.h b/game/code/camera/followcamdatachunk.h
new file mode 100644
index 0000000..c07f61f
--- /dev/null
+++ b/game/code/camera/followcamdatachunk.h
@@ -0,0 +1,100 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: followcamdatachunk.h
+//
+// Description: Blahblahblah
+//
+// History: 17/06/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef FOLLOWCAMDATACHUNK_H
+#define FOLLOWCAMDATACHUNK_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/entity.hpp>
+#include <radmath/radmath.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class FollowCamDataChunk
+{
+public:
+ FollowCamDataChunk() {};
+ virtual ~FollowCamDataChunk() { SetName( NULL ); };
+
+ void SetName( const char* name ) { mName.SetText( name ); };
+
+ unsigned int mID;
+
+ float mRotation;
+ float mElevation;
+ float mMagnitude;
+
+ rmt::Vector mTargetOffset;
+
+ tName mName;
+
+ FollowCamDataChunk& operator=( const FollowCamDataChunk& followcamdatachunk );
+ bool operator==( const FollowCamDataChunk& followcamdatachunk );
+private:
+ //Prevent wasteful constructor creation.
+ FollowCamDataChunk( const FollowCamDataChunk& followcamdatachunk );
+};
+
+//=============================================================================
+// ::operator=
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const FollowCamDataChunk& followcamdatachunk )
+//
+// Return: FollowCamDataChunk
+//
+//=============================================================================
+inline FollowCamDataChunk& FollowCamDataChunk::operator=( const FollowCamDataChunk& followcamdatachunk )
+{
+ mID = followcamdatachunk.mID;
+ mRotation = followcamdatachunk.mRotation;
+ mElevation = followcamdatachunk.mElevation;
+ mMagnitude = followcamdatachunk.mMagnitude;
+
+ mTargetOffset = followcamdatachunk.mTargetOffset;
+
+ mName = followcamdatachunk.mName;
+
+ return *this;
+}
+
+//=============================================================================
+// ::operator==
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const FollowCamDataChunk& followcamdatachunk )
+//
+// Return: bool
+//
+//=============================================================================
+inline bool FollowCamDataChunk::operator==( const FollowCamDataChunk& followcamdatachunk )
+{
+ return ( mID == followcamdatachunk.mID &&
+ mRotation == followcamdatachunk.mRotation &&
+ mElevation == followcamdatachunk.mElevation &&
+ mMagnitude == followcamdatachunk.mMagnitude &&
+ mTargetOffset == followcamdatachunk.mTargetOffset &&
+ mName == followcamdatachunk.mName );
+}
+
+#endif //FOLLOWCAMDATACHUNK_H
diff --git a/game/code/camera/frustrumdrawable.h b/game/code/camera/frustrumdrawable.h
new file mode 100644
index 0000000..bf90064
--- /dev/null
+++ b/game/code/camera/frustrumdrawable.h
@@ -0,0 +1,175 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: frustrumdrawable.h
+//
+// Description: Blahblahblah
+//
+// History: 01/05/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef FRUSTRUMDRAWABLE_H
+#define FRUSTRUMDRAWABLE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+#include <p3d/debugdraw.hpp>
+#include <p3d/drawable.hpp>
+#include <p3d/shader.hpp>
+
+#include <camera/supercam.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class FrustrumDrawable : public tDrawable
+{
+public:
+ FrustrumDrawable() : mEnable( false ), mSphereScale( 1.0f ), mSuperCam( NULL ) {};
+ virtual ~FrustrumDrawable() {};
+
+ void Display();
+
+ void SetPoints( rmt::Vector collPos,
+ rmt::Vector position,
+ rmt::Vector p0,
+ rmt::Vector p1,
+ rmt::Vector p2,
+ rmt::Vector p3 );
+
+ void SetColour( tColour colour );
+
+ void Enable() { mEnable = true; };
+ void Disable() { mEnable = false; };
+
+ void SetScale( float scale ) { mSphereScale = scale; };
+
+ void SetSuperCam( const SuperCam* cam ) { mSuperCam = cam; };
+
+private:
+ rmt::Vector mColPos;
+ rmt::Vector mPosition;
+ rmt::Vector mPoints[ 4 ];
+
+ tColour mColour;
+
+ bool mEnable : 1;
+
+ float mSphereScale;
+
+ const SuperCam* mSuperCam;
+
+ //Prevent wasteful constructor creation.
+ FrustrumDrawable( const FrustrumDrawable& frustrumdrawable );
+ FrustrumDrawable& operator=( const FrustrumDrawable& frustrumdrawable );
+};
+
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// FrustrumDrawable::Display
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline void FrustrumDrawable::Display()
+{
+ if ( mEnable )
+ {
+ pddiPrimStream* stream;
+
+ unsigned int i;
+ for ( i = 0; i < 4; ++i )
+ {
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINES, PDDI_V_C, 2);
+ stream->Colour(mColour);
+ stream->Coord(mPosition.x, mPosition.y, mPosition.z);
+ stream->Colour(mColour);
+ stream->Coord(mPoints[i].x, mPoints[i].y, mPoints[i].z);
+ p3d::pddi->EndPrims(stream);
+ }
+
+ rmt::Vector circlePos = mColPos;
+
+ tShader* shader = new tShader("simple");
+ shader->AddRef();
+
+ #ifndef RAD_RELEASE
+ P3DDrawSphere( mSphereScale, circlePos, *shader, tColour( 255, 0, 0 ) );
+ #endif
+
+ if ( mSuperCam )
+ {
+ mSuperCam->Display();
+ }
+
+ shader->Release();
+ }
+}
+
+//=============================================================================
+// FrustrumDrawable::SetPoints
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector collPos,
+// rmt::Vector position,
+// rmt::Vector p0,
+// rmt::Vector p1,
+// rmt::Vector p2,
+// rmt::Vector p3 )
+//
+// Return: void
+//
+//=============================================================================
+inline void FrustrumDrawable::SetPoints( rmt::Vector collPos,
+ rmt::Vector position,
+ rmt::Vector p0,
+ rmt::Vector p1,
+ rmt::Vector p2,
+ rmt::Vector p3 )
+{
+ mColPos = collPos;
+ mPosition = position;
+
+ mPoints[ 0 ] = p0;
+ mPoints[ 1 ] = p1;
+ mPoints[ 2 ] = p2;
+ mPoints[ 3 ] = p3;
+}
+
+//=============================================================================
+// FrustrumDrawable::SetColour
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tColour colour )
+//
+// Return: void
+//
+//=============================================================================
+inline void FrustrumDrawable::SetColour( tColour colour )
+{
+ mColour = colour;
+}
+
+#endif //FRUSTRUMDRAWABLE_H
diff --git a/game/code/camera/icamerashaker.h b/game/code/camera/icamerashaker.h
new file mode 100644
index 0000000..a8171f3
--- /dev/null
+++ b/game/code/camera/icamerashaker.h
@@ -0,0 +1,62 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: icamerashaker.h
+//
+// Description: Blahblahblah
+//
+// History: 02/05/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef ICAMERASHAKER_H
+#define ICAMERASHAKER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+class tPointCamera;
+
+struct ShakeEventData;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ICameraShaker
+{
+public:
+ ICameraShaker() {};
+ virtual ~ICameraShaker() {};
+
+ virtual void Reset() = 0;
+ virtual void ShakeCamera( rmt::Vector* pos, rmt::Vector* targ, unsigned int milliseconds ) = 0;
+ virtual void SetSpeed( float speed ) = 0; //This should be between 0 and 1.
+ virtual void SetTime( unsigned int milliseconds ) = 0; //How long does the shake go?
+ virtual void SetCamera( tPointCamera* camera ) = 0; //What is the camera for screen-relative shake
+ virtual void SetCameraRelative( bool cameraRelative ) = 0;
+ virtual void SetDirection( const rmt::Vector& dir ) = 0;
+ virtual void SetLooping( bool loop ) = 0;
+ virtual bool DoneShaking() = 0;
+
+ virtual const char* const GetName() = 0;
+
+ virtual void RegisterDebugInfo() = 0;
+ virtual void UnregisterDebugInfo() = 0;
+
+ virtual void SetShakeData( const ShakeEventData* data ) = 0;
+private:
+
+ //Prevent wasteful constructor creation.
+ ICameraShaker( const ICameraShaker& icamerashaker );
+ ICameraShaker& operator=( const ICameraShaker& icamerashaker );
+};
+
+
+#endif //ICAMERASHAKER_H
diff --git a/game/code/camera/isupercamtarget.h b/game/code/camera/isupercamtarget.h
new file mode 100644
index 0000000..f189830
--- /dev/null
+++ b/game/code/camera/isupercamtarget.h
@@ -0,0 +1,68 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: isupercamtarget.h
+//
+// Description: This is the interface to a camera target.
+//
+// History: 03/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef ISUPERCAMTARGET_H
+#define ISUPERCAMTARGET_H
+
+//========================================
+// Nested Includes
+//========================================
+#ifndef WORLD_BUILDER
+namespace RadicalMathLibrary
+{
+ class Vector;
+}
+namespace rmt = RadicalMathLibrary;
+#else
+#include <radmath/radmath.hpp>
+#endif
+
+//========================================
+// Forward References
+//========================================
+//=============================================================================
+//
+// Synopsis: Inherit this if you want to be a target of the supercam.
+//
+//=============================================================================
+
+class ISuperCamTarget
+{
+public:
+ ISuperCamTarget() {};
+ virtual ~ISuperCamTarget() {};
+
+ virtual void GetPosition( rmt::Vector* position ) = 0;
+ virtual void GetHeading( rmt::Vector* heading ) = 0;
+ virtual void GetVUP( rmt::Vector* vup ) = 0;
+ virtual void GetVelocity( rmt::Vector* velocity ) = 0;
+ virtual unsigned int GetID() = 0;
+ virtual bool IsCar() const = 0;
+ virtual bool IsAirborn() = 0;
+ virtual bool IsUnstable() = 0;
+ virtual bool IsQuickTurn() = 0;
+ virtual bool IsInReverse() = 0;
+ virtual void GetFirstPersonPosition( rmt::Vector* position ) = 0;
+ virtual void GetTerrainIntersect( rmt::Vector& pos, rmt::Vector& normal ) const = 0;
+
+ //This is only for debugging, so in the implementation go ahead and
+ //make this return NULL in release.
+ virtual const char* const GetName() = 0;
+
+private:
+
+ //Prevent wasteful constructor creation.
+ ISuperCamTarget( const ISuperCamTarget& isupercamtarget );
+ ISuperCamTarget& operator=( const ISuperCamTarget& isupercamtarget );
+};
+
+
+#endif //ISUPERCAMTARGET_H
diff --git a/game/code/camera/kullcam.cpp b/game/code/camera/kullcam.cpp
new file mode 100644
index 0000000..6704c6b
--- /dev/null
+++ b/game/code/camera/kullcam.cpp
@@ -0,0 +1,198 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: KullCam.cpp
+//
+// Description: Implement KullCam
+//
+// History: 06/05/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <radmath/radmath.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/KullCam.h>
+#include <camera/supercamconstants.h>
+#include <input/inputmanager.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+#ifndef DEBUGWATCH
+const float KULL_CAM_INCREMENT = 0.0174f; //One degree
+//const float KULL_CAM_DIST = 1.0f;
+const float KULL_CAM_DIST = 0.1f;
+#else
+float KULL_CAM_INCREMENT = 0.0174f;
+//float KULL_CAM_DIST = 1.0f;
+float KULL_CAM_DIST = 0.1f;
+#endif
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// KullCam::KullCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+KullCam::KullCam() :
+ mRotation( 0.0f ), //Along the side
+ mElevation( 1.5707f ),
+ mMagnitude( 5.0f ),
+ mIgnoreDebugController( false )
+{
+ mIm = InputManager::GetInstance();
+}
+
+//==============================================================================
+// KullCam::~KullCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+KullCam::~KullCam()
+{
+}
+
+//=============================================================================
+// KullCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void KullCam::Update( unsigned int milliseconds )
+{
+ rAssert( mTarget );
+
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ //Reset the FOV.
+ SetFOV( SUPERCAM_FOV );//DEFAULT_FOV );
+ SetFlag( (Flag)CUT, false );
+ }
+
+ //This is to adjust interpolation when we're running substeps.
+ float timeMod = 1.0f;
+
+ timeMod = (float)milliseconds / 16.0f;
+
+ if( !mIgnoreDebugController )
+ {
+ float xAxis = mIm->GetValue( s_secondaryControllerID, InputManager::LeftStickX );
+ float yAxis = mIm->GetValue( s_secondaryControllerID, InputManager::LeftStickY );
+ float zAxis = mIm->GetValue( s_secondaryControllerID, InputManager::LeftStickY );
+ float zToggle = mIm->GetValue( s_secondaryControllerID, InputManager::AnalogR1 );
+
+ if ( rmt::Fabs( zToggle ) > STICK_DEAD_ZONE && rmt::Fabs( zToggle ) <= 1.0f )
+ {
+ //zToggled
+ yAxis = 0.0f;
+ }
+ else
+ {
+ zAxis = 0.0f;
+ }
+
+
+ mRotation += ( xAxis * KULL_CAM_INCREMENT * timeMod );
+ mElevation -= ( yAxis * KULL_CAM_INCREMENT * timeMod );
+ mMagnitude -= ( zAxis * KULL_CAM_DIST * timeMod );
+ }
+
+ if ( mElevation < 0.001f )
+ {
+ mElevation = 0.001f;
+ }
+ else if ( mElevation > rmt::PI - 0.05f )
+ {
+ mElevation = rmt::PI - 0.05f;
+ }
+
+ if ( mMagnitude < 2.0f )
+ {
+ mMagnitude = 2.0f;
+ }
+
+ //This positions itself always relative to the target.
+ rmt::Vector rod;
+ rmt::SphericalToCartesian( mMagnitude, mRotation, mElevation, &rod.x, &rod.z, &rod.y );
+
+ rmt::Vector targetPos;
+ mTarget->GetPosition( &targetPos );
+
+ rmt::Vector desiredPos, desiredTarget;
+ desiredPos.Add( rod, targetPos );
+ desiredTarget = targetPos;
+
+ SetCameraValues( milliseconds, desiredPos, desiredTarget );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// KullCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void KullCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+#endif
+}
+
+//=============================================================================
+// KullCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void KullCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+#endif
+} \ No newline at end of file
diff --git a/game/code/camera/kullcam.h b/game/code/camera/kullcam.h
new file mode 100644
index 0000000..62a8b3a
--- /dev/null
+++ b/game/code/camera/kullcam.h
@@ -0,0 +1,162 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: kullcam.h
+//
+// Description: Blahblahblah
+//
+// History: 06/05/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef KULLCAM_H
+#define KULLCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+#include <camera/isupercamtarget.h>
+
+//========================================
+// Forward References
+//========================================
+class InputManager;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class KullCam : public SuperCam
+{
+public:
+ KullCam();
+ virtual ~KullCam();
+
+ //Update: Called when you want the super cam to update its state.
+ void Update( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ const char* const GetName() const;
+
+ Type GetType();
+
+ //These are for favourable support of this command
+ void SetTarget( ISuperCamTarget* target );
+ void AddTarget( ISuperCamTarget* target );
+
+ unsigned int GetNumTargets() const;
+
+protected:
+ float mRotation;
+ float mElevation;
+ float mMagnitude;
+
+ bool mIgnoreDebugController : 1;
+
+private:
+
+ ISuperCamTarget* mTarget;
+
+ InputManager* mIm;
+
+ //These functions are to allow real-time control of the settings of
+ //the supercam.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+ //Prevent wasteful constructor creation.
+ KullCam( const KullCam& kullcam );
+ KullCam& operator=( const KullCam& kullcam );
+};
+
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+//=============================================================================
+// KullCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+inline const char* const KullCam::GetName() const
+{
+ return "KULL_CAM";
+}
+
+//=============================================================================
+// DebugCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type KullCam::GetType()
+{
+ return KULL_CAM;
+}
+
+//=============================================================================
+// KullCam::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void KullCam::SetTarget( ISuperCamTarget* target )
+{
+ mTarget = target;
+}
+
+//=============================================================================
+// KullCam::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void KullCam::AddTarget( ISuperCamTarget* target )
+{
+ rAssertMsg( false, "Only call SetTarget on the KullCam" );
+}
+
+//=============================================================================
+// KullCam::GetNumTargets
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int KullCam::GetNumTargets() const
+{
+ if ( mTarget )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif //KULLCAM_H
diff --git a/game/code/camera/pccam.cpp b/game/code/camera/pccam.cpp
new file mode 100644
index 0000000..b1f68da
--- /dev/null
+++ b/game/code/camera/pccam.cpp
@@ -0,0 +1,504 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pccam.cpp
+//
+// Description: Implement PCCam
+//
+// History: 7/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radmath/radmath.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/pccam.h>
+#include <camera/isupercamtarget.h>
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercamconstants.h>
+#include <camera/supercamcontroller.h>
+
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactercontroller.h>
+#include <worldsim/character/charactermappable.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+
+#include <ai/actor/intersectionlist.h>
+#include <choreo/utility.hpp>
+
+#include <input/inputmanager.h>
+#include <input/usercontrollerwin32.h>
+#include <input/mouse.h>
+#include <input/realcontroller.h>
+
+#include <roads/geometry.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+const float DEFAULT_LENGTH = 5.0f;
+const float gMaxRodLength = DEFAULT_LENGTH;
+const float gMinRodLength = 1.0f;
+
+const rmt::Vector gOffset( 0.0f, 2.0f, 0.0f );
+
+static float gDefaultElevation = 1.57079f;
+const float gTopElevation = 3.0;
+const float gBottomElevation = 1.57079f;
+const float gElevationLag = 0.05f;
+const float gElevationIncrement = 0.1f;
+
+const float gMagAdjust = 0.0f;
+const float gMagIncrement = 0.1f;
+
+const float gMaxLookUp = 2.0f;
+
+static float gDefaultPitch = 0.0f;
+static float gPitchIncrement = 0.02f;
+static float gPitchLag = 0.05f;
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// PCCam::PCCam
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+PCCam::PCCam() :
+ mTarget( NULL ),
+ m_fAngularSpeed(0.0f),
+ m_fYawVelocity(0.0f),
+ m_fPitchVelocity(0.0f),
+ mFOVDelta( 0.0f ),
+ mLastPCCamFacing( 0 ),
+ m_fInterpolationSpeed( 0.1f )
+{
+ mGroundOffset.Set( 0.0f, 0.0f, 0.0f );
+}
+
+//=============================================================================
+// PCCam::~PCCam
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+PCCam::~PCCam()
+{
+}
+
+//=============================================================================
+// PCCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void PCCam::Update( unsigned int milliseconds )
+{
+ float timeMod = milliseconds / 16.0f;
+
+ rAssert( mTarget );
+
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ mFOVDelta = 0.0f;
+ }
+
+ // Update the angular speed modifier.
+ static float decelerationSpeed = 0.7f;
+ m_fAngularSpeed = 1.0f * timeMod;
+ m_fYawVelocity *= decelerationSpeed;
+ m_fPitchVelocity *= decelerationSpeed;
+
+ rmt::Vector targetPos;
+ mTarget->GetPosition( &targetPos );
+
+ rmt::Vector rod( 0.0f, 0.0f, 0.0f );
+
+ float elevation = gDefaultElevation;
+
+ //Calculate the length and the elevation based on the mouse values.
+ if ( mController )
+ {
+ //---- Do the Elevation calculation
+
+ float mouseUp = mController->GetValue( SuperCamController::mouseLookUp );
+ float mouseDown = mController->GetAxisValue( SuperCamController::mouseLookDown );
+ float yAxis = ( mouseUp > mouseDown ) ? -mouseUp : mouseDown;
+
+ m_fYawVelocity += yAxis;
+
+ //rDebugPrintf( " yAxis = %g xAxis = %g \n", yAxis, xAxis );
+
+ static float Y_SENSE_MOD = 0.1f;
+ float mouseSense = static_cast<Mouse*>(GetInputManager()->GetController( 0 )->GetRealController( MOUSE ))->getSensitivityY();
+
+ gDefaultElevation += gElevationIncrement * m_fYawVelocity * m_fAngularSpeed * mouseSense * Y_SENSE_MOD;
+
+ //rDebugPrintf( "Angular Velocity = %g \n", m_fAngularSpeed );
+
+ if ( gDefaultElevation > gTopElevation )
+ {
+ gDefaultElevation = gTopElevation;
+ elevation = gTopElevation;
+ }
+
+ if ( gDefaultElevation < gBottomElevation )
+ {
+ elevation = gBottomElevation;
+ }
+ }
+
+ //---- Do the Pitch calculation
+ // If target is walking towards camera, let them...
+ Character* player = GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter();
+
+ float pitch = 0.0f;
+ if ( player->mPCCamFacing == 2 )
+ {
+ if ( mLastPCCamFacing != 2 )
+ {
+ //Reset the pitch to be the direction we're already facing.
+ rmt::Vector camHeading;
+ GetHeading( &camHeading );
+ camHeading.y = 0.0f;
+ camHeading *= -1.0f;
+
+ gDefaultPitch = choreo::GetWorldAngle( camHeading.x, camHeading.z );
+ }
+
+ float mouseSenseX = static_cast<Mouse*>(GetInputManager()->GetController( 0 )->GetRealController( MOUSE ))->getSensitivityX();
+
+ gDefaultPitch += gPitchIncrement * m_fPitchVelocity * m_fAngularSpeed * mouseSenseX;
+
+ pitch = gDefaultPitch;
+ }
+
+ rmt::SphericalToCartesian( -gMaxRodLength, pitch, elevation, &rod.z, &rod.x, &rod.y );
+
+
+ ///////////////
+ // Do camera rotation as necessary
+
+ if( player->mPCCamFacing == 0 || player->mPCCamFacing == 1 )
+ {
+ static float ROT_DIR = 4.0f;
+ static float X_SENSE_MOD = 0.01f;
+ float mouseSense = static_cast<Mouse*>(GetInputManager()->GetController( 0 )->GetRealController( MOUSE ))->getSensitivityX();
+
+ float fScaleFactor = 1.0f;
+ if( player->IsTurbo() )
+ {
+ rmt::Vector playerVel;
+ player->GetVelocity( playerVel );
+ fScaleFactor *= playerVel.Magnitude();
+ }
+
+ rmt::Vector camHeading;
+ GetHeading( &camHeading );
+
+ float currAngle = choreo::GetWorldAngle( camHeading.x, camHeading.z );
+ currAngle += ROT_DIR * mouseSense * X_SENSE_MOD * GetAngularSpeed() * GetPitchVelocity();
+
+ player->SetDesiredDir( currAngle );
+
+ rmt::Vector newCamHeading;
+ newCamHeading = choreo::DEFAULT_FACING_VECTOR;
+ choreo::RotateYVector( currAngle, newCamHeading );
+
+ rmt::Matrix mat;
+ mat.Identity();
+ mat.FillHeading( newCamHeading, rmt::Vector( 0.0f, 1.0f, 0.0f ) );
+ rod.Transform( mat );
+ }
+ /*
+ rmt::Vector targetHeading;
+ mTarget->GetHeading( &targetHeading );
+ player->GetParentTransform().RotateVector( targetHeading, &targetHeading );
+
+ rmt::Vector camHeading;
+ GetHeading( &camHeading );
+
+ // Compensate targetHeading so PC_CAM doesn't rotate behind the character
+ // when they're walking towards the camera or walking to the left or
+ // walking to the right.
+ switch( player->mPCCamFacing )
+ {
+ case 1: // character facing backwards
+ targetHeading *= -1.0f;
+ case 0: // character facing in cam's direction
+ {
+ //Orient according to the target.
+ rmt::Matrix mat;
+ mat.Identity();
+ mat.FillHeading( targetHeading, rmt::Vector( 0.0f, 1.0f, 0.0f ) );
+ rod.Transform( mat );
+ }
+ break;
+ case 2: // character somewhere else
+ break;
+ }
+ */
+
+ rod.Add( gOffset );
+ rod.Add( targetPos );
+
+ rmt::Vector lookTo = targetPos;
+ lookTo.Add( gOffset );
+
+ if ( gDefaultElevation < gBottomElevation )
+ {
+ if ( gDefaultElevation < 0.0f )
+ {
+ gDefaultElevation = 0.0f;
+ }
+
+ float lookUpMod = ( rmt::PI_BY2 - gDefaultElevation ) / rmt::PI_BY2;
+ rmt::Vector lookUpOffset;
+ lookUpOffset.Set( 0.0f, gMaxLookUp * lookUpMod, 0.0f );
+ lookTo.Add( lookUpOffset );
+ }
+
+ //--------- Goofin' with the FOV
+
+ float zoom = mController->GetValue( SuperCamController::zToggle );
+
+ float FOV = GetFOV();
+
+ if ( GetFlag((Flag)CUT ) )
+ {
+ FilterFov( zoom, SUPERCAM_DEFAULT_MIN_FOV, SUPERCAM_DEFAULT_MAX_FOV, FOV, mFOVDelta, 1.0f, timeMod );
+ }
+ else
+ {
+ FilterFov( zoom, SUPERCAM_DEFAULT_MIN_FOV, SUPERCAM_DEFAULT_MAX_FOV, FOV, mFOVDelta, SUPERCAM_DEFAULT_FOV_LAG, timeMod );
+ }
+
+ SetFOV( FOV );
+
+ SetCameraValues( milliseconds, rod, lookTo );
+
+ mLastPCCamFacing = player->mPCCamFacing;
+}
+
+//=============================================================================
+// PCCam::UpdateForPhysics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void PCCam::UpdateForPhysics( unsigned int milliseconds )
+{
+ /* float nearPlane = GetNearPlane();
+ float radius = GetCollisionRadius()*2.0f;
+
+ rmt::Matrix camMat;
+ camMat.IdentityTranslation();
+
+ rmt::Vector heading;
+ GetHeading( &heading );
+ rmt::Vector vup;
+ GetCameraUp( &vup );
+ camMat.FillHeading( heading, vup ); //I do this because the tCamera may not be the same as I think it is.
+
+ //Test the sides and rotate if there's a problem.
+ rmt::Vector camPos;
+ GetPosition( &camPos );
+ rmt::Vector camTargPos;
+ mTarget->GetPosition( &camTargPos );
+ rmt::Vector lookFrom( radius, 0.0f, nearPlane );
+ lookFrom.Transform( camMat );
+ lookFrom.Add( camTargPos );
+ lookFrom.Add( gOffset );
+
+ //rmt::Vector lookTo;
+ rmt::Vector lookTo( 0.5f, 0.0f, -0.5f );
+ lookTo.Transform( camMat );
+ lookTo.Add( camPos );
+ //GetPosition( &lookTo );*/
+
+ rmt::Vector lookFrom;
+ mTarget->GetPosition( &lookFrom );
+ lookFrom.Add( gOffset );
+
+ rmt::Vector lookTo;
+ GetPosition( &lookTo );
+
+ static float NEARCLIP_OFFSET = GetNearPlane()*0.8f;
+
+ // Offset the lookFrom to compensate for the near clip plane. (Thanks for the math help Ian)
+ // lookFrom = (Normalize(LT-LF) * NEARCLIP_OFFSET) + LF
+ //rmt::Vector offsetVector = (((lookTo-lookFrom)/(lookTo-lookFrom).Magnitude()) * NEARCLIP_OFFSET)+lookFrom;
+ //rmt::Vector offsetVector = (((lookFrom-lookTo)/(lookFrom-lookTo).Magnitude()) * NEARCLIP_OFFSET)+lookTo;
+
+ rmt::Vector newPos;
+ newPos = lookTo;
+ newPos.Add( mGroundOffset );
+
+
+/*
+ if ( mNumCollisions )
+ {
+ //Deal with collisions too;
+ unsigned int i;
+ unsigned int collCount = 0;
+ rmt::Vector collisionOffset( 0.0f, 0.0f, 0.0f );
+ for ( i = 0; i < mNumCollisions; ++i )
+ {
+ if ( mCollisionOffset[ i ].y >= 0.0f )
+ {
+ collisionOffset.Add( mCollisionOffset[ i ] );
+ ++collCount;
+ }
+ }
+
+ if ( collCount )
+ {
+ collisionOffset.x /= collCount;
+ collisionOffset.y /= collCount;
+ collisionOffset.z /= collCount;
+
+ newPos.Add( collisionOffset );
+ }
+ }
+*/
+ IntersectionList& iList = GetSuperCamManager()->GetSCC( GetPlayerID() )->GetIntersectionList();
+
+ static float ZOOMIN_FACTOR = 0.5f;
+ static float ZOOMOUT_FACTOR = 0.9f;
+
+ rmt::Vector intersection( 0.0f, 0.0f, 0.0f );
+ if ( !iList.LineOfSight( lookFrom, lookTo, 0.01f, true ) )
+ {
+ if ( iList.TestIntersection( lookFrom, lookTo, &intersection, 1.0f ) )
+ {
+ // Offset the intersection to compensate for the near clip plane.
+ // F = D + [( D-S )/ (|| D-S ||)] * NEARCLIP_OFFSET
+
+ rmt::Vector offSettedDest = intersection + ((intersection-newPos)/((intersection-newPos).Magnitude())*(NEARCLIP_OFFSET));
+ //offSettedDest = lookFrom-((lookFrom-offSettedDest)/((lookFrom-offSettedDest).Magnitude())*(NEARCLIP_OFFSET*2.0f));
+
+ if( (lookFrom-offSettedDest).Magnitude() < 5.0f )
+ {
+ offSettedDest.Add( gOffset );
+ }
+
+ newPos = offSettedDest;
+
+ //newPos = intersection;
+ // Now interpolate between the current position and the intersection,
+ // to achieve a smoother translation towards the character.
+ //newPos.Interpolate( offSettedDest, m_fInterpolationSpeed );
+
+ //interpolation not used at the moment.
+ m_fInterpolationSpeed /= ZOOMIN_FACTOR;
+ if( m_fInterpolationSpeed >= 1.0f ) m_fInterpolationSpeed = 1.0f;
+ }
+ }
+ else
+ {
+ if( iList.TestIntersectionAnimPhys( lookFrom, lookTo, &intersection ) )
+ {
+ // Offset the intersection to compensate for the near clip plane.
+ // F = D + [( D-S )/ (|| D-S ||)] * NEARCLIP_OFFSET
+
+ rmt::Vector offSettedDest = intersection + ((intersection-newPos)/((intersection-newPos).Magnitude())*(NEARCLIP_OFFSET));
+
+ newPos = intersection;
+ // Now interpolate between the current position and the intersection,
+ // to achieve a smoother translation towards the character.
+ //newPos.Interpolate( offSettedDest, m_fInterpolationSpeed );
+
+ m_fInterpolationSpeed /= ZOOMIN_FACTOR;
+ if( m_fInterpolationSpeed >= 1.0f ) m_fInterpolationSpeed = 1.0f;
+ }
+ else
+ {
+ m_fInterpolationSpeed *= ZOOMOUT_FACTOR;
+ if( m_fInterpolationSpeed <= 0.1f ) m_fInterpolationSpeed = 0.1f;
+ }
+ }
+
+// rmt::Vector newToTarg;
+// newToTarg.Sub( lookFrom, newPos );
+// float mag = newToTarg.Magnitude();
+// mag -= 0.5f; //This is for the size of the character.
+// SetNearPlane( rmt::Clamp( mag, 0.1f, SUPERCAM_NEAR ) );
+
+ SetNearPlane( 0.1f );
+
+ rmt::Vector camTarg;
+ GetTarget( &camTarg );
+ SetCameraValues( 0, newPos, camTarg );
+ //rDebugPrintf( "mNumCollisions = %d \n", mNumCollisions );
+// rDebugPrintf( "m_fInterpolationSpeed = %g \n", m_fInterpolationSpeed );
+}
+
+//=============================================================================
+// PCCam::SetCollisionOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
+//
+// Return: void
+//
+//=============================================================================
+void PCCam::SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
+{
+ mCollisionOffset = offset;
+ mNumCollisions = numCollisions;
+ mGroundOffset = groundOffset;
+}
+
+//=============================================================================
+// PCCam::GetIntersectionRadius
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float PCCam::GetIntersectionRadius() const
+{
+ return gMaxRodLength;
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/camera/pccam.h b/game/code/camera/pccam.h
new file mode 100644
index 0000000..e125d74
--- /dev/null
+++ b/game/code/camera/pccam.h
@@ -0,0 +1,91 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pccam.h
+//
+// Description: Blahblahblah
+//
+// History: 7/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef PCCAM_H
+#define PCCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class PCCam : public SuperCam
+{
+public:
+ PCCam();
+ virtual ~PCCam();
+
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds );
+ virtual void UpdateForPhysics( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const { return "PC_CAM"; };
+
+ virtual Type GetType() { return SuperCam::PC_CAM; };
+
+ //These are for favourable support of this command
+ virtual void SetTarget( ISuperCamTarget* target ) { mTarget = target; };
+
+ virtual unsigned int GetNumTargets() const { return mTarget == NULL ? 0 : 1; };
+
+ virtual void SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset );
+ virtual float GetIntersectionRadius() const;
+
+ SuperCamController* GetController() const { return mController; }
+
+ void SetPitchVelocity( float speed ) { m_fPitchVelocity += speed; }
+ void SetYawVelocity( float speed ) { m_fYawVelocity += speed; }
+
+ float GetAngularSpeed() const { return m_fAngularSpeed; }
+ float GetYawVelocity() const { return m_fYawVelocity; }
+ float GetPitchVelocity() const { return m_fPitchVelocity; }
+
+
+protected:
+ virtual void OnInit() { InitMyController(); };
+
+private:
+ ISuperCamTarget* mTarget;
+ rmt::Vector mGroundOffset;
+ const rmt::Vector* mCollisionOffset;
+ unsigned int mNumCollisions;
+ float mFOVDelta;
+ float m_fAngularSpeed,
+ m_fYawVelocity,
+ m_fPitchVelocity;
+
+ float m_fInterpolationSpeed;
+ int mLastPCCamFacing;
+
+ //Prevent wasteful constructor creation.
+ PCCam( const PCCam& pccam );
+ PCCam& operator=( const PCCam& pccam );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //PCCAM_H
diff --git a/game/code/camera/railcam.cpp b/game/code/camera/railcam.cpp
new file mode 100644
index 0000000..f3e240c
--- /dev/null
+++ b/game/code/camera/railcam.cpp
@@ -0,0 +1,1260 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: RailCam.cpp
+//
+// Description: Implement RailCam
+//
+// History: 17/07/2002 + Created -- Cary Brisebois (Borrowed and adapted from J-L Duprat)
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+#ifndef WORLD_BUILDER
+#include <camera/RailCam.h>
+#include <camera/isupercamtarget.h>
+#include <camera/supercamcontroller.h>
+#include <camera/supercamconstants.h>
+#include <debug/debuginfo.h>
+#include <memory/classsizetracker.h>
+#include <p3d/pointcamera.hpp>
+//TODO: I only really need the tuner variables... Break this file up.
+#include <worldsim/character/character.h>
+#else
+#include "RailCam.h"
+#include "isupercamtarget.h"
+#include "supercamcontroller.h"
+#include "supercamconstants.h"
+#include "../../../tools/globalcode/utility/GLExt.h"
+#include <maya/mpoint.h>
+class tPointCamera
+{
+public:
+ void GetFOV( float* fov, float* aspect ) { *fov = 1.5707f; *aspect = 4.0f / 3.0f; };
+};
+
+namespace CharacterTune
+{
+ static float sfMaxSpeed;
+ static float sfDashBurstMax;
+};
+#endif
+
+#ifdef DEBUGWATCH
+float RAIL_CAM_MIN_FOV = SUPERCAM_DEFAULT_MIN_FOV;
+#else
+const float RAIL_CAM_MIN_FOV = SUPERCAM_DEFAULT_MIN_FOV;
+#endif
+
+const float RAIL_CAM_FOV_LAG = SUPERCAM_DEFAULT_FOV_LAG;
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+const float DEFAULT_MIN_RADIUS = 6.0f;
+const float DEFAULT_MAX_RADIUS = 12.0f;
+
+const char* const RailCam::BehaviourNames[] = { "Distance", "Projection" };
+
+#ifdef DEBUGWATCH
+float MAX_STEP = 0.25f;
+#else
+const float MAX_STEP = 0.25f;
+#endif
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// RailCam::RailCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RailCam::RailCam() :
+ mTarget( NULL ),
+ mBehaviour( PROJECTION ),
+ mMinRadius( DEFAULT_MIN_RADIUS ),
+ mMaxRadius( DEFAULT_MAX_RADIUS ),
+ mTrackDist( 0.0f ),
+ mStartU( -1.0f ),
+ mU( 0.0f ),
+ mStep( MAX_STEP ),
+ mPositionLag( 0.04f ),
+ mTargetLag( 0.04f ),
+ mFOVDelta( 0.0f ),
+ mMaxFOV( 0.0f ),
+ mFOVLag( RAIL_CAM_FOV_LAG ),
+ mTrackRail( false ),
+ mReverseSensing( false ),
+ mDrawRail( false ),
+ mDrawHull( false ),
+ mDrawCylinder( false ),
+ mDrawIntersections( false ),
+ mAllowUpdate( true ),
+ mReset( false ),
+ mResetting( false )
+{
+ CLASSTRACKER_CREATE( RailCam );
+ mQ.SetBasis( rmt::Spline::BSpline );
+ mQd.SetBasis( rmt::Spline::DBSpline );
+
+ mTargetOffset.Set( 0.0f, 0.0f, 0.0f );
+
+ mPosition.Set( 0.0f, 0.0f, 0.0f );
+ mPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+
+ mTargetPosition.Set( 0.0f, 0.0f, 0.0f );
+ mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+}
+
+//==============================================================================
+// RailCam::~RailCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RailCam::~RailCam()
+{
+ CLASSTRACKER_DESTROY( RailCam );
+}
+
+//=============================================================================
+// RailCam::OnInit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RailCam::OnInit()
+{
+ //Override the Near plane...
+ SetNearPlane( mMinRadius );
+ InitMyController();
+
+ if ( !mReset )
+ {
+ //This is to make the cameras move to a better place before interpolation.
+ DenyUpdate();
+ DoFirstTime();
+ DoCameraCut();
+ Update( 16 );
+ AllowUpdate();
+ }
+ else
+ {
+ mResetting = true;
+ }
+}
+
+//=============================================================================
+// RailCam::OnShutdown
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RailCam::OnShutdown()
+{
+ //Reset me to the origin of the spline.
+ mU = 0.0f;
+}
+
+//=============================================================================
+// RailCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void RailCam::Update( unsigned int milliseconds )
+{
+ float timeMod = milliseconds / 16.0f;
+
+ rmt::Vector targetPos;
+ GetTargetPosition( &targetPos, true );
+
+// rmt::Vector camPos;
+// GetPosition( &camPos );
+
+ rmt::Vector desiredPos;
+
+ if ( mResetting )
+ {
+ mU = 0.0f;
+ mResetting = false;
+ }
+
+ // compute camera position
+ switch(mBehaviour)
+ {
+ case DISTANCE:
+ default:
+ desiredPos = FindCameraPosition_Distance(targetPos, mMaxRadius);
+ break;
+ case PROJECTION:
+ desiredPos = FindCameraPosition_Projection(targetPos, mMaxRadius);
+ break;
+ }
+
+ rmt::Vector desiredTarget;
+ desiredTarget = FindCameraLookAt( targetPos, desiredPos );
+
+ bool cut, firstTime;
+ cut = GetFlag( (Flag)CUT );
+ firstTime = GetFlag( (Flag)FIRST_TIME );
+
+ if ( cut || firstTime )
+ {
+ //Reset the smoothing stuff.
+ mPosition = desiredPos;
+ mPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+
+ mTargetPosition = desiredTarget;
+ mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+
+ //SetFOV( mMaxFOV );
+ mFOVDelta = 0.0f;
+
+ if ( cut )
+ {
+ if ( GetFlag( (Flag)START_TRANSITION ) )
+ {
+ SetFlag( (Flag)START_TRANSITION, false );
+ }
+ else if ( GetFlag( (Flag)TRANSITION ) )
+ {
+ SetFlag( (Flag)END_TRANSITION, true );
+ }
+
+ SetFlag( (Flag)CUT, false );
+ }
+ else
+ {
+ //Not cutting, so let's try this.
+ float fov, aspect = 0.0f;
+ GetCamera()->GetFOV( &fov, &aspect );
+ if ( mAllowUpdate )
+ {
+ SetFOV( fov );
+ }
+ }
+
+ SetFlag( (Flag)FIRST_TIME, false );
+ }
+ else
+ {
+ //Let's smooth out the motion...
+ float posLag = mPositionLag * timeMod;
+ CLAMP_TO_ONE(posLag);
+ MotionCubic( &mPosition.x, &mPositionDelta.x, desiredPos.x, posLag );
+ MotionCubic( &mPosition.y, &mPositionDelta.y, desiredPos.y, posLag );
+ MotionCubic( &mPosition.z, &mPositionDelta.z, desiredPos.z, posLag );
+
+ float targLag = mTargetLag * timeMod;
+ CLAMP_TO_ONE(targLag);
+ MotionCubic( &mTargetPosition.x, &mTargetPositionDelta.x, desiredTarget.x, targLag );
+ MotionCubic( &mTargetPosition.y, &mTargetPositionDelta.y, desiredTarget.y, targLag );
+ MotionCubic( &mTargetPosition.z, &mTargetPositionDelta.z, desiredTarget.z, targLag );
+ }
+
+ //--------- Goofin' with the FOV
+
+ if ( GetFlag( (Flag)TRANSITION ) || mTarget->IsCar() )
+ {
+ if ( mAllowUpdate )
+ {
+ SetFOV( mMaxFOV );
+ }
+ mFOVDelta = 0.0f; //reset
+ }
+ else
+ {
+#ifndef WORLD_BUILDER
+ float zoom = mController->GetValue( SuperCamController::zToggle );
+#else
+ float zoom = 0.0f;
+#endif
+ float FOV = GetFOV();
+ FilterFov( zoom, RAIL_CAM_MIN_FOV, mMaxFOV, FOV, mFOVDelta, mFOVLag, timeMod );
+
+ if ( mAllowUpdate )
+ {
+ SetFOV( FOV );
+ }
+ }
+
+ //--------- Set values.
+
+ if ( mAllowUpdate )
+ {
+ SetCameraValues( milliseconds, mPosition, mTargetPosition );
+ }
+
+ DrawRail( true );
+ DrawHull( true );
+ DrawCylinder( targetPos );
+}
+
+//=============================================================================
+// RailCam::InitController
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RailCam::InitController()
+{
+}
+
+//=============================================================================
+// RailCam::LoadSettings
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned char* settings )
+//
+// Return: void
+//
+//=============================================================================
+void RailCam::LoadSettings( unsigned char* settings )
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// RailCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RailCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\Rail", GetPlayerID() );
+
+ radDbgWatchAddUnsignedInt( ((unsigned int*)(&mBehaviour)), "Behaviour", nameSpace, NULL, NULL, RailCam::DISTANCE, RailCam::PROJECTION );
+ radDbgWatchAddFloat( &mMinRadius, "Min. Radius", nameSpace, NULL, NULL, 0.0f, 12.0f );
+ radDbgWatchAddFloat( &mMaxRadius, "Max. Radius", nameSpace, NULL, NULL, 0.0f, 12.0f );
+ radDbgWatchAddBoolean( &mTrackRail, "Track Rail", nameSpace, NULL, NULL );
+ radDbgWatchAddFloat( &mTrackDist, "Track Distance", nameSpace, NULL, NULL, -10.0f, 10.0f );
+ radDbgWatchAddBoolean( &mReverseSensing, "Reverse Sense", nameSpace, NULL, NULL );
+ radDbgWatchAddVector( &mTargetOffset.x, "Target Offset", nameSpace, NULL, NULL, -5.0f, 5.0f );
+ radDbgWatchAddVector( &mAxisPlay.x, "Axis Play", nameSpace, NULL, NULL, 0.0f, rmt::PI_BY2 );
+ radDbgWatchAddBoolean( &mDrawRail, "Draw Rails", nameSpace, NULL, NULL );
+ radDbgWatchAddBoolean( &mDrawHull, "Draw Hulls", nameSpace, NULL, NULL );
+ radDbgWatchAddBoolean( &mDrawCylinder, "Draw Cylinder", nameSpace, NULL, NULL );
+ radDbgWatchAddBoolean( &mDrawIntersections, "Draw Intersections", nameSpace, NULL, NULL );
+ radDbgWatchAddFloat( &MAX_STEP, "Max Step", nameSpace, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &mPositionLag, "Position Lag Factor", nameSpace, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &mTargetLag, "Target Lag Factor", nameSpace, NULL, NULL, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat( &mFOVLag, "FOV Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &RAIL_CAM_MIN_FOV, "FOV Min", nameSpace, NULL, NULL, 0.0f, rmt::PI_BY2 );
+
+#endif
+}
+
+//=============================================================================
+// RailCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RailCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mBehaviour );
+ radDbgWatchDelete( &mMinRadius );
+ radDbgWatchDelete( &mMaxRadius );
+ radDbgWatchDelete( &mTrackRail );
+ radDbgWatchDelete( &mTrackDist );
+ radDbgWatchDelete( &mReverseSensing );
+ radDbgWatchDelete( &mTargetOffset.x );
+ radDbgWatchDelete( &mAxisPlay.x );
+ radDbgWatchDelete( &mDrawRail );
+ radDbgWatchDelete( &mDrawHull );
+ radDbgWatchDelete( &mDrawCylinder );
+ radDbgWatchDelete( &mDrawIntersections );
+ radDbgWatchDelete( &MAX_STEP );
+ radDbgWatchDelete( &mPositionLag );
+ radDbgWatchDelete( &mTargetLag );
+ radDbgWatchDelete( &RAIL_CAM_MIN_FOV );
+ radDbgWatchDelete( &mFOVLag );
+#endif
+}
+
+//=============================================================================
+// RailCam::GetTargetSpeedModifier
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float RailCam::GetTargetSpeedModifier()
+{
+ rmt::Vector vel;
+ mTarget->GetVelocity( &vel );
+ float speed = vel.Magnitude(); //Square root!
+
+ //TODO:I wish this was a const.
+ if ( speed < CharacterTune::sfMaxSpeed || rmt::Epsilon( speed, CharacterTune::sfMaxSpeed, 0.01f ) )
+ {
+ return 1.0f;
+ }
+
+ float maxMod = CharacterTune::sfMaxSpeed + CharacterTune::sfDashBurstMax / CharacterTune::sfMaxSpeed;
+ float modifier = (speed - CharacterTune::sfMaxSpeed) / CharacterTune::sfDashBurstMax * maxMod;
+ rAssert( modifier > 0.01f );
+
+ return modifier;
+}
+
+//=============================================================================
+// RailCam::GetTargetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* position, bool withOffset )
+//
+// Return: void
+//
+//=============================================================================
+void RailCam::GetTargetPosition( rmt::Vector* position,
+ bool withOffset )
+{
+ rAssert( mTarget );
+
+ mTarget->GetPosition( position );
+}
+
+//=============================================================================
+// RailCam::IntervalClamp
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float &t )
+//
+// Return: SolutionType
+//
+//=============================================================================
+RailCam::SolutionType RailCam::IntervalClamp( float &t ) const
+{
+ if(t>1.0f)
+ {
+ t=1.0f;
+ return WORSTCASE;
+ }
+ if(t<0.0f)
+ {
+ t=0.0f;
+ return WORSTCASE;
+ }
+ return APPROX;
+}
+
+//=============================================================================
+// RailCam::ProjectPointOnLine
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (const rmt::Vector& A, const rmt::Vector& B, const rmt::Vector& O, float& t)
+//
+// Return: float
+//
+//=============================================================================
+float& RailCam::ProjectPointOnLine(const rmt::Vector& A, const rmt::Vector& B, const rmt::Vector& O, float& t) const
+{
+ // Projects the point O on the line that goes through A and B
+ // Caller needs to clamp the parameter t such that it always lies between A and B
+
+ if( A == B )
+ t = 0.0f; // not really a segment...
+ else
+ t = -((B.x-A.x)*(A.x-O.x) + (B.y-A.y)*(A.y-O.y) + (B.z-A.z)*(A.z-O.z))/((B.x-A.x)*(B.x-A.x) + (B.y-A.y)*(B.y-A.y) + (B.z-A.z)*(B.z-A.z));
+ return t;
+}
+
+//=============================================================================
+// RailCam::IntersectLineCylinder
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (const int segment, const rmt::Vector& origin, const float radius, const rmt::Vector& neighbour, float& t)
+//
+// Return: RailCam
+//
+//=============================================================================
+RailCam::SolutionType RailCam::IntersectLineCylinder(const int segment, const rmt::Vector& origin, const float radius, const rmt::Vector& neighbour, float& t)
+{
+ // Intersects the line segment defined by the knots at segment and segment+1 with a
+ // infinitely long cylinder centered on origin and of specified radius. If there are
+ // multiple solutions (potentially an infinity), pick the one closest to neighbour.
+ // Returns true if there is a solution and t is set to the parametric position of intersection
+ // along the line segment... If there is no solution, return false and t is set to the closest
+ // point to the cylinder along the line segment
+
+ // To simplify the math, we consider that the spline segments are straight lines between the knots
+ // we return the t at which the intersection occured on the line, but camera will be moved to that
+ // parametric position along the curve.
+
+ // vertical axis of cylinder is Y
+
+ rmt::Vector A = mQ.Evaluate(float(segment));
+ rmt::Vector B = mQ.Evaluate(float(segment+1));
+
+ if(rmt::Epsilon(B.x, A.x) && rmt::Epsilon(B.z, A.z))
+ {
+ // curve segment is parallel to cylinder (zero or infinity of intersections)
+ // project origin on segment instead
+ ProjectPointOnLine(A, B, origin, t);
+
+ rmt::Vector L(A.x, origin.y, A.z);
+ if(rmt::Epsilon(L.Magnitude(), radius))
+ return EXACT; // edge is along the cylinder
+ else
+ {
+ return IntervalClamp(t); // edge is inside the cylinder
+ }
+ }
+
+
+ // There are 0, 1 or 2 intersections along the segment
+
+ // Replace the parametrized line equation into the equation for the cylinder
+ // and solve for the parameter t. The algebra is ugly, but here goes: We are
+ // solving a quadratic and use the expected names for variables...
+
+ float a = (A.x-B.x)*(A.x-B.x)+(A.z-B.z)*(A.z-B.z);
+ float b = 2.0f* (- A.x*A.x - A.z*A.z + A.x*B.x + A.z*B.z + A.x*origin.x - B.x*origin.x + A.z*origin.z - B.z*origin.z);
+ float c = (A.x-origin.x)*(A.x-origin.x)+(A.z-origin.z)*(A.z-origin.z)-radius*radius;
+
+ float b2m4ac = b*b-4.0f*a*c;
+ if(b2m4ac>0.0f)
+ {
+ float t1 = (-b+rmt::Sqrt(b2m4ac))/(2.0f*a);
+ float t2 = (-b-rmt::Sqrt(b2m4ac))/(2.0f*a);
+
+ if((t1<0.0f || t1>1.0f) && (t2<0.0f || t2>1.0f))
+ {
+ // both intersections are outside of the segment, project origin on segment
+ return IntervalClamp(ProjectPointOnLine(A, B, origin, t));
+ }
+ if(t1<0.0f || t1>1.0f)
+ {
+ // only t2 is on the segment
+ t = t2;
+ return EXACT;
+ }
+ if(t2<0.0f || t2>1.0f)
+ {
+ // only t1 is on the segment
+ t = t1;
+ return EXACT;
+ }
+
+ // we have two intersections on the segment, pick the one closest to neighbour
+ rmt::Vector X1;
+ X1.Interpolate(A, B, t1);
+ rmt::Vector L;
+ L.Sub(neighbour, X1);
+ float l1 = L.MagnitudeSqr();
+ rmt::Vector X2;
+ X2.Interpolate(A, B, t2);
+ L.Sub(neighbour, X2);
+ float l2 = L.MagnitudeSqr();
+
+ if(l1<l2)
+ t = t1; // X1 is closer
+ else
+ t = t2; // X2 is closer
+ return EXACT;
+ }
+ else
+ {
+ // no intersection, project origin on segment
+ return IntervalClamp(ProjectPointOnLine(A, B, origin, t));
+ }
+}
+
+//=============================================================================
+// RailCam::FindCameraPosition_Distance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (const rmt::Vector& target, const float radius)
+//
+// Return: rmt
+//
+//=============================================================================
+rmt::Vector RailCam::FindCameraPosition_Distance(const rmt::Vector& target, const float radius)
+{
+ // finds the position of camera along rail where the camera is at distance radius
+ // from origin (target) and closest to prevRailPos. Each segment provides a candidate and the closest
+ // one to previous camera position is used...
+ if(mQ.GetNumSegments()==0)
+ return rmt::Vector(0.0f, 0.0f, 0.0f);
+
+ unsigned int i;
+ for(i=0; i<3; i++)
+ mCandidates[i].Reset();
+
+ if(mReverseSensing)
+ {
+ mU=mQ.GetEndParam()-mU; // we need to know where the camera really is for the evaluation
+ }
+
+ rmt::Vector prevRailPos;
+// if( GetFlag( (Flag)CUT ) || GetFlag( (Flag)FIRST_TIME ) )
+// {
+// // when initializing camera minimize distance to actor
+// prevRailPos = target;
+// }
+// else
+// {
+// // when not initializing camera we want to minize movement between frames
+ prevRailPos = mQ.Evaluate(mU); // previous camera position on the rail
+// }
+
+ for(i=0; i<mQ.GetNumSegments(); i++)
+ {
+ float segT = 0.0f;
+
+ // each segment will return the best it can do (best solution type would be APPROX in this case)
+ SolutionType index = IntersectLineCylinder(i, target, radius, prevRailPos, segT);
+ rmt::Vector p=mQ.Evaluate(i+segT);
+ rmt::Vector delta(p.x-prevRailPos.x, p.y-prevRailPos.y, p.z-prevRailPos.z);
+ float len = delta.MagnitudeSqr();
+
+#ifdef DEBUGINFO_ENABLED
+ if( mDrawIntersections )
+ {
+ // mark the actual intersection of the cylinder and the spline curve
+ tColour col;
+ if(EXACT==index)
+ col.Set(255,255,255); // this should not happen in corridors
+ else if(APPROX==index)
+ col.Set(255,255,0);
+ else
+ col.Set(255,0,0);
+
+ if ( DEBUGINFO_PUSH_SECTION( "Rail Cam" ) )
+ {
+ DEBUGINFO_ADDSTAR( p, col, 0.5f );
+ }
+
+ DEBUGINFO_POP_SECTION();
+ }
+#endif
+
+ if(len<mCandidates[index].dist)
+ {
+ mCandidates[index].dist = len;
+ mCandidates[index].segment = i;
+ mCandidates[index].u = segT;
+ mCandidates[index].pu = p;
+ mCandidates[index].pDist = rmt::Abs(segT+i-mU);
+ if(mQ.GetClosed() && mCandidates[index].pDist>mQ.GetEndParam()/2.0f)
+ mCandidates[index].pDist = rmt::Abs(mCandidates[index].pDist-mQ.GetEndParam()); // its shorter going the other way!
+ }
+ }
+
+ rmt::Vector railPosition;
+
+ // Decide whether to use the exact or approx solution to minimize camera travel
+ if(mCandidates[EXACT].segment != -1 && mCandidates[EXACT].pDist>mStep)
+ {
+ // closest intersection point is quite far from current parametric position and would cause camera to jump.
+
+ // if approximate solution is closer use that instead...
+ if(mCandidates[APPROX].pDist<=mStep)
+ {
+ railPosition = FinalizeRailPosition(APPROX);
+ }
+ else
+ {
+ // Pick the closest (parameter space) point and move towards it.
+ SolutionType index = (mCandidates[EXACT].pDist<=mCandidates[APPROX].pDist)?EXACT:APPROX;
+ railPosition = FinalizeRailPosition(index);
+ // we never considered WorstCase solutions
+ }
+ }
+ else if(mCandidates[EXACT].segment != -1)
+ {
+ // use the proper intersection, its close enough
+ railPosition = FinalizeRailPosition(EXACT);
+ }
+ else if(mCandidates[APPROX].segment != -1)
+ {
+ // use the closest approximation
+ railPosition = FinalizeRailPosition(APPROX);
+ }
+ else
+ railPosition = FinalizeRailPosition(WORSTCASE);
+
+ return railPosition;
+}
+
+//=============================================================================
+// RailCam::FindCameraPosition_Projection
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (const rmt::Vector& target, const float pOffset)
+//
+// Return: rmt
+//
+//=============================================================================
+rmt::Vector RailCam::FindCameraPosition_Projection(const rmt::Vector& target, const float pOffset)
+{
+ // finds the position of camera along rail where the camera is at fixed parametric distance from
+ // from projection of target on the rail. Each segment provides a candidate projection and the closest
+ // one to previous camera position is used...
+
+ if(mQ.GetNumSegments()==0)
+ return rmt::Vector(0.0f, 0.0f, 0.0f);
+
+ unsigned int i;
+ for(i=0; i<3; i++)
+ mCandidates[i].Reset();
+
+ if( mReverseSensing )
+ mU=mQ.GetEndParam()-mU; // we need to know where the camera really is for the evaluation
+
+ rmt::Vector prevRailPos;
+ if( GetFlag( (Flag)CUT ) || GetFlag( (Flag)FIRST_TIME ) )
+ {
+ // when initializing camera minimize distance to actor
+ prevRailPos = target;
+ }
+ else
+ {
+ // when not initializing camera we want to minize movement between frames
+ prevRailPos = mQ.Evaluate(mU); // previous camera position on the rail
+ }
+
+ for(i=0; i<mQ.GetNumSegments(); i++)
+ {
+ float segT = 0.0f;
+
+ // each segment will return the best it can do
+
+ SolutionType index = IntervalClamp(ProjectPointOnLine(mQ.GetKnot(i), mQ.GetKnot(i+1), target, segT));
+ float curveT = i + segT + pOffset;
+ if(curveT < 0.0f)
+ {
+ if(mQ.GetClosed())
+ while(curveT<0.0f) curveT += mQ.GetEndParam();
+ else
+ curveT = 0.0f;
+ }
+ if(curveT > mQ.GetEndParam())
+ {
+ if(mQ.GetClosed())
+ while(curveT>mQ.GetEndParam()) curveT -= mQ.GetEndParam();
+ else
+ curveT = 0.0f;
+ }
+
+ rmt::Vector p=mQ.Evaluate(curveT);
+ //rmt::Vector delta(p.x-prevRailPos.x, p.y-prevRailPos.y, p.z-prevRailPos.z);
+ rmt::Vector delta( p.x-target.x, p.y-target.y, p.z-target.z);
+ float len = delta.MagnitudeSqr();
+
+#ifdef DEBUGINFO_ENABLED
+ if( mDrawIntersections )
+ {
+ // mark the actual intersection of the cylinder and the spline curve
+ tColour col;
+ if(EXACT==index)
+ col.Set(255,255,255); // this should not happen in corridors
+ else if(APPROX==index)
+ col.Set(255,255,0);
+ else
+ col.Set(255,0,0);
+
+ if ( DEBUGINFO_PUSH_SECTION( "Rail Cam" ) )
+ {
+ DEBUGINFO_ADDSTAR( mQ.Evaluate(i+segT), col, 0.5f );
+ }
+
+ DEBUGINFO_POP_SECTION();
+ }
+#endif
+
+ if(len<mCandidates[index].dist)
+ {
+ mCandidates[index].dist = len;
+ mCandidates[index].segment = int(rmt::Floor(curveT));
+ mCandidates[index].u = curveT-rmt::Floor(curveT);
+ mCandidates[index].pu = p;
+ mCandidates[index].pDist = rmt::Abs(curveT-mU);
+ if(mQ.GetClosed() && mCandidates[index].pDist>mQ.GetEndParam()/2.0f)
+ mCandidates[index].pDist = rmt::Abs(mCandidates[index].pDist-mQ.GetEndParam()); // its shorter going the other way!
+ }
+ }
+
+ // TODO: this could be bad because if the second intersection is a better choice than
+ // the best approximation it will be ignored...
+
+ rmt::Vector railPosition;
+
+ // Decide whether to use the exact or approx solution to minimize camera travel
+ if(mCandidates[EXACT].segment != -1 && mCandidates[EXACT].pDist>mStep)
+ {
+ // closest intersection point is quite far from current parametric position and would cause camera to jump.
+
+ // if approximate solution is closer use that instead...
+ if(mCandidates[APPROX].pDist<=mStep)
+ {
+ railPosition = FinalizeRailPosition(APPROX);
+ }
+ else
+ {
+ // Pick the closest (parameter space) point and move towards it.
+ SolutionType index = (mCandidates[EXACT].pDist<=mCandidates[APPROX].pDist)?EXACT:APPROX;
+ railPosition = FinalizeRailPosition(index);
+ // we never considered WorstCase solutions
+ }
+ }
+ else if(mCandidates[EXACT].segment != -1)
+ {
+ // use the proper intersection, its close enough
+ railPosition = FinalizeRailPosition(EXACT);
+ }
+ else if(mCandidates[APPROX].segment != -1)
+ {
+ // use the closest approximation
+ railPosition = FinalizeRailPosition(APPROX);
+ }
+ else
+ railPosition = FinalizeRailPosition(WORSTCASE);
+
+ return railPosition;
+
+}
+
+//=============================================================================
+// RailCam::FinalizeRailPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (SolutionType index)
+//
+// Return: rmt
+//
+//=============================================================================
+rmt::Vector RailCam::FinalizeRailPosition(SolutionType index)
+{
+ rAssert(mCandidates[index].segment!=-1);
+
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ // when we are cutting to a camera we can jump straight to the parametric position selected
+ mU = mCandidates[index].u+mCandidates[index].segment;
+
+ if(mReverseSensing)
+ {
+ mU=mQ.GetEndParam()-mU; // reverse camera if needed
+ return mQ.Evaluate(mU);
+ }
+ else
+ {
+ return mCandidates[index].pu;
+ }
+ }
+
+ //if(mCandidates[index].pDist>0.5f)
+ //{
+ // if(mReverseSensing)
+ // mU=mQ.GetEndParam()-mU; // reverse camera if needed
+ // return mQ.Evaluate(mU); // target is too far along rail, just stay put.
+ //}
+
+ // TODO: take care of acceleration for start/stop on the rail
+
+ if(mCandidates[index].pDist<=mStep)
+ {
+ // target is within step size, snap to it
+ mU = mCandidates[index].u+mCandidates[index].segment;
+ if(mReverseSensing)
+ {
+ mU=mQ.GetEndParam()-mU; // reverse camera if needed
+ return mQ.Evaluate(mU);
+ }
+ else
+ {
+ return mCandidates[index].pu;
+ }
+
+ return mCandidates[index].pu;
+ }
+
+ // target is too far, walk towards it
+ if(mCandidates[index].u+mCandidates[index].segment>mU)
+ mU+=mStep;
+ else
+ mU-=mStep;
+
+ if(mReverseSensing)
+ mU=mQ.GetEndParam()-mU; // reverse camera if needed
+
+ return mQ.Evaluate(mU);
+}
+
+//=============================================================================
+// RailCam::FindCameraLookAt
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (const rmt::Vector& target, const rmt::Vector& desiredPos)
+//
+// Return: rmt
+//
+//=============================================================================
+rmt::Vector RailCam::FindCameraLookAt(const rmt::Vector& target, const rmt::Vector& desiredPos)
+{
+ rmt::Vector lookAt(target);
+
+ if(mTrackRail && rmt::Abs(mTrackDist)>0.001f)
+ {
+ float trackU = mU + mTrackDist;
+
+ lookAt = TestEval( trackU );
+ }
+
+
+/*
+#ifdef DEBUGINFO_ENABLED
+ if(mTrackRail && mDrawRail)
+ {
+ if ( DEBUGINFO_PUSH_SECTION( "Rail Cam" ) )
+ {
+ DEBUGINFO_ADDLINE(mQ.Evaluate(trackU), lookAt, tColour(0,0,255));
+ DEBUGINFO_ADDSTAR(lookAt, tColour(0,0,255), 0.5f);
+ }
+
+ DEBUGINFO_POP_SECTION();
+ }
+#endif
+*/
+
+ //Take the offset and apply it to the look at.
+ //If the trackDist is 0, then do a little track dist anyway and use it to calculate the heading.
+ if ( mTargetOffset.x != 0 ||
+ mTargetOffset.y != 0 ||
+ mTargetOffset.z != 0 )
+ {
+ rmt::Vector offset;
+
+ //Make a transformation that puts the offset into the rotation space of
+ //the camera target.
+
+ offset = lookAt - desiredPos;
+ rmt::Vector vup = UpdateVUP( desiredPos, lookAt );
+
+ rmt::Matrix mat;
+ mat.Identity();
+ mat.FillHeading( offset, vup );
+
+ //reuse offset.
+ offset = mTargetOffset;
+
+ offset.Transform( mat );
+
+ lookAt.Add( offset );
+
+ CorrectDist( desiredPos, lookAt );
+ }
+
+ return lookAt;
+}
+
+//=============================================================================
+// RailCam::TestEval
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float u )
+//
+// Return: rmt
+//
+//=============================================================================
+rmt::Vector RailCam::TestEval( float u )
+{
+ rmt::Vector lookAt;
+
+ if(u > mQ.GetEndParam())
+ {
+ // past end of curve
+ if(mQ.GetClosed())
+ {
+ u -= mQ.GetEndParam(); // cycle on closed curve
+ lookAt = mQ.Evaluate(u);
+ }
+ else
+ {
+ // Interpolate along curve tangent at endpoint
+ float mult = rmt::Floor(u/mQ.GetEndParam());
+ u -= mult*mQ.GetEndParam(); // distance beyond end of curve
+
+ // if curve has triple end-knots, derivative there is zero so move in...
+ float evalAt=mQ.GetEndParam();
+ int index = mQ.GetNumVertices();
+ if(mQ.GetCntrlVertex(index-1) == mQ.GetCntrlVertex(index-2) &&
+ mQ.GetCntrlVertex(index-2) == mQ.GetCntrlVertex(index-3))
+ evalAt -= 1.0f;
+
+ rmt::Vector p = mQ.Evaluate(mQ.GetEndParam()); // point at the end of the spline
+ rmt::Vector t = mQd.Evaluate(evalAt); // tangent at the end of the spline
+ t.Scale(u);
+ lookAt.Add(p, t);
+ }
+ }
+ else if(u < 0.0f)
+ {
+ // before begining of curve
+ if(mQ.GetClosed())
+ {
+ u += mQ.GetEndParam(); // cycle on closed curve
+ lookAt = mQ.Evaluate(u);
+ }
+ else
+ {
+ // Interpolate along curve tangent at endpoint
+ float mult = rmt::Floor(-u/mQ.GetEndParam());
+ u += mult*mQ.GetEndParam(); // distance beyond end of curve
+
+ // if curve has triple end-knots, derivative there is zero so move in...
+ float evalAt=0.0f;
+ if(mQ.GetCntrlVertex(0) == mQ.GetCntrlVertex(1) &&
+ mQ.GetCntrlVertex(1) == mQ.GetCntrlVertex(2))
+ evalAt = 1.0f;
+
+ rmt::Vector p = mQ.Evaluate(0.0f); // point at the end of the spline
+ rmt::Vector t = mQd.Evaluate(evalAt); // tangent at the end of the spline
+ t.Scale(u);
+ lookAt.Add(p, t);
+ }
+ }
+ else
+ {
+ // along the curve this is easy
+ lookAt = mQ.Evaluate(u);
+ }
+
+ return lookAt;
+}
+
+//=============================================================================
+// RailCam::DrawRail
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (bool active)
+//
+// Return: void
+//
+//=============================================================================
+void RailCam::DrawRail(bool active)
+{
+#ifdef DEBUGINFO_ENABLED
+ if(!mDrawRail)
+ return; // not set to draw
+
+ if ( DEBUGINFO_PUSH_SECTION( "Rail Cam" ) )
+ {
+ tColour cRail;
+ if(active)
+ cRail.Set(255,255,255);
+ else
+ cRail.Set(255,255,0);
+
+ // spline path
+ const unsigned int numSteps = 10;
+ rmt::Vector p0, p1;
+
+ p0=mQ.InitForwardDifferencing(numSteps);
+ unsigned int j;
+ for(j=0; j<numSteps*mQ.GetNumSegments(); j++)
+ {
+ p1=mQ.Forward();
+ DEBUGINFO_ADDLINE(p0, p1, cRail);
+ if(j%numSteps==0)
+ DEBUGINFO_ADDSTAR(p0, cRail, 0.5f);
+ p0=p1;
+ }
+ DEBUGINFO_ADDSTAR(p1, cRail, 0.5f);
+ }
+
+ DEBUGINFO_POP_SECTION();
+
+#endif
+}
+
+//=============================================================================
+// RailCam::DrawHull
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (bool active)
+//
+// Return: void
+//
+//=============================================================================
+void RailCam::DrawHull(bool active)
+{
+#ifdef DEBUGINFO_ENABLED
+ if(!mDrawHull)
+ return; // not set to draw
+
+ if ( DEBUGINFO_PUSH_SECTION( "Rail Cam" ) )
+ {
+ tColour cHull;
+ if(active)
+ cHull.Set(255,255,0);
+ else
+ cHull.Set(255,0,0);
+
+ int numCVs = mQ.GetNumVertices();
+ int i;
+ for(i=1; i<numCVs; i++)
+ {
+ DEBUGINFO_ADDSTAR(mQ.GetCntrlVertex(i-1), cHull, 0.5f);
+ DEBUGINFO_ADDLINE(mQ.GetCntrlVertex(i-1), mQ.GetCntrlVertex(i), cHull);
+ }
+ DEBUGINFO_ADDSTAR(mQ.GetCntrlVertex(numCVs-1), cHull, 0.5f);
+
+ if(mQ.GetClosed())
+ DEBUGINFO_ADDLINE(mQ.GetCntrlVertex(0), mQ.GetCntrlVertex(numCVs-1), cHull); // close the hull
+ }
+
+ DEBUGINFO_POP_SECTION();
+#endif
+}
+
+//=============================================================================
+// RailCam::DrawCylinder
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& origin)
+//
+// Return: void
+//
+//=============================================================================
+void RailCam::DrawCylinder( const rmt::Vector& origin)
+{
+#ifdef DEBUGINFO_ENABLED
+ if(!mDrawCylinder)
+ return; // not set to draw
+
+ if ( DEBUGINFO_PUSH_SECTION( "Rail Cam" ) )
+ {
+ tColour cCyln(255,255,0);
+
+ rmt::Vector bottomC(origin);
+ bottomC.y -= 1.2f;
+ rmt::Vector topC(origin);
+ topC.y += 2.0f;
+
+ // bottom cap
+ //g_pDebug->AddCircle(bottomC, m_minRadius, cCyln);
+ DEBUGINFO_ADDCIRCLE(bottomC, mMaxRadius, cCyln);
+ DEBUGINFO_ADDCIRCLE(bottomC, mMinRadius, cCyln);
+
+ // top cap
+ //g_pDebug->AddCircle(topC, m_minRadius, cCyln);
+ DEBUGINFO_ADDCIRCLE(topC, mMaxRadius, cCyln);
+ DEBUGINFO_ADDCIRCLE(topC, mMinRadius, cCyln);
+
+ // sides
+ int max = 8;
+ int i;
+ for(i=0; i<max; i++)
+ {
+ rmt::Vector A(bottomC.x+mMaxRadius*rmt::Cos(rmt::DegToRadian(360.0f/max*i)), bottomC.y, bottomC.z+mMaxRadius*rmt::Sin(rmt::DegToRadian(360.0f/max*i)));
+ rmt::Vector B(topC.x+mMaxRadius*rmt::Cos(rmt::DegToRadian(360.0f/max*i)), topC.y, topC.z+mMaxRadius*rmt::Sin(rmt::DegToRadian(360.0f/max*i)));
+ DEBUGINFO_ADDLINE(A, B, cCyln);
+ }
+ }
+
+ DEBUGINFO_POP_SECTION();
+#endif
+
+#ifdef WORLD_BUILDER
+ GLExt::drawSphere( mMaxRadius * 100.0f, MPoint( 0, 0, 0 ) );
+#endif
+}
+
+//=============================================================================
+// RailCam::GetWatcherName
+//=============================================================================
+// Description: the name of the class for the watcher or other debug purposes
+//
+// Parameters: NONE
+//
+// Return: const char* - the name of the class
+//
+//=============================================================================
+#ifdef DEBUGWATCH
+const char* RailCam::GetWatcherName() const
+{
+ return "RailCam";
+}
+#endif
diff --git a/game/code/camera/railcam.h b/game/code/camera/railcam.h
new file mode 100644
index 0000000..0735408
--- /dev/null
+++ b/game/code/camera/railcam.h
@@ -0,0 +1,730 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: railcam.h
+//
+// Description: Blahblahblah
+//
+// History: 17/07/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef RAILCAM_H
+#define RAILCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+#ifndef WORLD_BUILDER
+#include <camera/supercam.h>
+#else
+#include "supercam.h"
+#endif
+
+//========================================
+// Forward References
+//========================================
+class ISuperCamTarget;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class RailCam : public SuperCam
+{
+public:
+ RailCam();
+ virtual ~RailCam();
+
+ //----------- From SuperCam
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ virtual Type GetType();
+
+ virtual void InitController();
+
+ //This loads the off-line created settings for the camera.
+ //It is passed in as a byte stream of some data of known size.
+ virtual void LoadSettings( unsigned char* settings );
+
+ //These are for favourable support of this command
+ virtual void SetTarget( ISuperCamTarget* target );
+ virtual void AddTarget( ISuperCamTarget* target );
+
+ virtual unsigned int GetNumTargets() const;
+
+ //----------- RailCam specific
+ typedef enum { DISTANCE=1, PROJECTION, NUM_BEHAVIOUR } Behaviour;
+ static const char* const BehaviourNames[];
+
+ void SetBehaviour( Behaviour b );
+ void SetMaxStep( float du );
+ void SetMinRadius( float r );
+ void SetMaxRadius( float r );
+ void SetClosedRail( bool closed );
+ void SetNumCVs( unsigned int num );
+ void SetVertex( unsigned int idx, const rmt::Vector& cv );
+ void SetTrackRail( bool trackRail );
+ void SetTrackDist( float offset );
+ void SetStartPosition( float u0 );
+ void SetStartPositionCB(); //Called by the watcher.
+ void SetReverseSense( bool reverseSense );
+ void SetTargetOffset( const rmt::Vector& offset );
+ void SetAxisPlay( const rmt::Vector& play );
+ void SetPositionLag( float lag );
+ void SetTargetLag( float lag );
+
+ void SetDrawRail( bool draw );
+ void SetDrawHull( bool draw );
+ void SetDrawCylinder( bool draw );
+ void SetDrawIntersections( bool draw );
+
+ void SetSplineCurve( rmt::SplineCurve& curve );
+
+ void SetMaxFOV( float fov );
+ void SetFOVLag( float lag );
+
+ void SetReset( bool reset );
+
+ #ifdef DEBUGWATCH
+ virtual const char* GetWatcherName() const;
+ #endif
+
+protected:
+ //Init... This gets called when the camera is registered
+ virtual void OnInit();
+ virtual void OnShutdown();
+
+ //These functions are to allow real-time control of the settings of
+ //the supercam.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+ virtual float GetTargetSpeedModifier();
+
+ //----------- RailCam specific RENDERING
+ void DrawRail( bool active );
+ void DrawHull( bool active );
+ void DrawCylinder( const rmt::Vector& origin );
+
+
+private:
+
+ //----------- From SuperCam
+ void GetTargetPosition( rmt::Vector* position, bool withOffset = true );
+ void DenyUpdate();
+ void AllowUpdate();
+
+
+ ISuperCamTarget* mTarget;
+
+
+ //----------- RailCam specific
+ typedef enum { EXACT, APPROX, WORSTCASE } SolutionType;
+
+ SolutionType IntervalClamp(float &t) const;
+ float& ProjectPointOnLine(const rmt::Vector& A, const rmt::Vector& B, const rmt::Vector& O, float& t) const;
+ SolutionType IntersectLineCylinder(const int segment, const rmt::Vector& origin, const float radius, const rmt::Vector& neighbour, float& t);
+
+ rmt::Vector FindCameraPosition_Distance(const rmt::Vector& target, const float radius);
+ rmt::Vector FindCameraPosition_Projection(const rmt::Vector& target, const float pOffset);
+
+ rmt::Vector FindCameraLookAt(const rmt::Vector& target, const rmt::Vector& desiredPos );
+ rmt::Vector FinalizeRailPosition(SolutionType index);
+
+ rmt::Vector TestEval( float u );
+
+ Behaviour mBehaviour;
+ float mMinRadius;
+ float mMaxRadius;
+ float mTrackDist;
+ float mStartU;
+
+ rmt::SplineCurve mQ;
+ rmt::SplineCurve mQd; //Derivative of mQ
+
+ float mU; //Current parametric position along curve
+ float mStep; //max parametric step size along rail
+
+ struct CamPosition {
+ CamPosition() { /**/ };
+ void Reset() { segment=-1; u=0.0f; dist=9.9E9f; pDist=9.9E9f; }
+
+ int segment; // segment of the curve for this solution
+ float u; // u value along this segment [0, 1]
+ float dist; // world-space distance to current camera position
+ float pDist; // parameter space distance to current camera position
+ rmt::Vector pu; // world-space position of camera at u
+ } mCandidates[3];
+
+ rmt::Vector mTargetOffset;
+ rmt::Vector mAxisPlay;
+ float mPositionLag;
+ float mTargetLag;
+
+ rmt::Vector mPosition;
+ rmt::Vector mPositionDelta;
+
+ rmt::Vector mTargetPosition;
+ rmt::Vector mTargetPositionDelta;
+
+ float mFOVDelta;
+ float mMaxFOV;
+ float mFOVLag;
+
+#ifdef DEBUGWATCH
+ bool mTrackRail;
+ bool mReverseSensing;
+ bool mDrawRail;
+ bool mDrawHull;
+ bool mDrawCylinder;
+ bool mDrawIntersections;
+#else
+ bool mTrackRail : 1;
+ bool mReverseSensing : 1;
+ bool mDrawRail : 1;
+ bool mDrawHull : 1;
+ bool mDrawCylinder : 1;
+ bool mDrawIntersections : 1;
+#endif
+ bool mAllowUpdate : 1;
+ bool mReset : 1;
+ bool mResetting : 1;
+
+ //Prevent wasteful constructor creation.
+ RailCam( const RailCam& railcam );
+ RailCam& operator=( const RailCam& railcam );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// RailCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+inline const char* const RailCam::GetName() const
+{
+ return "RAIL_CAM";
+}
+
+//=============================================================================
+// RailCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type RailCam::GetType()
+{
+ return RAIL_CAM;
+}
+
+//=============================================================================
+// RailCam::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetTarget( ISuperCamTarget* target )
+{
+ mTarget = target;
+}
+
+//=============================================================================
+// RailCam::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::AddTarget( ISuperCamTarget* target )
+{
+ rAssertMsg( false, "Only call SetTarget on the RailCam" );
+}
+
+//=============================================================================
+// ChaseCam::GetNumTargets
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int RailCam::GetNumTargets() const
+{
+ if ( mTarget )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+//=============================================================================
+// RailCam::SetBehaviour
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Behaviour b )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetBehaviour( Behaviour b )
+{
+ mBehaviour = b;
+}
+
+//=============================================================================
+// RailCam::SetMaxStep
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float du )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetMaxStep( float du )
+{
+ mStep = du;
+}
+
+//=============================================================================
+// RailCam::SetMinRadius
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float r )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetMinRadius( float r )
+{
+ mMinRadius = r;
+}
+
+//=============================================================================
+// RailCam::SetMaxRadius
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float r )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetMaxRadius( float r )
+{
+ mMaxRadius = r;
+}
+
+//=============================================================================
+// RailCam::SetClosedRail
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool closed )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetClosedRail( bool closed )
+{
+ mQ.SetClosed( closed );
+}
+
+//=============================================================================
+// RailCam::SetNumCVs
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int num )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetNumCVs( unsigned int num )
+{
+ mQ.SetNumVertices( num );
+ mQd.SetNumVertices( num );
+
+ if ( (mStartU >= 0.0f) && (mStartU <= 1.0f) )
+ {
+ mStartU = mStartU * mQ.GetEndParam();
+ }
+ else
+ {
+ mStartU = -1.0f;
+ }
+}
+
+//=============================================================================
+// RailCam::SetVertex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int idx, const rmt::Vector& cv )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetVertex( unsigned int idx, const rmt::Vector& cv )
+{
+ mQ.SetCntrlVertex(idx, cv);
+ mQd.SetCntrlVertex(idx, cv);
+
+}
+
+//=============================================================================
+// RailCam::SetTrackRail
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool trackRail )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetTrackRail( bool trackRail )
+{
+ mTrackRail = trackRail;
+}
+
+//=============================================================================
+// RailCam::SetTrackDist
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float offset )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetTrackDist( float offset )
+{
+ mTrackDist = offset;
+}
+
+//=============================================================================
+// RailCam::SetStartPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float u0 )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetStartPosition( float u0 )
+{
+ if ( mQ.GetNumVertices() > 0 )
+ {
+ if ( (u0 >= 0.0f && u0 <= 1.0f) )
+ {
+ mStartU = u0 * mQ.GetEndParam();
+ }
+ else
+ {
+ mStartU = -1.0f;
+ }
+ }
+ else
+ {
+ // The first time this is called CVs are not yet set on the spline,
+ // so we don't know how long it'll be.
+ // SetCameraNumCVs() fixes this...
+ mStartU = u0;
+ }
+
+}
+
+//=============================================================================
+// RailCam::SetStartPositionCB
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetStartPositionCB()
+{
+ SetStartPosition(mStartU);
+}
+
+//=============================================================================
+// RailCam::SetReverseSense
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool reverseSense )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetReverseSense( bool reverseSense )
+{
+ mReverseSensing = reverseSense;
+}
+
+//=============================================================================
+// RailCam::SetTargetOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& offset )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetTargetOffset( const rmt::Vector& offset )
+{
+ mTargetOffset = offset;
+}
+
+//=============================================================================
+// RailCam::SetAxisPlay
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& play )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetAxisPlay( const rmt::Vector& play )
+{
+ mAxisPlay = play;
+}
+
+//=============================================================================
+// RailCam::SetPositionLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetPositionLag( float lag )
+{
+ mPositionLag = lag;
+}
+
+//=============================================================================
+// RailCam::SetTargetLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetTargetLag( float lag )
+{
+ mTargetLag = lag;
+}
+
+
+//=============================================================================
+// RailCam::SetDrawRail
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool draw )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetDrawRail( bool draw )
+{
+ mDrawRail = draw;
+}
+
+//=============================================================================
+// RailCam::SetDrawHull
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool draw )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetDrawHull( bool draw )
+{
+ mDrawHull = draw;
+}
+
+//=============================================================================
+// RailCam::SetDrawCylinder
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool draw )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetDrawCylinder( bool draw )
+{
+ mDrawCylinder = draw;
+}
+
+//=============================================================================
+// RailCam::SetDrawIntersections
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool draw )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetDrawIntersections( bool draw )
+{
+ mDrawIntersections = draw;
+}
+
+//=============================================================================
+// RailCam::SetSplineCurve
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::SplineCurve& curve )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetSplineCurve( rmt::SplineCurve& curve )
+{
+ mQ.SetNumVertices( curve.GetNumVertices() );
+ mQd.SetNumVertices( curve.GetNumVertices() );
+
+ mQ = curve;
+ mQd = curve;
+
+ mQd.SetBasis( rmt::Spline::DBSpline );
+
+ if ( (mStartU >= 0.0f) && (mStartU <= 1.0f) )
+ {
+ mStartU = mStartU * mQ.GetEndParam();
+ }
+ else
+ {
+ mStartU = -1.0f;
+ }
+}
+
+//=============================================================================
+// RailCam::SetMaxFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float fov )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetMaxFOV( float fov )
+{
+ mMaxFOV = fov;
+}
+
+//=============================================================================
+// RailCam::SetFOVLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetFOVLag( float lag )
+{
+ mFOVLag = lag;
+}
+
+//=============================================================================
+// RailCam::SetReset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool reset )
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::SetReset( bool reset )
+{
+ mReset = true;
+}
+
+//=============================================================================
+// RailCam::AllowUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::AllowUpdate()
+{
+ mAllowUpdate = true;
+}
+
+//=============================================================================
+// RailCam::DenyUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void RailCam::DenyUpdate()
+{
+ mAllowUpdate = false;
+}
+
+#endif //RAILCAM_H
diff --git a/game/code/camera/relativeanimatedcam.cpp b/game/code/camera/relativeanimatedcam.cpp
new file mode 100644
index 0000000..c6628d3
--- /dev/null
+++ b/game/code/camera/relativeanimatedcam.cpp
@@ -0,0 +1,204 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: RelativeAnimatedCam.cpp
+//
+// Description: Implement RelativeAnimatedCam
+//
+// History: 24/04/2002 + Created -- Ian Gipson
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <p3d/anim/cameraanimation.hpp>
+#include <camera/relativeanimatedcam.h>
+#include <camera/supercammanager.h>
+
+//========================================
+// Definitions
+//========================================
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// RelativeAnimatedCam::RelativeAnimatedCam():
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+RelativeAnimatedCam::RelativeAnimatedCam():
+ gCameraAnimationController( NULL )
+{
+ mOffsetMatrix.Identity();
+}
+
+//==============================================================================
+// RelativeAnimatedCam::CheckPendingCameraSwitch
+//==============================================================================
+// Description: checks if we've queued up a camera switch to the animated camera
+// because the camera system isn't always set up in time.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void RelativeAnimatedCam::CheckPendingCameraSwitch()
+{
+ if( CameraSwitchPending() )
+ {
+ LookupCamera();
+ LookupMulticontroller();
+ SuperCamManager* scm = GetSuperCamManager();
+ SuperCamCentral* scc = scm->GetSCC( 0 );
+ SuperCam* sc = scc->GetActiveSuperCam();
+ if( sc == NULL )
+ {
+ AnimatedCam::SupressNextLetterbox();
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( ANIMATED_CAM );
+ }
+ else
+ {
+ SuperCam::Type type = sc->GetType();
+ if( type != RELATIVE_ANIMATED_CAM )
+ {
+ AnimatedCam::SupressNextLetterbox();
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( ANIMATED_CAM );
+ }
+ }
+ SetCameraSwitchPending( false );
+ }
+}
+
+//=============================================================================
+// RelativeAnimatedCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+const char* const RelativeAnimatedCam::GetName() const
+{
+ return "RELATIVE_ANIMATED_CAM";
+}
+
+//=============================================================================
+// RelativeAnimatedCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+SuperCam::Type RelativeAnimatedCam::GetType()
+{
+ return RELATIVE_ANIMATED_CAM;
+}
+
+//=============================================================================
+// RelativeAnimatedCam::LetterBoxStart
+//=============================================================================
+// Description: start the letterbox for this camera - does nothing
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+void RelativeAnimatedCam::LetterBoxStart()
+{
+ //nothing
+}
+
+//=============================================================================
+// RelativeAnimatedCam::LetterBoxStop
+//=============================================================================
+// Description: stop the letterbox for this camera - does nothing
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+void RelativeAnimatedCam::LetterBoxStop()
+{
+ //nothing
+}
+
+//=============================================================================
+// RelativeAnimatedCam::SetOffsetMatrix
+//=============================================================================
+// Description: sets the matrix offset of the camera
+//
+// Parameters: m - the matrix offset
+//
+// Return: N/A.
+//
+//=============================================================================
+void RelativeAnimatedCam::SetCameraAnimationController( tCameraAnimationController* controller )
+{
+ gCameraAnimationController = controller;
+ if( gCameraAnimationController )
+ {
+ gCameraAnimationController->SetOffsetMatrix( mOffsetMatrix );
+ }
+}
+
+//=============================================================================
+// RelativeAnimatedCam::SetOffsetMatrix
+//=============================================================================
+// Description: sets the matrix offset of the camera
+//
+// Parameters: m - the matrix offset
+//
+// Return: N/A.
+//
+//=============================================================================
+void RelativeAnimatedCam::SetOffsetMatrix( const rmt::Matrix& m )
+{
+ mOffsetMatrix = m;
+ if( gCameraAnimationController != NULL )
+ {
+ gCameraAnimationController->SetOffsetMatrix( m );
+ }
+}
+
+//=============================================================================
+// RelativeAnimatedCam::Update
+//=============================================================================
+// Description: updates the camera.
+//
+// Parameters: milliseconds - the nubmer of miliseconds elapsed
+//
+// Return: N/A.
+//
+//=============================================================================
+void RelativeAnimatedCam::Update( unsigned int milliseconds )
+{
+ AnimatedCam::Update( milliseconds );
+} \ No newline at end of file
diff --git a/game/code/camera/relativeanimatedcam.h b/game/code/camera/relativeanimatedcam.h
new file mode 100644
index 0000000..f78c201
--- /dev/null
+++ b/game/code/camera/relativeanimatedcam.h
@@ -0,0 +1,66 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: relativeanimatedcam.h
+//
+// Description: camers used for animated camera transitions that are relative
+// to a fixed frame in the world
+//
+// History: 21/01/2003 + Created -- Ian Gipson
+//
+//=============================================================================
+
+#ifndef RELATIVEANIMATEDCAMERA_H
+#define RELATIVEANIMATEDCAMERA_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/animatedcam.h>
+#include <camera/supercam.h>
+#include <events/eventlistener.h>
+#include <input/mappable.h>
+
+//========================================
+// Forward References
+//========================================
+class ISuperCamTarget;
+class tCameraAnimationController;
+class tMultiController;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class RelativeAnimatedCam :
+ public AnimatedCam
+{
+public:
+ RelativeAnimatedCam();
+ static void CheckPendingCameraSwitch();
+ const char* const GetName() const;
+ SuperCam::Type GetType();
+ void SetCameraAnimationController( tCameraAnimationController* controller );
+ void SetOffsetMatrix( const rmt::Matrix& m );
+ void Update( unsigned int milliseconds );
+
+protected:
+ virtual void LetterBoxStart();
+ virtual void LetterBoxStop();
+
+ //Prevent wasteful constructor creation.
+ RelativeAnimatedCam( const AnimatedCam& AnimatedCam );
+ RelativeAnimatedCam& operator=( const AnimatedCam& AnimatedCam );
+ rmt::Matrix mOffsetMatrix;
+ tCameraAnimationController* gCameraAnimationController;
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //ANIMATEDCAMERA_H
diff --git a/game/code/camera/reversecam.cpp b/game/code/camera/reversecam.cpp
new file mode 100644
index 0000000..bebd64c
--- /dev/null
+++ b/game/code/camera/reversecam.cpp
@@ -0,0 +1,425 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ReverseCam.cpp
+//
+// Description: Implement ReverseCam
+//
+// History: 11/14/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/ReverseCam.h>
+#include <camera/isupercamtarget.h>
+#include <camera/SuperCamController.h>
+#include <camera/supercamconstants.h>
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+
+#include <worldsim/redbrick/vehicle.h>
+
+#include <mission/gameplaymanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+#ifdef DEBUGWATCH
+static float CREEPY_TWIST_REVERSE = 6.107f;
+#else
+static const float CREEPY_TWIST_REVERSE = 6.107f;
+#endif
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ReverseCam::ReverseCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ReverseCam::ReverseCam() :
+ mTarget( NULL ),
+ mYHeight( 0.0f ),
+ mMagnitude( 4.0f ),
+ mLag( 0.05f ),
+ mTargetLag( 0.5f ),
+ mCollisionOffset( NULL ),
+ mCollisionRadius( 2.0f ),
+ mNumCollisions( 0 ),
+ mOtherY( 0.0f )
+{
+}
+
+//==============================================================================
+// ReverseCam::~ReverseCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ReverseCam::~ReverseCam()
+{
+}
+
+
+//=============================================================================
+// ReverseCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void ReverseCam::Update( unsigned int milliseconds )
+{
+ //This is to adjust interpolation when we're running substeps.
+ float timeMod = (float)milliseconds / EXPECTED_FRAME_RATE;
+
+ if ( GetFlag( (Flag)FIRST_TIME ) )
+ {
+ GetPosition( &mPosition );
+ mPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+
+ mTarget->GetPosition( &mTargetPosition );
+ mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+
+ rmt::Vector camToTarg;
+ camToTarg.Sub( mPosition, mTargetPosition );
+ mYHeight = camToTarg.y;
+
+ mMagnitude = camToTarg.Magnitude();
+
+ rmt::Vector normal, pointInPlane;
+ GetCameraUp( &normal );
+ GetPosition( &pointInPlane );
+ float D = -(normal.DotProduct( pointInPlane ));
+ rmt::Plane collisionPlane( normal, D );
+
+ rmt::Vector intersection;
+ if ( collisionPlane.Intersect( mTargetPosition, rmt::Vector(0.0f, 1.0f, 0.0f), &intersection ) )
+ {
+ mOtherY = intersection.y - mTargetPosition.y;
+ }
+
+ mTargetPosition.y += mOtherY;
+
+ SetFlag( (Flag)FIRST_TIME, false );
+ }
+
+ rmt::Vector desiredPosition, desiredTarget;
+
+ GetPosition( &desiredPosition );
+ mTarget->GetPosition( &desiredTarget );
+
+ rmt::Vector targToCam;
+ targToCam.Sub( desiredPosition, desiredTarget );
+
+ if ( targToCam.MagnitudeSqr() > mMagnitude * mMagnitude )
+ {
+ targToCam.NormalizeSafe();
+ targToCam.Scale( mMagnitude );
+
+ desiredPosition.Add( desiredTarget, targToCam );
+ //Override the y height.
+ desiredPosition.y = desiredTarget.y + mYHeight;
+
+ mMagnitude = mMagnitude;
+ }
+ else if ( targToCam.MagnitudeSqr() < 49.0f )
+ {
+ rmt::Vector velocity;
+ mTarget->GetVelocity( &velocity );
+
+ //Do I go left or right?
+ rmt::Vector heading, vup;
+ mTarget->GetHeading( &heading );
+ mTarget->GetVUP( &vup );
+
+ if ( velocity.DotProduct( targToCam ) > 0.0f && heading.DotProduct( targToCam ) < 0.0f )
+ {
+ rmt::Vector left;
+ left.CrossProduct( heading, vup );
+ left.y = 0.0f;
+
+ left.NormalizeSafe();
+
+ if ( left.DotProduct(targToCam) < 0.0f )
+ {
+ left.Scale( -mMagnitude / 2.0f );
+ }
+ else
+ {
+ left.Scale( mMagnitude / 2.0f );
+ }
+
+ float lag = mLag * timeMod;
+ CLAMP_TO_ONE( lag );
+ left.Scale( lag );
+
+ desiredPosition.Add( left );
+ }
+ }
+
+ rmt::Vector velocity;
+ mTarget->GetVelocity( &velocity );
+ float velMod = velocity.MagnitudeSqr() / ( 20.0f * 20.0f );
+ CLAMP_TO_ONE( velMod );
+
+ float lag = (mLag + velMod) * timeMod;
+ CLAMP_TO_ONE( lag );
+ MotionCubic( &mPosition.x, &mPositionDelta.x, desiredPosition.x, lag );
+ MotionCubic( &mPosition.y, &mPositionDelta.y, desiredPosition.y, lag );
+ MotionCubic( &mPosition.z, &mPositionDelta.z, desiredPosition.z, lag );
+
+ //Add the UP offset to make the camera "stay" looking at the right point.
+ desiredTarget.y += mOtherY;
+
+ lag = mTargetLag * timeMod;
+ CLAMP_TO_ONE( lag );
+ MotionCubic( &mTargetPosition.x, &mTargetPositionDelta.x, desiredTarget.x, lag );
+ MotionCubic( &mTargetPosition.y, &mTargetPositionDelta.y, desiredTarget.y, lag );
+ MotionCubic( &mTargetPosition.z, &mTargetPositionDelta.z, desiredTarget.z, lag );
+
+/*
+ //Are we on level 7?
+ if ( GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L7 )
+ {
+ rmt::Vector velocity;
+ mTarget->GetVelocity( &velocity );
+ if ( velocity.MagnitudeSqr() > 0.1f )
+ {
+ SetTwist( 0.0f );
+ }
+ else
+ {
+ SetTwist( CREEPY_TWIST_REVERSE );
+ }
+ }
+*/
+ SetCameraValues( milliseconds, mPosition, mTargetPosition );
+}
+
+//=============================================================================
+// ReverseCam::UpdateForPhysics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void ReverseCam::UpdateForPhysics( unsigned int milliseconds )
+{
+ rmt::Vector currentPosition = mPosition;
+
+ bool collision = false;
+
+ unsigned int i;
+ for ( i = 0; i < mNumCollisions; ++i )
+ {
+ rmt::Vector camHeading;
+ GetHeadingNormalized( &camHeading );
+ if ( camHeading.DotProduct( mCollisionOffset[ i ] ) >= -0.5f )
+ {
+ currentPosition.Add( mCollisionOffset[ i ] );
+ collision = true;
+ }
+ }
+
+ //Hmmm. Offset the campos at the start by the ground plane collision offset.
+ if ( mGroundOffset.MagnitudeSqr() > 0.1f )
+ {
+ currentPosition.Add( mGroundOffset );
+ collision = true;
+ }
+
+ if ( collision )
+ {
+ mPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+ }
+
+ rmt::Vector lookFrom = mTargetPosition;
+
+ rmt::Vector lookTo = currentPosition;
+
+ IntersectionList& iList = GetSuperCamManager()->GetSCC( GetPlayerID() )->GetIntersectionList();
+
+ if ( !iList.LineOfSight( lookFrom, lookTo, 0.1f, true ) )
+ {
+ currentPosition = mTargetPosition;
+ currentPosition.Add( rmt::Vector( 0.0f, 6.0f, 0.0f ) ); //BAD!
+ mMagnitude = 6.0f;
+
+ mPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+ }
+
+
+ SetCameraValues( 0, currentPosition, mTargetPosition ); //No extra transition
+}
+
+//=============================================================================
+// ReverseCam::EnableShake
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ReverseCam::EnableShake()
+{
+ SetShaker( &mSineCosShaker );
+
+ SuperCam::EnableShake();
+}
+
+//=============================================================================
+// ReverseCam::DisableShake
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ReverseCam::DisableShake()
+{
+ SuperCam::DisableShake();
+}
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// ReverseCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ReverseCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\Reverse", GetPlayerID() );
+
+ radDbgWatchAddFloat( &mYHeight, "Y Height", nameSpace, NULL, NULL, 0.0f, 5.0f );
+ radDbgWatchAddFloat( &mMagnitude, "Magnitude", nameSpace, NULL, NULL, 1.0f, 10.0f );
+ radDbgWatchAddFloat( &mLag, "Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &mTargetLag, "Target Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat( &CREEPY_TWIST_REVERSE, "Twist", nameSpace, NULL, NULL, 0.0f, rmt::PI_2 );
+#endif
+}
+
+//=============================================================================
+// ReverseCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ReverseCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mYHeight );
+ radDbgWatchDelete( &mMagnitude );
+ radDbgWatchDelete( &mLag );
+ radDbgWatchDelete( &mTargetLag );
+
+ radDbgWatchDelete( &CREEPY_TWIST_REVERSE );
+#endif
+}
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// ReverseCam::CanSwitch
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool ReverseCam::CanSwitch()
+{
+ rAssert( mTarget );
+
+ bool canSwitch = true;
+
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ GetSuperCamManager()->GetSCC( GetPlayerID() )->SelectSuperCam( SuperCam::FOLLOW_CAM, SuperCamCentral::CUT | SuperCamCentral::FORCE | SuperCamCentral::NO_TRANS, 0 );
+ }
+ else if ( mTarget )
+ {
+ if ( mTarget->IsInReverse() )
+ {
+ canSwitch = false;
+ }
+ else if ( static_cast<Vehicle*>(mTarget)->mGas < 0.5f ) //I hate this.
+ {
+ canSwitch = false;
+ }
+// else if ( mNumCollisions > 0 )
+// {
+// canSwitch = false;
+// }
+ }
+
+ if ( canSwitch )
+ {
+ //Hmmmm.. HACK
+ SetFlag( (Flag)END_TRANSITION, true );
+ }
+
+ return canSwitch;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/camera/reversecam.h b/game/code/camera/reversecam.h
new file mode 100644
index 0000000..eb863ef
--- /dev/null
+++ b/game/code/camera/reversecam.h
@@ -0,0 +1,202 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: reversecam.h
+//
+// Description: Blahblahblah
+//
+// History: 11/14/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef REVERSECAM_H
+#define REVERSECAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+
+//========================================
+// Forward References
+//========================================
+class ISuperCamTarget;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ReverseCam : public SuperCam
+{
+public:
+ ReverseCam();
+ virtual ~ReverseCam();
+
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds );
+ virtual void UpdateForPhysics( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ //This loads the off-line created settings for the camera.
+ //It is passed in as a byte stream of some data of known size.
+ virtual void LoadSettings( unsigned char* settings ) {};
+
+ virtual Type GetType();
+
+ //These are for favourable support of this command
+ virtual void SetTarget( ISuperCamTarget* target );
+ virtual void AddTarget( ISuperCamTarget* target );
+
+ unsigned int GetNumTargets() const;
+
+ void EnableShake();
+ void DisableShake();
+
+ //Support for colliding with the world.
+ void SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset );
+ float GetCollisionRadius() const { return mCollisionRadius; };
+
+protected:
+ bool CanSwitch();
+ virtual void OnInit() { InitMyController(); };
+
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+private:
+ ISuperCamTarget* mTarget;
+
+ rmt::Vector mPosition;
+ rmt::Vector mPositionDelta;
+
+ rmt::Vector mTargetPosition;
+ rmt::Vector mTargetPositionDelta;
+
+ float mYHeight;
+ float mMagnitude;
+ float mLag;
+ float mTargetLag;
+
+ const rmt::Vector* mCollisionOffset;
+ float mCollisionRadius;
+ unsigned int mNumCollisions;
+ rmt::Vector mGroundOffset;
+
+ float mOtherY;
+
+ //Prevent wasteful constructor creation.
+ ReverseCam( const ReverseCam& reversecam );
+ ReverseCam& operator=( const ReverseCam& reversecam );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ReverseCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+
+inline const char* const ReverseCam::GetName() const
+{
+ return "REVERSE_CAM";
+}
+
+//=============================================================================
+// ReverseCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type ReverseCam::GetType()
+{
+ return REVERSE_CAM;
+}
+
+
+//=============================================================================
+// ReverseCam::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void ReverseCam::SetTarget( ISuperCamTarget* target )
+{
+ mTarget = target;
+}
+
+//=============================================================================
+// ReverseCam::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void ReverseCam::AddTarget( ISuperCamTarget* target )
+{
+ rAssertMsg( false, "Only call SetTarget on the ReverseCam" );
+}
+
+//=============================================================================
+// ReverseCam::GetNumTargets
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int ReverseCam::GetNumTargets() const
+{
+ if ( mTarget )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+//=============================================================================
+// ReverseCam::SetCollisionOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
+//
+// Return: void
+//
+//=============================================================================
+inline void ReverseCam::SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
+{
+ mCollisionOffset = offset;
+ mNumCollisions = numCollisions;
+ mGroundOffset = groundOffset;
+}
+
+#endif //REVERSECAM_H
diff --git a/game/code/camera/sinecosshaker.cpp b/game/code/camera/sinecosshaker.cpp
new file mode 100644
index 0000000..d4253ab
--- /dev/null
+++ b/game/code/camera/sinecosshaker.cpp
@@ -0,0 +1,226 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: SineCosShaker.cpp
+//
+// Description: Implement SineCosShaker
+//
+// History: 02/05/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#ifdef WORLD_BUILDER
+#include "main/toolhack.h"
+#endif
+
+#include <p3d/pointcamera.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#ifndef WORLD_BUILDER
+#include <camera/SineCosShaker.h>
+#include <camera/SuperCam.h>
+#include <camera/supercamconstants.h>
+#include <events/eventdata.h>
+#else
+#include "SineCosShaker.h"
+#include "SuperCam.h"
+#include "supercamconstants.h"
+#include "events/eventdata.h"
+#endif
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SineCosShaker::SineCosShaker
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SineCosShaker::SineCosShaker() :
+ mTime( 180 ),
+ mCurrentTime( 0 ),
+ mSpeed( 0.0f ),
+ mCamera( NULL ),
+ mIsCameraRelative( true ),
+ mAmpYIncrement( 0.942478f ),
+ mAmplitudeY( 0.87f ),
+ mAmpScale( 1.0f ),
+ mAmpScaleMax( 1.10f ),
+ mLooping( false )
+{
+ mDirection.Set( 0.0f, 1.0f, 0.0f );
+}
+
+//==============================================================================
+// SineCosShaker::~SineCosShaker
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SineCosShaker::~SineCosShaker()
+{
+ tRefCounted::Release( mCamera );
+}
+
+//=============================================================================
+// SineCosShaker::SetCamera
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tCamera* camera )
+//
+// Return: void
+//
+//=============================================================================
+void SineCosShaker::SetCamera( tPointCamera* camera )
+{
+ tRefCounted::Assign( mCamera, camera );
+}
+
+//=============================================================================
+// SineCosShaker::ShakeCamera
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* pos,
+// rmt::Vector* targ,
+// unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void SineCosShaker::ShakeCamera( rmt::Vector* pos,
+ rmt::Vector* targ,
+ unsigned int milliseconds )
+{
+ if ( mCurrentTime < mTime || mLooping )
+ {
+ //This is to adjust interpolation when we're running substeps.
+ float timeMod = (float)milliseconds / EXPECTED_FRAME_RATE * mSpeed;
+
+ mAmplitudeY += mAmpYIncrement * timeMod;
+
+ float amp = rmt::Sin( mAmplitudeY );
+
+ if ( !mLooping )
+ {
+ mAmpScale = mAmpScaleMax - ( ( mAmpScaleMax ) * ( (float)mCurrentTime / (float)mTime ) );
+ }
+ else
+ {
+ mAmpScale = 1.0f;
+ }
+
+
+ rmt::Vector dir = mDirection;
+// rmt::Vector dir = rmt::Vector( 1.0f, 0.0f, 0.0f );
+
+// const rmt::Matrix mat = mCamera->GetWorldToCameraMatrix();
+
+// dir.Transform( mat );
+// dir.NormalizeSafe();
+// dir.Scale( amp * mAmpScale );
+
+ dir.Scale( amp * mAmpScale );
+
+ pos->Sub( dir );
+ }
+
+ mCurrentTime += milliseconds;
+}
+
+//=============================================================================
+// SineCosShaker::RegisterDebugInfo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SineCosShaker::RegisterDebugInfo()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchAddUnsignedInt( &mTime, "Total Time", "SuperCam\\Shaker\\SineCos", NULL, NULL, 0, 10000 );
+ radDbgWatchAddFloat( &mSpeed, "Speed", "SuperCam\\Shaker\\SineCos", NULL, NULL, 0.0f, 100.0f );
+ radDbgWatchAddFloat( &mAmpYIncrement, "Amplitude Y Increment", "SuperCam\\Shaker\\SineCos", NULL, NULL, 0.0f, rmt::PI_2 );
+ radDbgWatchAddFloat( &mAmpScaleMax, "Amplitude Scale Max", "SuperCam\\Shaker\\SineCos", NULL, NULL, 0.0f, 100.0f );
+ radDbgWatchAddBoolean( &mIsCameraRelative, "Camera Relative", "SuperCam\\Shaker\\SineCos" );
+ radDbgWatchAddBoolean( &mLooping, "Loop", "SuperCam\\Shaker\\SineCos" );
+#endif
+}
+
+//=============================================================================
+// SineCosShaker::UnregisterDebugInfo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SineCosShaker::UnregisterDebugInfo()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mTime );
+ radDbgWatchDelete( &mSpeed );
+ radDbgWatchDelete( &mAmpYIncrement );
+ radDbgWatchDelete( &mAmpScaleMax );
+ radDbgWatchDelete( &mIsCameraRelative );
+ radDbgWatchDelete( &mLooping );
+#endif
+}
+
+//=============================================================================
+// SineCosShaker::SetShakeData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const ShakeEventData* data )
+//
+// Return: void
+//
+//=============================================================================
+void SineCosShaker::SetShakeData( const ShakeEventData* data )
+{
+ mDirection = data->direction;
+ SetLooping( data->looping );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
diff --git a/game/code/camera/sinecosshaker.h b/game/code/camera/sinecosshaker.h
new file mode 100644
index 0000000..cd2cb9b
--- /dev/null
+++ b/game/code/camera/sinecosshaker.h
@@ -0,0 +1,252 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: sinecosshaker.h
+//
+// Description: Blahblahblah
+//
+// History: 02/05/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef SINECOSSHAKER_H
+#define SINECOSSHAKER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#ifndef WORLD_BUILDER
+//This is the non-tool includes
+#include <camera/icamerashaker.h>
+#else
+#include "icamerashaker.h"
+#endif
+
+//========================================
+// Forward References
+//========================================
+class tPointCamera;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class SineCosShaker : public ICameraShaker
+{
+public:
+ SineCosShaker();
+ virtual ~SineCosShaker();
+
+ void Reset();
+ void ShakeCamera( rmt::Vector* pos, rmt::Vector* targ, unsigned int milliseconds );
+ void SetSpeed( float speed );
+ void SetTime( unsigned int milliseconds );
+ void SetCamera( tPointCamera* camera );
+ void SetCameraRelative( bool cameraRelative );
+ void SetDirection( const rmt::Vector& dir );
+
+ void SetLooping( bool loop );
+ bool DoneShaking();
+
+ const char* const GetName();
+
+ void RegisterDebugInfo();
+ void UnregisterDebugInfo();
+
+ void SetShakeData( const ShakeEventData* data );
+
+ void SetAmpYIncrement( float inc );
+ void SetAmpMaxScale( float max );
+
+
+private:
+
+ unsigned int mTime;
+ unsigned int mCurrentTime;
+ float mSpeed;
+
+ //This camera is screen-relative
+ tPointCamera* mCamera;
+ bool mIsCameraRelative;
+
+ float mAmpYIncrement; //rate of amplitude
+ float mAmplitudeY; //aplitude stored
+
+ float mAmpScale; //scale the amplitude
+ float mAmpScaleMax; //scale the amplitude MAX
+
+ bool mLooping;
+
+ rmt::Vector mDirection;
+
+
+
+ //Prevent wasteful constructor creation.
+ SineCosShaker( const SineCosShaker& sinecosshaker );
+ SineCosShaker& operator=( const SineCosShaker& sinecosshaker );
+};
+
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SineCosShaker::Reset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void SineCosShaker::Reset()
+{
+ //mTime = 0;
+ mCurrentTime = 0;
+ mLooping = false;
+ mAmpScale = 0.0f;
+}
+
+//=============================================================================
+// SineCosShaker::SetSpeed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float speed )
+//
+// Return: void
+//
+//=============================================================================
+inline void SineCosShaker::SetSpeed( float speed )
+{
+ mSpeed = speed;
+}
+
+//=============================================================================
+// SineCosShaker::SetTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+inline void SineCosShaker::SetTime( unsigned int milliseconds )
+{
+ mTime = milliseconds;
+}
+
+//=============================================================================
+// SineCosShaker::SetCameraRelative
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool cameraRelative )
+//
+// Return: void
+//
+//=============================================================================
+inline void SineCosShaker::SetCameraRelative( bool cameraRelative )
+{
+ mIsCameraRelative = cameraRelative;
+}
+
+//=============================================================================
+// SineCosShaker::SetDirection
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& dir )
+//
+// Return: void
+//
+//=============================================================================
+inline void SineCosShaker::SetDirection( const rmt::Vector& dir )
+{
+ mDirection = dir;
+}
+
+//=============================================================================
+// SineCosShaker::SetLooping
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool loop )
+//
+// Return: void
+//
+//=============================================================================
+inline void SineCosShaker::SetLooping( bool loop )
+{
+ mLooping = loop;
+}
+
+//=============================================================================
+// SineCosShaker::DoneShaking
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool SineCosShaker::DoneShaking()
+{
+ return (mCurrentTime >= mTime);
+}
+
+//=============================================================================
+// SineCosShaker::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+inline const char* const SineCosShaker::GetName()
+{
+ return "SineCos Shaker";
+}
+
+//=============================================================================
+// SineCosShaker::SetAmpYIncrement
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float inc )
+//
+// Return: void
+//
+//=============================================================================
+inline void SineCosShaker::SetAmpYIncrement( float inc )
+{
+ mAmpYIncrement = inc;
+}
+
+//=============================================================================
+// SineCosShaker::SetAmpMaxScale
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float max )
+//
+// Return: void
+//
+//=============================================================================
+inline void SineCosShaker::SetAmpMaxScale( float max )
+{
+ mAmpScaleMax = max;
+}
+
+#endif //SINECOSSHAKER_H
diff --git a/game/code/camera/snapshotcam.cpp b/game/code/camera/snapshotcam.cpp
new file mode 100644
index 0000000..0246c83
--- /dev/null
+++ b/game/code/camera/snapshotcam.cpp
@@ -0,0 +1,208 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: SnapshotCam.cpp
+//
+// Description: Implement SnapshotCam
+//
+// History: 11/28/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <p3d/utility.hpp>
+#include <p3d/vectorcamera.hpp>
+#include <camera/SnapshotCam.h>
+#include <camera/supercamcontroller.h>
+
+#include <memory/srrmemory.h>
+
+#include <loading/loadingmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SnapshotCam::SnapshotCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SnapshotCam::SnapshotCam() :
+ mNumCameras( 0 ),
+ mCurrentCamera( 0 ),
+ mToggling( false )
+{
+}
+
+//==============================================================================
+// SnapshotCam::~SnapshotCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SnapshotCam::~SnapshotCam()
+{
+ unsigned int i;
+ for ( i = 0; i < mNumCameras; ++i )
+ {
+ mCameras[ i ]->Release();
+ mCameras[ i ] = NULL;
+ }
+}
+
+//=============================================================================
+// SnapshotCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void SnapshotCam::Update( unsigned int milliseconds )
+{
+ if ( GetFlag( (Flag)FIRST_TIME ) )
+ {
+ unsigned int i;
+ for ( i = 0; i < mNumCameras; ++i )
+ {
+ mCameras[ i ]->Release();
+ mCameras[ i ] = NULL;
+ }
+
+ mNumCameras = 0;
+
+ //Load the file.
+ GetLoadingManager()->LoadSync( FILEHANDLER_PURE3D, "snapshot.p3d" );
+
+ //Find all the tCameras.
+ p3d::inventory->SelectSection("Default");
+ HeapMgr()->PushHeap( GMA_TEMP );
+ tInventory::Iterator<tVectorCamera> it;
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ tVectorCamera* cam = it.First();
+
+ while( cam )
+ {
+ mCameras[ mNumCameras ] = cam;
+ mCameras[ mNumCameras ]->AddRef();
+ ++mNumCameras;
+
+ cam = it.Next();
+ }
+
+ for ( i = 0; i < mNumCameras; ++i )
+ {
+ //Take all the cameras out of the inventory.
+ p3d::inventory->Remove( mCameras[ i ] );
+ }
+
+ SetFlag( (Flag)FIRST_TIME, false );
+ }
+
+ rmt::Vector position, target, vup;
+ float fov, aspect;
+
+ if ( mNumCameras > 0 )
+ {
+ //Select a camera.
+ if ( !mToggling && rmt::Fabs(mController->GetAxisValue( SuperCamController::stickX )) == 1.0f )
+ {
+ mToggling = true;
+
+ mCurrentCamera = (mCurrentCamera + rmt::FtoL(mController->GetAxisValue( SuperCamController::stickX ))) % mNumCameras;
+
+#ifdef RAD_DEBUG
+ char str[256];
+ sprintf( str, "\n\nSNAPSHOT_CAM: %s\n\n", mCameras[mCurrentCamera]->GetName() );
+ rDebugString( str );
+#endif
+ }
+ else if ( mToggling && mController->GetAxisValue( SuperCamController::stickX ) == 0.0f )
+ {
+ mToggling = false;
+ }
+
+ mCameras[mCurrentCamera]->GetPosition( &position );
+ mCameras[mCurrentCamera]->GetDirection( &target );
+ target.Scale( 3.0f );
+ target.Add( position );
+ mCameras[mCurrentCamera]->GetUpVector( &vup );
+ mCameras[mCurrentCamera]->GetFOV( &fov, &aspect );
+ }
+ else
+ {
+ GetPosition( &position );
+ GetTarget( &target );
+ GetCameraUp( &vup );
+ fov = GetFOV();
+ aspect = GetAspect();
+ }
+
+ SetFOV( fov );
+ SetAspect( aspect );
+
+ SetCameraValues( milliseconds, position, target, &vup );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// SnapshotCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SnapshotCam::OnRegisterDebugControls()
+{
+}
+
+//=============================================================================
+// SnapshotCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SnapshotCam::OnUnregisterDebugControls()
+{
+}
diff --git a/game/code/camera/snapshotcam.h b/game/code/camera/snapshotcam.h
new file mode 100644
index 0000000..a05f162
--- /dev/null
+++ b/game/code/camera/snapshotcam.h
@@ -0,0 +1,156 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: snapshotcam.h
+//
+// Description: Blahblahblah
+//
+// History: 11/28/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef SNAPSHOTCAM_H
+#define SNAPSHOTCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+
+//========================================
+// Forward References
+//========================================
+class tVectorCamera;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class SnapshotCam : public SuperCam
+{
+public:
+ enum { MAX_TCAMERAS = 50 };
+
+ SnapshotCam();
+ virtual ~SnapshotCam();
+
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ //This loads the off-line created settings for the camera.
+ //It is passed in as a byte stream of some data of known size.
+ virtual void LoadSettings( unsigned char* settings ) {};
+
+ virtual Type GetType();
+
+ //These are for favourable support of this command
+ virtual void SetTarget( ISuperCamTarget* target );
+ virtual void AddTarget( ISuperCamTarget* target );
+
+ unsigned int GetNumTargets() const;
+
+private:
+ //These functions are to allow real-time control of the settings of
+ //the supercam.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+ virtual void OnInit() { InitMyController(); };
+
+ tVectorCamera* mCameras[ MAX_TCAMERAS ];
+ unsigned int mNumCameras;
+ unsigned int mCurrentCamera;
+
+ bool mToggling;
+
+ //Prevent wasteful constructor creation.
+ SnapshotCam( const SnapshotCam& snapshotcam );
+ SnapshotCam& operator=( const SnapshotCam& snapshotcam );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SnapshotCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+inline const char* const SnapshotCam::GetName() const
+{
+ return "SNAPSHOT_CAM";
+}
+
+//=============================================================================
+// SnapshotCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type SnapshotCam::GetType()
+{
+ return SNAPSHOT_CAM;
+}
+
+//=============================================================================
+// SnapshotCam::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void SnapshotCam::SetTarget( ISuperCamTarget* target )
+{
+}
+
+//=============================================================================
+// SnapshotCam::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void SnapshotCam::AddTarget( ISuperCamTarget* target )
+{
+}
+
+//=============================================================================
+// SnapshotCam::GetNumTargets
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int SnapshotCam::GetNumTargets() const
+{
+ return 0;
+}
+
+#endif //SNAPSHOTCAM_H
diff --git a/game/code/camera/staticcam.cpp b/game/code/camera/staticcam.cpp
new file mode 100644
index 0000000..8bdb5ed
--- /dev/null
+++ b/game/code/camera/staticcam.cpp
@@ -0,0 +1,323 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: StaticCam.cpp
+//
+// Description: Implement StaticCam
+//
+// History: 9/18/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+#ifndef WORLD_BUILDER
+#include <camera/StaticCam.h>
+#include <camera/isupercamtarget.h>
+#include <camera/supercamcontroller.h>
+#include <camera/supercamconstants.h>
+#include <p3d/pointcamera.hpp>
+//TODO: I only really need the tuner variables... Break this file up.
+#include <worldsim/character/character.h>
+#else
+#include "StaticCam.h"
+#include "isupercamtarget.h"
+#include "supercamcontroller.h"
+#include "supercamconstants.h"
+class tPointCamera
+{
+public:
+ void GetFOV( float* fov, float* aspect ) { *fov = 1.5707f; *aspect = 4.0f / 3.0f; };
+};
+
+namespace CharacterTune
+{
+ static float sfMaxSpeed;
+ static float sfDashBurstMax;
+};
+#endif
+
+
+#ifdef DEBUGWATCH
+float STATIC_CAM_MIN_FOV = SUPERCAM_DEFAULT_MIN_FOV;
+#else
+const float STATIC_CAM_MIN_FOV = SUPERCAM_DEFAULT_MIN_FOV;
+#endif
+
+const float STATIC_CAM_FOV_LAG = 0.022f;
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// StaticCam::StaticCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+StaticCam::StaticCam() :
+ mTarget( NULL ),
+ mTargetLag( 1.0f ),
+ mTracking( false ),
+ mFOVDelta( 0.0f ),
+ mMaxFOV( 1.5707f ),
+ mFOVLag( STATIC_CAM_FOV_LAG )
+{
+ mTargetOffset.Set( 0.0f, 0.0f, 0.0f );
+ mTargetPosition.Set( 0.0f, 0.0f, 0.0f );
+ mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+}
+
+//==============================================================================
+// StaticCam::~StaticCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+StaticCam::~StaticCam()
+{
+}
+
+//=============================================================================
+// StaticCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void StaticCam::Update( unsigned int milliseconds )
+{
+ //This is to adjust interpolation when we're running substeps.
+ float timeMod = (float)milliseconds / EXPECTED_FRAME_RATE;
+
+ rmt::Vector targ;
+
+ if ( mTracking )
+ {
+ mTarget->GetPosition( &targ );
+
+ if( mTargetOffset.x != 0.0f ||
+ mTargetOffset.y != 0.0f ||
+ mTargetOffset.z != 0.0f )
+ {
+ rmt::Matrix mat;
+ mat.Identity();
+
+ rmt::Vector heading, vup;
+ mTarget->GetHeading( &heading );
+ mTarget->GetVUP( &vup );
+
+ mat.FillHeading( heading, vup );
+
+ rmt::Vector offset = mTargetOffset;
+ offset.Transform( mat );
+
+ targ.Add( offset );
+ }
+ }
+ else
+ {
+ //The offset is actually a world coordinate position to look at.
+ targ = mTargetOffset;
+ }
+
+ rmt::Vector desiredTarget = targ;
+
+ bool cut, firstTime;
+ cut = GetFlag( (Flag)CUT );
+ firstTime = GetFlag( (Flag)FIRST_TIME );
+
+ if ( cut || firstTime )
+ {
+ //Cutting camera.
+ mTargetPosition = desiredTarget;
+ mTargetPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+
+ //SetFOV( mMaxFOV );
+ mFOVDelta = 0.0f;
+
+ if ( cut )
+ {
+ if ( GetFlag( (Flag)START_TRANSITION ) )
+ {
+ SetFlag( (Flag)START_TRANSITION, false );
+ }
+ else if ( GetFlag( (Flag)TRANSITION ) )
+ {
+ SetFlag( (Flag)END_TRANSITION, true );
+ }
+
+ SetFlag( (Flag)CUT, false );
+ }
+ else
+ {
+ //Not cutting, so let's try this.
+ float fov, aspect = 0.0f;
+ GetCamera()->GetFOV( &fov, &aspect );
+ SetFOV( fov );
+ }
+
+ SetFlag( (Flag)FIRST_TIME, false );
+ }
+
+ if ( mTracking )
+ {
+ float lag = mTargetLag * timeMod;
+ CLAMP_TO_ONE( lag );
+ MotionCubic( &mTargetPosition.x, &mTargetPositionDelta.x, desiredTarget.x, lag );
+ MotionCubic( &mTargetPosition.y, &mTargetPositionDelta.y, desiredTarget.y, lag );
+ MotionCubic( &mTargetPosition.z, &mTargetPositionDelta.z, desiredTarget.z, lag );
+ }
+ else
+ {
+ mTargetPosition = desiredTarget;
+ }
+
+ //--------- Goofin' with the FOV
+
+ if ( ( GetFlag( (Flag)TRANSITION ) ||
+ ( !GetFlag( SuperCam::WRECKLESS_ZOOM ) && mTarget->IsCar() ) ) )
+ {
+ SetFOV( mMaxFOV );
+
+ //Reset this.
+ mFOVDelta = 0.0f;
+ }
+ else if ( GetFlag( SuperCam::WRECKLESS_ZOOM ) )
+ {
+ rmt::Vector posToTarg;
+ posToTarg.Sub( mTargetPosition, mPosition );
+ float dist = posToTarg.Magnitude(); //Square root!
+ float FOV = GetFOV();
+ float FOVDelta = 0.0f;
+ DoWrecklessZoom( dist, 10.0f, 25.0f, STATIC_CAM_MIN_FOV, mMaxFOV, FOV, FOVDelta, mFOVLag, timeMod );
+
+ SetFOV( FOV );
+ }
+ else
+ {
+#ifndef WORLD_BUILDER
+ float zoom = mController->GetValue( SuperCamController::zToggle );
+#else
+ float zoom = 0.0f;
+#endif
+ float FOV = GetFOV();
+ FilterFov( zoom, STATIC_CAM_MIN_FOV, mMaxFOV, FOV, mFOVDelta, mFOVLag, timeMod );
+
+ SetFOV( FOV );
+ }
+
+
+ //--------- Set values.
+
+ SetCameraValues( milliseconds, mPosition, mTargetPosition );
+}
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// StaticCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void StaticCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\Static", GetPlayerID() );
+
+ radDbgWatchAddFloat( &mFOVLag, "FOV Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &STATIC_CAM_MIN_FOV, "FOV Min", nameSpace, NULL, NULL, 0.0f, rmt::PI_BY2 );
+#endif
+}
+
+//=============================================================================
+// StaticCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void StaticCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mFOVLag );
+ radDbgWatchDelete( &STATIC_CAM_MIN_FOV );
+#endif
+}
+
+//=============================================================================
+// StaticCam::GetTargetSpeedModifier
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float StaticCam::GetTargetSpeedModifier()
+{
+ rmt::Vector vel;
+ mTarget->GetVelocity( &vel );
+ float speed = vel.Magnitude(); //Square root!
+
+ //TODO:I wish this was a const.
+ if ( speed < CharacterTune::sfMaxSpeed || rmt::Epsilon( speed, CharacterTune::sfMaxSpeed, 0.01f ) )
+ {
+ return 1.0f;
+ }
+
+ float maxMod = CharacterTune::sfMaxSpeed + CharacterTune::sfDashBurstMax / CharacterTune::sfMaxSpeed;
+ float modifier = (speed - CharacterTune::sfMaxSpeed) / CharacterTune::sfDashBurstMax * maxMod;
+ rAssert( modifier > 0.01f );
+
+ return modifier;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/camera/staticcam.h b/game/code/camera/staticcam.h
new file mode 100644
index 0000000..25097b6
--- /dev/null
+++ b/game/code/camera/staticcam.h
@@ -0,0 +1,268 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: staticcam.h
+//
+// Description: Blahblahblah
+//
+// History: 9/18/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef STATICCAM_H
+#define STATICCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+#ifndef WORLD_BUILDER
+#include <camera/supercam.h>
+#else
+#include "supercam.h"
+#endif
+
+//========================================
+// Forward References
+//========================================
+class ISuperCamTarget;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class StaticCam : public SuperCam
+{
+public:
+ StaticCam();
+ virtual ~StaticCam();
+
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ virtual Type GetType();
+
+ //These are for favourable support of this command
+ virtual void SetTarget( ISuperCamTarget* target );
+ virtual void AddTarget( ISuperCamTarget* target );
+ virtual unsigned int GetNumTargets() const;
+
+ void SetTargetLag( float lag );
+ void SetTargetOffset( const rmt::Vector& offset );
+ void SetPosition( const rmt::Vector& position );
+ void SetTracking( bool isTracking );
+
+ void SetMaxFOV( float fov );
+ void SetFOVLag( float lag );
+
+protected:
+ //Init... This gets called when the camera is registered
+ virtual void OnInit() { InitMyController(); };
+
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+ virtual float GetTargetSpeedModifier();
+
+private:
+ ISuperCamTarget* mTarget;
+ float mTargetLag;
+ rmt::Vector mTargetOffset;
+ rmt::Vector mTargetPosition;
+ rmt::Vector mTargetPositionDelta;
+ rmt::Vector mPosition;
+ bool mTracking;
+
+ float mFOVDelta;
+ float mMaxFOV;
+ float mFOVLag;
+
+ //Prevent wasteful constructor creation.
+ StaticCam( const StaticCam& staticcam );
+ StaticCam& operator=( const StaticCam& staticcam );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+//=============================================================================
+// StaticCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+inline const char* const StaticCam::GetName() const
+{
+ return "STATIC_CAM";
+}
+
+//=============================================================================
+// DebugCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type StaticCam::GetType()
+{
+ return STATIC_CAM;
+}
+
+//=============================================================================
+// StaticCam::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void StaticCam::SetTarget( ISuperCamTarget* target )
+{
+ mTarget = target;
+}
+
+//=============================================================================
+// StaticCam::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void StaticCam::AddTarget( ISuperCamTarget* target )
+{
+ rAssertMsg( false, "Only call SetTarget on the StaticCam" );
+}
+
+//=============================================================================
+// StaticCam::GetNumTargets
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int StaticCam::GetNumTargets() const
+{
+ if ( mTarget )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+//=============================================================================
+// StaticCam::SetTargetLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void StaticCam::SetTargetLag( float lag )
+{
+ mTargetLag = lag;
+}
+
+//=============================================================================
+// StaticCam::SetTargetOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& offset )
+//
+// Return: void
+//
+//=============================================================================
+inline void StaticCam::SetTargetOffset( const rmt::Vector& offset )
+{
+ mTargetOffset = offset;
+}
+
+//=============================================================================
+// StaticCam::SetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& position )
+//
+// Return: void
+//
+//=============================================================================
+inline void StaticCam::SetPosition( const rmt::Vector& position )
+{
+ mPosition = position;
+}
+
+//=============================================================================
+// StaticCam::SetTracking
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool isTracking )
+//
+// Return: void
+//
+//=============================================================================
+inline void StaticCam::SetTracking( bool isTracking )
+{
+ mTracking = isTracking;
+}
+
+//=============================================================================
+// StaticCam::SetMaxFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float fov )
+//
+// Return: void
+//
+//=============================================================================
+inline void StaticCam::SetMaxFOV( float fov )
+{
+ mMaxFOV = fov;
+}
+
+//=============================================================================
+// StaticCam::SetFOVLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void StaticCam::SetFOVLag( float lag )
+{
+ mFOVLag = lag;
+}
+
+
+#endif //STATICCAM_H
diff --git a/game/code/camera/supercam.cpp b/game/code/camera/supercam.cpp
new file mode 100644
index 0000000..822a7ed
--- /dev/null
+++ b/game/code/camera/supercam.cpp
@@ -0,0 +1,1544 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supercam.cpp
+//
+// Description: Implement SuperCam
+//
+// History: 03/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+//These are included in the precompiled headers on XBOX and GC
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <radmath/radmath.hpp>
+
+#include <camera/supercamcontroller.h>
+#include <input/inputmanager.h>
+
+#ifdef WORLD_BUILDER
+#include "main/toolhack.h"
+#endif
+
+#include <p3d/pointcamera.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#ifndef WORLD_BUILDER
+#include <camera/SuperCam.h>
+#include <camera/icamerashaker.h>
+#include <camera/supercamconstants.h>
+
+#include <memory/srrmemory.h>
+#else
+#include "SuperCam.h"
+#include "icamerashaker.h"
+#include "supercamconstants.h"
+#define MEMTRACK_PUSH_GROUP(x)
+#define MEMTRACK_POP_GROUP(x)
+#endif
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+// DEFAULTS
+//const float SUPERCAM_FOV = 1.57079f; //90 Degrees
+//const float SUPERCAM_ASPECT = 4.0f / 3.0f; //Standard aspect ratio
+//const float SUPERCAM_NEAR = 0.1f;
+//const float SUPERCAM_FAR = 20000.0f;
+
+const float CAMERA_DEAD_ZONE = 0.02f; //This is a controller dead zone
+const float CAMERA_ROTATE_SPEED = 0.01f; //How fast to rotate camera
+
+#ifdef DEBUGWATCH
+unsigned int TRANSITION_TIME_LIMIT = 0; //This is in miliseconds
+float FOV_TRANSITION_RATE = 0.122f;
+float MAX_MODIFIER = 30.0f;
+#else
+//NOTE!!!!
+//The values for TRANSITION_RATE and TARGET_TRANSITION RATE should be very close!!!
+//How close? Within 0.001 or you will get some strange effects when transitioning!
+const unsigned int TRANSITION_TIME_LIMIT = 0; //This is in miliseconds
+const float FOV_TRANSITION_RATE = 0.122f;
+const float MAX_MODIFIER = 30.0f;
+#endif
+
+const float TRANSITION_RATE = 0.02f;
+const float TARGET_TRANSITION_RATE = 0.02f;
+
+unsigned int SuperCam::s_secondaryControllerID = 1;
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperCam::SuperCam
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+SuperCam::SuperCam() :
+ mFlags( 0 ),
+ mController( NULL ),
+ mControllerHandle( -1 ),
+ mCamera( NULL ),
+ mSCFOV( SUPERCAM_FOV ),
+ mSCAspect( SUPERCAM_ASPECT ),
+ mSCNearPlane( SUPERCAM_NEAR ),
+ mSCFarPlane( SUPERCAM_FAR ),
+ mCameraVirtualFOV( 0.0f ),
+ mCameraVirtualFOVDelta( 0.0f ),
+ mTransitionTime( 0 ),
+ mTransitionTimeLimit( TRANSITION_TIME_LIMIT ),
+ mTransitionPositionRate( TRANSITION_RATE ),
+ mTransitionTargetRate( TARGET_TRANSITION_RATE ),
+ mFOVTransitionRate( FOV_TRANSITION_RATE ),
+ mShaker( NULL ),
+ mPlayerID( -1 ),
+ mSCBackupFOV( SUPERCAM_FOV ),
+ mSCTwist( 0.0f ),
+ mOldTwist( 0.0f ),
+ mTwistDelta( 0.0f ),
+ mTwistLag( 0.01f )
+{
+ //This get set automatically for all camera's. It's up to the camera
+ //to care if this is set or not. And to clear it.
+ SetFlag( FIRST_TIME, true );
+ SetFlag( DIRTY_HEADING, true );
+
+ mVelocity.Set(0.0f,0.0f,0.0f);
+ mCameraVirtualPosition.Set(0.0f,0.0f,0.0f);
+ mCameraVirtualTarget.Set(0.0f,0.0f,0.0f);
+ mCameraVirtualPositionDelta.Set(0.0f,0.0f,0.0f);
+ mCameraVirtualTargetDelta.Set(0.0f,0.0f,0.0f);
+ mNormalizedHeading.Set(0.0f, 0.0f, 1.0f);
+
+#ifndef WORLD_BUILDER
+ if(p3d::display->IsWidescreen())
+ {
+ mSCAspect = ( 16.0f / 9.0f );
+ }
+#endif
+}
+
+//=============================================================================
+// SuperCam::~SuperCam
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+SuperCam::~SuperCam()
+{
+ if ( mCamera )
+ {
+ mCamera->Release();
+ }
+
+ if ( mController )
+ {
+ InputManager* im = InputManager::GetInstance();
+ rAssert( im );
+
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( this->GetPlayerID() );
+ im->UnregisterMappable( static_cast<unsigned int>( controllerID ), mControllerHandle );
+ mController->Release();
+ }
+}
+
+//=============================================================================
+// SuperCam::RegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::RegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\Base", GetPlayerID() );
+
+ radDbgWatchAddFloat( &mSCFOV, "FOV", nameSpace, NULL, NULL, 0, rmt::PI_2 );
+ radDbgWatchAddFloat( &mSCAspect, "Aspect Ratio", nameSpace, NULL, NULL, 0, 5.0f );
+ radDbgWatchAddFloat( &mSCNearPlane, "Near Plane", nameSpace, NULL, NULL, -1.0f, 30.0f );
+ radDbgWatchAddFloat( &mSCFarPlane, "Far Plane", nameSpace, NULL, NULL, 0, 20000.0f );
+
+ radDbgWatchAddUnsignedInt( &TRANSITION_TIME_LIMIT, "Transition Time", nameSpace, NULL, NULL, 0, 10000 );
+ radDbgWatchAddFloat( &mTransitionPositionRate, "Transition Rate", nameSpace, NULL, NULL, 0, 1.0f );
+ radDbgWatchAddFloat( &mTransitionTargetRate, "Target Transition Rate", nameSpace, NULL, NULL, 0, 1.0f );
+ radDbgWatchAddFloat( &FOV_TRANSITION_RATE, "FOV Transition Rate", nameSpace, NULL, NULL, 0, 1.0f );
+
+ radDbgWatchAddFloat( &MAX_MODIFIER, "Static|Rail max tran speed", nameSpace, NULL, NULL, 10.0f, 100.0f );
+
+ radDbgWatchAddFloat( &mSCTwist, "Camera Twist", nameSpace, NULL, NULL, 0.0f, rmt::PI_2 );
+ radDbgWatchAddFloat( &mTwistLag, "Camera Twist Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+#endif
+
+ OnRegisterDebugControls();
+}
+
+//=============================================================================
+// SuperCam::UnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::UnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mSCFOV );
+ radDbgWatchDelete( &mSCAspect );
+ radDbgWatchDelete( &mSCNearPlane );
+ radDbgWatchDelete( &mSCFarPlane );
+
+ radDbgWatchDelete( &TRANSITION_TIME_LIMIT );
+ radDbgWatchDelete( &mTransitionPositionRate );
+ radDbgWatchDelete( &mTransitionTargetRate );
+ radDbgWatchDelete( &FOV_TRANSITION_RATE );
+
+ radDbgWatchDelete( &MAX_MODIFIER );
+
+ radDbgWatchDelete( &mSCTwist );
+ radDbgWatchDelete( &mTwistLag );
+#endif
+
+ OnUnregisterDebugControls();
+}
+
+//=============================================================================
+// SuperCam::GetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* position )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::GetPosition( rmt::Vector* position ) const
+{
+ if( mCamera )
+ {
+ if ( GetFlag( (Flag)SHAKE ) || GetFlag( (Flag)TRANSITION ) )
+ {
+ *position = mCameraVirtualPosition;
+ }
+ else
+ {
+ mCamera->GetPosition( position );
+ }
+ }
+ else
+ {
+ position->Set( 0.0f, 0.0f, 0.0f );
+ }
+}
+
+//=============================================================================
+// SuperCam::GetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* target )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::GetTarget( rmt::Vector* target ) const
+{
+ if( mCamera )
+ {
+ if ( GetFlag( (Flag)SHAKE ) || GetFlag( (Flag)TRANSITION ) )
+ {
+ *target = mCameraVirtualTarget;
+ }
+ else
+ {
+ mCamera->GetTarget( target );
+ }
+ }
+ else
+ {
+ target->Set(0,0,0);
+ }
+}
+
+//=============================================================================
+// SuperCam::GetHeading
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* heading )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::GetHeading( rmt::Vector* heading ) const
+{
+ if ( mCamera )
+ {
+ rmt::Vector pos, targ;
+ if ( GetFlag((Flag)SHAKE ) || GetFlag( (Flag)TRANSITION ) )
+ {
+ pos = mCameraVirtualPosition;
+ targ = mCameraVirtualTarget;
+ }
+ else
+ {
+ mCamera->GetPosition( &pos );
+ mCamera->GetTarget( &targ );
+ }
+
+ heading->Sub( targ, pos );
+ }
+ else
+ {
+ heading->Set(0,0,0);
+ }
+}
+
+//=============================================================================
+// SuperCam::GetHeadingNormalized
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* heading )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::GetHeadingNormalized( rmt::Vector* heading )
+{
+ rAssert( mCamera );
+
+ if ( GetFlag((Flag)DIRTY_HEADING) )
+ {
+ GetHeading( &mNormalizedHeading );
+ mNormalizedHeading.Normalize();
+ SetFlag((Flag)DIRTY_HEADING, false);
+ }
+
+ rAssert(mNormalizedHeading.MagnitudeSqr() != 0.0f);
+
+ *heading = mNormalizedHeading;
+}
+
+//=============================================================================
+// SuperCam::GetCameraUp
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* up )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::GetCameraUp( rmt::Vector* up ) const
+{
+ if ( mCamera )
+ {
+ //This shouldn't change during the SHAKE or TRANSITION
+ mCamera->GetVUp( up );
+ }
+ else
+ {
+ up->Set(0,0,0);
+ }
+}
+
+//=============================================================================
+// SuperCam::SetFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float FOVinRadians )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::SetFOV( float FOVinRadians )
+{
+ if ( GetFlag((Flag)OVERRIDE_FOV ) )
+ {
+ //We' are overriding, this will be the regular FOV
+ //when we're done
+ mSCBackupFOV = FOVinRadians;
+ }
+ else if ( GetFlag( (Flag)SHAKE ) || GetFlag( (Flag)TRANSITION ) )
+ {
+ //Middle of transition.
+ mCameraVirtualFOV = FOVinRadians;
+ mCameraVirtualFOVDelta = 0.0f;
+ }
+ else
+ {
+ //The is the regular FOV of the camera.
+ mSCFOV = FOVinRadians;
+ }
+}
+
+//=============================================================================
+// SuperCam::GetFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float (in radians)
+//
+//=============================================================================
+float SuperCam::GetFOV() const
+{
+ if ( GetFlag((Flag)SHAKE ) || GetFlag( (Flag)TRANSITION ) )
+ {
+ return mCameraVirtualFOV;
+ }
+ else
+ {
+ return mSCFOV;
+ }
+}
+
+//=============================================================================
+// SuperCam::Display
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::Display() const
+{
+#ifdef SUPERCAM_DEBUG
+ //We could display the name of the camera or something.
+
+ OnDisplay();
+#endif
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//==============================================================================
+// SuperCam::SetCameraValues
+//==============================================================================
+// Description: This is the best way to change the position and target of the
+// camera. If you do not pass in a VUP vector one will be
+// calculated for you.
+// Also, if DEBUG is defined then the values of the camera matrix
+// will be checked.
+//
+// Parameters: ( unsigned int milliseconds,
+// rmt::Vector pos,
+// rmt::Vector targ,
+// const rmt::Vector* vup )
+//
+// Return: void
+//
+//==============================================================================
+void SuperCam::SetCameraValues( unsigned int milliseconds,
+ rmt::Vector pos,
+ rmt::Vector targ,
+ const rmt::Vector* vup )
+{
+#ifdef PROFILER_ENABLED
+ char sCname[256];
+ sprintf( sCname, "SC: %d SetCameraValues", mPlayerID );
+ BEGIN_PROFILE( sCname )
+#endif
+
+ rAssert( !rmt::Epsilon( pos.MagnitudeSqr(), 0.0f, 0.01f ) );
+
+ CorrectDist( pos, targ );
+
+ rAssertMsg( mPlayerID >= 0, "You need to set a player ID before anyting else!" );
+ rAssertMsg( !(GetFlag( (Flag)SHAKE ) && GetFlag( (Flag)TRANSITION )), "Do not shake the camera while transitioning!" );
+
+ if ( GetFlag( (Flag)START_SHAKE ) )
+ {
+ SetupShake();
+ }
+ else if ( GetFlag( (Flag)END_SHAKE ) )
+ {
+ EndShake();
+ }
+ else if ( GetFlag( (Flag)START_TRANSITION ) )
+ {
+ SetupTransition();
+ }
+ else if ( GetFlag( (Flag)END_TRANSITION ) )
+ {
+ EndTransition();
+ }
+
+ //Calculate the velocity of the camera
+ rmt::Vector oldPos;
+ GetPosition( &oldPos );
+
+ if ( GetFlag( (Flag)SHAKE ) || GetFlag( (Flag)TRANSITION ) )
+ {
+ //If we're doing an ease in, we don't do this...
+ if ( !GetFlag( (Flag)EASE_IN ) )
+ {
+ //Store the position that the super cam THINKS it's at...
+ mCameraVirtualPosition = pos;
+ mCameraVirtualTarget = targ;
+ }
+
+ if ( GetFlag( (Flag)SHAKE ) )
+ {
+ mShaker->ShakeCamera( &pos, &targ, milliseconds );
+ }
+ else
+ {
+
+ if ( GetFlag( (Flag)EASE_IN ) )
+ {
+ if( mTransitionPositionRate >= 1.0f )
+ {
+ //Hackish
+ //Emulate a cut... Clean this up sometime eh?
+ mTransitionTime = mTransitionTimeLimit - 1;
+ }
+ else
+ {
+ float timeMod = rmt::LtoF(milliseconds) / EXPECTED_FRAME_RATE;
+ float timeEffect = MAX_MODIFIER * mTransitionPositionRate;
+
+ //Also take into account the target's velocity.
+ //This allows us to transition faster when the character is
+ //moving quickly.
+ timeEffect *= GetTargetSpeedModifier();
+
+ rDebugPrintf( "timeEffect: %.4f\n", timeEffect );
+
+ mTransitionTime += rmt::FtoL((rmt::LtoF(milliseconds) * timeEffect));
+ }
+ }
+ else
+ {
+ mTransitionTime += milliseconds;
+ }
+
+
+ rmt::Vector distPos, distTarg;
+ rmt::Vector actPos, actTarg;
+ mCamera->GetPosition( &actPos );
+ mCamera->GetTarget( &actTarg );
+
+ distPos.Sub( pos, actPos );
+ distTarg.Sub( targ, actTarg );
+
+ if ( mTransitionTime >= mTransitionTimeLimit ||
+ (rmt::Epsilon( distPos.MagnitudeSqr(), 0.0f, 0.001f ) &&
+ rmt::Epsilon( distTarg.MagnitudeSqr(), 0.0f, 0.001f ) &&
+ rmt::Epsilon( mSCFOV, mCameraVirtualFOV, 0.001f ) )
+ )
+ {
+ //Turn it off now.
+ SetFlag( (Flag)END_TRANSITION, true );
+ EndTransition();
+ }
+ else
+ {
+ if ( GetFlag( (Flag)EASE_IN ) )
+ {
+ //Do the Ease in
+ EaseIn( rmt::LtoF(mTransitionTime) / rmt::LtoF(mTransitionTimeLimit), &pos, &targ, milliseconds );
+ TransitionFOV( rmt::LtoF(mTransitionTime) / rmt::LtoF(mTransitionTimeLimit), &mSCFOV, milliseconds );
+ }
+ else
+ {
+ //Do the Transition
+ TransitionCamera( rmt::LtoF(mTransitionTime) / rmt::LtoF(mTransitionTimeLimit), &pos, &targ, &mSCFOV, milliseconds, GetFlag( (Flag)QUICK_TRANSITION ) );
+ }
+ }
+ }
+ }
+
+ //Better place for the velocity calculation.
+ mVelocity.Sub( pos, oldPos );
+
+ //Update the position of the camera and set the VUP
+ mCamera->SetPosition( pos );
+ mCamera->SetTarget( targ );
+
+ SetFlag((Flag)DIRTY_HEADING, true);
+
+ if ( vup )
+ {
+ mCamera->SetVUp( *vup );
+ }
+ else
+ {
+ mCamera->SetVUp( UpdateVUP( pos, targ ) );
+ }
+
+ //This will be cool in the halloween level
+ float oldTwist = mCamera->GetTwist();
+ if ( !rmt::Epsilon( mSCTwist, oldTwist, 0.001f ) )
+ {
+ AdjustAngles( &mSCTwist, &oldTwist, &mTwistDelta );
+ MotionCubic( &oldTwist, &mTwistDelta, mSCTwist, mTwistLag );
+ }
+
+ mCamera->SetTwist( oldTwist );
+
+ //Write the overriding FOV, aspect and near and far planes.
+ mCamera->SetFOV( mSCFOV, mSCAspect );
+ mCamera->SetNearPlane( mSCNearPlane );
+ mCamera->SetFarPlane( mSCFarPlane );
+
+#ifdef RAD_DEBUG
+ TestCameraMatrix();
+#endif
+
+#ifdef PROFILER_ENABLED
+ END_PROFILE( sCname )
+#endif
+
+// char out[256];
+// sprintf( out, "pos: %.2f, %.2f, %.2f targ: %.2f, %.2f, %.2f\n", pos.x, pos.y, pos.z, targ.x, targ.y, targ.z );
+// rDebugString( out );
+}
+
+//=============================================================================
+// SuperCam::OverrideFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool enable, float time )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::OverrideFOV( bool enable, float time, float rate )
+{
+ if ( enable != GetFlag( (Flag)OVERRIDE_FOV ) )
+ {
+ //Toggling
+
+ if ( enable )
+ {
+ //Turning on.
+ if ( GetFlag( (Flag)TRANSITION ) )
+ {
+ //Switch the stored override value for the current regular value
+ //which is stored in the virtual fov since we're tansitioning.
+ float tempFOV = mSCBackupFOV;
+ mSCBackupFOV = mCameraVirtualFOV;
+ mCameraVirtualFOV = tempFOV;
+ }
+ else
+ {
+ //Swap the regular fov for the stored override FOV
+ float tempFOV = mSCBackupFOV;
+ mSCBackupFOV = mSCFOV;
+ mSCFOV = tempFOV;
+ }
+ }
+ else
+ {
+ //Bring the regular FOV value back.
+ if ( GetFlag( (Flag)TRANSITION ) )
+ {
+ mCameraVirtualFOV = mSCBackupFOV;
+ }
+ else
+ {
+ mSCFOV = mSCBackupFOV;
+ }
+
+ //Clear out the stored FOV.
+ mSCBackupFOV = 0.0f;
+ }
+
+ //Use the transition system to transition the
+ //fov..
+ mTransitionTimeLimit = rmt::FtoL(time * 1000.0f);
+ mFOVTransitionRate = rate;
+
+ SetFlag( (Flag)OVERRIDE_FOV, enable );
+
+ if ( !GetFlag((Flag)TRANSITION ) )
+ {
+ SetupTransition( true );
+ }
+ }
+}
+
+//=============================================================================
+// SuperCam::DisableOverride
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::DisableOverride()
+{
+ if ( GetFlag( (Flag)OVERRIDE_FOV ) )
+ {
+ if ( GetFlag( (Flag)TRANSITION ) )
+ {
+ mCameraVirtualFOV = mSCBackupFOV;
+ }
+ else
+ {
+ mSCFOV = mSCBackupFOV;
+ }
+
+ mSCBackupFOV = 0.0f;
+
+ SetFlag( (Flag)OVERRIDE_FOV, false );
+ }
+}
+
+
+//=============================================================================
+// SuperCam::SetFOVOverride
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float newFOV )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::SetFOVOverride( float newFOV )
+{
+ if ( GetFlag( (Flag)OVERRIDE_FOV) )
+ {
+ if ( GetFlag( (Flag)TRANSITION ) )
+ {
+ //Middle of a transition, set the virtual FOV...
+ mCameraVirtualFOV = newFOV;
+ mCameraVirtualFOVDelta = 0.0f; //Must reset when we change this!
+ }
+ else
+ {
+ //Already overriding, need to set a new number
+ mSCFOV = newFOV;
+ }
+ }
+ else
+ {
+ //Hang on to the override until we need it.
+ mSCBackupFOV = newFOV;
+ }
+}
+
+//=============================================================================
+// SuperCam::SetCamera
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tPointCamera* cam )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::SetCamera( tPointCamera* cam )
+{
+ if ( mCamera )
+ {
+ mCamera->Release();
+ }
+
+ mCamera = cam;
+ mCamera->AddRef();
+}
+
+//=============================================================================
+// SuperCam::ClampAngle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float* angle, bool* clamped )
+//
+// Return: int (This number of times PI_2 was added
+//
+//=============================================================================
+int SuperCam::ClampAngle( float* angle ) const
+{
+ int total = 0;
+
+ if ( *angle < 0.0f )
+ {
+ do
+ {
+ //Add 360 until the number is positive
+ *angle += rmt::PI_2;
+ ++total;
+ }
+ while ( *angle < 0.0f );
+ }
+ else if ( *angle > rmt::PI_2 )
+ {
+ do
+ {
+ //Subtract 360 until number is <= 360
+ *angle -= rmt::PI_2;
+ --total;
+ }
+ while ( *angle > rmt::PI_2 );
+ }
+
+ return total;
+}
+
+//=============================================================================
+// SuperCam::AdjustAngles
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float* desiredAngle,
+// float* currentAngle,
+// float* curentAngleDelta )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::AdjustAngles( float* desiredAngle,
+ float* currentAngle,
+ float* currentAngleDelta ) const
+{
+ //First we should get all values within 0 360 ( 0 - 2 PI )
+ ClampAngle( desiredAngle );
+ ClampAngle( currentAngle );
+
+ //We only want to interpolate to a rotation via the fewest number of degrees.
+ if ( rmt::Fabs( *desiredAngle - *currentAngle ) > rmt::PI ) //More than 180deg
+ {
+ //They are too far apart, which way to rotate?
+ if ( *desiredAngle - *currentAngle < 0.0f )
+ {
+ //desiredAngle is bigger than currentAngle by >= 180 deg
+ //rotate towards 0
+ //Add 360 deg
+ *desiredAngle += rmt::PI_2;
+ }
+ else
+ {
+ //desiredAngle is smaller than currentAngle by >= 180 deg
+ //rotate away from 0
+ //Subtract 360 deg
+ *desiredAngle -= rmt::PI_2;
+ }
+ }
+}
+
+//=============================================================================
+// SuperCam::Shutdown
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::Shutdown()
+{
+ DisableOverride();
+ EndTransition( true );
+ ShutDownMyController();
+ OnShutdown(); //Tell the child..
+}
+
+//=============================================================================
+// SuperCam::CorrectDist
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& pos, rmt::Vector& targ )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::CorrectDist( const rmt::Vector& pos, rmt::Vector& targ )
+{
+ //Make sure pos and target aren't too close!
+ rmt::Vector pos2targ;
+ pos2targ.Sub( targ, pos );
+ if ( pos2targ.MagnitudeSqr() < 1.0f )
+ {
+ pos2targ.NormalizeSafe();
+ pos2targ.Scale( 1.0f );
+
+ targ.Add( pos, pos2targ );
+
+ // rDebugString( "\n\nWARNING! Adjusting camera targ as it is too close to the position!\n\n" );
+ }
+}
+
+//=============================================================================
+// SuperCam::InitMyController
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int controllerID )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::InitMyController( int controllerID )
+{
+MEMTRACK_PUSH_GROUP( "SuperCam" );
+ if ( mController == NULL )
+ {
+ mController = new SuperCamController();
+ rAssert( mController );
+ mController->AddRef();
+ }
+
+ if ( mControllerHandle == -1 )
+ {
+ InputManager* im = InputManager::GetInstance();
+ rAssert( im );
+
+ if ( controllerID == -1 )
+ {
+ //Get a new one, otherwise use the passed in one.
+ controllerID = GetInputManager()->GetControllerIDforPlayer( this->GetPlayerID() );
+ }
+
+ if (controllerID != -1 )
+ {
+ mControllerHandle = im->RegisterMappable( static_cast<unsigned int>( controllerID ), mController );
+ }
+ }
+MEMTRACK_POP_GROUP("SuperCam");
+}
+
+//=============================================================================
+// SuperCam::ShutDownMyController
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::ShutDownMyController()
+{
+ if ( mControllerHandle != -1 )
+ {
+ InputManager* im = InputManager::GetInstance();
+ rAssert( im );
+
+ rTuneAssert( mControllerHandle != -1 );
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( this->GetPlayerID() );
+ im->UnregisterMappable( static_cast<unsigned int>( controllerID ), mController );
+ mControllerHandle = -1;
+ }
+}
+
+//=============================================================================
+// SuperCam::GetCameraNonConst
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: tPointCamera
+//
+//=============================================================================
+tPointCamera* SuperCam::GetCameraNonConst()
+{
+ return mCamera;
+}
+
+//=============================================================================
+// SuperCam::SphericalMotion
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& target,
+// rmt::Vector& currentPos,
+// rmt::Vector& desiredPos,
+// rmt::Vector& desiredPosDelta,
+// float rate,
+// float timeMod )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::SphericalMotion( const rmt::Vector& target,
+ rmt::Vector& currentPos,
+ const rmt::Vector& desiredPos,
+ rmt::Vector& desiredPosDelta,
+ const float rate ) const
+{
+#ifdef PROFILER_ENABLED
+ char sCname[256];
+ sprintf( sCname, "SC: %d Spherical Motion", mPlayerID );
+ BEGIN_PROFILE( sCname )
+#endif
+
+ rmt::Vector virtTargToPos;
+ rmt::Vector virtTargToVirtPos;
+
+ virtTargToPos.Sub( currentPos, target );
+ virtTargToVirtPos.Sub( desiredPos, target );
+
+ float actRot, actElev, actMag;
+ float virtRot, virtElev, virtMag;
+
+ rmt::CartesianToSpherical( virtTargToPos.x, virtTargToPos.z, virtTargToPos.y, &actMag, &actRot, &actElev );
+ rmt::CartesianToSpherical( virtTargToVirtPos.x, virtTargToVirtPos.z, virtTargToVirtPos.y, &virtMag, &virtRot, &virtElev );
+
+ //Clean up the angles.
+ AdjustAngles( &virtRot, &actRot, &desiredPosDelta.x );
+ AdjustAngles( &actElev, &actElev, &desiredPosDelta.y );
+
+ MotionCubic( &actRot, &desiredPosDelta.x, virtRot, rate );
+ MotionCubic( &actElev, &desiredPosDelta.y, virtElev, rate );
+ MotionCubic( &actMag, &desiredPosDelta.z, virtMag, rate );
+
+ rmt::Vector newPos;
+ rmt::SphericalToCartesian( actMag, actRot, actElev, &newPos.x, &newPos.z, &newPos.y );
+
+ currentPos = newPos;
+
+#ifdef PROFILER_ENABLED
+ END_PROFILE( sCname )
+#endif
+}
+
+//=============================================================================
+// SuperCam::UpdateVUP
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector position, rmt::Vector target )
+//
+// Return: rmt::Vector
+//
+//=============================================================================
+rmt::Vector SuperCam::UpdateVUP( rmt::Vector position, rmt::Vector target )
+{
+#ifdef PROFILER_ENABLED
+ char sCname[256];
+ sprintf( sCname, "SC: %d UpdateVUP", mPlayerID );
+ BEGIN_PROFILE( sCname )
+#endif
+
+ const float epsilon = 0.01f;
+ //Set the vUP by projecting the heading into the ZX plane and creating a
+ //crossproduct of a right angle to the projected heading along the X axis
+ //and the heading.
+ rmt::Vector CamX, CamY, CamZ;
+ CamZ.Sub(target, position);
+ CamX.Set(CamZ.z, 0, -CamZ.x);
+
+ if ( rmt::Epsilon( CamX.x, 0, epsilon ) &&
+ rmt::Epsilon( CamX.y, 0, epsilon ) &&
+ rmt::Epsilon( CamX.z, 0, epsilon ) )
+ {
+ //Then the camera is looking straight down.
+ CamY.Set( 0, 0, 1.0f ); //Up along the Z...
+ }
+ else
+ {
+ CamY.CrossProduct(CamZ, CamX);
+ CamY.Normalize();
+ }
+
+#ifdef PROFILER_ENABLED
+ END_PROFILE( sCname )
+#endif
+
+ return CamY;
+}
+
+//=============================================================================
+// SuperCam::EaseMotion
+//=============================================================================
+// Description: Parabolic ease-in / ease-out
+// The default case of no ease-in/ease-out would produce a velocity
+// curve that is a horizontal straight line of vec0 as t goes from 0
+// to 1. The distance covered would be ease(1) = vec0*1.
+// Assume constant acceleration and deceleration at the beginning
+// and end of the motion, and zero acceleration during the middle
+// of the motion. t is parametric time ( 0 - 1 ), a and b are times
+// when acceleration ends and deceleration begins.
+//
+// Parameters: ( float t, float a, float b )
+//
+// Return: float
+//
+//=============================================================================
+float SuperCam::EaseMotion( float t, float a, float b )
+{
+ float vec0, d;
+
+ vec0 = 2.0f / ( 1 + b - a ); //Constant velocity attained.
+
+ if ( t < a )
+ {
+ d = vec0 * t * t / ( 2.0f * a );
+ }
+ else
+ {
+ d = vec0 * a / 2.0f;
+
+ if ( t < b )
+ {
+ d += ( t - a ) / 2.0f;
+ }
+ else
+ {
+ d += ( b - a ) * vec0;
+ d += ( t - t * t / 2.0f - b + b * b / 2.0f ) * vec0 / ( 1 - b );
+ }
+ }
+
+ return d;
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperCam::DoCameraTransition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool quick, unsigned int timems )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::DoCameraTransition( bool quick, unsigned int timems )
+{
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ //Let's not transition if we're waiting to cut.
+ return;
+ }
+
+#ifdef DEBUGWATCH
+ if ( TRANSITION_TIME_LIMIT > 0 )
+ {
+ //Override the time limit by the watcher value.
+ timems = TRANSITION_TIME_LIMIT;
+ }
+#endif
+
+ mTransitionTimeLimit = timems;
+
+ if ( GetFlag((Flag)SHAKE ) )
+ {
+ //Hmmmm... This could be a hack.
+ EndShake();
+ }
+
+ SetFlag( (Flag)START_TRANSITION, true );
+
+ if ( quick )
+ {
+ SetFlag( (Flag)QUICK_TRANSITION, true );
+ }
+
+ if ( GetType() == STATIC_CAM || GetType() == RAIL_CAM )
+ {
+ SetFlag( (Flag)EASE_IN, true );
+ }
+}
+
+//=============================================================================
+// SuperCam::TestCameraMatrix
+//=============================================================================
+// Description: This is the only way to tell when the camera gets a bad
+// setting that will blow up the game.
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::TestCameraMatrix()
+{
+#ifdef PROFILER_ENABLED
+ char sCname[256];
+ sprintf( sCname, "SC: %d TestCameraMatrix", mPlayerID );
+ BEGIN_PROFILE( sCname )
+#endif
+ const rmt::Matrix c2w = mCamera->GetWorldToCameraMatrix();
+
+ int i, j;
+ for( i = 0; i < 4; i++ )
+ {
+ for( j = 0; j < 4; j++ )
+ {
+ rAssertMsg( !rmt::IsNan(c2w.m[i][j]), "This is a bad matrix for the super cam!");
+ }
+ }
+#ifdef PROFILER_ENABLED
+ END_PROFILE( sCname )
+#endif
+
+}
+
+//=============================================================================
+// SuperCam::SetupShake
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::SetupShake()
+{
+ if ( !GetFlag( (Flag)SHAKE ) && mShaker )
+ {
+ rmt::Vector oldPos;
+ GetPosition( &oldPos );
+
+ mCameraVirtualPosition = oldPos;
+
+ SetFlag( (Flag)START_SHAKE, false );
+ SetFlag( (Flag)SHAKE, true );
+
+ rAssert( mShaker );
+
+ mShaker->SetCamera( mCamera );
+ mShaker->SetSpeed( 1.0f );
+ mShaker->RegisterDebugInfo();
+ }
+}
+
+
+//=============================================================================
+// SuperCam::EndShake
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::EndShake()
+{
+ if ( GetFlag( (Flag)SHAKE ) )
+ {
+ mCamera->SetPosition( mCameraVirtualPosition );
+ mCamera->SetTarget( mCameraVirtualTarget );
+
+ mCameraVirtualPosition.Set(0.0f, 0.0f, 0.0f);
+ mCameraVirtualTarget.Set(0.0f, 0.0f, 0.0f);
+
+ SetFlag( (Flag)START_SHAKE, false );
+ SetFlag( (Flag)SHAKE, false );
+ SetFlag( (Flag)END_SHAKE, false );
+
+ mShaker->UnregisterDebugInfo();
+ mShaker = NULL;
+ }
+}
+
+//=============================================================================
+// SuperCam::SetupTransition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool swap )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::SetupTransition( bool swap )
+{
+ if ( !GetFlag( (Flag)TRANSITION ) )
+ {
+ rmt::Vector oldPos, oldTarget;
+ GetPosition( &oldPos );
+
+ mCameraVirtualPosition = oldPos;
+
+ GetTarget( &oldTarget );
+ mCameraVirtualTarget = oldTarget;
+
+ if ( swap )
+ {
+ float temp = mCameraVirtualFOV;
+ mCameraVirtualFOV = mSCFOV;
+ mSCFOV = temp;
+ }
+ else
+ {
+ mCameraVirtualFOV = mSCFOV;
+
+ float waste = 0.0f;
+ mCamera->GetFOV( &mSCFOV, &waste );
+ }
+
+ SetFlag( (Flag)TRANSITION, true );
+ }
+
+ mTransitionTime = 0;
+ mCameraVirtualPositionDelta.Set(0.0f, 0.0f, 0.0f);
+ mCameraVirtualTargetDelta.Set(0.0f, 0.0f, 0.0f);
+ mCameraVirtualFOVDelta = 0.0f;
+
+ SetFlag( (Flag)START_TRANSITION, false );
+}
+
+//=============================================================================
+// SuperCam::EndTransition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool abort )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::EndTransition( bool abort )
+{
+ if ( GetFlag( (Flag)TRANSITION ) )
+ {
+ if ( !abort )
+ {
+ mCamera->SetPosition( mCameraVirtualPosition );
+ mCamera->SetTarget( mCameraVirtualTarget );
+ mSCFOV = mCameraVirtualFOV;
+ }
+
+ mCameraVirtualPosition.Set(0.0f, 0.0f, 0.0f);
+ mCameraVirtualTarget.Set(0.0f, 0.0f, 0.0f);
+ mCameraVirtualPositionDelta.Set(0.0f, 0.0f, 0.0f);
+ mCameraVirtualTargetDelta.Set(0.0f, 0.0f, 0.0f);
+ mCameraVirtualFOVDelta = 0.0f;
+
+ mTransitionTimeLimit = TRANSITION_TIME_LIMIT;
+ mFOVTransitionRate = FOV_TRANSITION_RATE;
+
+ SetFlag( (Flag)TRANSITION, false );
+ SetFlag( (Flag)END_TRANSITION, false );
+ SetFlag( (Flag)QUICK_TRANSITION, false );
+ }
+
+ SetFlag( (Flag)START_TRANSITION, false );
+}
+
+//=============================================================================
+// SuperCam::ShakeCamera
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* pos, rmt::Vector* targ, unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::ShakeCamera( rmt::Vector* pos,
+ rmt::Vector* targ,
+ unsigned int milliseconds )
+{
+ if ( mShaker )
+ {
+ if ( !mShaker->DoneShaking() )
+ {
+ mShaker->ShakeCamera( pos, targ, milliseconds );
+
+ if ( mShaker->DoneShaking() )
+ {
+ DisableShake();
+ }
+ }
+ }
+}
+
+//=============================================================================
+// SuperCam::TransitionCamera
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float timeLeftPct, rmt::Vector* pos, rmt::Vector* targ, float* fov, unsigned int milliseconds, bool quick )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::TransitionCamera( float timeLeftPct,
+ rmt::Vector* pos,
+ rmt::Vector* targ,
+ float* fov,
+ unsigned int milliseconds,
+ bool quick )
+{
+ //This is to adjust interpolation when we're running substeps.
+ float timeMod = (float)milliseconds / EXPECTED_FRAME_RATE;
+
+ float posInterval = ( ( ( 1.0f - mTransitionPositionRate ) * timeLeftPct ) + mTransitionPositionRate ) * timeMod;
+ CLAMP_TO_ONE(posInterval);
+
+ float targInterval = ( ( ( 1.0f - mTransitionTargetRate ) * timeLeftPct ) + mTransitionTargetRate )* timeMod;
+ CLAMP_TO_ONE(targInterval);
+
+ rmt::Vector actPos, actTarg;
+ mCamera->GetPosition( &actPos );
+ mCamera->GetTarget( &actTarg );
+
+ if ( !quick )
+ {
+ //Calculate the new position
+ SphericalMotion( mCameraVirtualTarget, actPos, mCameraVirtualPosition, mCameraVirtualPositionDelta, posInterval );
+
+ actPos.Add( actPos, mCameraVirtualTarget );
+ }
+ else
+ {
+ //Do it quick.
+ MotionCubic( &actPos.x, &mCameraVirtualPositionDelta.x, mCameraVirtualPosition.x, posInterval );
+ MotionCubic( &actPos.y, &mCameraVirtualPositionDelta.y, mCameraVirtualPosition.y, posInterval );
+ MotionCubic( &actPos.z, &mCameraVirtualPositionDelta.z, mCameraVirtualPosition.z, posInterval );
+ }
+
+ //Calculate the new targetPosition
+ MotionCubic( &actTarg.x, &mCameraVirtualTargetDelta.x, mCameraVirtualTarget.x, targInterval );
+ MotionCubic( &actTarg.y, &mCameraVirtualTargetDelta.y, mCameraVirtualTarget.y, targInterval );
+ MotionCubic( &actTarg.z, &mCameraVirtualTargetDelta.z, mCameraVirtualTarget.z, targInterval );
+
+ float actFOV, actAspect;
+ GetCamera()->GetFOV( &actFOV, &actAspect );
+ TransitionFOV( timeLeftPct, &actFOV, milliseconds );
+
+ *pos = actPos;
+ *targ = actTarg;
+ *fov = actFOV;
+}
+
+//=============================================================================
+// SuperCam::TransitionFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float timeLeftPct, float* fov, unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::TransitionFOV( float timeLeftPct, float* fov, unsigned int milliseconds )
+{
+ //This is to adjust interpolation when we're running substeps.
+ float timeMod = (float)milliseconds / EXPECTED_FRAME_RATE;
+
+ float fovInterval = ( ( ( 1.0f - mFOVTransitionRate ) * timeLeftPct ) + mFOVTransitionRate ) * timeMod;
+ CLAMP_TO_ONE(fovInterval);
+
+ //Calculate the new FOV
+ MotionCubic( fov, &mCameraVirtualFOVDelta, mCameraVirtualFOV, fovInterval );
+}
+
+
+//=============================================================================
+// SuperCam::EaseIn
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float timeLeftPct, rmt::Vector* pos, rmt::Vector* targ, unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::EaseIn( float timeLeftPct, rmt::Vector* pos, rmt::Vector* targ, unsigned int milliseconds )
+{
+ float d = EaseMotion( timeLeftPct, 0.0f, 0.0f );
+// char out[256];
+// sprintf( out, "T = %.2f, D = %.2f\n", timeLeftPct, d );
+// rDebugString( out );
+
+ pos->x = ( pos->x - mCameraVirtualPosition.x ) * d + mCameraVirtualPosition.x;
+ pos->y = ( pos->y - mCameraVirtualPosition.y ) * d + mCameraVirtualPosition.y;
+ pos->z = ( pos->z - mCameraVirtualPosition.z ) * d + mCameraVirtualPosition.z;
+
+ targ->x = ( targ->x - mCameraVirtualTarget.x ) * d + mCameraVirtualTarget.x;
+ targ->y = ( targ->y - mCameraVirtualTarget.y ) * d + mCameraVirtualTarget.y;
+ targ->z = ( targ->z - mCameraVirtualTarget.z ) * d + mCameraVirtualTarget.z;
+
+}
+
+//=============================================================================
+// SuperCam::SetCameraShakerData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const ShakeEventData* data )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCam::SetCameraShakerData( const ShakeEventData* data )
+{
+ if ( mShaker )
+ {
+ mShaker->Reset();
+ mShaker->SetShakeData( data );
+ }
+}
+
+//=============================================================================
+// SuperCam::GetWatcherName
+//=============================================================================
+// Description: char for identifying this object in the watcher
+//
+// Parameters: NONE
+//
+// Return: void
+//
+//=============================================================================
+#ifdef DEBUGWATCH
+const char* SuperCam::GetWatcherName() const
+{
+ return "No Name";
+}
+
+void SuperCam::PrintClassName() const
+{
+ const char* watcherName = GetWatcherName();
+ rDebugPrintf( "Camera - %s\n", watcherName );
+}
+#endif //DEBUGWATCH \ No newline at end of file
diff --git a/game/code/camera/supercam.h b/game/code/camera/supercam.h
new file mode 100644
index 0000000..6bf90c9
--- /dev/null
+++ b/game/code/camera/supercam.h
@@ -0,0 +1,958 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supercam.h
+//
+// Description: This is the base class of all SuperCams
+//
+// History: 03/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef SUPERCAM_H
+#define SUPERCAM_H
+
+//Uncomment this for supercam debugging.
+#ifndef RAD_RELEASE
+#define SUPERCAM_DEBUG
+#endif
+
+//========================================
+// Nested Includes
+//========================================
+//These are included in the precompiled headers on XBOX and GC
+#include <raddebug.hpp>
+
+#ifndef WORLD_BUILDER
+//This is the non tool includes.
+#include <p3d/refcounted.hpp>
+#include <radmath/radmath.hpp>
+#include <camera/SineCosShaker.h>
+
+#include <debug/profiler.h>
+
+#include <events/eventdata.h>
+#else
+//The tools need a different include style.
+#include <p3d/refcounted.hpp>
+#include <radmath/radmath.hpp>
+#include "sinecosshaker.h"
+#include "events/eventdata.h"
+#endif
+
+
+
+//========================================
+// Forward References
+//========================================
+
+class tPointCamera;
+class tCamera;
+class SuperCamCentral;
+class ISuperCamTarget;
+
+class ICameraShaker;
+
+class SuperCamController;
+
+//========================================
+// Constants
+//========================================
+
+#define CLAMP_TO_ONE(x) if ((x) > 1.0f) { (x)=1.0f; } else if ((x)< 0.0f) { (x)=0.0f; }
+
+//=============================================================================
+//
+// Synopsis: This is the base class of all SuperCams
+//
+//=============================================================================
+
+class SuperCam : public tRefCounted
+{
+public:
+
+ //These are the types of SuperCamera that are available. When a new one is
+ //created it is registered in this enum. If this gets to be larger than
+ //32 types, we'll have to change some things.
+ typedef enum Type
+ {
+ DEFAULT_CAM,
+ FOLLOW_CAM,
+ NEAR_FOLLOW_CAM,
+ FAR_FOLLOW_CAM,
+ BUMPER_CAM,
+ CHASE_CAM,
+ DEBUG_CAM,
+ WRECKLESS_CAM,
+ WALKER_CAM,
+ COMEDY_CAM,
+ KULL_CAM,
+ TRACKER_CAM,
+ SPLINE_CAM, //Deprecated
+ RAIL_CAM,
+ CONVERSATION_CAM,
+ STATIC_CAM,
+ BURNOUT_CAM,
+ REVERSE_CAM,
+ SNAPSHOT_CAM,
+ SURVEILLANCE_CAM,
+ ANIMATED_CAM,
+ RELATIVE_ANIMATED_CAM,
+ SUPER_SPRINT_CAM,
+ FIRST_PERSON_CAM,
+#ifdef RAD_WIN32
+ ON_FOOT_CAM,
+ PC_CAM,
+#endif
+
+ //Add cameras before here!
+ NUM_TYPES,
+ INVALID = NUM_TYPES
+ };
+
+ SuperCam();
+ virtual ~SuperCam();
+
+ //Init... This gets called when the camera is registered
+ void Init();
+
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds ) = 0;
+ virtual void UpdateForPhysics( unsigned int milliseconds ) {};
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const = 0;
+
+ virtual Type GetType() = 0;
+
+ //This loads the off-line created settings for the camera.
+ //It is passed in as a byte stream of some data of known size.
+ virtual void LoadSettings( unsigned char* settings ) {};
+
+ //These are for favourable support of this command
+ virtual void SetTarget( ISuperCamTarget* target ) {};
+ virtual void AddTarget( ISuperCamTarget* target ) {};
+
+ virtual unsigned int GetNumTargets() const { return 0; };
+
+ //Override this if you want physics updates.
+ virtual void SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset ) {};
+ virtual float GetCollisionRadius() const { return 2.0f; };
+
+ virtual float GetIntersectionRadius() const { return 1.0f; };
+
+ //These functions are to allow real-time control of the settings of
+ //the supercam.
+ void RegisterDebugControls();
+ void UnregisterDebugControls();
+
+ //Questions other objects can ask of the SuperCam;
+ //Note: Due to the camera shaking, this is how all children of
+ //SuperCam must access the mCamera values.
+ void GetPosition( rmt::Vector* position ) const;
+ void GetTarget( rmt::Vector* target ) const;
+ void GetHeading( rmt::Vector* heading ) const;
+ void GetHeadingNormalized( rmt::Vector* heading );
+ void GetVelocity( rmt::Vector* velocity ) const;
+ void GetCameraUp( rmt::Vector* up ) const;
+ tPointCamera* const GetCamera() const;
+
+ //These will override any chnages to the camera if they are set directly
+ //on the tCamera. They are applied to the tCamera when the SuperCam is
+ //updated.
+ void SetFOV( float FOVinRadians );
+ float GetFOV() const; //Returns value in degrees
+ void SetAspect( float aspect );
+ float GetAspect() const;
+ void SetNearPlane( float nearPlane );
+ float GetNearPlane() const;
+ void SetFarPlane( float farPlane );
+ float GetFarPlane() const;
+
+ virtual void EnableShake();
+ virtual void DisableShake();
+
+ virtual void LookBack( bool lookBack );
+
+ virtual void DoFirstTime();
+ virtual void DoCameraCut();
+ virtual void DoCameraTransition( bool quick, unsigned int timems = 7000 );
+
+ bool IsRegistered() const;
+
+ void SetTransitionPositionRate( float rate );
+ void SetTransitionTargetRate( float rate );
+
+ //This is for debug rendering.
+ void Display() const;
+
+ static void SetSecondaryControllerID( unsigned int controllerID );
+
+ void SetTwist( float twist );
+
+ //For Wreckless Effect
+ void EnableWrecklessZoom( bool enable );
+
+ inline void AllowShake(void);
+
+ #ifdef DEBUGWATCH
+ virtual const char* GetWatcherName() const;
+ void PrintClassName() const;
+ #endif
+
+protected:
+
+ //Go ahead and make flags specific to the subclass, just make sure there's
+ //no overlap.
+ enum Flag
+ {
+ FIRST_TIME, //First time into this supercam (cleared after use)
+ CUT, //Do a camera cut to the default settings of this cam (cleared after use)
+ START_TRANSITION, //
+ TRANSITION, //This is a blended transition for target and position.
+ END_TRANSITION,
+ QUICK_TRANSITION, //This does the cubic interpolation on both pos and targ.
+ LOOK_BACK, //Lookback requested (cleared after use)
+ MULTIPLE_TARGET, //There are multiple targets available
+ START_SHAKE, //This says we want to start shaking, (cleared after use)
+ SHAKE, //The camera is shaking!
+ END_SHAKE, //The shake is coming to an end. (cleared after use)
+ OVERRIDE_FOV, //This allows temporary overriding of FOV over time.
+ IS_REGISTERED,
+ DIRTY_HEADING, //This is for the normalized heading calculations...
+ EASE_IN,
+ WRECKLESS_ZOOM, //This is the wreckless-style zoom effect
+ SUPERCAM_END
+ };
+
+ //Override this if you want to get initted.
+ virtual void OnInit() {};
+ virtual void OnShutdown() {};
+
+ //You'll need to overload these if you want debug watcher or other debug controls.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+ //Call this when you want to update the camera. Sets VUP and test for
+ //bad camera settings.
+ void SetCameraValues( unsigned int milliseconds,
+ rmt::Vector pos,
+ rmt::Vector targ,
+ const rmt::Vector* vup = 0 );
+
+ void SetFOVOverride( float newFOV );
+ void OverrideFOV( bool enable, float time = 0.0f, float rate = 0.0f );
+ void DisableOverride();
+
+ void SetRegistered( bool isRegistered );
+
+ virtual bool CanSwitch();
+ virtual float GetTargetSpeedModifier() { return 1.0f; };
+
+ friend class SuperCamCentral;
+
+#ifdef WORLD_BUILDER
+ friend class GameEngine;
+ friend class RailCamLocatorNode;
+ friend class StaticCameraLocatorNode;
+#endif
+
+ void SetCamera( tPointCamera* cam );
+ inline void SetFlag( Flag flag, bool value );
+ bool GetFlag( Flag flag ) const;
+ void SetPlayerID( int id );
+
+ //Motion Cubic
+ void MotionCubic( float* Position,
+ float* Rate,
+ float DesiredPosition,
+ float Interval ) const;
+
+ void SphericalMotion( const rmt::Vector& target,
+ rmt::Vector& currentPos,
+ const rmt::Vector& desiredPos,
+ rmt::Vector& desiredPosDelta,
+ const float rate ) const;
+
+ float EaseMotion( float t, float a, float b );
+
+ //Camera shaking stuff
+ inline void SetShaker( ICameraShaker* shaker );
+ void SetCameraShakerData( const ShakeEventData* data );
+
+ int GetPlayerID() const;
+
+ int ClampAngle( float* angle ) const;
+ void AdjustAngles( float* desiredAngle,
+ float* currentAngle,
+ float* currentAngleDelta ) const;
+
+ rmt::Vector UpdateVUP( rmt::Vector position, rmt::Vector target );
+
+ void Shutdown();
+ void CorrectDist( const rmt::Vector& pos, rmt::Vector& targ );
+
+ //For display in the children.
+ virtual void OnDisplay() const {};
+
+ void InitMyController( int controllerID = -1 );
+ void ShutDownMyController();
+
+ tPointCamera* GetCameraNonConst();
+
+ void FilterFov( float zoom,
+ float min,
+ float max,
+ float& curFOV,
+ float& delta,
+ float lag,
+ float timeMod,
+ float offset = 0.0f );
+
+ void ResetTwistDelta() { mTwistDelta = 0.0f; };
+
+ void DoWrecklessZoom( float distance, float minDist, float maxDist,
+ float minFOV, float maxFOV, float& fov,
+ float& fovDelta, float lag, float time );
+
+ void EndTransition( bool abort = false ); //Use caution.
+
+
+ //Tell me when we're first running.
+ unsigned int mFlags;
+
+ //These are shared, default shakers.
+ SineCosShaker mSineCosShaker;
+
+ SuperCamController* mController;
+ int mControllerHandle;
+
+ static unsigned int s_secondaryControllerID;
+
+ tPointCamera* mCamera;
+private:
+ float mSCFOV; //This is in RADIANS
+ float mSCAspect;
+ float mSCNearPlane;
+ float mSCFarPlane;
+
+ //We do some fakery to the camera to hide the camera shaking it.
+ //tPointCamera* mCamera;
+ rmt::Vector mVelocity;
+
+ //This is the position that the camera thinks it's in, while shaking.
+ rmt::Vector mCameraVirtualPosition;
+ rmt::Vector mCameraVirtualTarget;
+ float mCameraVirtualFOV;
+
+ //These are used with the virtual position to do transitions.
+ rmt::Vector mCameraVirtualPositionDelta;
+ rmt::Vector mCameraVirtualTargetDelta;
+ float mCameraVirtualFOVDelta;
+ unsigned int mTransitionTime;
+ unsigned int mTransitionTimeLimit;
+ float mTransitionPositionRate;
+ float mTransitionTargetRate;
+ float mFOVTransitionRate;
+
+ ICameraShaker* mShaker;
+
+ int mPlayerID;
+
+ float mSCBackupFOV;
+
+ rmt::Vector mNormalizedHeading;
+
+ float mSCTwist;
+ float mOldTwist;
+ float mTwistDelta;
+ float mTwistLag;
+
+ void TestCameraMatrix();
+ void SetupShake();
+ void EndShake();
+ void SetupTransition( bool swap = false );
+ void ShakeCamera( rmt::Vector* pos, rmt::Vector* targ, unsigned int milliseconds );
+ void TransitionCamera( float timeLeftPct, rmt::Vector* pos, rmt::Vector* targ, float* fov, unsigned int milliseconds, bool quick = false );
+ void TransitionFOV( float timeLeftPct, float* fov, unsigned int milliseconds );
+ void EaseIn( float timeLeftPct, rmt::Vector* pos, rmt::Vector* targ, unsigned int milliseconds );
+
+ //Prevent wasteful constructor creation.
+ SuperCam( const SuperCam& supercam );
+ SuperCam& operator=( const SuperCam& supercam );
+
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperCam::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::Init()
+{
+ const int NO_CLEAR_FLAGS = ( 1 << IS_REGISTERED );
+ mFlags &= NO_CLEAR_FLAGS;
+
+ OnInit();
+}
+
+//=============================================================================
+// SuperCam::SetTwist
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float twist )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::SetTwist( float twist )
+{
+ mSCTwist = twist;
+}
+
+//*****************************************************************************
+//
+// Inline Protected Member Functions
+//
+//*****************************************************************************
+//=============================================================================
+// SuperCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::OnRegisterDebugControls()
+{
+}
+
+//=============================================================================
+// SuperCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::OnUnregisterDebugControls()
+{
+}
+//=============================================================================
+// SuperCam::SetFlag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Flag flag, bool value )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::SetFlag( Flag flag, bool value )
+{
+ if ( value )
+ {
+ mFlags |= ( 1 << flag );
+ }
+ else
+ {
+ mFlags &= ~( 1 << flag );
+ }
+}
+//=============================================================================
+// SuperCam::SetRegistered
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool isRegistered )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::SetRegistered( bool isRegistered )
+{
+ SetFlag( (Flag)IS_REGISTERED, isRegistered );
+}
+
+//=============================================================================
+// SuperCam::CanSwitch
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool SuperCam::CanSwitch()
+{
+ return true;
+}
+//=============================================================================
+// SuperCam::GetFlag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Flag flag )
+//
+// Return: bool
+//
+//=============================================================================
+inline bool SuperCam::GetFlag( Flag flag ) const
+{
+ return ( (mFlags & ( 1 << flag )) > 0 );
+}
+
+//=============================================================================
+// SuperCam::MotionCubic
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float* Position,
+// float* Rate,
+// float DesiredPosition,
+// float Interval)
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::MotionCubic( float* Position,
+ float* Rate,
+ float DesiredPosition,
+ float Interval ) const
+{
+ //Note: If you are using this function and the camera shake, make
+ //sure to reset the rate to the position after each camera shake.
+ //Otherwise you'll get funny interpolation.
+#ifdef PROFILER_ENABLED
+ char sCname[256];
+ sprintf( sCname, "SC: %d MotionCubic", mPlayerID );
+ BEGIN_PROFILE( sCname )
+#endif
+
+ float a, b, c, d, Interval_Cube, Interval_Square;
+
+ Interval_Square = Interval * Interval;
+ Interval_Cube = Interval_Square * Interval;
+
+ a = *Rate + ((*Position - DesiredPosition) * 2.0f );
+ b = ((DesiredPosition - *Position) * 3.0f) - (*Rate * 2.0f);
+ c = *Rate;
+ d = *Position;
+
+ *Position = (a * Interval_Cube) + (b * Interval_Square) + (c * Interval) + d;
+ *Rate = (a * Interval_Square * 3.0f) + (b * Interval * 2.0f) + c;
+
+#ifdef PROFILER_ENABLED
+ END_PROFILE( sCname )
+#endif
+
+}
+
+//=============================================================================
+// SuperCam::SetShaker
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ICameraShaker* shaker )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::SetShaker( ICameraShaker* shaker )
+{
+ mShaker = shaker;
+}
+
+inline void SuperCam::AllowShake(void) { SetShaker(&mSineCosShaker); }
+
+//=============================================================================
+// SuperCam::FilterFov
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float zoom, float min, float max, float& curFOV, float& delta, float lag, float timeMod, float offset )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::FilterFov( float zoom, float min, float max, float& curFOV, float& delta, float lag, float timeMod, float offset )
+{
+ float diffFOV = max - min;
+
+ //The closer we get to the max speed, the wider the FOV.
+ float desiredFOV = max - (diffFOV * zoom);
+ desiredFOV -= offset;
+ if ( desiredFOV < min )
+ {
+ desiredFOV = min;
+ }
+
+ float fovLag = lag * timeMod;
+ CLAMP_TO_ONE(fovLag);
+
+ MotionCubic( &curFOV, &delta, desiredFOV, fovLag );
+}
+
+//=============================================================================
+// SuperCam::DoWrecklessZoom
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float distance, float minDist, float maxDist, float minFOV,
+// float maxFOV, float& fov, float& fovDelta, float lag,
+// float time )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::DoWrecklessZoom( float distance, float minDist, float maxDist,
+ float minFOV, float maxFOV, float& fov,
+ float& fovDelta, float lag, float time )
+{
+ float percent = (distance - minDist) / (maxDist - minDist);
+ CLAMP_TO_ONE( percent );
+
+ float desiredFOV = maxFOV - ((maxFOV - minFOV) * percent);
+
+ float fovlag = lag * time;
+ CLAMP_TO_ONE(fovlag);
+
+ MotionCubic( &fov, &fovDelta, desiredFOV, fovlag );
+}
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperCam::GetVelocity
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* velocity )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::GetVelocity( rmt::Vector* velocity ) const
+{
+ *velocity = mVelocity;
+}
+
+//=============================================================================
+// SuperCam::GetCamera
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: tPointCamera*
+//
+//=============================================================================
+inline tPointCamera* const SuperCam::GetCamera() const
+{
+ return mCamera;
+}
+
+//=============================================================================
+// SuperCam::SetAspect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float aspect )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::SetAspect( float aspect )
+{
+ mSCAspect = aspect;
+}
+
+//=============================================================================
+// SuperCam::GetAspect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float SuperCam::GetAspect() const
+{
+ return mSCAspect;
+}
+
+//=============================================================================
+// SuperCam::SetNearPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float nearPlane )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::SetNearPlane( float nearPlane )
+{
+ mSCNearPlane = nearPlane;
+}
+
+//=============================================================================
+// SuperCam::GetNearPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float SuperCam::GetNearPlane() const
+{
+ return mSCNearPlane;
+}
+
+//=============================================================================
+// SuperCam::SetFarPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float farPlane )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::SetFarPlane( float farPlane )
+{
+ mSCFarPlane = farPlane;
+}
+
+//=============================================================================
+// SuperCam::GetFarPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float SuperCam::GetFarPlane() const
+{
+ return mSCFarPlane;
+}
+
+//=============================================================================
+// SuperCam::EnableShake
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::EnableShake()
+{
+ if ( !GetFlag( (Flag)SHAKE ) &&
+ !GetFlag( (Flag)TRANSITION ) &&
+ !GetFlag( (Flag)START_TRANSITION ) &&
+ !GetFlag( (Flag)END_TRANSITION ) )
+ {
+ SetFlag( (Flag)START_SHAKE, true );
+ }
+}
+
+//=============================================================================
+// SuperCam::EnableShake
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool enable )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::DisableShake()
+{
+ if ( GetFlag( (Flag)SHAKE ) )
+ {
+ SetFlag( (Flag)END_SHAKE, true );
+ }
+}
+
+//=============================================================================
+// SuperCam::IsRegistered
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool SuperCam::IsRegistered() const
+{
+ return GetFlag( (Flag)IS_REGISTERED );
+}
+
+//=============================================================================
+// SuperCam::SetTransitionPositionRate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float rate )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::SetTransitionPositionRate( float rate )
+{
+ mTransitionPositionRate = rate;
+}
+
+//=============================================================================
+// SuperCam::SetTransitionTargetRate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float rate )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::SetTransitionTargetRate( float rate )
+{
+ mTransitionTargetRate = rate;
+}
+
+//=============================================================================
+// SuperCam::SetPlayerID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int id )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::SetPlayerID( int id )
+{
+ //rAssertMsg( mPlayerID < 0, "This should only be set once!" );
+ mPlayerID = id;
+}
+
+//=============================================================================
+// SuperCam::GetPlayerID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline int SuperCam::GetPlayerID() const
+{
+ rAssertMsg( mPlayerID >= 0, "This should be set once!" );
+ return mPlayerID;
+}
+
+//=============================================================================
+// SuperCam::LookBack
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool lookBack )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::LookBack( bool lookBack )
+{
+ SetFlag( (Flag)LOOK_BACK, lookBack );
+}
+
+//=============================================================================
+// SuperCam::DoFirstTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::DoFirstTime()
+{
+ SetFlag( (Flag)FIRST_TIME, true );
+}
+
+//=============================================================================
+// SuperCam::DoCameraCut
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::DoCameraCut()
+{
+ SetFlag( (Flag)CUT, true );
+}
+
+//=============================================================================
+// SuperCam::SetSecondaryControllerID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::SetSecondaryControllerID( unsigned int controllerID )
+{
+ s_secondaryControllerID = controllerID;
+}
+
+//=============================================================================
+// SuperCam::EnableWrecklessZoom
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool enable )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCam::EnableWrecklessZoom( bool enable )
+{
+ SetFlag( WRECKLESS_ZOOM, enable );
+}
+
+#endif //SUPERCAM_H
diff --git a/game/code/camera/supercamcentral.cpp b/game/code/camera/supercamcentral.cpp
new file mode 100644
index 0000000..a2ccf2f
--- /dev/null
+++ b/game/code/camera/supercamcentral.cpp
@@ -0,0 +1,2357 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supercamcentral.cpp
+//
+// Description: Implement SuperCamCentral
+//
+// History: 03/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+//These are included in the precompiled headers on XBOX and GC
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <stdlib.h>
+#include <simcollision/collisionobject.hpp>
+#include <simcollision/collisionvolume.hpp>
+#include <simcollision/collisionmanager.hpp>
+#include <simcommon/simstate.hpp>
+#include <p3d/pointcamera.hpp>
+#include <p3d/view.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/SuperCam.h>
+#include <camera/SuperCamCentral.h>
+#include <camera/animatedcam.h>
+#include <camera/conversationcam.h>
+#include <camera/frustrumdrawable.h>
+#include <camera/isupercamtarget.h>
+#include <camera/supercamcontroller.h>
+#include <camera/followcam.h>
+#include <camera/wrecklesscam.h>
+#ifdef RAD_WIN32
+#include <camera/pccam.h>
+#endif
+#include <data/gamedatamanager.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/RenderLayer.h>
+#include <memory/srrmemory.h>
+#include <input/inputmanager.h>
+#include <input/controller.h>
+#include <debug/profiler.h>
+#include <meta/fovlocator.h>
+#include <meta/eventlocator.h>
+#include <worldsim/physicsairef.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <events/eventmanager.h>
+#include <events/eventdata.h>
+
+#include <render/IntersectManager/IntersectManager.h>
+
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+#include <main/game.h>
+
+#include <cheats/cheatinputsystem.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+//#define PRINTCAMERANAMES
+const rmt::Vector DEBUG_NAME_POS(0.8f, 0.8f, 0);
+
+//Debug Camera settings.
+const float DEBUG_CAM_XZ_ANGLE = 0.0f;
+const float DEBUG_CAM_Y_ANGLE = 0.03f;
+const float DEBUG_CAM_DIST = 70.0f;
+const float DEBUG_CAM_FOV = 90.0f;
+const float DEBUG_CAM_ASPECT = 4.0f / 3.0f;
+const float DEBUG_CAM_NEAR = 0.1f;
+const float DEBUG_CAM_FAR = 700.0f;
+
+SuperCam::Type CAMERAS_FOR_DRIVING[] =
+{
+ SuperCam::NEAR_FOLLOW_CAM,
+ SuperCam::FAR_FOLLOW_CAM,
+ SuperCam::BUMPER_CAM,
+
+ //Start of cheat cams
+ SuperCam::COMEDY_CAM,
+ SuperCam::WRECKLESS_CAM,
+ SuperCam::CHASE_CAM,
+ SuperCam::KULL_CAM,
+ SuperCam::TRACKER_CAM,
+ SuperCam::DEBUG_CAM
+};
+
+const int NUM_CAMERAS_FOR_DRIVING = 9;
+
+// this must be less than or equal to NUM_CAMERAS_FOR_DRIVING
+//
+const int NUM_CAMERAS_FOR_DRIVING_WITHOUT_CHEAT = 3;
+
+SuperCam::Type CAMERAS_FOR_WALKING[] =
+{
+ SuperCam::WALKER_CAM,
+#ifdef RAD_WIN32
+ SuperCam::PC_CAM,
+#endif
+ SuperCam::DEBUG_CAM,
+ SuperCam::KULL_CAM
+};
+
+#ifdef RAD_WIN32
+const int NUM_CAMERAS_FOR_WALKING = sizeof(CAMERAS_FOR_WALKING)/sizeof(SuperCam::Type);
+
+// this must be less than or equal to NUM_CAMERAS_FOR_WALKING
+//
+const int NUM_CAMERAS_FOR_WALKING_WITHOUT_CHEAT = 2;
+#else
+const int NUM_CAMERAS_FOR_WALKING = 3;
+
+// this must be less than or equal to NUM_CAMERAS_FOR_WALKING
+//
+const int NUM_CAMERAS_FOR_WALKING_WITHOUT_CHEAT = 1;
+#endif
+
+SuperCam::Type SUPER_SPRINT_CAMERAS[] =
+{
+ SuperCam::NEAR_FOLLOW_CAM,
+ SuperCam::FAR_FOLLOW_CAM,
+ SuperCam::BUMPER_CAM,
+ SuperCam::COMEDY_CAM,
+ SuperCam::WRECKLESS_CAM
+#ifndef RAD_RELEASE
+ ,SuperCam::KULL_CAM
+#endif
+};
+
+const int NUM_SUPERSPRINT_CAMS =
+sizeof( SUPER_SPRINT_CAMERAS ) / sizeof( SUPER_SPRINT_CAMERAS[ 0 ] );
+
+const unsigned int DEFAULT_CAM_SWITCH_DELAY = 0; //milliseconds
+
+unsigned char SuperCamCentral::mTotalSuperCamCentrals = 0;
+const char* SuperCamCentral::CAMERA_INVENTORY_SECTION = "CameraInventorySection";
+FollowCamDataChunk SuperCamCentral::mFollowCamDataChunks[ MAX_DATA_CHUNKS ];
+unsigned int SuperCamCentral::mNumUsedFDC = 0;
+
+
+#ifdef DEBUGWATCH
+
+void ToggleDebugViewCallBack( void* userData )
+{
+ SuperCamCentral* ssc = (SuperCamCentral*)userData;
+
+ ssc->ToggleDebugView();
+}
+
+void ToggleCameraForwardsCallBack( void* userData )
+{
+ SuperCamCentral* ssc = (SuperCamCentral*)userData;
+
+ ssc->ToggleSuperCam( true );
+}
+
+void ToggleCameraBackwardsCallBack( void* userData )
+{
+ SuperCamCentral* ssc = (SuperCamCentral*)userData;
+
+ ssc->ToggleSuperCam( false );
+}
+
+void ToggleBurnoutCallBack( void* userData )
+{
+ static bool gBurnout = false;
+
+ if ( !gBurnout )
+ {
+ GetEventManager()->TriggerEvent( EVENT_BURNOUT );
+ gBurnout = true;
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_BURNOUT_END );
+ gBurnout = false;
+ }
+}
+
+void ToggleCameraCut( void* userData )
+{
+ SuperCamCentral* ssc = (SuperCamCentral*)userData;
+
+ ssc->DoCameraCut();
+}
+#endif
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperCamCentral::SuperCamCentral
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+SuperCamCentral::SuperCamCentral() :
+ mCollisionAreaIndex(-1),
+ mActiveSuperCam( NULL ),
+ mActiveSuperCamIndex( 0 ),
+ mNumRegisteredSuperCams( 0 ),
+ mCamera( NULL ),
+ mTarget( NULL ),
+#ifdef SUPERCAM_DEBUG
+ mDebugXZAngle( 0.0f ),
+ mDebugYAngle( rmt::PI ),
+ mDebugMagnitude( DEBUG_CAM_DIST ),
+#endif
+ mCurrentFOVLocator( NULL ),
+ mDoCameraCut( false ),
+ mChanceToBurnout( 10 ),
+ mCameraSimState( NULL ),
+ mCameraCollisionFudge( 1.05f ),
+ mController( NULL ),
+ mControllerHandle( -1 ),
+ mWrecklessCount( 0 ),
+ mPreferredFollowCam( FollowCam::FAR_FOLLOW_CAM ),
+ mInitialCamera(false),
+ mIsInvertedCameraEnabled( false ),
+ mJumpCamsEnabled( true ),
+ mCameraToggling( false ),
+ mNastyHypeCamHackEnabled( false )
+{
+MEMTRACK_PUSH_GROUP( "SuperCamCentral" );
+ //Each SuperCamCentral is uniquely numbered.
+ //These numbers correspond to players.
+ mMyNumber = mTotalSuperCamCentrals;
+ ++mTotalSuperCamCentrals;
+
+ unsigned int i;
+
+ for ( i = 0; i < MAX_CAMERAS; ++i )
+ {
+ mSuperCameras[ i ] = NULL;
+ }
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+
+#ifdef SUPERCAM_DEBUG
+ mDebugCamera = new tPointCamera();
+ mDebugCamera->AddRef();
+
+ char camName[256];
+ sprintf( camName, "SuperCamCentral %d DebugCam", mMyNumber );
+ mDebugCamera->SetName( camName );
+
+ mFrustrumDrawable = new FrustrumDrawable();
+ mFrustrumDrawable->AddRef();
+
+ char frustName[256];
+ sprintf( frustName, "FrustrumDrawable%d", mMyNumber );
+ mFrustrumDrawable->SetName( frustName );
+#endif
+
+ GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_END );
+ GetEventManager()->AddListener( this, EVENT_BURNOUT );
+ GetEventManager()->AddListener( this, EVENT_BURNOUT_END );
+
+ GetEventManager()->AddListener( this, EVENT_CAMERA_SHAKE );
+
+ GetGameDataManager()->RegisterGameData( this, 1, "Super Cam Central" );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+#ifdef DEBUGWATCH
+ char player[256];
+ sprintf( player, "SuperCamCentral\\Player%d", mMyNumber );
+ radDbgWatchAddFunction( "Toggle Debug View", &ToggleDebugViewCallBack, this, player );
+ radDbgWatchAddFunction( "Toggle Cameras Forwards", &ToggleCameraForwardsCallBack, this, player );
+ radDbgWatchAddFunction( "Toggle Cameras Backwards", &ToggleCameraBackwardsCallBack, this, player );
+ radDbgWatchAddFunction( "Toggle Burnout Test", &ToggleBurnoutCallBack, this, player );
+ radDbgWatchAddFunction( "Toggle Camera Cut", &ToggleCameraCut, this, player );
+
+ radDbgWatchAddFloat( &mDebugXZAngle, "Debug Camera XZ Angle", player, NULL, NULL, 0.0f, rmt::PI_2 );
+ radDbgWatchAddFloat( &mDebugYAngle, "Debug Camera Y Angle", player, NULL, NULL, 0.001f, rmt::PI_2 );
+ radDbgWatchAddFloat( &mDebugMagnitude, "Debug Camera Distance", player, NULL, NULL, 10.0f, 1000.0f );
+ radDbgWatchAddUnsignedInt( &mChanceToBurnout, "Burnout Cam Chance", player, NULL, NULL, 1, 100 );
+ radDbgWatchAddFloat( &mCameraCollisionFudge, "Camera Collision Fudge", player, NULL, NULL, 0.01f, 2.0f );
+#endif
+MEMTRACK_POP_GROUP( "SuperCamCentral" );
+}
+
+//=============================================================================
+// SuperCamCentral::~SuperCamCentral
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+SuperCamCentral::~SuperCamCentral()
+{
+ const bool shutdown = true;
+ Init( shutdown ); //Clear everything out.
+
+#ifdef SUPERCAM_DEBUG
+ if ( mDebugCamera )
+ {
+ mDebugCamera->Release();
+ mDebugCamera = NULL;
+ }
+
+ if ( mFrustrumDrawable )
+ {
+ mFrustrumDrawable->Release();
+ mFrustrumDrawable = NULL;
+ }
+#endif
+
+ if ( mCurrentFOVLocator )
+ {
+ mCurrentFOVLocator->Release();
+ mCurrentFOVLocator = NULL;
+ }
+
+ GetEventManager()->RemoveAll( this );
+
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( & ToggleDebugViewCallBack );
+ radDbgWatchDelete( & ToggleCameraForwardsCallBack );
+ radDbgWatchDelete( & ToggleCameraBackwardsCallBack );
+ radDbgWatchDelete( & mDebugXZAngle );
+ radDbgWatchDelete( & mDebugYAngle );
+ radDbgWatchDelete( & mDebugMagnitude );
+ radDbgWatchDelete( & mChanceToBurnout );
+ radDbgWatchDelete( & mCameraCollisionFudge );
+#endif
+}
+
+//=============================================================================
+// SuperCamCentral::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool shutdown )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::Init( bool shutdown )
+{
+ // Make sure this is a valid call
+ if( !shutdown && IsInit() || // already initialized
+ shutdown && !IsInit() ) // already shut down
+ {
+ return;
+ }
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ unsigned int i;
+
+ if ( mActiveSuperCam )
+ {
+ mActiveSuperCam->Shutdown();
+ }
+
+ for ( i = 0; i < MAX_CAMERAS; ++i )
+ {
+ if ( mSuperCameras[ i ] )
+ {
+ mSuperCameras[ i ]->UnregisterDebugControls();
+ mSuperCameras[ i ]->Release();
+ }
+
+ mSuperCameras[ i ] = NULL;
+ }
+
+ mActiveSuperCam = NULL;
+ mActiveSuperCamIndex = 0;
+
+ if ( mCurrentFOVLocator )
+ {
+ mCurrentFOVLocator->Release();
+ mCurrentFOVLocator = NULL;
+ }
+
+ if ( mCamera )
+ {
+ mCamera->Release();
+ }
+ mCamera = NULL;
+
+ mTarget = NULL;
+
+#ifdef SUPERCAM_DEBUG
+ mDebugViewOn = false;
+ mDebugCamera->SetFOV( rmt::DegToRadian( DEBUG_CAM_FOV ), DEBUG_CAM_ASPECT );
+ mDebugCamera->SetNearPlane( DEBUG_CAM_NEAR );
+ mDebugCamera->SetFarPlane( DEBUG_CAM_FAR );
+#endif
+
+ mIntersectionList.Clear();
+
+ if ( !shutdown )
+ {
+ // find mCollisionAreaIndex we can use:
+ mCollisionAreaIndex = GetWorldPhysicsManager()->GetCameraCollisionAreaIndex();
+ rAssert(mCollisionAreaIndex != -1);
+
+
+ // probably a good place to make the camera collision sphere
+
+ const rmt::Vector p(0.0f, 0.0f,0.0f);
+ const float sphereRadius = 2.0f; // TODO - what should this be.
+
+ mCameraCollisionVolume = new sim::SphereVolume(p, sphereRadius);
+ mCameraCollisionVolume->AddRef();
+
+ //mCameraCollisionObject = new(GMA_PERSISTENT)sim::CollisionObject(mCameraCollisionVolume);
+ //mCameraCollisionObject->AddRef();
+
+ mCameraSimState = (sim::ManualSimState*)sim::SimState::CreateManualSimState(mCameraCollisionVolume);
+ mCameraSimState->AddRef();
+
+ mCameraCollisionObject = mCameraSimState->GetCollisionObject();
+ mCameraCollisionObject->AddRef();
+
+ mCameraCollisionObject->SetManualUpdate(true);
+ mCameraCollisionObject->SetAutoPair(true);
+ mCameraCollisionObject->SetIsStatic(false); // hmmmmmm....
+
+ mCameraCollisionObject->SetCollisionEnabled(true);
+
+ char buffy[128];
+ sprintf(buffy, "camera_with_collision_area_index_%d", mCollisionAreaIndex);
+ mCameraCollisionObject->SetName(buffy);
+
+ mCameraSimState->mAIRefIndex = PhysicsAIRef::CameraSphere;
+ mCameraSimState->mAIRefPointer = (void*)this;
+
+
+ GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(mCameraCollisionObject, mCollisionAreaIndex);
+
+ mCameraCollisionCount = 0;
+ mCameraTerrainCollisionOffsetFix.Set(0.0f, 0.0f, 0.0f);
+
+ mController = new SuperCamController();
+ rAssert( mController );
+ mController->AddRef();
+
+ InputManager* im = InputManager::GetInstance();
+ rAssert( im );
+
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( mMyNumber );
+ if( controllerID != -1 )
+ {
+ mControllerHandle = im->RegisterMappable( controllerID, mController );
+
+ if( mMyNumber == 0 )
+ {
+ const int NUM_CONTROLLERS = Input::MaxControllers;
+
+ // set secondary SuperCam controller to be = (primary + 1),
+ // and wrap back to controller 0
+ //
+ int secondaryControllerID = (controllerID + 1) % NUM_CONTROLLERS;
+ while ( !(GetInputManager()->GetController( secondaryControllerID )->IsConnected()) )
+ {
+ secondaryControllerID = (secondaryControllerID + 1) % NUM_CONTROLLERS;
+ if ( secondaryControllerID == controllerID )
+ {
+ secondaryControllerID = (secondaryControllerID + 1) % NUM_CONTROLLERS;
+ break;
+ }
+ }
+
+ SuperCam::SetSecondaryControllerID( static_cast<unsigned int>( secondaryControllerID ) );
+ }
+ }
+
+ if ( GetGameplayManager()->mIsDemo )
+ {
+ //GetEventManager()->AddListener( this, (EventEnum)(EVENT_LOCATOR + LocatorEvent::CAMERA_CUT) );
+ }
+
+ p3d::inventory->AddSection( CAMERA_INVENTORY_SECTION );
+ }
+ else
+ {
+ // remove from collision manager
+ // empty area?!
+
+ mCameraCollisionVolume->Release();
+ mCameraCollisionObject->Release();
+
+ GetWorldPhysicsManager()->FreeCollisionAreaIndex( mCollisionAreaIndex );
+
+ if ( mController )
+ {
+ InputManager* im = InputManager::GetInstance();
+ rAssert( im );
+
+ if( mControllerHandle != -1 )
+ {
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( mMyNumber );
+
+ if ( controllerID == -1 )
+ {
+ im->UnregisterMappable( mController );
+ }
+ else
+ {
+ im->UnregisterMappable( controllerID, mControllerHandle );
+ }
+ }
+
+ mController->Release();
+ mController = NULL;
+ mControllerHandle = -1;
+ }
+
+ if ( mCameraSimState )
+ {
+ mCameraSimState->Release();
+ mCameraSimState = NULL;
+ }
+
+ p3d::inventory->RemoveSectionElements( tEntity::MakeUID( CAMERA_INVENTORY_SECTION ) );
+ p3d::inventory->DeleteSection( CAMERA_INVENTORY_SECTION );
+ }
+
+ //This is safe for startup or shutdown.
+ CleanupDataChunks();
+
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+}
+
+//=============================================================================
+// SuperCamCentral::IsInit
+//=============================================================================
+// Description: Returns true if cam initialized.
+//
+// Parameters: n/a
+//
+// Return: bool...
+//
+//=============================================================================
+
+bool SuperCamCentral::IsInit() const
+{
+ return mController != NULL;
+}
+
+//=============================================================================
+// SuperCamCentral::UpdateCameraCollisionSpherePosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& p)
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::UpdateCameraCollisionSpherePosition(rmt::Vector& p)
+{
+ mCameraCollisionVolume->mPosition = p;
+
+ //mCameraCollisionObject->Update();
+
+ mCameraCollisionObject->PostManualUpdate();
+}
+
+
+//=============================================================================
+// SuperCamCentral::UpdateCameraCollisionSphereRadius
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float radius)
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::UpdateCameraCollisionSphereRadius(float radius)
+{
+ // this if for Cary
+ //
+ // not sure how he wants to use it.
+
+ mCameraCollisionVolume->UpdateRadius(radius);
+
+}
+
+//=============================================================================
+// SuperCamCentral::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds, bool isFirstSubstep )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::Update( unsigned int milliseconds, bool isFirstSubstep )
+{
+#if defined(RAD_XBOX) || defined(RAD_WIN32)
+ if ( mController &&
+ !mCameraToggling &&
+ mController->GetValue( SuperCamController::cameraToggle ) == 1.0f &&
+ AllowCameraToggle() &&
+ GetGameplayManager()->GetGameType() != GameplayManager::GT_SUPERSPRINT &&
+ mActiveSuperCam &&
+#ifdef RAD_WIN32
+ mActiveSuperCam->GetType() != SuperCam::PC_CAM &&
+#endif
+ mActiveSuperCam->GetType() != SuperCam::WALKER_CAM )
+ {
+ ToggleSuperCam( true );
+ mCameraToggling = true;
+ }
+ else if ( mController && mCameraToggling && mController->GetValue( SuperCamController::cameraToggle) < 0.5f )
+ {
+ mCameraToggling = false;
+ }
+#endif
+
+ //Test to see if we should change cameras or not.
+ if ( mNextSuperCam.incoming )
+ {
+ //Let's see if we should switch cameras.
+ if ( mNextSuperCam.nextCamDelay - static_cast<int>(milliseconds) <= 0 || mActiveSuperCam == NULL || mDoCameraCut )
+ {
+ if ( mDoCameraCut )
+ {
+ mNextSuperCam.timems = 0;
+ }
+
+ SetActiveSuperCam( mNextSuperCam.nextSuperCam, mNextSuperCam.flags, mNextSuperCam.timems );
+ }
+ else
+ {
+ mNextSuperCam.nextCamDelay -= milliseconds;
+ }
+ }
+
+ if ( mActiveSuperCam != NULL )
+ {
+
+#ifdef PROFILER_ENABLED
+ char sCCname[256];
+ sprintf( sCCname, "SCC: %d Update", mMyNumber );
+ BEGIN_PROFILE( sCCname )
+#endif
+#if defined(DEBUGMENU) && defined(SUPERCAM_DEBUG)
+ if ( BEGIN_DEBUGINFO_SECTION("Super Cam Debug") )
+ {
+ DEBUGINFO_ADDLINE( rmt::Vector( 0,0,0 ), rmt::Vector(10.0f,0,0), tColour( 255, 0, 0 ) );
+ DEBUGINFO_ADDLINE( rmt::Vector( 0,0,0 ), rmt::Vector(0,10.0f,0), tColour( 0, 255, 0 ) );
+ DEBUGINFO_ADDLINE( rmt::Vector( 0,0,0 ), rmt::Vector(0,0,10.0f), tColour( 0, 0, 255 ) );
+ }
+#endif
+
+ //Do the reverse cam thing if we're using the followcam
+ if ( ( mActiveSuperCam->GetType() == SuperCam::NEAR_FOLLOW_CAM ||
+ mActiveSuperCam->GetType() == SuperCam::FAR_FOLLOW_CAM ) &&
+ static_cast<FollowCam*>(mActiveSuperCam)->ShouldReverse() )
+ {
+ if ( !mNextSuperCam.incoming || mSuperCameras[mNextSuperCam.nextSuperCam]->GetType() != SuperCam::REVERSE_CAM )
+ {
+ SelectSuperCam( SuperCam::REVERSE_CAM, FORCE | NO_TRANS, 0 );
+ }
+ }
+ else if ( mActiveSuperCam->GetType() == SuperCam::REVERSE_CAM && mActiveSuperCam->CanSwitch() )
+ {
+ if ( !mNextSuperCam.incoming ||
+ ( mSuperCameras[mNextSuperCam.nextSuperCam]->GetType() != SuperCam::NEAR_FOLLOW_CAM &&
+ mSuperCameras[mNextSuperCam.nextSuperCam]->GetType() != SuperCam::FAR_FOLLOW_CAM ) )
+ {
+ SelectSuperCam( SuperCam::FOLLOW_CAM, FORCE | NO_TRANS, 0 );
+ }
+ }
+
+ if ( mDoCameraCut )
+ {
+ mActiveSuperCam->DoCameraCut();
+ mDoCameraCut = false;
+ }
+
+ if ( mController )
+ {
+ //Test for lookback.
+ float lookBack = mController->GetValue( SuperCamController::lookBack );
+
+#if defined(RAD_GAMECUBE) || defined(RAD_XBOX) //Both now!
+ float altLookBack = mController->GetValue( SuperCamController::altLookBack );
+
+ lookBack = mController->GetAxisValue( SuperCamController::stickY );
+#if defined(RAD_XBOX)
+ lookBack = lookBack < -0.8f ? -1.0f : 0.0f;
+#endif
+
+ if ( altLookBack == 1.0f || lookBack == -1.0f )
+#elif defined(RAD_WIN32)
+ if( lookBack > 0.8f )
+#else //This is PS2
+ if ( mController->GetValue( SuperCamController::l2) >= 0.9f && mController->GetValue( SuperCamController::r2 ) >= 0.9f )
+ {
+ lookBack = 1.0f;
+ }
+
+ if ( rmt::Fabs( lookBack ) > STICK_DEAD_ZONE )
+#endif
+ {
+ //This will be reset at end of use.
+ mActiveSuperCam->LookBack( true );
+ }
+ }
+
+#ifdef PROFILER_ENABLED
+ char sCname[256];
+ sprintf( sCname, "SC: %s #%d Update", mActiveSuperCam->GetName(), mMyNumber );
+ BEGIN_PROFILE( sCname )
+#endif
+
+ if ( isFirstSubstep )
+ {
+ mIntersectionList.Clear();
+
+ rmt::Vector targetPos;
+ mTarget->GetPosition( &targetPos );
+ mIntersectionList.FillIntersectionListStatics( targetPos, mActiveSuperCam->GetIntersectionRadius() );
+ if ( mTarget->IsCar() )
+ {
+ Vehicle* playerCar = GetGameplayManager()->GetCurrentVehicle();
+ mIntersectionList.FillIntersectionListDynamics( targetPos, mActiveSuperCam->GetIntersectionRadius(), false, playerCar );
+ }
+ else
+ {
+ Character* playerCharacter = GetCharacterManager()->GetCharacter( 0 );
+ mIntersectionList.FillIntersectionListDynamics( targetPos, mActiveSuperCam->GetIntersectionRadius(), false, playerCharacter );
+ mIntersectionList.FillIntersectionListAnimPhys( targetPos, mActiveSuperCam->GetIntersectionRadius() );
+ }
+ }
+
+ //Update the currently active supercam.
+ #ifdef DEBUGWATCH
+ #ifdef PRINTCAMERANAMES
+ mActiveSuperCam->PrintClassName();
+ #endif
+ #endif
+ mActiveSuperCam->Update( milliseconds );
+
+#ifdef PROFILER_ENABLED
+ END_PROFILE( sCname )
+#endif
+
+#if defined(DEBUGMENU) && defined(SUPERCAM_DEBUG)
+ //This displays the name of the active SuperCam
+ DEBUGINFO_ADDSCREENTEXTVECTOR( mActiveSuperCam->GetName(), DEBUG_NAME_POS )
+ END_DEBUGINFO_SECTION;
+#endif
+#ifdef PROFILER_ENABLED
+ END_PROFILE( sCCname )
+#endif
+
+ //Hmmm Must think about this. This could be a virtual position and
+ //not really where the camera is.
+ rmt::Vector pos;
+ mActiveSuperCam->GetPosition(&pos);
+
+ if ( mActiveSuperCam->GetType() == SuperCam::WALKER_CAM ||
+ mActiveSuperCam->GetType() == SuperCam::FIRST_PERSON_CAM )
+ {
+ rmt::Vector heading;
+ mActiveSuperCam->GetHeading( &heading );
+ heading.NormalizeSafe();
+ heading.Scale( mActiveSuperCam->GetCollisionRadius() );
+
+ pos.Add( heading );
+ }
+
+ UpdateCameraCollisionSpherePosition(pos);
+ UpdateCameraCollisionSphereRadius( mActiveSuperCam->GetCollisionRadius() );
+ }
+}
+
+
+//=============================================================================
+// SuperCamCentral::PreCollisionPrep
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::PreCollisionPrep()
+{
+ mCameraCollisionCount = 0;
+
+ mCameraTerrainCollisionOffsetFix.Set(0.0f, 0.0f, 0.0f);
+ // new
+ // special vector just for ground collision
+
+ // do get intersects here.
+
+ bool foundTri = false;
+ bool foundPlane = false;
+
+ rmt::Vector closestTriNormal, closestTriPosn;
+ rmt::Vector planeNormal, planePosn;
+ rmt::Vector fudgedCollisionPos = mCameraCollisionVolume->mPosition;
+// fudgedCollisionPos.y -= 1.0f; //Lower it down.
+
+ GetIntersectManager()->FindIntersection(fudgedCollisionPos,
+ foundPlane,
+ planeNormal,
+ planePosn );
+
+
+ // hmmm....
+ // if we get both a tri and a plane, use the one with the more upright normal?
+
+ // for starters, just use plane
+
+ if(foundPlane)
+ {
+ mCameraTerrainCollisionOffsetFix = planeNormal;
+ //mCameraTerrainCollisionOffsetFix.Set(0.0f, 1.0f, 0.0f);
+
+ rmt::Vector uptest(0.0f, 1.0f, 0.0f);
+ rAssert(planeNormal.DotProduct(uptest) > 0.0f);
+
+ float y = planePosn.y;
+ float penetratingDepth = y - (fudgedCollisionPos.y - mCameraCollisionVolume->mSphereRadius);
+
+ if(penetratingDepth > 0.0f)
+ {
+
+ float fixAlongPlaneNormal = penetratingDepth / rmt::Fabs( (planeNormal.DotProduct(uptest))); // the fabs should be redundant here
+
+ mCameraTerrainCollisionOffsetFix.Scale(fixAlongPlaneNormal);
+
+ }
+ else
+ {
+ mCameraTerrainCollisionOffsetFix.Set( 0.0f, 0.0f, 0.0f );
+ }
+
+ }
+}
+
+
+//=============================================================================
+// SuperCamCentral::AddCameraCollisionOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& fixOffset)
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::AddCameraCollisionOffset(rmt::Vector& fixOffset)
+{
+ rAssertMsg( mCameraCollisionCount < MAX_COLLISIONS, "Too many collisions for the camera! This one is ignored! \n" );
+ if ( mCameraCollisionCount >= MAX_COLLISIONS )
+ {
+ return;
+ }
+
+ if ( rmt::Epsilon( fixOffset.MagnitudeSqr(), 0.0000001f ) )
+ {
+ //Too small to care about.
+ return;
+ }
+
+// if ( fixOffset.DotProduct( rmt::Vector( 0.0f, -1.0f, 0.0f ) ) > 0.0f )
+// {
+// //We don't get forced down.
+// return;
+// }
+
+ //Do we want to reject this collision?
+ rmt::Vector normalTestFix = fixOffset;
+ normalTestFix.NormalizeSafe();
+ unsigned int i;
+ for ( i = 0; i < mCameraCollisionCount; ++i )
+ {
+ const float dotTest = 0.7f;
+ float dotResult = normalTestFix.DotProduct( mCameraCollisionOffsetFix[ i ] );
+ if ( dotResult >= dotTest )
+ {
+ //Throw me away please.
+ return;
+ }
+ }
+
+ //Fuge-ma scale
+ fixOffset.Scale( 1.0f + mCameraCollisionFudge );
+
+ if ( mCameraCollisionCount < MAX_COLLISIONS )
+ {
+ mCameraCollisionOffsetFix[ mCameraCollisionCount ] = fixOffset;
+ mCameraCollisionCount++;
+ }
+}
+
+
+//=============================================================================
+// SuperCamCentral::RegisterSuperCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( SuperCam* cam )
+//
+// Return: unsigned int
+//
+//=============================================================================
+unsigned int SuperCamCentral::RegisterSuperCam( SuperCam* cam )
+{
+ rAssert( cam->GetType() < SuperCam::INVALID );
+
+ cam->AddRef();
+
+ cam->SetPlayerID( (int)mMyNumber );
+
+ unsigned int i;
+ bool added = false;
+
+ for ( i = 0; i < MAX_CAMERAS; ++i )
+ {
+ if ( mSuperCameras[ i ] == NULL )
+ {
+ mSuperCameras[ i ] = cam;
+ added = true;
+ break;
+ }
+ }
+
+ rAssert( added );
+
+ if ( mCamera )
+ {
+ cam->SetCamera( mCamera );
+ }
+
+ if ( mTarget )
+ {
+ cam->SetTarget( mTarget );
+ }
+
+ //DO this last in case the init needs to know about the cameras.
+// cam->Init(); //Took out since it was call to often. Cary Jan 29
+
+ // increment number of registered super cams
+ mNumRegisteredSuperCams++;
+
+ cam->SetRegistered( true );
+
+ return i;
+}
+
+//=============================================================================
+// SuperCamCentral::UnregisterSuperCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( SuperCam* cam )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::UnregisterSuperCam( SuperCam* cam )
+{
+ rAssert( cam != NULL );
+ if ( cam == NULL )
+ return;
+
+ rAssert( cam->GetType() < SuperCam::INVALID );
+
+ bool found = false;
+ unsigned int i;
+ for ( i = 0; i < MAX_CAMERAS; ++i )
+ {
+ if ( mSuperCameras[ i ] == cam )
+ {
+ mSuperCameras[ i ]->SetRegistered( false );
+ mSuperCameras[ i ]->UnregisterDebugControls();
+ mSuperCameras[ i ]->Release();
+ mSuperCameras[ i ] = NULL;
+
+ found = true;
+ break;
+ }
+ }
+
+ if ( !found )
+ {
+ return;
+ }
+
+ //If there is no incoming camera and the current cam is the active one, let's choose a new
+ //one intelligently.
+ if ( !mNextSuperCam.incoming && mActiveSuperCam == cam )
+ {
+ //This one is gone now.
+ mActiveSuperCam = NULL;
+
+ unsigned int timems = 7000;
+ int extraFlags = 0;
+ if ( mDoCameraCut )
+ {
+ timems = 0;
+ extraFlags = CUT;
+ }
+
+ //Should do something smart here. How about this...
+ SelectSuperCam( SuperCam::DEFAULT_CAM, FORCE | extraFlags, timems );
+
+ rAssert( mActiveSuperCam );
+ }
+ else if ( mNextSuperCam.incoming && mActiveSuperCam == cam )
+ {
+ SetActiveSuperCam( mNextSuperCam.nextSuperCam, mNextSuperCam.flags, mNextSuperCam.timems );
+ }
+
+ // decrement number of registered super cams
+ mNumRegisteredSuperCams--;
+}
+
+//=============================================================================
+// SuperCamCentral::UnregisterSuperCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int which )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::UnregisterSuperCam( unsigned int which )
+{
+ rAssert( mSuperCameras[ which ] != NULL );
+
+ UnregisterSuperCam( mSuperCameras[ which ] );
+}
+
+//=============================================================================
+// SuperCamCentral::GetNumRegisteredSuperCams
+//=============================================================================
+// Description: Comment
+//
+// Parameters: none
+//
+// Return: number of registered super cams
+//
+//=============================================================================
+unsigned int SuperCamCentral::GetNumRegisteredSuperCams() const
+{
+ return mNumRegisteredSuperCams;
+}
+
+//=============================================================================
+// SuperCamCentral::GetActiveSuperCamIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: none
+//
+// Return: index to active super cam
+//
+//=============================================================================
+unsigned int SuperCamCentral::GetActiveSuperCamIndex() const
+{
+ return mActiveSuperCamIndex;
+}
+
+//=============================================================================
+// SuperCamCentral::GetSuperCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: super cam index
+//
+// Return: reference to specified super cam
+//
+//=============================================================================
+SuperCam* SuperCamCentral::GetSuperCam( unsigned int which ) const
+{
+ rAssert( which < MAX_CAMERAS );
+
+ return mSuperCameras[ which ];
+}
+
+//=============================================================================
+// SuperCamCentral::GetSuperCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: super cam type
+//
+// Return: if found, return reference to super cam; otherwise, return NULL
+//
+//=============================================================================
+SuperCam* SuperCamCentral::GetSuperCam( SuperCam::Type type ) const
+{
+ SuperCam* superCam = NULL;
+
+ for( int i = 0; i < MAX_CAMERAS; i++ )
+ {
+ if( mSuperCameras[ i ] != NULL &&
+ mSuperCameras[ i ]->GetType() == type )
+ {
+ // found it!
+ //
+ superCam = mSuperCameras[ i ];
+
+ break;
+ }
+ }
+
+ return superCam;
+}
+
+//=============================================================================
+// SuperCamCentral::ToggleSuperCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool forwards, bool quickTransition )
+//
+// Return: bool
+//
+//=============================================================================
+bool SuperCamCentral::ToggleSuperCam( bool forwards, bool quickTransition )
+{
+ SuperCam::Type* cameras = NULL;
+ unsigned int numCameras = 0;
+ if ( mTarget->IsCar() )
+ {
+ if ( GetGameplayManager()->IsSuperSprint() )
+ {
+ cameras = SUPER_SPRINT_CAMERAS;
+ numCameras = NUM_SUPERSPRINT_CAMS;
+ }
+ else
+ {
+ cameras = CAMERAS_FOR_DRIVING;
+
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_CAMERAS ) )
+ {
+ numCameras = NUM_CAMERAS_FOR_DRIVING;
+ }
+ else
+ {
+ numCameras = NUM_CAMERAS_FOR_DRIVING_WITHOUT_CHEAT;
+ }
+ }
+
+ }
+ else
+ {
+ cameras = CAMERAS_FOR_WALKING;
+
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_CAMERAS ) )
+ {
+ numCameras = NUM_CAMERAS_FOR_WALKING;
+ }
+ else
+ {
+ numCameras = NUM_CAMERAS_FOR_WALKING_WITHOUT_CHEAT;
+ }
+ }
+
+ unsigned int start = 0;
+
+ unsigned int i;
+ for ( i = 0; i < numCameras; ++i )
+ {
+ if ( mActiveSuperCam && cameras[ i ] == mActiveSuperCam->GetType() )
+ {
+ start = i;
+ break;
+ }
+ }
+
+ unsigned int current;
+
+ if ( forwards )
+ {
+ current = (start+1) % numCameras;
+ }
+ else
+ {
+ current = ((start-1) + numCameras) % numCameras;
+ }
+
+ SelectSuperCam( cameras[ current ], SuperCamCentral::CUT | SuperCamCentral::QUICK, 0 );
+
+ return true;
+}
+
+//=============================================================================
+// SuperCamCentral::SelectSuperCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int which, int flags, unsigned int timems )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::SelectSuperCam( unsigned int which, int flags, unsigned int timems )
+{
+ if ( mNastyHypeCamHackEnabled )
+ {
+ //This is for the frickin' camera on level 6 where the user should never
+ //go, but can and will enevitably fuck the camera system.
+ return;
+ }
+
+ if ( which < MAX_CAMERAS && mSuperCameras[ which ] != NULL )
+ {
+ SuperCam::Type whichType = mSuperCameras[ which ]->GetType();
+
+ if ( !IsLegalType( whichType ) )
+ {
+ //This is a fix to trying to select cams you shouldn't (hack)
+ //You can't switch to a camera that isn't legal.
+ SelectSuperCam( SuperCam::DEFAULT_CAM, flags, timems );
+ return;
+ }
+
+ if( whichType == SuperCam::CONVERSATION_CAM )
+ {
+ ConversationCam* conv = dynamic_cast< ConversationCam* >( mSuperCameras[ which ] );
+ rAssert( conv != NULL );
+ conv->LockCharacterPositions();
+ }
+ else if ( whichType == SuperCam::BUMPER_CAM &&
+ mTarget->IsCar() )
+ {
+ //Always force.
+ flags |= FORCE;
+
+ //No bumper cam for RC Car.
+ Vehicle* target = dynamic_cast<Vehicle*>( mTarget );
+ rAssert( target );
+
+ if ( target->mVehicleID == VehicleEnum::DUNE_V )
+ {
+ SelectSuperCam( SuperCam::NEAR_FOLLOW_CAM, flags, timems );
+ return;
+ }
+ }
+
+ SuperCam::Type activeType;
+ if( mActiveSuperCam != NULL )
+ {
+ activeType = mActiveSuperCam->GetType();
+ }
+
+ if ( mActiveSuperCam == NULL || timems == 0 )
+ {
+ SetActiveSuperCam( which, flags, timems );
+ }
+ else if(
+ ( mSuperCameras[ which ]->GetType() == SuperCam::ANIMATED_CAM ) ||
+ ( mSuperCameras[ which ]->GetType() == SuperCam::RELATIVE_ANIMATED_CAM )
+ )
+ {
+ AnimatedCam* cam = static_cast< AnimatedCam* >( mSuperCameras[ which ] );
+ SuperCam::Type currentType = mSuperCameras[ mActiveSuperCamIndex ]->GetType();
+ cam->SetNextCameraType( currentType );
+ SetActiveSuperCam( which, flags, timems );
+ }
+ else if( mActiveSuperCam && ( activeType == SuperCam::ANIMATED_CAM ) && ( ( flags & FORCE ) == 0x00 ) )
+ {
+ AnimatedCam* cam = dynamic_cast< AnimatedCam* >( GetSuperCam( SuperCam::ANIMATED_CAM ) );
+ rAssert( cam != NULL );
+ cam->SetNextCameraType( mSuperCameras[ which ]->GetType() );
+ }
+ else
+ {
+ //Crap.. Let's eliminate this...
+ /*
+ mNextSuperCam.nextSuperCam = which;
+
+ if ( timems == 0 )
+ {
+ mNextSuperCam.nextCamDelay = 0;
+ }
+ else
+ {
+ mNextSuperCam.nextCamDelay = DEFAULT_CAM_SWITCH_DELAY;
+ }
+
+ mNextSuperCam.flags = flags;
+ mNextSuperCam.timems = timems;
+ mNextSuperCam.incoming = true;
+ */
+
+ SetActiveSuperCam( which, flags, timems );
+ }
+ }
+}
+
+//=============================================================================
+// SuperCamCentral::SelectSuperCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( SuperCam::Type type, int flags, unsigned int timems )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::SelectSuperCam( SuperCam::Type type, int flags, unsigned int timems )
+{
+ if ( type == SuperCam::DEFAULT_CAM )
+ {
+ if( mTarget == NULL )
+ {
+ type = SuperCam::FOLLOW_CAM;
+ }
+ else if ( mTarget->IsCar() )
+ {
+ if ( GetGameplayManager()->mIsDemo )
+ {
+ type = SuperCam::FOLLOW_CAM;
+ }
+ else if ( GetGameplayManager()->IsSuperSprint() )
+ {
+ type = SuperCam::SUPER_SPRINT_CAM;
+ }
+ else
+ {
+ type = SuperCam::FOLLOW_CAM;
+ }
+ }
+ else
+ {
+#ifdef RAD_WIN32
+ type = SuperCam::ON_FOOT_CAM;
+#else
+ type = SuperCam::WALKER_CAM;
+#endif
+ }
+ }
+
+#ifdef RAD_WIN32
+ if ( type == SuperCam::ON_FOOT_CAM )
+ {
+ //Ziemek? Check the PC cam flag here.
+ bool bMouseLookMode = GetInputManager()->GetController(0)->IsMouseLookOn();
+ if ( bMouseLookMode ) //Or PC flag is set
+ {
+ type = SuperCam::PC_CAM;
+ }
+ else
+ {
+ type = SuperCam::WALKER_CAM;
+ }
+ }
+#endif
+
+ if ( type == SuperCam::FOLLOW_CAM )
+ {
+ //Find the preferred type
+ type = mPreferredFollowCam;
+ }
+ else if ( type == SuperCam::NEAR_FOLLOW_CAM || type == SuperCam::FAR_FOLLOW_CAM || type == SuperCam::BUMPER_CAM || type == SuperCam::COMEDY_CAM )
+ {
+ //Set the new preferred type
+ mPreferredFollowCam = type;
+ }
+
+ //If it still ain't legal, make it legal.
+ if ( !IsLegalType( type ) )
+ {
+ type = SuperCam::FAR_FOLLOW_CAM;
+ mPreferredFollowCam = type;
+ }
+
+ unsigned int i;
+ for ( i = 0; i < MAX_CAMERAS; ++i )
+ {
+ if ( mSuperCameras[ i ] != NULL )
+ {
+ if ( mSuperCameras[ i ]->GetType() == type )
+ {
+ SelectSuperCam( i, flags, timems );
+ break;
+ }
+ }
+ }
+}
+
+//=============================================================================
+// SuperCamCentral::SelectSuperCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( SuperCam* cam, int flags = CUT | QUICK, unsigned int timems = 7000 )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::SelectSuperCam( SuperCam* cam, int flags, unsigned int timems )
+{
+ rAssert( cam );
+
+ if ( cam )
+ {
+ unsigned int i;
+ for ( i = 0; i < MAX_CAMERAS; ++i )
+ {
+ if ( mSuperCameras[ i ] != NULL )
+ {
+ if ( mSuperCameras[ i ] == cam )
+ {
+ SelectSuperCam( i, flags, timems );
+ break;
+ }
+ }
+ }
+ }
+}
+
+//=============================================================================
+// SuperCamCentral::SetCamera
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tPointCamera* cam )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::SetCamera( tPointCamera* cam )
+{
+ if ( mCamera )
+ {
+ mCamera->Release();
+ }
+
+ unsigned int i;
+ for ( i = 0; i < MAX_CAMERAS; ++i )
+ {
+ if ( mSuperCameras[ i ] )
+ {
+ mSuperCameras[ i ]->SetCamera( cam );
+ }
+ }
+
+ mCamera = cam;
+ cam->AddRef();
+}
+
+//=============================================================================
+// SuperCamCentral::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::SetTarget( ISuperCamTarget* target )
+{
+ if ( mTarget != target )
+ {
+ unsigned int i;
+ for ( i = 0; i < MAX_CAMERAS; ++i )
+ {
+ if ( mSuperCameras[ i ] )
+ {
+ mSuperCameras[ i ]->SetTarget( target );
+ }
+ }
+
+ //We should do a transition on the active super cam to make sure that
+ //We don't jump around too much.
+ if ( mActiveSuperCam )
+ {
+ mActiveSuperCam->DoCameraTransition( false );
+ }
+
+ mTarget = target;
+ }
+
+}
+
+//=============================================================================
+// SuperCamCentral::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::AddTarget( ISuperCamTarget* target )
+{
+ mActiveSuperCam->AddTarget( target );
+
+ if ( mNextSuperCam.incoming )
+ {
+ rAssert( mNextSuperCam.nextSuperCam < MAX_CAMERAS );
+ rAssert( mSuperCameras[ mNextSuperCam.nextSuperCam ] != NULL );
+ mSuperCameras[ mNextSuperCam.nextSuperCam ]->AddTarget( target );
+ }
+}
+
+//=============================================================================
+// SuperCamCentral::SetActiveSuperCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int which, int flags, unsigned int timems )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::SetActiveSuperCam( unsigned int which, int flags, unsigned int timems )
+{
+ //Clear the incoming.
+ mNextSuperCam.incoming = false;
+
+ if ( !AllowCameraToggle() && (flags & FORCE) == 0 ) //Not forced
+ {
+ return;
+ }
+
+ if ( mSuperCameras[ which ] == mActiveSuperCam )
+ {
+ //This one is already active.
+ if( ( mNextSuperCam.flags & FORCE ) != 0 )
+ {
+ mActiveSuperCam->EndTransition();
+ }
+ if ( (flags & CUT) != 0 )
+ {
+ mActiveSuperCam->DoCameraCut();
+ }
+ return;
+ }
+
+ bool oldWasBumperCam = false;
+
+ if ( mActiveSuperCam != NULL )
+ {
+ mActiveSuperCam->UnregisterDebugControls();
+
+ mActiveSuperCam->Shutdown();
+
+ oldWasBumperCam = mActiveSuperCam->GetType() == SuperCam::BUMPER_CAM;
+ }
+
+ if ( mSuperCameras[ which ] != NULL)
+ {
+ mActiveSuperCam = mSuperCameras[ which ];
+ mActiveSuperCam->RegisterDebugControls();
+
+ GetEventManager()->TriggerEvent( EVENT_CAMERA_CHANGE, mActiveSuperCam );
+ mActiveSuperCamIndex = which;
+
+ //Hack.. TODO: Should this be moved?
+ mActiveSuperCam->SetNearPlane( SUPERCAM_NEAR );
+
+ mActiveSuperCam->Init();
+ mActiveSuperCam->ResetTwistDelta(); //This stops funny twist snap
+
+ if ( mCurrentFOVLocator )
+ {
+ //Enable the FOV override here. There could be some issues with timing, but we'll see.
+ mActiveSuperCam->SetFOVOverride( mCurrentFOVLocator->GetFOV() );
+ mActiveSuperCam->OverrideFOV( true, mCurrentFOVLocator->GetTime(), mCurrentFOVLocator->GetRate() );
+ }
+
+ //Cut the camera view
+ if ( (flags & CUT) != 0 ||
+ oldWasBumperCam ||
+ mActiveSuperCam->GetType() == SuperCam::BUMPER_CAM ) //Auto cut on bumper cam too
+ {
+ mActiveSuperCam->DoCameraCut();
+ }
+ else if ( (flags & NO_TRANS) == 0 )
+ {
+ //Smothly blend the camera views
+ mActiveSuperCam->DoCameraTransition( (flags & QUICK) != 0, timems );
+ }
+
+ mActiveSuperCam->DoFirstTime();
+
+ rDebugString( "NEW SUPER CAM!\n" );
+ }
+}
+
+
+//=============================================================================
+// SuperCamCentral::SubmitStatics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::SubmitStatics()
+{
+ // only do this for certain types of camers:
+ // TODO - add others?
+ SuperCam::Type camType = mActiveSuperCam->GetType();
+ rmt::Vector pos;
+ pos = mCameraCollisionVolume->mPosition;
+
+ if( mActiveSuperCam &&
+ (camType == SuperCam::NEAR_FOLLOW_CAM ||
+ camType == SuperCam::FAR_FOLLOW_CAM ||
+ camType == SuperCam::WALKER_CAM ||
+#ifdef RAD_WIN32
+ camType == SuperCam::PC_CAM ||
+#endif
+ camType == SuperCam::COMEDY_CAM ||
+ camType == SuperCam::FIRST_PERSON_CAM ||
+ camType == SuperCam::REVERSE_CAM)
+ )
+ {
+
+ GetWorldPhysicsManager()->SubmitStaticsPseudoCallback(pos, mActiveSuperCam->GetCollisionRadius() /* need multiplier? */, mCollisionAreaIndex, mCameraSimState);
+ GetWorldPhysicsManager()->SubmitFencePiecesPseudoCallback(pos, mActiveSuperCam->GetCollisionRadius(),
+ mCollisionAreaIndex, mCameraSimState);
+ GetWorldPhysicsManager()->SubmitAnimCollisionsPseudoCallback( pos, mActiveSuperCam->GetCollisionRadius(), mCollisionAreaIndex, mCameraSimState );
+#ifdef RAD_WIN32
+ if ( camType == SuperCam::PC_CAM )
+ {
+ //No dynamics in PC_CAM
+ return;
+ }
+#endif
+ GetWorldPhysicsManager()->SubmitDynamicsPseudoCallback( pos, mActiveSuperCam->GetCollisionRadius(), mCollisionAreaIndex, mCameraSimState );
+ }
+}
+
+//=============================================================================
+// SuperCamCentral::UpdateForPhysics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::UpdateForPhysics( unsigned int milliseconds )
+{
+ if ( mActiveSuperCam )
+ {
+ //Inform the supercam of the physics collision
+
+
+ // hi Cary
+ // at this point you should now also have a
+ // rmt::Vector mCameraTerrainCollisionOffsetFix
+ // that will be something other than 0,0,0 if the camera sphere hit the terrain
+ //
+ // use as you please.
+
+ mActiveSuperCam->SetCollisionOffset( mCameraCollisionOffsetFix, mCameraCollisionCount, mCameraTerrainCollisionOffsetFix );
+ mActiveSuperCam->UpdateForPhysics( milliseconds );
+
+#ifdef SUPERCAM_DEBUG
+ //This is the debug system to allow the game to "see" what the active supercam sees.
+ if ( mDebugViewOn )
+ {
+ //Position the debug cam relative to the actual cam
+ rmt::Vector newPos, camPos;
+ mCamera->GetPosition( &camPos );
+
+ rmt::SphericalToCartesian( mDebugMagnitude, mDebugXZAngle, mDebugYAngle,
+ &newPos.x, &newPos.z, &newPos.y );
+ newPos.Add( camPos );
+ mDebugCamera->SetPosition( newPos );
+ mDebugCamera->SetTarget( camPos );
+#ifdef DEBUGWATCH
+ //Draw the cam's frustrum.
+ DrawFrustrum( );
+#endif
+ }
+#endif
+ }
+}
+
+//=============================================================================
+// SuperCamCentral::AllowCameraToggle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool SuperCamCentral::AllowCameraToggle()
+{
+ if ( mActiveSuperCam == NULL )
+ {
+ return true;
+ }
+
+ //In these modes do not allow the camera to be toggled.
+ SuperCam::Type type = mActiveSuperCam->GetType();
+
+ return !( type == SuperCam::RAIL_CAM || type == SuperCam::STATIC_CAM || type == SuperCam::ANIMATED_CAM );
+}
+
+//=============================================================================
+// SuperCamCentral::AllowAutoCameraChange
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool SuperCamCentral::AllowAutoCameraChange()
+{
+ if ( mActiveSuperCam == NULL )
+ {
+ return true;
+ }
+
+ SuperCam::Type type = mActiveSuperCam->GetType();
+
+ return ( type == SuperCam::FOLLOW_CAM ||
+ type == SuperCam::NEAR_FOLLOW_CAM ||
+ type == SuperCam::FAR_FOLLOW_CAM ||
+ type == SuperCam::WALKER_CAM ||
+#ifdef RAD_WIN32
+ type == SuperCam::PC_CAM ||
+#endif
+ type == SuperCam::COMEDY_CAM ||
+ type == SuperCam::BUMPER_CAM ||
+ type == SuperCam::RAIL_CAM ||
+ type == SuperCam::STATIC_CAM ||
+ type == SuperCam::SPLINE_CAM );
+}
+
+//=============================================================================
+// SuperCamCentral::RegisterFOVLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( FOVLocator* loc )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::RegisterFOVLocator( FOVLocator* loc )
+{
+ rAssertMsg( NULL == mCurrentFOVLocator, "There should never be FOV locators that interpenetrate!" );
+
+ mCurrentFOVLocator = loc;
+ mCurrentFOVLocator->AddRef();
+
+ if ( mActiveSuperCam )
+ {
+ mActiveSuperCam->SetFOVOverride( loc->GetFOV() );
+ mActiveSuperCam->OverrideFOV( true, loc->GetTime(), loc->GetRate() );
+ }
+}
+
+//=============================================================================
+// SuperCamCentral::UnregisterFOVLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::UnregisterFOVLocator()
+{
+ rAssert( mCurrentFOVLocator );
+
+ if ( mActiveSuperCam )
+ {
+ mActiveSuperCam->OverrideFOV( false, mCurrentFOVLocator->GetTime(), mCurrentFOVLocator->GetRate() );
+ }
+
+ mCurrentFOVLocator->Release();
+ mCurrentFOVLocator = NULL;
+}
+
+//=============================================================================
+// SuperCamCentral::NoTransition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::NoTransition()
+{
+ if ( mNextSuperCam.incoming )
+ {
+ mNextSuperCam.flags |= FORCE;
+ }
+ else
+ {
+ if( mActiveSuperCam != NULL )
+ {
+ mActiveSuperCam->EndTransition();
+ }
+ }
+}
+
+//=============================================================================
+// SuperCamCentral::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_ENTER_INTERIOR_END:
+ {
+ DoCameraCut();
+ break;
+ }
+ case EVENT_CAMERA_SHAKE:
+ {
+ ShakeEventData* shakeData = static_cast<ShakeEventData*>(pEventData);
+ if ( shakeData->playerID == mMyNumber && GetGameplayManager()->GetGameType() == GameplayManager::GT_NORMAL )
+ {
+ //Do the shaking.
+ mActiveSuperCam->EnableShake();
+ mActiveSuperCam->SetCameraShakerData( shakeData );
+ }
+ break;
+ }
+ case EVENT_LOCATOR + LocatorEvent::CAMERA_CUT:
+ {
+ EventLocator* evtLoc = static_cast<EventLocator*>(pEventData);
+ if ( evtLoc->GetPlayerID() == mMyNumber )
+ {
+ //Only switch to Wreckless cam in demo mode.
+ if ( GetGameplayManager()->mIsDemo )
+ {
+ if ( evtLoc->GetPlayerEntered() )
+ {
+ SelectSuperCam( SuperCam::WRECKLESS_CAM, CUT | FORCE, 0 ); //0 means do right away.
+
+ //Hackish, trigger the cameras event listener. This is specific to wreckless cams.
+ WrecklessCam* wc = static_cast<WrecklessCam*>(GetSuperCam( SuperCam::WRECKLESS_CAM ));
+ if ( wc )
+ {
+ wc->GetEventListener()->HandleEvent( id, pEventData );
+ }
+
+ ++mWrecklessCount;
+ }
+ else
+ {
+ if ( mWrecklessCount == 1 )
+ {
+ if ( mTarget->IsCar() )
+ {
+ SelectSuperCam( SuperCam::FOLLOW_CAM, CUT | FORCE );
+ }
+ else
+ {
+#ifdef RAD_WIN32
+ SelectSuperCam( SuperCam::ON_FOOT_CAM, CUT | FORCE );
+#else
+ SelectSuperCam( SuperCam::WALKER_CAM, CUT | FORCE );
+#endif
+ }
+ }
+
+ mWrecklessCount--;
+ }
+ }
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+
+
+/* else if ( id == EVENT_BURNOUT )
+ {
+ if ( rand() % mChanceToBurnout == 0 )
+ {
+ SelectSuperCam( SuperCam::BURNOUT_CAM, true, false, true );
+ }
+ }
+ else if ( id == EVENT_BURNOUT_END )
+ {
+ SelectSuperCam( SuperCam::FOLLOW_CAM, false, false, true );
+ }
+*/
+
+}
+
+//=============================================================================
+// SuperCamCentral::ToggleFirstPerson
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int controllerID )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::ToggleFirstPerson( int controllerID )
+{
+ /*
+ if ( controllerID == GetInputManager()->GetControllerIDforPlayer( mMyNumber ) )
+ {
+ if ( mActiveSuperCam && mActiveSuperCam->GetType() == SuperCam::WALKER_CAM )
+ {
+ //Switch to first person cam.
+ SelectSuperCam( SuperCam::FIRST_PERSON_CAM, SuperCamCentral::CUT | SuperCamCentral::FORCE, 0 );
+ GetEventManager()->TriggerEvent(EVENT_TOGGLE_FIRSTPERSON, (void*)true);
+ }
+ else if ( mActiveSuperCam && mActiveSuperCam->GetType() == SuperCam::FIRST_PERSON_CAM )
+ {
+ //Switch to walker cam
+ SelectSuperCam( SuperCam::WALKER_CAM, SuperCamCentral::CUT | SuperCamCentral::FORCE, 0 );
+ GetEventManager()->TriggerEvent(EVENT_TOGGLE_FIRSTPERSON, (void*)false);
+ }
+ }
+ */
+}
+
+//=============================================================================
+// SuperCamCentral::IsCutCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool SuperCamCentral::IsCutCam()
+{
+ return !( mActiveSuperCam != NULL &&
+ mActiveSuperCam->GetType() != SuperCam::STATIC_CAM &&
+ mActiveSuperCam->GetType() != SuperCam::RAIL_CAM );
+}
+
+
+//*****************************************************************************
+// Debug Stuff
+//*****************************************************************************
+
+//=============================================================================
+// SuperCamCentral::ToggleDebugView
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool on )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::ToggleDebugView( bool on )
+{
+#ifdef SUPERCAM_DEBUG
+ rAssert( mCamera );
+ RenderManager* rm = GetRenderManager();
+ RenderLayer* rloutside = rm->mpLayer( RenderEnums::LevelSlot );
+ rAssert( rloutside );
+
+ // TO DO: Cary, this isn't good... do we care which player?
+ tView* viewOutside = rloutside->pView( 0 );
+
+ if ( on && !mDebugViewOn )
+ {
+ //Turn on
+ viewOutside->SetCamera( mDebugCamera );
+
+ rloutside->AddGuts( mFrustrumDrawable );
+
+ mFrustrumDrawable->Enable();
+
+ mDebugXZAngle = DEBUG_CAM_XZ_ANGLE;
+ mDebugYAngle = DEBUG_CAM_Y_ANGLE;
+ mDebugMagnitude = DEBUG_CAM_DIST;
+ }
+ else if ( !on && mDebugViewOn )
+ {
+ //Turn off
+ viewOutside->SetCamera( mCamera );
+
+ rloutside->RemoveGuts( mFrustrumDrawable );
+
+ mFrustrumDrawable->Disable();
+ }
+
+ mDebugViewOn = on;
+#endif
+}
+
+//=============================================================================
+// SuperCamCentral::ToggleDebugView
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::ToggleDebugView()
+{
+#ifdef SUPERCAM_DEBUG
+ ToggleDebugView( !mDebugViewOn );
+#endif
+}
+
+#ifdef DEBUGWATCH
+//=============================================================================
+// SuperCamCentral::DrawFrustrum
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::DrawFrustrum()
+{
+ rmt::Vector wnp[4], wfp[4];
+ rmt::Vector temp;
+ rmt::Vector realPos;
+ const tColour colour( tColour( 255, 255, 255 ) );
+
+ mCamera->GetPosition( &realPos );
+
+ float tempFP = mCamera->GetFarPlane();
+
+ mCamera->SetFarPlane(200.0f);
+ mCamera->ViewToWorld( rmt::Vector( -0.5f, 0.5f, 0 ), &wnp[0], &wfp[0] );
+ mCamera->ViewToWorld( rmt::Vector( 0.5f, 0.5f, 0 ), &wnp[1], &wfp[1] );
+ mCamera->ViewToWorld( rmt::Vector( 0.5f, -0.5f, 0 ), &wnp[2], &wfp[2] );
+ mCamera->ViewToWorld( rmt::Vector( -0.5f, -0.5f, 0 ), &wnp[3], &wfp[3] );
+
+ mCamera->SetFarPlane(tempFP);
+ //This is hard coded line drawing.
+ mFrustrumDrawable->SetColour( colour );
+ mFrustrumDrawable->SetPoints( mCameraCollisionVolume->mPosition, realPos, wfp[0], wfp[1], wfp[2], wfp[3] );
+ mFrustrumDrawable->SetScale( mActiveSuperCam->GetCollisionRadius() );
+ mFrustrumDrawable->SetSuperCam( mActiveSuperCam );
+}
+#endif
+
+//=============================================================================
+// SuperCamCentral::LoadData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const GameDataByte* dataBuffer, unsigned int numBytes )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::LoadData( const GameDataByte* dataBuffer, unsigned int numBytes )
+{
+ GameDataByte bitmask = dataBuffer[ 0 ];
+
+ /*
+ * BITS: 7654 3210
+ *
+ * bitmask = [ **** ***? ] = jump cameras on/off flag
+ * bitmask = [ **** **?* ] = inverted camera on/off flag
+ * bitmask = [ ???? ??** ] = preferred follow camera type
+ *
+ */
+
+ mJumpCamsEnabled = ( (bitmask & 0x01) > 0 );
+ mIsInvertedCameraEnabled = ( (bitmask & 0x02) > 0 );
+
+ GameDataByte preferredFollowCam = (bitmask >> 2);
+ if( preferredFollowCam == 0 )
+ {
+ // this is for backward-compatibility w/ old saved game formats
+ // that didn't have the preferred follow cam data
+ //
+ mPreferredFollowCam = SuperCam::FAR_FOLLOW_CAM;
+ }
+ else
+ {
+ mPreferredFollowCam = static_cast<SuperCam::Type>( preferredFollowCam );
+ }
+}
+
+//=============================================================================
+// SuperCamCentral::SaveData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( GameDataByte* dataBuffer, unsigned int numBytes )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::SaveData( GameDataByte* dataBuffer, unsigned int numBytes )
+{
+ GameDataByte bitmask = 0x00;
+
+ /*
+ * BITS: 7654 3210
+ *
+ * bitmask = [ **** ***? ] = jump cameras on/off flag
+ * bitmask = [ **** **?* ] = inverted camera on/off flag
+ * bitmask = [ ???? ??** ] = preferred follow camera type
+ *
+ */
+
+ if( mJumpCamsEnabled )
+ {
+ bitmask |= 0x01;
+ }
+
+ if( mIsInvertedCameraEnabled )
+ {
+ bitmask |= 0x02;
+ }
+
+ GameDataByte preferredFollowCam = static_cast<GameDataByte>( mPreferredFollowCam );
+ rAssertMsg( (preferredFollowCam & 0xC0) == 0,
+ "Not enough bits to save preferred follow cam data!" );
+
+ bitmask |= (preferredFollowCam << 2);
+
+ dataBuffer[ 0 ] = bitmask;
+}
+
+//=============================================================================
+// SuperCamCentral::ResetData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::ResetData()
+{
+ mJumpCamsEnabled = true;
+ mIsInvertedCameraEnabled = false;
+ mPreferredFollowCam = FollowCam::FAR_FOLLOW_CAM;
+}
+
+//=============================================================================
+// SuperCamCentral::IsLegalType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( SuperCam::Type type )
+//
+// Return: bool
+//
+//=============================================================================
+bool SuperCamCentral::IsLegalType( SuperCam::Type type )
+{
+ if( mTarget == NULL )
+ {
+ return false;
+ }
+
+ if ( type == SuperCam::STATIC_CAM ||
+ type == SuperCam::RAIL_CAM ||
+ type == SuperCam::CONVERSATION_CAM ||
+ (mTarget != NULL && !mTarget->IsCar() && type == SuperCam::FIRST_PERSON_CAM ) ||
+ (mTarget != NULL && mTarget->IsCar() && type == SuperCam::REVERSE_CAM) ||
+ type == SuperCam::ANIMATED_CAM ||
+ type == SuperCam::RELATIVE_ANIMATED_CAM ||
+ type == SuperCam::SUPER_SPRINT_CAM )
+ {
+ return true;
+ }
+
+ SuperCam::Type* cameras = NULL;
+ unsigned int numCameras = 0;
+
+ if( mTarget != NULL && mTarget->IsCar() )
+ {
+ if ( GetGameplayManager()->IsSuperSprint() )
+ {
+ cameras = SUPER_SPRINT_CAMERAS;
+ numCameras = NUM_SUPERSPRINT_CAMS;
+ }
+ else
+ {
+ cameras = CAMERAS_FOR_DRIVING;
+
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_CAMERAS ) )
+ {
+ numCameras = NUM_CAMERAS_FOR_DRIVING;
+ }
+ else
+ {
+ numCameras = NUM_CAMERAS_FOR_DRIVING_WITHOUT_CHEAT;
+ }
+ }
+ }
+ else
+ {
+ cameras = CAMERAS_FOR_WALKING;
+
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_CAMERAS ) )
+ {
+ numCameras = NUM_CAMERAS_FOR_WALKING;
+ }
+ else
+ {
+ numCameras = NUM_CAMERAS_FOR_WALKING_WITHOUT_CHEAT;
+ }
+ }
+
+ unsigned int i;
+ for ( i = 0; i < numCameras; ++i )
+ {
+ if ( cameras[ i ] == type )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//=============================================================================
+// SuperCamCentral::GetNewFollowCamDataChunk
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: FollowCamDataChunk
+//
+//=============================================================================
+FollowCamDataChunk& SuperCamCentral::GetNewFollowCamDataChunk()
+{
+ rAssert( mNumUsedFDC < MAX_DATA_CHUNKS );
+
+ if ( mNumUsedFDC < MAX_DATA_CHUNKS )
+ {
+ FollowCamDataChunk& chunk = mFollowCamDataChunks[ mNumUsedFDC ];
+ ++mNumUsedFDC;
+ return chunk;
+ }
+
+ return mFollowCamDataChunks[ 0 ]; //This is BAD, but not fatal
+}
+
+//=============================================================================
+// SuperCamCentral::ReleaseFollowCamDataChunk
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( FollowCamDataChunk& chunk )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamCentral::ReleaseFollowCamDataChunk( FollowCamDataChunk& chunk )
+{
+ unsigned int i;
+ for ( i = 0; i < mNumUsedFDC; ++i )
+ {
+ if ( mFollowCamDataChunks[ i ] == chunk )
+ {
+ mFollowCamDataChunks[ i ] = mFollowCamDataChunks[ mNumUsedFDC - 1 ];
+ mNumUsedFDC--;
+ return;
+ }
+ }
+}
+
+//=============================================================================
+// SuperCamCentral::FindFCD
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int id )
+//
+// Return: FollowCamDataChunk*
+//
+//=============================================================================
+FollowCamDataChunk* SuperCamCentral::FindFCD( unsigned int id )
+{
+ unsigned int i;
+ for ( i = 0; i < mNumUsedFDC; ++i )
+ {
+ if ( mFollowCamDataChunks[ i ].mID == id )
+ {
+ return &mFollowCamDataChunks[ i ];
+ }
+ }
+
+ return NULL;
+}
+
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
diff --git a/game/code/camera/supercamcentral.h b/game/code/camera/supercamcentral.h
new file mode 100644
index 0000000..804dd00
--- /dev/null
+++ b/game/code/camera/supercamcentral.h
@@ -0,0 +1,388 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supercamcentral.h
+//
+// Description: Blahblahblah
+//
+// History: 03/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef SUPERCAMCENTRAL_H
+#define SUPERCAMCENTRAL_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+#include <data/gamedata.h>
+#include <events/eventlistener.h>
+
+#include <ai/actor/intersectionlist.h>
+
+#include <camera/followcamdatachunk.h>
+
+//========================================
+// Forward References
+//========================================
+class tCamera;
+class tPointCamera;
+class ISuperCamTarget;
+class FrustrumDrawable;
+class FOVLocator;
+class SuperCamController;
+
+namespace sim
+{
+ class CollisionObject;
+ //class CollisionVolume;
+ class SphereVolume;
+ class ManualSimState;
+};
+
+extern SuperCam::Type CAMERAS_FOR_DRIVING[];
+extern const int NUM_CAMERAS_FOR_DRIVING;
+extern const int NUM_CAMERAS_FOR_DRIVING_WITHOUT_CHEAT;
+
+extern SuperCam::Type CAMERAS_FOR_WALKING[];
+extern const int NUM_CAMERAS_FOR_WALKING;
+extern const int NUM_CAMERAS_FOR_WALKING_WITHOUT_CHEAT;
+
+extern SuperCam::Type SUPER_SPRINT_CAMERAS[];
+extern const int NUM_SUPERSPRINT_CAMS;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class SuperCamCentral : public EventListener,
+ public GameDataHandler
+{
+public:
+ enum
+ {
+ MAX_CAMERAS = 32,
+#ifdef RAD_WIN32
+ MAX_COLLISIONS = 15,
+#else
+ MAX_COLLISIONS = 10,
+#endif
+ MAX_DATA_CHUNKS = 180
+ };
+ static const char* CAMERA_INVENTORY_SECTION;
+
+ SuperCamCentral();
+ virtual ~SuperCamCentral();
+
+ void Init( bool shutdown = false );
+ bool IsInit() const;
+
+ //Update the active super camera
+ void Update( unsigned int milliseconds, bool isFirstSubstep = true );
+
+ //Physics stuff
+ void PreCollisionPrep();
+ void AddCameraCollisionOffset(rmt::Vector& fixOffset);
+
+ // submission to collision detection
+ void SubmitStatics();
+
+ void UpdateForPhysics( unsigned int milliseconds );
+
+ //Adding and removing available cameras
+ unsigned int RegisterSuperCam( SuperCam* cam );
+ void UnregisterSuperCam( SuperCam* cam );
+ void UnregisterSuperCam( unsigned int which );
+
+ //Accessing cameras
+ unsigned int GetNumRegisteredSuperCams() const;
+ SuperCam* GetSuperCam( unsigned int which ) const;
+ SuperCam* GetSuperCam( SuperCam::Type type ) const; // returns NULL if not found
+ unsigned int GetActiveSuperCamIndex() const;
+
+ //Selecting which cam(s) to make active
+ enum SelectFlag { CUT = 0x00000001, QUICK = 0x00000010, FORCE = 0x00000100, NO_TRANS = 0x10000000};
+ bool ToggleSuperCam( bool forwards, bool quickTransition = true );
+ void SelectSuperCam( unsigned int which, int flags = CUT | QUICK, unsigned int timems = 7000 );
+ void SelectSuperCam( SuperCam::Type type, int flags = CUT | QUICK, unsigned int timems = 7000 );
+ void SelectSuperCam( SuperCam* cam, int flags = CUT | QUICK, unsigned int timems = 7000 );
+ SuperCam* GetActiveSuperCam();
+
+ //This defines what tPointCamera class is used with all the SuperCams
+ void SetCamera( tPointCamera* cam );
+ tPointCamera* const GetCamera();
+
+ void SetTarget( ISuperCamTarget* target );
+ void AddTarget( ISuperCamTarget* target );
+ const ISuperCamTarget* const GetTarget() const;
+
+ //Debugging stuff
+ void ToggleDebugView( bool on );
+ void ToggleDebugView();
+
+ bool AllowCameraToggle();
+ bool AllowAutoCameraChange();
+
+ void RegisterFOVLocator( FOVLocator* loc );
+ void UnregisterFOVLocator();
+
+ void DoCameraCut();
+ void NoTransition();
+
+ int mCollisionAreaIndex; // index into the collision area this instance will use.
+
+ //FROM EventListener
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void ToggleFirstPerson( int controllerID );
+
+ bool IsCutCam();
+
+ void SetIsInitialCamera(bool b) { mInitialCamera = b; }
+ bool IsInitialCamera(void) { return mInitialCamera; }
+
+ void EnableInvertedCamera( bool isEnabled );
+ bool IsInvertedCameraEnabled() const;
+
+ // Implements GameDataHandler
+ //
+ virtual void LoadData( const GameDataByte* dataBuffer, unsigned int numBytes );
+ virtual void SaveData( GameDataByte* dataBuffer, unsigned int numBytes );
+ virtual void ResetData();
+
+ void EnableJumpCams( bool enable );
+ bool JumpCamsEnabled() const;
+
+ bool IsLegalType( SuperCam::Type type );
+
+ IntersectionList& GetIntersectionList() { return mIntersectionList; };
+
+ SuperCam::Type GetPreferredFollowCam(void) { return mPreferredFollowCam; }
+
+ static FollowCamDataChunk& GetNewFollowCamDataChunk();
+ static void ReleaseFollowCamDataChunk( FollowCamDataChunk& chunk );
+ static FollowCamDataChunk* FindFCD( unsigned int id );
+ static void CleanupDataChunks() { mNumUsedFDC = 0; };
+
+ void NastyHypeCamHackEnable( bool enable ) { mNastyHypeCamHackEnabled = enable; };
+
+private:
+ void SetActiveSuperCam( unsigned int which = 0, int flags = CUT | QUICK, unsigned int timems = 7000 );
+
+#ifdef DEBUGWATCH
+ void DrawFrustrum();
+#endif
+
+ SuperCam* mSuperCameras[ MAX_CAMERAS ];
+ SuperCam* mActiveSuperCam;
+ unsigned int mActiveSuperCamIndex;
+ unsigned int mNumRegisteredSuperCams;
+
+ struct NextSuperCam
+ {
+ NextSuperCam() : nextSuperCam( 0 ), nextCamDelay( 0 ), flags( 0 ), timems( 0 ), incoming( false ) {};
+ unsigned int nextSuperCam; //This is for delaying transitions.
+ int nextCamDelay;
+ int flags;
+ unsigned int timems;
+ bool incoming;
+ };
+
+ NextSuperCam mNextSuperCam;
+
+ //Things to share between SuperCams
+ tPointCamera* mCamera;
+ ISuperCamTarget* mTarget;
+
+ static unsigned char mTotalSuperCamCentrals;
+ unsigned char mMyNumber;
+
+#ifdef SUPERCAM_DEBUG
+ //Debugging stuff.
+ bool mDebugViewOn;
+ tPointCamera* mDebugCamera;
+ float mDebugXZAngle;
+ float mDebugYAngle;
+ float mDebugMagnitude;
+
+ FrustrumDrawable* mFrustrumDrawable;
+#endif
+
+ FOVLocator* mCurrentFOVLocator;
+
+ bool mDoCameraCut;
+
+ unsigned int mChanceToBurnout;
+
+ //Physics stuff
+ sim::CollisionObject* mCameraCollisionObject;
+ //sim::CollisionVolume* mCameraCollisionVolume;
+ sim::SphereVolume* mCameraCollisionVolume;
+
+ sim::ManualSimState* mCameraSimState;
+
+ float mCameraCollisionFudge;
+ rmt::Vector mCameraCollisionOffsetFix[ MAX_COLLISIONS ];
+ unsigned int mCameraCollisionCount;
+
+ rmt::Vector mCameraTerrainCollisionOffsetFix;
+
+ void UpdateCameraCollisionSpherePosition(rmt::Vector& p);
+ void UpdateCameraCollisionSphereRadius(float radius);
+
+ SuperCamController* mController;
+ int mControllerHandle;
+
+ unsigned int mWrecklessCount;
+
+ SuperCam::Type mPreferredFollowCam;
+
+ IntersectionList mIntersectionList;
+
+ bool mInitialCamera : 1;
+ bool mIsInvertedCameraEnabled : 1;
+ bool mJumpCamsEnabled : 1;
+ bool mCameraToggling : 1;
+ bool mNastyHypeCamHackEnabled : 1;
+
+ static FollowCamDataChunk mFollowCamDataChunks[ MAX_DATA_CHUNKS ];
+ static unsigned int mNumUsedFDC;
+
+ //Prevent wasteful constructor creation.
+ SuperCamCentral( const SuperCamCentral& supercamcentral );
+ SuperCamCentral& operator=( const SuperCamCentral& supercamcentral );
+};
+
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperCamCentral::GetActiveSuperCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: SuperCam
+//
+//=============================================================================
+inline SuperCam* SuperCamCentral::GetActiveSuperCam()
+{
+ return mActiveSuperCam;
+}
+
+//=============================================================================
+// SuperCamCentral::GetCamera
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: tCamera
+//
+//=============================================================================
+inline tPointCamera* const SuperCamCentral::GetCamera()
+{
+ return mCamera;
+}
+
+//=============================================================================
+// SuperCamCentral::GetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+inline const ISuperCamTarget* const SuperCamCentral::GetTarget() const
+{
+ return mTarget;
+}
+
+//=============================================================================
+// SuperCamCentral::DoCameraCut
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCamCentral::DoCameraCut()
+{
+ mDoCameraCut = true;
+}
+
+//=============================================================================
+// SuperCamCentral::EnableInvertedCamera
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool isEnabled )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCamCentral::EnableInvertedCamera( bool isEnabled )
+{
+ mIsInvertedCameraEnabled = isEnabled;
+}
+
+//=============================================================================
+// SuperCamCentral::IsInvertedCameraEnabled
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool SuperCamCentral::IsInvertedCameraEnabled() const
+{
+ return mIsInvertedCameraEnabled;
+}
+
+//=============================================================================
+// SuperCamCentral::EnableJumpCams
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool enable )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCamCentral::EnableJumpCams( bool enable )
+{
+ mJumpCamsEnabled = enable;
+}
+
+//=============================================================================
+// SuperCamCentral::JumpCamsEnabled
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool SuperCamCentral::JumpCamsEnabled() const
+{
+ return mJumpCamsEnabled;
+}
+
+
+
+#endif //SUPERCAMCENTRAL_H
diff --git a/game/code/camera/supercamconstants.h b/game/code/camera/supercamconstants.h
new file mode 100644
index 0000000..79b64e5
--- /dev/null
+++ b/game/code/camera/supercamconstants.h
@@ -0,0 +1,19 @@
+#ifndef SUPERCAM_CONSTANTS
+#define SUPERCAM_CONSTANTS
+
+
+const float STICK_DEAD_ZONE = 0.05f;
+
+const float EXPECTED_FRAME_RATE = 16.0f;
+
+//const float SUPERCAM_FOV = 1.57079f; //90 Degrees
+const float SUPERCAM_FOV = 1.363451f; //78 Degrees
+const float SUPERCAM_ASPECT = 4.0f / 3.0f; //Standard aspect ratio
+const float SUPERCAM_NEAR = 1.0f;
+const float SUPERCAM_FAR = 8000.0f;
+
+const float SUPERCAM_DEFAULT_MIN_FOV = 0.3487f;
+const float SUPERCAM_DEFAULT_MAX_FOV = 1.2222f;
+const float SUPERCAM_DEFAULT_FOV_LAG = 0.022f;
+
+#endif \ No newline at end of file
diff --git a/game/code/camera/supercamcontroller.cpp b/game/code/camera/supercamcontroller.cpp
new file mode 100644
index 0000000..f61b305
--- /dev/null
+++ b/game/code/camera/supercamcontroller.cpp
@@ -0,0 +1,213 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: SuperCamController.cpp
+//
+// Description: Implement SuperCamController
+//
+// History: 14/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#ifndef WORLD_BUILDER
+#include <camera/animatedcam.h>
+#include <camera/supercammanager.h>
+#endif
+
+#include <camera/SuperCamController.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SuperCamController::SuperCamController
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SuperCamController::SuperCamController() :
+ Mappable( Input::ACTIVE_GAMEPLAY |
+ Input::ACTIVE_FIRST_PERSON |
+ Input::ACTIVE_SS_GAME |
+ Input::ACTIVE_ANIM_CAM ),
+ mIsWheel( false )
+{
+}
+
+//==============================================================================
+// SuperCamController::~SuperCamController
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SuperCamController::~SuperCamController()
+{
+}
+
+//=============================================================================
+// SuperCamController::LoadControllerMappings
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int controllerId )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamController::LoadControllerMappings( unsigned int controllerId )
+{
+ #ifdef RAD_XBOX
+ ClearMap(0);
+ Map( "RightStickX", stickX, 0, controllerId );
+ Map( "RightStickY", stickY, 0, controllerId );
+ Map( "LeftTrigger", zToggle, 0, controllerId );
+ Map( "RightTrigger", lookToggle, 0, controllerId );
+ Map( "A", A, 0, controllerId );
+ Map( "RightThumb", toggleFirstPerson, 0, controllerId );
+ Map( "LeftStickY", leftStickY, 0, controllerId );
+ Map( "Start", start, 0, controllerId );
+ Map( "Black", cameraToggle, 0, controllerId );
+ #endif
+
+ #ifdef RAD_PS2
+ ClearMap(0);
+ if ( Map( "Wheel", stickX, 0, controllerId ) )
+ {
+ mIsWheel = true;
+ //This is a wheel. No right stickY
+ Map( "Gas", in, 0, controllerId );
+ Map( "Brake", out, 0, controllerId );
+
+ if ( !Map( "Y", lookBack, 0, controllerId ) )
+ {
+ //This is a D-Pad Wheel
+ Map( "L2", lookBack, 0, controllerId );
+ Map( "X", A, 0, controllerId );
+ }
+ else
+ {
+ //GT Wheel
+ Map( "Gas", A, 0, controllerId );
+ }
+ }
+ else
+ {
+ Map( "RightStickX", stickX, 0, controllerId );
+ Map( "RightStickY", stickY, 0, controllerId );
+ Map( "AnalogR1", zToggle, 0, controllerId );
+ Map( "L1", lookToggle, 0, controllerId );
+ Map( "X", A, 0, controllerId );
+ Map( "R3", toggleFirstPerson, 0, controllerId );
+
+ Map( "L2", l2, 0, controllerId );
+ Map( "R2", r2, 0, controllerId );
+
+ Map( "LeftStickY", leftStickY, 0, controllerId );
+ }
+ #endif
+
+ #ifdef RAD_GAMECUBE
+ ClearMap(0);
+ Map( "AnalogTriggerL", zToggle, 0, controllerId );
+ Map( "A", A, 0, controllerId );
+ Map( "TriggerZ", toggleFirstPerson, 0, controllerId );
+
+ if ( Map( "Wheel", stickX, 0, controllerId ) )
+ {
+ //This is a wheel. No right stickY
+ mIsWheel = true;
+
+ Map( "Gas", in, 0, controllerId );
+ Map( "Brake", out, 0, controllerId );
+ Map( "AnalogTriggerR", lookToggle, 0, controllerId );
+ Map( "DPadDown", altLookBack, 0, controllerId );
+ }
+ else
+ {
+ Map( "RightStickX", stickX, 0, controllerId );
+ Map( "RightStickY", stickY, 0, controllerId );
+ Map( "AnalogTriggerR", lookToggle, 0, controllerId );
+
+ Map( "LeftStickY", leftStickY, 0, controllerId );
+ }
+ #endif
+
+ #ifdef RAD_WIN32
+ ClearMap(0);
+ Map( "CameraLeft", stickXdown, 0, controllerId );
+ Map( "CameraRight", stickXup, 0, controllerId );
+ Map( "CameraMoveIn", stickYup, 0, controllerId );
+ Map( "CameraMoveOut", stickYdown, 0, controllerId );
+ Map( "CameraZoom", zToggle, 0, controllerId );
+ Map( "CameraLookUp", lookToggle, 0, controllerId );
+ Map( "CameraCarLeft", carLookLeft, 0, controllerId );
+ Map( "CameraCarRight", carLookRight, 0, controllerId );
+ Map( "CameraCarLookUp", carLookUp, 0, controllerId );
+ Map( "CameraCarLookBack", lookBack, 0, controllerId );
+ Map( "CameraToggle", cameraToggle, 0, controllerId );
+ Map( "feSelect", A, 0, controllerId );
+ Map( "feMouseUp", mouseLookUp, 0, controllerId );
+ Map( "feMouseDown", mouseLookDown, 0, controllerId );
+ #endif
+}
+
+//=============================================================================
+// SuperCamController::OnButtonDown
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int controllerId, int buttonId, const IButton* pButton )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamController::OnButtonDown( int controllerId, int buttonId, const IButton* pButton )
+{
+#ifndef WORLD_BUILDER
+ //
+ // Tell the animated camera to skip
+ //
+ if( ( buttonId == A ) || ( buttonId == start ) )
+ {
+ AnimatedCam::Skip();
+ }
+
+ if ( buttonId == toggleFirstPerson )
+ {
+ //This is ugly.
+ GetSuperCamManager()->ToggleFirstPerson( controllerId );
+ }
+#endif
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/camera/supercamcontroller.h b/game/code/camera/supercamcontroller.h
new file mode 100644
index 0000000..ae11192
--- /dev/null
+++ b/game/code/camera/supercamcontroller.h
@@ -0,0 +1,177 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supercamcontroller.h
+//
+// Description: Blahblahblah
+//
+// History: 14/05/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef SUPERCAMCONTROLLER_H
+#define SUPERCAMCONTROLLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <input/mappable.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class SuperCamController : public Mappable
+{
+public:
+ enum Button
+ {
+ stickX,
+ stickY,
+ zToggle,
+ lookToggle,
+ zoom,
+ in,
+ out,
+ lookBack,
+ altLookBack,
+ A,
+ toggleFirstPerson,
+ l2,
+ r2,
+ leftStickY,
+ start,
+ cameraToggle
+#ifdef RAD_WIN32
+ ,stickXup,
+ stickXdown,
+ stickYup,
+ stickYdown,
+ carLookLeft,
+ carLookRight,
+ carLookUp,
+ mouseLookUp,
+ mouseLookDown
+#endif
+ };
+
+ SuperCamController();
+ virtual ~SuperCamController();
+
+ virtual void OnButton( int controllerId, int id, const IButton* pButton );
+
+ // This method is called when a button changes state from "Pressed" to "Released".
+ //
+ virtual void OnButtonUp( int controllerId, int buttonId, const IButton* pButton );
+
+ // This method is called when a button changes state from "Released" to "Pressed".
+ //
+ virtual void OnButtonDown( int controllerId, int buttonId, const IButton* pButton );
+
+ // This is how we create our controller device mappings to logical game mappings.
+ // The mappings set up in this method are platform specific.
+ //
+ // The basic format of the calls is to "Map" a input, to a enumerated output id.
+ // The output of the specified input will be contained in the Button[] array.
+ // This id will also be sent as a the second parameter in the OnButton... messages.
+ //
+ virtual void LoadControllerMappings( unsigned int controllerId );
+
+ bool IsWheel() { return mIsWheel; };
+
+ // A specific camera button value accessor.
+ // On PC it's specialized to process axes.
+ // On consoles it's optimized out and replaced with GetValue().
+ float GetAxisValue( unsigned int buttonId ) const;
+
+private:
+
+ bool mIsWheel;
+
+ //Prevent wasteful constructor creation.
+ SuperCamController( const SuperCamController& supercamcontroller );
+ SuperCamController& operator=( const SuperCamController& supercamcontroller );
+};
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperCamController::OnButton
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int controllerId, int id, const IButton* pButton )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCamController::OnButton( int controllerId, int id, const IButton* pButton )
+{
+}
+
+//=============================================================================
+// SuperCamController::OnButtonUp
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int controllerId, int buttonId, const IButton* pButton )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperCamController::OnButtonUp( int controllerId, int buttonId, const IButton* pButton )
+{
+}
+
+//=============================================================================
+// SuperCamController::GetAxisValue
+//=============================================================================
+// Description: A specific camera button value accessor.
+// On PC it's specialized to process axes.
+// On consoles it's optimized out and replaced with GetValue().
+// This is the cleanest way to implement camera controls for PC.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//=============================================================================
+inline float SuperCamController::GetAxisValue( unsigned int buttonId ) const
+{
+#ifdef RAD_WIN32
+ switch( buttonId )
+ {
+ case stickX:
+ {
+ float up = GetValue( stickXup );
+ float down = GetValue( stickXdown );
+
+ return ( up > down ) ? up : -down;
+ }
+ case stickY:
+ {
+ float up = GetValue( stickYup );
+ float down = GetValue( stickYdown );
+
+ return ( up > down ) ? up : -down;
+ }
+ default:
+ {
+ return GetValue( buttonId );
+ }
+ }
+#else
+ return GetValue( buttonId );
+#endif
+}
+
+#endif //SUPERCAMCONTROLLER_H
diff --git a/game/code/camera/supercammanager.cpp b/game/code/camera/supercammanager.cpp
new file mode 100644
index 0000000..4d0b2ff
--- /dev/null
+++ b/game/code/camera/supercammanager.cpp
@@ -0,0 +1,302 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supercammanager.cpp
+//
+// Description: Implementation for supercammanager class.
+//
+// History: Implemented --Devin [5/1/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/supercammanager.h>
+#include <memory/srrmemory.h>
+
+#include <mission/gameplaymanager.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+//
+// Static pointer to instance of this singleton.
+//
+SuperCamManager* SuperCamManager::mspInstance = NULL;
+
+//************************************************************************
+//
+// Public Member Functions : supercammanager Interface
+//
+//************************************************************************
+
+//========================================================================
+// supercammanager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+SuperCamManager* SuperCamManager::CreateInstance()
+{
+ if (mspInstance == NULL)
+ {
+ mspInstance = new(GMA_PERSISTENT) SuperCamManager;
+ }
+
+ return mspInstance;
+}
+
+//========================================================================
+// supercammanager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+SuperCamManager* SuperCamManager::GetInstance()
+{
+ return mspInstance;
+}
+
+//========================================================================
+// supercammanager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void SuperCamManager::DestroyInstance()
+{
+ rAssert(mspInstance != NULL);
+ delete mspInstance;
+}
+
+//=============================================================================
+// SuperCamManager::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool shutdown )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamManager::Init( bool shutdown )
+{
+ int i;
+ for ( i = 0; i < MAX_PLAYERS; ++i )
+ {
+ mSCCs[ i ].Init( shutdown );
+ }
+}
+
+//========================================================================
+// supercammanager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+/*
+SuperCamCentral& SuperCamManager::GetSCC( int iPlayer )
+{
+ rAssert( iPlayer < MAX_PLAYERS && iPlayer >= 0 );
+ return mSCCs[iPlayer];
+}
+*/
+
+//=============================================================================
+// SuperCamManager::GetSCC
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int iPlayer)
+//
+// Return: SuperCamCentral
+//
+//=============================================================================
+SuperCamCentral* SuperCamManager::GetSCC(int iPlayer)
+{
+ if(iPlayer < MAX_PLAYERS && iPlayer >= 0)
+ {
+ return &(mSCCs[iPlayer]);
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+//=============================================================================
+// SuperCamManager::PreCollisionPrep
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamManager::PreCollisionPrep()
+{
+// int i;
+// for( i = 0; i < MAX_PLAYERS; ++i )
+// {
+// mSCCs[i].PreCollisionPrep();
+// }
+
+ //We're not doing multiple views, so there!
+ mSCCs[0].PreCollisionPrep();
+}
+
+
+//========================================================================
+// supercammanager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void SuperCamManager::Update( unsigned int iElapsedTime, bool isFirstSubstep )
+{
+// int i;
+
+ //TODO: This should get setup when going into the game. Also,
+ //it should only initialize the number of players equal to the number
+ //playing.
+// for( i = 0; i < MAX_PLAYERS; ++i )
+// {
+// mSCCs[i].Update(iElapsedTime);
+// }
+
+ //We're not doing multiple views, so there!
+ mSCCs[ 0 ].Update( iElapsedTime, isFirstSubstep );
+}
+
+//=============================================================================
+// SuperCamManager::SubmitStatics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamManager::SubmitStatics()
+{
+// int i;
+
+// for(i = 0; i < GetGameplayManager()->GetNumPlayers(); i++)
+// {
+ // TODO - should probably be a more robust way to tell which of these
+ // is active
+// mSCCs[i].SubmitStatics();
+// }
+ //We're not doing multiple views, so there!
+ mSCCs[0].SubmitStatics();
+}
+
+//=============================================================================
+// SuperCamManager::ToggleFirstPerson
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int controllerID )
+//
+// Return: void
+//
+//=============================================================================
+void SuperCamManager::ToggleFirstPerson( int controllerID )
+{
+ int i;
+ for ( i = 0; i < GetGameplayManager()->GetNumPlayers(); ++i )
+ {
+ mSCCs[i].ToggleFirstPerson( controllerID );
+ }
+}
+
+
+//************************************************************************
+//
+// Protected Member Functions : supercammanager
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : supercammanager
+//
+//************************************************************************
+//========================================================================
+// supercammanager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+SuperCamManager::SuperCamManager()
+{
+}
+
+//========================================================================
+// supercammanager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+SuperCamManager::~SuperCamManager()
+{
+}
diff --git a/game/code/camera/supercammanager.h b/game/code/camera/supercammanager.h
new file mode 100644
index 0000000..826ef6b
--- /dev/null
+++ b/game/code/camera/supercammanager.h
@@ -0,0 +1,75 @@
+#ifndef __SuperCamManager_H__
+#define __SuperCamManager_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: supercammanager
+//
+// Description: The supercammanager does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/01]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <p3d/pointcamera.hpp>
+
+//=================================================
+// Project Includes
+//=================================================
+#include <constants/maxplayers.h>
+#include <camera/supercamcentral.h>
+
+//========================================================================
+//
+// Synopsis: The supercammanager; Synopsis by Inspection.
+//
+//========================================================================
+class SuperCamManager
+{
+public:
+ // Static Methods (for creating, destroying and acquiring an instance
+ // of the SuperCamManager )
+ static SuperCamManager* CreateInstance();
+ static SuperCamManager* GetInstance();
+ static void DestroyInstance();
+
+ void Init( bool shutdown );
+
+ //SuperCamCentral& GetSCC( int iPlayer );
+
+ // I don't think there's any need for reference here
+ SuperCamCentral* GetSCC( int iPlayer );
+
+ void PreCollisionPrep();
+
+ void Update( unsigned int iElapsedTime, bool isFirstSubstep );
+
+
+ // called by worldphysicsmanager for cameras to make their submissions to collision
+ void SubmitStatics();
+
+ void ToggleFirstPerson( int controllerID );
+
+private:
+ SuperCamManager();
+ ~SuperCamManager();
+
+ // Static Private Render Data
+ static SuperCamManager* mspInstance;
+
+ SuperCamCentral mSCCs[MAX_PLAYERS];
+};
+
+//
+// A little syntactic sugar for getting at this singleton.
+//
+inline SuperCamManager* GetSuperCamManager()
+{
+ return( SuperCamManager::GetInstance() );
+}
+
+#endif
diff --git a/game/code/camera/supersprintcam.cpp b/game/code/camera/supersprintcam.cpp
new file mode 100644
index 0000000..744c588
--- /dev/null
+++ b/game/code/camera/supersprintcam.cpp
@@ -0,0 +1,405 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supersprintcam.cpp
+//
+// Description: Implement SuperSprintCam
+//
+// History: 2/8/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radmath/radmath.hpp>
+
+#include <p3d/vectorcamera.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/supersprintcam.h>
+#include <camera/supercamconstants.h>
+#include <camera/supercamcontroller.h>
+
+#include <mission/gameplaymanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+unsigned int SuperSprintCam::mSprintCamCount = 0;
+
+#ifdef RAD_PS2
+static float CAMERA_VALUES[ RenderEnums::MAX_LEVEL - RenderEnums::numLevels ][ 3 ] =
+{
+// { 4.847f, 306.69729f, -289.8292f },
+ { 26.88f, 157.87f, -206.23f },
+ { 35.67f, 195.66f, -207.82f },
+ { -27.78f, 146.18f, -228.17f },
+ { 32.92f, 305.26f, -292.99f },
+ { 26.73f, 128.08f, -208.55f },
+ { 30.338f, 208.018f, -358.739f },
+ { 30.62f, 157.05f, -213.99f },
+};
+
+//Theres a 1-1 bonus to main levels.
+static rmt::Vector* CAMERA_SETTINGS = (rmt::Vector*)(CAMERA_VALUES);
+
+static float CAMERA_FOV[ RenderEnums::MAX_LEVEL - RenderEnums::numLevels ] =
+{
+// 0.2618f,
+ 0.4f,
+ 0.4f,
+ 0.35f,
+ 0.26f,
+ 0.4f,
+ 0.2618f,
+ 0.4f
+};
+
+#endif
+
+#ifdef RAD_XBOX
+static float CAMERA_VALUES[ RenderEnums::MAX_LEVEL - RenderEnums::numLevels ][ 3 ] =
+{
+// { 49.99f, 160.21f, -233.60f },
+ { 49.99f, 160.21f, -233.60f },
+ { 36.79f, 213.18f, -224.51f },
+ { -35.01f, 164.01f, -234.05f },
+ { 37.59f, 347.682f, -316.03f },
+ { 38.66f, 152.70f, -215.20f },
+ { -0.729f, 149.45f, -235.30f },
+ { 54.581f, 178.399f, -220.06f }
+};
+
+//Theres a 1-1 bonus to main levels.
+static rmt::Vector* CAMERA_SETTINGS = (rmt::Vector*)(CAMERA_VALUES);
+
+static float CAMERA_FOV[ RenderEnums::MAX_LEVEL - RenderEnums::numLevels ] =
+{
+// 0.2618f,
+ 0.4f,
+ 0.4f,
+ 0.35f,
+ 0.26f,
+ 0.4f,
+ 0.42f,
+ 0.4f
+};
+
+#endif
+
+#ifdef RAD_WIN32
+//Straight copy of xbox. Will figure out later.
+static float CAMERA_VALUES[ RenderEnums::MAX_LEVEL - RenderEnums::numLevels ][ 3 ] =
+{
+// {-14.268f, 322.9046f, -313.13299f},
+ {-14.268f, 322.9046f, -313.13299f},
+ {-10.0362f, 290.3872f, -336.8332f},
+ {-14.268f, 322.9046f, -313.13299f},
+ {-12.757f, 261.6806f, -341.0744f},
+ {-14.268f, 322.9046f, -313.13299f},
+ {-14.268f, 322.9046f, -313.13299f},
+ {-14.268f, 322.9046f, -313.13299f}
+};
+
+//Theres a 1-1 bonus to main levels.
+static rmt::Vector* CAMERA_SETTINGS = (rmt::Vector*)(CAMERA_VALUES);
+
+static float CAMERA_FOV[ RenderEnums::MAX_LEVEL - RenderEnums::numLevels ] =
+{
+// 0.2618f,
+ 0.2618f,
+ 0.2618f,
+ 0.2618f,
+ 0.2618f,
+ 0.2618f,
+ 0.2618f,
+ 0.2618f
+};
+
+#endif
+
+#ifdef RAD_GAMECUBE
+static float CAMERA_VALUES[ RenderEnums::MAX_LEVEL - RenderEnums::numLevels ][ 3 ] =
+{
+// { 4.847f, 343.4719f, -244.00512f },
+ { 48.38f, 253.394f, -347.603f },
+ { 39.991f, 277.6909f, -331.487f },
+ { -13.377f, 196.149f, -306.219f },
+ { 13.81f, 276.968f, -327.466f },
+ { 43.636f, 198.81f, -314.173f },
+ { 27.499f, 233.753f, -377.58f },
+ { 79.005f, 239.61f, -354.552f }
+};
+
+//Theres a 1-1 bonus to main levels.
+static rmt::Vector* CAMERA_SETTINGS = (rmt::Vector*)(CAMERA_VALUES);
+
+static float CAMERA_FOV[ RenderEnums::MAX_LEVEL - RenderEnums::numLevels ] =
+{
+// 0.2618f,
+ 0.2618f,
+ 0.2618f,
+ 0.2618f,
+ 0.2618f,
+ 0.2618f,
+ 0.2618f,
+ 0.2618f
+};
+
+#endif
+
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperSprintCam::SuperSprintCam
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+SuperSprintCam::SuperSprintCam() :
+ mNear( 150.0f ),
+ mFar( 800.0f ),
+ mFOV( rmt::PI_BY2 ),
+ mAspect( 4.0f / 3.0f ),
+ mLag( 0.05f ),
+ mDist( 10.0f )
+{
+}
+
+//=============================================================================
+// SuperSprintCam::~SuperSprintCam
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+SuperSprintCam::~SuperSprintCam()
+{
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperSprintCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintCam::Update( unsigned int milliseconds )
+{
+ if ( GetFlag( (Flag)FIRST_TIME ) )
+ {
+ //Get the position of the vector cam.
+ //Find and set up the camera.
+ tVectorCamera* cam = p3d::find<tVectorCamera>("sprint_camShape");
+
+// mPos.Set( 4.847f, 240.5329f, -234.17244f );
+ mPos = CAMERA_SETTINGS[ GetGameplayManager()->GetCurrentLevelIndex() - 1 ];
+ mTarg.Set( 0.0f, 0.0f, 0.0f );
+
+ mFOV = CAMERA_FOV[ GetGameplayManager()->GetCurrentLevelIndex() - 1 ];
+ SetFOV( mFOV );
+
+ mAspect = 4.0f / 3.0f;
+ SetAspect( mAspect );
+
+ mUp.Set( -0.019949f, 0.6996f, 0.714194f );
+
+ mPosDelta.Set( 0.0f, 0.0f, 0.0f );
+
+#ifndef RAD_RELEASE
+// InitMyController( s_secondaryControllerID );
+#endif
+ SetFlag( (Flag)FIRST_TIME, false );
+ }
+ else
+ {
+#ifndef RAD_RELEASE
+// float x, y, z;
+// x = mController->GetValue( SuperCamController::stickX );
+// y = mController->GetValue( SuperCamController::leftStickY );
+// z = mController->GetValue( SuperCamController::stickY );
+
+// if ( rmt::Epsilon( x, 0.0f, 0.001f ) && rmt::Epsilon( y, 0.0f, 0.001f ) && rmt::Epsilon( z, 0.0f, 0.001f ) )
+// {
+#endif
+ GetPosition( &mPos );
+ GetTarget( &mTarg );
+ GetCameraUp( &mUp );
+#ifndef RAD_RELEASE
+// }
+// else
+// {
+// //Use the controller values to move the camera.
+//
+// rmt::Vector xMove( mDist, 0.0f, 0.0f );
+// xMove.Scale( x );
+//
+// rmt::Vector yMove( 0.0f, -mDist, 0.0f );
+// yMove.Scale( y );
+//
+// rmt::Vector zMove( 0.0f, 0.0f, mDist );
+// zMove.Scale( z );
+//
+// GetPosition( &mPos );
+// rmt::Vector desiredPos( mPos );
+// desiredPos.Add( xMove );
+// desiredPos.Add( yMove );
+// desiredPos.Add( zMove );
+//
+// float timeMod = milliseconds / 16.0f;
+//
+// float lag = mLag * timeMod;
+// CLAMP_TO_ONE( lag );
+//
+// MotionCubic( &mPos.x, &mPosDelta.x, desiredPos.x, lag );
+// MotionCubic( &mPos.y, &mPosDelta.y, desiredPos.y, lag );
+// MotionCubic( &mPos.z, &mPosDelta.z, desiredPos.z, lag );
+// }
+#endif
+ }
+
+#ifdef RAD_PS2
+ if ( (GetGameplayManager()->GetCurrentLevelIndex() + RenderEnums::B00) == RenderEnums::B06 )
+ {
+ SetNearPlane( 250.0f ); //HACK!
+ }
+ else
+ {
+ SetNearPlane( mNear );
+ }
+#else
+ SetNearPlane( mNear );
+#endif
+ SetFarPlane( mFar );
+ SetFOV( mFOV );
+ SetAspect( mAspect );
+ SetCameraValues( milliseconds, mPos, mTarg );
+
+ SetFlag( (Flag)CUT, false );
+}
+
+//=============================================================================
+// SuperSprintCam::OnInit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintCam::OnInit()
+{
+}
+
+//=============================================================================
+// SuperSprintCam::OnShutdown
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintCam::OnShutdown()
+{
+}
+
+//=============================================================================
+// SuperSprintCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ if ( mSprintCamCount == 0 )
+ {
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\SuperSprint" );
+
+ radDbgWatchAddFloat( &mNear, "mNear", nameSpace, NULL, NULL, 1.0, 2000.0f );
+ radDbgWatchAddFloat( &mFar, "mFar", nameSpace, NULL, NULL, 1.0, 2000.0f );
+ radDbgWatchAddFloat( &mFOV, "mFOV", nameSpace, NULL, NULL, 0.001f, rmt::PI );
+ radDbgWatchAddFloat( &mAspect, "mAspect", nameSpace, NULL, NULL, 0.001f, 3.0f );
+
+ radDbgWatchAddVector(&(mPos.x), "mPos", nameSpace, NULL, NULL, -2000.0f, 2000.0f );
+ radDbgWatchAddVector(&(mTarg.x), "mTarg", nameSpace, NULL, NULL, -2000.0f, 2000.0f );
+ radDbgWatchAddVector(&(mUp.x), "mUp", nameSpace, NULL, NULL, -2000.0f, 2000.0f );
+
+ radDbgWatchAddFloat( &mDist, "Dist", nameSpace, NULL, NULL, 1.0f, 1000.0f );
+ }
+#endif
+ mSprintCamCount++;
+}
+
+//=============================================================================
+// SuperSprintCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ if ( mSprintCamCount == 1 )
+ {
+ radDbgWatchDelete( &mNear );
+ radDbgWatchDelete( &mFar );
+ radDbgWatchDelete( &mFOV );
+ radDbgWatchDelete( &mAspect );
+
+ radDbgWatchDelete( &mPos.x );
+ radDbgWatchDelete( &mTarg.x );
+ radDbgWatchDelete( &mUp.x );
+
+ radDbgWatchDelete( &mDist );
+ }
+#endif
+ mSprintCamCount--;
+}
+
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/camera/supersprintcam.h b/game/code/camera/supersprintcam.h
new file mode 100644
index 0000000..e3bff09
--- /dev/null
+++ b/game/code/camera/supersprintcam.h
@@ -0,0 +1,103 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supersprintcam.h
+//
+// Description: Blahblahblah
+//
+// History: 2/8/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef SUPERSPRINTCAM_H
+#define SUPERSPRINTCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class SuperSprintCam : public SuperCam
+{
+public:
+ SuperSprintCam();
+ virtual ~SuperSprintCam();
+
+ virtual void Update( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ virtual Type GetType();
+
+protected:
+ virtual void OnInit();
+ virtual void OnShutdown();
+
+ //You'll need to overload these if you want debug watcher or other debug controls.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+ float mNear, mFar, mFOV, mAspect;
+ rmt::Vector mPos, mTarg, mUp;
+
+ rmt::Vector mPosDelta;
+ float mLag;
+ float mDist;
+
+private:
+ static unsigned int mSprintCamCount;
+
+ //Prevent wasteful constructor creation.
+ SuperSprintCam( const SuperSprintCam& supersprintcam );
+ SuperSprintCam& operator=( const SuperSprintCam& supersprintcam );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperSprintCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+inline const char* const SuperSprintCam::GetName() const
+{
+ return "SuperSprintCam";
+}
+
+//=============================================================================
+// SuperSprintCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: SuperCam
+//
+//=============================================================================
+inline SuperCam::Type SuperSprintCam::GetType()
+{
+ return SuperCam::SUPER_SPRINT_CAM;
+}
+
+#endif //SUPERSPRINTCAM_H
diff --git a/game/code/camera/surveillancecam.cpp b/game/code/camera/surveillancecam.cpp
new file mode 100644
index 0000000..20cae90
--- /dev/null
+++ b/game/code/camera/surveillancecam.cpp
@@ -0,0 +1,239 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SurveillanceCam
+//
+// Description: A surveillance cam, derived from SuperCam. The camera
+// is at a fixed position, such as mounted on a call
+// and can rotate to lock onto a target
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <camera/surveillancecam.h>
+#include <camera/isupercamtarget.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#ifdef RAD_GAMECUBE
+#include <main/gamecube_extras/gcmanager.h>
+#include <main/gcplatform.h>
+
+#endif
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// SurveillanceCam::SurveillanceCam
+//===========================================================================
+// Description:
+// SurveillanceCam ctor
+//
+// Constraints:
+//
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+SurveillanceCam::SurveillanceCam():
+mTarget( NULL )
+{
+}
+//===========================================================================
+// SurveillanceCam::~SurveillanceCam
+//===========================================================================
+// Description:
+// SurveillanceCam dtor
+//
+// Constraints:
+//
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+SurveillanceCam::~SurveillanceCam()
+{
+
+}
+//===========================================================================
+// SurveillanceCam::Update
+//===========================================================================
+// Description:
+// Updates camera for a new frame. Reorients to target (if target is locked)
+// or swivels it around
+//
+// Constraints:
+//
+// Parameters:
+// time elapsed in milliseconds
+//
+// Return:
+// None
+//
+//===========================================================================
+void
+SurveillanceCam::Update( unsigned int milliseconds )
+{
+ if ( mTarget != NULL )
+ {
+ //SetFOV( FOV );
+
+ rmt::Vector targetPos;
+
+ mTarget->GetPosition( &targetPos );
+
+ SetCameraValues( milliseconds, mPosition, targetPos );
+
+ }
+ else
+ {
+
+ }
+}
+
+
+//===========================================================================
+// SurveillanceCam::GetName
+//===========================================================================
+// Description:
+// Returns the name of the camera for debugging purposes
+//
+// Constraints:
+//
+// Parameters:
+//
+//
+// Return:
+//
+//
+//===========================================================================
+const char* const
+SurveillanceCam::GetName() const
+{
+ return "Surveillance Camera";
+}
+//===========================================================================
+// SurveillanceCam::GetType
+//===========================================================================
+// Description:
+// Returns the type of camera as listed in SuperCam::Type
+//
+// Constraints:
+//
+// Parameters:
+//
+//
+// Return:
+//
+//
+//===========================================================================
+SuperCam::Type
+SurveillanceCam::GetType()
+{
+ return SURVEILLANCE_CAM;
+}
+//===========================================================================
+// SurveillanceCam::SetTarget
+//===========================================================================
+// Description:
+// Sets the target for the camera to lock on to
+//
+// Constraints:
+//
+// Parameters:
+// ISuperCamTarget*
+//
+// Return:
+// None
+//
+//===========================================================================
+void
+SurveillanceCam::SetTarget( ISuperCamTarget* target )
+{
+ mTarget = target;
+}
+//===========================================================================
+// SurveillanceCam::SetPosition
+//===========================================================================
+// Description:
+// Sets the location of the camera
+//
+// Constraints:
+//
+// Parameters:
+// const reference to the position
+//
+// Return:
+// None
+//
+//===========================================================================
+void
+SurveillanceCam::SetPosition( const rmt::Vector& position )
+{
+ mPosition = position;
+}
+
+//=============================================================================
+// SurveillanceCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SurveillanceCam::OnRegisterDebugControls()
+{
+#ifdef RAD_GAMECUBE
+ GCManager::GetInstance()->ChangeResolution( WindowSizeX / 8, WindowSizeY / 8, WindowBPP );
+#endif
+
+ // Hide the HUD Map.
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_MAP );
+}
+
+//=============================================================================
+// SurveillanceCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SurveillanceCam::OnUnregisterDebugControls()
+{
+#ifdef RAD_GAMECUBE
+ GCManager::GetInstance()->ChangeResolution( WindowSizeX, WindowSizeY, WindowBPP );
+#endif
+
+ // Show the HUD Map.
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_MAP );
+}
+
diff --git a/game/code/camera/surveillancecam.h b/game/code/camera/surveillancecam.h
new file mode 100644
index 0000000..bee1896
--- /dev/null
+++ b/game/code/camera/surveillancecam.h
@@ -0,0 +1,92 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SurveillanceCam
+//
+// Description: A surveillance cam, derived from SuperCam. The camera
+// is at a fixed position, such as mounted on a call
+// and can rotate to lock onto a target
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef SURVEILLANCECAM_H
+#define SURVEILLANCECAM_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <camera/supercam.h>
+
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// A Surveillance camera
+//
+// Constraints:
+//
+//
+//===========================================================================
+class SurveillanceCam : public SuperCam
+{
+ public:
+ SurveillanceCam();
+ virtual ~SurveillanceCam();
+
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ // Forces the camera to lookat the suspect
+ virtual void SetTarget( ISuperCamTarget* target );
+
+ virtual Type GetType();
+
+ // Sets the position of the camera, it will remain fixed but can rotate
+ void SetPosition( const rmt::Vector& position );
+
+ void OnRegisterDebugControls();
+ void OnUnregisterDebugControls();
+
+ protected:
+
+ // Camera target
+ ISuperCamTarget* mTarget;
+
+ // Current camera position
+ rmt::Vector mPosition;
+
+private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow XxxClassName from being copied and assigned.
+ SurveillanceCam( const SurveillanceCam& );
+ SurveillanceCam& operator=( const SurveillanceCam& );
+
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/camera/trackercam.cpp b/game/code/camera/trackercam.cpp
new file mode 100644
index 0000000..1bf6253
--- /dev/null
+++ b/game/code/camera/trackercam.cpp
@@ -0,0 +1,224 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: TrackerCam.cpp
+//
+// Description: Implement TrackerCam
+//
+// History: 08/05/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <p3d/pointcamera.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/TrackerCam.h>
+#include <camera/ISuperCamTarget.h>
+#include <camera/supercamconstants.h>
+#include <input/inputmanager.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+#ifndef DEBUGWATCH
+const float TRACKER_CAM_INCREMENT = 0.0174f; //One degree
+const float TRACKER_CAM_DIST = 1.0f;
+const float TRACKER_CAM_MIN_DIST = 10.0f;
+const float TRACKER_CAM_MAX_DIST = 25.0f;
+#else
+float TRACKER_CAM_INCREMENT = 0.0174f;
+float TRACKER_CAM_DIST = 1.0f;
+float TRACKER_CAM_MIN_DIST = 10.0f;
+float TRACKER_CAM_MAX_DIST = 25.0f;
+#endif
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// TrackerCam::TrackerCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TrackerCam::TrackerCam() :
+ mTarget( NULL ),
+ mFOVDelta( 0.0f )
+{
+ mIm = InputManager::GetInstance();
+}
+
+//==============================================================================
+// TrackerCam::~TrackerCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TrackerCam::~TrackerCam()
+{
+}
+
+//=============================================================================
+// TrackerCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void TrackerCam::Update( unsigned int milliseconds )
+{
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ //Reset the FOV.
+ SetFOV( SUPERCAM_FOV );//DEFAULT_FOV );
+ SetFlag( (Flag)CUT, false );
+ }
+
+ //This is to adjust interpolation when we're running substeps.
+ float timeMod = (float)milliseconds / (float)16;
+
+ float xAxis = mIm->GetValue( s_secondaryControllerID, InputManager::LeftStickX );
+ float yAxis = mIm->GetValue( s_secondaryControllerID, InputManager::LeftStickY );
+ float zAxis = mIm->GetValue( s_secondaryControllerID, InputManager::LeftStickY );
+ float zToggle = mIm->GetValue( s_secondaryControllerID, InputManager::AnalogL1 );
+ float zoom = mIm->GetValue( s_secondaryControllerID, InputManager::AnalogR1 );
+
+ if ( rmt::Fabs( zToggle ) > STICK_DEAD_ZONE && rmt::Fabs( zToggle ) <= 1.0f )
+ {
+ //zToggled
+ yAxis = 0.0f;
+ }
+ else
+ {
+ zAxis = 0.0f;
+ }
+
+ rmt::Vector targetPos, camPos;
+ rmt::Vector desiredPosition;
+
+ mTarget->GetPosition( &targetPos );
+ GetPosition( &camPos );
+
+ desiredPosition = targetPos;
+
+ //--------- Figure out where to move the camera...
+
+ rmt::Vector rod;
+ rod.Sub( camPos, targetPos );
+ float magnitude, rotation, elevation;
+
+ rmt::CartesianToSpherical( rod.x, rod.z, rod.y, &magnitude, &rotation, &elevation );
+
+ rotation += ( xAxis * TRACKER_CAM_INCREMENT * timeMod );
+ elevation -= ( yAxis * TRACKER_CAM_INCREMENT * timeMod );
+ magnitude -= ( zAxis * TRACKER_CAM_DIST * timeMod );
+
+ if ( elevation < 0.05f )
+ {
+ elevation = 0.05f;
+ }
+ else if ( elevation > rmt::PI_BY2 )
+ {
+ elevation = rmt::PI_BY2;
+ }
+
+ if ( magnitude < 2.0f )
+ {
+ magnitude = 2.0f;
+ }
+
+ rmt::SphericalToCartesian( magnitude, rotation, elevation, &rod.x, &rod.z, &rod.y );
+
+ desiredPosition.Add( rod );
+
+ //--------- Goofin' with the FOV
+
+ rmt::Vector posToTarg;
+ posToTarg.Sub( targetPos, desiredPosition );
+ float dist = posToTarg.Magnitude(); //Square root!
+ float FOV = GetFOV();
+
+ DoWrecklessZoom( dist, TRACKER_CAM_MIN_DIST, TRACKER_CAM_MAX_DIST, mData.GetMinFOV(), mData.GetMaxFOV(), FOV, mFOVDelta, mData.GetFOVLag(), timeMod );
+
+ SetFOV( FOV );
+
+ SetCameraValues( milliseconds, desiredPosition, targetPos );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+//=============================================================================
+// TrackerCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrackerCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\Tracker", GetPlayerID() );
+
+ radDbgWatchAddFloat( &mData.mMinFOV, "Min FOV", nameSpace, NULL, NULL, 0.0f, rmt::PI );
+ radDbgWatchAddFloat( &mData.mMaxFOV, "Max FOV", nameSpace, NULL, NULL, 0.0f, rmt::PI );
+ radDbgWatchAddFloat( &mData.mFOVLag, "FOV Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat( &TRACKER_CAM_MIN_DIST, "Min Dist", nameSpace, NULL, NULL, 1.0f, 50.0f );
+ radDbgWatchAddFloat( &TRACKER_CAM_MAX_DIST, "Max Dist", nameSpace, NULL, NULL, 1.0f, 50.0f );
+#endif
+}
+
+//=============================================================================
+// TrackerCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrackerCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mData.mMinFOV );
+ radDbgWatchDelete( &mData.mMaxFOV );
+ radDbgWatchDelete( &mData.mFOVLag );
+
+ radDbgWatchDelete( &TRACKER_CAM_MIN_DIST );
+ radDbgWatchDelete( &TRACKER_CAM_MAX_DIST );
+#endif
+}
diff --git a/game/code/camera/trackercam.h b/game/code/camera/trackercam.h
new file mode 100644
index 0000000..85d0d0e
--- /dev/null
+++ b/game/code/camera/trackercam.h
@@ -0,0 +1,159 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trackercam.h
+//
+// Description: Blahblahblah
+//
+// History: 08/05/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef TRACKERCAM_H
+#define TRACKERCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+#include <camera/trackercamdata.h>
+
+//========================================
+// Forward References
+//========================================
+class ISuperCamTarget;
+class InputManager;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class TrackerCam : public SuperCam
+{
+public:
+ TrackerCam();
+ virtual ~TrackerCam();
+
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ virtual Type GetType();
+
+ //These are for favourable support of this command
+ virtual void SetTarget( ISuperCamTarget* target );
+ virtual void AddTarget( ISuperCamTarget* target );
+
+ virtual unsigned int GetNumTargets() const;
+
+private:
+ ISuperCamTarget* mTarget;
+
+ InputManager* mIm;
+
+ TrackerCamData mData;
+
+ float mFOVDelta;
+
+ //These functions are to allow real-time control of the settings of
+ //the supercam.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+ //Prevent wasteful constructor creation.
+ TrackerCam( const TrackerCam& trackercam );
+ TrackerCam& operator=( const TrackerCam& trackercam );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// TrackerCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+inline const char* const TrackerCam::GetName() const
+{
+ return "TRACKER_CAM";
+}
+
+//=============================================================================
+// TrackerCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type TrackerCam::GetType()
+{
+ return TRACKER_CAM;
+}
+
+//=============================================================================
+// TrackerCam::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void TrackerCam::SetTarget( ISuperCamTarget* target )
+{
+ mTarget = target;
+}
+
+//=============================================================================
+// TrackerCam::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+inline void TrackerCam::AddTarget( ISuperCamTarget* target )
+{
+ rAssertMsg( false, "Only call SetTarget on the TrackerCam" );
+}
+
+//=============================================================================
+// TrackerCam::GetNumTargets
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int TrackerCam::GetNumTargets() const
+{
+ if ( mTarget )
+ {
+ return 1;
+ }
+
+ return 0;
+}
+
+#endif //TRACKERCAM_H
diff --git a/game/code/camera/trackercamdata.h b/game/code/camera/trackercamdata.h
new file mode 100644
index 0000000..bd42cfc
--- /dev/null
+++ b/game/code/camera/trackercamdata.h
@@ -0,0 +1,168 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trackercamdata.h
+//
+// Description: Blahblahblah
+//
+// History: 08/05/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef TRACKERCAMDATA_H
+#define TRACKERCAMDATA_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class TrackerCamData
+{
+public:
+ TrackerCamData();
+ virtual ~TrackerCamData() {};
+
+ void SetMinFOV( float min );
+ float GetMinFOV() const;
+
+ void SetMaxFOV( float max );
+ float GetMaxFOV() const;
+
+ void SetFOVLag( float lag );
+ float GetFOVLag() const;
+
+ float mMinFOV;
+ float mMaxFOV;
+ float mFOVLag;
+
+private:
+
+ //Prevent wasteful constructor creation.
+ TrackerCamData( const TrackerCamData& trackercamdata );
+ TrackerCamData& operator=( const TrackerCamData& trackercamdata );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// TrackerCamData::TrackerCamData
+//=============================================================================
+// Description: Constructor
+//
+// Parameters: ()
+//
+// Return: TrackerCamData
+//
+//=============================================================================
+inline TrackerCamData::TrackerCamData() :
+ mMinFOV( 0.3487f ), //Fudge...
+ mMaxFOV( 1.75079f ), //Fudge...
+ mFOVLag( 0.05f )
+{
+}
+
+//=============================================================================
+// TrackerCamData::SetMinFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float min )
+//
+// Return: void
+//
+//=============================================================================
+inline void TrackerCamData::SetMinFOV( float min )
+{
+ mMinFOV = min;
+}
+
+//=============================================================================
+// TrackerCamData::GetMinFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float TrackerCamData::GetMinFOV() const
+{
+ return mMinFOV;
+}
+
+//=============================================================================
+// TrackerCamData::SetMaxFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float max )
+//
+// Return: void
+//
+//=============================================================================
+inline void TrackerCamData::SetMaxFOV( float max )
+{
+ mMaxFOV = max;
+}
+
+//=============================================================================
+// TrackerCamData::GetMaxFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float TrackerCamData::GetMaxFOV() const
+{
+ return mMaxFOV;
+}
+
+//=============================================================================
+// TrackerCamData::SetFOVLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void TrackerCamData::SetFOVLag( float lag )
+{
+ mFOVLag = lag;
+}
+
+//=============================================================================
+// TrackerCamData::GetFOVLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float TrackerCamData::GetFOVLag() const
+{
+ return mFOVLag;
+}
+
+#endif //TRACKERCAMDATA_H
diff --git a/game/code/camera/walkercam.cpp b/game/code/camera/walkercam.cpp
new file mode 100644
index 0000000..d44348b
--- /dev/null
+++ b/game/code/camera/walkercam.cpp
@@ -0,0 +1,1519 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: walkercam.cpp
+//
+// Description: Implement WalkerCam
+//
+// History: 25/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+//These are included in the precompiled headers on XBOX and GC
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <p3d/pointcamera.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/WalkerCam.h>
+#include <camera/WalkerCamData.h>
+#include <camera/WalkerCamDataChunk.h>
+#include <camera/isupercamtarget.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercamcontroller.h>
+#include <camera/supercammanager.h>
+
+#include <memory/srrmemory.h>
+
+#include <events/eventmanager.h>
+#include <events/eventenum.h>
+
+#include <main/game.h>
+
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactertarget.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/ped/pedestrianmanager.h>
+
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/animatedicon.h>
+
+#include <ai/actor/intersectionlist.h>
+
+#include <choreo/utility.hpp>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+#ifdef DEBUGWATCH
+float SLIDE_INTERVAL = 0.06f;
+float AVERAGE_LAG = 1.0f;
+float PED_MAX_DIST = 5.0f;
+float PED_MIN_DIST = 0.0f;
+float PED_ZOOM_OFFSET = 0.49f;
+float PED_FOV_TEST = 0.567424f; //~33 deg
+unsigned int MIN_PED_ACCUM = 1500;
+static float CREEPY_TWIST_WALKER = 0.176f;
+
+rmt::Vector gLookFromR;
+rmt::Vector gLookFromL;
+rmt::Vector gLookToR;
+rmt::Vector gLookToL;
+rmt::Vector gLookFromU;
+rmt::Vector gLookFromD;
+rmt::Vector gLookToU;
+rmt::Vector gLookToD;
+
+#else
+const float SLIDE_INTERVAL = 0.06f;
+const float AVERAGE_LAG = 1.0f;
+const float PED_MAX_DIST = 5.0f;
+const float PED_MIN_DIST = 0.0f;
+const float PED_ZOOM_OFFSET = 0.49f;
+const float PED_FOV_TEST = 0.567424f; //~33 deg
+const unsigned int MIN_PED_ACCUM = 1500;
+static const float CREEPY_TWIST_WALKER = 0.176f;
+#endif
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// WalkerCam::WalkerCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WalkerCam::WalkerCam() :
+ mFOVDelta( 0.0f ),
+ mMagnitude( 0.0f ),
+ mMagnitudeDelta( 0.0f ),
+ mElevation( 0.0f ),
+ mElevationDelta( 0.0f ),
+ mIsAirborn( false ),
+ mLandingCountdown( 0 ),
+ mCameraHeight( 0.0f ),
+ mNumCollisions( 0 ),
+ mLastCollisionFrame( 0 ),
+ mLastCharacter( 0 ),
+ mPedTimeAccum( 0 ),
+ mXAxis( 0.0f ),
+ mOldMagnitude( 0.0f ),
+ mCollectible( NULL )
+{
+ mTarget = NULL;
+
+ mTargetPos.Set(0.0f, 0.0f, 0.0f);
+ mTargetPosDelta.Set(0.0f, 0.0f, 0.0f);
+ mPosition.Set( 0.0f, 0.0f, 0.0f );
+ mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+
+ mGroundOffset.Set( 0.0f, 0.0f, 0.0f );
+}
+
+//==============================================================================
+// WalkerCam::~WalkerCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WalkerCam::~WalkerCam()
+{
+}
+
+//=============================================================================
+// WalkerCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void WalkerCam::Update( unsigned int milliseconds )
+{
+ if ( mTarget->IsUnstable() )
+ {
+ return;
+ }
+
+ //This is to adjust interpolation when we're running substeps.
+ float timeMod = 1.0f;
+ timeMod = (float)milliseconds / 16.0f;
+
+ UpdatePositionNormal( milliseconds, timeMod );
+
+ //--------- Adjust where we look
+
+ rmt::Vector targetPos;
+ rmt::Vector desiredTargetPos;
+
+ GetTargetPosition( &targetPos );
+
+#ifdef RAD_DEBUG
+ rAssert( !rmt::IsNan( targetPos.x ) );
+ rAssert( !rmt::IsNan( targetPos.y ) );
+ rAssert( !rmt::IsNan( targetPos.z ) );
+#endif
+
+ desiredTargetPos = targetPos;
+
+ desiredTargetPos.y = mTargetPos.y;
+
+ //Look up
+ rmt::Vector centre = targetPos;
+
+ centre.y += mData.GetUpAngle();
+
+ float lookUp = mController->GetValue( SuperCamController::lookToggle );
+
+ if ( !GetCamera()->SphereVisible( centre, 1.0f ) && lookUp <= 0.5f )
+ {
+ desiredTargetPos = targetPos;
+ }
+ else
+ {
+ //Look down
+
+ centre = targetPos;
+ centre.y -= mData.GetUpAngle();
+
+ if ( !GetCamera()->SphereVisible( centre, 1.0f ) && lookUp <= 0.5f )
+ {
+ desiredTargetPos = targetPos;
+ }
+ else
+ {
+ //Look normal
+ if ( !mTarget->IsCar() )
+ {
+ if ( lookUp > 0.5f )
+ {
+ const float howFarUp = 3.0f;
+ float addY = howFarUp * lookUp;
+ targetPos.y += addY;
+ desiredTargetPos = targetPos;
+ }
+ }
+
+ rmt::Vector camPos;
+ GetPosition( &camPos );
+
+ rmt::Vector camToTarg;
+ camToTarg.Sub( targetPos, camPos );
+
+ float mag, theta, phi;
+ rmt::CartesianToSpherical( camToTarg.x, camToTarg.z, camToTarg.y, &mag, &theta, &phi );
+
+ if ( phi > 1.65f )
+ {
+ desiredTargetPos = targetPos;
+ }
+ }
+ }
+
+
+ rmt::Vector targetVelocity;
+ mTarget->GetVelocity( &targetVelocity );
+
+ float targLag = mData.GetTargetLag() * timeMod;
+ if ( mTarget->IsCar() || targetVelocity.y < -1.0f )
+ {
+ targLag *= 2.0f;
+ }
+
+ CLAMP_TO_ONE(targLag);
+
+ MotionCubic( &mTargetPos.x, &mTargetPosDelta.x, desiredTargetPos.x, targLag );
+ MotionCubic( &mTargetPos.y, &mTargetPosDelta.y, desiredTargetPos.y, targLag );
+ MotionCubic( &mTargetPos.z, &mTargetPosDelta.z, desiredTargetPos.z, targLag );
+
+ //--------- Goofin' with the FOV
+
+ if ( !mTarget->IsCar() )
+ {
+ float zoom = mController->GetValue( SuperCamController::zToggle );
+
+ float FOV = GetFOV();
+ float pedPCT = IsTargetNearPed( milliseconds );
+ float offset = PED_ZOOM_OFFSET * pedPCT;
+
+ if ( GetFlag((Flag)CUT ) )
+ {
+ FilterFov( zoom, mData.GetMinFOV(), mData.GetMaxFOV(), FOV, mFOVDelta, 1.0f, timeMod, offset );
+ }
+ else
+ {
+ FilterFov( zoom, mData.GetMinFOV(), mData.GetMaxFOV(), FOV, mFOVDelta, mData.GetFOVLag(), timeMod, offset );
+ }
+
+ SetFOV( FOV );
+
+ //--------- Goofin' with the twist
+ //Are we on level 7?
+ if ( GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L7 && pedPCT > 0.0f)
+ {
+ rmt::Vector velocity;
+ mTarget->GetVelocity( &velocity );
+ float minCharSpeed = CharacterTune::sfMaxSpeed / 3.0f;
+ minCharSpeed *= minCharSpeed;
+
+ if ( velocity.MagnitudeSqr() < minCharSpeed )
+ {
+ SetTwist( CREEPY_TWIST_WALKER * pedPCT );
+ }
+ else
+ {
+ SetTwist( 0.0f );
+ }
+ }
+ else
+ {
+ SetTwist( 0.0f );
+ }
+ }
+
+ //--------- Set values.
+
+#ifdef RAD_DEBUG
+ rAssert( !rmt::IsNan( mPosition.x ) );
+ rAssert( !rmt::IsNan( mPosition.y ) );
+ rAssert( !rmt::IsNan( mPosition.z ) );
+
+ rAssert( !rmt::IsNan( mTargetPos.x ) );
+ rAssert( !rmt::IsNan( mTargetPos.y ) );
+ rAssert( !rmt::IsNan( mTargetPos.z ) );
+#endif
+
+ SetCameraValues( milliseconds, mPosition, mTargetPos );
+}
+
+//=============================================================================
+// WalkerCam::UpdateForPhysics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void WalkerCam::UpdateForPhysics( unsigned int milliseconds )
+{
+ //If we're in collision OR we're just coming out of collision.
+ bool hasGroundOffset = !rmt::Epsilon( mGroundOffset.MagnitudeSqr(), 0.0f, 0.01f);
+ if ( mNumCollisions || hasGroundOffset || GetFlag( (Flag)CUT ) )
+ {
+ float timeMod = 1.0f;
+ timeMod = (float)milliseconds / 16.0f;
+
+ if ( mNumCollisions )
+ {
+ //Adjust the mPosition and the mMagnitude if the camera is colliding.
+ UpdatePositionInCollision( milliseconds, timeMod );
+ }
+
+ if ( hasGroundOffset )
+ {
+ mPosition.Add( mGroundOffset );
+
+ rmt::Vector posToTarg;
+ posToTarg.Sub( mTargetPos, mPosition );
+ mMagnitude = posToTarg.Magnitude();
+ }
+
+ //This should test that the magnitude of the rod isn't too long. If it
+ //is, we ray cast backwards and move there.
+ if ( mMagnitude > (mData.GetMaxMagnitude() * 1.2f) ||
+ mTargetPos.y - mPosition.y > mData.GetMinMagnitude() ||
+ GetFlag( (Flag)CUT ) )
+ {
+ rmt::Vector lookFrom;
+ GetTargetPosition( &lookFrom );
+
+ rmt::Vector lookTo;
+ GetPosition( &lookTo );
+
+ rmt::Vector fromTo;
+ fromTo.Sub( lookTo, lookFrom );
+ fromTo.NormalizeSafe();
+ fromTo.Scale( mData.GetMinMagnitude() );
+
+ lookTo.Add( lookFrom, fromTo );
+
+ IntersectionList& iList = GetSuperCamManager()->GetSCC( GetPlayerID() )->GetIntersectionList();
+
+ if ( iList.LineOfSight( lookFrom, lookTo, 0.1f, true ) )
+ {
+ //Go here!
+ mPosition = lookTo;
+ mMagnitude = mData.GetMinMagnitude();
+ }
+ else
+ {
+ rmt::Vector intersection( 0.0f, 0.0f, 0.0f );
+ iList.TestIntersectionStatics( lookFrom, lookTo, &intersection );
+
+ rmt::Vector fromToIntersection;
+ fromToIntersection.Sub( intersection, lookFrom );
+ if ( rmt::Epsilon( intersection.MagnitudeSqr(), 0.0f, 0.01f ) ||
+ fromToIntersection.MagnitudeSqr() < (mData.GetMinMagnitude() * mData.GetMinMagnitude() ) )
+ {
+ //Trouble.
+ GetTargetPosition( &mPosition );
+ mPosition.y += 2.0f;
+ mMagnitude = 2.0f;
+ }
+ else
+ {
+ mPosition = intersection;
+ mMagnitude = fromToIntersection.Magnitude();
+ }
+ }
+ }
+
+ SetCameraValues( 0, mPosition, mTargetPos ); //No extra transition
+ }
+ else if ( GetFlag( (Flag)IN_COLLISION ) && !IsPushingStick() )
+ {
+ SetFlag( (Flag)IN_COLLISION, false );
+
+ //Get the old position.
+ GetPosition( &mOldPos );
+ mOldMagnitude = mMagnitude;
+ }
+
+ SetFlag( (Flag)CUT, false );
+}
+
+//=============================================================================
+// WalkerCam::LoadSettings
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned char* settings )
+//
+// Return: void
+//
+//=============================================================================
+void WalkerCam::LoadSettings( unsigned char* settings )
+{
+}
+
+//=============================================================================
+// WalkerCam::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+void WalkerCam::SetTarget( ISuperCamTarget* target )
+{
+ rAssert( target );
+
+ mTarget = target;
+
+ //Load the camera data for this guy since he is the primary
+ //dood.
+
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( SuperCamCentral::CAMERA_INVENTORY_SECTION );
+ HeapMgr()->PushHeap( GMA_TEMP );
+ tInventory::Iterator<WalkerCamDataChunk> it;
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ WalkerCamDataChunk* wcD = it.First();
+
+ while( wcD )
+ {
+ if ( wcD->mID == target->GetID() )
+ {
+ //Load the data.
+ mData.SetMaxMagnitude( wcD->mMaxMagnitude);
+ mData.SetMaxMagnitude( wcD->mMaxMagnitude);
+ mData.SetElevation( wcD->mElevation );
+
+ p3d::inventory->Remove( wcD );
+
+ p3d::inventory->PopSection();
+ return;
+ }
+
+ wcD = it.Next();
+ }
+
+ p3d::inventory->PopSection();
+ rDebugString( "There should have been camera data loaded!\n" );
+}
+
+//=============================================================================
+// WalkerCam::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+void WalkerCam::AddTarget( ISuperCamTarget* target )
+{
+ //We don't care.
+ return;
+}
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// WalkerCam::OnDisplay
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WalkerCam::OnDisplay() const
+{
+#ifdef DEBUGWATCH
+ if ( mNumCollisions == 1 && mPlaneIntersectPoint.MagnitudeSqr() != 0.0f )
+ {
+ rmt::Vector camPos;
+ rmt::Vector targPos;
+
+ GetPosition( &camPos );
+ GetTargetPosition( &targPos );
+
+ rmt::Vector normal = mCollisionOffset[ 0 ];
+
+ rmt::Vector pointInPlane;
+ pointInPlane.Add( mPlaneIntersectPoint, normal );
+ float D = -(normal.DotProduct( pointInPlane ));
+ rmt::Plane collisionPlane( normal, D );
+
+ normal.NormalizeSafe();
+
+ rmt::Vector left;
+ left.CrossProduct( rmt::Vector( 0.0f, 1.0f, 0.0f ), normal );
+
+ rmt::Vector line;
+
+ line.Add( mPlaneIntersectPoint, normal );
+
+ pddiPrimStream* stream;
+
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINES, PDDI_V_C, 6);
+
+ line.Add( mPlaneIntersectPoint, normal );
+ stream->Colour( tColour( 0, 255, 0 ) );
+ stream->Coord(line.x, line.y, line.z);
+
+ normal.Scale( -1.0f );
+ line.Add( mPlaneIntersectPoint, normal );
+ stream->Colour( tColour( 0, 255, 0 ) );
+ stream->Coord(line.x, line.y, line.z);
+
+ line.Add( mPlaneIntersectPoint, left );
+ stream->Colour( tColour( 0, 255, 0 ) );
+ stream->Coord(line.x, line.y, line.z);
+
+ left.Scale( -1.0f );
+ line.Add( mPlaneIntersectPoint, left );
+ stream->Colour( tColour( 0, 255, 0 ) );
+ stream->Coord(line.x, line.y, line.z);
+
+ stream->Colour( tColour( 0, 0, 255 ) );
+ stream->Coord(camPos.x, camPos.y, camPos.z);
+
+ stream->Colour( tColour( 0, 0, 255 ) );
+ stream->Coord(targPos.x, targPos.y, targPos.z);
+ p3d::pddi->EndPrims(stream);
+ }
+
+ pddiPrimStream* stream;
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINES, PDDI_V_C, 2);
+ stream->Colour( tColour( 0, 255, 0 ) );
+ stream->Coord( gLookFromR.x, gLookFromR.y, gLookFromR.z );
+ stream->Coord( gLookToR.x, gLookToR.y, gLookToR.z );
+ p3d::pddi->EndPrims(stream);
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINES, PDDI_V_C, 2);
+ stream->Colour( tColour( 0, 255, 0 ) );
+ stream->Coord( gLookFromL.x, gLookFromL.y, gLookFromL.z );
+ stream->Coord( gLookToL.x, gLookToL.y, gLookToL.z );
+ p3d::pddi->EndPrims(stream);
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINES, PDDI_V_C, 2);
+ stream->Colour( tColour( 0, 255, 0 ) );
+ stream->Coord( gLookFromU.x, gLookFromU.y, gLookFromU.z );
+ stream->Coord( gLookToU.x, gLookToU.y, gLookToU.z );
+ p3d::pddi->EndPrims(stream);
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINES, PDDI_V_C, 2);
+ stream->Colour( tColour( 0, 255, 0 ) );
+ stream->Coord( gLookFromD.x, gLookFromD.y, gLookFromD.z );
+ stream->Coord( gLookToD.x, gLookToD.y, gLookToD.z );
+ p3d::pddi->EndPrims(stream);
+#endif
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// WalkerCam::UpdatePositionNormal
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds, float timeMod )
+//
+// Return: void
+//
+//=============================================================================
+void WalkerCam::UpdatePositionNormal( unsigned int milliseconds, float timeMod )
+{
+ rmt::Vector targetPos, camPos;
+ rmt::Vector desiredPosition;
+ rmt::Vector desiredTargetPos;
+ rmt::Vector rod;
+
+ GetTargetPosition( &targetPos );
+ GetPosition( &camPos );
+
+ if ( mTarget->IsAirborn() && !mIsAirborn )
+ {
+ //Going Airborn!
+ mIsAirborn = true;
+
+ mCameraHeight = camPos.y;
+ }
+ else if ( !mTarget->IsAirborn() && mIsAirborn )
+ {
+ //Come down!
+ mIsAirborn = false;
+ }
+
+ desiredPosition = targetPos;
+ desiredTargetPos = targetPos;
+
+ float desiredMagnitude, rotation, elevation;
+
+ desiredMagnitude = mMagnitude;
+
+ if ( GetFlag( (Flag)FIRST_TIME ) )
+ {
+ mTargetPos = targetPos;
+ mMagnitude = mData.GetMagnitude();
+ mPosition = camPos;
+ mTargetPosDelta.Set( 0.0f, 0.0f, 0.0f );
+ mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+ }
+
+ bool topRayNotBlocked = false;
+ bool bottomRayNotBlocked = false;
+
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ //Reset the FOV.
+ SetFOV( mData.GetMinFOV() + (mData.GetMaxFOV() - mData.GetMinFOV()) );
+
+ mMagnitude = mData.GetMagnitude();
+ rotation = mData.GetRotation();
+ mElevation = mData.GetElevation();
+ mElevationDelta = 0.0f;
+
+ mTargetPos = targetPos;
+ mTargetPosDelta.Set( 0.0f, 0.0f, 0.0f );
+ mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+
+ //Reset the deltas
+ mFOVDelta = 0.0f;
+ mMagnitudeDelta = 0.0f;
+ }
+ else
+ {
+ //Try to look at the collectible
+ bool lookAtCollectible = false;
+
+ if ( mCollectible )
+ {
+ rmt::Vector collectiblePos;
+ mCollectible->GetPosition( collectiblePos );
+
+ rmt::Vector targToCollect;
+ targToCollect.Sub( collectiblePos, targetPos );
+ if ( targToCollect.MagnitudeSqr() < 100.0f )
+ {
+ //We should look at the collectible
+
+ lookAtCollectible = true;
+ rod = targToCollect;
+ rod.NormalizeSafe();
+ rod.Scale( mData.GetMagnitude() );
+ rod.x *= -1.0f;
+ rod.z *= -1.0f;
+ }
+ }
+
+ if ( !lookAtCollectible )
+ {
+ rod.Sub( camPos, targetPos );
+ }
+
+ float magWaste;
+ rmt::CartesianToSpherical( rod.x, rod.z, rod.y, &magWaste, &rotation, &elevation );
+
+ if ( GetFlag( (Flag)FIRST_TIME ) )
+ {
+ mElevation = elevation;
+ }
+ }
+
+ SetFlag( (Flag)FIRST_TIME, false );
+
+ //--------- Adjust the camera according to user input...
+
+ if ( !GetFlag((Flag)CUT ) )
+ {
+ float xAxis = mController->GetAxisValue( SuperCamController::stickX );
+ float zAxis = mController->GetAxisValue( SuperCamController::stickY );
+
+ if ( GetSuperCamManager()->GetSCC( GetPlayerID() )->IsInvertedCameraEnabled() )
+ {
+ xAxis *= -1.0f; //Invert
+ }
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ if ( mController->IsWheel() )
+ {
+ //This is a wheel. No left right on wheels.
+ xAxis *= 3.0f;
+ rmt::Clamp( xAxis, -1.0f, 1.0f );
+ }
+#endif
+
+ if ( GetFlag( (Flag)IN_COLLISION ) && IsPushingStick() )
+ {
+ xAxis = 0.0f;
+ }
+
+
+ //Auto avoid visual impediments
+ if ( !mTarget->IsCar() &&
+ ( IsStickStill() ||
+ ( GetFlag( (Flag)IN_COLLISION ) && IsPushingStick() ) ) )
+ {
+ float nearPlane = GetNearPlane();
+ float radius = GetCollisionRadius() * 0.8f;
+
+ rmt::Matrix camMat;
+ camMat.IdentityTranslation();
+
+ rmt::Vector heading;
+ GetHeading( &heading );
+ rmt::Vector vup;
+ GetCameraUp( &vup );
+ camMat.FillHeading( heading, vup ); //I do this because the tCamera may not be the same as I think it is.
+
+ //Test the sides and rotate if there's a problem.
+ rmt::Vector lookFrom( radius, 0.0f, nearPlane );
+ lookFrom.Transform( camMat );
+ lookFrom.Add( camPos );
+
+ rmt::Vector lookTo( 0.5f, 0.0f, -0.5f );
+ lookTo.Transform( camMat );
+ lookTo.Add( targetPos );
+
+#ifdef DEBUGWATCH
+ gLookFromR = lookFrom;
+ gLookToR = lookTo;
+#endif
+
+ IntersectionList& iList = GetSuperCamManager()->GetSCC( GetPlayerID() )->GetIntersectionList();
+ if ( !iList.LineOfSight( lookFrom, lookTo, 0.1f ) )
+ {
+ xAxis += -0.25f;
+ }
+
+ lookFrom.Set( -radius, 0.0f, nearPlane );
+ lookFrom.Transform( camMat );
+ lookFrom.Add( camPos );
+
+ lookTo.Set( -0.5f, 0.0f, -0.5f );
+ lookTo.Transform( camMat );
+ lookTo.Add( targetPos );
+
+#ifdef DEBUGWATCH
+ gLookFromL = lookFrom;
+ gLookToL = lookTo;
+#endif
+ if ( !iList.LineOfSight( lookFrom, lookTo, 0.1f ) )
+ {
+ xAxis += 0.25f;
+ }
+
+ //Look to see if we can go up.
+ lookFrom.Set( 0.0f, radius, nearPlane );
+ lookFrom.Transform( camMat );
+ lookFrom.Add( camPos );
+
+ lookTo.Set( 0.0f, 0.5f, -0.5f );
+ lookTo.Transform( camMat );
+ lookTo.Add( targetPos );
+
+#ifdef DEBUGWATCH
+ gLookFromU = lookFrom;
+ gLookToU = lookTo;
+#endif
+ topRayNotBlocked = iList.LineOfSight( lookFrom, lookTo, 0.1f );
+
+ //Do this one the other way since we want the ray to maintain it's length.
+ lookTo = targetPos;
+ lookTo.y = targetPos.y + 0.5f;
+
+ lookFrom.Set( 0.0f, 0.0, -0.2f );
+ lookFrom.Transform( camMat );
+
+ lookFrom.x += camPos.x;
+ lookFrom.y = lookTo.y;
+ lookFrom.z += camPos.z;
+
+ rmt::Vector toFrom;
+ toFrom.Sub( lookFrom, lookTo );
+ toFrom.Normalize();
+ toFrom.Scale( mMagnitude + 0.5f );
+
+ lookFrom.Add( lookTo, toFrom );
+
+#ifdef DEBUGWATCH
+ gLookFromD = lookFrom;
+ gLookToD = lookTo;
+#endif
+ bottomRayNotBlocked = iList.LineOfSight( lookFrom, lookTo, 0.1f );
+ }
+
+ rmt::Clamp( xAxis, -1.0f, 1.0f );
+
+ rotation += ( xAxis * mData.GetRotationIncrement() * timeMod );
+
+ const unsigned int frameTest = 0;
+ float lookUp = mController->GetValue( SuperCamController::lookToggle );
+
+ if ( rmt::Fabs( zAxis ) == 1.0f && mLastCollisionFrame == 0 ||
+ ( mLastCollisionFrame + frameTest < GetGame()->GetFrameCount() ) )
+ {
+ float halfMag = (mData.GetMaxMagnitude() - mData.GetMinMagnitude()) / 2.0f;
+
+ desiredMagnitude -= ( zAxis * halfMag );
+
+ if ( desiredMagnitude < mData.GetMinMagnitude() )
+ {
+ desiredMagnitude = mData.GetMinMagnitude();
+ }
+ else if ( desiredMagnitude > mData.GetMaxMagnitude() )
+ {
+ desiredMagnitude = mData.GetMaxMagnitude();
+ }
+
+ float lag = mData.GetLag() * timeMod;
+ CLAMP_TO_ONE(lag);
+
+ MotionCubic( &mMagnitude, &mMagnitudeDelta, desiredMagnitude, lag );
+ mLastCollisionFrame = 0;
+ }
+ }
+
+ //--------- Adjust the camera according to where the target is...
+
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ //Transform the rod to have the angle rmt::PI be behind the target...
+ rmt::Matrix mat;
+ rmt::Vector heading, vup;
+ mTarget->GetHeading( &heading );
+
+ // Dusit:
+ // assume vup of target is always 0,1,0 because
+ // the code won't work otherwise, especially now
+ // that the player character can transit into simulation control
+ // and tumble about in physics.
+ //mTarget->GetVUP( &vup );
+ vup.Set( 0.0f, 1.0f, 0.0f );
+
+ mat.Identity();
+ mat.FillHeading( heading, vup );
+
+ mElevation = mData.GetElevation();
+ rmt::SphericalToCartesian( mMagnitude, rotation, mElevation, &rod.x, &rod.z, &rod.y );
+ mElevationDelta = 0.0f;
+
+ rod.Transform( mat );
+
+ desiredPosition.Add( rod );
+
+ mPosition = desiredPosition;
+ }
+ else
+ {
+ float elevation = mData.GetElevation();
+
+ rmt::Vector groundNormal, pos;
+ mTarget->GetTerrainIntersect( pos, groundNormal );
+
+ if ( groundNormal.y < 0.97f )
+ {
+ elevation -= 0.3f;
+ }
+ else if ( topRayNotBlocked && !bottomRayNotBlocked )
+ {
+ elevation -= 0.2f;
+ }
+
+ float elevlag = SLIDE_INTERVAL * timeMod;
+ CLAMP_TO_ONE(elevlag);
+
+ MotionCubic( &mElevation, &mElevationDelta, elevation, elevlag );
+
+ rmt::SphericalToCartesian( mMagnitude, rotation, mElevation, &rod.x, &rod.z, &rod.y );
+
+ if ( mMagnitude < GetNearPlane() + 1.5f )
+ {
+ rod.x *= -1.0f;
+ rod.z *= -1.0f;
+
+ desiredPosition.Add( rod );
+
+ mPosition = desiredPosition;
+ mPosition.Add( mGroundOffset ); //Add the ground correction.
+
+ mMagnitude = mData.GetMinMagnitude();
+ mMagnitudeDelta = 0.0f;
+
+ mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+
+ //Tell the character controller that we're cutting to a new position.
+ GetEventManager()->TriggerEvent( EVENT_CAMERA_CHANGE, this );
+ }
+ else
+ {
+ float lag = mData.GetLag() * timeMod;
+ CLAMP_TO_ONE(lag);
+
+ desiredPosition.Add( rod );
+
+ if ( mIsAirborn )
+ {
+ desiredPosition.y = mCameraHeight;
+ }
+
+ MotionCubic( &mPosition.x, &mDesiredPositionDelta.x, desiredPosition.x, lag );
+ MotionCubic( &mPosition.y, &mDesiredPositionDelta.y, desiredPosition.y, lag );
+ MotionCubic( &mPosition.z, &mDesiredPositionDelta.z, desiredPosition.z, lag );
+ }
+ }
+}
+
+//=============================================================================
+// WalkerCam::UpdatePositionInCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds, float timeMod )
+//
+// Return: void
+//
+//=============================================================================
+void WalkerCam::UpdatePositionInCollision( unsigned int milliseconds, float timeMod )
+{
+ //Store the new collision and the num of collisions;
+ mLastCollisionFrame = GetGame()->GetFrameCount();
+
+ rmt::Vector camPos;
+ GetPosition( &camPos );
+
+ if ( mNumCollisions == 1 )
+ {
+ UpdatePositionOneCollsion( milliseconds, timeMod );
+ }
+ else
+ {
+ UpdatePositionMultipleCollision( milliseconds, timeMod );
+ }
+
+ SetFlag( (Flag)IN_COLLISION, true );
+ mXAxis = mController->GetAxisValue( SuperCamController::stickX );
+}
+
+//=============================================================================
+// WalkerCam::UpdatePositionOneCollsion
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds, float timeMod, unsigned int collisionIndex )
+//
+// Return: bool
+//
+//=============================================================================
+bool WalkerCam::UpdatePositionOneCollsion( unsigned int milliseconds, float timeMod, unsigned int collisionIndex )
+{
+
+ rmt::Vector camPos;
+ GetPosition( &camPos );
+ rmt::Vector targetPos;
+ mTarget->GetPosition( &targetPos );
+
+ rmt::Vector newPos;
+ mPlaneIntersectPoint.Set( 0.0f, 0.0f, 0.0f );
+ newPos.Add( camPos, mCollisionOffset[ collisionIndex ] );
+
+ mPosition = newPos;
+ mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+
+ rmt::Vector camToTarg;
+ camToTarg.Sub( targetPos, mPosition );
+
+ mMagnitude = camToTarg.Magnitude();
+ mMagnitudeDelta = 0.0f;
+
+/*
+ bool speedUp = false;
+
+ float xAxis = mController->GetValue( SuperCamController::stickX );
+
+ if ( GetFlag( (Flag)IN_COLLISION ) )
+ {
+ if ( IsPushingStick() )
+ {
+ return false;
+ }
+ else if ( !IsStickStill() )
+ {
+ speedUp = true;
+ }
+ }
+ else
+ {
+ if ( !IsStickStill() )
+ {
+ mPosition = mOldPos;
+ mMagnitude = mOldMagnitude;
+ return false;
+ }
+ }
+
+ rmt::Vector camPos, targetPos;
+ GetPosition( &camPos );
+ GetTargetPosition( &targetPos );
+
+ //Let's use the plane equation Ax+By+Cz+D = 0
+ //Where x,y,z are points in 3D and A,B,C are the plane normal
+ //D is the distance to the origin. YEAH!
+ rmt::Vector normal = mCollisionOffset[ collisionIndex ];
+ rmt::Vector normalScaled = normal;
+ normalScaled.NormalizeSafe();
+ normalScaled.Scale( -GetNearPlane() * 1.2f );
+
+ rmt::Vector pointInPlane;
+ pointInPlane.Add( camPos, normal );
+ pointInPlane.Add( normalScaled );
+
+ float D = -(normal.DotProduct( pointInPlane ));
+ rmt::Plane collisionPlane( normal, D );
+
+ rmt::Vector camToTargDir;
+ camToTargDir.Sub( targetPos, camPos );
+ camToTargDir.NormalizeSafe();
+
+ rmt::Vector newPos;
+ mPlaneIntersectPoint.Set( 0.0f, 0.0f, 0.0f );
+ newPos.Add( camPos, mCollisionOffset[ collisionIndex ] );
+
+ if ( collisionPlane.Intersect( camPos, camToTargDir, &mPlaneIntersectPoint ) )
+ {
+ rmt::Vector camToIntersect;
+ camToIntersect.Sub( mPlaneIntersectPoint, camPos );
+
+ //Test to ignore intersects that are on the wrong side of the plane.
+ if ( camToIntersect.DotProduct( camToTargDir ) > 0.0f && camToIntersect.MagnitudeSqr() < 16.0f )
+ {
+ rmt::Vector intersectPointOffset = mPlaneIntersectPoint;
+ intersectPointOffset.Sub( normalScaled );
+
+ newPos = intersectPointOffset;
+ }
+ else
+ {
+ mPlaneIntersectPoint.Set( 0.0f, 0.0f, 0.0f );
+ }
+ }
+ else
+ {
+ mPlaneIntersectPoint.Set( 0.0f, 0.0f, 0.0f );
+ }
+
+ float lag = mData.GetCollisionLag() * timeMod;
+ CLAMP_TO_ONE( lag );
+
+// if ( speedUp )
+// {
+// lag = 1.0f;
+// }
+
+ mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+ MotionCubic( &mPosition.x, &mDesiredPositionDelta.x, newPos.x, lag );
+ MotionCubic( &mPosition.y, &mDesiredPositionDelta.y, newPos.y, lag );
+ MotionCubic( &mPosition.z, &mDesiredPositionDelta.z, newPos.z, lag );
+
+ rmt::Vector newDir;
+ newDir.Sub( mPosition, targetPos );
+ float mag = newDir.Magnitude();
+
+ bool panic = false;
+
+ if ( mag > mData.GetMaxMagnitude() )
+ {
+ //Build a new rod.
+ newDir.NormalizeSafe();
+ newDir.Scale( mData.GetMagnitude() );
+
+ mPosition.Add( targetPos, newDir );
+ mag = mData.GetMagnitude();
+
+ }
+
+ if ( mag < GetNearPlane() + 1.5f )
+ {
+ MotionCubic( &mPosition.x, &mDesiredPositionDelta.x, mOldPos.x, lag );
+ MotionCubic( &mPosition.y, &mDesiredPositionDelta.y, mOldPos.y, lag );
+ MotionCubic( &mPosition.z, &mDesiredPositionDelta.z, mOldPos.z, lag );
+
+ rmt::Vector targToPos;
+ targToPos.Sub( mPosition, targetPos );
+ mag = targToPos.Magnitude();
+ }
+
+ mMagnitude = mag;
+ mMagnitudeDelta = 0.0f;
+*/
+ return false;
+}
+
+struct OldCamData
+{
+ rmt::Vector position;
+ rmt::Vector positionDelta;
+ float elevation;
+ float elevationDelta;
+ float magnitude;
+ float magnitudeDelta;
+};
+
+//=============================================================================
+// WalkerCam::UpdatePositionMultipleCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds, float timeMod )
+//
+// Return: void
+//
+//=============================================================================
+void WalkerCam::UpdatePositionMultipleCollision( unsigned int milliseconds, float timeMod )
+{
+ OldCamData origData;
+
+ origData.elevation = mElevation;
+ origData.elevationDelta = mElevationDelta;
+ origData.magnitude = mMagnitude;
+ origData.magnitudeDelta = mMagnitudeDelta;
+ origData.position = mPosition;
+ origData.positionDelta = mDesiredPositionDelta;
+
+ float desiredElevation = 0.0f;
+ float desiredMagnitude = 0.0f;
+ rmt::Vector desiredPosition( 0.0f, 0.0f, 0.0f );
+
+ unsigned int i;
+ for ( i = 0; i < mNumCollisions; ++i )
+ {
+ UpdatePositionOneCollsion( milliseconds, timeMod, i );
+
+ desiredElevation += mElevation;
+ desiredMagnitude += mMagnitude;
+ desiredPosition.Add( mPosition );
+
+ //Reset
+ mElevation = origData.elevation;
+ mElevationDelta = origData.elevationDelta;
+ mMagnitude = origData.magnitude;
+ mMagnitudeDelta = origData.magnitudeDelta;
+ mPosition = origData.position;
+ mDesiredPositionDelta = origData.positionDelta;
+ }
+
+ mElevationDelta = 0.0f;
+ mDesiredPositionDelta.Set( 0.0f, 0.0f, 0.0f );
+ mMagnitudeDelta = 0.0f;
+
+ desiredElevation += origData.elevation;
+ desiredMagnitude += origData.magnitude;
+ desiredPosition.x += origData.position.x;
+ desiredPosition.y += origData.position.y;
+ desiredPosition.z += origData.position.z;
+
+ desiredElevation /= mNumCollisions + 1;
+ desiredMagnitude /= mNumCollisions + 1;
+ desiredPosition.x /= mNumCollisions + 1;
+ desiredPosition.y /= mNumCollisions + 1;
+ desiredPosition.z /= mNumCollisions + 1;
+
+ float newLag = AVERAGE_LAG * timeMod;
+ CLAMP_TO_ONE(newLag);
+
+ MotionCubic( &mElevation, &mElevationDelta, desiredElevation, newLag );
+
+ MotionCubic( &mPosition.x, &mDesiredPositionDelta.x, desiredPosition.x, newLag );
+ MotionCubic( &mPosition.y, &mDesiredPositionDelta.y, desiredPosition.y, newLag );
+ MotionCubic( &mPosition.z, &mDesiredPositionDelta.z, desiredPosition.z, newLag );
+
+ MotionCubic( &mMagnitude, &mMagnitudeDelta, desiredMagnitude, newLag );
+}
+
+//=============================================================================
+// WalkerCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WalkerCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\Walker", GetPlayerID() );
+
+ radDbgWatchAddFloat( &mData.mMinFOV, "Min FOV", nameSpace, NULL, NULL, 0.0f, rmt::PI );
+ radDbgWatchAddFloat( &mData.mMaxFOV, "Max FOV", nameSpace, NULL, NULL, 0.0f, rmt::PI );
+ radDbgWatchAddFloat( &mData.mLag, "Main lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &mData.mFOVLag, "FOV lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat( &mData.mMinMagnitude, "Min Magnitude", nameSpace, NULL, NULL, 1.0f, 100.0f );
+ radDbgWatchAddFloat( &mData.mMaxMagnitude, "Max Magnitude", nameSpace, NULL, NULL, 1.0f, 100.0f );
+
+ radDbgWatchAddFloat( &mData.mElevation, "Elevation (M)", nameSpace, NULL, NULL, 0.0f, 5.0 );
+ radDbgWatchAddFloat( &mData.mRotation, "Rotation", nameSpace, NULL, NULL, 0.0f, rmt::PI_2 );
+ radDbgWatchAddFloat( &mData.mMagnitude, "Magnitude", nameSpace, NULL, NULL, 0.0f, 100.0f );
+
+ radDbgWatchAddFloat( &mData.mRotationIncrement, "Rotation Increment", nameSpace, NULL, NULL, 0.0f, rmt::PI_BY2 );
+
+ radDbgWatchAddVector( &mData.mTargetOffset.x, "Target Offset (M)", nameSpace, NULL, NULL, 0.0f, 2.0f );
+
+ radDbgWatchAddFloat( &mData.mTargetLag, "Target Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &mData.mJumpLag, "Jump Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+
+ radDbgWatchAddUnsignedInt( &mData.mLandingTransitionTime, "Landing Transition", nameSpace, NULL, NULL, 0, 10000 );
+
+ radDbgWatchAddFloat( &mData.mUpAngle, "Up Height (M)", nameSpace, NULL, NULL, 0.0f, 5.0f );
+
+ radDbgWatchAddFloat( &mData.mCollisionLag, "Collision Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat( &SLIDE_INTERVAL, "Slide Interval", nameSpace, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &AVERAGE_LAG, "Average Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat( &PED_MIN_DIST, "Ped Min Dist", nameSpace, NULL, NULL, 0.0f, 10.0f );
+ radDbgWatchAddFloat( &PED_MAX_DIST, "Ped Max Dist", nameSpace, NULL, NULL, 0.0f, 10.0f );
+ radDbgWatchAddFloat( &PED_ZOOM_OFFSET, "Ped Zoom", nameSpace, NULL, NULL, 0.0f, rmt::PI_BY2 );
+ radDbgWatchAddFloat( &PED_FOV_TEST, "Ped FOV Test", nameSpace, NULL, NULL, 0.01f, rmt::PI );
+
+ radDbgWatchAddUnsignedInt( &MIN_PED_ACCUM, "Min Ped Time Acc", nameSpace, NULL, NULL, 0, 10000 );
+
+ radDbgWatchAddFloat( &CREEPY_TWIST_WALKER, "Twist", nameSpace, NULL, NULL, 0.0f, rmt::PI_2 );
+#endif
+}
+
+//=============================================================================
+// WalkerCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WalkerCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mData.mMinFOV );
+ radDbgWatchDelete( &mData.mMaxFOV );
+ radDbgWatchDelete( &mData.mLag );
+ radDbgWatchDelete( &mData.mFOVLag );
+
+ radDbgWatchDelete( &mData.mMinMagnitude );
+ radDbgWatchDelete( &mData.mMaxMagnitude );
+
+ radDbgWatchDelete( &mData.mElevation );
+ radDbgWatchDelete( &mData.mRotation );
+ radDbgWatchDelete( &mData.mMagnitude );
+
+ radDbgWatchDelete( &mData.mRotationIncrement );
+
+ radDbgWatchDelete( &mData.mTargetOffset.x );
+
+ radDbgWatchDelete( &mData.mTargetLag );
+ radDbgWatchDelete( &mData.mJumpLag );
+
+ radDbgWatchDelete( &mData.mLandingTransitionTime );
+
+ radDbgWatchDelete( &mData.mUpAngle );
+
+ radDbgWatchDelete( &mData.mCollisionLag );
+
+ radDbgWatchDelete( &SLIDE_INTERVAL );
+ radDbgWatchDelete( &AVERAGE_LAG );
+
+ radDbgWatchDelete( &PED_MIN_DIST );
+ radDbgWatchDelete( &PED_MAX_DIST );
+ radDbgWatchDelete( &PED_ZOOM_OFFSET );
+ radDbgWatchDelete( &PED_FOV_TEST );
+
+ radDbgWatchDelete( &MIN_PED_ACCUM );
+
+ radDbgWatchDelete( &CREEPY_TWIST_WALKER );
+#endif
+}
+
+//=============================================================================
+// WalkerCam::IsPushingStick
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool WalkerCam::IsPushingStick()
+{
+ float xAxis = mController->GetAxisValue( SuperCamController::stickX );
+
+ return ( !rmt::Epsilon( xAxis, 0.0f, 0.001f ) &&
+ rmt::Sign( xAxis ) == rmt::Sign( mXAxis ) );
+}
+
+//=============================================================================
+// WalkerCam::IsStickStill
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool WalkerCam::IsStickStill()
+{
+ float xAxis = mController->GetAxisValue( SuperCamController::stickX );
+ return rmt::Epsilon( xAxis, 0.0f, 0.01f );
+}
+
+//=============================================================================
+// WalkerCam::GetTargetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* position, bool withOffset )
+//
+// Return: void
+//
+//=============================================================================
+void WalkerCam::GetTargetPosition( rmt::Vector* position,
+ bool withOffset ) const
+{
+ rAssert( mTarget );
+
+ mTarget->GetPosition( position );
+
+ rAssert( !rmt::IsNan( position->x ) );
+ rAssert( !rmt::IsNan( position->y ) );
+ rAssert( !rmt::IsNan( position->z ) );
+
+ if ( withOffset )
+ {
+ rmt::Vector offset;
+ mData.GetTargetOffset( &offset );
+
+ //Now put the offset in the target's space
+/* rmt::Matrix mat;
+ rmt::Vector targetHeading, targetVUP;
+ mTarget->GetHeading( &targetHeading );
+
+#ifdef RAD_DEBUG
+ rAssert( !rmt::IsNan( targetHeading.x ) );
+ rAssert( !rmt::IsNan( targetHeading.y ) );
+ rAssert( !rmt::IsNan( targetHeading.z ) );
+#endif
+
+ // Dusit:
+ // assume vup of target is always 0,1,0 because
+ // the code won't work otherwise, especially now
+ // that the player character can transit into simulation control
+ // and tumble about in physics.
+ //mTarget->GetVUP( &targetVUP );
+ targetVUP.Set( 0.0f, 1.0f, 0.0f );
+
+ mat.Identity();
+ mat.FillHeading( targetHeading, targetVUP );
+
+ offset.Transform( mat );
+*/
+ (*position).Add( offset );
+ }
+}
+
+//=============================================================================
+// WalkerCam::IsTargetNearPed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: float
+//
+//=============================================================================
+float WalkerCam::IsTargetNearPed( unsigned int milliseconds )
+{
+ if ( mPedTimeAccum - static_cast<int>(milliseconds) <= 0 )
+ {
+ mPedTimeAccum = 0;
+ }
+ else
+ {
+ mPedTimeAccum -= milliseconds;
+ }
+
+ if ( mPedTimeAccum == 0 )
+ {
+ //Reset the time.
+ mPedTimeAccum = MIN_PED_ACCUM;
+
+ //Retest for a new ped
+ CharacterManager* cm = GetCharacterManager();
+ rmt::Vector targetPos;
+
+ mTarget->GetPosition( &targetPos );
+
+ int i;
+ float pedMagSqr = 10000.0f;
+ mLastCharacter = 0;
+
+ //Find the closest, visible character.
+ float oldFOV, oldAspect;
+ GetCamera()->GetFOV( &oldFOV, &oldAspect );
+ GetCameraNonConst()->SetFOV( PED_FOV_TEST, oldAspect );
+
+ for ( i = 0; i < cm->GetMaxCharacters(); ++i )
+ {
+ Character* tempCharacter = cm->GetCharacter( i );
+ //If it's valid and not the player.
+ if ( tempCharacter != NULL &&
+ static_cast<ISuperCamTarget*>(tempCharacter->GetTarget()) != mTarget &&
+ !PedestrianManager::GetInstance()->IsPed( tempCharacter ) )
+ {
+ rmt::Vector charPos;
+ tempCharacter->GetPosition( &charPos );
+
+ if ( GetCamera()->SphereVisible( charPos, 1.0f ) )
+ {
+ rmt::Vector targetToPed;
+ targetToPed.Sub( charPos, targetPos );
+ if ( targetToPed.MagnitudeSqr() < pedMagSqr )
+ {
+ if ( targetToPed.MagnitudeSqr() < PED_MAX_DIST * PED_MAX_DIST )
+ {
+ pedMagSqr = targetToPed.MagnitudeSqr();
+ mLastCharacter = tempCharacter->GetUID();
+ }
+ }
+ }
+ }
+ }
+
+ //Reset the FOV.
+ GetCameraNonConst()->SetFOV( oldFOV, oldAspect );
+ }
+
+ //If we have a candidate ped...
+ if ( mLastCharacter != static_cast< tUID >( 0 ) )
+ {
+ Character* c = GetCharacterManager()->GetCharacterByName(mLastCharacter);
+ if(c)
+ {
+ rmt::Vector charPos;
+ c->GetPosition( &charPos );
+ rmt::Vector targetPos;
+ mTarget->GetPosition( &targetPos );
+ rmt::Vector targetToPed;
+ targetToPed.Sub( charPos, targetPos );
+
+ float distNormal = targetToPed.Magnitude() / (PED_MAX_DIST - PED_MIN_DIST); //Square root!
+
+ CLAMP_TO_ONE( distNormal );
+
+ distNormal = 1.0f - distNormal;
+ return distNormal;
+ }
+ }
+ return 0.0f;
+}
+
+//=============================================================================
+// WalkerCam::GetWatcherName
+//=============================================================================
+// Description: the name of the class for the watcher or other debug purposes
+//
+// Parameters: NONE
+//
+// Return: const char* - the name of the class
+//
+//=============================================================================
+#ifdef DEBUGWATCH
+const char* WalkerCam::GetWatcherName() const
+{
+ return "WalkerCam";
+}
+#endif
diff --git a/game/code/camera/walkercam.h b/game/code/camera/walkercam.h
new file mode 100644
index 0000000..9747f9d
--- /dev/null
+++ b/game/code/camera/walkercam.h
@@ -0,0 +1,224 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: walkercam.h
+//
+// Description: Blahblahblah
+//
+// History: 25/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef WALKERCAM_H
+#define WALKERCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+#include <camera/walkercamdata.h>
+
+//========================================
+// Forward References
+//========================================
+class ISuperCamTarget;
+class Character;
+class AnimatedIcon;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class WalkerCam : public SuperCam
+{
+public:
+ enum
+ {
+ MAX_TARGETS = 3,
+ IN_COLLISION = SUPERCAM_END
+ };
+
+ WalkerCam();
+ virtual ~WalkerCam();
+
+ //Update: Called when you want the super cam to update its state.
+ void Update( unsigned int milliseconds );
+ void UpdateForPhysics( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ virtual Type GetType();
+
+ //This loads the off-line created settings for the camera.
+ //It is passed in as a byte stream of some data of known size.
+ void LoadSettings( unsigned char* settings );
+
+ //These are for favourable support of this command
+ void SetTarget( ISuperCamTarget* target );
+ void AddTarget( ISuperCamTarget* target );
+
+ unsigned int GetNumTargets() const;
+
+ void SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset );
+ float GetCollisionRadius() const { return GetNearPlane(); };
+
+ virtual float GetIntersectionRadius() const { return mMagnitude; };
+
+ void SetCollectibleLocation( AnimatedIcon* icon ) { mCollectible = icon; };
+
+ #ifdef DEBUGWATCH
+ virtual const char* GetWatcherName() const;
+ #endif
+
+protected:
+ void OnDisplay() const;
+ virtual void OnInit() { InitMyController(); };
+
+private:
+ ISuperCamTarget* mTarget;
+
+ WalkerCamData mData;
+
+ float mFOVDelta;
+ float mMagnitude;
+ float mMagnitudeDelta;
+ float mElevation;
+ float mElevationDelta;
+
+ rmt::Vector mTargetPos;
+ rmt::Vector mTargetPosDelta;
+
+ rmt::Vector mPosition;
+ rmt::Vector mDesiredPositionDelta;
+
+ bool mIsAirborn;
+ int mLandingCountdown;
+
+ float mCameraHeight;
+
+ const rmt::Vector* mCollisionOffset;
+ unsigned int mNumCollisions;
+ rmt::Vector mGroundOffset;
+
+ unsigned int mLastCollisionFrame;
+
+ rmt::Vector mPlaneIntersectPoint;
+
+ tUID mLastCharacter;
+ int mPedTimeAccum;
+
+ float mXAxis;
+
+ rmt::Vector mOldPos;
+ float mOldMagnitude;
+
+ AnimatedIcon* mCollectible;
+
+ void UpdatePositionNormal( unsigned int milliseconds, float timeMod );
+ void UpdatePositionInCollision( unsigned int milliseconds, float timeMod );
+ bool UpdatePositionOneCollsion( unsigned int milliseconds, float timeMod, unsigned int collisionIndex = 0 );
+ void UpdatePositionMultipleCollision( unsigned int milliseconds, float timeMod );
+
+ //These functions are to allow real-time control of the settings of
+ //the supercam.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+ void GetTargetPosition( rmt::Vector* position, bool withOffset = true ) const;
+
+ float IsTargetNearPed( unsigned int milliseconds );
+
+ bool IsPushingStick();
+ bool IsStickStill();
+ bool PushingTheWrongWay();
+
+ //Prevent wasteful constructor creation.
+ WalkerCam( const WalkerCam& walkercam );
+ WalkerCam& operator=( const WalkerCam& walkercam );
+};
+
+//Subclass, just ignore this.
+class ComedyCam : public WalkerCam
+{
+public:
+ ComedyCam(){};
+ virtual ~ComedyCam() {};
+
+ virtual const char* const GetName() const { return "COMEDY_CAM"; };
+ virtual Type GetType() { return SuperCam::COMEDY_CAM; };
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// WalkerCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+inline const char* const WalkerCam::GetName() const
+{
+ return "WALKER_CAM";
+}
+
+//=============================================================================
+// WalkerCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type WalkerCam::GetType()
+{
+ return WALKER_CAM;
+}
+
+//=============================================================================
+// WalkerCam::GetNumTargets
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int WalkerCam::GetNumTargets() const
+{
+ return (mTarget != NULL) ? 1 : 0;
+}
+
+//=============================================================================
+// WalkerCam::SetCollisionOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCam::SetCollisionOffset( const rmt::Vector* offset, unsigned int numCollisions, const rmt::Vector& groundOffset )
+{
+ mCollisionOffset = offset;
+ mNumCollisions = numCollisions;
+ mGroundOffset = groundOffset;
+}
+
+
+#endif //WALKERCAM_H
diff --git a/game/code/camera/walkercamdata.h b/game/code/camera/walkercamdata.h
new file mode 100644
index 0000000..621e461
--- /dev/null
+++ b/game/code/camera/walkercamdata.h
@@ -0,0 +1,638 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: walkercamdata.h
+//
+// Description: Blahblahblah
+//
+// History: 25/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef WALKERCAMDATA_H
+#define WALKERCAMDATA_H
+
+//========================================
+// Nested Includes
+//========================================
+//These are included in the precompiled headers on XBOX and GC
+#include <radmath/radmath.hpp>
+#include <camera/supercamconstants.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class WalkerCamData
+{
+public:
+ WalkerCamData();
+ virtual ~WalkerCamData() {};
+
+ void SetLag( float lag );
+ float GetLag() const;
+
+ void SetMinFOV( float min );
+ float GetMinFOV() const;
+
+ void SetMaxFOV( float max );
+ float GetMaxFOV() const;
+
+ void SetFOVLag( float lag );
+ float GetFOVLag() const;
+
+ void SetMinMagnitude( float mag );
+ float GetMinMagnitude() const;
+
+ void SetMaxMagnitude( float mag );
+ float GetMaxMagnitude() const;
+
+ void SetElevation( float elev );
+ float GetElevation() const;
+
+ void SetRotation( float rot );
+ float GetRotation() const;
+
+ void SetMagnitude( float mag );
+ float GetMagnitude() const;
+
+ void SetRotationIncrement( float inc );
+ float GetRotationIncrement() const;
+
+ //This is the offset from the target
+ void GetTargetOffset( rmt::Vector* offset ) const;
+ void SetTargetOffset( rmt::Vector offset );
+
+ float GetTargetLag() const;
+ void SetTargetLag( float lag );
+
+ float GetTargetJumpLag() const;
+ void SetTargetJumpLag( float lag );
+
+ unsigned int GetLandingTransitionTime() const;
+ void SetLandingTransitionTime( unsigned int time );
+
+ float GetUpAngle() const;
+ void SetUpAngle( float angle );
+
+ float GetCollisionLag() const;
+ void SetCollisionLag( float lag );
+
+ float mLag;
+
+ float mMinFOV;
+ float mMaxFOV;
+ float mFOVLag;
+
+ float mMinMagnitude;
+ float mMaxMagnitude;
+
+ float mElevation;
+ float mRotation;
+ float mMagnitude;
+
+ float mRotationIncrement;
+
+ //This is the target offset.
+ rmt::Vector mTargetOffset;
+
+ float mTargetLag;
+ float mJumpLag;
+
+ float mUpAngle;
+
+ unsigned int mLandingTransitionTime;
+
+ float mCollisionLag;
+
+private:
+ //Prevent wasteful constructor creation.
+ WalkerCamData( const WalkerCamData& walkercamdata );
+ WalkerCamData& operator=( const WalkerCamData& walkercamdata );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// WalkerCamData::WalkerCamData
+//=============================================================================
+// Description: Constructor
+//
+// Parameters: ()
+//
+// Return: nothing
+//
+//=============================================================================
+inline WalkerCamData::WalkerCamData() :
+ mLag( 0.08f ),
+ mMinFOV( SUPERCAM_DEFAULT_MIN_FOV ), //Fudge...
+ mMaxFOV( SUPERCAM_DEFAULT_MAX_FOV ), //Fudge...
+ mFOVLag( SUPERCAM_DEFAULT_FOV_LAG ),
+ mMinMagnitude( 4.26f ),
+ mMaxMagnitude( 9.0f ),
+ mElevation( 1.36f ),
+ mRotation( rmt::PI_BY2 ),
+ mMagnitude( ((mMaxMagnitude - mMinMagnitude) / 2.0f) + mMinMagnitude ),
+ mRotationIncrement( 0.4f ),
+ mTargetLag( 0.044f ),
+ mJumpLag( 0.3f ),
+ mUpAngle( 2.44f ),
+ mLandingTransitionTime( 2000 ),
+ mCollisionLag( 0.15f )
+{
+ mTargetOffset.Set( 0.0f, 1.0f, 0.0f );
+}
+
+//=============================================================================
+// WalkerCamData::SetLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetLag( float lag )
+{
+ mLag = lag;
+}
+
+//=============================================================================
+// WalkerCamData::GetLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetLag() const
+{
+ return mLag;
+}
+
+//=============================================================================
+// WalkerCamData::SetMinFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float min )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetMinFOV( float min )
+{
+ mMinFOV = min;
+}
+
+//=============================================================================
+// WalkerCamData::GetMinFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetMinFOV() const
+{
+ return mMinFOV;
+}
+
+//=============================================================================
+// WalkerCamData::SetMaxFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float max )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetMaxFOV( float max )
+{
+ mMaxFOV = max;
+}
+
+//=============================================================================
+// WalkerCamData::GetMaxFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetMaxFOV() const
+{
+ return mMaxFOV;
+}
+
+//=============================================================================
+// WalkerCamData::SetFOVLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetFOVLag( float lag )
+{
+ mFOVLag = lag;
+}
+
+//=============================================================================
+// WalkerCamData::GetFOVLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetFOVLag() const
+{
+ return mFOVLag;
+}
+
+//=============================================================================
+// WalkerCamData::SetMinMagnitude
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float mag )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetMinMagnitude( float mag )
+{
+ mMinMagnitude = mag;
+}
+
+//=============================================================================
+// WalkerCamData::GetMinMagnitude
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetMinMagnitude() const
+{
+ return mMinMagnitude;
+}
+
+//=============================================================================
+// WalkerCamData::SetMaxMagnitude
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float mag )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetMaxMagnitude( float mag )
+{
+ mMaxMagnitude = mag;
+}
+
+//=============================================================================
+// WalkerCamData::GetMaxMagnitude
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetMaxMagnitude() const
+{
+ return mMaxMagnitude;
+}
+
+//=============================================================================
+// WalkerCamData::SetElevation
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float elev )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetElevation( float elev )
+{
+ mElevation = elev;
+}
+
+//=============================================================================
+// WalkerCamData::GetElevation
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetElevation() const
+{
+ return mElevation;
+}
+
+//=============================================================================
+// WalkerCamData::SetRotation
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float rot )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetRotation( float rot )
+{
+ mRotation = rot;
+}
+
+//=============================================================================
+// WalkerCamData::GetRotation
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetRotation() const
+{
+ return mRotation;
+}
+
+//=============================================================================
+// WalkerCamData::SetMagnitude
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float mag )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetMagnitude( float mag )
+{
+ mMagnitude = mag;
+}
+
+//=============================================================================
+// WalkerCamData::GetMagnitude
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetMagnitude() const
+{
+ return mMagnitude;
+}
+
+//=============================================================================
+// WalkerCamData::SetRotationIncrement
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float inc )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetRotationIncrement( float inc )
+{
+ mRotationIncrement = inc;
+}
+
+//=============================================================================
+// WalkerCamData::GetRotationIncrement
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetRotationIncrement() const
+{
+ return mRotationIncrement;
+}
+
+
+//=============================================================================
+// WalkerCamData::GetTargetOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* offset )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::GetTargetOffset( rmt::Vector* offset ) const
+{
+ *offset = mTargetOffset;
+}
+
+//=============================================================================
+// WalkerCamData::SetTargetOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector offset )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetTargetOffset( rmt::Vector offset )
+{
+ mTargetOffset = offset;
+}
+
+//=============================================================================
+// WalkerCamData::GetTargetLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetTargetLag() const
+{
+ return mTargetLag;
+}
+
+//=============================================================================
+// WalkerCamData::SetTargetLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetTargetLag( float lag )
+{
+ mTargetLag = lag;
+}
+
+//=============================================================================
+// WalkerCamData::GetTargetJumpLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetTargetJumpLag() const
+{
+ return mJumpLag;
+}
+
+//=============================================================================
+// WalkerCamData::SetTargetJumpLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetTargetJumpLag( float lag )
+{
+ mJumpLag = lag;
+}
+
+//=============================================================================
+// WalkerCamData::GetLandingTransitionTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned int
+//
+//=============================================================================
+inline unsigned int WalkerCamData::GetLandingTransitionTime() const
+{
+ return mLandingTransitionTime;
+}
+
+//=============================================================================
+// WalkerCamData::SetLandingTransitionTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int time )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetLandingTransitionTime( unsigned int time )
+{
+ mLandingTransitionTime = time;
+}
+
+//=============================================================================
+// WalkerCamData::GetUpAngle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetUpAngle() const
+{
+ return mUpAngle;
+}
+
+//=============================================================================
+// WalkerCamData::SetUpAngle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float angle )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetUpAngle( float angle )
+{
+ mUpAngle = angle;
+}
+
+//=============================================================================
+// WalkerCamData::GetCollisionLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float WalkerCamData::GetCollisionLag() const
+{
+ return mCollisionLag;
+}
+
+//=============================================================================
+// WalkerCamData::SetCollisionLag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float lag )
+//
+// Return: void
+//
+//=============================================================================
+inline void WalkerCamData::SetCollisionLag( float lag )
+{
+ mCollisionLag = lag;
+}
+
+
+#endif //WALKERCAMDATA_H
diff --git a/game/code/camera/walkercamdatachunk.h b/game/code/camera/walkercamdatachunk.h
new file mode 100644
index 0000000..2aef74a
--- /dev/null
+++ b/game/code/camera/walkercamdatachunk.h
@@ -0,0 +1,53 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: walkercamdatachunk.h
+//
+// Description: Blahblahblah
+//
+// History: 17/06/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef WALKERCAMDATACHUNK_H
+#define WALKERCAMDATACHUNK_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/entity.hpp>
+#include <radmath/radmath.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class WalkerCamDataChunk : public tEntity
+{
+public:
+ WalkerCamDataChunk() {};
+ virtual ~WalkerCamDataChunk() {};
+
+ unsigned int mID;
+
+ float mMinMagnitude;
+ float mMaxMagnitude;
+ float mElevation;
+
+ rmt::Vector mTargetOffset;
+
+private:
+
+ //Prevent wasteful constructor creation.
+ WalkerCamDataChunk( const WalkerCamDataChunk& walkercamdatachunk );
+ WalkerCamDataChunk& operator=( const WalkerCamDataChunk& walkercamdatachunk );
+};
+
+
+#endif //WALKERCAMDATACHUNK_H
diff --git a/game/code/camera/wrecklesscam.cpp b/game/code/camera/wrecklesscam.cpp
new file mode 100644
index 0000000..c642482
--- /dev/null
+++ b/game/code/camera/wrecklesscam.cpp
@@ -0,0 +1,344 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: wrecklesscam.cpp
+//
+// Description: Implement WrecklessCam
+//
+// History: 25/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+//These are included in the precompiled headers on XBOX and GC
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/WrecklessCam.h>
+#include <camera/isupercamtarget.h>
+
+#include <events/eventmanager.h>
+#include <events/eventenum.h>
+
+#include <meta/eventlocator.h>
+
+#include <mission/gameplaymanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// WrecklessCam::WrecklessCam
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WrecklessCam::WrecklessCam() :
+ mNumTargets( 0 ),
+ mActiveTarget( 0 ),
+ mFOVDelta( 0 ),
+ mMaxFOV( 1.5707f ),
+ mMinFOV( 0.5f ),
+ mMaxDistance( 35.0f ),
+ mMinDistance( 10.0f ),
+ mLag( 0.08f )
+{
+ unsigned int i;
+ for ( i = 0; i < MAX_TARGETS; ++i )
+ {
+ mTargets[ i ] = NULL;
+ }
+}
+
+//==============================================================================
+// WrecklessCam::~WrecklessCam
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WrecklessCam::~WrecklessCam()
+{
+}
+
+//=============================================================================
+// WrecklessCam::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void WrecklessCam::Update( unsigned int milliseconds )
+{
+ float timeMod = 1.0f;
+
+ timeMod = (float)milliseconds / 16.0f;
+
+ rmt::Vector position;
+ mEventListener.GetLastPosition( &position );
+
+ if ( position != rmt::Vector(0.0f, 0.0f, 0.0f) )
+ {
+ //Use this position for placing the camera.
+ }
+ else
+ {
+ //Get the target position.
+ mTargets[ mActiveTarget ]->GetPosition( &position );
+
+ //HACK
+ position.y += 10.0f;
+ }
+
+ rmt::Vector targetPos;
+ mTargets[ mActiveTarget ]->GetPosition( &targetPos );
+
+ if ( GetFlag( (Flag)CUT ) )
+ {
+ mFOV = mMaxFOV;
+ SetFlag( (Flag)CUT, false );
+ }
+
+ rmt::Vector camToTarg;
+ camToTarg.Sub( position, targetPos );
+ float dist = camToTarg.Magnitude();
+ DoWrecklessZoom( dist, mMinDistance, mMaxDistance, mMinFOV, mMaxFOV, mFOV, mFOVDelta, mLag, timeMod );
+
+ SetFOV( mFOV );
+
+ SetCameraValues( milliseconds, position, targetPos );
+}
+
+//=============================================================================
+// WrecklessCam::SetTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+void WrecklessCam::SetTarget( ISuperCamTarget* target )
+{
+ rAssert( target );
+
+ //First test to make sure we don't already have this target.
+ if( mNumTargets != 0 )
+ {
+ unsigned int i;
+ for ( i = 0; i < mNumTargets; ++i )
+ {
+ if ( mTargets[ i ] == target )
+ {
+ //Already got one!
+ mActiveTarget = i;
+ return;
+ }
+ }
+ }
+
+ //This is going to be the first target
+ mTargets[ mNumTargets ] = target;
+ mActiveTarget = mNumTargets;
+ mNumTargets = 1;
+}
+
+//=============================================================================
+// WrecklessCam::AddTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ISuperCamTarget* target )
+//
+// Return: void
+//
+//=============================================================================
+void WrecklessCam::AddTarget( ISuperCamTarget* target )
+{
+ rAssert( mNumTargets < MAX_TARGETS );
+ rAssert( target );
+
+ if ( mNumTargets < MAX_TARGETS )
+ {
+ //Add the target
+ mTargets[ mNumTargets ] = target;
+
+ ++mNumTargets;
+ }
+}
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// WrecklessCam::OnInit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WrecklessCam::OnInit()
+{
+ mEventListener.SetPlayerID( GetPlayerID() );
+
+ EventManager* evtMngr = GetEventManager();
+ evtMngr->AddListener( GetEventListener(), (EventEnum)(EVENT_LOCATOR + LocatorEvent::CAMERA_CUT) );
+
+ if ( !GetGameplayManager()->mIsDemo )
+ {
+ //Find all the CUT_CAM events and make them active.
+ p3d::inventory->PushSection();
+
+ if ( GetGameplayManager()->IsSuperSprint() || GetGameplayManager()->GetCurrentLevelIndex() > RenderEnums::L3 ) //Fucking hack. It's this or change the art pipes.
+ {
+ p3d::inventory->SelectSection( "Level" );
+ }
+ else
+ {
+ p3d::inventory->SelectSection( "Default" );
+ }
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+ tInventory::Iterator<EventLocator> it;
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ EventLocator* evtLoc = it.First();
+
+ while( evtLoc )
+ {
+ if ( evtLoc->GetEventType() == LocatorEvent::CAMERA_CUT )
+ {
+ evtLoc->SetFlag( Locator::ACTIVE, true );
+ }
+
+ evtLoc = it.Next();
+ }
+
+ p3d::inventory->PopSection();
+ }
+}
+
+//=============================================================================
+// WrecklessCam::OnShutdown
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WrecklessCam::OnShutdown()
+{
+ GetEventManager()->RemoveAll( GetEventListener() );
+
+ if ( !GetGameplayManager()->mIsDemo )
+ {
+ //Find all the CUT_CAM events and make them inactive.
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( "Default" );
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+ tInventory::Iterator<EventLocator> it;
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ EventLocator* evtLoc = it.First();
+
+ while( evtLoc )
+ {
+ if ( evtLoc->GetEventType() == LocatorEvent::CAMERA_CUT )
+ {
+ evtLoc->SetFlag( Locator::ACTIVE, false );
+ }
+
+ evtLoc = it.Next();
+ }
+
+ p3d::inventory->PopSection();
+ }
+}
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//=============================================================================
+// WrecklessCam::OnRegisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WrecklessCam::OnRegisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ char nameSpace[256];
+ sprintf( nameSpace, "SuperCam\\Player%d\\Wreckless", GetPlayerID() );
+
+ radDbgWatchAddFloat( &mMinFOV, "Min FOV", nameSpace, NULL, NULL, 0.0f, rmt::PI );
+ radDbgWatchAddFloat( &mMaxFOV, "Max FOV", nameSpace, NULL, NULL, 0.0f, rmt::PI );
+ radDbgWatchAddFloat( &mMinDistance, "Min Dist", nameSpace, NULL, NULL, 0.0f, 100.0f );
+ radDbgWatchAddFloat( &mMaxDistance, "Max Dist", nameSpace, NULL, NULL, 0.0f, 100.0f );
+ radDbgWatchAddFloat( &mLag, "Lag", nameSpace, NULL, NULL, 0.0f, 1.0f );
+#endif
+}
+
+//=============================================================================
+// WrecklessCam::OnUnregisterDebugControls
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WrecklessCam::OnUnregisterDebugControls()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mMinFOV );
+ radDbgWatchDelete( &mMaxFOV );
+ radDbgWatchDelete( &mMinDistance );
+ radDbgWatchDelete( &mMaxDistance );
+ radDbgWatchDelete( &mLag );
+#endif
+} \ No newline at end of file
diff --git a/game/code/camera/wrecklesscam.h b/game/code/camera/wrecklesscam.h
new file mode 100644
index 0000000..7395b4a
--- /dev/null
+++ b/game/code/camera/wrecklesscam.h
@@ -0,0 +1,162 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: wrecklesscam.h
+//
+// Description: Blahblahblah
+//
+// History: 25/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef WRECKLESSCAM_H
+#define WRECKLESSCAM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <camera/supercam.h>
+#include <camera/wrecklesseventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+class ISuperCamTarget;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class WrecklessCam : public SuperCam
+{
+public:
+ enum
+ {
+ MAX_TARGETS = 3
+ };
+
+ WrecklessCam();
+ virtual ~WrecklessCam();
+
+ //Update: Called when you want the super cam to update its state.
+ virtual void Update( unsigned int milliseconds );
+
+ //Returns the name of the super cam.
+ //This can be used in the FE or debug info
+ virtual const char* const GetName() const;
+
+ virtual Type GetType();
+
+ //These are for favourable support of this command
+ virtual void SetTarget( ISuperCamTarget* target );
+ virtual void AddTarget( ISuperCamTarget* target );
+
+ virtual unsigned int GetNumTargets() const;
+
+ EventListener* GetEventListener();
+
+protected:
+ virtual void OnInit();
+ virtual void OnShutdown();
+
+private:
+ ISuperCamTarget* mTargets[ MAX_TARGETS ];
+ unsigned int mNumTargets;
+ unsigned int mActiveTarget;
+
+ WrecklessEventListener mEventListener;
+
+ float mFOV;
+ float mFOVDelta;
+
+ float mMaxFOV;
+ float mMinFOV;
+ float mMaxDistance;
+ float mMinDistance;
+ float mLag;
+
+ //These functions are to allow real-time control of the settings of
+ //the supercam.
+ virtual void OnRegisterDebugControls();
+ virtual void OnUnregisterDebugControls();
+
+ //Prevent wasteful constructor creation.
+ WrecklessCam( const WrecklessCam& wrecklesscam );
+ WrecklessCam& operator=( const WrecklessCam& wrecklesscam );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// WrecklessCam::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const char* const
+//
+//=============================================================================
+inline const char* const WrecklessCam::GetName() const
+{
+ return "WRECKLESS_CAM";
+}
+
+//=============================================================================
+// WrecklessCam::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Type
+//
+//=============================================================================
+inline SuperCam::Type WrecklessCam::GetType()
+{
+ return WRECKLESS_CAM;
+}
+
+//=============================================================================
+// WrecklessCam::GetNumTargets
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int WrecklessCam::GetNumTargets() const
+{
+ return mNumTargets;
+}
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// WrecklessCam::GetEventListener
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: EventListener
+//
+//=============================================================================
+inline EventListener* WrecklessCam::GetEventListener()
+{
+ return &mEventListener;
+}
+
+#endif //WRECKLESSCAM_H
diff --git a/game/code/camera/wrecklesseventlistener.cpp b/game/code/camera/wrecklesseventlistener.cpp
new file mode 100644
index 0000000..e1f7abb
--- /dev/null
+++ b/game/code/camera/wrecklesseventlistener.cpp
@@ -0,0 +1,98 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: WrecklessEventListener.cpp
+//
+// Description: Implement WrecklessEventListener
+//
+// History: 13/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/WrecklessEventListener.h>
+#include <meta/eventlocator.h>
+#include <meta/locatorevents.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// WrecklessEventListener::WrecklessEventListener
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WrecklessEventListener::WrecklessEventListener() :
+ mPlayerID( -1 )
+{
+ mLastPosition.Set( 0.0f, 0.0f, 0.0f );
+}
+
+//==============================================================================
+// WrecklessEventListener::~WrecklessEventListener
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WrecklessEventListener::~WrecklessEventListener()
+{
+}
+
+//=============================================================================
+// WrecklessEventListener::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void WrecklessEventListener::HandleEvent( EventEnum id, void* pEventData )
+{
+ if ( id == (EVENT_LOCATOR + LocatorEvent::CAMERA_CUT) )
+ {
+ EventLocator* evtLoc = static_cast<EventLocator*>(pEventData);
+ rAssert( evtLoc );
+
+ if ( evtLoc->GetPlayerID() == (unsigned int) mPlayerID && evtLoc->GetPlayerEntered() )
+ {
+ evtLoc->GetLocation( &mLastPosition );
+ }
+ }
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/camera/wrecklesseventlistener.h b/game/code/camera/wrecklesseventlistener.h
new file mode 100644
index 0000000..4121d3e
--- /dev/null
+++ b/game/code/camera/wrecklesseventlistener.h
@@ -0,0 +1,91 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: wrecklesseventlistener.h
+//
+// Description: Blahblahblah
+//
+// History: 13/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef WRECKLESSEVENTLISTENER_H
+#define WRECKLESSEVENTLISTENER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <events/eventlistener.h>
+
+#include <radmath/radmath.hpp>
+
+//========================================
+// Forward References
+//========================================
+class Locator;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class WrecklessEventListener : public EventListener
+{
+public:
+ WrecklessEventListener();
+ virtual ~WrecklessEventListener();
+
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ void GetLastPosition( rmt::Vector* pos );
+
+ void SetPlayerID( int playerID );
+
+private:
+
+ rmt::Vector mLastPosition;
+ int mPlayerID;
+
+ //Prevent wasteful constructor creation.
+ WrecklessEventListener( const WrecklessEventListener& wrecklesseventlistener );
+ WrecklessEventListener& operator=( const WrecklessEventListener& wrecklesseventlistener );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// WrecklessEventListener::GetLastPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* pos )
+//
+// Return: void
+//
+//=============================================================================
+inline void WrecklessEventListener::GetLastPosition( rmt::Vector* pos )
+{
+ *pos = mLastPosition;
+}
+
+//=============================================================================
+// WrecklessEventListener::SetPlayerID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID )
+//
+// Return: void
+//
+//=============================================================================
+inline void WrecklessEventListener::SetPlayerID( int playerID )
+{
+ mPlayerID = playerID;
+}
+
+#endif //WRECKLESSEVENTLISTENER_H
diff --git a/game/code/cards/allcards.cpp b/game/code/cards/allcards.cpp
new file mode 100644
index 0000000..65d0dd9
--- /dev/null
+++ b/game/code/cards/allcards.cpp
@@ -0,0 +1,5 @@
+#include <cards/bonuscard.cpp>
+#include <cards/card.cpp>
+#include <cards/cardgallery.cpp>
+#include <cards/cardsdb.cpp>
+#include <cards/collectorcard.cpp>
diff --git a/game/code/cards/bonuscard.cpp b/game/code/cards/bonuscard.cpp
new file mode 100644
index 0000000..28e716f
--- /dev/null
+++ b/game/code/cards/bonuscard.cpp
@@ -0,0 +1,72 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: BonusCard
+//
+// Description: Implementation of the BonusCard class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/06 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <cards/bonuscard.h>
+
+//===========================================================================
+// Local Constants
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// BonusCard::BonusCard
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+BonusCard::BonusCard
+(
+ unsigned int ID,
+ unsigned int level,
+ unsigned int levelID,
+ tUID cardName,
+ const eQuoteID* quotes,
+ int numQuotes
+)
+: Card( ID, level, levelID, cardName, quotes, numQuotes )
+{
+}
+
+//===========================================================================
+// BonusCard::~BonusCard
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+BonusCard::~BonusCard()
+{
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
diff --git a/game/code/cards/bonuscard.h b/game/code/cards/bonuscard.h
new file mode 100644
index 0000000..27d9af2
--- /dev/null
+++ b/game/code/cards/bonuscard.h
@@ -0,0 +1,63 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: BonusCard
+//
+// Description: Interface for the BonusCard class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/06 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef BONUSCARD_H
+#define BONUSCARD_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <cards/card.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class BonusCard : public Card
+{
+public:
+ BonusCard( unsigned int ID,
+ unsigned int level,
+ unsigned int levelID,
+ tUID cardName,
+ const eQuoteID* quotes,
+ int numQuotes );
+
+ virtual ~BonusCard();
+
+ virtual eCardType GetType() const { return BONUS_CARD; }
+
+private:
+
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ BonusCard( const BonusCard& );
+ BonusCard& operator= ( const BonusCard& );
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+};
+
+#endif // BONUSCARD_H
diff --git a/game/code/cards/card.cpp b/game/code/cards/card.cpp
new file mode 100644
index 0000000..8794ec4
--- /dev/null
+++ b/game/code/cards/card.cpp
@@ -0,0 +1,98 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Card
+//
+// Description: Implementation of the Card class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/06 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <string.h>
+#include <cards/card.h>
+
+//===========================================================================
+// Local Constants
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// Card::Card
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+Card::Card
+(
+ unsigned int ID,
+ unsigned int level,
+ unsigned int levelID,
+ tUID cardName,
+ const eQuoteID* quotes,
+ int numQuotes
+)
+: m_ID( ID ),
+ m_level( level ),
+ m_levelID( levelID ),
+ m_cardName( cardName ),
+ m_numQuotes( 0 )
+{
+ rAssert( quotes != NULL );
+
+ for( int i = 0; i < numQuotes; i++ )
+ {
+ rAssert( quotes[ i ] < NUM_QUOTE_IDS );
+
+ m_quotes[ i ] = quotes[ i ];
+
+ if( m_quotes[ i ] != EMPTY_QUOTE )
+ {
+ m_numQuotes++;
+ }
+ }
+}
+
+//===========================================================================
+// Card::~Card
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+Card::~Card()
+{
+}
+
+eQuoteID
+Card::GetQuoteID( int index ) const
+{
+ rAssert( static_cast<unsigned int>( index ) < MAX_NUM_QUOTES );
+
+ return m_quotes[ index ];
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
diff --git a/game/code/cards/card.h b/game/code/cards/card.h
new file mode 100644
index 0000000..216fcb7
--- /dev/null
+++ b/game/code/cards/card.h
@@ -0,0 +1,130 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Card
+//
+// Description: Interface for the Card class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/06 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef CARD_H
+#define CARD_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <p3d/p3dtypes.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+enum eCardType
+{
+ COLLECTOR_CARD,
+ BONUS_CARD,
+
+ NUM_CARD_TYPES
+};
+
+enum eQuoteID
+{
+ EMPTY_QUOTE = -1,
+
+ QUOTE_ANNOUNCER,
+ QUOTE_APU,
+ QUOTE_BART,
+ QUOTE_BROCKMAN,
+ QUOTE_BURNS,
+ QUOTE_CARL,
+ QUOTE_CHILD,
+ QUOTE_DR_WOLFF,
+ QUOTE_GIL,
+ QUOTE_HOMER,
+ QUOTE_JASPER,
+ QUOTE_JIMBO,
+ QUOTE_KANG,
+ QUOTE_KRUSTY,
+ QUOTE_LENNY,
+ QUOTE_LISA,
+ QUOTE_MAGGIE,
+ QUOTE_MANJULA,
+ QUOTE_MARGE,
+ QUOTE_MEYERS,
+ QUOTE_MILHOUSE,
+ QUOTE_MOTHER,
+ QUOTE_MR_SPARKLE,
+ QUOTE_OTTO,
+ QUOTE_PHOTOGRAPHER,
+ QUOTE_RALPH,
+ QUOTE_SKINNER,
+ QUOTE_SMITHERS,
+ QUOTE_STACY,
+ QUOTE_WIGGUM,
+ QUOTE_WILLIE,
+
+ NUM_QUOTE_IDS
+};
+
+const unsigned int MAX_NUM_QUOTES = 3; // max. number of quotes per card
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class Card
+{
+public:
+ Card( unsigned int ID,
+ unsigned int level,
+ unsigned int levelID,
+ tUID cardName,
+ const eQuoteID* quotes,
+ int numQuotes );
+
+ virtual ~Card();
+
+ // Accessors to card info
+ unsigned int GetID() const { return m_ID; }
+ unsigned int GetLevel() const { return m_level; }
+ unsigned int GetLevelID() const { return m_levelID; }
+ tUID GetCardName() const { return m_cardName; }
+ eQuoteID GetQuoteID( int index ) const;
+ int GetNumQuotes() const { return m_numQuotes; }
+
+ // Pure virtual function to be implemented by derived classes
+ virtual eCardType GetType() const = 0;
+
+private:
+
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ Card( const Card& );
+ Card& operator= ( const Card& );
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ unsigned int m_ID; // ID >= 0 (for card image)
+ unsigned int m_level; // level >= 1
+ unsigned int m_levelID; // levelID >= 1
+
+ tUID m_cardName; // hashed ID for card name
+
+ eQuoteID m_quotes[ MAX_NUM_QUOTES ];
+ int m_numQuotes;
+
+};
+
+#endif // CARD_H
diff --git a/game/code/cards/cardgallery.cpp b/game/code/cards/cardgallery.cpp
new file mode 100644
index 0000000..d82fc74
--- /dev/null
+++ b/game/code/cards/cardgallery.cpp
@@ -0,0 +1,639 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CardGallery
+//
+// Description: Implementation of the CardGallery class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/06 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <cards/cardgallery.h>
+#include <cards/cardsdb.h>
+#include <cards/card.h>
+
+#include <data/gamedatamanager.h>
+#include <memory/srrmemory.h>
+
+#include <raddebugwatch.hpp>
+
+// Static pointer to instance of singleton.
+CardGallery* CardGallery::spInstance = NULL;
+
+#ifdef DEBUGWATCH
+ static const char* WATCHER_NAMESPACE = "Card Gallery";
+ static unsigned int s_wAddCollectedCard;
+ static void AddCardToGallery()
+ {
+ GetCardGallery()->AddCollectedCardByID( s_wAddCollectedCard );
+ }
+
+ static void AddAllCardsToGallery()
+ {
+ GetCardGallery()->AddAllCollectedCards();
+ }
+
+ static void RemoveAllCardsFromGallery()
+ {
+ GetCardGallery()->RemoveAllCollectedCards();
+ }
+#endif
+
+//===========================================================================
+// Local Constants
+//===========================================================================
+
+const unsigned int NUM_BITS_PER_BYTE = 8;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+CardList::CardList()
+: m_numCards( 0 )
+{
+ memset( m_cards, 0, sizeof( m_cards ) );
+}
+
+void
+CardList::Add( Card* card )
+{
+ rAssert( card != NULL );
+ rAssert( m_numCards < static_cast<int>( NUM_CARDS_PER_LEVEL ) );
+
+ unsigned int slot = card->GetLevelID() - 1;
+ rAssert( slot < NUM_CARDS_PER_LEVEL );
+
+ if( m_cards[ slot ] == NULL )
+ {
+ m_cards[ slot ] = card;
+ m_numCards++;
+ }
+ else
+ {
+ rAssertMsg( 0, "WARNING: *** Card already collected!" );
+ }
+}
+
+bool
+CardList::Remove( unsigned int cardID )
+{
+ bool cardFound = false;
+
+ // find card with cardID
+ //
+ for( int i = 0; i < m_numCards; i++ )
+ {
+ rAssert( m_cards[ i ] );
+
+ if( m_cards[ i ]->GetID() == cardID )
+ {
+ // found it! now remove it
+ m_cards[ i ] = NULL;
+ m_numCards--;
+
+ cardFound = true;
+ break;
+ }
+ }
+
+ return cardFound;
+}
+
+void
+CardList::Empty()
+{
+ memset( m_cards, 0, sizeof( m_cards ) );
+ m_numCards = 0;
+}
+
+//==============================================================================
+// CardGallery::CreateInstance
+//==============================================================================
+//
+// Description: - Creates the Card Gallery.
+//
+// Parameters: None.
+//
+// Return: Pointer to the CardGallery.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+CardGallery* CardGallery::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "CardGallery" );
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+ spInstance = new(GMA_PERSISTENT) CardGallery;
+ rAssert( spInstance != NULL );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+ MEMTRACK_POP_GROUP( "CardGallery" );
+ return spInstance;
+}
+
+//==============================================================================
+// CardGallery::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the GUI system.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void CardGallery::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete spInstance;
+ spInstance = NULL;
+}
+
+//==============================================================================
+// CardGallery::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the CardGallery singleton.
+// - Creates the CardGallery if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the CardGallery.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+CardGallery* CardGallery::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+//===========================================================================
+// CardGallery::CardGallery
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CardGallery::CardGallery()
+: m_cardsDB( NULL ),
+ m_numCollectedCards( 0 )
+{
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+#endif
+
+ m_cardsDB = new CardsDB();
+ rAssert( m_cardsDB );
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+#endif
+}
+
+//===========================================================================
+// CardGallery::~CardGallery
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CardGallery::~CardGallery()
+{
+ GetCheatInputSystem()->UnregisterCallback( this );
+
+ if( m_cardsDB != NULL )
+ {
+ delete m_cardsDB;
+ m_cardsDB = NULL;
+ }
+}
+
+//===========================================================================
+// CardGallery::Init
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CardGallery::Init()
+{
+ // load cards into database
+ //
+ rAssert( m_cardsDB );
+ m_cardsDB->LoadCards();
+
+ // determine number of bytes needed for loading/saving
+ //
+ unsigned int numDataBytes = m_cardsDB->GetNumCards() / NUM_BITS_PER_BYTE;
+ if( m_cardsDB->GetNumCards() % NUM_BITS_PER_BYTE > 0 )
+ {
+ numDataBytes++; // round up number of bytes
+ }
+
+ // register collected cards data for loading/saving
+ //
+ GetGameDataManager()->RegisterGameData( this, numDataBytes, "Card Gallery" );
+
+ // register callback for "Collect All Cards" cheat
+ //
+ GetCheatInputSystem()->RegisterCallback( this );
+
+#ifdef DEBUGWATCH
+ radDbgWatchAddUnsignedInt( &s_wAddCollectedCard,
+ "Add Collectible Card",
+ WATCHER_NAMESPACE,
+ (RADDEBUGWATCH_CALLBACK)AddCardToGallery,
+ NULL,
+ 0,
+ MAX_NUM_CARDS - 1 );
+
+ radDbgWatchAddFunction( "Add All Collectible Cards",
+ (RADDEBUGWATCH_CALLBACK)AddAllCardsToGallery,
+ NULL,
+ WATCHER_NAMESPACE );
+
+ radDbgWatchAddFunction( "Remove All Collectible Cards",
+ (RADDEBUGWATCH_CALLBACK)RemoveAllCardsFromGallery,
+ NULL,
+ WATCHER_NAMESPACE );
+#endif
+}
+
+//===========================================================================
+// CardGallery::AddCollectedCardByID
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+Card*
+CardGallery::AddCollectedCardByID( unsigned int cardID )
+{
+ rAssert( m_cardsDB );
+
+ // get card by ID from DB, and add it to collected cards
+ //
+ Card* collectedCard = m_cardsDB->GetCardByID( cardID );
+ if( collectedCard != NULL )
+ {
+ this->AddCollectedCard( collectedCard );
+ }
+
+ return collectedCard;
+}
+
+//===========================================================================
+// CardGallery::AddCollectedCardByName
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+Card*
+CardGallery::AddCollectedCardByName( tUID cardName )
+{
+ rAssert( m_cardsDB );
+
+ // get card by name from DB, and add it to collected cards
+ //
+ Card* collectedCard = m_cardsDB->GetCardByName( cardName );
+ if( collectedCard != NULL )
+ {
+ this->AddCollectedCard( collectedCard );
+ }
+ else
+ {
+ rAssertMsg( false, "ERROR: *** Card not found!" );
+ }
+
+ return collectedCard;
+}
+
+//===========================================================================
+// CardGallery::AddAllCollectedCards
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CardGallery::AddAllCollectedCards()
+{
+ for( unsigned int i = 0; i < MAX_NUM_CARDS; i++ )
+ {
+ rAssert( m_cardsDB != NULL );
+ Card* card = m_cardsDB->GetCardByID( i );
+ if( card != NULL )
+ {
+ this->AddCollectedCard( card );
+ }
+ }
+}
+
+//===========================================================================
+// CardGallery::RemoveAllCollectedCards
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CardGallery::RemoveAllCollectedCards()
+{
+ for( unsigned int i = 0; i < NUM_LEVELS; i++ )
+ {
+ m_collectedCards[ i ].Empty();
+ }
+
+ m_numCollectedCards = 0;
+}
+
+//===========================================================================
+// CardGallery::GetCollectedCards
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+const CardList*
+CardGallery::GetCollectedCards( unsigned int level ) const
+{
+ rAssert( level < NUM_LEVELS );
+ return( &(m_collectedCards[ level ]) );
+}
+
+//=============================================================================
+// CardGallery::IsCardCollected
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tUID cardName )
+//
+// Return: bool
+//
+//=============================================================================
+bool CardGallery::IsCardCollected( tUID cardName ) const
+{
+ bool cardCollected = false;
+
+ rAssert( m_cardsDB );
+ Card* card = m_cardsDB->GetCardByName( cardName );
+ if( card != NULL )
+ {
+ int level = card->GetLevel() - 1;
+ int levelID = card->GetLevelID() - 1;
+ if( m_collectedCards[ level ].m_cards[ levelID ] != NULL )
+ {
+ cardCollected = true;
+ }
+ }
+
+ return cardCollected;
+}
+
+//=============================================================================
+// CardGallery::GetNumCardDecksCompleted
+//=============================================================================
+// Description: Get number of card decks completed. Collecting all cards in
+// a given level constitutes a complete deck.
+//
+// Parameters: none
+//
+// Return: number of card decks completed
+//
+//=============================================================================
+int CardGallery::GetNumCardDecksCompleted() const
+{
+ int numDecksCompleted = 0;
+
+ for( unsigned int i = 0; i < NUM_LEVELS; i++ )
+ {
+ if( this->IsCardDeckComplete( i ) )
+ {
+ numDecksCompleted++;
+ }
+ }
+
+ return numDecksCompleted;
+}
+
+//=============================================================================
+// CardGallery::IsCardDeckComplete
+//=============================================================================
+// Description: Query whether or not a card deck is complete for a given level.
+//
+// Parameters: level index
+//
+// Return: true/false
+//
+//=============================================================================
+bool CardGallery::IsCardDeckComplete( unsigned int level ) const
+{
+ rAssert( level < NUM_LEVELS );
+
+ return( m_collectedCards[ level ].m_numCards == static_cast<int>( NUM_CARDS_PER_LEVEL ) );
+}
+
+//===========================================================================
+// CardGallery::LoadData
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CardGallery::LoadData( const GameDataByte* dataBuffer,
+ unsigned int numBytes )
+{
+ // remove all collected cards first
+ //
+ this->RemoveAllCollectedCards();
+
+ // add new set of collected cards from loaded data
+ //
+ for( unsigned int i = 0; i < numBytes * NUM_BITS_PER_BYTE; i++ )
+ {
+ unsigned int dataBufferIndex = i / NUM_BITS_PER_BYTE;
+ if( (dataBuffer[ dataBufferIndex ] & (1 << i % NUM_BITS_PER_BYTE)) > 0 )
+ {
+ this->AddCollectedCardByID( i );
+ }
+ }
+}
+
+//===========================================================================
+// CardGallery::SaveData
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CardGallery::SaveData( GameDataByte* dataBuffer,
+ unsigned int numBytes )
+{
+ if( CommandLineOptions::Get( CLO_MEMCARD_CHEAT ) )
+ {
+ // turn on all bits, and save
+ //
+ memset( dataBuffer, ~0, numBytes );
+ }
+ else
+ {
+ // clear data buffer first
+ //
+ for( unsigned int byteIndex = 0; byteIndex < numBytes; byteIndex++ )
+ {
+ dataBuffer[ byteIndex ] = 0;
+ }
+
+ // save collected cards using bit-masking algorithm
+ //
+ for( unsigned int i = 0; i < NUM_LEVELS; i++ )
+ {
+ for( unsigned int j = 0; j < NUM_CARDS_PER_LEVEL; j++ )
+ {
+ if( m_collectedCards[ i ].m_cards[ j ] != NULL )
+ {
+ unsigned int cardID = m_collectedCards[ i ].m_cards[ j ]->GetID();
+
+ unsigned int dataBufferIndex = cardID / NUM_BITS_PER_BYTE;
+ dataBuffer[ dataBufferIndex ] |= (1 << (cardID % NUM_BITS_PER_BYTE));
+ }
+ }
+ }
+ }
+}
+
+//===========================================================================
+// CardGallery::OnCheatEntered
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CardGallery::OnCheatEntered( eCheatID cheatID, bool isEnabled )
+{
+ if( cheatID == CHEAT_ID_UNLOCK_CARDS )
+ {
+ // remove all cards from gallery first
+ //
+ this->RemoveAllCollectedCards();
+
+ // now add all collectible cards to gallery
+ //
+ this->AddAllCollectedCards();
+ }
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+//===========================================================================
+// CardGallery::AddCollectedCard
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CardGallery::AddCollectedCard( Card* newCard )
+{
+ rAssert( newCard != NULL );
+ unsigned int level = newCard->GetLevel() - 1;
+ rAssert( level < NUM_LEVELS );
+ m_collectedCards[ level ].Add( newCard );
+
+ rDebugPrintf( "Card Gallery: Collected Card %d-%d\n",
+ newCard->GetLevel(), newCard->GetLevelID() );
+
+ m_numCollectedCards++;
+}
+
diff --git a/game/code/cards/cardgallery.h b/game/code/cards/cardgallery.h
new file mode 100644
index 0000000..0cf3d52
--- /dev/null
+++ b/game/code/cards/cardgallery.h
@@ -0,0 +1,131 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CardGallery
+//
+// Description: Interface for the CardGallery class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/06 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef CARDGALLERY_H
+#define CARDGALLERY_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <cheats/cheatinputsystem.h>
+
+#include <data/gamedata.h>
+#include <p3d/p3dtypes.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+const unsigned int NUM_LEVELS = 7;
+const unsigned int NUM_CARDS_PER_LEVEL = 7;
+
+class CardsDB;
+class Card;
+
+struct CardList
+{
+ Card* m_cards[ NUM_CARDS_PER_LEVEL ];
+ int m_numCards;
+
+ CardList();
+
+ void Add( Card* card );
+ bool Remove( unsigned int cardID );
+ void Empty();
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class CardGallery : public GameDataHandler,
+ public ICheatEnteredCallback
+{
+public:
+ // Static Methods for accessing this singleton.
+ static CardGallery* CreateInstance();
+ static void DestroyInstance();
+ static CardGallery* GetInstance();
+
+ CardGallery();
+ virtual ~CardGallery();
+
+ // Initialization (involves disc I/O)
+ void Init();
+
+ // Adding collected cards (either by UID name or by card ID)
+ Card* AddCollectedCardByName( tUID cardName );
+ Card* AddCollectedCardByID( unsigned int cardID );
+
+ void AddAllCollectedCards();
+ void RemoveAllCollectedCards();
+
+ // Querying collected cards
+ const CardList* GetCollectedCards( unsigned int level ) const;
+ bool IsCardCollected( tUID cardName ) const;
+
+ // Querying card decks
+ //
+ int GetNumCardDecksCompleted() const;
+ bool IsCardDeckComplete( unsigned int level ) const;
+
+ // Querying cards DB
+ CardsDB* GetCardsDB() const { return m_cardsDB; }
+
+ // Implements Game Data Handler
+ virtual void LoadData( const GameDataByte* dataBuffer,
+ unsigned int numBytes );
+ virtual void SaveData( GameDataByte* dataBuffer,
+ unsigned int numBytes );
+ virtual void ResetData();
+
+ virtual void OnCheatEntered( eCheatID cheatID, bool isEnabled );
+
+private:
+
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CardGallery( const CardGallery& );
+ CardGallery& operator= ( const CardGallery& );
+
+ void AddCollectedCard( Card* newCard );
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ // Pointer to the one and only instance of this singleton.
+ static CardGallery* spInstance;
+
+ CardsDB* m_cardsDB;
+
+ CardList m_collectedCards[ NUM_LEVELS ];
+ int m_numCollectedCards;
+
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline CardGallery* GetCardGallery() { return( CardGallery::GetInstance() ); }
+
+inline void CardGallery::ResetData()
+{
+ this->RemoveAllCollectedCards();
+}
+
+#endif // CARDGALLERY_H
diff --git a/game/code/cards/cards.h b/game/code/cards/cards.h
new file mode 100644
index 0000000..0e2e5a4
--- /dev/null
+++ b/game/code/cards/cards.h
@@ -0,0 +1,124 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Cards
+//
+// Description: Collector Cards Database
+//
+// Authors: Tony Chu (and Simpsons2 Designers)
+//
+// Revisions Date Author Revision
+// 2002/08/14 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef CARDS_H
+#define CARDS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <cards/card.h>
+
+struct CardData
+{
+ char name[ 32 ];
+ unsigned short level;
+ unsigned short levelID;
+ unsigned short imageID;
+ eCardType type;
+ eQuoteID quotes[ MAX_NUM_QUOTES ];
+};
+
+//===========================================================================
+// Cards Database
+//===========================================================================
+
+static const CardData s_cardsDB[] =
+{
+ // SYNTAX: { <Card Name>, <Card Level>, <Card Level ID>, <Card Image ID>, <Card Type> },
+ //
+ // - <Card Name> is the card name (max. 31 characters)
+ // - <Card Level> is the card level (1-7)
+ // - <Card Level ID> is the card level ID
+ // - <Card Image ID> is the ID reference for the associated Scrooby image
+ // - <Card Type> is the card type (either COLLECTOR_CARD or BONUS_CARD)
+ //
+
+/// Level 1
+//
+ { "card11", 1, 1, 0, COLLECTOR_CARD, { QUOTE_LENNY, QUOTE_CARL, EMPTY_QUOTE } },
+ { "card12", 1, 2, 1, COLLECTOR_CARD, { QUOTE_HOMER, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card13", 1, 3, 2, COLLECTOR_CARD, { QUOTE_HOMER, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card14", 1, 4, 3, COLLECTOR_CARD, { QUOTE_HOMER, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card15", 1, 5, 4, COLLECTOR_CARD, { QUOTE_HOMER, QUOTE_HOMER, EMPTY_QUOTE } },
+ { "card16", 1, 6, 5, COLLECTOR_CARD, { QUOTE_HOMER, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card17", 1, 7, 6, COLLECTOR_CARD, { QUOTE_MR_SPARKLE, EMPTY_QUOTE, EMPTY_QUOTE } },
+//
+/// Level 2
+//
+ { "card21", 2, 1, 8, COLLECTOR_CARD, { QUOTE_JIMBO, QUOTE_HOMER, EMPTY_QUOTE } },
+ { "card22", 2, 2, 9, COLLECTOR_CARD, { QUOTE_WIGGUM, QUOTE_JASPER, EMPTY_QUOTE } },
+ { "card23", 2, 3, 10, COLLECTOR_CARD, { QUOTE_BART, QUOTE_HOMER, EMPTY_QUOTE } },
+ { "card24", 2, 4, 11, COLLECTOR_CARD, { QUOTE_SKINNER, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card25", 2, 5, 12, COLLECTOR_CARD, { QUOTE_LISA, QUOTE_HOMER, EMPTY_QUOTE } },
+ { "card26", 2, 6, 13, COLLECTOR_CARD, { QUOTE_MILHOUSE, QUOTE_BART, EMPTY_QUOTE } },
+ { "card27", 2, 7, 14, COLLECTOR_CARD, { QUOTE_BART, EMPTY_QUOTE, EMPTY_QUOTE } },
+//
+/// Level 3
+//
+ { "card31", 3, 1, 16, COLLECTOR_CARD, { QUOTE_LISA, QUOTE_BROCKMAN, EMPTY_QUOTE } },
+ { "card32", 3, 2, 17, COLLECTOR_CARD, { QUOTE_BART, QUOTE_HOMER, EMPTY_QUOTE } },
+ { "card33", 3, 3, 18, COLLECTOR_CARD, { QUOTE_LISA, QUOTE_STACY, EMPTY_QUOTE } },
+ { "card34", 3, 4, 19, COLLECTOR_CARD, { QUOTE_RALPH, QUOTE_LISA, EMPTY_QUOTE } },
+ { "card35", 3, 5, 20, COLLECTOR_CARD, { QUOTE_HOMER, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card36", 3, 6, 21, COLLECTOR_CARD, { QUOTE_DR_WOLFF, QUOTE_LISA, EMPTY_QUOTE } },
+ { "card37", 3, 7, 22, COLLECTOR_CARD, { QUOTE_HOMER, EMPTY_QUOTE, EMPTY_QUOTE } },
+//
+/// Level 4
+//
+ { "card41", 4, 1, 24, COLLECTOR_CARD, { QUOTE_HOMER, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card42", 4, 2, 25, COLLECTOR_CARD, { QUOTE_BURNS, QUOTE_MARGE, EMPTY_QUOTE } },
+ { "card43", 4, 3, 26, COLLECTOR_CARD, { QUOTE_HOMER, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card44", 4, 4, 27, COLLECTOR_CARD, { QUOTE_HOMER, QUOTE_MARGE, EMPTY_QUOTE } },
+ { "card45", 4, 5, 28, COLLECTOR_CARD, { QUOTE_GIL, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card46", 4, 6, 29, COLLECTOR_CARD, { QUOTE_HOMER, QUOTE_PHOTOGRAPHER, EMPTY_QUOTE } },
+ { "card47", 4, 7, 30, COLLECTOR_CARD, { QUOTE_MARGE, QUOTE_HOMER, EMPTY_QUOTE } },
+//
+/// Level 5
+//
+ { "card51", 5, 1, 32, COLLECTOR_CARD, { QUOTE_LISA, QUOTE_HOMER, EMPTY_QUOTE } },
+ { "card52", 5, 2, 33, COLLECTOR_CARD, { QUOTE_BROCKMAN, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card53", 5, 3, 34, COLLECTOR_CARD, { QUOTE_APU, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card54", 5, 4, 35, COLLECTOR_CARD, { QUOTE_MANJULA, QUOTE_HOMER, EMPTY_QUOTE } },
+ { "card55", 5, 5, 36, COLLECTOR_CARD, { QUOTE_APU, QUOTE_MANJULA, EMPTY_QUOTE } },
+ { "card56", 5, 6, 37, COLLECTOR_CARD, { QUOTE_BART, QUOTE_OTTO, EMPTY_QUOTE } },
+ { "card57", 5, 7, 38, COLLECTOR_CARD, { QUOTE_APU, QUOTE_HOMER, EMPTY_QUOTE } },
+//
+/// Level 6
+//
+ { "card61", 6, 1, 40, COLLECTOR_CARD, { QUOTE_HOMER, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card62", 6, 2, 41, COLLECTOR_CARD, { QUOTE_CHILD, QUOTE_MOTHER, EMPTY_QUOTE } },
+ { "card63", 6, 3, 42, COLLECTOR_CARD, { QUOTE_KRUSTY, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card64", 6, 4, 43, COLLECTOR_CARD, { QUOTE_HOMER, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card65", 6, 5, 44, COLLECTOR_CARD, { QUOTE_MEYERS, QUOTE_ANNOUNCER, EMPTY_QUOTE } },
+ { "card66", 6, 6, 45, COLLECTOR_CARD, { QUOTE_KRUSTY, QUOTE_ANNOUNCER, EMPTY_QUOTE } },
+ { "card67", 6, 7, 46, COLLECTOR_CARD, { QUOTE_BART, EMPTY_QUOTE, EMPTY_QUOTE } },
+//
+/// Level 7
+//
+ { "card71", 7, 1, 48, COLLECTOR_CARD, { QUOTE_BURNS, QUOTE_SMITHERS, EMPTY_QUOTE } },
+ { "card72", 7, 2, 49, COLLECTOR_CARD, { QUOTE_HOMER, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card73", 7, 3, 50, COLLECTOR_CARD, { QUOTE_MARGE, QUOTE_KANG, EMPTY_QUOTE } },
+ { "card74", 7, 4, 51, COLLECTOR_CARD, { QUOTE_WILLIE, QUOTE_MAGGIE, EMPTY_QUOTE } },
+ { "card75", 7, 5, 52, COLLECTOR_CARD, { QUOTE_LISA, QUOTE_MARGE, EMPTY_QUOTE } },
+ { "card76", 7, 6, 53, COLLECTOR_CARD, { QUOTE_HOMER, EMPTY_QUOTE, EMPTY_QUOTE } },
+ { "card77", 7, 7, 54, COLLECTOR_CARD, { QUOTE_MARGE, QUOTE_HOMER, EMPTY_QUOTE } },
+//
+};
+
+static const unsigned int s_numCardsInDB = sizeof( s_cardsDB ) / sizeof( s_cardsDB[ 0 ] );
+
+
+#endif // CARDS_H
diff --git a/game/code/cards/cardsdb.cpp b/game/code/cards/cardsdb.cpp
new file mode 100644
index 0000000..704fa60
--- /dev/null
+++ b/game/code/cards/cardsdb.cpp
@@ -0,0 +1,218 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CardsDB
+//
+// Description: Implementation of the CardsDB class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/06 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <cards/cardsdb.h>
+#include <cards/collectorcard.h>
+#include <cards/bonuscard.h>
+
+// cards database header file
+#include <cards/cards.h>
+
+#include <memory/srrmemory.h>
+
+#include <p3d/entity.hpp>
+
+//===========================================================================
+// Local Constants
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CardsDB::CardsDB
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CardsDB::CardsDB()
+: m_cards( NULL ),
+ m_numCards( 0 )
+{
+MEMTRACK_PUSH_GROUP( "CardsDB" );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+
+ m_cards = new Card*[ MAX_NUM_CARDS ];
+ rAssert( m_cards );
+
+ for( unsigned int i = 0; i < MAX_NUM_CARDS; i++ )
+ {
+ m_cards[ i ] = NULL;
+ }
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+MEMTRACK_POP_GROUP( "CardsDB" );
+}
+
+//===========================================================================
+// CardsDB::~CardsDB
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CardsDB::~CardsDB()
+{
+ // Clean-up cards DB memory
+ //
+ if( m_cards != NULL )
+ {
+ for( unsigned int i = 0; i < MAX_NUM_CARDS; i++ )
+ {
+ if( m_cards[ i ] != NULL )
+ {
+ delete m_cards[ i ];
+ m_cards[ i ] = NULL;
+ }
+ }
+
+ delete [] m_cards;
+ m_cards = NULL;
+ }
+}
+
+//===========================================================================
+// CardsDB::LoadCards
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CardsDB::LoadCards()
+{
+ // load cards from memory (static array in Cards.h header file)
+ //
+ for( unsigned int i = 0; i < s_numCardsInDB; i++ )
+ {
+ int cardID = s_cardsDB[ i ].imageID;
+
+ if( s_cardsDB[ i ].type == COLLECTOR_CARD )
+ {
+ // create new collector card
+ //
+ m_cards[ cardID ] = new CollectorCard( cardID,
+ s_cardsDB[ i ].level,
+ s_cardsDB[ i ].levelID,
+ tEntity::MakeUID( s_cardsDB[ i ].name ),
+ s_cardsDB[ i ].quotes,
+ MAX_NUM_QUOTES );
+ }
+ else if( s_cardsDB[ i ].type == BONUS_CARD )
+ {
+ // create new bonus card
+ //
+ m_cards[ cardID ] = new BonusCard( cardID,
+ s_cardsDB[ i ].level,
+ s_cardsDB[ i ].levelID,
+ tEntity::MakeUID( s_cardsDB[ i ].name ),
+ s_cardsDB[ i ].quotes,
+ MAX_NUM_QUOTES );
+ }
+ else
+ {
+ rAssertMsg( 0, "ERROR: *** Invalid card type in database file!\n" );
+ }
+
+ m_numCards++;
+ }
+}
+
+//===========================================================================
+// CardsDB::GetCardByID
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+Card*
+CardsDB::GetCardByID( unsigned int cardID )
+{
+ rAssert( cardID < MAX_NUM_CARDS );
+
+ return m_cards[ cardID ];
+}
+
+//===========================================================================
+// CardsDB::GetCardByName
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+Card*
+CardsDB::GetCardByName( tUID cardName )
+{
+ Card* card = NULL;
+
+ // search for card with cardName
+ for( unsigned int i = 0; i < MAX_NUM_CARDS; i++ )
+ {
+ if( m_cards[ i ] != NULL )
+ {
+ if( m_cards[ i ]->GetCardName() == cardName )
+ {
+ // found it!
+ card = m_cards[ i ];
+ break;
+ }
+ }
+ }
+
+ return card;
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
diff --git a/game/code/cards/cardsdb.h b/game/code/cards/cardsdb.h
new file mode 100644
index 0000000..5620eb9
--- /dev/null
+++ b/game/code/cards/cardsdb.h
@@ -0,0 +1,71 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CardsDB
+//
+// Description: Interface for the CardsDB class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/06 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef CARDSDB_H
+#define CARDSDB_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <p3d/p3dtypes.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+const unsigned int MAX_NUM_CARDS = 64;
+
+class Card;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class CardsDB
+{
+public:
+ CardsDB();
+ virtual ~CardsDB();
+
+ // Load cards from config file into DB
+ void LoadCards();
+
+ // Accessing cards DB
+ Card* GetCardByID( unsigned int cardID );
+ Card* GetCardByName( tUID cardName );
+
+ int GetNumCards() const { return m_numCards; }
+
+private:
+
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CardsDB( const CardsDB& );
+ CardsDB& operator= ( const CardsDB& );
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ Card** m_cards;
+ int m_numCards;
+
+};
+
+#endif // CARDSDB_H
diff --git a/game/code/cards/collectorcard.cpp b/game/code/cards/collectorcard.cpp
new file mode 100644
index 0000000..7b6ae27
--- /dev/null
+++ b/game/code/cards/collectorcard.cpp
@@ -0,0 +1,72 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CollectorCard
+//
+// Description: Implementation of the CollectorCard class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/06 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <cards/collectorcard.h>
+
+//===========================================================================
+// Local Constants
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CollectorCard::CollectorCard
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CollectorCard::CollectorCard
+(
+ unsigned int ID,
+ unsigned int level,
+ unsigned int levelID,
+ tUID cardName,
+ const eQuoteID* quotes,
+ int numQuotes
+)
+: Card( ID, level, levelID, cardName, quotes, numQuotes )
+{
+}
+
+//===========================================================================
+// CollectorCard::~CollectorCard
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CollectorCard::~CollectorCard()
+{
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
diff --git a/game/code/cards/collectorcard.h b/game/code/cards/collectorcard.h
new file mode 100644
index 0000000..e04c48c
--- /dev/null
+++ b/game/code/cards/collectorcard.h
@@ -0,0 +1,63 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CollectorCard
+//
+// Description: Interface for the CollectorCard class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/06 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef COLLECTORCARD_H
+#define COLLECTORCARD_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <cards/card.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class CollectorCard : public Card
+{
+public:
+ CollectorCard( unsigned int ID,
+ unsigned int level,
+ unsigned int levelID,
+ tUID cardName,
+ const eQuoteID* quotes,
+ int numQuotes );
+
+ virtual ~CollectorCard();
+
+ virtual eCardType GetType() const { return COLLECTOR_CARD; }
+
+private:
+
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CollectorCard( const CollectorCard& );
+ CollectorCard& operator= ( const CollectorCard& );
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+};
+
+#endif // COLLECTORCARD_H
diff --git a/game/code/cheats/allcheats.cpp b/game/code/cheats/allcheats.cpp
new file mode 100644
index 0000000..1acd74c
--- /dev/null
+++ b/game/code/cheats/allcheats.cpp
@@ -0,0 +1,3 @@
+#include <cheats/cheatinputsystem.cpp>
+#include <cheats/cheatinputhandler.cpp>
+#include <cheats/cheats.cpp>
diff --git a/game/code/cheats/cheatinputhandler.cpp b/game/code/cheats/cheatinputhandler.cpp
new file mode 100644
index 0000000..ec70716
--- /dev/null
+++ b/game/code/cheats/cheatinputhandler.cpp
@@ -0,0 +1,319 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CheatInputHandler
+//
+// Description: Implementation of the CheatInputHandler class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/09/19 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <cheats/cheatinputhandler.h>
+#include <cheats/cheatinputsystem.h>
+
+//===========================================================================
+// Local Constants
+//===========================================================================
+
+struct CheatInputMapping
+{
+ char* inputName;
+ int inputID;
+};
+
+static const CheatInputMapping CHEAT_INPUT_MAPPINGS[] =
+{
+#ifdef RAD_GAMECUBE
+ { "A", CHEAT_INPUT_0 },
+ { "B", CHEAT_INPUT_1 },
+ { "X", CHEAT_INPUT_2 },
+ { "Y", CHEAT_INPUT_3 },
+ { "TriggerL", CHEAT_INPUT_LTRIGGER },
+ { "TriggerR", CHEAT_INPUT_RTRIGGER },
+#endif
+
+#ifdef RAD_PS2
+ { "X", CHEAT_INPUT_0 },
+ { "Circle", CHEAT_INPUT_1 },
+ { "Square", CHEAT_INPUT_2 },
+ { "Triangle", CHEAT_INPUT_3 },
+ { "L1", CHEAT_INPUT_LTRIGGER },
+ { "R1", CHEAT_INPUT_RTRIGGER },
+#endif
+
+#ifdef RAD_XBOX
+ { "A", CHEAT_INPUT_0 },
+ { "B", CHEAT_INPUT_1 },
+ { "X", CHEAT_INPUT_2 },
+ { "Y", CHEAT_INPUT_3 },
+ { "LeftTrigger", CHEAT_INPUT_LTRIGGER },
+ { "RightTrigger", CHEAT_INPUT_RTRIGGER },
+#endif
+
+#ifdef RAD_WIN32 // these are not laid out yet
+ { "Attack", CHEAT_INPUT_0 },
+ { "Jump", CHEAT_INPUT_1 },
+ { "Sprint", CHEAT_INPUT_2 },
+ { "DoAction", CHEAT_INPUT_3 },
+ { "CameraFunc1", CHEAT_INPUT_LTRIGGER },
+ { "CameraFunc2", CHEAT_INPUT_RTRIGGER },
+#endif
+
+ { "", UNKNOWN_CHEAT_INPUT }
+};
+
+static const int unsigned NUM_CHEAT_INPUT_MAPPINGS =
+ sizeof( CHEAT_INPUT_MAPPINGS ) / sizeof( CHEAT_INPUT_MAPPINGS[ 0 ] );
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CheatInputHandler::CheatInputHandler
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CheatInputHandler::CheatInputHandler()
+: m_LTriggerBitMask( 0 ),
+ m_RTriggerBitMask( 0 ),
+ m_currentInputIndex( 0 )
+{
+ this->ResetInputSequence();
+}
+
+//===========================================================================
+// CheatInputHandler::~CheatInputHandler
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CheatInputHandler::~CheatInputHandler()
+{
+}
+
+//===========================================================================
+// CheatInputHandler::ResetInputSequence
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CheatInputHandler::ResetInputSequence()
+{
+ m_currentInputIndex = 0;
+
+ // just to be sure
+ //
+ for( unsigned int i = 0; i < NUM_CHEAT_SEQUENCE_INPUTS; i++ )
+ {
+ m_inputSequence[ i ] = UNKNOWN_CHEAT_INPUT;
+ }
+}
+
+//===========================================================================
+// CheatInputHandler::GetInputName
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+const char*
+CheatInputHandler::GetInputName( eCheatInput cheatInput )
+{
+ rAssert( cheatInput < static_cast<int>( NUM_CHEAT_INPUT_MAPPINGS ) );
+
+ return CHEAT_INPUT_MAPPINGS[ cheatInput ].inputName;
+}
+
+//===========================================================================
+// CheatInputHandler::OnButton
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CheatInputHandler::OnButton( int controllerId,
+ int buttonId,
+ const IButton* pButton )
+{
+}
+
+//===========================================================================
+// CheatInputHandler::OnButtonDown
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CheatInputHandler::OnButtonDown( int controllerId,
+ int buttonId,
+ const IButton* pButton )
+{
+ switch( buttonId )
+ {
+ case CHEAT_INPUT_LTRIGGER:
+ {
+ m_LTriggerBitMask |= (1 << controllerId);
+
+ bool isRTriggerDown = ((m_RTriggerBitMask & (1 << controllerId)) > 0);
+ GetCheatInputSystem()->SetActivated( controllerId,
+ isRTriggerDown );
+
+ break;
+ }
+ case CHEAT_INPUT_RTRIGGER:
+ {
+ m_RTriggerBitMask |= (1 << controllerId);
+
+ bool isLTriggerDown = ((m_LTriggerBitMask & (1 << controllerId)) > 0);
+ GetCheatInputSystem()->SetActivated( controllerId,
+ isLTriggerDown );
+
+ break;
+ }
+ default:
+ {
+ if( GetCheatInputSystem()->IsActivated( controllerId ) )
+ {
+ rAssert( buttonId < NUM_CHEAT_INPUTS );
+
+ rReleasePrintf( "Received Cheat Input [%d] = [%d]\n",
+ m_currentInputIndex, buttonId );
+
+ // add input to current sequence
+ //
+ m_inputSequence[ m_currentInputIndex++ ] = static_cast<eCheatInput>( buttonId );
+ m_currentInputIndex %= NUM_CHEAT_SEQUENCE_INPUTS;
+
+ // if this is the last input for the current sequence,
+ // send current sequence to cheat input system for
+ // validation
+ //
+ if( m_currentInputIndex == 0 )
+ {
+ GetCheatInputSystem()->ReceiveInputs( m_inputSequence );
+ }
+ }
+
+ break;
+ }
+ }
+}
+
+//===========================================================================
+// CheatInputHandler::OnButtonUp
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CheatInputHandler::OnButtonUp( int controllerId,
+ int buttonId,
+ const IButton* pButton )
+{
+ switch( buttonId )
+ {
+ case CHEAT_INPUT_LTRIGGER:
+ {
+ m_LTriggerBitMask &= ~(1 << controllerId);
+
+ GetCheatInputSystem()->SetActivated( controllerId, false );
+
+ break;
+ }
+ case CHEAT_INPUT_RTRIGGER:
+ {
+ m_RTriggerBitMask &= ~(1 << controllerId);
+
+ GetCheatInputSystem()->SetActivated( controllerId, false );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//===========================================================================
+// CheatInputHandler::LoadControllerMappings
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CheatInputHandler::LoadControllerMappings( unsigned int controllerId )
+{
+ for( unsigned int i = 0; i < NUM_CHEAT_INPUT_MAPPINGS; i++ )
+ {
+ this->Map( CHEAT_INPUT_MAPPINGS[ i ].inputName,
+ CHEAT_INPUT_MAPPINGS[ i ].inputID,
+ 0,
+ controllerId );
+/*
+ rTunePrintf( "Load Mapping: %s, %d, %d\n",
+ CHEAT_INPUT_MAPPINGS[ i ].inputName,
+ CHEAT_INPUT_MAPPINGS[ i ].inputID,
+ controllerId );
+*/
+ }
+}
+
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
diff --git a/game/code/cheats/cheatinputhandler.h b/game/code/cheats/cheatinputhandler.h
new file mode 100644
index 0000000..0d28860
--- /dev/null
+++ b/game/code/cheats/cheatinputhandler.h
@@ -0,0 +1,86 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CheatInputHandler
+//
+// Description: Interface for the CheatInputHandler class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/09/19 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef CHEATINPUTHANDLER_H
+#define CHEATINPUTHANDLER_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <cheats/cheatinputs.h>
+#include <input/mappable.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+enum eAuxiliaryCheatInput
+{
+ CHEAT_INPUT_LTRIGGER = NUM_CHEAT_INPUTS,
+ CHEAT_INPUT_RTRIGGER,
+
+ NUM_AUXILIARY_CHEAT_INPUTS
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class CheatInputHandler : public Mappable
+{
+public:
+ CheatInputHandler();
+ virtual ~CheatInputHandler();
+
+ void ResetTriggerStates()
+ {
+ m_LTriggerBitMask = 0;
+ m_RTriggerBitMask = 0;
+ }
+
+ void ResetInputSequence();
+
+ static const char* GetInputName( eCheatInput cheatInput );
+
+ // Implements Mappable Interface
+ //
+ virtual void OnButton( int controllerId, int buttonId, const IButton* pButton );
+ virtual void OnButtonUp( int controllerId, int buttonId, const IButton* pButton );
+ virtual void OnButtonDown( int controllerId, int buttonId, const IButton* pButton );
+ virtual void LoadControllerMappings( unsigned int controllerId );
+
+private:
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CheatInputHandler( const CheatInputHandler& );
+ CheatInputHandler& operator= ( const CheatInputHandler& );
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ unsigned int m_LTriggerBitMask;
+ unsigned int m_RTriggerBitMask;
+
+ eCheatInput m_inputSequence[ NUM_CHEAT_SEQUENCE_INPUTS ];
+ unsigned int m_currentInputIndex;
+
+};
+
+#endif // CHEATINPUTHANDLER_H
diff --git a/game/code/cheats/cheatinputs.h b/game/code/cheats/cheatinputs.h
new file mode 100644
index 0000000..0871613
--- /dev/null
+++ b/game/code/cheats/cheatinputs.h
@@ -0,0 +1,27 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+//===========================================================================
+
+#ifndef CHEATINPUTS_H
+#define CHEATINPUTS_H
+
+//===========================================================================
+// Constants and Enums
+//===========================================================================
+
+const unsigned int NUM_CHEAT_SEQUENCE_INPUTS = 4;
+
+enum eCheatInput
+{
+ UNKNOWN_CHEAT_INPUT = -1,
+
+ CHEAT_INPUT_0,
+ CHEAT_INPUT_1,
+ CHEAT_INPUT_2,
+ CHEAT_INPUT_3,
+
+ NUM_CHEAT_INPUTS
+};
+
+#endif // CHEATINPUTS_H
diff --git a/game/code/cheats/cheatinputsystem.cpp b/game/code/cheats/cheatinputsystem.cpp
new file mode 100644
index 0000000..ede1393
--- /dev/null
+++ b/game/code/cheats/cheatinputsystem.cpp
@@ -0,0 +1,513 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CheatInputSystem
+//
+// Description: Implementation of the CheatInputSystem class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/09/19 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <cheats/cheatinputsystem.h>
+#include <cheats/cheatinputhandler.h>
+
+#include <events/eventmanager.h>
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/guiwindow.h>
+#include <presentation/gui/guimanager.h>
+
+// Static pointer to instance of singleton.
+CheatInputSystem* CheatInputSystem::spInstance = NULL;
+
+//===========================================================================
+// Local Constants
+//===========================================================================
+
+CHEATBITMASK CheatInputSystem::s_cheatsEnabled = 0;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//==============================================================================
+// CheatInputSystem::CreateInstance
+//==============================================================================
+//
+// Description: - Creates the Game Data Manager.
+//
+// Parameters: None.
+//
+// Return: Pointer to the CheatInputSystem.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+CheatInputSystem* CheatInputSystem::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "CheatInputSystem" );
+ spInstance = new( GMA_PERSISTENT ) CheatInputSystem;
+ rAssert( spInstance != NULL );
+MEMTRACK_POP_GROUP( "CheatInputSystem" );
+
+ return spInstance;
+}
+
+//==============================================================================
+// CheatInputSystem::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the Game Data Manager.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void CheatInputSystem::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+}
+
+//==============================================================================
+// CheatInputSystem::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the CheatInputSystem singleton.
+// - Creates the CheatInputSystem if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the CheatInputSystem.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+CheatInputSystem* CheatInputSystem::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+//===========================================================================
+// CheatInputSystem::CheatInputSystem
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CheatInputSystem::CheatInputSystem()
+: m_enabled( false ),
+ m_activatedBitMask( 0 ),
+ m_cheatsDB( NULL ),
+ m_cheatInputHandler( NULL ),
+ m_numClientCallbacks( 0 )
+{
+ for( unsigned int i = 0; i < sizeof( m_clientCallbacks ) /
+ sizeof( m_clientCallbacks[ 0 ] ); i++ )
+ {
+ m_clientCallbacks[ i ] = NULL;
+ }
+}
+
+//===========================================================================
+// CheatInputSystem::~CheatInputSystem
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CheatInputSystem::~CheatInputSystem()
+{
+ // clean-up memory
+ //
+ if( m_cheatsDB != NULL )
+ {
+ delete( GMA_PERSISTENT, m_cheatsDB );
+ m_cheatsDB = NULL;
+ }
+
+ if( m_cheatInputHandler != NULL )
+ {
+ m_cheatInputHandler->Release();
+ m_cheatInputHandler = NULL;
+ }
+}
+
+//===========================================================================
+// CheatInputSystem::Init
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CheatInputSystem::Init()
+{
+MEMTRACK_PUSH_GROUP( "CheatInputSystem Init" );
+ // create (one and only) instance of Cheats DB
+ //
+ m_cheatsDB = new( GMA_PERSISTENT ) CheatsDB();
+ rAssert( m_cheatsDB );
+
+ // create (one and only) cheat input handler
+ //
+ m_cheatInputHandler = new( GMA_PERSISTENT ) CheatInputHandler;
+ rAssert( m_cheatInputHandler );
+ m_cheatInputHandler->AddRef();
+MEMTRACK_POP_GROUP( "CheatInputSystem Init" );
+}
+
+//===========================================================================
+// CheatInputSystem::SetEnabled
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CheatInputSystem::SetEnabled( bool enable )
+{
+ m_enabled = enable;
+
+ int maxControllers = GetInputManager()->GetMaxControllers();
+ for( int i = 0; i < maxControllers; i++ )
+ {
+ if( enable )
+ {
+ GetInputManager()->RegisterMappable( i, m_cheatInputHandler );
+ }
+ else
+ {
+ GetInputManager()->UnregisterMappable( i, m_cheatInputHandler );
+ }
+ }
+
+ rAssert( m_cheatInputHandler );
+ m_cheatInputHandler->ResetInputSequence();
+ m_cheatInputHandler->ResetTriggerStates();
+}
+
+//===========================================================================
+// CheatInputSystem::SetActivated
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CheatInputSystem::SetActivated( int controllerId, bool activated )
+{
+ if( activated )
+ {
+ m_activatedBitMask |= (1 << controllerId);
+ }
+ else
+ {
+ m_activatedBitMask &= ~(1 << controllerId);
+ }
+
+#ifndef RAD_GAMECUBE
+ // TC: *** temporary work-around to GC-specific problem w/ L and R
+ // triggers constantly sending alternating UP/DOWN inputs
+ //
+ rAssert( m_cheatInputHandler );
+ m_cheatInputHandler->ResetInputSequence();
+#endif
+}
+
+//===========================================================================
+// CheatInputSystem::IsActivated
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+bool
+CheatInputSystem::IsActivated( int controllerId ) const
+{
+ return ((m_activatedBitMask & (1 << controllerId)) > 0);
+}
+
+//===========================================================================
+// CheatInputSystem::SetCheatEnabled
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CheatInputSystem::SetCheatEnabled( eCheatID cheatID, bool enable )
+{
+#ifdef FINAL
+ if( cheatID == CHEAT_ID_UNLOCK_CARDS ||
+ cheatID == CHEAT_ID_UNLOCK_SKINS ||
+ cheatID == CHEAT_ID_UNLOCK_VEHICLES )
+ {
+ // for these cheats, unless all missions are completed, don't do
+ // anything
+ //
+ if( !GetCharacterSheetManager()->IsAllStoryMissionsCompleted() )
+ {
+ return;
+ }
+ }
+#endif
+
+ // turn on/off corresponding bit in cheats bitmask
+ //
+ if( enable )
+ {
+ s_cheatsEnabled |= (1 << cheatID);
+ }
+ else
+ {
+ s_cheatsEnabled &= ~(1 << cheatID);
+ }
+
+ // notify registered clients that valid cheat code has been entered
+ //
+ for( int i = 0; i < m_numClientCallbacks; i++ )
+ {
+ rAssert( m_clientCallbacks[ i ] );
+ m_clientCallbacks[ i ]->OnCheatEntered( cheatID, enable );
+ }
+}
+
+//===========================================================================
+// CheatInputSystem::IsCheatEnabled
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+bool
+CheatInputSystem::IsCheatEnabled( eCheatID cheatID ) const
+{
+ return ((s_cheatsEnabled & (1 << cheatID)) > 0);
+}
+
+//===========================================================================
+// CheatInputSystem::ReceiveInputs
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CheatInputSystem::ReceiveInputs( eCheatInput* cheatInputs,
+ int numInputs )
+{
+ int cheatIndex = CheatsDB::ConvertSequenceToIndex( cheatInputs,
+ numInputs );
+
+ // get cheatID associated with received input sequence
+ //
+ rAssert( m_cheatsDB );
+ eCheatID cheatID = m_cheatsDB->GetCheatID( cheatIndex );
+
+ // validate cheatID
+ //
+ if( cheatID != CHEAT_ID_UNREGISTERED )
+ {
+ // Yay! Successful cheat code entered!!
+ //
+#ifdef FINAL
+ // TC: toggling cheats on/off isn't really supported for all cheats, so
+ // letz just not allow this in the final build
+ //
+ bool isCheatEnabled = true;
+#else
+ bool isCheatEnabled = !this->IsCheatEnabled( cheatID ); // toggle
+#endif
+
+ this->SetCheatEnabled( cheatID, isCheatEnabled );
+
+ if( this->IsCheatEnabled( cheatID ) )
+ {
+ GetEventManager()->TriggerEvent( EVENT_FE_CHEAT_SUCCESS );
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_FE_CHEAT_FAILURE );
+
+ isCheatEnabled = false;
+
+ rReleasePrintf( "*** This cheat cannot be enabled until all story missions have been completed!\n" );
+ }
+
+ rReleasePrintf( "*** Cheat code successfully entered: %s (%s)\n",
+ m_cheatsDB->GetCheat( cheatID )->m_cheatName,
+ isCheatEnabled ? "enabled" : "disabled" );
+
+ // if this is the "unlock everything" cheat, then unlock everything (duh...)
+ //
+ if( cheatID == CHEAT_ID_UNLOCK_EVERYTHING )
+ {
+ for( int unlockCheatID = CHEAT_ID_UNLOCK_BEGIN;
+ unlockCheatID < CHEAT_ID_UNLOCK_EVERYTHING;
+ unlockCheatID++ )
+ {
+ this->SetCheatEnabled( static_cast<eCheatID>( unlockCheatID ), isCheatEnabled );
+ }
+ }
+ else if ( cheatID == CHEAT_ID_DEMO_TEST )
+ {
+ GetGuiSystem()->GotoScreen( CGuiWindow::GUI_SCREEN_ID_SPLASH, 0, 0, CLEAR_WINDOW_HISTORY );
+ }
+ }
+ else
+ {
+ // Booo.... try again!!
+ //
+ GetEventManager()->TriggerEvent( EVENT_FE_CHEAT_FAILURE );
+
+ rReleasePrintf( "*** Invalid cheat code entered!\n" );
+ }
+}
+
+//===========================================================================
+// CheatInputSystem::RegisterCallback
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CheatInputSystem::RegisterCallback( ICheatEnteredCallback* callback )
+{
+ rAssert( callback != NULL );
+
+ // first, check if callback is already registered
+ //
+ for( int i = 0; i < m_numClientCallbacks; i++ )
+ {
+ if( m_clientCallbacks[ i ] == callback )
+ {
+ // yup, ignore redundant request
+ return;
+ }
+ }
+
+ rAssert( static_cast<unsigned int>( m_numClientCallbacks ) <
+ sizeof( m_clientCallbacks ) / sizeof( m_clientCallbacks[ 0 ] ) );
+
+ // add new registered callback
+ //
+ m_clientCallbacks[ m_numClientCallbacks ] = callback;
+ m_numClientCallbacks++;
+}
+
+//===========================================================================
+// CheatInputSystem::UnregisterCallback
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CheatInputSystem::UnregisterCallback( ICheatEnteredCallback* callback )
+{
+ // search for callback
+ //
+ for( int i = 0; i < m_numClientCallbacks; i++ )
+ {
+ if( m_clientCallbacks[ i ] == callback )
+ {
+ // found it! now remove it
+ //
+ m_clientCallbacks[ i ] = NULL;
+ m_numClientCallbacks--;
+
+ // if removed from the middle, replace w/ one at the end
+ //
+ if( i < m_numClientCallbacks )
+ {
+ m_clientCallbacks[ i ] = m_clientCallbacks[ m_numClientCallbacks ];
+ m_clientCallbacks[ m_numClientCallbacks ] = NULL;
+ }
+
+ // all done, return
+ return;
+ }
+ }
+
+ // callback not found!
+ //
+ rAssertMsg( 0, "WARNING: *** Callback not found!" );
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
diff --git a/game/code/cheats/cheatinputsystem.h b/game/code/cheats/cheatinputsystem.h
new file mode 100644
index 0000000..e695ddf
--- /dev/null
+++ b/game/code/cheats/cheatinputsystem.h
@@ -0,0 +1,107 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CheatInputSystem
+//
+// Description: Interface for the CheatInputSystem class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/09/19 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef CHEATINPUTSYSTEM_H
+#define CHEATINPUTSYSTEM_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <cheats/cheatinputs.h>
+#include <cheats/cheats.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CheatsDB;
+class CheatInputHandler;
+
+struct ICheatEnteredCallback
+{
+ virtual void OnCheatEntered( eCheatID cheatID, bool isEnabled ) = 0;
+};
+
+const unsigned int MAX_NUM_CHEAT_CALLBACKS = 32;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class CheatInputSystem
+{
+public:
+ // Static Methods for accessing this singleton.
+ static CheatInputSystem* CreateInstance();
+ static void DestroyInstance();
+ static CheatInputSystem* GetInstance();
+
+ CheatInputSystem();
+ virtual ~CheatInputSystem();
+
+ void Init();
+ void Reset() { s_cheatsEnabled = 0; }
+
+ void SetEnabled( bool enable );
+ bool IsEnabled() const { return m_enabled; }
+
+ void SetActivated( int controllerId, bool activated );
+ bool IsActivated( int controllerId ) const;
+
+ void SetCheatEnabled( eCheatID cheatID, bool enable );
+ bool IsCheatEnabled( eCheatID cheatID ) const;
+
+ void ReceiveInputs( eCheatInput* cheatInputs,
+ int numInputs = NUM_CHEAT_SEQUENCE_INPUTS );
+
+ CheatsDB* GetCheatsDB() const { return m_cheatsDB; }
+
+ void RegisterCallback( ICheatEnteredCallback* callback );
+ void UnregisterCallback( ICheatEnteredCallback* callback );
+
+private:
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CheatInputSystem( const CheatInputSystem& );
+ CheatInputSystem& operator= ( const CheatInputSystem& );
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ // Pointer to the one and only instance of this singleton.
+ static CheatInputSystem* spInstance;
+
+ bool m_enabled;
+ unsigned int m_activatedBitMask;
+
+ static CHEATBITMASK s_cheatsEnabled;
+ CheatsDB* m_cheatsDB;
+
+ CheatInputHandler* m_cheatInputHandler;
+
+ ICheatEnteredCallback* m_clientCallbacks[ MAX_NUM_CHEAT_CALLBACKS ];
+ int m_numClientCallbacks;
+
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline CheatInputSystem* GetCheatInputSystem() { return( CheatInputSystem::GetInstance() ); }
+
+#endif // CHEATINPUTSYSTEM_H
diff --git a/game/code/cheats/cheats.cpp b/game/code/cheats/cheats.cpp
new file mode 100644
index 0000000..3ebc119
--- /dev/null
+++ b/game/code/cheats/cheats.cpp
@@ -0,0 +1,435 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CheatsDB
+//
+// Description: Implementation of the CheatsDB class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/09/19 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <cheats/cheats.h>
+#include <cheats/cheatinputhandler.h>
+
+#include <memory/srrmemory.h>
+
+#include <string.h>
+
+//===========================================================================
+// Local Constants
+//===========================================================================
+
+/* --------- Platform-Specific Button Mappings for Cheat Inputs ----------
+
+[PLATFORM] [CHEAT_INPUT_0] [CHEAT_INPUT_1] [CHEAT_INPUT_2] [CHEAT_INPUT_3]
+
+GameCube A B X Y
+PS2 X Circle Square Triangle
+Xbox A B X Y
+
+ * ----------------------------------------------------------------------- */
+
+static const Cheat REGISTERED_CHEATS[] =
+{
+ // *** cheat name must be < 32 characters in length *** //
+ //
+ {
+ CHEAT_ID_UNLOCK_VEHICLES,
+ { CHEAT_INPUT_0, CHEAT_INPUT_1, CHEAT_INPUT_0, CHEAT_INPUT_1 },
+ "Unlock All Reward Vehicles"
+ },
+ {
+ CHEAT_ID_NO_TOP_SPEED,
+ { CHEAT_INPUT_2, CHEAT_INPUT_2, CHEAT_INPUT_2, CHEAT_INPUT_2 },
+ "No Top Speed"
+ },
+ {
+ CHEAT_ID_HIGH_ACCELERATION,
+ { CHEAT_INPUT_3, CHEAT_INPUT_3, CHEAT_INPUT_3, CHEAT_INPUT_3 },
+ "High Acceleration"
+ },
+ {
+ CHEAT_ID_CAR_JUMP_ON_HORN,
+ { CHEAT_INPUT_2, CHEAT_INPUT_2, CHEAT_INPUT_2, CHEAT_INPUT_3 },
+ "Car Jump on Horn"
+ },
+ {
+ CHEAT_ID_ONE_TAP_TRAFFIC_DEATH,
+ { CHEAT_INPUT_3, CHEAT_INPUT_3, CHEAT_INPUT_2, CHEAT_INPUT_2 },
+ "One Tap Traffic Death"
+ },
+ {
+ CHEAT_ID_UNLOCK_CAMERAS,
+ { CHEAT_INPUT_1, CHEAT_INPUT_1, CHEAT_INPUT_1, CHEAT_INPUT_0 },
+ "Unlock All Cameras"
+ },
+ {
+ CHEAT_ID_PLAY_CREDITS_DIALOG,
+ { CHEAT_INPUT_0, CHEAT_INPUT_2, CHEAT_INPUT_2, CHEAT_INPUT_3 },
+ "Play Credits Dialog"
+ },
+ {
+ CHEAT_ID_SHOW_SPEEDOMETER,
+ { CHEAT_INPUT_3, CHEAT_INPUT_3, CHEAT_INPUT_1, CHEAT_INPUT_2 },
+ "Show Speedometer"
+ },
+ {
+ CHEAT_ID_REDBRICK,
+ { CHEAT_INPUT_1, CHEAT_INPUT_1, CHEAT_INPUT_3, CHEAT_INPUT_2 },
+ "Red Brick"
+ },
+ {
+ CHEAT_ID_INVINCIBLE_CAR,
+ { CHEAT_INPUT_3, CHEAT_INPUT_0, CHEAT_INPUT_3, CHEAT_INPUT_0 },
+ "Invincible Car"
+ },
+ {
+ CHEAT_ID_SHOW_TREE,
+ { CHEAT_INPUT_1, CHEAT_INPUT_0, CHEAT_INPUT_1, CHEAT_INPUT_3 },
+ "Show Tree"
+ },
+ {
+ CHEAT_ID_TRIPPY,
+ { CHEAT_INPUT_3, CHEAT_INPUT_1, CHEAT_INPUT_3, CHEAT_INPUT_1 },
+ "Trippy"
+ },
+
+
+
+#ifndef FINAL
+ // register cheats that we don't want shipped in the final game here
+ //
+ {
+ CHEAT_ID_UNLOCK_CARDS,
+ { CHEAT_INPUT_0, CHEAT_INPUT_0, CHEAT_INPUT_0, CHEAT_INPUT_0 },
+ "Unlock All Collectible Cards"
+ },
+ {
+ CHEAT_ID_UNLOCK_SKINS,
+ { CHEAT_INPUT_2, CHEAT_INPUT_3, CHEAT_INPUT_2, CHEAT_INPUT_3 },
+ "Unlock All Character Clothing"
+ },
+ {
+ CHEAT_ID_MOTHER_OF_ALL_CHEATS,
+ { CHEAT_INPUT_0, CHEAT_INPUT_1, CHEAT_INPUT_2, CHEAT_INPUT_3 },
+ "Display All Cheats"
+ },
+ {
+ CHEAT_ID_UNLOCK_MISSIONS,
+ { CHEAT_INPUT_0, CHEAT_INPUT_1, CHEAT_INPUT_1, CHEAT_INPUT_0 },
+ "Unlock All Missions"
+ },
+ {
+ CHEAT_ID_UNLOCK_MOVIES,
+ { CHEAT_INPUT_1, CHEAT_INPUT_3, CHEAT_INPUT_2, CHEAT_INPUT_3 },
+ "Unlock All Movies"
+ },
+ {
+ CHEAT_ID_UNLOCK_EVERYTHING,
+ { CHEAT_INPUT_0, CHEAT_INPUT_1, CHEAT_INPUT_1, CHEAT_INPUT_3 },
+ "Unlock All (Unlockables)"
+ },
+ {
+ CHEAT_ID_EXTRA_COINS,
+ { CHEAT_INPUT_0, CHEAT_INPUT_2, CHEAT_INPUT_1, CHEAT_INPUT_3 },
+ "Add Extra Coins"
+ },
+ {
+ CHEAT_ID_KICK_TOGGLES_CHARACTER_MODEL,
+ { CHEAT_INPUT_1, CHEAT_INPUT_1, CHEAT_INPUT_1, CHEAT_INPUT_2 },
+ "Kick Swaps Character Model"
+ },
+ {
+ CHEAT_ID_SHOW_AVATAR_POSITION,
+ { CHEAT_INPUT_1, CHEAT_INPUT_1, CHEAT_INPUT_1, CHEAT_INPUT_1 },
+ "Show Avatar Position"
+ },
+ {
+ CHEAT_ID_FULL_DAMAGE_TO_CAR,
+ { CHEAT_INPUT_2, CHEAT_INPUT_2, CHEAT_INPUT_3, CHEAT_INPUT_3 },
+ "Apply Full Damage to Car"
+ },
+ {
+ CHEAT_ID_EXTRA_TIME,
+ { CHEAT_INPUT_1, CHEAT_INPUT_1, CHEAT_INPUT_1, CHEAT_INPUT_3 },
+ "Extra Objective Time"
+ },
+ {
+ CHEAT_ID_DEMO_TEST,
+ { CHEAT_INPUT_0, CHEAT_INPUT_0, CHEAT_INPUT_0, CHEAT_INPUT_1 },
+ "Enable Demotest mode"
+ },
+#endif
+
+ // dummy terminator
+ //
+ {
+ CHEAT_ID_UNREGISTERED,
+ { UNKNOWN_CHEAT_INPUT, UNKNOWN_CHEAT_INPUT, UNKNOWN_CHEAT_INPUT, UNKNOWN_CHEAT_INPUT },
+ ""
+ }
+};
+
+static const unsigned int NUM_REGISTERED_CHEATS =
+ sizeof( REGISTERED_CHEATS ) / sizeof( REGISTERED_CHEATS[ 0 ] ) - 1;
+
+unsigned int CheatsDB::s_maxNumPossibleCheats = 1;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CheatsDB::CheatsDB
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CheatsDB::CheatsDB()
+: m_cheats( NULL )
+{
+MEMTRACK_PUSH_GROUP( "CheatsDB" );
+ unsigned int i = 0;
+
+ // determine maximum number of possible cheat patterns
+ //
+ for( i = 0; i < NUM_CHEAT_SEQUENCE_INPUTS; i++ )
+ {
+ s_maxNumPossibleCheats *= NUM_CHEAT_INPUTS;
+ }
+
+ // create and initialize cheats database
+ //
+ m_cheats = new( GMA_PERSISTENT ) eCheatID[ s_maxNumPossibleCheats ];
+ for( i = 0; i < s_maxNumPossibleCheats; i++ )
+ {
+ m_cheats[ i ] = CHEAT_ID_UNREGISTERED;
+ }
+
+ rTunePrintf( "\n---=[ Registered Simpsons Cheats Begin ]=---\n\n" );
+
+ // add registered cheats to database
+ //
+ for( i = 0; i < NUM_REGISTERED_CHEATS; i++ )
+ {
+ int cheatIndex = this->ConvertSequenceToIndex( REGISTERED_CHEATS[ i ].m_cheatInputs );
+
+ rAssertMsg( m_cheats[ cheatIndex ] == CHEAT_ID_UNREGISTERED,
+ "WARNING: *** Duplicate cheat input sequence found! Clobbering previously registered cheat." );
+
+ rAssert( REGISTERED_CHEATS[ i ].m_cheatID < static_cast<int>( MAX_NUM_CHEATS ) );
+ m_cheats[ cheatIndex ] = REGISTERED_CHEATS[ i ].m_cheatID;
+
+#ifndef RAD_RELEASE
+ char buffer[ 256 ];
+ this->PrintCheatInfo( &(REGISTERED_CHEATS[ i ]), buffer );
+
+ rTunePrintf( "Cheat ID [%02d]: %s\n",
+ REGISTERED_CHEATS[ i ].m_cheatID, buffer );
+#endif
+ }
+
+ rTunePrintf( "\n---=[ Registered Simpsons Cheats End ]=---\n\n" );
+MEMTRACK_POP_GROUP( "CheatsDB" );
+}
+
+//===========================================================================
+// CheatsDB::~CheatsDB
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CheatsDB::~CheatsDB()
+{
+ // clean-up memory
+ //
+ if( m_cheats != NULL )
+ {
+ delete [] m_cheats;
+ m_cheats = NULL;
+ }
+}
+
+//===========================================================================
+// CheatsDB::GetCheatID
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+eCheatID
+CheatsDB::GetCheatID( unsigned int cheatIndex ) const
+{
+ rAssert( m_cheats );
+ rAssert( cheatIndex < s_maxNumPossibleCheats );
+
+ // return cheat ID associated with specified cheat index
+ //
+ return m_cheats[ cheatIndex ];
+}
+
+//===========================================================================
+// CheatsDB::GetNumRegisteredCheats
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+unsigned int
+CheatsDB::GetNumRegisteredCheats() const
+{
+ return NUM_REGISTERED_CHEATS;
+}
+
+//===========================================================================
+// CheatsDB::GetCheat
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+const Cheat*
+CheatsDB::GetCheat( eCheatID cheatID ) const
+{
+ // search for cheat
+ //
+ for( unsigned int i = 0; i < NUM_REGISTERED_CHEATS; i++ )
+ {
+ if( REGISTERED_CHEATS[ i ].m_cheatID == cheatID )
+ {
+ // found it!
+ //
+ return &(REGISTERED_CHEATS[ i ]);
+ }
+ }
+
+ // cheat not found, return NULL
+ //
+ return NULL;
+}
+
+//===========================================================================
+// CheatsDB::ConvertSequenceToIndex
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+unsigned int
+CheatsDB::ConvertSequenceToIndex( const eCheatInput* cheatInputs,
+ int numInputs )
+{
+ unsigned int cheatIndex = 0;
+
+ // convert cheat sequence inputs to an index number
+ //
+ // cheatIndex = 8 bit value w/ 2 bits allocated per cheat input
+ // bits [0,1] = cheat input 0
+ // bits [2,3] = cheat input 1
+ // bits [4,5] = cheat input 2
+ // bits [6,7] = cheat input 3
+ //
+ rAssert( cheatInputs != NULL );
+ for( int i = 0; i < numInputs; i++ )
+ {
+ cheatIndex |= (cheatInputs[ i ] << (i * 2));
+ }
+
+ rAssertMsg( cheatIndex < s_maxNumPossibleCheats,
+ "ERROR: *** Invalid cheat index computed!" );
+
+ return cheatIndex;
+}
+
+//===========================================================================
+// CheatsDB::PrintCheatInfo
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CheatsDB::PrintCheatInfo( const Cheat* cheat, char* buffer )
+{
+ rAssert( cheat != NULL );
+ rAssert( buffer != NULL );
+
+ sprintf( buffer, "%s: { ", cheat->m_cheatName );
+
+ const int MAX_CHEAT_INPUT_NAME_LENGTH = 4; // truncate if necessary
+ char inputNames[ NUM_CHEAT_SEQUENCE_INPUTS ][ MAX_CHEAT_INPUT_NAME_LENGTH ];
+
+ for( unsigned int i = 0; i < NUM_CHEAT_SEQUENCE_INPUTS; i++ )
+ {
+ strncpy( inputNames[ i ],
+ CheatInputHandler::GetInputName( cheat->m_cheatInputs[ i ] ),
+ MAX_CHEAT_INPUT_NAME_LENGTH - 1 );
+ inputNames[ i ][ MAX_CHEAT_INPUT_NAME_LENGTH - 1 ] = '\0';
+
+ if( i != 0 ) // insert comma, except for the first one
+ {
+ strcat( buffer, ", " );
+ }
+
+ strcat( buffer, inputNames[ i ] );
+ }
+
+ strcat( buffer, " }" );
+/*
+ sprintf( buffer, "%s: { %s, %s, %s, %s }",
+ cheat->m_cheatName,
+ CheatInputHandler::GetInputName( cheat->m_cheatInputs[ 0 ] ),
+ CheatInputHandler::GetInputName( cheat->m_cheatInputs[ 1 ] ),
+ CheatInputHandler::GetInputName( cheat->m_cheatInputs[ 2 ] ),
+ CheatInputHandler::GetInputName( cheat->m_cheatInputs[ 3 ] ) );
+*/
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
diff --git a/game/code/cheats/cheats.h b/game/code/cheats/cheats.h
new file mode 100644
index 0000000..57a068c
--- /dev/null
+++ b/game/code/cheats/cheats.h
@@ -0,0 +1,119 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CheatsDB
+//
+// Description: Interface for the CheatsDB class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/09/19 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef CHEATS_H
+#define CHEATS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <cheats/cheatinputs.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+typedef unsigned int CHEATBITMASK;
+
+const unsigned int MAX_NUM_CHEATS = sizeof( CHEATBITMASK ) * 8;
+
+enum eCheatID
+{
+ CHEAT_ID_UNREGISTERED = -1,
+
+ CHEAT_ID_MOTHER_OF_ALL_CHEATS, // display all cheats on screen
+
+ // this is not actually a cheat, but is merely to indicate that
+ // all cheat id's following this will be enabled whenever the
+ // CHEAT_ID_UNLOCK_EVERYTHING cheat is enabled
+ //
+ CHEAT_ID_UNLOCK_BEGIN,
+
+ CHEAT_ID_UNLOCK_CARDS = CHEAT_ID_UNLOCK_BEGIN,
+ CHEAT_ID_UNLOCK_SKINS,
+ CHEAT_ID_UNLOCK_MISSIONS,
+ CHEAT_ID_UNLOCK_MOVIES,
+ CHEAT_ID_UNLOCK_VEHICLES,
+ CHEAT_ID_UNLOCK_EVERYTHING, // unlock everything!!!
+
+ CHEAT_ID_NO_TOP_SPEED,
+ CHEAT_ID_HIGH_ACCELERATION,
+ CHEAT_ID_CAR_JUMP_ON_HORN,
+ CHEAT_ID_FULL_DAMAGE_TO_CAR,
+ CHEAT_ID_ONE_TAP_TRAFFIC_DEATH,
+ CHEAT_ID_EXTRA_TIME,
+ CHEAT_ID_SHOW_AVATAR_POSITION,
+ CHEAT_ID_KICK_TOGGLES_CHARACTER_MODEL,
+ CHEAT_ID_EXTRA_COINS,
+ CHEAT_ID_UNLOCK_CAMERAS,
+ CHEAT_ID_SPEED_CAM = CHEAT_ID_UNLOCK_CAMERAS,
+ CHEAT_ID_DEMO_TEST,
+ CHEAT_ID_PLAY_CREDITS_DIALOG,
+ CHEAT_ID_SHOW_SPEEDOMETER,
+ CHEAT_ID_REDBRICK,
+ CHEAT_ID_INVINCIBLE_CAR,
+ CHEAT_ID_SHOW_TREE,
+ CHEAT_ID_TRIPPY,
+
+ NUM_CHEATS
+};
+
+struct Cheat
+{
+ eCheatID m_cheatID;
+ eCheatInput m_cheatInputs[ NUM_CHEAT_SEQUENCE_INPUTS ];
+ const char* m_cheatName;
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class CheatsDB
+{
+public:
+ CheatsDB();
+ virtual ~CheatsDB();
+
+ eCheatID GetCheatID( unsigned int cheatIndex ) const;
+
+ unsigned int GetNumRegisteredCheats() const;
+ const Cheat* GetCheat( eCheatID cheatID ) const;
+
+ static unsigned int ConvertSequenceToIndex( const eCheatInput* cheatInputs,
+ int numInputs = NUM_CHEAT_SEQUENCE_INPUTS );
+
+ static void PrintCheatInfo( const Cheat* cheat, char* buffer );
+
+private:
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CheatsDB( const CheatsDB& );
+ CheatsDB& operator= ( const CheatsDB& );
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ static unsigned int s_maxNumPossibleCheats;
+ eCheatID* m_cheats;
+
+};
+
+#endif // CHEATS_H
diff --git a/game/code/console/allconsole.cpp b/game/code/console/allconsole.cpp
new file mode 100644
index 0000000..0288204
--- /dev/null
+++ b/game/code/console/allconsole.cpp
@@ -0,0 +1,5 @@
+#include <console/console.cpp>
+#include <console/debugconsolecallback.cpp>
+#include <console/fbstricmp.cpp>
+#include <console/nameinsensitive.cpp>
+#include <console/upcase.cpp>
diff --git a/game/code/console/console.cpp b/game/code/console/console.cpp
new file mode 100644
index 0000000..922b5d8
--- /dev/null
+++ b/game/code/console/console.cpp
@@ -0,0 +1,2356 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: console.cpp
+//
+// Description:
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+// Radcore
+#include <radDebug.hpp>
+#include <raddebugwatch.hpp>
+#include <radFile.hpp>
+// Pure3D
+#include <p3d/utility.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+#include <console/console.h>
+
+#include <console/fbstricmp.h>
+#include <main/commandlineoptions.h>
+#include <memory/srrmemory.h>
+#include <memory/memorypool.h>
+#include <loading/loadingmanager.h>
+
+#ifdef RAD_DEBUG
+#include <raddebugconsole.hpp>
+#include <console/debugconsolecallback.h>
+#endif
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+#define PROFILE_PARSER 0
+
+const char* gErrFileName = 0;
+unsigned int gErrLineNum = ~0;
+
+//---------------------------------------------------------------------------//
+
+// Static pointer to instance of singleton.
+Console* Console::spInstance = 0;
+
+static char gConsoleEntry[Console::MAX_STRING_LENGTH];
+
+//log message buffers needed so the async writes to the log file don't bog
+static char* gConsoleMsgBuffer[2] = {0};
+static char* gConsoleMsgBufferPtr = 0;
+static int gConsoleBufferIndex = 0;
+
+#ifdef RAD_GAMECUBE
+FBMemoryPool Console::FunctionTableEntry::sMemoryPool( sizeof(Console::FunctionTableEntry), Console::MAX_FUNCTIONS, GMA_GC_VMM );
+FBMemoryPool Console::AliasTableEntry::sMemoryPool( sizeof(Console::AliasTableEntry), Console::MAX_ALIASES, GMA_GC_VMM );
+#else
+FBMemoryPool Console::FunctionTableEntry::sMemoryPool( sizeof(Console::FunctionTableEntry), Console::MAX_FUNCTIONS, GMA_PERSISTENT );
+FBMemoryPool Console::AliasTableEntry::sMemoryPool( sizeof(Console::AliasTableEntry), Console::MAX_ALIASES, GMA_PERSISTENT );
+#endif
+
+//=============================================================================
+// static bool cConsoleTrue
+//=============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//=============================================================================
+static bool cConsoleTrue( int argc, char** argv )
+{
+ argc;
+ argv;
+ return( true );
+}
+
+static bool cConsoleFalse( int argc, char** argv )
+{
+ argc;
+ argv;
+ return( false );
+}
+
+static void cConsoleExit(int argc, char **argv)
+{
+ argc;
+ argv;
+ //does nothing, but the parser will terminate executing a script
+}
+
+static void cConsoleExec(int argc, char **argv)
+{
+ GetConsole()->ExecuteScriptSync(argv[1], true);
+}
+
+static void cConsoleSetLogMode(int argc, char **argv)
+{
+ argc;
+ if (! smStricmp(argv[1], "on"))
+ GetConsole()->SetConsoleLogMode(Console::LOGMODE_ON);
+ else if (! smStricmp(argv[1], "append"))
+ GetConsole()->SetConsoleLogMode(Console::LOGMODE_APPEND);
+ else
+ GetConsole()->SetConsoleLogMode(Console::LOGMODE_OFF);
+}
+
+static void cConsoleFlushLog(int argc, char **argv)
+{
+ GetConsole()->FlushConsoleLogFile();
+}
+
+static void cConsoleEcho(int argc, char **argv)
+{
+ if (argc == 3)
+ GetConsole()->Printf(atoi(argv[1]), argv[2]);
+ else
+ GetConsole()->Printf(argv[1]);
+}
+
+static void cConsoleError(int argc, char **argv)
+{
+ GetConsole()->Errorf(argv[1]);
+}
+
+static void cConsoleListFunctions(int argc, char **argv)
+{
+ argc;
+ argv;
+ GetConsole()->ListConsoleFunctions();
+}
+
+static void cConsoleListenChannel(int argc, char **argv)
+{
+ argc;
+ GetConsole()->ConsoleListenChannel(atoi(argv[1]));
+}
+
+static void cConsoleBlockChannel(int argc, char **argv)
+{
+ argc;
+ GetConsole()->ConsoleBlockChannel(atoi(argv[1]));
+}
+
+static void cConsoleAlias(int argc, char **argv)
+{
+ GetConsole()->AddAlias(argv[1], argv[2], argc - 3, &argv[3]);
+}
+
+static void cWatcherExecConsoleCommand(void *)
+{
+ GetConsole()->Printf("==> %s", gConsoleEntry);
+ GetConsole()->Evaluate(gConsoleEntry, "CMDLine");
+}
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Console::CreateInstance
+//==============================================================================
+//
+// Description: Creates the Console.
+//
+// Parameters: None.
+//
+// Return: Pointer to the Console.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+Console* Console::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "Console" );
+ rTuneAssert( spInstance == 0 );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+
+ spInstance = new Console;
+ rTuneAssert( spInstance );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+
+ Console::Initialize();
+MEMTRACK_POP_GROUP( "Console" );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// Console::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the Console singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the Console.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+Console* Console::GetInstance()
+{
+ rTuneAssert( spInstance != 0 );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// Console::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the Console.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void Console::DestroyInstance()
+{
+ rTuneAssert( spInstance != 0 );
+
+ delete spInstance;
+ spInstance = 0;
+}
+
+
+//==============================================================================
+// Console::Initialize
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+bool Console::Initialize()
+{
+//the console doesn't use remote drive in the release build
+#ifdef RAD_DEBUG
+MEMTRACK_PUSH_GROUP( "Console Initialize" );
+
+ if( !CommandLineOptions::Get( CLO_FIREWIRE ) )
+ {
+ //initialize the console log buffers
+ gConsoleMsgBuffer[0] = new(GMA_DEBUG) char[MAX_STRING_LENGTH * MAX_BUFFERS];
+ gConsoleMsgBuffer[1] = new(GMA_DEBUG) char[MAX_STRING_LENGTH * MAX_BUFFERS];
+ gConsoleMsgBufferPtr = gConsoleMsgBuffer[0];
+ gConsoleBufferIndex = 0;
+
+ //add a function to allow the program to generate a console.log file
+ GetConsole()->AddFunction("ifTrue", cConsoleTrue, "ifTrue() { } //returns true", 0, 0);
+ GetConsole()->AddFunction("ifFalse", cConsoleFalse, "ifFalse() { } //returns false", 0, 0);
+ GetConsole()->AddFunction("Exit", cConsoleExit, "Exit(msg);", 1, 1);
+ GetConsole()->AddFunction("ConsoleExec", cConsoleExec, "ConsoleExec(file);", 1, 1);
+ GetConsole()->AddFunction("ConsoleSetLogMode", cConsoleSetLogMode, "ConsoleSetLogMode(off/on/append);", 1, 1);
+ GetConsole()->AddFunction("ConsoleFlushLog", cConsoleFlushLog, "ConsoleFlushLog();", 0, 0);
+ GetConsole()->AddFunction("Echo", cConsoleEcho, "Echo([channel, ] msg);", 1, 2);
+ GetConsole()->AddFunction("Error", cConsoleError, "Error(msg);", 1, 1);
+ GetConsole()->AddFunction("ConsoleListFunctions", cConsoleListFunctions, "ConsoleListFunctions();", 0, 0);
+ GetConsole()->AddFunction("ConsoleListenChannel", cConsoleListenChannel, "ConsoleListenChannel(-1 or [0 .. 31]);", 1, 1);
+ GetConsole()->AddFunction("ConsoleBlockChannel", cConsoleBlockChannel, "ConsoleBlockChannel(-1 or [0 .. 31]);", 1, 1);
+ GetConsole()->AddFunction("Alias", cConsoleAlias, "Alias(aliasName, functionName, arg1, arg2, \"%1\", arg3, \"%2\", etc...);", 2, MAX_ARGS);
+
+ //add the console entry string and it's update function
+ radDbgWatchAddString(gConsoleEntry, sizeof(gConsoleEntry), "Console command", "\\", cWatcherExecConsoleCommand);
+
+ //add the debugconsole window as well...
+ radDebugConsoleCreate(&(spInstance->mDebugConsole), GMA_DEBUG);
+
+ if (spInstance->mDebugConsole)
+ {
+ //set the title and background color
+ spInstance->mDebugConsole->SetTitle( "Fish Bulb Console" );
+ spInstance->mDebugConsole->SetBackgroundColor(RGB( 0, 0, 0x188 ));
+ spInstance->mDebugConsole->Clear();
+
+ spInstance->mDebugConsole->SetCursorPosition(0, 0);
+ spInstance->mDebugConsole->SetTextColor(RGB(0xFF, 0xFF,0xFF));
+
+ spInstance->mDebugConsoleCallback = new(GMA_DEBUG) DebugConsoleCallback();
+ spInstance->mDebugConsole->SetKeyboardInputCallback(spInstance->mDebugConsoleCallback);
+ spInstance->mDebugConsole->SetPointerInputCallback(spInstance->mDebugConsoleCallback);
+
+ //initialize the debug console line number
+ spInstance->mDebugConsoleLine = 0;
+ }
+ }
+MEMTRACK_POP_GROUP( "Console Initialize" );
+#endif
+
+ return (true);
+}
+
+
+//==============================================================================
+// Console::Console
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+Console::Console()
+:
+mpCallback( 0 )
+{
+ // Initialize the tables
+ int i = 0;
+ for( i = 0; i < MAX_FUNCTIONS; ++i )
+ {
+ mFunctionTable[i] = 0;
+ }
+
+ for( i = 0; i < MAX_ALIASES; ++i )
+ {
+ mAliasTable[i] = 0;
+ }
+
+ mFunctionCount = 0;
+ mAliasCount = 0;
+
+
+ //create the argv array
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+
+ mArgv = new char*[MAX_ARGS];
+ mPrevArgv = new char*[MAX_ARGS];
+
+ for( i = 0; i < MAX_ARGS; ++i )
+ {
+ mArgv[i] = new char[MAX_ARG_LENGTH];
+ mPrevArgv[i] = new char[MAX_ARG_LENGTH];
+ }
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+ //set the log file mode
+ mLogFile = 0;
+ mLogMode = LOGMODE_OFF;
+
+ //initialize the console listen channels to everything
+ mChannelMask = ~0;
+
+#ifdef RAD_DEBUG
+ //initialize the radcore debugconsole members
+ mDebugConsole = 0;
+#endif
+}
+
+
+
+//==============================================================================
+// Console::~Console
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+Console::~Console()
+{
+ //clean up the console
+ if( spInstance )
+ {
+#ifdef RAD_DEBUG
+ if( spInstance->mDebugConsole )
+ {
+ spInstance->mDebugConsole->Release();
+ delete spInstance->mDebugConsoleCallback;
+ }
+#endif
+
+ if( spInstance->mLogFile )
+ spInstance->mLogFile->WaitForCompletion();
+ }
+
+ //clean up after the watcher
+#ifdef RAD_DEBUG
+ delete [] gConsoleMsgBuffer[0];
+ delete [] gConsoleMsgBuffer[1];
+ radDbgWatchDelete(gConsoleEntry);
+#endif
+
+ int i = 0;
+
+ //delete the tables
+ for( i = 0; i < mFunctionCount; ++i )
+ {
+ if (mFunctionTable[i])
+ {
+ delete mFunctionTable[i];
+ }
+
+ }
+
+ for( i = 0; i < MAX_ALIASES; ++i )
+ {
+ if( mAliasTable[i] )
+ {
+ delete mAliasTable[i];
+ }
+
+ }
+
+
+ //delete the argv array
+ for( i = 0; i < MAX_ARGS; ++i )
+ {
+ delete [] mArgv[i];
+ delete [] mPrevArgv[i];
+ }
+
+ delete [] mArgv;
+ delete [] mPrevArgv;
+
+
+ //close the logfile
+ if( mLogFile )
+ {
+ FlushLogFile();
+ mLogFile->Release();
+ }
+}
+
+
+//==============================================================================
+// Console::SetConsoleLogMode
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+bool Console::SetConsoleLogMode(int logMode)
+{
+#ifndef RAD_DEBUG
+ return (false);
+#else
+ //make sure we have a console
+ if (!spInstance)
+ return (false);
+
+ return (spInstance->SetLogMode(logMode));
+#endif
+}
+
+bool Console::SetLogMode(int logMode)
+{
+ //no work to do if we're not actually changing the mode
+ if (logMode == mLogMode)
+ return (true);
+
+ switch (logMode)
+ {
+ case LOGMODE_OFF:
+ if (mLogFile)
+ {
+ mLogFile->WaitForCompletion();
+ mLogFile->Release();
+ mLogFile = 0;
+ }
+ mLogMode = logMode;
+ return (true);
+
+ case LOGMODE_ON:
+ if (!mLogFile)
+ {
+ char fullPath[256];
+// RM sprintf(fullPath, "%sconsole.log", ResourceManager::GetInstance()->GetRemoteDriveName());
+ ::radFileOpen(&mLogFile, fullPath, true, CreateAlways, NormalPriority, 0, RADMEMORY_ALLOC_TEMP);
+ if (mLogFile)
+ {
+ const char *msg = "\r\n******** CONSOLE LOG OPENED ********\r\n";
+ mLogFile->WaitForCompletion();
+ mLogFile->WriteAsync(msg, strlen(msg));
+ mLogFile->WaitForCompletion();
+ mLogMode = logMode;
+ return (true);
+ }
+ else
+ {
+ mLogMode = LOGMODE_OFF;
+ return (false);
+ }
+ }
+ return (true);
+
+ case LOGMODE_APPEND:
+ if (!mLogFile)
+ {
+ char fullPath[256];
+// RM sprintf(fullPath, "%sconsole.log", ResourceManager::GetInstance()->GetRemoteDriveName());
+ ::radFileOpen(&mLogFile, fullPath, true, OpenAlways, NormalPriority, 0, RADMEMORY_ALLOC_TEMP);
+ if (mLogFile)
+ {
+ const char *msg = "\r\n******** CONSOLE LOG OPENED ********\r\n";
+ mLogFile->WaitForCompletion();
+ int fileSize = mLogFile->GetSize();
+ mLogFile->SetPositionAsync(fileSize);
+ mLogFile->WaitForCompletion();
+ mLogFile->WriteAsync(msg, strlen(msg));
+ mLogFile->WaitForCompletion();
+ mLogMode = logMode;
+ return (true);
+ }
+ else
+ {
+ mLogMode = LOGMODE_OFF;
+ return (false);
+ }
+ }
+ return (true);
+ }
+
+ // Set Error Condition, to remove compiler warning.
+ return (false);
+}
+
+
+//==============================================================================
+// Console::Printf
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::Printf(char *fmt, ...)
+{
+#ifdef RAD_DEBUG
+ //make sure we have a console
+ if (!spInstance)
+ return;
+
+ char message[MAX_STRING_LENGTH];
+ va_list args;
+ int i = 0;
+ va_start(args, fmt);
+ i = vsprintf(message, fmt, args);
+ va_end(args);
+
+ // do some tune printf for designers
+ rTunePrintf( message );
+
+ //record the message to the console log file
+ spInstance->LogMessage(0, message);
+#endif
+}
+
+//for now, it does the same as Printf()... should also echo
+//to an error page in the DebugInfo stuff Chakib is working on!
+void Console::Errorf(char *fmt, ...)
+{
+#ifdef RAD_DEBUG
+ //make sure we have a console
+ if (!spInstance)
+ return;
+
+ char message[MAX_STRING_LENGTH];
+ va_list args;
+ int i = 0;
+ va_start(args, fmt);
+ i = vsprintf(message, fmt, args);
+ va_end(args);
+
+ //record the message to the console log file
+ spInstance->LogMessage(0, message, true);
+#endif
+}
+
+void Console::Printf(int channel, char *fmt, ...)
+{
+#ifdef RAD_DEBUG
+ //make sure we have a console
+ if (!spInstance)
+ return;
+
+ char message[MAX_STRING_LENGTH];
+
+ //prefix the message with the channel number
+ sprintf(message, "ch%d ", channel);
+ char *msgPtr;
+ if (channel >= 10)
+ msgPtr = &message[5];
+ else
+ msgPtr = &message[4];
+
+ va_list args;
+ int i = 0;
+ va_start(args, fmt);
+ i = vsprintf(msgPtr, fmt, args);
+ va_end(args);
+
+ // do some tune printf for designers
+ rTunePrintf( message );
+
+ //record the message to the console log file
+ spInstance->LogMessage(channel, message);
+#endif
+}
+
+
+//==============================================================================
+// Console::AssertFatal
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::AssertFatal(char *fmt, ...)
+{
+ char msgFmt[512];
+ sprintf(msgFmt, "File: %s, line: %d %s", gErrFileName, gErrLineNum, fmt);
+ char message[MAX_STRING_LENGTH];
+ va_list args;
+ int i = 0;
+ va_start(args, fmt);
+ i = vsprintf(message, msgFmt, args);
+ va_end(args);
+
+ //record the message to the console log file
+ this->LogMessage(0, message, true);
+
+ //flush the log file in case we're about to crash
+ this->FlushLogFile();
+
+ //pop the assert
+ rTuneAssertMsg( 0, message );
+}
+
+
+//==============================================================================
+// Console::AssertWarn
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::AssertWarn(char *fmt, ...)
+{
+ char msgFmt[512];
+ sprintf(msgFmt, "File: %s, line: %d %s", gErrFileName, gErrLineNum, fmt);
+ char message[MAX_STRING_LENGTH];
+ va_list args;
+ int i = 0;
+ va_start(args, fmt);
+ i = vsprintf(message, msgFmt, args);
+ va_end(args);
+
+ //record the message to the console log file
+ spInstance->LogMessage(0, message, true);
+
+ //flush the log file in case we're about to crash
+ spInstance->FlushLogFile();
+
+ //assert warn using the p3d versions
+ rDebugWarningFail_Implementation(message, gErrFileName, gErrLineNum);
+}
+
+
+//==============================================================================
+// Console::LogMessage
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::LogMessage(int channel, char* msg, bool warning)
+{
+ //see if we're listening to this channel
+ int listenChannel = channel;
+ if (channel < 0 || channel > 31)
+ listenChannel = 0;
+ if (! (mChannelMask & (1 << listenChannel)))
+ return;
+
+ //append an '\r\n' to the end of every message
+ char logMessage[512];
+ if (strchr(msg, '\n'))
+ strcpy(logMessage, msg);
+ else
+ sprintf(logMessage, "%s\r\n", msg);
+
+ //print the message to the console
+ p3d::printf(logMessage);
+
+#ifdef RAD_DEBUG
+ //print the message out the the radcore debug console as well...
+ AddDebugConsoleLine(logMessage, warning);
+#endif
+
+ //write the msg out to the console.log (buffered)
+ if (mLogMode == LOGMODE_ON || mLogMode == LOGMODE_APPEND)
+ {
+ //see if there's enough room in the buffer to store the string
+ int bytesUsed = int(gConsoleMsgBufferPtr) - int(gConsoleMsgBuffer[gConsoleBufferIndex]);
+ int bytesLeft = (MAX_STRING_LENGTH * MAX_BUFFERS) - bytesUsed;
+ int strLength = strlen(logMessage);
+
+ //if we don't have space, wait for file completion, then start using the next buffer
+ if (bytesUsed > 0 && bytesLeft < strLength)
+ {
+ mLogFile->WaitForCompletion();
+ mLogFile->WriteAsync(gConsoleMsgBuffer[gConsoleBufferIndex], bytesUsed);
+ gConsoleBufferIndex = 1 - gConsoleBufferIndex;
+ gConsoleMsgBufferPtr = gConsoleMsgBuffer[gConsoleBufferIndex];
+ }
+
+ //copy the string into the static console msg buffer
+ strcpy(gConsoleMsgBufferPtr, logMessage);
+
+ //increment the console msg buffer
+ gConsoleMsgBufferPtr += strLength;
+ }
+}
+
+
+//==============================================================================
+// Console::FlushConsoleLogFile
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::FlushConsoleLogFile()
+{
+ //flush the log file in case we're about to crash
+ spInstance->FlushLogFile();
+}
+
+
+//==============================================================================
+// Console::FlushLogFile
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::FlushLogFile()
+{
+ if (mLogMode == LOGMODE_ON || mLogMode == LOGMODE_APPEND)
+ {
+ //see if there's enough room in the buffer to store the string
+ int bytesUsed = int(gConsoleMsgBufferPtr) - int(gConsoleMsgBuffer[gConsoleBufferIndex]);
+
+ //if there is info waiting in the buffer to be written out...
+ if (bytesUsed > 0)
+ mLogFile->WriteAsync(gConsoleMsgBuffer[gConsoleBufferIndex], bytesUsed);
+
+ //wait for the File server to complete
+ mLogFile->WaitForCompletion();
+
+ //reset the buffer vars
+ gConsoleBufferIndex = 0;
+ gConsoleMsgBufferPtr = gConsoleMsgBuffer[0];
+ }
+}
+
+#ifdef RAD_DEBUG
+//debug console methods
+
+
+//==============================================================================
+// char* FormatDebugConsoleString
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+
+const unsigned char MAX_STRING_LEN = 128;
+
+static const char* FormatDebugConsoleString(const char *text, bool consoleEntry)
+{
+ static char formatString[MAX_STRING_LEN + 1];
+
+ //copy the first 80 characters of the text
+ if (consoleEntry)
+ {
+ formatString[0] = '=';
+ formatString[1] = '>';
+ strncpy(&formatString[2], text, MAX_STRING_LEN - 2);
+ }
+ else
+ strncpy(formatString, text, MAX_STRING_LEN);
+
+ //make sure there are no \r\n's in the string
+ char *eolChar = strchr(formatString, '\r');
+ if (eolChar)
+ *eolChar = '\0';
+ eolChar = strchr(formatString, '\n');
+ if (eolChar)
+ *eolChar = '\0';
+
+ //pad the string with spaces
+ formatString[MAX_STRING_LEN] = '\0';
+ int strlength = strlen(formatString);
+ if (strlength < MAX_STRING_LEN)
+ memset(&formatString[strlength], ' ', MAX_STRING_LEN - strlength);
+ formatString[MAX_STRING_LEN] = '\0';
+
+ //return the formatted string
+ return(formatString);
+}
+
+
+//==============================================================================
+// Console::AddDebugConsoleLine
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::AddDebugConsoleLine(const char *text, bool warning)
+{
+ //make sure we have a debugconsole
+ if (! mDebugConsole)
+ return;
+
+ //create the string to send to the debug console
+ const char *formatString = FormatDebugConsoleString(text, false);
+ if (warning)
+ mDebugConsole->SetTextColor(RGB(0xFF, 0, 0));
+ else
+ mDebugConsole->SetTextColor(RGB(0xFF, 0xFF, 0xFF));
+ mDebugConsole->TextOutAt(formatString, 0, mDebugConsoleLine++);
+
+ //create the console entry string
+ int cursorPosition;
+ const char *consoleEntry = mDebugConsoleCallback->GetConsoleEntry(&cursorPosition);
+ SetDebugConsoleEntry(consoleEntry, cursorPosition);
+}
+
+
+//==============================================================================
+// Console::SetDebugConsoleEntry
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::SetDebugConsoleEntry(const char *text, int cursorPosition)
+{
+ const char *formatString = FormatDebugConsoleString(text, true);
+
+ //update the consoleEntry
+ mDebugConsole->SetTextColor(RGB(0xFF, 0xFF, 0xFF));
+ mDebugConsole->TextOutAt(formatString, 0, mDebugConsoleLine);
+
+ //update the cursor position
+ spInstance->mDebugConsole->SetCursorPosition(cursorPosition + 2, mDebugConsoleLine);
+}
+
+#endif
+
+
+//==============================================================================
+// Console::ConsoleListenChannel
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::ConsoleListenChannel(int channel)
+{
+ //make sure we have a console
+ if (!spInstance)
+ return;
+
+ if (channel <= -2 || channel > 31)
+ {
+ Printf("Console error - invalid channel: %d. Specify -1 for all channels, or 0 to 31", channel);
+ return;
+ }
+
+ spInstance->ListenChannel(channel);
+}
+
+
+//==============================================================================
+// Console::ConsoleBlockChannel
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::ConsoleBlockChannel(int channel)
+{
+ //make sure we have a console
+ if (!spInstance)
+ return;
+
+ if (channel <= -2 || channel > 31)
+ {
+ Printf("Console error - invalid channel: %d. Specify -1 for all channels, or 0 to 31", channel);
+ return;
+ }
+
+ spInstance->BlockChannel(channel);
+}
+
+
+//==============================================================================
+// Console::ListenChannel
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::ListenChannel(int channel)
+{
+ //validate params
+ if (channel <= -2 || channel > 31)
+ return;
+
+ if (channel == -1)
+ mChannelMask = 0xffffffff;
+ else
+ mChannelMask |= (1 << channel);
+}
+
+
+//==============================================================================
+// Console::BlockChannel
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::BlockChannel(int channel)
+{
+ //validate params
+ if (channel <= -2 || channel > 31)
+ return;
+
+ //note: can't block channel 0
+ if (channel == -1)
+ mChannelMask = (1 << 0);
+ else
+ mChannelMask &= ~(1 << channel);
+}
+
+
+//==============================================================================
+// Console::ListConsoleFunctions
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::ListConsoleFunctions()
+{
+ if (!spInstance)
+ return;
+
+ spInstance->ListFunctions();
+}
+
+
+//==============================================================================
+// Console::ListFunctions
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::ListFunctions()
+{
+ //for (int i = 0; i < mFunctionCount; i++)
+ //Printf(mFunctionTable[i]->help.GetText());
+}
+
+
+//==============================================================================
+// Console::AddFunction
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+bool Console::AddFunction
+(
+ const char* name,
+ CONSOLE_FUNCTION funcPtr,
+ const char* helpString,
+ int minArgs,
+ int maxArgs
+)
+{
+ //make sure we've initialized the global console
+ if (!spInstance)
+ {
+ rDebugString("Error - Console::AddFunction(): Console::Initialize() hasn't been called yet.");
+ return (false);
+ }
+
+ return (spInstance->AddFunc(name, funcPtr, 0, helpString, minArgs, maxArgs));
+}
+
+
+//==============================================================================
+// Console::AddFunction
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+bool Console::AddFunction
+(
+ const char* name,
+ CONSOLE_BOOL_FUNCTION funcPtr,
+ const char* helpString,
+ int minArgs,
+ int maxArgs
+)
+{
+ //make sure we've initialized the global console
+ if (!spInstance)
+ {
+ rDebugString("Error - Console::AddFunction(): Console::Initialize() hasn't been called yet.");
+ return (false);
+ }
+
+ return (spInstance->AddFunc(name, 0, funcPtr, helpString, minArgs, maxArgs));
+}
+
+
+
+//==============================================================================
+// Console::AddFunc
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+bool Console::AddFunc
+(
+ const char* name,
+ CONSOLE_FUNCTION funcPtr,
+ CONSOLE_BOOL_FUNCTION boolFuncPtr,
+ const char* helpString,
+ int minArgs,
+ int maxArgs
+)
+{
+ //validate the input
+ if (!name || !name[0] || strlen(name) >= MAX_STRING_LENGTH)
+ {
+ rDebugString("Error - Console::AddFunction(): invalid function name specified.\n");
+ return (false);
+ }
+ if (!helpString || strlen(helpString) >= MAX_STRING_LENGTH)
+ {
+ rDebugString("Error - Console::AddFunction(): invalid help string specified.\n");
+ return (false);
+ }
+ if (!funcPtr && !boolFuncPtr)
+ {
+ rDebugString("Error - Console::AddFunction(): no CONSOLE_FUNCTION specified.\n");
+ return (false);
+ }
+ if (minArgs < 0)
+ {
+ rDebugString("Error - Console::AddFunction(): invalid minArgs specified.\n");
+ return (false);
+ }
+ if (maxArgs < minArgs || maxArgs > MAX_ARGS)
+ {
+ rDebugString("Error - Console::AddFunction(): invalid maxArgs specified.\n");
+ return (false);
+ }
+
+ //make sure we have room for one more
+ if( mFunctionCount >= MAX_FUNCTIONS )
+ {
+ rDebugString("Error - Console::AddFunction(): Maximum number of functions has been reached.\n");
+ return (false);
+ }
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+#endif
+
+ //create the new function
+ FunctionTableEntry* newFunction;
+ if( funcPtr )
+ {
+ newFunction = new FunctionTableEntry(name, funcPtr, helpString, minArgs, maxArgs);
+ }
+ else
+ {
+ newFunction = new FunctionTableEntry(name, boolFuncPtr, helpString, minArgs, maxArgs);
+ }
+
+ //add the function to the table
+ mFunctionTable[mFunctionCount] = newFunction;
+ mFunctionLookup[radMakeCaseInsensitiveKey32(name)] = mFunctionCount;
+
+ //increment the function count
+ mFunctionCount++;
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+#endif
+
+ // return success condition
+ return (true);
+}
+
+
+//==============================================================================
+// Console::AddAlias
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+bool Console::AddAlias
+(
+ const char* name,
+ const char* functionName,
+ int argc,
+ char** argv
+)
+{
+ //make sure we've initialized the global console
+ if (!spInstance)
+ {
+ rDebugString("Error - Console::AddFunction(): Console::Initialize() hasn't been called yet.");
+ return (false);
+ }
+
+ return (spInstance->AddFunctionAlias(name, functionName, argc, argv));
+}
+
+
+//==============================================================================
+// Console::AddFunctionAlias
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+bool Console::AddFunctionAlias
+(
+ const char* name,
+ const char* functionName,
+ int argc,
+ char** argv
+)
+{
+ //validate the input
+ if (!name || !name[0] || strlen(name) >= MAX_STRING_LENGTH)
+ {
+ rDebugString("Error - Console::AddFunctionAlias(): invalid function name specified.\n");
+ return (false);
+ }
+
+ //make sure we have room for one more
+ if (mAliasCount >= MAX_FUNCTIONS)
+ {
+ rDebugString("Error - Console::AddFunctionAlias(): Maximum number of alias' has been reached.\n");
+ return (false);
+ }
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+#endif
+
+ //create the new alias
+ AliasTableEntry* newAlias = new AliasTableEntry(name, functionName, argc, argv);
+
+ //insert the new alias into the table alphabetically
+ int index = 0;
+ while (index < mAliasCount)
+ {
+ if (smStricmp(mAliasTable[index]->name, name) > 0)
+ break;
+
+ index++;
+ }
+
+ //the new alias should be inserted at position "index", shift everything else down by one
+ for (int i = mAliasCount; i > index; i--)
+ mAliasTable[i] = mAliasTable[i - 1];
+
+ //add the alias to the table
+ mAliasTable[index] = newAlias;
+
+ //increment the alias count
+ mAliasCount++;
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+#endif
+
+ // return success condition
+ return (true);
+}
+
+
+//==============================================================================
+// Console::TabCompleteFunction
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+const tNameInsensitive& Console::TabCompleteFunction
+(
+ const char* tabString,
+ const char* curFunction
+)
+{
+ static tNameInsensitive error( "ERROR" );
+ //make sure we've initialized the global console
+ if (!spInstance)
+ {
+ rDebugString("Error - Console::TabCompleteFunction(): Console::Initialize() hasn't been called yet.");
+ return error;
+ }
+
+ return (spInstance->TabComplete(tabString, curFunction));
+}
+
+
+//==============================================================================
+// Console::TabComplete
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+const tNameInsensitive& Console::TabComplete
+(
+ const char* tabString,
+ const char* curFunction
+)
+{
+ int i;
+ static tNameInsensitive error( "ERROR" );
+ //sanity check
+ if (! tabString || ! tabString[0])
+ {
+ rAssert( false );
+ return error;
+ }
+
+ //find the first function beginning with the tabString
+ //note - functions are stored alphabetically...
+ int firstFunctionIndex = -1;
+ for (i = 0; i < mFunctionCount; i++)
+ {
+ if (! smStrincmp(tabString, mFunctionTable[i]->name.GetText(), strlen(tabString)))
+ {
+ firstFunctionIndex = i;
+ break;
+ }
+ }
+
+ //if no function was found, return 0
+ if (firstFunctionIndex < 0)
+ {
+ rAssert( false );
+ return error;
+ }
+
+ //if there's no current function, return the first function we found
+ if (! curFunction || ! curFunction[0])
+ return (mFunctionTable[firstFunctionIndex]->name);
+
+ //if the current function doesn't contain the tabString, ???
+ if (smStrincmp(tabString, curFunction, strlen(tabString)))
+ return (mFunctionTable[firstFunctionIndex]->name);
+
+ //search for the next function that contains the tabstring, but follows the curFunction
+ int curFunctionIndex = -1;
+ for (i = firstFunctionIndex; i < mFunctionCount; i++)
+ {
+ if ( mFunctionTable[i]->name == curFunction )
+ {
+ curFunctionIndex = i;
+ break;
+ }
+ }
+
+ //if no current function was found, return the first function
+ if (curFunctionIndex < 0 || curFunctionIndex == mFunctionCount - 1)
+ return (mFunctionTable[firstFunctionIndex]->name);
+
+ //see if the next function also contains the tabString
+ if (! smStrincmp(tabString, mFunctionTable[curFunctionIndex + 1]->name.GetText(), strlen(tabString)))
+ return (mFunctionTable[curFunctionIndex + 1]->name);
+
+ //otherwise, return the first function found again
+ else
+ return (mFunctionTable[firstFunctionIndex]->name);
+}
+
+//---------------------------------------------------------------------------//
+
+static const char* gWhiteSpace = " \t\r\n";
+static const char* gEndOfToken = " \t\r\n,();[]{}+/*!@#$%^&:\"<>?'`~";
+
+char *Console::SkipWhiteSpace(const char *string)
+{
+ //this function should never be called with an invalid string
+ char *temp = const_cast<char*>(string);
+ while (true)
+ {
+ //break on an end of string character
+ if (*temp == '\0')
+ break;
+
+ //break when a non-white space character is found
+ char *found = strchr(gWhiteSpace, *temp);
+ if (! found)
+ break;
+
+ //see if we found an eol char
+ if (*found == '\n')
+ mLineNumber++;
+
+ //check the next character
+ temp++;
+ }
+
+ return (temp);
+}
+
+
+//==============================================================================
+// Console::FoundComment
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+bool Console::FoundComment(char** stringPtr)
+{
+ char *tokenPtr = *stringPtr;
+ if (*tokenPtr == '/')
+ {
+ tokenPtr++;
+
+ //see if we've found a line comment
+ if (*tokenPtr == '/')
+ {
+ while (*tokenPtr != '\r' && *tokenPtr != '\n' && *tokenPtr != '\0')
+ tokenPtr++;
+
+ //increment the line count
+ if (*tokenPtr == '\n')
+ mLineNumber++;
+
+ //now we've hit the end of the line or file
+ *stringPtr = tokenPtr;
+ return (true);
+ }
+
+ //else see if we've found a block comment
+ else if (*tokenPtr == '*')
+ {
+ //here we search for the end of the block: */
+ while (true)
+ {
+ tokenPtr++;
+ while (*tokenPtr != '*' && *tokenPtr != '\0')
+ {
+ //increment the line count
+ if (*tokenPtr == '\n')
+ mLineNumber++;
+
+ tokenPtr++;
+ }
+
+ //see if we've found the end of the file
+ if (*tokenPtr == '\0')
+ {
+ *stringPtr = tokenPtr;
+ return (true);
+ }
+
+ //else see if we've found the end of the comment block
+ else
+ {
+ tokenPtr++;
+ if (*tokenPtr == '/')
+ {
+ tokenPtr++;
+ *stringPtr = tokenPtr;
+ return (true);
+ }
+ }
+ }
+ }
+
+ //else no comment found
+ else
+ return (false);
+ }
+
+ //else no commment found
+ else
+ return (false);
+}
+
+
+//==============================================================================
+// Console::FindTokenEnd
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+char* Console::FindTokenEnd( const char* string )
+{
+ //this function should never be called with an invalid string
+ char *temp = const_cast<char*>(string);
+ bool finished = false;
+ while (true)
+ {
+ //break on an end of string character
+ if (*temp == '\0')
+ break;
+
+ //break on an end of token character
+ char *found = strchr(gEndOfToken, *temp);
+ if (found)
+ {
+ //see if we found an eol char
+ if (*found == '\n')
+ mLineNumber++;
+
+ break;
+ }
+
+ //neither was found, check the next character
+ temp++;
+ }
+
+ return (temp);
+}
+
+
+//==============================================================================
+// Console::ReadToken
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+bool Console::ReadToken(char **stringPtr, const char *requiredToken)
+{
+ //validate parameters
+ if (! stringPtr || ! *stringPtr || !requiredToken)
+ return (false);
+
+ //read the next token
+ char tempBuffer[MAX_STRING_LENGTH];
+ char *token = GetNextToken(stringPtr, tempBuffer);
+
+ //if we don't have a token, or it doesn't match what we're looking for, return false
+ if (! token || smStricmp(token, requiredToken))
+ return (false);
+
+ //everything is ok, return true
+ return (true);
+}
+
+
+//==============================================================================
+// Console::GetNextToken
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+char* Console::GetNextToken(char **stringPtr, char *tokenBuffer)
+{
+ //validate parameters
+ if (! stringPtr || ! *stringPtr || !tokenBuffer)
+ return (0);
+
+ char *tokenPtr = *stringPtr;
+ char *tokenEnd = 0;
+
+ //skip comments and white space until we reach the beginning of a valid token
+ while (true)
+ {
+ tokenPtr = SkipWhiteSpace(tokenPtr);
+ if (! FoundComment(&tokenPtr))
+ break;
+ }
+
+ //if the next character is a '\0', no more tokens
+ if (*tokenPtr == '\0')
+ return (0);
+
+ //if the token is enclosed in quotes, search for the end quote
+ if (*tokenPtr == '\"')
+ {
+ tokenEnd = tokenPtr + 1;
+ while (true)
+ {
+ tokenEnd = strchr(tokenEnd, '\"');
+ if (! tokenEnd)
+ return (0);
+
+ //make sure the previous character isn't a '\\'
+ if (*(tokenEnd - 1) == '\\')
+ tokenEnd++;
+
+ //otherwise, we've found our matching quotes
+ else
+ break;
+ }
+
+ //now we've got our string enclosed with quotes, copy it the contents to the tokenBuffer
+ int strLength = (tokenEnd - tokenPtr) - 1;
+ strncpy(tokenBuffer, tokenPtr + 1, strLength);
+ tokenBuffer[strLength] = '\0';
+
+ //update the next position in the string
+ *stringPtr = tokenEnd + 1;
+ return (tokenBuffer);
+ }
+
+ //else find the end of the token
+ else
+ {
+ tokenEnd = FindTokenEnd(tokenPtr);
+
+ //if 0 was returned, the token ends on a '\0'
+ if (! tokenEnd)
+ {
+ //copy the token to the tokenBuffer
+ strcpy(tokenBuffer, tokenPtr);
+
+ //update the next position in the string
+ *stringPtr = &tokenPtr[strlen(tokenPtr)];
+ return (tokenBuffer);
+ }
+
+ //if the tokenEnd == the tokenPtr, the token is either a '\0', or one of the token end chars
+ else if (tokenEnd == tokenPtr)
+ {
+ //copy the character into the tokenBuffer
+ tokenBuffer[0] = *tokenPtr;
+ tokenBuffer[1] = '\0';
+
+ //update the next position in the string
+ *stringPtr = tokenEnd + 1;
+ return (tokenBuffer);
+ }
+
+ else
+ {
+ //copy the token into the tokenBuffer
+ int strLength = tokenEnd - tokenPtr;
+ strncpy(tokenBuffer, tokenPtr, strLength);
+ tokenBuffer[strLength] = '\0';
+
+ //update the next position in the string
+ *stringPtr = tokenEnd;
+ return (tokenBuffer);
+ }
+ }
+}
+
+
+//==============================================================================
+// Console::Evaluate
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+bool Console::Evaluate( const char* string, const char* fileName )
+{
+ rTuneAssert( string != 0 );
+
+#if PROFILE_PARSER
+ float curTime = 0.0f, elapsedTime = 0.0f, totalTime = 0.0f;
+#endif
+
+ // make sure we have a real string
+ if( !string[0] )
+ {
+ return( false );
+ }
+
+ // intialize the file name line number
+ strcpy( mFileName, fileName );
+ mLineNumber = 1;
+
+ // setup the string ptr
+ char* stringPtr = const_cast<char*>(string);
+
+ // process the string until it's finished
+ int readEndBracketLevel = 0;
+ bool negateCondition = false;
+
+ do
+ {
+
+#if !PROFILE_PARSER
+// Sound::daSoundManagerService( );
+#endif
+
+ // first, get the function name
+ char* token = this->GetNextToken( &stringPtr, mArgv[0] );
+
+ // if no function name found, we're finished
+ if( !token )
+ {
+ if( readEndBracketLevel > 0 )
+ {
+ this->Printf( "ConERR file %s line %d - expecting '}'.", mFileName, mLineNumber );
+ rTuneAssert( 0 );
+ return( false );
+ }
+ break;
+ }
+
+ // it's possible we might read an end bracket - skip past it...
+ while( !smStricmp(token, "}") )
+ {
+ if( readEndBracketLevel <= 0 )
+ {
+ this->Printf( "ConERR file %s line %d - unexpected '}' found.", mFileName, mLineNumber );
+ rTuneAssert( 0 );
+ return( false );
+ }
+
+ // otherwise, set the bool, and read the next token
+ readEndBracketLevel--;
+ token = GetNextToken( &stringPtr, mArgv[0] );
+
+ // if no function name found, we're finished
+ if( !token )
+ {
+ if( readEndBracketLevel > 0 )
+ {
+ this->Printf("ConERR file %s line %d - expecting '}'.", mFileName, mLineNumber);
+ rTuneAssert( 0 );
+ return( false );
+ }
+ else
+ {
+ return( true );
+ }
+
+ }
+ }
+ mArgc = 1;
+
+ // see if we read a "!" symbol before a conditional function call...
+ if( !strcmp( token, "!") )
+ {
+ // set the bool
+ negateCondition = true;
+
+ //read the next token (should be function name)
+ token = GetNextToken(&stringPtr, mArgv[0]);
+
+ if (! token)
+ {
+ Printf("ConERR file %s line %d - unexpected '!'.", mFileName, mLineNumber);
+ rTuneAssert( 0 );
+ return (false);
+ }
+ }
+
+ //mArgv[0] currently contains the function name
+ //next token should be an '('
+ if (! ReadToken(&stringPtr, "("))
+ {
+ Printf("ConERR file %s line %d - unable to process function %s. Expecting '('", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+ return (false);
+ }
+
+ //some bools to ensure proper syntax
+ bool canReadBrace = true;
+ bool mustReadComma = false;
+ do
+ {
+ //make sure we haven't run out of arguments
+ if (mArgc >= MAX_ARGS)
+ {
+ Printf("ConERR file %s line %d - unable to process function %s. Too many args.", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+ return (false);
+ }
+
+ //get the next argument
+ token = GetNextToken(&stringPtr, mArgv[mArgc]);
+ if (! token)
+ {
+ Printf("ConERR file %s line %d - unable to process function %s. Expecting ')'", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+ return (false);
+ }
+
+ //if we've read a ')', the next token should be a ';' and we've found our function
+ if (! smStricmp(token, ")"))
+ {
+ //make sure we can read a ')'
+ if (! canReadBrace)
+ {
+ Printf("ConERR file %s line %d - unable to process function %s. Extra ',' found.", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+ return (false);
+ }
+
+ //our function and it's arguments is complete
+ break;
+ }
+
+ //else the next token is an argument or a comma
+ else
+ {
+ if (mustReadComma)
+ {
+ if (smStricmp(token, ","))
+ {
+ Printf("ConERR file %s line %d - unable to process function %s. Args must be separated by a ','", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+ return (false);
+ }
+
+ //else we read our comma in correctly
+ else
+ {
+ mustReadComma = false;
+ canReadBrace = false;
+ }
+ }
+
+ //otherwise the next token is a valid argument
+ else
+ {
+ mArgc++;
+ mustReadComma = true;
+ canReadBrace = true;
+ }
+ }
+
+ } while (true);
+
+ //at this point, the array mArgv should contain the function name and all the arg strings
+ //search the table looking for the matching function
+ //first, search the alias table...
+ int i;
+ for (i = 0; i < mAliasCount; i++)
+ {
+ if (! smStricmp(mAliasTable[i]->name, mArgv[0]))
+ {
+ //copy the original set of args
+
+ int mPrevArgc = mArgc;
+ int j;
+ for( j = 0; j < mPrevArgc; ++j )
+ {
+ strcpy( mPrevArgv[j], mArgv[j] );
+ }
+
+
+ //replace the function name
+ AliasTableEntry *alias = mAliasTable[i];
+ strcpy(mArgv[0], alias->functionName);
+
+ //do the argument substitution
+ bool argcSet = false;
+ for (j = 0; j < alias->argc; j++)
+ {
+ //see if the variable is a substitution (%1, %2, etc...)
+ if (alias->argv[j][0] == '%')
+ {
+ //copy the given argment in
+ int index = alias->argv[j][1] - '0';
+
+ if( index < mPrevArgc )
+ {
+ strcpy( mArgv[j + 1], mPrevArgv[index] );
+ }
+ //once we get to an argument which has not been provided
+ //the substitutions are complete...
+ else
+ {
+ //set the argc
+ mArgc = j + 1;
+ argcSet = true;
+ break;
+ }
+ }
+
+ //else use the alias's argument
+ else
+ {
+ strcpy(mArgv[j + 1], alias->argv[j]);
+ }
+ }
+
+ //if we used the entire set of args from the alias, set the argc count
+ if (! argcSet)
+ mArgc = alias->argc + 1;
+
+ break;
+ }
+ }
+
+ //now the mArgc and mArgv vars are set, and any alias substitutions have been done
+ //search for the function
+ bool isConditional = false;
+ bool conditionalResult = false;
+ bool found = false;
+
+ std::map<radKey32, int>::const_iterator lookup;
+ lookup = mFunctionLookup.find(radMakeCaseInsensitiveKey32(mArgv[0]));
+
+ {
+ if( lookup != mFunctionLookup.end() )
+ {
+ i = lookup->second;
+ rTuneAssert( mFunctionTable[i]->name == mArgv[0] ); // make sure there wasn't a hash collision
+
+ //call the function
+ found = true;
+
+ //now make sure the argument count is correct
+ if ((mArgc - 1) >= mFunctionTable[i]->minArgs && (mArgc - 1) <= mFunctionTable[i]->maxArgs)
+ {
+ //conditional function call
+ if (mFunctionTable[i]->isConditional)
+ {
+ //make sure we read the opening brace '{'
+ if (! ReadToken(&stringPtr, "{"))
+ {
+ Printf("ConERR file %s line %d - unable to process function %s. Expecting '{'", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+ return (false);
+ }
+
+ isConditional = true;
+ readEndBracketLevel++;
+ conditionalResult = (mFunctionTable[i]->functionPointer.boolReturn)(mArgc, (char**)mArgv);
+ if (negateCondition)
+ conditionalResult = (! conditionalResult);
+ negateCondition = false;
+ }
+ //non conditional function call
+ else
+ {
+ //make sure we're not trying to negate a non-conditional function
+ if (negateCondition)
+ {
+ Printf("ConERR file %s line %d - trying to negate non-conditional function %s.", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+ return (false);
+ }
+ //make sure the function call ends with a ';'
+ if (! ReadToken(&stringPtr, ";"))
+ {
+ Printf("ConERR file %s line %d - unable to process function %s. Expecting ';'", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+ return (false);
+ }
+
+ isConditional = false;
+ negateCondition = false;
+
+ (mFunctionTable[i]->functionPointer.voidReturn)(mArgc, (char**)mArgv);
+
+ //see if the function was "exit"
+ if (! smStricmp(mArgv[0], "exit"))
+ {
+ //print out any exit messages
+ if (mArgc >= 2)
+ GetConsole()->Printf(mArgv[1]);
+
+ //terminate evaluating...
+ return (true);
+ }
+ }
+ }
+
+ //else print out the help string, but continue processing the string
+ else
+ {
+ Printf("ConERR file %s line %d - invalid arg list for function: %s()", mFileName, mLineNumber, mArgv[0]);
+ //Printf(mFunctionTable[i]->help);
+
+ rTuneAssert( 0 );
+
+ //even though the function couldn't be called, still need to read in
+ //either the ';' or the '{ ... }'
+ if (mFunctionTable[i]->isConditional)
+ {
+ //make sure we read the opening brace '{'
+ if (! ReadToken(&stringPtr, "{"))
+ {
+ Printf("ConERR file %s line %d - unable to process function %s. Expecting '{'", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+ return (false);
+ }
+
+ int balanceBrackets = readEndBracketLevel;
+ readEndBracketLevel++;
+
+ //skip past everything until we find the closing bracket
+ while (readEndBracketLevel > balanceBrackets)
+ {
+ char *token = GetNextToken(&stringPtr, mArgv[0]);
+ if (! token)
+ {
+ Printf("ConERR file %s line %d - end bracket for function %s not found.", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+ return (false);
+ }
+
+ //see if we read another '{'
+ else if (! smStricmp(token, "{"))
+ readEndBracketLevel++;
+
+ else if (! smStricmp(token, "}"))
+ readEndBracketLevel--;
+ }
+ }
+ else
+ {
+ //make sure the function call ends with a ';'
+ if (! ReadToken(&stringPtr, ";"))
+ {
+ Printf("ConERR file %s line %d - unable to process function %s. Expecting ';'", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+ return (false);
+ }
+ }
+ }
+ }
+ }
+
+ //if we didn't find the function, print an error msg, but go on to the next...
+ if (! found)
+ {
+ Printf("ConERR file %s line %d - console function not found: %s", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+
+ //since the function couldn't be found, don't forget to skip past the ';'
+ if (! ReadToken(&stringPtr, ";"))
+ {
+ Printf("ConERR file %s line %d - unable to process function %s. Expecting ';'", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+ return (false);
+ }
+ }
+
+ //if the function is conditional, and the result is false
+ //skip past until we find the closing brace...
+ if (isConditional && ! conditionalResult)
+ {
+ //skip past everything until we find the closing bracket
+ while (true)
+ {
+ char *token = GetNextToken(&stringPtr, mArgv[0]);
+ if (! token)
+ {
+ Printf("ConERR file %s line %d - end bracket for function %s not found.", mFileName, mLineNumber, mArgv[0]);
+ rTuneAssert( 0 );
+ return (false);
+ }
+
+ else if (! smStricmp(token, "}"))
+ {
+ readEndBracketLevel--;
+ break;
+ }
+ }
+ }
+
+ } while (true);
+
+#if PROFILE_PARSER
+GetConsole()->Printf("DEBUG total time spent calling functions: %.3f", totalTime);
+#endif
+
+ return (true);
+}
+
+
+
+//==============================================================================
+// Console::ExecuteScriptSync
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::ExecuteScriptSync( const char* filename, bool reload )
+{
+#if PROFILE_PARSER
+float curTime = GameManager::GetRealTime();
+GetConsole()->Printf("Console loading script file %s\n", filename);
+#endif
+
+ GetLoadingManager()->LoadSync( FILEHANDLER_CONSOLE, filename );
+}
+
+
+//==============================================================================
+//
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+bool Console::atob( const char* value )
+{
+ if( atoi(value) != 0 || !smStricmp(value, "true") )
+ {
+ return( true );
+ }
+ else
+ {
+ return( false );
+ }
+}
+
+
+//==============================================================================
+// Console::ExecuteScript
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::ExecuteScript
+(
+ const char* filename,
+ Console::ExecuteScriptCallback* pCallback,
+ void* pUserData,
+ bool reload
+)
+{
+ rTuneAssert( filename != 0 );
+ rTuneAssert( pCallback != 0 );
+
+ mpCallback = pCallback;
+ GetLoadingManager()->AddRequest( FILEHANDLER_CONSOLE, filename, HeapMgr()->GetCurrentHeap(), this, pUserData );
+}
+
+//==============================================================================
+// Console::OnProcessRequestsComplete
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Console::OnProcessRequestsComplete( void* pUserData )
+{
+ mpCallback->OnExecuteScriptComplete( pUserData );
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// FunctionTableEntry::FunctionTableEntry
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+Console::FunctionTableEntry::FunctionTableEntry
+(
+ const char* in_name,
+ CONSOLE_FUNCTION in_func,
+ const char* in_help,
+ int in_minArgs,
+ int in_maxArgs
+)
+ :
+ minArgs( in_minArgs ),
+ maxArgs( in_maxArgs ),
+ isConditional( false )
+{
+ rTuneAssert( in_func != 0 );
+ rTuneAssert( strlen( in_name ) < MAX_FUNCTION_NAME );
+ rTuneWarningMsg( strlen( in_help ) < MAX_HELP_LENGTH, "Help text too long. Text has been truncated.\n" );
+
+ functionPointer.voidReturn = in_func;
+ name = in_name;
+ help = in_help;
+};
+
+
+//==============================================================================
+// FunctionTableEntry::FunctionTableEntry
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters:
+//
+// Return: N/A.
+//
+//==============================================================================
+Console::FunctionTableEntry::FunctionTableEntry
+(
+ const char* in_name,
+ CONSOLE_BOOL_FUNCTION in_func,
+ const char* in_help,
+ int in_minArgs,
+ int in_maxArgs
+)
+ :
+ minArgs( in_minArgs ),
+ maxArgs( in_maxArgs ),
+ isConditional( true )
+{
+ rTuneAssert( in_func != 0 );
+ rTuneAssert( strlen( in_name ) < MAX_FUNCTION_NAME );
+ rTuneAssert( strlen( in_help ) < MAX_HELP_LENGTH );
+
+ functionPointer.boolReturn = in_func;
+ name = in_name;
+ help = in_help;
+}
+
+
+//==============================================================================
+// AliasTableEntry::AliasTableEntry
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters:
+//
+// Return: N/A.
+//
+//==============================================================================
+Console::AliasTableEntry::AliasTableEntry
+(
+ const char* srcName,
+ const char* funcName,
+ int srcArgc,
+ char** srcArgv
+)
+{
+ rTuneAssert( strlen( srcName ) < MAX_ALIAS_NAME );
+ rTuneAssert( strlen( funcName ) < MAX_FUNCTION_NAME );
+ rTuneAssert( srcArgc < MAX_ARGS );
+
+ strcpy( name, srcName );
+ strcpy( functionName, funcName );
+
+ argc = srcArgc;
+
+ int i;
+ for( i = 0; i < argc; ++i )
+ {
+ rTuneAssert( strlen( srcArgv[i]) < MAX_ARG_LENGTH );
+ strcpy(argv[i], srcArgv[i]);
+ }
+}
+
+
diff --git a/game/code/console/console.h b/game/code/console/console.h
new file mode 100644
index 0000000..f548cb9
--- /dev/null
+++ b/game/code/console/console.h
@@ -0,0 +1,291 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: console.h
+//
+// Description:
+//
+// History: 05/06/2002 + Created. Thanks to Mirth. -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef CONSOLE_H
+#define CONSOLE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <console/nameinsensitive.h>
+#include <loading/loadingmanager.h>
+#include <memory/srrmemory.h>
+#include <memory/memorypool.h>
+// Radcore
+#include <raddebug.hpp>
+
+#ifdef RAD_DEBUG
+#include <raddebugconsole.hpp>
+#endif
+
+#include <p3d/entity.hpp>
+
+#include <map>
+
+//========================================
+// Forward References
+//========================================
+struct IRadFile;
+
+#ifdef RAD_DEBUG
+struct IRadDebugConsole;
+class DebugConsoleCallback;
+#endif
+
+//=============================================================================
+//
+// Synopsis:
+//
+//=============================================================================
+
+class Console : public LoadingManager::ProcessRequestsCallback
+{
+ public:
+
+ //
+ // Static Methods for accessing this singleton.
+ //
+ static Console* CreateInstance();
+ static Console* GetInstance();
+ static void DestroyInstance();
+
+ //----------------------------------------------------------------------
+ // Register functions with the console.
+ //
+ // Functions must be of format:
+ //
+ // void Foo( int argc, char** argv );
+ //
+ // or
+ //
+ // bool Foo( int argc, char** argv );
+ //
+ typedef void (*CONSOLE_FUNCTION)(int argc, char** argv);
+ bool AddFunction( const char* name,
+ CONSOLE_FUNCTION funcPtr,
+ const char* helpString,
+ int minArgs,
+ int maxArgs );
+
+ typedef bool (*CONSOLE_BOOL_FUNCTION)(int argc, char** argv);
+ bool AddFunction( const char* name,
+ CONSOLE_BOOL_FUNCTION funcPtr,
+ const char* helpString,
+ int minArgs,
+ int maxArgs );
+
+ //
+ // Register an alias to a function.
+ //
+ bool AddAlias( const char* name, const char* functionName, int argc, char** argv );
+
+
+ //
+ // Async load and execute script file.
+ //
+ struct ExecuteScriptCallback
+ {
+ virtual void OnExecuteScriptComplete( void* pUserData ) = 0;
+ };
+
+ void ExecuteScript( const char* filename,
+ Console::ExecuteScriptCallback* pCallback,
+ void* pUserData = 0,
+ bool reload = false );
+
+ //
+ // Sync load and execute script file.
+ //
+ void ExecuteScriptSync( const char* filename, bool reload = false );
+
+
+ //
+ // Evaluate a string buffer.
+ //
+ bool Evaluate( const char* string, const char* inputSourceName );
+
+
+ void Printf( char* fmt, ... );
+ void Printf( int channel, char* fmt, ... );
+ void Errorf( char* fmt, ... );
+
+ void AssertFatal( char* fmt, ... );
+ void AssertWarn( char* fmt, ... );
+
+ const tNameInsensitive& TabCompleteFunction( const char* tabString, const char* curFunction );
+
+ void ListConsoleFunctions();
+
+ void ConsoleListenChannel( int channel );
+ void ConsoleBlockChannel( int channel );
+
+ enum
+ {
+ LOGMODE_OFF,
+ LOGMODE_ON,
+ LOGMODE_APPEND
+ };
+ bool SetConsoleLogMode( int mode );
+ void FlushConsoleLogFile();
+
+ //utility functions
+ bool atob(const char* value);
+
+ //
+ // LoadingManager::ProcessRequestsCallback
+ //
+ void OnProcessRequestsComplete( void* pUserData );
+
+ enum
+ {
+ MAX_STRING_LENGTH = 256,
+ MAX_FUNCTIONS = 256,
+ MAX_FUNCTION_NAME = 32,
+ MAX_HELP_LENGTH = 256,
+ MAX_ALIAS_NAME = 32,
+ MAX_ARGS = 16,
+ MAX_ARG_LENGTH = 64,
+ MAX_ALIASES = 32,
+ MAX_BUFFERS = 16
+ };
+
+#ifdef RAD_DEBUG
+ public:
+ void AddDebugConsoleLine(const char* text, bool warning = false);
+ void SetDebugConsoleEntry(const char* text, int cursorPosition);
+ private:
+ int mDebugConsoleLine;
+ IRadDebugConsole* mDebugConsole;
+ DebugConsoleCallback* mDebugConsoleCallback;
+#endif
+
+ private:
+ Console();
+ ~Console();
+
+ // Prevent wasteful constructor creation.
+ Console( const Console& );
+ Console& operator=( const Console& );
+
+ static bool Initialize();
+
+ struct FunctionTableEntry
+ {
+ union
+ {
+ CONSOLE_FUNCTION voidReturn;
+ CONSOLE_BOOL_FUNCTION boolReturn;
+ } functionPointer;
+
+ //char name[MAX_FUNCTION_NAME];
+ tNameInsensitive name;
+ tName help;
+ //char help[MAX_HELP_LENGTH];
+ int minArgs;
+ int maxArgs;
+ bool isConditional;
+
+ static FBMemoryPool sMemoryPool;
+
+ FunctionTableEntry( const char* in_name, CONSOLE_FUNCTION in_func,
+ const char* in_help, int in_minArgs, int in_maxArgs );
+
+ FunctionTableEntry( const char* in_name, CONSOLE_BOOL_FUNCTION in_func,
+ const char* in_help, int in_minArgs, int in_maxArgs );
+
+ static void* operator new( size_t size ) { return( sMemoryPool.Allocate( size ) ); }
+ static void operator delete( void* deadObject, size_t size ) { sMemoryPool.Free( deadObject, size ); }
+
+ // Declared but not defined to prevent access.
+ static void* operator new[]( size_t size );
+ static void operator delete[]( void* pMemory );
+ };
+
+ struct AliasTableEntry
+ {
+ char name[MAX_ALIAS_NAME];
+ char functionName[MAX_FUNCTION_NAME];
+ int argc;
+ char argv[MAX_ARGS][MAX_ARG_LENGTH];
+
+ static FBMemoryPool sMemoryPool;
+
+ AliasTableEntry( const char* srcName, const char* funcName,
+ int srcArgc, char** srcArgv );
+
+ static void* operator new( size_t size ) { return( sMemoryPool.Allocate( size ) ); }
+ static void operator delete( void* deadObject, size_t size ) { sMemoryPool.Free( deadObject, size ); }
+
+ // Declared but not defined to prevent access.
+ static void* operator new[]( size_t size );
+ static void operator delete[]( void* pMemory );
+ };
+
+ // table of functions added by other modules through AddFunction()
+ FunctionTableEntry* mFunctionTable[MAX_FUNCTIONS];
+ int mFunctionCount;
+
+ // nv: to speed up the function name lookup
+ std::map<radKey32, int> mFunctionLookup;
+
+ AliasTableEntry* mAliasTable[MAX_ALIASES];
+ int mAliasCount;
+
+ // argv table for processing the function arguments
+ int mArgc;
+ char** mArgv;
+
+ int mPrevArgc;
+ char** mPrevArgv;
+
+ //line number for reporting console errors
+ int mLineNumber;
+ char mFileName[256];
+
+ //vars for echoing to a log file
+ int mLogMode;
+ IRadFile* mLogFile;
+
+ //internal functions for parsing the string
+ char* SkipWhiteSpace( const char* string );
+ bool FoundComment( char** stringPtr );
+ char* FindTokenEnd( const char* string );
+ char* GetNextToken( char** stringPtr, char* tokenBuffer );
+ bool ReadToken( char** stringPtr, const char* requiredToken );
+
+ bool AddFunc( const char* name,
+ CONSOLE_FUNCTION funcPtr,
+ CONSOLE_BOOL_FUNCTION boolFuncPtr,
+ const char* helpString,
+ int minArgs,
+ int maxArgs );
+ bool AddFunctionAlias(const char* name, const char* functionName, int argc, char** argv);
+ const tNameInsensitive& TabComplete(const char* tabString, const char* curFunction);
+ void ListFunctions();
+
+ bool SetLogMode(int mode);
+ void LogMessage(int channel, char* msg, bool warning = false);
+ void FlushLogFile();
+
+ //setting up a channel system so you can eliminate console spam on your local system
+ int mChannelMask;
+ void ListenChannel(int channel);
+ void BlockChannel(int channel);
+
+ static Console* spInstance;
+ ExecuteScriptCallback* mpCallback;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline Console* GetConsole() { return( Console::GetInstance() ); }
+
+
+#endif // CONSOLE_H \ No newline at end of file
diff --git a/game/code/console/debugconsolecallback.cpp b/game/code/console/debugconsolecallback.cpp
new file mode 100644
index 0000000..77f014e
--- /dev/null
+++ b/game/code/console/debugconsolecallback.cpp
@@ -0,0 +1,380 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: debugconsolecallback.cpp
+//
+// Description:
+//
+// History: + Created. Thanks to Mirth for this. -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Radcore
+#include <raddebugconsole.hpp>
+// Pure3D
+#include <p3d/utility.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+#include <console/debugconsolecallback.h>
+#include <console/console.h>
+#include <console/fbstricmp.h>
+
+#include <string.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// DebugConsoleCallback::DebugConsoleCallback
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+DebugConsoleCallback::DebugConsoleCallback()
+ :
+ mFirstHistoryIndex( 0 ),
+ mLastHistoryIndex( 0 ),
+ mGetHistoryIndex( 0 ),
+ mCursorPosition( 0 )
+{
+ mConsoleHistory[0][0] = '\0';
+ mTabCompleteString = "";
+ mTabCurrentFunction = "";
+}
+
+
+//==============================================================================
+// DebugConsoleCallback::~DebugConsoleCallback
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+DebugConsoleCallback::~DebugConsoleCallback()
+{
+}
+
+
+//==============================================================================
+// DebugConsoleCallback::OnVKey
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugConsoleCallback::OnVKey
+(
+ int virtualKey,
+ bool ctrl,
+ bool shift,
+ bool alt
+)
+{
+#ifdef RAD_DEBUG
+ //set the bool so that OnChar() will not process the event as well...
+ mInputHandled = true;
+
+ //see if the ctrl key was pressed
+ if (ctrl)
+ {
+ switch (virtualKey)
+ {
+ //cut
+ case 'X':
+ p3d::printf("DEBUG cut not yet implemented.");
+ break;
+
+ //copy:
+ case 'C':
+ p3d::printf("DEBUG copy not yet implemented.");
+ break;
+
+ //paste:
+ case 'V':
+ p3d::printf("DEBUG paste not yet implemented.");
+ break;
+ }
+ }
+ else
+ {
+ switch (virtualKey)
+ {
+ case VK_RETURN:
+ GetConsole()->Printf("=>%s", mConsoleHistory[0]);
+ GetConsole()->Evaluate(mConsoleHistory[0], "CMDLine");
+
+ //only copy the current console history if we have a non-empty line
+ if (mConsoleHistory[0][0] != '\0')
+ {
+ //don't duplicate exact history entries...
+ if (mLastHistoryIndex == 0 || smStricmp(mConsoleHistory[0], mConsoleHistory[mLastHistoryIndex]))
+ {
+ //copy the current console history[0] into the next history buffer
+ mLastHistoryIndex++;
+ if (mLastHistoryIndex >= CONSOLE_HISTORY_SIZE)
+ mLastHistoryIndex = 1;
+ strcpy(mConsoleHistory[mLastHistoryIndex], mConsoleHistory[0]);
+
+ //update the "firstHistory" buffer index
+ if (mFirstHistoryIndex == 0)
+ mFirstHistoryIndex = 1;
+ else if (mLastHistoryIndex == mFirstHistoryIndex)
+ mFirstHistoryIndex++;
+ }
+ }
+
+ //reset the "getHistory" index
+ mGetHistoryIndex = 0;
+
+ //reset the current buffer
+ mConsoleHistory[0][0] = '\0';
+
+ //reset the cursor position
+ mCursorPosition = 0;
+
+ //reset the tab completion string
+ mTabCompleteString = "";
+
+ break;
+
+ case VK_BACK:
+ //delete from the current cursor position
+ if (mCursorPosition > 0)
+ {
+ //copy from the cursor position into a temp buffer
+ char temp[Console::MAX_STRING_LENGTH];
+ strcpy(temp, &mConsoleHistory[0][mCursorPosition]);
+
+ //decriment the cursor position, and copy the string back
+ mCursorPosition--;
+ strcpy(&mConsoleHistory[0][mCursorPosition], temp);
+
+ //reset the tab completion string
+ mTabCompleteString = "";
+ }
+
+ break;
+
+ case VK_DELETE:
+ //delete the character at the cursor position
+ if( mCursorPosition < static_cast<int>(strlen(mConsoleHistory[0])) )
+ {
+ //copy from the cursor position + 1 into a temp buffer
+ char temp[Console::MAX_STRING_LENGTH];
+ strcpy(temp, &mConsoleHistory[0][mCursorPosition + 1]);
+
+ //and copy the string back to the cursor position
+ strcpy(&mConsoleHistory[0][mCursorPosition], temp);
+
+ //reset the tab completion string
+ mTabCompleteString = "";
+ }
+ break;
+
+ case VK_UP:
+ //retrieve the previous available history
+ if (mGetHistoryIndex == 0)
+ mGetHistoryIndex = mLastHistoryIndex;
+ else
+ {
+ //see if we've run out of history
+ if (mGetHistoryIndex == mFirstHistoryIndex)
+ break;
+
+ mGetHistoryIndex--;
+ if (mGetHistoryIndex <= 0)
+ mGetHistoryIndex = CONSOLE_HISTORY_SIZE - 1;
+ }
+
+ //now copy the history into the "current" entry buffer
+ strcpy(mConsoleHistory[0], mConsoleHistory[mGetHistoryIndex]);
+
+ //set the new cursor position
+ mCursorPosition = strlen(mConsoleHistory[0]);
+
+ //reset the tab completion string
+ mTabCompleteString = "";
+
+ break;
+
+ case VK_DOWN:
+ //retrieve the next available history
+ if (mGetHistoryIndex == 0 || mGetHistoryIndex == mLastHistoryIndex)
+ break;
+
+ mGetHistoryIndex++;
+ if (mGetHistoryIndex >= CONSOLE_HISTORY_SIZE)
+ mGetHistoryIndex = 1;
+
+ //now copy the history into the "current" entry buffer
+ strcpy(mConsoleHistory[0], mConsoleHistory[mGetHistoryIndex]);
+
+ //set the new cursor position
+ mCursorPosition = strlen(mConsoleHistory[0]);
+
+ //reset the tab completion string
+ mTabCompleteString = "";
+
+ break;
+
+ case VK_LEFT:
+ if (mCursorPosition > 0)
+ mCursorPosition--;
+ break;
+
+ case VK_RIGHT:
+ if (mCursorPosition < static_cast<int>(strlen(mConsoleHistory[0])) - 1)
+ mCursorPosition++;
+ break;
+
+ case VK_HOME:
+ mCursorPosition = 0;
+ break;
+
+ case VK_END:
+ mCursorPosition = static_cast<int>(strlen(mConsoleHistory[0]));
+ break;
+
+ case VK_ESCAPE:
+ //reset the "getHistory" index
+ mGetHistoryIndex = 0;
+
+ //reset the current buffer
+ mConsoleHistory[0][0] = '\0';
+
+ //reset the cursor position
+ mCursorPosition = 0;
+
+ //reset the tab completion string
+ mTabCompleteString = "";
+
+ break;
+
+ case VK_TAB:
+ {
+ //make sure we have *something* to tabcomplete
+ if (mConsoleHistory[0][0])
+ {
+ //initialize the tab search
+ if ( mTabCompleteString.GetUID() != static_cast< tUID >( 0 ) )
+ {
+ mTabCompleteString = mConsoleHistory[ 0 ];
+ mTabCurrentFunction = "";
+ }
+
+ const tNameInsensitive& tabCompleteFunction = GetConsole()->TabCompleteFunction(mTabCompleteString.GetText(), mTabCurrentFunction.GetText() );
+ if( tabCompleteFunction.GetUID() != static_cast< tUID >( 0 ) )
+ {
+ //copy the tabCompleteFunction
+ mTabCurrentFunction = tabCompleteFunction;
+
+ //create the new console entry string
+ char tabConsoleEntry[Console::MAX_STRING_LENGTH];
+ sprintf(tabConsoleEntry, "%s();", tabCompleteFunction.GetText() );
+ strcpy(mConsoleHistory[0], tabConsoleEntry);
+ mCursorPosition = strlen(mConsoleHistory[0]) - 2;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ //set the bool to allow OnChar() to handling this event
+ mInputHandled = false;
+ break;
+ }
+ }
+
+ //set the new debug console line
+ if (mInputHandled)
+ {
+ GetConsole()->SetDebugConsoleEntry(mConsoleHistory[0], mCursorPosition);
+ }
+#endif //RAD_DEBUG
+}
+
+
+//==============================================================================
+// DebugConsoleCallback::OnChar
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugConsoleCallback::OnChar(int asciiKey)
+{
+#ifdef RAD_DEBUG
+ //if the bool is set, OnVKey already handled the event
+ if (mInputHandled)
+ return;
+
+ //anything not handled by OnVKey will fall through and be handled here...
+ int length = strlen(mConsoleHistory[0]);
+ if (asciiKey >= 0x20 && length < Console::MAX_STRING_LENGTH - 1)
+ {
+ //add the character to the console entry at the cursor position
+ char temp[Console::MAX_STRING_LENGTH];
+ strcpy(temp, &mConsoleHistory[0][mCursorPosition]);
+
+ //insert the character and increment the cursor position
+ mConsoleHistory[0][mCursorPosition++] = asciiKey;
+ strcpy(&mConsoleHistory[0][mCursorPosition], temp);
+
+ //reset the tab completion string
+ mTabCompleteString = "";
+
+ //set the new debug console line
+ GetConsole()->SetDebugConsoleEntry(mConsoleHistory[0], mCursorPosition);
+ }
+#endif //RAD_DEBUG
+}
+
+
+void DebugConsoleCallback::OnButtonClick( int xTextPixels, int yTextPixels, int xScreenPixels, int yScreenPixels, bool ctrl, bool shift, bool alt )
+{
+}
+void DebugConsoleCallback::OnButtonDown( int xTextPixels, int yTextPixels, int xScreenPixels, int yScreenPixels, bool ctrl, bool shift, bool alt )
+{
+}
+void DebugConsoleCallback::OnButtonUp( int xTextPixels, int yTextPixels, int xScreenPixels, int yScreenPixels, bool ctrl, bool shift, bool alt )
+{
+}
+void DebugConsoleCallback::OnButtonMove( int xTextPixels, int yTextPixels, int xScreenPixels, int yScreenPixels, bool ctrl, bool shift, bool alt, bool bottondown )
+{
+}
diff --git a/game/code/console/debugconsolecallback.h b/game/code/console/debugconsolecallback.h
new file mode 100644
index 0000000..3dd3370
--- /dev/null
+++ b/game/code/console/debugconsolecallback.h
@@ -0,0 +1,74 @@
+//==============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: debugconsolecallback.h
+//
+// Description:
+//
+// History: 05/06/2002 + Created. Thanks to Mirth for this. -- Darwin Chau
+//
+//==============================================================================
+
+#ifndef DEBUG_CONSOLE_CALLBACK_H
+#define DEBUG_CONSOLE_CALLBACK_H
+
+//========================================
+// Nested Includes
+//========================================
+// Radcore
+#include <raddebugconsole.hpp>
+#include <console/console.h>
+
+//========================================
+// Forward References
+//========================================
+
+//==============================================================================
+//
+// Synopsis: Implement the callbacks so our console integrates with the
+// remote input support from radcore.
+//
+//==============================================================================
+class DebugConsoleCallback : public IRadDebugConsoleKeyboardInputCallback,
+ public IRadDebugConsolePointerInputCallback
+{
+ public:
+
+ DebugConsoleCallback();
+ ~DebugConsoleCallback();
+
+ // Implement IRadDebugConsoleKeyboardInputCallback
+ void OnChar( int asciiKey );
+ void OnVKey( int virtualKey, bool ctrl, bool shift, bool alt );
+
+ // Implement IRadDebugConsolePointerInputCallback
+ void OnButtonClick( int xTextPixels, int yTextPixels, int xScreenPixels, int yScreenPixels, bool ctrl, bool shift, bool alt );
+ void OnButtonDown( int xTextPixels, int yTextPixels, int xScreenPixels, int yScreenPixels, bool ctrl, bool shift, bool alt );
+ void OnButtonUp( int xTextPixels, int yTextPixels, int xScreenPixels, int yScreenPixels, bool ctrl, bool shift, bool alt );
+ void OnButtonMove( int xTextPixels, int yTextPixels, int xScreenPixels, int yScreenPixels, bool ctrl, bool shift, bool alt, bool bottondown );
+
+ const char* GetConsoleEntry( int* cursorPosition )
+ {
+ *cursorPosition = mCursorPosition;
+ return( mConsoleHistory[0] );
+ }
+
+ private:
+
+ enum{ CONSOLE_HISTORY_SIZE = 64 };
+
+ char mConsoleHistory[CONSOLE_HISTORY_SIZE][Console::MAX_STRING_LENGTH];
+ int mFirstHistoryIndex;
+ int mLastHistoryIndex;
+ int mGetHistoryIndex;
+ int mCursorPosition;
+
+ // Used to let events not handled by OnVKey() be passed to OnChar()
+ bool mInputHandled;
+
+ // Tab completion vars
+ tNameInsensitive mTabCompleteString;
+ tNameInsensitive mTabCurrentFunction ;
+};
+
+#endif // DEBUG_CONSOLE_CALLBACK_H \ No newline at end of file
diff --git a/game/code/console/fbstricmp.cpp b/game/code/console/fbstricmp.cpp
new file mode 100644
index 0000000..17256ac
--- /dev/null
+++ b/game/code/console/fbstricmp.cpp
@@ -0,0 +1,124 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: string subsystem
+//
+// Description: This file contains the implementation of the class UnicodeString
+//
+// Authors: Ian Gipson
+//
+// Revisions Date Author Revision
+// 2000/10/23 IJG Created
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <console/fbstricmp.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// stricmp
+//===========================================================================
+// Description: stricmp - case independant string comparison
+//
+// Constraints: none
+//
+// Parameters: string1, string2 - strings to be compared
+//
+// Return: int - <0 string1 < string2
+// =0 string1 = string2
+// >0 string1 > string2
+//
+//===========================================================================
+int smStricmp( const char* string1, const char* string2 )
+{
+ //IMPROVE: if this is slow, then it can be swapped for the built in stricmp
+ //on the ps2, pc
+ const char* index1 = string1;
+ const char* index2 = string2;
+
+ while( ( *index1 != 0x00 ) && ( *index2 != 0x00 ) )
+ {
+ char c1 = toupper( *index1 );
+ char c2 = toupper( *index2 );
+ if( c1 > c2 )
+ {
+ return 1;
+ }
+ if( c1 < c2 )
+ {
+ return -1;
+ }
+ index1++;
+ index2++;
+ }
+ if ((!*index1) && *index2)
+ return -1;
+ else if (*index1 && !*index2)
+ return 1;
+ else
+ return 0;
+}
+
+//===========================================================================
+// rstrincmp
+//===========================================================================
+// Description: stricmp - case independant string comparison with a character
+// limit.
+//
+// Constraints: none
+//
+// Parameters: string1, string2 - strings to be compared
+// tCount - how many characters to compare
+//
+// Return: int - <0 string1 < string2
+// =0 string1 = string2
+// >0 string1 > string2
+//
+//===========================================================================
+int smStrincmp( const char *string1, const char *string2, int tCount)
+{
+ //IMPROVE: if this is slow, then it can be swapped for the built in stricmp
+ //on the ps2, pc
+ const char* index1 = string1;
+ const char* index2 = string2;
+
+ while( ( *index1 != 0x00 ) && ( *index2 != 0x00 ) && ( tCount != 0 ) )
+ {
+ char c1 = toupper( *index1 );
+ char c2 = toupper( *index2 );
+ if( c1 > c2 )
+ {
+ return 1;
+ }
+ if( c1 < c2 )
+ {
+ return -1;
+ }
+ index1++;
+ index2++;
+ tCount--;
+ }
+
+ //only return 0 if string1 is completely contained within string2
+ if (tCount == 0 || *index1 == 0x00)
+ return 0;
+ else
+ return 1;
+};
diff --git a/game/code/console/fbstricmp.h b/game/code/console/fbstricmp.h
new file mode 100644
index 0000000..66672a8
--- /dev/null
+++ b/game/code/console/fbstricmp.h
@@ -0,0 +1,56 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: String Subsystem
+//
+// Description: Class definitions for a Unicode String UnicodeString
+//
+// Authors: Ian Gipson
+//
+// Revisions Date Author Revision
+// 2000/10/23 IJG Created
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef __SMSTRICMP_H
+#define __SMSTRICMP_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description: performs a case independant string comparison on any platform
+//
+// Constraints:
+//
+//===========================================================================
+int smStricmp( const char *string1, const char *string2 );
+
+//===========================================================================
+//
+// Description: performs a case independant string comparison on any platform
+// up to tCount characters
+//
+// Constraints:
+//
+//===========================================================================
+int smStrincmp( const char *string1, const char *string2, int tCount);
+
+
+#endif // End conditional inclusion
diff --git a/game/code/console/nameinsensitive.cpp b/game/code/console/nameinsensitive.cpp
new file mode 100644
index 0000000..12f0b26
--- /dev/null
+++ b/game/code/console/nameinsensitive.cpp
@@ -0,0 +1,145 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: string subsystem
+//
+// Description: This file contains the implementation of the class UnicodeString
+//
+// Authors: Ian Gipson
+//
+// Revisions Date Author Revision
+// 2000/10/23 IJG Created
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+#include "console/nameinsensitive.h"
+#include "console/upcase.h"
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// tNameInsensitive::tNameInsensitive
+//===========================================================================
+// Description: default constructor
+//
+// Constraints: none
+//
+// Parameters: none
+//
+// Return: none
+//
+//===========================================================================
+tNameInsensitive::tNameInsensitive():
+ Parent()
+{
+ //nothing
+}
+
+//===========================================================================
+// tNameInsensitive::tNameInsensitive
+//===========================================================================
+// Description: constructor from a const char*
+//
+// Constraints: none
+//
+// Parameters: name - const char* to the string to convert
+//
+// Return: none
+//
+//===========================================================================
+tNameInsensitive::tNameInsensitive( const char* name ):
+ Parent()
+{
+ SetText( name );
+}
+
+//===========================================================================
+// tNameInsensitive::tNameInsensitive
+//===========================================================================
+// Description: Copy constructor
+//
+// Constraints: none
+//
+// Parameters: name - tNameInsensitive to copy in
+//
+// Return: none
+//
+//===========================================================================
+tNameInsensitive::tNameInsensitive( const tNameInsensitive& name ):
+ Parent( name )
+{
+ //nothing
+}
+
+//===========================================================================
+// tNameInsensitive::tNameInsensitive
+//===========================================================================
+// Description: assignment from another name
+//
+// Constraints: none
+//
+// Parameters: other - char* to assign
+//
+// Return: none
+//
+//===========================================================================
+const tNameInsensitive& tNameInsensitive::operator=( const char* other )
+{
+ SetText( other );
+ return *this;
+}
+
+//===========================================================================
+// tNameInsensitive::tNameInsensitive
+//===========================================================================
+// Description: assignment from another name
+//
+// Constraints: none
+//
+// Parameters: other - tNameInsensitive to assign
+//
+// Return: none
+//
+//===========================================================================
+const tNameInsensitive& tNameInsensitive::operator=(const tNameInsensitive& other)
+{
+ Parent::operator=( other );
+ return *this;
+}
+
+//===========================================================================
+// tNameInsensitive::SetText
+//===========================================================================
+// Description: sets the text and the UID from a string
+//
+// Constraints: none
+//
+// Parameters: name - const char* to set this name to
+//
+// Return: none
+//
+//===========================================================================
+void tNameInsensitive::SetText( const char* name )
+{
+ char temp[ 1024 ];
+ #ifdef RAD_DEBUG
+ size_t length = strlen( name );
+ rAssert( length < 1024 );
+ #endif
+ strcpy( temp, name );
+ Upcase( temp );
+ Parent::SetText( name );
+}
diff --git a/game/code/console/nameinsensitive.h b/game/code/console/nameinsensitive.h
new file mode 100644
index 0000000..fb0c0d3
--- /dev/null
+++ b/game/code/console/nameinsensitive.h
@@ -0,0 +1,57 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: tNames with case insensitivity
+//
+// Description: hashed names that are case insensitive
+//
+// Authors: Ian Gipson
+//
+// Revisions Date Author Revision
+// 2003/06/02 IJG Created
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef __NAMEINSENSITIVE_H_
+#define __NAMEINSENSITIVE_H_
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <p3d/entity.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class tNameInsensitive : public tName
+{
+private:
+ typedef tName Parent;
+public:
+ // various constructors
+ tNameInsensitive(); // default constructor, both uid and text will be invalid
+ tNameInsensitive( const char* name ); // sets text name and uid
+ tNameInsensitive( const tNameInsensitive& ); // copy from another
+
+ const tNameInsensitive& operator=( const tNameInsensitive& other );
+ const tNameInsensitive& operator=( const char* other );
+ void SetText(const char* name);
+
+private:
+ const tNameInsensitive& operator=(const tName& other);
+ tNameInsensitive( const tName& ); // copy from another name - DO NOT PERMIT
+ void SetTextOnly(const char*); //set the text without changing the UID
+ // the real data
+};
+#endif //__NAMEINSENSITIVE_H_
diff --git a/game/code/console/upcase.cpp b/game/code/console/upcase.cpp
new file mode 100644
index 0000000..90aaafe
--- /dev/null
+++ b/game/code/console/upcase.cpp
@@ -0,0 +1,52 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: upcase function
+//
+// Description:
+//
+// Authors: Ian Gipson
+//
+// Revisions Date Author Revision
+// 2003/10/23 IJG Created
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+#include <console/upcase.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// Upcase
+//===========================================================================
+// Description: converts a string to upper case
+//
+// Constraints: none
+//
+// Parameters: string - string to convert to upper case
+//
+// Return: NONE
+//
+//===========================================================================
+void Upcase( char* string )
+{
+ size_t length = ::strlen( string );
+ size_t i;
+ for( i = 0; i < length; ++i )
+ {
+ string[ i ] = toupper( string[ i ] );
+ }
+} \ No newline at end of file
diff --git a/game/code/console/upcase.h b/game/code/console/upcase.h
new file mode 100644
index 0000000..79a22f9
--- /dev/null
+++ b/game/code/console/upcase.h
@@ -0,0 +1,38 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Upcase Function
+//
+// Description: converts a string to upper case
+//
+// Authors: Ian Gipson
+//
+// Revisions Date Author Revision
+// 2000/10/23 IJG Created
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef __UPCASE_H
+#define __UPCASE_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+void Upcase( char* string );
+
+#endif //__UPCASE_H
diff --git a/game/code/constants/actorenum.h b/game/code/constants/actorenum.h
new file mode 100644
index 0000000..09e5fd0
--- /dev/null
+++ b/game/code/constants/actorenum.h
@@ -0,0 +1,32 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: None, header only
+//
+// Description: Enumeration names actor states in the game
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef ACTORENUM_H
+#define ACTORENUM_H
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+namespace ActorEnum
+{
+ enum FlyingActorStates
+ {
+ eFadeIn = 0,
+ eUnaggressive = 1,
+ eTransitionToReadyToAttack = 2,
+ eIdleReadyToAttack = 3,
+ eAttacking = 4,
+ eDestroyed = 5
+ };
+}
+#endif \ No newline at end of file
diff --git a/game/code/constants/blobshadownames.h b/game/code/constants/blobshadownames.h
new file mode 100644
index 0000000..3eafdee
--- /dev/null
+++ b/game/code/constants/blobshadownames.h
@@ -0,0 +1,76 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: No component
+//
+// Description: string names of blobby shadows and the meshes they match up with
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef BLOBSHADOWNAMES_H
+#define BLOBSHADOWNAMES_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+namespace BlobbyShadowNames
+{
+ struct BlobbyShadows
+ {
+ const char* searchString;
+ const char* drawableName;
+ };
+
+ const BlobbyShadows shadowList[] =
+ {
+ { "treesmdead", "deadtree_shadowShape" },
+ { "treedead", "deadtree_shadowShape" },
+ { "treesm", "treeshadowsmall" },
+ { "treebig", "treeshadowbig" },
+ { "treeevermed", "treeshadoweversmallShape" },
+ { "treecypress", "treeshadowsmall" },
+ { "l5_streetlampold_Shape","l5_streetlamp_lightpoolShape" },
+ { "l5_oldstreetlamp_Shape","l5_streetlamp_lightpoolShape" }
+ };
+
+ // Retrieve the name of the tDrawable that
+ // is associated with the shadow identifier substring
+ // When an object is loaded, call FindShadowName to
+ // determine if a shadow needs to be placed under it
+ inline const char* FindShadowName( const char* objectName )
+ {
+ const int NUM_SHADOW_NAMES = sizeof( shadowList ) / sizeof( shadowList[0] );
+ const char* result = NULL;
+ for (int i = 0 ; i < NUM_SHADOW_NAMES ; i++)
+ {
+ if ( strstr( objectName, shadowList[i].searchString ) )
+ {
+ result = shadowList[i].drawableName;
+ break;
+ }
+
+ }
+ return result;
+ }
+
+};
+
+
+#endif \ No newline at end of file
diff --git a/game/code/constants/breakablesenum.h b/game/code/constants/breakablesenum.h
new file mode 100644
index 0000000..5a65f2c
--- /dev/null
+++ b/game/code/constants/breakablesenum.h
@@ -0,0 +1,56 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: None, header only
+//
+// Description: Enumeration names for all breakables in the game
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef BREAKABLESENUM_H
+#define BREAKABLESENUM_H
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+namespace BreakablesEnum
+{
+ enum BreakableID
+ {
+ eNull = -1, // No breakable. Assertion if this is attempted to play
+ eHydrantBreaking = 3,
+ eMailboxBreaking = 5 ,
+ eParkingMeterBreaking = 6,
+ eWoodenCratesBreaking = 7,
+ eTommacoPlantsBreaking = 8,
+ ePowerCouplingBreaking = 9,
+ ePineTreeBreaking = 14,
+ eOakTreeBreaking = 15,
+ eBigBarrierBreaking = 16,
+ eRailCrossBreaking = 17,
+ eSpaceNeedleBreaking = 18,
+ eKrustyGlassBreaking = 19,
+ eCypressTreeBreaking = 20,
+ eDeadTreeBreaking = 21,
+ eSkeletonBreaking = 22,
+ eWillow = 23,
+ eCarExplosion = 24,
+ eGlobeLight = 25,
+ eTreeMorn = 26,
+ ePalmTreeSmall = 27,
+ ePalmTreeLarge = 28,
+ eStopsign = 29,
+ ePumpkin = 30,
+ ePumpkinMed = 31,
+ ePumpkinSmall = 32,
+ eCasinoJump = 33,
+ eNumBreakables = 34
+ };
+
+ enum { eMaxBreakableNames = 10 };
+}
+#endif \ No newline at end of file
diff --git a/game/code/constants/characterenum.h b/game/code/constants/characterenum.h
new file mode 100644
index 0000000..51d1b29
--- /dev/null
+++ b/game/code/constants/characterenum.h
@@ -0,0 +1,24 @@
+#ifndef CHARACTER_ENUM
+#define CHARACTER_ENUM
+
+namespace CharacterEnum
+{
+
+ enum WalkerID
+ {
+ HOMER,
+ MARGE,
+ APU,
+ GRAMPA,
+ BART,
+ LISA,
+ RALPH,
+ MILHOUSE,
+
+ NUM_WALKERS,
+ INVALID
+ };
+}
+
+
+#endif //CHARACTER_ENUM \ No newline at end of file
diff --git a/game/code/constants/directionalarrowenum.h b/game/code/constants/directionalarrowenum.h
new file mode 100644
index 0000000..79b6e31
--- /dev/null
+++ b/game/code/constants/directionalarrowenum.h
@@ -0,0 +1,17 @@
+#ifndef DIRECTIONALARROWENUM_H
+#define DIRECTIONALARROWENUM_H
+
+namespace DirectionalArrowEnum
+{
+ enum TYPE
+ {
+ INTERSECTION = 0x01,
+ NEAREST_ROAD = 0x10,
+ BOTH = 0x11,
+ NEITHER = 0x100,
+
+ NUM_TYPES = 4
+ };
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/constants/maxnpccharacters.h b/game/code/constants/maxnpccharacters.h
new file mode 100644
index 0000000..8c02ad9
--- /dev/null
+++ b/game/code/constants/maxnpccharacters.h
@@ -0,0 +1,19 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: maxnpccharacters.h
+//
+// Description: Blahblahblah
+//
+// History: created Sept 6, 2002 - gmayer
+//
+//=============================================================================
+
+#ifndef MAXNPCCHARACTERS_H
+#define MAXNPCCHARACTERS_H
+
+
+const int MAX_NPC_CHARACTERS = 6;
+
+
+#endif //MAXNPCCHARACTERS_H
diff --git a/game/code/constants/maxplayers.h b/game/code/constants/maxplayers.h
new file mode 100644
index 0000000..0217e7f
--- /dev/null
+++ b/game/code/constants/maxplayers.h
@@ -0,0 +1,19 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: maxplayers.h
+//
+// Description: Blahblahblah
+//
+// History: 4/15/2002 + Created -- TBJ
+//
+//=============================================================================
+
+#ifndef MAX_PLAYERS_H
+#define MAX_PLAYERS_H
+
+
+const int MAX_PLAYERS = 4;
+
+
+#endif //MAX_PLAYERS_H
diff --git a/game/code/constants/movienames.h b/game/code/constants/movienames.h
new file mode 100644
index 0000000..31ddb93
--- /dev/null
+++ b/game/code/constants/movienames.h
@@ -0,0 +1,102 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: movieconsts.h
+//
+// Description: Blahblahblah
+//
+// History: 06/06/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef MOVIENAMES_H
+#define MOVIENAMES_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+namespace MovieNames
+{
+#ifdef RAD_XBOX
+
+ static char* DEMO = "D:\\movies\\demo.rmv";
+ static char* RADICALLOGO = "D:\\movies\\radlogo.rmv";
+ static char* FOXLOGO = "D:\\movies\\foxlogo.rmv";
+ static char* GRACIELOGO = "D:\\movies\\gracie.rmv";
+ static char* VUGLOGO = "D:\\movies\\vuglogo.rmv";
+ static char* INTROFMV = "D:\\movies\\intro.rmv";
+ static char* MOVIE1 = "D:\\movies\\fmv1a.rmv";
+ static char* MOVIE2 = "D:\\movies\\fmv2.rmv";
+ static char* MOVIE3 = "D:\\movies\\fmv3.rmv";
+ static char* MOVIE4 = "D:\\movies\\fmv4.rmv";
+ static char* MOVIE5 = "D:\\movies\\fmv5.rmv";
+ static char* MOVIE6 = "D:\\movies\\fmv6.rmv";
+ static char* MOVIE7 = "D:\\movies\\fmv7.rmv";
+ static char* MOVIE8 = "D:\\movies\\fmv8.rmv";
+
+#elif defined RAD_PS2
+
+ static char* DEMO = "movies\\demo.rmv";
+ static char* RADICALLOGO = "movies\\radlogo.rmv";
+ static char* FOXLOGO = "movies\\foxlogo.rmv";
+ static char* GRACIELOGO = "movies\\gracie.rmv";
+ static char* VUGLOGO = "movies\\vuglogo.rmv";
+ static char* INTROFMV = "movies\\intro.rmv";
+ static char* MOVIE1 = "movies\\fmv1a.rmv";
+ static char* MOVIE2 = "movies\\fmv2.rmv";
+ static char* MOVIE3 = "movies\\fmv3.rmv";
+ static char* MOVIE4 = "movies\\fmv4.rmv";
+ static char* MOVIE5 = "movies\\fmv5.rmv";
+ static char* MOVIE6 = "movies\\fmv6.rmv";
+ static char* MOVIE7 = "movies\\fmv7.rmv";
+ static char* MOVIE8 = "movies\\fmv8.rmv";
+
+#elif defined RAD_GAMECUBE
+
+ static char* DEMO = "movies/demo.rmv";
+ static char* RADICALLOGO = "movies/radlogo.rmv";
+ static char* FOXLOGO = "movies/foxlogo.rmv";
+ static char* GRACIELOGO = "movies/gracie.rmv";
+ static char* VUGLOGO = "movies/vuglogo.rmv";
+ static char* INTROFMV = "movies/intro.rmv";
+ static char* MOVIE1 = "movies/fmv1a.rmv";
+ static char* MOVIE2 = "movies/fmv2.rmv";
+ static char* MOVIE3 = "movies/fmv3.rmv";
+ static char* MOVIE4 = "movies/fmv4.rmv";
+ static char* MOVIE5 = "movies/fmv5.rmv";
+ static char* MOVIE6 = "movies/fmv6.rmv";
+ static char* MOVIE7 = "movies/fmv7.rmv";
+ static char* MOVIE8 = "movies/fmv8.rmv";
+
+#elif defined RAD_WIN32
+
+ //static char* DEMO = "movies\\demo.rmv";
+ static char* RADICALLOGO = "movies\\radlogo.rmv";
+ static char* FOXLOGO = "movies\\foxlogo.rmv";
+ static char* GRACIELOGO = "movies\\gracie.rmv";
+ static char* VUGLOGO = "movies\\vuglogo.rmv";
+ static char* INTROFMV = "movies\\intro.rmv";
+ static char* MOVIE1 = "movies\\fmv1a.rmv";
+ static char* MOVIE2 = "movies\\fmv2.rmv";
+ static char* MOVIE3 = "movies\\fmv3.rmv";
+ static char* MOVIE4 = "movies\\fmv4.rmv";
+ static char* MOVIE5 = "movies\\fmv5.rmv";
+ static char* MOVIE6 = "movies\\fmv6.rmv";
+ static char* MOVIE7 = "movies\\fmv7.rmv";
+ static char* MOVIE8 = "movies\\fmv8.rmv";
+
+#endif
+}
+
+#endif //MOVIENAMES_H
diff --git a/game/code/constants/particleenum.h b/game/code/constants/particleenum.h
new file mode 100644
index 0000000..4329b19
--- /dev/null
+++ b/game/code/constants/particleenum.h
@@ -0,0 +1,52 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: None, header only
+//
+// Description: Enumeration names for all particle systems in the game
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef PARTICLEENUM_H
+#define PARTICLEENUM_H
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+namespace ParticleEnum
+{
+ enum ParticleID
+ {
+ eNull = -1, // No Particle Effect. Assertion if this is attempted to play
+ eShrub = 3,
+ eGarbage = 4,
+ eOakTreeLeaves = 5,
+ eMail = 6,
+ ePineTreeNeedles = 7,
+ eStars = 8,
+ eSmokeSpray = 9,
+ eDirtSpray = 10, // 10
+ eGrassSpray = 11,
+ eWaterSpray = 12,
+ eEngineSmokeLight = 13,
+ eEngineSmokeHeavy = 14,
+ eEngineSmokeMedium = 16,
+ ePowerBoxExplosion = 17,
+ eFrinksCarSpecialEffect = 18,
+ eFireSpray = 19,
+ eAlienCameraExplosion = 20,
+ eHoverBikeFlame = 21,
+ eCoconutsDroppingShort = 22,
+ eCoconutsDroppingTall = 23,
+ eParkingMeter = 24,
+ eCarExplosion = 25,
+ ePopsicles = 26,
+ eNumParticleTypes = 27
+
+ };
+}
+#endif \ No newline at end of file
diff --git a/game/code/constants/physprop.h b/game/code/constants/physprop.h
new file mode 100644
index 0000000..3e3fe6b
--- /dev/null
+++ b/game/code/constants/physprop.h
@@ -0,0 +1,22 @@
+#ifndef PHYSPROPID_H
+#define PHYSPROPID_H
+
+// Enums for classtypeID
+
+enum enClasstypeID
+{
+ WTF,
+ GROUND,
+ PROP_STATIC,
+ PROP_MOVEABLE,
+ PROP_BREAKABLE,
+ ANIMATED_BV,
+ DRAWABLE,
+ STATIC,
+ PROP_DRAWABLE,
+ PROP_ANIM_BREAKABLE, //for the damn spider.
+ PROP_ONETIME_MOVEABLE
+};
+
+
+#endif
diff --git a/game/code/constants/srrchunks.h b/game/code/constants/srrchunks.h
new file mode 100644
index 0000000..4166ec1
--- /dev/null
+++ b/game/code/constants/srrchunks.h
@@ -0,0 +1,73 @@
+#ifndef SRR_CHUNKS_H
+#define SRR_CHUNKS_H
+
+//CHUNK IDs
+//0x03000000 - 0x03ffffff Simpsons
+
+namespace SRR2
+{
+ namespace ChunkID
+ {
+ //Track Editor / World Builder would like to reseve up to
+ //0x030000ff
+ const unsigned WALL = 0x03000000;
+ const unsigned FENCELINE = 0x03000001;
+ const unsigned ROAD_SEGMENT = 0x03000002;
+ const unsigned ROAD = 0x03000003;
+ const unsigned INTERSECTION = 0x03000004;
+
+ const unsigned LOCATOR = 0x03000005;
+ const unsigned TRIGGER_VOLUME = 0x03000006;
+ const unsigned SPLINE = 0x03000007;
+ const unsigned INSTANCES = 0x03000008;
+
+ const unsigned ROAD_SEGMENT_DATA = 0x03000009;
+
+ const unsigned RAIL = 0x0300000A;
+
+ const unsigned PED_PATH = 0x0300000B;
+ const unsigned EXTRA_MATRIX = 0x0300000C;
+ const unsigned PED_PATH_SEGMENT = 0x0300000D;
+ const unsigned TERRAIN_TYPE = 0x0300000E;
+
+ //Camera data ids
+ const unsigned FOLLOWCAM = 0x03000100;
+ const unsigned WALKERCAM = 0x03000101;
+
+ // SFX chunk id's.
+ const unsigned CHUNK_SET = 0x03000110;
+
+ //Next usable ID 0x03000120
+
+
+ //Object Attribute chunks for greg.
+ const unsigned OBJECT_ATTRIBUTES = 0x03000600;
+ const unsigned PHYS_WRAPPER = 0x03000601;
+ const unsigned ATTRIBUTE_TABLE = 0x03000602;
+
+ // Effects chunk id's
+ const unsigned BREAKABLE_OBJECT = 0x03001000;
+ const unsigned INST_PARTICLE_SYSTEM = 0x03001001;
+
+ //DSG chunk id's
+ const unsigned ENTITY_DSG = 0x03f00000;
+ const unsigned STATIC_PHYS_DSG = 0x03f00001;
+ const unsigned DYNA_PHYS_DSG = 0x03f00002;
+ const unsigned INTERSECT_DSG = 0x03f00003;
+ const unsigned TREE_DSG = 0x03f00004;
+ const unsigned CONTIGUOUS_BIN_NODE = 0x03f00005;
+ const unsigned SPATIAL_NODE = 0x03f00006;
+ const unsigned FENCE_DSG = 0x03f00007;
+ const unsigned ANIM_COLL_DSG = 0x03f00008;
+ const unsigned INSTA_ENTITY_DSG = 0x03f00009;
+ const unsigned INSTA_STATIC_PHYS_DSG = 0x03f0000A;
+ const unsigned WORLD_SPHERE_DSG = 0x03f0000B;
+ const unsigned ANIM_DSG = 0x03f0000C;
+ const unsigned LENS_FLARE_DSG = 0x03f0000D;
+ const unsigned INSTA_ANIM_DYNA_PHYS_DSG = 0x03f0000E;
+ const unsigned ANIM_DSG_WRAPPER = 0x03f0000F;
+ const unsigned ANIM_OBJ_DSG_WRAPPER = 0x03f00010;
+ }
+}
+
+#endif
diff --git a/game/code/constants/statepropenum.h b/game/code/constants/statepropenum.h
new file mode 100644
index 0000000..a8738af
--- /dev/null
+++ b/game/code/constants/statepropenum.h
@@ -0,0 +1,54 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: None, header only
+//
+// Description: A listing of all the events and callbacks in use by the Srr2
+// stateprop implementation
+//
+// "Stateprops are awesome, everything should be a stateprop. - Aryan"
+//
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef STATEPROPENUM_H
+#define STATEPROPENUM_H
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+namespace StatePropEnum
+{
+ enum Events
+ {
+
+ };
+
+ enum Callbacks
+ {
+ eStateChange = -1, // The only callback thats reserved for use by the stateprop system
+ eRemoveFromWorld = 0,
+ eSpawn5Coins = 1,
+ eRemoveCollisionVolume = 2,
+ eFireEnergyBolt = 3,
+ eKillSpeed = 4,
+ eSpawn10Coins = 5,
+ eSpawn15Coins = 6,
+ eSpawn20Coins = 7,
+ eRadiateForce = 8,
+ eEmitLeaves = 9,
+ eObjectDestroyed = 10,
+ eSpawn5CoinsZ = 11,
+ eSpawn1Coin = 12,
+ eColaDestroyed = 13,
+ eCamShake = 14,
+ eRemoveFirstCollisionVolume = 15,
+ eRemoveSecondCollisionVolume = 16,
+ eRemoveThirdCollisionVolume = 17
+ };
+}
+#endif \ No newline at end of file
diff --git a/game/code/constants/vehicleenum.h b/game/code/constants/vehicleenum.h
new file mode 100644
index 0000000..afa3680
--- /dev/null
+++ b/game/code/constants/vehicleenum.h
@@ -0,0 +1,176 @@
+#ifndef VEHICLE_ENUM
+#define VEHICLE_ENUM
+
+namespace VehicleEnum
+{
+ /*
+ enum VehicleID
+ {
+ FERRINI, //Bart
+ TRANS_AM, //Apu
+ BANDIT, //Snake
+ BARRACUDA, //Homer
+ PINK_SEDAN, //Homer
+ JEEP, //Grampa
+ PICKUP, //Cletus
+ POLICE, //Wiggum
+ CLOWN_CAR, //Krusty
+ CANYONARO, //Marge
+ OTTOBUS, //Otto
+ DUFF_TRUCK, //Moe
+ LIMO, //Smithers
+ MOTORHOME, //Flanders
+ HUMMER1, //McBain (regular)
+ ALIEN, //Kodos & Kang
+ BUGGY, //Zombie 1?
+ MUNSTERS, //Zombie 2?
+ HUMMER2, //McBain (Halloween)
+
+ NUM_VEHICLES,
+ INVALID
+ };
+ */
+
+ // new enum list with enums matching new names:
+ // ordered so that old numeric values will work
+
+ enum VehicleID
+ {
+ BART_V = 0, // bart_v 0
+ APU_V, // apu_v 1
+ SNAKE_V, // snake_v 2
+ HOMER_V, // homer_v 3
+ FAMIL_V, // famil_v 4
+ GRAMP_V, // gramp_v 5
+ CLETU_V, // cletu_v 6
+ WIGGU_V, // wiggu_v 7
+ KRUSTYKAR_NOTYETIN, //
+ MARGE_V, // marge_v 9
+ OTTOBUS_NOTYETIN,
+ MOESDUFFTRUCK_NOTYETIN,
+ SMITH_V, // smith_v 12
+ FLANDERSMOTORHOME_NOTYETIN,
+ MCBAINHUMMER_NOTYETIN,
+ ALIEN_NOTYETIN,
+ ZOMBI_V, // zombi_v 16
+ MUNSTERS_NOTYETIN,
+ HUMMER2_NOTYETIN,
+
+ CVAN, // cVan 19
+ COMPACTA, // compactA 20
+
+ COMIC_V, // comic_v 21
+ SKINN_V, // skinn_v 22
+
+
+ // some more new ai cars
+ CCOLA, // 23
+ CSEDAN, // 24
+ CPOLICE, // 25
+ CCELLA, // 26
+ CCELLB, // 27
+ CCELLC, // 28
+ CCELLD, // 29
+
+ // some more new traffic cars
+ MINIVANA, // 30
+ PICKUPA, // 31
+ TAXIA, // 32
+ SPORTSA, // 33
+ SPORTSB, // 34
+ SUVA, // 35
+ WAGONA, // 36
+
+ // some more new cars: nov 12, 2002
+ HBIKE_V, // 37
+ BURNS_V, // 38
+ HONOR_V, // 39
+
+ CARMOR, // 40
+ CCURATOR, // 41
+ CHEARS, // 42
+ CKLIMO, // 43
+ CLIMO, // 44
+ CNERD, // 45
+
+ FRINK_V, // 46
+
+ // some more new cars: dec 18, 2002
+ CMILK, // 47
+ CDONUT, // 48
+ BBMAN_V, // 49
+ BOOKB_V, // 50
+ CARHOM_V, // 51
+ ELECT_V, // 52
+ FONE_V, // 53
+ GRAMR_V, // 54
+ MOE_V, // 55
+ MRPLO_V, // 56
+ OTTO_V, // 57
+ PLOWK_V, // 58
+ SCORP_V, // 59
+ WILLI_V, // 60
+
+ // new traffic cars: Jan 11, 2002
+ SEDANA, // 61
+ SEDANB, // 62
+
+ // new Chase cars: Jan 11, 2002
+ CBLBART, // 63
+ CCUBE, // 64
+ CDUFF, // 65
+ CNONUP, // 66
+
+ // new Driver cars: Jan 11, 2002
+ LISA_V, // 67
+ KRUST_V, // 68
+
+ // new Traffic cars: Jan 13, 2002
+ COFFIN, // 69
+ HALLO, // 70
+ SHIP, // 71
+ WITCHCAR, // 72
+
+ // new Traffic husk: Feb 10, 2002
+ HUSKA, // 73
+
+ // new Driver cars: Feb 11, 2002
+ ATV_V, // 74
+ DUNE_V, // 75
+ HYPE_V, // 76
+ KNIGH_V, // 77
+ MONO_V, // 78
+ OBLIT_V, // 79
+ ROCKE_V, // 80
+
+ // new Traffic cars: Feb 24, 2002
+ AMBUL, // 81
+ BURNSARM, // 82
+ FISHTRUC, // 83
+ GARBAGE, // 84
+ ICECREAM, // 85
+ ISTRUCK, // 86
+ NUCTRUCK, // 87
+ PIZZA, // 88
+ SCHOOLBU, // 89
+ VOTETRUC, // 90
+
+ // new traffic car, april 2 2003
+ GLASTRUC,
+ CFIRE_V,
+
+ // new chase car, may 23, 2003 - good grief
+ CBONE, // 93
+ REDBRICK, // 94
+
+ NUM_VEHICLES,
+ INVALID
+ };
+
+
+
+
+
+}
+
+#endif //VEHICLE_ENUM \ No newline at end of file
diff --git a/game/code/contexts/allcontexts.cpp b/game/code/contexts/allcontexts.cpp
new file mode 100644
index 0000000..2890207
--- /dev/null
+++ b/game/code/contexts/allcontexts.cpp
@@ -0,0 +1,8 @@
+#include <contexts/bootupcontext.cpp>
+#include <contexts/context.cpp>
+#include <contexts/entrycontext.cpp>
+#include <contexts/exitcontext.cpp>
+#include <contexts/frontendcontext.cpp>
+#include <contexts/playingcontext.cpp>
+#include <contexts/loadingcontext.cpp>
+#include <contexts/pausecontext.cpp>
diff --git a/game/code/contexts/bootupcontext.cpp b/game/code/contexts/bootupcontext.cpp
new file mode 100644
index 0000000..0811517
--- /dev/null
+++ b/game/code/contexts/bootupcontext.cpp
@@ -0,0 +1,611 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: bootupcontext.cpp
+//
+// Description: Implementation of BootupContext.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifdef RAD_RELEASE
+ #ifndef RAD_E3
+ #define SHOW_MOVIES
+ #endif
+#endif
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+#include <radtime.hpp>
+#include <raddebugwatch.hpp>
+#include <radmovie2.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/context.hpp>
+#include <pddi/pddi.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/bootupcontext.h>
+
+#include <atc/atcmanager.h>
+#include <cards/cardgallery.h>
+#include <cheats/cheatinputsystem.h>
+#include <constants/movienames.h>
+#include <contexts/contextenum.h>
+#include <data/gamedatamanager.h>
+#include <data/memcard/memorycardmanager.h>
+#include <gameflow/gameflow.h>
+#include <input/inputmanager.h>
+#include <interiors/interiormanager.h>
+#include <loading/loadingmanager.h>
+#include <main/commandlineoptions.h>
+#include <memory/leakdetection.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <mission/missionmanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/rewards/rewardsmanager.h>
+#include <presentation/presentation.h>
+#include <presentation/presevents/presentationevent.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/tutorialmanager.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/renderlayer.h>
+#include <sound/soundmanager.h>
+#include <supersprint/supersprintmanager.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/character/charactermanager.h>
+
+#ifdef RAD_GAMECUBE
+ #include <main/gamecube_extras/gcmanager.h>
+#endif
+
+#ifdef RAD_WIN32
+ #include <main/win32platform.h>
+ #include <data/config/gameconfigmanager.h>
+#endif
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+BootupContext* BootupContext::spInstance = NULL;
+
+#ifdef RAD_RELEASE
+ #ifdef RAD_PS2
+ // TC: Edwin (Singh) says that the PS2 TRC requires that the license screen
+ // be displayed for at least 5 seconds.
+ //
+ const int MINIMUM_LICENSE_SCREEN_DISPLAY_TIME = 5000; // in msec
+ #else
+ const int MINIMUM_LICENSE_SCREEN_DISPLAY_TIME = 1000; // in msec
+ #endif
+#else
+ const int MINIMUM_LICENSE_SCREEN_DISPLAY_TIME = 1000; // in msec
+#endif
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// BootupContext::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the BootupContext singleton.
+// - Creates the BootupContext if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the BootupContext.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+BootupContext* BootupContext::GetInstance()
+{
+ if( spInstance == NULL )
+ {
+ spInstance = new(GMA_PERSISTENT) BootupContext;
+ rAssert( spInstance );
+ }
+
+ return spInstance;
+}
+
+//=============================================================================
+// BootupContext::StartMovies
+//=============================================================================
+// Description: Comment
+//
+// Parameters: Game Mode (Frontend/In-Game)
+//
+// Return: void
+//
+//=============================================================================
+void BootupContext::StartMovies()
+{
+#ifndef FINAL
+ if( CommandLineOptions::Get( CLO_SKIP_FE ) )
+ {
+ // enable 'unlock missions' cheat for 'skipfe' commandline option
+ //
+ GetCheatInputSystem()->SetCheatEnabled( CHEAT_ID_UNLOCK_MISSIONS, true );
+
+ short levelIndex = CommandLineOptions::GetDefaultLevel();
+
+ if( levelIndex == -1 ) // L0 = minigame
+ {
+ SetGameplayManager( SuperSprintManager::GetInstance() );
+
+ // skip FE and go to mini-game
+ //
+ GetGameFlow()->SetContext( CONTEXT_SUPERSPRINT_FE );
+ }
+ else
+ {
+ SetGameplayManager( MissionManager::GetInstance() );
+
+ // register controller ID [0] for player [0], by default
+ //
+ GetInputManager()->RegisterControllerID( 0, 0 );
+
+ short missionIndex = CommandLineOptions::GetDefaultMission();
+ if( levelIndex == RenderEnums::L1 )
+ {
+ // special case for level 1 due to tutorial mission being mission 0
+ //
+ missionIndex++;
+ }
+
+ // set level and mission to load for normal gameplay
+ //
+ GetGameplayManager()->SetLevelIndex( static_cast< RenderEnums::LevelEnum >( levelIndex ) );
+ GetGameplayManager()->SetMissionIndex( static_cast< RenderEnums::MissionEnum >( missionIndex ) );
+
+ // skip FE and go to normal gameplay
+ //
+ GetGameFlow()->SetContext( CONTEXT_LOADING_GAMEPLAY );
+ }
+ }
+ else
+#endif // !FINAL
+ {
+#ifdef SHOW_MOVIES
+ if( CommandLineOptions::Get( CLO_SKIP_MOVIE ) )
+ {
+ // Switch to frontend context.
+ GetGameFlow()->SetContext( CONTEXT_FRONTEND );
+ }
+ else
+ {
+ FMVEvent* pEvent = 0;
+
+ GetPresentationManager()->QueueFMV( &pEvent, this );
+ strcpy( pEvent->fileName, MovieNames::VUGLOGO );
+ pEvent->SetRenderLayer( RenderEnums::PresentationSlot );
+ pEvent->SetAutoPlay( true );
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_ENGLISH );
+ pEvent->SetClearWhenDone( true );
+
+ GetPresentationManager()->QueueFMV( &pEvent, this );
+ strcpy( pEvent->fileName, MovieNames::FOXLOGO );
+ pEvent->SetRenderLayer( RenderEnums::PresentationSlot );
+ pEvent->SetAutoPlay( true );
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_ENGLISH );
+ pEvent->SetClearWhenDone( true );
+
+ GetPresentationManager()->QueueFMV( &pEvent, this );
+ strcpy( pEvent->fileName, MovieNames::GRACIELOGO );
+ pEvent->SetRenderLayer( RenderEnums::PresentationSlot );
+ pEvent->SetAutoPlay( true );
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_ENGLISH );
+ pEvent->SetClearWhenDone( true );
+
+ GetPresentationManager()->QueueFMV( &pEvent, this );
+ strcpy( pEvent->fileName, MovieNames::RADICALLOGO );
+ pEvent->SetRenderLayer( RenderEnums::PresentationSlot );
+ pEvent->SetAutoPlay( true );
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_ENGLISH );
+ pEvent->SetClearWhenDone( true );
+
+ GetRenderManager()->mpLayer( RenderEnums::GUI )->Chill();
+ }
+#else
+ // Switch to frontend context.
+ GetGameFlow()->SetContext( CONTEXT_FRONTEND );
+#endif
+ }
+}
+
+void
+BootupContext::StartLoadingSound()
+{
+ GetSoundManager()->OnBootupStart();
+
+ GetLoadingManager()->AddCallback( this, (void*)GetSoundManager() );
+}
+
+#ifdef RAD_WIN32
+void BootupContext::LoadConfig()
+{
+ // Load the config file for the game.
+ GameConfigManager* gc = GetGameConfigManager();
+ bool success = gc->LoadConfigFile();
+
+ // If we couldn't load the config file, create a new one.
+ if( !success )
+ {
+ Win32Platform::GetInstance()->LoadDefaults();
+ gc->SaveConfigFile();
+ }
+}
+#endif
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// BootupContext::OnStart
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void BootupContext::OnStart( ContextEnum previousContext )
+{
+ SetMemoryIdentification( "BootupContext" );
+ HeapMgr()->PrepareHeapsFeCleanup();
+ HeapMgr()->PrepareHeapsFeSetup();
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+#ifdef DEBUGINFO_ENABLED
+ DebugInfo::InitializeStaticVariables();
+#endif
+
+ MEMTRACK_PUSH_FLAG( "Bootup" );
+ GetGameDataManager()->Init();
+
+#ifdef RAD_PS2
+ // must load memory card info first, before anything else, since the
+ // memory card boot-up check is done right at the beginning
+ //
+ if( !CommandLineOptions::Get( CLO_SKIP_MEMCHECK ) )
+ {
+ GetMemoryCardManager()->LoadMemcardInfo();
+ }
+#endif
+
+ GetGuiSystem()->Init();
+ GetGuiSystem()->RegisterUserInputHandlers();
+
+ GetCardGallery()->Init();
+ GetCheatInputSystem()->Init();
+ GetTutorialManager()->Initialize();
+ GetATCManager()->Init();
+
+ GetCharacterSheetManager()->InitCharacterSheet();
+
+ GetPresentationManager()->InitializePlayerDrawable();
+
+#ifdef RAD_GAMECUBE
+ //Initialize the GCManager's timers for testing reset and such.
+ GCManager::GetInstance()->Init();
+#endif
+
+ GetWorldPhysicsManager()->Init();
+
+ GetInteriorManager()->OnBootupStart();
+
+ // TC: for PS2, we shouldn't start loading sound stuff until we get to the
+ // license screen to avoid any synchronous script parsing that could
+ // lock-up the CPU briefly on a GUI prompt screen
+ //
+// GetSoundManager()->OnBootupStart();
+
+ GetCharacterManager()->PreLoad();
+
+ // load rewards script
+ //
+ GetRewardsManager()->LoadScript();
+
+ // preload some data that is common across all levels
+ // MissionScriptLoader::LoadP3DFile hacked to supress their loads in mission scripts
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, "art\\cars\\common.p3d", GMA_DEFAULT, "Global" );
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, "art\\cars\\huskA.p3d", GMA_DEFAULT, "Global");
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, "art\\phonecamera.p3d", GMA_DEFAULT, "Global");
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, "art\\cards.p3d", GMA_DEFAULT, "Global");
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, "art\\wrench.p3d", GMA_DEFAULT, "Global");
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, "art\\missions\\generic\\missgen.p3d", GMA_DEFAULT, "Global");
+
+ //
+ // Address any loading requests that the managers have queued up
+ //
+ GetLoadingManager()->AddCallback( this );
+
+#if defined( RAD_WIN32 ) && defined( SHOW_MOVIES )
+ GetInputManager()->GetFEMouse()->SetInGameMode( true );
+#endif
+}
+
+
+//==============================================================================
+// BootupContext::OnStop
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void BootupContext::OnStop( ContextEnum nextContext )
+{
+ rTunePrintf("BootupContext::OnStop... ");
+
+ GetGuiSystem()->UnregisterUserInputHandlers();
+
+ // release GUI bootup
+ GetGuiSystem()->HandleMessage( GUI_MSG_RELEASE_BOOTUP );
+
+#if defined( RAD_WIN32 ) && defined( SHOW_MOVIES )
+ GetInputManager()->GetFEMouse()->SetInGameMode( false );
+#endif
+
+
+ MEMTRACK_POP_FLAG( "" );
+
+ HeapMgr()->PopHeap ( GMA_PERSISTENT );
+
+ rTunePrintf("Finished\n");
+ SetMemoryIdentification( "BootupContext Finished" );
+}
+
+
+//==============================================================================
+// BootupContext::OnUpdate
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void BootupContext::OnUpdate( unsigned int elapsedTime )
+{
+ if( m_elapsedTime != -1 )
+ {
+ if( m_elapsedTime > MINIMUM_LICENSE_SCREEN_DISPLAY_TIME &&
+ m_bootupLoadCompleted && m_soundLoadCompleted )
+ {
+ // Tell GUI system to quit out of the boot-up state
+ GetGuiSystem()->HandleMessage( GUI_MSG_QUIT_BOOTUP );
+
+ m_elapsedTime = -1;
+ }
+ else
+ {
+ m_elapsedTime += elapsedTime;
+ }
+ }
+
+ // update game data manager
+ GetGameDataManager()->Update( elapsedTime );
+
+ GetPresentationManager()->Update( elapsedTime );
+
+ // update GUI system
+ GetGuiSystem()->Update( elapsedTime );
+}
+
+
+//==============================================================================
+// BootupContext::OnSuspend
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void BootupContext::OnSuspend()
+{
+}
+
+
+//==============================================================================
+// BootupContext::OnResume
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void BootupContext::OnResume()
+{
+}
+
+
+//==============================================================================
+// BootupContext::OnHandleEvent
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void BootupContext::OnHandleEvent( EventEnum id, void* pEventData )
+{
+}
+
+//=============================================================================
+// BootupContext::OnProcessRequestsComplete
+//=============================================================================
+// Description: Called when startup loading is done
+//
+// Parameters: pUserData - unused
+//
+// Return: void
+//
+//=============================================================================
+void BootupContext::OnProcessRequestsComplete( void* pUserData )
+{
+ if( pUserData == GetSoundManager() )
+ {
+ // set flag indicating all sound loads have completed
+ //
+ m_soundLoadCompleted = true;
+ }
+ else
+ {
+ // set flag indicating all bootup loads (except for sound) have completed
+ //
+ m_bootupLoadCompleted = true;
+ }
+
+ //
+ // Tell the sound manager to do some processing, now that the scripts
+ // are sure to have been loaded.
+ //
+ // NOTE: I've moved this here since this call triggers a CPU-hogging
+ // bit of dialog script postprocessing. That processing should be pulled out
+ // and done offline, but until then, do this somewhere where
+ // it won't starve the completion of FMVs. -- Esan
+ //
+ if( m_bootupLoadCompleted && m_soundLoadCompleted )
+ {
+ GetSoundManager()->OnBootupComplete();
+
+ GetInputManager()->ToggleRumble( false );
+ }
+}
+
+//=============================================================================
+// BootupContext::OnPresentationEventBegin
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( PresentationEvent* pEvent )
+//
+// Return: void
+//
+//=============================================================================
+void BootupContext::OnPresentationEventBegin( PresentationEvent* pEvent )
+{
+}
+
+//=============================================================================
+// BootupContext::OnPresentationEventLoadComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( PresentationEvent* pEvent )
+//
+// Return: void
+//
+//=============================================================================
+void BootupContext::OnPresentationEventLoadComplete( PresentationEvent* pEvent )
+{
+}
+
+
+//=============================================================================
+// BootupContext::OnPresentationEventEnd
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( PresentationEvent* pEvent )
+//
+// Return: void
+//
+//=============================================================================
+void BootupContext::OnPresentationEventEnd( PresentationEvent* pEvent )
+{
+ if( GetPresentationManager()->IsQueueEmpty() )
+ {
+ GetRenderManager()->mpLayer( RenderEnums::GUI )->Warm();
+
+ // Switch to frontend context.
+ GetGameFlow()->SetContext( CONTEXT_FRONTEND );
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// BootupContext::BootupContext
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================//
+BootupContext::BootupContext()
+: m_elapsedTime( -1 ),
+ m_bootupLoadCompleted( false ),
+ m_soundLoadCompleted( false ),
+ m_pSharedShader( 0 )
+{
+ m_pSharedShader = p3d::device->NewShader("simple");
+ rAssert( m_pSharedShader );
+ m_pSharedShader->AddRef();
+}
+
+
+//==============================================================================
+// BootupContext::~BootupContext
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================//
+BootupContext::~BootupContext()
+{
+ // Too bad we can't use tEntity::Release() since is
+ //a pddi object.
+ if( m_pSharedShader != 0 )
+ {
+ m_pSharedShader->Release();
+ m_pSharedShader = 0;
+ }
+ spInstance = NULL;
+}
+
diff --git a/game/code/contexts/bootupcontext.h b/game/code/contexts/bootupcontext.h
new file mode 100644
index 0000000..1304e7f
--- /dev/null
+++ b/game/code/contexts/bootupcontext.h
@@ -0,0 +1,98 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: bootupcontext.h
+//
+// Description:
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef BOOTUPCONTEXT_H
+#define BOOTUPCONTEXT_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/context.h> // is-a Context
+
+#include <loading/loadingmanager.h>
+#include <presentation/presevents/presentationevent.h>
+
+//========================================
+// Forward References
+//========================================
+class HeadToHeadManager;
+class pddiShader;
+
+//=============================================================================
+//
+// Synopsis:
+//
+//=============================================================================
+class BootupContext : public Context,
+ public LoadingManager::ProcessRequestsCallback,
+ public PresentationEvent::PresentationEventCallBack
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static BootupContext* GetInstance();
+
+ void StartMovies();
+ void StartLoadingSound();
+ void ResetLicenseScreenDisplayTime() { m_elapsedTime = 0; }
+
+#ifdef RAD_WIN32
+ void LoadConfig();
+#endif
+
+ pddiShader* GetSharedShader( void ) { return m_pSharedShader; }
+
+ protected:
+
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+ virtual void OnHandleEvent( EventEnum id, void* pEventData );
+
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+ virtual void OnPresentationEventBegin( PresentationEvent* pEvent );
+ virtual void OnPresentationEventLoadComplete( PresentationEvent* pEvent );
+ virtual void OnPresentationEventEnd( PresentationEvent* pEvent );
+
+ private:
+
+ // constructor and destructor are protected to force singleton implementation
+ BootupContext();
+ virtual ~BootupContext();
+
+ // Declared but not defined to prevent copying and assignment.
+ BootupContext( const BootupContext& );
+ BootupContext& operator=( const BootupContext& );
+
+ // Pointer to the one and only instance of this singleton.
+ static BootupContext* spInstance;
+
+ int m_elapsedTime;
+ bool m_bootupLoadCompleted : 1;
+ bool m_soundLoadCompleted : 1;
+
+ pddiShader* m_pSharedShader;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline BootupContext* GetBootupContext() { return( BootupContext::GetInstance() ); }
+
+
+#endif
diff --git a/game/code/contexts/context.cpp b/game/code/contexts/context.cpp
new file mode 100644
index 0000000..6252f25
--- /dev/null
+++ b/game/code/contexts/context.cpp
@@ -0,0 +1,197 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: context.cpp
+//
+// Description: Implementation of Context base class.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/context.h>
+#include <memory/srrmemory.h>
+
+
+#include <debug/debuginfo.h>
+
+// Managers.
+//
+#include <input/inputmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Context::DestroyInstance
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Context::DestroyInstance()
+{
+ delete( GMA_PERSISTENT, this );
+}
+
+//==============================================================================
+// Context::Start
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Context::Start( ContextEnum previousContext )
+{
+ this->OnStart( previousContext );
+}
+
+
+//==============================================================================
+// Context::Stop
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Context::Stop( ContextEnum nextContext )
+{
+ this->OnStop( nextContext );
+}
+
+
+//==============================================================================
+// Context::Update
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Context::Update( unsigned int elapsedTime )
+{
+ InputManager::GetInstance()->Update( elapsedTime );
+ this->OnUpdate( elapsedTime );
+}
+
+
+//==============================================================================
+// Context::HandleEvent
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Context::HandleEvent( EventEnum id, void* pEventData )
+{
+ this->OnHandleEvent( id, pEventData );
+}
+
+//=============================================================================
+// Context::Suspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Context::Suspend()
+{
+ m_state = S_SUSPENDED;
+
+ OnSuspend();
+}
+
+//=============================================================================
+// Context::Resume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Context::Resume()
+{
+ m_state = S_ACTIVE;
+
+ OnResume();
+}
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// Context::Context
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================//
+Context::Context()
+{
+}
+
+
+//==============================================================================
+// Context::~Context
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================//
+Context::~Context()
+{
+}
+
diff --git a/game/code/contexts/context.h b/game/code/contexts/context.h
new file mode 100644
index 0000000..b4435cb
--- /dev/null
+++ b/game/code/contexts/context.h
@@ -0,0 +1,94 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: context.h
+//
+// Description: Context base class declaration.
+//
+// History: + Stolen and cleaned up from Penthouse -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef CONTEXT_H
+#define CONTEXT_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/contextenum.h>
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: This is the Context Controller base class. It will be used as
+// a base class for all context controllers in the game, such as
+// PlayContext and PauseContext. These will
+// be updated from the GameFlow.
+//
+//=============================================================================
+class Context : public EventListener
+{
+ public:
+
+ void DestroyInstance();
+
+ // gameflow should call these functions to manipulate the context controller;
+ // derived classes must NOT over-ride the following non-virtual functions;
+ // these functions will call the corresponding protected virtual functions
+ void Start( ContextEnum previousContext );
+ void Stop( ContextEnum nextContext );
+ void Update( unsigned int elapsedTime );
+
+ // The game must halt when the disc door is opened on the GameCube.
+ void Suspend();
+ void Resume();
+ bool IsSuspended() const {return m_state == S_SUSPENDED;};
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ protected:
+
+ // constructor and destructor are protected to force singleton implementation
+ Context();
+ virtual ~Context();
+
+ // derived classes MUST over-ride the following virtual functions to implement
+ // custom behaviour; these functions are called by the corresponding public
+ // non-virtual functions
+ virtual void OnStart( ContextEnum previousContext ) = 0;
+ virtual void OnStop( ContextEnum nextContext ) = 0;
+ virtual void OnUpdate( unsigned int elapsedTime ) = 0;
+
+ virtual void OnSuspend() = 0;
+ virtual void OnResume() = 0;
+
+ virtual void OnHandleEvent( EventEnum id, void* pEventData ) = 0;
+
+ enum StateEnum
+ {
+ S_NONE,
+ S_READY,
+ S_ACTIVE,
+ S_SUSPENDED,
+ S_EXIT,
+ MAX_STATES
+ };
+
+ StateEnum m_state;
+
+ private:
+
+ // Declared but not defined to prevent copying and assignment.
+ Context( const Context& );
+ Context& operator=( const Context& );
+};
+
+#endif
diff --git a/game/code/contexts/contextenum.h b/game/code/contexts/contextenum.h
new file mode 100644
index 0000000..94871b0
--- /dev/null
+++ b/game/code/contexts/contextenum.h
@@ -0,0 +1,71 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: contextenum.h
+//
+// Description: Game contexts.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef CONTEXTENUM_H
+#define CONTEXTENUM_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+//========================================
+// Constants, Typedefs and Statics
+//========================================
+enum ContextEnum
+{
+ // The following shows the possible transitions between
+ // contexts:
+ // [ PREVIOUS ] [ CURRENT ] [ NEXT ]
+
+ CONTEXT_ENTRY, // (Start) -----> ENTRY -----> BOOTUP
+
+ CONTEXT_BOOTUP, // ENTRY -----> BOOTUP -----> FRONTEND
+
+ CONTEXT_FRONTEND, // BOOTUP -----> FRONTEND -----> LOADING_G
+ // | |
+ // PAUSE --| |--> LOADING_D
+
+ CONTEXT_LOADING_DEMO, // FRONTEND -----> LOADING_D -----> DEMO
+
+ CONTEXT_DEMO, // LOADING_D -----> DEMO -----> FRONTEND
+
+ CONTEXT_SUPERSPRINT_FE, // FRONTEND -----> SS_FE -----> LOADING_S
+ // |
+ // |--> FRONTEND
+
+ CONTEXT_LOADING_SUPERSPRINT, // SS_FE -----> LOADING_S -----> SUPERSPRINT
+
+ CONTEXT_SUPERSPRINT, // LOADING_S -----> SUPERSPRINT -----> SS_FE
+
+ CONTEXT_LOADING_GAMEPLAY, // FRONTEND -----> LOADING_G -----> GAMEPLAY
+
+ CONTEXT_GAMEPLAY, // LOADING_G -----> GAMEPLAY -----> PAUSE
+ // |
+ // PAUSE --|
+
+ CONTEXT_PAUSE, // GAMEPLAY -----> PAUSE -----> GAMEPLAY
+ // |
+ // |--> FRONTEND
+
+ CONTEXT_EXIT, // FRONTEND -----> EXIT -----> (End)
+
+ NUM_CONTEXTS
+};
+
+#endif // CONTEXTENUM_H
diff --git a/game/code/contexts/demo/alldemo.cpp b/game/code/contexts/demo/alldemo.cpp
new file mode 100644
index 0000000..8162474
--- /dev/null
+++ b/game/code/contexts/demo/alldemo.cpp
@@ -0,0 +1,2 @@
+#include <contexts/demo/loadingdemocontext.cpp>
+#include <contexts/demo/democontext.cpp>
diff --git a/game/code/contexts/demo/democontext.cpp b/game/code/contexts/demo/democontext.cpp
new file mode 100644
index 0000000..f1d2cfd
--- /dev/null
+++ b/game/code/contexts/demo/democontext.cpp
@@ -0,0 +1,563 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement DemoContext
+//
+// History: 21/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/shadow.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <ai/vehicle/vehicleairender.h>
+
+#include <contexts/demo/democontext.h>
+#include <gameflow/gameflow.h>
+#include <main/commandlineoptions.h>
+#include <main/game.h>
+#include <memory/srrmemory.h>
+#include <memory/leakdetection.h>
+#include <mission/gameplaymanager.h>
+#include <presentation/gui/guisystem.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/DSG/StatePropDSG.h>
+#include <render/rendermanager/renderlayer.h>
+#include <ai/actor/actormanager.h>
+
+#include <worldsim/character/footprint/footprintmanager.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/coins/sparkle.h>
+#include <worldsim/hitnrunmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/ped/pedestrianmanager.h>
+
+#include <worldsim/skidmarks/skidmarkmanager.h>
+
+#include <camera/walkercam.h>
+#include <camera/followcam.h>
+#include <camera/wrecklesscam.h>
+#include <camera/chasecam.h>
+#include <camera/conversationcam.h>
+#include <camera/supercammanager.h>
+
+#include <render/breakables/breakablesmanager.h>
+#include <render/particles/particlemanager.h>
+#include <render/animentitydsgmanager/animentitydsgmanager.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <worldsim/skidmarks/skidmarkgenerator.h>
+#include <sound/soundmanager.h>
+#include <meta/triggervolumetracker.h>
+#include <render/Loaders/AnimDynaPhysLoader.h>
+
+#include <pedpaths/pathmanager.h>
+
+#include <input/inputmanager.h>
+
+// TODO: Remove once we put CreateRoadNetwork in the levels pipe
+#include <roads/roadmanager.h>
+#include <p3d/light.hpp>
+#include <p3d/view.hpp>
+
+#include <worldsim/worldobject.h>
+#include <ai/actionbuttonmanager.h>
+
+#include <presentation/presentation.h>
+
+#include <mission/animatedicon.h>
+
+#include <render/animentitydsgmanager/animentitydsgmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+extern bool g_inDemoMode;
+
+// Static pointer to instance of singleton.
+DemoContext* DemoContext::spInstance = NULL;
+
+const unsigned int DEMO_LOOP_TIME = 60000; // in msec
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// DemoContext::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the DemoContext singleton.
+// - Creates the DemoContext if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the DemoContext.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+DemoContext* DemoContext::GetInstance()
+{
+ if( spInstance == NULL )
+ {
+ spInstance = new DemoContext;
+ rAssert( spInstance );
+ }
+
+ return spInstance;
+}
+
+//==============================================================================
+// DemoContext::DemoContext
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DemoContext::DemoContext()
+: m_demoLoopTime( DEMO_LOOP_TIME ),
+ m_elapsedTime( -1 )
+{
+}
+
+//==============================================================================
+// DemoContext::~DemoContext
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DemoContext::~DemoContext()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// DemoContext::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum previousContext )
+//
+// Return: void
+//
+//=============================================================================
+void DemoContext::OnStart( ContextEnum previousContext )
+{
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnStart( previousContext );
+
+ GetInputManager()->SetGameState( Input::ACTIVE_FRONTEND );
+
+ // We count the number of demos run.
+ //
+ GetGame()->IncrementDemoCount();
+
+ m_elapsedTime = 0; // reset elapsed demo time
+
+ ////////////////////////////////////////////////////////////
+ // RenderManager
+ RenderManager* rm = GetRenderManager();
+ RenderLayer* rl = rm->mpLayer( RenderEnums::LevelSlot );
+ rAssert( rl );
+
+#ifdef DEBUGWATCH
+ // bootstrap vehicleai renderer
+ VehicleAIRender::GetVehicleAIRender();
+#endif
+
+ ////////////////////////////////////////////////////////////
+ // Cameras set up
+ unsigned int iNumPlayers = GetGameplayManager()->GetNumPlayers();
+ rl->SetNumViews( iNumPlayers );
+ rl->SetUpViewCam();
+
+ p3d::inventory->SelectSection("Default");
+ tLightGroup* sun = p3d::find<tLightGroup>("sun");
+ rAssert( sun );
+ rm->SetLevelLayerLights( sun );
+
+ float aspect = p3d::display->IsWidescreen() ? (16.0f / 9.0f) : (4.0f / 3.0f);
+
+ unsigned int view = 0;
+
+ tPointCamera* cam = static_cast<tPointCamera*>( rl->pCam( view ) );
+ rAssert( dynamic_cast<tPointCamera*> ( cam ) != NULL );
+ rAssert( cam );
+
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC( view );
+ rAssert( scc );
+
+ scc->SetCamera( cam );
+
+ FollowCam* fc = new FollowCam( FollowCam::FOLLOW_NEAR );
+ fc->SetAspect( aspect );
+ fc->CopyToData();
+ scc->RegisterSuperCam( fc );
+
+ fc = new FollowCam( FollowCam::FOLLOW_FAR );
+ fc->SetAspect( aspect );
+ fc->CopyToData();
+ scc->RegisterSuperCam( fc );
+
+ SuperCam* sc = new WalkerCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+
+ sc = new ChaseCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+
+ sc = new WrecklessCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+
+ sc = new ConversationCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+
+
+ ////////////////////////////////////////////////////////////
+ // AvatarManager Bootstrapping
+ GetAvatarManager( )->EnterGame( );
+ /*
+ GetActionButtonManager( )->EnterGame( );
+ */
+
+ ////////////////////////////////////////////////////////////
+ // TrafficManager Init
+ TrafficManager::GetInstance()->Init();
+
+ ////////////////////////////////////////////////////////////
+ // PedestrianManager Init
+ PedestrianManager::GetInstance()->Init();
+
+ // TODO: Move this into level pipe
+ //Set up the sorting of the intersections and stuff.
+ RoadManager::GetInstance()->CreateRoadNetwork();
+
+ ////////////////////////////////////////////////////////////
+ // SkidMark Init Init
+ //Find skid mark shaders in the inventory and set proper values
+ SkidMarkGenerator::InitShaders();
+
+ GetSkidmarkManager()->Init();
+
+ ////////////////////////////////////////////////////////////
+ // OnStart calls
+ //
+ // Notify the sound system of gameplay start. This has been moved after
+ // the GUI startup above, since that leads to a mission reset, which causes
+ // character position in or out of the car to be decided, which the sound
+ // system uses to determine which sounds to start playing.
+ //
+ SoundManager::GetInstance()->OnGameplayStart();
+ /*
+ GetPresentationManager()->OnGameplayStart();
+ */
+
+ GetGuiSystem()->HandleMessage( GUI_MSG_RUN_INGAME );
+ GetGuiSystem()->HandleMessage( GUI_MSG_RUN_DEMO ); // TC: must be called after GUI_MSG_RUN_INGAME
+
+ GetGuiSystem()->RegisterUserInputHandlers();
+
+ GetGame()->SetTime( 0 );
+
+ g_inDemoMode = true;
+}
+
+//=============================================================================
+// DemoContext::OnStop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum nextContext )
+//
+// Return: void
+//
+//=============================================================================
+void DemoContext::OnStop( ContextEnum nextContext )
+{
+ HeapMgr()->PushHeap (GMA_TEMP);
+
+ //
+ // This is called to prevent DMA of destroyed textures,
+ // because we don't swap buffers until the next frame.
+ //
+ p3d::pddi->DrawSync();
+
+ // Clear the shadow list
+ AnimDynaPhysLoader::ClearShadowList();
+
+ GetCoinManager()->Destroy();
+ GetSparkleManager()->Destroy();
+ GetSkidmarkManager()->Destroy();
+ GetHitnRunManager()->Destroy();
+
+ StatePropDSG::RemoveAllSharedtPoses();
+
+
+ const bool shutdown = true;
+ GetSuperCamManager()->Init( shutdown );
+
+ TriggerVolumeTracker::GetInstance()->Cleanup();
+
+ // Clean up lights!
+ //
+ RenderLayer* rl = GetRenderManager()->mpLayer( RenderEnums::LevelSlot );
+ rAssert( rl );
+ for( unsigned int i = 0; i < rl->GetNumViews(); i++ )
+ {
+ rl->pView(i)->RemoveAllLights ();
+ }
+
+#ifdef DEBUGWATCH
+ VehicleAIRender::Destroy();
+#endif
+
+ GetPresentationManager()->OnGameplayStop();
+
+ GetPresentationManager()->Finalize();
+
+ //Destroy Avatars stuff first
+
+ GetGameplayManager()->Finalize();
+ SetGameplayManager( NULL );
+
+ GetAvatarManager()->Destroy();
+
+ TrafficManager::DestroyInstance();
+ PedestrianManager::DestroyInstance();
+ GetCharacterManager()->Destroy();
+ GetVehicleCentral()->Unload();
+ GetActorManager()->RemoveAllActors();
+ GetActorManager()->RemoveAllSpawnPoints();
+
+ //We never entered.
+ //GetActionButtonManager( )->Destroy( );
+
+ GetBreakablesManager()->FreeAllBreakables();
+ GetParticleManager()->ClearSystems();
+ GetRenderManager()->DumpAllLoadedData();
+ SkidMarkGenerator::ReleaseShaders();
+
+ GetSoundManager()->OnGameplayEnd( true );
+
+ PathManager::GetInstance()->Destroy();
+
+
+ // TODO - Darryl?
+ //
+ // all active vehicles should be destroyed
+ // they are owned by the missions that created them.
+
+ //This does cleanup.
+ RoadManager::GetInstance()->Destroy();
+
+ // release GUI in-game
+ GetGuiSystem()->HandleMessage( GUI_MSG_RELEASE_INGAME );
+
+ /*
+ // enable screen clearing
+ GetRenderManager()->mpLayer(RenderEnums::GUI)->pView( 0 )->SetClearMask( PDDI_BUFFER_ALL );
+ */
+
+ // Cleanup the Avatar Manager
+ //
+ GetAvatarManager()->ExitGame();
+
+ // Flush out the special section used by physics to cache SkeletonInfos.
+ //
+ p3d::inventory->RemoveSectionElements (SKELCACHE);
+ p3d::inventory->DeleteSection (SKELCACHE);
+
+ GetGuiSystem()->UnregisterUserInputHandlers();
+
+ // enable screen clearing
+ //
+ GetRenderManager()->mpLayer( RenderEnums::GUI )->pView( 0 )->SetClearMask( PDDI_BUFFER_ALL );
+
+#ifndef RAD_RELEASE
+ // Dump out the contents of the inventory sections
+ //
+ p3d::inventory->Dump (true);
+#endif
+
+ AnimatedIcon::ShutdownAnimatedIcons();
+ GetAnimEntityDSGManager()->RemoveAll();
+
+ HeapMgr()->PopHeap (GMA_TEMP);
+
+
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnStop( nextContext );
+}
+
+//=============================================================================
+// DemoContext::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void DemoContext::OnUpdate( unsigned int elapsedTime )
+{
+ if ( !mQuitting )
+ {
+ bool cont = true;
+
+ if( m_elapsedTime != -1 )
+ {
+ m_elapsedTime += static_cast<int>( elapsedTime );
+ if( m_elapsedTime > static_cast<int>( m_demoLoopTime ) )
+ {
+ if ( GetLoadingManager()->IsLoading() )
+ {
+ //HACK!
+ //Oops! Can't quit yet!
+ m_elapsedTime -= static_cast<int>( elapsedTime );
+ }
+ else
+ {
+ // demo loop time expired, quit out of demo
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_QUIT_DEMO );
+
+ m_elapsedTime = -1;
+ mQuitting = true; //Now we're quitting
+ }
+ cont = false;
+ }
+ }
+
+ if ( cont )
+ {
+ float timeins = (float)(elapsedTime) / 1000.0f;
+ GetAvatarManager()->Update( timeins );
+
+ GetCharacterManager()->GarbageCollect( );
+
+ ///////////////////////////////////////////////////////////////
+ // Update Particles
+ GetParticleManager()->Update( elapsedTime );
+ GetBreakablesManager()->Update( elapsedTime );
+ GetAnimEntityDSGManager()->Update( elapsedTime );
+
+ ///////////////////////////////////////////////////////////////
+ // Update Gameplay Manager
+ GetGameplayManager()->Update( elapsedTime );
+
+ ///////////////////////////////////////////////////////////////
+ // Update Physics
+ GetWorldPhysicsManager()->Update(elapsedTime);
+
+ ///////////////////////////////////////////////////////////////
+ // Update Peds
+ // ordering is important. Unless other parts of code change, we must call
+ // this before WorldPhysManager::Update() because PedestrianManager
+ // sets the flags for all characters to be updated in WorldPhys Update
+ PedestrianManager::GetInstance()->Update( elapsedTime );
+
+ /*
+ ///////////////////////////////////////////////////////////////
+ // Update PresentationManager
+ GetPresentationManager()->Update( elapsedTime );
+ */
+
+ ///////////////////////////////////////////////////////////////
+ // Update Trigger volumes
+ GetTriggerVolumeTracker()->Update( elapsedTime );
+
+ ///////////////////////////////////////////////////////////////
+ // Update Traffic
+ TrafficManager::GetInstance()->Update( elapsedTime );
+
+ /*
+ ///////////////////////////////////////////////////////////////
+ // Update Interiors
+ GetInteriorManager()->Update( elapsedTime );
+ */
+
+ //extra updates
+ GetFootprintManager()->Update( elapsedTime );
+ BEGIN_PROFILE("SkidmarkManager");
+ GetSkidmarkManager()->Update( elapsedTime );
+ END_PROFILE("SkidmarkManager");
+ ActorManager::GetInstance()->Update( elapsedTime );
+ GetCoinManager()->Update( elapsedTime );
+ GetSparkleManager()->Update( elapsedTime );
+ GetHitnRunManager()->Update( elapsedTime );
+ }
+ } //!mQuitting
+
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnUpdate( elapsedTime );
+}
+
+//=============================================================================
+// DemoContext::OnSuspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DemoContext::OnSuspend()
+{
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnSuspend();
+}
+
+//=============================================================================
+// DemoContext::OnResume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DemoContext::OnResume()
+{
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnResume();
+}
+
diff --git a/game/code/contexts/demo/democontext.h b/game/code/contexts/demo/democontext.h
new file mode 100644
index 0000000..32a50ed
--- /dev/null
+++ b/game/code/contexts/demo/democontext.h
@@ -0,0 +1,75 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: DemoContext.h
+//
+// Description:
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef DEMOCONTEXT_H
+#define DEMOCONTEXT_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/playingcontext.h> // is-a PlayingContext
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis:
+//
+//=============================================================================
+class DemoContext : public PlayingContext
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static DemoContext* GetInstance();
+
+ void EndDemo(void) { m_elapsedTime = m_demoLoopTime + 1; }
+
+ void SetDemoLoopTime( unsigned int timems ) { m_demoLoopTime = timems; };
+
+protected:
+
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+ private:
+
+ // constructor and destructor are protected to force singleton implementation
+ DemoContext();
+ virtual ~DemoContext();
+
+ // Declared but not defined to prevent copying and assignment.
+ DemoContext( const DemoContext& );
+ DemoContext& operator=( const DemoContext& );
+
+ // Pointer to the one and only instance of this singleton.
+ static DemoContext* spInstance;
+
+ unsigned int m_demoLoopTime;
+ int m_elapsedTime;
+
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline DemoContext* GetDemoContext() { return( DemoContext::GetInstance() ); }
+
+
+#endif // DEMOCONTEXT_H
diff --git a/game/code/contexts/demo/loadingdemocontext.cpp b/game/code/contexts/demo/loadingdemocontext.cpp
new file mode 100644
index 0000000..9d8323a
--- /dev/null
+++ b/game/code/contexts/demo/loadingdemocontext.cpp
@@ -0,0 +1,244 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement LoadingDemoContext
+//
+// History: 21/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/demo/loadingdemocontext.h>
+
+#include <loading/loadingmanager.h>
+#include <mission/gameplaymanager.h>
+#include <presentation/gui/guisystem.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <sound/soundmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+LoadingDemoContext* LoadingDemoContext::spInstance = NULL;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// LoadingDemoContext::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the LoadingDemoContext singleton.
+// - Creates the LoadingDemoContext if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the LoadingDemoContext.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+LoadingDemoContext* LoadingDemoContext::GetInstance()
+{
+ if( spInstance == NULL )
+ {
+ spInstance = new LoadingDemoContext;
+ rAssert( spInstance );
+ }
+
+ return spInstance;
+}
+
+//==============================================================================
+// LoadingDemoContext::LoadingDemoContext
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LoadingDemoContext::LoadingDemoContext()
+{
+}
+
+//==============================================================================
+// LoadingDemoContext::~LoadingDemoContext
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LoadingDemoContext::~LoadingDemoContext()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// LoadingDemoContext::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum previousContext )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingDemoContext::OnStart( ContextEnum previousContext )
+{
+ GetGameplayManager()->mIsDemo = true;
+
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnStart( previousContext );
+
+/*****************************************************************************
+ * Start inserting stuff below ...
+ *****************************************************************************/
+
+ // NOTE:
+ // Assumes we never start a DEMO on foot (always start inside the car)
+ GetVehicleCentral()->ActivateVehicleTriggers( false );
+
+ TrafficManager::GetInstance()->InitDefaultModelGroups();
+
+ // initialize GUI in-game mode (and load resources)
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_INIT_INGAME );
+
+ GetLoadingManager()->AddCallback( this );
+}
+
+//=============================================================================
+// LoadingDemoContext::OnStop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum nextContext )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingDemoContext::OnStop( ContextEnum nextContext )
+{
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnStop( nextContext );
+}
+
+//=============================================================================
+// LoadingDemoContext::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingDemoContext::OnUpdate( unsigned int elapsedTime )
+{
+ GetGameplayManager()->Update( elapsedTime );
+
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnUpdate( elapsedTime );
+}
+
+//=============================================================================
+// LoadingDemoContext::OnSuspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoadingDemoContext::OnSuspend()
+{
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnSuspend();
+}
+
+//=============================================================================
+// LoadingDemoContext::OnResume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoadingDemoContext::OnResume()
+{
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnResume();
+}
+
+//=============================================================================
+// LoadingDemoContext::PrepareNewHeaps
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoadingDemoContext::PrepareNewHeaps()
+{
+ HeapMgr()->PrepareHeapsInGame();
+}
+
+//=============================================================================
+// LoadingDemoContext::OnProcessRequestsComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingDemoContext::OnProcessRequestsComplete( void* pUserData )
+{
+ GetGameplayManager()->LevelLoaded();
+
+ //
+ // Queue the loading for level sounds
+ //
+ GetSoundManager()->QueueLevelSoundLoads();
+
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnProcessRequestsComplete( pUserData );
+}
diff --git a/game/code/contexts/demo/loadingdemocontext.h b/game/code/contexts/demo/loadingdemocontext.h
new file mode 100644
index 0000000..ca4c321
--- /dev/null
+++ b/game/code/contexts/demo/loadingdemocontext.h
@@ -0,0 +1,71 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: LoadingDemoContext.h
+//
+// Description:
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef LOADINGDEMOCONTEXT_H
+#define LOADINGDEMOCONTEXT_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/loadingcontext.h> // is-a LoadingContext
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis:
+//
+//=============================================================================
+class LoadingDemoContext : public LoadingContext
+{
+ public:
+ // Static Methods for accessing this singleton.
+ static LoadingDemoContext* GetInstance();
+
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+ protected:
+
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+ virtual void PrepareNewHeaps();
+
+ private:
+
+ // constructor and destructor are protected to force singleton implementation
+ LoadingDemoContext();
+ virtual ~LoadingDemoContext();
+
+ // Declared but not defined to prevent copying and assignment.
+ LoadingDemoContext( const LoadingDemoContext& );
+ LoadingDemoContext& operator=( const LoadingDemoContext& );
+
+ // Pointer to the one and only instance of this singleton.
+ static LoadingDemoContext* spInstance;
+
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline LoadingDemoContext* GetLoadingDemoContext() { return( LoadingDemoContext::GetInstance() ); }
+
+
+#endif // LOADINGDEMOCONTEXT_H
diff --git a/game/code/contexts/entrycontext.cpp b/game/code/contexts/entrycontext.cpp
new file mode 100644
index 0000000..41beb67
--- /dev/null
+++ b/game/code/contexts/entrycontext.cpp
@@ -0,0 +1,211 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: entrycontext.cpp
+//
+// Description: Implementation of EntryContext.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/entrycontext.h>
+#include <memory/srrmemory.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Static pointer to instance of singleton.
+//
+EntryContext* EntryContext::spInstance = NULL;
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// EntryContext::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the EntryContext singleton.
+// - Creates the EntryContext if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the EntryContext.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+EntryContext* EntryContext::GetInstance()
+{
+ if( spInstance == NULL )
+ {
+ spInstance = new(GMA_PERSISTENT) EntryContext;
+ rAssert( spInstance );
+ }
+
+ return spInstance;
+}
+
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// EntryContext::OnStart
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void EntryContext::OnStart( ContextEnum previousContext )
+{
+ MEMTRACK_PUSH_FLAG( "Entry" );
+}
+
+
+//==============================================================================
+// EntryContext::OnStop
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void EntryContext::OnStop( ContextEnum nextContext )
+{
+ MEMTRACK_POP_FLAG( "" );
+}
+
+
+//==============================================================================
+// EntryContext::OnUpdate
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void EntryContext::OnUpdate( unsigned int elapsedTime )
+{
+}
+
+
+//==============================================================================
+// EntryContext::OnSuspend
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void EntryContext::OnSuspend()
+{
+}
+
+
+//==============================================================================
+// EntryContext::OnResume
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void EntryContext::OnResume()
+{
+}
+
+
+//==============================================================================
+// EntryContext::OnHandleEvent
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void EntryContext::OnHandleEvent( EventEnum id, void* pEventData )
+{
+}
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// EntryContext::EntryContext
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+EntryContext::EntryContext()
+{
+}
+
+
+//==============================================================================
+// EntryContext::~EntryContext
+//==============================================================================
+//
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+EntryContext::~EntryContext()
+{
+ spInstance = NULL;
+}
+
diff --git a/game/code/contexts/entrycontext.h b/game/code/contexts/entrycontext.h
new file mode 100644
index 0000000..0d30fe5
--- /dev/null
+++ b/game/code/contexts/entrycontext.h
@@ -0,0 +1,72 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: entrycontext.h
+//
+// Description: EntryContext class declaration.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef ENTRYCONTEXT_H
+#define ENTRYCONTEXT_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/context.h> // is-a Context
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: The game is initialized to this empty context. It doesn't
+// do anything!
+//
+//=============================================================================
+class EntryContext : public Context
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static EntryContext* GetInstance();
+
+ protected:
+
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+ virtual void OnHandleEvent( EventEnum id, void* pEventData );
+
+ private:
+
+ // constructor and destructor are protected to force singleton implementation
+ EntryContext();
+ virtual ~EntryContext();
+
+ // Declared but not defined to prevent copying and assignment.
+ EntryContext( const EntryContext& );
+ EntryContext& operator=( const EntryContext& );
+
+ // Pointer to the one and only instance of this singleton.
+ static EntryContext* spInstance;
+};
+
+//
+// A little syntactic sugar for getting at this singleton.
+//
+inline EntryContext* GetEntryContext() { return( EntryContext::GetInstance() ); }
+
+
+#endif // ENTRYCONTEXT_H
diff --git a/game/code/contexts/exitcontext.cpp b/game/code/contexts/exitcontext.cpp
new file mode 100644
index 0000000..3c8916a
--- /dev/null
+++ b/game/code/contexts/exitcontext.cpp
@@ -0,0 +1,266 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: exitcontext.cpp
+//
+// Description: Implementation of ExitContext.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/view.hpp>
+#include <p3d/shadow.hpp>
+#include <raddebug.hpp>
+
+#include <p3d/shadow.hpp>
+//========================================
+// Project Includes
+//========================================
+#include <ai/vehicle/vehicleairender.h>
+#include <contexts/pausecontext.h>
+#include <contexts/exitcontext.h>
+#include <memory/leakdetection.h>
+#include <memory/srrmemory.h>
+#include <worldsim/worldobject.h>
+#include <camera/supercammanager.h>
+#include <interiors/interiormanager.h>
+#include <meta/triggervolumetracker.h>
+#include <loading/loadingmanager.h>
+#include <mission/gameplaymanager.h>
+#include <presentation/gui/guisystem.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/DSG/StatePropDSG.h>
+#include <sound/soundmanager.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/footprint/footprintmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/ped/pedestrianmanager.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/coins/sparkle.h>
+#include <worldsim/hitnrunmanager.h>
+#include <worldsim/parkedcars/parkedcarmanager.h>
+#include <ai/actionbuttonmanager.h>
+#include <render/breakables/breakablesmanager.h>
+#include <render/particles/ParticleManager.h>
+#include <worldsim/skidmarks/skidmarkgenerator.h>
+#include <worldsim/skidmarks/skidmarkmanager.h>
+#include <ai/actor/actormanager.h>
+#include <gameflow/gameflow.h>
+#include <roads/roadmanager.h>
+#include <render/Loaders/AnimDynaPhysLoader.h>
+#include <pedpaths/pathmanager.h>
+#include <data/gamedatamanager.h>
+#include <mission/animatedicon.h>
+#include <render/animentitydsgmanager/animentitydsgmanager.h>
+#include <presentation/presentation.h>
+#include <main/game.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+ExitContext* ExitContext::spInstance = NULL;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ExitContext::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the ExitContext singleton.
+// - Creates the ExitContext if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the ExitContext.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+ExitContext* ExitContext::GetInstance()
+{
+ if( spInstance == NULL )
+ {
+ spInstance = new(GMA_PERSISTENT) ExitContext;
+ rAssert( spInstance );
+ }
+
+ return spInstance;
+}
+
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ExitContext::OnStart
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+// Note: Exiting is currently not valid from every context. This needs more
+// checking and handling of special cases.
+//
+//==============================================================================
+void ExitContext::OnStart( ContextEnum previousContext )
+{
+ MEMTRACK_PUSH_FLAG( "Exit" );
+
+ if( previousContext == CONTEXT_GAMEPLAY || previousContext == CONTEXT_LOADING_GAMEPLAY )
+ {
+ // My precious little hack... my preciouss.
+ GameFlow::GetInstance()->GetContext( CONTEXT_PAUSE )->Start( CONTEXT_GAMEPLAY );
+ GameFlow::GetInstance()->GetContext( CONTEXT_PAUSE )->Stop( CONTEXT_EXIT );
+ }
+
+ GetLoadingManager()->CancelPendingRequests();
+ p3d::loadManager->CancelAll();
+
+ GetSoundManager()->OnGameplayEnd( false );
+}
+
+
+//==============================================================================
+// ExitContext::OnStop
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void ExitContext::OnStop( ContextEnum nextContext )
+{
+ MEMTRACK_POP_FLAG( "" );
+ GetGame()->Stop();
+
+ rAssertMsg( false, "No happen should." );
+}
+
+
+//==============================================================================
+// ExitContext::OnUpdate
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void ExitContext::OnUpdate( unsigned int elapsedTime )
+{
+}
+
+
+//==============================================================================
+// ExitContext::OnSuspend
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void ExitContext::OnSuspend()
+{
+}
+
+
+//==============================================================================
+// ExitContext::OnResume
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void ExitContext::OnResume()
+{
+}
+
+
+//==============================================================================
+// ExitContext::OnHandleEvent
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void ExitContext::OnHandleEvent( EventEnum id, void* pEventData )
+{
+}
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// ExitContext::ExitContext
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================//
+ExitContext::ExitContext()
+{
+}
+
+
+//==============================================================================
+// ExitContext::~ExitContext
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================//
+ExitContext::~ExitContext()
+{
+ spInstance = NULL;
+}
+
diff --git a/game/code/contexts/exitcontext.h b/game/code/contexts/exitcontext.h
new file mode 100644
index 0000000..425196b
--- /dev/null
+++ b/game/code/contexts/exitcontext.h
@@ -0,0 +1,69 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: exitcontext.h
+//
+// Description:
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef EXITCONTEXT_H
+#define EXITCONTEXT_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/context.h> // is-a Context
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis:
+//
+//=============================================================================
+class ExitContext : public Context
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static ExitContext* GetInstance();
+
+ protected:
+
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+ virtual void OnHandleEvent( EventEnum id, void* pEventData );
+
+ private:
+
+ // constructor and destructor are protected to force singleton implementation
+ ExitContext();
+ virtual ~ExitContext();
+
+ // Declared but not defined to prevent copying and assignment.
+ ExitContext( const ExitContext& );
+ ExitContext& operator=( const ExitContext& );
+
+ // Pointer to the one and only instance of this singleton.
+ static ExitContext* spInstance;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline ExitContext* GetExitContext() { return( ExitContext::GetInstance() ); }
+
+
+#endif
diff --git a/game/code/contexts/frontendcontext.cpp b/game/code/contexts/frontendcontext.cpp
new file mode 100644
index 0000000..3e3ca17
--- /dev/null
+++ b/game/code/contexts/frontendcontext.cpp
@@ -0,0 +1,326 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement FrontEndContext
+//
+// History: 21/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <cheats/cheatinputsystem.h>
+#include <contexts/frontendcontext.h>
+#include <contexts/contextenum.h>
+#include <memory/leakdetection.h>
+#include <memory/srrmemory.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/frontend/guimanagerfrontend.h>
+#include <mission/rewards/rewardsmanager.h>
+
+#include <sound/soundmanager.h>
+
+#include <input/inputmanager.h>
+
+#include <data/gamedatamanager.h>
+
+#include <worldsim/coins/coinmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+FrontEndContext* FrontEndContext::spInstance = NULL;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// FrontEndContext::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the FrontEndContext singleton.
+// - Creates the FrontEndContext if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the FrontEndContext.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+FrontEndContext* FrontEndContext::GetInstance()
+{
+ if( spInstance == NULL )
+ {
+ spInstance = new(GMA_PERSISTENT) FrontEndContext;
+ rAssert( spInstance );
+ }
+
+ return spInstance;
+}
+
+//==============================================================================
+// FrontEndContext::FrontEndContext
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+FrontEndContext::FrontEndContext()
+{
+}
+
+//==============================================================================
+// FrontEndContext::~FrontEndContext
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+FrontEndContext::~FrontEndContext()
+{
+}
+
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// FrontEndContext::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum previousContext )
+//
+// Return: void
+//
+//=============================================================================
+void FrontEndContext::OnStart( ContextEnum previousContext )
+{
+ SetMemoryIdentification( "FEContext" );
+ MEMTRACK_PUSH_FLAG( "Front End" );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+
+ if( previousContext != CONTEXT_BOOTUP )
+ {
+ HeapMgr()->PrepareHeapsFeCleanup();
+ LEAK_DETECTION_CHECKPOINT();
+ HeapMgr()->PrepareHeapsFeSetup();
+
+ // reset all cheats
+ //
+ GetCheatInputSystem()->Reset();
+
+ // unregister controller ID from all players
+ //
+ GetInputManager()->UnregisterAllControllerID();
+
+ // tell GUI system to run backend during loading
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_RUN_BACKEND );
+
+ // initialize GUI frontend mode (and load resources)
+ GetGuiSystem()->HandleMessage( GUI_MSG_INIT_FRONTEND );
+
+ GetLoadingManager()->AddCallback( this );
+ }
+ else
+ {
+ // Start the front end.
+ LEAK_DETECTION_CHECKPOINT();
+ this->StartFrontEnd( CGuiWindow::GUI_SCREEN_ID_SPLASH );
+ }
+
+ GetInputManager()->ToggleRumble( false );
+
+ GetGuiSystem()->RegisterUserInputHandlers();
+
+ GetInputManager()->SetGameState( Input::ACTIVE_FRONTEND );
+}
+
+//=============================================================================
+// FrontEndContext::OnStop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum nextContext )
+//
+// Return: void
+//
+//=============================================================================
+void FrontEndContext::OnStop( ContextEnum nextContext )
+{
+ GetGuiSystem()->UnregisterUserInputHandlers();
+
+ // release GUI frontend
+ GetGuiSystem()->HandleMessage( GUI_MSG_RELEASE_FRONTEND );
+
+ //
+ // Notify the sound system that the front end is stopping
+ //
+ GetSoundManager()->OnFrontEndEnd();
+
+ GetInputManager()->SetGameState( Input::ACTIVE_ALL );
+
+ HeapMgr()->PopHeap(GMA_LEVEL_FE);
+
+ MEMTRACK_POP_FLAG( "" );
+
+ SetMemoryIdentification( "FEContext Finished" );
+}
+
+//=============================================================================
+// FrontEndContext::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void FrontEndContext::OnUpdate( unsigned int elapsedTime )
+{
+ // update game data manager
+ //
+ GetGameDataManager()->Update( elapsedTime );
+
+ // update GUI system
+ //
+ GetGuiSystem()->Update( elapsedTime );
+
+ //Chuck: adding this so that the rewards manager reflects changes found in the charactersheet.
+ GetRewardsManager()->SynchWithCharacterSheet();
+}
+
+//=============================================================================
+// FrontEndContext::OnSuspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FrontEndContext::OnSuspend()
+{
+}
+
+//=============================================================================
+// FrontEndContext::OnResume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FrontEndContext::OnResume()
+{
+}
+
+//=============================================================================
+// FrontEndContext::OnHandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void FrontEndContext::OnHandleEvent( EventEnum id, void* pEventData )
+{
+}
+
+//=============================================================================
+// FrontEndContext::OnProcessRequestsComplete
+//=============================================================================
+// Description: Called when startup loading is done
+//
+// Parameters: pUserData - unused
+//
+// Return: void
+//
+//=============================================================================
+void FrontEndContext::OnProcessRequestsComplete( void* pUserData )
+{
+ // tell GUI system to quit backend
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_QUIT_BACKEND );
+
+#ifndef RAD_DEMO
+ if( GetGuiSystem()->IsSplashScreenFinished() )
+ {
+ if( GetGuiSystem()->IsShowCreditsUponReturnToFE() )
+ {
+ this->StartFrontEnd( CGuiWindow::GUI_SCREEN_ID_VIEW_CREDITS );
+ }
+ else
+ {
+ this->StartFrontEnd( CGuiWindow::GUI_SCREEN_ID_MAIN_MENU );
+ }
+ }
+ else
+#endif
+ {
+ this->StartFrontEnd( CGuiWindow::GUI_SCREEN_ID_SPLASH );
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// FrontEndContext::StartFrontEnd
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FrontEndContext::StartFrontEnd( unsigned int initialScreen )
+{
+ // Start up GUI frontend manager
+ GetGuiSystem()->HandleMessage( GUI_MSG_RUN_FRONTEND, initialScreen );
+
+ //
+ // Notify the sound system that the front end is starting
+ //
+ GetSoundManager()->OnFrontEndStart();
+}
+
diff --git a/game/code/contexts/frontendcontext.h b/game/code/contexts/frontendcontext.h
new file mode 100644
index 0000000..5395723
--- /dev/null
+++ b/game/code/contexts/frontendcontext.h
@@ -0,0 +1,75 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: FrontEndcontext.h
+//
+// Description:
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef FRONTENDCONTEXT_H
+#define FRONTENDCONTEXT_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/context.h> // is-a Context
+
+#include <loading/loadingmanager.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis:
+//
+//=============================================================================
+class FrontEndContext : public Context,
+ public LoadingManager::ProcessRequestsCallback
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static FrontEndContext* GetInstance();
+
+ protected:
+
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+ virtual void OnHandleEvent( EventEnum id, void* pEventData );
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+ private:
+
+ void StartFrontEnd( unsigned int initialScreen );
+
+ // constructor and destructor are protected to force singleton implementation
+ FrontEndContext();
+ virtual ~FrontEndContext();
+
+ // Declared but not defined to prevent copying and assignment.
+ FrontEndContext( const FrontEndContext& );
+ FrontEndContext& operator=( const FrontEndContext& );
+
+ // Pointer to the one and only instance of this singleton.
+ static FrontEndContext* spInstance;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline FrontEndContext* GetFrontEndContext() { return( FrontEndContext::GetInstance() ); }
+
+
+#endif // FRONTENDCONTEXT_H
diff --git a/game/code/contexts/gameplay/allgameplay.cpp b/game/code/contexts/gameplay/allgameplay.cpp
new file mode 100644
index 0000000..7b9a0b5
--- /dev/null
+++ b/game/code/contexts/gameplay/allgameplay.cpp
@@ -0,0 +1,2 @@
+#include <contexts/gameplay/loadinggameplaycontext.cpp>
+#include <contexts/gameplay/gameplaycontext.cpp>
diff --git a/game/code/contexts/gameplay/gameplaycontext.cpp b/game/code/contexts/gameplay/gameplaycontext.cpp
new file mode 100644
index 0000000..ccee4e0
--- /dev/null
+++ b/game/code/contexts/gameplay/gameplaycontext.cpp
@@ -0,0 +1,616 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement GameplayContext
+//
+// History: 21/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radtime.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <ai/vehicle/vehicleairender.h>
+
+#include <camera/animatedcam.h>
+#include <camera/debugcam.h>
+#include <camera/followcam.h>
+#include <camera/relativeanimatedcam.h>
+#include <camera/chasecam.h>
+#include <camera/bumpercam.h>
+#include <camera/kullcam.h>
+#include <camera/trackercam.h>
+#include <camera/walkercam.h>
+#include <camera/railcam.h>
+#include <camera/wrecklesscam.h>
+#include <camera/supercammanager.h>
+#include <camera/conversationcam.h>
+#include <camera/reversecam.h>
+#include <camera/snapshotcam.h>
+#ifdef RAD_WIN32
+#include <camera/pccam.h>
+#endif
+
+//#include <camera/firstpersoncam.h>
+
+#include <contexts/gameplay/gameplaycontext.h>
+#include <contexts/contextenum.h>
+#include <events/eventmanager.h>
+#include <gameflow/gameflow.h>
+
+#include <data/gamedatamanager.h>
+
+#include <interiors/interiormanager.h>
+#include <memory/leakdetection.h>
+#include <memory/srrmemory.h>
+
+#include <meta/triggervolumetracker.h>
+
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+#include <presentation/presentation.h>
+#include <presentation/gui/guisystem.h>
+
+#include <render/rendermanager/rendermanager.h>
+#include <render/RenderManager/RenderLayer.h>
+
+#include <worldsim/character/footprint/footprintmanager.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/skidmarks/skidmarkmanager.h>
+#include <worldsim/coins/sparkle.h>
+#include <worldsim/hitnrunmanager.h>
+#include <ai/actionbuttonmanager.h>
+#include <ai/actor/actormanager.h>
+
+
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/ped/pedestrianmanager.h>
+
+#include <render/breakables/breakablesmanager.h>
+#include <render/particles/particlemanager.h>
+#include <render/animentitydsgmanager/animentitydsgmanager.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <worldsim/skidmarks/skidmarkgenerator.h>
+
+#include <sound/soundmanager.h>
+
+#include <roads/roadmanager.h>
+
+#include <input/inputmanager.h>
+#include <p3d/light.hpp>
+#include <p3d/view.hpp>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+GameplayContext* GameplayContext::spInstance = NULL;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// GameplayContext::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the GameplayContext singleton.
+// - Creates the GameplayContext if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the GameplayContext.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+GameplayContext* GameplayContext::GetInstance()
+{
+ if( spInstance == NULL )
+ {
+ spInstance = new(GMA_PERSISTENT) GameplayContext;
+ rAssert( spInstance );
+ }
+
+ return spInstance;
+}
+
+//==============================================================================
+// GameplayContext::GameplayContext
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GameplayContext::GameplayContext() :
+ m_PausedAllButPresentation( false ),
+ mSlowMoHack( false )
+{
+#ifdef DEBUGWATCH
+ radDbgWatchAddUnsignedInt( &mDebugPhysTiming, "Debug Phys micros", "GameplayContext", NULL, NULL );
+ radDbgWatchAddUnsignedInt( &mDebugTimeDelta, "Debug dT micros", "GameplayContext", NULL, NULL );
+ radDbgWatchAddUnsignedInt( &mDebugOnUpdateDT, "Gameplay dT micros", "GameplayContext", NULL, NULL );
+ radDbgWatchAddBoolean(&mSlowMoHack, "Slow Mo Hack", "GameplayContext", NULL, NULL);
+#endif
+}
+
+//==============================================================================
+// GameplayContext::~GameplayContext
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GameplayContext::~GameplayContext()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete(&mDebugPhysTiming);
+ radDbgWatchDelete(&mDebugTimeDelta);
+ radDbgWatchDelete(&mDebugOnUpdateDT);
+
+ radDbgWatchDelete(&mSlowMoHack);
+#endif
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// GameplayContext::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum previousContext )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayContext::OnStart( ContextEnum previousContext )
+{
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnStart( previousContext );
+
+ MEMTRACK_PUSH_FLAG( "Gameplay" );
+ // do gameplay initializations only if previous context was loading context
+ if( previousContext == CONTEXT_LOADING_GAMEPLAY )
+ {
+ // RenderManager
+ //
+ RenderManager* rm = GetRenderManager();
+
+ RenderLayer* rl = rm->mpLayer( RenderEnums::LevelSlot );
+ rAssert( rl );
+
+#ifdef DEBUGWATCH
+ // bootstrap vehicleai renderer
+ VehicleAIRender::GetVehicleAIRender();
+#endif
+
+ // Set up cameras
+ //
+ unsigned int iNumPlayers = GetGameplayManager()->GetNumPlayers();
+
+ rl->SetNumViews( iNumPlayers );
+ rl->SetUpViewCam();
+
+ p3d::inventory->SelectSection("Default");
+ tLightGroup* sun = p3d::find<tLightGroup>("sun");
+ rAssert( sun );
+ rm->SetLevelLayerLights( sun );
+
+ float aspect = p3d::display->IsWidescreen() ? (16.0f / 9.0f) : (4.0f / 3.0f);
+
+ if( iNumPlayers == 2 )
+ {
+ aspect = 4.0f / 1.5f;
+ }
+
+ for( unsigned int view = 0; view < iNumPlayers; view++ )
+ {
+
+ tPointCamera* cam = static_cast<tPointCamera*>( rl->pCam( view ) );
+ rAssert( dynamic_cast<tPointCamera*> ( cam ) != NULL );
+ rAssert( cam );
+
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC( view );
+ rAssert( scc );
+ scc->SetCamera( cam );
+
+ FollowCam* fc = new FollowCam( FollowCam::FOLLOW_NEAR );
+ fc->SetAspect( aspect );
+ fc->CopyToData();
+ scc->RegisterSuperCam( fc );
+
+ fc = new FollowCam( FollowCam::FOLLOW_FAR );
+ fc->SetAspect( aspect );
+ fc->CopyToData();
+ scc->RegisterSuperCam( fc );
+
+ SuperCam* sc = new BumperCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+ sc = new ChaseCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+ sc = new KullCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+ sc = new DebugCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+ sc = new TrackerCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+ sc = new WalkerCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+ sc = new ComedyCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+ sc = new WrecklessCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+ sc = new ConversationCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+ sc = new ReverseCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+ sc = new AnimatedCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+ sc = new RelativeAnimatedCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+// sc = new FirstPersonCam();
+// sc->SetAspect( aspect );
+// scc->RegisterSuperCam( sc );
+#ifdef RAD_WIN32
+ sc = new PCCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+#endif
+#if !defined(FINAL) && defined(RAD_XBOX)
+ sc = new SnapshotCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+#endif
+ }
+
+ // Boot strap the AvatarManager.
+ //
+ GetAvatarManager( )->EnterGame( );
+ GetActionButtonManager( )->EnterGame( );
+
+ TrafficManager::GetInstance()->Init();
+ PedestrianManager::GetInstance()->Init();
+
+ // Prepare the manager to start gameplay
+ //
+ //GetGameplayManager()->Reset();
+
+#ifdef RAD_DEMO
+ GetGameplayManager()->ResetIdleTime();
+#endif
+
+ // Start up GUI in-game manager
+ GetGuiSystem()->HandleMessage( GUI_MSG_RUN_INGAME );
+
+ // register GUI user input handler for active players only
+ //
+ int activeControllerIDs = 0;
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( i );
+ if( controllerID != -1 )
+ {
+ activeControllerIDs |= (1 << controllerID);
+ }
+ }
+
+ GetGuiSystem()->RegisterUserInputHandlers( activeControllerIDs );
+
+ // Tell GameData Manager that game has been started
+ //
+ GetGameDataManager()->SetGameLoaded();
+
+ //Set up the sorting of the intersections and stuff.
+ RoadManager::GetInstance()->CreateRoadNetwork();
+
+ //Find skid mark shaders in the inventory and set proper values
+ SkidMarkGenerator::InitShaders();
+
+ GetSkidmarkManager()->Init();
+
+ //
+ // Notify the sound system of gameplay start. This has been moved after
+ // the GUI startup above, since that leads to a mission reset, which causes
+ // character position in or out of the car to be decided, which the sound
+ // system uses to determine which sounds to start playing.
+ //
+ SoundManager::GetInstance()->OnGameplayStart();
+
+ GetInteriorManager()->OnGameplayStart();
+
+ GetHitnRunManager()->ResetState();
+
+ //tick character manager once, to get state system and choreo started
+ GetCharacterManager()->PreSimUpdate( 0.0001f );
+ GetCharacterManager()->PreSubstepUpdate( 0.0001f );
+ GetCharacterManager()->Update( 0.0001f );
+ GetCharacterManager()->PostSubstepUpdate( 0.0001f );
+ GetCharacterManager()->PostSimUpdate( 0.0001f );
+
+ }
+
+ GetPresentationManager()->OnGameplayStart();
+
+ HeapMgr()->ResetArtStats();
+ GetEventManager()->TriggerEvent( EVENT_LEVEL_START );
+
+ if( previousContext != CONTEXT_PAUSE )
+ {
+ AnimatedCam::CheckPendingCameraSwitch();
+ }
+
+ for( int view = 0; view < GetGameplayManager()->GetNumPlayers(); view++ )
+ {
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC( view );
+ scc->SetIsInitialCamera(true);
+ }
+ GetGameplayManager()->Update( 0 );
+ GetTriggerVolumeTracker()->Update( 0 );
+}
+
+//=============================================================================
+// GameplayContext::OnStop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum nextContext )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayContext::OnStop( ContextEnum nextContext )
+{
+ GetPresentationManager()->OnGameplayStop();
+
+ // turn off controller rumble, to comply w/ TCR/TRC standards
+ //
+ GetInputManager()->ToggleRumble( false );
+
+
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnStop( nextContext );
+}
+
+//=============================================================================
+// GameplayContext::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayContext::OnUpdate( unsigned int elapsedTime )
+{
+#ifdef DEBUGWATCH
+ mDebugOnUpdateDT = radTimeGetMicroseconds();
+#endif
+
+#ifndef FINAL
+ if( CommandLineOptions::Get( CLO_PRINT_FRAMERATE ) )
+ {
+ //
+ // Merasure the time taken to render frames 100-500
+ //
+ const unsigned int startFrame = 200;
+ const unsigned int endFrame = 600;
+ static unsigned int count = 0;
+ ++count;
+ static unsigned int totalTime = 0;
+ totalTime += elapsedTime;
+
+ if( count == startFrame )
+ {
+ totalTime = 0;
+ }
+ if( count == endFrame )
+ {
+ const unsigned int frames = endFrame - startFrame;
+ float msPerFrame = totalTime / static_cast< float >( frames );
+ rReleasePrintf( "====================================\n" );
+ rReleasePrintf( "%d frames rendered in %dms\n", frames, totalTime );
+ rReleasePrintf( "%fms per frame\n", msPerFrame );
+ rReleasePrintf( "====================================\n" );
+ }
+ }
+
+ static int frameCount = 60;
+ frameCount--;
+ if( frameCount == 0 )
+ {
+ HeapMgr()->ResetArtStats();
+ }
+#endif
+
+ // hack for yousuf
+ if( mSlowMoHack )
+ {
+ //elapsedTime = 1;
+ elapsedTime = 2;
+ }
+
+ if( !m_PausedAllButPresentation )
+ {
+ float timeins = (float)(elapsedTime) / 1000.0f;
+
+ BEGIN_PROFILE("GameplayManager");
+ if ( m_state != S_SUSPENDED )
+ {
+ GetGameplayManager()->Update( elapsedTime );
+ }
+ END_PROFILE("GameplayManager");
+
+
+ GetAvatarManager()->Update( timeins );
+
+ GetFootprintManager()->Update( elapsedTime );
+
+ GetCharacterManager()->GarbageCollect( );
+
+ BEGIN_PROFILE( "Update Particles" );
+
+ GetParticleManager()->Update( elapsedTime );
+ GetBreakablesManager()->Update( elapsedTime );
+ GetAnimEntityDSGManager()->Update( elapsedTime );
+
+ END_PROFILE( "Update Particles" );
+
+ #ifdef DEBUGWATCH
+ unsigned int t0 = radTimeGetMicroseconds();
+ #endif
+
+ BEGIN_PROFILE("WorldPhysics");
+ GetWorldPhysicsManager()->Update(elapsedTime);
+ END_PROFILE("WorldPhysics");
+
+ // ordering is important. Unless other parts of code change, we must call
+ // this before WorldPhysManager::Update() because PedestrianManager
+ // sets the flags for all characters to be updated in WorldPhys Update
+ PedestrianManager::GetInstance()->Update( elapsedTime );
+
+ #ifdef DEBUGWATCH
+ mDebugTimeDelta = elapsedTime;
+ mDebugPhysTiming = radTimeGetMicroseconds()-t0 ;
+ #endif
+
+ BEGIN_PROFILE("TriggerVolumeTracker");
+ GetTriggerVolumeTracker()->Update( elapsedTime );
+ END_PROFILE("TriggerVolumeTracker");
+
+ BEGIN_PROFILE("TrafficManager");
+ TrafficManager::GetInstance()->Update( elapsedTime );
+ END_PROFILE("TrafficManager");
+
+ BEGIN_PROFILE("ActorManager");
+ ActorManager::GetInstance()->Update( elapsedTime );
+ END_PROFILE("ActorManager");
+
+ BEGIN_PROFILE("InteriorManager");
+ GetInteriorManager()->Update( elapsedTime );
+ END_PROFILE("InteriorManager");
+
+ BEGIN_PROFILE("SkidmarkManager");
+ GetSkidmarkManager()->Update( elapsedTime );
+ END_PROFILE("SkidmarkManager");
+
+ BEGIN_PROFILE( "CoinManager" );
+ GetCoinManager()->Update( elapsedTime );
+ GetSparkleManager()->Update( elapsedTime );
+ END_PROFILE( "CoinManager" );
+
+ BEGIN_PROFILE( "HitnRunManager" );
+ GetHitnRunManager()->Update( elapsedTime );
+ END_PROFILE( "HitnRunManager" );
+ }
+
+#ifdef RAD_DEMO
+ GetGameplayManager()->UpdateIdleTime( elapsedTime );
+#endif
+
+ BEGIN_PROFILE("PresentationManager");
+ GetPresentationManager()->Update( elapsedTime );
+ END_PROFILE("PresentationManager");
+
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnUpdate( elapsedTime );
+#ifdef DEBUGWATCH
+ mDebugOnUpdateDT = radTimeGetMicroseconds()-mDebugOnUpdateDT;
+#endif
+}
+
+//=============================================================================
+// GameplayContext::OnSuspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayContext::OnSuspend()
+{
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnSuspend();
+}
+
+//=============================================================================
+// GameplayContext::OnResume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayContext::OnResume()
+{
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnResume();
+}
+
+//=============================================================================
+// GameplayContext::PauseAllButPresentation
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayContext::PauseAllButPresentation( const bool pause )
+{
+ m_PausedAllButPresentation = pause;
+}
+
diff --git a/game/code/contexts/gameplay/gameplaycontext.h b/game/code/contexts/gameplay/gameplaycontext.h
new file mode 100644
index 0000000..01d788c
--- /dev/null
+++ b/game/code/contexts/gameplay/gameplaycontext.h
@@ -0,0 +1,77 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: Gameplaycontext.h
+//
+// Description:
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef GAMEPLAYCONTEXT_H
+#define GAMEPLAYCONTEXT_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/playingcontext.h> // is-a PlayingContext
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis:
+//
+//=============================================================================
+class GameplayContext : public PlayingContext
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static GameplayContext* GetInstance();
+ void PauseAllButPresentation( const bool pause );
+ bool IsPaused() { return m_PausedAllButPresentation; }
+
+ protected:
+
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+ private:
+
+ // constructor and destructor are protected to force singleton implementation
+ GameplayContext();
+ virtual ~GameplayContext();
+
+ // Declared but not defined to prevent copying and assignment.
+ GameplayContext( const GameplayContext& );
+ GameplayContext& operator=( const GameplayContext& );
+
+ // Pointer to the one and only instance of this singleton.
+ static GameplayContext* spInstance;
+
+#ifdef DEBUGWATCH
+ unsigned int mDebugPhysTiming, mDebugTimeDelta, mDebugOnUpdateDT;
+#endif
+ bool m_PausedAllButPresentation;
+
+ bool mSlowMoHack;
+
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline GameplayContext* GetGameplayContext() { return( GameplayContext::GetInstance() ); }
+
+
+#endif // GAMEPLAYCONTEXT_H
diff --git a/game/code/contexts/gameplay/loadinggameplaycontext.cpp b/game/code/contexts/gameplay/loadinggameplaycontext.cpp
new file mode 100644
index 0000000..1bb15c2
--- /dev/null
+++ b/game/code/contexts/gameplay/loadinggameplaycontext.cpp
@@ -0,0 +1,250 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement LoadingGameplayContext
+//
+// History: 21/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/gameplay/loadinggameplaycontext.h>
+
+#include <interiors/interiormanager.h>
+#include <loading/loadingmanager.h>
+#include <memory/leakdetection.h>
+#include <mission/gameplaymanager.h>
+#include <presentation/gui/guisystem.h>
+#include <worldsim/parkedcars/parkedcarmanager.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <sound/soundmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+LoadingGameplayContext* LoadingGameplayContext::spInstance = NULL;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// LoadingGameplayContext::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the LoadingGameplayContext singleton.
+// - Creates the LoadingGameplayContext if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the LoadingGameplayContext.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+LoadingGameplayContext* LoadingGameplayContext::GetInstance()
+{
+ if( spInstance == NULL )
+ {
+ spInstance = new(GMA_PERSISTENT) LoadingGameplayContext;
+ rAssert( spInstance );
+ }
+
+ return spInstance;
+}
+
+//==============================================================================
+// LoadingGameplayContext::LoadingGameplayContext
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LoadingGameplayContext::LoadingGameplayContext()
+{
+}
+
+//==============================================================================
+// LoadingGameplayContext::~LoadingGameplayContext
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LoadingGameplayContext::~LoadingGameplayContext()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// LoadingGameplayContext::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum previousContext )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingGameplayContext::OnStart( ContextEnum previousContext )
+{
+ GetGameplayManager()->mIsDemo = false;
+
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnStart( previousContext );
+
+/*****************************************************************************
+ * Start inserting stuff below ...
+ *****************************************************************************/
+
+ // NOTE:
+ // Assumes we never start a level in the car
+ GetVehicleCentral()->ActivateVehicleTriggers( true );
+
+ GetPCM(); //Start the parked car manager
+
+ TrafficManager::GetInstance()->InitDefaultModelGroups();
+
+ // initialize GUI in-game mode (and load resources)
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_INIT_INGAME );
+
+ GetLoadingManager()->AddCallback( this );
+}
+
+//=============================================================================
+// LoadingGameplayContext::OnStop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum nextContext )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingGameplayContext::OnStop( ContextEnum nextContext )
+{
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnStop( nextContext );
+}
+
+//=============================================================================
+// LoadingGameplayContext::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingGameplayContext::OnUpdate( unsigned int elapsedTime )
+{
+ GetGameplayManager()->Update( elapsedTime );
+
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnUpdate( elapsedTime );
+}
+
+//=============================================================================
+// LoadingGameplayContext::OnSuspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoadingGameplayContext::OnSuspend()
+{
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnSuspend();
+}
+
+//=============================================================================
+// LoadingGameplayContext::OnResume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoadingGameplayContext::OnResume()
+{
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnResume();
+}
+
+//=============================================================================
+// LoadingGameplayContext::PrepareNewHeaps
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoadingGameplayContext::PrepareNewHeaps()
+{
+ HeapMgr()->PrepareHeapsInGame();
+}
+
+//=============================================================================
+// LoadingGameplayContext::OnProcessRequestsComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingGameplayContext::OnProcessRequestsComplete( void* pUserData )
+{
+ GetGameplayManager()->LevelLoaded();
+
+ //
+ // Queue the loading for level sounds
+ //
+ GetSoundManager()->QueueLevelSoundLoads();
+
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnProcessRequestsComplete( pUserData );
+}
diff --git a/game/code/contexts/gameplay/loadinggameplaycontext.h b/game/code/contexts/gameplay/loadinggameplaycontext.h
new file mode 100644
index 0000000..6e6d5d9
--- /dev/null
+++ b/game/code/contexts/gameplay/loadinggameplaycontext.h
@@ -0,0 +1,72 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: LoadingGameplayContext.h
+//
+// Description:
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef LOADINGGAMEPLAYCONTEXT_H
+#define LOADINGGAMEPLAYCONTEXT_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/loadingcontext.h> // is-a LoadingContext
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis:
+//
+//=============================================================================
+class LoadingGameplayContext : public LoadingContext
+{
+ public:
+ // Static Methods for accessing this singleton.
+ static LoadingGameplayContext* GetInstance();
+
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+ protected:
+
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+ virtual void PrepareNewHeaps();
+
+ private:
+
+ // constructor and destructor are protected to force singleton implementation
+ LoadingGameplayContext();
+ virtual ~LoadingGameplayContext();
+
+ // Declared but not defined to prevent copying and assignment.
+ LoadingGameplayContext( const LoadingGameplayContext& );
+ LoadingGameplayContext& operator=( const LoadingGameplayContext& );
+
+ // Pointer to the one and only instance of this singleton.
+ static LoadingGameplayContext* spInstance;
+
+ bool mUnQueuedLoadRequests;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline LoadingGameplayContext* GetLoadingGameplayContext() { return( LoadingGameplayContext::GetInstance() ); }
+
+
+#endif // LOADINGGAMEPLAYCONTEXT_H
diff --git a/game/code/contexts/loadingcontext.cpp b/game/code/contexts/loadingcontext.cpp
new file mode 100644
index 0000000..fddf2ef
--- /dev/null
+++ b/game/code/contexts/loadingcontext.cpp
@@ -0,0 +1,270 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement LoadingContext
+//
+// History: 21/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/shadow.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/loadingcontext.h>
+#include <camera/animatedcam.h>
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <presentation/presentation.h>
+#include <presentation/gui/guisystem.h>
+#include <render/rendermanager/rendermanager.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/coins/sparkle.h>
+#include <worldsim/hitnrunmanager.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/vehiclecentral.h>
+
+#include <mission/animatedicon.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+#ifndef FINAL
+static unsigned int g_Timer;
+#endif
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// LoadingContext::LoadingContext
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LoadingContext::LoadingContext()
+{
+}
+
+//==============================================================================
+// LoadingContext::~LoadingContext
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LoadingContext::~LoadingContext()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// LoadingContext::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum previousContext )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingContext::OnStart( ContextEnum previousContext )
+{
+ SetMemoryIdentification( "LoadingContext" );
+#ifndef FINAL
+ if( CommandLineOptions::Get( CLO_PRINT_LOAD_TIME ) )
+ {
+ g_Timer = radTimeGetMicroseconds();
+ }
+#endif
+
+ MEMTRACK_PUSH_FLAG( "Loading Context" );
+
+ this->PrepareNewHeaps();
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ // Note: *** Do NOT add any calls before the following GUI system call
+ // that will cause an AddRequest to the loading manager.
+ // The GUI system should have first dibs at queueing whatever it
+ // wants loaded for the loading screen.
+ //
+
+ AnimatedIcon::InitAnimatedIcons( HeapMgr()->GetCurrentHeap() );
+
+ // no controller input while loading
+ //
+ GetInputManager()->SetGameState( Input::ACTIVE_NONE );
+
+ // tell GUI system to run backend during loading
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_RUN_BACKEND );
+
+
+ GetCoinManager()->Init();
+ GetSparkleManager()->Init();
+
+ GetHitnRunManager()->Init();
+
+ // This is here since it needs to be in all modes.
+ //
+ const bool shutdown = false;
+ GetSuperCamManager()->Init( shutdown );
+
+ GetVehicleCentral()->PreLoad();
+
+ GetRenderManager()->LoadAllNeededData();
+
+ PedestrianManager::GetInstance()->InitDefaultModelGroups();
+
+ GetPresentationManager()->Initialize();
+
+ GetGameplayManager()->Initialize();
+ GetGameplayManager()->LoadLevelData();
+}
+
+//=============================================================================
+// LoadingContext::OnStop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum nextContext )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingContext::OnStop( ContextEnum nextContext )
+{
+#ifdef RAD_WIN32
+ if( nextContext == CONTEXT_EXIT )
+ {
+ GetLoadingManager()->CancelPendingRequests();
+ p3d::loadManager->CancelAll();
+ }
+#endif
+
+ RoadManager::GetInstance()->DumpRoadSegmentDataMemory();
+
+ // tell GUI system to quit backend
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_QUIT_BACKEND );
+
+ GetInputManager()->SetGameState( Input::ACTIVE_ALL );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+#ifndef FINAL
+ if( CommandLineOptions::Get( CLO_PRINT_LOAD_TIME ) )
+ {
+ unsigned int stopTime = radTimeGetMicroseconds();
+ unsigned int time = stopTime - g_Timer;
+ float timeInSeconds = time / 1000000.0f;
+ rReleasePrintf( "Loading Time (s) = %f\n", timeInSeconds );
+ }
+#endif
+ AnimatedCam::TriggerMissionStartCamera();
+ MEMTRACK_POP_FLAG( "" );
+ SetMemoryIdentification( "LoadingContext Finished" );
+
+ GetInputManager()->EnableReset( true );
+}
+
+//=============================================================================
+// LoadingContext::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingContext::OnUpdate( unsigned int elapsedTime )
+{
+ // update GUI system
+ //
+ GetGuiSystem()->Update( elapsedTime );
+}
+
+//=============================================================================
+// LoadingContext::OnSuspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoadingContext::OnSuspend()
+{
+}
+
+//=============================================================================
+// LoadingContext::OnResume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoadingContext::OnResume()
+{
+}
+
+//=============================================================================
+// LoadingContext::OnHandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingContext::OnHandleEvent( EventEnum id, void* pEventData )
+{
+}
+
+//=============================================================================
+// LoadingContext::OnProcessRequestsComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingContext::OnProcessRequestsComplete( void* pUserData )
+{
+ GetRenderManager()->DoPostLevelLoad();
+}
+
diff --git a/game/code/contexts/loadingcontext.h b/game/code/contexts/loadingcontext.h
new file mode 100644
index 0000000..9c963e9
--- /dev/null
+++ b/game/code/contexts/loadingcontext.h
@@ -0,0 +1,66 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: Loadingcontext.h
+//
+// Description:
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef LOADINGCONTEXT_H
+#define LOADINGCONTEXT_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/context.h> // is-a Context
+#include <loading/loadingmanager.h> // also is-a ProcessRequests thingy
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis:
+//
+//=============================================================================
+class LoadingContext : public Context,
+ public LoadingManager::ProcessRequestsCallback
+{
+ public:
+ virtual void OnHandleEvent( EventEnum id, void* pEventData );
+
+ //
+ // LoadingManager::ProcessRequestsCallback
+ //
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+ protected:
+ // constructor and destructor are protected to force singleton implementation
+ LoadingContext();
+ virtual ~LoadingContext();
+
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+ virtual void PrepareNewHeaps() = 0;
+
+ private:
+ // Declared but not defined to prevent copying and assignment.
+ LoadingContext( const LoadingContext& );
+ LoadingContext& operator=( const LoadingContext& );
+
+};
+
+#endif // LOADINGCONTEXT_H
diff --git a/game/code/contexts/pausecontext.cpp b/game/code/contexts/pausecontext.cpp
new file mode 100644
index 0000000..b001c5f
--- /dev/null
+++ b/game/code/contexts/pausecontext.cpp
@@ -0,0 +1,575 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement PauseContext
+//
+// History: 21/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/view.hpp>
+#include <p3d/shadow.hpp>
+
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radtime.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <ai/vehicle/vehicleairender.h>
+
+#include <contexts/pausecontext.h>
+#include <contexts/contextenum.h>
+#include <memory/leakdetection.h>
+#include <memory/srrmemory.h>
+#include <worldsim/worldobject.h>
+
+#include <camera/supercammanager.h>
+#include <camera/animatedcam.h>
+#include <interiors/interiormanager.h>
+#include <meta/triggervolumetracker.h>
+#include <mission/gameplaymanager.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/utility/hudmap.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/RenderLayer.h>
+#include <render/DSG/StatePropDSG.h>
+#include <sound/soundmanager.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/footprint/footprintmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/ped/pedestrianmanager.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/coins/sparkle.h>
+#include <worldsim/hitnrunmanager.h>
+#include <worldsim/parkedcars/parkedcarmanager.h>
+#include <ai/actionbuttonmanager.h>
+#include <render/breakables/breakablesmanager.h>
+#include <render/particles/ParticleManager.h>
+#include <worldsim/skidmarks/skidmarkgenerator.h>
+#include <worldsim/skidmarks/skidmarkmanager.h>
+#include <ai/actor/actormanager.h>
+#include <gameflow/gameflow.h>
+#include <roads/roadmanager.h>
+#include <render/Loaders/AnimDynaPhysLoader.h>
+#include <pedpaths/pathmanager.h>
+
+#include <input/inputmanager.h>
+
+#include <data/gamedatamanager.h>
+
+#include <mission/animatedicon.h>
+
+#include <render/animentitydsgmanager/animentitydsgmanager.h>
+#include <presentation/presentation.h>
+#include <presentation/tutorialmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+PauseContext* PauseContext::spInstance = NULL;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// PauseContext::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the PauseContext singleton.
+// - Creates the PauseContext if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the PauseContext.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+PauseContext* PauseContext::GetInstance()
+{
+ if( spInstance == NULL )
+ {
+ spInstance = new(GMA_PERSISTENT) PauseContext;
+ rAssert( spInstance );
+ }
+
+ return spInstance;
+}
+
+//==============================================================================
+// PauseContext::PauseContext
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PauseContext::PauseContext()
+: mOldState( Input::ACTIVE_NONE ),
+ m_quitGamePending( false ),
+ m_waitingForContextSwitch( false )
+{
+}
+
+//==============================================================================
+// PauseContext::~PauseContext
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PauseContext::~PauseContext()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// PauseContext::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum previousContext )
+//
+// Return: void
+//
+//=============================================================================
+void PauseContext::OnStart( ContextEnum previousContext )
+{
+ SetMemoryIdentification( "PauseContext" );
+ MEMTRACK_PUSH_FLAG( "Pause" );
+
+ mOldState = GetInputManager()->GetGameState();
+ if( mOldState == Input::ACTIVE_ANIM_CAM )
+ {
+ // deactivate anim cam state first, since the input manager
+ // won't let us set the game state to anything else prior
+ // to that
+ //
+ GetInputManager()->SetGameState( Input::DEACTIVE_ANIM_CAM );
+ SuperCam* sc = GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam();
+ AnimatedCam* ac = dynamic_cast<AnimatedCam*>(sc);
+ if(ac)
+ {
+ ac->Abort();
+ }
+ }
+ GetInputManager()->SetGameState( Input::ACTIVE_FRONTEND );
+
+ GetCoinManager()->ClearHUDCoins();
+
+ m_quitGamePending = false;
+}
+
+
+extern void OutputHandler (const char * pString );
+
+
+//=============================================================================
+// PauseContext::OnStop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum nextContext )
+//
+// Return: void
+//
+//=============================================================================
+void PauseContext::OnStop( ContextEnum nextContext )
+{
+ // do gameplay terminations only if next context is frontend context or exit.
+ if( nextContext == CONTEXT_FRONTEND ||
+ nextContext == CONTEXT_LOADING_GAMEPLAY ||
+ nextContext == CONTEXT_EXIT )
+ {
+ rReleasePrintf("PauseContext::OnStop Begins\n");
+
+ GetInputManager()->EnableReset( false );
+
+ RenderLayer* l = GetRenderManager()->mpLayer( RenderEnums::GUI );
+ rAssert( l );
+ l->Thaw();
+// GetGameFlow()->SetQuickStartLoading( false );
+ // STL reallocs, etc may result in actually allocating some memory during this process.
+ // As such, reroute everything to temp.
+ //
+ HeapMgr()->PushHeap (GMA_TEMP);
+
+ //
+ // This is called to prevent DMA of destroyed textures,
+ // because we don't swap buffers until the next frame.
+ //
+ p3d::pddi->DrawSync();
+
+ //::radThreadSleep (500);
+
+ //::rDebugSetOutputHandler (OutputHandler);
+
+ //rReleaseString ("DrawSync\n");
+
+ // Clear the shadow list
+ AnimDynaPhysLoader::ClearShadowList();
+
+ GetCoinManager()->Destroy();
+ GetSparkleManager()->Destroy();
+ GetHitnRunManager()->Destroy();
+
+ StatePropDSG::RemoveAllSharedtPoses();
+
+
+ GetInteriorManager()->OnGameplayEnd();
+
+ //rReleaseString ("GetInteriorManager()->OnGameplayEnd()\n");
+
+ const bool shutdown = true;
+ GetSuperCamManager()->Init( shutdown );
+
+ //rReleaseString ("GetSuperCamManager()->Init( shutdown )\n");
+
+ TriggerVolumeTracker::GetInstance()->Cleanup();
+
+ //rReleaseString ("TriggerVolumeTracker::GetInstance()->Cleanup()\n");
+
+ // Clean up lights!
+ //
+ RenderLayer* rl = GetRenderManager()->mpLayer( RenderEnums::LevelSlot );
+ rAssert( rl );
+ for( unsigned int i = 0; i < rl->GetNumViews(); i++ )
+ {
+ rl->pView(i)->RemoveAllLights ();
+ }
+
+ //rReleaseString ("Remove all lights from all views\n");
+
+ //rReleaseString ("GetAvatarManager()->Destroy()\n");
+ GetActorManager()->RemoveAllActors();
+ GetActorManager()->RemoveAllSpawnPoints();
+
+ GetPresentationManager()->Finalize();
+
+ GetGameplayManager()->Finalize();
+ //rReleaseString ("GetGameplayManager()->Finalize()\n");
+
+ // needs to be finalized after gameplay manager (Mission shudown needs avatars)
+ GetAvatarManager()->Destroy();
+
+ ParkedCarManager::DestroyInstance();
+ //rReleaseString ("ParkedCarManager::DestroyInstance()\n");
+
+ TrafficManager::DestroyInstance();
+ //rReleaseString ("TrafficManager::DestroyInstance()\n");
+
+ PedestrianManager::DestroyInstance();
+ //rReleaseString ("PedestrianManager::DestroyInstance()\n");
+
+ GetCharacterManager()->Destroy();
+ //rReleaseString ("GetCharacterManager()->Destroy()\n");
+
+ GetVehicleCentral()->Unload();
+
+ GetActionButtonManager( )->Destroy( );
+ //rReleaseString ("GetActionButtonManager( )->Destroy( )\n");
+
+
+ GetBreakablesManager()->FreeAllBreakables();
+ //rReleaseString ("GetBreakablesManager()->FreeAllBreakables()\n");
+
+ GetParticleManager()->ClearSystems();
+ //rReleaseString ("GetParticleManager()->ClearSystems()\n");
+
+
+ GetWorldPhysicsManager()->OnQuitLevel(); // just some cleanup checks...
+
+
+
+ SkidMarkGenerator::ReleaseShaders();
+ //rReleaseString ("SkidMarkGenerator::ReleaseShaders()\n");
+ GetSkidmarkManager()->Destroy();
+ GetFootprintManager()->FreeTextures();
+
+ GetRenderManager()->ClearLevelLayerLights();
+ rReleasePrintf("PauseContext::OnStop DumpAllLoadedData\n");
+ GetRenderManager()->DumpAllLoadedData();
+ //rReleaseString ("GetRenderManager()->DumpAllLoadedData()\n");
+
+
+
+
+#ifdef DEBUGWATCH
+ VehicleAIRender::Destroy();
+ //rReleaseString ("VehicleAIRender::Destroy()\n");
+
+#endif
+ GetSoundManager()->OnGameplayEnd( nextContext == CONTEXT_FRONTEND );
+ //rReleaseString ("GetSoundManager()->OnGameplayEnd ()\n");
+
+
+ PathManager::GetInstance()->Destroy();
+ //rReleaseString ("PathManager::GetInstance()->Destroy()\n");
+
+
+
+ // TODO - Darryl?
+ //
+ // all active vehicles should be destroyed
+ // they are owned by the missions that created them.
+
+ //This does cleanup.
+ RoadManager::GetInstance()->Destroy();
+ //rReleaseString ("RoadManager::GetInstance()->Destroy()\n");
+
+
+
+ if( nextContext == CONTEXT_LOADING_GAMEPLAY )
+ {
+ // if reloading in-game, set new level and mission to load
+ //
+ int level = GetGuiSystem()->GetInGameManager()->GetNextLevelToLoad();
+ GetGameplayManager()->SetLevelIndex( static_cast<RenderEnums::LevelEnum>( level ) );
+
+ int mission = GetGuiSystem()->GetInGameManager()->GetNextMissionToLoad();
+ GetGameplayManager()->SetMissionIndex( static_cast<RenderEnums::MissionEnum>( mission ) );
+ //rReleaseString ("Set level and mission indices\n");
+
+ }
+ else
+ {
+ SetGameplayManager( NULL );
+ }
+
+ // Cleanup the Avatar Manager
+ //
+ GetAvatarManager()->ExitGame();
+ //rReleaseString ("GetAvatarManager()->ExitGame()\n");
+
+
+ // Flush out the special section used by physics to cache SkeletonInfos.
+ //
+ p3d::inventory->RemoveSectionElements (SKELCACHE);
+ p3d::inventory->DeleteSection (SKELCACHE);
+ rReleaseString ("Delete SKELCACHE inventory section\n");
+
+
+ // release GUI in-game
+ GetGuiSystem()->HandleMessage( GUI_MSG_RELEASE_INGAME );
+ rReleaseString ("GetGuiSystem()->HandleMessage( GUI_MSG_RELEASE_INGAME )\n");
+
+ CHudMap::ClearAllRegisteredIcons();
+
+ // unregister GUI user input handler for active players
+ //
+ int activeControllerIDs = 0;
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( i );
+ if( controllerID != -1 )
+ {
+ activeControllerIDs |= (1 << controllerID);
+ }
+ }
+ rReleaseString ("Change active controllers\n");
+
+
+ GetGuiSystem()->UnregisterUserInputHandlers( activeControllerIDs );
+ rReleaseString ("Change active controllers\n");
+
+
+ // enable screen clearing
+ GetRenderManager()->mpLayer(RenderEnums::GUI)->pView( 0 )->SetClearMask( PDDI_BUFFER_ALL );
+ rReleaseString ("Enable screen clearing\n");
+
+#ifndef RAD_RELEASE
+ // Dump out the contents of the inventory sections
+ //
+ p3d::inventory->Dump (true);
+ rReleaseString ("Dump inventory\n");
+
+#endif
+ AnimatedIcon::ShutdownAnimatedIcons();
+
+ GetAnimEntityDSGManager()->RemoveAll();
+
+ GetTutorialManager()->EnableTutorialMode( false );
+
+ HeapMgr()->PopHeap (GMA_TEMP);
+ }
+
+ if ( nextContext == CONTEXT_GAMEPLAY )
+ {
+ if( mOldState == Input::ACTIVE_ANIM_CAM )
+ {
+ GetInputManager()->SetGameState( Input::ACTIVE_GAMEPLAY );
+ }
+ else
+ {
+ GetInputManager()->SetGameState( mOldState );
+ }
+
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( 0 );
+ bool vibrationOn = GetInputManager()->IsRumbleEnabled();
+
+ if ( GetGameplayManager()->GetGameType() == GameplayManager::GT_NORMAL &&
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetVehicle() )
+ {
+#ifdef RAD_PS2
+ bool hasWheel = false;
+
+ if ( controllerID != Input::USB0 &&
+ controllerID != Input::USB1 )
+ {
+ if ( GetInputManager()->GetController( Input::USB0 )->IsConnected() )
+ {
+ GetInputManager()->SetRumbleForDevice( Input::USB0, vibrationOn );
+ hasWheel = true;
+ }
+ else if ( GetInputManager()->GetController( Input::USB1 )->IsConnected() )
+ {
+ GetInputManager()->SetRumbleForDevice( Input::USB1, vibrationOn );
+ hasWheel = true;
+ }
+ }
+ //I hate this.
+ if ( !hasWheel )
+#endif
+ GetInputManager()->SetRumbleForDevice( controllerID, vibrationOn );
+ }
+
+ }
+ else
+ {
+ GetInputManager()->SetGameState( Input::ACTIVE_ALL );
+ }
+ SetMemoryIdentification( "PauseContext Finished" );
+ MEMTRACK_POP_FLAG( "" );
+}
+
+//=============================================================================
+// PauseContext::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void PauseContext::OnUpdate( unsigned int elapsedTime )
+{
+ // update game data manager
+ GetGameDataManager()->Update( elapsedTime );
+
+ // update GUI system
+ GetGuiSystem()->Update( elapsedTime );
+
+ // update coin manager
+ GetCoinManager()->Update( elapsedTime );
+ GetSparkleManager()->Update( elapsedTime );
+
+ //update hitnrun????
+ //GetHitnRunManager()->Update(elapsedTime);
+
+#ifdef RAD_DEMO
+ GetGameplayManager()->UpdateIdleTime( elapsedTime );
+#endif
+
+ // Check to see if we're loading so we don't screw up when we quit the level.
+ //
+ if( GetGameplayManager()->GetLevelComplete() && !GetLoadingManager()->IsLoading() )
+ {
+ if( !m_quitGamePending )
+ {
+ m_quitGamePending = true;
+
+#ifdef RAD_DEMO
+ GetGuiSystem()->HandleMessage( GUI_MSG_QUIT_INGAME );
+#else
+ if( GetGameplayManager()->GetGameComplete() )
+ {
+ GetGuiSystem()->GotoScreen( CGuiWindow::GUI_SCREEN_ID_VIEW_CREDITS,
+ 0, 0, CLEAR_WINDOW_HISTORY );
+ }
+ else
+ {
+ GetGuiSystem()->GotoScreen( CGuiWindow::GUI_SCREEN_ID_LEVEL_END,
+ 0, 0, CLEAR_WINDOW_HISTORY );
+ }
+#endif // RAD_DEMO
+ }
+ }
+ else
+ {
+ // update mission loading
+ //
+ GetGameplayManager()->PerformLoading();
+ }
+}
+
+//=============================================================================
+// PauseContext::OnSuspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PauseContext::OnSuspend()
+{
+}
+
+//=============================================================================
+// PauseContext::OnResume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PauseContext::OnResume()
+{
+}
+
+//=============================================================================
+// PauseContext::OnHandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void PauseContext::OnHandleEvent( EventEnum id, void* pEventData )
+{
+}
diff --git a/game/code/contexts/pausecontext.h b/game/code/contexts/pausecontext.h
new file mode 100644
index 0000000..780c07e
--- /dev/null
+++ b/game/code/contexts/pausecontext.h
@@ -0,0 +1,77 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: PauseContext.h
+//
+// Description:
+//
+// History: + Created -- Tony Chu
+//
+//=============================================================================
+
+#ifndef PAUSECONTEXT_H
+#define PAUSECONTEXT_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/context.h> // is-a Context
+#include <input/controller.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis:
+//
+//=============================================================================
+class PauseContext : public Context
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static PauseContext* GetInstance();
+
+ bool IsWaitingForContextSwitch() { return m_waitingForContextSwitch; }
+ void SetWaitingForContextSwitch( bool tf ) { m_waitingForContextSwitch = tf; }
+
+ protected:
+
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+ virtual void OnHandleEvent( EventEnum id, void* pEventData );
+
+ private:
+
+ // constructor and destructor are protected to force singleton implementation
+ PauseContext();
+ virtual ~PauseContext();
+
+ // Declared but not defined to prevent copying and assignment.
+ PauseContext( const PauseContext& );
+ PauseContext& operator=( const PauseContext& );
+
+ // Pointer to the one and only instance of this singleton.
+ static PauseContext* spInstance;
+
+ Input::ActiveState mOldState;
+ bool m_quitGamePending : 1;
+ bool m_waitingForContextSwitch;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline PauseContext* GetPauseContext() { return( PauseContext::GetInstance() ); }
+
+
+#endif // PAUSECONTEXT_H
diff --git a/game/code/contexts/playingcontext.cpp b/game/code/contexts/playingcontext.cpp
new file mode 100644
index 0000000..5b58cc2
--- /dev/null
+++ b/game/code/contexts/playingcontext.cpp
@@ -0,0 +1,190 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement PlayingContext
+//
+// History: 21/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/view.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/playingcontext.h>
+
+#include <debug/profiler.h>
+#include <input/inputmanager.h>
+#include <interiors/interiormanager.h>
+#include <memory/srrmemory.h>
+#include <presentation/gui/guisystem.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/renderlayer.h>
+
+#include <mission/animatedicon.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// PlayingContext::PlayingContext
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PlayingContext::PlayingContext() :
+ mQuitting( false )
+{
+}
+
+//==============================================================================
+// PlayingContext::~PlayingContext
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PlayingContext::~PlayingContext()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// PlayingContext::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum previousContext )
+//
+// Return: void
+//
+//=============================================================================
+void PlayingContext::OnStart( ContextEnum previousContext )
+{
+ SetMemoryIdentification( "PlayingContext" );
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ GetInputManager()->SetGameState( Input::ACTIVE_GAMEPLAY );
+ GetRenderManager()->mpLayer( RenderEnums::LevelSlot )->Thaw();
+
+ // disable screen clearing for GUI render layer
+ //
+ GetRenderManager()->mpLayer( RenderEnums::GUI )->pView( 0 )->SetClearMask( PDDI_BUFFER_DEPTH | PDDI_BUFFER_STENCIL );
+
+#ifdef RAD_WIN32
+ GetInputManager()->GetFEMouse()->SetInGameMode( true );
+#endif
+}
+
+//=============================================================================
+// PlayingContext::OnStop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum nextContext )
+//
+// Return: void
+//
+//=============================================================================
+void PlayingContext::OnStop( ContextEnum nextContext )
+{
+ mQuitting = false; // I've quit after all.
+
+#ifdef RAD_WIN32
+ GetInputManager()->GetFEMouse()->SetInGameMode( false );
+#endif
+
+ GetInputManager()->SetGameState( Input::ACTIVE_ALL );
+
+ // Make sure no blur effects get carried over
+ GetRenderManager()->SetBlurAlpha( 0 );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ SetMemoryIdentification( "PlayingContext Finished" );
+}
+
+//=============================================================================
+// PlayingContext::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void PlayingContext::OnUpdate( unsigned int elapsedTime )
+{
+BEGIN_PROFILE( "GuiSystem" );
+ GetGuiSystem()->Update( elapsedTime );
+END_PROFILE( "GuiSystem" );
+}
+
+//=============================================================================
+// PlayingContext::OnSuspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PlayingContext::OnSuspend()
+{
+}
+
+//=============================================================================
+// PlayingContext::OnResume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PlayingContext::OnResume()
+{
+}
+
+//=============================================================================
+// PlayingContext::OnHandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void PlayingContext::OnHandleEvent( EventEnum id, void* pEventData )
+{
+}
+
diff --git a/game/code/contexts/playingcontext.h b/game/code/contexts/playingcontext.h
new file mode 100644
index 0000000..302e9d7
--- /dev/null
+++ b/game/code/contexts/playingcontext.h
@@ -0,0 +1,59 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: PlayingContext.h
+//
+// Description:
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef PLAYINGCONTEXT_H
+#define PLAYINGCONTEXT_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/context.h> // is-a Context
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis:
+//
+//=============================================================================
+class PlayingContext : public Context
+{
+ public:
+ virtual void OnHandleEvent( EventEnum id, void* pEventData );
+
+ protected:
+ // constructor and destructor are protected to force singleton implementation
+ PlayingContext();
+ virtual ~PlayingContext();
+
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+ bool mQuitting;
+
+ private:
+ // Declared but not defined to prevent copying and assignment.
+ PlayingContext( const PlayingContext& );
+ PlayingContext& operator=( const PlayingContext& );
+
+};
+
+#endif // PLAYINGCONTEXT_H
diff --git a/game/code/contexts/supersprint/allsupersprintctx.cpp b/game/code/contexts/supersprint/allsupersprintctx.cpp
new file mode 100644
index 0000000..eedae85
--- /dev/null
+++ b/game/code/contexts/supersprint/allsupersprintctx.cpp
@@ -0,0 +1,3 @@
+#include <contexts/supersprint/loadingsupersprintcontext.cpp>
+#include <contexts/supersprint/supersprintcontext.cpp>
+#include <contexts/supersprint/supersprintfecontext.cpp>
diff --git a/game/code/contexts/supersprint/loadingsupersprintcontext.cpp b/game/code/contexts/supersprint/loadingsupersprintcontext.cpp
new file mode 100644
index 0000000..e802da9
--- /dev/null
+++ b/game/code/contexts/supersprint/loadingsupersprintcontext.cpp
@@ -0,0 +1,240 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement LoadingSuperSprintContext
+//
+// History: 21/05/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/supersprint/loadingsupersprintcontext.h>
+
+#include <gameflow/gameflow.h>
+#include <loading/loadingmanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/gameplaymanager.h>
+#include <render/rendermanager/rendermanager.h>
+#include <supersprint/supersprintmanager.h>
+#include <sound/soundmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+LoadingSuperSprintContext* LoadingSuperSprintContext::spInstance = NULL;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// LoadingSuperSprintContext::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the LoadingSuperSprintContext singleton.
+// - Creates the LoadingSuperSprintContext if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the LoadingSuperSprintContext.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+LoadingSuperSprintContext* LoadingSuperSprintContext::GetInstance()
+{
+ if( spInstance == NULL )
+ {
+ spInstance = new(GMA_PERSISTENT) LoadingSuperSprintContext;
+ rAssert( spInstance );
+ }
+
+ return spInstance;
+}
+
+//==============================================================================
+// LoadingSuperSprintContext::LoadingSuperSprintContext
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LoadingSuperSprintContext::LoadingSuperSprintContext()
+{
+}
+
+//==============================================================================
+// LoadingSuperSprintContext::~LoadingSuperSprintContext
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LoadingSuperSprintContext::~LoadingSuperSprintContext()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// LoadingSuperSprintContext::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum previousContext )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingSuperSprintContext::OnStart( ContextEnum previousContext )
+{
+ GetGameplayManager()->mIsDemo = false;
+
+ //
+ // Queue the loading for level sounds
+ //
+ GetSoundManager()->QueueLevelSoundLoads();
+
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnStart( previousContext );
+
+/*****************************************************************************
+ * Start inserting stuff below ...
+ *****************************************************************************/
+
+ GetSSM()->LoadCars();
+
+ // This is an extra callback to let this system know when all the loading is done for the
+ // context.. Kinda sneaky..
+ //
+ GetLoadingManager()->AddCallback( this );
+}
+
+//=============================================================================
+// LoadingSuperSprintContext::OnStop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum nextContext )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingSuperSprintContext::OnStop( ContextEnum nextContext )
+{
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnStop( nextContext );
+}
+
+//=============================================================================
+// LoadingSuperSprintContext::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingSuperSprintContext::OnUpdate( unsigned int elapsedTime )
+{
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnUpdate( elapsedTime );
+}
+
+//=============================================================================
+// LoadingSuperSprintContext::OnSuspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoadingSuperSprintContext::OnSuspend()
+{
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnSuspend();
+}
+
+//=============================================================================
+// LoadingSuperSprintContext::OnResume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoadingSuperSprintContext::OnResume()
+{
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnResume();
+}
+
+//=============================================================================
+// LoadingSuperSprintContext::PrepareNewHeaps
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoadingSuperSprintContext::PrepareNewHeaps()
+{
+}
+
+//=============================================================================
+// LoadingSuperSprintContext::OnProcessRequestsComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void LoadingSuperSprintContext::OnProcessRequestsComplete( void* pUserData )
+{
+ GetSSM()->LoadCharacters();
+ GetSSM()->LoadScriptData();
+
+ // Common to all loading contexts.
+ //
+ LoadingContext::OnProcessRequestsComplete( pUserData );
+}
diff --git a/game/code/contexts/supersprint/loadingsupersprintcontext.h b/game/code/contexts/supersprint/loadingsupersprintcontext.h
new file mode 100644
index 0000000..ee17bb1
--- /dev/null
+++ b/game/code/contexts/supersprint/loadingsupersprintcontext.h
@@ -0,0 +1,72 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: LoadingSuperSprintContext.h
+//
+// Description:
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef LoadingSuperSprintContext_H
+#define LoadingSuperSprintContext_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/loadingcontext.h> // is-a LoadingContext
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis:
+//
+//=============================================================================
+class LoadingSuperSprintContext : public LoadingContext
+{
+ public:
+ // Static Methods for accessing this singleton.
+ static LoadingSuperSprintContext* GetInstance();
+
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+ protected:
+
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+ virtual void PrepareNewHeaps();
+
+ private:
+
+ // constructor and destructor are protected to force singleton implementation
+ LoadingSuperSprintContext();
+ virtual ~LoadingSuperSprintContext();
+
+ // Declared but not defined to prevent copying and assignment.
+ LoadingSuperSprintContext( const LoadingSuperSprintContext& );
+ LoadingSuperSprintContext& operator=( const LoadingSuperSprintContext& );
+
+ // Pointer to the one and only instance of this singleton.
+ static LoadingSuperSprintContext* spInstance;
+
+ bool mUnQueuedLoadRequests;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline LoadingSuperSprintContext* GetLoadingSuperSprintContext() { return( LoadingSuperSprintContext::GetInstance() ); }
+
+
+#endif // LoadingSuperSprintContext_H
diff --git a/game/code/contexts/supersprint/supersprintcontext.cpp b/game/code/contexts/supersprint/supersprintcontext.cpp
new file mode 100644
index 0000000..c6b87cc
--- /dev/null
+++ b/game/code/contexts/supersprint/supersprintcontext.cpp
@@ -0,0 +1,544 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supersprintcontext.cpp
+//
+// Description: Implement SuperSprintContext
+//
+// History: 2/8/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+#include <p3d/shadow.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+
+#include <ai/vehicle/vehicleairender.h>
+
+#include <contexts/supersprint/supersprintcontext.h>
+
+#include <memory/srrmemory.h>
+
+#include <gameflow/gameflow.h>
+#include <main/commandlineoptions.h>
+#include <main/game.h>
+#include <memory/leakdetection.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/renderlayer.h>
+
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/coins/sparkle.h>
+#include <worldsim/hitnrunmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/ped/pedestrianmanager.h>
+#include <worldsim/skidmarks/skidmarkmanager.h>
+
+#include <render/breakables/breakablesmanager.h>
+#include <render/particles/particlemanager.h>
+#include <render/animentitydsgmanager/animentitydsgmanager.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <worldsim/skidmarks/skidmarkgenerator.h>
+#include <sound/soundmanager.h>
+#include <meta/triggervolumetracker.h>
+
+#include <pedpaths/pathmanager.h>
+
+#include <input/inputmanager.h>
+
+// TODO: Remove once we put CreateRoadNetwork in the levels pipe
+#include <roads/roadmanager.h>
+#include <p3d/light.hpp>
+#include <p3d/view.hpp>
+
+#include <worldsim/worldobject.h>
+#include <ai/actionbuttonmanager.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercam.h>
+#include <camera/supersprintcam.h>
+#include <camera/bumpercam.h>
+#include <camera/kullcam.h>
+#include <camera/wrecklesscam.h>
+#include <camera/followcam.h>
+#include <camera/animatedcam.h>
+#include <camera/walkercam.h>
+
+#include <supersprint/supersprintmanager.h>
+
+#include <ai/actor/actormanager.h>
+#include <presentation/presentation.h>
+
+#include <mission/animatedicon.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiwindow.h>
+
+#include <render/animentitydsgmanager/animentitydsgmanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+SuperSprintContext* SuperSprintContext::spInstance = NULL;
+const int NUM_SKID_MARKS_SUPER_SPRINT = 30;
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+SuperSprintContext* SuperSprintContext::GetInstance()
+{
+ if ( spInstance == NULL )
+ {
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ spInstance = new SuperSprintContext();
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+ }
+
+ return spInstance;
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperSprintContext::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum previousContext )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintContext::OnStart( ContextEnum previousContext )
+{
+ this->Resume();
+
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnStart( previousContext );
+
+ ////////////////////////////////////////////////////////////
+ // RenderManager
+ RenderManager* rm = GetRenderManager();
+ RenderLayer* rl = rm->mpLayer( RenderEnums::LevelSlot );
+ rAssert( rl );
+
+ GetRenderManager()->mpLayer( RenderEnums::PresentationSlot )->Chill();
+
+#ifdef DEBUGWATCH
+ // bootstrap vehicleai renderer
+ VehicleAIRender::GetVehicleAIRender();
+#endif
+
+ ////////////////////////////////////////////////////////////
+ // Cameras set up
+ rl->SetNumViews( 1 );
+ rl->SetUpViewCam();
+
+ p3d::inventory->SelectSection("Default");
+ tLightGroup* sun = p3d::find<tLightGroup>("sun");
+ rAssert( sun );
+ for( unsigned int i = 0; i < rl->GetNumViews(); i++ )
+ {
+ for(int j=0;j<sun->GetNumLights();j++)
+ {
+ rl->pView(i)->AddLight( sun->GetLight(j) );
+ }
+ }
+ // Disable mood lighting in supersprint mode
+ rm->SetLevelLayerLights( NULL );
+
+
+ float aspect = p3d::display->IsWidescreen() ? (16.0f / 9.0f) : (4.0f / 3.0f);
+
+ unsigned int view = 0;
+
+ tPointCamera* cam = static_cast<tPointCamera*>( rl->pCam( view ) );
+ rAssert( dynamic_cast<tPointCamera*> ( cam ) != NULL );
+ rAssert( cam );
+
+ SuperCam* sc = NULL;
+ sc = new SuperSprintCam();
+ sc->SetAspect( aspect );
+
+ for( int i = 0; i < GetGameplayManager()->GetNumPlayers(); ++i )
+ {
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC( i );
+ rAssert( scc );
+
+ scc->SetCamera( cam );
+ scc->RegisterSuperCam( sc );
+
+ //Start with the right cam
+ scc->SelectSuperCam( SuperCam::SUPER_SPRINT_CAM, SuperCamCentral::CUT | SuperCamCentral::QUICK, 0 );
+ }
+
+ //Hack to add the other cameras to player 0
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC( 0 );
+ sc = new BumperCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+
+ FollowCam* fc = new FollowCam( FollowCam::FOLLOW_NEAR );
+ fc->SetAspect( aspect );
+ fc->CopyToData();
+ scc->RegisterSuperCam( fc );
+
+ fc = new FollowCam( FollowCam::FOLLOW_FAR );
+ fc->SetAspect( aspect );
+ fc->CopyToData();
+ scc->RegisterSuperCam( fc );
+
+ sc = new KullCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+
+ sc = new WrecklessCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+
+ sc = new AnimatedCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+
+ sc = new ComedyCam();
+ sc->SetAspect( aspect );
+ scc->RegisterSuperCam( sc );
+
+ ////////////////////////////////////////////////////////////
+ // AvatarManager Bootstrapping
+ GetAvatarManager( )->EnterGame( );
+
+ ////////////////////////////////////////////////////////////
+ // TrafficManager Init
+ //TrafficManager::GetInstance()->Init();
+
+ ////////////////////////////////////////////////////////////
+ // PedestrianManager Init
+ PedestrianManager::GetInstance()->Init();
+
+ // TODO: Move this into level pipe
+ //Set up the sorting of the intersections and stuff.
+ RoadManager::GetInstance()->CreateRoadNetwork();
+
+ ////////////////////////////////////////////////////////////
+ // SkidMark Init Init
+ //Find skid mark shaders in the inventory and set proper values
+ SkidMarkGenerator::InitShaders();
+ GetSkidmarkManager()->Init( NUM_SKID_MARKS_SUPER_SPRINT );
+ GetSkidmarkManager()->SetTimedFadeouts( true );
+
+
+ ////////////////////////////////////////////////////////////
+ // OnStart calls
+
+ rm->pWorldScene()->SetRenderAll(true);
+
+ GetSSM()->StartRace();
+
+ //
+ // Notify the sound system of gameplay start. This has been moved after
+ // the StartRace call above, since that causes character position in or out
+ // of the car to be decided, which the sound system uses to determine
+ // which sounds to start playing.
+ //
+ SoundManager::GetInstance()->OnGameplayStart();
+
+ GetGuiSystem()->HandleMessage( GUI_MSG_RUN_MINIGAME,
+ CGuiWindow::GUI_SCREEN_ID_MINI_HUD );
+
+ // register GUI user input handler for active players only
+ //
+ int activeControllerIDs = 0;
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( i );
+ if( controllerID != -1 )
+ {
+ activeControllerIDs |= (1 << controllerID);
+ }
+ }
+
+ GetGuiSystem()->RegisterUserInputHandlers( activeControllerIDs );
+
+ GetInputManager()->SetGameState( Input::ACTIVE_SS_GAME );
+}
+
+//=============================================================================
+// SuperSprintContext::OnStop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum nextContext )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintContext::OnStop( ContextEnum nextContext )
+{
+ GetInputManager()->EnableReset( false );
+
+ GetInputManager()->SetGameState( Input::ACTIVE_NONE );
+
+ // unregister GUI user input handlers
+ //
+ int activeControllerIDs = 0;
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( i );
+ if( controllerID != -1 )
+ {
+ activeControllerIDs |= (1 << controllerID);
+ }
+ }
+
+ GetGuiSystem()->UnregisterUserInputHandlers( activeControllerIDs );
+
+ HeapMgr()->PushHeap (GMA_TEMP);
+
+ //
+ // This is called to prevent DMA of destroyed textures,
+ // because we don't swap buffers until the next frame.
+ //
+ p3d::pddi->DrawSync();
+
+ const bool shutdown = true;
+ GetSuperCamManager()->Init( shutdown );
+
+ TriggerVolumeTracker::GetInstance()->Cleanup();
+
+ GetCoinManager()->Destroy();
+ GetSparkleManager()->Destroy();
+ GetHitnRunManager()->Destroy();
+
+ GetPresentationManager()->Finalize();
+
+ //Shut down the SuperSprintManager
+ GetGameplayManager()->Finalize();
+
+ GetSkidmarkManager()->Destroy();
+
+ GetRenderManager()->mpLayer( RenderEnums::PresentationSlot )->Warm();
+
+ // Clean up lights!
+ //
+ RenderLayer* rl = GetRenderManager()->mpLayer( RenderEnums::LevelSlot );
+ rAssert( rl );
+ for( unsigned int i = 0; i < rl->GetNumViews(); i++ )
+ {
+ rl->pView(i)->RemoveAllLights ();
+ }
+
+#ifdef DEBUGWATCH
+ VehicleAIRender::Destroy();
+#endif
+
+ GetPresentationManager()->OnGameplayStop();
+
+ //Destroy Avatars stuff first
+ GetAvatarManager()->Destroy();
+ TrafficManager::DestroyInstance();
+ PedestrianManager::DestroyInstance();
+ GetCharacterManager()->Destroy();
+ GetVehicleCentral()->Unload();
+ GetActorManager()->RemoveAllActors();
+ GetActorManager()->RemoveAllSpawnPoints();
+
+ //We never entered.
+ GetActionButtonManager( )->Destroy( );
+
+ GetBreakablesManager()->FreeAllBreakables();
+ GetParticleManager()->ClearSystems();
+ GetRenderManager()->DumpAllLoadedData();
+ SkidMarkGenerator::ReleaseShaders();
+
+ GetSoundManager()->OnGameplayEnd( true );
+
+ PathManager::GetInstance()->Destroy();
+
+ //This does cleanup.
+ RoadManager::GetInstance()->Destroy();
+
+ // Cleanup the Avatar Manager
+ //
+ GetAvatarManager()->ExitGame();
+
+ // Flush out the special section used by physics to cache SkeletonInfos.
+ //
+ p3d::inventory->RemoveSectionElements (SKELCACHE);
+ p3d::inventory->DeleteSection (SKELCACHE);
+ rReleaseString ("Delete SKELCACHE inventory section\n");
+
+ // enable screen clearing
+ //
+ GetRenderManager()->mpLayer( RenderEnums::GUI )->pView( 0 )->SetClearMask( PDDI_BUFFER_ALL );
+
+#ifndef RAD_RELEASE
+ // Dump out the contents of the inventory sections
+ //
+ p3d::inventory->Dump (true);
+#endif
+
+ AnimatedIcon::ShutdownAnimatedIcons();
+ GetAnimEntityDSGManager()->RemoveAll();
+
+ HeapMgr()->PopHeap (GMA_TEMP);
+
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnStop( nextContext );
+}
+
+//=============================================================================
+// SuperSprintContext::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintContext::OnUpdate( unsigned int elapsedTime )
+{
+ if( m_state != S_SUSPENDED )
+ {
+ float timeins = (float)(elapsedTime) / 1000.0f;
+ GetAvatarManager()->Update( timeins );
+
+ GetCharacterManager()->GarbageCollect( );
+
+ ///////////////////////////////////////////////////////////////
+ // Update Particles
+ GetParticleManager()->Update( elapsedTime );
+ GetBreakablesManager()->Update( elapsedTime );
+ GetAnimEntityDSGManager()->Update( elapsedTime );
+
+ ///////////////////////////////////////////////////////////////
+ // Update Gameplay Manager
+ GetGameplayManager()->Update( elapsedTime );
+
+ ///////////////////////////////////////////////////////////////
+ // Update Physics
+ GetWorldPhysicsManager()->Update(elapsedTime);
+
+ ///////////////////////////////////////////////////////////////
+ // Update Peds
+ // ordering is important. Unless other parts of code change, we must call
+ // this before WorldPhysManager::Update() because PedestrianManager
+ // sets the flags for all characters to be updated in WorldPhys Update
+ PedestrianManager::GetInstance()->Update( elapsedTime );
+
+ ///////////////////////////////////////////////////////////////
+ // Update Trigger volumes
+ GetTriggerVolumeTracker()->Update( elapsedTime );
+
+ ///////////////////////////////////////////////////////////////
+ // Update Traffic
+ //TrafficManager::GetInstance()->Update( elapsedTime );
+
+ ///////////////////////////////////////////////////////////////
+ // Update Skidmarks (fading)
+ GetSkidmarkManager()->Update( elapsedTime );
+ GetSparkleManager()->Update( elapsedTime );
+
+ //Update the Super Sprint Manager
+ GetSSM()->Update( elapsedTime );
+ }
+
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnUpdate( elapsedTime );
+}
+
+//=============================================================================
+// SuperSprintContext::OnSuspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintContext::OnSuspend()
+{
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnSuspend();
+}
+
+//=============================================================================
+// SuperSprintContext::OnResume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintContext::OnResume()
+{
+ // Common to all playing contexts.
+ //
+ PlayingContext::OnResume();
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperSprintContext::SuperSprintContext
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+SuperSprintContext::SuperSprintContext()
+{
+}
+
+//=============================================================================
+// SuperSprintContext::~SuperSprintContext
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+SuperSprintContext::~SuperSprintContext()
+{
+}
+
diff --git a/game/code/contexts/supersprint/supersprintcontext.h b/game/code/contexts/supersprint/supersprintcontext.h
new file mode 100644
index 0000000..ed484a0
--- /dev/null
+++ b/game/code/contexts/supersprint/supersprintcontext.h
@@ -0,0 +1,63 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supersprintcontext.h
+//
+// Description: Blahblahblah
+//
+// History: 2/8/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef SUPERSPRINTCONTEXT_H
+#define SUPERSPRINTCONTEXT_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <contexts/playingcontext.h> // is-a PlayingContext
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class SuperSprintContext : public PlayingContext
+{
+public:
+ // Static Methods for accessing this singleton.
+ static SuperSprintContext* GetInstance();
+
+protected:
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+private:
+ static SuperSprintContext* spInstance;
+
+ SuperSprintContext();
+ virtual ~SuperSprintContext();
+
+ //Prevent wasteful constructor creation.
+ SuperSprintContext( const SuperSprintContext& supersprintcontext );
+ SuperSprintContext& operator=( const SuperSprintContext& supersprintcontext );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+inline SuperSprintContext* GetSPCTX() { return SuperSprintContext::GetInstance(); };
+
+#endif //SUPERSPRINTCONTEXT_H
diff --git a/game/code/contexts/supersprint/supersprintfecontext.cpp b/game/code/contexts/supersprint/supersprintfecontext.cpp
new file mode 100644
index 0000000..1188bda
--- /dev/null
+++ b/game/code/contexts/supersprint/supersprintfecontext.cpp
@@ -0,0 +1,247 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: SuperSprintFEContext.cpp
+//
+// Description: Implement SuperSprintFEContext
+//
+// History: 03/26/2003 + Created -- Tony Chu
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/supersprint/supersprintfecontext.h>
+
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <presentation/gui/guisystem.h>
+#include <supersprint/supersprintdata.h>
+#include <supersprint/supersprintmanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+SuperSprintFEContext* SuperSprintFEContext::spInstance = NULL;
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+SuperSprintFEContext* SuperSprintFEContext::GetInstance()
+{
+ if( spInstance == NULL )
+ {
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ spInstance = new SuperSprintFEContext;
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ }
+
+ return spInstance;
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperSprintFEContext::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum previousContext )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintFEContext::OnStart( ContextEnum previousContext )
+{
+ if( previousContext != CONTEXT_SUPERSPRINT )
+ {
+ HeapMgr()->PrepareHeapsSuperSprint();
+
+ // run backend loading screen
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_RUN_BACKEND );
+
+ // load mini-game FE
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_INIT_MINIGAME );
+
+ GetLoadingManager()->AddCallback( this );
+ }
+
+ // register GUI user input handlers
+ //
+ GetGuiSystem()->RegisterUserInputHandlers();
+
+ // unregister all players' controller IDs
+ //
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+ GetInputManager()->UnregisterControllerID( i );
+ }
+
+ GetInputManager()->SetGameState( Input::ACTIVE_FRONTEND );
+}
+
+//=============================================================================
+// SuperSprintFEContext::OnStop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ContextEnum nextContext )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintFEContext::OnStop( ContextEnum nextContext )
+{
+ p3d::pddi->DrawSync();
+
+ GetInputManager()->SetGameState( Input::ACTIVE_NONE );
+
+ // unregister GUI user input handlers
+ //
+ GetGuiSystem()->UnregisterUserInputHandlers();
+
+ if( nextContext != CONTEXT_LOADING_SUPERSPRINT )
+ {
+ SuperSprintManager::DestroyInstance();
+
+ // unload mini-game FE
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_RELEASE_MINIGAME );
+
+ //Clear the gameplay manager.
+ SetGameplayManager( NULL );
+ }
+}
+
+//=============================================================================
+// SuperSprintFEContext::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintFEContext::OnUpdate( unsigned int elapsedTime )
+{
+ GetGuiSystem()->Update( elapsedTime );
+}
+
+//=============================================================================
+// SuperSprintFEContext::OnSuspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintFEContext::OnSuspend()
+{
+}
+
+//=============================================================================
+// SuperSprintFEContext::OnResume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintFEContext::OnResume()
+{
+}
+
+//=============================================================================
+// SuperSprintFEContext::OnHandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintFEContext::OnHandleEvent( EventEnum id, void* pEventData )
+{
+}
+
+//=============================================================================
+// SuperSprintFEContext::OnProcessRequestsComplete
+//=============================================================================
+// Description: Called when startup loading is done
+//
+// Parameters: pUserData - unused
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintFEContext::OnProcessRequestsComplete( void* pUserData )
+{
+ // quit backend loading screen
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_QUIT_BACKEND );
+
+ // startup the mini-game FE
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_RUN_MINIGAME );
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperSprintFEContext::SuperSprintFEContext
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+SuperSprintFEContext::SuperSprintFEContext()
+{
+}
+
+//=============================================================================
+// SuperSprintFEContext::~SuperSprintFEContext
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+SuperSprintFEContext::~SuperSprintFEContext()
+{
+}
+
diff --git a/game/code/contexts/supersprint/supersprintfecontext.h b/game/code/contexts/supersprint/supersprintfecontext.h
new file mode 100644
index 0000000..50439f9
--- /dev/null
+++ b/game/code/contexts/supersprint/supersprintfecontext.h
@@ -0,0 +1,73 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: SuperSprintFEContext.h
+//
+// Description: Blahblahblah
+//
+// History: 03/26/2003 + Created -- Tony Chu
+//
+//=============================================================================
+
+#ifndef SUPERSPRINTFECONTEXT_H
+#define SUPERSPRINTFECONTEXT_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <contexts/context.h> // is-a Context
+
+#include <loading/loadingmanager.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class SuperSprintFEContext : public Context,
+ public LoadingManager::ProcessRequestsCallback
+{
+public:
+ // Static Methods for accessing this singleton.
+ static SuperSprintFEContext* GetInstance();
+
+protected:
+ virtual void OnStart( ContextEnum previousContext );
+ virtual void OnStop( ContextEnum nextContext );
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ virtual void OnSuspend();
+ virtual void OnResume();
+
+ virtual void OnHandleEvent( EventEnum id, void* pEventData );
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+private:
+ static SuperSprintFEContext* spInstance;
+
+ SuperSprintFEContext();
+ virtual ~SuperSprintFEContext();
+
+ //Prevent wasteful constructor creation.
+ SuperSprintFEContext( const SuperSprintFEContext& SuperSprintFEContext );
+ SuperSprintFEContext& operator=( const SuperSprintFEContext& SuperSprintFEContext );
+
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+inline SuperSprintFEContext* GetSuperSprintFEContext()
+{
+ return SuperSprintFEContext::GetInstance();
+};
+
+#endif // SUPERSPRINTFECONTEXT_H
diff --git a/game/code/data/PersistentSectors.cpp b/game/code/data/PersistentSectors.cpp
new file mode 100644
index 0000000..b2fa151
--- /dev/null
+++ b/game/code/data/PersistentSectors.cpp
@@ -0,0 +1,87 @@
+#include <mission/charactersheet/charactersheetmanager.h>
+
+// Auto generated with SectorTable.
+struct SectorMap
+{
+ unsigned short Sector;
+ char CurIndex;
+};
+
+static const int NUM_SECTORS = NUM_PERSISTENT_SECTORS;
+
+SectorMap sSectorMap[ NUM_SECTORS ] = {
+{ 0x4d2d, 0 }, //l1r1.p3d
+{ 0xedae, 0 }, //l1r2.p3d
+{ 0x4b2b, 0 }, //l1r3.p3d
+{ 0xc9f3, 0 }, //l1r4a.p3d
+{ 0x6c70, 0 }, //l1r4b.p3d
+{ 0xebaa, 0 }, //l1r6.p3d
+{ 0x4917, 0 }, //l1r7.p3d
+{ 0x4f25, 0 }, //l1z1.p3d
+{ 0xefa6, 0 }, //l1z2.p3d
+{ 0x4d23, 0 }, //l1z3.p3d
+{ 0xedac, 0 }, //l1z4.p3d
+{ 0xeda2, 0 }, //l1z6.p3d
+{ 0x4b2f, 0 }, //l1z7.p3d
+{ 0xdfa2, 0 }, //l2r1.p3d
+{ 0x3f21, 0 }, //l2r2.p3d
+{ 0xdfac, 0 }, //l2r3.p3d
+{ 0x3f23, 0 }, //l2r4.p3d
+{ 0xd19a, 0 }, //l2z1.p3d
+{ 0xd279, 0 }, //l2z2.p3d
+{ 0xd264, 0 }, //l2z3.p3d
+{ 0x311b, 0 }, //l2z4.p3d
+{ 0x6b2b, 0 }, //l3r1.p3d
+{ 0x0da8, 0 }, //l3r2.p3d
+{ 0x6d2d, 0 }, //l3r3.p3d
+{ 0x0baa, 0 }, //l3r4.p3d
+{ 0x6917, 0 }, //l3r5.p3d
+{ 0x6d23, 0 }, //l3z1.p3d
+{ 0x0fa0, 0 }, //l3z2.p3d
+{ 0x6f25, 0 }, //l3z3.p3d
+{ 0x0da2, 0 }, //l3z4.p3d
+{ 0x6b2f, 0 }, //l3z5.p3d
+{ 0x7590, 0 }, //l4r1.p3d
+{ 0xd313, 0 }, //l4r2.p3d
+{ 0x7596, 0 }, //l4r3.p3d
+{ 0xe188, 0 }, //l4r4a.p3d
+{ 0x3f0b, 0 }, //l4r4b.p3d
+{ 0xd11f, 0 }, //l4r6.p3d
+{ 0x7392, 0 }, //l4r7.p3d
+{ 0x7398, 0 }, //l4z1.p3d
+{ 0xd11b, 0 }, //l4z2.p3d
+{ 0x739e, 0 }, //l4z3.p3d
+{ 0x7279, 0 }, //l4z4.p3d
+{ 0xdf07, 0 }, //l4z6.p3d
+{ 0x719a, 0 }, //l4z7.p3d
+{ 0x0d09, 0 }, //l5r1.p3d
+{ 0xadaa, 0 }, //l5r2.p3d
+{ 0x0b17, 0 }, //l5r3.p3d
+{ 0xafa8, 0 }, //l5r4.p3d
+{ 0x0f21, 0 }, //l5z1.p3d
+{ 0xafa2, 0 }, //l5z2.p3d
+{ 0x0d2f, 0 }, //l5z3.p3d
+{ 0xb1a0, 0 }, //l5z4.p3d
+{ 0x9fae, 0 }, //l6r1.p3d
+{ 0xff2d, 0 }, //l6r2.p3d
+{ 0x9fa8, 0 }, //l6r3.p3d
+{ 0xfb17, 0 }, //l6r4.p3d
+{ 0x9daa, 0 }, //l6r5.p3d
+{ 0xa1a6, 0 }, //l6z1.p3d
+{ 0x0125, 0 }, //l6z2.p3d
+{ 0xa1a0, 0 }, //l6z3.p3d
+{ 0xfd2f, 0 }, //l6z4.p3d
+{ 0x9fa2, 0 }, //l6z5.p3d
+{ 0x2b17, 0 }, //l7r1.p3d
+{ 0xcdb4, 0 }, //l7r2.p3d
+{ 0x2d09, 0 }, //l7r3.p3d
+{ 0xe871, 0 }, //l7r4a.p3d
+{ 0x88f2, 0 }, //l7r4b.p3d
+{ 0xcfa8, 0 }, //l7r6.p3d
+{ 0x2f2d, 0 }, //l7r7.p3d
+{ 0x2d2f, 0 }, //l7z1.p3d
+{ 0xcfac, 0 }, //l7z2.p3d
+{ 0x2f21, 0 }, //l7z3.p3d
+{ 0xd1a6, 0 }, //l7z4.p3d
+{ 0xd1a0, 0 }, //l7z6.p3d
+{ 0x3125, 0 } }; //l7z7.p3d
diff --git a/game/code/data/PersistentWorldManager.cpp b/game/code/data/PersistentWorldManager.cpp
new file mode 100644
index 0000000..eba5edf
--- /dev/null
+++ b/game/code/data/PersistentWorldManager.cpp
@@ -0,0 +1,199 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: PersistentWorldManager.cpp
+//
+// Description: Implementation of class PersistenWorldManager
+//
+// History:
+// 25 Feb 2003 James Harrison Created for SRR2
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#ifndef RAD_RELEASE
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#endif
+
+//========================================
+// Project Includes
+//========================================
+#include <data/persistentworldmanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/charactersheet/charactersheet.h>
+#include <mission/gameplaymanager.h>
+#include <render/Enums/RenderEnums.h>
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+PersistentWorldManager* PersistentWorldManager::spInstance = 0;
+
+#include <data/PersistentSectors.cpp>
+
+/*=============================================================================
+Construct manager and allocate arrays. Makes sure you have an appropriate
+memory pushed before you get here. The state information isn't that big though.
+=============================================================================*/
+PersistentWorldManager::PersistentWorldManager() :
+ mCurSectorLoad( 0 )
+{}
+
+/*=============================================================================
+All the memory for the world state is freed.
+=============================================================================*/
+PersistentWorldManager::~PersistentWorldManager()
+{}
+
+/*=============================================================================
+Ye'olde standard methods for creating, getting, and destroying the singleton.
+This should be the only way for outsiders to get at the manager.
+=============================================================================*/
+PersistentWorldManager* PersistentWorldManager::CreateInstance( void )
+{
+ rAssertMsg( spInstance == 0, "PersistentWorldManager already created.\n" );
+ spInstance = new PersistentWorldManager;
+ rAssert( spInstance );
+ return PersistentWorldManager::GetInstance();
+}
+
+PersistentWorldManager* PersistentWorldManager::GetInstance( void )
+{
+ rAssertMsg( spInstance != 0, "PersistentWorldManager has not been created yet.\n" );
+ return spInstance;
+}
+
+void PersistentWorldManager::DestroyInstance( void )
+{
+ rAssertMsg( spInstance != 0, "PersistentWorldManager has not been created.\n" );
+ delete spInstance;
+ spInstance = 0;
+}
+
+/*=============================================================================
+Call this when you begin loading a dynamic zone/rail (i.e. a sector). This is
+very important because to save space the manager assumes that objects are
+always loaded in the same order after this call.
+=============================================================================*/
+void PersistentWorldManager::OnSectorLoad( tUID Sector )
+{
+ int i = 0;
+ for( ; i < NUM_SECTORS; ++i )
+ {
+ if( sSectorMap[ i ].Sector == (unsigned short)( Sector & static_cast< tUID >( 0xFFFF ) ) )
+ {
+ break;
+ }
+ }
+ if( i >= NUM_SECTORS )
+ {
+ // this isn't a sector we are concerned with.
+ return;
+ }
+ sSectorMap[ i ].CurIndex = 0;
+ mCurSectorLoad = i;
+}
+
+void PersistentWorldManager::OnLevelLoad( int LevelNum )
+{
+ int i = NUM_PERSISTENT_SECTORS - ( (int)RenderEnums::numLevels - LevelNum );
+
+ rTuneAssert( i >= 0 && i < NUM_SECTORS );
+ sSectorMap[ i ].CurIndex = 0;
+}
+
+/*=============================================================================
+Call this to get a unique ID for a persistent object. Pass the ID back to the
+manager when the object breaks. Note that the object name isn't used right now,
+the manager assumes the objects are always loaded (i.e. this method gets called)
+in the same order.
+If you get back a -1 we don't track objects in that sector. If you get back a
+-2 then we do track the object but it's already been busted.
+=============================================================================*/
+short PersistentWorldManager::GetPersistentObjectID( tUID Sector, tUID ObjectName )
+{
+ unsigned char sectorIndex = mCurSectorLoad;
+ short rv = -1;
+ int i = 0;
+ for( ; i < NUM_SECTORS; ++i )
+ {
+ if( sSectorMap[ sectorIndex ].Sector == (unsigned short)( Sector & static_cast< tUID >( 0xFFFF ) ) )
+ {
+ mCurSectorLoad = sectorIndex;
+ if( CheckObject( sectorIndex, sSectorMap[ sectorIndex ].CurIndex ) )
+ {
+ rAssert( static_cast<unsigned char>( sSectorMap[sectorIndex].CurIndex ) < NUM_PERSISTENT_OBJECTS ); // Out of bits for this zone/rail.
+ rv = ( sectorIndex << 8 ) | sSectorMap[ sectorIndex ].CurIndex;
+ }
+ else
+ {
+ rv = -2;
+ }
+ ++(sSectorMap[ sectorIndex ].CurIndex);
+ return rv;
+ }
+ sectorIndex = ( sectorIndex + 1 ) % NUM_SECTORS;
+ }
+ rAssert(i < NUM_SECTORS); // Oh-oh. We requested an ID for an object in a sector we aren't tracking.
+ return rv;
+}
+
+/*=============================================================================
+This call is for objects which you don't want to associate with a particular
+sector. Basically there is an extra sector for each level which is for then
+global. Needless to say you can't put too many objects in there.
+=============================================================================*/
+short PersistentWorldManager::GetPersistentObjectID( void )
+{
+ // Figure out a sector index by going to the last sectors in the array.
+ RenderEnums::LevelEnum levelEnum = GetGameplayManager()->GetCurrentLevelIndex();
+ int sectorIndex = NUM_PERSISTENT_SECTORS - ( RenderEnums::numLevels - levelEnum );
+ short rv = -1;
+ if( CheckObject( sectorIndex, sSectorMap[ sectorIndex ].CurIndex ) )
+ {
+ rv = ( sectorIndex << 8 ) | sSectorMap[ sectorIndex ].CurIndex;
+ }
+ else
+ {
+ rv = -2;
+ }
+ ++(sSectorMap[ sectorIndex ].CurIndex);
+ return rv;
+}
+
+/*=============================================================================
+Call this when you want to flag an object as gone. Pass back the ID you got
+with the call to GetPersistentObjectID() method.
+=============================================================================*/
+void PersistentWorldManager::OnObjectBreak( short ObjectID )
+{
+ if( ObjectID < 0 )
+ {
+ return;
+ }
+ unsigned char sectorIndex = ( ObjectID >> 8 );
+ rAssert( sectorIndex < NUM_PERSISTENT_SECTORS );
+ unsigned char objectIndex = ( ObjectID & 0xFF );
+ rAssert( objectIndex < NUM_PERSISTENT_OBJECTS );
+ unsigned char bitMask = ~( 1 << ( objectIndex & 0x7 ) );
+ int stateIndex = ( sectorIndex * NUM_BYTES_PER_SECTOR ) + ( objectIndex >> 3 );
+ unsigned char* objectStates = GetCharacterSheetManager()->GetPersistentObjectStates();
+ objectStates[ stateIndex ] &= bitMask;
+}
+
+/*=============================================================================
+Check if an object is gone. Just checks the array to see if the bit is cleared.
+=============================================================================*/
+bool PersistentWorldManager::CheckObject( unsigned char SectorIndex, unsigned char ObjectIndex ) const
+{
+ rAssert( SectorIndex < NUM_PERSISTENT_SECTORS );
+ rAssert( ObjectIndex < NUM_PERSISTENT_OBJECTS );
+ unsigned char bitMask = 1 << ( ObjectIndex & 0x7 );
+ short stateIndex = ( SectorIndex * NUM_BYTES_PER_SECTOR ) + ( ObjectIndex >> 3 );
+ unsigned char* objectStates = GetCharacterSheetManager()->GetPersistentObjectStates();
+ return ( objectStates[ stateIndex ] & bitMask ) != 0;
+} \ No newline at end of file
diff --git a/game/code/data/PersistentWorldManager.h b/game/code/data/PersistentWorldManager.h
new file mode 100644
index 0000000..5b1fc88
--- /dev/null
+++ b/game/code/data/PersistentWorldManager.h
@@ -0,0 +1,50 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: PersistentWorldManager
+//
+// Description: Manage the state of the world we want to keep during gameplay
+// and record to memory card.
+//
+// Authors: James Harrison
+//
+// Revisions Date Author Revision
+// 24 Feb James Harrison Created for SRR2
+//
+//===========================================================================
+
+#ifndef PERSISTENT_WORLD_MANAGER_H
+#define PERSISTENT_WORLD_MANAGER_H
+
+class PersistentWorldManager
+{
+public:
+ // Static Methods for accessing this singleton.
+ static PersistentWorldManager* CreateInstance();
+ static void DestroyInstance();
+ static PersistentWorldManager* GetInstance();
+
+ void OnSectorLoad( tUID Sector );
+ void OnLevelLoad( int LevelNum );
+ short GetPersistentObjectID( tUID Sector, tUID ObjectName = 0 );
+ short GetPersistentObjectID( void ); // This is a level based object not associated with a particular sector.
+ void OnObjectBreak( short ObjectID );
+
+protected:
+ PersistentWorldManager();
+ ~PersistentWorldManager();
+ PersistentWorldManager( const PersistentWorldManager& );
+ PersistentWorldManager& operator= ( const PersistentWorldManager& );
+
+ bool CheckObject( unsigned char SectorIndex, unsigned char ObjectIndex ) const;
+
+ // Pointer to the one and only instance of this singleton.
+ static PersistentWorldManager* spInstance;
+
+ unsigned char mCurSectorLoad; // Just to speed up the searches.
+};
+
+// Standard syntactic sugar for getting at this singleton.
+inline PersistentWorldManager* GetPersistentWorldManager() { return PersistentWorldManager::GetInstance(); }
+
+#endif //#ifndef PERSISTENT_WORLD_MANAGER_H \ No newline at end of file
diff --git a/game/code/data/alldata.cpp b/game/code/data/alldata.cpp
new file mode 100644
index 0000000..f704ded
--- /dev/null
+++ b/game/code/data/alldata.cpp
@@ -0,0 +1,3 @@
+#include <data/gamedatamanager.cpp>
+#include <data/savegameinfo.cpp>
+#include <data/persistentworldmanager.cpp> \ No newline at end of file
diff --git a/game/code/data/config/configstring.cpp b/game/code/data/config/configstring.cpp
new file mode 100644
index 0000000..5065769
--- /dev/null
+++ b/game/code/data/config/configstring.cpp
@@ -0,0 +1,240 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ConfigString
+//
+// Description: Implementation of the ConfigString class.
+//
+// Authors: Ziemek Trzesicki
+//
+// Revisions Date Author Revision
+// 2003/05/05 ziemek Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+
+#include <data/config/configstring.h>
+
+//==============================================================================
+// ConfigString constructor
+//==============================================================================
+
+ConfigString::ConfigString( ConfigStringMode mode, int size )
+{
+ rAssert( size > 0 );
+
+ mMode = mode;
+ mBuffer = new char[size + 1];
+ mCursor = mBuffer;
+ mSize = size;
+
+ // init the buffer
+ mBuffer[0] = mBuffer[size] = '\0';
+}
+
+//==============================================================================
+// ConfigString destructor
+//==============================================================================
+
+ConfigString::~ConfigString()
+{
+ delete [] mBuffer;
+}
+
+//==============================================================================
+// ConfigString::ReadSection
+//==============================================================================
+
+bool ConfigString::ReadSection( char* section )
+{
+ rAssert( mMode == Read );
+
+ char line[MaxLength];
+
+ if( GetLine(line) )
+ {
+ if( line[0] == SectionTag )
+ {
+ strcpy( section, line + 1 );
+ return true;
+ }
+ else
+ {
+ // Try reading a section from the next line.
+ ReadSection( section );
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+}
+
+//==============================================================================
+// ConfigString::ReadProperty
+//==============================================================================
+
+bool ConfigString::ReadProperty( char* property, char* value )
+{
+ rAssert( mMode == Read );
+
+ char line[MaxLength];
+ char* oldCursor = mCursor;
+
+ if( GetLine(line) )
+ {
+ if( line[0] == SectionTag )
+ {
+ // rewind - it's a new section.
+ mCursor = oldCursor;
+ return false;
+ }
+ else
+ {
+ // search for a property define.
+ char* prop_def = strchr( line, PropertyTag );
+ if( prop_def != NULL )
+ {
+ *prop_def = '\0';
+ strcpy( property, line );
+ strcpy( value, prop_def+1 );
+
+ return true;
+ }
+ else
+ {
+ // Skip the invalid line.
+ // Call read property recursively to read the next line.
+ return ReadProperty( property, value );
+ }
+ }
+ }
+ else
+ {
+ return false;
+ }
+}
+
+//==============================================================================
+// ConfigString::WriteSection
+//==============================================================================
+
+void ConfigString::WriteSection( const char* section )
+{
+ rAssert( mMode == Write );
+
+ // Make sure it's not too long.
+ size_t len = strlen( section );
+ rAssert( len + 1 < MaxLength );
+
+ // Make sure we have room in the string buffer (extra chars for # and \n and \0).
+ bool HaveRoom = ( mCursor - mBuffer ) + len + 6 < (size_t) mSize;
+ rAssert( HaveRoom );
+
+ // Rough guide of whether this is the first section
+ bool FirstSection = mCursor == mBuffer;
+
+ if( HaveRoom )
+ {
+ if( !FirstSection )
+ {
+ *mCursor = '\r';
+ mCursor++;
+
+ *mCursor = '\n';
+ mCursor++;
+ }
+
+ *mCursor = SectionTag;
+ mCursor++;
+
+ strcpy( mCursor, section );
+ mCursor += len;
+
+ *mCursor = '\r';
+ mCursor++;
+
+ *mCursor = '\n';
+ mCursor++;
+
+ *mCursor = '\0';
+ }
+}
+
+//==============================================================================
+// ConfigString::WriteProperty
+//==============================================================================
+
+void ConfigString::WriteProperty( const char* property, const char* value )
+{
+ rAssert( mMode == Write );
+
+ size_t lenp = strlen( property );
+ size_t lenv = strlen( value );
+
+ // Make sure it's not too long
+ rAssert( lenp + lenv + 1 < MaxLength );
+
+ // Make sure we have room in the string buffer (extra 3 for = and \n and \0).
+ bool HaveRoom = ( mCursor - mBuffer ) + lenp + lenv + 4 < (size_t) mSize;
+ rAssert( HaveRoom );
+
+ if( HaveRoom )
+ {
+ strcpy( mCursor, property );
+ mCursor += lenp;
+
+ *mCursor = PropertyTag;
+ mCursor++;
+
+ strcpy( mCursor, value );
+ mCursor += lenv;
+
+ *mCursor = '\r';
+ mCursor++;
+
+ *mCursor = '\n';
+ mCursor++;
+
+ *mCursor = '\0';
+ }
+}
+
+//==============================================================================
+// ConfigString::GetLine
+//==============================================================================
+
+bool ConfigString::GetLine( char* buffer )
+{
+ // skip white space
+ while( *mCursor == ' ' || *mCursor == '\t' || *mCursor == '\n' || *mCursor == '\r' )
+ {
+ mCursor++;
+ }
+
+ // Copy the line
+ int i = 0;
+ while( *mCursor != '\0' && *mCursor != '\n' && *mCursor != '\r' )
+ {
+ if( i < MaxLength )
+ {
+ buffer[i++] = *mCursor;
+ }
+
+ mCursor++;
+ }
+
+ // Trim trailing white space
+ while( i > 0 && ( buffer[i-1] == ' ' || buffer[i-1] == '\t' ) )
+ {
+ i--;
+ }
+
+ buffer[i] = '\0';
+
+ return i > 0;
+} \ No newline at end of file
diff --git a/game/code/data/config/configstring.h b/game/code/data/config/configstring.h
new file mode 100644
index 0000000..6e6380e
--- /dev/null
+++ b/game/code/data/config/configstring.h
@@ -0,0 +1,84 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ConfigString
+//
+// Description: Game configuration string which builds the concepts of
+// properties and sections around strings. Really simple to
+// get the job done.
+//
+// Authors: Ziemek Trzesicki
+//
+// Revisions Date Author Revision
+// 2003/05/02 ziemek Created for SRR2
+//
+//===========================================================================
+
+#ifndef CONFIGSTRING_H
+#define CONFIGSTRING_H
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class ConfigString
+{
+public:
+ // The character used to tag sections.
+ // Cannot be used in names.
+ static const char SectionTag = '#';
+
+ // The character used to define properties.
+ // Cannot be used in property keys.
+ static const char PropertyTag = '=';
+
+ // The maximum length of a section/property name or property value.
+ static const int MaxLength = 80;
+
+ // Defines the two modes that config strings can be in.
+ enum ConfigStringMode
+ {
+ Read,
+ Write
+ };
+
+public:
+ // Creates a config string.
+ // For reading, size is the size of the string buffer.
+ // For writing, size is the max size of the buffer.
+ ConfigString( ConfigStringMode mode, int size );
+ ~ConfigString();
+
+ // Returns the mode - read / write
+ ConfigStringMode GetMode() const { return mMode; }
+ // Returns the string buffer for reading/writing to a file.
+ char* GetBuffer() { return mBuffer; }
+ // Returns the size of hte string buffer.
+ int GetSize() const { return mSize; }
+
+ // Reads the next section title, returning true if one is found.
+ bool ReadSection( char* section );
+ // Reads the next property. these are bracketed by sections.
+ bool ReadProperty( char* property, char* value );
+
+ // Writes a section. These bracket properties.
+ void WriteSection( const char* section );
+ // Writes a property.
+ void WriteProperty( const char* property, const char* value );
+
+private:
+ // Reads the next non-empty line starting from the cursor.
+ // Saves a max number of <MaxLength> characters to buffer, to avoid overflows.
+ // The cursor is advanced to the end of the line regardless.
+ // Returns true if a line is read, false if there are no more.
+ bool GetLine( char* buffer );
+
+private:
+ ConfigStringMode mMode;
+ char* mBuffer;
+ char* mCursor;
+
+ int mSize;
+};
+
+#endif // CONFIGSTRING_H
diff --git a/game/code/data/config/gameconfig.h b/game/code/data/config/gameconfig.h
new file mode 100644
index 0000000..91061f9
--- /dev/null
+++ b/game/code/data/config/gameconfig.h
@@ -0,0 +1,41 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: GameConfig
+//
+// Description: GameConfig Interface and Constants.
+//
+// Authors: Ziemek Trzesicki
+//
+// Revisions Date Author Revision
+// 2003/05/02 ziemek Created for SRR2
+//
+//===========================================================================
+
+#ifndef GAMECONFIG_H
+#define GAMECONFIG_H
+
+#include <data/config/configstring.h>
+
+struct GameConfigHandler
+{
+ // Returns the name/title of the configuration data.
+ // For example - "InputControls".
+ virtual const char* GetConfigName() const = 0;
+
+ // Returns the number of properties stored by this config handler.
+ virtual int GetNumProperties() const = 0;
+
+ // Called to load the default settings.
+ // Used in case that no configuration exists in the file for
+ // this handler, or simply to revert the config to the default.
+ virtual void LoadDefaults() = 0;
+
+ // Called to load configuration data from the config string.
+ virtual void LoadConfig( ConfigString& config ) = 0;
+
+ // Called to save configuration data to the config string.
+ virtual void SaveConfig( ConfigString& config ) = 0;
+};
+
+#endif // GAMECONFIG_H
diff --git a/game/code/data/config/gameconfigmanager.cpp b/game/code/data/config/gameconfigmanager.cpp
new file mode 100644
index 0000000..997a40c
--- /dev/null
+++ b/game/code/data/config/gameconfigmanager.cpp
@@ -0,0 +1,206 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: GameConfigManager
+//
+// Description: Implementation for the GameConfigManager class.
+//
+// Authors: Ziemek Trzesicki
+//
+// Revisions Date Author Revision
+// 2003/05/07 ziemek Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <data/config/gameconfigmanager.h>
+#include <data/config/configstring.h>
+#include <data/memcard/memorycardmanager.h>
+
+#include <memory/srrmemory.h>
+
+//===========================================================================
+// Static Member Initializations
+//===========================================================================
+
+GameConfigManager* GameConfigManager::spInstance = NULL;
+
+const char* GameConfigManager::ConfigFilename = "simpsons.ini";
+
+//===========================================================================
+// Static Public Member Functions
+//===========================================================================
+
+GameConfigManager* GameConfigManager::CreateInstance()
+{
+ MEMTRACK_PUSH_GROUP( "GameConfigManager" );
+ spInstance = new GameConfigManager;
+ rAssert( spInstance != NULL );
+ MEMTRACK_POP_GROUP( "GameConfigManager" );
+
+ return spInstance;
+}
+
+void GameConfigManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete spInstance;
+ spInstance = NULL;
+}
+
+GameConfigManager* GameConfigManager::GetInstance()
+{
+ rAssert( spInstance != NULL );
+ return spInstance;
+}
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+GameConfigManager::GameConfigManager()
+: mNumConfig( 0 ),
+ mConfigSize( 0 )
+{
+ for( int i = 0; i < MaxGameConfigs; i++ )
+ {
+ mConfig[ i ] = NULL;
+ }
+}
+
+GameConfigManager::~GameConfigManager()
+{}
+
+void GameConfigManager::RegisterConfig( GameConfigHandler* config )
+{
+ // validate the config
+ rAssert( config != NULL );
+ rAssert( strlen( config->GetConfigName() ) > 0 );
+ rAssert( config->GetNumProperties() > 0 );
+
+ // make sure we have room
+ rAssertMsg( mNumConfig < MaxGameConfigs,
+ "ERROR: *** Exceeded maximum number of registered Game Config!" );
+
+ // add to registered list of Game Config
+ mConfig[ mNumConfig++ ] = config;
+
+ // update Game Config size (extra 1 cause of section name)
+ mConfigSize += ( config->GetNumProperties() + 1 ) * ConfigString::MaxLength;
+}
+
+bool GameConfigManager::LoadConfigFile()
+{
+ // Open the config file for reading.
+ IRadFile* radFile = NULL;
+ radFileOpenSync( &radFile,
+ ConfigFilename,
+ false,
+ OpenExisting );
+ rAssert( radFile );
+
+ bool success = false;
+
+ // If opened, read the config file.
+ if( radFile->IsOpen() && radFile->GetSize() > 0 )
+ {
+ unsigned size = radFile->GetSize();
+
+ // Make sure we're not swamped with illegally huge files.
+ if( (int) size > 2 * mConfigSize )
+ {
+ size = 2 * mConfigSize;
+ }
+
+ ConfigString config( ConfigString::Read, size );
+
+ radFile->ReadSync( config.GetBuffer(), size );
+
+ LoadFromConfig( config );
+
+ success = true;
+ }
+
+ radFile->Release();
+ return success;
+}
+
+bool GameConfigManager::SaveConfigFile()
+{
+ // Save the configuration to a config string.
+ ConfigString config( ConfigString::Write, mConfigSize );
+ SaveToConfig( config );
+
+ // Open a file for saving.
+ IRadFile* radFile = NULL;
+ radFileOpenSync( &radFile,
+ ConfigFilename,
+ true,
+ CreateAlways );
+
+ rAssert( radFile );
+
+ bool success = false;
+
+ // Save the configuration.
+ if( radFile->IsOpen() )
+ {
+ radFile->WriteSync( config.GetBuffer(),
+ strlen( config.GetBuffer() ) );
+
+ radFile->CommitSync();
+
+ success = true;
+ }
+
+ radFile->Release();
+ return success;
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+void GameConfigManager::LoadFromConfig( ConfigString& config )
+{
+ rAssert( config.GetMode() == ConfigString::Read );
+
+ char section[ ConfigString::MaxLength ];
+ int i;
+
+ while( config.ReadSection( section ) )
+ {
+ // Find the config handler for the section.
+ for( i = 0; i < mNumConfig; i++ )
+ {
+ if( strcmp( section, mConfig[i]->GetConfigName() ) == 0 )
+ {
+ break;
+ }
+ }
+
+ // Check for unknown sections.
+ if( i == mNumConfig )
+ {
+ continue;
+ }
+
+ // Get the config handler to read the section
+ mConfig[i]->LoadConfig( config );
+ }
+}
+
+void GameConfigManager::SaveToConfig( ConfigString& config )
+{
+ rAssert( config.GetMode() == ConfigString::Write );
+
+ for( int i = 0; i < mNumConfig; i++ )
+ {
+ config.WriteSection( mConfig[i]->GetConfigName() );
+
+ mConfig[i]->SaveConfig( config );
+ }
+}
diff --git a/game/code/data/config/gameconfigmanager.h b/game/code/data/config/gameconfigmanager.h
new file mode 100644
index 0000000..de77e59
--- /dev/null
+++ b/game/code/data/config/gameconfigmanager.h
@@ -0,0 +1,85 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: GameConfigManager
+//
+// Description: Interface for the GameConfigManager class.
+//
+// Authors: Ziemek Trzesicki
+//
+// Revisions Date Author Revision
+// 2003/05/07 ziemek Created for SRR2
+//
+//===========================================================================
+
+#ifndef GAMECONFIGMANAGER_H
+#define GAMECONFIGMANAGER_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <data/config/gameconfig.h>
+#include <radfile.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class ConfigString;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class GameConfigManager
+{
+public:
+ // Static Methods for accessing this singleton.
+ static GameConfigManager* CreateInstance();
+ static void DestroyInstance();
+ static GameConfigManager* GetInstance();
+
+ static const char* ConfigFilename;
+ static const unsigned int MaxGameConfigs = 16;
+
+public:
+
+ // Registration of client game data
+ void RegisterConfig( GameConfigHandler* gdHandler );
+
+ // Loads the config file and applies loaded settings to
+ // registered config handlers.
+ bool LoadConfigFile();
+
+ // Prompts for config data from registered config handlers and
+ // saves it to the config file.
+ bool SaveConfigFile();
+
+private:
+ // Can only be created by CreateInstance().
+ GameConfigManager();
+ ~GameConfigManager();
+
+ // No copying or assignment.
+ GameConfigManager( const GameConfigManager& );
+ GameConfigManager& operator= ( const GameConfigManager& );
+
+ // Loads all config data from the config string.
+ void LoadFromConfig( ConfigString& config );
+ // Saves all config data to the config string.
+ void SaveToConfig( ConfigString& config );
+
+private:
+ // Pointer to the one and only instance of this singleton.
+ static GameConfigManager* spInstance;
+
+ GameConfigHandler* mConfig[ MaxGameConfigs ];
+ int mNumConfig;
+ int mConfigSize;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline GameConfigManager* GetGameConfigManager() { return( GameConfigManager::GetInstance() ); }
+
+#endif // GAMECONFIGMANAGER_H
diff --git a/game/code/data/gamedata.h b/game/code/data/gamedata.h
new file mode 100644
index 0000000..53c19bc
--- /dev/null
+++ b/game/code/data/gamedata.h
@@ -0,0 +1,62 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: GameData
+//
+// Description: GameData Interface and Constants.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/10/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GAMEDATA_H
+#define GAMEDATA_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+typedef unsigned char GameDataByte; // single byte of data
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+#ifdef RAD_WIN32
+const unsigned int NUM_GAME_SLOTS = 8; // number of available game slots
+#else
+const unsigned int NUM_GAME_SLOTS = 4; // number of available game slots
+#endif
+
+struct GameDataHandler
+{
+ virtual void LoadData( const GameDataByte* dataBuffer,
+ unsigned int numBytes ) = 0;
+
+ virtual void SaveData( GameDataByte* dataBuffer,
+ unsigned int numBytes ) = 0;
+
+ // reset data to defaults
+ //
+ virtual void ResetData() = 0;
+};
+
+struct GameData
+{
+ GameDataHandler* m_gdHandler;
+ unsigned int m_numBytes;
+
+#ifdef RAD_DEBUG
+ char m_name[ 64 ];
+#endif
+
+};
+
+#endif // GAMEDATA_H
diff --git a/game/code/data/gamedatamanager.cpp b/game/code/data/gamedatamanager.cpp
new file mode 100644
index 0000000..2f83b9c
--- /dev/null
+++ b/game/code/data/gamedatamanager.cpp
@@ -0,0 +1,1285 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: GameDataManager
+//
+// Description: Implementation of the GameDataManager class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/09/16 TChu Created for SRR2
+//
+//===========================================================================
+
+//#define PRINT_RAW_DATA // to debug output
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <data/gamedatamanager.h>
+#include <data/savegameinfo.h>
+#include <data/memcard/memorycardmanager.h>
+#include <string.h>
+
+#ifndef WORLD_BUILDER
+#include <memory/srrmemory.h>
+#else
+#define MEMTRACK_PUSH_GROUP(x)
+#define MEMTRACK_POP_GROUP(x)
+#define GMA_PERSISTENT 0
+#define GMA_DEFAULT 0
+#define GMA_TEMP 0
+#endif
+
+#include <radfile.hpp>
+#include <raddebugwatch.hpp>
+#include <radtime.hpp>
+
+#ifdef RAD_PS2
+ #include <radstring.hpp>
+#endif
+#ifdef RAD_PS2
+const unsigned int MAX_GAME_DATA_SIZE = 8600; // in bytes (since ps2 have different byte packing)
+#else
+const unsigned int MAX_GAME_DATA_SIZE = 7500; // in bytes
+#endif
+
+// Static pointer to instance of singleton.
+GameDataManager* GameDataManager::spInstance = NULL;
+
+//===========================================================================
+// Local Constants
+//===========================================================================
+
+#ifdef RAD_PS2
+ const char* SAVED_GAME_TITLE = "The Simpsons:Hit & Run"; // there should be no space betw. "Simpsons:" and "Hit"
+ // due to line break inserted there
+#endif
+
+#ifdef RAD_XBOX
+ // Xbox TCR Requirement!!!
+ //
+ #define ENABLE_MINIMUM_LOAD_SAVE_TIME
+ const unsigned int MINIMUM_LOAD_SAVE_TIME = 2000; // in msec
+#else
+ #define ENABLE_MINIMUM_LOAD_SAVE_TIME
+ const unsigned int MINIMUM_LOAD_SAVE_TIME = 1000; // in msec
+#endif
+
+#ifdef RAD_PS2
+ const unsigned int MINIMUM_DELETE_TIME = 3000; // in msec
+#else
+ const unsigned int MINIMUM_DELETE_TIME = 1000; // in msec
+#endif
+
+#ifdef RAD_PS2
+ /* Filename for PS2 saved games must be prefixed with the following:
+ *
+ * (BISLPS | BISLPM | BISCPS | BASLUS | BASCUS | BESLES | BESCES)-#####
+ *
+ * Example: "BASCUS-12345savegame.dat"
+ *
+ */
+ #ifdef PAL
+ #define PS2_FILENAME_PREFIX "BESLES-51897"
+ #else
+ #define PS2_FILENAME_PREFIX "BASLUS-20624"
+ #endif
+#else
+ #define PS2_FILENAME_PREFIX ""
+#endif
+
+#ifdef DEBUGWATCH
+ const char* WATCHER_NAMESPACE = "Game Data Manager";
+
+ unsigned int g_wCurrentSlot = 0;
+
+ static void LoadGameFromWatcher()
+ {
+ if( GetMemoryCardManager()->GetCurrentDrive() != NULL )
+ {
+ GetGameDataManager()->LoadGame( g_wCurrentSlot );
+ }
+ else
+ {
+ rTunePrintf( "*** Can't load game! No memory device selected!\n" );
+ }
+ }
+
+ static void SaveGameFromWatcher()
+ {
+ if( GetMemoryCardManager()->GetCurrentDrive() != NULL )
+ {
+ GetGameDataManager()->SaveGame( g_wCurrentSlot );
+ }
+ else
+ {
+ rTunePrintf( "*** Can't save game! No memory device selected!\n" );
+ }
+ }
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//==============================================================================
+// GameDataManager::CreateInstance
+//==============================================================================
+//
+// Description: - Creates the Game Data Manager.
+//
+// Parameters: None.
+//
+// Return: Pointer to the GameDataManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+GameDataManager* GameDataManager::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "GameDataManager" );
+ spInstance = new GameDataManager;
+ rAssert( spInstance != NULL );
+MEMTRACK_POP_GROUP( "GameDataManager" );
+
+ // create other singletons owned by GameDataManager
+ //
+ MemoryCardManager* pMemoryCardManager = MemoryCardManager::CreateInstance();
+ rAssert( pMemoryCardManager != NULL );
+
+ return spInstance;
+}
+
+//==============================================================================
+// GameDataManager::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the Game Data Manager.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void GameDataManager::DestroyInstance()
+{
+ // destroy other singletons owned by GameDataManager
+ //
+ MemoryCardManager::DestroyInstance();
+
+ rAssert( spInstance != NULL );
+
+ delete spInstance;
+ spInstance = NULL;
+}
+
+//==============================================================================
+// GameDataManager::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the GameDataManager singleton.
+// - Creates the GameDataManager if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the GameDataManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+GameDataManager* GameDataManager::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+//===========================================================================
+// GameDataManager::GameDataManager
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+GameDataManager::GameDataManager()
+: m_numRegisteredGameData( 0 ),
+ m_gameDataBuffer( NULL ),
+ m_gameDataSize( 0 ),
+ m_gameDataLoadCallback( NULL ),
+ m_gameDataSaveCallback( NULL ),
+ m_gameDataDeleteCallback( NULL ),
+#ifdef ENABLE_MINIMUM_LOAD_SAVE_TIME
+ m_minimumLoadSaveTime( MINIMUM_LOAD_SAVE_TIME ),
+#else
+ m_minimumLoadSaveTime( 0 ),
+#endif
+ m_elapsedOperationTime( 0 ),
+ m_isGameLoaded( false ),
+ m_saveGameInfoHandler( NULL ),
+ m_radFile( NULL ),
+ m_currentFileOperation( FILE_OP_NONE )
+{
+ rAssertMsg( sizeof( GameDataByte ) == 1,
+ "WARNING: *** Size of GameDataByte is not 1 byte! Is that OK??" );
+
+ for( unsigned int i = 0; i < sizeof( m_registeredGameData ) /
+ sizeof( m_registeredGameData[ 0 ] ); i++ )
+ {
+ m_registeredGameData[ i ] = NULL;
+ }
+
+ // create the save game info handler, and register it before anything else
+ //
+ m_saveGameInfoHandler = new SaveGameInfo;
+ rAssert( m_saveGameInfoHandler );
+ this->RegisterGameData( m_saveGameInfoHandler,
+ SaveGameInfo::GetSize(),
+ "Save Game Info" );
+}
+
+//===========================================================================
+// GameDataManager::~GameDataManager
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+GameDataManager::~GameDataManager()
+{
+#ifdef DEBUGWATCH
+ ::radDbgWatchDelete( &g_wCurrentSlot );
+#endif
+
+ if( m_saveGameInfoHandler != NULL )
+ {
+ delete m_saveGameInfoHandler;
+ m_saveGameInfoHandler = NULL;
+ }
+
+ for( unsigned int i = 0; i < m_numRegisteredGameData; i++ )
+ {
+ if( m_registeredGameData[ i ] != NULL )
+ {
+ delete m_registeredGameData[ i ];
+ m_registeredGameData[ i ] = NULL;
+ }
+ }
+}
+
+
+void
+GameDataManager::Init()
+{
+MEMTRACK_PUSH_GROUP( "GameDataManager" );
+ // initialize memory card manager
+ //
+ GetMemoryCardManager()->Init( this );
+
+#ifdef DEBUGWATCH
+ ::radDbgWatchAddUnsignedInt( &g_wCurrentSlot,
+ "Game Slot",
+ WATCHER_NAMESPACE,
+ NULL, NULL, 0, NUM_GAME_SLOTS - 1 );
+
+ ::radDbgWatchAddFunction( "Load Game",
+ (RADDEBUGWATCH_CALLBACK)LoadGameFromWatcher,
+ NULL,
+ WATCHER_NAMESPACE );
+
+ ::radDbgWatchAddFunction( "Save Game",
+ (RADDEBUGWATCH_CALLBACK)SaveGameFromWatcher,
+ NULL,
+ WATCHER_NAMESPACE );
+#endif
+MEMTRACK_POP_GROUP( "GameDataManager" );
+}
+
+void
+GameDataManager::Update( unsigned int elapsedTime )
+{
+ if( m_currentFileOperation != FILE_OP_NONE )
+ {
+#ifdef ENABLE_MINIMUM_LOAD_SAVE_TIME
+ if( m_elapsedOperationTime > m_minimumLoadSaveTime )
+ {
+ if( m_currentFileOperation == FILE_OP_LOAD_COMPLETED )
+ {
+ if( m_gameDataLoadCallback != NULL )
+ {
+ m_gameDataLoadCallback->OnLoadGameComplete( Success );
+ }
+
+ m_currentFileOperation = FILE_OP_NONE;
+ }
+
+ if( m_currentFileOperation == FILE_OP_SAVE_COMPLETED )
+ {
+ if( m_gameDataSaveCallback != NULL )
+ {
+ m_gameDataSaveCallback->OnSaveGameComplete( Success );
+ }
+
+ m_currentFileOperation = FILE_OP_NONE;
+ }
+ }
+#endif // ENABLE_MINIMUM_LOAD_SAVE_TIME
+
+ if( m_elapsedOperationTime > MINIMUM_DELETE_TIME )
+ {
+ if( m_currentFileOperation == FILE_OP_DELETE_COMPLETED )
+ {
+ if( m_gameDataDeleteCallback != NULL )
+ {
+ m_gameDataDeleteCallback->OnDeleteGameComplete( m_lastError );
+ m_gameDataDeleteCallback = NULL;
+ }
+
+ m_currentFileOperation = FILE_OP_NONE;
+ }
+ }
+
+ m_elapsedOperationTime += elapsedTime;
+ }
+}
+
+void
+GameDataManager::RegisterGameData( GameDataHandler* gdHandler,
+ unsigned int numBytes,
+ const char* name )
+{
+MEMTRACK_PUSH_GROUP( "GameDataManager" );
+ rAssert( gdHandler != NULL );
+ rAssert( numBytes >= 0 );
+
+ rAssertMsg( m_numRegisteredGameData < MAX_NUM_GAME_DATA,
+ "ERROR: *** Exceeded maximum number of registered game data!" );
+
+#ifndef WORLD_BUILDER
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+#endif
+
+ // create new game data
+ //
+ GameData* newGameData = new GameData;
+ rAssert( newGameData );
+ newGameData->m_gdHandler = gdHandler;
+ newGameData->m_numBytes = numBytes;
+
+#ifdef RAD_DEBUG
+ if( name != NULL )
+ {
+ strncpy( newGameData->m_name, name, sizeof( newGameData->m_name ) );
+ }
+ else
+ {
+ strcpy( newGameData->m_name, "" );
+ }
+
+ rTunePrintf( ">> Registered Game Data [%d]: %s (%d bytes)\n",
+ m_numRegisteredGameData,
+ newGameData->m_name,
+ newGameData->m_numBytes );
+#endif
+
+ // add to registered list of game data
+ //
+ m_registeredGameData[ m_numRegisteredGameData ] = newGameData;
+ m_numRegisteredGameData++;
+
+ // update game data size
+ //
+ m_gameDataSize += numBytes;
+
+ rReleasePrintf( ">> Total Game Data Size = %d bytes\n", m_gameDataSize );
+
+// rReleaseAssertMsg( m_gameDataSize <= MAX_GAME_DATA_SIZE,
+// "ERROR: *** Max Game Data Size Exceeded!" );
+
+#ifndef WORLD_BUILDER
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+#endif
+
+MEMTRACK_POP_GROUP( "GameDataManager" );
+}
+
+void
+GameDataManager::LoadGame( unsigned int slot, GameDataLoadCallback* callback, const char *load_filename )
+{
+ rAssertMsg( slot < NUM_GAME_SLOTS, "ERROR: *** Invalid game slot!" );
+
+ rReleasePrintf( ">> Loading game from slot %d ... ...\n", slot + 1 );
+
+ m_gameDataLoadCallback = callback;
+
+ // open save game file for reading
+ //
+ char filename[ radFileFilenameMax + 1 ];
+ if (load_filename==NULL) // set up predefined filename
+ this->FormatSavedGameFilename( filename,
+ sizeof( filename ),
+ slot );
+ else
+ strcpy( filename, load_filename ); // filename is passed in for xbox
+ IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
+ rAssert( currentDrive );
+#ifndef RAD_WIN32
+ currentDrive->SaveGameOpenAsync( &m_radFile,
+ filename,
+ false,
+ OpenExisting,
+ NULL,
+ m_gameDataSize,
+ true);
+#else
+ currentDrive->FileOpenAsync( &m_radFile,
+ filename,
+ false,
+ OpenExisting );
+#endif // ~RAD_WIN32
+ rAssert( m_radFile );
+ m_radFile->AddCompletionCallback( this, NULL );
+
+ // allocate temp memory for game data buffer
+ //
+ rAssert( m_gameDataBuffer == NULL );
+
+#ifndef WORLD_BUILDER
+ HeapMgr()->PushHeap( GMA_TEMP );
+#endif
+
+ m_gameDataBuffer = new GameDataByte[ m_gameDataSize ];
+
+#ifndef WORLD_BUILDER
+ HeapMgr()->PopHeap( GMA_TEMP );
+#endif
+
+ rAssert( m_gameDataBuffer != NULL );
+
+ m_currentFileOperation = FILE_OP_OPEN_FOR_READING;
+ m_elapsedOperationTime = 0;
+}
+
+radFileError GameDataManager::DeleteGame( const char *fileName,
+ bool async,
+ GameDataDeleteCallback* callback )
+{
+ rAssert(m_currentFileOperation == FILE_OP_NONE);
+ m_currentFileOperation = FILE_OP_DELETE;
+
+ IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
+
+ rAssert( currentDrive );
+
+ m_lastError = Success;
+
+ if( async )
+ {
+ currentDrive->DestroyFileAsync( fileName, true );
+ currentDrive->AddCompletionCallback( this, NULL );
+
+ rAssert( callback != NULL );
+ m_gameDataDeleteCallback = callback;
+
+ m_elapsedOperationTime = 0;
+ }
+ else
+ {
+ currentDrive->DestroyFileSync( fileName, true );
+ m_currentFileOperation = FILE_OP_NONE;
+ }
+
+ return m_lastError;
+}
+
+void
+GameDataManager::SaveGame( unsigned int slot, GameDataSaveCallback* callback )
+{
+ rAssertMsg( slot < NUM_GAME_SLOTS, "ERROR: *** Invalid game slot!" );
+
+ rReleasePrintf( ">> Saving game to slot %d ... ...\n", slot + 1 );
+
+ m_gameDataSaveCallback = callback;
+
+ // allocate temp memory for game data buffer
+ //
+ rAssert( m_gameDataBuffer == NULL );
+#ifndef WORLD_BUILDER
+ HeapMgr()->PushHeap( GMA_TEMP );
+#endif
+
+ m_gameDataBuffer = new GameDataByte[ m_gameDataSize ];
+#ifndef WORLD_BUILDER
+ HeapMgr()->PopHeap( GMA_TEMP );
+#endif
+ rAssert( m_gameDataBuffer != NULL );
+
+ this->SaveAllData();
+
+ // update saved game title in memcard info before saving
+ //
+#ifdef RAD_GAMECUBE
+ char levelMissionInfo[ 32 ];
+ m_saveGameInfoHandler->FormatLevelMissionInfo( levelMissionInfo );
+
+ char savedGameTitle[ 32 ];
+ sprintf( savedGameTitle, "Slot %d %s", slot + 1, levelMissionInfo );
+
+ GetMemoryCardManager()->UpdateMemcardInfo( savedGameTitle );
+#endif
+#ifdef RAD_PS2
+ char levelMissionInfo[ 32 ];
+ m_saveGameInfoHandler->FormatLevelMissionInfo( levelMissionInfo );
+
+ char savedGameTitle[ 32 ];
+ sprintf( savedGameTitle, "%s (%d)", SAVED_GAME_TITLE, slot + 1 );
+
+ GetMemoryCardManager()->UpdateMemcardInfo( savedGameTitle, 13 );
+#endif
+#ifdef RAD_XBOX
+ GetMemoryCardManager()->UpdateMemcardInfo( NULL );
+#endif
+#ifdef RAD_WIN32
+ GetMemoryCardManager()->UpdateMemcardInfo( NULL );
+#endif
+
+ // open save game file for writing
+ //
+ char filename[ radFileFilenameMax + 1 ];
+ this->FormatSavedGameFilename( filename,
+ sizeof( filename ),
+ slot );
+
+ IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
+ rAssert( currentDrive );
+
+#ifdef RAD_XBOX
+ m_saveGameInfoHandler->FormatDisplay(filename, sizeof(filename)); // define filename for xbox
+#endif
+
+#ifndef RAD_WIN32
+ const radMemcardInfo* memcardInfo = GetMemoryCardManager()->GetMemcardInfo();
+ bool simpleName = false;
+#ifdef RAD_XBOX
+ simpleName = true; // set the flag so radcore doesn't process ':' and '/'
+#endif
+
+#ifdef RAD_PS2
+ radFileOpenFlags fileOpenFlags = OpenAlways;
+#else
+ radFileOpenFlags fileOpenFlags = CreateAlways;
+#endif
+ currentDrive->SaveGameOpenAsync( &m_radFile,
+ filename,
+ true,
+ fileOpenFlags,
+ const_cast<radMemcardInfo*>( memcardInfo ),
+ m_gameDataSize, simpleName );
+#else
+ currentDrive->FileOpenAsync( &m_radFile,
+ filename,
+ true,
+ CreateAlways );
+#endif // ~RAD_WIN32
+
+
+ rAssert( m_radFile );
+ m_radFile->AddCompletionCallback( this, NULL );
+
+ m_currentFileOperation = FILE_OP_OPEN_FOR_WRITING;
+ m_elapsedOperationTime = 0;
+}
+
+void
+GameDataManager::ResetGame()
+{
+ // tell all game data handlers to reset their data to defaults
+ //
+ for( unsigned int i = 0; i < m_numRegisteredGameData; i++ )
+ {
+ rAssert( m_registeredGameData[ i ] != NULL &&
+ m_registeredGameData[ i ]->m_gdHandler != NULL );
+
+ m_registeredGameData[ i ]->m_gdHandler->ResetData();
+ }
+
+ // game is no longer loaded
+ //
+ m_isGameLoaded = false;
+}
+
+void
+GameDataManager::SetMinimumLoadSaveTime( unsigned int minimumTime )
+{
+ m_minimumLoadSaveTime = minimumTime;
+}
+
+void
+GameDataManager::RestoreDefaultMinimumLoadSaveTime()
+{
+#ifdef ENABLE_MINIMUM_LOAD_SAVE_TIME
+ m_minimumLoadSaveTime = MINIMUM_LOAD_SAVE_TIME;
+#else
+ m_minimumLoadSaveTime = 0;
+#endif
+}
+
+bool
+GameDataManager::GetSaveGameInfo( IRadDrive* pDrive, unsigned int slot,
+ SaveGameInfo* saveGameInfo,
+ bool *file_corrupt_flag)
+{
+ rAssert( pDrive != NULL );
+ rAssert( saveGameInfo != NULL );
+ // open save game file for checking if it exists
+ //
+ char filename[ radFileFilenameMax + 1 ];
+
+#ifdef RAD_XBOX
+ // xbox, we get the filename from the drive instead of predefined slot based filename
+ IRadDrive::DirectoryInfo dir_info;
+ dir_info.m_Type = IRadDrive::DirectoryInfo::IsDone; // initialize first, in case it errs out
+
+
+ pDrive->FindFirstSync("*",&dir_info);
+
+ if (dir_info.m_Type==IRadDrive::DirectoryInfo::IsDone)
+ return false;
+
+ for (unsigned int i = 0; i < slot; i++)
+ {
+ dir_info.m_Type = IRadDrive::DirectoryInfo::IsDone; // initialize first, in case it errs out
+ pDrive->FindNextSync(&dir_info);
+ if (dir_info.m_Type==IRadDrive::DirectoryInfo::IsDone)
+ break;
+ }
+
+ if (dir_info.m_Type==IRadDrive::DirectoryInfo::IsDone)
+ return false;
+ strcpy(filename,dir_info.m_Name);
+
+#else
+ this->FormatSavedGameFilename( filename,
+ sizeof( filename ),
+ slot );
+#endif
+
+ m_lastError = Success; // want to know what is the error from opensync
+ if (file_corrupt_flag)
+ *file_corrupt_flag = false;
+
+
+#ifndef RAD_WIN32
+ bool simpleName = false;
+#ifdef RAD_XBOX
+ simpleName = true; // set up flag for xbox so radcore doesn't process ':' '/' character
+#endif
+ pDrive->SaveGameOpenSync( &m_radFile,
+ filename,
+ false,
+ OpenExisting,
+ 0,
+ 0,
+ simpleName);
+#else
+
+ eFileOperation temp = m_currentFileOperation;
+ m_currentFileOperation = FILE_OP_FILE_CHECK;
+ pDrive->FileOpenSync( &m_radFile,
+ filename,
+ false,
+ OpenExisting );
+
+ m_currentFileOperation = temp;
+
+#endif // ~RAD_WIN32
+
+ rAssert( m_radFile );
+ bool saveGameExists = false;
+// since for xbox we know the file is definitely there
+// we could get FileNotFound because of old files, just to cut down
+// on bug report, let's treat that as corrupt file
+#ifdef RAD_XBOX
+ if (m_lastError==FileNotFound)
+ m_lastError = DataCorrupt;
+#endif
+
+ if (m_lastError==DataCorrupt && file_corrupt_flag)
+ {
+ *file_corrupt_flag = true;
+ saveGameExists = true;
+ }
+ else
+ {
+ saveGameExists = m_radFile->IsOpen();
+ if( saveGameExists )
+ {
+ // read save game info from file (synchronously)
+ //
+ unsigned int numBytes = SaveGameInfo::GetSize();
+
+ rAssert( m_gameDataBuffer == NULL );
+ #ifndef WORLD_BUILDER
+ HeapMgr()->PushHeap( GMA_TEMP );
+ #endif
+ m_gameDataBuffer = new GameDataByte[ numBytes ];
+ #ifndef WORLD_BUILDER
+ HeapMgr()->PopHeap( GMA_TEMP );
+ #endif
+ rAssert( m_gameDataBuffer != NULL );
+
+ m_radFile->ReadSync( m_gameDataBuffer, numBytes );
+ saveGameInfo->LoadData( m_gameDataBuffer, numBytes );
+
+ if( m_gameDataBuffer != NULL )
+ {
+ delete [] m_gameDataBuffer;
+ m_gameDataBuffer = NULL;
+ }
+ }
+ }
+
+#ifdef RAD_XBOX
+ strcpy(saveGameInfo->m_displayFilename, filename); // use the filename as the display name
+#else
+ saveGameInfo->FormatDisplay( saveGameInfo->m_displayFilename, sizeof(saveGameInfo->m_displayFilename) );
+#endif
+
+ m_radFile->Release();
+ m_radFile = NULL;
+
+ return saveGameExists;
+}
+
+bool
+GameDataManager::DoesSaveGameExist( IRadDrive* pDrive, bool check_valid/* = true*/, bool forAllSlots )
+{
+ bool saveGameExists = false;
+ unsigned int numSavedGameExists = 0;
+
+ unsigned int to_check_file = NUM_GAME_SLOTS;
+#ifdef RAD_XBOX
+ to_check_file = 1; // on xbox since we query the drive for the file,
+ // just check first one
+#endif
+
+ // check if at least one saved game file exists on the drive
+ //
+ for( unsigned int i = 0; i < to_check_file; i++ )
+ {
+ SaveGameInfo saveGameInfo;
+ bool corrupt = false;
+ if( this->GetSaveGameInfo( pDrive, i, &saveGameInfo, &corrupt ) )
+ {
+ if( !check_valid ) // no need to check validity
+ {
+ saveGameExists = true;
+
+ if( forAllSlots )
+ {
+ numSavedGameExists++;
+ continue;
+ }
+
+ break;
+ }
+ // ok, we found a saved game, but let's make sure the saved game
+ // info data are valid so we at least know the file size is correct
+ //
+ else if( !corrupt && saveGameInfo.CheckData() )
+ {
+ // everything's cool, we have a valid saved game
+ //
+ saveGameExists = true;
+
+ if( forAllSlots )
+ {
+ numSavedGameExists++;
+ continue;
+ }
+
+ break;
+ }
+ }
+ else
+ {
+ // no save game file for current slot
+ //
+ if( forAllSlots )
+ {
+ // if checking for save game file exists in all slots, then
+ // we've found a slot w/out a save game file, so stop checking
+ //
+ break;
+ }
+ }
+ }
+
+ if( forAllSlots )
+ {
+ saveGameExists = (numSavedGameExists == NUM_GAME_SLOTS);
+ }
+
+ return saveGameExists;
+}
+
+bool
+GameDataManager::FindMostRecentSaveGame( IRadDrive* pDrive,
+ unsigned int& slot,
+ radDate& timeStamp )
+{
+ bool saveGameExists = false;
+
+ timeStamp.m_Year = 0;
+
+ // search for most recent save game file on the drive
+ //
+ for( unsigned int i = 0; i < NUM_GAME_SLOTS; i++ )
+ {
+ SaveGameInfo saveGameInfo;
+ if( this->GetSaveGameInfo( pDrive, i, &saveGameInfo ) )
+ {
+ if( saveGameInfo.CheckData() )
+ {
+ // everything's cool, we have a valid saved game
+ //
+ saveGameExists = true;
+
+ // now let's check if this is the most recent one thus far
+ //
+ const SaveGameInfoData* pData = saveGameInfo.GetData();
+ rAssert( pData != NULL );
+ if( SaveGameInfo::CompareTimeStamps( pData->m_timeStamp, timeStamp ) > 0 )
+ {
+ memcpy( &timeStamp, &pData->m_timeStamp, sizeof( radDate ) );
+
+ slot = i; // update most recent saved game slot
+ }
+ }
+ }
+ }
+
+ return saveGameExists;
+}
+
+void
+GameDataManager::FormatSavedGameFilename( char* filename,
+ unsigned int filenameLength,
+ unsigned int slot )
+{
+ rAssert( filename != NULL );
+ rAssert( filenameLength >= 32 );
+
+#ifdef RAD_XBOX
+ sprintf( filename, "Saved Game (Slot %d)", slot + 1 );
+#else
+ sprintf( filename, "%sSave%d", PS2_FILENAME_PREFIX, slot + 1 );
+#endif
+}
+
+void
+GameDataManager::OnFileOperationsComplete( void* pUserData )
+{
+ switch( m_currentFileOperation )
+ {
+ // cases during loading
+ //
+
+ case FILE_OP_OPEN_FOR_READING: // done opening file for reading
+ {
+ rAssert( m_radFile );
+ rAssert( m_radFile->IsOpen() );
+
+ m_radFile->ReadAsync( m_gameDataBuffer,
+ m_gameDataSize );
+
+ m_radFile->AddCompletionCallback( this, NULL );
+ m_currentFileOperation = FILE_OP_READ;
+
+ break;
+ }
+
+ case FILE_OP_READ: // done reading data from file
+ {
+ rAssert( m_radFile );
+
+ m_radFile->Release();
+ m_radFile = NULL;
+
+ bool loadOK = this->LoadAllData();
+ if( loadOK )
+ {
+#ifdef ENABLE_MINIMUM_LOAD_SAVE_TIME
+ m_currentFileOperation = FILE_OP_LOAD_COMPLETED;
+#else
+ if( m_gameDataLoadCallback != NULL )
+ {
+ m_gameDataLoadCallback->OnLoadGameComplete( Success );
+ }
+
+ m_currentFileOperation = FILE_OP_NONE;
+#endif
+ rReleasePrintf( ">> Load game completed successfully. (%d ms)\n\n",
+ m_elapsedOperationTime );
+ }
+ else
+ {
+ if( m_gameDataLoadCallback != NULL )
+ {
+ m_gameDataLoadCallback->OnLoadGameComplete( DataCorrupt );
+ }
+
+ m_currentFileOperation = FILE_OP_NONE;
+ }
+
+ // free up temp memory allocated for game data buffer
+ //
+ if( m_gameDataBuffer != NULL )
+ {
+ delete [] m_gameDataBuffer;
+ m_gameDataBuffer = NULL;
+ }
+
+ break;
+ }
+
+ // cases during saving
+ //
+
+ case FILE_OP_OPEN_FOR_WRITING: // done opening file for writing
+ {
+ rAssert( m_radFile );
+ rAssert( m_radFile->IsOpen() );
+
+ m_radFile->WriteAsync( m_gameDataBuffer,
+ m_gameDataSize );
+
+ m_radFile->AddCompletionCallback( this, NULL );
+ m_currentFileOperation = FILE_OP_WRITE;
+
+ break;
+ }
+
+ case FILE_OP_WRITE: // done writing data to file
+ {
+ rAssert( m_radFile );
+
+ m_radFile->CommitAsync();
+
+ m_radFile->AddCompletionCallback( this, NULL );
+ m_currentFileOperation = FILE_OP_COMMIT;
+
+ break;
+ }
+
+ case FILE_OP_COMMIT: // done committing all data writes to file
+ {
+ rAssert( m_radFile );
+
+ m_radFile->Release();
+ m_radFile = NULL;
+
+#ifdef ENABLE_MINIMUM_LOAD_SAVE_TIME
+ m_currentFileOperation = FILE_OP_SAVE_COMPLETED;
+#else
+ if( m_gameDataSaveCallback != NULL )
+ {
+ m_gameDataSaveCallback->OnSaveGameComplete( Success );
+ }
+
+ m_currentFileOperation = FILE_OP_NONE;
+#endif
+ rReleasePrintf( ">> Save game completed successfully. (%d ms)\n\n",
+ m_elapsedOperationTime );
+
+ // free up temp memory allocated for game data buffer
+ //
+ if( m_gameDataBuffer != NULL )
+ {
+ delete [] m_gameDataBuffer;
+ m_gameDataBuffer = NULL;
+ }
+
+ break;
+ }
+
+ default:
+ {
+ rAssertMsg( m_currentFileOperation == FILE_OP_NONE,
+ "ERROR: *** Invalid file operation!" );
+
+ break;
+ }
+ }
+}
+
+void
+GameDataManager::OnDriveOperationsComplete( void* pUserData )
+{
+ switch( m_currentFileOperation )
+ {
+ case FILE_OP_DELETE:
+ {
+ m_currentFileOperation = FILE_OP_DELETE_COMPLETED;
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+bool
+GameDataManager::OnDriveError( radFileError error,
+ const char* pDriveName,
+ void* pUserData )
+{
+ m_lastError = error; // cache error for synchronous call
+ switch( m_currentFileOperation )
+ {
+ case FILE_OP_OPEN_FOR_READING:
+ case FILE_OP_READ:
+ {
+ rTunePrintf( "*** Error [%d] occurred on drive [%s] during loading!\n",
+ error, pDriveName );
+
+#ifdef RAD_XBOX
+ // on xbox we know the filename already
+ // this could happen because of old version file format
+
+ if (m_lastError==FileNotFound)
+ m_lastError = DataCorrupt;
+#endif
+
+ if( m_gameDataLoadCallback != NULL )
+ {
+ m_gameDataLoadCallback->OnLoadGameComplete( error );
+ }
+
+ if( m_radFile != NULL )
+ {
+ m_radFile->Release();
+ m_radFile = NULL;
+ }
+
+ // free up temp memory allocated for game data buffer
+ //
+ if( m_gameDataBuffer != NULL )
+ {
+ delete [] m_gameDataBuffer;
+ m_gameDataBuffer = NULL;
+ }
+
+ m_currentFileOperation = FILE_OP_NONE;
+
+ break;
+ }
+ case FILE_OP_OPEN_FOR_WRITING:
+ case FILE_OP_WRITE:
+ case FILE_OP_COMMIT:
+ {
+ rTunePrintf( "*** Error [%d] occurred on drive [%s] during saving!\n",
+ error, pDriveName );
+
+ if( m_gameDataSaveCallback != NULL )
+ {
+ m_gameDataSaveCallback->OnSaveGameComplete( error );
+ }
+
+ if( m_radFile != NULL )
+ {
+ m_radFile->Release();
+ m_radFile = NULL;
+ }
+
+ // free up temp memory allocated for game data buffer
+ //
+ if( m_gameDataBuffer != NULL )
+ {
+ delete [] m_gameDataBuffer;
+ m_gameDataBuffer = NULL;
+ }
+
+ m_currentFileOperation = FILE_OP_NONE;
+
+ break;
+ }
+ case FILE_OP_DELETE:
+ {
+ rTunePrintf( "*** Error [%d] occurred on drive [%s] during deleting!\n",
+ error, pDriveName );
+
+ if( m_gameDataDeleteCallback != NULL )
+ {
+ m_gameDataDeleteCallback->OnDeleteGameComplete( error );
+ m_gameDataDeleteCallback = NULL;
+ }
+
+ m_currentFileOperation = FILE_OP_NONE;
+
+ break;
+ }
+ default:
+ {
+// rDebugPrintf( "*** WARNING: Unhandled drive error [%d] occurred on drive: %s\n",
+// error, pDriveName );
+
+ break;
+ }
+ }
+
+ // we should always return false, since we're always going to prompt
+ // the user first before attempting to retry any operation
+ //
+ return false;
+}
+
+bool GameDataManager::IsUsingDrive() const
+{
+ return m_currentFileOperation != FILE_OP_NONE;
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+bool
+GameDataManager::LoadAllData()
+{
+ GameDataByte* currentGameDataBuffer = m_gameDataBuffer;
+
+ for( unsigned int i = 0; i < m_numRegisteredGameData; i++ )
+ {
+ rAssert( m_registeredGameData[ i ] );
+
+ // get number of bytes for current game data
+ //
+ unsigned int numDataBytes = m_registeredGameData[ i ]->m_numBytes;
+ rAssert( numDataBytes >= 0 );
+
+#ifdef PRINT_RAW_DATA
+ rDebugPrintf( "Game Data Load (%s): ",
+ m_registeredGameData[ i ]->m_name );
+
+ this->PrintRawData( currentGameDataBuffer, numDataBytes );
+
+ rDebugPrintf( "\n" );
+#endif
+
+ // call current game data handler to load data
+ //
+ rAssert( m_registeredGameData[ i ]->m_gdHandler );
+ m_registeredGameData[ i ]->m_gdHandler->LoadData( currentGameDataBuffer,
+ numDataBytes );
+
+ if( i == 0 ) // 0 = saved game info data handler
+ {
+ bool isDataValid = m_saveGameInfoHandler->CheckData();
+ if( !isDataValid )
+ {
+ // saved game info data is not valid, assume file corrupted
+ //
+ return false;
+ }
+ }
+
+ // advance reference to current game data buffer
+ //
+ currentGameDataBuffer += numDataBytes;
+ }
+
+ // set flag indicating that a saved game has been loaded
+ //
+ m_isGameLoaded = true;
+
+ // sanity check
+ //
+ rAssert( currentGameDataBuffer - m_gameDataBuffer == static_cast<int>( m_gameDataSize ) );
+
+ return true;
+}
+
+bool
+GameDataManager::SaveAllData()
+{
+MEMTRACK_PUSH_GROUP( "GameDataManager" );
+ GameDataByte* currentGameDataBuffer = m_gameDataBuffer;
+
+ for( unsigned int i = 0; i < m_numRegisteredGameData; i++ )
+ {
+ rAssert( m_registeredGameData[ i ] );
+
+ // get number of bytes for current game data
+ //
+ unsigned int numDataBytes = m_registeredGameData[ i ]->m_numBytes;
+ rAssert( numDataBytes >= 0 );
+/*
+ // create temporary buffer for client to write in
+ //
+ GameDataByte* tempDataBuffer = new( GMA_TEMP ) GameDataByte[ numDataBytes ];
+ rAssert( tempDataBuffer );
+*/
+ // call current game data handler to save data
+ //
+ rAssert( m_registeredGameData[ i ]->m_gdHandler );
+ m_registeredGameData[ i ]->m_gdHandler->SaveData( currentGameDataBuffer,
+ numDataBytes );
+/*
+ // copy data from temporary buffer to current buffer
+ //
+ memcpy( currentGameDataBuffer, tempDataBuffer, numDataBytes );
+
+ // and destroy temporary data buffer
+ //
+ if( tempDataBuffer != NULL )
+ {
+ delete tempDataBuffer;
+ tempDataBuffer = NULL;
+ }
+*/
+#ifdef PRINT_RAW_DATA
+ rDebugPrintf( "Game Data Save (%s): ",
+ m_registeredGameData[ i ]->m_name );
+
+ this->PrintRawData( currentGameDataBuffer, numDataBytes );
+
+ rDebugPrintf( "\n" );
+#endif
+
+ // advance reference to current game data buffer
+ //
+ currentGameDataBuffer += numDataBytes;
+ }
+
+ // sanity check
+ //
+ rAssert( currentGameDataBuffer - m_gameDataBuffer == static_cast<int>( m_gameDataSize ) );
+ MEMTRACK_POP_GROUP("GameDataManager");
+ return true;
+}
+
+void
+GameDataManager::PrintRawData( GameDataByte* dataBuffer,
+ unsigned int numBytes )
+{
+ rAssert( dataBuffer != NULL );
+
+ for( unsigned int i = 0; i < numBytes; i++ )
+ {
+ rDebugPrintf( "[ %02X ]", dataBuffer[ i ] );
+ }
+}
+
diff --git a/game/code/data/gamedatamanager.h b/game/code/data/gamedatamanager.h
new file mode 100644
index 0000000..f825242
--- /dev/null
+++ b/game/code/data/gamedatamanager.h
@@ -0,0 +1,217 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: GameDataManager
+//
+// Description: Interface for the GameDataManager class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/09/16 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GAMEDATAMANAGER_H
+#define GAMEDATAMANAGER_H
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <data/gamedata.h>
+#include <radfile.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+const unsigned int MAX_NUM_GAME_DATA = 16; // maximum number of
+ // client-registered game data
+
+const unsigned int KB = 1024;
+const unsigned int MB = 1024 * KB;
+
+struct GameDataLoadCallback
+{
+ virtual void OnLoadGameComplete( radFileError errorCode ) = 0;
+};
+
+struct GameDataSaveCallback
+{
+ virtual void OnSaveGameComplete( radFileError errorCode ) = 0;
+};
+
+struct GameDataDeleteCallback
+{
+ virtual void OnDeleteGameComplete( radFileError errorCode ) = 0;
+};
+
+struct IRadFile;
+struct radMemcardInfo;
+struct radDate;
+
+class SaveGameInfo;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class GameDataManager : public IRadFileCompletionCallback,
+ public IRadDriveCompletionCallback,
+ public IRadDriveErrorCallback
+{
+public:
+ // Static Methods for accessing this singleton.
+ static GameDataManager* CreateInstance();
+ static void DestroyInstance();
+ static GameDataManager* GetInstance();
+
+ GameDataManager();
+ virtual ~GameDataManager();
+
+ // Initialization
+ //
+ void Init();
+
+ // Update
+ //
+ void Update( unsigned int elapsedTime );
+
+ // Registration of client game data
+ //
+ void RegisterGameData( GameDataHandler* gdHandler,
+ unsigned int numBytes,
+ const char* name = NULL );
+
+ unsigned int GetGameDataSize() const { return m_gameDataSize; }
+
+ // Load/Save Game (Asynchronous)
+ void LoadGame( unsigned int slot, GameDataLoadCallback* callback = NULL, const char *filename = NULL );
+ void SaveGame( unsigned int slot, GameDataSaveCallback* callback = NULL );
+
+ // Delete Game
+ radFileError DeleteGame( const char *fileName, bool async = false, GameDataDeleteCallback* callback = NULL );
+
+ // Reset All Game Data (for New Games)
+ //
+ void ResetGame();
+
+ void SetMinimumLoadSaveTime( unsigned int minimumTime );
+ void RestoreDefaultMinimumLoadSaveTime();
+
+ bool IsGameLoaded() const { return m_isGameLoaded; }
+ void SetGameLoaded() { m_isGameLoaded = true; }
+
+ bool GetSaveGameInfo( IRadDrive* pDrive,
+ unsigned int slot,
+ SaveGameInfo* saveGameInfo,
+ bool *file_corrupt_flag = NULL);
+
+ bool DoesSaveGameExist( IRadDrive* pDrive,
+ bool check_valid = true,
+ bool forAllSlots = false );
+
+ bool FindMostRecentSaveGame( IRadDrive* pDrive,
+ unsigned int& slot,
+ radDate& timeStamp );
+
+ // saved game filename formatting
+ //
+ static void FormatSavedGameFilename( char* filename,
+ unsigned int filenameLength,
+ unsigned int slot );
+
+ // Implements IRadFileCompletionCallback
+ //
+ virtual void AddRef() {;}
+ virtual void Release() {;}
+ virtual void OnFileOperationsComplete( void* pUserData );
+
+ virtual void OnDriveOperationsComplete( void* pUserData );
+
+ // Implements IRadDriveErrorCallback
+ //
+ virtual bool OnDriveError( radFileError error, const char* pDriveName, void* pUserData );
+
+ bool IsUsingDrive() const;
+
+protected:
+
+ enum eFileOperation
+ {
+ FILE_OP_NONE,
+
+ FILE_OP_OPEN_FOR_READING,
+ FILE_OP_OPEN_FOR_WRITING,
+ FILE_OP_READ,
+ FILE_OP_WRITE,
+ FILE_OP_COMMIT,
+ FILE_OP_FILE_CHECK,
+
+ FILE_OP_LOAD_COMPLETED,
+ FILE_OP_SAVE_COMPLETED,
+
+ FILE_OP_DELETE,
+ FILE_OP_DELETE_COMPLETED,
+
+ NUM_FILE_OPERATIONS
+ };
+
+private:
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ GameDataManager( const GameDataManager& );
+ GameDataManager& operator= ( const GameDataManager& );
+
+ bool LoadAllData();
+ bool SaveAllData();
+
+ static void PrintRawData( GameDataByte* dataBuffer,
+ unsigned int numBytes );
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ // Pointer to the one and only instance of this singleton.
+ static GameDataManager* spInstance;
+
+ GameData* m_registeredGameData[ MAX_NUM_GAME_DATA ];
+ unsigned int m_numRegisteredGameData;
+
+ GameDataByte* m_gameDataBuffer;
+ unsigned int m_gameDataSize;
+
+ GameDataLoadCallback* m_gameDataLoadCallback;
+ GameDataSaveCallback* m_gameDataSaveCallback;
+ GameDataDeleteCallback* m_gameDataDeleteCallback;
+
+ unsigned int m_minimumLoadSaveTime;
+ unsigned int m_elapsedOperationTime;
+
+ bool m_isGameLoaded;
+
+ SaveGameInfo* m_saveGameInfoHandler;
+
+ // RadFile Stuff
+ //
+ IRadFile* m_radFile;
+ eFileOperation m_currentFileOperation;
+
+ radFileError m_lastError;
+
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline GameDataManager* GetGameDataManager() { return( GameDataManager::GetInstance() ); }
+
+#endif // GAMEDATAMANAGER_H
diff --git a/game/code/data/memcard/allmemcard.cpp b/game/code/data/memcard/allmemcard.cpp
new file mode 100644
index 0000000..62071e4
--- /dev/null
+++ b/game/code/data/memcard/allmemcard.cpp
@@ -0,0 +1 @@
+#include <data/memcard/memorycardmanager.cpp>
diff --git a/game/code/data/memcard/memorycardmanager.cpp b/game/code/data/memcard/memorycardmanager.cpp
new file mode 100644
index 0000000..36b7597
--- /dev/null
+++ b/game/code/data/memcard/memorycardmanager.cpp
@@ -0,0 +1,1333 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: MemoryCardManager
+//
+// Description: Implementation of the MemoryCardManager class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/09/16 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <data/memcard/memorycardmanager.h>
+#include <data/gamedatamanager.h>
+#include <data/savegameinfo.h>
+
+#ifndef WORLD_BUILDER
+#include <gameflow/gameflow.h>
+#endif
+
+#include <radfile.hpp>
+
+#ifndef WORLD_BUILDER
+#include <memory/srrmemory.h>
+#else
+#define MEMTRACK_PUSH_GROUP(x)
+#define MEMTRACK_POP_GROUP(x)
+#define GMA_PERSISTENT 0
+#define GMA_DEFAULT 0
+#define GMA_TEMP 0
+#endif
+
+#include <string.h>
+
+// Static pointer to instance of singleton.
+MemoryCardManager* MemoryCardManager::spInstance = NULL;
+
+//===========================================================================
+// Local Constants
+//===========================================================================
+
+#ifdef RAD_PS2
+ #ifdef PAL
+ const unsigned int MINIMUM_MEMCARD_CHECK_TIME = 3000; // in msec
+ #else
+ const unsigned int MINIMUM_MEMCARD_CHECK_TIME = 0; // in msec
+ #endif
+#elif RAD_WIN32
+ const unsigned int MINIMUM_MEMCARD_CHECK_TIME = 0; // in msec
+#else
+ const unsigned int MINIMUM_MEMCARD_CHECK_TIME = 1000; // in msec
+#endif
+
+const int MAX_SAVED_GAME_TITLE_LENGTH = 32; // # chars
+
+#ifdef RAD_WIN32
+char DEFAULT_GAME_DRIVE[radFileDrivenameMax+1]; // for win32, need to store the default.
+#endif
+
+const char* SAVE_GAME_DRIVE[] =
+{
+#ifdef RAD_GAMECUBE
+ "MEMCARDCHANNELA:",
+ "MEMCARDCHANNELB:",
+#endif
+
+#ifdef RAD_PS2
+ "MEMCARD1A:",
+ "MEMCARD1B:",
+ "MEMCARD1C:",
+ "MEMCARD1D:",
+ "MEMCARD2A:",
+ "MEMCARD2B:",
+ "MEMCARD2C:",
+ "MEMCARD2D:",
+#endif
+
+#ifdef RAD_XBOX
+// "T:",
+ "U:",
+ "MEMCARD1A:",
+ "MEMCARD1B:",
+ "MEMCARD2A:",
+ "MEMCARD2B:",
+ "MEMCARD3A:",
+ "MEMCARD3B:",
+ "MEMCARD4A:",
+ "MEMCARD4B:",
+#endif
+
+#ifdef RAD_WIN32
+ DEFAULT_GAME_DRIVE,
+#endif
+
+ "" // dummy terminator
+};
+
+const unsigned int NUM_SAVE_GAME_DRIVES =
+ sizeof( SAVE_GAME_DRIVE ) / sizeof( SAVE_GAME_DRIVE[ 0 ] ) - 1;
+
+#ifdef RAD_GAMECUBE
+ const char* GC_BANNER_FILE = "opening.bnr";
+ const char* GC_TPL_FILE = "icon.tpl";
+#endif
+
+#ifdef RAD_PS2
+ const char* PS2_LIST_ICON_FILE = "list.ico";
+ const char* PS2_COPY_ICON_FILE = "copy.ico";
+ const char* PS2_DELETE_ICON_FILE = "delete.ico";
+
+ // this will use the 'list' icon file for all three icons
+ //
+#define USE_ONE_ICON_FILE_ONLY
+#endif
+
+#ifdef RAD_XBOX
+ const char* XBOX_ICON_FILE = "saveimg.xbx";
+
+ // this will use the default title and save game image icons
+ //
+ #define USE_DEFAULT_ICONS
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//==============================================================================
+// MemoryCardManager::CreateInstance
+//==============================================================================
+//
+// Description: - Creates the Game Data Manager.
+//
+// Parameters: None.
+//
+// Return: Pointer to the MemoryCardManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+MemoryCardManager* MemoryCardManager::CreateInstance()
+{
+ spInstance = new MemoryCardManager;
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+//==============================================================================
+// MemoryCardManager::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the Game Data Manager.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void MemoryCardManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete spInstance;
+ spInstance = NULL;
+}
+
+//==============================================================================
+// MemoryCardManager::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the MemoryCardManager singleton.
+// - Creates the MemoryCardManager if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the MemoryCardManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+MemoryCardManager* MemoryCardManager::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+//===========================================================================
+// MemoryCardManager::MemoryCardManager
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+MemoryCardManager::MemoryCardManager()
+: m_currentState( STATE_UNINITIALIZED ),
+ m_numDrivesOpened( 0 ),
+ m_memcardInfo( NULL ),
+ m_memcardInfoLoadState( MEMCARD_INFO_NOT_LOADED ),
+ m_memcardInfoLoadCallback( NULL ),
+ m_radFile( NULL ),
+ m_elapsedMemcardInfoLoadTime( 0 ),
+ m_pDrives( NULL ),
+ m_currentDrive( NULL ),
+ m_radDriveErrorCallback( NULL ),
+ m_mediaInfos( NULL ),
+ m_currentMediaInfo( -1 ),
+ m_nextMediaInfo( 0 ),
+ m_formatDriveState( false ),
+ m_formatCallback( NULL ),
+ m_memcardCheckCallback( NULL ),
+ m_memcardCheckingState( MEMCARD_CHECK_NOT_DONE ),
+ m_elapsedMemcardCheckTime( 0 ),
+#ifdef RAD_XBOX
+ m_savedGameCreationSizeHD( 0 ),
+#endif
+ m_savedGameCreationSize( 0 )
+{
+ m_pDrives = new IRadDrive*[ NUM_SAVE_GAME_DRIVES ];
+ rAssert( m_pDrives != NULL );
+
+ m_mediaInfos = new IRadDrive::MediaInfo[ NUM_SAVE_GAME_DRIVES ];
+ rAssert( m_mediaInfos != NULL );
+
+ for( unsigned int i = 0; i < NUM_SAVE_GAME_DRIVES; i++ )
+ {
+ m_pDrives[ i ] = NULL;
+
+ m_mediaInfos[ i ].m_FreeFiles = 0;
+ m_mediaInfos[ i ].m_FreeSpace = 0;
+ m_mediaInfos[ i ].m_MediaState = IRadDrive::MediaInfo::MediaNotPresent;
+ m_mediaInfos[ i ].m_SectorSize = 0;
+ m_mediaInfos[ i ].m_VolumeName[ 0 ] = '\0';
+ }
+
+/*
+#ifdef RAD_WIN32
+ // Algorithm fix which is only needed on PC. If there is only one card,
+ // then having m_nextmediainfo = m_currentMediaInfo = 0 won't load anything.
+ // We start currentmediainfo at -1 to indicate that nothing has been loaded.
+ m_currentMediaInfo = -1;
+#endif
+*/
+}
+
+//===========================================================================
+// MemoryCardManager::~MemoryCardManager
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+MemoryCardManager::~MemoryCardManager()
+{
+ if( m_pDrives != NULL )
+ {
+ // release and un-mount drives
+ //
+ for( unsigned int i = 0; i < NUM_SAVE_GAME_DRIVES; i++ )
+ {
+ if( m_pDrives[ i ] != NULL )
+ {
+#ifndef RAD_WIN32
+ m_pDrives[ i ]->UnregisterErrorHandler( this );
+#endif
+
+ m_pDrives[ i ]->Release();
+ m_pDrives[ i ] = NULL;
+
+ m_numDrivesOpened--;
+ }
+
+#ifndef RAD_WIN32
+ radDriveUnmount( SAVE_GAME_DRIVE[ i ] );
+#endif
+ }
+
+ rAssert( m_numDrivesOpened == 0 );
+
+ delete [] m_pDrives;
+ m_pDrives = NULL;
+ }
+
+ if( m_mediaInfos != NULL )
+ {
+ delete [] m_mediaInfos;
+ m_mediaInfos = NULL;
+ }
+
+ m_currentState = STATE_UNINITIALIZED;
+}
+
+void
+MemoryCardManager::Init( IRadDriveErrorCallback* radDriveErrorCallback )
+{
+ m_radDriveErrorCallback = radDriveErrorCallback;
+
+#ifdef RAD_WIN32
+ radGetDefaultDrive( DEFAULT_GAME_DRIVE );
+#endif
+
+ // mount and open drives
+ //
+ for( unsigned int i = 0; i < NUM_SAVE_GAME_DRIVES; i++ )
+ {
+#ifndef RAD_WIN32
+ bool driveAlreadyMounted = radDriveMount( SAVE_GAME_DRIVE[ i ], GMA_PERSISTENT );
+ rAssert( !driveAlreadyMounted );
+#endif
+
+ radDriveOpenAsync( &m_pDrives[ i ], SAVE_GAME_DRIVE[ i ] );
+ rAssert( m_pDrives[ i ] );
+ m_pDrives[ i ]->AddCompletionCallback( this, reinterpret_cast<void*>( i ) );
+
+ // register error handler
+ //
+#ifndef RAD_WIN32
+ m_pDrives[ i ]->RegisterErrorHandler( this, NULL );
+#endif
+ }
+
+ m_currentState = STATE_OPENING_DRIVES;
+}
+
+void
+MemoryCardManager::Update( unsigned int elapsedTime )
+{
+ if( m_currentState == STATE_READY )
+ {
+ if( m_currentMediaInfo != m_nextMediaInfo )
+ {
+ m_currentMediaInfo = m_nextMediaInfo;
+
+ // get the next media info asynchronously
+ //
+ rAssert( m_pDrives[ m_currentMediaInfo ] != NULL );
+ m_pDrives[ m_currentMediaInfo ]->GetMediaInfoAsync( &m_mediaInfos[ m_currentMediaInfo ] );
+ m_pDrives[ m_currentMediaInfo ]->AddCompletionCallback( this, NULL );
+ }
+ }
+}
+
+void
+MemoryCardManager::StartMemoryCardCheck( IMemoryCardCheckCallback* callback )
+{
+ m_memcardCheckCallback = callback;
+ m_elapsedMemcardCheckTime = 0;
+
+ m_currentMediaInfo = -1;
+ m_nextMediaInfo = 0;
+
+ m_memcardCheckingState = MEMCARD_CHECK_IN_PROGRESS;
+}
+
+void
+MemoryCardManager::UpdateMemoryCardCheck( unsigned int elapsedTime )
+{
+ m_elapsedMemcardCheckTime += elapsedTime;
+ if( m_elapsedMemcardCheckTime > MINIMUM_MEMCARD_CHECK_TIME )
+ {
+ if( m_memcardCheckingState == MEMCARD_CHECK_COMPLETED )
+ {
+ if( this->IsMemcardInfoLoaded() )
+ {
+ this->OnMemoryCardCheckCompleted();
+ }
+ else
+ {
+ // memory card info must be loaded/loading during memory card check,
+ // and should be unloaded when check is completed
+ //
+ rAssertMsg( m_memcardInfoLoadState != MEMCARD_INFO_NOT_LOADED,
+ "ERROR: *** Memory card info not loaded during memory card check!" );
+ }
+ }
+ }
+
+ if( m_memcardCheckingState == MEMCARD_CHECK_IN_PROGRESS )
+ {
+ this->Update( elapsedTime );
+ }
+}
+
+const IRadDrive::MediaInfo*
+MemoryCardManager::GetMediaInfo( unsigned int driveIndex ) const
+{
+ rAssert( driveIndex < NUM_SAVE_GAME_DRIVES );
+
+ return &(m_mediaInfos[ driveIndex ] );
+}
+
+void
+MemoryCardManager::LoadMemcardInfo( IMemoryCardInfoLoadCallback* callback )
+{
+#ifndef WORLD_BUILDER
+ if( m_memcardInfoLoadState == MEMCARD_INFO_NOT_LOADED )
+ {
+MEMTRACK_PUSH_GROUP( "MemcardInfo" );
+ bool isIngame = GetGameFlow()->GetCurrentContext() == CONTEXT_GAMEPLAY ||
+ GetGameFlow()->GetCurrentContext() == CONTEXT_PAUSE;
+
+ GameMemoryAllocator heap = isIngame ? GMA_LEVEL_MISSION : GMA_LEVEL_MOVIE;
+
+ HeapMgr()->PushHeap( heap );
+
+ m_memcardInfoLoadCallback = callback;
+
+ rAssert( m_memcardInfo == NULL );
+ m_memcardInfo = new radMemcardInfo;
+ rAssert( m_memcardInfo != NULL );
+
+ rTunePrintf( ">> Loading memory card info ... ...\n" );
+
+ m_elapsedMemcardInfoLoadTime = radTimeGetMilliseconds();
+
+#ifdef RAD_GAMECUBE
+ this->LoadMemcardInfo_GC( heap );
+#endif
+
+#ifdef RAD_PS2
+ this->LoadMemcardInfo_PS2( heap );
+#endif
+
+#ifdef RAD_XBOX
+ this->LoadMemcardInfo_XBOX( heap );
+#endif
+
+#ifdef RAD_WIN32
+ // go straight to the requests complete method; there is no
+ // memcard info.
+ OnProcessRequestsComplete( NULL );
+#endif
+
+ HeapMgr()->PopHeap( heap );
+MEMTRACK_POP_GROUP( "MemcardInfo" );
+ }
+ else
+ {
+ rTuneWarningMsg( false, "WARNING: *** Memory card info already loaded! Ignoring request to load again.\n" );
+ }
+#endif
+}
+
+void
+MemoryCardManager::UnloadMemcardInfo()
+{
+ if( m_memcardInfoLoadState == MEMCARD_INFO_LOAD_COMPLETED )
+ {
+#ifdef RAD_GAMECUBE
+ this->UnloadMemcardInfo_GC();
+#endif
+
+#ifdef RAD_PS2
+ this->UnloadMemcardInfo_PS2();
+#endif
+
+#ifdef RAD_XBOX
+ this->UnloadMemcardInfo_XBOX();
+#endif
+
+ if( m_memcardInfo != NULL )
+ {
+ delete m_memcardInfo;
+ m_memcardInfo = NULL;
+ }
+
+ rTunePrintf( ">> Memory card info unloaded.\n" );
+
+ m_memcardInfoLoadState = MEMCARD_INFO_NOT_LOADED;
+ }
+ else
+ {
+ rTuneWarningMsg( false, "WARNING: *** Memory card info not completely loaded yet! Ignoring request to unload.\n" );
+ }
+}
+
+bool
+MemoryCardManager::IsMemcardInfoLoaded() const
+{
+ return (m_memcardInfoLoadState == MEMCARD_INFO_LOAD_COMPLETED);
+}
+
+void
+MemoryCardManager::SetMemcardIconData( char* dataBuffer,
+ unsigned int dataSize )
+{
+ rAssert( dataBuffer != NULL && dataSize > 0 );
+ rAssert( m_memcardInfo != NULL );
+
+ switch( m_memcardInfoLoadState )
+ {
+#ifdef RAD_GAMECUBE
+ case MEMCARD_INFO_LOADING_BANNER:
+ {
+ m_dvdBanner = reinterpret_cast<DVDBanner*>( dataBuffer );
+
+ // This banner is in RGB format
+ //
+ m_memcardInfo->m_Banner = m_dvdBanner->image;
+ m_memcardInfo->m_BannerFormat = radMemcardInfo::RGB5A3;
+
+ // Take the comment from the disk too.
+ //
+ memcpy( m_memcardInfo->m_CommentLine1, m_dvdBanner->shortTitle, 32 );
+ strcpy( m_memcardInfo->m_CommentLine2, "" );
+
+ break;
+ }
+ case MEMCARD_INFO_LOADING_TEXPALETTE:
+ {
+ m_texPalette = reinterpret_cast<TEXPalette*>( dataBuffer );
+
+ // We have a TEXPalette, we need to parse it.
+ //
+ UnpackTexPalette( m_texPalette );
+
+ //
+ // Set the animation to loop at find number of frames
+ //
+ m_memcardInfo->m_AnimType = radMemcardInfo::Loop;
+
+ m_memcardInfo->m_NumFrames = m_texPalette->numDescriptors > 8 ? 8 : m_texPalette->numDescriptors;
+
+ //
+ // Set the info for all frames
+ //
+ for( unsigned int i = 0; i < m_memcardInfo->m_NumFrames; i++ )
+ {
+ TEXDescriptorPtr tdp = TEXGet( m_texPalette, (u32) i );
+
+ rAssertMsg( tdp->textureHeader->height == 32 && tdp->textureHeader->width == 32,
+ "Icon is the wrong dimension. Should be 32 by 32." );
+
+ //
+ // Set format and data
+ //
+ m_memcardInfo->m_pIcon[ i ].m_CLUT = NULL;
+
+ switch( (GXTexFmt)(tdp->textureHeader->format) )
+ {
+ case GX_TF_RGB5A3:
+ {
+ m_memcardInfo->m_pIcon[ i ].m_Format = radMemcardIconData::RGB5A3;
+ m_memcardInfo->m_pIcon[ i ].m_Data = (void*) tdp->textureHeader->data;
+
+ break;
+ }
+ case GX_TF_C8:
+ {
+ m_memcardInfo->m_pIcon[ i ].m_Format = radMemcardIconData::C8;
+ m_memcardInfo->m_pIcon[ i ].m_Data = (void*) tdp->textureHeader->data;
+
+ rAssert( tdp->CLUTHeader );
+ rAssert( (GXTlutFmt) tdp->CLUTHeader->format == GX_TL_RGB5A3 );
+ rAssert( tdp->CLUTHeader->numEntries == 256 );
+
+ m_memcardInfo->m_pIcon[ i ].m_CLUT = (void*) tdp->CLUTHeader->data;
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Unsupported icon texture format." );
+ break;
+ }
+ }
+
+ // Set speed
+ //
+ m_memcardInfo->m_pIcon[ i ].m_Speed = radMemcardIconData::Slow;
+ }
+
+ break;
+ }
+#endif // RAD_GAMECUBE
+
+#ifdef RAD_PS2
+ case MEMCARD_INFO_LOADING_ICON_LIST:
+ {
+ m_memcardInfo->m_ListIcon = dataBuffer;
+ m_memcardInfo->m_ListIconSize = dataSize;
+
+#ifdef USE_ONE_ICON_FILE_ONLY
+ m_memcardInfo->m_CopyIcon = dataBuffer;
+ m_memcardInfo->m_CopyIconSize = dataSize;
+
+ m_memcardInfo->m_DelIcon = dataBuffer;
+ m_memcardInfo->m_DelIconSize = dataSize;
+
+ m_memcardInfoLoadState = MEMCARD_INFO_LOADING_ICON_DELETE;
+#endif
+
+ break;
+ }
+ case MEMCARD_INFO_LOADING_ICON_COPY:
+ {
+ m_memcardInfo->m_CopyIcon = dataBuffer;
+ m_memcardInfo->m_CopyIconSize = dataSize;
+
+ break;
+ }
+ case MEMCARD_INFO_LOADING_ICON_DELETE:
+ {
+ m_memcardInfo->m_DelIcon = dataBuffer;
+ m_memcardInfo->m_DelIconSize = dataSize;
+
+ break;
+ }
+#endif // RAD_PS2
+
+#ifdef RAD_XBOX
+ case MEMCARD_INFO_LOADING_ICON:
+ {
+ m_memcardInfo->m_Icon = dataBuffer;
+ m_memcardInfo->m_IconSize = dataSize;
+
+ break;
+ }
+#endif // RAD_XBOX
+
+ case MEMCARD_INFO_NOT_LOADED:
+ {
+ rAssertMsg( false, "*** ERROR: Invalid MemcardInfo load state!" );
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void
+MemoryCardManager::OnProcessRequestsComplete( void* pUserData )
+{
+ m_memcardInfoLoadState++;
+
+ if( m_memcardInfoLoadState == MEMCARD_INFO_LOAD_COMPLETED )
+ {
+ // determine amount of time it took to load memcard info
+ //
+ m_elapsedMemcardInfoLoadTime = radTimeGetMilliseconds() - m_elapsedMemcardInfoLoadTime;
+
+ rTunePrintf( ">> Memory card info loaded. (%d msec)\n",
+ m_elapsedMemcardInfoLoadTime );
+
+ if( m_memcardInfoLoadCallback != NULL )
+ {
+ m_memcardInfoLoadCallback->OnMemcardInfoLoadComplete();
+ m_memcardInfoLoadCallback = NULL;
+ }
+ }
+}
+
+void
+MemoryCardManager::SetCurrentDrive( unsigned int driveIndex )
+{
+ rAssert( driveIndex < NUM_SAVE_GAME_DRIVES );
+
+ this->SetCurrentDrive( m_pDrives[ driveIndex ] );
+}
+void
+MemoryCardManager::ClearCurrentDrive( )
+{
+ m_currentDrive = NULL;
+}
+
+int
+MemoryCardManager::GetCurrentDriveIndex() const
+{
+ return( this->GetDriveIndex( m_currentDrive ) );
+}
+const char *
+MemoryCardManager::GetDriveName(unsigned int driveIndex) const
+{
+ rAssert(driveIndex < NUM_SAVE_GAME_DRIVES);
+ rAssert(m_pDrives[ driveIndex ]);
+ return m_pDrives[driveIndex]->GetDriveName();
+}
+const char *
+MemoryCardManager::GetCurrentDriveVolumeName() const
+{
+ return m_mediaInfos[ GetCurrentDriveIndex() ].m_VolumeName;
+}
+
+bool
+MemoryCardManager::IsCurrentDrivePresent( unsigned int currentDriveIndex )
+{
+ bool isMissing = true;
+ if( static_cast<int>( currentDriveIndex ) != -1 )
+ {
+ isMissing = (m_mediaInfos[ currentDriveIndex ].m_MediaState == IRadDrive::MediaInfo::MediaNotPresent);
+ }
+ return !isMissing;
+}
+bool
+MemoryCardManager::IsCurrentDriveReady( bool forceSyncUpdate, bool *unformatted , IRadDrive::MediaInfo::MediaState *errorOut)
+{
+ bool isReady = false;
+ bool card_unformatted = false;
+
+ int currentDriveIndex = this->GetCurrentDriveIndex();
+ if (unformatted!=NULL)
+ *unformatted = false;
+ if( currentDriveIndex != -1 )
+ {
+ if( forceSyncUpdate )
+ {
+ // force synchronous update on current drive's media info
+ //
+ m_pDrives[ currentDriveIndex ]->GetMediaInfoSync( &m_mediaInfos[ currentDriveIndex ] );
+ }
+ if (errorOut)
+ *errorOut = m_mediaInfos[ currentDriveIndex ].m_MediaState;
+
+ if ( m_mediaInfos[ currentDriveIndex ].m_MediaState==IRadDrive::MediaInfo::MediaNotFormatted )
+ {
+ card_unformatted = true;
+ }
+#ifdef RAD_GAMECUBE
+ if ( m_mediaInfos[ currentDriveIndex ].m_MediaState==IRadDrive::MediaInfo::MediaEncodingErr )
+ card_unformatted = true;
+#endif
+ isReady = (m_mediaInfos[ currentDriveIndex ].m_MediaState == IRadDrive::MediaInfo::MediaPresent);
+ if (unformatted)
+ *unformatted = card_unformatted;
+
+ if( !isReady
+ && card_unformatted==false )
+ // we need to keep track of unformatted drive so we can format it
+ {
+ // memory card must have been pulled out, reset current drive
+ //
+ m_currentDrive = NULL;
+ }
+ }
+
+ return isReady;
+}
+
+int
+MemoryCardManager::GetAvailableDrives( IRadDrive** pDrives,
+ IRadDrive::MediaInfo** mediaInfos,
+ IRadDrive **drive_mounted)
+{
+ int numAvailableDrives = 0;
+
+ // query for all drives currently present
+ //
+ for( unsigned int i = 0; i < NUM_SAVE_GAME_DRIVES; i++ )
+ {
+ rAssert( m_pDrives[ i ] );
+
+ if( m_mediaInfos[ i ].m_MediaState != IRadDrive::MediaInfo::MediaNotPresent )
+ {
+ if( pDrives != NULL )
+ {
+ pDrives[ numAvailableDrives ] = m_pDrives[ i ];
+ }
+
+ if( mediaInfos != NULL )
+ {
+ mediaInfos[ numAvailableDrives ] = &m_mediaInfos[ i ];
+ }
+ if (drive_mounted)
+ {
+ drive_mounted[i] = m_pDrives[ i ];
+ }
+ numAvailableDrives++;
+ }
+ else
+ {
+ if (drive_mounted)
+ {
+ drive_mounted[i] = NULL;
+ }
+ }
+
+ }
+
+
+ // terminate w/ NULL
+ //
+ if( pDrives != NULL )
+ {
+ pDrives[ numAvailableDrives ] = NULL;
+ }
+
+ return numAvailableDrives;
+}
+
+bool
+MemoryCardManager::EnoughFreeSpace( unsigned int driveIndex ) const
+{
+ rAssert( driveIndex < NUM_SAVE_GAME_DRIVES );
+
+ const unsigned int NUM_FREE_FILES_REQUIRED = 1;
+
+ return( m_mediaInfos[ driveIndex ].m_FreeFiles >= NUM_FREE_FILES_REQUIRED &&
+ m_mediaInfos[ driveIndex ].m_FreeSpace >= this->GetSavedGameCreationSize( driveIndex ) );
+}
+
+void MemoryCardManager::FormatDrive(unsigned int driveIndex, IMemoryCardFormatCallback *callback)
+{
+ SetCurrentDrive(driveIndex);
+ m_formatDriveState = true;
+ rAssert(callback);
+ m_formatCallback = callback;
+ m_currentDrive->FormatAsync();
+ m_currentDrive->AddCompletionCallback( this , NULL);
+}
+void
+MemoryCardManager::UpdateMemcardInfo( const char* savedGameTitle, int lineBreak )
+{
+ rAssert( m_memcardInfo != NULL );
+
+#ifdef RAD_GAMECUBE
+ rAssert( savedGameTitle != NULL );
+ strncpy( m_memcardInfo->m_CommentLine2,
+ savedGameTitle,
+ MAX_SAVED_GAME_TITLE_LENGTH );
+#endif
+
+#ifdef RAD_PS2
+ rAssert( savedGameTitle != NULL );
+ rAssert( lineBreak != -1 );
+
+ radSJISChar title[ MAX_SAVED_GAME_TITLE_LENGTH ];
+ radAsciiToSjis( title, savedGameTitle );
+ radSetIconSysTitle( &(m_memcardInfo->m_IconSys),
+ title,
+ static_cast<unsigned short>( lineBreak ) );
+#endif
+
+#ifdef RAD_XBOX
+ // TC: stinky XBox! there's is no way to specify a descriptive name
+ // that's different from the file name
+ //
+ rWarningMsg( savedGameTitle == NULL, "Can't specify save game title on Xbox!" );
+#endif
+}
+
+void
+MemoryCardManager::OnDriveOperationsComplete( void* pUserData )
+{
+ if ( m_formatDriveState )
+ {
+ m_formatCallback->OnFormatOperationComplete(Success);
+ m_formatDriveState = false;
+ }
+ else if( m_currentState == STATE_OPENING_DRIVES )
+ {
+ unsigned int driveIndex = reinterpret_cast<unsigned int>( pUserData );
+ rAssert( driveIndex < NUM_SAVE_GAME_DRIVES );
+
+ m_numDrivesOpened++;
+
+ if( m_numDrivesOpened == NUM_SAVE_GAME_DRIVES )
+ {
+ // done opening all save game drives
+ //
+ m_currentState = STATE_READY;
+ }
+ }
+ else // checking memory cards during bootup
+ {
+ rAssert( m_currentMediaInfo == m_nextMediaInfo );
+ m_nextMediaInfo = (m_currentMediaInfo + 1) % NUM_SAVE_GAME_DRIVES;
+
+ if( m_memcardCheckingState == MEMCARD_CHECK_IN_PROGRESS )
+ {
+ if( m_nextMediaInfo == 0 )
+ {
+#if defined( RAD_GAMECUBE) || defined( RAD_PS2 )
+ // continue to poll for media info from all drives
+ // during the entire minimum checking time
+ //
+ if( m_elapsedMemcardCheckTime > MINIMUM_MEMCARD_CHECK_TIME )
+#endif
+ {
+ // ok, we finished querying media info from all drives
+ //
+ m_memcardCheckingState = MEMCARD_CHECK_COMPLETED;
+ }
+ }
+ }
+ }
+}
+
+bool
+MemoryCardManager::HasSaveGame(unsigned int driveIndex)
+{
+ rAssert( driveIndex < NUM_SAVE_GAME_DRIVES );
+ return GetGameDataManager()->DoesSaveGameExist( m_pDrives[ driveIndex ], false );
+}
+
+bool
+MemoryCardManager::OnDriveError( radFileError error,
+ const char* pDriveName,
+ void* pUserData )
+{
+
+ rDebugPrintf( "*** MEMCARD: ERROR [%d] occurred on drive [%s]! ***\n",
+ error,
+ pDriveName );
+
+ if ( m_formatDriveState )
+ {
+ m_formatCallback->OnFormatOperationComplete(error);
+ }
+ else if( m_radDriveErrorCallback != NULL )
+ {
+ m_radDriveErrorCallback->OnDriveError( error, pDriveName, pUserData );
+ }
+
+ // we should always return false, since we're always going to prompt
+ // the user first before attempting to retry any operation
+ //
+ return false;
+}
+
+int
+MemoryCardManager::GetDriveIndex( IRadDrive* pDrive ) const
+{
+ int driveIndex = -1;
+
+ if( pDrive != NULL )
+ {
+ for( unsigned int i = 0; i < NUM_SAVE_GAME_DRIVES; i++ )
+ {
+ if( strcmp( pDrive->GetDriveName(), SAVE_GAME_DRIVE[ i ] ) == 0 )
+ {
+ driveIndex = static_cast<int>( i );
+
+ break;
+ }
+ }
+ }
+
+ return driveIndex;
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+void
+MemoryCardManager::DetermineSavedGameCreationSize( unsigned int driveIndex )
+{
+#ifdef RAD_XBOX
+ if( driveIndex == 0 ) // Xbox hard disk
+ {
+ rAssert( driveIndex < NUM_SAVE_GAME_DRIVES );
+ m_savedGameCreationSizeHD = m_pDrives[ driveIndex ]->GetCreationSize( m_memcardInfo, GetGameDataManager()->GetGameDataSize() );
+
+ rReleasePrintf( "The Simpsons Hit & Run Saved Game File Size (on HD) = %.2f KB (%d bytes)\n",
+ m_savedGameCreationSizeHD / 1024.0f, m_savedGameCreationSizeHD );
+ }
+ else
+#endif
+ {
+ rAssert( driveIndex < NUM_SAVE_GAME_DRIVES );
+ m_savedGameCreationSize = m_pDrives[ driveIndex ]->GetCreationSize( m_memcardInfo, GetGameDataManager()->GetGameDataSize() );
+
+ rReleasePrintf( "The Simpsons Hit & Run Saved Game File Size = %.2f KB (%d bytes)\n",
+ m_savedGameCreationSize / 1024.0f, m_savedGameCreationSize );
+ }
+}
+
+void
+MemoryCardManager::OnMemoryCardCheckCompleted()
+{
+ radFileError errorCode = Success;
+ IRadDrive::MediaInfo::MediaState mediaState = IRadDrive::MediaInfo::MediaPresent;
+ int driveIndex = -1;
+
+ // search for a good memory card w/ enough free space to save games
+ //
+ bool goodCardExists = false;
+ bool fullCardExists = false;
+
+ // and, at the same time, search for most recent save game among
+ // all drives
+ //
+ int mostRecentSaveGameDriveIndex = -1;
+ int mostRecentSaveGameSlot = -1;
+ radDate mostRecentTimeStamp;
+ mostRecentTimeStamp.m_Year = 0;
+
+ for( unsigned int i = 0; i < NUM_SAVE_GAME_DRIVES; i++ )
+ {
+ // determine saved game creation size
+ //
+ this->DetermineSavedGameCreationSize( i );
+
+ if( m_mediaInfos[ i ].m_MediaState == IRadDrive::MediaInfo::MediaPresent )
+ {
+ unsigned int slot = 0;
+ radDate timeStamp;
+ bool saveGameExists = GetGameDataManager()->FindMostRecentSaveGame( m_pDrives[ i ],
+ slot,
+ timeStamp );
+ if( saveGameExists )
+ {
+ if( SaveGameInfo::CompareTimeStamps( timeStamp, mostRecentTimeStamp ) > 0 )
+ {
+ mostRecentSaveGameDriveIndex = static_cast<int>( i );
+ mostRecentSaveGameSlot = static_cast<int>( slot );
+ memcpy( &mostRecentTimeStamp, &timeStamp, sizeof( radDate ) );
+ }
+ }
+
+ if( this->EnoughFreeSpace( i ) )
+ {
+ goodCardExists = true; // we found good card, continue looping to find the latest save game
+ }
+ else
+ {
+#ifdef RAD_GAMECUBE
+ // Nintendo TRC: if there is a full memory card, we need to check later to see if
+ // all game slots are taken up; if not, then we must display a "full memory card" error message
+ //
+ fullCardExists = true;
+#endif
+ if( saveGameExists )
+ {
+ goodCardExists = true; // we found good card, continue looping to find the latest save game
+ }
+ }
+ }
+ }
+
+#ifdef RAD_XBOX
+ // only need to check xbox hard disk, no need to check memory units
+ //
+ rAssertMsg( m_mediaInfos[ 0 ].m_MediaState == IRadDrive::MediaInfo::MediaPresent,
+ "ERROR: *** WTF? The Xbox Hard Disk is not present??" );
+
+ if( !GetGameDataManager()->DoesSaveGameExist( m_pDrives[ 0 ] ) &&
+ !this->EnoughFreeSpace( 0 ) )
+ {
+ driveIndex = 0;
+ errorCode = NoFreeSpace;
+ }
+#endif // RAD_XBOX
+
+#ifdef RAD_WIN32
+ rAssertMsg( m_mediaInfos[ 0 ].m_MediaState == IRadDrive::MediaInfo::MediaPresent,
+ "ERROR: Default hard drive didn't mount." );
+#endif // RAD_WIN32
+
+#if defined( RAD_GAMECUBE ) || defined( RAD_PS2 )
+ if( !goodCardExists || fullCardExists )
+ {
+ // no good card exists; now search for first card w/ an error
+ //
+ bool errorExists = false;
+ for( unsigned int i = 0; i < NUM_SAVE_GAME_DRIVES; i++ )
+ {
+ if( m_mediaInfos[ i ].m_MediaState == IRadDrive::MediaInfo::MediaPresent )
+ {
+#ifdef RAD_GAMECUBE
+ bool saveGameExists = GetGameDataManager()->DoesSaveGameExist( m_pDrives[ i ], true, true );
+#else
+ bool saveGameExists = GetGameDataManager()->DoesSaveGameExist( m_pDrives[ i ] );
+#endif
+ if( !saveGameExists && !this->EnoughFreeSpace( i ) )
+ {
+ errorExists = true;
+
+ errorCode = NoFreeSpace;
+ driveIndex = static_cast<int>( i );
+
+ break;
+ }
+ }
+ else if( m_mediaInfos[ i ].m_MediaState != IRadDrive::MediaInfo::MediaNotPresent )
+ {
+ errorExists = true;
+
+ errorCode = m_pDrives[ i ]->GetLastError();
+ mediaState = m_mediaInfos[ i ].m_MediaState;
+ driveIndex = static_cast<int>( i );
+
+ break;
+ }
+ }
+
+ if( !errorExists && !fullCardExists )
+ {
+ // hmmm... no error exists, that means there are no memory cards attached
+ //
+ mediaState = IRadDrive::MediaInfo::MediaNotPresent;
+ driveIndex = -1;
+
+#ifdef RAD_DEBUG
+ // let's double check that there are, in fact, no memory cards
+ //
+ for( unsigned int i = 0; i < NUM_SAVE_GAME_DRIVES; i++ )
+ {
+ rAssert( m_mediaInfos[ i ].m_MediaState == IRadDrive::MediaInfo::MediaNotPresent );
+ }
+#endif
+ }
+ }
+#endif // RAD_GAMECUBE || RAD_PS2
+
+ if( m_memcardCheckCallback != NULL )
+ {
+ m_memcardCheckCallback->OnMemoryCardCheckDone( errorCode,
+ mediaState,
+ driveIndex,
+ mostRecentSaveGameDriveIndex,
+ mostRecentSaveGameSlot );
+ m_memcardCheckCallback = NULL;
+ }
+}
+
+
+#ifdef RAD_GAMECUBE
+ //
+ // I have no idea what this does ...
+ //
+ void
+ MemoryCardManager::UnpackTexPalette( TEXPalettePtr pal )
+ {
+ u16 i;
+
+ rAssertMsg( pal->versionNumber == 2142000, "Invalid version number for texture palette" );
+
+ pal->descriptorArray = (TEXDescriptorPtr)(((u32)(pal->descriptorArray)) + ((u32)pal));
+
+ //
+ // Go through each of the palette descriptors
+ //
+ for ( i = 0; i < pal->numDescriptors; i++ )
+ {
+ if( pal->descriptorArray[ i ].textureHeader )
+ {
+ //
+ // Fill in the texture header
+ //
+ pal->descriptorArray[ i ].textureHeader =
+ reinterpret_cast< TEXHeaderPtr >
+ (
+ ( (u32) pal->descriptorArray[i].textureHeader ) +
+ ( (u32) pal )
+ );
+
+ //
+ // If it is not unpacked, unpack it
+ //
+ if
+ (
+ !( pal->descriptorArray[i].textureHeader->unpacked )
+ )
+ {
+ pal->descriptorArray[i].textureHeader->data =
+ reinterpret_cast< Ptr >
+ (
+ ( (u32) pal->descriptorArray[ i ].textureHeader->data ) +
+ ( (u32) pal )
+ );
+ pal->descriptorArray[i].textureHeader->unpacked = 1;
+ }
+ }
+
+ if(pal->descriptorArray[i].CLUTHeader)
+ {
+ pal->descriptorArray[i].CLUTHeader = (CLUTHeaderPtr)((u32)(pal->descriptorArray[i].CLUTHeader) + (u32)pal);
+
+ if(!(pal->descriptorArray[i].CLUTHeader->unpacked))
+ {
+ pal->descriptorArray[i].CLUTHeader->data = (Ptr)((u32)(pal->descriptorArray[i].CLUTHeader->data) + (u32)pal);
+ pal->descriptorArray[i].CLUTHeader->unpacked = 1;
+ }
+ }
+ }
+ }
+
+ void
+ MemoryCardManager::LoadMemcardInfo_GC( GameMemoryAllocator heap )
+ {
+ m_dvdBanner = NULL;
+ m_texPalette = NULL;
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_ICON,
+ GC_BANNER_FILE,
+ heap,
+ this );
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_ICON,
+ GC_TPL_FILE,
+ heap,
+ this );
+
+ m_memcardInfoLoadState = MEMCARD_INFO_LOADING_BANNER;
+ }
+
+ void
+ MemoryCardManager::UnloadMemcardInfo_GC()
+ {
+ if( m_dvdBanner != NULL )
+ {
+ delete m_dvdBanner;
+ m_dvdBanner = NULL;
+ }
+
+ if( m_texPalette != NULL )
+ {
+ delete [] m_texPalette;
+ m_texPalette = NULL;
+ }
+ }
+#endif // RAD_GAMECUBE
+
+#ifdef RAD_PS2
+ void
+ MemoryCardManager::LoadMemcardInfo_PS2( GameMemoryAllocator heap )
+ {
+ // make a header structure
+ //
+ radSJISChar title[ MAX_SAVED_GAME_TITLE_LENGTH ];
+ radAsciiToSjis( title, "" );
+ rAssert( m_memcardInfo != NULL );
+ radMakeIconSys( &(m_memcardInfo->m_IconSys), title, 0 );
+
+ m_memcardInfo->m_ListIcon = NULL;
+ m_memcardInfo->m_CopyIcon = NULL;
+ m_memcardInfo->m_DelIcon = NULL;
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_ICON,
+ PS2_LIST_ICON_FILE,
+ heap,
+ this );
+
+#ifndef USE_ONE_ICON_FILE_ONLY
+ GetLoadingManager()->AddRequest( FILEHANDLER_ICON,
+ PS2_COPY_ICON_FILE,
+ heap,
+ this );
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_ICON,
+ PS2_DELETE_ICON_FILE,
+ heap,
+ this );
+#endif
+
+ m_memcardInfoLoadState = MEMCARD_INFO_LOADING_ICON_LIST;
+ }
+
+ void
+ MemoryCardManager::UnloadMemcardInfo_PS2()
+ {
+ if( m_memcardInfo != NULL )
+ {
+ if( m_memcardInfo->m_ListIcon != NULL )
+ {
+ delete [] reinterpret_cast<char*>( m_memcardInfo->m_ListIcon );
+ m_memcardInfo->m_ListIcon = NULL;
+ }
+
+#ifdef USE_ONE_ICON_FILE_ONLY
+ m_memcardInfo->m_CopyIcon = NULL;
+ m_memcardInfo->m_DelIcon = NULL;
+#else
+ if( m_memcardInfo->m_CopyIcon != NULL )
+ {
+ delete [] reinterpret_cast<char*>( m_memcardInfo->m_CopyIcon );
+ m_memcardInfo->m_CopyIcon = NULL;
+ }
+
+ if( m_memcardInfo->m_DelIcon != NULL )
+ {
+ delete [] reinterpret_cast<char*>( m_memcardInfo->m_DelIcon );
+ m_memcardInfo->m_DelIcon = NULL;
+ }
+#endif
+ }
+ }
+#endif // RAD_PS2
+
+#ifdef RAD_XBOX
+ void
+ MemoryCardManager::LoadMemcardInfo_XBOX( GameMemoryAllocator heap )
+ {
+ rAssert( m_memcardInfo != NULL );
+ m_memcardInfo->m_Icon = NULL;
+
+ m_memcardInfoLoadState = MEMCARD_INFO_LOADING_ICON;
+
+#ifdef USE_DEFAULT_ICONS
+ this->OnProcessRequestsComplete( NULL );
+#else
+ GetLoadingManager()->AddRequest( FILEHANDLER_ICON,
+ XBOX_ICON_FILE,
+ heap,
+ this );
+#endif
+ }
+
+ void
+ MemoryCardManager::UnloadMemcardInfo_XBOX()
+ {
+ if( m_memcardInfo != NULL )
+ {
+ if( m_memcardInfo->m_Icon != NULL )
+ {
+ delete [] reinterpret_cast<char*>( m_memcardInfo->m_Icon );
+ m_memcardInfo->m_Icon = NULL;
+ }
+ }
+ }
+#endif // RAD_XBOX
+
diff --git a/game/code/data/memcard/memorycardmanager.h b/game/code/data/memcard/memorycardmanager.h
new file mode 100644
index 0000000..2340f93
--- /dev/null
+++ b/game/code/data/memcard/memorycardmanager.h
@@ -0,0 +1,302 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: MemoryCardManager
+//
+// Description: Interface for the MemoryCardManager class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/09/16 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef MEMORYCARDMANAGER_H
+#define MEMORYCARDMANAGER_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#ifndef WORLD_BUILDER
+#include <loading/loadingmanager.h>
+#else
+namespace LoadingManager
+{
+ class ProcessRequestsCallback
+ {
+ };
+}
+
+#endif
+
+#include <radfile.hpp>
+
+#ifdef RAD_GAMECUBE
+ #include <charPipeline/texPalette.h>
+ #include <dolphin/dvd/DVDBanner.h>
+#endif
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+struct IMemoryCardInfoLoadCallback
+{
+ virtual void OnMemcardInfoLoadComplete() = 0;
+};
+
+struct IMemoryCardFormatCallback
+{
+ virtual void OnFormatOperationComplete(radFileError err) = 0;
+};
+struct IMemoryCardCheckCallback
+{
+ virtual void OnMemoryCardCheckDone( radFileError errorCode,
+ IRadDrive::MediaInfo::MediaState mediaState,
+ int driveIndex,
+ int mostRecentSaveGameDriveIndex,
+ int mostRecentSaveGameSlot ) = 0;
+};
+
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class MemoryCardManager : public LoadingManager::ProcessRequestsCallback,
+ public IRadDriveCompletionCallback,
+ public IRadDriveErrorCallback
+{
+public:
+ // Static Methods for accessing this singleton.
+ static MemoryCardManager* CreateInstance();
+ static void DestroyInstance();
+ static MemoryCardManager* GetInstance();
+
+ enum eState
+ {
+ STATE_UNINITIALIZED,
+ STATE_OPENING_DRIVES,
+ STATE_READY,
+ STATE_CHECKING_MEMCARDS,
+
+ NUM_STATES
+ };
+
+ MemoryCardManager();
+ virtual ~MemoryCardManager();
+
+ virtual void AddRef() {;}
+ virtual void Release() {;}
+
+ eState GetCurrentState() const { return m_currentState; }
+
+ // Initialization
+ //
+ void Init( IRadDriveErrorCallback* radDriveErrorCallback = NULL );
+
+ void Update( unsigned int elapsedTime );
+
+ // Boot-up Memory Card Check
+ //
+ void StartMemoryCardCheck( IMemoryCardCheckCallback* callback );
+ void UpdateMemoryCardCheck( unsigned int elapsedTime );
+
+ // Media Info Accessor
+ //
+ const IRadDrive::MediaInfo* GetMediaInfo( unsigned int driveIndex ) const;
+ unsigned int GetSavedGameCreationSize( unsigned int driveIndex ) const;
+
+ // Loading/Unloading Memory Card Info
+ //
+ void LoadMemcardInfo( IMemoryCardInfoLoadCallback* callback = NULL );
+ void UnloadMemcardInfo();
+ bool IsMemcardInfoLoaded() const;
+
+ void SetMemcardIconData( char* dataBuffer, unsigned int dataSize );
+
+ void OnProcessRequestsComplete( void* pUserData );
+
+ bool HasSaveGame(unsigned int driveIndex);
+
+ // Current Drive Accessors
+ //
+ void SetCurrentDrive( IRadDrive* currentDrive ) { m_currentDrive = currentDrive; }
+ void SetCurrentDrive( unsigned int driveIndex );
+ IRadDrive* GetCurrentDrive() const { return m_currentDrive; }
+ const char *GetDriveName(unsigned int driveIndex) const;
+ const char *GetCurrentDriveVolumeName() const;
+ int GetCurrentDriveIndex() const;
+ void ClearCurrentDrive();
+
+ void FormatDrive(unsigned int driveIndex, IMemoryCardFormatCallback *callback);
+
+ bool IsCurrentDriveReady( bool forceSyncUpdate = false,
+ bool *unformatted = NULL,
+ IRadDrive::MediaInfo::MediaState *errorOut = NULL);
+ bool IsCurrentDrivePresent( unsigned int driveIndex );
+
+ // Query current list of available drives
+ //
+ int GetAvailableDrives( IRadDrive** pDrives, IRadDrive::MediaInfo** mediaInfos, IRadDrive **drive_mounted = NULL );
+ int GetNumAvailableDrives()
+ {
+ return GetAvailableDrives( NULL, NULL );
+ }
+
+ bool EnoughFreeSpace( unsigned int driveIndex ) const;
+
+ // Memory Card Info
+ //
+ const radMemcardInfo* GetMemcardInfo() const { return m_memcardInfo; }
+ void UpdateMemcardInfo( const char* savedGameTitle, int lineBreak = -1 );
+
+ // Implements IRadDriveCompletionCallback
+ //
+ virtual void OnDriveOperationsComplete( void* pUserData );
+
+ // Implements IRadDriveErrorCallback
+ //
+ virtual bool OnDriveError( radFileError error, const char* pDriveName, void* pUserData );
+
+ int GetDriveIndex( IRadDrive* pDrive ) const;
+
+private:
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ MemoryCardManager( const MemoryCardManager& );
+ MemoryCardManager& operator= ( const MemoryCardManager& );
+
+ void DetermineSavedGameCreationSize( unsigned int driveIndex );
+ void OnMemoryCardCheckCompleted();
+
+#ifdef RAD_GAMECUBE
+ void UnpackTexPalette( TEXPalettePtr pal );
+
+ void LoadMemcardInfo_GC( GameMemoryAllocator heap );
+ void UnloadMemcardInfo_GC();
+#endif
+
+#ifdef RAD_PS2
+ void LoadMemcardInfo_PS2( GameMemoryAllocator heap );
+ void UnloadMemcardInfo_PS2();
+#endif
+
+#ifdef RAD_XBOX
+ void LoadMemcardInfo_XBOX( GameMemoryAllocator heap );
+ void UnloadMemcardInfo_XBOX();
+#endif
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ // Pointer to the one and only instance of this singleton.
+ static MemoryCardManager* spInstance;
+
+ eState m_currentState;
+ unsigned int m_numDrivesOpened;
+
+#ifdef RAD_GAMECUBE
+ enum eMemcardInfoLoadState
+ {
+ MEMCARD_INFO_NOT_LOADED,
+ MEMCARD_INFO_LOADING_BANNER,
+ MEMCARD_INFO_LOADING_TEXPALETTE,
+
+ MEMCARD_INFO_LOAD_COMPLETED
+ };
+
+ DVDBanner* m_dvdBanner;
+ TEXPalette* m_texPalette;
+#endif
+
+#ifdef RAD_PS2
+ enum eMemcardInfoLoadState
+ {
+ MEMCARD_INFO_NOT_LOADED,
+ MEMCARD_INFO_LOADING_ICON_LIST,
+ MEMCARD_INFO_LOADING_ICON_COPY,
+ MEMCARD_INFO_LOADING_ICON_DELETE,
+
+ MEMCARD_INFO_LOAD_COMPLETED
+ };
+#endif
+
+#ifdef RAD_XBOX
+ enum eMemcardInfoLoadState
+ {
+ MEMCARD_INFO_NOT_LOADED,
+ MEMCARD_INFO_LOADING_ICON,
+
+ MEMCARD_INFO_LOAD_COMPLETED
+ };
+#endif
+
+#ifdef RAD_WIN32
+ enum eMemcardInfoLoadState
+ {
+ MEMCARD_INFO_NOT_LOADED,
+ MEMCARD_INFO_LOAD_COMPLETED
+ };
+#endif
+
+ radMemcardInfo* m_memcardInfo;
+ unsigned int m_memcardInfoLoadState;
+ IMemoryCardInfoLoadCallback* m_memcardInfoLoadCallback;
+ IRadFile* m_radFile;
+ unsigned int m_elapsedMemcardInfoLoadTime;
+
+ IRadDrive** m_pDrives;
+ IRadDrive* m_currentDrive;
+ IRadDriveErrorCallback* m_radDriveErrorCallback;
+
+ IRadDrive::MediaInfo* m_mediaInfos;
+ int m_currentMediaInfo;
+ int m_nextMediaInfo;
+ bool m_formatDriveState;
+ IMemoryCardFormatCallback* m_formatCallback;
+
+ enum eMemcardCheckingState
+ {
+ MEMCARD_CHECK_NOT_DONE,
+ MEMCARD_CHECK_IN_PROGRESS,
+ MEMCARD_CHECK_COMPLETED
+ };
+
+ IMemoryCardCheckCallback* m_memcardCheckCallback;
+ eMemcardCheckingState m_memcardCheckingState;
+ unsigned int m_elapsedMemcardCheckTime;
+
+#ifdef RAD_XBOX
+ unsigned int m_savedGameCreationSizeHD;
+#endif
+ unsigned int m_savedGameCreationSize;
+
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline MemoryCardManager* GetMemoryCardManager() { return( MemoryCardManager::GetInstance() ); }
+
+inline unsigned int
+MemoryCardManager::GetSavedGameCreationSize( unsigned int driveIndex ) const
+{
+#ifdef RAD_XBOX
+ if( driveIndex == 0 ) // Xbox hard disk
+ {
+ return m_savedGameCreationSizeHD;
+ }
+ else
+#endif
+ {
+ return m_savedGameCreationSize;
+ }
+}
+
+#endif // MEMORYCARDMANAGER_H
diff --git a/game/code/data/savegameinfo.cpp b/game/code/data/savegameinfo.cpp
new file mode 100644
index 0000000..e5cfced
--- /dev/null
+++ b/game/code/data/savegameinfo.cpp
@@ -0,0 +1,338 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SaveGameInfo
+//
+// Description: Implementation of the SaveGameInfo class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/06 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <data/savegameinfo.h>
+#include <data/gamedatamanager.h>
+
+#include <main/commandlineoptions.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <presentation/gui/guitextbible.h>
+
+#include <p3d/unicode.hpp>
+
+#include <string.h>
+
+//===========================================================================
+// Local Constants
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// SaveGameInfo::SaveGameInfo
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+SaveGameInfo::SaveGameInfo()
+{
+ this->ResetData();
+}
+
+//===========================================================================
+// SaveGameInfo::~SaveGameInfo
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+SaveGameInfo::~SaveGameInfo()
+{
+}
+
+//===========================================================================
+// SaveGameInfo::LoadData
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+SaveGameInfo::LoadData( const GameDataByte* dataBuffer,
+ unsigned int numBytes )
+{
+ // copy data from buffer
+ //
+ memcpy( &m_data, dataBuffer, sizeof( m_data ) );
+
+// rWarningMsg( m_data.m_fileSize == GetGameDataManager()->GetGameDataSize(),
+// "*** ERROR: File size mis-match that is quite possibly due to loading an old saved game file." );
+}
+
+//===========================================================================
+// SaveGameInfo::SaveData
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+SaveGameInfo::SaveData( GameDataByte* dataBuffer,
+ unsigned int numBytes )
+{
+#ifndef WORLD_BUILDER
+ // store magic number
+ //
+ m_data.m_magicNumber = MAGIC_NUMBER;
+
+ // get current time stamp
+ //
+ radTimeGetDate( &(m_data.m_timeStamp) );
+
+ if( CommandLineOptions::Get( CLO_MEMCARD_CHEAT ) )
+ {
+ // TC: if saving w/ memory card cheat, set the date to be my birthday so
+ // it can be more easily distinguished from a regular saved game
+ //
+ m_data.m_timeStamp.m_Month = 6;
+ m_data.m_timeStamp.m_Day = 27;
+ m_data.m_timeStamp.m_Year = 1978;
+ m_data.m_timeStamp.m_Hour = 23;
+ m_data.m_timeStamp.m_Minute = 0;
+ m_data.m_timeStamp.m_Second = 0;
+ }
+
+ // get current level and mission
+ //
+ CurrentMissionStruct currentMission = GetCharacterSheetManager()->QueryCurrentMission();
+ m_data.m_level = static_cast<GameDataByte>( currentMission.mLevel + 1 );
+ m_data.m_mission = static_cast<GameDataByte>( currentMission.mMissionNumber + 1 );
+
+ // special case for level 1 due to tutorial mission being mission #0
+ //
+ if( currentMission.mLevel == 0 )
+ {
+ m_data.m_mission--;
+ }
+
+ // get file size (in bytes)
+ //
+ m_data.m_fileSize = GetGameDataManager()->GetGameDataSize();
+
+ // copy data to buffer
+ //
+ memcpy( dataBuffer, &m_data, sizeof( m_data ) );
+#endif
+}
+
+void
+SaveGameInfo::ResetData()
+{
+ memset( &m_data, 0, sizeof( m_data ) );
+}
+
+bool
+SaveGameInfo::CheckData()
+{
+ // do some rudimentary data checking to verify that file is not corrupted
+ // and that all expected data make sense
+
+ // check file size against expected file size (*** SHOULD BE CHECKED FIRST! ***)
+ //
+ if( m_data.m_fileSize != GetGameDataManager()->GetGameDataSize() )
+ {
+ rTunePrintf( "ERROR: *** Loaded game data size = %d bytes (Expected data size = %d bytes)!\n",
+ m_data.m_fileSize, GetGameDataManager()->GetGameDataSize() );
+
+ return false;
+ }
+
+ // check magic number
+ //
+ if( m_data.m_magicNumber != MAGIC_NUMBER )
+ {
+ rTunePrintf( "ERROR: *** Loaded game data contains magic number = %d (Expected magic number = %d)!\n",
+ m_data.m_magicNumber, MAGIC_NUMBER );
+
+ return false;
+ }
+
+ // check timestamp for valid date
+ //
+ if( m_data.m_timeStamp.m_Month < 1 || m_data.m_timeStamp.m_Month > 12 ||
+ m_data.m_timeStamp.m_Day < 1 || m_data.m_timeStamp.m_Day > 31 )
+ {
+ rTunePrintf( "ERROR: *** Loaded game data contains invalid timestamp information!\n" );
+
+ return false;
+ }
+
+ // everything's cool, return 'OK' status
+ //
+ return true;
+}
+void CopyUnicodeToCharString(char *str, P3D_UNICODE* uni_str, int max_char)
+{
+ int i = 0;
+ while (*uni_str && i < max_char)
+ {
+ *str++ = (char)*uni_str++;
+ i++;
+ }
+ *str++ = 0;
+}
+void SaveGameInfo::FormatLevelMissionInfo(char levelMissionInfo[32]) const
+{
+#define MAX_LEVEL_MISSION_STRING 10
+ char tut_string[MAX_LEVEL_MISSION_STRING+1];
+ char l_string[MAX_LEVEL_MISSION_STRING+1];
+ char m_string[MAX_LEVEL_MISSION_STRING+1];
+
+ P3D_UNICODE* uni_string = GetTextBibleString( "TUTORIAL_ABBREVIATION" );
+ rTuneAssert(uni_string);
+ CopyUnicodeToCharString(tut_string, uni_string, MAX_LEVEL_MISSION_STRING);
+
+ uni_string = GetTextBibleString( "LEVEL_ABBREVIATION" );
+ rTuneAssert(uni_string);
+ CopyUnicodeToCharString(l_string, uni_string, MAX_LEVEL_MISSION_STRING);
+
+ uni_string = GetTextBibleString( "MISSION_ABBREVIATION" );
+ rTuneAssert(uni_string);
+ CopyUnicodeToCharString(m_string, uni_string, MAX_LEVEL_MISSION_STRING);
+
+ if( m_data.m_level == 1 && m_data.m_mission == 0 )
+ {
+ // level 1 tutorial mission
+ //
+ sprintf( levelMissionInfo, "(%s) ", tut_string );
+ }
+ else
+ {
+ sprintf( levelMissionInfo, "(%s%d %s%d) ",
+ l_string,
+ m_data.m_level,
+ m_string,
+ m_data.m_mission );
+ }
+}
+
+void
+SaveGameInfo::FormatDisplay( char* displayBuffer,
+ unsigned int bufferLength ) const
+{
+ char dateBuffer[ 64 ];
+ rAssert( displayBuffer != NULL );
+ rAssert( bufferLength >= 64 );
+
+ // format time and date display
+ //
+ sprintf( dateBuffer, "%02d/%02d/%02d %02d:%02d:%02d",
+#if defined( PAL ) && defined( RAD_PS2 ) // English date format
+ m_data.m_timeStamp.m_Day,
+ m_data.m_timeStamp.m_Month,
+#else // American date format
+ m_data.m_timeStamp.m_Month,
+ m_data.m_timeStamp.m_Day,
+#endif
+ m_data.m_timeStamp.m_Year % 100,
+ m_data.m_timeStamp.m_Hour,
+ m_data.m_timeStamp.m_Minute,
+ m_data.m_timeStamp.m_Second
+ );
+
+ // format level and mission display
+ //
+ char levelMissionInfo[ 32 ];
+ FormatLevelMissionInfo( levelMissionInfo );
+
+#ifdef RAD_GAMECUBE
+ // add number of blocks to display
+ //
+ P3D_UNICODE* unicodeString = GetTextBibleString( "BLOCKS" );
+ rAssert( unicodeString != NULL );
+
+ char numBlocksBuffer[ 32 ];
+ p3d::UnicodeToAscii( unicodeString, numBlocksBuffer, sizeof( numBlocksBuffer ) );
+
+ sprintf( displayBuffer, "%s %s (2 %s)", levelMissionInfo, dateBuffer, numBlocksBuffer );
+#else
+ sprintf( displayBuffer, "%s %s", levelMissionInfo, dateBuffer );
+#endif
+
+ // sanity check
+ //
+ rAssertMsg( strlen( displayBuffer ) < bufferLength, "*** Buffer Overrun!" );
+}
+
+int
+SaveGameInfo::CompareTimeStamps( const radDate& date1, const radDate& date2 )
+{
+ // compares two timestamps, and returns:
+ //
+ // - a positive integer if date1 is later than date2
+ // - zero if date1 is the same as date2
+ // - a negative integer if date1 is earlier than date2
+ //
+
+ int diff = 0;
+
+ if( date1.m_Year != date2.m_Year ) // compare years
+ {
+ diff = date1.m_Year - date2.m_Year;
+ }
+ else if( date1.m_Month != date2.m_Month ) // compare months
+ {
+ diff = date1.m_Month - date2.m_Month;
+ }
+ else if( date1.m_Day != date2.m_Day ) // compare days
+ {
+ diff = date1.m_Day - date2.m_Day;
+ }
+ else if( date1.m_Hour != date2.m_Hour ) // compare hours
+ {
+ diff = date1.m_Hour - date2.m_Hour;
+ }
+ else if( date1.m_Minute != date2.m_Minute ) // compare minutes
+ {
+ diff = date1.m_Minute - date2.m_Minute;
+ }
+ else if( date1.m_Second != date2.m_Second ) // compare seconds
+ {
+ diff = date1.m_Second - date2.m_Second;
+ }
+
+ return diff;
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
diff --git a/game/code/data/savegameinfo.h b/game/code/data/savegameinfo.h
new file mode 100644
index 0000000..8aa7d49
--- /dev/null
+++ b/game/code/data/savegameinfo.h
@@ -0,0 +1,101 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SaveGameInfo
+//
+// Description: Interface for the SaveGameInfo class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/10/18 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef SAVEGAMEINFO_H
+#define SAVEGAMEINFO_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <data/gamedata.h>
+
+#include <radtime.hpp>
+#include <radfile.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+struct SaveGameInfoData
+{
+ unsigned short m_magicNumber; // used for data verification
+ radDate m_timeStamp;
+ GameDataByte m_level;
+ GameDataByte m_mission;
+ unsigned int m_fileSize; // used for file version verification
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class SaveGameInfo : public GameDataHandler
+{
+public:
+ enum{ MAGIC_NUMBER = 1978 };
+
+ SaveGameInfo();
+ virtual ~SaveGameInfo();
+
+ static const unsigned int GetSize() { return sizeof( SaveGameInfoData ); }
+
+ // Implements Game Data Handler
+ //
+ virtual void LoadData( const GameDataByte* dataBuffer,
+ unsigned int numBytes );
+ virtual void SaveData( GameDataByte* dataBuffer,
+ unsigned int numBytes );
+
+ virtual void ResetData();
+
+ void FormatLevelMissionInfo(char levelMissionInfo[32]) const;
+
+ // Data accessors
+ //
+ const SaveGameInfoData* GetData() const { return &m_data; }
+
+ // Verify data
+ //
+ bool CheckData();
+
+ // Format display string for viewing in frontend screen
+ //
+ void FormatDisplay( char* displayBuffer,
+ unsigned int bufferLength ) const;
+
+ static int CompareTimeStamps( const radDate& date1,
+ const radDate& date2 );
+
+ char m_displayFilename[radFileFilenameMax+1];
+
+private:
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ SaveGameInfo( const SaveGameInfo& );
+ SaveGameInfo& operator= ( const SaveGameInfo& );
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ SaveGameInfoData m_data;
+
+};
+
+#endif // SAVEGAMEINFO_H
diff --git a/game/code/debug/alldebug.cpp b/game/code/debug/alldebug.cpp
new file mode 100644
index 0000000..d11b9ea
--- /dev/null
+++ b/game/code/debug/alldebug.cpp
@@ -0,0 +1,3 @@
+#include <debug/debuginfo.cpp>
+#include <debug/profiler.cpp>
+#include <debug/section.cpp>
diff --git a/game/code/debug/debuginfo.cpp b/game/code/debug/debuginfo.cpp
new file mode 100644
index 0000000..7b41485
--- /dev/null
+++ b/game/code/debug/debuginfo.cpp
@@ -0,0 +1,782 @@
+//==============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: debuginfo.cpp
+//
+// Description: In-game visual debug output
+//
+// History: 2002/07/02 + Migrated from Mirth -- Darwin Chau
+//
+//==============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <string.h>
+// FTech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+// Pure3D
+#include <p3d/font.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/texturefont.hpp>
+#include <p3d/unicode.hpp>
+#include <p3d/utility.hpp>
+#include <pddi/pddi.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <debug/debuginfo.h>
+#include <debug/section.h>
+
+#include <memory/srrmemory.h>
+
+#ifdef DEBUGINFO_ENABLED
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//The Adlib font. <sigh>
+static unsigned char gFont[] =
+#include <font/defaultfont.h>
+
+// Static pointer to instance of singleton.
+DebugInfo* DebugInfo::_Instance = NULL;
+
+static void Next()
+{
+ DebugInfo::GetInstance()->OnNext();
+}
+
+static void Switch()
+{
+ DebugInfo::GetInstance()->OnSwitch();
+}
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// DebugInfo::DebugInfo
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+DebugInfo::DebugInfo() :
+ _pDebugFont(NULL),
+ _pTypeFace(NULL),
+ _NumSection(0),
+ _StackSize(0),
+ _CurrentSection(0),
+ _DebugMenuTime(0.0f),
+ _isRenderEnabled(false),
+ _isCreationSectionOpen(false),
+ _mode(OFF),
+ _shader(NULL)
+{
+ for(int i=0; i<MaxSections; i++)
+ {
+ _ppSections[i] = NULL;
+ }
+
+ radDbgWatchAddFunction( "Next Section", (RADDEBUGWATCH_CALLBACK)Next, 0, "DebugInfo" );
+ radDbgWatchAddFunction( "Toggle Display", (RADDEBUGWATCH_CALLBACK)Switch, 0, "DebugInfo" );
+}
+
+
+//==============================================================================
+// DebugInfo::~DebugInfo
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+DebugInfo::~DebugInfo()
+{
+ if(_pDebugFont)
+ {
+ _pDebugFont->Release ();
+ }
+
+ if(_shader)
+ _shader->Release();
+
+ int i;
+ for(i=0; i<MaxSections; i++)
+ {
+ if(_ppSections[i])
+ delete (GMA_DEBUG, _ppSections[i]);
+ }
+}
+
+
+//==============================================================================
+// DebugInfo::CreateInstance
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "DebugInfo" );
+ HeapMgr()->PushHeap( GMA_DEBUG );
+
+ _Instance = new DebugInfo;
+
+ HeapMgr()->PopHeap ( GMA_DEBUG );
+MEMTRACK_POP_GROUP( "DebugInfo" );
+}
+
+
+//==============================================================================
+// DebugInfo::CreateNewSection
+//==============================================================================
+//
+// Description: Creates a new section
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::CreateNewSection( const char* section )
+{
+ HeapMgr()->PushHeap( GMA_DEBUG );
+ //Lazy section allocation
+ if(_ppSections[_NumSection])
+ {
+ _ppSections[_NumSection]->Reset( section );
+ }
+ else
+ {
+ if( _pDebugFont == NULL )
+ {
+ //
+ // find the font
+ //
+ _pDebugFont = p3d::find<tTextureFont>("adlibn_20");
+
+ if( _pDebugFont == NULL )
+ {
+ // Convert memory buffer into a texturefont.
+ //
+ p3d::load(gFont, DEFAULTFONT_SIZE, GMA_DEBUG);
+ _pDebugFont = p3d::find<tTextureFont>("adlibn_20");
+ rAssertMsg(_pDebugFont, ("ERROR - debug font not found."));
+ }
+
+ _pDebugFont->AddRef();
+ }
+
+ _ppSections[_NumSection] = new Section( _pDebugFont, section );
+ }
+ //_pStack[_StackSize++] = _NumSection;
+ _NumSection++;
+ HeapMgr()->PopHeap( GMA_DEBUG );
+}
+
+//==============================================================================
+// DebugInfo::DestroyInstance
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::DestroyInstance()
+{
+ delete(GMA_DEBUG, _Instance);
+}
+
+
+//==============================================================================
+// DebugInfo::GetInstance
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+DebugInfo* DebugInfo::GetInstance()
+{
+ if(!_Instance)
+ {
+ CreateInstance();
+ }
+
+ return _Instance;
+}
+
+//==============================================================================
+// DebugInfo::InitializeStaticVariables
+//==============================================================================
+//
+// Description: Initializes all the sections that we're ever going to use
+//
+// Parameters: none
+//
+// Return: none
+//
+//==============================================================================
+void DebugInfo::InitializeStaticVariables()
+{
+ GetInstance()->CreateNewSection( "Vehicle Terrain Type" );
+ GetInstance()->CreateNewSection( "Vehicle Shit" );
+}
+
+//==============================================================================
+// DebugInfo::OnSwitch
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::OnSwitch()
+{
+ _mode = (Mode) ((_mode + 1) % MODE_MAX);
+
+ if(_mode == OFF)
+ {
+ _isRenderEnabled = false;
+ _isBackgroundEnabled = false;
+ }
+ else if(_mode == BACKGROUND)
+ {
+ _isRenderEnabled = true;
+ _isBackgroundEnabled = true;
+ }
+ else if(_mode == NOBACKGROUND)
+ {
+ _isRenderEnabled = true;
+ _isBackgroundEnabled = false;
+ }
+
+}
+
+
+//==============================================================================
+// DebugInfo::Push
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+bool DebugInfo::Push(char* szSection)
+{
+MEMTRACK_PUSH_GROUP( "DebugInfo" );
+ HeapMgr()->PushHeap (GMA_DEBUG);
+
+ rAssert(szSection);
+ rAssert(_StackSize<MaxStackSize);
+
+ int i;
+ bool bFound = false;
+
+ //Check for exsisting section
+ for(i=0;i<_NumSection;i++)
+ {
+ //look for the global section (NULL) or a matching name
+ const char* szName = _ppSections[i]->GetName();
+ if(0==strcmp(szSection,szName))
+ {
+ _pStack[_StackSize++] = i;
+ bFound = true;
+ break;
+ }
+ }
+ //Create a new section
+ if(!bFound)
+ {
+ CreateNewSection( szSection );
+ }
+MEMTRACK_POP_GROUP( "DebugInfo" );
+ HeapMgr()->PopHeap (GMA_DEBUG);
+
+ //Return true if this is the current section
+ return (bFound && i==_CurrentSection);
+}
+
+
+//==============================================================================
+// DebugInfo::GetCurrentSection
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+const char* DebugInfo::GetCurrentSection()
+{
+ Section *pSect = _ppSections[_CurrentSection];
+ if (pSect && _isRenderEnabled)
+ return (pSect->GetName());
+ else
+ return (NULL);
+}
+
+
+//==============================================================================
+// DebugInfo::Pop
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::Pop()
+{
+ rAssert( _NumSection > 0 );
+ rAssert( _StackSize >= 0 );
+ --_StackSize;
+}
+
+
+
+//==============================================================================
+// int SignedMod
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+
+//==============================================================================
+int SignedMod(int a, int b)
+{
+ rAssert(b>0);
+ if (a>=0) return (a%b);
+ else return (b - ((-a)%b));
+}
+
+
+//==============================================================================
+// DebugInfo::Toggle
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::Toggle(int step)
+{
+ _CurrentSection = SignedMod(_CurrentSection+step, _NumSection);
+ _DebugMenuTime = 1.0f;
+}
+
+
+//==============================================================================
+// DebugInfo::SetAutoReset
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::SetAutoReset(bool autoreset)
+{
+ rAssert(_NumSection>0);
+ rAssert(_StackSize>0);
+
+ _ppSections[_pStack[_StackSize-1]]->SetAutoReset(autoreset);
+}
+
+
+
+//==============================================================================
+// DebugInfo::Reset
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::Reset(char* sectionName)
+{
+ rAssert(_NumSection>0);
+ rAssert(_StackSize>0);
+
+ _ppSections[_pStack[_StackSize-1]]->Reset(sectionName);
+}
+
+
+//==============================================================================
+// DebugInfo::AddLine
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::AddLine(const rmt::Vector& a, const rmt::Vector& b, tColour colour)
+{
+ rAssert(_NumSection>0);
+ rAssert(_StackSize>0);
+
+ _ppSections[_pStack[_StackSize-1]]->AddLine(a, b, colour);
+}
+
+
+//==============================================================================
+// DebugInfo::AddHVector
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::AddHVector(rmt::Vector o, rmt::Vector v, float h, tColour colour)
+{
+ // Add the origin to the vector
+ v.Add(o);
+ // Add the elevation
+ v.Add(rmt::Vector(0, h, 0));
+ o.Add(rmt::Vector(0, h, 0));
+ // Then, its a regular line
+ AddLine(o, v, colour);
+}
+
+
+//==============================================================================
+// DebugInfo::AddStar
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::AddStar(const rmt::Vector& vx, tColour colour, float scale)
+{
+ rAssert(_NumSection>0);
+ rAssert(_StackSize>0);
+
+ //Draw a kind of star
+ rmt::Vector a, b;
+ a = b = vx;
+ a.x += scale; b.x -= scale;
+ AddLine( a, b, colour );
+ a = b = vx;
+ a.y += scale; b.y -= scale;
+ AddLine( a, b, colour );
+ a = b = vx;
+ a.z += scale; b.z -= scale;
+ AddLine( a, b, colour );
+}
+
+
+//==============================================================================
+// DebugInfo::AddBox
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::AddBox(const rmt::Vector& a, const rmt::Vector& b, tColour colour)
+{
+ //Draw all 12 lines that make up the box
+ for(int i=0;i<4;i++)
+ {
+ AddLine(rmt::Vector((i&2)?a.x:b.x,(i&1)?a.y:b.y,a.z),
+ rmt::Vector((i&2)?a.x:b.x,(i&1)?a.y:b.y,b.z),colour);
+ AddLine(rmt::Vector((i&2)?a.x:b.x,a.y,(i&1)?a.z:b.z),
+ rmt::Vector((i&2)?a.x:b.x,b.y,(i&1)?a.z:b.z),colour);
+ AddLine(rmt::Vector(a.x,(i&2)?a.y:b.y,(i&1)?a.z:b.z),
+ rmt::Vector(b.x,(i&2)?a.y:b.y,(i&1)?a.z:b.z),colour);
+ }
+}
+
+
+//==============================================================================
+// DebugInfo::AddBox
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::AddBox(const rmt::Vector &center, const float r, tColour colour)
+{
+ rmt::Vector a,b;
+ a.Sub(center, rmt::Vector(r,r,r));
+ b.Add(center, rmt::Vector(r,r,r));
+
+ AddBox(a,b,colour);
+}
+
+
+//==============================================================================
+// DebugInfo::AddCircle
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::AddCircle( const rmt::Vector& center, const float r, tColour colour )
+{
+ rmt::Vector a,b;
+ int i;
+ int n = 8;
+ for( i = 0; i < n; i++ )
+ {
+ a.x = center.x + r * rmt::Cos( i * rmt::PI_2 / n );
+ a.y = center.y;
+ a.z = center.z + r * rmt::Sin( i * rmt::PI_2 / n );
+ b.x = center.x + r * rmt::Cos( ( i + 1 ) * rmt::PI_2 / n );
+ b.y = center.y;
+ b.z = center.z + r * rmt::Sin( ( i + 1 ) * rmt::PI_2 / n );
+ AddLine( a, b, colour );
+ }
+}
+
+
+//==============================================================================
+// DebugInfo::AddText
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::AddText(const char *szName, const rmt::Vector &pos, tColour colour)
+{
+ rAssert(_NumSection>0);
+ rAssert(_StackSize>0);
+
+ _ppSections[_pStack[_StackSize-1]]->AddText(szName, pos, colour);
+}
+
+
+//==============================================================================
+// DebugInfo::AddScreenLine
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::AddScreenLine(const rmt::Vector& a, const rmt::Vector& b, tColour colour)
+{
+ rAssert(_NumSection>0);
+ rAssert(_StackSize>0);
+
+ _ppSections[_pStack[_StackSize-1]]->AddScreenLine(a, b, colour);
+}
+
+
+//==============================================================================
+// DebugInfo::AddScreenText
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::AddScreenText(const char* szName, tColour colour )
+{
+ rAssert(_NumSection>0);
+ rAssert(_StackSize>0);
+
+ _ppSections[_pStack[_StackSize-1]]->AddScreenText(szName, colour);
+}
+
+
+//==============================================================================
+// DebugInfo::AddScreenText
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::AddScreenText(const char* szName, const rmt::Vector &a, tColour colour )
+{
+ rAssert(_NumSection>0);
+ rAssert(_StackSize>0);
+
+ _ppSections[_pStack[_StackSize-1]]->AddScreenText(szName, a,colour);
+}
+
+
+//==============================================================================
+// DebugInfo::RenderBackground
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::RenderBackground()
+{
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+ pddiProjectionMode mode = p3d::pddi->GetProjectionMode();
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_DEVICE);
+ p3d::pddi->SetCullMode(PDDI_CULL_NONE);
+
+ if( _shader == NULL )
+ {
+ //
+ // init the shader
+ //
+ _shader = p3d::device->NewShader("simple");
+ _shader->SetInt(PDDI_SP_SHADEMODE, PDDI_SHADE_FLAT);
+ }
+ _shader->SetInt(PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA);
+
+ pddiPrimStream* stream = p3d::pddi->BeginPrims(_shader, PDDI_PRIM_TRIANGLES, PDDI_V_C, 6);
+
+ float nearplane, farplane, fov, aspect;
+ p3d::pddi->GetCamera(&nearplane, &farplane, &fov, &aspect);
+ nearplane += 0.01f;
+
+ float width, height;
+ width = (float)p3d::display->GetWidth();
+ height = (float)p3d::display->GetHeight();
+ pddiColour colour(0, 0, 0, 200);
+
+ stream->Colour(colour); stream->Coord(0, height, nearplane);
+ stream->Colour(colour); stream->Coord(width, height, nearplane);
+ stream->Colour(colour); stream->Coord(width, 0, nearplane);
+ stream->Colour(colour); stream->Coord(0, height, nearplane);
+ stream->Colour(colour); stream->Coord(width, 0, nearplane);
+ stream->Colour(colour); stream->Coord(0, 0, nearplane);
+
+
+ p3d::pddi->EndPrims(stream);
+
+ p3d::pddi->SetProjectionMode(mode);
+ p3d::stack->Pop();
+}
+
+
+//==============================================================================
+// DebugInfo::Render
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void DebugInfo::Render()
+{
+ //rAssert(_StackSize==0);
+
+ // skip if disabled or no sections
+ if(! _isRenderEnabled) return;
+ if(_NumSection == 0) return;
+
+ // this makes the text easier to read
+ if(_isBackgroundEnabled) RenderBackground();
+
+ // render the title with the name of the current section
+ char sectionName[255] = "DebugInfo : ";
+ Section *pSect = _ppSections[_CurrentSection];
+ sprintf(sectionName, "DebugInfo : %s", pSect->GetName());
+ p3d::pddi->DrawString(sectionName, 40, 50, tColour(0,255,255));
+
+ // render the current section
+ pSect->Render();
+
+ //Clear all the sections after we display
+ int i;
+ for(i=0;i<_NumSection;i++)
+ {
+ if(_ppSections[i]->GetAutoReset())
+ {
+ _ppSections[i]->Reset(_ppSections[i]->GetName());
+ }
+ }
+}
+
+#endif // DEBUGINFO_ENABLED \ No newline at end of file
diff --git a/game/code/debug/debuginfo.h b/game/code/debug/debuginfo.h
new file mode 100644
index 0000000..5c1b460
--- /dev/null
+++ b/game/code/debug/debuginfo.h
@@ -0,0 +1,204 @@
+//==============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: debuginfo.h
+//
+// Description: In-game visual debug output
+//
+// History: 2002/07/02 + Migrated from Mirth -- Darwin Chau
+//
+//==============================================================================
+
+#ifndef DEBUGINFO_HPP
+#define DEBUGINFO_HPP
+
+
+//
+// DebugInfo is only available in RAD_DEBUG and RAD_TUNE configurations
+//
+#ifndef RAD_RELEASE
+#define DEBUGINFO_ENABLED
+#endif // RAD_RELEASE
+
+
+
+#include "main/commandlineoptions.h"
+
+
+//==============================================================================
+// *** USE THESE MACROS, DO NOT INVOKE DEBUG INFO DIRECTLY ***
+//==============================================================================
+
+#ifndef DEBUGINFO_ENABLED
+
+#define CREATE_DEBUGINFO()
+#define DESTROY_DEBUGINFO()
+
+#define DEBUGINFO_PUSH_SECTION(s)
+#define DEBUGINFO_POP_SECTION()
+
+#define DEBUGINFO_ADDSCREENTEXT(s)
+#define DEBUGINFO_ADDSCREENTEXTVECTOR(s, v)
+#define DEBUGINFO_ADDSCREENTEXTVECTORCOLOUR(s, v, c)
+#define DEBUGINFO_ADDSCREENLINE(v1, v2, c)
+#define DEBUGINFO_ADDLINE(v1, v2, c)
+#define DEBUGINFO_ADDCIRCLE(v1, v2, c)
+#define DEBUGINFO_ADDSTAR(v1, c, s)
+
+#define DEBUGINFO_TOGGLE(step)
+
+#define DEBUGINFO_RENDER();
+
+#else
+
+#define CREATE_DEBUGINFO() if(!CommandLineOptions::Get(CLO_FIREWIRE)) { DebugInfo::CreateInstance(); }
+#define DESTROY_DEBUGINFO() if(!CommandLineOptions::Get(CLO_FIREWIRE)) { DebugInfo::DestroyInstance(); }
+
+#define DEBUGINFO_PUSH_SECTION(s) (!CommandLineOptions::Get(CLO_FIREWIRE)) ? DebugInfo::GetInstance()->Push(s) : false
+#define DEBUGINFO_POP_SECTION() if(!CommandLineOptions::Get(CLO_FIREWIRE)) { DebugInfo::GetInstance()->Pop(); }
+
+#define DEBUGINFO_ADDSCREENTEXT(s) if(!CommandLineOptions::Get(CLO_FIREWIRE)) { DebugInfo::GetInstance()->AddScreenText(s); }
+#define DEBUGINFO_ADDSCREENTEXTVECTOR(s, v) if(!CommandLineOptions::Get(CLO_FIREWIRE)) { DebugInfo::GetInstance()->AddScreenText(s, v); }
+#define DEBUGINFO_ADDSCREENTEXTVECTORCOLOUR(s, v, c) if(!CommandLineOptions::Get(CLO_FIREWIRE)) { DebugInfo::GetInstance()->AddScreenText(s, v, c); }
+#define DEBUGINFO_ADDSCREENLINE(v1, v2, c) if(!CommandLineOptions::Get(CLO_FIREWIRE)) { DebugInfo::GetInstance()->AddScreenLine(v1, v2, c); }
+#define DEBUGINFO_ADDLINE(v1, v2, c) if(!CommandLineOptions::Get(CLO_FIREWIRE)) { DebugInfo::GetInstance()->AddLine(v1, v2, c); }
+#define DEBUGINFO_ADDCIRCLE(v1, v2, c) if(!CommandLineOptions::Get(CLO_FIREWIRE)) { DebugInfo::GetInstance()->AddCircle(v1, v2, c); }
+#define DEBUGINFO_ADDSTAR(v1, c, s) if(!CommandLineOptions::Get(CLO_FIREWIRE)) { DebugInfo::GetInstance()->AddStar(v1, c, s); }
+
+#define DEBUGINFO_TOGGLE(step) if(!CommandLineOptions::Get(CLO_FIREWIRE)) { DebugInfo::GetInstance()->Toggle(step); }
+
+#define DEBUGINFO_RENDER() if(!CommandLineOptions::Get(CLO_FIREWIRE)) { DebugInfo::GetInstance()->Render(); }
+
+
+//========================================
+// Nested Includes
+//========================================
+// Radmath
+#include <radmath/radmath.hpp>
+// Pure3D
+#include <p3d/p3dtypes.hpp>
+#include <pddi/pddi.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+class tFont;
+class tTypeFace;
+class Section;
+
+//==============================================================================
+//
+// Synopsis: DebugInfo class provides visual debugging support.
+//
+//==============================================================================
+class DebugInfo
+{
+public:
+ /** get the singleton */
+ static DebugInfo* GetInstance();
+
+ /** creates the singleton */
+ static void CreateInstance();
+
+ /** destroys the instance */
+ static void DestroyInstance();
+ static void InitializeStaticVariables();
+
+
+ /** Render */
+ void Render();
+
+ /** Push a section */
+ bool Push(char* szSection);
+
+ /** Pop a section */
+ void Pop();
+
+ /** Get the name of the last section pushed */
+ const char* GetCurrentSection();
+
+
+ //Toggle through the current sections
+ void Toggle(int step);
+
+ //Debugging output functions, for world space primitives
+ void BeginSectionCreation() { _isCreationSectionOpen = true; };
+ void EndSectionCreation() { _isCreationSectionOpen = false; };
+
+ /** make the section to resest automatically after the render */
+ void SetAutoReset(bool autoreset);
+
+ /** reset the current section
+ * @param sectionName the new name of the section
+ */
+ void Reset(char* sectionName);
+
+ void AddLine(const rmt::Vector &a, const rmt::Vector &b, tColour colour = tColour(255,255,255));
+ void AddHVector(rmt::Vector o, rmt::Vector v, float h, tColour colour = tColour(255,255,255));
+ void AddBox(const rmt::Vector &a, const rmt::Vector &b, tColour colour = tColour(255,255,255));
+ void AddBox(const rmt::Vector &center, const float r, tColour colour = tColour(255,255,255));
+ void AddCircle(const rmt::Vector &center, const float r, tColour colour = tColour(255,255,255));
+ void AddStar(const rmt::Vector& vx, tColour colour = tColour(255,255,255), float scale = 0.1f);
+ void AddText(const char *szName, const rmt::Vector &pos, tColour colour = tColour(255,255,255));
+
+ //Output functions for screen space primitives
+ //All screen space is in x=[0..1], y=[0..1], with (0,0) at the top left
+ void AddScreenLine(const rmt::Vector &a, const rmt::Vector &b, tColour colour = tColour(255,255,255));
+ void AddScreenText(const char* szName, tColour colour = tColour(255,255,255));
+ void AddScreenText(const char* szName, const rmt::Vector &a, tColour colour = tColour(255,255,255));
+
+ //
+ // GUI events
+ //
+ void OnNext() { Toggle(1); };
+ void OnSwitch();
+
+protected:
+ void CreateNewSection( const char* section );
+private:
+ /** Constructor */
+ DebugInfo();
+
+ /** Destructor */
+ ~DebugInfo();
+
+ /** Render the backgound */
+ void RenderBackground();
+
+
+// TODO: (chakib) do we need this ?
+#if 1
+ tFont* _pDebugFont;
+ tTypeFace* _pTypeFace;
+#endif
+
+ enum { MaxSections = 20 };
+ int _NumSection;
+ Section* _ppSections[MaxSections];
+
+ enum { MaxStackSize = 20 };
+ int _StackSize;
+ int _pStack[MaxStackSize];
+
+ int _CurrentSection;
+
+ float _DebugMenuTime;
+
+ static DebugInfo* _Instance;
+
+ bool _isRenderEnabled;
+ bool _isBackgroundEnabled;
+ bool _isCreationSectionOpen;
+
+ enum Mode {OFF, BACKGROUND, NOBACKGROUND, MODE_MAX};
+ Mode _mode;
+
+ pddiShader* _shader; //@- used the render the background
+};
+
+
+#endif // ! DEBUGINFO_ENABLED
+
+#endif // DEBUGINFO_HPP
+
diff --git a/game/code/debug/profiler.cpp b/game/code/debug/profiler.cpp
new file mode 100644
index 0000000..cb2949c
--- /dev/null
+++ b/game/code/debug/profiler.cpp
@@ -0,0 +1,753 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: PS2Platform
+//
+// Description: Abstracts the differences for setting up and shutting down
+// the different platforms.
+//
+// History: + Stolen and cleaned up from Penthouse -- Darwin Chau
+//
+//===========================================================================
+
+//========================================
+// System Includes
+//========================================
+// Libraries
+#include <string.h>
+#include <stdio.h>
+// Foundation
+#include <radtime.hpp>
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+// Pure3D
+#include <pddi/pddi.hpp>
+#include <p3d/p3dtypes.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <debug/profiler.h>
+#include <memory/srrmemory.h>
+
+
+#ifdef PROFILER_ENABLED
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+Profiler* Profiler::spInstance = NULL;
+
+int Profiler::sRed = 0;
+int Profiler::sGreen = 0;
+int Profiler::sBlue = 0;
+int Profiler::sPage = 0;
+int Profiler::sLeftOffset = 0;
+int Profiler::sTopOffset = 0;
+bool Profiler::sDisplay = false;
+bool Profiler::sDumpToOutput = false;
+bool Profiler::sEnableCollection = false;
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Profiler::CreateInstance
+//==============================================================================
+//
+// Description: Creates the Profiler.
+//
+// Parameters: None.
+//
+// Return: Pointer to the Profiler.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+Profiler* Profiler::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "Profiler" );
+ rAssert( spInstance == NULL );
+
+ spInstance = new(GMA_DEBUG) Profiler;
+ rAssert( spInstance );
+MEMTRACK_POP_GROUP( "Profiler" );
+
+ return spInstance;
+}
+
+//==============================================================================
+// Profiler::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the Profiler singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the Profiler.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+Profiler* Profiler::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// Profiler::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the Profiler.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void Profiler::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete( GMA_DEBUG, spInstance );
+ spInstance = NULL;
+}
+
+
+
+//==============================================================================
+// Profiler::Init
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Profiler::Init()
+{
+}
+
+
+
+//==============================================================================
+// Profiler::AllocSample
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return: the next available sample or NULL
+//
+//==============================================================================
+Profiler::ProfileSample *Profiler::AllocSample(void)
+{
+ if (mNextSampleAllocIndex >= NUM_SAMPLES) return NULL;
+
+ return &mSamples[mNextSampleAllocIndex++];
+}
+
+//==============================================================================
+// Profiler::AllocHistory
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return: the next available history or NULL
+//
+//==============================================================================
+Profiler::ProfileSampleHistory *Profiler::AllocHistory(void)
+{
+ if (mNextHistoryAllocIndex >= NUM_SAMPLES) return NULL;
+
+ return &mHistory[mNextHistoryAllocIndex++];
+}
+
+//==============================================================================
+// Profiler::BeginFrame
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Profiler::BeginFrame()
+{
+ int i;
+ for( i = 0; i < NUM_SAMPLES; ++i )
+ {
+ mSamples[i].bValid = false;
+ }
+
+ mOpenStackTop = -1;
+ mNextSampleAllocIndex = 0;
+
+ mStartProfile = ((float)radTimeGetMicroseconds()) / 1000.0F;
+
+ mOpenSampleStore->RemoveAll();
+
+ //
+ // Only collect profile data if we're actually going to show it on the screen.
+ // Check it here so that we don't turn it on/off in mid-sample.
+ //
+ sEnableCollection = sDisplay;
+}
+
+
+//==============================================================================
+// Profiler::EndFrame
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Profiler::EndFrame()
+{
+ unsigned int i = 0;
+
+ if( !sEnableCollection )
+ {
+ return;
+ }
+
+ mEndProfile = ((float)radTimeGetMicroseconds()) / 1000.0F;
+
+ while( i < mNextSampleAllocIndex)
+ {
+ if (!mSamples[i].bValid) continue;
+ float sampleTime, percentTime;
+
+ rAssertMsg(!mSamples[i].isOpen, "ProfileEnd() called without a ProfileBegin()" );
+
+ sampleTime = mSamples[i].fAccumulator - mSamples[i].fChildrenSampleTime;
+
+ float profileTime = mEndProfile - mStartProfile;
+
+ if( profileTime < 0.001F )
+ {
+ percentTime = 0.0f;
+ }
+ else
+ {
+ percentTime = ( sampleTime / profileTime ) * 100.0f;
+ rAssert( percentTime <= 100.0f );
+ }
+
+ //
+ // Add new measurement into the history and get the ave, min, and max
+ //
+// this->StoreProfileHistory( mSamples[i].szName,
+// percentTime,
+// mEndProfile - mStartProfile,
+// mSamples[i].uiSampleTime, // single sample
+// mSamples[i].uiAccumulator ); // total sample
+ this->StoreProfileHistory( mSamples[i].uid,
+ percentTime,
+ profileTime,
+ mSamples[i].fSampleTime, // single sample
+ mSamples[i].fAccumulator ); // total sample
+ i++;
+ }
+}
+
+
+//==============================================================================
+// Profiler::BeginProfile
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Profiler::BeginProfile( const char* name )
+{
+ int i;
+ tUID nameUID;
+
+ if( !sEnableCollection )
+ {
+ return;
+ }
+
+ i = 0;
+ nameUID = tEntity::MakeUID( name );
+
+ // If the sample is alread open this frame it will be in the store
+ ProfileSample *sample = mOpenSampleStore->Find(nameUID);
+
+ // Sample isn't in the store. Alloc a new one, set it up, and store it
+ if (sample == NULL)
+ {
+ sample = AllocSample();
+ rAssertMsg(sample != NULL, "Exceeded Maximum Available Profile Samples." );
+
+ sample->bValid = true;
+ sample->uid = nameUID;
+ sample->iProfileInstances = 0;
+ sample->fAccumulator = 0.0F;
+ sample->fChildrenSampleTime = 0.0F;
+ sample->fSampleTime = 0.0F;
+ sample->fTotalTime = 0.0F;
+ sample->iNumParents = 0;
+ sample->isOpen = false;
+
+ strncpy(sample->szName, name, 255);
+
+ mOpenSampleStore->Store(nameUID, sample);
+ }
+
+ rAssertMsg(!sample->isOpen, "Profiler section error: Started a second time without closing.");
+
+ sample->iProfileInstances++;
+ sample->isOpen = true;
+ sample->fStartTime = ((float)radTimeGetMicroseconds()) / 1000.0F;
+
+ // Ahh, pointer math.
+ unsigned sampleIndex = sample - mSamples;
+
+ // Add this sample to the open stack
+ rAssertMsg(mOpenStackTop < (MAX_PROFILER_DEPTH - 1), "Profiler error: Too many levels deep.");
+ ++mOpenStackTop;
+ mOpenStack[mOpenStackTop] = sampleIndex;
+}
+
+
+//==============================================================================
+// Profiler::EndProfile
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Profiler::EndProfile( const char* name )
+{
+ tUID nameUID;
+
+ if( !sEnableCollection )
+ {
+ return;
+ }
+
+ nameUID = tEntity::MakeUID( name );
+
+ ProfileSample *sample = mOpenSampleStore->Find(nameUID);
+ rAssertMsg(sample != NULL, "Profiler error: Ending a section that isn't open.");
+ rAssertMsg(sample->isOpen, "Profiler error: Ending a section that isn't open.");
+
+ float endTime = ((float)radTimeGetMicroseconds()) / 1000.0F;
+ float duration = endTime - sample->fStartTime;
+
+ sample->isOpen = false;
+ sample->iNumParents = mOpenStackTop;
+ sample->fAccumulator += duration;
+ sample->fSampleTime = duration;
+
+ // Pop the stack
+ mOpenStackTop--;
+
+ // Only update the parent if there is one
+ if (mOpenStackTop > 0)
+ {
+ unsigned int parentIndex = mOpenStack[mOpenStackTop];
+ mSamples[parentIndex].fChildrenSampleTime += duration;
+ }
+}
+
+
+
+//==============================================================================
+// Profiler::Render
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Profiler::Render(void)
+{
+ const int LEFT = 10;
+ const int TOP = 45;
+
+ if( !sDisplay )
+ {
+ return;
+ }
+
+ //int i = mScrollCount * NUM_VISIBLE_LINES;
+ unsigned int i = sPage * NUM_VISIBLE_LINES;
+
+ if( sDumpToOutput )
+ {
+ i = 0;
+ }
+
+ float startTime = ((float)radTimeGetMicroseconds()) / 1000.0F;
+
+ BEGIN_PROFILE("* Render Profile *")
+
+ //
+ // Display header info.
+ //
+ char fps[256];
+ sprintf(fps,"Game Time: %3.1f(fps) : %3.1f(ms) Adjusted Time: %3.1f(fps) : %3.1f(ms)", 1/(mFrameRate/1000), mFrameRate, 1/(mFrameRateAdjusted/1000), mFrameRateAdjusted);
+
+// const int SHADOW_OFFSET = 1;
+// tColour SHADOW_COLOUR(0,0,0);
+ tColour stringColour = tColour(sRed, sGreen, sBlue);
+/*
+ p3d::pddi->DrawString( fps,
+ LEFT + sLeftOffset + SHADOW_OFFSET,
+ TOP + sTopOffset + SHADOW_OFFSET,
+ SHADOW_COLOUR );
+
+ p3d::pddi->DrawString( "-------------------------------------------------------------------",
+ LEFT + sLeftOffset + SHADOW_OFFSET,
+ 20 + TOP + sTopOffset + SHADOW_OFFSET,
+ SHADOW_COLOUR);
+
+ p3d::pddi->DrawString( "Ave(%)\t| Single\t| #\t| Total\t| Profile Name\n",
+ LEFT + sLeftOffset + SHADOW_OFFSET,
+ 40 + TOP + sTopOffset + SHADOW_OFFSET,
+ SHADOW_COLOUR );
+
+ p3d::pddi->DrawString( "-------------------------------------------------------------------",
+ LEFT + sLeftOffset + SHADOW_OFFSET,
+ 60 + TOP + sTopOffset + SHADOW_OFFSET,
+ SHADOW_COLOUR);
+*/
+
+
+ p3d::pddi->DrawString( fps,
+ LEFT + sLeftOffset,
+ TOP + sTopOffset,
+ stringColour);
+
+ p3d::pddi->DrawString( "-------------------------------------------------------------------",
+ LEFT + sLeftOffset,
+ 20 + TOP + sTopOffset,
+ stringColour );
+
+ p3d::pddi->DrawString( "Ave(%)\t| Single\t| #\t| Total\t| Profile Name\n",
+ LEFT + sLeftOffset,
+ 40 + TOP + sTopOffset,
+ stringColour );
+
+ p3d::pddi->DrawString( "-------------------------------------------------------------------",
+ LEFT + sLeftOffset,
+ 60 + TOP + sTopOffset,
+ stringColour );
+
+ if( sDumpToOutput )
+ {
+ rTuneString( "-------------------------------------------------------------------------------\n" );
+ rTuneString( "Ave(%)\t| Single\t| #\t| Total\t| Profile Name\n" );
+ rTuneString( "-------------------------------------------------------------------------------\n" );
+ }
+
+ while( (i < mNextSampleAllocIndex) && mSamples[i].bValid )
+ {
+ unsigned int indent = 0;
+ float aveTime, minTime, maxTime, sampleAve, totalTime;
+ char line[256], name[256], indentedName[256];
+ char ave[32], min[32], max[32], num[32], sample[32], total[32];
+
+// this->GetProfileHistory( mSamples[i].szName, &aveTime, &minTime, &maxTime, &sampleAve, &totalTime);
+ this->GetProfileHistory( mSamples[i].uid, &aveTime, &minTime, &maxTime, &sampleAve, &totalTime);
+
+
+ // Format the data
+ sprintf( ave, "%3.2f", aveTime );
+ sprintf( min, "%3.2f", minTime );
+ sprintf( max, "%3.2f", maxTime );
+ sprintf( sample,"%3.2f", sampleAve );
+ sprintf( num, "%3d", mSamples[i].iProfileInstances );
+ sprintf( total, "%3.2f", totalTime );
+
+
+ strcpy( indentedName, mSamples[i].szName );
+
+
+ for( indent=0; indent<mSamples[i].iNumParents; indent++ )
+ {
+ sprintf( name, " %s", indentedName );
+ strcpy( indentedName, name );
+ }
+ sprintf(line,"%5s\t| %5s\t|%2s\t| %5s\t| %s ", ave, sample, num, total, indentedName);
+
+// p3d::pddi->DrawString( line,
+// LEFT + sLeftOffset + SHADOW_OFFSET,
+// 80 + TOP + ((i % NUM_VISIBLE_LINES)*20) + sTopOffset + SHADOW_OFFSET,
+// SHADOW_COLOUR);
+
+ p3d::pddi->DrawString( line,
+ LEFT + sLeftOffset,
+ 80 + TOP + ((i % NUM_VISIBLE_LINES)*20) + sTopOffset,
+ stringColour);
+
+ if( sDumpToOutput )
+ {
+ rTunePrintf( "%s\n", line );
+ }
+
+ i++;
+
+ if( i >= (unsigned int) (( sPage + 1 ) * NUM_VISIBLE_LINES ))
+ {
+ break;
+ }
+ }
+
+ END_PROFILE("* Render Profile *")
+
+ mDisplayTime = (((float)radTimeGetMicroseconds()) / 1000.0F) - startTime;
+
+ sDumpToOutput = false;
+}
+
+
+//==============================================================================
+// Profiler::Next
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Profiler::NextPage()
+{
+ ++sPage;
+
+ if( sPage == NUM_VISIBLE_PANES )
+ {
+ sPage = 0;
+ }
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Profiler::Profiler
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================//
+Profiler::Profiler() :
+ mOpenStackTop( -1 ),
+ mStartProfile( 0.0F ),
+ mEndProfile( 0.0F ),
+ mFrameRate( 0.0f ),
+ mFrameRateAdjusted( 0.0f ),
+ mDisplayTime( 0.0F ),
+ mNextHistoryAllocIndex( 0 )
+{
+ int i;
+ for( i = 0; i < NUM_SAMPLES; ++i )
+ {
+ mSamples[i].bValid = false;
+ mSamples[i].iNumParents = 0;
+ }
+
+MEMTRACK_PUSH_GROUP( "Profiler" );
+
+ HeapMgr()->PushHeap( GMA_DEBUG );
+
+ mOpenSampleStore = new HashTable<ProfileSample>(NUM_SAMPLES * 2, 100, NUM_SAMPLES * 2);
+ mOpenHistoryStore = new HashTable<ProfileSampleHistory>(NUM_SAMPLES * 2, 100, NUM_SAMPLES * 2);
+
+ mOpenSampleStore->AddRef();
+ mOpenHistoryStore->AddRef();
+
+ HeapMgr()->PopHeap( GMA_DEBUG );
+
+MEMTRACK_POP_GROUP( "Profiler" );
+
+ radDbgWatchAddBoolean( &sDisplay, "Display", "Profiler", 0, 0 );
+ radDbgWatchAddInt( &sPage, "Select Page", "Profiler", 0, 0, 0, NUM_VISIBLE_PANES );
+ radDbgWatchAddInt( &sLeftOffset, "Left Position", "Profiler", 0, 0, -1000, 1000 );
+ radDbgWatchAddInt( &sTopOffset, "Top Position", "Profiler", 0, 0, -1000, 1000 );
+ radDbgWatchAddInt( &sRed, "Text Colour - Red", "Profiler", 0, 0, 0, 255 );
+ radDbgWatchAddInt( &sGreen, "Text Colour - Green", "Profiler", 0, 0, 0, 255 );
+ radDbgWatchAddInt( &sBlue, "Text Colour - Blue", "Profiler", 0, 0, 0, 255 );
+ radDbgWatchAddBoolean( &sDumpToOutput, "Dump to Output Window", "Profiler", 0, 0 );
+}
+
+
+//==============================================================================
+// Profiler::~Profiler
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================//
+Profiler::~Profiler()
+{
+}
+
+
+//==============================================================================
+// Profiler::StoreProfileHistory
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Profiler::StoreProfileHistory
+(
+// char* name,
+ tUID nameUID,
+ float percent,
+ float elapsedTime,
+ float sampleTime,
+ float totalTime
+)
+{
+ float oldRatio;
+ float newRatio = 0.8f * (elapsedTime / 1000.0f);
+
+ if( newRatio > 1.0f )
+ {
+ newRatio = 1.0f;
+ }
+ oldRatio = 1.0f - newRatio;
+
+ mFrameRate = (mFrameRate * oldRatio) + (elapsedTime * newRatio);
+
+ // TODO: I had to put this in because there are cases where elapsedTime
+ // is 0 and mDisplayTime isn't... How can that happen?
+ //
+ float adjustedTime = 0.0F;
+
+ if( elapsedTime >= mDisplayTime )
+ {
+ adjustedTime = elapsedTime - mDisplayTime;
+ }
+
+ mFrameRateAdjusted = (mFrameRateAdjusted*oldRatio) + (adjustedTime * newRatio);
+
+ ProfileSampleHistory *history = mOpenHistoryStore->Find(nameUID);
+ if (history == NULL)
+ {
+ history = AllocHistory();
+ rAssertMsg(history != NULL, "Too many profiler histories.");
+
+ history->uid = nameUID;
+ history->fAve = percent;
+ history->fMin = percent;
+ history->fMax = percent;
+ history->fSampleAve = sampleTime;
+ history->fSampleTotal = totalTime;
+
+ mOpenHistoryStore->Store(nameUID, history);
+ }
+ else
+ {
+ history->fAve = (history->fAve * oldRatio) + (percent * newRatio);
+ history->fSampleAve = (history->fSampleAve * oldRatio) + (sampleTime * newRatio);
+ history->fSampleTotal = (history->fSampleTotal * oldRatio) + (totalTime * newRatio);
+
+ if( percent < history->fMin ) history->fMin = percent;
+ else history->fMin = (history->fMin * oldRatio) + (percent * newRatio);
+
+ if( history->fMin < 0.0f ) history->fMin = 0.0f;
+
+ if( percent > history->fMax ) history->fMax = percent;
+ else history->fMax = (history->fMax * oldRatio) + (percent * newRatio);
+ }
+
+}
+
+
+//==============================================================================
+// Profiler::GetProfileHistory
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Profiler::GetProfileHistory
+(
+// char* name,
+ tUID nameUID,
+ float* ave,
+ float* min,
+ float* max,
+ float* sample,
+ float* total
+)
+{
+ *ave = *min = *max = *sample = 0.0f;
+
+ ProfileSampleHistory *history = mOpenHistoryStore->Find(nameUID);
+ //rAssertMsg(history != NULL, "Get Profile history Error: Never been stored!");
+
+ if (history != NULL)
+ {
+ *ave = history->fAve;
+ *min = history->fMin;
+ *max = history->fMax;
+ *sample = history->fSampleAve;
+ *total = history->fSampleTotal;
+ }
+}
+
+
+#endif // PROFILER_ENABLED \ No newline at end of file
diff --git a/game/code/debug/profiler.h b/game/code/debug/profiler.h
new file mode 100644
index 0000000..ba61ff1
--- /dev/null
+++ b/game/code/debug/profiler.h
@@ -0,0 +1,206 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: profiler.h
+//
+// Description: In-game profiler.
+//
+// History: 5/8/2002 + Migrated from SRR -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef PROFILER_H
+#define PROFILER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+#if !defined( RAD_RELEASE ) && !defined( WORLD_BUILDER ) && !defined( RAD_MW ) && !defined( RAD_WIN32 )
+ #define PROFILER_ENABLED
+#endif // RAD_RELEASE
+
+
+#include "main/commandlineoptions.h"
+
+#include <p3d/entity.hpp>
+#include <radload/utility/hashtable.hpp>
+
+//===========================================================================
+// *** USE THESE MACROS, DO NOT INVOKE PROFILER DIRECTLY ***
+//===========================================================================
+
+
+#ifndef PROFILER_ENABLED
+
+ #define CREATE_PROFILER()
+ #define DESTROY_PROFILER()
+
+ #define BEGIN_PROFILE(string)
+ #define END_PROFILE(string)
+
+ #define BEGIN_PROFILER_FRAME()
+ #define END_PROFILER_FRAME()
+
+ #define RENDER_PROFILER()
+
+ #define SNSTART(id, txt)
+ #define SNSTOP(id)
+
+#else
+
+#ifdef SNTUNER
+#include <SNTuner.h>
+ #define SNSTART(id, txt) snStartMarker(id, txt)
+ #define SNSTOP(id) snStopMarker(id);
+#endif
+
+ #define CREATE_PROFILER() if(!(CommandLineOptions::Get(CLO_DESIGNER) || CommandLineOptions::Get(CLO_FIREWIRE))) { Profiler::CreateInstance(); }
+ #define DESTROY_PROFILER() if(!(CommandLineOptions::Get(CLO_DESIGNER) || CommandLineOptions::Get(CLO_FIREWIRE))) { Profiler::DestroyInstance(); }
+
+ #define BEGIN_PROFILE(string) if(!(CommandLineOptions::Get(CLO_DESIGNER) || CommandLineOptions::Get(CLO_FIREWIRE))) { GetProfiler()->BeginProfile(string); }
+ #define END_PROFILE(string) if(!(CommandLineOptions::Get(CLO_DESIGNER) || CommandLineOptions::Get(CLO_FIREWIRE))) { GetProfiler()->EndProfile(string); }
+
+ #define BEGIN_PROFILER_FRAME() if(!(CommandLineOptions::Get(CLO_DESIGNER) || CommandLineOptions::Get(CLO_FIREWIRE))) { GetProfiler()->BeginFrame(); }
+ #define END_PROFILER_FRAME() if(!(CommandLineOptions::Get(CLO_DESIGNER) || CommandLineOptions::Get(CLO_FIREWIRE))) { GetProfiler()->EndFrame(); }
+
+ #define RENDER_PROFILER() if(!(CommandLineOptions::Get(CLO_DESIGNER) || CommandLineOptions::Get(CLO_FIREWIRE))) { GetProfiler()->Render(); }
+
+
+#define MAX_PROFILER_DEPTH 32
+
+//===========================================================================
+//
+// Description: Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints: Describe here what you require of the client which uses or
+// has this class - essentially, the converse of the description
+// above. A constraint is an expression of some semantic
+// condition that must be preserved, an invariant of a class or
+// relationship that must be preserved while the system is in a
+// steady state.
+//
+//===========================================================================
+class Profiler
+{
+ public:
+
+ static Profiler* CreateInstance();
+ static Profiler* GetInstance();
+ static void DestroyInstance();
+
+ void Init();
+
+ void BeginProfile( const char* name );
+ void EndProfile( const char* name );
+
+ void BeginFrame();
+ void EndFrame();
+
+ void ToggleDisplay() { sDisplay = !sDisplay; }
+ void NextPage();
+
+ void Render();
+
+ //
+ // Watcher tunable
+ //
+ static int sRed;
+ static int sGreen;
+ static int sBlue;
+ static int sPage;
+ static int sLeftOffset;
+ static int sTopOffset;
+ static bool sDisplay;
+ static bool sDumpToOutput;
+
+ private:
+
+
+ Profiler();
+ ~Profiler();
+
+
+ void StoreProfileHistory( tUID nameUID,
+ float percent,
+ float elapsedTime,
+ float sampleTime,
+ float totalTime );
+
+ void GetProfileHistory( tUID nameUID,
+ float* ave,
+ float* min,
+ float* max ,
+ float* sample,
+ float* total );
+ struct ProfileSample
+ {
+ bool bValid; // Whether the data is valid
+ tUID uid; // Hashed name for comparisons.
+ unsigned int iProfileInstances; // # of times ProfileBegin called
+ float fStartTime; // The current open profile start time
+ float fAccumulator; // All samples this frame added together
+ float fChildrenSampleTime; // Time taken by all children
+ float fSampleTime; // Time for a single pass
+ float fTotalTime; // Time for all passess
+ unsigned int iNumParents; // Number of profile parents
+ bool isOpen; // Is the profile section open?
+ char szName[256]; // Name of sample
+ };
+
+ struct ProfileSampleHistory
+ {
+ tUID uid; //Hased name for comparisons
+ float fAve; //Average time per frame (percentage)
+ float fMin; //Minimum time per frame (percentage)
+ float fMax; //Maximum time per frame (percentage)
+ float fSampleAve;
+ float fSampleTotal;
+ };
+
+ static Profiler* spInstance;
+
+ ProfileSample *AllocSample(void);
+ ProfileSampleHistory *AllocHistory(void);
+
+ enum
+ {
+ NUM_SAMPLES = 256, // Must be power of two for hash table
+ NUM_VISIBLE_LINES = 15,
+ NUM_VISIBLE_PANES = 1 + ((NUM_SAMPLES-1) / NUM_VISIBLE_LINES )
+ };
+
+ HashTable<ProfileSample> *mOpenSampleStore;
+ HashTable<ProfileSampleHistory> *mOpenHistoryStore;
+
+ ProfileSample mSamples[NUM_SAMPLES];
+ ProfileSampleHistory mHistory[NUM_SAMPLES];
+
+ unsigned int mOpenStack[MAX_PROFILER_DEPTH];
+ int mOpenStackTop;
+
+ float mStartProfile;
+ float mEndProfile;
+
+ float mFrameRate;
+ float mFrameRateAdjusted;
+
+ float mDisplayTime;
+ unsigned int mNextSampleAllocIndex;
+ unsigned int mNextHistoryAllocIndex;
+
+ static bool sEnableCollection;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline Profiler* GetProfiler() { return( Profiler::GetInstance() ); }
+
+#endif // PROFILER_ENABLED
+
+#endif // PROFILER_H \ No newline at end of file
diff --git a/game/code/debug/ps2perf.hpp b/game/code/debug/ps2perf.hpp
new file mode 100644
index 0000000..562b4bf
--- /dev/null
+++ b/game/code/debug/ps2perf.hpp
@@ -0,0 +1,263 @@
+// Performance counter class for PS2
+// Nov 2001 - Amit Bakshi
+
+#ifndef PS2PERF_HPP
+#define PS2PERF_HPP
+
+/************************************************************************************/
+// nov6/2001 amb - Performance Counter class
+// based on David Coombes' (david_coombes@playstation.sony.com) code
+// sample usage :
+//
+// void TestSinCos()
+// {
+// ps2Perf perf("mySinCos");
+//
+// for(int i =0; i < 16; i++)
+// {
+// perf.StartSample();
+//
+// float s,c;
+// mySinCos( i, &s , &c );
+//
+// perf.EndSample();
+// }
+// perf.PrintStats();
+// };
+/************************************************************************************/
+
+#define ClocksToMs 294912
+
+inline unsigned int GetCycleCounter(void)
+{
+ unsigned int ret;
+ asm __volatile__ ("mfc0 %0,$9" : "=r" (ret) );
+ return ret;
+}
+
+// amb nov6/2001 - caution : do not call in middle of the code,
+// it'll mess up PDDI stats
+inline void ResetCycleCounter(void)
+{
+ asm __volatile__ ("mtc0 $0,$9 ");
+};
+
+
+class ps2Perf
+{
+public:
+
+ ps2Perf(const char* what)
+ {
+ strncpy( desc,what,sizeof(desc)-1);
+ Reset();
+ };
+
+ ~ps2Perf()
+ {
+ asm __volatile__ ("mtps $0,0");
+ };
+
+ inline void Reset()
+ {
+ count = 0;
+ for(int i = 0; i < PC0_NO_EVENT; i++)
+ {
+ pc0[i].min = pc1[i].min = 0xffff;
+ pc0[i].max = pc1[i].max = 0;
+ pc0[i].cur = pc1[i].cur = 0;
+ pc0[i].tot = pc1[i].tot = 0;
+ pc0[i].num = pc1[i].num = 0;
+ }
+ }
+
+ inline void BeginSample()
+ {
+ int evt = count % PC0_NO_EVENT;
+ BeginSample(evt);
+ };
+
+ inline void BeginSample(int evt)
+ {
+ pccr.cl0 = 0x8; // only user mode
+ pccr.event0 = evt;
+ pccr.cl1 = 0x8;
+ pccr.event1 = evt;
+ pccr.cte = 1;
+
+ int hack = *((int*)(&pccr));
+
+ asm __volatile__("
+ .set noreorder
+ .set noat
+ mtps $0,0 # halt performance counters
+ sync.p #
+ mtpc $0,0 # set perfcounter 0 to zero
+ mtpc $0,1 # set perfcounter 1 to zero
+ sync.p #
+ mtps %0,0 # truly - we rule ( well stewart does anyway...)
+ .set reorder
+ .set at
+ "
+ : // no output
+ : "r"(hack)
+ );
+ }
+ inline void EndSample()
+ {
+ int evt = count % PC0_NO_EVENT;
+ EndSample(evt);
+ count++;
+ };
+
+ inline void EndSample(int evt)
+ {
+ register unsigned int ret_pc0=0;
+ register unsigned int ret_pc1=0;
+
+ asm __volatile__("
+ .set noreorder
+ .set noat
+ mfpc %0,0
+ mfpc %1,1
+ .set reorder
+ .set at
+ ":"=r"(ret_pc0),"=r"(ret_pc1));
+
+ if(ret_pc0<pc0[evt].min) pc0[evt].min = ret_pc0;
+ if(ret_pc0>pc0[evt].max) pc0[evt].max = ret_pc0;
+ pc0[evt].cur = ret_pc0;
+ pc0[evt].tot+= ret_pc0;
+ pc0[evt].num++;
+
+ if(ret_pc1<pc1[evt].min) pc1[evt].min = ret_pc1;
+ if(ret_pc1>pc1[evt].max) pc1[evt].max = ret_pc1;
+ pc1[evt].cur = ret_pc1;
+ pc1[evt].tot+= ret_pc1;
+ pc1[evt].num++;
+
+ asm __volatile__ ("mtps $0,0");
+ };
+
+ inline void StopCounters()
+ {
+ asm __volatile__ ("mtps $0,0");
+ }
+
+ void PrintStats()
+ {
+ #define PRINT_STAT_0(desc,i) if (pc0[i].num) printf(desc "%6d, %6d, %6d, %6d\n",pc0[i].min, pc0[i].max, pc0[i].cur, pc0[i].tot/pc0[i].num);
+ #define PRINT_STAT_1(desc,i) if (pc1[i].num) printf(desc "%6d, %6d, %6d, %6d\n",pc1[i].min, pc1[i].max, pc1[i].cur, pc1[i].tot/pc1[i].num);
+
+ printf("==== %s ====(total iterations)%d (per event)%d (frame%%) ===========\n",desc,count,(count/PC0_NO_EVENT));
+ printf("Event , min, max, cur, ave \n");
+
+ PRINT_STAT_0("Processor cycle ,", 1 );
+ PRINT_STAT_0("Single instructions issue ,", 2 );
+ PRINT_STAT_0("Branch issued ,", 3 );
+ PRINT_STAT_0("BTAC miss ,", 4 );
+ PRINT_STAT_0("ITLB miss ,", 5 );
+ PRINT_STAT_0("Instruction cache miss ,", 6 );
+ PRINT_STAT_0("DTLB accessed ,", 7 );
+ PRINT_STAT_0("Non-blocking load ,", 8 );
+ PRINT_STAT_0("WBB single request ,", 9 );
+ PRINT_STAT_0("WBB burst request ,",10 );
+ PRINT_STAT_0("CPU address bus busy ,",11 );
+ PRINT_STAT_0("Instruction completed ,",12 );
+ PRINT_STAT_0("Non-BDS instruction completed ,",13 );
+ PRINT_STAT_0("COP2 instruction completed ,",14 );
+ PRINT_STAT_0("Load completed ,",15 );
+
+ PRINT_STAT_1("Low-order branch issued ,", 0 );
+ PRINT_STAT_1("Processor cycle ,", 1 );
+ PRINT_STAT_1("Dual instructions issue ,", 2 );
+ PRINT_STAT_1("Branch miss-predicted ,", 3 );
+ PRINT_STAT_1("TLB miss ,", 4 );
+ PRINT_STAT_1("DTLB miss ,", 5 );
+ PRINT_STAT_1("Data cache miss ,", 6 );
+ PRINT_STAT_1("WBB single request unavailable,", 7 );
+ PRINT_STAT_1("WBB burst request unavailable ,", 8 );
+ PRINT_STAT_1("WBB burst request almost full ,", 9 );
+ PRINT_STAT_1("WBB burst request full ,",10 );
+ PRINT_STAT_1("CPU data bus busy ,",11 );
+ PRINT_STAT_1("Instruction completed ,",12 );
+ PRINT_STAT_1("Non-BDS instruction completed ,",13 );
+ PRINT_STAT_1("COP1 instruction completed ,",14 );
+ PRINT_STAT_1("Store completed ,",15 );
+
+ #undef PRINT_STAT_0
+ #undef PRINT_STAT_1
+
+ }
+
+private:
+ enum PCOUNT0_EVENT // Performance Counter 0 Events
+ {
+ PC0_RESERVED =(0 ),
+ PC0_CPU_CYCLE =(1 ), // Processor cycle
+ PC0_SINGLE_ISSUE =(2 ), // Single instructions issue
+ PC0_BRANCH_ISSUED =(3 ), // Branch issued
+ PC0_BTAC_MISS =(4 ), // BTAC miss
+ PC0_ITLB_MISS =(5 ), // ITLB miss
+ PC0_ICACHE_MISS =(6 ), // Instruction cache miss
+ PC0_DTLB_ACCESSED =(7 ), // DTLB accessed
+ PC0_NONBLOCK_LOAD =(8 ), // Non-blocking load
+ PC0_WBB_SINGLE_REQ =(9 ), // WBB single request
+ PC0_WBB_BURST_REQ =(10), // WBB burst request
+ PC0_ADDR_BUS_BUSY =(11), // CPU address bus busy
+ PC0_INST_COMP =(12), // Instruction completed
+ PC0_NON_BDS_COMP =(13), // Non-BDS instruction completed
+ PC0_COP2_COMP =(14), // COP2 instruction completed
+ PC0_LOAD_COMP =(15), // Load completed
+ PC0_NO_EVENT =(16) // No event
+ };
+
+ enum PCOUNT1_EVENT // Performance Counter 1 Events
+ {
+ PC1_LOW_BRANCH_ISSUED =(0 ), // Low-order branch issued
+ PC1_CPU_CYCLE =(1 ), // Processor cycle
+ PC1_DUAL_ISSUE =(2 ), // Dual instructions issue
+ PC1_BRANCH_MISS_PREDICT =(3 ), // Branch miss-predicted
+ PC1_TLB_MISS =(4 ), // TLB miss
+ PC1_DTLB_MISS =(5 ), // DTLB miss
+ PC1_DCACHE_MISS =(6 ), // Data cache miss
+ PC1_WBB_SINGLE_UNAVAIL =(7 ), // WBB single request unavailable
+ PC1_WBB_BURST_UNAVAIL =(8 ), // WBB burst request unavailable
+ PC1_WBB_BURST_ALMOST =(9 ), // WBB burst request almost full
+ PC1_WBB_BURST_FULL =(10), // WBB burst request full
+ PC1_DATA_BUS_BUSY =(11), // CPU data bus busy
+ PC1_INST_COMP =(12), // Instruction completed
+ PC1_NON_BDS_COMP =(13), // Non-BDS instruction completed
+ PC1_COP1_COMP =(14), // COP1 instruction completed
+ PC1_STORE_COMP =(15), // Store completed
+ PC1_NO_EVENT =(16) // No event
+ };
+
+ struct count_t
+ {
+ unsigned int min,max,tot,cur,num;
+ };
+
+ // nov6/2001 amb - see p82 EE Core User's Manual 4.0
+ struct pccr_t
+ {
+ unsigned pad0:1; // unused
+ unsigned cl0:4; // events in which mode (eg user/kernel/super/exception)
+ unsigned event0:5; // event to count in counter 0 (see PCOUNT0_EVENT)
+ unsigned pad1:1; // unused
+ unsigned cl1:4; // events in which mode (eg user/kernel/super/exception)
+ unsigned event1:5; // event to count in counter 1 (see PCOUNT1_EVENT)
+ unsigned pad2:11; // unused
+ unsigned cte:1; // counter enable
+ };
+
+ char desc[32];
+
+ pccr_t pccr; // 16 events
+
+ count_t pc0[PC0_NO_EVENT];
+ count_t pc1[PC0_NO_EVENT];
+ unsigned int count;
+};
+
+#endif // PS2PERF_HPP
diff --git a/game/code/debug/section.cpp b/game/code/debug/section.cpp
new file mode 100644
index 0000000..d87379e
--- /dev/null
+++ b/game/code/debug/section.cpp
@@ -0,0 +1,236 @@
+#include <debug/section.h>
+#include <p3d/matrixstack.hpp>
+#include <p3d/utility.hpp>
+
+Section::Section(tFont* pFont, const char* szName) :
+ _pDebugFont(pFont),
+ _pLineShader(NULL),
+ _autoReset(true)
+{
+ assert(szName);
+
+ int i;
+
+ Reset(szName);
+
+ _pLineShader = p3d::device->NewShader(/*p3d::pddi, */"simple");
+ _pLineShader->AddRef();
+ _pLineShader->SetInt(PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA);
+
+ //Allocate string primitives for the 3d strings
+ for(i=0;i<MaxText;i++)
+ {
+ if(_pDebugFont)
+ {
+ _pDebugFont->AddRef();
+
+ P3D_UNICODE string[256];
+ char emptyString[] = "Empty String";
+ p3d::AsciiToUnicode(emptyString, string, strlen(emptyString));
+
+ _pText[i]._pText = new tTextString(_pDebugFont, string);
+ _pText[i]._pText->AddRef();
+ }
+ else
+ {
+ _pText[i]._pText = NULL;
+ }
+ }
+}
+Section::~Section()
+{
+ for(int i=0;i<MaxText;i++)
+ {
+ tRefCounted::Release(_pText[i]._pText);
+ }
+
+ tRefCounted::Release(_pLineShader);
+ tRefCounted::Release(_pDebugFont);
+}
+
+void Section::Reset(const char *szName)
+{
+ assert(szName);
+ strncpy(_szName, szName, sizeof(_szName));
+
+ _NumLines = _NumText = _NumScreenLines = _NumScreenText = 0;
+ ClearScreenText();
+}
+
+const char* Section::GetName() const
+{
+
+ return _szName;
+}
+
+void Section::AddLine(const rmt::Vector& a, const rmt::Vector& b, tColour colour/*, float time*/)
+{
+ if(_NumLines < MaxLines)
+ {
+ _Lines[_NumLines]._a = a;
+ _Lines[_NumLines]._b = b;
+ _Lines[_NumLines]._Colour = colour;
+ _NumLines++;
+ }
+}
+
+void Section::AddText(const char *szName, const rmt::Vector &pos, tColour colour/*, float time = 0*/)
+{
+ assert(szName);
+ if(_NumText < MaxText && _pText[_NumText]._pText)
+ {
+ tTextString* p = _pText[_NumText]._pText;
+
+ P3D_UNICODE string[256];
+ p3d::AsciiToUnicode(szName, string, strlen(szName));
+
+ p->SetString(string);
+ p->SetColour(colour);
+ p->SetScale(20.0f); //Fudge factor to make font appear about 1 meter high
+ _pText[_NumText]._Pos = pos;
+ _NumText++;
+ }
+}
+
+void Section::AddScreenLine(const rmt::Vector &a, const rmt::Vector &b, tColour colour)
+{
+ if(_NumScreenLines < MaxScreenLines)
+ {
+ _ScreenLines[_NumScreenLines]._a = a;
+ _ScreenLines[_NumScreenLines]._b = b;
+ _ScreenLines[_NumScreenLines]._Colour = colour;
+ _NumScreenLines++;
+ }
+}
+
+void Section::AddScreenText(const char* szName, tColour colour )
+{
+ assert(szName);
+ ScreenText &pST = _pScreenText[_NumScreenText];
+
+ if(_NumScreenText<MaxScreenText && pST._pText)
+ {
+ strncpy(pST._pText, szName, sizeof(pST._pText));
+ pST._Colour = colour;
+ pST._Pos = rmt::Vector(0.05f,_ScreenTextPos, 0);
+
+ //TODO:get correct debug font height!
+ _ScreenTextPos += 0.03f;
+ _NumScreenText++;
+ }
+}
+void Section::AddScreenText(const char* szName, const rmt::Vector &a, tColour colour)
+{
+ assert(szName);
+ ScreenText &pST = _pScreenText[_NumScreenText];
+
+ if(_NumScreenText<MaxScreenText && pST._pText)
+ {
+ if(pST._pText)
+ strncpy(pST._pText, szName, sizeof(pST._pText));
+ pST._Colour = colour;
+ pST._Pos = a;
+ _NumScreenText++;
+ }
+}
+
+void Section::ClearScreenText()
+{
+ _NumScreenText = 0;
+ _ScreenTextPos = 0.15f;
+}
+
+void Section::Render()
+{
+ pddiPrimStream* pStream;
+ int i;
+ assert(_pLineShader);
+
+ //Special case to get the pddi debugging information
+ p3d::pddi->EnableStatsOverlay(0==strcmp(_szName, "pddi"));
+
+ //Stream out world space lines
+ if(_NumLines > 0)
+ {
+ pStream = p3d::pddi->BeginPrims(_pLineShader, PDDI_PRIM_LINES, PDDI_V_C, _NumLines*2);
+ if(pStream)
+ {
+ for(i=0; i<_NumLines; i++)
+ {
+ Line &l = _Lines[i];
+
+ pddiVector A(l._a.x, l._a.y, l._a.z);
+ pddiVector B(l._b.x, l._b.y, l._b.z);
+ pStream->Vertex(&A,l._Colour);
+ pStream->Vertex(&B,l._Colour);
+ }
+ }
+ p3d::pddi->EndPrims(pStream);
+ }
+
+ //Draw all the world space vector text
+ for(i=0;i<_NumText;i++)
+ {
+ p3d::stack->Push();
+ p3d::stack->Translate(_pText[i]._Pos);
+ _pText[i]._pText->Display();
+ p3d::stack->Pop();
+ }
+
+ //Draw screen space lines
+ {
+ //Setup the matrix stack
+ p3d::stack->Push();
+ p3d::pddi->EnableZBuffer(false);
+ p3d::stack->LoadIdentity();
+ p3d::stack->Scale((float)p3d::display->GetWidth(),(float)p3d::display->GetHeight(),1);
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_DEVICE);
+
+ //Stream out lines
+ if(_NumScreenLines > 0)
+ {
+ pStream = p3d::pddi->BeginPrims(_pLineShader, PDDI_PRIM_LINES, PDDI_V_C, _NumScreenLines*2);
+ if(pStream)
+ {
+ for(i=0; i<_NumScreenLines; i++)
+ {
+ Line &l = _ScreenLines[i];
+
+ pddiVector A(l._a.x, l._a.y, 1);
+ pddiVector B(l._b.x, l._b.y, 1);
+ pStream->Vertex(&A,l._Colour);
+ pStream->Vertex(&B,l._Colour);
+ }
+ }
+ p3d::pddi->EndPrims(pStream);
+ }
+
+ //Restore the old stack
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_PERSPECTIVE);
+ p3d::pddi->EnableZBuffer(true);
+ p3d::stack->Pop();
+ }
+
+ //Draw all the screen text
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+ int height = p3d::display->GetHeight();
+ int width = p3d::display->GetWidth();
+
+ for(i=0;i<_NumScreenText;i++)
+ {
+ ScreenText &pST = _pScreenText[i];
+ if(pST._pText)
+ {
+
+ p3d::pddi->DrawString(pST._pText,
+ (int)(pST._Pos.x*width),
+ (int)(pST._Pos.y*height),
+ pST._Colour);
+
+ }
+ }
+
+ p3d::stack->Pop();
+}
+
diff --git a/game/code/debug/section.h b/game/code/debug/section.h
new file mode 100644
index 0000000..85547ba
--- /dev/null
+++ b/game/code/debug/section.h
@@ -0,0 +1,108 @@
+
+
+#if 1
+
+#ifndef _SECTION_HPP
+#define _SECTION_HPP
+
+#include <p3d/textstring.hpp>
+#include <p3d/font.hpp>
+#include <pddi/pddi.hpp>
+#include <radmath/radmath.hpp>
+
+// TODO: move this to internal namespace
+
+/**
+ * Section.
+ */
+class Section
+{
+public:
+ /** Constructor
+ * @param sectionName name of the section
+ */
+ Section(tFont* pFont, const char* sectionName);
+
+ /** Destructor */
+ ~Section();
+
+ void SetAutoReset(bool autoReset) { _autoReset = autoReset; }
+ bool GetAutoReset() { return _autoReset; }
+
+ void Reset(const char *name);
+ void ClearScreenText();
+ void Render();
+
+ const char* GetName() const;
+
+ //Debugging output functions
+ void AddLine(const rmt::Vector &a, const rmt::Vector &b, tColour colour);
+ void AddText(const char *szName, const rmt::Vector &pos, tColour colour);
+
+ //Output functions for screen space primitives
+ //All screen space is in x=[0..1], y=[0..1], with (0,0) at the top left
+ void AddScreenLine(const rmt::Vector &a, const rmt::Vector &b, tColour colour);
+ void AddScreenText(const char* szName, tColour colour);
+ void AddScreenText(const char* szName, const rmt::Vector &a, tColour colour);
+
+private:
+ struct Line
+ {
+ rmt::Vector _a;
+ rmt::Vector _b;
+ tColour _Colour;
+ };
+
+ struct ScreenText
+ {
+ enum { MaxTextSize = 256 };
+
+ tColour _Colour;
+ char _pText[MaxTextSize];
+ rmt::Vector _Pos;
+ };
+
+ struct VectorText
+ {
+ tTextString* _pText;
+ rmt::Vector _Pos;
+ };
+
+
+private:
+ char _szName[256];
+
+ tFont* _pDebugFont;
+
+ float _ScreenTextPos;
+
+ pddiShader* _pLineShader;
+
+ int _NumLines;
+ enum { MaxLines = 50 };
+ Line _Lines[MaxLines];
+
+ int _NumScreenLines;
+ enum { MaxScreenLines = 50 };
+ Line _ScreenLines[MaxScreenLines];
+
+ int _NumText;
+ enum { MaxText = 50 };
+ VectorText _pText[MaxText];
+
+ int _NumScreenText;
+ enum { MaxScreenText = 50 };
+ ScreenText _pScreenText[MaxScreenText];
+
+ bool _autoReset;
+
+//denied
+private:
+ Section();//don't use default constructor
+};
+
+
+#endif
+
+
+#endif
diff --git a/game/code/events/allevents.cpp b/game/code/events/allevents.cpp
new file mode 100644
index 0000000..ee7484e
--- /dev/null
+++ b/game/code/events/allevents.cpp
@@ -0,0 +1,2 @@
+#include <events/eventlistener.cpp>
+#include <events/eventmanager.cpp>
diff --git a/game/code/events/eventdata.h b/game/code/events/eventdata.h
new file mode 100644
index 0000000..775d7d7
--- /dev/null
+++ b/game/code/events/eventdata.h
@@ -0,0 +1,151 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: eventdata.h
+//
+// Description: Define any custom data structures that are passed
+// with event triggers here.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef EVENTDATA_H
+#define EVENTDATA_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/p3dtypes.hpp>
+#include <p3d/anim/pose.hpp>
+
+//========================================
+// Forward References
+//========================================
+class Character;
+class AnimCollisionEntityDSG;
+
+namespace ActionButton
+{
+ class AnimSwitch;
+};
+
+namespace sim
+{
+ class Collision;
+};
+
+//==============================================================================
+//
+// Synopsis: Associated data for EVENT_INTERIOR_LOADED event.
+//
+//==============================================================================
+struct InteriorLoadedEventData
+{
+ tName interiorName;
+ tName sectionName;
+ bool first;
+};
+
+//==============================================================================
+//
+// Synopsis: Associated data for EVENT_CONVERSATION_INIT event.
+//
+//==============================================================================
+struct DialogEventData
+{
+ DialogEventData() : char1( NULL ), char2( NULL ), charUID1( 0 ), charUID2( 0 ), dialogName( 0 ) {};
+
+ Character* char1;
+ Character* char2;
+ tUID charUID1;
+ tUID charUID2;
+ radKey32 dialogName;
+};
+
+//==============================================================================
+//
+// Synopsis: Associated data for EVENT_START_ANIMATION_SOUND event.
+//
+//==============================================================================
+struct AnimSoundData
+{
+ AnimSoundData( const char* sound, const char* posnSettings ) :
+ soundName( sound ), animJoint( NULL ), soundObject( NULL ), positionalSettingName( posnSettings ) {};
+
+ const char* soundName;
+ tPose::Joint* animJoint;
+ ActionButton::AnimSwitch* soundObject;
+ const char* positionalSettingName;
+
+ private:
+ AnimSoundData();
+};
+
+//==============================================================================
+//
+// Synopsis: Associated data for EVENT_START_ANIM_ENTITY_DSG_SOUND event.
+//
+//==============================================================================
+struct AnimSoundDSGData
+{
+ AnimSoundDSGData( const char* sound, AnimCollisionEntityDSG* object, tPose::Joint* joint, const char* settingName ) :
+ soundName( sound ), animJoint( joint ), soundObject( object ), positionalSettingName( settingName ) {};
+
+ const char* soundName;
+ tPose::Joint* animJoint;
+ AnimCollisionEntityDSG* soundObject;
+ const char* positionalSettingName;
+
+private:
+ AnimSoundDSGData();
+};
+
+//==============================================================================
+//
+// Synopsis: Associated data for EVENT_CHANGE_MUSIC_STATE event.
+//
+//==============================================================================
+struct MusicStateData
+{
+ MusicStateData() : stateKey( 0 ), stateEventKey( 0 ) {};
+ MusicStateData( radKey32 state, radKey32 event ) : stateKey( state ), stateEventKey( event ) {};
+
+ radKey32 stateKey;
+ radKey32 stateEventKey;
+};
+
+//==============================================================================
+//
+// Synopsis: Associated data for EVENT_CAMERA_SHAKE events.
+//
+//==============================================================================
+struct ShakeEventData
+{
+ ShakeEventData() : playerID( -1 ), force( 0.0f ), looping( false ) { direction.Set(0.0f, 0.0f, 0.0f ); };
+
+ int playerID;
+ rmt::Vector direction;
+ float force;
+ bool looping;
+};
+
+class Vehicle;
+struct CarOnCarCollisionEventData
+{
+ CarOnCarCollisionEventData() : vehicle( NULL ), force ( 0.0f ), collision( NULL ) {};
+
+ Vehicle* vehicle;
+ float force;
+ sim::Collision* collision;
+};
+
+struct RumbleCollision
+{
+ RumbleCollision() : vehicle( NULL ), normalizedForce( 0.0f ) { point.Set( 0.0f, 0.0f, 0.0f ); };
+ Vehicle* vehicle;
+ float normalizedForce;
+ rmt::Vector point;
+};
+
+#endif // EVENTDATA_H \ No newline at end of file
diff --git a/game/code/events/eventenum.h b/game/code/events/eventenum.h
new file mode 100644
index 0000000..b0e29d1
--- /dev/null
+++ b/game/code/events/eventenum.h
@@ -0,0 +1,395 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: eventenum.h
+//
+// Description: Enumeration of all the unique Event IDs
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef EVENTENUM_H
+#define EVENTENUM_H
+
+#ifdef WORLD_BUILDER
+#include "../meta/locatorevents.h"
+#else
+#include <meta/locatorevents.h>
+#endif
+
+enum EventEnum
+{
+ // The next two "events" must appear in consecutive order.
+ //
+ EVENT_LOCATOR,
+ EVENT_PLACEHOLDER = EVENT_LOCATOR + LocatorEvent::NUM_EVENTS,
+
+ // Now add the rest of the events.
+ //
+
+ //Chuck Tree of Woe Negative Feedback for negative feedback sound
+ EVENT_TREE_OF_WOE_NEGATIVE_FEEDBACK,
+
+ //Mission Vehicle Creatation and Release
+
+ EVENT_MISSION_VEHICLE_CREATED,
+ EVENT_MISSION_VEHICLE_RELEASED,
+
+ EVENT_KICK,
+ EVENT_STOMP,
+ EVENT_DOUBLEJUMP,
+
+
+ EVENT_BREAK_CAMERA,
+ EVENT_BONUS_MISSION_CHARACTER_APPROACHED,
+ EVENT_OBJECT_DESTROYED,
+ EVENT_VEHICLE_VEHICLE_COLLISION,
+ EVENT_VEHICLE_DESTROYED,
+ EVENT_VEHICLE_DESTROYED_BY_USER,
+ EVENT_VEHICLE_DESTROYED_SYNC_SOUND,
+ EVENT_VEHICLE_DAMAGED,
+ EVENT_VEHICLE_SUSPENSION_BOTTOMED_OUT,
+ EVENT_USER_VEHICLE_ADDED_TO_WORLD,
+ EVENT_USER_VEHICLE_REMOVED_FROM_WORLD,
+ EVENT_GETINTOVEHICLE_START,
+ EVENT_GETINTOVEHICLE_END,
+ EVENT_GETOUTOFVEHICLE_START,
+ EVENT_GETOUTOFVEHICLE_END,
+ //EVENT_GETINTOTRAFFIC_END,
+ //EVENT_GETOUTOFTRAFFIC_END,
+ EVENT_ENTERING_TRAFFIC_CAR,
+ EVENT_ENTERING_PLAYER_CAR,
+ EVENT_ENTER_INTERIOR_START,
+ EVENT_ENTER_INTERIOR_TRANSITION_START,
+ EVENT_ENTER_INTERIOR_TRANSITION_END,
+ EVENT_ENTER_INTERIOR_END,
+ EVENT_EXIT_INTERIOR_START,
+ EVENT_EXIT_INTERIOR_END,
+ EVENT_INTERIOR_SWITCH,
+ EVENT_COLLECTED_COINS,
+ EVENT_LOST_COINS,
+ EVENT_SPAWNED_COINS,
+ EVENT_BOSS_DESTROYED_PLAYER_CAR,
+ EVENT_BOSS_DAMAGED,
+ EVENT_COLAPROP_DESTROYED,
+ EVENT_VEHICLE_COLLECTED_PROP,
+ EVENT_ABDUCTED,
+ // RenderManager Events
+ //
+ EVENT_INTERIOR_LOADED,
+ EVENT_INTERIOR_DUMPED,
+ EVENT_INTERIOR_LOAD_START,
+ EVENT_FIRST_DYNAMIC_ZONE_START,
+ EVENT_FIRST_DYNAMIC_ZONE_END,
+ EVENT_ALL_DYNAMIC_ZONE_END,
+ EVENT_DYNAMIC_ZONE_LOAD_ENDED,
+ EVENT_NAMED_DYNAMIC_ZONE_LOAD_ENDED,
+ EVENT_ALL_DYNAMIC_ZONES_DUMPED,
+
+ // Mission events
+ //
+ EVENT_WAYAI_AT_DESTINATION,
+ EVENT_WAYAI_HIT_LAST_WAYPOINT,
+ EVENT_WAYAI_HIT_WAYPOINT,
+ EVENT_WAYAI_HIT_CHECKPOINT,
+ EVENT_MISSION_RESET,
+ EVENT_DESTINATION_REACHED,
+ EVENT_COLLECT_OBJECT,
+ EVENT_GAG_START,
+ EVENT_GAG_END,
+ EVENT_INTERACTIVE_GAG,
+ EVENT_GAG_FOUND,
+ EVENT_DUMP_STATUS,
+
+ EVENT_MISSION_INTRO,
+ EVENT_MISSION_START,
+ EVENT_MISSION_OBJECTIVE_NEW,
+ EVENT_SHOW_MISSION_OBJECTIVE, // pEventData = mission objective message ID
+
+ EVENT_MISSION_DRAMA,
+ EVENT_CHANGE_MUSIC,
+ EVENT_CHANGE_MUSIC_STATE,
+
+ EVENT_STAGE_COMPLETE,
+ EVENT_STAGE_TRANSITION_FAILED,
+ EVENT_MISSION_FAILURE,
+ EVENT_MISSION_SUCCESS,
+
+ EVENT_MISSION_CHARACTER_RESET,
+ EVENT_LEVEL_START,
+
+ //Chuck: Attempting to Enter Gamble Race, and other Gamble Race Events
+ EVENT_ATTEMPT_TO_ENTER_GAMBLERACE,
+ EVENT_ENTER_GAMBLERACE_SUCCESS,
+ EVENT_ENTER_GAMBLERACE_FAILURE,
+ EVENT_GAMBLERACE_SUCCESS,
+ EVENT_GAMBLERACE_FAILURE,
+
+ //Chuck: mission Cancel and abort
+
+ EVENT_USER_CANCEL_MISSION_BRIEFING,
+ EVENT_USER_CANCEL_PAUSE_MENU,
+
+ //Chuck: Get out of car condition
+
+ EVENT_OUTOFCAR_CONDITION_ACTIVE,
+ EVENT_OUTOFCAR_CONDITION_INACTIVE,
+
+
+
+
+ EVENT_KICK_NPC,
+
+ // Sound events
+ //
+ EVENT_FE_MENU_SELECT,
+ EVENT_FE_MENU_BACK,
+ EVENT_FE_MENU_UPORDOWN,
+ EVENT_FE_START_GAME_SELECTED,
+ EVENT_FE_CHEAT_SUCCESS,
+ EVENT_FE_CHEAT_FAILURE,
+ EVENT_FE_CONTINUE,
+ EVENT_FE_CANCEL,
+ EVENT_FE_PAUSE_MENU_START,
+ EVENT_FE_PAUSE_MENU_END,
+ EVENT_FE_LOCKED_OUT,
+
+ EVENT_FE_GAG_INIT, // FE gag animation about to start, prep sound streamer
+ EVENT_FE_GAG_START, // FE gag animation started
+ EVENT_FE_GAG_STOP, // FE gag animation stopped
+
+ EVENT_FE_CREDITS_NEW_LINE, // new line displayed on credits screen (event ID = line number)
+
+ EVENT_DIALOG_SHUTUP,
+
+ EVENT_HUD_LAP_UPDATED, // sound fx when lap couter is updated
+ EVENT_HUD_TIMER_BLINK, // sound fx for blinking timer
+
+ EVENT_PHONE_BOOTH_BUSY, // phone booth inaccessible
+
+ EVENT_COLLISION,
+ EVENT_CAMERA_CHANGE,
+
+ EVENT_MINOR_CRASH,
+ EVENT_MINOR_VEHICLE_CRASH,
+ EVENT_BIG_CRASH,
+ EVENT_BIG_VEHICLE_CRASH,
+
+ EVENT_BIG_BOOM_SOUND,
+ EVENT_BARREL_BLOWED_UP,
+
+ EVENT_BURNOUT,
+ EVENT_BURNOUT_END,
+ EVENT_HIT_BREAKABLE,
+ EVENT_HIT_MOVEABLE,
+ EVENT_BIG_AIR,
+
+ EVENT_FOOTSTEP,
+ EVENT_JUMP_TAKEOFF,
+ EVENT_JUMP_LANDING,
+ EVENT_PEDESTRIAN_SMACKDOWN,
+ EVENT_TURBO_START,
+ EVENT_CHARACTER_TIRED_NOW,
+
+ EVENT_WRONG_SIDE_DUMBASS,
+
+ EVENT_PEDESTRIAN_DODGE,
+
+ EVENT_CARD_COLLECTED,
+ EVENT_BIG_RED_SWITCH_PRESSED,
+ EVENT_BREAK_CAMERA_OR_BOX,
+
+ EVENT_TUTORIAL_DIALOG_PLAY,
+ EVENT_TUTORIAL_DIALOG_DONE,
+
+ EVENT_CHASE_VEHICLE_SPAWNED,
+ EVENT_CHASE_VEHICLE_DESTROYED,
+ EVENT_CHASE_VEHICLE_PROXIMITY,
+
+ EVENT_TIME_RUNNING_OUT,
+ EVENT_RACE_PASSED_AI,
+ EVENT_RACE_GOT_PASSED_BY_AI,
+
+ EVENT_POSITIONAL_SOUND_TRIGGER_HIT,
+
+ EVENT_PC_NPC_COLLISION,
+ EVENT_PLAYER_CAR_HIT_NPC,
+ EVENT_PLAYER_MAKES_LIGHT_OF_CAR_HITTING_NPC,
+ EVENT_KICK_NPC_SOUND,
+ EVENT_HIT_HEAD,
+ EVENT_DEATH_VOLUME_SOUND,
+
+ EVENT_TRAFFIC_SPAWN,
+ EVENT_TRAFFIC_REMOVE,
+
+ EVENT_TRAFFIC_GOT_HIT,
+ EVENT_TRAFFIC_IMPEDED, // Hmm.. could differentiate diff sounds for impeded by character or a vehicle...
+
+ EVENT_PLAYER_VEHICLE_HORN,
+ EVENT_TRAFFIC_HORN,
+
+ EVENT_DING_DONG,
+
+ EVENT_PHONE_BOOTH_RIDE_REQUEST,
+ EVENT_PHONE_BOOTH_NEW_VEHICLE_SELECTED,
+ EVENT_PHONE_BOOTH_OLD_VEHICLE_RESELECTED,
+ EVENT_PHONE_BOOTH_CANCEL_RIDEREPLY_LINE,
+
+ EVENT_TAIL_LOST_DIALOG,
+ EVENT_MISSION_SUCCESS_DIALOG,
+
+ EVENT_VILLAIN_TAIL_EVADE,
+ EVENT_VILLAIN_CAR_HIT_PLAYER,
+
+ EVENT_AMBIENT_GREETING,
+ EVENT_AMBIENT_RESPONSE,
+
+ EVENT_AMBIENT_ASKFOOD,
+ EVENT_AMBIENT_FOODREPLY,
+
+ EVENT_WASP_CHARGING,
+ EVENT_WASP_CHARGED,
+ EVENT_WASP_ATTACKING,
+ EVENT_WASP_BLOWED_UP,
+ EVENT_WASP_BULLET_FIRED,
+ EVENT_WASP_BULLET_MISSED,
+ EVENT_WASP_BULLET_HIT_CHARACTER_STYLIZED_VIOLENCE_FOLLOWS,
+ EVENT_WASP_APPROACHED,
+
+ EVENT_MISSION_COLLECTIBLE_PICKED_UP,
+
+ EVENT_HIT_AND_RUN_START,
+ EVENT_HIT_AND_RUN_CAUGHT,
+ EVENT_HIT_AND_RUN_EVADED,
+ EVENT_HIT_AND_RUN_METER_THROB,
+
+ EVENT_HAGGLING_WITH_GIL,
+
+ EVENT_START_ANIMATION_SOUND,
+ EVENT_STOP_ANIMATION_SOUND,
+
+ EVENT_START_ANIM_ENTITY_DSG_SOUND,
+ EVENT_STOP_ANIM_ENTITY_DSG_SOUND,
+
+ EVENT_PLAY_BIRD_SOUND,
+
+ EVENT_SUPERSPRINT_WIN,
+ EVENT_SUPERSPRINT_LOSE,
+
+ EVENT_PLAY_CREDITS,
+ EVENT_PLAY_FE_MUSIC,
+ EVENT_PLAY_MUZAK,
+ EVENT_PLAY_IDLE_MUSIC,
+ EVENT_STOP_THE_MUSIC,
+
+ EVENT_AVATAR_VEHICLE_TOGGLE,
+
+ EVENT_MISSION_BRIEFING_ACCEPTED,
+
+ // GUI in-game events
+ //
+ EVENT_GUI_MISSION_LOAD_COMPLETE,
+ EVENT_GUI_COUNTDOWN_START,
+ EVENT_GUI_MISSION_START,
+ EVENT_GUI_LEAVING_PAUSE_MENU,
+ EVENT_GUI_IRIS_WIPE_CLOSED,
+ EVENT_GUI_IRIS_WIPE_OPEN,
+ EVENT_GUI_FADE_OUT_DONE, // fade to black completed
+ EVENT_GUI_FADE_IN_DONE, // fade to game completed
+ EVENT_LETTERBOX_CLOSED, // the letter box has completely closed on the screen
+ EVENT_DEATH_VOLUME_SCREEN_BLANK,
+ EVENT_GUI_ENTERING_MISSION_SUCCESS_SCREEN,
+ EVENT_GUI_TRIGGER_PATTY_AND_SELMA_SCREEN,
+
+
+ //Conversation EVENTS
+
+ EVENT_CONVERSATION_INIT,
+ EVENT_CONVERSATION_INIT_DIALOG,
+ EVENT_CONVERSATION_START,
+ EVENT_CONVERSATION_SKIP,
+ EVENT_CONVERSATION_DONE,
+ EVENT_CONVERSATION_DONE_AND_FINISHED, // the letter box screen listens for EVENT_CONVERSATION_DONE,
+ // and will then trigger EVENT_CONVERSATION_DONE_AND_FINISHED
+ // at its discretion
+
+ EVENT_IN_GAMEPLAY_CONVERSATION,
+
+ //MouthFlapping EVENTS
+
+ EVENT_PC_TALK,
+ EVENT_PC_SHUTUP,
+ EVENT_NPC_TALK,
+ EVENT_NPC_SHUTUP,
+
+ EVENT_OBJECT_KICKED,
+
+ //NPC Events
+ EVENT_TALK_TO_NPC,
+
+ //Camera shake events
+ EVENT_CAMERA_SHAKE,
+ EVENT_RUMBLE_COLLISION,
+
+ EVENT_BONUS_MISSION_DIALOGUE,
+
+ //This is the dumping of Dynaload sections in worldrenderlayer.
+ EVENT_DUMP_DYNA_SECTION,
+
+ //Rewards and Unlockable events
+ EVENT_UNLOCKED_CAR,
+ EVENT_UNLOCKED_SKIN,
+ EVENT_COMPLETED_ALLSTREETRACES,
+ EVENT_COMPLETED_BONUSMISSIONS,
+ EVENT_COLLECTED_ALLCOINS,
+ EVENT_COLLECTED_ALLCARDS,
+ EVENT_DESTROYED_ALLCAMERAS,
+
+ EVENT_SWITCH_SKIN,
+
+ //Collecting Wrench To Repair car
+
+ EVENT_REPAIR_CAR,
+ EVENT_COLLECTED_WRENCH,
+
+ // Actor events
+ EVENT_ACTOR_CREATED,
+ EVENT_ACTOR_REMOVED,
+
+ // Sent when a stateprop is destroyed, param = stateprop pointer
+ // This corresponds to the artist-set callback flag "REMOVE_FROM_WORLD"
+ EVENT_STATEPROP_DESTROYED,
+ // Sent when a stateprop is moved from AI to sim control
+ EVENT_STATEPROP_ADDED_TO_SIM,
+
+ //Teleport pads
+ EVENT_ENTERED_TELEPORT_PAD,
+ EVENT_EXITED_TELEPORT_PAD,
+ EVENT_TAKING_TELEPORT,
+
+ EVENT_LOSE_COLLECTIBLE,
+
+ // Nitro
+ EVENT_COLLECTED_NITRO,
+ EVENT_USE_NITRO,
+
+ EVENT_CHARACTER_POS_RESET,
+ EVENT_TOGGLE_FIRSTPERSON,
+
+ EVENT_ANIMATED_CAM_SHUTDOWN,
+
+ // Indicates that an exploding car has finished playing its animation
+ EVENT_CAR_EXPLOSION_DONE,
+
+ EVENT_STATEPROP_COLLECTIBLE_DESTROYED,
+
+ EVENT_AVATAR_OFF_ROAD,
+
+ EVENT_AVATAR_ON_ROAD,
+
+ NUM_EVENTS
+};
+
+
+#endif // EVENTENUM_H
diff --git a/game/code/events/eventlistener.cpp b/game/code/events/eventlistener.cpp
new file mode 100644
index 0000000..6971620
--- /dev/null
+++ b/game/code/events/eventlistener.cpp
@@ -0,0 +1,77 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: eventlistener.cpp
+//
+// Description: Implementation for EventListener abstract base class.
+// All objects that listen for events must be derived from
+// this class.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <events/eventlistener.h>
+#include <events/eventmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// EventListener::EventListener
+//==============================================================================
+//
+// Description: Constructor
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+EventListener::EventListener()
+{
+}
+
+
+//==============================================================================
+// EventListener::~EventListener
+//==============================================================================
+//
+// Description: Destructor
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+EventListener::~EventListener()
+{
+ //
+ // Remove all references to this object from the EventManager cause
+ // it's about to go away.
+ //
+ GetEventManager()->RemoveAll( this );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
diff --git a/game/code/events/eventlistener.h b/game/code/events/eventlistener.h
new file mode 100644
index 0000000..89636ad
--- /dev/null
+++ b/game/code/events/eventlistener.h
@@ -0,0 +1,58 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: eventlistener.h
+//
+// Description: EventManager class declaration.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef EVENTLISTENER_H
+#define EVENTLISTENER_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#ifdef WORLD_BUILDER
+#include "eventenum.h"
+#else
+#include <events/eventenum.h>
+#endif
+
+//========================================
+// Forward References
+//========================================
+
+//==============================================================================
+//
+// Synopsis: Abstract base class for all classes that want to be notified
+// of events.
+//
+//==============================================================================
+class EventListener
+{
+ public:
+
+ EventListener();
+ virtual ~EventListener();
+
+ // Derived classes must implement this method to receive
+ // event notification.
+ virtual void HandleEvent( EventEnum id, void* pEventData ) = 0;
+
+ private:
+
+ // Declared but not defined to prevent copying and assignment.
+ EventListener( const EventListener& );
+ EventListener& operator=( const EventListener& );
+};
+
+
+#endif // EVENTLISTENER_H
+
diff --git a/game/code/events/eventmanager.cpp b/game/code/events/eventmanager.cpp
new file mode 100644
index 0000000..04d0547
--- /dev/null
+++ b/game/code/events/eventmanager.cpp
@@ -0,0 +1,365 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: eventmanager.cpp
+//
+// Description: Implementation for the EventManager class.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// STL
+#include <algorithm>
+// Ftech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <events/eventmanager.h>
+#include <events/eventlistener.h>
+#include <memory/srrmemory.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+EventManager* EventManager::spInstance = NULL;
+
+#ifdef DEBUGWATCH
+ unsigned int g_simulatedEventID = 0;
+ static void TriggerSimulatedEvent()
+ {
+ GetEventManager()->TriggerEvent( static_cast<EventEnum>( g_simulatedEventID ) );
+ }
+#endif // DEBUGWATCH
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// EventManager::CreateInstance
+//==============================================================================
+//
+// Description: Creates the EventManager.
+//
+// Parameters: None.
+//
+// Return: Pointer to the EventManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+EventManager* EventManager::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "EventManager" );
+ rAssert( spInstance == NULL );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+
+ spInstance = new EventManager;
+ rAssert( spInstance );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+
+MEMTRACK_POP_GROUP( "EventManager" );
+
+ return spInstance;
+}
+
+//==============================================================================
+// EventManager::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the EventManager singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the EventManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+EventManager* EventManager::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// EventManager::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the EventManager.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void EventManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete spInstance;
+ spInstance = NULL;
+}
+
+
+//==============================================================================
+// EventManager::AddListener
+//==============================================================================
+//
+// Description: Register a listener for event notification.
+//
+// Parameters: pListener - the object to receive event notification
+// id - the EventEnum that will trigger notification
+//
+// Return: None.
+//
+//==============================================================================
+void EventManager::AddListener( EventListener* pListener, EventEnum id )
+{
+ rAssert( NULL != pListener );
+
+MEMTRACK_PUSH_GROUP( "EventManager" );
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+
+
+ ListenerVector& listeners = mListenerMap[id];
+
+ // check for duplicates.
+ ListenerVector::iterator result = std::find( listeners.begin(),
+ listeners.end(),
+ pListener );
+ if( result == listeners.end() )
+ {
+ listeners.push_back( pListener );
+ }
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+
+#ifdef TRACK_LISTENERS
+ mStats[id].numListening += 1;
+ if ( mStats[id].numListening > mStats[id].highWater )
+ {
+ mStats[id].highWater = mStats[id].numListening;
+ }
+#endif
+MEMTRACK_POP_GROUP( "EventManager" );
+}
+
+
+//==============================================================================
+// EventManager::RemoveListener
+//==============================================================================
+//
+// Description: Remove a listener from event notification.
+//
+// Parameters: pListener - the listener to be removed
+// id - the EventEnum it was listening for
+//
+// Return: None.
+//
+//==============================================================================
+void EventManager::RemoveListener( EventListener* pListener, EventEnum id )
+{
+ rAssert( NULL != pListener );
+
+ ListenerVector& listeners = mListenerMap[id];
+
+ ListenerVector::iterator result = std::find( listeners.begin(),
+ listeners.end(),
+ pListener );
+ // Did we find the listener?
+ if( result != listeners.end() )
+ {
+ listeners.erase( result );
+
+#ifdef TRACK_LISTENERS
+ mStats[id].numListening--;
+#endif
+ }
+}
+
+
+//==============================================================================
+// EventManager::RemoveAll
+//==============================================================================
+//
+// Description: Remove a listener from *ALL* event notifications.
+//
+// Parameters: pListener - the listener to be removed.
+//
+// Return: None.
+//
+//==============================================================================
+void EventManager::RemoveAll( EventListener* pListener )
+{
+ // Iterate through all the listener arrays.
+ int i = 0;
+ for( ; i < NUM_EVENTS; ++i )
+ {
+ this->RemoveListener( pListener, static_cast<EventEnum>(i) );
+ }
+}
+
+
+//==============================================================================
+// EventManager::TriggerEvent
+//==============================================================================
+//
+// Description: Trigger an event. All registered listeners for that event
+// are notified.
+//
+// Parameters: id - the event to trigger
+// pEventData - any custom data associated with the event
+//
+// Return: Number of listeners notified.
+//
+//==============================================================================
+int EventManager::TriggerEvent( EventEnum id, void* pEventData ) const
+{
+ const ListenerVector& listeners = mListenerMap[id];
+
+ ListenerVector::const_iterator iter = listeners.begin();
+
+ // Notify all listeners of the event.
+ size_t size = listeners.size();
+ size_t i;
+ for( i = 0; i < size; ++i )
+ {
+ EventListener* el = listeners[ i ];
+ rAssert( el != NULL );
+ el->HandleEvent( id, pEventData );
+ size = listeners.size(); //the event may have added or removed listeners
+
+ // if the current event was removed we need to detect that
+ EventListener* newel = listeners[ i ];
+
+ // Don't want to do anything if we are now past the end of the list,
+ // or if the current event hasn't changed
+ if((i < size) && (newel != el))
+ {
+ // we have removed the current event
+ // back the array index off one so that we will
+ // trigger the same event index again
+ i--;
+ }
+ }
+
+ return listeners.size();
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// EventManager::EventManager
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+EventManager::EventManager()
+{
+ //TODO: When we're leak free, make this smaller and see where we get.
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+ unsigned int i;
+ for ( i = 0; i < NUM_EVENTS; ++i )
+ {
+ mListenerMap[i].reserve( 32 );
+ }
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+#ifdef DEBUGWATCH
+ radDbgWatchAddUnsignedInt( &g_simulatedEventID,
+ "Simulated Event ID",
+ "Event Manager",
+ (RADDEBUGWATCH_CALLBACK)TriggerSimulatedEvent,
+ NULL,
+ 0,
+ NUM_EVENTS - 1 );
+#endif // DEBUGWATCH
+}
+
+
+
+//==============================================================================
+// EventManager::~EventManager
+//==============================================================================
+//
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+EventManager::~EventManager()
+{
+#ifdef RAD_DEBUG
+ // Iterate through all the listener arrays and check that all listeners
+ // have been removed.
+ int i = 0;
+ for( ; i < NUM_EVENTS; ++i )
+ {
+ const ListenerVector& listeners = mListenerMap[i];
+ rAssertMsg( 0 == listeners.size(), "EventListener has not been removed!\n" );
+ }
+#endif // RAD_DEBUG
+
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &g_simulatedEventID );
+#endif // DEBUGWATCH
+
+#ifdef TRACK_LISTENERS
+ for ( int j = 0; j < NUM_EVENTS; ++j )
+ {
+ rReleasePrintf( "EventID: %d HighWater: %d\n", j, mStats[j].highWater );
+ }
+#endif
+}
diff --git a/game/code/events/eventmanager.h b/game/code/events/eventmanager.h
new file mode 100644
index 0000000..ed4b9e3
--- /dev/null
+++ b/game/code/events/eventmanager.h
@@ -0,0 +1,90 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: eventmanager.h
+//
+// Description: EventManager class declaration.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef EVENTMANAGER_H
+#define EVENTMANAGER_H
+
+//========================================
+// System Includes
+//========================================
+#include <vector>
+
+//========================================
+// Project Includes
+//========================================
+#include <events/eventenum.h>
+#include <memory/stlallocators.h>
+
+//Uncomment this if you wanna see how many listeners there are to a given event.
+#define TRACK_LISTENERS
+
+//========================================
+// Forward References
+//========================================
+class EventListener;
+
+//==============================================================================
+//
+// Synopsis: Used to trigger events and route them to the listeners.
+//
+//==============================================================================
+class EventManager
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static EventManager* CreateInstance();
+ static EventManager* GetInstance();
+ static void DestroyInstance();
+
+ // Methods to add/remove listeners.
+ void AddListener( EventListener* pListener, EventEnum id );
+ void RemoveListener( EventListener* pListener, EventEnum id );
+ void RemoveAll( EventListener* pListener );
+
+ // Trigger an event.
+ int TriggerEvent( EventEnum id, void* pEventData = NULL ) const;
+
+ private:
+
+ // No public access to these, use singleton interface.
+ EventManager();
+ ~EventManager();
+
+ // Declared but not defined to prevent copying and assignment.
+ EventManager( const EventManager& );
+ EventManager& operator=( const EventManager& );
+
+ // Pointer to the one and only instance of this singleton.
+ static EventManager* spInstance;
+
+ // Each unique event has a container of attached listeners.
+ typedef std::vector< EventListener*, s2alloc<EventListener*> > ListenerVector;
+ ListenerVector mListenerMap[NUM_EVENTS];
+
+#ifdef TRACK_LISTENERS
+ struct EventStats {
+ EventStats() : numListening( 0 ), highWater( 0 ) {};
+ unsigned int numListening;
+ unsigned int highWater;
+ };
+
+ EventStats mStats[NUM_EVENTS];
+#endif
+};
+
+
+// A little syntactic sugar for getting at this singleton.
+inline EventManager* GetEventManager() { return( EventManager::GetInstance() ); }
+
+
+#endif // EVENTMANAGER_H
+
diff --git a/game/code/font/ADLIBN.TTF b/game/code/font/ADLIBN.TTF
new file mode 100644
index 0000000..04cf115
--- /dev/null
+++ b/game/code/font/ADLIBN.TTF
Binary files differ
diff --git a/game/code/font/buildfont.bat b/game/code/font/buildfont.bat
new file mode 100644
index 0000000..4b48138
--- /dev/null
+++ b/game/code/font/buildfont.bat
@@ -0,0 +1,9 @@
+echo ========================
+echo Building baked in font
+echo ========================
+@..\..\libs\pure3d\tools\commandline\bin\p3dmakefont -o adlibn.p3d -f -n -u ..\..\exportart\frontend\scrooby\resource\txtbible\srr2.X -t 0 -s 20 adlibn.ttf
+@attrib -r defaultfont.h
+@..\..\build\tools\bin2h adlibn.p3d > defaultfont.h
+@..\..\libs\pure3d\lib\perl\bin\perl ..\..\build\tools\sizeprint.pl defaultfont.h adlibn.p3d >> defaultfont.h
+@attrib +r defaultfont.h
+@del adlibn.p3d
diff --git a/game/code/font/defaultfont.h b/game/code/font/defaultfont.h
new file mode 100644
index 0000000..4381971
--- /dev/null
+++ b/game/code/font/defaultfont.h
@@ -0,0 +1,690 @@
+"\x50\x33\x44\xff\x0c\x00\x00\x00\x27\x30\x00\x00\x00\x20\x02\x00\x32\x00"
+"\x00\x00\x1b\x30\x00\x00\x00\x00\x00\x00\x0c\x61\x64\x6c\x69\x62\x6e\x5f"
+"\x32\x30\x00\x00\x00\x00\x00\x00\xa0\x41\x00\x00\xb8\x41\x00\x00\xc8\x41"
+"\x00\x00\xa0\x41\x01\x00\x00\x00\x00\x90\x01\x00\x45\x00\x00\x00\x31\x19"
+"\x00\x00\x14\x61\x64\x6c\x69\x62\x6e\x5f\x32\x30\x5f\x74\x65\x78\x74\x75"
+"\x72\x65\x5f\x30\x00\xb0\x36\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x04"
+"\x00\x00\x00\x08\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\x01\x90\x01\x00\x29\x00\x00\x00\xec\x18\x00\x00\x00"
+"\xb0\x36\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x04\x00\x00\x00\x01\x00"
+"\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x02\x90\x01\x00\xc3\x18\x00\x00"
+"\xc3\x18\x00\x00\xb3\x18\x00\x00\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00"
+"\x00\x0d\x49\x48\x44\x52\x00\x00\x01\x00\x00\x00\x01\x00\x04\x03\x00\x00"
+"\x00\xae\x5c\xb5\x55\x00\x00\x00\x30\x50\x4c\x54\x45\xff\xff\xff\xff\xff"
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+"\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"
+"\xff\xff\xff\xff\xff\xff\xff\x4e\xaf\xb8\x40\x00\x00\x00\x10\x74\x52\x4e"
+"\x53\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff\x76"
+"\x95\x01\x15\x00\x00\x18\x22\x49\x44\x41\x54\x78\x9c\xed\x5c\x7d\x8c\x63"
+"\xd7\x55\xbf\xfe\x1a\x7b\x3c\x33\x7e\x2e\x95\xb2\x45\x4a\x33\x06\x29\x89"
+"\x52\xc1\x8e\x8b\x50\x10\x28\xd9\xb1\x04\x34\x90\x42\xc6\x15\x48\x2b\xa5"
+"\x69\xc6\x94\x8f\xad\x50\xc0\x93\xc2\x26\x91\x92\x74\x4c\x10\x62\x42\x49"
+"\x6c\xd2\x56\xf0\x47\x93\x71\xa1\x20\xfe\x68\xd7\xb3\x82\x82\x22\xa1\x9d"
+"\x51\x41\x09\x28\x6a\x3c\x4d\xa9\x94\x45\x74\x9f\x77\xb7\xa4\x9b\x4d\xb4"
+"\xcf\xf3\xb9\x9e\xb1\xfd\x0e\xe7\xe3\xbe\xe7\xf7\x6c\x4f\x66\x76\xb3\x1f"
+"\x14\xbd\x93\xcd\x7b\xef\x3e\xdf\x77\xef\xef\x9d\x7b\xee\xb9\xe7\xdc\x73"
+"\xde\x28\x15\x50\x40\xff\x8f\x28\x74\x7b\xa9\xef\x4e\xf4\xa7\xae\xac\x85"
+"\xe5\xa5\xd4\x65\xe7\xfa\xe3\x36\xb6\x76\xf8\x9b\x2a\x7c\xc2\xfe\x6b\xbe"
+"\xf1\xc8\xe8\x06\x1e\x2f\x66\x06\x9e\x9a\x5c\x92\xf3\x38\x40\x53\xdd\x6f"
+"\x75\x0a\xd8\xf1\x09\x78\x85\x6e\xcd\x00\x28\x05\x48\xeb\x4a\x3d\x0d\xa7"
+"\xd3\x06\x5d\x37\x55\xfc\x75\x78\x55\xa9\xc8\x09\x78\x33\xcd\x4f\x46\x5e"
+"\x2e\x81\xd1\xc4\x33\xe4\x8a\x6b\x58\xc6\x87\x54\x0c\xa0\x9d\x39\x04\x2b"
+"\x6a\x1a\xc0\xce\x28\xfc\xf9\x62\x62\x57\xc5\xde\x80\xf4\x00\x80\xd9\x8a"
+"\xba\xcb\xec\xbe\xa0\x52\xd8\xf6\x28\x76\xd0\x56\xaa\x8c\x27\x84\x15\xb2"
+"\x1c\x00\x4d\xf5\x61\x3c\x9e\x13\x00\x21\x13\x8f\xdf\x56\x45\x3c\x6e\x72"
+"\x0b\x61\x68\x32\x80\x64\x47\x59\x15\x35\x72\x0a\x2e\xe6\xf0\x75\xf0\xc1"
+"\xb7\x4f\xa4\xd5\x32\x56\xab\x22\x80\xd8\x76\x14\xd4\xb8\xdd\xf9\xfc\x00"
+"\x80\xc5\x5c\x8c\xda\xcd\x63\xeb\xcd\x79\xba\x9a\x8b\xd2\x11\x79\x39\x06"
+"\x3d\x00\xd4\x4e\x5b\x00\x24\xe9\xb8\x1b\xa1\x23\x30\x43\x43\x02\xa0\xce"
+"\x77\x56\x6b\xfc\xb0\x81\x00\x9a\x31\x64\xba\x85\x17\x0d\x04\x30\xbe\xae"
+"\x20\x4f\xa8\x35\x8b\x7a\x74\x46\x4d\xd1\x83\x6b\xf8\x4c\x13\x6b\x1b\xb0"
+"\x92\x84\xed\x27\xa1\xab\x54\xb1\xcd\x00\x9a\xc4\x65\x3c\x02\x84\x91\x5f"
+"\xb0\x64\xd0\xb5\x9d\xc4\xa3\x01\x15\x6e\x82\x01\x84\xb8\x7f\xa8\x60\x21"
+"\x61\x33\x80\xc6\x4c\x81\xf0\x53\x0b\x46\x73\xb2\xa1\xea\x95\x51\xbb\x7b"
+"\x5f\x3f\x80\xf0\x8e\x5a\xa4\x6e\x5a\x04\x00\x08\x40\x33\x05\xcd\x11\xbc"
+"\x0a\xc3\xba\x0b\x20\xc1\x47\x24\x13\x72\x0c\x00\x52\x0c\x60\xb5\x07\x20"
+"\x26\x00\x72\x58\x10\x89\x82\x17\x76\xf9\x01\xe4\x0a\x02\x28\x56\x55\x79"
+"\x35\x7a\xae\x5e\xe9\x07\x10\xdf\x44\xde\xe1\xbd\x4e\x0f\xc0\xd8\x99\x66"
+"\x02\x6c\x6c\xa3\x84\xe5\x11\xd8\x36\xdf\xc6\x6b\x01\x10\xc3\x7a\x74\x4d"
+"\xc3\x41\x00\x9a\x1a\x00\x9f\xac\xd2\xe4\x16\xf7\xf8\xee\x17\x15\x0e\x63"
+"\xf7\x08\xc1\xa3\x51\xc5\xf9\xa0\x6a\x05\x35\xdb\x54\xe9\xdf\xc9\xf4\x03"
+"\x18\x6f\xd0\x30\x21\x4f\xa5\xb9\xd0\x2c\x4a\xae\x52\xa3\xc8\x91\xa2\xad"
+"\x18\x00\xd2\x4a\x0a\xb6\xea\x34\x83\xc6\x61\x4b\xf1\xe8\x9f\x4b\x0d\x00"
+"\x88\x42\x7a\xbe\x41\x83\x84\x74\x52\xfd\x8a\xfd\xc2\x19\xea\x0c\x25\xa6"
+"\xc3\xac\xcb\xaa\x29\x9a\x22\x8a\x05\xc9\x61\x28\xd2\x54\x45\x89\xa8\x71"
+"\x73\xb7\xa0\x24\xe5\xa9\x92\x3d\x17\x42\x51\x72\x00\x5c\xe6\xa7\xce\xd1"
+"\xc4\x6c\xf0\x4b\x75\xd2\xa3\x9e\x76\xf8\x34\xc3\x37\xb6\x54\xf8\xcb\x78"
+"\xda\xc1\x1b\x63\x9b\xf7\xa0\x2e\xf8\x94\x30\x80\xde\xda\x58\xf7\x01\x30"
+"\x58\x20\xd5\x7c\xce\x03\x80\x7a\xdb\xe6\x4a\xdd\xfc\x18\xf6\xc5\x00\x90"
+"\xe1\x1d\x7e\x0a\x87\xb4\x06\x73\x8a\x67\xc1\x49\x9e\x8c\x5e\x00\x8b\x2e"
+"\x22\x20\x7e\x62\xdd\x3f\x40\xe4\x31\x96\x4b\x01\x90\x5a\x1b\x06\xe0\x75"
+"\x25\x00\x6c\x9c\x0c\x4d\x7c\x8b\x0b\xa4\x29\x92\xdf\x82\x6d\xa9\xc8\x75"
+"\x00\x44\xf2\x71\xc2\xa1\x22\x29\x53\xf5\x1d\x52\x47\xcb\xc4\x0f\x07\x00"
+"\x03\xc2\xf1\x83\x0d\xe0\x16\x13\x6d\x9c\xc6\xdd\x09\xd8\x2c\xef\x12\x1c"
+"\xc8\x92\x36\x12\x12\x19\x10\x00\x38\x09\xf0\x51\xbc\xd7\x45\x9d\xd5\xac"
+"\xc1\x17\x9d\x3a\x5d\x1f\x80\x14\x4b\x3e\xb2\xa3\xad\xb4\xc8\x8c\xfc\x3c"
+"\xea\x37\x10\x2d\x6a\xf3\xa9\xdc\x18\xc7\x5f\x59\x63\x50\xb5\xe2\xd9\x14"
+"\x22\x41\xe4\xe3\x2c\x04\x66\x41\x4d\x36\x3c\x00\x66\xbf\x49\x62\x73\x29"
+"\x8b\x93\x00\xf9\xca\xaf\x34\x0b\x4d\x82\x12\x52\x32\xcf\x34\x00\x2e\x74"
+"\xc7\x85\x03\xe3\xa4\x9f\x64\xc4\x64\x16\xf0\xf0\x2a\x51\xaf\x30\x37\x4d"
+"\xda\x7e\x5a\xc4\x25\x06\x59\x03\x42\x36\x22\x0f\x59\x24\x11\xb5\x8a\x9a"
+"\x5d\xf1\x00\xb0\x08\x55\x0a\x1a\xe3\x4d\x92\x5b\xbc\xb7\x89\x5a\x0a\x15"
+"\x11\x52\x3a\x25\xbd\x09\x93\xb8\xeb\x1d\xd2\x03\x28\x03\x53\xb0\xa1\x19"
+"\x66\x4f\x30\x80\x2c\xb5\x97\xb0\x08\x47\x1c\x54\x8d\x7a\x08\xbf\x08\xf6"
+"\xab\x69\x35\x7d\x59\x4d\xc0\xc2\xae\xc8\x2a\xca\x75\x71\x45\x95\x2b\x1e"
+"\x00\xcc\x5b\x04\x30\x89\xcf\x4c\xc8\xe0\x95\x49\x0f\x10\xd3\x49\x05\xe3"
+"\x34\x14\x00\x3c\x0b\xce\x86\x81\x35\xff\x22\x8d\x36\xad\x14\x04\x49\x38"
+"\xcd\xd5\x7a\x93\xca\x69\x3f\x6c\x2d\x21\x24\xd8\x14\x59\x45\xf5\x30\x85"
+"\xba\xba\xe0\xa9\x50\xe4\x25\xeb\x52\x7e\xbe\x44\x95\x79\xc5\x5a\x74\x00"
+"\xf0\x44\x5f\x72\xc4\x84\xa4\x3b\xc7\xb3\xbb\x84\xc3\xbf\x24\x4b\x04\x2c"
+"\xe9\x25\x89\x28\x6e\x95\x06\x00\x44\xcf\xe0\xb8\x3c\x6d\xcf\xa9\x84\xd9"
+"\xf9\xa5\x97\x7f\x0b\x35\xc8\xa6\x92\x95\xc3\x21\x5b\x4e\x75\xe4\x8e\xfa"
+"\x90\xd9\xfd\x23\xef\x6f\x77\x99\xf6\x37\x5c\x39\x8d\x9d\xa2\x05\x1d\x97"
+"\xf5\x7f\xa4\x35\x96\xba\x7a\x90\x7f\x8e\xd7\x69\x51\xde\x93\x42\x7d\xe5"
+"\x91\xcb\x51\xdb\x87\x72\x47\xce\x9f\x1c\x5c\x9f\xaf\x13\xbd\x33\xba\xe3"
+"\x2d\xda\x95\xbd\x2a\x5e\x2f\x7a\x24\xf1\x9f\x37\xba\xcb\x80\x02\x0a\xe8"
+"\xa6\xd3\x53\xf0\x0e\xe9\xd9\xd0\x93\x56\xf7\xab\x54\x8e\x91\x79\x4a\x66"
+"\xbe\xfd\xef\xa4\xfe\xee\x22\xfd\x4b\x6b\xee\xe9\x9c\xf3\x84\xa1\x2d\x80"
+"\xbb\x4c\x38\x9d\x75\xd6\x98\xfe\xa5\xa6\xbf\xbc\x27\xd1\x1a\x47\x6b\x2e"
+"\x2f\x5e\xb4\xfa\x27\xc9\xf8\x67\x3f\x83\xac\x3b\x5e\xe5\xf2\xda\xd4\xd2"
+"\x66\x92\x06\xc0\x0b\xdd\x8e\x1f\x00\xb8\x96\xec\x81\x01\x70\xc7\x55\x75"
+"\x2b\x7c\x35\x1d\xfe\x1c\x2d\xdc\x29\x5e\x16\xb9\x8f\x8e\x36\x5e\xd7\xa5"
+"\xcb\x25\x3f\x80\x79\x3e\x15\xa8\xa7\xb8\x58\x1e\x8e\x05\x82\xb6\xcb\x96"
+"\xa1\xed\xb9\x1d\xf1\x26\x3d\x14\xd1\xea\x3d\x99\xe7\x93\x29\x76\x6e\xfd"
+"\x0d\x2a\x2c\x37\xd0\x54\xb1\xc9\xac\x24\xab\x8e\x5e\x87\x0d\xab\x5d\x6e"
+"\xcd\xb1\xff\x9a\xd2\x57\x58\x4c\x9e\xd5\x21\x00\xc6\x34\x2e\x02\x40\x8e"
+"\x04\x5d\x1e\xcb\xab\xa3\x3c\x88\xb7\x98\x1a\x8e\xd1\x7d\x46\x29\xc7\xce"
+"\x4d\x74\x79\xb9\x9b\x40\x53\xaa\xdc\x22\x33\x47\x03\x18\x11\x4b\x54\x00"
+"\x74\xb4\x81\x6a\x68\x2f\x88\x01\x34\x87\x00\x58\x16\x07\x4a\xa4\x85\x8c"
+"\xaf\x26\xb2\x61\x37\xc9\x4b\xeb\xfd\x68\xd4\x88\x15\x87\x9c\xf9\x86\x33"
+"\x5c\xab\x93\xeb\xea\xa1\xee\xcc\x52\x0c\x99\x6e\x6e\x58\x68\x12\x30\x80"
+"\x2e\xbd\x8b\xcb\x57\xb6\xff\x7a\x00\xc6\xc1\xb1\x5a\xfb\x01\xe0\x43\x95"
+"\x1e\x80\x6c\x11\xd8\x2c\xe3\xa7\x43\xbf\x07\xf0\x05\xa5\xed\xd8\x07\x01"
+"\xde\xcc\x48\xa5\xea\x7c\x25\x4e\xe3\x19\x02\xf2\x61\x97\xd1\xaa\x67\x00"
+"\xdb\x24\x10\x6e\x97\x7d\x00\x52\x7b\x02\x58\xa6\x31\xd4\x00\x6c\x28\x2c"
+"\xd6\xf1\x32\x09\xed\x24\xec\x46\x17\x61\x37\xaf\x1c\x00\x6a\xa4\x0e\xed"
+"\xbc\xa0\xac\xe7\xa6\x5b\xe9\x32\xbe\x38\x32\x7d\x65\x1e\x2d\x2a\xae\x52"
+"\x51\xde\x2e\x69\x24\xf8\x41\x43\xdb\xc1\x7c\x65\x7b\xcc\x62\x01\xd0\xe1"
+"\x79\xac\x01\xec\x42\xa5\x5e\xa6\xcb\xa3\xbf\x81\x32\x60\xc0\x0f\x32\x3e"
+"\x79\x7c\x49\x98\xf3\x6f\xca\x4a\xa3\x3d\xfc\x61\x15\xb6\x91\xe9\x95\x69"
+"\xb6\xb7\xd1\x5d\x28\xf8\x01\x74\x86\x01\xe8\x0c\x00\x60\xbb\xd4\x01\xb0"
+"\x8d\x23\x3e\xeb\xce\x02\x83\x79\xee\x72\x80\x46\x44\xbb\x8e\xa0\xac\x8c"
+"\xfa\x0c\xba\x29\x58\xc4\x59\xbf\x29\x75\xba\x69\xdf\x10\xb4\x7c\x00\x52"
+"\x72\xb5\x3b\x0c\x40\xd7\x05\xb0\x81\xff\x4d\x39\xb3\x20\x23\x3c\x77\x01"
+"\x44\x6b\xd0\x91\xb9\xa8\x20\x8d\xe5\x7f\x55\x33\xdb\x32\xf1\x5b\x4a\xb6"
+"\x22\x96\xc6\xbc\x00\x50\x1d\x6c\xa4\x95\xe3\x2c\xb1\x10\x1a\xe4\x90\x6a"
+"\xf9\x20\xaf\x9d\x01\x9c\x20\xa3\x54\x03\x68\xc2\x8e\x2d\x33\x73\x67\x14"
+"\x65\x0a\x79\x6e\xbb\x42\x78\xa7\xd9\x1b\x11\x33\x67\x15\x3e\xb2\x76\x08"
+"\x07\x6f\x51\x34\x90\xde\x0b\x89\x53\x2f\xb3\xda\xfd\x29\xb9\x4a\x0f\x9c"
+"\x69\x68\x68\x8d\xe3\x05\xd0\x8d\xd1\x76\x95\x03\xa0\xee\xec\x11\xd0\x2f"
+"\xf8\xd8\x23\xc0\x8e\xbd\xbc\xc1\xdf\xd2\xf9\x57\x3b\xa8\xfe\xca\x55\xd4"
+"\x88\x75\x32\xfb\xd9\xeb\x41\x86\x08\x80\x10\xf5\x32\x2f\xe3\xdc\x52\x3e"
+"\x00\xfc\xd3\x24\x6c\xf4\x00\xd0\x3e\x0f\xfa\x8a\xeb\xa8\x07\x3a\x32\x4e"
+"\x78\xb3\x2c\x3c\xc2\x29\xa0\xb9\xf9\xb3\x96\xab\x88\x3e\xcb\x67\x8b\x7c"
+"\xd7\xa9\x8d\x84\xf9\x76\xdc\xfe\x0a\x7a\x1b\x2d\x82\x96\x27\x00\x93\x88"
+"\x95\x36\xb9\x60\x0b\xc7\xdb\xfe\xe7\x8c\x72\x87\x1b\x1c\x25\x6e\xc3\x5a"
+"\x0f\xc0\x32\x80\xf4\x36\xc5\x9a\x50\x00\xcc\x6a\x88\xf7\xe5\xd5\x7d\x32"
+"\x6f\x63\x7a\xc3\x6f\x2c\xa7\xb9\x4f\x00\x12\x36\x95\x42\x79\x64\xec\x26"
+"\x31\xb7\x22\x5d\x65\x69\x86\x03\xaa\x05\x47\x88\xbd\x00\xc8\x9f\xdb\x10"
+"\xf9\x60\x6e\xb0\xbf\xb9\x22\xaf\x4b\xb8\x04\xc0\x24\x34\x9c\x75\x69\xa1"
+"\xa3\x86\xd1\x3d\xdc\x77\xad\xfd\x09\x75\xfb\xe2\xa6\x22\x67\x12\x7d\xae"
+"\x55\x96\xe5\x17\x14\x2f\xc7\x5f\x70\xaa\xfa\x01\xa8\xfb\xad\x76\xd6\xbb"
+"\xec\x45\x4e\xc1\x2b\x5c\x8c\xf0\x2e\x85\x08\x6f\x0a\xaa\xee\x16\xcd\xe6"
+"\x50\x00\x42\xfc\xa6\x76\xfe\x3d\x6a\x78\xe9\x47\x0e\x58\xcf\x4f\x8f\x65"
+"\xde\xeb\xd7\x43\xa7\xe0\x9d\xc2\x55\xb5\x1b\x50\x40\x01\x05\x74\x20\x32"
+"\x38\x20\x82\x2a\x31\xfa\x75\x8a\x94\x90\x4e\xc5\x93\x8a\x9d\x20\xdf\x66"
+"\x8c\xb6\xbf\xfe\x89\x9c\x23\x13\xde\x7d\x54\x79\x0c\x0c\x80\x09\x52\xaa"
+"\x0f\xbd\xec\x69\xaa\xc6\x5b\x73\x4a\xd5\xd1\x62\x2b\xda\x55\xf7\xfe\xd8"
+"\xda\x7b\x79\x23\x06\xad\x1b\xf4\x33\x45\x4a\xb4\x8b\xb3\xab\x42\x75\x3e"
+"\xa5\xb8\xc7\x73\xea\x30\xab\xe3\xac\x0f\xc0\x28\x2d\xe2\x8b\x6d\xda\x94"
+"\x9c\xc4\xcb\x51\x68\x4c\x81\x6c\x63\xcd\xe2\x22\x56\xa7\x65\x93\xa8\x09"
+"\xf6\x28\x6f\x5f\xe3\x43\x66\xd7\xe9\xb5\xb8\xe5\x05\xc0\xa1\x86\x31\x31"
+"\x36\x65\x55\x99\x93\x52\x55\x00\x74\x64\xfb\xd6\xb1\xda\x35\x80\x18\x99"
+"\xc7\xe6\xe5\x24\xae\xf2\x45\xde\x10\xad\xe2\x22\x58\xc0\xaa\xf9\x14\x6c"
+"\x86\x11\x92\x03\x40\x45\x77\x05\xc0\xb8\x6b\x71\xa8\xa8\x9d\xf6\x00\x20"
+"\x5b\xaf\x39\xcb\xdb\x9f\xeb\x06\xbf\xd0\x2a\x5a\x9f\xe1\x8f\xc1\x66\x8a"
+"\xa2\x4a\x96\x6c\x5e\xeb\x7e\xdd\x0b\x5c\x68\x73\x21\xd8\x88\xe3\x4a\x86"
+"\xcc\xcb\x18\x30\x87\x77\x56\xe3\xe8\x63\x24\x60\x77\xd4\x31\x1e\x39\xfe"
+"\x62\x09\x80\xe5\xed\x62\xdb\xe9\x76\xba\xda\x03\xd0\xe5\x75\xb9\xc6\x00"
+"\x76\x74\x20\x85\xed\x65\xd8\xd5\x00\xd2\xae\x67\xe9\x45\x62\xc2\x5c\x0c"
+"\x9a\x11\x0e\x4b\x40\x7e\x0a\x72\x68\x88\x6c\x4e\xa1\xed\x16\x22\xbb\x94"
+"\xdf\x35\x64\x32\x80\xc5\x1c\x01\x18\x85\x7c\x14\xbc\x9b\x7b\x77\x3f\xfe"
+"\x09\x06\x70\x99\x8d\x15\x93\x01\x74\x04\x40\x63\x99\x3b\xe9\x3a\x1c\xc0"
+"\x21\x78\xf7\x4b\xfd\x00\xca\xb0\x94\x20\x3f\x61\x37\x02\x16\x94\x88\x83"
+"\x29\x68\xd7\xc9\x8b\xad\xc1\x22\xc2\x41\x3a\x4c\x31\x28\xa5\x66\x4a\x04"
+"\xa0\x7c\x5e\xa9\x23\xad\x5e\xff\x0f\x03\xef\x9b\x1a\xb0\x06\x30\xc9\x76"
+"\xa3\xe3\xeb\x19\xb0\x62\x49\x27\x02\xa0\x25\x42\x78\xbe\x0f\xc0\x0c\xed"
+"\xe4\x17\x54\xbd\x1b\x47\x6b\x77\xa9\xdc\x15\xd3\x81\x42\xaf\xd3\x7a\x17"
+"\x3a\x62\xa1\x3b\x4d\xc0\x56\x55\x2f\xd8\xe0\x10\xbb\xf7\xdb\xdc\xdc\x22"
+"\xcc\x3b\x00\xb4\x95\x5b\x00\x17\x00\x49\xa6\x0a\xfd\x39\x59\xa4\x19\x3f"
+"\x00\x83\x24\x26\x8d\xc6\xf2\x18\x3c\x09\xab\x75\x92\x34\x4b\xc4\x6c\x9c"
+"\x2d\x32\x06\x92\x27\x00\xc9\xf5\x21\x00\x26\xa0\xf5\x65\xed\x26\x4c\xa1"
+"\xd5\xeb\x03\xb0\xab\x04\x80\xcd\x00\x64\x87\x36\x54\x46\x7d\xe1\x03\x30"
+"\x0e\xdb\x33\xbc\x4f\x30\x05\xbf\x8c\xcf\xd3\x5c\x9b\x07\x66\x7d\x4c\xf6"
+"\xca\xa3\xb2\x9f\x80\xe5\xed\x21\x00\xe2\x0b\x25\xa7\x39\xb2\x21\xb5\x0c"
+"\x70\x9c\xe5\x9d\x3c\x0a\x18\x7b\x5c\xc2\x81\x92\x96\xd6\x86\x00\x48\x69"
+"\x00\x28\xec\xf3\xc8\xf0\x29\x28\xc2\x08\x6c\xf1\x3b\x9b\x7a\xa2\x49\x48"
+"\x76\x96\xe1\x50\xbb\xed\x21\x00\x42\xbf\xfb\x3a\x68\x00\x14\x33\xd5\xb3"
+"\x60\x57\x9b\x95\x35\xee\x64\x27\x85\x93\x3c\xa5\x03\x4b\xee\x34\x1c\x17"
+"\xee\xe0\x0b\xda\xcb\x6b\x84\x67\xb9\x13\x81\x36\xdd\xa4\xad\x99\x2d\x47"
+"\x56\x89\x0f\x2c\x36\xf8\xff\xf2\x10\x00\x1c\xef\xd5\x0c\x45\x3b\x5e\xeb"
+"\x81\x4d\x0d\x60\x86\x2d\xea\x0d\x02\x30\x01\x6b\x7e\x0e\x24\x85\x3b\xec"
+"\xc8\xad\x72\xf8\x81\x27\xe2\x12\xb5\xd9\x05\xd6\x30\xdc\xc8\x2c\x74\x33"
+"\x1a\x40\x31\x3f\x08\xa0\x06\xbf\x96\x76\x00\x50\xe0\x3d\x05\x3f\x86\x95"
+"\x57\x34\x00\x0e\xed\xc1\x92\x03\x40\x2a\x6a\x19\x18\x11\x7f\x94\xc3\x5b"
+"\x15\x16\xfd\x75\xe2\xfd\x1c\x2d\x02\xc7\xf9\x96\xd2\x02\xe5\x28\x22\x35"
+"\x59\x1d\x04\x60\x41\x4f\xa4\x70\x46\x34\x25\x9a\x9e\x75\x2c\xfb\x45\xbd"
+"\x16\xac\xe3\x1b\xae\x6b\x55\x9c\xd5\x2d\xb3\x8f\xb6\xc6\x22\x97\x67\x51"
+"\x5b\xa5\x0a\x19\x6c\xc6\x46\x0c\x9b\x43\x00\x8c\x37\x07\x01\x80\x1e\x02"
+"\x97\x1e\x00\xf2\xde\x1d\x00\x91\x13\xc0\xac\x59\xc7\x55\x66\x83\xb3\x0f"
+"\x58\xcc\x55\x44\xb1\x7a\xc1\x95\x89\x67\x59\x9a\x3d\xc1\x12\x0e\x62\x97"
+"\xdc\xc1\x2d\x1c\x05\x1a\x83\x7e\x00\xf1\xed\x61\x43\xd0\x07\x20\xf4\x14"
+"\x70\x7e\x86\x13\xab\x31\xf4\xcf\x71\x52\x2a\x0f\x9a\x70\x3a\xe3\x56\x7d"
+"\xca\x7a\x97\x42\x44\x91\xa3\xcf\xf6\x35\xc9\x81\xae\xaa\x77\x53\x50\x87"
+"\xc0\xda\x83\x00\x12\x56\xe7\x51\x0b\xd4\xde\x34\xe6\xc7\x77\x45\x14\xba"
+"\xdd\x0f\x40\xfd\xdd\x20\x80\xfd\x1b\x79\xf1\xea\x01\x78\x49\x37\xe2\x00"
+"\xd0\x79\x2c\x37\x90\xbc\xfb\x67\x37\x05\x40\x1f\xdd\x74\x00\x01\x05\x34"
+"\x40\xfd\x41\xea\x3d\x28\xbd\x7f\x95\xab\x24\x43\xaf\xb6\x2e\xf5\xd4\xcf"
+"\x87\xea\xdd\x39\xdf\xdd\xd8\xa5\x42\xf4\x52\x56\xf6\xc6\x0e\x1c\x78\xd9"
+"\x1f\x40\x5f\x43\x2e\x00\x34\x29\xe1\xb2\xef\x2e\x6f\x6d\x35\x6e\x18\x00"
+"\xca\xe6\xea\xfa\xee\x8e\x5c\x2a\xbc\x0f\x00\xd6\x95\x02\x30\xb4\xa5\xea"
+"\xbb\x1b\xbb\x94\xbb\x4a\x00\xf1\x3d\x56\x16\x6c\xe8\x8e\xc7\x50\xc4\xc2"
+"\xc7\x7e\x13\x4b\xd1\xc7\xb3\xd4\x55\xf8\xd8\xa7\x5d\x00\xa1\xa3\x7f\x18"
+"\x5e\x28\x01\x44\x1e\xcf\x3a\x58\x5c\x00\xa1\x63\x39\x75\xdf\x71\xbe\xfb"
+"\x33\x9f\xc5\x41\x3b\x46\xae\x8e\xba\xe3\x78\xda\xbd\x56\x47\xb1\xa5\xfb"
+"\x1e\xff\x34\xd9\x8e\x7b\x01\x68\xd3\x50\x87\xd0\xa6\xf9\x0e\x0d\x7b\x87"
+"\x3a\xad\xd1\xaa\xae\xed\x05\xb4\x6d\xbf\x47\x50\xea\xbc\xfd\xcd\xa8\xe6"
+"\x46\x28\x39\x12\x2f\x66\x61\x17\xed\x98\x37\x14\x6c\xdc\x83\x06\x53\xb8"
+"\x4e\xd8\x60\x17\xe0\xfb\xdc\xde\x1f\x7f\xde\x80\x23\x68\x35\x91\x01\xc3"
+"\x81\x3d\x9d\x6c\x67\x42\xda\xe8\x7a\x00\x10\xe5\x68\xc0\x21\x73\x9b\x58"
+"\x29\x54\x68\x6b\x00\x11\x6d\xd7\x00\x87\x40\x34\x80\xa2\x9d\x31\x74\xca"
+"\x8c\xb8\x11\xbb\xd4\x07\x3d\xdd\x12\x43\x28\xc3\x99\x3d\xe8\x65\xa0\x20"
+"\xaf\x2e\xb7\x16\x16\x9e\xf1\x00\x28\x43\x76\x7a\xc7\x03\xa0\xb5\x40\xb1"
+"\x29\x58\x78\x1e\xaa\x65\x58\xc0\x02\xba\xdb\x0b\x65\xf1\xef\xc9\x3c\x6d"
+"\x61\xc1\xd9\xf1\x17\x00\xc7\x65\x4b\x01\xa8\x1a\x3d\x2d\x9d\x36\xca\xf0"
+"\x97\x45\xc8\xf2\x5d\x6c\xcf\x64\x00\x2d\x7a\x96\xa4\x85\xf6\x3a\x34\x80"
+"\x69\x28\xcc\x7b\xf7\x07\x28\x0c\xd4\xa8\x53\xfa\x51\xc3\x14\x4b\x15\x0b"
+"\x29\xc9\xbc\x14\x37\xc4\xe8\x03\x50\x97\x4d\x15\x69\x4e\x39\x00\x9a\x1c"
+"\xe9\xac\xf2\x5d\x6a\x2f\xe4\x3e\xc0\xe2\xba\x08\x4e\x28\x32\x05\x95\x7a"
+"\xc3\x07\xc0\xe2\xc6\x62\x0b\x73\x12\x0b\x14\xb3\x57\xef\xa5\x1b\x43\x00"
+"\xe8\xb8\x87\x17\x00\xa5\xd2\x71\x4e\xe3\xaa\xdc\x75\xa3\x8a\x89\x85\xb9"
+"\x3a\xd8\xff\x91\x91\xb4\x4b\xfe\x2d\x89\x95\x2a\x7d\x1c\xd0\x13\x4e\x03"
+"\x90\x17\xda\x1b\x40\x97\xf7\x72\xfc\x00\xc8\xc1\xf6\x64\x75\xba\x00\x94"
+"\x64\x2a\x6e\x39\x7e\x00\xde\x8a\xc1\x26\x64\xfb\x00\xac\xb2\x6b\xe8\x54"
+"\xb2\x7c\x61\xd0\x41\x00\x2b\x75\xc8\xf4\x03\x20\xe9\x26\x58\x0d\xbe\x5e"
+"\xb5\x3c\x00\xc2\xe8\x5b\x77\x9d\x97\x63\x57\xb0\xdd\x9b\x04\xda\x37\x2c"
+"\x2d\x42\x7a\x12\xe5\x55\x2a\xd5\x28\xac\xab\xa3\x09\x71\x67\x16\x78\x00"
+"\xe0\xd4\xac\x0e\x01\x50\xee\x7c\xd4\x80\x25\xbe\xae\xd4\x3c\x00\xb8\x17"
+"\x2a\x39\x4f\xd4\x7b\x2a\xde\x99\x86\x69\x9e\x80\xa5\xa2\x4c\xc3\x69\x3a"
+"\xe6\xf5\xd3\xe6\x10\x00\x13\x1c\xee\x21\x0f\x79\xa1\xd1\x03\x30\xc9\xee"
+"\x93\x4c\xeb\x19\x3d\x0d\x25\x64\x08\xb2\x67\xd5\xac\x53\x2c\xa9\x89\x2e"
+"\xd5\x5a\x1f\x80\x2d\x5e\x79\xba\xa4\x00\xba\x96\x64\xe0\xed\x3a\xca\x36"
+"\x61\xb6\x1d\x00\xca\x51\x8f\x9c\x18\xe9\xa8\x67\xd7\xec\x8d\xb2\xfb\x24"
+"\x9a\x21\x21\xaf\x12\xa6\x48\x4b\x99\x7d\x2e\x54\x6e\x4b\xa8\xd4\xce\x2a"
+"\x72\x62\x57\x7a\x00\x26\x5e\x3b\x5e\x6f\xe7\x38\x21\x0e\x35\xec\x73\xf6"
+"\x5f\xd0\xcd\x9f\xb3\x3a\x79\x12\x2b\x5d\x07\x01\xfc\xe2\x13\x2e\x00\xba"
+"\xf3\x01\xd5\x4f\xf4\xc3\x47\xac\x8b\x59\xac\xb6\xdc\x29\x28\xf5\x71\x78"
+"\xd3\x5d\x3f\x4e\xc1\x7f\x65\xd0\x13\xba\x98\x8e\xd5\xdf\xbe\x62\xc3\x42"
+"\x0b\xa9\xb7\xf8\xde\x34\xb8\x80\xbd\x3f\x12\x9d\xea\x31\x4c\x1a\x37\x18"
+"\x40\x54\xc6\xb4\xd7\x68\xe9\x06\x03\xa0\x94\x1c\xda\xbd\xd6\x64\xbd\xb9"
+"\x5f\xfd\x6b\x0e\xe0\xea\xe9\xef\x8f\xdf\x64\x00\xff\xd7\xa8\x5c\x29\x57"
+"\xe9\x3c\xcd\x6a\x30\xbe\xaf\x30\x5d\x3b\x0a\xc9\xda\xe7\xac\xcb\xcb\x3c"
+"\xa5\x52\x67\xd6\x6f\x18\x80\xa4\x98\x3f\xe5\x52\xad\x84\xa7\x28\xef\x35"
+"\xa9\xe7\x7f\xfc\xbf\x6f\x18\x80\x99\xca\xfe\x75\x86\x53\xe8\x45\x5a\xc1"
+"\x4e\xbb\xda\xb3\xfc\x83\x71\x34\xc6\xa2\xa8\xc9\x1f\xb1\x6c\x4a\xb0\xd3"
+"\x09\xcd\xa1\xe7\xf8\x8b\xa1\xa7\xbb\x05\xa7\xe6\xc7\x2d\xd2\xb9\x23\xdd"
+"\x9c\x72\x92\xf0\x78\x53\x6d\x4e\x5d\x19\x8d\xc9\x12\xba\xe4\x94\x01\x46"
+"\xd0\xea\x1c\x85\x02\xaf\xd1\x39\x15\x7b\x3b\x1b\x79\x09\x97\x9b\xd9\xb7"
+"\xd2\x77\x7e\x57\x25\x76\x46\x9d\x55\x36\x06\xaf\x51\xc2\x9d\xec\xf2\x4a"
+"\x12\x1e\xaf\x49\x55\xe5\xcb\x7a\xdb\xd7\xb3\x30\x04\x80\x5b\xaf\x7c\x41"
+"\x59\x5d\x6c\x36\x3d\x21\x2a\xe6\x01\xb4\x6b\xc2\x6f\xa9\x68\x07\xdf\xf6"
+"\xd6\x7c\x6a\x2d\xe4\xe4\x5e\x4f\xe0\xea\xdb\xf6\x24\x56\xb1\x09\x5a\x27"
+"\x35\x7e\xa5\x00\xf2\x06\x2e\xb2\xbd\x7a\x21\x7c\x8d\xcc\x0c\xad\xd4\x5c"
+"\x3a\x4f\xdf\x81\x1d\x4e\x4f\xae\x01\x5c\xfc\xc0\x59\x0f\x00\xa3\xcd\x6a"
+"\x30\x6a\xd2\x6a\x2c\x49\x78\x38\x17\x2c\x36\x25\x0f\x00\x20\x92\x16\xab"
+"\x90\xfa\x31\x9a\xaa\x57\x2f\x69\xe2\x52\x5e\xaa\x6d\xe2\x4b\x3d\xfe\xdb"
+"\xb8\xc0\x56\xa6\x4f\x8e\x74\x13\xa5\x72\x09\xd4\xfc\xca\x82\x67\x08\xc6"
+"\xdb\x69\x70\x33\xd1\x25\x09\x0f\x6d\x88\xaf\x21\x27\xdc\x04\x2c\x57\x4d"
+"\xa7\x60\x85\xf6\x42\xa7\xba\x51\xb4\x29\xeb\x5b\x63\x68\x52\x4c\x77\xf7"
+"\x00\x30\x49\xb6\xfb\x2a\x8a\x14\x45\x18\xec\xb9\x89\xec\x72\x06\xe1\xae"
+"\x9a\x59\xaa\x77\x2f\x0b\xe1\x18\x4a\x68\xcd\x8e\x80\xb0\x9d\xbb\x91\x24"
+"\x3c\xfc\xe5\x09\xce\xb2\xec\x07\x90\xa4\xc8\x0f\xfa\x22\x2d\x34\xdc\x8f"
+"\x42\x85\x60\x14\xb5\x7b\x43\x1d\xfa\x00\x4c\x03\x4e\x81\xcb\x28\xcb\xf4"
+"\x41\x01\x6c\x1b\xb2\x09\xd9\x44\x63\x09\x39\x60\xe8\xf7\xa9\x28\xb3\x2d"
+"\xa6\xc9\x8f\x9a\xec\x5b\x4a\x12\x1e\xf6\xf9\xd3\x68\x73\x0d\x02\x88\x73"
+"\x5e\x4d\xae\x46\x69\x32\x28\x61\x68\x02\x66\x6a\x14\xfc\xb8\x15\x91\x94"
+"\x05\xc0\x65\x75\xc8\xec\x7c\x29\x7d\x67\x83\x3e\x6a\x30\x69\x87\xfa\x30"
+"\x7c\x1d\x8d\x37\x43\xbe\x38\x20\x00\xd0\xcd\x18\x9a\x49\x05\xa5\x23\xb8"
+"\x6c\x4d\x2a\x27\x09\x8f\xbe\x03\xc1\x47\x7d\x49\x67\x32\xe0\xb0\x39\x73"
+"\x06\x4a\x16\xa7\xd8\x6c\xd2\x97\x13\x25\xab\x81\xc2\x6d\x92\x6b\xb4\x43"
+"\x00\x9c\x0f\x27\x28\xba\x25\x1f\xa4\x91\xd9\x8c\xb5\x27\xb2\xf5\x93\x31"
+"\x1b\x87\x20\x03\xa3\x76\xfa\x5e\xcd\xa4\x6c\x44\xf6\xd5\xc5\x9e\x56\x4e"
+"\x12\x9e\xaa\x51\x00\x36\x37\x08\x00\x5f\x70\xf1\x45\x38\x49\x18\x4d\x8a"
+"\xfb\xa4\xe0\x3b\xa4\xba\x8f\x00\xa5\xcf\x55\x08\xc0\xa8\x0b\xc0\xd4\x3e"
+"\x8b\xf6\x1f\x50\x08\xd1\x1b\x15\x21\x5c\x5c\x5a\xe0\xd6\x90\x49\x71\x09"
+"\x99\x39\x9f\x97\xe9\x24\x3c\x8a\xfe\x15\x29\xe9\x6c\x00\x40\x7d\xc7\xbc"
+"\x1f\xb6\x20\x4f\x88\xbb\x14\xf7\xd9\x21\x15\x43\xea\x47\x73\xa0\x06\xcd"
+"\x0b\x99\x0f\x3e\x69\x9d\xce\x80\x27\x66\xa6\xa0\x13\x3a\x1f\x59\xee\xce"
+"\x1d\xce\xdc\x46\xcb\x4a\xe8\x2c\xb7\xb6\x48\x72\xda\xd4\xe2\xc0\xe7\x59"
+"\xea\xaa\x25\x96\x15\x71\x55\xa7\xdd\x6d\xf4\xf4\x6b\xd7\xc6\x69\x00\x64"
+"\x2c\x5b\x28\x2c\xc8\x64\x9b\x82\x21\x64\x77\xbb\x32\xa0\x2b\xbb\x00\x2c"
+"\xf8\x93\x34\x32\xda\x51\x44\x5d\x56\x44\x5c\x65\x19\xf2\xb3\xda\x84\xd5"
+"\x4e\xcb\x22\x75\xdc\x51\xa3\x8e\x02\x68\x3a\xbf\x39\x34\x03\x2d\x49\xb0"
+"\x32\x5b\xb3\xa4\x33\x6b\x78\x3d\xea\x9b\x05\xae\x17\xe1\x02\xa8\x91\x7f"
+"\x38\xa7\xa2\x3d\x55\x7c\xc7\x77\xa5\x0a\xfb\xb5\x55\x97\x03\xf8\x32\x3a"
+"\x09\x4f\x22\x80\x1b\x43\x00\x50\x96\x1d\x89\xdf\x28\xac\x8e\xd3\x7b\x4f"
+"\x8b\xba\x18\xa6\x07\x5c\x00\xb4\xe7\x41\xfa\xd7\xbf\x18\x29\x8e\xdf\x80"
+"\x63\xb7\xb2\x10\xb6\x9c\x24\xbc\x69\x1c\xd3\x69\xf7\x03\x34\x7d\xd2\xb2"
+"\x42\x31\xa7\x86\x0a\x1f\xcd\xea\xf2\xda\x5e\x00\x9e\xfd\x17\x67\x6f\xe8"
+"\x39\xf7\x3d\xfd\x14\x81\xad\x33\xbf\x0f\x32\x1a\xcc\xfc\x6f\x3b\x49\x78"
+"\x65\x9c\x9f\x29\x4f\x9a\xa7\x47\x0a\x7d\x94\x78\x2c\xd9\xf3\x9b\x0c\x9c"
+"\x36\xbe\xb5\xa0\xc7\xbc\x48\x71\xa8\x26\x1f\xf1\xde\x8d\x9e\x80\x57\xdd"
+"\x24\x3c\xd4\x2f\xc8\x64\x7b\x5f\x00\x06\x4c\xf4\x56\xed\xfe\xd5\xd0\x5f"
+"\xb1\x31\xe4\xee\x68\xcf\x87\xba\x4a\x22\x59\x71\x27\xc8\xb8\x00\x58\x19"
+"\x5a\x6b\xe8\x10\x8c\xf7\x8c\x87\xab\xa4\x84\x13\x2d\x27\x0a\x9f\x20\x00"
+"\xef\x64\x06\x6a\x91\x7c\xf5\xb2\x40\x7c\xc8\x4a\xef\x13\x80\x7a\xc9\xf6"
+"\x35\xb1\x47\xb4\xed\x93\x96\xff\xe3\xb2\x1f\x6a\x9a\xc2\xb5\x2b\xda\xbe"
+"\x3b\x7f\xd3\x00\x2c\x97\x58\xda\x0a\x5c\x98\xe1\xdc\x8f\xc4\x4e\xa2\xe7"
+"\x0f\xf7\x95\xae\x39\x45\xf9\xcb\xc8\x8f\x7d\x8f\x45\x2a\x64\x55\xe9\x64"
+"\x80\x18\x7d\x23\xf0\xa8\xa7\x74\x23\x28\x29\x46\x5d\xa2\x95\x64\x9b\x62"
+"\xfe\x05\x6f\xe9\xa6\xd3\xd3\xf0\x0f\xd7\xa3\x59\xde\x7a\x43\x09\x30\xdc"
+"\x84\x3a\xa5\xee\x37\xc9\xef\xd1\x4b\x8f\xb3\x02\xd1\x16\x3a\xfe\x18\x59"
+"\x10\x45\x19\xe2\xcd\xce\x28\x9b\x21\xe5\xbf\xea\x09\x49\x9c\xbf\x4f\x51"
+"\xb1\x3f\xa5\xe3\x13\x94\xe0\x60\xe7\x47\x61\x45\x04\xe9\xc8\x85\x3b\x4a"
+"\xc3\x01\x9c\xe5\x3e\x24\xa1\x4e\xf2\x37\xaa\xfd\x00\x68\x0f\x71\x85\x7a"
+"\x13\x7b\x47\xbe\xbf\x9c\x64\xe1\xf5\x6a\xf5\x11\x49\x17\xe3\xbf\x25\x11"
+"\xa5\x95\x7c\xfa\xa4\x7a\xfe\xd7\xdf\x14\x41\xaa\x4b\x86\x47\xc2\xce\xe0"
+"\xd1\x5c\xf7\x00\xe0\xaf\x92\x74\x42\x9d\x24\x6c\xec\xf4\x03\xa8\x83\xac"
+"\x8b\x31\x11\x88\x29\xb6\x80\x96\xbf\x45\xba\xbf\xfc\x67\x1e\x21\x59\xe6"
+"\x45\xb5\x48\x1d\x25\x77\xdc\xfb\x22\x48\x9d\x43\x67\xa8\xeb\xa9\x73\x74"
+"\xa7\xb3\xeb\x01\x40\xc9\x23\x4e\x42\x5d\x8c\xfa\x31\x20\xd3\x07\x00\xf8"
+"\x4f\x31\x20\x7d\x8a\x9f\xab\x91\xe2\x48\xb4\xb5\xb3\xed\xa1\xdb\x88\x4d"
+"\xea\x35\xe6\x4f\xa1\x9c\xe9\xff\x99\xe8\x75\xba\x3b\x5d\x99\xcd\xb9\x00"
+"\xba\x9c\xfb\xae\x13\xea\x92\x02\xa0\x32\x04\x40\x55\x39\x39\x70\x91\x63"
+"\x14\x18\xf2\x15\x24\xea\xc4\xe1\x26\xdf\xd1\x47\xf7\xb9\xa1\x25\x27\xa8"
+"\xc4\x00\x5a\x64\xaa\x38\x09\x75\x13\x02\x60\xc5\x0f\x80\x16\x71\xb2\xfd"
+"\x24\x07\x2e\x66\xf1\xa7\xd5\xde\x82\x44\x9d\x24\xdc\xe4\x3d\xf6\xb6\xb4"
+"\x91\xbb\x9f\x43\xdb\x48\x42\x4b\x72\x74\x00\xac\x71\x1a\x1d\x8b\x92\xfe"
+"\x78\x4a\xf6\xdd\x3d\x00\xd0\xcb\x30\x5a\x69\x27\x07\x8e\x77\xb5\x0b\xbe"
+"\x82\x44\x9d\x24\xdc\xe4\x3d\x7a\x01\xd8\x78\x67\x55\x42\x4b\x72\x74\x00"
+"\x20\xf7\x8b\x9e\x7c\xb6\xa6\xff\xd5\xf9\xb4\x4c\x3f\x6c\x3a\x39\x70\x26"
+"\x3c\x5b\x87\x55\x5f\x61\x9e\xe2\x4d\x55\x09\x37\x79\x8f\x5e\x00\x6c\x18"
+"\x4b\x68\x49\x8e\x2e\x00\x7f\x42\xdd\x30\x00\xf2\x26\x19\x9d\x03\xf7\xc4"
+"\x02\xdd\xf3\x15\x28\x24\x63\x35\x24\xdc\xe4\x3d\xfa\x00\x50\x64\xc7\xe4"
+"\xd0\x92\x1c\x5d\x00\x49\xe8\x25\xd4\x69\x19\xe0\xa0\x87\xc1\x1f\xa8\xf4"
+"\x00\x54\x75\x0e\xdc\x2d\x5f\xa3\x20\x98\xaf\x40\x3d\x7c\x66\x4e\xc2\x4d"
+"\xde\xa3\x1f\xc0\xa4\x1b\x5a\xe2\xa3\x0b\x80\xbe\xce\x77\x12\xea\xf4\x2c"
+"\xa8\xd6\xb9\xe7\x86\x3e\x09\x80\x86\xe4\xc0\x85\x85\x99\xbe\x02\xe8\xa8"
+"\x13\xe8\x28\x63\xef\xe8\x05\x60\xb8\xa1\xa5\x8e\x72\x83\x52\x74\xb1\xd8"
+"\x4b\xa8\xd3\x7a\x20\x57\xe3\x9e\x57\xf4\x49\x3f\x2d\x39\x70\x49\xd8\xfa"
+"\x68\x7f\xc1\x74\xa3\x4e\xfe\xe3\x00\x00\x09\x2d\xc9\xb1\x07\x60\xb2\x97"
+"\x50\xa7\xac\xf5\xb4\x32\x3a\xfc\x17\x06\x0c\x0a\xe3\xf0\x49\x73\x40\xdc"
+"\x52\x1d\x4c\xec\x15\xa6\xc8\x75\xce\x44\xa0\x21\xe1\x26\xef\x71\x00\x80"
+"\x84\x96\xe4\xd8\x03\x10\xef\x25\xd4\x49\xc8\x68\x43\xa7\x55\x6b\x9f\x2a"
+"\xcd\xd2\x49\xcd\xf5\xcc\xfa\xa6\xaf\xc0\x51\xa7\x0a\x3f\x9b\xf7\x1e\x07"
+"\x00\x4c\x72\x2f\x72\xec\x53\x53\x92\x50\xa7\xa2\x16\x2f\x7c\xbc\x0d\x72"
+"\xde\x3d\x71\x4f\x19\xc9\x81\x93\x74\xc3\xa6\xaf\xc0\x51\xa7\xb4\x84\x9b"
+"\xbc\x47\x3f\xb9\xa1\x25\x39\xf6\x91\x93\x50\x77\xa8\xce\x7f\x04\x29\xfe"
+"\x3a\x7f\xdf\x2b\x27\x74\xd8\x0d\xbc\xab\x73\xe0\x9e\x87\x57\x6f\x83\x66"
+"\xaf\x40\xf1\x25\x74\x14\x4b\x3a\xdc\xe4\x3b\xf6\x03\x90\xfd\x1b\x7d\xbc"
+"\xa6\x64\x5c\xe3\xf6\xae\x1c\x00\xef\xcc\xde\x54\x04\x8d\x9b\xda\xfd\xe4"
+"\xfe\x11\xb5\xeb\x4b\x23\x66\xe7\x5a\x4b\x55\x40\x01\xdd\x18\x72\x9c\xdf"
+"\xc5\x8d\xbe\x3b\xb1\x53\xb2\xd8\xbc\x57\x60\xe3\xde\xed\x78\x37\xf7\xc0"
+"\x5b\x51\x6b\xae\x78\x5e\x3d\xfc\xfd\xab\x02\xe0\x38\xbf\xec\xe3\xa4\x68"
+"\x83\x48\xee\x44\x4c\xd8\x1f\x40\x1d\xab\xae\x9a\x30\x8e\x4b\x73\x87\xc2"
+"\x39\xbd\x46\x9b\x46\x7b\x0c\x0e\x94\x42\xab\x9d\xdf\xb8\xb0\x81\xfe\xf0"
+"\x99\xdc\x99\x86\x03\x00\x80\x16\x6d\x65\xb7\x24\x7d\xc8\x9b\xa8\x6d\x34"
+"\x0d\xfb\x08\xf0\xea\xb0\x6f\x64\x48\x68\x82\xec\xb3\xe8\xce\x88\x9b\xe2"
+"\x64\x42\xee\x83\xb3\xfb\x05\xc0\xf1\xe7\x54\x03\xe8\x0f\x86\x95\x41\xfe"
+"\x46\x52\x0f\xc0\xce\xe9\x03\x02\xb8\xfb\xf8\xb0\xfd\xae\x18\x25\xec\xbb"
+"\x81\x8d\x5b\x1e\xcb\x0c\x71\x73\x38\x47\xd0\x58\x31\xd5\xc4\x86\x8a\x20"
+"\xff\xbd\xc9\x39\x4d\x63\x73\xf3\x60\x00\x7e\x42\xfe\x18\x5f\x53\x1b\xe4"
+"\xce\x71\x8c\xac\x20\x07\x00\xda\x1c\x6d\x74\x0c\xbe\x32\xf0\xf0\x22\x0e"
+"\x7f\x61\x1e\x12\x38\x0e\x97\x95\x2f\x39\xa7\x69\x34\x57\x0f\x04\x80\xfe"
+"\x88\xa1\x7c\xbc\xee\x07\x90\x12\x7f\x80\x48\xe9\xbf\x1c\x08\xdd\x01\x56"
+"\x8d\x5a\x13\x17\xd0\x48\x51\x2f\x35\xed\x92\xf2\x6f\xf0\x31\x8c\x03\x00"
+"\x48\xc0\x85\xc7\x64\x17\xdd\x0f\xc0\xf0\x00\x88\x73\xce\xdc\xc2\x73\xbe"
+"\x0f\xb5\x0e\x42\x07\x01\x30\x41\xfe\xe3\xee\x30\x00\xb6\x0b\x60\x42\x8c"
+"\xca\xa4\x7c\xf7\x7d\x05\x3b\x56\x07\x01\xc0\x16\x77\x77\xd8\x10\x74\x38"
+"\x96\x20\x01\x96\xa6\xd7\xe3\x48\x1d\x64\x82\x5e\x01\x80\xb9\x81\x77\x17"
+"\x21\x74\x83\xbf\x12\xfb\xe0\x0e\xd9\xdb\x1a\xbf\xa6\x00\x26\xe8\x73\x30"
+"\x7b\x10\xc0\x88\xfb\x17\x3b\x54\x78\xdc\x9f\xcc\xb4\x37\x45\x2f\x65\xd5"
+"\x03\xe7\xe9\xdf\xc1\x01\x24\xe0\x19\xfd\xf7\x1a\xfd\x00\x74\xac\x87\x69"
+"\x04\x36\x7e\x81\x1d\x97\xc6\x3e\x00\x28\x5e\x6f\xd9\xf4\xef\xe0\x00\x38"
+"\xca\xb3\x41\x5c\xb5\xc0\x7b\xf4\xa8\x62\x50\xce\xb2\x50\xba\x0e\x00\x38"
+"\xdf\xb1\x10\xe3\xfd\xff\xde\x31\x23\x99\xdd\x0e\x80\x9f\x64\x4c\x30\xfc"
+"\xef\x24\x78\x28\x76\x29\xaf\x1e\xfe\x1f\xfa\x77\x05\x00\xc2\x27\xec\xbf"
+"\x51\xea\x21\x4e\xa3\xd3\x47\x51\x79\x23\xa7\x7a\x7f\x3b\xee\x69\x78\x65"
+"\xa4\x4e\x8e\xcf\x95\xd1\x95\x2c\x46\xd7\x85\x02\x00\xc1\x37\x3d\x01\x05"
+"\x14\x50\x40\x01\x05\x14\x50\x40\x01\x05\x14\x50\x40\x01\x05\x14\x50\x40"
+"\x01\x05\x14\x50\x40\x01\x05\x14\x50\x40\x01\x05\x14\x50\x40\x01\x05\x14"
+"\x50\x40\x01\x05\x14\x50\x40\x3f\x0c\xf4\xbf\xed\xbc\xf0\xa9\x4d\x4d\xb4"
+"\xc9\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\x01\x20\x02\x00\xb8"
+"\x16\x00\x00\xb8\x16\x00\x00\x91\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80"
+"\x3b\x00\x00\x66\x3f\x00\x00\xa0\x3c\x00\x00\x7f\x3f\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\x80\x40\x00\x00\x80\x40\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\xc0\x3c\x00\x00\x66\x3f\x00\x00\xc0\x3c\x00\x00\x7f\x3f\x00"
+"\x00\x00\x00\x00\x00\xe0\x40\x00\x00\x00\x00\x00\x00\xe0\x40\x20\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\xe0\x3c\x00\x00\x66\x3f\x00\x00\x30\x3d\x00"
+"\x00\x7f\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x80\x40\x00\x00\xc0"
+"\x40\x21\x00\x00\x00\x00\x00\x00\x00\x00\x00\x40\x3d\x00\x00\x66\x3f\x00"
+"\x00\x88\x3d\x00\x00\x7f\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\xa0"
+"\x40\x00\x00\xe0\x40\x22\x00\x00\x00\x00\x00\x00\x00\x00\x00\x90\x3d\x00"
+"\x00\x66\x3f\x00\x00\x04\x3e\x00\x00\x7f\x3f\x00\x00\x00\x00\x00\x00\x80"
+"\x3f\x00\x00\x70\x41\x00\x00\x80\x41\x23\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x08\x3e\x00\x00\x66\x3f\x00\x00\x38\x3e\x00\x00\x7f\x3f\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x40\x41\x00\x00\x60\x41\x24\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\x3c\x3e\x00\x00\x66\x3f\x00\x00\x78\x3e\x00\x00\x7f"
+"\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x70\x41\x00\x00\x88\x41\x25"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7c\x3e\x00\x00\x66\x3f\x00\x00\x98"
+"\x3e\x00\x00\x7f\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x50\x41\x00"
+"\x00\x70\x41\x26\x00\x00\x00\x00\x00\x00\x00\x00\x00\x9a\x3e\x00\x00\x66"
+"\x3f\x00\x00\x9e\x3e\x00\x00\x7f\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00"
+"\x00\x00\x40\x00\x00\x80\x40\x27\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0"
+"\x3e\x00\x00\x66\x3f\x00\x00\xa8\x3e\x00\x00\x7f\x3f\x00\x00\x00\x40\x00"
+"\x00\x80\x3f\x00\x00\x80\x40\x00\x00\xe0\x40\x28\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\xaa\x3e\x00\x00\x66\x3f\x00\x00\xb4\x3e\x00\x00\x7f\x3f\x00"
+"\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\xa0\x40\x00\x00\xe0\x40\x29\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\xb6\x3e\x00\x00\x66\x3f\x00\x00\xc4\x3e\x00"
+"\x00\x7f\x3f\x00\x00\x00\x40\x00\x00\x00\x40\x00\x00\xe0\x40\x00\x00\x30"
+"\x41\x2a\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6\x3e\x00\x00\x66\x3f\x00"
+"\x00\xde\x3e\x00\x00\x7f\x3f\x00\x00\x00\x40\x00\x00\x00\x40\x00\x00\x40"
+"\x41\x00\x00\x80\x41\x2b\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x3e\x00"
+"\x00\x66\x3f\x00\x00\xe8\x3e\x00\x00\x7f\x3f\x00\x00\x80\x3f\x00\x00\x80"
+"\x3f\x00\x00\x80\x40\x00\x00\xc0\x40\x2c\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\xea\x3e\x00\x00\x66\x3f\x00\x00\xf4\x3e\x00\x00\x7f\x3f\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\xa0\x40\x00\x00\xe0\x40\x2d\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\xf6\x3e\x00\x00\x66\x3f\x00\x00\xfe\x3e\x00\x00\x7f"
+"\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x80\x40\x00\x00\xc0\x40\x2e"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x3f\x00\x00\x66\x3f\x00\x00\x07"
+"\x3f\x00\x00\x7f\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x40\x00"
+"\x00\xe0\x40\x2f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x08\x3f\x00\x00\x66"
+"\x3f\x00\x00\x14\x3f\x00\x00\x7f\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00"
+"\x00\x40\x41\x00\x00\x60\x41\x30\x00\x00\x00\x00\x00\x00\x00\x00\x00\x15"
+"\x3f\x00\x00\x66\x3f\x00\x00\x1b\x3f\x00\x00\x7f\x3f\x00\x00\x40\x40\x00"
+"\x00\x80\x40\x00\x00\xc0\x40\x00\x00\x50\x41\x31\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x1c\x3f\x00\x00\x66\x3f\x00\x00\x27\x3f\x00\x00\x7f\x3f\x00"
+"\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x50\x41\x32\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\x28\x3f\x00\x00\x66\x3f\x00\x00\x34\x3f\x00"
+"\x00\x7f\x3f\x00\x00\x00\x00\x00\x00\x80\x3f\x00\x00\x40\x41\x00\x00\x50"
+"\x41\x33\x00\x00\x00\x00\x00\x00\x00\x00\x00\x35\x3f\x00\x00\x66\x3f\x00"
+"\x00\x42\x3f\x00\x00\x7f\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x50"
+"\x41\x00\x00\x70\x41\x34\x00\x00\x00\x00\x00\x00\x00\x00\x00\x43\x3f\x00"
+"\x00\x66\x3f\x00\x00\x4e\x3f\x00\x00\x7f\x3f\x00\x00\x80\x3f\x00\x00\x80"
+"\x3f\x00\x00\x30\x41\x00\x00\x50\x41\x35\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x4f\x3f\x00\x00\x66\x3f\x00\x00\x5a\x3f\x00\x00\x7f\x3f\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x50\x41\x36\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\x5b\x3f\x00\x00\x66\x3f\x00\x00\x65\x3f\x00\x00\x7f"
+"\x3f\x00\x00\x00\x40\x00\x00\x00\x40\x00\x00\x20\x41\x00\x00\x60\x41\x37"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x66\x3f\x00\x00\x66\x3f\x00\x00\x72"
+"\x3f\x00\x00\x7f\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x40\x41\x00"
+"\x00\x60\x41\x38\x00\x00\x00\x00\x00\x00\x00\x00\x00\x73\x3f\x00\x00\x66"
+"\x3f\x00\x00\x7f\x3f\x00\x00\x7f\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00"
+"\x00\x40\x41\x00\x00\x60\x41\x39\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80"
+"\x3b\x00\x00\x4c\x3f\x00\x00\xc0\x3c\x00\x00\x65\x3f\x00\x00\x80\x3f\x00"
+"\x00\x80\x3f\x00\x00\xa0\x40\x00\x00\xe0\x40\x3a\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\xe0\x3c\x00\x00\x4c\x3f\x00\x00\x40\x3d\x00\x00\x65\x3f\x00"
+"\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\xa0\x40\x00\x00\xe0\x40\x3b\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\x50\x3d\x00\x00\x4c\x3f\x00\x00\xc8\x3d\x00"
+"\x00\x65\x3f\x00\x00\x40\x40\x00\x00\x40\x40\x00\x00\x40\x41\x00\x00\x90"
+"\x41\x3c\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd0\x3d\x00\x00\x4c\x3f\x00"
+"\x00\x18\x3e\x00\x00\x65\x3f\x00\x00\x00\x40\x00\x00\x00\x40\x00\x00\x40"
+"\x41\x00\x00\x80\x41\x3d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1c\x3e\x00"
+"\x00\x4c\x3f\x00\x00\x4c\x3e\x00\x00\x65\x3f\x00\x00\x40\x40\x00\x00\x40"
+"\x40\x00\x00\x40\x41\x00\x00\x90\x41\x3e\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x50\x3e\x00\x00\x4c\x3f\x00\x00\x7c\x3e\x00\x00\x65\x3f\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x50\x41\x3f\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\x80\x3e\x00\x00\x4c\x3f\x00\x00\xa4\x3e\x00\x00\x65"
+"\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x90\x41\x00\x00\xa0\x41\x40"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa6\x3e\x00\x00\x4c\x3f\x00\x00\xc2"
+"\x3e\x00\x00\x65\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x60\x41\x00"
+"\x00\x60\x41\x41\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc4\x3e\x00\x00\x4c"
+"\x3f\x00\x00\xe0\x3e\x00\x00\x65\x3f\x00\x00\x80\x3f\x00\x00\x00\x00\x00"
+"\x00\x60\x41\x00\x00\x70\x41\x42\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe2"
+"\x3e\x00\x00\x4c\x3f\x00\x00\xfc\x3e\x00\x00\x65\x3f\x00\x00\x80\x3f\x00"
+"\x00\x80\x3f\x00\x00\x50\x41\x00\x00\x70\x41\x43\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\xfe\x3e\x00\x00\x4c\x3f\x00\x00\x0c\x3f\x00\x00\x65\x3f\x00"
+"\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x50\x41\x00\x00\x70\x41\x44\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\x0d\x3f\x00\x00\x4c\x3f\x00\x00\x1a\x3f\x00"
+"\x00\x65\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x50\x41\x00\x00\x70"
+"\x41\x45\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1b\x3f\x00\x00\x4c\x3f\x00"
+"\x00\x26\x3f\x00\x00\x65\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x30"
+"\x41\x00\x00\x50\x41\x46\x00\x00\x00\x00\x00\x00\x00\x00\x00\x27\x3f\x00"
+"\x00\x4c\x3f\x00\x00\x36\x3f\x00\x00\x65\x3f\x00\x00\x80\x3f\x00\x00\x80"
+"\x3f\x00\x00\x70\x41\x00\x00\x88\x41\x47\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x37\x3f\x00\x00\x4c\x3f\x00\x00\x44\x3f\x00\x00\x65\x3f\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x50\x41\x00\x00\x70\x41\x48\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\x45\x3f\x00\x00\x4c\x3f\x00\x00\x49\x3f\x00\x00\x65"
+"\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x80\x40\x00\x00\xc0\x40\x49"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x4a\x3f\x00\x00\x4c\x3f\x00\x00\x54"
+"\x3f\x00\x00\x65\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x20\x41\x00"
+"\x00\x40\x41\x4a\x00\x00\x00\x00\x00\x00\x00\x00\x00\x55\x3f\x00\x00\x4c"
+"\x3f\x00\x00\x64\x3f\x00\x00\x65\x3f\x00\x00\x80\x3f\x00\x00\x00\x00\x00"
+"\x00\x70\x41\x00\x00\x80\x41\x4b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x65"
+"\x3f\x00\x00\x4c\x3f\x00\x00\x6e\x3f\x00\x00\x65\x3f\x00\x00\x80\x3f\x00"
+"\x00\x80\x3f\x00\x00\x10\x41\x00\x00\x30\x41\x4c\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x6f\x3f\x00\x00\x4c\x3f\x00\x00\x7f\x3f\x00\x00\x65\x3f\x00"
+"\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x80\x41\x00\x00\x90\x41\x4d\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\x80\x3b\x00\x00\x32\x3f\x00\x00\x70\x3d\x00"
+"\x00\x4b\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x60\x41\x00\x00\x80"
+"\x41\x4e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3d\x00\x00\x32\x3f\x00"
+"\x00\xf0\x3d\x00\x00\x4b\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x60"
+"\x41\x00\x00\x80\x41\x4f\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x3d\x00"
+"\x00\x32\x3f\x00\x00\x2c\x3e\x00\x00\x4b\x3f\x00\x00\x80\x3f\x00\x00\x80"
+"\x3f\x00\x00\x40\x41\x00\x00\x60\x41\x50\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x30\x3e\x00\x00\x32\x3f\x00\x00\x68\x3e\x00\x00\x4b\x3f\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x60\x41\x00\x00\x80\x41\x51\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\x6c\x3e\x00\x00\x32\x3f\x00\x00\x92\x3e\x00\x00\x4b"
+"\x3f\x00\x00\x80\x3f\x00\x00\x00\x00\x00\x00\x60\x41\x00\x00\x70\x41\x52"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94\x3e\x00\x00\x32\x3f\x00\x00\xae"
+"\x3e\x00\x00\x4b\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x50\x41\x00"
+"\x00\x70\x41\x53\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\x3e\x00\x00\x32"
+"\x3f\x00\x00\xc8\x3e\x00\x00\x4b\x3f\x00\x00\x00\x00\x00\x00\x80\x3f\x00"
+"\x00\x40\x41\x00\x00\x50\x41\x54\x00\x00\x00\x00\x00\x00\x00\x00\x00\xca"
+"\x3e\x00\x00\x32\x3f\x00\x00\xe4\x3e\x00\x00\x4b\x3f\x00\x00\x80\x3f\x00"
+"\x00\x80\x3f\x00\x00\x50\x41\x00\x00\x70\x41\x55\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\xe6\x3e\x00\x00\x32\x3f\x00\x00\x00\x3f\x00\x00\x4b\x3f\x00"
+"\x00\x00\x00\x00\x00\x80\x3f\x00\x00\x50\x41\x00\x00\x60\x41\x56\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\x01\x3f\x00\x00\x32\x3f\x00\x00\x15\x3f\x00"
+"\x00\x4b\x3f\x00\x00\x00\x00\x00\x00\x80\x3f\x00\x00\xa0\x41\x00\x00\xa8"
+"\x41\x57\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x3f\x00\x00\x32\x3f\x00"
+"\x00\x25\x3f\x00\x00\x4b\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x70"
+"\x41\x00\x00\x70\x41\x58\x00\x00\x00\x00\x00\x00\x00\x00\x00\x26\x3f\x00"
+"\x00\x32\x3f\x00\x00\x33\x3f\x00\x00\x4b\x3f\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x50\x41\x00\x00\x50\x41\x59\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x34\x3f\x00\x00\x32\x3f\x00\x00\x41\x3f\x00\x00\x4b\x3f\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x50\x41\x00\x00\x70\x41\x5a\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\x42\x3f\x00\x00\x32\x3f\x00\x00\x46\x3f\x00\x00\x4b"
+"\x3f\x00\x00\x00\x40\x00\x00\x80\x3f\x00\x00\x80\x40\x00\x00\xe0\x40\x5b"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x47\x3f\x00\x00\x32\x3f\x00\x00\x4e"
+"\x3f\x00\x00\x4b\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x40\x00"
+"\x00\xe0\x40\x5c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x4f\x3f\x00\x00\x32"
+"\x3f\x00\x00\x53\x3f\x00\x00\x4b\x3f\x00\x00\x00\x00\x00\x00\x00\x40\x00"
+"\x00\x80\x40\x00\x00\xc0\x40\x5d\x00\x00\x00\x00\x00\x00\x00\x00\x00\x54"
+"\x3f\x00\x00\x32\x3f\x00\x00\x60\x3f\x00\x00\x4b\x3f\x00\x00\x80\x40\x00"
+"\x00\x80\x40\x00\x00\x40\x41\x00\x00\xa0\x41\x5e\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x61\x3f\x00\x00\x32\x3f\x00\x00\x6b\x3f\x00\x00\x4b\x3f\x00"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x41\x00\x00\x20\x41\x5f\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\x6c\x3f\x00\x00\x32\x3f\x00\x00\x71\x3f\x00"
+"\x00\x4b\x3f\x00\x00\x00\x40\x00\x00\x40\x40\x00\x00\xa0\x40\x00\x00\x20"
+"\x41\x60\x00\x00\x00\x00\x00\x00\x00\x00\x00\x72\x3f\x00\x00\x32\x3f\x00"
+"\x00\x7e\x3f\x00\x00\x4b\x3f\x00\x00\x00\x00\x00\x00\x80\x3f\x00\x00\x40"
+"\x41\x00\x00\x50\x41\x61\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3b\x00"
+"\x00\x18\x3f\x00\x00\x50\x3d\x00\x00\x31\x3f\x00\x00\x80\x3f\x00\x00\x80"
+"\x3f\x00\x00\x40\x41\x00\x00\x60\x41\x62\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x60\x3d\x00\x00\x18\x3f\x00\x00\xc8\x3d\x00\x00\x31\x3f\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x50\x41\x63\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\xd0\x3d\x00\x00\x18\x3f\x00\x00\x1c\x3e\x00\x00\x31"
+"\x3f\x00\x00\x80\x3f\x00\x00\x00\x00\x00\x00\x50\x41\x00\x00\x60\x41\x64"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x3e\x00\x00\x18\x3f\x00\x00\x4c"
+"\x3e\x00\x00\x31\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x30\x41\x00"
+"\x00\x50\x41\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x3e\x00\x00\x18"
+"\x3f\x00\x00\x7c\x3e\x00\x00\x31\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x30\x41\x00\x00\x30\x41\x66\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80"
+"\x3e\x00\x00\x18\x3f\x00\x00\x9a\x3e\x00\x00\x31\x3f\x00\x00\x00\x00\x00"
+"\x00\x80\x3f\x00\x00\x50\x41\x00\x00\x60\x41\x67\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x9c\x3e\x00\x00\x18\x3f\x00\x00\xb0\x3e\x00\x00\x31\x3f\x00"
+"\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x20\x41\x00\x00\x40\x41\x68\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\xb2\x3e\x00\x00\x18\x3f\x00\x00\xba\x3e\x00"
+"\x00\x31\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x80\x40\x00\x00\xc0"
+"\x40\x69\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\x3e\x00\x00\x18\x3f\x00"
+"\x00\xc6\x3e\x00\x00\x31\x3f\x00\x00\x00\x00\x00\x00\x80\x3f\x00\x00\xa0"
+"\x40\x00\x00\xc0\x40\x6a\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc8\x3e\x00"
+"\x00\x18\x3f\x00\x00\xe0\x3e\x00\x00\x31\x3f\x00\x00\x80\x3f\x00\x00\x80"
+"\x3f\x00\x00\x40\x41\x00\x00\x60\x41\x6b\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\xe2\x3e\x00\x00\x18\x3f\x00\x00\xea\x3e\x00\x00\x31\x3f\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x80\x40\x00\x00\xc0\x40\x6c\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\xec\x3e\x00\x00\x18\x3f\x00\x00\x08\x3f\x00\x00\x31"
+"\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x90\x41\x00\x00\xa0\x41\x6d"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x09\x3f\x00\x00\x18\x3f\x00\x00\x15"
+"\x3f\x00\x00\x31\x3f\x00\x00\x00\x00\x00\x00\x80\x3f\x00\x00\x40\x41\x00"
+"\x00\x50\x41\x6e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x16\x3f\x00\x00\x18"
+"\x3f\x00\x00\x21\x3f\x00\x00\x31\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00"
+"\x00\x30\x41\x00\x00\x50\x41\x6f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x22"
+"\x3f\x00\x00\x18\x3f\x00\x00\x2d\x3f\x00\x00\x31\x3f\x00\x00\x80\x3f\x00"
+"\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x50\x41\x70\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x2e\x3f\x00\x00\x18\x3f\x00\x00\x3a\x3f\x00\x00\x31\x3f\x00"
+"\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x40\x41\x00\x00\x60\x41\x71\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\x3b\x3f\x00\x00\x18\x3f\x00\x00\x45\x3f\x00"
+"\x00\x31\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x20\x41\x00\x00\x40"
+"\x41\x72\x00\x00\x00\x00\x00\x00\x00\x00\x00\x46\x3f\x00\x00\x18\x3f\x00"
+"\x00\x50\x3f\x00\x00\x31\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x20"
+"\x41\x00\x00\x40\x41\x73\x00\x00\x00\x00\x00\x00\x00\x00\x00\x51\x3f\x00"
+"\x00\x18\x3f\x00\x00\x59\x3f\x00\x00\x31\x3f\x00\x00\x00\x00\x00\x00\x80"
+"\x3f\x00\x00\x00\x41\x00\x00\x10\x41\x74\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x5a\x3f\x00\x00\x18\x3f\x00\x00\x65\x3f\x00\x00\x31\x3f\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x50\x41\x75\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\x66\x3f\x00\x00\x18\x3f\x00\x00\x70\x3f\x00\x00\x31"
+"\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x41\x00\x00\x20\x41\x76"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3b\x00\x00\xfc\x3e\x00\x00\x90"
+"\x3d\x00\x00\x17\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x88\x41\x00"
+"\x00\x88\x41\x77\x00\x00\x00\x00\x00\x00\x00\x00\x00\x98\x3d\x00\x00\xfc"
+"\x3e\x00\x00\xf0\x3d\x00\x00\x17\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x30\x41\x00\x00\x30\x41\x78\x00\x00\x00\x00\x00\x00\x00\x00\x00\xf8"
+"\x3d\x00\x00\xfc\x3e\x00\x00\x28\x3e\x00\x00\x17\x3f\x00\x00\x00\x00\x00"
+"\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x40\x41\x79\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x2c\x3e\x00\x00\xfc\x3e\x00\x00\x58\x3e\x00\x00\x17\x3f\x00"
+"\x00\x00\x00\x00\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x40\x41\x7a\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\x5c\x3e\x00\x00\xfc\x3e\x00\x00\x78\x3e\x00"
+"\x00\x17\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\xe0\x40\x00\x00\x10"
+"\x41\x7b\x00\x00\x00\x00\x00\x00\x00\x00\x00\x7c\x3e\x00\x00\xfc\x3e\x00"
+"\x00\x82\x3e\x00\x00\x17\x3f\x00\x00\x80\x40\x00\x00\x80\x40\x00\x00\x00"
+"\x40\x00\x00\x20\x41\x7c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x84\x3e\x00"
+"\x00\xfc\x3e\x00\x00\x92\x3e\x00\x00\x17\x3f\x00\x00\x80\x3f\x00\x00\x80"
+"\x3f\x00\x00\xe0\x40\x00\x00\x10\x41\x7d\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x94\x3e\x00\x00\xfc\x3e\x00\x00\xae\x3e\x00\x00\x17\x3f\x00\x00\x00"
+"\x40\x00\x00\x00\x40\x00\x00\x50\x41\x00\x00\x88\x41\x7e\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\xb0\x3e\x00\x00\xfc\x3e\x00\x00\xb8\x3e\x00\x00\x17"
+"\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x80\x40\x00\x00\xc0\x40\xa1"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\xba\x3e\x00\x00\xfc\x3e\x00\x00\xd2"
+"\x3e\x00\x00\x17\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x40\x41\x00"
+"\x00\x60\x41\xa5\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd4\x3e\x00\x00\xfc"
+"\x3e\x00\x00\xec\x3e\x00\x00\x17\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00"
+"\x00\x40\x41\x00\x00\x60\x41\xa7\x00\x00\x00\x00\x00\x00\x00\x00\x00\xee"
+"\x3e\x00\x00\xfc\x3e\x00\x00\x05\x3f\x00\x00\x17\x3f\x00\x00\x80\x3f\x00"
+"\x00\x80\x3f\x00\x00\x60\x41\x00\x00\x80\x41\xa9\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x06\x3f\x00\x00\xfc\x3e\x00\x00\x14\x3f\x00\x00\x17\x3f\x00"
+"\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x60\x41\x00\x00\x80\x41\xae\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\x15\x3f\x00\x00\xfc\x3e\x00\x00\x1c\x3f\x00"
+"\x00\x17\x3f\x00\x00\x00\x00\x00\x00\x80\x3f\x00\x00\xe0\x40\x00\x00\x00"
+"\x41\xb0\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1d\x3f\x00\x00\xfc\x3e\x00"
+"\x00\x2a\x3f\x00\x00\x17\x3f\x00\x00\x00\x00\x00\x00\x80\x3f\x00\x00\x50"
+"\x41\x00\x00\x60\x41\xb5\x00\x00\x00\x00\x00\x00\x00\x00\x00\x2b\x3f\x00"
+"\x00\xfc\x3e\x00\x00\x34\x3f\x00\x00\x17\x3f\x00\x00\x80\x3f\x00\x00\x80"
+"\x3f\x00\x00\x10\x41\x00\x00\x30\x41\xba\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x35\x3f\x00\x00\xfc\x3e\x00\x00\x40\x3f\x00\x00\x17\x3f\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x50\x41\xbf\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\x41\x3f\x00\x00\xfc\x3e\x00\x00\x4f\x3f\x00\x00\x17"
+"\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x60\x41\x00\x00\x60\x41\xc1"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x3f\x00\x00\xfc\x3e\x00\x00\x5e"
+"\x3f\x00\x00\x17\x3f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x60\x41\x00"
+"\x00\x60\x41\xc4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x5f\x3f\x00\x00\xfc"
+"\x3e\x00\x00\x6c\x3f\x00\x00\x17\x3f\x00\x00\x80\x3f\x00\x00\x80\x3f\x00"
+"\x00\x50\x41\x00\x00\x70\x41\xc9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x6d"
+"\x3f\x00\x00\xfc\x3e\x00\x00\x7a\x3f\x00\x00\x17\x3f\x00\x00\x80\x3f\x00"
+"\x00\x80\x3f\x00\x00\x50\x41\x00\x00\x70\x41\xca\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x80\x3b\x00\x00\xc8\x3e\x00\x00\xc0\x3c\x00\x00\xfa\x3e\x00"
+"\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\xa0\x40\x00\x00\xe0\x40\xcd\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\xe0\x3c\x00\x00\xc8\x3e\x00\x00\xa8\x3d\x00"
+"\x00\xfa\x3e\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x60\x41\x00\x00\x80"
+"\x41\xd1\x00\x00\x00\x00\x00\x00\x00\x00\x00\xb0\x3d\x00\x00\xc8\x3e\x00"
+"\x00\x10\x3e\x00\x00\xfa\x3e\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x60"
+"\x41\x00\x00\x80\x41\xd3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x3e\x00"
+"\x00\xc8\x3e\x00\x00\x4c\x3e\x00\x00\xfa\x3e\x00\x00\x80\x3f\x00\x00\x80"
+"\x3f\x00\x00\x60\x41\x00\x00\x80\x41\xd6\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x50\x3e\x00\x00\xc8\x3e\x00\x00\x82\x3e\x00\x00\xfa\x3e\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x50\x41\x00\x00\x70\x41\xda\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\x84\x3e\x00\x00\xc8\x3e\x00\x00\x9e\x3e\x00\x00\xfa"
+"\x3e\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x50\x41\x00\x00\x70\x41\xdc"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x3e\x00\x00\xc8\x3e\x00\x00\xba"
+"\x3e\x00\x00\xfa\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x41\x00"
+"\x00\x50\x41\xdf\x00\x00\x00\x00\x00\x00\x00\x00\x00\xbc\x3e\x00\x00\xc8"
+"\x3e\x00\x00\xd4\x3e\x00\x00\xfa\x3e\x00\x00\x00\x00\x00\x00\x80\x3f\x00"
+"\x00\x40\x41\x00\x00\x50\x41\xe0\x00\x00\x00\x00\x00\x00\x00\x00\x00\xd6"
+"\x3e\x00\x00\xc8\x3e\x00\x00\xee\x3e\x00\x00\xfa\x3e\x00\x00\x00\x00\x00"
+"\x00\x80\x3f\x00\x00\x40\x41\x00\x00\x50\x41\xe1\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\xf0\x3e\x00\x00\xc8\x3e\x00\x00\x04\x3f\x00\x00\xfa\x3e\x00"
+"\x00\x00\x00\x00\x00\x80\x3f\x00\x00\x40\x41\x00\x00\x50\x41\xe2\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\x05\x3f\x00\x00\xc8\x3e\x00\x00\x11\x3f\x00"
+"\x00\xfa\x3e\x00\x00\x00\x00\x00\x00\x80\x3f\x00\x00\x40\x41\x00\x00\x50"
+"\x41\xe4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x12\x3f\x00\x00\xc8\x3e\x00"
+"\x00\x1d\x3f\x00\x00\xfa\x3e\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x30"
+"\x41\x00\x00\x50\x41\xe7\x00\x00\x00\x00\x00\x00\x00\x00\x00\x1e\x3f\x00"
+"\x00\xc8\x3e\x00\x00\x29\x3f\x00\x00\xfa\x3e\x00\x00\x80\x3f\x00\x00\x80"
+"\x3f\x00\x00\x30\x41\x00\x00\x50\x41\xe8\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x2a\x3f\x00\x00\xc8\x3e\x00\x00\x35\x3f\x00\x00\xfa\x3e\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x50\x41\xe9\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\x36\x3f\x00\x00\xc8\x3e\x00\x00\x41\x3f\x00\x00\xfa"
+"\x3e\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x50\x41\xea"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x42\x3f\x00\x00\xc8\x3e\x00\x00\x47"
+"\x3f\x00\x00\xfa\x3e\x00\x00\x80\x3f\x00\x00\x00\x00\x00\x00\xa0\x40\x00"
+"\x00\xc0\x40\xed\x00\x00\x00\x00\x00\x00\x00\x00\x00\x48\x3f\x00\x00\xc8"
+"\x3e\x00\x00\x4e\x3f\x00\x00\xfa\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\xc0\x40\x00\x00\xc0\x40\xee\x00\x00\x00\x00\x00\x00\x00\x00\x00\x4f"
+"\x3f\x00\x00\xc8\x3e\x00\x00\x56\x3f\x00\x00\xfa\x3e\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\xe0\x40\x00\x00\xe0\x40\xef\x00\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x57\x3f\x00\x00\xc8\x3e\x00\x00\x63\x3f\x00\x00\xfa\x3e\x00"
+"\x00\x00\x00\x00\x00\x80\x3f\x00\x00\x40\x41\x00\x00\x50\x41\xf1\x00\x00"
+"\x00\x00\x00\x00\x00\x00\x00\x64\x3f\x00\x00\xc8\x3e\x00\x00\x6f\x3f\x00"
+"\x00\xfa\x3e\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x50"
+"\x41\xf3\x00\x00\x00\x00\x00\x00\x00\x00\x00\x70\x3f\x00\x00\xc8\x3e\x00"
+"\x00\x7b\x3f\x00\x00\xfa\x3e\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x30"
+"\x41\x00\x00\x50\x41\xf4\x00\x00\x00\x00\x00\x00\x00\x00\x00\x80\x3b\x00"
+"\x00\x94\x3e\x00\x00\x40\x3d\x00\x00\xc6\x3e\x00\x00\x80\x3f\x00\x00\x80"
+"\x3f\x00\x00\x30\x41\x00\x00\x50\x41\xf6\x00\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x50\x3d\x00\x00\x94\x3e\x00\x00\xc0\x3d\x00\x00\xc6\x3e\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x50\x41\xfa\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x00\xc8\x3d\x00\x00\x94\x3e\x00\x00\x10\x3e\x00\x00\xc6"
+"\x3e\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x30\x41\x00\x00\x50\x41\xfb"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x14\x3e\x00\x00\x94\x3e\x00\x00\x40"
+"\x3e\x00\x00\xc6\x3e\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x30\x41\x00"
+"\x00\x50\x41\xfc\x00\x00\x00\x00\x00\x00\x00\x00\x00\x44\x3e\x00\x00\x94"
+"\x3e\x00\x00\x90\x3e\x00\x00\xc6\x3e\x00\x00\x80\x3f\x00\x00\x80\x3f\x00"
+"\x00\xb8\x41\x00\x00\xc8\x41\x52\x01\x00\x00\x00\x00\x00\x00\x00\x00\x92"
+"\x3e\x00\x00\x94\x3e\x00\x00\xb6\x3e\x00\x00\xc6\x3e\x00\x00\x80\x3f\x00"
+"\x00\x80\x3f\x00\x00\x90\x41\x00\x00\xa0\x41\x53\x01\x00\x00\x00\x00\x00"
+"\x00\x00\x00\xb8\x3e\x00\x00\x94\x3e\x00\x00\xcc\x3e\x00\x00\xc6\x3e\x00"
+"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x20\x41\x00\x00\x20\x41\x13\x20\x00"
+"\x00\x00\x00\x00\x00\x00\x00\xce\x3e\x00\x00\x94\x3e\x00\x00\xf6\x3e\x00"
+"\x00\xc6\x3e\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xa0\x41\x00\x00\xa0"
+"\x41\x14\x20\x00\x00\x00\x00\x00\x00\x00\x00\xf8\x3e\x00\x00\x94\x3e\x00"
+"\x00\x00\x3f\x00\x00\xc6\x3e\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x80"
+"\x40\x00\x00\xc0\x40\x18\x20\x00\x00\x00\x00\x00\x00\x00\x00\x01\x3f\x00"
+"\x00\x94\x3e\x00\x00\x05\x3f\x00\x00\xc6\x3e\x00\x00\x80\x3f\x00\x00\x80"
+"\x3f\x00\x00\x80\x40\x00\x00\xc0\x40\x19\x20\x00\x00\x00\x00\x00\x00\x00"
+"\x00\x06\x3f\x00\x00\x94\x3e\x00\x00\x0f\x3f\x00\x00\xc6\x3e\x00\x00\x80"
+"\x3f\x00\x00\x80\x3f\x00\x00\x10\x41\x00\x00\x30\x41\x1c\x20\x00\x00\x00"
+"\x00\x00\x00\x00\x00\x10\x3f\x00\x00\x94\x3e\x00\x00\x1a\x3f\x00\x00\xc6"
+"\x3e\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x20\x41\x00\x00\x40\x41\x1d"
+"\x20\x00\x00\x00\x00\x00\x00\x00\x00\x1b\x3f\x00\x00\x94\x3e\x00\x00\x2c"
+"\x3f\x00\x00\xc6\x3e\x00\x00\x80\x3f\x00\x00\x80\x3f\x00\x00\x88\x41\x00"
+"\x00\x98\x41\x26\x20\x00\x00\x00\x00\x00\x00\x00\x00\x2d\x3f\x00\x00\x94"
+"\x3e\x00\x00\x3a\x3f\x00\x00\xc6\x3e\x00\x00\x00\x40\x00\x00\x00\x40\x00"
+"\x00\x50\x41\x00\x00\x88\x41\x22\x21\x00\x00\x00\x00\x00\x00\x00\x00\x3b"
+"\x3f\x00\x00\x94\x3e\x00\x00\x45\x3f\x00\x00\xc6\x3e\x00\x00\x80\x3f\x00"
+"\x00\x80\x3f\x00\x00\x20\x41\x00\x00\x40\x41\x7f\x7f\x00\x00";
+
+static const unsigned int DEFAULTFONT_SIZE = 52051;
+
+static const char* DEFAULTFONT_NAME = "adlibn.p3d";
+
diff --git a/game/code/gameflow/allgameflow.cpp b/game/code/gameflow/allgameflow.cpp
new file mode 100644
index 0000000..870b94b
--- /dev/null
+++ b/game/code/gameflow/allgameflow.cpp
@@ -0,0 +1 @@
+#include <gameflow/gameflow.cpp>
diff --git a/game/code/gameflow/gameflow.cpp b/game/code/gameflow/gameflow.cpp
new file mode 100644
index 0000000..deb4bb1
--- /dev/null
+++ b/game/code/gameflow/gameflow.cpp
@@ -0,0 +1,377 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: gameflow.cpp
+//
+// Description: Implementation for GameFlow class.
+//
+// History: + Stolen and cleaned up from Penthouse -- Darwin Chau
+//
+//=============================================================================
+
+#define DONTCHECKVECTORRESIZING
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+#include <radtime.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <gameflow/gameflow.h>
+
+#include <contexts/entrycontext.h>
+#include <contexts/bootupcontext.h>
+#include <contexts/frontendcontext.h>
+#include <contexts/pausecontext.h>
+#include <contexts/exitcontext.h>
+#include <contexts/gameplay/loadinggameplaycontext.h>
+#include <contexts/gameplay/gameplaycontext.h>
+#include <contexts/demo/loadingdemocontext.h>
+#include <contexts/demo/democontext.h>
+#include <contexts/supersprint/loadingsupersprintcontext.h>
+#include <contexts/supersprint/supersprintcontext.h>
+#include <contexts/supersprint/supersprintfecontext.h>
+
+#include <sound/soundmanager.h>
+#include <main/commandlineoptions.h>
+#include <main/game.h>
+#include <memory/memoryutilities.h>
+#include <memory/srrmemory.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Static pointer to instance of this singleton.
+//
+GameFlow* GameFlow::spInstance = NULL;
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// GameFlow::CreateInstance
+//==============================================================================
+//
+// Description: Create the gameflow controller.
+//
+// Parameters: None.
+//
+// Return: Pointer to the created gameflow controller.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+GameFlow* GameFlow::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "GameFlow" );
+ rAssert( spInstance == NULL );
+
+ spInstance = new(GMA_PERSISTENT) GameFlow;
+ rAssert( spInstance );
+MEMTRACK_POP_GROUP( "GameFlow" );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// GameFlow::GetInstance
+//==============================================================================
+//
+// Description: Return the gameflow controller.
+//
+// Parameters: None.
+//
+// Return: Pointer to the created gameflow controller.
+//
+//==============================================================================
+GameFlow* GameFlow::GetInstance()
+{
+ //rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// GameFlow::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the gameflow controller.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void GameFlow::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+}
+
+
+//==============================================================================
+// GameFlow::PushContext
+//==============================================================================
+//
+// Description: Change the context. Pushes the new context onto the stack.
+// The previous context(s) are preserved on the stack.
+// Note: the new context will not take effect until the next
+// timer tick (game loop update).
+//
+// Parameters: context - the new context
+//
+// Return: None.
+//
+//==============================================================================
+void GameFlow::PushContext( ContextEnum context )
+{
+ //
+ // Really bad news if this assert triggers. There's already a context
+ // change in the queue when this one was requested.
+ //
+ // This assert cannot be ignored, you must fix the problem.
+ //
+ rAssert( mCurrentContext == mNextContext );
+
+MEMTRACK_PUSH_GROUP( "GameFlow" );
+ mNextContext = context;
+ mContextStack.push( context );
+MEMTRACK_POP_GROUP( "GameFlow" );
+}
+
+
+//==============================================================================
+// GameFlow::PopContext
+//==============================================================================
+//
+// Description: Restore the previous context.
+//
+// Note: the new context will not take effect until the next
+// timer tick (game loop update).
+//
+// Constraints: Obviously we have a problem if there's nothing on the stack.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void GameFlow::PopContext()
+{
+ rAssert( mContextStack.size() > 1 );
+
+ //
+ // Really bad news if this assert triggers. There's already a context
+ // change in the queue when this one was requested.
+ //
+ // This assert cannot be ignored, you must fix the problem.
+ //
+ rAssert( mCurrentContext == mNextContext );
+
+ mContextStack.pop();
+ mNextContext = mContextStack.top();
+}
+
+
+//==============================================================================
+// GameFlow::SetContext
+//==============================================================================
+//
+// Description: Change the context and clear out the stack.
+//
+// Note: the new context will not take effect until the next
+// timer tick (game loop update).
+//
+// Parameters: context - the new context
+//
+// Return: None.
+//
+//==============================================================================
+void GameFlow::SetContext( ContextEnum context )
+{
+ while( !mContextStack.empty() )
+ {
+ mContextStack.pop();
+ }
+
+ this->PushContext( context );
+}
+
+
+//==============================================================================
+// GameFlow::OnTimerDone
+//==============================================================================
+//
+// Description: This routine is invoked to run the game. It gets called by the
+// dispatcher once per frame.
+//
+// Parameters: elapsedtime - time it actually took for timer to expire
+// pUserData - custom user data
+//
+// Return: None.
+//
+//==============================================================================
+void GameFlow::OnTimerDone( unsigned int elapsedtime, void* pUserData )
+{
+ //////////////////////////////////////////////////
+ // Debugging stuff.
+ //////////////////////////////////////////////////
+
+ bool printMemory = CommandLineOptions::Get( CLO_PRINT_MEMORY );
+ if( printMemory )
+ {
+ static int accumulatedTime = 0;
+ const int printTimeIntervalMs = 3000;
+ accumulatedTime += elapsedtime;
+ if( accumulatedTime > printTimeIntervalMs )
+ {
+ accumulatedTime %= printTimeIntervalMs;
+ Memory::PrintMemoryStatsToTty();
+ }
+ }
+
+ #ifndef RAD_RELEASE
+
+ // HACK to prevent elapsedtime from being ridiculously huge.
+ // This is so that when we set breakpoints we don't have really huge
+ // elapsedtime values screwing us up.
+ if( elapsedtime > 1000 )
+ {
+ elapsedtime = 20;
+ }
+
+ #endif
+
+ //////////////////////////////////////////////////
+ // Switch contexts if appropriate.
+ //////////////////////////////////////////////////
+
+ // If current and next contexts are different...
+ if( mCurrentContext != mNextContext )
+ {
+ mpContexts[mCurrentContext]->Stop( mNextContext );
+ mpContexts[mNextContext]->Start( mCurrentContext );
+
+ mCurrentContext = mNextContext;
+ }
+
+ //////////////////////////////////////////////////
+ // See if have anything to update.
+ //////////////////////////////////////////////////
+
+ // If current context is exit, then stop the game.
+ if( mCurrentContext == CONTEXT_EXIT )
+ {
+ GetGame()->Stop();
+ return;
+ }
+
+ //////////////////////////////////////////////////
+ // Update managers and controllers.
+ //////////////////////////////////////////////////
+
+ //
+ // Run the once-per-frame sound update
+ //
+ SoundManager::GetInstance()->UpdateOncePerFrame( elapsedtime, mCurrentContext );
+
+ // Update the current context.
+ mpContexts[mCurrentContext]->Update( elapsedtime );
+}
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// GameFlow::GameFlow
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+GameFlow::GameFlow() :
+ mpITimer( NULL ),
+ mCurrentContext( CONTEXT_ENTRY ),
+ mNextContext( CONTEXT_ENTRY )
+{
+ //
+ // Initialize members.
+ //
+ int i = CONTEXT_ENTRY;
+ for( ; i < NUM_CONTEXTS; ++i )
+ {
+ mpContexts[i] = NULL;
+ }
+
+ //
+ // Create the context controllers.
+ //
+ mpContexts[CONTEXT_ENTRY] = GetEntryContext();
+ mpContexts[CONTEXT_BOOTUP] = GetBootupContext();
+ mpContexts[CONTEXT_FRONTEND] = GetFrontEndContext();
+ mpContexts[CONTEXT_LOADING_DEMO] = GetLoadingDemoContext();
+ mpContexts[CONTEXT_DEMO] = GetDemoContext();
+ mpContexts[CONTEXT_LOADING_SUPERSPRINT] = GetLoadingSuperSprintContext();
+ mpContexts[CONTEXT_SUPERSPRINT] = GetSPCTX();
+ mpContexts[CONTEXT_SUPERSPRINT_FE] = GetSuperSprintFEContext();
+ mpContexts[CONTEXT_LOADING_GAMEPLAY] = GetLoadingGameplayContext();
+ mpContexts[CONTEXT_GAMEPLAY] = GetGameplayContext();
+ mpContexts[CONTEXT_PAUSE] = GetPauseContext();
+ mpContexts[CONTEXT_EXIT] = GetExitContext();
+
+ //
+ // Since we're starting with the entry context, call its Start function
+ // for the sake of symmetry and to allow memory tagging to work properly
+ //
+ mpContexts[mCurrentContext]->Start( mCurrentContext );
+}
+
+//==============================================================================
+// GameFlow::~GameFlow
+//==============================================================================
+//
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+GameFlow::~GameFlow()
+{
+ //
+ // Release the context controllers.
+ //
+ int i = CONTEXT_ENTRY;
+ for( ; i < NUM_CONTEXTS; ++i )
+ {
+ rAssert( mpContexts[i] != NULL );
+ mpContexts[i]->DestroyInstance();
+ }
+}
+
+#undef DONTCHECKVECTORRESIZING \ No newline at end of file
diff --git a/game/code/gameflow/gameflow.h b/game/code/gameflow/gameflow.h
new file mode 100644
index 0000000..6a0f83a
--- /dev/null
+++ b/game/code/gameflow/gameflow.h
@@ -0,0 +1,106 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: GameFlow
+//
+// Description: The GameFlow Controller orchestrates the overall game execution.
+//
+// History: + Stolen and cleaned up from Penthouse -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef GAMEFLOW_H
+#define GAMEFLOW_H
+
+//========================================
+// System Includes
+//========================================
+#include <vector>
+#include <stack>
+
+#include <radtime.hpp> // IRadTimerCallback
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/contextenum.h>
+#include <memory/stlallocators.h>
+
+//========================================
+// Forward References
+//========================================
+class Context;
+
+
+//=============================================================================
+//
+// Synopsis: The game "loop"
+//
+//=============================================================================
+class GameFlow : public IRadTimerCallback
+{
+ public:
+
+ // Static Methods (for creating and getting an instance of the game)
+ static GameFlow* CreateInstance();
+ static GameFlow* GetInstance();
+ static void DestroyInstance();
+
+ // Functions to change the context.
+ void PushContext( ContextEnum context );
+ void PopContext();
+ void SetContext( ContextEnum context );
+
+ // Implement IRadTimerCallback interface.
+ // This member is called whenever the timer expires.
+ void OnTimerDone( unsigned int elapsedtime, void* pUserData );
+
+ ContextEnum GetCurrentContext() const { return mCurrentContext; }
+ ContextEnum GetNextContext() const { return mNextContext; }
+
+ Context* GetContext( ContextEnum which ) const { return mpContexts[which]; }
+/*
+ bool GetQuickStartLoading( void ) const { return mQuickStartLoading; }
+ void SetQuickStartLoading( bool IsQuickStartLoading ) { mQuickStartLoading = IsQuickStartLoading; }
+*/
+ private:
+
+ // Declared but not defined to prevent copying and assignment.
+ GameFlow( const GameFlow& );
+ GameFlow& operator=( const GameFlow& );
+
+ // Constructor - these are private to prevent anybody else from
+ // creating me.
+ GameFlow();
+ virtual ~GameFlow();
+
+ // This member is called when the gameflow is being initialized.
+ void Initialize();
+
+ // The one and only GameFlow instance.
+ static GameFlow* spInstance;
+
+ // Timer for gameflow updates.
+ IRadTimer* mpITimer;
+
+ // Contexts
+ ContextEnum mCurrentContext;
+ ContextEnum mNextContext;
+
+ typedef std::vector< ContextEnum, s2alloc<ContextEnum> > ContextEnumSequence;
+ typedef std::stack< ContextEnum, ContextEnumSequence > ContextStack;
+
+ ContextStack mContextStack;
+
+ Context* mpContexts[NUM_CONTEXTS];
+ bool mQuickStartLoading : 1;
+};
+
+
+//
+// A little syntactic sugar for getting at this singleton.
+//
+inline GameFlow* GetGameFlow() { return( GameFlow::GetInstance() ); }
+
+
+#endif
diff --git a/game/code/input/FEMouse.cpp b/game/code/input/FEMouse.cpp
new file mode 100644
index 0000000..4a31ea6
--- /dev/null
+++ b/game/code/input/FEMouse.cpp
@@ -0,0 +1,338 @@
+#include <input/FEMouse.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiuserinputhandler.h>
+#include <gameflow/gameflow.h>
+#include <contexts/contextenum.h>
+#include <contexts/gameplay/gameplaycontext.h>
+#include <main/win32platform.h>
+
+#ifdef ENABLE_DYNA_LOADED_IMAGES
+const char* DYNAMIC_RESOURCES_DIR = "art\\frontend\\dynaload\\images\\";
+#endif
+
+const float WIN_TO_P3D_POINT_SCALE_FACTOR = 2.0f;
+
+/******************************************************************************
+ Construction/Destruction
+*****************************************************************************/
+
+FEMouse::FEMouse()
+: m_bMoved( false ),
+ m_bClickable( true ),
+ m_bSelectable( true ),
+ m_bMovable( true ),
+ m_oldPositionX(0),
+ m_oldPositionY(0),
+ m_hotSpotType( HOTSPOT_BUTTON ),
+ m_horzDir( NO_HORZ_MOVEMENT ),
+ m_bClickAndStop(false),
+ m_bInGame( false ),
+ m_bInGameOverride( false ),
+ m_inGamePosX( 0 ),
+ m_inGamePosY( 0 ),
+ m_buttonDownSelection( 0 )
+{
+ m_pCursor = new MouseCursor();
+ memset( m_button, 0, sizeof(m_button));
+}
+
+FEMouse::~FEMouse()
+{
+ delete m_pCursor;
+ m_pCursor = NULL;
+}
+
+void FEMouse::InitMouseCursor( tDrawable* pCursor )
+{
+ m_pCursor->Set( pCursor );
+}
+
+eFEMouseHorzDir FEMouse::OnSliderHorizontalClickDrag() const
+{
+ if (IsLeftButtonDown() &&
+ m_hotSpotType == HOTSPOT_SLIDER &&
+ m_horzDir != NO_HORZ_MOVEMENT &&
+ m_bMoved == true )
+ {
+ return m_horzDir;
+ }
+ else return NO_HORZ_MOVEMENT;
+}
+
+bool FEMouse::DidWeMove( int newX, int newY ) const
+{
+ return ( (newX != m_oldPositionX) || (newY != m_oldPositionY) );
+}
+
+void FEMouse::Move( int mouseX, int mouseY, long screenWidth, long screenHeight )
+{
+ if( !m_bMovable ) return;
+ m_bMoved = true;
+
+ // let us know what direction it moved horizontally.
+ if( m_oldPositionX < mouseX ) m_horzDir = MDIR_RIGHT;
+ else if( m_oldPositionX > mouseX ) m_horzDir = MDIR_LEFT;
+ else m_horzDir = NO_HORZ_MOVEMENT;
+
+
+ m_oldPositionX = mouseX;
+ m_oldPositionY = mouseY;
+
+ // Normalize the windows mouse coordinates that are in pixel units, to 2d world units.
+ // From ( -1 to 1 ) on the major axis and ( -height/width to -height/width ) on the minor.
+ // We must scale the incoming coordinates and offset them to the center.
+ // We must also handle the difference in width and height, to compensate for the aspect
+ // ratio of the display.
+
+ const float ASPECT = (float)screenWidth/(float)screenHeight; // The screen aspect ratio.
+
+ float deltaX = ( (mouseX/( screenWidth/WIN_TO_P3D_POINT_SCALE_FACTOR )) - 1.0f );
+ float deltaY = ( 1.0f - (mouseY/(screenHeight/WIN_TO_P3D_POINT_SCALE_FACTOR)))/ASPECT;
+
+ m_pCursor->SetPos( deltaX/WIN_TO_P3D_POINT_SCALE_FACTOR, deltaY/WIN_TO_P3D_POINT_SCALE_FACTOR );
+ //rDebugPrintf("Mouse moved to X: %g Y: %g \n", deltaX, deltaY);
+}
+
+void FEMouse::Update()
+{
+ bool ingame = m_bInGame && ! m_bInGameOverride;
+ bool bLoading = (GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_GAMEPLAY) ||
+ (GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_SUPERSPRINT) ||
+ (GetGameFlow()->GetCurrentContext() == CONTEXT_EXIT);
+ if( ingame || bLoading ) return;
+ if( !m_pCursor->IsVisible() ) return;
+
+ CGuiWindow* pCurrentWindow = GetGuiSystem()->GetCurrentManager()->GetCurrentWindow();
+ CGuiMenu* pCurrentMenu = pCurrentWindow->HasMenu();
+ if( !pCurrentMenu || (pCurrentMenu && !pCurrentMenu->HasSelectionBeenMade()) )//m_bSelectable )
+ {
+
+ // Dusit here,
+ // Took out the update only on mouse moved test.
+ // This is a problem when a new front-end screen item appears underneath
+ // the mouse cursor, but the mouse hasn't moved yet, so it can't click
+ // on the front-end item. So we either need to get notified when that
+ // happens and manually update the mouse focus via callback, or just
+ // do it every frame. I think it's reasonable to do it every frame; otherwise, if
+ // the call is really expensive, we'd end up with low framerate while mouse is
+ // moving and good framerate when mouse is stationary (which is not
+ // a good optimization).
+ //
+ //if( m_bMoved ) //Only update the hotspot checking if the mouse moved!
+ //{
+ //
+ m_hotSpotType = pCurrentWindow->CheckCursorAgainstHotspots( m_pCursor->XPos()*WIN_TO_P3D_POINT_SCALE_FACTOR,
+ m_pCursor->YPos()*WIN_TO_P3D_POINT_SCALE_FACTOR);
+ if( m_hotSpotType == HOTSPOT_NONE )
+ {
+ // if the user is still holding onto the button, don't tamper with its state.
+ // we deal with that case later (just below).
+ if( m_button[ BUTTON_LEFT ] != BUTTON_CLICKHOLD )
+ {
+ m_button[ BUTTON_LEFT ] = BUTTON_IDLE;
+ }
+ }
+ m_bMoved = false;
+ rAssert( !m_bMoved );
+
+ if( pCurrentMenu )
+ {
+ if( m_button[ BUTTON_LEFT ] == BUTTON_CLICKHOLD )
+ {
+ // deal with highlighting the hotspot...
+ CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( 0 );
+ switch( m_hotSpotType )
+ {
+ case HOTSPOT_BUTTON:
+ if( m_buttonDownSelection == pCurrentMenu->GetSelection() && m_bClickable )
+ {
+ pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 1 );
+ }
+ break;
+ case HOTSPOT_NONE:
+ if( m_bClickable )
+ {
+ pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 0 );
+ }
+ break;
+ default: break;
+ }
+ }
+ else
+ {
+ // If nothing is being held down we should eliminate the highlighting.
+ // This fixes the problem where if you hold down LMB then right-click to
+ // escape to previous screen, when you go back to the first screen, the
+ // highlighting will still be in effect.
+ //
+ if( m_bClickable && m_bSelectable )
+ {
+ pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 0 );
+ }
+ }
+ }
+ }
+ m_pCursor->Render();
+}
+
+void FEMouse::ButtonDown( eFEMouseButton buttonType )
+{
+ // don't allow clicking when selectable not on
+ CGuiMenu* pCurrentMenu = GetGuiSystem()->GetCurrentManager()->GetCurrentWindow()->HasMenu();
+ if( !(!pCurrentMenu || (pCurrentMenu && !pCurrentMenu->HasSelectionBeenMade())) )//!m_bSelectable )
+ {
+ return;
+ }
+
+ m_button[buttonType] = BUTTON_CLICKHOLD;
+
+ // what follows concerns only left mouse button
+ if( buttonType != BUTTON_LEFT )
+ {
+ return;
+ }
+
+ // remember what menu item it was that we pressed down on...
+ m_buttonDownSelection = pCurrentMenu ? pCurrentMenu->GetSelection() : -1;
+
+ // deal with highlighting the hotspot...
+ if( pCurrentMenu )
+ {
+ CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( 0 );
+ switch( m_hotSpotType )
+ {
+ case HOTSPOT_BUTTON:
+ if( m_bClickable )
+ {
+ pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 1 );
+ }
+ break;
+ case HOTSPOT_NONE:
+ if( m_bClickable )
+ {
+ pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_LCLICKHOLD, 0 );
+ }
+ break;
+ default: break;
+ }
+ }
+}
+
+void FEMouse::ButtonUp( enum eFEMouseButton buttonType )
+{
+ // don't allow clicking when selectable not on
+ CGuiMenu* pCurrentMenu = GetGuiSystem()->GetCurrentManager()->GetCurrentWindow()->HasMenu();
+ if( !(!pCurrentMenu || (pCurrentMenu && !pCurrentMenu->HasSelectionBeenMade())) )//!m_bSelectable )
+ {
+ return;
+ }
+
+ m_button[ buttonType ] = BUTTON_CLICKED;
+
+ // what follows concerns only left mouse button
+ if( buttonType != BUTTON_LEFT )
+ {
+ return;
+ }
+
+ /////////////////////////////////////////////////////////////////
+ // Deal with the consequence of left-clicking a menu button
+
+ //
+ // if we release mouse button on something other than the item we
+ // were on when we pressed the mouse button, we don't do anything
+ //
+ int currSelection = pCurrentMenu ? pCurrentMenu->GetSelection() : -1;
+ if( currSelection != m_buttonDownSelection )
+ {
+ return;
+ }
+
+ // at this point, the hotspot we're currently on matches the
+ // one we were at when we pressed the left mouse button... so click!
+ CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( 0 );
+ switch( m_hotSpotType )
+ {
+ case HOTSPOT_BUTTON:
+ userInputHandler->Select();
+ m_bSelectable = false; // need to disable selectable IMMEDIATELY (not when screen exits)
+ break;
+ case HOTSPOT_ARROWLEFT:
+ userInputHandler->Left();
+ break;
+ case HOTSPOT_ARROWRIGHT:
+ userInputHandler->Right();
+ break;
+ case HOTSPOT_ARROWUP:
+ userInputHandler->Up();
+ break;
+ case HOTSPOT_ARROWDOWN:
+ userInputHandler->Down();
+ break;
+ case HOTSPOT_LTRIGGER:
+ userInputHandler->L1();
+ break;
+ case HOTSPOT_RTRIGGER:
+ userInputHandler->R1();
+ break;
+ default: break;
+ }
+
+ // reset left button state
+ m_button[ BUTTON_LEFT ] = BUTTON_IDLE;
+}
+
+void FEMouse::SetInGameMode( bool ingame )
+{
+ m_bInGame = ingame;
+ SetupInGameMode();
+}
+
+void FEMouse::SetInGameOverride( bool override )
+{
+ m_bInGameOverride = override;
+ SetupInGameMode();
+}
+
+void FEMouse::SetupInGameMode()
+{
+ bool ingame = m_bInGame && ! m_bInGameOverride;
+
+ m_pCursor->SetVisible( !ingame );
+
+ if( ingame )
+ {
+ RECT windowRect;
+ GetWindowRect( Win32Platform::GetInstance()->GetHwnd(), &windowRect );
+
+ // Save the old cursor position.
+ POINT oldPos;
+ GetCursorPos( & oldPos );
+ m_inGamePosX = oldPos.x;
+ m_inGamePosY = oldPos.y;
+
+ // Center the cursor.
+ SetCursorPos( ( windowRect.left + windowRect.right ) / 2,
+ ( windowRect.top + windowRect.bottom ) / 2 );
+
+ // Set up the clipping rectangle.
+ windowRect.top += 30;
+ windowRect.bottom -= 10;
+ windowRect.left += 10;
+ windowRect.right -= 10;
+ ClipCursor( &windowRect );
+ }
+ else
+ {
+ // Remove the clipping rectangle.
+ ClipCursor( NULL );
+
+ // Restore the old cursor position.
+ if( m_inGamePosX != -1 )
+ {
+ SetCursorPos( m_inGamePosX, m_inGamePosY );
+ m_inGamePosX = -1;
+ }
+ }
+} \ No newline at end of file
diff --git a/game/code/input/FEMouse.h b/game/code/input/FEMouse.h
new file mode 100644
index 0000000..1298f80
--- /dev/null
+++ b/game/code/input/FEMouse.h
@@ -0,0 +1,109 @@
+/******************************************************************************
+ File: FEMouse.h
+ Desc: Interface for the Frontend Mouse class.
+ - Uses the windows mouse functionality.
+
+ Date: June 13, 2003
+ History:
+*****************************************************************************/
+
+#ifndef FEMOUSE_H
+#define FEMOUSE_H
+
+#include <input/MouseCursor.h>
+
+enum eFEMouseButton
+{
+ BUTTON_LEFT,
+ BUTTON_RIGHT,
+ NUM_BUTTON_TYPES
+};
+
+enum eFEMouseHorzDir
+{
+ MDIR_LEFT,
+ MDIR_RIGHT,
+ NUM_MOUSE_HORZ_DIRECTIONS,
+ NO_HORZ_MOVEMENT
+};
+
+enum eFEHotspotType
+{
+ HOTSPOT_BUTTON,
+ HOTSPOT_ARROWLEFT,
+ HOTSPOT_ARROWRIGHT,
+ HOTSPOT_ARROWUP,
+ HOTSPOT_ARROWDOWN,
+ HOTSPOT_SLIDER,
+ HOTSPOT_LTRIGGER,
+ HOTSPOT_RTRIGGER,
+ NUM_HOTSPOT_TYPES,
+ HOTSPOT_NONE
+};
+
+enum eFEButtonStates
+{
+ BUTTON_IDLE,
+ BUTTON_CLICKHOLD,
+ BUTTON_CLICKED,
+ NUM_BUTTON_STATES
+};
+
+class FEMouse
+{
+public:
+ FEMouse();
+ ~FEMouse();
+
+ MouseCursor* getCursor() const { return m_pCursor; }
+ bool Moved() const { return m_bMoved; }
+ bool IsClickable() const { return m_bClickable; }
+ eFEMouseHorzDir MovedHorizontally() const { return m_horzDir; }
+ bool IsLeftButtonDown() const { return (m_button[BUTTON_LEFT]==BUTTON_CLICKHOLD); }
+ eFEHotspotType LeftButtonDownOn() const
+ {
+ return ((m_button[BUTTON_LEFT]==BUTTON_CLICKHOLD) ? m_hotSpotType : HOTSPOT_NONE);
+ }
+ bool IsRightButtonDown() const { return (m_button[BUTTON_RIGHT]==BUTTON_CLICKHOLD); }
+
+ void InitMouseCursor( tDrawable* pCursor );
+ void SetClickable( bool bClickable ) { m_bClickable = bClickable; }
+ void SetSelectable( bool bSelectable ) { m_bSelectable = bSelectable; }
+ void SetClickStopMode( bool bClickAndStop ) { m_bClickAndStop = bClickAndStop; }
+ void ButtonDown( eFEMouseButton buttonType );
+ void ButtonUp( eFEMouseButton buttonType );
+
+ eFEMouseHorzDir OnSliderHorizontalClickDrag() const; //Is on a slider clicking and dragging horizontally.
+ bool DidWeMove( int newX, int newY ) const;
+ void Move( int mouseX, int mouseY, long screenWidth, long screenHeight );
+ void Update();
+
+ void SetInGameMode( bool ingame );
+ void SetInGameOverride( bool override );
+
+private:
+ void SetupInGameMode();
+
+private:
+ MouseCursor* m_pCursor;
+ bool m_bMoved;
+ bool m_bClickable;
+ bool m_bSelectable;
+ bool m_bMovable;
+ bool m_bClickAndStop; // if this is on, if a button is clicked, mouse processing stops.
+ char m_button[ NUM_BUTTON_TYPES ];
+ eFEHotspotType m_hotSpotType;
+ eFEMouseHorzDir m_horzDir;
+
+ int m_oldPositionX, //We save the last position of the mouse.
+ m_oldPositionY;
+
+ bool m_bInGame;
+ bool m_bInGameOverride;
+ int m_inGamePosX;
+ int m_inGamePosY;
+
+ int m_buttonDownSelection;
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/input/Gamepad.cpp b/game/code/input/Gamepad.cpp
new file mode 100644
index 0000000..5bf4445
--- /dev/null
+++ b/game/code/input/Gamepad.cpp
@@ -0,0 +1,567 @@
+//**********************************************************
+// C++ Class Name : Gamepad
+// ---------------------------------------------------------
+// Filetype: (SOURCE)
+// Filepath: D:/simpsonsrr2/game/code/input/Gamepad.cpp
+//
+//
+// GDPro Properties
+// ---------------------------------------------------
+// - GD Symbol Type : CLD_Class
+// - GD Method : UML ( 5.0 )
+// - GD System Name : Simpsons Controller System
+// - GD Diagram Type : Class Diagram
+// - GD Diagram Name : Input Device
+// ---------------------------------------------------
+// Author : nharan
+// Creation Date : Tues - May 20, 2003
+//
+// Change Log :
+//
+//**********************************************************
+#include <input/Gamepad.h>
+
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+
+//==============================================================================
+// Gamepad constructor
+//==============================================================================
+//
+// Description: Creates an empty gamepad.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+
+Gamepad::Gamepad()
+: RealController( GAMEPAD )
+{
+ ClearMappedButtons();
+}
+
+//==============================================================================
+// Gamepad destructor
+//==============================================================================
+//
+// Description: Destroy the gamepad.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+
+Gamepad::~Gamepad()
+{
+}
+
+//==============================================================================
+// Gamepad::IsInputAxis
+//==============================================================================
+//
+// Description: Identifies any keys that are input axes. (can go in two directions)
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if input is an axis
+//
+//==============================================================================
+
+bool Gamepad::IsInputAxis( int dxKey ) const
+{
+ switch( dxKey )
+ {
+ case DIJOFS_X:
+ case DIJOFS_Y:
+ case DIJOFS_Z:
+ case DIJOFS_RX:
+ case DIJOFS_RY:
+ case DIJOFS_RZ:
+ case DIJOFS_SLIDER(0):
+ case DIJOFS_SLIDER(1):
+ GetButtonEnum( dxKey );
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+//==============================================================================
+// Gamepad::IsPovHat
+//==============================================================================
+//
+// Description: Identifies any keys that are pov hats (go in 4 directions)
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if input is a pov hat
+//
+//==============================================================================
+
+bool Gamepad::IsPovHat( int dxKey ) const
+{
+ switch( dxKey )
+ {
+ case DIJOFS_POV(0):
+ case DIJOFS_POV(1):
+ return true;
+ default:
+ return false;
+ }
+}
+
+//==============================================================================
+// Gamepad::IsValidInput
+//==============================================================================
+//
+// Description: Returns true if this is a valid input for the controller.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if key it's a valid input
+//
+//==============================================================================
+
+bool Gamepad::IsValidInput( int dxKey ) const
+{
+ return GetButtonEnum( dxKey ) != NUM_GAMEPAD_BUTTONS;
+}
+
+//==============================================================================
+// Gamepad::CalculatePOV
+//==============================================================================
+//
+// Description: Takes a POV input point value and calculates the corresponding
+// up/down/left/right directional values.
+//
+// Parameters: POV value
+//
+// Return: Up/Down/Left/Right values.
+//
+//==============================================================================
+
+void Gamepad::CalculatePOV( float povValue, float* up, float* down, float* right, float* left ) const
+{
+ // Reset all the directional values to 0.
+ *up = *down = *left = *right = 0.0f;
+
+ // Convert the POV value to directional values.
+ // A value of 1 means that the pov is untouched.
+ if( povValue < 1)
+ {
+ // Convert the POV value (0.0f - 1.0f) into radians (0.0f - 2PI) for use with radmath Trig functions.
+ float angle = ((povValue * 360)/180)*rmt::PI;
+
+ // Calculate the corresponding x and y axis values.
+ float xValue = rmt::Sin(angle);
+ float yValue = rmt::Cos(angle);
+
+ // Now assign the value to all the directions.
+ // Note that we are making the POV a digital button with values of only 0 and 1.
+ // This is so that walking diagonally is doable, instead of annoyingly slow.
+ *right = ( xValue > 0.1f ) ? 1.0f : 0.0f;
+ *left = ( xValue < -0.1f ) ? 1.0f : 0.0f;
+ *up = ( yValue > 0.1f ) ? 1.0f : 0.0f;
+ *down = ( yValue < -0.1f ) ? 1.0f : 0.0f;
+ }
+}
+
+//==============================================================================
+// Gamepad::SetMap
+//==============================================================================
+//
+// Description: Sets up a mapping from a dxkey/direction to a virtual button
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input
+// virtualbutton - the virtual button that has been mapped
+//
+// Return: true if mapped successfully
+//
+//==============================================================================
+
+bool Gamepad::SetMap( int dxKey, eDirectionType dir, int virtualButton )
+{
+ rAssert( virtualButton >= 0 && virtualButton < Input::MaxPhysicalButtons );
+
+ eGamepadButton gbutton = GetButtonEnum( dxKey );
+ eMapType maptype = VirtualInputs::GetType( virtualButton );
+
+ if( gbutton != NUM_GAMEPAD_BUTTONS )
+ {
+ m_ButtonMap[ maptype ][ gbutton ][ dir ] = virtualButton;
+ }
+
+ return gbutton != NUM_GAMEPAD_BUTTONS;
+}
+
+//==============================================================================
+// Gamepad::ClearMap
+//==============================================================================
+//
+// Description: Clears the specified mapping so it no longer exists.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Gamepad::ClearMap( int dxKey, eDirectionType dir, int virtualButton )
+{
+ eGamepadButton gbutton = GetButtonEnum( dxKey );
+ eMapType maptype = VirtualInputs::GetType( virtualButton );
+
+ if( gbutton != NUM_GAMEPAD_BUTTONS )
+ {
+ m_ButtonMap[ maptype ][ gbutton ][ dir ] = Input::INVALID_CONTROLLERID;
+ }
+}
+
+//==============================================================================
+// Gamepad::GetMap
+//==============================================================================
+//
+// Description: Retrieves the virtual button of the given type mapped to
+// a dxKey, direction
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input.
+// maptype - the type of virtual button to look up
+//
+// Return: n/a
+//
+//==============================================================================
+
+int Gamepad::GetMap( int dxKey, eDirectionType dir, eMapType map ) const
+{
+ eGamepadButton gbutton = GetButtonEnum( dxKey );
+
+ if( gbutton != NUM_GAMEPAD_BUTTONS )
+ {
+ return m_ButtonMap[ map ][ gbutton ][ dir ];
+ }
+ else
+ {
+ return Input::INVALID_CONTROLLERID;
+ }
+}
+
+//==============================================================================
+// Gamepad::ClearMappedButtons
+//==============================================================================
+//
+// Description: Clears the cached button mappings
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Gamepad::ClearMappedButtons()
+{
+ memset( &m_ButtonMap, Input::INVALID_CONTROLLERID, sizeof( m_ButtonMap ) );
+}
+
+//==============================================================================
+// Gamepad::MapInputToDICode
+//==============================================================================
+//
+// Description: Creates an input point -> direct input virtual key code mapping
+// for the gamepad. Note: this method is really slow, but it only
+// should get called once per game.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Gamepad::MapInputToDICode()
+{
+ if( m_InputToDICode != NULL )
+ {
+ delete [] m_InputToDICode;
+ m_InputToDICode = NULL;
+ }
+
+ if( m_radController != NULL )
+ {
+ // Get the number of input points
+ m_numInputPoints = m_radController->GetNumberOfInputPoints();
+
+ // Set up a cleared index -> di map.
+ m_InputToDICode = new int[ m_numInputPoints ];
+
+ // Get/set each input point.
+ // We unfortunately have to special case each direct input code.
+ for( int i = 0; i < m_numInputPoints; i++ )
+ {
+ m_InputToDICode[ i ] = Input::INVALID_CONTROLLERID;
+
+ const char *type = m_radController->GetInputPointByIndex( i )->GetType();
+
+ if( strcmp( type, "XAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_X;
+ }
+ else if( strcmp( type, "YAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_Y;
+ }
+ else if( strcmp( type, "ZAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_Z;
+ }
+ else if( strcmp( type, "RxAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_RX;
+ }
+ else if( strcmp( type, "RyAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_RY;
+ }
+ else if( strcmp( type, "RzAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_RZ;
+ }
+ else if( strcmp( type, "Slider" ) == 0 )
+ {
+ // figure out which slider it is.
+ for( int j = 0; j < 3; j++ )
+ {
+ if( m_radController->GetInputPointByTypeAndIndex( "Slider", j ) ==
+ m_radController->GetInputPointByIndex( i ) )
+ {
+ m_InputToDICode[ i ] = DIJOFS_SLIDER(j);
+ break;
+ }
+ }
+ }
+ else if( strcmp( type, "POV" ) == 0 )
+ {
+ // figure out which pov it is.
+ for( int j = 0; j < 3; j++ )
+ {
+ if( m_radController->GetInputPointByTypeAndIndex( "POV", j ) ==
+ m_radController->GetInputPointByIndex( i ) )
+ {
+ m_InputToDICode[ i ] = DIJOFS_POV(j);
+ break;
+ }
+ }
+ }
+ else if( strcmp( type, "Button" ) == 0 )
+ {
+ // figure out which button it is
+ for( int j = 0; j < 32; j++ )
+ {
+ if( m_radController->GetInputPointByTypeAndIndex( "Button", j ) ==
+ m_radController->GetInputPointByIndex( i ) )
+ {
+ m_InputToDICode[ i ] = DIJOFS_BUTTON(j);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+//==============================================================================
+// Gamepad::GetButtonEnum
+//==============================================================================
+//
+// Description: Returns the gamepad button enum for the given input, or
+// NUM_GAMEPAD_BUTTONS if it's not a valid gamepad input.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+eGamepadButton Gamepad::GetButtonEnum( int dxKey ) const
+{
+ switch( dxKey )
+ {
+ case DIJOFS_X:
+ {
+ return GAMEPAD_X;
+ }
+ case DIJOFS_Y:
+ {
+ return GAMEPAD_Y;
+ }
+ case DIJOFS_Z:
+ {
+ return GAMEPAD_Z;
+ }
+ case DIJOFS_RX:
+ {
+ return GAMEPAD_RX;
+ }
+ case DIJOFS_RY:
+ {
+ return GAMEPAD_RY;
+ }
+ case DIJOFS_RZ:
+ {
+ return GAMEPAD_RZ;
+ }
+ case DIJOFS_SLIDER(0):
+ {
+ return GAMEPAD_SLIDER0;
+ }
+ case DIJOFS_SLIDER(1):
+ {
+ return GAMEPAD_SLIDER1;
+ }
+ case DIJOFS_POV(0):
+ {
+ return GAMEPAD_POV0;
+ }
+ case DIJOFS_POV(1):
+ {
+ return GAMEPAD_POV1;
+ }
+ case DIJOFS_BUTTON0:
+ {
+ return GAMEPAD_BUTTON0;
+ }
+ case DIJOFS_BUTTON1:
+ {
+ return GAMEPAD_BUTTON1;
+ }
+ case DIJOFS_BUTTON2:
+ {
+ return GAMEPAD_BUTTON2;
+ }
+ case DIJOFS_BUTTON3:
+ {
+ return GAMEPAD_BUTTON3;
+ }
+ case DIJOFS_BUTTON4:
+ {
+ return GAMEPAD_BUTTON4;
+ }
+ case DIJOFS_BUTTON5:
+ {
+ return GAMEPAD_BUTTON5;
+ }
+ case DIJOFS_BUTTON6:
+ {
+ return GAMEPAD_BUTTON6;
+ }
+ case DIJOFS_BUTTON7:
+ {
+ return GAMEPAD_BUTTON7;
+ }
+ case DIJOFS_BUTTON8:
+ {
+ return GAMEPAD_BUTTON8;
+ }
+ case DIJOFS_BUTTON9:
+ {
+ return GAMEPAD_BUTTON9;
+ }
+ case DIJOFS_BUTTON10:
+ {
+ return GAMEPAD_BUTTON10;
+ }
+ case DIJOFS_BUTTON11:
+ {
+ return GAMEPAD_BUTTON11;
+ }
+ case DIJOFS_BUTTON12:
+ {
+ return GAMEPAD_BUTTON12;
+ }
+ case DIJOFS_BUTTON13:
+ {
+ return GAMEPAD_BUTTON13;
+ }
+ case DIJOFS_BUTTON14:
+ {
+ return GAMEPAD_BUTTON14;
+ }
+ case DIJOFS_BUTTON15:
+ {
+ return GAMEPAD_BUTTON15;
+ }
+ case DIJOFS_BUTTON16:
+ {
+ return GAMEPAD_BUTTON16;
+ }
+ case DIJOFS_BUTTON17:
+ {
+ return GAMEPAD_BUTTON17;
+ }
+ case DIJOFS_BUTTON18:
+ {
+ return GAMEPAD_BUTTON18;
+ }
+ case DIJOFS_BUTTON19:
+ {
+ return GAMEPAD_BUTTON19;
+ }
+ case DIJOFS_BUTTON20:
+ {
+ return GAMEPAD_BUTTON20;
+ }
+ case DIJOFS_BUTTON21:
+ {
+ return GAMEPAD_BUTTON21;
+ }
+ case DIJOFS_BUTTON22:
+ {
+ return GAMEPAD_BUTTON22;
+ }
+ case DIJOFS_BUTTON23:
+ {
+ return GAMEPAD_BUTTON23;
+ }
+ case DIJOFS_BUTTON24:
+ {
+ return GAMEPAD_BUTTON24;
+ }
+ case DIJOFS_BUTTON25:
+ {
+ return GAMEPAD_BUTTON25;
+ }
+ case DIJOFS_BUTTON26:
+ {
+ return GAMEPAD_BUTTON26;
+ }
+ case DIJOFS_BUTTON27:
+ {
+ return GAMEPAD_BUTTON27;
+ }
+ case DIJOFS_BUTTON28:
+ {
+ return GAMEPAD_BUTTON28;
+ }
+ case DIJOFS_BUTTON29:
+ {
+ return GAMEPAD_BUTTON29;
+ }
+ case DIJOFS_BUTTON30:
+ {
+ return GAMEPAD_BUTTON30;
+ }
+ case DIJOFS_BUTTON31:
+ {
+ return GAMEPAD_BUTTON31;
+ }
+ default:
+ {
+ return NUM_GAMEPAD_BUTTONS;
+ }
+ };
+} \ No newline at end of file
diff --git a/game/code/input/Gamepad.h b/game/code/input/Gamepad.h
new file mode 100644
index 0000000..4e972de
--- /dev/null
+++ b/game/code/input/Gamepad.h
@@ -0,0 +1,107 @@
+//**********************************************************
+// C++ Class Name : Gamepad
+// ---------------------------------------------------------
+// Filetype: (HEADER)
+// Filepath: D:/simpsonsrr2/game/code/input/Gamepad.h
+//
+//
+// GDPro Properties
+// ---------------------------------------------------
+// - GD Symbol Type : CLD_Class
+// - GD Method : UML ( 5.0 )
+// - GD System Name : Simpsons Controller System
+// - GD Diagram Type : Class Diagram
+// - GD Diagram Name : Input Device
+// ---------------------------------------------------
+// Author : nharan
+// Creation Date : Tues - May 20, 2003
+//
+// Change Log :
+//
+//**********************************************************
+#ifndef GAMEPAD_H
+#define GAMEPAD_H
+
+#include <input/RealController.h>
+
+// We need the eGamepadButtons enum to provide a sequential equivalent to DIJOFS_X...
+// so that we can use them to access our arrays.
+enum eGamepadButton
+{
+ GAMEPAD_X,
+ GAMEPAD_Y,
+ GAMEPAD_Z,
+ GAMEPAD_RX,
+ GAMEPAD_RY,
+ GAMEPAD_RZ,
+ GAMEPAD_SLIDER0,
+ GAMEPAD_SLIDER1,
+ GAMEPAD_POV0,
+ GAMEPAD_POV1,
+ GAMEPAD_BUTTON0,
+ GAMEPAD_BUTTON1,
+ GAMEPAD_BUTTON2,
+ GAMEPAD_BUTTON3,
+ GAMEPAD_BUTTON4,
+ GAMEPAD_BUTTON5,
+ GAMEPAD_BUTTON6,
+ GAMEPAD_BUTTON7,
+ GAMEPAD_BUTTON8,
+ GAMEPAD_BUTTON9,
+ GAMEPAD_BUTTON10,
+ GAMEPAD_BUTTON11,
+ GAMEPAD_BUTTON12,
+ GAMEPAD_BUTTON13,
+ GAMEPAD_BUTTON14,
+ GAMEPAD_BUTTON15,
+ GAMEPAD_BUTTON16,
+ GAMEPAD_BUTTON17,
+ GAMEPAD_BUTTON18,
+ GAMEPAD_BUTTON19,
+ GAMEPAD_BUTTON20,
+ GAMEPAD_BUTTON21,
+ GAMEPAD_BUTTON22,
+ GAMEPAD_BUTTON23,
+ GAMEPAD_BUTTON24,
+ GAMEPAD_BUTTON25,
+ GAMEPAD_BUTTON26,
+ GAMEPAD_BUTTON27,
+ GAMEPAD_BUTTON28,
+ GAMEPAD_BUTTON29,
+ GAMEPAD_BUTTON30,
+ GAMEPAD_BUTTON31,
+ NUM_GAMEPAD_BUTTONS
+};
+
+class Gamepad : public RealController
+{
+public:
+ Gamepad () ;
+ virtual ~Gamepad();
+
+ virtual bool IsInputAxis( int dxKey ) const;
+ virtual bool IsPovHat( int dxKey ) const;
+
+ // Returns true if this is a valid input for the controller.
+ virtual bool IsValidInput( int dxKey ) const;
+
+ void CalculatePOV( float povVal, float* up, float* down, float* right, float* left ) const;
+
+ // Sets up a mapping from a dxkey/direction to a virtual button
+ virtual bool SetMap( int dxKey, eDirectionType dir, int virtualButton );
+ // Retrieves the virtual button of the given type mapped to a dxKey, direction
+ virtual int GetMap( int dxKey, eDirectionType dir, eMapType map ) const;
+ // Clears the specified mapping so it no longer exists.
+ virtual void ClearMap( int dxKey, eDirectionType dir, int virtualButton );
+ // Clears all the cached mappings.
+ virtual void ClearMappedButtons();
+
+private:
+ virtual void MapInputToDICode();
+
+ eGamepadButton GetButtonEnum( int dxKey ) const;
+
+private:
+ int m_ButtonMap[ NUM_MAPTYPES ][ NUM_GAMEPAD_BUTTONS ][ NUM_DIRTYPES ];
+};
+#endif
diff --git a/game/code/input/Keyboard.cpp b/game/code/input/Keyboard.cpp
new file mode 100644
index 0000000..f366e57
--- /dev/null
+++ b/game/code/input/Keyboard.cpp
@@ -0,0 +1,215 @@
+#include <input/Keyboard.h>
+
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+
+//--------------------------------------------------------
+// Constructor/Destructor
+//--------------------------------------------------------
+
+Keyboard::Keyboard()
+: RealController( KEYBOARD )
+{
+ ClearMappedButtons();
+}
+
+Keyboard::~Keyboard()
+{
+}
+
+//==============================================================================
+// Keyboard::IsValidInput
+//==============================================================================
+//
+// Description: Returns true if this is a valid input for the controller.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if key it's a valid input
+//
+//==============================================================================
+
+bool Keyboard::IsValidInput( int dxKey ) const
+{
+ return dxKey >= DIK_ESCAPE && dxKey <= DIK_MEDIASELECT;
+}
+
+//==============================================================================
+// Keyboard::IsBannedInput
+//==============================================================================
+//
+// Description: Returns true if the key is banned for mapping.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if key is banned for mapping
+//
+//==============================================================================
+
+bool Keyboard::IsBannedInput( int dxKey ) const
+{
+ switch( dxKey )
+ {
+ case DIK_LMENU:
+ case DIK_RMENU:
+ case DIK_LWIN:
+ case DIK_RWIN:
+ return true;
+ default:
+ return false;
+ }
+}
+
+//==============================================================================
+// Keyboard::SetMap
+//==============================================================================
+//
+// Description: Sets up a mapping from a dxkey/direction to a virtual button
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - for keyboard this must be DIR_UP
+// virtualbutton - the virtual button that has been mapped
+//
+// Return: true if mapped successfully
+//
+//==============================================================================
+
+bool Keyboard::SetMap( int dxKey, eDirectionType dir, int virtualButton )
+{
+ rAssert( dxKey >= 0 && dxKey < NUM_KEYBOARD_BUTTONS );
+ rAssert( virtualButton >= 0 && virtualButton < Input::MaxPhysicalButtons );
+ rAssert( dir == DIR_UP );
+
+ eMapType maptype = VirtualInputs::GetType( virtualButton );
+
+ if( dxKey >= 0 && dxKey < NUM_KEYBOARD_BUTTONS )
+ {
+ m_ButtonMap[ maptype ][ dxKey ] = virtualButton;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+//==============================================================================
+// Keyboard::ClearMap
+//==============================================================================
+//
+// Description: Clears the specified mapping so it no longer exists.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Keyboard::ClearMap( int dxKey, eDirectionType dir, int virtualButton )
+{
+ rAssert( dxKey >= 0 && dxKey < NUM_KEYBOARD_BUTTONS );
+ rAssert( virtualButton >= 0 && virtualButton < Input::MaxPhysicalButtons );
+ rAssert( dir == DIR_UP );
+
+ eMapType maptype = VirtualInputs::GetType( virtualButton );
+
+ if( dxKey >= 0 && dxKey < NUM_KEYBOARD_BUTTONS )
+ {
+ m_ButtonMap[ maptype ][ dxKey ] = Input::INVALID_CONTROLLERID;
+ }
+}
+
+//==============================================================================
+// Keyboard::GetMap
+//==============================================================================
+//
+// Description: Retrieves the virtual button of the given type mapped to
+// a dxKey, direction
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - for keyboard only DIR_UP is used.
+// maptype - the type of virtual button to look up
+//
+// Return: n/a
+//
+//==============================================================================
+
+int Keyboard::GetMap( int dxKey, eDirectionType dir, eMapType map ) const
+{
+ rAssert( dxKey >= 0 && dxKey < NUM_KEYBOARD_BUTTONS );
+
+ if( dir == DIR_UP )
+ {
+ return m_ButtonMap[ map ][ dxKey ];
+ }
+ else
+ {
+ return Input::INVALID_CONTROLLERID;
+ }
+}
+
+//==============================================================================
+// Keyboard::ClearMappedButtons
+//==============================================================================
+//
+// Description: Clears the cached button mappings
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Keyboard::ClearMappedButtons()
+{
+ memset( &m_ButtonMap, Input::INVALID_CONTROLLERID, sizeof( m_ButtonMap ) );
+}
+
+//==============================================================================
+// Keyboard::MapInputToDICode
+//==============================================================================
+//
+// Description: Creates an input point -> direct input virtual key code mapping
+// for the keyboard
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Keyboard::MapInputToDICode()
+{
+ if( m_InputToDICode != NULL )
+ {
+ delete [] m_InputToDICode;
+ m_InputToDICode = NULL;
+ }
+
+ if( m_radController != NULL )
+ {
+ // Get the number of input points
+ m_numInputPoints = m_radController->GetNumberOfInputPoints();
+
+ // Set up a cleared index -> di map.
+ m_InputToDICode = new int[ m_numInputPoints ];
+
+ for( int i = 0; i < m_numInputPoints; i++ )
+ {
+ m_InputToDICode[ i ] = Input::INVALID_CONTROLLERID;
+ }
+
+ // Reverse the di -> index map into what we want.
+ for( int di = DIK_ESCAPE; di <= NUM_KEYBOARD_BUTTONS; di++ )
+ {
+ int inputpoint = VirtualKeyToIndex[ di ];
+
+ if( inputpoint >= 0 &&
+ inputpoint < m_numInputPoints )
+ {
+ m_InputToDICode[ inputpoint ] = di;
+ }
+ }
+ }
+}
diff --git a/game/code/input/Keyboard.h b/game/code/input/Keyboard.h
new file mode 100644
index 0000000..0600dfb
--- /dev/null
+++ b/game/code/input/Keyboard.h
@@ -0,0 +1,58 @@
+//**********************************************************
+// C++ Class Name : Keyboard
+// ---------------------------------------------------------
+// Filetype: (HEADER)
+// Filepath: D:/simpsonsrr2/game/code/input/Keyboard.h
+//
+//
+// GDPro Properties
+// ---------------------------------------------------
+// - GD Symbol Type : CLD_Class
+// - GD Method : UML ( 5.0 )
+// - GD System Name : Simpsons Controller System
+// - GD Diagram Type : Class Diagram
+// - GD Diagram Name : Input Device
+// ---------------------------------------------------
+// Author : nharan
+// Creation Date : Tues - May 20, 2003
+//
+// Change Log :
+//
+//**********************************************************
+#ifndef KEYBOARD_H
+#define KEYBOARD_H
+
+#include <input/RealController.h>
+
+const NUM_KEYBOARD_BUTTONS = 256;
+
+class Keyboard : public RealController
+{
+public:
+ Keyboard();
+ virtual ~Keyboard();
+
+ // Returns true if this is a valid input for the controller.
+ virtual bool IsValidInput( int dxKey ) const;
+
+ // Returns true if the key is banned for mapping.
+ virtual bool IsBannedInput( int dxKey ) const;
+
+ // Sets up a mapping from a dxkey/direction to a virtual button
+ virtual bool SetMap( int dxKey, eDirectionType dir, int virtualButton );
+ // Retrieves the virtual button of the given type mapped to a dxKey, direction
+ virtual int GetMap( int dxKey, eDirectionType dir, eMapType map ) const;
+ // Clears the specified mapping so it no longer exists.
+ virtual void ClearMap( int dxKey, eDirectionType dir, int virtualButton );
+ // Clears all the cached mappings.
+ virtual void ClearMappedButtons();
+
+private:
+ // Sets up a input point index -> direct input keycode map.
+ virtual void MapInputToDICode();
+
+private:
+ int m_ButtonMap[ NUM_MAPTYPES ][ NUM_KEYBOARD_BUTTONS ];
+};
+
+#endif
diff --git a/game/code/input/Mouse.cpp b/game/code/input/Mouse.cpp
new file mode 100644
index 0000000..454f111
--- /dev/null
+++ b/game/code/input/Mouse.cpp
@@ -0,0 +1,399 @@
+#include <input/Mouse.h>
+
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+
+/******************************************************************************
+ Construction/Destruction
+ *****************************************************************************/
+
+Mouse::Mouse()
+: RealController(MOUSE),
+ m_fSensitivityX(10.0f),
+ m_fSensitivityY(10.0f)
+{
+ memset( &m_absPosition, 0, sizeof( m_absPosition ) );
+ memset( &m_relPosition, 0, sizeof( m_relPosition ) );
+
+ ClearMappedButtons();
+}
+
+Mouse::~Mouse()
+{
+}
+
+//==============================================================================
+// Mouse::IsMouseAxis
+//==============================================================================
+//
+// Description: Identifies any keys that are input axes. (can go in two directions)
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if input is an axis
+//
+//==============================================================================
+
+bool Mouse::IsMouseAxis( int dxKey ) const
+{
+ switch( dxKey )
+ {
+ case DIMOFS_X:
+ case DIMOFS_Y:
+ case DIMOFS_Z:
+ return true;
+ default:
+ return false;
+ }
+}
+
+//==============================================================================
+// Mouse::IsValidInput
+//==============================================================================
+//
+// Description: Returns true if this is a valid input for the controller.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if key it's a valid input
+//
+//==============================================================================
+
+bool Mouse::IsValidInput( int dxKey ) const
+{
+ return GetButtonEnum( dxKey ) != NUM_MOUSE_BUTTONS;
+}
+
+//==============================================================================
+// Mouse::CalculateMouseAxis
+//==============================================================================
+//
+// Description: Calculates a direct input axis value for a mouse axis based on
+// its current mouse value. Mouse values can cover -1000..1000,
+// and this gets mapped to an axis value of -1..1.
+//
+// Parameters: mouseAxis - the axis that's changed.
+// value - the value of the mouse axis
+//
+// Return: input point value of -1..1
+//
+//==============================================================================
+
+float Mouse::CalculateMouseAxis( int mouseAxis, float value )
+{
+ const float MAX_THRESH = 1.0f;
+ const float MIN_THRESH = -1.0f;
+
+ if( value == 0.0f )
+ {
+ return 0.0f;
+ }
+
+ switch( mouseAxis )
+ {
+ case DIMOFS_X:
+ {
+ //take the delta
+ m_relPosition.x = (value - m_absPosition.x) / m_fSensitivityX;
+
+ //re-center the cursor pos.
+ m_absPosition.x = value;
+
+ if( m_relPosition.x > MAX_THRESH ) m_relPosition.x = MAX_THRESH;
+ else if( m_relPosition.x < MIN_THRESH ) m_relPosition.x = MIN_THRESH;
+
+ return m_relPosition.x;
+ }
+ break;
+ case DIMOFS_Y:
+ case DIMOFS_Z:
+ {
+ //take the delta
+ m_relPosition.y = (value - m_absPosition.y) / m_fSensitivityY;
+
+ //re-center the cursor pos.
+ m_absPosition.y = value;
+
+ if( m_relPosition.y > MAX_THRESH ) m_relPosition.y = MAX_THRESH;
+ else if( m_relPosition.y < MIN_THRESH ) m_relPosition.y = MIN_THRESH;
+
+ return -m_relPosition.y;
+ }
+ break;
+ default:
+ break;
+ }
+ return 0.0f;
+}
+
+//==============================================================================
+// Mouse::SetMap
+//==============================================================================
+//
+// Description: Sets up a mapping from a dxkey/direction to a virtual button
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input
+// virtualbutton - the virtual button that has been mapped
+//
+// Return: true if button was mapped successfully
+//
+//==============================================================================
+
+bool Mouse::SetMap( int dxKey, eDirectionType dir, int virtualButton )
+{
+ rAssert( virtualButton >= 0 && virtualButton < Input::MaxPhysicalButtons );
+
+ eMouseButton mbutton = GetButtonEnum( dxKey );
+ eMapType maptype = VirtualInputs::GetType( virtualButton );
+
+ if( mbutton != NUM_MOUSE_BUTTONS )
+ {
+ m_ButtonMap[ maptype ][ mbutton ][ dir ] = virtualButton;
+ }
+
+ return mbutton != NUM_MOUSE_BUTTONS;
+}
+
+//==============================================================================
+// Mouse::ClearMap
+//==============================================================================
+//
+// Description: Clears the specified mapping so it no longer exists.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Mouse::ClearMap( int dxKey, eDirectionType dir, int virtualButton )
+{
+ eMouseButton mbutton = GetButtonEnum( dxKey );
+ eMapType maptype = VirtualInputs::GetType( virtualButton );
+
+ if( mbutton != NUM_MOUSE_BUTTONS )
+ {
+ m_ButtonMap[ maptype ][ mbutton ][ dir ] = Input::INVALID_CONTROLLERID;
+ }
+}
+
+//==============================================================================
+// Mouse::GetMap
+//==============================================================================
+//
+// Description: Retrieves the virtual button of the given type mapped to
+// a dxKey, direction
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+// direction - the direction of the input.
+// maptype - the type of virtual button to look up
+//
+// Return: n/a
+//
+//==============================================================================
+
+int Mouse::GetMap( int dxKey, eDirectionType dir, eMapType map ) const
+{
+ eMouseButton mbutton = GetButtonEnum( dxKey );
+
+ if( mbutton != NUM_MOUSE_BUTTONS )
+ {
+ return m_ButtonMap[ map ][ mbutton ][ dir ];
+ }
+ else
+ {
+ return Input::INVALID_CONTROLLERID;
+ }
+}
+
+//==============================================================================
+// Mouse::ClearMappedButtons
+//==============================================================================
+//
+// Description: Clears the cached button mappings
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Mouse::ClearMappedButtons()
+{
+ memset( &m_ButtonMap, Input::INVALID_CONTROLLERID, sizeof( m_ButtonMap ) );
+}
+
+//==============================================================================
+// Mouse::MapInputToDICode
+//==============================================================================
+//
+// Description: Creates an input point -> direct input virtual key code mapping
+// for the mouse
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Mouse::MapInputToDICode()
+{
+ if( m_InputToDICode != NULL )
+ {
+ delete [] m_InputToDICode;
+ m_InputToDICode = NULL;
+ }
+
+ if( m_radController != NULL )
+ {
+ // Get the number of input points
+ m_numInputPoints = m_radController->GetNumberOfInputPoints();
+
+ // Set up a cleared index -> di map.
+ m_InputToDICode = new int[ m_numInputPoints ];
+
+ // Get/set each input point.
+ // We unfortunately have to special case each direct input code.
+ for( int i = 0; i < m_numInputPoints; i++ )
+ {
+ m_InputToDICode[ i ] = Input::INVALID_CONTROLLERID;
+
+ const char *type = m_radController->GetInputPointByIndex( i )->GetType();
+ const char *name = m_radController->GetInputPointByIndex( i )->GetName();
+
+ if( strcmp( type, "RelXAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIMOFS_X;
+ }
+ else if( strcmp( type, "RelYAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIMOFS_Y;
+ }
+ else if( strcmp( type, "RelZAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIMOFS_Z;
+ }
+ else if( strcmp( type, "Button" ) == 0 )
+ {
+ int button;
+
+ if( sscanf( name, "Button %d", &button ) == 1 )
+ {
+ switch( button )
+ {
+ case 0:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON0;
+ break;
+ }
+ case 1:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON1;
+ break;
+ }
+ case 2:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON2;
+ break;
+ }
+ case 3:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON3;
+ break;
+ }
+ case 4:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON4;
+ break;
+ }
+ case 5:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON5;
+ break;
+ }
+ case 6:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON6;
+ break;
+ }
+ case 7:
+ {
+ m_InputToDICode[ i ] = DIMOFS_BUTTON7;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+//==============================================================================
+// Mouse::GetButtonEnum
+//==============================================================================
+//
+// Description: Returns the mouse button enum for the given input, or
+// NUM_MOUSE_BUTTONS if it's not a valid mouse input.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+eMouseButton Mouse::GetButtonEnum( int dxKey ) const
+{
+ switch( dxKey )
+ {
+ case DIMOFS_X:
+ {
+ return MOUSE_X;
+ }
+ case DIMOFS_Y:
+ {
+ return MOUSE_Y;
+ }
+ case DIMOFS_Z:
+ {
+ return MOUSE_Z;
+ }
+ case DIMOFS_BUTTON0:
+ {
+ return MOUSE_BUTTON0;
+ }
+ case DIMOFS_BUTTON1:
+ {
+ return MOUSE_BUTTON1;
+ }
+ case DIMOFS_BUTTON2:
+ {
+ return MOUSE_BUTTON2;
+ }
+ case DIMOFS_BUTTON3:
+ {
+ return MOUSE_BUTTON3;
+ }
+ case DIMOFS_BUTTON4:
+ {
+ return MOUSE_BUTTON4;
+ }
+ case DIMOFS_BUTTON5:
+ {
+ return MOUSE_BUTTON5;
+ }
+ case DIMOFS_BUTTON6:
+ {
+ return MOUSE_BUTTON6;
+ }
+ case DIMOFS_BUTTON7:
+ {
+ return MOUSE_BUTTON7;
+ }
+ default:
+ {
+ return NUM_MOUSE_BUTTONS;
+ }
+ }
+}
diff --git a/game/code/input/Mouse.h b/game/code/input/Mouse.h
new file mode 100644
index 0000000..7f33402
--- /dev/null
+++ b/game/code/input/Mouse.h
@@ -0,0 +1,80 @@
+/******************************************************************************
+ File: Mouse.h
+ Desc: Interface for the Mouse class.
+ - Responsible for managing relative axis info
+ for the mouse and other mouse related stuff.
+
+ Date: May 16, 2003
+ History:
+ *****************************************************************************/
+#ifndef MOUSE_H
+#define MOUSE_H
+
+#include <input/RealController.h>
+
+struct MouseCoord
+{
+ float x, y;
+};
+
+// We need the eMouseButtons enum to provide a sequential equivalent to DIMOFS_X...
+// so that we can use them to access our arrays.
+enum eMouseButton
+{
+ MOUSE_X,
+ MOUSE_Y,
+ MOUSE_Z,
+ MOUSE_BUTTON0,
+ MOUSE_BUTTON1,
+ MOUSE_BUTTON2,
+ MOUSE_BUTTON3,
+ MOUSE_BUTTON4,
+ MOUSE_BUTTON5,
+ MOUSE_BUTTON6,
+ MOUSE_BUTTON7,
+ NUM_MOUSE_BUTTONS
+};
+
+class Mouse : public RealController
+{
+public:
+ Mouse();
+ virtual ~Mouse();
+
+ float XPos() const { return m_relPosition.x; }
+ float YPos() const { return m_relPosition.y; }
+ void setSensitivityX( float fSensitivityX ) { m_fSensitivityX = fSensitivityX; }
+ void setSensitivityY( float fSensitivityY ) { m_fSensitivityY = fSensitivityY; }
+ float getSensitivityX() const { return m_fSensitivityX; }
+ float getSensitivityY() const { return m_fSensitivityY; }
+
+ virtual bool IsMouseAxis( int dxKey ) const;
+
+ // Returns true if this is a valid input for the controller.
+ virtual bool IsValidInput( int dxKey ) const;
+
+ float CalculateMouseAxis( int mouseAxis, float value );
+
+ // Sets up a mapping from a dxkey/direction to a virtual button
+ virtual bool SetMap( int dxKey, eDirectionType dir, int virtualButton );
+ // Retrieves the virtual button of the given type mapped to a dxKey, direction
+ virtual int GetMap( int dxKey, eDirectionType dir, eMapType map ) const;
+ // Clears the specified mapping so it no longer exists.
+ virtual void ClearMap( int dxKey, eDirectionType dir, int virtualButton );
+ // Clears all the cached mappings.
+ virtual void ClearMappedButtons();
+
+
+private:
+ virtual void MapInputToDICode();
+
+ eMouseButton GetButtonEnum( int dxKey ) const;
+
+private:
+ MouseCoord m_relPosition;
+ MouseCoord m_absPosition;
+ float m_fSensitivityX;
+ float m_fSensitivityY;
+ int m_ButtonMap[ NUM_MAPTYPES ][ NUM_MOUSE_BUTTONS ][ NUM_DIRTYPES ];
+};
+#endif
diff --git a/game/code/input/MouseCursor.cpp b/game/code/input/MouseCursor.cpp
new file mode 100644
index 0000000..bcedf16
--- /dev/null
+++ b/game/code/input/MouseCursor.cpp
@@ -0,0 +1,149 @@
+#include <input/MouseCursor.h>
+
+// THIS CURSOR STUFF IS JUST FOR TESTING!.
+static void StreamLine(pddiPrimStream* pstream, rmt::Vector a, rmt::Vector b, tColour colora, tColour colorb)
+{
+ pddiVector pa;
+ pddiVector pb;
+
+ pa.x = a.x;
+ pa.y = a.y;
+ pa.z = a.z;
+
+ pb.x = b.x;
+ pb.y = b.y;
+ pb.z = b.z;
+
+ pstream->Vertex(&pa, colora);
+ pstream->Vertex(&pb, colorb);
+}
+static void StreamLine(pddiPrimStream* pstream, rmt::Vector a, rmt::Vector b, tColour color)
+{
+ StreamLine(pstream, a, b, color, color);
+}
+
+static pddiShader* simpleShader = NULL;
+
+static void DrawSquare(rmt::Vector v1, rmt::Vector v2, rmt::Vector v3, rmt::Vector v4,
+ tColour color = tColour(255, 255, 255),
+ pddiPrimType primType = PDDI_PRIM_LINES)
+{
+ if(simpleShader == NULL)
+ {
+ simpleShader = p3d::device->NewShader("simple");
+ simpleShader->AddRef();
+ simpleShader->SetInt(PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA);
+ }
+
+ pddiPrimStream* pStream;
+ pStream = p3d::pddi->BeginPrims(simpleShader, primType, PDDI_V_C, 8);
+ if(pStream)
+ {
+ StreamLine(pStream, v1, v2, color);
+ StreamLine(pStream, v2, v4, color);
+ StreamLine(pStream, v4, v3, color);
+ StreamLine(pStream, v3, v1, color);
+ }
+ p3d::pddi->EndPrims(pStream);
+}
+
+/*
+* DrawSquare:
+* the square is defined as:
+*
+* v1 ---------+
+* | |
+* | |
+* | |
+* | |
+* +--------- v2
+*
+*/
+static void DrawSquare(rmt::Vector v1, rmt::Vector v2,
+ tColour color = tColour(255, 255, 255),
+ pddiPrimType primType = PDDI_PRIM_LINES)
+{
+ DrawSquare(
+ rmt::Vector(v1.x, v1.y, 0),
+ rmt::Vector(v2.x, v1.y, 0),
+ rmt::Vector(v1.x, v2.y, 0),
+ rmt::Vector(v2.x, v2.y, 0),
+ color, primType);
+}
+
+/******************************************************************************
+ Some global constants/defines for this file.
+ *****************************************************************************/
+
+
+/******************************************************************************
+ Initialization of static member variables.
+ *****************************************************************************/
+
+/******************************************************************************
+ Construction/Destruction
+ *****************************************************************************/
+
+MouseCursor::MouseCursor()
+: m_bVisible(false),
+ m_position(),
+ m_pCursorIcon(NULL),
+ m_width(10),
+ m_height(10)
+{
+}
+
+MouseCursor::~MouseCursor()
+{
+ tRefCounted::Release( m_pCursorIcon );
+}
+
+void MouseCursor::Set( tDrawable* pCursorIcon )
+{
+ if( !m_pCursorIcon )
+ tRefCounted::Assign(m_pCursorIcon, pCursorIcon);
+}
+
+void MouseCursor::Render()
+{
+ if( !m_bVisible ) return;
+
+ rmt::Vector v1(-0.1f, -0.1f, 0.0f);
+ rmt::Vector v2(-0.1f, 0.1f, 0.0f);
+ rmt::Vector v3(0.1f, 0.1f, 0.0f);
+ rmt::Vector v4(0.1f, -0.1f, 0.0f);
+
+ pddiProjectionMode pMode = p3d::pddi->GetProjectionMode();
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_ORTHOGRAPHIC);
+
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+
+ // Offsets are hardcoded right now for the type of cursor that it is.
+ static float XOFFSET = 0.020f;
+ static float YOFFSET = 0.035f;
+ p3d::stack->Translate( m_position.x-XOFFSET, m_position.y-YOFFSET, m_position.z );
+
+ if (m_pCursorIcon)
+ {
+ static float scaleFactor = 0.5f;
+ p3d::stack->Scale(scaleFactor, scaleFactor, 1.0f);
+ //p3d::stack->Scale(0.0007f, 0.0007f, 0.0007f);
+ m_pCursorIcon->Display();
+ }
+ else
+ {
+ float scale = 0.2f;
+ p3d::stack->Scale(scale, scale, scale);
+ pddiColour c = pddiColour(255, 0, 0); // 0-255
+ //This should be replaced with a DrawCursor function.
+ DrawSquare(v2, v3, v1, v4, c);
+ }
+
+ p3d::pddi->Clear(PDDI_BUFFER_DEPTH | PDDI_BUFFER_STENCIL);
+
+ p3d::stack->Pop();
+
+ //Restore things we've messed with
+ p3d::pddi->SetProjectionMode(pMode);
+} \ No newline at end of file
diff --git a/game/code/input/MouseCursor.h b/game/code/input/MouseCursor.h
new file mode 100644
index 0000000..43e2119
--- /dev/null
+++ b/game/code/input/MouseCursor.h
@@ -0,0 +1,57 @@
+/******************************************************************************
+ File: MouseCursor.h
+ Desc: Interface for the Mouse cursor.
+ - Responsible for drawing the 3D/2D cursor.
+
+ Date: May 16, 2003
+ History:
+ *****************************************************************************/
+#ifndef MOUSECURSOR_H
+#define MOUSECURSOR_H
+
+class MouseCursor
+{
+public:
+ MouseCursor();
+ ~MouseCursor();
+
+ void SetPos( float x, float y, float z = 10.0f )
+ {
+ m_position.x = x;
+ m_position.y = y;
+ m_position.z = z;
+ }
+
+ void SetVisible( bool bVisible )
+ {
+ m_bVisible = bVisible;
+ }
+
+ void SetSize( int width, int height )
+ {
+ m_width = width;
+ m_height = height;
+ }
+
+ float XPos() const { return m_position.x; }
+ float YPos() const { return m_position.y; }
+ float ZPos() const { return m_position.z; }
+
+ bool IsVisible() const { return m_bVisible; }
+
+ int Width() const { return m_width; }
+ int Height() const { return m_height; }
+
+ void Set( tDrawable* pCursorIcon );
+ void Render();
+
+private:
+ tDrawable* m_pCursorIcon;
+ rmt::Vector m_position;
+ bool m_bVisible;
+ int m_width;
+ int m_height;
+};
+
+
+#endif
diff --git a/game/code/input/RealController.cpp b/game/code/input/RealController.cpp
new file mode 100644
index 0000000..e7bf1e4
--- /dev/null
+++ b/game/code/input/RealController.cpp
@@ -0,0 +1,210 @@
+/******************************************************************************
+ File: RealController.cpp
+ Desc: Defines the RealController class.
+ Author: Neil Haran
+ Date: May 16, 2003
+ History:
+ *****************************************************************************/
+#include <input/RealController.h>
+#include <input/usercontrollerWin32.h>
+#include <input/inputmanager.h>
+
+/******************************************************************************
+ Construction/Destruction
+ *****************************************************************************/
+RealController::RealController( eControllerType type )
+: m_radController(NULL),
+ m_eControllerType(type),
+ m_bConnected(false),
+ m_InputToDICode(NULL),
+ m_numInputPoints(0)
+{
+}
+
+RealController::~RealController()
+{
+ if( m_InputToDICode != NULL )
+ {
+ delete [] m_InputToDICode;
+ }
+}
+
+//--------------------------------------------------------
+// Init
+//--------------------------------------------------------
+void RealController::Init( IRadController* pRadController )
+{
+// rAssert(!m_radController);
+ if( !m_radController )
+ {
+ m_radController = pRadController;
+
+ MapInputToDICode();
+ }
+}
+
+void RealController::Release()
+{
+ if( m_radController )
+ m_radController = NULL;
+}
+
+//==============================================================================
+// RealController::IsInputAxis
+//==============================================================================
+//
+// Description: Identifies any keys that are input axes. (can go in two directions)
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if input is an axis
+//
+//==============================================================================
+
+bool RealController::IsInputAxis( int dxKey ) const
+{
+ return false;
+}
+
+//==============================================================================
+// RealController::IsMouseAxis
+//==============================================================================
+//
+// Description: Identifies any keys that are mouse axes. (special to mouse)
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if input is a mouse axis
+//
+//==============================================================================
+
+bool RealController::IsMouseAxis( int dxKey ) const
+{
+ return false;
+}
+
+//==============================================================================
+// RealController::IsPovHat
+//==============================================================================
+//
+// Description: Identifies any keys that are pov hats (go in 4 directions)
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if input is a pov hat
+//
+//==============================================================================
+
+bool RealController::IsPovHat( int dxKey ) const
+{
+ return false;
+}
+
+//==============================================================================
+// RealController::IsBannedInput
+//==============================================================================
+//
+// Description: Returns true if the key is banned for mapping.
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: true if key is banned for mapping
+//
+//==============================================================================
+
+bool RealController::IsBannedInput( int dxKey ) const
+{
+ return false;
+}
+
+//==============================================================================
+// RealController::GetInputName
+//==============================================================================
+//
+// Description: Returns the name of the input
+//
+// Parameters: dxkey - the directinput index identifying the physical input.
+//
+// Return: name
+//
+//==============================================================================
+
+const char* RealController::GetInputName( int dxKey ) const
+{
+ if( IsValidInput( dxKey ) )
+ {
+ for( int i = 0; i < m_numInputPoints; i++ )
+ {
+ if( GetDICode( i ) == dxKey )
+ {
+ IRadControllerInputPoint* pInputPoint = m_radController->GetInputPointByIndex( i );
+ rAssert( pInputPoint != NULL );
+
+ return pInputPoint->GetName();
+ }
+ }
+ }
+
+ rAssert( false );
+ return NULL;
+}
+
+void RealController::AddInputPoints( IRadControllerInputPoint* pInputPoint )
+{
+ rAssert(pInputPoint);
+
+ // Check if the input point is already in the list.
+ for( INPUTPOINTITER iter = m_inputPointList.begin(); iter != m_inputPointList.end(); iter++ )
+ {
+ if( *iter == pInputPoint )
+ {
+ return;
+ }
+ }
+
+ // Add the point.
+ m_inputPointList.push_back( pInputPoint );
+}
+
+void RealController::ReleaseInputPoints( UserController* parent )
+{
+ INPUTPOINTITER iter;
+
+ for( iter = m_inputPointList.begin(); iter != m_inputPointList.end(); iter++ )
+ {
+ IRadControllerInputPoint* pInputPoint = *iter;
+ if( pInputPoint )
+ {
+ pInputPoint->UnRegisterControllerInputPointCallback( static_cast< IRadControllerInputPointCallback* >( parent ) );
+ }
+ }
+ // after unregistering, we don't need this point anymore.
+ m_inputPointList.clear();
+}
+
+//==============================================================================
+// RealController::GetDICode
+//==============================================================================
+//
+// Description: Returns the direct input keycode associated with an input point,
+// or Input::INVALID_CONTROLLERID if there is no controller or input code.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+int RealController::GetDICode( int inputpoint ) const
+{
+ rAssert( inputpoint >= 0 && inputpoint < m_numInputPoints );
+
+ if( m_InputToDICode != NULL && inputpoint >= 0 && inputpoint < m_numInputPoints )
+ {
+ return m_InputToDICode[ inputpoint ];
+ }
+ else
+ {
+ return Input::INVALID_CONTROLLERID;
+ }
+}
diff --git a/game/code/input/RealController.h b/game/code/input/RealController.h
new file mode 100644
index 0000000..2377069
--- /dev/null
+++ b/game/code/input/RealController.h
@@ -0,0 +1,131 @@
+/******************************************************************************
+ File: RealController.h
+ Desc: Interface for the RealController class.
+ - RealController essentially is a physical controller class.
+ - a wrapper around IRadController.
+
+ Date: May 16, 2003
+ History:
+ *****************************************************************************/
+
+#ifndef REALCONTROLLER_H
+#define REALCONTROLLER_H
+
+//========================================
+// System Includes
+//========================================
+#include <radcontroller.hpp>
+#include <list>
+using namespace std;
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <input/controller.h>
+#include <input/mapper.h>
+#include <input/virtualinputs.hpp>
+
+class UserController;
+/*****************************************
+ Some typedefs for convienince
+ ****************************************/
+typedef ref< IRadController > RADCONTROLLER;
+typedef list< IRadControllerInputPoint* > RADINPUTPOINTLIST;
+typedef list< IRadControllerInputPoint* >::iterator INPUTPOINTITER;
+
+//=============================================================================
+// Enumerations
+//=============================================================================
+
+// Types of real controllers.
+enum eControllerType
+{
+ GAMEPAD = 0, // gamepad/joystick
+ KEYBOARD,
+ MOUSE,
+ STEERINGWHEEL,
+ NUM_CONTROLLERTYPES
+};
+
+// Directions which inputs can process.
+enum eDirectionType
+{
+ DIR_UP = 0,
+ DIR_DOWN,
+ DIR_RIGHT,
+ DIR_LEFT,
+ NUM_DIRTYPES
+};
+
+//=============================================================================
+// Class: RealController
+//=============================================================================
+//
+// Description: The base class for every device plugged in to receive input
+// from the user. There is one instance for each keyboard, mouse,
+// gamepad...
+//
+//=============================================================================
+
+class RealController
+{
+public:
+ RealController( eControllerType type );
+ virtual ~RealController();
+
+ RADCONTROLLER getController() const { return m_radController; }
+ bool IsConnected() const { return m_bConnected; }
+ void Connect() { m_bConnected = true; }
+ void Disconnect() { m_bConnected = false; }
+ eControllerType ControllerType() const { return m_eControllerType; }
+
+ void Init( IRadController* pController );
+ void Release();
+
+ // Return true if the dxKey is one of the following types of inputs.
+ virtual bool IsInputAxis( int dxKey ) const;
+ virtual bool IsMouseAxis( int dxKey ) const;
+ virtual bool IsPovHat( int dxKey ) const;
+
+ // Returns true if this is a valid input for the controller.
+ virtual bool IsValidInput( int dxKey ) const = 0;
+
+ // Returns true if the key is banned for mapping.
+ virtual bool IsBannedInput( int dxKey ) const;
+
+ // Sets up a mapping from a dxkey/direction to a virtual button
+ virtual bool SetMap( int dxKey, eDirectionType dir, int virtualButton ) = 0;
+ // Retrieves the virtual button of the given type mapped to a dxKey, direction
+ virtual int GetMap( int dxKey, eDirectionType dir, eMapType map ) const = 0;
+ // Clears the specified mapping so it no longer exists.
+ virtual void ClearMap( int dxKey, eDirectionType dir, int virtualButton ) = 0;
+ // Clears all the cached mappings.
+ virtual void ClearMappedButtons() = 0;
+
+ // Returns the name of the input.
+ const char* GetInputName( int dxKey ) const;
+
+ // Store & release registered input points.
+ void AddInputPoints( IRadControllerInputPoint* pInputPoint );
+ void ReleaseInputPoints( UserController* parent );
+
+ // Returns the direct input code representing a given input point for
+ // the controller, or Input::INVALID_CONTROLLERID if the i.p. doesn't exist.
+ int GetDICode( int inputpoint ) const;
+
+protected:
+ // Sets up an input point index -> direct input keycode map for the
+ // controller in m_InputToDICode. Called automatically by Init().
+ virtual void MapInputToDICode() = 0;
+
+protected:
+ RADCONTROLLER m_radController;
+ bool m_bConnected;
+ eControllerType m_eControllerType;
+ RADINPUTPOINTLIST m_inputPointList;
+ int* m_InputToDICode;
+ int m_numInputPoints;
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/input/SteeringWheel.cpp b/game/code/input/SteeringWheel.cpp
new file mode 100644
index 0000000..7504633
--- /dev/null
+++ b/game/code/input/SteeringWheel.cpp
@@ -0,0 +1,170 @@
+#include <input/SteeringWheel.h>
+
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+
+//--------------------------------------------------------
+// Constructor/Destructor
+//--------------------------------------------------------
+
+SteeringWheel::SteeringWheel()
+: RealController( STEERINGWHEEL ),
+ m_bPedalInverted(false),
+ m_InputToDICode( NULL )
+{
+}
+
+SteeringWheel::~SteeringWheel()
+{
+ if( m_InputToDICode != NULL )
+ {
+ delete [] m_InputToDICode;
+ }
+}
+
+//==============================================================================
+// SteeringWheel::Init
+//==============================================================================
+//
+// Description: Initializes the wheel and figures out what special type it is.
+//
+// Parameters: controller - the rad controller for the steering wheel
+//
+// Return: None.
+//
+//==============================================================================
+
+void SteeringWheel::Init( IRadController* pController )
+{
+ // Clear the properties
+ m_bPedalInverted = false;
+
+ if( pController != NULL )
+ {
+ // Check for the wingman - a bad bad wheel.
+ m_bPedalInverted = (_stricmp( pController->GetType(), "Logitech WingMan Formula Force GP USB") == 0);
+ }
+
+ // Connect
+ RealController::Init( pController );
+}
+
+//==============================================================================
+// SteeringWheel::MapInputToDICode
+//==============================================================================
+//
+// Description: Creates an input point -> direct input virtual key code mapping
+// for the keyboard
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void SteeringWheel::MapInputToDICode()
+{
+ if( m_InputToDICode != NULL )
+ {
+ delete [] m_InputToDICode;
+ m_InputToDICode = NULL;
+ }
+
+ if( m_radController != NULL )
+ {
+ // Get the number of input points
+ unsigned num = m_radController->GetNumberOfInputPoints();
+
+ // Set up a cleared index -> di map.
+ m_InputToDICode = new int[ num ];
+
+ for( unsigned i = 0; i < num; i++ )
+ {
+ m_InputToDICode[ i ] = Input::INVALID_CONTROLLERID;
+ const char *type = m_radController->GetInputPointByIndex( i )->GetType();
+
+ if( strcmp( type, "XAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_X;
+ }
+ else if( strcmp( type, "YAxis" ) == 0 )
+ {
+ m_InputToDICode[ i ] = DIJOFS_Y;
+ }
+ else if( strcmp( type, "Slider" ) == 0 )
+ {
+ // figure out which slider it is.
+ for( int j = 0; j < 3; j++ )
+ {
+ if( m_radController->GetInputPointByTypeAndIndex( "Slider", j ) ==
+ m_radController->GetInputPointByIndex( i ) )
+ {
+ m_InputToDICode[ i ] = DIJOFS_SLIDER(j);
+ break;
+ }
+ }
+ }
+ else if( strcmp( type, "Button" ) == 0 )
+ {
+ // figure out which button it is
+ for( int j = 0; j < 32; j++ )
+ {
+ if( m_radController->GetInputPointByTypeAndIndex( "Button", j ) ==
+ m_radController->GetInputPointByIndex( i ) )
+ {
+ m_InputToDICode[ i ] = DIJOFS_BUTTON(j);
+ break;
+ }
+ }
+ }
+ // Now make the input code controller independent.
+ m_InputToDICode[ i ] = GetIndependentDICode( m_InputToDICode[ i ] );
+ }
+ }
+}
+
+//==============================================================================
+// SteeringWheel::GetDICode
+//==============================================================================
+//
+// Description: Returns the direct input keycode associated with an input point,
+// or -1 if there is no controller or input code.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+int SteeringWheel::GetDICode( int inputpoint ) const
+{
+ return m_InputToDICode == NULL ? Input::INVALID_CONTROLLERID : m_InputToDICode[ inputpoint ];
+}
+
+//==============================================================================
+// SteeringWheel::GetIndependentDICode
+//==============================================================================
+//
+// Description: Same as gamepad
+//
+// Parameters: The direct input code for the specific controller.
+//
+// Return: The inpendent & real direct input code they should have assigned
+// it. Often this is the same code.
+//
+//==============================================================================
+
+int SteeringWheel::GetIndependentDICode( int diCode ) const
+{
+ switch( diCode )
+ {
+ case DIJOFS_SLIDER(0):
+ {
+ return m_bPedalInverted ? DIJOFS_Y : DIJOFS_SLIDER(0);
+ }
+ default:
+ {
+ return diCode;
+ }
+ }
+} \ No newline at end of file
diff --git a/game/code/input/SteeringWheel.h b/game/code/input/SteeringWheel.h
new file mode 100644
index 0000000..7742958
--- /dev/null
+++ b/game/code/input/SteeringWheel.h
@@ -0,0 +1,31 @@
+/******************************************************************************
+ File: SteeringWheel.h
+ Desc: Interface for the SteeringWheel class.
+
+ Date: May 16, 2003
+ History:
+*****************************************************************************/
+#ifndef STEERINGWHEEL_H
+#define STEERINGWHEEL_H
+
+#include <input/RealController.h>
+
+class SteeringWheel : public RealController
+{
+public:
+ SteeringWheel();
+ virtual ~SteeringWheel();
+
+ virtual void Init( IRadController* pController );
+ virtual int GetDICode( int inputpoint ) const;
+ bool IsPedalInverted() const { return m_bPedalInverted; }
+
+private:
+ virtual void MapInputToDICode();
+ int GetIndependentDICode( int diCode ) const;
+
+private:
+ int* m_InputToDICode;
+ bool m_bPedalInverted;
+};
+#endif
diff --git a/game/code/input/allinput.cpp b/game/code/input/allinput.cpp
new file mode 100644
index 0000000..5967c24
--- /dev/null
+++ b/game/code/input/allinput.cpp
@@ -0,0 +1,20 @@
+#include <input/button.cpp>
+#include <input/inputmanager.cpp>
+#include <input/mappable.cpp>
+#include <input/mapper.cpp>
+#include <input/usercontroller.cpp>
+#include <input/rumbleeffect.cpp>
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+#include <input/steeringspring.cpp>
+#include <input/basedamper.cpp>
+#include <input/forceeffect.cpp>
+#include <input/constanteffect.cpp>
+#include <input/wheelrumble.cpp>
+#endif
+
+#ifdef RAD_PS2
+#include <input/rumbleps2.cpp>
+#elif defined( RAD_GAMECUBE )
+#include <input/rumblegc.cpp>
+#endif
diff --git a/game/code/input/basedamper.cpp b/game/code/input/basedamper.cpp
new file mode 100644
index 0000000..edafbb0
--- /dev/null
+++ b/game/code/input/basedamper.cpp
@@ -0,0 +1,185 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: BaseDamper.cpp
+//
+// Description: Implement BaseDamper
+//
+// History: 10/21/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/BaseDamper.h>
+#include <input/wheeldefines.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// BaseDamper::BaseDamper
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+BaseDamper::BaseDamper()
+{
+ //Setup the respective force effect structures.
+#ifdef RAD_WIN32
+ m_conditon.lOffset = 0;
+ m_conditon.lDeadBand = 0;
+ m_conditon.lPositiveCoefficient = 127;
+ m_conditon.lNegativeCoefficient = 127;
+ m_conditon.dwPositiveSaturation = 127;
+ m_conditon.dwNegativeSaturation = 127;
+
+ mForceEffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
+ mForceEffect.dwDuration = INFINITE;
+ mForceEffect.dwGain = DI_FFNOMINALMAX;
+ mForceEffect.dwTriggerButton = DIEB_NOTRIGGER;
+ mForceEffect.cAxes = 1;
+ mForceEffect.rgdwAxes = m_rgdwAxes;
+ mForceEffect.rglDirection = m_rglDirection;
+ mForceEffect.lpEnvelope = NULL;
+ mForceEffect.cbTypeSpecificParams = sizeof(DICONDITION);
+ mForceEffect.lpvTypeSpecificParams = &m_conditon;
+ mForceEffect.dwStartDelay = 0;
+#else
+ mForceEffect.type = LG_TYPE_DAMPER;
+ mForceEffect.duration = LG_DURATION_INFINITE;
+ mForceEffect.startDelay = 0;
+ mForceEffect.p.condition[0].offset = 0;
+ mForceEffect.p.condition[0].deadband = 0;
+ mForceEffect.p.condition[0].saturationNeg = 127;
+ mForceEffect.p.condition[0].saturationPos = 127;
+ mForceEffect.p.condition[0].coefficientNeg = 127;
+ mForceEffect.p.condition[0].coefficientPos = 127;
+#endif
+}
+
+//==============================================================================
+// BaseDamper::~BaseDamper
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+BaseDamper::~BaseDamper()
+{
+}
+
+//=============================================================================
+// BaseDamper::OnInit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void BaseDamper::OnInit()
+{
+}
+
+//=============================================================================
+// BaseDamper::SetCenterPoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( s8 point, u8 deadband )
+//
+// Return: void
+//
+//=============================================================================
+void BaseDamper::SetCenterPoint( s8 point, u8 deadband )
+{
+#ifdef RAD_WIN32
+ m_conditon.lOffset = point;
+ m_conditon.lDeadBand = deadband;
+#else
+ mForceEffect.p.condition[0].offset = point;
+ mForceEffect.p.condition[0].deadband = deadband;
+#endif
+
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// BaseDamper::SetDamperStrength
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( u8 strength )
+//
+// Return: void
+//
+//=============================================================================
+#ifdef RAD_WIN32
+void BaseDamper::SetDamperStrength( u16 strength )
+#else
+void BaseDamper::SetDamperStrength( u8 strength )
+#endif
+{
+#ifdef RAD_WIN32
+ m_conditon.dwPositiveSaturation = strength;
+ m_conditon.dwNegativeSaturation = strength;
+#else
+ mForceEffect.p.condition[0].saturationNeg = strength;
+ mForceEffect.p.condition[0].saturationPos = strength;
+#endif
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// BaseDamper::SetDamperCoefficient
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( s16 coeff )
+//
+// Return: void
+//
+//=============================================================================
+void BaseDamper::SetDamperCoefficient( s16 coeff )
+{
+#ifdef RAD_WIN32
+ m_conditon.lPositiveCoefficient = coeff;
+ m_conditon.lNegativeCoefficient = coeff;
+#else
+ mForceEffect.p.condition[0].coefficientNeg = coeff;
+ mForceEffect.p.condition[0].coefficientPos = coeff;
+#endif
+
+ mEffectDirty = true;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/input/basedamper.h b/game/code/input/basedamper.h
new file mode 100644
index 0000000..66b0dec
--- /dev/null
+++ b/game/code/input/basedamper.h
@@ -0,0 +1,59 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: basedamper.h
+//
+// Description: Blahblahblah
+//
+// History: 10/19/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef BASEDAMPER_H
+#define BASEDAMPER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radcontroller.hpp>
+
+#include <input/forceeffect.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class BaseDamper : public ForceEffect
+{
+public:
+ BaseDamper();
+ virtual ~BaseDamper();
+
+ void OnInit();
+
+ void SetCenterPoint( s8 degrees, u8 deadband ); //Where 0 is straight up.
+#ifdef RAD_WIN32
+ void SetDamperStrength( u16 strength );
+#else
+ void SetDamperStrength( u8 strength );
+#endif
+ void SetDamperCoefficient( s16 coeff );
+private:
+
+#ifdef RAD_WIN32
+ DICONDITION m_conditon;
+#endif
+
+ //Prevent wasteful constructor creation.
+ BaseDamper( const BaseDamper& basedamper );
+ BaseDamper& operator=( const BaseDamper& basedamper );
+};
+
+
+#endif //BASEDAMPER_H
diff --git a/game/code/input/button.cpp b/game/code/input/button.cpp
new file mode 100644
index 0000000..7e530a6
--- /dev/null
+++ b/game/code/input/button.cpp
@@ -0,0 +1,14 @@
+#include <input/button.h>
+
+unsigned int Button::mTickCount = 0;
+
+Button::Button( void ) :
+ mfValue( 0.0f ),
+ mTickCountAtChange( 0 )
+{
+}
+
+Button::~Button( void )
+{
+}
+
diff --git a/game/code/input/button.h b/game/code/input/button.h
new file mode 100644
index 0000000..7c76288
--- /dev/null
+++ b/game/code/input/button.h
@@ -0,0 +1,71 @@
+#ifndef BUTTON_H_
+#define BUTTON_H_
+
+// An input point, either physical (if storeig in a UserController), or logical (if stored in a Mappable
+// (even joysticks are treated as "buttons" by the game)
+class Button
+{
+public:
+ Button( void );
+ ~Button( void );
+
+ // test the properties of the button
+ inline float GetValue( void ) const;
+ inline void SetValue( float fValue );
+ inline bool IsDown( void ) const;
+ inline bool IsUp( void ) const;
+
+ // returns number of input ticks since the value of this button changed
+ inline unsigned int TimeSinceChange( void ) const;
+
+ // force the change flag to be set without resetting the value
+ inline void ForceChange(void);
+
+ inline static void Tick(int timeinms) { mTickCount += timeinms; }
+
+protected:
+ float mfValue;
+ unsigned int mTickCountAtChange;
+
+ // counter for current number of input ticks (for tracking
+ static unsigned int mTickCount;
+};
+
+// Inline member functions
+float Button::GetValue( void ) const
+{
+ return mfValue;
+}
+
+void Button::ForceChange(void)
+{
+ mTickCountAtChange = mTickCount;
+}
+
+void Button::SetValue( float fValue )
+{
+ mTickCountAtChange = mTickCount;
+ mfValue = fValue;
+}
+
+bool Button::IsDown( void ) const
+{
+ return mfValue != 0.0f;
+}
+
+bool Button::IsUp( void ) const
+{
+ return mfValue == 0.0f;
+}
+
+unsigned int Button::TimeSinceChange( void ) const
+{
+ return mTickCount - mTickCountAtChange;
+}
+
+// nbrooke 15/10/02 :I devirtualized the Button class (there was a lot of hierarchy
+// and virtual functions considering there was only one class avtually used my any other code)
+// but didn't feel like changing a bunch of stuff that was using IButton, thus the typedef
+typedef Button IButton;
+
+#endif \ No newline at end of file
diff --git a/game/code/input/constanteffect.cpp b/game/code/input/constanteffect.cpp
new file mode 100644
index 0000000..c0797a6
--- /dev/null
+++ b/game/code/input/constanteffect.cpp
@@ -0,0 +1,163 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: constanteffect.cpp
+//
+// Description: Implement ConstantEffect
+//
+// History: 6/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/constanteffect.h>
+#include <input/wheeldefines.h>
+#include <input/inputmanager.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ConstantEffect::ConstantEffect
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ConstantEffect::ConstantEffect()
+{
+ //Setup the respective force effect structures.
+#ifdef RAD_WIN32
+ m_diConstant.lMagnitude = 0;
+ m_diEnvelope.dwSize = sizeof(DIENVELOPE);
+ m_diEnvelope.dwAttackLevel = 0;
+ m_diEnvelope.dwAttackTime = 0;
+ m_diEnvelope.dwFadeLevel = 0;
+ m_diEnvelope.dwFadeTime = 0;
+
+ mForceEffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
+ mForceEffect.dwDuration = 500;
+ mForceEffect.dwGain = DI_FFNOMINALMAX;
+ mForceEffect.dwTriggerButton = DIEB_NOTRIGGER;
+ mForceEffect.cAxes = 1;
+ mForceEffect.rgdwAxes = m_rgdwAxes;
+ mForceEffect.rglDirection = m_rglDirection;
+ mForceEffect.lpEnvelope = NULL;
+ mForceEffect.cbTypeSpecificParams = sizeof(DICONSTANTFORCE);
+ mForceEffect.lpvTypeSpecificParams = &m_diConstant;
+ mForceEffect.dwStartDelay = 0;
+
+#else
+ mForceEffect.type = LG_TYPE_CONSTANT;
+ mForceEffect.duration = 500;
+ mForceEffect.startDelay = 0;
+ mForceEffect.p.constant.magnitude = 0;
+ mForceEffect.p.constant.direction = 0;
+ mForceEffect.p.constant.envelope.attackTime = 0;
+ mForceEffect.p.constant.envelope.fadeTime = 0;
+ mForceEffect.p.constant.envelope.attackLevel = 0;
+ mForceEffect.p.constant.envelope.fadeLevel = 0;
+#endif
+}
+
+//=============================================================================
+// ConstantEffect::~ConstantEffect
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ConstantEffect::~ConstantEffect()
+{
+}
+
+//=============================================================================
+// ConstantEffect::OnInit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ConstantEffect::OnInit()
+{
+#ifdef RAD_WIN32
+ m_diConstant.lMagnitude = 0;
+#else
+ mForceEffect.p.constant.magnitude = 0;
+#endif
+}
+
+//=============================================================================
+// ConstantEffect::SetMagnitude
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( s16 magnitude )
+//
+// Return: void
+//
+//=============================================================================
+void ConstantEffect::SetMagnitude( s16 magnitude )
+{
+#ifdef RAD_WIN32
+ m_diConstant.lMagnitude = magnitude;
+#else
+ mForceEffect.p.constant.magnitude = magnitude;
+#endif
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// ConstantEffect::SetDirection
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( u16 direction )
+//
+// Return: void
+//
+//=============================================================================
+void ConstantEffect::SetDirection( u16 direction )
+{
+#ifdef RAD_WIN32
+ LONG rglDirection[2] = { direction, 0 };
+ mForceEffect.rglDirection = rglDirection;
+#else
+ mForceEffect.p.constant.direction = direction;
+#endif
+
+ mEffectDirty = true;
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/input/constanteffect.h b/game/code/input/constanteffect.h
new file mode 100644
index 0000000..ed9ff9e
--- /dev/null
+++ b/game/code/input/constanteffect.h
@@ -0,0 +1,59 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: constanteffect.h
+//
+// Description: Blahblahblah
+//
+// History: 6/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef CONSTANTEFFECT_H
+#define CONSTANTEFFECT_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <input/forceeffect.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ConstantEffect : public ForceEffect
+{
+public:
+ ConstantEffect();
+ virtual ~ConstantEffect();
+
+ void OnInit();
+
+ void SetMagnitude( s16 magnitude );
+ void SetDirection( u16 direction );
+
+private:
+
+#ifdef RAD_WIN32
+ DICONSTANTFORCE m_diConstant;
+ DIENVELOPE m_diEnvelope;
+#endif
+
+ //Prevent wasteful constructor creation.
+ ConstantEffect( const ConstantEffect& constanteffect );
+ ConstantEffect& operator=( const ConstantEffect& constanteffect );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //CONSTANTEFFECT_H
diff --git a/game/code/input/controller.h b/game/code/input/controller.h
new file mode 100644
index 0000000..d98482a
--- /dev/null
+++ b/game/code/input/controller.h
@@ -0,0 +1,100 @@
+#ifndef CONTROLLER_HPP
+#define CONTROLLER_HPP
+
+namespace Input
+{
+ // Platform specific controller topology.
+ #ifdef RAD_XBOX
+ const static unsigned int MaxPorts = 4;
+ const static unsigned int MaxSlots = 1;
+ #endif
+ #ifdef RAD_PS2
+ const static unsigned int MaxPorts = 2;
+ const static unsigned int MaxSlots = 4;
+ #endif
+ #ifdef RAD_GAMECUBE
+ const static unsigned int MaxPorts = 4;
+ const static unsigned int MaxSlots = 1;
+ #endif
+ #if defined( RAD_WIN32 )
+ const static unsigned int MaxPorts = 4;
+ const static unsigned int MaxSlots = 1;
+ #endif
+
+ #ifdef RAD_PS2
+ enum USBID
+ {
+ USB0 = (MaxPorts * MaxSlots),
+ USB1 = (MaxPorts * MaxSlots) + 1
+ };
+ static const unsigned int MaxUSB = 2;
+
+ // Platform specific max controllers.
+ const static unsigned int MaxControllers = (MaxPorts * MaxSlots) + MaxUSB;
+
+ #else //NOT PS2
+ // Platform specific max controllers.
+ const static unsigned int MaxControllers = (MaxPorts * MaxSlots);
+ #endif
+
+
+ #ifdef RAD_WIN32
+ const static unsigned int NumExtraButtonsForSuperSprint = 7;
+ #endif
+
+ // Maximum number of physical buttons (in a UserController)
+ #ifdef RAD_WIN32
+ const static unsigned int MaxPhysicalButtons = 42 + NumExtraButtonsForSuperSprint;
+ #else
+ const static unsigned int MaxPhysicalButtons = 40;
+ #endif
+
+ // Maximum number of logical buttons (in a UserController)
+ #ifdef RAD_WIN32
+ const static unsigned int MaxLogicalButtons = 42 + NumExtraButtonsForSuperSprint;
+ #else
+ const static unsigned int MaxLogicalButtons = 40;
+ #endif
+
+ // Maximum number of logical controllers for a physical device
+ const static unsigned int MaxMappables = 16;
+
+ // Maximum number of control mappings for a logical controller
+ const static unsigned int MaxMappings = 2;
+
+ // Maximum number of physical keys that can be assigned to a virtual key
+ // for a controller.
+ #ifdef RAD_WIN32
+ const static unsigned int MaxVirtualMappings = 2;
+ #endif
+
+ // Maximum number of rumble motors
+ #ifdef RAD_GAMECUBE
+ const static unsigned int MaxOutputMotor = 1;
+ #else
+ const static unsigned int MaxOutputMotor = 2;
+ #endif
+
+ // Control system state (for keeping input roped in in certain gameplay states)
+ enum ActiveState
+ {
+ ACTIVE_NONE = 0,
+ ACTIVE_GAMEPLAY = 1 << 0,
+ ACTIVE_FRONTEND = 1 << 1,
+ ACTIVE_SS_GAME = 1 << 2,
+ ACTIVE_FIRST_PERSON = 1 << 3,
+ ACTIVE_ANIM_CAM = 1 << 4,
+ DEACTIVE_ANIM_CAM = 0xfffffffe,
+ ACTIVE_ALL = 0xffffffff
+ };
+
+ // handy constant for a bunk controller / input
+ enum
+ {
+ INVALID_CONTROLLERID = -1
+ };
+};
+
+
+
+#endif
diff --git a/game/code/input/forceeffect.cpp b/game/code/input/forceeffect.cpp
new file mode 100644
index 0000000..70fb63c
--- /dev/null
+++ b/game/code/input/forceeffect.cpp
@@ -0,0 +1,195 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: forceeffect.cpp
+//
+// Description: Implement ForceEffect
+//
+// History: 6/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/forceeffect.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ForceEffect::ForceEffect
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ForceEffect::ForceEffect() :
+mOutputPoint( NULL ),
+#ifdef RAD_WIN32
+m_effectTime(0),
+m_currentTime(0),
+#endif
+mEffectDirty( true )
+{
+#ifdef RAD_WIN32
+ ZeroMemory( &mForceEffect, sizeof(mForceEffect) );
+ ZeroMemory( &m_rglDirection, sizeof(m_rglDirection) );
+
+ mForceEffect.dwSize = sizeof(DIEFFECT);
+
+ m_rgdwAxes[0] = DIJOFS_X;
+ m_rgdwAxes[1] = DIJOFS_Y;
+#endif
+}
+
+//=============================================================================
+// ForceEffect::~ForceEffect
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ForceEffect::~ForceEffect()
+{
+ if ( mOutputPoint )
+ {
+ mOutputPoint->Stop();
+ mOutputPoint->Release();
+ mOutputPoint = NULL;
+ }
+}
+
+//=============================================================================
+// ForceEffect::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( IRadControllerOutputPoint* outputPoint )
+//
+// Return: void
+//
+//=============================================================================
+void ForceEffect::Init( IRadControllerOutputPoint* outputPoint )
+{
+#ifdef RAD_WIN32
+ rAssertMsg( outputPoint != NULL, "Attempt to set the outputPoint with a NULL pointer." );
+ // mOutputPoint is dirty.
+ if ( mOutputPoint )
+ {
+ ShutDownEffects();
+ }
+#endif
+ mOutputPoint = outputPoint;
+ mOutputPoint->AddRef();
+
+ OnInit();
+
+ mOutputPoint->UpdateEffect( &mForceEffect );
+}
+
+//=============================================================================
+// ForceEffect::Start
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ForceEffect::Start()
+{
+ //Clear out current effects.
+ OnInit();
+ mEffectDirty = true;
+
+ if ( mOutputPoint )
+ {
+ mOutputPoint->Start();
+ }
+}
+
+//=============================================================================
+// ForceEffect::Stop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ForceEffect::Stop()
+{
+ if ( mOutputPoint )
+ {
+ mOutputPoint->Stop();
+ }
+}
+
+//=============================================================================
+// ForceEffect::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+#ifdef RAD_WIN32
+void ForceEffect::Update(unsigned timeins)
+#else
+void ForceEffect::Update()
+#endif
+{
+ if ( mEffectDirty && mOutputPoint )
+ {
+ mOutputPoint->UpdateEffect( &mForceEffect );
+ mEffectDirty = false;
+ }
+}
+#ifdef RAD_WIN32
+void ForceEffect::ShutDownEffects()
+{
+ if ( mOutputPoint )
+ {
+ mOutputPoint->ReleaseEffect();
+ mOutputPoint->Release();
+ mOutputPoint = NULL;
+ }
+}
+
+void ForceEffect::SetResetTime( DWORD dwMilliSeconds )
+{
+ m_effectTime = dwMilliSeconds;
+}
+#endif
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/input/forceeffect.h b/game/code/input/forceeffect.h
new file mode 100644
index 0000000..050c284
--- /dev/null
+++ b/game/code/input/forceeffect.h
@@ -0,0 +1,104 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: forceeffect.h
+//
+// Description: Blahblahblah
+//
+// History: 6/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef FORCEEFFECT_H
+#define FORCEEFFECT_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radcontroller.hpp>
+
+#ifdef RAD_GAMECUBE
+#include <dolphin/lg.h>
+#endif
+
+#ifdef RAD_PS2
+#include <liblgdev.h>
+#endif
+
+#ifdef RAD_WIN32
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+#endif
+
+#if defined(RAD_PS2) || defined(RAD_WIN32)
+typedef char s8;
+typedef unsigned char u8;
+typedef short s16;
+typedef unsigned short u16;
+typedef unsigned int u32;
+#endif
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ForceEffect
+{
+public:
+ ForceEffect();
+ virtual ~ForceEffect();
+
+#ifdef RAD_WIN32
+ void SetForceID( u8 forceID ) { m_index = forceID; }
+ u8 GetForceID() const { return m_index; }
+
+#endif
+
+ void Init( IRadControllerOutputPoint* outputPoint );
+ bool IsInit() const { return ( mOutputPoint != NULL ); };
+
+ void Start();
+ void Stop();
+
+#ifdef RAD_WIN32
+ virtual void Update(unsigned timeins = 0);
+ void ShutDownEffects();
+ void SetResetTime( DWORD dwMilliSeconds );
+#else
+ void Update();
+#endif
+
+protected:
+ IRadControllerOutputPoint* mOutputPoint;
+#ifdef RAD_WIN32
+ DIEFFECT mForceEffect;
+ DWORD m_rgdwAxes[2];
+ LONG m_rglDirection[2];
+ u8 m_index; // the index ID of this force.
+ DWORD m_currentTime;
+ DWORD m_effectTime;
+#else
+ LGForceEffect mForceEffect;
+#endif
+ bool mEffectDirty;
+
+ virtual void OnInit() = 0;
+
+ //Prevent wasteful constructor creation.
+ ForceEffect( const ForceEffect& forceeffect );
+ ForceEffect& operator=( const ForceEffect& forceeffect );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //FORCEEFFECT_H
diff --git a/game/code/input/inputPointList.h b/game/code/input/inputPointList.h
new file mode 100644
index 0000000..2d581d5
--- /dev/null
+++ b/game/code/input/inputPointList.h
@@ -0,0 +1,176 @@
+#if !defined(AFX_LIST_H__F5C121CA_0F5B_4CE3_91D5_B7B24AEB8D37__INCLUDED_)
+#define AFX_LIST_H__F5C121CA_0F5B_4CE3_91D5_B7B24AEB8D37__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+
+/******************************************************************************
+ List
+
+ Date: 19-11-01
+
+ Name: Jeff Giles
+ Ezeikeil@Hotmail.com
+
+ Notes: A Simple, doubly linked list which returns NULL when run off
+ either end.
+
+
+******************************************************************************/
+template <class type>
+class List
+{
+ //*Node struct*******************************************************************
+ // Notes: The workhorse of the Linked List
+ //*****************************************************************************
+ struct Node
+ {
+ type * thedata; //pointer to the data
+ Node * nextnode; //pointer to the next node in the list
+ Node * lastnode; //pointer to the previous node in the list
+
+ //Ctors
+ Node(): thedata(0),nextnode(0),lastnode(0) {};
+ Node(type* data, Node* next): thedata(data),nextnode(next),lastnode(0) {};
+ };
+
+public:
+ //*Ctor/Dtor*****************************************************************
+ List(): myHead(0),myTail(0), numNodes(0) {};
+ virtual ~List(){};
+
+ //*GetSize*********************************************************************
+ // Notes: returns the current number of nodes
+ //*****************************************************************************
+ int GetSize()
+ {
+ return numNodes;
+ }
+
+ //*PushFront*******************************************************************
+ // Notes: Pushes an element onto the top of the list
+ //*****************************************************************************
+ void PushFront(type * data)
+ {
+ myHead = new Node(data, myHead);
+
+ if (myTail == NULL)
+ myTail = myHead;
+
+ if(myHead->nextnode !=NULL) //redirect the node following myHead
+ myHead->nextnode->lastnode = myHead; //to point to the new head
+
+ numNodes++;
+ }
+
+ //*PushBack******************************************************************
+ // Notes: Pushes an element onto the bottom of the list
+ //***************************************************************************
+ void PushBack(type * data)
+ {
+ if (myHead == NULL) //then the list must be empty
+ PushFront(data);
+ else
+ {
+ Node * temp = new Node;
+ temp->thedata = data;
+ temp->lastnode = myTail;
+
+ myTail= temp; //redirect myTail
+ myTail->lastnode->nextnode = temp; //redirect the nextnode before
+ //myTail to point to the new Node
+ numNodes++;
+ }
+ }
+
+ //*PopBack*********************************************************************
+ // Notes: Removes an element from the top of the list
+ //*****************************************************************************
+ type * PopBack()
+ {
+ if(myTail != NULL)
+ {
+ type * temp = myTail->thedata;
+ Node * oldtail = myTail; //keep a hold of the data to prevent memory leak
+ myTail = myTail->lastnode;
+ numNodes--;
+ delete oldtail;
+ return temp;
+ }
+ else
+ return NULL;
+ };
+
+ //*PopFront*******************************************************************
+ // Notes: Removes an element from the bottom of the list
+ //****************************************************************************
+ type * PopFront()
+ {
+
+ if(myHead != NULL)
+ {
+ type * temp = myHead->thedata;
+ Node * oldHead = myHead; //keep a hold of the data to prevent memory leak
+ myHead = myHead->nextnode;
+ numNodes--;
+ delete oldHead;
+ return temp;
+ }
+ else
+ return NULL;
+
+ };
+
+ //*PeekFront******************************************************************
+ // Notes: Accesses the top of the list without removing the node
+ //****************************************************************************
+ type * PeekFront()
+ {
+ if(myHead)
+ return myHead->thedata;
+ else
+ return NULL;
+ };
+
+ //*PeekBack*******************************************************************
+ // Notes: Accesses the bottom of the list without removing the node
+ //****************************************************************************
+ type * PeekBack()
+ {
+ if(myTail)
+ return myTail->thedata;
+ else
+ return NULL;
+ };
+
+ //*operator []****************************************************************
+ // Notes: Accesses the any node of the list without removing it
+ // Position is from top to
+ //****************************************************************************
+ type * operator[] (int index)
+ {
+ if(numNodes < 0 || index >= numNodes) //prevent overrun
+ return NULL;
+ else
+ {
+ Node * temp = myHead;
+ int counter = 0;
+
+ while (counter < index)
+ {
+ temp = temp->nextnode; //point temp at the next node in the list
+ counter++;
+ }
+ return temp->thedata;
+ }
+ };
+
+private:
+ Node * myTail;
+ Node * myHead;
+ int numNodes;
+};
+
+#endif // !defined(AFX_LIST_H__F5C121CA_0F5B_4CE3_91D5_B7B24AEB8D37__INCLUDED_)
diff --git a/game/code/input/inputmanager.cpp b/game/code/input/inputmanager.cpp
new file mode 100644
index 0000000..8bd8550
--- /dev/null
+++ b/game/code/input/inputmanager.cpp
@@ -0,0 +1,731 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: inputmanager.cpp
+//
+// Description: Implementation for the InputManager class.
+//
+// History: + Created -- TBJ
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <string.h> // ::strcmp()
+//FTech
+#include <raddebugwatch.hpp>
+#include <raddebug.hpp>
+#include <radcontroller.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/inputmanager.h>
+#ifdef RAD_WIN32
+#include <input/usercontrollerWin32.h>
+#else
+#include <input/usercontroller.h>
+#endif
+#include <input/button.h>
+
+#include <main/game.h>
+#include <main/platform.h>
+
+#include <data/gamedatamanager.h>
+
+#ifndef WORLD_BUILDER
+#include <memory/srrmemory.h>
+#else
+#define MEMTRACK_PUSH_GROUP(x)
+#define MEMTRACK_POP_GROUP(x)
+#define GMA_PERSISTENT 0
+#define GMA_DEFAULT 0
+#endif
+
+#ifdef RAD_PS2
+#include <main/ps2platform.h>
+#include <libmtap.h>
+#endif
+
+#if defined RAD_XBOX
+
+ InputManager::eButtonMap RESET_BUTTONS[] =
+ {
+ InputManager::Start,
+ InputManager::Select
+ };
+
+ const int NUM_RESET_BUTTONS = 2;
+
+#else //GC handled in gcmanager and we don't do this on the PC
+
+InputManager::eButtonMap RESET_BUTTONS[] =
+{
+ InputManager::Start
+};
+
+const int NUM_RESET_BUTTONS = 0;
+
+#endif
+
+const unsigned int RESET_TIMEOUT = 2000;
+
+//******************************************************************************
+// Global Data, Local Data, Local Classes
+//******************************************************************************
+
+//
+// Static pointer to instance of singleton.
+//
+InputManager* InputManager::spInstance = NULL;
+
+
+//******************************************************************************
+// Public Member Functions
+//******************************************************************************
+
+// Creates the InputManager.
+// This is a singleton so only one instance is allowed.
+InputManager* InputManager::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "InputManager" );
+
+ rAssert( spInstance == NULL );
+
+ spInstance = new InputManager;
+MEMTRACK_POP_GROUP("InputManager");
+
+ return spInstance;
+}
+
+// Access point for the InputManager singleton.
+InputManager* InputManager::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+// Destroy the InputManager.singleton
+void InputManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete spInstance;
+ spInstance = NULL;
+}
+
+
+void InputManager::Init()
+{
+
+MEMTRACK_PUSH_GROUP( "InputManager" );
+ ::radControllerInitialize( this, GMA_DEFAULT );
+
+ mxIControllerSystem2 = radControllerSystemGet();
+
+#ifdef RAD_PS2
+ // On PS2, check for initial button pushes.
+ m_isProScanButtonsPressed = PS2Platform::GetInstance()->CheckForStartupButtons( );
+ rDebugPrintf( "Do Progressive scan: %s\n", m_isProScanButtonsPressed ? "yup" : "nope" );
+#endif
+
+ unsigned int i = 0;
+
+ for ( i = 0; i < Input::MaxControllers; i++ )
+ {
+ // preallocate run time controller structure.
+ mControllerArray[ i ].Create(i);
+ }
+#ifndef RAD_WIN32
+ mxIControllerSystem2->RegisterConnectionChangeCallback( this );
+#endif
+ rDebugString( "Just created User controller system\n" );
+
+ EnumerateControllers( );
+MEMTRACK_POP_GROUP("InputManager");
+
+}
+
+
+void InputManager::Update( unsigned int timeinms )
+{
+ // update the button timestamp (so we can tell when buttons were pressed)
+ Button::Tick(timeinms);
+
+ ::radControllerSystemService();
+
+ // if controllers have been disconnected, change the state
+ if(mConnectStateChanged)
+ {
+ mConnectStateChanged = false;
+ EnumerateControllers( );
+ }
+
+ unsigned int i = 0;
+
+ // update physical controllers
+ bool resetting = false;
+ for ( i = 0; i < Input::MaxControllers; i++ )
+ {
+ if(mControllerArray[i].IsConnected())
+ {
+ mControllerArray[i].Update(timeinms);
+
+ if ( mResetEnabled &&
+ NUM_RESET_BUTTONS > 0 &&
+ !resetting &&
+ mControllerArray[i].GetInputValueRT( RESET_BUTTONS[ 0 ] ) > 0.5f &&
+ mControllerArray[i].GetInputValueRT( RESET_BUTTONS[ 1 ] ) > 0.5f )
+ {
+ resetting = true;
+ }
+ }
+ }
+
+#ifndef RAD_XBOX
+ if ( mResetEnabled )
+ {
+ if ( !resetting )
+ {
+ mResetTimeout = 0;
+ mIsResetting = false;
+ }
+ else if ( resetting && !mIsResetting )
+ {
+ mResetTimeout = RESET_TIMEOUT;
+ mIsResetting = true;
+ }
+ }
+#endif
+
+
+ // broadcast new game state if it has changed
+ if(mChangeGameState)
+ {
+ for ( i = 0; i < Input::MaxControllers; i++ )
+ {
+ // TC: Why does controller have to be connected for its game state to be updated?
+ // Having the 'if' conditional here causes a problem when the controller is
+ // disconnnected during a state when it's non-active.
+ //
+// if(mControllerArray[i].IsConnected())
+ {
+ mControllerArray[i].SetGameState(mGameState);
+ }
+ }
+ mChangeGameState = false;
+ }
+
+ if ( mResetEnabled && mIsResetting && mResetTimeout >= 0 )
+ {
+ if ( mResetTimeout <= timeinms )
+ {
+ GetGame()->GetPlatform()->LaunchDashboard();
+ mResetTimeout = 0;
+ mIsResetting = false;
+ }
+ else
+ {
+ mResetTimeout -= timeinms;
+ }
+ }
+}
+
+
+void InputManager::OnControllerConnectionStatusChange( IRadController * pIController2 )
+{
+ mConnectStateChanged = true;
+}
+
+bool InputManager::IsControllerInPort( int portnum ) const
+{
+ rAssert(portnum < static_cast< int >( Input::MaxControllers ) );
+ return mControllerArray[portnum].IsConnected();
+}
+
+void InputManager::ToggleRumble( bool on )
+{
+ unsigned int i;
+ for ( i = 0; i < Input::MaxControllers; ++i )
+ {
+ if ( mControllerArray[ i ].IsConnected() )
+ {
+ mControllerArray[ i ].SetRumble( on );
+ }
+ }
+}
+
+
+//=============================================================================
+// InputManager::SetRumbleEnabled
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool isEnabled )
+//
+// Return: nothing
+//
+//=============================================================================
+void InputManager::SetRumbleEnabled( bool isEnabled )
+{
+ mIsRumbleEnabled = isEnabled;
+}
+
+void InputManager::SetRumbleForDevice( int controllerId, bool bRumbleOn )
+{
+ if ( (unsigned int)controllerId < Input::MaxControllers)
+ {
+ mControllerArray[controllerId].SetRumble( bRumbleOn );
+ }
+}
+
+
+bool InputManager::IsRumbleOnForDevice( int controllerId ) const
+{
+ if ( (unsigned int)controllerId < Input::MaxControllers)
+ {
+ return mControllerArray[controllerId].IsRumbleOn( );
+ }
+ return false;
+}
+
+void InputManager::TriggerRumblePulse( int controllerId )
+{
+ if ( (unsigned int)controllerId < Input::MaxControllers)
+ {
+ mControllerArray[ controllerId ].PulseRumble();
+ }
+}
+
+// Returns the value of the input point 'inputIndex' owned by the controller at
+// controllerIndex.
+float InputManager::GetValue( unsigned int controllerIndex, unsigned int inputIndex ) const
+{
+ rAssert( controllerIndex < Input::MaxControllers);
+ if ( controllerIndex < Input::MaxControllers)
+ {
+ return mControllerArray[ controllerIndex ].GetInputValue( inputIndex );
+ }
+ else
+ {
+ return 0.0f;
+ }
+}
+
+// Returns a const pointer to the controller at controller index.
+UserController* InputManager::GetController( unsigned int controllerIndex )
+{
+ rAssert( controllerIndex < Input::MaxControllers );
+ if ( controllerIndex < Input::MaxControllers )
+ {
+ return &mControllerArray[ controllerIndex ];
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+// Associate this device with a game object.
+int InputManager::RegisterMappable( unsigned int index, Mappable *pMappable )
+{
+ int handle = Input::INVALID_CONTROLLERID;
+ if ( index < Input::MaxControllers)
+ {
+ handle = mControllerArray[ index ].RegisterMappable( pMappable );
+ }
+ return handle;
+}
+
+
+// Remove associations between this device and a game object.
+void InputManager::UnregisterMappable( unsigned int index, int handle )
+{
+ if ( handle > Input::INVALID_CONTROLLERID )
+ {
+ if ( index < Input::MaxControllers)
+ {
+ mControllerArray[ index ].UnregisterMappable( handle );
+ }
+ }
+}
+
+void InputManager::UnregisterMappable( unsigned int index, Mappable* mappable)
+{
+ mControllerArray[ index ].UnregisterMappable( mappable );
+}
+
+void InputManager::UnregisterMappable( Mappable *pMappable )
+{
+ unsigned int i;
+ for ( i = 0; i < Input::MaxControllers; ++i )
+ {
+ mControllerArray[ i ].UnregisterMappable( pMappable );
+ }
+}
+
+
+void InputManager::LoadData( const GameDataByte* dataBuffer, unsigned int numBytes )
+{
+ mIsRumbleEnabled = ( dataBuffer[ 0 ] != 0 );
+}
+
+void InputManager::SaveData( GameDataByte* dataBuffer, unsigned int numBytes )
+{
+ dataBuffer[ 0 ] = mIsRumbleEnabled ? ~0 : 0;
+}
+
+void InputManager::ResetData()
+{
+ //Rumble does not RESET. This is a TRC thing.
+ //mIsRumbleEnabled = true;
+}
+
+//******************************************************************************
+// Private Member Functions
+//******************************************************************************
+
+InputManager::InputManager()
+:
+mGameState(Input::ACTIVE_ALL),
+mChangeGameState(false),
+mConnectStateChanged(false),
+mIsRumbleEnabled(true),
+mIsResetting(false),
+mResetEnabled( false ),
+m_isProScanButtonsPressed( false )
+{
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ m_registeredControllerID[ i ] = -1;
+ }
+
+ GetGameDataManager()->RegisterGameData( this, 1, "Input Manager" );
+#ifdef RAD_WIN32
+ m_pFEMouse = new FEMouse;
+#endif
+#ifdef RAD_PS2
+ mLastMultitapStatus[0] = mLastMultitapStatus[1] = 0;
+ mCurMultitapStatus[0] = mCurMultitapStatus[1] = 0;
+
+#endif
+}
+
+
+InputManager::~InputManager()
+{
+ ReleaseAllControllers();
+
+#ifndef RAD_WIN32
+ mxIControllerSystem2->UnRegisterConnectionChangeCallback( this );
+#endif
+ ::radControllerTerminate();
+#ifdef RAD_WIN32
+ delete m_pFEMouse;
+ m_pFEMouse = NULL;
+#endif
+}
+
+void InputManager::EnumerateControllers( void )
+{
+ // on the console the controller device and all the mappables are
+ // preallocated. So we don't have to create new associations.
+ //
+#ifndef RAD_WIN32
+ ref< IRadController > xIC2;
+#else
+ ref< IRadController > radController[ NUM_CONTROLLERTYPES ];
+#endif
+
+ ReleaseAllControllers( );
+
+ bool somethingPluggedIn0 = false;
+ bool somethingPluggedIn1 = false;
+
+ unsigned int slot = 0;
+ unsigned int port = 0;
+ for ( port = 0; port < Input::MaxPorts; ++port )
+ {
+ for ( slot = 0; slot < Input::MaxSlots; ++slot )
+ {
+ unsigned int i = port * Input::MaxSlots + slot;
+
+ char szLocation[ 256 ];
+
+#if defined(RAD_XBOX) || defined( RAD_PS2 )
+ sprintf( szLocation, "Port%d\\Slot%d", port, slot );
+#elif defined(RAD_WIN32)
+ char szJoystickLoc[ 256 ];
+ char szMouseLoc[ 256 ];
+ char szWheelLoc[ 256 ];
+
+ sprintf( szLocation, "Keyboard%d", 0);
+ sprintf( szJoystickLoc, "Joystick%d", i);
+ sprintf( szMouseLoc, "Mouse%d", i);
+ sprintf( szWheelLoc, "SteeringWheel%d", i);
+#else //This is GC
+ sprintf( szLocation, "Channel%d", i );
+#endif
+
+#ifndef RAD_WIN32
+ xIC2 = mxIControllerSystem2->GetControllerAtLocation( szLocation );
+#else
+ radController[KEYBOARD] = mxIControllerSystem2->GetControllerAtLocation( szLocation );
+ radController[GAMEPAD] = mxIControllerSystem2->GetControllerAtLocation( szJoystickLoc );
+ radController[MOUSE] = mxIControllerSystem2->GetControllerAtLocation( szMouseLoc );
+ radController[STEERINGWHEEL] = mxIControllerSystem2->GetControllerAtLocation( szWheelLoc );
+
+ //Check to see if the steering wheel is on controller 0.
+
+#endif
+
+ //
+ // Grab the controller device associated with the foundation controller.
+ //
+ UserController* controller = &mControllerArray[ i ];
+
+
+#ifdef RAD_WIN32
+ // One keyboard has to be present. (might want to change later)
+ if ( (radController[KEYBOARD] == NULL || !radController[KEYBOARD]->IsConnected( )) && (i == 0) )
+ {
+ controller->NotifyDisconnect( );
+ }
+ else
+ {
+ controller->Initialize( radController );
+ controller->NotifyConnect( );
+ controller->LoadControllerMappings( );
+
+ for( int i = 0; i < NUM_CONTROLLERTYPES; i++ )
+ {
+ radController[i] = NULL;
+ }
+ }
+#else
+ if ( xIC2 == NULL || !xIC2->IsConnected( ) )
+ {
+ controller->NotifyDisconnect( );
+ }
+ else
+ {
+#ifdef RAD_PS2
+ if(strcmp( "PsxDualShock2", xIC2->GetType() ) == 0 )
+ {
+ controller->Initialize( xIC2 );
+ controller->NotifyConnect( );
+ controller->LoadControllerMappings( );
+ controller->SetRumble( IsRumbleEnabled() );
+ }
+ xIC2 = NULL;
+
+ if ( port == 0 )
+ {
+ somethingPluggedIn0 = true;
+ }
+ else
+ {
+ somethingPluggedIn1 = true;
+ }
+#else
+ controller->Initialize( xIC2 );
+ controller->NotifyConnect( );
+ controller->LoadControllerMappings( );
+ controller->SetRumble( IsRumbleEnabled() );
+
+ xIC2 = NULL;
+#endif
+ }
+#endif
+ }
+ }
+
+#ifdef RAD_PS2
+
+ //TEST THE MULTITAP STATUS
+ if ( somethingPluggedIn0 || somethingPluggedIn1 )
+ {
+ if ( somethingPluggedIn0 )
+ {
+ mLastMultitapStatus[0] = sceMtapGetConnection(0);
+ }
+
+ if ( somethingPluggedIn1 )
+ {
+ mLastMultitapStatus[1] = sceMtapGetConnection(1);
+ }
+ }
+#endif
+
+
+#ifdef RAD_PS2
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ char szLocation[ 256 ];
+ sprintf( szLocation, "USB%d", i );
+
+ xIC2 = mxIControllerSystem2->GetControllerAtLocation( szLocation );
+ //
+ // Grab the controller device associated with the foundation controller.
+ //
+ UserController* controller = &mControllerArray[ Input::USB0 + i ];
+
+ if ( xIC2 == NULL || !xIC2->IsConnected( ) )
+ {
+ controller->NotifyDisconnect( );
+ }
+ else
+ {
+ if ( xIC2->IsConnected( ) && strcmp( xIC2->GetClassification(), "Wheel" ) == 0 )
+ {
+ // Initialize the controller. This will notify all observers of the
+ // controller being added.
+
+ controller->Initialize( xIC2 );
+ controller->NotifyConnect( );
+ controller->LoadControllerMappings( );
+ }
+ else
+ {
+ controller->NotifyDisconnect( );
+ }
+ xIC2 = NULL;
+ }
+ }
+#endif
+
+//#ifdef RAD_DEBUG
+ char connectionMap[128] = "Controller status changed (connection : ";
+
+ for( port = 0; port < Input::MaxPorts; port++)
+ {
+ for( slot = 0; slot < Input::MaxSlots; slot++)
+ {
+ int index = port * Input::MaxSlots + slot;
+ if(mControllerArray[ index ].IsConnected())
+ {
+ strcat(connectionMap, "x");
+ }
+ else
+ {
+ strcat(connectionMap, "o");
+ }
+ }
+ strcat(connectionMap," ");
+ }
+#ifdef RAD_PS2
+ for(unsigned int i = (Input::MaxSlots * Input::MaxPorts); i < Input::MaxControllers; ++i )
+ {
+ if ( mControllerArray[i].IsConnected() )
+ {
+ strcat(connectionMap,"U");
+ }
+ else
+ {
+ strcat(connectionMap, "o");
+ }
+ }
+#endif
+ strcat(connectionMap,")\n");
+
+ rReleaseString(connectionMap);
+//#endif
+}
+
+void InputManager::ReleaseAllControllers( void )
+{
+ unsigned int i = 0;
+ for ( i = 0; i < Input::MaxControllers; i++ )
+ {
+ mControllerArray[ i ].ReleaseRadController( );
+ }
+}
+
+
+void InputManager::SetGameState( Input::ActiveState state )
+{
+ //
+ // The only way to get out of the animated cam input state is to set it
+ // to the DEACTIVE_ANIM_CAM state, which actually sends you to active all.
+ // Perhaps the input system should really be a stack?
+ //
+ if( mGameState == Input::ACTIVE_ANIM_CAM )
+ {
+ if( state == Input::DEACTIVE_ANIM_CAM )
+ {
+ state = Input::ACTIVE_ALL;
+ }
+ else
+ {
+ state = Input::ACTIVE_ANIM_CAM;
+ }
+ }
+ else
+ {
+ if( state == Input::DEACTIVE_ANIM_CAM )
+ {
+ return;
+ }
+ }
+ mChangeGameState = true;
+ mGameState = state;
+}
+
+Input::ActiveState InputManager::GetGameState() const
+{
+ return static_cast<Input::ActiveState>(mGameState);
+}
+
+
+void
+InputManager::RegisterControllerID( int playerID, int controllerID )
+{
+ rAssert( playerID >= 0 && playerID < MAX_PLAYERS );
+
+ m_registeredControllerID[ playerID ] = controllerID;
+
+ rTunePrintf( "*** Registered Controller ID [%d] for Player %d ***\n",
+ controllerID, playerID + 1 );
+}
+
+void
+InputManager::UnregisterControllerID( int playerID )
+{
+ rAssert( playerID >= 0 && playerID < MAX_PLAYERS );
+
+ m_registeredControllerID[ playerID ] = -1;
+}
+
+void
+InputManager::UnregisterAllControllerID()
+{
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ UnregisterControllerID( i );
+ }
+}
+
+#ifdef RAD_WIN32
+
+void InputManager::StartRumbleEffects()
+{
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ SetRumbleForDevice(i, true);
+ }
+}
+
+void InputManager::StopRumbleEffects()
+{
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ SetRumbleForDevice(i, false);
+ }
+}
+
+#endif
+
diff --git a/game/code/input/inputmanager.h b/game/code/input/inputmanager.h
new file mode 100644
index 0000000..d21303e
--- /dev/null
+++ b/game/code/input/inputmanager.h
@@ -0,0 +1,414 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: inputmanager.h
+//
+// Description: InputManager class declaration.
+//
+// History: + Created -- TBJ
+//
+//=============================================================================
+
+#ifndef InputManager_HPP
+#define InputManager_HPP
+
+//========================================
+// System Includes
+//========================================
+#include <radcontroller.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <data/gamedata.h>
+#include <input/controller.h>
+#ifdef RAD_WIN32
+#include <input/usercontrollerWin32.h>
+#include <input/FEMouse.h>
+#else
+#include <input/usercontroller.h>
+#endif
+#include <constants/maxplayers.h>
+
+//========================================
+// Forward References
+//========================================
+class UserController;
+class Mappable;
+
+//==============================================================================
+//
+// Synopsis: Used to trigger events and route them to the listeners.
+//
+//==============================================================================
+class InputManager : public IRadControllerConnectionChangeCallback,
+ public GameDataHandler
+{
+public:
+#ifdef RAD_XBOX
+ enum eButtonMap
+ {
+ DPadUp,
+ DPadDown,
+ DPadLeft,
+ DPadRight,
+ Start,
+ Back,
+ Select = Back,
+ LeftThumb,
+ L3 = LeftThumb,
+ RightThumb,
+ R3 = RightThumb,
+ A,
+ X = A,
+ B,
+ Circle = B,
+// X, //This one is conflicting with PS2
+ Square, //This is X
+ Y,
+ Triangle = Y,
+ AnalogA,
+ AnalogB,
+ AnalogX,
+ AnalogY,
+ Black,
+ White,
+ AnalogBlack,
+ AnalogWhite,
+ LeftTrigger,
+ AnalogL1 = LeftTrigger,
+ RightTrigger,
+ AnalogR1 = RightTrigger,
+ LeftStickX,
+ LeftStickY,
+ RightStickX,
+ RightStickY
+ };
+#elif defined(RAD_WIN32) // Clumsy because of win32<->console differences
+ enum eButtonMap
+ {
+ MoveUp, // These are the real buttons names
+ MoveDown,
+ MoveLeft,
+ MoveRight,
+ Attack,
+ Jump,
+ Sprint,
+ DoAction,
+ Accelerate,
+ Reverse,
+ SteerLeft,
+ SteerRight,
+ GetOutCar,
+ HandBrake,
+ Horn,
+ ResetCar,
+ CameraLeft,
+ CameraRight,
+ CameraMoveIn,
+ CameraMoveOut,
+ CameraZoom,
+ CameraLookUp,
+ CameraCarLeft,
+ CameraCarRight,
+ CameraCarLookUp,
+ CameraCarLookBack,
+ CameraToggle,
+ feBack,
+ feMoveUp,
+ feMoveDown,
+ feMoveLeft,
+ feMoveRight,
+ feSelect,
+ feFunction1,
+ feFunction2,
+ feMouseLeft,
+ feMouseRight,
+ feMouseUp,
+ feMouseDown,
+
+ P1_KBD_Start,
+ P1_KBD_Gas,
+ P1_KBD_Brake,
+ P1_KBD_EBrake,
+ P1_KBD_Nitro,
+ P1_KBD_Left,
+ P1_KBD_Right,
+ Select = feBack, // These are mappings to PS2 buttons, needed sometimes.
+ Start = feSelect,
+ DPadUp = MoveUp,
+ DPadRight = MoveRight,
+ DPadDown = MoveDown,
+ DPadLeft = MoveLeft,
+ R1 = HandBrake,
+ Triangle = DoAction,
+ Circle = Sprint,
+ X = Jump,
+ Square = Attack,
+ L3 = Horn,
+ AnalogL1 = CameraZoom,
+ AnalogR1 = CameraLookUp,
+ LeftStickX = 200, // Must hack to get camera stuff to compile
+ LeftStickY = 201, // Must hack to get camera stuff to compile
+ KeyboardEsc = 202 // Cute hack to get escape only back buttons
+ };
+#elif defined(RAD_PS2)
+ enum eButtonMap
+ {
+ Select,
+ Start,
+ DPadUp,
+ DPadRight,
+ DPadDown,
+ DPadLeft,
+ L2,
+ R2,
+ L1,
+ R1,
+ Triangle,
+ Circle,
+ X,
+ Square,
+ L3,
+ R3,
+ RightStickX,
+ RightStickY,
+ LeftStickX,
+ LeftStickY,
+ AnalogDPadRight,
+ AnalogDPadLeft,
+ AnalogDPadUp,
+ AnalogDPadDown,
+ AnalogTriangle,
+ AnalogCircle,
+ AnalogX,
+ AnalogSquare,
+ AnalogL1,
+ AnalogR1,
+ AnalogL2,
+ AnalogR2,
+ };
+#else //RAD_GAMECUBE
+ enum eButtonMap
+ {
+ DPadLeft,
+ DPadRight,
+ DPadDown,
+ DPadUp,
+ TriggerZ,
+ L3 = TriggerZ,
+ TriggerR,
+ AnalogR1 = TriggerR,
+ TriggerL,
+ AnalogL1 = TriggerL,
+ A,
+ X = A,
+ B,
+ Square = B,
+// X, //This one is conflicting with PS2
+ Circle, //This is X
+ Y,
+ Triangle = Y,
+ Menu,
+ Start = Menu,
+ LeftStickX,
+ LeftStickY,
+ RightStickX,
+ RightStickY,
+ AnalogTriggerL,
+ LeftTrigger = AnalogTriggerL,
+ AnalogTriggerR,
+ RightTrigger = AnalogTriggerR,
+ AnalogA,
+ AnalogueX = AnalogA,
+ AnalogB,
+ AnalogSquare = AnalogB
+ };
+#endif
+
+ // Static Methods for accessing this singleton.
+ static InputManager* CreateInstance( void );
+ static InputManager* GetInstance( void );
+ static void DestroyInstance( void );
+
+ // set everything up
+ void Init();
+
+ // per frame update
+ void Update ( unsigned int timeinms );
+
+ // various info
+ bool IsControllerInPort( int portnum ) const;
+ static unsigned int GetMaxControllers( void ) { return Input::MaxControllers; }
+ const char* GetName() const { return "Game Controller System"; }
+
+ void ToggleRumble( bool on );
+ void SetRumbleForDevice( int controllerId, bool bRumbleOn );
+ bool IsRumbleOnForDevice( int controllerId ) const;
+ void TriggerRumblePulse( int controllerId );
+
+ void SetRumbleEnabled( bool isEnabled );
+ bool IsRumbleEnabled() const;
+
+#ifdef RAD_WIN32
+ void StartRumbleEffects();
+ void StopRumbleEffects();
+#endif
+
+ // Returns the value of the input point 'inputIndex' owned by the controller at
+ // controllerIndex.
+ float GetValue( unsigned int controllerIndex, unsigned int inputIndex ) const;
+
+ // Get a physical controller
+ UserController* GetController( unsigned int controllerIndex );
+
+ // Associate this logical controller with the physical controller in slot "index"
+ int RegisterMappable( unsigned int index, Mappable *pMappable );
+
+ // Remove associations between a physical and logical controller
+ void UnregisterMappable( unsigned int index, int handle );
+ void UnregisterMappable( unsigned int index, Mappable *pMappable );
+ void UnregisterMappable( Mappable *pMappable );
+
+ // set the current game state for the input system (one of the enums in Input::Active state)
+ void SetGameState( Input::ActiveState state );
+ Input::ActiveState GetGameState() const;
+
+ // registration of controller ID to player ID
+ //
+ void RegisterControllerID( int playerID, int controllerID );
+ void UnregisterControllerID( int playerID );
+ void UnregisterAllControllerID();
+ int GetControllerIDforPlayer( int playerID ) const;
+ int GetControllerPlayerIDforController( int controllerIndex ) const;
+
+ // Implements GameDataHandler
+ //
+ virtual void LoadData( const GameDataByte* dataBuffer, unsigned int numBytes );
+ virtual void SaveData( GameDataByte* dataBuffer, unsigned int numBytes );
+ virtual void ResetData();
+
+ void EnableReset( bool reset ) { mResetEnabled = reset; };
+
+ bool IsProScanButtonsPressed() const { return m_isProScanButtonsPressed; }
+
+#ifdef RAD_WIN32
+ FEMouse* GetFEMouse() const { return m_pFEMouse; }
+#endif
+
+#ifdef RAD_PS2
+ int GetLastMultitapStatus(int port) const {return mLastMultitapStatus[port];}
+ int GetCurMultitapStatus(int port) const {return mCurMultitapStatus[port];}
+#endif
+
+private:
+ InputManager();
+ ~InputManager();
+
+ // IRadControllerConnectionChangeCallback interface
+ // called when someone plugs in or pulls out a controller
+ void OnControllerConnectionStatusChange( IRadController* pIController );
+
+ void ReleaseAllControllers( void );
+ void EnumerateControllers( void );
+
+ static InputManager* spInstance;
+
+ ref< IRadControllerSystem > mxIControllerSystem2;
+
+ UserController mControllerArray[ Input::MaxControllers ];
+
+ unsigned mGameState;
+ bool mChangeGameState : 1;
+ bool mConnectStateChanged : 1;
+ bool mIsRumbleEnabled : 1;
+ bool mIsResetting : 1;
+ bool mResetEnabled : 1;
+
+ int m_registeredControllerID[ MAX_PLAYERS ];
+
+ unsigned int mResetTimeout;
+
+ bool m_isProScanButtonsPressed : 1;
+
+#ifdef RAD_WIN32
+ FEMouse* m_pFEMouse;
+#endif
+#ifdef RAD_PS2
+ int mLastMultitapStatus[2];
+ int mCurMultitapStatus[2];
+#endif
+};
+
+
+// A little syntactic sugar for getting at this singleton.
+inline InputManager* GetInputManager() { return( InputManager::GetInstance() ); }
+
+inline int
+InputManager::GetControllerIDforPlayer( int playerID ) const
+{
+ int controllerID;
+ rAssert( playerID >= 0 && playerID < MAX_PLAYERS );
+
+ if ( playerID >= 0 && playerID < MAX_PLAYERS )
+ {
+ controllerID = m_registeredControllerID[ playerID ];
+ }
+ else
+ {
+ // Return error code
+ controllerID = -1;
+ }
+ #ifndef RAD_RELEASE
+ if( m_registeredControllerID[ playerID ] == -1 )
+ {
+ // too much spew, we are polling this constantly for controller unplugged
+ // rTunePrintf( "*** WARNING: No controller ID registered for player %d!]\n",
+ // playerID + 1 );
+ }
+ #endif
+
+ return controllerID;
+}
+
+//=============================================================================
+// InputManager::GetControllerPlayerIDforController
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int controllerIndex )
+//
+// Return: int
+//
+//=============================================================================
+inline int InputManager::GetControllerPlayerIDforController( int controllerIndex ) const
+{
+ int i;
+ for ( i = 0; i < MAX_PLAYERS; ++i )
+ {
+ if ( m_registeredControllerID[ i ] == controllerIndex )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+//=============================================================================
+// InputManager::IsRumbleEnabled
+//=============================================================================
+// Description: Comment
+//
+// Parameters: none
+//
+// Return: bool
+//
+//=============================================================================
+inline bool InputManager::IsRumbleEnabled() const
+{
+ return mIsRumbleEnabled;
+}
+
+
+#endif
diff --git a/game/code/input/mappable.cpp b/game/code/input/mappable.cpp
new file mode 100644
index 0000000..d0fc73f
--- /dev/null
+++ b/game/code/input/mappable.cpp
@@ -0,0 +1,251 @@
+#include <input/mappable.h>
+
+#ifdef RAD_WIN32
+#include <input/usercontrollerWin32.h>
+#else
+#include <input/usercontroller.h>
+#endif
+
+#include <input/inputmanager.h>
+
+#include <raddebug.hpp>
+
+Mappable::Mappable( unsigned active)
+ :
+mButtonMask( 0 ),
+mActiveMapper( 0 ),
+mActiveState(active),
+mActive(true),
+mNeedResync(false)
+{
+}
+
+Mappable::~Mappable( void )
+{
+}
+
+// Route the Button changes through the Active Mapper.
+//
+void Mappable::DispatchOnButton( int controllerId, int id, const Button* pButton )
+{
+ if(!mActive)
+ {
+ return;
+ }
+
+ // perform a lookup on this physical id and set state of the appropriate logical button
+ int destButtonID = GetActiveMapper( )->GetLogicalIndex(id);
+
+ if ( destButtonID != Input::INVALID_CONTROLLERID && IsActive())
+ {
+ // Grab the state of the button before we update the state.
+ bool bWasButtonDown = IsButtonDown( destButtonID );
+ bool duplicate = mButton[ destButtonID ].TimeSinceChange() == 0;
+
+ if(!duplicate || (duplicate && ( pButton->GetValue( ) >= mButton[ destButtonID].GetValue( ) )))
+ {
+ UpdateButtonState( controllerId, destButtonID, pButton );
+
+ OnButton( controllerId, destButtonID, pButton );
+
+ if ( 0.0f == pButton->GetValue( ) )
+ {
+ if ( bWasButtonDown )
+ {
+ OnButtonUp( controllerId, destButtonID, pButton );
+ }
+ }
+ else
+ {
+ if ( !bWasButtonDown )
+ {
+ OnButtonDown( controllerId, destButtonID, pButton );
+ }
+ }
+ }
+ }
+}
+
+// load in a complete button state
+// we only update the state, no edge-trigerred stuff (OnButton*)
+void Mappable::InitButtons( int controllerId, const Button* pButtons )
+{
+ for(unsigned int i = 0; i < Input::MaxPhysicalButtons; i++)
+ {
+ int destButtonID = GetActiveMapper( )->GetLogicalIndex(i);
+
+ if ( destButtonID != Input::INVALID_CONTROLLERID )
+ {
+ UpdateButtonState( controllerId, destButtonID, &pButtons[i] );
+ }
+ }
+
+}
+
+void Mappable::OnControllerDisconnect( int id )
+{
+ this->Reset();
+}
+
+void Mappable::OnControllerConnect( int id )
+{
+}
+
+// Update the internal variables that track the button state.
+// Always tracks the button state, regardless of game state.
+// OnButton will only be called in an active state.
+// Values updated BEFORE OnButton is called.
+//
+void Mappable::UpdateButtonState( int controllerId, int buttonId, const Button* pButton )
+{
+ if(!mActive)
+ {
+ return;
+ }
+
+ unsigned int newButtonMask = 0;
+ unsigned int bit = 1 << buttonId;
+
+ mButton[ buttonId ] = *pButton;
+ mButton[ buttonId ].ForceChange();
+
+ if ( 0.0f != mButton[ buttonId ].GetValue( ) )
+ {
+ newButtonMask = bit | mButtonMask;
+ }
+ else
+ {
+ bit = ~bit;
+ newButtonMask = bit & mButtonMask;
+ }
+ mButtonMask = newButtonMask;
+}
+
+// Returns the value of the logical output referenced by 'index'.
+//
+float Mappable::GetValue( unsigned int index ) const
+{
+ rAssert( index < Input::MaxLogicalButtons );
+ if ( index < Input::MaxLogicalButtons )
+ {
+ return mButton[ index ].GetValue( );
+ }
+
+ return 0.0f;
+}
+
+
+// Returns a const pointer to the Button at index.
+//
+Button* Mappable::GetButton( unsigned int index )
+{
+ return &mButton [ index ];
+}
+
+
+// To set up the different controller configurations.
+//
+void Mappable::SetControlMap( unsigned map )
+{
+ mActiveMapper = map;
+}
+
+// Returns the current control map enumeration value.
+//
+unsigned Mappable::GetControlMap( void ) const
+{
+ return mActiveMapper;
+}
+
+// Returns a Mapper by index.
+//
+Mapper* Mappable::GetMapper( unsigned int index )
+{
+ return &mMapper[ index ];
+}
+
+// Returns the Active mapper.
+//
+Mapper* Mappable::GetActiveMapper( void )
+{
+ return &mMapper[ mActiveMapper ];
+}
+
+void Mappable::SetGameState(unsigned state)
+{
+ if(state & mActiveState)
+ {
+ mNeedResync = !mActive;
+ mActive = true;
+ }
+ else
+ {
+ mNeedResync = false;
+ mActive = false;
+ Reset();
+ }
+}
+
+// Is this mappable object active in this game state.
+//
+bool Mappable::IsActive( void ) const
+{
+ return mActive;
+}
+
+// Is the button down?
+//
+bool Mappable::IsButtonDown( unsigned int id )
+{
+ unsigned int bit = 1 << id;
+ return ( mButtonMask & bit ) != 0;
+}
+
+// Is the button up?
+//
+bool Mappable::IsButtonUp( unsigned int id )
+{
+ return !IsButtonDown( id );
+}
+
+// set up controller mappings. return false if cannot find name.
+//
+bool Mappable::Map( const char* pszName, int destination, unsigned int mapperIndex, unsigned int controllerId )
+{
+ Mapper* pMapper = this->GetMapper( mapperIndex );
+ //rValid( pMapper );
+
+ InputManager* pInputManager = InputManager::GetInstance( );
+ rAssert( pInputManager );
+
+ const UserController* pUserController = pInputManager->GetController( controllerId );
+ //rValid( pUserController );
+
+ int source = pUserController->GetIdByName( pszName );
+ if ( source >=0 )
+ {
+ pMapper->SetAssociation( source, destination );
+ return true;
+ }
+ return false;
+}
+
+// Clear all associations.
+//
+void Mappable::ClearMap( unsigned int mapperIndex )
+{
+ Mapper* pMapper = GetMapper( mapperIndex );
+ //rValid( pMapper );
+
+ pMapper->ClearAssociations( );
+}
+
+void Mappable::Reset( void )
+{
+ for( unsigned int i = 0; i < Input::MaxLogicalButtons; i++)
+ {
+ mButton[i].SetValue(0.0f);
+ }
+
+ mButtonMask = 0;
+} \ No newline at end of file
diff --git a/game/code/input/mappable.h b/game/code/input/mappable.h
new file mode 100644
index 0000000..23d7c17
--- /dev/null
+++ b/game/code/input/mappable.h
@@ -0,0 +1,138 @@
+#ifndef MAPPABLE_HPP_
+#define MAPPABLE_HPP_
+
+#include <input/controller.h>
+#include <input/button.h>
+#include <input/mapper.h>
+
+#include <p3d/refcounted.hpp>
+
+struct ControlMap;
+class UserController;
+
+class Mappable
+:
+public tRefCounted
+{
+public:
+ Mappable( unsigned active = Input::ACTIVE_ALL & ~Input::ACTIVE_ANIM_CAM );
+
+ virtual ~Mappable( void );
+
+ //-------------------------------------------
+ //Stuff that derived classes should override
+ //-------------------------------------------
+
+ // This method is called when ever a button is active (i.e. is non zero, or just went zero).
+ virtual void OnButton( int controllerId, int id, const Button* pButton ) = 0;
+
+ // This method is called when a button changes state from "Pressed" to "Released".
+ virtual void OnButtonUp( int controllerId, int buttonId, const Button* pButton ) = 0;
+
+ // This method is called when a button changes state from "Released" to "Pressed".
+ virtual void OnButtonDown( int controllerId, int buttonId, const Button* pButton ) = 0;
+
+ // This is how we create our controller device mappings to logical game mappings.
+ // The mappings set up in this method are platform specific.
+ //
+ // The basic format of the calls is to "Map" a input, to a enumerated output id.
+ // The output of the specified input will be contained in the Button[] array.
+ // This id will also be sent as a the second parameter in the OnButton... messages.
+ virtual void LoadControllerMappings( unsigned int controllerId ) = 0;
+
+ // should be overriden by dewrived calasses to do something if the controller
+ // attached to this mappable unplugs.
+ // there is a default implementation which does nothing.
+ virtual void OnControllerDisconnect( int id );
+
+ // should be overriden by dewrived calasses to do something if the controller
+ // attached to this mappable plugs in.
+ // there is a default implementation which does nothing.
+ virtual void OnControllerConnect( int id );
+
+ // ---------------------
+ // Regular functions
+ // ---------------------
+
+ // Route the Button changes through the Mappers.
+ void DispatchOnButton( int controllerId, int id, const Button* pButton );
+
+ // load a complete physical button state
+ // This will set the internal state, but not generate any events (OnButton*)
+ void InitButtons( int controllerId, const Button* pButtons );
+
+ // Is the button down?
+ bool IsButtonDown( unsigned int id );
+
+ // Is the button up?
+ bool IsButtonUp( unsigned int id );
+
+ // Update the internal variables that track the button state.
+ // Always tracks the button state, regardless of game state.
+ // OnButton will only be called in an active state.
+ // Values updated BEFORE OnButton is called.
+ void UpdateButtonState( int controllerId, int id, const Button* pButton );
+
+ // set the current game state
+ void SetGameState(unsigned state);
+
+ // Returns true if the mappable is active in the current game state.
+ bool IsActive( void ) const;
+
+ // Returns the value of the logical output referenced by 'index'.
+ // Hint: make an enum with your own Logical Names.
+ float GetValue( unsigned int index ) const;
+
+ // Returns a pointer to the Button at index.
+ Button* GetButton( unsigned int index );
+
+ // To set up the different controller configurations.
+ void SetControlMap( unsigned map );
+
+ // Returns the current control map enumeration value.
+ //
+ unsigned GetControlMap( void ) const;
+
+ // Reset internal state (all vutton values go to 0)
+ void Reset( void );
+
+ bool GetResync(void) { return mNeedResync;}
+ void SetResync(bool b) { mNeedResync = b;}
+
+ // Returns a Mapper by index.
+ //
+ Mapper* GetMapper( unsigned int index );
+
+protected:
+ // set up controller mappings. return false if cannot find name.
+ //
+ bool Map( const char* pszName, int destination, unsigned int mapperIndex, unsigned int controllerId );
+
+ // Remove all associations.
+ //
+ void ClearMap( unsigned int mapperIndex );
+
+private:
+
+ // Returns the Active mapper.
+ //
+ Mapper* GetActiveMapper( void );
+ // A 32 Bit button mask.
+ //
+ unsigned int mButtonMask;
+
+ // State for all the (logical) buttons.
+ Button mButton[ Input::MaxLogicalButtons ];
+
+ // The button map.
+ Mapper mMapper[ Input::MaxMappings];
+
+ // The index of the currently active mapper.
+ unsigned mActiveMapper;
+
+ unsigned mActiveState;
+ bool mActive;
+ bool mNeedResync;
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/input/mapper.cpp b/game/code/input/mapper.cpp
new file mode 100644
index 0000000..107749f
--- /dev/null
+++ b/game/code/input/mapper.cpp
@@ -0,0 +1,39 @@
+#include <input/mapper.h>
+#include <input/mappable.h>
+
+Mapper::Mapper( void )
+{
+ ClearAssociations( );
+}
+
+int Mapper::SetAssociation( int sourceButtonID, int destButtonID )
+{
+ buttonMap[sourceButtonID] = destButtonID;
+ return 0;
+}
+
+int Mapper::GetLogicalIndex( int sourceButtonID ) const
+{
+ return buttonMap[sourceButtonID];
+}
+
+int Mapper::GetPhysicalIndex( int destButtonID ) const
+{
+ for ( unsigned i = 0; i < Input::MaxLogicalButtons; i++ )
+ {
+ if ( buttonMap[i] == destButtonID)
+ {
+ return i;
+ }
+ }
+ return Input::INVALID_CONTROLLERID;
+}
+
+
+void Mapper::ClearAssociations( void )
+{
+ for( unsigned int i = 0; i < Input::MaxLogicalButtons; i++)
+ {
+ buttonMap[i] = Input::INVALID_CONTROLLERID;
+ }
+}
diff --git a/game/code/input/mapper.h b/game/code/input/mapper.h
new file mode 100644
index 0000000..4f93f5a
--- /dev/null
+++ b/game/code/input/mapper.h
@@ -0,0 +1,40 @@
+#ifndef MAPPER_HPP
+#define MAPPER_HPP
+
+#include <input/controller.h>
+#include <input/button.h>
+
+class Mappable;
+
+// Holds mappings from physical inputs to logical inputs
+//
+// All inputs from controller system are passed through this table by the base Mappable
+// class before being passed on to derived classes (for a particular gameplay state)
+class Mapper
+{
+public:
+ Mapper( void );
+
+ // Associate sourceButtonID with destButtonID.
+ int SetAssociation( int physicalIndex, int logicalIndex);
+
+ // Remove all mappings. Set num associations to zero.
+ void ClearAssociations( void );
+
+ // Will return the physical button id ("X") associated with the logical
+ // input ("Gas"). Useful for controller config.
+ // Warning, could be miltiple mappings, it will only return the first one
+ int GetPhysicalIndex( int logicalIndex) const;
+
+ // Will return the logical button id ("Gas") associated with the logical
+ // input ("X"). Useful for controller config.
+ int GetLogicalIndex( int physicalIndex ) const;
+
+private:
+ // Association structure and members
+ int buttonMap[Input::MaxPhysicalButtons];
+};
+
+
+#endif
+
diff --git a/game/code/input/rumbleeffect.cpp b/game/code/input/rumbleeffect.cpp
new file mode 100644
index 0000000..8ecfcf4
--- /dev/null
+++ b/game/code/input/rumbleeffect.cpp
@@ -0,0 +1,464 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: rumbleeffect.cpp
+//
+// Description: Implement RumbleEffect
+//
+// History: 12/19/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/rumbleeffect.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+#ifdef DEBUGWATCH
+int gRECount = -1;
+#endif
+
+extern EffectValue VALUES[];
+
+extern EffectValue DYNA_VALUES[];
+
+float GAIN_FUDGE = 25.0f;
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// RumbleEffect::RumbleEffect
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+RumbleEffect::RumbleEffect() :
+ mWheelEffect( NULL )
+{
+ unsigned int i;
+ for ( i = 0; i < Input::MaxOutputMotor; ++i )
+ {
+ mMotors[ i ] = NULL;
+ mMotorUpdated[ i ] = false;
+ }
+
+#ifdef DEBUGWATCH
+ ++gRECount;
+ mEffectNum = gRECount;
+ char nameSpace[256];
+ sprintf( nameSpace, "Controller\\RumbleEffect%d", mEffectNum );
+
+ char name[256];
+ for ( i = 0; i < NUM_EFFECTS; ++i )
+ {
+ sprintf( name, "%s Effect", VALUES[i].name );
+ radDbgWatchAddUnsignedInt( &mCurrentEffects[i].mRumbleTimeLeft, name, nameSpace, NULL, NULL, 0, 100000 );
+ }
+
+ for ( i = 0; i < NUM_DYNA_EFFECTS; ++i )
+ {
+ sprintf( name, "%s Dyna Effect", DYNA_VALUES[i].name );
+ radDbgWatchAddUnsignedInt( &mCurrentDynaEffects[i].mRumbleTimeLeft, name, nameSpace, NULL, NULL, 0, 100000 );
+ radDbgWatchAddFloat( &mCurrentDynaEffects[i].mMaxGain, name, nameSpace, NULL, NULL, 0.0f, 1.0f );
+ }
+#endif
+
+ InitEffects();
+}
+
+//=============================================================================
+// RumbleEffect::~RumbleEffect
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+RumbleEffect::~RumbleEffect()
+{
+ unsigned int i;
+ for ( i = 0; i < Input::MaxOutputMotor; ++i )
+ {
+ if( mMotors[ i ] )
+ {
+ mMotors[ i ]->Release();
+ mMotors[ i ] = NULL;
+ }
+ }
+
+#ifdef DEBUGWATCH
+ gRECount--;
+ for ( i = 0; i < NUM_EFFECTS; ++i )
+ {
+ radDbgWatchDelete( &mCurrentEffects[i].mRumbleTimeLeft );
+ }
+
+ for ( i = 0; i < NUM_DYNA_EFFECTS; ++i )
+ {
+ radDbgWatchDelete( &mCurrentDynaEffects[i].mRumbleTimeLeft );
+ radDbgWatchDelete( &mCurrentDynaEffects[i].mMaxGain );
+ }
+#endif
+
+ ShutDownEffects();
+}
+
+//=============================================================================
+// RumbleEffect::SetMotor
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int whichMotor, IRadControllerOutputPoint* motor )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetMotor( unsigned int whichMotor, IRadControllerOutputPoint* motor )
+{
+ rAssert( whichMotor < Input::MaxOutputMotor );
+
+ if ( mMotors[ whichMotor ] )
+ {
+ mMotors[ whichMotor ]->Release();
+ }
+
+ mMotors[ whichMotor ] = motor;
+ mMotors[ whichMotor ]->AddRef();
+}
+
+//=============================================================================
+// RumbleEffect::SetEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Effect effect, unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetEffect( Effect effect, unsigned int milliseconds )
+{
+ if ( mCurrentEffects[ effect ].mRumbleTimeLeft < milliseconds )
+ {
+ mCurrentEffects[ effect ].mRumbleTimeLeft = milliseconds;
+ }
+}
+
+//=============================================================================
+// RumbleEffect::SetDynaEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( DynaEffect effect, unsigned int milliseconds, float gain )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetDynaEffect( DynaEffect effect, unsigned int milliseconds, float gain )
+{
+ if ( mCurrentDynaEffects[ effect ].mRumbleTimeLeft < milliseconds )
+ {
+ mCurrentDynaEffects[ effect ].mRumbleTimeLeft = milliseconds;
+
+ if ( gain > 1.0f )
+ {
+ gain = 1.0f;
+ }
+
+ mCurrentDynaEffects[ effect ].mMaxGain = gain * GAIN_FUDGE;
+
+#ifdef RAD_GAMECUBE
+ if ( gain * GAIN_FUDGE < 0.3f )
+ {
+ mCurrentDynaEffects[ effect ].mMaxGain = 0;
+ }
+#endif
+ }
+}
+
+//=============================================================================
+// RumbleEffect::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::Update( unsigned int milliseconds )
+{
+ //The heaviest effect with time left is the one that runs.
+ Effect currentEffect = NUM_EFFECTS;
+
+ unsigned int i;
+
+ //Update all the effects.
+ for ( i = 0; i < NUM_EFFECTS; ++i )
+ {
+ if ( mCurrentEffects[ i ].mRumbleTimeLeft <= milliseconds )
+ {
+ if ( mCurrentEffects[ i ].mRumbleTimeLeft > 0 )
+ {
+ UpdateEffect( static_cast<Effect>(i), mCurrentEffects[ i ].mRumbleTimeLeft );
+ }
+
+ mCurrentEffects[ i ].mRumbleTimeLeft = 0;
+ }
+ else
+ {
+ mCurrentEffects[ i ].mRumbleTimeLeft -= milliseconds;
+
+ UpdateEffect( static_cast<Effect>(i), milliseconds );
+ }
+ }
+
+ for ( i = 0; i < NUM_DYNA_EFFECTS; ++i )
+ {
+ if ( mCurrentDynaEffects[ i ].mRumbleTimeLeft <= milliseconds )
+ {
+ if ( mCurrentDynaEffects[ i ].mRumbleTimeLeft > 0 )
+ {
+ UpdateDynaEffect( static_cast<DynaEffect>(i), mCurrentDynaEffects[ i ].mRumbleTimeLeft, mCurrentDynaEffects[ i ].mMaxGain );
+ }
+
+ mCurrentDynaEffects[ i ].mRumbleTimeLeft = 0;
+ }
+ else
+ {
+ mCurrentDynaEffects[ i ].mRumbleTimeLeft -= milliseconds;
+
+ UpdateDynaEffect( static_cast<DynaEffect>(i), milliseconds, mCurrentDynaEffects[ i ].mMaxGain );
+ }
+ }
+
+ for ( i = 0; i < Input::MaxOutputMotor; ++i )
+ {
+ //You only update a motor when you want to give it a positive value.
+ if ( !mMotorUpdated[ i ] )
+ {
+ if ( mMotors[ i ]&& mMotors[ i ]->GetGain() > 0.0f )
+ {
+#ifdef RAD_GAMECUBE
+ mMotors[ i ]->SetGain( -1.0f ); //This stops the motor HARD
+#else
+ mMotors[ i ]->SetGain( 0.0f );
+#endif
+ }
+ }
+
+ //Reset them all.
+ mMotorUpdated[ i ] = false;
+ }
+}
+
+//=============================================================================
+// RumbleEffect::ShutDownEffects
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::ShutDownEffects()
+{
+ unsigned int i;
+ for ( i = 0; i < NUM_EFFECTS; ++i )
+ {
+ mCurrentEffects[ i ].mRumbleTimeLeft = 0;
+ }
+
+ for ( i = 0; i < Input::MaxOutputMotor; ++i )
+ {
+ if ( mMotors[ i ] && mMotors[ i ]->GetGain() > 0.0f )
+ {
+#ifdef RAD_GAMECUBE
+ mMotors[ i ]->SetGain( -1.0f ); //This stops the motor HARD
+#else
+ mMotors[ i ]->SetGain( 0.0f );
+#endif
+ }
+ }
+
+ OnShutDownEffects();
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// RumbleEffect::UpdateEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Effect effect, unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::UpdateEffect( Effect effect, unsigned int milliseconds )
+{
+ if ( mCurrentEffects[ effect ].mRumbleTimeLeft % VALUES[effect].pulseTime < 32 ) //We use 32 because we hope to only do this once a frame (or twice)
+ {
+ if ( mMotors[ VALUES[effect].motor ] )
+ {
+ float currMotorGain = mMotors[ VALUES[effect].motor ]->GetGain();
+ float desiredGain = VALUES[effect].gain;
+
+ if ( currMotorGain < desiredGain )
+ {
+ mMotors[ VALUES[effect].motor ]->SetGain( VALUES[effect].gain );
+ mMotorUpdated[ VALUES[effect].motor ] = true;
+ }
+#ifdef RAD_GAMECUBE
+ // Michael Riegger - Gamecube is different from other platforms in rumble
+ // control in that motor control is basically turn on / turn off commands
+ // and that rumble just 'goes' in between those commands. No need to
+ // toggle on and off manually
+ // so set the updated flag to true always if its where we want. Otherwise
+ // gain will be reset to 0 or -1 automatically
+ else if ( currMotorGain == desiredGain )
+ {
+ mMotorUpdated[ VALUES[effect].motor ] = true;
+ }
+#endif
+ }
+ }
+ /* if ( mCurrentEffects[ effect ].mRumbleTimeLeft % VALUES[effect].pulseTime < 32 ) //We use 32 because we hope to only do this once a frame (or twice)
+ {
+ if ( mMotors[ VALUES[effect].motor ] )
+ {
+
+
+ if ( currMotorGain == desiredGain )
+ {
+ mMotorUpdated[ VALUES[effect].motor ] = true;
+ }
+ else if ( currMotorGain < desiredGain )
+ {
+ mMotors[ VALUES[effect].motor ]->SetGain( VALUES[effect].gain );
+ mMotorUpdated[ VALUES[effect].motor ] = true;
+ }
+ }
+ }*/
+}
+
+//=============================================================================
+// RumbleEffect::UpdateDynaEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( DynaEffect effect, unsigned int milliseconds, float gain )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::UpdateDynaEffect( DynaEffect effect, unsigned int milliseconds, float gain ) //This is a pcnt
+{
+ if ( mCurrentDynaEffects[ effect ].mRumbleTimeLeft % DYNA_VALUES[effect].pulseTime < 32 ) //We use 32 because we hope to only do this once a frame (or twice)
+ {
+ if ( mMotors[ DYNA_VALUES[effect].motor ] &&
+ mMotors[ DYNA_VALUES[effect].motor ]->GetGain() < DYNA_VALUES[effect].gain * gain )
+ {
+ mMotors[ DYNA_VALUES[effect].motor ]->SetGain( DYNA_VALUES[effect].gain * gain );
+ mMotorUpdated[ DYNA_VALUES[effect].motor ] = true;
+ }
+ }
+}
+
+
+//=============================================================================
+// RumbleEffect::InitEffects
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::InitEffects()
+{
+#ifdef DEBUGWATCH
+ if ( gRECount == 0 )
+ {
+ char name[256];
+ unsigned int i;
+ for ( i = 0; i < NUM_EFFECTS; ++i )
+ {
+ sprintf( name, "Controller\\%s Effect", VALUES[i].name );
+ radDbgWatchAddUnsignedInt( &VALUES[i].pulseTime, "PulseTime", name, NULL, NULL, 1, 1000 );
+ radDbgWatchAddFloat( &VALUES[i].gain, "Gain", name, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddChar( &VALUES[i].motor, "Motor", name, NULL, NULL, 0, 1 );
+ }
+
+ for ( i = 0; i < NUM_DYNA_EFFECTS; ++i )
+ {
+ sprintf( name, "Controller\\%s Dyna Effect", VALUES[i].name );
+ radDbgWatchAddUnsignedInt( &DYNA_VALUES[i].pulseTime, "PulseTime", name, NULL, NULL, 1, 1000 );
+ radDbgWatchAddFloat( &DYNA_VALUES[i].gain, "Gain", name, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddChar( &DYNA_VALUES[i].motor, "Motor", name, NULL, NULL, 0, 1 );
+ }
+ }
+#endif
+}
+
+//=============================================================================
+// RumbleEffect::OnShutDownEffects
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::OnShutDownEffects()
+{
+#ifdef DEBUGWATCH
+ if ( gRECount == -1 )
+ {
+ unsigned int i;
+ for ( i = 0; i < NUM_EFFECTS; ++i )
+ {
+ radDbgWatchDelete( &VALUES[i].pulseTime );
+ radDbgWatchDelete( &VALUES[i].gain );
+ radDbgWatchDelete( &VALUES[i].motor );
+ }
+
+ for ( i = 0; i < NUM_DYNA_EFFECTS; ++i )
+ {
+ radDbgWatchDelete( &DYNA_VALUES[i].pulseTime );
+ radDbgWatchDelete( &DYNA_VALUES[i].gain );
+ radDbgWatchDelete( &DYNA_VALUES[i].motor );
+ }
+ }
+#endif
+} \ No newline at end of file
diff --git a/game/code/input/rumbleeffect.h b/game/code/input/rumbleeffect.h
new file mode 100644
index 0000000..bd5746d
--- /dev/null
+++ b/game/code/input/rumbleeffect.h
@@ -0,0 +1,116 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: rumbleeffect.h
+//
+// Description: Blahblahblah
+//
+// History: 12/19/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef RUMBLEEFFECT_H
+#define RUMBLEEFFECT_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radcontroller.hpp>
+
+#include <input/controller.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+struct EffectValue
+{
+ unsigned int pulseTime;
+ float gain;
+ char motor;
+ const char* name;
+};
+
+class RumbleEffect
+{
+public:
+ enum Effect
+ {
+ LIGHT,
+ MEDIUM,
+ HARD1,
+ HARD2,
+ GROUND1,
+ GROUND2,
+ GROUND3,
+ GROUND4,
+ PULSE,
+ NUM_EFFECTS
+ };
+
+ enum DynaEffect
+ {
+ COLLISION1,
+ COLLISION2,
+ NUM_DYNA_EFFECTS
+ };
+
+ RumbleEffect();
+ virtual ~RumbleEffect();
+
+ void SetWheelEffect( IRadControllerOutputPoint* wheelEffect );
+ void SetMotor( unsigned int whichMotor, IRadControllerOutputPoint* motor );
+ void SetEffect( Effect effect, unsigned int milliseconds );
+ void SetDynaEffect( DynaEffect effect, unsigned int milliseconds, float gain );
+
+ void Update( unsigned int milliseconds );
+ void ShutDownEffects();
+
+private:
+ IRadControllerOutputPoint* mWheelEffect;
+ IRadControllerOutputPoint* mMotors[ Input::MaxOutputMotor ];
+ bool mMotorUpdated[ Input::MaxOutputMotor ];
+
+ struct EffectInfo
+ {
+ EffectInfo() : mRumbleTimeLeft( 0 ) {};
+ unsigned int mRumbleTimeLeft;
+ };
+
+ EffectInfo mCurrentEffects[ NUM_EFFECTS ];
+
+ struct DynaEffectInfo : public EffectInfo
+ {
+ DynaEffectInfo() : mMaxGain( 1.0f ) {};
+ float mMaxGain;
+ };
+
+ DynaEffectInfo mCurrentDynaEffects[ NUM_DYNA_EFFECTS ];
+
+#ifdef DEBUGWATCH
+ int mEffectNum;
+#endif
+
+ void InitEffects();
+ void UpdateEffect( Effect effect, unsigned int milliseconds );
+ void UpdateDynaEffect( DynaEffect effect, unsigned int milliseconds, float gain );
+ void OnShutDownEffects();
+
+ //Prevent wasteful constructor creation.
+ RumbleEffect( const RumbleEffect& rumbleeffect );
+ RumbleEffect& operator=( const RumbleEffect& rumbleeffect );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //RUMBLEEFFECT_H
diff --git a/game/code/input/rumblegc.cpp b/game/code/input/rumblegc.cpp
new file mode 100644
index 0000000..9de065b
--- /dev/null
+++ b/game/code/input/rumblegc.cpp
@@ -0,0 +1,49 @@
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <input/rumbleeffect.h>
+
+#ifdef DEBUGWATCH
+extern int gRECount;
+#endif
+
+EffectValue VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 0.6f, 0, "Light" },
+ { 1, 0.8f, 0, "Med" },
+ { 1, 1.0f, 0, "Hard1" },
+ { 1, 1.0f, 0, "Hard2" },
+ { 100, 0.3f, 0, "Ground 1" },
+ { 120, 0.4f, 0, "Ground 2" },
+ { 150, 0.35f, 0, "Ground 3" },
+ { 120, 0.35f, 0, "Ground 4" },
+ { 1, 1.0f, 0, "Pulse" },
+};
+
+EffectValue DYNA_VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 1.0f, 0, "Collision1" },
+ { 1, 1.0f, 0, "Collision2" }
+};
+
+//=============================================================================
+// RumbleEffect::SetWheelEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( IRadControllerOutputPoint* wheelEffect )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetWheelEffect( IRadControllerOutputPoint* wheelEffect )
+{
+}
+
+//*****************************************************************************
+//
+//Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/input/rumbleps2.cpp b/game/code/input/rumbleps2.cpp
new file mode 100644
index 0000000..5b15999
--- /dev/null
+++ b/game/code/input/rumbleps2.cpp
@@ -0,0 +1,44 @@
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <input/rumbleeffect.h>
+
+#ifdef DEBUGWATCH
+extern int gRECount;
+#endif
+
+EffectValue VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 0.6f, 1, "Light" },
+ { 1, 0.8f, 1, "Med" },
+ { 1, 1.0f, 1, "Hard1" },
+ { 1, 1.0f, 0, "Hard2" },
+ { 100, 0.3f, 1, "Ground 1" },
+ { 120, 0.4f, 1, "Ground 2" },
+ { 150, 0.35f, 1, "Ground 3" },
+ { 120, 0.35f, 1, "Ground 4" },
+ { 1, 1.0f, 0, "Pulse" },
+};
+
+EffectValue DYNA_VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 1.0f, 0, "Collision1" },
+ { 1, 1.0f, 1, "Collision2" }
+};
+
+
+//=============================================================================
+// RumbleEffect::SetWheelEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( IRadControllerOutputPoint* wheelEffect )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetWheelEffect( IRadControllerOutputPoint* wheelEffect )
+{
+} \ No newline at end of file
diff --git a/game/code/input/rumblewin32.cpp b/game/code/input/rumblewin32.cpp
new file mode 100644
index 0000000..963b607
--- /dev/null
+++ b/game/code/input/rumblewin32.cpp
@@ -0,0 +1,68 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: rumblewin32.cpp
+//
+// Description: Implementation of rumble for win. This is probably just a
+// dummy file.. we need to define some constants for rumble to
+// compile, but who knows if we'll need them.
+//
+// History: 03/04/2003 + Created -- ziemek
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <input/rumbleeffect.h>
+
+#ifdef DEBUGWATCH
+extern int gRECount;
+#endif
+
+EffectValue VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 0.6f, 1, "Light" },
+ { 1, 0.8f, 1, "Med" },
+ { 1, 1.0f, 1, "Hard1" },
+ { 1, 1.0f, 0, "Hard2" },
+ { 100, 0.3f, 1, "Ground 1" },
+ { 120, 0.4f, 1, "Ground 2" },
+ { 150, 0.35f, 1, "Ground 3" },
+ { 120, 0.35f, 1, "Ground 4" },
+ { 1, 1.0f, 0, "Pulse" },
+};
+
+EffectValue DYNA_VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 1.0f, 0, "Collision1" },
+ { 1, 1.0f, 1, "Collision2" }
+};
+
+
+//=============================================================================
+// RumbleEffect::SetWheelEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( IRadControllerOutputPoint* wheelEffect )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetWheelEffect( IRadControllerOutputPoint* wheelEffect )
+{
+}
+
+//*****************************************************************************
+//
+//Private Member Functions
+//
+//*****************************************************************************
+
diff --git a/game/code/input/rumblexbox.cpp b/game/code/input/rumblexbox.cpp
new file mode 100644
index 0000000..276351b
--- /dev/null
+++ b/game/code/input/rumblexbox.cpp
@@ -0,0 +1,51 @@
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <input/rumbleeffect.h>
+
+#ifdef DEBUGWATCH
+extern int gRECount;
+#endif
+
+EffectValue VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 0.6f, 0, "Light" },
+ { 1, 0.8f, 0, "Med" },
+ { 1, 1.0f, 0, "Hard1" },
+ { 1, 1.0f, 1, "Hard2" },
+ { 100, 0.9f, 1, "Ground 1" },
+ { 120, 1.0f, 1, "Ground 2" },
+ { 150, 0.95f, 1, "Ground 3" },
+ { 120, 0.95f, 1, "Ground 4" },
+ { 1, 1.0f, 1, "Pulse" },
+};
+
+EffectValue DYNA_VALUES[] =
+{
+ // timeout gain motor name - Note: Do not change motor here! It is platform specific.
+ { 1, 1.0f, 1, "Collision1" },
+ { 1, 1.0f, 1, "Collision2" }
+};
+
+
+//=============================================================================
+// RumbleEffect::SetWheelEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( IRadControllerOutputPoint* wheelEffect )
+//
+// Return: void
+//
+//=============================================================================
+void RumbleEffect::SetWheelEffect( IRadControllerOutputPoint* wheelEffect )
+{
+}
+
+//*****************************************************************************
+//
+//Private Member Functions
+//
+//*****************************************************************************
+
diff --git a/game/code/input/steeringspring.cpp b/game/code/input/steeringspring.cpp
new file mode 100644
index 0000000..d78d4f0
--- /dev/null
+++ b/game/code/input/steeringspring.cpp
@@ -0,0 +1,187 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: SteeringSpring.cpp
+//
+// Description: Implement SteeringSpring
+//
+// History: 10/21/2002 + Created -- Cary Brisebois
+// Modified -- Neil Haran
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/SteeringSpring.h>
+#include <input/wheeldefines.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SteeringSpring::SteeringSpring
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SteeringSpring::SteeringSpring()
+{
+ //Setup the respective force effect structures.
+#ifdef RAD_WIN32
+ m_conditon.lOffset = 0;
+ m_conditon.lDeadBand = 0;
+ m_conditon.lPositiveCoefficient = 127;
+ m_conditon.lNegativeCoefficient = 127;
+ m_conditon.dwPositiveSaturation = 127;
+ m_conditon.dwNegativeSaturation = 127;
+
+ mForceEffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
+ mForceEffect.dwDuration = INFINITE;
+ mForceEffect.dwGain = DI_FFNOMINALMAX;
+ mForceEffect.dwTriggerButton = DIEB_NOTRIGGER;
+ mForceEffect.cAxes = 1;
+ mForceEffect.rgdwAxes = m_rgdwAxes;
+ mForceEffect.rglDirection = m_rglDirection;
+ mForceEffect.lpEnvelope = NULL;
+ mForceEffect.cbTypeSpecificParams = sizeof(DICONDITION);
+ mForceEffect.lpvTypeSpecificParams = &m_conditon;
+ mForceEffect.dwStartDelay = 0;
+#else
+ mForceEffect.type = LG_TYPE_SPRING;
+ mForceEffect.duration = LG_DURATION_INFINITE;
+ mForceEffect.startDelay = 0;
+ mForceEffect.p.condition[0].offset = 0;
+ mForceEffect.p.condition[0].deadband = 0;
+ mForceEffect.p.condition[0].saturationNeg = 127;
+ mForceEffect.p.condition[0].saturationPos = 127;
+ mForceEffect.p.condition[0].coefficientNeg = 127;
+ mForceEffect.p.condition[0].coefficientPos = 127;
+#endif
+}
+
+//==============================================================================
+// SteeringSpring::~SteeringSpring
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SteeringSpring::~SteeringSpring()
+{
+}
+
+//=============================================================================
+// SteeringSpring::OnInit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SteeringSpring::OnInit()
+{
+}
+
+//=============================================================================
+// SteeringSpring::SetCenterPoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( s8 point, u8 deadband )
+//
+// Return: void
+//
+//=============================================================================
+void SteeringSpring::SetCenterPoint( s8 point, u8 deadband )
+{
+#ifdef RAD_WIN32
+ m_conditon.lOffset = point;
+ m_conditon.lDeadBand = deadband;
+#else
+ mForceEffect.p.condition[0].offset = point;
+ mForceEffect.p.condition[0].deadband = deadband;
+#endif
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// SteeringSpring::SetSpringStrength
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( u8 strength )
+//
+// Return: void
+//
+//=============================================================================
+#ifdef RAD_WIN32
+void SteeringSpring::SetSpringStrength( u16 strength )
+#else
+void SteeringSpring::SetSpringStrength( u8 strength )
+#endif
+{
+#ifdef RAD_WIN32
+ m_conditon.dwPositiveSaturation = strength;
+ m_conditon.dwNegativeSaturation = strength;
+#else
+ mForceEffect.p.condition[0].saturationNeg = strength;
+ mForceEffect.p.condition[0].saturationPos = strength;
+#endif
+
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// SteeringSpring::SetSpringCoefficient
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( s16 coeff )
+//
+// Return: void
+//
+//=============================================================================
+void SteeringSpring::SetSpringCoefficient( s16 coeff )
+{
+#ifdef RAD_WIN32
+ m_conditon.lPositiveCoefficient = coeff;
+ m_conditon.lNegativeCoefficient = coeff;
+#else
+ mForceEffect.p.condition[0].coefficientNeg = coeff;
+ mForceEffect.p.condition[0].coefficientPos = coeff;
+#endif
+
+ mEffectDirty = true;
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/input/steeringspring.h b/game/code/input/steeringspring.h
new file mode 100644
index 0000000..d28530f
--- /dev/null
+++ b/game/code/input/steeringspring.h
@@ -0,0 +1,59 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: steeringspring.h
+//
+// Description: Blahblahblah
+//
+// History: 10/19/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef STEERINGSPRING_H
+#define STEERINGSPRING_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radcontroller.hpp>
+#include <input/forceeffect.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class SteeringSpring : public ForceEffect
+{
+public:
+ SteeringSpring();
+ virtual ~SteeringSpring();
+
+ void OnInit();
+ void SetCenterPoint( s8 degrees, u8 deadband ); //Where 0 is straight up.
+#ifdef RAD_WIN32
+ void SetSpringStrength( u16 strength );
+#else
+ void SetSpringStrength( u8 strength );
+#endif
+ void SetSpringCoefficient( s16 coeff );
+
+private:
+
+#ifdef RAD_WIN32
+ DICONDITION m_conditon;
+#endif
+ //Prevent wasteful constructor creation.
+ SteeringSpring( const SteeringSpring& steeringspring );
+ SteeringSpring& operator=( const SteeringSpring& steeringspring );
+};
+
+
+#endif //STEERINGSPRING_H
+
+
diff --git a/game/code/input/usercontroller.cpp b/game/code/input/usercontroller.cpp
new file mode 100644
index 0000000..e740374
--- /dev/null
+++ b/game/code/input/usercontroller.cpp
@@ -0,0 +1,726 @@
+#include <input/usercontroller.h>
+#include <input/mappable.h>
+#include <input/mapper.h>
+#include <input/button.h>
+
+#include <radcontroller.hpp>
+#include <raddebug.hpp>
+#include <radmath/radmath.hpp>
+
+#if defined(RAD_PS2) || defined(RAD_GAMECUBE)
+#include <input/wheeldefines.h>
+#endif
+
+
+
+#ifndef WORLD_BUILDER
+#include <main/commandlineoptions.h>
+#endif
+
+#ifdef WORLD_BUILDER
+#ifdef CONTROLLER_DEBUG
+#undef CONTROLLER_DEBUG
+#endif
+enum { CLO_NO_HAPTIC, CLO_RANDOM_BUTTONS };
+namespace CommandLineOptions
+{
+ static bool Get( int i ) { return false; };
+};
+#endif
+
+// We use ::strcmp()
+//
+#include <string.h>
+
+#include <raddebugwatch.hpp>
+#ifdef DEBUGWATCH
+#include <input/inputmanager.h>
+
+
+struct ButtonData
+{
+ ButtonData() : controllerID( -1 ), button( -1 ), range( 1.0f ) {};
+ int controllerID;
+ int button;
+ float range;
+};
+
+static ButtonData gData[Input::MaxPhysicalButtons];
+
+void ButtonCallback( void* userData )
+{
+ ButtonData* data = static_cast<ButtonData*>( userData );
+
+ if ( data->button != Input::INVALID_CONTROLLERID )
+ {
+ UserController* controller = GetInputManager()->GetController( data->controllerID );
+
+ Button* button = controller->GetInputButton( data->button );
+ button->SetValue( 1.0f );
+ button->ForceChange();
+
+ for ( unsigned j = 0; j < Input::MaxMappables; j++ )
+ {
+ Mappable* mappable = controller->GetMappable( j );
+ if ( mappable )
+ {
+ mappable->DispatchOnButton( data->controllerID, data->button, button );
+ }
+ }
+
+ button->SetValue( 0.0f );
+ button->ForceChange();
+
+ for ( unsigned j = 0; j < Input::MaxMappables; j++ )
+ {
+ Mappable* mappable = controller->GetMappable( j );
+ if ( mappable )
+ {
+ mappable->DispatchOnButton( data->controllerID, data->button, button );
+ }
+ }
+ }
+}
+
+#endif
+
+//This enables the random button pressing.
+#define CONTROLLER_DEBUG
+#ifdef CONTROLLER_DEBUG
+
+#define MAX_BUTTON_TIME 3000
+#define MAX_BUTTON_PRESSES 8
+struct RandomButton
+{
+ RandomButton() :
+ mRandomButtonEnable( false ),
+ mRandomButtonTiming( 0 ),
+ mRadomizeButtonTiming( false ),
+ mButtonTimeout( 0 ),
+ mMaxButtons( MAX_BUTTON_PRESSES ),
+ mCurrentButton( 0 )
+ {
+ int i;
+ for ( i = 0; i < MAX_BUTTON_PRESSES; ++i )
+ {
+ mLastButton[ i ] = -1;
+ }
+ }
+
+ bool mRandomButtonEnable;
+ unsigned int mRandomButtonTiming;
+ bool mRadomizeButtonTiming;
+ int mButtonTimeout;
+ int mLastButton[ MAX_BUTTON_PRESSES ];
+ unsigned int mMaxButtons;
+ unsigned int mCurrentButton;
+};
+
+RandomButton gRandomButtoners[ Input::MaxControllers ];
+
+#endif
+
+//******************************************************************************
+//
+// Constructors, Destructors and Operators
+//
+//******************************************************************************
+UserController::UserController( )
+:
+m_iPlayerIndex( -1 ),
+m_xIController2( NULL ),
+m_controllerId( -1 ),
+m_bConnected( false ),
+mbInputPointsRegistered( false ),
+mGameState( Input::ACTIVE_ALL ),
+mbIsRumbleOn( false )
+{
+ unsigned int i = 0;
+ for ( i = 0; i < Input::MaxMappables; i++ )
+ {
+ this->mMappable[ i ] = 0;
+ }
+
+#if defined(RAD_PS2) || defined(RAD_GAMECUBE)
+ mHeavyWheelRumble.SetRumbleType( LG_TYPE_SQUARE );
+#endif
+}
+
+void UserController::NotifyConnect( void )
+{
+ if ( !IsConnected( ) )
+ {
+ m_bConnected = true;
+
+ for ( unsigned int i = 0; i < Input::MaxMappables; i++ )
+ {
+ if (mMappable[ i ])
+ mMappable[ i ]->OnControllerConnect( GetControllerId( ) );
+ }
+ }
+}
+void UserController::NotifyDisconnect( void )
+{
+ if ( IsConnected( ) )
+ {
+ m_bConnected = false;
+
+ for ( unsigned int i = 0; i < Input::MaxMappables; i++ )
+ {
+ if (mMappable[ i ])
+ mMappable[ i ]->OnControllerDisconnect( GetControllerId( ) );
+ }
+ }
+}
+
+void UserController::Create( int id )
+{
+ m_controllerId = id;
+}
+
+void UserController::SetGameState( unsigned state)
+{
+ mGameState = state;
+ for ( unsigned i = 0; i < Input::MaxMappables; i++ )
+ {
+ if(mMappable[ i ])
+ {
+ mMappable[ i ]->SetGameState( state );
+
+ // if the game state change involved and inactive mappable being activated
+ // wwe need to reload the current button state
+ if(mMappable[ i ]->GetResync())
+ {
+ mMappable[ i ]->InitButtons( m_controllerId, mButtonArray);
+ mMappable[ i ]->SetResync(false);
+ }
+ }
+ }
+}
+
+void UserController::OnControllerInputPointChange( unsigned int buttonId, float value )
+{
+ // We need to query the input point directly to get the value
+ // remapped to the requested range.
+
+ float fLastValue = mButtonArray[ buttonId ].GetValue( );
+ float fDeadZone = mButtonDeadZones[buttonId];
+
+ value = m_xIController2->GetInputPointByIndex( buttonId )->GetCurrentValue( );
+
+ if ( rmt::Epsilon( value, 0.0f, fDeadZone ) )
+ {
+ value = 0.0f;
+ }
+ else
+ {
+ float sign = rmt::Sign( value );
+ float calibratedButtonData = rmt::Fabs( value ) - fDeadZone;
+ calibratedButtonData *= 1.0f / ( 1.0f - fDeadZone );
+ calibratedButtonData *= sign;
+ bool bChanged = !( rmt::Epsilon( fLastValue, calibratedButtonData, 0.05f ) );
+
+ if ( bChanged )
+ {
+ // go with the deadzone corrected value.
+ value = calibratedButtonData;
+ }
+ else
+ {
+ // restore the old deadzone corrected value.
+ value = fLastValue;
+ }
+ }
+
+ mButtonArray[ buttonId ].SetValue( value );
+}
+
+void UserController::Initialize( IRadController* pIController2 )
+{
+ // if we are being called while initialized, something is wrong
+ rAssert(!mbInputPointsRegistered && !m_xIController2);
+
+ m_xIController2 = pIController2;
+
+ if( !mbInputPointsRegistered && m_xIController2 != NULL )
+ {
+ // register input point callbacks
+ unsigned int i;
+
+ for( i = 0; i < Input::MaxPhysicalButtons; i++ )
+ {
+ mButtonNames[i] = 0;
+ }
+
+ // get number of input points on controller
+ mNumButtons = m_xIController2->GetNumberOfInputPoints();
+ rAssert(mNumButtons < Input::MaxPhysicalButtons);
+
+ // register all input points
+ for( i = 0; i < mNumButtons; i++ )
+ {
+ IRadControllerInputPoint* pInputPoint = m_xIController2->GetInputPointByIndex( i );
+ if( pInputPoint != NULL )
+ {
+ const char* inputPointType = pInputPoint->GetType();
+
+ if( ::strcmp( inputPointType, "Button" ) == 0 )
+ {
+ pInputPoint->SetTolerance( 1.0f );
+ mButtonDeadZones[ i ] = 0.0f;
+ }
+ else if( strcmp( inputPointType, "XAxis" ) == 0
+ || strcmp( inputPointType, "YAxis" ) == 0)
+ {
+ pInputPoint->SetTolerance( 0.01f );
+ pInputPoint->SetRange( -1.0f, 1.0f );
+
+ if ( strcmp( pInputPoint->GetName(), "Wheel" ) == 0 )
+ {
+ mButtonDeadZones[ i ] = 0.025f;
+ }
+ else
+ {
+ mButtonDeadZones[ i ] = 0.25f;
+ }
+ }
+ else // Analog?
+ {
+ pInputPoint->SetTolerance( 0.01f );
+ pInputPoint->SetRange( 0.0f, 1.0f );
+ mButtonDeadZones[ i ] = 0.0f;
+ }
+ mButtonNames[ i ] = radMakeKey(pInputPoint->GetName( ) );
+ pInputPoint->RegisterControllerInputPointCallback( static_cast< IRadControllerInputPointCallback* >( this ), i );
+
+ // [ps] Initialize the input points
+ OnControllerInputPointChange( i , pInputPoint->GetCurrentValue( ) );
+
+#ifdef DEBUGWATCH
+ char name[64];
+ sprintf( name, "%s Button", pInputPoint->GetName() );
+ char nameSpace[64];
+ sprintf( nameSpace, "Controller\\Input%d", m_controllerId );
+ gData[m_controllerId].controllerID = m_controllerId;
+ gData[m_controllerId].button = static_cast<InputManager::eButtonMap>(i);
+ float min, max;
+ pInputPoint->GetRange( &min, &max );
+ gData[m_controllerId].range = max;
+ radDbgWatchAddFunction( name, &ButtonCallback, &gData[m_controllerId], nameSpace );
+#endif
+ }
+ }
+
+ mbInputPointsRegistered = true;
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+ //Get the steering spring.
+ IRadControllerOutputPoint* p_OutputPoint = m_xIController2->GetOutputPointByName( "CenterSpring" );
+ if ( p_OutputPoint )
+ {
+ mSteeringSpring.Init( p_OutputPoint );
+ }
+
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "BaseDamper" );
+ if ( p_OutputPoint )
+ {
+ mSteeringDamper.Init( p_OutputPoint );
+ }
+
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "Constant" );
+ if ( p_OutputPoint )
+ {
+ mConstantEffect.Init( p_OutputPoint );
+ }
+
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "Rumble" );
+ if ( p_OutputPoint )
+ {
+ mWheelRumble.Init( p_OutputPoint );
+ }
+
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "HeavyRumble" );
+ if ( p_OutputPoint )
+ {
+ mHeavyWheelRumble.Init( p_OutputPoint );
+ }
+#endif
+
+#ifdef RAD_GAMECUBE
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "Motor" );
+ if ( p_OutputPoint )
+ {
+ mRumbleEffect.SetMotor( 0, p_OutputPoint );
+ }
+#elif defined( RAD_PS2 )
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "SmallMotor" );
+ if ( p_OutputPoint )
+ {
+ mRumbleEffect.SetMotor( 0, p_OutputPoint );
+ }
+
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "LargeMotor" );
+ if ( p_OutputPoint )
+ {
+ mRumbleEffect.SetMotor( 1, p_OutputPoint );
+ }
+#else
+ IRadControllerOutputPoint* p_OutputPoint = m_xIController2->GetOutputPointByName( "LeftMotor" );
+ if ( p_OutputPoint )
+ {
+ mRumbleEffect.SetMotor( 0, p_OutputPoint );
+ }
+
+ p_OutputPoint = m_xIController2->GetOutputPointByName( "RightMotor" );
+ if ( p_OutputPoint )
+ {
+ mRumbleEffect.SetMotor( 1, p_OutputPoint );
+ }
+#endif
+ }
+
+#ifdef CONTROLLER_DEBUG
+#ifdef DEBUGWATCH
+ char nameSpace[64];
+ sprintf( nameSpace, "Controller\\Input%d", m_controllerId );
+ RandomButton& rb = gRandomButtoners[ m_controllerId ];
+ radDbgWatchAddBoolean( &rb.mRandomButtonEnable, "Enable Random Buttons", nameSpace );
+ radDbgWatchAddUnsignedInt( &rb.mRandomButtonTiming, "Button Timing", nameSpace, NULL, NULL, 0, MAX_BUTTON_TIME );
+ radDbgWatchAddBoolean( &rb.mRadomizeButtonTiming, "Randomize Timing", nameSpace );
+ radDbgWatchAddUnsignedInt( &rb.mMaxButtons, "Max Simul. Buttons", nameSpace, NULL, NULL, 1, MAX_BUTTON_PRESSES );
+#endif
+#endif
+}
+
+void UserController::ReleaseRadController( void )
+{
+ // deregister input point callbacks
+ if( mbInputPointsRegistered && m_xIController2 != NULL )
+ {
+ // get number of input points on controller.
+ //
+ const unsigned int NUM_INPUT_POINTS = m_xIController2->GetNumberOfInputPoints();
+ unsigned int i;
+
+ // unregister all input points.
+ //
+ for( i = 0; i < NUM_INPUT_POINTS; i++ )
+ {
+ IRadControllerInputPoint* pInputPoint = m_xIController2->GetInputPointByIndex( i );
+ if( pInputPoint != NULL )
+ {
+ pInputPoint->UnRegisterControllerInputPointCallback( static_cast< IRadControllerInputPointCallback* >( this ) );
+#ifdef DEBUGWATCH
+ char name[64];
+ sprintf( name, "%s Button", pInputPoint->GetName() );
+ radDbgWatchDelete( &name );
+#endif
+ }
+ mButtonArray[ i ].SetValue(0.0f);
+ }
+
+ mbInputPointsRegistered = false;
+ }
+
+ mRumbleEffect.ShutDownEffects();
+
+ this->m_xIController2 = NULL;
+
+#ifdef CONTROLLER_DEBUG
+#ifdef DEBUGWATCH
+ RandomButton& rb = gRandomButtoners[ m_controllerId ];
+ radDbgWatchDelete( &rb.mRandomButtonEnable );
+ radDbgWatchDelete( &rb.mRandomButtonTiming );
+ radDbgWatchDelete( &rb.mRadomizeButtonTiming );
+ radDbgWatchDelete( &rb.mMaxButtons );
+#endif
+#endif
+}
+
+void UserController::SetRumble( bool bRumbleOn, bool pulse )
+{
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+ if ( bRumbleOn && !mbIsRumbleOn && !CommandLineOptions::Get( CLO_NO_HAPTIC ) )
+ {
+ mSteeringSpring.Start();
+ mSteeringDamper.Start();
+ mConstantEffect.Start();
+ mWheelRumble.Start();
+ mHeavyWheelRumble.Start();
+ }
+ else if ( !bRumbleOn && mbIsRumbleOn )
+ {
+ mSteeringSpring.Stop();
+ mSteeringDamper.Stop();
+ mConstantEffect.Stop();
+ mWheelRumble.Stop();
+ mHeavyWheelRumble.Stop();
+ }
+#endif
+
+ if ( pulse )
+ {
+ PulseRumble();
+ }
+
+ if ( !bRumbleOn && mbIsRumbleOn )
+ {
+ mRumbleEffect.ShutDownEffects();
+ }
+
+ mbIsRumbleOn = bRumbleOn;
+}
+
+bool UserController::IsRumbleOn( void ) const
+{
+ return mbIsRumbleOn;
+}
+
+void UserController::PulseRumble()
+{
+ mRumbleEffect.SetEffect( RumbleEffect::PULSE, 500 );
+}
+
+void UserController::ApplyEffect( RumbleEffect::Effect effect, unsigned int durationms )
+{
+ if ( mbIsRumbleOn && !CommandLineOptions::Get( CLO_NO_HAPTIC ) )
+ {
+ mRumbleEffect.SetEffect( effect, durationms );
+ }
+}
+
+void UserController::ApplyDynaEffect( RumbleEffect::DynaEffect effect, unsigned int durationms, float gain )
+{
+ if ( mbIsRumbleOn && !CommandLineOptions::Get( CLO_NO_HAPTIC ) )
+ {
+#ifdef RAD_XBOX
+ gain *= 2.0f;
+#endif
+ mRumbleEffect.SetDynaEffect( effect, durationms, gain );
+ }
+}
+
+void UserController::Update( unsigned timeins )
+{
+ if(!IsConnected())
+ return;
+
+#ifdef CONTROLLER_DEBUG
+ if ( gRandomButtoners[ m_controllerId ].mRandomButtonEnable || CommandLineOptions::Get( CLO_RANDOM_BUTTONS ) )
+ {
+ RandomButton& rb = gRandomButtoners[ m_controllerId ];
+ rb.mButtonTimeout -= static_cast<int>( timeins );
+ if ( rb.mButtonTimeout < 0 )
+ {
+ rb.mButtonTimeout = 0;
+ }
+
+ if ( rb.mButtonTimeout == 0 )
+ {
+ //Reset the last button.
+ if ( rb.mLastButton[ rb.mCurrentButton ] != -1 )
+ {
+ mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ].SetValue( 0.0f );
+ mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ].ForceChange();
+
+ for ( unsigned j = 0; j < Input::MaxMappables; j++ )
+ {
+ if ( mMappable[ j ] )
+ {
+ mMappable[ j ]->DispatchOnButton( m_controllerId, rb.mLastButton[ rb.mCurrentButton ], &mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ] );
+ }
+ }
+ }
+
+ rb.mLastButton[ rb.mCurrentButton ] = rand() % Input::MaxPhysicalButtons;
+ mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ].SetValue( 1.0f );
+ mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ].ForceChange();
+
+ for ( unsigned j = 0; j < Input::MaxMappables; j++ )
+ {
+ if ( mMappable[ j ] )
+ {
+ mMappable[ j ]->DispatchOnButton( m_controllerId, rb.mLastButton[ rb.mCurrentButton ], &mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ] );
+ }
+ }
+
+ //Increment the current button
+ rb.mCurrentButton = (rb.mCurrentButton + 1) % rb.mMaxButtons;
+
+ if ( rb.mRadomizeButtonTiming )
+ {
+ rb.mRandomButtonTiming = rand() % MAX_BUTTON_TIME;
+ }
+
+ rb.mButtonTimeout = rb.mRandomButtonTiming;
+ }
+ }
+
+#endif
+
+ // update the logical controller button values
+ for(unsigned int i = 0; i < Input::MaxPhysicalButtons; i++)
+ {
+ // always rebrodcast non-zero button values (otherwise multiple physical -> single logical
+ // button mappiung will screw up in some cases)
+ if((mButtonArray[ i ].TimeSinceChange() == 0) || (mButtonArray[ i ].IsDown()))
+ {
+ for ( unsigned j = 0; j < Input::MaxMappables; j++ )
+ {
+ if ( mMappable[ j ] )
+ {
+ mMappable[ j ]->DispatchOnButton( m_controllerId, i, &mButtonArray[ i ] );
+ }
+ }
+ }
+ }
+
+#ifdef RAD_GAMECUBE
+ if ( mbIsRumbleOn )
+ {
+#endif
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+ mSteeringSpring.Update();
+ mSteeringDamper.Update();
+ mConstantEffect.Update();
+ mWheelRumble.Update();
+ mHeavyWheelRumble.Update();
+#endif
+
+ //I leave this out so the FE can rumble me anyway.
+ mRumbleEffect.Update( timeins );
+
+#ifdef RAD_GAMECUBE
+ }
+#endif
+}
+
+// Returns the value stored by input point at index.
+float UserController::GetInputValue( unsigned int index ) const
+{
+ return mButtonArray[ index ].GetValue( );
+}
+
+// Returns the real-time value of the input point at index.
+float UserController::GetInputValueRT( unsigned int index ) const
+{
+ rAssert( m_xIController2 != NULL );
+
+ IRadControllerInputPoint* pInputPoint = m_xIController2->GetInputPointByIndex( index );
+ if( pInputPoint != NULL )
+ {
+ return pInputPoint->GetCurrentValue();
+ }
+ else
+ {
+ return 0.0f;
+ }
+}
+
+Button* UserController::GetInputButton( unsigned int index )
+{
+ return &mButtonArray[ index ];
+}
+
+
+UserController::~UserController( void )
+{
+ unsigned int i = 0;
+ for ( i = 0; i < Input::MaxMappables; i++ )
+ {
+ if( this->mMappable[ i ] != NULL )
+ {
+ mMappable[ i ]->Release( );
+ mMappable[ i ] = 0;
+ }
+ }
+}
+
+int UserController::RegisterMappable( Mappable *pMappable )
+{
+ // Find a slot.
+ unsigned int i = 0;
+ for ( i = 0; i < Input::MaxMappables; i++ )
+ {
+ if( this->mMappable[ i ] == NULL )
+ {
+ break;
+ }
+ }
+
+ // Assign the mappable.
+ if ( i < Input::MaxMappables )
+ {
+ this->mMappable[ i ] = pMappable;
+ mMappable[ i ]->AddRef( );
+ mMappable[ i ]->LoadControllerMappings( m_controllerId );
+ mMappable[ i ]->InitButtons( m_controllerId, mButtonArray);
+ }
+ else
+ {
+ rAssertMsg( false, "Not enough mappables allocated.\n" );
+ return -1;
+ }
+
+ // make sure the input state get correctly set up for newly bound mappables
+ pMappable->SetResync(true);
+ pMappable->SetGameState(mGameState);
+
+ return (int)i;
+}
+
+void UserController::UnregisterMappable( int handle )
+{
+ rAssert( handle < static_cast< int >( Input::MaxMappables ) );
+ if ( mMappable[ handle ] )
+ {
+ mMappable[ handle ]->Reset( );
+ mMappable[ handle ]->Release( );
+ mMappable[ handle ] = 0;
+ LoadControllerMappings();
+ }
+}
+
+void UserController::UnregisterMappable( Mappable* mappable)
+{
+ for ( unsigned i = 0; i < Input::MaxMappables; i++ )
+ {
+ if( this->mMappable[ i ] == mappable )
+ {
+ mMappable[ i ]->Reset( );
+ mMappable[ i ]->Release( );
+ mMappable[ i ] = 0;
+ LoadControllerMappings();
+ return;
+ }
+ }
+}
+
+void UserController::LoadControllerMappings( void )
+{
+ unsigned int i = 0;
+ for ( i = 0; i < Input::MaxMappables; i++ )
+ {
+ if( this->mMappable[ i ] != NULL )
+ {
+ this->mMappable[ i ]->LoadControllerMappings( m_controllerId );
+ mMappable[ i ]->InitButtons( m_controllerId, mButtonArray);
+ }
+ }
+}
+
+// return the button id from the name.
+int UserController::GetIdByName( const char* pszName ) const
+{
+ radKey key = radMakeKey(pszName);
+ unsigned int i;
+ for( i = 0; i < Input::MaxPhysicalButtons; i++ )
+ {
+ if (mButtonNames[i] == key)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
diff --git a/game/code/input/usercontroller.h b/game/code/input/usercontroller.h
new file mode 100644
index 0000000..922164a
--- /dev/null
+++ b/game/code/input/usercontroller.h
@@ -0,0 +1,139 @@
+#ifndef USERCONTROLLER_HPP
+#define USERCONTROLLER_HPP
+
+#include <input/controller.h>
+#include <input/button.h>
+#include <input/rumbleeffect.h>
+#include <radcontroller.hpp>
+#include <radkey.hpp>
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+#include <input/steeringspring.h>
+#include <input/baseDamper.h>
+#include <input/constanteffect.h>
+#include <input/wheelrumble.h>
+#endif
+
+class Mappable;
+class Mapper;
+
+
+class UserController :
+ public IRadControllerInputPointCallback
+{
+public:
+ UserController( void );
+ virtual ~UserController( void );
+
+ // initial set up of controller (called once at input ystem init)
+ virtual void Create( int id );
+
+ // set up actual controller mapping (called once at startup and prior to any time input
+ // connection state changes)
+ virtual void Initialize( IRadController* pIController2 );
+
+ // release ftech controllers (called prior to input connection state change)
+ virtual void ReleaseRadController( void );
+
+ // get the index of this controller
+ unsigned int GetControllerId( void ) const { return m_controllerId; }
+
+ // per-frame update
+ void Update( unsigned timeins );
+
+ // set the current game state (to activate / deactivate appropriate logical controllerts)
+ void SetGameState(unsigned);
+
+ // Rumble (not yet implimented)
+ void SetRumble( bool bRumbleOn, bool pulse = false );
+ bool IsRumbleOn( void ) const;
+ void PulseRumble();
+ void ApplyEffect( RumbleEffect::Effect effect, unsigned int durationms );
+ void ApplyDynaEffect( RumbleEffect::DynaEffect effect, unsigned int durationms, float gain );
+
+
+ // set the player index associated with this controller
+ void SetPlayerIndex( int i ) { m_iPlayerIndex = i; }
+ int GetPlayerIndex( void ) const { return m_iPlayerIndex; }
+
+ // connection status
+ bool IsConnected( void ) const { return m_bConnected; }
+ void NotifyConnect( void );
+ void NotifyDisconnect( void );
+
+ // Returns the value stored by input point at index.
+ float GetInputValue( unsigned int index ) const;
+ Button* GetInputButton( unsigned int index );
+
+ // Returns the current 'real-time' value of the input point
+ float GetInputValueRT( unsigned int index ) const;
+
+ // Attaches an logical controller this physical controller
+ int RegisterMappable( Mappable* pMappable );
+
+ // Detach a logical controller
+ void UnregisterMappable( int handle );
+ void UnregisterMappable( Mappable* pMappable );
+
+ // Set up the physical-logical button mappings.
+ void LoadControllerMappings( void );
+
+ // return the button id from the name.
+ int GetIdByName( const char* pszName ) const;
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+ SteeringSpring* GetSpring() { return mSteeringSpring.IsInit() ? &mSteeringSpring : NULL; };
+ BaseDamper* GetDamper() { return mSteeringDamper.IsInit() ? &mSteeringDamper : NULL; };
+ ConstantEffect* GetConstantEffect() { return mConstantEffect.IsInit() ? &mConstantEffect : NULL; };
+ WheelRumble* GetWheelRumble() { return mWheelRumble.IsInit() ? &mWheelRumble : NULL; };
+ WheelRumble* GetHeavyWheelRumble() { return mHeavyWheelRumble.IsInit() ? &mHeavyWheelRumble : NULL; };
+#endif
+
+ Mappable* GetMappable( unsigned int which ) { return mMappable[ which ]; };
+
+ bool IsWheel()
+ {
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+ return mSteeringSpring.IsInit();
+#else
+ return false;
+#endif
+ }
+protected:
+ // Implements IRadControllerInputPointCallback
+ void OnControllerInputPointChange( unsigned int buttonId, float value );
+
+ // the player index we are controlling.
+ //
+ int m_iPlayerIndex;
+
+ ref< IRadController > m_xIController2;
+
+ int m_controllerId;
+
+ bool m_bConnected;
+
+ Mappable* mMappable[ Input::MaxMappables ];
+ bool mbInputPointsRegistered;
+
+ unsigned mNumButtons;
+ Button mButtonArray[ Input::MaxPhysicalButtons ];
+ radKey mButtonNames[ Input::MaxPhysicalButtons ];
+ float mButtonDeadZones[ Input::MaxPhysicalButtons ];
+
+ unsigned mGameState;
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2)
+ SteeringSpring mSteeringSpring;
+ BaseDamper mSteeringDamper;
+ ConstantEffect mConstantEffect;
+ WheelRumble mWheelRumble;
+ WheelRumble mHeavyWheelRumble;
+#endif
+ bool mbIsRumbleOn;
+
+ RumbleEffect mRumbleEffect;
+};
+
+#endif
+
diff --git a/game/code/input/usercontrollerWin32.cpp b/game/code/input/usercontrollerWin32.cpp
new file mode 100644
index 0000000..47231d5
--- /dev/null
+++ b/game/code/input/usercontrollerWin32.cpp
@@ -0,0 +1,1443 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: UserController
+//
+// Description: Implementation of the usercontroller class for win32.
+//
+// History: Branched from the console UserController class.
+//
+//===========================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <radcontroller.hpp>
+#include <raddebug.hpp>
+#include <radmath/radmath.hpp>
+
+#define DIRECTINPUT_VERSION 0x0800
+#include <dinput.h>
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <input/usercontrollerWin32.h>
+#include <input/mappable.h>
+#include <input/mapper.h>
+#include <input/button.h>
+#include <input/Mouse.h>
+#include <input/Keyboard.h>
+#include <input/Gamepad.h>
+#include <input/SteeringWheel.h>
+#include <input/inputmanager.h>
+
+#include <input/steeringspring.h>
+#include <input/baseDamper.h>
+#include <input/constanteffect.h>
+#include <input/wheelrumble.h>
+#include <presentation/tutorialmanager.h>
+
+#include <console/fbstricmp.h>
+#include <data/config/gameconfigmanager.h>
+
+#ifndef WORLD_BUILDER
+#include <main/commandlineoptions.h>
+#endif
+
+#include <gameflow/gameflow.h>
+#include <contexts/context.h>
+
+// needed for the mouse reset hack.
+static int maxes[] = { DIMOFS_X, DIMOFS_Y, DIMOFS_Z };
+
+#ifdef WORLD_BUILDER
+#ifdef CONTROLLER_DEBUG
+#undef CONTROLLER_DEBUG
+#endif
+enum { CLO_NO_HAPTIC, CLO_RANDOM_BUTTONS };
+namespace CommandLineOptions
+{
+ static bool Get( int i ) { return false; };
+};
+#endif
+
+//This enables the random button pressing.
+#define CONTROLLER_DEBUG
+#ifdef CONTROLLER_DEBUG
+
+#define MAX_BUTTON_TIME 3000
+#define MAX_BUTTON_PRESSES 8
+struct RandomButton
+{
+ RandomButton() :
+ mRandomButtonEnable( false ),
+ mRandomButtonTiming( 0 ),
+ mRadomizeButtonTiming( false ),
+ mButtonTimeout( 0 ),
+ mMaxButtons( MAX_BUTTON_PRESSES ),
+ mCurrentButton( 0 )
+ {
+ int i;
+ for ( i = 0; i < MAX_BUTTON_PRESSES; ++i )
+ {
+ mLastButton[ i ] = -1;
+ }
+ }
+
+ bool mRandomButtonEnable;
+ unsigned int mRandomButtonTiming;
+ bool mRadomizeButtonTiming;
+ int mButtonTimeout;
+ int mLastButton[ MAX_BUTTON_PRESSES ];
+ unsigned int mMaxButtons;
+ unsigned int mCurrentButton;
+};
+
+RandomButton gRandomButtoners[ Input::MaxControllers ];
+
+#endif
+
+//=============================================================================
+// Class: InputCode
+//=============================================================================
+//
+// Description: Need this for compressing ( Controller / directX key code /
+// direction ) triplets into one integer. Then we can re-use the
+// Mapper class for virtual maps.
+//
+//=============================================================================
+
+class InputCode
+{
+public:
+ InputCode( unsigned int inputcode ) { mInputCode = inputcode; }
+ InputCode( eControllerType controller, int dxKey, eDirectionType direction )
+ {
+ mInputCode = dxKey | (direction << 24) | (controller << 28);
+ rAssert( controller == GetController() );
+ rAssert( dxKey == GetDxKeyCode() );
+ rAssert( direction == GetDirection() );
+ }
+
+ eControllerType GetController() const
+ {
+ rAssert( (mInputCode >> 28) < NUM_CONTROLLERTYPES );
+ return eControllerType( mInputCode >> 28 );
+ }
+ int GetDxKeyCode() const
+ {
+ return mInputCode & 0xFFFFFF;
+ }
+ eDirectionType GetDirection() const
+ {
+ rAssert( ((mInputCode >> 24) & 0xF) <= NUM_DIRTYPES );
+ return eDirectionType( (mInputCode >> 24) & 0xF );
+ }
+
+ int GetInputCode() const { return mInputCode; }
+
+private:
+ unsigned int mInputCode;
+};
+
+//******************************************************************************
+//
+// Constructors, Destructors and Operators
+//
+//******************************************************************************
+UserController::UserController( )
+:
+m_controllerId( -1 ),
+mIsConnected( false ),
+mGameState( Input::ACTIVE_ALL ),
+mbInputPointsRegistered( false ),
+mKeyboardBack( false ),
+mbIsRumbleOn( false ),
+m_bIsWheel( false ),
+m_bTutorialDisabled(true)
+{
+ int i = 0;
+ for ( i = 0; i < Input::MaxMappables; i++ )
+ {
+ mMappable[ i ] = 0;
+ }
+
+ for( i = 0; i < 3; i++ )
+ {
+ mResetMouseCounter[ i ] = 0;
+ }
+
+ // Set up the virtual buttons.
+ mNumButtons = VirtualInputs::GetNumber();
+ rAssert(mNumButtons < Input::MaxPhysicalButtons);
+
+ for( i = 0; i < mNumButtons; i++ )
+ {
+ // Define the button names for the virtual keys
+ mButtonNames[ i ] = radMakeKey( VirtualInputs::GetName( i ) );
+
+ mButtonDeadZones[ i ] = 0.10f;
+ mButtonSticky[ i ] = false;
+ }
+
+ // Set up the controllers for each type.
+ m_pController[GAMEPAD] = new Gamepad();
+ m_pController[KEYBOARD] = new Keyboard();
+ m_pController[MOUSE] = new Mouse();
+ m_pController[STEERINGWHEEL] = new Gamepad();
+
+ m_pSteeringSpring = new SteeringSpring;
+ m_pSteeringDamper = new BaseDamper;
+ m_pConstantEffect = new ConstantEffect;
+ m_pWheelRumble = new WheelRumble;
+ m_pHeavyWheelRumble = new WheelRumble;
+
+ //
+ // Register with the game config manager
+ //
+ GetGameConfigManager()->RegisterConfig( this );
+}
+
+void UserController::NotifyConnect( void )
+{
+ if ( !IsConnected( ) )
+ {
+ for( int i = 0; i < NUM_CONTROLLERTYPES; i++ )
+ {
+ m_pController[i]->Connect();
+ }
+
+ for ( unsigned int i = 0; i < Input::MaxMappables, this->mMappable[ i ]; i++ )
+ {
+ mMappable[ i ]->OnControllerConnect( GetControllerId( ) );
+ }
+
+ mIsConnected = true;
+ }
+}
+void UserController::NotifyDisconnect( void )
+{
+ if ( IsConnected( ) )
+ {
+ for( int i = 0; i < NUM_CONTROLLERTYPES; i++ )
+ {
+ m_pController[i]->Disconnect();
+ }
+
+ for ( unsigned int i = 0; i < Input::MaxMappables, this->mMappable[ i ]; i++ )
+ {
+ mMappable[ i ]->OnControllerDisconnect( GetControllerId( ) );
+ }
+
+ mIsConnected = false;
+ }
+}
+
+bool UserController::IsConnected() const
+{
+ return mIsConnected;
+}
+
+void UserController::Create( int id )
+{
+ m_controllerId = id;
+
+ // Now that we know our controller id,
+ // load the default controller mappings.
+ //
+ LoadDefaults();
+}
+
+void UserController::SetGameState( unsigned state)
+{
+ mGameState = state;
+ for ( unsigned i = 0; i < Input::MaxMappables; i++ )
+ {
+ if(mMappable[ i ])
+ {
+ mMappable[ i ]->SetGameState( state );
+
+ // if the game state change involved and inactive mappable being activated
+ // wwe need to reload the current button state
+ if(mMappable[ i ]->GetResync())
+ {
+ mMappable[ i ]->InitButtons( m_controllerId, mButtonArray);
+ mMappable[ i ]->SetResync(false);
+ }
+ }
+ }
+}
+
+void UserController::SetButtonValue( unsigned int buttonId, float value, bool sticky )
+{
+ float fLastValue = mButtonArray[ buttonId ].GetValue( );
+ float fDeadZone = mButtonDeadZones[ buttonId ];
+
+ if( mButtonSticky[ buttonId ] && !sticky )
+ {
+ return; // don't overwrite a sticky button.
+ }
+
+ if ( rmt::Epsilon( value, 0.0f, fDeadZone ) )
+ {
+ value = 0.0f;
+ }
+ else
+ {
+ //Clamp our values.
+ value = rmt::Clamp( value, MIN_AXIS_THRESH, MAX_AXIS_THRESH );
+
+ // Recalibrate them from [deadzone..1] to [0..1].
+ float sign = rmt::Sign( value );
+ float calibratedButtonData = rmt::Fabs( value ) - fDeadZone;
+
+ calibratedButtonData *= 1.0f / ( 1.0f - fDeadZone );
+ calibratedButtonData *= sign;
+
+ bool bChanged = !( rmt::Epsilon( fLastValue, calibratedButtonData, 0.05f ) );
+
+ if ( bChanged )
+ {
+ // go with the deadzone corrected value.
+ value = calibratedButtonData;
+ }
+ else
+ {
+ // restore the old deadzone corrected value.
+ value = fLastValue;
+ }
+ }
+
+ if( value > 0.0f || fLastValue > 0.0f )
+ {
+ mButtonArray[ buttonId ].SetValue( value );
+ }
+
+ // Reset the sticky flag.
+ mButtonSticky[ buttonId ] = sticky;
+ if( sticky && value == 0.0f )
+ {
+ mButtonSticky[ buttonId ] = false;
+ }
+}
+
+void UserController::OnControllerInputPointChange( unsigned int code, float value )
+{
+ // Decode the virtual button code.
+ InputCode icode( code );
+
+ eControllerType cont = icode.GetController();
+ int dxKey = icode.GetDxKeyCode();
+
+ // Set the IsWheel boolean if this is a steering wheel axis input
+ m_bIsWheel = STEERINGWHEEL == cont && m_pController[ cont ]->IsInputAxis( dxKey );
+
+ // Set the keyboard back value
+ if( KEYBOARD == cont && dxKey == DIK_ESCAPE && value > 0 )
+ {
+ mKeyboardBack = true;
+ }
+
+ // Derive the directional values
+ float dvalues[ NUM_DIRTYPES ];
+ int ndirections = DeriveDirectionValues( cont, dxKey, value, dvalues );
+
+ // Check if we're remapping a key.
+ if( mMapData.MapNext )
+ {
+ Remap( cont, dxKey, dvalues, ndirections );
+ return;
+ }
+
+ // Look up what virtual buttons have been mapped to this input, then update them.
+ for( int map = 0; map < NUM_MAPTYPES; map++ )
+ {
+ for( int dir = 0; dir < ndirections; dir++ )
+ {
+ int vButton = m_pController[ cont ]->GetMap( dxKey, (eDirectionType) dir, (eMapType) map );
+ if( vButton != Input::INVALID_CONTROLLERID )
+ {
+ SetButtonValue( vButton, dvalues[ dir ], cont == KEYBOARD );
+ }
+ if( vButton == InputManager::feBack && cont != KEYBOARD )
+ {
+ mKeyboardBack = false;
+ }
+ }
+ }
+}
+
+int UserController::DeriveDirectionValues( eControllerType type, int dxKey, float value, float* values )
+{
+ // Reset the directional values
+ values[ DIR_UP ] = values[ DIR_DOWN ] = values[ DIR_RIGHT ] = values[ DIR_LEFT ] = 0.0f;
+
+ // Calculate the values based on the type of input.
+ if( m_pController[ type ]->IsInputAxis( dxKey ) )
+ {
+ // Normalize the axis.
+ value = ( MAX_AXIS_THRESH - MIN_AXIS_THRESH ) * value + MIN_AXIS_THRESH;
+
+ // Assign the values.
+ values[ DIR_UP ] = ( value >= 0 ) ? value : 0.0f;
+ values[ DIR_DOWN ] = ( value >= 0 ) ? 0.0f : -value;
+
+ return 2;
+ }
+ else if( m_pController[ type ]->IsMouseAxis( dxKey ) )
+ {
+ // Translate mouse val to -1..1 axis value.
+ value = ((Mouse*)m_pController[MOUSE])->CalculateMouseAxis( dxKey, value );
+
+ // Invert if necessary
+ if( dxKey == DIMOFS_X && m_bInvertMouseX ||
+ dxKey == DIMOFS_Y && m_bInvertMouseY )
+ {
+ value *= -1.0f;
+ }
+
+ if( dxKey == DIMOFS_X )
+ {
+ value *= m_fMouseSensitivityX;
+ }
+ else if( dxKey == DIMOFS_Y )
+ {
+ value *= m_fMouseSensitivityY;
+ }
+
+ // Assign the values.
+ values[ DIR_UP ] = ( value >= 0 ) ? value : 0.0f;
+ values[ DIR_DOWN ] = ( value >= 0 ) ? 0.0f : -value;
+
+ // Initiate the mouse reset hack
+ for( int i = 0; i < 3; i++ )
+ {
+ if( maxes[ i ] == dxKey && value != 0.0f )
+ {
+ mResetMouseCounter[ i ] = 1;
+ }
+ }
+
+ return 2;
+ }
+ else if( m_pController[ type ]->IsPovHat( dxKey ) )
+ {
+ ((Gamepad*)m_pController[GAMEPAD])->CalculatePOV( value, &values[DIR_UP], &values[DIR_DOWN],
+ &values[DIR_RIGHT], &values[DIR_LEFT] );
+
+ return 4;
+ }
+ else // It's a button.
+ {
+ values[ DIR_UP ] = value;
+
+ return 1;
+ }
+}
+
+void UserController::Initialize( RADCONTROLLER* pIController )
+{
+ // if we are being called while initialized, something is wrong
+ rAssert(!mbInputPointsRegistered);
+
+ // Initialize the controllers.
+ //
+ for( int i = 0; i < NUM_CONTROLLERTYPES; i++ )
+ {
+ m_pController[i]->Init( pIController[i] );
+ }
+
+ // Register the input points now.
+ //
+ RegisterInputPoints();
+
+ RADCONTROLLER pSteeringWheel = m_pController[STEERINGWHEEL]->getController();
+
+ static bool bSteeringSet = false;
+ if( pSteeringWheel /*&& !bSteeringSet*/ )
+ {
+ //Get the steering spring.
+ IRadControllerOutputPoint* p_OutputPoint = pSteeringWheel->GetOutputPointByName( "Spring" );
+ if ( p_OutputPoint )
+ {
+ m_pSteeringSpring->Init( p_OutputPoint );
+ }
+
+ p_OutputPoint = pSteeringWheel->GetOutputPointByName( "Damper" );
+ if ( p_OutputPoint )
+ {
+ m_pSteeringDamper->Init( p_OutputPoint );
+ }
+
+ p_OutputPoint = pSteeringWheel->GetOutputPointByName( "Constant" );
+ if ( p_OutputPoint )
+ {
+ m_pConstantEffect->Init( p_OutputPoint );
+ }
+
+ p_OutputPoint = pSteeringWheel->GetOutputPointByName( "Sine Wave" );
+ if ( p_OutputPoint )
+ {
+ m_pWheelRumble->Init( p_OutputPoint );
+ m_pWheelRumble->SetResetTime( 1000 );
+ }
+
+ p_OutputPoint = pSteeringWheel->GetOutputPointByName( "Square Wave" );
+ if ( p_OutputPoint )
+ {
+ m_pHeavyWheelRumble->Init( p_OutputPoint );
+ m_pHeavyWheelRumble->SetResetTime( 1000 );
+ }
+ bSteeringSet = true;
+ }
+
+ // Set up rumbling for the gamepad.
+ //
+ RADCONTROLLER pGamepad = m_pController[GAMEPAD]->getController();
+ if( pGamepad != NULL )
+ {
+ IRadControllerOutputPoint* p_OutputPoint = pGamepad->GetOutputPointByName( "LeftMotor" );
+ if ( p_OutputPoint )
+ {
+ mRumbleEffect.SetMotor( 0, p_OutputPoint );
+ }
+
+ p_OutputPoint = pGamepad->GetOutputPointByName( "RightMotor" );
+ if ( p_OutputPoint )
+ {
+ mRumbleEffect.SetMotor( 1, p_OutputPoint );
+ }
+ }
+}
+
+void UserController::ReleaseRadController( void )
+{
+ // deregister input point callbacks
+ if( mbInputPointsRegistered )
+ {
+ //unregister the inputpoints associated with the controllers.
+ int i;
+ for( i = 0; i < NUM_CONTROLLERTYPES; i++ )
+ {
+ if( m_pController[ i ] != NULL )
+ {
+ m_pController[ i ]->ReleaseInputPoints( this );
+ }
+ }
+
+ for( i = 0; i < mNumButtons; i++ )
+ {
+ mButtonArray[ i ].SetValue(0.0f);
+ }
+
+ mbInputPointsRegistered = false;
+ }
+
+ if( m_pSteeringSpring ) m_pSteeringSpring->ShutDownEffects();
+ if( m_pSteeringDamper ) m_pSteeringDamper->ShutDownEffects();
+ if( m_pConstantEffect ) m_pConstantEffect->ShutDownEffects();
+ if( m_pWheelRumble ) m_pWheelRumble->ShutDownEffects();
+ if( m_pHeavyWheelRumble ) m_pHeavyWheelRumble->ShutDownEffects();
+
+ mRumbleEffect.ShutDownEffects();
+}
+
+void UserController::SetRumble( bool bRumbleOn, bool pulse )
+{
+ if ( bRumbleOn && !mbIsRumbleOn && !CommandLineOptions::Get( CLO_NO_HAPTIC ) && m_bForceFeedback )
+ {
+ StartForceEffects();
+ }
+ else if ( !bRumbleOn && mbIsRumbleOn && m_bForceFeedback )
+ {
+ StopForceEffects();
+ }
+
+ if ( pulse && m_bForceFeedback )
+ {
+ PulseRumble();
+ }
+
+ if ( !bRumbleOn && mbIsRumbleOn && m_bForceFeedback )
+ {
+ mRumbleEffect.ShutDownEffects();
+ }
+ mbIsRumbleOn = bRumbleOn;
+}
+
+bool UserController::IsRumbleOn( void ) const
+{
+ return mbIsRumbleOn;
+}
+
+void UserController::PulseRumble()
+{
+ mRumbleEffect.SetEffect( RumbleEffect::PULSE, 500 );
+}
+
+void UserController::ApplyEffect( RumbleEffect::Effect effect, unsigned int durationms )
+{
+ if ( mbIsRumbleOn && !CommandLineOptions::Get( CLO_NO_HAPTIC ) )
+ {
+ mRumbleEffect.SetEffect( effect, durationms );
+ }
+}
+
+void UserController::ApplyDynaEffect( RumbleEffect::DynaEffect effect, unsigned int durationms, float gain )
+{
+ if ( mbIsRumbleOn && !CommandLineOptions::Get( CLO_NO_HAPTIC ) )
+ {
+ mRumbleEffect.SetDynaEffect( effect, durationms, gain );
+ }
+}
+
+void UserController::Update( unsigned timeins )
+{
+ if(!IsConnected())
+ return;
+
+#ifdef CONTROLLER_DEBUG
+ if ( gRandomButtoners[ m_controllerId ].mRandomButtonEnable || CommandLineOptions::Get( CLO_RANDOM_BUTTONS ) )
+ {
+ RandomButton& rb = gRandomButtoners[ m_controllerId ];
+ rb.mButtonTimeout -= static_cast<int>( timeins );
+ if ( rb.mButtonTimeout < 0 )
+ {
+ rb.mButtonTimeout = 0;
+ }
+
+ if ( rb.mButtonTimeout == 0 )
+ {
+ //Reset the last button.
+ if ( rb.mLastButton[ rb.mCurrentButton ] != -1 )
+ {
+ mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ].SetValue( 0.0f );
+ mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ].ForceChange();
+
+ for ( unsigned j = 0; j < Input::MaxMappables; j++ )
+ {
+ if ( mMappable[ j ] )
+ {
+ mMappable[ j ]->DispatchOnButton( m_controllerId, rb.mLastButton[ rb.mCurrentButton ], &mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ] );
+ }
+ }
+ }
+
+ rb.mLastButton[ rb.mCurrentButton ] = rand() % Input::MaxPhysicalButtons;
+ mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ].SetValue( 1.0f );
+ mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ].ForceChange();
+
+ for ( unsigned j = 0; j < Input::MaxMappables; j++ )
+ {
+ if ( mMappable[ j ] )
+ {
+ mMappable[ j ]->DispatchOnButton( m_controllerId, rb.mLastButton[ rb.mCurrentButton ], &mButtonArray[ rb.mLastButton[ rb.mCurrentButton ] ] );
+ }
+ }
+
+ //Increment the current button
+ rb.mCurrentButton = (rb.mCurrentButton + 1) % rb.mMaxButtons;
+
+ if ( rb.mRadomizeButtonTiming )
+ {
+ rb.mRandomButtonTiming = rand() % MAX_BUTTON_TIME;
+ }
+
+ rb.mButtonTimeout = rb.mRandomButtonTiming;
+ }
+ }
+
+#endif
+
+ // Reset any mouse inputs that have gone stale.
+ ResetMouseAxes();
+
+ // update the logical controller button values
+ for(unsigned int i = 0; i < Input::MaxPhysicalButtons; i++)
+ {
+ // always rebrodcast non-zero button values (otherwise multiple physical -> single logical
+ // button mapping will screw up in some cases)
+ unsigned int timesincechange = mButtonArray[ i ].TimeSinceChange();
+ bool bIsDown = mButtonArray[ i ].IsDown();
+ if((timesincechange == 0) || bIsDown)
+ {
+ for ( unsigned j = 0; j < Input::MaxMappables; j++ )
+ {
+ if ( mMappable[ j ] )
+ {
+ mMappable[ j ]->DispatchOnButton( m_controllerId, i, &mButtonArray[ i ] );
+ }
+ }
+ }
+ }
+
+ m_pSteeringSpring->Update();
+ m_pSteeringDamper->Update();
+ m_pConstantEffect->Update();
+ m_pWheelRumble->Update(timeins);
+ m_pHeavyWheelRumble->Update(timeins);
+
+ //I leave this out so the FE can rumble me anyway.
+ mRumbleEffect.Update( timeins );
+}
+
+void UserController::StartForceEffects()
+{
+ m_pSteeringSpring->Start();
+ m_pSteeringDamper->Start();
+ m_pConstantEffect->Start();
+ m_pWheelRumble->Start();
+ m_pHeavyWheelRumble->Start();
+}
+
+void UserController::StopForceEffects()
+{
+ m_pSteeringSpring->Stop();
+ m_pSteeringDamper->Stop();
+ m_pConstantEffect->Stop();
+ m_pWheelRumble->Stop();
+ m_pHeavyWheelRumble->Stop();
+}
+
+// Returns the value stored by input point at index.
+float UserController::GetInputValue( unsigned int index ) const
+{
+ switch( index )
+ {
+ case InputManager::LeftStickX:
+ {
+ float up = mButtonArray[ InputManager::CameraRight ].GetValue();
+ float down = mButtonArray[ InputManager::CameraLeft ].GetValue();
+ return ( up > down ) ? up : -down;
+ }
+ case InputManager::LeftStickY:
+ {
+ float up = mButtonArray[ InputManager::CameraMoveIn ].GetValue();
+ float down = mButtonArray[ InputManager::CameraMoveOut ].GetValue();
+ return ( up > down ) ? up : -down;
+ }
+ case InputManager::KeyboardEsc:
+ {
+ return mKeyboardBack ? 1.0f : 0.0f;
+ }
+ default:
+ return mButtonArray[ index ].GetValue( );
+ }
+}
+
+float UserController::GetInputValueRT( unsigned int index ) const
+{
+ // not supported.
+ return 0.0f;
+}
+
+Button* UserController::GetInputButton( unsigned int index )
+{
+ return &mButtonArray[ index ];
+}
+
+UserController::~UserController( void )
+{
+ unsigned int i = 0;
+ for ( i = 0; i < Input::MaxMappables; i++ )
+ {
+ if( this->mMappable[ i ] != NULL )
+ {
+ mMappable[ i ]->Release( );
+ mMappable[ i ] = 0;
+ }
+ }
+ for( int i = 0; i < NUM_CONTROLLERTYPES; i++ )
+ {
+ delete m_pController[i];
+ m_pController[i] = NULL;
+ }
+
+ delete m_pSteeringSpring;
+ m_pSteeringSpring = NULL;
+ delete m_pSteeringDamper;
+ m_pSteeringDamper = NULL;
+ delete m_pConstantEffect;
+ m_pConstantEffect = NULL;
+ delete m_pWheelRumble;
+ m_pWheelRumble = NULL;
+ delete m_pHeavyWheelRumble;
+ m_pHeavyWheelRumble = NULL;
+}
+
+int UserController::RegisterMappable( Mappable *pMappable )
+{
+ // Find a slot.
+ unsigned int i = 0;
+ for ( i = 0; i < Input::MaxMappables; i++ )
+ {
+ if( this->mMappable[ i ] == NULL )
+ {
+ break;
+ }
+ }
+
+ // Assign the mappable.
+ if ( i < Input::MaxMappables )
+ {
+ this->mMappable[ i ] = pMappable;
+ mMappable[ i ]->AddRef( );
+ mMappable[ i ]->LoadControllerMappings( m_controllerId );
+ mMappable[ i ]->InitButtons( m_controllerId, mButtonArray);
+ }
+ else
+ {
+ rAssertMsg( false, "Not enough mappables allocated.\n" );
+ return -1;
+ }
+
+ // make sure the input state get correctly set up for newly bound mappables
+ pMappable->SetResync(true);
+ pMappable->SetGameState(mGameState);
+
+ return (int)i;
+}
+
+void UserController::UnregisterMappable( int handle )
+{
+ rAssert( handle < static_cast< int >( Input::MaxMappables ) );
+ if ( mMappable[ handle ] )
+ {
+ mMappable[ handle ]->Reset( );
+ mMappable[ handle ]->Release( );
+ mMappable[ handle ] = 0;
+ LoadControllerMappings();
+ }
+}
+
+void UserController::UnregisterMappable( Mappable* mappable)
+{
+ for ( unsigned i = 0; i < Input::MaxMappables; i++ )
+ {
+ if( this->mMappable[ i ] == mappable )
+ {
+ mMappable[ i ]->Reset( );
+ mMappable[ i ]->Release( );
+ mMappable[ i ] = 0;
+ LoadControllerMappings();
+ return;
+ }
+ }
+}
+
+void UserController::LoadControllerMappings( void )
+{
+ unsigned int i = 0;
+ for ( i = 0; i < Input::MaxMappables; i++ )
+ {
+ if( this->mMappable[ i ] != NULL )
+ {
+ this->mMappable[ i ]->LoadControllerMappings( m_controllerId );
+ mMappable[ i ]->InitButtons( m_controllerId, mButtonArray);
+ }
+ }
+}
+
+// return the button id from the name.
+int UserController::GetIdByName( const char* pszName ) const
+{
+ radKey key = radMakeKey(pszName);
+ unsigned int i;
+ for( i = 0; i < Input::MaxPhysicalButtons; i++ )
+ {
+ if (mButtonNames[i] == key)
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+SteeringSpring* UserController::GetSpring()
+{
+ return m_pSteeringSpring->IsInit() ? m_pSteeringSpring : NULL;
+}
+
+BaseDamper* UserController::GetDamper()
+{
+ return m_pSteeringDamper->IsInit() ? m_pSteeringDamper : NULL;
+}
+
+ConstantEffect* UserController::GetConstantEffect()
+{
+ return m_pConstantEffect->IsInit() ? m_pConstantEffect : NULL;
+}
+
+WheelRumble* UserController::GetWheelRumble()
+{
+ return m_pWheelRumble->IsInit() ? m_pWheelRumble : NULL;
+}
+
+WheelRumble* UserController::GetHeavyWheelRumble()
+{
+ return m_pHeavyWheelRumble->IsInit() ? m_pHeavyWheelRumble : NULL;
+}
+
+void UserController::RegisterInputPoints()
+{
+ // If we've already registered, then return.
+ if( mbInputPointsRegistered )
+ {
+ return;
+ }
+
+ // Register input points for each controller.
+ for( int type = 0; type < NUM_CONTROLLERTYPES; type++ )
+ {
+ // Retrieve the controller.. If not connected, skip.
+ RADCONTROLLER pController = m_pController[type]->getController();
+ if( pController == NULL )
+ {
+ continue;
+ }
+
+ // Go through each input point of the controller.
+ unsigned num = pController->GetNumberOfInputPoints();
+ for( unsigned ip = 0; ip < num; ip++ )
+ {
+ RegisterInputPoint( (eControllerType) type, ip );
+ }
+ }
+
+ mbInputPointsRegistered = true;
+}
+
+void UserController::RegisterInputPoint( eControllerType type, int inputpoint )
+{
+ // Can only map when the controller connected.. otherwise no work.
+ RADCONTROLLER pController = m_pController[type]->getController();
+ rAssert( pController != NULL );
+
+ IRadControllerInputPoint* pInputPoint = pController->GetInputPointByIndex( inputpoint );
+ rAssert( pInputPoint != NULL );
+
+ int dxKey = m_pController[type]->GetDICode( inputpoint );
+
+ // For banned input points, return right away
+ if( dxKey != Input::INVALID_CONTROLLERID && !m_pController[type]->IsBannedInput( dxKey ) )
+ {
+ // Register the callback.
+ // the direction value below is unimportant & ignored.
+ InputCode icode( type, dxKey, NUM_DIRTYPES );
+ pInputPoint->RegisterControllerInputPointCallback( this, icode.GetInputCode() );
+ m_pController[type]->AddInputPoints( pInputPoint );
+ }
+}
+
+const char* UserController::GetConfigName() const
+{
+ // do this unfortunate switch cause of multiplayer possibilities :/
+ // maybe clean it up later.
+ switch( m_controllerId )
+ {
+ case -1:
+ return "Control-default";
+ case 0:
+ return "Controller";
+ case 1:
+ return "Controller1";
+ case 2:
+ return "Controller2";
+ case 3:
+ return "Controller3";
+ default:
+ rAssert( false );
+ return "Controller3";
+ }
+}
+
+int UserController::GetNumProperties() const
+{
+ return Input::MaxVirtualMappings * Input::MaxPhysicalButtons + 8;
+}
+
+void UserController::LoadDefaults()
+{
+ ClearMappings();
+
+ if ( m_controllerId == 0 )
+ {
+ // The primary mapping - to the keyboard & mouse.
+ SetMap( SLOT_PRIMARY, InputManager::MoveUp, KEYBOARD, DIK_W );
+ SetMap( SLOT_PRIMARY, InputManager::MoveDown, KEYBOARD, DIK_S );
+ SetMap( SLOT_PRIMARY, InputManager::MoveLeft, KEYBOARD, DIK_A );
+ SetMap( SLOT_PRIMARY, InputManager::MoveRight, KEYBOARD, DIK_D );
+ SetMap( SLOT_PRIMARY, InputManager::Jump, KEYBOARD, DIK_SPACE );
+ SetMap( SLOT_PRIMARY, InputManager::Sprint, KEYBOARD, DIK_LSHIFT );
+
+ SetMap( SLOT_PRIMARY, InputManager::Accelerate, KEYBOARD, DIK_W );
+ SetMap( SLOT_PRIMARY, InputManager::Reverse, KEYBOARD, DIK_S );
+ SetMap( SLOT_PRIMARY, InputManager::SteerLeft, KEYBOARD, DIK_A );
+ SetMap( SLOT_PRIMARY, InputManager::SteerRight, KEYBOARD, DIK_D );
+ SetMap( SLOT_PRIMARY, InputManager::Horn, KEYBOARD, DIK_LSHIFT );
+ SetMap( SLOT_PRIMARY, InputManager::ResetCar, KEYBOARD, DIK_SPACE );
+
+ SetMap( SLOT_PRIMARY, InputManager::CameraLeft, KEYBOARD, DIK_NUMPAD4 );
+ SetMap( SLOT_PRIMARY, InputManager::CameraRight, KEYBOARD, DIK_NUMPAD6 );
+ SetMap( SLOT_PRIMARY, InputManager::CameraMoveIn, KEYBOARD, DIK_NUMPAD8 );
+ SetMap( SLOT_PRIMARY, InputManager::CameraMoveOut, KEYBOARD, DIK_NUMPAD2 );
+ SetMap( SLOT_PRIMARY, InputManager::CameraZoom, KEYBOARD, DIK_NUMPAD5 );
+ SetMap( SLOT_PRIMARY, InputManager::CameraLookUp, KEYBOARD, DIK_NUMPAD0 );
+ SetMap( SLOT_PRIMARY, InputManager::CameraToggle, KEYBOARD, DIK_NUMPAD0 );
+ SetMap( SLOT_PRIMARY, InputManager::CameraCarLeft, KEYBOARD, DIK_NUMPAD4 );
+ SetMap( SLOT_PRIMARY, InputManager::CameraCarRight, KEYBOARD, DIK_NUMPAD6 );
+ SetMap( SLOT_PRIMARY, InputManager::CameraCarLookUp, KEYBOARD, DIK_NUMPAD8 );
+ SetMap( SLOT_PRIMARY, InputManager::CameraCarLookBack, KEYBOARD, DIK_NUMPAD2 );
+ }
+
+ SetMap( SLOT_PRIMARY, InputManager::Attack, MOUSE, DIMOFS_BUTTON1 );
+ SetMap( SLOT_PRIMARY, InputManager::DoAction, MOUSE, DIMOFS_BUTTON0 );
+
+ SetMap( SLOT_PRIMARY, InputManager::GetOutCar, MOUSE, DIMOFS_BUTTON0 );
+ SetMap( SLOT_PRIMARY, InputManager::HandBrake, MOUSE, DIMOFS_BUTTON1 );
+
+ // The secondary mapping - to the gamepad.
+ SetMap( SLOT_SECONDARY, InputManager::MoveUp, GAMEPAD, DIJOFS_Y, DIR_UP );
+ SetMap( SLOT_SECONDARY, InputManager::MoveDown, GAMEPAD, DIJOFS_Y, DIR_DOWN );
+ SetMap( SLOT_SECONDARY, InputManager::MoveLeft, GAMEPAD, DIJOFS_X, DIR_DOWN );
+ SetMap( SLOT_SECONDARY, InputManager::MoveRight, GAMEPAD, DIJOFS_X, DIR_UP );
+ SetMap( SLOT_SECONDARY, InputManager::Attack, GAMEPAD, DIJOFS_BUTTON0 );
+ SetMap( SLOT_SECONDARY, InputManager::Jump, GAMEPAD, DIJOFS_BUTTON3 );
+ SetMap( SLOT_SECONDARY, InputManager::Sprint, GAMEPAD, DIJOFS_BUTTON7 );
+ SetMap( SLOT_SECONDARY, InputManager::DoAction, GAMEPAD, DIJOFS_BUTTON4 );
+
+ SetMap( SLOT_SECONDARY, InputManager::Accelerate, GAMEPAD, DIJOFS_BUTTON7 );
+ SetMap( SLOT_SECONDARY, InputManager::Reverse, GAMEPAD, DIJOFS_BUTTON6 );
+ SetMap( SLOT_SECONDARY, InputManager::SteerLeft, GAMEPAD, DIJOFS_X, DIR_DOWN );
+ SetMap( SLOT_SECONDARY, InputManager::SteerRight, GAMEPAD, DIJOFS_X, DIR_UP );
+ SetMap( SLOT_SECONDARY, InputManager::GetOutCar, GAMEPAD, DIJOFS_BUTTON4 );
+ SetMap( SLOT_SECONDARY, InputManager::HandBrake, GAMEPAD, DIJOFS_BUTTON1 );
+ SetMap( SLOT_SECONDARY, InputManager::Horn, GAMEPAD, DIJOFS_BUTTON2 );
+ SetMap( SLOT_SECONDARY, InputManager::ResetCar, GAMEPAD, DIJOFS_BUTTON5 );
+
+ SetMap( SLOT_SECONDARY, InputManager::CameraLeft, GAMEPAD, DIJOFS_Z, DIR_DOWN );
+ SetMap( SLOT_SECONDARY, InputManager::CameraRight, GAMEPAD, DIJOFS_Z, DIR_UP );
+ SetMap( SLOT_SECONDARY, InputManager::CameraMoveIn, GAMEPAD, DIJOFS_RZ, DIR_UP );
+ SetMap( SLOT_SECONDARY, InputManager::CameraMoveOut, GAMEPAD, DIJOFS_RZ, DIR_DOWN );
+ SetMap( SLOT_SECONDARY, InputManager::CameraZoom, GAMEPAD, DIJOFS_BUTTON6 );
+ SetMap( SLOT_SECONDARY, InputManager::CameraLookUp, GAMEPAD, DIJOFS_BUTTON7 );
+ SetMap( SLOT_SECONDARY, InputManager::CameraToggle, GAMEPAD, DIJOFS_BUTTON5 );
+ SetMap( SLOT_SECONDARY, InputManager::CameraCarLeft, GAMEPAD, DIJOFS_Z, DIR_DOWN );
+ SetMap( SLOT_SECONDARY, InputManager::CameraCarRight, GAMEPAD, DIJOFS_Z, DIR_UP );
+ SetMap( SLOT_SECONDARY, InputManager::CameraCarLookUp, GAMEPAD, DIJOFS_RZ, DIR_UP );
+ SetMap( SLOT_SECONDARY, InputManager::CameraCarLookBack, GAMEPAD, DIJOFS_RZ, DIR_DOWN );
+
+
+ // Initialize the game settings
+ m_bMouseLook = true;
+ m_bInvertMouseX = false;
+ m_bInvertMouseY = false;
+ m_bForceFeedback = false;
+ m_bTutorialDisabled = false;
+ m_fMouseSensitivityX = 0.35f;
+ m_fMouseSensitivityY = 0.5f;
+ m_fWheelSensitivityX = 0.5f;
+ m_fWheelSensitivityY = 0.5f;
+
+ //Enable the tutorials if controls are reverted.
+ TutorialManager* pTutorialManager = GetTutorialManager();
+ if( pTutorialManager )
+ pTutorialManager->EnableTutorialMode( m_bTutorialDisabled );
+}
+
+void UserController::LoadConfig( ConfigString& config )
+{
+ ClearMappings();
+
+ char property[ ConfigString::MaxLength ];
+ char value[ ConfigString::MaxLength ];
+
+ int map;
+ int virtualKey;
+ int cont;
+ int dxKey;
+ int dir;
+
+ // Load the mappings.
+ while ( config.ReadProperty( property, value ) )
+ {
+ if( _stricmp( property, "buttonmap" ) == 0 )
+ {
+ int ret = sscanf( value, "%d, %d: ( %d %d %d )", &map, &virtualKey, &cont, &dxKey, &dir );
+
+ if( ret == 5 &&
+ map >= 0 && map < Input::MaxVirtualMappings &&
+ virtualKey >= 0 && virtualKey < (int) mNumButtons &&
+ VirtualInputs::GetType( virtualKey ) != MAP_FRONTEND &&
+ cont >= 0 && cont < NUM_CONTROLLERTYPES &&
+ m_pController[ cont ]->IsValidInput( dxKey ) &&
+ dir >= 0 && dir < NUM_DIRTYPES
+ )
+ {
+ SetMap( map, virtualKey, (eControllerType) cont, dxKey, (eDirectionType) dir );
+ }
+ }
+ else if( _stricmp( property, "mouselook" ) == 0 )
+ {
+ if( strcmp( value, "yes" ) == 0 )
+ {
+ m_bMouseLook = true;
+ }
+ else if( strcmp( value, "no" ) == 0 )
+ {
+ m_bMouseLook = false;
+ }
+ }
+ else if( _stricmp( property, "invertmousex" ) == 0 )
+ {
+ if( strcmp( value, "yes" ) == 0 )
+ {
+ m_bInvertMouseX = true;
+ }
+ else if( strcmp( value, "no" ) == 0 )
+ {
+ m_bInvertMouseX = false;
+ }
+ }
+ else if( _stricmp( property, "invertmousey" ) == 0 )
+ {
+ if( strcmp( value, "yes" ) == 0 )
+ {
+ m_bInvertMouseY = true;
+ }
+ else if( strcmp( value, "no" ) == 0 )
+ {
+ m_bInvertMouseY = false;
+ }
+ }
+ else if( _stricmp( property, "useforcefeedback" ) == 0 )
+ {
+ if( strcmp( value, "yes" ) == 0 )
+ {
+ m_bForceFeedback = true;
+ }
+ else if( strcmp( value, "no" ) == 0 )
+ {
+ m_bForceFeedback= false;
+ }
+ }
+ else if( _stricmp( property, "disabletutorials" ) == 0 )
+ {
+ if( strcmp( value, "yes" ) == 0 )
+ {
+ m_bTutorialDisabled = true;
+ }
+ else if( strcmp( value, "no" ) == 0 )
+ {
+ m_bTutorialDisabled = false;
+ }
+ }
+ else if( _stricmp( property, "mousesensitivityx" ) == 0 )
+ {
+ float val = (float) atof( value );
+ if( val > 0 )
+ {
+ m_fMouseSensitivityX = val;
+ }
+ }
+ else if( _stricmp( property, "mousesensitivityy" ) == 0 )
+ {
+ float val = (float) atof( value );
+ if( val > 0 )
+ {
+ m_fMouseSensitivityY = val;
+ }
+ }
+ else if( _stricmp( property, "wheelsensitivityx" ) == 0 )
+ {
+ float val = (float) atof( value );
+ if( val > 0 )
+ {
+ m_fWheelSensitivityX = val;
+ }
+ }
+ else if( _stricmp( property, "wheelsensitivityy" ) == 0 )
+ {
+ float val = (float) atof( value );
+ if( val > 0 )
+ {
+ m_fWheelSensitivityY = val;
+ }
+ }
+ }
+}
+
+void UserController::SaveConfig( ConfigString& config )
+{
+ char value[ ConfigString::MaxLength ];
+ eControllerType cont;
+ int dxKey;
+ eDirectionType dir;
+
+ // For each of our mappings...
+ for( int map = 0; map < Input::MaxVirtualMappings; map++ )
+ {
+ // For each virtual button...
+ for( int vk = 0; vk < (int) mNumButtons; vk++ )
+ {
+ // Save any mapped key.
+ if( VirtualInputs::GetType( vk ) != MAP_FRONTEND &&
+ GetMap( map, vk, cont, dxKey, dir ) )
+ {
+ sprintf( value, "%d, %d: ( %d %d %d )", map, vk, cont, dxKey, dir );
+ config.WriteProperty( "buttonmap", value );
+ }
+ }
+ }
+
+ // Save the other controller settings
+ config.WriteProperty( "mouselook", m_bMouseLook ? "yes" : "no" );
+ config.WriteProperty( "invertmousex", m_bInvertMouseX ? "yes" : "no" );
+ config.WriteProperty( "invertmousey", m_bInvertMouseY ? "yes" : "no" );
+ config.WriteProperty( "useforcefeedback", m_bForceFeedback ? "yes" : "no" );
+ config.WriteProperty( "disabletutorials", m_bTutorialDisabled ? "yes" : "no" );
+
+ sprintf( value, "%f", m_fMouseSensitivityX );
+ config.WriteProperty( "mousesensitivityx", value );
+
+ sprintf( value, "%f", m_fMouseSensitivityY );
+ config.WriteProperty( "mousesensitivityy", value );
+
+ sprintf( value, "%f", m_fWheelSensitivityX );
+ config.WriteProperty( "wheelsensitivityx", value );
+
+ sprintf( value, "%f", m_fWheelSensitivityY );
+ config.WriteProperty( "wheelsensitivityy", value );
+
+}
+
+void UserController::RemapButton( int map, int VirtualButton, ButtonMappedCallback* callback )
+{
+ rAssert( map >= 0 && map < Input::MaxVirtualMappings );
+ rAssert( VirtualButton >= 0 && VirtualButton < mNumButtons );
+ rAssert( VirtualInputs::GetType( VirtualButton ) != MAP_FRONTEND );
+
+ // Save the data. Button gets mapped asynchronously in OnControllerInputPointChange()
+ mMapData.MapNext = true;
+ mMapData.map = map;
+ mMapData.virtualButton = VirtualButton;
+ mMapData.callback = callback;
+}
+
+void UserController::SetMap( int map, int virtualKey, eControllerType cont, int dxKey, eDirectionType dir )
+{
+ // Check over the inputs.
+ rAssert( map >= 0 && map < Input::MaxVirtualMappings );
+ rAssert( virtualKey >= 0 && virtualKey < mNumButtons );
+ rAssert( m_pController[ cont ]->IsValidInput( dxKey ) );
+ rAssert( VirtualInputs::GetType( virtualKey ) != MAP_FRONTEND );
+
+ // Create the compressed key code
+ InputCode icode( cont, dxKey, dir );
+
+ // Clear the previous key that was used for this virtual button.
+ eControllerType oldcont;
+ int olddxKey;
+ eDirectionType olddir;
+ if( GetMap( map, virtualKey, oldcont, olddxKey, olddir ) )
+ {
+ m_pController[ oldcont ]->ClearMap( olddxKey, olddir, virtualKey );
+ }
+
+ // Clear any previous virtual button mapped to this controller/key/direction
+ eMapType maptype = VirtualInputs::GetType( virtualKey );
+ int old_vb = m_pController[ cont ]->GetMap( dxKey, dir, maptype );
+ if( old_vb != Input::INVALID_CONTROLLERID )
+ {
+ for( int i = 0; i < Input::MaxVirtualMappings; i++ )
+ {
+ if( mVirtualMap[ i ].GetLogicalIndex( old_vb ) == icode.GetInputCode() )
+ {
+ mVirtualMap[ i ].SetAssociation( old_vb, Input::INVALID_CONTROLLERID );
+ }
+ }
+ }
+
+
+ // Save the new mapping
+ mVirtualMap[ map ].SetAssociation( virtualKey, icode.GetInputCode() );
+
+ // Propagate the mapping to the controller
+ m_pController[ cont ]->SetMap( dxKey, dir, virtualKey );
+}
+
+bool UserController::GetMap( int map, int virtualKey, eControllerType& type, int& dxKey, eDirectionType& dir ) const
+{
+ // Check over the inputs.
+ rAssert( map >= 0 && map < Input::MaxVirtualMappings );
+ rAssert( virtualKey >= 0 && virtualKey < mNumButtons );
+
+ // Load the mapping
+ int code = mVirtualMap[ map ].GetLogicalIndex( virtualKey );
+
+ if( code != Input::INVALID_CONTROLLERID )
+ {
+ InputCode icode = code;
+ type = icode.GetController();
+ dxKey = icode.GetDxKeyCode();
+ dir = icode.GetDirection();
+ }
+
+ return code != Input::INVALID_CONTROLLERID;
+}
+
+const char* UserController::GetMap( int map, int virtualKey, int& numDirs, eControllerType& cont, eDirectionType& dir ) const
+{
+ int dxKey;
+
+ // Retrieve the mapping for the key.
+ // If the controller is not plugged in, return null.
+ if( !GetMap( map, virtualKey, cont, dxKey, dir ) ||
+ m_pController[ cont ]->getController() == NULL )
+ {
+ return NULL;
+ }
+
+ // figure out how many directions there are for this input
+ if( m_pController[ cont ]->IsInputAxis( dxKey ) || m_pController[ cont ]->IsMouseAxis( dxKey ) )
+ {
+ numDirs = 2;
+ }
+ else if( m_pController[ cont ]->IsPovHat( dxKey ) )
+ {
+ numDirs = 4;
+ }
+ else
+ {
+ numDirs = 1;
+ }
+
+ // return the name of the input point
+ return m_pController[ cont ]->GetInputName( dxKey );
+}
+
+void UserController::Remap( eControllerType cont, int dxKey, float* dvalues, int ndirections )
+{
+ if( cont == KEYBOARD &&
+ m_pController[ cont ]->GetMap( dxKey, DIR_UP, MAP_FRONTEND ) == InputManager::feBack )
+ {
+ // If the user pressed back on the keyboard, cancel the mapping.
+ mMapData.MapNext = false;
+ mMapData.callback->OnButtonMapped( NULL, cont, 1, NUM_DIRTYPES );
+ }
+ else if( cont == MOUSE && ( dxKey == DIMOFS_X || dxKey == DIMOFS_Y ) )
+ {
+ // Not allowed to map these.
+ }
+ else
+ {
+ // Find out which direction the user is pressing in.
+ for( int dir = 0; dir < ndirections; dir++ )
+ {
+ if( dvalues[ dir ] >= MAPPING_DEADZONE )
+ {
+ SetMap( mMapData.map, mMapData.virtualButton, cont, dxKey, (eDirectionType) dir );
+
+ // Call back the mapper.
+ mMapData.MapNext = false;
+ mMapData.callback->OnButtonMapped( m_pController[ cont ]->GetInputName( dxKey ), cont, ndirections, (eDirectionType) dir );
+
+ break;
+ }
+ }
+ }
+}
+
+void UserController::ClearMappings()
+{
+ for( int i = 0; i < Input::MaxVirtualMappings; i++ )
+ {
+ mVirtualMap[ i ].ClearAssociations();
+ }
+
+ for( int j = 0; j < NUM_CONTROLLERTYPES; j++ )
+ {
+ m_pController[ j ]->ClearMappedButtons();
+ }
+
+ // Make sure these are always loaded.
+ LoadFEMappings();
+}
+
+void UserController::LoadFEMappings()
+{
+ switch ( m_controllerId )
+ {
+ case 0:
+ {
+ m_pController[KEYBOARD]->SetMap( DIK_ESCAPE, DIR_UP, InputManager::feBack );
+ m_pController[KEYBOARD]->SetMap( DIK_UP, DIR_UP, InputManager::feMoveUp );
+ m_pController[KEYBOARD]->SetMap( DIK_DOWN, DIR_UP, InputManager::feMoveDown );
+ m_pController[KEYBOARD]->SetMap( DIK_LEFT, DIR_UP, InputManager::feMoveLeft );
+ m_pController[KEYBOARD]->SetMap( DIK_RIGHT, DIR_UP, InputManager::feMoveRight );
+ m_pController[KEYBOARD]->SetMap( DIK_RETURN, DIR_UP, InputManager::feSelect );
+ m_pController[KEYBOARD]->SetMap( DIK_NUMPAD8, DIR_UP, InputManager::feMoveUp );
+ m_pController[KEYBOARD]->SetMap( DIK_NUMPAD2, DIR_UP, InputManager::feMoveDown );
+ m_pController[KEYBOARD]->SetMap( DIK_NUMPAD4, DIR_UP, InputManager::feMoveLeft );
+ m_pController[KEYBOARD]->SetMap( DIK_NUMPAD6, DIR_UP, InputManager::feMoveRight );
+ m_pController[KEYBOARD]->SetMap( DIK_NUMPADENTER, DIR_UP, InputManager::feSelect );
+ m_pController[KEYBOARD]->SetMap( DIK_F1, DIR_UP, InputManager::feFunction1 );
+ m_pController[KEYBOARD]->SetMap( DIK_F2, DIR_UP, InputManager::feFunction2 );
+ break;
+ }
+ case 3:
+ {
+ //P4
+ m_pController[KEYBOARD]->SetMap( DIK_F4, DIR_UP, InputManager::P1_KBD_Start );
+ m_pController[KEYBOARD]->SetMap( DIK_O, DIR_UP, InputManager::P1_KBD_Gas );
+ m_pController[KEYBOARD]->SetMap( DIK_L, DIR_UP, InputManager::P1_KBD_Brake );
+ m_pController[KEYBOARD]->SetMap( DIK_RETURN, DIR_UP, InputManager::P1_KBD_EBrake );
+ m_pController[KEYBOARD]->SetMap( DIK_RSHIFT, DIR_UP, InputManager::P1_KBD_Nitro );
+ m_pController[KEYBOARD]->SetMap( DIK_K, DIR_UP, InputManager::P1_KBD_Left );
+ m_pController[KEYBOARD]->SetMap( DIK_SEMICOLON, DIR_UP, InputManager::P1_KBD_Right );
+ break;
+ }
+ }
+
+ m_pController[MOUSE]->SetMap( DIMOFS_BUTTON1, DIR_UP, InputManager::feBack );
+ m_pController[MOUSE]->SetMap( DIMOFS_X, DIR_UP, InputManager::feMouseRight );
+ m_pController[MOUSE]->SetMap( DIMOFS_X, DIR_DOWN, InputManager::feMouseLeft );
+ m_pController[MOUSE]->SetMap( DIMOFS_Y, DIR_UP, InputManager::feMouseUp );
+ m_pController[MOUSE]->SetMap( DIMOFS_Y, DIR_DOWN, InputManager::feMouseDown );
+ m_pController[MOUSE]->SetMap( DIMOFS_Z, DIR_UP, InputManager::feMoveRight );
+ m_pController[MOUSE]->SetMap( DIMOFS_Z, DIR_DOWN, InputManager::feMoveLeft );
+
+ m_pController[GAMEPAD]->SetMap( DIJOFS_Y, DIR_UP, InputManager::feMoveUp );
+ m_pController[GAMEPAD]->SetMap( DIJOFS_Y, DIR_DOWN, InputManager::feMoveDown );
+ m_pController[GAMEPAD]->SetMap( DIJOFS_X, DIR_UP, InputManager::feMoveRight );
+ m_pController[GAMEPAD]->SetMap( DIJOFS_X, DIR_DOWN, InputManager::feMoveLeft );
+ m_pController[GAMEPAD]->SetMap( DIJOFS_BUTTON0, DIR_UP, InputManager::feSelect );
+ m_pController[GAMEPAD]->SetMap( DIJOFS_BUTTON1, DIR_UP, InputManager::feBack );
+ m_pController[GAMEPAD]->SetMap( DIJOFS_POV(0), DIR_UP, InputManager::feMoveUp );
+ m_pController[GAMEPAD]->SetMap( DIJOFS_POV(0), DIR_DOWN, InputManager::feMoveDown );
+ m_pController[GAMEPAD]->SetMap( DIJOFS_POV(0), DIR_RIGHT, InputManager::feMoveRight );
+ m_pController[GAMEPAD]->SetMap( DIJOFS_POV(0), DIR_LEFT, InputManager::feMoveLeft );
+}
+
+void UserController::ResetMouseAxes()
+{
+ for( int i = 0; i < 3; i++ )
+ {
+ if( mResetMouseCounter[ i ] > 1 )
+ {
+ // Reset this mouse axis.
+ InputCode icode( MOUSE, maxes[ i ], NUM_DIRTYPES );
+ OnControllerInputPointChange( icode.GetInputCode(), 0.0f );
+
+ mResetMouseCounter[ i ] = 0;
+ }
+ else if( mResetMouseCounter[ i ] > 0 )
+ {
+ mResetMouseCounter[ i ]++;
+ }
+ }
+}
diff --git a/game/code/input/usercontrollerWin32.h b/game/code/input/usercontrollerWin32.h
new file mode 100644
index 0000000..7d85957
--- /dev/null
+++ b/game/code/input/usercontrollerWin32.h
@@ -0,0 +1,294 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: UserController
+//
+// Description: An input/output controller for a given player in the game. It
+// bundles a number of actual controllers together to allow the
+// player to use different devices (keyboard, mouse, gamepad...).
+// This is the input interface between the game and the player,
+// sending inputs to registered input processing classes, letting
+// the player drive, move around, control the front end...
+//
+// This specific class is an implementation of the UserController
+// class strictly for win32.
+//
+// History: Branched from the console UserController class.
+//
+//=============================================================================
+
+#ifndef USERCONTROLLER_HPP
+#define USERCONTROLLER_HPP
+
+//========================================
+// Platform check
+//========================================
+
+#ifndef RAD_WIN32
+#error 'This implementation of the user controller is specific to Win32.'
+#endif
+
+//========================================
+// System Includes
+//========================================
+
+#include <radcontroller.hpp>
+#include <radkey.hpp>
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <data/config/gameconfig.h>
+#include <input/controller.h>
+#include <input/button.h>
+#include <input/mapper.h>
+#include <input/rumbleeffect.h>
+#include <input/RealController.h>
+
+//=============================================================================
+// Forward Class Declarations
+//=============================================================================
+
+class Mappable;
+class Mapper;
+class SteeringSpring;
+class BaseDamper;
+class ConstantEffect;
+class WheelRumble;
+
+//=============================================================================
+// Enumerations
+//=============================================================================
+
+enum eVirtualMapSlot
+{
+ SLOT_PRIMARY = 0,
+ SLOT_SECONDARY
+};
+
+#define MAX_AXIS_THRESH 1.0f
+#define MIN_AXIS_THRESH -1.0f
+
+#define MAPPING_DEADZONE 0.5f
+
+//=============================================================================
+// Interface: ButtonMappedCallback
+//=============================================================================
+//
+// Description: Callback interface for the UserController::RemapButton method.
+// Implement the method to receive notification for when a button
+// has been mapped.
+//
+//=============================================================================
+
+struct ButtonMappedCallback
+{
+ virtual void OnButtonMapped( const char* InputName, eControllerType cont, int num_dirs, eDirectionType direction ) = 0;
+};
+
+//=============================================================================
+// Struct: ButtonMapData
+//=============================================================================
+//
+// Description: Internal struct for user controller.
+//
+//=============================================================================
+
+struct ButtonMapData
+{
+ ButtonMapData() : MapNext(false) {};
+
+ bool MapNext;
+ int map;
+ int virtualButton;
+ ButtonMappedCallback* callback;
+};
+
+//=============================================================================
+// Class: UserController
+//=============================================================================
+//
+// Description: The controller interface between the user and the game. It
+// encapsulates a number of radControllers for a given player.
+//
+//=============================================================================
+
+class UserController :
+ public IRadControllerInputPointCallback,
+ public GameConfigHandler
+{
+public:
+ UserController( void );
+ virtual ~UserController( void );
+
+ // initial set up of controller (called once at input ystem init)
+ virtual void Create( int id );
+
+ // set up actual controller mapping (called once at startup and prior to any time input
+ // connection state changes)
+ virtual void Initialize( RADCONTROLLER* pIController );
+
+ // release ftech controllers (called prior to input connection state change)
+ virtual void ReleaseRadController( void );
+
+ RealController* GetRealController( eControllerType type ) { return m_pController[type]; }
+ // get the index of this controller
+ unsigned int GetControllerId( void ) const { return m_controllerId; }
+
+ // per-frame update
+ void Update( unsigned timeins );
+
+ void StartForceEffects();
+ void StopForceEffects();
+
+ // set the current game state (to activate / deactivate appropriate logical controllerts)
+ void SetGameState(unsigned);
+
+ // Rumble (not yet implimented)
+ void SetRumble( bool bRumbleOn, bool pulse = false );
+ bool IsRumbleOn( void ) const;
+ void PulseRumble();
+ void ApplyEffect( RumbleEffect::Effect effect, unsigned int durationms );
+ void ApplyDynaEffect( RumbleEffect::DynaEffect effect, unsigned int durationms, float gain );
+
+ // connection status
+ bool IsConnected( void ) const;
+ void NotifyConnect( void );
+ void NotifyDisconnect( void );
+
+ // Returns the value stored by input point at index.
+ float GetInputValue( unsigned int index ) const;
+ Button* GetInputButton( unsigned int index );
+
+ // Returns the current 'real-time' value of the input point
+ float GetInputValueRT( unsigned int index ) const;
+
+ // Attaches an logical controller this physical controller
+ int RegisterMappable( Mappable* pMappable );
+
+ // Detach a logical controller
+ void UnregisterMappable( int handle );
+ void UnregisterMappable( Mappable* pMappable );
+
+ // Set up the physical-logical button mappings.
+ void LoadControllerMappings( void );
+
+ // return the button id from the name.
+ int GetIdByName( const char* pszName ) const;
+
+ SteeringSpring* GetSpring();
+ BaseDamper* GetDamper();
+ ConstantEffect* GetConstantEffect();
+ WheelRumble* GetWheelRumble();
+ WheelRumble* GetHeavyWheelRumble();
+
+ Mappable* GetMappable( unsigned int which ) { return mMappable[ which ]; };
+
+ bool IsWheel() const { return m_bIsWheel; }
+
+ // Implementation of the GameConfigHandler interface
+ virtual const char* GetConfigName() const;
+ virtual int GetNumProperties() const;
+ virtual void LoadDefaults();
+ virtual void LoadConfig( ConfigString& config );
+ virtual void SaveConfig( ConfigString& config );
+
+ // Public mapping functions for the user controller.
+ const char* GetMap( int map, int virtualKey, int& numDirs, eControllerType& cont, eDirectionType& dir ) const;
+ void RemapButton( int map, int VirtualButton, ButtonMappedCallback* callback );
+
+ void SetMouseLook( bool bMouseLook ) { m_bMouseLook = bMouseLook; }
+ void SetInvertMouseX( bool bInvertMouseX ) { m_bInvertMouseX = bInvertMouseX; }
+ void SetInvertMouseY( bool bInvertMouseY ) { m_bInvertMouseY = bInvertMouseY; }
+ void SetForceFeedback( bool bForceFeedback ) { m_bForceFeedback = bForceFeedback; }
+ void SetTutorialDisabled( bool bDisabled ) { m_bTutorialDisabled = bDisabled; }
+ void SetMouseSensitivityX( float fValue ) { m_fMouseSensitivityX = fValue; }
+ void SetMouseSensitivityY( float fValue ) { m_fMouseSensitivityY = fValue; }
+ void SetWheelSensitivityX( float fValue ) { m_fWheelSensitivityX = fValue; }
+ void SetWheelSensitivityY( float fValue ) { m_fWheelSensitivityY = fValue; }
+
+ bool IsMouseLookOn() const { return m_bMouseLook; }
+ bool IsMouseXInverted() const { return m_bInvertMouseX; }
+ bool IsMouseYInverted() const { return m_bInvertMouseY; }
+ bool IsForceFeedbackOn() const { return m_bForceFeedback; }
+ bool IsTutorialDisabled() const { return m_bTutorialDisabled; }
+
+ float GetMouseSensitivityX() const { return m_fMouseSensitivityX; }
+ float GetMouseSensitivityY() const { return m_fMouseSensitivityY; }
+ float GetWheelSensitivityX() const { return m_fWheelSensitivityX; }
+ float GetWheelSensitivityY() const { return m_fWheelSensitivityY; }
+
+protected:
+
+ // These register our callbacks with the RadControllers.
+ void RegisterInputPoints();
+ void RegisterInputPoint( eControllerType type, int inputpoint );
+
+ // Callback for when keys/inputs are pressed by the user.
+ void OnControllerInputPointChange( unsigned int buttonId, float value );
+
+ // These process input point values as received by the input point callback.
+ int DeriveDirectionValues( eControllerType type, int dxKey, float value, float* dir_values );
+ void SetButtonValue( unsigned int virtualButton, float value, bool sticky );
+
+ // Needed to get mouse axes to function properly
+ void ResetMouseAxes();
+
+ // Private mapping functions for the user controller.
+ void SetMap( int map, int virtualKey, eControllerType cont, int dxKey, eDirectionType dir = DIR_UP );
+ bool GetMap( int map, int virtualKey, eControllerType& cont, int& dxKey, eDirectionType& dir ) const;
+ void Remap( eControllerType cont, int dxKey, float* dvalues, int directions );
+ void ClearMappings();
+ void LoadFEMappings();
+
+private:
+
+ int m_controllerId;
+ bool mIsConnected;
+
+ unsigned mGameState;
+ bool mbInputPointsRegistered;
+
+ RealController* m_pController[ NUM_CONTROLLERTYPES ];
+
+ SteeringSpring* m_pSteeringSpring;
+ BaseDamper* m_pSteeringDamper;
+ ConstantEffect* m_pConstantEffect;
+ WheelRumble* m_pWheelRumble;
+ WheelRumble* m_pHeavyWheelRumble;
+
+ Mappable* mMappable[ Input::MaxMappables ];
+
+ // Virtual mapping data.
+ Mapper mVirtualMap[ Input::MaxVirtualMappings ];
+ ButtonMapData mMapData;
+
+ // Button data.
+ int mNumButtons;
+ Button mButtonArray[ Input::MaxPhysicalButtons ];
+ radKey mButtonNames[ Input::MaxPhysicalButtons ];
+ float mButtonDeadZones[ Input::MaxPhysicalButtons ];
+ bool mButtonSticky[ Input::MaxPhysicalButtons ];
+
+ bool mKeyboardBack;
+
+ bool mbIsRumbleOn;
+ bool m_bIsWheel;
+ RumbleEffect mRumbleEffect;
+
+ bool m_bMouseLook,
+ m_bInvertMouseX,
+ m_bInvertMouseY,
+ m_bForceFeedback;
+ bool m_bTutorialDisabled;
+
+ float m_fMouseSensitivityX,
+ m_fMouseSensitivityY,
+ m_fWheelSensitivityX, // The wheel
+ m_fWheelSensitivityY; // The pedals
+
+ int mResetMouseCounter[3]; // Hack needed for proper mouse inputs.
+};
+
+#endif
diff --git a/game/code/input/virtualinputs.cpp b/game/code/input/virtualinputs.cpp
new file mode 100644
index 0000000..d4de3f3
--- /dev/null
+++ b/game/code/input/virtualinputs.cpp
@@ -0,0 +1,178 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: VirtualInputs
+//
+// Description: Implementation of the virtual input class.
+//
+// History: 03/28/2003 + Created -- Ziemek
+//
+//===========================================================================
+
+#include <input/virtualinputs.hpp>
+#include <input/inputmanager.h>
+
+static const char* szVirtualInputs[] =
+{
+ "MoveUp",
+ "MoveDown",
+ "MoveLeft",
+ "MoveRight",
+ "Attack",
+ "Jump",
+ "Sprint",
+ "DoAction",
+
+ "Accelerate",
+ "Reverse",
+ "SteerLeft",
+ "SteerRight",
+ "GetOutCar",
+ "HandBrake",
+ "Horn",
+ "ResetCar",
+
+ "CameraLeft",
+ "CameraRight",
+ "CameraMoveIn",
+ "CameraMoveOut",
+ "CameraZoom",
+ "CameraLookUp",
+ "CameraCarLeft",
+ "CameraCarRight",
+ "CameraCarLookUp",
+ "CameraCarLookBack",
+ "CameraToggle",
+
+ "feBack", // Do not allow the user to configure any fe buttons!
+ "feMoveUp", // These are standard, unconfigurable
+ "feMoveDown",
+ "feMoveLeft",
+ "feMoveRight",
+ "feSelect",
+ "feFunction1",
+ "feFunction2",
+ "feMouseLeft",
+ "feMouseRight",
+ "feMouseUp",
+ "feMouseDown",
+
+ "P1_KBD_Start",
+ "P1_KBD_Gas",
+ "P1_KBD_Brake",
+ "P1_KBD_EBrake",
+ "P1_KBD_Nitro",
+ "P1_KBD_Left",
+ "P1_KBD_Right"
+};
+
+//==============================================================================
+// VirtualInputs::GetName
+//==============================================================================
+//
+// Description: Returns the name of the given input
+//
+// Parameters: virtualinput - input to look up
+//
+// Return: name of input
+//
+//==============================================================================
+
+const char* VirtualInputs::GetName( int VirtualInput )
+{
+ rAssert( VirtualInput >= 0 && VirtualInput < GetNumber() );
+
+ return szVirtualInputs[ VirtualInput ];
+}
+
+//==============================================================================
+// VirtualInputs::GetType
+//==============================================================================
+//
+// Description: Returns the type of a virtual input
+//
+// Parameters: virtualinput - input to look up
+//
+// Return: type of input
+//
+//==============================================================================
+
+eMapType VirtualInputs::GetType( int VirtualInput )
+{
+ rAssert( VirtualInput >= 0 && VirtualInput < GetNumber() );
+
+ switch( VirtualInput )
+ {
+ case InputManager::MoveUp:
+ case InputManager::MoveDown:
+ case InputManager::MoveLeft:
+ case InputManager::MoveRight:
+ case InputManager::Attack:
+ case InputManager::Jump:
+ case InputManager::Sprint:
+ case InputManager::DoAction:
+ case InputManager::CameraLeft:
+ case InputManager::CameraRight:
+ case InputManager::CameraMoveIn:
+ case InputManager::CameraMoveOut:
+ case InputManager::CameraZoom:
+ case InputManager::CameraLookUp:
+ return MAP_CHARACTER;
+ case InputManager::Accelerate:
+ case InputManager::Reverse:
+ case InputManager::SteerLeft:
+ case InputManager::SteerRight:
+ case InputManager::GetOutCar:
+ case InputManager::HandBrake:
+ case InputManager::Horn:
+ case InputManager::ResetCar:
+ case InputManager::CameraCarLeft:
+ case InputManager::CameraCarRight:
+ case InputManager::CameraCarLookUp:
+ case InputManager::CameraCarLookBack:
+ case InputManager::CameraToggle:
+ return MAP_VEHICLE;
+ case InputManager::feBack:
+ case InputManager::feMoveUp:
+ case InputManager::feMoveDown:
+ case InputManager::feMoveLeft:
+ case InputManager::feMoveRight:
+ case InputManager::feSelect:
+ case InputManager::feFunction1:
+ case InputManager::feFunction2:
+ case InputManager::feMouseLeft:
+ case InputManager::feMouseRight:
+ case InputManager::feMouseUp:
+ case InputManager::feMouseDown:
+ return MAP_FRONTEND;
+ default:
+ {
+ if ( VirtualInput >= InputManager::P1_KBD_Start && VirtualInput <= InputManager::P1_KBD_Right )
+ {
+ return MAP_FRONTEND;
+ }
+ else
+ {
+ return MAP_CHARACTER;
+ }
+ }
+ }
+}
+//==============================================================================
+// VirtualInputs::GetNumber
+//==============================================================================
+//
+// Description: Returns the number of virtual inputs
+//
+// Parameters: n/a
+//
+// Return: number of virtual inputs
+//
+//==============================================================================
+
+int VirtualInputs::GetNumber()
+{
+ static int NumVirtualInputs = sizeof( szVirtualInputs ) / sizeof( szVirtualInputs[0] );
+
+ return NumVirtualInputs;
+}
diff --git a/game/code/input/virtualinputs.hpp b/game/code/input/virtualinputs.hpp
new file mode 100644
index 0000000..214bb07
--- /dev/null
+++ b/game/code/input/virtualinputs.hpp
@@ -0,0 +1,31 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: VirtualInput
+//
+// Description: Small helper class for dealing with virtual inputs.
+//
+// History: Created 19/08/03.
+//
+//=============================================================================
+
+#ifndef VIRTUALINPUTS_HPP
+#define VIRTUALINPUTS_HPP
+
+enum eMapType
+{
+ MAP_CHARACTER = 0,
+ MAP_VEHICLE,
+ MAP_FRONTEND,
+ NUM_MAPTYPES
+};
+
+class VirtualInputs
+{
+public:
+ static const char* GetName( int VirtualInput );
+ static eMapType GetType( int VirtualInput );
+ static int GetNumber();
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/input/wheeldefines.h b/game/code/input/wheeldefines.h
new file mode 100644
index 0000000..32533cf
--- /dev/null
+++ b/game/code/input/wheeldefines.h
@@ -0,0 +1,53 @@
+#ifndef WHEEL_DEFINES
+#define WHEEL_DEFINES
+
+#ifdef RAD_PS2
+typedef struct lgDevForceEffect LGForceEffect;
+#endif
+
+#ifdef RAD_PS2
+//Why the hell are these different?
+#define LG_TYPE_DAMPER LGTYPE_DAMPER
+#define LG_TYPE_SPRING LGTYPE_SPRING
+#define LG_TYPE_CONSTANT LGTYPE_CONSTANT
+#define LG_TYPE_TRIANGLE LGTYPE_TRIANGLE
+#define LG_TYPE_SQUARE LGTYPE_SQUARE
+#define LG_DURATION_INFINITE LGDURATION_INFINITE
+#define type Type
+#define duration Duration
+#define startDelay StartDelay
+#define magnitude Magnitude
+#define direction Direction
+#define offset Offset
+#define deadband Deadband
+#define saturationNeg SaturationNeg
+#define saturationPos SaturationPos
+#define coefficientNeg CoefficientNeg
+#define coefficientPos CoefficientPos
+#define period Period
+#define phase Phase
+#define attackTime AttackTime
+#define fadeTime FadeTime
+#define attackLevel AttackLevel
+#define fadeLevel FadeLevel
+#endif
+
+#ifdef RAD_WIN32
+enum eForceTypes
+{
+ CONSTANT_FORCE,
+ RAMP_FORCE,
+ SQUARE,
+ SINE,
+ TRIANGLE,
+ SAWTOOTH_UP,
+ SAWTOOTH_DOWN,
+ SPRING,
+ DAMPER,
+ INERTIA,
+ FRICTION,
+ CUSTOM_FORCE
+};
+#endif
+
+#endif \ No newline at end of file
diff --git a/game/code/input/wheelrumble.cpp b/game/code/input/wheelrumble.cpp
new file mode 100644
index 0000000..c9f4df3
--- /dev/null
+++ b/game/code/input/wheelrumble.cpp
@@ -0,0 +1,214 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: wheelrumble.cpp
+//
+// Description: Implement WheelRumble
+//
+// History: 6/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+#include <input/wheelrumble.h>
+#include <input/wheeldefines.h>
+#include <input/inputmanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// WheelRumble::WheelRumble
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+WheelRumble::WheelRumble()
+{
+#ifdef RAD_WIN32
+ m_diPeriodic.dwMagnitude = 0;
+ m_diPeriodic.lOffset = 0;
+ m_diPeriodic.dwPhase = 0;
+ m_diPeriodic.dwPeriod = 80;
+
+ m_diEnvelope.dwSize = sizeof(DIENVELOPE);
+ m_diEnvelope.dwAttackLevel = 0;
+ m_diEnvelope.dwAttackTime = (DWORD)(0.5 * DI_SECONDS);
+ m_diEnvelope.dwFadeLevel = 0;
+ m_diEnvelope.dwFadeTime = (DWORD)(1 * DI_SECONDS);
+
+ mForceEffect.dwFlags = DIEFF_CARTESIAN | DIEFF_OBJECTOFFSETS;
+ mForceEffect.dwDuration = INFINITE;
+ mForceEffect.dwSamplePeriod = 0;
+ mForceEffect.dwGain = DI_FFNOMINALMAX;
+ mForceEffect.dwTriggerButton = DIEB_NOTRIGGER;
+ mForceEffect.dwTriggerRepeatInterval = 0;
+ mForceEffect.cAxes = 1;
+ mForceEffect.rgdwAxes = m_rgdwAxes;
+ mForceEffect.rglDirection = m_rglDirection;
+ mForceEffect.lpEnvelope = &m_diEnvelope;
+ mForceEffect.cbTypeSpecificParams = sizeof(DIPERIODIC);
+ mForceEffect.lpvTypeSpecificParams = &m_diPeriodic;
+ mForceEffect.dwStartDelay = 0;
+#else
+ mForceEffect.type = LG_TYPE_TRIANGLE;
+ mForceEffect.duration = 500;
+ mForceEffect.startDelay = 0;
+ mForceEffect.p.periodic.magnitude = 0;
+ mForceEffect.p.periodic.direction = 90;
+ mForceEffect.p.periodic.period = 80;
+ mForceEffect.p.periodic.phase = 0;
+ mForceEffect.p.periodic.offset = 0;
+ mForceEffect.p.periodic.envelope.attackTime = 0;
+ mForceEffect.p.periodic.envelope.fadeTime = 0;
+ mForceEffect.p.periodic.envelope.attackLevel = 0;
+ mForceEffect.p.periodic.envelope.fadeLevel = 0;
+#endif
+}
+
+//=============================================================================
+// WheelRumble::~WheelRumble
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+WheelRumble::~WheelRumble()
+{
+}
+
+//=============================================================================
+// WheelRumble::OnInit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WheelRumble::OnInit()
+{
+#ifdef RAD_WIN32
+ m_diPeriodic.dwMagnitude = 0;
+#else
+ mForceEffect.p.periodic.magnitude = 0;
+#endif
+}
+
+//=============================================================================
+// WheelRumble::SetMagDir
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( u8 mag, u16 dir )
+//
+// Return: void
+//
+//=============================================================================
+#ifdef RAD_WIN32
+void WheelRumble::SetMagDir( u16 mag, u16 dir )
+#else
+void WheelRumble::SetMagDir( u8 mag, u16 dir )
+#endif
+{
+#ifdef RAD_WIN32
+ LONG rglDirection[2] = { dir, 0 };
+ m_diPeriodic.dwMagnitude = mag;
+ mForceEffect.rglDirection = rglDirection;
+#else
+ mForceEffect.p.periodic.magnitude = mag;
+ mForceEffect.p.periodic.direction = dir;
+#endif
+
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// WheelRumble::SetPPO
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( u16 per, u16 phas, s16 offset )
+//
+// Return: void
+//
+//=============================================================================
+void WheelRumble::SetPPO( u16 per, u16 phas, s16 offset )
+{
+#ifdef RAD_WIN32
+ m_diPeriodic.dwPeriod = per;
+ m_diPeriodic.dwPhase = phas;
+ m_diPeriodic.lOffset = offset;
+#else
+ mForceEffect.p.periodic.period = per;
+ mForceEffect.p.periodic.phase = phas;
+ mForceEffect.p.periodic.offset = offset;
+#endif
+
+ mEffectDirty = true;
+}
+
+//=============================================================================
+// WheelRumble::SetRumbleType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( u8 type )
+//
+// Return: void
+//
+//=============================================================================
+void WheelRumble::SetRumbleType( u8 type )
+{
+#ifdef RAD_WIN32
+
+#else
+ mForceEffect.type = type;
+#endif
+
+};
+
+#ifdef RAD_WIN32
+void WheelRumble::Update(unsigned timeins)
+{
+ m_currentTime += timeins;
+ if( m_currentTime > m_effectTime )
+ {
+ SetMagDir(0,0);
+ m_currentTime = 0;
+ }
+ ForceEffect::Update();
+}
+#endif
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/input/wheelrumble.h b/game/code/input/wheelrumble.h
new file mode 100644
index 0000000..2971833
--- /dev/null
+++ b/game/code/input/wheelrumble.h
@@ -0,0 +1,67 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: wheelrumble.h
+//
+// Description: Blahblahblah
+//
+// History: 6/25/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef WHEELRUMBLE_H
+#define WHEELRUMBLE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <input/forceeffect.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class WheelRumble : public ForceEffect
+{
+public:
+ WheelRumble();
+ virtual ~WheelRumble();
+
+ void OnInit();
+
+#ifdef RAD_WIN32
+ void SetMagDir( u16 mag, u16 dir );
+#else
+ void SetMagDir( u8 mag, u16 dir );
+#endif
+ void SetPPO( u16 per, u16 phas, s16 offset );
+
+ void SetRumbleType( u8 type );
+#ifdef RAD_WIN32
+ void Update(unsigned timeins = 0);
+#endif
+
+private:
+
+#ifdef RAD_WIN32
+ DIPERIODIC m_diPeriodic;
+ DIENVELOPE m_diEnvelope;
+#endif
+ //Prevent wasteful constructor creation.
+ WheelRumble( const WheelRumble& wheelrumble );
+ WheelRumble& operator=( const WheelRumble& wheelrumble );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //WHEELRUMBLE_H
diff --git a/game/code/interiors/allinteriors.cpp b/game/code/interiors/allinteriors.cpp
new file mode 100644
index 0000000..9202525
--- /dev/null
+++ b/game/code/interiors/allinteriors.cpp
@@ -0,0 +1 @@
+#include <interiors/interiormanager.cpp>
diff --git a/game/code/interiors/interiormanager.cpp b/game/code/interiors/interiormanager.cpp
new file mode 100644
index 0000000..8e699fa
--- /dev/null
+++ b/game/code/interiors/interiormanager.cpp
@@ -0,0 +1,2409 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: interiormanager.cpp
+//
+// Description: Implementation for the InteriorManager class.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// System
+#include <stdlib.h> // for rand()
+// Ftech
+#include <raddebug.hpp>
+#include <choreo/utility.hpp>
+#include <p3d/light.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/view.hpp>
+#include <simcommon/simstatearticulated.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <interiors/interiormanager.h>
+
+#include <ai/sequencer/actioncontroller.h>
+#include <ai/sequencer/action.h>
+#include <ai/actionbuttonhandler.h>
+#include <camera/supercammanager.h>
+#include <console/console.h>
+#include <events/eventdata.h>
+#include <events/eventmanager.h>
+#include <main/game.h>
+#include <memory/srrmemory.h>
+#include <meta/directionallocator.h>
+#include <meta/interiorentrancelocator.h>
+#include <meta/triggervolumetracker.h>
+#include <presentation/nisplayer.h>
+#include <presentation/fmvplayer/fmvplayer.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guiscreenletterbox.h>
+#include <presentation/presentation.h>
+#include <render/rendermanager/renderlayer.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <sound/soundmanager.h>
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <meta/locatorevents.h>
+#include <meta/eventlocator.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/triggervolumetracker.h>
+#include <cards/cardgallery.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/missionmanager.h>
+#include <mission/gameplaymanager.h>
+#include <mission/animatedicon.h>
+#include <worldsim/coins/sparkle.h>
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+InteriorManager* InteriorManager::spInstance = NULL;
+rmt::Randomizer InteriorManager::sRandom(0);
+bool InteriorManager::sRandomSeeded = false;
+unsigned InteriorManager::sPersistGagIndex = 0;
+extern bool cGuiManagerInGameActive;
+
+const int PLAYER_ONE = 0;
+
+// wow, what a hack
+
+struct InteriorCentre
+{
+ tName name;
+ rmt::Vector centre;
+ int level;
+};
+
+static InteriorCentre s_InteriorCentres[8];
+
+// another hack
+static const int NUM_OPAQUES = 1;
+static const char sOpaques[NUM_OPAQUES][16] = {
+ "gag_buly.p3d"
+};
+
+InteriorManager::GagBinding::GagBinding()
+{
+ Clear();
+}
+
+void InteriorManager::GagBinding::Clear(void)
+{
+ interiorUID = 0;
+ weight = 1;
+ random = true;
+
+ gagFileName[0] = 0;
+ cycleMode = DEFAULT_CYCLE_MODE;
+ triggered = false;
+ action = false;
+ retrigger = false;
+ useGagLocator = false;
+ gagLoc = 0;
+ gagPos.Set(0.0f,0.0f,0.0f);
+ useTriggerLocator = false;
+ triggerLoc = 0;
+ triggerPos.Set(0.0f,0.0f,0.0f);
+ soundID = 0;
+ gagFMVFileName[ 0 ] = 0;
+ loopIntro = 0;
+ loopOutro = 0;
+ cameraShake = false;
+ shakeDelay = 0.0f;
+ shakeDuration = 0.0f;
+ shake.playerID = 0;
+ shake.direction.Set(0.0f,1.0f,0.0f);
+ shake.looping = false;
+ coinDelay = 0.0f;
+ coins = 0;
+ sparkle = false;
+ animBV = false;
+ loadDist = 100;
+ unloadDist = 150;
+ soundLoadDist = 10;
+ soundUnloadDist = 20;
+ i_S_Movie = 0;
+ persistIndex = -1;
+ dialogChar1[0] = '\0';
+ dialogChar2[0] = '\0';
+ acceptDialogID = 0;
+ rejectDialogID = 0;
+ instructDialogID = 0;
+}
+
+class Gag;
+
+class GagDrawable : public DynaPhysDSG
+{
+ public:
+ GagDrawable(Gag* g);
+
+ virtual ~GagDrawable() { mpDrawstuff->Release(); };
+
+// void DisplayBoundingBox(tColour colour = tColour(0,255,0));
+// void DisplayBoundingSphere(tColour colour = tColour(0,255,0));
+
+ void GetBoundingBox(rmt::Box3D* box)
+ {
+ mpDrawstuff->GetBoundingBox(box);
+ if(useTranslate)
+ {
+ box->high += mPosn;
+ box->low += mPosn;
+ }
+ }
+
+ void GetBoundingSphere(rmt::Sphere* sphere)
+ {
+ mpDrawstuff->GetBoundingSphere(sphere);
+ if(useTranslate)
+ {
+ sphere->centre += mPosn;
+ }
+// sphere->radius += 10.0f;
+ }
+
+ virtual void Display();
+ void SetPosition(bool u, const rmt::Vector& v)
+ {
+ mPosn = v;
+ useTranslate = u;
+
+ if(useTranslate && mpSimStateObj)
+ {
+ rmt::Matrix matrix;
+ matrix.Identity();
+ matrix.FillTranslate(mPosn);
+ mpSimStateObj->SetTransform(matrix);
+ if(mPose)
+ {
+ mPose->Assign(((tCompositeDrawable*)mpDrawstuff)->GetPose());
+ mPose->GetJoint(0)->SetWorldTranslation(mPose->GetJoint(0)->GetWorldTranslation() + mPosn);
+ }
+ mpSimStateObj->GetCollisionObject()->SetHasRelocated( true );
+ mpSimStateObj->GetCollisionObject()->SetHasMoved( true );
+ mpSimStateObj->GetCollisionObject()->Update();
+ }
+ }
+
+ float sparkleStrength;
+
+ void SimUpdate(float timeins)
+ {
+ if(mPose)
+ {
+ mPose->Assign(((tCompositeDrawable*)mpDrawstuff)->GetPose());
+ mPose->GetJoint(0)->SetObjectTranslation(mPose->GetJoint(0)->GetObjectTranslation() + mPosn);
+ mpSimStateObj->GetCollisionObject()->SetHasMoved(true);
+ mpSimStateObj->GetCollisionObject()->Update();
+ }
+ }
+
+ void ApplyForce( const rmt::Vector& direction, float force )
+ {
+ // no force
+ }
+
+private:
+ tDrawable* mpDrawstuff;
+ Gag* gag;
+ bool useTranslate;
+ poser::Pose* mPose;
+
+private:
+ //Prevent wasteful constructor creation.
+ GagDrawable( const GagDrawable& pGagDrawable );
+ GagDrawable& operator=( const GagDrawable& pGagDrawable );
+};
+
+class Gag : public ActionButton::ButtonHandler, public AnimationPlayer::LoadDataCallBack, public EventListener, public NISSoundPlaybackCompleteCallback
+{
+public:
+ void Render(void)
+ {
+ gagPlayer->Render();
+ }
+
+ void Load(void)
+ {
+ char buffy[256];
+ sprintf( buffy, "art\\nis\\gags\\%s", binding->gagFileName );
+ gagPlayer->LoadData( buffy, this, false, 0 );
+
+ mLoading = true;
+ }
+
+ void SoundLoad(void)
+ {
+ if( binding->soundID != 0 )
+ {
+ GetSoundManager()->LoadNISSound( binding->soundID );
+ mSoundLoaded = true;
+ }
+ }
+
+ void Unload(void)
+ {
+ if(draw)
+ {
+ ((WorldRenderLayer*)GetRenderManager()->mpLayer(RenderEnums::LevelSlot))->pWorldScene()->Remove(draw);
+ }
+
+ gagPlayer->Stop();
+ gagPlayer->ClearData();
+
+ if(trigger)
+ {
+ GetTriggerVolumeTracker()->RemoveTrigger(trigger);
+ trigger->Release();
+ trigger = NULL;
+ }
+
+ if(locator)
+ {
+ locator->Release();
+ locator = NULL;
+ }
+
+ if(draw)
+ {
+ draw->Release();
+ draw = NULL;
+ }
+
+ if(collObj)
+ {
+ collObj->Release();
+ collObj = NULL;
+ }
+
+ mLoaded = false;
+ }
+
+ void SoundUnload(void)
+ {
+ if( binding->soundID != 0 )
+ {
+ GetSoundManager()->StopAndDumpNISSound( binding->soundID );
+ mSoundLoaded = false;
+ }
+ }
+
+ // Used when the dialog finishes.
+ void HandleEvent(EventEnum id, void* pEventData)
+ {
+ //Wait for the dialogue system to announce that the dialogue is finished
+ //playing. Right now we only listen to one type of event so we don't need
+ //to check the id.
+ GetInteriorManager()->SetISMovieDialogPlaying( false );
+ CGuiScreenLetterBox::ForceOpen();
+ gagPlayer->Play();
+ GetEventManager()->RemoveListener(this, EVENT_CONVERSATION_DONE);
+ TrafficManager::GetInstance()->RemoveCharacterToStopFor(mDialogEventData.char1);
+ TrafficManager::GetInstance()->RemoveCharacterToStopFor(mDialogEventData.char2);
+ // Display message for the player that they should go to the Aztec theature.
+ if(GetCharacterSheetManager()->IsState(1))
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_INGAME_DISPLAY_PROMPT, 10 );
+ }
+ }
+
+ void NISSoundPlaybackComplete()
+ {
+ m_isNISSoundComplete = true;
+ }
+
+ void Update( unsigned int elapsedTime )
+ {
+ if(mLoading)
+ {
+ return;
+ }
+
+ rmt::Vector charPos;
+ rmt::Vector gagPos = binding->gagPos;
+ GetCharacterManager()->GetCharacter(0)->GetPosition(charPos);
+ charPos.Sub(gagPos);
+
+ float dist = rmt::Abs(charPos.Magnitude());
+
+ if(mSoundLoaded && (dist > binding->soundUnloadDist))
+ {
+ SoundUnload();
+ }
+
+ if(!mSoundLoaded && (dist < binding->soundLoadDist))
+ {
+ SoundLoad();
+ }
+
+ // only do distance based loading for non interior gags, interior ones are always loaded
+ if( binding->interiorUID == static_cast< tUID >( 0 ) )
+ {
+ if(mLoaded && (dist > binding->unloadDist))
+ {
+ Unload();
+ }
+
+ if(!mLoaded && (dist < binding->loadDist))
+ {
+ Load();
+ }
+ }
+
+ if(draw)
+ {
+ if(binding->sparkle)
+ {
+ draw->sparkleStrength = 1.0f - rmt::Clamp((dist - 10.0f) * 0.1f, 0.0f, 1.0f);
+ }
+ else
+ {
+ draw->sparkleStrength = 0.0f;
+ }
+ }
+
+ if(!mLoaded)
+ {
+ return;
+ }
+
+ bool finished = (binding->gagFMVFileName[ 0 ] == 0) ? gagPlayer->IsFinished() : !GetPresentationManager()->GetFMVPlayer()->IsPlaying();
+ finished = finished && m_isNISSoundComplete;
+
+ if(finished && playing)
+ {
+ playing = false;
+ GetEventManager()->TriggerEvent(EVENT_GAG_END, (void*)binding);
+ if(binding->coinDelay < 0.0f)
+ {
+ rmt::Vector pos;
+ draw->GetPosition(&pos);
+ GetCoinManager()->SpawnInstantCoins(binding->coins, pos);
+ }
+ }
+
+ if(binding->retrigger)
+ {
+ if(!((binding->i_S_Movie == 1) && (!GetMissionManager()->IsSundayDrive())))
+ {
+ if(gagPlayer->IsFinished() && m_isNISSoundComplete)
+ {
+ gagPlayer->Rewind();
+
+ SoundUnload(); // Just to be sure
+ SoundLoad();
+
+ GetTriggerVolumeTracker()->AddTrigger(trigger);
+
+ //sparkles remain off once triggered
+ //sparkle = true;
+ }
+ }
+ }
+
+ if((mTimeToCameraShake > 0) && ((mTimeToCameraShake - (int)elapsedTime) <= 0))
+ {
+ mTimeToCameraShake = 0;
+ GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam()->AllowShake();
+ GetEventManager()->TriggerEvent(EVENT_CAMERA_SHAKE, &binding->shake);
+ }
+
+ if((mTimeToCameraShakeEnd > 0) && ((mTimeToCameraShakeEnd - (int)elapsedTime) <= 0))
+ {
+ mTimeToCameraShakeEnd = 0;
+ GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam()->DisableShake();
+ }
+
+ if((mTimeToCoinSpawn > 0) && ((mTimeToCoinSpawn - (int)elapsedTime) <= 0))
+ {
+ mTimeToCoinSpawn = 0;
+ rmt::Vector pos;
+ draw->GetPosition(&pos);
+ GetCoinManager()->SpawnInstantCoins(binding->coins, pos);
+ }
+
+ if(mTimeToCameraShake > 0)
+ {
+ mTimeToCameraShake -= elapsedTime;
+ }
+ if(mTimeToCameraShakeEnd > 0)
+ {
+ mTimeToCameraShakeEnd -= elapsedTime;
+ }
+ if(mTimeToCoinSpawn > 0)
+ {
+ mTimeToCoinSpawn -= elapsedTime;
+ }
+
+ gagPlayer->Update( elapsedTime );
+
+ draw->SimUpdate( static_cast< float >( elapsedTime) );
+ }
+
+ InteriorManager::GagBinding* GetBinding(void) { return binding; }
+
+protected:
+ void StartIntro(void)
+ {
+ if(binding->loopIntro != 0)
+ {
+ gagPlayer->Play();
+ }
+ }
+
+ DialogEventData mDialogEventData;
+
+ void PlayDialog(radKey32 DialogID)
+ {
+ rAssert(strlen(binding->dialogChar1) != 0 && strlen(binding->dialogChar1) < DialogueObjective::MAX_CHAR_NAME_LEN);
+ rAssert(strlen(binding->dialogChar2) != 0 && strlen(binding->dialogChar2) < DialogueObjective::MAX_CHAR_NAME_LEN);
+
+ //Register with the event system that you are listening for the dialogue end
+ //event.
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_DONE );
+
+ //Send the alert to the dialogue system to start playing this bad-ass
+ Character* c1 = GetCharacterManager()->GetCharacterByName(binding->dialogChar1);
+ tRefCounted::Assign(mDialogEventData.char1, c1);
+ TrafficManager::GetInstance()->AddCharacterToStopFor(c1);
+
+ Character* c2 = GetCharacterManager()->GetMissionCharacter(binding->dialogChar2);
+ tRefCounted::Assign(mDialogEventData.char2, c2);
+ TrafficManager::GetInstance()->AddCharacterToStopFor(c2);
+
+ mDialogEventData.dialogName = DialogID;
+ GetEventManager()->TriggerEvent(EVENT_CONVERSATION_INIT, (void*)(&mDialogEventData));
+ GetEventManager()->TriggerEvent(EVENT_CONVERSATION_INIT_DIALOG, (void*)(&mDialogEventData));
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_START, 0);
+ }
+
+ void Play(void)
+ {
+ rmt::Box3D box;
+ rmt::Vector pos;
+
+ playing = true;
+ bool alreadyPlayed = false;
+
+ if(binding->persistIndex != -1)
+ {
+ alreadyPlayed = GetCharacterSheetManager()->QueryGagViewed(GetGameplayManager()->GetCurrentLevelIndex(), binding->persistIndex);
+ if(!alreadyPlayed)
+ {
+ GetCharacterSheetManager()->AddGagViewed(GetGameplayManager()->GetCurrentLevelIndex(), binding->persistIndex);
+
+ GetEventManager()->TriggerEvent( EVENT_GAG_FOUND );
+ }
+ else
+ {
+ // We've already played this gag. Don't spawn any more coins. We can safely change this in the
+ //binding because we're dealing with a state that is persistent over the entire game. If this
+ //functionality changes we'll need to set some sort of flag in the the playing gag which the
+ //coin spawning coin can check later on.
+ binding->coins = 0;
+ }
+ }
+
+ //
+ // Not getting good position info here. Play from avatar position
+ //
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar != NULL ); //NIGEL - I put this assert in for you! love Ian.
+
+ if( avatar != NULL )
+ {
+ avatar->GetPosition( pos );
+ box.Set( pos, pos );
+ }
+ else
+ {
+ box.Set( rmt::Vector( 0.0f, 0.0f, 0.0f ), rmt::Vector( 0.0f, 0.0f, 0.0f ) );
+ }
+
+ // special case logic for the I&S movie. *sigh*
+ if((binding->i_S_Movie == 1) && (!GetCharacterSheetManager()->IsState(1)))
+ {
+ //The player is trying to pick up the I&S ticket!
+ bool haveAllCards = true;
+ for(int level = RenderEnums::L1; level < RenderEnums::numLevels; ++level)
+ {
+ if(GetCardGallery()->IsCardDeckComplete(level) == false)
+ {
+ haveAllCards = false;
+ break;
+ }
+ }
+ if(haveAllCards)
+ {
+ // They have all the cards, give them the ticket.
+ GetCharacterSheetManager()->SetState(1, true);
+ GetCharacterSheetManager()->SetFMVUnlocked(RenderEnums::L3);
+ GetInteriorManager()->SetISMovieDialogPlaying( true );
+ PlayDialog(binding->acceptDialogID);
+ binding->retrigger = false;
+ binding->triggered = false;
+ // Do the collection effect.
+ GetInteriorManager()->CollectionEffect("card_collect", binding->triggerPos);
+ }
+ else
+ {
+ if(GetCharacterSheetManager()->IsState(0))
+ {
+ // Not enough cards.
+ GetInteriorManager()->SetISMovieDialogPlaying( true );
+ PlayDialog(binding->rejectDialogID);
+// GetSoundManager()->PlayNISSound( binding->rejectSoundID, &box );
+ }
+ else
+ {
+ // Give instructions.
+ GetCharacterSheetManager()->SetState(0, true);
+ GetInteriorManager()->SetISMovieDialogPlaying( true );
+ PlayDialog(binding->instructDialogID);
+// GetSoundManager()->PlayNISSound( binding->instructSoundID, &box );
+ }
+ }
+ }
+ else if(binding->i_S_Movie == 2)
+ {
+ // The player is trying to watch the I&S movie.
+ if(GetCharacterSheetManager()->QueryFMVUnlocked(RenderEnums::L3))
+ {
+ // They have a ticket, play the movie.
+ GetPresentationManager()->PlayFMV( binding->gagFMVFileName );
+ }
+ else
+ {
+ // They don't have a ticket.
+ GetInteriorManager()->SetISMovieDialogPlaying( true );
+ PlayDialog(binding->rejectDialogID);
+ }
+ gagPlayer->Play();
+ }
+ else
+ {
+ if( !mSoundLoaded )
+ {
+ SoundLoad();
+ }
+ GetSoundManager()->PlayNISSound( binding->soundID, &box, this );
+ m_isNISSoundComplete = false;
+
+ if(binding->loopIntro != 0)
+ {
+ gagPlayer->DoneIntro();
+ }
+ else
+ {
+ gagPlayer->Play();
+ }
+
+ if( binding->gagFMVFileName[ 0 ] != 0 )
+ {
+ GetPresentationManager()->PlayFMV( binding->gagFMVFileName, NULL, true, false, false );
+ }
+ }
+
+ if(trigger)
+ {
+ GetTriggerVolumeTracker()->RemoveTrigger(trigger);
+ }
+
+ if(binding->cameraShake)
+ {
+ mTimeToCameraShake = (binding->shakeDelay == 0.0f) ? 1 : int(binding->shakeDelay * 1000.0f);
+ mTimeToCameraShakeEnd = (binding->shakeDuration == 0.0f) ? 0 : int((binding->shakeDelay + binding->shakeDuration) * 1000.0f);
+ }
+
+ if(binding->coins && (binding->coinDelay >= 0.0f) && !alreadyPlayed)
+ {
+ mTimeToCoinSpawn = (binding->coinDelay == 0.0f) ? 1 : int(binding->coinDelay * 1000.0f);
+ }
+
+ GetEventManager()->TriggerEvent(EVENT_GAG_START, (void*)binding);
+ sparkle = false;
+ }
+
+public:
+ Gag(InteriorManager::GagBinding* b)
+ : binding(NULL),
+ locator(NULL),
+ trigger(NULL),
+ draw(NULL),
+ gagPlayer(NULL),
+ collObj(NULL),
+ mLoaded(false),
+ mLoading(false),
+ mTimeToCameraShake(0),
+ mTimeToCameraShakeEnd(0),
+ mTimeToCoinSpawn(0),
+ sparkle(false),
+ mSoundLoaded(false),
+ playing(false),
+ m_isNISSoundComplete(true)
+ {
+ binding = b;
+
+ gagPlayer = new NISPlayer;
+ gagPlayer->SetNameData( "GagController", NULL, "GagScene" );
+ gagPlayer->SetPlayAfterLoad( false );
+ gagPlayer->SetCycleMode( binding->cycleMode );
+ gagPlayer->SetShowAlways( true );
+ gagPlayer->SetIntroLoop(binding->loopIntro);
+ gagPlayer->SetOutroLoop(binding->loopOutro);
+
+ if(binding->interiorUID != static_cast< tUID >( 0 ) )
+ {
+ Load();
+ }
+ }
+
+ ~Gag()
+ {
+ Unload();
+ SoundUnload();
+ delete gagPlayer;
+ }
+
+ bool IsLoaded(void)
+ {
+ return mLoaded;
+ }
+
+ void OnLoadDataComplete(void)
+ {
+ bool oldOnly = p3d::inventory->GetCurrentSectionOnly();
+ p3d::inventory->PushSection();
+
+ char buffy[256];
+ sprintf( buffy, "art\\nis\\gags\\%s", binding->gagFileName );
+ p3d::inventory->SelectSection(buffy);
+ p3d::inventory->SetCurrentSectionOnly(true);
+ mLoaded = true;
+ mLoading = false;
+
+ collObj = p3d::find<sim::CollisionObject>("GagScene");
+ if(collObj)
+ {
+ collObj->AddRef();
+ }
+
+ p3d::inventory->SetCurrentSectionOnly(false);
+ // Add gag animation to renderer
+ //
+ HeapMgr()->PushHeap(GMA_LEVEL_OTHER);
+ draw = new GagDrawable(this);
+ draw->AddRef();
+
+
+ if(binding->triggered)
+ {
+ if(!((binding->i_S_Movie == 1) &&
+ ((!GetMissionManager()->IsSundayDrive()) ||
+ (GetCharacterSheetManager()->QueryFMVUnlocked(RenderEnums::L3)))))
+ {
+ if(binding->useTriggerLocator)
+ {
+ Locator* l = p3d::find<Locator>(binding->triggerLoc);
+ //rAssert(l); //get's fixed up later
+ if(l)
+ {
+ l->GetLocation(&binding->triggerPos);
+ }
+ }
+
+ locator = new EventLocator;
+ locator->SetEventType( LocatorEvent::GAG );
+ locator->SetData((unsigned int)this);
+
+ trigger = new SphereTriggerVolume(binding->triggerPos, binding->triggerRadius);
+ trigger->SetLocator(locator);
+ locator->SetNumTriggers( 1, HeapMgr()->GetCurrentHeap() );
+ locator->AddTriggerVolume( trigger );
+
+ GetTriggerVolumeTracker()->AddTrigger(trigger);
+
+ locator->AddRef();
+ trigger->AddRef();
+
+ StartIntro();
+ }
+ }
+ else
+ {
+ SoundLoad();
+ Play();
+ }
+
+ rmt::Vector gagPos = binding->gagPos;
+ if(binding->useGagLocator)
+ {
+ Locator* l = p3d::find<Locator>(binding->gagLoc);
+ //rAssert(l); //apparently *something* else positions this properly later
+ if(l)
+ {
+ l->GetLocation(&gagPos);
+ binding->gagPos = gagPos;
+ }
+ }
+
+ if(gagPos == rmt::Vector(0.0f,0.0f,0.0f))
+ {
+ rmt::Box3D box;
+ draw->GetBoundingBox(&box);
+ rmt::Vector c = box.high + box.low;
+ c /= 2;
+ draw->SetPosition(false, c);
+ }
+ else
+ {
+ draw->SetPosition(true, gagPos);
+ }
+
+ ((WorldRenderLayer*)GetRenderManager()->mpLayer(RenderEnums::LevelSlot))->pWorldScene()->Add(draw);
+
+ p3d::inventory->PopSection();
+ p3d::inventory->SetCurrentSectionOnly(oldOnly);
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+
+ bool alreadyPlayed = false;
+ if(binding->persistIndex != -1)
+ {
+ alreadyPlayed = GetCharacterSheetManager()->QueryGagViewed(GetGameplayManager()->GetCurrentLevelIndex(), binding->persistIndex);
+ }
+ if(alreadyPlayed)
+ {
+ sparkle = false;
+ }
+ else
+ {
+ sparkle = true;
+ }
+ }
+
+ void Trigger(EventLocator* loc)
+ {
+ if(((binding->gagFMVFileName[0] != 0) || (binding->i_S_Movie != 0)))
+ {
+ if(!GetGameplayManager()->GetCurrentMission()->IsSundayDrive())
+ {
+ return;
+ }
+ }
+
+ if(binding->action)
+ {
+ Character* pCharacter = GetCharacterManager()->GetCharacter( PLAYER_ONE );
+ if(loc->GetPlayerEntered())
+ {
+ //
+ // Send a message about an active interactive gag
+ //
+ if(binding->triggered)
+ {
+ GetEventManager()->TriggerEvent( EVENT_INTERACTIVE_GAG );
+ }
+
+ Enter(pCharacter);
+ pCharacter->AddActionButtonHandler(this);
+ }
+ else
+ {
+ Exit(pCharacter);
+ pCharacter->RemoveActionButtonHandler(this);
+ }
+ }
+ else
+ {
+ if(loc->GetPlayerEntered())
+ {
+ Play();
+ }
+ }
+ }
+
+protected:
+ friend class GagDrawable;
+
+ InteriorManager::GagBinding* binding;
+ EventLocator* locator;
+ SphereTriggerVolume* trigger;
+ GagDrawable* draw;
+ NISPlayer* gagPlayer;
+ sim::CollisionObject* collObj;
+ bool mLoaded;
+ bool mLoading;
+ int mTimeToCameraShake;
+ int mTimeToCameraShakeEnd;
+ int mTimeToCoinSpawn;
+ bool sparkle;
+ bool mSoundLoaded;
+ bool playing;
+ bool m_isNISSoundComplete;
+
+
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+ {
+ Play();
+ return true;
+ }
+};
+
+GagDrawable::GagDrawable(Gag* g) : gag(g), mPose(NULL)
+{
+ mPosn.Set(0.0f, 0.0f, 0.0f);
+ mpDrawstuff = gag->gagPlayer->GetDrawable();
+ mpDrawstuff->AddRef();
+ mTranslucent = true;
+
+ // I need to go take a shower after this
+ for(int i = 0; i < NUM_OPAQUES; i++)
+ {
+ if(strcmp(sOpaques[i], gag->binding->gagFileName) == 0)
+ {
+ mTranslucent = false;
+ }
+ }
+
+ useTranslate = false;
+ sparkleStrength = 0.0f;
+
+ if(g->collObj)
+ {
+ tCompositeDrawable* cd = dynamic_cast<tCompositeDrawable*>(mpDrawstuff);
+ if(cd && gag->binding->animBV)
+ {
+ mPose = new poser::Pose(cd->GetPose());
+ SetSimState(sim::SimStateArticulated::CreateSimStateArticulated(mPose, g->collObj, NULL));
+ mpSimStateObj->SetControl(sim::simAICtrl);
+ mpSimStateObj->GetCollisionObject()->SetIsStatic(false);
+ }
+ else
+ {
+ SetSimState(sim::SimState::CreateStaticSimState(g->collObj));
+ mpSimStateObj->GetCollisionObject()->SetIsStatic(false);
+ }
+ }
+}
+
+void GagDrawable::Display()
+{
+ if((gag->binding->i_S_Movie == 1) && (GetCharacterSheetManager()->QueryFMVUnlocked(RenderEnums::L3)))
+ {
+ return;
+ }
+ if(useTranslate)
+ {
+ p3d::stack->Push();
+ p3d::stack->Translate(mPosn);
+ }
+
+ gag->Render();
+
+ if(useTranslate)
+ {
+ p3d::stack->Pop();
+ }
+
+ if((sparkleStrength > 0.0f) && gag->sparkle)
+ {
+ float size = gag->binding->interiorUID == static_cast< tUID >( 0 ) ? 1.0f : 0.5f;
+ GetSparkleManager()->AddGagSparkle(gag->binding->triggerPos, size, sparkleStrength, (unsigned int)this);
+ }
+}
+
+class InteriorExit : public ActionButton::ButtonHandler
+{
+public:
+ virtual bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+ {
+ if( cGuiManagerInGameActive && !GetGuiSystem()->GetInGameManager()->IsEnteringPauseMenu() )
+ {
+ GetEventManager()->TriggerEvent( EVENT_EXIT_INTERIOR_START );
+ }
+ return true;
+ }
+};
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// InteriorManager::CreateInstance
+//==============================================================================
+//
+// Description: Creates the InteriorManager.
+//
+// Parameters: None.
+//
+// Return: Pointer to the InteriorManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+InteriorManager* InteriorManager::CreateInstance()
+{
+ s_InteriorCentres[0].name.SetText("SpringfieldElementary");
+ s_InteriorCentres[0].centre.Set(500, -20, -350);
+ s_InteriorCentres[0].level = RenderEnums::L1;
+
+ s_InteriorCentres[1].name.SetText("KwikEMart");
+ s_InteriorCentres[1].centre.Set(500, -20, -300);
+ s_InteriorCentres[1].level = RenderEnums::L1;
+
+ s_InteriorCentres[2].name.SetText("SimpsonsHouse");
+ s_InteriorCentres[2].centre.Set(500, -20, -400);
+ s_InteriorCentres[2].level = RenderEnums::L1;
+
+ s_InteriorCentres[3].name.SetText("dmv");
+ s_InteriorCentres[3].centre.Set(0, -20, -200);
+ s_InteriorCentres[3].level = RenderEnums::L2;
+
+ s_InteriorCentres[4].name.SetText("moe1");
+ s_InteriorCentres[4].centre.Set(50, -20, -200);
+ s_InteriorCentres[4].level = RenderEnums::L2;
+
+ s_InteriorCentres[5].name.SetText("Android");
+ s_InteriorCentres[5].centre.Set(0, -20, -350);
+ s_InteriorCentres[5].level = RenderEnums::L3;
+
+ s_InteriorCentres[6].name.SetText("Observatory");
+ s_InteriorCentres[6].centre.Set(150, -20, -350);
+ s_InteriorCentres[6].level = RenderEnums::L3;
+
+ s_InteriorCentres[7].name.SetText("bartroom");
+ s_InteriorCentres[7].centre.Set(500, -20, -450);
+ s_InteriorCentres[7].level = RenderEnums::L1;
+
+ MEMTRACK_PUSH_GROUP( "InteriorManager" );
+
+ rAssert( spInstance == NULL );
+
+ spInstance = new InteriorManager;
+ rAssert( spInstance );
+
+ MEMTRACK_POP_GROUP("InteriorManager");
+
+ return spInstance;
+}
+
+const tName& InteriorManager::ClassifyPoint(const rmt::Vector& point)
+{
+ static tName none((tUID)0);
+
+ for(int i = 0; i < 8; i++)
+ {
+ if((GetGameplayManager()->GetCurrentLevelIndex() % 3) == s_InteriorCentres[i].level)
+ {
+ rmt::Vector dist = point - s_InteriorCentres[i].centre;
+ if(dist.Magnitude() < 24.0f)
+ {
+ return s_InteriorCentres[i].name;
+ }
+ }
+ }
+
+ return none;
+}
+
+//==============================================================================
+// InteriorManager::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the InteriorManager singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the InteriorManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+InteriorManager* InteriorManager::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// InteriorManager::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the InteriorManager.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void InteriorManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+}
+
+
+//==============================================================================
+// InteriorManager::OnBootupStart
+//==============================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void InteriorManager::OnBootupStart()
+{
+ GetEventManager()->AddListener( this, EVENT_INTERIOR_LOAD_START );
+ GetEventManager()->AddListener( this, EVENT_INTERIOR_LOADED );
+ GetEventManager()->AddListener( this, EVENT_INTERIOR_DUMPED );
+ GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_START );
+ GetEventManager()->AddListener( this, EVENT_EXIT_INTERIOR_START );
+ GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_TRANSITION_START );
+ GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_TRANSITION_END );
+ GetEventManager()->AddListener( this, static_cast<EventEnum>(EVENT_LOCATOR + LocatorEvent::INTERIOR_EXIT) );
+ GetEventManager()->AddListener( this, static_cast<EventEnum>(EVENT_LOCATOR + LocatorEvent::GAG) );
+ GetEventManager()->AddListener( this, EVENT_GUI_IRIS_WIPE_CLOSED );
+ GetEventManager()->AddListener( this, EVENT_GUI_IRIS_WIPE_OPEN );
+ GetEventManager()->AddListener( this, EVENT_CHARACTER_POS_RESET );
+
+ GetConsole()->AddFunction( "ClearGagBindings",
+ InteriorManager::ConsoleClearGagBindings,
+ "ClearGagBindings();",
+ 0, 0 );
+
+ GetConsole()->AddFunction( "AddGagBinding",
+ InteriorManager::ConsoleAddGagBinding,
+ "AddGagBinding(InteriorName, GagFileName, CycleMode, Weight, SoundResourceName);",
+ 5, 5 );
+
+ GetConsole()->AddFunction( "GagBegin",
+ InteriorManager::ConsoleGagBegin,
+ ",",
+ 1, 1 );
+
+ GetConsole()->AddFunction( "GagSetInterior",
+ InteriorManager::ConsoleGagSetInterior,
+ ",",
+ 1, 1 );
+
+ GetConsole()->AddFunction( "GagSetCycle",
+ InteriorManager::ConsoleGagSetCycle,
+ ",",
+ 1, 1 );
+
+ GetConsole()->AddFunction( "GagSetWeight",
+ InteriorManager::ConsoleGagSetWeight,
+ ",",
+ 1, 1 );
+
+ GetConsole()->AddFunction( "GagSetSound",
+ InteriorManager::ConsoleGagSetSound,
+ ",",
+ 1, 1 );
+
+ GetConsole()->AddFunction( "GagSetTrigger",
+ InteriorManager::ConsoleGagSetTrigger,
+ ",",
+ 3, 5 );
+
+ GetConsole()->AddFunction( "GagPlayFMV", InteriorManager::ConsoleGagPlayFMV,
+ "Play FMV specified by file name", 1, 1 );
+
+ GetConsole()->AddFunction( "GagSetPosition",
+ InteriorManager::ConsoleGagSetPosition,
+ ",",
+ 1, 3 );
+
+ GetConsole()->AddFunction( "GagSetRandom",
+ InteriorManager::ConsoleGagSetRandom,
+ ",",
+ 1, 1 );
+
+ GetConsole()->AddFunction( "GagSetIntro",
+ InteriorManager::ConsoleGagSetIntro,
+ ",",
+ 1, 1 );
+
+ GetConsole()->AddFunction( "GagSetOutro",
+ InteriorManager::ConsoleGagSetOutro,
+ ",",
+ 1, 1 );
+
+ GetConsole()->AddFunction( "GagSetCameraShake",
+ InteriorManager::ConsoleGagSetCameraShake,
+ ",",
+ 2, 3 );
+
+ GetConsole()->AddFunction( "GagSetCoins",
+ InteriorManager::ConsoleGagSetCoins,
+ ",",
+ 1, 2 );
+
+ GetConsole()->AddFunction( "GagSetSparkle",
+ InteriorManager::ConsoleGagSetSparkle,
+ ",",
+ 1, 1 );
+
+ GetConsole()->AddFunction( "GagSetAnimCollision",
+ InteriorManager::ConsoleGagSetAnimCollision,
+ ",",
+ 1, 1 );
+
+ GetConsole()->AddFunction( "GagEnd",
+ InteriorManager::ConsoleGagEnd,
+ ",",
+ 0, 0 );
+
+ GetConsole()->AddFunction( "GagSetLoadDistances",
+ InteriorManager::ConsoleGagSetLoadDistances,
+ ",",
+ 2, 2 );
+
+ GetConsole()->AddFunction( "GagSetSoundLoadDistances",
+ InteriorManager::ConsoleGagSetSoundLoadDistances,
+ ",",
+ 2, 2 );
+
+ GetConsole()->AddFunction( "GagSetPersist",
+ InteriorManager::ConsoleGagSetPersist,
+ ",",
+ 1, 1 );
+ GetConsole()->AddFunction( "GagCheckCollCards",
+ InteriorManager::ConsoleGagCheckCollCards,
+ ",",
+ 5, 5);
+ GetConsole()->AddFunction( "GagCheckMovie",
+ InteriorManager::ConsoleGagCheckMovie,
+ ",",
+ 4, 4);
+}
+
+
+//==============================================================================
+// InteriorManager::OnMissionRestart
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void InteriorManager::OnMissionRestart()
+{
+}
+
+
+//==============================================================================
+// InteriorManager::OnGameplayStart
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void InteriorManager::OnGameplayStart()
+{
+ if(!mLoadedGags)
+ {
+ mEntryRequested = false;
+ LoadGagNIS(0);
+ }
+ mCollectionEffect = new AnimatedIcon();
+ rAssert(mCollectionEffect);
+}
+
+
+//==============================================================================
+// InteriorManager::OnGameplayEnd
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void InteriorManager::OnGameplayEnd()
+{
+ ClearGags();
+ this->ClearGagBindings();
+
+ SwitchToExterior();
+ delete mCollectionEffect;
+ mCollectionEffect = 0;
+}
+
+//=============================================================================
+// InteriorManager::UnloadGagSounds
+//=============================================================================
+// Description: We need to do this before movies get played (i.e. Larry the
+// Looter)
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void InteriorManager::UnloadGagSounds()
+{
+ int i;
+
+ for( i = 0; i < mGagCount; i++ )
+ {
+ gags[i]->SoundUnload();
+ }
+}
+
+void InteriorManager::Enter(InteriorEntranceLocator* entry, Character* pCharacter, Sequencer* pSeq)
+{
+ //
+ // Once this sequence starts, we're on autopilot until the character
+ // appears and hits his cue in the interior so disable controller input.
+ //
+ pCharacter->GetController()->SetActive( false );
+
+ rmt::Matrix transform = entry->GetTransform();
+ rmt::Vector standPosition;
+ entry->GetLocation( &standPosition ) ;
+
+ Action* pAction = 0;
+
+ // trigger the event.
+ //
+ pSeq->BeginSequence();
+ pAction = new TriggerEventAction( EVENT_ENTER_INTERIOR_START, (void*)&mLoadedInteriorUID );
+ pSeq->AddAction( pAction );
+ pAction = new TriggerEventAction( EVENT_ENTER_INTERIOR_TRANSITION_START, (void*)&mLoadedInteriorUID );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+
+ // Walk to entry point
+ /*
+ pSeq->BeginSequence();
+
+ pAction = new Arrive( pCharacter, standPosition );
+ pSeq->AddAction( pAction );
+ pAction = pCharacter->GetWalkerLocomotionAction();
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence();
+ */
+
+ // Orient
+ rmt::Vector direction = transform.Row( 2 );
+ direction.y = 0.0f;
+ // If direction != 0, 0, 0, then rotate us to align with direction.
+ //
+ if ( direction.NormalizeSafe( ) )
+ {
+ pSeq->BeginSequence();
+ pAction = new Orient( pCharacter, direction );
+ pSeq->AddAction( pAction );
+ // SlaveLoco
+ //
+ pAction = 0;
+
+ pAction = pCharacter->GetWalkerLocomotionAction();
+
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+ }
+
+/*
+ // Move character.
+ //
+ pSeq->BeginSequence();
+ pAction = new Position( pCharacter, standPosition, 0.1f );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence( );
+*/
+
+}
+
+//==============================================================================
+// InteriorManager::Update
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void InteriorManager::Update( unsigned int elapsedTime )
+{
+ for(int i = 0; i < mGagCount; i++)
+ {
+ if(gags[i])
+ {
+ gags[i]->Update(elapsedTime);
+ }
+ }
+
+ if(mInteriorAnimations)
+ {
+ mInteriorAnimations->Advance((float)elapsedTime);
+ }
+
+ AttemptEntry();
+
+ mCollectionEffect->Update(elapsedTime);
+}
+
+
+//==============================================================================
+// InteriorManager::HandleEvent
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void InteriorManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_INTERIOR_LOAD_START:
+ {
+ rAssert( pEventData != 0 );
+ InteriorLoadedEventData* pData = reinterpret_cast<InteriorLoadedEventData*>( pEventData );
+
+ mSection = pData->sectionName.GetUID();
+ mLoadedInteriorUID = pData->interiorName.GetUID();
+ }
+ break;
+
+ case EVENT_ENTER_INTERIOR_TRANSITION_START:
+ {
+ mState = ENTER;
+ GetExitPos();
+ }
+ break;
+
+ case EVENT_INTERIOR_LOADED:
+ {
+ rAssert( pEventData != 0 );
+ InteriorLoadedEventData* pData = reinterpret_cast<InteriorLoadedEventData*>( pEventData );
+
+ mSection = pData->sectionName.GetUID();
+ mLoadedInteriorUID = pData->interiorName.GetUID();
+
+ mInteriorLoaded = true;
+
+ if(mIn)
+ {
+ SetupLightsAndAnims();
+ }
+ }
+ break;
+
+ case EVENT_INTERIOR_DUMPED:
+ {
+ mInteriorLoaded = false;
+ mLoadedInteriorUID = 0;
+ }
+ break;
+
+ case EVENT_CHARACTER_POS_RESET :
+ {
+ rmt::Vector pos;
+ GetCharacterManager()->GetCharacter(0)->GetPosition(pos);
+ LoadLevelGags(pos);
+ }
+ break;
+
+ case EVENT_GUI_IRIS_WIPE_OPEN:
+ {
+ Character* pCharacter = GetCharacterManager()->GetCharacter( PLAYER_ONE );
+ pCharacter->GetController()->SetActive( true );
+
+ if(mState == ENTER)
+ {
+ mState = INSIDE;
+ }
+
+ if(mState == EXIT)
+ {
+ mState = NONE;
+ }
+ }
+ break;
+
+ case EVENT_GUI_IRIS_WIPE_CLOSED:
+ {
+ switch( mState )
+ {
+ case ENTER:
+ {
+ GetRenderManager()->mpLayer(RenderEnums::LevelSlot)->Freeze();
+ mEntryRequested = true;
+ mLoadedGags = false;
+ }
+ break;
+
+ case EXIT:
+ {
+ this->ExitInterior();
+ }
+ break;
+
+ default:
+ {
+ //rAssert( 0 );
+ }
+ }
+ }
+ break;
+
+ case EVENT_EXIT_INTERIOR_START:
+ {
+ Character* pCharacter = GetCharacterManager()->GetCharacter( PLAYER_ONE );
+ pCharacter->GetController()->SetIntention( CharacterController::NONE );
+ pCharacter->GetController()->SetActive( false );
+ mState = EXIT;
+ }
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::INTERIOR_EXIT:
+ {
+ if((mState == INSIDE) || (mState == ENTER))
+ {
+ if(1)
+ {
+ Character* pCharacter = GetCharacterManager()->GetCharacter( PLAYER_ONE );
+ EventLocator* loc = (EventLocator*)pEventData;
+ if(loc->GetPlayerEntered())
+ {
+ mExit->Enter(pCharacter);
+ pCharacter->AddActionButtonHandler(mExit);
+ }
+ else
+ {
+ mExit->Exit(pCharacter);
+ pCharacter->RemoveActionButtonHandler(mExit);
+ }
+ }
+ else
+ {
+ EventLocator* loc = (EventLocator*)pEventData;
+ if(loc->GetPlayerEntered())
+ {
+ mExit->OnButtonPressed(NULL, NULL);
+ }
+ }
+ }
+ }
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::GAG:
+ {
+ EventLocator* loc = (EventLocator*)pEventData;
+ for(int i = 0; i < mGagCount; i++)
+ {
+ if(loc->GetData() == (unsigned int)gags[i])
+ {
+ gags[i]->Trigger(loc);
+ }
+ }
+ }
+ break;
+
+ default:
+ {
+ }
+ }
+}
+
+
+//==============================================================================
+// InteriorManager::ConsoleClearGagBindings
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void InteriorManager::ConsoleClearGagBindings( int argc, char** argv )
+{
+ GetInteriorManager()->ClearGagBindings();
+}
+
+void InteriorManager::ClearGagBindings()
+{
+ for(int i = 0; i < mBindingCount; i++)
+ {
+ mGagBindings[i].Clear();
+ }
+
+ mBindingCount = 0;
+ sPersistGagIndex = 0;
+}
+
+
+//==============================================================================
+// InteriorManager::ConsoleAddGagBinding
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void InteriorManager::ConsoleAddGagBinding( int argc, char** argv )
+{
+ // Convert interior name to UID
+ //
+ tUID interiorUID = tName::MakeUID( argv[1] );
+
+ char* gagFileName = argv[2];
+
+ // Select cycle mode.
+ //
+ p3dCycleMode cycleMode;
+
+ if( !strcmp( "default", argv[3] ) )
+ {
+ cycleMode = DEFAULT_CYCLE_MODE;
+ }
+ else if( !strcmp( "cycle", argv[3] ) )
+ {
+ cycleMode = FORCE_CYCLIC;
+ }
+ else if( !strcmp( "single", argv[3] ) )
+ {
+ cycleMode = FORCE_NON_CYCLIC;
+ }
+ else
+ {
+ rAssertMsg( 0, "Invalid cycle mode" );
+ cycleMode = DEFAULT_CYCLE_MODE;
+ }
+
+ // Assign weighted probability of selecting gag.
+ //
+ int weight = atoi( argv[4] );
+
+ //
+ // Get the name of the sound resource to play with this gag
+ //
+ char* gagSound = argv[5];
+
+ // Send it on down...
+ //
+ GetInteriorManager()->AddGagBinding( interiorUID, gagFileName, cycleMode, weight, gagSound );
+}
+
+
+//==============================================================================
+// InteriorManager::AddGagBinding
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void InteriorManager::AddGagBinding
+(
+ tUID interiorUID,
+ char* gagFileName,
+ p3dCycleMode cycleMode,
+ int weight,
+ char* gagSound
+)
+{
+ // Can raise the max if necessary.
+ //
+ rAssert( mBindingCount < MAX_BINDINGS );
+
+ // Convert interior name to UID
+ //
+ mGagBindings[mBindingCount].interiorUID = interiorUID;
+
+ // Save the gag filename.
+ //
+ rAssert( strlen( gagFileName ) < 13 );
+ strcpy( mGagBindings[mBindingCount].gagFileName, gagFileName );
+
+ // Select cycle mode.
+ //
+ mGagBindings[mBindingCount].cycleMode = cycleMode;
+
+ // Assign weighted probability of selecting gag.
+ //
+ mGagBindings[mBindingCount].weight = weight;
+
+ //
+ // Make a radKey out of the gag sound name
+ //
+ mGagBindings[mBindingCount].soundID = ::radMakeKey32( gagSound );
+
+ mGagBindings[ mBindingCount ].gagFMVFileName[ 0 ] = 0;
+
+ if(mGagBindings[mBindingCount].interiorUID)
+ {
+ if(!mGagBindings[ mBindingCount ].useGagLocator)
+ {
+ mGagBindings[ mBindingCount ].useGagLocator = true;
+ mGagBindings[ mBindingCount ].gagLoc = tEntity::MakeUID("InteriorOrigin");
+ }
+ }
+
+ ++mBindingCount;
+}
+
+void InteriorManager::GagBegin(char* gagFileName)
+{
+ rAssert(!mBuildingGag);
+ mBuildingGag = true;
+
+ // Can raise the max if necessary.
+ rAssert( mBindingCount < MAX_BINDINGS );
+
+ // Save the gag filename.
+ rAssert( strlen( gagFileName ) < 13 );
+ strcpy( mGagBindings[mBindingCount].gagFileName, gagFileName );
+}
+
+InteriorManager::GagBinding* InteriorManager::GetBuildBinding(void)
+{
+ rAssert(mBuildingGag);
+ return &mGagBindings[mBindingCount];
+}
+
+void InteriorManager::GagEnd(void)
+{
+ rAssert(mBuildingGag);
+ mBuildingGag = false;
+ if((mGagBindings[mBindingCount].i_S_Movie == 1) && (GetCharacterSheetManager()->IsState(1)))
+ {
+ // Oops we've already got the ticket! Don't put it in again.
+ mGagBindings[mBindingCount].Clear();
+ return;
+ }
+ ++mBindingCount;
+}
+
+void InteriorManager::ConsoleGagBegin(int argc, char** argv )
+{
+ GetInteriorManager()->GagBegin(argv[1]);
+}
+
+void InteriorManager::ConsoleGagSetInterior(int argc, char** argv )
+{
+ GetInteriorManager()->GetBuildBinding()->interiorUID = tEntity::MakeUID(argv[1]);
+
+ if(GetInteriorManager()->GetBuildBinding()->interiorUID)
+ {
+ if(!GetInteriorManager()->GetBuildBinding()->useGagLocator)
+ {
+ GetInteriorManager()->GetBuildBinding()->useGagLocator = true;
+ GetInteriorManager()->GetBuildBinding()->gagLoc = tEntity::MakeUID("InteriorOrigin");
+ }
+ }
+}
+
+void InteriorManager::ConsoleGagSetCycle(int argc, char** argv )
+{
+ // Select cycle mode.
+ //
+ p3dCycleMode cycleMode;
+
+ if( !strcmp( "default", argv[1] ) )
+ {
+ cycleMode = DEFAULT_CYCLE_MODE;
+ }
+ else if( !strcmp( "cycle", argv[1] ) )
+ {
+ cycleMode = FORCE_CYCLIC;
+ }
+ else if( !strcmp( "single", argv[1] ) )
+ {
+ cycleMode = FORCE_NON_CYCLIC;
+ }
+ else if( !strcmp( "reset", argv[1] ) )
+ {
+ cycleMode = FORCE_NON_CYCLIC;
+ GetInteriorManager()->GetBuildBinding()->retrigger = true;
+ }
+ else
+ {
+ rAssertMsg( 0, "Invalid cycle mode" );
+ cycleMode = DEFAULT_CYCLE_MODE;
+ }
+
+ GetInteriorManager()->GetBuildBinding()->cycleMode = cycleMode;
+}
+
+void InteriorManager::ConsoleGagSetWeight(int argc, char** argv )
+{
+ GetInteriorManager()->GetBuildBinding()->weight = atoi(argv[1]);
+}
+
+void InteriorManager::ConsoleGagSetSound(int argc, char** argv )
+{
+ GetInteriorManager()->GetBuildBinding()->soundID = ::radMakeKey32(argv[1]);
+}
+
+void InteriorManager::ConsoleGagSetIntro( int argc, char** argv )
+{
+ GetInteriorManager()->GetBuildBinding()->loopIntro = atoi(argv[1]);
+}
+
+void InteriorManager::ConsoleGagSetOutro( int argc, char** argv )
+{
+ GetInteriorManager()->GetBuildBinding()->loopOutro = atoi(argv[1]);
+}
+
+/*=============================================================================
+Description: Play the FMV specified by file name. Note that PlayFMV causes
+ the HUD to be un/reloaded.
+=============================================================================*/
+void InteriorManager::ConsoleGagPlayFMV( int argc, char** argv )
+{
+ rAssert( argc > 1 );
+ rAssert( strlen( argv[ 1 ] ) < 13 ); // Enforce 8.3 file naming.
+ strcpy( GetInteriorManager()->GetBuildBinding()->gagFMVFileName, argv[ 1 ] );
+}
+
+void InteriorManager::ConsoleGagSetTrigger(int argc, char** argv )
+{
+ rAssert((argc == 4) || (argc == 6));
+
+ GetInteriorManager()->GetBuildBinding()->triggered = true;
+
+ if( strcmp( "touch", argv[1] ) == 0 )
+ {
+ GetInteriorManager()->GetBuildBinding()->action = false;
+ }
+ else if( strcmp( "action", argv[1]) == 0 )
+ {
+ GetInteriorManager()->GetBuildBinding()->action = true;
+ }
+
+ if(argc == 4)
+ {
+ GetInteriorManager()->GetBuildBinding()->useTriggerLocator = true;
+ GetInteriorManager()->GetBuildBinding()->triggerLoc = tEntity::MakeUID(argv[2]);
+ }
+ else
+ {
+ GetInteriorManager()->GetBuildBinding()->useTriggerLocator = false;
+ GetInteriorManager()->GetBuildBinding()->triggerPos.Set((float)atof(argv[2]), (float)atof(argv[3]), (float)atof(argv[4]));
+ }
+
+ GetInteriorManager()->GetBuildBinding()->triggerRadius = (float)atof(argv[argc-1]);
+}
+
+void InteriorManager::ConsoleGagSetPosition(int argc, char** argv )
+{
+ rAssert((argc == 2) || (argc == 4));
+
+ if(argc == 2)
+ {
+ GetInteriorManager()->GetBuildBinding()->useGagLocator = true;
+ GetInteriorManager()->GetBuildBinding()->gagLoc = tEntity::MakeUID(argv[1]);
+ }
+ else
+ {
+ GetInteriorManager()->GetBuildBinding()->useGagLocator = false;
+ GetInteriorManager()->GetBuildBinding()->gagPos.Set((float)atof(argv[1]), (float)atof(argv[2]), (float)atof(argv[3]));
+ }
+}
+
+void InteriorManager::ConsoleGagSetRandom(int argc, char** argv )
+{
+ GetInteriorManager()->GetBuildBinding()->random = atoi(argv[1]) != 0;
+}
+
+void InteriorManager::ConsoleGagSetCameraShake(int argc, char** argv )
+{
+ GetInteriorManager()->GetBuildBinding()->cameraShake = true;
+ GetInteriorManager()->GetBuildBinding()->shakeDelay = (float)atof(argv[1]);
+ GetInteriorManager()->GetBuildBinding()->shake.force = (float)atof(argv[2]);
+
+ if(argc == 4)
+ {
+ GetInteriorManager()->GetBuildBinding()->shakeDuration = (float)atof(argv[3]);
+ GetInteriorManager()->GetBuildBinding()->shake.looping = GetInteriorManager()->GetBuildBinding()->shakeDuration != 0.0f;
+ }
+}
+
+void InteriorManager::ConsoleGagSetCoins(int argc, char** argv )
+{
+ GetInteriorManager()->GetBuildBinding()->coins = atoi(argv[1]);
+ if(argc == 3)
+ {
+ GetInteriorManager()->GetBuildBinding()->coinDelay = (float)atof(argv[2]);
+ }
+}
+
+void InteriorManager::ConsoleGagSetSparkle(int argc, char** argv )
+{
+ GetInteriorManager()->GetBuildBinding()->sparkle = atoi(argv[1]) != 0;
+}
+
+void InteriorManager::ConsoleGagSetAnimCollision(int argc, char** argv )
+{
+ GetInteriorManager()->GetBuildBinding()->animBV = atoi(argv[1]) != 0;
+}
+
+void InteriorManager::ConsoleGagSetLoadDistances( int argc, char** argv )
+{
+ GetInteriorManager()->GetBuildBinding()->loadDist = atoi(argv[1]);
+ GetInteriorManager()->GetBuildBinding()->unloadDist = atoi(argv[2]);
+}
+
+void InteriorManager::ConsoleGagSetSoundLoadDistances( int argc, char** argv )
+{
+ GetInteriorManager()->GetBuildBinding()->soundLoadDist = atoi(argv[1]);
+ GetInteriorManager()->GetBuildBinding()->soundUnloadDist = atoi(argv[2]);
+}
+
+void InteriorManager::ConsoleGagSetPersist( int argc, char** argv )
+{
+ if(atoi(argv[1]))
+ {
+ GetInteriorManager()->GetBuildBinding()->persistIndex = sPersistGagIndex++;
+ }
+ else
+ {
+ GetInteriorManager()->GetBuildBinding()->persistIndex = -1;
+ }
+}
+
+void InteriorManager::ConsoleGagCheckCollCards(int argc, char** argv)
+{
+ GetInteriorManager()->GetBuildBinding()->i_S_Movie = 1;
+
+ unsigned int len = strlen(argv[1]) < DialogueObjective::MAX_CHAR_NAME_LEN - 1 ? strlen(argv[1]) : DialogueObjective::MAX_CHAR_NAME_LEN - 2;
+ strncpy( GetInteriorManager()->GetBuildBinding()->dialogChar1, argv[1], len );
+ GetInteriorManager()->GetBuildBinding()->dialogChar1[len] = '\0';
+
+ len = strlen(argv[2]) < DialogueObjective::MAX_CHAR_NAME_LEN - 1 ? strlen(argv[2]) : DialogueObjective::MAX_CHAR_NAME_LEN - 2;
+ strncpy( GetInteriorManager()->GetBuildBinding()->dialogChar2, argv[2], len );
+ GetInteriorManager()->GetBuildBinding()->dialogChar2[len] = '\0';
+
+ GetInteriorManager()->GetBuildBinding()->acceptDialogID = ::radMakeKey32(argv[3]);
+ GetInteriorManager()->GetBuildBinding()->instructDialogID = ::radMakeKey32(argv[4]);
+ GetInteriorManager()->GetBuildBinding()->rejectDialogID = ::radMakeKey32(argv[5]);
+}
+
+void InteriorManager::ConsoleGagCheckMovie(int argc, char** argv)
+{
+ GetInteriorManager()->GetBuildBinding()->i_S_Movie = 2;
+
+ unsigned int len = strlen(argv[1]) < DialogueObjective::MAX_CHAR_NAME_LEN - 1 ? strlen(argv[1]) : DialogueObjective::MAX_CHAR_NAME_LEN - 2;
+ strncpy( GetInteriorManager()->GetBuildBinding()->dialogChar1, argv[1], len );
+ GetInteriorManager()->GetBuildBinding()->dialogChar1[len] = '\0';
+
+ len = strlen(argv[2]) < DialogueObjective::MAX_CHAR_NAME_LEN - 1 ? strlen(argv[2]) : DialogueObjective::MAX_CHAR_NAME_LEN - 2;
+ strncpy( GetInteriorManager()->GetBuildBinding()->dialogChar2, argv[2], len );
+ GetInteriorManager()->GetBuildBinding()->dialogChar2[len] = '\0';
+
+ rAssert( strlen( argv[ 3 ] ) < 13 ); // Enforce 8.3 file naming.
+ strcpy( GetInteriorManager()->GetBuildBinding()->gagFMVFileName, argv[ 3 ] );
+ GetInteriorManager()->GetBuildBinding()->rejectDialogID = ::radMakeKey32(argv[4]);
+}
+
+void InteriorManager::ConsoleGagEnd(int argc, char** argv )
+{
+ GetInteriorManager()->GagEnd();
+}
+
+void InteriorManager::LoadLevelGags(const rmt::Vector& startPos, bool initial)
+{
+ tUID interior = ClassifyPoint(startPos).GetUID();
+
+ bool needTo = true;
+
+ if(!initial)
+ {
+ // this will happen if we are trying to reset from one interior into another
+ // the mission system sends too many events, so we'll ignore the bogus ones
+ if(!((interior == static_cast< tUID >( 0 )) || (interior == mLoadedInteriorUID)))
+ {
+ return;
+ }
+
+ needTo = mCurrentInteriorUID != interior;
+ }
+
+ if(needTo)
+ {
+ mCurrentInteriorUID = interior;
+
+ if(mCurrentInteriorUID)
+ {
+ LoadGagNIS(mCurrentInteriorUID);
+ mState = INSIDE;
+ mIn = true;
+ mEntryRequested = false;
+ GetExitPos();
+ SwitchToInterior();
+ }
+ else
+ {
+ mState = NONE;
+ mEntryRequested = false;
+ SwitchToExterior();
+ LoadGagNIS(0);
+ }
+
+ GetCharacterManager()->GetCharacter(0)->SetPosition(startPos);
+ for(int i = 0; i < mGagCount; i++)
+ {
+ if(gags[i])
+ {
+ gags[i]->Update(0);
+ }
+ }
+ }
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// InteriorManager::InteriorManager
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+InteriorManager::InteriorManager()
+:
+mEntryRequested( false ),
+mInteriorLoaded( false ),
+mLoadedInteriorUID( 0 ),
+mCurrentInteriorUID( 0 ),
+mCollectionEffect(0),
+mIn (false),
+mBindingCount( 0 ),
+mGagCount(0),
+mBuildingGag(false),
+mExit(new InteriorExit),
+mInteriorAnimations(NULL),
+m_isPlayingISDialog( false )
+{
+ mExit->AddRef();
+
+ for(int i = 0; i < MAX_GAGS; i++)
+ {
+ gags[i] = NULL;
+ }
+
+ if (!sRandomSeeded)
+ {
+ sRandom.Seed (Game::GetRandomSeed ());
+ sRandomSeeded = true;
+ }
+}
+
+
+//==============================================================================
+// InteriorManager::~InteriorManager
+//==============================================================================
+//
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+InteriorManager::~InteriorManager()
+{
+ mExit->Release();
+
+ GetEventManager()->RemoveAll(this);
+
+ ClearGags();
+}
+
+
+//==============================================================================
+void InteriorManager::SwitchToInterior()
+{
+ if(mLoadedInteriorUID != tUID(0))
+ {
+ mCurrentInteriorUID = mLoadedInteriorUID;
+ }
+
+ if(mCurrentInteriorUID == tEntity::MakeUID("moe1"))
+ {
+ static rmt::Matrix mirrorMatrix(-1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ 109.5f, 0.0f, 0.0f, 1.0f);
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( RenderEnums::LevelSlot ));
+ pWorldRenderLayer->SetMirror(true, &mirrorMatrix);
+
+ Character* moe = GetCharacterManager()->GetCharacterByName("moe");
+ if(moe && moe->IsAmbient())
+ {
+ moe->ResetAmbientPosition();
+ }
+
+ } else if(mCurrentInteriorUID == tEntity::MakeUID("bartroom"))
+ {
+ static rmt::Matrix mirrorMatrix(1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, -1.0f, 0.0f,
+ 0.0f, 0.0f, -894.0f, 1.0f);
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( RenderEnums::LevelSlot ));
+ pWorldRenderLayer->SetMirror(true, &mirrorMatrix);
+ }
+
+ SetupLightsAndAnims();
+
+ mIn = true;
+
+ GetEventManager()->TriggerEvent(EVENT_INTERIOR_SWITCH, (void*)1);
+}
+
+void InteriorManager::SetupLightsAndAnims()
+{
+ // Setup the lights.
+ //
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection(mSection);
+ p3d::inventory->SetCurrentSectionOnly(true);
+ tLightGroup* pLightGroup = p3d::find<tLightGroup>("InteriorLightGroup");
+
+ if( !pLightGroup )
+ {
+ pLightGroup = p3d::find<tLightGroup>("LightGroup");
+ }
+
+ if( pLightGroup )
+ {
+ RenderLayer* rl = GetRenderManager()->mpLayer(RenderEnums::LevelSlot);
+ rAssert(rl);
+ tView* view = rl->pView( PLAYER_ONE );
+ if(view == 0)
+ {
+ // We've come in here before the view has been set up by the render manager.
+ rl->SetUpViewCam();
+ view = rl->pView(PLAYER_ONE);
+ }
+ rAssert(view);
+ view->RemoveAllLights();
+
+ for( int i = 0; i < pLightGroup->GetNumLights(); ++i )
+ {
+ tLight* light = pLightGroup->GetLight( i );
+ rAssert( light != NULL );
+ if( light != NULL )
+ {
+ GetRenderManager()->mpLayer(RenderEnums::LevelSlot)->pView( PLAYER_ONE )->AddLight( light );
+ }
+ }
+ }
+
+ tRefCounted::Assign(mInteriorAnimations, p3d::find<tFrameController>("MasterController"));
+ p3d::inventory->SetCurrentSectionOnly(false);
+
+ if(mInteriorAnimations)
+ {
+ mInteriorAnimations->SetCycleMode(FORCE_CYCLIC);
+ }
+ p3d::inventory->PopSection();
+}
+
+//==============================================================================
+void InteriorManager::AttemptEntry()
+{
+ if( mEntryRequested && mInteriorLoaded && !mIn)
+ {
+ SwitchToInterior();
+
+ //----------------------------------------------------------------------
+ // Teleport the character to the interior.
+ //----------------------------------------------------------------------
+
+ Character* pCharacter = GetCharacterManager()->GetCharacter( PLAYER_ONE );
+ rAssert( pCharacter != 0 );
+
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection(mSection);
+ p3d::inventory->SetCurrentSectionOnly(true);
+
+ // Find the end locator.
+ //
+ DirectionalLocator* pEndLoc = p3d::find<DirectionalLocator>( "InteriorEntryEnd" );
+ rAssert( pEndLoc != 0 );
+
+ p3d::inventory->SetCurrentSectionOnly(false);
+ p3d::inventory->PopSection();
+
+ rmt::Matrix endTransform = pEndLoc->GetTransform();
+ rmt::Vector standPosition;
+ pEndLoc->GetLocation( &standPosition ) ;
+
+ // Put the character in position.
+ //
+ rmt::Matrix transform = pEndLoc->GetTransform();
+ float facingDir = choreo::GetWorldAngle( transform.Row(2).x, transform.Row(2).z );
+
+ pCharacter->RelocateAndReset(standPosition, facingDir);
+ }
+
+ if( !mLoadedGags && mEntryRequested && !GetLoadingManager()->IsLoading())
+ {
+ mLoadedGags = true;
+ LoadGagNIS(mLoadedInteriorUID);
+ }
+ else if( mEntryRequested && mInteriorLoaded && mLoadedGags && !GetLoadingManager()->IsLoading())
+ {
+ GetRenderManager()->mpLayer(RenderEnums::LevelSlot)->Thaw();
+
+ mEntryRequested = false;
+
+ Character* pCharacter = GetCharacterManager()->GetCharacter( PLAYER_ONE );
+
+ pCharacter->GetActionController()->Clear();
+ Sequencer* pSeq = pCharacter->GetActionController()->GetNextSequencer();
+ Action* pAction = 0;
+
+ // Wait a while for camera to reorient itself
+ pSeq->BeginSequence();
+ pSeq->AddAction( new DelayAction(0.5f));
+ pSeq->EndSequence();
+
+ // Open Iris
+ //
+ pSeq->BeginSequence();
+ pAction = new TriggerEventAction( EVENT_ENTER_INTERIOR_TRANSITION_END, (void*)pCharacter );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence();
+
+ // Finish
+ //
+ pSeq->BeginSequence();
+ pAction = new TriggerEventAction( EVENT_ENTER_INTERIOR_END, (void*)pCharacter );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence();
+
+ GetSuperCamManager()->GetSCC( 0 )->SetIsInitialCamera(true);
+ }
+}
+
+
+void InteriorManager::SwitchToExterior()
+{
+ if(!mIn)
+ return;
+
+ mCurrentInteriorUID = 0;
+
+ mIn = false;
+
+ // reset the exterior lights
+ //
+ tView* view = GetRenderManager()->mpLayer(RenderEnums::LevelSlot)->pView( PLAYER_ONE );
+ view->RemoveAllLights();
+
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection("Default");
+ tLightGroup* sun = p3d::find<tLightGroup>("sun");
+ rAssert( sun );
+ p3d::inventory->PopSection();
+
+ for(int j=0;j<sun->GetNumLights();j++)
+ {
+ view->AddLight( sun->GetLight(j) );
+ }
+
+ tRefCounted::Release(mInteriorAnimations);
+
+ ClearGags();
+
+ // Show the HUD Map.
+ //
+// GetGuiSystem()->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_MAP );
+
+
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( RenderEnums::LevelSlot ));
+ pWorldRenderLayer->SetMirror(false, NULL);
+
+ GetSuperCamManager()->GetSCC( 0 )->SetIsInitialCamera(true);
+
+ GetEventManager()->TriggerEvent(EVENT_INTERIOR_SWITCH, 0);
+}
+
+void InteriorManager::ExitInterior()
+{
+ if(mState != EXIT)
+ return;
+
+ SwitchToExterior();
+ this->LoadGagNIS(0);
+
+ Character* pCharacter = GetCharacterManager()->GetCharacter( PLAYER_ONE );
+ rAssert( pCharacter != 0 );
+
+ // position the character outside
+ pCharacter->RelocateAndReset(mExitPos, mExitFacing);
+
+ pCharacter->GetActionController()->Clear();
+ Sequencer* pSeq = pCharacter->GetActionController()->GetNextSequencer();
+
+ // Wait a while for camera to reorient itself
+ pSeq->BeginSequence();
+ pSeq->AddAction( new DelayAction(1.0f));
+ pSeq->EndSequence();
+
+ // Open iris, and notify anyone else that might be interested
+ // (ambient sound, etc) that we are outside again
+ pSeq->BeginSequence();
+ pSeq->AddAction( new TriggerEventAction( EVENT_EXIT_INTERIOR_END, (void*)pCharacter ) );
+ pSeq->EndSequence();
+}
+
+
+//==============================================================================
+// InteriorManager::LoadGagNIS
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void InteriorManager::LoadGagNIS(tUID interiorUID)
+{
+ ClearGags();
+
+ int totalWeight = 0;
+
+ // Which gags are available for this interior?
+ //
+ int i;
+ for( i = 0; i < mBindingCount; ++i )
+ {
+ if( (mGagBindings[i].interiorUID == interiorUID) && mGagBindings[i].random)
+ {
+ totalWeight += mGagBindings[i].weight;
+ }
+ }
+
+ int pick = 0;
+ if(totalWeight)
+ {
+ pick = sRandom.IntRanged(0,totalWeight-1);
+ }
+ totalWeight = 0;
+
+ for( i = 0; i < mBindingCount; ++i )
+ {
+ if( (mGagBindings[i].interiorUID == interiorUID) && mGagBindings[i].random)
+ {
+ if((pick >= totalWeight) && (pick < (totalWeight + mGagBindings[i].weight)))
+ {
+ HeapMgr()->PushHeap(GMA_LEVEL_OTHER);
+ rAssert(mGagCount < MAX_GAGS);
+ gags[mGagCount] = new Gag(&mGagBindings[i]);
+ gags[mGagCount]->AddRef();
+ mGagCount++;
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+ }
+ totalWeight += mGagBindings[i].weight;
+ }
+ else if( (mGagBindings[i].interiorUID == interiorUID) && !mGagBindings[i].random)
+ {
+ HeapMgr()->PushHeap(GMA_LEVEL_OTHER);
+ rAssert(mGagCount < MAX_GAGS);
+ gags[mGagCount] = new Gag(&mGagBindings[i]);
+ gags[mGagCount]->AddRef();
+ mGagCount++;
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+ }
+ }
+}
+
+void InteriorManager::ClearGags()
+{
+ for(int i = 0; i < mGagCount; i++)
+ {
+ tRefCounted::Release(gags[i]);
+ }
+ mGagCount = 0;
+}
+
+void InteriorManager::GetExitPos(void)
+{
+ // get the position and facing of the exit locator
+ rmt::Vector heading;
+
+ Locator* loc = p3d::find<Locator>(mLoadedInteriorUID);
+ if(!loc)
+ {
+ loc = p3d::find<Locator>(mCurrentInteriorUID);
+ }
+ rAssert(loc);
+
+ loc->GetPosition(&mExitPos);
+ loc->GetHeading(&heading);
+ mExitFacing = choreo::GetWorldAngle( heading.x, heading.z ) + rmt::PI;
+}
+
+void InteriorManager::CollectionEffect(const char* Name, const rmt::Vector& Pos)
+{
+ mCollectionEffect->Init(Name, Pos, true, true);
+}
diff --git a/game/code/interiors/interiormanager.h b/game/code/interiors/interiormanager.h
new file mode 100644
index 0000000..5616c5d
--- /dev/null
+++ b/game/code/interiors/interiormanager.h
@@ -0,0 +1,253 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: interiormanager.h
+//
+// Description: InteriorManager class declaration.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef INTERIORMANAGER_H
+#define INTERIORMANAGER_H
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/p3dtypes.hpp> // tUID
+#include <p3d/anim/animate.hpp> // p3dCycleMode
+#include <radmath/radmath.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <events/eventlistener.h>
+#include <events/eventdata.h>
+#include <presentation/presevents/presentationevent.h>
+#include <mission/objectives/dialogueobjective.h>
+
+//========================================
+// Forward References
+//========================================
+class NISEvent;
+class InteriorEntranceLocator;
+class Character;
+class Sequencer;
+class NISPlayer;
+class GagDrawable;
+class Gag;
+class InteriorExit;
+class AnimatedIcon;
+class EventLocator;
+class SphereTriggerVolume;
+
+//==============================================================================
+//
+// Synopsis:
+//
+//==============================================================================
+class InteriorManager : public EventListener
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static InteriorManager* CreateInstance();
+ static InteriorManager* GetInstance();
+ static void DestroyInstance();
+
+ void OnBootupStart();
+ void OnGameplayStart();
+ void OnGameplayEnd();
+ void LoadLevelGags(const rmt::Vector& startPos, bool initial = false);
+
+ void UnloadGagSounds();
+
+ // start an entry (callled from EnterInterior button handler)
+ void Enter(InteriorEntranceLocator* entry, Character* character, Sequencer* seq);
+
+ bool IsInside(void) { return mIn || mEntryRequested; }
+ bool IsEntering(void) { return mState == ENTER; }
+ bool IsExiting(void) { return mState == EXIT; }
+ tUID GetInterior(void) { return mCurrentInteriorUID;}
+
+ void Update( unsigned int elapsedTime );
+
+ void CollectionEffect(const char* Name, const rmt::Vector& Pos);
+
+ // Implement EventListener interface
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ struct GagBinding
+ {
+ GagBinding();
+ void Clear(void);
+
+ // data for selection of gags
+ tUID interiorUID;
+ bool random;
+ int weight;
+
+ char gagFileName[13]; // force 8.3 compliance!
+ p3dCycleMode cycleMode;
+ bool triggered;
+ bool action;
+ bool retrigger;
+ bool useGagLocator;
+ tUID gagLoc;
+ rmt::Vector gagPos;
+ bool useTriggerLocator;
+ tUID triggerLoc;
+ rmt::Vector triggerPos;
+ float triggerRadius;
+ unsigned char i_S_Movie;
+ char gagFMVFileName[ 13 ]; // enforce 8.3 file naming.
+ radKey32 soundID;
+ bool cameraShake;
+ ShakeEventData shake;
+ float shakeDelay;
+ float shakeDuration;
+ float coinDelay;
+ unsigned coins;
+ unsigned loopIntro;
+ unsigned loopOutro;
+ char dialogChar1[DialogueObjective::MAX_CHAR_NAME_LEN];
+ char dialogChar2[DialogueObjective::MAX_CHAR_NAME_LEN];
+ radKey32 acceptDialogID;
+ radKey32 rejectDialogID;
+ radKey32 instructDialogID;
+ bool sparkle;
+ bool animBV;
+ unsigned int loadDist;
+ unsigned int unloadDist;
+ unsigned int soundLoadDist;
+ unsigned int soundUnloadDist;
+ int persistIndex;
+ };
+
+ void ExteriorCharPosn( rmt::Vector& orPosn ){ orPosn = mExitPos; }
+
+ const tName& ClassifyPoint(const rmt::Vector& point);
+
+ void SetISMovieDialogPlaying( bool isPlaying ) { m_isPlayingISDialog = isPlaying; }
+ bool IsPlayingISMovieDialog() const { return m_isPlayingISDialog; }
+
+private:
+
+ // No public access to these, use singleton interface.
+ InteriorManager();
+ ~InteriorManager();
+
+ // Declared but not defined to prevent copying and assignment.
+ InteriorManager( const InteriorManager& );
+ InteriorManager& operator=( const InteriorManager& );
+
+ void OnMissionRestart();
+
+ void LoadGagNIS(tUID uid);
+ void ClearGags();
+ void AttemptEntry();
+ void SwitchToInterior();
+ void ExitInterior();
+ void SwitchToExterior();
+ void SetupLightsAndAnims();
+
+ void ClearGagBindings();
+
+ void AddGagBinding( tUID interiorUID,
+ char* gagFileName,
+ p3dCycleMode cycleMode,
+ int weight,
+ char* gagSound );
+
+ void GagBegin(char* gagFileName);
+ GagBinding* GetBuildBinding(void);
+ void GagEnd(void);
+
+ // Expose to Console
+
+ static void ConsoleClearGagBindings( int argc, char** argv );
+ static void ConsoleAddGagBinding( int argc, char** argv );
+ static void ConsoleGagBegin(int argc, char** argv );
+ static void ConsoleGagSetInterior(int argc, char** argv );
+ static void ConsoleGagSetCycle(int argc, char** argv );
+ static void ConsoleGagSetWeight(int argc, char** argv );
+ static void ConsoleGagSetSound(int argc, char** argv );
+ static void ConsoleGagSetTrigger(int argc, char** argv );
+ static void ConsoleGagSetPosition(int argc, char** argv );
+ static void ConsoleGagSetRandom(int argc, char** argv );
+ static void ConsoleGagEnd(int argc, char** argv );
+ static void ConsoleGagPlayFMV( int argc, char** argv );
+ static void ConsoleGagSetIntro( int argc, char** argv );
+ static void ConsoleGagSetOutro( int argc, char** argv );
+ static void ConsoleGagSetCameraShake( int argc, char** argv );
+ static void ConsoleGagSetCoins( int argc, char** argv );
+ static void ConsoleGagSetSparkle( int argc, char** argv );
+ static void ConsoleGagSetAnimCollision( int argc, char** argv );
+ static void ConsoleGagSetLoadDistances( int argc, char** argv );
+ static void ConsoleGagSetSoundLoadDistances( int argc, char** argv );
+ static void ConsoleGagSetPersist( int argc, char** argv );
+ static void ConsoleGagCheckCollCards(int argc, char** argv);
+ static void ConsoleGagCheckMovie(int argc, char** argv);
+
+ // Pointer to the one and only instance of this singleton.
+ static InteriorManager* spInstance;
+
+ enum State
+ {
+ NONE,
+ ENTER,
+ EXIT,
+ INSIDE,
+ NUM_STATES
+ };
+
+ State mState;
+ bool mEntryRequested;
+ bool mInteriorLoaded;
+ bool mLoadedGags;
+ tUID mLoadedInteriorUID;
+ tUID mCurrentInteriorUID;
+ tUID mSection;
+ AnimatedIcon* mCollectionEffect;
+ bool mIn;
+
+ rmt::Vector mExitPos;
+ float mExitFacing;
+
+ void GetExitPos();
+
+ static const int MAX_BINDINGS = 64;
+ GagBinding mGagBindings[MAX_BINDINGS];
+ int mBindingCount;
+
+ GagBinding mBuildGag;
+
+ static const int MAX_GAGS = 32;
+ int mGagCount;
+ Gag* gags[MAX_GAGS];
+
+ bool mBuildingGag;
+
+ InteriorExit* mExit;
+
+ tFrameController* mInteriorAnimations;
+
+ static rmt::Randomizer sRandom;
+ static bool sRandomSeeded;
+
+ static unsigned sPersistGagIndex;
+
+ bool m_isPlayingISDialog : 1;
+
+ friend class GagDrawable;
+ friend class Gag;
+};
+
+
+// A little syntactic sugar for getting at this singleton.
+inline InteriorManager* GetInteriorManager() { return( InteriorManager::GetInstance() ); }
+
+
+#endif // INTERIORMANAGER_H
+
diff --git a/game/code/loading/allloadmanager.cpp b/game/code/loading/allloadmanager.cpp
new file mode 100644
index 0000000..a820a6f
--- /dev/null
+++ b/game/code/loading/allloadmanager.cpp
@@ -0,0 +1,15 @@
+#include <loading/cameradataloader.cpp>
+#include <loading/cementfilehandler.cpp>
+#include <loading/choreofilehandler.cpp>
+#include <loading/consolefilehandler.cpp>
+#include <loading/filehandlerfactory.cpp>
+#include <loading/iconfilehandler.cpp>
+#include <loading/intersectionloader.cpp>
+#include <loading/loadingmanager.cpp>
+#include <loading/locatorloader.cpp>
+#include <loading/p3dfilehandler.cpp>
+#include <loading/roaddatasegmentloader.cpp>
+#include <loading/roadloader.cpp>
+#include <loading/scroobyfilehandler.cpp>
+#include <loading/soundfilehandler.cpp>
+#include <loading/pathloader.cpp>
diff --git a/game/code/loading/cameradataloader.cpp b/game/code/loading/cameradataloader.cpp
new file mode 100644
index 0000000..7adbc8c
--- /dev/null
+++ b/game/code/loading/cameradataloader.cpp
@@ -0,0 +1,168 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: CameraDataLoader.cpp
+//
+// Description: Implement CameraDataLoader
+//
+// History: 17/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/chunkfile.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/CameraDataLoader.h>
+
+#include <camera/followcamdatachunk.h>
+#include <camera/walkercamdatachunk.h>
+#include <camera/supercamcentral.h>
+
+#include <memory/srrmemory.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// CameraDataLoader::CameraDataLoader
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CameraDataLoader::CameraDataLoader()
+{
+}
+
+//==============================================================================
+// CameraDataLoader::~CameraDataLoader
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CameraDataLoader::~CameraDataLoader()
+{
+}
+
+//=============================================================================
+// CameraDataLoader::Load
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (tChunkFile* f, tEntityStore* store)
+//
+// Return: tLoadStatus
+//
+//=============================================================================
+tLoadStatus CameraDataLoader::Load(tChunkFile* f, tEntityStore* store)
+{
+MEMTRACK_PUSH_GROUP( "Camera Data Loading" );
+
+ //This loads two types of chunks. Follow and walker cams.
+
+ unsigned int id = f->GetUInt();
+
+ char name[256];
+ sprintf( name, "CameraData%d", id );
+
+ HeapMgr()->PushHeap (GMA_LEVEL_OTHER);
+
+ if ( f->GetCurrentID() == SRR2::ChunkID::FOLLOWCAM )
+ {
+ if ( !SuperCamCentral::FindFCD( id ) )
+ {
+ //Load and create a FOLLOWCAM data chunk
+ FollowCamDataChunk& fcD = SuperCamCentral::GetNewFollowCamDataChunk();
+
+ fcD.SetName( name );
+
+ fcD.mID = id;
+ fcD.mRotation = f->GetFloat();
+ fcD.mElevation = f->GetFloat();
+ fcD.mMagnitude = f->GetFloat();
+
+ fcD.mTargetOffset.x = f->GetFloat();
+ fcD.mTargetOffset.y = f->GetFloat();
+ fcD.mTargetOffset.z = f->GetFloat();
+ }
+ }
+ else if ( f->GetCurrentID() == SRR2::ChunkID::WALKERCAM )
+ {
+ //Load and create a WALKERCAM data chunk
+ WalkerCamDataChunk* wcD = new WalkerCamDataChunk();
+
+ rAssert( wcD );
+
+ wcD->SetName( name );
+
+ wcD->mID = id;
+ wcD->mMinMagnitude = f->GetFloat();
+ wcD->mMaxMagnitude = f->GetFloat();
+ wcD->mElevation = f->GetFloat();
+
+ wcD->mTargetOffset.x = f->GetFloat();
+ wcD->mTargetOffset.y = f->GetFloat();
+ wcD->mTargetOffset.z = f->GetFloat();
+
+ //store->Store( wcD );
+ tEntity* camData = NULL;
+ camData = wcD;
+
+ if ( camData )
+ {
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( SuperCamCentral::CAMERA_INVENTORY_SECTION );
+ //const tName& name = camData->GetNameObject();
+ //p3d::inventory->find( name );
+ bool collision = p3d::inventory->TestCollision( camData );
+ if( !collision )
+ {
+ p3d::inventory->Store( camData );
+ }
+ else
+ {
+ camData->AddRef();
+ camData->Release();
+ }
+ p3d::inventory->PopSection();
+ }
+ }
+
+
+ HeapMgr()->PopHeap (GMA_LEVEL_OTHER);
+
+MEMTRACK_POP_GROUP("Camera Data Loading");
+
+ return LOAD_OK;
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/loading/cameradataloader.h b/game/code/loading/cameradataloader.h
new file mode 100644
index 0000000..be5b85f
--- /dev/null
+++ b/game/code/loading/cameradataloader.h
@@ -0,0 +1,77 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: cameradataloader.h
+//
+// Description: Blahblahblah
+//
+// History: 17/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef CAMERADATALOADER_H
+#define CAMERADATALOADER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <constants/srrchunks.h>
+#include <p3d/loadmanager.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class CameraDataLoader : public tChunkHandler
+{
+public:
+ CameraDataLoader();
+ virtual ~CameraDataLoader();
+
+ // P3D chunk loader.
+ virtual tLoadStatus Load(tChunkFile* f, tEntityStore* store);
+
+ // P3D chunk id.
+ virtual bool CheckChunkID(unsigned id);
+ virtual unsigned int GetChunkID();
+
+private:
+
+ //Prevent wasteful constructor creation.
+ CameraDataLoader( const CameraDataLoader& cameradataloader );
+ CameraDataLoader& operator=( const CameraDataLoader& cameradataloader );
+};
+
+//******************************************************************************
+//
+// Inline Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// CameraDataLoader::CheckChunkID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (unsigned id)
+//
+// Return: bool
+//
+//=============================================================================
+inline bool CameraDataLoader::CheckChunkID(unsigned id)
+{
+ return( SRR2::ChunkID::FOLLOWCAM == id || SRR2::ChunkID::WALKERCAM == id );
+}
+
+inline unsigned int CameraDataLoader::GetChunkID()
+{
+ return SRR2::ChunkID::FOLLOWCAM | SRR2::ChunkID::WALKERCAM;
+}
+
+#endif //CAMERADATALOADER_H
diff --git a/game/code/loading/cementfilehandler.cpp b/game/code/loading/cementfilehandler.cpp
new file mode 100644
index 0000000..ac17aca
--- /dev/null
+++ b/game/code/loading/cementfilehandler.cpp
@@ -0,0 +1,147 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: cementfilehandler.cpp
+//
+// Description: Implements CementFileHandler class, which allows us to add
+// cement file registration to the loading manager file queue
+//
+// History: 30/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/cementfilehandler.h>
+
+#include <main/commandlineoptions.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// CementFileHandler::CementFileHandler
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: library - Cement library that we're waiting on for registration
+//
+// Return: N/A.
+//
+//==============================================================================
+CementFileHandler::CementFileHandler( LoadingManager::CementLibraryStruct* libraryStruct ) :
+ m_libraryStruct( libraryStruct )
+{
+}
+
+//==============================================================================
+// CementFileHandler::~CementFileHandler
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CementFileHandler::~CementFileHandler()
+{
+}
+
+//=============================================================================
+// CementFileHandler::LoadFile
+//=============================================================================
+// Description: Start looking at the cement library to see if it's registered
+//
+// Parameters: filename - ignored, since we're not really loading a file
+// pCallback - loading manager, to be signalled on registration
+// completion
+// pUserData - user data to be passed back in callback
+//
+// Return: void
+//
+//=============================================================================
+void CementFileHandler::LoadFile( const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap )
+{
+ radCementLibraryPriority priority;
+
+ mpCallback = pCallback;
+ mpUserData = pUserData;
+
+ if( CommandLineOptions::Get( CLO_CD_FILES_ONLY ) )
+ {
+ priority = radCementLibraryBeforeDrive;
+ }
+ else
+ {
+ priority = radCementLibraryAfterDrive;
+ }
+
+ ::radFileRegisterCementLibrary( &(m_libraryStruct->library),
+ filename,
+ priority,
+ 0,
+ GMA_PERSISTENT );
+
+ m_libraryStruct->library->SetCompletionCallback( this, NULL );
+}
+
+//=============================================================================
+// CementFileHandler::LoadFileSync
+//=============================================================================
+// Description: Unused synchronous loading function
+//
+// Parameters: Ignored
+//
+// Return: void
+//
+//=============================================================================
+void CementFileHandler::LoadFileSync( const char* filename )
+{
+ //
+ // RadFile doesn't offer synchronous cement file registration, so we shouldn't
+ // see this function called
+ //
+ rAssert( false );
+}
+
+//=============================================================================
+// CementFileHandler::OnCementLibraryRegistered
+//=============================================================================
+// Description: Called by the cement library when registration is complete
+//
+// Parameters: Unused
+//
+// Return: void
+//
+//=============================================================================
+void CementFileHandler::OnCementLibraryRegistered( void* pUserData )
+{
+ m_libraryStruct->isLoading = false;
+
+ mpCallback->OnLoadFileComplete( mpUserData );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/loading/cementfilehandler.h b/game/code/loading/cementfilehandler.h
new file mode 100644
index 0000000..5aa3440
--- /dev/null
+++ b/game/code/loading/cementfilehandler.h
@@ -0,0 +1,76 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: cementfilehandler.h
+//
+// Description: Declares CementFileHandler class, which allows us to add
+// cement file registration to the loading manager file queue
+//
+// History: 30/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef CEMENTFILEHANDLER_H
+#define CEMENTFILEHANDLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radfile.hpp>
+
+#include <loading/filehandler.h>
+#include <loading/loadingmanager.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: CementFileHandler
+//
+//=============================================================================
+
+class CementFileHandler : public FileHandler,
+ public IRadCementLibraryCompletionCallback
+{
+ public:
+ CementFileHandler( LoadingManager::CementLibraryStruct* library );
+ virtual ~CementFileHandler();
+
+ //
+ // Load file asynchronously.
+ //
+ void LoadFile( const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap );
+
+ //
+ // Load file synchronously (unused)
+ //
+ void LoadFileSync( const char* filename );
+
+ //
+ // Cement file registration callback
+ //
+ void OnCementLibraryRegistered( void* pUserData );
+
+ //
+ // Playin' fast and loose with reference counting
+ //
+ virtual void AddRef() {FileHandler::AddRef();}
+ virtual void Release() {FileHandler::Release();}
+
+ private:
+ // Prevent wasteful constructor creation.
+ CementFileHandler();
+ CementFileHandler( const CementFileHandler& original );
+ CementFileHandler& operator=( const CementFileHandler& rhs );
+
+ LoadingManager::CementLibraryStruct* m_libraryStruct;
+};
+
+
+#endif // CEMENTFILEHANDLER_H
+
diff --git a/game/code/loading/choreofilehandler.cpp b/game/code/loading/choreofilehandler.cpp
new file mode 100644
index 0000000..1197536
--- /dev/null
+++ b/game/code/loading/choreofilehandler.cpp
@@ -0,0 +1,238 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: choreofilehandler.cpp
+//
+// Description: Implement ChoreoFileHandler
+//
+// History: 3/25/2002 + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <string.h>
+// Pure 3D
+#include <p3d/utility.hpp>
+// Foundation Tech
+#include <raddebug.hpp>
+// Choreo
+#include <choreo/load.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/choreofilehandler.h>
+#include <memory/srrmemory.h>
+#include <worldsim/character/charactermanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ChoreoFileHandler::ChoreoFileHandler
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ChoreoFileHandler::ChoreoFileHandler()
+:
+m_state( NONE ),
+mScriptString( 0 ),
+mpScriptFile( 0 )
+{
+}
+
+//==============================================================================
+// ChoreoFileHandler::~ChoreoFileHandler
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ChoreoFileHandler::~ChoreoFileHandler()
+{
+}
+
+
+//==============================================================================
+// ChoreoFileHandler::LoadFile
+//==============================================================================
+//
+// Description: Load a choreo file asynchronously.
+//
+// Parameters: filename - fully qualified path and filename
+// pCallback - client callback to invoke when load is complete
+// pUserData - optional client supplied user data
+//
+// Return: None.
+//
+//==============================================================================
+void ChoreoFileHandler::LoadFile
+(
+ const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap
+)
+{
+ rAssert( filename );
+ rAssert( pCallback );
+
+ mpCallback = pCallback;
+
+ // by default, use FTT IRadFiles
+ //
+ m_state = OPENFILE;
+ choreo::RegisterDefaultScriptHandlers();
+ radFileOpen(&mpScriptFile,
+ filename,
+ false,
+ OpenExisting,
+ NormalPriority);
+ P3DASSERT(mpScriptFile != 0);
+ mpScriptFile->AddCompletionCallback( this, (void*)pUserData );
+
+
+}
+
+
+//==============================================================================
+// ChoreoFileHandler::OnFileOperationsComplete
+//==============================================================================
+//
+// Description: FTech invokes this callback when the async load
+// is complete.
+//
+// Parameters: pUserData - optional client supplied user data
+//
+// Return: None.
+//
+//==============================================================================
+void ChoreoFileHandler::OnFileOperationsComplete( void* pUserData )
+{
+ switch ( m_state )
+ {
+ case OPENFILE:
+ {
+
+MEMTRACK_PUSH_GROUP( "ChoreoFileHandler" );
+ unsigned length = mpScriptFile->GetSize();
+ mScriptString = new(GMA_TEMP) char [length + 1];
+ mpScriptFile->ReadAsync( mScriptString, length );
+ mpScriptFile->AddCompletionCallback( this, (void*)pUserData );
+ m_state = READDATA;
+MEMTRACK_POP_GROUP("ChoreoFileHandler");
+
+ break;
+ }
+ case READDATA:
+ {
+ unsigned length = mpScriptFile->GetSize();
+ mScriptString[length] = '\0';
+
+ p3d::inventory->PushSection( );
+ p3d::inventory->SelectSection( GetSectionName() );
+ HeapMgr()->PushHeap(GMA_LEVEL_ZONE);
+ bool rc = choreo::ReadFromScriptString(mScriptString, mpScriptFile->GetFilename(), p3d::inventory );
+ HeapMgr()->PopHeap(GMA_LEVEL_ZONE);
+ p3d::inventory->PopSection();
+ rAssertMsg( rc, "Choreo Script load error\n" );
+
+ delete[] mScriptString;
+ mScriptString = 0;
+ mpScriptFile->Release();
+ mpScriptFile = 0;
+ m_state = DONE;
+
+ //
+ // Percolate the callback up to the client. This must be done last!!!
+ //
+ mpCallback->OnLoadFileComplete( pUserData );
+
+ break;
+ }
+ default:
+ {
+ rAssert( 0 );
+ break;
+ }
+ }
+
+}
+
+
+//==============================================================================
+// ChoreoFileHandler::LoadFileSync
+//==============================================================================
+//
+// Description: Load a Pure3D file synchronously.
+//
+// Parameters: filename - fully qualified path and filename
+//
+// Return: None.
+//
+//==============================================================================
+void ChoreoFileHandler::LoadFileSync( const char* filename )
+{
+ rReleasePrintf("\n\n!!!!!! TRC VIOLATION, USE ASYNC!!!!!!!\n\n");
+ rAssert( false );
+
+ rAssert( filename );
+
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( GetSectionName() );
+ bool result = p3d::load( filename );
+ p3d::inventory->PopSection();
+
+ rAssert( result = true );
+}
+
+
+
+//==============================================================================
+// ChoreoFileHandler::SetSectionName
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void ChoreoFileHandler::SetSectionName( const char* sectionName )
+{
+ rAssert( sectionName );
+
+ strcpy( mcSectionName, sectionName );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+
+
+
+
diff --git a/game/code/loading/choreofilehandler.h b/game/code/loading/choreofilehandler.h
new file mode 100644
index 0000000..4370ed0
--- /dev/null
+++ b/game/code/loading/choreofilehandler.h
@@ -0,0 +1,85 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: Choreofilehandler.h
+//
+// Description: Declaration of ChoreoFileHandler class.
+//
+// History: 3/25/2002 + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef CHOREOFILEHANDLER_H
+#define CHOREOFILEHANDLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <loading/filehandler.h>
+#include <radfile.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: File handler for loading Pure3D files.
+//
+//=============================================================================
+class ChoreoFileHandler : public FileHandler,
+ public IRadFileCompletionCallback
+{
+ public:
+
+ ChoreoFileHandler();
+ virtual ~ChoreoFileHandler();
+
+ //
+ // Implement FileHandler interface.
+ //
+ virtual void LoadFile( const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap );
+
+ virtual void LoadFileSync( const char* filename );
+
+ //
+ // Implement IRadFileCompletionCallback interface.
+ //
+ virtual void OnFileOperationsComplete( void* pUserData );
+ virtual void AddRef() {FileHandler::AddRef();}
+ virtual void Release() {FileHandler::Release();}
+
+ //
+ // Specify which Choreo inventory section to load the file into.
+ //
+ void SetSectionName( const char* sectionName );
+ const char* GetSectionName() { return( mcSectionName ); }
+
+ private:
+ enum ChoreoFileState
+ {
+ NONE,
+ OPENFILE,
+ READDATA,
+ DONE
+ };
+ // Async Load State.
+ //
+ ChoreoFileState m_state;
+ // Data buffer.
+ //
+ char* mScriptString;
+
+ // Prevent wasteful constructor creation.
+ ChoreoFileHandler( const ChoreoFileHandler& Choreofilehandler );
+ ChoreoFileHandler& operator=( const ChoreoFileHandler& Choreofilehandler );
+
+ char mcSectionName[32];
+ IRadFile* mpScriptFile;
+};
+
+
+#endif //CHOREOFILEHANDLER_H \ No newline at end of file
diff --git a/game/code/loading/consolefilehandler.cpp b/game/code/loading/consolefilehandler.cpp
new file mode 100644
index 0000000..83fe727
--- /dev/null
+++ b/game/code/loading/consolefilehandler.cpp
@@ -0,0 +1,237 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: consolefilehandler.cpp
+//
+// Description: Implement ConsoleFileHandler
+//
+// History: 3/25/2002 + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <string.h>
+// Pure 3D
+#include <p3d/utility.hpp>
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/consolefilehandler.h>
+#include <memory/srrmemory.h>
+#include <console/console.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ConsoleFileHandler::ConsoleFileHandler
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ConsoleFileHandler::ConsoleFileHandler()
+ :
+ mpConsoleFile( 0 ),
+ mAsyncLoadState( NONE ),
+ mFileDataBuffer( 0 )
+{
+}
+
+//==============================================================================
+// ConsoleFileHandler::~ConsoleFileHandler
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ConsoleFileHandler::~ConsoleFileHandler()
+{
+}
+
+
+//==============================================================================
+// ConsoleFileHandler::LoadFile
+//==============================================================================
+//
+// Description: Load a Pure3D file asynchronously.
+//
+// Parameters: filename - fully qualified path and filename
+// pCallback - client callback to invoke when load is complete
+// pUserData - optional client supplied user data
+//
+// Return: None.
+//
+//==============================================================================
+void ConsoleFileHandler::LoadFile
+(
+ const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap
+)
+{
+ rAssert( filename );
+ rAssert( pCallback );
+
+ mpCallback = pCallback;
+
+ // by default, use FTT IRadFiles
+ //
+ mAsyncLoadState = OPENFILE;
+
+ radFileOpen( &mpConsoleFile,
+ filename,
+ false,
+ OpenExisting,
+ NormalPriority,
+ 0,
+ GMA_TEMP );
+
+ rAssert( mpConsoleFile != 0 );
+
+ mpConsoleFile->AddCompletionCallback( this,
+ reinterpret_cast<void*>(pUserData) );
+}
+
+
+//==============================================================================
+// ConsoleFileHandler::OnFileOperationsComplete
+//==============================================================================
+//
+// Description: Pure3D (via FTech) invokes this callback when the async load
+// is complete.
+//
+// Parameters: pUserData - optional client supplied user data
+//
+// Return: None.
+//
+//==============================================================================
+void ConsoleFileHandler::OnFileOperationsComplete( void* pUserData )
+{
+ switch( mAsyncLoadState )
+ {
+ case OPENFILE:
+ {
+
+MEMTRACK_PUSH_GROUP( "ConsoleFileHandler" );
+ unsigned int length = mpConsoleFile->GetSize();
+ mFileDataBuffer = new(GMA_TEMP) char[length + 1];
+
+ mpConsoleFile->ReadAsync( mFileDataBuffer, length );
+ mpConsoleFile->AddCompletionCallback( this, (void*)pUserData );
+
+ mAsyncLoadState = READDATA;
+MEMTRACK_POP_GROUP("ConsoleFileHandler");
+ }
+ break;
+
+ case READDATA:
+ {
+ unsigned int length = mpConsoleFile->GetSize();
+ mFileDataBuffer[length] = '\0';
+
+ GetConsole()->Evaluate( mFileDataBuffer, mpConsoleFile->GetFilename() );
+
+
+ delete[]( GMA_TEMP, mFileDataBuffer );
+ mFileDataBuffer = 0;
+
+ mpConsoleFile->Release();
+ mpConsoleFile = 0;
+
+ mAsyncLoadState = DONE;
+
+ //
+ // Percolate the callback up to the client. This must be done last!!!
+ //
+ mpCallback->OnLoadFileComplete( pUserData );
+ }
+ break;
+
+ default:
+ {
+ rAssert( 0 );
+ }
+ }
+}
+
+
+//==============================================================================
+// ConsoleFileHandler::LoadFileSync
+//==============================================================================
+//
+// Description: Load a Pure3D file synchronously.
+//
+// Parameters: filename - fully qualified path and filename
+//
+// Return: None.
+//
+//==============================================================================
+void ConsoleFileHandler::LoadFileSync( const char* filename )
+{
+MEMTRACK_PUSH_GROUP( "ConsoleFileHandler" );
+ rAssert( filename );
+
+ radFileOpen( &mpConsoleFile,
+ filename,
+ false,
+ OpenExisting,
+ NormalPriority,
+ 0,
+ GMA_TEMP );
+
+ rAssert( mpConsoleFile != 0 );
+
+ mpConsoleFile->WaitForCompletion();
+
+ unsigned int length = mpConsoleFile->GetSize();
+ mFileDataBuffer = new(GMA_TEMP) char[length + 1];
+
+ mpConsoleFile->ReadAsync( mFileDataBuffer, length );
+ mpConsoleFile->WaitForCompletion();
+
+ mFileDataBuffer[length] = '\0';
+
+ GetConsole()->Evaluate( mFileDataBuffer, filename );
+
+ delete[]( GMA_TEMP, mFileDataBuffer );
+ mFileDataBuffer = 0;
+
+ mpConsoleFile->Release();
+ mpConsoleFile = 0;
+MEMTRACK_POP_GROUP("ConsoleFileHandler");
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+
+
+
+
diff --git a/game/code/loading/consolefilehandler.h b/game/code/loading/consolefilehandler.h
new file mode 100644
index 0000000..9d4c7d2
--- /dev/null
+++ b/game/code/loading/consolefilehandler.h
@@ -0,0 +1,78 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: consolefilehandler.h
+//
+// Description: Declaration of ConsoleFileHandler class.
+//
+// History: 3/25/2002 + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef CONSOLEFILEHANDLER_H
+#define CONSOLEFILEHANDLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <loading/filehandler.h>
+#include <radfile.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: File handler for loading Pure3D files.
+//
+//=============================================================================
+class ConsoleFileHandler : public FileHandler,
+ public IRadFileCompletionCallback
+{
+ public:
+
+ ConsoleFileHandler();
+ virtual ~ConsoleFileHandler();
+
+ //
+ // Implement FileHandler interface.
+ //
+ virtual void LoadFile( const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap );
+
+ virtual void LoadFileSync( const char* filename );
+
+ //
+ // Implement IRadFileCompletionCallback interface.
+ //
+ virtual void OnFileOperationsComplete( void* pUserData );
+
+ virtual void AddRef() {FileHandler::AddRef();}
+ virtual void Release() {FileHandler::Release();}
+
+ private:
+
+ // Prevent wasteful constructor creation.
+ ConsoleFileHandler( const ConsoleFileHandler& ConsoleFileHandler );
+ ConsoleFileHandler& operator=( const ConsoleFileHandler& ConsoleFileHandler );
+
+ IRadFile* mpConsoleFile;
+
+ enum AsyncLoadState
+ {
+ NONE,
+ OPENFILE,
+ READDATA,
+ DONE
+ };
+
+ AsyncLoadState mAsyncLoadState;
+
+ char* mFileDataBuffer;
+};
+
+
+#endif // CONSOLEFILEHANDLER_H \ No newline at end of file
diff --git a/game/code/loading/filehandler.h b/game/code/loading/filehandler.h
new file mode 100644
index 0000000..80050b2
--- /dev/null
+++ b/game/code/loading/filehandler.h
@@ -0,0 +1,105 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: filehandler.h
+//
+// Description: Declaration of FileHandler abstract base class.
+//
+// History: 3/25/2002 + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef FILEHANDLER_H
+#define FILEHANDLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radobject.hpp>
+#include <radload/utility/object.hpp>
+#include "memory/srrmemory.h"
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Derive from this abstract base class to implement custom handlers
+// for loading specific file types.
+//
+//=============================================================================
+class FileHandler
+//:
+//public IRefCount
+{
+ public:
+
+ FileHandler(): m_RefCount( 0 ) {}
+ virtual ~FileHandler() {}
+
+ //----------------------------------------------------------------------
+ // ASYNCHRONOUS LOADING
+ //----------------------------------------------------------------------
+
+ //
+ // Clients must implement this callback to be notified when the
+ // async load completes.
+ //
+ struct LoadFileCallback
+ {
+ virtual void OnLoadFileComplete( void* pUserData ) = 0;
+ };
+
+ //
+ // Load file asynchronously.
+ //
+ virtual void LoadFile( const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap ) = 0;
+
+ //----------------------------------------------------------------------
+ // SYNCHRONOUS LOADING
+ //----------------------------------------------------------------------
+
+ //
+ // Load file synchronously.
+ //
+ virtual void LoadFileSync( const char* filename ) = 0;
+
+ virtual void AddRef( void )
+ {
+ m_RefCount++;
+ }
+ virtual void Release( void )
+ {
+ // Copy and paste from radobject.hpp
+ //
+ rAssert( m_RefCount > 0 && m_RefCount < MAX_REFCOUNT );
+ m_RefCount--;
+ if (m_RefCount == 0 )
+ {
+ // Must avoid recursive destruction, set refcount to some high
+ // value.
+
+ m_RefCount = MAX_REFCOUNT / 2;
+
+ delete this;
+ }
+ }
+ protected:
+
+ LoadFileCallback* mpCallback;
+ void* mpUserData;
+
+ private:
+
+ // Declared but not defined to prevent copying and assignment.
+ FileHandler( const FileHandler& );
+ FileHandler& operator=( const FileHandler& );
+
+ int m_RefCount;
+};
+
+#endif // FILEHANDLER_H
diff --git a/game/code/loading/filehandlerenum.h b/game/code/loading/filehandlerenum.h
new file mode 100644
index 0000000..40eb3f2
--- /dev/null
+++ b/game/code/loading/filehandlerenum.h
@@ -0,0 +1,46 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: filehandlerenum.h
+//
+// Description: File handler types.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef FILEHANDLERENUM_H
+#define FILEHANDLERENUM_H
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+//========================================
+// Constants, Typedefs and Statics
+//========================================
+enum FileHandlerEnum
+{
+ FILEHANDLER_PURE3D,
+ FILEHANDLER_LEVEL,
+ FILEHANDLER_MISSION,
+ FILEHANDLER_ANIMATION,
+ FILEHANDLER_CHOREO,
+ FILEHANDLER_CONSOLE,
+ FILEHANDLER_SCROOBY,
+ FILEHANDLER_SOUND,
+ FILEHANDLER_TEMP,
+ FILEHANDLER_ICON,
+
+ NUM_FILEHANDLERS
+};
+
+#endif // FILEHANDLERENUM_H
diff --git a/game/code/loading/filehandlerfactory.cpp b/game/code/loading/filehandlerfactory.cpp
new file mode 100644
index 0000000..9edc285
--- /dev/null
+++ b/game/code/loading/filehandlerfactory.cpp
@@ -0,0 +1,173 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: filehandlerfactory.cpp
+//
+// Description: Implement FileHandlerFactory
+//
+// History: 3/27/2002 + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/filehandlerfactory.h>
+#include <loading/p3dfilehandler.h>
+#include <loading/choreofilehandler.h>
+#include <loading/consolefilehandler.h>
+#include <loading/iconfilehandler.h>
+#include <loading/scroobyfilehandler.h>
+#include <loading/soundfilehandler.h>
+#include <memory/srrmemory.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// FileHandlerFactory::CreateFileHandler
+//==============================================================================
+//
+// Description: Instantiates and returns a file handler of the specified type.
+//
+// Parameters: handlerType - the type of file handler to create
+// allocator - the memory heap to use
+//
+// secitonName - hack to allow p3d inventory sections to be created
+// based on filenames
+//
+// Return: pointer to the file handler
+//
+// Constraints: The client is responsible for deleting the returned file handler.
+//
+//==============================================================================
+FileHandler* FileHandlerFactory::CreateFileHandler
+(
+ FileHandlerEnum handlerType,
+ const char* sectionName
+)
+{
+MEMTRACK_PUSH_GROUP( "FileHandler Factory" );
+
+ FileHandler* pHandler = NULL;
+
+ HeapMgr()->PushHeap(GMA_TEMP);
+
+ switch( handlerType )
+ {
+ case FILEHANDLER_PURE3D:
+ {
+ pHandler = new P3DFileHandler();
+
+ if( sectionName != 0 )
+ {
+ static_cast<P3DFileHandler*>(pHandler)->SetSectionName( sectionName );
+ }
+ else
+ {
+ static_cast<P3DFileHandler*>(pHandler)->SetSectionName( "Default" );
+ }
+
+ break;
+ }
+
+ case FILEHANDLER_LEVEL:
+ {
+ pHandler = new P3DFileHandler();
+ if( sectionName != 0 )
+ {
+ static_cast<P3DFileHandler*>(pHandler)->SetSectionName( sectionName );
+ }
+ else
+ {
+ static_cast<P3DFileHandler*>(pHandler)->SetSectionName( "Level" );
+ }
+ break;
+ }
+ case FILEHANDLER_MISSION:
+ {
+ pHandler = new P3DFileHandler();
+ static_cast<P3DFileHandler*>(pHandler)->SetSectionName( "Mission" );
+
+ break;
+ }
+ case FILEHANDLER_ANIMATION:
+ {
+ pHandler = new P3DFileHandler();
+ static_cast<P3DFileHandler*>(pHandler)->SetSectionName( sectionName );
+
+ break;
+ }
+ case FILEHANDLER_CHOREO:
+ {
+ pHandler = new ChoreoFileHandler();
+ static_cast<ChoreoFileHandler*>(pHandler)->SetSectionName( sectionName );
+
+ break;
+ }
+ case FILEHANDLER_CONSOLE:
+ {
+ pHandler = new ConsoleFileHandler();
+
+ break;
+ }
+ case FILEHANDLER_SCROOBY:
+ {
+ pHandler = new ScroobyFileHandler();
+ static_cast<ScroobyFileHandler*>(pHandler)->SetSectionName( sectionName );
+
+ break;
+ }
+ case FILEHANDLER_SOUND:
+ {
+ pHandler = new SoundFileHandler();
+
+ break;
+ }
+ case FILEHANDLER_TEMP:
+ {
+ pHandler = new P3DFileHandler();
+ static_cast<P3DFileHandler*>(pHandler)->SetSectionName( "Temp" );
+
+ break;
+ }
+ case FILEHANDLER_ICON:
+ {
+ pHandler = new IconFileHandler();
+
+ break;
+ }
+ default:
+ {
+ rAssert( 0 );
+ }
+ }
+
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+MEMTRACK_POP_GROUP("FileHandler Factory");
+ return( pHandler );
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/loading/filehandlerfactory.h b/game/code/loading/filehandlerfactory.h
new file mode 100644
index 0000000..e2b1bff
--- /dev/null
+++ b/game/code/loading/filehandlerfactory.h
@@ -0,0 +1,41 @@
+//==============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: filehandlerfactory.h
+//
+// Description: This factory shields its clients from the details of derived
+// FileHandler classes.
+//
+// History: 3/27/2002 + Created -- Darwin Chau
+//
+//==============================================================================
+
+#ifndef FILEHANDLERFACTORY_H
+#define FILEHANDLERFACTORY_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <loading/filehandlerenum.h>
+
+//========================================
+// Forward References
+//========================================
+class FileHandler;
+
+//==============================================================================
+//
+// Synopsis: Constructs and returns a FileHandler of the desired derived type.
+// Clients are responsible for deleting the FileHandler.
+//
+//==============================================================================
+
+class FileHandlerFactory
+{
+ public:
+
+ static FileHandler* CreateFileHandler( FileHandlerEnum handlerType, const char* sectionName = 0 );
+};
+
+
+#endif // FILEHANDLERFACTORY_H
diff --git a/game/code/loading/iconfilehandler.cpp b/game/code/loading/iconfilehandler.cpp
new file mode 100644
index 0000000..81bb8ae
--- /dev/null
+++ b/game/code/loading/iconfilehandler.cpp
@@ -0,0 +1,200 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: IconFileHandler.cpp
+//
+// Description: Implement IconFileHandler
+//
+// History: 01/21/2002 + Created -- Tony Chu
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <string.h>
+// Pure 3D
+#include <p3d/utility.hpp>
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/iconfilehandler.h>
+#include <memory/srrmemory.h>
+
+#include <data/memcard/memorycardmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// IconFileHandler::IconFileHandler
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+IconFileHandler::IconFileHandler()
+ :
+ mpIconFile( 0 ),
+ mFileDataBuffer( 0 ),
+ mHeap( GMA_DEFAULT ),
+ mAsyncLoadState( NONE )
+{
+}
+
+//==============================================================================
+// IconFileHandler::~IconFileHandler
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+IconFileHandler::~IconFileHandler()
+{
+}
+
+
+//==============================================================================
+// IconFileHandler::LoadFile
+//==============================================================================
+//
+// Description: Load a Pure3D file asynchronously.
+//
+// Parameters: filename - fully qualified path and filename
+// pCallback - client callback to invoke when load is complete
+// pUserData - optional client supplied user data
+//
+// Return: None.
+//
+//==============================================================================
+void IconFileHandler::LoadFile
+(
+ const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap
+)
+{
+ rAssert( filename );
+ rAssert( pCallback );
+
+ mpCallback = pCallback;
+ mHeap = heap;
+
+ // by default, use FTT IRadFiles
+ //
+ mAsyncLoadState = OPENFILE;
+
+ radFileOpen( &mpIconFile,
+ filename,
+ false,
+ OpenExisting,
+ NormalPriority,
+ 0,
+ heap );
+
+ rAssert( mpIconFile != 0 );
+
+ mpIconFile->AddCompletionCallback( this, pUserData );
+}
+
+
+//==============================================================================
+// IconFileHandler::OnFileOperationsComplete
+//==============================================================================
+//
+// Description: Pure3D (via FTech) invokes this callback when the async load
+// is complete.
+//
+// Parameters: pUserData - optional client supplied user data
+//
+// Return: None.
+//
+//==============================================================================
+void IconFileHandler::OnFileOperationsComplete( void* pUserData )
+{
+ switch( mAsyncLoadState )
+ {
+ case OPENFILE:
+ {
+MEMTRACK_PUSH_GROUP( "IconFileHandler" );
+ unsigned int length = mpIconFile->GetSize();
+ mFileDataBuffer = new( mHeap ) char[ length ];
+
+ mpIconFile->ReadAsync( mFileDataBuffer, length );
+ mpIconFile->AddCompletionCallback( this, pUserData );
+
+ mAsyncLoadState = READDATA;
+MEMTRACK_POP_GROUP("IconFileHandler");
+ }
+ break;
+
+ case READDATA:
+ {
+ unsigned int length = mpIconFile->GetSize();
+
+ GetMemoryCardManager()->SetMemcardIconData( mFileDataBuffer, length );
+
+ // it's up to the client to free the file data buffer!
+ //
+ mFileDataBuffer = 0;
+
+ mpIconFile->Release();
+ mpIconFile = 0;
+
+ mAsyncLoadState = DONE;
+
+ //
+ // Percolate the callback up to the client. This must be done last!!!
+ //
+ mpCallback->OnLoadFileComplete( pUserData );
+ }
+ break;
+
+ default:
+ {
+ rAssert( 0 );
+ }
+ }
+}
+
+
+//==============================================================================
+// IconFileHandler::LoadFileSync
+//==============================================================================
+//
+// Description: Load a Pure3D file synchronously.
+//
+// Parameters: filename - fully qualified path and filename
+//
+// Return: None.
+//
+//==============================================================================
+void IconFileHandler::LoadFileSync( const char* filename )
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
diff --git a/game/code/loading/iconfilehandler.h b/game/code/loading/iconfilehandler.h
new file mode 100644
index 0000000..ea43dd7
--- /dev/null
+++ b/game/code/loading/iconfilehandler.h
@@ -0,0 +1,79 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: IconFileHandler.h
+//
+// Description: Declaration of IconFileHandler class.
+//
+// History: 01/21/2003 + Created -- Tony Chu
+//
+//=============================================================================
+
+#ifndef ICONFILEHANDLER_H
+#define ICONFILEHANDLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <loading/filehandler.h>
+#include <radfile.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: File handler for loading Pure3D files.
+//
+//=============================================================================
+class IconFileHandler : public FileHandler,
+ public IRadFileCompletionCallback
+{
+ public:
+
+ IconFileHandler();
+ virtual ~IconFileHandler();
+
+ //
+ // Implement FileHandler interface.
+ //
+ virtual void LoadFile( const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap );
+
+ virtual void LoadFileSync( const char* filename );
+
+ //
+ // Implement IRadFileCompletionCallback interface.
+ //
+ virtual void OnFileOperationsComplete( void* pUserData );
+
+ virtual void AddRef() {FileHandler::AddRef();}
+ virtual void Release() {FileHandler::Release();}
+
+ private:
+
+ // Prevent wasteful constructor creation.
+ IconFileHandler( const IconFileHandler& iconFileHandler );
+ IconFileHandler& operator=( const IconFileHandler& iconFileHandler );
+
+ IRadFile* mpIconFile;
+ char* mFileDataBuffer;
+ GameMemoryAllocator mHeap;
+
+ enum AsyncLoadState
+ {
+ NONE,
+ OPENFILE,
+ READDATA,
+ DONE
+ };
+
+ AsyncLoadState mAsyncLoadState;
+
+};
+
+
+#endif // ICONFILEHANDLER_H
diff --git a/game/code/loading/intersectionloader.cpp b/game/code/loading/intersectionloader.cpp
new file mode 100644
index 0000000..22a7880
--- /dev/null
+++ b/game/code/loading/intersectionloader.cpp
@@ -0,0 +1,157 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: intersectionloader.cpp
+//
+// Description: Implement IntersectionLoader
+//
+// History: 14/03/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/chunkfile.hpp>
+#include <p3d/inventory.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/IntersectionLoader.h>
+#include <roads/intersection.h>
+
+#include <constants/srrchunks.h>
+
+//STUB!!
+#include <roads/roadmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// IntersectionLoader::IntersectionLoader
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+IntersectionLoader::IntersectionLoader()
+{
+}
+
+//==============================================================================
+// IntersectionLoader::~IntersectionLoader
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+IntersectionLoader::~IntersectionLoader()
+{
+}
+
+//==============================================================================
+// IntersectionLoader::Load
+//==============================================================================
+// Description: This is a P3D chunk loader for Intersections. This will create
+// a simple intersection and put it in the inventory.
+//
+// Parameters: (tChunkFile* f, tEntityStore* store)
+//
+// Return: tLoadStatus
+//
+//==============================================================================
+tLoadStatus IntersectionLoader::Load(tChunkFile* f, tEntityStore* store)
+{
+ char name[256];
+ f->GetString( name );
+
+ rmt::Vector loc;
+ loc.x = f->GetFloat();
+ loc.y = f->GetFloat();
+ loc.z = f->GetFloat();
+
+ float radius = f->GetFloat();
+
+ unsigned int type = f->GetUInt();
+
+ RoadManager* rm = RoadManager::GetInstance();
+
+ Intersection* intersection = NULL;
+
+ //First see if we've already got this one!
+ intersection = rm->FindIntersection( name );
+
+ if ( NULL == intersection )
+ {
+ //Not here yet.
+ intersection = rm->GetFreeIntersectionMemory();
+ rAssert( intersection );
+
+ intersection->SetName( name );
+ intersection->SetType( (Intersection::Type)type );
+ intersection->SetRadius( radius );
+ intersection->SetLocation( loc );
+
+ rm->AddIntersection( intersection );
+ }
+
+#ifdef TOOLS
+ store->Store( intersection );
+ //HACK
+ intersection->AddRef();
+#endif
+
+ return LOAD_OK;
+}
+
+
+//==============================================================================
+// IntersectionLoader::CheckChunkID
+//==============================================================================
+// Description: Comment
+//
+// Parameters: (unsigned id)
+//
+// Return: bool
+//
+//==============================================================================
+bool IntersectionLoader::CheckChunkID(unsigned id)
+{
+ return SRR2::ChunkID::INTERSECTION == id;
+}
+
+unsigned int IntersectionLoader::GetChunkID()
+{
+ return SRR2::ChunkID::INTERSECTION;
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+
+
diff --git a/game/code/loading/intersectionloader.h b/game/code/loading/intersectionloader.h
new file mode 100644
index 0000000..7e48546
--- /dev/null
+++ b/game/code/loading/intersectionloader.h
@@ -0,0 +1,29 @@
+#ifndef INTERSECTION_LOADER_H
+#define INTERSECTION_LOADER_H
+
+// Pure 3D
+#include <p3d/loadmanager.hpp>
+#include <p3d/p3dtypes.hpp>
+
+class IntersectionLoader : public tChunkHandler
+{
+public:
+ IntersectionLoader();
+ virtual ~IntersectionLoader();
+
+ // P3D chunk loader.
+ virtual tLoadStatus Load(tChunkFile* f, tEntityStore* store);
+
+ // P3D chunk id.
+ virtual bool CheckChunkID(unsigned id);
+ virtual unsigned int GetChunkID();
+
+private:
+
+ // No copying or assignment. Declare but don't define.
+ //
+ IntersectionLoader( const IntersectionLoader& );
+ IntersectionLoader& operator= ( const IntersectionLoader& );
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/loading/loadingmanager.cpp b/game/code/loading/loadingmanager.cpp
new file mode 100644
index 0000000..88b2923
--- /dev/null
+++ b/game/code/loading/loadingmanager.cpp
@@ -0,0 +1,648 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: loadingmanager.cpp
+//
+// Description: Implementation for the LoadingManager class.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Ftech
+#include <raddebug.hpp>
+#include <radtime.hpp>
+#include <radfile.hpp>
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/loadingmanager.h>
+#include <loading/filehandlerfactory.h>
+#include <loading/cementfilehandler.h>
+#include <memory/srrmemory.h>
+#include <debug/profiler.h>
+#include <radtextdisplay.hpp>
+#include <main/game.h>
+
+#include <cheats/cheatinputsystem.h>
+
+#include <mission/gameplaymanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+LoadingManager* LoadingManager::spInstance = NULL;
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// LoadingManager::GetInstance
+//==============================================================================
+//
+// Description: Access point for the LoadingManager singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the LoadingManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+LoadingManager* LoadingManager::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// LoadingManager::CreateInstance
+//==============================================================================
+//
+// Description: Constructs the LoadingManager singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the LoadingManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+LoadingManager* LoadingManager::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "LoadingManager" );
+ rAssert( spInstance == NULL );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+ spInstance = new LoadingManager;
+ rAssert( spInstance );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+MEMTRACK_POP_GROUP("LoadingManager");
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// LoadingManager::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the LoadingManager.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void LoadingManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete spInstance;
+ spInstance = NULL;
+}
+
+
+
+void LoadingManager::AddCallback( LoadingManager::ProcessRequestsCallback* pCallback, void* pUserData)
+{
+ if ( mCancellingLoads )
+ {
+ return;
+ }
+
+ if(mRequestHead == mRequestTail && !mLoading )
+ {
+ pCallback->OnProcessRequestsComplete(pUserData);
+ return;
+ }
+
+ int newTail = (mRequestTail + 1) % MAX_REQUESTS;
+
+ rAssert( newTail != mRequestHead );
+
+ //rAssert(!mRequests[lastAddedRequest].pCallback);
+ mRequests[mRequestTail].pCallback = pCallback;
+ mRequests[mRequestTail].pUserData = pUserData;
+ mRequests[mRequestTail].filename[0] = '\0';
+
+ mRequestTail = newTail;
+}
+
+//==============================================================================
+// LoadingManager::AddRequest
+//==============================================================================
+//
+// Description: Clients use this method to submit a loading request.
+// This request is not serviced immediately. The request remains
+// queued until LoadingManager::ProcessRequests() is invoked.
+//
+// Parameters: handlerType - an enumeration is used to specify which
+// file handler to use for loading and processing the data
+// (the use of an enumeration keeps the clients of LoadingManager
+// from being dependant on any FileHandler classes.
+//
+// filename - fully qualified path and name of file.
+//
+// secitonName - hack to allow p3d inventory sections to be created
+// based on filenames
+//
+// groupName - this allows a memtag section to be declared.
+//
+// Return: true - on success
+// false - request queue is full or loading is in progress
+//
+//==============================================================================
+void LoadingManager::AddRequest
+(
+ FileHandlerEnum handlerType,
+ const char* filename,
+ GameMemoryAllocator heap,
+ const char* sectionName,
+ const char* groupTag,
+ LoadingManager::ProcessRequestsCallback* pCallback,
+ void* pUserData
+)
+{
+ if ( mCancellingLoads )
+ {
+ return;
+ }
+
+ unsigned newTail = (mRequestTail + 1) % MAX_REQUESTS;
+
+#ifndef FINAL
+ rReleaseAssertMsg( static_cast< int >( newTail ) != mRequestHead, "Too many load requests already!\n");
+
+ // Dusit [Dec 2, 2002]:
+ // Bad if we're overwriting the other load request. This is a fatal error.
+ if( static_cast<int>(newTail) == mRequestHead )
+ {
+ IRadTextDisplay* textDisplay;
+ ::radTextDisplayGet( &textDisplay, GMA_DEFAULT );
+ if ( textDisplay )
+ {
+ textDisplay->SetBackgroundColor( 0 );
+ textDisplay->SetTextColor( 0xffffffff );
+ textDisplay->Clear();
+ textDisplay->TextOutAt( "TOO MANY LOAD REQUESTS!", 20, 10 );
+ textDisplay->TextOutAt( ">:-8", 20, 14 );
+ if ( CommandLineOptions::Get( CLO_DEMO_TEST ) ||
+ GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_DEMO_TEST ) )
+ {
+ char buffy[32];
+ sprintf( buffy, "Demo Count: %d", GetGame()->GetDemoCount() );
+ textDisplay->TextOutAt( buffy, 20, 16 );
+
+ unsigned int time = GetGame()->GetTime();
+ unsigned int hours = time / 3600000;
+ unsigned int deltaTime = time % 3600000;
+
+ unsigned int minutes = deltaTime / 60000;
+ deltaTime = deltaTime % 60000;
+
+ unsigned int seconds = deltaTime / 1000;
+ deltaTime = deltaTime % 1000;
+ sprintf( buffy, "Time: %d:%d:%d.%d", hours, minutes, seconds, deltaTime );
+ textDisplay->TextOutAt( buffy, 20, 18 );
+
+ if ( GetGameplayManager() )
+ {
+ sprintf( buffy, "Level %d", GetGameplayManager()->GetCurrentLevelIndex() );
+ textDisplay->TextOutAt( buffy, 20, 20 );
+ }
+ }
+ textDisplay->SwapBuffers();
+ textDisplay->Release();
+ }
+ rReleaseBreak();
+ }
+#endif
+
+ //mRequests[mNumRequests].filename = filename;
+ rAssert( strlen( filename ) < LoadingRequest::LOADING_FILENAME_LENGTH );
+ strcpy( mRequests[mRequestTail].filename, filename );
+
+ mRequests[mRequestTail].pFileHandler = FileHandlerFactory::CreateFileHandler( handlerType, sectionName );
+ mRequests[mRequestTail].pFileHandler->AddRef();
+
+#ifdef MEMORYTRACKER_ENABLED
+ if( groupTag == NULL )
+ {
+ //
+ // Use the filename by default
+ //
+ rAssert( strlen( filename ) < LoadingRequest::LOADING_FILENAME_LENGTH );
+ strcpy( mRequests[mRequestTail].groupTag, filename );
+ }
+ else
+ {
+ rAssert( strlen( groupTag ) < LoadingRequest::LOADING_FILENAME_LENGTH );
+ strcpy( mRequests[mRequestTail].groupTag, groupTag );
+ }
+#endif
+
+ mRequests[mRequestTail].heap = heap;
+ mRequests[mRequestTail].pCallback = pCallback;
+ mRequests[mRequestTail].pUserData = pUserData;
+ mRequestTail = newTail;
+
+ this->ProcessNextRequest();
+}
+
+//==============================================================================
+// LoadingManager::OnLoadFileComplete
+//==============================================================================
+//
+// Description: FileHandler's will invoke this callback when an asynchronous
+// load completes.
+//
+// Parameters: pUserData - optional user data that was passed in when the
+// load was requested
+//
+// Return: None.
+//
+//==============================================================================
+void LoadingManager::OnLoadFileComplete( void* pUserData )
+{
+ rAssert( (int)pUserData == mRequestHead );
+
+ // Display some debug info.
+ LoadingRequest& request = mRequests[mRequestHead ];
+
+ extern bool gLoadingSpew;
+
+ if ( !(CommandLineOptions::Get( CLO_NO_LOADING_SPEW )) )
+ {
+ rReleasePrintf( "<< END >> Async Loading: %s (%u msecs)\n",
+ request.filename,
+ radTimeGetMilliseconds() - request.startTime );
+ }
+
+ // Continue onto the next request.
+ mRequestHead = (mRequestHead + 1) % MAX_REQUESTS;
+ mLoading = false;
+
+ if(request.pCallback)
+ {
+ request.pCallback->OnProcessRequestsComplete(request.pUserData);
+ }
+
+ request.pFileHandler->Release();
+
+ this->ProcessNextRequest();
+}
+
+
+//==============================================================================
+// LoadingManager::LoadSync
+//==============================================================================
+//
+// Description: Load a file synchronously.
+//
+// Parameters: handlerType - an enumeration is used to specify which
+// file handler to use for loading and processing the data
+// (the use of an enumeration keeps the clients of LoadingManager
+// from being dependant on any FileHandler classes.
+//
+// filename - fully qualified path and name of file.
+//
+// Return: None.
+//
+//==============================================================================
+void LoadingManager::LoadSync
+(
+ FileHandlerEnum handlerType,
+ const char* filename,
+ GameMemoryAllocator heap,
+ const char* sectionName
+)
+{
+ rReleasePrintf("\n\n!!!!!! TRC VIOLATION, USE ASYNC!!!!!!!\n\n");
+// rAssert( false );
+
+ unsigned int startTime = radTimeGetMilliseconds();
+
+ if ( !(CommandLineOptions::Get( CLO_NO_LOADING_SPEW )) )
+ {
+ rDebugPrintf( "<<START>> Sync Loading: %s\n", filename );
+ }
+
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+ FileHandler* pHandler = FileHandlerFactory::CreateFileHandler( handlerType, sectionName );
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+ pHandler->AddRef( );
+ rAssert( pHandler );
+
+ HeapMgr()->PushHeap( heap );
+ pHandler->LoadFileSync( filename );
+ HeapMgr()->PopHeap( heap );
+
+ pHandler->Release( );
+ pHandler = 0;
+
+ if ( !(CommandLineOptions::Get( CLO_NO_LOADING_SPEW )) )
+ {
+ rReleasePrintf( "<< END >> Sync Loading: %s (%u msecs)\n",
+ filename,
+ radTimeGetMilliseconds() - startTime );
+ }
+}
+
+//=============================================================================
+// LoadingManager::CancelPendingRequests
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoadingManager::CancelPendingRequests()
+{
+ if ( mRequestHead != mRequestTail )
+ {
+ mRequestTail = (mRequestHead + 1) % MAX_REQUESTS; //Catch up and lose the rest of the loads.
+ }
+
+ mCancellingLoads = true;
+
+ while ( mLoading )
+ {
+ ::radFileService();
+ p3d::loadManager->SwitchTask();
+ p3d::loadManager->TriggerCallbacks();
+ }
+
+ //Close all the open cement files.
+ CementFileHandle i;
+ for ( i = 0; i < MAX_CEMENT_LIBRARIES; ++i )
+ {
+ if ( mCementLibraries[ i ].library != NULL )
+ {
+ UnregisterCementLibrary( i );
+ }
+ }
+}
+
+//=============================================================================
+// LoadingManager::RegisterCementLibrary
+//=============================================================================
+// Description: Registers the named cement library with RadFile.
+//
+// Parameters: filename - name of cement file
+//
+// Return: CementFileHandle - internally, its the index of the cement
+// library pointer. Used for indicating which
+// cement library to release later on
+//
+//=============================================================================
+CementFileHandle LoadingManager::RegisterCementLibrary( const char* filename )
+{
+ if ( mCancellingLoads )
+ {
+ return 0;
+ }
+
+ int i;
+
+ for( i = 0; i < MAX_CEMENT_LIBRARIES; i++ )
+ {
+ if( ( mCementLibraries[i].library == NULL )
+ && ( mCementLibraries[i].isLoading == false ) )
+ {
+ break;
+ }
+ }
+
+ rAssertMsg( ( i < MAX_CEMENT_LIBRARIES ), "Too many cement libraries\n" );
+
+ //
+ // Put a file handler into the queue so that we'll wait until this
+ // thing is fully registered. This file handler is slightly different
+ // than the rest, and we're in a special-purpose function anyway, so don't
+ // go through the factory.
+ //
+ int newTail = (mRequestTail + 1) % MAX_REQUESTS;
+
+ if( newTail != mRequestHead)
+ {
+ rAssert( strlen( filename ) < LoadingRequest::LOADING_FILENAME_LENGTH );
+ strcpy( mRequests[mRequestTail].filename, filename );
+#ifdef MEMORYTRACKER_ENABLED
+ strcpy( mRequests[mRequestTail].groupTag, filename );
+#endif
+#ifdef RAD_GAMECUBE
+ mRequests[mRequestTail].heap = GMA_GC_VMM;
+#else
+ mRequests[mRequestTail].heap = GMA_PERSISTENT; // Cement library registrations should be around for the duration --jdy
+#endif
+
+MEMTRACK_PUSH_GROUP( "LoadingManager" );
+ HeapMgr()->PushHeap (GMA_TEMP);
+
+ mRequests[mRequestTail].pFileHandler = new(GMA_TEMP) CementFileHandler( &(mCementLibraries[i]) );
+ mRequests[mRequestTail].pFileHandler->AddRef();
+
+ mCementLibraries[i].isLoading = true;
+
+ HeapMgr()->PopHeap (GMA_TEMP);
+MEMTRACK_POP_GROUP("LoadingManager");
+
+ mRequests[mRequestTail].pCallback = NULL;
+
+ mRequestTail = newTail;
+
+ this->ProcessNextRequest();
+
+ return( i );
+ }
+ else
+ {
+ rWarningMsg( 0, "Failed to add cement library request" );
+ return( -1 );
+ }
+}
+
+//=============================================================================
+// LoadingManager::UnregisterCementLibrary
+//=============================================================================
+// Description: Unregister a cement library (hence the name)
+//
+// Parameters: handle - in practice, the array index for the cement library
+//
+// Return: void
+//
+//=============================================================================
+void LoadingManager::UnregisterCementLibrary( CementFileHandle handle )
+{
+ rAssert( handle >= 0 );
+ //rAssert( mCementLibraries[handle].library != NULL );
+
+ //
+ // Just to be safe, make sure that the cement library is inactive before
+ // we release it. Synchronous wait okay here?
+ //
+ if( mCementLibraries[handle].library != NULL )
+ {
+ mCementLibraries[handle].library->WaitForCompletion();
+
+ mCementLibraries[handle].library->Release();
+ mCementLibraries[handle].library = NULL;
+ }
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// LoadingManager::LoadingManager
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LoadingManager::LoadingManager()
+:
+ mRequestHead( 0 ),
+ mRequestTail( 0 ),
+ mLoading(false),
+ mCancellingLoads( false )
+{
+ int i;
+
+ for( i = 0; i < MAX_CEMENT_LIBRARIES; i++ )
+ {
+ mCementLibraries[i].library = NULL;
+ mCementLibraries[i].isLoading = false;
+ }
+
+#ifdef RAD_WIN32
+ mRequestsProcessed = 0;
+#endif
+}
+
+
+//==============================================================================
+// LoadingManager::~LoadingManager
+//==============================================================================
+//
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LoadingManager::~LoadingManager()
+{
+ int i;
+
+ for( i = 0; i < MAX_CEMENT_LIBRARIES; i++ )
+ {
+ if( mCementLibraries[i].library != NULL )
+ {
+ mCementLibraries[i].library->Release();
+ }
+ }
+}
+
+
+//==============================================================================
+// LoadingManager::ProcessNextRequest
+//==============================================================================
+//
+// Description: Handle the next load request in the queue.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void LoadingManager::ProcessNextRequest()
+{
+ // Are we done?
+ if(!mLoading)
+ {
+ if(mRequestHead != mRequestTail)
+ {
+ // Start loading the next file.
+ LoadingRequest& request = mRequests[mRequestHead];
+
+ if ( request.filename[0] != '\0' )
+ {
+ if ( !(CommandLineOptions::Get( CLO_NO_LOADING_SPEW )) )
+ {
+ rReleasePrintf( "<<START>> Async Loading: %s\n", request.filename );
+ }
+#ifdef RAD_WIN32
+ mRequestsProcessed++;
+#endif
+
+ request.startTime = radTimeGetMilliseconds();
+ mLoading = true;
+ GameMemoryAllocator heap = request.heap;
+
+#ifndef RAD_RELEASE
+ if (HeapManager::s_bSpecialRoute)
+ {
+ heap = GMA_SPECIAL;
+ }
+#endif
+
+ request.pFileHandler->LoadFile( request.filename,
+ this,
+ (void*)mRequestHead,
+ heap );
+ }
+ else
+ {
+ rAssert( request.pCallback != NULL );
+
+ mRequestHead = (mRequestHead + 1) % MAX_REQUESTS;
+ mLoading = false;
+
+ request.pCallback->OnProcessRequestsComplete(request.pUserData);
+ ProcessNextRequest();
+ }
+ }
+ }
+}
+
+
diff --git a/game/code/loading/loadingmanager.h b/game/code/loading/loadingmanager.h
new file mode 100644
index 0000000..4b0cc7b
--- /dev/null
+++ b/game/code/loading/loadingmanager.h
@@ -0,0 +1,213 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: loadingmanager.h
+//
+// Description: Declaration of LoadingManager singleton class.
+//
+// History: 3/25/2002 + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef LOADINGMANAGER_H
+#define LOADINGMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <loading/filehandlerenum.h>
+#include <loading/filehandler.h>
+
+#ifndef TOOLS
+#include <memory/srrmemory.h>
+#endif
+
+//========================================
+// Forward References
+//========================================
+class FileHandler;
+struct ILoadingListCallback;
+struct IRadCementLibrary;
+
+
+//
+// Types
+//
+typedef int CementFileHandle;
+
+
+//=============================================================================
+//
+// Synopsis: All non-streamed disc loads must be routed through this class.
+//
+//=============================================================================
+class LoadingManager : public FileHandler::LoadFileCallback
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static LoadingManager* GetInstance();
+ static LoadingManager* CreateInstance();
+ static void DestroyInstance();
+
+ //
+ // Cement file registration functions. Should probably be called from
+ // context switch functions and the like
+ //
+ CementFileHandle RegisterCementLibrary( const char* filename );
+ void UnregisterCementLibrary( CementFileHandle handle );
+
+ //----------------------------------------------------------------------
+ // ASYNCHRONOUS LOADING
+ //----------------------------------------------------------------------
+
+ struct ProcessRequestsCallback
+ {
+ virtual void OnProcessRequestsComplete( void* pUserData ) = 0;
+ };
+
+ void AddCallback( LoadingManager::ProcessRequestsCallback* pCallback = NULL,
+ void* pUserData = 0 );
+
+ void AddRequest( FileHandlerEnum handlerType,
+ const char* filename,
+ GameMemoryAllocator heap,
+ const char* sectionName = 0,
+ const char* groupTag = NULL ,
+ LoadingManager::ProcessRequestsCallback* pCallback = NULL,
+ void* pUserData = 0 );
+
+ inline void AddRequest( FileHandlerEnum handlerType,
+ const char* filename,
+ GameMemoryAllocator heap,
+ LoadingManager::ProcessRequestsCallback* pCallback,
+ void* pUserData = 0 )
+ {
+ AddRequest( handlerType, filename, heap, 0, NULL, pCallback, pUserData);
+ }
+
+ //----------------------------------------------------------------------
+ // SYNCHRONOUS LOADING
+ //----------------------------------------------------------------------
+
+ void LoadSync( FileHandlerEnum handlerType, const char* filename, GameMemoryAllocator heap = GMA_DEFAULT,
+ const char* sectionName = NULL );
+
+
+ //----------------------------------------------------------------------
+ // Loading Accessor
+ //----------------------------------------------------------------------
+ bool IsLoading();
+
+ inline int GetRequestHead() const { return mRequestHead; }
+ inline int GetRequestTail() const { return mRequestTail; }
+
+ int GetNumCurrentRequests() const;
+
+ void CancelPendingRequests(); //This will not cancel the current request.
+
+#ifdef RAD_WIN32
+ int GetNumRequestsProcessed() const { return mRequestsProcessed; }
+ inline void ResetRequestsProcessed();
+#endif
+
+ //
+ // Used to hold a pointer to a cement library object. isLoading bool
+ // needed since registration is asynchronous.
+ //
+ struct CementLibraryStruct
+ {
+ IRadCementLibrary* library;
+ bool isLoading;
+ };
+
+ private:
+
+ // No public access to these, use singleton interface.
+ LoadingManager();
+ ~LoadingManager();
+
+ // Declared but not defined to prevent copying and assignment.
+ LoadingManager( const LoadingManager& loadingmanager );
+ LoadingManager& operator=( const LoadingManager& loadingmanager );
+
+ // Implement FileHandler::LoadFileCallback interface.
+ virtual void OnLoadFileComplete( void* pUserData );
+
+ void ProcessNextRequest();
+
+ // Pointer to the one and only instance of this singleton.
+ static LoadingManager* spInstance;
+
+ // Store loading requests here.
+ enum { MAX_REQUESTS = 200 };
+
+ struct LoadingRequest
+ {
+ enum { LOADING_FILENAME_LENGTH = 128 };
+ // Darwin: is 64 too big/big enough?
+ char filename[LOADING_FILENAME_LENGTH];
+ FileHandler* pFileHandler;
+ unsigned int startTime;
+ GameMemoryAllocator heap;
+ ProcessRequestsCallback* pCallback;
+ void* pUserData;
+#ifdef MEMORYTRACKER_ENABLED
+ // Since filenames go in here by default, use filename length for the size
+ char groupTag[LOADING_FILENAME_LENGTH];
+#endif
+ };
+ LoadingRequest mRequests[MAX_REQUESTS];
+ int mRequestHead;
+ int mRequestTail;
+
+ bool mLoading;
+
+ bool mCancellingLoads;
+
+#ifdef RAD_WIN32
+ int mRequestsProcessed;
+#endif
+
+ //
+ // Store cement libraries
+ //
+#ifdef RAD_XBOX
+ static const int MAX_CEMENT_LIBRARIES = 15;
+#else
+ static const int MAX_CEMENT_LIBRARIES = 10;
+#endif
+ CementLibraryStruct mCementLibraries[MAX_CEMENT_LIBRARIES];
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline LoadingManager* GetLoadingManager() { return( LoadingManager::GetInstance() ); }
+
+//=============================================================================
+// LoadingManager::IsLoading
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool LoadingManager::IsLoading()
+{
+ return ( mLoading || mRequestTail != mRequestHead );
+}
+
+inline int LoadingManager::GetNumCurrentRequests() const
+{
+ return( (mRequestTail - mRequestHead + MAX_REQUESTS) % MAX_REQUESTS );
+}
+
+#ifdef RAD_WIN32
+ inline void LoadingManager::ResetRequestsProcessed()
+ {
+ mRequestsProcessed = 0;
+ }
+#endif // RAD_WIN32
+
+#endif //LOADINGMANAGER_H
diff --git a/game/code/loading/locatorloader.cpp b/game/code/loading/locatorloader.cpp
new file mode 100644
index 0000000..38432e4
--- /dev/null
+++ b/game/code/loading/locatorloader.cpp
@@ -0,0 +1,1118 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: LocatorLoader.cpp
+//
+// Description: Implement LocatorLoader
+//
+// History: 24/05/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radmath/radmath.hpp>
+#include <p3d/chunkfile.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/LocatorLoader.h>
+
+#include <constants/srrchunks.h>
+
+#include <memory/srrmemory.h>
+
+#include <meta/carstartlocator.h>
+#include <meta/eventlocator.h>
+#include <meta/locator.h>
+#include <meta/locatortypes.h>
+#include <meta/recttriggervolume.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/splinelocator.h>
+#include <meta/triggervolumetracker.h>
+#include <meta/zoneeventlocator.h>
+#include <meta/carstartlocator.h>
+#include <meta/occlusionlocator.h>
+#include <meta/interiorentrancelocator.h>
+#include <meta/directionallocator.h>
+#include <meta/actioneventlocator.h>
+#include <meta/fovlocator.h>
+#include <meta/staticcamlocator.h>
+#include <meta/pedgrouplocator.h>
+#include <meta/scriptlocator.h>
+
+#include <camera/railcam.h>
+#include <camera/staticcam.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercammanager.h>
+
+#include <render/RenderManager/RenderManager.h>
+#include <render/Culling/WorldScene.h>
+
+#include <mission/AnimatedIcon.h>
+#include <mission/gameplaymanager.h>
+
+#include <worldsim/parkedcars/parkedcarmanager.h>
+#include <worldsim/coins/coinmanager.h>
+
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+void Swap(char* data_in, char* data_out, int n);
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+int LocatorLoader::msZoneNameCount = 0;
+
+//==============================================================================
+// LocatorLoader::LocatorLoader
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LocatorLoader::LocatorLoader()
+: tSimpleChunkHandler( SRR2::ChunkID::LOCATOR )
+{
+ mpListenerCB = NULL;
+}
+
+//==============================================================================
+// LocatorLoader::~LocatorLoader
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LocatorLoader::~LocatorLoader()
+{
+}
+
+//=============================================================================
+// LocatorLoader::LoadObject
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (tChunkFile* f, tEntityStore* store)
+//
+// Return: tEntity
+//
+//=============================================================================
+tEntity* LocatorLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+MEMTRACK_PUSH_GROUP( "Locator Loading" );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap (GMA_GC_VMM);
+ #else
+ HeapMgr()->PushHeap (GMA_LEVEL_OTHER);
+ #endif
+
+ char name[256];
+ f->GetPString( name );
+
+ LocatorType::Type type;
+ type = (LocatorType::Type)(f->GetUInt());
+
+ unsigned int numElements = f->GetUInt();
+ unsigned int* elements = new unsigned int[ numElements ];
+
+ unsigned int i;
+ for( i = 0; i < numElements; i++ )
+ {
+ elements[ i ] = f->GetUInt();
+ }
+
+ rmt::Vector pos;
+
+ pos.x = f->GetFloat();
+ pos.y = f->GetFloat();
+ pos.z = f->GetFloat();
+
+ Locator* locator = NULL;
+
+ unsigned int numTriggers = f->GetUInt();
+ bool hasSubChunks = true;
+
+ switch( type )
+ {
+ case LocatorType::GENERIC:
+ {
+ MEMTRACK_PUSH_GROUP("Generic Locator");
+ locator = new Locator();
+ rAssert( locator );
+
+ hasSubChunks = false;
+ MEMTRACK_POP_GROUP("Generic Locator");
+ break;
+ }
+ case LocatorType::EVENT:
+ {
+ MEMTRACK_PUSH_GROUP("Event Locator");
+ EventLocator* eventloc = new EventLocator();
+ rAssert( eventloc );
+
+ eventloc->SetEventType( (LocatorEvent::Event)elements[ 0 ] );
+
+ if ( eventloc->GetEventType() == LocatorEvent::CAMERA_CUT )
+ {
+ //Make this locator inactive if the wreckless cam is not active.
+ //Hackish
+ SuperCam* sc = GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam();
+ if ( GetGameplayManager()->mIsDemo || (sc && sc->GetType() == SuperCam::WRECKLESS_CAM) )
+ {
+ eventloc->SetFlag( Locator::ACTIVE, true );
+ }
+ else
+ {
+ eventloc->SetFlag( Locator::ACTIVE, false );
+ }
+ }
+
+ if ( numElements == 2 )
+ {
+ rAssert( eventloc->GetEventType() == LocatorEvent::FAR_PLANE ||
+ eventloc->GetEventType() == LocatorEvent::GOO_DAMAGE ||
+ eventloc->GetEventType() == LocatorEvent::LIGHT_CHANGE ||
+ eventloc->GetEventType() == LocatorEvent::TRAP ||
+ eventloc->GetEventType() == LocatorEvent::CHECK_POINT );
+
+ eventloc->SetData( elements[1] );
+ }
+
+ locator = eventloc;
+ MEMTRACK_POP_GROUP("Event Locator");
+ break;
+ }
+ case LocatorType::SPLINE:
+ {
+ MEMTRACK_PUSH_GROUP("Spline Locator");
+ SplineLocator* splineloc = new SplineLocator();
+ rAssert( splineloc );
+
+ locator = splineloc;
+ MEMTRACK_POP_GROUP("Spline Locator");
+ break;
+ }
+ case LocatorType::DYNAMIC_ZONE:
+ {
+ MEMTRACK_PUSH_GROUP("Zone Locator");
+ ZoneEventLocator* eventloc = new ZoneEventLocator();
+ rAssert( eventloc );
+
+ //this is where we convert the data into a string... ba-dum-ching!
+ eventloc->SetZoneSize( numElements * 4 ); //This is to prevent fragmentation.
+
+ char* zone = new char[numElements * 4 + 4];
+ memcpy( zone, elements, numElements * 4 );
+ zone[numElements * 4] = '\0';
+
+#ifdef RAD_GAMECUBE
+ Swap( zone, zone, numElements * 4 + 4);
+#endif
+
+ eventloc->SetZone( zone );
+
+ delete[] zone;
+
+ locator = eventloc;
+ MEMTRACK_POP_GROUP("Zone Locator");
+ break;
+ }
+ case LocatorType::CAR_START:
+ {
+ MEMTRACK_PUSH_GROUP("Car Locator");
+ CarStartLocator* carStartloc = new CarStartLocator();
+ rAssert( carStartloc );
+
+ float rotation = 0;
+
+ memcpy( (void*)(&rotation), (const void*)&elements[0], sizeof(float) );
+
+ //HACK
+ if ( numElements >= 2 )
+ {
+ //There is parked car data.
+ if ( elements[1] == 1 )
+ {
+ if ( !GetGameplayManager()->mIsDemo )
+ {
+ //Should place a parked car here. Add to ParkedCarManager
+ GetPCM().AddLocator( carStartloc );
+ }
+ }
+
+ if ( numElements > 2 )
+ {
+ //We have a special car here.
+ char* carName = new char[(numElements - 2) * 4 + 4];
+ memcpy( carName, &elements[2], (numElements - 2) * 4 );
+ carName[(numElements - 2) * 4] = '\0';
+#ifdef RAD_GAMECUBE
+ Swap( carName, carName, (numElements - 2) * 4 + 4 );
+#endif
+
+ //Do something with the name
+ GetPCM().AddFreeCar( carName, carStartloc );
+
+ delete carName;
+ }
+ }
+
+ carStartloc->SetRotation( rotation );
+
+ locator = carStartloc;
+ hasSubChunks = false;
+ MEMTRACK_POP_GROUP("Car Locator");
+ break;
+ }
+ case LocatorType::OCCLUSION:
+ {
+ MEMTRACK_PUSH_GROUP("Occlusion Locator");
+ OcclusionLocator* occLoc = new OcclusionLocator();
+ rAssert( occLoc );
+
+ occLoc->SetNumTriggers( numTriggers, GMA_LEVEL_OTHER );
+
+ bool first = true;
+
+ while( f->ChunksRemaining() )
+ {
+ f->BeginChunk();
+
+ switch( f->GetCurrentID() )
+ {
+ case SRR2::ChunkID::TRIGGER_VOLUME:
+ {
+ //Only add the first one to the trigger tracker.
+ LoadTriggerVolume( f, occLoc, first );
+ first = false;
+ break;
+ }
+ }
+
+ f->EndChunk();
+ }
+
+ //How many of the locators are occlusion, the rest are visiblers - 1
+ if ( numElements )
+ {
+ occLoc->SetNumOccTriggers( elements[0] );
+ }
+ else
+ {
+ occLoc->SetNumOccTriggers( numTriggers - 1 );
+ }
+
+ locator = occLoc;
+ hasSubChunks = false; //Special case, already loaded.
+ MEMTRACK_POP_GROUP("Occlusion Locator");
+ break;
+ }
+ case LocatorType::INTERIOR_ENTRANCE:
+ {
+ MEMTRACK_PUSH_GROUP("Interior Locator");
+ InteriorEntranceLocator* intEntLoc = new InteriorEntranceLocator();
+ rAssert( intEntLoc );
+
+ rAssert( numElements * 4 < 256 );
+ unsigned int length = numElements * 4;
+
+ char charData[256];
+ memcpy( charData, elements, numElements * 4 );
+
+ #ifdef RAD_GAMECUBE
+ Swap( charData, charData, numElements * 4 );
+ #endif
+ unsigned int chari = 0;
+ for ( chari = 0; chari < length; ++chari )
+ {
+ if ( charData[chari] == '\0' )
+ {
+ //Found the null.
+ break;
+ }
+ }
+
+ //this is where we convert the data into a string... ba-dum-ching!
+ intEntLoc->SetInteriorFileNameSize( chari ); //This is to prevent fragmentation.
+ intEntLoc->SetInteriorFileName( charData );
+
+ //Now get the transform.
+ unsigned int index = chari / 4;
+
+ if ( chari % 4 != 0 )
+ {
+ index++; //Add one to cover the extra.
+ }
+
+ rmt::Matrix mat;
+ mat.Identity();
+
+ unsigned int i;
+ for ( i = 0; i < 3; ++i )
+ {
+ rmt::Vector v;
+ memcpy( &v.x, &elements[index + 3 * i], sizeof(float) * 3 );
+ mat.m[i][0] = v.x;
+ mat.m[i][1] = v.y;
+ mat.m[i][2] = v.z;
+ }
+
+ intEntLoc->SetTransform( mat );
+ locator = intEntLoc;
+ MEMTRACK_POP_GROUP("Interior Locator");
+ break;
+ }
+ case LocatorType::DIRECTIONAL:
+ {
+ MEMTRACK_PUSH_GROUP("Directional Locator");
+ DirectionalLocator* dirLoc = new DirectionalLocator();
+ rAssert( dirLoc );
+
+ rmt::Matrix mat;
+ mat.Identity();
+
+ unsigned int i;
+ for ( i = 0; i < 3; ++i )
+ {
+ rmt::Vector v;
+ memcpy( &v.x, &elements[3 * i], sizeof(float) * 3 );
+ mat.m[i][0] = v.x;
+ mat.m[i][1] = v.y;
+ mat.m[i][2] = v.z;
+ }
+
+ dirLoc->SetTransform( mat );
+
+ locator = dirLoc;
+ hasSubChunks = false;
+ MEMTRACK_POP_GROUP("Directional Locator");
+ break;
+ }
+ case LocatorType::ACTION:
+ {
+ MEMTRACK_PUSH_GROUP("Action Locator");
+ ActionEventLocator* actLoc = new ActionEventLocator();
+ rAssert( actLoc );
+
+ rAssert(numElements >= 5);
+ rAssert( (numElements - 2) * 4 < 256 );
+ unsigned int length = (numElements - 2) * 4;
+
+ char charData[256];
+ memcpy( charData, elements, (numElements - 2) * 4 );
+
+#ifdef RAD_GAMECUBE
+ Swap( charData, charData, (numElements - 2) * 4 );
+#endif
+
+ //Go through the charData until we find all 3 strings.
+ const unsigned int numStrings = 3;
+ char stringArray[numStrings][128]; //If the name is larger than 128, I'll shoot myself.
+
+ unsigned int i;
+ unsigned int startChari = 0;
+ unsigned int chari = 0;
+ for ( i = 0; i < numStrings; ++i )
+ {
+ while ( charData[chari] != '\0' && chari != length )
+ {
+ ++chari;
+ }
+
+ rAssertMsg( chari != length, "Missing text in action locator!" );
+
+ if ( chari != length )
+ {
+ rAssert( strlen(&charData[startChari]) < 128 );
+ strncpy( stringArray[i], &charData[startChari], chari - startChari );
+ stringArray[i][strlen(&charData[startChari])] = '\0';
+ ++chari;
+ if ( i == numStrings - 1 )
+ {
+ //We're done
+ break;
+ }
+ }
+
+ //move past the NULLS
+ while ( charData[chari] == '\0' && chari != length )
+ {
+ ++chari;
+ }
+
+ startChari = chari;
+ }
+
+ actLoc->SetObjNameSize( strlen(stringArray[0]) );
+ actLoc->SetObjName( stringArray[0] );
+
+ actLoc->SetJointNameSize( strlen(stringArray[1]) );
+ actLoc->SetJointName( stringArray[1] );
+
+ actLoc->SetActionNameSize( strlen(stringArray[2]) );
+ actLoc->SetActionName( stringArray[2] );
+
+ actLoc->SetButtonInput( (CharacterController::eIntention)elements[numElements - 2] );
+ actLoc->SetShouldTransform( elements[numElements - 1] == 1 ? true : false );
+
+ // TBJ [8/30/2002]
+ //
+ // I need to set the position of the locator before I add it to the game.
+ //
+ actLoc->SetLocation( pos );
+
+ // If AddToGame() fails, actLoc will be released.
+ // Hold on to it here.
+ //
+ actLoc->AddRef();
+ if ( actLoc->AddToGame( store ) )
+ {
+ locator = actLoc;
+ }
+ else
+ {
+ locator = NULL;
+ }
+ // If AddToGame() didn't addref the locator,
+ // this release will delete it.
+ //
+ actLoc->Release( );
+ MEMTRACK_POP_GROUP("Action Locator");
+ break;
+ }
+ case LocatorType::FOV:
+ {
+ MEMTRACK_PUSH_GROUP("FOV Locator");
+ FOVLocator* fovLoc = new FOVLocator();
+ rAssert( fovLoc );
+
+ float data[3];
+ memcpy( data, elements, sizeof(float) * 3 );
+
+ fovLoc->SetFOV( rmt::DegToRadian( data[0] ) );
+ fovLoc->SetTime( data[1] );
+ fovLoc->SetRate( data[2] );
+
+ locator = fovLoc;
+ hasSubChunks = true;
+ MEMTRACK_POP_GROUP("FOV Locator");
+ break;
+ }
+
+ case LocatorType::BREAKABLE_CAMERA:
+ {
+ MEMTRACK_PUSH_GROUP("Breakable Camera Locator");
+/*
+ //TODO: what do we do with these??
+ float data[10];
+ memcpy( data, elements, sizeof(float) * 10 );
+
+ rmt::Matrix mat;
+ mat.Identity();
+
+ unsigned int i;
+ for ( i = 0; i < 9;)
+ {
+ mat.m[i][0] = data[i];
+ mat.m[i][1] = data[i + 1];
+ mat.m[i][2] = data[i + 2];
+
+ i += 3;
+ }
+
+ float FOV = data[9];
+
+ // Create a new action event locator
+ ActionEventLocator* actLoc = new ActionEventLocator();
+ /*
+ actLoc->SetObjNameSize( strlen(stringArray[0]) );
+ actLoc->SetObjName( stringArray[0] );
+
+ actLoc->SetJointNameSize( strlen(stringArray[1]) );
+ actLoc->SetJointName( stringArray[1] );
+
+ actLoc->SetActionNameSize( strlen(stringArray[2]) );
+ actLoc->SetActionName( stringArray[2] );
+
+ actLoc->SetButtonInput( (CharacterController::eIntention)elements[numElements - 2] );
+ actLoc->SetShouldTransform( elements[numElements - 1] == 1 ? true : false );
+ */
+/*
+ actLoc->SetLocation( pos );
+
+ const char* ObjName = "Alien Camera Obj";
+ actLoc->SetObjNameSize( strlen( ObjName ) );
+ actLoc->SetObjName( ObjName );
+
+ const char* ActionName = "AlienCamera";
+
+ actLoc->SetActionNameSize( strlen( ActionName ) );
+ actLoc->SetActionName( ActionName );
+
+ actLoc->SetButtonInput( CharacterController::Attack );
+
+ bool addSuccess = actLoc->AddToGame( store );
+
+ // rAssertMsg( addSuccess, "Could not add camera locator to the game" );
+
+*/
+ MEMTRACK_POP_GROUP("Breakable Camera Locator");
+ break;
+ }
+ case LocatorType::STATIC_CAMERA:
+ {
+ MEMTRACK_PUSH_GROUP("Static Camera Locator");
+ StaticCamLocator* scLoc = new StaticCamLocator();
+
+ StaticCam* sCam = new StaticCam();
+
+ rAssert( numElements < 32 );
+ float data[32];
+ memcpy( data, elements, sizeof(float) * numElements );
+
+ rmt::Vector offset;
+ offset.x = data[0];
+ offset.y = data[1];
+ offset.z = data[2];
+
+ sCam->SetTargetOffset( offset );
+
+ float FOV = data[3];
+ sCam->SetMaxFOV( rmt::DegToRadian( FOV ) );
+
+ float lag = data[4];
+ sCam->SetTargetLag( lag );
+
+ int track;
+ memcpy( &track, &data[5], sizeof( int ) ); //This is sucky, sorry.
+ bool tracking = track == 1 ? true : false;
+
+ float trate = 0.04f;
+ if ( numElements >= 7 )
+ {
+ memcpy( &trate, &data[6], sizeof(float) );
+ }
+
+ if ( numElements >= 8 )
+ {
+ int flags = 0;
+ memcpy( &flags, &data[7], sizeof( int ) ); //This is sucky, sorry.
+
+ scLoc->SetOneShot( (flags & 1) > 0 );
+
+ if ( (flags & (1 << 1)) ) //This is the hacky way that I export this info.
+ {
+ //Disable fov on controller for this cam.
+ sCam->SetFOVLag( 0.0f );
+ }
+ }
+
+ if ( numElements >= 9 )
+ {
+ int inOut = 0;
+ memcpy( &inOut, &data[8], sizeof( int ) ); //This is sucky, sorry.
+
+ scLoc->SetCutInOut( inOut == 1 );
+
+ int intData = 0;
+ memcpy( &intData, &data[9], sizeof( int ) ); //This is sucky, sorry.
+
+ scLoc->SetCarOnly( (intData & 1) == 1 );
+ scLoc->SetOnFootOnly( (intData & (1 << 1)) == 1 << 1 );
+ }
+
+ sCam->SetTransitionPositionRate( trate );
+ sCam->SetTransitionTargetRate( trate );
+
+ sCam->SetTracking( tracking );
+
+ sCam->SetPosition( pos );
+
+ scLoc->SetStaticCam( sCam );
+
+ locator = scLoc;
+ hasSubChunks = true;
+ MEMTRACK_POP_GROUP( "Static Camera Locator" );
+ break;
+ }
+ case LocatorType::PED_GROUP:
+ {
+ MEMTRACK_PUSH_GROUP("Ped Group Locator");
+
+ PedGroupLocator* pgLoc = new PedGroupLocator();
+ rAssert( pgLoc );
+
+ unsigned int group = 0;
+
+ memcpy( (void*)(&group), (const void*)elements, sizeof(unsigned int) );
+
+ pgLoc->SetGroupNum( group );
+
+ locator = pgLoc;
+ hasSubChunks = true;
+ MEMTRACK_POP_GROUP( "Ped Group Locator" );
+ break;
+ }
+ case LocatorType::SCRIPT:
+ {
+ MEMTRACK_PUSH_GROUP("Script Locator");
+
+ ScriptLocator* sLoc = new ScriptLocator();
+ rAssert( sLoc );
+
+ char* text = new char[numElements * 4 + 4];
+ memcpy( text, elements, numElements * 4 );
+ text[numElements * 4] = '\0';
+
+#ifdef RAD_GAMECUBE
+ Swap( text, text, numElements * 4 + 4 );
+#endif
+
+ sLoc->SetString( text );
+
+ delete[] text;
+
+ locator = sLoc;
+ hasSubChunks = true;
+ MEMTRACK_POP_GROUP( "Script Locator" );
+ break;
+ }
+ case LocatorType::COIN:
+ {
+ MEMTRACK_PUSH_GROUP("Coin Locator");
+ //Adding world coin to coin manager
+ GetCoinManager()->AddWorldCoin( pos, GetRenderManager()->pWorldRenderLayer()->GetCurSectionName().GetUID() );
+ locator = NULL;
+ MEMTRACK_POP_GROUP( "Coin Locator" );
+ break;
+ }
+ default:
+ {
+ MEMTRACK_PUSH_GROUP("Unknown Locator");
+ rDebugString( "Bad locator loaded.. UNKNOWN type!\n" );
+ Locator* loc = new Locator();
+ rAssert( loc );
+ locator = loc;
+ MEMTRACK_POP_GROUP( "Unknown Locator" );
+ break;
+ }
+ }
+
+ // locator might be NULL if it was not successfully added to game.
+ //
+ // TBJ [8/30/2002]
+ //
+ if( locator != NULL )
+ {
+ if ( hasSubChunks )
+ {
+ locator->SetNumTriggers( numTriggers, HeapMgr()->GetCurrentHeap() );
+
+ while( f->ChunksRemaining() )
+ {
+ f->BeginChunk();
+
+ switch( f->GetCurrentID() )
+ {
+ case SRR2::ChunkID::TRIGGER_VOLUME:
+ {
+ LoadTriggerVolume( f, static_cast<TriggerLocator*>(locator) );
+ break;
+ }
+ case SRR2::ChunkID::SPLINE:
+ {
+ if ( locator->GetDataType() == LocatorType::SPLINE )
+ {
+ RailCam* railCam = LoadSpline( f, static_cast<SplineLocator*>(locator) );
+ rAssert( railCam );
+ static_cast<SplineLocator*>(locator)->SetRailCam( railCam );
+ }
+ else
+ {
+ RailCam* railCam = LoadSpline( f, NULL );
+ rAssert( railCam );
+ }
+ break;
+ }
+ case SRR2::ChunkID::EXTRA_MATRIX:
+ {
+ rmt::Matrix mat;
+ f->GetData(&(mat), 16, tFile::DWORD);
+
+ locator->SetMatrix( mat );
+
+ break;
+ }
+ }
+
+ f->EndChunk();
+ }
+ }
+
+ locator->SetName( name );
+ locator->SetLocation( pos );
+
+ //locator->SetFlag( Locator::ACTIVE, false );
+ }
+ delete[] elements;
+
+MEMTRACK_POP_GROUP("Locator Loading");
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap (GMA_GC_VMM);
+ #else
+ HeapMgr()->PopHeap (GMA_LEVEL_OTHER);
+ #endif
+
+ //
+ // Some locators should not be added to the inventory
+ //
+ if( type == LocatorType::DYNAMIC_ZONE )
+ {
+ //
+ // All the locators called load_something are never looked up
+ //
+ if( strncmp( name, "load", 4 ) == 0 )
+ {
+ msZoneNameCount++;
+ char tempName[256];
+ sprintf( tempName, "mfa%d%s", msZoneNameCount, name);
+ locator->SetName(tempName);
+ }
+ }
+
+ return locator;
+}
+
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// LocatorLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void LocatorLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// LocatorLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void LocatorLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "GeometryWrappedLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// LocatorLoader::LoadTriggerVolume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tChunkFile* f,
+// TriggerLocator* locator,
+// bool addToTracker )
+//
+// Return: void
+//
+//=============================================================================
+bool LocatorLoader::LoadTriggerVolume( tChunkFile* f,
+ TriggerLocator* locator,
+ bool addToTracker )
+{
+ bool good = true;
+
+ MEMTRACK_PUSH_GROUP("Trigger Volume Loading");
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap (GMA_LEVEL_OTHER);
+ #endif
+
+
+ if( f->GetCurrentID() == SRR2::ChunkID::TRIGGER_VOLUME )
+ {
+ char volname[256];
+ rmt::Matrix mat;
+ rmt::Vector scale;
+ rmt::Vector pos;
+
+ TriggerVolume* vol = NULL;
+
+ f->GetPString( volname );
+ unsigned int voltype = f->GetUInt();
+
+ scale.x = f->GetFloat();
+ scale.y = f->GetFloat();
+ scale.z = f->GetFloat();
+
+ float val;
+
+ for( unsigned int v = 0; v < 4; v++ )
+ {
+ for( unsigned int u = 0; u < 4; u++ )
+ {
+ val = f->GetFloat();
+
+ mat.m[v][u] = val;
+ }
+ }
+ pos = mat.Row(3);
+
+ switch( voltype )
+ {
+ case TriggerVolume::SPHERE:
+ {
+ MEMTRACK_PUSH_GROUP("Sphere Trigger");
+ SphereTriggerVolume* sphere = new SphereTriggerVolume();
+
+ // this is a sphere, so the radii should be equal
+ sphere->SetSphereRadius( scale.x );
+ vol = sphere;
+ MEMTRACK_POP_GROUP("Sphere Trigger");
+ break;
+ }
+ case TriggerVolume::RECTANGLE:
+ {
+ MEMTRACK_PUSH_GROUP("Rectangle Trigger");
+ RectTriggerVolume* box = new RectTriggerVolume( pos,
+ mat.Row(0),
+ mat.Row(1),
+ mat.Row(2),
+ scale.x,
+ scale.y,
+ scale.z );
+ vol = box;
+ MEMTRACK_POP_GROUP("Rectangle Trigger");
+ break;
+ }
+ default:
+ {
+ // never come here again!
+ MEMTRACK_PUSH_GROUP("Unknown Trigger");
+ rAssert( false );
+ MEMTRACK_POP_GROUP("Unknown Trigger");
+ break;
+ }
+ }
+
+ vol->SetName( volname );
+
+ vol->SetLocator( locator );
+ locator->AddTriggerVolume( vol );
+
+ vol->SetPosition( pos );
+
+ if ( addToTracker )
+ {
+ GetTriggerVolumeTracker()->AddTrigger( vol );
+ // dont notify the render manager (listener), instead, circumvent and add directly to the tree
+ //mpListenerCB->OnChunkLoaded(vol, mUserData, _id);
+ //GetRenderManager()->pWorldScene()->Add(vol); //Now we are adding to the DSG.
+ }
+
+ good = true;
+ }
+ else
+ {
+ good = false;
+ }
+
+ MEMTRACK_POP_GROUP("Trigger Volume Loading");
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap (GMA_LEVEL_OTHER);
+ #endif
+ return good;
+}
+
+//=============================================================================
+// LocatorLoader::LoadSpline
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tChunkFile* f, SplineLocator* splineLoc )
+//
+// Return: RailCam*
+//
+//=============================================================================
+
+RailCam* LocatorLoader::LoadSpline( tChunkFile* f, SplineLocator* splineLoc )
+{
+ if( f->GetCurrentID() == SRR2::ChunkID::SPLINE )
+ {
+MEMTRACK_PUSH_GROUP("Rail Cam");
+
+ char name[256];
+ f->GetPString( name );
+
+ int numCVs = f->GetInt();
+
+ rmt::SplineCurve newSpline;
+ newSpline.SetNumVertices( numCVs );
+
+ for( int i = 0; i < numCVs; i++ )
+ {
+ rmt::Vector v;
+
+ v.x = f->GetFloat();
+ v.y = f->GetFloat();
+ v.z = f->GetFloat();
+
+ newSpline.SetCntrlVertex( i, v );
+ }
+
+ rAssert( f->ChunksRemaining() );
+
+ //Load the railcam...
+ RailCam* newRailCam = new RailCam();
+
+ f->BeginChunk();
+
+ f->GetString( name );
+ newRailCam->SetBehaviour( (RailCam::Behaviour)f->GetUInt() );
+ newRailCam->SetMinRadius( f->GetFloat() );
+ newRailCam->SetMaxRadius( f->GetFloat() );
+ newRailCam->SetTrackRail( f->GetUInt() == 1 ? true : false );
+ newRailCam->SetTrackDist( f->GetFloat() );
+ newRailCam->SetReverseSense( f->GetUInt() == 1 ? true : false );
+ newRailCam->SetMaxFOV( rmt::DegToRadian( f->GetFloat() ) );
+
+ rmt::Vector targetOffset;
+ f->GetData( &targetOffset.x, 3, tFile::DWORD );
+ newRailCam->SetTargetOffset( targetOffset );
+
+ rmt::Vector play;
+ f->GetData( &play.x, 3, tFile::DWORD );
+ //newRailCam->SetAxisPlay( play );
+ //Hack to get some data
+ if ( play.x > 0.001f )
+ {
+ newRailCam->SetTransitionPositionRate( play.x );
+ newRailCam->SetTransitionTargetRate( play.x );
+ }
+
+ if ( play.y == 1.0f ) //This is set to one when we want to disable the lag.
+ {
+ //Disable FOV lagging.
+ newRailCam->SetFOVLag( 0.0f );
+ }
+
+ if ( splineLoc )
+ {
+ //Play.z is hiding stuff too
+ int hiddenX = (int)play.z; //I hate this.
+
+ splineLoc->SetCarOnly( (hiddenX & 0x00000001) != 0 );
+ splineLoc->SetCutInOut( (hiddenX & 0x0000002) != 0 );
+ splineLoc->SetReset( (hiddenX & 0x00000004) != 0 );
+ splineLoc->SetOnFootOnly( (hiddenX & 0x00000008) != 0 );
+ }
+
+ newRailCam->SetPositionLag( f->GetFloat() );
+ newRailCam->SetTargetLag( f->GetFloat() );
+
+ newRailCam->SetSplineCurve( newSpline );
+
+ f->EndChunk();
+
+MEMTRACK_POP_GROUP("Rail Cam");
+
+ return newRailCam;
+ }
+
+ return NULL;
+}
+
+//Endian swap.
+inline void Swap( char* data_in, char* data_out, int n )
+{
+ rAssert( n % 4 == 0 );
+
+ char tmp1, tmp2, tmp3, tmp4;
+ for(int i = 0; i < n; i += 4)
+ {
+ tmp1 = data_in[i];
+ tmp2 = data_in[i+1];
+ tmp3 = data_in[i+2];
+ tmp4 = data_in[i+3];
+
+ data_out[i] = tmp4;
+ data_out[i+1] = tmp3;
+ data_out[i+2] = tmp2;
+ data_out[i+3] = tmp1;
+ }
+}
diff --git a/game/code/loading/locatorloader.h b/game/code/loading/locatorloader.h
new file mode 100644
index 0000000..65e703b
--- /dev/null
+++ b/game/code/loading/locatorloader.h
@@ -0,0 +1,85 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: locatorloader.h
+//
+// Description: Blahblahblah
+//
+// History: 24/05/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef LOCATORLOADER_H
+#define LOCATORLOADER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/loadmanager.hpp>
+#include <p3d/p3dtypes.hpp>
+
+#include <constants/srrchunks.h>
+#include <render/Loaders/IWrappedLoader.h>
+//========================================
+// Forward References
+//========================================
+
+class TriggerLocator;
+class tEntity;
+class RailCam;
+class SplineLocator;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class LocatorLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ LocatorLoader();
+ virtual ~LocatorLoader();
+
+ //////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ //////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+private:
+
+ //////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ //////////////////////////////////////////////////////////////////////
+
+ //Prevent wasteful constructor creation.
+ LocatorLoader( const LocatorLoader& locatorloader );
+ LocatorLoader& operator=( const LocatorLoader& locatorloader );
+
+ // P3D chunk loader.
+ virtual tEntity* LoadObject(tChunkFile* f, tEntityStore* store);
+
+ void LoadTriggerLocator( tChunkFile* f,
+ tEntityStore* store,
+ TriggerLocator* locator );
+ bool LoadTriggerVolume( tChunkFile* f,
+ TriggerLocator* locator,
+ bool addToTracker = true );
+ RailCam* LoadSpline( tChunkFile* f, SplineLocator* splineLoc );
+
+ static int msZoneNameCount;
+};
+
+//******************************************************************************
+//
+// Inline Public Member Functions
+//
+//******************************************************************************
+
+#endif //LOCATORLOADER_H
diff --git a/game/code/loading/p3dfilehandler.cpp b/game/code/loading/p3dfilehandler.cpp
new file mode 100644
index 0000000..f9b8fae
--- /dev/null
+++ b/game/code/loading/p3dfilehandler.cpp
@@ -0,0 +1,196 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: p3dfilehandler.cpp
+//
+// Description: Implement P3DFileHandler
+//
+// History: 3/25/2002 + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <string.h>
+// Pure 3D
+#include <p3d/utility.hpp>
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/p3dfilehandler.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// P3DFileHandler::P3DFileHandler
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+P3DFileHandler::P3DFileHandler() : m_RefCount( 0 )
+{
+}
+
+//==============================================================================
+// P3DFileHandler::~P3DFileHandler
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+P3DFileHandler::~P3DFileHandler()
+{
+}
+
+
+//==============================================================================
+// P3DFileHandler::LoadFile
+//==============================================================================
+//
+// Description: Load a Pure3D file asynchronously.
+//
+// Parameters: filename - fully qualified path and filename
+// pCallback - client callback to invoke when load is complete
+// pUserData - optional client supplied user data
+//
+// Return: None.
+//
+//==============================================================================
+void P3DFileHandler::LoadFile
+(
+ const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap
+)
+{
+ rAssert( filename );
+ rAssert( pCallback );
+
+ mpCallback = pCallback;
+ mpUserData = pUserData;
+
+ //
+ // Ensure that the specified inventory section exists before loading
+ //
+ HeapMgr()->PushHeap (heap);
+ HeapMgr()->PushHeap (GMA_TEMP);
+ p3d::inventory->AddSection( mcSectionName );
+ HeapMgr()->PopHeap (GMA_TEMP);
+ p3d::loadAsync( filename, mcSectionName, this, pUserData, heap );
+ HeapMgr()->PopHeap (heap);
+}
+
+
+//==============================================================================
+// P3DFileHandler::Done
+//==============================================================================
+//
+// Description: Pure3D (via FTech) invokes this callback when the async load
+// is complete.
+//
+// Parameters: tLoadStatus status, tLoadRequest *load
+//
+// Return: None.
+//
+//==============================================================================
+void P3DFileHandler::Done( tLoadStatus status, tLoadRequest *load )
+{
+ //
+ // Percolate the callback up to the client.
+ //
+ mpCallback->OnLoadFileComplete( mpUserData );
+}
+
+
+//==============================================================================
+// P3DFileHandler::LoadFileSync
+//==============================================================================
+//
+// Description: Load a Pure3D file synchronously.
+//
+// Parameters: filename - fully qualified path and filename
+//
+// Return: None.
+//
+//==============================================================================
+void P3DFileHandler::LoadFileSync( const char* filename )
+{
+ rAssert( filename );
+
+ rReleasePrintf("Synchronous File Load. Bastard! %s\n", filename);
+ rAssert( false );
+
+ p3d::inventory->PushSection();
+
+ //
+ // Ensure that the specified inventory section exists before loading
+ //
+ p3d::inventory->AddSection( mcSectionName );
+ p3d::inventory->SelectSection( mcSectionName );
+
+ // SetInventorySection(mcSectionName );
+
+ tLoadRequest* pLR = new tLoadRequest(p3d::openFile(filename));
+ pLR->SetInventorySection(mcSectionName);
+ tLoadStatus result = p3d::loadManager->Load(pLR);
+
+ // bool result = p3d::load( filename );
+
+ p3d::inventory->PopSection();
+
+ rAssert( result == LOAD_OK );
+}
+
+
+
+//==============================================================================
+// P3DFileHandler::SetSectionName
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void P3DFileHandler::SetSectionName( const char* sectionName )
+{
+ rAssert( sectionName );
+
+ strcpy( mcSectionName, sectionName );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+
+
+
+
diff --git a/game/code/loading/p3dfilehandler.h b/game/code/loading/p3dfilehandler.h
new file mode 100644
index 0000000..8f5e542
--- /dev/null
+++ b/game/code/loading/p3dfilehandler.h
@@ -0,0 +1,81 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: p3dfilehandler.h
+//
+// Description: Declaration of P3DFileHandler class.
+//
+// History: 3/25/2002 + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef P3DFILEHANDLER_H
+#define P3DFILEHANDLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/loadmanager.hpp>
+#include <loading/filehandler.h>
+#include <radfile.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: File handler for loading Pure3D files.
+//
+//=============================================================================
+class P3DFileHandler : public tLoadRequest::Callback,
+ public FileHandler
+
+{
+ public:
+
+ P3DFileHandler();
+ virtual ~P3DFileHandler();
+
+ //
+ // Implement FileHandler interface.
+ //
+ virtual void LoadFile( const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap );
+
+ virtual void LoadFileSync( const char* filename );
+
+ //
+ // Implement tLoadRequest::Callback interface.
+ //
+ virtual void Done( tLoadStatus status, tLoadRequest *load );
+ virtual void AddRef( void )
+ {
+ radLoadObject::AddRef();
+ }
+ virtual void Release( void )
+ {
+ radLoadObject::Release();
+ }
+
+ //
+ // Specify which P3D inventory section to load the file into.
+ //
+ void SetSectionName( const char* sectionName );
+ const char* GetSectionName() { return( mcSectionName ); }
+
+ private:
+
+ // Prevent wasteful constructor creation.
+ P3DFileHandler( const P3DFileHandler& p3dfilehandler );
+ P3DFileHandler& operator=( const P3DFileHandler& p3dfilehandler );
+
+ char mcSectionName[32];
+ void* mpUserData;
+ int m_RefCount;
+};
+
+
+#endif //P3DFILEHANDLER_H \ No newline at end of file
diff --git a/game/code/loading/pathloader.cpp b/game/code/loading/pathloader.cpp
new file mode 100644
index 0000000..fb51ee7
--- /dev/null
+++ b/game/code/loading/pathloader.cpp
@@ -0,0 +1,214 @@
+//============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pathloader.cpp
+//
+// Description: Implements PathLoader class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//============================================================================
+
+#include <p3d/chunkfile.hpp>
+#include <loading/pathloader.h>
+#include <pedpaths/pathmanager.h>
+#include <pedpaths/path.h>
+#include <pedpaths/pathsegment.h>
+#include <constants/srrchunks.h>
+
+static unsigned int sNumPathsLoaded = 0;
+static unsigned int sNumPathSegmentsLoaded = 0;
+
+PathLoader::PathLoader() :
+ tSimpleChunkHandler(SRR2::ChunkID::PED_PATH)
+{
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+
+PathLoader::~PathLoader()
+{
+}
+
+bool PathLoader::CheckChunkID(unsigned id)
+{
+ unsigned int expectedID = SRR2::ChunkID::PED_PATH;
+ return expectedID == id;
+}
+
+tLoadStatus PathLoader::Load(tChunkFile* f, tEntityStore* store)
+{
+ // parse number of points (must be at least 2 points)
+ unsigned long int nPoints = f->GetUInt();
+ rAssert( nPoints >= 2 );
+
+ int nSegments = nPoints-1;
+
+ // Get path from PathManager, who keeps a list of Paths
+ // The manager should be a singleton, so do GetInstance
+ PathManager* pm = PathManager::GetInstance();
+ rAssert( pm != NULL );
+
+ // Get a free path & set up its segments
+ Path* path = pm->GetFreePath();
+ rAssert( path != NULL );
+
+ path->AllocateSegments( nSegments );
+
+
+
+ PathSegment* ps = NULL;
+ rmt::Vector first, start, end;
+
+ start.x = f->GetFloat();
+ start.y = f->GetFloat();
+ start.z = f->GetFloat();
+
+ first = start;
+
+ float myEpsilon = 0.001f;
+
+ long int i;
+ for( i=0; i<nSegments; i++ )
+ {
+ end.x = f->GetFloat();
+ end.y = f->GetFloat();
+ end.z = f->GetFloat();
+
+ // make sure that the points span a tangible distance
+ rAssert( !rmt::Epsilon( start.x, end.x, myEpsilon ) ||
+ !rmt::Epsilon( start.y, end.y, myEpsilon ) ||
+ !rmt::Epsilon( start.z, end.z, myEpsilon ) );
+
+ ps = path->GetPathSegmentByIndex( i );
+ ps->Initialize( path, i, start, end );
+
+ // DSG stuff
+ mpListenerCB->OnChunkLoaded( ps, mUserData, SRR2::ChunkID::PED_PATH_SEGMENT );
+ start = end;
+
+ sNumPathSegmentsLoaded++;
+ }
+ /*
+ int counter = 0;
+ long int i;
+ for( i=0; i<(nSegments-1); i = i+2 )
+ {
+ f->GetFloat(); f->GetFloat(); f->GetFloat();
+ end.x = f->GetFloat();
+ end.y = f->GetFloat();
+ end.z = f->GetFloat();
+
+ // make sure that the points span a tangible distance
+ rAssert( !rmt::Epsilon( start.x, end.x, myEpsilon ) ||
+ !rmt::Epsilon( start.y, end.y, myEpsilon ) ||
+ !rmt::Epsilon( start.z, end.z, myEpsilon ) );
+
+ ps = path->GetPathSegmentByIndex( counter );
+ ps->Initialize( path, counter, start, end );
+
+ // DSG stuff
+ mpListenerCB->OnChunkLoaded( ps, mUserData, SRR2::ChunkID::PED_PATH_SEGMENT );
+ start = end;
+
+ counter++;
+ sNumPathSegmentsLoaded++;
+ }
+ */
+
+ // determine if path is closed or open
+ if( rmt::Epsilon( first.x, end.x, myEpsilon ) &&
+ rmt::Epsilon( first.y, end.y, myEpsilon ) &&
+ rmt::Epsilon( first.z, end.z, myEpsilon ) )
+ {
+ path->SetIsClosed( true );
+ }
+
+ sNumPathsLoaded++;
+
+ /*
+ rDebugPrintf( "Pathsegments Loaded = %d, paths loaded = %d, sizeof segments = %d, sizeof paths = %d\n",
+ sNumPathSegmentsLoaded,
+ sNumPathsLoaded,
+ sizeof(PathSegment) * sNumPathSegmentsLoaded,
+ sizeof(Path) * sNumPathsLoaded );
+ */
+
+ return LOAD_OK;
+}
+
+
+
+
+tEntity* PathLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ return NULL;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// RoadLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void PathLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// RoadLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void PathLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "PathLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n",
+ pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+
diff --git a/game/code/loading/pathloader.h b/game/code/loading/pathloader.h
new file mode 100644
index 0000000..fb45bee
--- /dev/null
+++ b/game/code/loading/pathloader.h
@@ -0,0 +1,70 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pathloader.h
+//
+// Description: Defines PathLoader class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#ifndef PATHLOADER_H
+#define PATHLOADER_H
+
+#include <p3d/loadmanager.hpp>
+#include <p3d/p3dtypes.hpp>
+#include <render/Loaders/IWrappedLoader.h>
+
+
+
+class PathLoader :
+ public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+//MEMBERS
+public:
+
+//METHODS
+public:
+ PathLoader();
+ virtual ~PathLoader();
+
+ // P3D chunk loader.
+ virtual tLoadStatus Load(tChunkFile* f, tEntityStore* store);
+
+ // P3D chunk id.
+ virtual bool CheckChunkID(unsigned id);
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+ ///////////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* f, tEntityStore* store);
+ ///////////////////////////////////////////////////////////////////////
+
+
+
+//MEMBERS
+private:
+
+ // prevent wasteful copy constructors
+ PathLoader( const PathLoader& );
+ PathLoader& operator= ( const PathLoader& );
+
+};
+
+// *********************************** INLINES *******************************
+
+
+
+
+#endif //PATHLOADER_H \ No newline at end of file
diff --git a/game/code/loading/roaddatasegmentloader.cpp b/game/code/loading/roaddatasegmentloader.cpp
new file mode 100644
index 0000000..e058850
--- /dev/null
+++ b/game/code/loading/roaddatasegmentloader.cpp
@@ -0,0 +1,140 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: RoadDataSegmentLoader.cpp
+//
+// Description: Implement RoadDataSegmentLoader
+//
+// History: 27/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/chunkfile.hpp>
+#include <p3d/inventory.hpp>
+#include <p3d/entity.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/RoadDataSegmentLoader.h>
+#include <constants/srrchunks.h>
+#include <roads/roadmanager.h>
+#include <roads/roadsegmentdata.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// RoadDataSegmentLoader::RoadDataSegmentLoader
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RoadDataSegmentLoader::RoadDataSegmentLoader()
+{
+}
+
+//==============================================================================
+// RoadDataSegmentLoader::~RoadDataSegmentLoader
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RoadDataSegmentLoader::~RoadDataSegmentLoader()
+{
+}
+
+//=============================================================================
+// RoadDataSegmentLoader::Load
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (tChunkFile* f, tEntityStore* store)
+//
+// Return: tLoadStatus
+//
+//=============================================================================
+tLoadStatus RoadDataSegmentLoader::Load(tChunkFile* f, tEntityStore* store)
+{
+ char name[256];
+ f->GetString( name );
+ unsigned int type = f->GetUInt();
+ unsigned int numLanes = f->GetUInt();
+ bool hasShoulder = ( f->GetUInt() ? true : false );
+
+ rmt::Vector direction;
+ direction.x = f->GetFloat();
+ direction.y = f->GetFloat();
+ direction.z = f->GetFloat();
+
+ rmt::Vector top;
+ top.x = f->GetFloat();
+ top.y = f->GetFloat();
+ top.z = f->GetFloat();
+
+ rmt::Vector bottom;
+ bottom.x = f->GetFloat();
+ bottom.y = f->GetFloat();
+ bottom.z = f->GetFloat();
+
+ RoadManager* rm = RoadManager::GetInstance();
+
+ RoadSegmentData* rsd = rm->GetFreeRoadSegmentDataMemory();
+ rAssert( rsd );
+
+ rsd->SetName( name );
+ rsd->SetData( direction, top, bottom, numLanes );
+
+ rm->AddRoadSegmentData( rsd );
+
+ return LOAD_OK;
+}
+
+//=============================================================================
+// RoadDataSegmentLoader::CheckChunkID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (unsigned id)
+//
+// Return: bool
+//
+//=============================================================================
+bool RoadDataSegmentLoader::CheckChunkID(unsigned id)
+{
+ return ( SRR2::ChunkID::ROAD_SEGMENT_DATA == id );
+}
+
+unsigned int RoadDataSegmentLoader::GetChunkID()
+{
+ return SRR2::ChunkID::ROAD_SEGMENT_DATA;
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/loading/roaddatasegmentloader.h b/game/code/loading/roaddatasegmentloader.h
new file mode 100644
index 0000000..7da74ba
--- /dev/null
+++ b/game/code/loading/roaddatasegmentloader.h
@@ -0,0 +1,53 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: roaddatasegmentloader.h
+//
+// Description: Blahblahblah
+//
+// History: 27/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef ROADDATASEGMENTLOADER_H
+#define ROADDATASEGMENTLOADER_H
+
+//========================================
+// Nested Includes
+//========================================
+// Pure 3D
+#include <p3d/loadmanager.hpp>
+#include <p3d/p3dtypes.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class RoadDataSegmentLoader : public tChunkHandler
+{
+public:
+ RoadDataSegmentLoader();
+ virtual ~RoadDataSegmentLoader();
+
+ // P3D chunk loader.
+ virtual tLoadStatus Load(tChunkFile* f, tEntityStore* store);
+
+ // P3D chunk id.
+ virtual bool CheckChunkID(unsigned id);
+ virtual unsigned int GetChunkID();
+
+private:
+
+ //Prevent wasteful constructor creation.
+ RoadDataSegmentLoader( const RoadDataSegmentLoader& roaddatasegmentloader );
+ RoadDataSegmentLoader& operator=( const RoadDataSegmentLoader& roaddatasegmentloader );
+};
+
+
+#endif //ROADDATASEGMENTLOADER_H
diff --git a/game/code/loading/roadloader.cpp b/game/code/loading/roadloader.cpp
new file mode 100644
index 0000000..b600cd0
--- /dev/null
+++ b/game/code/loading/roadloader.cpp
@@ -0,0 +1,544 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: roadloader.cpp
+//
+// Description: Implement RoadLoader
+//
+// History: 15/03/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/chunkfile.hpp>
+#include <p3d/inventory.hpp>
+#include <p3d/entity.hpp>
+#include <radmath/radmath.hpp>
+
+
+#ifndef TOOLS
+#include <memory/srrmemory.h>
+#else
+#define MEMTRACK_PUSH_GROUP(x)
+#define MEMTRACK_POP_GROUP()
+#endif
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/roadloader.h>
+#include <roads/roadmanager.h>
+#include <roads/intersection.h>
+#include <roads/roadsegmentdata.h>
+#include <roads/roadsegment.h>
+#include <roads/road.h>
+
+#include <constants/srrchunks.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+static unsigned int sNumRoadsLoaded = 0;
+static unsigned int sNumRoadSegmentsLoaded = 0;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// RoadLoader::RoadLoader
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RoadLoader::RoadLoader() :
+tSimpleChunkHandler(SRR2::ChunkID::ROAD)
+{
+ mpListenerCB = NULL;
+ mUserData = -1;
+
+}
+
+//==============================================================================
+// RoadLoader::~RoadLoader
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RoadLoader::~RoadLoader()
+{
+}
+
+//==============================================================================
+// RoadLoader::Load
+//==============================================================================
+// Description: Comment
+//
+// Parameters: (tChunkFile* f, tEntityStore* store)
+//
+// Return: tLoadStatus
+//
+//==============================================================================
+tLoadStatus RoadLoader::Load(tChunkFile* f, tEntityStore* store)
+{
+MEMTRACK_PUSH_GROUP( "Road Loading" );
+
+ char name[256];
+ f->GetPString( name );
+ unsigned int type = f->GetUInt();
+
+ RoadManager* rm = RoadManager::GetInstance();
+ rAssert( rm );
+
+ char start[256];
+ f->GetString( start );
+ Intersection* startIntersection = rm->FindIntersection( tEntity::MakeUID( start ) );
+ rAssert( startIntersection );
+
+ char end[256];
+ f->GetString( end );
+ Intersection* endIntersection = rm->FindIntersection( tEntity::MakeUID( end ) );
+ rAssert( endIntersection );
+
+ unsigned int density = f->GetUInt();
+
+ unsigned int speed = f->GetUInt();
+
+
+ RoadList segments;
+
+ bool firstSeg = true;
+ unsigned int numLanes = 0;
+
+ if ( !f->ChunksRemaining() )
+ {
+ char error[256];
+ sprintf( error, "The Road: %s has no segments!\n", name );
+ rDebugString( error );
+MEMTRACK_POP_GROUP( "Road Loading" );
+ return LOAD_OK;
+ }
+
+ unsigned int segCount = 0;
+ while ( f->ChunksRemaining() )
+ {
+ f->BeginChunk();
+
+ unsigned int tmpNumLanes = 0;
+ RoadSegment* segment = LoadRoadSegment( f, tmpNumLanes );
+
+ if( firstSeg )
+ {
+ numLanes = tmpNumLanes;
+ }
+ else
+ {
+ rAssert( numLanes == tmpNumLanes ); //Should have the same number of lanes.
+ }
+
+ segments.push_back( segment );
+ ++segCount;
+
+ f->EndChunk();
+ }
+
+ Road* road = rm->GetFreeRoadMemory();
+ rAssert( road );
+
+#ifndef RAD_RELEASE
+ road->SetName( name );
+#endif
+
+ rm->AddRoad( road );
+
+ road->SetDestinationIntersection( endIntersection );
+ road->SetSourceIntersection( startIntersection );
+ road->SetNumLanes( numLanes );
+
+ //This works differently now.
+ //8 bits - speed
+ //8 bits - difficulty level
+ //1 bit - id Short cut
+ //15 bits - saved for later
+ const int SPEED_MASK = 0x000000FF;
+ const int DIFFIC_MASK = 0x0000FF00;
+ const int SC_MASK = 0x00010000;
+
+ road->SetSpeed( speed & SPEED_MASK );
+ road->SetDifficulty( (speed & DIFFIC_MASK) >> 8 );
+ road->SetShortCut( (speed & SC_MASK) ? true : false );
+ road->SetDensity( density );
+
+ startIntersection->AddRoadOut( road );
+ endIntersection->AddRoadIn( road );
+
+ road->AllocateSegments( segments.size() );
+
+
+ //Now we sort the road segments to make sure they are in order from the
+ //starting intersection...
+
+
+ RoadList::iterator i;
+
+ //Let's sort these.
+ float dist = 10000000000.0f;
+
+ RoadSegment* closest = NULL;
+ rmt::Vector startPoint;
+ startIntersection->GetLocation( startPoint );
+
+ for( i = segments.begin(); i != segments.end(); i++ )
+ {
+ RoadSegment* segment = (*i);
+
+ rmt::Vector origin;
+ segment->GetCorner( 0, origin );
+
+ origin.Sub( startPoint );
+ float segToInt = origin.MagnitudeSqr();
+
+ if ( segToInt < dist )
+ {
+ closest = segment;
+ dist = segToInt;
+ }
+ }
+
+ unsigned int numAllocated = 0;
+ RoadList allocatedSegments;
+
+ allocatedSegments.push_back( closest );
+ ++numAllocated;
+
+ //Now that we have the closest, let's find them in order....
+
+ RoadSegment* current = closest;
+
+ while ( allocatedSegments.size() < segments.size() )
+ {
+ bool found = false;
+
+ for ( i = segments.begin(); i != segments.end(); i++ )
+ {
+ RoadSegment* seg = *i;
+
+ rmt::Vector currentTrailingLeft;
+ rmt::Vector segOrigin;
+
+ current->GetCorner( 1, currentTrailingLeft );
+ seg->GetCorner( 0, segOrigin );
+
+ if ( rmt::Epsilon( currentTrailingLeft.x, segOrigin.x, 0.5f ) &&
+ rmt::Epsilon( currentTrailingLeft.y, segOrigin.y, 0.5f ) &&
+ rmt::Epsilon( currentTrailingLeft.z, segOrigin.z, 0.5f ) )
+ {
+ //This is the next segment.
+ current = seg;
+ allocatedSegments.push_back( seg );
+ ++numAllocated;
+
+ found = true;
+
+ break;
+ }
+ }
+
+
+ if ( !found )
+ {
+ bool error = true;
+ //This could be the case where the intersection is of the shape where the origin is further away than the next segments.
+ //If there is only on segment missing, then test this theory out.
+ if ( segCount - numAllocated == 1 )
+ {
+ //This is the strange case of the missing first segment.
+ //Find the missing segment and try to match it to the first element in the allocated segs list.
+ //if they match then add the found one to the start of the list.
+ for ( i = segments.begin(); i != segments.end(); i++ )
+ {
+ bool notTheMissingOne = false;
+ RoadList::iterator j;
+ for ( j = allocatedSegments.begin(); j != allocatedSegments.end(); j++ )
+ {
+ if ( (*i) == (*j) )
+ {
+ notTheMissingOne = true;
+ break;
+ }
+ }
+
+ if ( !notTheMissingOne )
+ {
+ //AHA!
+ //segment i is not allocated!
+ //Test it's trailing left against the first one's origin.
+ rmt::Vector trailingLeft;
+ rmt::Vector origin;
+
+ (*i)->GetCorner( 1, trailingLeft );
+ (*(allocatedSegments.begin()))->GetCorner( 0, origin );
+
+ if ( rmt::Epsilon( trailingLeft.x, origin.x, 0.5f ) &&
+ rmt::Epsilon( trailingLeft.y, origin.y, 0.5f ) &&
+ rmt::Epsilon( trailingLeft.z, origin.z, 0.5f ) )
+ {
+ //This is the next segment.
+ allocatedSegments.push_front( (*i) );
+ ++numAllocated;
+ error = false;
+ }
+ }
+ }
+ }
+
+ if ( error )
+ {
+ #ifndef RAD_RELEASE
+ char message[500];
+ sprintf( message,
+ "\b ERROR! The road segment \"%s\" is not touching any others...\n"
+ " Found: %d out of %d\n"
+ " Possibly due to duplicated segments\n"
+ " Please note the errors above and inform Cary/Dusit/Sheik\n"
+ " These errors are fatal! Skipping will result in crash\n"
+ " and/or lousy AI behaviour.\n",
+ current->GetName(),
+ numAllocated,
+ segCount );
+ rTuneAssertMsg( false, message );
+ #endif
+ }
+
+ break;
+ }
+ }
+
+ float roadLen = 0.0f;
+ for ( i = allocatedSegments.begin(); i != allocatedSegments.end(); i++ )
+ {
+ //
+ // Tell the segment which road it belongs to as well as tell the road
+ // it contains the segment.
+ //
+ RoadSegment* seg = *i;
+ seg->SetRoad( road );
+ road->AddRoadSegment( (*i) );
+
+ float segLen = seg->GetSegmentLength();
+ roadLen += segLen;
+
+#ifdef TOOLS
+ store->Store( seg );
+ seg->AddRef();
+#endif
+ }
+
+ rAssert( roadLen >= 0.0f );
+ road->SetRoadLength( roadLen );
+ road->CreateLanes();
+
+MEMTRACK_POP_GROUP( "Road Loading" );
+
+#ifdef TOOLS
+ road->SetName( name );
+ store->Store( road );
+ //HACK
+ road->AddRef();
+#endif
+
+ sNumRoadsLoaded++;
+
+ /*
+ rDebugPrintf( "*** RoadSegments loaded = %d, Roads loaded = %d, sizeof segments = %d, sizeof roads = %d\n",
+ sNumRoadSegmentsLoaded,
+ sNumRoadsLoaded,
+ sizeof(RoadSegment) * sNumRoadSegmentsLoaded,
+ sizeof(Road) * sNumRoadsLoaded );
+ */
+
+ return LOAD_OK;
+}
+
+//==============================================================================
+// RoadLoader::CheckChunkID
+//==============================================================================
+// Description: Comment
+//
+// Parameters: (unsigned id)
+//
+// Return: bool
+//
+//==============================================================================
+bool RoadLoader::CheckChunkID(unsigned id)
+{
+ return SRR2::ChunkID::ROAD == id;
+}
+
+
+
+///////////////////////////////////////////////////////////////////////
+// tSimpleChunkHandler
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// RoadLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* RoadLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ return NULL;
+}
+
+
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// RoadLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RoadLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// RoadLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RoadLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "RoadLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n",
+ pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+RoadSegment* RoadLoader::LoadRoadSegment( tChunkFile* f, unsigned int& numLanes )
+{
+MEMTRACK_PUSH_GROUP( "RoadSeg Loader" );
+
+ rAssert( f->GetCurrentID() == SRR2::ChunkID::ROAD_SEGMENT );
+
+ char name[256];
+ f->GetString( name );
+
+ char segDataName[256];
+ f->GetString( segDataName );
+
+ // grab the matrix
+ rmt::Matrix hierarchy;
+ f->GetData( &hierarchy, 16, tFile::DWORD );
+
+ // grab the scale along facing
+ rmt::Matrix scale;
+ f->GetData( &scale, 16, tFile::DWORD );
+ rmt::Vector z( 0,0,1.0f );
+ z.Transform( scale );
+ float scaleAlongFacing = z.z;
+
+
+ RoadManager* rm = RoadManager::GetInstance();
+
+ RoadSegmentData* rsd = rm->FindRoadSegmentData( segDataName );
+ rAssert( rsd );
+
+ RoadSegment* rs = rm->GetFreeRoadSegmentMemory();
+ rAssert( rs );
+
+ rs->SetName( name );
+ rs->Init( rsd, hierarchy, scaleAlongFacing );
+
+ // increment count in road manager
+ rm->AddRoadSegment( rs );
+
+ numLanes = rsd->GetNumLanes();
+
+MEMTRACK_POP_GROUP( "RoadSeg Loader" );
+
+ // DSG stuff
+#ifndef TOOLS
+ mpListenerCB->OnChunkLoaded( rs, mUserData, SRR2::ChunkID::ROAD_SEGMENT );
+#endif
+
+ sNumRoadSegmentsLoaded++;
+
+ return rs;
+}
+
diff --git a/game/code/loading/roadloader.h b/game/code/loading/roadloader.h
new file mode 100644
index 0000000..9b0b711
--- /dev/null
+++ b/game/code/loading/roadloader.h
@@ -0,0 +1,87 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: roadloader.h
+//
+// Description: This is the P3D Loader class for Road chunks.
+//
+// History: 15/03/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef ROADLOADER_H
+#define ROADLOADER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/loadmanager.hpp>
+#include <p3d/p3dtypes.hpp>
+#include <render/Loaders/IWrappedLoader.h>
+
+#include <list>
+#include <memory/stlallocators.h>
+
+
+//========================================
+// Forward References
+//========================================
+
+class RoadSegment;
+
+//=============================================================================
+//
+// Synopsis: This is the P3D Loader class for Road chunks.
+//
+//=============================================================================
+
+
+class RoadLoader :
+ public tSimpleChunkHandler,
+ public IWrappedLoader
+
+{
+public:
+ RoadLoader();
+ virtual ~RoadLoader();
+
+#ifndef TOOLS
+ typedef std::list< RoadSegment*, s2alloc<RoadSegment*> > RoadList;
+#else
+ typedef std::list< RoadSegment*> RoadList;
+#endif
+
+ // P3D chunk loader.
+ virtual tLoadStatus Load(tChunkFile* f, tEntityStore* store);
+
+ // P3D chunk id.
+ virtual bool CheckChunkID(unsigned id);
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+
+
+private:
+
+ RoadSegment* LoadRoadSegment( tChunkFile* f, unsigned int& numLanes );
+
+ // No copying or assignment. Declare but don't define.
+ //
+ RoadLoader( const RoadLoader& );
+ RoadLoader& operator= ( const RoadLoader& );
+};
+
+#endif //ROADLOADER_H \ No newline at end of file
diff --git a/game/code/loading/scroobyfilehandler.cpp b/game/code/loading/scroobyfilehandler.cpp
new file mode 100644
index 0000000..49907a9
--- /dev/null
+++ b/game/code/loading/scroobyfilehandler.cpp
@@ -0,0 +1,172 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ScroobyFileHandler.cpp
+//
+// Description: Implement ConsoleFileHandler
+//
+// History: 07/19/2002 + Created -- Tony Chu
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radtime.hpp>
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/scroobyfilehandler.h>
+#include <presentation/gui/guisystem.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ScroobyFileHandler::ScroobyFileHandler
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ScroobyFileHandler::ScroobyFileHandler()
+{
+ mpCallback = NULL;
+ mpUserData = NULL;
+ mcSectionName[ 0 ] = '\0';
+}
+
+//==============================================================================
+// ScroobyFileHandler::~ScroobyFileHandler
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ScroobyFileHandler::~ScroobyFileHandler()
+{
+}
+
+
+//==============================================================================
+// ScroobyFileHandler::LoadFile
+//==============================================================================
+//
+// Description: Load a Pure3D file asynchronously.
+//
+// Parameters: filename - fully qualified path and filename
+// pCallback - client callback to invoke when load is complete
+// pUserData - optional client supplied user data
+//
+// Return: None.
+//
+//==============================================================================
+void ScroobyFileHandler::LoadFile
+(
+ const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap
+)
+{
+ rAssert( filename );
+ rAssert( pCallback );
+
+ mpCallback = pCallback;
+ mpUserData = pUserData;
+
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ // load scrooby project
+ Scrooby::App::GetInstance()->LoadProject( filename, this, mcSectionName, heap );
+
+ HeapMgr()->PopHeap (GMA_PERSISTENT);
+}
+
+
+//==============================================================================
+// ScroobyFileHandler::OnProjectLoadComplete
+//==============================================================================
+//
+// Description: Scrooby invokes this callback when the async load
+// is complete.
+//
+// Parameters: pUserData - optional client supplied user data
+//
+// Return: None.
+//
+//==============================================================================
+void ScroobyFileHandler::OnProjectLoadComplete( Scrooby::Project* pProject )
+{
+ rAssert( mpCallback );
+
+ // notify GUI system that the project is loaded, and pass reference to
+ // project using the first message parameter
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_PROJECT_LOAD_COMPLETE, (unsigned int)pProject );
+
+ // notify client that async loading is completed
+ mpCallback->OnLoadFileComplete( mpUserData );
+}
+
+
+//==============================================================================
+// ScroobyFileHandler::LoadFileSync
+//==============================================================================
+//
+// Description: Load a Pure3D file synchronously.
+//
+// Parameters: filename - fully qualified path and filename
+//
+// Return: None.
+//
+//==============================================================================
+void ScroobyFileHandler::LoadFileSync( const char* filename )
+{
+ rAssertMsg( 0, "ERROR: Synchronous loading not supported by Scrooby!\n" );
+}
+
+//==============================================================================
+// ScroobyFileHandler::SetSectionName
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void ScroobyFileHandler::SetSectionName( const char* sectionName )
+{
+ rAssert( sectionName );
+
+ strcpy( mcSectionName, sectionName );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
diff --git a/game/code/loading/scroobyfilehandler.h b/game/code/loading/scroobyfilehandler.h
new file mode 100644
index 0000000..1997fef
--- /dev/null
+++ b/game/code/loading/scroobyfilehandler.h
@@ -0,0 +1,70 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ScroobyFileHandler.h
+//
+// Description: Declaration of ConsoleFileHandler class.
+//
+// History: 07/19/2002 + Created -- Tony Chu
+//
+//=============================================================================
+
+#ifndef SCROOBYFILEHANDLER_H
+#define SCROOBYFILEHANDLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <loading/filehandler.h>
+#include <app.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: File handler for loading Scrooby project files.
+//
+//=============================================================================
+class ScroobyFileHandler : public FileHandler,
+ public Scrooby::LoadProjectCallback
+{
+ public:
+
+ ScroobyFileHandler();
+ virtual ~ScroobyFileHandler();
+
+ //
+ // Implement FileHandler interface.
+ //
+ virtual void LoadFile( const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap );
+
+ virtual void LoadFileSync( const char* filename );
+
+ //
+ // Implements Scrooby::LoadProjectCallback interface.
+ //
+ virtual void OnProjectLoadComplete( Scrooby::Project* pProject );
+
+ //
+ // Specify which Scrooby inventory section to load the project.
+ //
+ void SetSectionName( const char* sectionName );
+ const char* GetSectionName() { return( mcSectionName ); }
+
+ private:
+
+ // Prevent wasteful constructor creation.
+ ScroobyFileHandler( const ScroobyFileHandler& scroobyFileHandler );
+ ScroobyFileHandler& operator=( const ScroobyFileHandler& scroobyFileHandler );
+
+ char mcSectionName[ 32 ];
+
+};
+
+
+#endif // SCROOBYFILEHANDLER_H
diff --git a/game/code/loading/soundfilehandler.cpp b/game/code/loading/soundfilehandler.cpp
new file mode 100644
index 0000000..74b2110
--- /dev/null
+++ b/game/code/loading/soundfilehandler.cpp
@@ -0,0 +1,132 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundfilehandler.cpp
+//
+// Description: Implement SoundFileHandler, which represents sound in the
+// loading queue
+//
+// History: 19/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <loading/soundfilehandler.h>
+
+#include <sound/soundmanager.h>
+#include <memory/srrmemory.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundFileHandler::SoundFileHandler
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundFileHandler::SoundFileHandler()
+{
+}
+
+//==============================================================================
+// SoundFileHandler::~SoundFileHandler
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundFileHandler::~SoundFileHandler()
+{
+}
+
+//=============================================================================
+// SoundFileHandler::LoadFile
+//=============================================================================
+// Description: Load sound file asynchronously
+//
+// Parameters: filename - name of file to load
+// pCallback - callback to invoke when loading complete
+// pUserData - user data, unused
+//
+// Return: void
+//
+//=============================================================================
+void SoundFileHandler::LoadFile( const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap )
+{
+ mpCallback = pCallback;
+ mpUserData = pUserData;
+
+ //
+ // Pass the load request on to the sound system, giving it this object
+ // for notification of completion
+ //
+ GetSoundManager()->LoadSoundFile( filename, this );
+}
+
+//=============================================================================
+// SoundFileHandler::LoadFileSync
+//=============================================================================
+// Description: Load sound file synchronously
+//
+// Parameters: filename - name of file to load
+//
+// Return: void
+//
+//=============================================================================
+void SoundFileHandler::LoadFileSync( const char* filename )
+{
+ //
+ // This shouldn't get called. We don't do synchronous in sound.
+ //
+ rAssert( false );
+}
+
+//=============================================================================
+// SoundFileHandler::LoadCompleted
+//=============================================================================
+// Description: Inform loading manager when asynchronous load completed
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundFileHandler::LoadCompleted()
+{
+ rAssert( mpCallback != NULL );
+ mpCallback->OnLoadFileComplete( mpUserData );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/loading/soundfilehandler.h b/game/code/loading/soundfilehandler.h
new file mode 100644
index 0000000..1ea661a
--- /dev/null
+++ b/game/code/loading/soundfilehandler.h
@@ -0,0 +1,66 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundfilehandler.h
+//
+// Description: Declaration for sound file loader class
+//
+// History: 19/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDFILEHANDLER_H
+#define SOUNDFILEHANDLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <loading/filehandler.h>
+
+//========================================
+// Forward References
+//========================================
+
+class SoundAsyncFileLoader;
+
+//=============================================================================
+//
+// Synopsis: SoundFileHandler
+//
+//=============================================================================
+
+class SoundFileHandler : public FileHandler
+{
+ public:
+ SoundFileHandler();
+ virtual ~SoundFileHandler();
+
+ //
+ // Load file asynchronously.
+ //
+ void LoadFile( const char* filename,
+ FileHandler::LoadFileCallback* pCallback,
+ void* pUserData,
+ GameMemoryAllocator heap );
+
+ //
+ // Load file synchronously.
+ //
+ void LoadFileSync( const char* filename );
+
+ //
+ // Called by sound system on load completion
+ //
+ void LoadCompleted();
+
+ private:
+ //Prevent wasteful constructor creation.
+ SoundFileHandler( const SoundFileHandler& original );
+ SoundFileHandler& operator=( const SoundFileHandler& rhs );
+
+ SoundAsyncFileLoader* m_subtypeFileLoader;
+};
+
+
+#endif // SOUNDFILEHANDLER_H
+
diff --git a/game/code/main/allgcmain.cpp b/game/code/main/allgcmain.cpp
new file mode 100644
index 0000000..ad8c143
--- /dev/null
+++ b/game/code/main/allgcmain.cpp
@@ -0,0 +1,10 @@
+#include <main/commandlineoptions.cpp>
+#include <main/game.cpp>
+#include <main/gcmain.cpp>
+#include <main/gcplatform.cpp>
+#include <main/singletons.cpp>
+#include <main/gamecube_extras/gcmanager.cpp>
+#ifndef RAD_RELEASE
+#include <main/gamecube_extras/screenshot.c>
+#endif
+#include <main/tuidunaligned.cpp>
diff --git a/game/code/main/allps2main.cpp b/game/code/main/allps2main.cpp
new file mode 100644
index 0000000..9d76b0d
--- /dev/null
+++ b/game/code/main/allps2main.cpp
@@ -0,0 +1,6 @@
+#include <main/commandlineoptions.cpp>
+#include <main/game.cpp>
+#include <main/ps2main.cpp>
+#include <main/ps2platform.cpp>
+#include <main/singletons.cpp>
+#include <main/tuidunaligned.cpp>
diff --git a/game/code/main/commandlineoptions.cpp b/game/code/main/commandlineoptions.cpp
new file mode 100644
index 0000000..58b9676
--- /dev/null
+++ b/game/code/main/commandlineoptions.cpp
@@ -0,0 +1,403 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: commandlineoptions.cpp
+//
+// Description: CommandLineOptions class implementation.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Standard C Library
+#include <string.h>
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <main/commandlineoptions.h>
+#include <memory/srrmemory.h>
+
+//#ifdef RAD_GAMECUBE
+//#define strupr(x) (x)
+//#pragma message("strupr not defined on the game cube")
+//#endif
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Bit flag for options.
+//
+#ifdef NEALL_DEMOTEST
+simpsonsUInt64 CommandLineOptions::sOptions = ( 1 << CLO_DEMO_TEST | 1 << CLO_NO_TRAFFIC );
+#else
+simpsonsUInt64 CommandLineOptions::sOptions = 0;
+#endif
+
+short CommandLineOptions::s_defaultLevel = 0;
+short CommandLineOptions::s_defaultMission = 0;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// CommandLineOptions::InitDefaults
+//==============================================================================
+//
+// Description: Initialize default command-line options.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void CommandLineOptions::InitDefaults()
+{
+ simpsonsUInt64 orValue = 1;
+
+#ifdef RAD_RELEASE
+ // enable CLO_CD_FILES_ONLY in release builds for all platforms
+ //
+ orValue = 1;
+ orValue <<= CLO_CD_FILES_ONLY;
+ sOptions |= orValue;
+#endif
+
+#if defined( RAD_GC ) || defined( RAD_PS2 )
+ // enable CLO_NO_HEAPS for GC and PS2 only
+ //
+ orValue = 1;
+ orValue <<= CLO_NO_HEAPS;
+ sOptions |= orValue;
+#endif
+}
+
+//==============================================================================
+// CommandLineOptions::HandleOption
+//==============================================================================
+//
+// Description: Interpret the command line string token.
+//
+// Parameters: option - command line string token.
+//
+// Return: None.
+//
+//==============================================================================
+void CommandLineOptions::HandleOption( const char* const optionIn )
+{
+ simpsonsUInt64 andValue;
+ simpsonsUInt64 orValue = 1;
+ bool optionFound = true;
+
+ rReleaseAssertMsg( NUM_CLO < (sizeof( sOptions ) * 8),
+ "*** WARNING: Too many commandline options!" );
+
+ char option[ 256 ];
+ strcpy( option, optionIn );
+ rReleasePrintf( "Commandline Option: %s\n", option );
+
+ if( strcmp( strupr(option), "NOMUSIC" ) == 0 )
+ {
+ orValue <<= CLO_NO_MUSIC;
+ }
+ else if( strcmp( strupr(option), "NOEFFECTS" ) == 0 )
+ {
+ orValue <<= CLO_NO_EFFECTS;
+ }
+ else if( strcmp( strupr(option), "NODIALOG" ) == 0 )
+ {
+ orValue <<= CLO_NO_DIALOG;
+ }
+ else if( strcmp( strupr(option), "MUTE" ) == 0 )
+ {
+ orValue <<= CLO_MUTE;
+ }
+ else if( strcmp( strupr(option), "SKIPMOVIE" ) == 0 )
+ {
+ orValue <<= CLO_SKIP_MOVIE;
+ }
+ else if( strcmp( strupr(option), "MEMMONITOR" ) == 0 )
+ {
+ orValue <<= CLO_MEMORY_MONITOR;
+ }
+ else if( strcmp( strupr(option), "HEAPSTATS" ) == 0 )
+ {
+ orValue <<= CLO_HEAP_STATS;
+ }
+ else if( strcmp( strupr(option), "CDFILES" ) == 0 )
+ {
+ orValue <<= CLO_CD_FILES_ONLY;
+ }
+ else if( strcmp( strupr(option), "HOSTFILES" ) == 0 )
+ {
+ //
+ // Careful to avoid the PS2-hated 64-bit constant
+ //
+ andValue = 1;
+ andValue <<= CLO_CD_FILES_ONLY;
+ andValue = ~andValue;
+
+ sOptions = sOptions & andValue;
+
+ //
+ // Skip the or operation below
+ //
+ optionFound = false;
+ }
+ else if( strcmp( strupr(option), "FIREWIRE" ) == 0 )
+ {
+ orValue <<= CLO_FIREWIRE;
+ }
+ else if( strcmp( strupr(option), "SNPROFILER" ) == 0 )
+ {
+ orValue <<= CLO_SN_PROFILER;
+ }
+ else if( strcmp( strupr(option), "ARTSTATS" ) == 0 )
+ {
+ orValue <<= CLO_ART_STATS;
+ }
+ else if( strcmp( strupr(option), "PROPSTATS" ) == 0 )
+ {
+ orValue <<= CLO_PROP_STATS;
+ }
+ else if( strcmp( strupr(option), "FEUNJOINED" ) == 0 )
+ {
+ orValue <<= CLO_FE_UNJOINED;
+ }
+ else if( strcmp( strupr(option), "SPEEDOMETER" ) == 0 )
+ {
+ orValue <<= CLO_SHOW_SPEED;
+ }
+ else if( strcmp( strupr(option), "NOHUD" ) == 0 )
+ {
+ orValue <<= CLO_NO_HUD;
+ }
+ else if( strcmp( strupr(option), "DEBUGBV" ) == 0 )
+ {
+ orValue <<= CLO_DEBUGBV;
+ }
+ else if( strcmp( strupr(option), "NOTRAFFIC" ) == 0 )
+ {
+ orValue <<= CLO_NO_TRAFFIC;
+ }
+ else if( strcmp( strupr(option), "SKIPSUNDAY" ) == 0 )
+ {
+ orValue <<= CLO_SKIP_SUNDAY;
+ }
+ else if( strcmp( strupr(option), "SKIPFE" ) == 0 )
+ {
+ orValue <<= CLO_SKIP_FE;
+ }
+ else if( strcmp( strupr(option), "FEGAGS" ) == 0 )
+ {
+ orValue <<= CLO_FE_GAGS_TEST;
+ }
+ else if( strcmp( strupr(option), "FPS" ) == 0 )
+ {
+ orValue <<= CLO_FPS;
+ }
+ else if( strcmp( strupr(option), "DESIGNER" ) == 0 )
+ {
+ orValue <<= CLO_DESIGNER;
+ }
+ else if( strcmp( strupr(option), "DETECTLEAKS" ) == 0 )
+ {
+ orValue <<= CLO_DETECT_LEAKS;
+ }
+ else if( strcmp( strupr(option), "NOHEAPS" ) == 0 )
+ {
+ orValue <<= CLO_NO_HEAPS;
+ }
+ else if( strcmp( strupr(option), "PRINTMEMORY" ) == 0 )
+ {
+ orValue <<= CLO_PRINT_MEMORY;
+ }
+ else if( strcmp( strupr(option), "DEMOTEST" ) == 0 )
+ {
+ orValue <<= CLO_DEMO_TEST;
+ }
+ else if( strcmp( strupr(option), "NOSPLASH" ) == 0 )
+ {
+ orValue <<= CLO_NO_SPLASH;
+ }
+ else if( strcmp( strupr(option), "LANGUAGE" ) == 0 )
+ {
+ orValue <<= CLO_LANG_PROMPT;
+ }
+ else if( strcmp( strupr(option), "SKIPMEMCHECK" ) == 0 )
+ {
+ orValue <<= CLO_SKIP_MEMCHECK;
+ }
+ else if( strcmp( strupr(option), "NOHAPTIC" ) == 0 )
+ {
+ orValue <<= CLO_NO_HAPTIC;
+ }
+ else if( strcmp( strupr(option), "RANDOMBUTTONS" ) == 0 )
+ {
+ orValue <<= CLO_RANDOM_BUTTONS;
+ }
+ else if( strcmp( strupr(option), "SEQUENTIALDEMO" ) == 0 )
+ {
+ orValue <<= CLO_SEQUENTIAL_DEMO;
+ }
+ else if ( strcmp( strupr(option), "PCTEST" ) == 0 )
+ {
+ orValue <<= CLO_PARKED_CAR_TEST;
+ }
+ else if ( strcmp( strupr(option), "NOAVRIL" ) == 0 )
+ {
+ orValue <<= CLO_NO_AVRIL;
+ }
+ else if ( strcmp( strupr(option), "SHORTDEMO" ) == 0 )
+ {
+ orValue <<= CLO_SHORT_DEMO;
+ }
+ else if( strcmp( strupr( option), "PRINTLOADTIME" ) == 0 )
+ {
+ orValue <<= CLO_PRINT_LOAD_TIME;
+ }
+ else if( strcmp( strupr( option), "PRINTFRAMERATE" ) == 0 )
+ {
+ orValue <<= CLO_PRINT_FRAMERATE;
+ }
+ else if ( strcmp( strupr( option ), "SHOWDYNALOAD" ) == 0 )
+ {
+ orValue <<= CLO_SHOW_DYNA_ZONES;
+ }
+ else if ( strcmp( strupr( option ), "NOPEDS" ) == 0 )
+ {
+ orValue <<= CLO_NO_PEDS;
+ }
+ else if ( strcmp( strupr( option ), "MANUALRESETDAMAGE" ) == 0 )
+ {
+ orValue <<= CLO_MANUAL_RESET_DAMAGE;
+ }
+ else if ( strcmp( strupr( option ), "WINDOW" ) == 0 )
+ {
+ orValue <<= CLO_WINDOW_MODE;
+ }
+ else if ( strcmp( strupr( option ), "NOTUTORIAL" ) == 0 )
+ {
+ orValue <<= CLO_NO_TUTORIAL;
+ }
+ else if ( strcmp( strupr( option ), "COINS" ) == 0 )
+ {
+ orValue <<= CLO_COINS;
+ }
+ else if ( strcmp( strupr( option ), "PROGSCAN" ) == 0 )
+ {
+ orValue <<= CLO_PROGRESSIVE_SCAN;
+ }
+ else if ( strcmp( strupr( option ), "LARGEHEAPS" ) == 0 )
+ {
+ orValue <<= CLO_LARGEHEAPS;
+ }
+ else if ( strcmp( strupr( option ), "MEMCARDCHEAT" ) == 0 )
+ {
+ orValue <<= CLO_MEMCARD_CHEAT;
+ }
+ else if ( strcmp( strupr( option ), "TOOL" ) == 0 )
+ {
+ orValue <<= CLO_PS2_TOOL;
+ }
+ else if ( strcmp( strupr( option ), "FILENOTFOUND" ) == 0 )
+ {
+ orValue <<= CLO_FILE_NOT_FOUND;
+ }
+ else if ( strcmp( strupr( option ), "NOLOADINGSPEW" ) == 0 )
+ {
+ orValue <<= CLO_NO_LOADING_SPEW;
+ }
+ else if ( strcmp( strupr( option ), "AUDIOSPEW" ) == 0 )
+ {
+ orValue <<= CLO_AUDIO_LOADING_SPEW;
+ }
+ else if ( strcmp( strupr( option ), "RELEASEPRINT" ) == 0 )
+ {
+ extern bool g_AllowDebugOutput;
+ g_AllowDebugOutput = true;
+ optionFound = false;
+ }
+ else if ( strcmp( strupr( option ), "NOFRUITLESS" ) == 0 )
+ {
+ extern bool gFruitless;
+ gFruitless = false;
+ optionFound = false;
+ }
+ else if ( strcmp( strupr( option ), "RADTUNER" ) == 0 )
+ {
+ extern bool gTuneSound;
+ gTuneSound = true;
+ optionFound = false;
+ }
+
+ else
+ {
+ // special case for "l<N>" and "m<N>" commandline options
+ //
+ optionFound = false;
+ int stringLength = strlen( option );
+ char lastChar = option[ stringLength - 1 ];
+ option[ stringLength - 1 ] = '\0';
+
+ if( strcmp( strupr(option), "L" ) == 0 )
+ {
+ s_defaultLevel = lastChar - '1';
+ }
+ else if( strcmp( strupr(option), "M" ) == 0 )
+ {
+ s_defaultMission = lastChar - '1';
+ }
+ }
+
+ if( optionFound )
+ {
+ sOptions = sOptions | orValue;
+ }
+ else
+ {
+ rDebugPrintf( "Unhandled command line option: %s\n", option );
+ }
+}
+
+
+//==============================================================================
+// CommandLineOptions::Get
+//==============================================================================
+//
+// Description: Retrieve the specified option.
+//
+// Parameters: eOption - the enumerated command line option.
+//
+// Return: bool - true if the option is enabled, false otherwise.
+//
+//==============================================================================
+bool CommandLineOptions::Get( CmdLineOptionEnum eOption )
+{
+ simpsonsUInt64 andValue = 1;
+
+ rAssert( NUM_CLO <= 64 );
+
+ andValue <<= eOption;
+ return( ( sOptions & andValue ) != 0 );
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/main/commandlineoptions.h b/game/code/main/commandlineoptions.h
new file mode 100644
index 0000000..35ef57f
--- /dev/null
+++ b/game/code/main/commandlineoptions.h
@@ -0,0 +1,132 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: commandlineoptions.h
+//
+// Description: CommandLineOptions class declaration.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef COMMANDLINEOPTIONS_H
+#define COMMANDLINEOPTIONS_H
+
+#include <main/globaltypes.h>
+
+//#if defined( WORLD_BUILDER ) || defined( TOOLS )
+//typedef long simpsonsUInt64;
+//#endif
+
+//
+// Remember, we can only have 64 of these things
+//
+enum CmdLineOptionEnum
+{
+ CLO_NO_MUSIC, // Disable music
+ CLO_NO_EFFECTS, // Disable sound effects
+ CLO_NO_DIALOG, // Disable dialog
+ CLO_MUTE, // Disable all sound
+ CLO_SKIP_MOVIE, // Skip intro movie
+ CLO_MEMORY_MONITOR, // Enable RadMemoryMonitor
+ CLO_HEAP_STATS, // Enable heap stats display
+ CLO_CD_FILES_ONLY, // PS2 Only - Only load files from CD/DVD
+ CLO_FIREWIRE, // PS2 Only - Enable IEEE Firewire support
+ CLO_SN_PROFILER, // PS2 Only - Enable SN profiler
+ CLO_ART_STATS, // Custom display for the Artists, Such as FPS,Texture/Mesh Allocations etc.
+ CLO_PROP_STATS, // Enables logging of prop memory usage
+
+ CLO_RANDOM_BUTTONS, // Enables random button pressing
+ CLO_DEMO_TEST, // Enables one-second demo loop time for testing purposes
+ CLO_SEQUENTIAL_DEMO,// Enable demo loop in sequential order
+ CLO_SHORT_DEMO, // Run demo loop at 60 seconds per demo
+
+ CLO_FE_UNJOINED, // Load Scrooby FE resources as un-joined files.
+ CLO_FE_GAGS_TEST, // Test FE main menu gags.
+ CLO_NO_SPLASH, // Disables the splash screen
+ CLO_SKIP_FE, // Skip FE upon boot-up
+ CLO_LANG_PROMPT, // Force language selection prompt at boot-up. (PAL only)
+ CLO_SKIP_MEMCHECK, // Skip memory card boot-up check.
+ CLO_SHOW_SPEED, // Show Speedometer(s) on In-game HUD
+ CLO_NO_HUD, // No Heads-Up-Display
+ CLO_NO_TUTORIAL, // No Tutorial Pop-Up Messages
+ CLO_COINS, // Starts game w/ 100 coins
+
+ CLO_DEBUGBV, // Display Debug Bounding Volumes.
+ CLO_SKIP_SUNDAY, // Jump into the mission, skip sunday drive
+ CLO_NO_TRAFFIC, // Disable traffic ya!
+ CLO_FPS, // Display render stats always
+ CLO_DESIGNER, // Designer-optimised tune parameter
+ CLO_DETECT_LEAKS, // Enables printout of all the memory leaks in the
+ CLO_NO_HEAPS, // Don't use radcore heaps - it's easier to track leaks without them
+ CLO_PRINT_MEMORY, // Print memory information to the TTY every few frames
+ CLO_NO_HAPTIC, // Disables controller rumble
+ CLO_PARKED_CAR_TEST,// Place parked cars on all locators (bye, bye framerate)
+ CLO_NO_AVRIL, // Disable licensed music
+ CLO_PRINT_LOAD_TIME,// print the time taken on the loading screen
+ CLO_PRINT_FRAMERATE,// print the framerate after a few seconds of running
+
+ CLO_SHOW_DYNA_ZONES,// renders all the dyna-load zones as white boxes
+
+ CLO_MANUAL_RESET_DAMAGE, // will reset the cars damage state when the user does a manual reset
+
+ CLO_NO_PEDS,
+
+ CLO_WINDOW_MODE, // windowed mode (versus fullscreen) for windows.
+
+ CLO_PROGRESSIVE_SCAN,
+ CLO_LARGEHEAPS,
+
+ CLO_MEMCARD_CHEAT, // unlock everything in the game temporarily before saving data to memory card
+
+ CLO_PS2_TOOL,
+ CLO_FILE_NOT_FOUND, //Testing for file not found
+ CLO_NO_LOADING_SPEW, // Don't Spew <<START>>, <<END>> messages
+ CLO_AUDIO_LOADING_SPEW, // Spew Audio loading info
+
+ NUM_CLO // Must not exceed 64
+};
+
+//-----------------------------------------------------------------------------
+//
+// Synopsis: Any user specified command line options are stored here.
+//
+//-----------------------------------------------------------------------------
+class CommandLineOptions
+{
+ public:
+
+ //
+ // Initialize default command-line options.
+ //
+ static void InitDefaults();
+
+ //
+ // Interpret the command line string token.
+ //
+ static void HandleOption( const char* const optionIn );
+
+ //
+ // Retrive the specified option.
+ //
+ static bool Get( CmdLineOptionEnum eOption );
+
+ inline static short GetDefaultLevel() { return s_defaultLevel; }
+ inline static short GetDefaultMission() { return s_defaultMission; }
+
+ private:
+
+ // Declared but not defined to prevent copying and assignment.
+ CommandLineOptions( const CommandLineOptions& );
+ CommandLineOptions& operator=( const CommandLineOptions& );
+
+ static simpsonsUInt64 sOptions;
+
+ // default level and mission to load for 'skipfe' option
+ //
+ static short s_defaultLevel;
+ static short s_defaultMission;
+
+};
+
+#endif // COMMANDLINEOPTIONS_H
diff --git a/game/code/main/dvderrors.xls b/game/code/main/dvderrors.xls
new file mode 100644
index 0000000..1167f9f
--- /dev/null
+++ b/game/code/main/dvderrors.xls
Binary files differ
diff --git a/game/code/main/errorsGC.h b/game/code/main/errorsGC.h
new file mode 100644
index 0000000..c1f2d2b
--- /dev/null
+++ b/game/code/main/errorsGC.h
@@ -0,0 +1,62 @@
+const char* ERROR_STRINGS[] =
+{
+//English
+ "Success",
+ "FILE NOT FOUND",
+ "The Disc Cover is open.\n If you want to continue the game,\n please close the Disc Cover.",
+ "This is not \nThe Simpsons Hit & Run\nGame Disc. Please insert\nThe Simpsons Hit & Run\nGame Disc.",
+ "Please insert \nThe Simpsons Hit & Run\n Game Disc.",
+ "Please insert \nThe Simpsons Hit & Run\n Game Disc.",
+ "The Game Disc could not be read.\nPlease read the\nNintendo GameCube Instruction Booklet\nfor more information.",
+ "Please insert \nThe Simpsons Hit & Run\n Game Disc.",
+ "Please insert \nThe Simpsons Hit & Run\n Game Disc.",
+ "Please insert \nThe Simpsons Hit & Run\n Game Disc.",
+ "Please insert \nThe Simpsons Hit & Run\n Game Disc.",
+ "Please insert \nThe Simpsons Hit & Run\n Game Disc.",
+ "Please insert \nThe Simpsons Hit & Run\n Game Disc.",
+//French
+ "Success",
+ "FILE NOT FOUND",
+ "Le couvercle est ouvert.\nPour continuer à jouer,\nveuillez fermer le couvercle.",
+ "Ce n'est pas le disque\nThe Simpsons Hit & Run. Veuillez insérer\nle disque: The Simpsons Hit & Run.",
+ "Veuillez insérer le disque:\nThe Simpsons Hit & Run.",
+ "Veuillez insérer le disque:\nThe Simpsons Hit & Run.",
+ "La lecture du disque a échoué.\nVeuillez vous référer au\nmanuel d'instructions\nNINTENDO GAMECUBE\npour de plus amples informations.",
+ "Veuillez insérer le disque:\nThe Simpsons Hit & Run.",
+ "Veuillez insérer le disque:\nThe Simpsons Hit & Run.",
+ "Veuillez insérer le disque:\nThe Simpsons Hit & Run.",
+ "Veuillez insérer le disque:\nThe Simpsons Hit & Run.",
+ "Veuillez insérer le disque:\nThe Simpsons Hit & Run.",
+ "Veuillez insérer le disque:\nThe Simpsons Hit & Run.",
+//German
+ "Success",
+ "FILE NOT FOUND",
+ "Der Disc-Deckel ist geöffnet.\nBitte den Disc-Deckel schließen,\num mit dem Spiel fortzufahren.",
+ "Es befindet sich nicht die THE SIMPSONS\nHit & Run Game Disc im Laufwerk.\nBitte legen Sie die THE SIMPSONS\nHit & Run Game Disc ein.",
+ "Bitte die\nTHE SIMPSONS Hit & Run-Game Disc\neinlegen.",
+ "Bitte die\nTHE SIMPSONS Hit & Run-Game Disc\neinlegen.",
+ "Diese Game Disc kann nicht gelesen\nwerden. Bitte lesen Sie die\nBedienungsanleitung,\num weitere Informationen zu erhalten.",
+ "Bitte die\nTHE SIMPSONS Hit & Run-Game Disc\neinlegen.",
+ "Bitte die\nTHE SIMPSONS Hit & Run-Game Disc\neinlegen.",
+ "Bitte die\nTHE SIMPSONS Hit & Run-Game Disc\neinlegen.",
+ "Bitte die\nTHE SIMPSONS Hit & Run-Game Disc\neinlegen.",
+ "Bitte die\nTHE SIMPSONS Hit & Run-Game Disc\neinlegen.",
+ "Bitte die\nTHE SIMPSONS Hit & Run-Game Disc\neinlegen.",
+//Spanish
+ "Success",
+ "FILE NOT FOUND",
+ "La tapa está abierta.\nSi quieres seguir jugando,\ncierra la tapa.",
+ "Este no es el disco de\nThe Simpsons Hit & Run. Coloca el disco de\nThe Simpsons Hit & Run.",
+ "Coloca el disco de The Simpsons Hit & Run.",
+ "Coloca el disco de The Simpsons Hit & Run.",
+ "No se puede leer el disco.\nConsulta el manual de\ninstrucciones de NINTENDO GAMECUBE\npara obtener más información.",
+ "Coloca el disco de The Simpsons Hit & Run.",
+ "Coloca el disco de The Simpsons Hit & Run.",
+ "Coloca el disco de The Simpsons Hit & Run.",
+ "Coloca el disco de The Simpsons Hit & Run.",
+ "Coloca el disco de The Simpsons Hit & Run.",
+ "Coloca el disco de The Simpsons Hit & Run.",
+
+ "" // dummy terminator
+
+};
diff --git a/game/code/main/errorsPS2.h b/game/code/main/errorsPS2.h
new file mode 100644
index 0000000..7fa2b28
--- /dev/null
+++ b/game/code/main/errorsPS2.h
@@ -0,0 +1,62 @@
+const char* ERROR_STRINGS[] =
+{
+//English
+ "Success",
+ "FILE NOT FOUND",
+ "The disc tray is open",
+ "Incorrect disc. \nInsert The Simpsons Hit & Run disc.",
+ "No disc. \nInsert The Simpsons Hit & Run disc.",
+ "Incorrect disc. \nInsert The Simpsons Hit & Run disc.",
+ "Incorrect disc. \nInsert The Simpsons Hit & Run disc.",
+ "Incorrect disc. \nInsert The Simpsons Hit & Run disc.",
+ "Incorrect disc. \nInsert The Simpsons Hit & Run disc.",
+ "Incorrect disc. \nInsert The Simpsons Hit & Run disc.",
+ "Incorrect disc. \nInsert The Simpsons Hit & Run disc.",
+ "Incorrect disc. \nInsert The Simpsons Hit & Run disc.",
+ "Incorrect disc. \nInsert The Simpsons Hit & Run disc.",
+//French
+ "Success",
+ "FILE NOT FOUND",
+ "Le compartiment à disque est ouvert.",
+ "Disque incorrect.\nInsérez le disque The Simpsons Hit & Run.",
+ "Aucun disque.\nInsérez le disque The Simpsons Hit & Run.",
+ "Disque incorrect.\nInsérez le disque The Simpsons Hit & Run.",
+ "Disque incorrect.\nInsérez le disque The Simpsons Hit & Run.",
+ "Disque incorrect.\nInsérez le disque The Simpsons Hit & Run.",
+ "Disque incorrect.\nInsérez le disque The Simpsons Hit & Run.",
+ "Disque incorrect.\nInsérez le disque The Simpsons Hit & Run.",
+ "Disque incorrect.\nInsérez le disque The Simpsons Hit & Run.",
+ "Disque incorrect.\nInsérez le disque The Simpsons Hit & Run.",
+ "Disque incorrect.\nInsérez le disque The Simpsons Hit & Run.",
+//German
+ "Success",
+ "FILE NOT FOUND",
+ "Die DVD/CD - Lade ist geöffnet",
+ "Falsche DVD/CD-ROM. Leg die\nTHE SIMPSONS Hit & Run-DVD/CD-ROM ein.",
+ "Keine DVD/CD-ROM. Leg die\nTHE SIMPSONS Hit & Run-DVD/CD-ROM ein.",
+ "Falsche DVD/CD-ROM. Leg die\nTHE SIMPSONS Hit & Run-DVD/CD-ROM ein.",
+ "Falsche DVD/CD-ROM. Leg die\nTHE SIMPSONS Hit & Run-DVD/CD-ROM ein.",
+ "Falsche DVD/CD-ROM. Leg die\nTHE SIMPSONS Hit & Run-DVD/CD-ROM ein.",
+ "Falsche DVD/CD-ROM. Leg die\nTHE SIMPSONS Hit & Run-DVD/CD-ROM ein.",
+ "Falsche DVD/CD-ROM. Leg die\nTHE SIMPSONS Hit & Run-DVD/CD-ROM ein.",
+ "Falsche DVD/CD-ROM. Leg die\nTHE SIMPSONS Hit & Run-DVD/CD-ROM ein.",
+ "Falsche DVD/CD-ROM. Leg die\nTHE SIMPSONS Hit & Run-DVD/CD-ROM ein.",
+ "Falsche DVD/CD-ROM. Leg die\nTHE SIMPSONS Hit & Run-DVD/CD-ROM ein.",
+//Spanish
+ "Success",
+ "FILE NOT FOUND",
+ "La bandeja del disco está abierta.",
+ "Disco incorrecto.\nInserta el disco de The Simpsons Hit & Run.",
+ "No se ha introducido ningún disco.\nInserta el disco de Simpsons Hit & Run.",
+ "Disco incorrecto.\nInserta el disco de The Simpsons Hit & Run.",
+ "Disco incorrecto.\nInserta el disco de The Simpsons Hit & Run.",
+ "Disco incorrecto.\nInserta el disco de The Simpsons Hit & Run.",
+ "Disco incorrecto.\nInserta el disco de The Simpsons Hit & Run.",
+ "Disco incorrecto.\nInserta el disco de The Simpsons Hit & Run.",
+ "Disco incorrecto.\nInserta el disco de The Simpsons Hit & Run.",
+ "Disco incorrecto.\nInserta el disco de The Simpsons Hit & Run.",
+ "Disco incorrecto.\nInserta el disco de The Simpsons Hit & Run.",
+
+ "" // dummy terminator
+
+};
diff --git a/game/code/main/errorsWIN32.h b/game/code/main/errorsWIN32.h
new file mode 100644
index 0000000..2ad955d
--- /dev/null
+++ b/game/code/main/errorsWIN32.h
@@ -0,0 +1,31 @@
+const char* ERROR_STRINGS[] =
+{
+//English
+ "Success",
+ "FileNotFound",
+ "ShellOpen",
+ "WrongMedia",
+ "There's a problem with the disc you're using. It may be dirty or damaged. Press A to continue.",
+ "There's a problem with the disc you're using. It may be dirty or damaged. Press A to continue.",
+ "There's a problem with the disc you're using. It may be dirty or damaged. Press A to continue.",
+ "Your Xbox doesn't have enough free blocks to save games. Press A to continue without saving or B to free more blocks.",
+ "Your Xbox doesn't have enough free blocks to save games. Press A to continue without saving or B to free more blocks.",
+ "MediaEncodingErr",
+ "MediaWrongType",
+ "MediaInvalid",
+ "DataCorrupt",
+//French
+ "NON SOUTENU",
+ "NON SOUTENU",
+ "NON SOUTENU",
+ "NON SOUTENU",
+ "ÉCHEC DE MATÉRIEL",
+ "ÉCHEC DE MATÉRIEL",
+ "ÉCHEC DE MATÉRIEL",
+ "ÉCHEC DE MATÉRIEL",
+ "HORS DE L'ESPACE",
+ "NON SOUTENU",
+ "NON SOUTENU",
+ "NON SOUTENU",
+ "NON SOUTENU",
+};
diff --git a/game/code/main/errorsXBOX.h b/game/code/main/errorsXBOX.h
new file mode 100644
index 0000000..ec4d492
--- /dev/null
+++ b/game/code/main/errorsXBOX.h
@@ -0,0 +1,62 @@
+const char* ERROR_STRINGS[] =
+{
+//English
+ "Success",
+ "There is a problem with\nthe disc you're using.\nIt may be dirty or damaged.",
+ "The Disc Tray is open.\nIf you want to continue the game,\nplease close the Disc Tray.",
+ "This is not \nThe Simpsons Hit & Run\nGame Disc. Please insert\nThe Simpsons Hit & Run\nGame Disc.",
+ "There is a problem with\nthe disc you're using.\nIt may be dirty or damaged.",
+ "There is a problem with\nthe disc you're using.\nIt may be dirty or damaged.",
+ "There is a problem with\nthe disc you're using.\nIt may be dirty or damaged.",
+ "There is a problem with\nthe disc you're using.\nIt may be dirty or damaged.",
+ "There is a problem with\nthe disc you're using.\nIt may be dirty or damaged.",
+ "There is a problem with\nthe disc you're using.\nIt may be dirty or damaged.",
+ "There is a problem with\nthe disc you're using.\nIt may be dirty or damaged.",
+ "There is a problem with\nthe disc you're using.\nIt may be dirty or damaged.",
+ "There is a problem with\nthe disc you're using.\nIt may be dirty or damaged.",
+//French
+ "Success",
+ "Le disque utilisé présente une anomalie.\nIl est peut-être sale ou endommagé.",
+ "",
+ "",
+ "Le disque utilisé présente une anomalie.\nIl est peut-être sale ou endommagé.",
+ "Le disque utilisé présente une anomalie.\nIl est peut-être sale ou endommagé.",
+ "Le disque utilisé présente une anomalie.\nIl est peut-être sale ou endommagé.",
+ "Le disque utilisé présente une anomalie.\nIl est peut-être sale ou endommagé.",
+ "Le disque utilisé présente une anomalie.\nIl est peut-être sale ou endommagé.",
+ "Le disque utilisé présente une anomalie.\nIl est peut-être sale ou endommagé.",
+ "Le disque utilisé présente une anomalie.\nIl est peut-être sale ou endommagé.",
+ "Le disque utilisé présente une anomalie.\nIl est peut-être sale ou endommagé.",
+ "Le disque utilisé présente une anomalie.\nIl est peut-être sale ou endommagé.",
+//German
+ "Success",
+ "Problem bei der benutzten CD:\nCD ist verschmutzt oder beschädigt.",
+ "",
+ "",
+ "Problem bei der benutzten CD:\nCD ist verschmutzt oder beschädigt.",
+ "Problem bei der benutzten CD:\nCD ist verschmutzt oder beschädigt.",
+ "Problem bei der benutzten CD:\nCD ist verschmutzt oder beschädigt.",
+ "Problem bei der benutzten CD:\nCD ist verschmutzt oder beschädigt.",
+ "Problem bei der benutzten CD:\nCD ist verschmutzt oder beschädigt.",
+ "Problem bei der benutzten CD:\nCD ist verschmutzt oder beschädigt.",
+ "Problem bei der benutzten CD:\nCD ist verschmutzt oder beschädigt.",
+ "Problem bei der benutzten CD:\nCD ist verschmutzt oder beschädigt.",
+ "Problem bei der benutzten CD:\nCD ist verschmutzt oder beschädigt.",
+//Spanish
+ "Success",
+ "Hay un problema con el disco que estás usando.\nPuede estar sucio o dañado.",
+ "",
+ "",
+ "Hay un problema con el disco que estás usando.\nPuede estar sucio o dañado.",
+ "Hay un problema con el disco que estás usando.\nPuede estar sucio o dañado.",
+ "Hay un problema con el disco que estás usando.\nPuede estar sucio o dañado.",
+ "Hay un problema con el disco que estás usando.\nPuede estar sucio o dañado.",
+ "Hay un problema con el disco que estás usando.\nPuede estar sucio o dañado.",
+ "Hay un problema con el disco que estás usando.\nPuede estar sucio o dañado.",
+ "Hay un problema con el disco que estás usando.\nPuede estar sucio o dañado.",
+ "Hay un problema con el disco que estás usando.\nPuede estar sucio o dañado.",
+ "Hay un problema con el disco que estás usando.\nPuede estar sucio o dañado.",
+
+ "" // dummy terminator
+
+};
diff --git a/game/code/main/game.cpp b/game/code/main/game.cpp
new file mode 100644
index 0000000..8330f32
--- /dev/null
+++ b/game/code/main/game.cpp
@@ -0,0 +1,709 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: game.cpp
+//
+// Description: The game loop
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Standard Library
+#include <stdlib.h>
+#include <string.h>
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugcommunication.hpp>
+#include <raddebugconsole.hpp>
+#include <raddebugwatch.hpp>
+#include <radfile.hpp>
+#include <radmemorymonitor.hpp>
+#include <radtime.hpp>
+// Pure3D
+#include <p3d/loadmanager.hpp>
+#include <p3d/utility.hpp>
+
+#ifdef RAD_WIN32
+#pragma warning( push )
+#pragma warning( disable : 4005 ) // disable warning for redefinition of RGB macro (wingdi.h,raddebugconsole.hpp)
+#include <windows.h> // for peekmessage...
+#pragma warning( pop )
+#endif
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/contextenum.h>
+#include <debug/profiler.h>
+#include <gameflow/gameflow.h>
+#include <presentation/gui/ingame/guiscreenmissionload.h>
+#include <main/commandlineoptions.h>
+#include <main/game.h>
+#include <main/platform.h>
+
+#ifdef RAD_GAMECUBE
+#include <main/gamecube_extras/gcmanager.h>
+#endif
+
+#include <memory/srrmemory.h>
+#include <memory/memoryutilities.h>
+
+#include <render/RenderFlow/RenderFlow.h>
+#include <sound/soundmanager.h>
+#include <input/inputmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Static pointer to instance of this singleton.
+//
+Game* Game::spInstance = NULL;
+
+bool g_inDemoMode = false;
+
+//#define DEMO_MODE_PROFILER
+
+#ifdef DEMO_MODE_PROFILER
+ #define DEMOPROFILE(X) X
+#else
+ #define DEMOPROFILE(X)
+#endif
+
+#ifdef DEMO_MODE_PROFILER
+
+#ifdef RAD_PS2
+#include <libgraph.h>
+#endif
+
+DemoProfiler::DemoProfiler(unsigned mf) :
+ recording(false), maxFrames(mf), nChannel(0), currentFrame(0),
+ alertStatus(PROFILER_ALERT_GREEN),
+ numFramesBelow_20(0), numFramesBetween_20_30(0),numFramesBetween_30_40(0), numFramesAbove_40(0)
+{
+ for( int i=0; i < MAX_CHANNEL; i++)
+ {
+ channel[i] = NULL;
+ }
+}
+
+void DemoProfiler::AddChannel(unsigned c, const char* name)
+{
+ rReleaseAssert(c < MAX_CHANNEL);
+ channel[c] = new Channel;
+ channel[c]->samples = new unsigned[maxFrames];
+ memset(channel[c]->samples, 0, maxFrames*sizeof(unsigned));
+ channel[c]->t0 = 0;
+ strncpy(channel[c]->name, name, 254);
+ nChannel++;
+}
+
+void DemoProfiler::Start(unsigned c)
+{
+ if(!channel[c]) return;
+ if( recording )
+ {
+ channel[c]->t0 = radTimeGetMicroseconds64();
+ }
+}
+
+
+void DemoProfiler::Stop(unsigned c)
+{
+ if(!channel[c]) return;
+ if( recording )
+ {
+ radTime64 elapsed = radTimeGetMicroseconds64() - channel[c]->t0;
+ channel[c]->samples[currentFrame] += elapsed;
+
+ if(c == 0)
+ {
+ alertStatus = PROFILER_ALERT_GREEN;
+ if( elapsed >= 50000 )
+ {
+ numFramesBelow_20++;
+ alertStatus = PROFILER_ALERT_RED;
+ }
+ else if( (elapsed < 50000) && (elapsed >= 33333) )
+ {
+ numFramesBetween_20_30++;
+ alertStatus = PROFILER_ALERT_YELLOW;
+ }
+ else if( (elapsed < 33333) && (elapsed >= 25000) )
+ {
+ numFramesBetween_30_40++;
+ }
+ else
+ {
+ numFramesAbove_40++;
+ }
+ }
+ }
+}
+
+void DemoProfiler::Set(unsigned c, unsigned val)
+{
+ if(!channel[c]) return;
+ if( recording )
+ {
+ channel[c]->samples[currentFrame] = val;
+ }
+}
+
+void DemoProfiler::StartRecording()
+{
+ recording = true;
+}
+
+bool DemoProfiler::IsRecording()
+{
+ return recording;
+}
+
+unsigned DemoProfiler::GetSample(unsigned c)
+{
+ return (recording && channel[c]) ? channel[c]->samples[currentFrame] : 0;
+}
+
+unsigned DemoProfiler::GetCurrentFrame()
+{
+ return currentFrame;
+}
+
+void DemoProfiler::Accumulate(unsigned c, unsigned val)
+{
+ if(!channel[c]) return;
+ if( recording )
+ {
+ channel[c]->samples[currentFrame] += val;
+ }
+}
+
+void DemoProfiler::NextFrame()
+{
+ if( recording )
+ {
+ currentFrame++;
+ if(currentFrame >= maxFrames)
+ {
+ Dump();
+ recording = false;
+ }
+ }
+}
+
+DemoProfiler::AlertStatus DemoProfiler::GetAlertStatus()
+{
+ return alertStatus;
+}
+
+void DemoProfiler::Dump()
+{
+ rReleasePrintf("\n\n~~~~~~~~~~~~~~~~~~~~~ PROFILER STATS ~~~~~~~~~~~~~~~~~~~~~\n");
+ rReleasePrintf( "Total Frames: %d\n"
+ "< 20 fps: %d (%.2f%%)\n"
+ "20-30 fps: %d (%.2f%%)\n"
+ "30-40 fps: %d (%.2f%%)\n"
+ ">40 fps: %d (%.2f%%)\n",
+ maxFrames,
+ numFramesBelow_20, 100.0f * (float)numFramesBelow_20 / (float)maxFrames,
+ numFramesBetween_20_30, 100.0f * (float)numFramesBetween_20_30 / (float)maxFrames,
+ numFramesBetween_30_40, 100.0f * (float)numFramesBetween_30_40 / (float)maxFrames,
+ numFramesAbove_40, 100.0f * (float)numFramesAbove_40 / (float)maxFrames );
+
+ for( unsigned i=0; i < MAX_CHANNEL; i++)
+ {
+ if(channel[i])
+ {
+ rReleasePrintf("%s\t", channel[i]->name);
+ }
+ }
+ rReleasePrintf("\n");
+
+ for( unsigned i=0; i < maxFrames; i++ )
+ {
+ for( unsigned j=0; j < MAX_CHANNEL; j++ )
+ {
+ if(channel[j])
+ {
+ rReleasePrintf("%.1f\t", (float)channel[j]->samples[i] * 0.001f);
+ }
+ }
+ rReleasePrintf("\n");
+
+ if( !(i % 10) )
+ {
+ rmt::Sin(0.0f);
+#ifdef RAD_PS2
+ sceGsSyncV(0);
+#endif
+ }
+ }
+
+ while(1)
+ {
+ rmt::Sin(0.0f);
+ }
+}
+
+
+DemoProfiler g_DemoProfiler( 1850 ); // about a minute at 30 fps
+
+#include <pddi/pddi.hpp>
+
+static bool g_DemoProfiler_Started = false;
+static const int g_DemoProfiler_StartFrame = 150;
+static int g_DemoProfiler_CurrentFrame = 0;
+
+#endif
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Game::CreateInstance
+//==============================================================================
+// Description: Create the game
+//
+// Parameters: platform - the platform that the game is to be created on
+//
+// Return: pointer to the created game
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+Game* Game::CreateInstance( Platform* platform )
+{
+ rAssert( platform != NULL );
+
+ rAssertMsg( (spInstance == NULL), "Trying to create more than one instance of the game!" );
+
+MEMTRACK_PUSH_GROUP( "Game" );
+ if( spInstance == NULL )
+ {
+ spInstance = new(GMA_PERSISTENT) Game( platform );
+ rAssert( spInstance != NULL );
+ }
+MEMTRACK_POP_GROUP( "Game" );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// Game::DestroyInstance
+//==============================================================================
+// Description: Destroy the game
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void Game::DestroyInstance()
+{
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+}
+
+
+//==============================================================================
+// Game::GetInstance
+//==============================================================================
+// Synopsis: Get an instance of the game
+//
+// Parameters: None.
+//
+// Returns: a poitner to the game
+//
+// Constraints: Game must be created before this is called
+//
+//==============================================================================
+Game* Game::GetInstance()
+{
+ rAssertMsg((spInstance != NULL), "Trying to get an instance of the game before it is created!");
+
+ return spInstance;
+}
+
+//=============================================================================
+// Game::GetPlatform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Platform
+//
+//=============================================================================
+Platform* Game::GetPlatform()
+{
+ return mpPlatform;
+}
+
+
+//==============================================================================
+// Game::Initialize
+//==============================================================================
+// Synopsis: Initialize the game
+//
+// Parameters: None.
+//
+// Returns: None.
+//
+//==============================================================================
+void Game::Initialize()
+{
+ rAssert( mpPlatform != NULL );
+
+ //
+ // Initialize the platform and core systems.
+ //
+ mpPlatform->InitializePlatform();
+
+ //
+ // Initialize the timer system
+ //
+ ::radTimeCreateList( &mpTimerList,
+ 16, // Default
+ GMA_PERSISTENT );
+
+ rAssert( mpTimerList != NULL );
+
+ //
+ // Create the GameFlow & Couple the RenderFlow
+ //
+ mpGameFlow = GameFlow::CreateInstance();
+ mpRenderFlow = RenderFlow::GetInstance();
+ mpRenderFlow->DoAllRegistration();
+
+ CGuiScreenMissionLoad::InitializePermanentVariables();
+
+#ifdef RAD_E3
+ rReleasePrintf( "\n----------=[ SIMPSONS HIT & RUN - E3 BUILD ]=----------\n\n" );
+#endif
+
+ //
+ // Set the starting context
+ //
+ mpGameFlow->SetContext( CONTEXT_BOOTUP );
+}
+
+
+//==============================================================================
+// Game::Terminate
+//==============================================================================
+// Synopsis: Clean up and shut down.
+//
+// Parameters: None.
+//
+// Returns: None.
+//
+//==============================================================================
+void Game::Terminate()
+{
+ rAssert( mpGameFlow != NULL );
+ rAssert( mpRenderFlow != NULL );
+ rAssert( mpTimerList != NULL );
+ rAssert( mpPlatform != NULL );
+
+ //
+ // Kill the flow servers.
+ //
+ mpGameFlow->DestroyInstance();
+ mpGameFlow = NULL;
+
+ // Render flow destroyed by singletons.cpp
+ //mpRenderFlow->DestroyInstance();
+ mpRenderFlow = NULL;
+
+ //
+ // Release the game's references to the timer list.
+ //
+ mpTimerList->Release();
+ mpTimerList = NULL;
+}
+
+
+//==============================================================================
+// Game::Run
+//==============================================================================
+// Synopsis: This is where game loop spins. It exits after Stop() is called.
+//
+// Parameters: None.
+//
+// Returns: None.
+//
+//==============================================================================
+const unsigned PROFILE_CHANNEL_ALL = 0;
+const unsigned PROFILE_CHANNEL_AI = 1;
+const unsigned PROFILE_CHANNEL_RENDER = 2;
+const unsigned PROFILE_CHANNEL_LOAD = 3;
+
+void Game::Run()
+{
+ extern bool g_AllowDebugOutput;
+
+#ifdef DEMO_MODE_PROFILER
+ g_AllowDebugOutput = false;
+ g_DemoProfiler.AddChannel(0, "All");
+ g_DemoProfiler.AddChannel(1, "Ai");
+ g_DemoProfiler.AddChannel(2, "Render");
+ g_DemoProfiler.AddChannel(3, "Load");
+ g_DemoProfiler.AddChannel(4, "Opaque");
+ g_DemoProfiler.AddChannel(5, "Translucent");
+ g_DemoProfiler.AddChannel(6, "World Sphere");
+ g_DemoProfiler.AddChannel(7, "Anim Entity");
+ g_DemoProfiler.AddChannel(8, "Breakable");
+ g_DemoProfiler.AddChannel(9, "InstAnimDynaPhys");
+ g_DemoProfiler.AddChannel(10, "InstDynaPhys");
+ g_DemoProfiler.AddChannel(11, "InstStat");
+ g_DemoProfiler.AddChannel(12, "InstStatPhys");
+ g_DemoProfiler.AddChannel(13,"Lens Flare");
+ g_DemoProfiler.AddChannel(14, "State Prop");
+ g_DemoProfiler.AddChannel(15, "Static");
+ g_DemoProfiler.AddChannel(16, "Tristrip");
+ g_DemoProfiler.AddChannel(17, "AnimCollision");
+ g_DemoProfiler.AddChannel(18, "Cars");
+ g_DemoProfiler.AddChannel(19, "Characters");
+ g_DemoProfiler.AddChannel(20, "ABH");
+ g_DemoProfiler.AddChannel(21, "Actor");
+ g_DemoProfiler.AddChannel(22, "AnimatedIcon");
+ g_DemoProfiler.AddChannel(23, "ParticleSystem");
+
+#endif
+
+ unsigned time = radTimeGetMilliseconds();
+ while( !mExitNow )
+ {
+ DEMOPROFILE( g_DemoProfiler.Start(PROFILE_CHANNEL_ALL); )
+
+ BEGIN_PROFILER_FRAME();
+
+ BEGIN_PROFILE( "GameLoop" )
+
+ unsigned newTime = radTimeGetMilliseconds();
+ unsigned elapsed = newTime - time;
+ time = newTime;
+
+ //
+ // Service the windows message loop.
+ //
+#ifdef RAD_WIN32
+ MSG msg;
+ while( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
+ {
+ if( msg.message == WM_QUIT )
+ {
+ //Chuck someone closed the Window we are going to try to exit the game
+ //if the game isnt in a context that can easily transition to the EXIT context we
+ // are going to call the Launch Dashboard and return to the main loop and shutdown
+ if ( GetGameFlow()->GetCurrentContext() != CONTEXT_FRONTEND &&
+ GetGameFlow()->GetCurrentContext() != CONTEXT_GAMEPLAY &&
+ GetGameFlow()->GetCurrentContext() != CONTEXT_PAUSE )
+ {
+ GetGame()->GetPlatform()->LaunchDashboard();
+ //return to the winmain and shutdown
+ return;
+ }
+ //we are in a context that will transition nicely to the Exit context.
+ else
+ {
+ mpGameFlow->SetContext( CONTEXT_EXIT );
+ }
+ }
+ TranslateMessage( &msg );
+ DispatchMessage( &msg );
+ }
+#endif // RAD_WIN32
+
+ //
+ // Service the GameFlow and RenderFlow.
+ //
+
+ if ( !mpPlatform->PausedForErrors() )
+ {
+ DEMOPROFILE( g_DemoProfiler.Start(PROFILE_CHANNEL_AI); )
+ mpTimerList->Service(); //nv
+ mpGameFlow->OnTimerDone(elapsed, NULL);
+ DEMOPROFILE( g_DemoProfiler.Stop(PROFILE_CHANNEL_AI); )
+
+ if( !mExitNow )
+ {
+ DEMOPROFILE( g_DemoProfiler.Start(PROFILE_CHANNEL_RENDER); )
+ mpRenderFlow->OnTimerDone(elapsed, NULL);
+ DEMOPROFILE( g_DemoProfiler.Stop(PROFILE_CHANNEL_RENDER); )
+ }
+ }
+ else if (mpPlatform->IsControllerError()) // if controller unplugged
+ { // need to update input manager
+ if (InputManager::GetInstance())
+ {
+ InputManager::GetInstance()->Update( elapsed );
+ }
+ }
+ else
+ {
+#ifdef RAD_GAMECUBE
+ GCManager::GetInstance()->OnTimerDone( elapsed, NULL );
+#endif
+ }
+
+ //
+ // Service FTech subsystems.
+ //
+ ::radFileService();
+ ::radDbgComService();
+ ::radDebugConsoleService();
+
+ if( CommandLineOptions::Get( CLO_MEMORY_MONITOR) )
+ {
+ ::radMemoryMonitorService();
+ }
+
+ //
+ // Service the sound renderer.
+ //
+ SoundManager::GetInstance()->Update();
+
+ if ( mpPlatform->PausedForErrors() )
+ {
+ //
+ // [ps] We update sound without a valid context or elapsed time.
+ // We use 0 and NUM_CONTEXTS, since these values are unlikely to
+ // happen in the real game.
+ //
+ SoundManager::GetInstance()->UpdateOncePerFrame( 0, NUM_CONTEXTS, false, true );
+ }
+
+ //
+ // Spin Pure3D async loading.
+ //
+ DEMOPROFILE( g_DemoProfiler.Start(PROFILE_CHANNEL_LOAD); )
+ p3d::loadManager->SwitchTask();
+ DEMOPROFILE( g_DemoProfiler.Stop(PROFILE_CHANNEL_LOAD); )
+
+ ++mFrameCount;
+
+ DEMOPROFILE( g_DemoProfiler.Stop(PROFILE_CHANNEL_ALL); )
+
+#ifdef DEMO_MODE_PROFILER
+ g_AllowDebugOutput = true;
+ if(g_inDemoMode)
+ {
+ if( !g_DemoProfiler_Started && (g_DemoProfiler_CurrentFrame == g_DemoProfiler_StartFrame) )
+ {
+ g_DemoProfiler_Started = true;
+ g_DemoProfiler.StartRecording();
+ rReleasePrintf("Beginning demo profile run\n");
+ }
+
+ if(g_DemoProfiler.IsRecording())
+ {
+ pddiColour colour(255,255,255);
+ if( g_DemoProfiler.GetAlertStatus() == DemoProfiler::PROFILER_ALERT_YELLOW)
+ colour.Set(255,255,0);
+ else
+ if( g_DemoProfiler.GetAlertStatus() == DemoProfiler::PROFILER_ALERT_RED)
+ colour.Set(255,0,0);
+
+ char duff[255];
+ sprintf(duff, "%d %d", g_DemoProfiler.GetCurrentFrame(), (g_DemoProfiler.GetSample(0) + 500) / 1000);
+ p3d::pddi->DrawString( duff, 10, 400, colour );
+ }
+
+ g_DemoProfiler.NextFrame();
+ g_DemoProfiler_CurrentFrame++;
+ }
+
+ g_AllowDebugOutput = false;
+#endif // DEMO_MODE_PROFILER
+
+ END_PROFILE( "GameLoop" )
+
+ END_PROFILER_FRAME();
+ }
+}
+
+
+//==============================================================================
+// Game::Stop
+//==============================================================================
+// Synopsis: Sets the flag to break us out of the game loop.
+//
+// Parameters: None.
+//
+// Returns: None.
+//
+//==============================================================================
+void Game::Stop()
+{
+ //
+ // Stop any further rendering from happening.
+ //
+ mExitNow = true;
+}
+
+
+unsigned Game::GetRandomSeed ()
+{
+ radDate date;
+ ::radTimeGetDate (&date);
+ return ( ( date.m_Year << 16 ) | ( date.m_Month << 8 ) | ( date.m_Day ) ) ^ ( ( date.m_Second << 24 ) | ( date.m_Minute << 8 ) | ( date.m_Hour ) );
+}
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Game::Game
+//==============================================================================
+// Synopsis: Constructor.
+//
+// Parameters: platform - platform on which game should be created
+//
+// Returns: N/A.
+//
+//==============================================================================
+Game::Game( Platform* platform ) :
+ mpPlatform( platform ),
+ mpTimerList( NULL ),
+ mpGameFlow( NULL ),
+ mpRenderFlow( NULL ),
+ mFrameCount( 0 ),
+ mExitNow( false ),
+ mDemoCount( 0 ),
+ mTimeMS( 0 )
+{
+}
+
+
+//==============================================================================
+// Game::~Game
+//==============================================================================
+// Synopsis: Destructor.
+//
+// Parameters: None.
+//
+// Returns: N/A.
+//
+//==============================================================================
+Game::~Game()
+{
+}
diff --git a/game/code/main/game.h b/game/code/main/game.h
new file mode 100644
index 0000000..f090ce8
--- /dev/null
+++ b/game/code/main/game.h
@@ -0,0 +1,139 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: game.h
+//
+// Description: The game loop
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef GAME_H
+#define GAME_H
+
+//========================================
+// Forward References
+//========================================
+class Platform;
+struct IRadTimerList;
+class GameFlow;
+class RenderFlow;
+
+#include <radtime.hpp>
+
+class DemoProfiler
+{
+public:
+ DemoProfiler(unsigned maxFrames);
+
+ void AddChannel(unsigned c, const char* name);
+
+ void NextFrame();
+
+ void Start(unsigned c); // start timing channel c
+ void Stop(unsigned c); // stop timing channel c
+ void Set(unsigned c, unsigned val); // set sample value for channel c
+ unsigned GetSample(unsigned c);
+ unsigned GetCurrentFrame();
+ void Accumulate(unsigned c, unsigned val); // add value to sample for channel c
+ void Dump(); // print out all samples
+
+ void StartRecording();
+ bool IsRecording();
+ enum AlertStatus { PROFILER_ALERT_GREEN, PROFILER_ALERT_YELLOW, PROFILER_ALERT_RED };
+ AlertStatus GetAlertStatus();
+
+private:
+ enum { MAX_CHANNEL = 64 };
+
+ struct Channel
+ {
+ char name[255];
+ unsigned* samples;
+ radTime64 t0;
+ };
+
+ bool recording;
+ unsigned maxFrames;
+
+ Channel* channel[MAX_CHANNEL];
+ unsigned nChannel;
+ unsigned currentFrame;
+
+ AlertStatus alertStatus;
+ unsigned numFramesBelow_20;
+ unsigned numFramesBetween_20_30;
+ unsigned numFramesBetween_30_40;
+ unsigned numFramesAbove_40;
+};
+
+extern DemoProfiler g_DemoProfiler;
+
+//=============================================================================
+//
+// Synopsis: The game loop
+//
+//=============================================================================
+class Game
+{
+ public:
+
+ // Static Methods (for creating and getting an instance of the game)
+ static Game* CreateInstance( Platform* platform );
+ static void DestroyInstance();
+ static Game* GetInstance();
+
+ Platform* GetPlatform();
+
+
+ // Game Flow Public Methods
+ void Initialize();
+ void Terminate();
+
+ void Run();
+ void Stop();
+
+ IRadTimerList* GetTimerList() { return mpTimerList; }
+
+ unsigned int GetFrameCount() const { return mFrameCount; };
+
+ unsigned int GetDemoCount() const { return mDemoCount; };
+ void IncrementDemoCount() { ++mDemoCount; };
+ void SetTime( unsigned int timeMS ) { mTimeMS = timeMS; };
+ unsigned int GetTime() { return mTimeMS; };
+
+ static unsigned GetRandomSeed ();
+
+ private:
+
+ // Constructors, Destructors, and Operators
+ Game( Platform* platform );
+ virtual ~Game();
+
+ // Unused Constructors, Destructors, and Operators
+ Game();
+ Game( const Game& aGame );
+ Game& operator=( const Game& aGame );
+
+ // Static Singleton Attribute
+ static Game* spInstance;
+
+ // Private Attributes
+ Platform* mpPlatform;
+ IRadTimerList* mpTimerList;
+ GameFlow* mpGameFlow;
+ RenderFlow* mpRenderFlow;
+
+ unsigned int mFrameCount;
+
+ bool mExitNow;
+
+ unsigned int mDemoCount;
+ unsigned int mTimeMS;
+};
+
+inline Game* GetGame() { return( Game::GetInstance() ); }
+
+#endif // GAME_H
+
diff --git a/game/code/main/gamecube_extras/buildlicense.bat b/game/code/main/gamecube_extras/buildlicense.bat
new file mode 100644
index 0000000..500aea5
--- /dev/null
+++ b/game/code/main/gamecube_extras/buildlicense.bat
@@ -0,0 +1,8 @@
+..\..\..\libs\pure3d\tools\commandline\bin\p3dimage -b 4 -c -o licensee.p3d ..\..\..\exportart\frontend\scrooby\resource\images\_gc\license.png
+@rem..\..\..\libs\pure3d\tools\commandline\bin\convert2dxtn -f 1 licensee.p3d
+..\..\..\libs\pure3d\tools\commandline\bin\p3dgc licensee.p3d
+attrib -r license.h
+..\..\..\build\tools\bin2h licensee.p3d > license.h
+..\..\..\libs\pure3d\lib\perl\bin\perl ..\..\..\build\tools\sizeprint.pl license.h license.png >> license.h
+attrib +r license.h
+rem @del licensee.p3d
diff --git a/game/code/main/gamecube_extras/gcmanager.cpp b/game/code/main/gamecube_extras/gcmanager.cpp
new file mode 100644
index 0000000..8d7fa7c
--- /dev/null
+++ b/game/code/main/gamecube_extras/gcmanager.cpp
@@ -0,0 +1,711 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: GCManager.cpp
+//
+// Description: Implement GCManager
+//
+// History: 14/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <dolphin.h>
+#include <dolphin/os.h>
+#include <dolphin/lg.h>
+#include <dolphin/dvd.h>
+
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <radFile.hpp>
+#include <radcontroller.hpp>
+#include <radthread.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+#include <main/gamecube_extras/GCManager.h>
+#include <main/gcplatform.h>
+#include <main/game.h>
+#include <gameflow/gameflow.h>
+#include <main/platform.h>
+#include <memory/srrmemory.h>
+#include <input/inputmanager.h>
+
+#include <debug/profiler.h>
+
+#include <sound/soundmanager.h>
+
+#include <data/gamedatamanager.h>
+
+#include <presentation/fmvplayer/fmvplayer.h>
+#include <presentation/presentation.h>
+
+unsigned int MIN_TIME = 500; //0.5 seconds in milliseconds, as per doc
+
+#ifdef DEBUGWATCH
+int sX = 640;
+int sY = 480;
+#endif
+
+//*****************************************************************************
+//
+// Call backs
+//
+//*****************************************************************************
+
+void ResetButtonCallBack( void )
+{
+ if ( OSGetResetButtonState() )
+ {
+ //Inform the GCManager
+ GCManager::GetInstance()->Reset();
+ }
+ else
+ {
+ //Reset the callback the last one was bogus.
+ OSSetResetCallback( ResetButtonCallBack );
+ }
+}
+
+void CheckForGCNReset()
+{
+ GCManager::GetInstance()->TestForReset();
+}
+
+
+#ifdef DEBUGWATCH
+void ChangeResolutionCallback( void* userData )
+{
+ sY = sX * 12;
+ sX *= 16;
+
+ GCManager::GetInstance()->ChangeResolution( sX, sY, 32 );
+}
+#endif
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+GCManager* GCManager::mInstance = NULL;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+GCManager* GCManager::GetInstance()
+{
+MEMTRACK_PUSH_GROUP( "GCManager" );
+ if ( !mInstance )
+ {
+ mInstance = new(GMA_PERSISTENT) GCManager();
+ }
+MEMTRACK_POP_GROUP( "GCManager" );
+
+ return mInstance;
+}
+
+//=============================================================================
+// GCManager::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GCManager::Init()
+{
+ //We want a callback to test for reset
+ GetGame()->GetTimerList()->CreateTimer( &mTimer, 500, this );
+}
+
+//=============================================================================
+// GCManager::Reset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GCManager::Reset()
+{
+ mReset = true;
+}
+
+//=============================================================================
+// GCManager::OnTimerDone
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime, void * pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void GCManager::OnTimerDone( unsigned int elapsedTime, void * pUserData )
+{
+ BEGIN_PROFILE( "GCManager" );
+ if ( mReset )
+ {
+ PerformReset();
+ }
+
+ TestForReset();
+
+ END_PROFILE( "GCManager" );
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// GCManager::GCManager
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GCManager::GCManager() :
+ mDoingReset( false ),
+ mTimer( NULL )
+{
+ unsigned int i;
+ for ( i = 0; i < Input::MaxControllers; ++i )
+ {
+ mControllerReset[i] = false;
+ mResetTime[i] = 0;
+ }
+
+ //Set up the reset callback.
+ OSSetResetCallback( ResetButtonCallBack );
+
+#ifdef DEBUGWATCH
+ radDbgWatchAddFunction( "Set Resolution", &ChangeResolutionCallback, this, "GCManager" );
+
+ radDbgWatchAddInt( &sX, "X Resolution (multiple of 16)", "GCManager", NULL, NULL, 1, 40 );
+// radDbgWatchAddInt( &sY, "Y Resolution", "GCManager", NULL, NULL, 16, 480 );
+#endif
+}
+
+//==============================================================================
+// GCManager::~GCManager
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GCManager::~GCManager()
+{
+ if ( mTimer )
+ {
+ mTimer->Release();
+ }
+}
+
+//=============================================================================
+// GCManager::TestForReset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GCManager::TestForReset()
+{
+
+ unsigned int inputCount = 0;
+ PADStatus controllers[PAD_MAX_CONTROLLERS];
+
+ for ( inputCount = 0; inputCount < PAD_MAX_CONTROLLERS; ++inputCount )
+ {
+ controllers[ inputCount ].button = 0;
+ }
+
+ PADRead(controllers);
+ //while( PAD_ERR_TRANSFER == PADRead(controllers) ){ ::radThreadSleep( 32 ); }
+
+ LGPosition lgStatus[ SI_MAX_CHAN ];
+ memset( lgStatus, 0, sizeof( LGPosition )*SI_MAX_CHAN );
+
+ extern bool g_isLGInitialized;
+ if( g_isLGInitialized )
+ {
+ LGRead( lgStatus );
+ }
+
+ rAssert( PAD_MAX_CONTROLLERS == SI_MAX_CHAN );
+ for ( inputCount = 0; inputCount < PAD_MAX_CONTROLLERS; ++inputCount )
+ {
+ if( lgStatus[inputCount].err == LG_ERR_NONE )
+ {
+ controllers[inputCount].err = PAD_ERR_NONE;
+ controllers[inputCount].button = 0;
+
+ if( lgStatus[inputCount].button & LG_BUTTON_B )
+ {
+ controllers[inputCount].button |= PAD_BUTTON_B;
+ }
+ if( lgStatus[inputCount].button & LG_BUTTON_X )
+ {
+ controllers[inputCount].button |= PAD_BUTTON_X;
+ }
+ if( lgStatus[inputCount].button & LG_BUTTON_START )
+ {
+ controllers[inputCount].button |= PAD_BUTTON_MENU;
+ }
+ }
+
+ if ( controllers[ inputCount ].err == PAD_ERR_NONE &&
+ controllers[ inputCount ].button & PAD_BUTTON_B &&
+ controllers[ inputCount ].button & PAD_BUTTON_X &&
+ controllers[ inputCount ].button & PAD_BUTTON_MENU )
+ {
+ if ( mControllerReset[inputCount] )
+ {
+ if ( OSTicksToMilliseconds( OSGetTime() ) - mResetTime[inputCount] > MIN_TIME )
+ {
+ mControllerReset[inputCount] = false;
+ mResetTime[inputCount] = 0;
+
+ //Call reset
+ ControllerReset();
+ break;
+ }
+ }
+ else
+ {
+ if ( mResetTime[inputCount] == 0 )
+ {
+ if(GetGameFlow()==NULL)return;
+ ContextEnum context = GetGameFlow()->GetCurrentContext();
+
+ if ( context == CONTEXT_LOADING_SUPERSPRINT ||
+ context == CONTEXT_SUPERSPRINT ||
+ context == CONTEXT_LOADING_GAMEPLAY ||
+ context == CONTEXT_GAMEPLAY ||
+ context == CONTEXT_PAUSE )
+ {
+ if ( GetInputManager()->GetControllerPlayerIDforController( inputCount ) >= 0 )
+ {
+ mResetTime[inputCount] = OSTicksToMilliseconds( OSGetTime() );
+ mControllerReset[inputCount] = true;
+ }
+ }
+ else
+ {
+ mResetTime[inputCount] = OSTicksToMilliseconds( OSGetTime() );
+ mControllerReset[inputCount] = true;
+ }
+
+ }
+ }
+ }
+ else
+ {
+ mResetTime[inputCount] = 0;
+ mControllerReset[inputCount] = false;
+ }
+ }
+
+ if ( OSGetResetButtonState() )
+ {
+ mReset = true;
+ }
+
+ if ( mReset )
+ {
+ //Bye bye!
+ PerformReset();
+ }
+}
+
+//=============================================================================
+// GCManager::ControllerReset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GCManager::ControllerReset()
+{
+ //TODO: Test if we should reset here or not!
+ //For now...
+ Reset();
+
+ //Potentially, we could reset to the main menu... That might be better.
+}
+
+//=============================================================================
+// GCManager::PerformReset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool displaySplash )
+//
+// Return: void
+//
+//=============================================================================
+void GCManager::PerformReset( bool displaySplash, bool launchIPL )
+{
+ // Wait until the switch is released
+ if ( !mDoingReset && ( mReset ) && ( !OSGetResetButtonState( ) ) )
+ {
+ mDoingReset = true;
+
+ if ( !GetGame()->GetPlatform()->PausedForErrors() )
+ {
+ // If we have troubles with sound, we should shut it down here. Don't foget
+ // to service the sound system enough time for the changes to be reflected.
+
+ if ( GetPresentationManager()->GetFMVPlayer()->IsPlaying() )
+ {
+ GetPresentationManager()->GetFMVPlayer()->Pause( );
+ }
+ else
+ {
+ GetSoundManager()->StopForMovie();
+
+ while( !( GetSoundManager()->IsStoppedForMovie() ) )
+ {
+ ::radMovieService2( );
+ ::radFileService( );
+ SoundManager::GetInstance()->Update();
+ SoundManager::GetInstance()->UpdateOncePerFrame( 0, NUM_CONTEXTS, false, true );
+ }
+ }
+ }
+
+ if ( displaySplash && !launchIPL && DVDCheckDisk() )
+ {
+ //Fade to black
+ // GetGame()->GetPlatform()->DisplaySplashScreen( Platform::FadeToBlack, NULL, 1.0f, 1.0f, 0.0f, tColour(0,0,0), 100 );
+ }
+
+ StopEverything();
+
+ GXDrawDone();
+
+ //
+ // One last TRC when we reset
+ //
+ VISetBlack(true);
+ VIFlush();
+ VIWaitForRetrace();
+
+ //TODO: If we have progressive scan mode enabled, we need to keep it enabled on reset.
+ //This could be tricky.
+
+ if ( launchIPL || !DVDCheckDisk() )
+ {
+ // Reset the system
+ OSResetSystem(
+ OS_RESET_HOTRESET,
+ 0,
+ launchIPL );
+ }
+ else
+ {
+ // Reset the system
+ OSResetSystem(
+ OS_RESET_RESTART,
+ 0,
+ false );
+ }
+ }
+}
+
+//=============================================================================
+// GCManager::StopEverything
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GCManager::StopEverything()
+{
+ //Stop the controllers.
+ StopRumble();
+
+ //Wait for the memory cards to complete.
+ while ( GetGameDataManager()->IsUsingDrive() )
+ {
+ ::radFileService();
+ ::radControllerSystemService();
+ GetGameDataManager()->Update( 16 );
+ }
+
+ ::radControllerTerminate();
+
+ //Shutdown the platform.
+#ifdef RAD_RELEASE
+ //GetGame()->GetPlatform()->ShutdownPlatform(); //This causes a crash in FTech...
+#endif
+}
+
+//=============================================================================
+// GCManager::ChangeResolution
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int x, int y, int bpp )
+//
+// Return: void
+//
+//=============================================================================
+void GCManager::ChangeResolution( int x, int y, int bpp )
+{
+ rAssert( x % 16 == 0 ); //This must be a multiple of 16pixels!
+
+ GetGame()->GetPlatform()->DisplaySplashScreen( Platform::Error, NULL, NULL, 0.0f, 0.0f, tColour(0,0,0), 0 );
+ p3d::context->GetDisplay()->InitDisplay( x, y, bpp );
+}
+
+//=============================================================================
+// GCManager::DoProgressiveScanTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GCManager::DoProgressiveScanTest()
+{
+ //Test for button b being held down and ask if they want progressive mode
+ //Or not.
+ bool doProgressiveQuestion = false;
+
+ if ( VIGetDTVStatus() != 1 )
+ {
+ return;
+ }
+
+ //There's a cable present..
+
+ //If the progressive mode flagis set, then we make the message show also.
+ if ( OSGetProgressiveMode() )
+ {
+ //The flag is set from last time.
+ doProgressiveQuestion = true;
+ }
+ else
+ {
+ unsigned int inputCount = 0;
+ PADStatus controllers[PAD_MAX_CONTROLLERS];
+
+ for ( inputCount = 0; inputCount < PAD_MAX_CONTROLLERS; ++inputCount )
+ {
+ controllers[ inputCount ].button = 0;
+ }
+
+ ::radThreadSleep( 32 );
+
+ PADRead(controllers);
+
+ for ( inputCount = 0; inputCount < PAD_MAX_CONTROLLERS && !doProgressiveQuestion; ++inputCount )
+ {
+ if ( controllers[ inputCount ].err == PAD_ERR_NONE &&
+ controllers[ inputCount ].button & PAD_BUTTON_B )
+ {
+ //Someone is holding B.
+ doProgressiveQuestion = true;
+ break;
+ }
+ }
+
+ ::radControllerSystemService();
+ ::radThreadSleep( 16 );
+ ::radControllerSystemService();
+ ::radThreadSleep( 16 );
+
+ LGPosition lgStatus[ SI_MAX_CHAN ];
+ memset( lgStatus, 0, sizeof( LGPosition )*SI_MAX_CHAN );
+ LGRead( lgStatus );
+
+ for ( inputCount = 0; inputCount < SI_MAX_CHAN && !doProgressiveQuestion; ++inputCount )
+ {
+ if ( lgStatus[ inputCount ].err == LG_ERR_NONE &&
+ lgStatus[ inputCount ].button & LG_BUTTON_B )
+ {
+ //Someone is holding B.
+ doProgressiveQuestion = true;
+ break;
+ }
+ }
+ }
+
+ if ( doProgressiveQuestion )
+ {
+ //Display the question.
+ //TODO: Localization
+ GetGame()->GetPlatform()->DisplaySplashScreen( Platform::Error, "Do you want to display in progressive mode?", 0.7f );
+
+ bool setProgressiveOn = ((GCPlatform*)GetGame()->GetPlatform())->DisplayYesNo( 0.7f, -0.2f, -0.1f, 0.1f, -0.1f );
+
+ if ( setProgressiveOn != ((GCPlatform*)GetGame()->GetPlatform())->GetInitData()->progressive )
+ {
+
+ //Do the TRC delay stuff.
+
+ //Set the screen black
+ VISetBlack( TRUE );
+ VIFlush();
+ VIWaitForRetrace();
+
+ unsigned int i;
+ for( i=0; i<10; i++ )
+ { //Set screen black and wait for several frames
+ VIWaitForRetrace();
+ }
+
+ //Change modes
+ ((GCPlatform*)GetGame()->GetPlatform())->GetInitData()->progressive = setProgressiveOn;
+
+ //Need to re-init the display.
+ p3d::context->GetDisplay()->InitDisplay( ((GCPlatform*)GetGame()->GetPlatform())->GetInitData() );
+
+ for( i=0; i<100; i++ )
+ { //Set screen black and wait for several dozen frames
+ VIWaitForRetrace();
+ }
+
+ VISetBlack(FALSE);
+ VIFlush();
+ VIWaitForRetrace();
+ }
+
+ //Set progressive scan mode
+ OSSetProgressiveMode( setProgressiveOn );
+
+ //Display message that progressive is enabled
+ //TODO: localization
+ if ( setProgressiveOn && OSGetProgressiveMode() )
+ {
+ GetGame()->GetPlatform()->DisplaySplashScreen( Platform::Error, "Screen has been set to progressive mode.\n Press the A Button to continue.", 0.7f );
+ }
+ else if ( !setProgressiveOn && !OSGetProgressiveMode() )
+ {
+ GetGame()->GetPlatform()->DisplaySplashScreen( Platform::Error, "Screen has been set to interlaced mode.\n Press the A Button to continue.", 0.7f );
+ }
+
+ if ( setProgressiveOn == OSGetProgressiveMode() )
+ {
+ //Wait for input or 10 second timeout.
+ unsigned int time = OSTicksToMilliseconds( OSGetTime() );
+
+ bool waiting = true;
+
+ PADStatus controllers[PAD_MAX_CONTROLLERS];
+ PADStatus lastControllers[PAD_MAX_CONTROLLERS];
+
+ unsigned int inputCount = 0;
+
+ for ( inputCount = 0; inputCount < PAD_MAX_CONTROLLERS; ++inputCount )
+ {
+ controllers[ inputCount ].button = 0;
+ lastControllers[ inputCount ].button = 0;
+ }
+
+ LGPosition lgStatus[ SI_MAX_CHAN ];
+ memset( lgStatus, 0, sizeof( LGPosition )*SI_MAX_CHAN );
+
+ while ( waiting )
+ {
+ ::radControllerSystemService();
+ ::radThreadSleep( 32 );
+ PADRead(controllers);
+ LGRead( lgStatus );
+
+ for ( inputCount = 0; inputCount < PAD_MAX_CONTROLLERS; ++inputCount )
+ {
+ if( lgStatus[inputCount].err == LG_ERR_NONE )
+ {
+ controllers[inputCount].err = PAD_ERR_NONE;
+ controllers[inputCount].button = 0;
+
+ if( lgStatus[inputCount].button & LG_BUTTON_A )
+ {
+ controllers[inputCount].button = PAD_BUTTON_A;
+ }
+ if( lgStatus[inputCount].button & LG_BUTTON_START )
+ {
+ controllers[inputCount].button = PAD_BUTTON_MENU;
+ }
+ }
+
+ if ( controllers[ inputCount ].err == PAD_ERR_NONE &&
+ ( !( controllers[ inputCount ].button & PAD_BUTTON_A ) &&
+ lastControllers[ inputCount ].button & PAD_BUTTON_A ) ||
+ ( !( controllers[ inputCount ].button & PAD_BUTTON_MENU ) &&
+ lastControllers[ inputCount ].button & PAD_BUTTON_MENU ) )
+ {
+ waiting = false;
+ break;
+ }
+
+ if( controllers[ inputCount ].err == PAD_ERR_NONE )
+ {
+ lastControllers[ inputCount ] = controllers[ inputCount ];
+ }
+ }
+
+ unsigned int newTime = OSTicksToMilliseconds( OSGetTime() );
+ if ( newTime - time > 10000 )
+ {
+ waiting = false;
+ }
+ }
+ }
+ }
+}
+
+//=============================================================================
+// GCManager::StopRumble
+//=============================================================================
+// Description: Lets get ready to stop rumbling!
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+
+void GCManager::StopRumble()
+{
+ PADControlMotor( PAD_CHAN0, PAD_MOTOR_STOP_HARD );
+ PADControlMotor( PAD_CHAN1, PAD_MOTOR_STOP_HARD );
+ PADControlMotor( PAD_CHAN2, PAD_MOTOR_STOP_HARD );
+ PADControlMotor( PAD_CHAN3, PAD_MOTOR_STOP_HARD );
+}
diff --git a/game/code/main/gamecube_extras/gcmanager.h b/game/code/main/gamecube_extras/gcmanager.h
new file mode 100644
index 0000000..720e1fa
--- /dev/null
+++ b/game/code/main/gamecube_extras/gcmanager.h
@@ -0,0 +1,83 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: gcmanager.h
+//
+// Description: Blahblahblah
+//
+// History: 14/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef GCMANAGER_H
+#define GCMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radtime.hpp> // IRadTimerCallback
+
+//========================================
+// Forward References
+//========================================
+#include <dolphin/pad.h>
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class GCManager : public IRadTimerCallback
+{
+public:
+ static GCManager* GetInstance();
+
+ void Init();
+
+ void Reset();
+ void PerformReset( bool displaySplash = true, bool launchIPL = false );
+
+ //For IRadTimerCallback
+ void OnTimerDone( unsigned int elapsedTime, void * pUserData );
+
+ void ChangeResolution( int x, int y, int bpp );
+
+ void DoProgressiveScanTest();
+
+ // MKR - adding StopRumble, turn off rumble immediately (for use when the GC cover is opened)
+ void StopRumble();
+
+ void TestForReset();
+
+private:
+ static GCManager* mInstance;
+
+ bool mReset;
+ bool mDoingReset;
+
+ bool mControllerReset[PAD_MAX_CONTROLLERS];
+ unsigned int mResetTime[PAD_MAX_CONTROLLERS];
+
+ // Timer for updates.
+ IRadTimer* mTimer;
+
+ GCManager();
+ virtual ~GCManager();
+
+ void ControllerReset();
+ void StopEverything();
+
+ //Prevent wasteful constructor creation.
+ GCManager( const GCManager& gcmanager );
+ GCManager& operator=( const GCManager& gcmanager );
+};
+
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //GCMANAGER_H
diff --git a/game/code/main/gamecube_extras/license.h b/game/code/main/gamecube_extras/license.h
new file mode 100644
index 0000000..332339b
--- /dev/null
+++ b/game/code/main/gamecube_extras/license.h
@@ -0,0 +1,963 @@
+"\xff\x44\x33\x50\x00\x00\x00\x0c\x00\x00\x43\x52\x00\x00\x70\x00\x00\x00"
+"\x00\x9d\x00\x00\x00\x9d\x00\x03\x24\x70\x33\x64\x67\x63\x20\x76\x65\x72"
+"\x73\x69\x6f\x6e\x20\x31\x2e\x30\x2e\x31\x20\x28\x77\x69\x74\x68\x20\x41"
+"\x54\x47\x20\x32\x2e\x30\x29\x00\x00\x40\x2e\x2e\x5c\x2e\x2e\x5c\x2e\x2e"
+"\x5c\x6c\x69\x62\x73\x5c\x70\x75\x72\x65\x33\x64\x5c\x74\x6f\x6f\x6c\x73"
+"\x5c\x63\x6f\x6d\x6d\x61\x6e\x64\x6c\x69\x6e\x65\x5c\x62\x69\x6e\x5c\x70"
+"\x33\x64\x67\x63\x20\x6c\x69\x63\x65\x6e\x73\x65\x65\x2e\x70\x33\x64\x00"
+"\x00\x00\x28\x52\x75\x6e\x20\x61\x74\x20\x4a\x75\x6c\x79\x20\x30\x36\x2c"
+"\x20\x32\x30\x30\x33\x2c\x20\x32\x31\x3a\x33\x33\x3a\x33\x35\x20\x62\x79"
+"\x20\x74\x63\x68\x75\x00\x00\x00\x00\x70\x00\x00\x00\x00\xf1\x00\x00\x00"
+"\xf1\x00\x03\x28\x70\x33\x64\x69\x6d\x61\x67\x65\x20\x76\x65\x72\x73\x69"
+"\x6f\x6e\x20\x31\x2e\x34\x2e\x30\x20\x28\x77\x69\x74\x68\x20\x41\x54\x47"
+"\x20\x32\x2e\x30\x29\x00\x00\x00\x90\x2e\x2e\x5c\x2e\x2e\x5c\x2e\x2e\x5c"
+"\x6c\x69\x62\x73\x5c\x70\x75\x72\x65\x33\x64\x5c\x74\x6f\x6f\x6c\x73\x5c"
+"\x63\x6f\x6d\x6d\x61\x6e\x64\x6c\x69\x6e\x65\x5c\x62\x69\x6e\x5c\x70\x33"
+"\x64\x69\x6d\x61\x67\x65\x20\x2d\x62\x20\x34\x20\x2d\x63\x20\x2d\x6f\x20"
+"\x6c\x69\x63\x65\x6e\x73\x65\x65\x2e\x70\x33\x64\x20\x2e\x2e\x5c\x2e\x2e"
+"\x5c\x2e\x2e\x5c\x65\x78\x70\x6f\x72\x74\x61\x72\x74\x5c\x66\x72\x6f\x6e"
+"\x74\x65\x6e\x64\x5c\x73\x63\x72\x6f\x6f\x62\x79\x5c\x72\x65\x73\x6f\x75"
+"\x72\x63\x65\x5c\x69\x6d\x61\x67\x65\x73\x5c\x5f\x67\x63\x5c\x6c\x69\x63"
+"\x65\x6e\x73\x65\x2e\x70\x6e\x67\x00\x28\x52\x75\x6e\x20\x61\x74\x20\x4a"
+"\x75\x6c\x79\x20\x30\x36\x2c\x20\x32\x30\x30\x33\x2c\x20\x32\x31\x3a\x33"
+"\x33\x3a\x33\x35\x20\x62\x79\x20\x74\x63\x68\x75\x00\x00\x00\x01\x90\x00"
+"\x00\x00\x00\x3d\x00\x00\x41\xb8\x0c\x6c\x69\x63\x65\x6e\x73\x65\x2e\x70"
+"\x6e\x67\x00\x00\x00\x36\xb0\x00\x00\x02\x80\x00\x00\x01\xe0\x00\x00\x00"
+"\x04\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00"
+"\x00\x00\x00\x00\x01\x90\x01\x00\x00\x00\x35\x00\x00\x41\x7b\x0c\x6c\x69"
+"\x63\x65\x6e\x73\x65\x2e\x70\x6e\x67\x00\x00\x00\x36\xb0\x00\x00\x02\x80"
+"\x00\x00\x01\xe0\x00\x00\x00\x04\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00"
+"\x00\x01\x00\x01\x90\x02\x00\x00\x41\x46\x00\x00\x41\x46\x00\x00\x41\x36"
+"\x89\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\x00"
+"\x02\x80\x00\x00\x01\xe0\x04\x03\x00\x00\x00\xc7\xff\xc1\xd7\x00\x00\x00"
+"\x30\x50\x4c\x54\x45\x00\x00\x00\x03\x04\x08\x06\x11\x0e\x0f\x25\x21\x09"
+"\x3c\x40\x0f\x5d\x5e\x54\x53\x18\xa0\x2d\x19\x24\x98\x5b\xf0\xeb\x70\xcf"
+"\xbf\x1d\x11\x77\x80\x19\xb3\xc0\x44\xe0\xed\x1d\xb4\xc1\xd6\xd6\xd7\x9b"
+"\x02\x02\xf4\x00\x00\x20\x00\x49\x44\x41\x54\x78\x9c\xed\x7d\x5b\x6c\x1b"
+"\x47\xba\x66\xa9\x9b\xcd\x41\x44\xaa\x8f\x92\xe0\x78\x10\xbf\x0c\xe3\x1b"
+"\x30\x4e\xcc\x61\x2c\x5b\xe7\xf8\x42\x86\xb0\x95\x27\x9b\x02\xd7\xb6\x0e"
+"\x06\x90\x49\xc8\x36\x86\xb1\xf4\xb0\xce\x9e\x03\x38\xc8\xbc\x04\x38\x83"
+"\x09\xf2\xb4\xc8\x24\x80\x03\xf2\x49\x89\x01\x05\xe4\x13\x37\xb2\x9f\x18"
+"\x0a\x44\x3c\x7e\xb2\x28\x08\x16\xf9\x44\x56\xa1\x57\x14\x67\x17\x90\xaa"
+"\xf7\x32\x58\xe5\xed\x70\xff\xbf\xaa\x9b\x17\x5d\x7c\x95\xed\x38\xae\xcf"
+"\x6e\xb1\xbb\xba\xbb\xaa\xfa\xeb\xbf\x2e\xdd\x7f\xff\xff\x4f\x88\x82\x82"
+"\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82"
+"\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82"
+"\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82"
+"\x82\x82\x82\x8b\xbe\x37\xf6\x0e\x0d\xbe\xe8\x4a\xbc\xbc\x00\xf6\x86\x86"
+"\xae\xbc\xe8\x5a\xbc\xac\xe8\xdb\x03\xec\x0d\xa7\x3e\xbc\xfe\xa2\x2b\xf2"
+"\x72\x02\x85\x0f\xd8\xfb\xf8\xe3\x8f\x15\x81\x8f\x0f\x21\x7c\xa9\xd4\xc7"
+"\x12\x2f\xba\x36\x2f\x1d\x34\x21\x7c\x1f\xb7\xf1\xa2\xeb\xf3\x92\xe1\x0d"
+"\x14\xbe\x0f\x3f\xee\xc2\x9e\x17\x5d\xa5\x97\x08\x7d\x1b\x84\x4f\x12\x18"
+"\x78\xd1\xd5\x7a\x59\xb0\xc7\x1d\x37\x7a\x91\x7a\xfd\x45\x57\xec\xa5\x00"
+"\x0e\xbb\x6d\xf6\x52\xd7\xff\x47\x87\xc0\xa9\xff\xa2\xa6\xd2\x0f\xc5\x9e"
+"\xbd\x3d\xc2\x97\xfa\xb4\xab\x1d\x4f\xcd\x28\x11\x7c\x18\xb4\x6e\xf6\xae"
+"\xb7\x86\x5b\xd7\xfe\xd0\xd9\x9e\xca\x28\x11\x7c\x18\xb4\x5d\x5d\x7d\xde"
+"\xf5\x54\xea\x6f\xce\x24\xf0\xd3\x9f\xe0\xcf\x70\x56\x89\xe0\xc3\xf0\x86"
+"\xb7\x67\xd4\xb8\x7e\xe0\xba\x24\xb0\x85\x3f\x87\x95\x08\x3e\x14\x1a\x91"
+"\x2d\xb6\xd5\x42\x91\xbb\xee\x8a\xe2\xc7\x3f\x09\x3e\x77\x29\x11\x7c\x18"
+"\xfa\x24\x81\x9f\xfe\x47\xfb\xf9\xed\x53\x48\xb8\xbe\xfe\xa9\x1c\x85\x95"
+"\x08\x3e\x1c\xbf\x76\x5a\xac\xd3\xf7\x7d\xbc\xfe\xe1\xa7\x1f\x5f\xff\x29"
+"\xf5\x13\xb4\xe5\xa9\xef\x94\x08\x3e\x1c\xd8\x09\x7e\xba\xee\xf6\x82\xeb"
+"\xa9\x16\xb0\xd9\xfa\xe9\xba\x20\x30\xfb\xcd\x54\xe6\x92\x12\xc1\x07\xa3"
+"\xef\x43\xd9\x6c\x51\x0e\xaf\x7d\xfc\xd3\xfa\xfb\xeb\x3f\xad\x7f\xfa\xe1"
+"\xa7\x7f\xc3\x26\x9c\xfb\x5a\x89\xe0\xc3\x81\x04\xca\xd6\xdb\x6a\x5d\x6f"
+"\xa5\xae\xad\x5f\x5b\x4f\x7d\x28\x66\x87\x53\x39\x25\x82\x8f\x00\x98\x09"
+"\x5e\xff\x49\x76\x7f\xff\x6b\xfd\x6f\x1f\x5f\x13\xe4\x89\x1e\x71\x2a\x97"
+"\xcb\x28\x11\x7c\x28\xa0\x13\xbc\x7e\xed\x27\x41\xe0\xf0\x4f\x1f\x7e\xdc"
+"\x79\x32\xb9\x7e\x3c\xa7\x44\xf0\x11\x80\x9d\x60\xea\x3f\xff\x04\x04\x7e"
+"\xf8\x71\xd7\x1b\x99\xd4\x61\x02\x12\x88\x22\xf8\x9d\x12\xc1\x07\x03\x59"
+"\x4b\xc1\xbc\xa5\x9b\xbe\x2b\xef\x11\xa2\x03\x7f\xb9\xec\x7f\x51\x22\xf8"
+"\x30\x88\x99\x60\xea\xda\xf5\x36\x7b\xd7\xaf\x04\x30\xfd\x9f\x90\x40\x25"
+"\x82\x0f\x87\x57\x3e\xcb\xb9\xf2\x77\xfd\x30\x0a\x5c\xdf\xde\xd4\x1f\x73"
+"\x4a\x04\x1f\x09\xd0\x09\x5e\x6b\xb5\xfe\xf6\xa1\xdb\xf5\x11\xf1\x96\x4b"
+"\xd2\x97\x53\x03\xf1\x23\x00\x86\xde\x94\x1c\x7c\xaf\xbc\x8d\xdb\x6f\x74"
+"\xd1\x27\x07\x62\xf5\x44\xfc\x40\xfc\xda\x1d\x39\x02\xb8\xa5\x0d\x7f\x98"
+"\xeb\x81\x12\xc1\x87\xc1\x2b\x47\x0e\xd9\xf5\x05\x36\xd0\xa7\x44\xf0\xe1"
+"\xe8\x73\x47\x0e\xa2\xa7\xae\x1f\x10\xa4\xa5\x33\x6d\xf9\x53\x22\xf8\x70"
+"\x0c\x4b\xfa\xc8\xdb\xff\x94\xbb\x3c\xe5\xb2\xd6\x25\x82\xea\xbd\xe0\xa3"
+"\x41\x3b\x90\x9b\x91\x02\x78\xf3\xcd\x61\x87\xbc\xd4\x14\x30\xa9\x5e\xca"
+"\x3c\x1a\xf6\x4c\xe5\xfe\x4d\x0a\x60\x80\x90\x5d\x59\x41\xe0\x4c\x4a\x89"
+"\xe0\xa3\x42\x1b\xce\xe6\xa4\xe0\xdd\xc0\xcd\xe3\x52\x16\xfb\xf0\x57\x89"
+"\xe0\xa3\x60\x68\x2a\x73\x73\x4a\x36\x5c\x31\x20\x0b\x11\x0c\x88\x47\x62"
+"\x18\x88\xd3\xff\xaa\x44\xf0\xc1\xd0\xdb\x02\x98\xcb\x4c\x60\xc2\x71\x47"
+"\x16\x91\xc8\x0c\x74\x8f\x8a\xc0\x07\x43\x08\xa0\x33\xf6\xce\x60\x82\x86"
+"\x2d\x18\x7e\x3f\x41\x11\x9c\x18\x4e\xbd\xfd\xfa\x63\xe1\x55\xe3\x5b\x08"
+"\x60\x26\x87\x14\xc2\xc0\x31\x81\x49\xd0\x9e\x2f\x13\x29\x89\x99\xe1\xb1"
+"\xc7\xc5\xbf\x04\x5e\xe8\xf5\x3c\x77\xc8\x1e\x70\x8f\x17\xa4\x2e\xa0\x67"
+"\x85\x08\xee\xce\xe5\xf0\x3b\xe9\x83\xc8\xe9\x3f\x7f\xfe\xd8\xf8\x4f\x2f"
+"\xf8\x8a\x9e\x2f\x64\x0f\x78\x99\xf4\x4d\xe5\xa6\x89\x9e\x15\xbd\xe0\x41"
+"\xd9\x84\x7f\x8d\x4d\xf9\xf7\x9f\x7f\xfe\xd9\x70\xea\x0f\xa9\x54\xea\x5f"
+"\x52\xa9\xab\x29\x81\xdf\x4b\xa2\xfe\x8c\xeb\xf8\xf7\xf7\xb0\xe7\x33\x58"
+"\x85\xe5\x2a\xa4\xff\xfb\x0b\xbe\xa4\xe7\x0b\xd9\x03\x4e\xe0\x90\x71\x93"
+"\x68\xd9\xec\xb7\x04\x99\xcb\x22\x81\xbf\x72\x08\x4c\xb5\x5a\x97\xaf\xb5"
+"\xd6\x0f\x88\x1f\xc4\x55\x60\x0d\x88\xfa\x13\xac\xae\x0f\xc3\xdf\xd4\xb5"
+"\xd6\x1f\x52\xad\xf5\xab\xc3\xad\x16\x72\x7b\xee\x05\x5f\xd2\x73\x85\x33"
+"\x04\x4f\x60\xc7\x37\x83\x33\x98\xef\x88\x64\x2e\xd0\x21\x10\x98\xfb\xdf"
+"\x40\xa0\xb7\xd5\x9a\x70\x09\xfc\xc3\x75\x20\xea\x32\xae\xff\xbf\xc3\xad"
+"\xd6\x25\xd8\x0b\xe4\xa5\x7e\x03\xcb\xab\x46\xa0\x33\x07\xfc\x57\x42\xfe"
+"\x98\xfb\x1a\x59\xcc\x84\x44\x1f\x88\x04\xfe\x93\x43\xe0\xdb\xd7\x5a\x81"
+"\xdf\xb5\x2e\x6b\xd7\xd6\x03\xd7\xfe\x7e\xad\x95\x6a\x5d\xfd\xec\xda\x3a"
+"\x10\x35\xf1\x7e\xeb\x0a\xec\x7a\xbf\x35\x71\xa0\xf5\xb7\xf7\xae\xb5\xae"
+"\xbc\xd1\xfa\xe9\x9f\x5f\x31\x02\xdd\x39\x20\xb6\xde\xcc\x8c\x43\x60\xdf"
+"\xc1\xf4\xbf\x1d\x76\x74\x4b\x48\xe0\xc4\xfb\xeb\x7b\xbd\xad\x2b\xa1\xf7"
+"\xd7\xdf\xbe\x36\xf1\xbb\x56\x5f\xeb\xea\x95\xd6\x10\x12\xf5\x0f\x2d\xe2"
+"\x6d\x4d\xc0\x2e\xad\xf5\x19\xb0\xf9\xf7\xc1\x6b\xeb\x57\x5f\x31\x02\x41"
+"\x00\xe1\x29\x38\x7b\xf9\x3d\x0d\x27\x31\x40\x60\x5a\xb0\x98\x03\x31\x24"
+"\xde\x6c\x3a\x9d\x45\x02\xff\xf4\xfe\xfa\xef\x0f\xb4\xfe\xf0\xef\xef\xaf"
+"\x5f\x79\x73\xe8\x77\xad\xc1\x3d\x57\x0f\xac\x7b\x81\xa8\xcf\x7e\xd3\x4a"
+"\x05\x5a\x7f\xdf\xd5\xba\x3a\xd4\x4a\xfd\xe9\xfd\x16\xc8\x23\x0a\xe6\xab"
+"\x44\xa0\xfb\x14\x3c\x74\x49\xcb\x1d\x1e\xd4\xfa\x86\xff\x38\x03\x13\xc0"
+"\x4f\x32\x97\xc5\xce\x29\x47\x02\x81\xc0\x3f\xec\x12\x04\xa6\xf6\xa4\x7e"
+"\xd7\x3a\xbc\xf7\x73\x6f\x6b\x18\x44\xed\xcf\xbf\x69\x5d\x9d\x68\xad\xef"
+"\x42\x89\x14\x04\x9e\x7b\xf5\x08\x9c\xca\x7d\x0b\x02\x38\x43\x06\xfb\xf6"
+"\x1e\x98\x09\x78\xd3\x37\x03\x90\x2a\x86\x60\x82\xf6\x5f\x6f\x1f\x98\x11"
+"\x04\x8a\x71\x16\x08\xbc\xfa\xf9\x67\xbf\x83\x21\xe4\xf3\x37\x5a\xad\xa3"
+"\x57\xae\x02\x81\xc3\xc3\xad\x54\x87\xc0\xff\xfd\xea\x11\x98\xcd\x5d\x86"
+"\x27\xde\x7f\x83\xf6\x0a\x4d\x38\xe0\x9d\x09\xa0\x72\x69\x2a\xd4\x3e\x40"
+"\x77\x08\x04\x74\x13\x38\xf1\x0f\xeb\x7b\xaf\x8d\x01\x81\xeb\xc0\xa4\xd7"
+"\x21\x70\x1d\x46\x98\x57\x8d\x40\x18\x27\x86\x73\xd9\x6f\xe1\xe9\xd5\x3b"
+"\x93\x05\xda\x02\x53\x30\x0d\xdc\xd3\x47\xb4\xf7\xdc\x03\xb6\x26\xf0\x4f"
+"\xd7\x60\xcc\x58\x07\x02\x5b\xad\xff\x7b\xb5\x4d\xe0\x6f\x5a\xaf\x26\x81"
+"\xb9\xe1\xd4\x15\xa2\x05\x86\x03\xe4\x40\xe8\x93\x4b\x44\xfb\x6e\x10\x9e"
+"\x82\x03\xb8\x7b\x6f\xc0\x25\x70\x68\xb8\x97\xc0\x56\x60\xb8\xb5\x3e\x86"
+"\x04\xee\xfd\x73\x9b\x40\x98\x28\xbe\x6a\x04\x3a\x4d\x18\x1e\x7d\x07\x53"
+"\x7d\xe2\xed\x41\x80\x1c\xcf\x0c\x92\xc9\xaf\xc5\x2b\x95\xa9\x9b\xbf\x96"
+"\x04\xae\xbf\xae\xf7\x36\xe1\x56\xa8\xaf\x75\x05\x08\xf4\xb6\x8e\x76\x08"
+"\xec\x7b\xf5\x08\x24\xf8\x31\xaf\x7c\x97\xff\xc7\xcb\x1f\x06\xa6\xf0\x5d"
+"\xc2\xc1\xcc\x65\xaf\x78\xa0\x03\x42\x33\xee\x28\x7c\xf5\x40\x2f\x81\xd7"
+"\xfe\xfe\xfe\xdf\x75\x68\xc2\xa4\x95\x02\x02\xff\x65\x58\x10\x18\xba\xf6"
+"\xea\x11\xb8\x2b\x3b\xb3\x4b\xbc\x08\xbc\xf2\xc7\x5c\x76\x62\x68\x12\xc6"
+"\x5f\x3d\x8b\xba\x10\xb1\x77\x77\x46\xcc\x03\xcf\xc3\xd3\xc7\x81\x56\xea"
+"\xdf\xaf\xad\xff\xf3\xe7\x57\xde\x6f\x0d\x03\x45\xbf\x83\xe7\xe2\xab\x40"
+"\x66\x00\x7a\x3d\xbd\xb5\x7e\xad\xf5\x6f\x7f\xba\xb6\x7e\xf8\x1f\x5e\x3d"
+"\x02\xfb\xa6\x32\x97\xa6\x5c\x2d\xe6\x0c\x39\x88\x6f\xb3\x3e\x49\x67\xb2"
+"\x13\x62\x6f\xbf\x9c\x07\xc2\x63\xee\xff\x91\x2f\x13\x52\x7f\x86\xf1\xe4"
+"\x27\x90\xc3\xbe\x6b\xeb\xff\xf8\xf9\xf9\x56\xeb\xef\xbf\x59\xbf\x1a\x12"
+"\x0f\xc8\xbb\x60\x3c\x01\x2a\x3f\xff\x9f\xaf\x16\x81\x1d\x11\x14\xed\xb8"
+"\x6f\x88\x60\x4f\x98\xce\xc8\x89\xcc\x41\xd9\x84\x77\x89\xb7\x08\x38\x46"
+"\x88\xd9\x1e\xb6\xe1\xf7\x88\x76\x15\x5f\x26\xfc\xdd\xdb\x4a\xc1\xb3\x9c"
+"\x24\x70\x3d\x04\x04\xb6\xfe\xe3\xd5\x22\xb0\x47\x04\xf7\x4a\x87\x27\xc3"
+"\xae\x1a\x64\x4a\x12\x18\x80\xd5\x03\xa9\xcb\xda\xd0\xd0\xd1\x3f\xe9\x57"
+"\x86\x2e\xe3\x2b\x42\x7c\xf1\x37\x31\x3c\x3c\x04\x53\xea\x54\x40\x23\xc3"
+"\x57\xfa\x52\xa9\xa3\xf1\x5d\x57\x3e\xff\xaf\xff\xf3\xd5\x22\xb0\x47\x04"
+"\x7f\x2d\x95\x4a\xe4\x0d\x47\xad\x31\x2c\x9b\xf0\x95\xa1\xa1\x2b\x83\x44"
+"\x7f\x8f\xf4\x5d\xfd\x2c\x00\x9d\xa4\xfb\xe6\xf9\xfc\xeb\x83\xfb\x27\xae"
+"\xad\x5f\x3d\x3f\x74\xf8\xf5\x37\x0f\xf7\xf5\xfd\xf3\x67\x81\x03\xaf\x5a"
+"\x1f\xd8\x2b\x82\xc7\xd3\x33\x81\x9e\x9d\xc7\x73\x33\x57\x25\x59\x63\x63"
+"\xbf\xff\x1c\xfe\x7f\xfe\x67\x5c\xe9\xc2\xbf\xf7\xbd\xb9\xe9\x95\xfe\xa5"
+"\x17\x73\x25\x2f\x0a\xdd\x22\x28\x5f\xe5\x77\x70\x30\x37\x73\xe0\xc1\xfa"
+"\x8f\xcf\x0e\x0f\x6f\x4c\x7a\xd5\x94\x4a\x3d\xbd\x60\xb6\x8b\xc0\x41\x31"
+"\x8a\x5c\xda\xf3\x40\x0c\x0d\x0d\x6d\x4c\x7a\xfb\xc5\x5d\xcb\x8b\x41\x8f"
+"\x08\x5e\x22\x07\xae\x07\x88\x36\x05\xbf\x37\xf1\x15\x43\x76\xe6\xcd\xd4"
+"\x95\x57\x4d\xd3\xfb\x98\xe8\x11\xc1\xef\xde\x86\x3f\x1f\xe7\x70\x32\x8d"
+"\x1f\x27\x78\xb3\x37\xa7\xd2\x37\x1f\x9a\xc5\x2b\x8e\x9e\xb9\xe0\x5e\x6c"
+"\xc8\x72\x15\x76\xfd\x3a\x33\x9c\xcd\x85\x1e\x9a\xc3\x2b\x8e\x6e\x11\xcc"
+"\x78\x3b\x9f\xa8\x86\x88\xfe\xc9\xcd\xe3\x19\x25\x80\x0f\x45\x97\x08\xde"
+"\xec\x10\x98\x9b\xd0\xff\xa8\x04\xf0\x91\xd0\x25\x82\x37\xb5\xae\x29\x0d"
+"\x74\x7f\x4a\x00\x1f\x09\x1d\x11\xfc\x2e\xe0\xed\x30\x98\x55\x02\xf8\x88"
+"\xe8\x12\x41\xf1\x69\x6f\x5b\x06\x95\x00\x3e\x22\x7a\x1f\x47\xda\x50\x02"
+"\xf8\xa8\xe8\x99\x0b\x76\x75\x82\x4a\x00\x1f\x15\x5b\x89\x60\x66\x48\x09"
+"\xe0\x23\x63\x0b\x11\xcc\x2a\x01\x7c\x1c\x6c\x16\x41\x25\x80\x8f\x85\x4d"
+"\x22\xa8\x04\xf0\x31\xb1\x51\x04\x95\x00\x3e\x26\x36\x8a\xa0\x12\xc0\xc7"
+"\xc5\x06\x11\x54\x73\xc0\xc7\x45\xaf\x08\x2a\x01\x7c\x7c\xf4\x88\xa0\x12"
+"\xc0\xc7\x47\x97\x08\x66\xd5\x53\xf0\x93\xa0\x23\x82\xea\x35\xcc\x13\xa1"
+"\x2d\x82\x4a\x00\x9f\x10\xae\x08\x2a\x01\x7c\x42\x38\x22\xa8\x04\xf0\x89"
+"\x81\x22\x98\x55\x02\xf8\xe4\x00\x11\xbc\x3c\xa5\xe6\x80\x4f\x81\x5d\xd9"
+"\xef\xf6\x0e\x1d\x50\x02\xf8\xc4\x00\x11\xcc\x7d\x9c\x53\x02\xf8\xe4\x90"
+"\x6e\x63\x94\x00\x3e\x31\x50\x04\x73\x4a\x00\x9f\x02\x28\x82\x4a\x00\x9f"
+"\x02\x7d\x53\x39\x15\x1b\xf2\xa9\xf0\xc6\x50\xe0\x45\x57\x41\x41\x41\x41"
+"\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\x41\xe1\x25\x85\x9e\x4b"
+"\xa7\x95\x86\xe2\xc9\xa1\x67\x33\xb9\x5c\x56\x19\x9c\x3e\x31\xa6\x72\xb9"
+"\x2b\x6f\x4e\xe5\xa4\x7b\x67\x85\xc7\x46\x7f\x46\x78\x81\xf0\x0a\xdb\x5d"
+"\x81\xd4\x04\xfe\xed\x4b\x09\x23\xf2\x03\x13\x32\xed\x91\x54\x18\x7d\xdd"
+"\x77\xc1\x9b\xeb\xda\x33\x7c\x85\xbc\xf9\x54\xd5\x94\xd0\x73\xa1\xde\x7c"
+"\x9d\xe4\x54\xa8\xef\x11\x75\x2c\xc7\x77\xbc\xa5\x4d\x3a\xea\x9d\x83\x8e"
+"\x2b\x21\x32\x99\x4e\x4f\x88\x1f\x34\x27\xdf\x9d\x41\x07\x39\x7a\x56\xa6"
+"\x79\xd3\x02\x37\xb6\xcb\xcb\x9b\xfe\xa2\xbd\xae\x65\xd3\xed\xf5\xbe\x6c"
+"\x06\xfa\x89\x47\xa9\x0d\xfa\x04\x85\x1e\x79\xbb\xe4\xfe\xf4\xb4\x37\xbb"
+"\x69\xef\xee\x74\x2e\x9d\x9b\x7e\x94\xec\x09\xd9\x7c\xf6\x53\x42\xcf\x7d"
+"\xeb\xac\x39\x8a\x46\x2f\xd4\x26\x83\x82\xf9\x26\x72\x96\x9d\x9e\xfc\x0a"
+"\x36\x1c\xd6\xbc\x99\xc0\x6b\xe9\xc1\x83\x5b\xd7\x01\x64\xc0\x9b\x9e\xee"
+"\x6c\xf4\xa7\xdb\x7e\x10\x26\x33\x13\x7d\x93\x99\x5e\xb7\x08\xde\xad\xeb"
+"\xe3\x4d\x7f\x43\x76\x6d\x2e\xc0\x49\x06\x02\xc9\x5b\x1b\xf7\x7a\xd3\x33"
+"\x64\x17\xec\x7f\x38\xf6\x3e\x03\x02\xfb\xdb\xfa\xc5\x83\x52\x44\xde\xba"
+"\x31\x38\x99\x0e\x91\x49\xf8\xf9\x0b\xd4\x2d\xd0\x0f\x25\xbe\x96\x99\x12"
+"\x52\xe1\xfd\x92\x00\x81\x64\x6b\x59\xea\x9f\x86\x96\x3a\xd8\xd9\xe8\x10"
+"\x88\x57\xbf\x89\x95\xc9\xad\xeb\x23\x8e\xcd\x6e\x72\x41\xe1\x26\x43\xbf"
+"\xf2\xda\x86\x3b\x41\xf0\xd6\xf4\x4d\x4e\x3f\xec\x4a\x01\x33\x90\xd1\x4e"
+"\x7b\x9c\x39\x96\xc3\xae\x0b\x1d\xeb\x7a\xd3\x82\xca\xc9\xf7\xe0\xd2\x2f"
+"\x91\xec\x5f\xc8\xb1\x1b\x58\x59\x2f\x08\xe2\x31\x20\x14\x1b\xa4\x77\x5a"
+"\x10\x38\xb5\x75\x4e\xd3\x18\x56\xbd\xb3\xd1\x21\xf0\xad\x34\xd6\x3a\xdb"
+"\x73\x74\x5f\xef\x66\x1b\x82\xa9\xcd\x05\x74\x25\x6f\x24\xb0\x2f\x7d\x03"
+"\x12\xfa\xbf\xd8\x74\xce\xe6\x4c\xb6\xed\x7b\x9e\x02\xe8\xc5\x65\x77\x66"
+"\x37\xb0\xd4\x97\x9d\xc6\x84\x14\xd6\xf6\x52\x1f\xb4\x95\xd7\x32\x20\x87"
+"\xa2\x59\x4e\x61\xb5\xe5\xf1\xaf\xa5\x37\x49\x87\x84\xde\xdd\x88\x70\xa3"
+"\x43\xa0\x6c\xbd\x93\x3d\x27\xee\x4e\x93\x2d\xe1\xdd\xba\x2d\x76\x25\x6f"
+"\x24\x50\xee\xf2\xfe\x65\x9b\x0b\xec\xc2\xe4\x8d\x67\xe0\x5c\x25\x87\x4d"
+"\xe3\x1b\xd9\x42\xa6\xdb\x55\xba\xe4\x95\x0c\x40\x91\x7d\xb2\x5f\xfb\xad"
+"\x73\xf7\x84\x04\xe6\x66\x60\xda\x13\x98\xca\x05\xa0\x73\xff\x96\xec\xca"
+"\xee\x06\x09\x9e\x84\xc9\xb8\x06\x59\xc0\xe0\x21\x37\xfa\xd3\x07\xd3\x22"
+"\x70\x83\xd3\xef\x78\xd1\x85\x34\x0c\x49\x53\x37\x26\xd3\x13\x70\x62\x2e"
+"\x95\xbb\xe9\xcd\xe5\xc8\xc1\xcc\xe4\x0d\xf4\x8b\x37\x25\x7c\x82\x62\xc1"
+"\xbb\x89\x38\x74\x82\x40\x72\xbf\x8c\x3f\xe2\x24\x7b\x61\xa4\xc3\x56\x91"
+"\x4d\x65\x33\x30\x9a\x88\x26\xd3\x2f\x2b\x38\x21\x4b\x86\xec\xb3\x37\xbc"
+"\xd9\x29\xec\xb3\x3b\x09\xa2\xe4\xfe\x34\x0c\x64\x53\x90\x7e\x10\xf3\xd6"
+"\xb2\x37\x9d\x0c\x9e\x0e\x99\x69\x10\x17\xa8\xea\x04\x94\xe4\xb6\x02\xe8"
+"\xf9\x80\x43\xa8\xd9\x04\x3a\x76\x91\x23\xab\xcb\x2e\x12\x08\x42\xd9\x97"
+"\xfe\x12\x5b\x44\xf6\xdb\xc9\xcc\x9b\xd9\x74\x36\x9b\x0e\x0c\xa7\x67\x2e"
+"\x4d\xc1\xb5\x1c\xbb\x71\x30\x3d\x81\x1b\xfd\x98\x2c\xe2\xff\xb8\xc3\xb6"
+"\x37\x73\x20\xfd\x15\x5c\x46\x26\x7d\x43\x9b\x4a\xa7\x02\x90\xfb\x6f\x33"
+"\x62\x80\xdd\x8d\xf7\xeb\x2b\x97\xa9\x49\x3c\xf4\x72\x16\xa5\xff\x9b\x3e"
+"\xd9\xb1\x38\xc9\x53\x69\x41\xa0\x28\x31\x9b\x91\xcc\xbd\x25\x26\x08\x80"
+"\x63\x37\x0e\xe0\x20\x0d\xa3\xb5\x18\xb3\xa7\xbb\x12\xfa\xd3\xc3\xe9\x2f"
+"\xbc\x93\x99\x94\x17\x72\xd5\xd3\x97\x27\x33\x90\x95\x9b\xc1\x53\x12\x38"
+"\x41\xbc\x4e\x0d\x8e\xbb\x04\xf6\x63\xd5\x7b\x08\xf4\x4e\xb9\x37\x0b\x09"
+"\xec\x83\x24\xd8\xd3\x3f\xed\xc5\x71\x71\x62\x77\xfa\x5b\xf8\x4b\x50\x88"
+"\x61\xc9\x4e\xe8\xb0\x5b\x08\xb0\x48\xc6\x8b\x77\x08\x7c\xeb\x4b\x68\xcd"
+"\x20\x9d\x97\x26\x33\xa2\x81\x4f\x8a\x4e\xf6\xb5\x74\x2e\xf3\x06\x5c\x4a"
+"\xff\xb4\x64\x6a\x66\x0a\x76\x81\xc0\xe3\xc9\xd9\x4c\xbf\x6c\x76\x4e\x32"
+"\x56\x4b\xf4\xcb\x33\xde\x34\xf0\x28\x28\x9f\x74\x09\x04\xe9\x84\x1c\x27"
+"\xd3\x53\x37\x76\xa5\x81\xba\xaf\xba\x12\xde\x82\x83\x6f\x88\x26\x0c\x8b"
+"\xb8\x3c\xa8\x6f\x26\x90\xfd\x72\x87\x08\x94\x23\xd3\x6e\x97\xc0\xc9\x6f"
+"\x1c\x02\x2f\xb9\x04\xbe\xd6\x9e\x9b\x89\x3e\x10\x2b\x93\x21\xc7\x26\xfa"
+"\x33\xa9\xa9\xf4\x37\xc0\x85\xd7\x25\x70\x5a\x4f\xa7\x52\x40\x98\xec\x01"
+"\x64\xce\x6d\x02\x27\x67\x52\x30\x39\x84\x4b\x80\xeb\x69\x13\x08\x39\x06"
+"\x06\x45\x8e\x13\x6d\xa6\x84\xc0\x63\xe7\x7b\x30\x9d\x95\x75\x73\x92\xdb"
+"\x04\x5e\xc2\x12\x26\x6f\x74\x13\x08\xed\x06\xa4\x11\x2a\xf6\x7a\x1f\xee"
+"\x87\x36\xde\x49\x90\xdd\xb9\x43\xe0\xe4\x97\x42\x04\xf0\x66\xef\xc0\xa8"
+"\x22\x08\x9c\x26\xe4\x72\x47\x02\xfb\xa0\xe0\x0d\x12\x38\x9c\x4a\x3b\xd5"
+"\x14\x04\x4e\x62\x2b\x9a\xc8\x0e\xbe\x96\x49\xa5\x52\x13\x0e\x53\x52\x02"
+"\xbd\x40\x60\xea\xf2\xd6\x04\x66\x67\x60\xdf\xa0\x94\xbb\x2e\x09\xc4\x5b"
+"\xf3\xdb\x8c\x33\x4a\x8b\xb6\x3a\x28\x7a\x0c\x28\x58\x4f\x3b\x23\x86\x93"
+"\xdc\x26\x70\x62\x33\x81\xfd\x69\x5c\x42\xc8\x12\xee\x9f\xcc\xec\xee\x24"
+"\x68\x97\xb0\x53\x74\x08\xcc\xc2\x05\x81\xf0\xc2\x55\x4f\xee\x04\x81\x78"
+"\xab\x61\xc2\xf7\x55\xa7\x0f\xc4\xce\x08\x49\x85\x6b\xcc\xde\x18\xd4\x64"
+"\x47\x71\xcc\xe9\x2f\x04\x81\xfd\xe9\x03\x99\xf4\x37\x39\xb8\x96\xd7\xfb"
+"\x06\xc9\x06\x02\x03\xaf\xf7\x91\x1e\x02\x49\xda\x19\xfd\xb2\xdf\xf4\xf5"
+"\xb9\xb4\x75\x13\x28\x72\xbc\x9c\x69\x33\xd5\x2f\xce\xc7\x99\x14\x99\xec"
+"\x22\xb0\x9f\x6c\x49\xe0\x6b\x4e\x03\xea\x97\x4d\xdc\x25\xf0\xb7\x40\x60"
+"\x3b\x81\x4c\x65\x3a\x4d\x58\xe4\x7b\x63\xb0\x9d\xc1\xd3\x01\x46\x61\x0d"
+"\x6e\xf3\x41\xa0\xc7\x7d\x1a\xc2\x67\x3a\x0d\xb2\x87\x9a\x42\x39\xce\xd3"
+"\x45\x7f\x37\x81\xf0\xb4\xf2\x97\xec\x0d\xd1\x00\xe1\x99\x63\x03\x81\x70"
+"\xf6\x7b\xbd\x04\x66\x1d\x16\xb0\xcb\xf1\x6e\x4d\x20\x34\xd1\x2f\xdb\x4c"
+"\x11\x29\xf6\x20\x29\x7d\x59\xa7\x58\x27\x79\x2b\x02\xfb\x9d\x19\x0e\x08"
+"\x1b\xce\x65\x5d\x02\x5f\x03\x02\xdb\x09\xfd\x82\x47\x97\x40\x10\x10\x78"
+"\xbe\xda\x29\x02\x67\xb0\x11\x64\xd2\x38\xae\xcb\xba\xca\x6e\x1b\x6a\x7f"
+"\x2c\x83\x2d\x0b\x2b\x13\x22\xed\xa7\x34\x41\x60\x5f\x1a\x2a\x04\xe3\x30"
+"\xf6\xf3\xfd\xd3\x1b\x08\x9c\x26\x7d\x5f\xf5\x12\x38\x29\x46\xd2\xb7\x26"
+"\x70\x3a\x33\x89\x97\xb0\x99\x40\xb2\x81\x29\xe4\x1a\xea\xd3\x7f\xc3\x69"
+"\xfe\x1b\x09\xec\xea\x03\xbd\x69\x1c\x4b\xfa\x6e\x60\x65\xbc\x62\xe6\x25"
+"\xea\xfc\xd6\x8d\xdd\x9d\x04\xc1\x5e\x5b\x02\xe1\x34\x78\xca\xda\x21\x02"
+"\xf1\x49\x64\x77\x1a\x1b\x99\x37\x2b\x07\x5a\xe8\xb6\x61\x82\x02\xb7\x68"
+"\x12\x19\x82\x87\xe2\x41\x78\x00\x21\xfd\xdd\x7d\x20\x5c\x6e\xe0\x2d\xc1"
+"\x11\xf6\xfd\x3d\x04\xa2\x38\xef\x9e\xee\x25\x50\xc8\x48\x5f\x0e\x7b\x2b"
+"\x3d\xd3\x23\x81\x83\x93\x2e\x81\x93\x69\x77\xb4\x10\x4c\xc1\x18\xd5\x87"
+"\x1d\x19\xdc\xa7\xc0\x56\x04\x76\x49\xa0\x14\xf0\xdd\x30\x7c\x4d\x63\x65"
+"\xdb\x7d\xe0\x97\x5d\x09\x70\x64\xa7\x09\x83\x60\x40\x6f\xe2\x64\xe0\x4d"
+"\x3d\x25\x81\xfd\x38\x03\x1c\x4e\x05\xd0\x99\xa9\x4c\x48\xe7\x72\xd0\x4b"
+"\xf4\x67\xf0\xc9\x44\x4b\x4f\xc0\x03\x9d\x7e\x43\xf4\xf2\x02\x6f\x89\x0b"
+"\xc2\xc9\x00\x5c\x0b\x48\x62\x4e\x70\x21\x08\xbc\x91\x13\x95\x12\xe2\x0c"
+"\x1b\x1d\x02\x35\x0c\xe4\x35\xf5\x15\xbe\x93\x80\xe6\xe3\x12\x38\x35\x0d"
+"\x13\x8c\x74\xe6\x92\x7c\xc6\xe9\x6f\x77\x76\xd3\xb2\x16\x21\x2f\x10\x85"
+"\xc7\x4d\x77\x25\x6f\x49\x20\xce\xd7\xf1\xee\x23\x45\x7f\x11\xbd\x26\x4e"
+"\x54\xa1\xf6\x9d\x04\x51\xd2\x17\x93\x99\xc9\x09\x51\xf3\x09\x10\x4c\x27"
+"\x83\x63\xe9\xa7\x9c\x4c\xeb\xee\xd7\xde\x9a\xb3\x22\x26\xa1\xdf\x60\xec"
+"\x28\xbc\xa4\x63\x19\xe4\x20\x7d\x45\x77\x5e\x76\xed\x82\xc9\x3d\xde\xef"
+"\xbf\x10\x0d\x77\x1f\x4f\xa7\x6f\xbe\x3e\x99\xbe\x7c\x1c\xae\x21\x9b\xfe"
+"\xe6\x20\x48\x32\x88\x33\xec\x81\x27\x94\xc9\xf4\xb7\xc7\x9d\x47\x91\x5d"
+"\x82\x68\x7c\x32\x80\x67\x9c\x6c\xe6\x30\x9c\xe1\x85\x61\x1d\x08\x9d\x4c"
+"\xdf\x94\x2f\xc3\x9d\xe7\x54\x98\x60\xdf\xc0\x92\xf0\x05\xd8\xb7\x28\xb2"
+"\xe2\x89\xa4\x9d\x0c\x19\x42\x26\x97\xa7\xd2\x37\xa1\xa8\x03\xce\x93\x44"
+"\x1f\xdc\x33\x90\x28\xbc\xf5\x99\xf7\x74\xd8\x0d\x04\x66\xa6\x32\x83\x5d"
+"\x09\x50\xa9\x2c\x96\x97\x11\x41\xd9\x20\xef\x9b\x83\x90\xc1\x2e\xc8\xe0"
+"\x58\xfa\x3d\xf2\x74\x98\x74\x5e\x82\x1e\x77\x28\x1a\xc6\x48\x6e\x01\xb8"
+"\xa2\x14\xe6\xac\x09\xe7\x9c\x70\xe1\x0e\xcd\x07\x70\x22\x02\x3b\x27\xc4"
+"\x43\x33\xe9\x83\x43\x35\x98\xb6\xc0\x49\x64\x57\x2a\x30\x8c\x3b\x87\xf1"
+"\xc4\x5d\xa9\xb7\x9d\x64\xc4\xe0\x2e\x91\x25\xa4\xc2\xc5\xa5\x52\x43\xa9"
+"\xd4\xa5\x3e\x58\x85\x65\xd7\x65\x38\x1d\xbf\x97\xee\x97\x63\x88\x8e\xf3"
+"\x22\xc1\x39\x16\x7c\x20\x15\x3a\x20\xce\x74\x93\x21\x43\x58\xbd\x04\xa7"
+"\xc0\x1a\x64\x2a\x09\xec\xc3\x89\x13\xfc\x0e\xa7\x2e\xe1\x91\x48\x60\x0a"
+"\x2b\xd1\x49\x80\x92\xbc\xa9\xc1\xbe\xd4\x25\x1d\xf3\x79\x53\x56\x33\x05"
+"\x79\xbf\xe7\x4d\x3d\xed\xf3\x71\x7f\x26\x77\x19\x84\xe3\x78\xfb\x8d\x74"
+"\x1f\x91\x59\x0e\x3a\x5b\x88\xae\x17\xd2\xed\xf2\xc4\x9e\xc1\xc1\x4e\x92"
+"\xfb\xf3\xba\xb3\xd1\x55\xb3\xbe\xbe\xae\x9f\x3e\xb1\xab\xaf\xbd\x26\xf0"
+"\xd6\x74\x77\xb6\x6e\xce\x9d\xed\xf6\x7b\x9e\xf6\x56\x57\x09\xce\xb1\x7d"
+"\x4e\x9e\xd8\xd2\x07\x7b\x12\xe0\x48\xe7\x14\xb9\x0c\xb6\xeb\xd1\x9b\xef"
+"\x13\x61\xca\x71\xa7\x36\xf1\xf4\x59\x3d\x31\x76\xa5\xdc\xf7\xe1\x3b\x02"
+"\xf7\xe1\xf4\xf9\x40\x17\x76\x43\x2f\x56\x2b\x37\x99\xde\xf8\x96\xf4\xa9"
+"\xf0\x7c\x09\xfc\x39\xe8\x85\x27\x1f\xe9\x8d\xfc\x23\x63\xf7\x4e\xbc\x66"
+"\x79\xa9\xf0\xf4\x5d\x79\x6f\x6e\xa9\xa7\x9d\xdd\xbd\x74\xd8\xd9\x57\xc5"
+"\x7d\x83\x3b\x9b\x9f\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\x82\xc2"
+"\x2f\x06\x66\x94\xe8\xf3\xa5\x1f\xee\x12\x72\x72\x7e\x7e\x21\x4e\x7c\x4d"
+"\x4c\x3d\x39\xdf\x75\xc4\xfc\xfc\x7c\x63\xbb\xd3\x7d\xf3\x96\x5c\xb9\x08"
+"\x47\xdd\xed\xa4\xaf\x75\x6f\x6c\xc0\x42\xb7\x06\xc2\x0f\xe7\xcd\x37\xdd"
+"\x5c\x08\x31\xca\x4d\x4b\xd6\x61\x7b\x60\x59\xf3\x91\xf9\x76\x36\x27\xec"
+"\x66\x34\x52\x7a\xc0\x09\x3a\x54\xe7\xe2\xfc\x42\x3b\xdf\xa4\x1d\xc5\xa2"
+"\xb0\xe4\x07\x97\xf4\x08\x08\x5a\x84\x14\x17\x07\x6a\x84\x68\x74\x25\xc9"
+"\x02\x03\x0c\x53\x0d\x0a\x7f\x34\x4b\x1e\xc2\xab\x49\x1a\xdf\xee\x7c\xee"
+"\x54\xdc\xa0\x2b\x67\x6a\x9d\x64\xd3\x39\xc3\x97\xdf\x78\x82\xa5\xf5\x64"
+"\x36\xb0\x02\xd9\x97\xca\x25\x77\xbb\x78\x2a\x59\x92\x75\xd8\x1e\x73\x17"
+"\xe8\x4a\xe2\x34\x8d\xba\xdb\x6b\x43\x3c\x9e\x58\x7a\xd0\x19\x50\x1d\x93"
+"\x5a\x26\x23\x26\x6e\xe9\xb5\x84\x85\xbf\x09\x36\x4a\xad\x07\x17\xf5\x70"
+"\x14\xa1\xe0\x98\x20\x90\xd0\x92\x8f\xc6\x0d\x21\x6c\x1e\x24\xf0\xdd\x45"
+"\xe7\x90\x8a\xb6\x7d\x39\xc5\x92\xfc\xf5\xd0\x3c\x59\xed\x24\xfb\x9c\xcb"
+"\x0b\x6f\x24\xd0\x5c\x24\x73\xdd\x12\x68\x92\x72\x45\xef\x22\xd0\x26\x46"
+"\xc9\xd8\x56\xe0\x25\x0a\x50\x55\x33\xda\x26\xd0\x00\x5e\xf2\xc1\x4d\x77"
+"\xaa\x1b\x7e\x1a\x35\x68\x1e\xf2\xe5\xa2\xcc\xea\x80\x85\xbf\xb1\x1a\xe1"
+"\x0f\xe4\xfd\x51\xc0\x59\x17\x81\x1e\x6a\x8d\xdf\x22\x47\x9b\x23\xfb\x68"
+"\xb0\xa1\xf1\x5a\x7c\x74\x8c\x20\x81\x84\x2f\x12\x4f\x83\x18\xcd\x38\x89"
+"\xc4\x8d\xc2\xe9\x66\xa1\x70\xb4\x40\xde\x39\x47\x8c\x55\x57\x02\x81\xc0"
+"\xf1\x23\x5a\x72\x96\x14\x0a\xe3\xb3\x82\xc0\x33\xb7\x93\x71\x83\xdf\x0b"
+"\x19\x0d\xbd\x90\x4f\x06\x4e\x34\x43\xe4\x44\x33\xc0\x6b\xe3\x85\x0f\x0a"
+"\xb7\x4e\xcc\x92\xa4\xe0\xd1\x03\x04\x92\x78\xf9\x6e\x33\x80\xf9\x11\x9d"
+"\x36\x48\x7e\xfc\x96\xaf\x70\xa2\x69\x34\x49\x61\x16\xc8\x86\x3a\x18\x76"
+"\xc8\x28\x7c\xd0\x24\xfa\xaa\x10\xde\x53\x50\x55\x3d\x44\x3f\x58\x21\x64"
+"\x34\x8a\x65\x7d\x6f\x9c\x2f\xe4\xa1\xbc\xef\x4f\xac\x78\x0a\xb7\x9a\x21"
+"\x2d\x79\x81\x44\x9a\x21\xc8\xa5\x41\xf6\x43\xa5\x91\x40\xac\xe0\x2d\x93"
+"\xde\x46\x3a\xeb\x89\xb8\x43\x60\xb1\xea\x83\x4b\x89\x42\x41\xd1\xcd\xdc"
+"\x3c\x0a\x0c\x9b\x05\x3a\x04\x9a\x34\x14\xae\x19\x2c\xc1\x8e\x50\x9b\x79"
+"\xca\xb5\xb1\x2a\x17\x04\xea\xd4\xd2\xed\x84\xc5\x93\x96\x59\xb5\xf7\x72"
+"\x9b\x15\x99\x5e\xf3\xad\x2e\x84\x12\x6b\xb4\x43\xe0\x2a\xdc\x5a\x1e\x4d"
+"\xb0\xd8\x39\x41\x60\x84\xf1\x9a\x8f\xde\x3b\x6a\xf3\x38\x67\xf4\x83\x5a"
+"\x71\x59\xb7\xf9\x1d\x5e\x1b\xa3\xb3\x7c\xf9\xdd\xe5\xc8\x6a\x55\x9e\x09"
+"\x04\x92\xb2\xcd\x2d\xcc\x0f\x6e\x28\x30\x18\xae\x99\xd4\xa6\x6b\x34\x9e"
+"\x60\xbc\x62\x40\x1d\xf8\x5c\x55\x87\x94\x78\x62\xce\x11\x18\x2c\x15\x12"
+"\x42\xef\x36\x19\x16\xcd\xe2\x70\x8b\x23\xcc\x86\x7f\x87\x39\xe3\x96\xd9"
+"\x5c\xd5\xed\xe4\x32\xe4\xc2\x02\x76\xb2\xe4\x12\x18\xae\x45\x04\x81\x26"
+"\xbd\x73\xa2\x10\x92\x12\x68\x19\x74\x84\xe6\xa1\xa0\x27\x14\x45\x73\x14"
+"\x7a\x24\x97\xc0\x55\xbe\x4a\x06\x6a\x66\xcd\xac\x7a\xe8\x11\x1a\x2a\x2e"
+"\xfa\x59\x10\x09\x64\x76\x3d\x64\xd6\x06\x96\xf8\xb2\x05\x2c\xc6\xcb\xd5"
+"\xa6\xc9\x74\x2b\x7c\xbf\x68\xd1\x78\x47\x02\xed\x1a\x49\x58\x31\xcb\xa4"
+"\xd8\x92\x81\x40\x3f\x7b\xb7\x4e\x68\xde\xac\xc4\xac\x22\x4b\xbe\xcb\xc2"
+"\x15\xb3\x16\x5e\x8c\x2d\x42\x5a\x71\xc9\x0c\xf1\x38\xeb\x22\x70\x99\x97"
+"\x30\x3f\x42\x22\xc0\xc6\x40\xcd\x43\xe3\xd4\x2a\x5b\x31\x66\xd6\xa1\x0e"
+"\x1e\xba\x8f\x06\xe8\x8f\x65\x8b\x2f\xff\xb5\x8b\xc0\x38\x94\x6e\x95\x05"
+"\xe9\x2c\x10\x83\xca\xbe\x03\xb5\x8e\x16\x97\x0e\x55\xc3\xd5\xb3\x26\x33"
+"\x99\x87\x5e\xa0\x51\x3a\x2b\x08\x04\xe4\xcd\x9a\xe8\x9a\x34\x9b\x2e\x9b"
+"\x20\x37\x24\x56\xb7\x59\x5c\x83\x43\xf2\xb1\xda\x40\xe5\xc9\x08\x3c\x0b"
+"\xc2\xd5\x26\xd0\x86\x7a\x09\x02\xa1\x20\x0f\x54\x65\xd1\xa4\xd8\x1b\x15"
+"\x6b\x70\x7b\x06\xd8\x5c\x85\xb3\x00\x5f\x2d\x5b\xd0\x61\x19\x74\x34\x9e"
+"\x58\xe5\x7f\x85\x83\xda\x04\x36\x57\x61\x44\x89\x2d\x69\xdc\x72\x08\xac"
+"\xfb\x91\xc0\x18\x2b\x2e\x42\x27\xe0\x69\xf2\x8a\x59\x35\xa2\x82\xc0\x43"
+"\x2c\xa2\xd3\x55\x1a\xea\x10\x58\x2a\x5a\x45\xd1\x1d\x68\x9c\xde\x47\x02"
+"\xe1\xaa\xca\xa5\x58\xc5\x47\xa1\x0e\x3e\xaa\xcb\xed\x04\x0b\x75\x11\x08"
+"\x49\xfc\x1e\x87\xbe\xcf\xc3\xa9\x05\x04\xd6\x7d\x4e\xad\x19\x0c\x18\x03"
+"\x35\x1f\x10\x38\x42\xa3\x1c\xa9\x92\x12\xe8\x97\x04\x9a\x4b\x9c\x99\x58"
+"\xed\x18\xb4\x11\x99\x4f\xac\x32\x50\x23\x4f\x04\x7b\x9e\x57\xdb\x04\xfe"
+"\x08\xeb\x40\x20\x3b\xb4\xe4\x10\xa8\x71\xac\x71\xb1\x92\xc0\xd1\x79\xe4"
+"\x54\x04\xa4\x62\x65\x24\x54\x06\x86\xb8\x0d\xdd\xc7\xc8\xd1\x6e\x02\xf3"
+"\xe3\x04\x2f\x19\xd2\x7b\x09\xac\x8d\x44\x8b\xf7\x21\x17\x2e\x2a\x29\x08"
+"\x34\xd9\x8a\x4e\x57\x46\x02\xdd\x04\x2e\x42\x7e\x21\xe2\x89\xeb\xe5\x6a"
+"\x0f\x81\x50\x87\x08\x85\x73\x70\xfb\x5d\xda\xd8\x40\xe0\x5f\xe1\x1c\x5f"
+"\xc0\xe0\xdd\x04\xfa\xeb\x06\x65\x40\x20\x15\x04\x46\xf0\xd8\x1e\x02\x63"
+"\xf7\x23\xd4\xc6\xb2\x63\xd0\xa4\x43\x4f\x47\xa0\x0e\xe2\x57\xef\xf4\x81"
+"\x61\x1a\x82\x35\x6e\x87\x1c\x02\x8d\x20\x4e\x39\x8a\x15\x39\x05\xd8\x73"
+"\xae\x5c\x81\xd9\x42\xa8\x9c\xc7\x66\x4d\x62\x55\x32\x04\x07\x59\x46\xdc"
+"\x21\xf0\x08\x4a\x60\x45\x0c\x4b\x5d\x04\xc2\x9c\x04\x3a\x03\xe2\x63\x03"
+"\x95\x01\xe6\x10\x88\x62\xcf\x2d\x7d\xdf\xb9\x6e\x02\x21\x3f\x20\xb0\x84"
+"\x63\x64\x17\x81\x0c\xea\x30\x0e\x12\x18\xc7\xed\x53\xbc\xb6\x3f\xda\x43"
+"\xa0\xa5\x01\x81\x71\x12\xee\x91\xc0\x9a\x11\xa1\xa6\x2b\x81\xe7\xb1\x77"
+"\xeb\x21\x90\x57\x81\x60\x22\xfa\x40\x83\x2e\x63\x57\xf0\xe4\x04\xfa\xf3"
+"\xd0\xa3\x46\x13\x4b\x62\xe6\x45\xa1\x43\x5d\x1e\xa8\x19\x4b\x38\xab\x13"
+"\xbd\xc9\xc5\x3c\x47\x02\xab\x1a\xaf\xfa\xe8\xad\x3b\x77\xcd\xa5\x30\x4b"
+"\xc6\xb1\x8d\x86\xb1\x51\x8f\x59\xbc\xc1\x17\x13\x55\x87\x40\x64\x27\x61"
+"\x19\x77\x71\xad\x4d\xa0\x7d\x92\x8d\x01\x3b\xc4\x64\xbc\x1a\xa4\x2b\x77"
+"\x62\xd5\xef\x61\x7f\x19\x7a\xfd\xda\xc5\x43\xac\x43\x20\x5f\xc4\xfc\x88"
+"\xa7\x46\x4c\x68\x7e\x86\x43\x20\xf3\x33\x7f\x9e\x9f\xa7\xfb\x59\x00\xa4"
+"\xbf\x34\x67\x56\x65\x69\x40\xa0\x86\x17\x9e\x60\x67\x81\xc0\x15\x98\x2d"
+"\x25\x96\x24\x81\xf1\x62\x05\x3a\xdb\xc3\x4c\xf6\x81\x40\x60\x25\x78\xdf"
+"\x9d\xc6\x60\xca\x19\xa8\x25\xbd\x55\xa6\xf0\xcc\x00\x4d\x98\x94\x19\xa1"
+"\x4d\x20\xb0\xfa\x84\x04\xf2\x26\x49\xd2\x26\x87\x71\x37\x8a\xdd\x77\xa8"
+"\x0c\xe3\xeb\x28\xa5\x4d\x90\x71\x6a\x45\x18\x0c\x6c\x01\xe2\xe3\x0c\x86"
+"\xc3\xf3\x9c\x46\xcb\x36\x0c\x11\x0c\xa6\xcc\xd0\x8f\x58\x04\x6f\xe3\x59"
+"\xca\x6b\x31\xd1\xfd\x5e\xc4\xb1\xc3\x64\x76\xc8\xbe\x4b\x61\x7a\x3f\x47"
+"\x9b\x31\x36\x07\x37\x81\x19\x9c\x0d\x81\x50\x9a\x2c\xc9\xf6\xc0\x40\x1a"
+"\xa4\x49\x68\x87\xc9\x00\x14\xd7\x08\xa2\x3c\x9c\x80\xec\x3d\xbc\x29\xc5"
+"\x82\x78\x6c\xdb\x86\xd2\xce\xc2\x55\x35\xf9\x0a\x74\x52\xd0\xdd\xdb\x81"
+"\xc4\x42\xd3\x80\x94\x95\xe2\xc2\x72\x02\x49\x1f\x85\xaa\x9a\x90\x00\x39"
+"\x00\x9f\x3e\x6e\xc3\xd4\x80\x8d\xb2\x24\x9d\xa3\xa5\x22\x8c\xc2\x03\x36"
+"\xdc\x73\xbe\xec\xc7\x5e\x1d\xc6\x77\xa2\x43\x75\x92\x74\x35\xc1\x4e\x41"
+"\x27\x45\x4c\x8e\x97\x8c\x5d\x27\xcb\x07\xa9\x05\x53\x84\x06\xaf\x73\x16"
+"\x7a\x30\x55\x8f\x0e\x9d\x52\x27\xb3\x53\xfa\xbe\x68\x3b\xd9\x13\x25\x87"
+"\x4f\x13\x72\x44\xee\x82\x96\x23\x56\x4f\x0f\x11\xbd\xab\xe4\x23\xa7\x36"
+"\x66\x16\x22\xfb\xe4\xfe\x10\x89\x12\x0d\x76\x0f\x89\x64\xc8\x61\x84\x68"
+"\x3f\xf4\x1c\x8b\xf9\x69\xa1\xee\x2c\x62\x95\x11\x82\x75\xd0\x9d\xee\xf2"
+"\x30\x6c\xf6\x3e\x22\xe2\x0e\x5d\x1b\xe9\xaa\x42\xf1\xfe\x69\xa2\x43\x56"
+"\x9e\x11\xb9\x8d\x95\xee\xa9\x0f\x21\xfb\x4e\xbb\x95\x12\x49\x51\xb2\xb3"
+"\x30\x57\x46\xf8\x8e\xdd\x8d\x07\x42\x7b\xd8\x93\x68\x6c\xf3\xdc\xc2\xb3"
+"\xfc\x90\x73\x8a\x8b\x4f\x5c\x9f\x1d\x82\xd9\xfc\xc0\x7e\x4e\x25\x45\x1f"
+"\x72\x40\xa2\xba\x29\x29\x12\x78\xc8\x39\xe5\xfb\x4f\x58\x9b\x9d\xc3\xf8"
+"\xed\xe8\x8b\xae\x82\x83\x42\x21\xfa\xb8\xa7\x78\x0a\x85\x67\x50\x11\x05"
+"\x05\x05\x05\x05\x05\x05\x05\x05\x85\xc7\xc0\xda\x82\xf8\xb9\x28\x7f\xc8"
+"\xf6\xda\x46\xa1\x25\xec\x9c\x76\x17\xb5\x95\x8f\xfe\x34\x98\xdc\x42\xe5"
+"\x77\x71\xbe\x77\x5b\x9f\xbf\xb3\xc5\x41\x0b\x0f\xcc\xf7\xe4\x16\x75\xf0"
+"\xcd\x6f\xab\x5f\x7c\x06\x30\xeb\xf8\x27\xea\xaf\x3b\x9b\xdb\x6a\x1b\xa9"
+"\x50\x89\xb9\x6a\x50\xb3\x86\x6f\xb7\xa2\x8f\x5c\x4c\x59\x9c\xd7\xfb\x00"
+"\x61\xe2\x8b\x1a\x4f\xa9\x93\x10\xde\xe2\x95\xbb\x59\xdf\x9c\x36\xde\x59"
+"\x35\xb6\xaa\x03\x7d\xa0\xf6\x6e\x87\x21\x98\x4b\xb4\x09\x34\x56\xb6\x3b"
+"\x50\x2a\xd8\x4c\xe7\x31\xde\xff\x98\x04\xa2\x66\x8d\xe8\xbd\xef\xe3\x7c"
+"\x48\x60\xb0\xd4\x49\xd8\x4a\x67\xe1\xdf\x4c\xa0\xde\xa5\x5a\xdd\xb2\x0e"
+"\xcf\x81\x40\x63\x2e\xee\x29\x8c\x35\x89\x96\xbc\x58\xc7\xdb\xb8\xea\xaf"
+"\x27\xcf\x11\xad\x10\x18\xbf\xed\x2b\x1c\x6d\x40\x25\xe7\xce\xa3\xb2\x10"
+"\x95\x9c\xc9\x59\x60\x6f\xd6\x28\x44\x21\x4d\x2b\xd7\xe2\xba\x38\x4d\x12"
+"\xe8\xb1\xe3\x44\xb7\x2f\x88\xdf\x77\x9a\x51\x71\x1e\x2a\x38\x43\x06\x9c"
+"\x24\x34\x9d\xef\x7c\x8f\x07\x8c\x14\xe2\x9e\x66\x3c\x59\x47\x45\xe7\x89"
+"\x82\xb1\xac\x8d\x07\x48\xc4\x06\x02\x75\xbe\x7a\x06\x0a\xd4\xe7\x66\x89"
+"\x9e\x4c\x54\x88\x67\x05\xf5\x9c\x71\xa8\x8c\x61\x43\x15\xcf\x37\xfd\x75"
+"\x5f\x21\x1a\xb9\x1d\xc2\xd7\x88\x90\x70\x6e\x0e\x6e\x46\x7d\x16\x72\x26"
+"\xa8\x43\x8d\x34\x81\x40\xcc\xf2\x9d\x15\x03\xab\xd5\x20\xe3\xb7\x46\xa3"
+"\x74\xb6\x70\xcb\xd3\x7c\xa6\xed\x78\x61\x94\xe9\xdc\xa6\x79\xb3\xce\xeb"
+"\x28\x0a\xab\x7e\x66\x33\x92\x9c\x6b\x84\xeb\x06\x45\x2d\x4c\xa2\x20\x94"
+"\x85\x89\xe4\xa2\xbf\xc6\xe3\x66\x95\x1e\xa1\x79\x48\xdb\x57\xae\x9d\x2b"
+"\x27\x2d\x53\x68\x6a\x80\xc0\x44\x93\x11\xbe\xca\x4a\xf8\xbb\x96\xcc\xcb"
+"\xf3\x8a\x8c\x8e\xd9\xf8\x9a\x1b\x35\x9d\xb6\x1d\xe7\xab\xf4\x0e\xc7\xac"
+"\x8a\xf5\xd9\xa4\x5d\x35\x29\xaf\x24\xed\xa6\x87\x71\x20\xd0\x00\x02\xa1"
+"\xc0\xd1\x26\x0b\x05\x19\xaf\xa0\x62\x35\xc1\x78\x1d\x2a\x63\x8f\xd6\xa0"
+"\x8a\xcc\x5f\x37\xd8\x10\x2b\xc2\xbd\x81\x32\x34\xbe\x40\x43\x24\x51\x9f"
+"\x75\x75\xa8\x9c\xd3\xe8\xe8\x6a\x15\x75\x9b\xf3\x34\x0e\x74\xc6\x63\x8c"
+"\x96\x68\x9e\x9f\x4b\xcc\x3d\xcb\x57\x5e\x3a\x3d\x51\x27\xe5\xe5\x72\x29"
+"\xbc\x14\xc4\x26\x82\xfa\xc9\x77\xea\x3a\xdd\x0f\xf5\xf5\xd0\x26\x48\x60"
+"\xf9\x96\x50\x16\x96\x1b\x56\x62\x31\x66\x85\x2d\x8e\xfa\x8a\x5b\x42\x87"
+"\x52\xf7\x57\x13\xf7\x83\x82\xc0\x53\x90\x7c\x9e\x9e\xa6\x87\xe1\xf7\x02"
+"\x1b\xcb\xcb\xf3\x62\xf5\x64\xa4\x12\xb3\x50\xa9\x92\x0c\x2e\xc5\x7e\xe4"
+"\xe7\x69\xb4\x5c\x2a\x2f\x5b\xa8\x8b\xb9\xc0\x7c\xb4\xb9\xcc\x8f\xe0\xfb"
+"\x78\x6c\xc2\xe5\x12\x16\xc8\x2d\xd4\x7d\x84\x2b\x26\x1b\x58\x8a\x55\x07"
+"\x44\x35\xa0\xf1\x96\xab\x20\x81\xc1\xb8\x8f\x45\xa2\x44\x83\x32\xe2\x45"
+"\x06\x12\x08\xd9\xc0\xef\xbb\xb5\x18\x1c\x0e\x7d\x20\x3f\x07\x7f\x87\x68"
+"\x9c\xe7\x13\x16\xcf\x0f\xb0\x64\x94\x7e\xbf\x42\xf8\x8f\x0f\x7b\xa9\xf8"
+"\x54\xb0\x47\xa1\x76\xa5\x72\x29\xb1\xe8\x77\x08\xac\xfb\xa8\xc1\xe6\x44"
+"\xcd\xb1\x07\x49\x26\x85\xb2\x10\xff\x2f\xc6\x2a\x89\xd2\xd1\x00\xcd\x63"
+"\x1a\xea\xc3\xe6\x6a\xf0\x57\x10\x88\x1a\xc5\x15\x7a\x4a\xfc\x2e\xd3\x9a"
+"\x73\x5e\xac\x42\x62\x35\xbe\x28\x3e\x77\x80\x95\x25\xb8\x15\xf1\x72\x89"
+"\xb3\x10\x2a\x92\x56\xa9\x0f\x95\x3f\x73\xf4\x50\xd5\xe7\x12\x18\x27\x91"
+"\x71\x54\x8f\xa0\x4a\x6a\xae\x82\xca\x1f\x5f\x5d\x5b\x4d\xd6\x71\xe8\x81"
+"\xa6\x11\x30\x50\x81\x00\xf2\xce\x2d\xf1\x26\x15\x09\xac\x10\x03\x75\xa8"
+"\x90\xe1\x69\xc8\xf2\x08\x25\x78\x8f\x22\x63\x48\x20\xea\x6e\x2d\xa0\xf8"
+"\x99\xbe\x30\x3e\x69\x4b\x02\x8b\xdd\x04\xfa\xe8\x85\x11\x24\x10\x3b\x0f"
+"\x1f\x83\xb6\x41\x4b\x11\x0a\x62\x1a\xab\xa2\x8a\x89\xe6\x7d\x75\x41\x20"
+"\x1b\x39\xcd\x3b\x04\x02\x33\x63\xcc\x83\xbf\x09\xfc\xb8\x06\x8f\x89\x2d"
+"\x11\xd4\x73\xe2\x9b\x77\x12\xab\x8e\x44\xf9\x6d\x16\x28\x97\x82\xb4\x81"
+"\x04\xde\x1a\xf1\xd1\x80\x07\x4a\x8a\x75\x08\x8c\x42\x87\x07\x12\x28\x08"
+"\x1c\x39\x25\x09\x84\x2e\x12\xab\x88\xea\x72\x8b\x70\xa8\x93\x81\x65\xa0"
+"\x5a\x5e\x10\x88\xb7\xa7\x5c\x19\x10\x04\xae\x8c\xec\x93\x04\x1a\x36\x10"
+"\x88\x5a\x60\x5a\x25\x41\xfa\x90\x8f\x6f\x9e\x0e\xfc\x8c\x23\x81\xf7\xbb"
+"\x08\x04\xea\x74\x24\x10\x2e\x9d\x14\x57\x84\xb2\xf0\x5c\xb9\x8a\x12\xc8"
+"\x2b\x48\x20\xa6\xa1\x22\x3e\xa4\x27\xba\x24\xd0\x0a\xb2\x65\x94\x8e\xc6"
+"\x07\x48\x33\x1e\x13\x5b\x24\x87\x84\x4a\x11\x2e\x33\x0c\x2b\x41\xd6\x80"
+"\xc2\xce\x71\xd4\x8a\x51\x4b\x07\xde\xf0\x33\x12\x54\xea\xb6\x09\x4c\x58"
+"\x92\x40\x3f\x23\x7b\x1c\x09\xe4\x27\x25\x81\x75\x5e\xf1\x9c\x00\x12\xa5"
+"\x04\x96\xda\x04\xfa\x18\x36\x61\x91\xaa\x3b\x04\x86\x97\xcb\x92\xc0\x39"
+"\x16\x3a\xcf\x37\xbf\xe9\xde\x39\x18\xf8\xcd\x09\x12\x18\xae\x26\x05\x81"
+"\x73\x48\x20\xe1\xcd\xa4\xe9\x10\x08\xbd\xf5\xb9\x4a\xf0\xfe\x5d\x7f\x25"
+"\x76\x3f\x61\xc5\xd8\x18\x5e\x21\xa4\x15\x2b\x17\xe8\x4a\x33\x5c\x4d\x30"
+"\xd9\xa8\xe2\x34\xba\x86\x87\xc7\xe9\xc8\x72\xd8\x92\xe7\x25\x96\xa0\xe1"
+"\x8d\x95\x04\x81\x26\x9c\x69\x0b\x9e\xee\x99\xd0\xb7\xcd\x96\x9b\x17\x91"
+"\x37\x28\x09\x3a\x37\x1a\x80\x1d\xcd\x0b\x50\x20\x0c\x68\x8d\xc4\x12\x7e"
+"\x67\x73\xeb\x0e\x10\xc8\xb0\x73\xb3\x59\x88\x23\x81\x41\xf6\x4e\x09\x3f"
+"\x55\xe3\xe7\x90\x2c\xc8\xcb\xcf\xce\x63\xce\x75\x5e\x0d\xb2\x77\x21\xad"
+"\x76\x71\x3f\x0d\x89\x5e\xc2\xe6\x0d\x41\xe0\x2c\x2f\x15\xcc\x67\x49\x20"
+"\x4c\x1e\xe8\x0a\x5d\xe5\x15\x03\x6a\x2e\x86\xcb\x51\x9a\xa4\x71\x68\x84"
+"\xf8\x39\x1d\xca\x7e\xb9\xc9\x97\x19\xb7\xca\x76\xc9\xc7\xec\x90\xc1\x61"
+"\x70\x6e\x60\x5a\x90\x85\xca\xd4\x82\xd3\x58\x14\xbf\xdf\x89\xc0\x70\x89"
+"\x0a\x3e\xf8\xd5\x58\x19\x73\x6a\xf2\x46\x99\xc1\xe0\xca\x02\x50\x0a\x43"
+"\xbd\x68\x80\x53\x36\xc2\xab\x45\x7b\xd9\x47\x2d\xc8\x3d\x02\xe2\x94\xa0"
+"\xcb\x7a\x99\x61\x9b\x8f\x31\x6c\x6c\xc5\x66\xb9\xf1\x0e\xe7\xec\x30\x34"
+"\x56\x5e\x4f\xb0\x39\x18\x9b\x56\x29\xdc\x2b\xd8\x3f\x4a\x9b\x50\x05\x02"
+"\x6d\x9a\x79\x84\x58\x41\x55\x1c\x1d\xea\x5e\x18\x8d\xad\x08\x66\xb9\x8a"
+"\x1a\xd1\x04\x5c\x51\x82\x45\xc5\xfe\xe2\xc2\x33\x1d\x44\xf6\x4a\xcd\x23"
+"\x48\xd1\x10\xf6\xb5\x8e\xd6\x52\xef\x68\x07\x75\xe2\x41\x65\xe1\xe1\x11"
+"\xa9\xc6\xf4\x84\x9c\x34\x72\x8a\xe8\xb0\xbd\x6f\xaf\xa3\xf9\x1c\x81\x59"
+"\x05\xa5\x25\xfc\x1d\x3a\xed\x1e\x03\x68\xab\x14\x8f\x88\x03\x2c\xa1\xae"
+"\x84\x42\xb5\xd3\xed\x92\xf4\xa1\x28\xe6\x10\x75\x8b\x23\x47\x3c\x01\x54"
+"\xac\x6e\xac\x22\x1c\x33\x74\xca\x29\x4b\x62\x9f\xf8\x8b\x3a\x54\xfd\x54"
+"\x14\x75\xa7\x6e\xa5\x03\x7a\xc0\x5d\x3f\xdc\x4e\xfd\xb9\x43\xb7\x47\x46"
+"\xad\x87\x1c\x70\xf6\x81\x07\xbc\xe2\xd0\xd9\x85\xb9\x07\x4e\xfa\xe1\x80"
+"\xe4\xf3\x7c\x38\x7d\xe9\x70\xb4\x70\xfe\xc1\x07\xec\x7f\xd8\x01\x0a\x0a"
+"\x0a\x0a\x0a\x0a\x0a\x0a\x0a\xcf\x07\x1e\xfb\x0e\x9a\x1d\xce\x6f\xd2\x5e"
+"\x69\xe2\xc3\x76\xc0\x9a\xd4\x8f\x69\x73\xac\xb9\xad\x92\xc4\xb3\x06\x39"
+"\x58\x9d\x4d\x91\xa1\xfd\xc1\x7c\xa9\x73\x48\x84\x2f\x24\x1f\x50\x8f\xde"
+"\x09\xf8\xc5\xf9\xbb\xb0\x6c\x7c\xaa\x59\x5b\x40\xc3\x45\xc8\xbc\x71\x52"
+"\x56\x37\xb8\xc5\x33\xae\xb4\x9b\x1c\xe5\x76\x32\xfa\x80\xe2\x76\x0e\x87"
+"\x56\x19\x89\xad\xf0\xca\x86\x07\x04\xcd\x72\x54\x34\x11\xf7\xb3\xd1\x20"
+"\x3b\x9f\xec\x28\x7a\xc6\x49\x2f\x0e\xd1\x11\xb4\x10\x72\x91\x60\x23\x73"
+"\x79\x9a\xe7\x9d\x57\xe9\x3a\x6f\x44\xf8\xb6\xb5\x58\x69\x1b\x31\x4a\xf8"
+"\xe0\x89\xd9\x68\x6c\xfc\xf8\xd4\xac\xeb\xf8\xba\x23\xc1\xdc\x6f\x5d\xb7"
+"\xf8\xa2\x55\xda\x4d\x9a\xf4\x7c\x84\x47\xb7\x2d\x6e\x27\x91\xf8\xf1\x14"
+"\x39\x4b\xca\x8b\x46\x2f\x81\xfe\x45\x22\x3e\x5b\xd5\xd6\x9c\x5a\x6a\x7c"
+"\x99\xe8\xed\xfa\x76\x2b\xc3\x04\x06\x28\xd1\x4b\x9d\xcd\x43\x75\x62\xe4"
+"\x93\xa1\x2e\x06\x22\x8c\x90\xe2\x76\x95\x30\x2a\xa4\x57\x5e\x34\x7e\x9f"
+"\x98\xf1\xc8\x86\x56\xe1\xaf\x93\xb9\x00\x1a\x1c\x11\x22\x2d\x98\x4c\x6b"
+"\x73\x5e\xc2\xb2\xa1\x58\x23\x24\x1c\xdd\xbc\x73\x87\xad\xdd\x20\xa6\x00"
+"\x00\x20\x00\x49\x44\x41\x54\x61\xd8\x51\x4f\x79\x15\x5f\x8c\x94\x17\xf5"
+"\xd5\xdb\xe3\x05\x61\x12\x88\x26\x7b\x1a\xda\x08\x86\xb4\xf1\x40\x90\xdd"
+"\x8a\x55\xd1\x30\xcf\x87\x6f\xf7\x6f\x91\x77\xbe\x47\x0d\x5e\x20\x52\x9f"
+"\x2d\xcc\x16\xf2\x27\x0a\xfb\x9b\xc2\x90\x10\x09\xb4\xe2\x42\x79\x56\x28"
+"\xc0\x65\x0f\xd4\xf5\xef\xcf\x14\xa2\xc5\xc5\xf1\x5b\xc9\xf8\x28\xbe\x13"
+"\x43\xeb\x0d\x93\xec\xbf\x1b\x32\x0a\x23\x2b\xc2\xd6\xcf\x59\xd7\x92\x2b"
+"\x20\x53\xb7\x0b\x71\x54\xfe\xb5\x8d\x00\x79\x8d\x44\xf4\x42\xbe\x50\x30"
+"\x6e\x93\x48\x47\x6b\xe8\x13\x26\x6f\x48\xa0\x30\x46\x2c\xcc\x8e\x17\x8e"
+"\x14\xc8\xfe\xef\xf5\xc2\xf7\x49\x2d\x39\x46\x4e\xac\x86\x90\x40\xe4\x9f"
+"\x98\x51\x27\xc7\x78\x32\x4e\x74\xb4\x5c\xdc\xf1\xa7\x48\x6e\x57\x05\x81"
+"\x04\x08\xd4\x78\x35\xc6\xcc\xe5\xc8\x6a\xcd\xa4\x0c\x35\x6e\xe3\x34\x9a"
+"\xb4\x9b\x67\x81\x40\xc6\x1b\xd8\x28\xf0\xca\x8c\x55\xfb\x30\xb7\xb9\x95"
+"\xac\xcf\xf2\x45\xbe\x18\xa4\xbc\xca\x97\x82\x3f\x22\x81\x46\x09\x75\x65"
+"\x3a\xaf\xa0\x09\xe1\x40\xdd\xcc\x1b\x34\x5f\x5c\x0c\x4b\xb5\x1a\xd1\x29"
+"\x0a\xa3\xc6\x2f\x36\x3c\x14\xad\x2c\x78\xc5\x5d\x0f\xde\xe5\xf1\x22\x9b"
+"\xe5\xa5\x60\xd5\x0e\x25\x44\x59\x80\x30\x23\x05\x0d\x8a\x60\x7a\xd5\xac"
+"\xda\x43\x9c\x49\xad\xa1\xd0\x9d\x0b\x02\x3d\x68\x8c\x88\x36\x55\x7b\x6d"
+"\xc3\xb6\xcf\xc1\x01\x67\x9b\x6b\x1e\x3b\xd1\x40\x02\x0d\x69\x12\x99\x28"
+"\xb0\x10\x67\xf0\xaf\x4a\x78\x72\x39\xbc\xfa\x84\xe6\x71\xdb\x42\xa7\x87"
+"\x69\x48\x9a\x6e\x95\xf1\x85\xbb\xc9\x4c\x7c\xad\x2c\x4c\xf6\x8a\x8b\xa8"
+"\x67\x3b\xc2\x7c\x75\x12\xab\xe2\x57\x02\x03\xc2\xd2\x2d\x7c\x3f\x61\x95"
+"\x1b\xc5\x92\x1f\xdf\xa4\x27\x16\x7d\x74\xd5\x2a\x2e\xf9\xa3\xb8\x9b\x97"
+"\xd0\x14\x26\x1e\xae\x63\xc7\x3e\xc0\x78\x1e\x5f\xfc\x2f\x0e\x30\xb4\x09"
+"\xc4\x86\x85\xe5\xf8\x98\x0f\x0d\x5d\x56\xe0\x56\x31\x77\x1d\x32\xb4\x06"
+"\x2a\x68\x7a\x53\x2a\x5a\xb1\x9a\xf3\x45\x82\x49\xcf\x2d\x43\x11\xc1\xba"
+"\x51\x8a\x59\x45\xcb\xd5\x1a\x7a\xda\x04\x12\x6a\xa1\xc1\x91\x9f\xed\x59"
+"\x0e\x2e\xa1\x79\xe3\xdc\xa1\xea\x49\x93\x0d\x54\x91\x40\x9f\xb4\x77\x2e"
+"\x37\x68\x54\xe8\xf5\x98\xc1\xcc\x6a\xb1\xb1\xed\x18\xf8\x84\xf0\x51\x02"
+"\x37\xd1\x25\xd0\x64\x49\x9a\x24\x74\x95\x8e\xa0\x7a\x03\x09\x1c\xa1\x73"
+"\x14\x09\xac\xe0\x57\x02\x52\x02\x13\xab\xdc\x12\xea\x27\x24\x30\x06\x04"
+"\x86\x60\x70\x89\x20\x63\x34\x82\x0a\x0d\x9e\x97\xa6\xeb\x03\xf5\xa4\x43"
+"\x60\x0d\x2d\xd9\xf0\x66\xe1\x25\xa1\x96\x25\x80\xd6\x8e\x15\x1f\x75\xd7"
+"\x23\x63\xa8\x00\x41\x02\x2d\xc8\xb2\xe2\x7c\x91\xe0\xa1\x77\x2d\x28\xc2"
+"\x47\x23\xd1\xe2\x2a\x2a\x1c\xa4\xd6\xb0\x9b\xc0\x3c\x12\xe8\xa1\x63\x79"
+"\x54\xfb\x15\x97\xa0\x86\x96\xde\x2c\xd6\x5c\x09\x2c\x14\x6e\x9d\x15\x6a"
+"\x41\xac\x82\xc9\x7e\x10\xc6\x45\x3b\x4f\x60\xbe\x4d\x20\xbe\xfc\x5e\xd5"
+"\x84\x5a\xab\x4d\xe0\x85\x91\x2e\x02\x91\x98\x62\x75\x24\xda\x43\x20\xee"
+"\x68\x12\xd9\x07\x0a\x5d\x99\x64\x0a\xfa\xc0\x0e\x81\xd2\x60\x6d\xd1\x21"
+"\x30\x0a\x9d\x29\x12\xf8\xae\xb3\xee\xb1\x1d\x02\xe1\xf0\xfb\x6d\x02\x09"
+"\x5a\x12\x15\x17\x35\x6a\x93\x62\x65\x24\xe4\x6a\x0d\x05\x81\x61\x20\xf0"
+"\x8e\x43\x20\x1a\xfa\xa1\xb6\x0f\x86\x2b\x83\x33\x92\xe4\x82\x40\x1d\x08"
+"\x84\x0a\xfb\xec\x0e\x81\x23\xa7\x4d\xba\xd3\x4d\xd8\xe8\x91\x40\xc2\x2d"
+"\xe8\xe8\x79\x49\x6f\x13\x78\x1a\xd5\x66\x2e\x81\xa2\x5b\x89\x86\xab\x24"
+"\xb4\x81\x40\x9d\xde\x97\x04\x46\x91\x10\x4b\x47\xad\x1d\x10\x48\xa2\x1b"
+"\x08\xac\x42\x1e\xb8\x1e\x72\x08\x74\xd7\xc3\xcb\xe5\x5e\x09\xd4\x67\xb1"
+"\x46\xa8\xa9\x02\x56\xe0\xb4\x84\x28\x53\x6a\x0d\x05\x81\x90\x39\xb1\x5c"
+"\x02\xe1\xb8\xb0\x34\x6f\x34\x22\x74\x3f\x1b\x70\xec\x0b\x97\xd0\x7a\x2f"
+"\x61\xb9\x04\x1a\x34\xa4\x9f\x2e\x3e\xc4\x35\xc3\x63\x43\xe7\x43\xd0\x07"
+"\x5a\x82\xc0\xfb\xa8\x4d\x0b\x5b\x30\xfa\x9d\xdd\x2f\x08\xac\x40\x47\xc8"
+"\x9b\x49\x1f\xfb\x20\x56\x09\xa3\xfa\x26\x51\x23\x86\x85\x8a\xb5\xb2\x85"
+"\x0a\xe1\xf3\x89\xfb\xe5\x25\xa1\x8c\x14\xda\xb1\x01\xb1\x76\x8e\x46\x83"
+"\xa3\x58\xcd\xb0\xd0\xef\xb5\x9b\x70\x00\xa7\x91\x21\xcd\x36\xb0\x4f\x45"
+"\x02\x51\xd5\x26\xfa\xc0\x3c\x5a\x27\xdf\x19\xa8\x61\xc7\x60\x71\x0b\xcb"
+"\x92\x1f\x85\x99\x4b\x52\x15\xba\x0c\xfc\x8c\xc7\x5d\xad\xa1\x50\x54\x9b"
+"\x2c\x64\x0a\x02\xe1\x70\x62\x56\x84\xb6\x0f\x0e\x1d\x38\x4c\xfd\x30\x62"
+"\x1c\xc5\xce\x3a\xc8\x90\x40\x6e\xd3\xef\x25\x81\x3a\x6d\x36\x7f\x30\x76"
+"\x9a\x40\x92\x58\xab\x1a\xc2\x58\xf5\x04\x85\xfe\x21\x1c\x30\xe3\x68\x12"
+"\xe8\x77\x0c\x0f\xa9\x85\xfa\x32\xde\xc0\x71\x2c\x84\x5f\xae\xac\xad\x05"
+"\x0c\xca\xf6\xc1\xc0\x5b\x35\xe8\x32\x8c\xc0\x78\x0c\x74\x49\x51\x61\x02"
+"\xbd\x82\xba\xb2\x9a\xc7\x4e\xa2\xde\x96\xb3\x59\x6c\xdb\xbc\x9a\xc0\x9e"
+"\x15\x67\x40\x5a\x99\x95\xf3\xd0\xe0\x1a\x3e\xda\x40\xcb\xe7\x65\xb9\xbe"
+"\x42\x62\xab\xbc\x8a\x86\x84\x2b\xf8\xdd\x01\x94\x55\x3f\x29\xbe\xc1\xf2"
+"\x95\xe0\x06\x57\x89\xdf\x82\x7c\x98\xc7\xd5\x1a\x26\xe9\x1d\x1c\xcb\xeb"
+"\x58\x93\xa6\x50\xc8\xf9\x84\x15\xe4\x5e\x58\x1b\xb0\xab\x3e\x76\x91\x61"
+"\xf9\x70\x88\x6d\xdb\x21\xde\xe4\xcb\x65\xe8\xfb\x92\x2c\xca\xe1\x6a\xec"
+"\x1d\x57\xaf\xeb\xdd\x46\x7b\x44\x27\x7a\xa0\xa3\xd6\x42\xdd\x17\xea\xcb"
+"\x3c\x01\x77\xbf\x07\x0d\xfd\x1c\xdb\x43\xb2\xcf\xd5\xa9\xc1\xdd\x6e\xe7"
+"\xd0\x63\x04\xb8\xb9\xb0\x28\xfe\x11\xeb\x68\x54\xe8\xae\x0b\xf5\x99\x50"
+"\xba\x1d\x89\xca\x5c\xc4\x8f\xe6\x28\x07\xbb\xcb\x1c\x0a\x6d\xae\xb7\x6b"
+"\x05\x49\x84\xc5\x21\x6a\xe7\x9c\x43\x46\x3a\x6a\x41\x79\x29\x7b\x8f\xb4"
+"\x2f\xe5\xe7\x04\xdd\x7e\x02\x53\xab\xad\x1e\xc1\x5e\x55\xe8\xec\x09\x94"
+"\xd6\x5b\x18\x15\xbe\xba\x18\x0b\x3c\xfe\x39\x4f\x60\x54\xa8\xa0\xa0\xa0"
+"\xa0\xa0\xa0\xa0\xa0\xa0\xf0\x92\x61\x83\x09\x22\xfa\x4b\xfd\x2b\x59\xbb"
+"\xb7\xe1\x43\x40\x7d\xfe\x8e\xc7\x46\xe3\xbe\xf9\xf8\x45\xe9\x34\x75\xab"
+"\x0f\x01\x65\x26\x17\xa9\xbd\xa5\x86\x73\x6d\x0b\x03\xc3\x2d\xb1\x95\x59"
+"\xa1\xd1\xa5\x56\xfd\x79\x61\x83\x09\x22\x3a\xa6\x8a\x13\x73\x59\xbc\xc5"
+"\xea\x02\xbe\xe3\x23\xe2\xbd\x9f\x21\x1f\xee\x36\x1e\x20\x4e\xc5\x4c\x82"
+"\x2c\x9e\xdc\xc2\x6c\xf0\x61\x8f\xc8\x67\x3b\xab\xfa\x56\x56\x71\xbd\x8a"
+"\xd1\x9f\x11\x36\x98\x20\xe2\xbb\x55\x8b\x04\x43\xc9\x0d\x42\x10\xab\xe8"
+"\xf8\x6a\xba\x58\x21\x5a\x49\x24\x6c\x54\x4a\xb6\xf3\x42\x05\xdd\x0f\x9b"
+"\xf7\x3d\x84\x40\x6d\xad\x6b\x63\x2b\x02\x3b\x7e\x59\x7f\x16\xd8\xd6\x04"
+"\x91\x08\x06\x92\x46\x61\xa4\x70\xdb\x77\x8b\x8c\x46\x7d\x85\x13\x2b\x44"
+"\x4f\x16\x2b\x27\xf0\xbd\x0c\x10\x48\x4a\x27\x0a\x27\x1a\x9e\xc2\x79\x47"
+"\x29\x89\x4e\x58\x75\xfb\x3c\x39\xd3\x0c\xf8\xeb\x92\x7f\x62\x42\x01\xe7"
+"\x3c\x85\x0b\xf6\x9e\xb9\x28\x31\xa4\xbb\x56\x24\x70\xb4\x19\x20\x91\xe6"
+"\x88\xf8\x25\x78\xc6\x89\xc2\x7e\x4b\x94\x1a\x61\xb7\x0a\xb3\xc9\xd0\x89"
+"\xd5\x10\x39\x8a\x66\x85\xa2\xe0\xa6\x01\xc7\x25\x9b\xe4\xcc\xad\x48\xbc"
+"\x5c\x2a\xdc\x7a\x06\xaa\xcb\x27\xc5\xb6\x26\x88\x44\xbc\x6e\x6f\xe8\xf4"
+"\x7b\x5e\x31\x1b\x27\x9b\x4c\x18\x9a\x04\x19\xad\x08\xe7\x6c\x82\x40\x34"
+"\xf8\xd3\x79\x89\xd7\x8c\x8a\xe3\x84\x35\xdc\x84\x35\x6e\x21\x81\xa6\xb4"
+"\x40\xb5\x93\x4c\xe3\x36\x9a\x73\x08\x77\xad\x17\x2d\x24\xd0\xa8\x27\x2c"
+"\x93\x15\xeb\xf8\x8b\xde\x77\xb9\x15\x44\x73\x44\x2c\x35\xc9\x6e\x15\x19"
+"\xbd\x60\x27\x1a\x70\x1a\x8d\x46\x64\xc1\x0b\xdc\x32\x57\x78\x3c\x82\x66"
+"\x18\xa5\x44\x63\xe7\x55\x97\x4f\x8a\xed\x4d\x10\x09\x76\x62\xfb\x2d\x52"
+"\xce\xc7\xaa\x66\x9c\x5b\xfc\x08\xcd\xa3\xd5\x4b\x42\xf6\x81\x82\x40\x83"
+"\x5e\x40\x85\x69\x90\x19\xae\x13\xd6\x44\xe3\xf6\x40\x55\x5a\xe3\xf9\x45"
+"\xfb\xd3\xe8\x09\x0a\x05\x14\x97\xd0\xd5\xaa\x70\xd7\x8a\x04\x9a\x55\xb3"
+"\x7a\xa8\x1a\x5e\xc2\x5f\x42\xf0\x0c\xb8\x75\x0d\x51\x2a\x9c\x8a\x96\x8b"
+"\x6c\xa0\x6a\x32\x1d\xf5\x7f\xf4\x88\x70\xbc\x5a\x8a\x2d\x96\x4b\x7e\x9a"
+"\x8c\x97\x7f\x6c\x92\x9d\x57\x5d\x3e\x31\xb6\x35\x41\x24\x28\x43\x17\x43"
+"\x40\xa0\xc9\x82\x84\xdf\xe3\x17\x3a\xee\x3b\x89\x43\xa0\x74\x88\x09\x82"
+"\xf8\x6e\xc8\x71\xc2\x1a\x66\x71\x63\x85\x2f\x4a\x09\x8c\xea\x85\x42\x7e"
+"\x2e\x49\xd1\x85\xe8\x22\x1a\xc5\x15\x2b\xc2\xf3\x69\xac\x82\x8a\xde\x70"
+"\x65\x40\xfc\xa2\xc5\x37\x47\xcd\xa9\x41\xe7\x1c\x02\xab\x04\x15\x97\xe8"
+"\xe7\x31\x4a\x57\xf9\x08\x16\x0c\xb7\xe9\x36\x2f\x61\xbe\x65\xdb\x22\x3b"
+"\xaf\xba\x7c\x62\x6c\x6b\x82\x48\x50\x3c\xa1\x43\x2f\xe7\xa1\x63\x24\xf4"
+"\xaf\x23\x47\xf0\x3a\x50\xc1\xe3\x12\xa8\xbb\x04\xe2\x71\x8e\x13\x56\x93"
+"\xa2\x8b\x44\x91\x97\x0f\x06\xf1\x30\x0b\x9c\x5c\xeb\x10\x28\xdc\xb5\x3a"
+"\x04\xd6\xcd\x6a\xc2\xc2\x5f\xd4\x6c\x20\x81\x44\x96\x8a\x04\xc2\xcd\x49"
+"\xf2\x9a\x29\x08\x44\xfd\xab\x20\x50\xb7\x81\x40\x60\xb8\x4c\x19\x16\xf3"
+"\x62\x69\xeb\x60\x5b\x13\x44\xdc\x59\xae\x22\x81\x1a\xaa\x41\x2d\x4d\x10"
+"\xc8\x5d\x09\x14\xca\x3b\x87\x40\x3c\xce\x71\xc2\x3a\x94\x60\x66\x35\x26"
+"\x08\x84\xce\x11\x35\x93\xe5\x71\x97\x40\xe1\xae\x15\xb5\xa1\xb1\x4a\x18"
+"\x24\x0f\xba\xb8\x50\x58\x48\x20\x9e\x21\xcc\x11\xb1\x54\x49\xa0\xc1\x06"
+"\x24\x81\x50\xb0\x43\x60\x70\xa9\x2c\x25\x70\x81\x46\x77\x5e\x75\xf9\xa4"
+"\xd8\xde\x04\x11\xf7\x0a\x7b\x3f\x8b\x14\xb1\xcd\x9c\x3d\x22\x9c\x9b\x2e"
+"\x73\x69\xb2\x1a\x66\x24\x18\x15\x2e\x59\xe1\x80\x98\x45\x1c\x27\xac\xa3"
+"\x06\x0b\xb3\x72\x55\x4e\x89\xaa\xa8\x78\x17\x05\x58\x89\xa5\x43\x55\x93"
+"\xde\xfa\xf1\x2e\xaa\x31\xa1\x53\xc5\x7f\x16\x52\x87\x7d\x20\x9e\xe1\x98"
+"\x23\xa2\x59\xe1\x05\x20\x50\x28\x2e\x59\x84\xc6\x8b\xb5\xb3\x78\xe7\x2c"
+"\xe8\x7c\x6b\xbc\x81\x73\x55\xfe\x23\x5f\x7e\x06\xaa\xcb\x27\xc4\xf6\x26"
+"\x88\xb8\xd7\x44\x93\xdd\x15\xfc\x80\x2c\x48\xe1\xfa\x17\x68\xe3\x1d\xa1"
+"\xe4\x8c\xe3\x89\x6b\x55\x12\xa1\x73\x74\x05\xa6\xd4\x26\xfa\xbc\x15\x4e"
+"\x58\x63\x6b\x70\x91\x68\xbd\x18\x47\x1d\xa8\xcd\x9b\x1a\xbf\x8b\x87\x94"
+"\x19\xba\x5c\x15\xee\x5a\xa1\x90\xfa\x90\x0d\xa3\x2a\xa5\x2b\x3a\x7a\x42"
+"\x85\x29\x7c\xa2\xee\x98\x23\xe2\xc7\x1e\xcb\x65\x16\x42\xc5\xe5\x61\x18"
+"\x8d\x1b\x11\x2c\xf8\x2e\xd4\xb0\x1a\x66\xc5\x6a\x8c\x45\x3d\x90\x1d\x7b"
+"\x06\xaa\xcb\x27\xc5\x03\x4d\x10\x75\xd2\xfe\x71\xbc\x9d\x92\x23\xfb\x9c"
+"\x95\x6e\x9d\x63\x47\x29\x89\x3a\xc5\x90\xd6\x56\x4b\xa2\x7d\x61\xc7\x80"
+"\x70\x5f\xb4\xed\xf9\x74\xdf\x69\xb8\x35\xd0\x99\xed\x13\xdb\xce\x19\xb2"
+"\xd4\x2e\xb3\xc2\xe8\x29\x28\xd8\x3d\x5b\x0b\x75\xbc\xba\xfe\x4c\x55\x97"
+"\xcf\x17\x91\x95\x91\x07\x3b\xd8\x51\x78\x30\xc2\xcd\x0f\x9e\x3e\x6c\xca"
+"\xab\x0c\x6d\xfc\xf6\xf3\x71\x2a\xac\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xf0"
+"\xf4\x18\x5d\x0b\xa1\x42\xce\x37\xdf\xdc\x60\x04\xe6\x9f\x8f\x63\x10\xc2"
+"\x8b\xf3\x0b\x9e\x79\x61\x9f\xa5\xaf\x6d\x3e\x5b\x17\x56\x89\x86\xcd\x9a"
+"\xa5\x2d\x73\x37\xe6\x7f\xb6\x2a\xb7\x1d\x82\xc6\x8a\x96\x88\x0d\x64\x07"
+"\x37\xbc\x0e\xd1\x68\x3e\x51\x91\x8a\x90\x43\x62\x7e\x6c\xd0\x2d\xa6\xc9"
+"\xc2\x08\x82\x37\xf7\xda\xdb\x38\xfa\x4b\xfc\x5c\x5e\xb2\x3c\x2b\x18\xf5"
+"\x7d\x01\x7c\x93\x45\x56\xf4\x8d\x8f\x61\x34\x8f\xd1\x28\xd1\x91\xde\x80"
+"\xd8\xd6\x36\xda\x29\x22\xd0\x2a\x11\x2d\x51\x8c\x6d\x08\x8c\x3d\x61\x0c"
+"\xae\x97\x03\xc9\x15\x72\x96\xe5\x05\x03\x9e\xd2\x99\xdb\x27\x0a\xf9\x02"
+"\xfa\x06\x45\x3b\x43\xb2\xbf\x49\x67\x0b\xb3\x2e\x81\x68\x4b\x78\xa2\xe0"
+"\x2b\xcc\x8e\xe7\xb5\xdb\x81\x33\xb7\x83\xf9\xfd\x4d\x61\x12\x88\x04\x22"
+"\xff\xa4\x24\x35\x71\x91\xc6\xfe\xa6\x70\xb6\x2a\x42\x2d\x22\x81\x7a\xe1"
+"\xbc\x1d\x20\xa3\xbf\xc4\xa7\x64\x93\xf1\xb8\x20\xd0\x43\x4b\x66\x3c\x52"
+"\x0f\xd2\x78\x22\x5a\x4e\x2e\x87\xd1\xc8\xaf\x8c\xce\x00\x2b\x2e\x81\x18"
+"\x50\x30\x48\x7d\xd4\x0a\xe7\x8b\x3f\x34\x82\x8c\x2f\xda\xc9\xbc\xf0\x14"
+"\xba\x28\x02\x87\x01\x0c\xa1\x89\xb3\xd9\x1a\x8d\xbf\xdb\x5c\x33\x6a\xd2"
+"\xc6\x38\x56\x43\x0d\x9e\x05\x9b\xcf\xd3\xab\xf1\x73\x42\x78\xe9\xd0\x92"
+"\x4f\x68\xdd\xf9\x52\x84\xa0\x51\x8b\x95\x34\x98\xbf\x3a\xc0\x82\x35\x83"
+"\x6a\xc2\x9e\xc8\x21\x10\x03\x0a\xe2\x7b\x6d\x2b\xa2\xd3\x77\x98\x9f\x16"
+"\x56\xd8\x98\xe3\x4d\x54\xda\xf8\x8a\xd7\xf8\x4b\x3e\xa1\x70\xcb\x17\xad"
+"\x72\xb2\xfe\xae\xe8\x31\x31\x2a\x66\xa3\xb8\x68\x2e\xc9\x30\xad\xbf\x2c"
+"\x14\x17\x07\xaa\x92\xc0\x04\x6b\xa2\xea\x82\x2f\x37\xfc\xec\x87\xda\x40"
+"\xcd\x5f\x33\xd1\x18\xa9\x8b\xc0\x8a\x70\xb5\x98\xa8\x34\x7d\x6c\xae\xee"
+"\xa7\x30\xc4\xd4\x88\xf4\x26\x2a\x08\x3c\x51\x28\x1c\x5d\xe1\x15\x11\x2f"
+"\x8e\xe7\xcb\xf7\xf8\x6d\xc7\xf7\x24\x12\x58\x2a\x2e\x86\x17\xf7\xff\x02"
+"\x5f\xd4\xa0\x3d\x97\x24\x30\xc8\x2c\x24\x30\xcc\x2c\xf4\x0d\x8a\x04\xca"
+"\x58\x87\x1b\x09\x34\x99\xe5\xa3\x17\x4e\xa3\xde\x28\x41\x2d\xe9\x4d\x54"
+"\x44\x7d\xd3\xf8\x92\xc6\x1d\x02\xcb\xf9\xf2\xca\xc8\x61\x27\x7a\x99\x43"
+"\xe0\x83\x83\x0a\xbf\xac\x48\xb4\x25\x10\xbf\x39\x00\xca\xfc\x34\xea\xa3"
+"\x21\x1d\x09\x14\x06\xa1\x48\x20\x1a\x71\x46\x5c\x02\x7d\x34\x2f\x5d\x8c"
+"\x12\x7d\x84\x5b\xd2\x9b\x28\xf6\xa5\x01\x52\x5e\x34\x6b\x31\x97\x40\x7e"
+"\x5f\x3b\x1a\xa4\xe7\x2e\x90\x0e\x81\xd0\xca\x3f\x08\xbc\xd8\xcb\xdd\x79"
+"\x44\x96\xc2\x4b\x3e\x31\x00\xe8\x40\x01\x88\x95\xc1\x40\xe0\x56\x9a\x18"
+"\xb5\xd3\x43\x3f\x10\x26\x81\xd0\x3f\x2e\x93\xbb\x04\x03\x0a\xfa\x68\x40"
+"\xc7\x08\x97\xe8\xf8\x93\xe8\xcb\x61\x8b\xdb\xf4\x1c\x12\x88\x1c\x97\x17"
+"\x07\xea\xbc\xfa\x8e\x68\xc2\xe8\x41\xf3\x84\xc5\xc7\x85\x9f\x09\x0c\x6c"
+"\x5b\x5c\x0c\xd2\xb1\x3b\x4f\x1f\xa3\xfa\xe7\x06\x83\x95\xe3\x49\x79\x59"
+"\x36\x06\x21\x8c\x93\x26\x46\xb0\x85\xeb\x4f\xb0\x68\x92\xd1\x66\x19\xd9"
+"\x8d\xd0\x05\x98\x6a\xd7\xa5\x81\xa2\x2d\xda\xee\x28\x8b\x13\x38\x17\x3d"
+"\x85\x0a\x77\x9e\x27\x99\x6d\x5b\xfe\x7a\x92\xa1\x52\x4f\x86\x31\xf4\xd5"
+"\xed\xfd\x20\xd6\x06\x67\xb3\x68\xe3\xe8\x29\xb3\x3d\xbf\xc0\x71\xf8\x48"
+"\xdb\x5b\xe8\xe1\xf6\x8f\xee\xc4\x2b\xd4\x4e\x9d\xde\x74\x94\x88\x76\xe8"
+"\xe8\xf7\x86\x4e\x77\x99\x04\x1e\x41\x7b\xc4\xb6\xee\x0e\x35\x7e\x18\xe6"
+"\xb0\xfb\x33\x39\x3d\xd4\xd1\xe5\x29\x3c\x22\x1e\xe4\xae\x47\xe1\xe1\xd0"
+"\x9f\xa9\x07\x5e\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05"
+"\x05\x05\x05\x05\x05\x05\x05\x05\x85\x57\x08\xe6\xfc\xfc\xc2\xb9\xde\x84"
+"\xde\x2f\x28\x0c\xfc\x00\xf2\x87\xbb\x9d\xcd\xf9\x79\xd7\x32\xfc\xe4\x7c"
+"\xa8\x1d\x74\x01\x03\x9a\xeb\xbd\x71\x1d\x7a\x72\xdd\xd6\x10\x70\x6d\xde"
+"\x35\xee\x92\x3e\x56\xa0\x42\xf3\xb3\x9d\x02\xda\x0e\x54\xb0\x5e\x9e\xad"
+"\x42\x4f\x48\x6c\x15\xdc\xe1\xf9\x80\x57\x47\xd1\x62\xbc\x8d\x4d\xc1\xb6"
+"\xf1\x03\x48\xb3\xf3\x8d\x5e\x91\x25\xdd\xd0\xdb\x1e\xf9\x59\x81\x04\x15"
+"\x1f\xb9\xb9\x71\x1d\x3a\xf1\x0d\x4c\x91\xdd\xa1\x2d\x3f\xf2\xc3\x7d\xa6"
+"\x50\x2b\x8b\xb5\xba\x53\xa1\x8b\xcc\x21\xc9\x70\xa2\x68\x22\x84\xbb\x93"
+"\x30\x1b\x71\x18\x35\xdb\x05\x48\x3f\xae\x5b\x7b\xfd\x08\x6e\x95\xb8\xc3"
+"\x28\x56\x34\xde\x2d\x1e\x9b\x08\x44\x8e\xfc\x9d\xeb\x8f\xd5\x3a\x5e\x61"
+"\x68\xb4\xa3\xb8\x45\x02\xfd\x94\x0c\x95\xc4\x56\xa4\x7d\x7d\x32\x32\x85"
+"\x6f\x4b\x09\xc4\x7d\x22\xd2\x81\x58\x73\x9c\xab\x14\x2b\xba\xfb\x05\x02"
+"\xd4\x65\xb4\x53\x40\x14\xdd\x48\x6b\xfb\x65\xce\x89\x76\x01\x65\xf1\x77"
+"\xab\xe0\x0e\xee\xbe\x67\x8b\xa2\x70\x68\xed\x59\xd1\x0b\xb7\x7d\xb7\xc9"
+"\x68\x54\x46\x4c\x17\x71\x1b\xc8\x3b\xdf\xa3\xb3\x71\x41\xe0\x68\x5c\x7c"
+"\x1b\x29\x08\x2c\x2f\x11\xe1\x81\x64\x85\x9e\x2e\xe4\x49\xb2\x81\x7e\x52"
+"\x1c\x02\x8d\x7c\xfe\xcc\xed\xe4\x39\x9d\xdf\x0b\x19\x0d\x52\x98\x4d\x46"
+"\x68\x13\x7d\x93\x14\x6e\xe1\x97\x93\x7a\x61\xd6\x3e\x9c\x8c\xcb\x08\xea"
+"\xc9\xa8\x70\x84\xee\x93\xa2\xbb\x4a\x84\x8f\x15\x59\x21\xb8\x43\x4e\xe4"
+"\xf4\x91\x42\x5c\x4b\x9e\x27\xfb\xed\xa8\x43\xa0\xef\x54\x1c\xbf\xde\x34"
+"\xe8\xbd\x90\x0c\xe4\x10\xa4\xb7\xb5\x39\x2c\x0a\xcb\x85\x6a\xaf\xee\x9d"
+"\x0b\x11\x51\xf6\x5c\x08\xf6\x3d\x17\x02\xc3\x4c\x5b\x48\x2c\x97\xab\x46"
+"\xe3\xdd\xe6\x9a\x8c\x98\xae\x73\xc6\x2d\x63\xd5\x8e\x26\x6c\x8c\xc5\xe0"
+"\x67\x0b\x4c\x7c\x1b\x29\xbf\x77\x2c\x79\x1c\x0f\x24\xa7\xf8\xa2\x59\xe5"
+"\xd1\x85\x51\xe6\x10\x18\xcc\x93\x20\xb3\x99\xc1\xef\x0d\xd9\xc5\x78\x91"
+"\xd1\x24\x6d\xa2\x6f\x92\x44\x0d\xbf\x9c\xd4\x20\x57\x9b\x2f\x41\x69\x18"
+"\x41\x7d\x29\xd2\x26\x30\x42\x57\xa5\x8f\x15\x22\x25\x30\x6f\x54\x65\xe4"
+"\xf4\xd3\xbc\x64\x36\xd9\x9e\xb5\x64\xd5\x21\x30\x12\x25\xf8\xf5\xa6\x8f"
+"\xde\x3b\x2a\x03\x39\x5c\xa4\xb7\x31\x44\x7d\xb1\x82\xe5\xea\xc2\x99\xca"
+"\xb2\x6e\x17\xf3\x9c\xf2\xe5\xb3\xcf\x89\xc0\x01\x6a\x32\x73\x29\x5c\xf3"
+"\xe1\x77\x8d\x17\x30\x62\x3a\x8f\x97\x97\x13\x4b\x87\x96\x62\x16\x17\xa1"
+"\x69\xfc\xcc\xa0\x21\x6e\x61\xe8\x81\x58\xdd\xae\x87\x4c\xd7\x03\x49\x71"
+"\xf1\x90\xc5\x67\xd1\x4f\x8a\x24\x10\xbf\xd1\x62\xe8\x35\x25\x6f\x56\x07"
+"\x16\x63\xf5\xe4\x11\x0c\xd4\x5c\x2e\xe1\x87\x45\x1f\x50\x52\xc4\xc8\xe9"
+"\x35\x93\xf9\x97\x0e\x89\xe0\x0b\x51\x87\x40\x58\x73\x7c\xac\x60\x27\x8b"
+"\x05\x54\xd1\x79\x87\xf0\xd0\x1f\x2b\xf1\x0f\x98\x9f\x49\x02\x29\xfc\x35"
+"\xeb\xef\xe2\xfd\x72\x02\x39\xf8\x20\x57\xa8\x25\x1e\x2e\xbc\xb5\x40\x11"
+"\x15\x51\x76\xd5\xf1\xe4\xf0\x1c\x08\x3c\xc4\xcc\xfa\x5c\xd5\xcf\xcc\x40"
+"\xf9\x1e\xbf\x25\x02\x7e\x5b\x70\xcd\x55\x19\x18\x5d\xf6\x81\x3a\x8d\x27"
+"\x2a\xf8\xd1\x72\x0c\xbf\xde\x6d\x7b\x20\x29\x2e\x62\xdc\x75\x7b\x94\x3a"
+"\x04\x46\xf0\x23\x37\xa8\x77\x39\x8f\x0e\x4d\x62\x55\xa4\x06\x7d\x93\x20"
+"\x81\x1e\x4a\x84\xc7\xfc\x1a\x96\x36\x20\xe2\x7a\x77\x11\xe8\xf8\x58\xc1"
+"\x8f\x2d\x69\x43\x44\x4e\x17\x07\xa0\x9f\xfd\xa3\x7a\x33\x59\x77\x24\x30"
+"\x19\x25\xee\xa7\x87\x32\x90\x03\x14\x16\x19\x17\x04\x8a\x72\x4b\xa2\x08"
+"\x2c\xbb\xf2\xfc\x08\x8c\xd5\xcd\xfa\xc8\x69\x0f\xb5\xd1\x51\xc9\x11\x19"
+"\x31\x1d\x85\x26\x51\x1d\x19\x71\x09\x24\x34\x6e\x8a\x70\x21\xb1\x5a\x02"
+"\x46\x6d\xd7\x03\x49\x71\x11\x77\x9f\xb4\x69\xbb\x0f\xdc\xe7\x10\x18\x16"
+"\x61\xd2\x91\x1a\xf4\x4d\xb2\x81\xc0\x91\xd3\x3d\x04\xfa\x8e\xc8\xb0\xf7"
+"\x4e\x85\x8a\xf8\xb9\x25\xaf\x84\x25\x81\xe8\x17\x6b\xd4\x76\x09\xf4\x45"
+"\x87\x5c\x02\x65\x20\x07\x28\x0c\x63\xaa\xf7\x10\x78\x48\x94\xfd\xfc\x08"
+"\x2c\x62\x10\x80\xbd\xe8\x16\x8b\xdf\xd7\x8e\xd0\xd3\xae\x04\x86\xab\xe4"
+"\x30\x8d\x0b\x02\x99\x87\x86\x64\xa8\x93\x58\x0d\x26\x1e\x46\x7d\xa0\x4d"
+"\x60\x15\xfd\xa4\xb8\x04\x6a\xd1\x55\x87\x40\xc8\xf2\xb0\x24\x10\x7d\x93"
+"\xf4\x12\x08\xa5\xf5\x10\x18\x44\x09\xbc\xef\xaf\x6b\x17\x44\x85\x4c\x9a"
+"\x17\x91\xd3\x1d\x02\x97\x88\xce\xde\x75\x09\xd4\x43\x2b\x0e\x81\x4e\x20"
+"\x07\x28\x2c\x66\x95\x7b\x09\x84\xaa\x1d\x7e\x7e\x04\x56\x0d\x18\x2e\xe8"
+"\xca\x1d\x92\x28\xc1\x78\x3c\x3a\x42\xa1\x07\xa4\xf1\x72\x23\xb1\x84\xa1"
+"\x12\xf8\x1d\x8c\x51\xe4\xaf\x1b\x20\x15\x22\xac\x48\x02\x06\x91\x6a\xc7"
+"\x03\xc9\x62\x98\x8d\x35\xd1\x8d\x89\x70\x2b\x46\xc9\x7e\xe8\x8b\xf0\x42"
+"\x9a\x11\x7a\x6b\x59\x10\x38\x8e\xbe\x49\x90\x40\xe8\x46\x25\x81\x58\x5a"
+"\xb8\x8a\x04\x8e\x4b\x02\xb9\x87\x26\x0f\x55\x93\x75\x61\x88\x03\x83\x08"
+"\x5f\x6a\x47\x4e\x2f\xe3\xb7\x96\xe3\x74\xae\x3e\x44\x45\xbc\x26\x72\xe2"
+"\x3e\x74\xa1\x40\xe0\x5c\x50\x06\x72\x80\xbe\x15\x9d\x70\xc5\x9c\x72\x91"
+"\x40\xe1\x4c\x25\x21\x08\xfc\xe0\x99\xf3\xe7\x83\x81\xb1\x09\x2d\x06\xaa"
+"\x17\x8c\x4b\x4f\x26\x22\x62\x7a\x99\xf3\x38\x06\x46\x3f\x49\xd1\x08\xc9"
+"\x6f\xdb\xcb\xe2\x1b\x4a\xfc\xde\x31\x1e\xa6\xe3\xae\x07\x12\x8e\x31\x21"
+"\xf6\x72\x11\xda\x9c\xe8\x9c\xae\xd1\x25\xe9\x23\x05\x83\x2c\x84\xca\x2c"
+"\x44\xd0\x37\x09\xaf\x72\xf1\xe5\x64\x03\x57\x12\xed\x08\xea\x21\xfc\xb6"
+"\x72\x8e\xae\x2d\xd4\x09\xe6\x63\xd3\x31\x1a\xc0\x0a\xc1\xd9\x17\x9d\xc8"
+"\xe9\x7f\x85\x01\x97\x33\x0f\x9b\x13\xc1\x1c\x3c\x9c\x2e\xd0\x45\x19\xa2"
+"\x81\x39\x81\x1c\x74\xde\x48\xac\x62\xdc\x09\x2c\xf7\x02\x6f\xf2\x1a\xaf"
+"\xeb\xa2\x6c\x0e\x83\xf2\x73\xf3\x84\xe2\x39\xd5\xe5\xc9\x04\x23\x33\x94"
+"\xef\x9c\x92\x6e\x4b\x86\x8e\x40\x8a\x2e\xbe\x89\xec\x0a\x28\xd7\xf6\x40"
+"\x22\x3e\x7c\xdc\xbb\xe9\xd3\x47\x2d\x8a\x0e\x4d\xe4\xee\x6e\xdf\x24\x9d"
+"\xd2\x9c\x53\x49\x7b\x0d\x7d\xac\x74\xb2\xc1\x6f\x2d\xf5\xc3\x22\x0b\x4f"
+"\x88\x1c\x76\xbf\xdf\x24\x5d\x27\x38\x91\x1e\x3c\x81\xee\x98\xea\x12\x4e"
+"\xd9\x5d\xf1\x28\x9e\x3b\x36\xbb\x34\x34\x1a\x3f\x1b\xc7\x36\x2f\x01\xb4"
+"\xcd\x4e\x3c\x4d\xb6\x29\x49\x61\x5b\x78\x0a\x9b\xe6\xf0\xda\xd8\x8b\xa8"
+"\x88\x82\x82\x82\x82\x82\x82\x82\x82\xc2\x73\x85\xd0\x90\x2d\xb8\x4f\x5a"
+"\x1b\x35\x75\xdb\x42\x9f\xdf\x22\xa6\xc0\xc5\x1e\x9f\x8a\xba\xdd\x24\x91"
+"\x2e\x55\xe0\x0f\x0b\xc4\x8e\x3a\xeb\x18\x23\x1d\xd5\x7e\xdb\x14\xb7\x55"
+"\x88\x03\x01\xcf\x9a\xab\x29\xdc\xb2\x02\x2f\x02\xa8\x21\x43\x0f\xba\x96"
+"\xd8\xda\xa4\x68\xea\x81\xa7\xd4\x59\x0f\x6f\xa1\x1c\x33\x7b\xa2\x0f\x98"
+"\x55\x7c\xaf\x53\x49\x32\x87\x24\xbf\x7c\x99\x2d\x20\x43\x1a\xb0\x11\x27"
+"\x44\x7a\x47\xa7\x26\x9f\xc2\xb7\x0a\x71\x20\xf7\x1d\xc2\x10\x9e\xe4\x22"
+"\x2c\x89\x9f\x89\x3b\x73\xf1\x72\x73\x2e\x10\x2c\xc9\xcd\x07\x12\xe8\x1e"
+"\x84\x18\xd8\xa2\xfe\xfe\x1e\x02\x13\xf7\x4f\x61\x44\x59\x83\x3a\xea\x36"
+"\xa0\xb7\x1d\xdb\xc0\x71\xa6\xac\xfb\x65\x71\xed\xc0\xf6\x4e\x58\xf7\xad"
+"\x42\x1c\xc8\x7d\x03\x68\x55\x2b\xdc\xfa\xbc\xd8\xc0\x89\xbd\x51\x08\x7c"
+"\x85\x21\xbe\x1a\x4d\xe2\x13\x1c\x6a\xea\xce\x41\xeb\xbb\x20\x7e\x41\xe8"
+"\xe6\xce\xa1\x46\x0c\xd5\x76\xe8\xf4\xf7\x4c\xe1\x68\x43\x9b\x9b\x25\x7a"
+"\x12\x04\x40\xaa\xdc\xe2\xc9\x73\x44\xc4\x5d\x3f\xdf\xf4\xd7\x7d\x85\x68"
+"\x04\x5d\x53\xa2\xba\xaf\x8c\x1a\xd2\xf2\xa2\x87\x5a\x64\xbc\x09\xcd\x32"
+"\x59\x37\x0a\x51\x1d\x72\x3d\xb1\x1a\x12\x04\xc6\x6a\xa6\x11\x42\x27\x29"
+"\x26\x95\x31\xd6\xcf\x37\x23\xf5\x59\x7d\xee\xbc\xa7\x90\x17\x0a\xb9\xc2"
+"\x07\x4d\xcf\x6a\x40\x84\x7c\xbf\x20\x42\xbe\xcb\xa8\xb9\x24\xc2\x66\x75"
+"\xfb\xc5\x4a\x60\x6f\x14\x02\x83\x8e\x00\x37\x55\x2e\x08\x4c\xdc\x65\x84"
+"\xaf\xd2\x12\xfe\x12\x52\xc4\x68\x02\x8c\x7e\xb0\x0a\xdd\x97\x01\x07\x51"
+"\x5e\x1b\x6d\xb2\x50\x90\xf1\x0a\x86\x17\x48\xb0\x32\x46\x57\xb7\x47\x6b"
+"\x90\x1d\xc3\xb7\xb1\x43\xac\x18\x25\x18\x88\xdd\x25\xd0\x47\xa3\xe6\x0a"
+"\xb7\x0c\xc6\xeb\x1e\x9a\x8f\x2c\x30\xc3\x2e\x36\x1c\x02\x13\x84\x48\xa5"
+"\xdd\x6d\x27\x9c\x41\xb2\x3e\x9b\x28\xb0\x3d\x65\x0c\xdb\x5e\x35\xe8\x1a"
+"\xb5\x85\xca\xf0\x94\x13\xf2\xdd\x21\x30\xc9\x66\x23\x6c\xc7\x23\x31\x3f"
+"\x0e\x36\x44\x21\xd0\xf0\x85\xba\x29\xa2\x80\xd3\x28\x8f\xf3\x73\x18\x43"
+"\x18\x35\x76\xd0\xb8\x2e\x50\x52\x64\x73\xe1\xa5\x98\x85\x6f\xbe\x3c\xb4"
+"\xd9\xe0\x16\x3a\x2b\x09\x57\x4c\x36\x80\xaa\x37\x66\x02\x31\xd0\x78\xcb"
+"\x55\x90\xc0\x60\xdc\xc7\x22\x51\xa2\xe1\xab\x6e\xf1\x2d\x43\xb9\x56\x5e"
+"\x25\xe1\xa5\xa2\x75\xa8\x8a\x9a\x81\x3c\x3f\xcf\xe0\x3c\xf9\x0a\x3e\x46"
+"\x29\xb2\x78\xa8\xe2\xa1\x24\x61\x15\x31\x9c\x81\xbf\x46\xa4\x83\x14\x74"
+"\xa7\x42\xa8\x55\x5e\x86\x92\x96\x12\x16\x6f\x14\x2d\xa1\xf3\x17\x04\xa2"
+"\xd3\x10\xeb\xc5\x4a\xe0\xa6\x28\x04\x40\x20\x3a\xc2\x26\x42\xd1\xd4\xa0"
+"\x51\x8c\x61\x5f\xb6\xe0\x66\x8f\xd2\x50\x71\x09\xba\x2b\xec\xd7\x91\xc0"
+"\x38\x89\x8c\x63\x58\x02\x74\xee\x3e\x57\x41\x7d\x86\xaf\xae\xad\x26\x21"
+"\x3b\x8b\x60\xf0\x60\x03\x55\xc0\xc2\x1b\x82\x24\x90\x61\x20\xef\x15\xbe"
+"\x18\xab\x88\xb0\xcd\x3c\x7a\xc1\xd3\x2c\x3a\x04\xb2\x04\x76\x65\x22\xe4"
+"\x3c\xc7\x18\xeb\x16\xea\xb4\xa4\x83\x14\x74\x66\x41\xb0\x1c\x74\x42\x01"
+"\x79\xc9\x80\xdb\x5d\x04\xe6\x5f\x6c\x1f\xb8\x29\x0a\x41\xb9\xa4\x8b\x28"
+"\xce\x52\x53\xc7\x6f\xb3\x7d\xf8\x8b\x4e\x4e\x84\xb6\x2e\x51\x45\x43\x7d"
+"\x24\x10\x5a\x32\xfa\x29\x2a\xc9\xf0\x02\xb1\x8a\x1f\xbd\xa1\x44\x44\x76"
+"\xa8\x2d\xb6\x08\xea\x0c\x84\x8e\x48\x12\xb8\xc8\x6b\x44\x5f\x40\xbd\x24"
+"\x12\x38\x8b\xe3\xf0\x1c\xaf\xb8\x7d\x20\x89\xc7\x84\xce\x89\x70\x0c\x67"
+"\x50\x42\x02\x7d\x6c\x23\x81\xa8\x94\xeb\x26\xf0\x68\xdb\xcf\xca\x8b\xc3"
+"\xa6\x28\x04\xbc\x64\xbc\x8b\x23\xb0\xd4\xd4\x05\x59\x43\x97\x12\xc8\xa5"
+"\xc7\x21\x0c\xbb\xee\x12\x98\xb0\x04\x81\x55\x3f\x23\x7b\x24\x81\x1a\x3f"
+"\x29\x09\xac\xf3\x8a\xe7\x04\x06\xbf\xee\x48\xe0\x62\x84\x86\xcc\x4a\x71"
+"\x31\x2c\x25\x10\x76\x1a\x75\xb7\x09\xd7\x0c\xdd\x72\x08\x14\xe1\x0c\x04"
+"\x81\xd2\x41\x4a\x17\x81\x58\x76\x0f\x81\x05\x47\x02\x3d\xe7\x1e\x74\x89"
+"\xcf\x14\x1b\xa2\x10\xc0\x3c\xb0\xb8\x72\x31\xce\x43\xb8\x86\x7d\x1f\x2a"
+"\x95\x44\x1f\xa8\xc1\x81\xd8\x99\xa1\xda\x0e\xd8\x68\x42\xc3\x26\x30\xf8"
+"\x34\x12\x4b\xbc\xe6\xa3\xb7\xee\xc4\xaa\x26\x46\x52\xa7\x36\x0b\x71\x24"
+"\x30\xc8\xde\x29\x89\x20\x06\xe7\x9c\xef\xb9\x38\x4c\x63\x96\x63\x30\x50"
+"\x40\x17\x8b\x5f\x19\x94\xd9\xd9\x08\xe3\xec\x1d\x9c\xce\x25\x6a\x24\x52"
+"\x8a\x55\x0e\x01\x81\x67\x30\x9c\x81\xb8\x05\xe7\xf9\x02\x3a\x48\x19\x90"
+"\xfe\x90\x90\x40\xa1\x32\xc4\x90\xef\xf5\xf3\x22\xcc\xbb\xde\xc4\xa3\x96"
+"\xcb\x95\x8d\x4e\xf8\x9e\x23\x36\x44\x21\x40\xfd\x1c\x3b\xc9\x6c\x42\x1c"
+"\x4d\x1d\xa7\x2c\x8a\xbf\x40\xc0\x5d\x11\xe4\x1c\xd5\x76\xd8\x69\x05\x69"
+"\x83\x14\x9b\xe5\xc6\x3b\x9c\xb7\x55\x6e\x73\xf4\x3c\x5d\x85\x3e\x73\x05"
+"\xf6\x8f\xd2\x26\xb3\x43\x18\x88\x9d\x79\xb0\x4b\x88\x50\x60\x16\xa8\x4b"
+"\xe0\x26\x1d\x13\x5e\x4e\x4c\x96\x14\x6e\x51\x0c\xce\x16\xa8\x85\x4e\x52"
+"\x0e\x63\x00\x76\xe6\x81\x1c\x80\xed\x72\x93\x8b\xb0\xed\x09\x76\x86\x8a"
+"\xd5\xae\x90\xef\xa8\xad\x5b\x83\x5a\xd3\xe5\x08\xe5\xcc\x7c\x81\xfe\x06"
+"\x37\x44\x21\x00\x9c\xd2\xf7\xc9\x35\xd4\xd4\x25\xa0\x33\x93\xb1\xd4\x75"
+"\x38\x14\x21\x14\x63\x5a\x94\x10\x27\xa8\xf9\x11\x4f\x80\x78\xa2\x9b\xb2"
+"\x83\x63\x86\x4e\x91\x8d\x81\xd8\x85\xb6\xce\x29\xe9\x08\xe9\xc4\x48\x6f"
+"\xa3\xa3\x79\xdb\x07\xd9\xef\xdb\xb0\xb7\x13\xf2\xdd\xc5\x3e\x8c\x8b\xe0"
+"\x54\xe7\xe7\x08\xdd\x1e\x39\xbb\x8d\x93\x44\x85\x47\x01\x3c\x88\x6c\xfb"
+"\x38\xaf\xf0\x28\xd8\x5f\xf8\xd9\x44\x6e\x53\x50\x50\x50\x50\x50\x50\x78"
+"\x34\xf4\xbd\xfe\xa2\x6b\xf0\x72\xc3\x58\x5b\xf8\xa5\x7e\x19\x77\x71\x7e"
+"\xc1\x90\x71\x02\x1c\xa0\x4e\xac\x41\xfc\x0d\xb9\x6e\x61\x94\x00\x91\xe8"
+"\x84\x1a\x30\x37\x58\xaa\x25\xd9\xbd\x71\xb2\x01\x86\x50\x60\x74\x1d\xd9"
+"\x67\x37\xc7\x6c\xb1\x75\x31\x4a\x82\xb0\x86\xcb\x9a\xb4\xaa\x4b\xda\x01"
+"\x78\x42\x96\x4b\x12\x1f\x97\x3b\x2a\x3c\x34\x8c\x13\xce\xcb\x7f\x24\xe4"
+"\xec\xfc\xfc\x3d\xf1\xe4\xe6\xd9\x2a\x68\x01\x9a\xd7\xcd\xbb\xa9\x6d\xb3"
+"\xbe\xc8\x76\x66\x75\x3b\x09\x34\x54\x0b\xf7\xe8\x10\x13\x6c\x94\xc5\xc3"
+"\x32\xcc\x27\xbe\xf9\x33\xf1\xb3\x65\x11\x6a\x20\xc2\x44\x78\x81\xee\x83"
+"\x0d\x76\x3e\xb9\xc9\x08\x4e\x3e\xcc\xcb\x23\x85\xb2\xd1\x04\x1a\x0c\xa4"
+"\x22\x49\xa3\x1a\x8f\xd4\xc4\x42\x29\x6a\x95\x4c\x4a\x4b\xda\xda\x21\xb1"
+"\x78\xa8\xf8\xcc\xba\xa3\x81\xc2\x60\x88\x50\x03\x4f\x0d\x5f\x0c\xad\x70"
+"\x41\xe6\x96\x41\x0b\xfc\x4d\x38\xd4\x4d\x1d\x68\x57\x88\x3f\x87\x60\xa5"
+"\x46\x3b\x4e\x80\x8b\x58\x4d\xa7\x96\x4f\x9a\x3e\x4a\x02\x09\xe9\x0a\x35"
+"\xd0\x4b\x20\x56\x71\x53\x7c\x01\x27\x24\x01\x1e\x69\x88\xb7\x9b\xc9\xb8"
+"\x7e\x5e\x68\xde\xce\xd0\xa8\x87\x62\xec\x4d\x83\x1e\x65\xab\x65\x48\x39"
+"\xd4\xe4\x4b\x66\xcd\xac\x8a\xe5\x5c\x19\x48\xf0\x75\x08\x1c\xa8\x8b\x88"
+"\xe2\xe2\x86\xd0\x92\x21\x09\x8f\x13\xcf\x66\x02\xa3\xb1\x1a\xb1\xdc\xb3"
+"\xda\x04\x96\x9f\xc3\xbb\x55\xe9\x23\x1b\x63\x74\x47\xe0\x3f\x2a\xce\x04"
+"\x81\x85\xd9\x13\x85\xc8\x32\x10\x38\x7e\xdb\x90\x41\x85\x45\xa8\x01\x0c"
+"\x2f\x90\xc7\x30\x01\xfb\x57\x65\x7b\x42\x01\x39\x83\x21\xbd\xcf\xdc\x1e"
+"\x3d\x67\x14\x66\xcf\xcc\x16\xbe\x4f\xe2\x71\xab\x23\x21\x7a\xbb\x49\x8a"
+"\xe8\x66\x9f\xd8\xe4\xe2\x5c\xdc\x6f\x89\x57\xfa\x68\xec\x30\x0e\xcb\x58"
+"\x54\x7c\xb2\xbe\x97\xf0\xfb\x03\x35\x3f\xc3\x85\x90\x62\x09\x09\x8c\x40"
+"\x4e\xb7\xce\x48\x4d\x11\xda\x50\x84\x91\x39\x10\x54\xd4\x2d\x88\xa0\x05"
+"\xc2\x90\x0e\x23\x91\xa3\x58\x47\xa2\x70\x9f\xd0\x80\x2f\x9a\x44\x1d\x5f"
+"\x13\x09\x4c\x8e\x39\x04\xa2\x02\x0f\x12\x9f\xe1\x03\xa8\x24\x90\x5f\xb4"
+"\x50\xfd\x55\x43\xc5\x59\xac\x16\x61\xd1\x44\x25\x48\xc5\xeb\xf8\x44\x43"
+"\x67\xa4\x1d\x6a\x40\x84\x17\x78\xb7\x69\x13\x9e\x14\x37\x57\x5a\xaa\x1a"
+"\x2c\xb1\x1c\x61\x76\xdd\x43\x97\x23\xcb\x9c\xd1\x64\x5d\xe3\x49\x16\xa7"
+"\x36\x3d\xcf\x91\x40\xad\xa9\x35\xcd\xb8\xe1\x12\xa8\xd1\x02\x3a\xd9\x27"
+"\xba\x6c\x60\xc5\x52\xb8\xe6\xaf\xe3\x42\x34\x0e\xb7\xc5\xc7\x6c\xb6\x8f"
+"\x2e\xa3\xf9\x2f\xc6\xd1\x45\xdb\x00\x2c\x05\x6e\x15\x0a\x54\x42\xc8\x27"
+"\x1a\xd2\x99\x4e\xd0\x82\x2a\xde\x07\x24\x10\xe3\x22\xe0\x32\x00\xc2\xbc"
+"\xe6\x10\x88\x61\x64\xe3\xfe\xea\x56\x51\x4c\x76\x96\xc0\x65\x8c\xe6\x3d"
+"\x8e\x8a\x33\xd4\x89\x35\x09\x5a\x58\x60\x14\xef\x12\xd4\x4e\x5c\x27\x86"
+"\x1a\x30\xeb\x06\xdd\x83\x41\x7d\xcb\x63\xcc\x23\xfa\x39\xbf\x68\xd0\x66"
+"\xcd\x5f\x35\xd9\x10\x8d\xf3\xfb\x66\xa8\xc8\xe6\xcc\xba\x8f\xa1\xaa\xcd"
+"\x72\x14\x14\x5a\x13\x1a\xf5\xa0\xf1\xdf\x05\x81\x06\x3d\x4f\x6f\xc3\x92"
+"\x27\x86\x74\xed\x5e\x8e\xc7\x40\xfa\x70\x91\x81\x5c\x7d\x4c\x83\x9c\x96"
+"\xcc\x10\x6a\xcb\x6d\x16\x27\xda\x32\x32\x47\x45\xc0\xe2\x4e\xd0\x82\x8a"
+"\x08\x5a\xc0\xf3\x89\x52\x39\x2e\x09\xc4\xb8\x08\xb8\x00\x81\xb5\x88\x43"
+"\x20\xe1\x3f\x96\xad\xb0\xf5\x0c\xdd\x4f\x3b\x04\xb2\x00\x5f\x2d\x37\x85"
+"\xf3\x7f\x61\x88\x84\xf5\x43\xab\x95\x85\xfb\x84\x88\x31\x1a\x43\x0d\xf8"
+"\xea\xf2\x75\xfa\x3d\xfe\x03\x48\x1b\xde\x54\x19\x72\x3e\x5c\xf5\xd7\x85"
+"\x62\x23\x0c\x15\x47\xe3\xb5\x3a\x52\x88\x8a\x8e\x36\x81\xf6\x9b\xff\xcd"
+"\x67\xc9\x2f\x44\xe6\x60\xf0\xc0\x85\x98\x96\x51\x28\x10\x10\xf0\xb6\x04"
+"\x1a\xd8\xa5\x62\x4e\x56\x58\x50\x30\xc0\x50\x87\x6c\x58\x18\xd2\xc6\x95"
+"\xc0\x1a\xf1\x15\x0a\x47\x9b\x42\xff\x24\x7c\xee\xdf\xe3\x79\x49\x20\xc6"
+"\x45\xc0\x05\x08\xa4\xcb\x2e\x81\x70\x40\x09\x8d\xf9\x9e\x29\x81\x26\x89"
+"\x50\x0b\xa3\x79\x73\x54\x1f\xa1\x3d\xf5\x92\x4b\x20\x85\xaa\x8b\x51\x03"
+"\xeb\xe4\xab\xfb\x05\x81\x2b\x23\x27\xeb\xf2\x63\x19\x43\xda\x7e\xa1\x8d"
+"\x10\x5e\xb6\x1f\x58\x46\xd3\x21\xd8\x8b\xda\x5b\x97\x40\xe8\xec\xf8\x9a"
+"\xdb\x07\x12\x6d\x94\x89\x85\x84\xe3\x7e\x4a\x81\x46\xf4\xbb\x5f\xc7\x45"
+"\x9a\xb5\xfb\x50\xb9\x6b\x32\x1c\x88\x06\xea\x11\xb4\xcd\x5c\x2b\xa3\xf7"
+"\xf8\x12\x41\x8b\x29\x11\xb4\xa0\x22\x83\x16\x10\x27\x68\x41\xa8\x4d\x60"
+"\x0d\x97\x81\x9a\x54\x24\xb6\x09\x44\x25\xd8\x33\x83\x0e\x04\x9e\x24\xe7"
+"\x78\x25\xb1\x44\x8e\x9c\x10\xb1\xce\xbb\x09\xbc\x47\xe3\x9a\xe8\x81\x85"
+"\x45\x60\x5d\x84\x17\xe0\xf7\xb5\x08\x50\x74\xea\x02\xda\x42\xc0\xad\xde"
+"\x77\xc8\x95\x40\xfc\x5a\x43\x10\x48\x12\x76\xbc\x8b\xc0\x64\xfc\x1f\x9b"
+"\x62\x14\x16\xb4\x07\xab\x72\x49\x84\xf4\x91\x11\x62\x8f\x24\x4d\x0c\x9d"
+"\x81\xda\x36\x62\x3a\x12\x18\xf7\x88\x3b\x33\x50\x47\x7d\x47\x92\xa0\x8d"
+"\x1e\x2d\x09\x33\x76\x19\xb4\x00\x0d\xe9\x1c\x02\x81\x55\x87\x40\x8c\x8b"
+"\x90\x10\x12\x68\x44\xe8\xd0\x6c\x87\x40\x5e\x79\x96\x06\x06\x7c\x59\x6b"
+"\x92\xbb\xe6\x52\xb8\x9e\x1c\xb7\x8a\x68\x4f\xcd\x0c\x5a\x42\x6b\x33\x61"
+"\x78\xc6\x1b\xa6\x70\xa8\x80\x21\x04\x9c\xf0\x02\x68\x4e\xc7\x0c\x26\xe6"
+"\x87\x11\x16\xd0\x9b\x7e\x66\x56\x7d\xec\x34\x6c\x43\x6b\x12\xaa\x3a\x67"
+"\xc2\x03\x4d\xb1\x8a\x44\xb4\xe7\x81\x28\xef\x1a\x97\x8b\x18\x84\x31\x20"
+"\x73\x15\xf2\x6a\xe0\x92\x58\x36\xb1\x0f\xa4\x3a\xda\x87\xe1\x79\xe1\x3a"
+"\x4e\x40\x9b\x30\xa5\x5c\x02\x02\x47\xf1\x73\x2e\x9d\x63\xd0\x02\x53\x04"
+"\x2d\xd8\x0b\x7d\x20\xd6\x26\x2a\x09\xc4\xb8\x08\xb8\x0c\x30\xf3\x14\x17"
+"\x9a\x39\xec\x03\xad\x72\x29\xc6\xc6\x6e\x3f\xbb\x61\x24\xc2\x40\xc2\xcb"
+"\xb6\x65\x52\x68\x86\xf0\x24\x60\x70\xc6\x59\x88\x43\xe3\x41\xc3\xb3\x0a"
+"\x67\x27\x64\x73\x90\xa1\x06\x92\x8c\xae\x04\x69\x95\x70\x1b\x2e\x39\x80"
+"\xd7\x53\xb7\xd1\x9a\xd5\xc2\xb1\x53\x08\x15\x5c\xf1\x28\x1d\xa3\x18\x1b"
+"\x1c\xfa\x54\xbf\xf8\x04\xab\xcf\xbe\x3b\xb6\x80\x3d\xc1\x0f\x74\x55\xb7"
+"\xff\x4a\x70\x21\x9a\x18\x9c\xc2\x94\x56\x88\x0d\x9d\x3c\x2c\x31\xa1\xb8"
+"\xf3\xd9\x18\xe9\x3b\x81\xf2\x0a\x63\x78\x90\xae\xd5\xa3\x3e\xce\x92\x94"
+"\xb1\x65\x59\x5f\x0c\x5a\xc0\xdc\xa0\x05\x2b\x7e\x8a\xc3\x8a\x51\x66\x79"
+"\x8c\x8b\x80\x4b\x82\x9d\xb5\x99\x1f\x64\xfd\x04\x67\x63\x78\x88\x81\x66"
+"\x75\xcf\x6e\x1c\xc6\x38\x01\x18\xa3\xfb\x48\x48\xdb\x7b\x6a\xab\x03\x64"
+"\xe0\x00\x69\xaa\x26\xc2\x0b\x8c\x04\x88\x0e\x8b\x50\xb8\x89\x98\xe0\x9e"
+"\xd3\xd0\xf0\x50\x47\x76\xd2\x3d\x87\x53\xe7\xe3\x35\xa9\x95\x33\xd6\xe6"
+"\xdd\x80\x52\x18\xa8\x40\x04\x2b\xd0\x3a\x85\x39\x69\x8e\xbe\x6f\x0f\xc6"
+"\xff\x6e\xe7\xb4\xb9\xbe\x9b\x82\x16\x74\x5f\x89\x88\x7a\x80\x8a\xc4\x44"
+"\xa0\x73\x8a\xe7\x25\x88\x62\x80\xcf\x0f\x9e\xa6\xcb\x92\x59\x1d\xe9\x8d"
+"\x7f\xa4\xed\x79\xac\xdc\xf4\xe6\x53\x07\xf0\xb2\x9f\x36\x83\xe7\x8c\x20"
+"\x34\x68\xa3\x6d\x4b\xe7\x63\x63\xf6\xd3\xb4\x19\xe3\xa9\xad\xf2\xcc\x67"
+"\xff\x1a\x61\x67\x71\xa6\x00\x7d\xdc\x58\xa0\xb3\xb9\x65\x6f\xf0\xc8\xe8"
+"\xe4\xa4\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xa0\xf0\xb3\x84"
+"\x9e\xda\xe8\x7b\x49\x4f\x91\x9d\x7b\x03\x77\x20\xb5\x63\x59\xfd\x8c\xe0"
+"\xcd\xa6\x67\x9c\xbf\xde\x6c\x26\x93\x4b\x65\xd3\xe9\xf4\xd7\xde\x74\x3a"
+"\xe4\xcd\xce\x68\xd9\x6c\x66\x7a\x0a\x12\xd2\x1f\xe1\x5f\x38\x90\xe8\xb8"
+"\xff\x5b\x71\xfc\x54\x66\x82\x4c\xa5\x6f\x90\xfe\xf4\x4c\x36\x9d\x91\x6b"
+"\xd3\x78\x70\xfa\x26\x1e\x94\x81\x83\x26\x48\x36\x83\x67\x7e\x17\x20\xe4"
+"\x78\x26\x9b\x79\x76\xef\x43\x5f\x1c\x26\xd3\xf8\xf7\x18\xfc\x9d\xcc\x84"
+"\x0e\x64\x27\x7e\x9b\x26\xde\xaf\x61\xfb\x5b\xf2\xda\xc4\xaf\x6e\x90\xc9"
+"\x69\x2d\xfd\x05\x99\xfc\xc8\x9b\xfe\xe2\x60\x7a\x1a\x8e\xec\x4f\x7f\xf4"
+"\x49\x3a\xf0\x5b\x58\xf7\x7e\x49\x88\x96\x4d\x87\x48\x0e\x13\x9d\xb5\xaf"
+"\x87\xe1\xc0\x69\xd8\xf6\xe6\xc8\x41\xe4\x74\x9a\x64\x6f\x78\xd3\x37\x89"
+"\x17\xce\xc8\x7e\xf5\x62\xaf\xf5\x99\xe0\xb5\xb4\xf3\xd7\x9b\x86\xcb\x3b"
+"\x36\x01\x6b\xa1\xef\x60\x3b\x43\xfa\x27\xfa\xbf\x42\x02\xd2\x5f\xe8\xfd"
+"\x48\xa0\x96\xfd\x1a\x8e\xfc\x55\xfa\xa3\xfe\xf4\x04\x1e\xdc\x3f\x01\x9b"
+"\xd9\xf4\x97\xe4\x63\x24\xd0\x59\xfb\x02\x0f\x44\x02\xf7\x1c\x87\xd4\x74"
+"\xe0\x57\xb0\x03\x6e\x43\x06\x6e\x49\x40\x2c\xbf\x38\xb4\x09\x04\x5a\x40"
+"\xbe\x90\xc0\x2f\x86\x49\x3f\x34\xbf\xfe\x89\x63\xe9\x09\x1d\x09\xec\xd7"
+"\x81\xb1\x2f\xa4\xb0\x4a\x02\xa1\x69\x92\x29\x3c\xf1\x3b\x60\x5a\x12\x28"
+"\xd7\xc4\x81\x13\xb0\x3d\xe3\x25\xfd\x9f\xa4\xa7\x25\x81\xaf\xa5\x27\xe0"
+"\x04\x59\xc4\x2f\x0d\x6d\x02\x8f\xa5\x43\x72\xcd\xfb\x05\x5c\x6a\x7f\xfa"
+"\x06\x48\x60\x1a\xba\x39\x20\x70\x12\x7a\x49\xe0\xe5\x18\x5e\x3f\x10\x88"
+"\x44\x4e\xa6\x2f\x8b\xf6\x38\x33\x99\x9e\x96\x04\xca\x35\x71\x20\x6c\xff"
+"\x2b\x74\x98\xfd\xa9\x74\x46\x12\x08\x9d\x23\x8a\x6f\xbf\xe8\x04\x7e\x61"
+"\x68\x13\x28\x3b\x43\x58\xcb\x22\x03\x5a\x3a\x7d\x10\xa4\x29\x9d\x09\x90"
+"\x74\x26\x2d\x79\x79\x4d\x12\x98\x83\xfe\x11\xb8\xc8\x4d\xe3\xe1\x33\xc0"
+"\xb4\x43\xa0\x58\x73\x09\x04\xd6\x40\x9c\xb3\xe9\x0f\x5d\x02\xd3\x5f\xe3"
+"\xb9\xd3\x2f\xea\x32\x9f\x1d\x36\x13\xf8\x09\x32\x00\xe2\x96\xc3\x41\x16"
+"\x2e\x39\xfd\x75\x2f\x81\x33\x59\x60\x55\x4f\xcb\xfe\x6c\x06\xfa\xbe\xef"
+"\x24\x81\x62\xcd\x25\x30\x27\x08\x04\x11\x7e\x25\x08\xd4\x42\x82\x40\xa7"
+"\x09\xeb\x82\x40\x90\xbe\x89\xbd\x90\xf8\x15\x34\xe1\x63\x92\x97\xdf\xe2"
+"\x11\xa2\x0f\xfc\x88\x60\x27\x88\x98\x81\xd4\xaf\x1d\x02\x71\xcd\x25\xf0"
+"\x23\x6c\xc2\x13\x20\xc7\x0e\x81\x1f\xfd\xa2\x9b\xb0\xf7\x23\xf8\xfb\x5b"
+"\xd9\xc3\xc3\xda\xb4\x77\xa2\x1f\xc7\xd7\x89\x29\x98\xa6\xdc\xc0\x41\x84"
+"\xa4\xba\x07\x91\x5f\x21\x47\x9f\x7c\x2d\x4e\x9f\x01\x59\x74\x09\xd4\xbb"
+"\x09\x4c\xc1\x58\x3c\x01\x72\xfc\x4a\x0c\x22\x62\xec\xfd\x15\x4c\x43\xc8"
+"\x41\xdc\x0e\xf5\x87\x0e\x8a\xab\x9d\x0c\x91\xd7\x50\x02\x75\x32\x83\xbc"
+"\x60\xbf\x26\x25\x70\xba\x8b\x40\x32\xe9\x12\x88\x6b\x6d\x02\xf7\xfc\xea"
+"\x23\x20\xd0\x9b\xee\x4c\x63\xc8\x2f\x73\x1a\x83\x17\x95\x9d\x70\x26\xd2"
+"\x7a\x0e\xb7\xf5\x2c\x99\x0c\x80\xf0\x4d\x4c\xde\x24\xc7\xa6\x75\x68\xc5"
+"\xc7\x71\x7a\x77\xdc\x99\x48\x4f\x8b\xd1\xf8\x13\xd9\x84\xb1\xab\x93\x04"
+"\xca\x35\x97\xc0\x69\x32\x39\xf1\x1a\x1c\x96\x45\x02\x0f\x88\x89\xf4\xb7"
+"\xbf\xc8\x89\x34\x3c\x6f\xe5\xb2\xe9\x61\xf1\x28\x97\xce\x64\x27\x60\x1b"
+"\x1e\xb9\xe0\x31\x0e\x27\xd5\x93\xd9\x5c\x26\x04\x03\x49\x2e\xfd\xc5\x14"
+"\x8c\xc5\xe2\x51\xee\x13\xb9\xf2\x61\xf2\xfc\xa3\x01\x00\x00\x00\xa9\x49"
+"\x44\x41\x54\x3a\x7d\x1d\x36\xa7\x32\xd3\x30\x17\x84\x93\xbe\x96\x6b\x90"
+"\x02\x53\x1f\x1d\x33\x49\xa7\xb2\x39\xa0\x52\x3e\x04\x06\x70\x47\xee\x17"
+"\xf9\x28\xd7\x05\x6f\xaa\xe7\x02\xbd\xa1\x61\x3d\x35\xf1\x08\xe7\x1d\xd8"
+"\x62\xcd\x85\xd6\xc9\x60\xf8\xfa\x2f\x9c\x3f\x05\x05\x05\x05\x05\x05\x05"
+"\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05"
+"\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05"
+"\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05"
+"\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05"
+"\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05"
+"\x05\x85\xed\xf1\xff\x01\xf2\x3f\x50\x38\x83\xb3\x93\x7a\x00\x00\x00\x00"
+"\x49\x45\x4e\x44\xae\x42\x60\x82";
+
+static const unsigned int LICENSE_SIZE = 72771;
+
+static const char* LICENSE_NAME = "license.png";
+
diff --git a/game/code/main/gamecube_extras/screenshot.c b/game/code/main/gamecube_extras/screenshot.c
new file mode 100644
index 0000000..bf44be2
--- /dev/null
+++ b/game/code/main/gamecube_extras/screenshot.c
@@ -0,0 +1,345 @@
+/*---------------------------------------------------------------------------*
+ Project: onetri
+ File: screenshot.h
+
+ Copyright 1998, 1999, 2000 Nintendo. All rights reserved.
+
+ These coded instructions, statements, and computer programs contain
+ proprietary information of Nintendo of America Inc. and/or Nintendo
+ Company Ltd., and are protected by Federal copyright law. They may
+ not be disclosed to third parties or copied or duplicated in any form,
+ in whole or in part, without the prior written consent of Nintendo.
+
+ Created 05-25-01 by Steve Rabin
+
+ *---------------------------------------------------------------------------*/
+
+#include <main/gamecube_extras/screenshot.h>
+#include <dolphin/hio.h>
+#include <string.h>
+
+
+#define WIDTH_SCREENSHOT 640
+#define HEIGHT_SCREENSHOT 480
+#define SIZE_SCREENSHOT_RGB WIDTH_SCREENSHOT*HEIGHT_SCREENSHOT*3
+#define SIZE_SCREENSHOT_YUV WIDTH_SCREENSHOT*HEIGHT_SCREENSHOT*2
+
+#define XFB_BASE 1
+#define XFB_RANGE 6
+#define EFB_BASE 11
+#define EFB_RANGE 9
+#define PING_BASE 20
+#define PING_RANGE 9
+#define MINIMIZE_BUFFER_TRUE 35
+#define MINIMIZE_BUFFER_FALSE 36
+
+
+typedef enum {
+ GRAB_NOT_TRANSFERRING,
+ GRAB_TRANSFERRING
+} GRAB_STATUS;
+
+
+//Globals
+BOOL g_connected = FALSE;
+BOOL g_ping_received = FALSE;
+u32 g_received_signal = 0;
+BOOL g_mail_waiting = FALSE;
+BOOL g_minimize_buffer = FALSE;
+s32 g_usb_channel = -1;
+u8* g_data;
+
+
+static BOOL HostIOEnumCallback( s32 chan )
+{
+ g_usb_channel = chan;
+ return( FALSE );
+}
+
+static void HostIOCallback( void )
+{
+ //Mail is waiting for us - don't look at it yet
+ g_mail_waiting = TRUE;
+}
+
+
+
+static void CopyoutEFB( u8* image_buffer, u32 width, u32 height )
+{
+ u16 i, j;
+ u32 color;
+
+ for( j=0; j<height; j++ ) {
+ for( i=0; i<width; i++ ) {
+ GXPeekARGB( i, j, &color );
+ image_buffer[2] = (u8)(color & 0x000000FF);
+ image_buffer[1] = (u8)((color & 0x0000FF00) >> 8);
+ image_buffer[0] = (u8)((color & 0x00FF0000) >> 16);
+ image_buffer += 3;
+ }
+ }
+}
+
+
+
+static void CopyoutPortionEFB( u8* image_buffer, u32 width, u32 startByte, u32 totalBytes )
+{
+ u32 color;
+
+ u16 x = (u16)((u32)(startByte/3) % width);
+ u16 y = (u16)((u32)(startByte/3) / width);
+ u32 component = startByte%3;
+
+ u32 count = 0;
+ while( count < totalBytes )
+ {
+ GXPeekARGB( x, y, &color );
+
+ if( component == 0 ) {
+ count++;
+ image_buffer[0] = (u8)((color & 0x00FF0000) >> 16);
+ }
+ if( component <= 1 && count < totalBytes ) {
+ count++;
+ image_buffer[1 - component] = (u8)((color & 0x0000FF00) >> 8);
+ }
+ if( component <= 2 && count < totalBytes ) {
+ count++;
+ image_buffer[2 - component] = (u8)(color & 0x000000FF);
+ }
+ image_buffer += 3 - component;
+ component = 0;
+
+ x++;
+ if( x >= width ) {
+ x = 0;
+ y++;
+ }
+ }
+
+
+}
+
+
+
+static void TakeScreenshotEFB( SCREENSHOTAllocator allocator )
+{
+ if( g_data == 0 ) {
+ g_data = (u8*)allocator( SIZE_SCREENSHOT_RGB );
+ }
+ CopyoutEFB( g_data, WIDTH_SCREENSHOT, HEIGHT_SCREENSHOT );
+ DCFlushRange( &g_data[0], SIZE_SCREENSHOT_RGB );
+ OSReport( "SCREENSHOT: Saved off screenshot to local memory.\n" );
+}
+
+
+
+static void TakeScreenshotXFB( void* bufferXFB, SCREENSHOTAllocator allocator )
+{
+ void* buffer = bufferXFB;
+ if( g_data == 0 ) {
+ g_data = (u8*)allocator( SIZE_SCREENSHOT_YUV );
+ }
+ memcpy( g_data, buffer, SIZE_SCREENSHOT_YUV );
+ DCFlushRange( &g_data[0], SIZE_SCREENSHOT_YUV );
+ OSReport( "SCREENSHOT: Saved off screenshot to local memory.\n" );
+}
+
+
+
+static void WriteScreenshotPortionEFBtoUSB( u32 chunk, SCREENSHOTAllocator allocator )
+{
+ u32 i;
+ u32 size1K = 1024;
+ u32 size100K = size1K*100;
+ if( g_data == 0 ) {
+ g_data = (u8*)allocator( size1K );
+ }
+ for( i=0; i<100; i++ )
+ {
+ CopyoutPortionEFB( g_data, WIDTH_SCREENSHOT, (chunk*size100K)+(i*size1K), size1K );
+ DCFlushRange( &g_data[0], size1K );
+ while( !HIOWrite( 0x00000500 + (i*size1K), g_data, (s32)size1K ) ) {
+ //Spin until communication is successful
+ }
+ }
+}
+
+
+
+static GRAB_STATUS GrabChunk( u32 chunk,
+ void* bufferXFB,
+ SCREENSHOTAllocator allocator,
+ SCREENSHOTDeallocator deallocator )
+{
+ if( chunk >= XFB_BASE && chunk < XFB_BASE + XFB_RANGE )
+ {
+ void* buffer = 0;
+
+ if( chunk == XFB_BASE )
+ { //save off XFB screenshot
+ if( !g_minimize_buffer ) {
+ TakeScreenshotXFB( bufferXFB, allocator );
+ }
+ }
+
+ //transfer chunk
+ if( g_minimize_buffer ) {
+ u8* fb = (u8*)bufferXFB;
+ buffer = (void*)( &fb[100*1024*(chunk - XFB_BASE)] );
+ }
+ else {
+ buffer = (void*)( &g_data[100*1024*(chunk - XFB_BASE)] );
+ }
+
+ while( !HIOWrite( 0x00000500, buffer, 100*1024 ) ) {
+ //Spin until communication is successful
+ }
+ OSReport( "SCREENSHOT: Wrote chunk #%d.\n", chunk );
+
+ //communicate that the chunk is ready to be read
+ while( !HIOWriteMailbox( chunk ) ) {
+ //Spin until communication is successful
+ }
+ OSReport( "SCREENSHOT: Notify of write chunk #%d put in mailbox.\n", g_received_signal );
+ }
+ else if( chunk >= EFB_BASE && chunk < EFB_BASE + EFB_RANGE )
+ {
+ void* buffer;
+
+ if( chunk == EFB_BASE )
+ { //save off EFB screenshot
+ if( !g_minimize_buffer ) {
+ TakeScreenshotEFB( allocator );
+ }
+ }
+
+ if( g_minimize_buffer )
+ {
+ WriteScreenshotPortionEFBtoUSB( chunk - EFB_BASE, allocator );
+ }
+ else
+ { //transfer chunk
+ buffer = (void*)( &g_data[100*1024*(chunk - EFB_BASE)] );
+ while( !HIOWrite( 0x00000500, buffer, 100*1024 ) ) {
+ //Spin until communication is successful
+ }
+ }
+ OSReport( "SCREENSHOT: Wrote chunk #%d.\n", chunk );
+
+ //communicate that the chunk is ready to be read
+ while( !HIOWriteMailbox( chunk ) ) {
+ //Spin until communication is successful
+ }
+ OSReport( "SCREENSHOT: Notify of write chunk #%d put in mailbox.\n", g_received_signal );
+
+ }
+
+ if( chunk == XFB_BASE + XFB_RANGE - 1 ||
+ chunk == EFB_BASE + EFB_RANGE - 1 )
+ { //free screenshot buffer space
+ if( g_data ) {
+ deallocator( g_data );
+ g_data = 0;
+ }
+ return( GRAB_NOT_TRANSFERRING );
+ }
+ else {
+ return( GRAB_TRANSFERRING );
+ }
+
+}
+
+
+
+static void CheckMail( void* bufferXFB,
+ SCREENSHOTAllocator allocator,
+ SCREENSHOTDeallocator deallocator )
+{
+ u32 temp = 0;
+ u32 escapeCount = 0;
+ GRAB_STATUS status = GRAB_TRANSFERRING;
+ while( status == GRAB_TRANSFERRING )
+ {
+ escapeCount++;
+ if( g_mail_waiting )
+ {
+ if( HIOReadMailbox( &temp ) ) {
+ g_mail_waiting = FALSE;
+ status = GRAB_NOT_TRANSFERRING;
+ if( temp >= PING_BASE && temp < PING_BASE + PING_RANGE )
+ { //ping message received
+ while( !HIOWriteMailbox( temp ) ) {
+ //Spin until communication is successful
+ }
+ OSReport( "SCREENSHOT: Sent ping back.\n" );
+ }
+ else if( (temp >= XFB_BASE && temp < XFB_BASE + XFB_RANGE) ||
+ (temp >= EFB_BASE && temp < EFB_BASE + EFB_RANGE) )
+ { //request for screen capture
+ escapeCount = 0;
+ g_received_signal = temp;
+ OSReport( "SCREENSHOT: Grab screenshot request.\n" );
+ status = GrabChunk( g_received_signal, bufferXFB, allocator, deallocator );
+ }
+ else if( temp == MINIMIZE_BUFFER_TRUE )
+ { //request to use minimal memory (100K), however game pauses
+ g_minimize_buffer = TRUE;
+ }
+ else if( temp == MINIMIZE_BUFFER_FALSE )
+ { //request to not use minimal memory and keep game running
+ g_minimize_buffer = FALSE;
+ }
+ }
+ }
+ if( !g_minimize_buffer )
+ { //Escape loop since were not pausing the game to transfer
+ return;
+ }
+ if( g_minimize_buffer && escapeCount > 150000000 )
+ { //The PC screenshot app has died - escape
+ if( g_data ) {
+ deallocator( g_data );
+ g_data = 0;
+ }
+ return;
+ }
+ }
+
+}
+
+
+
+static BOOL ConnectToUSB( void )
+{
+ if( !g_connected )
+ {
+ if( HIOEnumDevices( HostIOEnumCallback ) ) {
+ if( g_usb_channel >= 0 ) {
+ if( HIOInit( g_usb_channel, HostIOCallback ) ) {
+ u32 temp = 0;
+ HIOReadMailbox( &temp );
+ g_connected = TRUE;
+ OSReport( "SCREENSHOT: USB connected\n" );
+ }
+ }
+ }
+ }
+
+ return( g_connected );
+}
+
+
+
+void SCREENSHOTService( void* bufferXFB,
+ SCREENSHOTAllocator allocator,
+ SCREENSHOTDeallocator deallocator )
+{
+ if( !g_connected ) {
+ ConnectToUSB();
+ }
+ else if( g_mail_waiting ) {
+ CheckMail( bufferXFB, allocator, deallocator );
+ }
+
+}
diff --git a/game/code/main/gamecube_extras/screenshot.h b/game/code/main/gamecube_extras/screenshot.h
new file mode 100644
index 0000000..45d86e0
--- /dev/null
+++ b/game/code/main/gamecube_extras/screenshot.h
@@ -0,0 +1,43 @@
+/*---------------------------------------------------------------------------*
+ Project: onetri
+ File: screenshot.h
+
+ Copyright 1998, 1999, 2000 Nintendo. All rights reserved.
+
+ These coded instructions, statements, and computer programs contain
+ proprietary information of Nintendo of America Inc. and/or Nintendo
+ Company Ltd., and are protected by Federal copyright law. They may
+ not be disclosed to third parties or copied or duplicated in any form,
+ in whole or in part, without the prior written consent of Nintendo.
+
+ Created 05-25-01 by Steve Rabin
+
+ *---------------------------------------------------------------------------*/
+
+
+#ifndef __SCREENSHOT_H__
+#define __SCREENSHOT_H__
+
+#include "dolphin.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// access to the memory allocator of choice
+typedef void*(*SCREENSHOTAllocator) ( u32 size );
+typedef void (*SCREENSHOTDeallocator) ( void* block );
+
+
+void SCREENSHOTService( void* bufferXFB,
+ SCREENSHOTAllocator allocator,
+ SCREENSHOTDeallocator deallocator );
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif // __SCREENSHOT_H__ \ No newline at end of file
diff --git a/game/code/main/gcmain.cpp b/game/code/main/gcmain.cpp
new file mode 100644
index 0000000..d3ff15a
--- /dev/null
+++ b/game/code/main/gcmain.cpp
@@ -0,0 +1,221 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: GCmain.cpp
+//
+// Description: This file contains the main enrty point to the game.
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <string.h>
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radobject.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <main/game.h>
+#include <main/gcplatform.h>
+#include <main/singletons.h>
+#include <main/commandlineoptions.h>
+#include <memory/srrmemory.h>
+
+//========================================
+// Forward Declarations
+//========================================
+static void ProcessCommandLineArguments( int argc, char* argv[] );
+
+static void ProcessCommandLineArgumentsFromFile( );
+
+//=============================================================================
+// Function: main
+//=============================================================================
+//
+// Description: Main entry point.
+//
+// Parameters: GC argc, number of command line argurments
+// argv, array of pointes to these tokens.
+//
+// Returns: 0 success, non zero error.
+//
+//=============================================================================
+int main( int argc, char* argv[] )
+{
+ //
+ // Pick out and store command line settings.
+ //
+ CommandLineOptions::InitDefaults();
+ ProcessCommandLineArguments( argc, argv );
+
+ //
+ // Have to get FTech setup first so that we can use all the memory services.
+ //
+ GCPlatform::InitializeFoundation();
+
+
+ srand (Game::GetRandomSeed ());
+
+
+ // Now disable the default heap
+ //
+#ifndef RAD_RELEASE
+ tName::SetAllocator (GMA_DEBUG);
+
+ //g_HeapActivityTracker.EnableHeapAllocs (GMA_DEFAULT, false);
+ //g_HeapActivityTracker.EnableHeapFrees (GMA_DEFAULT, false);
+#endif
+
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ //Process any commandline options from the command.txt file
+ ProcessCommandLineArgumentsFromFile();
+
+
+ //
+ // Instantiate all the singletons before doing anything else.
+ //
+ CreateSingletons();
+
+ //
+ // Construct the platform object.
+ //
+ GCPlatform* pPlatform = GCPlatform::CreateInstance();
+ rAssert( pPlatform != NULL );
+
+ //
+ // Create the game object.
+ //
+ Game* pGame = Game::CreateInstance( pPlatform );
+ rAssert( pGame != NULL );
+
+
+ //
+ // Initialize the game.
+ //
+ pGame->Initialize();
+
+ HeapMgr()->PopHeap (GMA_PERSISTENT);
+
+ //
+ // Run it! Control will not return from here until the game is stopped.
+ //
+ pGame->Run();
+
+ //
+ // Terminate the game (this frees all resources allocated by the game).
+ //
+ pGame->Terminate();
+
+ //
+ // Destroy the game object.
+ //
+ Game::DestroyInstance();
+
+ //
+ // Destroy the game and platform (do it in this order in case the game's
+ // destructor references the platform.
+ //
+ GCPlatform::DestroyInstance();
+
+ //
+ // Show some debug output
+ //
+#ifdef RAD_DEBUG
+ radObject::DumpObjects();
+#endif;
+
+ //
+ // Dump all the singletons.
+ //
+ DestroySingletons();
+
+ //
+ // Pass any error codes back to the operating system.
+ //
+ return( 0 );
+}
+
+
+//=============================================================================
+// Function: ProcessCommandLineArguments
+//=============================================================================
+//
+// Description: Pick out the command line options and stuff them in
+// the game database.
+//
+// Parameters: GC argc, number of command line argurments
+// argv, array of pointes to these tokens.
+//
+// Returns: None.
+//
+//=============================================================================
+void ProcessCommandLineArguments( int argc, char* argv[] )
+{
+#ifndef FINAL
+
+ rDebugPrintf( "*************************************************************\n" );
+ rDebugPrintf( "Command Line Args:\n" );
+
+ //
+ // Pick out all the command line options and store them in GameDB.
+ // Also dump them to the output for handy dandy viewing.
+ //
+ int i;
+ for( i = 0; i < argc; ++i )
+ {
+ char* argument = argv[i];
+
+ rDebugPrintf( "arg%d: %s\n", i, argument );
+
+ CommandLineOptions::HandleOption( argument );
+ }
+ if( !CommandLineOptions::Get( CLO_ART_STATS ) )
+ {
+ CommandLineOptions::HandleOption( "noheaps" );
+ }
+
+
+ rDebugPrintf( "*************************************************************\n" );
+
+#endif // FINAL
+}
+
+
+
+void ProcessCommandLineArgumentsFromFile()
+{
+#ifndef FINAL
+
+ //Chuck: looking for additional command line args being passed in from a file
+ //its for QA testing etc.
+
+ IRadFile* pfile =NULL;
+
+ ::radFileOpenSync(&pfile,"command.txt");
+
+ if (pfile != NULL)
+ {
+
+ char commandlinestring [256];
+ commandlinestring[ 0 ] = '\0';
+
+ pfile->ReadSync(commandlinestring,255);
+
+ //QA created the command line file and wants to pass additional arguements
+
+ char* argument = strtok(commandlinestring," ");
+ while (argument != NULL)
+ {
+ CommandLineOptions::HandleOption(argument);
+ argument=strtok(NULL," ");
+ }
+ pfile->Release();
+ }
+#endif //FINAL
+} //end of Function
diff --git a/game/code/main/gcplatform.cpp b/game/code/main/gcplatform.cpp
new file mode 100644
index 0000000..4c30055
--- /dev/null
+++ b/game/code/main/gcplatform.cpp
@@ -0,0 +1,1758 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: GCPlatform
+//
+// Description: Abstracts the differences for setting up and shutting down
+// the different platforms.
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//===========================================================================
+
+//========================================
+// System Includes
+//========================================
+
+// Standard Lib
+#include <stdlib.h>
+#include <string.h>
+#include <dolphin/vi.h>
+#include <dolphin/gx.h>
+#include <dolphin/os.h>
+#include <dolphin/lg.h>
+#include <dolphin/dvd.h>
+#include <dolphin.h>
+// Pure 3D
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/anim/expression.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/anim/polyskin.hpp>
+#include <p3d/anim/sequencer.hpp>
+#include <p3d/anim/skeleton.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/gameattr.hpp>
+#include <p3d/image.hpp>
+#include <p3d/imagefont.hpp>
+#include <p3d/light.hpp>
+#include <p3d/locator.hpp>
+#include <p3d/platform.hpp>
+#include <p3d/scenegraph/scenegraph.hpp>
+#include <p3d/sprite.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/texture.hpp>
+#include <p3d/file.hpp>
+#include <p3d/shader.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/memory.hpp>
+#include <p3d/bmp.hpp>
+#include <p3d/png.hpp>
+#include <p3d/targa.hpp>
+#include <p3d/font.hpp>
+#include <p3d/texturefont.hpp>
+#include <p3d/unicode.hpp>
+
+// Pure 3D: Loader-specific
+#include <render/Loaders/GeometryWrappedLoader.h>
+#include <render/Loaders/StaticEntityLoader.h>
+#include <render/Loaders/StaticPhysLoader.h>
+#include <render/Loaders/TreeDSGLoader.h>
+#include <render/Loaders/FenceLoader.h>
+#include <render/Loaders/IntersectLoader.h>
+#include <render/Loaders/AnimCollLoader.h>
+#include <render/Loaders/AnimDSGLoader.h>
+#include <render/Loaders/DynaPhysLoader.h>
+#include <render/Loaders/InstStatPhysLoader.h>
+#include <render/Loaders/InstStatEntityLoader.h>
+#include <render/Loaders/WorldSphereLoader.h>
+#include <render/Loaders/BillboardWrappedLoader.h>
+#include <render/Loaders/instparticlesystemloader.h>
+#include <render/Loaders/breakableobjectloader.h>
+#include <render/Loaders/lensflareloader.h>
+#include <render/Loaders/AnimDynaPhysLoader.h>
+#include <p3d/shadow.hpp>
+#include <p3d/anim/vertexanimkey.hpp>
+#include <p3d/anim/animatedobject.hpp>
+#include <p3d/effects/particleloader.hpp>
+#include <p3d/effects/opticloader.hpp>
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugcommunication.hpp>
+#include <raddebugwatch.hpp>
+#include <radfile.hpp>
+#include <radmemorymonitor.hpp>
+#include <radplatform.hpp>
+#include <radthread.hpp>
+#include <radtime.hpp>
+#include <radmovie2.hpp>
+
+//This is so we can get the name of the file that's failing.
+#include <../src/radfile/common/requests.hpp>
+
+// sim - for InstallSimLoaders
+#include <simcommon/simutility.hpp>
+
+// Stateprops
+#include <stateprop/statepropdata.hpp>
+
+// To turn off movies during an error.
+#include <presentation/fmvplayer/fmvplayer.h>
+#include <presentation/presentation.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/inputmanager.h>
+#include <main/gcplatform.h>
+#include <main/game.h>
+#include <main/commandlineoptions.h>
+#include <memory/srrmemory.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/Loaders/AllWrappers.h>
+
+#include <loading/locatorloader.h>
+#include <loading/cameradataloader.h>
+#include <loading/roadloader.h>
+#include <loading/pathloader.h>
+#include <loading/intersectionloader.h>
+#include <loading/roaddatasegmentloader.h>
+#include <atc/atcloader.h>
+
+#include <main/gamecube_extras/gcmanager.h>
+#include <debug/debuginfo.h>
+
+#include <radload/radload.hpp>
+
+#include <main/errorsgc.h>
+#include <presentation/gui/guitextbible.h>
+
+#define GC_SECTION "GC_SECTION"
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+GCPlatform* GCPlatform::spInstance = NULL;
+
+//Add baked in loadable headers here.
+static unsigned char gLicenseP3D[] =
+#include <main/gamecube_extras/license.h>
+
+//The Adlib font. <sigh>
+unsigned char gFont[] =
+#include <font/defaultfont.h>
+
+//GX Warning Levels.
+//GX_WARN_NONE
+//GX_WARN_SEVERE
+//GX_WARN_MEDIUM
+//GX_WARN_ALL
+
+const GXWarningLevel gxWarningLevel = GX_WARN_NONE;
+
+void LoadMemP3DFile( unsigned char* buffer, unsigned int size, tEntityStore* store )
+{
+ tFileMem* file = new tFileMem(buffer,size);
+ file->AddRef();
+ file->SetFilename("memfile.p3d");
+ p3d::loadManager->GetP3DHandler()->Load( file, p3d::inventory );
+ file->Release();
+}
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// GCPlatform::CreateInstance
+//==============================================================================
+//
+// Description: Creates the GCPlatform.
+//
+// Parameters: None.
+//
+// Return: Pointer to the GCPlatform.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+GCPlatform* GCPlatform::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "GCPlatform" );
+ rAssert( spInstance == NULL );
+
+ spInstance = new(GMA_PERSISTENT) GCPlatform;
+ rAssert( spInstance );
+MEMTRACK_POP_GROUP( "GCPlatform" );
+
+ return spInstance;
+}
+
+//==============================================================================
+// GCPlatform::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the GCPlatform singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the GCPlatform.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+GCPlatform* GCPlatform::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// GCPlatform::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the GCPlatform.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void GCPlatform::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+}
+
+
+
+//==============================================================================
+// GCPlatform::InitializeFoundation
+//==============================================================================
+// Description: Get FTech ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: The FTech systems must be initialized in a particular order.
+// Consult their documentation before changing. Also, we need
+// to only init the quick things to allow time for drawing the
+// first license screens.
+//
+//==============================================================================
+void GCPlatform::InitializeFoundation()
+{
+ //
+ // Initialize the memory heaps
+ //
+ GCPlatform::InitializeMemory();
+
+#ifndef FINAL
+ //
+ // Register an out-of-memory display handler in case something goes bad
+ // while allocating the heaps
+ //
+ ::radMemorySetOutOfMemoryCallback( PrintOutOfMemoryMessage, NULL );
+#endif
+
+#ifndef RAD_RELEASE
+ //
+ // Initialize memory monitor by JamesCo. TM.
+ //
+ if( CommandLineOptions::Get( CLO_MEMORY_MONITOR ) )
+ {
+ const int KB = 1024;
+ ::radMemoryMonitorInitialize( 2048 * KB, GMA_DEBUG );
+ }
+#endif
+
+ // Setup the memory heaps
+ //
+ HeapMgr()->PrepareHeapsStartup ();
+
+ // Seed the heap stack
+ //
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ //
+ // Initilalize the platform system
+ //
+ ::radPlatformInitialize();
+
+ //
+ // Initialize the timer system
+ //
+ ::radTimeInitialize();
+
+#ifndef RAD_RELEASE
+ //
+ // Initialize the debug communication system.
+ //
+ ::radDbgComTargetInitialize( HostIO,
+ radDbgComDefaultPort, // Default
+ NULL, // Default
+ GMA_DEBUG );
+
+ //
+ // Initialize the Watcher.
+ //
+ ::radDbgWatchInitialize( "SRR2",
+ 16384 * 16, // Default
+ GMA_DEBUG );
+#endif
+
+ //
+ // Initialize the file system.
+ //
+ ::radFileInitialize( 50, // Default
+ 32, // Default
+ GMA_PERSISTENT );
+
+ ::radLoadInitialize();
+
+ ::radDriveMount(0, GMA_PERSISTENT);
+
+ //
+ // Initialize the new movie player
+ //
+ ::radMovieInitialize2( GMA_PERSISTENT );
+
+}
+
+
+//==============================================================================
+// GCPlatform::InitializeMemory
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void GCPlatform::InitializeMemory()
+{
+ //
+ // Only do this once!
+ //
+ if( gMemorySystemInitialized == true )
+ {
+ return;
+ }
+
+ gMemorySystemInitialized = true;
+
+ //
+ // Initialize thread system.
+ //
+ ::radThreadInitialize();
+
+ //
+ // Initialize memory system.
+ //
+ ::radMemoryInitialize( VMM_MAIN_RAM, VMM_ARAM );
+}
+
+//==============================================================================
+// GCPlatform::InitializePlatform
+//==============================================================================
+// Description: Get the GC ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//=============================================================================
+void GCPlatform::InitializePlatform()
+{
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ //
+ // Get some rendering action setup.
+ //
+ InitializePure3D();
+
+ //
+ // Add anything here that needs to be before the drive is opened.
+ //
+
+ //This is slow.
+ InitializeFoundationDrive();
+
+ //
+ // Initialize the controller.
+ //
+ GetInputManager()->Init();
+
+#ifndef PAL
+ // no progressive scan on PAL builds, only on NTSC
+ //
+ GCManager::GetInstance()->DoProgressiveScanTest();
+#endif // !PAL
+
+ //TRC: The license screen needs to be
+ DisplaySplashScreen( License, NULL );
+
+
+ //Set the serial cable debug verbosity level
+ GXSetVerifyLevel( gxWarningLevel );
+
+ HeapMgr()->PopHeap (GMA_PERSISTENT);
+}
+
+void GCPlatform::OnControllerError(const char *msg)
+{
+ DisplaySplashScreen( Error, msg, 0.7f, 0.0f, 0.0f, tColour(255, 255, 255), 0 );
+ mErrorState = CTL_ERROR;
+ mPauseForError = true;
+
+}
+
+
+//==============================================================================
+// GCPlatform::ShutdownPlatform
+//==============================================================================
+// Description: Shut down the GC.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void GCPlatform::ShutdownPlatform()
+{
+ ShutdownPure3D();
+ ShutdownFoundation();
+
+ //Shutdown the steering wheel system.
+ ::radControllerTerminate();
+}
+
+//=============================================================================
+// GCPlatform::ResetMachine
+//=============================================================================
+// Description: resets the machine
+//
+// Parameters: none
+//
+// Return: void
+//
+//=============================================================================
+void GCPlatform::ResetMachine()
+{
+ LaunchDashboard();
+}
+
+//=============================================================================
+// GCPlatform::LaunchDashboard
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GCPlatform::LaunchDashboard()
+{
+ GCManager::GetInstance()->Reset();
+ GCManager::GetInstance()->PerformReset( false );
+}
+
+
+//=============================================================================
+// GCPlatform::DisplaySplashScreen
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( SplashScreen screenID,
+// const char* overlayText = NULL,
+// float fontScale = 1.0f,
+// float textPosX = 0.0f,
+// float textPosY = 0.0f,
+// tColour textColour,
+// int fadeFrames = 3 )
+//
+// Return: void
+//
+//=============================================================================
+void GCPlatform::DisplaySplashScreen( SplashScreen screenID,
+ const char* overlayText,
+ float fontScale,
+ float textPosX,
+ float textPosY,
+ tColour textColour,
+ int fadeFrames )
+{
+ //Just in case this was called during a draw!
+ GXWaitDrawDone();
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+
+ p3d::inventory->PushSection();
+ p3d::inventory->AddSection( GC_SECTION );
+ p3d::inventory->SelectSection( GC_SECTION );
+
+ unsigned char* screen = NULL;
+ const char* texname = NULL;
+ int size = 0;
+ tTexture* texture = NULL;
+
+ P3D_UNICODE unicodeText[256];
+
+
+ //Create shader
+ tShader* splashShader = new tShader("simple");
+ splashShader->SetInt(PDDI_SP_FILTER, PDDI_FILTER_NONE);
+ splashShader->AddRef();
+
+ if ( screenID == FadeToBlack )
+ {
+ splashShader->SetInt(PDDI_SP_BLENDMODE,PDDI_BLEND_ALPHA);
+ splashShader->SetInt(PDDI_SP_FILTER,PDDI_FILTER_BILINEAR);
+ }
+ else if ( screenID == Error )
+ {
+ splashShader->SetColour(PDDI_SP_DIFFUSE, tColour(0, 0, 0));
+ splashShader->SetColour(PDDI_SP_AMBIENT, tColour(0, 0, 0));
+ splashShader->SetColour(PDDI_SP_SPECULAR, tColour(0, 0, 0));
+ splashShader->SetColour(PDDI_SP_EMISSIVE, tColour(0, 0, 0));
+ }
+ else
+ {
+ //This is so we can handle more baked in screens if we want
+ switch (screenID)
+ {
+ case License:
+ {
+ screen = gLicenseP3D;
+ texname = LICENSE_NAME;
+ size = LICENSE_SIZE;
+ }
+ break;
+ default:
+ rAssert( false);
+ }
+
+ // Load the in screen texture file from memory
+ p3d::load(screen, size, HeapMgr()->GetCurrentHeap());
+
+ // Get the texture to draw
+ texture = p3d::find<tTexture>(texname);
+ rAssert( texture );
+
+ // Put it in our shader
+ splashShader->SetTexture(PDDI_SP_BASETEX, texture);
+ }
+
+ // Save the current Projection mode so I can restore it later
+ pddiProjectionMode pm = p3d::pddi->GetProjectionMode();
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_DEVICE);
+
+ pddiCullMode cm = p3d::pddi->GetCullMode();
+ p3d::pddi->SetCullMode(PDDI_CULL_NONE);
+
+ //CREATE THE FONT
+ tTextureFont* thisFont = NULL;
+
+ if ( overlayText != NULL )
+ {
+ // Convert memory buffer into a texturefont.
+ //
+ //p3d::load(gFont, DEFAULTFONT_SIZE, HeapMgr()->GetCurrentHeap() );
+ LoadMemP3DFile( gFont, DEFAULTFONT_SIZE, p3d::inventory );
+
+ thisFont = p3d::find<tTextureFont>("adlibn_20");
+ rAssert( thisFont );
+
+ thisFont->AddRef();
+ tShader* fontShader = thisFont->GetShader();
+ fontShader->SetInt(PDDI_SP_BLENDMODE,PDDI_BLEND_ALPHA);
+ fontShader->SetInt(PDDI_SP_FILTER,PDDI_FILTER_BILINEAR);
+
+ p3d::AsciiToUnicode( overlayText, unicodeText, 256 );
+
+ // Make the missing letter into somthing I can see
+ //
+ thisFont->SetMissingLetter(p3d::ConvertCharToUnicode('j'));
+ }
+
+ // Fade up the screen
+ int a = 0;
+ do
+ {
+ if ( screenID != FadeToBlack )
+ {
+ p3d::pddi->SetColourWrite(true, true, true, true);
+ p3d::pddi->SetClearColour( pddiColour(0,0,0) );
+ }
+
+ p3d::pddi->BeginFrame();
+
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_DEVICE);
+
+ GXSetScissorBoxOffset(0, 0);
+#ifdef PAL
+ GXSetScissor(0, 0, 640, 528);
+#else
+ GXSetScissor(0, 0, 640, 480);
+#endif
+
+ if ( screenID != FadeToBlack )
+ {
+ p3d::pddi->Clear(PDDI_BUFFER_ALL);
+ }
+
+ int bright = 255;
+ if (a < fadeFrames) bright = (a * 255) / fadeFrames / 10;
+ if ( bright > 255 ) bright = 255;
+
+ tColour c;
+ if ( screenID == FadeToBlack)
+ {
+ c.Set(0, 0, 0, bright);
+ }
+ else
+ {
+ c.Set(bright, bright, bright, 255);
+ }
+
+ if ( screenID != Error )
+ {
+ p3d::pddi->PushMatrix(PDDI_MATRIX_MODELVIEW);
+ p3d::pddi->IdentityMatrix(PDDI_MATRIX_MODELVIEW);
+
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+
+ pddiPrimStream* stream;
+
+ if ( screenID == FadeToBlack )
+ {
+ stream = p3d::pddi->BeginPrims(splashShader->GetShader(), PDDI_PRIM_TRISTRIP, PDDI_V_C, 4);
+ }
+ else
+ {
+ stream = p3d::pddi->BeginPrims(splashShader->GetShader(), PDDI_PRIM_TRISTRIP, PDDI_V_CT, 4);
+ }
+
+#ifdef PAL
+ stream->Colour(c); stream->UV(0.0F, 0.0F); stream->Coord( 0.0F, 528.0F, 2.01F);
+ stream->Colour(c); stream->UV(1.0F, 0.0F); stream->Coord(640.0F, 528.0F, 2.01F);
+#else
+ stream->Colour(c); stream->UV(0.0F, 0.0F); stream->Coord( 0.0F, 480.0F, 2.01F);
+ stream->Colour(c); stream->UV(1.0F, 0.0F); stream->Coord(640.0F, 480.0F, 2.01F);
+#endif
+ stream->Colour(c); stream->UV(0.0F, 1.0F); stream->Coord( 0.0F, 0.0F, 2.01F);
+ stream->Colour(c); stream->UV(1.0F, 1.0F); stream->Coord(640.0F, 0.0F, 2.01F);
+
+ p3d::pddi->EndPrims(stream);
+
+ p3d::stack->Pop();
+
+ p3d::pddi->PopMatrix(PDDI_MATRIX_MODELVIEW);
+ }
+
+ if (overlayText != NULL)
+ {
+ tColour colour = textColour;
+ colour.SetAlpha( bright );
+
+ thisFont->SetColour( colour );
+
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_ORTHOGRAPHIC);
+
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+
+ p3d::stack->Translate( textPosX, textPosY, 1.5f);
+ float scaleSize = 1.0f / 480.0f; //This is likely good for 528 also.
+ p3d::stack->Scale(scaleSize * fontScale, scaleSize* fontScale , 1.0f);
+
+ if ( textPosX != 0.0f || textPosY != 0.0f )
+ {
+ thisFont->DisplayText( unicodeText );
+ }
+ else
+ {
+ thisFont->DisplayText( unicodeText, 3 );
+ }
+
+ p3d::stack->Pop();
+ }
+
+ p3d::pddi->EndFrame();
+ p3d::context->SwapBuffers();
+
+ ++a;
+
+ if (screenID == FadeToBlack && bright >= 255)
+ {
+ //This is a sucky hack...
+ break;
+ }
+
+ } while (a <= fadeFrames);
+
+ // [ps]: flush out this screen now.
+ if ( screenID == FadeToBlack )
+ {
+ GXSetScissorBoxOffset(0, 0);
+#ifdef PAL
+ GXSetScissor(0, 0, 640, 528);
+#else
+ GXSetScissor(0, 0, 640, 480);
+#endif
+
+ p3d::pddi->Clear(PDDI_BUFFER_ALL);
+ }
+
+ // Remove the texture from the shader
+ splashShader->SetTexture(PDDI_SP_BASETEX, NULL);
+ splashShader->Release();
+
+ // remove the texture from the inventory
+ if ( texture )
+ {
+ p3d::inventory->Remove(texture);
+ }
+
+ p3d::pddi->SetCullMode(cm);
+ p3d::pddi->SetProjectionMode(pm);
+
+ if ( thisFont )
+ {
+ thisFont->Release();
+ p3d::inventory->Remove(thisFont);
+ }
+
+ p3d::inventory->RemoveSectionElements(GC_SECTION);
+ p3d::inventory->DeleteSection(GC_SECTION);
+ p3d::inventory->PopSection();
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+}
+
+//=============================================================================
+// GCPlatform::DisplaySplashScreen
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* overlayText = NULL,
+// float fontScale = 1.0f,
+// float textPosX = 0.0f,
+// float textPosY = 0.0f,
+// tColour textColour,
+// int fadeFrames = 3 )
+//
+// Return: void
+//
+//=============================================================================
+void GCPlatform::DisplaySplashScreen( const char* textureName,
+ const char* overlayText,
+ float fontScale,
+ float textPosX,
+ float textPosY,
+ tColour textColour,
+ int fadeFrames )
+{
+ //Just in case this was called during a draw!
+ GXWaitDrawDone();
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+
+ p3d::inventory->PushSection();
+ p3d::inventory->AddSection( GC_SECTION );
+ p3d::inventory->SelectSection( GC_SECTION );
+
+ tTexture* texture = NULL;
+
+ P3D_UNICODE unicodeText[256];
+
+ rAssertMsg( textureName, "There must be a texture, use the other one with Error otherwise." );
+
+ //Create shader
+ tShader* splashShader = new tShader("simple");
+ splashShader->SetInt(PDDI_SP_FILTER, PDDI_FILTER_NONE);
+ splashShader->AddRef();
+
+
+ // Get the texture to draw
+ texture = p3d::find<tTexture>(textureName);
+ rAssert( texture );
+
+ // Put it in our shader
+ splashShader->SetTexture(PDDI_SP_BASETEX, texture);
+
+
+ // Save the current Projection mode so I can restore it later
+ pddiProjectionMode pm = p3d::pddi->GetProjectionMode();
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_DEVICE);
+
+ pddiCullMode cm = p3d::pddi->GetCullMode();
+ p3d::pddi->SetCullMode(PDDI_CULL_NONE);
+
+ //CREATE THE FONT
+ tTextureFont* thisFont = NULL;
+
+ if ( overlayText != NULL )
+ {
+ // Convert memory buffer into a texturefont.
+ //
+ //p3d::load(gFont, DEFAULTFONT_SIZE, HeapMgr()->GetCurrentHeap());
+ LoadMemP3DFile( gFont, DEFAULTFONT_SIZE, p3d::inventory );
+
+ thisFont = p3d::find<tTextureFont>("adlibn_20");
+ rAssert( thisFont );
+
+ thisFont->AddRef();
+ tShader* fontShader = thisFont->GetShader();
+ fontShader->SetInt(PDDI_SP_BLENDMODE,PDDI_BLEND_ALPHA);
+ fontShader->SetInt(PDDI_SP_FILTER,PDDI_FILTER_BILINEAR);
+
+
+ p3d::AsciiToUnicode( overlayText, unicodeText, 256 );
+
+ // Make the missing letter into somthing I can see
+ //
+ thisFont->SetMissingLetter(p3d::ConvertCharToUnicode('j'));
+ }
+
+ // Fade up the screen
+ int a = 0;
+
+ do
+ {
+ p3d::pddi->SetColourWrite(true, true, true, true);
+ p3d::pddi->SetClearColour( pddiColour(0,0,0) );
+ p3d::pddi->BeginFrame();
+ p3d::pddi->Clear(PDDI_BUFFER_ALL);
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_DEVICE);
+
+ GXSetScissorBoxOffset(0, 0);
+#ifdef PAL
+ GXSetScissor(0, 0, 640, 528);
+#else
+ GXSetScissor(0, 0, 640, 480);
+#endif
+
+ p3d::pddi->PushMatrix(PDDI_MATRIX_MODELVIEW);
+ p3d::pddi->IdentityMatrix(PDDI_MATRIX_MODELVIEW);
+
+ int bright = 255;
+ if (a < fadeFrames) bright = (a * 255) / fadeFrames;
+ if ( bright > 255 ) bright = 255;
+ tColour c(bright, bright, bright, 255);
+
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+
+ pddiPrimStream* stream = p3d::pddi->BeginPrims(splashShader->GetShader(), PDDI_PRIM_TRISTRIP, PDDI_V_CT, 4);
+
+#ifdef PAL
+ stream->Colour(c); stream->UV(0.0F, 0.0F); stream->Coord( 0.0F, 528.0F, 2.01F);
+ stream->Colour(c); stream->UV(1.0F, 0.0F); stream->Coord(640.0F, 528.0F, 2.01F);
+#else
+ stream->Colour(c); stream->UV(0.0F, 0.0F); stream->Coord( 0.0F, 480.0F, 2.01F);
+ stream->Colour(c); stream->UV(1.0F, 0.0F); stream->Coord(640.0F, 480.0F, 2.01F);
+#endif
+ stream->Colour(c); stream->UV(0.0F, 1.0F); stream->Coord( 0.0F, 0.0F, 2.01F);
+ stream->Colour(c); stream->UV(1.0F, 1.0F); stream->Coord(640.0F, 0.0F, 2.01F);
+
+ p3d::pddi->EndPrims(stream);
+
+ p3d::stack->Pop();
+
+ p3d::pddi->PopMatrix(PDDI_MATRIX_MODELVIEW);
+
+ if (overlayText != NULL)
+ {
+ tColour colour = textColour;
+ colour.SetAlpha( bright );
+
+ thisFont->SetColour( colour );
+
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_ORTHOGRAPHIC);
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+
+ p3d::stack->Translate( textPosX, textPosY, 1.5f);
+ float scaleSize = 1.0f / 480.0f; //This is likely good for 528 also.
+ p3d::stack->Scale(scaleSize * fontScale, scaleSize* fontScale , 1.0f);
+
+ thisFont->DisplayText( unicodeText );
+
+ p3d::stack->Pop();
+ }
+
+ p3d::pddi->EndFrame();
+ p3d::context->SwapBuffers();
+
+ ++a;
+
+ } while (a <= fadeFrames);
+
+ // Remove the texture from the shader
+ splashShader->SetTexture(PDDI_SP_BASETEX, NULL);
+ splashShader->Release();
+
+ p3d::pddi->SetCullMode(cm);
+ p3d::pddi->SetProjectionMode(pm);
+
+ if ( overlayText != NULL )
+ {
+ thisFont->Release();
+ p3d::inventory->Remove(thisFont);
+ }
+
+ p3d::inventory->RemoveSectionElements(GC_SECTION);
+ p3d::inventory->DeleteSection(GC_SECTION);
+ p3d::inventory->PopSection();
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+}
+
+//=============================================================================
+// GCPlatform::DisplayYesNo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float fontScale = 1.0f,
+// float yesPosX = 0.0f,
+// float yesPosY = 0.0f,
+// float noPosX = 0.0f,
+// float noPosY = 0.0f,
+// int fadeFrames = 3 )
+//
+// Return: bool
+//
+//=============================================================================
+bool GCPlatform::DisplayYesNo( float fontScale,
+ float yesPosX,
+ float yesPosY,
+ float noPosX,
+ float noPosY,
+ int fadeFrames )
+{
+ //Just in case this was called during a draw!
+ GXWaitDrawDone();
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+
+ p3d::inventory->PushSection();
+ p3d::inventory->AddSection( GC_SECTION );
+ p3d::inventory->SelectSection( GC_SECTION );
+
+ // Save the current Projection mode so I can restore it later
+ pddiProjectionMode pm = p3d::pddi->GetProjectionMode();
+
+ pddiCullMode cm = p3d::pddi->GetCullMode();
+ p3d::pddi->SetCullMode(PDDI_CULL_NONE);
+
+ //CREATE THE FONT
+ tTextureFont* thisFont = NULL;
+
+ // Convert memory buffer into a texturefont.
+ //
+ //p3d::load(gFont, DEFAULTFONT_SIZE, HeapMgr()->GetCurrentHeap());
+ LoadMemP3DFile( gFont, DEFAULTFONT_SIZE, p3d::inventory );
+
+ thisFont = p3d::find<tTextureFont>("adlibn_20");
+ rAssert( thisFont );
+
+ thisFont->AddRef();
+ tShader* fontShader = thisFont->GetShader();
+ fontShader->SetInt(PDDI_SP_BLENDMODE,PDDI_BLEND_ALPHA);
+ fontShader->SetInt(PDDI_SP_FILTER,PDDI_FILTER_BILINEAR);
+
+ //This is the text!
+ const char* yes = "Yes";
+ const char* no = "No";
+
+ P3D_UNICODE unicodeYes[256];
+ P3D_UNICODE unicodeNo[256];
+
+ p3d::AsciiToUnicode( yes, unicodeYes, 256 );
+ p3d::AsciiToUnicode( no, unicodeNo, 256 );
+
+ // Make the missing letter into somthing I can see
+ //
+ thisFont->SetMissingLetter(p3d::ConvertCharToUnicode('j'));
+
+ unsigned int inputCount = 0;
+ PADStatus controllers[PAD_MAX_CONTROLLERS];
+
+ for ( inputCount = 0; inputCount < PAD_MAX_CONTROLLERS; ++inputCount )
+ {
+ controllers[ inputCount ].button = 0;
+ }
+
+ LGPosition lgStatus[ SI_MAX_CHAN ];
+ memset( lgStatus, 0, sizeof( LGPosition )*SI_MAX_CHAN );
+
+ bool yesSelected = true;
+ bool buttonAPressed[ PAD_MAX_CONTROLLERS ];
+ bool buttonAReleased = false;
+
+ // Fade up the screen
+ int a = 0;
+ tColour colour;
+
+ unsigned int start = OSTicksToMilliseconds( OSGetTime() );
+
+ bool buttonDown[ PAD_MAX_CONTROLLERS ];
+
+ unsigned int i;
+
+ for ( i = 0; i < PAD_MAX_CONTROLLERS; ++i )
+ {
+ buttonDown[ i ] = false;
+ buttonAPressed[ i ] = false;
+ }
+
+ do
+ {
+ unsigned int end = OSTicksToMilliseconds( OSGetTime() );
+ unsigned int milliseconds = end - start;
+ start = end;
+
+ p3d::pddi->BeginFrame();
+
+ GXSetScissorBoxOffset(0, 0);
+#ifdef PAL
+ GXSetScissor(0, 0, 640, 528);
+#else
+ GXSetScissor(0, 0, 640, 480);
+#endif
+ int bright = 255;
+ if (a < fadeFrames) bright = (a * 255) / fadeFrames;
+ if ( bright > 255 ) bright = 255;
+ tColour c(bright, bright, bright, 255);
+
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_ORTHOGRAPHIC);
+
+ float scaleSize = 1.0f / 480.0f; //This is likely good for 528 also.
+
+ //Draw Yes
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+
+ p3d::stack->Translate( yesPosX, yesPosY, 1.5f);
+ p3d::stack->Scale(scaleSize * fontScale, scaleSize* fontScale , 1.0f);
+
+ if ( yesSelected )
+ {
+ colour = tColour(255, 255, 255, bright);
+ }
+ else
+ {
+ colour = tColour(100, 100, 100, bright);
+ }
+
+ thisFont->SetColour( colour );
+ thisFont->DisplayText( unicodeYes );
+
+ p3d::stack->Pop();
+
+ //Draw No
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+
+ p3d::stack->Translate( noPosX, noPosY, 1.5f);
+ p3d::stack->Scale(scaleSize * fontScale, scaleSize* fontScale , 1.0f);
+
+ if ( !yesSelected )
+ {
+ colour = tColour(255, 255, 255, bright);
+ }
+ else
+ {
+ colour = tColour(100, 100, 100, bright);
+ }
+
+ thisFont->SetColour( colour );
+ thisFont->DisplayText( unicodeNo );
+
+ p3d::stack->Pop();
+
+ p3d::pddi->EndFrame();
+ p3d::context->SwapBuffers();
+
+ if ( bright < 255 )
+ {
+ ++a;
+ }
+
+ ::radControllerSystemService();
+ ::radThreadSleep( 32 );
+
+ PADRead(controllers);
+ LGRead( lgStatus );
+
+ rAssert( PAD_MAX_CONTROLLERS == SI_MAX_CHAN );
+ for ( i = 0; i < PAD_MAX_CONTROLLERS; ++i )
+ {
+ bool start = controllers[ i ].err == PAD_ERR_NONE &&
+ ( controllers[ i ].button & PAD_BUTTON_MENU );
+
+ start = start ||
+ (lgStatus[ i ].err == LG_ERR_NONE && (lgStatus[ i ].button & LG_BUTTON_START ));
+
+ bool x = controllers[ i ].err == PAD_ERR_NONE &&
+ ( controllers[ i ].button & PAD_BUTTON_A );
+
+ x = x ||
+ (lgStatus[ i ].err == LG_ERR_NONE && (lgStatus[ i ].button & LG_BUTTON_A ));
+
+ if ( x || start )
+ {
+ buttonAPressed[ i ] = true;
+ }
+ else if ( buttonAPressed[ i ] )
+ {
+ buttonAReleased = true;
+ break;
+ }
+
+ bool dLeft = ( controllers[ i ].err == PAD_ERR_NONE &&
+ ( ( controllers[ i ].button & PAD_BUTTON_LEFT ) ||
+ ( controllers[ i ].stickX < -50 ) ) );
+ bool dRight = ( controllers[ i ].err == PAD_ERR_NONE &&
+ ( ( controllers[ i ].button & PAD_BUTTON_RIGHT ) ||
+ ( controllers[ i ].stickX > 50 ) ) );
+
+ dLeft = dLeft ||
+ (lgStatus[ i ].err == LG_ERR_NONE && ((lgStatus[ i ].button & LG_BUTTON_LEFT) || (lgStatus[ i ].wheel < -50 )));
+
+ dRight = dRight ||
+ (lgStatus[ i ].err == LG_ERR_NONE && ((lgStatus[ i ].button & LG_BUTTON_RIGHT) || (lgStatus[ i ].wheel > 50 )));
+
+ if ( ( dLeft || dRight ) && !buttonDown[ i ] )
+ {
+ yesSelected = !yesSelected;
+ buttonDown[ i ] = true;
+ }
+ else if ( !dLeft && !dRight )
+ {
+ buttonDown[ i ] = false;
+ }
+ }
+
+ GXWaitDrawDone();
+ } while ( !buttonAReleased );
+
+ p3d::pddi->SetCullMode(cm);
+ p3d::pddi->SetProjectionMode(pm);
+
+ thisFont->Release();
+
+ p3d::inventory->Remove(thisFont);
+
+ p3d::inventory->RemoveSectionElements(GC_SECTION);
+ p3d::inventory->DeleteSection(GC_SECTION);
+ p3d::inventory->PopSection();
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ return yesSelected;
+}
+
+//=============================================================================
+// GCPlatform::OnDriveError
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( radFileError error, const char* pDriveName, void* pUserData )
+//
+// Return: bool
+//
+//=============================================================================
+bool GCPlatform::OnDriveError( radFileError error, const char* pDriveName, void* pUserData )
+{
+ GCManager::GetInstance()->OnTimerDone( 16, NULL );
+
+ // Halt rumbling
+ GCManager::GetInstance()->StopRumble();
+
+ bool inFrame = p3d::context->InFrame();
+
+ const int NUM_RADFILE_ERRORS = 13;
+ unsigned int errorIndex = error;
+
+#ifdef PAL
+ switch( CGuiTextBible::GetCurrentLanguage() )
+ {
+ case Scrooby::XL_FRENCH:
+ {
+ errorIndex += 1 * NUM_RADFILE_ERRORS;
+
+ break;
+ }
+ case Scrooby::XL_GERMAN:
+ {
+ errorIndex += 2 * NUM_RADFILE_ERRORS;
+
+ break;
+ }
+ case Scrooby::XL_SPANISH:
+ {
+ errorIndex += 3 * NUM_RADFILE_ERRORS;
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+#endif // PAL
+
+ rAssert( errorIndex < sizeof( ERROR_STRINGS ) / sizeof( ERROR_STRINGS[ 0 ] ) );
+
+ switch ( error )
+ {
+ case Success:
+ {
+ if ( mErrorState != NONE )
+ {
+ if ( inFrame ) p3d::context->EndFrame( true );
+ DisplaySplashScreen( FadeToBlack );
+ if ( inFrame ) p3d::context->BeginFrame( );
+ mErrorState = NONE;
+ mPauseForError = false;
+ }
+
+ if ( GetPresentationManager()->GetFMVPlayer()->IsPlaying() )
+ {
+ GetPresentationManager()->GetFMVPlayer()->UnPause( );
+ }
+ else
+ {
+ GetSoundManager()->ResumeAfterMovie();
+ }
+ return true;
+ break;
+ }
+ case FileNotFound:
+ {
+ if ( CommandLineOptions::Get( CLO_FILE_NOT_FOUND ) )
+ {
+ rAssert( pUserData != NULL );
+
+ radFileRequest* request = static_cast<radFileRequest*>( pUserData );
+ const char* fileName = request->GetFilename();
+
+ //Get rid of the slashes.
+ unsigned int i;
+ unsigned int lastIndex = 0;
+ for ( i = 0; i < strlen( fileName ); ++i )
+ {
+ if ( fileName[ i ] == '\\' )
+ {
+ lastIndex = i;
+ }
+ }
+
+ unsigned int adjustedIndex = lastIndex == 0 ? lastIndex : lastIndex + 1;
+
+ char adjustedName[32];
+ strncpy( adjustedName, &fileName[adjustedIndex], ( strlen( fileName ) - lastIndex ) );
+ adjustedName[ strlen( fileName ) - lastIndex ] = '\0';
+
+ char errorString[256];
+ sprintf( errorString, "%s:\n%s", ERROR_STRINGS[errorIndex], adjustedName );
+ if ( inFrame ) p3d::context->EndFrame( true );
+ DisplaySplashScreen( Error, errorString, 1.0f, 0.0f, 0.0f, tColour(255, 255, 255), 0 );
+ if ( inFrame ) p3d::context->BeginFrame( );
+ mErrorState = P_ERROR;
+ mPauseForError = true;
+
+ if ( GetPresentationManager()->GetFMVPlayer()->IsPlaying() )
+ {
+ GetPresentationManager()->GetFMVPlayer()->Pause( );
+ }
+ else
+ {
+ GetSoundManager()->StopForMovie();
+ }
+ return true;
+ }
+ else
+ {
+ error = NoMedia;
+ //Fall through.
+ }
+ }
+ case ShellOpen:
+ case WrongMedia:
+ case NoMedia:
+ case MediaCorrupt:
+ case HardwareFailure:
+ {
+ //This could be the wrong disc.
+ if ( inFrame ) p3d::context->EndFrame( true );
+ DisplaySplashScreen( Error, ERROR_STRINGS[errorIndex], 1.0f, 0.0f, 0.0f, tColour(255, 255, 255), 0 );
+ if ( inFrame ) p3d::context->BeginFrame( );
+ mErrorState = P_ERROR;
+ mPauseForError = true;
+
+ if ( GetPresentationManager()->GetFMVPlayer()->IsPlaying() )
+ {
+ GetPresentationManager()->GetFMVPlayer()->Pause( );
+ }
+ else
+ {
+ GetSoundManager()->StopForMovie();
+ }
+ return true;
+ }
+ default:
+ {
+ //Others are not supported.
+ rAssert( false );
+ }
+ }
+
+ return false;
+}
+
+
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// GCPlatform::InitializeFoundationDrive
+//==============================================================================
+// Description: Get FTech ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: The FTech systems must be initialized in a particular order.
+// Consult their documentation before changing. This is all the
+// slower elements of FTech
+//
+//==============================================================================
+void GCPlatform::InitializeFoundationDrive()
+{
+ //
+ // Get the DVD drive and hold it open for the life of the game.
+ // This is a costly operation so we only want to do it once.
+ //
+
+ ::radDriveOpen( &mpIRadDrive,
+ "DVD:",
+ NormalPriority, // Default
+ GMA_PERSISTENT );
+
+ rAssert( mpIRadDrive != NULL );
+
+ mpIRadDrive->RegisterErrorHandler( this, NULL );
+
+ //
+ // Set the read-write granulatity to prevent operations from
+ // being partitioned.
+ //
+// const int GRANULARITY = 20 * 1024 * 1024;
+// mpIRadDrive->SetReadWriteGranularity( GRANULARITY );
+
+ DVDSetAutoFatalMessaging( true );
+}
+
+
+//==============================================================================
+// GCPlatform::ShutdownFoundation
+//==============================================================================
+// Description: Shut down Foundation Tech.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: The FTech systems must be terminated in the reverse order that
+// they were initialized in.
+//
+//==============================================================================
+void GCPlatform::ShutdownFoundation()
+{
+ //
+ // Release the drive we've held open since the begining.
+ //
+ mpIRadDrive->Release();
+ mpIRadDrive = NULL;
+
+ //
+ // Shutdown the systems in the reverse order.
+ //
+ ::radDriveUnmount();
+ ::radFileTerminate();
+
+#ifndef RAD_RELEASE
+ ::radDbgWatchTerminate();
+ if( CommandLineOptions::Get( CLO_MEMORY_MONITOR ) )
+ {
+ ::radMemoryMonitorTerminate();
+ }
+ ::radDbgComTargetTerminate();
+#endif
+
+ ::radTimeTerminate();
+ ::radPlatformTerminate();
+ ::radMemoryTerminate();
+ ::radThreadTerminate();
+}
+
+
+//==============================================================================
+// GCPlatform::InitializePure3D
+//==============================================================================
+// Description: Get Pure3D ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void GCPlatform::InitializePure3D()
+{
+MEMTRACK_PUSH_GROUP( "GCPlatform" );
+ //
+ // Initialise Pure3D platform object.
+ // This call differs between different platforms. The Win32 version,
+ // for example requires the application instance to be passed in.
+ //
+ mpPlatform = tPlatform::Create();
+ rAssert( mpPlatform != NULL );
+
+ //
+ // Initialiase the Pure3D context object.
+ // We have to create on of these for every window, and for every PDDI
+ // instance we use for rendering. Since almost every application only
+ // uses one window and PDDI library at a time, we one need to create one
+ // context.
+ //
+
+ //tContextInitData init; We're storing this as a member var called mInitData.
+
+ //
+ // These values only take effect in fullscreen mode. In windowed mode, the
+ // dimensions of the window define the rendering area. We'll define them
+ // anyway for completeness sake.
+ //
+ mInitData.xsize = WindowSizeX;
+ mInitData.ysize = WindowSizeY;
+
+ //
+ // Depth of the rendering buffer. Again, this value only works in
+ // fullscreen mode. In window mode, the depth of the desktop is used.
+ // This value should be either 16 or 32. Depths of 4, 8, and 24 are not
+ // supported by most 3D hardware, and not by Pure3D.
+ //
+// mInitData.bpp = WindowBPP;
+ mInitData.bufferMask = PDDI_BUFFER_COLOUR | PDDI_BUFFER_DEPTH;
+ //
+ // Rendering to NTSC or PAL.
+ //
+#ifdef PAL
+ mInitData.pal = true;
+#else
+ mInitData.pal = false;
+
+ //Progressive only available in NTSC
+// mInitData.progressive = OSGetProgressiveMode() && VIGetDTVStatus();
+ mInitData.progressive = false;
+#endif
+
+ //TODO: Investigate VSync
+ mInitData.lockToVsync = false;
+ //
+ // Create the context.
+ //
+ mpContext = mpPlatform->CreateContext( &mInitData );
+ rAssert( mpContext != NULL );
+
+ VISetBlack(FALSE);
+ VIFlush();
+ VIWaitForRetrace();
+
+ //
+ // Assign this context to the platform.
+ //
+ mpPlatform->SetActiveContext( mpContext );
+ p3d::pddi->EnableZBuffer( true );
+
+ //
+ // This call installs chunk handlers for all the primary chunk types that
+ // Pure3D supports. This includes textures, materials, geometries, and the
+ // like.
+ //
+// p3d::InstallDefaultLoaders();
+ P3DASSERT(p3d::context);
+ tP3DFileHandler* p3d = new(GMA_GC_VMM) tP3DFileHandler;
+// p3d::loadManager->AddHandler(p3d);
+ p3d::context->GetLoadManager()->AddHandler(p3d, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_GC_VMM) tPNGHandler, "png");
+
+ if( CommandLineOptions::Get( CLO_FE_UNJOINED ) )
+ {
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_GC_VMM) tBMPHandler, "bmp");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_GC_VMM) tTargaHandler, "tga");
+ }
+ else
+ {
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_GC_VMM) tBMPHandler, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_GC_VMM) tPNGHandler, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_GC_VMM) tTargaHandler, "p3d");
+ }
+
+// p3d->AddHandler(new tGeometryLoader);
+// GeometryWrappedLoader* pGWL = new GeometryWrappedLoader;
+ GeometryWrappedLoader* pGWL =
+ (GeometryWrappedLoader*)GetAllWrappers()->mpLoader( AllWrappers::msGeometry );
+ pGWL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pGWL );
+
+ StaticEntityLoader* pSEL =
+ (StaticEntityLoader*)GetAllWrappers()->mpLoader( AllWrappers::msStaticEntity );
+ pSEL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pSEL );
+
+ StaticPhysLoader* pSPL =
+ (StaticPhysLoader*)GetAllWrappers()->mpLoader( AllWrappers::msStaticPhys );
+ pSPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pSPL );
+
+ TreeDSGLoader* pTDL =
+ (TreeDSGLoader*)GetAllWrappers()->mpLoader( AllWrappers::msTreeDSG );
+ pTDL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pTDL );
+
+ FenceLoader* pFL =
+ (FenceLoader*)GetAllWrappers()->mpLoader( AllWrappers::msFenceEntity );
+ pFL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pFL );
+
+ IntersectLoader* pIL =
+ (IntersectLoader*)GetAllWrappers()->mpLoader( AllWrappers::msIntersectDSG );
+ pIL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pIL );
+
+ AnimCollLoader* pACL =
+ (AnimCollLoader*)GetAllWrappers()->mpLoader( AllWrappers::msAnimCollEntity );
+ pACL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pACL );
+
+ AnimDSGLoader* pAnimDSGLoader =
+ (AnimDSGLoader*)GetAllWrappers()->mpLoader( AllWrappers::msAnimEntity );
+ pAnimDSGLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pAnimDSGLoader );
+
+ DynaPhysLoader* pDPL =
+ (DynaPhysLoader*)GetAllWrappers()->mpLoader( AllWrappers::msDynaPhys );
+ pDPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pDPL );
+
+ InstStatPhysLoader* pISPL =
+ (InstStatPhysLoader*)GetAllWrappers()->mpLoader( AllWrappers::msInstStatPhys );
+ pISPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pISPL );
+
+ InstStatEntityLoader* pISEL =
+ (InstStatEntityLoader*)GetAllWrappers()->mpLoader( AllWrappers::msInstStatEntity );
+ pISEL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pISEL );
+
+ LocatorLoader* pLL =
+ (LocatorLoader*)GetAllWrappers()->mpLoader( AllWrappers::msLocator);
+ pLL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pLL );
+
+ RoadLoader* pRL =
+ (RoadLoader*)GetAllWrappers()->mpLoader( AllWrappers::msRoadSegment);
+ pRL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pRL );
+
+ PathLoader* pPL =
+ (PathLoader*)GetAllWrappers()->mpLoader( AllWrappers::msPathSegment);
+ pPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pPL );
+
+ WorldSphereLoader* pWSL =
+ (WorldSphereLoader*)GetAllWrappers()->mpLoader( AllWrappers::msWorldSphere);
+ pWSL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pWSL );
+
+ LensFlareLoader* pLSL =
+ (LensFlareLoader*)GetAllWrappers()->mpLoader( AllWrappers::msLensFlare);
+ pLSL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pLSL );
+
+ BillboardWrappedLoader* pBWL =
+ (BillboardWrappedLoader*)GetAllWrappers()->mpLoader( AllWrappers::msBillboard);
+ pBWL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pBWL );
+
+ InstParticleSystemLoader* pInstParticleSystemLoader =
+ (InstParticleSystemLoader*) GetAllWrappers()->mpLoader( AllWrappers::msInstParticleSystem);
+ pInstParticleSystemLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pInstParticleSystemLoader );
+
+ BreakableObjectLoader* pBreakableObjectLoader =
+ (BreakableObjectLoader*) GetAllWrappers()->mpLoader( AllWrappers::msBreakableObject);
+ pBreakableObjectLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pBreakableObjectLoader );
+
+ AnimDynaPhysLoader* pAnimDynaPhysLoader =
+ (AnimDynaPhysLoader*) GetAllWrappers()->mpLoader( AllWrappers::msAnimDynaPhys);
+ pAnimDynaPhysLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pAnimDynaPhysLoader );
+
+ AnimDynaPhysWrapperLoader* pAnimWrapperLoader =
+ (AnimDynaPhysWrapperLoader*) GetAllWrappers()->mpLoader( AllWrappers::msAnimDynaPhysWrapper);
+ pAnimWrapperLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pAnimWrapperLoader );
+
+ p3d->AddHandler(new(GMA_GC_VMM) tTextureLoader);
+ p3d->AddHandler( new(GMA_GC_VMM) tSetLoader );
+ p3d->AddHandler(new(GMA_GC_VMM) tShaderLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tCameraLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tGameAttrLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tLightLoader);
+
+ p3d->AddHandler(new(GMA_GC_VMM) tLocatorLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tLightGroupLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tImageLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tTextureFontLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tImageFontLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tSpriteLoader);
+ //p3d->AddHandler(new(GMA_GC_VMM) tBillboardQuadGroupLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tSkeletonLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tPolySkinLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tCompositeDrawableLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tAnimationLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tFrameControllerLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tMultiControllerLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tAnimatedObjectFactoryLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tAnimatedObjectLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tParticleSystemFactoryLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tParticleSystemLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tLensFlareGroupLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) sg::Loader);
+
+ p3d->AddHandler(new(GMA_GC_VMM) tExpressionGroupLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tExpressionMixerLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) tExpressionLoader);
+
+ p3d->AddHandler(new(GMA_GC_VMM) tVertexAnimKeyLoader);
+
+ //p3d->AddHandler(new p3d::tIgnoreLoader);
+
+ tSEQFileHandler* sequencerFileHandler = new(GMA_GC_VMM) tSEQFileHandler;
+ p3d::loadManager->AddHandler(sequencerFileHandler, "seq");
+
+ // sim lib
+ sim::InstallSimLoaders();
+
+ p3d->AddHandler(new(GMA_GC_VMM) CameraDataLoader, SRR2::ChunkID::WALKERCAM );
+ p3d->AddHandler(new(GMA_GC_VMM) CameraDataLoader, SRR2::ChunkID::FOLLOWCAM );
+ p3d->AddHandler(new(GMA_GC_VMM) IntersectionLoader);
+// p3d->AddHandler(new(GMA_GC_VMM) RoadLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) RoadDataSegmentLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) ATCLoader);
+ p3d->AddHandler(new(GMA_GC_VMM) CStatePropDataLoader);
+MEMTRACK_POP_GROUP( "GCPlatform" );
+}
+
+
+//==============================================================================
+// GCPlatform::ShutdownPure3D
+//==============================================================================
+// Description: Clean up and shut down Pure3D.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void GCPlatform::ShutdownPure3D()
+{
+ //
+ // Clean-up the Pure3D Inventory
+ //
+ p3d::inventory->RemoveAllElements();
+ p3d::inventory->DeleteAllSections();
+
+ //
+ // Clean-up the space taken by the Pure 3D context.
+ //
+ if( mpContext != NULL )
+ {
+ mpPlatform->DestroyContext( mpContext );
+ mpContext = NULL;
+ }
+
+ //
+ // Clean-up the space taken by the Pure 3D platform.
+ //
+ if( mpPlatform != NULL )
+ {
+ tPlatform::Destroy( mpPlatform );
+ mpPlatform = NULL;
+ }
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// GCPlatform::GCPlatform
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GCPlatform::GCPlatform() :
+ mpPlatform( NULL ),
+ mpContext( NULL )
+{
+}
+
+
+//==============================================================================
+// GCPlatform::~GCPlatform
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GCPlatform::~GCPlatform()
+{
+}
diff --git a/game/code/main/gcplatform.h b/game/code/main/gcplatform.h
new file mode 100644
index 0000000..ddab488
--- /dev/null
+++ b/game/code/main/gcplatform.h
@@ -0,0 +1,160 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: gcplatform.h
+//
+// Description: Abstracts the differences for setting up and shutting down
+// the different platforms.
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef GCPLATFORM_H
+#define GCPLATFORM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <main/platform.h> // base class
+
+#include <p3d/platform.hpp>
+
+//========================================
+// Forward References
+//========================================
+struct IRadMemoryHeap;
+//class tPlatform;
+class tContext;
+
+//
+// This value define the resolution of the rendering area.
+// Based on the width, Pure3D figures out the approriate height.
+//
+const int WindowSizeX = 640;
+
+#ifdef PAL
+const int WindowSizeY = 524;
+#else
+const int WindowSizeY = 480;
+#endif
+
+//
+// The depth of the rendering area. This value only has an effect
+// when Pure3D has taken over the entire display. When running in
+// a window on the desktop, Pure3D uses the same bit depth as the
+// desktop. Pure3D only supports 16, and 32 rendering depths.
+//
+static const int WindowBPP = 16;
+
+//Turn this on to disable ARAM use. Use this to test leaks in the ARAM heap.
+//#define NO_ARAM
+
+#ifdef NO_ARAM
+const int VMM_MAIN_RAM = 0;
+const int VMM_ARAM = 0;
+#else
+const int VMM_MAIN_RAM = ( 1 * 1024 * 1024 ) + ( 500 * 1024 );
+const int VMM_ARAM = 6 * 1024 * 1024;
+#endif
+
+
+//=============================================================================
+//
+// Synopsis: Provides abstraction for setting up and closing down the GC.
+//
+//=============================================================================
+class GCPlatform : public Platform
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static GCPlatform* CreateInstance();
+ static GCPlatform* GetInstance();
+ static void DestroyInstance();
+
+ // Had to workaround our nice clean design cause FTech must be init'ed
+ // before anything else is done.
+ static void InitializeFoundation();
+ static void InitializeMemory();
+
+ // Implement Platform interface.
+ virtual void InitializePlatform();
+ virtual void ShutdownPlatform();
+
+ virtual void InitializeFoundation();
+
+ virtual void LaunchDashboard();
+ virtual void ResetMachine();
+ virtual void DisplaySplashScreen( SplashScreen screenID,
+ const char* overlayText = NULL,
+ float fontScale = 1.0f,
+ float textPosX = 0.0f,
+ float textPosY = 0.0f,
+ tColour textColour = tColour( 255,255,255 ),
+ int fadeFrames = 3 );
+
+ virtual void DisplaySplashScreen( const char* textureName,
+ const char* overlayText = NULL,
+ float fontScale = 1.0f,
+ float textPosX = 0.0f,
+ float textPosY = 0.0f,
+ tColour textColour = tColour( 255,255,255 ),
+ int fadeFrames = 3 );
+
+ bool DisplayYesNo( float fontScale = 1.0f,
+ float yesPosX = 0.0f,
+ float yesPosY = 0.0f,
+ float noPosX = 0.0f,
+ float noPosY = 0.0f,
+ int fadeFrames = 3 );
+
+ tContextInitData* GetInitData();
+
+ virtual bool OnDriveError( radFileError error, const char* pDriveName, void* pUserData );
+ virtual void OnControllerError(const char *msg);
+
+ protected:
+
+ virtual void InitializeFoundationDrive();
+ virtual void ShutdownFoundation();
+
+ virtual void InitializePure3D();
+ virtual void ShutdownPure3D();
+
+ private:
+
+ // Constructors, Destructors, and Operators
+ GCPlatform();
+ virtual ~GCPlatform();
+
+ // Unused Constructors, Destructors, and Operators
+ GCPlatform( const GCPlatform& aPlatform );
+ GCPlatform& operator=( const GCPlatform& aPlatform );
+
+ // Pointer to the one and only instance of this singleton.
+ static GCPlatform* spInstance;
+
+ // Pure 3D attributes
+ tPlatform* mpPlatform;
+ tContext* mpContext;
+
+ tContextInitData mInitData;
+};
+
+//=============================================================================
+// GCPlatform::GetInitData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: tContextInitData
+//
+//=============================================================================
+inline tContextInitData* GCPlatform::GetInitData()
+{
+ return &mInitData;
+}
+
+#endif // GCPLATFORM_H
diff --git a/game/code/main/globaltypes.h b/game/code/main/globaltypes.h
new file mode 100644
index 0000000..9a8aaae
--- /dev/null
+++ b/game/code/main/globaltypes.h
@@ -0,0 +1,30 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: globaltypes.h
+//
+// Description: Declarations of utility types.
+//
+// History: 2/18/2003 + Created -- Esan
+//
+//=============================================================================
+
+#ifndef GLOBALTYPES_H
+#define GLOBALTYPES_H
+
+//
+// define 64 bit integer for all platforms
+//
+#ifdef RAD_PS2
+typedef unsigned long simpsonsUInt64;
+#endif
+
+#ifdef RAD_GAMECUBE
+typedef unsigned long long simpsonsUInt64;
+#endif
+
+#if defined RAD_XBOX || defined RAD_WIN32
+typedef unsigned __int64 simpsonsUInt64;
+#endif
+
+#endif //GLOBALTYPES_H
diff --git a/game/code/main/pchsrr2.cpp b/game/code/main/pchsrr2.cpp
new file mode 100644
index 0000000..3c4b8cb
--- /dev/null
+++ b/game/code/main/pchsrr2.cpp
@@ -0,0 +1,15 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pchsrr2.cpp
+//
+// Description: Manually generate pre-compiled headers.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// Project Includes
+//========================================
+#include <main/pchsrr2.h>
diff --git a/game/code/main/pchsrr2.h b/game/code/main/pchsrr2.h
new file mode 100644
index 0000000..931fe85
--- /dev/null
+++ b/game/code/main/pchsrr2.h
@@ -0,0 +1,23 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pchsrr2.h
+//
+// Description: Manually generate pre-compiled headers.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/pure3d.hpp>
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+//#include <memory/memoryoverrides.h>
+
diff --git a/game/code/main/platform.h b/game/code/main/platform.h
new file mode 100644
index 0000000..bd7dfcd
--- /dev/null
+++ b/game/code/main/platform.h
@@ -0,0 +1,104 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Platform
+//
+// Description: Pure virtual interface that abstracts the differences for
+// setting up and shutting down the different platforms.
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+// + Added the Splash screens + dashboard -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/p3dtypes.hpp> // tColour
+
+#include <radfile.hpp>
+
+struct IRadDrive;
+
+//=============================================================================
+//
+// Synopsis: Implement this interface for each platform that the game will
+// support (e.g. PS2, GameCube, Xbox, etc.).
+//
+//=============================================================================
+class Platform : public IRadDriveErrorCallback
+{
+ public:
+
+ // NOTE: Each derived platform must also implement the following
+ // static function. This has to be static because we need
+ // to init FTech before anything is allocated.
+ //
+ // static void InitializeFoundation();
+
+ virtual void InitializePlatform() = 0;
+ virtual void ShutdownPlatform() = 0;
+
+ virtual void LaunchDashboard() = 0;
+ virtual void ResetMachine() = 0;
+
+ //These are the baked in screens
+ enum SplashScreen
+ {
+ License, //Shows the baked in license screen as the background
+ Error, //Displays a blank black background
+ FadeToBlack
+ };
+
+ virtual void DisplaySplashScreen( SplashScreen screenID,
+ const char* overlayText = NULL,
+ float fontScale = 1.0f,
+ float textPosX = 0.0f,
+ float textPosY = 0.0f,
+ tColour textColour = tColour( 255,255,255 ),
+ int fadeFrames = 3 ) = 0;
+
+ virtual void DisplaySplashScreen( const char* textureName,
+ const char* overlayText = NULL,
+ float fontScale = 1.0f,
+ float textPosX = 0.0f,
+ float textPosY = 0.0f,
+ tColour textColour = tColour( 255,255,255 ),
+ int fadeFrames = 3 ) = 0;
+ virtual void OnControllerError(const char *msg) = 0;
+
+ //Override this if you wanna support it.
+ virtual bool OnDriveError( radFileError error, const char* pDriveName, void* pUserData ) { return false; };
+ void ClearControllerError() { OnDriveError(Success, NULL, NULL);}
+ bool PausedForErrors() const { return mPauseForError; };
+ bool IsControllerError() const { return mErrorState==CTL_ERROR; };
+ IRadDrive* GetHostDrive( void ) const { return mpIRadDrive; }
+
+ protected:
+ enum ErrorState
+ {
+ NONE,
+ P_ERROR,
+ CTL_ERROR
+ };
+
+ ErrorState mErrorState;
+
+ virtual void InitializeFoundationDrive() = 0;
+ virtual void ShutdownFoundation() = 0;
+
+ virtual void InitializePure3D() = 0;
+ virtual void ShutdownPure3D() = 0;
+
+ bool mPauseForError;
+
+ // Foundation attributes
+ IRadDrive* mpIRadDrive;
+
+ Platform() : mPauseForError( false ), mpIRadDrive( 0 ) {};
+};
+
+#endif // PLATFORM_H
diff --git a/game/code/main/ps2main.cpp b/game/code/main/ps2main.cpp
new file mode 100644
index 0000000..b5d0b61
--- /dev/null
+++ b/game/code/main/ps2main.cpp
@@ -0,0 +1,295 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ps2main.cpp
+//
+// Description: This file contains the main enrty point to the game.
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <string.h>
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radobject.hpp>
+#include <radthread.hpp>
+#include <radtextdisplay.hpp>
+
+#include <pddi/ps2/ps2context.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <main/game.h>
+#include <main/ps2platform.h>
+#include <main/singletons.h>
+#include <main/commandlineoptions.h>
+#include <memory/memoryutilities.h>
+#include <memory/srrmemory.h>
+
+#ifdef RAD_MW
+ #include <mwutils.h>
+#endif
+
+//========================================
+// Forward Declarations
+//========================================
+static void ProcessCommandLineArguments( int argc, char* argv[] );
+
+static void ProcessCommandLineArgumentsFromFile( );
+
+
+
+void OutputHandler (const char * pString )
+{
+ static int ypos = 0;
+ static IRadTextDisplay* textDisplay = 0;
+
+
+ if (!textDisplay || (ypos > 20))
+ {
+ if (textDisplay)
+ {
+ textDisplay->Release ();
+ }
+ ::radTextDisplayGet( &textDisplay, GMA_TEMP );
+ textDisplay->SetBackgroundColor( 0 );
+ textDisplay->SetTextColor( 0xffffffff );
+ textDisplay->Clear();
+ ypos = 0;
+ }
+
+ if ( textDisplay )
+ {
+ textDisplay->TextOutAt( pString, 0, ypos );
+ textDisplay->SwapBuffers();
+ ypos++;
+ }
+
+ radThreadSleep (200);
+}
+
+
+//=============================================================================
+// Function: main
+//=============================================================================
+//
+// Description: Main entry point.
+//
+// Parameters: PS2EE argc, number of command line argurments
+// argv, array of pointes to these tokens.
+//
+// Returns: 0 success, non zero error.
+//
+//=============================================================================
+int main( int argc, char* argv[] )
+{
+ //::rDebugSetOutputHandler (OutputHandler);
+
+ // Get rid of junk in the frame buffer,
+ // before pddi gets initialised
+ sceGsSyncV(0);
+ *GS_PMODE = SCE_GS_SET_PMODE(0,0,0,0,0,0,0);
+ sceGsResetPath();
+ ps2_clearvram();
+
+#ifdef RAD_MW
+ mwInit();
+#endif
+
+ /*
+ int bytes = static_cast<int>(27.0f * 1024.0 * 1024.0);
+ char* p = (char*)malloc (bytes);
+ memset (p, 0x00, bytes);
+ free (p);
+ */
+
+ /*
+#ifdef RAD_RELEASE
+ if (CommandLineOptions::Get (CLO_CD_FILES_ONLY))
+ {
+ void* p = malloc (96 * 1024 * 1024);
+ }
+#endif
+ */
+
+ //
+ // All settings get stored in GameDB.
+ //
+ CommandLineOptions::InitDefaults();
+ ProcessCommandLineArguments( argc, argv );
+
+ //
+ // Have to get FTech setup first so that we can use all the memory services.
+ //
+ PS2Platform::InitializeFoundation();
+
+ srand (Game::GetRandomSeed ());
+
+#ifndef RAD_RELEASE
+ tName::SetAllocator (GMA_DEBUG);
+
+ // Now disable the default heap
+ //
+ //g_HeapActivityTracker.EnableHeapAllocs (GMA_DEFAULT, false);
+ //g_HeapActivityTracker.EnableHeapFrees (GMA_DEFAULT, false);
+
+ // Can never free from the persistent heap
+ //
+ //g_HeapActivityTracker.EnableHeapFrees (GMA_PERSISTENT, false);
+#endif
+
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ //Process any commandline options from the command.txt file
+ ProcessCommandLineArgumentsFromFile();
+
+ //
+ // Instantiate all the singletons before doing anything else.
+ //
+ CreateSingletons();
+
+ //
+ // Construct the platform object.
+ //
+ PS2Platform* pPlatform = PS2Platform::CreateInstance();
+ rAssert( pPlatform != NULL );
+
+ //
+ // Create the game object.
+ //
+ Game* pGame = Game::CreateInstance( pPlatform );
+ rAssert( pGame != NULL );
+
+
+
+
+
+ //
+ // Initialize the game.
+ //
+ pGame->Initialize();
+
+ HeapMgr()->PopHeap (GMA_PERSISTENT);
+
+ //
+ // Run it! Control will not return from here until the game is stopped.
+ //
+ pGame->Run();
+
+ //
+ // Terminate the game (this frees all resources allocated by the game).
+ //
+ pGame->Terminate();
+
+ //
+ // Destroy the game object.
+ //
+ Game::DestroyInstance();
+
+ //
+ // Destroy the game and platform (do it in this order in case the game's
+ // destructor references the platform.
+ //
+ PS2Platform::DestroyInstance();
+
+ //
+ // Show some debug output
+ //
+#ifdef RAD_DEBUG
+ radObject::DumpObjects();
+#endif;
+
+ //
+ // Dump all the singletons.
+ //
+ DestroySingletons();
+
+ //
+ // Pass any error codes back to the operating system.
+ //
+ return( 0 );
+}
+
+
+//=============================================================================
+// Function: ProcessCommandLineArguments
+//=============================================================================
+//
+// Description: Pick out the command line options and stuff them in
+// the game database.
+//
+// Parameters: PS2EE argc, number of command line argurments
+// argv, array of pointes to these tokens.
+//
+// Returns: None.
+//
+//=============================================================================
+void ProcessCommandLineArguments( int argc, char* argv[] )
+{
+//#ifndef FINAL
+
+ rDebugPrintf( "*************************************************************\n" );
+ rDebugPrintf( "Command Line Args:\n" );
+
+ //
+ // Pick out all the command line options and store them in GameDB.
+ // Also dump them to the output for handy dandy viewing.
+ //
+ int i;
+ for( i = 0; i < argc; ++i )
+ {
+ char* argument = argv[i];
+
+ rDebugPrintf( "arg%d: %s\n", i, argument );
+
+ CommandLineOptions::HandleOption( argument );
+
+ }
+ if( !CommandLineOptions::Get( CLO_ART_STATS ) )
+ {
+ //CommandLineOptions::HandleOption( "noheaps" );
+ }
+
+
+ rDebugPrintf( "*************************************************************\n" );
+
+//#endif // FINAL
+}
+
+
+
+void ProcessCommandLineArgumentsFromFile()
+{
+#ifndef FINAL
+
+ //Chuck: looking for additional command line args being passed in from a file
+ //its for QA testing etc.
+
+ IRadFile* pfile =NULL;
+
+ ::radFileOpenSync(&pfile,"command.txt");
+
+ if (pfile != NULL)
+ {
+ char commandlinestring [256];
+ commandlinestring[ 0 ] = '\0';
+
+ pfile->ReadSync(commandlinestring,255);
+
+ //QA created the command line file and wants to pass additional arguements
+
+ char* argument = strtok(commandlinestring," ");
+ while (argument != NULL)
+ {
+ CommandLineOptions::HandleOption(argument);
+ argument=strtok(NULL," ");
+ }
+ pfile->Release();
+ }
+#endif //FINAL
+} //end of Function \ No newline at end of file
diff --git a/game/code/main/ps2platform.cpp b/game/code/main/ps2platform.cpp
new file mode 100644
index 0000000..8849a30
--- /dev/null
+++ b/game/code/main/ps2platform.cpp
@@ -0,0 +1,1847 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: PS2Platform
+//
+// Description: Abstracts the differences for setting up and shutting down
+// the different platforms.
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//===========================================================================
+
+//========================================
+// System Includes
+//========================================
+// Sony
+#include <libcdvd.h>
+#include <sifdev.h>
+#include <sifrpc.h>
+#include <sifcmd.h>
+#include <eetypes.h>
+#include <eekernel.h>
+#include <libpad.h>
+
+// Standard Lib
+#include <stdlib.h>
+#include <string.h>
+// SN Systems
+#include <libsn.h>
+// Pure 3D
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/anim/expression.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/anim/polyskin.hpp>
+#include <p3d/anim/sequencer.hpp>
+#include <p3d/anim/skeleton.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/gameattr.hpp>
+#include <p3d/image.hpp>
+#include <p3d/imagefont.hpp>
+#include <p3d/light.hpp>
+#include <p3d/locator.hpp>
+#include <p3d/platform.hpp>
+#include <p3d/scenegraph/scenegraph.hpp>
+#include <p3d/sprite.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/texture.hpp>
+#include <p3d/file.hpp>
+#include <p3d/shader.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/memory.hpp>
+#include <p3d/bmp.hpp>
+#include <p3d/png.hpp>
+#include <p3d/targa.hpp>
+#include <p3d/font.hpp>
+#include <p3d/texturefont.hpp>
+#include <p3d/unicode.hpp>
+#include <pddi/pddiext.hpp>
+// Pure 3D: Loader-specific
+#include <render/Loaders/GeometryWrappedLoader.h>
+#include <render/Loaders/StaticEntityLoader.h>
+#include <render/Loaders/StaticPhysLoader.h>
+#include <render/Loaders/TreeDSGLoader.h>
+#include <render/Loaders/FenceLoader.h>
+#include <render/Loaders/IntersectLoader.h>
+#include <render/Loaders/AnimCollLoader.h>
+#include <render/Loaders/AnimDSGLoader.h>
+#include <render/Loaders/DynaPhysLoader.h>
+#include <render/Loaders/InstStatPhysLoader.h>
+#include <render/Loaders/InstStatEntityLoader.h>
+#include <render/Loaders/WorldSphereLoader.h>
+#include <render/Loaders/BillboardWrappedLoader.h>
+#include <render/Loaders/instparticlesystemloader.h>
+#include <render/Loaders/breakableobjectloader.h>
+#include <render/Loaders/lensflareloader.h>
+#include <render/Loaders/AnimDynaPhysLoader.h>
+#include <p3d/shadow.hpp>
+#include <p3d/anim/animatedobject.hpp>
+#include <p3d/anim/vertexanimkey.hpp>
+#include <p3d/effects/particleloader.hpp>
+#include <p3d/effects/opticloader.hpp>
+
+//This is so we can get the name of the file that's failing.
+#include <../src/radfile/common/requests.hpp>
+
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radthread.hpp>
+#include <radplatform.hpp>
+#include <radtime.hpp>
+#include <radmemorymonitor.hpp>
+#include <raddebugcommunication.hpp>
+#include <raddebugwatch.hpp>
+#include <radfile.hpp>
+#include <radmovie2.hpp>
+#include <radload/radload.hpp>
+#include <radtextdisplay.hpp>
+
+// sim - for InstallSimLoaders
+#include <simcommon/simutility.hpp>
+// StateProps
+#include <stateprop/statepropdata.hpp>
+
+// To turn off movies during an error.
+#include <presentation/fmvplayer/fmvplayer.h>
+#include <presentation/presentation.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/inputmanager.h>
+#include <main/ps2platform.h>
+#include <main/game.h>
+#include <main/commandlineoptions.h>
+#include <memory/srrmemory.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/Loaders/AllWrappers.h>
+
+#include <loading/locatorloader.h>
+#include <loading/cameradataloader.h>
+#include <loading/roadloader.h>
+#include <loading/pathloader.h>
+#include <loading/intersectionloader.h>
+#include <loading/roaddatasegmentloader.h>
+#include <atc/atcloader.h>
+
+#include <debug/debuginfo.h>
+
+#include <main/errorsps2.h>
+
+#include <sound/soundmanager.h>
+
+#include <presentation/presentation.h>
+#include <presentation/gui/guitextbible.h>
+#include <data/gamedatamanager.h>
+
+#include <cheats/cheatinputsystem.h>
+
+#include <mission/gameplaymanager.h>
+#include <pddi/pddi.hpp>
+
+#define PS2_SECTION "PS2_SECTION"
+
+// #define IOP_MEMORY_TEST
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+IRadCementLibrary* PS2Platform::s_MainCement = NULL;
+
+// Static pointer to instance of singleton.
+PS2Platform* PS2Platform::spInstance = NULL;
+
+
+//The Adlib font. <sigh>
+unsigned char gFont[] =
+#include <font/defaultfont.h>
+
+
+//
+// Uncomment the define below to run off a DVD
+//
+/***** #define DVD_MEDIA *****/
+#define DVD_MEDIA
+
+//
+// This value define the resolution of the rendering area.
+// Based on the width, Pure3D figures out the approriate height.
+//
+static const int WindowSizeX = 640;
+
+//
+// The depth of the rendering area. This value only has an effect
+// when Pure3D has taken over the entire display. When running in
+// a window on the desktop, Pure3D uses the same bit depth as the
+// desktop. Pure3D only supports 16, and 32 rendering depths.
+//
+static const int WindowBPP = 32;
+
+void LoadMemP3DFile( unsigned char* buffer, unsigned int size, tEntityStore* store )
+{
+ tFileMem* file = new tFileMem(buffer,size);
+ file->AddRef();
+ file->SetFilename("memfile.p3d");
+ p3d::loadManager->GetP3DHandler()->Load( file, p3d::inventory );
+ file->Release();
+}
+
+extern bool gIgnoreLastFrameSyncCheck;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// PS2Platform::CreateInstance
+//==============================================================================
+//
+// Description: Creates the PS2Platform.
+//
+// Parameters: None.
+//
+// Return: Pointer to the PS2Platform.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+PS2Platform* PS2Platform::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "PS2Platform" );
+ rAssert( spInstance == NULL );
+
+ spInstance = new(GMA_PERSISTENT) PS2Platform;
+ rAssert( spInstance );
+MEMTRACK_POP_GROUP("PS2Platform");
+
+ return spInstance;
+}
+
+//==============================================================================
+// PS2Platform::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the PS2Platform singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the PS2Platform.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+PS2Platform* PS2Platform::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// PS2Platform::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the PS2Platform.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void PS2Platform::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+}
+
+
+//==============================================================================
+// PS2Platform::InitializeFoundation
+//==============================================================================
+// Description: Get FTech ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: The FTech systems must be initialized in a particular order.
+// Consult their documentation before changing.
+//
+//==============================================================================
+void PS2Platform::InitializeFoundation()
+{
+ const char IRX_PATH[] = "IRX\\";
+ const char SONY_LIB_NAME[] = "IOPRP254.IMG";
+
+ //
+ // Initialize the memory heaps
+ //
+ PS2Platform::InitializeMemory();
+
+#ifndef FINAL
+ //
+ // Register an out-of-memory display handler in case something goes bad
+ // while allocating the heaps
+ //
+ ::radMemorySetOutOfMemoryCallback( PrintOutOfMemoryMessage, NULL );
+#endif
+
+ //
+ // Initialize memory monitor by JamesCo. TM.
+ //
+ if( CommandLineOptions::Get( CLO_MEMORY_MONITOR) )
+ {
+ const int KB = 1024;
+ ::radMemoryMonitorInitialize( 100 * KB, GMA_DEBUG );
+ }
+
+ // Setup the memory heaps
+ //
+ HeapMgr()->PrepareHeapsStartup ();
+
+
+ // Seed the heap stack with this
+ //
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ //
+ // Initilalize the platform system
+ // On the PS2, provide the root path for IRXs.
+ //
+#ifdef DVD_MEDIA
+ radPlatformGameMediaType mediaType = GameMediaDVD;
+#else
+ radPlatformGameMediaType mediaType = GameMediaCD;
+#endif // DVD_MEDIA
+
+ if( CommandLineOptions::Get( CLO_FIREWIRE ) )
+ {
+ ::radPlatformInitialize( IRX_PATH,
+ IOPMediaCDVD,
+ mediaType,
+ NULL,
+ GMA_PERSISTENT );
+ }
+ else if( CommandLineOptions::Get( CLO_CD_FILES_ONLY ) )
+ {
+ ::radPlatformInitialize( IRX_PATH,
+ IOPMediaCDVD,
+ mediaType,
+ SONY_LIB_NAME,
+ GMA_PERSISTENT );
+ }
+ else
+ {
+ ::radPlatformInitialize( IRX_PATH,
+ IOPMediaHost,
+ mediaType,
+ SONY_LIB_NAME,
+ GMA_PERSISTENT );
+ }
+
+
+
+#ifdef IOP_MEMORY_TEST
+
+ IRadTextDisplay * pDisp;
+
+ radTextDisplayGet( & pDisp, RADMEMORY_ALLOC_DEFAULT );
+
+ char buf[ 256 ];
+
+ unsigned int maxFreeMemSize = sceSifQueryMaxFreeMemSize( );
+ unsigned int totalFreeMemSize = sceSifQueryTotalFreeMemSize( );
+ unsigned int memSize = sceSifQueryMemSize( );
+
+ sprintf(
+ buf,
+ "\nMemSize: [0x%x]\nTotalFreeMemSize: [0x%x]\nMaxFreeMemSize:[0x%x]\n",
+ memSize,
+ totalFreeMemSize,
+ maxFreeMemSize );
+
+ pDisp->TextOut( buf );
+ pDisp->SwapBuffers( );
+
+ while( 1 ) { }
+
+ pDisp->Release( );
+#endif
+
+
+
+ // Eat up 6 mb if "TOOL" in command line for testing IOP crashes.
+
+ if ( CommandLineOptions::Get( CLO_PS2_TOOL ) )
+ {
+
+ // Results from above:
+
+ // Tool:
+ // MemSize: [0x7fff00]
+ // TotalFreeMemSize: [0x798400]
+ // MaxFreeMemSize: [0x798200]
+ //
+ // Artist Box:
+ // MemSize: [0x200000]
+ // TotalFreeMemSize: [0x1a3900]
+ // MaxFreeMemSize: [0x1a3900]
+
+
+ const unsigned int toolMaxFreeMemSize = 0x798200;
+ const unsigned int consumerMaxFreeMemSize = 0x1a3900;
+ const unsigned int difMaxFreeMemSize = toolMaxFreeMemSize - consumerMaxFreeMemSize;
+
+ const int allocChunkSize = 4096 * 16;
+
+ rReleasePrintf( "Consuming [0x%x] bytes on tool:\n", difMaxFreeMemSize );
+
+ unsigned int maxFreeMemSize;
+
+ do
+ {
+ void * pMem = sceSifAllocIopHeap( allocChunkSize );
+ rReleaseAssertMsg( pMem, "Out of memory consuming extra tool memory" );
+ maxFreeMemSize = sceSifQueryMaxFreeMemSize( );
+ rReleasePrintf( "BURNING IOP: MaxFreeMemSize: [0x%x]\n", maxFreeMemSize );
+ } while( maxFreeMemSize > consumerMaxFreeMemSize );
+ }
+
+ //
+ // Initialize the timer system
+ //
+ ::radTimeInitialize();
+
+ //
+ // Initialize the debug communication system.
+ //
+ if( CommandLineOptions::Get( CLO_FIREWIRE ) )
+ {
+ ::radDbgComTargetInitialize( FireWire,
+ radDbgComDefaultPort,
+ (void*)"",
+ GMA_DEBUG );
+ }
+ else
+ {
+ ::radDbgComTargetInitialize( Deci,
+ radDbgComDefaultPort, // Default
+ NULL, // Default
+ GMA_DEBUG );
+ }
+
+ //
+ // Initialize the Watcher.
+ //
+#ifdef DEBUGWATCH
+ ::radDbgWatchInitialize( "SRR2",
+ 16384 * 32, // Default
+ GMA_DEBUG );
+#endif // DEBUGWATCH
+
+ //
+ // Initialize the file system.
+ //
+ ::radFileInitialize( 50, // Default
+ 32, // Default
+ GMA_PERSISTENT );
+
+ ::radLoadInitialize( );
+
+ if( CommandLineOptions::Get( CLO_FIREWIRE ) )
+ {
+ ::radSetDefaultDrive( "REMOTEDRIVE:" );
+ }
+ else if( CommandLineOptions::Get( CLO_CD_FILES_ONLY ) )
+ {
+ ::radSetDefaultDrive( "CDROM:" );
+ }
+ else
+ {
+ ::radSetDefaultDrive( "HOSTDRIVE:" );
+ }
+
+ ::radDriveMount( 0, GMA_PERSISTENT );
+
+ //
+ // On PS2, synchronously load the art.rcf file. This is needed before
+ // anything else can happen.
+ //
+ if( CommandLineOptions::Get( CLO_CD_FILES_ONLY ) )
+ {
+ radFileRegisterCementLibrarySync( & s_MainCement, "art.rcf" );
+ }
+
+
+ //
+ // Initialize the new movie player
+ //
+ ::radMovieInitialize2( GMA_PERSISTENT );
+
+ //
+ // Init VU0 for optimizations.
+ //
+ ::radMathInitialize();
+
+#ifndef FINAL
+ //
+ // Set up exception handlers, so that when the game crashes we can
+ // see something.
+ //
+ if( CommandLineOptions::Get( CLO_CD_FILES_ONLY ) ||
+ CommandLineOptions::Get( CLO_FIREWIRE ) )
+ {
+ SetDebugHandler( 1, handleTLBChange );
+ SetDebugHandler( 2, handleTLBLoadMismatch );
+ SetDebugHandler( 3, handleTLBStoreMismatch );
+ SetDebugHandler( 4, handleAddressLoadError );
+ SetDebugHandler( 5, handleAddressStoreError );
+ SetDebugHandler( 6, handleBusFetchError );
+ SetDebugHandler( 7, handleBusDataError );
+
+ // #8 is "system call exception", #9 is "breakpoint exception".
+ // I don't think we need those.
+
+ SetDebugHandler( 10, handleReservedInstruction );
+ SetDebugHandler( 11, handleCoprocessor );
+ SetDebugHandler( 12, handleOverflow );
+ SetDebugHandler( 13, handleTrap );
+ }
+#endif
+
+ HeapMgr()->PopHeap (GMA_PERSISTENT);
+}
+
+
+
+//==============================================================================
+// PS2Platform::InitializeMemory
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void PS2Platform::InitializeMemory()
+{
+ //
+ // Only do this once!
+ //
+ if( gMemorySystemInitialized == true )
+ {
+ return;
+ }
+
+ gMemorySystemInitialized = true;
+
+ //
+ // Initialize thread system.
+ //
+ ::radThreadInitialize();
+
+ //
+ // Initialize memory system.
+ //
+ ::radMemoryInitialize();
+}
+
+
+
+
+//==============================================================================
+// PS2Platform::InitializePlatform
+//==============================================================================
+// Description: Get the PS2 ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void PS2Platform::InitializePlatform()
+{
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ if( CommandLineOptions::Get( CLO_SN_PROFILER ) )
+ {
+ this->EnableSnProfiler();
+ }
+
+ InitializePure3D();
+
+ // Add anything here that needs to be before the
+ // drive is opened.
+ DisplaySplashScreen( Error ); // blank screen
+
+ //
+ // This is SLOW so do it last.
+ //
+ InitializeFoundationDrive();
+
+ //
+ // Can only setup the controller after the IOP has been rebooted.
+ //
+ GetInputManager()->Init();
+
+ HeapMgr()->PopHeap (GMA_PERSISTENT);
+}
+
+
+//==============================================================================
+// PS2Platform::ShutdownPlatform
+//==============================================================================
+// Description: Shut down the PS2.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void PS2Platform::ShutdownPlatform()
+{
+ ShutdownPure3D();
+ ShutdownFoundation();
+}
+
+void PS2Platform::ResetMachine()
+{
+ scePadEnd();
+ sceSifExitCmd();
+
+ char build = 0;
+
+#ifdef RAD_DEBUG
+ build = 'd';
+#elif defined RAD_TUNE
+ build = 't';
+#else
+ build = 'r';
+#endif
+
+ char imageName[64];
+
+ if( CommandLineOptions::Get( CLO_CD_FILES_ONLY ) )
+ {
+#ifndef RAD_E3
+ sprintf( imageName, "cdrom0:\\slps123.45" );
+#else
+ sprintf( imageName, "cdrom0:\\slps123.45" );
+#endif
+ LoadExecPS2( imageName, 0, NULL );
+ }
+ else
+ {
+ char *args[] = { "hostfiles" };
+#ifndef RAD_E3
+ sprintf( imageName, "hostdrive:\\srr2p%c.elf", build );
+#else
+ sprintf( imageName, "hostdrive:\\srr2e3p%c.elf", build );
+#endif
+ LoadExecPS2( imageName, 1, args );
+ }
+}
+
+//=============================================================================
+// PS2Platform::LaunchDashboard
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PS2Platform::LaunchDashboard()
+{
+ GetLoadingManager()->CancelPendingRequests();
+
+ //TODO: Make sure sounds shut down too.
+ GetSoundManager()->SetMasterVolume( 0.0f );
+
+ DisplaySplashScreen( FadeToBlack );
+
+ GetPresentationManager()->StopAll();
+
+ //Oh boy.
+ GameDataManager::DestroyInstance(); //Get rid of memcards
+
+ p3d::loadManager->CancelAll();
+
+ SoundManager::DestroyInstance();
+
+ ShutdownPlatform();
+ ResetMachine();
+}
+
+
+//=============================================================================
+// PS2Platform::DisplaySplashScreen
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( SplashScreen screenID,
+// const char* overlayText = NULL,
+// float fontScale = 1.0f,
+// float textPosX = 0.0f,
+// float textPosY = 0.0f,
+// tColour textColour,
+// int fadeFrames = 3 )
+//
+// Return: void
+//
+//=============================================================================
+void PS2Platform::DisplaySplashScreen( SplashScreen screenID,
+ const char* overlayText,
+ float fontScale,
+ float textPosX,
+ float textPosY,
+ tColour textColour,
+ int fadeFrames )
+{
+ DisplaySplashScreen( NULL, overlayText, fontScale, textPosX, textPosY, textColour, fadeFrames );
+}
+
+//=============================================================================
+// PS2Platform::DisplaySplashScreen
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* textureName,
+// const char* overlayText = NULL,
+// float fontScale = 1.0f,
+// float textPosX = 0.0f,
+// float textPosY = 0.0f,
+// tColour textColour,
+// int fadeFrames = 3 )
+//
+// Return: void
+//
+//=============================================================================
+void PS2Platform::DisplaySplashScreen( const char* textureName,
+ const char* overlayText,
+ float fontScale,
+ float textPosX,
+ float textPosY,
+ tColour textColour,
+ int fadeFrames )
+{
+ p3d::pddi->DrawSync();
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+ p3d::inventory->PushSection();
+ p3d::inventory->AddSection( PS2_SECTION );
+ p3d::inventory->SelectSection( PS2_SECTION );
+
+ P3D_UNICODE unicodeText[256];
+
+ // Save the current Projection mode so I can restore it later
+ pddiProjectionMode pm = p3d::pddi->GetProjectionMode();
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_DEVICE);
+
+ pddiCullMode cm = p3d::pddi->GetCullMode();
+ p3d::pddi->SetCullMode(PDDI_CULL_NONE);
+
+
+ //CREATE THE FONT
+ tTextureFont* thisFont = NULL;
+
+ // Convert memory buffer into a texturefont.
+ //
+ //p3d::load(gFont, DEFAULTFONT_SIZE, GMA_TEMP);
+ LoadMemP3DFile( gFont, DEFAULTFONT_SIZE, p3d::inventory );
+
+ thisFont = p3d::find<tTextureFont>("adlibn_20");
+ rAssert( thisFont );
+
+ thisFont->AddRef();
+ tShader* fontShader = thisFont->GetShader();
+ //fontShader->SetInt( )
+
+
+ p3d::AsciiToUnicode( overlayText, unicodeText, 256 );
+
+ // Make the missing letter into somthing I can see
+ //
+ thisFont->SetMissingLetter(p3d::ConvertCharToUnicode('j'));
+
+ int a = 0;
+
+ do
+ {
+ p3d::pddi->SetColourWrite(true, true, true, true);
+ p3d::pddi->SetClearColour( pddiColour(0,0,0) );
+ p3d::pddi->BeginFrame();
+ p3d::pddi->Clear(PDDI_BUFFER_COLOUR);
+
+ //This is for fading in the font and shaders.
+ int bright = 255;
+ if (a < fadeFrames) bright = (a * 255) / fadeFrames;
+ if ( bright > 255 ) bright = 255;
+ tColour c(bright, bright, bright, 255);
+
+ //Display font
+ if (overlayText != NULL)
+ {
+ tColour colour = textColour;
+ colour.SetAlpha( bright );
+
+ thisFont->SetColour( colour );
+
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_ORTHOGRAPHIC);
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+
+ p3d::stack->Translate( textPosX, textPosY, 1.5f);
+ float scaleSize = 1.0f / 480.0f; //This is likely good for 528 also.
+ p3d::stack->Scale(scaleSize * fontScale, scaleSize* fontScale , 1.0f);
+
+ if ( textPosX != 0.0f || textPosY != 0.0f )
+ {
+ thisFont->DisplayText( unicodeText );
+ }
+ else
+ {
+ thisFont->DisplayText( unicodeText, 3 );
+ }
+
+ p3d::stack->Pop();
+ }
+
+ p3d::pddi->EndFrame();
+ p3d::context->SwapBuffers();
+
+ ++a;
+
+ } while (a <= fadeFrames);
+
+ p3d::pddi->SetCullMode(cm);
+ p3d::pddi->SetProjectionMode(pm);
+
+ p3d::pddi->DrawSync();
+
+ gIgnoreLastFrameSyncCheck = true;
+
+ //Should do this after a vsync.
+ thisFont->Release();
+
+ p3d::inventory->RemoveSectionElements(PS2_SECTION);
+ p3d::inventory->DeleteSection(PS2_SECTION);
+ p3d::inventory->PopSection();
+
+ gIgnoreLastFrameSyncCheck = false;
+
+ HeapMgr()->PopHeap(GMA_TEMP);
+}
+
+
+
+
+
+
+
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// PS2Platform::InitializeFoundationDrive
+//==============================================================================
+// Description: Get FTech ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: The FTech systems must be initialized in a particular order.
+// Consult their documentation before changing.
+//
+//==============================================================================
+void PS2Platform::InitializeFoundationDrive()
+{
+ //
+ // No remote drives if we're only allowing files from the CD.
+ //
+
+// if( CommandLineOptions::Get( CLO_CD_FILES_ONLY ) )
+// {
+// ::radDriveSetShadow( false );
+// }
+
+ //
+ // Get the CDROM drive and hold it open for the life of the game.
+ // This is a costly operation so we only want to do it once.
+ //
+ if( CommandLineOptions::Get( CLO_CD_FILES_ONLY ) )
+ {
+ ::radDriveOpen( &mpIRadDrive,
+ "CDROM:",
+ NormalPriority, // Default
+ GMA_PERSISTENT );
+
+ //Only care about CD drives.
+ mpIRadDrive->RegisterErrorHandler( this, NULL );
+ }
+ else
+ {
+ ::radDriveOpen( &mpIRadDrive,
+ "HOSTDRIVE:",
+ NormalPriority, // Default
+ GMA_PERSISTENT );
+
+ //Only care about CD drives.
+ mpIRadDrive->RegisterErrorHandler( this, NULL );
+ }
+ rAssert( mpIRadDrive != NULL );
+
+ //
+ // Set the read-write granulatity to prevent operations from
+ // being partitioned.
+ //
+ //const int GRANULARITY = 20 * 1024 * 1024;
+ //mpIRadDrive->SetReadWriteGranularity( GRANULARITY );
+
+}
+
+
+//==============================================================================
+// PS2Platform::ShutdownFoundation
+//==============================================================================
+// Description: Shut down Foundation Tech.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: The FTech systems must be terminated in the reverse order that
+// they were initialized in.
+//
+//==============================================================================
+void PS2Platform::ShutdownFoundation()
+{
+ //
+ // Release the main cement file.
+ //
+ s_MainCement->Release();
+ s_MainCement = NULL;
+
+ //
+ // Release the drive we've held open since the begining.
+ //
+ mpIRadDrive->Release();
+ mpIRadDrive = NULL;
+
+/*
+ spIRadMemoryHeapDefault->Release();
+ spIRadMemoryHeapDefault = NULL;
+ spIRadMemoryHeapTemp->Release();
+ spIRadMemoryHeapTemp = NULL;
+ spIRadMemoryHeapPersistent->Release();
+ spIRadMemoryHeapPersistent = NULL;
+ spIRadMemoryHeapLevel->Release();
+ spIRadMemoryHeapLevel = NULL;
+ spIRadMemoryHeapSwapA->Release();
+ spIRadMemoryHeapSwapA = NULL;
+ spIRadMemoryHeapSwapB->Release();
+ spIRadMemoryHeapSwapB = NULL;
+#ifndef RAD_RELEASE
+ spIRadMemoryHeapDebug->Release();
+ spIRadMemoryHeapDebug = NULL;
+#endif // RAD_RELEASE
+*/
+
+ //
+ // Shutdown the systems in the reverse order.
+ //
+ ::radDriveUnmount();
+ ::radFileTerminate();
+ ::radDbgWatchTerminate();
+ if( CommandLineOptions::Get( CLO_MEMORY_MONITOR) )
+ {
+ ::radMemoryMonitorTerminate();
+ }
+ ::radDbgComTargetTerminate();
+ ::radTimeTerminate();
+ ::radPlatformTerminate();
+ ::radMemoryTerminate();
+ ::radThreadTerminate();
+ ::radMovieTerminate2( );
+}
+
+
+//==============================================================================
+// PS2Platform::InitializePure3D
+//==============================================================================
+// Description: Get Pure3D ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void PS2Platform::InitializePure3D()
+{
+MEMTRACK_PUSH_GROUP( "PS2Platform" );
+// p3d::SetMemAllocator( p3d::ALLOC_DEFAULT, GMA_PERSISTENT );
+// p3d::SetMemAllocator( p3d::ALLOC_LOADED, GMA_LEVEL );
+
+ //
+ // Initialise Pure3D platform object.
+ // This call differs between different platforms. The Win32 version,
+ // for example requires the application instance to be passed in.
+ //
+ mpPlatform = tPlatform::Create();
+ rAssert( mpPlatform != NULL );
+
+ //
+ // Initialiase the Pure3D context object.
+ // We have to create on of these for every window, and for every PDDI
+ // instance we use for rendering. Since almost every application only
+ // uses one window and PDDI library at a time, we one need to create one
+ // context.
+ //
+ tContextInitData init;
+
+ //
+ // These values only take effect in fullscreen mode. In windowed mode, the
+ // dimensions of the window define the rendering area. We'll define them
+ // anyway for completeness sake.
+ //
+ init.xsize = WindowSizeX;
+
+ //
+ // Rendering to NTSC or PAL.
+ //
+#ifdef PAL
+ init.pal = true;
+#else
+ init.pal = false;
+#endif
+
+ //for progressive scan
+ if( CommandLineOptions::Get( CLO_PROGRESSIVE_SCAN ) )
+ {
+ init.dtv480 = true;
+ }
+
+ //TODO: Investigate VSync
+// init.lockToVsync = true;
+ init.lockToVsync = false;
+
+ //
+ // Create the context.
+ //
+ mpContext = mpPlatform->CreateContext( &init );
+ rAssert( mpContext != NULL );
+
+ //
+ // Assign this context to the platform.
+ //
+ mpPlatform->SetActiveContext( mpContext );
+
+ //((pddiExtPS2Control*)p3d::pddi->GetExtension(PDDI_EXT_PS2_CONTROL))->EnableClipper(false);
+ //((pddiExtPS2Control*)p3d::pddi->GetExtension(PDDI_EXT_PS2_CONTROL))->ForceMFIFOSync(true);
+
+ p3d::pddi->EnableZBuffer( true );
+
+ //
+ // This call installs chunk handlers for all the primary chunk types that
+ // Pure3D supports. This includes textures, materials, geometries, and the
+ // like.
+ //
+// p3d::InstallDefaultLoaders();
+ P3DASSERT(p3d::context);
+ tP3DFileHandler* p3d = new(GMA_PERSISTENT) tP3DFileHandler;
+ //p3d::loadManager->AddHandler(p3d, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(p3d, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tPNGHandler, "png");
+
+ if( CommandLineOptions::Get( CLO_FE_UNJOINED ) )
+ {
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tBMPHandler, "bmp");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tTargaHandler, "tga");
+ }
+ else
+ {
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tBMPHandler, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tPNGHandler, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tTargaHandler, "p3d");
+ }
+
+// p3d->AddHandler(new tGeometryLoader);
+// GeometryWrappedLoader* pGWL = new GeometryWrappedLoader;
+ GeometryWrappedLoader* pGWL =
+ (GeometryWrappedLoader*)GetAllWrappers()->mpLoader( AllWrappers::msGeometry );
+ pGWL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pGWL );
+
+ StaticEntityLoader* pSEL =
+ (StaticEntityLoader*)GetAllWrappers()->mpLoader( AllWrappers::msStaticEntity );
+ pSEL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pSEL );
+
+ StaticPhysLoader* pSPL =
+ (StaticPhysLoader*)GetAllWrappers()->mpLoader( AllWrappers::msStaticPhys );
+ pSPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pSPL );
+
+ TreeDSGLoader* pTDL =
+ (TreeDSGLoader*)GetAllWrappers()->mpLoader( AllWrappers::msTreeDSG );
+ pTDL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pTDL );
+
+ FenceLoader* pFL =
+ (FenceLoader*)GetAllWrappers()->mpLoader( AllWrappers::msFenceEntity );
+ pFL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pFL );
+
+ IntersectLoader* pIL =
+ (IntersectLoader*)GetAllWrappers()->mpLoader( AllWrappers::msIntersectDSG );
+ pIL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pIL );
+
+ AnimCollLoader* pACL =
+ (AnimCollLoader*)GetAllWrappers()->mpLoader( AllWrappers::msAnimCollEntity );
+ pACL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pACL );
+
+ AnimDSGLoader* pAnimDSGLoader =
+ (AnimDSGLoader*)GetAllWrappers()->mpLoader( AllWrappers::msAnimEntity );
+ pAnimDSGLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pAnimDSGLoader );
+
+ DynaPhysLoader* pDPL =
+ (DynaPhysLoader*)GetAllWrappers()->mpLoader( AllWrappers::msDynaPhys );
+ pDPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pDPL );
+
+ InstStatPhysLoader* pISPL =
+ (InstStatPhysLoader*)GetAllWrappers()->mpLoader( AllWrappers::msInstStatPhys );
+ pISPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pISPL );
+
+ InstStatEntityLoader* pISEL =
+ (InstStatEntityLoader*)GetAllWrappers()->mpLoader( AllWrappers::msInstStatEntity );
+ pISEL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pISEL );
+
+ LocatorLoader* pLL =
+ (LocatorLoader*)GetAllWrappers()->mpLoader( AllWrappers::msLocator);
+ pLL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pLL );
+
+ RoadLoader* pRL =
+ (RoadLoader*)GetAllWrappers()->mpLoader( AllWrappers::msRoadSegment);
+ pRL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pRL );
+
+ PathLoader* pPL =
+ (PathLoader*)GetAllWrappers()->mpLoader( AllWrappers::msPathSegment);
+ pPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pPL );
+
+ WorldSphereLoader* pWSL =
+ (WorldSphereLoader*)GetAllWrappers()->mpLoader( AllWrappers::msWorldSphere);
+ pWSL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pWSL );
+
+ LensFlareLoader* pLSL =
+ (LensFlareLoader*)GetAllWrappers()->mpLoader( AllWrappers::msLensFlare);
+ pLSL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pLSL );
+
+ BillboardWrappedLoader* pBWL =
+ (BillboardWrappedLoader*)GetAllWrappers()->mpLoader( AllWrappers::msBillboard);
+ pBWL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pBWL );
+
+ InstParticleSystemLoader* pInstParticleSystemLoader =
+ (InstParticleSystemLoader*) GetAllWrappers()->mpLoader( AllWrappers::msInstParticleSystem);
+ pInstParticleSystemLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pInstParticleSystemLoader );
+
+ BreakableObjectLoader* pBreakableObjectLoader =
+ (BreakableObjectLoader*) GetAllWrappers()->mpLoader( AllWrappers::msBreakableObject);
+ pBreakableObjectLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pBreakableObjectLoader );
+
+ AnimDynaPhysLoader* pAnimDynaPhysLoader =
+ (AnimDynaPhysLoader*) GetAllWrappers()->mpLoader( AllWrappers::msAnimDynaPhys);
+ pAnimDynaPhysLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pAnimDynaPhysLoader );
+
+ AnimDynaPhysWrapperLoader* pAnimWrapperLoader =
+ (AnimDynaPhysWrapperLoader*) GetAllWrappers()->mpLoader( AllWrappers::msAnimDynaPhysWrapper);
+ pAnimWrapperLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pAnimWrapperLoader );
+
+ p3d->AddHandler(new(GMA_PERSISTENT) tTextureLoader);
+ p3d->AddHandler( new(GMA_PERSISTENT) tSetLoader );
+ p3d->AddHandler(new(GMA_PERSISTENT) tShaderLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tCameraLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tGameAttrLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tLightLoader);
+
+ p3d->AddHandler(new(GMA_PERSISTENT) tLocatorLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tLightGroupLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tImageLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tTextureFontLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tImageFontLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tSpriteLoader);
+ //p3d->AddHandler(new(GMA_PERSISTENT) tBillboardQuadGroupLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tSkeletonLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tPolySkinLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tCompositeDrawableLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tAnimationLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tFrameControllerLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tMultiControllerLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tAnimatedObjectFactoryLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tAnimatedObjectLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tParticleSystemFactoryLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tParticleSystemLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tLensFlareGroupLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) sg::Loader);
+
+ p3d->AddHandler(new(GMA_PERSISTENT) tExpressionGroupLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tExpressionMixerLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tExpressionLoader);
+
+ p3d->AddHandler(new(GMA_PERSISTENT) tVertexAnimKeyLoader);
+
+ //ATCloader
+ p3d->AddHandler(new(GMA_PERSISTENT) ATCLoader);
+
+ //p3d->AddHandler(new p3d::tIgnoreLoader);
+
+
+ tSEQFileHandler* sequencerFileHandler = new(GMA_PERSISTENT) tSEQFileHandler;
+ p3d::loadManager->AddHandler(sequencerFileHandler, "seq");
+
+ // sim lib
+ sim::InstallSimLoaders();
+
+ p3d->AddHandler(new(GMA_PERSISTENT) CameraDataLoader, SRR2::ChunkID::WALKERCAM );
+ p3d->AddHandler(new(GMA_PERSISTENT) CameraDataLoader, SRR2::ChunkID::FOLLOWCAM );
+ p3d->AddHandler(new(GMA_PERSISTENT) IntersectionLoader);
+ //p3d->AddHandler(new(GMA_PERSISTENT) RoadLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) RoadDataSegmentLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) CStatePropDataLoader);
+MEMTRACK_POP_GROUP("PS2Platform");
+
+ p3d::context->SetClearColour(tColour(0,0,0));
+ p3d::pddi->Clear(PDDI_BUFFER_ALL);
+ p3d::context->SwapBuffers();
+ p3d::pddi->Clear(PDDI_BUFFER_ALL);
+}
+
+
+//==============================================================================
+// PS2Platform::ShutdownPure3D
+//==============================================================================
+// Description: Clean up and shut down Pure3D.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void PS2Platform::ShutdownPure3D()
+{
+ //
+ // Clean-up the Pure3D Inventory
+ //
+ p3d::inventory->RemoveAllElements();
+ p3d::inventory->DeleteAllSections();
+
+ //
+ // Clean-up the space taken by the Pure 3D context.
+ //
+ if( mpContext != NULL )
+ {
+ mpPlatform->DestroyContext( mpContext );
+ mpContext = NULL;
+ }
+
+ //
+ // Clean-up the space taken by the Pure 3D platform.
+ //
+ if( mpPlatform != NULL )
+ {
+ tPlatform::Destroy( mpPlatform );
+ mpPlatform = NULL;
+ }
+}
+
+//==============================================================================
+// PS2Platform::SetProgressiveMode
+//==============================================================================
+// Description: Reinitialize PDDI to change progressive scan mode (480p)
+//
+// Parameters: boolean : true means go to 480p mode
+//
+// Return: None.
+//
+//==============================================================================
+void PS2Platform::SetProgressiveMode( bool progressiveScan )
+{
+ pddiDisplayInit init;
+
+ init.xsize = WindowSizeX;
+
+ //
+ // Rendering to NTSC or PAL.
+ //
+#ifdef PAL
+ init.pal = true;
+#else
+ init.pal = false;
+#endif
+
+ init.dtv480 = progressiveScan;
+
+ init.lockToVsync = false;
+
+ p3d::display->InitDisplay(&init);
+
+ mProgressiveMode = progressiveScan;
+}
+
+//==============================================================================
+// PS2Platform::CheckForStartupButtons
+//==============================================================================
+
+bool PS2Platform::CheckForStartupButtons( void )
+{
+ unsigned char buffer[ 32 ];
+ bool buttonsPushed = false;
+
+ for ( int p = 0; p < 2; p++ )
+ {
+ for ( int s = 0; s < 4; s++ )
+ {
+ // spin until the system understands that there's a controller around.
+ int state = scePadStateExecCmd;
+ do
+ {
+ state = scePadGetState( p, s );
+ } while( state != scePadStateDiscon && state != scePadStateFindCTP1 &&
+ state != scePadStateStable && state != scePadStateError );
+
+ //
+ // Now we have a controller.
+ //
+ if ( state == scePadStateFindCTP1 || state == scePadStateStable )
+ {
+ if ( scePadRead( p, s, buffer ) != 0 && // read success
+ buffer[ 0 ] == 0 && // buffer fill success
+ ( buffer[ 3 ] & (1<<6) ) == 0 && // X pushed
+ ( buffer [ 3 ] & (1<<4) ) == 0 // triangle pushed
+ )
+ {
+ buttonsPushed = true;
+ break;
+ }
+ }
+ }
+ if ( buttonsPushed ) break;
+ }
+
+ return buttonsPushed;
+}
+
+void PS2Platform::OnControllerError(const char *msg)
+{
+ bool inFrame = p3d::context->InFrame();
+
+ if ( inFrame ) p3d::context->EndFrame( true );
+ DisplaySplashScreen( Error, msg, 0.7f, 0.0f, 0.0f, tColour(255, 255, 255), 0 );
+ if ( inFrame ) p3d::context->BeginFrame( );
+ mErrorState = CTL_ERROR;
+ mPauseForError = true;
+
+ if ( GetPresentationManager()->GetFMVPlayer()->IsPlaying() )
+ {
+ GetPresentationManager()->GetFMVPlayer()->Pause( );
+ }
+ else
+ {
+ GetSoundManager()->StopForMovie();
+ }
+}
+
+//=============================================================================
+// PS2Platform::OnDriveError
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( radFileError error, const char* pDriveName, void* pUserData )
+//
+// Return: bool
+//
+//=============================================================================
+bool PS2Platform::OnDriveError( radFileError error, const char* pDriveName, void* pUserData )
+{
+ bool inFrame = p3d::context->InFrame();
+
+ const int NUM_RADFILE_ERRORS = 13;
+ unsigned int errorIndex = error;
+
+#ifdef PAL
+ switch( CGuiTextBible::GetCurrentLanguage() )
+ {
+ case Scrooby::XL_FRENCH:
+ {
+ errorIndex += 1 * NUM_RADFILE_ERRORS;
+
+ break;
+ }
+ case Scrooby::XL_GERMAN:
+ {
+ errorIndex += 2 * NUM_RADFILE_ERRORS;
+
+ break;
+ }
+ case Scrooby::XL_SPANISH:
+ {
+ errorIndex += 3 * NUM_RADFILE_ERRORS;
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+#endif // PAL
+
+ rAssert( errorIndex < sizeof( ERROR_STRINGS ) / sizeof( ERROR_STRINGS[ 0 ] ) );
+
+ switch ( error )
+ {
+ case Success:
+ {
+ if ( mErrorState != NONE )
+ {
+ if ( inFrame ) p3d::context->EndFrame( true );
+ DisplaySplashScreen( FadeToBlack );
+ if ( inFrame ) p3d::context->BeginFrame( );
+ mErrorState = NONE;
+ mPauseForError = false;
+ }
+
+ if ( GetPresentationManager()->GetFMVPlayer()->IsPlaying() )
+ {
+ GetPresentationManager()->GetFMVPlayer()->UnPause( );
+ }
+ else
+ {
+ GetSoundManager()->ResumeAfterMovie();
+ }
+ return true;
+ break;
+ }
+ case FileNotFound:
+ {
+ if ( CommandLineOptions::Get( CLO_FILE_NOT_FOUND ) )
+ {
+ rAssert( pUserData != NULL );
+
+ radFileRequest* request = static_cast<radFileRequest*>( pUserData );
+ const char* fileName = request->GetFilename();
+
+ //Get rid of the slashes.
+ unsigned int i;
+ unsigned int lastIndex = 0;
+ for ( i = 0; i < strlen( fileName ); ++i )
+ {
+ if ( fileName[ i ] == '\\' )
+ {
+ lastIndex = i;
+ }
+ }
+
+ unsigned int adjustedIndex = lastIndex == 0 ? lastIndex : lastIndex + 1;
+
+ char adjustedName[32];
+ strncpy( adjustedName, &fileName[adjustedIndex], ( strlen( fileName ) - lastIndex ) );
+ adjustedName[ strlen( fileName ) - lastIndex ] = '\0';
+
+ char errorString[256];
+ sprintf( errorString, "%s:\n%s", ERROR_STRINGS[errorIndex], adjustedName );
+ if ( inFrame ) p3d::context->EndFrame( true );
+ DisplaySplashScreen( Error, errorString, 1.0f, 0.0f, 0.0f, tColour(255, 255, 255), 0 );
+ if ( inFrame ) p3d::context->BeginFrame( );
+ mErrorState = P_ERROR;
+ mPauseForError = true;
+
+ if ( GetPresentationManager()->GetFMVPlayer()->IsPlaying() )
+ {
+ GetPresentationManager()->GetFMVPlayer()->Pause( );
+ }
+ else
+ {
+ GetSoundManager()->StopForMovie();
+ }
+ return true;
+ }
+ else
+ {
+ //Hmmm... This could be a hack.
+ error = WrongMedia;
+ //Fall through.
+ }
+ }
+ case ShellOpen:
+ case WrongMedia:
+ case NoMedia:
+ case HardwareFailure:
+ {
+ //This could be the wrong disc.
+ if ( inFrame ) p3d::context->EndFrame( true );
+ DisplaySplashScreen( Error, ERROR_STRINGS[errorIndex], 1.0f, 0.0f, 0.0f, tColour(255, 255, 255), 0 );
+ if ( inFrame ) p3d::context->BeginFrame( );
+ mErrorState = P_ERROR;
+ mPauseForError = true;
+
+ if ( GetPresentationManager()->GetFMVPlayer()->IsPlaying() )
+ {
+ GetPresentationManager()->GetFMVPlayer()->Pause( );
+ }
+ else
+ {
+ GetSoundManager()->StopForMovie();
+ }
+ return true;
+ }
+ default:
+ {
+ //Others are not supported.
+ rAssert( false );
+ }
+ }
+
+ return false;
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// PS2Platform::PS2Platform
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PS2Platform::PS2Platform() :
+ mpPlatform( NULL ),
+ mpContext( NULL )
+{
+}
+
+
+//==============================================================================
+// PS2Platform::~PS2Platform
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PS2Platform::~PS2Platform()
+{
+}
+
+
+//==============================================================================
+// PS2Platform::EnableSnProfiler
+//==============================================================================
+// Description: Prepare the SN function level profiler.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void PS2Platform::EnableSnProfiler()
+{
+#ifndef RAD_RELEASE
+#ifndef RAD_MW
+
+ //
+ // Quadword aligned, can be 2K to 64K bytes
+ //
+ static u_long128 profdata[2048];
+
+ snDebugInit();
+
+ sceSifInitRpc( 0 );
+
+ //
+ // Load the SNProfile module
+ //
+ if( sceSifLoadModule( "host0:/usr/local/sce/iop/modules/SNProfil.irx", 0, NULL ) < 0 )
+ {
+ rDebugString( "Can't load SNProfil.IRX module\n" );
+ exit( -1 );
+ }
+
+ //
+ // Initialize the profiler
+ //
+ if( snProfInit( _4KHZ, profdata, sizeof(profdata) ) != 0 )
+ {
+ //
+ // See SN_PRF... in LIBSN.H
+ //
+ rDebugString( "SN Profiler init failed\n" );
+ }
+
+ snProfSetFlagValue( 1 );
+
+#endif
+#endif
+}
+
+#ifndef FINAL
+
+//
+// Exception-handling functions. These functions are set to be called
+// whenever the PS2 crashes on a variety of conditions.
+//
+
+void PS2Platform::handleTLBChange( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers )
+{
+ dumpExceptionData( "TLB Change Error", stat, cause, epc, bva, bpa, registers );
+}
+
+void PS2Platform::handleTLBLoadMismatch( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers )
+{
+ dumpExceptionData( "TLB Load Error", stat, cause, epc, bva, bpa, registers );
+}
+
+void PS2Platform::handleTLBStoreMismatch( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers )
+{
+ dumpExceptionData( "TLB Store Error", stat, cause, epc, bva, bpa, registers );
+}
+
+void PS2Platform::handleAddressLoadError( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers )
+{
+ dumpExceptionData( "Address Load Error", stat, cause, epc, bva, bpa, registers );
+}
+
+void PS2Platform::handleAddressStoreError( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers )
+{
+ dumpExceptionData( "Address Store Error", stat, cause, epc, bva, bpa, registers );
+}
+
+void PS2Platform::handleBusFetchError( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers )
+{
+ dumpExceptionData( "Bus Fetch Error", stat, cause, epc, bva, bpa, registers );
+}
+
+void PS2Platform::handleBusDataError( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers )
+{
+ dumpExceptionData( "Bus Data Error", stat, cause, epc, bva, bpa, registers );
+}
+
+void PS2Platform::handleReservedInstruction( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers )
+{
+ dumpExceptionData( "Reserved Instruction Error", stat, cause, epc, bva, bpa, registers );
+}
+
+void PS2Platform::handleCoprocessor( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers )
+{
+ dumpExceptionData( "Coprocessor Error", stat, cause, epc, bva, bpa, registers );
+}
+
+void PS2Platform::handleOverflow( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers )
+{
+ dumpExceptionData( "Overflow Error", stat, cause, epc, bva, bpa, registers );
+}
+
+void PS2Platform::handleTrap( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers )
+{
+ dumpExceptionData( "Trap Error", stat, cause, epc, bva, bpa, registers );
+}
+
+//=============================================================================
+// PS2Platform::dumpExceptionData
+//=============================================================================
+// Description: Dump the parameters from an exception handler to the screen
+//
+// Parameters: exceptionName - string describing the exception that was set
+// stat - status register
+// cause - cause register? (I should look this up)
+// epc - program counter
+// bva - address involved in bad access or branch
+// bpa - address involved in bus error
+// registers - array of MIPS general-purpose registers
+//
+// Return: Why?
+//
+//==============================================================================
+void PS2Platform::dumpExceptionData( const char* exceptionName,
+ unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers )
+{
+ IRadTextDisplay* textDisplay;
+ char buffer[50];
+ int i;
+ unsigned int reg1, reg2;
+
+#ifdef RAD_PS2
+ //
+ // Need to shut down the MFIFO for this to work properly
+ //
+ ((pddiExtPS2Control*)p3d::pddi->GetExtension(PDDI_EXT_PS2_CONTROL))->MFIFOEnable( false );
+#endif
+
+ ::radTextDisplayGet( &textDisplay, GMA_DEFAULT );
+
+ textDisplay->SetBackgroundColor( 0 );
+ textDisplay->SetTextColor( 0xffffffff );
+ textDisplay->Clear();
+ textDisplay->TextOutAt( exceptionName, 15, 1 );
+ sprintf( buffer, "PC: 0x%x Address Value: 0x%x", epc, bva );
+ textDisplay->TextOutAt( buffer, 15, 3 );
+ sprintf( buffer, "stat: 0x%x cause: 0x%x", stat, cause );
+ textDisplay->TextOutAt( buffer, 15, 5 );
+ sprintf( buffer, "Bus error address: 0x%x", bpa );
+ textDisplay->TextOutAt( buffer, 15, 7 );
+
+ //
+ // GPR printout
+ //
+ for( i = 0; i < 16; i++ )
+ {
+ reg1 = (unsigned int)registers[i] & 0xffffffff;
+ reg2 = (unsigned int)registers[i+16] & 0xffffffff;
+ sprintf( buffer, "GPR%02d: 0x%08x GPR%02d: 0x%08x", i, reg1, i+16, reg2 );
+ textDisplay->TextOutAt( buffer, 15, 9+i );
+ }
+
+ textDisplay->TextOutAt( ":-(", 6, 13 );
+
+ if ( CommandLineOptions::Get( CLO_DEMO_TEST ) ||
+ GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_DEMO_TEST ) )
+ {
+ char buffy[32];
+ sprintf( buffy, "Demo Count: %d", GetGame()->GetDemoCount() );
+ textDisplay->TextOutAt( buffy, 6, 15 );
+
+ unsigned int time = GetGame()->GetTime();
+ unsigned int hours = time / 3600000;
+ unsigned int deltaTime = time % 3600000;
+
+ unsigned int minutes = deltaTime / 60000;
+ deltaTime = deltaTime % 60000;
+
+ unsigned int seconds = deltaTime / 1000;
+ deltaTime = deltaTime % 1000;
+ sprintf( buffy, "Time: %d:%d:%d.%d", hours, minutes, seconds, deltaTime );
+ textDisplay->TextOutAt( buffy, 6, 17 );
+
+ if ( GetGameplayManager() )
+ {
+ sprintf( buffy, "Level %d", GetGameplayManager()->GetCurrentLevelIndex() );
+ textDisplay->TextOutAt( buffy, 6, 19 );
+ }
+ }
+
+ textDisplay->SwapBuffers();
+ textDisplay->Release();
+
+ rReleaseBreak();
+}
+
+void Simpsons2MFIFODisable()
+{
+#ifdef RAD_PS2
+ ((pddiExtPS2Control*)p3d::pddi->GetExtension(PDDI_EXT_PS2_CONTROL))->MFIFOEnable( false );
+#endif
+}
+
+#endif
diff --git a/game/code/main/ps2platform.h b/game/code/main/ps2platform.h
new file mode 100644
index 0000000..5b5df10
--- /dev/null
+++ b/game/code/main/ps2platform.h
@@ -0,0 +1,207 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ps2platform.h
+//
+// Description: Abstracts the differences for setting up and shutting down
+// the different platforms.
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef PS2PLATFORM_H
+#define PS2PLATFORM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <main/platform.h> // base class
+
+//========================================
+// Forward References
+//========================================
+struct IRadCementLibrary;
+struct IRadMemoryHeap;
+class tPlatform;
+class tContext;
+
+//
+// Global function for disabling MFIFO, which we do to use radTextDisplay.
+// I'm making it global because this also gets used in the heart of radSound,
+// and I don't want to drag P3D dependencies in there. It's icky, and I'm
+// going to ask for a fix to radTextDisplay so we don't need to do this anymore.
+//
+void Simpsons2MFIFODisable();
+
+//=============================================================================
+//
+// Synopsis: Provides abstraction for setting up and closing down the PS2.
+//
+//=============================================================================
+class PS2Platform : public Platform
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static PS2Platform* CreateInstance();
+ static PS2Platform* GetInstance();
+ static void DestroyInstance();
+
+ // Had to workaround our nice clean design cause FTech must be init'ed
+ // before anything else is done.
+ static void InitializeFoundation();
+ static void InitializeMemory();
+
+ // Implement Platform interface.
+ virtual void InitializePlatform();
+ virtual void ShutdownPlatform();
+
+ virtual void LaunchDashboard();
+ virtual void ResetMachine();
+ virtual void DisplaySplashScreen( SplashScreen screenID,
+ const char* overlayText = NULL,
+ float fontScale = 1.0f,
+ float textPosX = 0.0f,
+ float textPosY = 0.0f,
+ tColour textColour = tColour( 255,255,255 ),
+ int fadeFrames = 3 );
+
+ virtual void DisplaySplashScreen( const char* textureName,
+ const char* overlayText = NULL,
+ float fontScale = 1.0f,
+ float textPosX = 0.0f,
+ float textPosY = 0.0f,
+ tColour textColour = tColour( 255,255,255 ),
+ int fadeFrames = 3 );
+
+ virtual bool OnDriveError( radFileError error, const char* pDriveName, void* pUserData );
+ virtual void OnControllerError(const char *msg);
+
+ void SetProgressiveMode( bool progressiveScan );
+ bool GetProgressiveMode() { return mProgressiveMode; }
+
+ bool CheckForStartupButtons( void );
+
+ protected:
+
+ virtual void InitializeFoundationDrive();
+ virtual void ShutdownFoundation();
+
+ virtual void InitializePure3D();
+ virtual void ShutdownPure3D();
+
+ private:
+
+ // Constructors, Destructors, and Operators
+ PS2Platform();
+ virtual ~PS2Platform();
+
+ // Unused Constructors, Destructors, and Operators
+ PS2Platform( const PS2Platform& aPlatform );
+ PS2Platform& operator=( const PS2Platform& aPlatform );
+
+ // PS2 specific methods
+ void EnableSnProfiler();
+
+ // Crash printout routines
+#ifndef FINAL
+ static void dumpExceptionData( const char* exceptionName,
+ unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers );
+
+ static void handleTLBChange( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers );
+
+ static void handleTLBLoadMismatch( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers );
+
+ static void handleTLBStoreMismatch( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers );
+
+ static void handleAddressLoadError( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers );
+
+ static void handleAddressStoreError( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers );
+
+ static void handleBusFetchError( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers );
+
+ static void handleBusDataError( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers );
+
+ static void handleReservedInstruction( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers );
+
+ static void handleCoprocessor( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers );
+
+ static void handleOverflow( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers );
+
+ static void handleTrap( unsigned int stat,
+ unsigned int cause,
+ unsigned int epc,
+ unsigned int bva,
+ unsigned int bpa,
+ u_long128* registers );
+#endif
+
+ // Pointer to the one and only instance of this singleton.
+ static PS2Platform* spInstance;
+
+ // Pure 3D attributes
+ tPlatform* mpPlatform;
+ tContext* mpContext;
+
+ bool mProgressiveMode;
+
+ static IRadCementLibrary* s_MainCement;
+};
+
+#endif // PS2PLATFORM_H
diff --git a/game/code/main/singletons.cpp b/game/code/main/singletons.cpp
new file mode 100644
index 0000000..ce6cb86
--- /dev/null
+++ b/game/code/main/singletons.cpp
@@ -0,0 +1,333 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: singletons.cpp
+//
+// Description: Create and destroy all our singletons here.
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <main/singletons.h>
+
+#include <ai/actionbuttonmanager.h>
+#include <atc/atcmanager.h>
+#include <camera/supercammanager.h>
+#include <cards/cardgallery.h>
+#include <cheats/cheatinputsystem.h>
+#include <data/gamedatamanager.h>
+#include <console/console.h>
+#include <debug/debuginfo.h>
+#include <debug/profiler.h>
+#include <events/eventmanager.h>
+#include <input/inputmanager.h>
+#include <interiors/interiormanager.h>
+#include <loading/loadingmanager.h>
+#include <main/commandlineoptions.h>
+#include <meta/triggervolumetracker.h>
+#include <mission/missionmanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/rewards/rewardsmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/missionscriptloader.h>
+#include <worldsim/Skidmarks/skidmarkmanager.h>
+#include <presentation/mouthflapper.h>
+#include <presentation/presentation.h>
+#include <presentation/tutorialmanager.h>
+#include <presentation/gui/guisystem.h>
+#include <render/RenderFlow/renderflow.h>
+#include <sound/soundmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/coins/sparkle.h>
+#include <worldsim/hitnrunmanager.h>
+#include <ai/actor/actormanager.h>
+#include <data/persistentworldmanager.h>
+#include <worldsim/character/footprint/footprintmanager.h>
+#include <memory/propstats.h>
+#include <simcommon/simenvironment.hpp>
+
+#ifdef RAD_WIN32
+#include <data/config/gameconfigmanager.h>
+#include <input/MouseCursor.h>
+#endif
+
+//=============================================================================
+// Function: void AddVariablesToWatcher
+//=============================================================================
+//
+// Description: Adds lots of global variables to the watcher
+//
+// Parameters: None
+//
+// Returns: None
+//
+//=============================================================================
+#ifdef DEBUGWATCH
+void AddVariablesToWatcher()
+{
+ MouthFlapper::AddVariablesToWatcher();
+}
+#endif
+
+//=============================================================================
+// Function: CreateSingletons
+//=============================================================================
+//
+// Description: Construct all the singletons
+//
+// Parameters: None
+//
+// Returns: None
+//
+//=============================================================================
+void CreateSingletons()
+{
+ //CREATE_MEMORYTRACKER();
+
+ CREATE_DEBUGINFO();
+
+ MEMTRACK_PUSH_GROUP( "Singletons" );
+
+ GameDataManager* pGameDataManager = GameDataManager::CreateInstance();
+ rAssert( pGameDataManager != NULL );
+
+#ifdef RAD_WIN32
+ GameConfigManager* pGameConfigManager = GameConfigManager::CreateInstance();
+ rAssert( pGameConfigManager != NULL );
+#endif
+
+ EventManager* pEventManager = EventManager::CreateInstance();
+ rAssert( pEventManager != NULL );
+
+ LoadingManager* pLoadingManager = LoadingManager::CreateInstance();
+ rAssert( pLoadingManager != NULL );
+
+ InputManager* pInputManager = InputManager::CreateInstance();
+ rAssert( pInputManager != NULL );
+
+ SkidmarkManager* pSkidmarkManager = SkidmarkManager::CreateInstance();
+ rAssert( pSkidmarkManager != NULL );
+
+ //AttributeTableChunk Manager, has the the info of physprops in a table
+ ATCManager * pATCManager =ATCManager::CreateInstance();
+ rAssert(pATCManager != NULL);
+
+ CardGallery* pCardCallery = CardGallery::CreateInstance();
+ rAssert( pCardCallery != NULL );
+
+ //CharacterSheetManager
+ CharacterSheetManager* pCharacterSheetManager = CharacterSheetManager::CreateInstance();
+ rAssert(pCharacterSheetManager != NULL);
+
+ //RewardsManager
+ RewardsManager* pRewardsManager = RewardsManager::CreateInstance();
+ rAssert(pRewardsManager);
+
+ MEMTRACK_PUSH_GROUP( "Console" );
+
+ // must be around before VehicleCentral tries to tie in some shit
+ // ...MissionScriptLoader too
+ Console* pConsole = Console::CreateInstance();
+ rAssert( pConsole != NULL );
+
+ MEMTRACK_POP_GROUP( "Console" );
+
+
+
+ // must be done before init'ing worldphysicsmanager
+ VehicleCentral* pVC = VehicleCentral::CreateInstance();
+ rAssert(pVC != 0);
+
+ sim::SimUnits::Initialize();
+ WorldPhysicsManager* pWPM = WorldPhysicsManager::CreateInstance();
+ rAssert(pWPM != 0);
+
+ CREATE_PROFILER();
+
+ PresentationManager* pPM = PresentationManager::CreateInstance();
+ rAssert( pPM != NULL );
+
+ CGuiSystem* pGuiSystem = CGuiSystem::CreateInstance();
+ rAssert( pGuiSystem != NULL );
+
+ SoundManager* pSoundManager =
+ SoundManager::CreateInstance( CommandLineOptions::Get( CLO_MUTE ),
+ CommandLineOptions::Get( CLO_NO_MUSIC ),
+ CommandLineOptions::Get( CLO_NO_EFFECTS ),
+ CommandLineOptions::Get( CLO_NO_DIALOG ) );
+ rAssert( pSoundManager != NULL );
+
+ MissionManager* pMM = MissionManager::CreateInstance();
+ rAssert( pMM != NULL );
+
+ /*
+ HeadToHeadManager* pH2HM = HeadToHeadManager::CreateInstance();
+ rAssert( pH2HM != NULL );
+ */
+
+ MissionScriptLoader* pMSL = MissionScriptLoader::CreateInstance();
+ rAssert( pMSL != NULL );
+
+ CharacterManager* pCM = CharacterManager::CreateInstance();
+ rAssert( pCM != NULL );
+
+ AvatarManager* pAM = AvatarManager::CreateInstance();
+ rAssert( pAM != (AvatarManager*)0 );
+
+ ActionButtonManager* pABM = ActionButtonManager::CreateInstance();
+ rAssert( pABM != (ActionButtonManager*)0 );
+
+ SuperCamManager* pSCM = SuperCamManager::CreateInstance();
+ rAssert( pSCM != NULL );
+
+ TriggerVolumeTracker::CreateInstance();
+
+ InteriorManager* pInteriorManager = InteriorManager::CreateInstance();
+ rAssert( pInteriorManager != NULL );
+
+ CheatInputSystem* pCheatInputSystem = CheatInputSystem::CreateInstance();
+ rAssert( pCheatInputSystem != NULL );
+
+ TutorialManager* pTutorialManager = TutorialManager::CreateInstance();
+ rAssert( pTutorialManager != NULL );
+
+ ActorManager* pActorManager = ActorManager::CreateInstance();
+ rAssert( pActorManager != NULL );
+
+ PersistentWorldManager* pPWManager = PersistentWorldManager::CreateInstance();
+ rAssert( pPWManager );
+
+ FootprintManager* pFootprintManager = FootprintManager::CreateInstance();
+ rAssert( pFootprintManager != NULL );
+
+ CoinManager* pCoinManager = CoinManager::CreateInstance();
+ rAssert( pCoinManager );
+ Sparkle* pSparkle = Sparkle::CreateInstance();
+ rAssert( pSparkle );
+
+ HitnRunManager* pHitnRunManager = HitnRunManager::CreateInstance();
+ rAssert( pHitnRunManager );
+
+ //
+ // Create The RenderFlow Instance; this creates the RenderManager.
+ // Create it under singletons.cpp because of co-dependency between singletons.
+ //
+ RenderFlow* pRenderFlow = RenderFlow::CreateInstance();
+ rAssert( pRenderFlow );
+
+
+#ifndef RAD_RELEASE
+ if( CommandLineOptions::Get( CLO_PROP_STATS ) )
+ {
+ PropStats::EnableTracking();
+ }
+#endif
+
+ MEMTRACK_POP_GROUP("Singletons");
+
+ AddVariablesToWatcher();
+}
+
+
+//=============================================================================
+// Function: DestroySingletons
+//=============================================================================
+//
+// Description: Destroy all the singletons
+//
+// Parameters: None
+//
+// Returns: None
+//
+//=============================================================================
+void DestroySingletons()
+{
+ InteriorManager::DestroyInstance();
+
+ ActionButtonManager::DestroyInstance();
+
+ SuperCamManager::DestroyInstance();
+
+ AvatarManager::DestroyInstance();
+
+ CharacterManager::DestroyInstance();
+
+ MissionScriptLoader::DestroyInstance();
+
+ //HeadToHeadManager::DestroyInstance();
+
+ MissionManager::DestroyInstance();
+
+ SoundManager::DestroyInstance();
+
+ CGuiSystem::DestroyInstance();
+
+ PresentationManager::DestroyInstance();
+
+ WorldPhysicsManager::DestroyInstance();
+
+ VehicleCentral::DestroyInstance();
+
+ Console::DestroyInstance();
+
+ ATCManager::DestroyInstance();
+
+ CharacterSheetManager::DestroyInstance();
+
+ RewardsManager::DestroyInstance();
+
+ CardGallery::DestroyInstance();
+
+ TutorialManager::DestroyInstance();
+
+ CoinManager::DestroyInstance();
+
+ Sparkle::DestroyInstance();
+
+ CheatInputSystem::DestroyInstance();
+
+ HitnRunManager::DestroyInstance();
+
+ InputManager::DestroyInstance();
+
+ LoadingManager::DestroyInstance();
+
+ SkidmarkManager::DestroyInstance();
+
+ FootprintManager::DestroyInstance();
+
+ ActorManager::DestroyInstance();
+
+ PersistentWorldManager::DestroyInstance();
+
+ TriggerVolumeTracker::DestroyInstance();
+
+ RenderFlow::DestroyInstance();
+
+ GameDataManager::DestroyInstance();
+
+ EventManager::DestroyInstance();
+
+#ifdef RAD_WIN32
+ GameConfigManager::DestroyInstance();
+#endif
+
+ DESTROY_PROFILER();
+
+ DESTROY_DEBUGINFO();
+
+ //DESTROY_MEMORYTRACKER();
+}
diff --git a/game/code/main/singletons.h b/game/code/main/singletons.h
new file mode 100644
index 0000000..80b915c
--- /dev/null
+++ b/game/code/main/singletons.h
@@ -0,0 +1,35 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: singletons.h
+//
+// Description: Define all our global objects here
+//
+// History: + Created -- Darwin Chau
+//
+//=============================================================================
+#ifndef SINGLETONS_H_
+#define SINGLETONS_H_
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+#ifdef DEBUGWATCH
+ void AddVariablesToWatcher();
+#else
+ inline void AddVariablesToWatcher(){};
+#endif //DEBUGWATCH
+void CreateSingletons();
+void DestroySingletons();
+
+//========================================
+// Constants, Typedefs and Statics
+//========================================
+#endif \ No newline at end of file
diff --git a/game/code/main/tuidunaligned.cpp b/game/code/main/tuidunaligned.cpp
new file mode 100644
index 0000000..e9fdb78
--- /dev/null
+++ b/game/code/main/tuidunaligned.cpp
@@ -0,0 +1,266 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: tuidunaligned.cpp
+//
+// Description: tuids won't cause your class to get overly padded
+//
+// History:
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <main/tuidunaligned.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// tUidUnaligned::tUidUnaligned
+//==============================================================================
+// Description: constructor
+//
+// Parameters: none
+//
+// Return: none
+//
+// Constraints: none
+//
+//==============================================================================
+tUidUnaligned::tUidUnaligned():
+ u0( 0 ),
+ u1( 1 )
+{
+}
+
+//==============================================================================
+// tUidUnaligned::tUidUnaligned
+//==============================================================================
+// Description: copy constructor
+//
+// Parameters: right - the object we're copying from
+//
+// Return: none
+//
+// Constraints: none
+//
+//==============================================================================
+tUidUnaligned::tUidUnaligned( const tUidUnaligned& right ):
+ u0( right.u0 ),
+ u1( right.u1 )
+{
+}
+
+//==============================================================================
+// tUidUnaligned::tUidUnaligned
+//==============================================================================
+// Description: conversion constructor from 64 bit integers - these are meant
+// to be interchangable
+//
+// Parameters: right - the object we're copying from
+//
+// Return: none
+//
+// Constraints: none
+//
+//==============================================================================
+//tUidUnaligned::tUidUnaligned( const radInt64 right )
+//{
+// const unsigned int* r = reinterpret_cast< const unsigned int* >( &right );
+// u0 = r[ 0 ];
+// u1 = r[ 1 ];
+//}
+
+//=============================================================================
+// tUidUnaligned::operator radInt64
+//=============================================================================
+// Description: conversion operator to 64 bit ints
+//
+// Parameters: none
+//
+// Return: none
+//
+// Constraints: none
+//
+//=============================================================================
+//tUidUnaligned::operator radInt64()
+//{
+// radInt64 returnMe;
+// unsigned int* r = reinterpret_cast< unsigned int* >( &returnMe );
+// r[ 0 ] = u0;
+// r[ 1 ] = u1;
+// return returnMe;
+//}
+
+//=============================================================================
+// tUidUnaligned::operator !=
+//=============================================================================
+// Description: inequality operator
+//
+// Parameters: right - are we equal to this?
+//
+// Return: none
+//
+// Constraints: none
+//
+//=============================================================================
+bool tUidUnaligned::operator !=( const tUidUnaligned right ) const
+{
+ return ! operator==( right );
+}
+
+//=============================================================================
+// tUidUnaligned::operator !=
+//=============================================================================
+// Description: equality operator
+//
+// Parameters: right - are we equal to this?
+//
+// Return: none
+//
+// Constraints: none
+//
+//=============================================================================
+bool tUidUnaligned::operator ==( const tUidUnaligned right ) const
+{
+ bool returnMe = ( ( u0 == right.u0 ) && ( u1 == right.u1 ) );
+ return returnMe;
+}
+
+//=============================================================================
+// tUidUnaligned::operator <
+//=============================================================================
+// Description: less than operator
+//
+// Parameters: right - are we less than this?
+//
+// Return: none
+//
+// Constraints: none
+//
+//=============================================================================
+bool tUidUnaligned::operator <( const tUidUnaligned right ) const
+{
+ radInt64 thisOne;
+ unsigned int* t = reinterpret_cast< unsigned int* >( &thisOne );
+ t[ 0 ] = u0;
+ t[ 1 ] = u1;
+ radInt64 rightOne;
+ unsigned int* r = reinterpret_cast< unsigned int* >( &rightOne );
+ r[ 0 ] = right.u0;
+ r[ 1 ] = right.u1;
+ return thisOne < rightOne;
+}
+
+//=============================================================================
+// tUidUnaligned::operator =
+//=============================================================================
+// Description: assignment operator
+//
+// Parameters: assign the two objects
+//
+// Return: none
+//
+// Constraints: none
+//
+//=============================================================================
+//tUidUnaligned& tUidUnaligned::operator=( const tUidUnaligned& right )
+//{
+//}
+
+//=============================================================================
+// tUidUnaligned::operator^
+//=============================================================================
+// Description: less than operator
+//
+// Parameters: right - are we less than this?
+//
+// Return: none
+//
+// Constraints: none
+//
+//=============================================================================
+tUidUnaligned tUidUnaligned::operator^( const tUidUnaligned right ) const
+{
+ radInt64 thisOne;
+ unsigned int* t = reinterpret_cast< unsigned int* >( &thisOne );
+ t[ 0 ] = u0;
+ t[ 1 ] = u1;
+ radInt64 rightOne;
+ unsigned int* r = reinterpret_cast< unsigned int* >( &rightOne );
+ r[ 0 ] = right.u0;
+ r[ 1 ] = right.u1;
+ return thisOne ^ rightOne;
+}
+
+//=============================================================================
+// tUidUnaligned::operator *=
+//=============================================================================
+// Description: in place multiplication
+//
+// Parameters: right 8 what are we multiplying by
+//
+// Return: none
+//
+// Constraints: none
+//
+//=============================================================================
+tUidUnaligned tUidUnaligned::operator*=( const radInt64 right )
+{
+ radInt64 thisOne;
+ unsigned int* t = reinterpret_cast< unsigned int* >( &thisOne );
+ t[ 0 ] = u0;
+ t[ 1 ] = u1;
+ thisOne *= right;
+ ( *this ) = thisOne;
+ return *this;
+}
+
+//=============================================================================
+// tUidUnaligned::operator &
+//=============================================================================
+// Description: and operator
+//
+// Parameters: right - what are we anding with
+//
+// Return: none
+//
+// Constraints: none
+//
+//=============================================================================
+tUidUnaligned tUidUnaligned::operator&( const tUidUnaligned right ) const
+{
+ unsigned int r0 = u0 & right.u0;
+ unsigned int r1 = u1 & right.u1;
+ tUidUnaligned returnMe;
+ returnMe.u0 = r0;
+ returnMe.u1 = r1;
+ return returnMe;
+}
+
+
+tUidUnaligned tUidUnaligned::operator >>( const int bits ) const
+{
+ radInt64 returnMe;
+ unsigned int* t = reinterpret_cast< unsigned int* >( &returnMe );
+ t[ 0 ] = u0;
+ t[ 1 ] = u1;
+ returnMe = returnMe >> bits;
+ return returnMe;
+}
diff --git a/game/code/main/tuidunaligned.h b/game/code/main/tuidunaligned.h
new file mode 100644
index 0000000..ea39437
--- /dev/null
+++ b/game/code/main/tuidunaligned.h
@@ -0,0 +1,79 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: game.h
+//
+// Description: The game loop
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef TUIDUNALIGNED_H
+#define TUIDUNALIGNED_H
+
+//========================================
+// Forward References
+//========================================
+
+//
+// This is a 64 bit number
+//
+#ifdef RAD_PS2
+ typedef unsigned long radInt64;
+#elif defined( RAD_WIN32 ) || defined ( RAD_XBOX )
+ typedef unsigned _int64 radInt64;
+#elif defined(RAD_GAMECUBE)
+ typedef unsigned long long radInt64;
+#else
+ #error 'FTech requires definition of RAD_GAMECUBE, RAD_PS2, RAD_XBOX, or RAD_WIN32'
+#endif
+
+
+//=============================================================================
+//
+// Synopsis: tUidUnaligned
+//
+//=============================================================================
+class tUidUnaligned
+{
+public:
+ tUidUnaligned();
+ tUidUnaligned( const tUidUnaligned& right );
+ tUidUnaligned( const radInt64 right )
+ {
+ const unsigned int* r = reinterpret_cast< const unsigned int* >( &right );
+ u0 = r[ 0 ];
+ u1 = r[ 1 ];
+ }
+ operator radInt64()
+ {
+ radInt64 returnMe;
+ unsigned int* r = reinterpret_cast< unsigned int* >( &returnMe );
+ r[ 0 ] = u0;
+ r[ 1 ] = u1;
+ return returnMe;
+ }
+
+ bool operator !=( const tUidUnaligned right ) const;
+ bool operator ==( const tUidUnaligned right ) const;
+ bool operator <( const tUidUnaligned right ) const;
+ tUidUnaligned& operator=( const tUidUnaligned& right )
+ {
+ u0 = right.u0;
+ u1 = right.u1;
+ return *this;
+ }
+ tUidUnaligned operator ^( const tUidUnaligned right ) const;
+ tUidUnaligned operator &( const tUidUnaligned right ) const;
+ tUidUnaligned operator *=( const radInt64 right );
+ tUidUnaligned operator >>( const int bits ) const;
+
+protected:
+private:
+ unsigned int u0;
+ unsigned int u1;
+};
+
+#endif // TUIDUNALIGNED_H
+
diff --git a/game/code/main/win32main.cpp b/game/code/main/win32main.cpp
new file mode 100644
index 0000000..6c7ac62
--- /dev/null
+++ b/game/code/main/win32main.cpp
@@ -0,0 +1,237 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: xboxmain.cpp
+//
+// Description: This file contains the main enrty point to the game.
+//
+// History: + Based on xbox main and winmain from squidney
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Standard Library
+#include <string.h>
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radobject.hpp>
+// file reading before radtech
+#include <stdio.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <main/game.h>
+#include <main/win32platform.h>
+#include <main/singletons.h>
+#include <main/commandlineoptions.h>
+#include <memory/memoryutilities.h>
+#include <memory/srrmemory.h>
+
+//========================================
+// Forward Declarations
+//========================================
+static void ProcessCommandLineArguments( LPSTR lpCmdLine );
+
+static void ProcessCommandLineArgumentsFromFile();
+
+
+//=============================================================================
+// Function: WinMain
+//=============================================================================
+//
+// Description: Main Windows entry point.
+//
+// Parameters: win32 parameters
+//
+// Returns: win32 return.
+//
+//=============================================================================
+int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
+{
+ //
+ // Pick out and store command line settings.
+ //
+ CommandLineOptions::InitDefaults();
+ ProcessCommandLineArguments( lpCmdLine );
+ ProcessCommandLineArgumentsFromFile();
+
+ //
+ // Have to get FTech setup first so that we can use all the memory services.
+ // The initialize window call will fail if another Simpsons window exists. In
+ // this case, we exit.
+ //
+ if( !Win32Platform::InitializeWindow( hInstance ) )
+ {
+ return 0;
+ }
+ Win32Platform::InitializeFoundation();
+
+ srand (Game::GetRandomSeed ());
+
+
+ // Now disable the default heap
+ //
+#ifndef RAD_RELEASE
+ tName::SetAllocator (GMA_DEBUG);
+
+ //g_HeapActivityTracker.EnableHeapAllocs (GMA_DEFAULT, false);
+ //g_HeapActivityTracker.EnableHeapFrees (GMA_DEFAULT, false);
+#endif
+
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ //
+ // Instantiate all the singletons before doing anything else.
+ //
+ CreateSingletons();
+
+ //
+ // Construct the platform object.
+ //
+ Win32Platform* pPlatform = Win32Platform::CreateInstance( hInstance, hPrevInstance, lpCmdLine, nCmdShow );
+ rAssert( pPlatform != NULL );
+
+ //
+ // Create the game object.
+ //
+ Game* pGame = Game::CreateInstance( pPlatform );
+ rAssert( pGame != NULL );
+
+
+ //
+ // Initialize the game.
+ //
+ pGame->Initialize();
+
+ HeapMgr()->PopHeap (GMA_PERSISTENT);
+
+ //
+ // Run it! Control will not return from here until the game is stopped.
+ //
+ pGame->Run();
+
+ //
+ // Terminate the game (this frees all resources allocated by the game).
+ //
+ pGame->Terminate();
+
+ //
+ // Dump all the singletons.
+ //
+ DestroySingletons();
+
+ //
+ // Destroy the game object.
+ //
+ Game::DestroyInstance();
+
+ //
+ // Shutdown the platform.
+ //
+ pPlatform->ShutdownPlatform();
+
+ //
+ // Destroy the game and platform (do it in this order in case the game's
+ // destructor references the platform.
+ //
+ Win32Platform::DestroyInstance();
+
+ // As a last thing, shut down the memory.
+ Win32Platform::ShutdownMemory();
+
+ // Re-enable the default heap
+ //
+#ifndef RAD_RELEASE
+ tName::SetAllocator (RADMEMORY_ALLOC_DEFAULT);
+#endif
+
+ //
+ // Pass any error codes back to the operating system.
+ //
+ return 0;
+}
+
+
+//=============================================================================
+// Function: ProcessCommandLineArguments
+//=============================================================================
+//
+// Description: Pick out the command line options and store them.
+//
+// Parameters: None.
+//
+// Returns: None.
+//
+//=============================================================================
+void ProcessCommandLineArguments( LPSTR lpCmdLine )
+{
+ char* argument;
+ argument = strtok( lpCmdLine, " " );
+
+ rDebugPrintf( "*************************************************************************\n" );
+ rDebugPrintf( "Command Line Args:\n" );
+
+ //
+ // Pick out all the command line options and store them in GameDB.
+ // Also dump them to the output for handy dandy viewing.
+ //
+ int i = 0;
+ while( NULL != argument )
+ {
+ rDebugPrintf( "arg%d: %s\n", i++, argument );
+
+ CommandLineOptions::HandleOption( argument );
+
+ argument = strtok( NULL, " " );
+ }
+
+ if( !CommandLineOptions::Get( CLO_ART_STATS ) )
+ {
+ //CommandLineOptions::HandleOption( "noheaps" );
+ }
+
+ rDebugPrintf( "*************************************************************************\n" );
+}
+
+
+
+void ProcessCommandLineArgumentsFromFile()
+{
+#ifndef FINAL
+
+ //Chuck: looking for additional command line args being passed in from a file
+ //its for QA testing etc.
+
+ FILE* pfile = fopen( "command.txt", "r" );
+
+ if (pfile != NULL)
+ {
+ int ret = fseek( pfile, 0, SEEK_END );
+ rAssert( ret == 0 );
+
+ int len = ftell( pfile );
+ rAssertMsg( len < 256, "Command line file too large to process." );
+
+ rewind( pfile );
+
+ if( len > 0 && len < 256 )
+ {
+ char commandlinestring[256] = {0};
+
+ fgets( commandlinestring, 256, pfile );
+
+ char* argument = strtok(commandlinestring," ");
+ while (argument != NULL)
+ {
+ CommandLineOptions::HandleOption(argument);
+ argument=strtok(NULL," ");
+ }
+ }
+
+ fclose( pfile );
+ }
+#endif //FINAL
+} //end of Function
diff --git a/game/code/main/win32platform.cpp b/game/code/main/win32platform.cpp
new file mode 100644
index 0000000..ed88d08
--- /dev/null
+++ b/game/code/main/win32platform.cpp
@@ -0,0 +1,2286 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Win32Platform
+//
+// Description: Abstracts the differences for setting up and shutting down
+// the different platforms.
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//===========================================================================
+
+//========================================
+// System Includes
+//========================================
+// Standard Lib
+#include <stdlib.h>
+#include <string.h>
+// Pure 3D
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/anim/expression.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/anim/polyskin.hpp>
+#include <p3d/anim/sequencer.hpp>
+#include <p3d/anim/skeleton.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/gameattr.hpp>
+#include <p3d/image.hpp>
+#include <p3d/imagefont.hpp>
+#include <p3d/light.hpp>
+#include <p3d/locator.hpp>
+#include <p3d/platform.hpp>
+#include <p3d/scenegraph/scenegraph.hpp>
+#include <p3d/sprite.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/texture.hpp>
+#include <p3d/file.hpp>
+#include <p3d/shader.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/memory.hpp>
+#include <p3d/bmp.hpp>
+#include <p3d/png.hpp>
+#include <p3d/targa.hpp>
+#include <p3d/font.hpp>
+#include <p3d/texturefont.hpp>
+#include <p3d/unicode.hpp>
+// Pure 3D: Loader-specific
+#include <render/Loaders/GeometryWrappedLoader.h>
+#include <render/Loaders/StaticEntityLoader.h>
+#include <render/Loaders/StaticPhysLoader.h>
+#include <render/Loaders/TreeDSGLoader.h>
+#include <render/Loaders/FenceLoader.h>
+#include <render/Loaders/IntersectLoader.h>
+#include <render/Loaders/AnimCollLoader.h>
+#include <render/Loaders/AnimDSGLoader.h>
+#include <render/Loaders/DynaPhysLoader.h>
+#include <render/Loaders/InstStatPhysLoader.h>
+#include <render/Loaders/InstStatEntityLoader.h>
+#include <render/Loaders/WorldSphereLoader.h>
+#include <loading/roaddatasegmentloader.h>
+#include <render/Loaders/BillboardWrappedLoader.h>
+#include <render/Loaders/instparticlesystemloader.h>
+#include <render/Loaders/breakableobjectloader.h>
+#include <render/Loaders/AnimDynaPhysLoader.h>
+#include <render/Loaders/lensflareloader.h>
+#include <p3d/shadow.hpp>
+#include <p3d/anim/animatedobject.hpp>
+#include <p3d/effects/particleloader.hpp>
+#include <p3d/effects/opticloader.hpp>
+#include <p3d/anim/vertexanimkey.hpp>
+#include <stateprop/statepropdata.hpp>
+
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radthread.hpp>
+#include <radplatform.hpp>
+#include <radtime.hpp>
+#include <radmemorymonitor.hpp>
+#include <raddebugcommunication.hpp>
+#include <raddebugwatch.hpp>
+#include <radfile.hpp>
+#include <radmovie2.hpp>
+
+//This is so we can get the name of the file that's failing.
+#include <../src/radfile/common/requests.hpp>
+
+// sim - for InstallSimLoaders
+#include <simcommon/simutility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/inputmanager.h>
+#include <main/win32platform.h>
+#include <main/commandlineoptions.h>
+#include <main/game.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderFlow/renderflow.h>
+#include <render/Loaders/AllWrappers.h>
+#include <memory/srrmemory.h>
+
+#include <loading/locatorloader.h>
+#include <loading/cameradataloader.h>
+#include <loading/roadloader.h>
+#include <loading/pathloader.h>
+#include <loading/intersectionloader.h>
+#include <loading/roaddatasegmentloader.h>
+#include <atc/atcloader.h>
+#include <data/gamedatamanager.h>
+#include <data/config/gameconfigmanager.h>
+#include <debug/debuginfo.h>
+#include <constants/srrchunks.h>
+#include <gameflow/gameflow.h>
+#include <sound/soundmanager.h>
+#include <presentation/presentation.h>
+#include <presentation/gui/guitextbible.h>
+#include <cheats/cheatinputsystem.h>
+#include <mission/gameplaymanager.h>
+
+
+
+
+#include <radload/radload.hpp>
+
+#include <main/errorswin32.h>
+
+#define WIN32_SECTION "WIN32_SECTION"
+#define TIMER_LEAVE 1
+
+//#define PRINT_WINMESSAGES
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+Win32Platform* Win32Platform::spInstance = NULL;
+
+// Other static members.
+HINSTANCE Win32Platform::mhInstance = NULL;
+HWND Win32Platform::mhWnd = NULL;
+HANDLE Win32Platform::mhMutex = NULL;
+bool Win32Platform::mShowCursor = true;
+
+//The Adlib font. <sigh>
+unsigned char gFont[] =
+#include <font/defaultfont.h>
+
+//
+// Define the starting resolution.
+//
+static const Win32Platform::Resolution StartingResolution = Win32Platform::Res_800x600;
+static const int StartingBPP = 32;
+
+// This specifies the PDDI DLL to use. We are using directx8.
+#ifdef RAD_DEBUG
+static const char d3dLibraryName[] = "pddidx8d.dll";
+#endif
+#ifdef RAD_TUNE
+static const char d3dLibraryName[] = "pddidx8t.dll";
+#endif
+#ifdef RAD_RELEASE
+static const char d3dLibraryName[] = "pddidx8r.dll";
+#endif
+
+// Name of the application. This is the string that appears in the Window's
+// title bar.
+static const char ApplicationName[] = "The Simpsons: Hit & Run";
+
+// The window style
+static const DWORD WndStyle = WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX;
+
+// The gamma of the desktop.. needed to reset it on alt-tabs.
+static WORD DesktopGammaRamp[ 3 ][ 256 ] = { 0 };
+
+void LoadMemP3DFile( unsigned char* buffer, unsigned int size, tEntityStore* store )
+{
+ tFileMem* file = new tFileMem(buffer,size);
+ file->AddRef();
+ file->SetFilename("memfile.p3d");
+ p3d::loadManager->GetP3DHandler()->Load( file, p3d::inventory );
+ file->Release();
+}
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Win32Platform::CreateInstance
+//==============================================================================
+//
+// Description: Creates the Win32Platform.
+//
+// Parameters: win32 parameters.
+//
+// Return: Pointer to the Win32Platform.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+Win32Platform* Win32Platform::CreateInstance( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
+{
+MEMTRACK_PUSH_GROUP( "Win32Platform" );
+ rAssert( spInstance == NULL );
+
+ spInstance = new(GMA_PERSISTENT) Win32Platform( hInstance, hPrevInstance, lpCmdLine, nCmdShow );
+ rAssert( spInstance );
+MEMTRACK_POP_GROUP( "Win32Platform" );
+
+ return spInstance;
+}
+
+//==============================================================================
+// Win32Platform::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the Win32Platform singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the Win32Platform.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+Win32Platform* Win32Platform::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// Win32Platform::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the Win32Platform.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void Win32Platform::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+}
+
+
+
+//==============================================================================
+// Win32Platform::InitializeWindow
+//==============================================================================
+// Description: Creates the window class and window instance for the application.
+// We must do this before initializing the platform.
+//
+// Parameters: hInstance - the handle to the instance.
+//
+// Return: true if successful and the program can run.
+// false if another simpsons window already exists and this
+// instance should terminate.
+//
+// Constraints: Must be initialized before the platform.
+//
+//==============================================================================
+bool Win32Platform::InitializeWindow( HINSTANCE hInstance )
+{
+ // check to see if another instance is running...
+ mhMutex = CreateMutex(NULL, 0, ApplicationName);
+ if (GetLastError() == ERROR_ALREADY_EXISTS)
+ {
+ // simpsons is already running, so lets find the window and give it focus
+ HWND hwnd = FindWindow(ApplicationName, NULL);
+ if (hwnd != NULL)
+ {
+ // if window is minimized, restore it
+ WINDOWPLACEMENT wndpl;
+ if (GetWindowPlacement(hwnd, &wndpl) != 0)
+ {
+ if ((wndpl.showCmd == SW_MINIMIZE) ||
+ (wndpl.showCmd == SW_SHOWMINIMIZED))
+ {
+ ShowWindow(hwnd, SW_RESTORE);
+ }
+ }
+
+ // activate the window
+ SetForegroundWindow(hwnd);
+
+ return false;
+ }
+ }
+
+ mhInstance = hInstance;
+
+ // Create and resigter an object that defines the window that we will
+ // run our game in.
+ WNDCLASS Wndclass;
+ Wndclass.style = CS_HREDRAW | CS_VREDRAW;
+ Wndclass.lpfnWndProc = WndProc;
+ Wndclass.cbClsExtra = 0;
+ Wndclass.cbWndExtra = 0;
+ Wndclass.hInstance = mhInstance;
+ Wndclass.hIcon = LoadIcon( mhInstance, "IDI_SIMPSONSICON" );
+ Wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ Wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
+ Wndclass.lpszMenuName= NULL;
+ Wndclass.lpszClassName = ApplicationName;
+ ::RegisterClass(&Wndclass);
+
+ // Set up the window.
+ int x, y;
+ TranslateResolution( StartingResolution, x, y );
+
+ RECT clientRect;
+ clientRect.left = 0;
+ clientRect.top = 0;
+ clientRect.right = x;
+ clientRect.bottom = y;
+
+ // for windowed mode, center the screen
+ int nScreenWidth = GetSystemMetrics(SM_CXSCREEN);
+ int nScreenHeight = GetSystemMetrics(SM_CYSCREEN);
+
+ while (nScreenWidth > 1600 ) // probably multimonitor
+ {
+ nScreenWidth /= 2;
+ }
+
+ // centre the window
+ clientRect.left = (nScreenWidth-x)/2;
+ clientRect.top = (nScreenHeight-y)/2;
+ clientRect.right += clientRect.left;
+ clientRect.bottom += clientRect.top;
+
+ AdjustWindowRect(&clientRect,WndStyle,FALSE);
+
+ // Create the game's main window.
+ mhWnd = ::CreateWindow(ApplicationName,
+ ApplicationName,
+ WndStyle,
+ clientRect.left,
+ clientRect.top,
+ clientRect.right-clientRect.left,
+ clientRect.bottom-clientRect.top,
+ NULL,
+ NULL,
+ mhInstance,
+ NULL);
+
+ rAssert(mhWnd != NULL);
+
+ ShowTheCursor( false );
+
+ return true;
+}
+
+//==============================================================================
+// Win32Platform::InitializeFoundation
+//==============================================================================
+// Description: FTech must be setup first so that all the memory services
+// are ready to go before we begin allocating anything.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: The FTech systems must be initialized in a particular order.
+// Consult their documentation before changing.
+//
+//==============================================================================
+void Win32Platform::InitializeFoundation()
+{
+ //
+ // Initialize the memory heaps
+ // obsolete now.. the heaps initialize memory.
+ //
+ //InitializeMemory();
+
+ //
+ // Register an out-of-memory display handler in case something goes bad
+ // while allocating the heaps
+ //
+ ::radMemorySetOutOfMemoryCallback( PrintOutOfMemoryMessage, NULL );
+
+ //
+ // Initialize memory monitor by JamesCo. TM.
+ //
+ if( CommandLineOptions::Get( CLO_MEMORY_MONITOR ) )
+ {
+ const int KB = 1024;
+ ::radMemoryMonitorInitialize( 64 * KB, GMA_DEBUG );
+ }
+
+ // Setup the memory heaps
+ //
+ HeapMgr()->PrepareHeapsStartup ();
+
+ // Seed the heap stack
+ //
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ //
+ // Initilalize the platform system
+ //
+ ::radPlatformInitialize( mhWnd, mhInstance, 0 );
+
+ //
+ // Initialize the timer system
+ //
+ ::radTimeInitialize();
+
+ //
+ // Initialize the debug communication system.
+ //
+ ::radDbgComTargetInitialize( WinSocket,
+ radDbgComDefaultPort, // Default
+ NULL, // Default
+ GMA_DEBUG );
+
+
+ //
+ // Initialize the Watcher.
+ //
+ ::radDbgWatchInitialize( "SRR2",
+ 32 * 16384, // 2 * Default
+ GMA_DEBUG );
+
+ //
+ // Initialize the file system.
+ //
+ ::radFileInitialize( 50, // Default
+ 32, // Default
+ GMA_PERSISTENT );
+
+ ::radLoadInitialize();
+ //radLoad->SetSyncLoading( true );
+
+ ::radDriveMount( NULL, GMA_PERSISTENT);
+
+ //
+ // Initialize the new movie player
+ //
+ ::radMovieInitialize2( GMA_PERSISTENT );
+
+ HeapMgr()->PopHeap (GMA_PERSISTENT);
+}
+
+//==============================================================================
+// Win32Platform::InitializeMemory
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Win32Platform::InitializeMemory()
+{
+ //
+ // Only do this once!
+ //
+ if( gMemorySystemInitialized == true )
+ {
+ return;
+ }
+
+ gMemorySystemInitialized = true;
+
+ //
+ // Initialize the thread system.
+ //
+ ::radThreadInitialize();
+
+ //
+ // Initialize the memory system.
+ //
+ ::radMemoryInitialize();
+}
+
+//==============================================================================
+// Win32Platform::ShutdownMemory
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void Win32Platform::ShutdownMemory()
+{
+ if( gMemorySystemInitialized )
+ {
+ gMemorySystemInitialized = false;
+
+ // No shutdown the memory. This leads to bad errors when destroying
+ // static variables sprinkled here and there.
+ //::radMemoryTerminate();
+
+ ::radThreadTerminate();
+ }
+}
+
+//==============================================================================
+// Win32Platform::InitializePlatform
+//==============================================================================
+// Description: Get the Win32 ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void Win32Platform::InitializePlatform()
+{
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ //
+ // Rendering is good.
+ //
+ InitializePure3D();
+
+ //
+ // Add anything here that needs to be before the drive is opened.
+ //
+ DisplaySplashScreen( Error ); // blank screen
+
+ //
+ // Show the window on the screen. Must be done before initializing the input manager.
+ //
+ ShowWindow( mhWnd, mFullscreen ? SW_SHOWMAXIMIZED : SW_SHOW );
+
+ //
+ // Opening the drive is SLOW...
+ //
+ InitializeFoundationDrive();
+
+ //
+ // Initialize the controller.
+ //
+ GetInputManager()->Init();
+
+ //
+ // Register with the game config manager
+ //
+ GetGameConfigManager()->RegisterConfig( this );
+
+ HeapMgr()->PopHeap (GMA_PERSISTENT);
+}
+
+
+//==============================================================================
+// Win32Platform::ShutdownPlatform
+//==============================================================================
+// Description: Shut down the PS2.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void Win32Platform::ShutdownPlatform()
+{
+ ShutdownPure3D();
+ ShutdownFoundation();
+}
+
+//=============================================================================
+// Win32Platform::LaunchDashboard
+//=============================================================================
+// Description: We use this a the emergency exit from the game if we arent in a context that suppose the transition
+// to the CONTEXT_EXIT
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Win32Platform::LaunchDashboard()
+{
+
+ {
+ //chuck I copied and pasted from the other platform's implementations
+
+ GetLoadingManager()->CancelPendingRequests();
+ //TODO: Make sure sounds shut down too.
+ GetSoundManager()->SetMasterVolume( 0.0f );
+
+ // DisplaySplashScreen( FadeToBlack );
+
+ GetPresentationManager()->StopAll();
+
+ //Shouldn't need to do this since, this singleton and the others should get destroyed once we
+ //retrun the main loop
+ //GameDataManager::DestroyInstance(); //Get rid of memcards
+
+ p3d::loadManager->CancelAll();
+
+ GetSoundManager()->StopForMovie();
+
+ //Shouldnt need the early destruction of this singleton either
+ //SoundManager::DestroyInstance();
+
+ //Dont want to shutdown platform early either.
+ //ShutdownPlatform();
+ //rAssertMsg( false, "Doesn't make sense for win32." );
+ }
+}
+
+//=============================================================================
+// Win32Platform::ResetMachine
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Win32Platform::ResetMachine()
+{
+ rAssertMsg( false, "Doesn't make sense for win32." );
+}
+
+//=============================================================================
+// Win32Platform::DisplaySplashScreen
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( SplashScreen screenID,
+// const char* overlayText = NULL,
+// float fontScale = 1.0f,
+// float textPosX = 0.0f,
+// float textPosY = 0.0f,
+// tColour textColour,
+// int fadeFrames = 3 )
+//
+// Return: void
+//
+//=============================================================================
+void Win32Platform::DisplaySplashScreen( SplashScreen screenID,
+ const char* overlayText,
+ float fontScale,
+ float textPosX,
+ float textPosY,
+ tColour textColour,
+ int fadeFrames )
+{
+ HeapMgr()->PushHeap( GMA_TEMP );
+
+ p3d::inventory->PushSection();
+ p3d::inventory->AddSection( WIN32_SECTION );
+ p3d::inventory->SelectSection( WIN32_SECTION );
+
+ P3D_UNICODE unicodeText[256];
+
+ // Save the current Projection mode so I can restore it later
+ pddiProjectionMode pm = p3d::pddi->GetProjectionMode();
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_DEVICE);
+
+ pddiCullMode cm = p3d::pddi->GetCullMode();
+ p3d::pddi->SetCullMode(PDDI_CULL_NONE);
+
+
+ //CREATE THE FONT
+ tTextureFont* thisFont = NULL;
+
+ // Convert memory buffer into a texturefont.
+ //
+ //p3d::load(gFont, DEFAULTFONT_SIZE, GMA_TEMP);
+ LoadMemP3DFile( gFont, DEFAULTFONT_SIZE, p3d::inventory );
+
+ thisFont = p3d::find<tTextureFont>("adlibn_20");
+ rAssert( thisFont );
+
+ thisFont->AddRef();
+ tShader* fontShader = thisFont->GetShader();
+ //fontShader->SetInt( )
+
+
+ p3d::AsciiToUnicode( overlayText, unicodeText, 256 );
+
+ // Make the missing letter into somthing I can see
+ //
+ thisFont->SetMissingLetter(p3d::ConvertCharToUnicode('j'));
+
+ int a = 0;
+
+ do
+ {
+ p3d::pddi->SetColourWrite(true, true, true, true);
+ p3d::pddi->SetClearColour( pddiColour(0,0,0) );
+ p3d::pddi->BeginFrame();
+ p3d::pddi->Clear(PDDI_BUFFER_COLOUR);
+
+ //This is for fading in the font and shaders.
+ int bright = 255;
+ if (a < fadeFrames) bright = (a * 255) / fadeFrames;
+ if ( bright > 255 ) bright = 255;
+ tColour c(bright, bright, bright, 255);
+
+ //Display font
+ if (overlayText != NULL)
+ {
+ tColour colour = textColour;
+ colour.SetAlpha( bright );
+
+ thisFont->SetColour( colour );
+
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_ORTHOGRAPHIC);
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+
+ p3d::stack->Translate( textPosX, textPosY, 1.5f);
+ float scaleSize = 1.0f / 480.0f; //This is likely good for 528 also.
+ p3d::stack->Scale(scaleSize * fontScale, scaleSize* fontScale , 1.0f);
+
+ if ( textPosX != 0.0f || textPosY != 0.0f )
+ {
+ thisFont->DisplayText( unicodeText );
+ }
+ else
+ {
+ thisFont->DisplayText( unicodeText, 3 );
+ }
+
+ p3d::stack->Pop();
+ }
+
+ p3d::pddi->EndFrame();
+ p3d::context->SwapBuffers();
+
+ ++a;
+
+ } while (a <= fadeFrames);
+
+ p3d::pddi->SetCullMode(cm);
+ p3d::pddi->SetProjectionMode(pm);
+
+ //Should do this after a vsync.
+ thisFont->Release();
+
+ p3d::inventory->RemoveSectionElements(WIN32_SECTION);
+ p3d::inventory->DeleteSection(WIN32_SECTION);
+ p3d::inventory->PopSection();
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+}
+
+
+//=============================================================================
+// Win32Platform::DisplaySplashScreen
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* textureName,
+// const char* overlayText = NULL,
+// float fontScale = 1.0f,
+// float textPosX = 0.0f,
+// float textPosY = 0.0f,
+// tColour textColour,
+// int fadeFrames = 3 )
+//
+// Return: void
+//
+//=============================================================================
+void Win32Platform::DisplaySplashScreen( const char* textureName,
+ const char* overlayText,
+ float fontScale,
+ float textPosX,
+ float textPosY,
+ tColour textColour,
+ int fadeFrames )
+{
+}
+
+void Win32Platform::OnControllerError(const char *msg)
+{
+ DisplaySplashScreen( Error, msg, 0.7f, 0.0f, 0.0f, tColour(255, 255, 255), 0 );
+ mErrorState = CTL_ERROR;
+ mPauseForError = true;
+
+}
+
+
+//=============================================================================
+// Win32Platform::OnDriveError
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( radFileError error, const char* pDriveName, void* pUserData )
+//
+// Return: bool
+//
+//=============================================================================
+bool Win32Platform::OnDriveError( radFileError error, const char* pDriveName, void* pUserData )
+{
+ // First check if the error is related to loading/saving games.
+ // We do this here because windows has one drive for all operations.
+ // If the game data manager is using the drive, it handles the error.
+ GameDataManager* gm = GetGameDataManager();
+ if( gm->IsUsingDrive() )
+ {
+ return gm->OnDriveError( error, pDriveName, pUserData );
+ }
+
+ switch ( error )
+ {
+ case Success:
+ {
+ if ( mErrorState != NONE )
+ {
+ DisplaySplashScreen( FadeToBlack );
+ mErrorState = NONE;
+ mPauseForError = false;
+ }
+
+ return true;
+ break;
+ }
+ case FileNotFound:
+ {
+ rAssert( pUserData != NULL );
+
+ radFileRequest* request = static_cast<radFileRequest*>( pUserData );
+ const char* fileName = request->GetFilename();
+
+ //Get rid of the slashes.
+ unsigned int i;
+ unsigned int lastIndex = 0;
+ for ( i = 0; i < strlen( fileName ); ++i )
+ {
+ if ( fileName[ i ] == '\\' )
+ {
+ lastIndex = i;
+ }
+ }
+
+ unsigned int adjustedIndex = lastIndex == 0 ? lastIndex : lastIndex + 1;
+
+ char adjustedName[32];
+ strncpy( adjustedName, &fileName[adjustedIndex], ( strlen( fileName ) - lastIndex ) );
+ adjustedName[ strlen( fileName ) - lastIndex ] = '\0';
+
+ if( strcmp( fileName, GameConfigManager::ConfigFilename ) == 0 )
+ {
+ return false;
+ }
+
+ char errorString[256];
+ sprintf( errorString, "%s:\n%s", ERROR_STRINGS[error], adjustedName );
+ DisplaySplashScreen( Error, errorString, 1.0f, 0.0f, 0.0f, tColour(255, 255, 255), 0 );
+ mErrorState = P_ERROR;
+ mPauseForError = true;
+
+ return true;
+ }
+ case NoMedia:
+ case MediaNotFormatted:
+ case MediaCorrupt:
+ case NoFreeSpace:
+ case HardwareFailure:
+ {
+ //This could be the wrong disc.
+ DisplaySplashScreen( Error, ERROR_STRINGS[error], 1.0f, 0.0f, 0.0f, tColour(255, 255, 255), 0 );
+ mErrorState = P_ERROR;
+ mPauseForError = true;
+
+ return true;
+ }
+ default:
+ {
+ //Others are not supported.
+ rAssert( false );
+ }
+ }
+
+ return false;
+}
+
+//=============================================================================
+// Win32Platform::SetResolution
+//=============================================================================
+// Description: Sets the screen resolution
+//
+// Parameters: res - desired resolution
+//
+// Returns: true if successful
+// false if not supported
+//
+// Notes:
+//=============================================================================
+
+bool Win32Platform::SetResolution( Resolution res, int bpp, bool fullscreen )
+{
+ // Check if resolution is supported.
+ if( !IsResolutionSupported( res, bpp ) )
+ {
+ return false;
+ }
+
+ // Set up the new properties
+ mResolution = res;
+ mbpp = bpp;
+ mFullscreen = fullscreen;
+
+ // Reinitialize the d3d context.
+ InitializeContext();
+
+ // Resize the window for the new resolution
+ ResizeWindow();
+
+ return true;
+}
+
+//=============================================================================
+// Win32Platform::GetResolution
+//=============================================================================
+// Description: Returns the current resolution
+//
+// Parameters: n/a
+//
+// Returns: resolution
+//
+// Notes:
+//=============================================================================
+
+Win32Platform::Resolution Win32Platform::GetResolution() const
+{
+ return mResolution;
+}
+
+//=============================================================================
+// Win32Platform::GetBPP
+//=============================================================================
+// Description: Returns the current bit depth.
+//
+// Parameters: n/a
+//
+// Returns: bit depth
+//
+// Notes:
+//=============================================================================
+
+int Win32Platform::GetBPP() const
+{
+ return mbpp;
+}
+
+//=============================================================================
+// Win32Platform::IsFullscreen
+//=============================================================================
+// Description: Returns true if currently in full screen mode
+//
+// Parameters: n/a
+//
+// Returns: true if in full screen, false if in window
+//
+// Notes:
+//=============================================================================
+
+bool Win32Platform::IsFullscreen() const
+{
+ return mFullscreen;
+}
+
+//=============================================================================
+// Win32Platform::GetConfigName
+//=============================================================================
+// Description: Returns the name of the win32 platform's config
+//
+// Parameters: n/a
+//
+// Returns:
+//
+// Notes:
+//=============================================================================
+
+const char* Win32Platform::GetConfigName() const
+{
+ return "System";
+}
+
+//=============================================================================
+// Win32Platform::GetNumProperties
+//=============================================================================
+// Description: Returns the number of config properties
+//
+// Parameters: n/a
+//
+// Returns:
+//
+// Notes:
+//=============================================================================
+
+int Win32Platform::GetNumProperties() const
+{
+ return 4;
+}
+
+//=============================================================================
+// Win32Platform::LoadDefaults
+//=============================================================================
+// Description: Loads the default configuration for the system.
+//
+// Parameters: n/a
+//
+// Returns:
+//
+// Notes:
+//=============================================================================
+
+void Win32Platform::LoadDefaults()
+{
+#ifdef RAD_DEBUG
+ SetResolution( StartingResolution, StartingBPP, !CommandLineOptions::Get( CLO_WINDOW_MODE ) );
+#else
+ SetResolution( StartingResolution, StartingBPP, true );
+#endif
+
+
+ GetRenderFlow()->SetGamma( 1.0f );
+}
+
+//=============================================================================
+// Win32Platform::LoadConfig
+//=============================================================================
+// Description: Loads the platforms configuration
+//
+// Parameters: n/a
+//
+// Returns:
+//
+// Notes:
+//=============================================================================
+
+void Win32Platform::LoadConfig( ConfigString& config )
+{
+ char property[ ConfigString::MaxLength ];
+ char value[ ConfigString::MaxLength ];
+
+ while ( config.ReadProperty( property, value ) )
+ {
+ if( _stricmp( property, "display" ) == 0 )
+ {
+ if( _stricmp( value, "window" ) == 0 )
+ {
+ mFullscreen = false;
+ }
+ else if( _stricmp( value, "fullscreen" ) == 0 )
+ {
+ mFullscreen = true;
+ }
+ }
+ else if( _stricmp( property, "resolution" ) == 0 )
+ {
+ if( strcmp( value, "640x480" ) == 0 )
+ {
+ mResolution = Res_640x480;
+ }
+ else if( strcmp( value, "800x600" ) == 0 )
+ {
+ mResolution = Res_800x600;
+ }
+ else if( strcmp( value, "1024x768" ) == 0 )
+ {
+ mResolution = Res_1024x768;
+ }
+ else if( strcmp( value, "1152x864" ) == 0 )
+ {
+ mResolution = Res_1152x864;
+ }
+ else if( strcmp( value, "1280x1024" ) == 0 )
+ {
+ mResolution = Res_1280x1024;
+ }
+ else if( strcmp( value, "1600x1200" ) == 0 )
+ {
+ mResolution = Res_1600x1200;
+ }
+ }
+ else if( _stricmp( property, "bpp" ) == 0 )
+ {
+ if( strcmp( value, "16" ) == 0 )
+ {
+ mbpp = 16;
+ }
+ else if( strcmp( value, "32" ) == 0 )
+ {
+ mbpp = 32;
+ }
+ }
+ else if( _stricmp( property, "gamma" ) == 0 )
+ {
+ float val = (float) atof( value );
+ if( val > 0 )
+ {
+ GetRenderFlow()->SetGamma( val );
+ }
+ }
+ }
+
+ // apply the new settings.
+ SetResolution( mResolution, mbpp, mFullscreen );
+}
+
+//=============================================================================
+// Win32Platform::SaveConfig
+//=============================================================================
+// Description: Saves the system configuration to the config string.
+//
+// Parameters: config string to save to
+//
+// Returns:
+//
+// Notes:
+//=============================================================================
+
+void Win32Platform::SaveConfig( ConfigString& config )
+{
+ config.WriteProperty( "display", mFullscreen ? "fullscreen" : "window" );
+
+ const char* res = "800x600";
+ switch( mResolution )
+ {
+ case Res_640x480:
+ {
+ res = "640x480";
+ break;
+ }
+ case Res_800x600:
+ {
+ res = "800x600";
+ break;
+ }
+ case Res_1024x768:
+ {
+ res = "1024x768";
+ break;
+ }
+ case Res_1152x864:
+ {
+ res = "1152x864";
+ break;
+ }
+ case Res_1280x1024:
+ {
+ res = "1280x1024";
+ break;
+ }
+ case Res_1600x1200:
+ {
+ res = "1600x1200";
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ }
+ }
+
+ config.WriteProperty( "resolution", res );
+
+ config.WriteProperty( "bpp", mbpp == 16 ? "16" : "32" );
+
+ char gamma[20];
+ sprintf( gamma, "%f", GetRenderFlow()->GetGamma() );
+ config.WriteProperty( "gamma", gamma );
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Win32Platform::Win32Platform
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Win32Platform::Win32Platform( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) :
+ mpPlatform( NULL ),
+ mpContext( NULL ),
+ mResolution( StartingResolution ),
+ mbpp( StartingBPP )
+{
+ mhInstance = hInstance;
+ mFullscreen = false;
+
+ mScreenWidth = GetSystemMetrics(SM_CXSCREEN);
+ mScreenHeight = GetSystemMetrics(SM_CYSCREEN);
+
+ while (mScreenWidth > 1600 ) // probably multimonitor
+ {
+ mScreenWidth /= 2;
+ }
+}
+
+
+//==============================================================================
+// Win32Platform::~Win32Platform
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Win32Platform::~Win32Platform()
+{
+ HeapManager::DestroyInstance();
+
+ CloseHandle( mhMutex );
+}
+
+//==============================================================================
+// Win32Platform::InitializeFoundationDrive
+//==============================================================================
+// Description: Get FTech ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: The FTech systems must be initialized in a particular order.
+// Consult their documentation before changing.
+//
+//==============================================================================
+void Win32Platform::InitializeFoundationDrive()
+{
+ //
+ // Get the default drive and hold it open for the life of the game.
+ // This is a costly operation so we only want to do it once.
+ //
+
+ char defaultDrive[ radFileDrivenameMax + 1 ];
+
+ ::radGetDefaultDrive( defaultDrive );
+
+ ::radDriveOpenSync( &mpIRadDrive,
+ defaultDrive,
+ NormalPriority, // Default
+ GMA_PERSISTENT );
+
+ rAssert( mpIRadDrive != NULL );
+
+ mpIRadDrive->RegisterErrorHandler( this, NULL );
+}
+
+
+//==============================================================================
+// Win32Platform::ShutdownFoundation
+//==============================================================================
+// Description: Shut down Foundation Tech.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: The FTech systems must be terminated in the reverse order that
+// they were initialized in.
+//
+//==============================================================================
+void Win32Platform::ShutdownFoundation()
+{
+ //
+ // Release the drive we've held open since the begining.
+ //
+ mpIRadDrive->Release();
+ mpIRadDrive = NULL;
+
+ //
+ // Shutdown the systems in the reverse order.
+ //
+ ::radMovieTerminate2();
+ ::radDriveUnmount( NULL );
+ ::radLoadTerminate();
+ ::radFileTerminate();
+ ::radDbgWatchTerminate();
+ if( CommandLineOptions::Get( CLO_MEMORY_MONITOR ) )
+ {
+ ::radMemoryMonitorTerminate();
+ }
+ ::radDbgComTargetTerminate();
+ ::radTimeTerminate();
+ ::radPlatformTerminate();
+}
+
+
+//==============================================================================
+// Win32Platform::InitializePure3D
+//==============================================================================
+// Description: Get Pure3D ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void Win32Platform::InitializePure3D()
+{
+MEMTRACK_PUSH_GROUP( "Win32Platform" );
+ // p3d::SetMemAllocator( p3d::ALLOC_DEFAULT, GMA_PERSISTENT );
+ // p3d::SetMemAllocator( p3d::ALLOC_LOADED, GMA_LEVEL );
+
+ //
+ // Initialise Pure3D platform object.
+ // This call differs between different platforms. The Win32 version,
+ // for example requires the application instance to be passed in.
+ //
+ mpPlatform = tPlatform::Create( mhInstance );
+ rAssert( mpPlatform != NULL );
+
+ //
+ // Initialize the d3d context.
+ //
+ InitializeContext();
+
+ //
+ // This call installs chunk handlers for all the primary chunk types that
+ // Pure3D supports. This includes textures, materials, geometries, and the
+ // like.
+ //
+ // p3d::InstallDefaultLoaders();
+ P3DASSERT(p3d::context);
+ tP3DFileHandler* p3d = new(GMA_PERSISTENT) tP3DFileHandler;
+ // p3d::loadManager->AddHandler(p3d, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(p3d, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tPNGHandler, "png");
+
+ if( CommandLineOptions::Get( CLO_FE_UNJOINED ) )
+ {
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tBMPHandler, "bmp");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tTargaHandler, "tga");
+ }
+ else
+ {
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tBMPHandler, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tPNGHandler, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tTargaHandler, "p3d");
+ }
+
+ // p3d->AddHandler(new tGeometryLoader);
+ // GeometryWrappedLoader* pGWL = new GeometryWrappedLoader;
+ GeometryWrappedLoader* pGWL =
+ (GeometryWrappedLoader*)GetAllWrappers()->mpLoader( AllWrappers::msGeometry );
+ pGWL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pGWL );
+
+ StaticEntityLoader* pSEL =
+ (StaticEntityLoader*)GetAllWrappers()->mpLoader( AllWrappers::msStaticEntity );
+ pSEL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pSEL );
+
+ StaticPhysLoader* pSPL =
+ (StaticPhysLoader*)GetAllWrappers()->mpLoader( AllWrappers::msStaticPhys );
+ pSPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pSPL );
+
+ TreeDSGLoader* pTDL =
+ (TreeDSGLoader*)GetAllWrappers()->mpLoader( AllWrappers::msTreeDSG );
+ pTDL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pTDL );
+
+ FenceLoader* pFL =
+ (FenceLoader*)GetAllWrappers()->mpLoader( AllWrappers::msFenceEntity );
+ pFL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pFL );
+
+ IntersectLoader* pIL =
+ (IntersectLoader*)GetAllWrappers()->mpLoader( AllWrappers::msIntersectDSG );
+ pIL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pIL );
+
+ AnimCollLoader* pACL =
+ (AnimCollLoader*)GetAllWrappers()->mpLoader( AllWrappers::msAnimCollEntity );
+ pACL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pACL );
+
+ AnimDSGLoader* pAnimDSGLoader =
+ (AnimDSGLoader*)GetAllWrappers()->mpLoader( AllWrappers::msAnimEntity );
+ pAnimDSGLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pAnimDSGLoader );
+
+
+ DynaPhysLoader* pDPL =
+ (DynaPhysLoader*)GetAllWrappers()->mpLoader( AllWrappers::msDynaPhys );
+ pDPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pDPL );
+
+ InstStatPhysLoader* pISPL =
+ (InstStatPhysLoader*)GetAllWrappers()->mpLoader( AllWrappers::msInstStatPhys );
+ pISPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pISPL );
+
+ InstStatEntityLoader* pISEL =
+ (InstStatEntityLoader*)GetAllWrappers()->mpLoader( AllWrappers::msInstStatEntity );
+ pISEL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pISEL );
+
+ LocatorLoader* pLL =
+ (LocatorLoader*)GetAllWrappers()->mpLoader( AllWrappers::msLocator);
+ pLL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pLL );
+
+ RoadLoader* pRL =
+ (RoadLoader*)GetAllWrappers()->mpLoader( AllWrappers::msRoadSegment);
+ pRL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pRL );
+
+ PathLoader* pPL =
+ (PathLoader*)GetAllWrappers()->mpLoader( AllWrappers::msPathSegment);
+ pPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pPL );
+
+ WorldSphereLoader* pWSL =
+ (WorldSphereLoader*)GetAllWrappers()->mpLoader( AllWrappers::msWorldSphere);
+ pWSL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pWSL );
+
+ LensFlareLoader* pLSL =
+ (LensFlareLoader*)GetAllWrappers()->mpLoader( AllWrappers::msLensFlare);
+ pLSL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pLSL );
+
+ BillboardWrappedLoader* pBWL =
+ (BillboardWrappedLoader*)GetAllWrappers()->mpLoader( AllWrappers::msBillboard);
+ pBWL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pBWL );
+
+
+ InstParticleSystemLoader* pInstParticleSystemLoader =
+ (InstParticleSystemLoader*) GetAllWrappers()->mpLoader( AllWrappers::msInstParticleSystem);
+ pInstParticleSystemLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pInstParticleSystemLoader );
+
+ BreakableObjectLoader* pBreakableObjectLoader =
+ (BreakableObjectLoader*) GetAllWrappers()->mpLoader( AllWrappers::msBreakableObject);
+ pBreakableObjectLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pBreakableObjectLoader );
+
+ AnimDynaPhysLoader* pAnimDynaPhysLoader =
+ (AnimDynaPhysLoader*) GetAllWrappers()->mpLoader( AllWrappers::msAnimDynaPhys);
+ pAnimDynaPhysLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pAnimDynaPhysLoader );
+
+ AnimDynaPhysWrapperLoader* pAnimWrapperLoader =
+ (AnimDynaPhysWrapperLoader*) GetAllWrappers()->mpLoader( AllWrappers::msAnimDynaPhysWrapper);
+ pAnimWrapperLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pAnimWrapperLoader );
+
+ p3d->AddHandler(new(GMA_PERSISTENT) tTextureLoader);
+ p3d->AddHandler( new(GMA_PERSISTENT) tSetLoader );
+ p3d->AddHandler(new(GMA_PERSISTENT) tShaderLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tCameraLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tGameAttrLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tLightLoader);
+
+ p3d->AddHandler(new(GMA_PERSISTENT) tLocatorLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tLightGroupLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tImageLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tTextureFontLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tImageFontLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tSpriteLoader);
+ //p3d->AddHandler(new(GMA_PERSISTENT) tBillboardQuadGroupLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tSkeletonLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tPolySkinLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tCompositeDrawableLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tVertexAnimKeyLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tAnimationLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tFrameControllerLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tMultiControllerLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tAnimatedObjectFactoryLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tAnimatedObjectLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tParticleSystemFactoryLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tParticleSystemLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tLensFlareGroupLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) sg::Loader);
+
+ p3d->AddHandler(new(GMA_PERSISTENT) tExpressionGroupLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tExpressionMixerLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tExpressionLoader);
+
+ //ATCloader, hope this doesnt blow up
+ p3d->AddHandler(new(GMA_PERSISTENT) ATCLoader);
+
+ //p3d->AddHandler(new p3d::tIgnoreLoader);
+
+ tSEQFileHandler* sequencerFileHandler = new(GMA_PERSISTENT) tSEQFileHandler;
+ p3d::loadManager->AddHandler(sequencerFileHandler, "seq");
+
+ // sim lib
+ sim::InstallSimLoaders();
+
+ p3d->AddHandler(new(GMA_PERSISTENT) CameraDataLoader, SRR2::ChunkID::FOLLOWCAM);
+ p3d->AddHandler(new(GMA_PERSISTENT) CameraDataLoader, SRR2::ChunkID::WALKERCAM);
+ p3d->AddHandler(new(GMA_PERSISTENT) IntersectionLoader);
+ //p3d->AddHandler(new(GMA_PERSISTENT) RoadLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) RoadDataSegmentLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) CStatePropDataLoader);
+MEMTRACK_POP_GROUP( "Win32Platform" );
+}
+
+
+//==============================================================================
+// Win32Platform::ShutdownPure3D
+//==============================================================================
+// Description: Clean up and shut down Pure3D.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void Win32Platform::ShutdownPure3D()
+{
+ //
+ // Clean-up the Pure3D Inventory
+ //
+ p3d::inventory->RemoveAllElements();
+ p3d::inventory->DeleteAllSections();
+
+ //
+ // Clean-up the space taken by the Pure 3D context.
+ //
+ if( mpContext != NULL )
+ {
+ mpPlatform->DestroyContext( mpContext );
+ mpContext = NULL;
+ }
+
+ //
+ // Clean-up the space taken by the Pure 3D platform.
+ //
+ if( mpPlatform != NULL )
+ {
+ tPlatform::Destroy( mpPlatform );
+ mpPlatform = NULL;
+ }
+}
+
+//==============================================================================
+// Win32Platform::InitializeContext
+//==============================================================================
+// Description: Initializes the d3d context for this application according to
+// the class' display settings - resolution, bpp, fullscreen.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//==============================================================================
+
+void Win32Platform::InitializeContext()
+{
+ tContextInitData init;
+
+ //
+ // This is the window we want to render into.
+ //
+ init.hwnd = mhWnd;
+
+ //
+ // Set the fullscreen/window mode.
+ //
+ init.displayMode = mFullscreen ? PDDI_DISPLAY_FULLSCREEN : PDDI_DISPLAY_WINDOW;
+
+ //
+ // This the name of the PDDI we will be using for rendering
+ //
+ strncpy(init.PDDIlib, d3dLibraryName, 128);
+
+ //
+ // All applications should supply PDDI_BUFFER_COLOUR. PDDI_BUFFER_DEPTH
+ // specifies that we also want to allocate a Z-buffer.
+ //
+ init.bufferMask = PDDI_BUFFER_COLOUR | PDDI_BUFFER_DEPTH;
+ init.enableSnapshot = false;
+
+ //
+ // These values only take effect in fullscreen mode. In windowed mode, the
+ // dimensions of the window define the rendering area. We'll define them
+ // anyway for completeness sake.
+ //
+ TranslateResolution( mResolution, init.xsize, init.ysize );
+
+ //
+ // Depth of the rendering buffer. Again, this value only works in
+ // fullscreen mode. In window mode, the depth of the desktop is used.
+ // This value should be either 16 or 32.
+ //
+ init.bpp = mbpp;
+
+ init.lockToVsync = false;
+
+ if( mpContext == NULL )
+ {
+ // Create the context
+ mpContext = mpPlatform->CreateContext( &init );
+ rAssert( mpContext != NULL );
+
+ //
+ // Assign this context to the platform.
+ //
+ mpPlatform->SetActiveContext( mpContext );
+ p3d::pddi->EnableZBuffer( true );
+ }
+ else
+ {
+ // Update the display settings.
+ mpContext->GetDisplay()->InitDisplay( &init );
+ }
+}
+
+//==============================================================================
+// Win32Platform::TranslateResolution
+//==============================================================================
+// Description: translates resolution enums to x and y
+//
+// Parameters: resolution - the res enum
+// x - corresponding width
+// y - corresponding height
+//
+// Return: N/A.
+//
+//==============================================================================
+
+void Win32Platform::TranslateResolution( Resolution res, int&x, int&y )
+{
+ switch( res )
+ {
+ case Res_640x480:
+ {
+ x = 640;
+ y = 480;
+ break;
+ }
+ case Res_800x600:
+ {
+ x = 800;
+ y = 600;
+ break;
+ }
+ case Res_1024x768:
+ {
+ x = 1024;
+ y = 768;
+ break;
+ }
+ case Res_1152x864:
+ {
+ x = 1152;
+ y = 864;
+ break;
+ }
+ case Res_1280x1024:
+ {
+ x = 1280;
+ y = 1024;
+ break;
+ }
+ case Res_1600x1200:
+ {
+ x = 1600;
+ y = 1200;
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ }
+ }
+}
+
+//==============================================================================
+// Win32Platform::IsResolutionSupported
+//==============================================================================
+// Description: Determines if a resolution is supported on this pc
+//
+// Parameters: resolution - the res enum
+//
+// Return: true if supported.
+//
+//==============================================================================
+
+bool Win32Platform::IsResolutionSupported( Resolution res, int bpp ) const
+{
+ int x,y;
+
+ TranslateResolution( res, x, y );
+
+ // Get the display info for the device
+ pddiDisplayInfo* displays = NULL;
+ int num_adapters = mpContext->GetDevice()->GetDisplayInfo( &displays );
+ rAssert( num_adapters > 0 );
+
+ // Go through the supported modes and see if we can do it.
+ // Ignore the refresh rate - directx uses default.
+ for( int i = 0; i < displays[0].nDisplayModes; i++ )
+ {
+ if( displays[0].modeInfo[i].width == x &&
+ displays[0].modeInfo[i].height == y &&
+ displays[0].modeInfo[i].bpp == bpp )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//==============================================================================
+// Win32Platform::TrackMouseEvent
+//==============================================================================
+// Description: Determines if a resolution is supported on this pc
+//
+// Parameters: pMouseEventTracker - the tracker
+//
+// Return: true if succeeded in creating a timer.
+//
+//==============================================================================
+BOOL Win32Platform::TrackMouseEvent( MOUSETRACKER* pMouseEventTracker )
+{
+ if ( !pMouseEventTracker || pMouseEventTracker->cbSize < sizeof(MOUSETRACKER) )
+ return false;
+
+ if( !IsWindow( pMouseEventTracker->hwndTrack ) )
+ return false;
+
+ if( !(pMouseEventTracker->dwFlags & TIMER_LEAVE) )
+ return false;
+
+ return SetTimer( pMouseEventTracker->hwndTrack,
+ pMouseEventTracker->dwFlags,
+ 100, (TIMERPROC)TrackMouseTimerProc );
+}
+
+//=============================================================================
+// Win32Platform::ResizeWindow
+//=============================================================================
+// Description: Resizes the app's window based on the current resolution.
+//
+// Parameters: n/a
+//
+// Returns: n/a
+//
+// Notes:
+//=============================================================================
+
+void Win32Platform::ResizeWindow()
+{
+ // If fullscreen, no need to change the window size.
+ if( mFullscreen )
+ {
+ return;
+ }
+
+ int x,y,cx,cy;
+ RECT clientRect;
+
+ TranslateResolution( mResolution, cx, cy );
+
+ if( cx < mScreenWidth ) // if the window fits on the desktop
+ {
+ x = ( mScreenWidth - cx ) / 2;
+ y = ( mScreenHeight - cy ) / 2;
+
+ // Adjust the rectangle for the title bar and borders
+ clientRect.left = x;
+ clientRect.top = y;
+ clientRect.right = x+cx;
+ clientRect.bottom = y+cy;
+
+ AdjustWindowRect(&clientRect,WndStyle,FALSE);
+ }
+ else // if the window is bigger than the client area
+ {
+ clientRect.left = 0;
+ clientRect.top = 0;
+ clientRect.right = cx;
+ clientRect.bottom = cy;
+ }
+
+ SetWindowPos( mhWnd,
+ HWND_TOP,
+ clientRect.left,
+ clientRect.top,
+ clientRect.right-clientRect.left,
+ clientRect.bottom-clientRect.top,
+ 0 );
+ ShowWindow( mhWnd, SW_SHOW );
+}
+
+//=============================================================================
+// Win32Platform::ShowTheCursor
+//=============================================================================
+// Description: Shows or hides the cursor. Wrapper for the windows ShowCursor
+// function, except it doesn't keep a counter for the number of
+// shows/hides.
+//
+// Parameters: show - show cursor
+//
+// Returns: n/a
+//
+// Notes:
+//=============================================================================
+
+void Win32Platform::ShowTheCursor( bool show )
+{
+ if( mShowCursor != show )
+ {
+ mShowCursor = show;
+ ShowCursor( mShowCursor );
+ }
+}
+
+//=============================================================================
+// GetMessageName
+//=============================================================================
+// Description: Prints the name of windows messages.
+//
+// Parameters: message - message id
+//
+// Returns: string name
+//
+// Notes:
+//=============================================================================
+
+#if defined( PRINT_WINMESSAGES ) && defined( RAD_DEBUG )
+static const char* GetMessageName( UINT message )
+{
+ switch ( message )
+ {
+ case 0x0000: return "WM_NULL";
+ case 0x0001: return "WM_CREATE";
+ case 0x0002: return "WM_DESTROY";
+ case 0x0003: return "WM_MOVE";
+ case 0x0005: return "WM_SIZE";
+ case 0x0006: return "WM_ACTIVATE";
+ case 0x0007: return "WM_SETFOCUS";
+ case 0x0008: return "WM_KILLFOCUS";
+ case 0x000A: return "WM_ENABLE";
+ case 0x000B: return "WM_SETREDRAW";
+ case 0x000C: return "WM_SETTEXT";
+ case 0x000D: return "WM_GETTEXT";
+ case 0x000E: return "WM_GETTEXTLENGTH";
+ case 0x000F: return "WM_PAINT";
+ case 0x0010: return "WM_CLOSE";
+ case 0x0011: return "WM_QUERYENDSESSION";
+ case 0x0013: return "WM_QUERYOPEN";
+ case 0x0016: return "WM_ENDSESSION";
+ case 0x0012: return "WM_QUIT";
+ case 0x0014: return "WM_ERASEBKGND";
+ case 0x0015: return "WM_SYSCOLORCHANGE";
+ case 0x0018: return "WM_SHOWWINDOW";
+ case 0x001A: return "WM_WININICHANGE";
+ case 0x001B: return "WM_DEVMODECHANGE";
+ case 0x001C: return "WM_ACTIVATEAPP";
+ case 0x001D: return "WM_FONTCHANGE";
+ case 0x001E: return "WM_TIMECHANGE";
+ case 0x001F: return "WM_CANCELMODE";
+ case 0x0020: return "WM_SETCURSOR";
+ case 0x0021: return "WM_MOUSEACTIVATE";
+ case 0x0022: return "WM_CHILDACTIVATE";
+ case 0x0023: return "WM_QUEUESYNC";
+ case 0x0024: return "WM_GETMINMAXINFO";
+ case 0x0026: return "WM_PAINTICON";
+ case 0x0027: return "WM_ICONERASEBKGND";
+ case 0x0028: return "WM_NEXTDLGCTL";
+ case 0x002A: return "WM_SPOOLERSTATUS";
+ case 0x002B: return "WM_DRAWITEM";
+ case 0x002C: return "WM_MEASUREITEM";
+ case 0x002D: return "WM_DELETEITEM";
+ case 0x002E: return "WM_VKEYTOITEM";
+ case 0x002F: return "WM_CHARTOITEM";
+ case 0x0030: return "WM_SETFONT";
+ case 0x0031: return "WM_GETFONT";
+ case 0x0032: return "WM_SETHOTKEY";
+ case 0x0033: return "WM_GETHOTKEY";
+ case 0x0037: return "WM_QUERYDRAGICON";
+ case 0x0039: return "WM_COMPAREITEM";
+ case 0x0041: return "WM_COMPACTING";
+ case 0x0044: return "WM_COMMNOTIFY";
+ case 0x0046: return "WM_WINDOWPOSCHANGING";
+ case 0x0047: return "WM_WINDOWPOSCHANGED";
+ case 0x0048: return "WM_POWER";
+ case 0x004A: return "WM_COPYDATA";
+ case 0x004B: return "WM_CANCELJOURNAL";
+ case 0x004E: return "WM_NOTIFY";
+ case 0x0050: return "WM_INPUTLANGCHANGEREQUEST";
+ case 0x0051: return "WM_INPUTLANGCHANGE";
+ case 0x0052: return "WM_TCARD";
+ case 0x0053: return "WM_HELP";
+ case 0x0054: return "WM_USERCHANGED";
+ case 0x0055: return "WM_NOTIFYFORMAT";
+ case 0x007B: return "WM_CONTEXTMENU";
+ case 0x007C: return "WM_STYLECHANGING";
+ case 0x007D: return "WM_STYLECHANGED";
+ case 0x007E: return "WM_DISPLAYCHANGE";
+ case 0x007F: return "WM_GETICON";
+ case 0x0080: return "WM_SETICON";
+ case 0x0081: return "WM_NCCREATE";
+ case 0x0082: return "WM_NCDESTROY";
+ case 0x0083: return "WM_NCCALCSIZE";
+ case 0x0084: return "WM_NCHITTEST";
+ case 0x0085: return "WM_NCPAINT";
+ case 0x0086: return "WM_NCACTIVATE";
+ case 0x0087: return "WM_GETDLGCODE";
+ case 0x0088: return "WM_SYNCPAINT";
+ case 0x00A0: return "WM_NCMOUSEMOVE";
+ case 0x00A1: return "WM_NCLBUTTONDOWN";
+ case 0x00A2: return "WM_NCLBUTTONUP";
+ case 0x00A3: return "WM_NCLBUTTONDBLCLK";
+ case 0x00A4: return "WM_NCRBUTTONDOWN";
+ case 0x00A5: return "WM_NCRBUTTONUP";
+ case 0x00A6: return "WM_NCRBUTTONDBLCLK";
+ case 0x00A7: return "WM_NCMBUTTONDOWN";
+ case 0x00A8: return "WM_NCMBUTTONUP";
+ case 0x00A9: return "WM_NCMBUTTONDBLCLK";
+ case 0x00AB: return "WM_NCXBUTTONDOWN";
+ case 0x00AC: return "WM_NCXBUTTONUP";
+ case 0x00AD: return "WM_NCXBUTTONDBLCLK";
+ case 0x00FF: return "WM_INPUT";
+ case 0x0100: return "WM_KEYDOWN";
+ case 0x0101: return "WM_KEYUP";
+ case 0x0102: return "WM_CHAR";
+ case 0x0103: return "WM_DEADCHAR";
+ case 0x0104: return "WM_SYSKEYDOWN";
+ case 0x0105: return "WM_SYSKEYUP";
+ case 0x0106: return "WM_SYSCHAR";
+ case 0x0107: return "WM_SYSDEADCHAR";
+ case 0x0109: return "WM_KEYLAST";
+ case 0xFFFF: return "UNICODE_NOCHAR";
+ case 0x0108: return "WM_KEYLAST";
+ case 0x010D: return "WM_IME_STARTCOMPOSITION";
+ case 0x010E: return "WM_IME_ENDCOMPOSITION";
+ case 0x010F: return "WM_IME_KEYLAST";
+ case 0x0110: return "WM_INITDIALOG";
+ case 0x0111: return "WM_COMMAND";
+ case 0x0112: return "WM_SYSCOMMAND";
+ case 0x0113: return "WM_TIMER";
+ case 0x0114: return "WM_HSCROLL";
+ case 0x0115: return "WM_VSCROLL";
+ case 0x0116: return "WM_INITMENU";
+ case 0x0117: return "WM_INITMENUPOPUP";
+ case 0x011F: return "WM_MENUSELECT";
+ case 0x0120: return "WM_MENUCHAR";
+ case 0x0121: return "WM_ENTERIDLE";
+ case 0x0122: return "WM_MENURBUTTONUP";
+ case 0x0123: return "WM_MENUDRAG";
+ case 0x0124: return "WM_MENUGETOBJECT";
+ case 0x0125: return "WM_UNINITMENUPOPUP";
+ case 0x0126: return "WM_MENUCOMMAND";
+ case 0x0127: return "WM_CHANGEUISTATE";
+ case 0x0128: return "WM_UPDATEUISTATE";
+ case 0x0129: return "WM_QUERYUISTATE";
+ case 0x0132: return "WM_CTLCOLORMSGBOX";
+ case 0x0133: return "WM_CTLCOLOREDIT";
+ case 0x0134: return "WM_CTLCOLORLISTBOX";
+ case 0x0135: return "WM_CTLCOLORBTN";
+ case 0x0136: return "WM_CTLCOLORDLG";
+ case 0x0137: return "WM_CTLCOLORSCROLLBAR";
+ case 0x0138: return "WM_CTLCOLORSTATIC";
+ case 0x0200: return "WM_MOUSEMOVE";
+ case 0x0201: return "WM_LBUTTONDOWN";
+ case 0x0202: return "WM_LBUTTONUP";
+ case 0x0203: return "WM_LBUTTONDBLCLK";
+ case 0x0204: return "WM_RBUTTONDOWN";
+ case 0x0205: return "WM_RBUTTONUP";
+ case 0x0206: return "WM_RBUTTONDBLCLK";
+ case 0x0207: return "WM_MBUTTONDOWN";
+ case 0x0208: return "WM_MBUTTONUP";
+ case 0x0209: return "WM_MBUTTONDBLCLK";
+ case 0x020A: return "WM_MOUSEWHEEL";
+ case 0x020B: return "WM_XBUTTONDOWN";
+ case 0x020C: return "WM_XBUTTONUP";
+ case 0x020D: return "WM_MOUSELAST";
+ case 0x0210: return "WM_PARENTNOTIFY";
+ case 0x0211: return "WM_ENTERMENULOOP";
+ case 0x0212: return "WM_EXITMENULOOP";
+ case 0x0213: return "WM_NEXTMENU";
+ case 0x0214: return "WM_SIZING";
+ case 0x0215: return "WM_CAPTURECHANGED";
+ case 0x0216: return "WM_MOVING";
+ case 0x0218: return "WM_POWERBROADCAST";
+ case 0x0219: return "WM_DEVICECHANGE";
+ case 0x0220: return "WM_MDICREATE";
+ case 0x0221: return "WM_MDIDESTROY";
+ case 0x0222: return "WM_MDIACTIVATE";
+ case 0x0223: return "WM_MDIRESTORE";
+ case 0x0224: return "WM_MDINEXT";
+ case 0x0225: return "WM_MDIMAXIMIZE";
+ case 0x0226: return "WM_MDITILE";
+ case 0x0227: return "WM_MDICASCADE";
+ case 0x0228: return "WM_MDIICONARRANGE";
+ case 0x0229: return "WM_MDIGETACTIVE";
+ case 0x0230: return "WM_MDISETMENU";
+ case 0x0231: return "WM_ENTERSIZEMOVE";
+ case 0x0232: return "WM_EXITSIZEMOVE";
+ case 0x0233: return "WM_DROPFILES";
+ case 0x0234: return "WM_MDIREFRESHMENU";
+ case 0x0281: return "WM_IME_SETCONTEXT";
+ case 0x0282: return "WM_IME_NOTIFY";
+ case 0x0283: return "WM_IME_CONTROL";
+ case 0x0284: return "WM_IME_COMPOSITIONFULL";
+ case 0x0285: return "WM_IME_SELECT";
+ case 0x0286: return "WM_IME_CHAR";
+ case 0x0288: return "WM_IME_REQUEST";
+ case 0x0290: return "WM_IME_KEYDOWN";
+ case 0x0291: return "WM_IME_KEYUP";
+ case 0x02A1: return "WM_MOUSEHOVER";
+ case 0x02A3: return "WM_MOUSELEAVE";
+ case 0x02A0: return "WM_NCMOUSEHOVER";
+ case 0x02A2: return "WM_NCMOUSELEAVE";
+ case 0x02B1: return "WM_WTSSESSION_CHANGE";
+ case 0x02c0: return "WM_TABLET_FIRST";
+ case 0x02df: return "WM_TABLET_LAST";
+ case 0x0300: return "WM_CUT";
+ case 0x0301: return "WM_COPY";
+ case 0x0302: return "WM_PASTE";
+ case 0x0303: return "WM_CLEAR";
+ case 0x0304: return "WM_UNDO";
+ case 0x0305: return "WM_RENDERFORMAT";
+ case 0x0306: return "WM_RENDERALLFORMATS";
+ case 0x0307: return "WM_DESTROYCLIPBOARD";
+ case 0x0308: return "WM_DRAWCLIPBOARD";
+ case 0x0309: return "WM_PAINTCLIPBOARD";
+ case 0x030A: return "WM_VSCROLLCLIPBOARD";
+ case 0x030B: return "WM_SIZECLIPBOARD";
+ case 0x030C: return "WM_ASKCBFORMATNAME";
+ case 0x030D: return "WM_CHANGECBCHAIN";
+ case 0x030E: return "WM_HSCROLLCLIPBOARD";
+ case 0x030F: return "WM_QUERYNEWPALETTE";
+ case 0x0310: return "WM_PALETTEISCHANGING";
+ case 0x0311: return "WM_PALETTECHANGED";
+ case 0x0312: return "WM_HOTKEY";
+ case 0x0317: return "WM_PRINT";
+ case 0x0318: return "WM_PRINTCLIENT";
+ case 0x0319: return "WM_APPCOMMAND";
+ case 0x031A: return "WM_THEMECHANGED";
+ case 0x0358: return "WM_HANDHELDFIRST";
+ case 0x035F: return "WM_HANDHELDLAST";
+ case 0x0360: return "WM_AFXFIRST";
+ case 0x0380: return "WM_PENWINFIRST";
+ case 0x038F: return "WM_PENWINLAST";
+ case 0x8000: return "WM_APP";
+ default: return "UNKNOWN MESSAGE";
+ }
+}
+#endif
+
+void Win32Platform::TrackMouseTimerProc( HWND hWnd, UINT uMsg, UINT idEvent, DWORD dwTime )
+{
+ RECT clientRect;
+ POINT point;
+
+ GetClientRect( hWnd, &clientRect );
+ MapWindowPoints( hWnd, NULL, (LPPOINT)&clientRect, 2 );
+ GetCursorPos( &point );
+ if( !PtInRect( &clientRect, point ) || (WindowFromPoint(point) != hWnd ) )
+ {
+ if( !KillTimer( hWnd, idEvent ) )
+ {
+ rDebugPrintf( "Couldn't kill the timer" );
+ }
+
+ PostMessage( hWnd, WM_MOUSELEAVE, 0, 0 );
+ }
+ else
+ {
+ GetInputManager()->GetFEMouse()->getCursor()->SetVisible( true );
+ }
+}
+
+//=============================================================================
+// Win32Platform::WndProc
+//=============================================================================
+// Description: The windows os messaging callback for the game.
+// Routes messages to pure3d.
+//
+// Parameters: hwnd - handle for window
+// message - message ID
+// wParam - word parameter
+// lParam - long parameter
+//
+// Returns: windows result
+//
+// Notes:
+//=============================================================================
+
+LRESULT Win32Platform::WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+#if defined( PRINT_WINMESSAGES ) && defined( RAD_DEBUG )
+ rDebugPrintf( "Windows Message: 0x%08x (%s) [0x%08x, 0x%08x]\n", message, GetMessageName(message), wParam, lParam );
+#endif
+ MOUSETRACKER mouseEventTracker;
+ static bool bMouseInWindow; // A flag to poll if you want to check if the mouse is in the clientwindow.
+
+ //
+ // Under Win32, Pure3D needs to get a crack at the Windows messages so
+ // it can detect window moving, resizing, and activation.
+ //
+ p3d::platform->ProcessWindowsMessage(hwnd, message, wParam, lParam);
+
+ switch(message)
+ {
+ case WM_ACTIVATEAPP:
+ {
+ InputManager* pInputManager = GetInputManager();
+
+ if( spInstance != NULL && spInstance->mpContext != NULL )
+ {
+ if( wParam ) // Window is being shown (in focus)
+ {
+ RenderFlow* rf = GetRenderFlow();
+
+ rf->SetGamma( rf->GetGamma() );
+ if( pInputManager )
+ {
+ //GetInputManager()->SetRumbleForDevice(0, true);
+ //rDebugPrintf("Force Effects Started!!! \n");
+ }
+ }
+ else // Window is being hidden (not in focus)
+ {
+ SetDeviceGammaRamp( GetDC( GetDesktopWindow( ) ), DesktopGammaRamp );
+ if( pInputManager )
+ {
+ //GetInputManager()->SetRumbleForDevice(0, false);
+ //rDebugPrintf("Force Effects Stopped!!! \n");
+ }
+ }
+
+ if( GetInputManager() != NULL )
+ {
+ GetInputManager()->GetFEMouse()->SetInGameOverride( !wParam );
+ }
+ }
+
+ break;
+ }
+
+ case WM_SYSKEYDOWN:
+ case WM_SYSKEYUP:
+ {
+ //Ignore Alt and F10 keys.
+ switch(wParam)
+ {
+ case VK_MENU:
+ return 0;
+ case VK_F10:
+ return 0;
+ default: break;
+ }
+ }
+ case WM_SHOWWINDOW:
+ {
+ break;
+ }
+
+ case WM_CREATE:
+ {
+ GetDeviceGammaRamp( GetDC( GetDesktopWindow( ) ), DesktopGammaRamp );
+ bMouseInWindow = false;
+ break;
+ }
+
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+ case WM_MOUSELEAVE:
+ bMouseInWindow = false;
+ GetInputManager()->GetFEMouse()->getCursor()->SetVisible( false );
+ break;
+
+ case WM_NCMOUSEMOVE:
+ {
+ POINT pPoint;
+ GetCursorPos( &pPoint );
+
+ // Convert the absolute screen coordinates from windows to client window absolute coordinates.
+ ScreenToClient( hwnd, &pPoint );
+
+ RECT clientRect;
+ GetClientRect( hwnd, &clientRect );
+
+ GetInputManager()->GetFEMouse()->Move( pPoint.x, pPoint.y, clientRect.right, clientRect.bottom );
+
+ ShowTheCursor( true );
+ break;
+ }
+ case WM_MOUSEMOVE:
+ {
+ POINT pPoint;
+ pPoint.x = LOWORD(lParam);
+ pPoint.y = HIWORD(lParam);
+
+ // For some reason beyond my comprehension WM_MOUSEMOVE seems to be getting called regardless if the
+ // mouse moved or not. So let the FEMouse determine if we moved.
+ FEMouse* pFEMouse = GetInputManager()->GetFEMouse();
+ if( pFEMouse->DidWeMove( pPoint.x, pPoint.y ) )
+ {
+ RECT clientRect;
+ GetClientRect( hwnd, &clientRect );
+ pFEMouse->Move( pPoint.x, pPoint.y, clientRect.right, clientRect.bottom );
+ }
+
+ ShowTheCursor( false );
+
+ if( !bMouseInWindow )
+ {
+ bMouseInWindow = true;
+ mouseEventTracker.cbSize = sizeof(MOUSETRACKER);
+ mouseEventTracker.dwFlags = TIMER_LEAVE;
+ mouseEventTracker.hwndTrack = hwnd;
+ if( !TrackMouseEvent( &mouseEventTracker ) )
+ {
+ rDebugPrintf( "TrackMouseEvent Failed" );
+ }
+ }
+
+ break;
+ }
+
+ case WM_LBUTTONDOWN:
+ {
+ GetInputManager()->GetFEMouse()->ButtonDown( BUTTON_LEFT );
+ // rDebugPrintf("LEFT MOUSE BUTTON PRESSED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! \n");
+ break;
+ }
+ case WM_LBUTTONUP:
+ {
+ GetInputManager()->GetFEMouse()->ButtonUp( BUTTON_LEFT );
+ break;
+ }
+ case WM_SYSCOMMAND:
+ {
+ switch (wParam)
+ {
+ case SC_SCREENSAVE: // Screensaver Trying To Start?
+ case SC_MONITORPOWER: // Monitor Trying To Enter Powersave?
+ return 0;
+ }
+ break;
+ }
+
+ // PDDI will sent this message to enable or disable rendering in response to an
+ // application level window event. For example, if the user clicks away from
+ // the rendering window, or uses ALT-TAB to select another application, PDDI
+ // will tell sent a WM_PDDI_DRAW_ENABLE(0) message. When the application
+ // regains focus, WM_PDDI_DRAW_ENABLE(1) will be sent.
+ case WM_PDDI_DRAW_ENABLE:
+ //GetApplication()->EnableRendering(wParam == 1);
+ break;
+
+ case WM_CHAR:
+ {
+ break;
+ }
+ default:
+ break;
+ }
+
+ return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
diff --git a/game/code/main/win32platform.h b/game/code/main/win32platform.h
new file mode 100644
index 0000000..9be1e57
--- /dev/null
+++ b/game/code/main/win32platform.h
@@ -0,0 +1,176 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Win32Platform
+//
+// Description: Abstracts the differences for setting up and shutting down
+// the different platforms.
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef WIN32PLATFORM_H
+#define WIN32PLATFORM_H
+
+//========================================
+// System Includes
+//========================================
+
+#include <windows.h>
+
+//========================================
+// Nested Includes
+//========================================
+#include "platform.h" // base class
+#include <data/config/gameconfig.h> // interface
+
+//========================================
+// Forward References
+//========================================
+struct IRadMemoryHeap;
+class tPlatform;
+class tContext;
+
+
+struct MOUSETRACKER
+{
+ DWORD cbSize;
+ DWORD dwFlags;
+ HWND hwndTrack;
+};
+
+//=============================================================================
+//
+// Synopsis: Provides abstraction for setting up and closing a win32 exe.
+//
+//=============================================================================
+class Win32Platform : public Platform, public GameConfigHandler
+{
+public:
+
+ enum Resolution
+ {
+ Res_640x480 = 0,
+ Res_800x600,
+ Res_1024x768,
+ Res_1152x864,
+ Res_1280x1024,
+ Res_1600x1200
+ };
+
+public:
+
+ // Static Methods for accessing this singleton.
+ static Win32Platform* CreateInstance( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow );
+ static Win32Platform* GetInstance();
+ static void DestroyInstance();
+
+ // Had to workaround our nice clean design cause FTech must be init'ed
+ // before anything else is done.
+ static bool InitializeWindow( HINSTANCE hInstance );
+ static void InitializeFoundation();
+ static void InitializeMemory();
+ static void ShutdownMemory();
+
+ // Implement Platform interface.
+ virtual void InitializePlatform();
+ virtual void ShutdownPlatform();
+
+ virtual void LaunchDashboard();
+ virtual void ResetMachine();
+
+ virtual void DisplaySplashScreen( SplashScreen screenID,
+ const char* overlayText = NULL,
+ float fontScale = 1.0f,
+ float textPosX = 0.0f,
+ float textPosY = 0.0f,
+ tColour textColour = tColour( 255,255,255 ),
+ int fadeFrames = 3 );
+
+ virtual void DisplaySplashScreen( const char* textureName,
+ const char* overlayText = NULL,
+ float fontScale = 1.0f,
+ float textPosX = 0.0f,
+ float textPosY = 0.0f,
+ tColour textColour = tColour( 255,255,255 ),
+ int fadeFrames = 3 );
+
+ virtual bool OnDriveError( radFileError error, const char* pDriveName, void* pUserData );
+ virtual void OnControllerError(const char *msg);
+
+ HWND GetHwnd() const { return mhWnd; }
+
+ // Set the resolution of the display, or increase the size of the window.
+ // Returns true if the change was successful, false if resolution is not supported.
+ bool SetResolution( Resolution res, int bpp, bool fullscreen );
+
+ Resolution GetResolution() const;
+ int GetBPP() const;
+ bool IsFullscreen() const;
+
+ // Implementation of the GameConfigHandler interface
+ virtual const char* GetConfigName() const;
+ virtual int GetNumProperties() const;
+ virtual void LoadDefaults();
+ virtual void LoadConfig( ConfigString& config );
+ virtual void SaveConfig( ConfigString& config );
+
+private:
+
+ // Constructors, Destructors, and Operators
+ Win32Platform( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow );
+ virtual ~Win32Platform();
+
+ // Unused Constructors, Destructors, and Operators
+ Win32Platform( const Win32Platform& aPlatform );
+ Win32Platform& operator=( const Win32Platform& aPlatform );
+
+ // Methods from Platform
+ virtual void InitializeFoundationDrive();
+ virtual void ShutdownFoundation();
+
+ virtual void InitializePure3D();
+ virtual void ShutdownPure3D();
+
+ // Initializes the d3d context
+ void InitializeContext();
+
+ // Resolution related methods
+ static void TranslateResolution( Resolution res, int&x, int&y );
+ bool IsResolutionSupported( Resolution res, int bpp ) const;
+
+ // Mouse tracking method
+ static TrackMouseEvent( MOUSETRACKER* pMouseEventTracker );
+ static void CALLBACK TrackMouseTimerProc( HWND hWnd, UINT uMsg, UINT idEvent,DWORD dwTime );
+
+ // Windows methods.
+ void ResizeWindow();
+ static void ShowTheCursor( bool show );
+ static LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam );
+
+private:
+
+ // Pointer to the one and only instance of this singleton.
+ static Win32Platform* spInstance;
+
+ // Private Attributes
+ // Had to make these static because of the initialization order problem.
+ static HINSTANCE mhInstance;
+ static HWND mhWnd;
+ static HANDLE mhMutex;
+ static bool mShowCursor;
+
+ // Pure 3D attributes
+ tPlatform* mpPlatform;
+ tContext* mpContext;
+
+ // window properties.
+ Resolution mResolution;
+ int mbpp;
+ bool mFullscreen;
+ int mScreenWidth;
+ int mScreenHeight;
+};
+
+#endif // WIN32PLATFORM_H
diff --git a/game/code/main/xboxmain.cpp b/game/code/main/xboxmain.cpp
new file mode 100644
index 0000000..fca821a
--- /dev/null
+++ b/game/code/main/xboxmain.cpp
@@ -0,0 +1,236 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: xboxmain.cpp
+//
+// Description: This file contains the main enrty point to the game.
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Standard Library
+#include <string.h>
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radobject.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <main/game.h>
+#include <main/xboxplatform.h>
+#include <main/singletons.h>
+#include <main/commandlineoptions.h>
+#include <memory/memoryutilities.h>
+#include <memory/srrmemory.h>
+
+//========================================
+// Forward Declarations
+//========================================
+static void ProcessCommandLineArguments();
+
+static void ProcessCommandLineArgumentsFromFile();
+
+
+//=============================================================================
+// Function: main
+//=============================================================================
+//
+// Description: Main entry point.
+//
+// Parameters: None.
+//
+// Returns: None.
+//
+//=============================================================================
+void main()
+{
+ //
+ // Pick out and store command line settings.
+ //
+ CommandLineOptions::InitDefaults();
+ ProcessCommandLineArguments();
+
+ //
+ // Have to get FTech setup first so that we can use all the memory services.
+ //
+ XboxPlatform::InitializeFoundation();
+
+ srand (Game::GetRandomSeed ());
+
+
+ // Now disable the default heap
+ //
+#ifndef RAD_RELEASE
+ tName::SetAllocator (GMA_DEBUG);
+
+ //g_HeapActivityTracker.EnableHeapAllocs (GMA_DEFAULT, false);
+ //g_HeapActivityTracker.EnableHeapFrees (GMA_DEFAULT, false);
+#endif
+
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ //Process any commandline options from the command.txt file
+ ProcessCommandLineArgumentsFromFile();
+
+
+ //
+ // Instantiate all the singletons before doing anything else.
+ //
+ CreateSingletons();
+
+ //
+ // Construct the platform object.
+ //
+ XboxPlatform* pPlatform = XboxPlatform::CreateInstance();
+ rAssert( pPlatform != NULL );
+
+ //
+ // Create the game object.
+ //
+ Game* pGame = Game::CreateInstance( pPlatform );
+ rAssert( pGame != NULL );
+
+
+ //
+ // Initialize the game.
+ //
+ pGame->Initialize();
+
+ HeapMgr()->PopHeap (GMA_PERSISTENT);
+
+ //
+ // Run it! Control will not return from here until the game is stopped.
+ //
+ pGame->Run();
+
+ //
+ // Terminate the game (this frees all resources allocated by the game).
+ //
+ pGame->Terminate();
+
+ //
+ // Destroy the game object.
+ //
+ Game::DestroyInstance();
+
+ //
+ // Destroy the game and platform (do it in this order in case the game's
+ // destructor references the platform.
+ //
+ XboxPlatform::DestroyInstance();
+
+ //
+ // Show some debug output
+ //
+#ifdef RAD_DEBUG
+ radObject::DumpObjects();
+#endif;
+
+ //
+ // Dump all the singletons.
+ //
+ DestroySingletons();
+}
+
+
+//=============================================================================
+// Function: ProcessCommandLineArguments
+//=============================================================================
+//
+// Description: Pick out the command line options and store them.
+//
+// Parameters: None.
+//
+// Returns: None.
+//
+//=============================================================================
+void ProcessCommandLineArguments()
+{
+ //
+ // None of this gets into the shipping code.
+ //
+#ifndef FINAL
+
+ DWORD dwLaunchMethod;
+ LAUNCH_DATA launchData;
+
+ XGetLaunchInfo( &dwLaunchMethod, &launchData );
+
+ if( LDT_FROM_DEBUGGER_CMDLINE == dwLaunchMethod )
+ {
+ LD_FROM_DEBUGGER_CMDLINE* pCmdLine = (LD_FROM_DEBUGGER_CMDLINE*)&launchData;
+
+ char* argument;
+ argument = strtok( pCmdLine->szCmdLine, " " );
+
+ rDebugPrintf( "*************************************************************************\n" );
+ rDebugPrintf( "Command Line Args:\n" );
+
+ //
+ // Pick out all the command line options and store them in GameDB.
+ // Also dump them to the output for handy dandy viewing.
+ //
+ int i = 0;
+ while( NULL != argument )
+ {
+ rDebugPrintf( "arg%d: %s\n", i++, argument );
+
+ CommandLineOptions::HandleOption( argument );
+
+ argument = strtok( NULL, " " );
+ }
+
+ if( !CommandLineOptions::Get( CLO_ART_STATS ) )
+ {
+ //CommandLineOptions::HandleOption( "noheaps" );
+ }
+
+
+
+
+
+ rDebugPrintf( "*************************************************************************\n" );
+ }
+
+#endif // FINAL
+}
+
+
+
+void ProcessCommandLineArgumentsFromFile()
+{
+#ifndef FINAL
+
+ //Chuck: looking for additional command line args being passed in from a file
+ //its for QA testing etc.
+
+ IRadFile* pfile =NULL;
+
+ ::radFileOpenSync(&pfile,"command.txt", false, OpenExisting );
+
+ if (pfile != NULL)
+ {
+ char commandlinestring [256];
+ memset( commandlinestring, 0, sizeof( commandlinestring ) );
+
+ unsigned int fileSize = 0;
+ pfile->GetSizeSync( &fileSize );
+ pfile->ReadSync(commandlinestring, fileSize);
+
+ //QA created the command line file and wants to pass additional arguements
+
+ char* argument = strtok(commandlinestring," ");
+ while (argument != NULL)
+ {
+ CommandLineOptions::HandleOption(argument);
+ argument=strtok(NULL," ");
+ }
+ pfile->Release();
+ }
+#endif //FINAL
+} //end of Function
diff --git a/game/code/main/xboxplatform.cpp b/game/code/main/xboxplatform.cpp
new file mode 100644
index 0000000..e71c7d0
--- /dev/null
+++ b/game/code/main/xboxplatform.cpp
@@ -0,0 +1,1315 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: XboxPlatform
+//
+// Description: Abstracts the differences for setting up and shutting down
+// the different platforms.
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//===========================================================================
+
+//========================================
+// System Includes
+//========================================
+// Standard Lib
+#include <stdlib.h>
+#include <string.h>
+// Pure 3D
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/anim/expression.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/anim/polyskin.hpp>
+#include <p3d/anim/sequencer.hpp>
+#include <p3d/anim/skeleton.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/gameattr.hpp>
+#include <p3d/image.hpp>
+#include <p3d/imagefont.hpp>
+#include <p3d/light.hpp>
+#include <p3d/locator.hpp>
+#include <p3d/platform.hpp>
+#include <p3d/scenegraph/scenegraph.hpp>
+#include <p3d/sprite.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/texture.hpp>
+#include <p3d/file.hpp>
+#include <p3d/shader.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/memory.hpp>
+#include <p3d/bmp.hpp>
+#include <p3d/png.hpp>
+#include <p3d/targa.hpp>
+#include <p3d/font.hpp>
+#include <p3d/texturefont.hpp>
+#include <p3d/unicode.hpp>
+// Pure 3D: Loader-specific
+#include <render/Loaders/GeometryWrappedLoader.h>
+#include <render/Loaders/StaticEntityLoader.h>
+#include <render/Loaders/StaticPhysLoader.h>
+#include <render/Loaders/TreeDSGLoader.h>
+#include <render/Loaders/FenceLoader.h>
+#include <render/Loaders/IntersectLoader.h>
+#include <render/Loaders/AnimCollLoader.h>
+#include <render/Loaders/AnimDSGLoader.h>
+#include <render/Loaders/DynaPhysLoader.h>
+#include <render/Loaders/InstStatPhysLoader.h>
+#include <render/Loaders/InstStatEntityLoader.h>
+#include <render/Loaders/WorldSphereLoader.h>
+#include <loading/roaddatasegmentloader.h>
+#include <render/Loaders/BillboardWrappedLoader.h>
+#include <render/Loaders/instparticlesystemloader.h>
+#include <render/Loaders/breakableobjectloader.h>
+#include <render/Loaders/AnimDynaPhysLoader.h>
+#include <render/Loaders/lensflareloader.h>
+#include <p3d/shadow.hpp>
+#include <p3d/anim/animatedobject.hpp>
+#include <p3d/effects/particleloader.hpp>
+#include <p3d/effects/opticloader.hpp>
+#include <p3d/anim/vertexanimkey.hpp>
+#include <stateprop/statepropdata.hpp>
+
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radthread.hpp>
+#include <radplatform.hpp>
+#include <radtime.hpp>
+#include <radmemorymonitor.hpp>
+#include <raddebugcommunication.hpp>
+#include <raddebugwatch.hpp>
+#include <radfile.hpp>
+#include <radmovie2.hpp>
+
+//This is so we can get the name of the file that's failing.
+#include <../src/radfile/common/requests.hpp>
+
+// sim - for InstallSimLoaders
+#include <simcommon/simutility.hpp>
+
+// To turn off movies during an error.
+#include <presentation/fmvplayer/fmvplayer.h>
+#include <presentation/presentation.h>
+#include <presentation/language.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <input/inputmanager.h>
+#include <main/xboxplatform.h>
+#include <main/commandlineoptions.h>
+#include <main/game.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/Loaders/AllWrappers.h>
+#include <memory/srrmemory.h>
+
+#include <loading/locatorloader.h>
+#include <loading/cameradataloader.h>
+#include <loading/roadloader.h>
+#include <loading/pathloader.h>
+#include <loading/intersectionloader.h>
+#include <loading/roaddatasegmentloader.h>
+#include <atc/atcloader.h>
+#include <debug/debuginfo.h>
+#include <constants/srrchunks.h>
+
+#include <radload/radload.hpp>
+
+#include <main/errorsxbox.h>
+
+#include <sound/soundmanager.h>
+
+#include <presentation/presentation.h>
+#include <presentation/gui/guitextbible.h>
+#include <data/gamedatamanager.h>
+
+#define XBOX_SECTION "XBOX_SECTION"
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+XboxPlatform* XboxPlatform::spInstance = NULL;
+
+//The Adlib font. <sigh>
+unsigned char gFont[] =
+#include <font/defaultfont.h>
+
+//
+// The depth of the rendering area. This value only has an effect
+// when Pure3D has taken over the entire display. When running in
+// a window on the desktop, Pure3D uses the same bit depth as the
+// desktop. Pure3D only supports 16, and 32 rendering depths.
+//
+static const int WindowBPP = 32;
+
+void LoadMemP3DFile( unsigned char* buffer, unsigned int size, tEntityStore* store )
+{
+ tFileMem* file = new tFileMem(buffer,size);
+ file->AddRef();
+ file->SetFilename("memfile.p3d");
+ p3d::loadManager->GetP3DHandler()->Load( file, p3d::inventory );
+ file->Release();
+}
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// XboxPlatform::CreateInstance
+//==============================================================================
+//
+// Description: Creates the XboxPlatform.
+//
+// Parameters: None.
+//
+// Return: Pointer to the XboxPlatform.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+XboxPlatform* XboxPlatform::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "XboxPlatform" );
+ rAssert( spInstance == NULL );
+
+ spInstance = new(GMA_PERSISTENT) XboxPlatform;
+ rAssert( spInstance );
+MEMTRACK_POP_GROUP( "XboxPlatform" );
+
+ return spInstance;
+}
+
+//==============================================================================
+// XboxPlatform::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the XboxPlatform singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the XboxPlatform.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+XboxPlatform* XboxPlatform::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// XboxPlatform::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the XboxPlatform.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void XboxPlatform::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+}
+
+
+
+//==============================================================================
+// XboxPlatform::InitializeFoundation
+//==============================================================================
+// Description: FTech must be setup first so that all the memory services
+// are ready to go before we begin allocating anything.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: The FTech systems must be initialized in a particular order.
+// Consult their documentation before changing.
+//
+//==============================================================================
+void XboxPlatform::InitializeFoundation()
+{
+ //
+ // Initialize the memory heaps
+ //
+ XboxPlatform::InitializeMemory();
+
+#ifndef FINAL
+ //
+ // Register an out-of-memory display handler in case something goes bad
+ // while allocating the heaps
+ //
+ ::radMemorySetOutOfMemoryCallback( PrintOutOfMemoryMessage, NULL );
+#endif
+
+ //
+ // Initialize memory monitor by JamesCo. TM.
+ //
+ if( CommandLineOptions::Get( CLO_MEMORY_MONITOR ) )
+ {
+ const int KB = 1024;
+ ::radMemoryMonitorInitialize( 64 * KB, GMA_DEBUG );
+ }
+
+ // Setup the memory heaps
+ //
+ HeapMgr()->PrepareHeapsStartup ();
+
+ // Seed the heap stack
+ //
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ //
+ // Initilalize the platform system
+ //
+ ::radPlatformInitialize();
+
+ //
+ // Initialize the timer system
+ //
+ ::radTimeInitialize();
+
+ //
+ // Initialize the debug communication system.
+ //
+ ::radDbgComTargetInitialize( WinSocket,
+ radDbgComDefaultPort, // Default
+ NULL, // Default
+ GMA_DEBUG );
+
+
+ //
+ // Initialize the Watcher.
+ //
+ ::radDbgWatchInitialize( "SRR2",
+ 32 * 16384, // 2 * Default
+ GMA_DEBUG );
+
+ //
+ // Initialize the file system.
+ //
+ ::radFileInitialize( 50, // Default
+ 32, // Default
+ GMA_PERSISTENT );
+
+ ::radLoadInitialize();
+ //radLoad->SetSyncLoading( true );
+
+ ::radDriveMount(0, GMA_PERSISTENT);
+
+ //
+ // Initialize the new movie player
+ //
+ ::radMovieInitialize2( GMA_PERSISTENT );
+
+ HeapMgr()->PopHeap (GMA_PERSISTENT);
+}
+
+//==============================================================================
+// XboxPlatform::InitializeMemory
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void XboxPlatform::InitializeMemory()
+{
+ //
+ // Only do this once!
+ //
+ if( gMemorySystemInitialized == true )
+ {
+ return;
+ }
+
+ gMemorySystemInitialized = true;
+
+ //
+ // Initialize the thread system.
+ //
+ ::radThreadInitialize();
+
+ //
+ // Initialize the memory system.
+ //
+ ::radMemoryInitialize();
+}
+
+
+
+
+//==============================================================================
+// XboxPlatform::InitializePlatform
+//==============================================================================
+// Description: Get the Xbox ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void XboxPlatform::InitializePlatform()
+{
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ //
+ // Rendering is good.
+ //
+ InitializePure3D();
+
+ //
+ // Add anything here that needs to be before the drive is opened.
+ //
+ DisplaySplashScreen( Error ); // blank screen
+
+ //
+ // Opening the drive is SLOW...
+ //
+ InitializeFoundationDrive();
+
+ //
+ // Initialize the controller.
+ //
+ GetInputManager()->Init();
+
+#ifndef RAD_RELEASE
+
+ //
+ // Display Title IP address.
+ //
+ XNADDR addr;
+ XNetGetTitleXnAddr( &addr );
+
+ const int BUFFER_SIZE = 256;
+ char ip[BUFFER_SIZE];
+ XNetInAddrToString( addr.ina, ip, BUFFER_SIZE );
+ rDebugString( ip );
+ rDebugString( "\n" );
+
+#endif // FINAL
+
+ HeapMgr()->PopHeap (GMA_PERSISTENT);
+
+}
+
+
+//==============================================================================
+// XboxPlatform::ShutdownPlatform
+//==============================================================================
+// Description: Shut down the PS2.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void XboxPlatform::ShutdownPlatform()
+{
+ ShutdownPure3D();
+ ShutdownFoundation();
+}
+
+//=============================================================================
+// XboxPlatform::ResetMachine
+//=============================================================================
+// Description: resets the xbox
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void XboxPlatform::ResetMachine()
+{
+ DWORD dwLaunchMethod;
+ LAUNCH_DATA launchData;
+
+ XGetLaunchInfo( &dwLaunchMethod, &launchData );
+
+ char build = 0;
+
+#ifdef RAD_DEBUG
+ build = 'd';
+#elif defined RAD_TUNE
+ build = 't';
+#else
+ build = 'r';
+#endif
+
+ char imageName[64];
+
+ sprintf( imageName, "D:\\srr2x%c.xbe", build );
+
+ XLaunchNewImage( imageName, &launchData );
+
+ //If it failed, try launching default.xbe
+ XLaunchNewImage( "D:\\default.xbe", &launchData );
+
+ //
+ // The game exits, we should never reach this point
+ //
+ rAssertMsg( false, "What, you're still here?" );
+}
+//=============================================================================
+// XboxPlatform::LaunchDashboard
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void XboxPlatform::LaunchDashboard()
+{
+
+
+ GetLoadingManager()->CancelPendingRequests();
+
+ //TODO: Make sure sounds shut down too.
+ GetSoundManager()->SetMasterVolume( 0.0f );
+
+ DisplaySplashScreen( FadeToBlack );
+
+ GetPresentationManager()->StopAll();
+
+ //Oh boy.
+ GameDataManager::DestroyInstance(); //Get rid of memcards
+
+
+ p3d::loadManager->CancelAll();
+
+ SoundManager::DestroyInstance();
+
+ ShutdownPlatform();
+ /* ResetMachine();*/ // want to go to dashboard instead
+
+ LD_LAUNCH_DASHBOARD dashboardInfo;
+
+ dashboardInfo.dwReason = XLD_LAUNCH_DASHBOARD_MEMORY;
+ dashboardInfo.dwContext = 0;
+ dashboardInfo.dwParameter1 = 'U'; // go directly to hard disk save game menu
+ dashboardInfo.dwParameter2 = 3; // number of blocks required for a single save game
+
+ XLaunchNewImage( NULL, reinterpret_cast<PLAUNCH_DATA>(&dashboardInfo) );
+
+ //
+ // The game exits, we should never reach this point
+ //
+ rAssertMsg( false, "What, you're still here?" );
+
+}
+
+
+//=============================================================================
+// XboxPlatform::DisplaySplashScreen
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( SplashScreen screenID,
+// const char* overlayText = NULL,
+// float fontScale = 1.0f,
+// float textPosX = 0.0f,
+// float textPosY = 0.0f,
+// tColour textColour,
+// int fadeFrames = 3 )
+//
+// Return: void
+//
+//=============================================================================
+void XboxPlatform::DisplaySplashScreen( SplashScreen screenID,
+ const char* overlayText,
+ float fontScale,
+ float textPosX,
+ float textPosY,
+ tColour textColour,
+ int fadeFrames )
+{
+ HeapMgr()->PushHeap( GMA_TEMP );
+
+ p3d::inventory->PushSection();
+ p3d::inventory->AddSection( XBOX_SECTION );
+ p3d::inventory->SelectSection( XBOX_SECTION );
+
+ P3D_UNICODE unicodeText[256];
+
+ // Save the current Projection mode so I can restore it later
+ pddiProjectionMode pm = p3d::pddi->GetProjectionMode();
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_DEVICE);
+
+ pddiCullMode cm = p3d::pddi->GetCullMode();
+ p3d::pddi->SetCullMode(PDDI_CULL_NONE);
+
+
+ //CREATE THE FONT
+ tTextureFont* thisFont = NULL;
+
+ // Convert memory buffer into a texturefont.
+ //
+ //p3d::load(gFont, DEFAULTFONT_SIZE, GMA_TEMP);
+ LoadMemP3DFile( gFont, DEFAULTFONT_SIZE, p3d::inventory );
+
+ thisFont = p3d::find<tTextureFont>("adlibn_20");
+ rAssert( thisFont );
+
+ thisFont->AddRef();
+ tShader* fontShader = thisFont->GetShader();
+ //fontShader->SetInt( )
+
+
+ p3d::AsciiToUnicode( overlayText, unicodeText, 256 );
+
+ // Make the missing letter into somthing I can see
+ //
+ thisFont->SetMissingLetter(p3d::ConvertCharToUnicode('j'));
+
+ int a = 0;
+
+ do
+ {
+ p3d::pddi->SetColourWrite(true, true, true, true);
+ p3d::pddi->SetClearColour( pddiColour(0,0,0) );
+ p3d::pddi->BeginFrame();
+ p3d::pddi->Clear(PDDI_BUFFER_COLOUR);
+
+ //This is for fading in the font and shaders.
+ int bright = 255;
+ if (a < fadeFrames) bright = (a * 255) / fadeFrames;
+ if ( bright > 255 ) bright = 255;
+ tColour c(bright, bright, bright, 255);
+
+ //Display font
+ if (overlayText != NULL)
+ {
+ tColour colour = textColour;
+ colour.SetAlpha( bright );
+
+ thisFont->SetColour( colour );
+
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_ORTHOGRAPHIC);
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+
+ p3d::stack->Translate( textPosX, textPosY, 1.5f);
+ float scaleSize = 1.0f / 480.0f; //This is likely good for 528 also.
+ p3d::stack->Scale(scaleSize * fontScale, scaleSize* fontScale , 1.0f);
+
+ if ( textPosX != 0.0f || textPosY != 0.0f )
+ {
+ thisFont->DisplayText( unicodeText );
+ }
+ else
+ {
+ thisFont->DisplayText( unicodeText, 3 );
+ }
+
+ p3d::stack->Pop();
+ }
+
+ p3d::pddi->EndFrame();
+ p3d::context->SwapBuffers();
+
+ ++a;
+
+ } while (a <= fadeFrames);
+
+ // [ps]: flush out this screen now.
+ if ( screenID == FadeToBlack )
+ {
+ p3d::pddi->Clear(PDDI_BUFFER_ALL);
+ }
+
+ p3d::pddi->SetCullMode(cm);
+ p3d::pddi->SetProjectionMode(pm);
+
+ //Should do this after a vsync.
+ thisFont->Release();
+
+ p3d::inventory->RemoveSectionElements(XBOX_SECTION);
+ p3d::inventory->DeleteSection(XBOX_SECTION);
+ p3d::inventory->PopSection();
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+}
+
+
+//=============================================================================
+// XboxPlatform::DisplaySplashScreen
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* textureName,
+// const char* overlayText = NULL,
+// float fontScale = 1.0f,
+// float textPosX = 0.0f,
+// float textPosY = 0.0f,
+// tColour textColour,
+// int fadeFrames = 3 )
+//
+// Return: void
+//
+//=============================================================================
+void XboxPlatform::DisplaySplashScreen( const char* textureName,
+ const char* overlayText,
+ float fontScale,
+ float textPosX,
+ float textPosY,
+ tColour textColour,
+ int fadeFrames )
+{
+}
+
+
+//=============================================================================
+// XboxPlatform::OnDriveError
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( radFileError error, const char* pDriveName, void* pUserData )
+//
+// Return: bool
+//
+//=============================================================================
+bool XboxPlatform::OnDriveError( radFileError error, const char* pDriveName, void* pUserData )
+{
+ bool inFrame = p3d::context->InFrame();
+
+ const int NUM_RADFILE_ERRORS = 13;
+ unsigned int errorIndex = error;
+
+#ifdef PAL
+ switch( CGuiTextBible::GetCurrentLanguage() )
+ {
+ case Scrooby::XL_FRENCH:
+ {
+ errorIndex += 1 * NUM_RADFILE_ERRORS;
+
+ break;
+ }
+ case Scrooby::XL_GERMAN:
+ {
+ errorIndex += 2 * NUM_RADFILE_ERRORS;
+
+ break;
+ }
+ case Scrooby::XL_SPANISH:
+ {
+ errorIndex += 3 * NUM_RADFILE_ERRORS;
+
+ break;
+ }
+ default:
+ {
+ if ( !CGuiTextBible::IsTextBibleLoaded() )
+ {
+ switch ( Language::GetHardwareLanguage() )
+ {
+ case Language::FRENCH:
+ {
+ errorIndex += 1 * NUM_RADFILE_ERRORS;
+ break;
+ }
+ case Language::GERMAN:
+ {
+ errorIndex += 2 * NUM_RADFILE_ERRORS;
+ break;
+ }
+ case Language::SPANISH:
+ {
+ errorIndex += 3 * NUM_RADFILE_ERRORS;
+ break;
+ }
+ }
+ }
+ break;
+ }
+ }
+#endif // PAL
+
+ rAssert( errorIndex < sizeof( ERROR_STRINGS ) / sizeof( ERROR_STRINGS[ 0 ] ) );
+
+ switch ( error )
+ {
+ case Success:
+ {
+ if ( mErrorState != NONE )
+ {
+ if ( inFrame ) p3d::context->EndFrame( true );
+ DisplaySplashScreen( FadeToBlack );
+ if ( inFrame ) p3d::context->BeginFrame( );
+ mErrorState = NONE;
+ mPauseForError = false;
+ }
+
+ if ( GetPresentationManager()->GetFMVPlayer()->IsPlaying() )
+ {
+ GetPresentationManager()->GetFMVPlayer()->UnPause( );
+ }
+ else
+ {
+ GetSoundManager()->ResumeAfterMovie();
+ }
+ return true;
+ break;
+ }
+ case FileNotFound:
+ {
+ if ( CommandLineOptions::Get( CLO_FILE_NOT_FOUND ) )
+ {
+ rAssert( pUserData != NULL );
+
+ radFileRequest* request = static_cast<radFileRequest*>( pUserData );
+ const char* fileName = request->GetFilename();
+
+ //Get rid of the slashes.
+ unsigned int i;
+ unsigned int lastIndex = 0;
+ for ( i = 0; i < strlen( fileName ); ++i )
+ {
+ if ( fileName[ i ] == '\\' )
+ {
+ lastIndex = i;
+ }
+ }
+
+ unsigned int adjustedIndex = lastIndex == 0 ? lastIndex : lastIndex + 1;
+
+ char adjustedName[32];
+ strncpy( adjustedName, &fileName[adjustedIndex], ( strlen( fileName ) - lastIndex ) );
+ adjustedName[ strlen( fileName ) - lastIndex ] = '\0';
+
+ char errorString[256];
+ sprintf( errorString, "%s:\n%s", ERROR_STRINGS[errorIndex], adjustedName );
+ if ( inFrame ) p3d::context->EndFrame( true );
+ DisplaySplashScreen( Error, errorString, 1.0f, 0.0f, 0.0f, tColour(255, 255, 255), 0 );
+ if ( inFrame ) p3d::context->BeginFrame( );
+ mErrorState = P_ERROR;
+ mPauseForError = true;
+
+ if ( GetPresentationManager()->GetFMVPlayer()->IsPlaying() )
+ {
+ GetPresentationManager()->GetFMVPlayer()->Pause( );
+ }
+ else
+ {
+ GetSoundManager()->StopForMovie();
+ }
+ return true;
+ }
+ else
+ {
+ error = HardwareFailure;
+ //Fall through.
+ }
+ }
+ case ShellOpen:
+ case WrongMedia:
+ case NoMedia:
+ case HardwareFailure:
+ {
+ //This could be the wrong disc.
+ if ( inFrame ) p3d::context->EndFrame( true );
+ DisplaySplashScreen( Error, ERROR_STRINGS[errorIndex], 1.0f, 0.0f, 0.0f, tColour(255, 255, 255), 0 );
+ if ( inFrame ) p3d::context->BeginFrame( );
+ mErrorState = P_ERROR;
+ mPauseForError = true;
+
+ if ( GetPresentationManager()->GetFMVPlayer()->IsPlaying() )
+ {
+ GetPresentationManager()->GetFMVPlayer()->Pause( );
+ }
+ else
+ {
+ GetSoundManager()->StopForMovie();
+ }
+ return true;
+ }
+ default:
+ {
+ //Others are not supported.
+ rAssert( false );
+ }
+ }
+
+ return false;
+}
+
+void XboxPlatform::OnControllerError(const char *msg)
+{
+ bool inFrame = p3d::context->InFrame();
+
+ if ( inFrame ) p3d::context->EndFrame( true );
+ DisplaySplashScreen( Error, msg, 0.7f, 0.0f, 0.0f, tColour(255, 255, 255), 0 );
+ if ( inFrame ) p3d::context->BeginFrame( );
+ mErrorState = CTL_ERROR;
+ mPauseForError = true;
+
+ if ( GetPresentationManager()->GetFMVPlayer()->IsPlaying() )
+ {
+ GetPresentationManager()->GetFMVPlayer()->Pause( );
+ }
+ else
+ {
+ GetSoundManager()->StopForMovie();
+ }
+}
+
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// XboxPlatform::InitializeFoundationDrive
+//==============================================================================
+// Description: Get FTech ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: The FTech systems must be initialized in a particular order.
+// Consult their documentation before changing.
+//
+//==============================================================================
+void XboxPlatform::InitializeFoundationDrive()
+{
+ //
+ // No shadow drive on the Xbox.
+ //
+// ::radDriveSetShadow( false );
+
+ //
+ // Get the CDROM drive and hold it open for the life of the game.
+ // This is a costly operation so we only want to do it once.
+ //
+
+ ::radDriveOpen( &mpIRadDrive,
+ "D:",
+ NormalPriority, // Default
+ GMA_PERSISTENT );
+
+ rAssert( mpIRadDrive != NULL );
+
+ mpIRadDrive->RegisterErrorHandler( this, NULL );
+
+ //
+ // Set the read-write granulatity to prevent operations from
+ // being partitioned.
+ //
+// const int GRANULARITY = 20 * 1024 * 1024;
+// mpIRadDrive->SetReadWriteGranularity( GRANULARITY );
+}
+
+
+//==============================================================================
+// XboxPlatform::ShutdownFoundation
+//==============================================================================
+// Description: Shut down Foundation Tech.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: The FTech systems must be terminated in the reverse order that
+// they were initialized in.
+//
+//==============================================================================
+void XboxPlatform::ShutdownFoundation()
+{
+ //
+ // Release the drive we've held open since the begining.
+ //
+ mpIRadDrive->Release();
+ mpIRadDrive = NULL;
+
+ //
+ // Shutdown the systems in the reverse order.
+ //
+ ::radMovieTerminate2();
+ ::radDriveUnmount();
+ ::radLoadTerminate();
+ ::radFileTerminate();
+ ::radDbgWatchTerminate();
+ if( CommandLineOptions::Get( CLO_MEMORY_MONITOR ) )
+ {
+ ::radMemoryMonitorTerminate();
+ }
+ ::radDbgComTargetTerminate();
+ ::radTimeTerminate();
+ ::radPlatformTerminate();
+ ::radMemoryTerminate();
+ ::radThreadTerminate();
+}
+
+
+//==============================================================================
+// XboxPlatform::InitializePure3D
+//==============================================================================
+// Description: Get Pure3D ready to go.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void XboxPlatform::InitializePure3D()
+{
+MEMTRACK_PUSH_GROUP( "XboxPlatform" );
+// p3d::SetMemAllocator( p3d::ALLOC_DEFAULT, GMA_PERSISTENT );
+// p3d::SetMemAllocator( p3d::ALLOC_LOADED, GMA_LEVEL );
+
+ //
+ // Initialise Pure3D platform object.
+ // This call differs between different platforms. The Win32 version,
+ // for example requires the application instance to be passed in.
+ //
+ mpPlatform = tPlatform::Create();
+ rAssert( mpPlatform != NULL );
+
+ //
+ // Initialiase the Pure3D context object.
+ // We have to create on of these for every window, and for every PDDI
+ // instance we use for rendering. Since almost every application only
+ // uses one window and PDDI library at a time, we one need to create one
+ // context.
+ //
+ tContextInitData init;
+
+ //
+ // All applications should supply PDDI_BUFFER_COLOUR. PDDI_BUFFER_DEPTH
+ // specifies that we also want to allocate a Z-buffer. Some applications
+ // may want to also specifiy PDDI_BUFFER_STENCIL to allocate a stencil
+ // buffer.
+ //
+ init.bufferMask = PDDI_BUFFER_COLOUR | PDDI_BUFFER_DEPTH | PDDI_BUFFER_STENCIL;
+ init.enableSnapshot = true;
+
+ //
+ // These values only take effect in fullscreen mode. In windowed mode, the
+ // dimensions of the window define the rendering area. We'll define them
+ // anyway for completeness sake.
+ //
+ //
+// init.xsize = 1920;
+// init.ysize = 1080;
+ init.xsize = 720;
+ init.ysize = 480;
+ init.allowWidescreen = true;
+ init.allowHDTV = true;
+
+ //
+ // Depth of the rendering buffer. Again, this value only works in
+ // fullscreen mode. In window mode, the depth of the desktop is used.
+ // This value should be either 16 or 32. Depths of 4, 8, and 24 are not
+ // supported by most 3D hardware, and not by Pure3D.
+ //
+ init.bpp = WindowBPP;
+
+ //
+ // Select anti-aliasing mode.
+ //
+// init.antiAlias = tContextInitData::NONE;
+ init.antiAlias = tContextInitData::MULTISAMPLE_2_QUINCUNX;
+// init.antiAlias = tContextInitData::MULTISAMPLE_4_GAUSSIAN;
+
+ //TODO: Investigate VSync
+// init.lockToVsync = true;
+ init.lockToVsync = false;
+
+ //
+ // Create the context.
+ //
+ mpContext = mpPlatform->CreateContext( &init );
+ rAssert( mpContext != NULL );
+
+ //
+ // Assign this context to the platform.
+ //
+ mpPlatform->SetActiveContext( mpContext );
+ p3d::pddi->EnableZBuffer( true );
+
+ //
+ // This call installs chunk handlers for all the primary chunk types that
+ // Pure3D supports. This includes textures, materials, geometries, and the
+ // like.
+ //
+// p3d::InstallDefaultLoaders();
+ P3DASSERT(p3d::context);
+ tP3DFileHandler* p3d = new(GMA_PERSISTENT) tP3DFileHandler;
+// p3d::loadManager->AddHandler(p3d, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(p3d, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tPNGHandler, "png");
+
+ if( CommandLineOptions::Get( CLO_FE_UNJOINED ) )
+ {
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tBMPHandler, "bmp");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tTargaHandler, "tga");
+ }
+ else
+ {
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tBMPHandler, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tPNGHandler, "p3d");
+ p3d::context->GetLoadManager()->AddHandler(new(GMA_PERSISTENT) tTargaHandler, "p3d");
+ }
+
+// p3d->AddHandler(new tGeometryLoader);
+// GeometryWrappedLoader* pGWL = new GeometryWrappedLoader;
+ GeometryWrappedLoader* pGWL =
+ (GeometryWrappedLoader*)GetAllWrappers()->mpLoader( AllWrappers::msGeometry );
+ pGWL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pGWL );
+
+ StaticEntityLoader* pSEL =
+ (StaticEntityLoader*)GetAllWrappers()->mpLoader( AllWrappers::msStaticEntity );
+ pSEL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pSEL );
+
+ StaticPhysLoader* pSPL =
+ (StaticPhysLoader*)GetAllWrappers()->mpLoader( AllWrappers::msStaticPhys );
+ pSPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pSPL );
+
+ TreeDSGLoader* pTDL =
+ (TreeDSGLoader*)GetAllWrappers()->mpLoader( AllWrappers::msTreeDSG );
+ pTDL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pTDL );
+
+ FenceLoader* pFL =
+ (FenceLoader*)GetAllWrappers()->mpLoader( AllWrappers::msFenceEntity );
+ pFL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pFL );
+
+ IntersectLoader* pIL =
+ (IntersectLoader*)GetAllWrappers()->mpLoader( AllWrappers::msIntersectDSG );
+ pIL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pIL );
+
+ AnimCollLoader* pACL =
+ (AnimCollLoader*)GetAllWrappers()->mpLoader( AllWrappers::msAnimCollEntity );
+ pACL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pACL );
+
+ AnimDSGLoader* pAnimDSGLoader =
+ (AnimDSGLoader*)GetAllWrappers()->mpLoader( AllWrappers::msAnimEntity );
+ pAnimDSGLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pAnimDSGLoader );
+
+
+ DynaPhysLoader* pDPL =
+ (DynaPhysLoader*)GetAllWrappers()->mpLoader( AllWrappers::msDynaPhys );
+ pDPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pDPL );
+
+ InstStatPhysLoader* pISPL =
+ (InstStatPhysLoader*)GetAllWrappers()->mpLoader( AllWrappers::msInstStatPhys );
+ pISPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pISPL );
+
+ InstStatEntityLoader* pISEL =
+ (InstStatEntityLoader*)GetAllWrappers()->mpLoader( AllWrappers::msInstStatEntity );
+ pISEL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pISEL );
+
+ LocatorLoader* pLL =
+ (LocatorLoader*)GetAllWrappers()->mpLoader( AllWrappers::msLocator);
+ pLL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pLL );
+
+ RoadLoader* pRL =
+ (RoadLoader*)GetAllWrappers()->mpLoader( AllWrappers::msRoadSegment);
+ pRL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pRL );
+
+ PathLoader* pPL =
+ (PathLoader*)GetAllWrappers()->mpLoader( AllWrappers::msPathSegment);
+ pPL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pPL );
+
+ WorldSphereLoader* pWSL =
+ (WorldSphereLoader*)GetAllWrappers()->mpLoader( AllWrappers::msWorldSphere);
+ pWSL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pWSL );
+
+ LensFlareLoader* pLSL =
+ (LensFlareLoader*)GetAllWrappers()->mpLoader( AllWrappers::msLensFlare);
+ pLSL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pLSL );
+
+ BillboardWrappedLoader* pBWL =
+ (BillboardWrappedLoader*)GetAllWrappers()->mpLoader( AllWrappers::msBillboard);
+ pBWL->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pBWL );
+
+
+ InstParticleSystemLoader* pInstParticleSystemLoader =
+ (InstParticleSystemLoader*) GetAllWrappers()->mpLoader( AllWrappers::msInstParticleSystem);
+ pInstParticleSystemLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pInstParticleSystemLoader );
+
+ BreakableObjectLoader* pBreakableObjectLoader =
+ (BreakableObjectLoader*) GetAllWrappers()->mpLoader( AllWrappers::msBreakableObject);
+ pBreakableObjectLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pBreakableObjectLoader );
+
+ AnimDynaPhysLoader* pAnimDynaPhysLoader =
+ (AnimDynaPhysLoader*) GetAllWrappers()->mpLoader( AllWrappers::msAnimDynaPhys);
+ pAnimDynaPhysLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pAnimDynaPhysLoader );
+
+ AnimDynaPhysWrapperLoader* pAnimWrapperLoader =
+ (AnimDynaPhysWrapperLoader*) GetAllWrappers()->mpLoader( AllWrappers::msAnimDynaPhysWrapper);
+ pAnimWrapperLoader->SetRegdListener( GetRenderManager(), 0 );
+ p3d->AddHandler( pAnimWrapperLoader );
+
+ p3d->AddHandler(new(GMA_PERSISTENT) tTextureLoader);
+ p3d->AddHandler( new(GMA_PERSISTENT) tSetLoader );
+ p3d->AddHandler(new(GMA_PERSISTENT) tShaderLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tCameraLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tGameAttrLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tLightLoader);
+
+ p3d->AddHandler(new(GMA_PERSISTENT) tLocatorLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tLightGroupLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tImageLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tTextureFontLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tImageFontLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tSpriteLoader);
+ //p3d->AddHandler(new(GMA_PERSISTENT) tBillboardQuadGroupLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tSkeletonLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tPolySkinLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tCompositeDrawableLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tVertexAnimKeyLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tAnimationLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tFrameControllerLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tMultiControllerLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tAnimatedObjectFactoryLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tAnimatedObjectLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tParticleSystemFactoryLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tParticleSystemLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tLensFlareGroupLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) sg::Loader);
+
+ p3d->AddHandler(new(GMA_PERSISTENT) tExpressionGroupLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tExpressionMixerLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) tExpressionLoader);
+
+ //ATCloader, hope this doesnt blow up
+ p3d->AddHandler(new(GMA_PERSISTENT) ATCLoader);
+
+ //p3d->AddHandler(new p3d::tIgnoreLoader);
+
+ tSEQFileHandler* sequencerFileHandler = new(GMA_PERSISTENT) tSEQFileHandler;
+ p3d::loadManager->AddHandler(sequencerFileHandler, "seq");
+
+ // sim lib
+ sim::InstallSimLoaders();
+
+ p3d->AddHandler(new(GMA_PERSISTENT) CameraDataLoader, SRR2::ChunkID::FOLLOWCAM);
+ p3d->AddHandler(new(GMA_PERSISTENT) CameraDataLoader, SRR2::ChunkID::WALKERCAM);
+ p3d->AddHandler(new(GMA_PERSISTENT) IntersectionLoader);
+ //p3d->AddHandler(new(GMA_PERSISTENT) RoadLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) RoadDataSegmentLoader);
+ p3d->AddHandler(new(GMA_PERSISTENT) CStatePropDataLoader);
+MEMTRACK_POP_GROUP( "XboxPlatform" );
+}
+
+
+//==============================================================================
+// XboxPlatform::ShutdownPure3D
+//==============================================================================
+// Description: Clean up and shut down Pure3D.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void XboxPlatform::ShutdownPure3D()
+{
+ //
+ // Clean-up the Pure3D Inventory
+ //
+ p3d::inventory->RemoveAllElements();
+ p3d::inventory->DeleteAllSections();
+
+ //
+ // Clean-up the space taken by the Pure 3D context.
+ //
+ if( mpContext != NULL )
+ {
+ mpPlatform->DestroyContext( mpContext );
+ mpContext = NULL;
+ }
+
+ //
+ // Clean-up the space taken by the Pure 3D platform.
+ //
+ if( mpPlatform != NULL )
+ {
+ tPlatform::Destroy( mpPlatform );
+ mpPlatform = NULL;
+ }
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// XboxPlatform::XboxPlatform
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+XboxPlatform::XboxPlatform() :
+ mpPlatform( NULL ),
+ mpContext( NULL )
+{
+}
+
+
+//==============================================================================
+// XboxPlatform::~XboxPlatform
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+XboxPlatform::~XboxPlatform()
+{
+}
+
+
diff --git a/game/code/main/xboxplatform.h b/game/code/main/xboxplatform.h
new file mode 100644
index 0000000..1465716
--- /dev/null
+++ b/game/code/main/xboxplatform.h
@@ -0,0 +1,99 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: XboxPlatform
+//
+// Description: Abstracts the differences for setting up and shutting down
+// the different platforms.
+//
+// History: + Stolen and cleaned up from Svxy -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef XBOXPLATFORM_H
+#define XBOXPLATFORM_H
+
+//========================================
+// Nested Includes
+//========================================
+#include "platform.h" // base class
+
+//========================================
+// Forward References
+//========================================
+struct IRadMemoryHeap;
+class tPlatform;
+class tContext;
+
+//=============================================================================
+//
+// Synopsis: Provides abstraction for setting up and closing down the XBox.
+//
+//=============================================================================
+class XboxPlatform : public Platform
+{
+ public:
+
+ // Static Methods for accessing this singleton.
+ static XboxPlatform* CreateInstance();
+ static XboxPlatform* GetInstance();
+ static void DestroyInstance();
+
+ // Had to workaround our nice clean design cause FTech must be init'ed
+ // before anything else is done.
+ static void InitializeFoundation();
+ static void InitializeMemory();
+
+ // Implement Platform interface.
+ virtual void InitializePlatform();
+ virtual void ShutdownPlatform();
+
+ virtual void LaunchDashboard();
+ virtual void ResetMachine();
+ virtual void DisplaySplashScreen( SplashScreen screenID,
+ const char* overlayText = NULL,
+ float fontScale = 1.0f,
+ float textPosX = 0.0f,
+ float textPosY = 0.0f,
+ tColour textColour = tColour( 255,255,255 ),
+ int fadeFrames = 3 );
+
+ virtual void DisplaySplashScreen( const char* textureName,
+ const char* overlayText = NULL,
+ float fontScale = 1.0f,
+ float textPosX = 0.0f,
+ float textPosY = 0.0f,
+ tColour textColour = tColour( 255,255,255 ),
+ int fadeFrames = 3 );
+
+ virtual bool OnDriveError( radFileError error, const char* pDriveName, void* pUserData );
+ virtual void OnControllerError(const char *msg);
+
+
+ protected:
+
+ virtual void InitializeFoundationDrive();
+ virtual void ShutdownFoundation();
+
+ virtual void InitializePure3D();
+ virtual void ShutdownPure3D();
+
+ private:
+
+ // Constructors, Destructors, and Operators
+ XboxPlatform();
+ virtual ~XboxPlatform();
+
+ // Unused Constructors, Destructors, and Operators
+ XboxPlatform( const XboxPlatform& aPlatform );
+ XboxPlatform& operator=( const XboxPlatform& aPlatform );
+
+ // Pointer to the one and only instance of this singleton.
+ static XboxPlatform* spInstance;
+
+ // Pure 3D attributes
+ tPlatform* mpPlatform;
+ tContext* mpContext;
+};
+
+#endif // XBOXPLATFORM_H
diff --git a/game/code/memory/allmemory.cpp b/game/code/memory/allmemory.cpp
new file mode 100644
index 0000000..c4e945a
--- /dev/null
+++ b/game/code/memory/allmemory.cpp
@@ -0,0 +1,7 @@
+#include <memory/classsizetracker.cpp>
+#include <memory/createheap.cpp>
+#include <memory/leakdetection.cpp>
+#include <memory/memorypool.cpp>
+#include <memory/memoryutilities.cpp>
+#include <memory/srrmemory.cpp>
+#include <memory/propstats.cpp> \ No newline at end of file
diff --git a/game/code/memory/allocpool.h b/game/code/memory/allocpool.h
new file mode 100644
index 0000000..74094d9
--- /dev/null
+++ b/game/code/memory/allocpool.h
@@ -0,0 +1,143 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 23/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef ALLOCPOOL_H
+#define ALLOCPOOL_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <memory/srrmemory.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+template <class T> class AllocPool
+{
+ public:
+ AllocPool( GameMemoryAllocator heap, unsigned int iPoolSize );
+ virtual ~AllocPool();
+
+ T* AllocateFromPool();
+ void ReturnToPool( unsigned int pItem );
+
+ protected:
+ // override initialize if you have a different constructor
+ virtual void Initialize();
+ void Finalize();
+
+ GameMemoryAllocator GetHeap() { return( mHeap ); }
+
+ private:
+
+ //Prevent wasteful constructor creation.
+ AllocPool( const AllocPool& pAllocPool );
+ AllocPool& operator=( const AllocPool& pAllocPool );
+
+ GameMemoryAllocator mHeap;
+
+ unsigned int mPoolIndex;
+ unsigned int miPoolSize;
+
+ T* mPool;
+ bool* mUsed;
+};
+
+template <class T>
+AllocPool<T>::AllocPool( GameMemoryAllocator heap, unsigned int iPoolSize )
+{
+ mHeap = heap;
+ miPoolSize = iPoolSize;
+ mPoolIndex = 0;
+
+ mPool = new(mHeap) T[ miPoolSize ];
+ mUsed = new(mHeap) bool[ miPoolSize ];
+
+ Initialize();
+
+}
+
+template <class T>
+AllocPool<T>::~AllocPool()
+{
+ Finalize();
+
+ delete[] ( mHeap, mPool );
+ delete[] ( mHeap, mUsed );
+}
+
+template <class T>
+T* AllocPool<T>::AllocateFromPool()
+{
+ // if you hit this assert then your pool is too small
+ rAssert( mPoolIndex < miPoolSize );
+
+ T* pItem = &mPool[ mPoolIndex ];
+ mUsed[ mPoolIndex ] = true;
+
+ while(( mPoolIndex < miPoolSize ) && ( mUsed[ mPoolIndex ] ))
+ {
+ mPoolIndex++;
+ }
+
+ return( pItem );
+}
+
+template <class T>
+void AllocPool<T>::ReturnToPool( unsigned int pItem )
+{
+ for( unsigned int i = 0; i < miPoolSize; i++ )
+ {
+ if( (unsigned int)&mPool[ i ] == pItem )
+ {
+ mUsed[ i ] = false;
+ if( i < mPoolIndex )
+ {
+ mPoolIndex = i;
+ return;
+ }
+ }
+ }
+
+}
+
+template <class T>
+void AllocPool<T>::Initialize()
+{
+ for( unsigned int i = 0; i < miPoolSize; i++ )
+ {
+ mUsed[ i ] = false;
+ }
+}
+
+template <class T>
+void AllocPool<T>::Finalize()
+{
+/* for( unsigned int i = 0; i < miPoolSize; i++ )
+ {
+ if( mPool[ i ] == NULL )
+ {
+ delete( mHeap, mPool[ i ] );
+ mPool[ i ] = NULL;
+ }
+ }*/
+}
+
+#endif // ALLOCPOOL_H
+
diff --git a/game/code/memory/classsizetracker.cpp b/game/code/memory/classsizetracker.cpp
new file mode 100644
index 0000000..47d1976
--- /dev/null
+++ b/game/code/memory/classsizetracker.cpp
@@ -0,0 +1,318 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: memoryutilities.h
+//
+// Description: some funcitons for checking the amount of free memory, etc
+//
+// History: 2002/12/03 + Created -- Ian Gipson
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <memory/classSizeTracker.h>
+#include <memory/srrmemory.h>
+#include <map>
+#include <p3d/entity.hpp>
+#include <raddebugwatch.hpp>
+
+#define SHUTOFFCLASSSIZETRACKER
+
+#ifdef USECLASSSIZETRACKER
+#ifndef RAD_RELEASE
+//=============================================================================
+//
+// Global Data, Local Data, Local Classes
+//
+//=============================================================================
+typedef std::map< tName, int > NAMEINTMAP;
+typedef std::map< tName, bool > NAMEBOOLMAP;
+NAMEINTMAP g_NameToClassSize;
+NAMEINTMAP g_NameToInt;
+NAMEINTMAP g_TotalMemorySize;
+NAMEBOOLMAP g_AddedToWatcher;
+NAMEBOOLMAP g_SizeAddedToWatcher;
+bool g_DisableClassTracker;
+
+//=============================================================================
+//
+// Public Member Functions
+//
+//=============================================================================
+
+bool operator<( const tName& left, const tName& right )
+{
+ return( left.GetUID() < right.GetUID() );
+}
+//==============================================================================
+// ClassSizeTracker::ClassSizeTracker()
+//==============================================================================
+//
+// Description: constructor
+//
+// Parameters: None.
+//
+// Return: NONE
+//
+// Constraints: None
+//
+//==============================================================================
+ClassSizeTracker::ClassSizeTracker()
+{
+ size_t size = sizeof( ClassSizeTracker );
+}
+
+
+//==============================================================================
+// ClassSizeTracker::RegisterClassSize
+//==============================================================================
+//
+// Description: register the size of the class
+//
+// Parameters: None.
+//
+// Return: NONE
+//
+// Constraints: None
+//
+//==============================================================================
+void ClassSizeTracker::RegisterClassSize( const tName& className, const unsigned int size )
+{
+ #ifdef SHUTOFFCLASSSIZETRACKER
+ return;
+ #endif //SHUTOFFCLASSSIZETRACKER
+ if( !HeapManager::IsCreated() )
+ {
+ return;
+ }
+ HeapMgr()->PushHeap( GMA_DEFAULT );
+ int& sizeInMap = g_NameToClassSize[ className ];
+ sizeInMap = size;
+
+ bool& addedYet = g_SizeAddedToWatcher[ className ];
+ if( !addedYet )
+ {
+ bool watcherEnabled = radDebugWatchEnabled();
+ if( watcherEnabled )
+ {
+ //
+ // Add the variable to the watcher
+ //
+ radDbgWatchAddInt( &sizeInMap, className.GetText(), "ClassSizeTracker", NULL, NULL, 0, 1000000, true );
+ addedYet = true;
+ }
+ }
+ HeapMgr()->PopHeap( GMA_DEFAULT );
+}
+
+//==============================================================================
+// ClassSizeTracker::RegisterClassSize
+//==============================================================================
+//
+// Description: register the size of the class
+//
+// Parameters: None.
+//
+// Return: NONE
+//
+// Constraints: None
+//
+//==============================================================================
+void ClassSizeTracker::RegisterClassSize( const char* className, const unsigned int size )
+{
+ #ifdef SHUTOFFCLASSSIZETRACKER
+ return;
+ #endif //SHUTOFFCLASSSIZETRACKER
+ if( !HeapManager::IsCreated() ) { return; }
+ if( g_DisableClassTracker ) { return; }
+ g_DisableClassTracker = true;
+ RegisterClassSize( static_cast< tName >( className ), size );
+ g_DisableClassTracker = false;
+}
+
+//==============================================================================
+// ClassSizeTracker::RegisterCreation()
+//==============================================================================
+//
+// Description: registers creation of a class by a char*
+//
+// Parameters: None.
+//
+// Return: NONE
+//
+// Constraints: None
+//
+//==============================================================================
+void ClassSizeTracker::RegisterCreation( const tName& className )
+{
+ #ifdef SHUTOFFCLASSSIZETRACKER
+ return;
+ #endif //SHUTOFFCLASSSIZETRACKER
+
+ if( !HeapManager::IsCreated() )
+ {
+ return;
+ }
+ HeapMgr()->PushHeap( GMA_DEFAULT );
+ int& size = g_NameToInt[ className ];
+ int& totalMemorySize = g_TotalMemorySize[ className ];
+ ++size;
+
+ bool& addedYet = g_AddedToWatcher[ className ];
+ if( !addedYet )
+ {
+ bool watcherEnabled = radDebugWatchEnabled();
+ if( watcherEnabled )
+ {
+ //
+ // Add the variable to the watcher
+ //
+ radDbgWatchAddInt( &size, className.GetText(), "ClassCountTracker", NULL, NULL, 0, 1000000, true );
+ radDbgWatchAddInt( &totalMemorySize, className.GetText(), "ClassTotalSize", NULL, NULL, 0, 1000000, true );
+ addedYet = true;
+ }
+ }
+ HeapMgr()->PopHeap( GMA_DEFAULT );
+}
+
+//==============================================================================
+// ClassSizeTracker::RegisterCreation()
+//==============================================================================
+//
+// Description: registers creation of a class via a char*
+//
+// Parameters: None.
+//
+// Return: NONE
+//
+// Constraints: None
+//
+//==============================================================================
+void ClassSizeTracker::RegisterCreation( const char* className )
+{
+ #ifdef SHUTOFFCLASSSIZETRACKER
+ return;
+ #endif //SHUTOFFCLASSSIZETRACKER
+
+ if( !HeapManager::IsCreated() ) { return; }
+ if( g_DisableClassTracker ) { return; }
+ g_DisableClassTracker = true;
+ tName name = className;
+ RegisterCreation( name );
+ g_DisableClassTracker = false;
+}
+
+//==============================================================================
+// ClassSizeTracker::RegisterDestruction()
+//==============================================================================
+//
+// Description: registers the destruction of a class
+//
+// Parameters: None.
+//
+// Return: NONE
+//
+// Constraints: None
+//
+//==============================================================================
+void ClassSizeTracker::RegisterDestruction( const tName& className )
+{
+ #ifdef SHUTOFFCLASSSIZETRACKER
+ return;
+ #endif //SHUTOFFCLASSSIZETRACKER
+
+ if( !HeapManager::IsCreated() )
+ {
+ return;
+ }
+
+ int& size = g_NameToInt[ className ];
+ --size;
+}
+
+//==============================================================================
+// ClassSizeTracker::RegisterDestruction()
+//==============================================================================
+//
+// Description: registers the destruction of a class
+//
+// Parameters: classname - the name of the class we're destroying
+//
+// Return: NONE
+//
+// Constraints: None
+//
+//==============================================================================
+void ClassSizeTracker::RegisterDestruction( const char* className )
+{
+ #ifdef SHUTOFFCLASSSIZETRACKER
+ return;
+ #endif //SHUTOFFCLASSSIZETRACKER
+
+ if( !HeapManager::IsCreated() ) { return; }
+ if( g_DisableClassTracker ) { return; }
+ g_DisableClassTracker = true;
+ RegisterDestruction( static_cast< tName >( className ) );
+ g_DisableClassTracker = false;
+}
+
+//==============================================================================
+// ClassSizeTracker::UpdateTotalMemorySize()
+//==============================================================================
+//
+// Description: updates the total memory size variable in the watcher
+//
+// Parameters: None.
+//
+// Return: NONE
+//
+// Constraints: None
+//
+//==============================================================================
+void ClassSizeTracker::UpdateTotalMemorySize( const tName& className )
+{
+ #ifdef SHUTOFFCLASSSIZETRACKER
+ return;
+ #endif //SHUTOFFCLASSSIZETRACKER
+
+ if( !HeapManager::IsCreated() )
+ {
+ return;
+ }
+ int totalMemorySize = g_NameToClassSize[ className ] * g_NameToInt[ className ];
+ g_TotalMemorySize[ className ] = totalMemorySize;
+}
+
+//==============================================================================
+// ClassSizeTracker::UpdateTotalMemorySize()
+//==============================================================================
+//
+// Description: updates the total memory size variable in the watcher
+//
+// Parameters: className - the class that we're updating the memory size for.
+//
+// Return: NONE
+//
+// Constraints: None
+//
+//==============================================================================
+void ClassSizeTracker::UpdateTotalMemorySize( const char* className )
+{
+ #ifdef SHUTOFFCLASSSIZETRACKER
+ return;
+ #endif //SHUTOFFCLASSSIZETRACKER
+
+ if( !HeapManager::IsCreated() ) { return; }
+ if( g_DisableClassTracker ) { return; }
+ g_DisableClassTracker = true;
+ UpdateTotalMemorySize( static_cast< tName >( className ) );
+ g_DisableClassTracker = false;
+}
+#endif RAD_RELEASE
+#endif USECLASSSIZETRACKER \ No newline at end of file
diff --git a/game/code/memory/classsizetracker.h b/game/code/memory/classsizetracker.h
new file mode 100644
index 0000000..ccec63b
--- /dev/null
+++ b/game/code/memory/classsizetracker.h
@@ -0,0 +1,58 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: memoryutilities.h
+//
+// Description: functions for telling you things about memory
+//
+// History: 2002/12/03 + Created -- Ian Gipson
+//
+//=============================================================================
+
+#ifndef CLASSSIZETRACKER_H_
+#define CLASSSIZETRACKER_H_
+
+//========================================
+// Nested Includes
+//========================================
+class tName;
+#ifndef WIN32
+#define USECLASSSIZETRACKER
+#endif
+
+//==============================================================================
+//
+// Synopsis: This class allows you to tag allocations for reporting in the
+// FTech Memory Monitor.
+//
+//==============================================================================
+#if !(defined RAD_RELEASE) && (defined USECLASSSIZETRACKER)
+class ClassSizeTracker
+{
+public:
+ ClassSizeTracker();
+ static void RegisterClassSize ( const tName& className, const unsigned int size );
+ static void RegisterClassSize ( const char* className, const unsigned int size );
+ static void RegisterCreation ( const tName& className );
+ static void RegisterCreation ( const char* className );
+ static void RegisterDestruction( const tName& className );
+ static void RegisterDestruction( const char* className );
+ static void UpdateTotalMemorySize( const tName& className );
+ static void UpdateTotalMemorySize( const char* className );
+protected:
+private:
+};
+
+ #define CLASSTRACKER_CREATE( myname ) ClassSizeTracker::RegisterCreation( #myname ); \
+ ClassSizeTracker::RegisterClassSize( #myname, sizeof( myname ) );\
+ ClassSizeTracker::UpdateTotalMemorySize( #myname ) \
+
+ #define CLASSTRACKER_DESTROY( myname ) ClassSizeTracker::RegisterDestruction( #myname ); \
+ ClassSizeTracker::UpdateTotalMemorySize( #myname ) \
+
+#else
+ #define CLASSTRACKER_CREATE( myname ) ((void)0)
+ #define CLASSTRACKER_DESTROY( myname ) ((void)0)
+#endif
+
+#endif //MEMORYTAGGER_H
diff --git a/game/code/memory/createheap.cpp b/game/code/memory/createheap.cpp
new file mode 100644
index 0000000..745bf35
--- /dev/null
+++ b/game/code/memory/createheap.cpp
@@ -0,0 +1,322 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: createheap.cpp
+//
+// Description: this set of functions creates heaps
+//
+// History: 2003/04/13 + Created -- Ian Gipson
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <memory/createheap.h>
+#include <radmemorymonitor.hpp>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+IRadMemoryHeap* g_HeapArray[ NUM_GAME_MEMORY_ALLOCATORS ];
+
+struct HeapCreationData
+{
+ HeapType type;
+ GameMemoryAllocator parent;
+ char name[ 256 ];
+};
+
+HeapCreationData g_HeapCreationData[] =
+{
+ { HEAP_TYPE_DOUG_LEA, GMA_DEFAULT, "Default" },
+ { HEAP_TYPE_DOUG_LEA, GMA_DEFAULT, "Temp" },
+ { HEAP_TYPE_NONE, GMA_DEFAULT, "Gamecube VMM" },
+#ifdef RAD_WIN32
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Persistent" }, // no static heap for pc
+#else
+ { HEAP_TYPE_STATIC, GMA_DEFAULT, "Persistent" },
+#endif // RAD_WIN32
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Level" },
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Level Movie" },
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Level FE" },
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Level Zone" },
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Level Other" },
+ { HEAP_TYPE_DOUG_LEA, GMA_DEFAULT, "Level Hud" },
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Level Mission" },
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Level Audio" },
+ { HEAP_TYPE_NONE, GMA_DEFAULT, "Debug" },
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Special" },
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Music" },
+ { HEAP_TYPE_DOUG_LEA, GMA_DEFAULT, "Audio Persistent" },
+ { HEAP_TYPE_DOUG_LEA, GMA_DEFAULT, "Small Alloc" },
+#ifdef RAD_XBOX
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "XBOX Sound" },
+#endif
+#ifdef USE_CHAR_GAG_HEAP
+ { HEAP_TYPE_DOUG_LEA, GMA_DEFAULT, "Characters and Gags" },
+#endif
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Anywhere in Level" },
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Anywhere in FE" },
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Either Other or Zone" },
+ { HEAP_TYPE_TRACKING, GMA_DEFAULT, "Search" },
+ { HEAP_TYPE_NONE, GMA_DEFAULT, "???" },
+ { HEAP_TYPE_NONE, GMA_DEFAULT, "???" },
+};
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// AllocatorType
+//=============================================================================
+//
+// Description: which type of heap is this?
+//
+// Parameters: allocator - which heap are we concerned with
+//
+// Return: N/A.
+//
+//=============================================================================
+HeapType AllocatorType( GameMemoryAllocator allocator )
+{
+ return g_HeapCreationData[ allocator ].type;
+}
+
+//=============================================================================
+// CreateHeap
+//=============================================================================
+//
+// Description: Creates the specified heap
+//
+// Parameters: allocator - which heap are we creating
+//
+// Return: N/A.
+//
+//=============================================================================
+void CreateHeap ( GameMemoryAllocator allocator, const unsigned int size )
+{
+ unsigned int index = static_cast< unsigned int >( allocator );
+ rAssert( g_HeapArray[ index ] == NULL );
+
+ HeapType type = g_HeapCreationData[ index ].type;
+ const char* name = g_HeapCreationData[ index ].name;
+ GameMemoryAllocator parent = g_HeapCreationData[ index ].parent;
+
+ rReleasePrintf ("Creating Heap: %s (%d)\n", name, size );
+
+ #ifdef RAD_RELEASE
+ if( type == HEAP_TYPE_TRACKING )
+ {
+ type = HEAP_TYPE_NONE;
+ }
+ #endif
+
+ switch( type )
+ {
+ case HEAP_TYPE_STATIC :
+ {
+ HeapMgr()->PushHeap( GMA_DEBUG );
+ g_HeapArray[ index ] = radMemoryCreateStaticHeap( size, RADMEMORY_ALLOC_DEFAULT, name );
+ g_HeapArray[ index ]->AddRef();
+ HeapMgr()->PopHeap( GMA_DEBUG );
+ break;
+ }
+ case HEAP_TYPE_TRACKING :
+ {
+ HeapMgr()->PushHeap( GMA_DEBUG );
+ g_HeapArray[ index ] = radMemoryCreateTrackingHeap( size, RADMEMORY_ALLOC_DEFAULT, name );
+ HeapMgr()->PopHeap( GMA_DEBUG );
+ break;
+ }
+ case HEAP_TYPE_DOUG_LEA :
+ {
+ HeapMgr()->PushHeap( GMA_DEBUG );
+ g_HeapArray[ index ] = radMemoryCreateDougLeaHeap( size, RADMEMORY_ALLOC_DEFAULT, name );
+ g_HeapArray[ index ]->AddRef();
+ HeapMgr()->PopHeap( GMA_DEBUG );
+ break;
+ }
+ case HEAP_TYPE_NONE :
+ {
+ //rAssert( false );
+ return;
+ }
+ default:
+ {
+ rAssert( false );
+ return;
+ }
+ }
+ radMemoryRegisterAllocator( allocator, parent,g_HeapArray[ index ] );
+}
+
+//=============================================================================
+// DestroyHeap
+//=============================================================================
+//
+// Description: Destroys the specified heap
+//
+// Parameters: allocator - which heap are we destroying
+//
+// Return: N/A.
+//
+//=============================================================================
+void DestroyHeapA( GameMemoryAllocator allocator )
+{
+ unsigned int index = static_cast< unsigned int >( allocator );
+ if( g_HeapArray[ index ] == NULL )
+ {
+ return;
+ }
+
+ // Check for heap not empty.
+ // If it's not empty this is a Bad Thing. We will be leaving dangling pointers to all the contained memory.
+ // This either means we have a leak and they will never get deleted or they will get deleted eventually but
+ // the heap will no longer exist.
+ //
+ // If you assert here, DO NOT IGNORE IT. If you need help diagnosing the cause of the assertion, see Joel.
+ //
+ unsigned int numAllocs;
+ unsigned int totalFree;
+ g_HeapArray[ index ]->GetStatus ( &totalFree, 0, &numAllocs, 0);
+
+ //
+ // Suspend memory monitor etc if we've leaked
+ //
+ if( numAllocs > 0 )
+ {
+ #ifndef FINAL
+ unsigned int size = g_HeapArray[ index ]->GetSize();
+ char s[ 256 ];
+ const char* name = g_HeapCreationData[ index ].name;
+ unsigned int leakSize = size - totalFree;
+ sprintf( s, "MEMORY LEAK: '%s' %d blocks %d bytes ", name, numAllocs, leakSize );
+ rTunePrintf( s );
+ g_HeapArray[index]->ValidateHeap();
+ #endif
+
+ // Destroy the heap
+ //
+ g_HeapArray[ index ]->Release ();
+ g_HeapArray[ index ] = 0;
+ ::radMemoryUnregisterAllocator( allocator );
+
+ if( CommandLineOptions::Get( CLO_MEMORY_MONITOR ) )
+ {
+ ::radMemoryMonitorSuspend ();
+ }
+ #ifndef RAD_RELEASE
+ else
+ {
+// rAssertMsg (0, s);
+ }
+ #endif // RAD_RELEASE
+ }
+ else
+ {
+ // Destroy the heap
+ //
+ g_HeapArray[ index ]->Release ();
+ g_HeapArray[ index ] = 0;
+ ::radMemoryUnregisterAllocator( allocator );
+ }
+}
+
+//=============================================================================
+// GetHeap
+//=============================================================================
+//
+// Description: allows access to the heaps
+//
+// Parameters: allocator - which heap are we getting
+//
+// Return: N/A.
+//
+//=============================================================================
+IRadMemoryAllocator* GetAllocator( GameMemoryAllocator allocator )
+{
+ IRadMemoryAllocator* allocatorPtr = radMemoryGetAllocator( static_cast< int >( allocator ) );
+ return allocatorPtr;
+}
+
+//=============================================================================
+// GetHeapReference
+//=============================================================================
+//
+// Description: allows access to the heaps in the array
+//
+// Parameters: allocator - which heap are we getting
+//
+// Return: N/A.
+//
+//=============================================================================
+IRadMemoryHeap** GetHeapReference( GameMemoryAllocator allocator )
+{
+ return &( g_HeapArray[ allocator ] );
+}
+
+//=============================================================================
+// GetTotalMemoryFreeInAllHeaps
+//=============================================================================
+//
+// Description: tells us how much free space there is in all the heaps
+//
+// Parameters: none
+//
+// Return: total free space
+//
+//=============================================================================
+size_t GetTotalMemoryFreeInAllHeaps()
+{
+ unsigned int totalFreeMemory = 0;
+ unsigned int i;
+ unsigned int size = NUM_GAME_MEMORY_ALLOCATORS;
+ for( i = 0; i < size; ++i )
+ {
+ IRadMemoryHeap* heap = g_HeapArray[ i ];
+ unsigned int free;
+ unsigned int largestBlock;
+ unsigned int numberOfObjects;
+ unsigned int highWaterMark;
+ if( heap != NULL )
+ {
+ heap->GetStatus( &free, &largestBlock, &numberOfObjects, &highWaterMark );
+ totalFreeMemory += free;
+ }
+ }
+ return totalFreeMemory;
+}
+
+//=============================================================================
+// AllocatorType
+//=============================================================================
+//
+// Description: figure out what heap this is
+//
+// Parameters: none
+//
+// Return: the index of the heap
+//
+//=============================================================================
+GameMemoryAllocator WhichAllocator( const IRadMemoryAllocator* heap )
+{
+ int i;
+ for( i = 0; i < NUM_GAME_MEMORY_ALLOCATORS; ++i )
+ {
+ if( g_HeapArray[ i ] == heap )
+ {
+ return static_cast< GameMemoryAllocator >( i );
+ }
+ }
+ return NUM_GAME_MEMORY_ALLOCATORS;
+}
diff --git a/game/code/memory/createheap.h b/game/code/memory/createheap.h
new file mode 100644
index 0000000..8726b88
--- /dev/null
+++ b/game/code/memory/createheap.h
@@ -0,0 +1,39 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: createheap.h
+//
+// Description: this set of functions creates heaps
+//
+// History: 2003/04/13 + Created -- Ian Gipson
+//
+//=============================================================================
+
+#ifndef CREATEHEAP_H
+#define CREATEHEAP_H
+
+#include <memory/srrmemory.h>
+
+//=============================================================================
+// Enumerated Types
+//=============================================================================
+enum HeapType
+{
+ HEAP_TYPE_STATIC,
+ HEAP_TYPE_TRACKING,
+ HEAP_TYPE_DOUG_LEA,
+ HEAP_TYPE_NONE
+};
+
+//=============================================================================
+// Functions
+//=============================================================================
+HeapType AllocatorType( GameMemoryAllocator allocator );
+void CreateHeap ( GameMemoryAllocator allocator, const unsigned int size );
+void DestroyHeapA( GameMemoryAllocator allocator );
+IRadMemoryAllocator* GetAllocator( GameMemoryAllocator allocator );
+IRadMemoryHeap** GetHeapReference( GameMemoryAllocator allocator );
+size_t GetTotalMemoryFreeInAllHeaps();
+GameMemoryAllocator WhichAllocator( const IRadMemoryAllocator* heap );
+
+#endif //CREATEHEAP_H
diff --git a/game/code/memory/leakdetection.cpp b/game/code/memory/leakdetection.cpp
new file mode 100644
index 0000000..729409d
--- /dev/null
+++ b/game/code/memory/leakdetection.cpp
@@ -0,0 +1,238 @@
+//-----------------------------------------------------------------------------
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// leakdetection.hpp
+//
+// Description: A stimple allocation counter for leak detection
+//
+// Modification History:
+//
+// + Created Sept 10, 2002 Robert Sparks
+//-----------------------------------------------------------------------------
+
+#ifndef LEAK_DETECTION_DISABLE
+
+//-----------------------------------------------------------------------------
+// Included Files
+//-----------------------------------------------------------------------------
+#ifdef WORLD_BUILDER
+#include "main/toolhack.h"
+#pragma warning( disable:4786 )
+#endif
+
+#include "main/commandlineoptions.h"
+#include <map>
+#include "memory/leakdetection.h"
+#include "memory/srrmemory.h"
+#include "memory/stlallocators.h"
+#include "p3d/utility.hpp"
+#include <radtime.hpp>
+
+
+//-----------------------------------------------------------------------------
+// Local Declarations
+//-----------------------------------------------------------------------------
+
+struct MemoryInfo
+{
+ void* m_Address;
+ unsigned int m_Size;
+};
+
+typedef std::map< const void*, unsigned int, std::less<const void*>, s2alloc< std::pair<const void* const, unsigned int> > > VOIDINTMAP;
+static VOIDINTMAP g_MemoryMap;
+static bool g_MemoryAllocationEnabled = false;
+static unsigned int g_TrapIndex = 0xFFFFFFFF;
+
+
+//-----------------------------------------------------------------------------
+// D o W e T r a c k L e a k s I n T h i s H e a p
+//
+// Synopsis: Some heaps are on their own as far as leak detection goes.
+// Sound and Temp for example
+//
+// Parameters: heap - do we care about this heap?
+//
+// Returns: bool - well do we?
+//
+// Constraints: NONE
+//
+//-----------------------------------------------------------------------------
+bool DoWeTrackLeaksInThisHeap( radMemoryAllocator heap )
+{
+ if( heap == GMA_TEMP ) return false;
+ if( heap == GMA_PERSISTENT ) return false;
+ if( heap == GMA_MUSIC ) return false;
+ if( heap == GMA_AUDIO_PERSISTENT ) return false;
+#ifdef RAD_XBOX
+ if( heap == GMA_XBOX_SOUND_MEMORY ) return false;
+#endif
+ if( heap == GMA_DEBUG ) return false;
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// L e a k D e t e c t i o n S t a r t
+//
+// Synopsis: Begins the tracking of allocations, signifies the beginnig of a section
+//
+// Parameters: NONE
+//
+// Returns: NOTHING
+//
+// Constraints: NONE
+//
+//-----------------------------------------------------------------------------
+
+void LeakDetectionStart( void )
+{
+ bool detectLeaks = CommandLineOptions::Get( CLO_DETECT_LEAKS );
+ if( detectLeaks )
+ {
+ g_MemoryAllocationEnabled = false;
+ g_MemoryMap.clear();
+ g_MemoryAllocationEnabled = true;
+ }
+}
+
+//-----------------------------------------------------------------------------
+// L e a k D e t e c t i o n S t o p
+//
+// Synopsis: Ends the tracking of allocation, signifies the end of a section.
+// Will print out a warning or will assert if a leak is detected
+//
+// Parameters: nON
+//
+// Returns: NOTHING
+//
+// Constraints:
+//
+//-----------------------------------------------------------------------------
+
+void LeakDetectionStop( void )
+{
+
+ if( !g_MemoryAllocationEnabled )
+ {
+ return;
+ }
+
+ int totalNumberOfLeaks = g_MemoryMap.size();
+
+ bool printLeakResults = CommandLineOptions::Get( CLO_DETECT_LEAKS );
+ if( printLeakResults )
+ {
+ //
+ // Print out leaks
+ //
+ rReleasePrintf( "*****************************************************\n");
+ rReleasePrintf( "************** LEAK DETECTION RESULTS ***************\n");
+ rReleasePrintf( "** Total Leaked Memory Blocks = %d\n", totalNumberOfLeaks );
+ VOIDINTMAP::iterator it;
+ for( it = g_MemoryMap.begin(); it != g_MemoryMap.end(); ++it )
+ {
+ const void* address = ( *it ).first;
+ unsigned int size = ( *it ).second;
+
+ if( address != NULL )
+ {
+ rReleasePrintf( "** Leak at address 0x%x, size 0x%x, Index = %d\n", address, size );
+ }
+ }
+
+ //
+ // Display number of leaks to the screen
+ //
+ if( totalNumberOfLeaks > 0 )
+ {
+ char buf[ 256 ];
+ sprintf( buf, "MEMORY LEAKED: [%d] blocks\n", totalNumberOfLeaks );
+
+ p3d::pddi->DrawString( buf, 256, (12 * 15), pddiColour( 255, 128, 0 ) );
+
+ unsigned int nowTime = ::radTimeGetSeconds( );
+
+ while( nowTime + 5 > ::radTimeGetSeconds( ) )
+ {
+ p3d::display->SwapBuffers();
+ }
+ radMemoryMonitorSuspend();
+ }
+ rReleasePrintf( "*****************************************************\n");
+ }
+
+
+ g_MemoryAllocationEnabled = false;
+ g_MemoryMap.clear();
+}
+
+//-----------------------------------------------------------------------------
+// L e a k D e t e c t i o n A d d R e c o r d
+//
+// Synopsis: Reports that an allocation has happened
+//
+// Parameters: the allocation address and sizeo
+//
+// Returns: NOTHING
+//
+// Constraints: NONE
+//
+//-----------------------------------------------------------------------------
+
+void LeakDetectionAddRecord( const void* pMemory, const unsigned int size, const radMemoryAllocator heap )
+{
+ if( !g_MemoryAllocationEnabled )
+ {
+ return;
+ }
+
+ bool doWeTrackLeaksInThisHeap = DoWeTrackLeaksInThisHeap( heap );
+ if( !doWeTrackLeaksInThisHeap )
+ {
+ return;
+ }
+ //
+ // Is this address already there? It shouldn't be
+ //
+ HeapMgr()->PushHeap( GMA_DEBUG );
+ VOIDINTMAP::iterator found = g_MemoryMap.find( pMemory );
+ rAssert( found == g_MemoryMap.end() );
+ g_MemoryAllocationEnabled = false;
+ g_MemoryMap[ pMemory ] = size;
+ g_MemoryAllocationEnabled = true;
+ HeapMgr()->PopHeap( GMA_DEBUG );
+}
+
+//-----------------------------------------------------------------------------
+// L e a k D e t e c t i o n R e m o v e R e c o r d
+//
+// Synopsis: Reports that an allocation has been freed
+//
+// Parameters: the address of the freed allocation
+//
+// Returns: NOTHING
+//
+// Constraints: NONE
+//
+//-----------------------------------------------------------------------------
+
+void LeakDetectionRemoveRecord( void* pMemory )
+{
+ if( g_MemoryAllocationEnabled && ( pMemory != NULL ) )
+ {
+ VOIDINTMAP::iterator found = g_MemoryMap.find( pMemory );
+ if( found == g_MemoryMap.end() )
+ {
+ //
+ // IAN - put this in later - it means that we're freeing stuff that came from permanent memory
+ //
+ //rReleasePrintf( "LeakDetection: Untracked memory block at %x freed\n", pMemory);
+ }
+ else
+ {
+ g_MemoryMap.erase( found );
+ }
+ }
+}
+
+#endif // FINAL \ No newline at end of file
diff --git a/game/code/memory/leakdetection.h b/game/code/memory/leakdetection.h
new file mode 100644
index 0000000..1b12134
--- /dev/null
+++ b/game/code/memory/leakdetection.h
@@ -0,0 +1,61 @@
+#ifndef LEAK_DETECTION_HPP
+#define LEAK_DETECTION_HPP
+
+#include "main/commandlineoptions.h"
+#include <radmemorymonitor.hpp>
+//-----------------------------------------------------------------------------
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// leakdetection.hpp
+//
+// Description: A stimple allocation counter for leak detection
+//
+// Modification History:
+//
+// + Created Sept 10, 2002 Robert Sparks
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// Definitions
+//-----------------------------------------------------------------------------
+
+//
+// Use these macros for accessing the leak detection
+//
+#ifdef RAD_RELEASE
+ #define LEAK_DETECTION_DISABLE
+#endif
+
+#ifndef LEAK_DETECTION_DISABLE
+ // Begin tracking allocations. If any allocations are outstanding from the last
+ // time this macro is called a warning or assert will happen
+ #define LEAK_DETECTION_CHECKPOINT( ) { LeakDetectionStop( ); LeakDetectionStart( ); }
+
+ // Declare allocation
+ #define LEAK_DETECTION_ADD_ALLOCATION( pMemory, size, heap ) { LeakDetectionAddRecord( pMemory, size, heap ); }
+
+ // Rescind allocation
+ #define LEAK_DETECTION_REMOVE_ALLOCATION( pMemory ) { LeakDetectionRemoveRecord( pMemory ); }
+#else
+ // Compile these things out in final
+ #define LEAK_DETECTION_CHECKPOINT( ) ( ( void ) 0 )
+ #define LEAK_DETECTION_ADD_ALLOCATION( pMemory , size, heap ) ( ( void ) 0 )
+ #define LEAK_DETECTION_REMOVE_ALLOCATION( pMemory ) ( ( void ) 0 )
+#endif // FINAL
+
+//-----------------------------------------------------------------------------
+// Functions
+//-----------------------------------------------------------------------------
+
+#ifndef LEAK_DETECTION_DISABLE
+ // Start / stop leak tracking
+ void LeakDetectionStart( void );
+ void LeakDetectionStop( void );
+
+ // Declare / rescind allocation
+ void LeakDetectionAddRecord( const void* pMemory, const unsigned int size, const radMemoryAllocator heap );
+ void LeakDetectionRemoveRecord( void* pMemory );
+#endif // FINAL
+
+#endif // LEAK_DETECTION_HPP
+
diff --git a/game/code/memory/map.h b/game/code/memory/map.h
new file mode 100644
index 0000000..d6c257d
--- /dev/null
+++ b/game/code/memory/map.h
@@ -0,0 +1,295 @@
+#ifndef MAP_H
+#define MAP_H
+
+#include <algorithm>
+#include <vector>
+#include <raddebug.hpp>
+#include <memory/stlallocators.h>
+
+// Ian Gipson's Map.
+// Similar to STL map but with the addition of a reserve function that works much
+// like the original stl::vector::reserve.
+// I've made 2 minor changes. One is prefixing the Map namespace to the iterators
+// that prevent the PS2 version from compiling because of typedef confusion
+// with STL's vectors and I've moved the STL onto Darwin's
+// persistent_allocator for memory tracking
+
+//=============================================================================
+// ForwardReference
+//=============================================================================
+template < class KeyClass, class T >
+class Map;
+
+
+//=============================================================================
+// MapElement
+//=============================================================================
+template < class KeyClass, class T >
+class MapElement
+{
+public:
+ KeyClass first;
+ T second;
+
+ bool operator<( const MapElement& right ) const{ return first < right.first;}
+ bool operator==( const MapElement& right ) const{ return first == right.first;}
+protected:
+};
+
+//=============================================================================
+// Map
+//=============================================================================
+template < class KeyClass, class T >
+class Map
+{
+public:
+ //=========================================================================
+ // iterator
+ //=========================================================================
+ typedef MapElement< KeyClass, T > MapElementType;
+ typedef std::vector< MapElementType, s2alloc< MapElementType > > MapElementVector;
+ typedef typename MapElementVector::iterator iterator;
+ typedef typename MapElementVector::const_iterator const_iterator;
+ //=========================================================================
+
+ Map();
+
+ typename Map::const_iterator begin() const;
+ typename Map::iterator begin();
+ size_t capacity() const;
+ void clear();
+ typename Map::const_iterator end() const;
+ typename Map::iterator end();
+ typename Map::iterator erase( typename Map::iterator it );
+ size_t erase( const KeyClass& key );
+ typename Map::const_iterator find( const KeyClass& key ) const;
+ typename Map::iterator find( const KeyClass& key );
+ T& get( const KeyClass& key );
+ void insert( const KeyClass& key, const T& element );
+ const T& operator[]( const KeyClass& key ) const;
+ void reserve( const size_t size );
+ size_t size() const;
+
+protected:
+ void RefreshIfDirty() const;
+ mutable MapElementVector m_Elements; //oh how i lie about constness...
+ mutable bool m_Dirty;
+};
+
+//=============================================================================
+// Constructor
+//=============================================================================
+template < class KeyClass, class T >
+Map< KeyClass, T >::Map( ):
+ m_Dirty( false )
+{
+ //
+ // nothing
+ //
+}
+
+//=============================================================================
+// begin
+//=============================================================================
+template < class KeyClass, class T >
+typename Map< KeyClass, T >::const_iterator
+Map< KeyClass, T >::begin() const
+{
+ return m_Elements.begin();
+}
+
+//=============================================================================
+// begin
+//=============================================================================
+template < class KeyClass, class T >
+typename Map< KeyClass, T >::iterator Map< KeyClass, T >::begin()
+{
+ return m_Elements.begin();
+}
+
+//=============================================================================
+// capacity
+//=============================================================================
+template < class KeyClass, class T >
+size_t Map< KeyClass, T >::capacity() const
+{
+ return m_Elements.capacity();
+}
+
+//=============================================================================
+// clear
+//=============================================================================
+template < class KeyClass, class T >
+void Map< KeyClass, T >::clear()
+{
+ m_Elements.erase( m_Elements.begin(), m_Elements.end() );
+}
+
+//=============================================================================
+// end
+//=============================================================================
+template < class KeyClass, class T >
+typename Map< KeyClass, T >::const_iterator Map< KeyClass, T >::end() const
+{
+ return m_Elements.end();
+}
+
+//=============================================================================
+// end
+//=============================================================================
+template < class KeyClass, class T >
+typename Map< KeyClass, T >::iterator Map< KeyClass, T >::end()
+{
+ return m_Elements.end();
+}
+
+//=============================================================================
+// erase
+//=============================================================================
+template < class KeyClass, class T >
+typename Map< KeyClass, T >::iterator Map< KeyClass, T >::erase( typename Map< KeyClass, T >::iterator it )
+{
+ return m_Elements.erase( it );
+}
+
+//=============================================================================
+// erase
+//=============================================================================
+template < class KeyClass, class T >
+size_t Map< KeyClass, T >::erase( const KeyClass& key )
+{
+ MapElementVector::iterator it;
+ for( it = m_Elements.begin(); it != m_Elements.end(); ++it )
+ {
+ if( (*it).first == key )
+ {
+ it = m_Elements.erase( it );
+ return 1;
+ }
+ }
+ return 0;
+}
+
+//======`=======================================================================
+// find
+//=============================================================================
+template < class KeyClass, class T >
+typename Map< KeyClass, T >::const_iterator Map< KeyClass, T >::find( const KeyClass& key ) const
+{
+ RefreshIfDirty();
+ MapElement< KeyClass, T > findme;
+ findme.first = key;
+ const Map::iterator it = std::lower_bound( m_Elements.begin(), m_Elements.end(), findme );
+ return it;
+}
+
+//=============================================================================
+// find
+//=============================================================================
+template < class KeyClass, class T >
+typename Map< KeyClass, T >::iterator Map< KeyClass, T >::find( const KeyClass& key )
+{
+ RefreshIfDirty();
+ MapElement< KeyClass, T > findme;
+ findme.first = key;
+ Map::iterator it = std::lower_bound( m_Elements.begin(), m_Elements.end(), findme );
+ if( it != m_Elements.end() )
+ {
+ if( ( *it).first == key )
+ {
+ return it;
+ }
+ }
+ return m_Elements.end();
+}
+
+//=============================================================================
+// insert
+//=============================================================================
+template < class KeyClass, class T >
+void Map< KeyClass, T >::insert( const KeyClass& key, const T& element )
+{
+ //if the key is already in the map, then just replace it
+ Map::iterator found = find( key );
+ if( found != end() )
+ {
+ ( *found ).second = element;
+ }
+ else
+ {
+ // this function could be faster, not requiring a sort every time
+ // something gets inserted. if maps turn out to be slow, make this
+ // adjustment
+ MapElement< KeyClass, T > me;
+ me.first = key;
+ me.second = element;
+#ifdef RAD_DEBUG
+ size_t capacityBefore = m_Elements.capacity();
+#endif
+ m_Elements.push_back( me );
+#ifdef RAD_DEBUG
+ size_t capacityAfter = m_Elements.capacity();
+ if( capacityBefore != capacityAfter )
+ {
+ //rDebugPrintf( "This map should have been RESERVED larger before use\n" );
+ }
+#endif
+ m_Dirty = true;
+ }
+}
+
+//=============================================================================
+// operator[]
+//=============================================================================
+template < class KeyClass, class T >
+const T& Map< KeyClass, T >::operator[]( const KeyClass& key ) const
+{
+ Map::const_iterator it = find( key );
+ rAssert( it != end() );
+ return (*it).second;
+}
+
+//=============================================================================
+// get
+//=============================================================================
+template < class KeyClass, class T >
+T& Map< KeyClass, T >::get( const KeyClass& key )
+{
+ Map::iterator it = find( key );
+ rAssert( it != end() );
+ return (*it).second;
+}
+
+//=============================================================================
+// Refresh the sorting of the map if it is currently dirty
+//=============================================================================
+template < class KeyClass, class T >
+void Map< KeyClass, T >::RefreshIfDirty() const
+{
+ //THIS FUNCTION DOES NOTHING
+ if( m_Dirty )
+ {
+ std::sort( m_Elements.begin(), m_Elements.end() );
+ m_Dirty = false;
+ }
+}
+
+//=============================================================================
+// Reserves space in the map, so that it won't have to autoresize
+//=============================================================================
+template < class KeyClass, class T >
+void Map< KeyClass, T >::reserve( const size_t size )
+{
+ m_Elements.reserve( size );
+}
+
+//=============================================================================
+// returns the number of elements in the map
+//=============================================================================
+template < class KeyClass, class T >
+size_t Map< KeyClass, T >::size() const
+{
+ return m_Elements.size();
+}
+
+#endif \ No newline at end of file
diff --git a/game/code/memory/memorypool.cpp b/game/code/memory/memorypool.cpp
new file mode 100644
index 0000000..72eb097
--- /dev/null
+++ b/game/code/memory/memorypool.cpp
@@ -0,0 +1,341 @@
+//==============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: memorypool.cpp
+//
+// Description: Implementation of the memory pool based on the one described
+// in "Effective C++".
+//
+// History: + 2001/12/04 Created -- Darwin Chau
+// + 2002/06/19 Adapted form Simpsons2 -- Darwin Chau
+//
+//==============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <memory/memorypool.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// FBMemoryPool::FBMemoryPool
+//==============================================================================
+//
+// Description: Constructor
+//
+// Parameters: size
+// - size (in bytes) of the object being memory pooled
+//
+// blockCapacity
+// - controls the size of each memory block in the the pool by
+// specifying the number of objects that will fit in each block
+// (block size = size * blockCapacity)
+//
+// allocator
+// - specifiy which heap to allocate memory from
+//
+// Return: N/A.
+//
+//==============================================================================
+FBMemoryPool::FBMemoryPool
+(
+ size_t size,
+ int blockCapacity,
+ GameMemoryAllocator allocator
+)
+:
+ mSize( size ),
+ mBlockCapacity( blockCapacity ),
+ mGMAllocator( allocator ),
+ mpHeadOfFreeList( NULL ),
+ mCurrentAllocs( 0 ),
+ mCurrentBlock( 0 )
+{
+ int i;
+ for( i = 0; i < MAX_BLOCKS; ++i )
+ {
+ mpBlockArray[mCurrentBlock] = NULL;
+ }
+
+#ifndef RAD_RELEASE
+ mTotalAllocs = 0;
+ mTotalFrees = 0;
+ mPeakAllocs = 0;
+#endif // !RAD_RELEASE
+}
+
+
+//==============================================================================
+// FBMemoryPool::~FBMemoryPool
+//==============================================================================
+//
+// Description: Destructor
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+FBMemoryPool::~FBMemoryPool()
+{
+ rAssertMsg( mCurrentAllocs == 0, "*** MEMORY LEAK ! ***\n" );
+}
+
+
+//==============================================================================
+// FBMemoryPool::Allocate
+//==============================================================================
+//
+// Description: Allocate memory from the pool. The pool allocates memory in
+// large blocks which it then parcels out for subsequent allocation
+// requests. All allocations from the pool must be the same size.
+//
+// Constraints: The size of the memory pool is aritificially capped by the
+// max number of blocks that can be allocated. This was done
+// to avoid managing the memory required by an STL container class.
+//
+// Parameters: size - amount of memory requested; this is only used to verify
+// the size is the same as what the pool is configured for
+//
+// Return: pointer to allocated memory on success
+// NULL on failure
+//
+//==============================================================================
+void* FBMemoryPool::Allocate( size_t size )
+{
+ rAssert( size <= mSize );
+
+ void* pMemory = NULL;
+
+ //
+ // Allocate memory from our pool.
+ //
+ if( mpHeadOfFreeList != NULL )
+ {
+ //
+ // Allocate from the free list.
+ //
+ pMemory = static_cast<void*>( mpHeadOfFreeList );
+ mpHeadOfFreeList = mpHeadOfFreeList->mpNext;
+ }
+ else
+ {
+ //
+ // Need to allocate a new block of memory.
+ //
+ if( mCurrentBlock == MAX_BLOCKS )
+ {
+ //
+ // Array to store blocks is full.
+ //
+ rTuneString( "FBMemoryPool - Max blocks exceeded, tune pool config.\n");
+ return( NULL );
+ }
+
+ void* pNewBlock = ::operator new( (mBlockCapacity * mSize), mGMAllocator );
+ rAssert( pNewBlock != 0 );
+
+ //
+ // Store the pointer so we can delete the block later.
+ //
+ mpBlockArray[mCurrentBlock] = pNewBlock;
+ ++mCurrentBlock;
+
+
+ //
+ // Make a linked list out of the block.
+ //
+ for( int i = 0; i < mBlockCapacity - 1; ++i )
+ {
+ char* pCurrent = static_cast<char*>(pNewBlock) + (i * mSize);
+ MemoryPoolList* pList = reinterpret_cast<MemoryPoolList*>( pCurrent );
+ char* pNext = static_cast<char*>(pNewBlock) + ( (i + 1) * mSize);
+ pList->mpNext = reinterpret_cast<MemoryPoolList*>( pNext );
+ }
+
+ //
+ // Mark the end of the list with a NULL.
+ //
+ char* pLast = static_cast<char*>(pNewBlock) + ( (mBlockCapacity - 1) * mSize);
+ MemoryPoolList* pListEnd = reinterpret_cast<MemoryPoolList*>( pLast );
+ pListEnd->mpNext = NULL;
+
+ //
+ // Return this piece of memory.
+ //
+ pMemory = pNewBlock;
+
+ //
+ // This is the new head of the free list.
+ //
+ mpHeadOfFreeList = reinterpret_cast<MemoryPoolList*>( pNewBlock )->mpNext;
+ }
+
+ ++mCurrentAllocs;
+
+
+#ifndef RAD_RELEASE
+
+ ++mTotalAllocs;
+
+ if( mTotalAllocs > mPeakAllocs )
+ {
+ mPeakAllocs = mTotalAllocs;
+ }
+
+#endif // !RAD_RELEASE
+
+ return( pMemory );
+}
+
+
+//==============================================================================
+// FBMemoryPool::Free
+//==============================================================================
+//
+// Description: Return memory to the pool. When all of the memory has been
+// returned to the pool, all of the memory blocks are released.
+//
+// Parameters: mem - pointer to memory being returned to the pool
+// size - size of memory being returned.
+//
+// Return: None.
+//
+//==============================================================================
+void FBMemoryPool::Free( void* mem, size_t size )
+{
+ //
+ // Calling delete on a NULL pointer is "legal" so better handle it.
+ //
+ if( mem == NULL )
+ {
+ return;
+ }
+
+ rAssert( size <= mSize );
+
+ //
+ // Return the memory to the free list.
+ //
+ MemoryPoolList* pCarcass = static_cast<MemoryPoolList*>( mem );
+
+ pCarcass->mpNext = mpHeadOfFreeList;
+ mpHeadOfFreeList = pCarcass;
+
+ --mCurrentAllocs;
+
+ //
+ // If there are no outstanding alloctions, free all the memory blocks.
+ //
+ if( mCurrentAllocs == 0 )
+ {
+ int i;
+ for( i = 0; i < MAX_BLOCKS; ++i )
+ {
+ if( mpBlockArray[i] != NULL )
+ {
+ ::operator delete( mpBlockArray[i], mGMAllocator );
+ mpBlockArray[i] = NULL;
+ }
+ }
+
+ mpHeadOfFreeList = NULL;
+ mCurrentBlock = 0;
+ }
+
+#ifndef RAD_RELEASE
+
+ ++mTotalFrees;
+
+ if( mCurrentAllocs == 0 )
+ {
+ this->DumpStats();
+ }
+
+#endif // !RAD_RELEASE
+}
+
+
+
+//==============================================================================
+// FBMemoryPool::ChangeAllocator
+//==============================================================================
+//
+// Description: Can only switch heaps if there are no outstanding allocations.
+//
+// Parameters: allocator - switch to allocating from this heap
+//
+// Return: true if successful
+// false otherwise
+//
+//==============================================================================
+bool FBMemoryPool::ChangeAllocator( GameMemoryAllocator allocator )
+{
+ if( mCurrentAllocs == 0 )
+ {
+ mGMAllocator = allocator;
+ return( true );
+ }
+ else
+ {
+ rAssertMsg( false, "Failed to change allocators" );
+ return( false );
+ }
+}
+
+bool FBMemoryPool::OneOfOurs(void* mem)
+{
+ for( int i = 0; i < MAX_BLOCKS; ++i )
+ {
+ if( mpBlockArray[i] != NULL )
+ {
+ if((mem >= mpBlockArray[i]) && (mem < (((char*)mpBlockArray[i]) + (mBlockCapacity * mSize))))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+//==============================================================================
+// FBMemoryPool::DumpStats
+//==============================================================================
+//
+// Description: Debug spew.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void FBMemoryPool::DumpStats()
+{
+ rTunePrintf( "FBMemoryPool Stats: (Element Size: %d ; Block Capacity: %d)\n",
+ mSize, mBlockCapacity );
+ rTunePrintf( "\tCurrent Allocs: %d\n", mCurrentAllocs );
+#ifndef RAD_RELEASE
+ rTunePrintf( "\tTotal Allocs: %d\n", mTotalAllocs );
+ rTunePrintf( "\tTotal Frees: %d\n", mTotalFrees );
+ rTunePrintf( "\tPeak Allocs: %d\n", mPeakAllocs );
+#endif // !RAD_RELEASE
+}
+
diff --git a/game/code/memory/memorypool.h b/game/code/memory/memorypool.h
new file mode 100644
index 0000000..1ae4eec
--- /dev/null
+++ b/game/code/memory/memorypool.h
@@ -0,0 +1,79 @@
+//==============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: memorypool.h
+//
+// Description: Implementation of the memory pool based on the one described
+// in "Effective C++".
+//
+// History: + 2001/12/04 Created -- Darwin Chau
+// + 2002/06/19 Adapted form Simpsons2 -- Darwin Chau
+//
+//==============================================================================
+
+#ifndef MEMORYPOOL_H
+#define MEMORYPOOL_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <memory/srrmemory.h>
+
+//========================================
+// Forward References
+//========================================
+
+//==============================================================================
+//
+// Synopsis:
+//
+//==============================================================================
+class FBMemoryPool
+{
+ public:
+
+ FBMemoryPool( size_t size,
+ int blockCapacity,
+ GameMemoryAllocator allocator );
+ ~FBMemoryPool();
+
+ void* Allocate( size_t size );
+ void Free( void* mem, size_t size );
+
+ bool ChangeAllocator( GameMemoryAllocator allocator );
+ void DumpStats();
+
+ bool OneOfOurs(void*);
+
+ private:
+
+ // Declared but not defined to prevent copying and assignment.
+ FBMemoryPool( const FBMemoryPool& );
+ FBMemoryPool& operator=( const FBMemoryPool& );
+
+ struct MemoryPoolList
+ {
+ MemoryPoolList* mpNext;
+ };
+
+ size_t mSize;
+ int mBlockCapacity;
+ GameMemoryAllocator mGMAllocator;
+
+ MemoryPoolList* mpHeadOfFreeList;
+ int mCurrentAllocs;
+
+ enum{ MAX_BLOCKS = 16 };
+ void* mpBlockArray[MAX_BLOCKS];
+ int mCurrentBlock;
+
+#ifndef RAD_RELEASE
+ int mTotalAllocs;
+ int mTotalFrees;
+ int mPeakAllocs;
+#endif // !RAD_RELEASE
+};
+
+#endif // MEMORYPOOL_H
+
+
diff --git a/game/code/memory/memoryutilities.cpp b/game/code/memory/memoryutilities.cpp
new file mode 100644
index 0000000..9cac6f4
--- /dev/null
+++ b/game/code/memory/memoryutilities.cpp
@@ -0,0 +1,614 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: memoryutilities.h
+//
+// Description: some funcitons for checking the amount of free memory, etc
+//
+// History: 2002/12/03 + Created -- Ian Gipson
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#ifdef _WIN32
+ #include <crtdbg.h>
+ #ifndef _XBOX
+ #include <windows.h>
+ #endif
+#endif
+
+
+#ifdef _XBOX
+ #include <typeinfo.h>
+ #include <xtl.h>
+#endif // XBOX
+
+#ifdef RAD_PS2
+ #include <malloc.h>
+ #include <typeinfo>
+#endif // RAD_PS2
+
+#ifdef RAD_GAMECUBE
+ #include <dolphin.h>
+ extern OSHeapHandle gGCHeap;
+#endif
+//========================================
+// Project Includes
+//========================================
+#include <radmemorymonitor.hpp>
+#include <memory/createheap.h>
+#include <memory/memoryutilities.h>
+
+namespace Memory
+{
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+#ifdef RAD_PS2
+static size_t g_MaxFreeMemory = 0; //used by the PS2 to determine how much memory is free
+#endif
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+size_t AllocateLargestFreeBlock( IRadMemoryAllocator* allocator, void*& returnMemory )
+{
+ //
+ // Find out the largest block of memory I can grab
+ //
+ size_t lo = 0;
+ size_t hi = 1024*1024*256; //this is just less than 256mb
+ int pivot;
+ void* memory = NULL;
+ do
+ {
+ pivot = ( hi + lo ) / 2;
+ if( memory != NULL )
+ {
+ allocator->FreeMemory( memory );
+ memory = NULL;
+ }
+ memory = allocator->GetMemory( pivot );
+ if( memory != NULL )
+ {
+ lo = pivot;
+ }
+ else
+ {
+ memory = allocator->GetMemory( lo );
+ hi = pivot;
+ }
+ } while( ( hi - lo ) > 1 );
+ returnMemory = memory;
+ return lo;
+}
+
+//==============================================================================
+// InitializeMemoryUtilities
+//==============================================================================
+//
+// Description: does any setup required for the memory utility system. This is
+// absolutely essential on the PS2
+//
+// Parameters: None.
+//
+// Return: total amount of free memory.
+//
+// Constraints: None
+//
+//==============================================================================
+void InitializeMemoryUtilities()
+{
+ static bool alreadyCalled = false;
+ if( alreadyCalled )
+ {
+ return;
+ }
+ alreadyCalled = true;
+#ifdef RAD_PS2
+ //this is the largest amount of memory that is free
+ g_MaxFreeMemory = GetFreeMemoryProfile();
+#endif
+}
+
+//==============================================================================
+// GetFreeMemoryEntropy
+//==============================================================================
+//
+// Description: attempt at getting a measurement of fragmentation
+//
+// Parameters: None.
+//
+// Return: fragmentation metric
+//
+// Constraints: None
+//
+//==============================================================================
+float GetFreeMemoryEntropy( IRadMemoryAllocator* allocator )
+{
+ //
+ // if it's a tracking heap, just quit
+ //
+ GameMemoryAllocator which = WhichAllocator( allocator );
+ HeapType type = AllocatorType( which );
+ if( type != HEAP_TYPE_DOUG_LEA )
+ {
+ return 0.0f;
+ }
+
+// #ifdef RAD_XBOX
+// return 0.0;
+// #endif
+
+ unsigned int totalFreeMemory;
+ allocator->GetStatus( &totalFreeMemory, NULL, NULL, NULL );
+
+ //
+ // Get the largest N blocks of memory
+ //
+ const int N = 10;
+ size_t largestSize[ N ];
+ void* memory [ N ];
+
+ int i;
+ for( i = 0; i < N; ++i )
+ {
+ void* pointer = NULL;
+ unsigned int freeMemory;
+ unsigned int largestBlock;
+ allocator->GetStatus( &freeMemory, &largestBlock, NULL, NULL );
+
+ largestSize[ i ] = AllocateLargestFreeBlock( allocator, pointer );
+ memory[ i ] = pointer;
+ }
+
+ //
+ // Compute the entropy
+ //
+ float totalEntropy = 0.0f;
+ float remainingPercentage = 1.0f;
+ for( i = 0; i < N; ++i )
+ {
+ float size = static_cast< float >( largestSize[ i ] );
+ float percentage = size / static_cast< float >( totalFreeMemory );
+ float entropy = -percentage * logf( percentage ) / logf( 2.0f );
+ totalEntropy += entropy;
+ remainingPercentage -= percentage;
+ }
+
+ //
+ //
+ //
+
+ //
+ // free the memory
+ //
+ for( i = 0; i < N; ++i )
+ {
+ allocator->FreeMemory( memory[ i ] );
+ memory[ i ] = NULL;
+ }
+
+ return totalEntropy;
+}
+
+//==============================================================================
+// GetFreeMemoryProfile
+//==============================================================================
+//
+// Description: Tries to fill up availiable memory and reports back on how much
+// it got
+//
+// Parameters: None.
+//
+// Return: total amount of free memory.
+//
+// Constraints: None
+//
+//==============================================================================
+size_t GetFreeMemoryProfile()
+{
+ static int numberOfTimesCalled = 0;
+ ++numberOfTimesCalled;
+
+#ifdef _WIN32
+ return 0;
+#endif
+ const int size = 256;
+ void* pointers[ size ];
+ size_t sizes[ size ];
+
+ int index = 0;
+ int i;
+ for( i = 0; i < size; i++ )
+ {
+ pointers[ i ] = NULL;
+ sizes[ i ] = 0;
+ }
+
+ int retrys = 0;
+ do
+ {
+ //
+ // Find out the largest block of memory I can grab
+ //
+ int lo = 0;
+ int hi = 1024*1024*256; //this is just less than 256mb
+ int pivot;
+ void* memory = NULL;
+ do
+ {
+ pivot = ( hi + lo ) / 2;
+ if( memory != NULL )
+ {
+ free( memory );
+ memory = NULL;
+ }
+ memory = malloc( pivot );
+ if( memory != NULL )
+ {
+ lo = pivot;
+ }
+ else
+ {
+ memory = malloc( lo );
+ hi = pivot;
+ }
+ } while( ( hi - lo ) > 1 );
+
+ if( ( memory == NULL ) && ( retrys < 2 ) )
+ {
+ ++retrys;
+ }
+ else
+ {
+ sizes[ index ] = lo;
+ pointers[ index ] = memory;
+ memory = NULL;
+ ++index;
+ }
+ } while( ( pointers[ index - 1 ] != NULL ) && ( index < size ) );
+
+ //
+ // Need to sum the memory
+ //
+ size_t total = 0;
+ for( i = 0; i < size; i++ )
+ {
+ total += sizes[ i ];
+ }
+
+ //
+ // Need to delete the memory
+ //
+ for( i = 0; i < size; i++ )
+ {
+ void* pointer = pointers[ i ];
+ if( pointer != NULL )
+ {
+ free( pointer );
+ pointers[ i ] = NULL;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+#ifndef FINAL
+ //rReleasePrintf( "Free Memory Profile - %d\n", total );
+#endif
+ return total;
+}
+
+//==============================================================================
+// GetLargestFreeBlock
+//==============================================================================
+//
+// Description: Attempts via a binary search to determine the largest free
+// block of main memory (uses malloc)
+//
+// Parameters: None.
+//
+// Return: total amount of free memory.
+//
+// Constraints: None
+//
+//==============================================================================
+size_t GetLargestFreeBlock()
+{
+ #ifdef _WIN32
+ return 0;
+ #endif
+
+ #ifdef RAD_GAMECUBE
+ return 0;
+ #endif
+
+ //
+ // Find out the largest block of memory I can grab
+ //
+ size_t lo = 0;
+ size_t hi = 1024*1024*256; //this is just less than 256mb
+ int pivot;
+ void* memory = NULL;
+ do
+ {
+ pivot = ( hi + lo ) / 2;
+ if( memory != NULL )
+ {
+ free( memory );
+ memory = NULL;
+ }
+ memory = malloc( pivot );
+ if( memory != NULL )
+ {
+ lo = pivot;
+ }
+ else
+ {
+ memory = malloc( lo );
+ hi = pivot;
+ }
+ } while( ( hi - lo ) > 1 );
+ free( memory );
+ return hi;
+}
+
+//==============================================================================
+// GetLargestFreeBlock
+//==============================================================================
+//
+// Description: Attempts via a binary search to determine the largest free
+// block of main memory (uses malloc)
+//
+// Parameters: allocator - the allocator to test
+//
+// Return: total amount of free memory.
+//
+// Constraints: None
+//
+//==============================================================================
+size_t GetLargestFreeBlock( IRadMemoryAllocator* allocator )
+{
+ //
+ // Can't call this on the persistent heap
+ //
+ IRadMemoryAllocator* persistent = GetAllocator( GMA_PERSISTENT );
+ if( allocator == persistent )
+ {
+ unsigned int totalFreeMemory;
+ unsigned int largestBlock;
+ unsigned int numberOfObjects;
+ unsigned int highWater;
+ allocator->GetStatus( &totalFreeMemory, &largestBlock, &numberOfObjects, &highWater );
+ return largestBlock;
+ }
+
+ //
+ // Find out the largest block of memory I can grab
+ //
+ void* memory = NULL;
+ size_t size = AllocateLargestFreeBlock( allocator, memory );
+ allocator->FreeMemory( memory );
+ return size;
+}
+
+//==============================================================================
+// GetLargestNFreeBlocks
+//==============================================================================
+//
+// Description: Attempts via a binary search to determine the largest free
+// block of main memory (uses malloc)
+//
+// Parameters: allocator - the allocator to test
+//
+// Return: total amount of free memory.
+//
+// Constraints: None
+//
+//==============================================================================
+void GetLargestNFreeBlocks( IRadMemoryAllocator* allocator, const int n, size_t blocks[] )
+{
+}
+
+//==============================================================================
+// GetMaxFreeMemory
+//==============================================================================
+//
+// Description: Returns the maximum amount of memory that has ever been free
+// in the lifetime of the game
+//
+// Parameters: None.
+//
+// Return: total amount of free memory.
+//
+// Constraints: None
+//
+//==============================================================================
+size_t GetMaxFreeMemory()
+{
+#ifdef RAD_PS2
+ return g_MaxFreeMemory;
+#else
+ rAssertMsg( false, "GetMaxFreeMemory - this doesnt work on any platform but the PS2\n" );
+ return 0;
+#endif
+}
+
+//=============================================================================
+// GetTotalMemoryFree
+//=============================================================================
+//
+// Description: Returns the total amount of free memory
+//
+// Parameters: None.
+//
+// Return: total amount of free memory.
+//
+// Constraints: None
+//
+//=============================================================================
+size_t GetTotalMemoryFree()
+{
+ #if defined _WIN32
+ MEMORYSTATUS status;
+ GlobalMemoryStatus (&status);
+ return status.dwAvailPhys;
+ #elif defined RAD_PS2
+ size_t used = GetTotalMemoryUsed();
+ size_t maxFree = GetMaxFreeMemory();
+ size_t free = maxFree - used;
+ return free;
+ #elif defined RAD_GAMECUBE
+ return OSCheckHeap( gGCHeap );
+ #else
+ #pragma error( "CMemoryTracker::GetTotalMemoryFree - What platform is this then?");
+ #endif
+ return 0;
+}
+
+//=============================================================================
+// GetTotalMemoryFreeLowWaterMark
+//=============================================================================
+//
+// Description: Returns the low water mark of free memory. Must be called at
+// the frequency at which you want the memory sampled
+//
+// Parameters: None.
+//
+// Return: total amount of free memory at the worst point in the game
+//
+// Constraints: None
+//
+//=============================================================================
+size_t GetTotalMemoryFreeLowWaterMark()
+{
+ static size_t lowWaterMark = GetTotalMemoryFree();
+ size_t currentFree = GetTotalMemoryFree();
+ if( currentFree < lowWaterMark )
+ {
+ lowWaterMark = currentFree;
+ }
+ return lowWaterMark;
+}
+
+//=============================================================================
+// GetTotalMemoryUnavailable
+//=============================================================================
+//
+// Description: Returns the total amount of free memory
+//
+// Parameters: None.
+//
+// Return: total amount of free memory.
+//
+// Constraints: None
+//
+//=============================================================================
+size_t GetTotalMemoryUnavailable()
+{
+ #if defined _WIN32
+ //IAN didn't bother writing this yet
+ return 0;
+ #elif defined RAD_PS2
+ #ifdef RAD_RELEASE
+ size_t totalPhysicalMemory = 1024 * 1024 * 32;
+ #else
+ size_t totalPhysicalMemory = 1024 * 1024 * 128;
+ #endif
+ size_t maxFree = GetMaxFreeMemory();
+ size_t unavailable = totalPhysicalMemory - maxFree;
+ return unavailable;
+ #elif defined RAD_GAMECUBE
+ return 0;
+ #else
+ #pragma error( "CMemoryTracker::GetTotalMemoryFree - What platform is this then?");
+ #endif
+ return 0;
+}
+
+//==============================================================================
+// GetTotalMemoryUsed
+//==============================================================================
+//
+// Description: query the OS with regard to the amount of memory used
+//
+// Parameters: None.
+//
+// Return: total amount of memory used
+//
+// Constraints: None
+//
+//==============================================================================
+size_t GetTotalMemoryUsed()
+{
+#if defined _WIN32
+ #ifdef RAD_DEBUG
+ _CrtMemState state;
+ _CrtMemCheckpoint( &state );
+ int total = 0;
+ int i;
+ for( i = 0; i < _MAX_BLOCKS; i++ )
+ {
+ total += state.lSizes[ i ];
+ }
+
+ //double check against the MEMORYSTATUS numbers
+ MEMORYSTATUS status;
+ GlobalMemoryStatus (&status);
+ size_t doubleCheck = status.dwTotalPhys - status.dwAvailPhys;
+ //double check these numbers - they should match on the XBOX!
+ return total;
+ #else
+ return 0;
+ #endif
+#endif
+
+#if defined RAD_PS2
+ struct mallinfo info = mallinfo();
+ return info.uordblks;
+#endif
+
+#if defined RAD_GAMECUBE
+ return 0;
+#endif
+};
+
+//=============================================================================
+// PrintMemoryStatsToTty
+//=============================================================================
+//
+// Description: Prints the total free memory to the TTY
+//
+// Parameters: None.
+//
+// Return: None
+//
+// Constraints: None
+//
+//=============================================================================
+void PrintMemoryStatsToTty()
+{
+#ifdef RAD_PS2
+ size_t totalFree = GetTotalMemoryFree();
+ size_t profileFree = GetFreeMemoryProfile();
+ size_t largestFree = GetLargestFreeBlock();
+ size_t lowWaterMark = GetTotalMemoryFreeLowWaterMark();
+ rReleasePrintf( "MEMSTATS\ttotalfree:%d\tprofile:%d\tlargestBlock:%d\tlowWater:%d\n", totalFree, profileFree, largestFree, lowWaterMark );
+#else
+ size_t totalFree = GetTotalMemoryFree();
+ rReleasePrintf( "MEMSTATS\t%d\t\n", totalFree );
+#endif
+}
+
+} //namespace MEMORY
diff --git a/game/code/memory/memoryutilities.h b/game/code/memory/memoryutilities.h
new file mode 100644
index 0000000..7d6bf0b
--- /dev/null
+++ b/game/code/memory/memoryutilities.h
@@ -0,0 +1,42 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: memoryutilities.h
+//
+// Description: functions for telling you things about memory
+//
+// History: 2002/12/03 + Created -- Ian Gipson
+//
+//=============================================================================
+
+#ifndef MEMORYUTILITIES_H
+#define MEMORYUTILITIES_H
+
+//========================================
+// Nested Includes
+//========================================
+struct IRadMemoryAllocator;
+
+//==============================================================================
+//
+// Synopsis: This class allows you to tag allocations for reporting in the
+// FTech Memory Monitor.
+//
+//==============================================================================
+namespace Memory
+{
+ void InitializeMemoryUtilities();
+ float GetFreeMemoryEntropy( IRadMemoryAllocator* allocator );
+ size_t GetFreeMemoryProfile();
+ size_t GetLargestFreeBlock();
+ size_t GetLargestFreeBlock( IRadMemoryAllocator* allocator );
+ void GetLargestNFreeBlocks( IRadMemoryAllocator* allocator, const int n, size_t blocks[] );
+ size_t GetMaxFreeMemory();
+ size_t GetTotalMemoryFree();
+ size_t GetTotalMemoryFreeLowWaterMark();
+ size_t GetTotalMemoryUnavailable();
+ size_t GetTotalMemoryUsed();
+ void PrintMemoryStatsToTty();
+}
+
+#endif //MEMORYTAGGER_H \ No newline at end of file
diff --git a/game/code/memory/propstats.cpp b/game/code/memory/propstats.cpp
new file mode 100644
index 0000000..bb387d5
--- /dev/null
+++ b/game/code/memory/propstats.cpp
@@ -0,0 +1,181 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Propstats
+//
+// Description: Singleton that logs prop memory usage
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#ifndef RAD_RELEASE
+
+#include <memory/propstats.h>
+#include <sstream>
+#include <raddebugwatch.hpp>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+static GameMemoryAllocator s_ListOfValidHeaps[] =
+{
+#ifdef RAD_GAMECUBE
+ GMA_GC_VMM,
+#endif
+ GMA_LEVEL_ZONE,
+};
+
+static int s_NumHeapsToCheck = sizeof( s_ListOfValidHeaps )/sizeof( s_ListOfValidHeaps[0] );
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+bool PropStats::s_TrackingEnabled = false;
+int PropStats::s_MemInUseAtStart = 0;
+PropStats::PropMemMap PropStats::s_PropMemData;
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// PropStats::PropStats
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+PropStats::PropStats()
+{
+
+}
+
+PropStats::~PropStats()
+{
+
+}
+
+void PropStats::EnableTracking()
+{
+ if ( s_TrackingEnabled == false )
+ {
+ s_TrackingEnabled = true;
+ s_PropMemData.reserve( 50 );
+ radDbgWatchAddFunction( "Dump PropStats", Print, NULL, "Memory" );
+ }
+}
+
+void PropStats::StartTracking( const char* proptype )
+{
+ if ( s_TrackingEnabled )
+ {
+ // Check to see if we already have this prop in memory
+ PropMemMapIt it = s_PropMemData.find( proptype );
+ if ( it == s_PropMemData.end() )
+ {
+ // Not present, push a new pair
+ MemUsage usage;
+ s_PropMemData.insert( proptype, usage );
+ }
+ s_MemInUseAtStart = GetMemAvailable();
+ }
+}
+
+void PropStats::IncreaseInstanceCount( const char* proptype, int count )
+{
+ if ( s_TrackingEnabled )
+ {
+ PropMemMapIt it = s_PropMemData.find( proptype );
+ rAssert( it != s_PropMemData.end() );
+ it->second.numInstances += count;
+ }
+
+}
+
+void PropStats::StopTracking( const char* proptype, int count )
+{
+ if ( s_TrackingEnabled )
+ {
+ int memInUseNow = GetMemAvailable();
+ PropMemMapIt it = s_PropMemData.find( proptype );
+ rAssert( it != s_PropMemData.end() );
+ if ( it->second.numInstances == 0 && count == 1 )
+ {
+ it->second.memUsedInitialInstance = ( memInUseNow - s_MemInUseAtStart );
+ }
+ it->second.numInstances += count;
+ it->second.memUsed += ( memInUseNow - s_MemInUseAtStart );
+ }
+}
+
+void PropStats::Print( void* userData )
+{
+ rTunePrintf( "--------------------------------------------------------\n" );
+
+ float totalMemoryAllProps = 0;
+ int totalNumAllProps = 0;
+
+ PropMemMapIt it;
+ for ( it = s_PropMemData.begin() ; it != s_PropMemData.end() ; it++ )
+ {
+ std::ostringstream stream;
+ int count = it->second.numInstances;
+ float initialCost = it->second.memUsedInitialInstance / 1024.0f;
+ float totalKBUsed = static_cast< float >( it->second.memUsed ) / 1024.0f;
+ float avgKBUsed = (totalKBUsed - initialCost ) / count;
+
+ totalMemoryAllProps += totalKBUsed;
+ totalNumAllProps += count;
+
+ stream << it->first << ", " << count << " instances, Total: " << totalKBUsed <<
+ " KB";
+
+ if ( avgKBUsed > 0 )
+ {
+ stream << " Instance cost: " << avgKBUsed << " KB.";
+ }
+
+ if ( it->second.memUsedInitialInstance > 0 )
+ {
+ stream << " Initial cost: " << initialCost << " KB.";
+ }
+
+ stream << std::endl;
+
+
+ rTunePrintf( stream.str().c_str() );
+ }
+ rTunePrintf( "--------------------------------------------------------\n" );
+
+ std::ostringstream stream;
+ stream << "Total number of props : " << totalNumAllProps << std::endl;;
+ stream << "Total number of kbytes used : " << totalMemoryAllProps << std::endl;;
+ rTunePrintf( stream.str().c_str() );
+ rTunePrintf( "--------------------------------------------------------\n" );
+}
+
+int PropStats::GetMemAvailable()
+{
+ int memcount = 0;
+ for ( int i = 0 ; i < s_NumHeapsToCheck ; i++ )
+ {
+ memcount += HeapMgr()->GetLoadedUsage( s_ListOfValidHeaps[i] );
+ }
+ return memcount;
+}
+
+#endif \ No newline at end of file
diff --git a/game/code/memory/propstats.h b/game/code/memory/propstats.h
new file mode 100644
index 0000000..47487ed
--- /dev/null
+++ b/game/code/memory/propstats.h
@@ -0,0 +1,93 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Propstats
+//
+// Description: Singleton that logs prop memory usage
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+#ifndef RAD_RELEASE
+
+// Recompilation protection flag.
+#ifndef PROPSTATS_H
+#define PROPSTATS_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <memory/srrmemory.h>
+#include <memory/map.h>
+#include <string>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+//
+// Constraints:
+//
+//
+//===========================================================================
+class PropStats
+{
+ public:
+ PropStats();
+ ~PropStats();
+
+ static void EnableTracking();
+
+ static void StartTracking( const char* proptype );
+ static void IncreaseInstanceCount( const char* proptype, int count );
+ static void StopTracking( const char* proptype, int count = 0 );
+
+ static void Print( void* userData );
+
+ protected:
+
+ static int s_MemInUseAtStart;
+ static bool s_TrackingEnabled;
+ static int GetMemAvailable();
+
+ struct MemUsage
+ {
+ MemUsage() : numInstances( 0 ), memUsed( 0 ), memUsedInitialInstance( 0 ){}
+ int numInstances;
+ int memUsed;
+ int memUsedInitialInstance;
+ };
+
+ typedef Map< std::string, MemUsage > PropMemMap;
+ typedef PropMemMap::iterator PropMemMapIt;
+
+ static PropMemMap s_PropMemData;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow PropStats from being copied and assigned.
+ PropStats( const PropStats& );
+ PropStats& operator=( const PropStats& );
+
+};
+
+#endif
+#endif \ No newline at end of file
diff --git a/game/code/memory/srrmemory.cpp b/game/code/memory/srrmemory.cpp
new file mode 100644
index 0000000..8a55859
--- /dev/null
+++ b/game/code/memory/srrmemory.cpp
@@ -0,0 +1,2083 @@
+//==============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: memory.cpp
+//
+// Description:
+//
+// History: 3/20/2002 + Created -- Darwin Chau
+//
+//==============================================================================
+
+//========================================
+// System Includes
+//========================================
+#ifdef RAD_PS2
+#include <malloc.h>
+#endif
+
+// Foundation Tech
+#include <radmath/util.hpp>
+#include <radmemory.hpp>
+#include <radmemorymonitor.hpp>
+#include <radthread.hpp>
+#include <raddebug.hpp>
+#include <radtextdisplay.hpp>
+#include <radsound_hal.hpp>
+#include <p3d/utility.hpp>
+
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <main/game.h>
+#include <memory/createheap.h>
+#include <memory/srrmemory.h>
+#include <memory/memoryutilities.h>
+
+#include <cheats/cheatinputsystem.h>
+
+#include <mission/gameplaymanager.h>
+
+#ifdef RAD_PS2
+#include <pddi/pddiext.hpp>
+#include <main/ps2platform.h>
+#define INIT_MEM() Memory::InitializeMemoryUtilities();PS2Platform::InitializeMemory();
+#endif // RAD_PS2
+
+#ifdef RAD_XBOX
+#include <main/xboxplatform.h>
+#define INIT_MEM() Memory::InitializeMemoryUtilities();XboxPlatform::InitializeMemory();radMemoryInitialize();
+void MemoryHackCallback() { INIT_MEM() };
+#endif // RAD_XBOX
+
+#ifdef RAD_GAMECUBE
+#include <main/gcplatform.h>
+#include <dolphin.h>
+#define INIT_MEM() Memory::InitializeMemoryUtilities();GCPlatform::InitializeMemory();
+extern IRadMemoryHeap *vmmHeap;
+void MemoryHackCallback() { INIT_MEM() };
+#endif // RAD_GAMECUBE
+
+#ifdef RAD_PS2
+ void MemoryHackCallback() { INIT_MEM() };
+#endif
+
+#ifdef RAD_WIN32
+#include <main/win32platform.h>
+#define INIT_MEM() Memory::InitializeMemoryUtilities();Win32Platform::InitializeMemory();
+#define SHUTDOWN_MEM() Win32Platform::ShutdownMemory();
+#endif // RAD_WIN32
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+bool g_LockedPersistentHeap = false;
+bool g_HeapManagerCreated = false;
+//
+// Has the memory system been intialized via FTech?
+//
+bool gMemorySystemInitialized = false;
+
+//
+// Temporarily disable allocation routing (to avoid infinite loops)
+//
+bool g_NoHeapRoute = false;
+
+const char* HeapNames[] =
+{
+ "Default Heap",
+ "Temp Heap",
+ "GameCube Virtual Memory Heap",
+ "Persistent Heap",
+ "Level Heap",
+ "Movie Heap",
+ "Frontend Heap",
+ "Zone and Rail Heap",
+ "Level Other Heap",
+ "HUD Heap",
+ "Mission Heap",
+ "Audio Heap",
+ "Debug Heap",
+ "Special Heap",
+ "Music Heap",
+ "Audio Persistent",
+ "Small Alloc Heap",
+#ifdef RAD_XBOX
+ "Xbox Sound Heap",
+#endif
+#ifdef USE_CHAR_GAG_HEAP
+ "Character and Gag Heap",
+#endif
+ "Anywhere in Level",
+ "Anywhere in FE",
+ "Either Other or Zone",
+ "Allocator Search"
+};
+
+
+#ifndef RAD_RELEASE
+//
+// For overriding all allocation routing and sending everything to the special heap
+//
+bool HeapManager::s_bSpecialRoute = false;
+#endif
+
+inline void* AllocateThis( GameMemoryAllocator allocator, size_t size )
+{
+ void* pMemory = NULL;
+#ifdef CORRAL_SMALL_ALLOCS
+ if( size < 201 )
+ {
+ if( allocator != GMA_AUDIO_PERSISTENT && allocator != GMA_PERSISTENT && allocator != GMA_TEMP)
+ {
+ pMemory = radMemoryAlloc( GMA_SMALL_ALLOC, size );
+ }
+ else
+ {
+ pMemory = radMemoryAlloc( allocator, size );
+ }
+ }
+ else
+#endif
+ {
+ if ( allocator >= GMA_ANYWHERE_IN_LEVEL && allocator != ALLOCATOR_SEARCH )
+ {
+ pMemory = FindFreeMemory( allocator, size );
+ }
+ else
+ {
+ //pMemory = radMemoryAlloc( ::radMemoryGetCurrentAllocator (), size );
+ pMemory = radMemoryAlloc( allocator, size );
+ }
+ }
+
+ return pMemory;
+}
+
+//==============================================================================
+// new
+//==============================================================================
+//
+// Description: regular new
+//
+// Parameters: size - size of memory we're allocating
+//
+// Return:
+//
+//==============================================================================
+void* operator new( size_t size )
+#ifdef RAD_PS2
+#ifndef RAD_MW
+throw( std::bad_alloc )
+#endif
+#endif
+{
+ if( gMemorySystemInitialized == false )
+ {
+ INIT_MEM();
+ }
+
+ void* pMemory;
+
+ if (g_NoHeapRoute)
+ {
+ pMemory = radMemoryAlloc( 0, size );
+ }
+ else
+ {
+ GameMemoryAllocator curr = HeapMgr()->GetCurrentHeap();
+ pMemory = AllocateThis( curr, size );
+
+#ifdef MEMORYTRACKER_ENABLED
+ ::radMemoryMonitorIdentifyAllocation (pMemory, HeapMgr()->GetCurrentGroupID ());
+#endif
+ }
+
+
+ //MEMTRACK_ALLOC( pMemory, size, 0 );
+
+ return( pMemory );
+}
+
+
+//==============================================================================
+// delete
+//==============================================================================
+//
+// Description: regular delete
+//
+// Parameters: pMemory - pointer to the memory we're deleting
+//
+// Return:
+//
+//==============================================================================
+void operator delete( void* pMemory )
+#ifdef RAD_PS2
+#ifndef RAD_MW
+throw()
+#endif
+#endif
+{
+ radMemoryFree( pMemory );
+}
+
+
+//==============================================================================
+//
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void* operator new[]( size_t size )
+#ifdef RAD_PS2
+#ifndef RAD_MW
+throw( std::bad_alloc )
+#endif
+#endif
+{
+ if( gMemorySystemInitialized == false )
+ {
+ INIT_MEM();
+ }
+
+ void* pMemory;
+
+ if (g_NoHeapRoute)
+ {
+ pMemory = radMemoryAlloc( 0, size );
+ }
+ else
+ {
+ GameMemoryAllocator curr = HeapMgr()->GetCurrentHeap();
+ pMemory = AllocateThis( curr, size );
+
+#ifdef MEMORYTRACKER_ENABLED
+ ::radMemoryMonitorIdentifyAllocation (pMemory, HeapMgr()->GetCurrentGroupID ());
+#endif
+ }
+
+ //MEMTRACK_ALLOC( pMemory, size, ALLOC_ARRAY );
+
+ return( pMemory );
+}
+
+
+//==============================================================================
+//
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void operator delete[]( void* pMemory )
+#ifdef RAD_PS2
+#ifndef RAD_MW
+throw()
+#endif
+#endif
+{
+ radMemoryFree( pMemory );
+}
+
+
+//==============================================================================
+//
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void* operator new( size_t size, GameMemoryAllocator allocator )
+{
+ if( gMemorySystemInitialized == false )
+ {
+ INIT_MEM();
+ }
+
+#ifndef RAD_RELEASE
+ if (HeapManager::s_bSpecialRoute)
+ {
+ allocator = GMA_SPECIAL;
+ }
+#endif
+
+ void* pMemory = AllocateThis( allocator, size );
+
+ if (!g_NoHeapRoute)
+ {
+#ifdef MEMORYTRACKER_ENABLED
+ ::radMemoryMonitorIdentifyAllocation (pMemory, HeapMgr()->GetCurrentGroupID ());
+#endif
+ }
+
+ //MEMTRACK_ALLOC( pMemory, size, 0 );
+
+ return( pMemory );
+}
+
+
+//==============================================================================
+//
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void operator delete( void* pMemory, GameMemoryAllocator allocator )
+{
+ radMemoryFree( pMemory );
+}
+
+
+//==============================================================================
+//
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void* operator new[]( size_t size, GameMemoryAllocator allocator )
+{
+ if( gMemorySystemInitialized == false )
+ {
+ INIT_MEM();
+ }
+
+#ifndef RAD_RELEASE
+ if (HeapManager::s_bSpecialRoute)
+ {
+ allocator = GMA_SPECIAL;
+ }
+#endif
+
+ void* pMemory = AllocateThis( allocator, size );
+
+ if (!g_NoHeapRoute)
+ {
+#ifdef MEMORYTRACKER_ENABLED
+ ::radMemoryMonitorIdentifyAllocation (pMemory, HeapMgr()->GetCurrentGroupID ());
+#endif
+ }
+
+ //MEMTRACK_ALLOC( pMemory, size, ALLOC_ARRAY );
+
+ return( pMemory );
+}
+
+
+//==============================================================================
+//
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void operator delete[]( void* pMemory, GameMemoryAllocator allocator )
+{
+ radMemoryFree( pMemory );
+}
+
+static GameMemoryAllocator AVAILABLE_FOR_RENT_IN_LEVEL[] =
+{
+ GMA_TEMP,
+ GMA_LEVEL_ZONE, // 7 6 6
+ GMA_LEVEL_OTHER, // 8 7 7
+ GMA_LEVEL_MISSION, // 10 9 9
+ GMA_LEVEL_HUD // 9 8 8
+};
+static GameMemoryAllocator AVAILABLE_FOR_RENT_IN_FE[] =
+{
+ GMA_LEVEL_MOVIE, // 5 4 4
+ GMA_LEVEL_FE, // 6 5 5
+ GMA_LEVEL_AUDIO // 11 10 10
+};
+static GameMemoryAllocator AVAILABLE_IN_OTHER_OR_ZONE[] =
+{
+ GMA_LEVEL_OTHER, // 8 7 7
+ GMA_LEVEL_ZONE // 7 6 6
+};
+
+
+void* FindFreeMemory( GameMemoryAllocator allocator, size_t size )
+{
+ GameMemoryAllocator* list = NULL;
+ unsigned int numAvailable = 0;
+
+ if ( allocator == GMA_ANYWHERE_IN_LEVEL )
+ {
+ list = AVAILABLE_FOR_RENT_IN_LEVEL;
+ numAvailable = sizeof ( AVAILABLE_FOR_RENT_IN_LEVEL ) / sizeof( GameMemoryAllocator );
+ }
+ else if ( allocator == GMA_ANYWHERE_IN_FE )
+ {
+ list = AVAILABLE_FOR_RENT_IN_FE;
+ numAvailable = sizeof ( AVAILABLE_FOR_RENT_IN_FE ) / sizeof( GameMemoryAllocator );
+ }
+ else if ( allocator == GMA_EITHER_OTHER_OR_ZONE )
+ {
+ list = AVAILABLE_IN_OTHER_OR_ZONE;
+ numAvailable = sizeof ( AVAILABLE_IN_OTHER_OR_ZONE ) / sizeof( GameMemoryAllocator );
+ }
+ else
+ {
+ rAssert( false );
+ }
+
+ if ( list != NULL )
+ {
+ ::radMemorySetUsableAllocators( (radMemoryAllocator*)list, numAvailable );
+ void* memory = radMemoryAlloc( ALLOCATOR_SEARCH, size );
+
+ return memory;
+ }
+
+ return NULL;
+}
+
+void SetupAllocatorSearch( GameMemoryAllocator allocator )
+{
+ rAssert( allocator >= GMA_ANYWHERE_IN_LEVEL );
+
+ GameMemoryAllocator* list = NULL;
+ unsigned int numAvailable = 0;
+
+ if ( allocator == GMA_ANYWHERE_IN_LEVEL )
+ {
+ list = AVAILABLE_FOR_RENT_IN_LEVEL;
+ numAvailable = sizeof ( AVAILABLE_FOR_RENT_IN_LEVEL ) / sizeof( GameMemoryAllocator );
+ }
+ else if ( allocator == GMA_ANYWHERE_IN_FE )
+ {
+ list = AVAILABLE_FOR_RENT_IN_FE;
+ numAvailable = sizeof ( AVAILABLE_FOR_RENT_IN_FE ) / sizeof( GameMemoryAllocator );
+ }
+ else if ( allocator == GMA_EITHER_OTHER_OR_ZONE )
+ {
+ list = AVAILABLE_IN_OTHER_OR_ZONE;
+ numAvailable = sizeof ( AVAILABLE_IN_OTHER_OR_ZONE ) / sizeof( GameMemoryAllocator );
+ }
+ else
+ {
+ rAssert( false );
+ }
+
+ if ( list != NULL )
+ {
+ radMemorySetUsableAllocators( (radMemoryAllocator*)list, numAvailable );
+ }
+}
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//
+// Use FTT's radTextDisplay functionality to slap an out-of-memory
+// message onto the screen before we die
+//
+void PrintOutOfMemoryMessage( void* userData, radMemoryAllocator heap, const unsigned int size )
+{
+#ifndef FINAL
+ //
+ // Print out memory information to the TTY first
+ //
+ Memory::PrintMemoryStatsToTty();
+
+ //Disable this while we're here...
+ ::radMemorySetOutOfMemoryCallback( NULL, NULL );
+
+#ifdef RAD_GAMECUBE
+ ::radMemoryMonitorSuspend();
+#endif
+
+ IRadTextDisplay* textDisplay;
+ GameMemoryAllocator heapEnum = static_cast<GameMemoryAllocator>(heap);
+
+#ifdef RAD_PS2
+ //
+ // Need to shut down the MFIFO for this to work properly
+ //
+ ((pddiExtPS2Control*)p3d::pddi->GetExtension(PDDI_EXT_PS2_CONTROL))->MFIFOEnable( false );
+ rReleasePrintf("MFIFO Disabled.\n");
+#endif
+
+ //
+ // Ironically, the text display object needs to be allocated on a heap,
+ // one of which is out of memory. Hopefully we're not out on more
+ // than one.
+ //
+ if( heapEnum == GMA_DEFAULT )
+ {
+ ::radTextDisplayGet( &textDisplay, GMA_TEMP );
+ }
+ else
+ {
+ ::radTextDisplayGet( &textDisplay, GMA_DEFAULT );
+ }
+
+ IRadMemoryAllocator* heapPtr = GetAllocator( static_cast< GameMemoryAllocator >( heap ) );
+ unsigned int totalFree = 0;
+ unsigned int largestBlock = 0;
+ unsigned int numberOfObjects = 0;
+ unsigned int highWaterMark = 0;
+ unsigned int lastAllocation = 0;
+ if( heapPtr != NULL )
+ {
+ lastAllocation = size;
+ heapPtr->GetStatus( &totalFree, &largestBlock, &numberOfObjects, &highWaterMark );
+ largestBlock = Memory::GetLargestFreeBlock( heapPtr );
+ }
+ char freeMemoryString[256] = "";
+ char largestBlockString[256] = "";
+ char lastAllocationStr[256] = "";
+ sprintf( freeMemoryString, "Free: %d", totalFree );
+ sprintf( largestBlockString, "LargestBlock: %d", largestBlock );
+ sprintf( lastAllocationStr, "LastAlloc: %d", lastAllocation );
+
+ int yPos = 10;
+ if ( textDisplay )
+ {
+ //GetHeapMgr()->DumpHeapStats(true)
+ textDisplay->SetBackgroundColor( 0 );
+ textDisplay->SetTextColor( 0xffffffff );
+ textDisplay->Clear();
+ textDisplay->TextOutAt( "OUT OF MEMORY", 20, yPos );
+ textDisplay->TextOutAt( HeapNames[heapEnum], 20, yPos += 1 );
+ textDisplay->TextOutAt( freeMemoryString, 20, yPos += 1 );
+ textDisplay->TextOutAt( largestBlockString, 20, yPos += 1 );
+ textDisplay->TextOutAt( lastAllocationStr, 20, yPos += 1 );
+
+ if ( CommandLineOptions::Get( CLO_DEMO_TEST ) ||
+ GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_DEMO_TEST ) )
+ {
+ char buffy[32];
+ sprintf( buffy, "Demo Count: %d", GetGame()->GetDemoCount() );
+ textDisplay->TextOutAt( buffy, 20, yPos += 2 );
+
+ unsigned int time = GetGame()->GetTime();
+ unsigned int hours = time / 3600000;
+ unsigned int deltaTime = time % 3600000;
+
+ unsigned int minutes = deltaTime / 60000;
+ deltaTime = deltaTime % 60000;
+
+ unsigned int seconds = deltaTime / 1000;
+ deltaTime = deltaTime % 1000;
+ sprintf( buffy, "Time: %d:%d:%d.%d", hours, minutes, seconds, deltaTime );
+ textDisplay->TextOutAt( buffy, 20, yPos += 2 );
+
+ if ( GetGameplayManager() )
+ {
+ sprintf( buffy, "Level %d", GetGameplayManager()->GetCurrentLevelIndex() );
+ textDisplay->TextOutAt( buffy, 20, yPos += 2 );
+ }
+ }
+
+ textDisplay->SwapBuffers();
+ textDisplay->Release();
+ }
+
+ rReleaseBreak();
+
+#ifndef RAD_GAMECUBE
+ ::radMemoryMonitorSuspend();
+#endif
+
+ //Re-enable now that we're through
+ ::radMemorySetOutOfMemoryCallback( PrintOutOfMemoryMessage, NULL );
+#endif
+}
+
+static int pushNumberStack[ 48 ];
+static int currentStackPointer = 0;
+
+HeapStack::HeapStack (GameMemoryAllocator defaultAllocator)
+{
+ int i;
+ for( i = 0; i < 48; ++i )
+ {
+ pushNumberStack[ i ] = -1;
+ }
+
+ m_Size = STACKSIZE;
+ m_CurPos = 0;
+ m_Stack = new(GMA_PERSISTENT) GameMemoryAllocator[m_Size];
+
+ // Seed this with the default heap
+ //
+ m_Stack[m_CurPos] = defaultAllocator;
+}
+
+
+HeapStack::~HeapStack ()
+{
+ delete[] m_Stack;
+}
+
+void HeapStack::Push (GameMemoryAllocator alloc)
+{
+ static int pushNumber = 0;
+ rAssert( currentStackPointer < 48 );
+ pushNumberStack[ currentStackPointer ] = pushNumber;
+ ++currentStackPointer;
+ ++pushNumber;
+ m_CurPos++;
+ rAssertMsg (m_CurPos < m_Size, "Heap Stack Overflow! Increase size of the Heap Stack. [jdy]");
+ m_Stack[m_CurPos] = alloc;
+}
+
+void HeapStack::Pop ()
+{
+ --currentStackPointer;
+ pushNumberStack[ currentStackPointer ] = -1;
+ GameMemoryAllocator current = m_Stack[ m_CurPos ];
+ m_CurPos--;
+ rAssertMsg (m_CurPos >= 0, "Heap Stack Underflow! Calls to Push and Pop on heap stack are mismatched. [jdy]");
+}
+
+void HeapStack::Pop( GameMemoryAllocator alloc )
+{
+ --currentStackPointer;
+ pushNumberStack[ currentStackPointer ] = -1;
+ rAssertMsg( m_Stack[ m_CurPos ] == alloc, "HeapStack - Detected mismatch in push/pop calls - fix, or tell Ian Gipson immediately" );
+ m_CurPos--;
+ rAssertMsg (m_CurPos >= 0, "Heap Stack Underflow! Calls to Push and Pop on heap stack are mismatched. [jdy]");
+}
+
+
+void HeapStack::SetTop (GameMemoryAllocator alloc)
+{
+ m_Stack[m_CurPos] = alloc;
+}
+
+
+GameMemoryAllocator HeapStack::Top () const
+{
+ return m_Stack[m_CurPos];
+}
+
+
+HeapActivityTracker::HeapActivityTracker ()
+{
+ for (int i = 0; i < NUM_GAME_MEMORY_ALLOCATORS; i++)
+ {
+ m_AllocsEnabled[i] = true;
+ m_FreesEnabled[i] = true;
+ }
+}
+
+
+void HeapActivityTracker::MemoryAllocated (radMemoryAllocator allocator, void* address, unsigned int size)
+{
+ if (CommandLineOptions::Get( CLO_NO_HEAPS ))
+ {
+ return;
+ }
+
+ // If you assert here, you are trying to allocate on a heap that does not allow it
+ //
+ rTuneAssert (m_AllocsEnabled[allocator]);
+
+/*
+ if (HeapManager::s_pIRadMemoryHeapTemp)
+ {
+ unsigned int size;
+ size = HeapManager::s_pIRadMemoryHeapTemp->GetSize();
+ unsigned int totalFree, largestBlock, numObjects, highWater;
+ HeapManager::s_pIRadMemoryHeapTemp->GetStatus( &totalFree, &largestBlock, &numObjects, &highWater );
+ if (totalFree < size)
+ {
+ unsigned int used = size - totalFree;
+ if (used > 200 * 1024)
+ {
+ rTunePrintf ("Temp Used: %d\n", used);
+ ::radMemoryMonitorSuspend ();
+ }
+ }
+ }
+*/
+
+}
+
+
+void HeapActivityTracker::MemoryFreed (radMemoryAllocator allocator, void* address)
+{
+ if (CommandLineOptions::Get( CLO_NO_HEAPS ))
+ {
+ return;
+ }
+
+ // If you assert here, you are trying to free on a heap that does not allow it
+ //
+ rTuneAssert (m_FreesEnabled[allocator]);
+}
+
+HeapActivityTracker g_HeapActivityTracker;
+
+
+
+IRadThreadLocalStorage* HeapManager::s_Instance = 0;
+
+#ifdef DEBUGINFO_ENABLED
+HeapManager* HeapManager::s_Instances[MAX_INST];
+int HeapManager::s_NumInstances = 0;
+#endif
+
+HeapManager* HeapManager::GetInstance ()
+{
+ g_NoHeapRoute = true;
+
+ // First check to see if the thread local storage object has been created
+ //
+ if (!s_Instance)
+ {
+ ::radThreadCreateLocalStorage (&s_Instance, GMA_PERSISTENT);
+ s_Instance->SetValue (0); // Is there a better way to initialize these values? Perhaps radThread should do this.
+ }
+
+ // Now s_Instance is non-null.
+ // Check to see if we have a HeapManager created for this thread yet.
+ // If not, create it.
+ //
+ void* p = s_Instance->GetValue ();
+ if (!p)
+ {
+ GameMemoryAllocator currentHeap = static_cast<GameMemoryAllocator>(::radMemoryGetCurrentAllocator ());
+ p = static_cast<void*>(new(GMA_PERSISTENT) HeapManager (currentHeap)); // Create a heap manager. Init the heap stack with the current heap.
+ s_Instance->SetValue (p);
+ ::radMemorySetAllocatorCallback (static_cast<IRadMemorySetAllocatorCallback*>(p));
+
+#ifdef DEBUGINFO_ENABLED
+ s_Instances[s_NumInstances] = static_cast<HeapManager*>(p);
+ s_NumInstances++;
+#endif
+ }
+
+ g_NoHeapRoute = false;
+ g_HeapManagerCreated = true;
+ return static_cast<HeapManager*>(p);
+}
+
+bool HeapManager::IsCreated()
+{
+ if( !g_HeapManagerCreated )
+ {
+ return false;
+ }
+ else
+ {
+ void* p = s_Instance->GetValue();
+ if( p == NULL )
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+ }
+}
+
+void HeapManager::DestroyInstance()
+{
+ if( s_Instance != NULL )
+ {
+ void* p = s_Instance->GetValue();
+ if( p != NULL )
+ {
+ ::radMemorySetAllocatorCallback( NULL );
+
+ HeapManager* hm = static_cast<HeapManager*>( p );
+ delete( GMA_PERSISTENT, hm );
+
+ s_Instance->SetValue( NULL );
+ }
+
+ s_Instance->Release();
+ }
+}
+
+
+/*
+void HeapManager::DumpHeapStacks ()
+{
+ for (int i = 0; i < s_NumInstances; i++)
+ {
+ HeapManager* pHeapMgr = s_Instances[i];
+ char s[64];
+ if (pHeapMgr == HeapMgr())
+ {
+ strcpy (s, "Main Thread Heap Stack");
+ }
+ else
+ {
+ sprintf (s, "Heap Stack %p", pHeapMgr);
+ }
+ DEBUGINFO_PUSH_SECTION (s);
+
+ pHeapMgr->m_HeapStack.Dump ();
+
+ DEBUGINFO_POP_SECTION ();
+ }
+}
+
+
+void HeapStack::Dump ()
+{
+ for (int i = m_CurPos-1; i >= 0; i--)
+ {
+ char s[64];
+ sprintf (s, "%2d: %s", i, HeapNames[m_Stack[i]]);
+ DEBUGINFO_ADDSCREENTEXT (s);
+ }
+}
+*/
+
+#ifdef RAD_GAMECUBE
+IRadMemoryHeap* s_pIRadMemoryHeapVMM = 0;
+#endif
+
+HeapManager::HeapManager (GameMemoryAllocator defaultAllocator) :
+ m_HeapStack (defaultAllocator)
+#ifndef FINAL
+ ,mLowestFPS(2000), mHighestTris(0), mFPSLWTris(0), mHWTriFPS(0)
+#endif
+{
+
+}
+
+
+HeapManager::~HeapManager ()
+{
+ // Shut down all the heaps.
+ for( int i = 0; i < NUM_GAME_MEMORY_ALLOCATORS; i++ )
+ {
+ DestroyHeapA( static_cast<GameMemoryAllocator>( i ) );
+ }
+}
+
+
+void HeapManager::DestroyHeap (IRadMemoryHeap*& heap, bool justTest)
+{
+ if (heap)
+ {
+ // Check for heap not empty.
+ // If it's not empty this is a Bad Thing. We will be leaving dangling pointers to all the contained memory.
+ // This either means we have a leak and they will never get deleted or they will get deleted eventually but
+ // the heap will no longer exist.
+ //
+ // If you assert here, DO NOT IGNORE IT. If you need help diagnosing the cause of the assertion, see Joel.
+ //
+ unsigned int numAllocs;
+ heap->GetStatus (0, 0, &numAllocs, 0);
+ if (numAllocs > 0)
+ {
+#ifndef FINAL
+#ifndef RAD_RELEASE
+ char s[64];
+ sprintf (s, "%s is being destroyed but is not empty!", HeapNames[GetHeapID (heap)]);
+ if ( CommandLineOptions::Get( CLO_MEMORY_MONITOR ) )
+ {
+ rTunePrintf( s );
+ ::radMemoryMonitorSuspend ();
+ }
+ else
+ {
+// rAssertMsg (0, s);
+ }
+#endif
+#endif // RAD_RELEASE
+ }
+
+ if ( !justTest )
+ {
+ // Destroy the heap
+ //
+ ::radMemoryUnregisterAllocator (GetHeapID (heap));
+ heap->Release ();
+ heap = 0;
+ }
+ }
+}
+
+
+GameMemoryAllocator HeapManager::GetHeapID (const IRadMemoryHeap* heap)
+{
+/*
+ if (s_pIRadMemoryHeapDefault && (heap == s_pIRadMemoryHeapDefault))
+ {
+ return GMA_DEFAULT;
+ }
+ if (s_pIRadMemoryHeapLevelMovie && (heap == s_pIRadMemoryHeapLevelMovie))
+ {
+ return GMA_LEVEL_MOVIE;
+ }
+ if (s_pIRadMemoryHeapDebug && (heap == s_pIRadMemoryHeapDebug))
+ {
+ return GMA_DEBUG;
+ }
+#ifdef RAD_GAMECUBE
+ if (s_pIRadMemoryHeapVMM && (heap == s_pIRadMemoryHeapVMM))
+ {
+#ifdef NO_ARAM
+ return GMA_LEVEL_OTHER;
+#else
+ return GMA_GC_VMM;
+#endif
+ }
+#endif
+
+ rAssert (false);
+ */
+ return GMA_DEFAULT;
+}
+
+
+float HeapManager::GetFudgeFactor ()
+{
+ float FUDGE;
+
+#ifndef RAD_RELEASE
+ if( CommandLineOptions::Get( CLO_FIREWIRE ) )
+ {
+ FUDGE = 1.0f;
+ }
+ else
+ {
+ #if defined( RAD_GAMECUBE ) && defined( RAD_TUNE )
+ FUDGE = 1.0f;
+ #else
+ #ifdef RAD_MW
+ FUDGE = 1.5f;
+ #else
+ FUDGE = 2.0f;
+ #endif
+ #endif
+ }
+#else
+ if( CommandLineOptions::Get( CLO_LARGEHEAPS ) )
+ {
+ FUDGE = 1.5f;
+ }
+ else
+ {
+ FUDGE = 1.0f;
+ }
+#endif
+
+ return FUDGE;
+}
+
+
+void HeapManager::PushHeap (GameMemoryAllocator alloc)
+{
+#ifndef RAD_RELEASE
+ static bool init = false;
+ if (!init)
+ {
+ init = true;
+ //::radMemorySetActivityCallback (&g_HeapActivityTracker);
+ }
+#endif
+
+ m_HeapStack.Push (alloc);
+}
+
+void HeapManager::PopHeap( GameMemoryAllocator alloc )
+{
+ m_HeapStack.Pop( alloc );
+}
+
+GameMemoryAllocator HeapManager::GetCurrentHeap () const
+{
+#ifndef RAD_RELEASE
+ if (s_bSpecialRoute)
+ {
+ return GMA_SPECIAL;
+ }
+#endif
+
+ return m_HeapStack.Top ();
+}
+
+
+#ifdef MEMORYTRACKER_ENABLED
+
+void HeapManager::PushGroup( const char* name )
+{
+ rAssert( strcmp( name, "ScroobyBootup" ) != 0 );
+ HeapMgr()->PushHeap (GMA_DEBUG);
+
+ MemoryIDString str;
+ strncpy (str.id, name, MAXSTRING);
+
+ m_GroupIDStack.push (str);
+
+ HeapMgr()->PopHeap ( GMA_DEBUG );
+}
+
+
+void HeapManager::PopGroup ( const char* name )
+{
+ HeapMgr()->PushHeap( GMA_DEBUG );
+
+ rAssert( !m_GroupIDStack.empty () );
+ MemoryIDString str = m_GroupIDStack.top();
+ int equal = strncmp( name, str.id, MAXSTRING );
+ rAssert( equal == 0 );
+ m_GroupIDStack.pop();
+
+ HeapMgr()->PopHeap( GMA_DEBUG );
+}
+
+
+const char* HeapManager::GetCurrentGroupID () const
+{
+ if (m_GroupIDStack.empty ())
+ {
+ return 0;
+ }
+ else
+ {
+ return m_GroupIDStack.top ().id;
+ }
+}
+
+
+void HeapManager::PushFlag (const char* name)
+{
+ HeapMgr()->PushHeap (GMA_DEBUG);
+
+ MemoryIDString str;
+ strncpy (str.id, name, MAXSTRING);
+
+ m_FlagStack.push (str);
+
+ ::radMemoryMonitorIssueFlag (name);
+
+ HeapMgr()->PopHeap (GMA_DEBUG);
+}
+
+
+void HeapManager::PopFlag (const char* name)
+{
+ HeapMgr()->PushHeap (GMA_DEBUG);
+
+ assert (!m_FlagStack.empty ());
+ MemoryIDString str = m_GroupIDStack.top();
+ int equal = strncmp( name, str.id, MAXSTRING );
+ //rAssert( equal == 0 );
+ m_FlagStack.pop ();
+
+ if (m_FlagStack.empty ())
+ {
+ ::radMemoryMonitorIssueFlag ("");
+ }
+ else
+ {
+ ::radMemoryMonitorIssueFlag (m_FlagStack.top ().id);
+ }
+
+ HeapMgr()->PopHeap (GMA_DEBUG);
+}
+
+
+#endif
+
+
+radMemoryAllocator HeapManager::GetCurrentAllocator ()
+{
+ return GetCurrentHeap ();
+}
+
+
+radMemoryAllocator HeapManager::SetCurrentAllocator ( radMemoryAllocator allocator )
+{
+ radMemoryAllocator old = GetCurrentHeap ();
+ m_HeapStack.SetTop ((GameMemoryAllocator)allocator);
+ return old;
+}
+
+
+void HeapManager::DumpHeapStats ( bool text )
+{
+//#ifndef FINAL
+//#ifndef RAD_GAMECUBE
+ struct HeapInfo
+ {
+ GameMemoryAllocator gma;
+ const char* name;
+ IRadMemoryHeap** heap;
+ unsigned int highwater;
+ };
+
+#ifdef RAD_GAMECUBE
+ if ( vmmHeap && !s_pIRadMemoryHeapVMM )
+ {
+ //The virtual memory heap.
+ rReleaseString ("Creating Heap: VMM\n");
+ s_pIRadMemoryHeapVMM = vmmHeap;
+ s_pIRadMemoryHeapVMM->AddRef();
+ }
+#endif
+
+/*
+
+ HeapInfo info[] =
+ {
+ { GMA_DEFAULT, "Default", &s_pIRadMemoryHeapDefault, 0 },
+ { GMA_TEMP, "Temp", &s_pIRadMemoryHeapTemp, 0 },
+ { GMA_PERSISTENT, "Persist", &s_pIRadMemoryHeapPersistent, 0 },
+ { GMA_MUSIC, "Music", &s_pIRadMemoryHeapMusic, 0 },
+ { GMA_AUDIO_PERSISTENT, "Audio Persistent", &s_pIRadMemoryHeapAudioPersistent, 0 },
+ { GMA_LEVEL, "Level", &s_pIRadMemoryHeapLevel, 0 },
+ { GMA_LEVEL_MOVIE, "- Movie", &s_pIRadMemoryHeapLevelMovie, 0 },
+ { GMA_LEVEL_FE, "- FE", &s_pIRadMemoryHeapLevelFE, 0 },
+
+ { GMA_LEVEL_ZONE, "- Zone", &s_pIRadMemoryHeapLevelZone, 0 },
+// { GMA_LEVEL_OTHER, "- Other", &s_pIRadMemoryHeapLevelOther, 0 },
+ { GMA_LEVEL_HUD, "- HUD", &s_pIRadMemoryHeapLevelHUD, 0 },
+// { GMA_LEVEL_MISSION, "- Mission", &s_pIRadMemoryHeapLevelMission, 0 },
+
+ { GMA_LEVEL_AUDIO, "- Audio", &s_pIRadMemoryHeapLevelAudio, 0 },
+
+ { GMA_GC_VMM, "VMM", &s_pIRadMemoryHeapVMM, 0 }
+ };
+ */
+
+ HeapInfo info[] =
+ {
+ { GMA_DEFAULT, "Default", GetHeapReference( GMA_DEFAULT ), 0 },
+ { GMA_TEMP, "Temp", GetHeapReference( GMA_TEMP ), 0 },
+ { GMA_PERSISTENT, "Persist", GetHeapReference( GMA_PERSISTENT ), 0 },
+ { GMA_MUSIC, "Music", GetHeapReference( GMA_MUSIC ), 0 },
+ { GMA_AUDIO_PERSISTENT, "Audio Persistent", GetHeapReference( GMA_AUDIO_PERSISTENT ), 0 },
+ { GMA_LEVEL_ZONE, "Zones", GetHeapReference( GMA_LEVEL_ZONE ), 0 },
+ { GMA_SMALL_ALLOC, "Small Alloc", GetHeapReference( GMA_SMALL_ALLOC ), 0 },
+#ifdef USE_CHAR_GAG_HEAP
+ { GMA_CHARS_AND_GAGS, "Chars and Gags", GetHeapReference( GMA_CHARS_AND_GAGS ), 0 },
+#endif
+// { GMA_LEVEL_MISSION, "Mission", &s_pIRadMemoryHeapLevelMission, 0 },
+ { GMA_LEVEL_AUDIO, "Audio", GetHeapReference( GMA_LEVEL_AUDIO ),0 },
+ { GMA_LEVEL_FE, "F/E", GetHeapReference( GMA_LEVEL_FE ), 0 },
+ { GMA_LEVEL_HUD, "HUD", GetHeapReference( GMA_LEVEL_HUD ), 0 }
+// { GMA_LEVEL_OTHER, "Other", &s_pIRadMemoryHeapLevelOther, 0 }
+#ifdef RAD_GAMECUBE
+ ,{ GMA_GC_VMM, "VMM", &s_pIRadMemoryHeapVMM, 0 }
+#endif
+ };
+
+ if ( text )
+ {
+ size_t used = Memory::GetTotalMemoryUsed();
+ size_t available = Memory::GetTotalMemoryFree();
+ size_t unavailable = Memory::GetTotalMemoryUnavailable();
+ size_t lowWater = Memory::GetTotalMemoryFreeLowWaterMark();
+ size_t freeInAllHeaps = GetTotalMemoryFreeInAllHeaps();
+ //size_t allocatable = Memory::GetFreeMemoryProfile();
+ size_t allocatable = 0;
+ size_t largestBlock = Memory::GetLargestFreeBlock();
+ char buffer[ 256 ];
+ sprintf( buffer, "Used: %d\n", used );
+ rReleaseString( buffer );
+ sprintf( buffer, "Free: %d Free in Malloc: %d\n", freeInAllHeaps, available );
+ rReleaseString( buffer );
+ sprintf( buffer, "Unavailable: %d\n", unavailable );
+ rReleaseString( buffer );
+ sprintf( buffer, "LargestFragment: %d\n", largestBlock );
+ rReleaseString( buffer );
+ sprintf( buffer, "Allocatable: %d\n", allocatable );
+ rReleaseString( buffer );
+ sprintf( buffer, "LowWater: %d\n", lowWater );
+ rReleaseString( buffer );
+
+
+ int i;
+ int line = 0;
+ for( i = 0; i < static_cast<int>(sizeof(info) / sizeof(info[0])); ++i )
+ {
+
+ IRadMemoryHeap* heap = *(info[i].heap);
+ if(i==4)
+ {
+ // rReleasePrintf("\nheap %s size: %d", info[i].name, heap->GetSize() );
+ }
+ if (heap)
+ {
+ unsigned int size;
+ size = heap->GetSize();
+
+ unsigned int totalFree, largestBlock, numObjects, highWater;
+ heap->GetStatus( &totalFree, &largestBlock, &numObjects, &highWater );
+ unsigned int used = size - totalFree;
+
+ float percentUsed = static_cast<float>( used ) / static_cast<float>( size ) * 100.0f;
+ float highWaterPercentUsed = static_cast<float>( highWater ) / static_cast<float>( size ) * 100.0f;
+ float usedMB = (float)used / MB;
+ float sizeMB = (float)size / MB;
+ float hwMB = (float)highWater / MB;
+
+ char buffy[256];
+ sprintf( buffy, "%8s %d/%d [%2.1f%%] HW: %2.2f [%2.1f%%] %d\n", info[i].name, used, size, percentUsed, hwMB, highWaterPercentUsed, numObjects );
+ rReleaseString( buffy );
+ ++line;
+ }
+ }
+ }
+
+ const pddiColour BLACK( 0, 0, 0 );
+ const pddiColour ORANGE(255,127,0);
+ const pddiColour WHITE(255,255,255);
+ const pddiColour WHITE_50(255,255,255,128);
+ const int LEFT = 30;
+ const int TOP = 200;
+
+ if( CommandLineOptions::Get( CLO_HEAP_STATS ) )
+ {
+ //
+ // What does Malloc have left
+ //
+
+ static pddiShader* shader = p3d::device->NewShader( "simple" );
+ shader->SetInt(PDDI_SP_SHADEMODE, PDDI_SHADE_FLAT);
+ shader->SetInt(PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA);
+
+ pddiProjectionMode proj = p3d::pddi->GetProjectionMode();
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_DEVICE);
+ p3d::pddi->PushIdentityMatrix(PDDI_MATRIX_MODELVIEW);
+ pddiPrimStream* stream;
+ stream = p3d::pddi->BeginPrims(shader, PDDI_PRIM_TRISTRIP, PDDI_V_C, 4 );
+ float z = 1.001f;
+ float xMin = 0.0f;
+ float xMax = 500.0f;
+ float yMin = 0.0f + TOP;
+ float yMax = 250.0f + TOP;
+ stream->Colour(WHITE_50);
+ stream->Coord( 0.0f, yMin, z );
+ stream->Colour(WHITE_50);
+ stream->Coord( xMax, yMin, z );
+ stream->Colour(WHITE_50);
+ stream->Coord( 0.0f, yMax, z );
+ stream->Colour(WHITE_50);
+ stream->Coord( xMax, yMax, z );
+ p3d::pddi->EndPrims(stream);
+ p3d::pddi->PopMatrix(PDDI_MATRIX_MODELVIEW);
+ p3d::pddi->SetProjectionMode(proj);
+
+ size_t used = Memory::GetTotalMemoryUsed();
+ size_t available = Memory::GetTotalMemoryFree();
+ size_t unavailable = Memory::GetTotalMemoryUnavailable();
+ size_t lowWater = Memory::GetTotalMemoryFreeLowWaterMark();
+ size_t freeInAllHeaps = GetTotalMemoryFreeInAllHeaps();
+ //size_t allocatable = Memory::GetFreeMemoryProfile();
+ size_t allocatable = 0;
+ size_t largestBlock = Memory::GetLargestFreeBlock();
+ char buffer[ 256 ];
+ int printLine = TOP - 10;
+ sprintf( buffer, "Used: %d", used );
+ p3d::pddi->DrawString( buffer, LEFT, printLine += 15 , BLACK );
+ sprintf( buffer, "Free: %d Free in Malloc: %d", freeInAllHeaps, available );
+ p3d::pddi->DrawString( buffer, LEFT, printLine += 15 , BLACK );
+ sprintf( buffer, "Unavailable: %d", unavailable );
+ p3d::pddi->DrawString( buffer, LEFT, printLine += 15 , BLACK );
+ sprintf( buffer, "LargestFragment: %d", largestBlock );
+ p3d::pddi->DrawString( buffer, LEFT, printLine += 15 , BLACK );
+ sprintf( buffer, "Allocatable: %d", allocatable );
+ p3d::pddi->DrawString( buffer, LEFT, printLine += 15 , BLACK );
+ sprintf( buffer, "LowWater: %d", lowWater );
+ p3d::pddi->DrawString( buffer, LEFT, printLine += 15 , BLACK );
+
+
+ int i;
+ int line = 0;
+ for( i = 0; i < static_cast<int>(sizeof(info) / sizeof(info[0])); ++i )
+ {
+
+ IRadMemoryHeap* heap = *(info[i].heap);
+ if (heap)
+ {
+ unsigned int size;
+ size = heap->GetSize();
+
+ unsigned int totalFree, largestBlock, numObjects, highWater;
+ heap->GetStatus( &totalFree, &largestBlock, &numObjects, &highWater );
+ unsigned int used = size - totalFree;
+
+ float percentUsed = static_cast<float>( used ) / static_cast<float>( size ) * 100.0f;
+ float highWaterPercentUsed = static_cast<float>( highWater ) / static_cast<float>( size ) * 100.0f;
+ float usedMB = (float)used / MB;
+ float sizeMB = (float)size / MB;
+ float hwMB = (float)highWater / MB;
+
+ char buffy[256];
+ sprintf( buffy, "%8s %d/%d [%2.1f%%] HW: %2.2f [%2.1f%%] %d", info[i].name, used, size, percentUsed, hwMB, highWaterPercentUsed, numObjects );
+ p3d::pddi->DrawString( buffy,
+ LEFT ,
+ 100 + TOP + (line*20),
+ BLACK );
+ ++line;
+ }
+ }
+ }
+
+//#endif // !RAD_GAMECUBE
+
+//#endif // !FINAL
+}
+
+void HeapManager::ResetArtStats()
+{
+#ifndef FINAL
+ mLowestFPS = 2000;
+ mHighestTris = 0;
+ mFPSLWTris = 0;
+ mHWTriFPS = 0;
+#endif
+}
+
+void HeapManager::DumpArtStats ()
+{
+#ifndef FINAL
+
+ struct HeapInfo
+ {
+ GameMemoryAllocator gma;
+ const char* name;
+ IRadMemoryHeap** heap;
+ unsigned int highwater;
+ };
+
+ static HeapInfo info[] =
+ {
+ { GMA_LEVEL_ZONE, "Zones", GetHeapReference( GMA_LEVEL_ZONE ), 0 },
+// { GMA_LEVEL_MISSION, "Mission", &s_pIRadMemoryHeapLevelMission, 0 },
+// { GMA_LEVEL_OTHER, "Other", &s_pIRadMemoryHeapLevelOther, 0 }
+ };
+
+ if( CommandLineOptions::Get( CLO_ART_STATS ) )
+ {
+ const int LEFT = 35;
+ const int TOP = 285;
+ const pddiColour BLACK(0,0,0);
+ const pddiColour WHITE(128,128,128);
+
+ int i;
+ int count = static_cast<int>(sizeof(info) / sizeof(info[0]));
+ for( i = 0; i < count; ++i )
+ {
+ IRadMemoryHeap* heap = *(info[i].heap);
+ if (heap)
+ {
+ unsigned int size;
+ size = heap->GetSize();
+
+ unsigned int totalFree, largestBlock, numObjects, highWater;
+ heap->GetStatus( &totalFree, &largestBlock, &numObjects, &highWater );
+
+ unsigned int used = size - totalFree;
+ float percentUsed = static_cast<float>( used ) / static_cast<float>( size ) * 100.0f;
+ float highWaterPercentUsed = static_cast<float>( highWater ) / static_cast<float>( size ) * 100.0f;
+ float usedMB = (float)used / MB;
+ float sizeMB = (float)size / MB;
+ float hwMB = (float)info[i].highwater / MB;
+
+ char buffy[256];
+ sprintf( buffy, "%8s %2.2f/%2.2f [%2.1f%%] HW: %2.2f [%2.1f%%]", info[i].name, usedMB, sizeMB, percentUsed, hwMB, highWaterPercentUsed );
+
+ p3d::pddi->DrawString( buffy,
+ LEFT ,
+ TOP + (i*20),
+ WHITE );
+ }
+ }
+
+ char buff1[256];
+ char buff2[256];
+ char buff3[256];
+ char buff4[256];
+ float memtextures = 0;
+ float memmeshes = 0;
+ int fps=0,tris;
+
+ int frameTime = p3d::pddi->GetIntStat(PDDI_STAT_FRAME_TIME);
+ if( frameTime == 0 )
+ {
+ frameTime = 1;
+ }
+ fps = 1000 / ( frameTime );
+#ifdef RAD_RELEASE
+ tris = 300000;
+#else
+ tris = p3d::pddi->GetIntStat(PDDI_STAT_BUFFERED_PRIM) + p3d::pddi->GetIntStat(PDDI_STAT_BUFFERED_INDEXED_PRIM);
+#endif
+
+ if(tris>20000 && fps>12)
+ {
+ if(fps<mLowestFPS)
+ {
+ mLowestFPS = fps;
+ mFPSLWTris = tris;
+ }
+ }
+ if(tris>mHighestTris)
+ {
+ mHighestTris = tris;
+ mHWTriFPS = fps;
+ }
+
+ memtextures = p3d::pddi->GetFloatStat(PDDI_STAT_TEXTURE_ALLOC_8BIT);
+ memtextures = memtextures /1024;
+ memmeshes = p3d::pddi->GetFloatStat(PDDI_STAT_BUFFERED_ALLOC);
+ memmeshes = memmeshes/1024 ;
+
+ sprintf(buff1,"FPS: %d \n", fps);
+ sprintf(buff2,"# Textures 8bit: %d ,MemUsage %2.2f MB \n",p3d::pddi->GetIntStat(PDDI_STAT_TEXTURE_COUNT_8BIT),memtextures );
+ sprintf(buff3,"# Meshes: %d , MemUsage %2.2f MB \n",p3d::pddi->GetIntStat(PDDI_STAT_BUFFERED_COUNT),memmeshes);
+ sprintf(buff4,"Tri's: %d Tri(HW): %d, FPS at TriHW: %d\n FPS(LW):%d, Tri's at FPS LW: %d\n",tris,mHighestTris,mHWTriFPS,
+ mLowestFPS,mFPSLWTris);
+
+ p3d::pddi->DrawString(buff1,LEFT,TOP+(count*20)+ 0,WHITE);
+ p3d::pddi->DrawString(buff2,LEFT,TOP+(count*20)+20,WHITE);
+ p3d::pddi->DrawString(buff3,LEFT,TOP+(count*20)+40,WHITE);
+ p3d::pddi->DrawString(buff4,LEFT,TOP+(count*20)+60,WHITE);
+ p3d::pddi->EnableStatsOverlay(0); //disable the regular p3d debug stats.
+ }
+#endif
+}
+
+
+//Memory Layout.
+//--HS_DEFAULT
+//--HS_TEMP
+//--HS_MUSIC
+//--HS_AUDIO_PERSISTENT
+//--HS_PERSISTENT
+//--HS_LEVEL
+// ||
+// | --HS_LEVEL_MOVIE -|
+// | --HS_LEVEL_AUDIO_FE | - Front end sub heaps.
+// | --HS_LEVEL_FE -|
+// |
+// --HS_LEVEL_ZONE -|
+// --HS_LEVEL_OTHER |
+// --HS_LEVEL_HUD | - In-game sub heaps.
+// --HS_LEVEL_MISSION |
+// --HS_LEVEL_AUDIO_INGAME -|
+
+//==================================
+//These only exist in debug and tune
+
+//--HS_DEBUG
+//--HS_DEBUG_FIREWIRE
+//--HS_SPECIAL
+
+// These constants are the heap sizes for each platform
+//
+#if defined (RAD_PS2)
+ #ifdef RAD_RELEASE
+ const float HS_DEFAULT = 0.12f; // For only very core FTech stuff (DO NOT change this)
+ #else // If you run out of room in GMA_DEFAULT,
+ // you have an unrouted allocation
+ const float HS_DEFAULT = 0.13f; // For that plus debug comm stuff, etc (DO NOT change this)
+ #endif
+
+ #ifdef CORRAL_SMALL_ALLOCS
+ const float HS_TEMP = 0.350000f;
+ #else
+ const float HS_TEMP = 0.550000f;
+ #endif
+
+ #ifdef PAL
+ // an additional 4-5K is needed for PAL builds, due to other languages
+ // in the text bible using up slightly more memory
+ //
+ const float HS_PERSISTENT = 1.905f;
+ #else
+ const float HS_PERSISTENT = 1.900f;
+ #endif
+
+ const float HS_MUSIC = 0.2f;
+ const float HS_AUDIO_PERSISTENT = 0.62f;//0.693000f;
+
+ //FE Only
+ const float HS_LEVEL_MOVIE = 1.94f;
+ const float HS_LEVEL_AUDIO_FE = 0.05f;
+ const float HS_LEVEL_FE = 14.0f;
+ //In-game Only
+ //const float HS_LEVEL_ZONE = 11.25f;
+ const float HS_LEVEL_ZONE = 11.50f;
+ //const float HS_LEVEL_OTHER = 6.48f;
+ #ifdef CORRAL_SMALL_ALLOCS
+ const float HS_LEVEL_HUD = 1.6700f;
+ #else
+ const float HS_LEVEL_HUD = 2.51f;;
+ #endif
+
+ const float HS_LEVEL_AUDIO_INGAME = 0.05f;
+
+ // level heap is the sum of other heaps
+ const float HS_LEVEL = 0.01 + rmt::Max( ( HS_LEVEL_ZONE + HS_LEVEL_HUD + HS_LEVEL_AUDIO_INGAME ), ( HS_LEVEL_FE + HS_LEVEL_AUDIO_FE + HS_LEVEL_MOVIE ) );
+
+ //Mnigame Only
+ const float HS_MINIGAME_ZONE = 3.0f;
+ const float HS_MINIGAME_OTHER = 7.7f;
+ const float HS_MINIGAME_HUD = 2.3f;
+ const float HS_MINIGAME_MISSION = 2.0f;
+ const float HS_MINIGAME_AUDIO = 0.5f;
+
+ #ifndef RAD_RELEASE
+ const float HS_DEBUG = 5.0f;
+ const float HS_DEBUG_FIREWIRE = 0.4f;
+ const float HS_SPECIAL = 10.0f;
+ #endif
+
+#elif defined (RAD_GAMECUBE)
+ //NOTE: The VMM is using 1 MEG - See VMM_MAIN_RAM in gcplatform.h
+ //NOTE2: GameCube libraries now allocate from GMA_AUDIO_PERSISTENT rather than OSAlloc,
+ //NOTE3: There is also a static heap defined in radCore/radmemory memorymanager.cpp of
+ // STATIC_HEAP_SIZE ( 1024 * 1024 * 2 ) + ( 600 * 1024 ) = 2.6 Megs This is replacing the
+ // regular persistent heap.
+ //NOTE4: There is also an amount of emergency memory in radmemory (memorymanager) in the initializePlatform
+ // for printfs and such. It is 32 * 1024 bytes = 32K
+ const float HS_DEFAULT = 0.08f; //0.13f // For only very core FTech stuff
+ const float HS_TEMP = 0.3f; //0.37f;
+
+ const float HS_PERSISTENT = 2.56f; //Need extra 8K for PAL
+ const float HS_MUSIC = 0.2f;
+ const float HS_AUDIO_PERSISTENT = 0.65f;
+ const float HS_LEVEL = 12.95f; // a little extra sub-heap creation overhead
+ //FE Only
+ const float HS_LEVEL_MOVIE = 3.1f;
+ const float HS_LEVEL_AUDIO_FE = 0.1f;
+ const float HS_LEVEL_FE = 9.75f;
+ //In-game Only
+ const float HS_LEVEL_ZONE = 6.48f;
+ const float HS_LEVEL_OTHER = 3.20f;
+ const float HS_LEVEL_HUD = 2.50f;
+ //const float HS_LEVEL_MISSION = 0.0f;
+ const float HS_LEVEL_AUDIO_INGAME = 0.04f;
+ //Mnigame Only
+ const float HS_MINIGAME_ZONE = 3.0f;
+ const float HS_MINIGAME_OTHER = 7.5f;
+ const float HS_MINIGAME_HUD = 1.7f;
+ const float HS_MINIGAME_MISSION = 2.0f;
+ const float HS_MINIGAME_AUDIO = 0.5f;
+
+ #ifndef RAD_RELEASE
+ const float HS_DEBUG = 2.0f;
+ const float HS_DEBUG_FIREWIRE = 99999.99f;
+ const float HS_SPECIAL = 0.0f;
+ #endif
+
+#elif defined (RAD_XBOX)
+ #ifdef RAD_RELEASE
+ const float HS_DEFAULT = 0.1f; // For only very core FTech stuff
+ #else
+ const float HS_DEFAULT = 0.4f; // For that plus debug comm stuff, etc
+ #endif
+ const float HS_TEMP = 1.0f;
+ const float HS_PERSISTENT = 1.65f;
+ const float HS_MUSIC = 0.2f;
+ const float HS_AUDIO_PERSISTENT = 0.7f;
+ const float HS_LEVEL = 20.01f; // 0.01 for sub-heap creation overhead
+ //FE Only
+ const float HS_LEVEL_MOVIE = 3.94f; // TC: added extra 2.0 MB until memory leak
+ // in radMovie Bink player is fixed
+ const float HS_LEVEL_AUDIO_FE = 0.05f;
+ const float HS_LEVEL_FE = 5.0f;
+ //In-game Only
+ const float HS_LEVEL_ZONE = 8.0f;
+ //const float HS_LEVEL_OTHER = 5.0f;
+ const float HS_LEVEL_HUD = 2.5f;
+ //const float HS_LEVEL_MISSION = 2.8f;
+ const float HS_LEVEL_AUDIO_INGAME = 0.05f;
+ //Mnigame Only
+ const float HS_MINIGAME_ZONE = 3.0f;
+ const float HS_MINIGAME_OTHER = 8.0f;
+ const float HS_MINIGAME_HUD = 1.55f;
+ const float HS_MINIGAME_MISSION = 2.0f;
+ const float HS_MINIGAME_AUDIO = 0.5f;
+
+ #ifndef RAD_RELEASE
+ const float HS_DEBUG = 5.0f;
+ const float HS_DEBUG_FIREWIRE = 0.4f;
+ const float HS_SPECIAL = 10.0f;
+ #endif
+
+#elif defined (RAD_WIN32) // these have not been optimized yet.
+ #ifdef RAD_RELEASE
+ const float HS_DEFAULT = 0.1f; // For only very core FTech stuff
+ #else
+ const float HS_DEFAULT = 0.4f; // For that plus debug comm stuff, etc
+ #endif
+ const float HS_TEMP = 1.0f;
+ const float HS_PERSISTENT = 1.65f;
+ const float HS_MUSIC = 0.2f;
+ const float HS_AUDIO_PERSISTENT = 2.7f;
+ const float HS_LEVEL = 20.01f; // 0.01 for sub-heap creation overhead
+ //FE Only
+ const float HS_LEVEL_MOVIE = 3.94f; // TC: added extra 2.0 MB until memory leak
+ // in radMovie Bink player is fixed
+ const float HS_LEVEL_AUDIO_FE = 0.05f;
+ const float HS_LEVEL_FE = 5.0f;
+ //In-game Only
+ const float HS_LEVEL_ZONE = 8.0f;
+ //const float HS_LEVEL_OTHER = 5.0f;
+ const float HS_LEVEL_HUD = 2.5f;
+ //const float HS_LEVEL_MISSION = 2.8f;
+ const float HS_LEVEL_AUDIO_INGAME = 0.05f;
+ //Mnigame Only
+ const float HS_MINIGAME_ZONE = 3.0f;
+ const float HS_MINIGAME_OTHER = 8.0f;
+ const float HS_MINIGAME_HUD = 1.55f;
+ const float HS_MINIGAME_MISSION = 2.0f;
+ const float HS_MINIGAME_AUDIO = 0.5f;
+
+ #ifndef RAD_RELEASE
+ const float HS_DEBUG = 5.0f;
+ const float HS_DEBUG_FIREWIRE = 0.4f;
+ const float HS_SPECIAL = 10.0f;
+ #endif
+
+#endif
+
+
+#define ALL_DL_HEAPS
+
+void HeapManager::PrepareHeapsStartup ()
+{
+ //
+ // Setup our memory heaps.
+ //
+ const float FUDGE = GetFudgeFactor ();
+
+ // The temporary heap.
+ // This heap is for temporary allocations that happen during loading, to prevent fragmentation.
+ //
+ CreateHeap( GMA_TEMP, static_cast<unsigned int>(HS_TEMP * MB * FUDGE) );
+
+ // The persistent heap.
+ // This heap holds everything that is allocated at the beginning of the game and never gets freed.
+ //
+ float persistent_size = HS_PERSISTENT;
+#ifdef RAD_PS2
+#ifndef RAD_RELEASE
+ // On the PS2, in non-release, radSound allocates about 0.1 MB more for radRemoteScript
+ //
+ persistent_size += 0.1f;
+#endif
+
+ // On the PS2, the host drive mount costs about 254K more than the CD drive mount
+ //
+ if (!CommandLineOptions::Get( CLO_CD_FILES_ONLY ))
+ {
+ persistent_size += 0.25f;
+ }
+#endif
+
+ CreateHeap( GMA_PERSISTENT, static_cast<unsigned int>(persistent_size * MB * FUDGE) );
+
+//#ifndef RAD_GAMECUBE
+ //
+ // The audio persistent heap.
+ // This heap holds allocations that audio makes that are done once
+ // and persist throughout the game. They aren't necessarily done
+ // at start up, hence its own heap
+ //
+#if defined( RAD_GAMECUBE ) || defined( RAD_PS2 )
+ CreateHeap( GMA_AUDIO_PERSISTENT, static_cast< int >( HS_AUDIO_PERSISTENT * FUDGE * MB ) );
+ IRadMemoryAllocator* audioHeap = GetAllocator( GMA_AUDIO_PERSISTENT );
+ radMemoryRegisterAllocator( GMA_MUSIC, RADMEMORY_ALLOC_DEFAULT, audioHeap );
+#endif
+#ifdef CORRAL_SMALL_ALLOCS
+ CreateHeap( GMA_SMALL_ALLOC, (int)(((float)4*(1024*1024))*FUDGE+(200*1024)));//-170608) );
+ extern bool gbSmallAllocCreated;
+ gbSmallAllocCreated = true;
+#endif
+//#else
+// radMemoryRegisterAllocator( GMA_AUDIO_PERSISTENT, RADMEMORY_ALLOC_VMM, vmmHeap );
+// radMemoryRegisterAllocator( GMA_MUSIC, RADMEMORY_ALLOC_VMM, vmmHeap );
+//#endif
+
+#ifdef RAD_GAMECUBE
+ // The default heap.
+ // This heap holds everything we are otherwise unable to route to a heap.
+ // This includes objects initialized before the heaps are created.
+ //
+ unsigned int size = Memory::GetTotalMemoryFree() - 3 * 1024;
+ CreateHeap( GMA_DEFAULT, size );
+ IRadMemoryAllocator* defaultHeap = GetAllocator( GMA_DEFAULT );
+ //radMemoryRegisterAllocator( GMA_TEMP, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_LEVEL_MOVIE, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_LEVEL_FE, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_LEVEL_ZONE, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_LEVEL_OTHER, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+// radMemoryRegisterAllocator( GMA_LEVEL_HUD, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_LEVEL_MISSION, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_LEVEL_AUDIO, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_DEBUG, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_SPECIAL, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+// radMemoryRegisterAllocator( GMA_MUSIC, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+// radMemoryRegisterAllocator( GMA_AUDIO_PERSISTENT, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+#endif
+
+#if defined(RAD_PS2) && defined(ALL_DL_HEAPS)
+ unsigned int fudgeFactor;
+
+ if( CommandLineOptions::Get( CLO_MEMORY_MONITOR ) )
+ {
+ fudgeFactor = 64 * 1024;
+ }
+ else
+ {
+ #ifdef RAD_TUNE
+ fudgeFactor = 64 * 1024;
+ #else
+ fudgeFactor = 16 * 1024;
+ #endif
+ }
+
+ //unsigned int size = Memory::GetTotalMemoryFree() - fudgeFactor;// - 256 * 1024; //mfifo
+ unsigned int size = Memory::GetLargestFreeBlock() - fudgeFactor;// - 256 * 1024; //mfifo
+ #ifdef RAD_MW
+ size -= (512 * 1024);
+ #endif
+ //size -= (512*1024);
+ CreateHeap( GMA_DEFAULT, size );
+ IRadMemoryAllocator* defaultHeap = GetAllocator( GMA_DEFAULT );
+ //radMemoryRegisterAllocator( GMA_TEMP, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_LEVEL_MOVIE, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_LEVEL_FE, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_LEVEL_ZONE, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_LEVEL_OTHER, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ //radMemoryRegisterAllocator( GMA_LEVEL_HUD, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_LEVEL_MISSION, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_LEVEL_AUDIO, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_DEBUG, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+ radMemoryRegisterAllocator( GMA_SPECIAL, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+// radMemoryRegisterAllocator( GMA_MUSIC, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+// radMemoryRegisterAllocator( GMA_AUDIO_PERSISTENT, RADMEMORY_ALLOC_DEFAULT, defaultHeap );
+
+#endif
+
+ if (CommandLineOptions::Get( CLO_NO_HEAPS ))
+ {
+ return;
+ }
+
+ //
+ // The music heap.
+ // This heap holds allocations related to radMusic. It's a bit of a tweener,
+ // since it can allocate/deallocate unpredictably, and can occasionally
+ // persist temporarily over the FE/game crossover. It doesn't seem to fit
+ // anywhere else, hence its own heap.
+ //
+// CreateHeap( GMA_MUSIC, static_cast< unsigned int >( HS_MUSIC * FUDGE ) );
+
+ //
+ // The audio persistent heap.
+ // This heap holds allocations that audio makes that are done once
+ // and persist throughout the game. They aren't necessarily done
+ // at start up, hence its own heap
+ //
+ rReleasePrintf( "Creating Heap: AUDIO PERSISTENT (%f)\n", HS_AUDIO_PERSISTENT * FUDGE );
+ //s_pIRadMemoryHeapAudioPersistent = radMemoryCreateDougLeaHeap( static_cast<unsigned int>( HS_AUDIO_PERSISTENT * MB * FUDGE ), RADMEMORY_ALLOC_DEFAULT, "Audio Persistent" );
+ //s_pIRadMemoryHeapAudioPersistent->AddRef();
+ //radMemoryRegisterAllocator( GMA_AUDIO_PERSISTENT, RADMEMORY_ALLOC_DEFAULT, s_pIRadMemoryHeapAudioPersistent );
+
+ // The level heap.
+ // This is the main heap for holding game data. This includes the front end and all in-game assets.
+ // This is the largest heap.
+ //
+ //rReleasePrintf ("Creating Heap: LEVEL (%f)\n", HS_LEVEL * FUDGE);
+ //s_pIRadMemoryHeapLevel = radMemoryCreateTrackingHeap( static_cast<unsigned int>(HS_LEVEL * MB * FUDGE), RADMEMORY_ALLOC_DEFAULT, "Level" );
+ //s_pIRadMemoryHeapLevel->AddRef();
+ //radMemoryRegisterAllocator( GMA_LEVEL, RADMEMORY_ALLOC_DEFAULT, s_pIRadMemoryHeapLevel );
+
+#ifndef RAD_RELEASE
+ // The debug heap.
+ // This heap is for holding all debugging related materials, such as the host communication channel, the watcher, the profiler, etc.
+ // It is not created in release mode.
+ //
+ float debugSize;
+ if( CommandLineOptions::Get( CLO_FIREWIRE ) )
+ {
+ debugSize = HS_DEBUG_FIREWIRE;
+ }
+ else
+ {
+ debugSize = HS_DEBUG;
+ }
+
+ CreateHeap( GMA_DEBUG, static_cast<unsigned int>(debugSize * MB) );
+
+ if ( !CommandLineOptions::Get( CLO_FIREWIRE ) )
+ {
+ // The special heap.
+ // This heap is created in debug and tune only and is used for routing a group of allocations to a specific place.
+ //
+ CreateHeap( GMA_SPECIAL, static_cast<unsigned int>(HS_SPECIAL * MB) );
+ }
+#endif
+
+ // The default heap.
+ // This heap holds everything we are otherwise unable to route to a heap.
+ // This includes objects initialized before the heaps are created.
+ //
+ //rReleasePrintf ("Creating Heap: DEFAULT (%f)\n", HS_DEFAULT);
+ //s_pIRadMemoryHeapDefault = radMemoryCreateDougLeaHeap( static_cast<unsigned int>(HS_DEFAULT * MB), RADMEMORY_ALLOC_DEFAULT, "Default" );
+ //s_pIRadMemoryHeapDefault->AddRef();
+ //radMemoryRegisterAllocator( GMA_DEFAULT, RADMEMORY_ALLOC_DEFAULT, s_pIRadMemoryHeapDefault );
+
+ /*
+#ifdef RAD_GAMECUBE
+ if ( vmmHeap )
+ {
+ //The virtual memory heap.
+ rReleaseString ("Creating Heap: VMM\n");
+ s_pIRadMemoryHeapVMM = vmmHeap;
+ s_pIRadMemoryHeapVMM->AddRef();
+ }
+
+ // The default heap.
+ // This heap holds everything we are otherwise unable to route to a heap.
+ // This includes objects initialized before the heaps are created.
+ //
+ unsigned int size = Memory::GetTotalMemoryFree();
+ rReleasePrintf ("Creating Heap: DEFAULT (%f)\n", HS_DEFAULT);
+ s_pIRadMemoryHeapDefault = radMemoryCreateDougLeaHeap( static_cast<unsigned int>(size), RADMEMORY_ALLOC_DEFAULT, "Default" );
+ s_pIRadMemoryHeapDefault->AddRef();
+ radMemoryRegisterAllocator( GMA_DEFAULT, RADMEMORY_ALLOC_DEFAULT, s_pIRadMemoryHeapDefault );
+
+#endif
+*/
+}
+//=============================================================================
+// HeapManager::PrepareHeapsFeCleanup
+//=============================================================================
+void HeapManager::PrepareHeapsFeCleanup()
+{
+ DestroyHeapA( GMA_LEVEL_HUD );
+
+ if (CommandLineOptions::Get( CLO_NO_HEAPS ))
+ {
+ return;
+ }
+
+ // Free in game heaps
+ //
+ DestroyHeapA( GMA_LEVEL_ZONE );
+ DestroyHeapA( GMA_LEVEL_OTHER );
+ DestroyHeapA( GMA_LEVEL_MISSION );
+ DestroyHeapA( GMA_LEVEL_AUDIO );
+#ifdef USE_CHAR_GAG_HEAP
+ DestroyHeapA( GMA_CHARS_AND_GAGS );
+#endif
+}
+
+//=============================================================================
+// HeapManager::PrepareHeapsFeSetup
+//=============================================================================
+void HeapManager::PrepareHeapsFeSetup()
+{
+ if (CommandLineOptions::Get( CLO_NO_HEAPS ))
+ {
+ return;
+ }
+ const float FUDGE = GetFudgeFactor ();
+ CreateHeap( GMA_LEVEL_FE, static_cast<unsigned int>(HS_LEVEL_FE * FUDGE * MB) );
+ CreateHeap( GMA_LEVEL_MOVIE, static_cast<unsigned int>(HS_LEVEL_MOVIE * MB) );
+ CreateHeap( GMA_LEVEL_AUDIO, static_cast<unsigned int>(HS_LEVEL_AUDIO_FE * MB) );
+}
+
+
+void HeapManager::PrepareHeapsInGame ()
+{
+ const float FUDGE = GetFudgeFactor ();
+ DestroyHeapA( GMA_LEVEL_MOVIE );
+
+ DestroyHeapA( GMA_LEVEL_HUD );
+ CreateHeap( GMA_LEVEL_HUD, static_cast<unsigned int>(HS_LEVEL_HUD * FUDGE * MB) );
+
+#ifdef USE_CHAR_GAG_HEAP
+ CreateHeap( GMA_CHARS_AND_GAGS, static_cast<unsigned int>(1.3f * MB) );
+#endif
+
+ if (CommandLineOptions::Get( CLO_NO_HEAPS ))
+ {
+ return;
+ }
+
+ DestroyHeapA( GMA_LEVEL_AUDIO );
+ DestroyHeapA( GMA_LEVEL_FE );
+ DestroyHeapA( GMA_LEVEL_ZONE );
+
+ // Create in game heaps
+ //
+ CreateHeap( GMA_LEVEL_ZONE, static_cast<unsigned int>(HS_LEVEL_ZONE * FUDGE * MB) );
+ CreateHeap( GMA_LEVEL_AUDIO, static_cast<unsigned int>(HS_LEVEL_AUDIO_INGAME * FUDGE * MB) );
+}
+
+void HeapManager::PrepareHeapsSuperSprint ()
+{
+ const float FUDGE = GetFudgeFactor ();
+
+ DestroyHeapA( GMA_LEVEL_HUD );
+ CreateHeap( GMA_LEVEL_HUD, static_cast<unsigned int>(HS_MINIGAME_HUD * FUDGE * MB) );
+
+ if (CommandLineOptions::Get( CLO_NO_HEAPS ))
+ {
+ return;
+ }
+
+ // Free front end heaps
+ //
+ DestroyHeapA( GMA_LEVEL_FE );
+ DestroyHeapA( GMA_LEVEL_MOVIE );
+ DestroyHeapA( GMA_LEVEL_AUDIO );
+
+ // Free in game heaps (this has to happen for the mission select case, i.e. jumping level to level)
+ //
+ DestroyHeapA( GMA_LEVEL_ZONE );
+// DestroyHeap (s_pIRadMemoryHeapLevelOther);
+ DestroyHeapA( GMA_LEVEL_HUD );
+// DestroyHeap (s_pIRadMemoryHeapLevelMission);
+
+ // Create in game heaps
+ //
+ CreateHeap( GMA_LEVEL_ZONE, static_cast<unsigned int>(HS_MINIGAME_ZONE * FUDGE * MB) );
+
+// rReleaseString ("Creating Heap: OTHER\n");
+// s_pIRadMemoryHeapLevelOther = radMemoryCreateDougLeaHeap ( static_cast<unsigned int>(HS_MINIGAME_OTHER * FUDGE * MB), GMA_LEVEL, "Other" );
+// s_pIRadMemoryHeapLevelOther->AddRef ();
+// radMemoryRegisterAllocator (GMA_LEVEL_OTHER, GMA_LEVEL, s_pIRadMemoryHeapLevelOther);
+
+ CreateHeap( GMA_LEVEL_HUD, static_cast<unsigned int>(HS_MINIGAME_HUD * FUDGE * MB) );
+
+// rReleaseString ("Creating Heap: MISSION\n");
+// s_pIRadMemoryHeapLevelMission = radMemoryCreateDougLeaHeap ( static_cast<unsigned int>(HS_MINIGAME_MISSION * FUDGE * MB), GMA_LEVEL, "Mission" );
+// s_pIRadMemoryHeapLevelMission->AddRef ();
+// radMemoryRegisterAllocator (GMA_LEVEL_MISSION, GMA_LEVEL, s_pIRadMemoryHeapLevelMission);
+
+ CreateHeap( GMA_LEVEL_AUDIO, static_cast<unsigned int>(HS_MINIGAME_AUDIO * FUDGE * MB) );
+
+#ifdef USE_CHAR_GAG_HEAP
+ CreateHeap( GMA_CHARS_AND_GAGS, static_cast<unsigned int>(1.0f * MB) );
+#endif
+}
+
+
+int HeapManager::GetLoadedUsageFE ()
+{
+ return GetHeapUsage( GetAllocator( GMA_LEVEL_FE ) ) +
+ GetHeapUsage ( GetAllocator( GMA_LEVEL_MOVIE ) ) +
+ GetHeapUsage ( GetAllocator( GMA_LEVEL_AUDIO ) ) +
+ GetSoundMemoryHeapUsage();
+}
+
+
+int HeapManager::GetLoadedUsageInGame ()
+{
+ return GetHeapUsage ( GetAllocator( GMA_LEVEL_ZONE ) ) +
+// GetHeapUsage (s_pIRadMemoryHeapLevelOther) +
+ GetHeapUsage ( GetAllocator( GMA_LEVEL_HUD ) ) +
+// GetHeapUsage (s_pIRadMemoryHeapLevelMission) +
+ GetHeapUsage ( GetAllocator( GMA_LEVEL_AUDIO ) ) +
+ GetSoundMemoryHeapUsage();
+}
+
+
+int HeapManager::GetLoadedUsageSuperSprint ()
+{
+ // Right now this should be the same as In Game loading
+ //
+ return GetLoadedUsageInGame ();
+}
+
+int HeapManager::GetLoadedUsage( enum GameMemoryAllocator allocator )
+{
+ return GetHeapUsage ( GetAllocator( allocator ) );
+}
+
+int HeapManager::GetHeapUsage (IRadMemoryAllocator* allocator)
+{
+ if( allocator != NULL )
+ {
+ unsigned int size, totalFree, largestBlock, numObjects, highWater;
+ size = allocator->GetSize();
+ allocator->GetStatus( &totalFree, &largestBlock, &numObjects, &highWater );
+ return size - totalFree;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+int HeapManager::GetSoundMemoryHeapUsage()
+{
+ int soundMemoryUsage = 0;
+
+ #ifdef RAD_PS2
+ unsigned int size;
+ unsigned int totalFree;
+ unsigned int largestBlock;
+ unsigned int numObjects;
+ IRadSoundHalMemoryRegion* soundMemory = ::radSoundHalSystemGet()->GetRootMemoryRegion();
+ rAssert( soundMemory != NULL );
+
+ size = soundMemory->GetSize();
+ soundMemory->GetStats( &totalFree, &numObjects, &largestBlock, true );
+
+ soundMemoryUsage = size - totalFree;
+ #endif
+
+ return( soundMemoryUsage );
+}
+
+HeapManager* HeapMgr ()
+{
+ return HeapManager::GetInstance ();
+}
+
+void LockPersistentHeap()
+{
+ g_LockedPersistentHeap = true;
+}
diff --git a/game/code/memory/srrmemory.h b/game/code/memory/srrmemory.h
new file mode 100644
index 0000000..6ed112b
--- /dev/null
+++ b/game/code/memory/srrmemory.h
@@ -0,0 +1,314 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: srrmemory.h
+//
+// Description: Overides for new and delete.
+//
+// History: 3/20/2002 + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef SRRMEMORY_H
+#define SRRMEMORY_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmemory.hpp>
+#include <radthread.hpp>
+#include <main/commandlineoptions.h>
+#include <memory/stlallocators.h>
+#include <stddef.h>
+#include <vector>
+#include <stack>
+
+//========================================
+// Forward References
+//========================================
+
+extern bool gMemorySystemInitialized;
+
+//========================================
+// Global Declarations
+//========================================
+
+
+// Enable memory monitor in tune and debug
+//
+#ifndef TOOLS
+ #ifndef RAD_MW
+ #ifndef RAD_RELEASE
+ //#define MEMORYTRACKER_ENABLED
+ #endif // RAD_RELEASE
+ #endif // RAD_MW
+#endif // TOOLS
+
+
+
+
+//
+// Override built-in new.
+//
+void* operator new( size_t size )
+#ifdef RAD_PS2
+#ifndef RAD_MW
+throw( std::bad_alloc )
+#endif
+#endif
+;
+
+//
+// Override built-in delete.
+//
+void operator delete( void* pMemory )
+#ifdef RAD_PS2
+#ifndef RAD_MW
+throw()
+#endif
+#endif
+;
+
+//
+// Override built-in array new.
+//
+void* operator new[]( size_t size )
+#ifdef RAD_PS2
+#ifndef RAD_MW
+throw( std::bad_alloc )
+#endif
+#endif
+;
+
+//
+// Override built-in array delete.
+//
+void operator delete[]( void* pMemory )
+#ifdef RAD_PS2
+#ifndef RAD_MW
+throw()
+#endif
+#endif
+;
+
+#ifdef RAD_PS2
+ #define CORRAL_SMALL_ALLOCS
+#endif
+
+// #define USE_CHAR_GAG_HEAP
+
+//
+// Enumeration of the available memory heaps.
+//
+enum GameMemoryAllocator
+{ //
+ GMA_DEFAULT = RADMEMORY_ALLOC_DEFAULT, // 0
+ GMA_TEMP = RADMEMORY_ALLOC_TEMP, // 1
+#ifdef RAD_GAMECUBE
+ GMA_GC_VMM = RADMEMORY_ALLOC_VMM, // 2
+#endif
+ GMA_PERSISTENT = 3, // 3
+ GMA_LEVEL, // 4
+ GMA_LEVEL_MOVIE, // 5
+ GMA_LEVEL_FE, // 6
+ GMA_LEVEL_ZONE, // 7
+ GMA_LEVEL_OTHER, // 8
+ GMA_LEVEL_HUD, // 9
+ GMA_LEVEL_MISSION, // 10
+ GMA_LEVEL_AUDIO, // 11
+ GMA_DEBUG, // 12
+ GMA_SPECIAL, // 13
+ GMA_MUSIC, // 14
+ GMA_AUDIO_PERSISTENT, // 15
+ GMA_SMALL_ALLOC, // 16
+#ifdef RAD_XBOX
+ GMA_XBOX_SOUND_MEMORY, // 17
+#endif
+#ifdef USE_CHAR_GAG_HEAP
+ GMA_CHARS_AND_GAGS,
+#else
+ GMA_CHARS_AND_GAGS = GMA_LEVEL_OTHER,
+#endif
+ GMA_ANYWHERE_IN_LEVEL = 25, // 25
+ GMA_ANYWHERE_IN_FE, // 26
+ GMA_EITHER_OTHER_OR_ZONE, // 27
+
+ GMA_ALLOCATOR_SEARCH = ALLOCATOR_SEARCH, // If you feel like using this one, see an example in FMVPlayer::LoadData
+ NUM_GAME_MEMORY_ALLOCATORS
+};
+
+extern const char* HeapNames[];
+
+void* operator new( size_t size, GameMemoryAllocator allocator );
+void operator delete( void* pMemory, GameMemoryAllocator allocator );
+
+void* operator new[]( size_t size, GameMemoryAllocator allocator );
+void operator delete[]( void* pMemory, GameMemoryAllocator allocator );
+
+void* FindFreeMemory( GameMemoryAllocator allocator, size_t size ); //Use with caution. This is meant to be temporary.
+void LockPersistentHeap();
+void PrintOutOfMemoryMessage( void* userData, radMemoryAllocator heap, const unsigned int size );
+void SetupAllocatorSearch( GameMemoryAllocator allocator );
+
+
+// These are for the global heap stack. This stack maintains where unrouted allocations
+// should go. This way we are able to trap virtually every allocation.
+//
+
+class HeapStack
+{
+public:
+ HeapStack (GameMemoryAllocator defaultAllocator);
+ virtual ~HeapStack ();
+
+ void Push (GameMemoryAllocator alloc);
+ void Pop (GameMemoryAllocator alloc);
+ void Pop ();
+ void SetTop (GameMemoryAllocator alloc);
+ GameMemoryAllocator Top () const;
+
+#ifndef RAD_REALEASE
+ void Dump ();
+#endif
+
+private:
+ GameMemoryAllocator* m_Stack;
+ int m_CurPos;
+ int m_Size;
+
+ enum { STACKSIZE = 48 };
+
+ // Disable
+ HeapStack (const HeapStack&);
+};
+
+
+class HeapActivityTracker : public IRadMemoryActivityCallback
+{
+public:
+ HeapActivityTracker ();
+
+ void EnableHeapAllocs (GameMemoryAllocator alloc, bool enable) { m_AllocsEnabled[alloc] = enable; }
+ void EnableHeapFrees (GameMemoryAllocator alloc, bool enable) { m_FreesEnabled[alloc] = enable; }
+ bool AllocsAllowed (GameMemoryAllocator alloc) const { return m_AllocsEnabled[alloc]; }
+ bool FreesAllowed (GameMemoryAllocator alloc) const { return m_FreesEnabled[alloc]; }
+
+ void MemoryAllocated (radMemoryAllocator allocator, void* address, unsigned int size);
+ void MemoryFreed (radMemoryAllocator allocator, void* address);
+
+private:
+ bool m_AllocsEnabled[NUM_GAME_MEMORY_ALLOCATORS];
+ bool m_FreesEnabled[NUM_GAME_MEMORY_ALLOCATORS];
+
+};
+
+extern HeapActivityTracker g_HeapActivityTracker;
+
+
+class HeapManager : public IRadMemorySetAllocatorCallback
+{
+public:
+ friend class HeapActivityTracker;
+
+ static HeapManager* GetInstance ();
+ static bool IsCreated();
+ static void DestroyInstance();
+
+ void PrepareHeapsStartup ();
+ void PrepareHeapsFeCleanup ();
+ void PrepareHeapsFeSetup ();
+ void PrepareHeapsInGame ();
+ void PrepareHeapsSuperSprint ();
+
+ int GetLoadedUsageFE ();
+ int GetLoadedUsageInGame ();
+ int GetLoadedUsageSuperSprint ();
+ int GetLoadedUsage( GameMemoryAllocator allocator );
+
+ void PushHeap (GameMemoryAllocator alloc);
+ void PopHeap (GameMemoryAllocator alloc);
+ GameMemoryAllocator GetCurrentHeap () const;
+
+#ifdef MEMORYTRACKER_ENABLED
+ void PushGroup( const char* name );
+ void PopGroup ( const char* name );
+ const char* GetCurrentGroupID () const;
+
+ void PushFlag (const char* name);
+ void PopFlag (const char* name);
+#endif
+
+ // From IRadMemorySetAllocatorCallback
+ //
+ radMemoryAllocator GetCurrentAllocator ();
+ radMemoryAllocator SetCurrentAllocator ( radMemoryAllocator allocator );
+
+ void DumpHeapStats ( bool text = false );
+ void DumpArtStats ();
+
+#ifndef RAD_RELEASE
+ static bool s_bSpecialRoute;
+#endif
+
+ void ResetArtStats();
+
+private:
+ HeapManager (GameMemoryAllocator defaultAllocator);
+ HeapManager (const HeapManager&);
+ virtual ~HeapManager ();
+
+ void DestroyHeap (IRadMemoryHeap*& heap, bool justTest = false );
+ GameMemoryAllocator GetHeapID (const IRadMemoryHeap* heap);
+ static float GetFudgeFactor ();
+ int GetHeapUsage ( IRadMemoryAllocator* allocator );
+ int GetSoundMemoryHeapUsage ();
+
+ static IRadThreadLocalStorage* s_Instance; // We maintain a separate heap stack for each thread
+
+#ifdef MEMORYTRACKER_ENABLED
+public: //HACK for the GC, sorry.
+ enum { MAXSTRING = 128 };
+private:
+ typedef struct MemoryIDString { char id[MAXSTRING]; };
+ typedef std::vector< MemoryIDString, s2alloc<MemoryIDString> > MemoryIDVector;
+ typedef std::stack< MemoryIDString, MemoryIDVector > MemoryIDStack;
+
+ MemoryIDStack m_GroupIDStack;
+ MemoryIDStack m_FlagStack;
+#endif
+
+ HeapStack m_HeapStack;
+ enum { MB = 1048576, KB = 1024 };
+
+#ifndef FINAL
+ int mLowestFPS, mHighestTris, mFPSLWTris, mHWTriFPS;
+#endif
+
+};
+
+HeapManager* HeapMgr ();
+
+#ifndef MEMORYTRACKER_ENABLED
+
+ // Push and Pop Group Name
+ #define MEMTRACK_PUSH_GROUP( string )
+ #define MEMTRACK_POP_GROUP( string )
+
+ // Push and Pop Flag Name
+ #define MEMTRACK_PUSH_FLAG( string )
+ #define MEMTRACK_POP_FLAG( string )
+
+#else
+
+ // Push and Pop Group Name
+ #define MEMTRACK_PUSH_GROUP( string ) HeapMgr()->PushGroup( string )
+ #define MEMTRACK_POP_GROUP( string ) HeapMgr()->PopGroup( string )
+
+ // Push and Pop Flag Name
+ #define MEMTRACK_PUSH_FLAG( string ) HeapMgr()->PushFlag( string )
+ #define MEMTRACK_POP_FLAG( string ) HeapMgr()->PopFlag( string )
+
+#endif // !MEMORYTRACKER_ENABLED
+
+
+#endif //SRRMEMORY_H
diff --git a/game/code/memory/stlallocators.h b/game/code/memory/stlallocators.h
new file mode 100644
index 0000000..eea43c2
--- /dev/null
+++ b/game/code/memory/stlallocators.h
@@ -0,0 +1,106 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: stlallocators.h
+//
+// Description: Use these custom allocators to route STL memory management
+// through our memory system.
+//
+// History: 3/20/2002 + Created -- Darwin Chau
+//
+//=============================================================================
+
+#ifndef STLALLOCATORS_H
+#define STLALLOCATORS_H
+
+
+//========================================
+// Nested Includes
+//========================================
+#include <stddef.h>
+
+
+
+template <class T> class s2alloc
+{
+ public:
+
+ // type definitions
+ typedef size_t size_type;
+ typedef ptrdiff_t difference_type;
+ typedef T* pointer;
+ typedef const T* const_pointer;
+ typedef T& reference;
+ typedef const T& const_reference;
+ typedef T value_type;
+
+ // rebind allocator type to U
+ template<class U>
+ struct rebind
+ {
+ typedef s2alloc<U> other;
+ };
+
+ // return address of values
+ pointer address( reference x ) const
+ {
+ return &x;
+ }
+
+ const_pointer address( const_reference x ) const
+ {
+ return &x;
+ }
+
+ // constructors and destructor - nothing to do cause the allocator
+ // has no state
+ s2alloc() {}
+ s2alloc( const s2alloc& ) {}
+#ifndef TOOLS
+ template<class U> s2alloc( const s2alloc<U>&) {}
+#endif
+ ~s2alloc() {}
+
+ // return maximum number of elements that can be allocated
+ size_type max_size() const
+ {
+ size_type count = (size_type)(-1) / sizeof(T);
+ return (0 < count ? count : 1);
+ }
+
+ // allocate but don't initialize num elemetns of type T
+ pointer allocate( size_type n, const void* hint = 0 )
+ {
+ return (pointer) ::operator new(( n*sizeof(T) ));
+ }
+
+ // initialize elements of allocated storage p with value val
+ void construct( pointer p, const T& val )
+ {
+ // Placement new. Don't have to override this new cause it
+ // doesn't actually alloctate memory.
+ //
+ new((void*)p) T(val);
+ }
+
+
+ // destroy elemetns of initialized storage p
+ void destroy( pointer p )
+ {
+ // Placement delete. Must explicitly invoke destructor
+ // but it doesn't actually free memory.
+ //
+ p->~T();
+ }
+
+ // deallocate storage p of deleted items
+ void deallocate( pointer p, size_type n )
+ {
+ ::operator delete( (void*)p );
+ }
+
+}; // end s2alloc<T>
+
+
+
+#endif //STLALLOCATORS_H
diff --git a/game/code/meta/actioneventlocator.cpp b/game/code/meta/actioneventlocator.cpp
new file mode 100644
index 0000000..3986175
--- /dev/null
+++ b/game/code/meta/actioneventlocator.cpp
@@ -0,0 +1,335 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ActionEventLocator.cpp
+//
+// Description: Implement ActionEventLocator
+//
+// History: 30/07/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <string.h>
+#include <p3d/inventory.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <meta/ActionEventLocator.h>
+#include <memory/srrmemory.h>
+#include <ai/actionbuttonmanager.h>
+#include <ai/actionbuttonhandler.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <debug/profiler.h>
+
+void PrepareString( char** string, unsigned char* lengthHolder, unsigned int length )
+{
+MEMTRACK_PUSH_GROUP( "ActionEventLocator" );
+ rAssert( !(*lengthHolder) );
+
+ if ( *string )
+ {
+ rAssertMsg( false, "Why is someone changing this! BAD! Get Cary!" );
+ (*lengthHolder) = 0;
+ delete[] *string;
+ *string = NULL;
+ }
+ else
+ {
+ (*lengthHolder) = length + 1;
+ }
+
+ *string = new(GMA_LEVEL_OTHER) char[ (*lengthHolder) ];
+MEMTRACK_POP_GROUP( "ActionEventLocator" );
+}
+
+void SetString( char** string, unsigned int length, const char* newString )
+{
+ rAssert( newString );
+ rAssert( length );
+ rAssert( *string );
+
+ if ( *string )
+ {
+ unsigned int newLength = (length - 1) > strlen(newString) ? strlen(newString) : length - 1;
+ strncpy( *string, newString, newLength );
+ (*string)[newLength] = '\0';
+ }
+}
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+static int AELcount=0;
+//==============================================================================
+// ActionEventLocator::ActionEventLocator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ActionEventLocator::ActionEventLocator() :
+ mObjNameSize( 0 ),
+ mJointNameSize( 0 ),
+ mActionNameSize( 0 ),
+ mShouldTransform( false ),
+ mObjName( NULL ),
+ mJointName( NULL ),
+ mActionName( NULL ),
+ mButton( CharacterController::DoAction )
+{
+ SetData( -1 );
+ AELcount++;
+ mMatrix.Identity();
+}
+
+//==============================================================================
+// ActionEventLocator::~ActionEventLocator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ActionEventLocator::~ActionEventLocator()
+{
+ if ( mObjName )
+ {
+ delete [] mObjName;
+ mObjName = NULL;
+ }
+
+ mObjNameSize = 0;
+ AELcount--;
+ if ( mJointName )
+ {
+ delete [] mJointName;
+ mJointName = NULL;
+ }
+
+ mJointNameSize = 0;
+
+ if ( mActionName )
+ {
+ delete [] mActionName;
+ mActionName = NULL;
+ }
+
+ mActionNameSize = 0;
+
+ int actionId = (int)this->GetData( );
+ if ( actionId != -1 )
+ {
+ actionId = -1;
+ //The Action Button Manager will clean this out when the Dump Dyna or Destroy are called.
+ //This sucks.
+ }
+}
+
+//=============================================================================
+// ActionEventLocator::SetObjNameSize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int size )
+//
+// Return: void
+//
+//=============================================================================
+void ActionEventLocator::SetObjNameSize( unsigned char size )
+{
+ PrepareString( &mObjName, &mObjNameSize, size );
+}
+
+//=============================================================================
+// ActionEventLocator::SetObjName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void ActionEventLocator::SetObjName( const char* name )
+{
+ SetString( &mObjName, mObjNameSize, name );
+}
+
+//=============================================================================
+// ActionEventLocator::SetJointNameSize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int size )
+//
+// Return: void
+//
+//=============================================================================
+void ActionEventLocator::SetJointNameSize( unsigned char size )
+{
+ PrepareString( &mJointName, &mJointNameSize, size );
+}
+
+//=============================================================================
+// ActionEventLocator::SetJointName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void ActionEventLocator::SetJointName( const char* name )
+{
+ SetString( &mJointName, mJointNameSize, name );
+}
+
+//=============================================================================
+// ActionEventLocator::SetActionNameSize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int size )
+//
+// Return: void
+//
+//=============================================================================
+void ActionEventLocator::SetActionNameSize( unsigned char size )
+{
+ PrepareString( &mActionName, &mActionNameSize, size );
+}
+
+//=============================================================================
+// ActionEventLocator::SetActionName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void ActionEventLocator::SetActionName( const char* name )
+{
+ SetString( &mActionName, mActionNameSize, name );
+}
+/*
+==============================================================================
+ActionEventLocator::AddToGame
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+bool ActionEventLocator::AddToGame( tEntityStore* store )
+{
+ return GetActionButtonManager()->AddActionEventLocator( this, store );
+}
+//=============================================================================
+// ActionEventLocator::Reset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void )
+//
+// Return: void
+//
+//=============================================================================
+void ActionEventLocator::Reset( void )
+{
+ // Get the index for the action ptr.
+ //
+ int actionId = (int)this->GetData( );
+ ActionButton::ButtonHandler* pActionButtonHandler = GetActionButtonManager()->GetActionByIndex( actionId );
+ rAssert( pActionButtonHandler );
+ pActionButtonHandler->Reset( );
+}
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// ActionEventLocator::OnTrigger
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int playerID, bool bActive )
+//
+// Return: void
+//
+//=============================================================================
+void ActionEventLocator::OnTrigger( unsigned int playerID )
+{
+BEGIN_PROFILE( "AEL On Trigger" );
+ if ( this->GetPlayerEntered() )
+ {
+ // Get the index for the action ptr.
+ //
+ int actionId = (int)this->GetData( );
+
+ // Trigger the enter context.
+ //
+ unsigned int playerId = GetPlayerID();
+ Character* pCharacter = GetCharacterManager( )->GetCharacter( playerId );
+ rAssert( pCharacter );
+
+ ActionButton::ButtonHandler* pActionButtonHandler = GetActionButtonManager()->GetActionByIndex( actionId );
+ rAssert( pActionButtonHandler );
+
+ // Entered an action volume.
+ //
+ if(pActionButtonHandler->UsesActionButton())
+ {
+ pCharacter->AddActionButtonHandler( pActionButtonHandler );
+ }
+
+ GetActionButtonManager()->EnterActionTrigger( pCharacter, actionId );
+ }
+ else
+ {
+ // Get the index for the action ptr.
+ //
+ int actionId = (int)this->GetData( );
+ rAssert( actionId >= 0 );
+
+ ActionButton::ButtonHandler* pActionButtonHandler = GetActionButtonManager()->GetActionByIndex( actionId );
+ rAssert( pActionButtonHandler );
+
+ // Trigger the exit context.
+ //
+ unsigned int playerId = GetPlayerID();
+ Character* pCharacter = GetCharacterManager( )->GetCharacter( playerId );
+
+ GetActionButtonManager()->ExitActionTrigger( pCharacter, actionId );
+ pCharacter->RemoveActionButtonHandler( pActionButtonHandler );
+ }
+END_PROFILE( "AEL On Trigger" );
+}
+
diff --git a/game/code/meta/actioneventlocator.h b/game/code/meta/actioneventlocator.h
new file mode 100644
index 0000000..2d9ca4f
--- /dev/null
+++ b/game/code/meta/actioneventlocator.h
@@ -0,0 +1,248 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: actioneventlocator.h
+//
+// Description: Blahblahblah
+//
+// History: 30/07/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef ACTIONEVENTLOCATOR_H
+#define ACTIONEVENTLOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <meta/locatortypes.h>
+#include <meta/locatorevents.h>
+#include <meta/triggerlocator.h>
+#include <worldsim/character/charactercontroller.h>
+
+//========================================
+// Forward References
+//========================================
+class tEntityStore;
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ActionEventLocator : public TriggerLocator
+{
+public:
+ ActionEventLocator();
+ virtual ~ActionEventLocator();
+
+ //From Locator
+ virtual LocatorType::Type GetDataType() const;
+
+ void SetObjNameSize( unsigned char size );
+ void SetObjName( const char* name );
+ const char* GetObjName() const;
+
+ void SetJointNameSize( unsigned char size );
+ void SetJointName( const char* name );
+ const char* GetJointName() const;
+
+ void SetActionNameSize( unsigned char size );
+ void SetActionName( const char* name );
+ const char* GetActionName() const;
+
+ void SetButtonInput( CharacterController::eIntention button );
+ CharacterController::eIntention GetButtonInput() const;
+
+ void SetShouldTransform( bool should );
+ bool GetShouldTransform() const;
+
+ bool AddToGame( tEntityStore* store );
+ void Reset( void );
+
+ void SetMatrix( const rmt::Matrix& mat );
+ rmt::Matrix& GetMatrix();
+
+private:
+ unsigned char mObjNameSize;
+ unsigned char mJointNameSize;
+ unsigned char mActionNameSize;
+
+ bool mShouldTransform;
+
+ char* mObjName;
+ char* mJointName;
+ char* mActionName;
+
+ CharacterController::eIntention mButton;
+ rmt::Matrix mMatrix;
+
+
+
+ //From TriggerLocator
+ virtual void OnTrigger( unsigned int playerID );
+
+ //Prevent wasteful constructor creation.
+ ActionEventLocator( const ActionEventLocator& actioneventlocator );
+ ActionEventLocator& operator=( const ActionEventLocator& actioneventlocator );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ActionEventLocator::GetDataType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: LocatorType
+//
+//=============================================================================
+inline LocatorType::Type ActionEventLocator::GetDataType() const
+{
+ return LocatorType::ACTION;
+}
+
+//=============================================================================
+// ActionEventLocator::GetObjName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+inline const char* ActionEventLocator::GetObjName() const
+{
+ return mObjName;
+}
+
+
+//=============================================================================
+// ActionEventLocator::GetJointName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+inline const char* ActionEventLocator::GetJointName() const
+{
+ return mJointName;
+}
+
+//=============================================================================
+// ActionEventLocator::GetActionName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+inline const char* ActionEventLocator::GetActionName() const
+{
+ return mActionName;
+}
+
+//=============================================================================
+// ActionEventLocator::SetButtonInput
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( CharacterController::eIntention button )
+//
+// Return: void
+//
+//=============================================================================
+inline void ActionEventLocator::SetButtonInput( CharacterController::eIntention button )
+{
+ mButton = button;
+}
+
+//=============================================================================
+// ActionEventLocator::GetButtonInput
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: CharacterController
+//
+//=============================================================================
+inline CharacterController::eIntention ActionEventLocator::GetButtonInput() const
+{
+ return mButton;
+}
+
+//=============================================================================
+// ActionEventLocator::SetShouldTransform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool should )
+//
+// Return: void
+//
+//=============================================================================
+inline void ActionEventLocator::SetShouldTransform( bool should )
+{
+ mShouldTransform = should;
+}
+
+//=============================================================================
+// ActionEventLocator::GetShouldTransform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool ActionEventLocator::GetShouldTransform() const
+{
+ return mShouldTransform;
+}
+
+//=============================================================================
+// ActionEventLocator::SetMatrix
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Matrix& mat )
+//
+// Return: void
+//
+//=============================================================================
+inline void ActionEventLocator::SetMatrix( const rmt::Matrix& mat )
+{
+ mMatrix = mat;
+}
+
+//=============================================================================
+// ActionEventLocator::GetMatrix
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: rmt
+//
+//=============================================================================
+inline rmt::Matrix& ActionEventLocator::GetMatrix()
+{
+ return mMatrix;
+}
+
+
+#endif //ACTIONEVENTLOCATOR_H
diff --git a/game/code/meta/allmeta.cpp b/game/code/meta/allmeta.cpp
new file mode 100644
index 0000000..9c010d1
--- /dev/null
+++ b/game/code/meta/allmeta.cpp
@@ -0,0 +1,17 @@
+#include <meta/actioneventlocator.cpp>
+#include <meta/carstartlocator.cpp>
+#include <meta/directionallocator.cpp>
+#include <meta/eventlocator.cpp>
+#include <meta/fovlocator.cpp>
+#include <meta/interiorentrancelocator.cpp>
+#include <meta/locator.cpp>
+#include <meta/occlusionlocator.cpp>
+#include <meta/recttriggervolume.cpp>
+#include <meta/scriptlocator.cpp>
+#include <meta/spheretriggervolume.cpp>
+#include <meta/splinelocator.cpp>
+#include <meta/triggerlocator.cpp>
+#include <meta/triggervolume.cpp>
+#include <meta/triggervolumetracker.cpp>
+#include <meta/zoneeventlocator.cpp>
+#include <meta/staticcamlocator.cpp> \ No newline at end of file
diff --git a/game/code/meta/carstartlocator.cpp b/game/code/meta/carstartlocator.cpp
new file mode 100644
index 0000000..356fcc4
--- /dev/null
+++ b/game/code/meta/carstartlocator.cpp
@@ -0,0 +1,69 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: CarStartLocator.cpp
+//
+// Description: Implement CarStartLocator
+//
+// History: 19/06/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <meta/carstartlocator.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// CarStartLocator::CarStartLocator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CarStartLocator::CarStartLocator()
+{
+}
+
+//==============================================================================
+// CarStartLocator::~CarStartLocator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CarStartLocator::~CarStartLocator()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/meta/carstartlocator.h b/game/code/meta/carstartlocator.h
new file mode 100644
index 0000000..a410a87
--- /dev/null
+++ b/game/code/meta/carstartlocator.h
@@ -0,0 +1,102 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: carstartlocator.h
+//
+// Description: Blahblahblah
+//
+// History: 19/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef CARSTARTLOCATOR_H
+#define CARSTARTLOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <meta/locator.h>
+#include <meta/locatortypes.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class CarStartLocator : public Locator
+{
+public:
+ CarStartLocator();
+ virtual ~CarStartLocator();
+
+ virtual LocatorType::Type GetDataType() const;
+
+ float GetRotation() const;
+ void SetRotation( float rot );
+
+private:
+
+ float mYRotation;
+
+ //Prevent wasteful constructor creation.
+ CarStartLocator( const CarStartLocator& carstartlocator );
+ CarStartLocator& operator=( const CarStartLocator& carstartlocator );
+};
+
+//******************************************************************************
+//
+// Inline Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// CarStartLocator::GetDataType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline LocatorType::Type CarStartLocator::GetDataType() const
+{
+ return( LocatorType::CAR_START );
+}
+
+//=============================================================================
+// CarStartLocator::GetRotation
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float CarStartLocator::GetRotation() const
+{
+ return mYRotation;
+}
+
+//=============================================================================
+// CarStartLocator::SetRotation
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float rot )
+//
+// Return: void
+//
+//=============================================================================
+inline void CarStartLocator::SetRotation( float rot )
+{
+ mYRotation = rot;
+}
+
+#endif //CARSTARTLOCATOR_H
diff --git a/game/code/meta/directionallocator.cpp b/game/code/meta/directionallocator.cpp
new file mode 100644
index 0000000..3f338c0
--- /dev/null
+++ b/game/code/meta/directionallocator.cpp
@@ -0,0 +1,70 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: DirectionalLocator.cpp
+//
+// Description: Implement DirectionalLocator
+//
+// History: 30/07/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <meta/DirectionalLocator.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// DirectionalLocator::DirectionalLocator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DirectionalLocator::DirectionalLocator()
+{
+ mTransform.Identity();
+}
+
+//==============================================================================
+// DirectionalLocator::~DirectionalLocator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DirectionalLocator::~DirectionalLocator()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/meta/directionallocator.h b/game/code/meta/directionallocator.h
new file mode 100644
index 0000000..ccc6d44
--- /dev/null
+++ b/game/code/meta/directionallocator.h
@@ -0,0 +1,105 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: directionallocator.h
+//
+// Description: Blahblahblah
+//
+// History: 30/07/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef DIRECTIONALLOCATOR_H
+#define DIRECTIONALLOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <meta/locator.h>
+#include <meta/locatortypes.h>
+
+#include <radmath/radmath.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class DirectionalLocator : public Locator
+{
+public:
+ DirectionalLocator();
+ virtual ~DirectionalLocator();
+
+ virtual LocatorType::Type GetDataType() const;
+
+ void SetTransform( const rmt::Matrix& transform );
+ const rmt::Matrix& GetTransform() const;
+
+private:
+
+ rmt::Matrix mTransform;
+
+ //Prevent wasteful constructor creation.
+ DirectionalLocator( const DirectionalLocator& directionallocator );
+ DirectionalLocator& operator=( const DirectionalLocator& directionallocator );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// DirectionalLocator::GetDataType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: LocatorType::Type
+//
+//=============================================================================
+inline LocatorType::Type DirectionalLocator::GetDataType() const
+{
+ return LocatorType::DIRECTIONAL;
+}
+
+
+//=============================================================================
+// DirectionalLocator::SetTransform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Matrix& transform )
+//
+// Return: void
+//
+//=============================================================================
+inline void DirectionalLocator::SetTransform( const rmt::Matrix& transform )
+{
+ mTransform = transform;
+}
+
+//=============================================================================
+// DirectionalLocator::GetTransform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: rmt
+//
+//=============================================================================
+inline const rmt::Matrix& DirectionalLocator::GetTransform() const
+{
+ return mTransform;
+}
+
+#endif //DIRECTIONALLOCATOR_H
diff --git a/game/code/meta/eventlocator.cpp b/game/code/meta/eventlocator.cpp
new file mode 100644
index 0000000..d4dbaee
--- /dev/null
+++ b/game/code/meta/eventlocator.cpp
@@ -0,0 +1,88 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement EventLocator
+//
+// History: 04/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <meta/EventLocator.h>
+#include <meta/locatorevents.h>
+#include <events/eventmanager.h>
+
+#include <debug/profiler.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// EventLocator::EventLocator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+EventLocator::EventLocator()
+{
+ mMatrix.Identity();
+}
+
+//==============================================================================
+// EventLocator::~EventLocator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+EventLocator::~EventLocator()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// EventLocator::OnTrigger
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int playerID )
+//
+// Return: void
+//
+//=============================================================================
+void EventLocator::OnTrigger( unsigned int playerID )
+{
+ //This will fire an event off that uses the data field
+ GetEventManager()->TriggerEvent( (EventEnum)( EVENT_LOCATOR + mEventType ), (void*)this );
+}
diff --git a/game/code/meta/eventlocator.h b/game/code/meta/eventlocator.h
new file mode 100644
index 0000000..8dd2d10
--- /dev/null
+++ b/game/code/meta/eventlocator.h
@@ -0,0 +1,135 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: eventlocator.h
+//
+// Description: Blahblahblah
+//
+// History: 04/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef EVENTLOCATOR_H
+#define EVENTLOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+#include <meta/triggerlocator.h>
+#include <meta/locatortypes.h>
+#include <meta/locatorevents.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class EventLocator : public TriggerLocator
+{
+public:
+ EventLocator();
+ virtual ~EventLocator();
+
+ virtual LocatorType::Type GetDataType() const;
+
+ LocatorEvent::Event GetEventType() const;
+ void SetEventType( LocatorEvent::Event eventType );
+
+ void SetMatrix( const rmt::Matrix& mat );
+ rmt::Matrix& GetMatrix();
+
+private:
+ virtual void OnTrigger( unsigned int playerID );
+
+ //Prevent wasteful constructor creation.
+ EventLocator( const EventLocator& eventlocator );
+ EventLocator& operator=( const EventLocator& eventlocator );
+
+ LocatorEvent::Event mEventType;
+
+ rmt::Matrix mMatrix;
+};
+
+//=============================================================================
+// EventLocator::GetDataType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline LocatorType::Type EventLocator::GetDataType() const
+{
+ return( LocatorType::EVENT );
+}
+
+//=============================================================================
+// EventLocator::SetEventType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( LocatorEvent eventType )
+//
+// Return: inline
+//
+//=============================================================================
+inline void EventLocator::SetEventType( LocatorEvent::Event eventType )
+{
+ mEventType = eventType;
+}
+
+//=============================================================================
+// EventLocator::GetEventType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline LocatorEvent::Event EventLocator::GetEventType() const
+{
+ return( mEventType );
+}
+
+//=============================================================================
+// EventLocator::SetMatrix
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Matrix& mat )
+//
+// Return: void
+//
+//=============================================================================
+inline void EventLocator::SetMatrix( const rmt::Matrix& mat )
+{
+ mMatrix = mat;
+}
+
+//=============================================================================
+// EventLocator::GetMatrix
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: rmt
+//
+//=============================================================================
+inline rmt::Matrix& EventLocator::GetMatrix()
+{
+ return mMatrix;
+}
+
+#endif //EVENTLOCATOR_H
diff --git a/game/code/meta/fovlocator.cpp b/game/code/meta/fovlocator.cpp
new file mode 100644
index 0000000..ff20dc6
--- /dev/null
+++ b/game/code/meta/fovlocator.cpp
@@ -0,0 +1,125 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: FOVLocator.cpp
+//
+// Description: Implement FOVLocator
+//
+// History: 04/08/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <meta/FOVLocator.h>
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// FOVLocator::FOVLocator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+FOVLocator::FOVLocator()
+{
+ RegisterDebugData();
+}
+
+//==============================================================================
+// FOVLocator::~FOVLocator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+FOVLocator::~FOVLocator()
+{
+ UnRegisterDebugData();
+}
+
+//=============================================================================
+// FOVLocator::RegisterDebugData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FOVLocator::RegisterDebugData()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchAddFloat( &mFOV, "FOV", "FOV Override", NULL, NULL, 0.01f, rmt::PI );
+ radDbgWatchAddFloat( &mTime, "Max Transition Time (seconds)", "FOV Override", NULL, NULL, 0.0f, 5.0f );
+ radDbgWatchAddFloat( &mRate, "Transition Rate", "FOV Override", NULL, NULL, 0.0f, 1.0f );
+#endif
+}
+
+//=============================================================================
+// FOVLocator::UnRegisterDebugData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FOVLocator::UnRegisterDebugData()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mFOV );
+ radDbgWatchDelete( &mTime );
+ radDbgWatchDelete( &mRate );
+#endif
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+void FOVLocator::OnTrigger( unsigned int playerID )
+{
+BEGIN_PROFILE( "FOVL OnTrigger" );
+ if ( GetPlayerEntered() )
+ {
+ SuperCamManager::GetInstance()->GetSCC( playerID )->RegisterFOVLocator( this );
+ }
+ else
+ {
+ SuperCamManager::GetInstance()->GetSCC( playerID )->UnregisterFOVLocator();
+ }
+END_PROFILE( "FOVL OnTrigger" );
+}
diff --git a/game/code/meta/fovlocator.h b/game/code/meta/fovlocator.h
new file mode 100644
index 0000000..c09dbdc
--- /dev/null
+++ b/game/code/meta/fovlocator.h
@@ -0,0 +1,176 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: fovlocator.h
+//
+// Description: Blahblahblah
+//
+// History: 03/08/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef FOVLOCATOR_H
+#define FOVLOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/entity.hpp>
+#include <radmath/radmath.hpp>
+
+//========================================
+// Forward References
+//========================================
+#include <meta/triggerlocator.h>
+#include <meta/locatortypes.h>
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class FOVLocator : public TriggerLocator
+{
+public:
+ FOVLocator();
+ virtual ~FOVLocator();
+
+ virtual LocatorType::Type GetDataType() const;
+
+ void SetFOV( float fovInRadians );
+ float GetFOV() const;
+
+ void SetTime( float seconds );
+ float GetTime() const;
+
+ void SetRate( float rate );
+ float GetRate() const;
+
+ void RegisterDebugData();
+ void UnRegisterDebugData();
+
+private:
+ float mFOV;
+ float mTime;
+ float mRate;
+
+ virtual void OnTrigger( unsigned int playerID );
+
+ //Prevent wasteful constructor creation.
+ FOVLocator( const FOVLocator& fovlocator );
+ FOVLocator& operator=( const FOVLocator& fovlocator );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// FOVLocator::GetDataType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: LocatorType ::Type
+//
+//=============================================================================
+inline LocatorType::Type FOVLocator::GetDataType() const
+{
+ return( LocatorType::FOV );
+}
+
+//=============================================================================
+// FOVLocator::SetFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float fovInRadians )
+//
+// Return: void
+//
+//=============================================================================
+inline void FOVLocator::SetFOV( float fovInRadians )
+{
+ mFOV = fovInRadians;
+}
+
+//=============================================================================
+// FOVLocator::GetFOV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FOVLocator::GetFOV() const
+{
+ return mFOV;
+}
+
+//=============================================================================
+// FOVLocator::SetTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float seconds )
+//
+// Return: void
+//
+//=============================================================================
+inline void FOVLocator::SetTime( float seconds )
+{
+ mTime = seconds;
+}
+
+//=============================================================================
+// FOVLocator::GetTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FOVLocator::GetTime() const
+{
+ return mTime;
+}
+
+//=============================================================================
+// FOVLocator::SetRate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float rate )
+//
+// Return: void
+//
+//=============================================================================
+inline void FOVLocator::SetRate( float rate )
+{
+ mRate = rate;
+}
+
+//=============================================================================
+// FOVLocator::GetRate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+inline float FOVLocator::GetRate() const
+{
+ return mRate;
+}
+
+#endif //FOVLOCATOR_H
diff --git a/game/code/meta/interiorentrancelocator.cpp b/game/code/meta/interiorentrancelocator.cpp
new file mode 100644
index 0000000..b16cf8b
--- /dev/null
+++ b/game/code/meta/interiorentrancelocator.cpp
@@ -0,0 +1,217 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: InteriorEntranceLocator.cpp
+//
+// Description: Implement InteriorEntranceLocator
+//
+// History: 30/07/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <meta/InteriorEntranceLocator.h>
+#include <memory/srrmemory.h>
+#include <meta/locatorevents.h>
+#include <events/eventmanager.h>
+#include <ai/actionbuttonmanager.h>
+#include <ai/actionbuttonhandler.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <debug/profiler.h>
+
+#include <mission/gameplaymanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// InteriorEntranceLocator::InteriorEntranceLocator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+InteriorEntranceLocator::InteriorEntranceLocator() :
+ mInteriorFileName( NULL ),
+ mInteriorFileNameSize( 0 )
+{
+ SetEventType( LocatorEvent::INTERIOR_ENTRANCE );
+ mTransform.Identity();
+ mpEnterInteriorAction = new ActionButton::EnterInterior( this );
+ mpEnterInteriorAction->AddRef();
+}
+
+//==============================================================================
+// InteriorEntranceLocator::~InteriorEntranceLocator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+InteriorEntranceLocator::~InteriorEntranceLocator()
+{
+ if ( mInteriorFileName )
+ {
+ delete[] mInteriorFileName;
+ mInteriorFileName = NULL;
+ }
+
+ // Tell the button handler to forget about us, then release it
+ //
+ mpEnterInteriorAction->SetLocator( 0 );
+ tRefCounted::Release( mpEnterInteriorAction );
+ mpEnterInteriorAction = 0;
+ mInteriorFileNameSize = 0;
+}
+
+//=============================================================================
+// InteriorEntranceLocator::SetInteriorFileName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* fileName )
+//
+// Return: void
+//
+//=============================================================================
+void InteriorEntranceLocator::SetInteriorFileName( const char* fileName )
+{
+ rAssert( fileName );
+ rAssert( mInteriorFileNameSize );
+ rAssert( mInteriorFileName );
+
+ if ( mInteriorFileName )
+ {
+ unsigned int length = (mInteriorFileNameSize - 1) > strlen(fileName) ? strlen(fileName) : mInteriorFileNameSize - 1;
+ strncpy( mInteriorFileName, fileName, length );
+ mInteriorFileName[length] = '\0';
+ }
+}
+
+//=============================================================================
+// InteriorEntranceLocator::SetInteriorFileNameSize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int size )
+//
+// Return: void
+//
+//=============================================================================
+void InteriorEntranceLocator::SetInteriorFileNameSize( unsigned int size )
+{
+MEMTRACK_PUSH_GROUP( "InteriorEntranceLocator" );
+ rAssert( !mInteriorFileNameSize );
+
+ if ( mInteriorFileName )
+ {
+ rAssertMsg( false, "Why is someone changing this! BAD! Get Cary!" );
+ mInteriorFileNameSize = 0;
+ delete[] mInteriorFileName;
+ mInteriorFileName = NULL;
+ }
+ else
+ {
+ mInteriorFileNameSize = size + 1;
+ }
+
+ mInteriorFileName = new(GMA_LEVEL_OTHER) char[ mInteriorFileNameSize ];
+MEMTRACK_POP_GROUP( "InteriorEntranceLocator" );
+}
+/*
+==============================================================================
+InteriorEntranceLocator::OnTrigger
+==============================================================================
+Description: Comment
+
+Parameters: ( unsigned int playerID )
+
+Return: void
+
+=============================================================================
+*/
+void InteriorEntranceLocator::OnTrigger( unsigned int playerID )
+{
+BEGIN_PROFILE( "IEL OnTrigger" );
+ //This will fire an event off that uses the data field
+ GetEventManager()->TriggerEvent( (EventEnum)( EVENT_LOCATOR + GetEventType() ), (void*)this );
+
+ rAssert( mpEnterInteriorAction );
+
+ bool safeToEnter = true;
+
+ if(GetGameplayManager()->GetCurrentMission() != NULL)
+ {
+ //chuck: adding a check, if we are in a street race or gamble race interiors are off.
+ if(
+ GetGameplayManager()->GetCurrentMission()->IsRaceMission()
+ ||
+ GetGameplayManager()->GetCurrentMission()->IsWagerMission()
+ )
+ {
+ safeToEnter = false;
+ }
+ }
+
+ if ( this->GetPlayerEntered() && safeToEnter )
+ {
+ if ( mpEnterInteriorAction )
+ {
+ // Trigger the enter context.
+ //
+ unsigned int playerId = GetPlayerID();
+ Character* pCharacter = GetCharacterManager( )->GetCharacter( playerId );
+ rAssert( pCharacter );
+
+ // Entered an action volume.
+ //
+ pCharacter->AddActionButtonHandler( mpEnterInteriorAction );
+ mpEnterInteriorAction->Enter( pCharacter );
+ }
+ }
+ else
+ {
+ if ( mpEnterInteriorAction )
+ {
+
+ // Trigger the exit context.
+ //
+ unsigned int playerId = GetPlayerID();
+ Character* pCharacter = GetCharacterManager( )->GetCharacter( playerId );
+
+ mpEnterInteriorAction->Exit( pCharacter );
+ pCharacter->RemoveActionButtonHandler( mpEnterInteriorAction );
+ }
+ }
+END_PROFILE( "IEL OnTrigger" );
+}
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/meta/interiorentrancelocator.h b/game/code/meta/interiorentrancelocator.h
new file mode 100644
index 0000000..d457795
--- /dev/null
+++ b/game/code/meta/interiorentrancelocator.h
@@ -0,0 +1,147 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: interiorentrancelocator.h
+//
+// Description: Blahblahblah
+//
+// History: 30/07/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef INTERIORENTRANCELOCATOR_H
+#define INTERIORENTRANCELOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <meta/locatortypes.h>
+#include <meta/locatorevents.h>
+#include <meta/eventlocator.h>
+
+#include <radmath/radmath.hpp>
+
+//========================================
+// Forward References
+//========================================
+namespace ActionButton
+{
+ class EnterInterior;
+};
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class InteriorEntranceLocator : public EventLocator
+{
+public:
+ InteriorEntranceLocator();
+ virtual ~InteriorEntranceLocator();
+
+ virtual LocatorType::Type GetDataType() const;
+
+ void SetInteriorFileName( const char* fileName );
+ const char* GetInteriorFileName() const;
+
+ void SetInteriorFileNameSize( unsigned int size ); //This is to prevent fragmentation.
+ unsigned int GetInteriorFileNameSize() const;
+
+ void SetTransform( const rmt::Matrix& transform );
+ const rmt::Matrix& GetTransform() const;
+
+private:
+ virtual void OnTrigger( unsigned int playerID );
+
+ char* mInteriorFileName;
+ unsigned int mInteriorFileNameSize;
+ rmt::Matrix mTransform;
+ ActionButton::EnterInterior* mpEnterInteriorAction;
+
+ //Prevent wasteful constructor creation.
+ InteriorEntranceLocator( const InteriorEntranceLocator& interiorentrancelocator );
+ InteriorEntranceLocator& operator=( const InteriorEntranceLocator& interiorentrancelocator );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// InteriorEntranceLocator::GetDataType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline LocatorType::Type InteriorEntranceLocator::GetDataType() const
+{
+ return( LocatorType::INTERIOR_ENTRANCE );
+}
+
+//=============================================================================
+// InteriorEntranceLocator::GetInteriorFileName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+inline const char* InteriorEntranceLocator::GetInteriorFileName() const
+{
+ return mInteriorFileName;
+}
+
+//=============================================================================
+// InteriorEntranceLocator::GetInteriorFileNameSize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int InteriorEntranceLocator::GetInteriorFileNameSize() const
+{
+ return mInteriorFileNameSize;
+}
+
+//=============================================================================
+// InteriorEntranceLocator::SetTransform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Matrix& transform )
+//
+// Return: void
+//
+//=============================================================================
+inline void InteriorEntranceLocator::SetTransform( const rmt::Matrix& transform )
+{
+ mTransform = transform;
+}
+
+//=============================================================================
+// InteriorEntranceLocator::GetTransform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: rmt
+//
+//=============================================================================
+inline const rmt::Matrix& InteriorEntranceLocator::GetTransform() const
+{
+ return mTransform;
+}
+#endif //INTERIORENTRANCELOCATOR_H
diff --git a/game/code/meta/locator.cpp b/game/code/meta/locator.cpp
new file mode 100644
index 0000000..5f696e5
--- /dev/null
+++ b/game/code/meta/locator.cpp
@@ -0,0 +1,109 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement Locator
+//
+// History: 04/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#ifndef WORLD_BUILDER
+#include <meta/Locator.h>
+#else
+#include "Locator.h"
+#endif
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Locator::Locator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Locator::Locator() :
+ mID( ~0 ),
+ mData( 0 ),
+ mFlags( 0 ),
+ mLocation( rmt::Vector( 0.0f, 0.0f, 0.0f ) )
+{
+ SetFlag( ACTIVE, true );
+}
+
+//==============================================================================
+// Locator::~Locator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Locator::~Locator()
+{
+}
+
+//=============================================================================
+// Locator::GetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* currentLoc )
+//
+// Return: void
+//
+//=============================================================================
+void Locator::GetPosition( rmt::Vector* currentLoc )
+{
+ GetLocation( currentLoc );
+}
+
+//=============================================================================
+// Locator::GetHeading
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* heading )
+//
+// Return: void
+//
+//=============================================================================
+void Locator::GetHeading( rmt::Vector* heading )
+{
+ //Locators have no heading.
+ heading->Set( 0.0f, 0.0f, 0.0f );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/meta/locator.h b/game/code/meta/locator.h
new file mode 100644
index 0000000..a387833
--- /dev/null
+++ b/game/code/meta/locator.h
@@ -0,0 +1,233 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: locator.h
+//
+// Description: Blahblahblah
+//
+// History: 04/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef LOCATOR_H
+#define LOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/entity.hpp>
+#include <radmath/radmath.hpp>
+
+#include <presentation/gui/utility/hudmap.h>
+
+#ifndef WORLD_BUILDER
+#include <meta/locatortypes.h>
+#else
+#include "locatortypes.h"
+#endif
+
+//========================================
+// Forward References
+//========================================
+
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class Locator : public tEntity, public IHudMapIconLocator
+{
+public:
+ enum Flag
+ {
+ ACTIVE,
+ DRAWN,
+ USED,
+ INTERIOR
+ };
+
+ Locator();
+ virtual ~Locator();
+
+ void SetLocation( const rmt::Vector& loc );
+ void GetLocation( rmt::Vector* loc ) const;
+
+ void SetID( unsigned int id );
+ unsigned int GetID() const;
+
+ void SetData( unsigned int data );
+ unsigned int GetData() const;
+
+ virtual LocatorType::Type GetDataType() const; //MUST OVERRIDE THIS!
+
+ void SetFlag( Flag flag, bool on );
+ bool GetFlag( Flag flag ) const ;
+
+ virtual void SetNumTriggers( unsigned int i, int allocID ) {}; //This makes loading simpler.
+ virtual unsigned int GetNumTriggers() { return 0; }; //This makes things simpler.
+ virtual void SetMatrix( const rmt::Matrix& mat ) {}; //Again, for simplicity.
+
+ //For IHudMapIconLocator
+ virtual void GetPosition( rmt::Vector* currentLoc );
+ virtual void GetHeading( rmt::Vector* heading );
+
+ virtual bool TriggerAllowed( int playerID ) { return true; }; //This is different from the ACTIVE flag.
+
+private:
+ unsigned int mID; // Used to number-id locators.
+ unsigned int mData; // Here is the data in the locator
+ unsigned int mFlags; // Flags, we don't need no... oh wait, maybe we do.
+
+ rmt::Vector mLocation; //Where am I?
+
+ //Prevent wasteful constructor creation.
+ Locator( const Locator& locator );
+ Locator& operator=( const Locator& locator );
+};
+
+
+//******************************************************************************
+//
+// Inline Public Member Functions
+//
+//******************************************************************************
+
+
+//=============================================================================
+// Locator::SetLocation
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& loc )
+//
+// Return: void
+//
+//=============================================================================
+inline void Locator::SetLocation( const rmt::Vector& loc )
+{
+ mLocation = loc;
+}
+
+//=============================================================================
+// Locator::GetLocation
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* loc )
+//
+// Return: void
+//
+//=============================================================================
+inline void Locator::GetLocation( rmt::Vector* loc ) const
+{
+ *loc = mLocation;
+}
+
+//=============================================================================
+// Locator::SetID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int id )
+//
+// Return: void
+//
+//=============================================================================
+inline void Locator::SetID( unsigned int id )
+{
+ mID = id;
+}
+
+//=============================================================================
+// Locator::GetID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int Locator::GetID() const
+{
+ return mID;
+}
+
+//=============================================================================
+// Locator::SetData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int data )
+//
+// Return: void
+//
+//=============================================================================
+inline void Locator::SetData( unsigned int data )
+{
+ mData = data;
+}
+
+//=============================================================================
+// Locator::GetData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int Locator::GetData() const
+{
+ return mData;
+}
+
+//=============================================================================
+// Locator::SetFlag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Flag flag, bool on )
+//
+// Return: void
+//
+//=============================================================================
+inline void Locator::SetFlag( Flag flag, bool on )
+{
+ on ? mFlags |= ( 1 << flag ) : mFlags &= ~( 1 << flag );
+}
+
+//=============================================================================
+// Locator::GetFlag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Flag flag )
+//
+// Return: bool
+//
+//=============================================================================
+inline bool Locator::GetFlag( Flag flag ) const
+{
+ return ( (mFlags & ( 1 << flag )) > 0 );
+}
+
+//=============================================================================
+// ::Type GetDataType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline LocatorType::Type Locator::GetDataType() const
+{
+ return( LocatorType::GENERIC );
+}
+
+#endif //LOCATOR_H
diff --git a/game/code/meta/locatorevents.h b/game/code/meta/locatorevents.h
new file mode 100644
index 0000000..2ff1cd4
--- /dev/null
+++ b/game/code/meta/locatorevents.h
@@ -0,0 +1,232 @@
+#ifndef LOCATOR_EVENTS_H
+#define LOCATOR_EVENTS_H
+
+
+//HEY, when you add a new event, make sure to add a string for it's name too!!!
+
+namespace LocatorEvent
+{
+ enum Event
+ {
+ FLAG, //Capture the flag - Flag
+ CAMERA_CUT, //Used for static position cameras
+ CHECK_POINT, //Used by missions
+ BASE, //Capture the flag - Base
+ DEATH, //Death Zones!
+ INTERIOR_EXIT, //Leave an interior environment
+ BOUNCEPAD, // Bounce the character towards the locator.
+ //
+ // Trigger a change of ambient sound
+ //
+ AMBIENT_SOUND_CITY,
+ AMBIENT_SOUND_SEASIDE,
+ AMBIENT_SOUND_SUBURBS,
+ AMBIENT_SOUND_FOREST,
+ AMBIENT_KWIK_E_MART_ROOFTOP,
+ AMBIENT_SOUND_FARM,
+ AMBIENT_SOUND_BARN,
+ AMBIENT_SOUND_POWERPLANT_EXTERIOR,
+ AMBIENT_SOUND_POWERPLANT_INTERIOR,
+ AMBIENT_SOUND_RIVER,
+
+ AMBIENT_SOUND_CITY_BUSINESS,
+ AMBIENT_SOUND_CONSTRUCTION,
+ AMBIENT_SOUND_STADIUM,
+ AMBIENT_SOUND_STADIUM_TUNNEL,
+ AMBIENT_SOUND_EXPRESSWAY,
+ AMBIENT_SOUND_SLUM,
+ AMBIENT_SOUND_RAILYARD,
+ AMBIENT_SOUND_HOSPITAL,
+
+ AMBIENT_SOUND_LIGHT_CITY,
+ AMBIENT_SOUND_SHIPYARD,
+ AMBIENT_SOUND_QUAY,
+ AMBIENT_SOUND_LIGHTHOUSE,
+ AMBIENT_SOUND_COUNTRY_HIGHWAY,
+ AMBIENT_SOUND_KRUSTYLU,
+ AMBIENT_SOUND_DAM,
+
+ AMBIENT_SOUND_FOREST_HIGHWAY,
+ AMBIENT_SOUND_RETAINING_WALL_TUNNEL,
+ AMBIENT_SOUND_KRUSTYLU_EXTERIOR,
+ AMBIENT_SOUND_DUFF_EXTERIOR,
+ AMBIENT_SOUND_DUFF_INTERIOR,
+
+ AMBIENT_SOUND_STONE_CUTTER_TUNNEL,
+ AMBIENT_SOUND_STONE_CUTTER_HALL,
+ AMBIENT_SOUND_SEWERS,
+ AMBIENT_SOUND_BURNS_TUNNEL,
+ AMBIENT_SOUND_PP_ROOM_1,
+ AMBIENT_SOUND_PP_ROOM_2,
+ AMBIENT_SOUND_PP_ROOM_3,
+ AMBIENT_SOUND_PP_TUNNEL_1,
+ AMBIENT_SOUND_PP_TUNNEL_2,
+ AMBIENT_SOUND_MANSION_INTERIOR,
+
+ PARKED_BIRDS,
+
+ WHACKY_GRAVITY,
+
+ FAR_PLANE,
+
+ AMBIENT_SOUND_COUNTRY_NIGHT,
+ AMBIENT_SOUND_SUBURBS_NIGHT,
+ AMBIENT_SOUND_FOREST_NIGHT,
+
+ AMBIENT_SOUND_HALLOWEEN1,
+ AMBIENT_SOUND_HALLOWEEN2,
+ AMBIENT_SOUND_HALLOWEEN3,
+ AMBIENT_SOUND_PLACEHOLDER3,
+ AMBIENT_SOUND_PLACEHOLDER4,
+ AMBIENT_SOUND_PLACEHOLDER5,
+ AMBIENT_SOUND_PLACEHOLDER6,
+ AMBIENT_SOUND_PLACEHOLDER7,
+ AMBIENT_SOUND_PLACEHOLDER8,
+ AMBIENT_SOUND_PLACEHOLDER9,
+
+ GOO_DAMAGE,
+ COIN_ZONE,
+ LIGHT_CHANGE,
+ TRAP,
+
+ AMBIENT_SOUND_SEASIDE_NIGHT,
+ AMBIENT_SOUND_LIGHTHOUSE_NIGHT,
+ AMBIENT_SOUND_BREWERY_NIGHT,
+ AMBIENT_SOUND_PLACEHOLDER10,
+ AMBIENT_SOUND_PLACEHOLDER11,
+ AMBIENT_SOUND_PLACEHOLDER12,
+ AMBIENT_SOUND_PLACEHOLDER13,
+ AMBIENT_SOUND_PLACEHOLDER14,
+ AMBIENT_SOUND_PLACEHOLDER15,
+ AMBIENT_SOUND_PLACEHOLDER16,
+
+ SPECIAL, //This denotes the end of the regular events. Only the World builder
+ //Uses this, please add events before this that you want to show up as
+ //normal info-less events, all events after this are specialy controlled
+ //in the worldbuilder.
+
+ DYNAMIC_ZONE = SPECIAL, //This is used in a special locator only for dynamic loading.
+
+ OCCLUSION_ZONE,
+
+ CAR_DOOR, //This is only used to detect when the player is close enough to a car.
+ ACTION_BUTTON, //This is for Object switches
+ INTERIOR_ENTRANCE, //This is for going into interiors
+ GENERIC_BUTTON_HANDLER_EVENT,
+ FOUNTAIN_JUMP,
+ LOAD_PED_MODEL_GROUP,
+ GAG,
+
+ NUM_EVENTS
+ };
+
+ const char* const Name[] =
+ {
+ "Flag",
+ "Camera Cut",
+ "Check Point",
+ "Base",
+ "Death",
+ "Interior Exit",
+ "Bounce Pad",
+ "Ambient Sound - City",
+ "Ambient Sound - Seaside",
+ "Ambient Sound - Suburbs",
+ "Ambient Sound - Forest",
+ "Ambient Sound - KEM Rooftop",
+ "Ambient Sound - Farm",
+ "Ambient Sound - Barn",
+ "Ambient Sound - PP - Interior",
+ "Ambient Sound - PP - Exterior",
+ "Ambient Sound - River",
+
+ "Ambient Sound - Business",
+ "Ambient Sound - Construction",
+ "Ambient Sound - Stadium",
+ "Ambient Sound - Stadium Tunnel",
+ "Ambient Sound - Expressway",
+ "Ambient Sound - Slum",
+ "Ambient Sound - Railyard",
+ "Ambient Sound - Hospital",
+
+ "Ambient Sound - Light City",
+ "Ambient Sound - Shipyard",
+ "Ambient Sound - Quay",
+ "Ambient Sound - Lighthouse",
+ "Ambient Sound - Country Highway",
+ "Ambient Sound - Krustylu",
+ "Ambient Sound - Dam",
+
+ "Ambient Sound - Forest Highway",
+ "Ambient Sound - Retaining Wall",
+ "Ambient Sound - Krustylu Ext.",
+ "Ambient Sound - Duff Exterior",
+ "Ambient Sound - Duff Interior",
+
+ "Ambient Sound - Stonecutter Tunnel",
+ "Ambient Sound - stonecutter Hall",
+ "Ambient Sound - Sewers",
+ "Ambient Sound - Burns Tunnel",
+ "Ambient Sound - PP Room 1",
+ "Ambient Sound - PP Room 2",
+ "Ambient Sound - PP Room 3",
+ "Ambient Sound - PP Tunnel 1",
+ "Ambient Sound - PP Tunnel 2",
+ "Ambient Sound - Mansion Interior",
+
+ "Park Birds",
+ "Whacky Gravity",
+ "Far Plane Change",
+
+ "Ambient Sound - Country Night",
+ "Ambient Sound - Suburbs Night",
+ "Ambient Sound - Forest Night",
+
+ "Ambient Sound - Halloween1",
+ "Ambient Sound - Halloween2",
+ "Ambient Sound - Halloween3",
+ "Ambient Sound - Placeholder3",
+ "Ambient Sound - Placeholder4",
+ "Ambient Sound - Placeholder5",
+ "Ambient Sound - Placeholder6",
+ "Ambient Sound - Placeholder7",
+ "Ambient Sound - Placeholder8",
+ "Ambient Sound - Placeholder9",
+
+ "Goo Damage",
+ "Coin Zone", //Not used, just loaded.
+ "Light Change",
+ "Trap",
+
+ "Ambient Sound - Seaside Night",
+ "Ambient Sound - Lighthouse Night",
+ "Ambient Sound - Brewery Night",
+ "Ambient Sound - Placeholder10",
+ "Ambient Sound - Placeholder11",
+ "Ambient Sound - Placeholder12",
+ "Ambient Sound - Placeholder13",
+ "Ambient Sound - Placeholder14",
+ "Ambient Sound - Placeholder15",
+ "Ambient Sound - Placeholder16",
+
+ //This and below not used in any offline tool!
+ "Dynamic Zone",
+ "Occlusion Zone",
+ "Car Door",
+ "Action Button",
+ "Interior Entrance",
+ "Start Bonus Mission Dialogue",
+ "Talk to Character",
+ "Jump on Fountain",
+ "Load Pedestrian Model Group",
+ "Gag"
+ };
+
+ inline bool TestEvents()
+ {
+ return NUM_EVENTS == ( sizeof(Name) / sizeof(Name[0]) );
+ }
+}
+
+#endif
+ \ No newline at end of file
diff --git a/game/code/meta/locatortypes.h b/game/code/meta/locatortypes.h
new file mode 100644
index 0000000..c115425
--- /dev/null
+++ b/game/code/meta/locatortypes.h
@@ -0,0 +1,51 @@
+#ifndef LOCATOR_TYPES_H
+#define LOCATOR_TYPES_H
+
+//HEY, when you add a new type, make sure to add a string for it's name too!!!
+
+namespace LocatorType
+{
+ enum Type
+ {
+ EVENT,
+ SCRIPT,
+ GENERIC,
+ CAR_START,
+ SPLINE,
+ DYNAMIC_ZONE,
+ OCCLUSION,
+ INTERIOR_ENTRANCE,
+ DIRECTIONAL,
+ ACTION,
+ FOV,
+ BREAKABLE_CAMERA,
+ STATIC_CAMERA,
+ PED_GROUP,
+ COIN,
+ SPAWN_POINT,
+
+ NUM_TYPES
+ };
+
+ const char* const Name[NUM_TYPES] =
+ {
+ "Event",
+ "Script",
+ "Generic",
+ "Car Start",
+ "Spline",
+ "Dynamic Zone",
+ "Occlusion",
+ "Interior Entrance",
+ "Directional",
+ "Action",
+ "FOV",
+ "Breakable Camera",
+ "Static Camera",
+ "Ped Group",
+ "Coin",
+ "Spawn Point"
+ };
+}
+
+#endif
diff --git a/game/code/meta/occlusionlocator.cpp b/game/code/meta/occlusionlocator.cpp
new file mode 100644
index 0000000..f48af54
--- /dev/null
+++ b/game/code/meta/occlusionlocator.cpp
@@ -0,0 +1,86 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: OcclusionLocator.cpp
+//
+// Description: Implement OcclusionLocator
+//
+// History: 02/07/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <meta/OcclusionLocator.h>
+#include <meta/LocatorEvents.h>
+#include <events/EventManager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// OcclusionLocator::OcclusionLocator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+OcclusionLocator::OcclusionLocator() :
+ mNumOccTriggers( 0 )
+{
+}
+
+//==============================================================================
+// OcclusionLocator::~OcclusionLocator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+OcclusionLocator::~OcclusionLocator()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// OcclusionLocator::OnTrigger
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int playerID )
+//
+// Return: void
+//
+//=============================================================================
+void OcclusionLocator::OnTrigger( unsigned int playerID )
+{
+ GetEventManager()->TriggerEvent((EventEnum)(EVENT_LOCATOR+LocatorEvent::OCCLUSION_ZONE),this);
+}
diff --git a/game/code/meta/occlusionlocator.h b/game/code/meta/occlusionlocator.h
new file mode 100644
index 0000000..f5b47ef
--- /dev/null
+++ b/game/code/meta/occlusionlocator.h
@@ -0,0 +1,113 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: occlusionlocator.h
+//
+// Description: Blahblahblah
+//
+// History: 02/07/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef OCCLUSIONLOCATOR_H
+#define OCCLUSIONLOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <meta/locatortypes.h>
+#include <meta/triggerlocator.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class OcclusionLocator : public TriggerLocator
+{
+public:
+ OcclusionLocator();
+ virtual ~OcclusionLocator();
+
+ virtual LocatorType::Type GetDataType() const;
+
+ unsigned int GetNumOccTriggers() const;
+
+protected:
+ friend class LocatorLoader;
+ void SetNumOccTriggers( unsigned int num );
+
+private:
+ virtual void OnTrigger( unsigned int playerID );
+
+ unsigned int mNumOccTriggers;
+
+ //Prevent wasteful constructor creation.
+ OcclusionLocator( const OcclusionLocator& occlusionlocator );
+ OcclusionLocator& operator=( const OcclusionLocator& occlusionlocator );
+};
+
+//******************************************************************************
+//
+// Inline Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// OcclusionLocator::GetDataType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline LocatorType::Type OcclusionLocator::GetDataType() const
+{
+ return( LocatorType::OCCLUSION );
+}
+
+
+//=============================================================================
+// OcclusionLocator::GetNumOccTriggers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int OcclusionLocator::GetNumOccTriggers() const
+{
+ return mNumOccTriggers;
+}
+
+//******************************************************************************
+//
+// Inline Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// OcclusionLocator::SetNumOccTriggers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int num )
+//
+// Return: void
+//
+//=============================================================================
+inline void OcclusionLocator::SetNumOccTriggers( unsigned int num )
+{
+ mNumOccTriggers = num;
+}
+
+#endif //OCCLUSIONLOCATOR_H
diff --git a/game/code/meta/pedgrouplocator.h b/game/code/meta/pedgrouplocator.h
new file mode 100644
index 0000000..cf522cf
--- /dev/null
+++ b/game/code/meta/pedgrouplocator.h
@@ -0,0 +1,91 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pedgrouplocatorPedGroupLocator.h
+//
+// Description: Blahblahblah
+//
+// History: 12/4/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef PEDGROUPLOCATOR_H
+#define PEDGROUPLOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <meta/eventlocator.h>
+
+#include <radmath/radmath.hpp>
+
+#include <meta/triggerlocator.h>
+#include <meta/locatortypes.h>
+#include <meta/locatorevents.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class PedGroupLocator : public EventLocator
+{
+public:
+ PedGroupLocator() : mGroupNum( 0 ) { SetEventType(LocatorEvent::LOAD_PED_MODEL_GROUP); };
+ virtual ~PedGroupLocator() {};
+
+ void SetGroupNum( unsigned int num );
+ unsigned int GetGroupNum() const;
+
+ virtual LocatorType::Type GetDataType() const { return LocatorType::PED_GROUP; };
+
+private:
+ unsigned int mGroupNum;
+
+ //Prevent wasteful constructor creation.
+ PedGroupLocator( const PedGroupLocator& pedgrouplocator );
+ PedGroupLocator& operator=( const PedGroupLocator& pedgrouplocator );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// PedGroupLocator::SetGroupNum
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int num )
+//
+// Return: void
+//
+//=============================================================================
+inline void PedGroupLocator::SetGroupNum( unsigned int num )
+{
+ mGroupNum = num;
+}
+
+//=============================================================================
+// PedGroupLocator::GetGroupNum
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int PedGroupLocator::GetGroupNum() const
+{
+ return mGroupNum;
+}
+
+#endif //PEDGROUPLOCATOR_H
diff --git a/game/code/meta/recttriggervolume.cpp b/game/code/meta/recttriggervolume.cpp
new file mode 100644
index 0000000..d425e6e
--- /dev/null
+++ b/game/code/meta/recttriggervolume.cpp
@@ -0,0 +1,461 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement RectTriggerVolume
+//
+// History: 03/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#ifndef WORLD_BUILDER
+#include <meta/recttriggervolume.h>
+#include <memory/srrmemory.h>
+#include <debug/profiler.h>
+#else
+#define BEGIN_PROFILE(s)
+#define END_PROFILE(s)
+#include "recttriggervolume.h"
+#endif
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// RectTriggerVolume::RectTriggerVolume
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: ( const rmt::Vector& center,
+// const rmt::Vector& axisX,
+// const rmt::Vector& axisY,
+// const rmt::Vector& axisZ,
+// float extentX,
+// float extentY,
+// float extentZ )
+//
+// Return: N/A.
+//
+//==============================================================================
+RectTriggerVolume::RectTriggerVolume( const rmt::Vector& center,
+ const rmt::Vector& axisX,
+ const rmt::Vector& axisY,
+ const rmt::Vector& axisZ,
+ float extentX,
+ float extentY,
+ float extentZ ) :
+ mAxisX( axisX ),
+ mAxisY( axisY ),
+ mAxisZ( axisZ ),
+ mExtentX( extentX ),
+ mExtentY( extentY ),
+ mExtentZ( extentZ )
+{
+ SetPosition( center );
+ UpdateW2T();
+
+ mRadius = rmt::Sqrt(extentX*extentX+extentY*extentY+extentZ*extentZ);
+}
+
+//==============================================================================
+// RectTriggerVolume::~RectTriggerVolume
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RectTriggerVolume::~RectTriggerVolume()
+{
+}
+
+//=============================================================================
+// RectTriggerVolume::Contains
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& point, float epsilon )
+//
+// Return: bool
+//
+//=============================================================================
+bool RectTriggerVolume::Contains ( const rmt::Vector& point,
+ float epsilon ) const
+{
+//BEGIN_PROFILE( "Rect Contains" );
+
+ //Transform the point to rect space
+ rmt::Vector temp;
+ temp.Sub( GetPosition(), point );
+
+ temp.Transform( mWorld2Trigger );
+
+ if( (temp.x >= -mExtentX ) &&
+ (temp.x <= mExtentX ) &&
+ (temp.y >= -mExtentY ) &&
+ (temp.y <= mExtentY) &&
+ (temp.z >= -mExtentZ ) &&
+ (temp.z <= mExtentZ ) )
+ {
+//END_PROFILE( "Rect Contains" );
+ return true;
+ }
+
+//END_PROFILE( "Rect Contains" );
+ return false;
+}
+
+//=============================================================================
+// RectTriggerVolume::IntersectsBox
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& p1,
+// const rmt::Vector& p2,
+// const rmt::Vector& p3,
+// const rmt::Vector& p4 )
+//
+// Return: bool
+//
+//=============================================================================
+bool RectTriggerVolume::IntersectsBox( const rmt::Vector& p1,
+ const rmt::Vector& p2,
+ const rmt::Vector& p3,
+ const rmt::Vector& p4 ) const
+{
+ if ( Contains( p1 ) || Contains( p2 ) || Contains( p3 ) || Contains( p4 ) )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+//=============================================================================
+// RectTriggerVolume::IntersectsBox
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Box3D& box )
+//
+// Return: bool
+//
+//=============================================================================
+bool RectTriggerVolume::IntersectsBox( const rmt::Box3D& box )
+{
+ rmt::Vector p1, p2, p3, p4;
+
+ p1 = box.low;
+ p2 = box.high;
+ p3 = box.low;
+ p3.x = box.high.x;
+ p4 = box.high;
+ p4.x = box.low.x;
+
+ return IntersectsBox( p1, p2, p3, p4 );
+}
+
+
+//=============================================================================
+// RectTriggerVolume::IntersectsSphere
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& position, float radius )
+//
+// Return: bool
+//
+//=============================================================================
+bool RectTriggerVolume::IntersectsSphere( const rmt::Vector& position,
+ float radius ) const
+{
+ return Contains( position, radius );
+}
+
+//=============================================================================
+// RectTriggerVolume::IntersectLine
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& p1, const rmt::Vector& p2 )
+//
+// Return: bool
+//
+//=============================================================================
+bool RectTriggerVolume::IntersectLine( const rmt::Vector& p1, const rmt::Vector& p2 ) const
+{
+ if ( Contains( p1 ) || Contains( p2 ) )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+
+//=============================================================================
+// RectTriggerVolume::GetBoundingBox
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& p1, rmt::Vector& p2)
+//
+// Return: bool
+//
+//=============================================================================
+bool RectTriggerVolume::GetBoundingBox (rmt::Vector& p1, rmt::Vector& p2) const
+{
+ //TODO: Make this work.
+ return false;
+}
+
+//=============================================================================
+// RectTriggerVolume::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: TriggerVolume
+//
+//=============================================================================
+TriggerVolume::Type RectTriggerVolume::GetType() const
+{
+ return RECTANGLE;
+}
+//////////////////////////////////////////////////////////////////////////
+// tDrawable interface
+//////////////////////////////////////////////////////////////////////////
+//========================================================================
+// recttriggervolume::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RectTriggerVolume::GetBoundingBox(rmt::Box3D* box)
+{
+ box->low.x = GetPosition().x-mExtentX;
+ box->low.y = GetPosition().y-mExtentY;
+ box->low.z = GetPosition().z-mExtentZ;
+
+ box->high.x = GetPosition().x+mExtentX;
+ box->high.y = GetPosition().y+mExtentY;
+ box->high.z = GetPosition().z+mExtentZ;
+}
+//========================================================================
+// recttriggervolume::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RectTriggerVolume::GetBoundingSphere(rmt::Sphere* sphere)
+{
+ sphere->centre = GetPosition();
+ sphere->radius = mRadius;
+}
+
+void RectTriggerVolume::SetTransform(rmt::Matrix& m)
+{
+ mAxisX = m.Row(0);
+ mAxisY = m.Row(1);
+ mAxisZ = m.Row(2);
+ SetPosition(m.Row(3));
+
+ UpdateW2T();
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// RectTriggerVolume::UpdateW2T
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RectTriggerVolume::UpdateW2T()
+{
+ mTrigger2World.Row(0) = mAxisX;
+ mTrigger2World.Row(1) = mAxisY;
+ mTrigger2World.Row(2) = mAxisZ;
+ mTrigger2World.Row(3) = GetPosition();
+
+ mTrigger2World.m[0][3] = 0.0f;
+ mTrigger2World.m[1][3] = 0.0f;
+ mTrigger2World.m[2][3] = 0.0f;
+ mTrigger2World.m[3][3] = 0.0f;
+
+ mWorld2Trigger = mTrigger2World;
+ mWorld2Trigger.Invert(); //This is orthonormal!!!
+
+ //We only use the rot/scale...
+ mWorld2Trigger.IdentityTranslation();
+}
+
+//=============================================================================
+// RectTriggerVolume::InitPoints
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RectTriggerVolume::InitPoints()
+{
+#ifndef WORLD_BUILDER
+#ifndef RAD_RELEASE
+MEMTRACK_PUSH_GROUP( "RectTriggerVolume" );
+ numFaces = 12 * 3;
+ numVerts = 8;
+
+ verts = new(GMA_LEVEL_OTHER) pddiVector[numVerts];
+ faces = new(GMA_LEVEL_OTHER) unsigned short[numFaces];
+
+ unsigned int face = 0;
+
+ // Joel made the sphere normals inverted,
+ // so all these normals have to be inverted too
+ faces[face++] = 0;
+ faces[face++] = 1;
+ faces[face++] = 2;
+
+ faces[face++] = 2;
+ faces[face++] = 3;
+ faces[face++] = 0;
+
+ faces[face++] = 0;
+ faces[face++] = 6;
+ faces[face++] = 7;
+
+ faces[face++] = 7;
+ faces[face++] = 1;
+ faces[face++] = 0;
+
+ faces[face++] = 1;
+ faces[face++] = 7;
+ faces[face++] = 4;
+
+ faces[face++] = 4;
+ faces[face++] = 2;
+ faces[face++] = 1;
+
+ faces[face++] = 2;
+ faces[face++] = 4;
+ faces[face++] = 5;
+
+ faces[face++] = 5;
+ faces[face++] = 3;
+ faces[face++] = 2;
+
+ faces[face++] = 3;
+ faces[face++] = 5;
+ faces[face++] = 6;
+
+ faces[face++] = 6;
+ faces[face++] = 0;
+ faces[face++] = 3;
+
+ faces[face++] = 6;
+ faces[face++] = 5;
+ faces[face++] = 4;
+
+ faces[face++] = 4;
+ faces[face++] = 7;
+ faces[face++] = 6;
+
+ rAssert( static_cast< int >( face ) == numFaces );
+MEMTRACK_POP_GROUP( "RectTriggerVolume" );
+#endif
+#endif
+}
+
+void RectTriggerVolume::CalcPoints()
+{
+#ifndef RAD_RELEASE
+ rmt::Vector radius( GetExtentX(), GetExtentY(), GetExtentZ() );
+
+ verts[0].Set( -radius.x, -radius.y, -radius.z );
+ verts[4].Set( radius.x, radius.y, radius.z );
+
+ // counter-clockwise from verts[0]
+ verts[1].Set( verts[0].x, verts[0].y, verts[4].z );
+ verts[2].Set( verts[4].x, verts[0].y, verts[4].z );
+ verts[3].Set( verts[4].x, verts[0].y, verts[0].z );
+
+ // counter-clockwise from verts[4]
+ verts[5].Set( verts[4].x, verts[4].y, verts[0].z );
+ verts[6].Set( verts[0].x, verts[4].y, verts[0].z );
+ verts[7].Set( verts[0].x, verts[4].y, verts[4].z );
+
+ for( unsigned int i = 0; i < 8; i++ )
+ {
+ mTrigger2World.Transform( verts[ i ], &verts[ i ] );
+ }
+#endif
+}
+
+//==============================================================================
+// RectTriggerVolume::RectTriggerVolume
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RectTriggerVolume::RectTriggerVolume() :
+ mAxisX( rmt::Vector( 1.0f, 0, 0 ) ),
+ mAxisY( rmt::Vector( 0, 1.0f, 0 ) ),
+ mAxisZ( rmt::Vector( 0, 0, 1.0f ) ),
+ mExtentX( 1.0f ),
+ mExtentY( 1.0f ),
+ mExtentZ( 1.0f )
+{
+}
diff --git a/game/code/meta/recttriggervolume.h b/game/code/meta/recttriggervolume.h
new file mode 100644
index 0000000..d3ab841
--- /dev/null
+++ b/game/code/meta/recttriggervolume.h
@@ -0,0 +1,214 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: recttriggervolume.h
+//
+// Description: Blahblahblah
+//
+// History: 03/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef RECTTRIGGERVOLUME_H
+#define RECTTRIGGERVOLUME_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <raddebugwatch.hpp>
+#include <radmath/radmath.hpp>
+
+#ifndef WORLD_BUILDER
+#include <meta/triggervolume.h>
+#else
+#include "triggervolume.h"
+#endif
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class RectTriggerVolume : public TriggerVolume
+{
+public:
+ RectTriggerVolume( const rmt::Vector& center,
+ const rmt::Vector& axisX,
+ const rmt::Vector& axisY,
+ const rmt::Vector& axisZ,
+ float extentX,
+ float extentY,
+ float extentZ );
+ virtual ~RectTriggerVolume();
+
+
+ // Intersection information
+ bool Contains ( const rmt::Vector& point, float epsilon = 0.00f ) const;
+
+ // Intersection of bounding box with trigger volume.
+ //p1 - p4 are the four corners of the box.
+ bool IntersectsBox( const rmt::Vector& p1,
+ const rmt::Vector& p2,
+ const rmt::Vector& p3,
+ const rmt::Vector& p4 ) const;
+
+ virtual bool IntersectsBox( const rmt::Box3D& box );
+
+ bool IntersectsSphere( const rmt::Vector& position,
+ float radius ) const;
+
+ bool IntersectLine( const rmt::Vector& p1,
+ const rmt::Vector& p2 ) const;
+
+ // Bounding box
+ bool GetBoundingBox ( rmt::Vector& p1, rmt::Vector& p2 ) const;
+
+ Type GetType() const;
+
+ void SetTransform(rmt::Matrix&);
+
+ const rmt::Vector& GetAxisX() const;
+ const rmt::Vector& GetAxisY() const;
+ const rmt::Vector& GetAxisZ() const;
+
+ float GetExtentX() const;
+ void SetExtentX( float extent );
+
+ float GetExtentY() const;
+ void SetExtentY( float extent );
+
+ float GetExtentZ() const;
+ void SetExtentZ( float extent );
+
+ const rmt::Matrix& GetW2T();
+ const rmt::Matrix& GetT2W();
+
+ //////////////////////////////////////////////////////////////////////////
+ // tDrawable interface
+ //////////////////////////////////////////////////////////////////////////
+ void GetBoundingBox(rmt::Box3D* box);
+ void GetBoundingSphere(rmt::Sphere* sphere);
+protected:
+ virtual void InitPoints();
+ virtual void CalcPoints();
+
+private:
+ RectTriggerVolume();
+ bool IntersectLineRect( const rmt::Vector& lineOrig,
+ const rmt::Vector& lineDir ) const;
+
+ void UpdateW2T();
+
+ rmt::Vector mAxisX;
+ rmt::Vector mAxisY;
+ rmt::Vector mAxisZ;
+
+ float mExtentX;
+ float mExtentY;
+ float mExtentZ;
+
+ float mRadius;
+
+ rmt::Matrix mWorld2Trigger;
+ rmt::Matrix mTrigger2World;
+
+ //Prevent wasteful constructor creation.
+ RectTriggerVolume( const RectTriggerVolume& recttriggervolume );
+ RectTriggerVolume& operator=( const RectTriggerVolume& recttriggervolume );
+};
+
+//=============================================================================
+// RectTriggerVolume::SetExtentX
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float extent )
+//
+// Return: inline
+//
+//=============================================================================
+inline void RectTriggerVolume::SetExtentX( float extent )
+{
+ mExtentX = extent;
+}
+
+//=============================================================================
+// RectTriggerVolume::SetExtentY
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float extent )
+//
+// Return: inline
+//
+//=============================================================================
+inline void RectTriggerVolume::SetExtentY( float extent )
+{
+ mExtentY = extent;
+}
+
+//=============================================================================
+// RectTriggerVolume::SetExtentZ
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float extent )
+//
+// Return: inline
+//
+//=============================================================================
+inline void RectTriggerVolume::SetExtentZ( float extent )
+{
+ mExtentZ = extent;
+}
+
+//=============================================================================
+// RectTriggerVolume::GetExtentX
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline float RectTriggerVolume::GetExtentX() const
+{
+ return( mExtentX );
+}
+
+//=============================================================================
+// RectTriggerVolume::GetExtentY
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline float RectTriggerVolume::GetExtentY() const
+{
+ return( mExtentY );
+}
+
+//=============================================================================
+// RectTriggerVolume::GetExtentZ
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline float RectTriggerVolume::GetExtentZ() const
+{
+ return( mExtentZ );
+}
+
+#endif //RECTTRIGGERVOLUME_H
diff --git a/game/code/meta/scriptlocator.cpp b/game/code/meta/scriptlocator.cpp
new file mode 100644
index 0000000..74d8c03
--- /dev/null
+++ b/game/code/meta/scriptlocator.cpp
@@ -0,0 +1,89 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement ScriptLocator
+//
+// History: 04/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <meta/ScriptLocator.h>
+
+#include <events/eventmanager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ScriptLocator::ScriptLocator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ScriptLocator::ScriptLocator()
+{
+}
+
+//==============================================================================
+// ScriptLocator::~ScriptLocator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ScriptLocator::~ScriptLocator()
+{
+}
+
+//=============================================================================
+// ScriptLocator::OnTrigger
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int playerID )
+//
+// Return: void
+//
+//=============================================================================
+void ScriptLocator::OnTrigger( unsigned int playerID )
+{
+ //This will fire an event off that tell the sound system that a positional
+ //sound is to be played using the position parameters given in the
+ //the object indicated by the text key.
+ //
+ GetEventManager()->TriggerEvent( EVENT_POSITIONAL_SOUND_TRIGGER_HIT, this );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/meta/scriptlocator.h b/game/code/meta/scriptlocator.h
new file mode 100644
index 0000000..87ed24d
--- /dev/null
+++ b/game/code/meta/scriptlocator.h
@@ -0,0 +1,96 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: scriptlocator.h
+//
+// Description: Blahblahblah
+//
+// History: 04/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef SCRIPTLOCATOR_H
+#define SCRIPTLOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <meta/triggerlocator.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ScriptLocator : public TriggerLocator
+{
+public:
+ ScriptLocator();
+ virtual ~ScriptLocator();
+
+ virtual void OnTrigger( unsigned int playerID );
+ virtual LocatorType::Type GetDataType() const;
+
+ void SetString( const char* text );
+ radKey32 GetKey();
+
+private:
+
+ radKey32 m_key;
+
+ //Prevent wasteful constructor creation.
+ ScriptLocator( const ScriptLocator& scriptlocator );
+ ScriptLocator& operator=( const ScriptLocator& scriptlocator );
+};
+
+//=============================================================================
+// ScriptLocator::GetDataType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline LocatorType::Type ScriptLocator::GetDataType() const
+{
+ return( LocatorType::SCRIPT );
+}
+
+//=============================================================================
+// ScriptLocator::SetString
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* text )
+//
+// Return: void
+//
+//=============================================================================
+inline void ScriptLocator::SetString( const char* text )
+{
+ m_key = ::radMakeKey32( text );
+}
+
+//=============================================================================
+// ScriptLocator::GetKey
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: radKey32
+//
+//=============================================================================
+inline radKey32 ScriptLocator::GetKey()
+{
+ return m_key;
+}
+
+#endif //SCRIPTLOCATOR_H
diff --git a/game/code/meta/spheretriggervolume.cpp b/game/code/meta/spheretriggervolume.cpp
new file mode 100644
index 0000000..2067acc
--- /dev/null
+++ b/game/code/meta/spheretriggervolume.cpp
@@ -0,0 +1,481 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement SphereTriggerVolume
+//
+// History: 03/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#ifdef WORLD_BUILDER
+#include "main/toolhack.h"
+#endif
+
+//========================================
+// Project Includes
+//========================================
+#ifndef WORLD_BUILDER
+#include <meta/spheretriggervolume.h>
+#include <memory/srrmemory.h>
+#include <debug/profiler.h>
+#else
+#define BEGIN_PROFILE(s)
+#define END_PROFILE(s)
+#include "spheretriggervolume.h"
+#endif
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SphereTriggerVolume::SphereTriggerVolume
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SphereTriggerVolume::SphereTriggerVolume() :
+ mRadius( 0.0f )
+{
+}
+
+//==============================================================================
+// SphereTriggerVolume::SphereTriggerVolume
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: ( const rmt::Vector& centre, float radius )
+//
+// Return: N/A.
+//
+//==============================================================================
+SphereTriggerVolume::SphereTriggerVolume( const rmt::Vector& centre,
+ float radius ) :
+ mRadius( radius )
+{
+ SetPosition( centre );
+}
+
+//==============================================================================
+// SphereTriggerVolume::~SphereTriggerVolume
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SphereTriggerVolume::~SphereTriggerVolume()
+{
+}
+
+//=============================================================================
+// SphereTriggerVolume::Contains
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& point, float epsilon )
+//
+// Return: bool
+//
+//=============================================================================
+bool SphereTriggerVolume::Contains( const rmt::Vector& point,
+ float epsilon ) const
+{
+//BEGIN_PROFILE( "Sphere Contains" );
+
+ register float dist_sq;
+ rmt::Vector diff;
+ diff.Sub( point, GetPosition() );
+ dist_sq = diff.MagnitudeSqr();
+
+//END_PROFILE( "Sphere Contains" );
+
+ return( dist_sq < mRadius * mRadius );
+}
+
+//=============================================================================
+// SphereTriggerVolume::IntersectsBox
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& p1,
+// const rmt::Vector& p2,
+// const rmt::Vector& p3,
+// const rmt::Vector& p4 )
+//
+// Return: bool
+//
+//=============================================================================
+bool SphereTriggerVolume::IntersectsBox( const rmt::Vector& p1,
+ const rmt::Vector& p2,
+ const rmt::Vector& p3,
+ const rmt::Vector& p4 ) const
+{
+
+ return ( Contains( p1 ) || Contains( p2 ) || Contains( p3 ) || Contains( p4 ) );
+
+/*
+ //Build line segments from the box.
+ rmt::Vector l1Dir;
+ rmt::Vector l2Dir;
+ rmt::Vector l3Dir;
+ rmt::Vector l4Dir;
+
+ //This makes the vector directions of the lines making the bounding box.
+ l1Dir.Sub(p2, p1);
+ l2Dir.Sub(p3, p2);
+ l3Dir.Sub(p4, p3);
+ l4Dir.Sub(p1, p4);
+
+ if ( (IntersectLineSphere(p1, l1Dir)) ||
+ (IntersectLineSphere(p2, l2Dir)) ||
+ (IntersectLineSphere(p3, l3Dir)) ||
+ (IntersectLineSphere(p4, l4Dir)) )
+ {
+ return true;
+ }
+
+ return false;
+*/
+}
+
+//=============================================================================
+// SphereTriggerVolume::IntersectsBox
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Box3D& box )
+//
+// Return: bool
+//
+//=============================================================================
+bool SphereTriggerVolume::IntersectsBox( const rmt::Box3D& box )
+{
+ rmt::Sphere thisAsSphere;
+ GetBoundingSphere( &thisAsSphere );
+ return box.Intersects( thisAsSphere );
+}
+
+//=============================================================================
+// SphereTriggerVolume::IntersectLine
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& p1, const rmt::Vector& p2 )
+//
+// Return: bool
+//
+//=============================================================================
+bool SphereTriggerVolume::IntersectLine( const rmt::Vector& p1, const rmt::Vector& p2 ) const
+{
+ rmt::Vector l1Dir;
+
+ //This makes the vector directions of the lines making the bounding box.
+ l1Dir.Sub(p2, p1);
+
+ if ( IntersectLineSphere(p1, l1Dir) )
+ {
+ return true;
+ }
+
+ return false;
+}
+
+//=============================================================================
+// SphereTriggerVolume::IntersectsSphere
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& position, float radius )
+//
+// Return: bool
+//
+//=============================================================================
+bool SphereTriggerVolume::IntersectsSphere( const rmt::Vector& position, float radius ) const
+{
+ register float dist_sq;
+ rmt::Vector diff;
+ diff.Sub( position, GetPosition() );
+ dist_sq = diff.MagnitudeSqr();
+
+ return( dist_sq <= (mRadius + radius) * (mRadius + radius) );
+}
+
+//=============================================================================
+// SphereTriggerVolume::GetBoundingBox
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (const rmt::Vector& p1, rmt::Vector& p2)
+//
+// Return: bool
+//
+//=============================================================================
+bool SphereTriggerVolume::GetBoundingBox(rmt::Vector& p1, rmt::Vector& p2) const
+{
+ rmt::Vector center = GetPosition();
+
+ p1 = rmt::Vector (center.x - mRadius,
+ center.y - mRadius,
+ center.z - mRadius);
+ p2 = rmt::Vector (center.x + mRadius,
+ center.y + mRadius,
+ center.z + mRadius);
+ return true;
+}
+
+//=============================================================================
+// SphereTriggerVolume::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: TriggerVolume
+//
+//=============================================================================
+TriggerVolume::Type SphereTriggerVolume::GetType() const
+{
+ return SPHERE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// tDrawable interface
+//////////////////////////////////////////////////////////////////////////
+//========================================================================
+// spheretriggervolume::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void SphereTriggerVolume::GetBoundingBox(rmt::Box3D* box)
+{
+ GetBoundingBox(box->low, box->high);
+}
+//========================================================================
+// spheretriggervolume::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void SphereTriggerVolume::GetBoundingSphere(rmt::Sphere* sphere)
+{
+ sphere->centre = GetPosition();
+ sphere->radius = mRadius;
+}
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// SphereTriggerVolume::IntersectLineSphere
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (const rmt::Vector lOrig, const rmt::Vector lDir)
+//
+// Return: bool
+//
+//=============================================================================
+bool SphereTriggerVolume::IntersectLineSphere( const rmt::Vector& lOrig,
+ const rmt::Vector& lDir ) const
+{
+ rmt::Vector lineOrig = lOrig;
+ rmt::Vector lineDir = lDir;
+
+ rmt::Vector kDiff, spherePos;
+
+ spherePos = GetPosition();
+
+ kDiff.Sub(spherePos, lineOrig);
+ float fSqrLen = lineDir.MagnitudeSqr();
+ float fT = (kDiff.DotProduct(lineDir)) / fSqrLen;
+
+ rmt::Vector closestSegPt;
+ if( fT > 1.0f )
+ {
+ closestSegPt = lOrig + lDir;
+ }
+ else if( fT < 0.0f )
+ {
+ closestSegPt = lOrig;
+ }
+ else
+ {
+ closestSegPt = lOrig + lDir * fT;
+ }
+
+ kDiff.Sub( closestSegPt, spherePos );
+
+ return kDiff.MagnitudeSqr() <= (mRadius*mRadius);
+}
+
+//=============================================================================
+// SphereTriggerVolume::InitPoints
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+static const int numLong = 12;
+static const int numLat = 8;
+
+void SphereTriggerVolume::InitPoints()
+{
+#ifndef WORLD_BUILDER
+#ifndef RAD_RELEASE
+MEMTRACK_PUSH_GROUP( "SphereTriggerVolume" );
+ // === Initialize ===
+ numVerts = numLong * (numLat - 1) + 2;
+ numFaces = ((numLong * 2) + (numLong * (numLat-2) * 2)) * 3;
+
+ verts = new(GMA_LEVEL_OTHER) pddiVector[numVerts];
+ faces = new(GMA_LEVEL_OTHER) unsigned short[numFaces];
+
+ // === Faces ===
+ int curVert, curFace;
+
+ // Faces adjacent to north pole
+ curFace = 0;
+ for (curVert = 1; curVert <= numLong; curVert++)
+ {
+ faces[curFace++] = 0;
+ faces[curFace++] = curVert;
+ if (curVert == numLong)
+ faces[curFace++] = 1;
+ else
+ faces[curFace++] = curVert+1;
+ }
+
+ // Faces not adjacent to poles
+ int y;
+
+ for (y = 0; y < numLat-2; y++)
+ {
+ for (curVert = y * numLong + 1;
+ curVert < ((y+1) * numLong + 1);
+ curVert++)
+ {
+ bool boundary = (curVert == ((y+1) * numLong));
+ int idxNW, idxNE, idxSW, idxSE;
+ idxNW = curVert;
+ idxNE = curVert + 1;
+ idxSW = curVert + numLong;
+ idxSE = curVert + numLong + 1;
+ if (boundary)
+ {
+ idxNE -= numLong;
+ idxSE -= numLong;
+ }
+
+ faces[curFace++] = idxNW;
+ faces[curFace++] = idxSW;
+ faces[curFace++] = idxSE;
+
+ faces[curFace++] = idxNW;
+ faces[curFace++] = idxSE;
+ faces[curFace++] = idxNE;
+ }
+ }
+
+ // Faces adjacent to south pole
+ for (curVert = numVerts-numLong-1; curVert <= numVerts-2; curVert++)
+ {
+ faces[curFace++] = numVerts-1;
+ if (curVert == numVerts-2)
+ faces[curFace++] = numVerts-numLong-1;
+ else
+ faces[curFace++] = curVert+1;
+ faces[curFace++] = curVert;
+ }
+
+ rAssert (curFace == numFaces);
+ for (int i = 0; i < numFaces; i++)
+ {
+ rAssert (faces[i] < numVerts);
+ }
+
+ CalcPoints();
+MEMTRACK_POP_GROUP( "SphereTriggerVolume" );
+#endif
+#endif
+}
+
+void SphereTriggerVolume::CalcPoints()
+{
+#ifndef RAD_RELEASE
+ rmt::Vector centre;
+ float radius;
+ centre = GetPosition ();
+ radius = GetSphereRadius ();
+
+ // === Vertices ===
+ // Poles
+ verts[0].Set (centre.x, centre.y + radius, centre.z);
+ verts[numVerts-1].Set (centre.x, centre.y - radius, centre.z);
+
+ // Other points
+ int curIdx = 1;
+
+ int y = 0;
+
+ for (y = 1; y < numLat; y++)
+ {
+ float angLat = ((float)y / (float)numLat) * rmt::PI;
+ for (int r = 0; r < numLong; r++)
+ {
+ float angLong = ((float)r / (float)numLong) * rmt::PI_2;
+ rmt::Vector point ((float)(rmt::Cos(angLong)*rmt::Sin(angLat)), (float)rmt::Cos(angLat), (float)(rmt::Sin(angLong)*rmt::Sin(angLat)));
+ point.Scale (radius);
+ point.Add (centre);
+
+ verts[curIdx++].Set (point.x, point.y, point.z);
+
+ }
+ }
+ rAssert (curIdx == numVerts-1);
+
+#endif
+}
diff --git a/game/code/meta/spheretriggervolume.h b/game/code/meta/spheretriggervolume.h
new file mode 100644
index 0000000..ec643cc
--- /dev/null
+++ b/game/code/meta/spheretriggervolume.h
@@ -0,0 +1,103 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: spheretriggervolume.h
+//
+// Description: Sphere Trigger Volume class
+//
+// History: 03/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef SPHERETRIGGERVOLUME_H
+#define SPHERETRIGGERVOLUME_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+#ifndef WORLD_BUILDER
+#include <meta/triggervolume.h>
+#else
+#include "triggervolume.h"
+#endif
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class SphereTriggerVolume : public TriggerVolume
+{
+public:
+ SphereTriggerVolume();
+ SphereTriggerVolume( const rmt::Vector& centre, float radius );
+ virtual ~SphereTriggerVolume();
+
+ // Intersection information
+ bool Contains( const rmt::Vector& point, float epsilon = 0.00f ) const;
+
+ // Intersection of bounding box with trigger volume.
+ //p1 - p4 are the four corners of the box.
+ bool IntersectsBox( const rmt::Vector& p1,
+ const rmt::Vector& p2,
+ const rmt::Vector& p3,
+ const rmt::Vector& p4 ) const;
+
+ virtual bool IntersectsBox( const rmt::Box3D& box );
+
+ bool IntersectsSphere( const rmt::Vector& position,
+ float radius ) const;
+
+ bool IntersectLine( const rmt::Vector& p1,
+ const rmt::Vector& p2 ) const;
+
+
+ // Bounding box
+ bool GetBoundingBox( rmt::Vector& p1, rmt::Vector& p2 ) const;
+
+ float GetSphereRadius() const;
+ void SetSphereRadius( float radius );
+
+ virtual Type GetType() const;
+
+ //////////////////////////////////////////////////////////////////////////
+ // tDrawable interface
+ //////////////////////////////////////////////////////////////////////////
+ void GetBoundingBox(rmt::Box3D* box);
+ void GetBoundingSphere(rmt::Sphere* sphere);
+
+
+protected:
+ virtual void InitPoints();
+ virtual void CalcPoints();
+
+private:
+ bool IntersectLineSphere( const rmt::Vector& lineOrig,
+ const rmt::Vector& lineDir ) const;
+
+ // For sphere type
+ float mRadius;
+
+ //Prevent wasteful constructor creation.
+ SphereTriggerVolume( const SphereTriggerVolume& spheretriggervolume );
+ SphereTriggerVolume& operator=( const SphereTriggerVolume& spheretriggervolume );
+};
+
+inline float SphereTriggerVolume::GetSphereRadius() const
+{
+ return( mRadius );
+}
+
+inline void SphereTriggerVolume::SetSphereRadius( float radius )
+{
+ mRadius = radius;
+}
+
+#endif //SPHERETRIGGERVOLUME_H
diff --git a/game/code/meta/splinelocator.cpp b/game/code/meta/splinelocator.cpp
new file mode 100644
index 0000000..fa9671e
--- /dev/null
+++ b/game/code/meta/splinelocator.cpp
@@ -0,0 +1,260 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement SplineLocator
+//
+// History: 05/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <meta/splinelocator.h>
+
+#include <camera/supercam.h>
+#include <camera/supercammanager.h>
+#include <camera/railcam.h>
+#include <camera/isupercamtarget.h>
+
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+
+#include <interiors/interiormanager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+extern const float MIN_SPEED_FOR_CAM_TRIGGER;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SplineLocator::SplineLocator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SplineLocator::SplineLocator() :
+ mRailCam( NULL ),
+ mRailNum( -1 ),
+ mTriggerCount( 0 ),
+ mLastSuperCam( NULL ),
+ mCarOnly( false ),
+ mOnFootOnly( false ),
+ mCutInOut( false ),
+ mReset( false )
+{
+}
+
+//==============================================================================
+// SplineLocator::~SplineLocator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SplineLocator::~SplineLocator()
+{
+ if ( mRailCam )
+ {
+ if ( mRailCam->IsRegistered() )
+ {
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC( 0 );
+ scc->UnregisterSuperCam( mRailCam );
+ mRailNum = -1;
+ }
+
+ tRefCounted::Release(mRailCam);
+ }
+
+ tRefCounted::Release(mLastSuperCam);
+
+}
+
+//=============================================================================
+// SplineLocator::SetRailCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( RailCam* railCam )
+//
+// Return: inline
+//
+//=============================================================================
+void SplineLocator::SetRailCam( RailCam* railCam )
+{
+ rAssert( mRailCam == NULL );
+ mRailCam = railCam;
+ mRailCam->AddRef();
+}
+
+//=============================================================================
+// SplineLocator::TriggerAllowed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID )
+//
+// Return: bool
+//
+//=============================================================================
+bool SplineLocator::TriggerAllowed( int playerID )
+{
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer( playerID );
+ float speed = playerAvatar->GetSpeedMps();
+ bool isInCar = playerAvatar->IsInCar();
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC( playerID );
+
+ if ( ( mCarOnly && ( !isInCar || speed < MIN_SPEED_FOR_CAM_TRIGGER ) ) ||
+ ( mOnFootOnly && isInCar ) ||
+ ( !mOnFootOnly && !scc->JumpCamsEnabled() ) ||
+ !scc->AllowAutoCameraChange() )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// SplineLocator::OnTrigger
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int playerID )
+//
+// Return: void
+//
+//=============================================================================
+void SplineLocator::OnTrigger( unsigned int playerID )
+{
+ rAssert(playerID == 0); // shouldn' be multiple players right now, and if there were, the shutdown logic would break
+
+BEGIN_PROFILE( "SL OnTrigger" );
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC( playerID );
+
+ //Don't trigger on enter if the conditions are not met. Allow the on exit trigger though in any case.
+
+ unsigned int transitionTime = 1000;
+ int extraFlags = 0;
+
+ InteriorManager* im = GetInteriorManager();
+
+ if ( mCutInOut || scc->GetTarget()->IsCar() ||
+ im->IsEntering() || im->IsExiting() )
+ {
+ transitionTime = 0;
+ extraFlags = SuperCamCentral::CUT;
+ }
+
+ if ( GetSuperCamManager()->GetSCC( playerID )->IsInitialCamera())
+ {
+ transitionTime = 0;
+ extraFlags = SuperCamCentral::CUT;
+ GetSuperCamManager()->GetSCC( playerID )->SetIsInitialCamera(false);
+ }
+
+ if( GetPlayerEntered() )
+ {
+ if ( mTriggerCount == 0 )
+ {
+ //Add only once.
+ mRailNum = scc->RegisterSuperCam( mRailCam );
+ }
+
+ if ( mReset )
+ {
+ mRailCam->SetReset( mReset );
+ }
+
+ //Let's not go back to ourself if these are the same.
+ if ( scc->GetActiveSuperCam() != mRailCam )
+ {
+ //Get the last cam
+ tRefCounted::Assign( mLastSuperCam, scc->GetActiveSuperCam() );
+
+ if ( mLastSuperCam->GetType() == SuperCam::RAIL_CAM ||
+ mLastSuperCam->GetType() == SuperCam::STATIC_CAM )
+ {
+ //Going from rail to rail
+ scc->SelectSuperCam( mRailNum, SuperCamCentral::QUICK | SuperCamCentral::FORCE | extraFlags, transitionTime );
+ }
+ else
+ {
+ scc->SelectSuperCam( mRailNum, SuperCamCentral::FORCE | extraFlags, transitionTime );
+ }
+ }
+
+ ++mTriggerCount;
+ }
+ else
+ {
+ if ( mTriggerCount > 0 )
+ {
+ mTriggerCount--;
+
+ if ( mTriggerCount == 0 )
+ {
+ bool isActive = scc->GetActiveSuperCam() == mRailCam;
+
+ if ( isActive )
+ {
+ //Test to make sure the last cam we knew of is still registered
+ //with the super cam central
+ if ( mLastSuperCam && mLastSuperCam->IsRegistered() )
+ {
+ if ( mLastSuperCam != NULL &&
+ ( mLastSuperCam->GetType() == SuperCam::RAIL_CAM ||
+ mLastSuperCam->GetType() == SuperCam::STATIC_CAM ) )
+ {
+ //Going from rail to rail
+ scc->SelectSuperCam( mLastSuperCam, SuperCamCentral::QUICK | SuperCamCentral::FORCE | extraFlags, transitionTime );
+ }
+ }
+ }
+
+ if ( mCutInOut || scc->GetTarget()->IsCar() )
+ {
+ scc->DoCameraCut();
+ }
+
+ tRefCounted::Release(mLastSuperCam);
+ mLastSuperCam = NULL;
+ scc->UnregisterSuperCam( mRailNum );
+
+ mRailNum = -1;
+ }
+ }
+ }
+END_PROFILE( "SL OnTrigger" );
+}
diff --git a/game/code/meta/splinelocator.h b/game/code/meta/splinelocator.h
new file mode 100644
index 0000000..6dc7082
--- /dev/null
+++ b/game/code/meta/splinelocator.h
@@ -0,0 +1,168 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: splinelocator.h
+//
+// Description: Blahblahblah
+//
+// History: 05/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef SPLINELOCATOR_H
+#define SPLINELOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <meta/triggerlocator.h>
+
+//========================================
+// Forward References
+//========================================
+
+class RailCam;
+class SuperCam;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class SplineLocator : public TriggerLocator
+{
+public:
+ SplineLocator();
+ virtual ~SplineLocator();
+
+ virtual LocatorType::Type GetDataType() const;
+
+ RailCam* GetRailCam() const;
+ void SetRailCam( RailCam* spline );
+ void SetCarOnly( bool carOnly );
+ void SetOnFootOnly( bool onFootOnly );
+ void SetCutInOut( bool cut );
+ void SetReset( bool reset );
+
+ bool TriggerAllowed( int playerID );
+
+private:
+ RailCam* mRailCam;
+ int mRailNum;
+ unsigned int mTriggerCount;
+ SuperCam* mLastSuperCam;
+ bool mCarOnly;
+ bool mOnFootOnly;
+ bool mCutInOut;
+ bool mReset;
+
+ virtual void OnTrigger( unsigned int playerID );
+
+ //Prevent wasteful constructor creation.
+ SplineLocator( const SplineLocator& splinelocator );
+ SplineLocator& operator=( const SplineLocator& splinelocator );
+};
+
+//******************************************************************************
+//
+// Inline Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// SplineLocator::GetDataType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline LocatorType::Type SplineLocator::GetDataType() const
+{
+ return( LocatorType::SPLINE );
+}
+//=============================================================================
+// SplineLocator::GetRailCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline RailCam* SplineLocator::GetRailCam() const
+{
+ return mRailCam;
+}
+
+//=============================================================================
+// SplineLocator::SetCarOnly
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool carOnly )
+//
+// Return: void
+//
+//=============================================================================
+inline void SplineLocator::SetCarOnly( bool carOnly )
+{
+ mCarOnly = carOnly;
+}
+
+//=============================================================================
+// SplineLocator::SetOnFootOnly
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool onFootOnly )
+//
+// Return: void
+//
+//=============================================================================
+inline void SplineLocator::SetOnFootOnly( bool onFootOnly )
+{
+ if ( onFootOnly == true )
+ {
+ rAssert( mCarOnly != true );
+ }
+
+ mOnFootOnly = onFootOnly;
+}
+
+//=============================================================================
+// SplineLocator::SetCutInOut
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool cut )
+//
+// Return: void
+//
+//=============================================================================
+inline void SplineLocator::SetCutInOut( bool cut )
+{
+ mCutInOut = cut;
+}
+
+//=============================================================================
+// SplineLocator::SetReset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool reset )
+//
+// Return: void
+//
+//=============================================================================
+inline void SplineLocator::SetReset( bool reset )
+{
+ mReset = reset;
+}
+
+#endif //SPLINELOCATOR_H
diff --git a/game/code/meta/staticcamlocator.cpp b/game/code/meta/staticcamlocator.cpp
new file mode 100644
index 0000000..6aedbff
--- /dev/null
+++ b/game/code/meta/staticcamlocator.cpp
@@ -0,0 +1,300 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: StaticCamLocator.cpp
+//
+// Description: Implement StaticCamLocator
+//
+// History: 9/18/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <meta/StaticCamLocator.h>
+
+#include <camera/staticcam.h>
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/isupercamtarget.h>
+
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+
+#include <interiors/interiormanager.h>
+
+const tUID hypecam = tEntity::MakeUID("hypecam");
+const tUID ufocam = tEntity::MakeUID("z3_ufocam");
+
+//AAAAARRRRRGH, this code won't work for 2 players....
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+extern const float MIN_SPEED_FOR_CAM_TRIGGER = 20.0f;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// StaticCamLocator::StaticCamLocator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+StaticCamLocator::StaticCamLocator() :
+ mStaticCam( NULL ),
+ mCamNum( -1 ),
+ mTriggerCount( 0 ),
+ mLastSuperCam( NULL ),
+ mOneShot( false ),
+ mOneShotted( false ),
+ mCarOnly( false ),
+ mOnFootOnly( false ),
+ mCutInOut( false )
+{
+}
+
+//==============================================================================
+// StaticCamLocator::~StaticCamLocator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+StaticCamLocator::~StaticCamLocator()
+{
+ if ( hypecam == GetUID() )
+ {
+ GetSuperCamManager()->GetSCC( 0 )->NastyHypeCamHackEnable( false );
+ }
+
+ if ( mStaticCam )
+ {
+ if ( mStaticCam->IsRegistered() )
+ {
+ GetSuperCamManager()->GetSCC( 0 )->UnregisterSuperCam( mStaticCam );
+ mCamNum = -1;
+ }
+
+ mStaticCam->Release();
+ mStaticCam = NULL;
+ }
+
+ if ( mLastSuperCam )
+ {
+ mLastSuperCam->Release();
+ mLastSuperCam = NULL;
+ }
+}
+
+//=============================================================================
+// StaticCamLocator::SetRailCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( StaticCam* railCam )
+//
+// Return: void
+//
+//=============================================================================
+void StaticCamLocator::SetStaticCam( StaticCam* cam )
+{
+ rAssert( mStaticCam == NULL );
+ mStaticCam = cam;
+ mStaticCam->AddRef();
+}
+
+//=============================================================================
+// StaticCamLocator::TriggerAllowed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID )
+//
+// Return: bool
+//
+//=============================================================================
+bool StaticCamLocator::TriggerAllowed( int playerID )
+{
+ //Always allow the triggering of the UFO cam
+ if ( GetUID() == ufocam )
+ {
+ return true;
+ }
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer( playerID );
+ float speed = playerAvatar->GetSpeedMps();
+ bool isInCar = playerAvatar->IsInCar();
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC( playerID );
+
+ if ( ( mCarOnly && ( !isInCar || speed < MIN_SPEED_FOR_CAM_TRIGGER ) ) ||
+ ( mOnFootOnly && isInCar ) ||
+ ( !mOnFootOnly && !scc->JumpCamsEnabled() ) ||
+ !scc->AllowAutoCameraChange() )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// StaticCamLocator::OnTrigger
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int playerID )
+//
+// Return: void
+//
+//=============================================================================
+void StaticCamLocator::OnTrigger( unsigned int playerID )
+{
+BEGIN_PROFILE( "SCL OnTrigger" );
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC( playerID );
+
+ //Test the one shottedness of this trigger.
+ if ( mOneShot )
+ {
+ if ( mOneShotted )
+ {
+ END_PROFILE( "SCL OnTrigger" );
+ return;
+ }
+ else if ( !GetPlayerEntered() ) //Only set this on leaving the volume
+ {
+ mOneShotted = true;
+ }
+ }
+
+ unsigned int transitionTime = 1000;
+ int extraFlags = 0;
+
+ InteriorManager* im = GetInteriorManager();
+
+ if ( mCutInOut || scc->GetTarget()->IsCar() ||
+ im->IsEntering() || im->IsExiting() )
+ {
+ transitionTime = 0;
+ extraFlags = SuperCamCentral::CUT;
+ }
+
+ if ( GetSuperCamManager()->GetSCC( playerID )->IsInitialCamera())
+ {
+ transitionTime = 0;
+ extraFlags = SuperCamCentral::CUT;
+ GetSuperCamManager()->GetSCC( playerID )->SetIsInitialCamera(false);
+ }
+
+
+ if( GetPlayerEntered() )
+ {
+ if ( mTriggerCount == 0 )
+ {
+ //Add only once.
+ mCamNum = scc->RegisterSuperCam( mStaticCam );
+ }
+
+ //Let's not go back to ourself if these are the same.
+ if ( scc->GetActiveSuperCam() != mStaticCam )
+ {
+ //Get the last cam
+ tRefCounted::Assign( mLastSuperCam, scc->GetActiveSuperCam() );
+
+ if ( mLastSuperCam->GetType() == SuperCam::RAIL_CAM ||
+ mLastSuperCam->GetType() == SuperCam::STATIC_CAM )
+ {
+ //Going from rail to rail
+ scc->SelectSuperCam( mCamNum, SuperCamCentral::QUICK | SuperCamCentral::FORCE | extraFlags, transitionTime );
+ }
+ else
+ {
+ scc->SelectSuperCam( mCamNum, SuperCamCentral::FORCE | extraFlags, transitionTime );
+ }
+ }
+
+ //This is for the frickin' camera on level 6 where the user should never
+ //go, but can and will enevitably fuck the camera system.
+ if ( hypecam == this->GetUID() )
+ {
+ scc->NastyHypeCamHackEnable( true );
+ }
+
+ ++mTriggerCount;
+ }
+ else
+ {
+ if ( mTriggerCount > 0 )
+ {
+ mTriggerCount--;
+
+ if ( mTriggerCount == 0 )
+ {
+ //This is for the frickin' camera on level 6 where the user should never
+ //go, but can and will enevitably fuck the camera system.
+ if ( hypecam == this->GetUID() )
+ {
+ scc->NastyHypeCamHackEnable( false );
+ }
+
+ bool isActive = scc->GetActiveSuperCam() == mStaticCam;
+
+ if ( isActive )
+ {
+ //Test to make sure the last cam we knew of is still registered
+ //with the super cam central
+ if ( mLastSuperCam && mLastSuperCam->IsRegistered() )
+ {
+ if ( mLastSuperCam != NULL &&
+ ( mLastSuperCam->GetType() == SuperCam::RAIL_CAM ||
+ mLastSuperCam->GetType() == SuperCam::STATIC_CAM ) )
+ {
+ //Going from rail to rail
+ scc->SelectSuperCam( mLastSuperCam, SuperCamCentral::QUICK | SuperCamCentral::FORCE | extraFlags, transitionTime );
+ }
+ }
+ }
+
+ if ( mCutInOut || scc->GetTarget()->IsCar() )
+ {
+ scc->DoCameraCut();
+ }
+
+ tRefCounted::Release(mLastSuperCam);
+ mLastSuperCam = NULL;
+ scc->UnregisterSuperCam( mCamNum );
+
+ mCamNum = -1;
+ }
+ }
+ }
+END_PROFILE( "SCL OnTrigger" );
+}
+
diff --git a/game/code/meta/staticcamlocator.h b/game/code/meta/staticcamlocator.h
new file mode 100644
index 0000000..c703800
--- /dev/null
+++ b/game/code/meta/staticcamlocator.h
@@ -0,0 +1,170 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: staticcamlocator.h
+//
+// Description: Blahblahblah
+//
+// History: 9/18/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef STATICCAMLOCATOR_H
+#define STATICCAMLOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <meta/triggerlocator.h>
+
+//========================================
+// Forward References
+//========================================
+
+class StaticCam;
+class SuperCam;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class StaticCamLocator : public TriggerLocator
+{
+public:
+ StaticCamLocator();
+ virtual ~StaticCamLocator();
+
+ virtual LocatorType::Type GetDataType() const;
+
+ StaticCam* GetStaticCam() const;
+ void SetStaticCam( StaticCam* cam );
+ void SetOneShot( bool oneShot );
+ void SetCarOnly( bool carOnly );
+ void SetOnFootOnly( bool onFootOnly );
+ void SetCutInOut( bool cut );
+
+ bool TriggerAllowed( int playerID );
+
+private:
+ StaticCam* mStaticCam;
+ int mCamNum;
+ unsigned int mTriggerCount;
+ SuperCam* mLastSuperCam;
+ bool mOneShot;
+ bool mOneShotted; //Haha.
+ bool mCarOnly;
+ bool mOnFootOnly;
+ bool mCutInOut;
+
+ virtual void OnTrigger( unsigned int playerID );
+
+ //Prevent wasteful constructor creation.
+ StaticCamLocator( const StaticCamLocator& staticcamlocator );
+ StaticCamLocator& operator=( const StaticCamLocator& staticcamlocator );
+};
+
+//******************************************************************************
+//
+// Inline Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// StaticCamLocator::GetDataType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline LocatorType::Type StaticCamLocator::GetDataType() const
+{
+ return( LocatorType::STATIC_CAMERA );
+}
+
+//=============================================================================
+// StaticCamLocator::GetRailCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: StaticCamera
+//
+//=============================================================================
+inline StaticCam* StaticCamLocator::GetStaticCam() const
+{
+ return mStaticCam;
+}
+
+//=============================================================================
+// StaticCamLocator::SetOneShot
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool oneShot )
+//
+// Return: void
+//
+//=============================================================================
+inline void StaticCamLocator::SetOneShot( bool oneShot )
+{
+ mOneShot = oneShot;
+}
+
+//=============================================================================
+// StaticCamLocator::SetCarOnly
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool carOnly )
+//
+// Return: void
+//
+//=============================================================================
+inline void StaticCamLocator::SetCarOnly( bool carOnly )
+{
+ mCarOnly = carOnly;
+}
+
+//=============================================================================
+// StaticCamLocator::SetOnFootOnly
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool onFootOnly )
+//
+// Return: void
+//
+//=============================================================================
+inline void StaticCamLocator::SetOnFootOnly( bool onFootOnly )
+{
+ if ( onFootOnly == true )
+ {
+ rAssert( mCarOnly != true );
+ }
+
+ mOnFootOnly = onFootOnly;
+}
+
+//=============================================================================
+// StaticCamLocator::SetCutInOut
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool cut )
+//
+// Return: void
+//
+//=============================================================================
+inline void StaticCamLocator::SetCutInOut( bool cut )
+{
+ mCutInOut = cut;
+}
+
+
+#endif //STATICCAMLOCATOR_H
diff --git a/game/code/meta/triggerlocator.cpp b/game/code/meta/triggerlocator.cpp
new file mode 100644
index 0000000..30f8fe3
--- /dev/null
+++ b/game/code/meta/triggerlocator.cpp
@@ -0,0 +1,203 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement TriggerLocator
+//
+// History: 04/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#ifndef WORLD_BUILDER
+#include <meta/TriggerLocator.h>
+#include <meta/TriggerVolume.h>
+#include <meta/triggervolumetracker.h>
+#include <memory/srrmemory.h>
+#else
+#include "TriggerLocator.h"
+#include "TriggerVolume.h"
+#include "triggervolumetracker.h"
+#define new(s) new
+#endif
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// TriggerLocator::TriggerLocator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TriggerLocator::TriggerLocator() :
+ mTriggerVolumes( NULL ),
+ mNumTriggers( 0 ),
+ mMaxNumTriggers( 0 ),
+ mPlayerEntered( false ),
+ mPlayerID( -1 )
+{
+}
+
+//==============================================================================
+// TriggerLocator::~TriggerLocator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TriggerLocator::~TriggerLocator()
+{
+ unsigned int i;
+
+ for ( i = 0; i < mMaxNumTriggers; ++i )
+ {
+ if ( mTriggerVolumes[ i ] != NULL )
+ {
+ //Chuck We should check if the Trig Track even exists
+ // since it maybe destroyed when we are clearing the inventory of these things.
+ if(GetTriggerVolumeTracker() != NULL)
+ {
+ GetTriggerVolumeTracker( )->RemoveTrigger( mTriggerVolumes[ i ] );
+ }
+ mTriggerVolumes[ i ]->Release( );
+ mTriggerVolumes[ i ] = NULL;
+ }
+ }
+
+ delete[] mTriggerVolumes;
+ mTriggerVolumes = NULL;
+}
+
+//=============================================================================
+// TriggerLocator::SetNumTriggers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int num, int allocID )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerLocator::SetNumTriggers( unsigned int num, int allocID )
+{
+ MEMTRACK_PUSH_GROUP( "TriggerLocator" );
+ rAssert( !mTriggerVolumes );
+
+ #ifdef RAD_DEBUG
+ if ( allocID == 0 )
+ {
+ rDebugString( "Someone is allocating trigger volume space in the DEFAULT heap!\n");
+ rDebugString( "Pass the correct allocator id to SetNumTriggers please!!\n");
+ }
+ #endif
+
+
+ mTriggerVolumes = new( (GameMemoryAllocator)allocID ) TriggerVolume*[num];
+ mMaxNumTriggers = (short)num;
+
+ unsigned short i;
+ for ( i = 0; i < mMaxNumTriggers; ++i )
+ {
+ mTriggerVolumes[ i ] = NULL;
+ }
+ MEMTRACK_POP_GROUP( "TriggerLocator" );
+}
+
+//=============================================================================
+// TriggerLocator::AddTriggerVolume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( TriggerVolume* volume )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerLocator::AddTriggerVolume( TriggerVolume* volume )
+{
+ rAssert( mNumTriggers < mMaxNumTriggers );
+ rAssert( mTriggerVolumes );
+
+ if ( volume )
+ {
+ volume->AddRef();
+
+ mTriggerVolumes[ mNumTriggers ] = volume;
+ mNumTriggers++;
+ }
+}
+
+//=============================================================================
+// TriggerLocator::GetTriggerVolume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int i )
+//
+// Return: TriggerVolume
+//
+//=============================================================================
+TriggerVolume* TriggerLocator::GetTriggerVolume( unsigned int i )
+{
+ rAssert( i < mMaxNumTriggers );
+ rAssert( mTriggerVolumes );
+
+ return mTriggerVolumes[ i ];
+}
+
+//=============================================================================
+// TriggerLocator::IsPlayerTracked
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID )
+//
+// Return: unsigned int
+//
+//=============================================================================
+unsigned int TriggerLocator::IsPlayerTracked( int playerID ) const
+{
+ unsigned int count = 0;
+ unsigned int i;
+ for ( i = 0; i < mNumTriggers; ++i )
+ {
+ if ( mTriggerVolumes[ i ] && mTriggerVolumes[ i ]->IsPlayerTracking( playerID ) )
+ {
+ ++count;
+ }
+ }
+
+ return count;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/meta/triggerlocator.h b/game/code/meta/triggerlocator.h
new file mode 100644
index 0000000..ccf7af0
--- /dev/null
+++ b/game/code/meta/triggerlocator.h
@@ -0,0 +1,163 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: triggerlocator.h
+//
+// Description: Blahblahblah
+//
+// History: 04/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef TRIGGERLOCATOR_H
+#define TRIGGERLOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#ifndef WORLD_BUILDER
+#include <meta/locator.h>
+#else
+#include "locator.h"
+#endif
+
+//========================================
+// Forward References
+//========================================
+class TriggerVolume;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class TriggerLocator : public Locator
+{
+public:
+ TriggerLocator();
+ virtual ~TriggerLocator();
+
+ void SetNumTriggers( unsigned int num, int allocID = 0 );
+ unsigned int GetNumTriggers();
+ void AddTriggerVolume( TriggerVolume* volume );
+ TriggerVolume* GetTriggerVolume( unsigned int i );
+
+ void Trigger( unsigned int playerID, bool bActive );
+
+ unsigned int GetPlayerID();
+ bool GetPlayerEntered();
+ // Only for use with fake trigger for first dynamic load.
+ void SetPlayerEntered( bool always = true );
+
+ unsigned int IsPlayerTracked( int playerID ) const;
+
+protected:
+ virtual void OnTrigger( unsigned int playerID )
+ {
+ // HEY! Don't call pure virtual functions from either a constructor
+ // or destructor unless you know the vtable has a valid entry
+ // I'm changing this from pure virtual to having an empty implementation
+ // as OnTrigger was being called from TriggerLocator's dtor (in a roundabout way)
+ };
+
+private:
+ TriggerVolume** mTriggerVolumes;
+ unsigned short mNumTriggers;
+ unsigned short mMaxNumTriggers;
+
+ bool mPlayerEntered; // If the last player entered or left
+ int mPlayerID; // The ID of the last player to trigger this locator
+
+ //Prevent wasteful constructor creation.
+ TriggerLocator( const TriggerLocator& triggerlocator );
+ TriggerLocator& operator=( const TriggerLocator& triggerlocator );
+};
+
+
+//******************************************************************************
+//
+// Inline Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// TriggerLocator::GetNumTriggers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned int
+//
+//=============================================================================
+inline unsigned int TriggerLocator::GetNumTriggers()
+{
+ return mNumTriggers;
+}
+
+//=============================================================================
+// TriggerLocator::Trigger
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int playerID, bool bActive )
+//
+// Return: void
+//
+//=============================================================================
+inline void TriggerLocator::Trigger( unsigned int playerID, bool bActive )
+{
+ mPlayerID = playerID;
+ mPlayerEntered = bActive;
+ OnTrigger( playerID );
+}
+
+//=============================================================================
+// TriggerLocator::GetPlayerID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline unsigned int TriggerLocator::GetPlayerID()
+{
+ return mPlayerID;
+}
+
+//=============================================================================
+// TriggerLocator::GetPlayerEntered
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool TriggerLocator::GetPlayerEntered()
+{
+ return mPlayerEntered;
+}
+
+
+//=============================================================================
+// TriggerLocator::SetPlayerEntered
+//=============================================================================
+// Description: Comment
+//
+// Parameters: always true
+//
+// Return: None.
+//
+//=============================================================================
+inline void TriggerLocator::SetPlayerEntered( bool always )
+{
+ mPlayerEntered = always;
+}
+
+
+#endif //TRIGGERLOCATOR_H
diff --git a/game/code/meta/triggervolume.cpp b/game/code/meta/triggervolume.cpp
new file mode 100644
index 0000000..6c2b787
--- /dev/null
+++ b/game/code/meta/triggervolume.cpp
@@ -0,0 +1,284 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement TriggerVolume
+//
+// History: 03/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#ifdef WORLD_BUILDER
+#include "main/toolhack.h"
+#endif
+
+#include <p3d/utility.hpp>
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#ifndef WORLD_BUILDER
+#include <meta/triggervolume.h>
+#include <meta/triggerlocator.h>
+#include <meta/locator.h>
+#include <meta/triggervolumetracker.h>
+#else
+#include "triggervolume.h"
+#include "triggerlocator.h"
+#include "locator.h"
+#endif
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// TriggerVolume::TriggerVolume
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TriggerVolume::TriggerVolume() :
+ mLocator( NULL ),
+ mPosition( rmt::Vector( 0.0f, 0.0f, 0.0f ) ),
+ mTrackingPlayers( 0 ),
+ mFrameUsed( 0 ),
+ mUser( 0 )
+ {
+#ifndef WORLD_BUILDER
+#ifndef RAD_RELEASE
+ verts = 0;
+ faces = 0;
+#endif
+#endif
+}
+
+//==============================================================================
+// TriggerVolume::~TriggerVolume
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TriggerVolume::~TriggerVolume()
+{
+ ClearPoints();
+
+ if ( mLocator )
+ {
+ mLocator = NULL;
+ }
+}
+
+
+void TriggerVolume::Trigger( unsigned int playerID, bool bActive )
+{
+ rAssert( mLocator != NULL );
+
+ mLocator->Trigger( playerID, bActive );
+}
+
+//=============================================================================
+// TriggerVolume::SetLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( TriggerLocator* locator )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolume::SetLocator( TriggerLocator* locator )
+{
+ rAssert( NULL == mLocator);
+ mLocator = locator;
+ //mLocator->AddRef();
+}
+
+//=============================================================================
+// TriggerVolume::IsActive
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool TriggerVolume::IsActive()
+{
+ rAssert( mLocator != NULL );
+
+ return( mLocator->GetFlag( Locator::ACTIVE ) );
+}
+
+//=============================================================================
+// TriggerVolume::Render
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolume::Render()
+{
+#ifndef WORLD_BUILDER
+#ifndef RAD_RELEASE
+
+ if ((verts == 0) || (faces == 0))
+ {
+ InitPoints ();
+ }
+
+ CalcPoints ();
+
+ pddiCullMode cm = p3d::pddi->GetCullMode();
+ p3d::pddi->SetCullMode( PDDI_CULL_INVERTED );
+
+ pddiPrimStream* stream = p3d::pddi->BeginPrims( GetTriggerVolumeTracker()->GetMaterial(), PDDI_PRIM_TRIANGLES, PDDI_V_C, numFaces );
+
+ unsigned int vert = 0;
+ pddiColour colour( 128, 128, 128, 64 );
+
+ for( int i = 0; i < numFaces; i++ )
+ {
+ vert = faces[ i ];
+
+ stream->Colour( colour );
+ stream->Coord( verts[ vert ].x, verts[ vert ].y, verts[ vert ].z );
+ }
+
+ p3d::pddi->EndPrims( stream );
+
+ p3d::pddi->SetCullMode( cm );
+#endif
+#endif
+}
+//========================================================================
+// triggervolume::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void TriggerVolume::GetBoundingBox(rmt::Box3D* box)
+{
+ rAssert(false);
+}
+//========================================================================
+// triggervolume::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void TriggerVolume::GetBoundingSphere(rmt::Sphere* sphere)
+{
+ rAssert(false);
+}
+//========================================================================
+// triggervolume::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void TriggerVolume::Display()
+{
+ if(( GetLocator()->GetFlag( Locator::ACTIVE ))
+ && ( GetLocator()->GetFlag( Locator::DRAWN )))
+ Render();
+}
+
+//=============================================================================
+// TriggerVolume::TriggerAllowed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID )
+//
+// Return: bool
+//
+//=============================================================================
+bool TriggerVolume::TriggerAllowed( int playerID )
+{
+ if ( mLocator == NULL )
+ {
+ return true;
+ }
+
+ return mLocator->TriggerAllowed( playerID );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// TriggerVolume::ClearPoints
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolume::ClearPoints()
+{
+#ifndef WORLD_BUILDER
+#ifndef RAD_RELEASE
+ delete[] verts;
+ delete[] faces;
+ verts = 0;
+ faces = 0;
+
+ numVerts = 0;
+ numFaces = 0;
+#endif
+#endif
+}
diff --git a/game/code/meta/triggervolume.h b/game/code/meta/triggervolume.h
new file mode 100644
index 0000000..57418ee
--- /dev/null
+++ b/game/code/meta/triggervolume.h
@@ -0,0 +1,359 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: triggervolume.h
+//
+// Description: Triggervolume class
+//
+// History: 03/04/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef TRIGGERVOLUME_H
+#define TRIGGERVOLUME_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+//#include <p3d/entity.hpp>
+#ifdef WORLD_BUILDER
+#include "../render/DSG/IEntityDSG.h"
+#else
+#include <render/DSG/IEntityDSG.h>
+#endif //WORLD_BUILDER
+
+#include <raddebugwatch.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+class TriggerLocator;
+class pddiShader;
+
+//=============================================================================
+//
+// Synopsis: Trigger volumes ahoy
+//
+//=============================================================================
+
+class TriggerVolume : public IEntityDSG//public tEntity
+{
+public:
+ enum Type
+ {
+ SPHERE,
+ RECTANGLE
+ };
+
+ TriggerVolume();
+ virtual ~TriggerVolume();
+
+ //Intersect tests
+ virtual bool Contains( const rmt::Vector& point,
+ float epsilon = 0.0f ) const = 0;
+ virtual bool IntersectsBox( const rmt::Vector& p1,
+ const rmt::Vector& p2,
+ const rmt::Vector& p3,
+ const rmt::Vector& p4 ) const = 0;
+
+ virtual bool IntersectsBox( const rmt::Box3D& box ) = 0;
+
+ virtual bool IntersectsSphere( const rmt::Vector& position,
+ float radius ) const = 0;
+
+ virtual bool IntersectLine( const rmt::Vector& p1,
+ const rmt::Vector& p2 ) const = 0;
+
+ virtual bool GetBoundingBox( rmt::Vector& p1, rmt::Vector& p2 ) const = 0;
+ virtual Type GetType() const = 0;
+
+ TriggerLocator* GetLocator();
+ void SetLocator( TriggerLocator* locator );
+
+ void SetFrameUsed( unsigned int frame, int user );
+ unsigned int GetFrameUsed() const;
+ int GetUser() const;
+
+ virtual void Trigger( unsigned int playerID, bool bActive );
+ bool IsActive();
+
+ const rmt::Vector& GetPosition() const;
+ void SetPosition( const rmt::Vector& pos );
+
+ bool IsPlayerTracking( int playerID );
+ void SetTrackingPlayer( int playerID, bool on );
+
+ bool TriggerAllowed( int playerID );
+
+ //////////////////////////////////////////////////////////////////////////
+ // IEntityDSG Interface
+ //////////////////////////////////////////////////////////////////////////
+ void DisplayBoundingBox(tColour colour = tColour(0,255,0));
+ void DisplayBoundingSphere(tColour colour = tColour(0,255,0));
+
+ virtual void GetBoundingBox(rmt::Box3D* box);
+ virtual void GetBoundingSphere(rmt::Sphere* sphere);
+ void Display();
+
+ rmt::Vector* pPosition();
+ const rmt::Vector& rPosition();
+ void GetPosition( rmt::Vector* ipPosn );
+
+ // Debug drawing stuff
+ void Render();
+
+protected:
+
+#ifndef RAD_RELEASE
+ // Cache for mesh to draw
+ int numVerts;
+ int numFaces;
+
+ rmt::Vector* verts;
+ unsigned short* faces;
+#endif
+
+ virtual void InitPoints() = 0;
+ virtual void CalcPoints() = 0;
+
+ void ClearPoints();
+private:
+ TriggerLocator* mLocator;
+ rmt::Vector mPosition;
+
+ unsigned char mTrackingPlayers; //This is a bit flag.
+
+ //I think these will go away.
+ unsigned int mFrameUsed;
+ int mUser;
+
+ //Prevent wasteful constructor creation.
+ TriggerVolume( const TriggerVolume& triggervolume );
+ TriggerVolume& operator=( const TriggerVolume& triggervolume );
+};
+
+//******************************************************************************
+//
+// Inline Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// TriggerVolume::GetLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: TriggerLocator
+//
+//=============================================================================
+inline TriggerLocator* TriggerVolume::GetLocator()
+{
+ return mLocator;
+}
+
+//=============================================================================
+// TriggerVolume::SetFrameUsed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int frame, int user )
+//
+// Return: void
+//
+//=============================================================================
+inline void TriggerVolume::SetFrameUsed( unsigned int frame, int user )
+{
+ mFrameUsed = frame;
+ mUser = user;
+}
+
+//=============================================================================
+// TriggerVolume::GetFrameUsed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned int
+//
+//=============================================================================
+inline unsigned int TriggerVolume::GetFrameUsed() const
+{
+ return mFrameUsed;
+}
+
+//=============================================================================
+// TriggerVolume::GetUser
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+inline int TriggerVolume::GetUser() const
+{
+ return mUser;
+}
+
+//=============================================================================
+// TriggerVolume::GetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline const rmt::Vector& TriggerVolume::GetPosition() const
+{
+ return( mPosition );
+}
+
+//=============================================================================
+// TriggerVolume::SetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& pos )
+//
+// Return: inline
+//
+//=============================================================================
+inline void TriggerVolume::SetPosition( const rmt::Vector& pos )
+{
+ mPosition = pos;
+}
+//========================================================================
+// triggervolume::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+inline void TriggerVolume::DisplayBoundingBox(tColour colour)
+{
+ rAssert(false);
+}
+//========================================================================
+// triggervolume::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+inline void TriggerVolume::DisplayBoundingSphere(tColour colour)
+{
+ rAssert(false);
+}
+//========================================================================
+// triggervolume::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+inline rmt::Vector* TriggerVolume::pPosition()
+{
+ return &mPosition;
+}
+//========================================================================
+// triggervolume::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+inline const rmt::Vector& TriggerVolume::rPosition()
+{
+ return mPosition;
+}
+//========================================================================
+// triggervolume::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+inline void TriggerVolume::GetPosition( rmt::Vector* ipPosn )
+{
+ *ipPosn = mPosition;
+}
+
+//=============================================================================
+// TriggerVolume::IsPlayerTracking
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID )
+//
+// Return: bool
+//
+//=============================================================================
+inline bool TriggerVolume::IsPlayerTracking( int playerID )
+{
+ rAssert( playerID < 32 );
+ return ( mTrackingPlayers & ( 1 << playerID ) ) != 0;
+}
+
+//=============================================================================
+// TriggerVolume::SetTrackingPlayer
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID, bool on )
+//
+// Return: void
+//
+//=============================================================================
+inline void TriggerVolume::SetTrackingPlayer( int playerID, bool on )
+{
+ rAssert( playerID < 32 );
+
+ if ( on )
+ {
+ mTrackingPlayers |= ( 1 << playerID );
+ }
+ else
+ {
+ mTrackingPlayers &= ~( 1 << playerID );
+ }
+}
+
+#endif //TRIGGERVOLUME_H
diff --git a/game/code/meta/triggervolumetracker.cpp b/game/code/meta/triggervolumetracker.cpp
new file mode 100644
index 0000000..97bd817
--- /dev/null
+++ b/game/code/meta/triggervolumetracker.cpp
@@ -0,0 +1,1329 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: triggervolumetracker.cpp
+//
+// Description: Implement TriggerVolumeTracker
+//
+// History: 13/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <radmath/radmath.hpp>
+#include <radtime.hpp>
+
+#include <p3d/debugdraw.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <meta/triggervolumetracker.h>
+#include <meta/triggervolume.h>
+#include <meta/triggerlocator.h>
+#include <meta/locatorevents.h>
+#include <meta/eventlocator.h>
+#include <meta/locatortypes.h>
+#include <meta/zoneeventlocator.h>
+
+#include <main/commandlineoptions.h>
+
+#include <memory/srrmemory.h>
+
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/vehicle.h>
+
+#include <mission/gameplaymanager.h>
+
+#include <debug/profiler.h>
+
+#include <render/intersectmanager/intersectmanager.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercam.h>
+
+#include <simcommon/simstate.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <simcommon/simulatedobject.hpp>
+#include <simcommon/simenvironment.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <simcollision/collisionvolume.hpp>
+#include <simcollision/collisionmanager.hpp>
+
+
+LocatorEvent::Event ALLOWED_AI_EVENTS[] =
+{
+ LocatorEvent::DEATH,
+ LocatorEvent::PARKED_BIRDS,
+ LocatorEvent::WHACKY_GRAVITY,
+ LocatorEvent::GOO_DAMAGE
+};
+
+unsigned int NUM_ALLOWED_AI_EVENTS = sizeof( ALLOWED_AI_EVENTS ) / sizeof( ALLOWED_AI_EVENTS[0] );
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+TriggerVolumeTracker* TriggerVolumeTracker::spInstance = NULL;
+
+rmt::Vector TriggerVolumeTracker::sP1;
+rmt::Vector TriggerVolumeTracker::sP2;
+rmt::Vector TriggerVolumeTracker::sP3;
+rmt::Vector TriggerVolumeTracker::sP4;
+
+sim::CollisionVolumeTypeEnum TriggerVolumeTracker::sColType;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+void TriggerVolumeTracker::CreateInstance()
+{
+ MEMTRACK_PUSH_GROUP( "TriggerVolumeTracker" );
+ rAssert( spInstance == NULL );
+ spInstance = new(GMA_PERSISTENT) TriggerVolumeTracker();
+ rAssert( spInstance );
+ MEMTRACK_POP_GROUP( "TriggerVolumeTracker" );
+}
+
+//=============================================================================
+// TriggerVolumeTracker::GetInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: TriggerVolumeTracker
+//
+//=============================================================================
+TriggerVolumeTracker* TriggerVolumeTracker::GetInstance()
+{
+ //rAssert( spInstance );
+ return spInstance;
+}
+
+//=============================================================================
+// TriggerVolumeTracker::DestroyInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::DestroyInstance()
+{
+ if( spInstance != NULL )
+ {
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+ }
+}
+
+//==============================================================================
+// TriggerVolumeTracker::TriggerVolumeTracker
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TriggerVolumeTracker::TriggerVolumeTracker() :
+ mpTriggerSphere( NULL ),
+ mNumRegisteredAI( 0 ),
+ mIgnoreTriggers ( false )
+#ifndef RAD_RELEASE
+ ,
+ mDisplayAmbients( false ),
+ mDisplayActive( true ),
+ mDisplayInactive( false ),
+ mDisplayAll( false )
+#endif
+{
+ int i;
+ for( i = 0; i < MAX_PLAYERS; i++ )
+ {
+ mActiveCount[ i ] = 0;
+ }
+
+ for ( i = 0; i < MAX_AI; ++i )
+ {
+ mAICount[ i ] = 0;
+ }
+
+ mTriggerCount = 0;
+
+#ifndef RAD_RELEASE
+ radDbgWatchAddBoolean( &mDisplayActive, "Display Active Vols" , "Trigger Volume Tracker" );
+ radDbgWatchAddBoolean( &mDisplayInactive, "Display Inactive Vols" , "Trigger Volume Tracker" );
+ radDbgWatchAddBoolean( &mDisplayAll, "Display ALL" , "Trigger Volume Tracker" );
+
+ for ( i = 0; i < LocatorType::NUM_TYPES; ++i )
+ {
+ char name[256];
+ sprintf( name, "Display %s", LocatorType::Name[i] );
+ radDbgWatchAddBoolean( &mDisplayEnable[i], name , "Trigger Volume Tracker" );
+ mDisplayEnable[i] = false;
+ }
+
+ radDbgWatchAddBoolean( &mDisplayAmbients, "Display Ambients", "Trigger Volume Tracker" );
+
+ material = NULL;
+
+#endif
+}
+
+
+#ifndef RAD_RELEASE
+pddiShader* TriggerVolumeTracker::GetMaterial(void)
+{
+ if(!material)
+ {
+ HeapMgr()->PushHeap(GMA_PERSISTENT);
+ material = p3d::device->NewShader( "simple" );
+ material->AddRef();
+ material->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD);
+ material->SetColour( PDDI_SP_AMBIENT, pddiColour( 128, 128, 128, 64 ));
+ material->SetColour( PDDI_SP_DIFFUSE, pddiColour( 128, 128, 128, 64 ));
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+ }
+
+ return material;
+}
+#endif
+
+//==============================================================================
+// TriggerVolumeTracker::~TriggerVolumeTracker
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TriggerVolumeTracker::~TriggerVolumeTracker()
+{
+ Cleanup();
+
+#ifndef RAD_RELEASE
+ radDbgWatchDelete( &mDisplayActive );
+ radDbgWatchDelete( &mDisplayInactive );
+ radDbgWatchDelete( &mDisplayAll );
+
+ int i;
+ for ( i = 0; i < LocatorType::NUM_TYPES; ++i )
+ {
+ radDbgWatchDelete( &mDisplayEnable[i] );
+ }
+
+ radDbgWatchDelete( &mDisplayAmbients );
+
+ if ( material )
+ {
+ material->Release();
+ }
+#endif
+}
+
+//=============================================================================
+// TriggerVolumeTracker::Cleanup
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::Cleanup()
+{
+ unsigned int i;
+ int j;
+ for( j = 0; j < MAX_PLAYERS; j++ )
+ {
+ for( i = 0; i < mActiveCount[ j ]; i++ )
+ {
+ mActiveVolumes[ j ][ i ]->Release();
+ mActiveVolumes[ j ][ i ] = NULL;
+ }
+
+ mActiveCount[ j ] = 0;
+ }
+
+ for ( j = 0; j < MAX_AI; ++j )
+ {
+ for ( i = 0; i < mAICount[ j ]; ++i )
+ {
+ mActiveAIVolumes[ j ][ i ]->Release();
+ mActiveAIVolumes[ j ][ i ] = NULL;
+ }
+
+ mAICount[ j ] = 0;
+ }
+
+ if ( mNumRegisteredAI > 0 )
+ {
+ for ( i = 0; i < mNumRegisteredAI; ++i )
+ {
+ mRegisteredVehicles[ i ].mVehicle->Release();
+ mRegisteredVehicles[ i ].mVehicle = NULL;
+ }
+ }
+
+ mNumRegisteredAI = 0;
+
+ for( i = 0; i < mTriggerCount; i++ )
+ {
+ mTriggerVolumes[ i ]->Release();
+ mTriggerVolumes[ i ] = NULL;
+ }
+
+ mTriggerCount = 0;
+}
+
+
+//=============================================================================
+// TriggerVolumeTracker::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::Update( int elapsedTime )
+{
+ if ( mIgnoreTriggers )
+ {
+ //Do nothing
+ return;
+ }
+
+BEGIN_PROFILE("Trigger Tracker");
+
+ rmt::Vector center;
+ const float radius = 1.0f;
+
+ unsigned int iNumPlayers = GetGameplayManager()->GetNumPlayers();
+
+ unsigned int i;
+ for( i = 0; i < iNumPlayers; i++ )
+ {
+ // get the player's location and bounding sphere radius
+ GetAvatarManager()->GetAvatarForPlayer( i )->GetPosition( center );
+
+ CheckForActiveVolumes( i, center, radius );
+ }
+
+ VehicleCentral* vc = GetVehicleCentral();
+
+ for ( i = 0; i < mNumRegisteredAI; ++i )
+ {
+ rmt::Vector pos;
+ mRegisteredVehicles[ i ].mVehicle->GetPosition( &pos );
+
+ int id = vc->GetVehicleId( mRegisteredVehicles[ i ].mVehicle );
+
+ CheckForActiveVolumes( id + MAX_PLAYERS, pos, radius );
+ }
+
+END_PROFILE("Trigger Tracker");
+}
+
+
+//=============================================================================
+// TriggerVolumeTracker::AddTrigger
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( TriggerVolume* triggerVolume )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::AddTrigger( TriggerVolume* triggerVolume )
+{
+ rAssert( mTriggerCount < MAX_VOLUMES );
+ unsigned int index;
+ for( index = 0; index < mTriggerCount; index++ )
+ {
+ if( mTriggerVolumes[ index ] == triggerVolume )
+ {
+ return;
+ }
+ }
+
+ mTriggerVolumes[ mTriggerCount ] = triggerVolume;
+ triggerVolume->AddRef();
+
+ mTriggerCount++;
+}
+
+//=============================================================================
+// TriggerVolumeTracker::RemoveTrigger
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( TriggerVolume* triggerVolume )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::RemoveTrigger( TriggerVolume* triggerVolume )
+{
+ unsigned int index;
+ for( index = 0; index < mTriggerCount; index++ )
+ {
+ if( mTriggerVolumes[ index ] == triggerVolume )
+ {
+ //Shift the last trigger into the new empty position.
+ mTriggerVolumes[ index ] = mTriggerVolumes[ mTriggerCount-1 ];
+ triggerVolume->Release();
+
+ mTriggerCount--;
+
+ break;
+ }
+ }
+
+ for( int player = 0; player < GetGameplayManager()->GetNumPlayers(); player++ )
+ {
+ for( index = 0; index < mActiveCount[ player ]; index++ )
+ {
+ if( mActiveVolumes[ player ][ index ] == triggerVolume )
+ {
+ RemoveActive( player, index );
+ break;
+ }
+ }
+ }
+
+ for ( unsigned int ai = 0; ai < mNumRegisteredAI; ++ai )
+ {
+ for ( index = 0; index < mAICount[ ai ]; ++index )
+ {
+ if ( mActiveAIVolumes[ ai ][ index ] == triggerVolume )
+ {
+ RemoveAIActive( ai, index );
+ }
+ }
+ }
+}
+
+
+//=============================================================================
+// TriggerVolumeTracker::UnregisterFromInventory
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tInventory* inv )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::UnregisterFromInventory( tInventory* inv )
+{
+//BEGIN_PROFILE( "Trigger Tracker Unregister Inventory ");
+
+ rAssert( inv );
+
+ if ( inv )
+ {
+ HeapMgr()->PushHeap( GMA_TEMP );
+ tInventory::Iterator<TriggerVolume> it;
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ TriggerVolume* tv = it.First();
+
+ while ( tv != NULL )
+ {
+ RemoveTrigger( tv );
+
+ tv = it.Next();
+ }
+ }
+//END_PROFILE( "Trigger Tracker Unregister Inventory ");
+}
+
+
+//=============================================================================
+// TriggerVolumeTracker::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_LOCATOR:
+ {
+ break;
+ }
+ default:
+ {
+ // implement all events this class is registered for!
+ rAssert( false );
+ }
+ }
+}
+
+//=============================================================================
+// TriggerVolumeTracker::Render
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::Render()
+{
+#ifndef RAD_RELEASE
+ const Locator* locator;
+ unsigned int i;
+
+/*
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( playerAvatar );
+
+ if ( playerAvatar->IsInCar() )
+ {
+ //Test the car like it was the "Green Lantern" | 0 |. Two lines and
+ //a sphere.
+ //Get the BBOX of the car
+
+ Vehicle* vehicle = playerAvatar->GetVehicle();
+
+ sim::CollisionVolume* vecol = vehicle->GetSimState()->GetCollisionObject()->GetCollisionVolume()->GetSubCollisionVolume( 0 );
+ rAssert( vecol->Type() == sim::OBBoxVolumeType );
+
+ sim::OBBoxVolume* obBox = static_cast<sim::OBBoxVolume*>( vecol );
+
+ rmt::Vector minCorner, maxCorner;
+
+ minCorner.x = - obBox->mLength[0];
+ minCorner.y = - obBox->mLength[1];
+ minCorner.z = - obBox->mLength[2];
+
+ maxCorner.x = obBox->mLength[0];
+ maxCorner.y = obBox->mLength[1];
+ maxCorner.z = obBox->mLength[2];
+
+ p3d::stack->Push();
+
+ rmt::Matrix positionMatrix;
+ positionMatrix.Identity();
+ positionMatrix.Row(0) = obBox->mAxis[0];
+ positionMatrix.Row(1) = obBox->mAxis[1];
+ positionMatrix.Row(2) = obBox->mAxis[2];
+ positionMatrix.Row(3) = obBox->mPosition;
+
+
+ p3d::stack->Multiply(positionMatrix);
+
+
+ tShader* shader = new tShader("simple");
+ shader->AddRef();
+
+ P3DDrawOrientedBox( minCorner, maxCorner, *shader );
+
+ shader->Release();
+
+ p3d::stack->Pop();
+ }
+*/
+ for( i = 0; i < mTriggerCount; i++ )
+ {
+ if ( mDisplayAll )
+ {
+ mTriggerVolumes[ i ]->Render();
+ continue;
+ }
+
+ locator = mTriggerVolumes[ i ]->GetLocator();
+
+ if ( CommandLineOptions::Get( CLO_SHOW_DYNA_ZONES ) && locator->GetDataType() == LocatorType::DYNAMIC_ZONE )
+ {
+ mTriggerVolumes[ i ]->Render();
+ }
+ else if ( !locator )
+ {
+ }
+ else if ( mDisplayActive && locator->GetFlag( Locator::ACTIVE ) )
+ {
+ if ( mDisplayEnable[ locator->GetDataType() ] )
+ {
+ mTriggerVolumes[ i ]->Render();
+ }
+ else if ( locator->GetDataType() == LocatorType::EVENT )
+ {
+ //We want to look at the ambient events.
+ EventLocator* evtLoc = (EventLocator*)( locator );
+ LocatorEvent::Event event = evtLoc->GetEventType();
+ if ( mDisplayAmbients &&
+ (( event >= LocatorEvent::AMBIENT_SOUND_CITY &&
+ event <= LocatorEvent::AMBIENT_SOUND_MANSION_INTERIOR ) ||
+ ( event >= LocatorEvent::AMBIENT_SOUND_COUNTRY_NIGHT &&
+ event <= LocatorEvent::AMBIENT_SOUND_PLACEHOLDER9 ) ||
+ ( event >= LocatorEvent::AMBIENT_SOUND_SEASIDE_NIGHT &&
+ event <= LocatorEvent::AMBIENT_SOUND_PLACEHOLDER16 ))
+ )
+ {
+ mTriggerVolumes[ i ]->Render();
+ }
+ }
+ }
+ else if ( mDisplayInactive && !locator->GetFlag( Locator::ACTIVE ) )
+ {
+ if ( mDisplayEnable[ locator->GetDataType() ] )
+ {
+ mTriggerVolumes[ i ]->Render();
+ }
+ }
+ }
+
+/*
+ rmt::Vector center;
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( center );
+
+ IntersectManager* im = IntersectManager::GetInstance();
+ rAssert( im );
+
+ ReserveArray<TriggerVolume*> volDSGList;
+
+ //float radius = GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam()->GetFarPlane();
+ float radius = 180.0f;
+ im->FindTrigVolElems( center, radius, volDSGList );
+
+ unsigned int time = radTimeGetMilliseconds();
+
+ //Test all the triggers around the car
+ for ( i = 0; i < static_cast<unsigned int>(volDSGList.mUseSize); ++i )
+ {
+ TriggerVolume* vol = volDSGList[ i ];
+ if ( mDisplayAll )
+ {
+ vol->Render();
+ continue;
+ }
+
+ locator = vol->GetLocator();
+
+ if ( mDisplayActive && locator->GetFlag( Locator::ACTIVE ) )
+ {
+ if ( mDisplayEnable[ locator->GetDataType() ] )
+ {
+ vol->Render();
+ }
+ }
+ else if ( mDisplayInactive && !locator->GetFlag( Locator::ACTIVE ) )
+ {
+ if ( mDisplayEnable[ locator->GetDataType() ] )
+ {
+ vol->Render();
+ }
+ }
+ }
+*/
+#endif
+}
+
+
+//=============================================================================
+// TriggerVolumeTracker::GetTriggerByName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: TriggerVolume
+//
+//=============================================================================
+TriggerVolume* TriggerVolumeTracker::GetTriggerByName( const char* name )
+{
+ unsigned int i;
+ for( i = 0; i < mTriggerCount; i++ )
+ {
+ if( strcmp( name, mTriggerVolumes[ i ]->GetName() ) == 0 )
+ {
+ return( mTriggerVolumes[ i ] );
+ }
+ }
+
+ return( NULL );
+}
+
+//=============================================================================
+// TriggerVolumeTracker::RegisterAI
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* ai, int locatorTypes )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::RegisterAI( Vehicle* ai, int locatorTypes )
+{
+ rAssert( mNumRegisteredAI < MAX_AI );
+
+#ifdef RAD_DEBUG
+ unsigned int i;
+ for ( i = 0; i < mNumRegisteredAI; ++i )
+ {
+ rAssert( mRegisteredVehicles[ i ].mVehicle != ai );
+ }
+#endif
+
+ if ( mNumRegisteredAI < MAX_AI )
+ {
+ tRefCounted::Assign( mRegisteredVehicles[ mNumRegisteredAI ].mVehicle, ai );
+ mRegisteredVehicles[ mNumRegisteredAI ].mTriggerTypes = locatorTypes;
+ }
+
+ mNumRegisteredAI++;
+}
+
+//=============================================================================
+// TriggerVolumeTracker::UnregisterAI
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* ai )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::UnregisterAI( Vehicle* ai )
+{
+ unsigned int i;
+ for ( i = 0; i < mNumRegisteredAI; ++i )
+ {
+ if ( mRegisteredVehicles[ i ].mVehicle == ai )
+ {
+ mRegisteredVehicles[ i ].mVehicle->Release();
+ mRegisteredVehicles[ i ].mVehicle = NULL;
+
+ if ( mNumRegisteredAI > 1 )
+ {
+ mRegisteredVehicles[ i ] = mRegisteredVehicles[ mNumRegisteredAI - 1 ];
+ mRegisteredVehicles[ mNumRegisteredAI - 1 ].mVehicle = NULL;
+ }
+
+ mNumRegisteredAI -= 1;
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// TriggerVolumeTracker::GetAI
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int id )
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* TriggerVolumeTracker::GetAI( int id )
+{
+ rAssert( id - MAX_PLAYERS < static_cast<int>(mNumRegisteredAI) );
+
+ return mRegisteredVehicles[ id - MAX_PLAYERS ].mVehicle;
+}
+
+//=============================================================================
+// TriggerVolumeTracker::RegisterLocatorTypeForAI
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( LocatorType::Type type, Vehicle* ai )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::RegisterLocatorTypeForAI( LocatorType::Type type, Vehicle* ai )
+{
+ unsigned int i;
+ for ( i = 0; i < mNumRegisteredAI; ++i )
+ {
+ if ( mRegisteredVehicles[ i ].mVehicle == ai )
+ {
+ mRegisteredVehicles[ i ].mTriggerTypes |= ( 1 << type );
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// TriggerVolumeTracker::UnregisterLocatorTypeForAI
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( LocatorType::Type type, Vehicle* ai )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::UnregisterLocatorTypeForAI( LocatorType::Type type, Vehicle* ai )
+{
+ unsigned int i;
+ for ( i = 0; i < mNumRegisteredAI; ++i )
+ {
+ if ( mRegisteredVehicles[ i ].mVehicle == ai )
+ {
+ mRegisteredVehicles[ i ].mTriggerTypes &= ~( 1 << type );
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// TriggerVolumeTracker::ResetDynaloadZones
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::ResetDynaloadZones()
+{
+ for( int player = 0; player < GetGameplayManager()->GetNumPlayers(); player++ )
+ {
+ for( int index = 0; index < static_cast<int>( mActiveCount[ player ] ); index++ )
+ {
+ Locator* loc = mActiveVolumes[ player ][ index ]->GetLocator();
+ if ( !loc )
+ {
+ continue;
+ }
+
+ if( loc->GetDataType() == LocatorType::DYNAMIC_ZONE )
+ {
+ RemoveActive( player, index );
+ break;
+ }
+ }
+ }
+}
+//========================================================================
+// triggervolumetracker::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void TriggerVolumeTracker::ActivateNearestInteriorLoadZone(int iPlayerID, rmt::Vector& irPosnRef, float iMaxDistance)
+{
+ ZoneEventLocator* pNearestZEL = NULL;
+ float nearestLocatorDistSqr = iMaxDistance*iMaxDistance;
+
+ Locator* loc = NULL;
+ rmt::Sphere sphere;
+
+ int i;
+
+ for ( i = 0; i < static_cast<int>( mTriggerCount ); ++i )
+ {
+ loc = mTriggerVolumes[i]->GetLocator();
+ if(loc == NULL) continue;
+
+ if( loc->GetDataType() == LocatorType::DYNAMIC_ZONE )
+ {
+ ZoneEventLocator* pZEL = (ZoneEventLocator*)(loc);
+ if(pZEL->IsInteriorLoad())
+ {
+ mTriggerVolumes[i]->GetBoundingSphere(&sphere);
+
+ rmt::Vector dist;
+ dist.Sub(sphere.centre, irPosnRef);
+ float curDistSqr = dist.MagnitudeSqr();
+
+ if( (curDistSqr < nearestLocatorDistSqr)
+ && (curDistSqr < ((iMaxDistance + sphere.radius) * (iMaxDistance + sphere.radius))))
+ {
+ pNearestZEL = pZEL;
+ nearestLocatorDistSqr = curDistSqr;
+ }
+ }
+ }
+ }
+
+ if(pNearestZEL!=NULL)
+ {
+ pNearestZEL->Trigger(iPlayerID,true);
+ }
+ }
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// TriggerVolumeTracker::AddActive
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID, TriggerVolume* triggerVolume )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::AddActive( int playerID, TriggerVolume* triggerVolume )
+{
+ rAssert( triggerVolume != NULL );
+
+ if ( triggerVolume == NULL || !triggerVolume->TriggerAllowed( playerID ) )
+ return;
+
+ rAssert( mActiveCount[ playerID ] < MAX_ACTIVE );
+
+ mActiveVolumes[ playerID ][ mActiveCount[ playerID ] ] = triggerVolume;
+ mActiveCount[ playerID ]++;
+
+ triggerVolume->AddRef();
+
+ TriggerLocator* trigLoc = triggerVolume->GetLocator();
+ if ( trigLoc && trigLoc->IsPlayerTracked( playerID ) == 0 ) //returns number tracked.
+ {
+ triggerVolume->Trigger( playerID, true );
+ }
+
+ triggerVolume->SetTrackingPlayer( playerID, true );
+}
+
+//=============================================================================
+// TriggerVolumeTracker::CheckForActiveVolumes
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID, rmt::Vector& center, float radius )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::CheckForActiveVolumes( int playerID, rmt::Vector& center, float radius )
+{
+ unsigned int time = radTimeGetMilliseconds();
+ unsigned int i;
+
+//BEGIN_PROFILE( "CheckForActiveVolumes" );
+// IntersectManager* im = IntersectManager::GetInstance();
+// rAssert( im );
+//
+// ReserveArray<TriggerVolume*> volDSGList;
+//
+//BEGIN_PROFILE( "Query Intersect Manager" );
+// im->FindTrigVolElems( center, radius, volDSGList );
+//END_PROFILE( "Query Intersect Manager" );
+
+//BEGIN_PROFILE( "Car Triggers" );
+ //Test all the triggers around the car
+// for ( i = 0; i < static_cast<unsigned int>(volDSGList.mUseSize); ++i )
+// {
+// TriggerVolume* vol = volDSGList[ i ];
+//
+// TestVolume( vol, center, radius, playerID, time );
+// }
+//END_PROFILE( "Car Triggers" );
+
+ int localID = playerID;
+ if ( playerID >= MAX_PLAYERS )
+ {
+ //localize the id
+ VehicleCentral* vc = GetVehicleCentral();
+
+ for ( i = 0; i < mNumRegisteredAI; ++i )
+ {
+ if ( mRegisteredVehicles[ i ].mVehicle == vc->GetVehicle( playerID - MAX_PLAYERS ) )
+ {
+ localID = i + MAX_PLAYERS;
+ break;
+ }
+ }
+
+ rAssert( localID != -1 );
+ }
+ else
+ {
+ //This is a player. Store their points for testing.
+ //Test the car like it was the "Green Lantern" | 0 |. Two lines and
+ //a sphere.
+ //Get the BBOX of the car
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer( playerID );
+ Vehicle* vehicle = playerAvatar->GetVehicle();
+
+ if ( vehicle )
+ {
+ /*
+ rmt::Vector box = vehicle->GetExtents();
+ rmt::Matrix carToWorld = vehicle->GetTransform();
+
+ sP1.x = -box.x;
+ sP1.y = -box.y;
+ sP1.z = -box.z;
+ sP1.Transform( carToWorld );
+
+ sP2.x = +box.x;
+ sP2.y = -box.y;
+ sP2.z = -box.z;
+ sP2.Transform( carToWorld );
+
+ sP3.x = +box.x;
+ sP3.y = +box.y;
+ sP3.z = +box.z;
+ sP3.Transform( carToWorld );
+
+ sP4.x = -box.x;
+ sP4.y = +box.y;
+ sP4.z = +box.z;
+ sP4.Transform( carToWorld );
+ */
+
+ sim::CollisionVolume* vecol = vehicle->GetSimState()->GetCollisionObject()->GetCollisionVolume()->GetSubCollisionVolume( 0 );
+ sColType = vecol->Type();
+
+ if( sColType == sim::OBBoxVolumeType )
+ {
+ sim::OBBoxVolume* obBox = static_cast<sim::OBBoxVolume*>( vecol );
+
+ sP1.x = - obBox->mLength[0];
+ sP1.y = - obBox->mLength[1];
+ sP1.z = - obBox->mLength[2];
+
+ sP2.x = obBox->mLength[0];
+ sP2.y = - obBox->mLength[1];
+ sP2.z = - obBox->mLength[2];
+
+ sP3.x = obBox->mLength[0];
+ sP3.y = obBox->mLength[1];
+ sP3.z = obBox->mLength[2];
+
+ sP4.x = - obBox->mLength[0];
+ sP4.y = obBox->mLength[1];
+ sP4.z = obBox->mLength[2];
+
+ rmt::Matrix positionMatrix;
+ positionMatrix.Identity();
+ positionMatrix.Row(0) = obBox->mAxis[0];
+ positionMatrix.Row(1) = obBox->mAxis[1];
+ positionMatrix.Row(2) = obBox->mAxis[2];
+ positionMatrix.Row(3) = obBox->mPosition;
+
+ sP1.Transform( positionMatrix );
+ sP2.Transform( positionMatrix );
+ sP3.Transform( positionMatrix );
+ sP4.Transform( positionMatrix );
+
+ radius = rmt::Sqrt(obBox->mLength[0] + obBox->mLength[1]);
+ }
+ }
+ }
+
+//BEGIN_PROFILE( "Added Triggers" );
+ //Test all the triggers added to the trigger tracker
+ for ( i = 0; i < mTriggerCount; ++i )
+ {
+ rmt::Sphere sphere;
+ mTriggerVolumes[ i ]->GetBoundingSphere(&sphere);
+
+ rmt::Vector dist;
+ dist.Sub(sphere.centre, center);
+
+ if(dist.MagnitudeSqr() < ((radius + sphere.radius) * (radius + sphere.radius)))
+ {
+ TestVolume( mTriggerVolumes[ i ], center, radius, localID, time );
+ }
+ }
+//END_PROFILE( "Added Triggers" );
+
+ if ( playerID < MAX_PLAYERS )
+ {
+ //BEGIN_PROFILE( "Active Triggers" );
+ //Test for triggers that we've left.
+ for( i = 0; i < mActiveCount[ playerID ]; ++i )
+ {
+ // Added a test for the active trigger volume, because it would
+ // seem that if a locator were not active, you wouldn't want
+ // the TV in the active list.
+ //
+ // TBJ [8/23/2002]
+ //
+ if( (mActiveVolumes[ playerID ][ i ]->GetLocator() && !mActiveVolumes[ playerID ][ i ]->GetLocator()->GetFlag( Locator::ACTIVE )) ||
+ mActiveVolumes[ playerID ][ i ]->GetFrameUsed() != time )
+ {
+ RemoveActive( playerID, i );
+ }
+ }
+ //END_PROFILE( "Active Triggers" );
+ }
+ else
+ {
+ localID -= MAX_PLAYERS;
+
+ for ( i = 0; i < mAICount[ localID ]; ++i )
+ {
+ if ( !mActiveAIVolumes[ localID ][ i ]->GetLocator()->GetFlag( Locator::ACTIVE ) ||
+ mActiveAIVolumes[ localID ][ i ]->GetFrameUsed() != time )
+ {
+ RemoveAIActive( localID, i );
+ }
+ }
+ }
+
+//END_PROFILE( "CheckForActiveVolumes" );
+}
+
+
+//=============================================================================
+// TriggerVolumeTracker::RemoveActive
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID, int index )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::RemoveActive( int playerID, int index )
+{
+ TriggerVolume* vol = mActiveVolumes[ playerID ][ index ];
+
+ TriggerLocator* trigLoc = vol->GetLocator();
+ if ( trigLoc && trigLoc->IsPlayerTracked( playerID ) <= 1 ) //Some locators are screwy so they never track trigger voluemes. Gags, mostly
+ {
+ vol->Trigger( playerID, false );
+ }
+
+ vol->SetTrackingPlayer( playerID, false );
+ vol->Release();
+
+ if ( mActiveCount[ playerID ] > 0 )
+ {
+ mActiveVolumes[ playerID ][ index ] = mActiveVolumes[ playerID ][ mActiveCount[ playerID ] - 1 ];
+ mActiveVolumes[ playerID ][ mActiveCount[ playerID ] - 1 ] = NULL;
+ }
+
+ mActiveCount[ playerID ]--;
+}
+
+//=============================================================================
+// TriggerVolumeTracker::AddAIActive
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID, TriggerVolume* triggerVolume )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::AddAIActive( int playerID, TriggerVolume* triggerVolume )
+{
+ rAssert( mAICount[ playerID ] < MAX_AI_VOLUMES );
+
+ if ( !triggerVolume->TriggerAllowed( playerID ) )
+ {
+ return;
+ }
+
+ mActiveAIVolumes[ playerID ][ mAICount[ playerID ] ] = triggerVolume;
+ triggerVolume->AddRef();
+
+ TriggerLocator* trigLoc = triggerVolume->GetLocator();
+ if ( trigLoc && trigLoc->IsPlayerTracked( playerID + MAX_PLAYERS ) == 0 )
+ {
+ triggerVolume->Trigger( playerID + MAX_PLAYERS, true ); //And the hacks go on
+ }
+
+ triggerVolume->SetTrackingPlayer( playerID + MAX_PLAYERS, true );
+
+ mAICount[ playerID ] += 1;
+}
+
+//=============================================================================
+// TriggerVolumeTracker::RemoveAIActive
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID, int index )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::RemoveAIActive( int playerID, int index )
+{
+ TriggerVolume* vol = mActiveAIVolumes[ playerID ][ index ];
+
+ TriggerLocator* trigLoc = vol->GetLocator();
+ if ( trigLoc && trigLoc->IsPlayerTracked( playerID + MAX_PLAYERS ) == 1 )
+ {
+ vol->Trigger( playerID + MAX_PLAYERS, false );
+ }
+
+ vol->SetTrackingPlayer( playerID + MAX_PLAYERS, false ); //This is a hack.
+ vol->Release();
+
+ if ( mAICount[ playerID ] > 0 )
+ {
+ mActiveAIVolumes[ playerID ][ index ] = mActiveAIVolumes[ playerID ][ mAICount[ playerID ] - 1 ];
+ mActiveAIVolumes[ playerID ][ mAICount[ playerID ] - 1 ] = NULL;
+ }
+
+ mAICount[ playerID ] -= 1;
+}
+
+//=============================================================================
+// TriggerVolumeTracker::TestVolume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( TriggerVolume* vol, rmt::Vector& center, float radius, int playerID, unsigned int time )
+//
+// Return: void
+//
+//=============================================================================
+void TriggerVolumeTracker::TestVolume( TriggerVolume* vol,
+ rmt::Vector& center,
+ float radius,
+ int playerID,
+ unsigned int time )
+{
+ rAssert( vol );
+
+ // This is added because the AmbientDialogueTriggers do not have a locator associated with them
+ // with them.
+ if ( vol->GetLocator() != NULL )
+ {
+ if ( playerID >= MAX_PLAYERS &&
+ ( ((1 << vol->GetLocator()->GetDataType()) & mRegisteredVehicles[ playerID - MAX_PLAYERS ].mTriggerTypes) == 0 ) )
+ {
+ return;
+ }
+ else if ( playerID >= MAX_PLAYERS )
+ {
+ //Test if this is a legal event.
+ if ( vol->GetLocator()->GetDataType() == LocatorType::EVENT )
+ {
+ EventLocator* evtLoc = static_cast<EventLocator*>(vol->GetLocator());
+ unsigned int i;
+ for ( i = 0; i < NUM_ALLOWED_AI_EVENTS; ++i )
+ {
+ if ( evtLoc->GetEventType() == ALLOWED_AI_EVENTS[ i ] )
+ {
+ break;
+ }
+ }
+
+ if ( i == NUM_ALLOWED_AI_EVENTS )
+ {
+ //Not allowed to trigger this.
+ return;
+ }
+ }
+ }
+ }
+
+ bool collision = false;
+
+ Locator* loc = vol->GetLocator();
+
+ if ( playerID < MAX_PLAYERS &&
+ GetAvatarManager()->GetAvatarForPlayer( playerID )->IsInCar() &&
+ ( loc && loc->GetDataType() != LocatorType::OCCLUSION ) )
+ {
+ if ( GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ collision = (!loc || loc->GetFlag( Locator::ACTIVE )) && vol->IntersectsSphere( center, 1.5f );
+ }
+ else
+ {
+ collision = (!loc || loc->GetFlag( Locator::ACTIVE )) &&
+ (vol->IntersectsSphere( center, 1.5f ) || vol->IntersectsBox( sP1, sP2, sP4, sP3 ));
+ }
+ }
+ else
+ {
+ collision = (!loc || loc->GetFlag( Locator::ACTIVE )) &&
+ vol->Contains( center, radius );
+ }
+
+//BEGIN_PROFILE( "Test Volume" );
+ if ( collision )
+ {
+ //Test to see if this is a DEATH volume.
+ if ( loc->GetDataType() == LocatorType::EVENT )
+ {
+ EventLocator* evtLoc = static_cast<EventLocator*>(loc);
+ if ( evtLoc->GetEventType() == LocatorEvent::DEATH )
+ {
+ //This is so that death volumes always say you're in them until you leave.
+ //Hack for Chuka.
+ vol->Trigger( playerID, true );
+ return; //Early death.
+ }
+ }
+
+//BEGIN_PROFILE( "Is Player Tracking" );
+ if ( !vol->IsPlayerTracking( playerID ) )
+ {
+ //New trigger!
+#ifdef RAD_DEBUG
+ char name[256];
+ sprintf(name, "Hit trigger: %s\n", vol->GetName() );
+ rDebugString( name );
+#endif
+
+ if ( playerID < MAX_PLAYERS )
+ {
+ //BEGIN_PROFILE( "AddActive" );
+ AddActive( playerID, vol );
+ //END_PROFILE( "AddActive" );
+ }
+ else
+ {
+ AddAIActive( playerID - MAX_PLAYERS, vol );
+ }
+ }
+
+ vol->SetFrameUsed( time, (int)this );
+ }
+//END_PROFILE( "Test Volume" );
+}
diff --git a/game/code/meta/triggervolumetracker.h b/game/code/meta/triggervolumetracker.h
new file mode 100644
index 0000000..0234cfc
--- /dev/null
+++ b/game/code/meta/triggervolumetracker.h
@@ -0,0 +1,198 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 13/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef TRIGGERVOLUMETRACKER_H
+#define TRIGGERVOLUMETRACKER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <raddebugwatch.hpp>
+#include <p3d/inventory.hpp>
+
+#include <meta/triggervolume.h>
+
+#ifndef WORLD_BUILDER
+#include <constants/maxplayers.h>
+#include <events/eventlistener.h>
+#include <meta/locatortypes.h>
+#else
+#include "../constants/maxplayers.h"
+#include "../events/eventlistener.h"
+#include "locatortypes.h"
+#endif
+
+#include <simcollision/collisionvolume.hpp>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+
+namespace sim
+{
+ enum CollisionVolumeTypeEnum;
+}
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class TriggerVolumeTracker : public EventListener
+{
+ public:
+ // Static Methods for accessing this singleton.
+ static void CreateInstance();
+ static TriggerVolumeTracker* GetInstance();
+ static void DestroyInstance();
+
+ void Cleanup();
+
+ void Update( int elapsedTime );
+
+ void AddTrigger( TriggerVolume* triggerVolume );
+ void RemoveTrigger( TriggerVolume* triggerVolume );
+ void UnregisterFromInventory( tInventory* inv );
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void Render();
+
+ unsigned int GetNumTriggers() { return( mTriggerCount ); }
+ TriggerVolume* GetTrigger( unsigned int index ) { return( mTriggerVolumes[ index ] ); }
+ TriggerVolume* GetTriggerByName( const char* name );
+
+ void RegisterAI( Vehicle* ai, int locatorTypes = ( 1 << LocatorType::EVENT ) );
+ void UnregisterAI( Vehicle* ai );
+ Vehicle* GetAI( int id );
+ void RegisterLocatorTypeForAI( LocatorType::Type type, Vehicle* ai );
+ void UnregisterLocatorTypeForAI( LocatorType::Type type, Vehicle* ai );
+
+ void IgnoreTriggers( bool on );
+
+ void ActivateNearestInteriorLoadZone(int iPlayerID, rmt::Vector& irPosnRef, float iMaxDistance);
+
+ // MS9: yadda yadda
+ void SetTriggerSphere( tDrawable* triggersphere );
+
+ void ResetDynaloadZones();
+
+#ifndef RAD_RELEASE
+#ifndef WORLD_BUILDER
+ pddiShader* GetMaterial(void);
+#endif
+#endif
+ tDrawable* mpTriggerSphere;
+
+ protected:
+ private:
+ TriggerVolumeTracker();
+ virtual ~TriggerVolumeTracker();
+
+ //Prevent wasteful constructor creation.
+ TriggerVolumeTracker( const TriggerVolumeTracker& triggerVolumeTracker );
+ TriggerVolumeTracker& operator=( const TriggerVolumeTracker& triggerVolumeTracker );
+
+ // Pointer to the one and only instance of this singleton.
+ static TriggerVolumeTracker* spInstance;
+
+ void AddActive( int playerID, TriggerVolume* triggerVolume );
+ void RemoveActive( int playerID, int index );
+ void CheckForActiveVolumes( int playerID, rmt::Vector& center, float radius );
+ void AddAIActive( int playerID, TriggerVolume* triggerVolume );
+ void RemoveAIActive( int playerID, int index );
+
+ void TestVolume( TriggerVolume* vol, rmt::Vector& center, float radius, int playerID, unsigned int time );
+
+ enum
+ {
+ MAX_VOLUMES = 500,
+ MAX_ACTIVE = 20,
+ MAX_AI = 10,
+ MAX_AI_VOLUMES = 100
+ };
+
+ unsigned int mTriggerCount;
+ TriggerVolume* mTriggerVolumes[ MAX_VOLUMES ];
+
+ unsigned int mActiveCount[ MAX_PLAYERS ];
+ TriggerVolume* mActiveVolumes[ MAX_PLAYERS ][ MAX_ACTIVE ];
+
+ unsigned int mAICount[ MAX_AI ];
+ TriggerVolume* mActiveAIVolumes[ MAX_AI ][ MAX_AI_VOLUMES ];
+ unsigned int mNumRegisteredAI;
+
+ struct RegisteredAI
+ {
+ RegisteredAI() : mTriggerTypes( 0 ), mVehicle( NULL ) {};
+ int mTriggerTypes;
+ Vehicle* mVehicle;
+ };
+
+ RegisteredAI mRegisteredVehicles[ MAX_AI ];
+
+ bool mIgnoreTriggers;
+
+ static rmt::Vector sP1, sP2, sP3, sP4;
+ static sim::CollisionVolumeTypeEnum sColType;
+
+#ifndef RAD_RELEASE
+ bool mDisplayEnable[ LocatorType::NUM_TYPES ];
+ bool mDisplayAmbients;
+
+ bool mDisplayActive;
+ bool mDisplayInactive;
+ bool mDisplayAll;
+
+ pddiShader* material;
+#endif
+};
+
+#ifndef WORLD_BUILDER
+// A little syntactic sugar for getting at this singleton.
+inline TriggerVolumeTracker* GetTriggerVolumeTracker() { return( TriggerVolumeTracker::GetInstance() ); }
+#endif
+
+//=============================================================================
+// TriggerVolumeTracker::SetTriggerSphere
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tDrawable* triggersphere )
+//
+// Return: void
+//
+//=============================================================================
+inline void TriggerVolumeTracker::SetTriggerSphere( tDrawable* triggersphere )
+{
+ //rAssert( triggersphere != NULL );
+ mpTriggerSphere = triggersphere;
+}
+
+//=============================================================================
+// TriggerVolumeTracker::IgnoreTriggers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool on )
+//
+// Return: void
+//
+//=============================================================================
+inline void TriggerVolumeTracker::IgnoreTriggers( bool on )
+{
+ mIgnoreTriggers = on;
+}
+
+#endif //TRIGGERVOLUMETRACKER_H
+
diff --git a/game/code/meta/zoneeventlocator.cpp b/game/code/meta/zoneeventlocator.cpp
new file mode 100644
index 0000000..88cb8e7
--- /dev/null
+++ b/game/code/meta/zoneeventlocator.cpp
@@ -0,0 +1,330 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ZoneEventLocator.cpp
+//
+// Description: Implement ZoneEventLocator
+//
+// History: 18/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <meta/ZoneEventLocator.h>
+#include <meta/locatorevents.h>
+#include <memory/srrmemory.h>
+#include <events/eventmanager.h>
+
+#include <string.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ZoneEventLocator::ZoneEventLocator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ZoneEventLocator::ZoneEventLocator() :
+ mZoneSize( 0 ),
+ mInteriorSection( 0 ),
+ mInteriorLoad(false),
+ mInteriorDump(false),
+ mZone( NULL )
+{
+ SetEventType( LocatorEvent::DYNAMIC_ZONE );
+}
+
+//==============================================================================
+// ZoneEventLocator::~ZoneEventLocator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ZoneEventLocator::~ZoneEventLocator()
+{
+ if ( mZone )
+ {
+ delete[] mZone;
+ mZone = NULL;
+ mZoneSize = 0;
+ }
+}
+
+//=============================================================================
+// ZoneEventLocator::GetZone
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+const char* const ZoneEventLocator::GetZone() const
+{
+ return mZone;
+}
+//=============================================================================
+// ZoneEventLocator::GetZone
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+int ZoneEventLocator::GetNumLoadZones()
+{
+ return mLoadZones.mUseSize;
+}
+//=============================================================================
+// ZoneEventLocator::GetZone
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+int ZoneEventLocator::GetNumDumpZones()
+{
+ return mDumpZones.mUseSize;
+}
+
+//=============================================================================
+// ZoneEventLocator::GetZone
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+int ZoneEventLocator::GetNumLWSActivates()
+{
+ return mLWSActivates.mUseSize;
+}
+//=============================================================================
+// ZoneEventLocator::GetZone
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+int ZoneEventLocator::GetNumLWSDeactivates()
+{
+ return mLWSDeactivates.mUseSize;
+}
+//=============================================================================
+// ZoneEventLocator::GetZone
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+const char* const ZoneEventLocator::GetLoadZone(int i)
+{
+ return &(mZone[mLoadZones[i]]);
+}
+//=============================================================================
+// ZoneEventLocator::GetZone
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+const char* const ZoneEventLocator::GetDumpZone(int i)
+{
+ return &(mZone[mDumpZones[i]]);
+}
+//=============================================================================
+// ZoneEventLocator::GetZone
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+const char* const ZoneEventLocator::GetLWSActivates(int i)
+{
+ return &(mZone[mLWSActivates[i]]);
+}
+//=============================================================================
+// ZoneEventLocator::GetZone
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+const char* const ZoneEventLocator::GetLWSDeactivates(int i)
+{
+ return &(mZone[mLWSDeactivates[i]]);
+}
+
+//=============================================================================
+// ZoneEventLocator::GetInteriorSection
+//=============================================================================
+const char* ZoneEventLocator::GetInteriorSection()
+{
+ return &(mZone[mInteriorSection]);
+}
+
+//=============================================================================
+// ZoneEventLocator::SetZone
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* zone )
+//
+// Return: void
+//
+//=============================================================================
+void ZoneEventLocator::SetZone( const char* zone )
+{
+ int zoneStrLength;
+
+ rAssert( mZoneSize );
+ rAssert( strlen(zone) + 1 <= mZoneSize );
+
+ zoneStrLength = strlen(zone);
+
+ strncpy( mZone, zone, zoneStrLength );
+ mZone[zoneStrLength] = '\0';
+
+ mLoadZones.Allocate(mZoneSize/6);
+ mDumpZones.Allocate(mZoneSize/6);
+
+ mLWSDeactivates.Allocate(mZoneSize/6);
+ mLWSActivates.Allocate(mZoneSize/6);
+
+ int i, startPosn;
+ for(startPosn=0,i=0; i<zoneStrLength; i++)
+ {
+ switch(mZone[i])
+ {
+ case ';':
+ mLoadZones.Add(startPosn);
+ mZone[i]='\0';
+ i++;
+ startPosn = i;
+ break;
+ case ':':
+ mDumpZones.Add(startPosn);
+ mZone[i]='\0';
+ i++;
+ startPosn = i;
+ break;
+ case '@':
+ mInteriorLoad = true;
+ mInteriorSection = startPosn;
+ mLoadZones.Add(startPosn);
+ mZone[i]='\0';
+ i++;
+ startPosn = i;
+ break;
+ case '$':
+ mInteriorDump = true;
+ mDumpZones.Add(startPosn);
+ mZone[i]='\0';
+ i++;
+ startPosn = i;
+ break;
+ case '&':
+ mLWSDeactivates.Add(startPosn);
+ mZone[i]='\0';
+ i++;
+ startPosn = i;
+ break;
+ case '*':
+ mLWSActivates.Add(startPosn);
+ mZone[i]='\0';
+ i++;
+ startPosn = i;
+ break;
+ case '\0':
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// ZoneEventLocator::SetZoneSize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned char size )
+//
+// Return: void
+//
+//=============================================================================
+void ZoneEventLocator::SetZoneSize( unsigned char size )
+{
+ MEMTRACK_PUSH_GROUP( "ZoneEventLocator" );
+ rAssert( !mZoneSize );
+
+ if ( mZone )
+ {
+ rAssertMsg( false, "Why is someone changing this! BAD! Get Cary!" );
+ mZoneSize = 0;
+ delete[] mZone;
+ mZone = NULL;
+ }
+ else
+ {
+ mZoneSize = size + 1;
+ }
+
+ mZone = new(GMA_LEVEL_OTHER) char[ mZoneSize ];
+ MEMTRACK_POP_GROUP( "ZoneEventLocator" );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/meta/zoneeventlocator.h b/game/code/meta/zoneeventlocator.h
new file mode 100644
index 0000000..fe45ca3
--- /dev/null
+++ b/game/code/meta/zoneeventlocator.h
@@ -0,0 +1,101 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: zoneeventlocator.h
+//
+// Description: Blahblahblah
+//
+// History: 18/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef ZONEEVENTLOCATOR_H
+#define ZONEEVENTLOCATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+#include <meta/eventlocator.h>
+#include <render/Culling/UseArray.h>
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ZoneEventLocator : public EventLocator
+{
+public:
+ ZoneEventLocator();
+ virtual ~ZoneEventLocator();
+
+ virtual LocatorType::Type GetDataType() const;
+
+ const char* const GetZone() const;
+ void SetZone( const char* zone );
+ void SetZoneSize( unsigned char size ); //This is to prevent fragmentation.
+
+ const char* const GetLoadZone(int i);
+ const char* const GetDumpZone(int i);
+ const char* const GetInterLoadZone(int i);
+ const char* const GetInterDumpZone(int i);
+ const char* const GetLWSActivates(int i);
+ const char* const GetLWSDeactivates(int i);
+
+ int GetNumLoadZones();
+ int GetNumDumpZones();
+ int GetNumLWSActivates();
+ int GetNumLWSDeactivates();
+
+ bool IsInteriorLoad(void) { return mInteriorLoad; }
+ bool IsInteriorDump(void) { return mInteriorDump; }
+ const char* GetInteriorSection();
+
+private:
+
+ unsigned char mZoneSize;
+ UseArray<int> mLoadZones;
+ UseArray<int> mDumpZones;
+ UseArray<int> mLWSActivates;
+ UseArray<int> mLWSDeactivates;
+ int mInteriorSection;
+
+ bool mInteriorLoad;
+ bool mInteriorDump;
+ char* mZone;
+
+
+ //Prevent wasteful constructor creation.
+ ZoneEventLocator( const ZoneEventLocator& zoneeventlocator );
+ ZoneEventLocator& operator=( const ZoneEventLocator& zoneeventlocator );
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ZoneEventLocator::GetDataType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline LocatorType::Type ZoneEventLocator::GetDataType() const
+{
+ return( LocatorType::DYNAMIC_ZONE );
+}
+
+#endif //ZONEEVENTLOCATOR_H
diff --git a/game/code/mission/allmission.cpp b/game/code/mission/allmission.cpp
new file mode 100644
index 0000000..0d66cef
--- /dev/null
+++ b/game/code/mission/allmission.cpp
@@ -0,0 +1,13 @@
+#include <mission/gameplaymanager.cpp>
+#include <mission/haspresentationinfo.cpp>
+#include <mission/mission.cpp>
+#include <mission/missionmanager.cpp>
+#include <mission/missionscriptloader.cpp>
+#include <mission/missionstage.cpp>
+#include <mission/animatedicon.cpp>
+#include <mission/bonusmissioninfo.cpp>
+#include <mission/nocopbonusobjective.cpp>
+#include <mission/nodamagebonusobjective.cpp>
+#include <mission/timeremainbonusobjective.cpp>
+#include <mission/racepositionbonusobjective.cpp>
+#include <mission/statepropcollectible.cpp> \ No newline at end of file
diff --git a/game/code/mission/animatedicon.cpp b/game/code/mission/animatedicon.cpp
new file mode 100644
index 0000000..11f7cf2
--- /dev/null
+++ b/game/code/mission/animatedicon.cpp
@@ -0,0 +1,806 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: AnimatedIcon.cpp
+//
+// Description: Implement AnimatedIcon
+//
+// History: 10/3/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <radtime.hpp>
+#include <raddebug.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/effects/particlesystem.hpp>
+#include <p3d/view.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/AnimatedIcon.h>
+
+#include <memory/srrmemory.h>
+
+#include <mission/gameplaymanager.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <render/culling/worldscene.h>
+
+#include <debug/profiler.h>
+
+#include <main/game.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+AnimatedIcon* AnimatedIcon::sAnimatedIconPool = NULL;
+unsigned int AnimatedIcon::sNumAllocated = 0;
+
+// Setup default parameters for our watcher variables
+#ifdef RAD_TUNE
+float AnimatedIcon::sDbgMinArrowScale = MIN_ARROW_SCALE;
+float AnimatedIcon::sDbgMaxArrowScale = MAX_ARROW_SCALE;
+float AnimatedIcon::sDbgMinArrowScaleDist = MIN_ARROW_SCALE_DIST;
+float AnimatedIcon::sDbgMaxArrowScaleDist = MAX_ARROW_SCALE_DIST;
+bool AnimatedIcon::sDbgEnableArrowScaling = false;
+#endif
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// AnimatedIcon::AnimatedIcon
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AnimatedIcon::AnimatedIcon() :
+ mDSGEntity( NULL ),
+ mRenderLayer( RenderEnums::LevelSlot ),
+ mFlags( 0 )
+{
+#ifdef RAD_TUNE
+ AttachWatcherVariables();
+#endif
+}
+
+//==============================================================================
+// AnimatedIcon::~AnimatedIcon
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AnimatedIcon::~AnimatedIcon()
+{
+ Deallocate();
+}
+
+//=============================================================================
+// AnimatedIcon::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* iconName, const rmt::Matrix& mat, bool render = true, bool oneShot = false )
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::Init( const char* iconName, const rmt::Matrix& mat, bool render, bool oneShot )
+{
+ MEMTRACK_PUSH_GROUP( "Mission - Animated Icon" );
+ rAssert( iconName );
+ rAssertMsg( mDSGEntity == NULL, "Never call this twice on the same object!!!! LEAK, LEAK!" );
+
+ //========================= SET UP THE ICON
+
+ HeapMgr()->PushHeap( GetGameplayManager()->GetCurrentMissionHeap() );
+
+ SetUpContents( iconName );
+
+ mDSGEntity = new AnimIconDSG;
+
+ mDSGEntity->mTranslucent = true;
+
+ rAssert( mDSGEntity != NULL );
+
+ mDSGEntity->AddRef();
+
+ //
+ // The entity takes overship of m, so spake Devin
+ //
+ rmt::Matrix* m = new rmt::Matrix;
+ rAssert( m );
+
+ *m = mat;
+
+ mDSGEntity->LoadSetUp( m, mAnimIcon.drawable, mAnimIcon.shadowDrawable );
+
+ mRenderLayer = static_cast<RenderEnums::LayerEnum>(GetRenderManager()->rCurWorldRenderLayer());
+
+ if ( render )
+ {
+ ShouldRender( true );
+ }
+
+ SetFlag( (Flag)ONE_SHOT, oneShot );
+
+ SetUpEffects();
+
+ HeapMgr()->PopHeap( GetGameplayManager()->GetCurrentMissionHeap() );
+
+ MEMTRACK_POP_GROUP( "Mission - Animated Icon" );
+}
+
+//=============================================================================
+// AnimatedIcon::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* iconName, const rmt::Vector& pos, bool render, bool oneShot )
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::Init( const char* iconName, const rmt::Vector& pos, bool render, bool oneShot )
+{
+MEMTRACK_PUSH_GROUP( "Mission - Animated Icon" );
+ rAssert( iconName );
+ rAssertMsg( mDSGEntity == NULL, "Never call this twice on the same object!!!! LEAK, LEAK!" );
+
+ //========================= SET UP THE ICON
+
+ HeapMgr()->PushHeap( GetGameplayManager()->GetCurrentMissionHeap() );
+
+ SetUpContents( iconName );
+
+ mDSGEntity = new AnimIconDSG;
+
+ mDSGEntity->mTranslucent = true;
+
+ rAssert( mDSGEntity != NULL );
+
+ mDSGEntity->AddRef();
+
+ mDSGEntity->SetName( iconName );
+
+ //
+ // The entity takes overship of m, so spake Devin
+ //
+ rmt::Matrix* m = new rmt::Matrix;
+ rAssert( m );
+
+ m->Identity();
+ m->FillTranslate( pos );
+
+ mDSGEntity->LoadSetUp( m, mAnimIcon.drawable, mAnimIcon.shadowDrawable );
+
+ mRenderLayer = static_cast<RenderEnums::LayerEnum>(GetRenderManager()->rCurWorldRenderLayer());
+
+ if ( render )
+ {
+ ShouldRender( true );
+ }
+
+ SetFlag( (Flag)ONE_SHOT, oneShot );
+
+ SetUpEffects();
+
+ HeapMgr()->PopHeap( GetGameplayManager()->GetCurrentMissionHeap() );
+
+MEMTRACK_POP_GROUP( "Mission - Animated Icon" );
+}
+
+//=============================================================================
+// AnimatedIcon::ScaleByCameraDistance
+//=============================================================================
+// Description: Converts all billboardquadgroups in the icon drawable to quads that are
+// scaled by distance to camera
+//
+// Parameters: (float minScale, float maxScale, float minDist, float maxDist)
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::ScaleByCameraDistance( float minScale, float maxScale, float minDist, float maxDist )
+{
+ // Default to no scaling
+ mDSGEntity->SetScaleParameters( minScale, maxScale, minDist, maxDist );
+}
+
+//=============================================================================
+// AnimatedIcon::Move
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& newPos )
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::Move( const rmt::Vector& newPos )
+{
+ if ( mRenderLayer == static_cast<RenderEnums::LayerEnum>(GetRenderManager()->rCurWorldRenderLayer()) )
+ {
+ rmt::Box3D oldBox;
+ mDSGEntity->GetBoundingBox( &oldBox );
+
+ mDSGEntity->pMatrix()->FillTranslate( newPos );
+
+ if ( !GetFlag( (Flag)RENDERING ) )
+ {
+ ShouldRender( true );
+ }
+
+ GetRenderManager()->pWorldScene()->Move( oldBox, mDSGEntity );
+ mDSGEntity->RecomputeShadowPosition();
+ }
+}
+
+//=============================================================================
+// AnimatedIcon::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedMilliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::Update( unsigned int elapsedMilliseconds )
+{
+ rmt::Sphere iconBoundingSphere;
+ if ( mDSGEntity )
+ {
+ mDSGEntity->GetBoundingSphere( &iconBoundingSphere );
+ mDSGEntity->m_Visible = false;
+ }
+ else
+ {
+ iconBoundingSphere.centre = rmt::Vector(0,0,0);
+ iconBoundingSphere.radius = 1.0f;
+ }
+
+ if ( GetFlag( (Flag)RENDERING ) && GetGameplayManager()->TestPosInFrustrumOfPlayer(iconBoundingSphere.centre, 0, iconBoundingSphere.radius))
+ {
+ if ( mDSGEntity )
+ {
+ mDSGEntity->m_Visible = true;
+ }
+
+ if ( mAnimIcon.multiController )
+ {
+ BEGIN_PROFILE( mAnimIcon.multiController->GetName() );
+ //This allows me to ignore the extra updates caused by being part of
+ //a substep.
+ unsigned int frame = GetGame()->GetFrameCount();
+ if ( (mAnimIcon.multiController)->GetLastFrameUpdated() != frame )
+ {
+ (mAnimIcon.multiController)->Advance( (float)elapsedMilliseconds );
+ (mAnimIcon.multiController)->SetLastFrameUpdated( frame );
+ // Handle particle systems
+ if ( mAnimIcon.effectIndex != -1 )
+ {
+ tCompositeDrawable* compDraw = static_cast< tCompositeDrawable* >( mAnimIcon.drawable );
+ tCompositeDrawable::DrawableEffectElement* effectElement = static_cast< tCompositeDrawable::DrawableEffectElement* > ( compDraw->GetDrawableElement( mAnimIcon.effectIndex ) );
+ tParticleSystem* system = static_cast< tParticleSystem* >( effectElement->GetDrawable() );
+ if ( system )
+ {
+ rmt::Matrix mat;
+ mat.Identity();
+ system->Update( &mat );
+ }
+ }
+
+ if ( GetFlag( (Flag)ONE_SHOT ) && (mAnimIcon.multiController)->LastFrameReached() > 0 )
+ {
+ //This is a way to remove non-cyclic animations. Beware,
+ //if the animation is intended to be cyclic it will disappear...
+ ShouldRender( false );
+ }
+ }
+ END_PROFILE( mAnimIcon.multiController->GetName() );
+ }
+
+ if ( mAnimIcon.shadowController != NULL )
+ {
+ mAnimIcon.shadowController->Advance( (float)elapsedMilliseconds );
+ }
+
+#ifdef RAD_TUNE
+ if ( sDbgEnableArrowScaling )
+ {
+ // Jeff P. wants to have watcher variables to tune the AI scaling.
+ // If we are in tune mode, lets override the input parameters and set them to be
+ // the values of some static watcher variables
+ ScaleByCameraDistance( sDbgMinArrowScale, sDbgMaxArrowScale, sDbgMinArrowScaleDist, sDbgMaxArrowScaleDist );
+ }
+#endif
+ }
+}
+
+//=============================================================================
+// AnimatedIcon::Reset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::Reset()
+{
+ if ( mAnimIcon.multiController )
+ {
+ (mAnimIcon.multiController)->Reset();
+ }
+}
+
+//=============================================================================
+// AnimatedIcon::ShouldRender
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool shouldRender )
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::ShouldRender( bool shouldRender )
+{
+ if ( shouldRender && !GetFlag( (Flag)RENDERING ) )
+ {
+ (reinterpret_cast<WorldRenderLayer*>(GetRenderManager()->mpLayer( mRenderLayer )))->pWorldScene()->Add( mDSGEntity );
+ SetFlag( (Flag)RENDERING, true );
+ }
+ else if ( !shouldRender && GetFlag( (Flag)RENDERING ) )
+ {
+ (reinterpret_cast<WorldRenderLayer*>(GetRenderManager()->mpLayer( mRenderLayer )))->pWorldScene()->RemoveQuietFail( mDSGEntity );
+ SetFlag( (Flag)RENDERING, false );
+ }
+}
+
+//=============================================================================
+// AnimatedIcon::GetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector& pos )
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::GetPosition( rmt::Vector& pos )
+{
+ mDSGEntity->GetPosition( &pos );
+}
+
+//=============================================================================
+// ::operator new
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( size_t size )
+//
+// Return: void
+//
+//=============================================================================
+void* AnimatedIcon::operator new( size_t size )
+{
+ rAssert( sAnimatedIconPool != NULL );
+ rAssert( sNumAllocated < MAX_ICONS );
+
+ void* data = NULL;
+ if ( sNumAllocated < MAX_ICONS )
+ {
+ unsigned int i;
+ for ( i = 0; i < MAX_ICONS; ++i )
+ {
+ if ( !sAnimatedIconPool[ i ].mAllocated )
+ {
+ sAnimatedIconPool[ i ].mAllocated = true;
+ data = static_cast<void*>(&sAnimatedIconPool[ i ]);
+ ++sNumAllocated;
+ break;
+ }
+ }
+ }
+
+ return data;
+}
+
+//=============================================================================
+// ::operator new
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( size_t size, GameMemoryAllocator allocator )
+//
+// Return: void
+//
+//=============================================================================
+void* AnimatedIcon::operator new( size_t size, GameMemoryAllocator allocator )
+{
+ return new AnimatedIcon();
+}
+
+//=============================================================================
+// ::operator delete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* mem )
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::operator delete( void* mem )
+{
+ AnimatedIcon* icon = static_cast<AnimatedIcon*>( mem );
+
+ unsigned int i;
+ for ( i = 0; i < MAX_ICONS; ++i )
+ {
+ if ( icon == &sAnimatedIconPool[ i ] )
+ {
+ icon->Deallocate();
+ sNumAllocated--;
+ break;
+ }
+ }
+
+ rAssert( i < MAX_ICONS );
+}
+
+//=============================================================================
+// void operator delete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* mem, GameMemoryAllocator allocator )
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::operator delete( void* mem, GameMemoryAllocator allocator )
+{
+ AnimatedIcon::operator delete( mem );
+}
+
+//=============================================================================
+// AnimatedIcon::InitAnimatedIcons
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( GameMemoryAllocator allocator )
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::InitAnimatedIcons( GameMemoryAllocator allocator )
+{
+ rAssert( sAnimatedIconPool == NULL );
+
+ if ( sAnimatedIconPool == NULL )
+ {
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( allocator );
+ #endif
+ sAnimatedIconPool = new AnimatedIcon[ MAX_ICONS ];
+
+ unsigned int i;
+ for ( i = 0; i < MAX_ICONS; ++i )
+ {
+ sAnimatedIconPool[ i ].mAllocated = false;
+ }
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( allocator );
+ #endif
+ }
+}
+
+//=============================================================================
+// AnimatedIcon::ShutdownAnimatedIcons
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::ShutdownAnimatedIcons()
+{
+ rAssert( sNumAllocated == 0 );
+
+ //Paranoia Test
+#ifdef RAD_DEBUG
+ unsigned int i;
+ for ( i = 0; i < MAX_ICONS; ++i )
+ {
+ rAssert( sAnimatedIconPool[ i ].mDSGEntity == NULL );
+ }
+#endif
+
+ delete[] sAnimatedIconPool;
+ sAnimatedIconPool = NULL;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// AnimatedIcon::SetFlag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Flag flag, bool value )
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::SetFlag( Flag flag, bool value )
+{
+ if ( value )
+ {
+ mFlags |= ( 1 << flag );
+ }
+ else
+ {
+ mFlags &= ~( 1 << flag );
+ }
+}
+
+//=============================================================================
+// AnimatedIcon::GetFlag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Flag flag )
+//
+// Return: bool
+//
+//=============================================================================
+bool AnimatedIcon::GetFlag( Flag flag ) const
+{
+ return ( (mFlags & ( 1 << flag )) > 0 );
+}
+
+//=============================================================================
+// AnimatedIcon::Deallocate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::Deallocate()
+{
+ if ( mDSGEntity )
+ {
+ //Scary, scary!
+ if ( GetFlag( (Flag)RENDERING ) )
+ {
+ ShouldRender(false);
+ }
+
+ mDSGEntity->Release();
+ }
+
+ mFlags = 0;
+ mAllocated = false;
+
+ mDSGEntity = NULL;
+
+ tRefCounted::Release(mAnimIcon.drawable);
+ tRefCounted::Release(mAnimIcon.multiController);
+ tRefCounted::Release(mAnimIcon.shadowController);
+ tRefCounted::Release(mAnimIcon.shadowDrawable);
+}
+
+//=============================================================================
+// AnimatedIcon::SetUpContents
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* iconName )
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::SetUpContents( const char* iconName )
+{
+ tRefCounted::Assign(mAnimIcon.drawable, p3d::find<tDrawable>( iconName ));
+ if ( mAnimIcon.drawable == NULL )
+ {
+ rReleasePrintf( "ERORR!!!!! Can not init Animicon with missing (%s) drawable!!!!\n", iconName );
+ tRefCounted::Assign(mAnimIcon.drawable, p3d::find<tDrawable>( "triggersphere_000" ));
+ }
+
+ rAssert( mAnimIcon.drawable != NULL );
+
+ char controllerName[256];
+ sprintf( controllerName, "%s_controller", iconName );
+
+ tRefCounted::Assign(mAnimIcon.multiController, p3d::find<tMultiController>( controllerName ) );
+
+ // Try and find the shadows (Collector cards have them, others are optional)
+ // Append _shadow to the icon name to get the shadow name
+ char shadowDrawableName[256];
+ sprintf( shadowDrawableName, "%s_shadow", iconName );
+ tRefCounted::Assign(mAnimIcon.shadowDrawable, p3d::find<tDrawable>( shadowDrawableName ));
+ // Now try and find the shadow multicontroller
+ char shadowControllerName[256];
+ sprintf( shadowControllerName, "%s_controller", shadowDrawableName );
+ tRefCounted::Assign(mAnimIcon.shadowController, p3d::find<tMultiController>( shadowControllerName ) );
+}
+
+//=============================================================================
+// AnimatedIcon::SetUpEffects
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void AnimatedIcon::SetUpEffects()
+{
+ //Only do this once.
+ rAssert( mAnimIcon.effectIndex == -1 );
+
+ // Try and find particle systems
+ tCompositeDrawable* compDraw = dynamic_cast< tCompositeDrawable* >( mAnimIcon.drawable );
+ if ( compDraw )
+ {
+ for ( int i = 0 ; i < compDraw->GetNumDrawableElement() ; i++ )
+ {
+ tCompositeDrawable::DrawableEffectElement* effectElement = dynamic_cast< tCompositeDrawable::DrawableEffectElement* > ( compDraw->GetDrawableElement( i ) );
+ if (effectElement)
+ {
+ tEffect* effect = static_cast< tEffect* >( effectElement->GetDrawable() );
+ if ( effect != NULL )
+ {
+ tParticleSystem* ps = static_cast< tParticleSystem* >( effect );
+ tParticleSystemFactory* factory = static_cast< tParticleSystemFactory* >( ps->GetFactory() );
+ factory->SetAlwaysUpdateAfterDisplay( false );
+ ps->ConvertEmittersToLocal();
+ mAnimIcon.effectIndex = i;
+ break;
+ }
+ }
+ }
+ }
+}
+
+#ifdef RAD_TUNE
+void AnimatedIcon::AttachWatcherVariables()
+{
+ static bool sDbgArrowsAttachedToWatcher = false; // has the watcher variables been
+ // attached already ? dont attach them more than once
+ if ( sDbgArrowsAttachedToWatcher == false )
+ {
+ const char* ANIMATED_ICON_DBG_NAMESPACE = "Animated Icons";
+
+ radDbgWatchAddBoolean( &sDbgEnableArrowScaling, "Enable Arrow Scaling", ANIMATED_ICON_DBG_NAMESPACE );
+ radDbgWatchAddFloat( &sDbgMinArrowScale, "Min Arrow Scale", ANIMATED_ICON_DBG_NAMESPACE, NULL, NULL, 0, 20.0f );
+ radDbgWatchAddFloat( &sDbgMaxArrowScale, "Max Arrow Scale", ANIMATED_ICON_DBG_NAMESPACE, NULL, NULL, 0, 20.0f );
+ radDbgWatchAddFloat( &sDbgMinArrowScaleDist, "Min Arrow Scale Distance", ANIMATED_ICON_DBG_NAMESPACE, NULL, NULL, 0, 250.0f );
+ radDbgWatchAddFloat( &sDbgMaxArrowScaleDist, "Max Arrow Scale Distance", ANIMATED_ICON_DBG_NAMESPACE, NULL, NULL, 0, 250.0f );
+ }
+ sDbgArrowsAttachedToWatcher = true;
+}
+
+#endif
+
+AnimatedIcon::AnimIconDSG::AnimIconDSG():
+m_ScalingEnabled( false )
+{
+
+ // Default to no scaling
+ SetScaleParameters( 1.0f, 1.0f, 1.0f, 1.0f );
+}
+
+AnimatedIcon::AnimIconDSG::~AnimIconDSG(){
+
+}
+
+float AnimatedIcon::AnimIconDSG::CalcScale()
+{
+ rmt::Vector objPosition;
+ GetPosition( &objPosition );
+ // Determine the distance to the camera
+ rmt::Vector camPosition;
+ p3d::context->GetView()->GetCamera()->GetWorldPosition( &camPosition );
+ float distToBillboard = (camPosition - objPosition ).Magnitude();
+ float billboardScale = m_Slope * ( distToBillboard - m_NearDist ) + m_MinSize;
+ billboardScale = rmt::Clamp( billboardScale, m_MinSize, m_MaxSize );
+
+ return billboardScale;
+}
+
+void AnimatedIcon::AnimIconDSG::SetScaleParameters( float minSize, float maxSize, float nearDist, float farDist )
+{
+ // Compute m
+ // The slope of the equation determining icon scaling
+
+ if ( ( farDist - nearDist ) > 0.01f )
+ {
+ // m = ( maxSize - 1.0f ) / ( far - near )
+ m_MaxSize = maxSize;
+ m_MinSize = minSize;
+ m_NearDist = nearDist;
+ m_Slope = ( maxSize - minSize ) / ( farDist - nearDist );
+ m_ScalingEnabled = true;
+ }
+ else
+ {
+ m_ScalingEnabled = false;
+ }
+
+}
+
+// Display with scaling.
+void AnimatedIcon::AnimIconDSG::Display()
+{
+ if( m_Visible )
+ {
+ extern bool g_ParticleLOD;
+ g_ParticleLOD = true;
+ bool wasBBQManagerEnabled = BillboardQuadManager::sEnabled;
+ BillboardQuadManager::Enable();
+ if ( m_ScalingEnabled )
+ {
+ float scale = CalcScale();
+ rmt::Matrix scaleMat;
+ scaleMat.Identity();
+ scaleMat.FillScale( scale );
+
+ // Apply rotation/transform THEN scale to the local object
+ rmt::Matrix origMatrix = *mpMatrix;
+ mpMatrix->Mult( scaleMat, origMatrix );
+
+ InstStatEntityDSG::Display();
+
+ *mpMatrix = origMatrix;
+ }
+ else
+ {
+ InstStatEntityDSG::Display();
+ }
+ if ( wasBBQManagerEnabled == false )
+ {
+ BillboardQuadManager::Disable();
+ }
+ g_ParticleLOD = false;
+ }
+}
diff --git a/game/code/mission/animatedicon.h b/game/code/mission/animatedicon.h
new file mode 100644
index 0000000..723edc1
--- /dev/null
+++ b/game/code/mission/animatedicon.h
@@ -0,0 +1,181 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: animatedicon.h
+//
+// Description: Blahblahblah
+//
+// History: 10/3/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef ANIMATEDICON_H
+#define ANIMATEDICON_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+#include <render/enums/renderenums.h>
+#include <memory/srrmemory.h>
+#include <render/DSG/inststatentitydsg.h>
+
+//========================================
+// Forward References
+//========================================
+class tCompositeDrawable;
+class tDrawable;
+class tMultiController;
+class InstStatEntityDSG;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+static char* EXCLAMATION = "exclamation";
+static const char* GIFT = "gift";
+static const char* ARROW = "arrow";
+static const char* ARROW_CHASE = "arrow_evade";
+static const char* ARROW_EVADE = "arrow_chase";
+static const char* ARROW_RACE = "arrow_race";
+static const char* ARROW_DESTROY = "arrow_destroy";
+
+// Constants controlling the way the arrows scale as they get go farther away
+const float MIN_ARROW_SCALE = 1.8f;
+const float MAX_ARROW_SCALE = 5.0f;
+const float MIN_ARROW_SCALE_DIST = 10.0f;
+const float MAX_ARROW_SCALE_DIST = 80.0f;
+
+
+
+class AnimatedIcon
+{
+public:
+ enum { MAX_ICONS = 100 };
+
+ AnimatedIcon();
+ virtual ~AnimatedIcon();
+
+ void Init( const char* iconName, const rmt::Vector& pos, bool render = true, bool oneShot = false );
+ void Init( const char* iconName, const rmt::Matrix& mat, bool render = true, bool oneShot = false );
+
+ // Convert the icon's billboards to be scaled by the distance to the camera
+ // Scaling is linear between minDist and maxDist with a scaling between minScale and maxScale
+ void ScaleByCameraDistance( float minScale, float maxScale, float minDist, float maxDist );
+
+ void Move( const rmt::Vector& newPos );
+ void Update( unsigned int elapsedMilliseconds );
+ void Reset();
+ void ShouldRender( bool shouldRender );
+ bool IsRendering() const;
+
+ void GetPosition( rmt::Vector& pos );
+
+ static void* operator new( size_t size );
+ static void* operator new( size_t size, GameMemoryAllocator allocator );
+ static void operator delete( void* mem );
+ static void operator delete( void* mem, GameMemoryAllocator allocator );
+
+ static void InitAnimatedIcons( GameMemoryAllocator allocator );
+ static void ShutdownAnimatedIcons();
+
+ // A class that has useful functions for scaling an object after local
+ // transformations but before local->world is applied
+ class AnimIconDSG : public InstStatEntityDSG
+ {
+ public:
+ AnimIconDSG();
+ virtual ~AnimIconDSG();
+ virtual void Display();
+ // minSize - icon will never be smaller than this
+ // maxSize - icon will never be bigger than this
+ // nearDist - the distance at which size will be minSize
+ // farDist - distance where size will be maxSize
+ // In between, it is scaled linearly
+ void SetScaleParameters( float minSize, float maxSize, float nearDist, float farDist );
+ bool m_Visible;
+
+ private:
+
+ // Calculates the distance to camera and returns a scalar scale value
+ // indicating how much scale to apply to the object
+ float CalcScale();
+ float m_Slope; // Slope of the scaling equation
+ // m_Slope = ( m_MaxSize - m_MinSize ) / ( far - m_NearDist )
+ float m_MaxSize; // scale factor maximum
+ float m_MinSize; // scale factor minimum
+ float m_NearDist; // distance where the scaling is at minSize
+ bool m_ScalingEnabled;
+ };
+
+private:
+ static AnimatedIcon* sAnimatedIconPool;
+ static unsigned int sNumAllocated;
+
+ struct Icon
+ {
+ Icon() : drawable( NULL ), multiController( NULL ), effectIndex( -1 ), shadowDrawable( NULL ), shadowController( NULL ) {};
+ tDrawable* drawable;
+ tMultiController* multiController;
+ int effectIndex;
+
+ tDrawable* shadowDrawable;
+ tMultiController* shadowController;
+ };
+
+ AnimIconDSG* mDSGEntity;
+ Icon mAnimIcon;
+ RenderEnums::LayerEnum mRenderLayer;
+
+ enum Flag
+ {
+ ONE_SHOT,
+ RENDERING
+ };
+
+ unsigned int mFlags;
+
+ char mAllocated; //Slighty wasteful due to padding.
+
+ void SetFlag( Flag flag, bool value );
+ bool GetFlag( Flag flag ) const;
+
+ void Deallocate();
+
+ void SetUpContents( const char* iconName );
+ void SetUpEffects();
+
+ // Jeff. P's tune mode watcher variables for determining the desired
+ // arrow scaling parameters
+#ifdef RAD_TUNE
+ static float sDbgMinArrowScale;
+ static float sDbgMaxArrowScale;
+ static float sDbgMinArrowScaleDist;
+ static float sDbgMaxArrowScaleDist;
+ static bool sDbgEnableArrowScaling;
+ static void AttachWatcherVariables();
+#endif
+
+ //Prevent wasteful constructor creation.
+ AnimatedIcon( const AnimatedIcon& animatedicon );
+ AnimatedIcon& operator=( const AnimatedIcon& animatedicon );
+};
+
+//=============================================================================
+// AnimatedIcon::IsRendering
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool AnimatedIcon::IsRendering() const
+{
+ return GetFlag( RENDERING );
+};
+
+#endif //ANIMATEDICON_H
diff --git a/game/code/mission/bonusmissioninfo.cpp b/game/code/mission/bonusmissioninfo.cpp
new file mode 100644
index 0000000..98ac009
--- /dev/null
+++ b/game/code/mission/bonusmissioninfo.cpp
@@ -0,0 +1,792 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: BonusMissionInfo.cpp
+//
+// Description: Implement BonusMissionInfo
+//
+// History: 11/19/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/BonusMissionInfo.h>
+#include <mission/animatedicon.h>
+#include <mission/gameplaymanager.h>
+#include <mission/missionmanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <meta/eventlocator.h>
+#include <meta/triggervolumetracker.h>
+#include <meta/carstartlocator.h>
+#include <events/eventmanager.h>
+#include <events/eventenum.h>
+#include <events/eventdata.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/vehiclecentral.h>
+
+#include <memory/srrmemory.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guiscreenmissionload.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// BonusMissionInfo::BonusMissionInfo
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+BonusMissionInfo::BonusMissionInfo() :
+ mIcon( NULL ),
+ mAlternateIcon( NULL ),
+ mMissionNum( -1 ),
+ mEventLocator( NULL ),
+ mIsOneShot( false ),
+ mIsCompleted( false ),
+ mChar1Pos( NULL ),
+ mChar2Pos( NULL ),
+ mCarPos( NULL ),
+ mChar1Rotation( 0.0f ),
+ mChar2Rotation( 0.0f ),
+ mCarRotation( 0.0f ),
+ mReset( false ),
+ mMoved( false ),
+ mHideAnimatedIcon( false ),
+ mHidTheCar( false ),
+ mHidDefault( false ),
+ mHudMapIconID( -1 )
+{
+
+ mDialogEventData.char1 = NULL;
+ mDialogEventData.char2 = NULL;
+
+ mChar1OldPos.Set( 0.0f, 0.0f, 0.0f );
+ mChar2OldPos.Set( 0.0f, 0.0f, 0.0f );
+ mCarOldPos.Set( 0.0f, 0.0f, 0.0f );
+}
+
+//==============================================================================
+// BonusMissionInfo::~BonusMissionInfo
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+BonusMissionInfo::~BonusMissionInfo()
+{
+ if ( mIcon )
+ {
+ delete mIcon;
+ mIcon = NULL;
+ }
+
+ if ( mAlternateIcon )
+ {
+ delete mAlternateIcon;
+ mAlternateIcon = NULL;
+ }
+
+ if ( mEventLocator )
+ {
+ mEventLocator->Release();
+ mEventLocator = NULL;
+ }
+
+ if( mDialogEventData.char1 != NULL )
+ {
+ mDialogEventData.char1->Release();
+ mDialogEventData.char1 = NULL;
+ }
+ if( mDialogEventData.char2 != NULL )
+ {
+ mDialogEventData.char2->Release();
+ mDialogEventData.char2 = NULL;
+ }
+
+ if ( mChar1Pos )
+ {
+ mChar1Pos->Release();
+ mChar1Pos = NULL;
+ }
+
+ if ( mChar2Pos )
+ {
+ mChar2Pos->Release();
+ mChar2Pos = NULL;
+ }
+
+ if ( mCarPos )
+ {
+ mCarPos->Release();
+ mCarPos = NULL;
+ }
+}
+
+//=============================================================================
+// BonusMissionInfo::HandleEvent
+//=============================================================================
+// Description: handles events that this object listens for
+//
+// Parameters: id - the event we received
+// pEventData - extra info that we get
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::HandleEvent( EventEnum id, void* pEventData )
+{
+ rAssert( id == EVENT_CONVERSATION_DONE );
+ //
+ // Turn on the animated icon
+ //
+ if ( mAlternateIcon && mIsCompleted )
+ {
+ mAlternateIcon->ShouldRender( true );
+ }
+ else if ( !mIsCompleted )
+ {
+ mIcon->ShouldRender( true );
+ }
+
+ mHideAnimatedIcon = false;
+ GetEventManager()->RemoveListener( this , EVENT_CONVERSATION_DONE );
+
+ ResetCharacterPositions();
+}
+
+//=============================================================================
+// BonusMissionInfo::SetUpIcon
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* iconName, rmt::Vector position )
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::SetUpIcon( const char* iconName, rmt::Vector position )
+{
+MEMTRACK_PUSH_GROUP( "BonusMissionInfo" );
+ rAssert( mIcon == NULL );
+
+ mIcon = new(GMA_LEVEL_OTHER) AnimatedIcon();
+ mIcon->Init( iconName, position, true );
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ // un-register old one first
+ //
+ if( mHudMapIconID != -1 )
+ {
+ currentHud->GetHudMap( 0 )->UnregisterIcon( mHudMapIconID );
+ mHudMapIconID = -1;
+ }
+
+ // determine which icon type to register
+ //
+ Mission* mission = GetGameplayManager()->GetMission( this->GetMissionNum() );
+ rAssert( mission != NULL );
+
+ HudMapIcon::eIconType iconType = HudMapIcon::UNKNOWN_ICON_TYPE;
+ if( mission->IsRaceMission() )
+ {
+ iconType = HudMapIcon::ICON_STREET_RACE;
+ }
+ else if( mission->IsWagerMission() )
+ {
+ iconType = HudMapIcon::ICON_WAGER_RACE;
+ }
+ else if( mission->IsBonusMission() )
+ {
+ if( !GetCharacterSheetManager()->QueryBonusMissionCompleted( GetGameplayManager()->GetCurrentLevelIndex() ) )
+ {
+ iconType = HudMapIcon::ICON_BONUS_MISSION;
+ }
+ }
+ else
+ {
+ rAssertMsg( false, "Then what the hell is this??" );
+ }
+
+ // register the hud map icon
+ //
+ if( iconType != HudMapIcon::UNKNOWN_ICON_TYPE )
+ {
+ mHudMapIconID = currentHud->GetHudMap( 0 )->RegisterIcon( iconType, position );
+ }
+ }
+MEMTRACK_POP_GROUP( "BonusMissionInfo" );
+}
+
+//=============================================================================
+// BonusMissionInfo::SetUpAlternateIcon
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* iconName, rmt::Vector position )
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::SetUpAlternateIcon( const char* iconName, rmt::Vector position )
+{
+ MEMTRACK_PUSH_GROUP( "BonusMissionInfo" );
+ rAssert( mAlternateIcon == NULL );
+
+ mAlternateIcon = new(GMA_LEVEL_OTHER) AnimatedIcon();
+ mAlternateIcon->Init( iconName, position, true );
+ MEMTRACK_POP_GROUP( "BonusMissionInfo" );
+}
+
+//=============================================================================
+// BonusMissionInfo::SetEventLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventLocator* loc )
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::SetEventLocator( EventLocator* loc )
+{
+ tRefCounted::Assign( mEventLocator, loc );
+}
+
+//=============================================================================
+// BonusMissionInfo::SetPositions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( CarStartLocator* pos1, CarStartLocator* pos2, CarStartLocator* carPos )
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::SetPositions( CarStartLocator* pos1, CarStartLocator* pos2, CarStartLocator* carPos )
+{
+ tRefCounted::Assign( mChar1Pos, pos1 );
+ tRefCounted::Assign( mChar2Pos, pos2 );
+ tRefCounted::Assign( mCarPos, carPos );
+}
+
+//=============================================================================
+// BonusMissionInfo::CleanUp
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::CleanUp()
+{
+ ResetCharacterPositions();
+
+ // if not already done so, unregister hud map icon
+ //
+ if( mHudMapIconID != -1 )
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->GetHudMap( 0 )->UnregisterIcon( mHudMapIconID );
+ mHudMapIconID = -1;
+ }
+ }
+
+ if ( mIcon )
+ {
+ delete mIcon;
+ mIcon = NULL;
+ }
+
+ if ( mAlternateIcon )
+ {
+ delete mAlternateIcon;
+ mAlternateIcon = NULL;
+ }
+
+ if ( mEventLocator )
+ {
+ mEventLocator->Release();
+ mEventLocator = NULL;
+ }
+
+ if( mDialogEventData.char1 != NULL )
+ {
+ mDialogEventData.char1->Release();
+ mDialogEventData.char1 = NULL;
+ }
+ if( mDialogEventData.char2 != NULL )
+ {
+ mDialogEventData.char2->Release();
+ mDialogEventData.char2 = NULL;
+ }
+
+ tRefCounted::Release( mChar1Pos );
+ tRefCounted::Release( mChar2Pos );
+ tRefCounted::Release( mCarPos );
+
+ mMissionNum = -1;
+ mIsOneShot = false;
+ mIsCompleted = false;
+}
+
+//=============================================================================
+// BonusMissionInfo::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::Update( unsigned int milliseconds )
+{
+ //chuck adding a check for invalid mission number since this will cause a array bounds read.
+ if (mMissionNum == -1)
+ {
+ return;
+ }
+ if ( !mIsCompleted || (mIsCompleted && !mIsOneShot) )
+ {
+ rmt::Vector charPos;
+ if (mDialogEventData.char2 != NULL)
+ {
+ mDialogEventData.char2->GetPosition( &charPos );
+ }
+
+ //
+ // Test if the avatar is near the position
+ //
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rmt::Vector avatarPosition;
+ avatar->GetPosition( avatarPosition );
+ float distSquared = ( charPos - avatarPosition ).MagnitudeSqr();
+ if( distSquared < 5.0f *5.0f )
+ {
+ int missionNum = GetMissionNum();
+ GetEventManager()->TriggerEvent( EVENT_BONUS_MISSION_CHARACTER_APPROACHED, reinterpret_cast< void* >( missionNum ) );
+ }
+
+ if ( mIcon )
+ {
+ if ( mIsCompleted && mAlternateIcon )
+ {
+ //This will get called too often.
+ mIcon->ShouldRender( false );
+ }
+ else
+ {
+ if( !mHideAnimatedIcon )
+ {
+ mIcon->Move( charPos );
+ mIcon->Update( milliseconds );
+ }
+ }
+ }
+
+ if ( mAlternateIcon )
+ {
+ if ( mIsCompleted )
+ {
+ if( !mHideAnimatedIcon )
+ {
+ mAlternateIcon->Move( charPos );
+ mAlternateIcon->Update( milliseconds );
+ }
+ }
+ else
+ {
+ mAlternateIcon->ShouldRender( false );
+ }
+ }
+
+ if(mEventLocator)
+ {
+ mEventLocator->GetTriggerVolume( 0 )->SetPosition( charPos );
+ }
+ }
+}
+
+//=============================================================================
+// BonusMissionInfo::Enable
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::Enable()
+{
+ if ( !mIsCompleted || (mIsCompleted && !mIsOneShot) )
+ {
+ if ( mIcon && ( !mIsCompleted || !mAlternateIcon ) )
+ {
+ mIcon->ShouldRender( true );
+ }
+
+ if ( mAlternateIcon && mIsCompleted )
+ {
+ mAlternateIcon->ShouldRender( true );
+ }
+
+ if ( mEventLocator )
+ {
+ TriggerVolumeTracker::GetInstance()->AddTrigger( mEventLocator->GetTriggerVolume(0) );
+ }
+ }
+}
+
+//=============================================================================
+// BonusMissionInfo::Disable
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::Disable()
+{
+ if ( mIcon )
+ {
+ mIcon->ShouldRender( false );
+ }
+
+ if ( mAlternateIcon )
+ {
+ mAlternateIcon->ShouldRender( false );
+ }
+
+ if ( mEventLocator )
+ {
+ TriggerVolumeTracker::GetInstance()->RemoveTrigger( mEventLocator->GetTriggerVolume(0) );
+ }
+}
+
+//=============================================================================
+// BonusMissionInfo::SetCompleted
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool completed )
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::SetCompleted( bool completed )
+{
+ mIsCompleted = completed;
+
+ if(mIsCompleted && mIsOneShot)
+ {
+ mDialogEventData.char2->SetRole(Character::ROLE_COMPLETED_BONUS);
+ GetCharacterManager()->SetGarbage(mDialogEventData.char2, true);
+ tRefCounted::Assign( mDialogEventData.char2, (Character*)NULL);
+
+ if( mHudMapIconID != -1 )
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->GetHudMap( 0 )->UnregisterIcon( mHudMapIconID );
+ mHudMapIconID = -1;
+ }
+ }
+ }
+}
+
+//=============================================================================
+// BonusMissionInfo::TriggerDialogue
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::TriggerDialogue()
+{
+ Parent::Reset();
+
+ //Set the primary character
+ Character* c1 = GetCharacterManager()->GetCharacter( 0 );
+ tRefCounted::Assign( mDialogEventData.char1, c1 );
+
+ Character* c2 = mDialogEventData.char2;
+
+ //POSITIONS, PLEASE!
+ c1->GetPosition( &mChar1OldPos );
+ mChar1Rotation = c1->GetFacingDir();
+
+ rmt::Vector location;
+ if ( mChar1Pos )
+ {
+ mChar1Pos->GetLocation( &location );
+ c1->RelocateAndReset( location, mChar1Pos->GetRotation() );
+ }
+
+ c2->GetPosition( &mChar2OldPos );
+ mChar2Rotation = c2->GetFacingDir();
+
+ if ( mChar2Pos )
+ {
+ mChar2Pos->GetLocation( &location );
+ c2->RelocateAndReset( location, mChar2Pos->GetRotation() );
+ }
+
+ Vehicle* v = GetGameplayManager()->GetCurrentVehicle();
+ v->GetPosition( &mCarOldPos );
+ mCarRotation = v->GetFacingInRadians();
+
+ bool needToHide = false;
+
+ float dist = 0.0f;
+
+ if ( mChar1Pos )
+ {
+ mChar1Pos->GetLocation( &location );
+ rmt::Vector c1ToCar;
+ c1ToCar.Sub( mCarOldPos, location );
+ if ( c1ToCar.MagnitudeSqr() < 49.0f )
+ {
+ needToHide = true;
+ }
+ }
+
+ if ( !needToHide && mChar2Pos )
+ {
+ mChar2Pos->GetLocation( &location );
+ rmt::Vector c2ToCar;
+ c2ToCar.Sub( mCarOldPos, location );
+ if ( c2ToCar.MagnitudeSqr() < 49.0f )
+ {
+ needToHide = true;
+ }
+ }
+
+ if ( needToHide )
+ {
+ //hide this car
+ v->CarDisplay( false );
+ GetVehicleCentral()->RemoveVehicleFromActiveList( v );
+ mHidTheCar = true;
+ }
+
+ //Should also hide the default car if it is not your current vehicle.
+ Vehicle* defaultCar = GetGameplayManager()->GetVehicleInSlot( GameplayManager::eDefaultCar );
+ if ( defaultCar && defaultCar != v )
+ {
+ bool needToHideDefault = false;
+
+ rmt::Vector defaultPos;
+ defaultCar->GetPosition( &defaultPos );
+
+ if ( mChar1Pos )
+ {
+ mChar1Pos->GetLocation( &location );
+ rmt::Vector c1ToCar;
+ c1ToCar.Sub( defaultPos, location );
+ if ( c1ToCar.MagnitudeSqr() < 49.0f )
+ {
+ needToHideDefault = true;
+ }
+ }
+
+ if ( !needToHideDefault && mChar2Pos )
+ {
+ mChar2Pos->GetLocation( &location );
+ rmt::Vector c2ToCar;
+ c2ToCar.Sub( defaultPos, location );
+ if ( c2ToCar.MagnitudeSqr() < 49.0f )
+ {
+ needToHideDefault = true;
+ }
+ }
+
+ if ( needToHideDefault )
+ {
+ //hide default
+ defaultCar->CarDisplay( false );
+ GetVehicleCentral()->RemoveVehicleFromActiveList( defaultCar );
+
+ mHidDefault = true;
+ }
+ }
+
+ if ( mChar1Pos || mChar2Pos )
+ {
+ GetSuperCamManager()->GetSCC( 0 )->DoCameraCut();
+
+ mChar1Pos->GetLocation( &location );
+ TrafficManager::GetInstance()->ClearTrafficInSphere( rmt::Sphere( location, 100.0f ) );
+
+ mMoved = true;
+ }
+
+ mReset = false;
+
+ CGuiScreenMissionBase::GetBitmapName( mPreviousMissionPic );
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_INIT, (void*)(&mDialogEventData) );
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_INIT_DIALOG, (void*)(&mDialogEventData) );
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_START, (void*)(NULL) );
+
+ //
+ // Turn off the animated icon
+ //
+ if ( mAlternateIcon )
+ {
+ mAlternateIcon->ShouldRender( false );
+ }
+
+ if ( mIcon )
+ {
+ mIcon->ShouldRender( false );
+ }
+
+ mHideAnimatedIcon = true;
+ GetEventManager()->AddListener( this , EVENT_CONVERSATION_DONE );
+}
+
+
+//=============================================================================
+// BonusMissionInfo::SetNPC
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* character )
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::SetNPC( Character* character )
+{
+ tRefCounted::Assign( mDialogEventData.char2, character );
+}
+
+//=============================================================================
+// BonusMissionInfo::ResetCharacterPositions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::ResetCharacterPositions()
+{
+ if(mIcon != NULL)
+ {
+ mIcon->ShouldRender( true );
+ }
+ mHideAnimatedIcon = false;
+
+ if ( mReset || !mMoved )
+ {
+ return;
+ }
+
+ if ( mChar1Pos && mDialogEventData.char1 )
+ {
+ mDialogEventData.char1->RelocateAndReset( mChar1OldPos, mChar1Rotation );
+ }
+
+ if ( mChar2Pos && mDialogEventData.char2 )
+ {
+ mDialogEventData.char2->RelocateAndReset( mChar2OldPos, mChar2Rotation );
+ }
+
+ if ( mHidTheCar )
+ {
+ Vehicle* v = GetGameplayManager()->GetCurrentVehicle();
+
+ v->CarDisplay( true );
+ GetVehicleCentral()->AddVehicleToActiveList( v );
+ mHidTheCar = false;
+ }
+
+ if ( mHidDefault )
+ {
+ Vehicle* defaultCar = GetGameplayManager()->GetVehicleInSlot( GameplayManager::eDefaultCar );
+ defaultCar->CarDisplay( true );
+ GetVehicleCentral()->AddVehicleToActiveList( defaultCar );
+
+ mHidDefault = false;
+ }
+
+ if ( mChar1Pos || mChar2Pos )
+ {
+ GetSuperCamManager()->GetSCC( 0 )->DoCameraCut();
+ GetSuperCamManager()->GetSCC( 0 )->Update( 16 );
+ }
+
+ mMoved = false;
+ mReset = true;
+}
+
+//=============================================================================
+// BonusMissionInfo::ResetMissionBitmap
+//=============================================================================
+// Description: Puts the mission briefing bitmap back to how it was before
+// this bonus mission got triggered
+//
+// Parameters: none
+//
+// Return: void
+//
+//=============================================================================
+void BonusMissionInfo::ResetMissionBitmap()
+{
+ CGuiScreenMissionBase::SetBitmapName( mPreviousMissionPic );
+ CGuiScreenMissionBase::ReplaceBitmap();
+ strcpy( mPreviousMissionPic, "" );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/mission/bonusmissioninfo.h b/game/code/mission/bonusmissioninfo.h
new file mode 100644
index 0000000..92dd578
--- /dev/null
+++ b/game/code/mission/bonusmissioninfo.h
@@ -0,0 +1,226 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: bonusmissioninfo.h
+//
+// Description: Blahblahblah
+//
+// History: 11/19/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef BONUSMISSIONINFO_H
+#define BONUSMISSIONINFO_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <events/eventdata.h>
+#include <events/eventlistener.h>
+#include <mission/haspresentationinfo.h>
+#include <radmath/radmath.hpp>
+
+//========================================
+// Forward References
+//========================================
+class AnimatedIcon;
+class EventLocator;
+class Character;
+class CarStartLocator;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class BonusMissionInfo:
+ public HasPresentationInfo,
+ public EventListener
+{
+private:
+ typedef HasPresentationInfo Parent;
+public:
+ BonusMissionInfo();
+ virtual ~BonusMissionInfo();
+
+ void SetUpIcon( const char* iconName, rmt::Vector position );
+ void SetUpAlternateIcon( const char* iconName, rmt::Vector position );
+
+ void SetMissionNum( int missionNum );
+ int GetMissionNum() const;
+
+ void SetEventLocator( EventLocator* loc );
+ EventLocator* GetEventLocator();
+
+ void SetOneShot( bool isOneShot );
+
+ void SetCompleted( bool completed );
+ bool GetCompleted() const;
+
+ void SetNPC( Character* character );
+ Character* GetNPC();
+
+ void SetDialogueName( const char* name );
+
+ void Update( unsigned int milliseconds );
+ void CleanUp();
+
+ void Enable();
+ void Disable();
+
+ void TriggerDialogue();
+
+ void SetPositions( CarStartLocator* pos1, CarStartLocator* pos2, CarStartLocator* carPos );
+ void ResetCharacterPositions();
+ void HandleEvent( EventEnum id, void* pEventData );
+ void ResetMissionBitmap();
+
+private:
+ AnimatedIcon* mIcon;
+ AnimatedIcon* mAlternateIcon;
+ int mMissionNum;
+ EventLocator* mEventLocator;
+ DialogEventData mDialogEventData;
+ bool mIsOneShot;
+ bool mIsCompleted;
+
+ CarStartLocator* mChar1Pos;
+ CarStartLocator* mChar2Pos;
+ CarStartLocator* mCarPos;
+
+ rmt::Vector mChar1OldPos;
+ rmt::Vector mChar2OldPos;
+ rmt::Vector mCarOldPos;
+ float mChar1Rotation;
+ float mChar2Rotation;
+ float mCarRotation;
+ char mPreviousMissionPic[ 256 ];
+
+ bool mReset : 1;
+ bool mMoved : 1;
+ bool mHideAnimatedIcon : 1;
+ bool mHidTheCar : 1;
+ bool mHidDefault : 1;
+
+ int mHudMapIconID;
+
+ //Prevent wasteful constructor creation.
+ BonusMissionInfo( const BonusMissionInfo& bonusmissioninfo );
+ BonusMissionInfo& operator=( const BonusMissionInfo& bonusmissioninfo );
+};
+
+//******************************************************************************
+//
+// Inline Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// BonusMissionInfo::SetMissionNum
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int missionNum )
+//
+// Return: void
+//
+//=============================================================================
+inline void BonusMissionInfo::SetMissionNum( int missionNum )
+{
+ mMissionNum = missionNum;
+}
+
+//=============================================================================
+// BonusMissionInfo::GetMissionNum
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+inline int BonusMissionInfo::GetMissionNum() const
+{
+ return mMissionNum;
+}
+
+//=============================================================================
+// BonusMissionInfo::GetEventLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: EventLocator
+//
+//=============================================================================
+inline EventLocator* BonusMissionInfo::GetEventLocator()
+{
+ return mEventLocator;
+}
+
+
+//=============================================================================
+// BonusMissionInfo::SetOneShot
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool isOneShot )
+//
+// Return: void
+//
+//=============================================================================
+inline void BonusMissionInfo::SetOneShot( bool isOneShot )
+{
+ mIsOneShot = isOneShot;
+}
+
+
+//=============================================================================
+// BonusMissionInfo::GetCompleted
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool BonusMissionInfo::GetCompleted() const
+{
+ return mIsCompleted;
+}
+
+//=============================================================================
+// BonusMissionInfo::GetNPC
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Character
+//
+//=============================================================================
+inline Character* BonusMissionInfo::GetNPC()
+{
+ return mDialogEventData.char2;
+}
+
+//=============================================================================
+// BonusMissionInfo::SetDialogueName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+inline void BonusMissionInfo::SetDialogueName( const char* name )
+{
+ mDialogEventData.dialogName = ::radMakeKey32( name );
+}
+
+#endif //BONUSMISSIONINFO_H
diff --git a/game/code/mission/bonusobjective.h b/game/code/mission/bonusobjective.h
new file mode 100644
index 0000000..68686a2
--- /dev/null
+++ b/game/code/mission/bonusobjective.h
@@ -0,0 +1,202 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: bonusobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 12/5/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef BONUSOBJECTIVE_H
+#define BONUSOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class BonusObjective
+{
+public:
+ enum Type { NO_DAMAGE, NO_CHASE_COLLISIONS, TIME, POSITION, HIT_N };
+
+ BonusObjective() : mType( NO_DAMAGE ) {};
+ virtual ~BonusObjective() {};
+
+ virtual void Initialize() = 0;
+ virtual void Finalize() = 0;
+ virtual unsigned int GetNumericData() = 0; //All bonus objective have numeric data in common...
+
+ void Reset();
+ void Start();
+ void Update( unsigned int milliseconds );
+
+ Type GetType() const;
+ bool GetSuccessful() const;
+
+protected:
+ virtual void OnReset() {};
+ virtual void OnStart() {};
+ virtual void OnUpdate( unsigned int milliseconds ) {};
+
+ void SetType( Type type );
+ void SetSuccessful( bool successful );
+ bool GetStarted() const;
+
+private:
+ Type mType;
+ bool mSuccessful;
+ bool mStarted;
+
+ //Prevent wasteful constructor creation.
+ BonusObjective( const BonusObjective& bonusobjective );
+ BonusObjective& operator=( const BonusObjective& bonusobjective );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// BonusObjective::Reset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void BonusObjective::Reset()
+{
+ mStarted = false;
+ OnReset();
+}
+
+//=============================================================================
+// BonusObjective::Start
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void BonusObjective::Start()
+{
+ mStarted = true;
+ OnStart();
+}
+
+//=============================================================================
+// BonusObjective::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+inline void BonusObjective::Update( unsigned int milliseconds )
+{
+ OnUpdate( milliseconds );
+}
+
+//=============================================================================
+// BonusObjective::Type GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: BonusObjective
+//
+//=============================================================================
+inline BonusObjective::Type BonusObjective::GetType() const
+{
+ return mType;
+}
+
+//=============================================================================
+// BonusObjective::GetSuccessful
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool BonusObjective::GetSuccessful() const
+{
+ return mSuccessful;
+}
+
+
+//*****************************************************************************
+//
+//Inline Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// BonusObjective::SetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Type type )
+//
+// Return: void
+//
+//=============================================================================
+inline void BonusObjective::SetType( Type type )
+{
+ mType = type;
+}
+
+//=============================================================================
+// BonusObjective::SetSuccessful
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool successful )
+//
+// Return: void
+//
+//=============================================================================
+inline void BonusObjective::SetSuccessful( bool successful )
+{
+ mSuccessful = successful;
+}
+
+//=============================================================================
+// BonusObjective::GetStarted
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool BonusObjective::GetStarted() const
+{
+ return mStarted;
+}
+
+#endif //BONUSOBJECTIVE_H
diff --git a/game/code/mission/boss.h b/game/code/mission/boss.h
new file mode 100644
index 0000000..c78ee7a
--- /dev/null
+++ b/game/code/mission/boss.h
@@ -0,0 +1,87 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Name of subsystem or component this class belongs to.
+//
+// Description: This file contains...
+//
+// Authors: Name Here
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef BOSS_H
+#define BOSS_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <p3d\entity.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class Weapon;
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+// Describe here what you require of the client which uses or
+// has this class - essentially, the converse of the description
+// above. A constraint is an expression of some semantic
+// condition that must be preserved, an invariant of a class or
+// relationship that must be preserved while the system is in a
+// steady state.
+//
+//===========================================================================
+class Boss : public tEntity
+{
+ public:
+ Boss();
+ virtual ~Boss();
+
+ virtual void Update( float timeInMS )=0;
+ virtual bool LoadSetup( const char* statePropDSGName, const rmt::Vector& position )=0;
+ virtual void AddWeapon( Weapon* pWeapon, const rmt::Vector& offset )=0;
+
+ protected:
+
+
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow Boss from being copied and assigned.
+ Boss( const Boss& );
+ Boss& operator=( const Boss& );
+
+
+};
+
+inline Boss::Boss()
+{
+
+}
+inline Boss::~Boss()
+{
+
+}
+
+#endif \ No newline at end of file
diff --git a/game/code/mission/charactersheet/allcharactersheet.cpp b/game/code/mission/charactersheet/allcharactersheet.cpp
new file mode 100644
index 0000000..55ce037
--- /dev/null
+++ b/game/code/mission/charactersheet/allcharactersheet.cpp
@@ -0,0 +1 @@
+#include <mission\charactersheet\charactersheetmanager.cpp> \ No newline at end of file
diff --git a/game/code/mission/charactersheet/charactersheet.h b/game/code/mission/charactersheet/charactersheet.h
new file mode 100644
index 0000000..d580719
--- /dev/null
+++ b/game/code/mission/charactersheet/charactersheet.h
@@ -0,0 +1,169 @@
+//CharacterSheet struct for saving data
+#ifndef CHARACTERSHEET_H
+#define CHARACTERSHEET_H
+
+
+#include <render/enums/renderenums.h>
+#include <camera/supercam.h>
+
+const int MAX_MISSIONS = 8; //this is set to 8 since there is a Stupid tutorial mission in level 1
+const int MAX_CARDS = 7;
+const int MAX_STREETRACES = 3;
+const int MAX_CAMERAS = 12;
+const int MAX_COINS = 100;
+const int MAX_LEVELS = 7;
+const int MAX_PURCHASED_ITEMS = 12; //the maximum number of things that can be buy with tokens.
+const int MAX_CARS_OWNED =60;
+const unsigned int MAX_LEVEL_GAGS = 32; // stored as a bitmask, so if you need to make it bigger,
+ // change the type in the level struct
+
+// on PS2, always use a byte for a boolean, so that this data type size
+// is the same for all builds (debug/tune/release)
+//
+#ifdef RAD_PS2
+ typedef unsigned char CS_BOOL;
+#else
+ typedef bool CS_BOOL;
+#endif
+
+ enum CarDamageStateEnum
+ {
+ eNull,
+ eNoDamage,
+ eDamaged,
+ eHusk,
+ eNumOfStates
+ };
+
+ static const int NUM_PERSISTENT_SECTORS = 82; // Add 7 'sectors' on the end of global level objects.
+ static const int NUM_PERSISTENT_OBJECTS = 128; // Number of persistent objects we'll track per zone/rail.
+ static const int NUM_BYTES_PER_SECTOR = ( NUM_PERSISTENT_OBJECTS + 7 ) >> 3; // We'll bit pack the object states so this is the number of bytes we'll need per zone/rail.
+ static const int NUM_BYTES_FOR_PERSISTENT_STATES = NUM_BYTES_PER_SECTOR * NUM_PERSISTENT_SECTORS; // And this is the number of bytes we'll need for the entire game.
+ static const int NUM_BYTES_FOR_STATES = 1;
+
+ struct CarCharacterSheet
+ {
+ char mName[16];
+ float mCurrentHealth; // 0.0f = hust, 1.0f = no damage;
+ float mMaxHealth;
+ };
+
+ struct CarInventoryStruct
+ {
+ CarCharacterSheet mCars [MAX_CARS_OWNED];
+ int mCounter;
+ };
+
+
+ struct CurrentMissionStruct
+ {
+ RenderEnums::LevelEnum mLevel;
+ RenderEnums::MissionEnum mMissionNumber;
+ };
+ struct Record
+ {
+ char mName [16];
+ CS_BOOL mCompleted;
+ };
+
+ struct MissionRecord
+ {
+ char mName [16];
+ CS_BOOL mCompleted;
+ CS_BOOL mBonusObjective;
+ unsigned int mNumAttempts;
+ CS_BOOL mSkippedMission;
+ int mBestTime;
+ };
+
+
+ struct CharCardList
+ {
+ Record mList [MAX_CARDS];
+ };
+
+ struct MissionList
+ {
+ MissionRecord mList [MAX_MISSIONS];
+ };
+ struct StreetRaceList
+ {
+ MissionRecord mList [MAX_STREETRACES];
+ };
+
+
+ struct LevelRecord
+ {
+ //collectable items
+ CharCardList mCard;
+
+ //Mission items
+ MissionList mMission;
+ StreetRaceList mStreetRace;
+ MissionRecord mBonusMission;
+ MissionRecord mGambleRace;
+
+
+ CS_BOOL mFMVunlocked;
+ int mNumCarsPurchased;
+ int mNumSkinsPurchased;
+ int mWaspsDestroyed;
+ char mCurrentSkin [16];
+
+ int mGagsViewed;
+ unsigned mGagMask;
+
+ CS_BOOL mGags[MAX_LEVEL_GAGS];
+
+ //stuff that player has bought with tokens
+ CS_BOOL mPurchasedRewards [MAX_PURCHASED_ITEMS];
+
+ };
+
+/*
+ //struct for saving player preferences For FE options etc.
+ struct PlayerOptions
+ {
+ CS_BOOL mRumble;
+ float mSFXVolume;
+ float mMusicVolume;
+ float mDialogVolume;
+ CS_BOOL mInvertCamera;
+ CS_BOOL mRadarOn;
+ CS_BOOL mTutorialOn;
+ CS_BOOL mNavSystemOn;
+
+ SuperCam::Type mDriverCam;
+
+ };
+
+ struct TutorialsSeen
+ {
+ int mBitfield;
+ };
+*/
+
+ //this is the master character sheet that we will be saving to the memory card
+ struct CharacterSheet
+ {
+ char mPlayerName [16];
+// PlayerOptions mPlayerOptions;
+ LevelRecord mLevelList [MAX_LEVELS];
+ CurrentMissionStruct mCurrentMissionInfo;
+ CurrentMissionStruct mHighestMissionPlayed;
+// TutorialsSeen mTutoiralsSeen;
+ CS_BOOL mIsNavSystemEnabled;
+ int mNumberOfTokens; // used to track the number of coins/token whatever used to buy things
+ CarInventoryStruct mCarInventory;
+ unsigned char mPersistentObjectStates[ NUM_BYTES_FOR_PERSISTENT_STATES ]; // The broken state for objects in the world.
+ unsigned char mStates[NUM_BYTES_FOR_STATES]; // Flags used for the state of the game, such as if the player has the IS movie ticket.
+ };
+
+
+
+
+
+
+
+#endif //CHARACTER_SHEET.h
+
diff --git a/game/code/mission/charactersheet/charactersheetmanager.cpp b/game/code/mission/charactersheet/charactersheetmanager.cpp
new file mode 100644
index 0000000..ae8b34f
--- /dev/null
+++ b/game/code/mission/charactersheet/charactersheetmanager.cpp
@@ -0,0 +1,1962 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: charactersheetmanager.cpp
+//
+// Description: Implementation for the CharacterSheetManager class.
+//
+// History: + Created -- Chuck Chow
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/charactersheet/charactersheet.h>
+#include <mission/rewards/rewardsmanager.h>
+
+#include <cards/cardgallery.h>
+#include <cheats/cheatinputsystem.h>
+#include <data/gamedatamanager.h>
+#include <events/eventmanager.h>
+#include <main/commandlineoptions.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <p3d/entity.hpp>
+#include <string.h>
+
+#ifndef WORLD_BUILDER
+#include <memory/srrmemory.h>
+#else
+#define MEMTRACK_PUSH_GROUP(x)
+#define MEMTRACK_POP_GROUP(x)
+#define GMA_PERSISTENT 0
+#define GMA_DEFAULT 0
+#define GMA_TEMP 0
+#endif
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+
+CharacterSheetManager* CharacterSheetManager::spInstance = NULL;
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// CharacterSheetManager::CreateInstance
+//==============================================================================
+//
+// Description: Creates the CharacterSheetManager.
+//
+// Parameters: None.
+//
+// Return: Pointer to the CharacterSheetManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+CharacterSheetManager* CharacterSheetManager::CreateInstance()
+{
+ rAssert( spInstance == NULL );
+MEMTRACK_PUSH_GROUP("CharacterSheetManager");
+
+ spInstance = new (GMA_PERSISTENT) CharacterSheetManager;
+ rAssert( spInstance );
+
+MEMTRACK_POP_GROUP("CharacterSheetManager");
+ return spInstance;
+}
+
+//==============================================================================
+// CharacterSheetManager::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the CharacterSheetManager singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the CharacterSheetManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+CharacterSheetManager* CharacterSheetManager::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// CharacterSheetManager::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the CharacterSheetManager.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void CharacterSheetManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete (GMA_PERSISTENT,spInstance);
+ spInstance = NULL;
+}
+
+
+
+//set a mission complete this includes StreetRaces and BonusMissions
+void CharacterSheetManager::SetMissionComplete(RenderEnums::LevelEnum level,char* name, bool completed_secondary_objective,int seconds)
+ {
+ MissionRecord* p_missionrecord =NULL;
+
+ //p_missionrecord = GetMissionRecord(level,name);
+
+ if ( GetMissionRecord(level,name)!= NULL)
+ {
+ p_missionrecord = GetMissionRecord(level,name);
+ p_missionrecord->mCompleted =true;
+ p_missionrecord->mBonusObjective = completed_secondary_objective;
+
+ }
+ else if ( GetStreetRaceRecord (level,name) != NULL )
+ {
+ p_missionrecord = GetStreetRaceRecord (level,name);
+ p_missionrecord->mCompleted =true;
+ p_missionrecord->mBonusObjective = completed_secondary_objective;
+
+ if (seconds > 0)
+ {
+ //check if we have a legit best time or the intial default of -1
+ if ( p_missionrecord->mBestTime < 0)
+ { //default case so seconds becomes new best time
+ p_missionrecord->mBestTime = seconds;
+ }
+ else
+ {
+ //we didnt beat our best time
+ if(p_missionrecord->mBestTime < seconds)
+ {
+ //do nothing
+ }
+ else
+ {
+ //we have a new best time
+ p_missionrecord->mBestTime=seconds;
+ }
+ }
+ }
+
+
+ //we check after check mission complete if we have unlocked anything
+ if (spInstance->QueryAllStreetRacesCompleted(level) == true)
+ {
+ Reward* pReward=GetRewardsManager()->GetReward(level,Reward::eStreetRace);
+ //assert if we there isnt a reward for the streetrace.
+ rTuneAssert(pReward != NULL);
+ pReward->UnlockReward();
+ GetEventManager()->TriggerEvent(EVENT_COMPLETED_ALLSTREETRACES);
+ }
+
+
+ }
+ else if ( GetBonusMissionRecord (level,name ) != NULL)
+ {
+ p_missionrecord = GetBonusMissionRecord(level,name);
+ p_missionrecord->mCompleted =true;
+ p_missionrecord->mBonusObjective = completed_secondary_objective;
+
+ //we check after check mission complete if we have unlocked anything
+ if (spInstance->QueryBonusMissionCompleted(level) == true)
+ {
+ Reward* pReward=GetRewardsManager()->GetReward(level,Reward::eBonusMission);
+ //assert if we there isnt a reward for the BonusMission.
+ rTuneAssert(pReward != NULL);
+ pReward->UnlockReward();
+ GetEventManager()->TriggerEvent(EVENT_COMPLETED_BONUSMISSIONS);
+ }
+
+ }
+ else
+ {
+ // TC: this is now implemented in MissionStage::Update
+ //
+/*
+ if(strcmp("gr1",name)==0)
+ {
+ mCharacterSheet.mLevelList[level].mGambleRace.mCompleted = true;
+
+ if (seconds > 0)
+ {
+ //check if we have a legit best time or the intial default of -1
+ if ( mCharacterSheet.mLevelList[level].mGambleRace.mBestTime < 0)
+ { //default case so seconds becomes new best time
+ mCharacterSheet.mLevelList[level].mGambleRace.mBestTime = seconds;
+ }
+ else
+ {
+ //we didnt beat our best time
+ if( mCharacterSheet.mLevelList[level].mGambleRace.mBestTime < seconds)
+ {
+ //do nothing
+ }
+ else
+ {
+ //we have a new best time
+ mCharacterSheet.mLevelList[level].mGambleRace.mBestTime = seconds;
+ }
+ }
+ }
+
+ }
+ else
+ {
+ //Mission not found in character sheet trouble
+ rReleasePrintf("trouble mission %s not found in charactersheet \n",name);
+ // rAssert(0);
+ }
+*/
+ }
+ }
+
+ //inc the number of attempts
+ void CharacterSheetManager::IncrementMissionAttempt(RenderEnums::LevelEnum level, char* name)
+ {
+ MissionRecord* pMissionRecord =NULL;
+ pMissionRecord = spInstance->GetMissionRecord(level,name);
+
+ if ( pMissionRecord != NULL)
+ {
+ pMissionRecord->mNumAttempts++;
+ }
+ else
+ {
+ //mission not found prolly a sunday drive or something
+ }
+ }
+
+ //update the mission as user chose to skip it
+ void CharacterSheetManager::SetMissionSkipped(RenderEnums::LevelEnum level,char* name)
+ {
+ MissionRecord* pMissionRecord =NULL;
+ pMissionRecord = spInstance->GetMissionRecord(level,name);
+
+ if ( pMissionRecord != NULL)
+ {
+ pMissionRecord->mSkippedMission = true;
+ }
+ else
+ {
+ //mission not found prolly a sunday drive or something
+ //rAssertMsg(0,"Mission: not found \n");
+
+ }
+ }
+
+ void CharacterSheetManager::SetMissionSkipped(RenderEnums::LevelEnum level,RenderEnums::MissionEnum mission)
+ {
+ MissionRecord* pMissionRecord =NULL;
+ pMissionRecord = spInstance->GetMissionRecord(level,mission);
+
+ if ( pMissionRecord != NULL)
+ {
+ pMissionRecord->mSkippedMission = true;
+ }
+ else
+ {
+ //mission not found prolly a sunday drive or something
+ //rAssertMsg(0,"Mission: not found \n");
+
+ }
+ }
+
+ //returns the number of attempts the use tried to complete mission
+ int CharacterSheetManager::QueryNumberOfAttempts(RenderEnums::LevelEnum level,int index)
+ {
+ MissionRecord* pMissionRecord =NULL;
+ pMissionRecord = spInstance->GetMissionRecord(level,index);
+
+ if ( pMissionRecord != NULL)
+ {
+ //dividing by 2 since number of attempts is incremented at mission reset, and all mission get reset twice for whatever
+ //reason
+ return (pMissionRecord->mNumAttempts / 2) ;
+ }
+ else
+ {
+ rAssertMsg(0,"Mission: not found \n");
+ return -666;
+ }
+ }
+
+
+
+
+
+
+//get a ptr to the record, you shouldn't modify the record though, use the set methods to modify instead
+MissionRecord* CharacterSheetManager::QueryMissionStatus(RenderEnums::LevelEnum level, char* name)
+ {
+ return (GetMissionRecord(level,name) );
+ }
+
+//get a ptr to the record, you shouldn't modify the record though, use the set methods to modify instead
+MissionRecord* CharacterSheetManager::QueryMissionStatus(RenderEnums::LevelEnum level, int index)
+ {
+ return (GetMissionRecord(level,index) );
+ }
+
+//get a ptr to the record, you shouldn't modify the record though, use the set methods to modify instead
+MissionRecord* CharacterSheetManager::QueryStreetRaceStatus(RenderEnums::LevelEnum level, char* name)
+ {
+ return (GetStreetRaceRecord(level,name) );
+ }
+
+ MissionRecord* CharacterSheetManager::QueryBonusMissionStatus( RenderEnums::LevelEnum level, char* name )
+ {
+ return GetBonusMissionRecord( level, name );
+ }
+
+
+
+bool CharacterSheetManager::QueryAllCardsCollected(RenderEnums::LevelEnum level )
+ {
+ for (int i=0;i<MAX_CARDS;i++)
+ {
+ //cycle through the list of cards, check if they have been collected
+ if( spInstance->mCharacterSheet.mLevelList[level].mCard.mList[i].mCompleted )
+ {
+ //do nothing
+ }
+ else
+ {
+ //we have come across a card that hasn't been collected return false
+ return false;
+ }
+ }
+ //checked the entire list and didnt encounter a card that wasnt collected so we return true
+ return true;
+ }
+
+
+
+
+ // method returns boolean of bonus mission
+ bool CharacterSheetManager::QueryBonusMissionCompleted(RenderEnums::LevelEnum level)
+ {
+ return spInstance->mCharacterSheet.mLevelList[level].mBonusMission.mCompleted != 0;
+ }
+
+
+ //returns true if we cycle the entire list of streetraces and do not encounter a streetrace thats not completed
+ bool CharacterSheetManager::QueryAllStreetRacesCompleted(RenderEnums::LevelEnum level)
+ {
+ //cycle throught the streetraces
+ for (int i =0; i<MAX_STREETRACES;i++)
+ {
+ //check for incomplete races, if found return false
+ if ( spInstance->mCharacterSheet.mLevelList[level].mStreetRace.mList[i].mCompleted == false)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+
+
+
+
+
+
+
+//Fill the Character Sheet with default data.
+void CharacterSheetManager::InitCharacterSheet()
+ {
+ for (int i = 0; i<MAX_LEVELS;i++)
+ {
+ int j ;
+ for (j=0;j<MAX_CARDS;j++)
+ {
+ strcpy(mCharacterSheet.mLevelList[i].mCard.mList[j].mName, "NULL");
+ mCharacterSheet.mLevelList[i].mCard.mList[j].mCompleted = false;
+ }
+
+ for (j=0;j<MAX_MISSIONS;j++)
+ {
+ strcpy(mCharacterSheet.mLevelList[i].mMission.mList[j].mName,"NULL");
+ mCharacterSheet.mLevelList[i].mMission.mList[j].mCompleted = false;
+ mCharacterSheet.mLevelList[i].mMission.mList[j].mBonusObjective = false;
+ mCharacterSheet.mLevelList[i].mMission.mList[j].mSkippedMission = false;
+ mCharacterSheet.mLevelList[i].mMission.mList[j].mNumAttempts = 0;
+ mCharacterSheet.mLevelList[i].mMission.mList[j].mBestTime = -1;
+ }
+ for (j=0;j<MAX_STREETRACES;j++)
+ {
+ strcpy(mCharacterSheet.mLevelList[i].mStreetRace.mList[j].mName , "NULL");
+ mCharacterSheet.mLevelList[i].mStreetRace.mList[j].mCompleted = false;
+ mCharacterSheet.mLevelList[i].mStreetRace.mList[j].mBonusObjective = false;
+ mCharacterSheet.mLevelList[i].mStreetRace.mList[j].mNumAttempts =0;
+ mCharacterSheet.mLevelList[i].mStreetRace.mList[j].mBestTime = -1;
+
+ }
+ //Defaults
+ strcpy(mCharacterSheet.mLevelList[i].mBonusMission.mName,"NULL");
+ //Bonus Mission
+ mCharacterSheet.mLevelList[i].mBonusMission.mCompleted =false;
+ mCharacterSheet.mLevelList[i].mBonusMission.mBonusObjective =false;
+ mCharacterSheet.mLevelList[i].mBonusMission.mNumAttempts = 0;
+ mCharacterSheet.mLevelList[i].mBonusMission.mBestTime = -1;
+
+ //Gamble Race
+ mCharacterSheet.mLevelList[i].mGambleRace.mCompleted =false;
+ mCharacterSheet.mLevelList[i].mGambleRace.mBonusObjective =false;
+ mCharacterSheet.mLevelList[i].mGambleRace.mNumAttempts = 0;
+ mCharacterSheet.mLevelList[i].mGambleRace.mBestTime = -1;
+
+ //Current skin
+ strcpy(mCharacterSheet.mLevelList[i].mCurrentSkin,"NULL");
+
+ mCharacterSheet.mLevelList[i].mFMVunlocked = false;
+ mCharacterSheet.mLevelList[i].mNumCarsPurchased =0;
+ mCharacterSheet.mLevelList[i].mNumSkinsPurchased =0;
+ mCharacterSheet.mLevelList[i].mWaspsDestroyed =0;
+ mCharacterSheet.mLevelList[i].mGagsViewed = 0;
+ mCharacterSheet.mLevelList[i].mGagMask = 0;
+
+
+ //set default for purchased rewards.
+ for(j=0;j<MAX_PURCHASED_ITEMS;j++)
+ {
+ mCharacterSheet.mLevelList[i].mPurchasedRewards[j]=false;
+ }
+ }
+
+ //top level defaults
+#ifdef RAD_DEMO
+ mCharacterSheet.mNumberOfTokens = 500;
+#else
+ if( CommandLineOptions::Get( CLO_COINS ) ||
+ GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_EXTRA_COINS ) )
+ {
+ mCharacterSheet.mNumberOfTokens = 1000; // start game w/ some money
+ }
+ else
+ {
+ mCharacterSheet.mNumberOfTokens = 0;
+ }
+#endif // RAD_DEMO
+
+ strcpy(mCharacterSheet.mPlayerName,"Player1");
+ mCharacterSheet.mCurrentMissionInfo.mLevel = RenderEnums::L1;
+ mCharacterSheet.mCurrentMissionInfo.mMissionNumber = RenderEnums::M1;
+ mCharacterSheet.mHighestMissionPlayed.mLevel = RenderEnums::L1;
+ mCharacterSheet.mHighestMissionPlayed.mMissionNumber = RenderEnums::M1;
+
+ for(int i=0;i<MAX_CARS_OWNED;i++)
+ {
+ mCharacterSheet.mCarInventory.mCars[i].mCurrentHealth = -1.0f;
+ mCharacterSheet.mCarInventory.mCars[i].mMaxHealth = -1.0f;
+ strcpy(mCharacterSheet.mCarInventory.mCars[i].mName,"n/a");
+ }
+ mCharacterSheet.mCarInventory.mCounter=0;
+
+
+/*
+ //setting the player preference option defaults
+ mCharacterSheet.mPlayerOptions.mRumble = true;
+ mCharacterSheet.mPlayerOptions.mDialogVolume = 0.5;
+ mCharacterSheet.mPlayerOptions.mMusicVolume = 0.5;
+ mCharacterSheet.mPlayerOptions.mSFXVolume = 0.5;
+ mCharacterSheet.mPlayerOptions.mDriverCam = SuperCam::FOLLOW_CAM;
+ mCharacterSheet.mPlayerOptions.mInvertCamera = false;
+ mCharacterSheet.mPlayerOptions.mNavSystemOn = true;
+ mCharacterSheet.mPlayerOptions.mRadarOn = true;
+ mCharacterSheet.mPlayerOptions.mTutorialOn = true;
+
+ mCharacterSheet.mTutoiralsSeen.mBitfield = 0x00;
+*/
+ mCharacterSheet.mIsNavSystemEnabled = true;
+
+ memset( mCharacterSheet.mPersistentObjectStates, 0xFF, NUM_BYTES_FOR_PERSISTENT_STATES );
+ memset(mCharacterSheet.mStates, 0x00, NUM_BYTES_FOR_STATES);
+ }
+
+//this is called from the missionscriptloader to fill in charactersheet
+int CharacterSheetManager::AddMission(RenderEnums::LevelEnum level, char* name)
+ {
+ //if mission is not in the list then
+ if (GetMissionRecord(level,name) == NULL)
+ {
+ //check to see if the slot is open
+ for ( int i =0;i<MAX_MISSIONS;i++)
+ {
+ //look for an empty slot
+ if (strcmp(mCharacterSheet.mLevelList[level].mMission.mList[i].mName,"NULL") == 0 )
+ {
+ if(strlen (name) > 16)
+ {
+ //name too big
+ rAssert(0);
+ }
+ strcpy(mCharacterSheet.mLevelList[level].mMission.mList[i].mName,name);
+ mCharacterSheet.mLevelList[level].mMission.mList[i].mName[15]='\0';
+// mCharacterSheet.mLevelList[level].mMission.mList[i].mCompleted = false;
+ mCharacterSheet.mLevelList[level].mMission.mList[i].mBonusObjective = false;
+ mCharacterSheet.mLevelList[level].mMission.mList[i].mNumAttempts =0;
+ return 0;
+ }//end if
+ }//end for
+ //return -1 list is full
+ return -1;
+ rAssert(0);
+ }
+ else
+ {
+ return 0;
+
+ }
+ }//end function
+
+
+//this is called from the missionscriptloader to fill in charactersheet
+int CharacterSheetManager::AddStreetRace(RenderEnums::LevelEnum level, char* name)
+{
+ //if mission is not in the list then
+ if (GetStreetRaceRecord(level,name) == NULL)
+ {
+ //check to see if the slot is open
+ for ( int i =0;i<MAX_STREETRACES;i++)
+ {
+ //look for an empty slot
+ if (strcmp(mCharacterSheet.mLevelList[level].mStreetRace.mList[i].mName,"NULL") == 0 )
+ {
+ if(strlen (name) > 16)
+ {
+ //name too big
+ rAssert(0);
+ }
+ strcpy(mCharacterSheet.mLevelList[level].mStreetRace.mList[i].mName,name);
+ mCharacterSheet.mLevelList[level].mStreetRace.mList[i].mName[15]='\0';
+// mCharacterSheet.mLevelList[level].mStreetRace.mList[i].mCompleted = false;
+ mCharacterSheet.mLevelList[level].mStreetRace.mList[i].mBonusObjective = false;
+ mCharacterSheet.mLevelList[level].mStreetRace.mList[i].mNumAttempts =0;
+ mCharacterSheet.mLevelList[level].mStreetRace.mList[i].mBestTime = -1;
+ return 0;
+ }//end if
+ }//end for
+ //return -1 list is full
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}//end function
+
+
+ //this is called from the missionscriptloader to fill in charactersheet
+int CharacterSheetManager::AddBonusMission(RenderEnums::LevelEnum level, char* name)
+ {
+
+
+ //if mission is not in the list then
+ if (GetBonusMissionRecord(level,name) == NULL)
+ {
+ //look for an empty slot
+ if (strcmp(mCharacterSheet.mLevelList[level].mBonusMission.mName,"NULL") == 0 )
+ {
+ if(strlen (name) > 16)
+ {
+ //name too big
+ rAssert(0);
+ }
+ strcpy(mCharacterSheet.mLevelList[level].mBonusMission.mName,name);
+ mCharacterSheet.mLevelList[level].mBonusMission.mName[15]='\0';
+// mCharacterSheet.mLevelList[level].mBonusMission.mCompleted = false;
+ mCharacterSheet.mLevelList[level].mBonusMission.mBonusObjective = false;
+ mCharacterSheet.mLevelList[level].mBonusMission.mNumAttempts =0;
+ return 0;
+ }//end if
+
+ //return -1 list is full
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+
+
+ return 0;
+
+ }//end function
+
+ //Add a card the Card List for a level
+int CharacterSheetManager::AddCard(RenderEnums::LevelEnum level, char* name)
+ {
+ //if mission is not in the list then
+ if (GetCollectableRecord(level,eCard,name) == NULL)
+ {
+ //check to see if the slot is open
+ for ( int i =0;i<MAX_CARDS;i++)
+ {
+ //look for an empty slot
+ if (strcmp(mCharacterSheet.mLevelList[level].mCard.mList[i].mName,"NULL") == 0 )
+ {
+ if(strlen (name) > 16)
+ {
+ //name too big
+ rAssert(0);
+ }
+ strcpy(mCharacterSheet.mLevelList[level].mCard.mList[i].mName,name);
+ mCharacterSheet.mLevelList[level].mCard.mList[i].mName[15]='\0';
+ mCharacterSheet.mLevelList[level].mCard.mList[i].mCompleted = false;
+ return 0;
+ }//end if
+ }//end for
+ //return -1 list is full
+ return -1;
+ }
+ else
+ {
+ //card already exists.
+ return -2;
+ }
+ }//end function
+
+
+ //Add a card the Card List for a level, insert by index
+ //danger this type of insertion has less safeguards than insert by name
+int CharacterSheetManager::AddCard(RenderEnums::LevelEnum level, int index)
+ {
+ Record* pRecord = spInstance->GetCollectableRecord(level,eCard,index);
+
+ if ( pRecord == NULL)
+ {
+ //trouble retrieving record
+ rAssert(0);
+ return -1;
+ }
+ else
+ {
+ if ( strcmp (pRecord->mName,"NULL") !=0 )
+ {
+ rAssertMsg(0,"Warning: You are attempting to overwriting an existing Record! \n");
+ strcpy(pRecord->mName,"Cardx");
+ pRecord->mName[15]='\0';
+ pRecord->mCompleted = false;
+ return 0;
+ }
+ else
+ {
+ strcpy(pRecord->mName,"Cardx");
+ pRecord->mName[15]='\0';
+ pRecord->mCompleted = true;
+ return 0;
+ }
+ }
+ }//end function
+
+
+
+//memory card load method
+void CharacterSheetManager::LoadData(const GameDataByte* dataBuffer,unsigned int numBytes )
+ {
+ memcpy(&mCharacterSheet,dataBuffer,sizeof(CharacterSheet));
+ GetRewardsManager()->IncUpdateQue();
+ int size = sizeof(CharacterSheet);
+ }
+
+
+//memory card save method
+
+void CharacterSheetManager::SaveData(GameDataByte* dataBuffer,unsigned int numBytes )
+ {
+ if( CommandLineOptions::Get( CLO_MEMCARD_CHEAT ) )
+ {
+ rReleasePrintf( "Saving data w/ memory card cheat ... ...\n" );
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+
+ CharacterSheet* tempCharacterSheet = new CharacterSheet;
+ rAssert( tempCharacterSheet != NULL );
+
+ memcpy( tempCharacterSheet, &mCharacterSheet, sizeof( CharacterSheet ) );
+
+ tempCharacterSheet->mHighestMissionPlayed.mLevel = RenderEnums::L7;
+ tempCharacterSheet->mHighestMissionPlayed.mMissionNumber = RenderEnums::M7;
+ tempCharacterSheet->mNumberOfTokens = 1000;
+
+ // unlock items for all levels
+ //
+ for( int i = 0; i < MAX_LEVELS; i++ )
+ {
+ int j = 0;
+
+ // unlock all missions
+ for( j = 0; j < (i == RenderEnums::L1 ? MAX_MISSIONS : MAX_MISSIONS - 1); j++ )
+ {
+ tempCharacterSheet->mLevelList[ i ].mMission.mList[ j ].mCompleted = true;
+ tempCharacterSheet->mLevelList[ i ].mMission.mList[ j ].mNumAttempts = 1;
+ }
+
+ // unlock all street races
+ for( j = 0; j < MAX_STREETRACES; j++ )
+ {
+ tempCharacterSheet->mLevelList[ i ].mStreetRace.mList[ j ].mCompleted = true;
+ tempCharacterSheet->mLevelList[ i ].mStreetRace.mList[ j ].mNumAttempts = 1;
+ }
+
+ // unlock bonus mission
+ tempCharacterSheet->mLevelList[ i ].mBonusMission.mCompleted = true;
+ tempCharacterSheet->mLevelList[ i ].mBonusMission.mNumAttempts = 1;
+
+ // unlock movie
+ tempCharacterSheet->mLevelList[ i ].mFMVunlocked = true;
+
+ // unlock merchandise skins and cars
+ const int MAX_CARS = 3;
+ const int MAX_SKINS = 3;
+ tempCharacterSheet->mLevelList[ i ].mNumCarsPurchased = MAX_CARS;
+ tempCharacterSheet->mLevelList[ i ].mNumSkinsPurchased = MAX_SKINS;
+ for( j = 0; j < (MAX_CARS + MAX_SKINS); j++ )
+ {
+ tempCharacterSheet->mLevelList[ i ].mPurchasedRewards[ j ] = true;
+ }
+
+ // unlock cards
+ for( j = 0; j < MAX_CARDS; j++ )
+ {
+ tempCharacterSheet->mLevelList[ i ].mCard.mList[ j ].mCompleted = true;
+ }
+
+ // unlock gags
+ tempCharacterSheet->mLevelList[ i ].mGagsViewed = GetRewardsManager()->GetTotalGags( i );
+
+ // unlock wasps
+ tempCharacterSheet->mLevelList[ i ].mWaspsDestroyed = GetRewardsManager()->GetTotalWasps( i );
+ }
+
+ memcpy( dataBuffer, tempCharacterSheet, sizeof( CharacterSheet ) );
+
+ if( tempCharacterSheet != NULL )
+ {
+ delete tempCharacterSheet;
+ tempCharacterSheet = NULL;
+ }
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+ }
+ else
+ {
+ memcpy(dataBuffer,&mCharacterSheet,sizeof(CharacterSheet));
+ }
+ }
+
+
+//updates the charactersheets current mission struct
+
+ void CharacterSheetManager::SetCurrentMission(RenderEnums::LevelEnum level,RenderEnums::MissionEnum mission_number)
+ {
+ if( ( level == RenderEnums::L1 && mission_number == RenderEnums::M9 ) ||
+ ( level != RenderEnums::L1 && mission_number == RenderEnums::M8 ) )
+ {
+ // set level and mission to next level's first mission (and wrap back to
+ // L1 M2 if last mission of the game)
+ //
+ int nextLevel = (level + 1) % RenderEnums::numLevels;
+ level = static_cast<RenderEnums::LevelEnum>( nextLevel );
+ mission_number = (level != RenderEnums::L1) ? RenderEnums::M1 : RenderEnums::M2;
+ }
+
+ spInstance->mCharacterSheet.mCurrentMissionInfo.mLevel = level;
+ spInstance->mCharacterSheet.mCurrentMissionInfo.mMissionNumber = mission_number;
+
+ // update highest mission played
+ //
+ if( static_cast<int>( level ) > static_cast<int>( spInstance->mCharacterSheet.mHighestMissionPlayed.mLevel ) )
+ {
+ spInstance->mCharacterSheet.mHighestMissionPlayed.mLevel = level;
+ spInstance->mCharacterSheet.mHighestMissionPlayed.mMissionNumber = mission_number;
+ }
+ else if( level == spInstance->mCharacterSheet.mHighestMissionPlayed.mLevel )
+ {
+ if( static_cast<int>( mission_number ) > static_cast<int>( spInstance->mCharacterSheet.mHighestMissionPlayed.mMissionNumber ) )
+ {
+ spInstance->mCharacterSheet.mHighestMissionPlayed.mMissionNumber = mission_number;
+ }
+ }
+ }
+
+
+ //method for collecting stuff
+ void CharacterSheetManager::SetCardCollected(RenderEnums::LevelEnum level,char* name)
+ {
+ Record* pCollectableRecord = spInstance->GetCollectableRecord(level,eCard,name);
+ rAssert(pCollectableRecord);
+ pCollectableRecord->mCompleted = true;
+
+ //check if we have collected all cards
+ if( spInstance->QueryAllCardsCollected(level) == true)
+ {
+ Reward* pReward = GetRewardsManager()->GetReward(level,Reward::eCards);
+ //Assert if no reward found
+ rTuneAssert(pReward != NULL);
+ pReward->UnlockReward();
+ GetEventManager()->TriggerEvent(EVENT_COLLECTED_ALLCARDS);
+ }
+
+ }
+
+
+ void CharacterSheetManager::SetCardCollected(RenderEnums::LevelEnum level,int index)
+ {
+ Record* pCollectableRecord = spInstance->GetCollectableRecord(level,eCard,index);
+ rAssert(pCollectableRecord);
+ pCollectableRecord->mCompleted = true;
+
+ //check if we have collected all cards
+ if( spInstance->QueryAllCardsCollected(level) == true)
+ {
+ Reward* pReward = GetRewardsManager()->GetReward(level,Reward::eCards);
+ //Assert if no reward found
+ rTuneAssert(pReward != NULL);
+ pReward->UnlockReward();
+ GetEventManager()->TriggerEvent(EVENT_COLLECTED_ALLCARDS);
+ }
+ }
+
+
+
+ //Returns the number of tokens that the character has in their inventory for that level
+ int CharacterSheetManager::GetNumberOfTokens(RenderEnums::LevelEnum level)
+ {
+ return spInstance->mCharacterSheet.mNumberOfTokens;
+ }
+
+ //Set the number of tokens in the character sheet of that level to number
+ void CharacterSheetManager::SetNumberOfTokens(RenderEnums::LevelEnum level,int number)
+ {
+ if( number >= 0 )
+ {
+ spInstance->mCharacterSheet.mNumberOfTokens;
+ }
+ }
+
+ //add number to the current number of tokens
+ void CharacterSheetManager::AddTokens(RenderEnums::LevelEnum level,int number)
+ {
+ spInstance->mCharacterSheet.mNumberOfTokens += number;
+
+ if( spInstance->mCharacterSheet.mNumberOfTokens < 0 )
+ {
+ spInstance->mCharacterSheet.mNumberOfTokens = 0;
+ }
+ }
+
+ //subtracts the number from the current number of tokens
+ void CharacterSheetManager::SubtractTokens(RenderEnums::LevelEnum level,int number)
+ {
+ spInstance->mCharacterSheet.mNumberOfTokens -= number;
+
+ if( spInstance->mCharacterSheet.mNumberOfTokens < 0 )
+ {
+ spInstance->mCharacterSheet.mNumberOfTokens = 0;
+ }
+ }
+
+ //returns the current mission mission info
+CurrentMissionStruct CharacterSheetManager::QueryCurrentMission()
+ {
+ return spInstance->mCharacterSheet.mCurrentMissionInfo;
+ }
+
+CurrentMissionStruct CharacterSheetManager::QueryHighestMission()
+ {
+ return spInstance->mCharacterSheet.mHighestMissionPlayed;
+ }
+
+ //use this to query if player has collect a card. This should be called at load time so we dont create the card uneccessarily
+ bool CharacterSheetManager::QueryCardCollected(RenderEnums::LevelEnum level,char* CardName)
+ {
+ Record* pRecord = NULL;
+
+ pRecord = spInstance->GetCollectableRecord(level,eCard,CardName);
+ if ( pRecord != NULL)
+ {
+ return pRecord->mCompleted != 0;
+ }
+ else
+ {
+ rAssertMsg(0,"Card Name doesnt Exists\n");
+ return false;
+ }
+ }
+
+ //use this to query if player has collect a card. This should be called at load time so we dont create the card uneccessarily
+ bool CharacterSheetManager::QueryCardCollected(RenderEnums::LevelEnum level,int index)
+ {
+ Record* pRecord = NULL;
+
+ pRecord = spInstance->GetCollectableRecord(level,eCard,index);
+ if ( pRecord != NULL)
+ {
+ return pRecord->mCompleted != 0;
+ }
+ else
+ {
+ rAssertMsg(0,"Card Name doesnt Exists\n");
+ return false;
+ }
+ }
+
+
+/*
+ //get the player settings
+ bool CharacterSheetManager::QueryRumble()
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ return pPlayerOptions->mRumble;
+ }
+
+ float CharacterSheetManager::QuerySFXVolume()
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ return pPlayerOptions->mSFXVolume;
+ }
+
+
+ float CharacterSheetManager::QueryMusicVolume()
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ return pPlayerOptions->mMusicVolume;
+ }
+
+
+ float CharacterSheetManager::QueryDialogVolume()
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ return pPlayerOptions->mDialogVolume;
+ }
+
+ SuperCam::Type CharacterSheetManager::QueryDriverCam()
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ return pPlayerOptions->mDriverCam;
+ }
+
+ bool CharacterSheetManager::QueryInvertedCameraSetting()
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ return pPlayerOptions->mInvertCamera;
+ }
+
+ bool CharacterSheetManager::QueryRadarSetting()
+ {
+
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ return pPlayerOptions->mRadarOn;
+ }
+
+ bool CharacterSheetManager::QueryTutorialSetting()
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ return pPlayerOptions->mTutorialOn;
+ }
+
+//methods for setting the player setting at the FE or PM
+ void CharacterSheetManager::SetRumble(bool setting)
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ pPlayerOptions->mRumble = setting;
+ }
+
+ void CharacterSheetManager::SetSFXVolume(float volume)
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ pPlayerOptions->mSFXVolume = volume;
+ }
+
+ void CharacterSheetManager::SetMusicVolume(float volume)
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ pPlayerOptions->mMusicVolume = volume;
+ }
+
+ void CharacterSheetManager::SetDialogVolume(float volume)
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ pPlayerOptions->mDialogVolume = volume;
+ }
+
+ void CharacterSheetManager::SetDriverCamera(SuperCam::Type camera)
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ pPlayerOptions->mDriverCam = camera;
+ }
+
+ void CharacterSheetManager::SetTutorialOn(bool setting)
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ pPlayerOptions->mTutorialOn = setting;
+ }
+
+ void CharacterSheetManager::SetRadarOn(bool setting)
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ pPlayerOptions->mRadarOn = setting;
+ }
+ void CharacterSheetManager::SetInvertedCamera(bool setting)
+ {
+ PlayerOptions* pPlayerOptions = spInstance->GetPlayerOptions();
+ rAssert(pPlayerOptions);
+ pPlayerOptions->mInvertCamera = setting;
+ }
+*/
+ bool CharacterSheetManager::QueryNavSystemSetting()
+ {
+ return mCharacterSheet.mIsNavSystemEnabled != 0;
+ }
+
+ void CharacterSheetManager::SetNavSystemOn(bool setting)
+ {
+ mCharacterSheet.mIsNavSystemEnabled = setting;
+ }
+
+ //Called from the Rewards Manager, basically records which reward the player has purchased from the RewardManager's token store
+ void CharacterSheetManager::SetPurchasedRewards(RenderEnums::LevelEnum level,int index)
+ {
+ mCharacterSheet.mLevelList[level].mPurchasedRewards[index] = true;
+
+ Merchandise* pmerchandise = NULL;
+ //update the nNumCarsPurchased or nNumSkinsPurchased
+ pmerchandise = GetRewardsManager()->GetMerchandise( (int) level, index);
+
+ //if NULL then We got a problem see Chuck!
+ rTuneAssert(pmerchandise != NULL);
+
+ if (pmerchandise->GetRewardType() == Reward::ALT_SKIN_OTHER)
+ {
+ spInstance->mCharacterSheet.mLevelList[level].mNumSkinsPurchased++;
+ }
+ else if ( pmerchandise->GetRewardType() == Reward::ALT_PLAYERCAR)
+ {
+ spInstance->mCharacterSheet.mLevelList[level].mNumCarsPurchased++;
+ //spInstance->AddCarToInventory(pmerchandise->GetName(),eNoDamage, -10.0f);
+ }
+ else
+ {
+ //unknown type of reward being purchased
+ rAssert(0);
+ }
+ }
+
+
+
+
+ bool CharacterSheetManager::QueryPurchasedReward(RenderEnums::LevelEnum level, int index)
+ {
+ if ((index > 0 ) && (index<MAX_PURCHASED_ITEMS))
+ {
+ return mCharacterSheet.mLevelList[level].mPurchasedRewards[index] != 0;
+ }
+ else
+ {
+ //invalid index as input
+ rTuneAssert(0);
+ return false;
+ }
+ }
+
+ //add a car to carinventory to the charactersheet
+ int CharacterSheetManager::AddCarToInventory( const char* Name )
+ {
+ rAssertMsg( mCharacterSheet.mCarInventory.mCounter < MAX_CARS_OWNED, "Car inventory is full.\n" );
+ int i = 0;
+ Vehicle* pVehicle = NULL;
+ for( i = 0; i < mCharacterSheet.mCarInventory.mCounter; ++i )
+ {
+ if( strcmp( mCharacterSheet.mCarInventory.mCars[ i ].mName, Name ) == 0 )
+ {
+ return i;
+ }
+ }
+ mCharacterSheet.mCarInventory.mCars[ i ].mCurrentHealth = 1.0f;
+
+ pVehicle = GetVehicleCentral()->GetVehicleByName(Name);
+
+
+
+ //check if vehicle is intialized already.
+ if ( pVehicle != NULL)
+ {
+ mCharacterSheet.mCarInventory.mCars[ i ].mMaxHealth = pVehicle->mDesignerParams.mHitPoints;
+ }
+ strcpy( mCharacterSheet.mCarInventory.mCars[ i ].mName, Name );
+ ++mCharacterSheet.mCarInventory.mCounter;
+ return i;
+ }
+
+ int CharacterSheetManager::GetCarIndex( const char* Name )
+ {
+ // Simple sequential search.
+ for( int i = 0; i < mCharacterSheet.mCarInventory.mCounter; ++i )
+ {
+ if( strcmp( mCharacterSheet.mCarInventory.mCars[ i ].mName, Name ) == 0 )
+ {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ //Return Number of Cars player has in there inventory
+ int CharacterSheetManager::GetNumberOfCarsInInventory()
+ {
+ return mCharacterSheet.mCarInventory.mCounter;
+ }
+
+
+ //Returns The cars state or eNull if it can't find the car.
+ CarDamageStateEnum CharacterSheetManager::GetCarDamageState( int CarIndex )
+ {
+ if( CarIndex < 0 || CarIndex >= mCharacterSheet.mCarInventory.mCounter )
+ {
+ return eNull;
+ }
+ else if( mCharacterSheet.mCarInventory.mCars[ CarIndex ].mCurrentHealth >= 1.0f )
+ {
+ return eNoDamage;
+ }
+ else if( mCharacterSheet.mCarInventory.mCars[ CarIndex ].mCurrentHealth <= 0.0f )
+ {
+ return eHusk;
+ }
+ else
+ {
+ return eDamaged;
+ }
+ }
+
+ // Returns the Car's health, or -1 if index out of range.
+ float CharacterSheetManager::GetCarHealth( int CarIndex )
+ {
+ if( CarIndex < 0 || CarIndex >= mCharacterSheet.mCarInventory.mCounter )
+ {
+ return -1.0f;
+ }
+ return mCharacterSheet.mCarInventory.mCars[ CarIndex ].mCurrentHealth;
+ }
+
+ void CharacterSheetManager::UpdateCarHealth( int CarIndex, float Health )
+ {
+ if( CarIndex >= 0 && CarIndex < mCharacterSheet.mCarInventory.mCounter )
+ {
+ mCharacterSheet.mCarInventory.mCars[ CarIndex ].mCurrentHealth = Health;
+ }
+ }
+
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// CharacterSheetManager::CharacterSheetManager
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+CharacterSheetManager::CharacterSheetManager()
+{
+ GetGameDataManager()->RegisterGameData( this, sizeof( CharacterSheet ),
+ "Character Sheet" );
+}
+
+
+//==============================================================================
+// CharacterSheetManager::~CharacterSheetManager
+//==============================================================================
+//
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+CharacterSheetManager::~CharacterSheetManager()
+{
+
+}
+
+
+
+
+//Get a ptr to a mission record for missions
+MissionRecord* CharacterSheetManager::GetMissionRecord(RenderEnums::LevelEnum level, char* name)
+ {
+ //check to see if name array is too long
+ if (strlen (name) > 16)
+ {
+ //you asserted here cause your name array is greater than 64 bytes
+ rAssert(0);
+ }
+ for (int i=0;i<MAX_MISSIONS;i++)
+ {
+
+ if (strcmp (spInstance->mCharacterSheet.mLevelList[level].mMission.mList[i].mName,name) ==0)
+ {
+ return &(spInstance->mCharacterSheet.mLevelList[level].mMission.mList[i]) ;
+ }
+ }
+ return NULL;
+ }
+
+//Get a ptr to a mission record for missions query by index
+MissionRecord* CharacterSheetManager::GetMissionRecord(RenderEnums::LevelEnum level, int index)
+ {
+ //check to see if index is valid
+ if ((index > MAX_MISSIONS) || (index < 0))
+ {
+ //you asserted here cause the input index is too large or less than 0
+ rAssert(0);
+ return NULL;
+ }
+ else
+ {
+ return &(spInstance->mCharacterSheet.mLevelList[level].mMission.mList[index]) ;
+ }
+ }
+
+//Get a ptr to a mission record for streetraces
+MissionRecord* CharacterSheetManager::GetStreetRaceRecord(RenderEnums::LevelEnum level, char* name)
+ {
+ //check to see if name array is too long
+ if (strlen (name) > 16)
+ {
+ //you asserted here cause your name array is greater than 16 bytes
+ rAssert(0);
+ }
+ for (int i=0;i<MAX_STREETRACES;i++)
+ {
+
+ if (strcmp (spInstance->mCharacterSheet.mLevelList[level].mStreetRace.mList[i].mName,name) ==0)
+ {
+ return &(spInstance->mCharacterSheet.mLevelList[level].mStreetRace.mList[i]) ;
+ }
+ }
+ return NULL;
+ }
+
+//Get a ptr to a mission record for streetraces
+MissionRecord* CharacterSheetManager::GetStreetRaceRecord(RenderEnums::LevelEnum level, int index)
+ {
+ //check to see if index is valid
+ if ((index > MAX_STREETRACES) || (index < 0))
+ {
+ //you asserted here cause the input index is too large or less than 0
+ rAssert(0);
+ return NULL;
+ }
+ else
+ {
+ return &(spInstance->mCharacterSheet.mLevelList[level].mStreetRace.mList[index]) ;
+
+ }
+ }
+
+
+ //get a ptr to a bonusmission record
+ MissionRecord* CharacterSheetManager::GetBonusMissionRecord (RenderEnums::LevelEnum level, char* name)
+ {
+ if (strlen(name)>16)
+ {
+ //Name is too long
+ rAssert(0);
+ }
+
+ if (strcmp (spInstance->mCharacterSheet.mLevelList[level].mBonusMission.mName,name) ==0)
+ {
+ return & (spInstance->mCharacterSheet.mLevelList[level].mBonusMission) ;
+ }
+ else
+ { //return NULL if there is no match
+ return NULL;
+ }
+ }
+
+
+
+ //private method for getting a collectable record internal use only
+ Record* CharacterSheetManager::GetCollectableRecord(RenderEnums::LevelEnum level,CharacterSheetManager::eCollectableType type,char* name)
+ {
+ if (strlen(name) >16)
+ {
+ //Asserted because the name is too long
+ rAssert(0);
+ }
+
+ switch (type)
+ {
+ case eCard:
+ {
+ for( int i=0; i<MAX_CARDS;i++)
+ {
+ if (strcmp(name,spInstance->mCharacterSheet.mLevelList[level].mCard.mList[i].mName))
+ {
+ return (&(spInstance->mCharacterSheet.mLevelList[level].mCard.mList[i]) );
+ }
+ }
+ //we have reached the end of the list and no matches return A NULL ptr
+ return NULL;
+ break;
+ }
+ case eCoin:
+ {
+ /*
+ for( int i=0; i<MAX_COINS;i++)
+ {
+ if (strcmp(name,spInstance->mCharacterSheet.mLevelList[level].mCoin.mList[i].mName))
+ {
+ return (&(spInstance->mCharacterSheet.mLevelList[level].mCoin.mList[i]) );
+ }
+ }
+ */
+ return NULL;
+ break;
+ }
+ case eCamera:
+ {
+ /*
+ for( int i=0; i<MAX_CAMERAS;i++)
+ {
+ if (strcmp(name,spInstance->mCharacterSheet.mLevelList[level].mCamera.mList[i].mName))
+ {
+ return (&(spInstance->mCharacterSheet.mLevelList[level].mCamera.mList[i]) );
+ }
+ }
+ */
+ return NULL;
+ break;
+ }
+ default:
+ {
+ //error unknown type
+ rAssert(0);
+ return NULL;
+ }
+ }//end switch
+
+ }//end function
+
+ //private method for getting a collectable record internal use only, retrieving by index
+ Record* CharacterSheetManager::GetCollectableRecord(RenderEnums::LevelEnum level,CharacterSheetManager::eCollectableType type,int index)
+ {
+
+ switch (type)
+ {
+ case eCard:
+ {
+ //check for valid index. it must be 0-6
+ if ( index > -1 && index < MAX_CARDS)
+ {
+ return (&(spInstance->mCharacterSheet.mLevelList[level].mCard.mList[index]) );
+ }
+ //we have reached the end of the list and no matches return A NULL ptr
+ return NULL;
+ break;
+ }
+ case eCoin:
+ {
+ /*
+ //check for valid index. it must be 0-99
+ if ( index > -1 && index < MAX_COINS)
+ {
+ return (&(spInstance->mCharacterSheet.mLevelList[level].mCoin.mList[index]) );
+ }
+ //we have reached the end of the list and no matches return A NULL ptr
+
+ */
+ return NULL;
+ break;
+ }
+ case eCamera:
+ {
+ /*
+ //check for valid index. it must be 0-11
+ if ( index > -1 && index < MAX_CAMERAS)
+ {
+ return (&(spInstance->mCharacterSheet.mLevelList[level].mCamera.mList[index]) );
+ }
+ //we have reached the end of the list and no matches return A NULL ptr
+ */
+ return NULL;
+ break;
+ }
+ default:
+ {
+ //error unknown type
+ rAssert(0);
+ return NULL;
+ }
+ }//end switch
+
+ }//end function
+
+/*
+ //Returns a ptr to the player options Struct.
+ PlayerOptions* CharacterSheetManager::GetPlayerOptions ()
+ {
+ return &(spInstance->mCharacterSheet.mPlayerOptions);
+ }
+*/
+
+/*
+//=============================================================================
+// CharacterSheetManager::QueryTutorialSeen
+//=============================================================================
+// Description: checks to see if a given tutorial has already been played
+//
+// Parameters: tutorial - enum of the tutorial in question
+//
+// Return: bool - has the tutorial been shown
+//
+//=============================================================================
+bool CharacterSheetManager::QueryTutorialSeen( const TutorialMode tutorial )
+{
+ rAssert( spInstance != NULL );
+ return (spInstance->mCharacterSheet.mTutoiralsSeen.mBitfield & (1 << tutorial)) > 0;
+
+}
+
+//=============================================================================
+// CharacterSheetManager::SetTutorialSeen
+//=============================================================================
+// Description: sets the status of a specific tutorial
+//
+// Parameters: tutorial - enum of the tutorial in question
+//
+// Return: bool - has the tutorial been shown
+//
+//=============================================================================
+void CharacterSheetManager::SetTutorialSeen( const TutorialMode tutorial, const bool seen )
+{
+ rAssert( spInstance != NULL );
+ if ( seen )
+ {
+ spInstance->mCharacterSheet.mTutoiralsSeen.mBitfield |= (1 << tutorial);
+ }
+ else
+ {
+ spInstance->mCharacterSheet.mTutoiralsSeen.mBitfield &= ~(1 << tutorial);
+ }
+}
+*/
+
+
+//Returns a bool, if the player has unlocked the FMV for the level, We make the presumption that only ONE FMV per level.
+bool CharacterSheetManager::QueryFMVUnlocked(RenderEnums::LevelEnum level)
+{
+ return spInstance->mCharacterSheet.mLevelList[level].mFMVunlocked != 0;
+}
+
+
+// Record that the FMV is unlocked, call this after the FMV is done , from the FMV objective.
+void CharacterSheetManager::SetFMVUnlocked(RenderEnums::LevelEnum level)
+{
+ spInstance->mCharacterSheet.mLevelList[level].mFMVunlocked = true;
+}
+
+
+//returns the number of missions completed by the player. Use this for the FE Scrapbook
+int CharacterSheetManager::QueryNumMissionsCompleted(RenderEnums::LevelEnum level)
+{
+ int numMissionsCompleted =0;
+ for (int i =0;i<MAX_MISSIONS;i++)
+ {
+ if (strcmp(spInstance->mCharacterSheet.mLevelList[level].mMission.mList[i].mName,"m0") ==0 )
+ {
+ //M0 mission dont count so we ingnore counting of these
+ }
+ else
+ {
+ if (spInstance->mCharacterSheet.mLevelList[level].mMission.mList[i].mCompleted)
+ {
+ numMissionsCompleted++;
+ }
+ }
+ }
+
+ return numMissionsCompleted;
+}
+
+
+int CharacterSheetManager::QueryNumWaspsDestroyed(RenderEnums::LevelEnum level)
+{
+ return mCharacterSheet.mLevelList[level].mWaspsDestroyed;
+}
+
+int CharacterSheetManager::QueryNumGagsViewed(RenderEnums::LevelEnum level)
+{
+ return mCharacterSheet.mLevelList[level].mGagsViewed;
+}
+
+bool CharacterSheetManager::QueryGagViewed(RenderEnums::LevelEnum level, unsigned which)
+{
+ rAssert(which < MAX_LEVEL_GAGS);
+ unsigned mask = 1 << which;
+ return (mCharacterSheet.mLevelList[level].mGagMask & mask) != 0;
+}
+
+void CharacterSheetManager::IncNumWaspsDestroyed(RenderEnums::LevelEnum level)
+{
+ mCharacterSheet.mLevelList[level].mWaspsDestroyed++;
+}
+
+void CharacterSheetManager::AddGagViewed(RenderEnums::LevelEnum level, unsigned which)
+{
+ rAssert(which < MAX_LEVEL_GAGS);
+ unsigned mask = 1 << which;
+
+ if(!(mCharacterSheet.mLevelList[level].mGagMask & mask))
+ {
+ mCharacterSheet.mLevelList[level].mGagMask |= mask;
+ mCharacterSheet.mLevelList[level].mGagsViewed++;
+ }
+}
+
+//Called by the FE scrapbook
+
+int CharacterSheetManager::QueryNumCarUnlocked(RenderEnums::LevelEnum level )
+{
+ int defaultCar = 0;
+ int bm =0;
+ int str =0;
+
+ if( static_cast<int>( QueryHighestMission().mLevel ) >= static_cast<int>( level ) )
+ {
+ defaultCar = 1;
+ }
+
+ if (QueryAllStreetRacesCompleted(level) == true)
+ {
+ str =1;
+ }
+ if (QueryBonusMissionCompleted(level) == true)
+ {
+ bm = 1;
+ }
+
+ return ( spInstance->mCharacterSheet.mLevelList[level].mNumCarsPurchased + bm + str );
+}
+
+
+//Called by the FE scrapbook
+int CharacterSheetManager::QueryNumSkinsUnlocked(RenderEnums::LevelEnum level)
+{
+ return spInstance->mCharacterSheet.mLevelList[level].mNumSkinsPurchased;
+}
+
+//Returns number of bonus missions completed this should be 0 or 1 since only one Bonus Mission per level
+int CharacterSheetManager::QueryNumBonusMissionsCompleted(RenderEnums::LevelEnum level)
+{
+ int counter =0;
+
+ if(spInstance->mCharacterSheet.mLevelList[level].mBonusMission.mCompleted)
+ {
+ counter++;
+ }
+
+ return counter;
+}
+
+
+//Returns number of street races completed this should be 0 to 3
+int CharacterSheetManager::QueryNumStreetRacesCompleted(RenderEnums::LevelEnum level)
+{
+ int counter =0;
+
+ for (int i=0;i<MAX_STREETRACES;i++)
+ {
+ if (spInstance->mCharacterSheet.mLevelList[level].mStreetRace.mList[i].mCompleted)
+ {
+ counter++;
+ }
+ }
+ return counter;
+}
+
+
+CarCharacterSheet* CharacterSheetManager::GetCarCharacterSheet(char* name)
+{
+ for (int i=0;i<=mCharacterSheet.mCarInventory.mCounter;i++)
+ {
+ if ( strcmp(name,mCharacterSheet.mCarInventory.mCars[i].mName) ==0)
+ {
+ return (&mCharacterSheet.mCarInventory.mCars[i]);
+ }
+ }
+ return NULL;
+}
+
+int CharacterSheetManager::QueryNumCardsCollected(RenderEnums::LevelEnum level)
+{
+ int numCards=0;
+
+ for (int i=0;i<MAX_CARDS;i++)
+ {
+ if (spInstance->mCharacterSheet.mLevelList[level].mCard.mList[i].mCompleted)
+ {
+ numCards++;
+ }
+ }
+
+ return numCards;
+
+}
+
+
+float CharacterSheetManager::QueryPercentLevelCompleted(RenderEnums::LevelEnum level) const
+ {
+ float level_completed = 0.0f;
+ float cars = 0.0f;
+ float skins = 0.0f;
+ float missions = 0.0f;
+ float cards = 0.0f;
+ float wasps= 0.0f;
+ float gags = 0.0f;
+ float bonusmission =0.0f;
+ float streetraces = 0.0f;
+
+
+ missions = static_cast< float >( spInstance->QueryNumMissionsCompleted(level) );
+ bonusmission = static_cast< float >( spInstance->QueryNumBonusMissionsCompleted(level) );
+ streetraces = static_cast< float >( spInstance->QueryNumStreetRacesCompleted(level) );
+ skins = static_cast< float >( spInstance->QueryNumSkinsUnlocked(level) );
+ cars = static_cast< float >( spInstance->QueryNumCarUnlocked(level) );
+ cards = static_cast< float >( spInstance->QueryNumCardsCollected(level) );
+ wasps = static_cast< float >( spInstance->QueryNumWaspsDestroyed(level) );
+ gags = static_cast< float >( spInstance->QueryNumGagsViewed(level) );
+
+ missions = missions/7;
+ skins = skins/3;
+ cars = cars/5;
+ cards= cards/7;
+ streetraces = streetraces/3;
+ wasps = wasps/GetRewardsManager()->GetTotalWasps((int) level);
+ gags = gags /GetRewardsManager()->GetTotalGags( (int) level);
+
+
+ level_completed = ((missions+bonusmission+streetraces+skins+cars+cards+wasps+gags)/8);
+
+ if(level ==RenderEnums::L7) //level 7 has only 5 missions so we divide by 21 (5+3+6+7)
+ {
+ float floatnumber = 0.0f;
+ floatnumber = (float) level_completed;
+ floatnumber*= 100;
+ return floatnumber;
+ }
+ else
+ {
+ float floatnumber = 0.0f;
+ floatnumber = (float) level_completed;
+ floatnumber *= 100.0f;
+ return floatnumber;
+
+ }
+ }
+
+
+
+ //Calculate percent game completed. return a whole number.
+float CharacterSheetManager::QueryPercentGameCompleted( ) const
+ {
+ float gameCompleted = 0.0f;
+
+ float l1,l2,l3,l4,l5,l6,l7 = 0.0f;
+
+ l1 = spInstance->QueryPercentLevelCompleted(RenderEnums::L1);
+ l2 = spInstance->QueryPercentLevelCompleted(RenderEnums::L2);
+ l3 = spInstance->QueryPercentLevelCompleted(RenderEnums::L3);
+ l4 = spInstance->QueryPercentLevelCompleted(RenderEnums::L4);
+ l5 = spInstance->QueryPercentLevelCompleted(RenderEnums::L5);
+ l6 = spInstance->QueryPercentLevelCompleted(RenderEnums::L6);
+ l7 = spInstance->QueryPercentLevelCompleted(RenderEnums::L7);
+
+ // Magic number here: convention is L3 is the bonus movie since there is not story movie for that level
+ //
+ bool bFMVBonus = spInstance->QueryFMVUnlocked (RenderEnums::L3);
+
+ gameCompleted = l1+l2+l3+l4+l5+l6+l7;
+
+ gameCompleted = gameCompleted /7;
+
+ // Levels complete = 99%; Bonus movie = 1% (kind of arbitrary)
+ //
+ gameCompleted = (gameCompleted * 0.99f) + (bFMVBonus ? 1.0f : 0.0f);
+
+ return gameCompleted;
+ }
+
+bool
+CharacterSheetManager::IsAllStoryMissionsCompleted()
+{
+ bool isAllMissionsCompleted = true;
+
+ for( int i = 0; i < RenderEnums::numLevels; i++ )
+ {
+ if( this->QueryNumMissionsCompleted( static_cast<RenderEnums::LevelEnum>( i ) ) < 7 )
+ {
+ isAllMissionsCompleted = false;
+
+ break;
+ }
+ }
+
+ return isAllMissionsCompleted;
+}
+
+
+ int CharacterSheetManager::UpdateRewardsManager()
+ {
+ for (int i = 0; i<MAX_LEVELS;i++)
+ {
+ //update default vehicle
+ if( static_cast<int>( QueryHighestMission().mLevel ) >= i )
+ {
+ GetRewardsManager()->GetReward((RenderEnums::LevelEnum) i,Reward::eDefaultCar)->UnlockReward();
+ }
+ else
+ {
+ GetRewardsManager()->GetReward((RenderEnums::LevelEnum) i,Reward::eDefaultCar)->LockReward();
+ }
+
+ //update the cards unlockable
+ if (spInstance->QueryAllCardsCollected((RenderEnums::LevelEnum)i) == true)
+ {
+ GetRewardsManager()->GetReward((RenderEnums::LevelEnum) i,Reward::eCards)->UnlockReward();
+ }
+ else
+ {
+ GetRewardsManager()->GetReward((RenderEnums::LevelEnum) i,Reward::eCards)->LockReward();
+ }
+
+ //update the streetraces
+ if (spInstance->QueryAllStreetRacesCompleted((RenderEnums::LevelEnum) i) == true)
+ {
+ GetRewardsManager()->GetReward((RenderEnums::LevelEnum) i,Reward::eStreetRace)->UnlockReward();
+ }
+ else
+ {
+ GetRewardsManager()->GetReward((RenderEnums::LevelEnum) i,Reward::eStreetRace)->LockReward();
+ }
+
+ //update the bonus mission
+ if (spInstance->QueryBonusMissionCompleted((RenderEnums::LevelEnum)i) == true )
+ {
+ GetRewardsManager()->GetReward((RenderEnums::LevelEnum) i,Reward::eBonusMission)->UnlockReward();
+ }
+ else
+ {
+ GetRewardsManager()->GetReward((RenderEnums::LevelEnum) i,Reward::eBonusMission)->LockReward();
+ }
+
+ //update the purchase rewards now
+
+ //cycle thru the rewards that we have used coins to by and unlock the rewards in the manager
+ for (int j=0;j<MAX_PURCHASED_ITEMS;j++)
+ {
+ if (spInstance->mCharacterSheet.mLevelList[i].mPurchasedRewards[j])
+ {
+ GetRewardsManager()->GetMerchandise(i,j)->UnlockReward();
+ }
+ else
+ {
+ Merchandise* merchandise = GetRewardsManager()->GetMerchandise(i,j);
+ if( merchandise != NULL )
+ {
+ merchandise->LockReward();
+ }
+ }
+ }
+
+ }
+ return 0;
+
+ }
+
+ bool CharacterSheetManager::IsMiniGameUnlocked() const
+ {
+ return( GetCardGallery()->GetNumCardDecksCompleted() > 0 );
+ }
+
+ static const int NUM_STATES = 2;
+ static tUID sKeyTable[NUM_STATES] =
+ {
+ tEntity::MakeUID("is_cbg_first"),
+ tEntity::MakeUID("is_ticket")
+ };
+
+ bool CharacterSheetManager::IsState(tUID State)
+ {
+ for(int i = 0; i < NUM_STATES; ++i)
+ {
+ if(State == sKeyTable[i])
+ {
+ return IsState(i);
+ }
+ }
+ return false;
+ }
+
+ bool CharacterSheetManager::IsState(int Index)
+ {
+ int offset = Index >> 3;
+ unsigned char bitMask = 1 << (Index & 7);
+ return ((mCharacterSheet.mStates[offset]) & bitMask) == bitMask;
+ }
+
+ void CharacterSheetManager::SetState(tUID State, bool IsSet)
+ {
+ for(int i = 0; i < NUM_STATES; ++i)
+ {
+ if(State == sKeyTable[i])
+ {
+ SetState(i, IsSet);
+ return;
+ }
+ }
+ }
+
+ void CharacterSheetManager::SetState(int Index, bool IsSet)
+ {
+ int offset = Index >> 3;
+ unsigned char bitMask = 1 << (Index & 7);
+ if(IsSet)
+ {
+ mCharacterSheet.mStates[offset] |= bitMask;
+ }
+ else
+ {
+ mCharacterSheet.mStates[offset] = mCharacterSheet.mStates[offset] & ~bitMask;
+ }
+ }
+
+ int CharacterSheetManager::AddGambleRace(RenderEnums::LevelEnum level,char* name)
+ {
+ strcpy(mCharacterSheet.mLevelList[level].mGambleRace.mName,name);
+// mCharacterSheet.mLevelList[level].mGambleRace.mBestTime = -1;
+ mCharacterSheet.mLevelList[level].mGambleRace.mBonusObjective =false;
+// mCharacterSheet.mLevelList[level].mGambleRace.mCompleted = false;
+// mCharacterSheet.mLevelList[level].mGambleRace.mNumAttempts =0;
+// mCharacterSheet.mLevelList[level].mGambleRace.mSkippedMission = false;
+ return 0;
+ }
+
+ void CharacterSheetManager::SetGambleRaceBestTime( RenderEnums::LevelEnum level, int seconds )
+ {
+ mCharacterSheet.mLevelList[ level ].mGambleRace.mBestTime = seconds;
+ }
+
+ int CharacterSheetManager::GetGambleRaceBestTime(RenderEnums::LevelEnum level)
+ {
+ return mCharacterSheet.mLevelList[level].mGambleRace.mBestTime;
+ }
+
+ int CharacterSheetManager::GetStreetRaceBestTime(RenderEnums::LevelEnum level,char* name)
+ {
+ for (int i =0; i <MAX_STREETRACES;i++)
+ {
+ if (strcmp (name,mCharacterSheet.mLevelList[level].mStreetRace.mList[i].mName) ==0)
+ {
+ return mCharacterSheet.mLevelList[level].mStreetRace.mList[i].mBestTime;
+ }
+ }
+
+#ifndef FINAL
+ //you asserted here cause your name for the street race is not found
+ rTuneAssert(0);
+#endif
+ return -666;
+ }
+
+
+ int CharacterSheetManager::GetStreetRaceBestTime(RenderEnums::LevelEnum level,int index)
+ {
+ if( (index >= 0) && (index<MAX_STREETRACES))
+ {
+ return mCharacterSheet.mLevelList[level].mStreetRace.mList[index].mBestTime;
+ }
+ else
+ {
+ //bad invalid index
+ rTuneAssert(0);
+ }
+
+ return 666;
+ }
+
+
+ char* CharacterSheetManager::QueryCurrentSkin(RenderEnums::LevelEnum level)
+ {
+ return mCharacterSheet.mLevelList[level].mCurrentSkin;
+ }
+
+ int CharacterSheetManager::SetCurrentSkin(RenderEnums::LevelEnum level,char* skinName)
+ {
+ strncpy(mCharacterSheet.mLevelList[level].mCurrentSkin,skinName,16);
+ mCharacterSheet.mLevelList[level].mCurrentSkin[15] = '\0';
+ return 0;
+ }
+
+
diff --git a/game/code/mission/charactersheet/charactersheetmanager.h b/game/code/mission/charactersheet/charactersheetmanager.h
new file mode 100644
index 0000000..6a6c093
--- /dev/null
+++ b/game/code/mission/charactersheet/charactersheetmanager.h
@@ -0,0 +1,239 @@
+#ifndef CHARACTERSHEETMANAGER_H
+#define CHARACTERSHEETMANAGER_H
+#include <mission/charactersheet/charactersheet.h>
+#include <presentation/tutorialmode.h>
+#include <render/enums/renderenums.h>
+#include <data/gamedata.h>
+#include <p3d/entity.hpp>
+
+//CharacterSheet Manager. Use this class to manipuated and retrieve data from the character sheet
+//The Character sheet records the users progress and items collected.
+
+
+
+class CharacterSheetManager: GameDataHandler
+ {
+ public:
+
+ enum eCollectableType
+ {
+ eCard,
+ eCoin,
+ eCamera
+ };
+
+
+ //Load Save Functionality
+ virtual void LoadData( const GameDataByte* dataBuffer, unsigned int numBytes );
+ virtual void SaveData( GameDataByte* dataBuffer,unsigned int numBytes );
+
+ inline virtual void ResetData() { InitCharacterSheet(); }
+
+ //Updates the Reward contained by the Rewards Manager
+ //This should be called whenever the CharacterSheet is loaded from
+ //the Memory Card or Hard Disk
+ int UpdateRewardsManager();
+
+
+ //Set a Collectable to be true
+ //you can use the cards name or the index of the card in the array (0 - 6)
+ //as parameters
+
+
+
+ void SetCardCollected(RenderEnums::LevelEnum level,int CardID);
+ void SetCardCollected(RenderEnums::LevelEnum level,char* CardName);
+
+
+
+ //use these methods to modifiy the number of Tokens the character has in their inventory
+ //Chuck: Update the methods below shouldnt take parameters anymore since number of tokens
+ //The player has is carried over from level to level damn, lousy designers changing their mind.
+ // so just use any valid parameters.
+
+ int GetNumberOfTokens(RenderEnums::LevelEnum level);
+ void SetNumberOfTokens(RenderEnums::LevelEnum level,int number);
+ void AddTokens(RenderEnums::LevelEnum level,int number);
+ void SubtractTokens(RenderEnums::LevelEnum level,int number);
+
+
+ //methods to fill in a blank character sheet
+
+ //can add a collectable, using the colllectables name as unique key or by trying to place it in the slot by index
+ //methods return 0 for success and -1 for failure , -2 if the item is already in the list.
+ int AddCard(RenderEnums::LevelEnum level,char* name);
+ int AddCard(RenderEnums::LevelEnum level,int index);
+
+
+
+ int AddMission(RenderEnums::LevelEnum level, char* name);
+ int AddStreetRace(RenderEnums::LevelEnum level, char* name);
+ int AddBonusMission(RenderEnums::LevelEnum level,char* name);
+ int AddGambleRace(RenderEnums::LevelEnum level,char* name);
+
+ void SetStreetRaceBestTime(RenderEnums::LevelEnum level,char* name,int seconds);
+ void SetGambleRaceBestTime(RenderEnums::LevelEnum level,int seconds);
+
+ int GetGambleRaceBestTime(RenderEnums::LevelEnum level);
+ int GetStreetRaceBestTime(RenderEnums::LevelEnum level,char* name);
+ int GetStreetRaceBestTime(RenderEnums::LevelEnum level,int index);
+
+
+ char* QueryCurrentSkin(RenderEnums::LevelEnum level);
+ int SetCurrentSkin(RenderEnums::LevelEnum level,char* skinName);
+
+
+ //Init CharacterSheet with defaults, clear all info. FE should call this for new games.
+ void InitCharacterSheet();
+
+ //methods for recording mission results, this is called for all missions,streetraces and the bonus mission
+ void SetMissionComplete(RenderEnums::LevelEnum level,char* name, bool completed_secondary_objective,int seconds = -1);
+ void IncrementMissionAttempt(RenderEnums::LevelEnum level,char* name);
+ void SetMissionSkipped (RenderEnums::LevelEnum level,char* name);
+ void SetMissionSkipped (RenderEnums::LevelEnum level,RenderEnums::MissionEnum mission);
+ void SetFMVUnlocked (RenderEnums::LevelEnum level);
+
+
+ //this method gets called/updated in gameplaymanagers::SetCurrentMissionMethod
+ void SetCurrentMission(RenderEnums::LevelEnum level, RenderEnums::MissionEnum mission_number);
+
+ //methods for Token operations
+ void SetPurchasedRewards (RenderEnums::LevelEnum level, int index);
+ bool QueryPurchasedReward (RenderEnums::LevelEnum level, int index);
+
+
+ //PhoneBooth Methods Used to Record and Retrieve Info about the Cars the User has purchased
+ //this -1 if inventory is full, or car index. Use this index to update the state of the
+ //car in the inventory. If the car already exists the state is set to the provided values.
+ int AddCarToInventory( const char* Name );
+ // Returns -1 if car isn't in inventory.
+ int GetCarIndex( const char* Name );
+ int GetNumberOfCarsInInventory();
+
+ // Returns eNull if index is out of range,
+ // eNoDamage if hitpoints == total hitpoints.
+ // eDamaged if hitpoints < total hitpoints, > 0.0f;
+ // eHusk if hitpoints == 0.0f;
+ CarDamageStateEnum GetCarDamageState( int CarIndex );
+
+ //returns car's health. 0.0f = husk. 1.0f = no damage.
+ float GetCarHealth( int CarIndex );
+ void UpdateCarHealth( int CarIndex, float Health );
+
+ //FE STUFF: Use these methods for the Scrapbook
+ int QueryNumMissionsCompleted(RenderEnums::LevelEnum level);
+ int QueryNumBonusMissionsCompleted(RenderEnums::LevelEnum level);
+ int QueryNumStreetRacesCompleted(RenderEnums::LevelEnum level);
+ int QueryNumCarUnlocked(RenderEnums::LevelEnum level);
+ int QueryNumSkinsUnlocked(RenderEnums::LevelEnum level);
+ int QueryNumCardsCollected(RenderEnums::LevelEnum level);
+ int QueryNumWaspsDestroyed(RenderEnums::LevelEnum level);
+ int QueryNumGagsViewed(RenderEnums::LevelEnum level);
+ bool QueryGagViewed(RenderEnums::LevelEnum level, unsigned which);
+ bool QueryFMVUnlocked(RenderEnums::LevelEnum level);
+ float QueryPercentLevelCompleted(RenderEnums::LevelEnum level) const;
+ float QueryPercentGameCompleted() const;
+
+ bool IsAllStoryMissionsCompleted();
+
+ //Wasp and gag recording
+
+ void IncNumWaspsDestroyed(RenderEnums::LevelEnum level);
+ void AddGagViewed(RenderEnums::LevelEnum level, unsigned which);
+
+
+ //query methods for races
+ MissionRecord* QueryMissionStatus(RenderEnums::LevelEnum ,char* name);
+ MissionRecord* QueryMissionStatus(RenderEnums::LevelEnum ,int index);
+
+ //get the number of attempts for a mission
+ int QueryNumberOfAttempts(RenderEnums::LevelEnum level, int index);
+
+ MissionRecord* QueryStreetRaceStatus(RenderEnums::LevelEnum ,char* name);
+ MissionRecord* QueryStreetRaceStatus(RenderEnums::LevelEnum ,int index);
+
+ MissionRecord* QueryBonusMissionStatus(RenderEnums::LevelEnum level, char* name);
+ MissionRecord* QueryBonusMissionStatus(RenderEnums::LevelEnum level, int index);
+
+
+
+ //query methods for Collectables. the Loaders and Creation processes should query
+ //on chunk encounter before creating the instance
+
+ bool QueryCardCollected(RenderEnums::LevelEnum level,int index);
+ bool QueryCardCollected(RenderEnums::LevelEnum level,char* CardName);
+
+
+
+ //used by the FE for resume game.
+ CurrentMissionStruct QueryCurrentMission();
+ CurrentMissionStruct QueryHighestMission();
+
+ bool QueryNavSystemSetting();
+ void SetNavSystemOn(bool setting);
+
+ //query methods for rewards
+ bool QueryBonusMissionCompleted(RenderEnums::LevelEnum level);
+ bool QueryAllStreetRacesCompleted(RenderEnums::LevelEnum level);
+ bool QueryAllCardsCollected(RenderEnums::LevelEnum level);
+
+ unsigned char* GetPersistentObjectStates( void ) { return &(mCharacterSheet.mPersistentObjectStates[0]); }
+
+ bool IsMiniGameUnlocked() const;
+
+ bool IsState(tUID State);
+ bool IsState(int Index);
+ void SetState(tUID State, bool IsSet);
+ void SetState(int Index, bool IsSet);
+
+ //Static Methods for getting access
+ static CharacterSheetManager* GetInstance();
+ static CharacterSheetManager* CreateInstance();
+ static void DestroyInstance ();
+
+ private:
+
+ CharacterSheetManager();
+ virtual ~CharacterSheetManager();
+
+ //declared but not defined.
+ CharacterSheetManager ( const CharacterSheetManager& );
+ CharacterSheetManager& operator=( const CharacterSheetManager& );
+// PlayerOptions* GetPlayerOptions ();
+
+ MissionRecord* GetMissionRecord(RenderEnums::LevelEnum level,char* name);
+ MissionRecord* GetMissionRecord(RenderEnums::LevelEnum level,int index );
+
+ MissionRecord* GetStreetRaceRecord(RenderEnums::LevelEnum level, char* name);
+ MissionRecord* GetStreetRaceRecord(RenderEnums::LevelEnum level, int index );
+
+ MissionRecord* GetBonusMissionRecord(RenderEnums::LevelEnum level,char* name);
+
+ Record* GetCollectableRecord(RenderEnums::LevelEnum level,eCollectableType type, char* name);
+ Record* GetCollectableRecord(RenderEnums::LevelEnum level,eCollectableType type, int index);
+
+ CarCharacterSheet* GetCarCharacterSheet(char * name);
+
+ //implement if dusit doesnt do the above.
+ //CardRecord* GetCardRecord(eLevelID,char* name);
+
+
+ //member variables
+ CharacterSheet mCharacterSheet;
+
+ static CharacterSheetManager* spInstance;
+ };
+
+
+//Global function to get access to the singleton
+inline CharacterSheetManager* GetCharacterSheetManager()
+ {
+ return ( CharacterSheetManager::GetInstance() );
+ }
+
+
+
+#endif //CHARACTERSHEETMANAGER_H
+
+
+ \ No newline at end of file
diff --git a/game/code/mission/conditions/allconditions.cpp b/game/code/mission/conditions/allconditions.cpp
new file mode 100644
index 0000000..71eaa4b
--- /dev/null
+++ b/game/code/mission/conditions/allconditions.cpp
@@ -0,0 +1,13 @@
+#include <mission/conditions/damagecondition.cpp>
+#include <mission/conditions/followcondition.cpp>
+#include <mission/conditions/leaveinteriorcondition.cpp>
+#include <mission/conditions/missioncondition.cpp>
+#include <mission/conditions/outofboundscondition.cpp>
+#include <mission/conditions/racecondition.cpp>
+#include <mission/conditions/timeoutcondition.cpp>
+#include <mission/conditions/vehiclecondition.cpp>
+#include <mission/conditions/positioncondition.cpp>
+#include <mission/conditions/vehiclecarryingstateprop.cpp>
+#include <mission/conditions/getoutofcarcondition.cpp>
+#include <mission/conditions/notabductedcondition.cpp>
+#include <mission/conditions/keepbarrelcondition.cpp>
diff --git a/game/code/mission/conditions/damagecondition.cpp b/game/code/mission/conditions/damagecondition.cpp
new file mode 100644
index 0000000..acb2e10
--- /dev/null
+++ b/game/code/mission/conditions/damagecondition.cpp
@@ -0,0 +1,201 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement DamageCondition
+//
+// History: 24/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <events/eventmanager.h>
+#include <mission/gameplaymanager.h>
+
+#include <mission/conditions/damagecondition.h>
+
+#include <worldsim/redbrick/vehicle.h>
+
+const static float CLOSE_DAMAGE_THRESHOLD = 0.1f; // was .2
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// DamageCondition::DamageCondition
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DamageCondition::DamageCondition() :
+ mMinValue( 0.0f ),
+ mLastVehicleDamage( 1.0f ),
+ mbCarDestroyed(false)
+{
+ this->SetType( COND_VEHICLE_DAMAGE );
+}
+
+//==============================================================================
+// DamageCondition::~DamageCondition
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DamageCondition::~DamageCondition()
+{
+}
+
+//=============================================================================
+// DamageCondition::OnInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DamageCondition::OnInitialize()
+{
+
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DAMAGED );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_ENTERING_PLAYER_CAR );
+ GetEventManager()->AddListener( this, EVENT_ENTERING_TRAFFIC_CAR );
+ GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_CAR_EXPLOSION_DONE );
+
+ //chuck: update our vehicle pointer incase the user has switched cars
+ if (GetGameplayManager()->GetCurrentVehicle() != NULL)
+ {
+ SetVehicle(GetGameplayManager()->GetCurrentVehicle());
+ }
+
+
+}
+
+//=============================================================================
+// DamageCondition::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DamageCondition::OnFinalize()
+{
+ GetEventManager()->RemoveAll(this);
+}
+
+//=============================================================================
+// DamageCondition::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void DamageCondition::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+
+ case EVENT_VEHICLE_DESTROYED:
+ case EVENT_VEHICLE_DAMAGED:
+ {
+ Vehicle* pVehicle = reinterpret_cast<Vehicle*>(pEventData);
+
+ if( pVehicle == GetVehicle())
+ {
+ mLastVehicleDamage = pVehicle->GetVehicleLifePercentage(pVehicle->mHitPoints);
+ if( mLastVehicleDamage <= mMinValue )
+ {
+ //flag that our car is destroyed and wait for explosion to play
+ mbCarDestroyed = true;
+ }
+ }
+ break;
+ }
+ case EVENT_CAR_EXPLOSION_DONE:
+ {
+ if (mbCarDestroyed == true)
+ {
+ //explosion is done playing signal failure condition.
+ SetIsViolated (true);
+ }
+ break;
+ }
+
+ case EVENT_ENTERING_PLAYER_CAR:
+ case EVENT_ENTERING_TRAFFIC_CAR:
+ case EVENT_GETINTOVEHICLE_END:
+ //chuck: we should update our vehicle pointer incase user decides to switch cars during a this stage.
+ {
+ this->SetVehicle(GetGameplayManager()->GetCurrentVehicle());
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ MissionCondition::HandleEvent( id, pEventData );
+}
+
+//=============================================================================
+// DamageCondition::IsClose
+//=============================================================================
+// Description: Indicates whether this condition is at all close to failure.
+// Very subjective, used for interactive music
+//
+// Parameters: None
+//
+// Return: True if close, false otherwise
+//
+//=============================================================================
+bool DamageCondition::IsClose()
+{
+ //
+ // Smashing stuff is exciting
+ //
+ return( true );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/mission/conditions/damagecondition.h b/game/code/mission/conditions/damagecondition.h
new file mode 100644
index 0000000..85b12f3
--- /dev/null
+++ b/game/code/mission/conditions/damagecondition.h
@@ -0,0 +1,61 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: damagecondition.h
+//
+// Description: Blahblahblah
+//
+// History: 24/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef DAMAGECONDITION_H
+#define DAMAGECONDITION_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/conditions/vehiclecondition.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class DamageCondition : public VehicleCondition
+{
+public:
+ DamageCondition();
+ virtual ~DamageCondition();
+
+ void SetMinValue( float value ) { mMinValue = value; }
+ float GetMinValue() { return mMinValue; }
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ bool IsClose();
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+
+private:
+
+ //Prevent wasteful constructor creation.
+ DamageCondition( const DamageCondition& damagecondition );
+ DamageCondition& operator=( const DamageCondition& damagecondition );
+
+ float mMinValue;
+
+ float mLastVehicleDamage;
+ bool mbCarDestroyed;
+};
+
+
+#endif //DAMAGECONDITION_H
diff --git a/game/code/mission/conditions/followcondition.cpp b/game/code/mission/conditions/followcondition.cpp
new file mode 100644
index 0000000..9e99cd9
--- /dev/null
+++ b/game/code/mission/conditions/followcondition.cpp
@@ -0,0 +1,248 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement FollowCondition
+//
+// History: 04/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+#include <radmath/radmath.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <mission/conditions/followcondition.h>
+
+#include <ai/vehicle/vehicleai.h>
+
+#include <mission/gameplaymanager.h>
+
+#include <roads/road.h>
+#include <roads/roadmanager.h>
+
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/vehicle.h>
+
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#include <interiors/interiormanager.h>
+
+#include <worldsim/hitnrunmanager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// FollowCondition::FollowCondition
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+FollowCondition::FollowCondition() :
+ mMinDistance( 0.0f ),
+ mMaxDistance( 0.0f )
+{
+ this->SetType( COND_FOLLOW_DISTANCE );
+}
+
+//==============================================================================
+// FollowCondition::~FollowCondition
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+FollowCondition::~FollowCondition()
+{
+}
+
+//=============================================================================
+// FollowCondition::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void FollowCondition::Update( unsigned int elapsedTime )
+{
+ rTuneAssertMsg( !rmt::Epsilon( mMaxDistance, 0.0f ), "FollowCondition must have a max distance greater than 0!\n" );
+
+ muTime += elapsedTime;
+
+ if( muTime > MIN_UPDATE_PERIOD )
+ {
+ muTime = 0;
+
+ if ( !GetInteriorManager()->IsEntering() &&
+ !GetInteriorManager()->IsExiting() &&
+ !GetHitnRunManager()->BustingPlayer() )
+ {
+ float dist = CalculateDistanceToTarget();
+
+ if( dist > mMaxDistance * mMaxDistance )
+ {
+ SetIsViolated( true );
+ }
+ else
+ {
+ CGuiScreenHud* pHUD = GetCurrentHud();
+ if( pHUD != NULL )
+ {
+ // update proximity meter on HUD
+ //
+ pHUD->SetProximityMeter( 1.0f - rmt::Sqrt( dist ) / mMaxDistance );
+
+ // pHUD->GetHudMap( 0 )->UpdateAICarDistance( rmt::Sqrt(dist), mMaxDistance );
+ }
+ }
+ }
+ }
+}
+
+//=============================================================================
+// FollowCondition::IsChaseCondition
+//=============================================================================
+// Description: Indicate that this condition involves chasing
+//
+// Parameters: None
+//
+// Return: True
+//
+//=============================================================================
+bool FollowCondition::IsChaseCondition()
+{
+ return( true );
+}
+
+//=============================================================================
+// FollowCondition::IsClose
+//=============================================================================
+// Description: Indicates whether this condition is at all close to failure.
+// Very subjective, used for interactive music
+//
+// Parameters: None
+//
+// Return: True if close, false otherwise
+//
+//=============================================================================
+bool FollowCondition::IsClose()
+{
+ //
+ // For now, assume that follow conditions are always tense, since it's
+ // hard to say whether you're running away with this one
+ //
+ return( true );
+}
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// FollowCondition::OnInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FollowCondition::OnInitialize()
+{
+ //Set this vehicle as the focus of the HUD.
+ rAssert( GetVehicle() );
+ VehicleAI* vehicleAI = GetVehicleCentral()->GetVehicleAI( GetVehicle() );
+ rAssert( vehicleAI );
+
+ int hudID = vehicleAI->GetHUDIndex();
+ if ( GetCurrentHud() )
+ {
+ GetCurrentHud()->GetHudMap( 0 )->SetFocalPointIcon( hudID );
+ }
+}
+
+//=============================================================================
+// FollowCondition::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FollowCondition::OnFinalize()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// FollowCondition::CalculateDistanceToTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float FollowCondition::CalculateDistanceToTarget()
+{
+ //
+ // TODO: Find the distance along the road?
+ //
+ rmt::Vector myPos;
+
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( myPos );
+
+ Vehicle* target = GetVehicle();
+ rAssert( target != NULL );
+
+ rmt::Vector targetPos;
+ target->GetPosition( &targetPos );
+
+ targetPos.Sub( myPos );
+ return targetPos.MagnitudeSqr();
+
+} \ No newline at end of file
diff --git a/game/code/mission/conditions/followcondition.h b/game/code/mission/conditions/followcondition.h
new file mode 100644
index 0000000..784ef91
--- /dev/null
+++ b/game/code/mission/conditions/followcondition.h
@@ -0,0 +1,128 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: followcondition.h
+//
+// Description: Blahblahblah
+//
+// History: 04/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef FOLLOWCONDITION_H
+#define FOLLOWCONDITION_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/conditions/vehiclecondition.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class FollowCondition : public VehicleCondition
+{
+public:
+ FollowCondition();
+ virtual ~FollowCondition();
+
+ void SetMaxDistance( float maxDist );
+ float GetMaxDistance();
+
+ void SetMinDistance( float minDist );
+ float GetMinDistance();
+
+ virtual void Update( unsigned int elapsedTime );
+
+ bool IsChaseCondition();
+ bool IsClose();
+
+protected:
+ void OnInitialize();
+ void OnFinalize();
+
+private:
+
+ //Prevent wasteful constructor creation.
+ FollowCondition( const FollowCondition& followcondition );
+ FollowCondition& operator=( const FollowCondition& followcondition );
+
+ float CalculateDistanceToTarget();
+
+ static const unsigned int MIN_UPDATE_PERIOD = 100;
+
+ float mMinDistance;
+ float mMaxDistance;
+
+ unsigned int muTime;
+};
+
+//=============================================================================
+// FollowCondition::SetMaxDistance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float maxDist )
+//
+// Return: inline
+//
+//=============================================================================
+inline void FollowCondition::SetMaxDistance( float maxDist )
+{
+ mMaxDistance = maxDist;
+}
+
+//=============================================================================
+// FollowCondition::GetMaxDistance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline float FollowCondition::GetMaxDistance()
+{
+ return mMaxDistance;
+}
+
+//=============================================================================
+// FollowCondition::SetMinDistance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float minDist )
+//
+// Return: inline
+//
+//=============================================================================
+inline void FollowCondition::SetMinDistance( float minDist )
+{
+ mMinDistance = minDist;
+}
+
+//=============================================================================
+// FollowCondition::GetMinDistance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline float FollowCondition::GetMinDistance()
+{
+ return mMinDistance;
+}
+
+#endif //FOLLOWCONDITION_H
diff --git a/game/code/mission/conditions/getoutofcarcondition.cpp b/game/code/mission/conditions/getoutofcarcondition.cpp
new file mode 100644
index 0000000..d343aab
--- /dev/null
+++ b/game/code/mission/conditions/getoutofcarcondition.cpp
@@ -0,0 +1,306 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: getoutofcarcondition.cpp
+//
+// Description: Implement GetOutOfCarCondition
+//
+// History: 4/7/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/conditions/getoutofcarcondition.h>
+
+#include <mission/gameplaymanager.h>
+#include <mission/mission.h>
+#include <mission/objectives/raceobjective.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+
+#include <events/eventmanager.h>
+
+#include <worldsim/character/character.h>
+#include <worldsim/avatarmanager.h>
+
+#include <interiors/interiormanager.h>
+
+#include <worldsim/hitnrunmanager.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// GetOutOfCarCondition::GetOutOfCarCondition
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+GetOutOfCarCondition::GetOutOfCarCondition() :
+
+mTimeAmount( 11000 )
+{
+ SetType( COND_PLAYER_OUT_OF_VEHICLE );
+ mTimeRemainingmilliseconds = 10000; //set it to 10 milliseconds by default
+ mbIsConditionActive = false;
+}
+
+//=============================================================================
+// GetOutOfCarCondition::~GetOutOfCarCondition
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+GetOutOfCarCondition::~GetOutOfCarCondition()
+{
+}
+
+//=============================================================================
+// GetOutOfCarCondition::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void GetOutOfCarCondition::Update( unsigned int elapsedTime )
+{
+ if ( mbIsConditionActive == true )
+ {
+ mTimeRemainingmilliseconds -= static_cast <int>(elapsedTime);
+
+ if ( ( mTimeRemainingmilliseconds <= 0 || GetInteriorManager()->IsInside() ) //If you go inside you fail.
+ &&
+ !GetInteriorManager()->IsEntering() &&
+ !GetInteriorManager()->IsExiting() &&
+ !GetHitnRunManager()->BustingPlayer() )
+ {
+ mTimeRemainingmilliseconds = 0;
+ //HE has failed!
+ SetIsViolated( true );
+ }
+ }
+}
+
+//=============================================================================
+// GetOutOfCarCondition::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void GetOutOfCarCondition::HandleEvent( EventEnum id, void* pEventData )
+{
+ if ( id == EVENT_GETOUTOFVEHICLE_END )
+ {
+ //The character has left the car!
+ //Start the count down.
+ Character* whichCharacter = static_cast< Character* >( pEventData );
+
+ //check to see if the Charactar bailing out of the car is a PC or NPC
+ //if its a NPC then this condition should not apply to them
+ if ( whichCharacter != GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter())
+ {
+ return;
+ }
+
+
+
+ Vehicle* whichVehicle = whichCharacter->GetTargetVehicle();
+ rAssert( whichVehicle );
+
+ if ( mbIsConditionActive == false ) //This is the first time and not me cheating by jumping in a traffic car
+ {
+ //We're gonna look at the car since this is the car we care about.
+ mMyVehicle = whichVehicle;
+ mbIsConditionActive = true;
+
+ GetEventManager()->TriggerEvent(EVENT_OUTOFCAR_CONDITION_ACTIVE,this);
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_TIMER_TEMP );
+ currentHud->SetTimerBlinkingInterval(10000,0);
+ }
+ }
+ else if ( whichVehicle != mMyVehicle )
+ {
+ //Ignore.
+ return;
+ }
+
+
+ //Set the TimeRemainmilliseconds
+ mTimeRemainingmilliseconds = mTimeAmount;
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->DisplayMessage( true, 299 ); //299 is the magic number in the textbible for the gET IN CAR message.
+ }
+
+ }
+ else if ( id == EVENT_GETINTOVEHICLE_START )
+ {
+ //Now he's back in!
+
+ Character* whichCharacter = static_cast< Character* >( pEventData );
+
+ Vehicle* whichVehicle = whichCharacter->GetTargetVehicle();
+ rAssert( whichVehicle );
+
+ //make sure we get back into the same vehicle
+ if ( whichVehicle == mMyVehicle )
+ {
+ mbIsConditionActive = false;
+ GetEventManager()->TriggerEvent(EVENT_OUTOFCAR_CONDITION_INACTIVE);
+ mTimeRemainingmilliseconds = mTimeAmount;
+ if (GetGameplayManager()->GetCurrentMission()->IsWagerMission() == true)
+ {
+ RaceObjective* pRaceObjective= NULL;
+
+ pRaceObjective = dynamic_cast <RaceObjective*>( GetGameplayManager()->GetCurrentMission()->GetCurrentStage()->GetObjective());
+ rAssert(pRaceObjective != NULL);
+
+ int partime = pRaceObjective->GetParTime();
+
+ GetCurrentHud()->SetTimerBlinkingInterval( (partime-10)*1000,partime*1000);
+ }
+
+
+ }
+ else
+ {
+ SetIsViolated( true );
+ }
+ }
+
+ MissionCondition::HandleEvent( id, pEventData );
+}
+
+
+//return the time remaining in milliseconds the player has to get back into there old vehicle
+
+int GetOutOfCarCondition::GetTimeRemainingTilFailuremilliseconds()
+{
+ return mTimeRemainingmilliseconds;
+}
+
+
+
+//=============================================================================
+// GetOutOfCarCondition::IsClose
+//=============================================================================
+// Description: Indicates whether this condition is at all close to failure.
+// Very subjective, used for interactive music
+//
+// Parameters: None
+//
+// Return: True if close, false otherwise
+//
+//=============================================================================
+bool GetOutOfCarCondition::IsClose()
+{
+ return( mTimeRemainingmilliseconds > 1000 && mTimeRemainingmilliseconds < 3000 );
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// GetOutOfCarCondition::OnInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GetOutOfCarCondition::OnInitialize()
+{
+ GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_START );
+}
+
+//=============================================================================
+// GetOutOfCarCondition::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GetOutOfCarCondition::OnFinalize()
+{
+ GetEventManager()->RemoveListener( this, EVENT_GETOUTOFVEHICLE_END );
+ GetEventManager()->RemoveListener( this, EVENT_GETINTOVEHICLE_START );
+
+ GetEventManager()->TriggerEvent(EVENT_OUTOFCAR_CONDITION_INACTIVE);
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_TIMER_TEMP );
+ }
+
+ mbIsConditionActive = false;
+}
+
+
+
+void GetOutOfCarCondition::SetConditionActive()
+{
+ mbIsConditionActive = true;
+ mMyVehicle = GetGameplayManager()->GetCurrentVehicle();
+ GetEventManager()->TriggerEvent(EVENT_OUTOFCAR_CONDITION_ACTIVE,this);
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_TIMER_TEMP );
+ currentHud->SetTimerBlinkingInterval(10000,0);
+ }
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/mission/conditions/getoutofcarcondition.h b/game/code/mission/conditions/getoutofcarcondition.h
new file mode 100644
index 0000000..d44aab8
--- /dev/null
+++ b/game/code/mission/conditions/getoutofcarcondition.h
@@ -0,0 +1,76 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: getoutofcarcondition.h
+//
+// Description: Blahblahblah
+//
+// History: 4/7/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef GETOUTOFCARCONDITION_H
+#define GETOUTOFCARCONDITION_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/conditions/missioncondition.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class GetOutOfCarCondition : public MissionCondition
+{
+public:
+ GetOutOfCarCondition();
+ virtual ~GetOutOfCarCondition();
+
+ // the Mission Stage should call this every frame
+ virtual void Update( unsigned int elapsedTime );
+
+ virtual bool IsClose();
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ virtual int GetTimeRemainingTilFailuremilliseconds ();
+
+
+ bool IsConditionActive( ) { return mbIsConditionActive; };
+ void SetTime( unsigned int time ) { mTimeAmount = time + 1000; };
+ void SetConditionActive();
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+
+private:
+
+ int mTimeAmount;
+ Vehicle* mMyVehicle;
+
+ int mOldTime;
+
+
+ bool mbIsConditionActive; //state of the condition false if user is in a car , true if they have left the car
+ int mTimeRemainingmilliseconds; //amount of time remaining till user fails due to being out of the car
+
+
+ //Prevent wasteful constructor creation.
+ GetOutOfCarCondition( const GetOutOfCarCondition& getoutofcarcondition );
+ GetOutOfCarCondition& operator=( const GetOutOfCarCondition& getoutofcarcondition );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //GETOUTOFCARCONDITION_H
diff --git a/game/code/mission/conditions/keepbarrelcondition.cpp b/game/code/mission/conditions/keepbarrelcondition.cpp
new file mode 100644
index 0000000..a61c698
--- /dev/null
+++ b/game/code/mission/conditions/keepbarrelcondition.cpp
@@ -0,0 +1,132 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: keepbarrelcondition.cpp
+//
+// Description: Implement KeepBarrelCondition
+//
+// History: 5/30/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/conditions/keepbarrelcondition.h>
+#include <mission/gameplaymanager.h>
+#include <mission/mission.h>
+
+#include <events/eventenum.h>
+#include <events/eventmanager.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// KeepBarrelCondition::KeepBarrelCondition
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+KeepBarrelCondition::KeepBarrelCondition() :
+ mJumpBackBy( 1 )
+{
+}
+
+//=============================================================================
+// KeepBarrelCondition::~KeepBarrelCondition
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+KeepBarrelCondition::~KeepBarrelCondition()
+{
+}
+
+//=============================================================================
+// KeepBarrelCondition::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void KeepBarrelCondition::HandleEvent( EventEnum id, void* pEventData )
+{
+ if ( id == EVENT_STATEPROP_COLLECTIBLE_DESTROYED )
+ {
+ //This is kinda hacky, but it's only for this mission anyway.
+ //Maybe I should signal and have the stage change in the update
+ //of the mission.
+ //TODO: Think about it.
+ GetGameplayManager()->GetCurrentMission()->SpecialCaseStageBackup( mJumpBackBy );
+ }
+
+ MissionCondition::HandleEvent( id, pEventData );
+}
+
+
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// KeepBarrelCondition::OnInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void KeepBarrelCondition::OnInitialize()
+{
+ //Add a listener for the barrel exploding
+ GetEventManager()->AddListener( this, EVENT_STATEPROP_COLLECTIBLE_DESTROYED );
+}
+
+//=============================================================================
+// KeepBarrelCondition::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void KeepBarrelCondition::OnFinalize()
+{
+ //Remove the listeners
+ GetEventManager()->RemoveListener( this, EVENT_STATEPROP_COLLECTIBLE_DESTROYED );
+}
diff --git a/game/code/mission/conditions/keepbarrelcondition.h b/game/code/mission/conditions/keepbarrelcondition.h
new file mode 100644
index 0000000..cd226fa
--- /dev/null
+++ b/game/code/mission/conditions/keepbarrelcondition.h
@@ -0,0 +1,58 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: keepbarrelcondition.h
+//
+// Description: Blahblahblah
+//
+// History: 5/30/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef KEEPBARRELCONDITION_H
+#define KEEPBARRELCONDITION_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/conditions/missioncondition.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class KeepBarrelCondition : public MissionCondition
+{
+public:
+ KeepBarrelCondition();
+ virtual ~KeepBarrelCondition();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void JumpBackBy( unsigned int num ) { mJumpBackBy = num; };
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+
+private:
+ unsigned int mJumpBackBy;
+
+ //Prevent wasteful constructor creation.
+ KeepBarrelCondition( const KeepBarrelCondition& keepbarrelcondition );
+ KeepBarrelCondition& operator=( const KeepBarrelCondition& keepbarrelcondition );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //KEEPBARRELCONDITION_H
diff --git a/game/code/mission/conditions/leaveinteriorcondition.cpp b/game/code/mission/conditions/leaveinteriorcondition.cpp
new file mode 100644
index 0000000..b93f835
--- /dev/null
+++ b/game/code/mission/conditions/leaveinteriorcondition.cpp
@@ -0,0 +1,153 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: LeaveInteriorCondition.cpp
+//
+// Description: Implement LeaveInteriorCondition
+//
+// History: 30/08/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <events/eventmanager.h>
+
+#include <mission/conditions/LeaveInteriorCondition.h>
+
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// LeaveInteriorCondition::LeaveInteriorCondition
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+LeaveInteriorCondition::LeaveInteriorCondition()
+{
+ this->SetType( COND_LEAVE_INTERIOR );
+}
+
+//=============================================================================
+// LeaveInteriorCondition::~LeaveInteriorCondition
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+LeaveInteriorCondition::~LeaveInteriorCondition()
+{
+}
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// LeaveInteriorCondition::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void LeaveInteriorCondition::HandleEvent( EventEnum id, void* pEventData )
+{
+ if ( id == (EVENT_LOCATOR + LocatorEvent::INTERIOR_EXIT) )
+ {
+ SetLeaveInterior( true );
+ }
+
+ MissionCondition::HandleEvent( id, pEventData );
+}
+
+//=============================================================================
+// LeaveInteriorCondition::IsClose
+//=============================================================================
+// Description: Indicates whether this condition is at all close to failure.
+// Very subjective, used for interactive music
+//
+// Parameters: None
+//
+// Return: True if close, false otherwise
+//
+//=============================================================================
+bool LeaveInteriorCondition::IsClose()
+{
+ //
+ // There's nothing tense about leaving a building
+ //
+ return( false );
+}
+
+//******************************************************************************
+//
+// ProtectedMember Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// LeaveInteriorCondition::OnInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LeaveInteriorCondition::OnInitialize()
+{
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_LOCATOR + LocatorEvent::INTERIOR_EXIT) );
+}
+
+//=============================================================================
+// LeaveInteriorCondition::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LeaveInteriorCondition::OnFinalize()
+{
+ GetEventManager()->RemoveListener( this, (EventEnum)(EVENT_LOCATOR + LocatorEvent::INTERIOR_EXIT) );
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/mission/conditions/leaveinteriorcondition.h b/game/code/mission/conditions/leaveinteriorcondition.h
new file mode 100644
index 0000000..49fb581
--- /dev/null
+++ b/game/code/mission/conditions/leaveinteriorcondition.h
@@ -0,0 +1,52 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: leaveinteriorcondition.h
+//
+// Description: Blahblahblah
+//
+// History: 30/08/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef LEAVEINTERIORCONDITION_H
+#define LEAVEINTERIORCONDITION_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/conditions/missioncondition.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class LeaveInteriorCondition : public MissionCondition
+{
+public:
+ LeaveInteriorCondition();
+ virtual ~LeaveInteriorCondition();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ bool IsClose();
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+
+private:
+
+ //Prevent wasteful constructor creation.
+ LeaveInteriorCondition( const LeaveInteriorCondition& leaveinteriorcondition );
+ LeaveInteriorCondition& operator=( const LeaveInteriorCondition& leaveinteriorcondition );
+};
+
+
+#endif //LEAVEINTERIORCONDITION_H
diff --git a/game/code/mission/conditions/missioncondition.cpp b/game/code/mission/conditions/missioncondition.cpp
new file mode 100644
index 0000000..6468a2b
--- /dev/null
+++ b/game/code/mission/conditions/missioncondition.cpp
@@ -0,0 +1,167 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement MissionCondition
+//
+// History: 09/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <mission/conditions/missioncondition.h>
+
+#include <events/eventmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+bool MissionCondition::mFailedHitNRun = false;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// MissionCondition::MissionCondition
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MissionCondition::MissionCondition() :
+ mType( COND_INVALID ),
+ mbIsViolated( false ),
+ mLeaveInterior( false )
+{
+}
+
+//==============================================================================
+// MissionCondition::~MissionCondition
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MissionCondition::~MissionCondition()
+{
+}
+
+//=============================================================================
+// MissionCondition::Initialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionCondition::Initialize()
+{
+ mbIsViolated = false;
+ mLeaveInterior = false;
+
+ mFailedHitNRun = false;
+ //GetEventManager()->AddListener( this, EVENT_HIT_AND_RUN_CAUGHT );
+
+ OnInitialize();
+}
+
+//=============================================================================
+// MissionCondition::Finalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionCondition::Finalize()
+{
+ //GetEventManager()->RemoveListener( this, EVENT_HIT_AND_RUN_CAUGHT );
+
+ OnFinalize();
+
+ mFailedHitNRun = false;
+}
+
+//=============================================================================
+// MissionCondition::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void MissionCondition::HandleEvent( EventEnum id, void* pEventData )
+{
+ //if ( id == EVENT_HIT_AND_RUN_CAUGHT )
+ //{
+ // mbIsViolated = true;
+ // mFailedHitNRun = true;
+ //}
+}
+
+//=============================================================================
+// MissionCondition::IsChaseCondition
+//=============================================================================
+// Description: Indicates whether this condition involves chasing. Assume
+// false, and let those classes which have chases override this
+// function
+//
+// Parameters: None
+//
+// Return: Default value of false, to be overridden by chase conditions
+//
+//=============================================================================
+bool MissionCondition::IsChaseCondition()
+{
+ return( false );
+}
+
+//=============================================================================
+// MissionCondition::IsClose
+//=============================================================================
+// Description: Indicates whether this condition is at all close to failure.
+// Very subjective, used for interactive music
+//
+// Parameters: None
+//
+// Return: True if close, false otherwise
+//
+//=============================================================================
+bool MissionCondition::IsClose()
+{
+ return( true );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/mission/conditions/missioncondition.h b/game/code/mission/conditions/missioncondition.h
new file mode 100644
index 0000000..b76ff15
--- /dev/null
+++ b/game/code/mission/conditions/missioncondition.h
@@ -0,0 +1,222 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: missioncondition.h
+//
+// Description: Blahblahblah
+//
+// History: 09/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef MISSIONCONDITION_H
+#define MISSIONCONDITION_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+namespace MissionConditionNames
+{
+ const char* const Name[] =
+ {
+ "invalid",
+ "damage",
+ "playerhit",
+ "timeout",
+ "outofvehicle",
+ "followdistance",
+ "outofbounds",
+ "race",
+ "leaveinterior",
+ "position",
+ "carryingspcollectible",
+ "notabducted",
+ "hitandruncought",
+ "keepbarrel",
+ "getcollectibles"
+ };
+
+}
+
+class MissionCondition : public EventListener
+{
+public:
+ MissionCondition();
+ virtual ~MissionCondition();
+
+ enum ConditionTypeEnum
+ {
+ COND_INVALID,
+ COND_VEHICLE_DAMAGE,
+ COND_PLAYER_HIT,
+ COND_TIME_OUT,
+ COND_PLAYER_OUT_OF_VEHICLE,
+ COND_FOLLOW_DISTANCE,
+ COND_OUT_OF_BOUNDS,
+ COND_RACE,
+ COND_LEAVE_INTERIOR,
+ COND_POSITION,
+ COND_CARRYING_STATEPROP_COLLECTIBLE,
+ COND_NOT_ABDUCTED,
+ COND_HIT_AND_RUN_CAUGHT,
+ COND_KEEP_BARREL,
+ COND_GET_COLLECTIBLES,
+ NUM_CONDITIONS
+ };
+
+ ConditionTypeEnum GetType() const;
+
+ // Index in the text bible of the description for this condition
+ //unsigned int GetDescriptionIndex();
+ // Index in the text bible of the message when this condition fails
+ //unsigned int GetFailMessageIndex();
+
+ void Initialize();
+ void Finalize();
+
+ // Returns true if this condition has been violated
+ bool IsViolated();
+ bool LeaveInterior();
+
+ // the Mission Stage should call this every frame
+ virtual void Update( unsigned int elapsedTime ) {};
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ virtual bool IsChaseCondition();
+ virtual bool IsClose();
+ void SetIsViolated( bool bIsViolated );
+
+protected:
+ virtual void OnInitialize() {};
+ virtual void OnFinalize() {};
+
+ void SetType( ConditionTypeEnum type );
+
+ void SetLeaveInterior( bool bIsLeaving );
+
+ ConditionTypeEnum mType;
+
+private:
+ //Prevent wasteful constructor creation.
+ MissionCondition( const MissionCondition& missioncondition );
+ MissionCondition& operator=( const MissionCondition& missioncondition );
+
+ bool mbIsViolated;
+ bool mLeaveInterior;
+
+ static bool mFailedHitNRun; //Shared among all the conditions.
+};
+
+
+//=============================================================================
+// MissionCondition::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline MissionCondition::ConditionTypeEnum
+MissionCondition::GetType() const
+{
+ if ( mFailedHitNRun )
+ {
+ return COND_HIT_AND_RUN_CAUGHT;
+ }
+
+ return mType;
+}
+
+//=============================================================================
+// MissionCondition::GetType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline void
+MissionCondition::SetType( MissionCondition::ConditionTypeEnum type )
+{
+ mType = type;
+}
+
+//=============================================================================
+// MissionCondition::IsViolated
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline bool MissionCondition::IsViolated()
+{
+ return mbIsViolated;
+}
+
+//=============================================================================
+// MissionCondition::LeaveInterior
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool MissionCondition::LeaveInterior()
+{
+ return mLeaveInterior;
+}
+
+//=============================================================================
+// MissionCondition::SetIsViolated
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool bIsViolated )
+//
+// Return: inline
+//
+//=============================================================================
+inline void MissionCondition::SetIsViolated( bool bIsViolated )
+{
+ mbIsViolated = bIsViolated;
+}
+
+//=============================================================================
+// MissionCondition::SetLeaveInterior
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool bIsLeaving )
+//
+// Return: void
+//
+//=============================================================================
+inline void MissionCondition::SetLeaveInterior( bool bIsLeaving )
+{
+ mLeaveInterior = bIsLeaving;
+}
+
+#endif //MISSIONCONDITION_H
diff --git a/game/code/mission/conditions/notabductedcondition.cpp b/game/code/mission/conditions/notabductedcondition.cpp
new file mode 100644
index 0000000..7678d2c
--- /dev/null
+++ b/game/code/mission/conditions/notabductedcondition.cpp
@@ -0,0 +1,78 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: NotAbductedCondition
+//
+// Description: Mission condition - player must not have been abducted by aliens
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <mission/conditions/notabductedcondition.h>
+#include <worldsim/character/character.h>
+#include <worldsim/avatarmanager.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+NotAbductedCondition::NotAbductedCondition()
+{
+
+}
+
+NotAbductedCondition::~NotAbductedCondition()
+{
+
+}
+
+void NotAbductedCondition::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_ABDUCTED:
+ {
+ // Check to see if the character that was abducted was the user
+ Character* character = reinterpret_cast< Character* >( pEventData );
+ if ( character )
+ {
+ Avatar* avatar = GetAvatarManager()->FindAvatarForCharacter( character );
+ if ( avatar )
+ {
+ // It was the player that was sucked up
+ // this condition has failed
+ SetIsViolated( true );
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ MissionCondition::HandleEvent( id, pEventData );
+}
+
+void NotAbductedCondition::OnInitialize()
+{
+ GetEventManager()->AddListener( this, EVENT_ABDUCTED );
+}
+
+void NotAbductedCondition::OnFinalize()
+{
+ GetEventManager()->RemoveAll( this );
+} \ No newline at end of file
diff --git a/game/code/mission/conditions/notabductedcondition.h b/game/code/mission/conditions/notabductedcondition.h
new file mode 100644
index 0000000..2e6936d
--- /dev/null
+++ b/game/code/mission/conditions/notabductedcondition.h
@@ -0,0 +1,68 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: NotAbductedCondition
+//
+// Description: Mission condition - player must not have been abducted by aliens
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef NOTABDUCTEDCONDITION_H
+#define NOTABDUCTEDCONDITION_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <mission/conditions/missioncondition.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// If the player is sucked into the UFO this condition will be false
+//
+// Constraints:
+//
+//
+//===========================================================================
+class NotAbductedCondition : public MissionCondition
+{
+ public:
+
+ NotAbductedCondition();
+ virtual ~NotAbductedCondition();
+
+ protected:
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow NotAbductedCondition from being copied and assigned.
+ NotAbductedCondition( const NotAbductedCondition& );
+ NotAbductedCondition& operator=( const NotAbductedCondition& );
+};
+
+
+#endif \ No newline at end of file
diff --git a/game/code/mission/conditions/outofboundscondition.cpp b/game/code/mission/conditions/outofboundscondition.cpp
new file mode 100644
index 0000000..13652f9
--- /dev/null
+++ b/game/code/mission/conditions/outofboundscondition.cpp
@@ -0,0 +1,157 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement OutOfBoundsCondition
+//
+// History: 24/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <events/eventmanager.h>
+
+#include <mission/conditions/outofboundscondition.h>
+
+#include <meta/eventlocator.h>
+
+#include <constants/maxplayers.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// OutOfBoundsCondition::OutOfBoundsCondition
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+OutOfBoundsCondition::OutOfBoundsCondition()
+{
+ this->SetType( COND_OUT_OF_BOUNDS );
+}
+
+//==============================================================================
+// OutOfBoundsCondition::~OutOfBoundsCondition
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+OutOfBoundsCondition::~OutOfBoundsCondition()
+{
+}
+
+//=============================================================================
+// OutOfBoundsCondition::OnInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void OutOfBoundsCondition::OnInitialize()
+{
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_LOCATOR + LocatorEvent::DEATH) );
+}
+
+//=============================================================================
+// OutOfBoundsCondition::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void OutOfBoundsCondition::OnFinalize()
+{
+ GetEventManager()->RemoveListener( this, (EventEnum)(EVENT_LOCATOR + LocatorEvent::DEATH) );
+}
+
+//=============================================================================
+// OutOfBoundsCondition::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void OutOfBoundsCondition::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case (EventEnum)(EVENT_LOCATOR + LocatorEvent::DEATH):
+ {
+ EventLocator* evtLoc = static_cast<EventLocator*>(pEventData);
+ if ( evtLoc->GetPlayerID() < static_cast<unsigned int>(MAX_PLAYERS) )
+ {
+ //This is a player
+ SetIsViolated( true );
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ MissionCondition::HandleEvent( id, pEventData );
+}
+
+//=============================================================================
+// OutOfBoundsCondition::IsClose
+//=============================================================================
+// Description: Indicates whether this condition is at all close to failure.
+// Very subjective, used for interactive music
+//
+// Parameters: None
+//
+// Return: True if close, false otherwise
+//
+//=============================================================================
+bool OutOfBoundsCondition::IsClose()
+{
+ //
+ // I don't really know what this does, let's say not close
+ //
+ return( false );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/mission/conditions/outofboundscondition.h b/game/code/mission/conditions/outofboundscondition.h
new file mode 100644
index 0000000..ceb9b04
--- /dev/null
+++ b/game/code/mission/conditions/outofboundscondition.h
@@ -0,0 +1,53 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: outofboundscondition.h
+//
+// Description: Blahblahblah
+//
+// History: 24/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef OUTOFBOUNDSCONDITION_H
+#define OUTOFBOUNDSCONDITION_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/conditions/missioncondition.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class OutOfBoundsCondition : public MissionCondition
+{
+public:
+ OutOfBoundsCondition();
+ virtual ~OutOfBoundsCondition();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ bool IsClose();
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+
+private:
+
+ //Prevent wasteful constructor creation.
+ OutOfBoundsCondition( const OutOfBoundsCondition& outofboundscondition );
+ OutOfBoundsCondition& operator=( const OutOfBoundsCondition& outofboundscondition );
+};
+
+
+#endif //OUTOFBOUNDSCONDITION_H
diff --git a/game/code/mission/conditions/positioncondition.cpp b/game/code/mission/conditions/positioncondition.cpp
new file mode 100644
index 0000000..4f54fb3
--- /dev/null
+++ b/game/code/mission/conditions/positioncondition.cpp
@@ -0,0 +1,130 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: PositionCondition.cpp
+//
+// Description: Implement PositionCondition
+//
+// History: 11/20/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/conditions/PositionCondition.h>
+
+#include <events/eventmanager.h>
+#include <events/eventenum.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// PositionCondition::PositionCondition
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PositionCondition::PositionCondition() :
+ mRequiredPosition( 1 ),
+ mNumOthersFinished( 0 )
+{
+ this->SetType( COND_POSITION );
+}
+
+//==============================================================================
+// PositionCondition::~PositionCondition
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PositionCondition::~PositionCondition()
+{
+}
+
+//=============================================================================
+// PositionCondition::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void PositionCondition::HandleEvent( EventEnum id, void* pEventData )
+{
+ if ( id == EVENT_WAYAI_AT_DESTINATION )
+ {
+ mNumOthersFinished++;
+
+ if ( mNumOthersFinished >= mRequiredPosition )
+ {
+ SetIsViolated( true );
+ }
+ }
+
+ MissionCondition::HandleEvent( id, pEventData );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// PositionCondition::OnInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PositionCondition::OnInitialize()
+{
+
+ mNumOthersFinished = 0;
+ GetEventManager()->AddListener( this, EVENT_WAYAI_AT_DESTINATION );
+}
+
+//=============================================================================
+// PositionCondition::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PositionCondition::OnFinalize()
+{
+ GetEventManager()->RemoveListener( this, EVENT_WAYAI_AT_DESTINATION );
+}
diff --git a/game/code/mission/conditions/positioncondition.h b/game/code/mission/conditions/positioncondition.h
new file mode 100644
index 0000000..dba0913
--- /dev/null
+++ b/game/code/mission/conditions/positioncondition.h
@@ -0,0 +1,73 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: positioncondition.h
+//
+// Description: Blahblahblah
+//
+// History: 11/20/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef POSITIONCONDITION_H
+#define POSITIONCONDITION_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/conditions/vehiclecondition.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class PositionCondition : public VehicleCondition
+{
+public:
+ PositionCondition();
+ virtual ~PositionCondition();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void SetRequiredPosition( int position );
+
+private:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+
+ int mRequiredPosition;
+ int mNumOthersFinished;
+
+ //Prevent wasteful constructor creation.
+ PositionCondition( const PositionCondition& positioncondition );
+ PositionCondition& operator=( const PositionCondition& positioncondition );
+};
+
+//******************************************************************************
+//
+// Inline Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// PositionCondition::SetRequiredPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int position )
+//
+// Return: void
+//
+//=============================================================================
+inline void PositionCondition::SetRequiredPosition( int position )
+{
+ mRequiredPosition = position;
+}
+
+#endif //POSITIONCONDITION_H
diff --git a/game/code/mission/conditions/racecondition.cpp b/game/code/mission/conditions/racecondition.cpp
new file mode 100644
index 0000000..85b3c3b
--- /dev/null
+++ b/game/code/mission/conditions/racecondition.cpp
@@ -0,0 +1,181 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement RaceCondition
+//
+// History: 24/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <events/eventmanager.h>
+
+#include <mission/conditions/racecondition.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// RaceCondition::RaceCondition
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RaceCondition::RaceCondition() :
+ m_playerWaypointsRemaining( 0 ),
+ m_aiWaypointsRemaining( 0 )
+{
+ this->SetType( COND_RACE );
+}
+
+//==============================================================================
+// RaceCondition::~RaceCondition
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RaceCondition::~RaceCondition()
+{
+}
+
+//=============================================================================
+// RaceCondition::OnInitalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RaceCondition::OnInitialize()
+{
+ GetEventManager()->AddListener( this, EVENT_WAYAI_AT_DESTINATION );
+ GetEventManager()->AddListener( this, EVENT_WAYAI_HIT_CHECKPOINT );
+ GetEventManager()->AddListener( this, EVENT_WAYAI_HIT_LAST_WAYPOINT );
+}
+
+//=============================================================================
+// RaceCondition::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RaceCondition::OnFinalize()
+{
+ GetEventManager()->RemoveListener( this, EVENT_WAYAI_AT_DESTINATION );
+ GetEventManager()->RemoveListener( this, EVENT_WAYAI_HIT_CHECKPOINT );
+ GetEventManager()->RemoveListener( this, EVENT_WAYAI_HIT_LAST_WAYPOINT );
+}
+
+//=============================================================================
+// RaceCondition::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void RaceCondition::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_WAYAI_HIT_LAST_WAYPOINT: // fall thru
+ case EVENT_WAYAI_AT_DESTINATION:
+ {
+ if( pEventData == (void*)GetVehicle() )
+ {
+ SetIsViolated( true );
+ }
+
+ m_aiWaypointsRemaining = 0;
+ break;
+ }
+ case EVENT_WAYAI_HIT_CHECKPOINT:
+ {
+ m_aiWaypointsRemaining = *(static_cast<int*>( pEventData ));
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ MissionCondition::HandleEvent( id, pEventData );
+}
+
+//=============================================================================
+// RaceCondition::IsChaseCondition
+//=============================================================================
+// Description: Indicate that this condition involves chasing
+//
+// Parameters: None
+//
+// Return: True
+//
+//=============================================================================
+bool RaceCondition::IsChaseCondition()
+{
+ return( true );
+}
+
+//=============================================================================
+// RaceCondition::IsClose
+//=============================================================================
+// Description: Indicates whether this condition is at all close to failure.
+// Very subjective, used for interactive music
+//
+// Parameters: None
+//
+// Return: True if close, false otherwise
+//
+//=============================================================================
+bool RaceCondition::IsClose()
+{
+ //
+ // TEMPORARY: this is certainly wrong, but let's use it until I have
+ // a chance to ask someone how to implement this correctly -- Esan
+ //
+ return( m_aiWaypointsRemaining <= 1 );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/mission/conditions/racecondition.h b/game/code/mission/conditions/racecondition.h
new file mode 100644
index 0000000..8839481
--- /dev/null
+++ b/game/code/mission/conditions/racecondition.h
@@ -0,0 +1,57 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: racecondition.h
+//
+// Description: Blahblahblah
+//
+// History: 24/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef RACECONDITION_H
+#define RACECONDITION_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/conditions/vehiclecondition.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class RaceCondition : public VehicleCondition
+{
+public:
+ RaceCondition();
+ virtual ~RaceCondition();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ bool IsChaseCondition();
+ bool IsClose();
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+
+private:
+
+ //Prevent wasteful constructor creation.
+ RaceCondition( const RaceCondition& racecondition );
+ RaceCondition& operator=( const RaceCondition& racecondition );
+
+ int m_playerWaypointsRemaining;
+ int m_aiWaypointsRemaining;
+};
+
+
+#endif //RACECONDITION_H
diff --git a/game/code/mission/conditions/timeoutcondition.cpp b/game/code/mission/conditions/timeoutcondition.cpp
new file mode 100644
index 0000000..facdf87
--- /dev/null
+++ b/game/code/mission/conditions/timeoutcondition.cpp
@@ -0,0 +1,153 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement TimeOutCondition
+//
+// History: 08/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <mission/gameplaymanager.h>
+#include <mission/mission.h>
+#include <mission/conditions/timeoutcondition.h>
+
+#include <worldsim/hitnrunmanager.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#include <events/eventmanager.h>
+
+#include <interiors/interiormanager.h>
+
+#include <worldsim/hitnrunmanager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// TimeOutCondition::TimeOutCondition
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TimeOutCondition::TimeOutCondition() :
+ //mHitNRun( false ),
+ mDone( false )
+{
+ this->SetType( COND_TIME_OUT );
+
+ //GetEventManager()->AddListener( this, EVENT_HIT_AND_RUN_CAUGHT );
+}
+
+//==============================================================================
+// TimeOutCondition::~TimeOutCondition
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TimeOutCondition::~TimeOutCondition()
+{
+ //GetEventManager()->RemoveListener( this, EVENT_HIT_AND_RUN_CAUGHT );
+}
+
+//=============================================================================
+// TimeOutCondition::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void TimeOutCondition::Update( unsigned int elapsedTime )
+{
+ if ( !mDone )
+ {
+ Mission* mission = GetGameplayManager()->GetCurrentMission();
+ int timeLeft = mission->GetMissionTimeLeftInSeconds();
+ if( timeLeft < 1 && !mission->IsComplete() &&
+ !GetInteriorManager()->IsEntering() &&
+ !GetInteriorManager()->IsExiting() &&
+ !GetHitnRunManager()->BustingPlayer() )
+ {
+ mDone = true;
+
+ //if ( mHitNRun )
+ //{
+ // //Not failed, start the action!
+ // GetHitnRunManager()->MaxHitnRunValue();
+ // //Disable run down of time.
+ // GetHitnRunManager()->DisableMeterDecay();
+
+ // //Pre-emptive.
+ // GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_TIMER );
+ //}
+ //else
+ {
+ SetIsViolated( true );
+ }
+ }
+ }
+}
+
+//=============================================================================
+// TimeOutCondition::IsClose
+//=============================================================================
+// Description: Indicates whether this condition is at all close to failure.
+// Very subjective, used for interactive music
+//
+// Parameters: None
+//
+// Return: True if close, false otherwise
+//
+//=============================================================================
+bool TimeOutCondition::IsClose()
+{
+ return( GetGameplayManager()->GetCurrentMission()->GetMissionTimeLeftInSeconds() < 20 );
+}
+
+
+//chuck hack hack to for gamble races.
+void TimeOutCondition::SetViolated(bool flag)
+{
+ SetIsViolated(flag);
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/mission/conditions/timeoutcondition.h b/game/code/mission/conditions/timeoutcondition.h
new file mode 100644
index 0000000..8c037f6
--- /dev/null
+++ b/game/code/mission/conditions/timeoutcondition.h
@@ -0,0 +1,55 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: timeoutcondition.h
+//
+// Description: Blahblahblah
+//
+// History: 08/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef TIMEOUTCONDITION_H
+#define TIMEOUTCONDITION_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/conditions/missioncondition.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class TimeOutCondition : public MissionCondition
+{
+public:
+ TimeOutCondition();
+ virtual ~TimeOutCondition();
+
+ virtual void Update( unsigned int elapsedTime );
+ void SetViolated(bool flag);
+
+ //void SetHitNRun() { mHitNRun = true; };
+
+ bool IsClose();
+
+private:
+
+ //bool mHitNRun;
+ bool mDone;
+
+ //Prevent wasteful constructor creation.
+ TimeOutCondition( const TimeOutCondition& timeoutcondition );
+ TimeOutCondition& operator=( const TimeOutCondition& timeoutcondition );
+};
+
+
+#endif //TIMEOUTCONDITION_H
diff --git a/game/code/mission/conditions/vehiclecarryingstateprop.cpp b/game/code/mission/conditions/vehiclecarryingstateprop.cpp
new file mode 100644
index 0000000..852eb7e
--- /dev/null
+++ b/game/code/mission/conditions/vehiclecarryingstateprop.cpp
@@ -0,0 +1,94 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component:
+//
+// Description:
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <mission\conditions\vehiclecarryingstateprop.h>
+#include <worldsim\redbrick\vehicle.h>
+#include <events/eventmanager.h>
+#include <mission/statepropcollectible.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+VehicleCarryingStateProp::VehicleCarryingStateProp()
+{
+ SetIsViolated( true );
+}
+
+VehicleCarryingStateProp::~VehicleCarryingStateProp()
+{
+
+}
+
+// the Mission Stage should call this every frame
+void VehicleCarryingStateProp::Update( unsigned int elapsedTime )
+{
+
+}
+
+void VehicleCarryingStateProp::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_VEHICLE_COLLECTED_PROP:
+ {
+ // A vehicle has picked up a collectible,
+ // check that the vehicle is the users car, and that
+ // the object that was collected is the same
+ // type that is associated with success
+ Vehicle* vehicle = reinterpret_cast< Vehicle* >(pEventData);
+ if ( vehicle->IsUserDrivingCar() )
+ {
+ StatePropCollectible* collectible = vehicle->GetAttachedCollectible();
+ if ( collectible != NULL )
+ {
+ if ( collectible->GetUID() == m_PropName )
+ {
+ SetIsViolated( false );
+ }
+ else
+ {
+ SetIsViolated( true );
+ }
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ MissionCondition::HandleEvent( id, pEventData );
+}
+
+void VehicleCarryingStateProp::OnInitialize()
+{
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_COLLECTED_PROP );
+}
+
+void VehicleCarryingStateProp::OnFinalize()
+{
+ GetEventManager()->RemoveListener( this, EVENT_VEHICLE_COLLECTED_PROP );
+}
diff --git a/game/code/mission/conditions/vehiclecarryingstateprop.h b/game/code/mission/conditions/vehiclecarryingstateprop.h
new file mode 100644
index 0000000..27eca37
--- /dev/null
+++ b/game/code/mission/conditions/vehiclecarryingstateprop.h
@@ -0,0 +1,78 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: vehiclecarryingstateprop
+//
+// Description: The vehicle must be carrying a statepropcollectible object
+// as a condition for mission success
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef VEHICLECARRYINGSTATEPROP_H
+#define VEHICLECARRYINGSTATEPROP_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <mission\conditions\missioncondition.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+//
+// Constraints:
+//
+//===========================================================================
+class VehicleCarryingStateProp : public MissionCondition
+{
+ public:
+ VehicleCarryingStateProp();
+ virtual ~VehicleCarryingStateProp();
+
+ virtual void Update( unsigned int elapsedTime );
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ // Sets the prop name that the vehicle must be carrying for condition success
+ void SetDesiredProp( const char* name )
+ {
+ m_PropName = tName::MakeUID( name );
+ }
+
+ protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+
+ tUID m_PropName;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow VehicleCarryingStateProp from being copied and assigned.
+ VehicleCarryingStateProp( const VehicleCarryingStateProp& );
+ VehicleCarryingStateProp& operator=( const VehicleCarryingStateProp& );
+
+};
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/mission/conditions/vehiclecondition.cpp b/game/code/mission/conditions/vehiclecondition.cpp
new file mode 100644
index 0000000..8193478
--- /dev/null
+++ b/game/code/mission/conditions/vehiclecondition.cpp
@@ -0,0 +1,70 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement VehicleCondition
+//
+// History: 08/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <mission/conditions/vehiclecondition.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// VehicleCondition::VehicleCondition
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+VehicleCondition::VehicleCondition() :
+ mpVehicle( NULL )
+{
+ //this->SetType( COND_PLAYER_OUT_OF_VEHICLE );
+}
+
+//==============================================================================
+// VehicleCondition::~VehicleCondition
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+VehicleCondition::~VehicleCondition()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/mission/conditions/vehiclecondition.h b/game/code/mission/conditions/vehiclecondition.h
new file mode 100644
index 0000000..89dc3cf
--- /dev/null
+++ b/game/code/mission/conditions/vehiclecondition.h
@@ -0,0 +1,85 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehiclecondition.h
+//
+// Description: Blahblahblah
+//
+// History: 08/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef VEHICLECONDITION_H
+#define VEHICLECONDITION_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/conditions/missioncondition.h>
+
+//========================================
+// Forward References
+//========================================
+
+class Vehicle;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class VehicleCondition : public MissionCondition
+{
+public:
+ VehicleCondition();
+ virtual ~VehicleCondition();
+
+ Vehicle* GetVehicle();
+ void SetVehicle( Vehicle* vehicle );
+
+protected:
+ virtual void OnInitialize() {};
+ virtual void OnFinalize() {};
+
+private:
+
+ //Prevent wasteful constructor creation.
+ VehicleCondition( const VehicleCondition& vehiclecondition );
+ VehicleCondition& operator=( const VehicleCondition& vehiclecondition );
+
+ Vehicle* mpVehicle;
+};
+
+//=============================================================================
+// VehicleCondition::SetVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* vehicle )
+//
+// Return: inline
+//
+//=============================================================================
+inline void VehicleCondition::SetVehicle( Vehicle* vehicle )
+{
+ mpVehicle = vehicle;
+}
+
+//=============================================================================
+// VehicleCondition::GetVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline Vehicle* VehicleCondition::GetVehicle()
+{
+ return mpVehicle;
+}
+
+#endif //VEHICLECONDITION_H
diff --git a/game/code/mission/gameplaymanager.cpp b/game/code/mission/gameplaymanager.cpp
new file mode 100644
index 0000000..e5eb569
--- /dev/null
+++ b/game/code/mission/gameplaymanager.cpp
@@ -0,0 +1,3360 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: gameplaymgr.cpp
+//
+// Description: Implement GameplayManager
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <contexts/context.h>
+#include <gameflow/gameflow.h>
+
+#include <loading/filehandlerenum.h>
+
+#include <memory/srrmemory.h>
+
+#include <meta/carstartlocator.h>
+#include <meta/locator.h>
+#include <meta/triggerlocator.h>
+#include <meta/triggervolume.h>
+#include <meta/triggervolumetracker.h>
+#include <meta/eventlocator.h>
+#include <meta/locatorevents.h>
+#include <meta/spheretriggervolume.h>
+
+#include <mission/gameplaymanager.h>
+#include <mission/missionmanager.h>
+#include <mission/mission.h>
+#include <mission/missionscriptloader.h>
+#include <mission/animatedicon.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <presentation/presentation.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiwindow.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guiscreeniriswipe.h>
+#include <presentation/gui/ingame/guiscreenmissionload.h>
+#include <presentation/gui/ingame/hudevents/hudeventhandler.h>
+
+#include <render/rendermanager/rendermanager.h>
+
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/parkedcars/parkedcarmanager.h>
+
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+
+#include <events/eventmanager.h>
+#include <events/eventlistener.h>
+#include <events/eventenum.h>
+
+#include <sound/soundmanager.h>
+
+#include <render/culling/worldscene.h>
+
+#include <main/commandlineoptions.h>
+#include <worldsim/harass/chasemanager.h>
+
+#include <ai/actionbuttonhandler.h>
+#include <ai/actionbuttonmanager.h>
+
+#include <camera/supercammanager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+GameplayManager* GameplayManager::spInstance = NULL;
+
+//THESE ARE THE CONTINUITY ISSUES RELATING TO CARS
+struct ContinuityError
+{
+ RenderEnums::LevelEnum mLevel;
+ RenderEnums::MissionEnum mMission;
+ const char* mPlayerCarName;
+ const char* mOtherCarName;
+};
+
+const ContinuityError CONTINUITY_ERRORS[] =
+{
+ { RenderEnums::L2, RenderEnums::M5, "cletu_v", "cletu_v" },
+ { RenderEnums::L4, RenderEnums::M3, "cletu_v", "cletu_v" },
+ { RenderEnums::L6, RenderEnums::M1, "otto_v", "otto_v" },
+ { RenderEnums::L7, RenderEnums::M3, "frink_v", "frink_v" },
+ { RenderEnums::L7, RenderEnums::M6, "snake_v", "snake_v" },
+ { RenderEnums::L7, RenderEnums::M7, "gramp_v", "gramR_v" }
+};
+
+unsigned int NUM_CONTINUITY_ERRORS = 6;
+
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// GameplayManager::GetInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: GameplayManager
+//
+//=============================================================================
+GameplayManager* GameplayManager::GetInstance()
+{
+// rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+//=============================================================================
+// GameplayManager::SetInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( GameplayManager* pInstance)
+//
+// Return: GameplayManager
+//
+//=============================================================================
+void GameplayManager::SetInstance( GameplayManager* pInstance )
+{
+// rAssert( pInstance != NULL );
+
+ spInstance = pInstance;
+}
+
+//==============================================================================
+// GameplayManager::GameplayManager
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GameplayManager::GameplayManager() :
+ mIsDemo( false ),
+ //mVehicleIndex( -1 ),
+ mCharacterIndex( -1 ),
+ mShouldLoadDefaultVehicle(false),
+ mSkipSunday( 0 ),
+ mGameType( GT_NORMAL ),
+ mIrisClosed( false ),
+ mFadedToBlack( false ),
+ mWaitingOnFMV( false ),
+ mCurrentMission( -1 ),
+ mNumPlayers( 0 ),
+ mNumMissions( 0 ),
+#ifdef RAD_GAMECUBE
+ mCurrentMissionHeap( GMA_GC_VMM ),
+#else
+ mCurrentMissionHeap( GMA_LEVEL_MISSION ),
+#endif
+ mLevelComplete( false ),
+ mGameComplete( false ),
+ mCurrentVehicle( NULL ),
+ //miNumLevelVehicles( 0 ),
+ mNumBonusMissions( 0 ),
+ mCurrentBonusMission( -1 ),
+ mDesiredBonusMission( -1 ),
+ mIsInBonusMission( false ),
+ mFireBonusMissionDialogue( false ),
+ mJumpToBonusMission( false ),
+ mUpdateBonusMissions( true ),
+ mCurrentMessage( NONE ),
+ mpRespawnManager( NULL ),
+ mIrisSpeed( 1.0f ),
+ mPutPlayerInCar( false ),
+ mbManualControlFade( false ),
+ mCurrentVehicleIconID( -1 ),
+ m_elapsedIdleTime( 0 )
+{
+ mEnablePhoneBooths = true;
+ int i;
+ //for( i = 0; i < MAX_LEVEL_VEHICLES; i++ )
+ //{
+ // mLevelVehicles[ i ] = NULL;
+ //}
+ //strcpy(mDefaultVehicle,"NULL");
+
+
+ // new
+ // greg
+ // jan 7, 2003
+
+ for(i = 0; i < MAX_MISSION_VEHICLE_SLOTS; i++)
+ {
+ mMissionVehicleSlots[i].vehicle = NULL;
+ mMissionVehicleSlots[i].name[0] = 0;
+ //mMissionVehicleSlots[i].vehicleCentralIndex = -1;
+
+ mMissionVehicleSlots[i].pHuskVehicle = 0;
+ mMissionVehicleSlots[i].usingHusk = false;
+
+ }
+
+ for(i=0; i<MAX_MISSIONS + MAX_BONUS_MISSIONS; i++ )
+ {
+ mMissions[i] = NULL;
+ }
+
+ if ( CommandLineOptions::Get( CLO_SKIP_SUNDAY ) )
+ {
+ mSkipSunday = 1;
+ }
+
+ //mLevelData.mission = RenderEnums::M1;
+
+ for (i =0;i< MAX_VEHICLE_SLOTS;i++)
+ {
+ strcpy(mVehicleSlots[i].filename,"NULL");
+ strcpy(mVehicleSlots[i].name,"NULL");
+ mVehicleSlots[i].mp_vehicle=NULL;
+ mVehicleSlots[i].heading =0;
+ mVehicleSlots[i].position.x=0;
+ mVehicleSlots[i].position.y=0;
+ mVehicleSlots[i].position.z=0;
+
+ mVehicleSlots[i].pHuskVehicle = 0;
+ mVehicleSlots[i].usingHusk = false;
+
+ }
+
+ for (i=0;i<MAX_CHASEMANAGERS;i++)
+ {
+ strcpy(m_ChaseManager_Array[i].hostvehicle,"NULL");
+ m_ChaseManager_Array[i].mp_chasemanager = NULL;
+ strcpy(m_ChaseManager_Array[i].hostvehiclefilename,"NULL");
+ }
+
+ for(int index =0;index<MAX_VDU_CARS;index++)
+ {
+ mVDU.mpVehicle[index]=NULL;
+ }
+ mVDU.mCounter=0;
+ mBlackScreenTimer = 2000; //Chuck set this to 1000 milliseconds
+ mPlayerAndCarInfo.mbDirtyFlag =false;
+
+ mPostLevelFMV[ 0 ] = 0;
+}
+
+//==============================================================================
+// GameplayManager::~GameplayManager
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GameplayManager::~GameplayManager()
+{
+}
+
+//=============================================================================
+// GameplayManager::LevelLoaded
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::LevelLoaded()
+{
+ //GetGameFlow()->SetContext( CONTEXT_GAMEPLAY );
+
+ //Now we should init level...
+ mPostLevelFMV[ 0 ] = 0;
+ mWaitingOnFMV = false;
+// GetGameFlow()->SetQuickStartLoading( false );
+
+ InitLevelData();
+
+ SetCurrentMission( mLevelData.mission * 2 + mSkipSunday );
+}
+
+//=============================================================================
+// GameplayManager::SetLevelIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( RenderEnums::LevelEnum level )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::SetLevelIndex( RenderEnums::LevelEnum level )
+{
+ //
+ // Insert level hack here:
+ //
+#ifdef RAD_DEBUG
+ //level = RenderEnums::L5;
+#endif
+
+ mLevelData.level = level;
+
+ GetRenderManager()->SetLoadData( RenderEnums::LevelSlot,
+ level,
+ RenderEnums::M1 );
+}
+
+//=============================================================================
+// GameplayManager::SetMissionIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( RenderEnums::MissionEnum mission )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::SetMissionIndex( RenderEnums::MissionEnum mission )
+{
+ mLevelData.mission = mission;
+}
+
+
+bool GameplayManager::TestPosInFrustrumOfPlayer( const rmt::Vector& pos, int playerID, float radius )
+{
+ rAssert( 0 <= playerID && playerID < GetNumPlayers() );
+
+ tPointCamera* pCam = (tPointCamera*)GetSuperCamManager()->GetSCC(playerID)->GetCamera();
+ float oldFar = pCam->GetFarPlane();
+ // Camera's quite far away in SuperSprint, so we'll omit the farplane hack
+ if( ::GetGameplayManager()->GetGameType() != GameplayManager::GT_SUPERSPRINT )
+ {
+ pCam->SetFarPlane(250.0f);
+ }
+ bool r = pCam->SphereVisible(pos, radius);
+ pCam->SetFarPlane(oldFar);
+ return r;
+}
+
+//=============================================================================
+// GameplayManager::ContinueGameplay
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::ContinueGameplay()
+{
+ if( GetLevelComplete() )
+ {
+ return;
+ }
+
+ Mission* currentMission = GetCurrentMission();
+
+ if( currentMission == NULL )
+ {
+ SetCurrentMission( 0 );
+ }
+ else
+ {
+ switch( currentMission->GetState() )
+ {
+ case Mission::STATE_FAILED:
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_RESUME_INGAME );
+
+ // follow-through
+ }
+ case Mission::STATE_WAITING:
+ {
+ //Dirty dirty...
+ currentMission->Reset();
+ currentMission->GetCurrentStage()->Start();
+ if ( currentMission->GetCurrentStage()->StartBonusObjective() )
+ {
+ //Ugly. Drink another for Darryl.
+ currentMission->StartBonusObjectives();
+ }
+ break;
+ }
+
+ case Mission::STATE_INPROGRESS:
+ {
+ currentMission->Reset();
+ break;
+ }
+ case Mission::STATE_SUCCESS:
+ {
+ //
+ // Stupid logic because sometimes the "final" stage
+ // isn't the last stage in the mission
+ //
+ if( currentMission->IsComplete() )
+ {
+ if ( mIsInBonusMission )
+ {
+ //Mark this bonus mission complete.
+ mBonusMissions[ mCurrentBonusMission - MAX_MISSIONS ].SetCompleted( true );
+ }
+#ifdef RAD_E3
+ if ( currentMission->IsSundayDrive() )
+ {
+ NextMission();
+ }
+ else
+ {
+ PrevMission(); //Go back and forth only for E3.
+ }
+#else
+ NextMission();
+#endif
+ }
+ else
+ {
+ currentMission->NextStage();
+
+ GetGuiSystem()->HandleMessage( GUI_MSG_RESUME_INGAME );
+ }
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ break;
+ }
+ }
+ }
+}
+
+//=============================================================================
+// MissionManager::Update
+//=============================================================================
+// Description:
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::Update( int elapsedTime )
+{
+ if ( mCurrentMessage != NONE )
+ {
+ switch ( mCurrentMessage )
+ {
+ case NEXT_MISSION:
+ {
+
+ DoNextMission();
+ break;
+ }
+ case PREV_MISSION:
+ {
+ DoPrevMission();
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ }
+ }
+
+ mCurrentMessage = NONE;
+ }
+
+ bool isWager = false;
+
+ if ( mFireBonusMissionDialogue )
+ {
+ isWager = GetMission( mDesiredBonusMission )->IsWagerMission();
+
+ if ( !isWager )
+ {
+ //HEY, we wanna do a bonus mission.. Have to do it here since
+ //we can't nest events that effect the Character.
+ mBonusMissions[ mDesiredBonusMission - MAX_MISSIONS ].TriggerDialogue();
+
+ //This is probably not the best thing to do.
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Suspend();
+ }
+
+ mFireBonusMissionDialogue = false;
+ }
+
+ if ( mJumpToBonusMission || isWager )
+ {
+ //
+ // if it's a bonus mission, then wipe out the bitmap on the mission briefing screen
+ //
+
+ CGuiScreenMissionLoad::ClearBitmap();
+
+ if( isWager )
+ {
+ GetPresentationManager()->ReplaceMissionBriefingBitmap( "b_louie" );
+ }
+/*
+ else
+ {
+ //
+ // Clear the loaded mission briefing picture
+ //
+ CGuiScreenMissionBase::ClearBitmap();
+ }
+*/
+
+ //Start the mission itself!
+ SetCurrentMission( mDesiredBonusMission );
+ mDesiredBonusMission = -1;
+ mJumpToBonusMission = false;
+ }
+
+ //convert in seconds for ChaseManager's update
+ float sectime = elapsedTime/1000.00f;
+ //update any chase managers if we have any
+ for (int i=0;i<MAX_CHASEMANAGERS;i++)
+ {
+ if (m_ChaseManager_Array[i].mp_chasemanager != NULL)
+ {
+ m_ChaseManager_Array[i].mp_chasemanager->Update(sectime);
+ }
+ }
+
+
+ Mission* currentMission = GetCurrentMission();
+
+ if( currentMission == NULL || GetLevelComplete() )
+ {
+ return;
+ }
+
+ if( currentMission->IsComplete() )
+ {
+ switch( currentMission->GetState() )
+ {
+ case Mission::STATE_SUCCESS:
+ {
+#ifdef RAD_E3
+ if ( currentMission->IsSundayDrive() )
+ {
+ NextMission();
+ }
+ else
+ {
+ PrevMission(); //Go back and forth only for E3.
+ }
+#else
+ NextMission();
+#endif
+ break;
+ }
+ case Mission::STATE_FAILED:
+ {
+ ContinueGameplay();
+ break;
+ }
+ case Mission::STATE_INPROGRESS:
+ {
+ currentMission->Update( elapsedTime );
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ }
+ }
+ }
+ else
+ {
+ currentMission->Update( elapsedTime );
+ }
+
+ if ( mUpdateBonusMissions )
+ {
+ int bonusMission;
+ for ( bonusMission = 0; bonusMission < mNumBonusMissions; ++bonusMission )
+ {
+ mBonusMissions[ bonusMission ].Update( elapsedTime );
+ }
+ }
+ //chuck update the VDU struct and try to delete the cars
+ UpdateVDU();
+
+ //Chuck check if the screen is black if it is then update the blackscreentimer
+
+ if (mFadedToBlack == true)
+ {
+ mBlackScreenTimer -= elapsedTime;
+
+ //check if time less than 0 then transition out of black screen to
+ if (mBlackScreenTimer < 0)
+ {
+ PauseForFadeFromBlack();
+ }
+ }
+
+
+ }
+
+//=============================================================================
+// GameplayManager::PlaceCharacterAtLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* character, CarStartLocator* locator )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::PlaceCharacterAtLocator( Character* character,
+ Locator* locator )
+{
+ rAssert( character != NULL );
+
+ rAssert( locator != NULL );
+
+ rmt::Vector pos;
+ locator->GetLocation( &pos );
+ float rotation = 0.0f;
+
+ CarStartLocator* csl = dynamic_cast<CarStartLocator*>(locator);
+ if(csl)
+ {
+ rotation = csl->GetRotation();
+ }
+
+ character->RelocateAndReset( pos, rotation );
+}
+
+//=============================================================================
+// GameplayManager::PlaceVehicleAtLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* vehicle, const char* locatorName )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::PlaceVehicleAtLocator( Vehicle* vehicle,
+ CarStartLocator* locator )
+{
+ rAssert( vehicle != NULL );
+
+ rAssert( locator != NULL );
+
+ rmt::Vector pos;
+
+ locator->GetLocation( &pos );
+
+ // fucking butt-ugly hack, 4 days after scheduled final, to deal with setting an AI car ontop of user's car
+
+ // 1 day later...
+ // you fucking retard, only do this if you are not the users car...
+ if(GetAvatarManager()->GetAvatarForPlayer(0))
+ {
+ Vehicle* playersCar = GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle();
+
+ if(playersCar == NULL)
+ {
+ playersCar = this->GetCurrentVehicle();
+ }
+
+ if(playersCar && playersCar != vehicle)
+ {
+ if(TestProximityToUsersCarAndNudgeUpIfNecessaryDamnUglyHack(pos, playersCar))
+ {
+ pos.y += 2.0f;
+ }
+ }
+
+ }
+
+ vehicle->SetInitialPositionGroundOffsetAutoAdjust( &pos );
+ float rotation = locator->GetRotation();
+ vehicle->SetResetFacingInRadians( rotation );
+ vehicle->Reset( false );
+
+ if( vehicle->IsVehicleDestroyed() )
+ {
+ Vehicle* husk = GetVehicleCentral()->mHuskPool.FindHuskGivenOriginalVehicle( vehicle );
+ //chuck testing if the vehicle generates a husk or not.
+ if (husk != NULL)
+ {
+ husk->SetInitialPositionGroundOffsetAutoAdjust( &pos );
+ husk->SetResetFacingInRadians( rotation );
+ husk->Reset();
+ }
+ }
+}
+
+
+//=============================================================================
+// GameplayManager::TestProximityToUsersCarAndNudgeUpIfNecessaryDamnUglyHack
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool GameplayManager::TestProximityToUsersCarAndNudgeUpIfNecessaryDamnUglyHack(rmt::Vector& pos, Vehicle* playersCar)
+{
+ if(playersCar)
+ {
+ rmt::Vector playersCarPosition;
+ playersCar->GetPosition(&playersCarPosition);
+
+ playersCarPosition.Sub(pos);
+ float dist = playersCarPosition.Magnitude();
+ if(dist < 3.0f)
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//=============================================================================
+// GameplayManager::PlaceVehicleAtLocation
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* vehicle, rmt::Vector pos, float rotation )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::PlaceVehicleAtLocation( Vehicle* vehicle, rmt::Vector pos, float rotation )
+{
+ rAssert( vehicle != NULL );
+ vehicle->SetInitialPositionGroundOffsetAutoAdjust( &pos );
+ vehicle->SetResetFacingInRadians( rotation );
+ vehicle->Reset();
+}
+
+//=============================================================================
+// GameplayManager::PlaceVehicleAtLocatorName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* vehicle, const char* locatorName )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::PlaceVehicleAtLocatorName( Vehicle* vehicle,
+ const char* locatorName )
+{
+ CarStartLocator* loc = p3d::find<CarStartLocator>( locatorName );
+ #ifndef FINAL
+ char outputbuffer [255];
+ if (loc == NULL)
+ {
+ sprintf(outputbuffer,"Error:Locator %s cannot be found, Make sure its loaded!\n",locatorName);
+ rTuneAssertMsg(0,outputbuffer);
+ }
+ #endif
+ PlaceVehicleAtLocator( vehicle, loc );
+}
+
+//=============================================================================
+// GameplayManager::InitVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char* vehicleName )
+//
+// Return: Vehicle
+//
+//=============================================================================
+/*
+Vehicle* GameplayManager::InitVehicle( char* vehicleName )
+{
+ // just to make sure
+ rAssert(0);
+ return GetVehicleCentral()->InitVehicle( vehicleName, true );
+}
+*/
+
+//=============================================================================
+// GameplayManager::AddLevelVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char* vehicleName )
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* GameplayManager::AddLevelVehicle( char* vehicleName,eCarSlots slot, char* confile )
+{
+
+ Vehicle* v = NULL;
+
+ if ( mIsDemo )
+ {
+ v = GetVehicleCentral()->InitVehicle( vehicleName, true, confile, VT_AI );
+
+ mAIIndex = GetVehicleCentral()->GetVehicleId( v );
+ rAssert( mAIIndex != -1 );
+ WaypointAI* wai = new WaypointAI( v );
+ GetVehicleCentral()->SetVehicleController( mAIIndex, wai );
+ }
+ else
+ {
+ //If this car is being placed in the Default slot it is owned by the Player, so we use init the car with mbPlayerCar true.
+ if(slot == GameplayManager::eDefaultCar)
+ {
+ // default car gets driver, if it isn't supressed
+ v = GetVehicleCentral()->InitVehicle( vehicleName, true, confile, VT_USER, VehicleCentral::ALLOW_DRIVER,true );
+ }
+ else
+ {
+
+ // ai and other slot always have driver, even if supressed
+ v = GetVehicleCentral()->InitVehicle( vehicleName, true, confile, VT_USER, VehicleCentral::FORCE_DRIVER);
+ }
+
+ }
+
+
+
+#ifndef FINAL
+
+ if(strcmp(mVehicleSlots[slot].name,vehicleName) != 0)
+ {
+ rReleasePrintf("Attempting to add vehicle, name mismatch!! \n");
+ rAssert (0);
+ }
+#endif
+
+ mVehicleSlots[slot].mp_vehicle = v;
+
+
+ v->AddRef();
+
+
+ return v;
+}
+
+void GameplayManager::RemoveLevelVehicleController()
+{
+ //
+ //Judging from the symmetry of the code, this appears to be a hack that I'm willfully adding.
+ //When/if you figure out wtf Greg seems to resolute in his refusal to addref or release,
+ //feel free to remove the code.
+ //
+ // This is a Triage Hack for demo-mode stability --dm 12/01/02
+ //
+ if ( mIsDemo )
+ {
+ VehicleController* v = GetVehicleCentral()->RemoveVehicleController( mAIIndex );
+ if(v)
+ {
+ v->ReleaseVerified();
+ }
+ }
+ }
+
+//=============================================================================
+// GameplayManager::PauseForIrisClose
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float speedMod )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::PauseForIrisClose( float speedMod )
+{
+ mIrisSpeed = speedMod;
+
+ GetEventManager()->AddListener( this, EVENT_GUI_IRIS_WIPE_CLOSED );
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Suspend();
+ GetGuiSystem()->HandleMessage( GUI_MSG_START_IRIS_WIPE_CLOSE );
+
+ CGuiScreenIrisWipe* iw = static_cast<CGuiScreenIrisWipe*>(GetGuiSystem()->GetInGameManager()->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_IRIS_WIPE ));
+ iw->SetRelativeSpeed( mIrisSpeed );
+}
+
+//=============================================================================
+// GameplayManager::PauseForIrisOpen
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float speedMod )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::PauseForIrisOpen( float speedMod )
+{
+ GetEventManager()->AddListener( this, EVENT_GUI_IRIS_WIPE_OPEN );
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Suspend();
+ GetGuiSystem()->HandleMessage( GUI_MSG_START_IRIS_WIPE_OPEN );
+
+ float speed = mIrisSpeed;
+
+ if ( speedMod != 0.0f )
+ {
+ speed = speedMod;
+ }
+
+ CGuiScreenIrisWipe* iw = static_cast<CGuiScreenIrisWipe*>(GetGuiSystem()->GetInGameManager()->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_IRIS_WIPE ));
+ iw->SetRelativeSpeed( speed );
+
+ //Reset
+ mIrisSpeed = 1.0f;
+}
+
+void GameplayManager::PauseForFadeToBlack( float speedMod )
+{
+ mIrisSpeed = speedMod;
+
+ GetEventManager()->AddListener( this, EVENT_GUI_FADE_OUT_DONE );
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Suspend();
+ GetGuiSystem()->HandleMessage( GUI_MSG_INGAME_FADE_OUT );
+}
+
+void GameplayManager::PauseForFadeFromBlack( float speedMod )
+{
+ //check for manual control if some system has locked the screen to say black
+ //return and do nothing, and let the mBlackScreenTimer do its job in The ::Update().
+
+ if (mbManualControlFade == true || mBlackScreenTimer > 0)
+ {
+ return;
+ }
+ GetEventManager()->AddListener( this, EVENT_GUI_FADE_IN_DONE );
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Suspend();
+ GetGuiSystem()->HandleMessage( GUI_MSG_INGAME_FADE_IN );
+
+ //Reset
+ mIrisSpeed = 1.0f;
+}
+
+
+//=============================================================================
+// GameplayManager::AddMissionVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (char* vehiclename, char* confile)
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* GameplayManager::AddMissionVehicle(char* vehiclename, char* confile, char* driver)
+{
+ // check if the vehicle is already in our list
+
+ Vehicle* vehicleSavedFromExecution = NULL;
+ this->ReleaseFromVDU(vehiclename, &vehicleSavedFromExecution);
+ if(vehicleSavedFromExecution)
+ {
+ //GetVehicleCentral()->RemoveVehicleFromActiveList(vehicleSavedFromExecution);
+ //chuck this was added to fixed bug 11114 but now cause popping at the end of l4m2
+ //trying a different method
+ AddToVDU(vehicleSavedFromExecution);
+
+ }
+ // check this down below
+
+ int i;
+ for(i = 0; i < MAX_MISSION_VEHICLE_SLOTS; i++)
+ {
+ if(strcmp(vehiclename, mMissionVehicleSlots[i].name) == 0)
+ {
+ // this vehicle is already in our list
+ rTuneAssertMsg(vehicleSavedFromExecution == 0, "you are trying to use two of the same car in a mission");
+
+ tRefCounted::Release(vehicleSavedFromExecution);
+ return mMissionVehicleSlots[i].vehicle;
+ }
+ }
+
+ // extra test -
+ // you should never be calling AddMissionVehicle on a car that is in the OTHER slot
+ rTuneAssertMsg( (strcmp(GetGameplayManager()->GetVehicleSlotVehicleName(GameplayManager::eOtherCar),vehiclename) != 0), "you can't add the OTHER car to a mission");
+
+
+ for(i = 0; i < MAX_MISSION_VEHICLE_SLOTS; i++)
+ {
+ if(mMissionVehicleSlots[i].vehicle == 0)
+ {
+ // here is an empty slot
+
+ if(vehicleSavedFromExecution != 0)
+ {
+ mMissionVehicleSlots[i].vehicle = vehicleSavedFromExecution;
+ mMissionVehicleSlots[i].vehicle->ResetFlagsOnly(true);
+ mMissionVehicleSlots[i].vehicle->SetDriverName(driver);
+ mMissionVehicleSlots[i].vehicle->mDriverInit = VehicleCentral::FORCE_DRIVER;
+ GetVehicleCentral()->SetupDriver(mMissionVehicleSlots[i].vehicle);
+ }
+ else
+ {
+ //mMissionVehicleSlots[i].vehicle = GetVehicleCentral()->InitVehicle(vehiclename, false, confile, VT_AI); // allow driver I guess?
+
+ mMissionVehicleSlots[i].vehicle = GetVehicleCentral()->InitVehicle( vehiclename,
+ false, // add to active list
+ confile,
+ VT_AI, // VehicleType
+ VehicleCentral::ALLOW_DRIVER,
+ false, // player car
+ //false); // start out of car
+ true);
+
+
+
+
+ if(driver && (driver[0] != 0))
+ {
+ mMissionVehicleSlots[i].vehicle->mDriverInit = VehicleCentral::FORCE_DRIVER;
+ mMissionVehicleSlots[i].vehicle->SetDriverName(driver);
+ GetVehicleCentral()->SetupDriver(mMissionVehicleSlots[i].vehicle);
+ }
+ }
+
+ strcpy(mMissionVehicleSlots[i].name, vehiclename);
+
+ mMissionVehicleSlots[i].vehicle->AddRef();
+
+ GetEventManager()->TriggerEvent(EVENT_MISSION_VEHICLE_CREATED,mMissionVehicleSlots[i].vehicle);
+
+ //update the AI vehicle slot so It points to the correct vehicle.
+ if(strcmp(GetGameplayManager()->GetVehicleSlotVehicleName(GameplayManager::eAICar),"NULL") != 0)
+ {
+ if(strcmp(mMissionVehicleSlots[i].vehicle->GetName(),GetGameplayManager()->GetVehicleSlotVehicleName(GameplayManager::eAICar)) ==0)
+ {
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eAICar].mp_vehicle = mMissionVehicleSlots[i].vehicle;
+ }
+ }
+
+
+
+ //int dummy = GetVehicleCentral()->AddVehicleToActiveList(mMissionVehicleSlots[i].vehicle);
+ //if(dummy == -1)
+ //{
+ // rAssertMsg(0, "can't add vehicle to active list - see greg \n");
+ //}
+ //else
+ //{
+ // //mMissionVehicleSlots[i].vehicleCentralIndex = dummy;
+ //}
+
+ tRefCounted::Release(vehicleSavedFromExecution);
+ return mMissionVehicleSlots[i].vehicle;
+ }
+ }
+
+ tRefCounted::Release(vehicleSavedFromExecution);
+ return 0; // bad
+}
+
+
+//=============================================================================
+// GameplayManager::EmptyMissionVehicleSlots
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::EmptyMissionVehicleSlots()
+{
+ int i;
+ for(i = 0; i < MAX_MISSION_VEHICLE_SLOTS; i++)
+ {
+ if(mMissionVehicleSlots[i].vehicle != NULL)
+ {
+ if(mMissionVehicleSlots[i].usingHusk)
+ {
+ spInstance->AddToVDU(mMissionVehicleSlots[i].pHuskVehicle);
+ //GetVehicleCentral()->mHuskPool.FreeHusk(mMissionVehicleSlots[i].pHuskVehicle);
+ mMissionVehicleSlots[i].pHuskVehicle->Release();
+ mMissionVehicleSlots[i].pHuskVehicle = NULL;
+ mMissionVehicleSlots[i].usingHusk = false;
+ }
+ else
+ {
+ spInstance->AddToVDU(mMissionVehicleSlots[i].vehicle);
+ }
+
+ GetEventManager()->TriggerEvent(EVENT_MISSION_VEHICLE_RELEASED,mMissionVehicleSlots[i].vehicle);
+
+ //update the AI vehicle slot so It points to the correct vehicle.
+ if(strcmp(GetGameplayManager()->GetVehicleSlotVehicleName(GameplayManager::eAICar),"NULL") != 0 && GetGameplayManager()->mVehicleSlots[GameplayManager::eAICar].mp_vehicle != NULL )
+ {
+ //update the Gameplaymanagers AICar slot mp_vehicle ptr.
+ if(strcmp(mMissionVehicleSlots[i].vehicle->GetName(),GetGameplayManager()->GetVehicleSlotVehicleName(GameplayManager::eAICar)) ==0)
+ {
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eAICar].mp_vehicle = NULL;
+ }
+ }
+
+ mMissionVehicleSlots[i].vehicle->Release();
+ mMissionVehicleSlots[i].vehicle = 0;
+ mMissionVehicleSlots[i].name[0] = 0;
+
+ }
+ }
+}
+
+
+//=============================================================================
+// GameplayManager::GetMissionVehicleByName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (char* name)
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* GameplayManager::GetMissionVehicleByName( const char* name)
+{
+ int i;
+ for(i = 0; i < MAX_MISSION_VEHICLE_SLOTS; i++)
+ {
+ if(strcmp(mMissionVehicleSlots[i].name, name) == 0)
+ {
+ return mMissionVehicleSlots[i].vehicle;
+ }
+ }
+ return 0;
+
+}
+
+//=============================================================================
+// GameplayManager::GetUserVehicleByName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (char* name)
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* GameplayManager::GetUserVehicleByName(const char* name)
+{
+ if(mVehicleSlots[eDefaultCar].mp_vehicle &&
+ strcmp(mVehicleSlots[eDefaultCar].name, name) == 0)
+ {
+ return mVehicleSlots[eDefaultCar].mp_vehicle;
+ }
+
+ if(mVehicleSlots[eOtherCar].mp_vehicle &&
+ strcmp(mVehicleSlots[eOtherCar].name, name) == 0)
+ {
+ return mVehicleSlots[eOtherCar].mp_vehicle;
+ }
+
+ if(strcmp("current",name) == 0)
+ {
+ return GetCurrentVehicle();
+ }
+
+ return 0;
+
+ // recall, mp_vehicle pointer in eAICar is never filled or used.
+}
+
+
+//=============================================================================
+// GameplayManager::GetMissionVehicleIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (Vehicle* vehicle)
+//
+// Return: int
+//
+//=============================================================================
+int GameplayManager::GetMissionVehicleIndex(Vehicle* vehicle)
+{
+ int i;
+ for(i = 0; i < MAX_MISSION_VEHICLE_SLOTS; i++)
+ {
+ if(mMissionVehicleSlots[i].vehicle == vehicle)
+ {
+ return GetVehicleCentral()->GetVehicleId(mMissionVehicleSlots[i].vehicle, false);
+ }
+ }
+ return -1;
+}
+
+
+
+
+void GameplayManager::RepairVehicle( CarDataStruct* carData )
+{
+ // check that the car is one of ours
+ rAssert( carData );
+ rAssert( carData == &(mVehicleSlots[ eDefaultCar ]) ||
+ carData == &(mVehicleSlots[ eOtherCar ]) );
+ rAssert( GetGameplayManager()->GetCurrentVehicle() == carData->mp_vehicle );
+
+ //
+ // if this car is fully destroyed, we need to
+ // - put any NPC driver back into the driver seat
+ //
+ // if it was also using a husk, we need to
+ // - swap out the husk
+ // - add back original vehicle (only do this if we have husk,
+ // cuz GameplayManager never removes original vehicle if there
+ // wasn't husk for it).
+ //
+ if( carData->mp_vehicle->mVehicleDestroyed )
+ {
+ if( carData->usingHusk )
+ {
+ // Want to swap out the husk and put the original vehicle
+ // back in its place... so...
+ //
+ // First we need to populate a matrix with husk's position and facing
+ // and set the original vehicle's location to these position and facing
+ //
+ rmt::Vector carPosition = carData->pHuskVehicle->rPosition();
+ float ang = carData->pHuskVehicle->GetFacingInRadians();
+ rmt::Matrix m;
+ m.Identity();
+ m.FillRotateXYZ( 0.0f, ang, 0.0f );
+ m.FillTranslate( carPosition );
+ carData->mp_vehicle->SetTransform( m );
+
+ // Remove the husk
+ GetVehicleCentral()->RemoveVehicleFromActiveList( carData->pHuskVehicle );
+ GetVehicleCentral()->mHuskPool.FreeHusk( carData->pHuskVehicle );
+ carData->pHuskVehicle->Release();
+ carData->pHuskVehicle = NULL;
+ carData->usingHusk = false;
+
+ // Add back the original vehicle
+ GetVehicleCentral()->AddVehicleToActiveList( carData->mp_vehicle );
+
+ // if the avatar is inside a vehicle the vehicle
+ // is probably a husk, update this vehicle to be the original
+ // vehicle and place the character in this new vehicle
+ //
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ if( avatar->IsInCar() )
+ {
+ rAssert( avatar->GetVehicle() );
+ rAssert( GetVehicleCentral()->mHuskPool.IsHuskType( avatar->GetVehicle()->mVehicleID ) );
+
+ avatar->SetVehicle( carData->mp_vehicle );
+
+ Character* character = avatar->GetCharacter();
+ GetAvatarManager()->PutCharacterInCar( character, carData->mp_vehicle );
+ }
+ }
+
+ // put the driver back inside the original vehicle (done
+ // regardless of whether or not the vehicle obtained a husk
+ // when it was destroyed.
+ GetVehicleCentral()->SetupDriver( carData->mp_vehicle );
+
+ // fire off event so Esan can know when we switch the vehicle on him.
+ GetEventManager()->TriggerEvent(
+ EVENT_VEHICLE_DESTROYED_SYNC_SOUND, (void*)carData->mp_vehicle );
+ }
+ // reset original vehicle's states
+ bool resetDamage = true;
+ carData->mp_vehicle->ResetFlagsOnly( resetDamage );
+}
+
+
+
+
+//=============================================================================
+// GameplayManager::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch (id)
+ {
+ //events we are interested in.
+ //case EVENT_GETINTOTRAFFIC_END:
+ case EVENT_GETINTOVEHICLE_END:
+ {
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+
+ Vehicle* avatarVehicle = avatar->GetVehicle();
+ rAssert( avatarVehicle );
+
+ if( GetVehicleCentral()->mHuskPool.IsHuskType( avatarVehicle->mVehicleID ) )
+ {
+ // we're driving a husk.. so if this is a husk allocated
+ // by the husk pool, then we've got to set the current
+ // vehicle to point to the original vehicle (not the husk itself)
+ // But if we couldn't find the original vehicle, then this
+ // husk either:
+ // 1) doesn't belong to the pool (somehow we created an original husk vehicle), or
+ // 2) belongs to the pool, but is a free husk (so why do we have its pointer here??)
+
+ Vehicle* originalVehicle = GetVehicleCentral()->
+ mHuskPool.FindOriginalVehicleGivenHusk( avatarVehicle );
+
+ rAssert( originalVehicle );
+
+ SetCurrentVehicle( originalVehicle );
+ }
+ else
+ {
+ SetCurrentVehicle( avatarVehicle );
+ }
+
+ break;
+ }
+ case EVENT_GETOUTOFVEHICLE_START:
+ {
+ break;
+ }
+ case EVENT_BONUS_MISSION_DIALOGUE:
+ {
+ //OKAY, let's start a bonus mission dialogue!
+ rAssert( GetCurrentMission()->IsSundayDrive() == true );
+
+ //Which mission?
+ EventLocator* evtLoc = static_cast<EventLocator*>(pEventData);
+
+ int i;
+ for ( i = 0; i < mNumBonusMissions; ++i )
+ {
+ if ( mBonusMissions[ i ].GetEventLocator() == evtLoc )
+ {
+ //Store this for next time.
+ mDesiredBonusMission = i + MAX_MISSIONS;
+ break;
+ }
+ }
+
+ rAssert( i < mNumBonusMissions );
+
+ //We have to fire the dialogue event outside of an eventhandler... Sigh.
+ mFireBonusMissionDialogue = true;
+ break;
+ }
+ case EVENT_CONVERSATION_DONE_AND_FINISHED:
+ {
+ if ( mDesiredBonusMission != -1 )
+ {
+ mJumpToBonusMission = true;
+
+ GetBonusMissionInfo( mDesiredBonusMission - MAX_MISSIONS )->ResetCharacterPositions();
+ }
+ break;
+ }
+ case EVENT_REPAIR_CAR:
+ {
+ // Dusit [08/04/2003]:
+ // the fact that we're not passing in the vehicle pointer as pEventData
+ // and the fact that this event is only fired in the main game (not in demo
+ // or supersprint) imply that we're only handling the event if the player
+ // picks up a wrench/repair icon.
+ //
+ // The rule is that we repair the user's current vehicle (the one he
+ // was last in, as pointed to by
+ //
+ // Avatar::GetLastVehicle()
+ //
+ // All managers that deal with husks (gameplaymanager, trafficmanager,
+ // parkedcarmanager, and chasemanager) will need to test the current vehicle
+ // against their list of vehicles to see if they need to swap out the husk
+ // of that vehicle (if it's damaged to that extent)... otherwise, just reset
+ // the vehicle's damage state.
+ //
+
+ // NOTE:
+ // We are assuming here that the gameplaymanager's current vehicle
+ // is never set to the husk, but remains the original vehicle, even if
+ // it was destroyed and replaced with a husk
+ //
+ Vehicle* currVehicle = GetGameplayManager()->GetCurrentVehicle();
+ if( currVehicle == NULL )
+ {
+ break;
+ }
+
+ // check the default car slot's original vehicle pointer
+ Vehicle* testVehicle = mVehicleSlots[ eDefaultCar ].mp_vehicle;
+ if( testVehicle == currVehicle ) // which also implies testVehicle != NULL
+ {
+ RepairVehicle( &(mVehicleSlots[ eDefaultCar ]) );
+
+ }
+ else
+ {
+ // now check the other car slot
+ testVehicle = mVehicleSlots[eOtherCar].mp_vehicle;
+ if( testVehicle == currVehicle ) // which also implies testVehicle != NULL
+ {
+ RepairVehicle( &(mVehicleSlots[ eOtherCar ]) );
+
+ // if it exists in OtherSlot, it could also exist in MissionVehicleSlots
+ // clear the husk out of these too
+ for( int i=0; i<MAX_MISSION_VEHICLE_SLOTS; i++ )
+ {
+ if( mMissionVehicleSlots[i].vehicle == testVehicle )
+ {
+ mMissionVehicleSlots[i].pHuskVehicle->Release();
+ mMissionVehicleSlots[i].pHuskVehicle = NULL;
+ mMissionVehicleSlots[i].usingHusk = false;
+ }
+ }
+ }
+ }
+
+
+ break;
+ }
+
+ case EVENT_VEHICLE_DESTROYED:
+ {
+ bool foundInVehicleSlots = false;
+
+ // see if the vehicle is one of ours and if so, swap in husk:
+ for(int i = 0; i < MAX_VEHICLE_SLOTS; i++)
+ {
+ Vehicle* v = (Vehicle*)pEventData;
+ if( mVehicleSlots[i].mp_vehicle == v &&
+ mVehicleSlots[i].usingHusk == false )
+ {
+ foundInVehicleSlots = true;
+
+
+ // one of ours
+ // swap in a husk
+ VehicleType vt = mVehicleSlots[i].mp_vehicle->mVehicleType;
+ Vehicle* husk = GetVehicleCentral()->mHuskPool.RequestHusk
+ ( vt, mVehicleSlots[i].mp_vehicle );
+ if(husk)
+ {
+ husk->AddRef();
+ mVehicleSlots[i].pHuskVehicle = husk;
+ mVehicleSlots[i].usingHusk = true;
+
+ // try to find it in MissionVehicleSlot... It's possible for the
+ // same car to exist in 2 lists: the principle (heavy-weight) AI car,
+ // the forced car (for forced car missions), etc.
+ for( int j=0; j<MAX_MISSION_VEHICLE_SLOTS; j++ )
+ {
+ if( mMissionVehicleSlots[j].vehicle == v )
+ {
+ husk->AddRef();
+ mMissionVehicleSlots[j].pHuskVehicle = husk;
+ mMissionVehicleSlots[j].usingHusk = true;
+ }
+ }
+
+ // set position and facing
+ rmt::Vector carPosition = mVehicleSlots[i].mp_vehicle->rPosition();
+ float ang = mVehicleSlots[i].mp_vehicle->GetFacingInRadians();
+
+ rmt::Matrix m;
+ m.Identity();
+ m.FillRotateXYZ( 0.0f, ang, 0.0f );
+ m.FillTranslate(carPosition);
+
+ GetVehicleCentral()->RemoveVehicleFromActiveList(mVehicleSlots[i].mp_vehicle);
+
+ husk->SetTransform(m);
+
+ GetVehicleCentral()->AddVehicleToActiveList(husk);
+
+ //
+ // Don't set mCurrentVehicle to husk because we want to preserve the original
+ // vehicle for comparison during the REPAIR event
+ //
+ // Also, don't set the avatar's vehicle to husk because we want the player
+ // to play dodge animation out of the vehicle.
+ //
+ //this->SetCurrentVehicle(husk);
+ //Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ //avatar->SetVehicle(husk);
+
+ // new
+ // fire off event so Esan can sync stuff up.
+ GetEventManager()->TriggerEvent(EVENT_VEHICLE_DESTROYED_SYNC_SOUND, (void*)husk);
+
+ break;
+ }
+ }
+
+ }
+
+ if( !foundInVehicleSlots )
+ {
+ for(int i = 0; i < MAX_MISSION_VEHICLE_SLOTS; i++)
+ {
+ if( mMissionVehicleSlots[i].vehicle == (Vehicle*)pEventData &&
+ mMissionVehicleSlots[i].usingHusk == false )
+ {
+ // one of ours
+ // swap in a husk
+ VehicleType vt = mMissionVehicleSlots[i].vehicle->mVehicleType;
+ Vehicle* husk = GetVehicleCentral()->mHuskPool.RequestHusk
+ ( vt, mMissionVehicleSlots[i].vehicle );
+
+ if(husk)
+ {
+ husk->AddRef();
+ mMissionVehicleSlots[i].pHuskVehicle = husk;
+ mMissionVehicleSlots[i].usingHusk = true;
+
+
+ // set position and facing
+ rmt::Vector carPosition = mMissionVehicleSlots[i].vehicle->rPosition();
+ float ang = mMissionVehicleSlots[i].vehicle->GetFacingInRadians();
+
+ rmt::Matrix m;
+ m.Identity();
+ m.FillRotateXYZ( 0.0f, ang, 0.0f );
+ m.FillTranslate(carPosition);
+
+
+ GetVehicleCentral()->RemoveVehicleFromActiveList(mMissionVehicleSlots[i].vehicle);
+ //mMissionVehicleSlots[i].vehicleCentralIndex = -1;
+
+ husk->SetTransform(m);
+
+ int index = GetVehicleCentral()->AddVehicleToActiveList(husk);
+ //mMissionVehicleSlots[i].vehicleCentralIndex = index;
+
+ // TODO - is this correct?
+ // are we thrown out of the car?
+ // is it safe to set mCurrentVehicle to 0?
+ //this->SetCurrentVehicle(husk);
+ //Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ //avatar->SetVehicle(husk); - what the hell were you doing you retard?
+
+ // new
+ // fire off event so Esan can sync stuff up.
+ GetEventManager()->TriggerEvent(EVENT_VEHICLE_DESTROYED_SYNC_SOUND, (void*)husk);
+
+ break;
+ }
+ }
+ } // end of for loop
+ }
+ break;
+ }
+
+ case EVENT_GUI_IRIS_WIPE_CLOSED:
+ {
+ GetGameFlow()->GetContext( GetGameFlow()->GetCurrentContext() )->Resume();
+ GetEventManager()->RemoveListener( this, EVENT_GUI_IRIS_WIPE_CLOSED );
+ rAssert( mFadedToBlack == false );
+ mIrisClosed = true;
+ GetEventManager()->AddListener( this, EVENT_GUI_IRIS_WIPE_OPEN );
+
+ //Chuck WTF if comment this line out the game seem to go into a suspend state.
+ //GetGuiSystem()->HandleMessage( GUI_MSG_START_IRIS_WIPE_OPEN );
+ break;
+ }
+ case EVENT_GUI_FADE_OUT_DONE:
+ {
+ GetGameFlow()->GetContext( GetGameFlow()->GetCurrentContext() )->Resume();
+ GetEventManager()->RemoveListener( this, EVENT_GUI_FADE_OUT_DONE );
+ rAssert( mIrisClosed == false );
+ mFadedToBlack = true;
+ mBlackScreenTimer = 2000; //Chuck set this timer to 1000 milliseconds, we will use this to countdown
+ break;
+ }
+ case EVENT_GUI_IRIS_WIPE_OPEN:
+ case EVENT_GUI_FADE_IN_DONE:
+ {
+ GetGameFlow()->GetContext( GetGameFlow()->GetCurrentContext() )->Resume();
+ GetEventManager()->RemoveListener( this, EVENT_GUI_IRIS_WIPE_OPEN );
+ GetEventManager()->RemoveListener( this, EVENT_GUI_FADE_IN_DONE );
+
+ //Reset both.
+ mIrisClosed = false;
+ mFadedToBlack = false;
+ break;
+ }
+ case EVENT_GUI_MISSION_LOAD_COMPLETE:
+ {
+ spInstance->MDKVDU();
+ break;
+ }
+ case EVENT_WAYAI_HIT_LAST_WAYPOINT:
+ {
+ if ( mIsDemo )
+ {
+ HeapMgr()->DumpHeapStats( true );
+ }
+ }
+ case EVENT_USER_CANCEL_MISSION_BRIEFING:
+ {
+ if ( GetCurrentMission()->IsForcedCar() == true)
+ {
+ //do some swapp.
+ int swap =1;
+ //put the player on the ground and place them were they were before when conversation started.
+ //lock the forced car so the player can't enter.
+ mVehicleSlots[eOtherCar].mp_vehicle->TransitToAI();
+ GetCharacterManager()->RemoveCharacter(mVehicleSlots[eOtherCar].mp_vehicle->GetDriver());
+ mVehicleSlots[eOtherCar].mp_vehicle->SetDriver(NULL);
+ }
+
+ if(GetCurrentMission()->IsBonusMission())
+ {
+ GetCharacterManager()->ResetBonusCharacters();
+ }
+
+ break;
+ }
+ case EVENT_USER_CANCEL_PAUSE_MENU:
+ {
+ //Allow user control of car, do this incase we exited from a stage with a countdown presentation.
+ mCurrentVehicle->SetDisableGasAndBrake(false);
+ mPlayerAndCarInfo.mbDirtyFlag = false;
+ if(GetCurrentMission()->IsForcedCar() ==true)
+ { //we are quiting from forced car mission we need to lock the car so user cannot drive it.
+ mVehicleSlots[eOtherCar].mp_vehicle->TransitToAI();
+ Character* c = mVehicleSlots[eOtherCar].mp_vehicle->GetDriver();
+ if(c)
+ {
+ GetCharacterManager()->RemoveCharacter(c);
+ }
+ mVehicleSlots[eOtherCar].mp_vehicle->SetDriver(NULL);
+ }
+
+ if(GetCurrentMission()->IsBonusMission())
+ {
+ GetCharacterManager()->ResetBonusCharacters();
+ }
+ break;
+ }
+
+ default:
+
+ {
+
+ break;
+ }
+ }
+}
+
+
+//returns a pointer to a vehicle if found in the internal index.
+/*
+Vehicle* GameplayManager::GetVehicle(char* name)
+{
+ for (int i=0; i<miNumLevelVehicles;i++)
+ {
+ //found the car
+ if(strcmp(mLevelVehicles[i]->mName,name)==0)
+ {
+ return mLevelVehicles[i];
+ }
+ }
+
+ //default if we didnt find car return a NULL
+ return NULL;
+}
+*/
+
+//returns lasted used car
+Vehicle* GameplayManager::GetCurrentVehicle( void)
+{
+ //rAssert( mCurrentVehicle != NULL);
+ return mCurrentVehicle;
+}
+
+/*=============================================================================
+Force whatever icon we have for the HUD to be unregistered.
+=============================================================================*/
+void GameplayManager::UnregisterVehicleHUDIcon(void)
+{
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if(currentHud && mCurrentVehicleIconID != -1)
+ {
+ currentHud->GetHudMap( 0 )->UnregisterIcon( mCurrentVehicleIconID );
+ mCurrentVehicleIconID = -1;
+ }
+}
+
+//sets current car
+void GameplayManager::SetCurrentVehicle(Vehicle* vehicle)
+{
+
+ if( vehicle )
+ {
+ //
+ // Dusit [08/04/2003]:
+ // Make sure here that we don't set make husk (that is, the husk
+ // that replaced our original vehicle because it got destroyed)
+ // as the current vehicle because there is code in various managers'
+ // HandleEvent() for EVENT_VEHICLE_DESTROYED and EVENT_REPAIR_CAR
+ // cases that assume mCurrentVehicle points to the original vehicle.
+ //
+ rAssert( !GetVehicleCentral()->mHuskPool.IsHuskType( vehicle->mVehicleID ) );
+ }
+
+ //release old add ref
+ if (mCurrentVehicle != NULL)
+ {
+ mCurrentVehicle->Release();
+ }
+
+ mCurrentVehicle = vehicle;
+
+ if (mCurrentVehicle !=NULL )
+ {
+ mCurrentVehicle->AddRef();
+ }
+
+
+ // update player car icon in HUD map w/ new vehicle reference
+ //
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ // register new icon only if current vehicle is a user vehicle
+ //
+ if( mCurrentVehicle != NULL && mCurrentVehicle->mVehicleType == VT_USER )
+ {
+ // un-register old icon first
+ //
+ if( mCurrentVehicleIconID != -1 )
+ {
+ currentHud->GetHudMap( 0 )->UnregisterIcon( mCurrentVehicleIconID );
+ mCurrentVehicleIconID = -1;
+ }
+
+ mCurrentVehicleIconID = currentHud->GetHudMap( 0 )->RegisterIcon( HudMapIcon::ICON_PLAYER_CAR,
+ rmt::Vector( 0.0f, 0.0f, 0.0f ),
+ mCurrentVehicle );
+ }
+ }
+
+ if ( vehicle )
+ {
+ //
+ // This seems to be the best place to inform the sound manager
+ // of the new car, just in case someone introduces a new way
+ // to switch cars. I'm open to other suggestions, though---Esan
+ //
+ GetSoundManager()->LoadCarSound( vehicle, true );
+ }
+}
+
+//Call this to free memory taken up by player car if it was loaded with LoadDisposeable Car.
+void GameplayManager::DumpCurrentCar ()
+{
+ if (mCurrentVehicle !=NULL )
+ {
+ if ( mVehicleSlots[eDefaultCar].mp_vehicle == mCurrentVehicle)
+ {
+ ClearVehicleSlot(eDefaultCar);
+ }
+ else if (mVehicleSlots[eOtherCar].mp_vehicle == mCurrentVehicle)
+ {
+ ClearVehicleSlot(eOtherCar);
+ }
+ //if the first two conditions are not met then player must have hijacked a AI car
+ else if (mVehicleSlots[eDefaultCar].mp_vehicle != NULL)
+ {
+ //rAssertMsg( 0, "I don't think we should ever be here - see greg \n" );
+ // no, actually it's ok - perhaps they drove a husk up to a phone booth
+
+ //clear the current car
+ GetVehicleCentral()->RemoveVehicleFromActiveList(mCurrentVehicle);
+
+ //clear our default car too now.
+ ClearVehicleSlot(eDefaultCar);
+ }
+ else if (mVehicleSlots[eOtherCar].mp_vehicle != NULL)
+ {
+ //rAssertMsg( 0, "I don't think we should ever be here - see greg \n" );
+ ClearVehicleSlot(eOtherCar);
+ }
+ else
+ {
+ //we are getting rid some free car or traffic car
+ GetVehicleCentral()->RemoveVehicleFromActiveList(mCurrentVehicle);
+ }
+ SetCurrentVehicle( NULL );
+ }
+}
+
+//=============================================================================
+// GameplayManager::ClearVehicleSlotIfInSphere
+//=============================================================================
+void GameplayManager::ClearVehicleSlotIfInSphere( eCarSlots slot, const rmt::Sphere& s )
+{
+ Vehicle* vehicle = GetVehicleInSlot( slot );
+ rmt::Vector position;
+ if( vehicle != NULL &&
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle() != vehicle )
+ {
+ vehicle->GetPosition( &position );
+ bool isVehicleInSphere = s.Contains( position );
+ if( isVehicleInSphere )
+ {
+ ClearVehicleSlot( slot );
+ }
+ }
+}
+
+//=============================================================================
+// GameplayManager::DumpCurrentCarIfInSphere
+//=============================================================================
+// Description: Dumps the current car only if it was in the sphere provided
+//
+// Parameters: s - the sphere that the car's center must be inside
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::DumpCurrentCarIfInSphere( const rmt::Sphere& s )
+{
+ ClearVehicleSlotIfInSphere( eDefaultCar, s );
+ ClearVehicleSlotIfInSphere( eOtherCar, s );
+ //ClearVehicleSlotIfInSphere( eAICar, s );
+}
+
+//=============================================================================
+// GameplayManager::MakeSureHusksAreReverted
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::MakeSureHusksAreReverted(Vehicle* pvehicle)
+{
+ // just called from Mission::Reset
+ int i;
+ for(i = 0; i < MAX_VEHICLE_SLOTS; i++)
+ {
+ //we should only fix car if they match what is need for the mission stage
+ if( (mVehicleSlots[i].usingHusk == true) && (mVehicleSlots[i].mp_vehicle == pvehicle))
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList(mVehicleSlots[i].pHuskVehicle);
+ GetVehicleCentral()->mHuskPool.FreeHusk(mVehicleSlots[i].pHuskVehicle);
+ mVehicleSlots[i].pHuskVehicle->Release();
+ mVehicleSlots[i].pHuskVehicle = NULL;
+ mVehicleSlots[i].usingHusk = false;
+
+ if(mVehicleSlots[i].mp_vehicle)
+ {
+ // add back to world
+ mVehicleSlots[i].mp_vehicle->ResetFlagsOnly(true);
+ GetVehicleCentral()->AddVehicleToActiveList(mVehicleSlots[i].mp_vehicle);
+
+ }
+ }
+ }
+ for(i = 0; i < MAX_MISSION_VEHICLE_SLOTS; i++)
+ {
+ if ( (mMissionVehicleSlots[i].usingHusk == true) && (mMissionVehicleSlots[i].vehicle == pvehicle))
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList(mMissionVehicleSlots[i].pHuskVehicle);
+ GetVehicleCentral()->mHuskPool.FreeHusk(mMissionVehicleSlots[i].pHuskVehicle);
+ mMissionVehicleSlots[i].pHuskVehicle->Release();
+ mMissionVehicleSlots[i].pHuskVehicle = NULL;
+ mMissionVehicleSlots[i].usingHusk = false;
+
+ // shoudl this also add back the vehicles to the world???
+ if(mMissionVehicleSlots[i].vehicle)
+ {
+ mMissionVehicleSlots[i].vehicle->ResetFlagsOnly(true);
+ GetVehicleCentral()->AddVehicleToActiveList(mMissionVehicleSlots[i].vehicle);
+ }
+
+ }
+ }
+}
+
+
+//=============================================================================
+//dumps car in slot from inventory and resets the slot
+// GameplayManager::ClearVehicleSlot
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (eCarSlots slot)
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::ClearVehicleSlot (eCarSlots slot)
+{
+
+
+ if( mVehicleSlots[slot].mp_vehicle != NULL ||
+ mVehicleSlots[slot].pHuskVehicle != NULL )
+ {
+ p3d::pddi->DrawSync();
+
+ if(mVehicleSlots[slot].usingHusk)
+ {
+ rAssert( mVehicleSlots[slot].pHuskVehicle );
+
+ GetVehicleCentral()->RemoveVehicleFromActiveList(mVehicleSlots[slot].pHuskVehicle);
+ GetVehicleCentral()->mHuskPool.FreeHusk(mVehicleSlots[slot].pHuskVehicle);
+ mVehicleSlots[slot].pHuskVehicle->Release();
+ mVehicleSlots[slot].pHuskVehicle = NULL;
+ mVehicleSlots[slot].usingHusk = false;
+ }
+ else
+ {
+ rAssert( mVehicleSlots[slot].mp_vehicle );
+
+ GetEventManager()->TriggerEvent(EVENT_VEHICLE_DESTROYED_SYNC_SOUND,mVehicleSlots[slot].mp_vehicle);
+
+ //if the slot we are removing this car is the in the other we put it into the VDU since we dont want popping
+ if (slot == GameplayManager::eOtherCar)
+ {
+ spInstance->AddToVDU(mVehicleSlots[slot].mp_vehicle);
+ }
+ else
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList(mVehicleSlots[slot].mp_vehicle);
+ }
+ }
+
+ if( mVehicleSlots[slot].mp_vehicle )
+ {
+ mVehicleSlots[slot].mp_vehicle->Release ();
+ mVehicleSlots[slot].mp_vehicle =NULL;
+ }
+
+ p3d::inventory->RemoveSectionElements(mVehicleSlots[slot].filename);
+ p3d::inventory->DeleteSection(mVehicleSlots[slot].filename);
+
+ //we should never clear the default cars info
+
+ // if we load "other" car, we need this info around
+
+ // but, loading a new phone booth car will be essentially loading a new default car
+ // things should still work ok, as long as the loading of the phone booth car overwrites
+ // all the default slots
+
+ if (slot != GameplayManager::eDefaultCar)
+ {
+ strcpy(mVehicleSlots[slot].name,"Null");
+ strcpy(mVehicleSlots[slot].filename,"Null");
+ mVehicleSlots[slot].heading=0;
+ mVehicleSlots[slot].position.x =0;
+ mVehicleSlots[slot].position.y =0;
+ mVehicleSlots[slot].position.z =0;
+ }
+ else
+ {
+ UnregisterVehicleHUDIcon();
+ }
+ }
+
+}
+
+void GameplayManager::CopyVehicleSlot( eCarSlots sourceSlot, eCarSlots destSlot )
+{
+ ClearVehicleSlot(sourceSlot);
+ // Now copy the data from source to dest
+
+ // copy name and filenames over
+ const char* vehicleName = mVehicleSlots[ sourceSlot ].name;
+ strcpy( mVehicleSlots[ destSlot ].filename, mVehicleSlots[ sourceSlot ].filename);
+ strcpy( mVehicleSlots[ destSlot ].name, vehicleName );
+
+ Vehicle* vehicle = GetVehicleCentral()->InitVehicle( vehicleName, true, 0, VT_USER );
+ rAssert( vehicle != NULL );
+ mVehicleSlots[ destSlot ].mp_vehicle = vehicle;
+ // lets not use a husk, make a new one
+ mVehicleSlots[ destSlot ].pHuskVehicle = NULL;
+ mVehicleSlots[ destSlot ].usingHusk = false;
+ // Use the same position ( could this cause a problem? )
+ mVehicleSlots[ destSlot ].position = mVehicleSlots[ sourceSlot ].position;
+ mVehicleSlots[ destSlot ].heading = mVehicleSlots[ sourceSlot ].heading;
+}
+
+//returns filename/path of car in that slot
+char* GameplayManager::GetVehicleSlotFilename(eCarSlots slot)
+{
+ return mVehicleSlots[slot].filename;
+}
+
+
+//returns the name of the car in that slot
+char* GameplayManager::GetVehicleSlotVehicleName(eCarSlots slot)
+{
+ return mVehicleSlots[slot].name;
+}
+
+//=============================================================================
+// GameplayManager::GetVehicleInSlot
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (eCarSlots slot)
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* GameplayManager::GetVehicleInSlot(eCarSlots slot)
+{
+ return mVehicleSlots[slot].mp_vehicle;
+}
+
+
+//=============================================================================
+// GameplayManager::SetBonusMissionInfo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* NPCname, const char* missionName, const char* iconName, bool isOneShot )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::SetBonusMissionInfo( const char* NPCname,
+ const char* missionName,
+ const char* iconName,
+ const char* dialogueName,
+ bool isOneShot,
+ const char* alternateIconName,
+ bool wasCompletedAlready )
+{
+MEMTRACK_PUSH_GROUP( "GamePlayManager::BonusMissionInfo" );
+ //Which mission is this for?
+
+int missionNum = GetMissionNumByName( missionName );
+ rAssert( missionNum >= 0 && missionNum >= MAX_MISSIONS && missionNum < MAX_BONUS_MISSIONS + MAX_MISSIONS );
+
+ if ( missionNum >= MAX_MISSIONS )
+ {
+ if (strcmp(missionName,"bm1") ==0)
+ {
+ if (GetCharacterSheetManager()->QueryBonusMissionCompleted(GetMissionManager()->GetCurrentLevelIndex()) == true)
+ {
+ Character* character = GetCharacterManager()->GetCharacterByName( NPCname );
+ rAssert( character );
+ character->SetRole(Character::ROLE_COMPLETED_BONUS);
+ GetCharacterManager()->SetGarbage(character, true);
+ return;
+ }
+ }
+
+ int missionIndex = missionNum - MAX_MISSIONS;
+ mBonusMissions[ missionIndex ].SetMissionNum( missionNum );
+
+ Character* character = GetCharacterManager()->GetCharacterByName( NPCname );
+ rAssert( character );
+
+ mBonusMissions[ missionIndex ].SetNPC( character );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+ #endif
+ //Set up the talk trigger around this guy.
+ EventLocator* evtLoc = new EventLocator();
+
+ rAssert( evtLoc );
+
+ mBonusMissions[ missionIndex ].SetEventLocator( evtLoc );
+
+ evtLoc->SetName( NPCname );
+ evtLoc->SetEventType( LocatorEvent::GENERIC_BUTTON_HANDLER_EVENT );
+
+ int id = -1;
+ ActionButton::GenericEventButtonHandler* pABHandler = static_cast<ActionButton::GenericEventButtonHandler*>( ActionButton::GenericEventButtonHandler::NewAction( evtLoc, EVENT_BONUS_MISSION_DIALOGUE, HeapMgr()->GetCurrentHeap() ) );
+ rAssert( dynamic_cast<ActionButton::GenericEventButtonHandler*> ( pABHandler ) != NULL );
+ rAssert( pABHandler );
+
+ pABHandler->SetEventData( evtLoc );
+
+ bool bResult = GetActionButtonManager()->AddAction( pABHandler, id );
+
+ rAssert( bResult );
+
+ evtLoc->SetData( id );
+
+ // Radius should equal about 1m.
+ //
+ const float r = 1.3f;
+ rmt::Vector charPos;
+ character->GetPosition( charPos );
+ SphereTriggerVolume* pTriggerVolume = new SphereTriggerVolume( charPos, r );
+
+ evtLoc->SetNumTriggers( 1, HeapMgr()->GetCurrentAllocator() );
+
+ evtLoc->AddTriggerVolume( pTriggerVolume );
+ pTriggerVolume->SetLocator( evtLoc );
+ pTriggerVolume->SetName( "BONUS Trigger" );
+
+ //Set up the animated icon
+ mBonusMissions[ missionIndex ].SetUpIcon( iconName, charPos );
+
+ mBonusMissions[ missionIndex ].SetOneShot( isOneShot );
+
+ mBonusMissions[ missionIndex ].SetDialogueName( dialogueName );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_MISSION );
+ #endif
+
+ if ( alternateIconName != NULL )
+ {
+ mBonusMissions[ missionIndex ].SetUpAlternateIcon( alternateIconName, charPos );
+ }
+
+ mBonusMissions[ missionIndex ].SetCompleted( wasCompletedAlready );
+
+ }
+MEMTRACK_POP_GROUP( "GamePlayManager::BonusMissionInfo" );
+}
+
+
+
+
+// Kill this!
+//
+/*
+void GameplayManager::GetOverrideVehicleName( char* name )
+{
+ if( mVehicleIndex < 0 )
+ {
+ // ignore, using default vehicle specified in script
+ return;
+ }
+
+ const char* VEHICLE_NAMES[] =
+ {
+ "apu_v", // Vehicle 1
+ "bart_v", // Vehicle 2
+ "cletu_v", // Vehicle 3
+ "comic_v", // and so on ...
+ "famil_v",
+ "gramp_v",
+ "homer_v",
+ "marge_v",
+ "skinn_v",
+ "smith_v",
+ "snake_v",
+ "wiggu_v",
+ "zombi_v",
+ "cVan",
+ "compactA",
+
+ // new additions
+ "frink_v",
+ "lisa_v",
+ "cCola"
+ };
+
+ if( mVehicleIndex >= 0 &&
+ mVehicleIndex < static_cast<int>(sizeof( VEHICLE_NAMES ) / sizeof( VEHICLE_NAMES[ 0 ] )) )
+ {
+ strcpy( name, VEHICLE_NAMES[ mVehicleIndex ] );
+ }
+ else
+ {
+ rAssertMsg( 0, "WARNING: *** Vehicle not available!\n" );
+ }
+}
+*/
+
+// MS10: Hack to allow character selection
+void GameplayManager::GetOverrideCharacterName( char* name )
+{
+ switch( mCharacterIndex )
+ {
+ case -1:
+ {
+ strcpy( name, "homer" );
+ break;
+ }
+ case 0:
+ {
+ strcpy( name, "homer" );
+ break;
+ }
+ case 1:
+ {
+ strcpy( name, "marge" );
+ break;
+ }
+ case 2:
+ {
+ strcpy( name, "bart" );
+ break;
+ }
+ case 3:
+ {
+ strcpy( name, "lisa" );
+ break;
+ }
+ case 4:
+ {
+ strcpy( name, "apu" );
+ break;
+ }
+ }
+}
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// GameplayManager::Initialize
+//=============================================================================
+// Description:
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::Initialize()
+{
+ rAssertMsg( mNumMissions == 0, "The Gameplay Manager wasn't finalized"
+ " when the game was stopped!" );
+
+ mNumMissions = 0;
+ mNumBonusMissions = 0;
+ mLevelComplete = false;
+ mGameComplete = false;
+ mCurrentMission = -1;
+ mCurrentBonusMission = -1;
+ mDesiredBonusMission = -1;
+ mIsInBonusMission = false;
+ mFireBonusMissionDialogue = false;
+ //miNumLevelVehicles = 0;
+ GetEventManager()->AddListener(this,EVENT_GETINTOVEHICLE_END);
+ GetEventManager()->AddListener(this,EVENT_BONUS_MISSION_DIALOGUE);
+ GetEventManager()->AddListener(this,EVENT_CONVERSATION_DONE_AND_FINISHED);
+ GetEventManager()->AddListener(this,EVENT_REPAIR_CAR);
+ GetEventManager()->AddListener(this,EVENT_VEHICLE_DESTROYED);
+ //GetEventManager()->AddListener(this,EVENT_GETINTOTRAFFIC_END);
+ GetEventManager()->AddListener(this,EVENT_GUI_MISSION_LOAD_COMPLETE);
+ GetEventManager()->AddListener(this,EVENT_USER_CANCEL_MISSION_BRIEFING);
+ GetEventManager()->AddListener(this,EVENT_USER_CANCEL_PAUSE_MENU);
+
+ if ( mIsDemo )
+ {
+ GetEventManager()->AddListener( this, EVENT_WAYAI_HIT_LAST_WAYPOINT );
+ }
+
+ mDefaultLevelVehicleName[0] = '\0';
+ mDefaultLevelVehicleLocator[0] = '\0';
+ mDefaultLevelVehicleConfile[0] = '\0';
+ mShouldLoadDefaultVehicle = false;
+ //Create the RespawnManager
+ mpRespawnManager = new (GMA_LEVEL_OTHER) RespawnManager();
+}
+
+//=============================================================================
+// GameplayManager::Finalize
+//=============================================================================
+// Description:
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::Finalize()
+{
+ p3d::pddi->DrawSync();
+ //clear any cars that we are holding on to.
+ RemoveLevelVehicleController();
+ SetCurrentMission( -1 );
+
+ int i;
+ for( i = 0; i < MAX_MISSIONS + MAX_BONUS_MISSIONS; i++)
+ {
+ if ( mMissions[ i ] != NULL )
+ {
+ delete mMissions[ i ];
+ }
+
+ mMissions[ i ] = NULL;
+ }
+
+ for ( i = 0; i < mNumBonusMissions; ++i )
+ {
+ mBonusMissions[i].CleanUp();
+ }
+
+ mNumMissions = 0;
+ mNumBonusMissions = 0;
+ mUpdateBonusMissions = true;
+ mJumpToBonusMission = false;
+
+
+ EmptyMissionVehicleSlots();
+ spInstance->MDKVDU();
+
+
+ //miNumLevelVehicles = 0;
+ SetCurrentVehicle( NULL );
+
+ ClearVehicleSlot(eDefaultCar);
+ ClearVehicleSlot(eOtherCar);
+ ClearVehicleSlot(eAICar);
+
+ KillAllChaseManagers();
+
+ //Kill RespawnManager
+ delete mpRespawnManager;
+ mpRespawnManager = NULL;
+
+
+
+ p3d::inventory->RemoveSectionElements("Mission");
+ p3d::inventory->RemoveSectionElements("Level");
+ p3d::inventory->DeleteSection("Mission");
+ p3d::inventory->DeleteSection("Level");
+ GetEventManager()->RemoveAll( this );
+}
+
+//=============================================================================
+// GameplayManager::SetCurrentMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index )
+//
+// Return: inline
+//
+//=============================================================================
+void GameplayManager::SetCurrentMission( int index )
+{
+ //Switching to a new mission, so clean up the old one.
+ Mission* oldMission = GetCurrentMission();
+
+ //Set to new mission.
+ if ( mIsDemo && index >= mNumMissions )
+ {
+ //Reselect the mission in the range of mNumMissions
+ mCurrentMission = rand() % mNumMissions;
+ mIsInBonusMission = false;
+ }
+ else if ( index >= mNumMissions )
+ {
+ //This better be a bonus mission.
+ rAssert( index < MAX_MISSIONS + MAX_BONUS_MISSIONS);
+ mCurrentBonusMission = index;
+ mIsInBonusMission = true;
+ }
+ else
+ {
+ mCurrentMission = index;
+ mIsInBonusMission = false;
+ }
+
+ Mission* newMission = GetCurrentMission();
+
+ if( oldMission != NULL )
+ {
+ // stop mission-related HUD event handlers
+ //
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->GetEventHandler( CGuiScreenHud::HUD_EVENT_HANDLER_MISSION_OBJECTIVE )->Stop();
+ currentHud->GetEventHandler( CGuiScreenHud::HUD_EVENT_HANDLER_MISSION_OBJECTIVE )->Stop();
+ }
+
+ oldMission->Finalize();
+ CleanMissionData();
+ }
+
+ if( newMission != NULL )
+ {
+
+ if ( mIsInBonusMission )
+ {
+ mCurrentMissionHeap = GetMissionHeap( mCurrentBonusMission );
+ }
+ else
+ {
+ mCurrentMissionHeap = GetMissionHeap( mCurrentMission );
+ }
+
+ LoadMission();
+
+
+
+
+ if ( mShouldLoadDefaultVehicle )
+ {
+ if ( newMission->IsForcedCar() )
+ {
+ //We're loading a different car, so ignore. TRUCK, I HATE THIS HACK
+ mShouldLoadDefaultVehicle = false;
+ mPutPlayerInCar = false;
+ }
+ else
+ {
+ //Create the filename
+ char filename[128];
+ sprintf( filename, "art\\cars\\%s.p3d", mDefaultLevelVehicleName );
+ char* argv[] = { "", filename, mDefaultLevelVehicleName, "DEFAULT" };
+ MissionScriptLoader::LoadDisposableCar( 4, argv );
+ }
+ }
+ //update CharacterSheet with current mission
+ GetCharacterSheetManager()->SetCurrentMission(spInstance->GetCurrentLevelIndex(),
+ static_cast<RenderEnums::MissionEnum>( spInstance->GetCurrentMissionIndex() ) );
+
+ }
+}
+
+//=============================================================================
+// GameplayManager::NextMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::NextMission()
+{
+ mCurrentMessage = NEXT_MISSION;
+}
+
+//=============================================================================
+// GameplayManager::PrevMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::PrevMission()
+{
+ mCurrentMessage = PREV_MISSION;
+}
+
+//=============================================================================
+// GameplayManager::DoNextMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::DoNextMission()
+{
+ if((mLevelComplete == true) || (mGameComplete == true))
+ {
+ return;
+ }
+
+ int newMission = mCurrentMission;
+
+ if ( mIsInBonusMission )
+ {
+ //Return to the current mission. (Should be sunday drive mode)
+ //Do nothing.
+ }
+ else if ( (mCurrentMission + 1) >= mNumMissions )
+ {
+ if( strlen( mPostLevelFMV ) > 0 )
+ {
+ /*
+ GetPresentationManager()->PlayFMV( mPostLevelFMV, this );
+ mPostLevelFMV[ 0 ] = 0;
+ mWaitingOnFMV = true;
+ GetGameFlow()->SetQuickStartLoading( true );
+ GetRenderManager()->FreezeAllLayers();
+ return;
+ */
+ }
+ //Loop
+ //TODO: ???
+ if( !mWaitingOnFMV )
+ {
+ mLevelComplete = true;
+ if( GetCurrentLevelIndex() == RenderEnums::numLevels - 1 )
+ {
+ mGameComplete = true;
+ }
+ GetGameFlow()->SetContext( CONTEXT_PAUSE );
+ }
+
+ //Hmmmm.
+ return;
+ }
+ else
+ {
+ //Next
+ if((mLevelComplete != true) && (mGameComplete != true))
+ {
+ newMission = mCurrentMission + 1;
+ }
+ }
+
+ if ( GetCurrentMission()->IsForcedCar() && (GetCurrentMission()->GetSwappedCarsFlag() != true ) )
+ {
+ mShouldLoadDefaultVehicle = true;
+ }
+ SetCurrentMission( newMission );
+}
+
+//=============================================================================
+// GameplayManager::DoPrevMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::DoPrevMission()
+{
+ int newMission = mCurrentMission;
+
+ if ( mIsInBonusMission )
+ {
+ //Return to the current mission. (Should be sunday drive mode)
+ //mIsInBonusMission = false;
+ }
+ else if ( (mCurrentMission - 1) < 0 || (mCurrentMission - 1) >= mNumMissions )
+ {
+ //Loop
+ newMission = 0;
+ }
+ else
+ {
+ //Next
+ newMission = mCurrentMission - 1;
+ }
+
+ SetCurrentMission( newMission );
+}
+
+//=============================================================================
+// GameplayManager::GetCurrentMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Mission
+//
+//=============================================================================
+Mission* GameplayManager::GetCurrentMission()
+{
+ if ( mIsInBonusMission )
+ {
+ if ( mCurrentBonusMission >= MAX_MISSIONS && mCurrentBonusMission < MAX_MISSIONS + MAX_BONUS_MISSIONS )
+ {
+ return ( GetMission( mCurrentBonusMission ) );
+ }
+ }
+ else
+ {
+ if(( mCurrentMission >= 0 ) && ( mCurrentMission < (int)mNumMissions ))
+ {
+ return( GetMission( mCurrentMission ) );
+ }
+ }
+
+ return( NULL );
+}
+
+//=============================================================================
+// GameplayManager::GetCurrentBonusMissionInfo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: BonusMissionInfo
+//
+//=============================================================================
+const BonusMissionInfo* GameplayManager::GetCurrentBonusMissionInfo() const
+{
+ if( mIsInBonusMission )
+ {
+ int bonusMissionInfoIndex = mCurrentBonusMission - MAX_MISSIONS;
+ rAssert( bonusMissionInfoIndex >= 0 && bonusMissionInfoIndex < MAX_BONUS_MISSIONS );
+
+ return &(mBonusMissions[ bonusMissionInfoIndex ]);
+ }
+
+ return NULL;
+}
+
+//=============================================================================
+// GameplayManager::GetBonusMissionInfo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int missionNumber )
+//
+// Return: BonusMissionInfo
+//
+//=============================================================================
+BonusMissionInfo* GameplayManager::GetBonusMissionInfo( int missionNumber )
+{
+ if ( missionNumber < MAX_BONUS_MISSIONS )
+ {
+ return &mBonusMissions[ missionNumber ];
+ }
+
+ return NULL;
+}
+
+
+//=============================================================================
+// GameplayManager::GetMissionHeap
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index )
+//
+// Return: GameMemoryAllocator
+//
+//=============================================================================
+GameMemoryAllocator GameplayManager::GetMissionHeap( int index )
+{
+#ifdef RAD_GAMECUBE
+ return GMA_GC_VMM;
+#else
+ return( GMA_LEVEL_MISSION );
+#endif
+}
+
+//=============================================================================
+// GameplayManager::GetCurrentMissionNum
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+int GameplayManager::GetCurrentMissionNum() const
+{
+ if ( mIsInBonusMission )
+ {
+ return mCurrentBonusMission;
+ }
+ else
+ {
+ return mCurrentMission;
+ }
+}
+
+//=============================================================================
+// GameplayManager::RestartCurrentMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::RestartCurrentMission()
+{
+
+}
+
+//=============================================================================
+// GameplayManager::RestartToMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( RenderEnums::MissionEnum mission )
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::RestartToMission( RenderEnums::MissionEnum mission )
+{
+
+}
+
+
+//=============================================================================
+// GameplayManager::AbortCurrentMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::AbortCurrentMission()
+{
+
+}
+
+//=============================================================================
+// GameplayManager::IsBonusMissionDesired
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+bool GameplayManager::IsBonusMissionDesired() const
+{
+ return ( mDesiredBonusMission != -1 );
+}
+
+//=============================================================================
+// GameplayManager::CancelBonusMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::CancelBonusMission()
+{
+ mBonusMissions[ mDesiredBonusMission - MAX_MISSIONS ].ResetMissionBitmap();
+ mDesiredBonusMission = -1;
+}
+
+//=============================================================================
+// GameplayManager::EnabledPhonesBooths
+//=============================================================================
+// Description: Sets mEnablePhoneBooths flag to true
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::EnablePhoneBooths()
+{
+ mEnablePhoneBooths = true;
+}
+
+
+
+//=============================================================================
+// GameplayManager::DisablePhonesBooths
+//=============================================================================
+// Description: Sets mEnablePhoneBooths flag to false
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::DisablePhoneBooths()
+{
+ mEnablePhoneBooths = false;
+}
+
+//=============================================================================
+// GameplayManager::QueryPhoneBoothsEnabled
+//=============================================================================
+// Description: returns mEnablePhoneBooths flag to true
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+bool GameplayManager::QueryPhoneBoothsEnabled()
+{
+ return mEnablePhoneBooths;
+}
+
+
+//creates a chasemanager for the hostvehicle, returns 0 if good -1 if it cant
+//create a chasemanager
+int GameplayManager::CreateChaseManager(char* hostvehiclename,char* confile,int spawnrate)
+{
+ for (int i=0;i<MAX_CHASEMANAGERS;i++)
+ {
+ if (strcmp(m_ChaseManager_Array[i].hostvehicle,"NULL") == 0 && m_ChaseManager_Array[i].mp_chasemanager==NULL)
+ {
+ //found an empty slot make the chasemanager and return 0
+ strcpy(m_ChaseManager_Array[i].hostvehicle,hostvehiclename);
+ m_ChaseManager_Array[i].mp_chasemanager=new (GMA_LEVEL_OTHER) ChaseManager();
+
+ m_ChaseManager_Array[i].mp_chasemanager->RegisterModel(hostvehiclename,spawnrate);
+ m_ChaseManager_Array[i].mp_chasemanager->SetConfileName( confile);
+ m_ChaseManager_Array[i].mp_chasemanager->Init();
+ m_ChaseManager_Array[i].mp_chasemanager->SetMaxObjects(0);
+ m_ChaseManager_Array[i].mp_chasemanager->SetActive(false);
+
+
+ return 0;
+ }
+ }
+
+ //default return if no empty slots
+ return -1;
+}
+
+
+//returns a ptr to the correct chasemanager for a host vehicle or NULL if failure
+ChaseManager* GameplayManager::GetChaseManager(char* hostvehiclename)
+{
+ for(int i =0; i<MAX_CHASEMANAGERS;i++)
+ {
+ if (strcmp(m_ChaseManager_Array[i].hostvehicle,hostvehiclename) == 0)
+ {
+ return m_ChaseManager_Array[i].mp_chasemanager;
+ }
+ }
+ return NULL;
+}
+
+
+// overloaded function , GetChaseManager by index.
+ChaseManager* GameplayManager::GetChaseManager(int index)
+{
+ if ((index >=0) && (index <= MAX_CHASEMANAGERS))
+ {
+ return m_ChaseManager_Array[index].mp_chasemanager ;
+ }
+
+ else
+ {
+ return NULL;
+ }
+}
+
+
+
+
+//=============================================================================
+// GameplayManager::GetMissionNumByName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: int
+//
+//=============================================================================
+int GameplayManager::GetMissionNumByName( const char* name )
+{
+ int i;
+ for ( i = 0; i < MAX_MISSIONS + mNumBonusMissions; ++i )
+ {
+ if ( mMissions[i] && strcmp( mMissions[i]->GetName(), name ) == 0 )
+ {
+ return i;
+ }
+ }
+
+ rAssertMsg( false, "Trying to access non-existant mission... Poo.\n" );
+ return -1;
+}
+
+//=============================================================================
+// GameplayManager::GetBonusMissionNumByName
+//=============================================================================
+// Description: bonus missions are enumerated differently
+//
+// Parameters: ( const char* name )
+//
+// Return: int
+//
+//=============================================================================
+int GameplayManager::GetBonusMissionNumByName( const char* name )
+{
+ int missionNum = GetMissionNumByName( name );
+ return missionNum - MAX_MISSIONS;
+}
+
+//Kills all chasemanagers, call this when level is over
+void GameplayManager::KillAllChaseManagers()
+{
+ //kill kill kill
+ for (int i =0;i<MAX_CHASEMANAGERS;i++)
+ {
+ strcpy(m_ChaseManager_Array[i].hostvehicle,"NULL");
+ delete m_ChaseManager_Array[i].mp_chasemanager;
+ m_ChaseManager_Array[i].mp_chasemanager=NULL;
+ }
+}
+
+//=============================================================================
+// GameplayManager::EnableBonusMissions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::EnableBonusMissions()
+{
+ mUpdateBonusMissions = true;
+
+ int i;
+ for ( i = 0; i < mNumBonusMissions; ++i )
+ {
+ mBonusMissions[ i ].Enable();
+ }
+}
+
+//=============================================================================
+// GameplayManager::DisableBonusMissions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GameplayManager::DisableBonusMissions()
+{
+ mUpdateBonusMissions = false;
+
+ int i;
+ for ( i = 0; i < mNumBonusMissions; ++i )
+ {
+ mBonusMissions[ i ].Disable();
+ }
+}
+
+
+//clears all Chase Cars from the Screen
+void GameplayManager::ClearAllChaseCars ()
+{
+ for (int i =0;i< MAX_CHASE_STRUCTS;i++)
+ {
+ if (m_ChaseManager_Array[i].mp_chasemanager != NULL)
+ {
+ m_ChaseManager_Array[i].mp_chasemanager->ClearAllObjects();
+ }
+ }
+}
+
+//disable all harrass cars
+void GameplayManager::DisableAllChaseAI()
+{
+ for (int i =0;i< MAX_CHASE_STRUCTS;i++)
+ {
+ if (m_ChaseManager_Array[i].mp_chasemanager != NULL)
+ {
+ m_ChaseManager_Array[i].mp_chasemanager->DisableAllActiveVehicleAIs();
+ }
+ }
+}
+
+//enable all harass cars
+void GameplayManager::EnableAllChaseAI()
+{
+ for (int i =0;i< MAX_CHASE_STRUCTS;i++)
+ {
+ if (m_ChaseManager_Array[i].mp_chasemanager != NULL)
+ {
+ m_ChaseManager_Array[i].mp_chasemanager->EnableAllActiveVehicleAIs();
+ }
+ }
+}
+
+GameplayManager::GameTypeEnum GameplayManager::GetGameType()
+{
+ return mGameType;
+}
+
+tColour GameplayManager::GetControllerColour( int id )
+{
+ tColour cColour;
+ switch( id )
+ {
+ case 0:
+ cColour.Set( 255, 2, 2 ); // red
+ break;
+ case 1:
+ cColour.Set( 36, 232, 255 ); //blue
+ break;
+ case 2:
+ cColour.Set( 246, 233, 5 ); // yellow
+ break;
+ case 3:
+ cColour.Set( 33, 209, 14 ); // green
+ break;
+ case 4:
+ cColour.Set( 0, 255, 0 ); // all green
+ break;
+ default:
+ cColour.Set( 0, 0, 0 ); // black
+ break;
+ }
+ return cColour;
+}
+
+
+RespawnManager* GameplayManager::GetRespawnManager()
+{
+ return mpRespawnManager;
+}
+
+
+
+
+void GameplayManager::SetPostLevelFMV( const char* FileName )
+{
+ if( ( FileName == 0 ) || ( strlen( FileName ) == 0 ) )
+ {
+ mPostLevelFMV[ 0 ] = 0;
+ }
+ else
+ {
+ strcpy( mPostLevelFMV, FileName );
+ }
+}
+
+int GameplayManager::AddToVDU(Vehicle* pvehicle)
+{
+ if (mVDU.mCounter < (MAX_VDU_CARS -1))
+ {
+ //we have room add it
+
+ //check if its already in our list we dont want to re-add it
+ for(int i =0;i<MAX_VDU_CARS;i++)
+ {
+ if(mVDU.mpVehicle[i] == pvehicle)
+ {
+ //already in the list return
+ return 0;
+ }
+ }
+
+
+ //if the car isnt in our list then find room for it.
+ for(int i =0;i<MAX_VDU_CARS;i++)
+ {
+ if(mVDU.mpVehicle[i] == NULL)
+ {
+ mVDU.mpVehicle[i]=pvehicle;
+ mVDU.mpVehicle[i]->AddRef();
+ mVDU.mCounter++;
+ return 0;
+ }
+
+ }
+
+ //searched the array no room trouble tell Chuck.
+ rTuneAssert(0);
+ return 0;
+ }
+ else
+ {
+ rTuneAssert(0);
+ return 1;
+
+ }
+}
+
+
+int GameplayManager::UpdateVDU()
+{
+ if (mVDU.mCounter==0)
+ {
+ return 0;
+ }
+ else
+ {
+ rmt::Vector CharacterPositionStart;
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter()->GetPosition(CharacterPositionStart);
+
+ for(int i=0;i<MAX_VDU_CARS;i++)
+ {
+ if (mVDU.mpVehicle[i] != NULL)
+ {
+ rmt::Vector position;
+ rmt::Vector CarPositionDifference;
+
+ mVDU.mpVehicle[i]->GetPosition(&position);
+ mVDU.mpVehicle[i]->GetPosition(&CarPositionDifference);
+
+ CarPositionDifference.Sub(CharacterPositionStart);
+
+ //dont care about the y vaule since we are going to just do a top down radial check
+ CarPositionDifference.y =0;
+ //chuck: remove the car if it is out of the players view and greater than 5.0 meters from player
+ if ((spInstance->TestPosInFrustrumOfPlayer(position,0) == false) && (rmt::Sqrt(CarPositionDifference.MagnitudeSqr()) > 5.0f))
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList(mVDU.mpVehicle[i]);
+
+ if( mVDU.mpVehicle[i]->mVehicleID == VehicleEnum::HUSKA )
+ {
+ GetVehicleCentral()->mHuskPool.FreeHusk(mVDU.mpVehicle[i]);
+ }
+ mVDU.mpVehicle[i]->Release();
+ mVDU.mpVehicle[i]=NULL;
+ mVDU.mCounter--;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+int GameplayManager::MDKVDU()
+{
+ for(int i=0;i<MAX_VDU_CARS;i++)
+ {
+ if (mVDU.mpVehicle[i] != NULL)
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList(mVDU.mpVehicle[i]);
+ if( mVDU.mpVehicle[i]->mVehicleID == VehicleEnum::HUSKA )
+ {
+ GetVehicleCentral()->mHuskPool.FreeHusk(mVDU.mpVehicle[i]);
+ }
+ mVDU.mpVehicle[i]->Release();
+ mVDU.mpVehicle[i]=NULL;
+ mVDU.mCounter--;
+ }
+ }
+ return 0;
+}
+
+void GameplayManager::ReleaseFromVDU(char* carname, Vehicle** outVehicle)
+{
+ bool found = false;
+
+ for (int i=0;i<MAX_VDU_CARS;i++)
+ {
+ if(mVDU.mpVehicle[i] != NULL)
+ {
+ if (strcmp(mVDU.mpVehicle[i]->GetName(),carname)== 0)
+ {
+ //check for cars that have the same name
+ if (found == true)
+ {
+ //problems there were are 2 or more cars in this list with the same name can't decide which one to return;
+ rAssert(0);
+ }
+ else
+ {
+
+ if(outVehicle)
+ {
+ tRefCounted::Assign(*outVehicle, mVDU.mpVehicle[i]);
+ }
+ mVDU.mCounter--;
+ mVDU.mpVehicle[i]->Release(); //undo our add ref.
+ found = true;
+ mVDU.mpVehicle[i]=NULL;
+ }
+ }//end if matching name loop
+
+ }//end if NULL check
+ }//end of for loop
+}
+
+
+
+
+//=============================================================================
+// GameplayManager::TestForContinuityErrorWithCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* v, bool aiSlot )
+//
+// Return: bool
+//
+//=============================================================================
+bool GameplayManager::TestForContinuityErrorWithCar( Vehicle* v, bool aiSlot )
+{
+ bool continuityError = false;
+
+ Vehicle* vehicle = NULL;
+ if ( aiSlot )
+ {
+ vehicle = GetVehicleInSlot( eAICar );
+ }
+ else
+ {
+ vehicle = GetCurrentVehicle();
+ }
+
+ unsigned int i;
+ for ( i = 0; vehicle && i < NUM_CONTINUITY_ERRORS; ++i )
+ {
+ if ( GetCurrentLevelIndex() != CONTINUITY_ERRORS[ i ].mLevel ||
+ GetCurrentMissionIndex() != CONTINUITY_ERRORS[ i ].mMission )
+ {
+ continue;
+ }
+
+ if ( strcmp( CONTINUITY_ERRORS[ i ].mPlayerCarName, v->mName ) == 0 )
+ {
+ if ( strcmp( vehicle->mName, CONTINUITY_ERRORS[ i ].mOtherCarName ) == 0 )
+ {
+ continuityError = true;
+ break;
+ }
+ }
+ }
+
+ return continuityError;
+}
+
+int GameplayManager::RemoveVehicleFromMissionVehicleSlots(Vehicle* pVehicle)
+{
+ for (int i=0;i< MAX_MISSION_VEHICLE_SLOTS; i++)
+ {
+ //match the vehicle to be removed
+ if (mMissionVehicleSlots[i].vehicle == pVehicle)
+ {
+ if(mMissionVehicleSlots[i].usingHusk)
+ {
+ spInstance->AddToVDU(mMissionVehicleSlots[i].pHuskVehicle);
+ mMissionVehicleSlots[i].pHuskVehicle->Release();
+ mMissionVehicleSlots[i].pHuskVehicle = NULL;
+ mMissionVehicleSlots[i].usingHusk = false;
+ }
+ else
+ {
+ spInstance->AddToVDU(mMissionVehicleSlots[i].vehicle);
+ }
+
+ GetEventManager()->TriggerEvent(EVENT_MISSION_VEHICLE_RELEASED,mMissionVehicleSlots[i].vehicle);
+
+ //update the AI vehicle slot so It points to the correct vehicle.
+ if(strcmp(GetGameplayManager()->GetVehicleSlotVehicleName(GameplayManager::eAICar),"NULL") != 0 && GetGameplayManager()->mVehicleSlots[GameplayManager::eAICar].mp_vehicle != NULL )
+ {
+ //update the Gameplaymanagers AICar slot mp_vehicle ptr.
+ if(strcmp(mMissionVehicleSlots[i].vehicle->GetName(),GetGameplayManager()->GetVehicleSlotVehicleName(GameplayManager::eAICar)) ==0)
+ {
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eAICar].mp_vehicle = NULL;
+ }
+ }
+
+ mMissionVehicleSlots[i].vehicle->Release();
+ mMissionVehicleSlots[i].vehicle = 0;
+ mMissionVehicleSlots[i].name[0] = 0;
+ return 0;
+ }//end of if loop
+
+ }//end of for loop
+ return -1;
+}
+
+void GameplayManager::AbortFade()
+{
+ mFadedToBlack = false;
+ mBlackScreenTimer = 0;
+
+ //
+ // Go to the HUD screen and turn off the bloody fade
+ //
+ CGuiScreenHud* hud = GetCurrentHud();
+ hud->AbortFade();
+
+}
+
+
+//******************************************************************************
+//
+// E3-Specific Member Functions
+//
+//******************************************************************************
+
+#ifdef RAD_DEMO
+
+void
+GameplayManager::ResetIdleTime()
+{
+ m_elapsedIdleTime = 0;
+}
+
+void
+GameplayManager::UpdateIdleTime( unsigned int elapsedTime )
+{
+ m_elapsedIdleTime += elapsedTime;
+
+ const unsigned int MAX_IDLE_TIME = 3 * 60000; // in msec
+ if( m_elapsedIdleTime > MAX_IDLE_TIME )
+ {
+ mLevelComplete = true; // this is how we quit out of gameplay automatically
+
+ // make sure we don't try to switch context when another
+ // switch is already pending
+ //
+ if( GetGameFlow()->GetCurrentContext() == GetGameFlow()->GetNextContext() )
+ {
+ GetGameFlow()->SetContext( CONTEXT_PAUSE );
+ }
+ }
+}
+
+#endif // RAD_DEMO
diff --git a/game/code/mission/gameplaymanager.h b/game/code/mission/gameplaymanager.h
new file mode 100644
index 0000000..8302e3b
--- /dev/null
+++ b/game/code/mission/gameplaymanager.h
@@ -0,0 +1,840 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: gameplaymgr.h
+//
+// Description: Blahblahblah
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef GAMEPLAYMGR_H
+#define GAMEPLAYMGR_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <constants/maxplayers.h>
+#include <events/eventlistener.h>
+#include <loading/loadingmanager.h>
+#include <memory/srrmemory.h>
+#include <render/enums/renderenums.h>
+#include <mission/mission.h>
+#include <mission/bonusmissioninfo.h>
+#include <mission/respawnmanager/respawnmanager.h>
+#include <presentation/presevents/presentationevent.h>
+//========================================
+// Forward References
+//========================================
+
+class RespawnManager;
+class ChaseManager;
+class Character;
+class Vehicle;
+class CarStartLocator;
+
+//========================================
+//Constants
+//========================================
+
+
+
+//=============================================================================
+//
+// Synopsis: Base class for all gameplay types (e.g. H2H or Missions)
+//
+//=============================================================================
+
+
+class GameplayManager : public EventListener,
+ public PresentationEvent::PresentationEventCallBack
+{
+public:
+ static GameplayManager* GetInstance();
+ static void SetInstance( GameplayManager* pInstance );
+
+ static const int MAX_MISSIONS = 20;
+ static const int MAX_BONUS_MISSIONS = 10;
+ static const int MAX_LEVELS = 8;
+ static const int MAX_CHASEMANAGERS = 1;
+ static const int MAX_VDU_CARS = 10;
+
+ // DEMO MODE YA!
+ bool mIsDemo;
+
+ enum GameTypeEnum
+ {
+ GT_NORMAL,
+ GT_SUPERSPRINT,
+ NUM_GAMETYPES
+ };
+
+
+ // Guess what? No matter your rating, you suck!
+ //
+ enum RatingEnum
+ {
+ RATING_DNF,
+ RATING_BRONZE,
+ RATING_SILVER,
+ RATING_GOLD,
+ NUM_RATINGS
+ };
+
+
+ struct ChaseManagerStruct
+ {
+ ChaseManager* mp_chasemanager;
+ char hostvehicle [16];
+ char hostvehiclefilename[64];
+ };
+
+
+ //Chuck: use this to record the starting location of a player and Forced car prior
+ //to player start the forced car mission, we save this so we can place player in safe
+ //spot on mission cancel from Pause menu, so car is spawn on top of them.
+ struct PlayerAndCarInfo
+ {
+ rmt::Vector mPlayerPosition;
+ rmt::Vector mForceLocation;
+ bool mbDirtyFlag; //we use this to signal that we just started a forced car mission and any retries and restarts do record the character position again
+ };
+
+
+ PlayerAndCarInfo mPlayerAndCarInfo;
+
+
+
+
+ struct VDUStruct
+ {
+ Vehicle* mpVehicle[MAX_VDU_CARS];
+ int mCounter;
+ };
+
+ // Data about the currently loaded level.
+ //
+ struct LevelDataEnum
+ {
+ LevelDataEnum() : level( (RenderEnums::LevelEnum)(RenderEnums::numLevels - 1) ), numMissions( 0 ), numBonusCollectibles( 0 ), numBonusCollected( 0 ), mission( (RenderEnums::MissionEnum)(RenderEnums::numMissions - 1) ) {};
+ RenderEnums::LevelEnum level;
+
+ // missions in this level
+ int numMissions;
+
+ // The single player rating of each mission
+ RatingEnum rating[ MAX_MISSIONS ];
+
+ // Number of bonus collectibles available in this level
+ int numBonusCollectibles;
+ // Number the single player has collected
+ int numBonusCollected;
+
+ RenderEnums::MissionEnum mission;
+ };
+
+ /////////////////////////////////////////////////
+ // Public accessor type stuff
+ /////////////////////////////////////////////////
+
+ int GetNumPlayers();
+
+ //methods for disabling phonebooths since there are sometime
+ //like get into vehicle objective that we dont want user switching cars or in forced car missions
+ void EnablePhoneBooths ();
+ void DisablePhoneBooths ();
+ bool QueryPhoneBoothsEnabled ();
+
+
+ //getting access to the ChaseManagers
+
+ //gets ChaseManager quering by the hostvehicle name
+ ChaseManager* GetChaseManager( char* hostvehicle);
+ ChaseManager* GetChaseManager(int index);
+
+ //creates a chase manager, returns -1 if function fails
+ int CreateChaseManager ( char* hostvehicle,char* confile, int spawnrate);
+
+ //method to clear all chase cars that are active.
+ void ClearAllChaseCars ( );
+
+ //Kills the chase managers
+ void KillAllChaseManagers ( );
+
+ //Get the RespawnManager for wrenches and nitro etc.
+ RespawnManager* GetRespawnManager();
+
+
+ // the heap the current mission data uses
+ GameMemoryAllocator GetCurrentMissionHeap();
+
+ GameMemoryAllocator GetMissionHeap( int index );
+
+ int GetNumMissions();
+ int GetNumBonusMissions();
+
+ Mission* GetMission( int index );
+
+ virtual Mission* GetCurrentMission();
+ const BonusMissionInfo* GetCurrentBonusMissionInfo() const;
+ BonusMissionInfo* GetBonusMissionInfo( int missionNumber );
+ int GetCurrentMissionIndex();
+ int GetCurrentMissionNum() const;
+
+ virtual void CleanMissionData() = 0;
+
+ RenderEnums::LevelEnum GetCurrentLevelIndex();
+ RenderEnums::MissionEnum GetCurrentMissionForDemo();
+
+ virtual bool IsSundayDrive();
+ virtual bool IsSuperSprint();
+
+ bool TestPosInFrustrumOfPlayer( const rmt::Vector& pos, int playerID, float radius = 3.0f );
+
+ //
+ // Tells the manager that presentation stuff is done and it
+ // should move on with gameplay
+ //
+ void ContinueGameplay();
+
+ /////////////////////////////////////////////////
+ // Load the next level
+ // Use this once and only once for a gameplay session
+ // The name is irrevelevant when the gameplay is head to head
+ /////////////////////////////////////////////////
+
+ virtual void SetLevelIndex( RenderEnums::LevelEnum level );
+ virtual void SetMissionIndex( RenderEnums::MissionEnum mission );
+
+
+ /////////////////////////////////////////////////
+ // MS9: Temporarily set the from the main menu
+ // -1 is the mission car, 0->n overrides
+ //
+ //int mVehicleIndex;
+ //void SetVehicleIndex( int vehicleId ) { mVehicleIndex = vehicleId; }
+ //int GetVehicleIndex() { return mVehicleIndex; }
+ //Vehicle* InitVehicle( char* vehicleName );
+ //void GetOverrideVehicleName( char* name );
+
+ int mCharacterIndex;
+ void SetCharacterIndex( int characterId ) { mCharacterIndex = characterId; }
+ int GetCharacterIndex() { return mCharacterIndex; }
+ void GetOverrideCharacterName( char* name );
+ /////////////////////////////////////////////////
+
+ /////////////////////////////////////////////////
+ // tells the GPM to load a file and read a script defining which
+ // files need to be loaded for this level
+ virtual void LoadLevelData() = 0;
+ virtual void InitLevelData() = 0;
+
+ /////////////////////////////////////////////////
+ // Service the GPM loading process for missions
+ //
+ virtual void PerformLoading() = 0;
+
+ /////////////////////////////////////////////////
+ // Allows you to set the position of a vehicle
+ // TO DO: Extend this to place ANY DSG object
+ /////////////////////////////////////////////////
+
+ void PlaceCharacterAtLocator( Character* character, Locator* locator );
+
+ void PlaceVehicleAtLocator( Vehicle* vehicle, CarStartLocator* locator );
+ void PlaceVehicleAtLocation( Vehicle* vehicle, rmt::Vector pos, float rotation );
+ void PlaceVehicleAtLocatorName( Vehicle* vehicle, const char* locatorName );
+
+ /////////////////////////////////////////////////
+ // Flow stuff, don't use this
+ /////////////////////////////////////////////////
+
+ // call this when all the other level data is done loading
+ // and the gameplay manager will load the mission scripts
+ virtual void LevelLoaded();
+
+ // must be called to set shit up
+ virtual void Initialize();
+ // call this before destroying the GPM
+ virtual void Finalize();
+ // call this to start gameplay again
+ virtual void Reset() = 0;
+
+ // call Update every frame
+ virtual void Update( int elapsedTime );
+
+ void SetNumMissions( int num );
+ void SetNumBonusMissions( int num );
+
+ void SetMission( int index, Mission* mission );
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ virtual void RestartCurrentMission();
+ virtual void RestartToMission( RenderEnums::MissionEnum mission );
+ virtual void AbortCurrentMission();
+
+ // SetBonusMissionInfo
+ // Optional parameter isCompleted - bonus missions can be completed but still
+ // playable, but the animated icons they use are the alternative.
+ void SetBonusMissionInfo( const char* NPCname, const char* missionName, const char* iconName, const char* dialogueName, bool isOneShot, const char* alternateIconName = NULL, bool wasCompletedAlready = false );
+ int GetMissionNumByName( const char* name );
+ int GetBonusMissionNumByName( const char* name );
+
+ void EnableBonusMissions();
+ void DisableBonusMissions();
+ void DisableAllChaseAI();
+ void EnableAllChaseAI();
+
+ bool IsBonusMissionDesired() const;
+ void CancelBonusMission();
+
+ // return true when the last mission has been completed
+ bool GetLevelComplete();
+ bool GetGameComplete( void ) const { return mGameComplete; }
+ //char mDefaultVehicle [32];
+
+ enum eCarSlots{ eDefaultCar,eOtherCar,eAICar};
+
+
+ /////////////////////////////////////////////////
+ // Adds a level vehicle (a vehicle that is always present)
+ //
+ Vehicle* AddLevelVehicle( char* vehicleName,eCarSlots slot, char* confile);
+
+ //returns a pointer to vehicle, query by name
+ //Vehicle* GetVehicle(char* name);
+
+ //returns a pointer to the car that the PC last used/is using
+ Vehicle* GetCurrentVehicle(void);
+
+ //sets the mCurrentVehicle ptr.
+ void SetCurrentVehicle(Vehicle* vehicle);
+ void UnregisterVehicleHUDIcon(void);
+
+ //Removes the current car from the inventory and releases the vehicle
+ void DumpCurrentCar();
+ void DumpCurrentCarIfInSphere( const rmt::Sphere& s );
+
+ void ClearVehicleSlot(eCarSlots slot);
+ void ClearVehicleSlotIfInSphere( eCarSlots slot, const rmt::Sphere& s );
+ // Dumps the car in the source slot, then copies the vehicle
+ // in the source slot into the dest slot
+ // Used by LoadDisposableCarAsync
+ void CopyVehicleSlot( eCarSlots sourceSlot, eCarSlots destSlot );
+ char* GetVehicleSlotFilename(eCarSlots slot);
+ char* GetVehicleSlotVehicleName(eCarSlots slot);
+ Vehicle* GetVehicleInSlot(eCarSlots slot);
+
+ // I guess we will need to call this afterall, in between each mission
+ // call at the start of a new mission...
+ void EmptyMissionVehicleSlots();
+
+ int RemoveVehicleFromMissionVehicleSlots(Vehicle* pVehicle);
+
+ struct CarDataStruct
+ {
+ char filename [64];
+ char name [32];
+ rmt::Vector position; //used to store the car position for unloading & reloading
+ float heading; //also used to store cars facing for unloading & reloading
+
+ Vehicle* mp_vehicle;
+
+ Vehicle* pHuskVehicle;
+ bool usingHusk;
+ };
+
+ static const int MAX_VEHICLE_SLOTS = 3;
+
+ CarDataStruct mVehicleSlots [MAX_VEHICLE_SLOTS];
+ char mDefaultLevelVehicleName[64];
+ char mDefaultLevelVehicleLocator[64];
+ char mDefaultLevelVehicleConfile[64];
+ bool mShouldLoadDefaultVehicle;
+
+ // TODO greg - we don't actually need the AI slot in CarDataStruct
+
+
+ // new
+ // greg
+ // jan 7, 2003
+
+ // GameplayManager is now going to be the owner of "mission" cars.
+
+ static const int MAX_MISSION_VEHICLE_SLOTS = 5; // this has to have room for the "AI-heavyweight" car
+
+ struct MissionCarDataStruct
+ {
+ Vehicle* vehicle;
+ char name[32]; // for convenience
+ //int vehicleCentralIndex;
+
+ Vehicle* pHuskVehicle;
+ bool usingHusk;
+ };
+
+ MissionCarDataStruct mMissionVehicleSlots[MAX_MISSION_VEHICLE_SLOTS];
+
+ Vehicle* AddMissionVehicle(char* vehiclename, char* confile = 0, char* driver = 0);
+
+ // called from Mission::Reset
+ void MakeSureHusksAreReverted(Vehicle* pvehicle = NULL );
+
+ Vehicle* GetMissionVehicleByName( const char* name );
+ Vehicle* GetUserVehicleByName(const char* name);
+ int GetMissionVehicleIndex(Vehicle* vehicle);
+
+ GameTypeEnum GetGameType();
+
+ tColour GetControllerColour( int id );
+
+ void PauseForIrisClose( float speedMod );
+ void PauseForIrisOpen( float speedMod = 0.0f );
+ void PauseForFadeToBlack( float speedMod );
+ void PauseForFadeFromBlack( float speedMod = 0.0f );
+
+ bool IsIrisClosed() { return mIrisClosed || mFadedToBlack; };
+
+ void PutPlayerInCar( bool should ) { mPutPlayerInCar = should; };
+ bool ShouldPutPlayerInCar() const { return mPutPlayerInCar; };
+
+
+
+
+ //Chuck Moving this to public, bad design :-s but faster for missionscriptloader.
+ ChaseManagerStruct m_ChaseManager_Array [ MAX_CHASEMANAGERS ];
+
+ void SetPostLevelFMV( const char* FileName );
+ const char* GetPostLevelFMV( void ) const { return mPostLevelFMV; }
+
+ // advances sequentially to the next mission
+ void NextMission();
+ void PrevMission();
+
+ void ResetIdleTime();
+ void UpdateIdleTime( unsigned int elapsedTime );
+
+ //Add cars to the Vehicle Disposal Unit for removal ,this prevents visual popping.
+ int AddToVDU(Vehicle* pvehicle);
+
+ //Removes a Car slated for disposal, pass in a car name if the car is present it returns a ptr to that car else a NULL PTR
+ void ReleaseFromVDU(char* carname, Vehicle**);
+
+ //explicity removing cars that we are hangin on too, this should be called only in Finalize
+ int MDKVDU();
+ bool FadeInProgress() const
+ {
+ return mBlackScreenTimer > 0;
+ }
+ void AbortFade();
+ void ManualControlFade(bool flag)
+ {
+ mbManualControlFade = flag;
+ };
+
+ bool QueryManualControlFade()
+ {
+ return mbManualControlFade;
+ };
+
+ inline void SetLevelComplete()
+ {
+ mLevelComplete =true;
+ }
+
+ inline void SetGameComplete(void) { mGameComplete = true; }
+
+ bool TestForContinuityErrorWithCar( Vehicle* v, bool aiSlot ); //If you are testing the ai slot you must be loading from phone. Otherwise it will test the current car.
+
+ int mBlackScreenTimer;
+
+ //Try to break up the flow to allow for presentation.
+ enum Message
+ {
+ NONE,
+ NEXT_MISSION,
+ PREV_MISSION
+ };
+
+ inline Message GetCurrentMessage() const { return mCurrentMessage; }
+
+protected:
+ GameplayManager();
+ ~GameplayManager();
+
+ virtual void LoadMission() = 0;
+
+ virtual void OnPresentationEventBegin( PresentationEvent* pEvent ) {};
+ virtual void OnPresentationEventLoadComplete( PresentationEvent* pEvent ) {};
+ virtual void OnPresentationEventEnd( PresentationEvent* pEvent ) { mWaitingOnFMV = false; }
+
+ void SetCurrentMission( int index );
+
+ void RepairVehicle( CarDataStruct* carData );
+
+ //
+ // Only use this from SetLevelIndex in a subclass
+ //
+ void SetCurrentLevelIndex( RenderEnums::LevelEnum level );
+
+ void SetNumPlayers( int numPlayers );
+
+ char mSkipSunday;
+
+
+ //Triage hack, only for demo mode. --dm 12/01/02
+ void RemoveLevelVehicleController();
+ int mAIIndex;
+
+ GameTypeEnum mGameType;
+
+ char mPostLevelFMV[ 13 ]; // Force 8.3 naming.
+ bool mIrisClosed : 1;
+ bool mFadedToBlack : 1;
+ bool mWaitingOnFMV : 1;
+
+private:
+ GameplayManager( const GameplayManager& gameplayManager );
+ GameplayManager& operator=( const GameplayManager& gameplayManager );
+
+ // Pointer to the one and only instance of this singleton.
+ static GameplayManager* spInstance;
+
+ int mCurrentMission;
+
+ int mNumPlayers;
+
+ LevelDataEnum mLevelData;
+
+ int mNumMissions;
+ Mission* mMissions[ MAX_MISSIONS + MAX_BONUS_MISSIONS ];
+
+ GameMemoryAllocator mCurrentMissionHeap;
+
+ bool mLevelComplete : 1;
+ bool mEnablePhoneBooths : 1;
+ bool mGameComplete : 1;
+
+
+
+ //static const int MAX_LEVEL_VEHICLES = 2;
+ //Vehicle* mLevelVehicles[ MAX_LEVEL_VEHICLES ];
+ //int miNumLevelVehicles;
+
+ Vehicle* mCurrentVehicle;
+
+
+ //let the VDU release missions cars that arent in the players view, to avoid popping
+ int UpdateVDU();
+
+ VDUStruct mVDU;
+
+
+
+
+ //Bonus mission stuff.
+ int mNumBonusMissions;
+ int mCurrentBonusMission;
+ int mDesiredBonusMission;
+ bool mIsInBonusMission;
+ bool mFireBonusMissionDialogue;
+ bool mJumpToBonusMission;
+ bool mUpdateBonusMissions;
+
+ BonusMissionInfo mBonusMissions[ MAX_BONUS_MISSIONS ];
+
+ Message mCurrentMessage;
+
+ void DoNextMission();
+ void DoPrevMission();
+
+ //Chuck: Respawn ManagerStuff
+ RespawnManager* mpRespawnManager;
+
+ float mIrisSpeed;
+
+ //This is a crappy way to force the player back in their car after a forced car mission.
+ bool mPutPlayerInCar;
+
+ //flag used to control the fader, used by the mission stage call back
+ //turn off once the loading is done
+ bool mbManualControlFade;
+
+ int mCurrentVehicleIconID;
+
+ bool TestProximityToUsersCarAndNudgeUpIfNecessaryDamnUglyHack(rmt::Vector& pos, Vehicle* playersCar);
+
+ unsigned int m_elapsedIdleTime;
+
+};
+
+// A little syntactic sugar for getting at this pseudo-singleton.
+inline GameplayManager* GetGameplayManager() { return( GameplayManager::GetInstance() ); }
+inline void SetGameplayManager( GameplayManager* pInstance ) { GameplayManager::SetInstance( pInstance ); }
+
+//=============================================================================
+// GameplayManager::GetNumPlayers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+inline int GameplayManager::GetNumPlayers()
+{
+ return( mNumPlayers );
+}
+
+//=============================================================================
+// GameplayManager::GetCurrentMissionHeap
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: GameMemoryAllocator
+//
+//=============================================================================
+inline GameMemoryAllocator GameplayManager::GetCurrentMissionHeap()
+{
+ return( mCurrentMissionHeap );
+}
+
+//=============================================================================
+// GameplayManager::GetNumMissions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+inline int GameplayManager::GetNumMissions()
+{
+ return( mNumMissions );
+}
+
+//=============================================================================
+// GameplayManager::GetNumBonusMissions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+inline int GameplayManager::GetNumBonusMissions()
+{
+ return( mNumBonusMissions );
+}
+
+//=============================================================================
+// GameplayManager::GetMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int index )
+//
+// Return: Mission
+//
+//=============================================================================
+inline Mission* GameplayManager::GetMission( int index )
+{
+ if ( (index >= 0) && (index < (MAX_MISSIONS + MAX_BONUS_MISSIONS)))
+ {
+ return( mMissions[ index ] );
+ }
+ else
+ {
+ rTuneAssertMsg(0,"Illegal array access in GameplayManager::GetMission()\n");
+ return NULL;
+ }
+
+}
+
+//=============================================================================
+// GameplayManager::GetCurrentLevelIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: RenderEnums
+//
+//=============================================================================
+inline RenderEnums::LevelEnum GameplayManager::GetCurrentLevelIndex()
+{
+ if ( mLevelData.level >= RenderEnums::numLevels )
+ {
+ //This is a bonus game.
+ return (RenderEnums::LevelEnum)(mLevelData.level - RenderEnums::numLevels);
+ }
+
+ return( mLevelData.level);
+}
+
+//=============================================================================
+// GameplayManager::SetNumMissions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int num )
+//
+// Return: void
+//
+//=============================================================================
+inline void GameplayManager::SetNumMissions( int num )
+{
+ mNumMissions = num;
+}
+
+//=============================================================================
+// GameplayManager::SetNumBonusMissions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int num )
+//
+// Return: inline
+//
+//=============================================================================
+inline void GameplayManager::SetNumBonusMissions( int num )
+{
+ mNumBonusMissions = num;
+}
+
+//=============================================================================
+// GameplayManager::SetMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int index, Mission* mission )
+//
+// Return: void
+//
+//=============================================================================
+inline void GameplayManager::SetMission( int index, Mission* mission )
+{
+ mMissions[ index ] = mission;
+}
+
+//=============================================================================
+// GameplayManager::GetLevelComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool GameplayManager::GetLevelComplete()
+{
+ return( mLevelComplete );
+}
+
+//=============================================================================
+// GameplayManager::SetNumPlayers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int numPlayers )
+//
+// Return: void
+//
+//=============================================================================
+inline void GameplayManager::SetNumPlayers( int numPlayers )
+{
+ mNumPlayers = numPlayers;
+}
+
+
+//=============================================================================
+// GameplayManager::GetCurrentMissionIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+inline int GameplayManager::GetCurrentMissionIndex()
+{
+ return( ( mCurrentMission - ( mCurrentMission & 0x00000001 ) ) ) >> 1; //Heh...
+}
+
+//=============================================================================
+// GameplayManager::SetCurrentLevelIndex
+//=============================================================================
+// Description: Only use this in the subclass implementation of SetLevelIndex
+//
+// Parameters: ( RenderEnums::LevelEnum level )
+//
+// Return: void
+//
+//=============================================================================
+inline void GameplayManager::SetCurrentLevelIndex( RenderEnums::LevelEnum level )
+{
+ mLevelData.level = level;
+}
+
+//=============================================================================
+// GameplayManager::GetCurrentMissionForDemo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: RenderEnums
+//
+//=============================================================================
+inline RenderEnums::MissionEnum GameplayManager::GetCurrentMissionForDemo()
+{
+ return mLevelData.mission;
+}
+
+//=============================================================================
+// GameplayManager::IsSundayDrive
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool GameplayManager::IsSundayDrive()
+{
+ bool isSunday = false;
+
+ if ( mCurrentMission == 0 )
+ {
+ isSunday = true;
+ }
+
+ return false;
+}
+
+//=============================================================================
+// GameplayManager::IsSuperSprint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline bool GameplayManager::IsSuperSprint()
+{
+ return false;
+}
+
+#endif //GAMEPLAYMGR_H
diff --git a/game/code/mission/haspresentationinfo.cpp b/game/code/mission/haspresentationinfo.cpp
new file mode 100644
index 0000000..9def2f0
--- /dev/null
+++ b/game/code/mission/haspresentationinfo.cpp
@@ -0,0 +1,320 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: haspresentationinfo.cpp
+//
+// Description: Implement haspresentationinfo class
+//
+// History: 06/05/2003 + Created -- Ian Gipson
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <camera/conversationcam.h>
+#include <events/eventmanager.h>
+#include <memory/classsizetracker.h>
+#include <mission/haspresentationinfo.h>
+#include <presentation/presentation.h>
+#include <presentation/presentationanimator.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+#define TYPICAL_NUM_ANIMATIONS 16
+#define TYPICAL_NUM_LINES 8
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// HasPresentationInfo::HasPresentationInfo
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+HasPresentationInfo::HasPresentationInfo():
+ mConversationCamName ( "unknown" ),
+ mConversationCamNpcName( "npc_near" ),
+ mConversationCamPcName ( "pc_near" ),
+ mPcIsChild( false ),
+ mNpcIsChild( false ),
+ mGoToPattyAndSelmaScreenWhenDone( false )
+{
+ CLASSTRACKER_CREATE( HasPresentationInfo );
+ mAmbientPcAnimations.reserve( TYPICAL_NUM_ANIMATIONS );
+ mAmbientNpcAnimations.reserve( TYPICAL_NUM_ANIMATIONS );
+ mCamerasForLinesOfDialog.reserve( TYPICAL_NUM_LINES );
+}
+
+
+//=============================================================================
+// HasPresentationInfo::HasPresentationInfo
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+HasPresentationInfo::~HasPresentationInfo()
+{
+ CLASSTRACKER_DESTROY( HasPresentationInfo );
+}
+
+//=============================================================================
+// HasPresentationInfo::AddAmbientCharacterAnimation
+//=============================================================================
+// Description: adds an animation name to the list that will be randomly chosen
+// for a specific character in the conversation
+// Parameters: character 0 = PC
+// 1 = NPC
+// animationName - the tName representing the animation
+//
+// Return: void
+//
+//=============================================================================
+void HasPresentationInfo::AddAmbientCharacterAnimation( const unsigned int character, const tName& animationName )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ rAssertMsg( character < 2, "There is only code for the PC and 1 NPC" );
+ const unsigned int index = character % 2;
+ if( index == 0 )
+ {
+ mAmbientPcAnimations.push_back( animationName );
+ }
+ else
+ {
+ mAmbientNpcAnimations.push_back( animationName );
+ }
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+}
+
+//=============================================================================
+// HasPresentationInfo::AmbientCharacterAnimationSetRandom
+//=============================================================================
+// Description: determines whether or not to randomize animations
+// Parameters: character 0 = PC
+// 1 = NPC
+// random - should animation selection be random or not?
+//
+// Return: void
+//
+//=============================================================================
+void HasPresentationInfo::AmbientCharacterAnimationSetRandom( const unsigned int character, const bool random )
+{
+ if( character == 0 )
+ {
+ mAmbientPcAnimationsRandom = random;
+ }
+ else
+ {
+ mAmbientNpcAnimationsRandom = random;
+ }
+}
+
+//=============================================================================
+// HasPresentationInfo::CharacterIsChild
+//=============================================================================
+// Description: marks a specific character as being a child
+// Parameters: index - which character
+// 0 = PC
+// 1 = NPC
+//
+// Return: void
+//
+//=============================================================================
+void HasPresentationInfo::CharacterIsChild( const int index )
+{
+ if( index == 0 )
+ {
+ mPcIsChild = true;
+ }
+ else if( index == 1 )
+ {
+ mNpcIsChild = true;
+ }
+ else
+ {
+ rAssertMsg( false, "character index out of range" );
+ }
+}
+
+//=============================================================================
+// HasPresentationInfo::ClearAmbientAnimations
+//=============================================================================
+// Description: clears out all the ambient animations that have been set up
+//
+// Parameters:
+//
+// Return: void
+//
+//=============================================================================
+void HasPresentationInfo::ClearAmbientAnimations()
+{
+ mAmbientPcAnimations.erase( mAmbientPcAnimations.begin(), mAmbientPcAnimations.end() );
+ mAmbientNpcAnimations.erase( mAmbientNpcAnimations.begin(), mAmbientNpcAnimations.end() );
+}
+
+//=============================================================================
+// HasPresentationInfo::GoToPattyAndSelmaScreenWhenDone
+//=============================================================================
+// Description: if this is set, the race or mission will go to the patty and
+// selma screen when it is completed
+//
+// Parameters:
+//
+// Return: void
+//
+//=============================================================================
+void HasPresentationInfo::GoToPattyAndSelmaScreenWhenDone()
+{
+ mGoToPattyAndSelmaScreenWhenDone = true;
+}
+
+//=============================================================================
+// HasPresentationInfo::OnStageCompleteSuccessful
+//=============================================================================
+// Description: called when the mission is complete
+//
+// Parameters:
+//
+// Return: void
+//
+//=============================================================================
+void HasPresentationInfo::OnStageCompleteSuccessful() const
+{
+ if( mGoToPattyAndSelmaScreenWhenDone )
+ {
+ GetEventManager()->TriggerEvent( EVENT_GUI_TRIGGER_PATTY_AND_SELMA_SCREEN );
+ }
+}
+
+//=============================================================================
+// HasPresentationInfo::Reset
+//=============================================================================
+// Description: called to activate all the info stored in this class
+//
+// Parameters:
+//
+// Return: void
+//
+//=============================================================================
+void HasPresentationInfo::Reset()
+{
+ PresentationManager* pm = PresentationManager::GetInstance();
+
+ //
+ // Set Up the Conversation cameras
+ //
+ ConversationCam::SetNpcIsChild( mNpcIsChild );
+ ConversationCam::SetPcIsChild( mPcIsChild );
+ ConversationCam::SetNpcCameraByName( mConversationCamNpcName );
+ ConversationCam::SetPcCameraByName( mConversationCamPcName );
+ size_t size = mCamerasForLinesOfDialog.size();
+ rAssert( size < 10 );
+ pm->SetCamerasForLineOfDialog( mCamerasForLinesOfDialog );
+ ConversationCam::SetCamBestSide( mBestSideLocator );
+
+ //
+ // Set up the Ambient Animations
+ PresentationAnimator* paPc = pm->GetAnimatorPc();
+ PresentationAnimator* paNpc = pm->GetAnimatorNpc();
+ size = mAmbientPcAnimations.size();
+ paPc-> AddAmbientAnimations( mAmbientPcAnimations );
+ paNpc->AddAmbientAnimations( mAmbientNpcAnimations );
+ paPc->SetRandomSelection( mAmbientPcAnimationsRandom );
+ paNpc->SetRandomSelection( mAmbientNpcAnimationsRandom );
+
+}
+
+//=============================================================================
+// HasPresentationInfo::SetCameraForDialogLine
+//=============================================================================
+// Description: some lines of dialog need specific cameras attached to them
+// Parameters: dialogLine - which dialog line do we care about?
+// camera - which camera should we use
+//
+// Return: void
+//
+//=============================================================================
+void HasPresentationInfo::SetCameraForDialogLine( const unsigned int dialogLine, const tName& camera )
+{
+ size_t currentSize = mCamerasForLinesOfDialog.size();
+ if( currentSize < dialogLine + 1 )
+ {
+ mCamerasForLinesOfDialog.resize( dialogLine + 1, tName( "NONE" ) );
+ }
+ mCamerasForLinesOfDialog[ dialogLine ] = camera;
+}
+
+//=============================================================================
+// HasPresentationInfo::SetConversationCamName
+//=============================================================================
+// Description: sets the name of the conversation camera used by default
+// Parameters: name - the tname representing this camera
+//
+// Return: void
+//
+//=============================================================================
+void HasPresentationInfo::SetConversationCamName( const tName& name )
+{
+ mConversationCamName = name;
+}
+//=============================================================================
+// HasPresentationInfo::SetConversationCamPcName
+//=============================================================================
+// Description: sets the name of the conversation camera used when the PC is
+// talking
+// Parameters: name - the tname representing this camera
+//
+// Return: void
+//
+//=============================================================================
+void HasPresentationInfo::SetConversationCamPcName ( const tName& name )
+{
+ mConversationCamPcName = name;
+}
+//=============================================================================
+// HasPresentationInfo::SetConversationCamNpcName
+//=============================================================================
+// Description: sets the name of the conversation camera used when the PC is
+// talking
+// Parameters: name - the tname representing this camera
+//
+// Return: void
+//
+//=============================================================================
+void HasPresentationInfo::SetConversationCamNpcName( const tName& name )
+{
+ mConversationCamNpcName = name;
+}
+
+//=============================================================================
+// HasPresentationInfo::SetBestSideLocator
+//=============================================================================
+// Description: sets the name of the bestsidelocator for this stage
+// Parameters: name - the tname representing this locator
+//
+// Return: void
+//
+//=============================================================================
+void HasPresentationInfo::SetBestSideLocator( const tName& name )
+{
+ mBestSideLocator = name;
+} \ No newline at end of file
diff --git a/game/code/mission/haspresentationinfo.h b/game/code/mission/haspresentationinfo.h
new file mode 100644
index 0000000..89364f1
--- /dev/null
+++ b/game/code/mission/haspresentationinfo.h
@@ -0,0 +1,77 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: haspresentationinfo.h
+//
+// Description: accessors/data members for stages with presentation information
+//
+// History: 06/05/2003 + Created -- Ian Gipson
+//
+//=============================================================================
+
+#ifndef HASPRESENTATIONINFO_H
+#define HASPRESENTATIONINFO_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <memory/stlallocators.h>
+#include <vector>
+
+//========================================
+// Forward References
+//========================================
+class tName;
+
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class HasPresentationInfo
+{
+public:
+ HasPresentationInfo();
+ ~HasPresentationInfo();
+ void AmbientCharacterAnimationSetRandom( const unsigned int character, const bool random );
+ void AddAmbientCharacterAnimation( const unsigned int character, const tName& animationName );
+ void CharacterIsChild( const int index );
+ void ClearAmbientAnimations();
+ void GoToPattyAndSelmaScreenWhenDone();
+ void OnStageCompleteSuccessful() const;
+ void Reset();
+ void SetCameraForDialogLine ( const unsigned int dialogLine, const tName& camera );
+ void SetConversationCamName ( const tName& name );
+ void SetConversationCamPcName ( const tName& name );
+ void SetConversationCamNpcName( const tName& name );
+ void SetBestSideLocator ( const tName& name );
+
+protected:
+private:
+ //Prevent wasteful constructor creation.
+ HasPresentationInfo( const HasPresentationInfo& bonusmissioninfo );
+ HasPresentationInfo& operator=( const HasPresentationInfo& bonusmissioninfo );
+
+ tName mConversationCamName;
+ tName mConversationCamNpcName;
+ tName mConversationCamPcName;
+ typedef std::vector< tName, s2alloc<tName> > TNAMEVECTOR;
+ bool mAmbientPcAnimationsRandom;
+ bool mAmbientNpcAnimationsRandom;
+ TNAMEVECTOR mAmbientPcAnimations;
+ TNAMEVECTOR mAmbientNpcAnimations;
+ bool mPcIsChild : 1;
+ bool mNpcIsChild : 1;
+ bool mGoToPattyAndSelmaScreenWhenDone : 1;
+ TNAMEVECTOR mCamerasForLinesOfDialog;
+ tName mBestSideLocator;
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+#endif //HASPRESENTATIONINFO_H
diff --git a/game/code/mission/mission.cpp b/game/code/mission/mission.cpp
new file mode 100644
index 0000000..870ba7f
--- /dev/null
+++ b/game/code/mission/mission.cpp
@@ -0,0 +1,2001 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: mission.cpp
+//
+// Description: Implement Mission
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radtime.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/gameplaymanager.h>
+#include <mission/mission.h>
+#include <mission/missionstage.h>
+#include <mission/conditions/missioncondition.h>
+#include <mission/objectives/missionobjective.h>
+#include <mission/objectives/dialogueobjective.h>
+#include <mission/bonusobjective.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/harass/chasemanager.h>
+
+#include <worldsim/parkedcars/parkedcarmanager.h>
+
+#include <events/eventmanager.h>
+
+#include <meta/zoneeventlocator.h>
+#include <meta/carstartlocator.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+
+#include <memory/srrmemory.h>
+
+#include <debug/profiler.h>
+
+#include <contexts/context.h>
+#include <gameflow/gameflow.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/boss.h>
+#include <mission/ufo/tractorbeam.h>
+#include <mission/statepropcollectible.h>
+#include <mission/animatedicon.h>
+
+#include <roads/roadmanager.h>
+#include <roads/road.h>
+#include <roads/roadsegment.h>
+#include <roads/intersection.h>
+
+#include <interiors/interiormanager.h>
+#include <stateprop/statepropdata.hpp>
+
+#include <atc/atcmanager.h>
+
+#include <ai/actionbuttonhandler.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+const float MS_PER_SEC = 1000.0f;
+const float SEC_IN_MSEC = 0.001f;
+const int FINAL_DELAY = 3000; //How long to delay changing stages when this one is marked "final"
+const int COMPLETE_DELAY = 0; //How long to delay changing stages when this one shows COMPLETE
+const int MAX_NUM_STATEPROP_COLLECTIBLES = 10;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Mission::Mission
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Mission::Mission() :
+ mIsStreetRace1Or2( false ),
+ mNumMissionStages( 0 ),
+ mCurrentStage( -1 ),
+#ifdef RAD_GAMECUBE
+ mHeap( GMA_GC_VMM ),
+#else
+ mHeap( GMA_LEVEL_MISSION ),
+#endif
+ mbComplete( false ),
+ mbIsLastStage( false ),
+ mMissionTimer( 0 ),
+ mState( STATE_WAITING ),
+ mLastStageState( MissionStage::STAGE_IDLE ),
+ //mNumVehicles( 0 ),
+ mVehicleRestart( NULL ),
+ mPlayerRestart( NULL ),
+ mDynaloadLoc( NULL ),
+ mStreetRacePropsLoad(NULL),
+ mStreetRacePropsUnload(NULL),
+ mResetToStage( 0 ),
+ mSundayDrive( false ),
+ mBonusMisison( false ),
+ mNumBonusObjectives( 0 ),
+ mIsForcedCar( false ),
+ mbAutoRepairCar(false),
+ mbSwappedCars(false),
+ mbTriggerPattyAndSelmaScreen(false),
+ mFinalDelay( -1 ),
+ mCompleteDelay( -1 ),
+ mChangingStages( false ),
+ mNoTimeUpdate(false ),
+ mJumpBackStage( false ),
+ mJumpBackBy( 1 ),
+ mNumStatePropCollectibles( 0 ),
+ mStatePropCollectibles( NULL ),
+ mDoorStars( NULL ),
+ mInitPedGroupId( 0 ),
+ mShowHUD( true ),
+ mNumValidFailureHints( -1 )
+{
+ strcpy( mcName, "" );
+ mbCarryOverOutOfCarCondition = false;
+
+ unsigned int i;
+ for ( i = 0; i < MAX_BONUS_OBJECTIVES; ++i )
+ {
+ mBonusObjectives[ i ] = NULL;
+ }
+ mElapsedTimems = 0;
+ for (int j =0; j<MAX_STAGES;j++)
+ {
+ mMissionStages[j]=NULL;
+ }
+}
+
+//==============================================================================
+// Mission::~Mission
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Mission::~Mission()
+{
+ /*
+ for( i = 0; i < mNumVehicles; i++ )
+ {
+ mVehicles[ i ]->Release();
+ mVehicles[ i ] = NULL;
+ }
+ */
+
+ if ( mDynaloadLoc )
+ {
+ mDynaloadLoc->Release();
+ }
+
+ unsigned int k;
+ for ( k = 0; k < mNumBonusObjectives; ++k )
+ {
+ delete mBonusObjectives[ k ];
+ mBonusObjectives[ k ] = NULL;
+ }
+ mNumBonusObjectives = 0;
+
+ if ( mStreetRacePropsLoad )
+ {
+ mStreetRacePropsLoad->ReleaseVerified();
+ }
+
+
+ if ( mStreetRacePropsUnload )
+ {
+ mStreetRacePropsUnload->ReleaseVerified();
+ }
+
+ // Release all statepropcollectibles and remove them from the world
+ for ( int i = 0 ; i < mNumStatePropCollectibles ; i++ )
+ {
+ StatePropCollectible* collectible = mStatePropCollectibles[i];
+ if ( collectible )
+ {
+ collectible->RemoveFromDSG();
+ collectible->Release();
+ }
+ }
+ delete [] mStatePropCollectibles;
+ mStatePropCollectibles = NULL;
+ mNumStatePropCollectibles = 0;
+
+ if ( mDoorStars )
+ {
+ delete mDoorStars;
+ }
+}
+
+//=============================================================================
+// Mission::AddPlayerVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* vehicle )
+//
+// Return: void
+//
+//=============================================================================
+/*
+void Mission::AddVehicle( Vehicle* vehicle )
+{
+ rAssert( vehicle != NULL );
+
+ int i;
+ for ( i = 0; i < mNumVehicles; ++i )
+ {
+ if ( mVehicles[ i ] == vehicle )
+ {
+ //We've already got this one!
+ return;
+ }
+ }
+
+ rAssert( mNumVehicles < MAX_VEHICLES );
+ mVehicles[ mNumVehicles ] = vehicle;
+ vehicle->AddRef();
+
+ mNumVehicles++;
+}
+*/
+
+//=============================================================================
+// Mission::GetVehicleByName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char* name )
+//
+// Return: Vehicle
+//
+//=============================================================================
+/*
+Vehicle* Mission::GetVehicleByName( char* name )
+{
+ Vehicle* vehicle = NULL;
+
+ for( int i = 0; i < mNumVehicles; i++ )
+ {
+ if( strcmp( mVehicles[ i ]->GetName(), name ) == 0 )
+ {
+ vehicle = mVehicles[ i ];
+ break;
+ }
+ }
+
+ // TODOGREG - is this what we want to do here?
+ // if mission doesn't have one of these, do we really want to return one from vehicle central??????????
+ if ( !vehicle )
+ {
+ vehicle = GetVehicleCentral()->GetVehicleByName( name );
+ }
+
+ return vehicle;
+}
+*/
+
+//=============================================================================
+// Mission::Initialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Mission::Initialize( GameMemoryAllocator heap)
+{
+ mHeap = heap;
+
+ unsigned int i;
+ for ( i = 0; i < mNumBonusObjectives; ++i )
+ {
+ mBonusObjectives[ i ]->Initialize();
+ }
+
+ mbComplete = false;
+ mbIsLastStage = false;
+ mJumpBackStage = false;
+ mJumpBackBy = 1;
+
+ SetToStage( -1 );
+
+ mState = STATE_WAITING;
+
+
+ //if missions are forced cars turn off phone booths
+ if(mIsForcedCar == true)
+ {
+ GetGameplayManager()->DisablePhoneBooths();
+ }
+
+ if(mbAutoRepairCar == true)
+ {
+ //GetGameplayManager()->GetCurrentVehicle()->ResetDamageState();
+ if (GetGameplayManager()->GetCurrentVehicle() != NULL)
+ {
+ if (GetGameplayManager()->GetCurrentVehicle()->IsVehicleDestroyed() == true)
+ {
+ GetEventManager()->TriggerEvent(EVENT_REPAIR_CAR);
+
+ }
+ }
+ }
+ else
+ {
+ if (IsSundayDrive() == true ||IsWagerMission()== true )
+ {
+
+ }
+ else
+ {
+ mbAutoRepairCar = true;
+ }
+ }
+
+ //Purchase rewards only in sunday drive.
+ ActionButton::PurchaseReward::SetEnabled( mSundayDrive );
+
+ GetEventManager()->AddListener(this, EVENT_ENTER_INTERIOR_TRANSITION_START);
+ GetEventManager()->AddListener(this, EVENT_ENTER_INTERIOR_END);
+ GetEventManager()->AddListener(this, EVENT_EXIT_INTERIOR_START);
+ GetEventManager()->AddListener(this, EVENT_EXIT_INTERIOR_END);
+ //chuck: Adding this to handle the patty and selma done talking event. since this class will now trigger screen.
+ GetEventManager()->AddListener(this, EVENT_GUI_TRIGGER_PATTY_AND_SELMA_SCREEN);
+
+ //Try to setup the doorstars if this is sunday drive
+ rAssert( mDoorStars == NULL );
+ if ( mSundayDrive )
+ {
+ int level = GetGameplayManager()->GetCurrentLevelIndex() + 1;
+ if ( level == 5 )
+ {
+ level = 2;
+ }
+ else if ( level == 6 )
+ {
+ level = 3;
+ }
+ else if ( level == 7 )
+ {
+ level = 4;
+ }
+
+ char name[ 64 ];
+ sprintf( name, "l%d_doorstars", level );
+ mDoorStars = new AnimatedIcon();
+ mDoorStars->Init( name, rmt::Vector( 0.0f, 0.0f, 0.0f ), true, false );
+ }
+
+ if( strncmp( mcName, "sr1", 3 ) == 0 ||
+ strncmp( mcName, "sr2", 3 ) == 0 )
+ {
+ mIsStreetRace1Or2 = true;
+ }
+ else
+ {
+ mIsStreetRace1Or2 = false;
+ }
+
+}
+
+//=============================================================================
+// Mission::Finalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Mission::Finalize()
+{
+ SetToStage( -1 );
+
+ //chuck adding draw synch I hope to fix the level 5 m1 crash when user aborts missions.
+ p3d::pddi->DrawSync();
+
+ //Stop harrass cars from spawning
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->ClearAllObjects();
+ chaseManager->SetMaxObjects(0);
+ }
+ gameplayManager->EmptyMissionVehicleSlots();
+ }
+
+
+
+ int i;
+ for ( i = 0; i < mNumMissionStages; i++ )
+ {
+ delete mMissionStages[ i ];
+ mMissionStages[ i ] = NULL;
+ }
+
+ // Remove all collectibles from the vehicles
+ GetVehicleCentral()->DetachAllCollectibles();
+
+
+ // Release all statepropcollectibles and remove them from the world
+ for ( int i = 0 ; i < mNumStatePropCollectibles ; i++ )
+ {
+ StatePropCollectible* collectible = mStatePropCollectibles[i];
+ if ( collectible )
+ {
+ collectible->RemoveFromDSG();
+ collectible->Release();
+ p3d::inventory->Remove( collectible );
+ collectible = NULL;
+ }
+ }
+ mNumStatePropCollectibles = 0;
+
+
+
+ /*
+ for ( j = 0; j < mNumVehicles; ++j )
+ {
+ //bool succeeded = GetVehicleCentral()->RemoveVehicleFromActiveList( mVehicles[ j ] );
+// rAssert( succeeded );
+ mVehicles[ j ]->Release();
+ mVehicles[ j ] = NULL;
+ }
+
+ mNumVehicles = 0;
+ */
+
+ mNumMissionStages = 0;
+
+ mbComplete = false;
+ mbIsLastStage = false;
+
+ unsigned int k;
+ for ( k = 0; k < mNumBonusObjectives; ++k )
+ {
+ mBonusObjectives[ k ]->Finalize();
+ delete mBonusObjectives[ k ];
+ }
+
+ mNumBonusObjectives = 0;
+
+ //This is a safety catch
+ GetEventManager()->RemoveAll( this );
+
+ //
+ // TODO: Dump all the mission vehicles (player & AI)
+ //
+
+ //Dump the car if this is a forced car mission
+ if ( mIsForcedCar )
+ {
+ //re-enable phone booths
+ GetGameplayManager()->EnablePhoneBooths();
+
+
+ Character* player = GetAvatarManager()->GetAvatarForPlayer( 0 )->GetCharacter();
+
+ Vehicle* forcedCar = GetGameplayManager()->GetVehicleInSlot(GameplayManager::eOtherCar);
+ Vehicle* currCar = GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle();
+ if(currCar && currCar->mVehicleID == VehicleEnum::HUSKA)
+ {
+ currCar = GetVehicleCentral()->mHuskPool.FindOriginalVehicleGivenHusk(currCar);
+ }
+ if ( forcedCar != NULL && currCar == forcedCar && (mbSwappedCars == false) )
+ {
+
+ //locking forced car since we are done with it.
+ forcedCar->TransitToAI();
+ //The character is in the forced car
+ GetAvatarManager()->PutCharacterOnGround( player, forcedCar );
+
+ //Move the character somewhere reasonable
+ rmt::Vector pos;
+ player->GetPosition( &pos );
+ float rotation = player->GetFacingDir();
+
+
+ const Road* road;
+ RoadSegment* roadSeg;
+ int segIndex;
+ float in;
+ float lateral;
+
+ if ( RoadManager::GetInstance()->FindRoad( pos, &road, &roadSeg, segIndex, in, lateral, true ) )
+ {
+ //We're on a road set our position to the side of the road.
+ roadSeg->GetPosition( 0.5f, 0.5f, &pos );
+ }
+ else
+ {
+ //Am I in an intersection?
+ Intersection* intersection = RoadManager::GetInstance()->FindIntersection( pos );
+ if ( intersection != NULL )
+ {
+ //Take the first road going out of the intersection and
+ //put the player on the corner.
+ const Road* road = intersection->GetRoadOut( 0 );
+
+ if(!road)
+ {
+ road = intersection->GetRoadIn( 0 );
+ }
+
+ if(road)
+ {
+ roadSeg = road->GetRoadSegment( 0 );
+ roadSeg->GetPosition( 0.5f, 0.5f, &pos );
+ }
+ }
+ else
+ {
+ // we're not on a road or on an intersection. get the "closeset" road
+ float distance;
+ RoadSegment* segment;
+ GetIntersectManager()->FindClosestRoad(
+ pos,
+ 50.0f, //yay the magic numbers i just made this one up
+ segment,
+ distance
+ );
+ rAssert( segment != NULL );
+ segment->GetPosition( &pos );
+ }
+ }
+
+ rmt::Vector CharacterPositionVsCar;
+
+ rmt::Vector CharacterPositionVsCharacterStartPosition;
+
+ player->GetPosition(CharacterPositionVsCar);
+
+ player->GetPosition(CharacterPositionVsCharacterStartPosition);
+
+ //subtract the character position from the forced car start locator
+ CharacterPositionVsCar.Sub(GetGameplayManager()->mPlayerAndCarInfo.mForceLocation);
+
+ //subtract the character's position from the original start position.
+
+ CharacterPositionVsCharacterStartPosition.Sub(GetGameplayManager()->mPlayerAndCarInfo.mPlayerPosition);
+
+
+
+ float vaule1,vaule2;
+
+ //distance between character and the forced cars respawn locator
+ vaule1 = rmt::Sqrt(CharacterPositionVsCar.MagnitudeSqr());
+
+ //distance between character and the spot where they talked to NPC to start mission.
+ vaule2 = rmt::Sqrt(CharacterPositionVsCharacterStartPosition.MagnitudeSqr());
+
+
+
+ //if the character is less than 2 meter from the forced carstart location then place them at the spot that
+ //they were in when starting the talk to objective that started this mission
+ if (vaule1 < 2.0f && vaule2 < 20.0f)
+ {
+ pos = GetGameplayManager()->mPlayerAndCarInfo.mPlayerPosition;
+ }
+ player->RelocateAndReset( pos, rotation, true );
+
+ //GetGameplayManager()->PutPlayerInCar( true ); //Hackish way to put the player in the car in the next / prev stage / mission
+ //GetGameplayManager()->mShouldLoadDefaultVehicle = true;
+
+ GetEventManager()->TriggerEvent( EVENT_CHARACTER_POS_RESET );
+ }
+ GetGameplayManager()->ClearVehicleSlot(GameplayManager::eOtherCar);
+ }
+
+ //unload streetrace props if there are any for this mission
+ InitStreetRacePropUnload();
+ mbSwappedCars = false;
+
+
+ if ( mDoorStars )
+ {
+ delete mDoorStars;
+ mDoorStars = NULL;
+ }
+}
+
+//=============================================================================
+// Mission::SetToStage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (unsigned int index)
+//
+// Return: void
+//
+//=============================================================================
+void Mission::SetToStage( int index, bool resetting )
+{
+ if( index == mCurrentStage )
+ {
+ return;
+ }
+
+ MissionStage* stage = GetCurrentStage();
+ if( stage != NULL )
+ {
+
+ stage->Finalize();
+ }
+
+ stage = GetStage( index );
+ //Test if this is a "locked" stage and see if you can do it.
+ if ( stage && stage->GetMissionLocked() )
+ {
+ if ( UnlockStage( stage ) )
+ {
+
+ //Now we always play the dialogue.
+// //Go to the next stage
+// rAssert( index < MAX_STAGES );
+// ++index;
+ }
+ }
+
+ mCurrentStage = index;
+
+ stage = GetCurrentStage();
+ if( stage != NULL )
+ {
+ unsigned int time;
+ MissionStage::StageTimeType type;
+
+ stage->GetStageTime( type, time );
+
+ switch( type )
+ {
+ case MissionStage::STAGETIME_NOT_TIMED:
+ {
+ mMissionTimer = -1;
+ break;
+ }
+ case MissionStage::STAGETIME_ADD:
+ {
+ mMissionTimer += static_cast<int>( time * MS_PER_SEC );
+ break;
+ }
+ case MissionStage::STAGETIME_SET:
+ {
+ mMissionTimer = static_cast<int>( time * MS_PER_SEC );
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ break;
+ }
+ }
+
+ // new
+ // greg
+ // jan 10, 2003
+
+ // loop through up to and including this stage and
+ // re-initialize the vehicle stuff
+ if(resetting)
+ {
+ ResetPlayer();
+
+ int i;
+ for(i = 0; i < index; i++)
+ {
+ GetStage(i)->VehicleInfoInitialize();
+ GetStage(i)->VehicleFinalize();
+ }
+ }
+ stage->Initialize();
+
+ // Is this the start of the mission?
+ //
+ if( index == 0 )
+ {
+ mState = STATE_INPROGRESS;
+ }
+ }
+}
+
+//=============================================================================
+// Mission::NextStage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Mission::NextStage()
+{
+ SetToStage( mCurrentStage + 1 );
+}
+
+//=============================================================================
+// Mission::PrevStage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Mission::PrevStage()
+{
+ rAssert( mCurrentStage != -1 );
+
+ if ( mCurrentStage == 0 )
+ { //We're on the first stage.
+ GetCurrentStage()->Finalize();
+
+ ResetStage();
+ }
+ else
+ {
+ SetToStage( mCurrentStage - 1 );
+ }
+}
+
+//=============================================================================
+// Mission::ResetStage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Mission::ResetStage()
+{
+ GetCurrentStage()->Reset();
+}
+
+//=============================================================================
+// Mission::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void Mission::HandleEvent( EventEnum id, void* pEventData )
+{
+ if((id == EVENT_ENTER_INTERIOR_TRANSITION_START) || (id == EVENT_EXIT_INTERIOR_START))
+ {
+ mNoTimeUpdate = true;
+ }
+ else if ((id == EVENT_ENTER_INTERIOR_END) || (id == EVENT_EXIT_INTERIOR_END))
+ {
+ mNoTimeUpdate = false;
+ }
+ else if ( id == EVENT_GUI_MISSION_START )
+ {
+ //Dirty dirty
+ GetCurrentStage()->Start();
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Resume();
+
+ if ( GetCurrentStage()->StartBonusObjective() )
+ {
+ StartBonusObjectives();
+ }
+ }
+ else if ( id == EVENT_GUI_TRIGGER_PATTY_AND_SELMA_SCREEN)
+ {
+ mbTriggerPattyAndSelmaScreen = true;
+ }
+ else
+ {
+ MissionStage* pStage = GetCurrentStage();
+ if( pStage != NULL )
+ {
+ pStage->HandleEvent( id, pEventData );
+ }
+ }
+}
+
+//=============================================================================
+// Mission::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void Mission::Update( unsigned int elapsedTime )
+{
+ if ( mChangingStages )
+ {
+ DoStageChange();
+ }
+
+ MissionStage* stage = GetCurrentStage();
+
+ // MS9: take this out! There should always be a stage
+ if( stage == NULL )
+ return;
+
+ rAssert( stage != NULL );
+
+
+BEGIN_PROFILE( "Mission Update" );
+ MissionStage::MissionStageState state = stage->GetProgress();
+
+ ContextEnum currentContext = GetGameFlow()->GetCurrentContext();
+ if( currentContext == CONTEXT_PAUSE )
+ {
+ state = MissionStage::STAGE_INPROGRESS;
+ }
+
+ switch( state )
+ {
+ case MissionStage::STAGE_INPROGRESS:
+ {
+ if (GetCurrentStage() != NULL)
+ {
+ if( mMissionTimer >=1 && !mNoTimeUpdate && GetCurrentStage()->mbDisablePlayerControlForCountDown != true)
+ {
+ if(GetCurrentStage()->QueryUseElapsedTime() ==true) //make timer count up if stage is using elasped time.
+ {
+ mMissionTimer += elapsedTime;
+ }
+ else
+ {
+ mMissionTimer -= elapsedTime;
+ }
+
+ mElapsedTimems += elapsedTime;
+
+ //Chuck:only trigger red flashing timer if we aren't using elasped time.
+
+
+ if( ( mMissionTimer <= 10000 )
+ && ( mMissionTimer + elapsedTime > 10000 )
+ && ( stage->GetFinalStage()
+ && !(GetCurrentStage()->QueryUseElapsedTime())
+ ) )
+ {
+ GetEventManager()->TriggerEvent( EVENT_TIME_RUNNING_OUT );
+ }
+
+
+ }
+ GetCurrentStage()->Update( elapsedTime );
+
+ unsigned int i;
+ for ( i = 0; i < mNumBonusObjectives; ++i )
+ {
+ mBonusObjectives[ i ]->Update( elapsedTime );
+ }
+ break;
+ }
+ }
+ case MissionStage::STAGE_COMPLETE:
+ {
+ if( state == mLastStageState && mFinalDelay == -1 && mCompleteDelay == -1 )
+ {
+ break;
+ }
+
+ mbComplete = mCurrentStage == mNumMissionStages-1;
+
+ if ( mFinalDelay == -1 && stage->GetFinalStage() && mbComplete )
+ {
+ //Setup the delay and skip outta here.
+ MissionStage* thisStage = this->GetCurrentStage();
+ MissionObjective* objective = thisStage->GetObjective();
+ if( objective->IsPattyAndSelmaDialog() )
+ {
+ mFinalDelay = 0;
+ }
+ else
+ {
+ mFinalDelay = FINAL_DELAY;
+ //
+ // Tell the GUI system that the mission succeeded
+ //
+ if(!IsSundayDrive())
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_INGAME_MISSION_COMPLETE );
+ }
+ }
+
+ //TODO: Send off the bonus mission info too.
+ GetEventManager()->TriggerEvent( EVENT_MISSION_SUCCESS );
+
+ if( stage->GetObjective()->GetObjectiveType() == MissionObjective::OBJ_LOSETAIL )
+ {
+ GetEventManager()->TriggerEvent( EVENT_TAIL_LOST_DIALOG );
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_MISSION_SUCCESS_DIALOG );
+ }
+
+ break;
+ }
+ else if ( stage->GetFinalStage() && mbComplete )
+ {
+ if ( static_cast<int>(elapsedTime) >= mFinalDelay )
+ {
+ //All done, carry on.
+ mFinalDelay = -1;
+ }
+ else
+ {
+ //Decrement the time and get outta here. WE pause to show the mission complete stuff.
+ mFinalDelay -= elapsedTime;
+ break;
+ }
+ }
+
+ // trigger stage complete event
+ //
+ unsigned int showStageComplete = stage->IsShowStageComplete() ? 1 : 0;
+
+ if ( showStageComplete && mCompleteDelay == -1 )
+ {
+ //We need to delay the mission stage change to prevent weirdness.
+ mCompleteDelay = COMPLETE_DELAY;
+
+ GetEventManager()->TriggerEvent( EVENT_STAGE_COMPLETE,
+ reinterpret_cast<void*>( showStageComplete ) );
+
+ if( stage->GetObjective()->GetObjectiveType() == MissionObjective::OBJ_LOSETAIL )
+ {
+ GetEventManager()->TriggerEvent( EVENT_TAIL_LOST_DIALOG );
+ }
+
+ break;
+ }
+ else if ( showStageComplete )
+ {
+ if ( mCompleteDelay > static_cast<int>(elapsedTime) )
+ {
+ mCompleteDelay -= elapsedTime;
+ break;
+ }
+ else
+ {
+ //We're good.
+ mCompleteDelay = -1;
+ }
+ }
+
+ if ( stage->GetFinalStage() || mbComplete )
+ {
+ mState = STATE_SUCCESS;
+
+ if ( stage->GetFinalStage() )
+ {
+ //To Do::MESA Insert Call to CharacterSheetManager to update
+ //character sheet with complete and bonus object stuff
+
+
+ if (mBonusObjectives[0] != NULL)
+ {
+ int seconds = (mElapsedTimems /1000);
+ GetCharacterSheetManager()->SetMissionComplete(GetGameplayManager()->GetCurrentLevelIndex(),mcName,mBonusObjectives[0]->GetSuccessful(),seconds );
+ }
+ else
+ {
+ int seconds = (mElapsedTimems/1000);
+ GetCharacterSheetManager()->SetMissionComplete(GetGameplayManager()->GetCurrentLevelIndex(),mcName,false,seconds);
+ }
+
+ if ( mbTriggerPattyAndSelmaScreen == true)
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_MISSION_SUCCESS,
+ CLEAR_WINDOW_HISTORY );
+ GetEventManager()->TriggerEvent( EVENT_GUI_ENTERING_MISSION_SUCCESS_SCREEN );
+ GetGameFlow()->SetContext( CONTEXT_PAUSE );
+ InitStreetRacePropUnload();
+ mbTriggerPattyAndSelmaScreen = false;
+ }
+
+
+
+ }
+
+ //This is silly.... Dirty too. At least it's not the GUI doing it.
+ //Really this just updates the state of all the missions. GAH.
+ GetGameplayManager()->ContinueGameplay();
+
+ if ( mCurrentStage != -1 )
+ {
+ //GetCurrentStage()->Reset();
+ GetCurrentStage()->Start();
+
+ if ( GetCurrentStage()->StartBonusObjective() )
+ {
+ StartBonusObjectives();
+ }
+ }
+ }
+ else
+ {
+ SetupStageChange();
+ }
+ break;
+ }
+ case MissionStage::STAGE_FAILED:
+ {
+ unsigned int i;
+ unsigned int numConditions;
+ bool isChaseCondition = false;
+
+ if( state == mLastStageState )
+ {
+ break;
+ }
+
+ mState = STATE_FAILED;
+ //if we fail a forced car relock the forced car
+ if ( IsForcedCar() == true)
+ {
+ if (GetGameplayManager()-> mVehicleSlots[GameplayManager::eOtherCar].mp_vehicle != NULL)
+ {
+
+ GetGameplayManager()-> mVehicleSlots[GameplayManager::eOtherCar].mp_vehicle->ActivateTriggers(false);
+ }
+
+ }
+
+
+
+ // Tell the GUI system that mission failed
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_INGAME_MISSION_FAILED );
+
+ // get the failure condition
+ //
+ MissionCondition* failedCondition = stage->GetFailureCondition();
+ rTuneAssertMsg( failedCondition != NULL,
+ "WTF?? How could there be no failure condition?" );
+
+ //
+ // Determine whether any of the conditions involve chases
+ //
+ numConditions = stage->GetNumConditions();
+ for( i = 0; i < numConditions; i++ )
+ {
+ if( stage->GetCondition( i )->IsChaseCondition() )
+ {
+ isChaseCondition = true;
+ break;
+ }
+ }
+ GetEventManager()->TriggerEvent( EVENT_MISSION_FAILURE, reinterpret_cast<void*>( failedCondition ) );
+
+ mbComplete = true;
+
+ //
+ // We need to re-enable world rendering if we failed because of
+ // entering an interior
+ //
+ bool entering = GetInteriorManager()->IsEntering();
+ if( entering )
+ {
+ GetRenderManager()->mpLayer( RenderEnums::LevelSlot )->Thaw();
+ }
+
+ if(IsBonusMission())
+ {
+ GetCharacterManager()->ResetBonusCharacters();
+ }
+
+ break;
+ }
+ case MissionStage::STAGE_BACKUP:
+ {
+ //This is a bad thing.
+ rAssert( false );
+
+/* PrevStage();
+ GetCurrentStage()->Start();
+*/
+ break;
+ }
+ default:
+ {
+ // whatever
+ break;
+ }
+ }
+
+ mLastStageState = state;
+
+ if ( mDoorStars )
+ {
+ mDoorStars->Update( elapsedTime );
+ }
+
+ DoUpdate( elapsedTime );
+END_PROFILE( "Mission Update" );
+}
+
+//=============================================================================
+// Mission::IsComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool Mission::IsComplete()
+{
+ return( mbComplete );
+}
+
+//=============================================================================
+// Mission::SetRestartDyanload
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* loadString )
+//
+// Return: void
+//
+//=============================================================================
+void Mission::SetRestartDynaload( const char* loadString, const char* interior)
+{
+ HeapMgr()->PushHeap (GMA_LEVEL_MISSION);
+ MEMTRACK_PUSH_GROUP( "Mission - Dynaload" );
+
+ if ( mDynaloadLoc )
+ {
+ mDynaloadLoc->Release();
+ }
+
+ if( strcmp( loadString, "" ) != 0 )
+ {
+ mDynaloadLoc = new ZoneEventLocator;
+ mDynaloadLoc->AddRef();
+ mDynaloadLoc->SetZoneSize( strlen(loadString) + 1 );
+ mDynaloadLoc->SetZone( loadString );
+ mDynaloadLoc->SetPlayerEntered();
+
+ if(interior)
+ {
+ mDynaloadLoc->SetName(interior);
+ }
+ }
+ else
+ {
+ mDynaloadLoc = NULL;
+ }
+
+ MEMTRACK_POP_GROUP( "Mission - Dynaload" );
+ HeapMgr()->PopHeap (GMA_LEVEL_MISSION);
+}
+
+//=============================================================================
+// Mission::AddBonusObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( BonusObjective* bo )
+//
+// Return: void
+//
+//=============================================================================
+void Mission::AddBonusObjective( BonusObjective* bo )
+{
+ rAssert( mNumBonusObjectives < MAX_BONUS_OBJECTIVES );
+
+ mBonusObjectives[ mNumBonusObjectives ] = bo;
+ ++mNumBonusObjectives;
+}
+
+//=============================================================================
+// Mission::StartBonusObjectives
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Mission::StartBonusObjectives()
+{
+ unsigned int i;
+ for ( i = 0; i < mNumBonusObjectives; ++i )
+ {
+ mBonusObjectives[ i ]->Start();
+ }
+}
+
+
+//=============================================================================
+// Mission::SetForcedCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool isForced )
+//
+// Return: void
+//
+//=============================================================================
+void Mission::SetForcedCar( bool isForced )
+{
+ mIsForcedCar = isForced;
+}
+
+//=============================================================================
+// Mission::SetMissionTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int timeMilliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void Mission::SetMissionTime( int timeMilliseconds )
+{
+ mMissionTimer = timeMilliseconds;
+}
+//=============================================================================
+// Mission::CreateStatePropCollectible
+//=============================================================================
+// Description: Creates a new statepropcollectible and places it at the given locator
+//
+// Parameters: name of the CStateProp data chunk in the inventory, name of the locator
+//
+// Return: void
+//
+//=============================================================================
+void Mission::CreateStatePropCollectible( const char* statepropname, const char* locatorname, int collisionattributes )
+{
+ // No space in the array, bail
+ if ( mNumStatePropCollectibles >= MAX_NUM_STATEPROP_COLLECTIBLES )
+ return;
+
+ HeapMgr()->PushHeap (GMA_LEVEL_MISSION);
+ Locator* locator = p3d::find< Locator >( locatorname );
+ if ( locator )
+ {
+ CStatePropData* propdata = p3d::find< CStatePropData >(statepropname);
+ if ( propdata )
+ {
+ // Get the locator's position
+ rmt::Vector locatorposition;
+ locator->GetPosition( &locatorposition );
+
+ // Fill out a translation matrix
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( locatorposition );
+
+ StatePropCollectible* collectible = new StatePropCollectible();
+ CollisionAttributes* collAttr = GetATCManager()->CreateCollisionAttributes( PROP_MOVEABLE, collisionattributes, 2.242f );
+ collAttr->AddRef();
+ collectible->LoadSetup( propdata, 0, transform, collAttr, false, NULL );
+ collAttr->Release();
+
+ collectible->SetName( statepropname );
+ p3d::inventory->Store( collectible );
+
+ // Add it to the internal list of prop collectibles
+ // Allocate a new list if necessary
+ if ( mNumStatePropCollectibles == 0 )
+ {
+ mStatePropCollectibles = new StatePropCollectible*[ MAX_NUM_STATEPROP_COLLECTIBLES ];
+ }
+ mStatePropCollectibles[ mNumStatePropCollectibles ] = collectible;
+ collectible->AddRef();
+
+ mNumStatePropCollectibles++;
+
+ }
+ else
+ {
+ rReleaseAssertMsg("can't find stateprop %s\n",statepropname);
+ }
+ }
+ else
+ {
+ rReleaseAssertMsg("can't find locator %s\n",locatorname);
+ }
+ HeapMgr()->PopHeap (GMA_LEVEL_MISSION);
+}
+
+
+
+//=============================================================================
+// Mission::AttachStatePropCollectible
+//=============================================================================
+// Description: Creates and attaches a new statepropcollectible on a vehicle
+//
+// Parameters: name of the CStateProp data chunk in the inventory, name of the vehicle
+//
+// Return: void
+//
+//=============================================================================
+void Mission::AttachStatePropCollectible( const char* statepropname, const char* vehicleName, int collisionattributes )
+{
+ // No space in the array, bail
+ if ( mNumStatePropCollectibles >= MAX_NUM_STATEPROP_COLLECTIBLES )
+ return;
+
+ CStatePropData* statePropData = p3d::find< CStatePropData > (statepropname);
+ if ( statePropData )
+ {
+ Vehicle* vehicle = GetVehicleCentral()->GetVehicleByName( vehicleName );
+ StatePropCollectible* stateprop = new StatePropCollectible();
+ rmt::Matrix transform;
+ transform.Identity();
+ // Create some collision attributes. Unfort I may just have to leave these hardcoded.
+ // Settings seem to make very little difference anyway.
+ CollisionAttributes* collAttr = GetATCManager()->CreateCollisionAttributes( PROP_MOVEABLE, collisionattributes, 2.242f );
+ collAttr->AddRef();
+ stateprop->LoadSetup( statePropData, 0, transform, collAttr, false );
+ collAttr->Release();
+ // Add it to scenegraph
+ stateprop->AddToDSG();
+ vehicle->AttachCollectible( stateprop );
+
+ // Add it to the internal list of prop collectibles
+ // Allocate a new list if necessary
+ if ( mNumStatePropCollectibles == 0 )
+ {
+ mStatePropCollectibles = new StatePropCollectible*[ MAX_NUM_STATEPROP_COLLECTIBLES ];
+ }
+
+ stateprop->SetName( statepropname );
+ p3d::inventory->Store( stateprop );
+
+ mStatePropCollectibles[ mNumStatePropCollectibles ] = stateprop;
+ stateprop->AddRef();
+
+ mNumStatePropCollectibles++;
+ }
+
+
+}
+
+
+//=============================================================================
+// Mission::Reset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool JumpStage )
+//
+// Return: void
+//
+//=============================================================================
+void Mission::Reset( bool jumpStage )
+{
+ mbComplete = false;
+ mState = STATE_INPROGRESS;
+ mbIsLastStage = false;
+ mElapsedTimems = 0;
+ mbCarryOverOutOfCarCondition = false;
+
+ //Reset ChaseManager Spawn Rate incase of Retries
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->SetMaxObjects(0);
+ }
+ gameplayManager->MakeSureHusksAreReverted(); // do this rather than let GameplayManager catch event below, because that's too late I think
+ }
+
+ SetToStage( -1 );
+
+ if ( jumpStage )
+ {
+ SetToStage( mResetToStage, jumpStage );
+
+ // repair car on mission reset or retry
+
+ GetEventManager()->TriggerEvent(EVENT_REPAIR_CAR);
+
+
+ }
+ else
+ {
+ SetToStage( 0 );
+
+ }
+
+ //Reset the bonus objectives.
+ unsigned int i;
+ for ( i = 0; i < mNumBonusObjectives; ++i )
+ {
+ mBonusObjectives[ i ]->Reset();
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_MISSION_RESET, (void*)( jumpStage ) );
+
+
+ GetCharacterSheetManager()->IncrementMissionAttempt(GetGameplayManager()->GetCurrentLevelIndex(),this->mcName);
+ InitStreetRacePropLoad();
+
+ //make sure our current car doesn't have locked doors
+ //since forced cars have locked doors.
+
+ if(IsForcedCar())
+ {
+ if(GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].mp_vehicle != NULL)
+ {
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].mp_vehicle->ActivateTriggers(true);
+ }
+ }
+
+}
+
+//=============================================================================
+// Mission::ResetPlayer
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Mission::ResetPlayer()
+{
+ //This is where we get the reset data and apply it. Sounds good eh?
+ Character* player = GetCharacterManager()->GetCharacter( 0 ); //This is the player...
+ Vehicle* car = GetGameplayManager()->GetCurrentVehicle(); //Current car
+
+ if( mVehicleRestart != NULL )
+ {
+ GetGameplayManager()->PlaceVehicleAtLocator( car, mVehicleRestart );
+
+ }
+
+ //if ( mPlayerRestart == NULL || IsForcedCar() || IsRaceMission() )
+ if ( mPlayerRestart == NULL || IsRaceMission() )
+ {
+ rmt::Vector pos;
+ mVehicleRestart->GetLocation( &pos );
+ player->RelocateAndReset( pos, 0, true );
+ if (GetGameplayManager()->GetCurrentVehicle()->mVehicleDestroyed != true)
+ {
+ GetAvatarManager()->PutCharacterInCar( player, car );
+ }
+ }
+ else
+ {
+ //TODO: Make a put player on ground type thingie. I shouldn't have to know about this.
+ GetGameplayManager()->PlaceCharacterAtLocator( player, mPlayerRestart );
+ GetAvatarManager()->PutCharacterOnGround( player, car );
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_CHARACTER_POS_RESET );
+
+ //Let's cut the camera.
+ GetSuperCamManager()->GetSCC( 0 )->NoTransition();
+ GetSuperCamManager()->GetSCC( 0 )->DoCameraCut();
+}
+
+//=============================================================================
+// Mission::InitDynaLoad
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Mission::InitDynaLoad()
+{
+ rTuneAssertMsg( mDynaloadLoc, "DYNALOAD information is required in all missions!" );
+
+ GetEventManager()->TriggerEvent( EVENT_FIRST_DYNAMIC_ZONE_START, mDynaloadLoc );
+
+ // set the pedgroup sheeyatsu...
+ if( 0 <= mInitPedGroupId && mInitPedGroupId < PedestrianManager::MAX_MODEL_GROUPS )
+ {
+ PedestrianManager::GetInstance()->SwitchModelGroup( mInitPedGroupId );
+ PedestrianManager::SetDefaultModelGroup( mInitPedGroupId );
+ }
+}
+
+void Mission::SetInitPedGroup( int initGroupId )
+{
+ rTuneAssert( 0 <= mInitPedGroupId && mInitPedGroupId < PedestrianManager::MAX_MODEL_GROUPS );
+ mInitPedGroupId = initGroupId;
+}
+
+//=============================================================================
+// Mission::DialogueCharactersTeleported
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool Mission::DialogueCharactersTeleported()
+{
+ if ( !GetCurrentStage() ||
+ !(GetCurrentStage()->GetObjective()) )
+ {
+ //DAMN THOSE COSMIC RAYS!
+ return false; //No cut.
+ }
+
+ if ( GetCurrentStage()->GetObjective()->GetObjectiveType() == MissionObjective::OBJ_DIALOGUE )
+ {
+ return static_cast<DialogueObjective*>(GetCurrentStage()->GetObjective())->CharactersReset();
+ }
+
+ return false;
+}
+
+//=============================================================================
+// Mission::GetCurrentStage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+MissionStage* Mission::GetCurrentStage()
+{
+ if(( mCurrentStage >= 0 ) && (mCurrentStage < (int)mNumMissionStages ))
+ {
+ return( mMissionStages[ mCurrentStage ] );
+ }
+ else
+ {
+ return( NULL );
+ }
+
+}
+
+//=============================================================================
+// Mission::GetMissionTimeLeftInSeconds
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+int Mission::GetMissionTimeLeftInSeconds()
+{
+ return( static_cast<int>( mMissionTimer * SEC_IN_MSEC ));
+}
+
+//=============================================================================
+// Mission::GetMissionTimeLeftInMilliSeconds
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+int Mission::GetMissionTimeLeftInMilliSeconds()
+{
+ return( mMissionTimer );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// Mission::DoUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void Mission::DoUpdate( int elapsedTime )
+{
+}
+
+//=============================================================================
+// Mission::SetBonusMission
+//=============================================================================
+// Description: sets a flag if this is a bonus mission
+//
+// Parameters: NONE
+//
+// Return: void
+//
+//=============================================================================
+void Mission::SetBonusMission()
+{
+ mBonusMisison = true;
+}
+
+//=============================================================================
+// Mission::IsBonusMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+bool Mission::IsBonusMission()
+{
+ if( IsRaceMission() )
+ {
+ return false;
+ }
+ return mBonusMisison;
+}
+
+//=============================================================================
+// Mission::IsBonusMission
+//=============================================================================
+// Description: determines if this mission is a special road race
+//
+// Parameters: ( int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+bool Mission::IsRaceMission()
+{
+ if( ( mcName[ 0 ] == 's' ) && ( mcName[ 1 ] == 'r' ) )
+ {
+ return true;
+ }
+ return false;
+}
+
+//=============================================================================
+// Mission::IsWagerMission
+//=============================================================================
+// Description: determines if this mission is a wager mission or not
+//
+// Parameters: NONE
+//
+// Return: bool - is it or isn't it?
+//
+//=============================================================================
+bool Mission::IsWagerMission()
+{
+ if( ( mcName[ 0 ] == 'g' ) && ( mcName[ 1 ] == 'r' ) )
+ {
+ return true;
+ }
+ return false;
+}
+
+//=============================================================================
+// Mission::UnlockStage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( MissionStage* stage )
+//
+// Return: bool
+//
+//=============================================================================
+bool Mission::UnlockStage( MissionStage* stage )
+{
+ unsigned int i;
+ for ( i = 0; i < MAX_LOCK_REQUIREMENTS; ++i )
+ {
+ const MissionStage::LockRequirement& requirement = stage->GetLockRequirement( i );
+ if ( requirement.mType == MissionStage::LockRequirement::NONE )
+ {
+ continue;
+ }
+
+ //Test this against what the character has.
+ if ( requirement.mType == MissionStage::LockRequirement::CAR )
+ {
+ if ( strcmp( GetGameplayManager()->GetCurrentVehicle()->mName, requirement.mName ) != 0 )
+ {
+ //Failed the car test.
+ return false;
+ }
+ }
+ else if ( requirement.mType == MissionStage::LockRequirement::SKIN /*Should test for skins.*/ )
+ {
+ Character* character = GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter();
+ if ( strcmp( GetCharacterManager()->GetModelName( character ), requirement.mName ) != 0 )
+ {
+ //failed the skin test
+ return false;
+ }
+ }
+ }
+
+ return true;
+}
+
+
+//=============================================================================
+// Mission::LoadStreetRaceProps
+//=============================================================================
+// Description: Load the p3d file that contains the instanced streetrace props
+//
+// Parameters: ( const char* loadString )
+//
+// Return: void
+//
+//=============================================================================
+void Mission::LoadStreetRaceProps( const char* loadString )
+{
+ HeapMgr()->PushHeap (GMA_LEVEL_MISSION);
+ MEMTRACK_PUSH_GROUP( "Mission - StreetRaceProps" );
+
+ //clear any junk
+ if ( mStreetRacePropsLoad != NULL)
+ {
+ mStreetRacePropsLoad->ReleaseVerified();
+ }
+
+ if( strcmp( loadString, "" ) != 0 )
+ {
+ mStreetRacePropsLoad = new ZoneEventLocator;
+ mStreetRacePropsLoad->AddRef();
+ mStreetRacePropsLoad->SetZoneSize( strlen(loadString) + 1 );
+ mStreetRacePropsLoad->SetZone( loadString );
+ mStreetRacePropsLoad->SetPlayerEntered();
+ }
+ else
+ {
+ mStreetRacePropsLoad = NULL;
+ }
+
+ MEMTRACK_POP_GROUP( "Mission - StreetRaceProps" );
+ HeapMgr()->PopHeap (GMA_LEVEL_MISSION);
+}
+
+
+//=============================================================================
+// Mission::LoadStreetRaceProps
+//=============================================================================
+// Description: Unload the p3d file that contains the instanced streetrace props
+//
+// Parameters: ( const char* loadString )
+//
+// Return: void
+//
+//=============================================================================
+void Mission::UnloadStreetRaceProps( const char* loadString )
+{
+ HeapMgr()->PushHeap (GMA_LEVEL_MISSION);
+ MEMTRACK_PUSH_GROUP( "Mission - StreetRacePropsUnload" );
+
+ //clear any junk
+ if ( mStreetRacePropsUnload != NULL)
+ {
+ mStreetRacePropsUnload->ReleaseVerified();
+ }
+
+ if( strcmp( loadString, "" ) != 0 )
+ {
+ mStreetRacePropsUnload = new ZoneEventLocator;
+ mStreetRacePropsUnload->AddRef();
+ mStreetRacePropsUnload->SetZoneSize( strlen(loadString) + 1 );
+ mStreetRacePropsUnload->SetZone( loadString );
+ mStreetRacePropsUnload->SetPlayerEntered();
+ }
+ else
+ {
+ mStreetRacePropsUnload = NULL;
+ }
+
+ MEMTRACK_POP_GROUP( "Mission - StreetRacePropsUnload" );
+ HeapMgr()->PopHeap (GMA_LEVEL_MISSION);
+}
+
+void Mission::SetupStageChange()
+{
+ mChangingStages = true;
+
+ GetCurrentStage()->DoTransition();
+}
+
+void Mission::DoStageChange()
+{
+// if( !GetInteriorManager()->IsEntering() && !GetInteriorManager()->IsExiting() )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_RESUME_INGAME );
+ }
+
+ MissionStage* currStage = GetCurrentStage();
+ if ( currStage->GetMissionLocked() && !UnlockStage( currStage ) )
+ {
+ //Display the new message and stop the next stage from displaying its message
+ rAssert( currStage->GetStartMessageIndex() >= 0 );
+
+ GetGuiSystem()->HandleMessage( GUI_MSG_INGAME_DISPLAY_PROMPT, currStage->GetStartMessageIndex() );
+ GetEventManager()->TriggerEvent( EVENT_STAGE_TRANSITION_FAILED );
+
+ GetStage( mCurrentStage + 1 )->ShowStartMessageIndex( false );
+
+ NextStage();
+ }
+ else if ( mJumpBackStage )
+ {
+ rTuneAssertMsg( (mCurrentStage - mJumpBackBy) >= 0, "Jumping back too far!" );
+ SetToStage( mCurrentStage - mJumpBackBy );
+ mJumpBackStage = false;
+ }
+ else
+ {
+ NextStage();
+ }
+
+ GetCurrentStage()->Start();
+ if ( GetCurrentStage()->StartBonusObjective() )
+ {
+ StartBonusObjectives();
+ }
+
+ mChangingStages = false;
+}
+
+
+void Mission::SetSwappedCarsFlag(bool flag)
+{
+ mbSwappedCars = flag;
+}
+
+
+bool Mission::GetSwappedCarsFlag()
+{
+ return mbSwappedCars;
+}
+
+
+void Mission::InitStreetRacePropLoad()
+{
+ //Chuck: Trigger the loading of the Street Race Specify Props
+ if (mStreetRacePropsLoad != NULL)
+ {
+ GetEventManager()->TriggerEvent( (EventEnum)(EVENT_LOCATOR+LocatorEvent::DYNAMIC_ZONE), mStreetRacePropsLoad );
+
+ // about to do a street race so disable peds and parked cars
+ if( IsRaceMission() )
+ {
+ PedestrianManager::GetInstance()->DumpAllPedModels();
+ }
+ else
+ {
+ PedestrianManager::GetInstance()->RemoveAllPeds();
+ }
+ PedestrianManager::GetInstance()->AllowAddingPeds(false);
+ GetPCM().DisableParkedCars();
+
+ }
+
+}
+
+void Mission::InitStreetRacePropUnload()
+{
+ if (mStreetRacePropsUnload != NULL)
+ {
+ //Chuck: Trigger the unloading of the Street Race Specify Props
+ GetEventManager()->TriggerEvent( (EventEnum)(EVENT_LOCATOR+LocatorEvent::DYNAMIC_ZONE), mStreetRacePropsUnload );
+
+ // street race done so reenable peds and parked cars
+ PedestrianManager::GetInstance()->AllowAddingPeds(true);
+ GetPCM().EnableParkedCars();
+
+ }
+}
+
+//=============================================================================
+// Mission::ShowHUD
+//=============================================================================
+// Description: Comment
+//
+// Parameters:
+//
+// Return: inline
+//
+//=============================================================================
+void Mission::ShowHUD( bool isShowHUD )
+{
+ mShowHUD = isShowHUD;
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->SetVisible( isShowHUD );
+ }
+}
+
+bool Mission::CanMDKCar(Vehicle* pVehicle,MissionStage* pStage)
+{
+ //search for the input stage.
+
+ int counter =0;
+ int i = 0;
+ int index =0;
+
+ for (i = 0;i<MAX_STAGES;i++)
+ {
+ if (mMissionStages[i] != NULL)
+ {
+ if (mMissionStages[i] == pStage)
+ {
+ index = i+1;
+ break;
+ }
+ }
+ }
+
+ for (index; index <MAX_STAGES;index++)
+ {
+ if (mMissionStages[index] != NULL )
+ {
+ for (int j =0; j< MissionStage::MAX_VEHICLES;j++)
+ {
+ if ( mMissionStages[index]->GetVehicle(j) == pVehicle)
+ {
+ counter++;
+ }
+ }//end of vehicle search loop
+ }
+ }//end of out missionstage loop
+
+ if ( counter > 0)
+ {
+ return false;
+ }
+ else
+ {
+ return true;
+ }
+}
+
+
+
diff --git a/game/code/mission/mission.h b/game/code/mission/mission.h
new file mode 100644
index 0000000..892a510
--- /dev/null
+++ b/game/code/mission/mission.h
@@ -0,0 +1,362 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: mission.h
+//
+// Description: Blahblahblah
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef MISSION_H
+#define MISSION_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <events/eventlistener.h>
+
+#include <events/eventenum.h>
+
+#include <memory/srrmemory.h>
+
+#include <mission/missionstage.h>
+
+#include <string.h>
+
+
+//========================================
+// Forward References
+//========================================
+
+class Vehicle;
+class CarStartLocator;
+class ZoneEventLocator;
+class BonusObjective;
+class StatePropCollectible;
+class AnimatedIcon;
+
+//=============================================================================
+//
+// Synopsis: Controls the flow through a mission by sequentially leading
+// the player through one or more stages. Upon the completion
+// of a stage, the next stage becomes active, and so on until
+// all stages (and thus the mission) are complete.
+//
+//=============================================================================
+
+class Mission : public EventListener
+{
+public:
+
+ enum { MAX_BONUS_OBJECTIVES = 3 };
+
+ Mission();
+ ~Mission();
+
+ // Gets the index in the text bible of the name of this mission
+ char* GetName() { return( &mcName[ 0 ] ); }
+ void SetName( char* name );
+
+ // Stages in this mission
+ void SetNumStages( int num ) { mNumMissionStages = num; }
+ int GetNumStages() { return( mNumMissionStages ); }
+
+ MissionStage* GetStage( int index );
+ void SetStage( int index, MissionStage* stage );
+
+
+ MissionStage* GetCurrentStage();
+ int GetCurrentStageIndex() const { return( mCurrentStage ); }
+
+ // loads and initializes all the stages
+ virtual void Initialize( GameMemoryAllocator heap );
+
+ // finalizes and dumps all the stages
+ virtual void Finalize();
+
+
+ // These two methods finalize the current stage then move
+ // to and initialize some other stage
+ void SetToStage( int index, bool resetting = false );
+ virtual void NextStage();
+ virtual void PrevStage();
+
+ // Resets the current mission stage
+ void ResetStage();
+
+ // fake event listener
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ // The GamePlayManager should call this for every frame
+ // the mission is active
+ void Update( unsigned int elapsedTime );
+
+ //
+ // Adds a vehicle for this mission only
+ //
+ //void AddVehicle( Vehicle* vehicle );
+ //Vehicle* GetVehicleByName( char* name );
+
+ //
+ // Accessors for the time left in the mission
+ //
+ int GetMissionTimeLeftInSeconds();
+ int GetMissionTimeLeftInMilliSeconds();
+
+ // Resets the entire mission. Use with caution!
+ void Reset( bool JumpStage = false );
+ void ResetPlayer();
+ void InitDynaLoad();
+
+ bool DialogueCharactersTeleported();
+
+ enum MissionState
+ {
+ STATE_WAITING,
+ STATE_INPROGRESS,
+ STATE_FAILED,
+ STATE_SUCCESS,
+ NUM_STATES
+ };
+
+ MissionState GetState() { return( mState ); }
+
+ // Returns true iff every stage in this mission is complete
+ bool IsComplete();
+
+ //methods called from the missionscriptloader these shouldnt be called by any thing other missionscriptloader
+ //since are for setup.
+ void SetVehicleRestart( CarStartLocator* loc ) { mVehicleRestart = loc; };
+ void SetPlayerRestart( Locator* loc ) { mPlayerRestart = loc; };
+ void SetRestartDynaload( const char* loadString, const char* interior = NULL ) ;
+ void LoadStreetRaceProps(const char* loadString );
+ void UnloadStreetRaceProps (const char* loadString);
+
+
+ //higher level methods for code use
+ void InitStreetRacePropLoad();
+ void InitStreetRacePropUnload();
+
+
+
+ void SetSundayDrive() { mSundayDrive = true; };
+ bool IsSundayDrive() const { return mSundayDrive; };
+ void SetResetToStage( int stage ) { mResetToStage = stage; };
+ void SetBonusMission();
+ bool IsBonusMission();
+ bool IsRaceMission(); //determines if this mission is a special road race
+ bool IsWagerMission();
+
+ void AddBonusObjective( BonusObjective* bo );
+ void StartBonusObjectives();
+
+
+ void SetForcedCar( bool isForced );
+ bool IsForcedCar() const { return mIsForcedCar; };
+
+ void SetMissionTime( int timeMilliseconds );
+
+ void CreateStatePropCollectible( const char* statepropname, const char* locator, int collisionattributes );
+ void AttachStatePropCollectible( const char* statepropname, const char* vehicleName, int collisionattributes );
+
+ bool GetSwappedCarsFlag();
+ void SetSwappedCarsFlag(bool flag);
+
+ void SpecialCaseStageBackup( unsigned int num ) { mChangingStages = true; mJumpBackStage = true; mJumpBackBy = num; };
+
+ void SetInitPedGroup( int initGroupId );
+
+ void ShowHUD( bool isShowHUD );
+ bool IsHUDVisible() const;
+ bool CanMDKCar(Vehicle* pVehicle,MissionStage* pStage);
+
+ void SetNumValidFailureHints( int numHints );
+ int GetNumValidFailureHints() const;
+ CarStartLocator* GetVehicleRestart () { return mVehicleRestart;};
+
+ inline bool IsChangingStages() const { return mChangingStages; }
+
+ bool GetCarryOverOutOfCarCondition() { return mbCarryOverOutOfCarCondition;};
+
+ void SetCarryOverOutOfCarCondition(bool flag) { mbCarryOverOutOfCarCondition = flag;};
+
+public:
+ bool mIsStreetRace1Or2;
+
+protected:
+ void SetCurrentStageIndex( int index ) { mCurrentStage = index; }
+
+ virtual void DoUpdate( int elapsedTime );
+private:
+
+ bool UnlockStage( MissionStage* stage );
+ void SetupStageChange();
+ void DoStageChange();
+
+
+ static const int MAX_STAGES = 25;
+
+ int mNumMissionStages;
+ MissionStage* mMissionStages[ MAX_STAGES ];
+ int mCurrentStage;
+ int mResetMission;
+
+ char mcName[16];
+
+ GameMemoryAllocator mHeap;
+
+ bool mbComplete;
+ bool mbIsLastStage;
+
+ int mMissionTimer;
+
+ int mElapsedTimems;
+
+ MissionState mState;
+ MissionStage::MissionStageState mLastStageState;
+
+ // TODO greg
+ // this is soon to be history
+
+ //static const int MAX_VEHICLES = 4;
+ //int mNumVehicles;
+ //Vehicle* mVehicles[ MAX_VEHICLES ];
+
+ CarStartLocator* mVehicleRestart;
+ Locator* mPlayerRestart;
+ ZoneEventLocator* mDynaloadLoc;
+
+ ZoneEventLocator* mStreetRacePropsLoad; //Chuck: adding these so we can load and unload street race barriers.
+ ZoneEventLocator* mStreetRacePropsUnload;
+
+
+ int mResetToStage;
+
+ bool mSundayDrive : 1;
+ bool mBonusMisison : 1;
+
+ BonusObjective* mBonusObjectives[ MAX_BONUS_OBJECTIVES ];
+ unsigned int mNumBonusObjectives;
+
+ bool mIsForcedCar;
+ //Chuck Adding this so on retry the car gets auto repaired
+ bool mbAutoRepairCar;
+
+ //Chuck: this variable used for forced missions so that the designers can swap
+ // the forced car for the default vehicle.
+ //if true that means that we have used a scripted sequence switched from the forced to our default
+ //and dont need to swap in the default car at the end of the mission
+ bool mbSwappedCars;
+
+ //flag for carrying over get out of car condition from stage to stage
+ bool mbCarryOverOutOfCarCondition;
+
+
+
+ //Flag to trigger the patty and selma screen after street race
+ bool mbTriggerPattyAndSelmaScreen;
+
+ int mFinalDelay;
+ int mCompleteDelay;
+
+ bool mChangingStages;
+
+ bool mNoTimeUpdate;
+
+ bool mJumpBackStage;
+ char mJumpBackBy;
+
+ int mNumStatePropCollectibles;
+ StatePropCollectible** mStatePropCollectibles;
+
+ AnimatedIcon* mDoorStars;
+ int mInitPedGroupId;
+
+ bool mShowHUD : 1;
+
+ int mNumValidFailureHints;
+
+};
+
+//=============================================================================
+// Mission::SetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char* name )
+//
+// Return: void
+//
+//=============================================================================
+inline void Mission::SetName( char* name )
+{
+ strcpy( &mcName[ 0 ], name );
+}
+
+//=============================================================================
+// Mission::SetStage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int index, MissionStage* stage )
+//
+// Return: inline
+//
+//=============================================================================
+inline void Mission::SetStage( int index, MissionStage* stage )
+{
+ rTuneAssertMsg( index < MAX_STAGES, "Too many stages, get Cary to increase the max!\n" );
+ mMissionStages[ index ] = stage;
+}
+
+//=============================================================================
+// Mission::GetStage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int index )
+//
+// Return: inline
+//
+//=============================================================================
+inline MissionStage* Mission::GetStage( int index )
+{
+ if( index >= 0 && index < mNumMissionStages )
+ {
+ return( mMissionStages[ index ] );
+ }
+ else
+ {
+ return( NULL );
+ }
+}
+
+//=============================================================================
+// Mission::IsHUDVisible
+//=============================================================================
+// Description: Comment
+//
+// Parameters:
+//
+// Return: inline
+//
+//=============================================================================
+inline bool Mission::IsHUDVisible() const
+{
+ return mShowHUD;
+}
+
+inline void Mission::SetNumValidFailureHints( int numHints )
+{
+ mNumValidFailureHints = numHints;
+}
+
+inline int Mission::GetNumValidFailureHints() const
+{
+ return mNumValidFailureHints;
+}
+
+#endif //MISSION_H
diff --git a/game/code/mission/missionmanager.cpp b/game/code/mission/missionmanager.cpp
new file mode 100644
index 0000000..3f0fff7
--- /dev/null
+++ b/game/code/mission/missionmanager.cpp
@@ -0,0 +1,1126 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: missionmanager.cpp
+//
+// Description: Implement MissionManager
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <ai/vehicle/vehicleai.h>
+
+#include <events/eventmanager.h>
+
+#include <contexts/context.h>
+#include <contexts/pausecontext.h>
+#include <gameflow/gameflow.h>
+
+#include <loading/loadingmanager.h>
+
+#include <meta/carstartlocator.h>
+#include <meta/eventlocator.h>
+#include <meta/locator.h>
+#include <meta/triggervolumetracker.h>
+#include <meta/zoneeventlocator.h>
+#include <events/eventenum.h>
+#include <meta/eventlocator.h>
+
+#include <data/gamedatamanager.h>
+#include <data/PersistentWorldManager.h>
+
+#include <mission/mission.h>
+#include <mission/missionmanager.h>
+#include <mission/missionstage.h>
+#include <mission/missionscriptloader.h>
+#include <mission/conditions/missioncondition.h>
+#include <mission/objectives/deliveryobjective.h>
+#include <mission/objectives/gotoobjective.h>
+#include <mission/objectives/missionobjective.h>
+#include <mission/rewards/rewardsmanager.h>
+#include <mission/rewards/reward.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/backend/guimanagerbackend.h>
+#include <presentation/gui/ingame/guiscreenmissionload.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/RenderLayer.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/loaders/billboardwrappedloader.h>
+
+#include <worldsim/avatarmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/worldobject.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <memory/srrmemory.h>
+
+#include <mission/animatedicon.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+MissionManager* MissionManager::spInstance = NULL;
+
+
+#ifdef DEBUGWATCH
+static bool gResetCurrentMission = false;
+#endif
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// MissionManager::GetInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: MissionManager
+//
+//=============================================================================
+MissionManager* MissionManager::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+//=============================================================================
+// MissionManager::CreateInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: MissionManager
+//
+//=============================================================================
+MissionManager* MissionManager::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "MissionManager" );
+ if( spInstance == NULL )
+ {
+ spInstance = new(GMA_PERSISTENT) MissionManager;
+ rAssert( spInstance );
+ }
+MEMTRACK_POP_GROUP( "MissionManager" );
+
+ return spInstance;
+}
+
+//=============================================================================
+// MissionManager::DestroyInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::DestroyInstance()
+{
+ if( spInstance != NULL )
+ {
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+ }
+}
+
+//==============================================================================
+// MissionManager::MissionManager
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MissionManager::MissionManager() :
+ mLoadingState( STATE_INVALID ),
+ mMissionState( MISSION_INVALID ),
+ mIsSundayDrive( true ),
+ mResetting( true ),
+ mHAHACK( false ),
+ mCollectionEffect( NULL )
+{
+ SetNumPlayers( 1 );
+
+ mLastFileName[0] = '\0';
+
+#ifdef DEBUGWATCH
+ radDbgWatchAddBoolean( &gResetCurrentMission, "Reset Current Mission", "Mission Manager" );
+#endif
+}
+
+//==============================================================================
+// MissionManager::~MissionManager
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MissionManager::~MissionManager()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &gResetCurrentMission );
+#endif
+
+ if ( mCollectionEffect )
+ {
+ delete mCollectionEffect;
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// MissionManager::Initialize
+//=============================================================================
+// Description: Fucking Chuck keeps shooting me
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::Initialize()
+{
+ //****** Dirty Hacky to get Replay to work, ask Carey More about this !!!!!!! Chuck 0ct12.
+ //Missed this one... Thanks Chuck. This makes the SD/actual mission thing work.
+ //Remind me to hunt down and kill Darryl for this.
+ mResetting=true;
+ GameplayManager::Initialize();
+
+ Reward* defaultReward = NULL;
+
+ // unlock default car for current level
+ //
+ defaultReward = GetRewardsManager()->GetReward( this->GetCurrentLevelIndex(), Reward::eDefaultCar );
+ rAssert( defaultReward );
+ if ( defaultReward != NULL )
+ {
+ defaultReward->UnlockReward();
+
+ // unlock default skin for current level
+ //
+
+ defaultReward = GetRewardsManager()->GetReward( this->GetCurrentLevelIndex(), Reward::eDefaultSkin );
+ rAssert( defaultReward );
+ if ( defaultReward != NULL )
+ defaultReward->UnlockReward();
+ }
+
+ GetEventManager()->AddListener( this, EVENT_FIRST_DYNAMIC_ZONE_END );
+ GetEventManager()->AddListener( this, EVENT_GUI_LEAVING_PAUSE_MENU );
+}
+
+//=============================================================================
+// MissionManager::Finalize
+//=============================================================================
+// Description: Take the vein train down the A-tube
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::Finalize()
+{
+ GetEventManager()->RemoveAll( this );
+
+ if ( mCollectionEffect )
+ {
+ delete mCollectionEffect;
+ mCollectionEffect = NULL;
+ }
+
+ GameplayManager::Finalize();
+}
+
+//=============================================================================
+// MissionManager
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::Update( int elapsedTime )
+{
+#ifdef DEBUGWATCH
+ if ( gResetCurrentMission )
+ {
+ RestartCurrentMission();
+ gResetCurrentMission = false;
+ }
+#endif
+
+ if( mLoadingState != STATE_INVALID )
+ {
+ PerformLoading();
+ }
+ else if ( mMissionState == MISSION_INIT )
+ {
+ //We're here because the loading is all done, and it is time to set up the current mission.
+ //I think this is better than an incestuous relationship with the GUI... WTF Darryl?
+
+ Mission* currentMission = GetCurrentMission();
+ currentMission->Reset( mResetting );
+
+ //Start right away.
+ currentMission->GetCurrentStage()->Start();
+ mMissionState = MISSION_RUNNING;
+
+ if ( currentMission->IsSundayDrive() )
+ {
+ //Reenable the bonus missions.
+ EnableBonusMissions();
+ }
+ else
+ {
+ HeapMgr()->PushHeap (GMA_LEVEL_MISSION);
+ GetEventManager()->TriggerEvent( EVENT_MISSION_START, reinterpret_cast< void* >( currentMission ) );
+ HeapMgr()->PopHeap (GMA_LEVEL_MISSION);
+
+ //No bonus missions while we're in a mission.
+ DisableBonusMissions();
+ }
+
+ if ( mResetting || currentMission->IsForcedCar() || currentMission->IsRaceMission() )
+ {
+ if ( mResetting || mMissionState != MISSION_RUNNING )
+ {
+ currentMission->ResetPlayer();
+ GetEventManager()->TriggerEvent( EVENT_MISSION_CHARACTER_RESET, (void*)1 );
+// GetCharacterManager()->Update(0.033f);
+ BEGIN_PROFILE("WorldPhysics");
+ GetWorldPhysicsManager()->Update(33);
+ END_PROFILE("WorldPhysics");
+ }
+
+ mResetting = false;
+
+ //HACK
+ if ( mHAHACK )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_RESUME_INGAME );
+ mHAHACK = false;
+ }
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_MISSION_CHARACTER_RESET, (void*)0 );
+ }
+ }
+ else if ( mMissionState == MISSION_SUSPEND )
+ {
+ GetEventManager()->RemoveListener( GetCurrentMission(), EVENT_GUI_MISSION_START );
+ mMissionState = MISSION_RUNNING;
+ }
+ else
+ {
+ GameplayManager::Update( elapsedTime );
+
+ if ( mMissionState == MISSION_RUNNING && mLoadingState == STATE_INVALID )
+ {
+ if ( mIrisClosed )
+ {
+ PauseForIrisOpen();
+ }
+ else if ( mFadedToBlack )
+ {
+ //check if there is manual control of the fade in/out
+ //this could be called by the mission stage swapcarstart
+ //to hide the loading
+ if (QueryManualControlFade() !=true)
+ {
+ PauseForFadeFromBlack();
+ }
+ }
+ }
+ }
+
+ if ( mCollectionEffect )
+ {
+ mCollectionEffect->Update( elapsedTime );
+ }
+}
+
+//=============================================================================
+// MissionManager::Reset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::Reset()
+{
+ // start the first mission
+
+ Mission* mission = GetCurrentMission();
+ rAssert( mission != NULL );
+
+ mission->Reset();
+
+ MissionStage* stage = mission->GetCurrentStage();
+ rAssert( stage != NULL );
+ stage->Start();
+
+ HeapMgr()->PushHeap (GMA_LEVEL_MISSION);
+ GetEventManager()->TriggerEvent( EVENT_MISSION_START );
+ HeapMgr()->PopHeap (GMA_LEVEL_MISSION);
+
+ //Hmmm. Disable the bonus missions.
+ DisableBonusMissions();
+}
+
+//=============================================================================
+// virtual void HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: virtual
+//
+//=============================================================================
+void MissionManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ if( mLoadingState == STATE_WAIT_FOR_DYNALOAD && id == EVENT_FIRST_DYNAMIC_ZONE_END )
+ {
+ mLoadingState = STATE_MISSION_START;
+
+ if ( !GetCurrentMission()->IsSundayDrive() && CommandLineOptions::Get( CLO_SKIP_SUNDAY ) || mIsDemo )
+ {
+ if ( mIsDemo )
+ {
+ if ( GetGameFlow()->GetNextContext() != CONTEXT_DEMO )
+ {
+ GetGameFlow()->SetContext( CONTEXT_DEMO );
+ }
+ }
+ else
+ {
+ rTuneAssert(GetGameFlow()->GetCurrentContext() != CONTEXT_PAUSE && GetGameFlow()->GetNextContext() != CONTEXT_PAUSE && GetGameFlow()->GetNextContext() != CONTEXT_GAMEPLAY);
+ if (GetGameFlow()->GetCurrentContext() != CONTEXT_PAUSE && GetGameFlow()->GetNextContext() != CONTEXT_PAUSE && GetGameFlow()->GetNextContext() != CONTEXT_GAMEPLAY )
+ {
+ //MAYBE bad - could return us to gameplay context while in pause screen
+ GetGameFlow()->SetContext( CONTEXT_GAMEPLAY );
+ }
+ }
+ }
+ }
+ else if ( id == EVENT_GUI_LEAVING_PAUSE_MENU )
+ {
+ if ( mMissionState != MISSION_SUSPEND )
+ {
+ //Reenable the gameplay...
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Resume();
+ }
+ }
+ else
+ {
+ Mission* pMission = GetCurrentMission();
+ if( pMission != NULL )
+ {
+ pMission->HandleEvent( id, pEventData );
+ }
+ }
+
+ GameplayManager::HandleEvent(id, pEventData);
+}
+
+//=============================================================================
+// MissionManager::PerformLoading
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::PerformLoading()
+{
+MEMTRACK_PUSH_GROUP( "MissionManager - Loading" );
+ switch( mLoadingState )
+ {
+ case STATE_LEVEL:
+ case STATE_MISSION_LOADING:
+ {
+ if( GetLoadingManager()->IsLoading() || GetVehicleCentral()->GetCurrentVehicleUnderConstruction() != NULL )
+ {
+ break;
+ }
+
+ mLoadingState = STATE_MISSION_INIT;
+ break;
+ }
+ case STATE_MISSION_INITING:
+ {
+ if( GetLoadingManager()->IsLoading() || GetVehicleCentral()->GetCurrentVehicleUnderConstruction() != NULL)
+ {
+ break;
+ }
+
+ mLoadingState = STATE_MISSION_DYNALOAD;
+ break;
+ }
+ case STATE_MISSION_LOAD:
+ {
+ #ifdef RAD_DEBUG
+ static int missionsLoaded = 0;
+ ++missionsLoaded;
+ char label[ 256 ];
+ sprintf( label, "mission%d", missionsLoaded );
+ SetMemoryIdentification( label );
+ #endif
+ mLoadingState = STATE_MISSION_LOADING;
+
+ Mission* mission = GetCurrentMission();
+ rAssert( mission != NULL );
+
+ //Load the mission data.
+ int levelnum = GetCurrentLevelIndex() + 1;
+ sprintf( mLastFileName, "scripts\\missions\\level0%d\\%sl.mfk", levelnum, mission->GetName() );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+ GetMissionScriptLoader()->SetFileHander( FILEHANDLER_MISSION );
+ GetMissionScriptLoader()->LoadScriptAsync( mLastFileName );
+ HeapMgr()->PopHeap(GMA_LEVEL_MISSION);
+
+ break;
+ }
+ case STATE_MISSION_INIT:
+ {
+ HeapMgr()->PushHeap (GMA_LEVEL_MISSION);
+
+ CGuiManagerInGame* pCGuiManagerInGame = NULL;
+
+ pCGuiManagerInGame= GetGuiSystem()->GetInGameManager();
+ //if the screen is black or there is a loading screen up
+ //can we clean up cars.
+
+ if (pCGuiManagerInGame != NULL)
+ {
+ if ( GetGameplayManager()->IsIrisClosed()
+ ||
+ pCGuiManagerInGame->IsLoadingNewMissionInSundayDrive()
+ ||
+ pCGuiManagerInGame->IsLoadingNewMission()
+ )
+ {
+ GetGameplayManager()->MDKVDU();
+
+ }
+ }
+ mLoadingState = STATE_MISSION_INITING;
+
+ Mission* mission = GetCurrentMission();
+ rAssert( mission != NULL );
+
+ // MS9: See previous cursing
+ GetTriggerVolumeTracker()->SetTriggerSphere( p3d::find<tDrawable>( "triggersphere_000" ));
+
+ rAssert( mCollectionEffect == NULL );
+
+ //Set up the collection effect.
+ mCollectionEffect = new AnimatedIcon();
+ mCollectionEffect->Init( "mission_col", rmt::Vector( 0.0f, 0.0f, 0.0f ), false, true );
+
+ //Setup the default car if required
+ if ( mShouldLoadDefaultVehicle )
+ {
+ AddLevelVehicle( mDefaultLevelVehicleName, eDefaultCar, mDefaultLevelVehicleConfile );
+ PlaceVehicleAtLocatorName( GetVehicleInSlot( eDefaultCar ), mDefaultLevelVehicleLocator );
+ SetCurrentVehicle( GetVehicleInSlot( eDefaultCar ) );
+
+ mShouldLoadDefaultVehicle = false;
+ }
+
+ char filename[128];
+
+ //Init the mission data
+ int levelnum = GetCurrentLevelIndex() + 1;
+
+ GetCharacterManager()->ClearInitialWalk();
+
+ sprintf( filename, "scripts\\missions\\level0%d\\%si.mfk", levelnum, mission->GetName() );
+
+ GetMissionScriptLoader()->SetFileHander( FILEHANDLER_MISSION );
+ GetMissionScriptLoader()->LoadScriptAsync( filename );
+
+ //
+ // NOTE: if the script gets changed to load things (which it shouldn't)
+ // then this should call ProcessRequests instead, and STATE_MISSION_INITING
+ // should just break (since the gameplaymanager is spinning)
+ //
+ PerformLoading();
+ //GetLoadingManager()->ProcessRequests( this );
+
+ HeapMgr()->PopHeap (GMA_LEVEL_MISSION);
+
+ break;
+ }
+ case STATE_MISSION_DYNALOAD:
+ {
+ Mission* mission = GetCurrentMission();
+
+ mission->Initialize( GetCurrentMissionHeap() );
+
+ mLoadingState = STATE_WAIT_FOR_DYNALOAD;
+ //chuck remove the default vehicle if user has selected a traffic car as current car
+
+ if ( GetCurrentVehicle() != mVehicleSlots[eDefaultCar].mp_vehicle)
+ {
+ ClearVehicleSlot(eDefaultCar);
+ }
+
+ //check if we are in the orange loading screen. if we are then lets clear some traffic
+ //so we dont have user traffic cars spawning on a parked user traffic car.
+ if (
+ (GetGuiSystem()->GetBackendManager()->GetCurrentScreen() == CGuiWindow::GUI_SCREEN_ID_LOADING)
+
+ &&
+ GetGuiSystem()->GetBackendManager()->GetCurrentWindow()->IsRunning()
+
+ )
+ {
+ rmt::Vector position(0,0,0);
+ rmt::Sphere nuke(position, 1000.0f);
+ TrafficManager::GetInstance()->ClearTrafficInSphere(nuke);
+ GetGameplayManager()->MDKVDU();
+ }
+
+ if ( mResetting )
+ {
+ //HeapMgr()->PushHeap (GMA_LEVEL_ZONE); // Is this the right one?? -- jdy
+ mission->InitDynaLoad();
+ //HeapMgr()->PopHeap ();
+ }
+
+ // Really there should always be stages, but some of
+ // the test levels don't have any
+ //
+ if( mission->GetNumStages() == 0 || !mResetting ) // || !mission->GetStage( 0 )->LoadMissionStart() )
+ {
+ mLoadingState = STATE_MISSION_START;
+ }
+
+ break;
+ }
+ case STATE_WAIT_FOR_DYNALOAD:
+ {
+ break;
+ }
+ case STATE_MISSION_START:
+ {
+ if( GetCurrentMission()->IsSundayDrive() )
+ {
+
+ GameFlow* gf = GetGameFlow();
+ ContextEnum currcon = gf->GetCurrentContext();
+ ContextEnum nextcon = gf->GetNextContext();
+
+ //Try this.
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_DEMO )
+ {
+ GetGameFlow()->SetContext( CONTEXT_DEMO );
+ }
+ else if ( GetGameFlow()->GetNextContext() != CONTEXT_GAMEPLAY )
+ {
+ if ( GetGameFlow()->GetCurrentContext() != CONTEXT_PAUSE ||
+ ( GetGameFlow()->GetCurrentContext() == CONTEXT_PAUSE && GetPauseContext()->IsWaitingForContextSwitch()))
+ {
+ GetGameFlow()->SetContext( CONTEXT_GAMEPLAY );
+ GetPauseContext()->SetWaitingForContextSwitch(false);
+ }
+ }
+ //set the Dirty Flag in the player car info struct to record stuff
+ mPlayerAndCarInfo.mbDirtyFlag = false;
+ }
+ else
+ {
+ HeapMgr()->PushHeap (GMA_LEVEL_MISSION);
+
+ //Chuck: Do our exception handling while we haven't officially started the mission
+
+ Mission* pCurrentMission= NULL;
+
+ pCurrentMission = GetCurrentMission();
+
+ //if we are going into a new non sunday check to see if we can repair the users car
+ //if and only if its a non main car ie traffic etc
+ if (pCurrentMission->IsSundayDrive() != true)
+ {
+ if(GetCurrentVehicle() != NULL)
+ {
+ if (GetCurrentVehicle()->IsVehicleDestroyed() == true)
+ {
+ //if the car is not owned by the player then it could be a traffic car free car or car from a cheat at the phone booth
+ // if car index returns a -1 then the car is not owned by the player
+ if (GetCurrentVehicle()->mVehicleType == VT_TRAFFIC)
+ {
+ GetEventManager()->TriggerEvent(EVENT_REPAIR_CAR);
+ }
+ }
+ }
+ }
+
+
+ //clear traffic at the start of these mission as per designer request
+ if (pCurrentMission->IsWagerMission() ==true)
+ {
+ //remove traffic
+ //get a traffic ptr
+ ITrafficSpawnController* pTraffic = TrafficManager::GetSpawnController();
+ pTraffic->DisableTraffic();
+ rmt::Vector vector;
+ GetCharacterManager()->GetCharacter(0)->GetPosition(&vector);
+ rmt::Sphere sphere (vector, 400.0f);
+ pTraffic->ClearTrafficInSphere( sphere);
+ }
+
+ // if the user is using something other than their default car at the start of these missions then remove them.
+ if(pCurrentMission->IsSundayDrive() != true )
+ {
+ //also remove the user default car if they have hijacked a traffic or parked car or etc so as not to impede the race
+ if (GetCurrentVehicle() != mVehicleSlots[eDefaultCar].mp_vehicle)
+ {
+ ClearVehicleSlot(eDefaultCar);
+ }
+ }
+
+ if (pCurrentMission->IsForcedCar() == true)
+ {
+ //record players position and location of forced car spawn pt
+
+ if(mPlayerAndCarInfo.mbDirtyFlag != true)
+ {
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter()->GetPosition( mPlayerAndCarInfo.mPlayerPosition);
+ pCurrentMission->GetVehicleRestart()->GetLocation(&mPlayerAndCarInfo.mForceLocation);
+ mPlayerAndCarInfo.mbDirtyFlag = true;
+ }
+ }
+
+ Mission* mission = GetCurrentMission();
+ mission->Reset();
+
+ if (GetGameplayManager()->IsIrisClosed() == true)
+ {
+ GetGameplayManager()->PauseForIrisOpen( 1.0f);
+ }
+
+
+ if ( mResetting || mission->IsForcedCar() || mission->IsRaceMission() )
+ {
+ GetCurrentMission()->ResetPlayer();
+// GetCharacterManager()->Update(0.033f);
+ BEGIN_PROFILE("WorldPhysics");
+ GetWorldPhysicsManager()->Update(33);
+ END_PROFILE("WorldPhysics");
+ }
+
+ // Tell the GUI to hide the mission loading screen
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_INGAME_MISSION_LOAD_END );
+
+ HeapMgr()->PopHeap (GMA_LEVEL_MISSION);
+ }
+
+ mLoadingState = STATE_INVALID;
+
+ mMissionState = MISSION_INIT;
+
+ break;
+ }
+ case STATE_INVALID:
+ {
+ break;
+ }
+ default:
+ // fook!
+ rAssert( false );
+ break;
+ }
+MEMTRACK_POP_GROUP( "MissionManager - Loading" );
+}
+
+//=============================================================================
+// MissionManager::RestartCurrentMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::RestartCurrentMission()
+{
+ mResetting = true;
+
+ GetVehicleCentral()->DetachAllCollectibles();
+
+ //Oh boy.. We need to dump everything else and don't know what it is...
+ //GetRenderManager()->pWorldRenderLayer()->DumpAllDynaLoads( 1, GetRenderManager()->mEntityDeletionList);
+ GetRenderManager()->mpLayer( RenderEnums::LevelSlot )->DumpAllDynaLoads(1, GetRenderManager()->mEntityDeletionList );
+
+ SetCurrentMission( GetCurrentMissionNum() );
+}
+
+//=============================================================================
+// MissionManager::RestartToMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( RenderEnums::MissionEnum mission )
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::RestartToMission( RenderEnums::MissionEnum mission )
+{
+ mResetting = true;
+
+ //Oh boy.. We need to dump everything else and don't know what it is...
+ //GetRenderManager()->pWorldRenderLayer()->DumpAllDynaLoads( 1, GetRenderManager()->mEntityDeletionList );
+ GetRenderManager()->mpLayer( RenderEnums::LevelSlot )->DumpAllDynaLoads(1, GetRenderManager()->mEntityDeletionList );
+
+ GetCharacterManager()->AllowGarbageCollection(true);
+ GetCharacterManager()->GarbageCollect(true);
+
+ SetCurrentMission( mission * 2 + mSkipSunday );
+
+ //HACK: until the screen thing is resolved by Carey and Joe.
+ mHAHACK = true;
+}
+
+//=============================================================================
+// MissionManager::AbortCurrentMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::AbortCurrentMission()
+{
+ rAssert( !GetCurrentMission()->IsSundayDrive() ); //This should NEVER happen.
+
+ if ( GetCurrentMission()->IsForcedCar() )
+ {
+ PauseForIrisClose( 1.0f );
+ mShouldLoadDefaultVehicle = true; //This is hackish
+ }
+
+ PrevMission(); //This should take us to the SD for this mission.
+}
+
+
+//=============================================================================
+// MissionManager::LoadLevelData
+//=============================================================================
+// Description:
+//
+// Parameters: ( char* name )
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::LoadLevelData()
+{
+ mLoadingState = STATE_LEVEL;
+
+ char name[128];
+
+ if ( !mIsDemo )
+ {
+#ifdef RAD_DEMO
+ if( GetCurrentLevelIndex() == RenderEnums::L7 )
+ {
+ sprintf( name, "scripts\\missions\\level0%d\\level-d.mfk", GetCurrentLevelIndex() + 1 );
+ }
+ else
+#endif
+ {
+ sprintf( name, "scripts\\missions\\level0%d\\level.mfk", GetCurrentLevelIndex() + 1 );
+ }
+ }
+ else
+ {
+ sprintf( name, "scripts\\missions\\level0%d\\demo.mfk", GetCurrentLevelIndex() + 1 );
+ }
+
+ GetPersistentWorldManager()->OnLevelLoad( GetCurrentLevelIndex() );
+ BillboardWrappedLoader::OverrideLoader( true );
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER ); // I choose other because this stuff will exist longer than a single mission
+ GetMissionScriptLoader()->SetFileHander( FILEHANDLER_LEVEL );
+ GetMissionScriptLoader()->LoadScriptAsync( name );
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+ BillboardWrappedLoader::OverrideLoader( false );
+}
+
+//=============================================================================
+// MissionManager::InitLevelData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::InitLevelData()
+{
+ //WARNING! NO data should be loaded here!
+
+ char name[128];
+
+ if ( !mIsDemo )
+ {
+#ifdef RAD_DEMO
+ if( GetCurrentLevelIndex() == RenderEnums::L7 )
+ {
+ sprintf( name, "scripts\\missions\\level0%d\\leveli-d.mfk", GetCurrentLevelIndex() + 1 );
+ }
+ else
+#endif
+ {
+ sprintf( name, "scripts\\missions\\level0%d\\leveli.mfk", GetCurrentLevelIndex() + 1 );
+ }
+ }
+ else
+ {
+ sprintf( name, "scripts\\missions\\level0%d\\demoi.mfk", GetCurrentLevelIndex() + 1 );
+ }
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ GetMissionScriptLoader()->SetFileHander( FILEHANDLER_LEVEL );
+ GetMissionScriptLoader()->LoadScriptAsync( name );
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+}
+
+//=============================================================================
+// MissionManager::OnProcessRequestsComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::OnProcessRequestsComplete( void* pUserData )
+{
+ switch( mLoadingState )
+ {
+ case STATE_MISSION_LOADING:
+ {
+
+ //Turn off the override.
+ BillboardWrappedLoader::OverrideLoader( false );
+ mLoadingState = STATE_MISSION_INIT;
+
+ break;
+ }
+ case STATE_MISSION_INITING:
+ {
+ mLoadingState = STATE_MISSION_DYNALOAD;
+ break;
+ }
+ default:
+ // fook!
+ rAssert( false );
+ }
+ GetRenderManager()->pWorldRenderLayer()->DoPostDynaLoad();
+
+}
+
+//=============================================================================
+// MissionManager::LoadMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::LoadMission()
+{
+
+ //If this is a Sunday Drive mission, we are not going to load anything.
+ //But we should display the new post-mission screen.
+ //TODO POST MISSION SCREEN
+
+ if ( !GetCurrentMission()->IsSundayDrive() && !CommandLineOptions::Get( CLO_SKIP_SUNDAY ) )
+ {
+ // Tell the GUI to show the mission loading screen
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_INGAME_MISSION_LOAD_BEGIN );
+
+ //What the heck is this for? TODO: Remove?
+ //GetEventManager()->TriggerEvent( EVENT_MISSION_INTRO );
+ }
+
+ // Clear our the p3d inventory
+ //
+ p3d::pddi->DrawSync();
+ p3d::inventory->RemoveSectionElements( "Mission" );
+
+ if ( mCollectionEffect )
+ {
+ delete mCollectionEffect;
+ mCollectionEffect = NULL;
+ }
+
+ // Instead of processing mission flow, each Update performs loading
+ //
+ mLoadingState = STATE_MISSION_LOAD;
+}
+
+//=============================================================================
+// MissionManager::CleanMissionData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::CleanMissionData()
+{
+//DEVIN LOOK AT THIS EVIL Memory stompage!!!!!
+ p3d::pddi->DrawSync();
+
+ if ( mLastFileName[0] != '\0' )
+ {
+ HeapMgr()->PushHeap (GMA_TEMP);
+ tName name( mLastFileName );
+ HeapMgr()->PopHeap (GMA_TEMP);
+/*
+ //Doing this because dumps were occuring in the middle of other loads
+ //which = BADNESS
+ char tempName[100];
+ strcpy( tempName, mLastFileName );
+ tempName[strlen(tempName)] = ':';
+ tempName[strlen(tempName)] = '\0';
+
+ ZoneEventLocator* pZEL = new(GMA_TEMP) ZoneEventLocator;
+ pZEL->SetName("gawdDamn");
+ pZEL->AddRef();
+ pZEL->AddRef();
+ pZEL->SetZoneSize( strlen(tempName) + 3 );
+ pZEL->SetZone( tempName );
+ pZEL->SetPlayerEntered();
+
+ GetEventManager()->TriggerEvent( (EventEnum)(EVENT_LOCATOR+LocatorEvent::DYNAMIC_ZONE),
+ static_cast<void*>( pZEL ) );
+
+ //THIS IS A LEAK!
+
+ */
+ GetRenderManager()->pWorldRenderLayer()->DumpDynaLoad( name, GetRenderManager()->mEntityDeletionList );
+ }
+
+ p3d::inventory->PushSection();
+
+ HeapMgr()->PushHeap (GMA_LEVEL_MISSION);
+ p3d::inventory->SelectSection( "Mission" );
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+ tInventory::Iterator<Locator> iterator;
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ Locator* loc = iterator.First();
+
+ while( loc != NULL )
+ {
+ TriggerVolume* volume;
+
+ unsigned int i;
+ for( i = 0; i < loc->GetNumTriggers(); i++ )
+ {
+ volume = reinterpret_cast<TriggerLocator*>(loc)->GetTriggerVolume( i );
+ GetTriggerVolumeTracker()->RemoveTrigger( volume );
+ }
+
+ loc = iterator.Next();
+ }
+
+ if ( mCollectionEffect )
+ {
+ delete mCollectionEffect;
+ mCollectionEffect = NULL;
+ }
+
+ //
+ // TODO: This string shouldn't be hardcoded
+ //
+ p3d::inventory->RemoveSectionElements( "Mission" );
+ HeapMgr()->PopHeap (GMA_LEVEL_MISSION);
+
+ p3d::inventory->PopSection();
+
+}
+
+//=============================================================================
+// MissionManager::PutEffectHere
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& pos )
+//
+// Return: void
+//
+//=============================================================================
+void MissionManager::PutEffectHere( const rmt::Vector& pos )
+{
+ mCollectionEffect->Reset();
+ mCollectionEffect->Move( pos );
+ mCollectionEffect->ShouldRender( true );
+}
diff --git a/game/code/mission/missionmanager.h b/game/code/mission/missionmanager.h
new file mode 100644
index 0000000..89c9720
--- /dev/null
+++ b/game/code/mission/missionmanager.h
@@ -0,0 +1,130 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: missionmanager.h
+//
+// Description: Blahblahblah
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef MISSIONMANAGER_H
+#define MISSIONMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+#include <mission/gameplaymanager.h>
+
+//========================================
+// Forward References
+//========================================
+class AnimatedIcon;
+
+//=============================================================================
+//
+// Synopsis: Loads two missions and keeps one active while the other is
+// held in reserve. Takes the player through all the missions
+// in a level until the level is complete.
+//
+//=============================================================================
+
+class MissionManager : public GameplayManager,
+ public LoadingManager::ProcessRequestsCallback
+{
+ public:
+
+ enum LoadingStateEnum {
+ STATE_INVALID,
+ STATE_LEVEL,
+ STATE_MISSION_LOAD,
+ STATE_MISSION_LOADING,
+ STATE_MISSION_INIT,
+ STATE_MISSION_INITING,
+ STATE_MISSION_DYNALOAD,
+ STATE_WAIT_FOR_DYNALOAD,
+ STATE_MISSION_START,
+ NUM_STATES
+ };
+
+
+
+ static MissionManager* GetInstance();
+ static MissionManager* CreateInstance();
+ static void DestroyInstance();
+
+ virtual void Initialize();
+ virtual void Finalize();
+ virtual void Reset();
+
+ virtual void LoadLevelData();
+ virtual void InitLevelData();
+ virtual void CleanMissionData();
+
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+ virtual void Update( int elapsedTime );
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ virtual void PerformLoading();
+
+ virtual bool IsSundayDrive() { return GetCurrentMission() ? GetCurrentMission()->IsSundayDrive() : true; };
+
+ virtual void RestartCurrentMission();
+ virtual void RestartToMission( RenderEnums::MissionEnum mission );
+ virtual void AbortCurrentMission();
+
+ void PutEffectHere( const rmt::Vector& pos );
+ bool InResetState(){ return mResetting; };
+
+ LoadingStateEnum GetLoadingState () { return mLoadingState; };
+
+
+
+
+protected:
+ MissionManager();
+ ~MissionManager();
+
+ virtual void LoadMission();
+ private:
+ //Prevent wasteful constructor creation.
+ MissionManager( const MissionManager& missionManager );
+ MissionManager& operator=( const MissionManager& missionManager );
+
+ // Pointer to the one and only instance of this singleton.
+ static MissionManager* spInstance;
+
+
+ LoadingStateEnum mLoadingState;
+
+ enum MissionStateEnum {
+ MISSION_INVALID,
+ MISSION_LOADING,
+ MISSION_INIT,
+ MISSION_SUSPEND,
+ MISSION_RUNNING,
+ MISSION_NUM_STATES
+ };
+
+ MissionStateEnum mMissionState;
+
+ char mLastFileName[256];
+
+ bool mIsSundayDrive;
+
+ bool mResetting;
+
+ //HACK
+ bool mHAHACK;
+
+ AnimatedIcon* mCollectionEffect;
+};
+
+inline MissionManager* GetMissionManager() { return( MissionManager::GetInstance() ); }
+
+#endif //MISSIONMANAGER_H
diff --git a/game/code/mission/missionscriptloader.cpp b/game/code/mission/missionscriptloader.cpp
new file mode 100644
index 0000000..03b9009
--- /dev/null
+++ b/game/code/mission/missionscriptloader.cpp
@@ -0,0 +1,5678 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement MissionScriptLoader
+//
+// History: 12/06/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <math.h>
+#include <string.h>
+#include <radtextdisplay.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <console/console.h>
+
+#include <ai/vehicle/vehicleai.h>
+#include <ai/vehicle/waypointai.h>
+#include <ai/vehicle/chaseai.h>
+#include <camera/animatedcam.h>
+#include <camera/conversationcam.h>
+#include <camera/supercam.h>
+#include <camera/supercammanager.h>
+#include <meta/carstartlocator.h>
+#include <meta/eventlocator.h>
+#include <meta/locator.h>
+
+#include <mission/mission.h>
+#include <mission/missionmanager.h>
+#include <mission/missionscriptloader.h>
+#include <mission/missionstage.h>
+#include <mission/nodamagebonusobjective.h>
+#include <mission/nocopbonusobjective.h>
+#include <mission/timeremainbonusobjective.h>
+#include <mission/racepositionbonusobjective.h>
+
+#include <mission/conditions/vehiclecarryingstateprop.h>
+#include <mission/conditions/damagecondition.h>
+#include <mission/conditions/followcondition.h>
+#include <mission/conditions/missioncondition.h>
+#include <mission/conditions/outofboundscondition.h>
+#include <mission/conditions/timeoutcondition.h>
+#include <mission/conditions/racecondition.h>
+#include <mission/conditions/leaveinteriorcondition.h>
+#include <mission/conditions/positioncondition.h>
+#include <mission/conditions/getoutofcarcondition.h>
+#include <mission/conditions/notabductedcondition.h>
+#include <mission/conditions/keepbarrelcondition.h>
+
+#include <mission/objectives/loadvehicleobjective.h>
+#include <mission/objectives/deliveryobjective.h>
+#include <mission/objectives/destroyobjective.h>
+#include <mission/objectives/followobjective.h>
+#include <mission/objectives/gotoobjective.h>
+#include <mission/objectives/loseobjective.h>
+#include <mission/objectives/missionobjective.h>
+#include <mission/objectives/raceobjective.h>
+#include <mission/objectives/talktoobjective.h>
+#include <mission/objectives/dialogueobjective.h>
+#include <mission/objectives/getinobjective.h>
+#include <mission/objectives/collectdumpedobjective.h>
+#include <mission/objectives/fmvobjective.h>
+#include <mission/objectives/interiorobjective.h>
+#include <mission/objectives/coinobjective.h>
+#include <mission/objectives/destroybossobjective.h>
+#include <mission/objectives/pickupitemobjective.h>
+#include <mission/objectives/timerobjective.h>
+#include <mission/objectives/buycarobjective.h>
+#include <mission/objectives/buyskinobjective.h>
+#include <mission/objectives/gooutsideobjective.h>
+
+#include <presentation/tutorialmanager.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/backend/guimanagerbackend.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guiscreenmissionload.h>
+
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/rewards/rewardsmanager.h>
+
+#include <mission/animatedicon.h>
+
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <render/rendermanager/renderlayer.h>
+
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <ai/actionbuttonmanager.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/harass/chasemanager.h>
+#include <worldsim/hitnrunmanager.h>
+
+#include <presentation/presentation.h>
+#include <mission/statepropcollectible.h>
+
+#include <events/eventmanager.h>
+#include <events/eventenum.h>
+
+#include <camera/supercam.h>
+
+#include <ai/actor/actormanager.h>
+
+#include <stdlib.h>
+
+#include <memory/srrmemory.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/traffic/trafficmodelgroup.h>
+#include <worldsim/ped/pedestrianmanager.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/coins/sparkle.h>
+
+#include <loading/loadingmanager.h>
+#include <mission/ufo/ufo.h>
+#include <mission/ufo/tractorbeam.h>
+
+#include <render/Loaders/AnimDynaPhysLoader.h>
+
+#include <gameflow/gameflow.h>
+#include <contexts/demo/democontext.h>
+#include <contexts/gameplay/gameplaycontext.h>
+
+#include <worldsim/parkedcars/parkedcarmanager.h>
+#include <supersprint/supersprintmanager.h>
+
+#include <meta/spheretriggervolume.h>
+#include <meta/actioneventlocator.h>
+#include <meta/triggervolumetracker.h>
+
+#include <atc/atcmanager.h>
+#include <stateprop/statepropdata.hpp>
+
+#include <interiors/interiormanager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+MissionScriptLoader* MissionScriptLoader::spInstance = 0;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// MissionScriptLoader::CreateInstance
+//==============================================================================
+//
+// Description: Creates the MissionScriptLoader.
+//
+// Parameters: None.
+//
+// Return: Pointer to the MissionScriptLoader.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+MissionScriptLoader* MissionScriptLoader::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "MissionScriptLoader" );
+ rAssert( spInstance == 0 );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+
+ spInstance = new MissionScriptLoader;
+ rAssert( spInstance );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+
+ spInstance->Register();
+
+MEMTRACK_POP_GROUP( "MissionScriptLoader" );
+ return spInstance;
+}
+
+//==============================================================================
+// MissionScriptLoader::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the MissionScriptLoader singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the MissionScriptLoader.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+MissionScriptLoader* MissionScriptLoader::GetInstance()
+{
+ rAssert( spInstance != 0 );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// MissionScriptLoader::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the MissionScriptLoader.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void MissionScriptLoader::DestroyInstance()
+{
+ rAssert( spInstance != 0 );
+
+ delete spInstance;
+ spInstance = 0;
+}
+
+//==============================================================================
+// MissionScriptLoader::MissionScriptLoader
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MissionScriptLoader::MissionScriptLoader() :
+ mp_ModelGroup( NULL ),
+ mPedGroupID( -1 ),
+ mp_TrafficGroup( NULL),
+ mpMission( NULL ),
+ mpStage( NULL ),
+ mpObjective( NULL ),
+ mpCondition( NULL ),
+ mFirstObjective( true )
+
+{
+
+}
+
+//==============================================================================
+// MissionScriptLoader::~MissionScriptLoader
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MissionScriptLoader::~MissionScriptLoader()
+{
+}
+
+//=============================================================================
+// MissionScriptLoader::Register
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::Register()
+{
+ Console* console = GetConsole();
+
+/*
+ * TC: removing this, since it's not needed anymore; plus, there's no more room
+ * in the console to add any more functions (it's already maxed out)
+ *
+#ifdef PAL
+ console->AddFunction( "SetLanguage", SetLanguage, "", 1, 1 );
+#endif // PAL
+*/
+
+ //
+ // Level methods
+ //
+ console->AddFunction( "InitLevelPlayerVehicle", InitLevelPlayerVehicle, "", 3, 4 ); //Inits a player vehicle and puts it at the locator ( carname, locatorname, slot, [.con] )
+ console->AddFunction( "PlacePlayerCar", PlacePlayerCar, "", 2, 2 ); //Places a Player car at the locator (carname, locatorname)
+ /*
+ console->AddFunction( "EnableTraffic", EnableTraffic,
+ "Traffic", 0, 0 );
+ console->AddFunction( "DisableTraffic", DisableTraffic,
+ "No Traffic", 0, 0 );
+ */
+ console->AddFunction( "AddPurchaseCarReward", AddPurchaseCarReward, "", 5, 6 ); //Add purchasable car reward (name), (character name), (choreo), (location), (float scale), [carstart]
+ console->AddFunction( "SetPostLevelFMV", SetPostLevelFMV, "", 1, 1 ); //FMV to play before loading next level.
+
+ console->AddFunction( "CreatePedGroup",CreatePedGroup, "",1,1); //Creates a ped group
+ console->AddFunction( "AddPed",AddPed, "",2,2); //Adds a ped to a group
+ console->AddFunction( "ClosePedGroup",ClosePedGroup, "",0,0); //Closes a group
+ console->AddFunction( "UsePedGroup",UsePedGroup, "", 1,1 ); //Sets current ped group to a group
+
+ console->AddFunction("BindReward",BindReward,"", 5, 7); //Binds a reward to a Quest
+
+ console->AddFunction("CreateTrafficGroup",CreateTrafficGroup,"",1,1); //Creates a Traffic Group
+ console->AddFunction("AddTrafficModel",AddTrafficModel, "",2,3); //Add a Traffic Model to a Group
+ console->AddFunction("CloseTrafficGroup",CloseTrafficGroup, "",0,0); //Closes a Traffic Group
+
+ console->AddFunction("SetCarAttributes",SetCarAttributes, "",5,5); //Set the Car Attributes displayed in the Phone Booth
+ console->AddFunction("SetTotalGags",SetTotalGags, "",2,2); //Set the total number of gags for a level
+ console->AddFunction("SetTotalWasps",SetTotalWasps, "",2,2); //Set the total number of wasps for a level
+ console->AddFunction("AddGlobalProp",AddGlobalProp, "",1,1); //Adds a Prop the Master Global Prop Hash Table
+
+ //Chase/HitnRun Methods
+ console->AddFunction( "CreateChaseManager",CreateChaseManager, "",3,3); //Creates a ChaseManager for a host car
+ console->AddFunction("DisableHitAndRun",DisableHitAndRun, "",0,0); //Disables Hit and Run Feature
+ console->AddFunction("EnableHitAndRun",EnableHitAndRun, "",0,0); //Enables Hit and Run Feature
+ console->AddFunction("SetHitAndRunMeter",SetHitAndRunMeter, "",1,1); //Set the value of the HitnRun meter
+ console->AddFunction("SetNumChaseCars",SetNumChaseCars, "",1,1); //Set the Max Number of Chase Cars
+ console->AddFunction( "SetChaseSpawnRate",SetChaseSpawnRate, "",2,2); //Sets the Spawn rate for a host car
+ console->AddFunction( "KillAllChaseAI",KillAllChaseAI, "",1,1); //Kills all Chase AI for a host car
+ console->AddFunction( "ResetHitAndRun",ResetHitAndRun, "",0,0); //Resets HitnRun system
+ console->AddFunction( "SetHitAndRunDecay",SetHitAndRunDecay, "",1,1); //Sets decay rate for HitnRun meter
+ console->AddFunction( "SetHitAndRunDecayInterior",KillAllChaseAI, "",1,1); //Sets interior decay rate for HitnRun meter
+ //
+ // Mission methods
+ //
+ console->AddFunction( "AddMission", AddMission, "", 1, 1 ); //Adds a new mission and names it (Name)
+
+ console->AddFunction( "AddBonusMission", AddBonusMission, "", 1, 1 ); //Adds a new bonus mission and names it (Name)
+
+ console->AddFunction( "SetMissionNameIndex", SetMissionNameIndex, "", 1, 1 ); //Sets the index in the text bible (index)
+
+ console->AddFunction( "SelectMission", SelectMission, "", 1, 1 ); //Selects a mission by name (Name)
+ //console->AddFunction( "InitMissionPlayerVehicle", InitMissionPlayerVehicle, "", 1, 2 ); //Adds a player vehicle to this mission (vehiclename), [.con]
+
+ console->AddFunction( "SetMissionResetPlayerInCar", SetMissionResetPlayerInCar, "", 1, 1 ); //This is where the player will go (in car) when the mission resets due to failure [locator]
+
+ console->AddFunction( "SetMissionResetPlayerOutCar", SetMissionResetPlayerOutCar, "", 2, 2 ); //This is where the player will go (out of car) when the mission resets due to failure [player locator, vehicle locator]
+ console->AddFunction( "SetDynaLoadData", SetDynaLoadData, "", 1, 2 ); //This is the string that defines what to load and dump..
+
+ console->AddFunction( "AddBonusObjective", AddBonusObjective, "", 1, 2 ); //This adds a bonus objective to the current mission (type).
+
+ console->AddFunction( "SetForcedCar", SetForcedCar, "", 0, 0 ); //This makes a mission a forced car mission
+
+ console->AddFunction( "CloseMission", CloseMission, "", 0, 0 ); //Closes the current mission
+
+ console->AddFunction( "SetDemoLoopTime", SetDemoLoopTime, "", 1, 1 ); //Sets the demo loop time only for demo loops
+
+ console->AddFunction("StreetRacePropsLoad",StreetRacePropsLoad, "",1,1); //String that defines which file that contains the props for streetraces to load
+
+ console->AddFunction("StreetRacePropsUnload",StreetRacePropsUnload, "",1,1); //String that defines which file that contains the props for streetraces to unload
+
+ console->AddFunction("UseElapsedTime",UseElapsedTime, "",0,0); //Make this Mission timer count UP
+
+ console->AddFunction("AttachStatePropCollectible",AttachStatePropCollectible, "" , 2, 2 ); //Attaches a collectible to the vehicle (vehicle name , statepropname )
+
+ console->AddFunction("ShowHUD",ShowHUD, "" , 1, 1 ); //Sets visibility of in-game HUD
+
+ console->AddFunction( "SetNumValidFailureHints", SetNumValidFailureHints, "", 1, 1 ); //Sets number of valid failure hints to be randomly chosen
+
+ //
+ // Stage methods
+ //
+ console->AddFunction( "AddStage", AddStage, "", 0, 7 ); //Adds a new stage to the current mission ([final], [locked, skin|car, name], [locked, skin|car, name])
+ console->AddFunction( "SetStageMessageIndex", SetStageMessageIndex, "", 1, 2 ); //Sets the index in the text bible (index)
+
+ console->AddFunction( "SetStageTime", SetStageTime, "", 1, 1 ); //Adds time to the current stage ()
+ console->AddFunction( "AddStageTime", AddStageTime, "", 1, 1 ); //Sets the time of the current stage ()
+
+ console->AddFunction( "AddStageVehicle", AddStageVehicle, "", //Adds an AI vehicle to the stage (name, startlocator, aitype, [.con], [driver name])
+ 3, 5 );
+ console->AddFunction( "MoveStageVehicle", MoveStageVehicle, "", //Moves an AI vehicle in the stage (name, startlocator, aitype)
+ 3, 3 );
+ console->AddFunction( "ActivateVehicle", ActivateVehicle, "", 3, 4 ); //Activates AI vehicle that has no AI in the stage (name, startlocator, aitype, [driver name])
+ console->AddFunction( "AddStageWaypoint", AddStageWaypoint, "", 1, 1 ); //Adds a waypoint to the stage (locatorname)
+ console->AddFunction( "AddStageCharacter", AddStageCharacter, "", 3, 5 ); //Adds a character to the stage (name, locatorname, dynaload string, [vehiclename], [carlocator])
+
+ console->AddFunction( "AddStageMusicChange", AddStageMusicChange, "", 0, 0 ); //Triggers check for change of music tension
+ console->AddFunction( "SetStageMusicAlwaysOn", SetStageMusicAlwaysOn, "", 0, 0 ); //Locks music on for walking out of car in stage
+ console->AddFunction( "SetCompletionDialog", SetCompletionDialog, "", 1, 2 ); //Plays dialog with given event name at end of stage
+ console->AddFunction( "StageStartMusicEvent", SetStageStartMusicEvent, "", 1, 1 ); //Triggers a music event when stage begins
+ console->AddFunction( "SetMusicState", SetMusicState, "", 2, 2 ); //Sets a radMusic state using the given state and event names
+
+ console->AddFunction( "SetStageCamera", SetStageCamera, "", 3, 3 ); //Sets the current camera mode for this stage (mode, cut, quick transition)
+
+ console->AddFunction( "RESET_TO_HERE", ResetToThisStage, "", 0, 0 ); //Sets the desred stage for resseting to... hehe hehehshshehahrheh
+
+ console->AddFunction( "SetMaxTraffic", SetTrafficDensity, "", 1, 1 ); //Sets the max traffic density.
+
+ console->AddFunction( "AddSafeZone", AddSafeZone, "", 2, 2 ); //Adds a Safezone at locatorname,radius in meters ()
+
+ console->AddFunction( "SetBonusMissionStart", SetBonusMissionStart, "", 0, 0 ); //Makes this stage the start of a bonus mission.
+
+ console->AddFunction( "ShowStageComplete", ShowStageComplete, "", 0, 0 ); //Show stage complete presentation.
+
+ console->AddFunction( "SetHUDIcon", SetHUDIcon, "", 1, 1 ); //Set HUD Icon Image.
+
+ console->AddFunction( "SetIrisWipe", SetIrisWipe, "", 1, 1 ); //Turns on Iris wipe.
+
+ console->AddFunction( "SetFadeOut", SetFadeOut, "", 1, 1 ); //Turns on Fade Out.
+
+ console->AddFunction( "CloseStage", CloseStage, "", 0, 0 ); //Closes the current stage ()
+
+ console->AddFunction( "SetVehicleAIParams", SetVehicleAIParams, "", 3, 3 ); //Sets params for an existing stage vehicle AI (STAGEVEHICLENAME, MINSHORTCUTSKILL, MAXSHORTCUTSKILL)
+
+ console->AddFunction("PlacePlayerAtLocatorName",PlacePlayerAtLocatorName, "",1,1); //On Stage Reset/Init places the player at a Locator
+ console->AddFunction("msPlacePlayerCarAtLocatorName",msPlacePlayerCarAtLocatorName, "",1,1); //Place the players current car at locator at the start of stage
+
+ //Chuck:Commands for Swapping Default for forced cars As per the Agreed template
+ console->AddFunction("SwapInDefaultCar",SwapInDefaultCar, "",0,0); //At the End of this Stage We Swap In the Default during the Iris Wipe
+ console->AddFunction("SetSwapPlayerLocator",SetSwapPlayerRespawnLocatorName, "",1,1); //Sets the Locator to spawn the player after we swap cars
+ console->AddFunction("SetSwapDefaultCarLocator",SetSwapDefaultCarRespawnLocatorName, "",1,1); //Set the Locator to spawn the default car after we swap cars
+ console->AddFunction("SetSwapForcedCarLocator",SetSwapForcedCarRespawnLocatorName, "",1,1); //Set the Locator to spawn the forced car after we swap cars
+
+ console->AddFunction("NoTrafficForStage",NoTrafficForStage, "",0,0); //Sets No Traffic For a Stage
+
+ console->AddFunction("ClearTrafficForStage",ClearTrafficForStage, "",0,0); //Clear traffic at the beginning of the stage
+
+ console->AddFunction( "SetStageAIRaceCatchupParams",SetStageAIRaceCatchupParams, "", 5,5 ); //Sets Race AI catchup params.
+
+ console->AddFunction( "SetStageAIEvadeCatchupParams",SetStageAIEvadeCatchupParams, "", 3,3 ); //Sets Evade AI catchup params.
+
+ console->AddFunction( "SetStageAITargetCatchupParams",SetStageAITargetCatchupParams, "", 3,3 ); //Sets Target AI catchup params.
+
+
+ console->AddFunction("SetCharacterToHide",SetCharacterToHide, "",1,1); //Set a Character To Be Hidden for duration at start of stage
+
+ console->AddFunction("SetLevelOver",SetLevelOver, "",0,0); //Explictly Command to End Level at Stage Finalize
+ console->AddFunction("SetGameOver", SetGameOver, "", 0, 0); //Explicitly end the game.
+
+ console->AddFunction("StayInBlack",StayInBlack, "",0,0); //Makes Scree black for duration of stage
+
+ console->AddFunction("AllowMissionAbort",AllowMissionAbort,"",1,1);
+
+ //
+ //Gamble Race Commands
+ //
+ console->AddFunction("SetParTime",SetParTime, "",1,1); //Sets Par Time for Gambling Races ONLY!
+
+ console->AddFunction("SetRaceEnteryFee",SetRaceEnteryFee, "",1,1); //Sets The Amount of Coins Required to Enter Race
+
+ console->AddFunction("PutMFPlayerInCar",PutMFPlayerInCar, "",0,0); //Forces the player to respawn in their car at the beginning of this stage
+
+
+ console->AddFunction("SetStatepropShadow",SetStatepropShadow, "", 2,2 ); //Sets which element of a stateprop is the shadow and projects it onto the ground
+
+
+ //
+ // Objective methods
+ //
+ console->AddFunction( "AddObjective", AddObjective, "", 1, 3 ); //Adds a new objective to the current stage ( objtype )
+ console->AddFunction( "CloseObjective", CloseObjective, "", 0, 0 ); //Closes the current objective ()
+
+ console->AddFunction( "SetDestination", SetDestination, "", 1, 3); //Sets the destination of the current objective (locator name, [p3d name], [float scale factor])
+
+ console->AddFunction( "AddNPC", AddNPC, "",2, 3 ); //Adds an NPC (name) to the position (locator name) beginning at this objective
+
+ console->AddFunction( "RemoveNPC", RemoveNPC, "",1, 1 ); //Remove an NPC (name) in this objective
+
+ console->AddFunction( "AddDriver", AddDriver, "",2, 2 ); //Adds an driver (name) int the car (other name) in this objective
+
+ console->AddFunction( "RemoveDriver", RemoveDriver, "",1, 1 ); //Remove an driver (name) in this objective
+
+ console->AddFunction( "SetTalkToTarget", SetTalkToTarget, "", 1, 4 ); //Sets the target of the objective's conversation (name, [0 - exclamation (default), 1 - gift, 2 - Door], [y offset], [trigger radius]
+
+ console->AddFunction( "SetDialogueInfo", SetDialogueInfo, "", 4, 4 ); //Sets the members of the dialogue, the name of the dialogue sound and the length of pause
+
+ console->AddFunction( "SetDialoguePositions", SetDialoguePositions, "", 2, 4 ); //Sets the positions of the characters and the car for the dialogue
+
+ console->AddFunction( "SetRaceLaps", SetRaceLaps, "", 1, 1 ); //Sets the number of laps in the race.
+
+ console->AddFunction( "TurnGotoDialogOff", TurnGotoDialogOff, "", 0, 0 ); //Turns arrival dialog off for goto objectives.
+
+ console->AddFunction( "MustActionTrigger", MustActionTrigger, "", 0, 0 ); //Must use action button for goto objectives.
+
+ //Chuck Coin Objectives
+
+ console->AddFunction("SetCoinFee",SetCoinFee, "",1,1); //Set The Amount of Coins to get past a Stage
+
+ //Timer Objectives
+
+ console->AddFunction("SetDurationTime",SetPauseDuration, "",1,1); //Set the amount of time for the timer objective
+
+ //
+ // Collection objective methods
+ //
+ console->AddFunction( "AddCollectible", AddCollectible, "", 1, 4 ); //Adds a new collectible to the current objective (locator name, [p3d name], [dialog event], [float scale factor])
+ console->AddFunction( "SetCollectibleEffect", SetCollectibleEffect, "", 1, 1 ); //Sets the collectible effect (effect name)
+ console->AddFunction( "BindCollectibleTo", BindCollectibleToWaypoint, "", 2, 2 ); //Binds the collectible <num> to the waypoint <num> (collect num, waypoint num)
+ console->AddFunction( "AllowUserDump", AllowUserDump, "", 0, 0 ); //Allows the player to lose collected object when struck by ai vehicles.
+
+ console->AddFunction( "SetVehicleToLoad", SetVehicleToLoad, "", 3,3); //Starts an async load of a new player vehicle
+
+ //
+ // Follow objective methods
+ //
+ console->AddFunction( "SetObjTargetVehicle", SetObjTargetVehicle, "", 1, 1 ); //Sets the target vehicle for a follow objective (name)
+ console->AddFunction( "SetObjDistance", SetObjDistance, "", 1, 1 ); //Sets the distance needed to lose the tail (distance)
+
+ // Boss objective
+ console->AddFunction( "SetObjTargetBoss", SetObjTargetBoss, "", 1,1 ); //Sets the target boss for a destroy objective
+
+ console->AddFunction( "AddCollectibleStateProp", AddCollectibleStateProp, "", 3,3 ); //Creates a new collectible stateprop into world
+
+ console->AddFunction( "SetPickupTarget", SetPickupTarget, "", 1,1 ); //Sets the target for a pickupitem objective
+
+
+ console->AddFunction( "AllowRockOut", AllowRockOut, "", 0, 0 ); //Have the PC 'rock out' when idling during this objective
+
+ //
+ // Mission Condition methods
+ //
+ console->AddFunction( "AddCondition", AddCondition, "", 1, 2 ); //Adds a new condition to the stage (type)
+ console->AddFunction( "CloseCondition", CloseCondition, "", 0, 0 ); //Closes the current condition
+
+ console->AddFunction( "SetFollowDistances", SetFollowDistances, "", 2, 2 ); //Sets the follow condition distances (min, max)
+
+ //
+ // Follow Condition methods
+ //
+ console->AddFunction( "SetCondTargetVehicle", SetCondTargetVehicle, "", 1, 1 ); //Sets the target vehicle for a follow condition (name)
+
+ //
+ // Position Condition methods
+ console->AddFunction("SetConditionPosition", SetConditionPosition, "", 1, 1 ); //Sets the position required for the position condition on a race.
+
+ console->AddFunction("SetCondTime", SetCondTime, "", 1, 1 ); //Sets the timeout for the out of car condition.
+
+ console->AddFunction("SetHitNRun", SetHitNRun, "", 0, 0 ); //Turns on Hit N Run on timeout condition.
+
+ //
+ // Presentation Functions
+ //
+ console->AddFunction( "EnableTutorialMode", EnableTutorialMode, "", 1, 1 ); //Should tutorial mode be enabled?
+ console->AddFunction( "SetConversationCamName", SetConversationCamName, "x", 1, 1 ); //Should tutorial mode be enabled?
+ console->AddFunction( "SetConversationCamPcName", SetConversationCamPcName, "", 1, 1 ); //Sets which of the conversation cameras will be used when the PC is talking
+ console->AddFunction( "SetConversationCamNpcName", SetConversationCamNpcName, "", 1, 1 ); //Sets which of the conversation cameras will be used when the NPC is talking
+ console->AddFunction( "SetConversationCam", SetConversationCam, "", 2, 3 ); //Sets the conversation cmaera for a particular line of dialog
+ console->AddFunction( "SetConversationCamDistance", SetConversationCamDistance, "", 2, 2 ); //Sets which of the conversation cameras will be used when the NPC is talking
+ console->AddFunction( "AmbientAnimationRandomize", AmbientAnimationRandomize, "", 2, 2 ); //should the animation for a particular character be randomized?
+ console->AddFunction( "ClearAmbientAnimations", ClearAmbientAnimations, "", 1, 1 );
+ console->AddFunction( "AddAmbientPcAnimation", AddAmbientPcAnimation, "", 1, 2 ); //Adds an animation that will be chosen at random during conversations
+ console->AddFunction( "AddAmbientNpcAnimation", AddAmbientNpcAnimation, "", 1, 2 ); //Adds an animation that will be chosen at random during conversations
+ console->AddFunction( "CharacterIsChild", CharacterIsChild, "", 1, 1 ); //Tells the conversation camera that this character is a child, and to look down
+ console->AddFunction( "SetPresentationBitmap", SetPresentationBitmap, "", 1, 1 ); //this is the bitmap to use on the mission breifing screen
+ console->AddFunction( "SetAnimCamMulticontName", SetAnimatedCameraMulticontrollerName, "", 1, 1 ); //This is the multicontroller for the animated camera that we should use
+ console->AddFunction( "SetAnimatedCameraName", SetAnimatedCameraName, "", 1, 1 ); //This is the animated camera that we should use
+ console->AddFunction( "SetMissionStartMulticontName", SetMissionStartMulticontrollerName, "", 1, 1 ); //This is the multicontroller for the animated camera that we should use
+ console->AddFunction( "SetMissionStartCameraName", SetMissionStartCameraName, "", 1, 1 ); //This is the animated camera that we should use
+ console->AddFunction( "SetCamBestSide", SetCamBestSide, "", 1, 2 ); //This is the name of the locator that corresponds to the best side for the camera
+ console->AddFunction( "SetFMVInfo", SetFMVInfo, "", 1, 2 ); //Play the FMV specified by file name
+ console->AddFunction( "StartCountdown", StartCountdown, "", 1, 2 ); //start the 321 go countdown, specify dialog
+ console->AddFunction( "AddToCountdownSequence", AddToCountdownSequence, "", 1, 2 ); //Add message and dialog to countdown sequence
+ console->AddFunction( "SetCarStartCamera", SetCarStartCamera, "", 1, 1 ); //Sets the camera that will be used when a car is loaded from a phone booth
+ console->AddFunction( "GoToPsScreenWhenDone", GoToPattyAndSelmaScreenWhenDone, "", 0, 0 ); //go to patty and selma screen when stage done?
+
+ //SuperSprint methods
+ console->AddFunction( "SetPlayerCarName", SetPlayerCarName, "", 2, 2 ); //This sets the car name for a given player
+
+ console->AddFunction( "SetCondMinHealth", SetCondMinHealth, "", 1, 1 ); //This sets the min health before failing the condition
+
+ //
+ // Loading script methods
+ //
+ console->AddFunction( "LoadP3DFile", LoadP3DFile, "", 1, 3 ); //Loads a Pure3D file ( filename, [heap name], [inventory section name] )
+ console->AddFunction( "LoadDisposableCar", LoadDisposableCar, "", 3, 3 ); //Loads a Pure3D CAR file ( filename,name,slot )
+
+
+ //Setting the respawn time for wrenches and wasps and nitro etc
+
+ console->AddFunction("SetRespawnRate",SetRespawnRate, "",2,2); //Set RespawnRate for Wrenches and Nitro in seconds,etc
+
+ console->AddFunction( "AddCharacter", AddCharacter, "", 2, 2 ); //Adds a character to the game world. All files must still be loaded ( p3d, choreo )
+ console->AddFunction( "AddNPCCharacterBonusMission", AddNPCCharacterBonusMission, "", 7, 8 ); //Adds an NPC character to the game world. All files must still be loaded ( p3d, choreo, loc, mission name, icon name, dialogue name, <bool>isRace, alternate icon name )
+ console->AddFunction( "SetBonusMissionDialoguePos", SetBonusMissionDialoguePositions, "", 3, 4 ); //Sets the positions of the characters and the car for the bonus mission dialogue (mission name, char 1 loc, char 2 loc, car loc)
+
+ console->AddFunction( "AddAmbientCharacter", AddAmbientCharacter, "", 2, 3 ); //Adds an ambient NPC character to the game world. All files must still be loaded ( p3d, loc)
+
+ console->AddFunction( "AddBonusMissionNPCWaypoint", AddBonusMissionNPCWaypoint, "", 2, 2 ); //Adds a waypoint for specified bonus mission NPC ( NPCNAME, LOCATOR )
+ console->AddFunction( "AddObjectiveNPCWaypoint", AddObjectiveNPCWaypoint, "", 2, 2 ); //Adds a waypoint for specified mission objective NPC ( NPCNAME, LOCATOR )
+ console->AddFunction( "AddAmbientNPCWaypoint", AddAmbientNPCWaypoint, "", 2, 2 ); //Adds a waypoint for specified ambient NPC ( NPCNAME, LOCATOR )
+ console->AddFunction( "AddPurchaseCarNPCWaypoint", AddPurchaseCarNPCWaypoint, "", 2, 2 ); //Adds a waypoint for specified Purchase Car Reward NPC ( NPCNAME, LOCATOR )
+
+ console->AddFunction( "ActivateTrigger", ActivateTrigger, "", 1, 1 ); //Activate a locator ( locator name )
+ console->AddFunction( "DeactivateTrigger", DeactivateTrigger, "", 1, 1 ); //Deactivate a locator ( locator name )
+
+ //
+ // Oh, that's a creative name. Like, duh.
+ //
+ console->AddFunction( "CreateAnimPhysObject", CreateAnimPhysObject, "", 2, 2 ); //Creates an animated physics object (name, animname)
+ console->AddFunction( "CreateActionEventTrigger", CreateActionEventTrigger, "", 5, 5 ); //Creates an action event trigger CreateActionEventTrigger(const char* triggerName, rmt::Vector& pos, float r)
+ console->AddFunction( "LinkActionToObjectJoint", LinkActionToObjectJoint, "", 5, 5 ); //Links the Action Trigger to an object joint. LinkActionToObjectJoint(const char* objectName, const char* jointName, const char* triggerName, const char* typeName, const char* buttonName )
+ console->AddFunction( "LinkActionToObject", LinkActionToObject, "", 5, 5 ); //Links the Action Trigger to an object. LinkActionToObject(const char* objectName, const char* jointName, const char* triggerName, const char* typeName, const char* buttonName)
+ console->AddFunction( "SetCoinDrawable", SetCoinDrawable, "", 1, 1 ); //Name the drawable for the coins
+ console->AddFunction( "SetParticleTexture", SetParticleTexture, "", 2, 2 ); //Name of texture used for particle
+
+}
+//=============================================================================
+// MissionScriptLoader::LoadScript
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char* filename )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::LoadScript( char* filename )
+{
+ mFirstObjective = true;
+
+ HeapMgr()->PushHeap (GMA_LEVEL_MISSION);
+
+ //
+ // NOTE: This has to stay sync because the scripts queue requests to the
+ // loading manager and these do not get processed if the loadmanager is
+ // already busy loading something
+ //
+ GetConsole()->ExecuteScriptSync( filename );
+
+ HeapMgr()->PopHeap (GMA_LEVEL_MISSION);
+}
+
+void MissionScriptLoader::LoadScriptAsync( char* filename, LoadingManager::ProcessRequestsCallback* callback )
+{
+ mFirstObjective = true;
+ HeapMgr()->PushHeap (GMA_LEVEL_MISSION);
+ GetConsole()->ExecuteScript( filename, this, callback );
+ HeapMgr()->PopHeap(GMA_LEVEL_MISSION);
+}
+
+
+void MissionScriptLoader::OnExecuteScriptComplete( void* pUserData )
+{
+ if ( pUserData != NULL )
+ {
+ LoadingManager::ProcessRequestsCallback* callback = static_cast<LoadingManager::ProcessRequestsCallback*>(pUserData);
+ if ( callback )
+ {
+ callback->OnProcessRequestsComplete( NULL );
+ }
+ }
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// MissionScriptLoader::SetLanguage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetLanguage( int argc, char** argv )
+{
+#ifdef PAL
+ const char languageID = argv[ 1 ][ 0 ];
+
+ switch( languageID )
+ {
+ case 'F':
+ {
+ CGuiTextBible::SetCurrentLanguage( Scrooby::XL_FRENCH );
+
+ break;
+ }
+ case 'G':
+ {
+ CGuiTextBible::SetCurrentLanguage( Scrooby::XL_GERMAN );
+
+ break;
+ }
+
+#ifndef RAD_GAMECUBE
+ case 'S':
+ {
+ CGuiTextBible::SetCurrentLanguage( Scrooby::XL_SPANISH );
+
+ break;
+ }
+#endif // !RAD_GAMECUBE
+
+ default:
+ {
+ rTuneAssertMsg( languageID == 'E', "Invalid language specification!" );
+
+ break;
+ }
+ }
+#endif // PAL
+}
+
+//=============================================================================
+// MissionScriptLoader::InitVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::InitLevelPlayerVehicle( int argc, char** argv )
+{
+ char locator[64], vehicleName[64], confile[64], slot[64];
+ GameplayManager::eCarSlots eslot = GameplayManager::eDefaultCar;
+ strcpy( vehicleName, argv[ 1 ]);
+ strcpy( locator, argv[ 2 ]);
+ strcpy(slot,argv[3]);
+
+
+ //determining which slot init on.
+ if(strcmp (slot,"DEFAULT") == 0)
+ {
+ eslot = GameplayManager::eDefaultCar;
+ }
+ else if ( strcmp (slot,"OTHER") == 0 )
+ {
+ eslot = GameplayManager::eOtherCar;
+ }
+ else if (strcmp (slot,"AI") == 0 )
+ {
+ rTuneAssertMsg(0, "is this ever called - see greg \n");
+ eslot = GameplayManager::eAICar;
+ }
+ else
+ {
+ printf(" Unknown Slot type, trying Init Level Car\n");
+ rAssert (0);
+ }
+
+ //empty vehicle point case
+ if ( GetGameplayManager()->mVehicleSlots[eslot].mp_vehicle == NULL)
+ {
+ Vehicle* vehicle;
+
+ if(argc == 5)
+ {
+ strcpy( confile, argv[ 4 ]);
+ vehicle = GetGameplayManager()->AddLevelVehicle( vehicleName,eslot, confile );
+ }
+ else
+ {
+ vehicle = GetGameplayManager()->AddLevelVehicle( vehicleName,eslot, 0 );
+ }
+ GetGameplayManager()->SetCurrentVehicle(vehicle);
+
+ // the mDefaultVehicle member data is old daryll legacy bs
+ //if(eslot == GameplayManager::eDefaultCar)
+ //{
+ // strcpy(GetGameplayManager()->mDefaultVehicle,vehicleName);
+ //}
+
+ GetGameplayManager()->PlaceVehicleAtLocatorName( vehicle, locator );
+ }
+ //non empty ptr
+ else
+ {
+ // greg
+ // jan 4, 2003
+ // i'm curious to see if we ever get here
+ rAssert(0);
+
+
+
+ if (strcmp(GetGameplayManager()->mVehicleSlots[eslot].mp_vehicle->GetName(), vehicleName) == 0)
+ {
+ //do nothing same vehicle exist
+ }
+ else
+ {
+ //trying to add some vehicle to a slot thats occupied
+ rAssert(0);
+ }
+ }
+
+ if ( eslot == GameplayManager::eDefaultCar )
+ {
+ //Save this as the default level data.
+ sprintf( GetGameplayManager()->mDefaultLevelVehicleName, vehicleName );
+ if ( argc == 5 )
+ {
+ sprintf( GetGameplayManager()->mDefaultLevelVehicleConfile, confile );
+ }
+ sprintf( GetGameplayManager()->mDefaultLevelVehicleLocator, locator );
+ }
+
+}
+
+//=============================================================================
+// MissionScriptLoader::PlacePlayerCar
+//=============================================================================
+// Description: Moves a Player car to a locator.
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::PlacePlayerCar( int argc, char** argv )
+{
+ char locator[64], vehicleName[64];
+ strcpy( vehicleName, argv[ 1 ]);
+
+ strcpy( locator, argv[ 2 ]);
+
+ Vehicle* vehicle = NULL;
+
+
+ //checking command to move current car.
+ if (strcmp(vehicleName,"current")==0)
+ {
+ vehicle= GetGameplayManager()->GetCurrentVehicle();
+ }
+ else
+ {
+ rTuneAssertMsg(0, "was this ever getting called, 'cause it wouldn't have worked - see greg \n");
+ //vehicle= GetGameplayManager()->GetVehicle(vehicleName);
+
+ }
+
+ rAssert(vehicle != NULL);
+ GetGameplayManager()->PlaceVehicleAtLocatorName( vehicle, locator );
+
+
+}
+
+/*
+void MissionScriptLoader::EnableTraffic( int argc, char** argv )
+{
+ ITrafficSpawnController* p_TrafficController= TrafficManager::GetSpawnController();
+ rAssert(p_TrafficController);
+ p_TrafficController->EnableTraffic();
+
+}
+void MissionScriptLoader::DisableTraffic( int argc, char** argv )
+{
+
+ ITrafficSpawnController* p_TrafficController= TrafficManager::GetSpawnController();
+ rAssert(p_TrafficController);
+ p_TrafficController->DisableTraffic();
+}
+*/
+
+//=============================================================================
+// MissionScriptLoader::AddPurchaseCarReward
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddPurchaseCarReward( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ ActionEventLocator* rewLoc = NULL;
+
+ rewLoc = new ActionEventLocator();
+ rAssert( rewLoc );
+
+ rewLoc->SetName( argv[1] );
+ rewLoc->SetActionNameSize( strlen("PurchaseCar") );
+ rewLoc->SetActionName( "PurchaseCar" );
+
+ rAssert( argc == 7 );
+
+ //The obj name is the name of the carstart locator
+ rewLoc->SetObjNameSize( strlen( argv[6] ) );
+ rewLoc->SetObjName( argv[6] );
+
+ //We use people for cars.
+ char uniqueName[64];
+ sprintf(uniqueName, "reward_%s", argv[2]);
+
+ Character* c = GetCharacterManager()->AddCharacterDeferedLoad( CharacterManager::NPC, uniqueName, argv[2], argv[3], argv[4] );
+ c->SetRole(Character::ROLE_REWARD);
+ c->SetAmbient(argv[4], 0.0f);
+
+ //This is a hack to tie the character name to the action event.
+ rewLoc->SetJointNameSize( strlen( uniqueName ) );
+ rewLoc->SetJointName( uniqueName );
+
+ c->SetRenderLayer(RenderEnums::LevelSlot);
+ c->AddToWorldScene();
+
+ rewLoc->SetButtonInput( (CharacterController::DoAction ) );
+ rewLoc->SetShouldTransform( false );
+
+ Locator* posLoc = p3d::find<Locator>(argv[4]);
+ rAssert( posLoc );
+
+ rmt::Vector pos( 0.0f, 0.0f, 0.0f );
+ if ( posLoc )
+ {
+ posLoc->GetLocation( &pos );
+ }
+
+ rewLoc->SetLocation( pos );
+
+ // If AddToGame() fails, actLoc will be released.
+ // Hold on to it here.
+ //
+ rewLoc->AddRef();
+ bool added = rewLoc->AddToGame( NULL );
+ rAssert( added );
+
+ // If AddToGame() didn't addref the locator,
+ // this release will delete it.
+ //
+ rewLoc->Release( );
+
+ if ( added )
+ {
+ //Create a trigger volume for the locator;
+ float scale = static_cast<float>( atof( argv[5] ) );
+
+ SphereTriggerVolume* newTrigger = new SphereTriggerVolume( pos, scale );
+ rAssert( newTrigger );
+
+ newTrigger->SetLocator( rewLoc );
+ rewLoc->SetNumTriggers( 1, GMA_LEVEL_OTHER );
+ rewLoc->AddTriggerVolume( newTrigger );
+
+ GetTriggerVolumeTracker()->AddTrigger( newTrigger );
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+}
+
+void MissionScriptLoader::SetPostLevelFMV( int argc, char** argv )
+{
+ rAssert( argc > 1 );
+ GetGameplayManager()->SetPostLevelFMV( argv[ 1 ] );
+}
+
+//=============================================================================
+// MissionScriptLoader::CreateChaseManager
+//=============================================================================
+// Description: Creates a ChaseManager instance for the hostvehicle
+//
+// Parameters: the hostvehicle (char* )and the spawn rate (int)
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::CreateChaseManager(int argc,char** argv)
+{
+ char vehiclename [64];
+ char confile [64];
+ int spawnrate =0;
+
+ strcpy(vehiclename,argv[1]);
+ strcpy(confile,argv[2]);
+ spawnrate = atoi(argv[3]);
+
+ if ( GetGameplayManager()->CreateChaseManager(vehiclename,confile,spawnrate) != 0)
+ {
+ //no room to create new chasemanager, WTF?
+ rAssert(0);
+ }
+
+}
+
+
+//=============================================================================
+// MissionScriptLoader::SetChaseSpawnRate
+//=============================================================================
+// Description: Sets the spawn rate for the hostvehicle
+//
+// Parameters: the hostvehicle (char* )and the spawn rate (int) 0-10 whole integer
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetChaseSpawnRate (int argc,char** argv)
+{
+
+ char vehiclename [64];
+ int num_cars =0;
+ ChaseManager* p_chasemanager = NULL;
+
+
+ strcpy(vehiclename,argv[1]);
+ num_cars = atoi(argv[2]);
+
+ spInstance->mpStage->SetChaseSpawnRate(vehiclename,num_cars);
+}
+
+
+//=============================================================================
+// MissionScriptLoader::KillAllChaseAI
+//=============================================================================
+// Description: Stop all chase AI vehicle for a hostvehicle
+//
+// Parameters: the hostvehicle (char* )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::KillAllChaseAI(int argc,char** argv)
+{
+
+ char vehiclename [64];
+ ChaseManager* p_chasemanager = NULL;
+ strcpy(vehiclename,argv[1]);
+ p_chasemanager=GetGameplayManager()->GetChaseManager(vehiclename);
+ rAssert(p_chasemanager);
+ p_chasemanager->DisableAllActiveVehicleAIs();
+
+}
+
+
+
+
+
+//=============================================================================
+// MissionScriptLoader::AddMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddMission( int argc, char** argv )
+{
+ int index = GetGameplayManager()->GetNumMissions();
+
+ spInstance->mMissionHeap = GetGameplayManager()->GetMissionHeap( index );
+
+ HeapMgr()->PushHeap( spInstance->mMissionHeap );
+
+MEMTRACK_PUSH_GROUP( "Mission" );
+ spInstance->mpMission = new Mission();
+ spInstance->mpMission->SetName( argv[ 1 ] );
+
+ if ( !GetGameplayManager()->mIsDemo )
+ {
+ Mission* sdPart = new Mission();
+
+ char name[64];
+ sprintf( name, "%ssd", argv[ 1 ] );
+ sdPart->SetName( name );
+ spInstance->mpStage = NULL;
+
+ //Add the SD part of the mission...
+ index;
+ GetGameplayManager()->SetNumMissions( index + 1 );
+ GetGameplayManager()->SetMission( index, sdPart );
+ sdPart->SetSundayDrive();
+ index++;
+ }
+
+ //Add the Main part of the mission...
+ GetGameplayManager()->SetNumMissions( index + 1 );
+ GetGameplayManager()->SetMission( index, spInstance->mpMission );
+
+ //chuck: when we are in demo we shouldn't add missions to the charactersheet.
+ if (GetGameplayManager()->mIsDemo == false)
+ {
+ //check for m8, m8 is a bogus mission and should not get added to charactersheet.
+ if (strcmp(argv[1],"m8") != 0)
+ {
+
+ if (GetCharacterSheetManager()->AddMission(GetGameplayManager()->GetCurrentLevelIndex(),spInstance->mpMission->GetName()) !=0 )
+ {
+ //asserted cause something failed adding mission to charactersheet.
+ rAssert(0);
+ }
+ }
+ }
+
+ HeapMgr()->PopHeap( spInstance->mMissionHeap );
+MEMTRACK_POP_GROUP( "Mission" );
+}
+
+//=============================================================================
+// MissionScriptLoader::AddBonusMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddBonusMission( int argc, char** argv )
+{
+ int index = GameplayManager::MAX_MISSIONS + GetGameplayManager()->GetNumBonusMissions();
+
+ spInstance->mMissionHeap = GetGameplayManager()->GetMissionHeap( index );
+
+ HeapMgr()->PushHeap( spInstance->mMissionHeap );
+
+MEMTRACK_PUSH_GROUP( "Mission" );
+ spInstance->mpMission = new Mission();
+ spInstance->mpMission->SetName( argv[ 1 ] );
+ spInstance->mpMission->SetBonusMission();
+
+ //Add the Main part of the mission...
+ int bonusMissionsCount = GetGameplayManager()->GetNumBonusMissions();
+ GetGameplayManager()->SetNumBonusMissions( bonusMissionsCount + 1 );
+ GetGameplayManager()->SetMission( index, spInstance->mpMission );
+MEMTRACK_POP_GROUP( "Mission" );
+
+//Chuck: a Code Design Shortcut.
+//I'm going to ASSUME That all bonusmission will be name "bm1"
+
+//its a bonus mission
+ if (strcmp(argv[1],"bm1") ==0)
+ {
+ if (GetCharacterSheetManager()->AddBonusMission(GetGameplayManager()->GetCurrentLevelIndex(),spInstance->mpMission->GetName()) !=0 )
+ {
+ //asserted cause something failed adding mission to charactersheet.
+ rAssert(0);
+ }
+ }
+ else if (strcmp (argv[1],"gr1")==0)
+ {
+ //its a gamble race and we do nothing
+ if(GetCharacterSheetManager()->AddGambleRace(GetGameplayManager()->GetCurrentLevelIndex(),spInstance->mpMission->GetName()) != 0)
+ {
+ rTuneAssert(0);
+ }
+
+ }
+
+ else
+ {
+ //add it the charactersheet since it must be a streetrace
+ if(GetCharacterSheetManager()->AddStreetRace(GetGameplayManager()->GetCurrentLevelIndex(),spInstance->mpMission->GetName()) !=0 )
+ {
+ //problems adding the streetrace to the charactersheet
+ //rAssert(0);
+ rReleasePrintf("Warning: Could NOT add %S into the CharacterSheet, All 3 StreetRace Slots are Full, Double Check this Mission's Name\n",spInstance->mpMission->GetName());
+ }
+ }
+
+ HeapMgr()->PopHeap( spInstance->mMissionHeap );
+}
+
+
+//=============================================================================
+// MissionScriptLoader::CloseMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::CloseMission( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpMission != NULL, "Mission already closed!\n" );
+ spInstance->mpMission = NULL;
+}
+
+//=============================================================================
+// MissionScriptLoader::SetForcedCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetForcedCar( int argc, char** argv )
+{
+ spInstance->mpMission->SetForcedCar( true );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetDemoLoopTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetDemoLoopTime( int argc, char** argv )
+{
+ if ( ( CommandLineOptions::Get( CLO_DEMO_TEST ) ||
+ GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_DEMO_TEST ) ) &&
+ ( !CommandLineOptions::Get( CLO_SHORT_DEMO ) ) )
+ {
+ ContextEnum currctx = GetGameFlow()->GetCurrentContext();
+ if ( currctx == CONTEXT_DEMO || currctx == CONTEXT_LOADING_DEMO )
+ {
+ DemoContext* dctx = static_cast<DemoContext*>(GetGameFlow()->GetContext( CONTEXT_DEMO ));
+ rAssert( dctx != NULL );
+
+ dctx->SetDemoLoopTime( atoi( argv[1] ) );
+ }
+ }
+}
+
+
+//=============================================================================
+// MissionScriptLoader::SelectMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SelectMission( int argc, char** argv )
+{
+ GameplayManager* gpm = GetGameplayManager();
+ int missionNum = gpm->GetMissionNumByName( argv[1] );
+
+ if ( missionNum > -1 )
+ {
+ spInstance->mpMission = gpm->GetMission( missionNum );
+
+
+ // new
+ // jan 8, 2003
+ // greg
+
+ // this is the very beginning of a brand new mission
+ //
+ // dump out old mission car list from GameplayManger
+
+ // HUGE TODO!!!!
+ //
+ // only want to do this the first time we start a mission
+ // NOT on a mission reset
+
+ gpm->EmptyMissionVehicleSlots();
+
+ return;
+ }
+
+ spInstance->mpMission = NULL;
+
+ // This really bad
+ rAssert( false );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetMissionNameIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetMissionNameIndex( int argc, char** argv )
+{
+ rAssert( spInstance->mpMission != NULL );
+
+ int index = atoi( argv[ 1 ] );
+ //spInstance->mpMission->SetNameIndex( index );
+}
+
+//=============================================================================
+// MissionScriptLoader::AddMissionPlayerVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+/*
+void MissionScriptLoader::InitMissionPlayerVehicle( int argc, char** argv )
+{
+ // don't think this is ever called
+
+ rAssert(0);
+
+
+
+ rAssert( spInstance->mpMission != NULL );
+
+ char confile[64];
+
+ Vehicle* vehicle = NULL;
+ if(argc == 2)
+ {
+ vehicle = GetVehicleCentral()->InitVehicle( argv[ 1 ], false, 0, VT_USER );
+ }
+ else if(argc == 3)
+ {
+ strcpy (confile, argv[2]);
+ vehicle = GetVehicleCentral()->InitVehicle( argv[ 1 ], false, confile, VT_USER );
+ }
+ else
+ {
+ rTuneAssertMsg(0,"wrong number of arguments for InitMissionPlayerVehicle\n");
+ }
+
+ // now handled by VehicleCentral::AddVehicleToActiveList
+ //GetRenderManager()->mpLayer(RenderEnums::LevelSlot)->AddGuts((tDrawable*)( vehicle ) );
+
+ //spInstance->mpMission->AddVehicle( vehicle );
+}
+*/
+
+//=============================================================================
+// MissionScriptLoader::SetMissionResetPlayerInCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetMissionResetPlayerInCar( int argc, char** argv )
+{
+ //Find the locator named argv[ 1 ]
+ CarStartLocator* loc = p3d::find<CarStartLocator>( argv[ 1 ] );
+
+ if ( loc != NULL )
+ {
+ spInstance->mpMission->SetVehicleRestart( loc );
+ }
+ else
+ {
+#ifdef RAD_DEBUG
+ char error[256];
+ sprintf( error, "Can not find locator: %s\n", argv[ 1 ] );
+ rDebugString( error );
+#endif
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::SetMissionResetPlayerOutCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetMissionResetPlayerOutCar( int argc, char** argv )
+{
+ //Find the locator named argv[ 1 ]
+ Locator* playerLoc = p3d::find<Locator>( argv[ 1 ] );
+ //Find the locator named argv[ 2 ]
+ CarStartLocator* vehicleLoc = p3d::find<CarStartLocator>( argv[ 2 ] );
+
+ if ( playerLoc != NULL && vehicleLoc != NULL )
+ {
+ spInstance->mpMission->SetVehicleRestart( vehicleLoc );
+ spInstance->mpMission->SetPlayerRestart( playerLoc );
+ }
+ else
+ {
+#ifndef FINAL
+
+ if(playerLoc == NULL)
+ {
+ char error[256];
+ sprintf( error, "Can not find locator: %s, Designer Error!!!!\n", argv[ 1 ] );
+ TreeOfWoeErrorMsg(error);
+ rTuneAssertMsg(0, error );
+ }
+ if(vehicleLoc == NULL)
+ {
+ char error[256];
+ sprintf( error, "Can not find locator: %s, Designer Error!!!!\n", argv[ 1 ] );
+ TreeOfWoeErrorMsg(error);
+ rTuneAssertMsg(0, error );
+ }
+
+#endif
+ }
+
+ if(playerLoc)
+ {
+ bool loading = ((GetGuiSystem()->GetBackendManager()->GetCurrentScreen() == CGuiWindow::GUI_SCREEN_ID_LOADING) &&
+ GetGuiSystem()->GetBackendManager()->GetCurrentWindow()->IsRunning());
+
+ if((GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_GAMEPLAY) || loading)
+ {
+ rmt::Vector pos;
+ playerLoc->GetLocation(&pos);
+ GetInteriorManager()->LoadLevelGags(pos, true);
+ }
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::SetDynaLoadData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetDynaLoadData( int argc, char** argv )
+{
+ if(argc == 3)
+ {
+ spInstance->mpMission->SetRestartDynaload( argv[ 1 ], argv[ 2 ] );
+ }
+ else
+ {
+ spInstance->mpMission->SetRestartDynaload( argv[ 1 ] );
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::AddBonusObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddBonusObjective( int argc, char** argv )
+{
+ rAssert( spInstance->mpMission );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+ #endif
+
+ if ( strncmp("no_damage", argv[1], 9 ) == 0 )
+ {
+ //This is a no damage type bonus objective;
+ NoDamageBonusObjective* ndbo = new NoDamageBonusObjective();
+ rAssert( ndbo );
+
+ spInstance->mpMission->AddBonusObjective( ndbo );
+ }
+ else if ( strncmp("no_harass", argv[1], 9 ) == 0 )
+ {
+ //This is a no harass cars type bonus objective;
+ NoCopBonusObjective* ncbo = new NoCopBonusObjective();
+ rAssert( ncbo );
+
+ spInstance->mpMission->AddBonusObjective( ncbo );
+ }
+ else if ( strncmp("time", argv[1], 4 ) == 0 )
+ {
+ //This is a minimum time remaining type bonus objective;
+ TimeRemainBonusObjective* trbo = new TimeRemainBonusObjective();
+ rAssert( trbo );
+
+ trbo->SetTime( atoi(argv[2]) );
+ trbo->SetMission( spInstance->mpMission );
+ spInstance->mpMission->AddBonusObjective( trbo );
+ }
+ else if ( strncmp("position", argv[1], 8 ) == 0 )
+ {
+ //This is a minimum time remaining type bonus objective;
+ RacePositionBonusObjective* rpbo = new RacePositionBonusObjective();
+ rAssert( rpbo );
+
+ rpbo->SetDesiredPosition( atoi(argv[2]) );
+ spInstance->mpMission->AddBonusObjective( rpbo );
+ }
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_MISSION );
+ #endif
+}
+
+
+//=============================================================================
+// MissionScriptLoader::AddObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddObjective( int argc, char** argv )
+{
+ // The AddObjective script command takes between 1 and 3 parameters
+ // The first parameter is the objective type.
+ // The 2nd depends is either the arrow type or , if it is a race, it could be "gamble"
+ // If the 2nd parameter is gamble, the third is optionally the arrow type
+ // Make the last parameter optionally be the directional arrow type
+ // Default is BOTH, if it is not present
+ DirectionalArrowEnum::TYPE darrowType;
+ if ( GetDirectionalArrowType( argv[ argc - 1 ], &darrowType ) == false )
+ {
+ darrowType = DirectionalArrowEnum::BOTH;
+ }
+
+ HeapMgr()->PushHeap( spInstance->mMissionHeap );
+
+MEMTRACK_PUSH_GROUP( "Mission - Objectives" );
+
+ rAssert( spInstance->mpStage->GetObjective() == NULL );
+
+ rAssert( spInstance->mpObjective == NULL );
+
+ spInstance->mObjType = MissionObjective::OBJ_INVALID;
+
+ bool found = false;
+ for( unsigned int i = 0; i < MissionObjective::NUM_OBJECTIVES; i++ )
+ {
+ if( strcmp( MissionObjectiveNames::Name[ i ], argv[ 1 ] ) == 0 )
+ {
+ spInstance->mObjType = (MissionObjective::ObjectiveTypeEnum)( i );
+ found = true;
+ break;
+ }
+ }
+
+ rTuneAssertMsg( found, "This is not a valid Objective type" );
+
+ switch( spInstance->mObjType )
+ {
+ case MissionObjective::OBJ_DELIVERY:
+ {
+ DeliveryObjective* pDel = new DeliveryObjective();
+
+ spInstance->mpObjective = pDel;
+ break;
+ }
+ case MissionObjective::OBJ_GOTO:
+ {
+ GoToObjective* pGoto = new GoToObjective();
+
+ spInstance->mpObjective = pGoto;
+ break;
+ }
+ case MissionObjective::OBJ_FOLLOW:
+ {
+ FollowObjective* pFollow = new FollowObjective();
+
+ spInstance->mpObjective = pFollow;
+ break;
+ }
+ case MissionObjective::OBJ_RACE:
+ {
+ RaceObjective* pRace = new RaceObjective();
+
+ //Chuck: check if this race is a gamble race
+ if(strcmp(argv[2],"gamble") ==0)
+ {
+ pRace->SetGambleRace(true);
+ }
+ spInstance->mpObjective = pRace;
+ break;
+ }
+ case MissionObjective::OBJ_LOSETAIL:
+ {
+ LoseObjective* pLose = new LoseObjective();
+
+ spInstance->mpObjective = pLose;
+ break;
+ }
+ case MissionObjective::OBJ_DESTROY:
+ {
+ DestroyObjective* pDestroy = new DestroyObjective();
+
+ spInstance->mpObjective = pDestroy;
+ break;
+ }
+ case MissionObjective::OBJ_TALKTO:
+ {
+ TalkToObjective* pTalkTo = new TalkToObjective();
+
+ spInstance->mpObjective = pTalkTo;
+ break;
+ }
+ case MissionObjective::OBJ_DIALOGUE:
+ {
+ DialogueObjective* pDialogue = new DialogueObjective();
+
+ spInstance->mpObjective = pDialogue;
+ break;
+ }
+ case MissionObjective::OBJ_GETIN:
+ {
+ GetInObjective* pGetIn = new GetInObjective();
+
+ if(argc == 3) //added another param for strict getin
+ {
+ pGetIn->SetStrict(argv[2]);
+ }
+
+ spInstance->mpObjective = pGetIn;
+
+ break;
+ }
+ case MissionObjective::OBJ_DUMP:
+ {
+ CollectDumpedObjective* pDump = new CollectDumpedObjective();
+
+ spInstance->mpObjective = pDump;
+ break;
+ }
+ case MissionObjective::OBJ_FMV:
+ {
+ FMVObjective* fmvObjective = new FMVObjective();
+ spInstance->mpObjective = fmvObjective;
+ break;
+ }
+ case MissionObjective::OBJ_INTERIOR:
+ {
+ InteriorObjective* intObjective = new InteriorObjective();
+ spInstance->mpObjective = intObjective;
+ break;
+ }
+ case MissionObjective::OBJ_COIN:
+ {
+ CoinObjective* pCoinObjective = new CoinObjective();
+ spInstance->mpObjective = pCoinObjective;
+ break;
+ }
+ case MissionObjective::OBJ_DESTROY_BOSS:
+ {
+ DestroyBossObjective* pObjective = new DestroyBossObjective();
+ spInstance->mpObjective = pObjective;
+ break;
+ }
+ case MissionObjective::OBJ_LOAD_VEHICLE:
+ {
+ LoadVehicleObjective* objective = new LoadVehicleObjective();
+ spInstance->mpObjective = objective;
+ break;
+ }
+ case MissionObjective::OBJ_PICKUP_ITEM:
+ {
+ PickupItemObjective* objective = new PickupItemObjective();
+ spInstance->mpObjective = objective;
+ break;
+ }
+ case MissionObjective::OBJ_TIMER:
+ {
+ TimerObjective* pTimerObjective = new TimerObjective();
+ spInstance->mpObjective = pTimerObjective;
+ break;
+ }
+ case MissionObjective::OBJ_BUY_CAR:
+ {
+ BuyCarObjective* bco = new BuyCarObjective();
+ spInstance->mpObjective = bco;
+ rTuneAssertMsg( argc == 3, "You need to specify which car to buy for BuyCar Objectives\n");
+
+ if ( argc == 3 )
+ {
+ bco->SetVehicleName( argv[2] );
+ }
+ break;
+ }
+ case MissionObjective::OBJ_BUY_SKIN:
+ {
+ BuySkinObjective* bso = new BuySkinObjective();
+ spInstance->mpObjective = bso;
+ rTuneAssertMsg( argc == 3, "You need to specify which skin to buy for BuSkinr Objectives\n");
+
+ if ( argc == 3 )
+ {
+ bso->SetSkinName( argv[2] );
+ }
+ break;
+ }
+ case MissionObjective::OBJ_GO_OUTSIDE:
+ {
+
+ GoOutsideObjective* pGoOutSide = new GoOutsideObjective();
+
+ spInstance->mpObjective = pGoOutSide;
+
+
+ break;
+ }
+ default:
+ {
+ MissionObjective* objective = new MissionObjective();
+ spInstance->mpObjective = objective;
+ break;
+ }
+ }
+
+ //GetRoadManager()->SetDirectionalArrowType( darrowType );
+
+ rAssert( spInstance->mpObjective );
+ spInstance->mpObjective->SetDirectionalArrowType( &darrowType );
+ spInstance->mpObjective->SetObjectiveType( spInstance->mObjType );
+ spInstance->mpStage->SetObjective( spInstance->mpObjective );
+MEMTRACK_POP_GROUP( "Mission - Objectives" );
+
+ HeapMgr()->PopHeap( spInstance->mMissionHeap );
+}
+
+//=============================================================================
+// MissionScriptLoader::AddNPC
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddNPC( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+ //Find the locator named argv[ 2 ]
+ spInstance->mpObjective->AddNPC( argv[ 1 ], argv[ 2 ], false );
+
+ if(spInstance->mFirstObjective)
+ {
+ GetCharacterManager()->PreloadCharacter(argv[1], "npd");
+ }
+
+ HeapMgr()->PopHeap(GMA_LEVEL_MISSION);
+}
+
+//=============================================================================
+void MissionScriptLoader::AddDriver( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+
+ spInstance->mpObjective->AddNPC( argv[ 1 ], argv[ 2 ], true );
+
+ if(spInstance->mFirstObjective)
+ {
+ GetCharacterManager()->PreloadCharacter(argv[1], "npd");
+ }
+
+ HeapMgr()->PopHeap(GMA_LEVEL_MISSION);
+}
+
+//=============================================================================
+void MissionScriptLoader::RemoveNPC( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+ //Find the locator named argv[ 2 ]
+ spInstance->mpObjective->RemoveNPC( argv[ 1 ], false);
+ HeapMgr()->PopHeap(GMA_LEVEL_MISSION);
+}
+
+//=============================================================================
+void MissionScriptLoader::RemoveDriver( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+ //Find the locator named argv[ 2 ]
+ spInstance->mpObjective->RemoveNPC( argv[ 1 ], true);
+ HeapMgr()->PopHeap(GMA_LEVEL_MISSION);
+}
+
+//=============================================================================
+// MissionScriptLoader::SetTalkToTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetTalkToTarget( int argc, char** argv )
+{
+ if ( spInstance->mObjType != MissionObjective::OBJ_TALKTO )
+ {
+ rDebugString( "Can only set talk to targets in talk to objectives\n" );
+ return;
+ }
+
+ TalkToObjective::IconType icon = TalkToObjective::EXCLAMATION;
+
+ if ( argc > 2 )
+ {
+ icon = static_cast<TalkToObjective::IconType>(::atoi( argv[2] ));
+ }
+
+ float yOffset = 0.0f;
+
+ if ( argc > 3 )
+ {
+ yOffset = static_cast<float>(::atof( argv[3] ));
+ }
+
+ float trigRad = 1.3f;
+ if ( argc > 4 )
+ {
+ trigRad = static_cast<float>(::atof( argv[4] ));
+ }
+
+ reinterpret_cast<TalkToObjective*>(spInstance->mpObjective)->SetTalkToTarget( argv[1], icon, yOffset, trigRad );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetDialogueInfo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetDialogueInfo( int argc, char** argv )
+{
+ if ( spInstance->mObjType != MissionObjective::OBJ_DIALOGUE )
+ {
+ rDebugString( "Can only set dialoge settings in dialogue objectives!" );
+ return;
+ }
+
+ DialogueObjective* dialogObj = reinterpret_cast<DialogueObjective*>(spInstance->mpObjective);
+ dialogObj->SetChar1Name( argv[1] );
+ dialogObj->SetChar2Name( argv[2] );
+ dialogObj->SetDialogueName( argv[3] );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetDialoguePositions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetDialoguePositions( int argc, char** argv )
+{
+ if ( spInstance->mObjType != MissionObjective::OBJ_DIALOGUE )
+ {
+ rDebugString( "Can only set dialoge settings in dialogue objectives!" );
+ return;
+ }
+
+ CarStartLocator* char1Pos = p3d::find<CarStartLocator>( argv[1] );
+
+#ifndef RAD_RELEASE
+ if ( !char1Pos )
+ {
+ char error[128];
+ sprintf( error, "Could not find %s for dialogue position\n", argv[1] );
+ rTuneAssertMsg( false, error );
+ }
+#endif
+
+ CarStartLocator* char2Pos = p3d::find<CarStartLocator>( argv[2] );
+
+#ifndef RAD_RELEASE
+ if ( !char2Pos )
+ {
+ char error[128];
+ sprintf( error, "Could not find %s for dialogue position\n", argv[2] );
+ rTuneAssertMsg( false, error );
+ }
+#endif
+
+ CarStartLocator* carPos = NULL;
+ /*if ( argc >= 4 )
+ {
+ //Also need a car start
+ carPos = p3d::find<CarStartLocator>( argv[3] );
+
+#ifndef RAD_RELEASE
+ if ( !carPos )
+ {
+ char error[128];
+ sprintf( error, "Could not find %s for dialogue position\n", argv[3] );
+ rTuneAssertMsg( false, error );
+ }
+#endif
+ }*/
+
+ bool dontReset = false;
+
+ if ( argc >= 5 )
+ {
+ dontReset = (atoi( argv[ 4 ] ) == 1);
+ }
+
+ static_cast<DialogueObjective*>(spInstance->mpObjective)->SetPositions( char1Pos, char2Pos, carPos, dontReset );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetRaceLaps
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetRaceLaps( int argc, char** argv )
+{
+ rAssert( spInstance->mObjType == MissionObjective::OBJ_RACE );
+
+ RaceObjective* ro = reinterpret_cast<RaceObjective*>(spInstance->mpObjective);
+ ro->SetNumLaps( atoi(argv[1] ) );
+}
+
+//=============================================================================
+// MissionScriptLoader::BindCollectibleToWaypoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::BindCollectibleToWaypoint( int argc, char** argv )
+{
+ rAssert( spInstance->mObjType == MissionObjective::OBJ_DUMP );
+ CollectDumpedObjective* cdo = reinterpret_cast<CollectDumpedObjective*>(spInstance->mpObjective);
+ cdo->BindCollectibleToWaypoint( atoi( argv[1] ), atoi( argv[2] ) );
+}
+
+//=============================================================================
+// MissionScriptLoader::AllowUserDump
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AllowUserDump( int argc, char** argv )
+{
+ rAssert( spInstance->mObjType == MissionObjective::OBJ_DUMP ||
+ spInstance->mObjType == MissionObjective::OBJ_DELIVERY );
+
+ static_cast<CollectibleObjective*>(spInstance->mpObjective)->AllowUserDump();
+}
+
+//=============================================================================
+// MissionScriptLoader::SetVehicleToLoad
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetVehicleToLoad( int argc, char** argv )
+{
+ rAssert( spInstance->mObjType == MissionObjective::OBJ_LOAD_VEHICLE );
+
+ LoadVehicleObjective* objective = static_cast< LoadVehicleObjective* >( spInstance->mpObjective );
+
+ const char* filename = argv[1];
+ const char* vehiclename = argv[2];
+ const char* locatorname = argv[3];
+
+ objective->SetFilename( filename );
+ objective->SetVehicleName( vehiclename );
+ objective->SetLocatorName( locatorname );
+}
+
+
+//=============================================================================
+// MissionScriptLoader::CloseObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::CloseObjective( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpObjective != NULL, "Objective already closed!\n" );
+ spInstance->mpObjective = NULL;
+ spInstance->mObjType = MissionObjective::OBJ_INVALID;
+ spInstance->mFirstObjective = false;
+}
+
+
+
+//=============================================================================
+// MissionScriptLoader::AddCollectible
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddCollectible( int argc, char** argv )
+{
+ bool bValid = spInstance->mObjType == MissionObjective::OBJ_DELIVERY ||
+ spInstance->mObjType == MissionObjective::OBJ_RACE ||
+ spInstance->mObjType == MissionObjective::OBJ_DUMP;
+ rTuneAssertMsg( bValid,
+ "The current objective cannot accept collectibles." );
+
+ rAssert( dynamic_cast<CollectibleObjective*>( spInstance->mpObjective ) != NULL );
+ CollectibleObjective* pCol = static_cast<CollectibleObjective*>( spInstance->mpObjective );
+ rAssert( pCol );
+
+ if ( argc == 2 )
+ {
+ pCol->AddCollectibleLocatorName( argv[ 1 ], "", 0, 0, 0.0f );
+ }
+ else if ( argc == 3 )
+ {
+ pCol->AddCollectibleLocatorName( argv[ 1 ], argv[ 2 ], 0, 0, 0.0f );
+ }
+ else if ( argc == 4 )
+ {
+ pCol->AddCollectibleLocatorName( argv[ 1 ], argv[ 2 ], ::radMakeKey32( argv[ 3 ] ), 0, 0.0f );
+ }
+ else if ( argc == 5 )
+ {
+ pCol->AddCollectibleLocatorName( argv[ 1 ], argv[ 2 ], ::radMakeKey32( argv[ 3 ] ),
+ tEntity::MakeUID( argv[ 4 ] ), 0.0f );
+ }
+ else
+ {
+ pCol->AddCollectibleLocatorName( argv[ 1 ], argv[ 2 ], ::radMakeKey32( argv[ 3 ] ),
+ tEntity::MakeUID( argv[ 4 ] ), static_cast<float>(::atof( argv[ 5 ] )) );
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::AddCollectibleStateProp
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddCollectibleStateProp( int argc, char** argv )
+{
+ rmt::Vector position;
+ const char* statepropName = argv[1];
+ const char* locatorName = argv[2];
+ int physID = atoi( argv[3] );
+ spInstance->mpMission->CreateStatePropCollectible( statepropName, locatorName, physID );
+}
+
+
+
+//=============================================================================
+// MissionScriptLoader::SetCollectibleEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetCollectibleEffect( int argc, char** argv )
+{
+ if ( spInstance->mpObjective->GetObjectiveType() == MissionObjective::OBJ_GOTO )
+ {
+ GoToObjective* gto = static_cast<GoToObjective*>( spInstance->mpObjective );
+ gto->SetCollectEffectName( argv[ 1 ] );
+ }
+ else
+ {
+ rTuneAssertMsg( dynamic_cast< CollectibleObjective* >( spInstance->mpObjective ) != NULL, "The current objective cannot accept collectibles.");
+ CollectibleObjective* pCol = static_cast<CollectibleObjective*>( spInstance->mpObjective );
+ rAssert( pCol );
+
+ pCol->SetCollectEffectName( argv[ 1 ] );
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::SetDestination
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetDestination( int argc, char** argv )
+{
+ MissionObjective::ObjectiveTypeEnum type = spInstance->mObjType;
+
+ switch( type )
+ {
+ case MissionObjective::OBJ_GO_OUTSIDE:
+ case MissionObjective::OBJ_GOTO:
+ {
+ GoToObjective* pObj = (GoToObjective*)(spInstance->mpObjective);
+
+ //Test to see if there is no drawable, but a scale.
+ if ( argc == 3 && static_cast<float>(::atof( argv[ 2 ] )) != 0.0f )
+ {
+ pObj->SetDestinationNames( argv[ 1 ], "", static_cast<float>(::atof( argv[ 2 ] )) );
+ }
+ else if ( argc == 3 )
+ {
+ //There is no scale factor
+ pObj->SetDestinationNames( argv[ 1 ], argv[ 2 ], 0.0f );
+ }
+ else if ( argc == 4 )
+ {
+ pObj->SetDestinationNames( argv[ 1 ], argv[ 2 ], static_cast<float>(::atof( argv[ 3 ] )) );
+ }
+ else
+ {
+ pObj->SetDestinationNames( argv[ 1 ], "", 0.0f );
+ }
+ break;
+ }
+ case MissionObjective::OBJ_INTERIOR:
+ {
+ InteriorObjective* pObj = (InteriorObjective*)(spInstance->mpObjective);
+ pObj->SetDestination(argv[1]);
+
+ if ( argc >= 3 )
+ {
+ pObj->SetIcon( argv[2] );
+ }
+ }
+ break;
+ default:
+ {
+ // fook!
+ rTuneAssertMsg( false, "The current objective cannot accept a destination." );
+ }
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::TurnGotoDialogOff
+//=============================================================================
+// Description: Turn the arrival dialogue off for a goto objective
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::TurnGotoDialogOff( int argc, char** argv )
+{
+ rAssert( spInstance->mpObjective->GetObjectiveType() == MissionObjective::OBJ_GOTO || spInstance->mpObjective->GetObjectiveType() == MissionObjective::OBJ_GO_OUTSIDE );
+
+ static_cast<GoToObjective*>(spInstance->mpObjective)->SetGotoDialogOff();
+}
+
+//=============================================================================
+// MissionScriptLoader::MustActionTrigger
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::MustActionTrigger( int argc, char** argv )
+{
+ rAssertMsg( spInstance->mpObjective->GetObjectiveType() == MissionObjective::OBJ_GOTO, "MustActionTrigger can only be applied to GOTO objectives!\n" );
+
+ static_cast<GoToObjective*>(spInstance->mpObjective)->SetMustActionTrigger();
+}
+
+//=============================================================================
+// MissionScriptLoader::AddStageVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddStageVehicle( int argc, char** argv )
+{
+MEMTRACK_PUSH_GROUP( "Mission - Stage Vehicle" );
+ char vehiclename[32];
+ char spawnname[32];
+ char ainame[32];
+ char confile[32];
+
+ strcpy( vehiclename, argv[ 1 ] );
+ strcpy( spawnname, argv[ 2 ] );
+
+ if(argc >= 4)
+ {
+ strcpy( ainame, argv[ 3 ] );
+ }
+ else
+ {
+ // ever have this case?
+ ainame[0] = 0;
+ }
+
+ rTuneAssertMsg( spInstance->mpMission != NULL, "No mission is selected\n" );
+
+ // new
+ // greg
+ // jan 7, 2003
+
+ // GameplayManager is now the owner of these vehilces
+ //
+ // mission stages can continue to reference them as before, but they do not own them
+ //
+ // I don't think there is any use in having a reference in the mission also
+
+
+ Vehicle* vehicle = NULL;
+
+ switch(argc)
+ {
+ case 4:
+ {
+ vehicle = GetGameplayManager()->AddMissionVehicle(vehiclename);
+ }
+ break;
+
+ case 5:
+ {
+ strcpy(confile, argv[4]);
+ vehicle = GetGameplayManager()->AddMissionVehicle(vehiclename, confile);
+ // this method will return a pointer if the vehicle already exists in the list of mission cars,
+ // or it will allocate a new one
+ }
+ break;
+
+ case 6:
+ {
+ strcpy(confile, argv[4]);
+ vehicle = GetGameplayManager()->AddMissionVehicle(vehiclename, confile, argv[5]);
+ // this method will return a pointer if the vehicle already exists in the list of mission cars,
+ // or it will allocate a new one
+ }
+ break;
+ }
+
+ rTuneAssertMsg( vehicle != NULL, "Could not find or create the requested vehicle\n" );
+
+
+ /*
+ if(argc == 5)
+ {
+ strcpy(confile, argv[4]);
+ vehicle = GetVehicleCentral()->InitVehicle( vehiclename, false, confile, VT_AI );
+ }
+ else
+ {
+ vehicle = GetVehicleCentral()->InitVehicle( vehiclename, false, 0, VT_AI );
+ }
+ */
+
+
+ //spInstance->mpMission->AddVehicle( vehicle ); // useless
+
+
+ // now handled by VehicleCentral::AddVehicleToActiveList
+ //GetRenderManager()->mpLayer(RenderEnums::LevelSlot)->AddGuts((tDrawable*)( vehicle ) );
+
+ CarStartLocator* locator = NULL;
+ if( argc >= 3 && strlen( spawnname ) != 0 )
+ {
+ locator = p3d::find<CarStartLocator>( spawnname );
+#ifndef FINAL
+ char error [255];
+ sprintf(error,"AddStageVehicle: Could not find the requested Car Start locator %s\n",spawnname);
+ rTuneAssertMsg( locator != NULL,error);
+#endif
+ }
+
+ rTuneAssertMsg( spInstance->mpStage != NULL, "No stage active\n" );
+
+ // new
+ // greg
+ // jan 9, 2003
+
+ // move the allocation of the vehicle AI into the MissionStage
+
+/*
+MEMTRACK_PUSH_GROUP( "Mission - AI " );
+ VehicleAI* pAI = NULL;
+ if( argc >= 4 )
+ {
+ if( strcmp( ainame, "chase") == 0 )
+ {
+ pAI = new ChaseAI( vehicle );
+ }
+ else if( strcmp( ainame, "waypoint" ) == 0 )
+ {
+ pAI = new WaypointAI( vehicle );
+ }
+ else if ( strcmp( ainame, "NULL" ) == 0 )
+ {
+ pAI = NULL;
+
+ }
+
+ else
+ {
+ char buffy[64];
+ sprintf( buffy, "Unknown AI vehicle type: %s", argv[ 3 ] );
+ rTuneAssertMsg( false, buffy );
+ }
+ }
+MEMTRACK_POP_GROUP();
+*/
+
+
+ int vehicleCentralIndex = GetGameplayManager()->GetMissionVehicleIndex(vehicle);
+ //rAssert(vehicleCentralIndex != -1);
+
+ //spInstance->mpStage->AddVehicle( vehicle, vehicleCentralIndex, locator, pAI );
+ spInstance->mpStage->AddVehicle( vehicle, vehicleCentralIndex, locator, ainame );
+
+
+MEMTRACK_POP_GROUP( "Mission - Stage Vehicle" );
+}
+
+//=============================================================================
+// MissionScriptLoader::MoveStageVehicle
+//=============================================================================
+// Description: Used to move a staged Vehicle from one location to another. This vehicle must have been
+// Initialized or the command to fail. and
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::MoveStageVehicle( int argc, char** argv )
+{
+MEMTRACK_PUSH_GROUP( "Mission - Stage Vehicle" );
+ char vehiclename[32];
+ char spawnname[32];
+ char ainame[32];
+
+ strcpy( vehiclename, argv[ 1 ] );
+ strcpy( spawnname, argv[ 2 ] );
+
+
+ if(argc >= 4)
+ {
+ strcpy( ainame, argv[ 3 ] );
+ }
+ else
+ {
+ // ever have this case?
+ ainame[0] = 0;
+ }
+
+ rTuneAssertMsg( spInstance->mpMission != NULL, "No mission is selected\n" );
+
+ Vehicle* vehicle = NULL;
+
+ vehicle = spInstance->GetVehicleByName(vehiclename);
+
+ CarStartLocator* locator = NULL;
+ if( argc >= 3 && strlen( spawnname ) != 0 )
+ {
+ locator = p3d::find<CarStartLocator>( spawnname );
+ rTuneAssertMsg( locator != NULL, "Could not find the requested Car Start locator\n" );
+ }
+
+ rTuneAssertMsg( spInstance->mpStage != NULL, "No stage active\n" );
+/*
+MEMTRACK_PUSH_GROUP( "Mission - AI " );
+ VehicleAI* pAI = NULL;
+ if( argc >= 4 )
+ {
+ if( strcmp( ainame, "chase") == 0 )
+ {
+ pAI = new ChaseAI( vehicle );
+ }
+ else if( strcmp( ainame, "waypoint" ) == 0 )
+ {
+ pAI = new WaypointAI( vehicle );
+ }
+ else if ( strcmp( ainame, "NULL" ) == 0 )
+ {
+ pAI = NULL;
+
+ }
+ else
+ {
+ char buffy[64];
+ sprintf( buffy, "Unknown AI vehicle type: %s", argv[ 3 ] );
+ rTuneAssertMsg( false, buffy );
+ }
+ }
+MEMTRACK_POP_GROUP();
+*/
+ int vehicleCentralIndex = GetGameplayManager()->GetMissionVehicleIndex(vehicle);
+ rAssert(vehicleCentralIndex != -1);
+
+ spInstance->mpStage->AddVehicle( vehicle, vehicleCentralIndex, locator, ainame );
+
+MEMTRACK_POP_GROUP( "Mission - Stage Vehicle" );
+
+ GetGameplayManager()->PlaceVehicleAtLocator( vehicle, locator );
+}
+
+
+//=============================================================================
+// MissionScriptLoader::ActivateVehicle
+//=============================================================================
+// Description: Used to give a staged Vehicle a new AI type . Call this on a vehicle that is idle.
+// This vehicle must have been Initialized via AddStageVehicle or the command to fail.
+// Parameters: ( int argc, char** argv ) vehicle,locator,AI
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::ActivateVehicle( int argc, char** argv )
+{
+MEMTRACK_PUSH_GROUP( "Mission - Stage Vehicle" );
+#if (RAD_TUNE||RAD_DEBUG)
+ char errMsg[256];
+#endif
+
+ char vehiclename[32];
+ char spawnname[32];
+ char ainame[32];
+
+ strcpy( vehiclename, argv[ 1 ] );
+ strcpy( spawnname, argv[ 2 ] );
+
+ if(argc >= 4)
+ {
+ strcpy( ainame, argv[ 3 ] );
+ }
+ else
+ {
+ // ever have this case?
+ ainame[0] = 0;
+ }
+
+ rTuneAssertMsg( spInstance->mpMission != NULL, "No mission is selected\n" );
+
+ Vehicle* vehicle = NULL;
+
+ vehicle = spInstance->GetVehicleByName(vehiclename);
+
+#if (RAD_TUNE||RAD_DEBUG)
+ sprintf( errMsg, "ActivateVehicle: Cannot find vehicle called \"%s\"", vehiclename );
+ rTuneAssertMsg( vehicle, errMsg );
+#endif
+
+ if ( argc >= 5 )
+ {
+ vehicle->SetDriverName( argv[4] );
+ }
+
+ CarStartLocator* locator = NULL;
+ if( argc >= 3 && strlen( spawnname ) != 0 )
+ {
+ if (strcmp (spawnname,"NULL") == 0)
+ {
+ //do nothing
+
+ }
+ else
+ {
+ //get a ptr to the locator
+ locator = p3d::find<CarStartLocator>( spawnname );
+
+#if (RAD_TUNE||RAD_DEBUG)
+ sprintf( errMsg, "ActivateVehicle: Could not find the requested Car Start locator \"%s\"", spawnname );
+ rTuneAssertMsg( locator, errMsg );
+#endif
+ }
+ }
+
+#if (RAD_TUNE||RAD_DEBUG)
+ sprintf( errMsg, "ActivateVehicle: This command is invalid because no stage is active." );
+ rTuneAssertMsg( spInstance->mpStage, errMsg );
+#endif
+/*
+MEMTRACK_PUSH_GROUP( "Mission - AI " );
+ VehicleAI* pAI = NULL;
+ if( argc >= 4 )
+ {
+ if( strcmp( ainame, "chase") == 0 )
+ {
+ pAI = new ChaseAI( vehicle );
+ }
+ else if( strcmp( ainame, "waypoint" ) == 0 )
+ {
+ pAI = new WaypointAI( vehicle );
+ }
+ else if ( strcmp( ainame, "NULL" ) == 0 )
+ {
+ pAI = NULL;
+
+ }
+ else
+ {
+ char buffy[64];
+ sprintf( buffy, "Unknown AI vehicle type: %s", argv[ 3 ] );
+ rTuneAssertMsg( false, buffy );
+ }
+ }
+MEMTRACK_POP_GROUP();
+*/
+ int vehicleCentralIndex = GetGameplayManager()->GetMissionVehicleIndex(vehicle);
+ //rAssert(vehicleCentralIndex != -1);
+
+ spInstance->mpStage->AddVehicle( vehicle, vehicleCentralIndex, locator, ainame );
+
+
+MEMTRACK_POP_GROUP( "Mission - Stage Vehicle" );
+
+}
+
+//=============================================================================
+// MissionScriptLoader::AddStageWaypoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddStageWaypoint( int argc, char** argv )
+{
+ Locator* locator = p3d::find<Locator>( argv[ 1 ] );
+ rTuneAssertMsg( locator != NULL, "Could not find the requested waypoint locator\n" );
+
+ rTuneAssertMsg( spInstance->mpStage != NULL, "No stage active\n" );
+
+ spInstance->mpStage->AddWaypoint( locator );
+}
+
+//=============================================================================
+// MissionScriptLoader::AddStageCharacter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddStageCharacter( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "No stage active\n" );
+
+/*
+ // Characters not loaded until Gameplay Context starts
+ //
+ Character* character = GetCharacterManager()->GetCharacterByName( argv[ 1 ] );
+ rTuneAssertMsg( character != NULL, "Could not find the requested character\n" );
+*/
+
+ CarStartLocator* locator = NULL;
+ if ( strcmp( argv[ 2 ], "" ) != 0 )
+ {
+ locator = p3d::find<CarStartLocator>( argv[ 2 ] );
+ rTuneAssertMsg( locator != NULL, "Could not find the requested car start locator\n" );
+ }
+
+ CarStartLocator* carlocator = NULL;
+ if ( argc >= 6 )
+ {
+ carlocator = p3d::find<CarStartLocator>( argv[ 5 ] );
+ rTuneAssertMsg( carlocator != NULL, "Could not find the requested car start locator\n" );
+ }
+
+ const char* dynaloadString = argv[3];
+
+ Vehicle* vehicle = NULL;
+ if( argc >= 5 )
+ {
+ //chuck: we will use the overloader AddCharacter function.
+ if(strcmp(argv[4],"current") ==0)
+ {
+ spInstance->mpStage->AddCharacter( argv[ 1 ], locator, carlocator, dynaloadString, argv[4] );
+ return;
+ }
+
+ vehicle = spInstance->GetVehicleByName( argv[ 4 ] );
+ rTuneAssertMsg( vehicle != NULL, "Could not find the requested vehicle\n" );
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // MS10: Hack to allow character to be selected.
+// char name[32];
+// GetMissionManager()->GetOverrideCharacterName( name );
+// spInstance->mpStage->AddCharacter( name, locator, dynaloadString, vehicle );
+ //
+ // Put this back!
+ //
+ spInstance->mpStage->AddCharacter( argv[ 1 ], locator, carlocator, dynaloadString, vehicle );
+ //
+ ////////////////////////////////////////////////////////////////////////////
+}
+
+//=============================================================================
+// MissionScriptLoader::AddStageMusicChange
+//=============================================================================
+// Description: Sets an indicator used by the music player to tell it that
+// we should change up the music and make it more dramatic for
+// this stage.
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddStageMusicChange( int argc, char** argv )
+{
+ spInstance->mpStage->SetMusicChangeFlag();
+}
+
+//=============================================================================
+// MissionScriptLoader::SetStageMusicAlwaysOn
+//=============================================================================
+// Description: Sets an indicator for the music player to say that we should
+// continue playing music out of the car for this stage.
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetStageMusicAlwaysOn( int argc, char** argv )
+{
+ spInstance->mpStage->SetMusicAlwaysOnFlag();
+}
+
+//=============================================================================
+// MissionScriptLoader::SetCompletionDialog
+//=============================================================================
+// Description: Sets the event name of a dialog line to play when stage
+// is completed
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetCompletionDialog( int argc, char** argv )
+{
+ spInstance->mpStage->SetDialogKey( ::radMakeKey32( argv[1] ) );
+
+ if( argc == 3 )
+ {
+ spInstance->mpStage->SetConversationCharacterKey( tEntity::MakeUID( argv[2] ) );
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::SetStageStartMusicEvent
+//=============================================================================
+// Description: Sets the name of a music event to trigger when the current
+// stage begins
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetStageStartMusicEvent( int argc, char** argv )
+{
+ spInstance->mpStage->SetStageStartMusicEvent( ::radMakeCaseInsensitiveKey32( argv[1] ) );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetMusicState
+//=============================================================================
+// Description: Set a state in that funky radMusic matrix stuff
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetMusicState( int argc, char** argv )
+{
+ spInstance->mpStage->SetStageMusicState( ::radMakeCaseInsensitiveKey32( argv[1] ),
+ ::radMakeCaseInsensitiveKey32( argv[2] ) );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetStageCamera
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetStageCamera( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "No stage active\n" );
+ if ( spInstance->mpStage == NULL )
+ {
+ return;
+ }
+
+ bool cut = atoi( argv[2] ) == 1 ? true : false;
+ bool qTrans = atoi( argv[3] ) == 1 ? true : false;
+
+ if ( strcmp( argv[1], "follow" ) == 0 )
+ {
+ //Set to follow cam
+ spInstance->mpStage->SetCameraInfo( SuperCam::FOLLOW_CAM, cut, qTrans );
+
+ }
+ else if ( strcmp( argv[1], "walker" ) == 0 )
+ {
+ //Set to walker cam
+#ifdef RAD_WIN32
+ spInstance->mpStage->SetCameraInfo( SuperCam::ON_FOOT_CAM, cut, qTrans );
+#else
+ spInstance->mpStage->SetCameraInfo( SuperCam::WALKER_CAM, cut, qTrans );
+#endif
+ }
+ else
+ {
+ rTuneAssertMsg( false, "Unsupported camera type set in script!" );
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::ResetToThisStage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::ResetToThisStage( int argc, char** argv )
+{
+ int stagNum = spInstance->spInstance->mpMission->GetNumStages() - 1;
+ spInstance->mpMission->SetResetToStage( stagNum );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetTrafficDensity
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetTrafficDensity( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "No stage active\n" );
+
+ if ( spInstance->mpStage == NULL )
+ {
+ return;
+ }
+
+ spInstance->mpStage->SetTrafficDensity( static_cast<char>(atoi(argv[1])) );
+}
+
+//=============================================================================
+// MissionScriptLoader::AddCondition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddCondition( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( spInstance->mMissionHeap );
+
+MEMTRACK_PUSH_GROUP( "Mission - Conditions" );
+ rAssert( spInstance->mpCondition == NULL );
+
+ spInstance->mCondType = MissionCondition::COND_INVALID;
+
+ for( unsigned int i = 0; i < MissionCondition::NUM_CONDITIONS; i++ )
+ {
+ if( strcmp( MissionConditionNames::Name[ i ], argv[ 1 ] ) == 0 )
+ {
+ spInstance->mCondType = (MissionCondition::ConditionTypeEnum)( i );
+ break;
+ }
+ }
+
+ rTuneAssertMsg( spInstance->mCondType != MissionCondition::COND_INVALID, "This is not a valid Condition type\n" );
+
+ switch( spInstance->mCondType )
+ {
+ case MissionCondition::COND_FOLLOW_DISTANCE:
+ {
+ FollowCondition* pCond = new FollowCondition();
+
+ spInstance->mpCondition = pCond;
+ break;
+ }
+ case MissionCondition::COND_TIME_OUT:
+ {
+ TimeOutCondition* pCond = new TimeOutCondition();
+
+ spInstance->mpCondition = pCond;
+ break;
+ }
+ case MissionCondition::COND_RACE:
+ {
+ RaceCondition* pCond = new RaceCondition();
+
+ spInstance->mpCondition = pCond;
+ break;
+ }
+ case MissionCondition::COND_OUT_OF_BOUNDS:
+ {
+ OutOfBoundsCondition* pCond = new OutOfBoundsCondition();
+
+ spInstance->mpCondition = pCond;
+ break;
+ }
+ case MissionCondition::COND_VEHICLE_DAMAGE:
+ {
+ DamageCondition* pCond = new DamageCondition();
+
+ spInstance->mpCondition = pCond;
+ break;
+ }
+ case MissionCondition::COND_LEAVE_INTERIOR:
+ {
+ LeaveInteriorCondition* lic = new LeaveInteriorCondition();
+
+ spInstance->mpCondition = lic;
+ break;
+ }
+ case MissionCondition::COND_POSITION:
+ {
+ PositionCondition* pc = new PositionCondition();
+
+ spInstance->mpCondition = pc;
+ break;
+ }
+ case MissionCondition::COND_CARRYING_STATEPROP_COLLECTIBLE:
+ {
+ VehicleCarryingStateProp* vcsp = new VehicleCarryingStateProp();
+ spInstance->mpCondition = vcsp;
+ if ( argc > 2 )
+ {
+ // grab the collectible name, if specified
+ const char* collectiblename = argv[2];
+ vcsp->SetDesiredProp( collectiblename );
+ }
+ break;
+ }
+ case MissionCondition::COND_PLAYER_OUT_OF_VEHICLE:
+ {
+ GetOutOfCarCondition* goocc = new GetOutOfCarCondition();
+ spInstance->mpCondition = goocc;
+ break;
+ }
+ case MissionCondition::COND_NOT_ABDUCTED:
+ {
+ NotAbductedCondition* condition = new NotAbductedCondition();
+ spInstance->mpCondition = condition;
+ break;
+ }
+ case MissionCondition::COND_KEEP_BARREL:
+ {
+ KeepBarrelCondition* kbc = new KeepBarrelCondition();
+ spInstance->mpCondition = kbc;
+
+ if ( argc == 3 )
+ {
+ kbc->JumpBackBy( atoi( argv[2] ) );
+ }
+
+ break;
+ }
+ default:
+ {
+ rTuneAssertMsg( false, "Unknown Condition type\n" );
+ break;
+ }
+ }
+
+
+ rAssert( spInstance->mpCondition != NULL );
+
+ unsigned int num = spInstance->mpStage->GetNumConditions();
+ spInstance->mpStage->SetCondition( num,
+ spInstance->mpCondition );
+ spInstance->mpStage->SetNumConditions( num + 1 );
+MEMTRACK_POP_GROUP( "Mission - Conditions" );
+
+ HeapMgr()->PopHeap( spInstance->mMissionHeap );
+}
+
+//=============================================================================
+// MissionScriptLoader::CloseCondition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::CloseCondition( int argc, char** argv )
+{
+ spInstance->mpCondition = NULL;
+ spInstance->mCondType = MissionCondition::COND_INVALID;
+}
+
+//=============================================================================
+// MissionScriptLoader::AmbientAnimationRandomize
+//=============================================================================
+// Description: should the animations for a particular character be randomized
+// or not?
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AmbientAnimationRandomize( int argc, char** argv )
+{
+ rAssert( argc == 3 );
+ int characterNumber = atoi( argv[ 1 ] );
+ int randomInt = atoi( argv[ 2 ] );
+ bool random = ( randomInt != 0 );
+ spInstance->mpStage->AmbientCharacterAnimationSetRandom( characterNumber, random );
+}
+
+//=============================================================================
+// MissionScriptLoader::ClearAmbientAnimations
+//=============================================================================
+// Description: adds an ambient animation to be played during conversations
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::ClearAmbientAnimations( int argc, char** argv )
+{
+ rAssert( argc == 2 );
+ char* bonusMissionName = argv[ 1 ];
+ const int index = GetGameplayManager()->GetMissionNumByName( bonusMissionName ) - GameplayManager::MAX_MISSIONS; //the bonus missions come after the regular missions
+ rAssert( index >= 0 );
+ BonusMissionInfo* bmi = GetGameplayManager()->GetBonusMissionInfo( index );
+ rAssert( bmi != NULL );
+ bmi->ClearAmbientAnimations();
+}
+
+//=============================================================================
+// MissionScriptLoader::AddAmbientNpcAnimation
+//=============================================================================
+// Description: adds an ambient animation to be played during conversations
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddAmbientNpcAnimation( int argc, char** argv )
+{
+ if( argc == 2 )
+ {
+ spInstance->mpStage->AddAmbientCharacterAnimation( 1, argv[ 1 ] );
+ }
+ else
+ {
+ rAssert( argc == 3 );
+ char* bonusMissionName = argv[ 2 ];
+ const int index = GetGameplayManager()->GetMissionNumByName( bonusMissionName ) - GameplayManager::MAX_MISSIONS; //the bonus missions come after the regular missions
+ rAssert( index >= 0 );
+ BonusMissionInfo* bmi = GetGameplayManager()->GetBonusMissionInfo( index );
+ rAssert( bmi != NULL );
+ bmi->AddAmbientCharacterAnimation( 1, argv[ 1 ] );
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::AddAmbientPcAnimation
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddAmbientPcAnimation( int argc, char** argv )
+{
+ if( argc == 2 )
+ {
+ HeapMgr()->PushHeap( GMA_TEMP );
+ spInstance->mpStage->AddAmbientCharacterAnimation( 0, argv[ 1 ] );
+ HeapMgr()->PopHeap(GMA_TEMP);
+ }
+ else
+ {
+ rAssert( argc == 3 );
+ char* bonusMissionName = argv[ 2 ];
+ const int index = GetGameplayManager()->GetMissionNumByName( bonusMissionName ) - GameplayManager::MAX_MISSIONS; //the bonus missions come after the regular missions
+ rAssert( index >= 0 );
+ BonusMissionInfo* bmi = GetGameplayManager()->GetBonusMissionInfo( index );
+ rAssert( bmi != NULL );
+ bmi->AddAmbientCharacterAnimation( 0, argv[ 1 ] );
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::SetCamBestSide
+//=============================================================================
+// Description: Sets the best side locator name for the conversation cameras
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetCamBestSide( int argc, char** argv )
+{
+ char* name = argv[ 1 ];
+ if( argc == 2 )
+ {
+ spInstance->mpStage->SetBestSideLocator( name );
+ }
+ else
+ {
+ rAssert( argc == 3 );
+ char* bonusMissionName = argv[ 2 ];
+ const int index = GetGameplayManager()->GetMissionNumByName( bonusMissionName ) - GameplayManager::MAX_MISSIONS; //the bonus missions come after the regular missions
+ rAssert( index >= 0 );
+ BonusMissionInfo* bmi = GetGameplayManager()->GetBonusMissionInfo( index );
+ rAssert( bmi != NULL );
+ bmi->SetBestSideLocator( name );
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::SetConversationCamName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetConversationCamName( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( GMA_TEMP );
+ spInstance->mpStage->SetConversationCamName( argv[ 1 ] );
+ HeapMgr()->PopHeap( GMA_TEMP );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetConversationCamPcName
+//=============================================================================
+// Description: sets up the conversation camera used when the PC is talking
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetConversationCamPcName( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( GMA_TEMP );
+ spInstance->mpStage->SetConversationCamPcName( argv[ 1 ] );
+ HeapMgr()->PopHeap( GMA_TEMP );
+}
+//=============================================================================
+// MissionScriptLoader::SetConversationCamNpcName
+//=============================================================================
+// Description: sets up the conversation camera used when the NPC is talking
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetConversationCamNpcName( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( GMA_TEMP );
+ spInstance->mpStage->SetConversationCamNpcName( argv[ 1 ] );
+ HeapMgr()->PopHeap( GMA_TEMP );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetConversationCam
+//=============================================================================
+// Description: sets the conversation camera for a particular line of dialog
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetConversationCam( int argc, char** argv )
+{
+ HeapMgr()->PushHeap( GMA_TEMP );
+ int lineOfDialog = atoi( argv[ 1 ] );
+ tName name = argv[ 2 ];
+ if( argc == 3 )
+ {
+ spInstance->mpStage->SetCameraForDialogLine( lineOfDialog, name );
+ }
+ else
+ {
+ rAssert( argc == 4 );
+ char* bonusMissionName = argv[ 3 ];
+ const int index = GetGameplayManager()->GetMissionNumByName( bonusMissionName ) - GameplayManager::MAX_MISSIONS; //the bonus missions come after the regular missions
+ rAssert( index >= 0 );
+ BonusMissionInfo* bmi = GetGameplayManager()->GetBonusMissionInfo( index );
+ rAssert( bmi != NULL );
+ bmi->SetCameraForDialogLine( lineOfDialog, name );
+ }
+ HeapMgr()->PopHeap( GMA_TEMP );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetConversationCamAngle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetConversationCamAngle( int argc, char** argv )
+{
+ rAssert( argc == 2 );
+ const char* cameraName = argv[ 1 ];
+ float camAngle = static_cast< float >( ::atof( argv[ 2 ] ) );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetConversationCamDistance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetConversationCamDistance( int argc, char** argv )
+{
+ rAssert( argc == 3 );
+ const char* cameraName = argv[ 1 ];
+ float camDistance = static_cast< float >( ::atof( argv[ 2 ] ) );
+ ConversationCam::SetCameraDistanceByName( cameraName, camDistance );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetConversationCamDistance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetPresentationBitmap( int argc, char** argv )
+{
+ bool changeRequired = !CGuiScreenMissionLoad::IsCurrentBitmap( argv[ 1 ] );
+ if( changeRequired )
+ {
+ CGuiScreenMissionLoad::SetBitmapName( argv[ 1 ] );
+ CGuiScreenMissionLoad::ReplaceBitmap();
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::SetAnimatedCameraMulticontrollerName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetAnimatedCameraMulticontrollerName( int argc, char** argv )
+{
+ rAssert( argc > 1 );
+ AnimatedCam::SetMulticontroller( argv[ 1 ] );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetAnimatedCameraName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetAnimatedCameraName( int argc, char** argv )
+{
+ rAssert( argc > 1 );
+ AnimatedCam::ClearCamera();
+ AnimatedCam::SetCamera( argv[ 1 ] );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetMissionStartMulticontrollerName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetMissionStartMulticontrollerName( int argc, char** argv )
+{
+ rAssert( argc > 1 );
+ AnimatedCam::SetMissionStartMulticontroller( argv[ 1 ] );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetMissionStartCameraName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetMissionStartCameraName ( int argc, char** argv )
+{
+ rAssert( argc > 1 );
+ AnimatedCam::SetMissionStartCamera( argv[ 1 ] );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetPlayerCarName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetPlayerCarName( int argc, char** argv )
+{
+ GetSSM()->SetVehicle( ::atoi(argv[1]), argv[2] );
+}
+
+
+/*=============================================================================
+Description: Plays a FMV, usually after a mission. Note that it causes the HUD
+ to be un/reloaded.
+=============================================================================*/
+void MissionScriptLoader::SetFMVInfo( int argc, char** argv )
+{
+ rAssert( argc > 1 );
+
+ if ( spInstance->mObjType != MissionObjective::OBJ_FMV )
+ {
+ rDebugString( "Can only set FMV information in fmv objectives!" );
+ return;
+ }
+
+ FMVObjective* fmvObj = reinterpret_cast<FMVObjective*>(spInstance->mpObjective);
+ fmvObj->SetFileName( argv[ 1 ] );
+
+ if( argc > 2 )
+ {
+ fmvObj->SetMusicStop();
+ }
+}
+//=============================================================================
+// MissionScriptLoader::CharacterIsChild
+//=============================================================================
+// Description: marks a specific character as a child character - camera
+// will be set lower in this case
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::CharacterIsChild( int argc, char** argv )
+{
+ rAssert( argc > 1 );
+ AnimatedCam::SetMulticontroller( argv[ 1 ] );
+}
+//=============================================================================
+// MissionScriptLoader::SetFollowDistances
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetFollowDistances( int argc, char** argv )
+{
+ rAssert( spInstance->mpCondition != NULL );
+
+ switch( spInstance->mCondType )
+ {
+ case MissionCondition::COND_FOLLOW_DISTANCE:
+ {
+ FollowCondition* pCond = (FollowCondition*)(spInstance->mpCondition);
+
+ float dist;
+
+ dist = static_cast<float>( atoi( argv[ 1 ] ));
+ pCond->SetMinDistance( dist );
+
+ dist = static_cast<float>( atoi( argv[ 2 ] ));
+ pCond->SetMaxDistance( dist );
+ break;
+ }
+ default:
+ {
+ // fook!
+ rTuneAssertMsg( false, "The current Condition cannot accept a follow distance\n" );
+ }
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::SetCondMinHealth
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetCondMinHealth( int argc, char** argv )
+{
+ rAssert( spInstance->mpCondition != NULL );
+
+ switch( spInstance->mCondType )
+ {
+ case MissionCondition::COND_VEHICLE_DAMAGE:
+ {
+ DamageCondition* pCond = (DamageCondition*)(spInstance->mpCondition);
+
+ float damage = static_cast<float>( atoi( argv[ 1 ] ));
+ pCond->SetMinValue( damage );
+
+ break;
+ }
+ default:
+ {
+ // fook!
+ rTuneAssertMsg( false, "The current Condition cannot accept a min health value\n" );
+ }
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::SetConditionPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetConditionPosition( int argc, char** argv )
+{
+ rAssert( spInstance->mpCondition != NULL );
+ rAssert( spInstance->mCondType == MissionCondition::COND_POSITION );
+
+ PositionCondition* pc = static_cast<PositionCondition*>(spInstance->mpCondition);
+ pc->SetRequiredPosition( atoi(argv[1] ) );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetCondTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetCondTime( int argc, char** argv )
+{
+ rAssert( spInstance->mpCondition->GetType() == MissionCondition::COND_PLAYER_OUT_OF_VEHICLE );
+ GetOutOfCarCondition* goocc = static_cast<GetOutOfCarCondition*>(spInstance->mpCondition);
+ goocc->SetTime( static_cast<unsigned int>(::atoi( argv[1] )) );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetHitNRun
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetHitNRun( int argc, char** argv )
+{
+ rAssert( spInstance->mpCondition->GetType() == MissionCondition::COND_TIME_OUT );
+ TimeOutCondition* toc = static_cast<TimeOutCondition*>(spInstance->mpCondition);
+ //toc->SetHitNRun();
+}
+
+//=============================================================================
+// MissionScriptLoader::SetObjTargetVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetObjTargetVehicle( int argc, char** argv )
+{
+ rAssert( spInstance->mpObjective != NULL );
+
+ Vehicle* vehicle = NULL;
+
+ if (strcmp("current",argv[1]) ==0)
+ {
+ vehicle = GetGameplayManager()->GetCurrentVehicle();
+ }
+ else
+ {
+ vehicle = spInstance->GetVehicleByName( argv[ 1 ] );
+ }
+
+ rTuneAssertMsg( vehicle != NULL, "Could not find the requested target vehicle\n" );
+
+ switch( spInstance->mObjType )
+ {
+ case MissionObjective::OBJ_FOLLOW:
+ {
+ FollowObjective* pObj = (FollowObjective*)(spInstance->mpObjective);
+ pObj->SetTargetVehicle( vehicle );
+ break;
+ }
+ case MissionObjective::OBJ_LOSETAIL:
+ {
+ LoseObjective* pObj = (LoseObjective*)(spInstance->mpObjective);
+ pObj->SetTargetVehicle( vehicle );
+ break;
+ }
+ case MissionObjective::OBJ_DESTROY:
+ {
+ DestroyObjective* pObj = (DestroyObjective*)(spInstance->mpObjective);
+ pObj->SetTargetVehicle( vehicle );
+ break;
+ }
+ case MissionObjective::OBJ_GETIN:
+ {
+// GetInObjective* pObj = (GetInObjective*)(spInstance->mpObjective);
+// pObj->SetTargetVehicle( vehicle );
+ break;
+ }
+ case MissionObjective::OBJ_DUMP:
+ {
+ CollectDumpedObjective* pObj = (CollectDumpedObjective*)(spInstance->mpObjective);
+ pObj->SetDumpVehicle( vehicle );
+ break;
+ }
+ default:
+ {
+ // fook!
+ rTuneAssertMsg( false, "The current objective cannot accept a target vehicle\n" );
+ }
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::SetObjTargetBoss
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetObjTargetBoss( int argc, char** argv )
+{
+ rAssert( spInstance->mpObjective != NULL );
+
+ // Only supporting destroying right now
+ rAssert( spInstance->mObjType == MissionObjective::OBJ_DESTROY_BOSS );
+
+ rAssert( dynamic_cast< DestroyBossObjective* >( spInstance->mpObjective ) != NULL );
+ DestroyBossObjective* pObj = (DestroyBossObjective*)(spInstance->mpObjective);
+ // Ok, we are looking for a boss, which means UFO, which means
+ // actormanager
+ const char* bossName = argv[1];
+ Actor* actor = GetActorManager()->GetActorByName( bossName );
+ pObj->SetTarget( actor );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetPickupTarget
+//=============================================================================
+// Description: Set an object that the user must pickup in his vehicle
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetPickupTarget( int argc, char** argv )
+{
+ rAssert( spInstance->mObjType == MissionObjective::OBJ_PICKUP_ITEM );
+ rAssert( dynamic_cast< PickupItemObjective* >( spInstance->mpObjective ) != NULL );
+ PickupItemObjective* pObj = (PickupItemObjective*)(spInstance->mpObjective);
+ const char* targetname = argv[1];
+ pObj->SetTarget( targetname );
+}
+
+//=============================================================================
+// MissionScriptLoader::AllowRockOut
+//=============================================================================
+// Description: The PC character will rock out during this objective
+//=============================================================================
+void MissionScriptLoader::AllowRockOut( int argc, char** argv )
+{
+ spInstance->mpObjective->SetRockOut(true);
+}
+
+
+
+//=============================================================================
+// MissionScriptLoader::SetObjDistance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetObjDistance( int argc, char** argv )
+{
+ rAssert( spInstance->mpObjective != NULL );
+
+ switch( spInstance->mObjType )
+ {
+ case MissionObjective::OBJ_LOSETAIL:
+ {
+ LoseObjective* pObj = (LoseObjective*)(spInstance->mpObjective);
+
+ float dist = static_cast<float>( atoi( argv[ 1 ] ));
+ pObj->SetDistance( dist );
+
+ break;
+ }
+ default:
+ {
+ // fook!
+ rTuneAssertMsg( false, "The current objective cannot accept a distance\n" );
+ }
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::SetCondTargetVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetCondTargetVehicle( int argc, char** argv )
+{
+ rAssert( spInstance->mpCondition != NULL );
+
+ Vehicle* vehicle = NULL;
+ if ( strncmp( argv[ 1 ], "current", 7 ) == 0 )
+ {
+ vehicle = GetGameplayManager()->GetCurrentVehicle();
+
+ }
+ else
+ {
+ vehicle = spInstance->GetVehicleByName( argv[ 1 ] );
+ }
+
+ rTuneAssertMsg( vehicle != NULL, "Could not find the requested target vehicle\n" );
+
+ switch( spInstance->mCondType )
+ {
+ case MissionCondition::COND_FOLLOW_DISTANCE:
+ case MissionCondition::COND_RACE:
+ case MissionCondition::COND_VEHICLE_DAMAGE:
+ {
+ VehicleCondition* pCond = (VehicleCondition*)(spInstance->mpCondition);
+ pCond->SetVehicle( vehicle );
+ break;
+ }
+ default:
+ {
+ // fook!
+ rTuneAssertMsg( false, "The current Condition cannot accept a target vehicle\n" );
+ }
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::AddStage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddStage( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpStage == NULL, "No stage active\n" );
+
+ HeapMgr()->PushHeap( spInstance->mMissionHeap );
+ MEMTRACK_PUSH_GROUP( "Mission - Stages" );
+
+ if ( argc <= 3 ) //This should only be "final" and/or some number or nothing.
+ {
+ //This is a standard
+ spInstance->mpStage = new MissionStage();
+
+ for( int i = 1; i < argc; i++ )
+ {
+ if( strcmp(argv[ i ], "final") == 0 )
+ {
+ spInstance->mpStage->SetFinalStage(true);
+ break;
+ }
+ }
+
+ int index = spInstance->mpMission->GetNumStages();
+
+ spInstance->mpMission->SetNumStages( index + 1 );
+ spInstance->mpMission->SetStage( index, spInstance->mpStage );
+
+ spInstance->mpObjective = NULL;
+ spInstance->mpCondition = NULL;
+ }
+ else if ( argc == 4 || argc == 7 )
+ {
+ //This is a new type of add stage that allows for locked stages.
+ unsigned int num = 0;
+ unsigned int i;
+ for ( i = 1; i < (MAX_LOCK_REQUIREMENTS * 3) + 1; i = i + 3 )
+ {
+ if ( strcmp(argv[i], "locked") == 0 )
+ {
+ //This is a locked stage.
+ rAssert( strcmp(argv[i + 1], "skin") == 0 || strcmp(argv[i + 1], "car") == 0 );
+
+ MissionStage::LockRequirement::Type type = MissionStage::LockRequirement::NONE;
+ if ( strcmp(argv[i + 1], "skin") == 0 )
+ {
+ type = MissionStage::LockRequirement::SKIN;
+ }
+ else if ( strcmp(argv[i + 1], "car") == 0 )
+ {
+ type = MissionStage::LockRequirement::CAR;
+ }
+ else
+ {
+ rTuneAssertMsg(false, "Invalid use of locked stage type\n");
+ }
+
+ if ( type != MissionStage::LockRequirement::NONE )
+ {
+ if ( spInstance->mpStage == NULL )
+ {
+ spInstance->mpStage = new MissionStage();
+ int index = spInstance->mpMission->GetNumStages();
+
+ spInstance->mpMission->SetNumStages( index + 1 );
+ spInstance->mpMission->SetStage( index, spInstance->mpStage );
+
+ spInstance->mpObjective = NULL;
+ spInstance->mpCondition = NULL;
+ }
+
+ spInstance->mpStage->SetLockRequirement( num, type, argv[i + 2] );
+ ++num;
+ }
+ }
+ }
+ }
+ else
+ {
+ rTuneAssertMsg( false, "Incorrect format of AddStage script command. Get Cary\n" );
+ }
+
+ MEMTRACK_POP_GROUP( "Mission - Stages" );
+ HeapMgr()->PopHeap(spInstance->mMissionHeap);
+}
+
+//=============================================================================
+// MissionScriptLoader::SetStageMessageIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetStageMessageIndex( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "No stage active\n" );
+
+ int index = atoi( argv[ 1 ] );
+ spInstance->mpStage->SetStartMessageIndex( index );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetStageTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetStageTime( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "No stage active\n" );
+ int factor = GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_EXTRA_TIME) ? 5 : 1;
+ spInstance->mpStage->SetStageTime( MissionStage::STAGETIME_SET, atoi( argv[ 1 ] ) * factor);
+}
+
+//=============================================================================
+// MissionScriptLoader::AddStageTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddStageTime( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "No stage active\n" );
+ int factor = GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_EXTRA_TIME) ? 5 : 1;
+ spInstance->mpStage->SetStageTime( MissionStage::STAGETIME_ADD, atoi( argv[ 1 ] ) * factor);
+}
+
+//=============================================================================
+// MissionScriptLoader::ShowStageComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::ShowStageComplete( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "No stage active\n" );
+ spInstance->mpStage->ShowStageComplete( true );
+
+}
+
+//=============================================================================
+// MissionScriptLoader::SetHUDIcon
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetHUDIcon( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "No stage active\n" );
+
+ spInstance->mpStage->SetHUDIcon( argv[ 1 ] );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetIrisWipe
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetIrisWipe( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "No stage active\n" );
+
+ spInstance->mpStage->SetIrisAtEnd();
+}
+
+//=============================================================================
+// MissionScriptLoader::SetFadeOut
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetFadeOut( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "No stage active\n" );
+
+ spInstance->mpStage->SetFadeOutAtEnd();
+}
+
+//=============================================================================
+// MissionScriptLoader::CloseStage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::CloseStage( int argc, char** argv )
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "Stage already closed!\n" );
+ spInstance->mpStage = NULL;
+}
+
+void MissionScriptLoader::SetVehicleAIParams( int argc, char** argv )
+{
+#if (RAD_TUNE || RAD_DEBUG)
+ char errMsg[256];
+#endif
+
+ rTuneAssertMsg( argc == 4, "Bad number of args for this command!\n"
+ "SYNTAX: SetVehicleAIParams( vehiclename, minshortcutskill, maxshortcutskill )" );
+
+ // parse the args
+ char vehiclename[32];
+ strcpy( vehiclename, argv[1] );
+
+ MissionStage::AIParams params;
+ params.minShortcutSkill = atoi( argv[2] );
+ params.maxShortcutSkill = atoi( argv[3] );
+
+ rAssert( spInstance );
+ Vehicle* vehicle = spInstance->GetVehicleByName( vehiclename );
+
+#if (RAD_TUNE||RAD_DEBUG)
+ sprintf( errMsg, "SetVehicleAIParams: Cannot find vehicle called \"%s\"", vehiclename );
+ rTuneAssertMsg( vehicle, errMsg );
+#endif
+
+#if (RAD_TUNE||RAD_DEBUG)
+ sprintf( errMsg, "SetVehicleAIParams: This command is invalid because no stage is active." );
+ rTuneAssertMsg( spInstance->mpStage, errMsg );
+#endif
+
+ spInstance->mpStage->SetAIParams( vehicle, params );
+
+}
+//=============================================================================
+// MissionScriptLoader::SetBonusMissionStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetBonusMissionStart( int argc, char** argv )
+{
+ rAssert( spInstance->mpStage );
+
+ spInstance->mpStage->MakeBonusObjectiveStart();
+}
+
+//=============================================================================
+// MissionScriptLoader::GetLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char* name )
+//
+// Return: EventLocator
+//
+//=============================================================================
+Locator* MissionScriptLoader::GetLocator( char* name )
+{
+ Locator* locator = p3d::find<Locator>( name );
+
+ rTuneAssertMsg( locator != NULL, "Could not find the requested locator\n" );
+
+ return( locator );
+}
+
+//=============================================================================
+// MissionScriptLoader::GetVehicleOfName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char* name )
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* MissionScriptLoader::GetVehicleByName( const char* name )
+{
+ // change this to look through GameplayManagers list:
+
+ Vehicle* vehicle = GetGameplayManager()->GetMissionVehicleByName(name);
+
+ if(vehicle == NULL)
+ {
+ // also look through the 'user' cars...
+ vehicle = GetGameplayManager()->GetUserVehicleByName(name);
+
+ }
+
+ rTuneAssertMsg(vehicle, "missionscriptloader: can't find vehicle by name\n");
+
+ return vehicle;
+
+ /*
+ if( spInstance->mpMission != NULL )
+ {
+ vehicle = spInstance->mpMission->GetVehicleByName( name );
+ }
+
+ if( vehicle == NULL )
+ {
+ for( int i = 0; i < GetVehicleCentral()->GetMaxActiveVehicles(); i++ )
+ //for( int i = 0; i < GetVehicleCentral()->GetNumVehicles(); i++ )
+ {
+ Vehicle* v = GetVehicleCentral()->GetVehicle( i );
+ if( v == NULL )
+ {
+ continue;
+ }
+ if( strcmp( v->GetName(), name ) == 0 )
+ {
+ vehicle = v;
+ break;
+ }
+ }
+ }
+
+ return vehicle;
+ */
+}
+
+//=============================================================================
+// MissionScriptLoader::GetDirectionalArrowType
+//=============================================================================
+// Description: Returns enumeration based upon string fetched from a mission script
+//
+// Parameters: const char* input string name. Ptr to output enumeration type
+//
+// Return: bool indicating whether or not the arrow is of a valid type
+//
+//=============================================================================
+bool MissionScriptLoader::GetDirectionalArrowType( const char* string, DirectionalArrowEnum::TYPE* outArrowType )
+{
+ if ( string == NULL )
+ return false;
+
+ bool validType = true;
+ if ( strcmp( string, "BOTH") == 0 ||
+ strcmp( string, "both" ) == 0 ||
+ strcmp( string, "b" ) == 0 )
+ {
+ *outArrowType = DirectionalArrowEnum::BOTH;
+ }
+ else if ( strcmp( string, "NEITHER") == 0 ||
+ strcmp( string, "neither" ) == 0 ||
+ strcmp( string, "n" ) == 0 )
+ {
+ *outArrowType = DirectionalArrowEnum::NEITHER;
+ }
+ else if ( strcmp( string, "INTERSECTION") == 0 ||
+ strcmp( string, "intersection" ) == 0 ||
+ strcmp( string, "i" ) == 0 )
+ {
+ *outArrowType = DirectionalArrowEnum::INTERSECTION;
+ }
+ else if ( strcmp( string, "nearest road" ) == 0 ||
+ strcmp( string, "NEAREST ROAD" ) == 0 ||
+ strcmp( string, "n" ) == 0 )
+ {
+ *outArrowType = DirectionalArrowEnum::NEAREST_ROAD;
+ }
+ else
+ {
+ validType = false;
+ }
+
+ return validType;
+}
+
+
+//=============================================================================
+// MissionScriptLoader::LoadP3DFile
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::LoadP3DFile( int argc, char** argv )
+{
+#ifdef MEMORYTRACKER_ENABLED
+ char name[256];
+ sprintf( name, "Mission - Load: %s", argv[1] );
+#else
+ char* name = "";
+#endif
+
+ // HACK HACK HACK : ignore loads ona bunch of stuff that we know we have preloaded
+ if((strcmp(argv[ 1 ], "art\\phonecamera.p3d") == 0) ||
+ (strcmp(argv[ 1 ], "art\\cards.p3d") == 0) ||
+ (strcmp(argv[ 1 ], "art\\wrench.p3d") == 0) ||
+ (strcmp(argv[ 1 ], "art\\missions\\generic\\missgen.p3d") == 0) ||
+ (strcmp(argv[ 1 ], "art\\cars\\common.p3d") == 0) ||
+ (strcmp(argv[ 1 ], "art\\cars\\huskA.p3d") == 0))
+ {
+ return;
+ }
+
+
+ GameMemoryAllocator heap = GMA_LEVEL_MISSION;
+
+ //
+ // tName allocates string space for the life of this function,
+ // so direct it to the temp heap
+ //
+ HeapMgr()->PushHeap( GMA_TEMP );
+ tName heapName = argv[2];
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ if ( argc > 2 )
+ {
+ // 2nd argument is the heap, switch upon it
+ if ( heapName == "GMA_DEFAULT" )
+ heap = GMA_DEFAULT;
+ else if ( heapName == "GMA_TEMP" )
+ heap = GMA_TEMP;
+ #ifdef RAD_GAMECUBE
+ else if ( heapName == "GMA_GC_VMM" )
+ heap = GMA_GC_VMM;
+ #endif
+ else if ( heapName == "GMA_PERSISTENT" )
+ heap = GMA_PERSISTENT;
+ else if ( heapName == "GMA_LEVEL" )
+ {
+ rAssert( false );
+ heap = GMA_LEVEL;
+ }
+ else if ( heapName == "GMA_LEVEL_MOVIE" )
+ heap = GMA_LEVEL_MOVIE;
+ else if ( heapName == "GMA_LEVEL_FE" )
+ heap = GMA_LEVEL_FE;
+ else if ( heapName == "GMA_LEVEL_ZONE" )
+ heap = GMA_LEVEL_ZONE;
+ else if ( heapName == "GMA_LEVEL_OTHER" )
+ heap = GMA_LEVEL_OTHER;
+ else if ( heapName == "GMA_LEVEL_HUD" )
+ heap = GMA_LEVEL_HUD;
+ else if ( heapName == "GMA_LEVEL_MISSION" )
+ heap = GMA_LEVEL_MISSION;
+ else if ( heapName == "GMA_LEVEL_AUDIO" )
+ heap = GMA_LEVEL_AUDIO;
+ else if ( heapName == "GMA_DEBUG" )
+ heap = GMA_DEBUG;
+ else if ( heapName == "GMA_SPECIAL" )
+ heap = GMA_SPECIAL;
+ #ifdef RAD_XBOX
+ else if ( heapName == "GMA_XBOX_SOUND_MEMORY" )
+ heap = GMA_XBOX_SOUND_MEMORY;
+ #endif
+ else
+ {
+ // unrecognized heapName!
+ rTuneAssertMsg( false, "Mission Script LoadP3DFile not given a valid heap!" );
+ }
+ }
+
+ if ( argc == 4 )
+ {
+ GetLoadingManager()->AddRequest( spInstance->mFileHandler, argv[ 1 ], heap, argv[ 3 ], argv[ 1 ], 0, name );
+ }
+ else
+ {
+ GetLoadingManager()->AddRequest( spInstance->mFileHandler, argv[ 1 ], heap, 0, name );
+ }
+}
+
+
+//=============================================================================
+// MissionScriptLoader::LoadDisposableCar
+//=============================================================================
+// Description: Use this to load any vehicle that may get unloaded during a level gameplay session,
+// ie player car,ai car, forced car into its own iventory section.
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::LoadDisposableCar( int argc, char** argv )
+{
+
+ if (strcmp (argv[3],"DEFAULT")== 0)
+ {
+ //loading a default vehicle
+ if ( GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].mp_vehicle !=NULL )
+ {
+ //problem designer wants to load a default car when one has already be specified in the script previously
+ rAssert(0);
+ }
+ else
+ {
+ //load pure3d car into the inventory under its filename
+ GetLoadingManager()->AddRequest( spInstance->mFileHandler, argv[ 1 ], GMA_LEVEL_MISSION, argv[1], "Default" );
+ strcpy(GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].filename,argv[1]);
+ strcpy(GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].name,argv[2]);
+ }
+
+ }
+ else if (strcmp (argv[3],"OTHER") == 0)
+ {
+ if( strcmp(GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].name,argv[2]) ==0)
+ {
+ rTuneAssertMsg(0, "why are you doing this - see greg \n");
+ //we are trying to load a car that already exists,so do nothing
+ }
+ else
+ {
+ //clear player default car
+
+ GetGameplayManager()->DumpCurrentCar();
+ GetGameplayManager()->ClearVehicleSlot(GameplayManager::eOtherCar);
+
+ //TO DO dump Phone booth car if the player is using it.
+ if ( GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].mp_vehicle !=NULL )
+ {
+ //problem default vehicle already defined time to unload it and load another in its place.
+ #ifndef FINAL
+ printf("other vehicle already in inventory, time to dispose of car \n");
+ #endif
+
+ rTuneAssertMsg(0, "this is concerning to me - see greg \n");
+
+
+ //clear the inventory of old car
+ GetGameplayManager()->ClearVehicleSlot(GameplayManager::eOtherCar);
+ //clear player default car
+ GetGameplayManager()->ClearVehicleSlot(GameplayManager::eDefaultCar);
+ //TO DO dump Phone booth car if the player is using it.
+
+ strcpy(GetGameplayManager()->GetVehicleSlotFilename(GameplayManager::eOtherCar),argv[1]);
+ strcpy(GetGameplayManager()->GetVehicleSlotFilename(GameplayManager::eOtherCar),argv[2]);
+ //load pure3d car into the inventory under its filename
+ GetLoadingManager()->AddRequest( spInstance->mFileHandler, argv[ 1 ], GMA_LEVEL_MISSION, argv[1], "Default" );
+
+ }
+ else
+ {
+ strcpy(GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].filename,argv[1]);
+ strcpy(GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].name,argv[2]);
+ //load pure3d car into the inventory under its filename
+ GetLoadingManager()->AddRequest( spInstance->mFileHandler, argv[ 1 ], GMA_LEVEL_MISSION, argv[1], "Default" );
+
+ }
+ }
+ }
+ else if (strcmp(argv[3],"AI")== 0)
+ {
+
+ //loading an AI car
+ GetGameplayManager()->ClearVehicleSlot(GameplayManager::eAICar);
+ strcpy(GetGameplayManager()->mVehicleSlots[GameplayManager::eAICar].filename,argv[1]);
+ strcpy(GetGameplayManager()->mVehicleSlots[GameplayManager::eAICar].name,argv[2]);
+ //load pure3d car into the inventory under its filename
+ //TODO: change GMA_LEVEL_MISSION to GMA_LEVEL_HUD if we deicide to unload cars for FMV
+
+ GetLoadingManager()->AddRequest( spInstance->mFileHandler, argv[ 1 ], GMA_LEVEL_MISSION, argv[1], "Default" );
+
+ }
+ else if (strcmp(argv[3],"CHASE")==0)
+ {
+ //copy the filename for this chase car into the car data struct for the chasemanager
+ //will come in handy when we unload it.
+ strcpy(GetGameplayManager()->m_ChaseManager_Array[0].hostvehiclefilename,argv[1]);
+ GetLoadingManager()->AddRequest(spInstance->mFileHandler,argv[1],GMA_LEVEL_MISSION,"level_chase",argv[1]);
+ }
+
+ else
+ {
+ //loading unknown flag
+ rAssert (0);
+ }
+}
+
+
+
+/*
+==============================================================================
+MissionScriptLoader::AddCharacter
+==============================================================================
+Description: Comment
+
+Parameters: (int argc, char** argv )
+
+Return: void
+
+=============================================================================
+*/
+void MissionScriptLoader::AddCharacter(int argc, char** argv )
+{
+ rAssert( argc == 3 );
+ GetCharacterManager( )->AddPCCharacter( argv[ 1 ], argv[ 2 ] );
+}
+
+
+//=============================================================================
+// MissionScriptLoader::AddNPCCharacterBonusMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddNPCCharacterBonusMission( int argc, char** argv )
+{
+ char uniqueName[16];
+ sprintf(uniqueName, "b_%s", argv[1]);
+
+ Character* c = GetCharacterManager()->AddCharacterDeferedLoad( CharacterManager::NPC, uniqueName, argv[1], argv[2], argv[3] );
+ c->AddToWorldScene();
+ c->SetRole(Character::ROLE_ACTIVE_BONUS);
+
+ if ( !GetGameplayManager()->mIsDemo )
+ {
+ bool isRace = atoi(argv[7]) == 1;
+
+ // Try and retrieve the mission record
+ const MissionRecord* missionRecord = GetCharacterSheetManager()->QueryStreetRaceStatus( GetGameplayManager()->GetCurrentLevelIndex(), argv[4] );
+ if ( missionRecord == NULL )
+ GetCharacterSheetManager()->QueryBonusMissionStatus( GetGameplayManager()->GetCurrentLevelIndex(), argv[4] );
+
+ // Mission record is valid
+ // See if this bonus mission or street race has already been done
+ bool wasMissionAlreadyCompleted;
+ if ( missionRecord != NULL )
+ {
+ wasMissionAlreadyCompleted = missionRecord->mCompleted;
+ }
+ else
+ {
+ wasMissionAlreadyCompleted = false;
+ }
+
+
+ if ( argc == 8 )
+ {
+ GetGameplayManager()->SetBonusMissionInfo( uniqueName, argv[4], argv[5], argv[6], isRace, NULL, wasMissionAlreadyCompleted );
+ }
+ else if ( argc > 8 )
+ {
+ GetGameplayManager()->SetBonusMissionInfo( uniqueName, argv[4], argv[5], argv[6], isRace, argv[8], wasMissionAlreadyCompleted );
+ }
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::SetBonusMissionDialoguePositions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetBonusMissionDialoguePositions( int argc, char** argv )
+{
+ if ( GetGameplayManager()->mIsDemo )
+ {
+ return;
+ }
+
+ int missionNum = GetGameplayManager()->GetMissionNumByName( argv[1] );
+
+ rTuneAssertMsg( missionNum != -1, "Trying to set character positions on non-existant mission\n" );
+ rTuneAssertMsg( missionNum >= GameplayManager::MAX_MISSIONS, "Trying to set bonus mission positions on regular mission\n");
+
+ if ( missionNum != -1 && missionNum >= GameplayManager::MAX_MISSIONS )
+ {
+ CarStartLocator* char1Pos = p3d::find<CarStartLocator>( argv[2] );
+
+#ifndef RAD_RELEASE
+ if ( !char1Pos )
+ {
+ char error[128];
+ sprintf( error, "Could not find %s for dialogue position\n", argv[2] );
+ rTuneAssertMsg( false, error );
+ }
+#endif
+
+ CarStartLocator* char2Pos = p3d::find<CarStartLocator>( argv[3] );
+
+#ifndef RAD_RELEASE
+ if ( !char2Pos )
+ {
+ char error[128];
+ sprintf( error, "Could not find %s for dialogue position\n", argv[3] );
+ rTuneAssertMsg( false, error );
+ }
+#endif
+
+ CarStartLocator* carPos = NULL;
+ if ( argc >= 5 )
+ {
+ //Also need a car start
+ carPos = p3d::find<CarStartLocator>( argv[4] );
+
+#ifndef RAD_RELEASE
+ if ( !carPos )
+ {
+ char error[128];
+ sprintf( error, "Could not find %s for dialogue position\n", argv[4] );
+ rTuneAssertMsg( false, error );
+ }
+#endif
+ }
+
+ BonusMissionInfo* bmi = GetGameplayManager()->GetBonusMissionInfo( missionNum - GameplayManager::MAX_MISSIONS );
+ bmi->SetPositions( char1Pos, char2Pos, carPos );
+ }
+}
+
+
+//=============================================================================
+// MissionScriptLoader::AddAmbientCharacter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddAmbientCharacter( int argc, char** argv )
+{
+ Locator* loc = p3d::find<Locator>( argv[2] );
+ if(!loc)
+ {
+ char error[256];
+ sprintf( error, "Can not find locator: %s, Designer Error!!!!\n", argv[2] );
+ MissionScriptLoader::TreeOfWoeErrorMsg(error);
+ rTuneAssertMsg(0, error );
+ }
+
+ Character* c = GetCharacterManager()->AddCharacterDeferedLoad( CharacterManager::NPC, argv[1], argv[1], "npd", argv[2] );
+ c->SetAmbient(argv[2], (argc == 4) ? (float)atof(argv[3]) : 1.3f);
+ c->AddToWorldScene();
+}
+
+void MissionScriptLoader::AddBonusMissionNPCWaypoint( int argc, char** argv )
+{
+ // Check syntax: commandname, NPCname, locatorname
+ rTuneAssertMsg( argc == 3, "AddBonusMissionNPCWaypoint takes 2 args: NPCName, locatorName" );
+
+ // grab the two arguments
+ rTuneAssert( argv[1] );
+ const char* locName = argv[2];
+ rTuneAssert( locName );
+
+
+ // Bonus mission characters have "b_" prefix... so prepend it before
+ // calling CharacterManager::GetCharacterByName.
+ char npcName[64];
+ sprintf( npcName, "b_%s", argv[1] );
+
+ bool succeeded = AddNPCWaypoint( npcName, locName );
+ rTuneAssertMsg( succeeded, "Failed adding waypoint for a Bonus Mission NPC" );
+}
+
+void MissionScriptLoader::AddObjectiveNPCWaypoint( int argc, char** argv )
+{
+#if (RAD_TUNE || RAD_DEBUG)
+ char errMsg[ 256 ];
+#endif
+
+ // Check syntax: commandname, NPCname, locatorname
+ rTuneAssertMsg( argc == 3, "AddObjectiveNPCWaypoint takes 2 args: NPCName, locatorName" );
+
+ // grab the two arguments
+ const char* npcName = argv[1];
+ rTuneAssert( npcName );
+ const char* locName = argv[2];
+ rTuneAssert( locName );
+
+ // First look for the locator
+ Locator* loc = p3d::find< Locator >( locName );
+ if( loc == NULL )
+ {
+#if (RAD_TUNE || RAD_DEBUG)
+ sprintf( errMsg, "Couldn't find locator %s for NPC %s", locName, npcName );
+ rTuneAssertMsg( false, errMsg );
+#endif
+ return;
+ }
+ rmt::Vector locPos;
+ loc->GetPosition( &locPos );
+
+ // Now, look for the character
+
+ // see if we have an objective.. if so, we search through the objective first
+ // to update its mNPCs member
+ MissionScriptLoader* msl = GetInstance();
+ rAssert( msl );
+
+ if( msl->mpObjective )
+ {
+ bool foundInObjective = msl->mpObjective->AddNPCWaypoint( npcName, locName );
+
+ // if we couldn't add it to the objective or if the NPC does not exist in the
+ // objective, we search for it in Character manager...
+ if( !foundInObjective )
+ {
+#if (RAD_TUNE || RAD_DEBUG)
+ sprintf( errMsg, "Waypoint at locator %s could not be "
+ "added for mission objective NPC %s", locName, npcName );
+ rTuneAssertMsg( false, errMsg );
+#endif
+ return;
+ }
+ }
+ else
+ {
+ // objective doesn't exist... But this is an objective NPC!!
+#if (RAD_TUNE || RAD_DEBUG)
+ sprintf( errMsg, "Mission Objective undefined before calling this command!" );
+ rTuneAssertMsg( false, errMsg );
+#endif
+ return;
+ }
+}
+
+bool MissionScriptLoader::AddNPCWaypoint( const char* npcName, const char* locName )
+{
+#if (RAD_TUNE || RAD_DEBUG)
+ char errMsg[256];
+#endif
+
+ // First look for the locator
+ Locator* loc = p3d::find< Locator >( locName );
+ if( loc == NULL )
+ {
+#if (RAD_TUNE || RAD_DEBUG)
+ sprintf( errMsg, "Couldn't find locator %s for NPC %s", locName, npcName );
+ rTuneAssertMsg( false, errMsg );
+#endif
+ return false;
+ }
+ rmt::Vector locPos;
+ loc->GetPosition( &locPos );
+
+ // Now, look for the character in CharacterManager
+ Character* character = GetCharacterManager()->GetCharacterByName( npcName );
+ if( character == NULL )
+ {
+#if (RAD_TUNE || RAD_DEBUG)
+ sprintf( errMsg, "NPC %s does not exist in character manager", npcName );
+ rTuneAssertMsg( false, errMsg );
+#endif
+ return false;
+ }
+
+ // Better make sure it is also an NPC
+ rAssert( character->IsNPC() );
+ NPCharacter* npc = (NPCharacter*) character;
+
+ // Make sure the controller exists (gets created in NPC constructor)
+ NPCController* npcController = (NPCController*) npc->GetController();
+ rAssert( npcController );
+
+ bool succeeded = npcController->AddNPCWaypoint( locPos );
+ if( !succeeded )
+ {
+#if (RAD_TUNE || RAD_DEBUG)
+ sprintf( errMsg, "Could not add waypoint at %s for NPC %s", locName, npcName );
+ rTuneAssertMsg( false, errMsg );
+#endif
+ return false;
+ }
+ return true;
+}
+
+
+void MissionScriptLoader::AddPurchaseCarNPCWaypoint( int argc, char** argv )
+{
+
+ // Check syntax: commandname, NPCname, locatorname
+ rTuneAssertMsg( argc == 3, "AddPurchaseCarNPCWaypoint takes 2 args: NPCName, locatorName" );
+
+ // grab the two arguments
+ rTuneAssert( argv[1] );
+ const char* locName = argv[2];
+ rTuneAssert( locName );
+
+ // Purchase car NPCs are named differently
+ char npcName[64];
+ sprintf( npcName, "reward_%s", argv[1] );
+
+ bool succeeded = AddNPCWaypoint( npcName, locName );
+ rTuneAssertMsg( succeeded, "Failed to add waypoint for Purchase Car Reward NPC." );
+}
+
+void MissionScriptLoader::AddAmbientNPCWaypoint( int argc, char** argv )
+{
+ // Check syntax: commandname, NPCname, locatorname
+ rTuneAssertMsg( argc == 3, "AddAmbientNPCWaypoint takes 2 args: NPCName, locatorName" );
+
+ // grab the two arguments
+ const char* npcName = argv[1];
+ rTuneAssert( npcName );
+ const char* locName = argv[2];
+ rTuneAssert( locName );
+
+ bool succeeded = AddNPCWaypoint( npcName, locName );
+ rTuneAssertMsg( succeeded, "Failed to add waypoint for Ambient NPC." );
+}
+
+//=============================================================================
+// MissionScriptLoader::ActivateTrigger
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::ActivateTrigger( int argc, char** argv )
+{
+ Locator* loc;
+
+ loc = p3d::find<Locator>( argv[1] );
+
+ rAssert( loc );
+
+ loc->SetFlag( Locator::ACTIVE, true );
+}
+
+//=============================================================================
+// MissionScriptLoader::DeactivateTrigger
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::DeactivateTrigger( int argc, char** argv )
+{
+ Locator* loc;
+
+ loc = p3d::find<Locator>( argv[1] );
+
+ rAssert( loc );
+
+ loc->SetFlag( Locator::ACTIVE, false );
+}
+/*
+//=============================================================================
+// MissionScriptLoader::InitAIVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::InitAIVehicle( int argc, char** argv )
+{
+ char locator[16], vehicleName[16], aitype[16];
+ strcpy( vehicleName, argv[ 1 ]);
+ strcpy( aitype, argv[ 2 ]);
+ strcpy( locator, argv[ 3 ]);
+
+ Vehicle* vehicle = GetVehicleCentral()->InitVehicle( vehicleName, false );
+
+ GetGameplayManager()->PlaceAtLocatorName( vehicle, locator );
+
+ GetRenderManager()->mpLayer(RenderEnums::LevelSlot)->AddGuts((tDrawable*)( vehicle ) );
+
+ VehicleAI* pAI;
+ if( strcmp( aitype, "follow") == 0 )
+ {
+ pAI = new ChaseAI( vehicle );
+ ((ChaseAI*)pAI)->SetTarget( GetVehicleCentral()->GetVehicle( 0 ) );
+ }
+ else if( strcmp( aitype, "waypoint" ) == 0 )
+ {
+ pAI = new WaypointAI( vehicle );
+ }
+ else
+ {
+ char buffy[64];
+ sprintf( buffy, "Unknown AI vehicle type: %s", aitype );
+ rTuneAssertMsg( false, buffy );
+ }
+ GetVehicleCentral()->SetVehicleController( GetVehicleCentral()->GetVehicleId( vehicle ), pAI );
+
+ pAI->Initialize();
+
+ spInstance->mpCurrentVehicleAI = pAI;
+}
+
+//=============================================================================
+// MissionScriptLoader::AddAIWaypoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddAIWaypoint( int argc, char** argv )
+{
+ rAssert( spInstance->mpCurrentVehicleAI );
+
+ Locator* loc = p3d::find<Locator>( argv[1] );
+ rAssert( loc );
+
+ WaypointAI* wpAI = dynamic_cast<WaypointAI*>( spInstance->mpCurrentVehicleAI );
+ rTuneAssertMsg( wpAI != NULL, "This is not a waypoint following vehicle AI\n" );
+
+ wpAI->AddWaypoint( loc );
+}
+*/
+/*
+==============================================================================
+MissionScriptLoader::CreateAnimPhysObject
+==============================================================================
+Description: Comment
+
+Parameters: ( int argc, char** argv )
+
+Return: void
+
+=============================================================================
+*/
+void MissionScriptLoader::CreateAnimPhysObject( int argc, char** argv )
+{
+// No longer neccessary; loading being done uniformly now.
+// You have been assimilated.
+ rTuneAssertMsg( false, "No longer neccessary; loading being done uniformly now. You have been assimilated.\n" );
+}
+
+void MissionScriptLoader::CreateActionEventTrigger( int argc, char** argv )
+{
+//const char* triggerName, rmt::Vector& pos, float r );
+ rmt::Vector pos;
+ pos.x = static_cast<float>( atoi( argv[ 2 ] ) );
+ pos.y = static_cast<float>( atoi( argv[ 3 ] ) );
+ pos.z = static_cast<float>( atoi( argv[ 4 ] ) );
+
+ float r = static_cast<float>( atoi( argv[ 5 ] ) );
+
+ GetActionButtonManager()->CreateActionEventTrigger( argv[1], pos, r );
+}
+void MissionScriptLoader::LinkActionToObjectJoint( int argc, char** argv )
+{
+ //const char* objectName, const char* jointName, const char* triggerName, const char* typeName, const char* buttonName );
+
+ GetActionButtonManager()->LinkActionToObjectJoint( argv[1], argv[2], argv[3], argv[4], argv[5] );
+}
+void MissionScriptLoader::LinkActionToObject( int argc, char** argv )
+{
+ //const char* objectName, const char* triggerName, const char* typeName, const char* buttonName = (const char*)0 );
+
+ GetActionButtonManager()->LinkActionToObject( argv[1], argv[2], argv[3], argv[4], argv[5], false );
+}
+
+void MissionScriptLoader::SetCoinDrawable( int argc, char** argv )
+{
+ rAssert( argc >= 1 );
+ tDrawable* coinDrawable = p3d::find<tDrawable>( argv[ 1 ] );
+ GetCoinManager()->SetCoinDrawable( coinDrawable );
+}
+
+void MissionScriptLoader::SetParticleTexture( int argc, char** argv )
+{
+ tTexture* t = p3d::find<tTexture>( argv[ 2 ] );
+ int i = atoi( argv[ 1 ] );
+ GetSparkleManager()->SetTexture( i, t );
+}
+
+//adds a safezone to a stage
+void MissionScriptLoader::AddSafeZone(int argc, char** argv)
+{
+ Locator* locator = NULL;
+
+ locator=p3d::find<Locator> (argv[1]) ;
+ int radius =10;
+
+ radius= atoi(argv[2]);
+
+ rAssert( dynamic_cast< CarStartLocator* > (locator ) != NULL );
+ CarStartLocator* carstartlocator = static_cast < CarStartLocator*>(locator);
+
+ rAssert(carstartlocator);
+ rTuneAssertMsg( spInstance->mpStage != NULL, "This Command: AddSafeZone needs to be in a stage \n" );
+ spInstance->mpStage->AddSafeZone(carstartlocator,radius);
+}
+
+
+
+
+//create a new ped group
+void MissionScriptLoader::CreatePedGroup(int argc,char** argv)
+ {
+ rAssert(argc == 2);
+ int groupid = atoi(argv[1]);
+ GetMissionScriptLoader()->mp_ModelGroup = new PedestrianManager::ModelGroup;
+ GetMissionScriptLoader()->mPedGroupID = groupid;
+ GetMissionScriptLoader()->mp_ModelGroup->numModels =0;
+ }
+
+void MissionScriptLoader::AddPed(int argc,char** argv)
+ {
+ //if you asserted here then the wrong number of arguements have been passed
+ rAssert(argc == 3);
+ //check if group is full, if its full then dont add the ped
+ if (spInstance->mp_ModelGroup->numModels == PedestrianManager::MAX_MODELS)
+ {
+ //you asserted here cause your adding too many peds to a group.
+ rTuneAssert(0);
+
+ }
+ else
+ {
+ char name [64];
+ int max_number;
+ strncpy(name,argv[1],64);
+ name[63]='\0';
+ max_number = atoi(argv[2]);
+ spInstance->mp_ModelGroup->models[spInstance->mp_ModelGroup->numModels].Init(name,max_number);
+ spInstance->mp_ModelGroup->numModels++;
+ }
+ }
+
+//Closes a ped grp.
+void MissionScriptLoader::ClosePedGroup(int argc, char** argv)
+ {
+ if (spInstance->mp_ModelGroup->numModels == 0 )
+ {
+ //if your asserted here, then the group has no peds, smack the designer.
+ rTuneAssert(0);
+ }
+
+ PedestrianManager::GetInstance()->SetModelGroup(spInstance->mPedGroupID, *(spInstance->mp_ModelGroup));
+ spInstance->mPedGroupID = -1;
+ delete spInstance->mp_ModelGroup;
+ spInstance->mp_ModelGroup =NULL;
+ }
+
+
+void MissionScriptLoader::UsePedGroup( int argc, char** argv )
+{
+ rTuneAssert(argc == 2);
+ int groupId = atoi(argv[1]);
+
+ rTuneAssertMsg( 0 <= groupId && groupId < PedestrianManager::MAX_MODEL_GROUPS,
+ "Invalid groupId" );
+
+ spInstance->mpMission->SetInitPedGroup( groupId );
+}
+
+void MissionScriptLoader::BindReward (int argc,char** argv)
+ {
+
+ char name [16];
+ char filename [64];
+ int level =0;
+ Reward::eQuestType qtype = Reward::eBlank;
+ Reward::eRewardType rtype = Reward::eNULL;
+
+ //Arguement check
+ //rTuneAssertMsg((argc >= 6 && argc <= 8) ,"Incorrect Number of Arguements passed \n");
+
+ strncpy (name,argv[1],16);
+ name[15] = '\0';
+ strncpy (filename,argv[2],64);
+ filename[63] = '\0';
+ level =atoi (argv[5]);
+
+ //decrement level since all the internal arrays for levels are 0-6, F#@King level enums!!
+ level--;
+
+ //check for the reward type
+ if ( strcmp (argv[3],"skin") ==0)
+ {
+ rtype = Reward::ALT_SKIN_OTHER;
+ }
+ else if (strcmp (argv[3],"car") ==0)
+ {
+ rtype = Reward::ALT_PLAYERCAR;
+ }
+ else if (strcmp (argv[3],"fe_toy") ==0)
+ {
+ rtype = Reward::FE_TOY;
+ }
+ else
+ {
+ rTuneAssertMsg(0,"ERROR: Unknown Reward type! \n");
+ }
+
+ bool isMerchandise = false;
+
+ //check for quest type
+ if (strcmp (argv[4],"streetrace") ==0)
+ {
+ qtype = Reward::eStreetRace;
+ }
+ else if (strcmp (argv[4],"bonusmission") ==0)
+ {
+ qtype =Reward::eBonusMission;
+ }
+ else if (strcmp (argv[4],"cards") ==0)
+ {
+ qtype = Reward::eCards;
+ }
+ else if (strcmp (argv[4],"goldcards") ==0)
+ {
+ qtype = Reward::eGoldCards;
+ }
+ else if (strcmp (argv[4],"forsale") ==0)
+ {
+ isMerchandise = true;
+ }
+ else if (strcmp (argv[4],"defaultcar") == 0)
+ {
+ qtype = Reward::eDefaultCar;
+ }
+ else if (strcmp (argv[4],"defaultskin") == 0)
+ {
+ qtype = Reward::eDefaultSkin;
+ }
+ else
+ {
+ printf("ERROR: Unknown Quest type %s !\n",argv[4]);
+ }
+
+ if( isMerchandise )
+ {
+ rTuneAssertMsg( argc == 8, "Invalid number of arguments!" );
+
+ int cost = atoi(argv[6]);
+
+ Merchandise::eSellerType sellerType = Merchandise::INVALID_SELLER_TYPE;
+
+ if( strcmp( argv[ 7 ], "interior" ) == 0 )
+ {
+ sellerType = Merchandise::SELLER_INTERIOR;
+ }
+ else if( strcmp( argv[ 7 ], "simpson" ) == 0 )
+ {
+ sellerType = Merchandise::SELLER_SIMPSON;
+ }
+ else if( strcmp( argv[ 7 ], "gil" ) == 0 )
+ {
+ sellerType = Merchandise::SELLER_GIL;
+ }
+ else
+ {
+ rTuneAssertMsg( false, "Invalid seller type!" );
+ }
+
+ GetRewardsManager()->AddMerchandise(name,filename,rtype,level,cost,sellerType);
+ }
+ else
+ {
+ //update the RewardsManager
+ GetRewardsManager()->BindReward(name,filename,rtype,qtype,level);
+ }
+
+
+ }//end of Bind Reward
+
+
+//use to set the cars attributes displayed in the phone booth interface
+void MissionScriptLoader::SetCarAttributes(int argc,char** argv)
+{
+ char CarName [16] = "NULL";
+
+ //check the correct number of parameters
+ rTuneAssertMsg (argc == 6,"Incorrect number of parameter for the Set Car Attribute\n");
+
+ //check the name length
+ if(strlen(argv[1]) >16)
+ {
+ rTuneAssertMsg(0,"Name is too long \n");
+ }
+ strncpy(CarName,argv[1],16);
+ CarName[15]='\0';
+
+ float speed = static_cast<float>( atof(argv[2]) );
+ float acceleration = static_cast<float>( atof(argv[3]) );
+ float toughness= static_cast<float>( atof(argv[4]) );
+ float stability = static_cast<float>( atof(argv[5]) );
+
+ GetRewardsManager()->SetCarAttributes(CarName,speed,acceleration,toughness,stability);
+}
+
+//=============================================================================
+// MissionScriptLoader::SetTotalGags
+//=============================================================================
+// Description:
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetTotalGags(int argc,char ** argv)
+{
+ rAssert( argc >= 2 );
+ int level = atoi( argv[ 1 ] ) - 1;
+ int numGags = atoi( argv[ 2 ] );
+
+ GetRewardsManager()->SetTotalGags( level, numGags );
+}
+
+//=============================================================================
+// MissionScriptLoader::SetTotalWasps
+//=============================================================================
+// Description:
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetTotalWasps(int argc,char ** argv)
+{
+ rAssert( argc >= 2 );
+ int level = atoi( argv[ 1 ] ) - 1;
+ int numWasps = atoi( argv[ 2 ] );
+
+ GetRewardsManager()->SetTotalWasps( level, numWasps );
+}
+
+//create a new traffic group
+void MissionScriptLoader::CreateTrafficGroup(int argc,char** argv)
+ {
+ rAssert(argc == 2);
+ int groupid = atoi(argv[1]);
+ spInstance->mp_TrafficGroup=TrafficManager::GetInstance()->GetTrafficModelGroup(groupid);
+ rTuneAssert(spInstance->mp_TrafficGroup);
+ spInstance->mp_TrafficGroup->ClearGroup();
+ }
+
+void MissionScriptLoader::AddTrafficModel(int argc,char** argv)
+ {
+ //check if group is full, if its full then dont add the ped
+ {
+ char name [64];
+ int max_number;
+ strncpy(name,argv[1],64);
+ name[63]='\0';
+ max_number = atoi(argv[2]);
+ spInstance->mp_TrafficGroup->AddTrafficModel(name,max_number);
+ }
+
+ if ((GetGameFlow()->GetCurrentContext () == CONTEXT_GAMEPLAY) ||
+ (GetGameFlow()->GetCurrentContext () == CONTEXT_LOADING_GAMEPLAY))
+ {
+ bool isBig = false;
+ if ( argc == 4 )
+ {
+ isBig = (atoi( argv[3] ) == 1);
+ }
+
+ if ( !isBig )
+ {
+ //Add this to the Parked Car Manager
+ GetPCM().AddCarType( argv[1] );
+ }
+ }
+ }
+
+//Closes a traffic grp.
+void MissionScriptLoader::CloseTrafficGroup(int argc, char** argv)
+ {
+ spInstance->mp_TrafficGroup =NULL;
+ }
+
+
+//setting the respawn vaules inside the respawn manager
+void MissionScriptLoader::SetRespawnRate(int argc,char ** argv)
+{
+ int seconds =0;
+ seconds = atoi(argv[2]);
+ char item[32];
+ strncpy(item,argv[1],32);
+ item[31] ='\0';
+
+ seconds = seconds * 1000; //change seconds into milliseconds
+
+ if (strcmp(item,"wrench") == 0)
+ {
+ GetGameplayManager()->GetRespawnManager()->SetWrenchRespawnTime(seconds);
+ }
+ else if (strcmp(item,"nitro")==0)
+ {
+ GetGameplayManager()->GetRespawnManager()->SetNitroRespawnTime(seconds);
+ }
+ else if (strcmp(item,"wasp")==0)
+ {
+ GetGameplayManager()->GetRespawnManager()->SetWaspRespawnTime(seconds);
+ }
+ else
+ {
+ rReleasePrintf("Unknown %s item type \n",item);
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::EnableTutorialMode
+//=============================================================================
+// Description: Triggers the tutorial mode to be enabled
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::EnableTutorialMode(int argc,char ** argv)
+{
+ int enableInt = atoi( argv[ 1 ] );
+ bool enable = ( enableInt != 0 );
+#ifdef RAD_WIN32
+
+ if( !GetInputManager()->GetController(0)->IsTutorialDisabled() )
+ {
+#endif
+ TutorialManager::GetInstance()->EnableTutorialMode( enable );
+#ifdef RAD_WIN32
+ }
+#endif
+}
+
+//=============================================================================
+// MissionScriptLoader::StartCountdown
+//=============================================================================
+// Description: starts the 321go countdown
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::StartCountdown(int argc,char ** argv)
+{
+ tUID secondUID;
+
+ if( argc == 3 )
+ {
+ secondUID = tEntity::MakeUID( argv[2] );
+ }
+ else
+ {
+ secondUID = 0;
+ }
+
+ spInstance->mpStage->SetCountdownEnabled( radMakeKey32( argv[ 1 ] ), secondUID );
+}
+
+//=============================================================================
+// MissionScriptLoader::AddToCountdownSequence
+//=============================================================================
+// Description:
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::AddToCountdownSequence(int argc,char ** argv)
+{
+ if( argc > 2 )
+ {
+ spInstance->mpStage->AddCountdownSequenceUnit( argv[ 1 ],
+ atoi( argv[ 2 ] ) );
+ }
+ else
+ {
+ spInstance->mpStage->AddCountdownSequenceUnit( argv[ 1 ] );
+ }
+}
+
+//=============================================================================
+// MissionScriptLoader::SetCarStartCamera
+//=============================================================================
+// Description: this is the camera that will be used when a car is loaded from
+// a phone booth
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::SetCarStartCamera( int argc, char** argv )
+{
+}
+
+//=============================================================================
+// MissionScriptLoader::GoToPattyAndSelmaScreenWhenDone
+//=============================================================================
+// Description: should we trigger the patty and selma screen when we're done
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::GoToPattyAndSelmaScreenWhenDone( int argc, char** argv )
+{
+ spInstance->mpStage->GoToPattyAndSelmaScreenWhenDone();
+}
+
+//=============================================================================
+// MissionScriptLoader::StreetRacePropsLoad
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::StreetRacePropsLoad( int argc, char** argv )
+{
+ spInstance->mpMission->LoadStreetRaceProps( argv[ 1 ] );
+}
+
+
+//=============================================================================
+// MissionScriptLoader::StreetRacePropsUnload
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+void MissionScriptLoader::StreetRacePropsUnload( int argc, char** argv )
+{
+ spInstance->mpMission->UnloadStreetRaceProps( argv[ 1 ] );
+}
+
+
+void MissionScriptLoader::UseElapsedTime(int argc,char** argv)
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "This Command: UseElapsedTime needs to be in a stage \n" );
+ spInstance->mpStage->UseElapsedTime();
+}
+
+
+void MissionScriptLoader::AttachStatePropCollectible(int argc,char** argv)
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+
+ // Create a new statepropcollectible and attach it to the current
+ // player vehicle
+ const char* vehicleName = argv[1];
+ const char* statepropName = argv[2];
+
+ rAssert( spInstance != NULL );
+ spInstance->mpMission->AttachStatePropCollectible( statepropName, vehicleName, 2 );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_MISSION );
+}
+
+void MissionScriptLoader::ShowHUD(int argc,char** argv)
+{
+ bool isShowHUD = ( strcmp( argv[ 1 ], "false" ) != 0 );
+
+ rAssert( spInstance != NULL && spInstance->mpMission != NULL );
+ spInstance->mpMission->ShowHUD( isShowHUD );
+}
+
+void MissionScriptLoader::SetNumValidFailureHints( int argc, char** argv )
+{
+ rAssert( spInstance != NULL && spInstance->mpMission != NULL );
+ spInstance->mpMission->SetNumValidFailureHints( atoi( argv[ 1 ] ) );
+}
+
+//used to set the ParTime for Gamble Missions ONLY!!!!
+void MissionScriptLoader::SetParTime(int argc,char** argv)
+{
+ int i =0;
+ //first check that this we have a race objective first
+ if (spInstance->mObjType == MissionObjective::OBJ_RACE)
+ {
+ RaceObjective* pRaceObjective = dynamic_cast <RaceObjective*> (spInstance->mpObjective);
+
+ //check if its a gamble race
+ if (pRaceObjective->QueryIsGambleRace() == true)
+ {
+ pRaceObjective->SetParTime(atoi(argv[1]));
+ }
+ else
+ {
+ rTuneAssertMsg(0,"ERROR: You CANNOT set a par time for a NON GAMBLE Race Objective!!!!\n");
+ }
+ }
+ else
+ {
+ rTuneAssertMsg(0,"ERROR: You CANNOT set a par time for a NON RACE Objective!!!!\n");
+ }
+}
+
+
+//Sets The entery fee for a stage.
+void MissionScriptLoader::SetRaceEnteryFee(int argc, char** argv)
+{
+ int enteryfee = -1;
+ enteryfee=atoi(argv[1]);
+ if(enteryfee < 0 )
+ {
+ rTuneAssertMsg(0,"ERROR: Entery Fee MUST be at least GREATER than 1 coin, Double Check your Script!!\n");
+ }
+ else
+ {
+ spInstance->mpStage->SetRaceEnteryFee(enteryfee);
+ }
+}
+
+
+//Set the Coins Fee Required to Pass a Coin Objective
+void MissionScriptLoader::SetCoinFee(int argc,char** argv)
+{
+ int coinfee=0;
+ coinfee=atoi(argv[1]);
+ if (coinfee < 0)
+ {
+ rTuneAssertMsg(0,"ERROR: Entery Fee MUST be at least GREATER than 1 coin, Double Check your Script!!\n");
+ }
+ else
+ {
+ if ( spInstance->mObjType == MissionObjective::OBJ_COIN)
+ {
+ CoinObjective* pCoinObjective = dynamic_cast <CoinObjective*> (spInstance->mpObjective);
+ rAssert(pCoinObjective);
+ pCoinObjective->SetCoinFee(coinfee);
+
+ }
+ }
+}
+
+
+//Force the MF player in their car on mission stage start
+void MissionScriptLoader::PutMFPlayerInCar(int argc,char** argv)
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "This Command: PutMFPlayerInCar needs to be in a stage \n" );
+ spInstance->mpStage->PutMFPlayerInCar();
+}
+
+// Set which element of a stateprop is to be treated as the shadow (or light pool)
+void MissionScriptLoader::SetStatepropShadow(int argc,char** argv)
+{
+ const char* compDrawName = argv[1];
+ const char* compDrawElement = argv[2];
+
+ AnimDynaPhysLoader::SetShadowElement( compDrawName, compDrawElement );
+}
+
+
+void MissionScriptLoader::TreeOfWoeErrorMsg(const char* outputbuffer)
+{
+#ifndef RAD_XBOX
+#ifndef FINAL
+ IRadTextDisplay* textDisplay;
+
+ ::radTextDisplayGet( &textDisplay, GMA_DEFAULT );
+
+ if ( textDisplay )
+ {
+ textDisplay->SetBackgroundColor( 0 );
+ textDisplay->SetTextColor( 0xffffffff );
+ textDisplay->Clear();
+ textDisplay->TextOutAt("Another Body for The Tree Of Woe", 5, 2 );
+ textDisplay->TextOutAt(outputbuffer, 5, 5 );
+ textDisplay->SwapBuffers();
+ textDisplay->Release();
+ }
+#endif
+#endif
+}
+
+
+void MissionScriptLoader::DisableHitAndRun(int argc,char** argv)
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "This Command: DisableHitAndRun needs to be in a stage \n" );
+ spInstance->mpStage->DisableHitAndRun();
+ GetHitnRunManager()->DisableHitnRun();
+}
+
+void MissionScriptLoader::EnableHitAndRun(int argc,char** argv)
+{
+ GetHitnRunManager()->EnableHitnRun();
+}
+
+void MissionScriptLoader::ResetHitAndRun(int argc,char** argv)
+{
+ GetHitnRunManager()->ResetState();
+}
+
+void MissionScriptLoader::SetNumChaseCars(int argc,char** argv)
+{
+ int numofcars =0;
+ numofcars = atoi(argv[1]);
+ GetHitnRunManager()->SetNumChaseCars(numofcars);
+}
+
+void MissionScriptLoader::SetHitAndRunDecay(int argc,char** argv)
+{
+ float value = 0.0f;
+ value = static_cast<float>(atof(argv[1]));
+ GetHitnRunManager()->SetDecayRate(value);
+}
+
+void MissionScriptLoader::SetHitAndRunDecayInterior(int argc,char** argv)
+{
+ float value = 0.0f;
+ value = static_cast<float>(atof(argv[1]));
+ GetHitnRunManager()->SetDecayRateInside(value);
+}
+
+void MissionScriptLoader::SetHitAndRunMeter(int argc,char** argv)
+{
+ float value = 0.0f;
+ value = static_cast<float>(atof(argv[1]));
+ GetHitnRunManager()->SetHitnRunValue(value);
+}
+
+void MissionScriptLoader::SwapInDefaultCar(int argc,char** argv)
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "This Command: SwapInDefaultCar needs to be in a stage \n" );
+ spInstance->mpStage->SwapInDefaultCar();
+}
+
+
+void MissionScriptLoader::SetSwapDefaultCarRespawnLocatorName(int argc,char** argv)
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "This Command: SetSwapDefaultCarLocator needs to be in a stage \n" );
+ spInstance->mpStage->SetSwapDefaultCarRespawnLocatorName(argv[1]);
+}
+
+void MissionScriptLoader::SetSwapForcedCarRespawnLocatorName(int argc,char** argv)
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "This Command: SetSwapForcedCarLocator needs to be in a stage \n" );
+ spInstance->mpStage->SetSwapForcedCarRespawnLocatorName(argv[1]);
+}
+
+void MissionScriptLoader::SetSwapPlayerRespawnLocatorName(int argc,char** argv)
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "This Command: SetSwapPlayerLocator needs to be in a stage \n" );
+ spInstance->mpStage->SetSwapPlayerRespawnLocatorName(argv[1]);
+}
+
+
+void MissionScriptLoader::NoTrafficForStage(int argc,char** argv)
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "This Command:NoTrafficForStage needs to be in a stage \n" );
+ spInstance->mpStage->DisableTraffic();
+}
+
+
+void MissionScriptLoader::ClearTrafficForStage(int argc,char** argv)
+{
+ rTuneAssertMsg( spInstance->mpStage != NULL, "This Command: ClearTrafficForStage needs to be in a stage \n" );
+ spInstance->mpStage->ClearTrafficForStage();
+}
+
+
+void MissionScriptLoader::AddGlobalProp(int argc,char** argv)
+{
+ //GetGlobalPropManager()->AddProp(argv[1]));
+}
+
+void MissionScriptLoader::PlacePlayerAtLocatorName(int argc,char** argv)
+{
+ rTuneAssertMsg(spInstance->mpStage != NULL," You must USE: PlacePlayerAtLocatorName inside the Stage Command!! \n");
+ spInstance->mpStage->SetPlayerRespawnLocatorName(argv[1]);
+}
+
+void MissionScriptLoader::msPlacePlayerCarAtLocatorName(int argc,char** argv)
+{
+ rTuneAssertMsg(spInstance->mpStage != NULL," You must USE: msPlacePlayerCarAtLocatorName inside the Stage Command!! \n");
+ spInstance->mpStage->SetmsPlayerCarRespawnLocatorName(argv[1]);
+}
+
+
+
+void MissionScriptLoader::SetStageAIRaceCatchupParams( int argc, char** argv )
+{
+ rAssert( spInstance );
+
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ char cmd[64];
+ sprintf( cmd, "SetStageAIRaceCatchupParams" );
+ char errMsg[512];
+#endif
+
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ sprintf( errMsg, "You must use: %s inside the Stage Command!!", cmd );
+ rTuneAssertMsg( spInstance->mpStage != NULL, errMsg);
+#endif
+
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ sprintf( errMsg, "Bad number of args. Syntax: %s( STAGEVEHICLENAME, "
+ "ALONGROADDISTFROMPLAYER_TO_APPLYMAXCATCHUP, "
+ "FRACTIONPLAYERSPEED_MINCATCHUP, "
+ "FRACTIONPLAYERSPEED_NOCATCHUP, "
+ "FRACTIONPLAYERSPEED_MAXCATCHUP )", cmd );
+ rTuneAssertMsg( argc == 6, errMsg );
+#endif
+
+
+
+ char vehiclename[32];
+ strcpy( vehiclename, argv[1] );
+ Vehicle* vehicle = spInstance->GetVehicleByName( vehiclename );
+
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ sprintf( errMsg, "%s: Cannot find vehicle called \"%s\"", cmd, vehiclename );
+ rTuneAssertMsg( vehicle, errMsg );
+#endif
+
+ VehicleAI::RaceCatchupParams params;
+ params.DistMaxCatchup = (float) atof(argv[2]);
+ params.FractionPlayerSpeedMinCatchup = (float) atof(argv[3]);
+ params.FractionPlayerSpeedMidCatchup = (float) atof(argv[4]);
+ params.FractionPlayerSpeedMaxCatchup = (float) atof(argv[5]);
+
+ spInstance->mpStage->SetAIRaceCatchupParams( vehicle, params );
+
+}
+void MissionScriptLoader::SetStageAIEvadeCatchupParams( int argc, char** argv )
+{
+ rAssert( spInstance );
+
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ char cmd[64];
+ sprintf( cmd, "SetStageAIEvadeCatchupParams" );
+ char errMsg[512];
+#endif
+
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ sprintf( errMsg, "You must use: %s inside the Stage Command!!", cmd );
+ rTuneAssertMsg( spInstance->mpStage != NULL, errMsg);
+#endif
+
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ sprintf( errMsg, "Bad number of args. Syntax: %s( STAGEVEHICLENAME, "
+ "CROWFLYDIST_PLAYERTOONEAR, CROWFLYDIST_PLAYERFARENOUGH )", cmd );
+ rTuneAssertMsg( argc == 4, errMsg );
+#endif
+
+
+ char vehiclename[32];
+ strcpy( vehiclename, argv[1] );
+ Vehicle* vehicle = spInstance->GetVehicleByName( vehiclename );
+
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ sprintf( errMsg, "%s: Cannot find vehicle called \"%s\"", cmd, vehiclename );
+ rTuneAssertMsg( vehicle, errMsg );
+#endif
+
+ VehicleAI::EvadeCatchupParams params;
+ params.DistPlayerTooNear = (float) atof(argv[2]);
+ params.DistPlayerFarEnough = (float) atof(argv[3]);
+
+ spInstance->mpStage->SetAIEvadeCatchupParams( vehicle, params );
+
+
+}
+void MissionScriptLoader::SetStageAITargetCatchupParams( int argc, char** argv )
+{
+ rAssert( spInstance );
+
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ char cmd[64];
+ sprintf( cmd, "SetStageAITargetCatchupParams" );
+ char errMsg[512];
+#endif
+
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ sprintf( errMsg, "You must use: %s inside the Stage Command!!", cmd );
+ rTuneAssertMsg( spInstance->mpStage != NULL, errMsg);
+#endif
+
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ sprintf( errMsg, "Bad number of args. Syntax: %s( STAGEVEHICLENAME, "
+ "CROWFLYDIST_PLAYERNEARENOUGH, CROWFLYDIST_PLAYERTOOFAR )", cmd );
+ rTuneAssertMsg( argc == 4, errMsg );
+#endif
+
+
+ char vehiclename[32];
+ strcpy( vehiclename, argv[1] );
+ Vehicle* vehicle = spInstance->GetVehicleByName( vehiclename );
+
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ sprintf( errMsg, "%s: Cannot find vehicle called \"%s\"", cmd, vehiclename );
+ rTuneAssertMsg( vehicle, errMsg );
+#endif
+
+ VehicleAI::TargetCatchupParams params;
+ params.DistPlayerNearEnough = (float) atof(argv[2]);
+ params.DistPlayerTooFar = (float) atof(argv[3]);
+
+ spInstance->mpStage->SetAITargetCatchupParams( vehicle, params );
+}
+
+
+
+void MissionScriptLoader::SetPauseDuration(int argc,char** argv)
+{
+ if (spInstance->mpObjective != NULL)
+ {
+ if (spInstance->mObjType == MissionObjective::OBJ_TIMER)
+ {
+ TimerObjective* pTimerObjective = dynamic_cast <TimerObjective*> (spInstance->mpObjective);
+ rAssert(pTimerObjective);
+ unsigned int milliseconds =0;
+ milliseconds = atoi(argv[1]);
+ //convert to milliseconds
+ milliseconds *= 1000;
+ pTimerObjective->SetTimer( milliseconds);
+ }
+ else
+ {
+ rTuneAssertMsg(0," You must USE SetPauseDuration : inside a Timer Objective \n");
+ }
+ }
+ else
+ {
+ rTuneAssertMsg(0,"Timer Objective has not been declared, You must use AddObjective(timer); first \n");
+ }
+}
+
+
+void MissionScriptLoader::SetCharacterToHide(int argc,char** argv)
+{
+ rTuneAssert(spInstance->mpStage != NULL);
+ spInstance->mpStage->SetCharacterToHide(argv[1]);
+}
+
+void MissionScriptLoader::SetLevelOver(int argc,char** argv)
+{
+ rTuneAssert(spInstance->mpStage != NULL);
+ spInstance->mpStage->SetLevelOver();
+}
+
+void MissionScriptLoader::SetGameOver(int argc, char** argv)
+{
+ rTuneAssert(spInstance->mpStage != 0);
+ spInstance->mpStage->SetGameOver();
+}
+
+void MissionScriptLoader::StayInBlack(int argc,char** argv)
+{
+ rTuneAssertMsg(spInstance->mpStage != NULL,"You cant use this command to a Stage that doesnt exist, Use AddStage() before this command! \n");
+ spInstance->mpStage->mbStayBlackForStage= true;
+}
+
+void MissionScriptLoader::AllowMissionAbort(int argc,char** argv)
+{
+ rTuneAssertMsg(spInstance->mpStage != NULL,"You cant use this command to a Stage that doesnt exist, Use AddStage() before this command! \n");
+
+ bool allowMissionAbort = (strcmp( argv[ 1 ], "false" ) != 0);
+ spInstance->mpStage->SetMissionAbortEnabled( allowMissionAbort );
+}
+
diff --git a/game/code/mission/missionscriptloader.h b/game/code/mission/missionscriptloader.h
new file mode 100644
index 0000000..84d73d9
--- /dev/null
+++ b/game/code/mission/missionscriptloader.h
@@ -0,0 +1,350 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: missionscriptloader.h
+//
+// Description: Blahblahblah
+//
+// History: 12/06/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef MISSIONSCRIPTLOADER_H
+#define MISSIONSCRIPTLOADER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <loading/filehandlerenum.h>
+#include <loading/loadingmanager.h>
+
+#include <memory/srrmemory.h>
+
+#include <mission/objectives/missionobjective.h>
+#include <mission/conditions/missioncondition.h>
+#include <worldsim/ped/pedestrianmanager.h>
+
+#include <console/console.h>
+#include <constants/directionalarrowenum.h>
+
+
+//========================================
+// Forward References
+//========================================
+
+class TrafficModelGroup;
+class EventLocator;
+class Mission;
+class MissionStage;
+class VehicleAI;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class MissionScriptLoader : public Console::ExecuteScriptCallback
+{
+public:
+ // Static Methods for accessing this singleton.
+ static MissionScriptLoader* CreateInstance();
+ static MissionScriptLoader* GetInstance();
+ static void DestroyInstance();
+
+ void Register();
+
+ void LoadScript( char* filename );
+ void LoadScriptAsync( char* filename, LoadingManager::ProcessRequestsCallback* callback = NULL );
+
+ void SetFileHander( FileHandlerEnum handler ) { mFileHandler = handler; }
+
+ void OnExecuteScriptComplete( void* pUserData );
+
+ //Making ptr to this Struct public cuz of dusits damn structs
+ PedestrianManager::ModelGroup* mp_ModelGroup;
+ int mPedGroupID;
+
+ TrafficModelGroup* mp_TrafficGroup;
+
+private:
+ MissionScriptLoader();
+ virtual ~MissionScriptLoader();
+
+ //Prevent wasteful constructor creation.
+ MissionScriptLoader( const MissionScriptLoader& missionscriptloader );
+ MissionScriptLoader& operator=( const MissionScriptLoader& missionscriptloader );
+
+ //
+ // *** for GameCube only ***
+ //
+ static void SetLanguage( int argc, char** argv );
+
+ //
+ // Level functions
+ //
+ static void InitLevelPlayerVehicle( int argc, char** argv );
+ static void PlacePlayerCar( int argc, char** argv );
+ /*
+ static void DisableTraffic (int argc, char** argv );
+ static void EnableTraffic (int argc, char** argv );
+ */
+ static void AddPurchaseCarReward( int argc, char** argv );
+ static void SetPostLevelFMV( int argc, char** argv );
+
+ static void AddGlobalProp(int argc,char** argv );
+
+ //Ped Group
+ static void CreatePedGroup(int argc, char** argv );
+ static void AddPed(int argc, char** argv );
+ static void ClosePedGroup (int argc, char** argv );
+ static void UsePedGroup( int argc, char** argv );
+
+ //Reward Binding
+ static void BindReward(int argc, char** argv);
+ static void SetCarAttributes(int argc, char** argv);
+
+ //Wasps and Gags (in rewards script)
+ static void SetTotalGags(int argc, char** argv);
+ static void SetTotalWasps(int argc, char** argv);
+
+ //Traffic Group
+ static void CreateTrafficGroup(int argc, char** argv);
+ static void AddTrafficModel(int argc, char** argv);
+ static void CloseTrafficGroup(int argc, char** argv);
+
+ //
+ //Chase/Harrass AI Car functions
+ //
+ static void CreateChaseManager(int argc ,char** argv );
+ static void DisableHitAndRun(int argc,char** argv );
+ static void EnableHitAndRun(int argc,char** argv );
+ static void SetHitAndRunMeter(int argc,char** argv );
+ static void SetNumChaseCars(int argc,char** argv );
+ static void SetChaseSpawnRate(int argc, char** argv );
+ static void KillAllChaseAI(int argc,char** argv);
+ static void ResetHitAndRun(int argc,char** argv );
+ static void SetHitAndRunDecay(int argc,char** argv );
+ static void SetHitAndRunDecayInterior(int argc,char** argv );
+
+ //
+ // Mission functions
+ //
+ static void AddMission( int argc, char** argv );
+ static void AddBonusMission( int argc, char** argv );
+ static void SelectMission( int argc, char** argv );
+ static void SetMissionNameIndex( int argc, char** argv );
+ //static void InitMissionPlayerVehicle( int argc, char** argv );
+ static void SetMissionResetPlayerInCar( int argc, char** argv );
+ static void SetMissionResetPlayerOutCar( int argc, char** argv );
+ static void SetDynaLoadData( int argc, char** argv );
+ static void AddBonusObjective( int argc, char** argv );
+ static void SetForcedCar( int argc, char** argv );
+ static void CloseMission( int argc, char** argv );
+ static void SetDemoLoopTime( int argc, char** argv ); //This isn't really dependant on the mission.
+ static void StreetRacePropsLoad(int argc, char** argv);
+ static void StreetRacePropsUnload(int argc, char** argv);
+ static void UseElapsedTime(int argc,char** argv);
+ static void AttachStatePropCollectible(int argc,char** argv);
+ static void ShowHUD(int argc,char** argv);
+ static void SetNumValidFailureHints(int argc,char** argv);
+
+ //
+ // Mission Stage functions
+ //
+ static void AddStage( int argc, char** argv );
+ static void SetStageMessageIndex( int argc, char** argv );
+ static void SetStageTime( int argc, char** argv );
+ static void AddStageTime( int argc, char** argv );
+ static void AddStageVehicle( int argc, char** argv );
+ static void AddStageWaypoint( int argc, char** argv );
+ static void AddStageCharacter( int argc, char** argv );
+ static void AddStageMusicChange( int argc, char** argv );
+ static void SetStageMusicAlwaysOn( int argc, char** argv );
+ static void SetStageStartMusicEvent( int argc, char** argv );
+ static void SetCompletionDialog( int argc, char** argv );
+ static void SetMusicState( int argc, char** argv );
+ static void SetStageCamera( int argc, char** argv );
+ static void MoveStageVehicle( int argc, char** argv );
+ static void ActivateVehicle(int argc, char** argv );
+ static void ResetToThisStage( int argc, char** argv );
+ static void SetTrafficDensity( int argc, char** argv );
+ static void AddSafeZone ( int argc, char** argv );
+ static void SetBonusMissionStart( int argc, char** argv );
+ static void ShowStageComplete( int argc, char** argv );
+ static void SetHUDIcon( int argc, char** argv );
+ static void SetIrisWipe( int argc, char** argv );
+ static void SetFadeOut( int argc, char** argv );
+ static void CloseStage( int argc, char** argv );
+ static void SetVehicleAIParams( int argc, char** argv );
+ static void SetCoinFee(int argc,char** argv);
+ static void PutMFPlayerInCar(int argc,char** argv);
+ static void SetStatepropShadow(int argc,char** argv);
+ static void SwapInDefaultCar(int argc,char** argv);
+ static void SetSwapPlayerRespawnLocatorName(int argc,char** argv);
+ static void SetSwapDefaultCarRespawnLocatorName(int argc,char** argv);
+ static void SetSwapForcedCarRespawnLocatorName(int argc,char** argv);
+ static void NoTrafficForStage(int argc,char** argv);
+ static void ClearTrafficForStage(int argc,char** argv);
+ static void PlacePlayerAtLocatorName(int argc,char** argv);
+ static void msPlacePlayerCarAtLocatorName(int argc,char** argv);
+ static void SetCharacterToHide(int argc,char** argv);
+ static void SetLevelOver(int argc,char** argv);
+ static void SetGameOver(int argc, char** argv);
+ static void StayInBlack(int argc,char** argv);
+ static void AllowMissionAbort(int argc,char** argv);
+
+ static void SetStageAIRaceCatchupParams( int argc, char** argv );
+ static void SetStageAIEvadeCatchupParams( int argc, char** argv );
+ static void SetStageAITargetCatchupParams( int argc, char** argv );
+
+
+ //Gamble Race Specific Commands
+ static void SetParTime(int argc, char** argv);
+ static void SetRaceEnteryFee(int argc, char** argv);
+
+ //
+ // Objective methods
+ //
+ static void AddObjective( int argc, char** argv );
+ static void AddCollectible( int argc, char** argv );
+ static void AddCollectibleStateProp( int argc, char** argv );
+ static void SetCollectibleEffect( int argc, char** argv );
+ static void SetDestination( int argc, char** argv );
+ static void SetObjTargetVehicle( int argc, char** argv );
+ static void SetObjTargetBoss( int argc, char** argv );
+ static void SetPickupTarget( int argc, char** argv );
+ static void SetObjDistance( int argc, char** argv );
+ static void AddNPC( int argc, char** argv );
+ static void RemoveNPC( int argc, char** argv );
+ static void AddDriver( int argc, char** argv );
+ static void RemoveDriver( int argc, char** argv );
+ static void SetTalkToTarget( int argc, char** argv );
+ static void SetDialogueInfo( int argc, char** argv );
+ static void SetDialoguePositions( int argc, char** argv );
+ static void SetFMVInfo( int argc, char** argv );
+ static void SetRaceLaps( int argc, char** argv );
+ static void BindCollectibleToWaypoint( int argc, char** argv );
+ static void AllowUserDump( int argc, char** argv );
+ static void SetVehicleToLoad( int argc, char** argv );
+ static void AllowRockOut( int argc, char** argv );
+ static void TurnGotoDialogOff( int argc, char** argv );
+ static void SetPauseDuration(int argc,char** argv );
+ static void MustActionTrigger( int argc, char** argv );
+ static void CloseObjective( int argc, char** argv );
+
+ //
+ // Condition methods
+ //
+ static void AddCondition( int argc, char** argv );
+ static void SetCondTargetVehicle( int argc, char** argv );
+ static void SetFollowDistances( int argc, char** argv );
+ static void SetCondMinHealth( int argc, char** argv );
+ static void SetConditionPosition( int argc, char** argv );
+ static void SetCondTime( int argc, char** argv );
+ static void SetHitNRun( int argc, char** argv );
+ static void CloseCondition( int argc, char** argv );
+
+ //
+ // Conversation/Presentation methods
+ //
+ static void CharacterIsChild ( int argc, char** argv );
+ static void SetConversationCamName ( int argc, char** argv );
+ static void SetConversationCamPcName ( int argc, char** argv );
+ static void SetConversationCamNpcName ( int argc, char** argv );
+ static void SetConversationCam ( int argc, char** argv );
+ static void SetConversationCamDistance( int argc, char** argv );
+ static void SetConversationCamAngle ( int argc, char** argv );
+ static void SetPresentationBitmap ( int argc, char** argv );
+ static void EnableTutorialMode ( int argc, char** argv );
+ static void AmbientAnimationRandomize ( int argc, char** argv );
+ static void ClearAmbientAnimations ( int argc, char** argv );
+ static void AddAmbientNpcAnimation ( int argc, char** argv );
+ static void AddAmbientPcAnimation ( int argc, char** argv );
+ static void SetAnimatedCameraMulticontrollerName( int argc, char** argv );
+ static void SetAnimatedCameraName ( int argc, char** argv );
+ static void SetMissionStartMulticontrollerName( int argc, char** argv );
+ static void SetMissionStartCameraName ( int argc, char** argv );
+ static void SetCamBestSide ( int argc, char** argv );
+ static void StartCountdown ( int argc, char** argv );
+ static void AddToCountdownSequence ( int argc, char** argv );
+ static void SetCarStartCamera ( int argc, char** argv );
+ static void GoToPattyAndSelmaScreenWhenDone( int argc, char** argv );
+
+ //SuperSprint methods
+ static void SetPlayerCarName( int argc, char** argv );
+
+ //
+ // Loading methods
+ //
+
+public:
+ //Use to load cars that can be unloaded from memory easily. like the Default Car and Forced cars, and AI cars.
+ static void LoadDisposableCar(int argc, char** argv );
+
+ //spew text on the screen in for Ps2
+ static void TreeOfWoeErrorMsg(const char* outputbuffer);
+
+
+private:
+ static void LoadP3DFile( int argc, char** argv );
+ static void AddCharacter( int argc, char** argv );
+ static void AddNPCCharacterBonusMission( int argc, char** argv );
+ static void SetBonusMissionDialoguePositions( int argc, char** argv );
+ static void AddAmbientCharacter( int argc, char** argv );
+
+ // adding waypoints for NPCs to walk on
+ static void AddBonusMissionNPCWaypoint( int argc, char** argv );
+ static void AddObjectiveNPCWaypoint( int argc, char** argv ); // syntax: name locatorName
+ static void AddAmbientNPCWaypoint( int argc, char** argv );
+ static void AddPurchaseCarNPCWaypoint( int argc, char** argv );
+
+ static void ActivateTrigger( int argc, char** argv );
+ static void DeactivateTrigger( int argc, char** argv );
+
+ static void CreateAnimPhysObject( int argc, char** argv );
+ static void CreateActionEventTrigger( int argc, char** argv );
+ static void LinkActionToObjectJoint( int argc, char** argv );
+ static void LinkActionToObject( int argc, char** argv );
+ static void SetCoinDrawable( int argc, char** argv );
+ static void SetParticleTexture( int argc, char** argv );
+
+ //used to set respawnrate of wrenches and nitro etc.
+ static void SetRespawnRate (int argc,char** argv );
+
+
+
+ //
+ // Helper methods
+ //
+ static bool AddNPCWaypoint( const char* npcName, const char* locName );
+ Locator* GetLocator( char* name );
+ Vehicle* GetVehicleByName( const char* name );
+ // Return the type of directional arrow from the given string
+ // returns true if the arrow is valid, false if not
+ static bool GetDirectionalArrowType( const char* arg, DirectionalArrowEnum::TYPE* outArrowType );
+
+ GameMemoryAllocator mMissionHeap;
+
+ Mission* mpMission;
+ MissionStage* mpStage;
+ MissionObjective* mpObjective;
+ MissionObjective::ObjectiveTypeEnum mObjType;
+ MissionCondition* mpCondition;
+ MissionCondition::ConditionTypeEnum mCondType;
+
+ FileHandlerEnum mFileHandler;
+
+ bool mFirstObjective;
+
+
+ static MissionScriptLoader* spInstance;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline MissionScriptLoader* GetMissionScriptLoader() { return( MissionScriptLoader::GetInstance() ); }
+
+
+#endif //MISSIONSCRIPTLOADER_H
+
diff --git a/game/code/mission/missionstage.cpp b/game/code/mission/missionstage.cpp
new file mode 100644
index 0000000..6bf3087
--- /dev/null
+++ b/game/code/mission/missionstage.cpp
@@ -0,0 +1,2553 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: missionstage.cpp
+//
+// Description: Implement MissionStage
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <contexts/context.h>
+#include <gameflow/gameflow.h>
+
+#include <ai/vehicle/chaseai.h>
+#include <ai/vehicle/vehicleai.h>
+#include <ai/vehicle/waypointai.h>
+
+#include <meta/locator.h>
+#include <meta/zoneeventlocator.h>
+#include <meta/carstartlocator.h>
+
+#include <mission/gameplaymanager.h>
+#include <mission/missionmanager.h>
+#include <mission/missionstage.h>
+#include <mission/mission.h>
+#include <mission/objectives/missionobjective.h>
+#include <mission/conditions/missioncondition.h>
+#include <mission/conditions/timeoutcondition.h>
+#include <mission/conditions/getoutofcarcondition.h>
+#include <mission/safezone/safezone.h>
+#include <mission/objectives/raceobjective.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guiscreenmissionload.h>
+#include <presentation/gui/ingame/hudevents/hudeventhandler.h>
+
+#include <worldsim/avatarmanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/harass/chasemanager.h>
+#include <worldsim/hitnrunmanager.h>
+
+#include <camera/supercamcentral.h>
+#include <camera/supercammanager.h>
+#include <camera/animatedcam.h>
+
+#include <memory/srrmemory.h>
+
+#include <debug/profiler.h>
+
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h>
+#include <worldsim/redbrick/vehiclecontroller/vehiclemappable.h>
+
+#include <roads/roadmanager.h>
+#include <roads/road.h>
+#include <roads/roadsegment.h>
+#include <roads/intersection.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// MissionStage::MissionStage
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MissionStage::MissionStage() :
+ mState( STAGE_IDLE ),
+ mObjective( NULL ),
+ mNumConditions( 0 ),
+ mStageTimeType( STAGETIME_SET ),
+ mStageTime( 0 ),
+ mNumVehicles( 0 ),
+ mNumWaypoints( 0 ),
+ mNumCharacters( 0 ),
+ mbFinalStage( false ),
+ miNameIndex( -1 ),
+ miStartMessageIndex( -1 ),
+ mbShowMessage( true ),
+ mMusicChange( false ),
+ mKeepMusicOn( false ),
+ mMusicEventKey( 0 ),
+ mMusicStateKey( 0 ),
+ mMusicStateEventKey( 0 ),
+ mDialogEventKey( 0 ),
+ mb_InsideSafeZone (false),
+ mb_UseElapsedTime(false),
+ mRaceEnteryFee(0),
+ mbRacePaidOut( false ),
+ mTrafficDensity( -1 ),
+ mIsBonusObjectiveStart( false ),
+ mStartBonusObjectives( false ),
+ mMissionLocked( false ),
+ mShowStageComplete( false ),
+ mCountdownEnabled( false ),
+ mIrisAtEnd( false ),
+ mFadeOutAtEnd( false ),
+ mIrisSpeed( 1.0f ),
+ mCountdownSequenceUnits( NULL ),
+ mNumCountdownSequenceUnits( 0 ),
+ mCountdownDialogKey( 0 ),
+ mSecondSpeakerUID( 0 ),
+ mAllowMissionAbort( true )
+{
+#ifdef RAD_DEBUG
+ static int count = 0;
+ m_Id = count;
+ ++count;
+
+ if( m_Id == 12 )
+ {
+ int i = 0;
+ }
+#endif
+ strcpy( mHUDIconImage, "" );
+
+ int i;
+
+ //initializing the damn chasevehicle struct.
+ for (i=0; i<MAX_CHASE_STRUCTS;i++)
+ {
+ strcpy(m_ChaseData_Array[i].vehiclename,"NULL");
+ m_ChaseData_Array[i].mChaseVehicleSpawnRate = 0;
+ }
+
+ //intialize the safezones.
+ for (i=0;i<MAX_SAFEZONES;i++)
+ {
+ m_SafeZone_Array[i]=NULL;
+ }
+
+ for(i=0;i<MAX_CONDITIONS;i++)
+ {
+ mConditions[i] = NULL;
+ }
+
+ //intialize VehicleName Arrary to null
+ for(i=0;i<MAX_CHARACTERS_IN_STAGE;i++)
+ {
+ strcpy(mCharacters[i].VehicleName,"NULL");
+ }
+
+ mbClearTrafficForStage = false;
+ mbNoTrafficForStage = false;
+ mbPutPlayerInCar = false;
+ mb_DisableHitAndRun = false;
+ mbStayBlackForStage = false;
+ mbDisablePlayerControlForCountDown = false;
+
+ //
+ // By default line 0 of the conversation should be a wide angle PC shot
+ //
+ SetCameraForDialogLine( 0, "npc_far" );
+ //Chuck Setting the Forced Car Locators to "NULL"
+
+ strcpy(mSwapDefaultCarRespawnLocatorName,"NULL");
+ strcpy(mSwapForcedCarRespawnLocatorName,"NULL");
+ strcpy(mSwapPlayerRespawnLocatorName,"NULL");
+ strcpy(mPlayerRespawnLocatorName,"NULL");
+ strcpy(mPlayerCarRespawnLocatorName,"NULL");
+ strcpy(mCharacterToHide,"NULL");
+ mbLevelOver =false;
+ mbGameOver = false;
+ mResetCounter = 0;
+
+}
+
+//==============================================================================
+// MissionStage::~MissionStage
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MissionStage::~MissionStage()
+{
+ if( mCountdownSequenceUnits != NULL )
+ {
+ delete [] mCountdownSequenceUnits;
+ mCountdownSequenceUnits = NULL;
+ }
+
+ mNumCountdownSequenceUnits = 0;
+
+ delete mObjective;
+ mObjective = NULL;
+
+ int i;
+ for( i = 0; i < mNumConditions; i++ )
+ {
+ delete mConditions[ i ];
+ mConditions[ i ] = NULL;
+ }
+ mNumConditions = 0;
+
+ for( i = 0; i < mNumVehicles; i++ )
+ {
+ //This will be done too many times, but should be okay...
+ //GetVehicleCentral()->RemoveVehicleFromActiveList( mVehicles[ i ].vehicle );
+
+ // greg
+ // jan 7, 2003
+ // no longer do this here!
+ //
+ // GameplayManger owns these cars and is the only one that will add to and remove from ActiveList
+
+ if(mVehicles[ i ].vehicle != NULL)
+ {
+ mVehicles[ i ].vehicle->Release();
+ }
+ mVehicles[ i ].vehicle = NULL;
+ mVehicles[ i ].spawn = NULL;
+
+
+ if ( mVehicles[ i ].vehicleAINum != -1 )
+ {
+ GetVehicleCentral()->SetVehicleController( mVehicles[ i ].vehicleAINum, NULL );
+ }
+
+ if (mVehicles[ i ].vehicleAI != NULL)
+ {
+ mVehicles[ i ].vehicleAI->Release();
+ }
+ mVehicles[ i ].vehicleAI = NULL;
+ }
+ mNumVehicles = 0;
+
+ for( i = 0; i < mNumCharacters; i++ )
+ {
+ if ( mCharacters[ i ].character )
+ {
+ mCharacters[ i ].character->Release();
+ mCharacters[ i ].character = NULL;
+ }
+
+ if ( mCharacters[ i ].pZoneEventLocator )
+ {
+ mCharacters[ i ].pZoneEventLocator->Release();
+ mCharacters[ i ].pZoneEventLocator = NULL;
+ }
+
+ if ( mCharacters[ i ].locator != NULL )
+ {
+ mCharacters[ i ].locator->Release();
+ mCharacters[ i ].locator = NULL;
+ }
+
+ if ( mCharacters[ i ].carLocator != NULL )
+ {
+ mCharacters[ i ].carLocator->Release();
+ mCharacters[ i ].carLocator = NULL;
+ }
+
+ if( mCharacters[ i ].vehicle != NULL )
+ {
+ //mCharacters[ i ].vehicle->Release();
+ mCharacters[ i ].vehicle = NULL;
+ }
+ }
+ DestroyAllSafeZones();
+}
+
+//==============================================================================
+// MissionStage::DestroyStageVehicleAI
+//==============================================================================
+// Description: After STAGE_COMPLETE is satified prepare to terminate all vehicle AI owned by this stage
+// with EXTREME PREJUDICE, by setting the AI into a inactive state
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+int MissionStage::DestroyStageVehicleAI()
+{
+
+ // TODO - greg
+ // jan 8, 2003
+
+ // stop calling this at the end of a stage when we switch to the new
+ // system of not killing and reallocating cars and ai on a mission restart.
+
+
+
+ int i;
+
+ for( i = 0; i < mNumVehicles; i++ )
+ {
+
+
+ if (mVehicles[ i ].vehicleAI != NULL)
+ {
+
+ if ( mVehicles[ i ].vehicleAINum != -1 )
+ {
+ GetVehicleCentral()->SetVehicleController( mVehicles[ i ].vehicleAINum, NULL );
+ }
+
+ if (mVehicles[ i ].vehicleAI != NULL)
+ {
+ mVehicles[ i ].vehicleAI->SetActive(false);
+ mVehicles[ i ].vehicleAI->Release();
+ }
+
+ mVehicles[ i ].vehicleAI = NULL;
+
+ }
+
+ }
+ return 0;
+}
+
+
+
+
+//=============================================================================
+// MissionStage::AddVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* vehicle, CarStartLocator* spawnlocator )
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::AddVehicle( Vehicle* vehicle,
+ int vehicleCentralIndex,
+ CarStartLocator* spawnlocator,
+ char* ainame)
+{
+ MEMTRACK_PUSH_GROUP( "MissionStage" );
+ //wrap allocates with the heap manager
+
+ // check if we already have this vehicle in our list.
+ // then, if we do, update it's ai and spawn
+
+ rAssert( mNumVehicles < MAX_VEHICLES );
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+
+ int i;
+ //for(i = 0; i < mNumVehicles; i++)
+ for(i = 0; i < MAX_VEHICLES; i++)
+ {
+ if(mVehicles[i].vehicle == vehicle)
+ {
+ // got this one.
+
+ // why would this ever actually be the case?
+ // I guess it would happen if they call
+ // AddStageVehicle and then
+ // MoveStageVehicle in the same stage?
+
+ rAssert(mVehicles[i].vehicleAINum == vehicleCentralIndex);
+
+ mVehicles[i].spawn = spawnlocator;
+
+ if(mVehicles[i].vehicleAI != NULL && ainame[0] != 0)
+ {
+ // we need to blow away the old ai to make room for the new
+ mVehicles[i].vehicleAI->Release();
+ mVehicles[i].vehicleAI = 0;
+ }
+
+ if(ainame[0] != 0)
+ {
+ // a string was passed in
+
+ MEMTRACK_PUSH_GROUP( "Mission - AI " );
+
+ //wrap allocates with the heap manager
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+#endif
+
+ VehicleAI* pAI = NULL;
+ if( strcmp( ainame, "chase") == 0 )
+ {
+ pAI = new ChaseAI( vehicle );
+ }
+ else if( strcmp( ainame, "race" ) == 0 )
+ {
+ pAI = new WaypointAI( vehicle );
+ ((WaypointAI*)pAI)->SetAIType( WaypointAI::RACE );
+ }
+ else if( strcmp( ainame, "evade" ) == 0 )
+ {
+ pAI = new WaypointAI( vehicle );
+ ((WaypointAI*)pAI)->SetAIType( WaypointAI::EVADE );
+ }
+ else if( strcmp( ainame, "target" ) == 0 )
+ {
+ pAI = new WaypointAI( vehicle );
+ ((WaypointAI*)pAI)->SetAIType( WaypointAI::TARGET );
+ }
+ else if ( strcmp( ainame, "NULL" ) == 0 )
+ {
+ pAI = NULL;
+ }
+ else
+ {
+ char buffy[64];
+ sprintf( buffy, "Unknown AI vehicle type: %s", ainame);
+ rTuneAssertMsg( false, buffy );
+ }
+ MEMTRACK_POP_GROUP("Mission - AI ");
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PopHeap( GMA_LEVEL_MISSION );
+#endif
+
+ mVehicles[i].vehicleAI = pAI;
+
+ if(pAI != 0)
+ {
+ mVehicles[i].vehicleAI->AddRef();
+ }
+ }
+
+ MEMTRACK_POP_GROUP("MissionStage");
+ return;
+ }
+ }
+
+
+ // below is Darryl's old way of not looking through the list
+ // makes me queazy
+
+ // let's add this test just to be safe
+ rAssert(mVehicles[mNumVehicles].vehicle == 0);
+
+
+ mVehicles[ mNumVehicles ].vehicle = vehicle;
+ vehicle->AddRef();
+
+ mVehicles[mNumVehicles].vehicleAINum = vehicleCentralIndex;
+
+ mVehicles[ mNumVehicles ].spawn = spawnlocator;
+
+ //wrap allocates with the heap manager
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+#endif
+
+ if(ainame[0] != 0)
+ {
+ // a string was passed in
+
+ MEMTRACK_PUSH_GROUP( "Mission - AI " );
+ VehicleAI* pAI = NULL;
+ if( strcmp( ainame, "chase") == 0 )
+ {
+ pAI = new ChaseAI( vehicle );
+ }
+ else if( strcmp( ainame, "race" ) == 0 )
+ {
+ pAI = new WaypointAI( vehicle );
+ ((WaypointAI*)pAI)->SetAIType( WaypointAI::RACE );
+ }
+ else if( strcmp( ainame, "evade" ) == 0 )
+ {
+ pAI = new WaypointAI( vehicle );
+ ((WaypointAI*)pAI)->SetAIType( WaypointAI::EVADE );
+ }
+ else if( strcmp( ainame, "target" ) == 0 )
+ {
+ pAI = new WaypointAI( vehicle );
+ ((WaypointAI*)pAI)->SetAIType( WaypointAI::TARGET );
+ }
+ else if ( strcmp( ainame, "NULL" ) == 0 )
+ {
+ pAI = NULL;
+ }
+ else
+ {
+ char buffy[256];
+ sprintf( buffy, "Unknown AI vehicle type: %s. "
+ "Need to specify race/evade/target/chase. Defaulting to \"race\".",
+ ainame);
+ rTunePrintf( buffy );
+
+ pAI = new WaypointAI( vehicle );
+ ((WaypointAI*)pAI)->SetAIType( WaypointAI::RACE );
+ }
+ MEMTRACK_POP_GROUP("Mission - AI ");
+
+
+ mVehicles[mNumVehicles].vehicleAI = pAI;
+
+ // recall, this still might be NULL
+ if(pAI != 0)
+ {
+ mVehicles[mNumVehicles].vehicleAI->AddRef();
+ }
+ }
+
+ mNumVehicles++;
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PopHeap( GMA_LEVEL_MISSION );
+#endif
+
+ MEMTRACK_POP_GROUP( "MissionStage" );
+}
+
+
+void MissionStage::SetAIParams( Vehicle* vehicle, const MissionStage::AIParams & params )
+{
+ rAssert( vehicle );
+
+ for( int i = 0; i < MAX_VEHICLES; i++ )
+ {
+ if(mVehicles[i].vehicle == vehicle)
+ {
+ rTuneAssertMsg( mVehicles[i].vehicleAI,
+ "The AI for this vehicle should have been initialized before "
+ "trying to set AI parameters." );
+
+ mVehicles[i].vehicleAI->SetMaxShortcutSkill( params.maxShortcutSkill );
+ mVehicles[i].vehicleAI->SetMinShortcutSkill( params.minShortcutSkill );
+ return;
+ }
+ }
+
+ rTuneAssertMsg( false, "The vehicle specified for setting AI params does not "
+ "exist in the mission stage." );
+}
+
+void MissionStage::SetAIRaceCatchupParams( Vehicle* vehicle, const VehicleAI::RaceCatchupParams& params )
+{
+ rAssert( vehicle );
+
+ for( int i = 0; i < MAX_VEHICLES; i++ )
+ {
+ if(mVehicles[i].vehicle == vehicle)
+ {
+ rTuneAssertMsg( mVehicles[i].vehicleAI,
+ "The AI for this vehicle should have been initialized before "
+ "trying to set AI parameters." );
+
+ mVehicles[i].vehicleAI->SetRaceCatchupParams( params );
+ return;
+ }
+ }
+
+ rTuneAssertMsg( false, "The vehicle specified for setting AI params does not "
+ "exist in the mission stage." );
+
+}
+
+void MissionStage::SetAIEvadeCatchupParams( Vehicle* vehicle, const VehicleAI::EvadeCatchupParams& params )
+{
+ rAssert( vehicle );
+
+ for( int i = 0; i < MAX_VEHICLES; i++ )
+ {
+ if(mVehicles[i].vehicle == vehicle)
+ {
+ rTuneAssertMsg( mVehicles[i].vehicleAI,
+ "The AI for this vehicle should have been initialized before "
+ "trying to set AI parameters." );
+
+ mVehicles[i].vehicleAI->SetEvadeCatchupParams( params );
+ return;
+ }
+ }
+
+ rTuneAssertMsg( false, "The vehicle specified for setting AI params does not "
+ "exist in the mission stage." );
+
+}
+
+void MissionStage::SetAITargetCatchupParams( Vehicle* vehicle, const VehicleAI::TargetCatchupParams& params )
+{
+ rAssert( vehicle );
+
+ for( int i = 0; i < MAX_VEHICLES; i++ )
+ {
+ if(mVehicles[i].vehicle == vehicle)
+ {
+ rTuneAssertMsg( mVehicles[i].vehicleAI,
+ "The AI for this vehicle should have been initialized before "
+ "trying to set AI parameters." );
+
+ mVehicles[i].vehicleAI->SetTargetCatchupParams( params );
+ return;
+ }
+ }
+
+ rTuneAssertMsg( false, "The vehicle specified for setting AI params does not "
+ "exist in the mission stage." );
+
+}
+
+
+
+//=============================================================================
+// MissionStage::AddWaypoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Locator* locator )
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::AddWaypoint( Locator* locator )
+{
+ rAssert( 0 <= mNumWaypoints && mNumWaypoints < WaypointAI::MAX_WAYPOINTS );
+
+ mWaypoints[ mNumWaypoints ] = locator;
+
+ mNumWaypoints++;
+}
+
+//=============================================================================
+// MissionStage::AddCharacter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* character, CarStartLocator* spawnlocator, CarStartLocator* carlocator, Vehicle* vehicle )
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::AddCharacter( char* name,
+ CarStartLocator* spawnlocator,
+ CarStartLocator* carlocator,
+ const char* dynaloadString,
+ Vehicle* vehicle )
+{
+MEMTRACK_PUSH_GROUP( "Mission - Characters" );
+ rAssert( mNumCharacters < MAX_CHARACTERS_IN_STAGE );
+
+ strcpy( mCharacters[ mNumCharacters ].name, name );
+ mCharacters[ mNumCharacters ].character = NULL;
+ if( strcmp( dynaloadString, "" ) != 0 )
+ {
+ mCharacters[ mNumCharacters ].pZoneEventLocator = new(GetGameplayManager()->GetCurrentMissionHeap()) ZoneEventLocator;
+ mCharacters[ mNumCharacters ].pZoneEventLocator->AddRef();
+ mCharacters[ mNumCharacters ].pZoneEventLocator->SetZoneSize( strlen(dynaloadString) + 1 );
+ mCharacters[ mNumCharacters ].pZoneEventLocator->SetZone( dynaloadString );
+ mCharacters[ mNumCharacters ].pZoneEventLocator->SetPlayerEntered();
+ }
+ else
+ {
+ mCharacters[ mNumCharacters ].pZoneEventLocator = NULL;
+ }
+
+ rAssert( mCharacters[ mNumCharacters ].locator == NULL );
+
+ if ( spawnlocator )
+ {
+ mCharacters[ mNumCharacters ].locator = spawnlocator;
+ mCharacters[ mNumCharacters ].locator->AddRef();
+ }
+
+ rAssert( mCharacters[ mNumCharacters ].carLocator == NULL );
+ if ( carlocator )
+ {
+ mCharacters[ mNumCharacters ].carLocator = carlocator;
+ mCharacters[ mNumCharacters ].carLocator->AddRef();
+ }
+
+ mCharacters[ mNumCharacters ].vehicle = vehicle;
+ if( vehicle != NULL )
+ {
+ //vehicle->AddRef();
+ }
+
+ mNumCharacters++;
+MEMTRACK_POP_GROUP( "Mission - Characters" );
+}
+
+
+//=============================================================================
+// MissionStage::AddCharacter
+//=============================================================================
+// Description: Overloaded Function Take in a char point for car name use only if the missionscript calls for the "current" car
+//
+// Parameters: ( Character* character, CarStartLocator* spawnlocator, CarStartLocator* carlocator, char* VehicleName )
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::AddCharacter( char* name,
+ CarStartLocator* spawnlocator,
+ CarStartLocator* carlocator,
+ const char* dynaloadString,
+ char* VehicleName )
+{
+MEMTRACK_PUSH_GROUP( "Mission - Characters" );
+ rAssert( mNumCharacters < MAX_CHARACTERS_IN_STAGE );
+
+ strcpy( mCharacters[ mNumCharacters ].name, name );
+ mCharacters[ mNumCharacters ].character = NULL;
+ if( strcmp( dynaloadString, "" ) != 0 )
+ {
+ mCharacters[ mNumCharacters ].pZoneEventLocator = new(GetGameplayManager()->GetCurrentMissionHeap()) ZoneEventLocator;
+ mCharacters[ mNumCharacters ].pZoneEventLocator->AddRef();
+ mCharacters[ mNumCharacters ].pZoneEventLocator->SetZoneSize( strlen(dynaloadString) + 1 );
+ mCharacters[ mNumCharacters ].pZoneEventLocator->SetZone( dynaloadString );
+ mCharacters[ mNumCharacters ].pZoneEventLocator->SetPlayerEntered();
+ }
+ else
+ {
+ mCharacters[ mNumCharacters ].pZoneEventLocator = NULL;
+ }
+
+ rAssert( mCharacters[ mNumCharacters ].locator == NULL );
+
+ if ( spawnlocator )
+ {
+ mCharacters[ mNumCharacters ].locator = spawnlocator;
+ mCharacters[ mNumCharacters ].locator->AddRef();
+ }
+
+ rAssert( mCharacters[ mNumCharacters ].carLocator == NULL );
+ if ( carlocator )
+ {
+ mCharacters[ mNumCharacters ].carLocator = carlocator;
+ mCharacters[ mNumCharacters ].carLocator->AddRef();
+ }
+
+ mCharacters[ mNumCharacters ].vehicle = NULL;
+ strcpy(mCharacters[ mNumCharacters ].VehicleName,VehicleName);
+
+ mNumCharacters++;
+MEMTRACK_POP_GROUP( "Mission - Characters" );
+}
+
+//=============================================================================
+// MissionStage::GetFailureCondition
+//=============================================================================
+// Description: Returns the first failure condition that is found.
+//
+// Parameters: ()
+//
+// Return: reference to a mission condition
+//
+//=============================================================================
+MissionCondition*
+MissionStage::GetFailureCondition() const
+{
+ MissionCondition* failureCondition = NULL;
+
+ // search for first failure condition
+ //
+ for( int i = 0; i < mNumConditions; i++ )
+ {
+ if( mConditions[ i ]->IsViolated() )
+ {
+ // ok, found it!
+ //
+ failureCondition = mConditions[ i ];
+
+ break;
+ }
+ }
+
+ return failureCondition;
+}
+
+//=============================================================================
+// MissionStage::Initialize
+//=============================================================================
+// Description: BEEEFCAAAKE! BEEEEEFCAAAAKE!
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::Initialize()
+{
+ Reset();
+ GetEventManager()->AddListener(this,EVENT_GUI_MISSION_START);
+}
+
+//=============================================================================
+// MissionStage::Finalize
+//=============================================================================
+// Description:
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::Finalize()
+{
+ GetEventManager()->RemoveAll(this);
+ VehicleFinalize();
+
+ mObjective->Finalize();
+
+ if( mCountdownEnabled )
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ HudEventHandler* hudEventHandler = currentHud->GetEventHandler( CGuiScreenHud::HUD_EVENT_HANDLER_COUNTDOWN );
+ if( hudEventHandler != NULL )
+ {
+ hudEventHandler->Stop();
+ }
+ }
+// GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_COUNT_DOWN );
+ }
+
+ for( int j = 0; j < mNumConditions; j++ )
+ {
+ rAssert( mConditions[ j ] != NULL );
+
+ if( mConditions[ j ] ->GetType() == MissionCondition::COND_PLAYER_OUT_OF_VEHICLE)
+ {
+ GetOutOfCarCondition* pOutOfCarCondition = NULL;
+
+ pOutOfCarCondition = dynamic_cast < GetOutOfCarCondition*> (mConditions[ j ]);
+
+ if (pOutOfCarCondition->IsConditionActive() == true)
+ {
+ Mission* currMission = GetGameplayManager()->GetCurrentMission();
+ if ( currMission )
+ {
+ currMission->SetCarryOverOutOfCarCondition(true);
+ }
+ }
+ }
+ mConditions[ j ]->Finalize();
+
+ if( mConditions[ j ]->GetType() == MissionCondition::COND_TIME_OUT )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_TIMER );
+ }
+
+ if( mConditions[ j ]->GetType() == MissionCondition::COND_FOLLOW_DISTANCE )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_PROXIMITY_METER );
+ }
+ }
+
+ //Reset the showmessage to true
+ mbShowMessage = true;
+ if (mb_DisableHitAndRun == true)
+ {
+ GetHitnRunManager()->EnableHitnRun();
+ }
+
+ //hide character.
+ if(strcmp(mCharacterToHide,"NULL") != 0)
+ {
+ Character* pCharacter =NULL;
+ pCharacter= GetCharacterManager()->GetCharacterByName(mCharacterToHide);
+ rTuneAssert(pCharacter !=NULL);
+ pCharacter->RemoveFromWorldScene();
+ }
+
+ if(mbStayBlackForStage == true)
+ {
+ GetGameplayManager()->ManualControlFade(false);
+ GetGameplayManager()->PauseForFadeFromBlack(1.0);
+ }
+
+ if ( mbDisablePlayerControlForCountDown )
+ {
+ mbDisablePlayerControlForCountDown = false;
+
+ if ( GetGameplayManager()->GetCurrentVehicle() )
+ {
+ VehicleController* controller = GetVehicleCentral()->GetVehicleController(
+ GetVehicleCentral()->GetVehicleId(GetGameplayManager()->GetCurrentVehicle()));
+ if( controller )
+ {
+ rAssert(dynamic_cast<HumanVehicleController*>(controller));
+ static_cast<HumanVehicleController*>(controller)->SetDisableReset(false);
+ }
+ }
+ }
+}
+
+
+//=============================================================================
+// MissionStage::VehicleFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::VehicleFinalize()
+{
+ for( int i = 0; i < mNumVehicles; i++ )
+ {
+ // GetVehicleCentral()->RemoveVehicleFromActiveList( mVehicles[ i ].vehicle );
+ // only GameplayManager will do this now.
+
+ // TODOGREG:
+ //
+ // for now, added code to RemoveVehicleFromActiveList to set the vehiclecontroller to 0 if the slot had something.
+ // not sure that's really the palce for it?
+
+ VehicleAI* ai = mVehicles[ i ].vehicleAI;
+ if( ai != NULL )
+ {
+ ai->SetActive( false );
+
+ //if the car is not being used in the proceeding stages remove if the mission vehicles, and it will get autoplaced in
+ //Gameplayamanger's VDU only if stage is complete and it did not have a AI for this stage.
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ if( currentMission != NULL
+ &&
+ currentMission->CanMDKCar( mVehicles[i].vehicle, this )
+ &&
+ mState == STAGE_COMPLETE )
+ {
+ GetGameplayManager()->RemoveVehicleFromMissionVehicleSlots(mVehicles[i].vehicle);
+ mVehicles[i].vehicle->Release();
+ mVehicles[i].vehicle = NULL;
+ }
+ else
+ {
+ //add to to be removed from the world.
+ if (mState != STAGE_IDLE)
+ {
+ GetGameplayManager()->AddToVDU(mVehicles[i].vehicle);
+ }
+ }
+
+ } //end if null AI check
+
+
+ }//end for loop
+ if( mObjective->IsFinished() )
+ {
+ DestroyStageVehicleAI();
+ }
+
+}
+
+
+//=============================================================================
+// MissionStage::Reset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::Reset()
+{
+ int i;
+
+ mResetCounter++;
+
+ //
+ // call up the parent class
+ HasPresentationInfo::Reset();
+
+ //get a traffic ptr
+ ITrafficSpawnController* pTraffic = TrafficManager::GetSpawnController();
+
+
+ //Chuck:clear traffic only if this stage is a race
+ //if ( this->GetObjective()->GetObjectiveType() == MissionObjective::OBJ_RACE )
+ if (mbClearTrafficForStage == true)
+ {
+
+ pTraffic->DisableTraffic();
+ rmt::Vector vector;
+ GetCharacterManager()->GetCharacter(0)->GetPosition(&vector);
+ rmt::Sphere sphere (vector, 200.0f);
+ pTraffic->ClearTrafficInSphere( sphere);
+ }
+
+ if (mbNoTrafficForStage == true)
+ {
+ pTraffic->DisableTraffic();
+ rmt::Vector vector;
+ GetCharacterManager()->GetCharacter(0)->GetPosition(&vector);
+ rmt::Sphere sphere (vector, 200.0f);
+ pTraffic->ClearTrafficInSphere( sphere);
+ pTraffic->SetMaxTraffic(0);
+ }
+ else
+ {
+ pTraffic->EnableTraffic();
+ }
+
+
+
+ //Chuck we need to revert any vehicle that are presently husks to normal if we are going to use them
+ //for this stage since there will only be one instance of any vehicle in the world and we like to recycle
+ //vehicles
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ for(int j = 0;j<MAX_VEHICLES;j++)
+ {
+ if(mVehicles[j].vehicle != NULL)
+ {
+ gameplayManager->MakeSureHusksAreReverted(mVehicles[j].vehicle); // make sure the same car (eg. CellA) used in multiple stages isn't left as a husk...
+ }
+ }
+ }
+
+ //
+ // Place the vehicles at their spawn positions
+ //
+
+
+ VehicleInfoInitialize();
+
+ mbRacePaidOut = false;
+
+ if(mbPutPlayerInCar)
+ {
+#ifndef RAD_RELEASE
+
+ if ( GetGameplayManager()->GetCurrentMission()->IsWagerMission() || GetGameplayManager()->GetCurrentMission()->IsRaceMission())
+ {
+ }
+ else
+ { //if you assert here, tell chuck.
+ //rTuneAssertMsg(0,"If you assert here tell Chuck, Tree of Woe \n");
+ }
+
+#endif
+ Character* pCharacter= GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter();
+ rTuneAssert(pCharacter != NULL);
+
+ Vehicle* v = GetGameplayManager()->GetCurrentVehicle();
+
+ //
+ // If vehicle is gone and there's husk for it, just put the character down with no car
+ // to his name.
+ // repair the user's vehicle
+ bool destroyed = v->IsVehicleDestroyed();
+ if( destroyed )
+ {
+ GetEventManager()->TriggerEvent( EVENT_REPAIR_CAR );
+ }
+ GetAvatarManager()->PutCharacterInCar(pCharacter,v);
+ }
+
+ if(mb_DisableHitAndRun == true)
+ {
+ GetHitnRunManager()->DisableHitnRun();
+ }
+
+ for ( i=0;i<MAX_CHASE_STRUCTS;i++)
+ {
+ //Reset the Chase Vehicle Rates for this stage
+ if (strcmp(m_ChaseData_Array[i].vehiclename,"NULL") !=0)
+ {
+ ChaseManager* p_chasemanager = NULL;
+ p_chasemanager=GetGameplayManager()->GetChaseManager ( m_ChaseData_Array[i].vehiclename);
+ rAssert(p_chasemanager);
+ p_chasemanager->SetMaxObjects(m_ChaseData_Array[i].mChaseVehicleSpawnRate);
+ if (m_ChaseData_Array[i].mChaseVehicleSpawnRate > 0)
+ {
+ p_chasemanager->SetActive(true);
+ }
+ else
+ {
+ p_chasemanager->SetActive(false);
+ }
+
+
+ }
+ }
+
+ // Place the characters and vehicles in their spawn positions
+ //
+ for( i = 0; i < mNumCharacters; i++ )
+ {
+ if( mCharacters[ i ].character == NULL )
+ {
+ mCharacters[ i ].character = GetCharacterManager()->
+ GetCharacterByName( mCharacters[ i ].name );
+ rAssert( mCharacters[ i ].character != NULL );
+ mCharacters[ i ].character->AddRef();
+ }
+
+ //chuck: adding support for current vehicles should the user switch current cars during a stage.
+ if (strcmp(mCharacters[ i ].VehicleName,"current") ==0)
+ {
+ mCharacters[ i ].vehicle= GetGameplayManager()->GetCurrentVehicle();
+ }
+
+
+ if( mCharacters[ i ].vehicle != NULL )
+ {
+ ITrafficSpawnController* tsc = TrafficManager::GetSpawnController();
+ rmt::Vector spawnLoc;
+
+ if ( mCharacters[ i ].carLocator )
+ {
+ mCharacters[ i ].carLocator->GetLocation( &spawnLoc );
+ GetGameplayManager()->PlaceVehicleAtLocator(
+ mCharacters[ i ].vehicle,
+ mCharacters[ i ].carLocator );
+ }
+ else
+ {
+ rAssert( mCharacters[ i ].locator != NULL );
+ mCharacters[ i ].locator->GetLocation( &spawnLoc );
+ GetGameplayManager()->PlaceVehicleAtLocator(
+ mCharacters[ i ].vehicle,
+ mCharacters[ i ].locator );
+ }
+
+ rmt::Sphere spawnSphere( spawnLoc, 5.0f );
+ tsc->ClearTrafficInSphere( spawnSphere );
+
+ if ( mCharacters[ i ].locator == NULL )
+ {
+ GetAvatarManager()->PutCharacterInCar(
+ mCharacters[ i ].character,
+ mCharacters[ i ].vehicle );
+ }
+ else
+ {
+ if ( mCharacters[ i ].character->IsInCarOrGettingInOut())
+ {
+ GetAvatarManager()->PutCharacterOnGround( mCharacters[ i ].character, GetGameplayManager()->GetCurrentVehicle() );
+ }
+
+ GetGameplayManager()->PlaceCharacterAtLocator(
+ mCharacters[ i ].character,
+ mCharacters[ i ].locator );
+ }
+ }
+ else
+ {
+ GetGameplayManager()->PlaceCharacterAtLocator(
+ mCharacters[ i ].character,
+ mCharacters[ i ].locator );
+
+ if ( mCharacters[ i ].character->IsInCarOrGettingInOut() )
+ {
+ GetAvatarManager()->PutCharacterOnGround( mCharacters[ i ].character, GetGameplayManager()->GetCurrentVehicle() );
+ }
+ }
+ }
+
+ if ( mNumCharacters == 0 && GetGameplayManager()->ShouldPutPlayerInCar() )
+ {
+ Character* player = GetAvatarManager()->GetAvatarForPlayer( 0 )->GetCharacter();
+ float rotation = player->GetFacingDir();
+ rmt::Vector pos;
+ player->GetPosition( &pos );
+
+ //We need to put the character in the default car anyway.
+ Vehicle* veh = GetGameplayManager()->GetVehicleInSlot( GameplayManager::eDefaultCar );
+ rAssert( veh );
+
+ GetGameplayManager()->PlaceVehicleAtLocation( veh, pos, -rotation ); //The car and characters are reversed.
+ GetAvatarManager()->PutCharacterInCar( player, veh );
+ GetGameplayManager()->PutPlayerInCar( false );
+
+ //chuck: presentation stuff since we faded to black at the end of the forced car mission and set
+ //fader to manual
+ GetGameplayManager()->ManualControlFade(false);
+ GetGameplayManager()->PauseForFadeFromBlack();
+
+ GetGameplayManager()->GetCurrentVehicle()->ResetOnSpot(false);
+
+ //clear traffic
+ rmt::Vector vector;
+ GetCharacterManager()->GetCharacter(0)->GetPosition(&vector);
+ rmt::Sphere sphere (vector, 10.0f);
+ pTraffic->ClearTrafficInSphere( sphere);
+
+ }
+
+ //The Character is likely moved from into to out of car or vice versa.
+ GetEventManager()->TriggerEvent( EVENT_CHARACTER_POS_RESET );
+
+ mObjective->Initialize();
+
+ int j;
+ for( j = 0; j < mNumConditions; j++ )
+ {
+ rAssert( mConditions[ j ] != NULL );
+ mConditions[ j ]->Initialize();
+
+ if ( mConditions[ j ]->GetType() == MissionCondition::COND_PLAYER_OUT_OF_VEHICLE)
+ {
+ if (GetGameplayManager()->GetCurrentMission()->GetCarryOverOutOfCarCondition() == true)
+ {
+ GetOutOfCarCondition* pCarCondition = dynamic_cast < GetOutOfCarCondition*> (mConditions[ j ]);
+ rAssert(pCarCondition != NULL);
+ pCarCondition->SetConditionActive();
+ GetGameplayManager()->GetCurrentMission()->SetCarryOverOutOfCarCondition(false);
+ }
+ }
+
+
+ if( mConditions[ j ]->GetType() == MissionCondition::COND_TIME_OUT )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_TIMER );
+ }
+
+ if( mConditions[ j ]->GetType() == MissionCondition::COND_FOLLOW_DISTANCE )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_PROXIMITY_METER, 1 /* 1 = green bar */ );
+ }
+ }
+
+ if ( mCamInfo.active )
+ {
+ //TODO: if there are multiple players, should determine which gets the camera set...
+ int camFlags = SuperCamCentral::FORCE;
+ camFlags |= mCamInfo.cut ? SuperCamCentral::CUT : 0x00000000;
+ camFlags |= mCamInfo.quickTransition ? SuperCamCentral::QUICK : 0x00000000;
+ SuperCamManager::GetInstance()->GetSCC( 0 )->SelectSuperCam( mCamInfo.type, camFlags );
+ }
+
+ mState = STAGE_IDLE;
+
+ //Clear all Harass cars if they are present
+
+ //Chase cars should only be removed on mission success/fail
+ //GetGameplayManager()->ClearAllChaseCars();
+
+
+
+ if ( mTrafficDensity >= 0 )
+ {
+ ITrafficSpawnController* tsc = TrafficManager::GetSpawnController();
+ tsc->SetMaxTraffic( mTrafficDensity );
+ }
+
+ mStartBonusObjectives = false;
+
+ //Enable traffic so long as the notrafficflag is false.
+ if (mbNoTrafficForStage == false)
+ {
+ pTraffic->EnableTraffic();
+ }
+
+ //
+ // Start the countdown timer if required
+ //
+ if( mCountdownEnabled )
+ {
+ //
+ // put all the vehicles into "limbo"
+ //
+ PutAllAisInLimbo( true );
+
+ // supress letter box
+ //
+ AnimatedCam::SupressNextLetterbox();
+
+ //making sure that we only trigger this event once this once since Evil DLongs code makes the first mission stage reset twice :'(
+ if(GetMissionManager()->GetLoadingState() == MissionManager::STATE_INVALID)
+ {
+ GetEventManager()->TriggerEvent( EVENT_GUI_COUNTDOWN_START );
+
+ if(GetGameplayManager()->GetCurrentMission()->IsWagerMission() == true)
+ {
+ GetGameplayManager()->GetCurrentMission()->SetMissionTime(1);
+ }
+ }
+ mbDisablePlayerControlForCountDown = true;
+
+// GetGuiSystem()->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_COUNT_DOWN );
+ }
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ if( mb_UseElapsedTime && this->GetObjective()->GetObjectiveType() == MissionObjective::OBJ_RACE )
+ {
+ RaceObjective* raceObjective = static_cast<RaceObjective*>( this->GetObjective() );
+ rAssert( raceObjective != NULL );
+
+ // set blinking interval to [ partime - 10 sec, partime ]
+ //
+ int parTimeInMilliseconds = raceObjective->GetParTime() * 1000;
+ currentHud->SetTimerBlinkingInterval( parTimeInMilliseconds - 10000, parTimeInMilliseconds );
+ }
+ else
+ {
+ // restore blinking interval to [ 10 sec, 0 sec ]
+ //
+ currentHud->SetTimerBlinkingInterval( 10000, 0 );
+ }
+ }
+
+ //check for character respawn on this stage
+
+ if ( strcmp(mPlayerRespawnLocatorName,"NULL") != 0)
+ {
+ Vehicle* pVehicle= NULL;
+ Character* pCharacter = NULL;
+
+ pVehicle = GetGameplayManager()->GetCurrentVehicle();
+ pCharacter= GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter();
+
+
+ //check if the player is in a car
+ if (pCharacter->IsInCarOrGettingInOut() == true )
+ {
+ //they are in a car, put their ass on the street
+ GetAvatarManager()->PutCharacterOnGround(pCharacter,pVehicle);
+ Locator* playerLoc = p3d::find<Locator>( mPlayerRespawnLocatorName);
+ rTuneAssertMsg(playerLoc != NULL,"Cant Find Locator to Player for PlacePlayerAtLocatorName!\n");
+ GetGameplayManager()->PlaceCharacterAtLocator( pCharacter, playerLoc );
+ }
+ }
+
+ //check for player car reset
+ if (strcmp(mPlayerCarRespawnLocatorName,"NULL") !=0 )
+ {
+ Vehicle* pVehicle = NULL;
+
+ pVehicle = GetGameplayManager()->GetCurrentVehicle();
+ rTuneAssertMsg(pVehicle != NULL,"Error: with msPlacePlayerCarAtLocatorName(); Player does NOT currently have a vehicle! \n");
+
+ GetGameplayManager()->PlaceVehicleAtLocatorName(pVehicle,mPlayerCarRespawnLocatorName);
+ }
+
+
+ //if this a dummy stage and we want to pause the screen black
+
+ if(mbStayBlackForStage == true)
+ {
+
+ GetGameplayManager()->ManualControlFade(true);
+ //check if the iris is closed
+ if (GetGameplayManager()->IsIrisClosed() == true)
+ {
+ //do nothing since its black no need to black screen again
+ }
+ else
+ {
+ GetGameplayManager()->PauseForFadeToBlack(1.0);
+ }
+ }
+
+
+
+ //check for level over script command
+ //
+ if( mbLevelOver || mbGameOver )
+ {
+ GetGameplayManager()->SetLevelComplete();
+ GetGameFlow()->SetContext( CONTEXT_PAUSE );
+
+ if(mbGameOver)
+ {
+ GetGameplayManager()->SetGameComplete();
+ }
+ }
+}
+
+
+
+//=============================================================================
+// MissionStage::VehicleInfoInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::VehicleInfoInitialize()
+{
+ int i;
+ for( i = 0; i < mNumVehicles; i++ )
+ {
+ // new
+ // greg
+ // jan 7, 2003
+
+ //Chuck 3:16
+ //add car to activelist in case it was used in a earlier stage and got removed from the world.
+ if ( mVehicles[ i ].vehicle != NULL)
+ {
+ //This is to deal with continuity errors.
+ if ( !GetGameplayManager()->TestForContinuityErrorWithCar( mVehicles[ i ].vehicle, false ) )
+ {
+ mVehicles[ i ].vehicleAINum = GetVehicleCentral()->AddVehicleToActiveList( mVehicles[ i ].vehicle );
+ GetGameplayManager()->ReleaseFromVDU(mVehicles[ i ].vehicle->mName, NULL);
+ }
+ else
+ {
+ //Remove again, just in case.
+ GetVehicleCentral()->RemoveVehicleFromActiveList( mVehicles[ i ].vehicle );
+ continue;
+ }
+ }
+
+ // just in case
+ // this could change when husk is swapped in and then out and then vehicle back in
+ mVehicles[i].vehicleAINum = GetVehicleCentral()->GetVehicleId(mVehicles[i].vehicle, true);
+
+ if( mVehicles[ i ].spawn != NULL )
+ {
+ ITrafficSpawnController* tsc = TrafficManager::GetSpawnController();
+ rmt::Vector spawnLoc;
+ mVehicles[ i ].spawn->GetLocation( &spawnLoc );
+ rmt::Sphere spawnSphere( spawnLoc, 5.0f );
+ tsc->ClearTrafficInSphere( spawnSphere );
+ GetGameplayManager()->PlaceVehicleAtLocator(
+ mVehicles[ i ].vehicle,
+ mVehicles[ i ].spawn );
+
+ //chuck: repair the car in the case it was used in a previous stage like the cell phone cars or cola truck.
+ mVehicles[i].vehicle->ResetFlagsOnly(true); // greg - that call didn't quite do enough.
+ }
+
+ VehicleAI* ai = mVehicles[ i ].vehicleAI;
+ if( ai != NULL )
+ {
+ GetVehicleCentral()->SetVehicleController( mVehicles[ i ].vehicleAINum, ai );
+
+ // try this
+ // set all cars to use out of car controller, unless avatar gets in them or they have some ai
+ //
+ if(mVehicles[i].vehicle)
+ {
+ //chuck: regardless of ai type we will now allow the car to be damaged
+ //unless its a dump and collect mission
+ if (GetObjective()->GetObjectiveType() != MissionObjective::OBJ_DUMP)
+ {
+ mVehicles[i].vehicle->SetVehicleCanSustainDamage(true);
+ }
+ else
+ {
+ mVehicles[i].vehicle->SetVehicleCanSustainDamage(false);
+ }
+
+ mVehicles[i].vehicle->SetInCarSimState();
+ }
+
+
+ // enable driving on both sides of the road when there's no traffic
+ if( mbNoTrafficForStage || TrafficManager::GetInstance()->GetMaxTraffic() == 0 )
+ {
+ ai->SetUseMultiplier( false );
+ }
+ ai->Initialize();
+ ai->SetActive( true );
+
+ //chuck: regardless of ai type we will now allow the car to be damaged
+
+
+
+ switch( ai->GetType() )
+ {
+ case VehicleAI::AI_WAYPOINT:
+ {
+ WaypointAI* wai;
+ wai = dynamic_cast<WaypointAI*>( ai );
+ rAssert( wai != NULL );
+
+ wai->ClearWaypoints();
+ for( int j = 0; j < mNumWaypoints; j++ )
+ {
+ wai->AddWaypoint( mWaypoints[ j ] );
+ }
+
+ break;
+ }
+ case VehicleAI::AI_CHASE:
+ {
+ ChaseAI* cai;
+ cai = dynamic_cast<ChaseAI*>( ai );
+ rAssert( cai != NULL );
+
+ //Vehicle* playervehicle;
+ //cai->SetTarget( playervehicle );
+
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ break;
+ }
+ }//end switch
+ }//end if ai null check
+ else
+ {
+ if(mVehicles[i].vehicle) // do this?
+ {
+ mVehicles[i].vehicle->SetOutOfCarSimState();
+
+ //chuck: any vehicles created with an AI with now be invulnerable to damage
+ //this will get set to sustain damage with the AI is intialized or added.
+ mVehicles[i].vehicle->SetVehicleCanSustainDamage(false);
+
+
+ }
+ }
+ }//end of mVehicles loop
+
+ if ( GetGameplayManager()->mIsDemo )
+ {
+ //Setup the player car to follow waypoints too..
+ Vehicle* playerCar = GetGameplayManager()->GetCurrentVehicle();
+ rAssert( playerCar );
+ VehicleAI* ai = GetVehicleCentral()->GetVehicleAI( playerCar );
+ rAssert( ai );
+
+ WaypointAI* wai = dynamic_cast<WaypointAI*>(ai);
+ rAssert( wai );
+
+ wai->Initialize();
+ wai->SetActive( true );
+
+ wai->ClearWaypoints();
+ for( int j = 0; j < mNumWaypoints; j++ )
+ {
+ wai->AddWaypoint( mWaypoints[ j ] );
+ }
+
+ GetVehicleCentral()->SetVehicleController( playerCar->mVehicleCentralIndex, ai );
+ playerCar->SetVehicleCanSustainDamage( false );
+ }
+}
+
+
+//=============================================================================
+// MissionStage::Start
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::Start()
+{
+ radKey32 musicKey;
+ radKey32 musicStateKey;
+ radKey32 musicStateEventKey;
+
+ DisplayMissionStageIndexMessage();
+
+ mState = STAGE_INPROGRESS;
+
+ if ( mIsBonusObjectiveStart )
+ {
+ mStartBonusObjectives = true;
+ }
+
+ //
+ // This appears to be the best point for analyzing the stage
+ // to see if we should inform the music player of something
+ // dramatic in the mission play -- Esan
+ //
+ if( GetMusicChangeFlag() )
+ {
+ unsigned int numConditions;
+ unsigned int i;
+ bool isClose = false;
+
+ //
+ // Last stage, cue the drama
+ //
+ numConditions = GetNumConditions();
+ for( i = 0; i < numConditions; i++ )
+ {
+ if( GetCondition( i )->IsClose() )
+ {
+ isClose = true;
+ break;
+ }
+ }
+
+ if( isClose )
+ {
+ GetEventManager()->TriggerEvent( EVENT_MISSION_DRAMA );
+ }
+ }
+
+ musicKey = GetStageStartMusicEvent();
+ if( musicKey != 0 )
+ {
+ GetEventManager()->TriggerEvent( EVENT_CHANGE_MUSIC, &musicKey );
+ }
+
+ GetStageMusicState( musicStateKey, musicStateEventKey );
+ if( musicStateKey != 0 )
+ {
+ MusicStateData data( musicStateKey, musicStateEventKey );
+ GetEventManager()->TriggerEvent( EVENT_CHANGE_MUSIC_STATE, &data );
+ }
+}
+
+//=============================================================================
+// MissionStage::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::Update( unsigned int elapsedTime )
+{
+BEGIN_PROFILE("MissionStage Update");
+ mObjective->Update( elapsedTime );
+
+ //Chuck: Reward player if this was a gamble race
+ //I hate putting in code there, seem ill planned imho too much info hiding
+
+ //Check if objective was a race
+ if ( mObjective->GetObjectiveType() == MissionObjective::OBJ_RACE)
+ {
+ //it was a race dynamic cast to a race objective ptr should be ok we dont do this alot
+ RaceObjective* pRaceObjective = dynamic_cast <RaceObjective *> (mObjective);
+ rAssert(pRaceObjective);
+
+ if (pRaceObjective->QueryIsGambleRace() == true && !mbRacePaidOut )
+ {
+ //check the player time vs the par time Reward the player with coins and reward screen
+ int elapsedtime = GetGameplayManager()->GetCurrentMission()->GetMissionTimeLeftInSeconds();
+ int partime =pRaceObjective->GetParTime();
+
+ if( ( elapsedtime <= partime ) && mObjective->IsFinished() )
+ {
+ mbRacePaidOut = true;
+ //do rewards stuff here
+ GetCoinManager()->AdjustBankValue( (int)( rmt::LtoF(mRaceEnteryFee) + (rmt::FtoL(GetGameplayManager()->GetCurrentVehicle()->GetGamblingOdds() * rmt::LtoF(mRaceEnteryFee))) ) );
+ GetEventManager()->TriggerEvent( EVENT_COLLECTED_COINS);
+
+ RenderEnums::LevelEnum currentLevel = GetGameplayManager()->GetCurrentLevelIndex();
+ int currentBestTime = GetCharacterSheetManager()->GetGambleRaceBestTime( currentLevel );
+ if( currentBestTime < 0 || elapsedtime < currentBestTime )
+ {
+ GetCharacterSheetManager()->SetGambleRaceBestTime( currentLevel, elapsedtime );
+
+ const int NEW_BEST_TIME = 1;
+ GetEventManager()->TriggerEvent( EVENT_GAMBLERACE_SUCCESS, (void*)NEW_BEST_TIME );
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_GAMBLERACE_SUCCESS );
+ }
+ //Fire off some thing saying user Beat Par Time you get coins overlay thingy
+ }
+ else
+ {
+ //GetCurrentHud()->HandleMessage(GUI_MSG_HIDE_HUD_OVERLAY,HUD_MISSION_COMPLETE);
+ if ( elapsedtime > partime)
+ {
+ GetEventManager()->TriggerEvent( EVENT_GAMBLERACE_FAILURE);
+
+ if (elapsedtime > (partime+15))
+ {
+ TimeOutCondition* pTimeOutCondition = new TimeOutCondition();
+ pTimeOutCondition->SetViolated(true);
+ unsigned int counter = 0;
+ counter += this->GetNumConditions() +1;
+ this->SetCondition(this->GetNumConditions(),pTimeOutCondition);
+ this->SetNumConditions( counter);
+
+ mState= STAGE_FAILED;
+ GetEventManager()->TriggerEvent( EVENT_GAMBLERACE_FAILURE);
+
+ mbRacePaidOut = true;
+ }
+ }
+ }
+ }
+ }
+
+ if( mObjective->IsFinished() )
+ {
+ mState = STAGE_COMPLETE;
+ OnStageCompleteSuccessful();
+ DestroyAllSafeZones ();
+ triggerStageDialog();
+
+ }
+ else
+ {
+ for( int i = 0; i < mNumConditions; i++ )
+ {
+ mConditions[ i ]->Update( elapsedTime );
+
+ if( mConditions[ i ]->IsViolated() )
+ {
+ mState = STAGE_FAILED;
+ }
+ else if ( mConditions[ i ]->LeaveInterior() )
+ {
+ mState = STAGE_BACKUP;
+ }
+ }
+ }
+
+ Vehicle* p_vehicle =NULL;
+ //get players p1 position
+ p_vehicle=GetAvatarManager()->GetAvatarForPlayer( 0 )->GetVehicle();
+
+ //player must be the car for safezone check
+ if (p_vehicle != NULL)
+ {
+
+ //check if the player car is inside the safezones for this stage.
+ for( int i=0; i<MAX_SAFEZONES;i++)
+ {
+ Vehicle* p_vehicle =NULL;
+ //get players p1 position
+ p_vehicle=GetAvatarManager()->GetAvatarForPlayer( 0 )->GetVehicle();
+ rmt::Vector player_position;
+ p_vehicle->GetPosition(&player_position);
+
+ if (m_SafeZone_Array[i] != NULL)
+ {
+ //if true and false
+ if ( m_SafeZone_Array[i]->InsideZone(player_position) && (mb_InsideSafeZone == false))
+ {
+ //player has entered safezone
+ GetGameplayManager()->DisableAllChaseAI();
+ mb_InsideSafeZone = true;
+ }
+
+
+ //if false and true
+ if ( (m_SafeZone_Array[i]->InsideZone(player_position) == false ) && (mb_InsideSafeZone) )
+ {
+ //just left the safezone
+ GetGameplayManager()->EnableAllChaseAI();
+ mb_InsideSafeZone = false;
+ }
+
+ } //end if check if safezone isnt null
+ }//end for loop to check safezones
+ }//end if player is inside their car
+
+ //Chuck: Disable player control of car while count down is in progress.
+ if (mbDisablePlayerControlForCountDown == true)
+ {
+ Vehicle* v = GetGameplayManager()->GetCurrentVehicle();
+ if (v != NULL)
+ {
+ v->SetDisableGasAndBrake(true);
+ v->SetEBrake( 1.0f, 0.0f );
+ VehicleController* controller = GetVehicleCentral()->GetVehicleController(
+ GetVehicleCentral()->GetVehicleId(GetGameplayManager()->GetCurrentVehicle()));
+ if( controller )
+ {
+ rAssert(dynamic_cast<HumanVehicleController*>(controller));
+ static_cast<HumanVehicleController*>(controller)->SetDisableReset(true);
+ }
+ }
+
+ }
+
+END_PROFILE("MissionStage Update");
+}
+
+//=============================================================================
+// MissionStage::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch (id)
+ {
+ case EVENT_GUI_MISSION_START:
+ {
+ mbDisablePlayerControlForCountDown = false;
+ if ( GetGameplayManager()->GetCurrentVehicle() != NULL)
+ {
+ GetGameplayManager()->GetCurrentVehicle()->SetDisableGasAndBrake(false);
+ VehicleController* controller = GetVehicleCentral()->GetVehicleController(
+ GetVehicleCentral()->GetVehicleId(GetGameplayManager()->GetCurrentVehicle()));
+ if( controller )
+ {
+ rAssert(dynamic_cast<HumanVehicleController*>(controller));
+ static_cast<HumanVehicleController*>(controller)->SetDisableReset(false);
+ }
+ }
+
+ if (GetGameplayManager()->GetCurrentMission()->IsWagerMission() == true)
+ {
+ //get a traffic ptr
+ ITrafficSpawnController* pTraffic = TrafficManager::GetSpawnController();
+ pTraffic->EnableTraffic();
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// MissionStage::SetStageTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( StageTimeType type, unsigned int seconds )
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::SetStageTime( StageTimeType type, unsigned int seconds )
+{
+ mStageTimeType = type;
+ mStageTime = seconds + 1; //This is to make the time down look better.
+}
+
+//=============================================================================
+// MissionStage::GetStageTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( StageTimeType &type, unsigned int &seconds )
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::GetStageTime( StageTimeType &type, unsigned int &seconds )
+{
+ type = mStageTimeType;
+ seconds = mStageTime;
+}
+
+//=============================================================================
+// MissionStage::LoadMissionStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool MissionStage::LoadMissionStart()
+{
+ if( mNumCharacters > 0 && mCharacters[ 0 ].pZoneEventLocator != NULL )
+ {
+ GetEventManager()->TriggerEvent( EVENT_FIRST_DYNAMIC_ZONE_START,
+ static_cast<void*>( mCharacters[ 0 ].pZoneEventLocator ) );
+
+ return true;
+ }
+
+ return false;
+}
+
+//=============================================================================
+// MissionStage::SetCameraInfo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( SuperCam::Type type, bool cut, bool quickTransition )
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::SetCameraInfo( SuperCam::Type type, bool cut, bool quickTransition )
+{
+ mCamInfo.active = true;
+ mCamInfo.cut = cut;
+ mCamInfo.quickTransition = quickTransition;
+ mCamInfo.type = type;
+}
+
+//=============================================================================
+// MissionStage::GetVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int which )
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* MissionStage::GetVehicle( int which )
+{
+
+ return mVehicles[ which ].vehicle;
+}
+
+
+//=============================================================================
+// MissionStage::SetChaseSpawnRate
+//=============================================================================
+// Description: Sets the Max number of harras AI cars that spawn for this stage
+//
+// Parameters: ( unsigned int which )
+//
+// Return: Vehicle
+//
+//=============================================================================
+void MissionStage::SetChaseSpawnRate( char* vehicle_name, unsigned int max_chase_cars )
+{
+ for (int i =0; i<MAX_CHASE_STRUCTS;i++)
+ {
+ if ( strcmp (m_ChaseData_Array[i].vehiclename,"NULL") ==0)
+ {
+ strcpy(m_ChaseData_Array[i].vehiclename,vehicle_name);
+ m_ChaseData_Array[i].mChaseVehicleSpawnRate = max_chase_cars;
+ }
+ }
+}
+
+
+//=============================================================================
+// MissionStage::AddSafeZone
+//=============================================================================
+// Description: Adds a safezone instance to the mission stages m_SafeZone_Array
+//
+// Parameters: ( CarStartLocator* locator, unsigned int radius)
+//
+// Return: int , 0 if ok -1 if out of space.
+//
+//=============================================================================
+int MissionStage::AddSafeZone ( CarStartLocator* locator, unsigned int radius)
+{
+ for (int i =0;i<MAX_SAFEZONES;i++)
+ {
+ if (m_SafeZone_Array[i] == NULL)
+ {
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+ #endif
+ m_SafeZone_Array[i] = new SafeZone (locator,radius);
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_MISSION );
+ #endif
+ return 0;
+ }
+ }
+
+ return -1;
+
+}
+
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// MissionStage::ActivateVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* vehicle, bool bIsActive )
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::ActivateVehicle( Vehicle* vehicle, bool bIsActive )
+{
+ rAssert( vehicle );
+ VehicleAI* ai = GetVehicleCentral()->GetVehicleAI( vehicle );
+ rAssert( ai );
+
+ ai->SetActive( bIsActive );
+}
+
+
+//=============================================================================
+// MissionStage::DestroyALLSafeZones
+//=============================================================================
+// Description: deletes all safezones in the mission stages
+// Parameters: none
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::DestroyAllSafeZones ( )
+{
+ for (int i =0;i<MAX_SAFEZONES;i++)
+ {
+ if (m_SafeZone_Array[i] != NULL)
+ {
+ delete m_SafeZone_Array[i];
+ m_SafeZone_Array[i]=NULL;
+
+ }
+ }
+
+}
+
+//=============================================================================
+// MissionStage::triggerStageDialog
+//=============================================================================
+// Description: Play dialog associated with stage completion if it exists
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::triggerStageDialog()
+{
+ DialogEventData data;
+ Character* avatarCharacter;
+ const char* modelName;
+
+ if( mDialogEventKey != 0 )
+ {
+ avatarCharacter = GetAvatarManager()->GetAvatarForPlayer( 0 )->GetCharacter();
+ modelName = GetCharacterManager()->GetModelName( avatarCharacter );
+ data.charUID1 = tEntity::MakeUID( modelName );
+ data.charUID2 = mConversationCharacterKey;
+ data.dialogName = mDialogEventKey;
+
+ GetEventManager()->TriggerEvent( EVENT_IN_GAMEPLAY_CONVERSATION, static_cast<void*>(&data) );
+ }
+}
+
+//=============================================================================
+// MissionStage::SetLockRequirement
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int which, LockRequirement::Type type, const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::SetLockRequirement( unsigned int which, LockRequirement::Type type, const char* name )
+{
+ rAssert( which < MAX_LOCK_REQUIREMENTS );
+
+ unsigned int nameLen = strlen(name);
+
+ rAssert( nameLen < LockRequirement::MAX_NAME_LEN );
+
+ mRequirement[ which ].mType = type;
+ strncpy( mRequirement[ which ].mName, name, (nameLen < LockRequirement::MAX_NAME_LEN) ? nameLen : LockRequirement::MAX_NAME_LEN );
+ mRequirement[ which ].mName[nameLen] = '\0'; //+1 is added on creation of this mName
+
+ mMissionLocked = true;
+}
+
+//=============================================================================
+// MissionStage::GetLockRequirement
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int which )
+//
+// Return: const
+//
+//=============================================================================
+const MissionStage::LockRequirement& MissionStage::GetLockRequirement( unsigned int which )
+{
+ rAssert( which < MAX_LOCK_REQUIREMENTS );
+
+ return mRequirement[ which ];
+}
+
+//=============================================================================
+// MissionStage::DisplayMissionStageIndexMessage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::DisplayMissionStageIndexMessage()
+{
+ if( mbShowMessage && !GetMissionLocked() && miStartMessageIndex >= 0 )
+ {
+ GetEventManager()->TriggerEvent( EVENT_SHOW_MISSION_OBJECTIVE,
+ reinterpret_cast<void*>( miStartMessageIndex ) );
+ }
+ else
+ {
+ // this just updates the mission objective icon on the HUD, if changed
+ //
+ GetEventManager()->TriggerEvent( EVENT_SHOW_MISSION_OBJECTIVE,
+ reinterpret_cast<void*>( -1 ) );
+ }
+}
+
+//=============================================================================
+// MissionStage::SetCountdownEnabled
+//=============================================================================
+// Description: Sets the flag that will later enable the countdown timer
+// when this stage starts
+//
+// Parameters: none
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::SetCountdownEnabled( radKey32 dialogID, tUID secondSpeakerUID )
+{
+ mCountdownEnabled = true;
+
+ rAssert( mCountdownSequenceUnits == NULL );
+ mCountdownSequenceUnits = new( GMA_LEVEL_MISSION ) CountdownSequenceUnit[ MAX_NUM_COUNTDOWN_SEQUENCE_UNITS ];
+ rAssert( mCountdownSequenceUnits != NULL );
+
+ for( int i = 0; i < MAX_NUM_COUNTDOWN_SEQUENCE_UNITS; i++ )
+ {
+ mCountdownSequenceUnits[ i ].durationTime = 0;
+ }
+
+ mNumCountdownSequenceUnits = 0;
+
+ mCountdownDialogKey = dialogID;
+ mSecondSpeakerUID = secondSpeakerUID;
+}
+
+void MissionStage::AddCountdownSequenceUnit( const char* textID, int durationTime )
+{
+ rTuneAssertMsg( mNumCountdownSequenceUnits < MAX_NUM_COUNTDOWN_SEQUENCE_UNITS,
+ "Number of countdown sequence units exceeds maximum!" );
+
+ strcpy( mCountdownSequenceUnits[ mNumCountdownSequenceUnits ].textID, textID );
+ mCountdownSequenceUnits[ mNumCountdownSequenceUnits ].durationTime = durationTime;
+
+ mNumCountdownSequenceUnits++;
+}
+
+MissionStage::CountdownSequenceUnit* MissionStage::GetCountdownSequenceUnit( int index ) const
+{
+ rAssert( index >= 0 );
+
+ if( index < mNumCountdownSequenceUnits )
+ {
+ rAssert( mCountdownSequenceUnits != NULL );
+
+ return &(mCountdownSequenceUnits[ index ]);
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+void MissionStage::PutAllAisInLimbo( bool inLimbo )
+{
+ if( inLimbo )
+ {
+ int i;
+ for( i = 0; i < MAX_VEHICLES; ++i )
+ {
+ VehicleAI* ai = mVehicles[ i ].vehicleAI;
+ if( ai != NULL )
+ {
+ ai->EnterLimbo();
+ }
+ }
+ }
+ else
+ {
+ int i;
+ for( i = 0; i < MAX_VEHICLES; ++i )
+ {
+ VehicleAI* ai = mVehicles[ i ].vehicleAI;
+ if( ai != NULL )
+ {
+ ai->ExitLimbo();
+ }
+ }
+ }
+}
+
+//=============================================================================
+// MissionStage::DoTransition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::DoTransition()
+{
+ if ( mIrisAtEnd )
+ {
+ //Do Forced car Swapping while we are irising
+ if(mbSwapInDefaultCar == true)
+ {
+ //do the swapping
+ }
+ GetGameplayManager()->PauseForIrisClose( mIrisSpeed );
+ } else if ( mFadeOutAtEnd )
+ {
+ //Do Forced car Swapping while we are irising
+ if(mbSwapInDefaultCar == true)
+ {
+ //do the swapping
+ SwapInDefaultCarStart();
+ }
+ GetGameplayManager()->PauseForFadeToBlack( mIrisSpeed );
+
+ }
+}
+
+
+//sets mb_UseElapsed time to true which make hud counter count up.
+void MissionStage::UseElapsedTime()
+{
+ mb_UseElapsedTime = true;
+}
+
+bool MissionStage::QueryUseElapsedTime()
+{
+ return mb_UseElapsedTime;
+}
+
+//used for Gamble Races
+void MissionStage::SetRaceEnteryFee(int coins)
+{
+ mRaceEnteryFee = coins;
+}
+
+void MissionStage::PutMFPlayerInCar( )
+{
+ mbPutPlayerInCar = true;
+}
+
+void MissionStage::DisableHitAndRun()
+{
+ mb_DisableHitAndRun = true;
+}
+
+
+void MissionStage::SwapInDefaultCar()
+{
+ mbSwapInDefaultCar = true;
+}
+
+void MissionStage::SwapInDefaultCarStart()
+{
+ Vehicle* pVehicle= NULL;
+ Character* pCharacter = NULL;
+
+ //get Gameplaymanager and take manual control of fadein
+ GetGameplayManager()->ManualControlFade(true);
+
+ pVehicle = GetGameplayManager()->GetCurrentVehicle();
+ pCharacter= GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter();
+
+ //check if car is a forced car
+
+ if (pVehicle == GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].mp_vehicle )
+ {
+ //check if the player is in a car
+ if (pCharacter->IsInCarOrGettingInOut() == true )
+ {
+ //they are in a car, put their ass on the street
+ GetAvatarManager()->PutCharacterOnGround(pCharacter,pVehicle);
+ Locator* playerLoc = p3d::find<Locator>( mSwapPlayerRespawnLocatorName);
+ rTuneAssertMsg(playerLoc != NULL,"Cant Find Locator to Spawn Player after Car SWAP !\n");
+ GetGameplayManager()->PlaceCharacterAtLocator( pCharacter, playerLoc );
+ }
+
+ //Removed the forced car from the world
+ //GetGameplayManager()->DumpCurrentCar();
+ GetGameplayManager()->MDKVDU();
+
+ //load the players default car
+ GetLoadingManager()->AddRequest(FILEHANDLER_PURE3D,
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].filename,
+ GMA_LEVEL_MISSION,
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].filename,
+ "Default",
+ this
+ );
+ }
+
+}
+
+
+void MissionStage::OnProcessRequestsComplete( void* pUserData)
+{
+
+ //changed the forced car to an AI type car and so its locked from player
+
+ Character* pCharacter = NULL;
+
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].mp_vehicle->SetUserDrivingCar(false);
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].mp_vehicle->TransitToAI();
+
+ //removed the NPC character driver.
+ pCharacter = GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].mp_vehicle->GetDriver();
+ if (pCharacter != NULL)
+ {
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].mp_vehicle->SetDriver(NULL);
+ GetCharacterManager()->RemoveCharacter(pCharacter);
+ }
+ GetGameplayManager()->PlaceVehicleAtLocatorName(
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eOtherCar].mp_vehicle,
+ mSwapForcedCarRespawnLocatorName);
+
+ //init the vehicle
+ Vehicle* vehicle;
+ char conName[64];
+ sprintf(conName,"%s.con", GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].name);
+ vehicle = GetGameplayManager()->AddLevelVehicle( GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].name,GameplayManager::eDefaultCar, conName);
+ GetGameplayManager()->SetCurrentVehicle(vehicle);
+
+ //place at the default car right locator.
+
+ GetGameplayManager()->PlaceVehicleAtLocatorName( vehicle, mSwapDefaultCarRespawnLocatorName );
+ //GetGameplayManager()->PlaceCharacterAtLocator( player, mPlayerRestart );
+
+ //update the mission so we dont reload default car since it being done in this method
+ GetGameplayManager()->GetCurrentMission()->SetSwappedCarsFlag(true);
+
+
+
+ //cleanup any cars lying around while we are in an iris state.
+ GetGameplayManager()->MDKVDU();
+
+ //open fade
+ GetGameplayManager()->ManualControlFade(false);
+
+};
+
+void MissionStage::SetSwapDefaultCarRespawnLocatorName(char* locatorName)
+{
+ strncpy(mSwapDefaultCarRespawnLocatorName,locatorName,32);
+ mSwapDefaultCarRespawnLocatorName[31]='\0';
+}
+
+void MissionStage::SetSwapPlayerRespawnLocatorName(char* locatorName)
+{
+ strncpy(mSwapPlayerRespawnLocatorName,locatorName,32);
+ mSwapPlayerRespawnLocatorName[31]='\0';
+}
+
+void MissionStage::SetSwapForcedCarRespawnLocatorName(char* locatorName)
+{
+ strncpy(mSwapForcedCarRespawnLocatorName,locatorName,32);
+ mSwapForcedCarRespawnLocatorName[31]='\0';
+}
+
+
+void MissionStage::EnableTraffic()
+{
+
+}
+
+void MissionStage::DisableTraffic()
+{
+ mbNoTrafficForStage = true;
+}
+
+void MissionStage::ClearTrafficForStage()
+{
+ mbClearTrafficForStage = true;
+}
+
+void MissionStage::SetPlayerRespawnLocatorName(char* locatorName)
+{
+ strncpy(mPlayerRespawnLocatorName,locatorName,32);
+ mPlayerRespawnLocatorName[31] = '\0';
+}
+
+void MissionStage::SetmsPlayerCarRespawnLocatorName(char* locatorName)
+{
+ strncpy(mPlayerCarRespawnLocatorName,locatorName,32);
+ mPlayerCarRespawnLocatorName[31]='\0';
+}
+
+void MissionStage::OnStageCompleteSuccessful()
+{
+ HasPresentationInfo::OnStageCompleteSuccessful();
+}
+
+void MissionStage::SetCharacterToHide(char* charactername)
+{
+ strncpy(mCharacterToHide,charactername,16);
+ mCharacterToHide[15]='\0';
+}
+
+void MissionStage::SetLevelOver()
+{
+ mbLevelOver = true;
+}
+
+//=============================================================================
+// MissionStage::SetStageMusicState
+//=============================================================================
+// Description: Set the radMusic state change that should be triggered when
+// this stage starts
+//
+// Parameters: stateKey - name of state
+// stateEventKey - name of event
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::SetStageMusicState( radKey32 stateKey, radKey32 stateEventKey )
+{
+ mMusicStateKey = stateKey;
+ mMusicStateEventKey = stateEventKey;
+}
+
+//=============================================================================
+// MissionStage::GetStageMusicState
+//=============================================================================
+// Description: Get the info for the radMusic state change that should be
+// triggered when this stage starts
+//
+// Parameters: stateKey - name of state
+// stateEventKey - name of event
+//
+// Return: void
+//
+//=============================================================================
+void MissionStage::GetStageMusicState( radKey32& stateKey, radKey32& stateEventKey )
+{
+ stateKey = mMusicStateKey;
+ stateEventKey = mMusicStateEventKey;
+}
+
+
+//returns the vehicle that hopefully is the main AI vehicle
+Vehicle* MissionStage::GetMainAIVehicleForThisStage()
+{
+ int indexArray[MAX_VEHICLES] = {-1,-1,-1,-1};
+ bool isAISlotInThisList = false;
+ Vehicle* pAISlotVehicle = NULL;
+
+ pAISlotVehicle = GetGameplayManager()->GetVehicleInSlot(GameplayManager::eAICar);
+
+ //find out which vehicles in this stage have drivers and Are Active with AI.
+ //we fill in array for the second pass
+ for (int i=0; i <mNumVehicles;i++)
+ {
+ if (mVehicles[i].vehicle != NULL && mVehicles[i].vehicleAI != NULL)
+ {
+ //check if it has a driver.
+ if (mVehicles[i].vehicle->GetDriver() != NULL)
+ {
+ indexArray[i]=i;
+ }
+ }
+ }
+
+ //check all Active AI against the Main AI slot car
+ for (int j=0;j<MAX_VEHICLES;j++)
+ {
+ if (indexArray[j] > -1)
+ {
+ if (pAISlotVehicle != NULL)
+ {
+ if ( mVehicles[indexArray[j]].vehicle == pAISlotVehicle)
+ {
+ isAISlotInThisList = true;
+ return mVehicles[indexArray[j]].vehicle;
+ }
+ }
+ }
+ isAISlotInThisList = false;
+ }
+
+ // return the first car since we cant determine which car is main ai for this stage
+ for (int j=0;j<MAX_VEHICLES;j++)
+ {
+ if (indexArray[j] > -1)
+ {
+ return mVehicles[indexArray[j]].vehicle;
+ }
+ }
+ //return NULL as a fall back.
+ return NULL;
+}
+
+
+
+
+
+
+
+
+
+
+ \ No newline at end of file
diff --git a/game/code/mission/missionstage.h b/game/code/mission/missionstage.h
new file mode 100644
index 0000000..c612463
--- /dev/null
+++ b/game/code/mission/missionstage.h
@@ -0,0 +1,745 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: missionstage.h
+//
+// Description: Blahblahblah
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef MISSIONSTAGE_H
+#define MISSIONSTAGE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <radkey.hpp>
+#include <events/eventlistener.h>
+#include <camera/supercam.h>
+#include <ai/vehicle/vehicleai.h>
+#include <ai/vehicle/waypointai.h>
+#include <vector>
+#include <memory/stlallocators.h>
+#include <mission/haspresentationinfo.h>
+#include <string.h>
+#include <loading/loadingmanager.h>
+//========================================
+// Forward References
+//========================================
+
+class MissionObjective;
+class MissionCondition;
+
+class Character;
+class Vehicle;
+class VehicleAI;
+class Locator;
+class ZoneEventLocator;
+class CarStartLocator;
+class SafeZone;
+
+
+
+
+//Const Declarations
+
+//max of 1 chase data structs since we will only have 2 different generic chase vehicles per level
+const int MAX_CHASE_STRUCTS = 1;
+//max of 3 safezone per stage.
+const int MAX_SAFEZONES = 3;
+
+const unsigned int MAX_LOCK_REQUIREMENTS = 2;
+//=============================================================================
+//
+// Synopsis: Represents a single "task" for the player to perform, such as
+// getting something or chasing someone. Each MissionStage has
+// a single objective (the action to perform) and zero or more
+// conditions for failure (such as wrecking the car).
+//
+//=============================================================================
+
+class MissionStage :
+ public EventListener,
+ public LoadingManager::ProcessRequestsCallback,
+ public HasPresentationInfo
+{
+public:
+
+ MissionStage();
+ ~MissionStage();
+
+ //
+ // AI and waypoints
+ //
+ /*
+ void AddVehicle(Vehicle* vehicle,
+ int vehicleCentralIndex,
+ CarStartLocator* spawnlocator,
+ VehicleAI* vehicleAI );
+ */
+
+ void AddVehicle(Vehicle* vehicle,
+ int vehicleCentralIndex,
+ CarStartLocator* spawnlocator,
+ char* ainame);
+
+ void AddWaypoint( Locator* locator );
+ void AddCharacter( char* name,
+ CarStartLocator* spawnlocator,
+ CarStartLocator* carlocator,
+ const char* dynaloadString,
+ Vehicle* vehicle );
+ //chuck overloaded function
+ void AddCharacter(char* name,
+ CarStartLocator* spawnlocator,
+ CarStartLocator* carlocator,
+ const char* dynaloadString,
+ char* VehicleName);
+
+
+
+ struct AIParams
+ {
+ int minShortcutSkill;
+ int maxShortcutSkill;
+
+ AIParams()
+ {
+ minShortcutSkill = 15;
+ maxShortcutSkill = 25;
+ };
+ };
+ void SetAIParams( Vehicle* vehicle, const AIParams& params );
+ void SetAIRaceCatchupParams( Vehicle* vehicle, const VehicleAI::RaceCatchupParams& params );
+ void SetAIEvadeCatchupParams( Vehicle* vehicle, const VehicleAI::EvadeCatchupParams& params );
+ void SetAITargetCatchupParams( Vehicle* vehicle, const VehicleAI::TargetCatchupParams& params );
+
+ //
+ // The objective of this mission
+ //
+ MissionObjective* GetObjective();
+ void SetObjective( MissionObjective* objective );
+
+ //
+ // The conditions for failure of this mission
+ //
+ unsigned int GetNumConditions();
+ void SetNumConditions( unsigned int num );
+
+ MissionCondition* GetCondition( int index );
+ void SetCondition( int index, MissionCondition* condition );
+
+ // Finds the first failure condition and returns it.
+ //
+ MissionCondition* GetFailureCondition() const;
+
+ //
+ // Activates all the stuff this stage uses
+ //
+ void Initialize();
+ //
+ // Cleans up all the stuff for this stage
+ //
+ void Finalize();
+ void VehicleFinalize();
+
+ //
+ // Resets all the objects and stuff to the default positions
+ //
+ void Reset();
+ void VehicleInfoInitialize();
+
+ void Start();
+
+ // Tells the dynamic loading system to start loading the area
+ // the mission starts in
+ //
+ bool LoadMissionStart();
+
+ //
+ // Update should call this every frame when this stage
+ // is active.
+ //
+ virtual void Update( unsigned int elapsedTime );
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ //
+ // Time for this stage
+ //
+ enum StageTimeType
+ {
+ STAGETIME_NOT_TIMED,
+ STAGETIME_ADD,
+ STAGETIME_SET,
+ NUM_STAGETIME_TYPES
+ };
+
+ void SetStageTime( StageTimeType type, unsigned int seconds );
+ void GetStageTime( StageTimeType &type, unsigned int &seconds );
+
+ void SetFinalStage( bool bIsFinal );
+ bool GetFinalStage();
+
+ //
+ // Progress in this stage
+ //
+ enum MissionStageState
+ {
+ STAGE_IDLE,
+ STAGE_INPROGRESS,
+ STAGE_COMPLETE,
+ STAGE_FAILED,
+ STAGE_BACKUP, //This tells us that something went wrong, we wanna go bak one stage.
+ STAGE_ALL_COMPLETE,
+ NUM_STATES
+ };
+
+ //
+ // Returns the progress of this mission stage
+ //
+ MissionStageState GetProgress() { return ( mState ); }
+
+ //
+ // Index in the bible of this mission stage's name
+ //
+ void SetNameIndex( int index );
+ int GetNameIndex();
+
+ //
+ // Returns the index in the text bible of the starting
+ // or success message
+ //
+ void SetStartMessageIndex( int index );
+ int GetStartMessageIndex();
+ void ShowStartMessageIndex( bool show ) { mbShowMessage = show; };
+
+ void SetCameraInfo( SuperCam::Type type, bool cut, bool quickTransition );
+
+ int GetNumVehicles() const { return mNumVehicles; };
+ Vehicle* GetVehicle( int which );
+
+ bool GetMusicChangeFlag() { return( mMusicChange ); }
+ void SetMusicChangeFlag() { mMusicChange = true; }
+
+ bool GetMusicAlwaysOnFlag() { return( mKeepMusicOn ); }
+ void SetMusicAlwaysOnFlag() { mKeepMusicOn = true; }
+
+ void SetStageStartMusicEvent( radKey32 key ) { mMusicEventKey = key; }
+ radKey32 GetStageStartMusicEvent() { return( mMusicEventKey ); }
+
+ void SetStageMusicState( radKey32 stateKey, radKey32 stateEventKey );
+ void GetStageMusicState( radKey32& stateKey, radKey32& stateEventKey );
+
+ void SetDialogKey( radKey32 key ) { mDialogEventKey = key; }
+ void SetConversationCharacterKey( tUID uid ) { mConversationCharacterKey = uid; }
+
+ void SetTrafficDensity( char density ) { mTrafficDensity = density; };
+
+
+ //Setting the chase vehicle spawnrate
+ void SetChaseSpawnRate (char* vehiclename,unsigned int spawnrate);
+
+ //Enable Traffic etc.
+ void EnableTraffic ();
+ void DisableTraffic ();
+ void ClearTrafficForStage();
+ void PutAllAisInLimbo( bool inLimbo );
+ void DisableHitAndRun();
+
+ //Add safezones
+ int AddSafeZone (CarStartLocator* locator,unsigned int radius);
+
+ //Make time into an elasped timer
+ void UseElapsedTime();
+ bool QueryUseElapsedTime();
+
+ //Chuck: Gambling Race Stuff
+ void SetRaceEnteryFee(int coins);
+ void PutMFPlayerInCar();
+
+ //Chuck:Adding these for Forced Car presentation clean up.
+ void SwapInDefaultCar();
+ void SwapInDefaultCarStart();
+ void OnProcessRequestsComplete( void* pUserData );
+ void SetSwapPlayerRespawnLocatorName(char* locatorName);
+ void SetSwapDefaultCarRespawnLocatorName(char* locatorName);
+ void SetSwapForcedCarRespawnLocatorName(char* locatorName);
+
+ //Call this to reset a player to this spawn pt once per stage.
+ void SetPlayerRespawnLocatorName(char* locatorName);
+ void SetmsPlayerCarRespawnLocatorName(char* locatorName);
+
+ //used to hide one character per stage, used for the bart abuduction on l1m7.
+ void SetCharacterToHide(char* charactername);
+ //script to trigger leaving the level
+ void SetLevelOver();
+ void SetGameOver(bool IsGameOver = true) { mbGameOver = IsGameOver; }
+
+ struct CountdownSequenceUnit
+ {
+ char textID[ 64 ];
+ int durationTime;
+ };
+
+ void SetCountdownEnabled( radKey32 dialogID, tUID secondSpeakerUID );
+ void AddCountdownSequenceUnit( const char* textID, int durationTime = 1000 );
+ CountdownSequenceUnit* GetCountdownSequenceUnit( int index ) const;
+ radKey32 GetCountdownDialogID() const { return mCountdownDialogKey; };
+ tUID GetCountdownSecondSpeakerUID() const { return mSecondSpeakerUID; };
+
+ inline bool StartBonusObjective() const { return mStartBonusObjectives; };
+ inline void MakeBonusObjectiveStart() { mIsBonusObjectiveStart = true; };
+
+ struct LockRequirement
+ {
+ enum Type { SKIN, CAR, NONE };
+ enum { MAX_NAME_LEN = 32 };
+ LockRequirement() : mType( NONE ) { mName[0] = '\0'; };
+ Type mType;
+ char mName[MAX_NAME_LEN + 1];
+ };
+
+ void SetLockRequirement( unsigned int which, LockRequirement::Type type, const char* name );
+ const LockRequirement& GetLockRequirement( unsigned int which );
+ bool GetMissionLocked() const { return mMissionLocked; };
+
+ void DisplayMissionStageIndexMessage();
+
+ // stage complete presentation
+ //
+ void ShowStageComplete( bool enabled ) { mShowStageComplete = enabled; }
+ bool IsShowStageComplete() const { return mShowStageComplete; }
+
+ // HUD icon image
+ //
+ void SetHUDIcon( const char* name );
+ const char* GetHUDIcon() const;
+
+ void SetIrisAtEnd() { mIrisAtEnd = true; };
+ void SetFadeOutAtEnd() { mFadeOutAtEnd = true; };
+ void SetIrisSpeed( float speed ) { mIrisSpeed = speed; };
+
+ enum Transition { IRIS, FADE, NONE };
+ Transition GetTransition() { if ( mIrisAtEnd ) { return IRIS; } else if ( mFadeOutAtEnd ) { return FADE; } else { return NONE; } };
+ void DoTransition();
+
+ void SetMissionAbortEnabled( bool isEnabled );
+ bool IsMissionAbortAllowed() const;
+
+ //Chuck dirty pulbic var
+ bool mbStayBlackForStage;
+ bool mbDisablePlayerControlForCountDown;
+
+ Vehicle* GetMainAIVehicleForThisStage();
+
+ static const int MAX_VEHICLES = 4;
+
+protected:
+ void ActivateVehicle( Vehicle* vehicle, bool bIsActive );
+ void OnStageCompleteSuccessful();
+private:
+#ifdef RAD_DEBUG
+ int m_Id;
+#endif
+ MissionStageState mState;
+ int DestroyStageVehicleAI();
+ void DestroyAllSafeZones ();
+
+ //
+ // Play dialog associated with stage completion if it exists
+ //
+ void triggerStageDialog();
+
+ //
+ // Objective (always only one per stage)
+ //
+ MissionObjective* mObjective;
+
+ //
+ // Conditions
+ //
+ static const int MAX_CONDITIONS = 8;
+
+ int mNumConditions;
+ MissionCondition* mConditions[ MAX_CONDITIONS ];
+
+ //
+ // Time for stage
+ //
+ StageTimeType mStageTimeType;
+ int mStageTime;
+
+ //
+ // AI Vehicles
+ //
+ struct VehicleInfo
+ {
+ VehicleInfo() : vehicle( NULL ), spawn( NULL ), vehicleAINum( -1 ), vehicleAI( NULL ) {};
+ Vehicle* vehicle;
+ CarStartLocator* spawn;
+ int vehicleAINum;//Stupid.
+ VehicleAI* vehicleAI;
+ };
+
+
+ int mNumVehicles;
+ VehicleInfo mVehicles[ MAX_VEHICLES ];
+
+ //
+ // Waypoints
+ //
+ int mNumWaypoints;
+ Locator* mWaypoints[ WaypointAI::MAX_WAYPOINTS ];
+
+ //
+ // Characters
+ //
+ struct CharacterInfo
+ {
+ CharacterInfo() : character( NULL ), locator( NULL ), carLocator( NULL ), pZoneEventLocator( NULL ), vehicle( NULL ) { name[0] = '\0'; };
+ char name[16];
+ Character* character;
+ CarStartLocator* locator;
+ CarStartLocator* carLocator;
+ ZoneEventLocator* pZoneEventLocator;
+ Vehicle* vehicle;
+ char VehicleName[16];
+ };
+
+ enum { MAX_CHARACTERS_IN_STAGE = 6 };
+
+ int mNumCharacters;
+ CharacterInfo mCharacters[ MAX_CHARACTERS_IN_STAGE ];
+
+ char mCharacterToHide [16];
+ bool mbLevelOver : 1;
+ bool mbGameOver : 1;
+ bool mbFinalStage : 1;
+
+ int miNameIndex;
+ int miStartMessageIndex;
+ bool mbShowMessage;
+
+ struct CameraInfo
+ {
+ CameraInfo() : type ( SuperCam::FOLLOW_CAM ), cut( false ), quickTransition( false ), active( false ) {};
+ SuperCam::Type type;
+ bool cut;
+ bool quickTransition;
+ bool active;
+ };
+
+ CameraInfo mCamInfo;
+
+ //
+ // Interactive music properties
+ //
+ bool mMusicChange;
+ bool mKeepMusicOn;
+ radKey32 mMusicEventKey;
+ radKey32 mMusicStateKey;
+ radKey32 mMusicStateEventKey;
+
+ //
+ // Dialog properties
+ //
+ radKey32 mDialogEventKey;
+ tUID mConversationCharacterKey;
+
+ //used to store any chase vehicle data that is for this stage
+ // more harrass AI etc
+ struct ChaseVehicleStruct
+ {
+ unsigned int mChaseVehicleSpawnRate;
+ char vehiclename [64] ;
+ };
+
+ ChaseVehicleStruct m_ChaseData_Array [MAX_CHASE_STRUCTS] ;
+
+ SafeZone* m_SafeZone_Array [MAX_SAFEZONES];
+
+ //Chuck Traffic Variable
+
+ bool mbClearTrafficForStage;
+ bool mbNoTrafficForStage;
+
+ bool mb_DisableHitAndRun;
+ bool mb_InsideSafeZone;
+
+ bool mb_UseElapsedTime; //used to make the time count up rather than down
+ int mRaceEnteryFee;
+ bool mbPutPlayerInCar;
+ bool mbRacePaidOut;
+
+
+ //Chuck: Variables for Forced car clean up
+ bool mbSwapInDefaultCar;
+ char mSwapDefaultCarRespawnLocatorName [32];
+ char mSwapForcedCarRespawnLocatorName [32];
+ char mSwapPlayerRespawnLocatorName [32];
+
+ //variable used for placing a player per stage basis
+ char mPlayerRespawnLocatorName [32];
+ char mPlayerCarRespawnLocatorName [32];
+
+ char mTrafficDensity;
+ bool mIsBonusObjectiveStart;
+ bool mStartBonusObjectives;
+
+ LockRequirement mRequirement[ MAX_LOCK_REQUIREMENTS ];
+ bool mMissionLocked;
+
+ bool mShowStageComplete : 1;
+ bool mCountdownEnabled : 1;
+ char mHUDIconImage[ 16 ];
+
+ bool mIrisAtEnd;
+ bool mFadeOutAtEnd;
+ float mIrisSpeed;
+
+ static const int MAX_NUM_COUNTDOWN_SEQUENCE_UNITS = 8;
+
+ CountdownSequenceUnit* mCountdownSequenceUnits;
+ int mNumCountdownSequenceUnits;
+
+ radKey32 mCountdownDialogKey;
+ tUID mSecondSpeakerUID;
+
+ bool mAllowMissionAbort : 1;
+ int mResetCounter;
+
+};
+
+//=============================================================================
+// MissionStage::SetFinalStage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool bIsFinal )
+//
+// Return: void
+//
+//=============================================================================
+inline void MissionStage::SetFinalStage( bool bIsFinal )
+{
+ mbFinalStage = bIsFinal;
+}
+
+//=============================================================================
+// MissionStage::GetFinalStage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool MissionStage::GetFinalStage()
+{
+ return mbFinalStage;
+}
+
+//=============================================================================
+// MissionStage::GetObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: MissionObjective
+//
+//=============================================================================
+inline MissionObjective* MissionStage::GetObjective()
+{
+ return ( mObjective );
+}
+
+//=============================================================================
+// MissionStage::SetObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( MissionObjective* objective )
+//
+// Return: void
+//
+//=============================================================================
+inline void MissionStage::SetObjective( MissionObjective* objective )
+{
+ mObjective = objective;
+}
+
+//=============================================================================
+// MissionStage::GetNumConditions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline unsigned int MissionStage::GetNumConditions()
+{
+ return ( mNumConditions );
+}
+
+//=============================================================================
+// MissionStage::SetNumConditions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int num )
+//
+// Return: inline
+//
+//=============================================================================
+inline void MissionStage::SetNumConditions( unsigned int num )
+{
+ mNumConditions = num;
+}
+
+//=============================================================================
+// MissionStage::GetCondition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (unsigned int index)
+//
+// Return: inline
+//
+//=============================================================================
+inline MissionCondition* MissionStage::GetCondition( int index )
+{
+ rAssert( index < MAX_CONDITIONS );
+ return ( mConditions[ index ] );
+}
+
+//=============================================================================
+// MissionStage::SetCondition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index, MissionCondition* condition )
+//
+// Return: void
+//
+//=============================================================================
+inline void MissionStage::SetCondition( int index, MissionCondition* condition )
+{
+ rAssert( index < MAX_CONDITIONS );
+ mConditions[ index ] = condition;
+}
+
+//=============================================================================
+// MissionStage::SetNameIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index )
+//
+// Return: void
+//
+//=============================================================================
+inline void MissionStage::SetNameIndex( int index )
+{
+ miNameIndex = index;
+}
+
+//=============================================================================
+// MissionStage::SetStartMessageIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index )
+//
+// Return: void
+//
+//=============================================================================
+inline void MissionStage::SetStartMessageIndex( int index )
+{
+ miStartMessageIndex = index;
+}
+
+//=============================================================================
+// MissionStage::GetStartMessageIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+inline int MissionStage::GetStartMessageIndex()
+{
+ return miStartMessageIndex;
+}
+
+//=============================================================================
+// MissionStage::SetHUDIcon
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index )
+//
+// Return: void
+//
+//=============================================================================
+inline void MissionStage::SetHUDIcon( const char* name )
+{
+ sprintf( mHUDIconImage, "%s.png", name );
+ rAssert( strlen( mHUDIconImage ) < sizeof( mHUDIconImage ) );
+}
+
+//=============================================================================
+// MissionStage::GetHUDIcon
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+inline const char* MissionStage::GetHUDIcon() const
+{
+ return mHUDIconImage;
+}
+
+//=============================================================================
+// MissionStage::SetMissionAbortEnabled
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void MissionStage::SetMissionAbortEnabled( bool isEnabled )
+{
+ mAllowMissionAbort = isEnabled;
+}
+
+//=============================================================================
+// MissionStage::IsMissionAbortAllowed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool MissionStage::IsMissionAbortAllowed() const
+{
+ return mAllowMissionAbort;
+}
+
+#endif //MISSIONSTAGE_H
diff --git a/game/code/mission/nocopbonusobjective.cpp b/game/code/mission/nocopbonusobjective.cpp
new file mode 100644
index 0000000..211f7ea
--- /dev/null
+++ b/game/code/mission/nocopbonusobjective.cpp
@@ -0,0 +1,174 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: nocopbonusobjective.cpp
+//
+// Description: Implement NoCopBonusObjective
+//
+// History: 12/6/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/nocopbonusobjective.h>
+#include <events/eventmanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// NoCopBonusObjective::NoCopBonusObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+NoCopBonusObjective::NoCopBonusObjective() : mElapsedTime( 0 )
+{
+ SetType( BonusObjective::NO_CHASE_COLLISIONS );
+ SetSuccessful( true );
+}
+
+//=============================================================================
+// NoCopBonusObjective::~NoCopBonusObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+NoCopBonusObjective::~NoCopBonusObjective()
+{
+}
+
+//=============================================================================
+// NoCopBonusObjective::Initialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NoCopBonusObjective::Initialize()
+{
+ GetEventManager()->AddListener( this, EVENT_COLLISION );
+}
+
+//=============================================================================
+// NoCopBonusObjective::Finalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NoCopBonusObjective::Finalize()
+{
+ GetEventManager()->RemoveListener( this, EVENT_COLLISION );
+}
+
+//=============================================================================
+// NoCopBonusObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void NoCopBonusObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ if ( GetStarted() && GetSuccessful() )
+ {
+ SetSuccessful( false );
+ //TODO: Update the HUD!
+ }
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// NoCopBonusObjective::OnReset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NoCopBonusObjective::OnReset()
+{
+ SetSuccessful( true );
+ mElapsedTime = 0;
+}
+
+//=============================================================================
+// NoCopBonusObjective::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NoCopBonusObjective::OnStart()
+{
+ //Turn on the HUD.
+}
+
+//=============================================================================
+// NoCopBonusObjective::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void NoCopBonusObjective::OnUpdate( unsigned int milliseconds )
+{
+ if ( GetStarted() )
+ {
+ mElapsedTime += milliseconds;
+ }
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/mission/nocopbonusobjective.h b/game/code/mission/nocopbonusobjective.h
new file mode 100644
index 0000000..6424be9
--- /dev/null
+++ b/game/code/mission/nocopbonusobjective.h
@@ -0,0 +1,77 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: nocopbonusobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 12/6/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef NOCOPBONUSOBJECTIVE_H
+#define NOCOPBONUSOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/bonusobjective.h>
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class NoCopBonusObjective : public BonusObjective, public EventListener
+{
+public:
+ NoCopBonusObjective();
+ virtual ~NoCopBonusObjective();
+
+ virtual void Initialize();
+ virtual void Finalize();
+ virtual unsigned int GetNumericData();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+protected:
+ virtual void OnReset();
+ virtual void OnStart();
+ virtual void OnUpdate( unsigned int milliseconds );
+
+private:
+ unsigned int mElapsedTime;
+
+ //Prevent wasteful constructor creation.
+ NoCopBonusObjective( const NoCopBonusObjective& nocopbonusobjective );
+ NoCopBonusObjective& operator=( const NoCopBonusObjective& nocopbonusobjective );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// NoCopBonusObjective::GetNumericData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int NoCopBonusObjective::GetNumericData()
+{
+ return mElapsedTime;
+}
+
+#endif //NoCopBonusObjective \ No newline at end of file
diff --git a/game/code/mission/nodamagebonusobjective.cpp b/game/code/mission/nodamagebonusobjective.cpp
new file mode 100644
index 0000000..3d6e27e
--- /dev/null
+++ b/game/code/mission/nodamagebonusobjective.cpp
@@ -0,0 +1,174 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: nodamagebonusobjective.cpp
+//
+// Description: Implement NoDamageBonusObjective
+//
+// History: 12/5/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/nodamagebonusobjective.h>
+#include <events/eventmanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// NoDamageBonusObjective::NoDamageBonusObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+NoDamageBonusObjective::NoDamageBonusObjective() : mElapsedTime( 0 )
+{
+ SetType( BonusObjective::NO_DAMAGE );
+ SetSuccessful( true );
+}
+
+//=============================================================================
+// NoDamageBonusObjective::~NoDamageBonusObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+NoDamageBonusObjective::~NoDamageBonusObjective()
+{
+}
+
+//=============================================================================
+// NoDamageBonusObjective::Initialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NoDamageBonusObjective::Initialize()
+{
+ GetEventManager()->AddListener( this, EVENT_COLLISION );
+}
+
+//=============================================================================
+// NoDamageBonusObjective::Finalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NoDamageBonusObjective::Finalize()
+{
+ GetEventManager()->RemoveListener( this, EVENT_COLLISION );
+}
+
+//=============================================================================
+// NoDamageBonusObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void NoDamageBonusObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ if ( GetStarted() && GetSuccessful() )
+ {
+ SetSuccessful( false );
+ //TODO: Update the HUD!
+ }
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// NoDamageBonusObjective::OnReset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NoDamageBonusObjective::OnReset()
+{
+ SetSuccessful( true );
+ mElapsedTime = 0;
+}
+
+//=============================================================================
+// NoDamageBonusObjective::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NoDamageBonusObjective::OnStart()
+{
+ //Turn on the HUD.
+}
+
+//=============================================================================
+// NoDamageBonusObjective::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void NoDamageBonusObjective::OnUpdate( unsigned int milliseconds )
+{
+ if ( GetStarted() && GetSuccessful() )
+ {
+ mElapsedTime += milliseconds;
+ }
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/mission/nodamagebonusobjective.h b/game/code/mission/nodamagebonusobjective.h
new file mode 100644
index 0000000..7fecd25
--- /dev/null
+++ b/game/code/mission/nodamagebonusobjective.h
@@ -0,0 +1,78 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: nodamagebonusobjective.h
+//
+// Description: This listens for EVENT_COLLISION and if that happens the objective
+// fails.
+//
+// History: 12/5/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef NODAMAGEBONUSOBJECTIVE_H
+#define NODAMAGEBONUSOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/bonusobjective.h>
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class NoDamageBonusObjective : public BonusObjective, public EventListener
+{
+public:
+ NoDamageBonusObjective();
+ virtual ~NoDamageBonusObjective();
+
+ virtual void Initialize();
+ virtual void Finalize();
+ virtual unsigned int GetNumericData();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+protected:
+ virtual void OnReset();
+ virtual void OnStart();
+ virtual void OnUpdate( unsigned int milliseconds );
+
+private:
+ unsigned int mElapsedTime;
+
+ //Prevent wasteful constructor creation.
+ NoDamageBonusObjective( const NoDamageBonusObjective& nodamagebonusobjective );
+ NoDamageBonusObjective& operator=( const NoDamageBonusObjective& nodamagebonusobjective );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// NoDamageBonusObjective::GetNumericData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int NoDamageBonusObjective::GetNumericData()
+{
+ return mElapsedTime;
+}
+
+#endif //NODAMAGEBONUSOBJECTIVE_H
diff --git a/game/code/mission/objectives/allobjectives.cpp b/game/code/mission/objectives/allobjectives.cpp
new file mode 100644
index 0000000..d7799fc
--- /dev/null
+++ b/game/code/mission/objectives/allobjectives.cpp
@@ -0,0 +1,22 @@
+#include <mission/objectives/deliveryobjective.cpp>
+#include <mission/objectives/destroyobjective.cpp>
+#include <mission/objectives/destroybossobjective.cpp>
+#include <mission/objectives/dialogueobjective.cpp>
+#include <mission/objectives/followobjective.cpp>
+#include <mission/objectives/getinobjective.cpp>
+#include <mission/objectives/gotoobjective.cpp>
+#include <mission/objectives/loseobjective.cpp>
+#include <mission/objectives/missionobjective.cpp>
+#include <mission/objectives/raceobjective.cpp>
+#include <mission/objectives/talktoobjective.cpp>
+#include <mission/objectives/collectibleobjective.cpp>
+#include <mission/objectives/collectdumpedobjective.cpp>
+#include <mission/objectives/fmvobjective.cpp>
+#include <mission/objectives/interiorobjective.cpp>
+#include <mission/objectives/coinobjective.cpp>
+#include <mission/objectives/loadvehicleobjective.cpp>
+#include <mission/objectives/pickupitemobjective.cpp>
+#include <mission/objectives/timerobjective.cpp>
+#include <mission/objectives/buycarobjective.cpp>
+#include <mission/objectives/buyskinobjective.cpp>
+#include <mission/objectives/gooutsideobjective.cpp> \ No newline at end of file
diff --git a/game/code/mission/objectives/buycarobjective.cpp b/game/code/mission/objectives/buycarobjective.cpp
new file mode 100644
index 0000000..2b57999
--- /dev/null
+++ b/game/code/mission/objectives/buycarobjective.cpp
@@ -0,0 +1,124 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: buycarobjective.cpp
+//
+// Description: Implement BuyCarObjective
+//
+// History: 6/3/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/objectives/buycarobjective.h>
+#include <mission/gameplaymanager.h>
+
+#include <worldsim/redbrick/vehicle.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// BuyCarObjective::BuyCarObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+BuyCarObjective::BuyCarObjective() :
+ mVehicleName( NULL )
+{
+}
+
+//=============================================================================
+// BuyCarObjective::~BuyCarObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+BuyCarObjective::~BuyCarObjective()
+{
+ if ( mVehicleName )
+ {
+ delete[] mVehicleName;
+ mVehicleName = NULL;
+ }
+}
+
+//=============================================================================
+// BuyCarObjective::SetVehicleName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void BuyCarObjective::SetVehicleName( const char* name )
+{
+ int length = strlen( name );
+
+ rTuneAssertMsg( mVehicleName == NULL, "Can not set the name of the car to be purchased twice!\n");
+
+ if ( mVehicleName )
+ {
+ delete[] mVehicleName;
+ }
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ mVehicleName = new char[ length + 1 ];
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+
+ strcpy( mVehicleName, name );
+ mVehicleName[ length ] = '\0';
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// BuyCarObjective::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void BuyCarObjective::OnUpdate( unsigned int elapsedTime )
+{
+ if ( strcmp( GetGameplayManager()->GetCurrentVehicle()->GetName(), mVehicleName ) == 0 )
+ {
+ SetFinished( true );
+ }
+}
diff --git a/game/code/mission/objectives/buycarobjective.h b/game/code/mission/objectives/buycarobjective.h
new file mode 100644
index 0000000..29e0d8f
--- /dev/null
+++ b/game/code/mission/objectives/buycarobjective.h
@@ -0,0 +1,56 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: buycarobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 6/3/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef BUYCAROBJECTIVE_H
+#define BUYCAROBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/objectives/missionobjective.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class BuyCarObjective : public MissionObjective
+{
+public:
+ BuyCarObjective();
+ virtual ~BuyCarObjective();
+
+ void SetVehicleName( const char* name );
+
+protected:
+
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+private:
+ char* mVehicleName;
+
+ //Prevent wasteful constructor creation.
+ BuyCarObjective( const BuyCarObjective& buycarobjective );
+ BuyCarObjective& operator=( const BuyCarObjective& buycarobjective );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //BUYCAROBJECTIVE_H
diff --git a/game/code/mission/objectives/buyskinobjective.cpp b/game/code/mission/objectives/buyskinobjective.cpp
new file mode 100644
index 0000000..1b21df4
--- /dev/null
+++ b/game/code/mission/objectives/buyskinobjective.cpp
@@ -0,0 +1,126 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: buyskinobjective.cpp
+//
+// Description: Implement BuySkinObjective
+//
+// History: 6/3/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/objectives/buyskinobjective.h>
+#include <mission/gameplaymanager.h>
+
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// BuySkinObjective::BuySkinObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+BuySkinObjective::BuySkinObjective() :
+ mSkinName( NULL )
+{
+}
+
+//=============================================================================
+// BuySkinObjective::~BuySkinObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+BuySkinObjective::~BuySkinObjective()
+{
+ if ( mSkinName )
+ {
+ delete[] mSkinName;
+ mSkinName = NULL;
+ }
+}
+
+//=============================================================================
+// BuySkinObjective::SetSkinName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void BuySkinObjective::SetSkinName( const char* name )
+{
+ int length = strlen( name );
+
+ rTuneAssertMsg( mSkinName == NULL, "Can not set the name of the skin to be purchased twice!\n");
+
+ if ( mSkinName )
+ {
+ delete[] mSkinName;
+ }
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ mSkinName = new char[ length + 1 ];
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+
+ strcpy( mSkinName, name );
+ mSkinName[ length ] = '\0';
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// BuySkinObjective::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void BuySkinObjective::OnUpdate( unsigned int elapsedTime )
+{
+ Character* character = GetCharacterManager()->GetCharacter( 0 );
+
+ if ( strcmp( GetCharacterManager()->GetModelName( character ), mSkinName ) == 0 )
+ {
+ SetFinished( true );
+ }
+}
diff --git a/game/code/mission/objectives/buyskinobjective.h b/game/code/mission/objectives/buyskinobjective.h
new file mode 100644
index 0000000..e2590ed
--- /dev/null
+++ b/game/code/mission/objectives/buyskinobjective.h
@@ -0,0 +1,56 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: buyskinobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 6/3/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef BUYSKINOBJECTIVE_H
+#define BUYSKINOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/objectives/missionobjective.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class BuySkinObjective : public MissionObjective
+{
+public:
+ BuySkinObjective();
+ virtual ~BuySkinObjective();
+
+ void SetSkinName( const char* name );
+
+protected:
+
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+private:
+ char* mSkinName;
+
+ //Prevent wasteful constructor creation.
+ BuySkinObjective( const BuySkinObjective& buyskinobjective );
+ BuySkinObjective& operator=( const BuySkinObjective& buyskinobjective );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //BUYSKINOBJECTIVE_H
diff --git a/game/code/mission/objectives/coinobjective.cpp b/game/code/mission/objectives/coinobjective.cpp
new file mode 100644
index 0000000..1fe932c
--- /dev/null
+++ b/game/code/mission/objectives/coinobjective.cpp
@@ -0,0 +1,204 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: CoinObjective.cpp
+//
+// Description: ImplementCoinObjective
+//
+// History: 03/09/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/objectives/coinobjective.h>
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <worldsim/coins/coinmanager.h>
+#include <events/eventmanager.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+//CoinObjective::CoinObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+CoinObjective::CoinObjective()
+
+{
+ mbFeeCollected = false;
+ mCoinFee = 0;
+
+}
+
+//=============================================================================
+//CoinObjective::~CoinObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+CoinObjective::~CoinObjective()
+{
+
+}
+
+//=============================================================================
+//CoinObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void CoinObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch (id)
+ {
+ case EVENT_ATTEMPT_TO_ENTER_GAMBLERACE:
+ {
+ //check if player has enough tokens to enter race
+ if (GetCoinManager()->GetBankValue() >= mCoinFee)
+ {
+ mbFeeCollected = true;
+ GetEventManager()->TriggerEvent(EVENT_ENTER_GAMBLERACE_SUCCESS);
+ int subtract_coins = 0;
+ subtract_coins -= mCoinFee;
+ GetCoinManager()->AdjustBankValue(subtract_coins);
+ SetFinished(true);
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent(EVENT_ENTER_GAMBLERACE_FAILURE);
+ GetEventManager()->TriggerEvent(EVENT_TREE_OF_WOE_NEGATIVE_FEEDBACK);
+ mbFeeCollected = false;
+ }
+ break;
+ }
+ default :
+ {
+ //Should never enter here
+ rAssert(0);
+ }
+ }
+
+
+
+}
+
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+//CoinObjective::OnInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void CoinObjective::OnInitialize()
+{
+ GetEventManager()->AddListener(this,EVENT_ATTEMPT_TO_ENTER_GAMBLERACE);
+}
+
+//=============================================================================
+//CoinObjective::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void CoinObjective::OnFinalize()
+{
+ GetEventManager()->RemoveAll(this);
+}
+
+
+void CoinObjective::Update(unsigned int elaspedTime)
+{
+ OnUpdate(elaspedTime);
+}
+
+
+void CoinObjective::OnUpdate(unsigned int elaspedTime)
+{
+ if (mbFeeCollected == true)
+ {
+ SetFinished(true);
+ }
+ else
+ {
+ GetGameplayManager()->AbortCurrentMission();
+ }
+}
+
+
+bool CoinObjective::PayCoinFee()
+{
+ if ( GetCharacterSheetManager()->GetNumberOfTokens( GetGameplayManager()->GetCurrentLevelIndex()) > mCoinFee)
+ {
+ //subtract token from character sheet and set mbFinished to true
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+void CoinObjective::SetCoinFee(int coins)
+{
+ mCoinFee= coins;
+}
+
+int CoinObjective::GetCoinAmount()
+{
+ return mCoinFee;
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/mission/objectives/coinobjective.h b/game/code/mission/objectives/coinobjective.h
new file mode 100644
index 0000000..018661e
--- /dev/null
+++ b/game/code/mission/objectives/coinobjective.h
@@ -0,0 +1,64 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: CoinObjective.h
+//
+// Description: Used for Race Missions that require coins to enter
+//
+// History: Mar. 27. 2003 + Created -- Chuck C.
+//
+//=============================================================================
+
+#ifndef COINOBJECTIVE_H
+#define COINOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/objectives/missionobjective.h>
+#include <events/eventdata.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Coin Objective
+//
+//=============================================================================
+
+class CoinObjective : public MissionObjective
+{
+public:
+
+
+ CoinObjective();
+ virtual ~CoinObjective();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ void SetCoinFee(int coins);
+ void SetFeeCollected();
+ int GetCoinAmount();
+ bool PayCoinFee();
+ //these two functions are soo hacky they are a work around for missionstages resetting for Coin Objectives
+ bool Proceed();
+ void SetProceed();
+
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+ virtual void OnUpdate(unsigned int elapsedTime);
+ virtual void Update(unsigned int elaspedTime);
+private:
+ bool mbFeeCollected;
+ int mCoinFee;
+ bool mHack;
+ //Prevent wasteful constructor creation.
+ CoinObjective( const CoinObjective& CoinObjective );
+ CoinObjective& operator=( const CoinObjective& CoinObjective );
+};
+
+
+#endif //COINOBJECTIVE_H
diff --git a/game/code/mission/objectives/collectdumpedobjective.cpp b/game/code/mission/objectives/collectdumpedobjective.cpp
new file mode 100644
index 0000000..a118530
--- /dev/null
+++ b/game/code/mission/objectives/collectdumpedobjective.cpp
@@ -0,0 +1,781 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: collectdumpedobjective.cpp
+//
+// Description: Implement CollectDumpedObjective
+//
+// History: 1/7/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radmath/radmath.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/objectives/collectdumpedobjective.h>
+#include <mission/gameplaymanager.h>
+
+#include <events/eventmanager.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#include <roads/roadmanager.h>
+#include <roads/road.h>
+#include <roads/roadsegment.h>
+#include <roads/lane.h>
+
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/hitnrunmanager.h>
+#include <ai/vehicle/chaseai.h>
+
+#include <meta/locator.h>
+
+#include <mission/animatedicon.h>
+
+#include <mission/conditions/missioncondition.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+#ifdef DEBUGWATCH
+float DEFAULT_FORCE = 0.038f; // was 0.072
+int MAX_TIMEOUT = 5000;
+static unsigned int CD_DEFAULT_TIMEOUT = 5000;
+static unsigned int DEFAULT_DUMP_LIFETIME = 15000;
+#else
+const float DEFAULT_FORCE = 0.05f; // was 0.072
+const int MAX_TIMEOUT = 5000;
+static const unsigned int CD_DEFAULT_TIMEOUT = 5000;
+static const unsigned int DEFAULT_DUMP_LIFETIME = 15000;
+#endif
+
+#if defined( DEBUGWATCH ) || defined( RAD_WIN32 )
+extern float DEFAULT_DIST;
+#else
+extern const float DEFAULT_DIST;
+#endif
+
+class CollectionCondition : public MissionCondition
+{
+public:
+ CollectionCondition() { SetType( COND_GET_COLLECTIBLES ); }
+ virtual ~CollectionCondition() {};
+
+ void Update( unsigned int elapsedTime ) {};
+ void SetViolated(bool flag) {SetIsViolated(true);}
+
+ bool IsClose() { return false; }
+
+private:
+ //Prevent wasteful constructor creation.
+ CollectionCondition ( const CollectionCondition & c);
+ CollectionCondition& operator=( const CollectionCondition & c);
+};
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// CollectDumpedObjective::CollectDumpedObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+CollectDumpedObjective::CollectDumpedObjective() :
+ mDumpVehicle( NULL ),
+ mDumpVehicleAI( NULL ),
+ mNumUncollected( 0 ),
+ mBumperCars( true ),
+ mNumSpawned( 0 ),
+ mDumpTimeout( -1 ),
+ mWhatToDump( 0 ),
+ mAmIDumping( false ),
+ mAssaultCar( NULL ),
+ mMeDumpTimout( 0 ),
+ mDumpLifetime ( 0 ),
+ mTerminalDump ( false ),
+ mAnimatedIcon ( NULL )
+{
+ mCondition = NULL;
+}
+
+//=============================================================================
+// CollectDumpedObjective::~CollectDumpedObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+CollectDumpedObjective::~CollectDumpedObjective()
+{
+ mCondition = NULL; //stage will delete it
+}
+
+//=============================================================================
+// CollectDumpedObjective::BindCollectibleToWaypoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int collectibleNum, unsigned int waypointNum )
+//
+// Return: void
+//
+//=============================================================================
+void CollectDumpedObjective::BindCollectibleToWaypoint( int collectibleNum, unsigned int waypointNum )
+{
+ rAssert( static_cast<int>(waypointNum) < WaypointAI::MAX_WAYPOINTS );
+ mDumpData[ waypointNum ].collectibleNum = collectibleNum;
+
+ //Aww... I was waiting for the candy.
+ mBumperCars = false;
+}
+
+//=============================================================================
+// CollectDumpedObjective::SetDumpVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* vehicle )
+//
+// Return: void
+//
+//=============================================================================
+void CollectDumpedObjective::SetDumpVehicle( Vehicle* vehicle )
+{
+ rAssert( vehicle );
+ mDumpVehicle = vehicle;
+}
+
+//=============================================================================
+// CollectDumpedObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void CollectDumpedObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_WAYAI_HIT_WAYPOINT:
+ {
+ //Test to see if this is a waypoint we care about
+ int lastWaypoint = mDumpVehicleAI->GetCurrentWayPoint();
+ rAssert( lastWaypoint >= 0 );
+ int collectibleNum = mDumpData[ lastWaypoint ].collectibleNum;
+ if ( !mDumpData[ lastWaypoint ].collected && lastWaypoint >= 0 &&
+ collectibleNum != -1 && mDumpTimeout == -1 )
+ {
+ //This is a dumping waypoint
+ mWhatToDump = static_cast<unsigned int>(collectibleNum);
+ mDumpTimeout = rmt::FtoL( rmt::LtoF(rand()) / rmt::LtoF(RAND_MAX) * rmt::LtoF(MAX_TIMEOUT) );
+ mAmIDumping = false;
+ mDumpLifetime = 0;
+
+ mDumpData[ lastWaypoint ].active = true;
+ }
+ break;
+ }
+ case EVENT_VEHICLE_DESTROYED:
+ {
+ if ( !mBumperCars || mNumCollectibles > 1 )
+ {
+ break;
+ }
+
+ Vehicle* data = static_cast<Vehicle*>(pEventData);
+ if ( data == mDumpVehicle )
+ {
+ //This is the end!
+ //Throw mamma from the train.
+ unsigned int collectibleNum = FindFreeSlot();
+
+ //Since there are no bindings, we'll fill the mDumpData table.
+ mDumpData[ collectibleNum ].collectibleNum = collectibleNum;
+ mDumpData[ collectibleNum ].active = true;
+
+ //Set the drop timout to something small to give the user a ittle reaction time
+ mDumpTimeout = 1000;
+ mWhatToDump = collectibleNum;
+ mAmIDumping = false;
+ mDumpLifetime = 0;
+ mTerminalDump = true;
+
+ ++mNumSpawned;
+ }
+
+ break;
+ }
+ case EVENT_VEHICLE_VEHICLE_COLLISION:
+ {
+ if ( (!mBumperCars && !IsUserDumpAllowed()) || mNumSpawned >= GetNumCollectibles() || mNumCollectibles == 1 )
+ {
+ break;
+ }
+
+ CarOnCarCollisionEventData* data = static_cast<CarOnCarCollisionEventData*>(pEventData);
+ if ( data->vehicle == mDumpVehicle && mBumperCars )
+ {
+ if ( data->force >= DEFAULT_FORCE && mDumpTimeout == -1 && mNumCollectibles > 1 ) //If numCollectibles is == 1 then it's on Death only
+ {
+ //Throw mamma from the train.
+ unsigned int collectibleNum = FindFreeSlot();
+
+ //Since there are no bindings, we'll fill the mDumpData table.
+ mDumpData[ collectibleNum ].collectibleNum = collectibleNum;
+ mDumpData[ collectibleNum ].active = true;
+
+ //Set the drop timout to something small to give the user a ittle reaction time
+ mDumpTimeout = 1000;
+ mWhatToDump = collectibleNum;
+ mAmIDumping = false;
+ mDumpLifetime = DEFAULT_DUMP_LIFETIME;
+
+ ++mNumSpawned;
+
+ //Let's damage the dumper car.
+ /*
+ float damage = data->vehicle->mDesignerParams.mHitPoints * rmt::LtoF(mNumSpawned) / rmt::LtoF(GetNumCollectibles());
+ float currDamage = data->vehicle->mDesignerParams.mHitPoints - data->vehicle->mHitPoints;
+ damage -= currDamage;
+ if ( damage < 0.0f )
+ {
+ damage = 0.0f;
+ }
+
+ data->vehicle->SwitchOnDamageTypeAndApply( damage, *(data->collision) );
+ */
+ }
+ }
+ else
+ {
+ //This is a car hit me type thing.
+ //Let's dump some of our cargo.
+ /*
+
+ greg
+ july 3, 2003
+
+ could this actually be my last bug?
+
+ Cary says we don't need this block.
+
+
+
+ if ( GetNumCollected() > 0 && mMeDumpTimout == 0 )
+ {
+ int id = GetVehicleCentral()->GetVehicleId( data->vehicle );
+ VehicleController* controller = GetVehicleCentral()->GetVehicleController( id );
+
+ if ( controller )
+ {
+ //This sounds slow
+ ChaseAI* pai = dynamic_cast<ChaseAI*>( controller );
+ if ( pai )
+ {
+ int collectedID = GetAnyCollectedID();
+ rAssert( collectedID > -1 );
+
+ //This is a guy who hit me.
+ //Set the drop timout to 0
+ mDumpTimeout = 0;
+ mWhatToDump = static_cast<unsigned int>( collectedID );
+ mAmIDumping = true;
+ mAssaultCar = data->vehicle;
+ mDumpLifetime = 0;
+
+ //reset the timeout
+ mMeDumpTimout = CD_DEFAULT_TIMEOUT;
+ }
+ }
+ }
+ */
+
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ CollectibleObjective::HandleEvent( id, pEventData );
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// CollectDumpedObjective::OnInitCollectibles
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void CollectDumpedObjective::OnInitCollectibles()
+{
+
+}
+
+//=============================================================================
+// CollectDumpedObjective::OnInitCollectibleObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void CollectDumpedObjective::OnInitCollectibleObjective()
+{
+ int i;
+ for( i = 0; i < static_cast<int>( mNumCollectibles ); i++ )
+ {
+ Activate( i, false, false );
+ }
+
+ mAnimatedIcon = new AnimatedIcon();
+
+ if ( mBumperCars )
+ {
+ //Listen for the collision event.
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_VEHICLE_COLLISION );
+
+ if ( mNumCollectibles == 1 )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_DAMAGE_METER );
+
+ // update damage meter
+ if ( GetCurrentHud() )
+ {
+ GetCurrentHud()->SetDamageMeter( 0.0f ); // between 0.0 and 1.0
+ }
+
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ }
+
+ rmt::Vector carPos;
+ mDumpVehicle->GetPosition( &carPos );
+ mAnimatedIcon->Init( ARROW_DESTROY, carPos );
+ mAnimatedIcon->ScaleByCameraDistance( MIN_ARROW_SCALE, MAX_ARROW_SCALE, MIN_ARROW_SCALE_DIST, MAX_ARROW_SCALE_DIST );
+
+ }
+ else
+ {
+ //Look for the bound waypoints
+ GetEventManager()->AddListener( this, EVENT_WAYAI_HIT_WAYPOINT );
+
+ if ( IsUserDumpAllowed() )
+ {
+ //Listen for the collision event.
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_VEHICLE_COLLISION );
+ }
+
+ rmt::Vector carPos;
+ mDumpVehicle->GetPosition( &carPos );
+ mAnimatedIcon->Init( ARROW_EVADE, carPos );
+ mAnimatedIcon->ScaleByCameraDistance( MIN_ARROW_SCALE, MAX_ARROW_SCALE, MIN_ARROW_SCALE_DIST, MAX_ARROW_SCALE_DIST );
+
+ }
+
+ if( mNumCollectibles > 1 ) // only show collectibles HUD overlay if more than 1 things to collect
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_COLLECTIBLES );
+ }
+
+ if ( mBumperCars )
+ //void disable damage on this guy
+ //The vehicle is damaged only by collecting the
+ {
+ if ( mNumCollectibles == 1 )
+ {
+ //void disable damage on this guy
+ //The vehicle is damaged only by collecting the items he has to offer.
+ //When you've smashed all the candy out of him, he damages out.
+ mDumpVehicle->SetVehicleCanSustainDamage( true );
+ mDumpVehicle->VehicleIsADestroyObjective( true );
+ GetGameplayManager()->GetCurrentVehicle()->SetVehicleCanSustainDamage( false );
+ }
+ else
+ {
+ //void disable damage on this guy
+ //The vehicle is damaged only by collecting the items he has to offer.
+ //When you've smashed all the candy out of him, he damages out.
+ mDumpVehicle->SetVehicleCanSustainDamage( false );
+ GetGameplayManager()->GetCurrentVehicle()->SetVehicleCanSustainDamage(false);
+ }
+ GetHitnRunManager()->RegisterVehicleImmunity( mDumpVehicle );
+ }
+ else
+ {
+ mDumpVehicle->SetVehicleCanSustainDamage( false );
+ }
+
+ //Set this vehicle as the focus of the HUD.
+ rAssert( mDumpVehicle );
+ VehicleAI* ai = GetVehicleCentral()->GetVehicleAI( mDumpVehicle );
+ rAssert( ai );
+
+ mDumpVehicleAI = dynamic_cast<WaypointAI*>(ai);
+ rAssert( mDumpVehicleAI );
+ mDumpVehicleAI->AddRef();
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud )
+ {
+ //Update the collection count
+ currentHud->SetCollectibles( 0, GetNumCollectibles() );
+
+ //Now set the focus back on the car.
+ currentHud->GetHudMap( 0 )->SetFocalPointIcon( mDumpVehicleAI->GetHUDIndex() );
+ }
+
+ for ( i = 0; i < WaypointAI::MAX_WAYPOINTS; ++i )
+ {
+ mDumpData[i].active = false;
+ mDumpData[i].collected = false;
+ }
+
+ mMeDumpTimout = 0;
+
+#ifdef DEBUGWATCH
+ if ( mBumperCars )
+ {
+ char name[64];
+ sprintf( name, "Mission\\Objectives\\BumperCar" );
+ radDbgWatchAddFloat( &DEFAULT_FORCE, "Min Force", name, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &DEFAULT_DIST, "Min Dist", name, NULL, NULL, 0.0f, 10.0f );
+ radDbgWatchAddInt( &MAX_TIMEOUT, "Max Timeout", name, NULL, NULL, 0, 10000 );
+ radDbgWatchAddUnsignedInt( &CD_DEFAULT_TIMEOUT, "Dump timeout", name, NULL, 0, 100000 );
+ radDbgWatchAddUnsignedInt( &DEFAULT_DUMP_LIFETIME, "Dump lifetime", name, NULL, 0, 100000 );
+ }
+#endif
+
+ MissionStage* stage = GetGameplayManager()->GetCurrentMission()->GetCurrentStage();
+
+ // not sure why this is needed, Init seems to be called twice in some cases
+ bool alreadyHave = false;
+ if ( mCondition == NULL)
+ {
+ mCondition = new CollectionCondition;
+ }
+
+ for(int i = 0; i < static_cast<int>( stage->GetNumConditions() ); i++)
+ {
+
+ if(stage->GetCondition(i) == mCondition)
+ {
+ alreadyHave = true;
+ break;
+ }
+ }
+
+ if(!alreadyHave)
+ {
+ stage->SetCondition(stage->GetNumConditions(), mCondition);
+ stage->SetNumConditions(stage->GetNumConditions() + 1);
+ }
+}
+
+//=============================================================================
+// CollectDumpedObjective::OnFinalizeCollectibleObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void CollectDumpedObjective::OnFinalizeCollectibleObjective()
+{
+ GetEventManager()->RemoveAll( this );
+
+ GetEventManager()->TriggerEvent(EVENT_DUMP_STATUS, (void*)0);
+
+ GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_COLLECTIBLES );
+
+ if ( mNumCollectibles == 1 )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_DAMAGE_METER );
+ }
+
+ mNumUncollected = 0 ;
+ mNumSpawned = 0;
+
+ if ( mBumperCars )
+ {
+ radDbgWatchDelete( (void*)(&DEFAULT_FORCE) );
+ radDbgWatchDelete( (void*)(&DEFAULT_DIST) );
+ radDbgWatchDelete( (void*)(&MAX_TIMEOUT) );
+ radDbgWatchDelete( (void*)(&CD_DEFAULT_TIMEOUT) );
+
+ GetGameplayManager()->GetCurrentVehicle()->SetVehicleCanSustainDamage(true);
+ GetHitnRunManager()->RegisterVehicleImmunity( NULL );
+ }
+ else
+ {
+ mDumpVehicle->SetVehicleCanSustainDamage(true);
+ }
+
+ rAssert( mDumpVehicleAI );
+ mDumpVehicleAI->Release();
+
+ delete mAnimatedIcon;
+ mAnimatedIcon = NULL;
+}
+
+//=============================================================================
+// CollectDumpedObjective::OnCollection
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int collectibleNum, bool &shouldReset )
+//
+// Return: bool
+//
+//=============================================================================
+bool CollectDumpedObjective::OnCollection( unsigned int collectibleNum, bool &shouldReset )
+{
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud )
+ {
+ //Update the collection count
+ currentHud->SetCollectibles( GetNumCollected() + 1, GetNumCollectibles() ); //I add 1 because the number is incremented after... Sorry
+ }
+
+ mNumUncollected--;
+ GetEventManager()->TriggerEvent(EVENT_DUMP_STATUS, (void*)mNumUncollected);
+
+ //Mark the collectible as collected.
+ int i;
+ for ( i = 0; i < WaypointAI::MAX_WAYPOINTS; ++i )
+ {
+ if ( mDumpData[i].collectibleNum == static_cast<int>(collectibleNum) )
+ {
+ mDumpData[i].collected = true;
+ break;
+ }
+ }
+
+ if ( currentHud )
+ {
+ if ( mNumUncollected == 0 )
+ {
+ //Now set the focus back on the car.
+ currentHud->GetHudMap( 0 )->SetFocalPointIcon( mDumpVehicleAI->GetHUDIndex() );
+ }
+ else
+ {
+ //Select the next uncollected object.
+ for ( i = 0; i < WaypointAI::MAX_WAYPOINTS; ++i )
+ {
+ if ( mDumpData[i].active && !mDumpData[i].collected )
+ {
+ SetFocus( mDumpData[i].collectibleNum );
+ }
+ }
+ }
+ }
+
+ //You should only be able to collect things that have been dropped, so this is always true.
+ return true;
+}
+
+//=============================================================================
+// CollectDumpedObjective::OnUpdateCollectibleObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTimeMilliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void CollectDumpedObjective::OnUpdateCollectibleObjective( unsigned int elapsedTimeMilliseconds )
+{
+ //Update the arrow
+ rmt::Vector carPos;
+ Vehicle* vehicle = mDumpVehicle;
+ vehicle->GetPosition( &carPos );
+
+ rmt::Box3D bbox;
+ vehicle->GetBoundingBox( &bbox );
+ carPos.y = bbox.high.y;
+
+ mAnimatedIcon->Move( carPos );
+ mAnimatedIcon->Update( elapsedTimeMilliseconds );
+
+ if ( mNumCollectibles == 1 )
+ {
+ //Update the HUD.
+ // update damage meter
+ if ( GetCurrentHud() )
+ {
+ GetCurrentHud()->SetDamageMeter( 1.0f - mDumpVehicle->GetVehicleLifePercentage(mDumpVehicle->mHitPoints) ); // between 0.0 and 1.0
+ }
+ }
+
+ for(int i = 0; i < MAX_COLLECTIBLES; i++)
+ {
+ if((mDumpData[ i ].lifetime > 0) && mDumpData[ i ].active && !mDumpData[ i ].collected)
+ {
+ if((mDumpData[ i ].lifetime - (int)elapsedTimeMilliseconds) < 0)
+ {
+ mDumpData[ i ].lifetime = 0;
+ Activate(i, false, false);
+ mDumpData[ i ].active = false;
+ mDumpData[ i ].collected= false;
+ --mNumUncollected;
+ --mNumSpawned;
+ GetEventManager()->TriggerEvent(EVENT_DUMP_STATUS, (void*)mNumUncollected);
+ }
+ else
+ {
+ mDumpData[ i ].lifetime -= elapsedTimeMilliseconds;
+ }
+
+ if(mDumpData[ i ].lifetime < 5000)
+ {
+ if( mCollectibles[ i ].mAnimatedIcon != NULL )
+ {
+ mCollectibles[ i ].mAnimatedIcon->ShouldRender( ((mDumpData[ i ].lifetime >> 8) & 1) != 0 );
+ }
+ }
+ }
+ }
+
+ if ( IsUserDumpAllowed() )
+ {
+ if ( mMeDumpTimout != 0 )
+ {
+ if ( mMeDumpTimout < elapsedTimeMilliseconds )
+ {
+ mMeDumpTimout = 0;
+ }
+ else
+ {
+ mMeDumpTimout -= elapsedTimeMilliseconds;
+ }
+ }
+ }
+
+ if(!mBumperCars) // don't fail on dropped objectives, they'll timeout anyway
+ {
+ for(int i = 0; i < MAX_COLLECTIBLES; i++)
+ {
+ if(mCollectibles[ i ].pLocator && mCollectibles[ i ].pLocator->GetFlag(Locator::ACTIVE))
+ {
+ rmt::Vector pos, charPos;
+ mCollectibles[ i ].pLocator->GetPosition(&pos);
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetPosition(charPos);
+ pos.Sub(charPos);
+ if(pos.Magnitude() > 200)
+ {
+ if(mCondition)
+ {
+ mCondition->SetViolated(true);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ if ( mDumpTimeout == -1 || mDumpVehicle->IsAirborn() )
+ {
+ //NO dumping!
+ return;
+ }
+
+ if ( mDumpTimeout - static_cast<int>(elapsedTimeMilliseconds) <= 0 )
+ {
+ if ( mAmIDumping )
+ {
+ DumpCollectible( mWhatToDump, GetGameplayManager()->GetCurrentVehicle(), mAssaultCar );
+ Uncollect( mWhatToDump );
+
+ ++mNumUncollected;
+ GetEventManager()->TriggerEvent(EVENT_DUMP_STATUS, (void*)mNumUncollected);
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud )
+ {
+ //Update the collection count
+ currentHud->SetCollectibles( GetNumCollected(), GetNumCollectibles() );
+ }
+
+ }
+ else
+ {
+ DumpCollectible( mWhatToDump, mDumpVehicle, GetGameplayManager()->GetCurrentVehicle(), true, mTerminalDump );
+ mDumpData[ mWhatToDump ].lifetime = mDumpLifetime;
+
+ ++mNumUncollected;
+ GetEventManager()->TriggerEvent(EVENT_DUMP_STATUS, (void*)mNumUncollected);
+ }
+ mDumpTimeout = -1;
+ }
+ else
+ {
+ mDumpTimeout -= static_cast<int>(elapsedTimeMilliseconds);
+ }
+}
+
+//=============================================================================
+// CollectDumpedObjective::FindFreeSlot
+//=============================================================================
+int CollectDumpedObjective::FindFreeSlot(void)
+{
+ for(int i = 0; i < MAX_COLLECTIBLES; i++)
+ {
+ if(!mDumpData[ i ].active && !mDumpData[ i ].collected)
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
diff --git a/game/code/mission/objectives/collectdumpedobjective.h b/game/code/mission/objectives/collectdumpedobjective.h
new file mode 100644
index 0000000..0c5e9fe
--- /dev/null
+++ b/game/code/mission/objectives/collectdumpedobjective.h
@@ -0,0 +1,109 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: collectdumpedobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 1/7/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef COLLECTDUMPEDOBJECTIVE_H
+#define COLLECTDUMPEDOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/objectives/collectibleobjective.h>
+
+#include <events/eventlistener.h>
+
+#include <ai/vehicle/waypointai.h>
+
+//========================================
+// Forward References
+//========================================
+class Locator;
+class Vehicle;
+class AnimatedIcon;
+class CollectionCondition;
+
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class CollectDumpedObjective : public CollectibleObjective
+{
+public:
+ CollectDumpedObjective();
+ virtual ~CollectDumpedObjective();
+
+ void BindCollectibleToWaypoint( int collectibleNum, unsigned int waypointNum );
+ void SetDumpVehicle( Vehicle* vehicle );
+ Vehicle* GetDumpVehicle() {return mDumpVehicle;}
+
+ bool IsBumperCars() {return mBumperCars;}
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+protected:
+ virtual void OnInitCollectibles();
+ virtual void OnInitCollectibleObjective();
+ virtual void OnFinalizeCollectibleObjective();
+ virtual bool OnCollection( unsigned int collectibleNum, bool &shouldReset );
+ virtual void OnUpdateCollectibleObjective( unsigned int elapsedTimeMilliseconds );
+
+ int FindFreeSlot(void);
+
+ CollectionCondition* mCondition;
+
+private:
+
+ Vehicle* mDumpVehicle;
+ WaypointAI* mDumpVehicleAI;
+
+ enum { MAX_ICON_NAME = 32 };
+
+ unsigned int mNumUncollected;
+
+ struct DumpData
+ {
+ DumpData() : collectibleNum( -1 ), active( false ), collected( false ), lifetime(0) {};
+ int collectibleNum;
+ bool active;
+ bool collected;
+ int lifetime;
+ };
+
+ DumpData mDumpData[ MAX_COLLECTIBLES ];
+
+ bool mBumperCars;
+ unsigned int mNumSpawned;
+
+ int mDumpTimeout;
+ unsigned int mWhatToDump;
+ bool mAmIDumping;
+ Vehicle* mAssaultCar;
+ unsigned int mMeDumpTimout;
+ unsigned int mDumpLifetime;
+ bool mTerminalDump;
+
+ AnimatedIcon* mAnimatedIcon;
+
+ //Prevent wasteful constructor creation.
+ CollectDumpedObjective( const CollectDumpedObjective& collectdumpedobjective );
+ CollectDumpedObjective& operator=( const CollectDumpedObjective& collectdumpedobjective );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //COLLECTDUMPEDOBJECTIVE_H
diff --git a/game/code/mission/objectives/collectibleobjective.cpp b/game/code/mission/objectives/collectibleobjective.cpp
new file mode 100644
index 0000000..0d0b5d8
--- /dev/null
+++ b/game/code/mission/objectives/collectibleobjective.cpp
@@ -0,0 +1,824 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: CollectibleObjective.cpp
+//
+// Description: Implement CollectibleObjective
+//
+// History: 10/8/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/utility.hpp>
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/objectives/CollectibleObjective.h>
+
+#include <events/eventmanager.h>
+
+#include <mission/animatedicon.h>
+#include <mission/gameplaymanager.h>
+#include <mission/missionmanager.h>
+
+#include <render/dsg/inststatentitydsg.h>
+
+#include <memory/srrmemory.h>
+
+#include <meta/triggerlocator.h>
+#include <meta/eventlocator.h>
+#include <meta/actioneventlocator.h>
+#include <meta/spheretriggervolume.h>
+
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <ai/actor/intersectionlist.h>
+
+#include <roads/road.h>
+//#include <roads/roadmanager.h>
+
+#include <worldsim/avatarmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+#if defined( DEBUGWATCH ) || defined( RAD_WIN32 )
+float DEFAULT_DIST = 5.0f;
+#else
+const float DEFAULT_DIST = 5.0f;
+#endif
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// CollectibleObjective::CollectibleObjective
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CollectibleObjective::CollectibleObjective() :
+ mNumCollectibles( 0 ),
+ mNumCollected( 0 ),
+ mCollectEffect( NULL ),
+ mAllowUserDump( false ),
+ mCurrentFocus( MAX_COLLECTIBLES )
+{
+ mEffectName[0] = '\0';
+}
+
+//==============================================================================
+// CollectibleObjective::~CollectibleObjective
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CollectibleObjective::~CollectibleObjective()
+{
+ if ( mCollectEffect )
+ {
+ delete mCollectEffect;
+ }
+}
+
+//=============================================================================
+// CollectibleObjective::MoveCollectible
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int num, const rmt::Vector& newPos )
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::MoveCollectible( unsigned int num, const rmt::Vector& newPos )
+{
+ rAssert( num < mNumCollectibles );
+
+ mCollectibles[ num ].pLocator->SetLocation( newPos );
+
+ TriggerLocator* trigLoc = dynamic_cast<TriggerLocator*>(mCollectibles[ num ].pLocator);
+ rAssert( trigLoc );
+
+ unsigned int numTriggers = trigLoc->GetNumTriggers();
+ unsigned int i;
+ for ( i = 0; i < numTriggers; ++i )
+ {
+ //These locators are always surrounded by their volumes.
+ trigLoc->GetTriggerVolume( i )->SetPosition( newPos );
+ }
+
+ mCollectibles[ num ].mAnimatedIcon->Move( newPos );
+}
+
+//=============================================================================
+// CollectibleObjective::AddCollectibleLocatorName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char* locatorname, char* p3dname, float scale )
+// dialogName - number crunched from name of dialog event to trigger
+// when collectible is collected
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::AddCollectibleLocatorName( char* locatorname,
+ char* p3dname,
+ radKey32 dialogName,
+ tUID speakerName,
+ float scale )
+{
+ rAssert( mNumCollectibles < MAX_COLLECTIBLES );
+ strcpy( mCollectibles[ mNumCollectibles ].locatorName, locatorname );
+ strcpy( mCollectibles[ mNumCollectibles ].p3dname, p3dname );
+ mCollectibles[ mNumCollectibles ].fScaleFactor = scale;
+ mCollectibles[ mNumCollectibles ].mDialogName = dialogName;
+ mCollectibles[ mNumCollectibles ].mSpeakerName = speakerName;
+
+ mNumCollectibles++;
+}
+
+//=============================================================================
+// CollectibleObjective::SetCollectEffectName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char* name )
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::SetCollectEffectName( char* name )
+{
+ rAssert( name );
+ strcpy( mEffectName, name );
+}
+
+//=============================================================================
+// CollectibleObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ EventLocator* locator = static_cast<EventLocator*>( pEventData );
+
+ if( locator && locator->GetPlayerEntered() && CheckCollectibleLocators( locator ) )
+ {
+ if( mNumCollected == mNumCollectibles )
+ {
+ SetFinished( true );
+ }
+ }
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// CollectibleObjective::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::OnUpdate( unsigned int elapsedTime )
+{
+ OnUpdateCollectibleObjective( elapsedTime );
+
+ unsigned int i;
+ for ( i = 0; i < mNumCollectibles; ++i )
+ {
+ if ( !mCollectibles[ i ].bTriggered && mCollectibles[ i ].mAnimatedIcon )
+ {
+ mCollectibles[ i ].mAnimatedIcon->Update( elapsedTime );
+ }
+
+ if( mCollectibles[i].pLocator->GetFlag( Locator::ACTIVE ) )
+ {
+ UpdateLightPath(mCollectibles[i].mArrowPath);
+ }
+ }
+
+ if ( mCollectEffect )
+ {
+ mCollectEffect->Update( elapsedTime );
+ }
+
+
+}
+
+//=============================================================================
+// CollectibleObjective::OnInitalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::OnInitialize()
+{
+MEMTRACK_PUSH_GROUP( "Mission - Collectible Objective" );
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+
+ for( unsigned int i = 0; i < mNumCollectibles; i++ )
+ {
+ rAssert( mCollectibles[ i ].pLocator == NULL );
+
+ mCollectibles[ i ].pLocator = p3d::find<Locator>( mCollectibles[ i ].locatorName );
+
+#ifndef FINAL
+ if (mCollectibles[ i ].pLocator == NULL)
+ {
+ char errorbuffer[255];
+ sprintf(errorbuffer,"ERROR: Can't Find Locator %s, Make Sure you Loaded it!!!!\n", mCollectibles[ i ].locatorName);
+ rReleasePrintf(errorbuffer);
+ }
+
+#endif
+
+ rAssert( mCollectibles[ i ].pLocator != NULL );
+ mCollectibles[ i ].pLocator->AddRef();
+
+ // get the locator's position
+ rmt::Vector locPos;
+ mCollectibles[ i ].pLocator->GetLocation( &locPos );
+
+ //Try to make it an event locator
+ EventLocator* eventLocator = dynamic_cast<EventLocator*>( mCollectibles[ i ].pLocator );
+ ////////////////////////////////////////////////////////////////////////
+ if( strcmp( mCollectibles[ i ].p3dname, "" ) != 0 )
+ {
+ HeapMgr()->PushHeap(GetGameplayManager()->GetCurrentMissionHeap());
+ mCollectibles[ i ].mAnimatedIcon = new AnimatedIcon();
+ HeapMgr()->PopHeap(GetGameplayManager()->GetCurrentMissionHeap());
+
+ //If the locator *happens* to be an oriented (eventLocator) locator, we will
+ //pass in it's matrix.
+ if ( eventLocator != NULL )
+ {
+ rmt::Matrix mat = eventLocator->GetMatrix();
+ mat.FillTranslate( locPos );
+
+ mCollectibles[ i ].mAnimatedIcon->Init( mCollectibles[ i ].p3dname, mat, false );
+ }
+ else
+ {
+ mCollectibles[ i ].mAnimatedIcon->Init( mCollectibles[ i ].p3dname, locPos, false );
+ }
+ }
+
+ mCollectibles[ i ].bTriggered = false;
+
+ if( eventLocator != NULL )
+ {
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_LOCATOR + eventLocator->GetEventType()));
+
+ if ( !rmt::Epsilon( mCollectibles[ i ].fScaleFactor, 0.0f ) )
+ {
+ // Reinterpret cast does not do what you think it does.
+ // Use a dynamic cast to check the type
+ TriggerVolume* tv = eventLocator->GetTriggerVolume( 0 );
+ rTuneAssert( dynamic_cast< SphereTriggerVolume* >( tv ) != NULL );
+ SphereTriggerVolume* sphT = reinterpret_cast<SphereTriggerVolume*>( tv );
+ rAssertMsg( sphT, "Collectibles should only use sphere trigger volumes!" );
+
+ if ( sphT )
+ {
+ sphT->SetSphereRadius( mCollectibles[ i ].fScaleFactor );
+ }
+ }
+ eventLocator->SetFlag(Locator::ACTIVE, false);
+ }
+
+ ActionEventLocator* pActionEventLocator = dynamic_cast<ActionEventLocator*>( mCollectibles[ i ].pLocator );
+ if ( pActionEventLocator )
+ {
+ pActionEventLocator->Reset( );
+ }
+ }
+
+ GetEventManager()->AddListener( this, EVENT_OBJECT_DESTROYED );
+
+ //Setup the collectibles
+ OnInitCollectibles();
+
+ //Setup the objective
+ OnInitCollectibleObjective();
+
+ mNumCollected = 0;
+
+ //Set up the effect;
+ if ( mEffectName[0] != '\0' && strcmp( mEffectName, "none" ) != 0 )
+ {
+ mCollectEffect = new AnimatedIcon();
+ mCollectEffect->Init( mEffectName, rmt::Vector( 0.0f, 0.0f, 0.0f ), false, true );
+ }
+
+ MEMTRACK_POP_GROUP("Mission - Collectible Objective");
+}
+
+//=============================================================================
+// CollectibleObjective::GetCollectiblePathInfo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index, RoadManager::PathElement& elem, float& roadT )
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::GetCollectiblePathInfo( unsigned int index, RoadManager::PathElement& elem, float& roadT )
+{
+ rAssert( index < mNumCollectibles );
+
+ elem = mCollectibles[ index ].elem;
+ roadT = mCollectibles[ index ].roadT;
+}
+
+//=============================================================================
+// CollectibleObjective::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::OnFinalize()
+{
+ //Finalize the collectible objective
+ OnFinalizeCollectibleObjective();
+
+ unsigned int i;
+ for( i = 0; i < mNumCollectibles; i++ )
+ {
+ rAssert( mCollectibles[ i ].pLocator != NULL );
+ Activate( i, false, false );
+
+ mCollectibles[ i ].pLocator->Release();
+ mCollectibles[ i ].pLocator = NULL;
+
+ if( mCollectibles[ i ].mAnimatedIcon )
+ {
+ delete mCollectibles[ i ].mAnimatedIcon;
+ mCollectibles[ i ].mAnimatedIcon = NULL;
+ }
+ }
+
+ GetEventManager()->RemoveAll( this );
+
+ if ( mCollectEffect )
+ {
+ delete mCollectEffect;
+ mCollectEffect = NULL;
+ }
+}
+
+//=============================================================================
+// CollectibleObjective::CheckCollectibleLocators
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Locator* locator )
+//
+// Return: void
+//
+//=============================================================================
+bool CollectibleObjective::CheckCollectibleLocators( Locator* locator )
+{
+ bool found = false;
+ unsigned int i;
+ for( i = 0; i < mNumCollectibles; i++ )
+ {
+ if( locator == mCollectibles[ i ].pLocator )
+ {
+ bool shouldReset = false;
+ if ( OnCollection( i, shouldReset ) )
+ {
+ Collect( i, shouldReset );
+ found = true;
+ break;
+ }
+ }
+ }
+
+ return found;
+}
+
+//=============================================================================
+// CollectibleObjective::Collect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index, bool shouldReset )
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::Collect( unsigned int index, bool shouldReset )
+{
+ rAssert( index >= 0 && index < mNumCollectibles
+ && mCollectibles[ index ].pLocator != NULL );
+
+ if( !mCollectibles[ index ].bTriggered )
+ {
+ mNumCollected += 1;
+
+ mCollectibles[ index ].bTriggered = true;
+ Activate( index, false, false );
+
+ GetEventManager()->TriggerEvent( EVENT_MISSION_COLLECTIBLE_PICKED_UP );
+
+ if( mCollectibles[index].mDialogName != 0 )
+ {
+ DialogEventData data;
+
+ //
+ // Make with the funny talk
+ //
+ data.dialogName = mCollectibles[index].mDialogName;
+
+ if( mCollectibles[index].mSpeakerName != static_cast< tUID >( 0 ) )
+ {
+ data.charUID1 = mCollectibles[index].mSpeakerName;
+ }
+ else
+ {
+ //
+ // No character specified, use player character
+ //
+ data.char1 = GetAvatarManager()->GetAvatarForPlayer( 0 )->GetCharacter();
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_IN_GAMEPLAY_CONVERSATION, static_cast<void*>(&data) );
+ }
+
+ //I do this so that races that have multiple laps don't get screwed because
+ //of shared waypoints.
+ if ( shouldReset )
+ {
+ mCollectibles[ index ].pLocator->SetFlag( Locator::ACTIVE, true );
+ mCollectibles[ index ].bTriggered = false;
+ }
+ }
+
+ if ( mCollectEffect )
+ {
+ mCollectEffect->Reset();
+ rmt::Vector pos;
+ mCollectibles[ index ].pLocator->GetLocation( &pos );
+ mCollectEffect->Move( pos );
+ mCollectEffect->ShouldRender( true );
+ }
+ else
+ {
+ //Only show the effect if there is a drawable.
+ if ( strcmp( mCollectibles[ index ].p3dname, "" ) != 0 && strcmp( mEffectName, "none" ) != 0 )
+ {
+ //USe the default one from the mission manager
+ rmt::Vector pos;
+ mCollectibles[ index ].pLocator->GetLocation( &pos );
+ GetMissionManager()->PutEffectHere( pos );
+ }
+ }
+
+ //Disable the NAV system for this collectible.
+ UnlightPath( mCollectibles[index].mArrowPath.mPathRoute );
+}
+
+
+//=============================================================================
+// CollectibleObjective::Activate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index, bool bIsActive, bool primary, HudMapIcon::eIconType icon )
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::Activate( unsigned int index,
+ bool bIsActive,
+ bool primary,
+ HudMapIcon::eIconType icon,
+ bool render )
+{
+ rAssert( index < mNumCollectibles );
+
+ bool alreadyActive = mCollectibles[ index ].pLocator->GetFlag( Locator::ACTIVE );
+
+ mCollectibles[ index ].pLocator->SetFlag( Locator::ACTIVE, bIsActive );
+
+ if( bIsActive )
+ {
+ if( mCollectibles[ index ].mAnimatedIcon != NULL )
+ {
+ mCollectibles[ index ].mAnimatedIcon->ShouldRender( render );
+
+ if ( mCollectibles[ index ].iHUDIndex == -1 )
+ {
+ //Only put up an icon in the HUD if there is a drawable in the world.
+ RegisterLocator( mCollectibles[ index ].pLocator, mCollectibles[ index ].iHUDIndex, primary, icon );
+ }
+ }
+ }
+ else if( !bIsActive && alreadyActive)
+ {
+ if( mCollectibles[ index ].mAnimatedIcon != NULL )
+ {
+ mCollectibles[ index ].mAnimatedIcon->ShouldRender( false );
+
+ UnregisterLocator( mCollectibles[ index ].iHUDIndex );
+ }
+
+ //Just in case
+ UnlightPath( mCollectibles[index].mArrowPath.mPathRoute );
+ }
+}
+
+//=============================================================================
+// CollectibleObjective::SetFocus
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index )
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::SetFocus( unsigned int index )
+{
+ rAssert( index < mNumCollectibles );
+
+ CGuiScreenMultiHud* currentHud = GetCurrentHud();
+ if( currentHud )
+ {
+ currentHud->GetHudMap( 0 )->SetFocalPointIcon( mCollectibles[ index ].iHUDIndex );
+ }
+
+ rmt::Vector posn;
+ mCollectibles[index].pLocator->GetPosition(&posn);
+
+ if ( index == mCurrentFocus )
+ {
+ UnlightPath( mCollectibles[index].mArrowPath.mPathRoute );
+ }
+
+ LightPath( posn, mCollectibles[index].mArrowPath );
+ mCurrentFocus = index;
+}
+
+//=============================================================================
+// CollectibleObjective::ChangeIcon
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index, HudMapIcon::eIconType type )
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::ChangeIcon( unsigned int index, HudMapIcon::eIconType type )
+{
+ rAssert( index < mNumCollectibles );
+
+ CGuiScreenMultiHud* currentHud = GetCurrentHud();
+ if( currentHud )
+ {
+ mCollectibles[ index ].iHUDIndex = ChangeIconType( mCollectibles[ index ].iHUDIndex, type );
+ }
+}
+
+//=============================================================================
+// CollectibleObjective::ResetCollectibles
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::ResetCollectibles()
+{
+ unsigned int i;
+ for ( i = 0; i < mNumCollectibles; ++i )
+ {
+ mCollectibles[ i ].bTriggered = false;
+ mCollectibles[ i ].pLocator->SetFlag( Locator::ACTIVE, true );
+ UnlightPath( mCollectibles[ i ].mArrowPath.mPathRoute );
+ }
+
+ //I hate to do this, but it should only be this way for a very brief amount
+ //of time and no one should be able to query it and get the bad, bad value it represents.
+ mNumCollected = ~0;//hmmmm
+}
+
+//=============================================================================
+// CollectibleObjective::DumpCollectible
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int collectibleNum, Vehicle* dumper, Vehicle* hitter, bool useIntersectionList )
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::DumpCollectible( int collectibleNum, Vehicle* dumper, Vehicle* hitter, bool useIntersectionList, bool terminal )
+{
+
+ //If it is, drop the collectible on the ground where the car is.
+ //Also, set the focus on the collectible.
+ rmt::Vector newPos;
+ dumper->GetPosition( &newPos );
+
+ rmt::Vector dumperPos;
+ dumper->GetPosition( &dumperPos );
+
+ if(!terminal) // final dump always goes right where car is
+ {
+ if ( hitter )
+ {
+ rmt::Vector eNorm( 0.0f, 0.0f, 0.0f );
+
+ rmt::Vector hitterHeading;
+ hitter->GetHeading( &hitterHeading );
+
+ rmt::Vector dumpHeading;
+ dumper->GetHeading( &dumpHeading );
+
+
+
+ if ( rmt::Fabs( hitterHeading.DotProduct( dumpHeading ) ) > 0.5f )
+ {
+ rmt::Vector toHitter;
+ hitter->GetPosition( &toHitter);
+
+ toHitter.Sub(dumperPos);
+ toHitter.Normalize();
+
+ eNorm = dumper->mTransform.Row(0);
+
+
+ if( rmt::Fabs(dumpHeading.DotProduct(toHitter)) < 0.5)
+ {
+ if ( toHitter.DotProduct(eNorm) < 0 )
+ {
+ eNorm *= -1.0f;
+ }
+ }
+ else
+ {
+ if ( rand() % 2 == 1 )
+ {
+ eNorm *= -1.0f;
+ }
+ }
+
+ eNorm += dumpHeading;
+ eNorm.Normalize();
+ }
+ else
+ {
+ eNorm = hitterHeading;
+ }
+
+ eNorm *= DEFAULT_DIST;
+ newPos.Add( eNorm );
+
+ //Make sure this hits the ground
+ rmt::Vector vWaste;
+ bool found;
+ GetIntersectManager()->FindIntersection( newPos, found, vWaste, newPos );
+ if ( !found )
+ {
+ dumper->GetPosition( &newPos );
+ }
+ }
+
+ if ( useIntersectionList )
+ {
+ // A holds pointers to sim::CollisionObjects and provides an interface to querying
+ // for LineOfSight and getting find the closest intersection along a line segment
+ IntersectionList staticIntersectionList;
+ // Lets try and minimize the size of our query
+ // Center = ( start + end ) / 2
+ // radius = abs( ( start - end ) / 2 )
+ rmt::Vector queryCenter = ( newPos + dumperPos ) / 2.0f;
+ float queryRadius = ( newPos - dumperPos ).Magnitude() / 2.0f; // ugh
+ // Grab all the static objects (and fence pieces) in the given area
+ staticIntersectionList.FillIntersectionListStatics( queryCenter, queryRadius );
+ rmt::Vector intersection;
+ // Did we hit anything?
+ if ( staticIntersectionList.TestIntersectionStatics( dumperPos, newPos, &intersection ) )
+ {
+ // Hit something. Lets adjust newpos so that it is halfway between the dumper and the intersection
+ // Sound like a good compromise Cary?
+ newPos = ( queryCenter + intersection ) / 2.0f;
+ // Better do a ground intersection query in case we got some REALLY uneven terrain
+ rmt::Vector vWaste;
+ bool found;
+ GetIntersectManager()->FindIntersection( newPos, found, vWaste, newPos );
+ }
+ }
+ }
+
+ if ( rmt::Fabs( newPos.y - dumperPos.y ) > 5.0f )
+ {
+ newPos.y = dumperPos.y;
+ }
+
+ MoveCollectible( collectibleNum, newPos );
+ Activate( collectibleNum, true, false );
+}
+
+//=============================================================================
+// CollectibleObjective::GetAnyCollectedID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+int CollectibleObjective::GetAnyCollectedID() const
+{
+ unsigned int i;
+ for ( i = 0; i < mNumCollectibles; ++i )
+ {
+ if ( mCollectibles[ i ].bTriggered )
+ {
+ return static_cast<int>(i);
+ }
+ }
+
+ return -1;
+}
+
+//=============================================================================
+// CollectibleObjective::Uncollect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int collectibleNum )
+//
+// Return: void
+//
+//=============================================================================
+void CollectibleObjective::Uncollect( int collectibleNum )
+{
+ if ( collectibleNum > -1 && collectibleNum < static_cast<int>(mNumCollectibles) )
+ {
+ mCollectibles[ collectibleNum ].bTriggered = false;
+ mNumCollected--;
+
+ //Trigger an event
+ GetEventManager()->TriggerEvent( EVENT_LOSE_COLLECTIBLE );
+ }
+}
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/mission/objectives/collectibleobjective.h b/game/code/mission/objectives/collectibleobjective.h
new file mode 100644
index 0000000..6b0f558
--- /dev/null
+++ b/game/code/mission/objectives/collectibleobjective.h
@@ -0,0 +1,257 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: collectibleobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 10/8/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef COLLECTIBLEOBJECTIVE_H
+#define COLLECTIBLEOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+#include <mission/objectives/missionobjective.h>
+
+//========================================
+// Forward References
+//========================================
+class Locator;
+class AnimatedIcon;
+class Vehicle;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class CollectibleObjective : public MissionObjective
+{
+public:
+ CollectibleObjective();
+ virtual ~CollectibleObjective();
+
+ enum { MAX_COLLECTIBLES = 32 };
+
+ unsigned int GetNumCollectibles() const;
+ unsigned int GetNumCollected() const;
+
+ const Locator* GetCollectibleLocator( unsigned int num );
+ void MoveCollectible( unsigned int num, const rmt::Vector& newPos );
+
+ void AddCollectibleLocatorName( char* locatorname, char* p3dname, radKey32 dialogName, tUID speakerName, float scale );
+ void SetCollectEffectName( char* name );
+
+ void AllowUserDump();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+protected:
+ void OnUpdate( unsigned int elapsedTime );
+ virtual void OnInitialize();
+ void OnFinalize();
+
+ virtual void OnInitCollectibles() = 0;
+ virtual void OnInitCollectibleObjective() = 0;
+ virtual void OnFinalizeCollectibleObjective() = 0;
+ virtual bool OnCollection( unsigned int collectibleNum, bool &shouldReset ) = 0;
+ virtual void OnUpdateCollectibleObjective( unsigned int elapsedTimeMilliseconds ) {};
+
+ virtual bool CheckCollectibleLocators( Locator* locator );
+ void Activate( unsigned int index, bool bIsActive, bool primary, HudMapIcon::eIconType icon = HudMapIcon::ICON_COLLECTIBLE, bool render = true );
+ void Collect( unsigned int index, bool shouldReset );
+ bool IsCollected( unsigned int index );
+ void SetFocus( unsigned int index );
+ void ChangeIcon( unsigned int index, HudMapIcon::eIconType type );
+
+ void ResetCollectibles();
+
+ void DumpCollectible( int collectibleNum, Vehicle* dumper, Vehicle* hitter = NULL, bool useIntersectionList = true, bool terminal = false );
+
+ bool IsUserDumpAllowed() const;
+ int GetAnyCollectedID() const;
+
+ void Uncollect( int collectibleNum );
+
+ void GetCollectiblePathInfo( unsigned int index, RoadManager::PathElement& elem, float& roadT );
+
+ unsigned int mNumCollectibles;
+ unsigned int mNumCollected;
+
+ struct CollectibleLocatorData
+ {
+ CollectibleLocatorData() :
+ pLocator( NULL ),
+ mAnimatedIcon( NULL ),
+ iHUDIndex( -1 ),
+ bTriggered( false ),
+ fScaleFactor( 1.0f ),
+ seg( NULL ),
+ segT( 0.0f ),
+ roadT( 0.0f ),
+ mDialogName( 0 )
+ {
+ locatorName[0] = '\0';
+ p3dname[0]='\0';
+ mArrowPath.mPathRoute.Allocate( RoadManager::GetInstance()->GetNumRoads() );
+ elem.elem = NULL;
+ };
+
+ char locatorName[ 32 ];
+ char p3dname[ 32 ];
+
+ Locator* pLocator;
+ AnimatedIcon* mAnimatedIcon;
+
+ int iHUDIndex;
+
+ bool bTriggered;
+ float fScaleFactor;
+
+ PathStruct mArrowPath;
+
+ // the current path info upon which my locator is located
+ RoadManager::PathElement elem;
+ RoadSegment* seg;
+ float segT;
+ float roadT;
+
+ //
+ // Key for the name of the line of dialog we're going to play
+ // when this thing is collected, or 0 if unused.
+ //
+ radKey32 mDialogName;
+ tUID mSpeakerName;
+ };
+
+ CollectibleLocatorData mCollectibles[ MAX_COLLECTIBLES ];
+
+private:
+
+ char mEffectName[32];
+ AnimatedIcon* mCollectEffect;
+
+ bool mAllowUserDump;
+
+ unsigned int mCurrentFocus;
+
+
+ //Prevent wasteful constructor creation.
+ CollectibleObjective( const CollectibleObjective& collectibleobjective );
+ CollectibleObjective& operator=( const CollectibleObjective& collectibleobjective );
+};
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// CollectibleObjective::GetNumCollectibles
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned int
+//
+//=============================================================================
+inline unsigned int CollectibleObjective::GetNumCollectibles() const
+{
+ return mNumCollectibles;
+}
+
+//=============================================================================
+// CollectibleObjective::GetNumCollected
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int CollectibleObjective::GetNumCollected() const
+{
+ return mNumCollected;
+}
+
+//=============================================================================
+// CollectibleObjective::GetCollectibleLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int num )
+//
+// Return: inline
+//
+//=============================================================================
+inline const Locator* CollectibleObjective::GetCollectibleLocator( unsigned int num )
+{
+ rAssert( num < MAX_COLLECTIBLES );
+ return mCollectibles[ num ].pLocator;
+}
+
+//=============================================================================
+// CollectibleObjective::AllowUserDump
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void CollectibleObjective::AllowUserDump()
+{
+ mAllowUserDump = true;
+}
+
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// CollectibleObjective::IsCollected
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index )
+//
+// Return: bool
+//
+//=============================================================================
+inline bool CollectibleObjective::IsCollected( unsigned int index )
+{
+ rAssert( index < mNumCollectibles );
+
+ return mCollectibles[ index ].bTriggered;
+}
+
+//=============================================================================
+// CollectibleObjective::IsUserDumpAllowed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool CollectibleObjective::IsUserDumpAllowed() const
+{
+ return mAllowUserDump;
+}
+
+#endif //COLLECTIBLEOBJECTIVE_H
diff --git a/game/code/mission/objectives/deliveryobjective.cpp b/game/code/mission/objectives/deliveryobjective.cpp
new file mode 100644
index 0000000..82a52f9
--- /dev/null
+++ b/game/code/mission/objectives/deliveryobjective.cpp
@@ -0,0 +1,463 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: deliveryobjective.cpp
+//
+// Description: Implement DeliveryObjective
+//
+// History: 16/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/objectives/deliveryobjective.h>
+#include <mission/gameplaymanager.h>
+
+#include <events/eventmanager.h>
+#include <events/eventdata.h>
+
+#include <meta/eventlocator.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <render/DSG/StatePropDSG.h>
+#include <meta/actioneventlocator.h>
+#include <ai/actionbuttonhandler.h>
+#include <ai/actionbuttonmanager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+#ifdef DEBUGWATCH
+static unsigned int D_DEFAULT_TIMEOUT = 5000;
+#else
+static const unsigned int D_DEFAULT_TIMEOUT = 5000;
+#endif
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// DeliveryObjective::DeliveryObjective
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DeliveryObjective::DeliveryObjective() :
+ mDumpTimeout( 0 )
+{
+ // Allocate mStateProps array
+ mStateProps.Allocate( MAX_COLLECTIBLES );
+}
+
+//==============================================================================
+// DeliveryObjective::~DeliveryObjective
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DeliveryObjective::~DeliveryObjective()
+{
+ ReleaseAllStateProps();
+}
+
+//=============================================================================
+// DeliveryObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EvenEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void DeliveryObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ if ( id == EVENT_VEHICLE_VEHICLE_COLLISION && GetNumCollected() > 0 && mDumpTimeout == 0 )
+ {
+ CarOnCarCollisionEventData* data = static_cast<CarOnCarCollisionEventData*>(pEventData);
+ if ( data->vehicle != GetGameplayManager()->GetCurrentVehicle() && data->vehicle->mVehicleType == VT_AI )
+ {
+ rAssert( IsUserDumpAllowed() );
+
+ int dumpId = GetAnyCollectedID();
+ rAssert( dumpId != -1 );
+
+ DumpCollectible( dumpId, GetGameplayManager()->GetCurrentVehicle(), data->vehicle );
+ Uncollect( dumpId );
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud )
+ {
+ //Update the collection count
+ currentHud->SetCollectibles( GetNumCollected(), GetNumCollectibles() );
+ }
+
+ //Reset the dumo timeout.
+ mDumpTimeout = D_DEFAULT_TIMEOUT;
+ }
+ }
+
+ bool handledCollect = false;
+
+ if ( id == EVENT_OBJECT_DESTROYED )
+ {
+ // Is this a stateprop? If so, deactivate the hud icon
+ if ( pEventData )
+ {
+ Locator* locator = dynamic_cast< Locator* >( static_cast< tEntity* >( pEventData ) );
+ // Iterate through the collectible locators and see if we have a pointer match
+ for ( unsigned int i = 0 ; i < GetNumCollectibles() ; i++ )
+ {
+ if ( locator == GetCollectibleLocator( i ) )
+ {
+ // Match, disable hud icon then abort this loop
+ UnregisterLocator( mCollectibles[ i ].iHUDIndex );
+ bool shouldReset = false;
+ if ( OnCollection( i, shouldReset ) )
+ {
+ Collect( i, shouldReset );
+ }
+ if( mNumCollected == mNumCollectibles )
+ {
+ SetFinished( true );
+ }
+ handledCollect = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if ( handledCollect == false )
+ CollectibleObjective::HandleEvent( id, pEventData );
+}
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// DeliveryObjective::InitCollectibles
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DeliveryObjective::OnInitCollectibles()
+{
+ unsigned int i;
+ for( i = 0; i < GetNumCollectibles(); i++ )
+ {
+ Activate( i, true, false, HudMapIcon::ICON_COLLECTIBLE );
+ }
+
+ // Find all the stateprops (could be none)
+ FindStateProps();
+
+ SetStatePropHUDIconEnable( true );
+ SetButtonHandlersEnabled( false );
+ // Make the stateprops visible
+ SetStatePropState( 1 );
+
+ SetFocus( 0 );
+}
+
+//=============================================================================
+// DeliveryObjective::OnInitCollectibleObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DeliveryObjective::OnInitCollectibleObjective()
+{
+ if( mNumCollectibles > 1 ) // only show collectibles HUD overlay if more than 1 things to collect
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_COLLECTIBLES );
+ }
+
+ if ( GetCurrentHud() )
+ {
+ GetCurrentHud()->SetCollectibles( 0, GetNumCollectibles() );
+ }
+
+ if ( IsUserDumpAllowed() )
+ {
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_VEHICLE_COLLISION );
+ mDumpTimeout = 0;
+#ifdef DEBUGWATCH
+ char name[64];
+ sprintf( name, "Mission\\Objectives\\Delivery" );
+ radDbgWatchAddUnsignedInt( &D_DEFAULT_TIMEOUT, "Dump timeout", name, NULL, 0, 100000 );
+#endif
+ }
+}
+
+//=============================================================================
+// DeliveryObjective::OnFinalizeCollectibleObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DeliveryObjective::OnFinalizeCollectibleObjective()
+{
+ // Turn off the handlers
+ SetButtonHandlersEnabled( false );
+ // Lets iterate through the stateprops (if any exist)
+ // And revert them to state 0. In this state, they will be deactivated
+ // invisible, and uncollideable
+ SetStatePropState( 0 );
+ SetStatePropHUDIconEnable( false );
+
+ GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_COLLECTIBLES );
+
+
+ if ( IsUserDumpAllowed() )
+ {
+ GetEventManager()->RemoveListener( this, EVENT_VEHICLE_VEHICLE_COLLISION );
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &D_DEFAULT_TIMEOUT );
+#endif
+ }
+ ReleaseAllStateProps();
+}
+
+//=============================================================================
+// DeliveryObjective::OnCollection
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int collectibleNum, bool &shouldReset )
+//
+// Return: bool
+//
+//=============================================================================
+bool DeliveryObjective::OnCollection( unsigned int collectibleNum, bool &shouldReset )
+{
+ if ( GetCurrentHud() )
+ {
+ GetCurrentHud()->SetCollectibles( GetNumCollected() + 1, GetNumCollectibles() ); //I add 1 because the number is incremented after... Sorry
+ }
+
+ unsigned int i;
+ for ( i = 0; i < GetNumCollectibles(); ++i )
+ {
+ if ( !IsCollected( i ) && i != collectibleNum )
+ {
+ SetFocus( i );
+ break;
+ }
+ }
+
+ return true; //The order doesn't matter
+}
+
+//=============================================================================
+// DeliveryObjective::OnUpdateCollectibleObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void DeliveryObjective::OnUpdateCollectibleObjective( unsigned int elapsedTime )
+{
+ if ( IsUserDumpAllowed() )
+ {
+ if ( mDumpTimeout != 0 )
+ {
+ if ( elapsedTime > mDumpTimeout )
+ {
+ mDumpTimeout = 0;
+ }
+ else
+ {
+ mDumpTimeout -= elapsedTime;
+ }
+ }
+ }
+}
+
+
+//=============================================================================
+// DeliveryObjective::FindStateProps
+//=============================================================================
+// Description:
+// Attempts to find the stateprops (if any) associated with this objective
+//
+// Parameters: (none)
+//
+// Return: void
+//
+//=============================================================================
+void DeliveryObjective::FindStateProps()
+{
+ // Is the array already filled out
+ if ( mStateProps.mUseSize > 0 )
+ return;
+
+ for( unsigned int i = 0; i < GetNumCollectibles(); i++ )
+ {
+ const Locator* loc = GetCollectibleLocator( i );
+ // Check to see if the locator is an ActionEventLocator
+ const ActionEventLocator* actionEventLoc = dynamic_cast< const ActionEventLocator* >( loc );
+ if( actionEventLoc )
+ {
+ // Find the stateprop and set its state to 1 (active)
+ const char* objName = actionEventLoc->GetObjName();
+ StatePropDSG* pDSG = p3d::find< StatePropDSG >( objName );
+ if ( pDSG )
+ {
+ pDSG->AddRef();
+ mStateProps.Add( pDSG );
+ }
+ }
+ }
+}
+
+//=============================================================================
+// DeliveryObjective::ReleaseAllStateProps
+//=============================================================================
+// Description:
+// Iterates through mStateProps and decrements their refcounts
+//
+// Parameters:
+//
+// Return: void
+//
+//=============================================================================
+void DeliveryObjective::ReleaseAllStateProps()
+{
+ for ( int i = 0 ; i < mStateProps.mUseSize ; i++ )
+ {
+ if ( mStateProps[i] != NULL )
+ {
+ mStateProps[i]->Release();
+ mStateProps[i] = NULL;
+ }
+ }
+ mStateProps.ClearUse();
+}
+
+
+//=============================================================================
+// DeliveryObjective::SetButtonHandlersEnabled
+//=============================================================================
+// Description:
+// This iterates through the collectibles and turns their associated
+// ActionEventHandlers enabled flag on or off
+// This is used to have the HUD icon only visible when the mission is in play
+//
+// Parameters: (bool enable)
+//
+// Return: void
+//
+//=============================================================================
+void DeliveryObjective::SetButtonHandlersEnabled( bool enable )
+{
+ for( unsigned int i = 0; i < GetNumCollectibles(); i++ )
+ {
+ const Locator* loc = GetCollectibleLocator( i );
+ // Check to see if the locator is an ActionEventLocator
+ const ActionEventLocator* actionEventLoc = dynamic_cast< const ActionEventLocator* >( loc );
+ if( actionEventLoc )
+ {
+ ActionButton::ActionEventHandler* handler = GetActionButtonManager()->FindHandler( actionEventLoc );
+ if ( handler )
+ {
+ handler->SetInstanceEnabled( enable );
+ }
+ }
+ }
+}
+
+//=============================================================================
+// DeliveryObjective::SetStatePropState
+//=============================================================================
+// Description: Iterates through the collectibles, any stateprops it finds associated
+// with the collectible locators gets set to the given
+// state. This Allows the stateprops to appear only when the mission is active
+// Note that Aryan set it up so that state 0 is disabled (invisible)
+// And state 1+ is enabled (visible)
+//
+// Parameters: (int state)
+//
+// Return: void
+//
+//=============================================================================
+void DeliveryObjective::SetStatePropState( int state )
+{
+ for ( int i = 0 ; i < mStateProps.mUseSize ; i++ )
+ {
+ mStateProps[i]->SetState( state );
+ }
+}
+
+//=============================================================================
+// DeliveryObjective::SetStatePropHUDIconEnable
+//=============================================================================
+// Description: Iterates through the stateprops (if any) and enables/disables HUD icons
+//
+// Parameters: (bool enable)
+//
+// Return: void
+//
+//=============================================================================
+// Iterates through the stateprops (if any) and enables/disables HUD icons
+void DeliveryObjective:: SetStatePropHUDIconEnable( bool enable )
+{
+ // Dont do anything if this is not a stateprop mission
+ if ( mStateProps.mUseSize == 0 )
+ return;
+
+ const bool primary = true;
+ for( unsigned int i = 0; i < GetNumCollectibles(); i++ )
+ {
+ if ( enable )
+ RegisterLocator( mCollectibles[ i ].pLocator, mCollectibles[ i ].iHUDIndex, primary, HudMapIcon::ICON_COLLECTIBLE );
+ else
+ UnregisterLocator( mCollectibles[ i ].iHUDIndex );
+ }
+}
diff --git a/game/code/mission/objectives/deliveryobjective.h b/game/code/mission/objectives/deliveryobjective.h
new file mode 100644
index 0000000..b238d7c
--- /dev/null
+++ b/game/code/mission/objectives/deliveryobjective.h
@@ -0,0 +1,78 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: deliveryobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef DELIVERYOBJECTIVE_H
+#define DELIVERYOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/objectives/collectibleobjective.h>
+#include <render/culling/swaparray.h>
+
+//========================================
+// Forward References
+//========================================
+
+class StatePropDSG;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class DeliveryObjective : public CollectibleObjective
+{
+public:
+ DeliveryObjective();
+ virtual ~DeliveryObjective();
+
+ //From EventListener
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+protected:
+ virtual void OnInitCollectibles();
+ virtual void OnInitCollectibleObjective();
+ virtual void OnFinalizeCollectibleObjective();
+ virtual bool OnCollection( unsigned int collectibleNum, bool &shouldReset );
+ virtual void OnUpdateCollectibleObjective( unsigned int elapsedTime );
+
+ void FindStateProps();
+ // Iterates through the mStateProp array and decrements their refcounts
+ // Kills mStateProp
+ void ReleaseAllStateProps();
+
+ // This iterates through the collectibles and turns their associated
+ // ActionEventHandlers enabled flag on or off
+ // This is used to have the HUD icon only visible when the mission is in play
+ void SetButtonHandlersEnabled( bool enable );
+
+ // Iterates through the collectibles, any stateprops it finds associated
+ // with the collectible locators gets set to the given
+ // state. This Allows the stateprops to appear only when the mission is active
+ // Note that Aryan set it up so that state 0 is disabled (invisible)
+ // And state 1+ is enabled (visible)
+ void SetStatePropState( int state );
+
+ // Iterates through the stateprops (if any) and enables/disables HUD icons
+ void SetStatePropHUDIconEnable( bool enable );
+
+ SwapArray< StatePropDSG* > mStateProps;
+
+private:
+ unsigned int mDumpTimeout;
+};
+
+
+
+#endif //DELIVERYOBJECTIVE_H
diff --git a/game/code/mission/objectives/destroybossobjective.cpp b/game/code/mission/objectives/destroybossobjective.cpp
new file mode 100644
index 0000000..fb2342a
--- /dev/null
+++ b/game/code/mission/objectives/destroybossobjective.cpp
@@ -0,0 +1,105 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: destroybossobjective
+//
+// Description: destroybossobjective
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <mission/objectives/destroybossobjective.h>
+#include <ai/actor/actor.h>
+#include <ai/actor/actormanager.h>
+#include <stateprop/statepropdata.hpp>
+#include <mission/gameplaymanager.h>
+#include <atc/atcmanager.h>
+#include <mission/statepropcollectible.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <render/rendermanager/rendermanager.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+DestroyBossObjective::DestroyBossObjective()
+{
+
+}
+
+DestroyBossObjective::~DestroyBossObjective()
+{
+
+}
+
+void
+DestroyBossObjective::SetTarget( Actor* actor )
+{
+ m_Boss = actor;
+ // Don't let the actor despawn if it goes out of range of the avatar
+ rTuneAssertMsg( actor != NULL, "DestroyBossObjective: can't find object in memory!" );
+ if( m_Boss != NULL )
+ {
+ m_Boss->SetShouldDespawn( false );
+ }
+}
+
+
+void
+DestroyBossObjective::OnInitialize()
+{
+ // Register an event listener so that we will know if the ufo has eaten the
+ // player's vehicle. This is failure
+ GetEventManager()->AddListener( this, EVENT_BOSS_DESTROYED_PLAYER_CAR );
+ // Event indicating boss has been damaged. This is success
+ GetEventManager()->AddListener( this, EVENT_BOSS_DAMAGED );
+}
+
+void
+DestroyBossObjective::OnFinalize()
+{
+ // We are done with these listeners.
+ GetEventManager()->RemoveListener( this, EVENT_BOSS_DESTROYED_PLAYER_CAR );
+ GetEventManager()->RemoveListener( this, EVENT_BOSS_DAMAGED );
+}
+
+void
+DestroyBossObjective::OnUpdate( unsigned int elapsedTime )
+{
+
+}
+
+void
+DestroyBossObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_BOSS_DESTROYED_PLAYER_CAR:
+ // Boss has destroyed the players can, register failure
+ SetFinished( true );
+ break;
+ case EVENT_BOSS_DAMAGED:
+ // Boss has been damaged by the player. This stage has been completed successfully
+ SetFinished( true );
+ break;
+
+ default:
+ rAssert(0);
+ break;
+ }
+}
diff --git a/game/code/mission/objectives/destroybossobjective.h b/game/code/mission/objectives/destroybossobjective.h
new file mode 100644
index 0000000..617adf2
--- /dev/null
+++ b/game/code/mission/objectives/destroybossobjective.h
@@ -0,0 +1,59 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: destroyobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef DESTROYBOSSOBJECTIVE_H
+#define DESTROYBOSSOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/objectives/missionobjective.h>
+#include <render/culling/swaparray.h>
+
+//========================================
+// Forward References
+//========================================
+
+class Actor;
+class StatePropCollectible;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class DestroyBossObjective : public MissionObjective
+{
+public:
+ DestroyBossObjective();
+ virtual ~DestroyBossObjective();
+
+ void SetTarget( Actor* actor );
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ Actor* m_Boss;
+
+private:
+
+ //Prevent wasteful constructor creation.
+ DestroyBossObjective( const DestroyBossObjective& objective );
+ DestroyBossObjective& operator=( const DestroyBossObjective& objective );
+};
+
+#endif //DESTROYOBJECTIVE_H
diff --git a/game/code/mission/objectives/destroyobjective.cpp b/game/code/mission/objectives/destroyobjective.cpp
new file mode 100644
index 0000000..a664f9e
--- /dev/null
+++ b/game/code/mission/objectives/destroyobjective.cpp
@@ -0,0 +1,252 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: destroyobjective.cpp
+//
+// Description: Implement DestroyObjective
+//
+// History: 10/06/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <mission/objectives/destroyobjective.h>
+#include <mission/animatedicon.h>
+#include <mission/gameplaymanager.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#include <events/eventmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/hitnrunmanager.h>
+
+#include <memory/srrmemory.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// DestroyObjective::DestroyObjective
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DestroyObjective::DestroyObjective() :
+ mDestroyVehicle( NULL ),
+ mAnimatedIcon( NULL )
+{
+
+}
+
+//==============================================================================
+// DestroyObjective::~DestroyObjective
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DestroyObjective::~DestroyObjective()
+{
+
+}
+
+//=============================================================================
+// DestroyObjective::OnInitalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DestroyObjective::OnInitialize()
+{
+MEMTRACK_PUSH_GROUP( "Mission - DestroyObjective" );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DAMAGED );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+
+ // show damage meter
+ GetGuiSystem()->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_DAMAGE_METER );
+
+ //Set this vehicle as the focus of the HUD.
+ rAssert( mDestroyVehicle );
+ VehicleAI* vehicleAI = GetVehicleCentral()->GetVehicleAI( mDestroyVehicle );
+ rAssert( vehicleAI );
+
+ GetHitnRunManager()->RegisterVehicleImmunity( mDestroyVehicle );
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->GetHudMap( 0 )->SetFocalPointIcon( vehicleAI->GetHUDIndex() );
+
+ // update damage meter
+ currentHud->SetDamageMeter( 0.0f ); // between 0.0 and 1.0
+ }
+
+ //========================= SET UP THE ICON
+
+ rAssert( mAnimatedIcon == NULL );
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mAnimatedIcon = new(gma) AnimatedIcon();
+
+ const rmt::Vector pos = mDestroyVehicle->GetPosition();
+ //TODO put in the actual name...
+ mAnimatedIcon->Init( ARROW_DESTROY, pos );
+ mAnimatedIcon->ScaleByCameraDistance( MIN_ARROW_SCALE, MAX_ARROW_SCALE, MIN_ARROW_SCALE_DIST, MAX_ARROW_SCALE_DIST );
+MEMTRACK_POP_GROUP("Mission - DestroyObjective");
+}
+
+//=============================================================================
+// DestroyObjective::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DestroyObjective::OnFinalize()
+{
+ // hide damage meter
+ GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_DAMAGE_METER );
+
+ GetEventManager()->RemoveListener( this, EVENT_VEHICLE_DAMAGED );
+ GetEventManager()->RemoveListener( this, EVENT_VEHICLE_DESTROYED );
+
+ GetHitnRunManager()->RegisterVehicleImmunity( NULL );
+
+ rAssert( mAnimatedIcon );
+
+ if ( mAnimatedIcon )
+ {
+ delete mAnimatedIcon;
+ mAnimatedIcon = NULL;
+ }
+}
+
+//=============================================================================
+// DestroyObjective::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void DestroyObjective::OnUpdate( unsigned int elapsedTime )
+{
+ //Update the position of the bv...
+ rmt::Vector carPos;
+ mDestroyVehicle->GetPosition( &carPos );
+
+ rmt::Box3D bbox;
+ mDestroyVehicle->GetBoundingBox( &bbox );
+ carPos.y = bbox.high.y;
+
+ mAnimatedIcon->Move( carPos );
+ mAnimatedIcon->Update( elapsedTime );
+}
+
+//=============================================================================
+// DestroyObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void DestroyObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_VEHICLE_DAMAGED:
+ {
+ if( pEventData == (void*)mDestroyVehicle )
+ {
+ //Update the HUD.
+ // update damage meter
+ if ( GetCurrentHud() )
+ {
+ GetCurrentHud()->SetDamageMeter( 1.0f - mDestroyVehicle->GetVehicleLifePercentage(mDestroyVehicle->mHitPoints) ); // between 0.0 and 1.0
+ }
+ }
+ break;
+ }
+ case EVENT_VEHICLE_DESTROYED:
+ {
+ if( pEventData == (void*)mDestroyVehicle )
+ {
+ // update damage meter
+ if ( GetCurrentHud() )
+ {
+ GetCurrentHud()->SetDamageMeter( 1.0f ); // between 0.0 and 1.0
+ }
+
+ SetFinished( true );
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// DestroyObjective::SetTargetVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* pVehicle )
+//
+// Return: void
+//
+//=============================================================================
+void DestroyObjective::SetTargetVehicle( Vehicle* pVehicle )
+{
+ mDestroyVehicle = pVehicle;
+ //mDestroyVehicle->SetVehicleCanSustainDamage( true );
+ mDestroyVehicle->VehicleIsADestroyObjective( true );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/mission/objectives/destroyobjective.h b/game/code/mission/objectives/destroyobjective.h
new file mode 100644
index 0000000..ed437a0
--- /dev/null
+++ b/game/code/mission/objectives/destroyobjective.h
@@ -0,0 +1,60 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: destroyobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef DESTROYOBJECTIVE_H
+#define DESTROYOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/objectives/missionobjective.h>
+
+//========================================
+// Forward References
+//========================================
+
+class Vehicle;
+class AnimatedIcon;
+
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class DestroyObjective : public MissionObjective
+{
+public:
+ DestroyObjective();
+ virtual ~DestroyObjective();
+
+ Vehicle* GetTargetVehicle() { return( mDestroyVehicle ); }
+ void SetTargetVehicle(Vehicle* pVehicle);
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+private:
+ Vehicle* mDestroyVehicle;
+ AnimatedIcon* mAnimatedIcon;
+
+ //Prevent wasteful constructor creation.
+ DestroyObjective( const DestroyObjective& objective );
+ DestroyObjective& operator=( const DestroyObjective& objective );
+};
+
+#endif //DESTROYOBJECTIVE_H
diff --git a/game/code/mission/objectives/dialogueobjective.cpp b/game/code/mission/objectives/dialogueobjective.cpp
new file mode 100644
index 0000000..488d954
--- /dev/null
+++ b/game/code/mission/objectives/dialogueobjective.cpp
@@ -0,0 +1,512 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: DialogueObjective.cpp
+//
+// Description: Implement DialogueObjective
+//
+// History: 03/09/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <string.h>
+#include <p3d/refcounted.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/objectives/DialogueObjective.h>
+#include <mission/gameplaymanager.h>
+
+#include <events/eventmanager.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+
+#include <meta/carstartlocator.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/vehiclecentral.h>
+
+
+//MS11 Hack
+#include <gameflow/gameflow.h>
+#include <contexts/context.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// DialogueObjective::DialogueObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+DialogueObjective::DialogueObjective():
+ mDialogueName( 0 ),
+ mCameraDistance( 3.0f ),
+ mChar1Pos( NULL ),
+ mChar2Pos( NULL ),
+ mCarPos( NULL ),
+ mChar1Rotation( 0.0f ),
+ mChar2Rotation( 0.0f ),
+ mCarRotation( 0.0f ),
+ mReset( false ),
+ mMoved( false ),
+ mCharacter1WasInCarToStartWith( false ),
+ mCharacter2WasInCarToStartWith( false ),
+ mHidTheCar( false ),
+ mHidDefault( false ),
+ mDontReset( false )
+{
+ mDialogEventData.char1 = NULL;
+ mDialogEventData.char2 = NULL;
+ mChar1Name[0] = '\0';
+ mChar2Name[0] = '\0';
+
+ mChar1OldPos.Set( 0.0f, 0.0f, 0.0f );
+ mChar2OldPos.Set( 0.0f, 0.0f, 0.0f );
+ mCarOldPos.Set( 0.0f, 0.0f, 0.0f );
+}
+
+//=============================================================================
+// DialogueObjective::~DialogueObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+DialogueObjective::~DialogueObjective()
+{
+ if( mDialogEventData.char1 != NULL )
+ {
+ mDialogEventData.char1->Release();
+ mDialogEventData.char1 = NULL;
+ }
+ if( mDialogEventData.char2 != NULL )
+ {
+ mDialogEventData.char2->Release();
+ mDialogEventData.char2 = NULL;
+ }
+
+ if ( mChar1Pos )
+ {
+ mChar1Pos->Release();
+ mChar1Pos = NULL;
+ }
+
+ if ( mChar2Pos )
+ {
+ mChar2Pos->Release();
+ mChar2Pos = NULL;
+ }
+
+ if ( mCarPos )
+ {
+ mCarPos->Release();
+ mCarPos = NULL;
+ }
+}
+
+//=============================================================================
+// DialogueObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void DialogueObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ rAssert( id == EVENT_CONVERSATION_DONE_AND_FINISHED );
+
+ //Wait for the dialogue system to announce that the dialogue is finished
+ //playing.
+ SetFinished( true );
+}
+
+//=============================================================================
+// DialogueObjective::SetCameraDistance
+//=============================================================================
+// Description: Sets the distance that the camera will use when the dialog
+// is played back
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void DialogueObjective::SetCameraDistance( const float distance )
+{
+ mCameraDistance = distance;
+}
+
+//=============================================================================
+// DialogueObjective::SetPositions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( CarStartLocator* pos1, CarStartLocator* pos2, CarStartLocator* carPos, bool dontReset )
+//
+// Return: void
+//
+//=============================================================================
+void DialogueObjective::SetPositions( CarStartLocator* pos1, CarStartLocator* pos2, CarStartLocator* carPos, bool dontReset )
+{
+ tRefCounted::Assign( mChar1Pos, pos1 );
+ tRefCounted::Assign( mChar2Pos, pos2 );
+ tRefCounted::Assign( mCarPos, carPos );
+
+ mDontReset = dontReset;
+}
+
+//=============================================================================
+// DialogueObjective::CharactersReset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool DialogueObjective::CharactersReset()
+{
+ return mMoved;
+}
+
+
+//=============================================================================
+// DialogueObjective::SetChar1Name
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void DialogueObjective::SetChar1Name( const char* name )
+{
+ unsigned int len = strlen(name) < MAX_CHAR_NAME_LEN - 1 ? strlen(name) : MAX_CHAR_NAME_LEN - 2;
+ strncpy( mChar1Name, name, len );
+ mChar1Name[len] = '\0';
+}
+
+//=============================================================================
+// DialogueObjective::SetChar2Name
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void DialogueObjective::SetChar2Name( const char* name )
+{
+ unsigned int len = strlen(name) < MAX_CHAR_NAME_LEN - 1 ? strlen(name) : MAX_CHAR_NAME_LEN - 2;
+ strncpy( mChar2Name, name, len );
+ mChar2Name[len] = '\0';
+}
+
+//=============================================================================
+// DialogueObjective::SetDialogueName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void DialogueObjective::SetDialogueName( const char* name )
+{
+ mDialogueName = ::radMakeKey32( name );
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// DialogueObjective::OnInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DialogueObjective::OnInitialize()
+{
+ rAssert( strlen(mChar1Name) != 0 );
+ rAssert( strlen(mChar2Name) != 0 );
+
+ //Register with the event system that you are listening for the dialogue end
+ //event.
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_DONE_AND_FINISHED );
+
+ //Send the alert to the dialogue system to start playing this bad-ass
+ Character* c1 = GetCharacterManager()->GetMissionCharacter( mChar1Name );
+ tRefCounted::Assign( mDialogEventData.char1, c1 );
+ TrafficManager::GetInstance()->AddCharacterToStopFor( c1 );
+
+ Character* c2 = GetCharacterManager()->GetMissionCharacter( mChar2Name );
+ tRefCounted::Assign( mDialogEventData.char2, c2 );
+ TrafficManager::GetInstance()->AddCharacterToStopFor( c2 );
+
+
+ //POSITIONS, PLEASE!
+ mCharacter1WasInCarToStartWith = c1->IsInCar();
+ c1->GetPosition( &mChar1OldPos );
+ mChar1Rotation = c1->GetFacingDir();
+
+ rmt::Vector location;
+ if ( mChar1Pos )
+ {
+ mChar1Pos->GetLocation( &location );
+ c1->RelocateAndReset( location, mChar1Pos->GetRotation() );
+ }
+
+
+ mCharacter2WasInCarToStartWith = c2->IsInCar();
+ c2->GetPosition( &mChar2OldPos );
+ mChar2Rotation = c2->GetFacingDir();
+
+ if ( mChar2Pos )
+ {
+ mChar2Pos->GetLocation( &location );
+ c2->RelocateAndReset( location, mChar2Pos->GetRotation() );
+ }
+
+ Vehicle* v = GetGameplayManager()->GetCurrentVehicle();
+ v->GetPosition( &mCarOldPos );
+ mCarRotation = v->GetFacingInRadians();
+
+ bool needToHide = false;
+
+ float dist = 0.0f;
+
+ if ( mChar1Pos )
+ {
+ mChar1Pos->GetLocation( &location );
+ rmt::Vector c1ToCar;
+ c1ToCar.Sub( mCarOldPos, location );
+ if ( c1ToCar.MagnitudeSqr() < 49.0f )
+ {
+ needToHide = true;
+ }
+ }
+
+ if ( !needToHide && mChar2Pos )
+ {
+ mChar2Pos->GetLocation( &location );
+ rmt::Vector c2ToCar;
+ c2ToCar.Sub( mCarOldPos, location );
+ if ( c2ToCar.MagnitudeSqr() < 49.0f )
+ {
+ needToHide = true;
+ }
+ }
+
+ if ( needToHide )
+ {
+ //hide this car
+ v->CarDisplay( false );
+ GetVehicleCentral()->RemoveVehicleFromActiveList( v );
+ mHidTheCar = true;
+ }
+
+ //Should also hide the default car if it is not your current vehicle.
+ Vehicle* defaultCar = GetGameplayManager()->GetVehicleInSlot( GameplayManager::eDefaultCar );
+ if ( defaultCar && defaultCar != v )
+ {
+ bool needToHideDefault = false;
+
+ rmt::Vector defaultPos;
+ defaultCar->GetPosition( &defaultPos );
+
+ if ( mChar1Pos )
+ {
+ mChar1Pos->GetLocation( &location );
+ rmt::Vector c1ToCar;
+ c1ToCar.Sub( defaultPos, location );
+ if ( c1ToCar.MagnitudeSqr() < 49.0f )
+ {
+ needToHideDefault = true;
+ }
+ }
+
+ if ( !needToHideDefault && mChar2Pos )
+ {
+ mChar2Pos->GetLocation( &location );
+ rmt::Vector c2ToCar;
+ c2ToCar.Sub( defaultPos, location );
+ if ( c2ToCar.MagnitudeSqr() < 49.0f )
+ {
+ needToHideDefault = true;
+ }
+ }
+
+ if ( needToHideDefault )
+ {
+ //hide default
+ defaultCar->CarDisplay( false );
+ GetVehicleCentral()->RemoveVehicleFromActiveList( defaultCar );
+
+ mHidDefault = true;
+ }
+ }
+
+ if ( mChar1Pos || mChar2Pos )
+ {
+ GetSuperCamManager()->GetSCC( 0 )->DoCameraCut();
+
+ mChar1Pos->GetLocation( &location );
+ TrafficManager::GetInstance()->ClearTrafficInSphere( rmt::Sphere( location, 50.0f ) );
+
+ mMoved = true;
+ }
+
+ mDialogEventData.dialogName = mDialogueName;
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_INIT, (void*)(&mDialogEventData) );
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_INIT_DIALOG, (void*)(&mDialogEventData) );
+
+ //Maybe we can move the start event into the update for this objective....
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_START, (void*)(NULL) );
+
+ //MS11 Hack
+ //Something else should be responsible for suspending us.
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Suspend();
+
+ mReset = false;
+}
+
+//=============================================================================
+// DialogueObjective::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DialogueObjective::OnFinalize()
+{
+ ResetCharacterPositions();
+
+ if( mDialogEventData.char1 != NULL )
+ {
+ TrafficManager::GetInstance()->RemoveCharacterToStopFor( mDialogEventData.char1 );
+ mDialogEventData.char1->Release();
+ mDialogEventData.char1 = NULL;
+ }
+
+ if( mDialogEventData.char2 != NULL )
+ {
+ TrafficManager::GetInstance()->RemoveCharacterToStopFor( mDialogEventData.char2 );
+ mDialogEventData.char2->Release();
+ mDialogEventData.char2 = NULL;
+ }
+
+ GetEventManager()->RemoveListener( this, EVENT_CONVERSATION_DONE_AND_FINISHED );
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// DialogueObjective::ResetCharacterPositions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DialogueObjective::ResetCharacterPositions()
+{
+ if ( mReset || !mMoved )
+ {
+ return;
+ }
+
+ if( !mCharacter1WasInCarToStartWith )
+ {
+ if ( mChar1Pos && mDialogEventData.char1 && !mDontReset )
+ {
+ mDialogEventData.char1->RelocateAndReset( mChar1OldPos, mChar1Rotation );
+ }
+ }
+
+ if( !mCharacter2WasInCarToStartWith )
+ {
+ if ( mChar2Pos && mDialogEventData.char2 && !mDontReset )
+ {
+ mDialogEventData.char2->RelocateAndReset( mChar2OldPos, mChar2Rotation );
+ }
+ }
+
+ if ( mHidTheCar )
+ {
+ Vehicle* v = GetGameplayManager()->GetCurrentVehicle();
+
+ v->CarDisplay( true );
+ GetVehicleCentral()->AddVehicleToActiveList( v );
+ mHidTheCar = false;
+ }
+
+ if ( mHidDefault )
+ {
+ Vehicle* defaultCar = GetGameplayManager()->GetVehicleInSlot( GameplayManager::eDefaultCar );
+ defaultCar->CarDisplay( true );
+ GetVehicleCentral()->AddVehicleToActiveList( defaultCar );
+
+ mHidDefault = false;
+ }
+
+ mReset = true;
+ mMoved = false;
+
+ if ( mChar1Pos || mChar2Pos )
+ {
+ GetSuperCamManager()->GetSCC( 0 )->DoCameraCut();
+ GetSuperCamManager()->GetSCC( 0 )->Update( 16 );
+ }
+}
diff --git a/game/code/mission/objectives/dialogueobjective.h b/game/code/mission/objectives/dialogueobjective.h
new file mode 100644
index 0000000..9a4663b
--- /dev/null
+++ b/game/code/mission/objectives/dialogueobjective.h
@@ -0,0 +1,94 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogueobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 03/09/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef DIALOGUEOBJECTIVE_H
+#define DIALOGUEOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/objectives/missionobjective.h>
+#include <events/eventdata.h>
+
+#include <radmath/radmath.hpp>
+
+//========================================
+// Forward References
+//========================================
+class CarStartLocator;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class DialogueObjective : public MissionObjective
+{
+public:
+ enum { MAX_CHAR_NAME_LEN = 30 };
+
+ DialogueObjective();
+ virtual ~DialogueObjective();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void SetChar1Name( const char* name );
+ void SetChar2Name( const char* name );
+ void SetDialogueName( const char* name );
+ void SetCameraDistance( const float distance );
+
+ void SetPositions( CarStartLocator* pos1, CarStartLocator* pos2, CarStartLocator* carPos, bool dontReset );
+
+ bool CharactersReset();
+
+ void DontReset() { mDontReset = true; };
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+
+private:
+ char mChar1Name[MAX_CHAR_NAME_LEN];
+ char mChar2Name[MAX_CHAR_NAME_LEN];
+ radKey32 mDialogueName;
+ float mCameraDistance;
+
+ DialogEventData mDialogEventData;
+
+ CarStartLocator* mChar1Pos;
+ CarStartLocator* mChar2Pos;
+ CarStartLocator* mCarPos;
+
+ rmt::Vector mChar1OldPos;
+ rmt::Vector mChar2OldPos;
+ rmt::Vector mCarOldPos;
+ float mChar1Rotation;
+ float mChar2Rotation;
+ float mCarRotation;
+
+ bool mReset : 1;
+ bool mMoved : 1;
+ bool mCharacter1WasInCarToStartWith : 1;
+ bool mCharacter2WasInCarToStartWith : 1;
+ bool mHidTheCar : 1;
+ bool mHidDefault : 1;
+ bool mDontReset : 1; //This is for mission complete.
+
+ void ResetCharacterPositions();
+
+ //Prevent wasteful constructor creation.
+ DialogueObjective( const DialogueObjective& dialogueobjective );
+ DialogueObjective& operator=( const DialogueObjective& dialogueobjective );
+};
+
+
+#endif //DIALOGUEOBJECTIVE_H
diff --git a/game/code/mission/objectives/fmvobjective.cpp b/game/code/mission/objectives/fmvobjective.cpp
new file mode 100644
index 0000000..bcc9988
--- /dev/null
+++ b/game/code/mission/objectives/fmvobjective.cpp
@@ -0,0 +1,107 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: FMVObjective.cpp
+//
+// Description: Implement FMVObjective
+//
+// History: 4 Feb 2003 + Created -- James Harrison
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/objectives/FMVObjective.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/gameplaymanager.h>
+
+#include <presentation/presentation.h>
+
+#include <input/inputmanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// DialogueObjective::DialogueObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+FMVObjective::FMVObjective() :
+ mMusicStop( false )
+{
+ mFileName[ 0 ] = 0;
+}
+
+//=============================================================================
+// DialogueObjective::~DialogueObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+FMVObjective::~FMVObjective()
+{
+}
+
+/*=============================================================================
+Description: Callback when the FMV is finished. We can mark this objective
+ as complete.
+=============================================================================*/
+void FMVObjective::OnPresentationEventEnd( PresentationEvent* pEvent )
+{
+ SetFinished( true );
+
+ //Chuck: After we watch the FMV record it on the charactersheet.
+ GetCharacterSheetManager()->SetFMVUnlocked( GetGameplayManager()->GetCurrentLevelIndex() ) ;
+
+ GetInputManager()->SetGameState( Input::ACTIVE_ALL );
+}
+
+/*=============================================================================
+Description: Start the FMV playing. We'll get an OnPresentationEventEnd call
+ back when it's finished.
+=============================================================================*/
+void FMVObjective::OnInitialize()
+{
+ if( mFileName[ 0 ] != 0 )
+ {
+ bool isSkippable = GetCharacterSheetManager()->QueryFMVUnlocked(GetGameplayManager()->GetCurrentLevelIndex());
+ GetPresentationManager()->PlayFMV( mFileName, this, isSkippable, mMusicStop );
+
+ GetInputManager()->SetGameState( Input::ACTIVE_FRONTEND );
+ }
+}
+/*=============================================================================
+Description: Set the file name of the FMV to be played. Note that there isn't
+ any directory seperator correction for the different platforms
+ (at least not at the time of this writting, ATG was talking
+ about putting it in).
+=============================================================================*/
+void FMVObjective::SetFileName( const char* FileName )
+{
+ rAssertMsg( strlen( FileName ) < FMV_FILE_NAME_LEN, "File name for FMV objective too long." );
+ strcpy( mFileName, FileName );
+} \ No newline at end of file
diff --git a/game/code/mission/objectives/fmvobjective.h b/game/code/mission/objectives/fmvobjective.h
new file mode 100644
index 0000000..639caac
--- /dev/null
+++ b/game/code/mission/objectives/fmvobjective.h
@@ -0,0 +1,62 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: fmvobjective.h
+//
+// Description: The FMV objective is used to play an FMV during a mission. Usually at mission end.
+//
+// History: 04 Feb 2003 + Created -- James Harrison
+//
+//=============================================================================
+
+#ifndef FMVOBJECTIVE_H
+#define FMVOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/objectives/missionobjective.h>
+#include <presentation/presevents/presentationevent.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class FMVObjective : public MissionObjective,
+ public PresentationEvent::PresentationEventCallBack
+{
+public:
+ enum { FMV_FILE_NAME_LEN = 13 }; // Enforce 8.3 filenaming.
+
+ FMVObjective();
+ virtual ~FMVObjective();
+
+ void SetFileName( const char* name );
+ void SetMusicStop() { mMusicStop = true; }
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize() {}; // nop.
+ virtual void OnPresentationEventBegin( PresentationEvent* pEvent ) {}; // nop.
+ virtual void OnPresentationEventLoadComplete( PresentationEvent* pEvent ) {}; // nop.
+ virtual void OnPresentationEventEnd( PresentationEvent* pEvent );
+
+private:
+ char mFileName[ FMV_FILE_NAME_LEN ];
+ bool mMusicStop;
+
+// FMVEventData mFMVEventData;
+
+ //Prevent wasteful constructor creation.
+ FMVObjective( const FMVObjective& That );
+ FMVObjective& operator=( const FMVObjective& That );
+};
+
+
+#endif //FMVOBJECTIVE_H
diff --git a/game/code/mission/objectives/followobjective.cpp b/game/code/mission/objectives/followobjective.cpp
new file mode 100644
index 0000000..d08d605
--- /dev/null
+++ b/game/code/mission/objectives/followobjective.cpp
@@ -0,0 +1,227 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: followobjective.cpp
+//
+// Description: Implement FollowObjective
+//
+// History: 10/06/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <events/eventmanager.h>
+
+#include <meta/locator.h>
+
+#include <mission/objectives/followobjective.h>
+#include <mission/animatedicon.h>
+#include <mission/gameplaymanager.h>
+
+#include <memory/srrmemory.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// FollowObjective::FollowObjective
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+FollowObjective::FollowObjective() :
+ mFollowVehicle( NULL ),
+ mAnimatedIcon( NULL )
+ //mDestinationLocator( NULL )
+{
+
+}
+
+//==============================================================================
+// FollowObjective::~FollowObjective
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+FollowObjective::~FollowObjective()
+{
+
+}
+
+//=============================================================================
+// FollowObjective::OnInitalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FollowObjective::OnInitialize()
+{
+MEMTRACK_PUSH_GROUP( "Mission - FollowObjective" );
+// rAssert( mDestinationLocator != NULL );
+// mDestinationLocator->SetFlag( Locator::ACTIVE, true );
+
+ GetEventManager()->AddListener( this, EVENT_WAYAI_HIT_LAST_WAYPOINT );
+
+ //========================= SET UP THE ICON
+
+ rAssert( mAnimatedIcon == NULL );
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mAnimatedIcon = new(gma) AnimatedIcon();
+
+ //TODO put in the actual name...
+ const rmt::Vector pos = mFollowVehicle->GetPosition();
+ mAnimatedIcon->Init( ARROW_EVADE, pos );
+ mAnimatedIcon->ScaleByCameraDistance( MIN_ARROW_SCALE, MAX_ARROW_SCALE, MIN_ARROW_SCALE_DIST, MAX_ARROW_SCALE_DIST );
+
+ //Set this vehicle as the focus of the HUD.
+ rAssert( mFollowVehicle );
+ VehicleAI* vehicleAI = GetVehicleCentral()->GetVehicleAI( mFollowVehicle );
+ rAssert( vehicleAI );
+
+ int hudID = vehicleAI->GetHUDIndex();
+ if ( GetCurrentHud() )
+ {
+ GetCurrentHud()->GetHudMap( 0 )->SetFocalPointIcon( hudID );
+ }
+
+ mFollowVehicle->SetVehicleCanSustainDamage(false);
+
+MEMTRACK_POP_GROUP("Mission - FollowObjective");
+}
+
+//=============================================================================
+// FollowObjective::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FollowObjective::OnFinalize()
+{
+// rAssert( mDestinationLocator != NULL );
+// mDestinationLocator->SetFlag( Locator::ACTIVE, false );
+ mFollowVehicle->SetVehicleCanSustainDamage(true);
+
+ GetEventManager()->RemoveListener( this, EVENT_WAYAI_HIT_LAST_WAYPOINT );
+
+ rAssert( mAnimatedIcon );
+
+ if ( mAnimatedIcon )
+ {
+ delete mAnimatedIcon;
+ mAnimatedIcon = NULL;
+ }
+}
+
+//=============================================================================
+// FollowObjective::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void FollowObjective::OnUpdate( unsigned int elapsedTime )
+{
+ //Update the position of the bv...
+ rmt::Vector carPos;
+ mFollowVehicle->GetPosition( &carPos );
+
+ rmt::Box3D bbox;
+ mFollowVehicle->GetBoundingBox( &bbox );
+ carPos.y = bbox.high.y;
+
+ mAnimatedIcon->Move( carPos );
+ mAnimatedIcon->Update( elapsedTime );
+}
+
+
+//=============================================================================
+// FollowObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void FollowObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+/*
+ case EVENT_LOCATOR + LocatorEvent::CHECK_POINT:
+ {
+ Locator* locator = static_cast<Locator*>( pEventData );
+
+ if( locator == mDestinationLocator )
+ {
+ SetFinished( true );
+ }
+
+ break;
+ }
+*/
+ case EVENT_WAYAI_HIT_LAST_WAYPOINT:
+ {
+ if( pEventData == (void*)mFollowVehicle )
+ {
+ SetFinished( true );
+ }
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/mission/objectives/followobjective.h b/game/code/mission/objectives/followobjective.h
new file mode 100644
index 0000000..351247a
--- /dev/null
+++ b/game/code/mission/objectives/followobjective.h
@@ -0,0 +1,61 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: followobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef FOLLOWOBJECTIVE_H
+#define FOLLOWOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/objectives/missionobjective.h>
+
+//========================================
+// Forward References
+//========================================
+
+class Vehicle;
+class Locator;
+class AnimatedIcon;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class FollowObjective : public MissionObjective
+{
+public:
+ FollowObjective();
+ virtual ~FollowObjective();
+
+ Vehicle* GetTargetVehicle() { return( mFollowVehicle ); }
+ void SetTargetVehicle(Vehicle* pVehicle) { mFollowVehicle = pVehicle; }
+
+/*
+ Locator* GetDestinationLocator() { return( mDestinationLocator ); }
+ void SetDestinationLocator( Locator* pLocator ) { mDestinationLocator = pLocator; }
+*/
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+private:
+ Vehicle* mFollowVehicle;
+ AnimatedIcon* mAnimatedIcon;
+// Locator* mDestinationLocator;
+};
+
+#endif //FOLLOWOBJECTIVE_H
diff --git a/game/code/mission/objectives/getinobjective.cpp b/game/code/mission/objectives/getinobjective.cpp
new file mode 100644
index 0000000..a9a631a
--- /dev/null
+++ b/game/code/mission/objectives/getinobjective.cpp
@@ -0,0 +1,305 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: GetInObjective.cpp
+//
+// Description: Implement GetInObjective
+//
+// History: 09/09/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/entity.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/objectives/GetInObjective.h>
+#include <mission/animatedicon.h>
+#include <mission/gameplaymanager.h>
+
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+
+#include <worldsim/avatarmanager.h>
+
+#include <ai/state.h>
+#include <memory/srrmemory.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// GetInObjective::GetInObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+GetInObjective::GetInObjective() :
+ mHUDID( -1 ),
+ mAnimatedIcon( NULL ),
+ mStrict(false),
+ mVehicleUID(0)
+{
+}
+
+//=============================================================================
+// GetInObjective::~GetInObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+GetInObjective::~GetInObjective()
+{
+}
+
+//=============================================================================
+// GetInObjective::OnInitalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GetInObjective::OnInitialize()
+{
+MEMTRACK_PUSH_GROUP( "Mission - GetInObjective" );
+
+ GameplayManager* gpm = GetGameplayManager();
+
+ // TC: No need to do this anymore since phone booths are now disabled
+ // all thoughout missionmode.
+ //
+// gpm->DisablePhoneBooths();
+
+ Vehicle* vehicle = NULL;
+ if(mStrict)
+ {
+ vehicle = GetVehicleCentral()->GetVehicleByUID(mVehicleUID);
+ }
+ else
+ {
+ vehicle = gpm->GetCurrentVehicle();
+ }
+
+ rAssert(vehicle);
+ if(!vehicle)
+ {
+ //fail
+ return;
+ }
+
+ rmt::Vector pos = vehicle->GetPosition();
+
+// RegisterPosition( pos, mHUDID, true, HudMapIcon::ICON_PLAYER_CAR, this );
+
+ //========================= SET UP THE ICON
+
+ rAssert( mAnimatedIcon == NULL );
+
+ GameMemoryAllocator gma = gpm->GetCurrentMissionHeap();
+ mAnimatedIcon = new(gma) AnimatedIcon();
+
+ //TODO put in the actual name...
+ mAnimatedIcon->Init( ARROW, pos );
+ mAnimatedIcon->ScaleByCameraDistance( MIN_ARROW_SCALE, MAX_ARROW_SCALE, MIN_ARROW_SCALE_DIST, MAX_ARROW_SCALE_DIST );
+
+MEMTRACK_POP_GROUP("Mission - GetInObjective");
+}
+
+//=============================================================================
+// GetInObjective::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GetInObjective::OnFinalize()
+{
+// UnregisterPosition( mHUDID );
+
+ rAssert( mAnimatedIcon );
+
+
+
+ if ( mAnimatedIcon )
+ {
+ delete mAnimatedIcon;
+ mAnimatedIcon = NULL;
+ }
+
+ // TC: No need to do this anymore since phone booths are now disabled
+ // all thoughout missionmode.
+ //
+// GetGameplayManager()->EnablePhoneBooths();
+}
+
+//=============================================================================
+// GetInObjective::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void GetInObjective::OnUpdate( unsigned int elapsedTime )
+{
+ Vehicle* vehicle = NULL;
+ if(mStrict)
+ {
+ vehicle = GetVehicleCentral()->GetVehicleByUID(mVehicleUID);
+ }
+ else
+ {
+ vehicle = GetGameplayManager()->GetCurrentVehicle();
+ }
+
+ rAssert(vehicle);
+ if(!vehicle)
+ {
+ //fail???
+ return;
+ }
+
+ //Update the position of the bv...
+ rmt::Vector carPos;
+ vehicle->GetPosition( &carPos );
+
+ rmt::Box3D bbox;
+ vehicle->GetBoundingBox( &bbox );
+ carPos.y = bbox.high.y;
+
+ mAnimatedIcon->Move( carPos );
+ mAnimatedIcon->Update( elapsedTime );
+
+ if(mStrict)
+ {
+ if ( GetAvatarManager()->GetAvatarForPlayer( 0 )->GetVehicle() == vehicle &&
+ GetCharacterManager()->GetCharacter( 0 )->GetStateManager()->GetState() == CharacterAi::INCAR )
+ {
+ SetFinished( true );
+ }
+ }
+ else
+ {
+ if ( GetAvatarManager()->GetAvatarForPlayer( 0 )->GetVehicle() &&
+ GetCharacterManager()->GetCharacter( 0 )->GetStateManager()->GetState() == CharacterAi::INCAR )
+ {
+ SetFinished( true );
+ }
+ }
+}
+
+void GetInObjective::SetStrict( const char* name )
+{
+ mVehicleUID = tEntity::MakeUID(name);
+ Vehicle* vehicle = GetVehicleCentral()->GetVehicleByUID(mVehicleUID);
+ if(vehicle)
+ {
+ mStrict = true;
+ }
+ else
+ {
+ mStrict = false;
+ mVehicleUID = 0;
+ }
+}
+
+//=============================================================================
+// GetInObjective::GetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* currentLoc )
+//
+// Return: void
+//
+//=============================================================================
+void GetInObjective::GetPosition( rmt::Vector* currentLoc )
+{
+ Vehicle* vehicle = NULL;
+ if(mStrict)
+ {
+ vehicle = GetVehicleCentral()->GetVehicleByUID(mVehicleUID);
+ }
+ else
+ {
+ vehicle = GetGameplayManager()->GetCurrentVehicle();
+ }
+ rAssert( vehicle );
+ if(!vehicle)
+ {
+ return;
+ }
+
+ vehicle->GetPosition( currentLoc );
+}
+
+//=============================================================================
+// GetInObjective::GetHeading
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* heading )
+//
+// Return: void
+//
+//=============================================================================
+void GetInObjective::GetHeading( rmt::Vector* heading )
+{
+ Vehicle* vehicle = NULL;
+ if(mStrict)
+ {
+ vehicle = GetVehicleCentral()->GetVehicleByUID(mVehicleUID);
+ }
+ else
+ {
+ vehicle = GetGameplayManager()->GetCurrentVehicle();
+ }
+ rAssert( vehicle );
+ if(!vehicle)
+ {
+ return;
+ }
+
+ vehicle->GetHeading( heading );
+}
+
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/mission/objectives/getinobjective.h b/game/code/mission/objectives/getinobjective.h
new file mode 100644
index 0000000..230918d
--- /dev/null
+++ b/game/code/mission/objectives/getinobjective.h
@@ -0,0 +1,65 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: getinobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 09/09/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef GETINOBJECTIVE_H
+#define GETINOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/objectives/missionobjective.h>
+#include <presentation/gui/utility/hudmap.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class AnimatedIcon;
+class tName;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class GetInObjective : public MissionObjective, public IHudMapIconLocator
+{
+public:
+ GetInObjective();
+ virtual ~GetInObjective();
+
+ //void SetTargetVehicle(Vehicle* pVehicle) { mGetInVehicle = pVehicle; };
+
+ void SetStrict( const char* name );
+
+ //Interface for IHudMapIconLocator
+ void GetPosition( rmt::Vector* currentLoc );
+ void GetHeading( rmt::Vector* heading );
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+private:
+ int mHUDID;
+ AnimatedIcon* mAnimatedIcon;
+ bool mStrict;
+ tUID mVehicleUID;
+
+ //Prevent wasteful constructor creation.
+ GetInObjective( const GetInObjective& getinobjective );
+ GetInObjective& operator=( const GetInObjective& getinobjective );
+};
+
+
+#endif //GETINOBJECTIVE_H
diff --git a/game/code/mission/objectives/gooutsideobjective.cpp b/game/code/mission/objectives/gooutsideobjective.cpp
new file mode 100644
index 0000000..cc505bf
--- /dev/null
+++ b/game/code/mission/objectives/gooutsideobjective.cpp
@@ -0,0 +1,186 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: gooutsidetoobjective.cpp
+//
+// Description: Implement GoOutsideObjective
+//
+// History: 16/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/utility.hpp>
+//========================================
+// Project Includes
+//========================================
+
+
+#include <mission/objectives/GoOutsideobjective.h>
+#include <mission/objectives/gooutsideobjective.h>
+
+#include <events/eventmanager.h>
+#include <input/inputmanager.h>
+#include <meta/eventlocator.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/triggervolumetracker.h>
+
+#include <mission/objectives/gotoobjective.h>
+#include <mission/objectives/gooutsideobjective.h>
+#include <mission/animatedicon.h>
+#include <mission/gameplaymanager.h>
+#include <mission/missionmanager.h>
+
+#include <render/dsg/inststatentitydsg.h>
+
+#include <ai/actionbuttonhandler.h>
+#include <ai/actionbuttonmanager.h>
+
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+
+#include <memory/srrmemory.h>
+
+#include <interiors/interiormanager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// GoOutsideObjective::GoOutsideObjective
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GoOutsideObjective::GoOutsideObjective()
+{
+
+}
+
+//==============================================================================
+// GoOutsideObjective::~GoOutsideObjective
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GoOutsideObjective::~GoOutsideObjective()
+{
+
+}
+
+//=============================================================================
+// GoOutsideObjective::OnInitalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GoOutsideObjective::OnInitialize()
+{
+MEMTRACK_PUSH_GROUP( "Mission - GoOutsideObjective" );
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+
+ rAssert( mDestinationLocator == NULL );
+
+ mDestinationLocator = p3d::find<Locator>( mDestinationLocatorName );
+ rAssert( mDestinationLocator != NULL );
+
+ //mDestinationLocator->AddRef();
+
+ mDestinationLocator->SetFlag( Locator::ACTIVE, true );
+ //mDestinationLocator->SetFlag( Locator::DRAWN, true );
+
+ EventLocator* eventLocator = dynamic_cast<EventLocator*>( mDestinationLocator );
+ if( eventLocator != NULL )
+ {
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_LOCATOR + eventLocator->GetEventType()));
+ }
+
+ if ( mActionTrigger )
+ {
+ //Swap the settings of the event locator to make it go through the action button
+ //handing system.
+ mOldEvent = eventLocator->GetEventType();
+
+ eventLocator->SetEventType( LocatorEvent::GENERIC_BUTTON_HANDLER_EVENT );
+
+ ActionButton::GenericEventButtonHandler* pABHandler = dynamic_cast<ActionButton::GenericEventButtonHandler*>( ActionButton::GenericEventButtonHandler::NewAction( eventLocator, (EventEnum)(EVENT_LOCATOR + mOldEvent), gma ) );
+ rAssert( pABHandler );
+
+ pABHandler->SetEventData( static_cast<void*>(eventLocator) );
+
+ int id = 0;
+ bool bResult = GetActionButtonManager()->AddAction( pABHandler, id );
+
+ rAssert( bResult );
+ mOldData = eventLocator->GetData();
+ eventLocator->SetData( id );
+ }
+
+ if( strcmp( mDestinationDrawableName, "" ) != 0 )
+ {
+ rmt::Vector pos;
+ mDestinationLocator->GetLocation( &pos );
+
+ mAnimatedIcon = new AnimatedIcon();
+ mAnimatedIcon->Init( mDestinationDrawableName, pos );
+
+ SphereTriggerVolume* sphTrig = dynamic_cast<SphereTriggerVolume*>(eventLocator->GetTriggerVolume(0));
+ //rAssertMsg( sphTrig != NULL, "GoOutside objectives should only use sphere triggers." );
+
+ if ( sphTrig )
+ {
+ if ( mScaleFactor != 0.0f )
+ {
+ sphTrig->SetSphereRadius( mScaleFactor );
+ }
+ }
+
+ //Only put up an icon in the HUD if there is a drawable in the world.
+ RegisterLocator( mDestinationLocator, mHudMapIconID, true, HudMapIcon::ICON_MISSION );
+ }
+ else
+ {
+// mDestinationLocator->SetFlag( Locator::DRAWN, true );
+ }
+
+ if ( strcmp( mEffectName, "" ) != 0 )
+ {
+ mCollectEffect = new AnimatedIcon();
+ mCollectEffect->Init( mEffectName, rmt::Vector( 0.0f, 0.0f, 0.0f ), false, true );
+ }
+
+ mWaitingForEffect = false;
+
+ rmt::Vector targetPosn;
+ mDestinationLocator->GetLocation(&targetPosn);
+ LightPath( targetPosn, mArrowPath );
+
+MEMTRACK_POP_GROUP("Mission - GoOutsideObjective");
+}
+
diff --git a/game/code/mission/objectives/gooutsideobjective.h b/game/code/mission/objectives/gooutsideobjective.h
new file mode 100644
index 0000000..7fba966
--- /dev/null
+++ b/game/code/mission/objectives/gooutsideobjective.h
@@ -0,0 +1,48 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: gooutsideobjective.h
+//
+// Description: Just like the GoToObjective except we dont do the interior check.
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef GOOUTSIDEOBJECTIVE_H
+#define GOOUTSIDEOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/objectives/gotoobjective.h>
+
+
+//========================================
+// Forward References
+//========================================
+
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class GoOutsideObjective : public GoToObjective
+{
+public:
+
+ GoOutsideObjective();
+ virtual ~GoOutsideObjective();
+
+
+protected:
+ virtual void OnInitialize();
+
+private:
+
+};
+
+#endif //GOOUTSIDEOBJECTIVE_H
diff --git a/game/code/mission/objectives/gotoobjective.cpp b/game/code/mission/objectives/gotoobjective.cpp
new file mode 100644
index 0000000..9982801
--- /dev/null
+++ b/game/code/mission/objectives/gotoobjective.cpp
@@ -0,0 +1,499 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: gotoobjective.cpp
+//
+// Description: Implement GoToObjective
+//
+// History: 16/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/utility.hpp>
+//========================================
+// Project Includes
+//========================================
+
+#include <events/eventmanager.h>
+#include <input/inputmanager.h>
+#include <meta/eventlocator.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/triggervolumetracker.h>
+
+#include <mission/objectives/gotoobjective.h>
+#include <mission/animatedicon.h>
+#include <mission/gameplaymanager.h>
+#include <mission/missionmanager.h>
+
+#include <render/dsg/inststatentitydsg.h>
+
+#include <ai/actionbuttonhandler.h>
+#include <ai/actionbuttonmanager.h>
+
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+
+#include <memory/srrmemory.h>
+
+#include <interiors/interiormanager.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/walkercam.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// GoToObjective::GoToObjective
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GoToObjective::GoToObjective() :
+ mDestinationLocator( NULL ),
+ mAnimatedIcon( NULL ),
+ mScaleFactor( 1.0f ),
+ mHudMapIconID( -1 ),
+ mCollectEffect( NULL ),
+ mWaitingForEffect( false ),
+ mGotoDialogOn( true ),
+ mActionTrigger( false ),
+ mOldEvent( LocatorEvent::NUM_EVENTS ),
+ mOldData( -1 )
+{
+ mArrowPath.mPathRoute.Allocate( RoadManager::GetInstance()->GetNumRoads() );
+
+ mEffectName[0] = '\0';
+}
+
+//==============================================================================
+// GoToObjective::~GoToObjective
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GoToObjective::~GoToObjective()
+{
+ if ( mAnimatedIcon )
+ {
+ delete mAnimatedIcon;
+ mAnimatedIcon = NULL;
+
+ UnregisterLocator( mHudMapIconID );
+
+ //Alert the walkercam that there is a drawable.
+ WalkerCam* wc = static_cast<WalkerCam*>(GetSuperCamManager()->GetSCC( 0 )->GetSuperCam( SuperCam::WALKER_CAM ));
+ if ( wc )
+ {
+ wc->SetCollectibleLocation( mAnimatedIcon );
+ }
+
+ }
+ if ( mCollectEffect )
+ {
+ delete mCollectEffect;
+ mCollectEffect = NULL;
+ }
+}
+
+//=============================================================================
+// GoToObjective::OnInitalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GoToObjective::OnInitialize()
+{
+MEMTRACK_PUSH_GROUP( "Mission - GoToObjective" );
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+
+ rAssert( mDestinationLocator == NULL );
+
+ mDestinationLocator = p3d::find<Locator>( mDestinationLocatorName );
+ rAssert( mDestinationLocator != NULL );
+
+ //mDestinationLocator->AddRef();
+
+ mDestinationLocator->SetFlag( Locator::ACTIVE, true );
+ //mDestinationLocator->SetFlag( Locator::DRAWN, true );
+
+ EventLocator* eventLocator = dynamic_cast<EventLocator*>( mDestinationLocator );
+ if( eventLocator != NULL )
+ {
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_LOCATOR + eventLocator->GetEventType()));
+ }
+
+ if ( mActionTrigger )
+ {
+ //Swap the settings of the event locator to make it go through the action button
+ //handing system.
+ mOldEvent = eventLocator->GetEventType();
+
+ eventLocator->SetEventType( LocatorEvent::GENERIC_BUTTON_HANDLER_EVENT );
+
+ ActionButton::GenericEventButtonHandler* pABHandler = dynamic_cast<ActionButton::GenericEventButtonHandler*>( ActionButton::GenericEventButtonHandler::NewAction( eventLocator, (EventEnum)(EVENT_LOCATOR + mOldEvent), gma ) );
+ rAssert( pABHandler );
+
+ pABHandler->SetEventData( static_cast<void*>(eventLocator) );
+
+ int id = 0;
+ bool bResult = GetActionButtonManager()->AddAction( pABHandler, id );
+
+ rAssert( bResult );
+ mOldData = eventLocator->GetData();
+ eventLocator->SetData( id );
+ }
+
+ if( strcmp( mDestinationDrawableName, "" ) != 0 )
+ {
+ rmt::Vector pos;
+ mDestinationLocator->GetLocation( &pos );
+
+ mAnimatedIcon = new AnimatedIcon();
+ mAnimatedIcon->Init( mDestinationDrawableName, pos );
+
+ SphereTriggerVolume* sphTrig = dynamic_cast<SphereTriggerVolume*>(eventLocator->GetTriggerVolume(0));
+ //rAssertMsg( sphTrig != NULL, "GOTO objectives should only use sphere triggers." );
+
+ if ( sphTrig )
+ {
+ if ( mScaleFactor != 0.0f )
+ {
+ sphTrig->SetSphereRadius( mScaleFactor );
+ }
+ }
+
+ //Only put up an icon in the HUD if there is a drawable in the world.
+ RegisterLocator( mDestinationLocator, mHudMapIconID, true, HudMapIcon::ICON_MISSION );
+
+ //Alert the walkercam that there is a drawable.
+ WalkerCam* wc = static_cast<WalkerCam*>(GetSuperCamManager()->GetSCC( 0 )->GetSuperCam( SuperCam::WALKER_CAM ));
+ if ( wc )
+ {
+ wc->SetCollectibleLocation( mAnimatedIcon );
+ }
+ }
+ else
+ {
+// mDestinationLocator->SetFlag( Locator::DRAWN, true );
+ }
+
+ if ( strcmp( mEffectName, "" ) != 0 && strcmp( mEffectName, "none" ) != 0 )
+ {
+ mCollectEffect = new AnimatedIcon();
+ mCollectEffect->Init( mEffectName, rmt::Vector( 0.0f, 0.0f, 0.0f ), false, true );
+ }
+
+ mWaitingForEffect = false;
+
+ rmt::Vector targetPosn;
+ mDestinationLocator->GetLocation(&targetPosn);
+ LightPath( targetPosn, mArrowPath );
+
+ // if the goto objective is near the entrance of our current interior,
+ // lets assume that we don't need to do it
+ if(GetInteriorManager()->IsInside())
+ {
+ Locator* entrance = p3d::find<Locator>(GetInteriorManager()->GetInterior());
+ if(entrance)
+ {
+ rmt::Vector e, d, dist;
+ entrance->GetLocation(&e);
+ mDestinationLocator->GetLocation(&d);
+ dist.Sub(d,e);
+
+ if(dist.Magnitude() < 20.0f)
+ {
+ SetFinished( true );
+
+ // don't wan't to show stage complete for doing nothing
+ GetGameplayManager()->GetCurrentMission()->GetCurrentStage()->ShowStageComplete(false);
+ }
+ }
+ }
+
+MEMTRACK_POP_GROUP("Mission - GoToObjective");
+}
+
+//=============================================================================
+// GoToObjective::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GoToObjective::OnFinalize()
+{
+ //
+ // Disable the controller
+ //
+ GetInputManager()->SetGameState( Input::ACTIVE_GAMEPLAY );
+
+
+ rAssert( mDestinationLocator != NULL );
+
+ mDestinationLocator->SetFlag( Locator::ACTIVE, false );
+
+ EventLocator* eventLocator = dynamic_cast<EventLocator*>( mDestinationLocator );
+ if( eventLocator != NULL )
+ {
+ GetEventManager()->RemoveListener( this, (EventEnum)(EVENT_LOCATOR + eventLocator->GetEventType()));
+
+ //This is to make sure that the exit volume triggers to kill characters hanging on
+ //to action button handlers. Semi-hack
+ unsigned int i;
+ for ( i = 0; i < eventLocator->GetNumTriggers(); ++i )
+ {
+ GetTriggerVolumeTracker()->RemoveTrigger( eventLocator->GetTriggerVolume( i ) );
+ GetTriggerVolumeTracker()->AddTrigger( eventLocator->GetTriggerVolume( i ) );
+ }
+ }
+
+ //Redundant if the old data is -1
+ if ( mActionTrigger && mOldData != -1 )
+ {
+ int id = eventLocator->GetData();
+
+ GetActionButtonManager()->RemoveActionByIndex( id );
+
+ eventLocator->SetData( mOldData );
+ eventLocator->SetEventType( mOldEvent );
+
+ //reset the old data
+ mOldData = -1;
+ }
+
+ //mDestinationLocator->Release();
+ mDestinationLocator = NULL;
+
+ if ( mAnimatedIcon )
+ {
+ delete mAnimatedIcon;
+ mAnimatedIcon = NULL;
+
+ UnregisterLocator( mHudMapIconID );
+
+ //Alert the walkercam that there is a drawable.
+ WalkerCam* wc = static_cast<WalkerCam*>(GetSuperCamManager()->GetSCC( 0 )->GetSuperCam( SuperCam::WALKER_CAM ));
+ if ( wc )
+ {
+ wc->SetCollectibleLocation( NULL );
+ }
+ }
+
+ if ( mCollectEffect )
+ {
+ delete mCollectEffect;
+ mCollectEffect = NULL;
+ }
+
+ UnlightPath( mArrowPath.mPathRoute );
+}
+
+//=============================================================================
+// GoToObjective::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void GoToObjective::OnUpdate( unsigned int elapsedTime )
+{
+ if ( mAnimatedIcon )
+ {
+ mAnimatedIcon->Update( elapsedTime );
+ }
+
+ if ( mCollectEffect )
+ {
+ mCollectEffect->Update( elapsedTime );
+ }
+
+ //This is so that we can see the effect finish before we blow it away.
+ if ( mWaitingForEffect )
+ {
+ if ( !mCollectEffect->IsRendering() )
+ {
+ //The effect is finished rendering.
+ SetFinished( true );
+ }
+ }
+
+ UpdateLightPath(mArrowPath);
+
+ //Hard-code a test to see if we're already in the volume. This will correct if
+ //We're starting a mission inside it.
+ if ( !GetFinished() && mDestinationLocator && !mActionTrigger )
+ {
+ unsigned int i;
+ for ( i = 0; i < mDestinationLocator->GetNumTriggers(); ++i )
+ {
+ if ( reinterpret_cast<TriggerLocator*>(mDestinationLocator)->GetTriggerVolume( i )->IsPlayerTracking( 0 ) )
+ {
+ SetFinished( true );
+ }
+ }
+ }
+}
+
+
+//=============================================================================
+// GoToObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void GoToObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_LOCATOR + LocatorEvent::CHECK_POINT:
+ {
+ Locator* locator = static_cast<Locator*>( pEventData );
+
+ if( locator == mDestinationLocator && !mWaitingForEffect )
+ {
+ if ( mActionTrigger && mOldData != -1 )
+ {
+ //This is a hack for if the Effect takes too long, we need
+ //to get rid of the frickin' button in the screen
+ int id = locator->GetData();
+ GetCharacterManager()->GetCharacter( 0 )->RemoveActionButtonHandler( GetActionButtonManager()->GetActionByIndex( id ) );
+ }
+
+ if( mGotoDialogOn )
+ {
+ GetEventManager()->TriggerEvent( EVENT_DESTINATION_REACHED );
+ }
+
+ if ( IsFinished() == false )
+ {
+ //
+ // Disable the controller
+ //
+ GetInputManager()->SetGameState( Input::ACTIVE_NONE );
+
+
+ if ( mAnimatedIcon )
+ {
+ //Put the collection effect here and hit start.
+ rmt::Vector pos;
+ locator->GetLocation( &pos );
+
+ if ( mCollectEffect )
+ {
+ mCollectEffect->Reset();
+ mCollectEffect->Move( pos );
+ mCollectEffect->ShouldRender( true );
+
+ mWaitingForEffect = true;
+ }
+ else if ( strcmp( mEffectName, "none" ) != 0 )
+ {
+ //Only show the effect if there is a drawable.
+ if ( strcmp( mDestinationDrawableName, "" ) != 0 )
+ {
+ //USe the default one from the mission manager
+ GetMissionManager()->PutEffectHere( pos );
+ }
+ }
+
+
+ //Also, get rid of the old one.
+ mAnimatedIcon->ShouldRender( false );
+ }
+ }
+
+ if ( !mWaitingForEffect )
+ {
+ SetFinished( true );
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ // this isn't a "real" event listener so it may get events
+ // it doesn't care about
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// GoToObjective::SetDestinationLocatorName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char* locatorname, char* p3dname, float scale )
+//
+// Return: void
+//
+//=============================================================================
+void GoToObjective::SetDestinationNames( char* locatorname, char* p3dname, float scale )
+{
+ strcpy( mDestinationLocatorName, locatorname );
+ strcpy( mDestinationDrawableName, p3dname );
+ mScaleFactor = scale;
+}
+
+//=============================================================================
+// GoToObjective::SetCollectEffectName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char* name )
+//
+// Return: void
+//
+//=============================================================================
+void GoToObjective::SetCollectEffectName( char* name )
+{
+ rAssert( name );
+ strcpy( mEffectName, name );
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/mission/objectives/gotoobjective.h b/game/code/mission/objectives/gotoobjective.h
new file mode 100644
index 0000000..31d62e6
--- /dev/null
+++ b/game/code/mission/objectives/gotoobjective.h
@@ -0,0 +1,82 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: gotoobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef GOTOOBJECTIVE_H
+#define GOTOOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/objectives/missionobjective.h>
+
+#include <meta/locatorevents.h>
+
+//========================================
+// Forward References
+//========================================
+
+class Locator;
+class AnimatedIcon;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class GoToObjective : public MissionObjective
+{
+public:
+ GoToObjective();
+ virtual ~GoToObjective();
+
+ Locator* GetDestinationLocator() { return( mDestinationLocator ); }
+ void SetDestinationLocator( Locator* pLocator ) { mDestinationLocator = pLocator; }
+
+ void SetDestinationNames( char* locatorname, char* p3dname, float scale );
+ void SetCollectEffectName( char* name );
+ void SetGotoDialogOff() { mGotoDialogOn = false; }
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void SetMustActionTrigger() { mActionTrigger = true; };
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ PathStruct mArrowPath;
+ char mDestinationLocatorName[ 32 ];
+ char mDestinationDrawableName[ 32 ];
+ char mEffectName[32];
+
+ Locator* mDestinationLocator;
+ AnimatedIcon* mAnimatedIcon;
+ float mScaleFactor;
+
+ int mHudMapIconID;
+
+ AnimatedIcon* mCollectEffect;
+ bool mWaitingForEffect;
+
+ bool mGotoDialogOn;
+
+ bool mActionTrigger;
+ LocatorEvent::Event mOldEvent;
+ int mOldData;
+
+private:
+
+};
+
+#endif //GOTOOBJECTIVE_H
diff --git a/game/code/mission/objectives/interiorobjective.cpp b/game/code/mission/objectives/interiorobjective.cpp
new file mode 100644
index 0000000..8578ecc
--- /dev/null
+++ b/game/code/mission/objectives/interiorobjective.cpp
@@ -0,0 +1,259 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: InteriorObjective.cpp
+//
+// Description: Implement InteriorObjective
+//
+// History: 09/09/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/objectives/InteriorObjective.h>
+#include <mission/animatedicon.h>
+#include <mission/gameplaymanager.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/character/character.h>
+#include <events/eventmanager.h>
+
+#include <interiors/interiormanager.h>
+
+#include <meta/locator.h>
+
+#include <memory/srrmemory.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// InteriorObjective::InteriorObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+InteriorObjective::InteriorObjective() :
+ mHUDID( -1 ),
+ mAnimatedIcon( NULL )
+{
+ mArrowPath.mPathRoute.Allocate( RoadManager::GetInstance()->GetNumRoads() );
+
+ mIcon[0] = '\0';
+}
+
+//=============================================================================
+// InteriorObjective::~InteriorObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+InteriorObjective::~InteriorObjective()
+{
+}
+
+//=============================================================================
+// InteriorObjective::OnInitalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void InteriorObjective::OnInitialize()
+{
+MEMTRACK_PUSH_GROUP( "Mission - InteriorObjective" );
+ // if we are already inside the interior, need to trigger the objective
+ if(GetInteriorManager()->IsInside() && (GetInteriorManager()->GetInterior() == tEntity::MakeUID(mDestination)))
+ {
+ SetFinished( true );
+
+ // don't wan't to show stage complete for doing nothing
+ GetGameplayManager()->GetCurrentMission()->GetCurrentStage()->ShowStageComplete(false);
+ return;
+ }
+
+ GameplayManager* gpm = GetGameplayManager();
+
+ GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_TRANSITION_START );
+
+ Locator* l = p3d::find<Locator>(mDestination);
+ rAssert(l);
+
+ l->GetPosition(&mPosition);
+
+ RegisterPosition( mPosition, mHUDID, true, HudMapIcon::ICON_MISSION, this );
+
+ //========================= SET UP THE ICON
+
+ rAssert( mAnimatedIcon == NULL );
+
+
+ //TODO put in the actual name...
+ if ( mIcon[0] != '\0' )
+ {
+ GameMemoryAllocator gma = gpm->GetCurrentMissionHeap();
+ mAnimatedIcon = new(gma) AnimatedIcon();
+ mAnimatedIcon->Init( mIcon, rmt::Vector( 0.0f, 0.0f, 0.0f ) ); //These are built in worldspace
+ }
+
+
+ LightPath( mPosition, mArrowPath );
+MEMTRACK_POP_GROUP("Mission - InteriorObjective");
+}
+
+//=============================================================================
+// InteriorObjective::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void InteriorObjective::OnFinalize()
+{
+ GetEventManager()->RemoveListener( this, EVENT_ENTER_INTERIOR_TRANSITION_START );
+
+ UnregisterPosition( mHUDID );
+
+ if ( mAnimatedIcon )
+ {
+ delete mAnimatedIcon;
+ mAnimatedIcon = NULL;
+ }
+ UnlightPath( mArrowPath.mPathRoute );
+}
+
+//=============================================================================
+// InteriorObjective::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void InteriorObjective::OnUpdate( unsigned int elapsedTime )
+{
+ if ( mAnimatedIcon )
+ {
+ mAnimatedIcon->Update( elapsedTime );
+ }
+ UpdateLightPath(mArrowPath);
+}
+
+
+//=============================================================================
+// InteriorObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void InteriorObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_ENTER_INTERIOR_TRANSITION_START:
+ {
+ if(*((tUID*)pEventData) == tEntity::MakeUID(mDestination))
+ {
+ SetFinished( true );
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// InteriorObjective::GetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* currentLoc )
+//
+// Return: void
+//
+//=============================================================================
+void InteriorObjective::GetPosition( rmt::Vector* currentLoc )
+{
+ *currentLoc = mPosition;
+}
+
+//=============================================================================
+// InteriorObjective::GetHeading
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* heading )
+//
+// Return: void
+//
+//=============================================================================
+void InteriorObjective::GetHeading( rmt::Vector* heading )
+{
+ heading->Set(0.0f,0.0f,1.0f);
+}
+
+//=============================================================================
+// InteriorObjective::SetDestination
+//=============================================================================
+void InteriorObjective::SetDestination(const char* d)
+{
+ strcpy(mDestination, d);
+}
+
+//=============================================================================
+// InteriorObjective::SetDestination
+//=============================================================================
+void InteriorObjective::SetIcon(const char* d)
+{
+ rAssert( strlen(d) < 64 );
+
+ strcpy(mIcon, d);
+ mIcon[ strlen( d ) ] = '\0';
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/mission/objectives/interiorobjective.h b/game/code/mission/objectives/interiorobjective.h
new file mode 100644
index 0000000..4179ca3
--- /dev/null
+++ b/game/code/mission/objectives/interiorobjective.h
@@ -0,0 +1,70 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: interiorobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 09/09/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef INTERIOROBJECTIVE_H
+#define INTERIOROBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/objectives/missionobjective.h>
+#include <presentation/gui/utility/hudmap.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class AnimatedIcon;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class InteriorObjective : public MissionObjective, public IHudMapIconLocator
+{
+public:
+ InteriorObjective();
+ virtual ~InteriorObjective();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ //void SetTargetVehicle(Vehicle* pVehicle) { mInteriorVehicle = pVehicle; };
+
+ //Interface for IHudMapIconLocator
+ void GetPosition( rmt::Vector* currentLoc );
+ void GetHeading( rmt::Vector* heading );
+
+ void SetDestination(const char*);
+ void SetIcon(const char*);
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ PathStruct mArrowPath;
+
+private:
+ int mHUDID;
+ AnimatedIcon* mAnimatedIcon;
+ char mDestination[64];
+ char mIcon[64];
+ rmt::Vector mPosition;
+
+ //Prevent wasteful constructor creation.
+ InteriorObjective( const InteriorObjective& Interiorobjective );
+ InteriorObjective& operator=( const InteriorObjective& Interiorobjective );
+};
+
+
+#endif //GETINOBJECTIVE_H
diff --git a/game/code/mission/objectives/loadvehicleobjective.cpp b/game/code/mission/objectives/loadvehicleobjective.cpp
new file mode 100644
index 0000000..3638067
--- /dev/null
+++ b/game/code/mission/objectives/loadvehicleobjective.cpp
@@ -0,0 +1,141 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: LoadVehicleObjective
+//
+// Description: Mission objective thats always winnable, wait for a car to dynamically
+// load
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <mission\objectives\LoadVehicleObjective.h>
+#include <memory\srrmemory.h>
+#include <mission\gameplaymanager.h>
+#include <worldsim\vehiclecentral.h>
+#include <meta\carstartlocator.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+LoadVehicleObjective::LoadVehicleObjective()
+{
+
+}
+
+LoadVehicleObjective::~LoadVehicleObjective()
+{
+
+}
+
+void LoadVehicleObjective::OnInitialize()
+{
+ // Start loading
+ strcpy( m_CallbackData.fileName, m_Filename );
+ strcpy( m_CallbackData.vehicleName, m_VehicleName );
+ strcpy( m_CallbackData.locatorName, m_LocatorName );
+
+ m_CallbackData.objective = this;
+
+ // We are going to dump the current vehicle and replace it with a new one
+ GetGameplayManager()->DumpCurrentCar();
+ GetGameplayManager()->CopyVehicleSlot( GameplayManager::eAICar, GameplayManager::eDefaultCar );
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, m_Filename, GMA_LEVEL_MISSION, m_Filename, NULL, &m_Callback, &m_CallbackData );
+}
+
+void LoadVehicleObjective::OnFinalize()
+{
+
+}
+
+void LoadVehicleObjective::LoadDisposableCarAsyncCallback::OnProcessRequestsComplete( void* pUserData )
+{
+ rAssert( pUserData != NULL );
+ LoadVehicleObjective::CallbackData* callbackData = reinterpret_cast < CallbackData* >( pUserData );
+
+ GetGameplayManager()->DumpCurrentCar();
+ Vehicle* vehicle = GetVehicleCentral()->InitVehicle( callbackData->vehicleName, true, NULL, VT_USER );
+ if ( vehicle )
+ {
+ // Set the vehicle position and orientation.
+ CarStartLocator* pLocator = p3d::find<CarStartLocator>( callbackData->locatorName );
+ if ( pLocator )
+ {
+ rmt::Vector position;
+ pLocator->GetLocation( &position );
+ float facing = pLocator->GetRotation();
+
+ vehicle->SetResetFacingInRadians( facing );
+ //
+ // This will activate the above settings.
+ //
+ vehicle->Reset( false );
+ GetGameplayManager()->SetCurrentVehicle(vehicle);
+
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].mp_vehicle = vehicle;
+ GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].mp_vehicle->AddRef();
+ }
+ }
+ // Tell the objective that we are all done
+ callbackData->objective->SetFinished( true );
+}
+
+/*
+//=============================================================================
+// MissionScriptLoader::LoadDisposableCarAsync
+//=============================================================================
+// Description: Use this to load any vehicle that may get unloaded during a level gameplay session,
+// ie player car,ai car, forced car into its own iventory section.
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+
+/// VERY TEMPORARY CALLBACK for async vehicle loading
+
+
+void MissionScriptLoader::LoadDisposableCarAsync( int argc, char** argv )
+{
+ const char* fileName = argv[1];
+ const char* vehicleName = argv[2];
+ const char* vehicleType = argv[3];
+
+ if ( strcmp( vehicleType, "DEFAULT" ) == 0 )
+ {
+ rAssertMsg( 0, "Can't async load player vehicles other than the default!!");
+ return;
+ }
+ // We are trying to load a player vehicle ingame
+ // first we must dump the player's vehicle
+ // then set the player vehicle to be a temporary traffic vehicle.
+ // this is just a placeholder while the new car is loaded async
+ // Setup a callback that gets triggered on
+
+// strcpy(GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].name, name);
+// strcpy(GetGameplayManager()->mVehicleSlots[GameplayManager::eDefaultCar].filename, filename);
+
+ GetGameplayManager()->DumpCurrentCar();
+
+ // GetGameplayManager()->ClearVehicleSlot(GameplayManager::eDefaultCar);
+ // GetVehicleCentral()->InitVehicle( vehicleName,
+
+ // Create a temp callback, this is just for testing purposes
+}*/
diff --git a/game/code/mission/objectives/loadvehicleobjective.h b/game/code/mission/objectives/loadvehicleobjective.h
new file mode 100644
index 0000000..95f0f18
--- /dev/null
+++ b/game/code/mission/objectives/loadvehicleobjective.h
@@ -0,0 +1,102 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: LoadVehicleObjective
+//
+// Description: Mission objective thats always winnable, wait for a car to dynamically
+// load
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef LoadVehicleObjective_H
+#define LoadVehicleObjective_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <mission/objectives/missionobjective.h>
+#include <loading/loadingmanager.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+// Describe here what you require of the client which uses or
+// has this class - essentially, the converse of the description
+// above. A constraint is an expression of some semantic
+// condition that must be preserved, an invariant of a class or
+// relationship that must be preserved while the system is in a
+// steady state.
+//
+//===========================================================================
+class LoadVehicleObjective : public MissionObjective
+{
+ public:
+ LoadVehicleObjective();
+ virtual ~LoadVehicleObjective();
+
+ void SetFilename( const char* filename ) { strcpy( m_Filename, filename ); }
+ void SetVehicleName( const char* vehicleName ) { strcpy( m_VehicleName, vehicleName ); }
+ void SetLocatorName( const char* locatorName ) { strcpy( m_LocatorName, locatorName ); }
+
+ protected:
+
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+
+ struct CallbackData
+ {
+ char vehicleName[256];
+ char fileName[256];
+ char locatorName[256];
+ LoadVehicleObjective* objective;
+ };
+
+ class LoadDisposableCarAsyncCallback : public LoadingManager::ProcessRequestsCallback
+ {
+ public:
+ LoadDisposableCarAsyncCallback( void ){}
+ ~LoadDisposableCarAsyncCallback( void ){}
+ void OnProcessRequestsComplete( void* pUserData );
+ };
+
+ char m_VehicleName[256];
+ char m_Filename[256];
+ char m_LocatorName[256];
+ LoadDisposableCarAsyncCallback m_Callback;
+ CallbackData m_CallbackData;
+
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow LoadVehicleObjective from being copied and assigned.
+ LoadVehicleObjective( const LoadVehicleObjective& );
+ LoadVehicleObjective& operator=( const LoadVehicleObjective& );
+
+ friend class LoadDisposableCarAsyncCallback;
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/mission/objectives/loseobjective.cpp b/game/code/mission/objectives/loseobjective.cpp
new file mode 100644
index 0000000..19e0fd3
--- /dev/null
+++ b/game/code/mission/objectives/loseobjective.cpp
@@ -0,0 +1,202 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement LoseObjective
+//
+// History: 24/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <mission/objectives/loseobjective.h>
+#include <mission/animatedicon.h>
+#include <mission/gameplaymanager.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/vehicle.h>
+
+#include <memory/srrmemory.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// LoseObjective::LoseObjective
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LoseObjective::LoseObjective() :
+ mDistance( 0 ),
+ mTargetVehicle( NULL ),
+ mAnimatedIcon( NULL )
+{
+}
+
+//==============================================================================
+// LoseObjective::~LoseObjective
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+LoseObjective::~LoseObjective()
+{
+}
+
+//=============================================================================
+// LoseObjective::OnInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoseObjective::OnInitialize()
+{
+MEMTRACK_PUSH_GROUP( "Mission - LoseObjective" );
+ GetGuiSystem()->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_PROXIMITY_METER, 0 /* 0 = red bar */ );
+
+ rAssert( mTargetVehicle );
+ rmt::Vector pos = mTargetVehicle->GetPosition();
+
+ //Set this vehicle as the focus of the HUD.
+ VehicleAI* vehicleAI = GetVehicleCentral()->GetVehicleAI( mTargetVehicle );
+ rAssert( vehicleAI != NULL );
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->GetHudMap( 0 )->SetFocalPointIcon( vehicleAI->GetHUDIndex() );
+ }
+
+ //========================= SET UP THE ICON
+
+ rAssert( mAnimatedIcon == NULL );
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mAnimatedIcon = new(gma) AnimatedIcon();
+
+ //TODO put in the actual name...
+ mAnimatedIcon->Init( ARROW_CHASE, pos );
+ mAnimatedIcon->ScaleByCameraDistance( MIN_ARROW_SCALE, MAX_ARROW_SCALE, MIN_ARROW_SCALE_DIST, MAX_ARROW_SCALE_DIST );
+
+MEMTRACK_POP_GROUP("Mission - LoseObjective");
+}
+
+//=============================================================================
+// LoseObjective::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void LoseObjective::OnFinalize()
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_PROXIMITY_METER );
+
+ rAssert( mAnimatedIcon );
+
+ if ( mAnimatedIcon )
+ {
+ delete mAnimatedIcon;
+ mAnimatedIcon = NULL;
+ }
+}
+
+//=============================================================================
+// LoseObjective::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void LoseObjective::OnUpdate( unsigned int elapsedTime )
+{
+ //
+ // TODO: Find the distance along the road?
+ //
+ rmt::Vector myPos;
+
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( myPos );
+
+ Vehicle* target = mTargetVehicle;
+ rAssert( target != NULL );
+
+ rmt::Vector targetPos;
+ target->GetPosition( &targetPos );
+
+ rmt::Vector arrowPos = targetPos;
+ rmt::Box3D bbox;
+ target->GetBoundingBox( &bbox );
+ arrowPos.y = bbox.high.y;
+
+ //Update the icon while the pos is good.
+ mAnimatedIcon->Move( arrowPos );
+ mAnimatedIcon->Update( elapsedTime );
+
+ targetPos.Sub( myPos );
+ float dist = targetPos.MagnitudeSqr();
+
+ if( dist > (mDistance * mDistance) )
+ {
+ SetFinished( true );
+ }
+ else
+ {
+ // update proximity meter on HUD
+ //
+ CGuiScreenHud* pHUD = GetCurrentHud();
+ if( pHUD != NULL )
+ {
+ pHUD->SetProximityMeter( 1.0f - rmt::Sqrt( dist ) / mDistance );
+ }
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/mission/objectives/loseobjective.h b/game/code/mission/objectives/loseobjective.h
new file mode 100644
index 0000000..9ba6627
--- /dev/null
+++ b/game/code/mission/objectives/loseobjective.h
@@ -0,0 +1,62 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: loseobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 24/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef LOSEOBJECTIVE_H
+#define LOSEOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/objectives/missionobjective.h>
+
+//========================================
+// Forward References
+//========================================
+
+class Vehicle;
+class AnimatedIcon;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class LoseObjective : public MissionObjective
+{
+public:
+ LoseObjective();
+ virtual ~LoseObjective();
+
+ Vehicle* GetTargetVehicle() { return mTargetVehicle; }
+ void SetTargetVehicle(Vehicle* pVehicle) { mTargetVehicle = pVehicle; }
+
+ void SetDistance( float dist ) { mDistance = dist; }
+ float GetDistance() { return mDistance; }
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+private:
+ float mDistance;
+ Vehicle* mTargetVehicle;
+ AnimatedIcon* mAnimatedIcon;
+
+ //Prevent wasteful constructor creation.
+ LoseObjective( const LoseObjective& loseobjective );
+ LoseObjective& operator=( const LoseObjective& loseobjective );
+};
+
+
+#endif //LOSEOBJECTIVE_H
diff --git a/game/code/mission/objectives/missionobjective.cpp b/game/code/mission/objectives/missionobjective.cpp
new file mode 100644
index 0000000..e9aad17
--- /dev/null
+++ b/game/code/mission/objectives/missionobjective.cpp
@@ -0,0 +1,1751 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: missionobjective.cpp
+//
+// Description: Implement MissionObjective
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <meta/locator.h>
+#include <meta/carstartlocator.h>
+#include <meta/zoneeventlocator.h>
+
+#include <mission/gameplaymanager.h>
+#include <mission/missionmanager.h>
+#include <mission/objectives/missionobjective.h>
+
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <render/culling/worldscene.h>
+#include <render/dsg/inststatentitydsg.h>
+#include <render/rendermanager/rendermanager.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/backend/guimanagerbackend.h>
+
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+
+#include <memory/srrmemory.h>
+
+#include <render/IntersectManager/IntersectManager.h>
+#include <worldsim/avatarmanager.h>
+#include <roads/roadmanager.h>
+#include <roads/intersection.h>
+#include <roads/road.h>
+#include <Render/DSG/AnimEntityDSG.h>
+#include <Render/AnimEntityDSGManager/AnimEntityDSGManager.h>
+#include <interiors/interiormanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// MissionObjective::MissionObjective
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MissionObjective::MissionObjective() :
+ mObjType( OBJ_INVALID ),
+ mbFinished( false ),
+ mVehicle( NULL ),
+ mNumNPCs( 0 ),
+ mNumRemoveNPCs( 0 ),
+ mRockOut( false )
+{
+
+}
+
+//==============================================================================
+// MissionObjective::~MissionObjective
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MissionObjective::~MissionObjective()
+{
+
+}
+
+bool MissionObjective::AddNPCWaypoint( const char* npc, const char* location )
+{
+ rTuneAssert( npc );
+ rTuneAssert( location );
+
+ for( unsigned int i = 0; i < mNumNPCs; ++i )
+ {
+ if( strcmp( mNPCNames[i], npc ) == 0 )
+ {
+ int numWays = mNPCs[i].numWayLocs;
+ rAssert( numWays >= 0 );
+
+ // add only if we have room
+ if( numWays >= (MAX_NPC_WAYPOINT_LOCATORS - 1) )
+ {
+#if (RAD_TUNE || RAD_DEBUG)
+ char errMsg[ 256 ];
+ sprintf( errMsg, "Cannot add more NPC waypoints for %s", npc );
+ rTuneAssertMsg( false, errMsg );
+#endif
+ return false;
+ }
+ mNPCs[i].wayLocs[ numWays ].SetText( location );
+ mNPCs[i].numWayLocs++;
+
+ return true;
+ }
+ }
+ // if we got here, the npc is not in our list.
+ return false;
+}
+
+//=============================================================================
+// MissionObjective::Initalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionObjective::Initialize()
+{
+#if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ char errMsg[ 256 ];
+#endif
+
+ //
+ // Trigger an event that this objective is now active
+ //
+ GetEventManager()->TriggerEvent( EVENT_MISSION_OBJECTIVE_NEW, reinterpret_cast< void* >( this ) );
+
+ mbFinished = false;
+
+ //Move the required NPCs to their new locations and store their old pos/rot.
+ unsigned int i;
+
+ for(i = 0; i < mNumRemoveNPCs; i++)
+ {
+ char name[64];
+ if(mRemoveNPCs[i].driver)
+ {
+ sprintf(name, "d_%s", mRemoveNPCs[i].name);
+ }
+ else
+ {
+ strcpy(name, mRemoveNPCs[i].name);
+ }
+
+ Character* c = GetCharacterManager()->GetCharacterByName(name);
+ if(c)
+ {
+ if(c->IsAmbient())
+ {
+ c->ResetAmbientPosition();
+ }
+ else
+ {
+ if(mRemoveNPCs[i].driver && c->GetTargetVehicle())
+ {
+ c->GetTargetVehicle()->SetDriver(NULL);
+ }
+ GetCharacterManager()->RemoveCharacter(c);
+ }
+ }
+ }
+
+ for ( i = 0; i < mNumNPCs; ++i )
+ {
+ if(mNPCs[ i ].driver)
+ {
+ Vehicle* vehicle = GetGameplayManager()->GetMissionVehicleByName(mNPCs[ i ].locator);
+
+ char name[64];
+ sprintf(name, "d_%s", mNPCNames[i]);
+ Character* character = GetCharacterManager( )->GetCharacterByName(name);
+
+ if(character && (character->GetTargetVehicle() == vehicle))
+ {
+ continue;
+ }
+
+ if(character)
+ {
+ GetCharacterManager()->RemoveCharacter(character);
+ }
+
+ character = GetCharacterManager()->AddCharacter(CharacterManager::NPC, name, mNPCNames[i], "npd", NULL );
+
+ static_cast<NPCController*>(character->GetController())->TransitToState(NPCController::NONE);
+ character->SetTargetVehicle( vehicle );
+ character->AddToWorldScene();
+ character->GetStateManager()->SetState<CharacterAi::InCar>();
+
+ vehicle->SetDriver(character);
+
+ character->SetRole(Character::ROLE_DRIVER);
+ }
+ else
+ {
+ // pick an appropriate name based on if we are a bonus mission or not
+ bool hadToAdd = false;
+
+ mNPCs[ i ].npc = GetCharacterManager( )->GetMissionCharacter(mNPCNames[i]);
+
+ if( !mNPCs[ i ].npc )
+ {
+ char name[64];
+ hadToAdd = true;
+ if(GetGameplayManager()->GetCurrentMission()->IsBonusMission())
+ {
+ sprintf(name, "b_%s", mNPCNames[i]);
+ }
+ else
+ {
+ strcpy(name, mNPCNames[i]);
+ }
+ mNPCs[ i ].npc = GetCharacterManager()->AddCharacter(CharacterManager::NPC, name, mNPCNames[i], "npd", NULL );
+ mNPCs[ i ].npc->SetRole(Character::ROLE_MISSION);
+ }
+ else
+ {
+ mNPCs[ i ].npc->EnableAmbientDialogue(false);
+ }
+
+
+ rAssert( mNPCs[ i ].npc );
+
+ CarStartLocator* newPos = p3d::find<CarStartLocator>(mNPCs[ i ].locator);
+
+ // TODO : is this neccesary
+ mNPCs[ i ].npc->RemoveFromWorldScene();
+ mNPCs[ i ].npc->AddToWorldScene();
+
+ GetCharacterManager( )->SetGarbage(mNPCs[ i ].npc, false);
+
+ mNPCs[ i ].npc->AddRef();
+
+ NPCController* controller = (NPCController*) (mNPCs[ i ].npc->GetController());
+ rAssert( controller );
+
+ // set position & facing
+ // if we have no locator, just leave them where they are
+ if(newPos)
+ {
+ rmt::Vector oldpos, pos, dist;
+ newPos->GetLocation(&pos);
+ mNPCs[ i ].npc->GetPosition(oldpos);
+ dist.Sub(pos, oldpos);
+
+ bool loading = ((GetGuiSystem()->GetBackendManager()->GetCurrentScreen() == CGuiWindow::GUI_SCREEN_ID_LOADING) &&
+ GetGuiSystem()->GetBackendManager()->GetCurrentWindow()->IsRunning());
+
+ if(hadToAdd || (dist.Magnitude() > 20.0f) ||
+ ((GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_GAMEPLAY) || loading))
+ {
+ mNPCs[ i ].npc->RelocateAndReset( pos, newPos->GetRotation() );
+
+ if(!hadToAdd)
+ {
+ if(mNPCs[ i ].npc->GetRole() != Character::ROLE_MISSION)
+ {
+ controller->SetTempWaypont(pos);
+ }
+ }
+ }
+
+ // add the start locator as the first waypoint
+ if((mNPCs[ i ].npc->GetRole() == Character::ROLE_MISSION))
+ {
+ controller->ClearNPCWaypoints();
+
+ bool res = controller->AddNPCWaypoint( pos );
+
+#if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ sprintf( errMsg, "Can't add more waypoints for this objective NPC %s", mNPCNames[ i ] );
+ rTuneAssertMsg( res, errMsg );
+#endif
+ }
+
+ }
+ else
+ {
+ #if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ // for existing characters it's okay not to have a locator,
+ // but for new ones, its an error
+ sprintf( errMsg, "NPC %s created without valid locator (%s). Check spelling!\n",
+ mNPCNames[i], mNPCs[ i ].locator);
+ rTuneAssertMsg( false, errMsg );
+ #endif
+ }
+
+ if(mNPCs[ i ].npc->GetRole() == Character::ROLE_MISSION)
+ {
+ for( int j=0; j<mNPCs[ i ].numWayLocs; j++ )
+ {
+ Locator* loc = p3d::find<Locator>( mNPCs[ i ].wayLocs[ j ].GetUID() );
+ if( loc == NULL )
+ {
+ #if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ sprintf( errMsg, "NPC Waypoint %s not found for NPC %s",
+ mNPCs[ i ].wayLocs[ j ].GetText(), mNPCNames[ i ] );
+ rTuneAssertMsg( false, errMsg );
+ #endif
+ continue;
+ }
+ rmt::Vector locPos;
+ loc->GetPosition( &locPos );
+
+ // the NPCController is created and assigned to the NPC in
+ // then NPCharacter constructor. So we should expect there
+ // to be a controller at this point
+ bool res = controller->AddNPCWaypoint( locPos );
+
+ #if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ sprintf( errMsg, "Can't add more waypoints for this objective NPC %s", mNPCNames[ i ] );
+ rTuneAssertMsg( res, errMsg );
+ #endif
+ }
+ }
+ }
+ }
+
+ GetCharacterManager()->AllowGarbageCollection(true);
+
+ GetCharacterManager()->GetCharacter(0)->SetRockinIdle(mRockOut);
+ OnInitialize();
+}
+
+//=============================================================================
+// MissionObjective::Finalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MissionObjective::Finalize()
+{
+ OnFinalize();
+
+ GetCharacterManager()->GetCharacter(0)->SetRockinIdle(false);
+
+ //return all NPCs to their original locations.
+ unsigned int i;
+ for ( i = 0; i < mNumNPCs; ++i )
+ {
+ if( mNPCs[ i ].driver)
+ {
+ continue;
+ }
+
+ rAssert( mNPCs[ i ].npc );
+
+ // make all NPC's in this mission canidates for garbage colleciton
+ GetCharacterManager( )->SetGarbage(mNPCs[ i ].npc, true);
+
+ /***
+ mNPCs[ i ].npc->SetPosition( mNPCs[ i ].oldPos );
+ mNPCs[ i ].npc->SetFacingDir( mNPCs[ i ].oldRotation );
+ mNPCs[ i ].npc->UpdateTransformToLoco();
+ ***/
+ mNPCs[ i ].npc->Release();
+ mNPCs[ i ].npc = NULL;
+ }
+
+ GetCharacterManager()->AllowGarbageCollection(false);
+}
+
+//=============================================================================
+// MissionObjective::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void MissionObjective::Update( unsigned int elapsedTime )
+{
+ OnUpdate( elapsedTime );
+}
+
+//=============================================================================
+// MissionObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void MissionObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+}
+
+
+//=============================================================================
+// MissionObjective::AddNPC
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name, CarStartLocator* newPos )
+//
+// Return: void
+//
+//=============================================================================
+void MissionObjective::AddNPC( const char* name, const char* loc, bool driver)
+{
+#if (RAD_TUNE || RAD_DEBUG)
+ char errMsg[ 128 ];
+
+ rTuneAssertMsg( name, "An NPC's name can't be null!" );
+
+ sprintf( errMsg, "Locator for NPC %s cannot be null!", name );
+ rTuneAssertMsg( loc, errMsg );
+
+ sprintf( errMsg, "Can't add more NPCs. Already reached max count of %d.", mNumNPCs );
+ rTuneAssertMsg( mNumNPCs < (MAX_NPCS - 1), errMsg );
+#endif
+
+ //Add the NPC name to the list.
+ unsigned int len = strlen(name) < MAX_NPC_NAME_LENGTH - 1 ? strlen(name) : MAX_NPC_NAME_LENGTH - 2;
+ strncpy( mNPCNames[mNumNPCs], name, len );
+ mNPCNames[mNumNPCs][len] = '\0';
+
+ strcpy(mNPCs[ mNumNPCs ].locator, loc);
+
+ // NOTE: Don't need to add the char start locator to list of waypoints here
+ // because when AddCharacter is called on this NPC, the start locator
+ // will be automatically added first in the list of NPC waypoints
+ mNPCs[ mNumNPCs ].numWayLocs = 0;
+
+ mNPCs[ mNumNPCs ].driver = driver;
+
+ ++mNumNPCs;
+}
+
+void MissionObjective::RemoveNPC( const char* npc, bool driver)
+{
+ rAssert(mNumRemoveNPCs < MAX_NPCS);
+ strcpy(mRemoveNPCs[mNumRemoveNPCs].name, npc);
+ mRemoveNPCs[mNumRemoveNPCs].driver = driver;
+ mNumRemoveNPCs++;
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// MissionObjective::RegisterLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Locator* locator, int& hudIndex, bool primary, HudMapIcon::eIconType icon )
+//
+// Return: void
+//
+//=============================================================================
+void MissionObjective::RegisterLocator( Locator* locator,
+ int& hudIndex,
+ bool primary,
+ HudMapIcon::eIconType icon )
+{
+ rmt::Vector destLoc;
+ locator->GetLocation( &destLoc );
+
+ RegisterPosition( destLoc, hudIndex, primary, icon, locator );
+}
+
+//=============================================================================
+// MissionObjective::RegisterPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& pos, int& hudIndex, bool primary, HudMapIcon::eIconType icon )
+//
+// Return: void
+//
+//=============================================================================
+void MissionObjective::RegisterPosition( const rmt::Vector& pos,
+ int& hudIndex,
+ bool primary,
+ HudMapIcon::eIconType icon,
+ IHudMapIconLocator* hml )
+{
+ CGuiScreenMultiHud* currentHud = GetCurrentHud();
+ if( currentHud )
+ {
+ hudIndex = currentHud->GetHudMap( 0 )->RegisterIcon( icon, pos, hml, primary );
+ }
+}
+
+//=============================================================================
+// MissionObjective::UnregisterLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int hudIndex )
+//
+// Return: void
+//
+//=============================================================================
+void MissionObjective::UnregisterLocator( int& hudIndex )
+{
+ // unregister hud map icon
+ //
+ if( hudIndex != -1 )
+ {
+ CGuiScreenMultiHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->GetHudMap( 0 )->UnregisterIcon( hudIndex );
+ }
+
+ hudIndex = -1;
+ }
+}
+
+//=============================================================================
+// MissionObjective::AddEntity
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char* entityname )
+//
+// Return: InstStatEntityDSG*
+//
+//=============================================================================
+InstStatEntityDSG* MissionObjective::AddEntity( char* entityname, rmt::Vector pos )
+{
+MEMTRACK_PUSH_GROUP( "Mission - Entities" );
+ tDrawable* pGeometry = p3d::find<tDrawable>( entityname );
+ rAssert( pGeometry != NULL );
+
+ InstStatEntityDSG* pEnt =
+ new( GetGameplayManager()->GetCurrentMissionHeap() )
+ InstStatEntityDSG;
+
+ rAssert( pEnt != NULL );
+
+ //
+ // The entity takes overship of m, so spake Devin
+ //
+ rmt::Matrix* m = new( GetGameplayManager()->GetCurrentMissionHeap() ) rmt::Matrix;
+
+ m->Identity();
+ m->FillTranslate( pos );
+
+ pEnt->LoadSetUp( m, pGeometry );
+
+MEMTRACK_POP_GROUP("Mission - Entities");
+ return pEnt;
+}
+
+//=============================================================================
+// MissionObjective::DrawEntity
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( InstStatEntityDSG* entity )
+//
+// Return: void
+//
+//=============================================================================
+void MissionObjective::DrawEntity( InstStatEntityDSG* entity )
+{
+ rAssert( entity != NULL );
+ GetRenderManager()->pWorldScene()->Add( entity );
+}
+
+//=============================================================================
+// MissionObjective::HideEntity
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( InstStatEntityDSG* entity )
+//
+// Return: void
+//
+//=============================================================================
+void MissionObjective::HideEntity( InstStatEntityDSG* entity )
+{
+ rAssert( entity != NULL );
+ GetRenderManager()->pWorldScene()->Remove( entity );
+}
+
+//=============================================================================
+// MissionObjective::UnregisterPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int& hudIndex )
+//
+// Return: void
+//
+//=============================================================================
+void MissionObjective::UnregisterPosition( int& hudIndex )
+{
+ UnregisterLocator( hudIndex );
+}
+
+//=============================================================================
+// MissionObjective::ChangeIconType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int hudIndex, HudMapIcon::eIconType type )
+//
+// Return: void
+//
+//=============================================================================
+int MissionObjective::ChangeIconType( int hudIndex, HudMapIcon::eIconType type )
+{
+ if( hudIndex != -1 )
+ {
+ CGuiScreenMultiHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ return( currentHud->GetHudMap( 0 )->ChangeIconType( hudIndex, type ) );
+ }
+ }
+
+ return -1;
+}
+
+//=============================================================================
+// MissionObjective::GetObjectiveType
+//=============================================================================
+// Description: return what type of objective this is
+//
+// Parameters: none
+//
+// Return: ObjectiveTypeEnum - the type of this objective
+//
+//=============================================================================
+MissionObjective::ObjectiveTypeEnum MissionObjective::GetObjectiveType() const
+{
+ return mObjType;
+}
+
+//=============================================================================
+// MissionObjective::SetObjectiveType
+//=============================================================================
+// Description: set the type of the objective
+//
+// Parameters: objType - the type of objective we're setting
+//
+// Return: NONE
+//
+//=============================================================================
+void MissionObjective::SetObjectiveType( const ObjectiveTypeEnum objType )
+{
+ mObjType = objType;
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void MissionObjective::UnlightPath( SwapArray<RoadManager::PathElement>& orPathRoute )
+{
+// rReleasePrintf("UnLightPath. 0x%x\n", orPathRoute.mpData );
+ int i;
+ for(i=orPathRoute.mUseSize-1; i>-1; i-- )
+ {
+ if( orPathRoute[i].type == RoadManager::ET_INTERSECTION )
+ {
+ ((Intersection*)(orPathRoute[i].elem))->AnimEntity(0)->SetVisibility(false);
+ ((Intersection*)(orPathRoute[i].elem))->AnimEntity(1)->SetVisibility(false);
+ }
+ }
+ for(i=GetAnimEntityDSGManager()->mpFloatingRightWayArrows.mUseSize-1; i>-1; i--)
+ {
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[i]->SetVisibility(false);
+ }
+ for(i=GetAnimEntityDSGManager()->mpFloatingWrongWayArrows.mUseSize-1; i>-1; i--)
+ {
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[i]->SetVisibility(false);
+ }
+
+ orPathRoute.ClearUse();
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0]->SetVisibility(false);
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[0]->SetVisibility(false);
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void MissionObjective::LightCurrentPath(SwapArray<RoadManager::PathElement>& orPathRoute )
+{
+ int i;
+ for(i=orPathRoute.mUseSize-1; i>-1; i-- )
+ {
+ if( orPathRoute[i].type == RoadManager::ET_INTERSECTION )
+ {
+ ((Intersection*)(orPathRoute[i].elem))->AnimEntity(0)->SetVisibility(true);
+ ((Intersection*)(orPathRoute[i].elem))->AnimEntity(1)->SetVisibility(false);
+ }
+ }
+ for(i=GetAnimEntityDSGManager()->mpFloatingRightWayArrows.mUseSize-1; i>-1; i--)
+ {
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[i]->SetVisibility(false);
+ }
+ for(i=GetAnimEntityDSGManager()->mpFloatingWrongWayArrows.mUseSize-1; i>-1; i--)
+ {
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[i]->SetVisibility(false);
+ }
+
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0]->SetVisibility(true);
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[0]->SetVisibility(false);
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void MissionObjective::UnlightCurrentPath(SwapArray<RoadManager::PathElement>& orPathRoute )
+{
+ int i;
+ for(i=orPathRoute.mUseSize-1; i>-1; i-- )
+ {
+ if( orPathRoute[i].type == RoadManager::ET_INTERSECTION )
+ {
+ ((Intersection*)(orPathRoute[i].elem))->AnimEntity(0)->SetVisibility(false);
+ ((Intersection*)(orPathRoute[i].elem))->AnimEntity(1)->SetVisibility(false);
+ }
+ }
+ for(i=GetAnimEntityDSGManager()->mpFloatingRightWayArrows.mUseSize-1; i>-1; i--)
+ {
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[i]->SetVisibility(false);
+ }
+ for(i=GetAnimEntityDSGManager()->mpFloatingWrongWayArrows.mUseSize-1; i>-1; i--)
+ {
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[i]->SetVisibility(false);
+ }
+
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0]->SetVisibility(false);
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[0]->SetVisibility(false);
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void MissionObjective::LightPathRightWay( SwapArray<RoadManager::PathElement>& orPathRoute, int iStart, int iPastEnd )
+{
+ int i;
+ if( DirectionalArrowEnum::INTERSECTION & mArrowType )
+ {
+ iStart--;
+ for( i=iPastEnd-1; i>iStart; i-- )
+ {
+ if( orPathRoute[i].type == RoadManager::ET_INTERSECTION )
+ {
+ ((Intersection*)(orPathRoute[i].elem))->AnimEntity(0)->SetVisibility(true);
+ ((Intersection*)(orPathRoute[i].elem))->AnimEntity(1)->SetVisibility(false);
+ }
+ }
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void MissionObjective::LightPathWrongWay( SwapArray<RoadManager::PathElement>& orPathRoute, int iStart, int iPastEnd )
+{
+ int i;
+
+ if( DirectionalArrowEnum::INTERSECTION & mArrowType )
+ {
+ iStart--;
+ for(i=iPastEnd; i>iStart; i-- )
+ {
+ if( orPathRoute[i].type == RoadManager::ET_INTERSECTION )
+ {
+ ((Intersection*)(orPathRoute[i].elem))->AnimEntity(0)->SetVisibility(false);
+ ((Intersection*)(orPathRoute[i].elem))->AnimEntity(1)->SetVisibility(true);
+ }
+ }
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void MissionObjective::UpdateLightPath( PathStruct& orPathStruct)
+{
+ SwapArray<RoadManager::PathElement>& orPathRoute = orPathStruct.mPathRoute;
+ if(!GetCharacterSheetManager()->QueryNavSystemSetting())
+ {
+ UnlightCurrentPath(orPathRoute);
+ return;
+ }
+ else
+ {
+ //LightCurrentPath(orPathRoute);
+ }
+
+ orPathStruct.mUpdateCount = (orPathStruct.mUpdateCount+1)%orPathStruct.msUpdateModulo;
+ if( orPathStruct.mUpdateCount == 0 )
+ UpdateGroundHeights(orPathRoute);
+
+ UpdateLongRoadArrows(orPathStruct);
+
+ rmt::Vector charPosn, charFacing, iconPosn, iconFacing, tempVect, tempVect2;
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetPosition(charPosn);
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetHeading(charFacing);
+
+ RoadManager::PathElement lastPathElem;
+ RoadSegment* pLastRoadSegment;
+ float lastSegmentT;
+ float lastRoadT;
+ int playerOnPathPosn;
+ float distPlayerFromRoad;
+
+
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetLastPathInfo( lastPathElem,
+ pLastRoadSegment,
+ lastSegmentT,
+ lastRoadT );
+ if(lastPathElem.type == RoadManager::ET_INTERSECTION)
+ {
+ playerOnPathPosn = FindIntersectionInPath( orPathRoute, (Intersection*)lastPathElem.elem );
+ }
+ else
+ {
+ playerOnPathPosn = FindRoadInPath( orPathRoute, (Road*)lastPathElem.elem );
+
+ if(playerOnPathPosn>0)
+ {
+ ((Intersection*)(orPathRoute[playerOnPathPosn-1].elem))->GetLocation(tempVect);
+ tempVect2.Sub( charPosn, tempVect );
+ distPlayerFromRoad = tempVect2.MagnitudeSqr(); //ensure it's out of view first, if we're setting it to red/wrong way
+ if(distPlayerFromRoad<120.0f)
+ playerOnPathPosn--;
+ }
+ }
+
+ if(playerOnPathPosn!=-1)
+ {
+ LightPathWrongWay(orPathRoute, 0, playerOnPathPosn);
+ LightPathRightWay(orPathRoute, playerOnPathPosn, orPathRoute.mUseSize);
+ }
+
+/*
+ if(FindNearestRoadIconAhead(orPathRoute, charPosn, iconPosn, iconFacing))
+ {
+ charFacing.Normalize();
+ iconFacing.Normalize(); //if two per frame kills us, we're already dead.
+
+ if(iconFacing.Dot(charFacing)>-0.3f)
+ {
+ LightPathRightWay(orPathRoute);
+ }
+ else
+ {
+ LightPathWrongWay(orPathRoute);
+ }
+
+ }
+ else
+ {
+// rAssert(false);
+ }
+ */
+}
+//========================================================================
+// missionobjective::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+int MissionObjective::FindIntersectionInPath(SwapArray<RoadManager::PathElement>& irPath, Intersection* ipIntToFind)
+{
+ //
+ // Determine whether the players on the path that we want to light.
+ //
+ for(int i=irPath.mUseSize-1; i>-1; i--)
+ {
+ if(irPath[i].elem == ipIntToFind)
+ return i;
+ }
+ return -1;
+}
+//========================================================================
+// missionobjective::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+int MissionObjective::FindRoadInPath(SwapArray<RoadManager::PathElement>& irPath, Road* ipRoadToFind)
+{
+ //
+ // Determine whether the players on the path that we want to light.
+ //
+ for(int i=irPath.mUseSize-1; i>-1; i--)
+ {
+ if(irPath[i].type == RoadManager::ET_INTERSECTION)
+ continue;
+ if( (((Road*)(irPath[i].elem))->GetSourceIntersection() == ipRoadToFind->GetSourceIntersection()
+ && ((Road*)(irPath[i].elem))->GetDestinationIntersection() == ipRoadToFind->GetDestinationIntersection())
+ ||
+ (((Road*)(irPath[i].elem))->GetSourceIntersection() == ipRoadToFind->GetDestinationIntersection()
+ && ((Road*)(irPath[i].elem))->GetDestinationIntersection() == ipRoadToFind->GetSourceIntersection())
+ ) //found matching path
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+//////////////////////////////////////////////////////////////////////////
+// MissionObjective::UpdateGroundHeights
+//
+// This function is neccessary because not all ground intersects are
+// loaded at any given time, due to memory considerations
+//
+// Should be called sparingly, since it needs to do ground intersects
+// Solution: To be Amortised internally
+//////////////////////////////////////////////////////////////////////////
+void MissionObjective::UpdateGroundHeights( SwapArray<RoadManager::PathElement>& orPathRoute )
+{
+ for(int i=orPathRoute.mUseSize-1; i>-1; i--)
+ {
+ if( orPathRoute[i].type == RoadManager::ET_INTERSECTION )
+ {
+ ((Intersection*)(orPathRoute[i].elem))->AnimEntity(0)->PlaceOnGround(0.2f, false);
+ ((Intersection*)(orPathRoute[i].elem))->AnimEntity(1)->PlaceOnGround(0.2f, false);
+ }
+ }
+
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void MissionObjective::UpdateLongRoadArrows( PathStruct& orPathStruct )
+{
+ SwapArray<RoadManager::PathElement>& orPathRoute = orPathStruct.mPathRoute;
+
+ if( !(DirectionalArrowEnum::INTERSECTION & mArrowType) )
+ return;
+
+ //This is a temporary hack to prevent crashes, when the list is size 1
+ // it will be removed as soon as this system is cleaned up to follow paths
+ // arbitrarily instead of at the quantised road/intersection level.
+ if( orPathRoute.mUseSize<2 )
+ return;
+
+ RoadManager::PathElement lastPathElem;
+ RoadSegment* pLastRoadSegment;
+ float lastSegmentT;
+ float lastRoadT;
+ rmt::Vector playerPosn;
+
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetLastPathInfo( lastPathElem,
+ pLastRoadSegment,
+ lastSegmentT,
+ lastRoadT );
+
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetPosition(playerPosn);
+ if(lastPathElem.type == RoadManager::ET_INTERSECTION)
+ {
+ return;
+ }
+
+ //
+ // Determine whether the players on the path that we want to light.
+ //
+ for(int i=orPathRoute.mUseSize-1; i>-1; i--)
+ {
+ if(orPathRoute[i].type == RoadManager::ET_INTERSECTION)
+ continue;
+ if( (((Road*)(orPathRoute[i].elem))->GetSourceIntersection() == ((Road*)(lastPathElem.elem))->GetSourceIntersection()
+ && ((Road*)(orPathRoute[i].elem))->GetDestinationIntersection() == ((Road*)(lastPathElem.elem))->GetDestinationIntersection())
+ ||
+ (((Road*)(orPathRoute[i].elem))->GetSourceIntersection() == ((Road*)(lastPathElem.elem))->GetDestinationIntersection()
+ && ((Road*)(orPathRoute[i].elem))->GetDestinationIntersection() == ((Road*)(lastPathElem.elem))->GetSourceIntersection())
+ ) //found matching path
+ {
+
+ Road* pRoad = ((Road*)(orPathRoute[i].elem));
+ float roadLen = pRoad->GetRoadLength();
+
+ int numArrowsToPlace = static_cast< int >( ( roadLen / 40.0f ) - 1.0f );
+
+ if(numArrowsToPlace>5) numArrowsToPlace=5;
+ if(numArrowsToPlace<0) numArrowsToPlace=0;
+
+ RoadSegment* pRoadSegmentPlacement = NULL;
+ RoadSegment* pRoadSegReference = NULL;
+ rmt::Vector pathHeading, roadSegPlacementFacing, pathPosn, roadStartPosn, roadEndPosn, tempVect, tempVect2;
+ float distStartToNextRoad, distEndToNextRoad, scaleFactor, distPlayerFromRoad;
+
+ RoadSegment* pRoadStart = pRoad->GetRoadSegment(0);
+ RoadSegment* pRoadEnd = pRoad->GetRoadSegment(pRoad->GetNumRoadSegments()-1);
+
+ int playerRoadSegIndex = 0;
+ pLastRoadSegment->GetPosition(&tempVect);
+ RoadManager::FindClosestPointOnRoad( pRoad, tempVect, tempVect2, distPlayerFromRoad, playerRoadSegIndex );
+
+
+ //
+ //Determine if our path lands on a road heading the wrong direction
+ //If so, we need to invert arrow headings
+ //
+ pRoadEnd->GetPosition(&roadEndPosn);
+ pRoadStart->GetPosition(&roadStartPosn);
+
+ //
+ // Yay! Another Special Case
+ //
+ if(pRoadEnd==pRoadStart)
+ {
+ pRoadStart->GetCorner(0,roadStartPosn);
+ pRoadStart->GetCorner(1,roadEndPosn);
+ }
+
+ if(i>0)
+ {
+ rAssert(orPathRoute[i-1].type == RoadManager::ET_INTERSECTION);
+ ((Intersection*)(orPathRoute[i-1].elem))->GetLocation(tempVect2);
+
+ //invert distances, since we're using the previous intersection in this case
+ tempVect.Sub(tempVect2, roadEndPosn);
+ distStartToNextRoad = tempVect.MagnitudeSqr();
+ tempVect.Sub(tempVect2, roadStartPosn);
+ distEndToNextRoad = tempVect.MagnitudeSqr();
+ }
+ else
+ {
+ if(orPathRoute.mUseSize==1) //return for now, since there's only one road, and we don't have origin, destination info
+ return;
+
+ rAssert(orPathRoute[i+1].type == RoadManager::ET_INTERSECTION);
+ ((Intersection*)(orPathRoute[i+1].elem))->GetLocation(tempVect2);
+
+ tempVect.Sub(tempVect2, roadEndPosn);
+ distEndToNextRoad = tempVect.MagnitudeSqr();
+ tempVect.Sub(tempVect2, roadStartPosn);
+ distStartToNextRoad = tempVect.MagnitudeSqr();
+ }
+
+ if(distStartToNextRoad<distEndToNextRoad)
+ scaleFactor = -1.0f;
+ else
+ scaleFactor = 1.0f;
+
+ /*
+ if(i>0) //if we have an intersection previous
+ {
+ rAssert(orPathRoute[i-1].type == RoadManager::ET_INTERSECTION);
+
+ ((Intersection*)(orPathRoute[i-1].elem))->AnimEntity(0)->GetAnimRootHeading(pathHeading);
+ ((Intersection*)(orPathRoute[i-1].elem))->AnimEntity(0)->GetPosition(&pathPosn);
+ }
+ else
+ {
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0]->GetAnimRootHeading(pathHeading);
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0]->GetPosition(&pathPosn);
+ }
+
+ //
+ // Find out whic part of the road we're closer to, so that we can use that
+ // piece of road for directional reference
+ //
+ pRoadStart->GetPosition(0.0f,0.0f,&roadStartPosn);
+ pRoadEnd->GetPosition( 0.0f,0.0f,&roadEndPosn);
+
+ tempVect.Sub(roadStartPosn,pathPosn);
+ distToStart = tempVect.MagnitudeSqr();
+
+ tempVect.Sub(roadEndPosn,pathPosn);
+ distToEnd = tempVect.MagnitudeSqr();
+
+ if(distToStart<distToEnd)
+ pRoadSegReference = (RoadSegment*)pRoadStart;
+ else
+ pRoadSegReference = (RoadSegment*)pRoadEnd;
+
+ pRoadSegReference->GetEdgeNormal(0,tempVect);
+ if( tempVect.Dot(pathHeading)<0.0f )
+ scaleFactor = -1.0f;
+ else
+ scaleFactor = 1.0f;
+ */
+ for(int j=5; j>0; j--)
+ {
+ if(j<=numArrowsToPlace)
+ {
+ float curDistPlacement = j*40.0f;
+ float realDistPlacement = 0.0f;
+
+ pRoad->FindRoadSegmentAtDist(curDistPlacement, &pRoadSegmentPlacement);
+
+ //if we're at the end of the path, cut arrows off past the goal
+ if(pRoad == orPathStruct.mpTargetRoadSeg->GetRoad() )
+ {
+ if( scaleFactor == 1.0f ) //if we're going forward on the road
+ {
+ if( pRoadSegmentPlacement->GetSegmentIndex() > orPathStruct.mpTargetRoadSeg->GetSegmentIndex() )
+ continue;
+ }
+ else // we're going backward on the road
+ {
+ if( pRoadSegmentPlacement->GetSegmentIndex() < orPathStruct.mpTargetRoadSeg->GetSegmentIndex() )
+ continue;
+ }
+ }
+
+ pRoadSegmentPlacement->GetPosition(0.0f, 0.0f, &tempVect);
+
+ UpdateAnimPosition(GetAnimEntityDSGManager()->mpFloatingRightWayArrows[j], tempVect);
+ UpdateAnimPosition(GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[j], tempVect);
+
+ tempVect2.Sub( playerPosn, tempVect );
+ distPlayerFromRoad = tempVect2.MagnitudeSqr(); //ensure it's out of view first
+ if( distPlayerFromRoad > 120.0f &&
+ ( scaleFactor == 1.0f && ((int)pRoadSegmentPlacement->GetSegmentIndex()) < playerRoadSegIndex
+ || scaleFactor == -1.0f && ((int)pRoadSegmentPlacement->GetSegmentIndex()) > playerRoadSegIndex)
+ )
+ {
+ //we're lighting a road piece further away from the goal than the user
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[j]->SetVisibility(false);
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[j]->SetVisibility(true);
+ }
+ else
+ {
+ //we're lighting a road piece further toward the goal than the user
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[j]->SetVisibility(true);
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[j]->SetVisibility(false);
+ }
+
+ //GetHeadingAlongPath(orPathRoute, i, (RoadSegment*)pRoadSegmentPlacement, tempVect);
+
+ //pRoadSegmentPlacement->GetPosition(0.0f, 0.0f, &tempVect2);
+ //tempVect.Sub( tempVect, tempVect2 );
+
+ pRoadSegmentPlacement->GetEdgeNormal(0,tempVect);
+
+ tempVect.Scale(scaleFactor);
+
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[j]->SetAnimRootHeadingYUp( tempVect );
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[j]->SetAnimRootHeadingYUp( tempVect );
+ /*
+
+ if( pRoadSegmentPlacement != NULL )
+ {
+ pRoadSegmentPlacement->GetPosition(0.0f, 0.0f, &tempVect);
+
+ UpdateAnimPosition(GetAnimEntityDSGManager()->mpFloatingRightWayArrows[j], tempVect);
+ UpdateAnimPosition(GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[j], tempVect);
+
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[j]->SetVisibility(true);
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[j]->SetVisibility(false);
+
+ pRoadSegmentPlacement->GetEdgeNormal(0,tempVect);
+ tempVect.Scale(scaleFactor);
+
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[j]->SetAnimRootHeadingYUp( tempVect );
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[j]->SetAnimRootHeadingYUp( tempVect );
+ }
+ else
+ {
+ rAssert(false);
+ }
+ */
+ }
+ else
+ {
+ tempVect.Set(0.0f,0.0f,0.0f);
+
+ UpdateAnimPosition(GetAnimEntityDSGManager()->mpFloatingRightWayArrows[j], tempVect);
+ UpdateAnimPosition(GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[j], tempVect);
+
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[j]->SetVisibility(false);
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[j]->SetVisibility(false);
+ }
+ }
+
+
+ i=-1;
+ }
+ }
+
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+bool MissionObjective::FindNearestRoadIconAhead
+(
+ SwapArray<RoadManager::PathElement>& orPathRoute,
+ rmt::Vector& irPosn,
+ rmt::Vector& orIconPosn,
+ rmt::Vector& orIconFacing
+)
+{
+ float nearestInter = 36000000.0f;//6000.0f*6000.0f;
+ float temp;
+ rmt::Vector tempPosn;
+ bool retVal = false;
+ int i;
+
+ if( DirectionalArrowEnum::INTERSECTION & mArrowType )
+ {
+ //
+ // Go through the intersections in the path, finding the nearest
+ //
+ for(i=orPathRoute.mUseSize-1; i>-1; i--)
+ {
+ if( orPathRoute[i].type == RoadManager::ET_INTERSECTION )
+ {
+ ((Intersection*)(orPathRoute[i].elem))->GetLocation(orIconPosn);
+ tempPosn.Sub(orIconPosn,irPosn);
+
+ temp = tempPosn.MagnitudeSqr();
+
+ if( temp < nearestInter )
+ {
+ nearestInter = temp;
+ ((Intersection*)(orPathRoute[i].elem))->AnimEntity(0)->GetAnimRootHeading(orIconFacing);
+ }
+ }
+ }
+
+ //
+ // Go through the active right way markers, finding the nearest
+ //
+ for(i=GetAnimEntityDSGManager()->mpFloatingRightWayArrows.mUseSize-1; i>0; i--)
+ {
+ if(!GetAnimEntityDSGManager()->mpFloatingRightWayArrows[i]->GetVisibility())
+ continue;
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[i]->GetPosition(&orIconPosn);
+ tempPosn.Sub(orIconPosn,irPosn);
+
+ temp = tempPosn.MagnitudeSqr();
+
+ if( temp < nearestInter )
+ {
+ nearestInter = temp;
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[i]->GetAnimRootHeading(orIconFacing);
+ }
+ }
+ //
+ // Go through the active wrong way markers, finding the nearest
+ //
+ for(i=GetAnimEntityDSGManager()->mpFloatingWrongWayArrows.mUseSize-1; i>0; i--)
+ {
+ if(!GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[i]->GetVisibility())
+ continue;
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[i]->GetPosition(&orIconPosn);
+ tempPosn.Sub(orIconPosn,irPosn);
+
+ temp = tempPosn.MagnitudeSqr();
+
+ if( temp < nearestInter )
+ {
+ nearestInter = temp;
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[i]->GetAnimRootHeading(orIconFacing);
+ }
+ }
+ }
+
+ if( DirectionalArrowEnum::NEAREST_ROAD & mArrowType )
+ {
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0]->GetPosition(&orIconPosn);
+ tempPosn.Sub(orIconPosn,irPosn);
+
+ temp = tempPosn.MagnitudeSqr();
+
+ if( temp < nearestInter )
+ {
+ nearestInter = temp;
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0]->GetAnimRootHeading(orIconFacing);
+ }
+ }
+
+ if(nearestInter<36000000.0f)
+ retVal = true;
+
+ return retVal;
+}
+//////////////////////////////////////////////////////////////////////////
+void MissionObjective::LightPath
+(
+ rmt::Vector& irTargetPosn,
+ PathStruct& orPathStruct
+)
+{
+ if( DirectionalArrowEnum::NEITHER & mArrowType )
+ return;
+
+ orPathStruct.mUpdateCount=0; //initialize the update for modulo updates (so we dont update arrows on ground height per frame)
+ SwapArray<RoadManager::PathElement>& orPathRoute = orPathStruct.mPathRoute;
+// rReleasePrintf("LightPath. 0x%x\n", orPathRoute.mpData );
+ RoadSegment* pSourceRoadSeg = NULL;
+ RoadSegment* pTargetRoadSeg = NULL;
+ rmt::Vector sourcePosn, targetPosn, closestPosn;
+ float SourceTVal, TargetTVal;
+
+ if( orPathRoute.mUseSize != 0 )
+ {
+ //this shouldn't happen but we are going to ignore it.
+ rTuneAssert(0);
+ return;
+ }
+
+ if( GetInteriorManager()->IsInside() )
+ {
+ //Check for an all-interior mission, in which case,
+ //forget lighting entirely
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetPosition(sourcePosn);
+ sourcePosn.Sub(sourcePosn,irTargetPosn);
+
+ //I'm assured that the interiors ar <100m in size and >=200m from the rest of the world
+ if(sourcePosn.MagnitudeSqr()< 10000.0f)
+ return;
+
+ GetInteriorManager()->ExteriorCharPosn(sourcePosn);
+
+ }
+ else
+ {
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetPosition(sourcePosn);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // If Target is in an interior, and source is not, we want to get
+ // the target's exterior posn
+ //////////////////////////////////////////////////////////////////////////
+ tName interiorName = GetInteriorManager()->ClassifyPoint( irTargetPosn );
+
+ float dummy;
+
+ if(interiorName.GetUID()!= static_cast< tUID >( 0 ) )
+ {
+ ZoneEventLocator* pZEL = p3d::find<ZoneEventLocator>(interiorName.GetUID());
+ pZEL->GetPosition(&targetPosn);
+ GetIntersectManager()->FindClosestRoad(targetPosn, 50.0f, pTargetRoadSeg, dummy );
+ }
+ else
+ {
+ targetPosn = irTargetPosn;
+ }
+
+ GetIntersectManager()->FindClosestRoad(targetPosn, 50.0f, pTargetRoadSeg, dummy );
+ rAssert( pTargetRoadSeg != NULL );
+
+ GetIntersectManager()->FindClosestRoad(sourcePosn, 50.0f, pSourceRoadSeg, dummy );
+ rAssert( pSourceRoadSeg != NULL );
+
+ RoadManager::PathElement sourcePath, targetPath;
+
+ if( pSourceRoadSeg != NULL ) sourcePath.elem = pSourceRoadSeg->GetRoad();
+ sourcePath.type = RoadManager::ET_NORMALROAD;
+
+ if( pTargetRoadSeg != NULL ) targetPath.elem = pTargetRoadSeg->GetRoad();
+ targetPath.type = RoadManager::ET_NORMALROAD;
+
+ //////////////////////////////////////////////////////////////////////////
+ // Determine Source and Target T Values
+ //////////////////////////////////////////////////////////////////////////
+ RoadManager* pRoadManager = RoadManager::GetInstance();
+
+ float closestDist;
+ int closestSegIndex;
+ pRoadManager->FindClosestPointOnRoad( (Road*)sourcePath.elem, sourcePosn,
+ closestPosn, closestDist, closestSegIndex );
+
+ SourceTVal = pRoadManager->DetermineRoadT( pSourceRoadSeg, pRoadManager->DetermineSegmentT( closestPosn, pSourceRoadSeg ) );
+
+ pRoadManager->FindClosestPointOnRoad( (Road*)targetPath.elem, targetPosn,
+ closestPosn, closestDist, closestSegIndex );
+
+ TargetTVal = pRoadManager->DetermineRoadT( pTargetRoadSeg, pRoadManager->DetermineSegmentT( closestPosn, pTargetRoadSeg ) );
+
+ //////////////////////////////////////////////////////////////////////////
+ // Find Path
+ //////////////////////////////////////////////////////////////////////////
+ if( pTargetRoadSeg != NULL && pSourceRoadSeg != NULL )
+ {
+ RoadManager::GetInstance()->FindPathElementsBetween(
+ false,
+ sourcePath, SourceTVal, sourcePosn,
+ targetPath, TargetTVal, targetPosn,
+ orPathRoute );
+ }
+ else
+ {
+ rAssert(false);
+ //we should always have nav info for this
+ }
+
+#ifdef RAD_DEBUG
+ pSourceRoadSeg->GetPosition( &mPathStart );
+ pTargetRoadSeg->GetPosition( &mPathEnd );
+#endif
+ orPathStruct.mpTargetRoadSeg = pTargetRoadSeg;
+ orPathStruct.mTargetPosn = targetPosn;
+
+ AnimEntityDSG* pAnimEDSG;
+ AnimEntityDSG* pWrongwayAnimDSG;
+ rmt::Vector pathHeading;
+ rmt::Vector pathTemp;
+ int i;
+
+ if( DirectionalArrowEnum::INTERSECTION & mArrowType )
+ {
+ for( i=orPathRoute.mUseSize-1; i>-1; i-- )
+ {
+ //Set visibility and align arrows for path highlighting
+ if( orPathRoute[i].type == RoadManager::ET_INTERSECTION )
+ {
+ ((Intersection*)(orPathRoute[i].elem))->GetLocation(sourcePosn);
+ pWrongwayAnimDSG = ((Intersection*)(orPathRoute[i].elem))->AnimEntity(1);
+ pAnimEDSG = ((Intersection*)(orPathRoute[i].elem))->AnimEntity(0);
+ pAnimEDSG->SetVisibility(true);
+ if( i+2 < orPathRoute.mUseSize )
+ //There's definitely some(other intersection) beyond this intersection
+ {
+ if( orPathRoute[i+1].type == RoadManager::ET_INTERSECTION )
+ {
+ ((Intersection*)(orPathRoute[i+1].elem))->GetLocation(pathHeading);
+ }
+ else
+ {
+ ((Road*)(orPathRoute[i+1].elem))->GetRoadSegment(0)->GetPosition(0.0f,0.0f,&pathHeading);
+ ((Road*)(orPathRoute[i+1].elem))->GetRoadSegment(((Road*)(orPathRoute[i+1].elem))->GetNumRoadSegments()-1)->GetPosition(0.0f, 0.0f,&pathTemp);
+ if( (pathHeading - sourcePosn).MagnitudeSqr() > (pathTemp - sourcePosn).MagnitudeSqr() )
+ {
+ pathHeading = pathTemp;
+ }
+ }
+ }
+ else //No(other intersection) beyond this intersection
+ {
+ //Alpha semi-hack: Doing this because intersections aren't as easy
+ //to find because they're not in the tree. So, only do direct
+ //point if intersection is closer than the nearest road seg.
+
+ float intersectionToTarget = (targetPosn - sourcePosn).MagnitudeSqr();
+
+ ((Road*)(orPathRoute[i+1].elem))->GetRoadSegment(0)->GetPosition(0.0f,0.0f,&pathHeading);
+ ((Road*)(orPathRoute[i+1].elem))->GetRoadSegment(((Road*)(orPathRoute[i+1].elem))->GetNumRoadSegments()-1)->GetPosition(0.0f, 0.0f,&pathTemp);
+ if( (pathHeading - sourcePosn).MagnitudeSqr() > (pathTemp - sourcePosn).MagnitudeSqr() )
+ {
+ pathHeading = pathTemp;
+ }
+
+ if( //intersectionToTarget < (targetPosn - pathHeading).MagnitudeSqr() ||
+ intersectionToTarget < 1600.0f )
+ {
+ pathHeading = targetPosn;
+ }
+ /*
+ if( (targetPosn - sourcePosn).MagnitudeSqr() < 900.0f)
+ {
+ pathHeading = targetPosn;
+ }
+ else
+ {
+ ((Road*)(orPathRoute[i+1].elem))->GetRoadSegment(0)->GetPosition(0.0f,0.0f,&pathHeading);
+ ((Road*)(orPathRoute[i+1].elem))->GetRoadSegment(((Road*)(orPathRoute[i+1].elem))->GetNumRoadSegments()-1)->GetPosition(0.0f, 0.0f,&pathTemp);
+ if( (pathHeading - sourcePosn).MagnitudeSqr() > (pathTemp - sourcePosn).MagnitudeSqr() )
+ {
+ pathHeading = pathTemp;
+ }
+ }
+ */
+ }
+ pathHeading.Sub( pathHeading, sourcePosn );
+ //pAnimEDSG->PlaceOnGround( 0.2f );
+ pAnimEDSG->SetAnimRootHeadingYUp( pathHeading );
+ pWrongwayAnimDSG->SetAnimRootHeadingYUp( pathHeading );
+ }
+ }
+ }
+ //////////////////////////////////////////////////////////////////////////
+ //move the floating arrow to the correct place
+ //////////////////////////////////////////////////////////////////////////
+
+ pSourceRoadSeg->GetPosition(0.0f, 0.0f, &sourcePosn);
+ UpdateAnimPosition(GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0], sourcePosn);
+ UpdateAnimPosition(GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[0], sourcePosn);
+
+ if( DirectionalArrowEnum::NEAREST_ROAD & mArrowType )
+ {
+/*
+ rmt::Box3D oldBBox;
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0]->GetBoundingBox( &oldBBox );
+ pSourceRoadSeg->GetPosition(0.0f, 0.0f, &sourcePosn);
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0]->SetPosition( sourcePosn );
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0]->PlaceOnGround( 0.2f );
+ GetRenderManager()->pWorldScene()->Move(oldBBox, GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0]);
+
+
+
+
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[0]->GetBoundingBox( &oldBBox );
+ pSourceRoadSeg->GetPosition(0.0f, 0.0f, &sourcePosn);
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[0]->SetPosition( sourcePosn );
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[0]->PlaceOnGround( 0.2f );
+ GetRenderManager()->pWorldScene()->Move(oldBBox, GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[0]);
+*/
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0]->SetVisibility(true);
+
+ i = 0;
+ if(i+1<orPathRoute.mUseSize) //There's something in this path
+ {
+ if( orPathRoute[i+1].type == RoadManager::ET_INTERSECTION )
+ {
+ GetHeadingAlongPath(orPathRoute, i, pSourceRoadSeg, pathHeading);
+ //todo: extract this code into a single function to call from for all long roads too.
+ }
+ else
+ {
+ ((Road*)(orPathRoute[i].elem))->GetRoadSegment(0)->GetPosition(0.0f,0.0f,&pathHeading);
+ ((Road*)(orPathRoute[i].elem))->GetRoadSegment(((Road*)(orPathRoute[i].elem))->GetNumRoadSegments()-1)->GetPosition(0.0f, 0.0f,&pathTemp);
+ if( (pathHeading - sourcePosn).MagnitudeSqr() > (pathTemp - sourcePosn).MagnitudeSqr() )
+ {
+ pathHeading = pathTemp;
+ }
+ }
+ }
+ else //Nothing in this path
+ {
+ pathHeading = targetPosn;
+ }
+ pathHeading.Sub( pathHeading, sourcePosn );
+ GetAnimEntityDSGManager()->mpFloatingRightWayArrows[0]->SetAnimRootHeadingYUp( pathHeading );
+ GetAnimEntityDSGManager()->mpFloatingWrongWayArrows[0]->SetAnimRootHeadingYUp( pathHeading );
+ }
+ if(!GetCharacterSheetManager()->QueryNavSystemSetting())
+ {
+ UnlightCurrentPath(orPathRoute);
+ return;
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+void MissionObjective::GetHeadingAlongPath
+(
+ SwapArray<RoadManager::PathElement>& irPathRoute, int i,
+ RoadSegment* ipSourceRoadSeg,
+ rmt::Vector& orPathHeading
+)
+{
+// bool invertResults;
+// rmt::Vector pathTemp2, pathTemp;
+ rmt::Vector tempVect, tempVect2;
+ rmt::Vector roadEndPosn, roadStartPosn;
+ float distStartToNextRoad, distEndToNextRoad, scaleFactor;
+
+ RoadSegment* pRoadStart = ipSourceRoadSeg->GetRoad()->GetRoadSegment(0);
+ RoadSegment* pRoadEnd = ipSourceRoadSeg->GetRoad()->GetRoadSegment(ipSourceRoadSeg->GetRoad()->GetNumRoadSegments()-1);
+
+ //
+ // Yay! Another Special Case
+ //
+ if(pRoadEnd==pRoadStart)
+ {
+ pRoadStart->GetCorner(0,roadStartPosn);
+ pRoadStart->GetCorner(1,roadEndPosn);
+ }
+ else
+ {
+ pRoadStart->GetPosition( &roadStartPosn );
+ pRoadEnd->GetPosition( &roadEndPosn );
+ }
+
+
+ if(i>0)
+ {
+ rAssert(irPathRoute[i-1].type == RoadManager::ET_INTERSECTION);
+ ((Intersection*)(irPathRoute[i-1].elem))->GetLocation(tempVect2);
+
+ //invert distances, since we're using the previous intersection in this case
+ tempVect.Sub(tempVect2, roadEndPosn);
+ distStartToNextRoad = tempVect.MagnitudeSqr();
+ tempVect.Sub(tempVect2, roadStartPosn);
+ distEndToNextRoad = tempVect.MagnitudeSqr();
+ }
+ else
+ {
+ if(irPathRoute.mUseSize==1) //return for now, since there's only one road, and we don't have origin, destination info
+ return;
+
+ rAssert(irPathRoute[i+1].type == RoadManager::ET_INTERSECTION);
+ ((Intersection*)(irPathRoute[i+1].elem))->GetLocation(tempVect2);
+
+ tempVect.Sub(tempVect2, roadEndPosn);
+ distEndToNextRoad = tempVect.MagnitudeSqr();
+ tempVect.Sub(tempVect2, roadStartPosn);
+ distStartToNextRoad = tempVect.MagnitudeSqr();
+ }
+
+ if(distStartToNextRoad<distEndToNextRoad)
+ scaleFactor = -1.0f;
+ else
+ scaleFactor = 1.0f;
+
+ ipSourceRoadSeg->GetEdgeNormal(0,orPathHeading);
+
+ orPathHeading.Scale(scaleFactor);
+ ipSourceRoadSeg->GetPosition(0.0f, 0.0f, &tempVect);
+ orPathHeading.Add(tempVect);
+
+ /*
+ if(i+1 < irPath.mUseSize)
+ {
+ ((Intersection*)(irPath[i+1].elem))->GetLocation(orPathHeading);
+ if(((Intersection*)(irPath[i+1].elem)) == ipSourceRoadSeg->GetRoad()->GetSourceIntersection())
+ invertResults=true;
+ else
+ invertResults=false;
+ }
+ else
+ {
+ ((Intersection*)(irPath[i-1].elem))->GetLocation(orPathHeading);
+ if(((Intersection*)(irPath[i-1].elem)) == ipSourceRoadSeg->GetRoad()->GetSourceIntersection())
+ invertResults=true;
+ else
+ invertResults=false;
+ }
+
+ if(ipSourceRoadSeg->GetSegmentIndex()+1 > ((Road*)(irPath[i].elem))->GetNumRoadSegments()-1)
+ ipSourceRoadSeg->GetPosition(0.0f,0.0f,&pathTemp);
+ else
+ ((Road*)(irPath[i].elem))->GetRoadSegment(ipSourceRoadSeg->GetSegmentIndex()+1)->GetPosition(0.0f,0.0f,&pathTemp);
+
+ float roadToIntersection1 = (orPathHeading - pathTemp).MagnitudeSqr();
+
+ if(ipSourceRoadSeg->GetSegmentIndex() == 0)
+ ipSourceRoadSeg->GetPosition(0.0f,0.0f,&pathTemp2);
+ else
+ ((Road*)(irPath[i].elem))->GetRoadSegment(ipSourceRoadSeg->GetSegmentIndex()-1)->GetPosition(0.0f,0.0f,&pathTemp2);
+
+ float roadToIntersection2 = (orPathHeading - pathTemp2).MagnitudeSqr();
+
+ if( roadToIntersection1 < roadToIntersection2 && !invertResults )
+ {
+ orPathHeading = pathTemp;
+ }
+ else
+ {
+ orPathHeading = pathTemp2;
+ }*/
+}
+//////////////////////////////////////////////////////////////////////////
+void MissionObjective::UpdateAnimPosition( AnimEntityDSG* pAnimEntityDSG, rmt::Vector& irPosn )
+{
+ rmt::Box3D oldBBox;
+ pAnimEntityDSG->GetBoundingBox( &oldBBox );
+ pAnimEntityDSG->SetPosition( irPosn );
+ pAnimEntityDSG->PlaceOnGround( 0.2f, false );
+ GetRenderManager()->pWorldScene()->Move(oldBBox, pAnimEntityDSG );
+}
+
+//=============================================================================
+// MissionObjective::IsPattyAndSelmaDialog
+//=============================================================================
+// Description: determines if this is a patty and selma dialog objective
+//
+// Parameters: NONE
+//
+// Return: true/false - is this patty and selma talking to one another?
+//
+//=============================================================================
+bool MissionObjective::IsPattyAndSelmaDialog() const
+{
+ unsigned int i = 0;
+ for( ; i < mNumNPCs; ++i )
+ {
+ if( strcmp( mNPCNames[ i ], "patty" ) == 0 )
+ {
+ return true;
+ }
+ if( strcmp( mNPCNames[ i ], "zmale1" ) == 0 )
+ {
+ return true;
+ }
+ if( strcmp( mNPCNames[ i ], "zmale2" ) == 0 )
+ {
+ return true;
+ }
+ if( strcmp( mNPCNames[ i ], "zmale3" ) == 0 )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+//=============================================================================
+// MissionObjective::IsRaceDialog
+//=============================================================================
+// Description: in a race dialog, you will be talking to milhouse, ralph, or
+// nelson
+//
+// Parameters: NONE
+//
+// Return: true/false - is this one of the race characters we're talking
+// to
+//
+//=============================================================================
+bool MissionObjective::IsRaceDialog() const
+{
+ unsigned int i = 0;
+ for( ; i < mNumNPCs; ++i )
+ {
+ if( strcmp( mNPCNames[ i ], "milhouse" ) == 0 )
+ {
+ return true;
+ }
+ if( strcmp( mNPCNames[ i ], "ralph" ) == 0 )
+ {
+ return true;
+ }
+ if( strcmp( mNPCNames[ i ], "nelson" ) == 0 )
+ {
+ return true;
+ }
+ }
+ return false;
+}
diff --git a/game/code/mission/objectives/missionobjective.h b/game/code/mission/objectives/missionobjective.h
new file mode 100644
index 0000000..b7f9691
--- /dev/null
+++ b/game/code/mission/objectives/missionobjective.h
@@ -0,0 +1,228 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: missionobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 15/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef MISSIONOBJECTIVE_H
+#define MISSIONOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <radmath/radmath.hpp>
+#include <events/eventlistener.h>
+#include <presentation/gui/utility/hudmap.h>
+
+#include <radmath/radmath.hpp>
+
+#include <p3d/entity.hpp>
+
+#include <roads/roadmanager.h>
+
+#include <worldsim/character/charactercontroller.h>
+#include <constants/directionalarrowenum.h>
+
+
+//========================================
+// Forward References
+//========================================
+
+class Vehicle;
+class Locator;
+class InstStatEntityDSG;
+class AnimEntityDSG;
+class CarStartLocator;
+class Character;
+class RoadSegment;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+namespace MissionObjectiveNames
+{
+ const char* const Name[] =
+ {
+ "dummy",
+ "goto",
+ "delivery",
+ "follow",
+ "destroy",
+ "race",
+ "losetail",
+ "talkto",
+ "dialogue",
+ "getin",
+ "dump",
+ "fmv",
+ "interior",
+ "coins",
+ "destroyboss",
+ "loadvehicle",
+ "pickupitem",
+ "timer",
+ "buycar",
+ "buyskin",
+ "gooutside"
+ };
+
+}
+
+class MissionObjective : public EventListener
+{
+public:
+
+ enum { MAX_NPC_WAYPOINT_LOCATORS = NPCController::MAX_NPC_WAYPOINTS };
+ enum { MAX_NPCS = 4, MAX_NPC_NAME_LENGTH = 10 };
+
+ MissionObjective();
+ virtual ~MissionObjective();
+
+ enum ObjectiveTypeEnum
+ {
+ OBJ_INVALID,
+ OBJ_GOTO,
+ OBJ_DELIVERY,
+ OBJ_FOLLOW,
+ OBJ_DESTROY,
+ OBJ_RACE,
+ OBJ_LOSETAIL,
+ OBJ_TALKTO,
+ OBJ_DIALOGUE,
+ OBJ_GETIN,
+ OBJ_DUMP,
+ OBJ_FMV,
+ OBJ_INTERIOR,
+ OBJ_COIN,
+ OBJ_DESTROY_BOSS,
+ OBJ_LOAD_VEHICLE,
+ OBJ_PICKUP_ITEM,
+ OBJ_TIMER,
+ OBJ_BUY_CAR,
+ OBJ_BUY_SKIN,
+ OBJ_GO_OUTSIDE,
+ NUM_OBJECTIVES
+ };
+
+ Vehicle* GetRequiredVehicle() { return( mVehicle ); }
+ void SetRequiredVehicle( Vehicle* vehicle ) { mVehicle = vehicle; }
+
+ void Initialize();
+ void Finalize();
+
+ bool IsFinished() { return ( mbFinished ); }
+ void Update( unsigned int elapsedTime );
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void AddNPC( const char* name, const char* location, bool driver);
+ bool AddNPCWaypoint( const char* npc, const char* location );
+ void RemoveNPC( const char* npc, bool driver);
+
+ ObjectiveTypeEnum GetObjectiveType() const;
+ void SetObjectiveType( const ObjectiveTypeEnum objType );
+
+ void SetDirectionalArrowType( DirectionalArrowEnum::TYPE* ipArrowType ){ mArrowType = *ipArrowType; }
+ bool IsPattyAndSelmaDialog() const;
+ bool IsRaceDialog() const;
+
+ DirectionalArrowEnum::TYPE mArrowType;
+
+ void SetRockOut(bool b) { mRockOut = b; }
+
+#ifdef RAD_DEBUG
+ rmt::Vector mPathStart, mPathEnd;
+#endif
+
+protected:
+ struct PathStruct
+ {
+ PathStruct(){ mpTargetRoadSeg = NULL; }
+ SwapArray<RoadManager::PathElement> mPathRoute;
+ rmt::Vector mTargetPosn;
+ RoadSegment* mpTargetRoadSeg;
+ static const char msUpdateModulo = 60;
+ char mUpdateCount;
+ };
+
+ void UpdateLightPath( PathStruct& orPathStruct );
+ void UpdateGroundHeights( SwapArray<RoadManager::PathElement>& orPathRoute );
+ void UpdateLongRoadArrows( PathStruct& orPathStruct );
+ void UpdateAnimPosition( AnimEntityDSG* pAnimEntityDSG, rmt::Vector& irPosn );
+
+ void LightCurrentPath( SwapArray<RoadManager::PathElement>& orPathRoute );
+ void UnlightCurrentPath( SwapArray<RoadManager::PathElement>& orPathRoute );
+ void UnlightPath( SwapArray<RoadManager::PathElement>& orPathRoute );
+ void LightPath( rmt::Vector& irDestinationPosn,
+ PathStruct& orPathStruct );
+ void LightPathRightWay( SwapArray<RoadManager::PathElement>& orPathRoute, int iStart, int iPastEnd );
+ void LightPathWrongWay( SwapArray<RoadManager::PathElement>& orPathRoute, int iStart, int iPastEnd );
+ void GetHeadingAlongPath( SwapArray<RoadManager::PathElement>& irPath, int i,
+ RoadSegment* ipSourceRoadSeg,
+ rmt::Vector& orPathHeading );
+
+ int FindIntersectionInPath( SwapArray<RoadManager::PathElement>& irPath, Intersection* ipIntToFind);
+ int FindRoadInPath( SwapArray<RoadManager::PathElement>& irPath, Road* ipRoadToFind);
+
+ bool FindNearestRoadIconAhead( SwapArray<RoadManager::PathElement>& orPathRoute,
+ rmt::Vector& irPosn,
+ rmt::Vector& orIconPosn,
+ rmt::Vector& irIconFacing);
+
+ virtual void OnInitialize() {};
+ virtual void OnFinalize() {};
+ virtual void OnUpdate( unsigned int elapsedTime ) {};
+ void SetFinished( bool bIsFinished ) { mbFinished = bIsFinished; };
+ bool GetFinished() const { return mbFinished; };
+ void RegisterLocator( Locator* locator, int& hudIndex, bool primary, HudMapIcon::eIconType icon );
+ void RegisterPosition( const rmt::Vector& pos, int& hudIndex, bool primary, HudMapIcon::eIconType icon, IHudMapIconLocator* hml = NULL );
+ void UnregisterLocator( int& hudIndex );
+ void UnregisterPosition( int& hudIndex );
+ int ChangeIconType( int hudIndex, HudMapIcon::eIconType type );
+
+ InstStatEntityDSG* AddEntity( char* entityname, rmt::Vector pos );
+ void DrawEntity( InstStatEntityDSG* entity );
+ void HideEntity( InstStatEntityDSG* entity );
+
+
+private:
+ ObjectiveTypeEnum mObjType;
+ bool mbFinished;
+ Vehicle* mVehicle;
+
+ typedef struct NPCStruct
+ {
+ NPCStruct() : npc( NULL ), numWayLocs( 0 ) {};
+ Character* npc;
+ char locator[32];
+ tName wayLocs[ MAX_NPC_WAYPOINT_LOCATORS ];
+ int numWayLocs;
+ bool driver;
+ } NPC;
+
+ struct NPCRemove
+ {
+ char name[MAX_NPC_NAME_LENGTH ];
+ bool driver;
+ };
+
+ char mNPCNames[MAX_NPCS][MAX_NPC_NAME_LENGTH];
+ NPC mNPCs[ MAX_NPCS ];
+ unsigned int mNumNPCs;
+
+ unsigned int mNumRemoveNPCs;
+ NPCRemove mRemoveNPCs[MAX_NPCS];
+
+ bool mRockOut;
+};
+
+#endif //MISSIONOBJECTIVE_H
diff --git a/game/code/mission/objectives/pickupitemobjective.cpp b/game/code/mission/objectives/pickupitemobjective.cpp
new file mode 100644
index 0000000..042a284
--- /dev/null
+++ b/game/code/mission/objectives/pickupitemobjective.cpp
@@ -0,0 +1,176 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: PickupItemObjective
+//
+// Description: PickupItemObjective
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <mission/objectives/PickupItemObjective.h>
+#include <ai/actor/actor.h>
+#include <ai/actor/actormanager.h>
+#include <stateprop/statepropdata.hpp>
+#include <mission/gameplaymanager.h>
+#include <atc/atcmanager.h>
+#include <mission/statepropcollectible.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <render/rendermanager/rendermanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/avatarmanager.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+const int MAX_NUM_COLLECTIBLE_STATEPROPS = 20;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+PickupItemObjective::PickupItemObjective():
+mTargetObj( NULL ),
+m_WaitingForExplosion( false )
+{
+
+}
+
+PickupItemObjective::~PickupItemObjective()
+{
+ if ( mTargetObj )
+ {
+ mTargetObj->RemoveFromDSG();
+ mTargetObj->Release();
+ mTargetObj = NULL;
+ }
+}
+
+void
+PickupItemObjective::SetTarget( const char* target )
+{
+ m_TargetUID = tName::MakeUID( target );
+ // Find the object in the inventory
+ mTargetObj = p3d::find< StatePropCollectible >( m_TargetUID );
+ rTuneAssertMsg( mTargetObj != NULL, "PickupItemObjective - can't find item in inventory!" );
+ mTargetObj->AddRef();
+}
+
+
+void
+PickupItemObjective::OnInitialize()
+{
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_COLLECTED_PROP );
+
+ rAssert( mTargetObj != NULL );
+
+ // Add it to the DSG, since the objective has just started
+ if ( mTargetObj )
+ {
+ mTargetObj->EnableHudIcon( true );
+ mTargetObj->EnableCollisionTesting( true );
+
+ if ( mTargetObj->GetState() == 3 )
+ {
+ m_WaitingForExplosion = true;
+ }
+ else
+ {
+ MoveObjectToStartPosition();
+ }
+ }
+}
+
+void
+PickupItemObjective::OnFinalize()
+{
+ GetEventManager()->RemoveListener( this, EVENT_VEHICLE_COLLECTED_PROP );
+}
+
+void
+PickupItemObjective::OnUpdate( unsigned int elapsedTime )
+{
+ if ( m_WaitingForExplosion )
+ {
+ // The thing no longer exists - ergo, it blew up.
+ if ( mTargetObj->IsInDSG() == false )
+ {
+ // Finally! We are done waiting for this to get reset
+ // Rest it now.
+ MoveObjectToStartPosition();
+ m_WaitingForExplosion = false;
+ }
+ }
+}
+
+void
+PickupItemObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_VEHICLE_COLLECTED_PROP:
+ {
+ Vehicle* vehicle = reinterpret_cast< Vehicle* >( pEventData );
+ // We only care if the user is driving a vehicle, not if its AI
+ if ( vehicle->IsUserDrivingCar() )
+ {
+ StatePropDSG* collectible = vehicle->GetAttachedCollectible();
+ if ( mTargetObj != NULL && collectible == mTargetObj )
+ {
+ SetFinished( true );
+ }
+ }
+ }
+ break;
+ default:
+ rTuneAssertMsg( 0, "PickupItemObjective - illegal event" );
+ break;
+ }
+}
+
+
+void
+PickupItemObjective::MoveObjectToStartPosition()
+{
+ // Is it attached to a vehicle?? We must detach it.
+ int numActiveVehicles;
+ Vehicle** activeVehicleList = NULL;
+ GetVehicleCentral()->GetActiveVehicleList( activeVehicleList, numActiveVehicles );
+ for ( int i = 0 ; i < numActiveVehicles ; i++ )
+ {
+ if ( activeVehicleList[i] != NULL )
+ {
+ Vehicle* v = activeVehicleList[i];
+ if ( v->GetAttachedCollectible() == mTargetObj )
+ {
+ // This car is carrying our stateprop
+ // Remove it from the vehicle
+ v->DetachCollectible( rmt::Vector(0,0,0 ), false );
+ }
+ }
+ }
+
+
+ rAssert( mTargetObj != NULL );
+ // Better make sure we dont try to move an object that isnt in the DSG
+ // This function is safe to call even if it already is in the dsg
+ mTargetObj->AddToDSG();
+ // Move the object to the start position
+ mTargetObj->SetTransform( mTargetObj->GetStartingPosition() );
+ // Reset state, from lord knows what to start state
+ mTargetObj->SetState( 0 );
+ mTargetObj->EnableCollisionVolume( true );
+ ///mTargetObj->GetSimState()->SetControl( sim::simAICtrl );
+} \ No newline at end of file
diff --git a/game/code/mission/objectives/pickupitemobjective.h b/game/code/mission/objectives/pickupitemobjective.h
new file mode 100644
index 0000000..af8e1fe
--- /dev/null
+++ b/game/code/mission/objectives/pickupitemobjective.h
@@ -0,0 +1,66 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pickup.h
+//
+// Description: Pickup an object
+//
+// Author: Michael Riegger
+//
+//=============================================================================
+
+#ifndef PICKUPITEMOBJECTIVE_H
+#define PICKUPITEMOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/objectives/missionobjective.h>
+
+
+//========================================
+// Forward References
+//========================================
+
+class StatePropCollectible;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class PickupItemObjective : public MissionObjective
+{
+public:
+ PickupItemObjective();
+ virtual ~PickupItemObjective();
+
+ void SetTarget( const char* instancename );
+ void SetLocation( const rmt::Matrix& transform );
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+ virtual void OnUpdate( unsigned int elapsedTime );
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ // Moves the target obj back to mItemStartPosition, called in OnInitialize
+ void MoveObjectToStartPosition();
+
+ tUID m_TargetUID;
+ StatePropCollectible* mTargetObj;
+ // If you lose the barrel, the mission should reset instantly
+ // However, the barrel should play out its explosion,
+ // use this flag to indicate when the explosion is still playing, and
+ // we are waiting on it to reset
+ bool m_WaitingForExplosion;
+
+private:
+
+ //Prevent wasteful constructor creation.
+ PickupItemObjective( const PickupItemObjective& objective );
+ PickupItemObjective& operator=( const PickupItemObjective& objective );
+};
+
+#endif //DESTROYOBJECTIVE_H
diff --git a/game/code/mission/objectives/raceobjective.cpp b/game/code/mission/objectives/raceobjective.cpp
new file mode 100644
index 0000000..381ac57
--- /dev/null
+++ b/game/code/mission/objectives/raceobjective.cpp
@@ -0,0 +1,855 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implement RaceObjective
+//
+// History: 23/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radmath/radmath.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <mission/objectives/raceobjective.h>
+#include <mission/gameplaymanager.h>
+#include <mission/mission.h>
+#include <mission/missionstage.h>
+#include <mission/animatedicon.h>
+
+#include <events/eventmanager.h>
+
+#include <meta/eventlocator.h>
+#include <meta/carstartlocator.h>
+
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#include <worldsim/redbrick/vehicle.h>
+
+#include <ai/vehicle/waypointai.h>
+
+#include <roads/road.h>
+
+#include <meta/locator.h>
+#include <meta/triggervolume.h>
+
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+#include <worldsim/vehiclecentral.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// RaceObjective::RaceObjective
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RaceObjective::RaceObjective() :
+ mNextCollectible( 0 ),
+ mNumAIVehicles( 0 ),
+ mMyPosition( 1 ),
+ mNumLaps( 1 ),
+ mNumLapsCompleted( 0 ),
+ mIsGambleRace (false),
+ mParTime(-666), // <<==-- EVIL!
+ mIsTimeTrial( false ),
+ mPlayerSeg( NULL ),
+ mPlayerSegT( 0.0f ),
+ mPlayerRoadT( 0.0f ),
+ mFinishLine( NULL ),
+ mFinishLineEffect( NULL ),
+ mFinishActive( false )
+{
+ mPlayerElem.elem = NULL;
+}
+
+//==============================================================================
+// RaceObjective::~RaceObjective
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RaceObjective::~RaceObjective()
+{
+}
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+void RaceObjective::OnInitialize()
+{
+ CollectibleObjective::OnInitialize();
+
+ for( unsigned int i = 0; i < GetNumCollectibles(); i++ )
+ {
+ rAssert( mCollectibles[ i ].pLocator != NULL );
+
+ rmt::Vector locPos;
+ mCollectibles[ i ].pLocator->GetPosition( &locPos );
+
+ //////////////////////////////////////////////////
+ // Get what the locator was placed on & other info
+ //
+ // NOTE: We assume that a collectible isn't going to move..
+ // and that it's always either on a road segment or
+ // an intersection... This is safe to do because
+ // the information below is only going to be used
+ // for race objectives (a subclass of collectibleobjective)
+ // which requires that its collectibles don't move around.
+ // It's not that bad to fix it if this changes... we
+ // will simply need to re-invoke FindClosestPathElement
+ // every frame, as the collectible moves around and
+ // remember the last valid (non-off-road) values.
+ //
+
+ RoadSegment* seg = NULL;
+ float segT = 0.0f;
+ float roadT = 0.0f;
+ RoadManager::PathElement closestElem;
+ closestElem.elem = NULL;
+
+ bool succeeded = VehicleAI::FindClosestPathElement( locPos, closestElem, seg, segT, roadT, true );
+ if( !succeeded )
+ {
+ char msg[512];
+ sprintf( msg, "Locator at (%0.1f,%0.1f,%0.1f) must either be placed on a roadsegment "
+ "or in an intersection! Woe be the designer who placed down this locator! "
+ "For now, the closest road segment will be chosen instead.\n",
+ locPos.x, locPos.y, -1 * locPos.z );
+ rTuneAssertMsg( false, msg );
+
+ RoadSegment* closestSeg = NULL;
+ float dummy;
+ GetIntersectManager()->FindClosestRoad( locPos, 100.0f, closestSeg, dummy );
+
+ seg = (RoadSegment*) closestSeg;
+ segT = RoadManager::DetermineSegmentT( locPos, seg );
+ roadT = RoadManager::DetermineRoadT( seg, segT );
+ closestElem.elem = seg->GetRoad();
+ closestElem.type = RoadManager::ET_NORMALROAD;
+ }
+
+ mCollectibles[ i ].elem = closestElem;
+ mCollectibles[ i ].seg = seg;
+ mCollectibles[ i ].segT = segT;
+ mCollectibles[ i ].roadT = roadT;
+
+ rAssert( mCollectibles[ i ].elem.elem != NULL );
+
+ }
+
+}
+
+
+//=============================================================================
+// RaceObjective::OnInitCollectibles
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RaceObjective::OnInitCollectibles()
+{
+ rAssert( GetCollectibleLocator( 0 ) != NULL );
+ Activate( 0, true, true, HudMapIcon::ICON_FLAG_WAYPOINT );
+
+ MissionStage* ms = GetGameplayManager()->GetCurrentMission()->GetCurrentStage();
+
+ unsigned int i;
+ for( i = 1; i < GetNumCollectibles(); i++ )
+ {
+ rAssert( GetCollectibleLocator( i ) != NULL );
+
+ if ( (mNumLapsCompleted == (mNumLaps - 1)) && ( i == GetNumCollectibles() - 1) ) //Last one
+ {
+ bool drawFinish = mFinishLine != NULL;
+
+ //Don't draw this.
+ Activate( i, false, false, HudMapIcon::ICON_FLAG_WAYPOINT, !drawFinish );
+
+ //Draw this!
+ if ( mFinishLine != NULL && !mFinishActive )
+ {
+ mFinishLine->ShouldRender( true );
+ mFinishActive = true;
+ }
+ }
+ else
+ {
+ Activate( i, false, false, HudMapIcon::ICON_COLLECTIBLE, true );
+ }
+ }
+}
+
+//=============================================================================
+// RaceObjective::OnInitCollectibleObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RaceObjective::OnInitCollectibleObjective()
+{
+ rAssert( mNextCollectible < GetNumCollectibles() );
+ mNextCollectible = 0;
+ MissionStage* ms = GetGameplayManager()->GetCurrentMission()->GetCurrentStage();
+ mNumAIVehicles = ms->GetNumVehicles();
+ mMyPosition = mNumAIVehicles + 1;
+
+ mPlayerElem.elem = NULL;
+ mPlayerSeg = NULL;
+ mPlayerSegT = 0.0f;
+ mPlayerRoadT = 0.0f;
+
+ unsigned int i;
+ for ( i = 0; i < mNumAIVehicles; ++i )
+ {
+ mAIRaceCars[ i ].raceCar = ms->GetVehicle( i );
+ (mAIRaceCars[ i ].raceCar)->AddRef();
+ VehicleAI* vAI = GetVehicleCentral()->GetVehicleAI( mAIRaceCars[ i ].raceCar );
+ rAssert( vAI );
+
+ WaypointAI* wpAI = dynamic_cast<WaypointAI*>(vAI);
+ rAssert( wpAI );
+
+ wpAI->SetCurrentCollectible( 0 );
+ wpAI->SetCurrentLap( 0 );
+
+ mAIRaceCars[ i ].raceCarAI = wpAI;
+
+ mAIRaceCars[ i ].mAnimatedIcon = new AnimatedIcon();
+ rmt::Vector carPos;
+ mAIRaceCars[ i ].raceCar->GetPosition( &carPos );
+ mAIRaceCars[ i ].mAnimatedIcon->Init( ARROW_RACE, carPos );
+ mAIRaceCars[ i ].mAnimatedIcon->ScaleByCameraDistance( MIN_ARROW_SCALE, MAX_ARROW_SCALE, MIN_ARROW_SCALE_DIST, MAX_ARROW_SCALE_DIST );
+ }
+
+ CGuiScreenHud* cgsmh = GetCurrentHud();
+
+ mIsTimeTrial = mNumAIVehicles == 0;
+
+ if( !mIsTimeTrial )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_RACE_POSITION );
+
+ if ( cgsmh )
+ {
+ cgsmh->SetRacePosition( mMyPosition, mNumAIVehicles + 1 );
+ }
+ }
+
+ if( mIsGambleRace )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_PAR_TIME );
+ }
+
+ mNumLapsCompleted = 0;
+
+ if ( mNumLaps > 1 )
+ {
+ //Let's throw up the lap hud.
+ GetGuiSystem()->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_LAP_COUNTER );
+ if ( cgsmh )
+ {
+ cgsmh->SetLap( mNumLapsCompleted + 1, mNumLaps );
+ }
+ }
+
+ //Set up the finih line
+ CarStartLocator* finish = p3d::find<CarStartLocator>("race_finish");
+ if ( finish )
+ {
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+ //Find and setup the finishline.
+ rAssert( mFinishLine == NULL );
+ mFinishLine = new AnimatedIcon();
+
+ rmt::Vector finishPos;
+ finish->GetLocation( &finishPos );
+
+ float heading = finish->GetRotation();
+
+ rmt::Matrix mat;
+ mat.Identity();
+ mat.FillRotateXYZ( 0.0f, heading, 0.0f );
+ mat.FillTranslate( finishPos );
+
+ mFinishLine->Init( "finish_line", mat, false, false );
+
+ //And the effect
+ mFinishLineEffect = new AnimatedIcon();
+ mFinishLineEffect->Init( "mission_col", mat, false, true ); //One shot
+
+ HeapMgr()->PopHeap( GMA_LEVEL_MISSION );
+ }
+
+ SetFocus( 0 );
+
+ mFinishActive = false;
+}
+
+//=============================================================================
+// RaceObjective::OnFinalizeCollectibleObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RaceObjective::OnFinalizeCollectibleObjective()
+{
+ if ( mNumAIVehicles )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_RACE_POSITION );
+ }
+
+ if( mIsGambleRace )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_PAR_TIME );
+ }
+
+ unsigned int i;
+ for ( i = 0; i < mNumAIVehicles; ++i )
+ {
+ (mAIRaceCars[ i ].raceCar)->Release();
+ mAIRaceCars[ i ].raceCar = NULL;
+ mAIRaceCars[ i ].raceCarAI = NULL;
+
+ delete mAIRaceCars[ i ].mAnimatedIcon;
+ mAIRaceCars[ i ].mAnimatedIcon = NULL;
+ }
+
+ mNumAIVehicles = 0;
+ mNextCollectible = 0;
+ mIsTimeTrial = false;
+
+ if ( mNumLaps > 0 )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_LAP_COUNTER );
+ }
+
+ if ( mFinishLine )
+ {
+ delete mFinishLine;
+ mFinishLine = NULL;
+ }
+
+ if ( mFinishLineEffect )
+ {
+ delete mFinishLineEffect;
+ mFinishLineEffect = NULL;
+ }
+}
+
+//=============================================================================
+// RaceObjective::OnCollection
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int collectibleNum, bool &shouldReset )
+//
+// Return: bool
+//
+//=============================================================================
+bool RaceObjective::OnCollection( unsigned int collectibleNum, bool &shouldReset )
+{
+ if ( collectibleNum == mNextCollectible )
+ {
+ mNextCollectible++;
+
+ if ( mNextCollectible < GetNumCollectibles() )
+ {
+ if ( mNextCollectible == mNumCollectibles - 1 && mFinishLine != NULL && !mFinishActive && mNumLapsCompleted == mNumLaps - 1 )
+ {
+ HudMapIcon::eIconType iconType = HudMapIcon::ICON_FLAG_WAYPOINT;
+
+ if( mFinishLine != NULL )
+ {
+ mFinishLine->ShouldRender( true );
+ mFinishActive = true;
+
+ iconType = HudMapIcon::ICON_FLAG_CHECKERED; // use checkered flag icon instead
+ }
+
+ Activate( mNextCollectible, true, true, iconType, false );
+ }
+ else
+ {
+ Activate( mNextCollectible, true, true, HudMapIcon::ICON_FLAG_WAYPOINT, true );
+ }
+ }
+ else
+ {
+ //Make sure we have laps left.
+ if ( mNumLapsCompleted >= mNumLaps - 1 )
+ {
+ //We're done...
+ mNumLapsCompleted++;
+
+ if ( mFinishLineEffect )
+ {
+ mFinishLineEffect->Reset();
+ mFinishLineEffect->ShouldRender( true );
+ }
+ return true;
+ }
+ else
+ {
+ // need to collect the last collectible
+ //
+ Collect( mNextCollectible - 1, false );
+
+ //Start over on a new lap!
+ ResetCollectibles();
+
+ mNextCollectible = 0;
+ mNumLapsCompleted++;
+
+ Activate( mNextCollectible, true, true, HudMapIcon::ICON_FLAG_WAYPOINT );
+
+ //Do this to prevent the locator from being disabled.
+ shouldReset = true;
+
+ if ( GetCurrentHud() )
+ {
+ GetCurrentHud()->SetLap( mNumLapsCompleted + 1, mNumLaps );
+ }
+ }
+ }
+ SetFocus( mNextCollectible );
+
+ return true;
+ }
+
+ return false;
+}
+
+//=============================================================================
+// RaceObjective::OnUpdateCollectibleObjective
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTimeMilliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void RaceObjective::OnUpdateCollectibleObjective( unsigned int elapsedTimeMilliseconds )
+{
+ unsigned int oldPosition = mMyPosition;
+
+ if ( mNumAIVehicles > 0 )
+ {
+ CalculatePosition();
+ }
+
+ if( mMyPosition < oldPosition )
+ {
+ GetEventManager()->TriggerEvent( EVENT_RACE_PASSED_AI );
+ }
+ else if( mMyPosition > oldPosition )
+ {
+ GetEventManager()->TriggerEvent( EVENT_RACE_GOT_PASSED_BY_AI );
+ }
+
+ if ( mNumAIVehicles > 0 && GetCurrentHud() )
+ {
+ GetCurrentHud()->SetRacePosition( mMyPosition, mNumAIVehicles + 1 );
+ }
+
+ if ( mFinishActive && mFinishLine )
+ {
+ mFinishLine->Update( elapsedTimeMilliseconds );
+ }
+
+ if ( mFinishActive && mFinishLineEffect )
+ {
+ mFinishLineEffect->Update( elapsedTimeMilliseconds );
+ }
+
+ unsigned int i;
+ for ( i = 0; i < mNumAIVehicles; ++i )
+ {
+ rmt::Vector carPos;
+ Vehicle* vehicle = mAIRaceCars[ i ].raceCar;
+ vehicle->GetPosition( &carPos );
+
+ rmt::Box3D bbox;
+ vehicle->GetBoundingBox( &bbox );
+ carPos.y = bbox.high.y;
+
+ mAIRaceCars[ i ].mAnimatedIcon->Move( carPos );
+ mAIRaceCars[ i ].mAnimatedIcon->Update( elapsedTimeMilliseconds );
+ }
+}
+
+
+
+//Return boolean mIsGambleRace
+
+bool RaceObjective::QueryIsGambleRace()
+{
+ return mIsGambleRace;
+}
+
+
+//Sets mIsGambleRace with input vaule boolean
+void RaceObjective::SetGambleRace(bool boolean)
+{
+ mIsGambleRace = boolean;
+}
+
+//Set the Partime or time to beat for gamble races
+void RaceObjective::SetParTime(int seconds)
+{
+ mParTime = seconds;
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->SetParTime( mParTime );
+ }
+}
+
+//Returns mParTime , call is from mission stage
+int RaceObjective::GetParTime()
+{
+ return mParTime;
+}
+
+
+
+
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// RaceObjective::CalculatePosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RaceObjective::CalculatePosition()
+{
+ rAssert( mNumLaps > 0 );
+
+ unsigned int numCollectibles = GetNumCollectibles();
+ if( numCollectibles == 0 )
+ {
+ return;
+ }
+
+ //////////////////////////////////////////////////////////////
+ // Figure out which collectibles have been reached by AI
+ //
+ for( unsigned int i=0; i<mNumAIVehicles; i++ )
+ {
+ WaypointAI* ai = mAIRaceCars[i].raceCarAI;
+ rAssert( ai );
+
+ rmt::Vector aiLoc;
+ ai->GetPosition( &aiLoc );
+
+ unsigned int currCollectible = (unsigned int)(ai->GetCurrentCollectible());
+ if( 0 <= currCollectible && currCollectible < numCollectibles )
+ {
+ //
+ // CollectibleObjective stores Locator*, but if we're
+ // in RaceObjective, the assumption is that it's an
+ // EventLocator (so it has a trigger volume)
+ //
+ rAssert( dynamic_cast<EventLocator*>( (Locator*)GetCollectibleLocator( currCollectible ) ) );
+
+ EventLocator* eloc = (EventLocator*) GetCollectibleLocator( currCollectible );
+ unsigned int triggerCount = eloc->GetNumTriggers();
+
+ bool reachedCollectible = false;
+
+ for( unsigned int j=0; j<triggerCount; j++ )
+ {
+ TriggerVolume* t = eloc->GetTriggerVolume( j );
+ rAssert( t );
+
+ if( t->Contains( aiLoc ) )
+ {
+ reachedCollectible = true;
+ break;
+ }
+ }
+
+ if( reachedCollectible )
+ {
+ if( currCollectible < (numCollectibles - 1) )
+ {
+ currCollectible++;
+
+ // if we're not at the last collectible, set our sights
+ // on the next collectible...
+ ai->SetCurrentCollectible( currCollectible );
+
+ // Broadcast how many waypoints we have left, so we can
+ // tell if the race is close
+ int numCollectiblesLeft = numCollectibles - currCollectible - 1;
+ GetEventManager()->TriggerEvent(
+ EVENT_WAYAI_HIT_CHECKPOINT, static_cast<void*>(&numCollectiblesLeft) );
+ }
+ else
+ {
+ // if we're at the last collectible, then...
+
+ // we've completed a lap, so increment
+ ai->SetCurrentLap( ai->GetCurrentLap() + 1 );
+
+ // no more laps... we're done!
+ if( ai->GetCurrentLap() >= mNumLaps )
+ {
+ GetEventManager()->TriggerEvent(
+ EVENT_WAYAI_AT_DESTINATION, (void*) ai->GetVehicle() );
+ ai->SetActive( false );
+ }
+ else
+ {
+ ai->SetCurrentCollectible( 0 ); // start a new lap...
+ }
+ }
+ } // end of if( reachedCollectible )
+
+ // calculate distance to this collectible
+ rAssert( 0 <= currCollectible && currCollectible < numCollectibles );
+
+ //////////////////////////////////////////////////////
+ // Query the road manager to get the distance
+ RoadManager::PathElement aiElem;
+ aiElem.elem = NULL;
+ RoadSegment* aiSeg = NULL;
+ float aiSegT = 0.0f;
+ float aiRoadT = 0.0f;
+
+ ai->GetRacePathInfo( aiElem, aiSeg, aiSegT, aiRoadT );
+
+ // make sure the AI is on a path element
+ rAssert( aiElem.elem != NULL );
+
+ RoadManager::PathElement collectibleElem;
+ float collectibleRoadT;
+ GetCollectiblePathInfo( currCollectible, collectibleElem, collectibleRoadT );
+
+ rmt::Vector collectiblePos;
+ eloc->GetLocation( &collectiblePos );
+
+
+ // make sure the collectible is on a path element
+ rAssert( collectibleElem.elem != NULL );
+
+ float aiDistToColl = NEAR_INFINITY;
+ if( aiElem.elem != NULL && collectibleElem.elem != NULL )
+ {
+ SwapArray<RoadManager::PathElement> dummy;
+ dummy.Allocate( RoadManager::GetInstance()->GetMaxPathElements() );
+ aiDistToColl = RoadManager::GetInstance()->FindPathElementsBetween(
+ false,
+ aiElem, aiRoadT, aiLoc,
+ collectibleElem, collectibleRoadT, collectiblePos,
+ dummy );
+ }
+ ai->SetDistToCurrentCollectible( aiDistToColl );
+
+ } // end of if( currCollectible is valid )
+ } // end of for-loop over AI vehicles
+
+ ////////////////////////////////////////////////////////////////////
+ // Update my position
+ //
+ if ( mNextCollectible == GetNumCollectibles() )
+ {
+ return;
+ }
+
+ unsigned int numInFront = 0;
+ unsigned int vehiclesWithMe[ MAX_RACECARS ];
+ unsigned int numWithMe = 0;
+ unsigned int numFinished = 0;
+
+ for( unsigned int i = 0; i < mNumAIVehicles; ++i )
+ {
+ int numLapsCompleted = (mAIRaceCars[ i ].raceCarAI)->GetCurrentLap();
+ if ( numLapsCompleted == mNumLaps )
+ {
+ //They're finshed racing
+ numFinished++;
+ }
+ else if ( numLapsCompleted > mNumLapsCompleted )
+ {
+ //This guys finished more laps than I.
+ numInFront++;
+ }
+ else if ( numLapsCompleted == mNumLapsCompleted )
+ {
+ //We're racing the same lap number.
+ int currCollectible = (mAIRaceCars[ i ].raceCarAI)->GetCurrentCollectible();
+
+ int playerCollectible = static_cast<int>( mNextCollectible );
+ if( currCollectible > playerCollectible )
+ {
+ numInFront++;
+ }
+ else if( currCollectible == playerCollectible )
+ {
+ vehiclesWithMe[ numWithMe ] = i;
+ numWithMe++;
+ }
+ }
+ }
+
+ mMyPosition = 1 + numInFront + numFinished;
+
+ //Now, who is actually in front of me going to the same waypoint?
+ const Locator* collectibleLoc = GetCollectibleLocator( mNextCollectible );
+ if( collectibleLoc == NULL )
+ {
+ rAssert( false ); //Why?
+ return;
+ }
+
+ ////////////////////////////////////////////////////////
+ // Find out player's dist to mNextCollectible
+ //
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( player );
+
+ RoadManager::PathElement playerElem;
+ playerElem.elem = NULL;
+ RoadSegment* playerSeg = NULL;
+ float playerSegT = 0.0f;
+ float playerRoadT = 0.0f;
+
+ player->GetLastPathInfo( playerElem, playerSeg, playerSegT, playerRoadT );
+ // update only if value is valid
+ if( playerElem.elem != NULL )
+ {
+ // update old values only if the new values are good..
+ if( mPlayerElem != playerElem )
+ {
+ mPlayerElem = playerElem;
+ }
+ if( playerSeg )
+ {
+ if( mPlayerSeg != playerSeg )
+ {
+ mPlayerSeg = playerSeg;
+ }
+ mPlayerSegT = playerSegT;
+ mPlayerRoadT = playerRoadT;
+ }
+ }
+
+ rmt::Vector playerPos;
+ player->GetPosition( playerPos );
+
+ //rAssert( mPlayerElem.elem != NULL );
+
+ RoadManager::PathElement collectibleElem;
+ float collectibleRoadT;
+ GetCollectiblePathInfo( mNextCollectible, collectibleElem, collectibleRoadT );
+
+ rmt::Vector collectiblePos;
+ collectibleLoc->GetLocation( &collectiblePos );
+
+ // make sure the collectible is on a path element
+ rAssert( collectibleElem.elem != NULL );
+
+ float playerDistToCurrCollectible = NEAR_INFINITY;
+ if( mPlayerElem.elem != NULL && collectibleElem.elem != NULL )
+ {
+ SwapArray<RoadManager::PathElement> dummy;
+ dummy.Allocate( RoadManager::GetInstance()->GetMaxPathElements() );
+ playerDistToCurrCollectible = RoadManager::GetInstance()->FindPathElementsBetween(
+ false,
+ mPlayerElem, mPlayerRoadT, playerPos,
+ collectibleElem, collectibleRoadT, collectiblePos,
+ dummy );
+ }
+
+ // store this info away in the avatar...
+ player->SetRaceInfo( playerDistToCurrCollectible, mNextCollectible, mNumLapsCompleted );
+
+
+ ////////////////////////////////////////////////////////////
+ // The last set of vehicles to consider are the ones that
+ // are headed to the same collectible (race checkpoint) and
+ // are in the same lap as we are...
+ //
+ for( unsigned int i = 0; i < numWithMe; ++i )
+ {
+ int index = vehiclesWithMe[ i ];
+
+ WaypointAI* ai = mAIRaceCars[ index ].raceCarAI;
+
+ // We wouldn't have added this AI to the vehiclesWithMe list if it
+ // wasn't headed to the same collectible as we are and is in the same lap
+ rAssert( ai->GetCurrentCollectible() == static_cast<int>( mNextCollectible ) );
+ rAssert( ai->GetCurrentLap() == mNumLapsCompleted );
+
+ //This guy and I are racing for the same collectible
+ //Test the dist to the collectible.
+
+ float aiDistToCurrCollectible = ai->GetDistToCurrentCollectible();
+ if( aiDistToCurrCollectible < playerDistToCurrCollectible )
+ {
+ // blast! he's ahead of me... my pos is thus bumped even lower...
+ mMyPosition++;
+ }
+ }
+ return;
+}
diff --git a/game/code/mission/objectives/raceobjective.h b/game/code/mission/objectives/raceobjective.h
new file mode 100644
index 0000000..0627aeb
--- /dev/null
+++ b/game/code/mission/objectives/raceobjective.h
@@ -0,0 +1,114 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: raceobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 23/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef RACEOBJECTIVE_H
+#define RACEOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <mission/objectives/collectibleobjective.h>
+
+//========================================
+// Forward References
+//========================================
+class Locator;
+class Vehicle;
+class WaypointAI;
+class AnimatedIcon;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class RaceObjective : public CollectibleObjective
+{
+public:
+ RaceObjective();
+ virtual ~RaceObjective();
+
+ void SetNumLaps( int numLaps );
+ void SetGambleRace(bool boolean);
+ bool QueryIsGambleRace();
+ void SetParTime(int seconds);
+ int GetParTime();
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnInitCollectibles();
+ virtual void OnInitCollectibleObjective();
+ virtual void OnFinalizeCollectibleObjective();
+ virtual bool OnCollection( unsigned int collectibleNum, bool &shouldReset );
+ virtual void OnUpdateCollectibleObjective( unsigned int elapsedTimeMilliseconds );
+
+private:
+ enum { MAX_RACECARS = 4 };
+
+ unsigned int mNextCollectible;
+ unsigned int mNumAIVehicles;
+
+ struct RaceVehicle
+ {
+ RaceVehicle() : raceCar( NULL ), raceCarAI( NULL ), mAnimatedIcon( NULL ) {};
+ Vehicle* raceCar;
+ WaypointAI* raceCarAI;
+ AnimatedIcon* mAnimatedIcon;
+ };
+ RaceVehicle mAIRaceCars[ MAX_RACECARS ];
+
+ unsigned int mMyPosition;
+
+ int mNumLaps;
+ int mNumLapsCompleted;
+
+
+ //Chuck: for Gambling Races
+ bool mIsGambleRace;
+ int mParTime; //used for GamlbeRaces as the time to beat
+
+
+ bool mIsTimeTrial;
+
+ RoadManager::PathElement mPlayerElem;
+ RoadSegment* mPlayerSeg;
+ float mPlayerSegT;
+ float mPlayerRoadT;
+
+ AnimatedIcon* mFinishLine;
+ AnimatedIcon* mFinishLineEffect;
+ bool mFinishActive;
+
+ void CalculatePosition();
+
+ //Prevent wasteful constructor creation.
+ RaceObjective( const RaceObjective& raceobjective );
+ RaceObjective& operator=( const RaceObjective& raceobjective );
+};
+
+//=============================================================================
+// RaceObjective::SetNumLaps
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int numLaps )
+//
+// Return: void
+//
+//=============================================================================
+inline void RaceObjective::SetNumLaps( int numLaps )
+{
+ mNumLaps = numLaps;
+}
+
+#endif //RACEOBJECTIVE_H
diff --git a/game/code/mission/objectives/talktoobjective.cpp b/game/code/mission/objectives/talktoobjective.cpp
new file mode 100644
index 0000000..c242c74
--- /dev/null
+++ b/game/code/mission/objectives/talktoobjective.cpp
@@ -0,0 +1,406 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: TalkToObjective.cpp
+//
+// Description: Implement TalkToObjective
+//
+// History: 29/08/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/objectives/TalkToObjective.h>
+#include <mission/animatedicon.h>
+
+#include <events/eventmanager.h>
+#include <input/inputmanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <ai/actionbuttonhandler.h>
+#include <ai/actionbuttonmanager.h>
+#include <mission/gameplaymanager.h>
+#include <meta/eventlocator.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/triggervolumetracker.h>
+#include <presentation/gui/ingame/guiscreenletterbox.h>
+
+#include <memory/srrmemory.h>
+
+#include <render/DSG/inststatentitydsg.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <render/culling/worldscene.h>
+#include <worldsim/traffic/trafficmanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// TalkToObjective::TalkToObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+TalkToObjective::TalkToObjective() :
+ mTalkToTarget( NULL ),
+ mHudMapIconID( -1 ),
+ mEvtLoc( NULL ),
+ mAnimatedIcon( NULL ),
+ mIconType( EXCLAMATION ),
+ mYOffset( 0.0f ),
+ mTriggerRadius( 1.3f ),
+ mRenderLayer( RenderEnums::LevelSlot ),
+ mIsDisabled( false ),
+ mActionID(-1),
+ m_PrevActiveGameState(Input::ACTIVE_NONE)
+{
+ mArrowPath.mPathRoute.Allocate( RoadManager::GetInstance()->GetNumRoads() );
+}
+
+//=============================================================================
+// TalkToObjective::~TalkToObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+TalkToObjective::~TalkToObjective()
+{
+}
+
+
+//=============================================================================
+// TalkToObjective::SetTalkToTarget
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name, IconType type, float yOffset, float rad )
+//
+// Return: void
+//
+//=============================================================================
+void TalkToObjective::SetTalkToTarget( const char* name, IconType type, float yOffset, float rad )
+{
+ unsigned int len = strlen(name) < MAX_CHARACTER_NAME - 1? strlen(name) : MAX_CHARACTER_NAME - 2;
+ strncpy( mCharacterName, name, len );
+ mCharacterName[len] = '\0';
+
+ mIconType = type;
+ mYOffset = yOffset;
+ mTriggerRadius = rad;
+
+ //TODO: This should also enable the exclamation mark over his head.
+}
+
+//=============================================================================
+// TalkToObjective::GetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* currentLoc )
+//
+// Return: void
+//
+//=============================================================================
+void TalkToObjective::GetPosition( rmt::Vector* currentLoc )
+{
+ rAssert( mTalkToTarget );
+
+ mTalkToTarget->GetPosition( *currentLoc );
+}
+
+//=============================================================================
+// TalkToObjective::GetHeading
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* heading )
+//
+// Return: void
+//
+//=============================================================================
+void TalkToObjective::GetHeading( rmt::Vector* heading )
+{
+ rAssert( mTalkToTarget );
+
+ mTalkToTarget->GetFacing( *heading );
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// TalkToObjective::OnInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: virtual
+//
+//=============================================================================
+void TalkToObjective::OnInitialize()
+{
+ rAssert( mCharacterName );
+
+ MEMTRACK_PUSH_GROUP( "Mission - TalkToObjective" );
+
+ //Setup the target
+ //This should set the character that the objective is waiting to talk to
+ Character* character = GetCharacterManager()->GetMissionCharacter( mCharacterName );
+
+ if ( character )
+ {
+ mTalkToTarget = character;
+
+ TrafficManager::GetInstance()->AddCharacterToStopFor( mTalkToTarget );
+ }
+ else
+ {
+#ifdef RAD_DEBUG
+ char error[256];
+ sprintf( error, "Can not find character: %s\n", mCharacterName );
+ rAssertMsg( false, error );
+#endif
+ }
+
+
+ //Create the Action Event button to trigger the talky-talk
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+
+ mEvtLoc = new(gma)EventLocator();
+
+ rAssert( mEvtLoc );
+ mEvtLoc->AddRef();
+
+ mEvtLoc->SetName( mCharacterName );
+ mEvtLoc->SetEventType( LocatorEvent::GENERIC_BUTTON_HANDLER_EVENT );
+
+ ActionButton::GenericEventButtonHandler* pABHandler = dynamic_cast<ActionButton::GenericEventButtonHandler*>( ActionButton::GenericEventButtonHandler::NewAction( mEvtLoc, EVENT_TALK_TO_NPC, gma ) );
+ rAssert( pABHandler );
+
+ pABHandler->SetEventData( mTalkToTarget );
+
+ bool bResult = GetActionButtonManager()->AddAction( pABHandler, mActionID );
+
+ rAssert( bResult );
+
+ mEvtLoc->SetData( mActionID );
+
+ // Radius should equal about 1m.
+ //
+ float r = mTriggerRadius;
+ rmt::Vector charPos;
+ mTalkToTarget->GetPosition( charPos );
+ SphereTriggerVolume* pTriggerVolume = new(gma) SphereTriggerVolume( charPos, r );
+
+ mEvtLoc->SetNumTriggers( 1, gma );
+
+ mEvtLoc->AddTriggerVolume( pTriggerVolume );
+ pTriggerVolume->SetLocator( mEvtLoc );
+ pTriggerVolume->SetName( "Talk Trigger" );
+
+ TriggerVolumeTracker::GetInstance()->AddTrigger( pTriggerVolume );
+
+ //This should be the event that the ActionButtonManager sends...
+ GetEventManager()->AddListener( this, EVENT_TALK_TO_NPC );
+
+ rmt::Vector pos;
+ mTalkToTarget->GetPosition( pos );
+
+ pos.y += mYOffset;
+
+ //Put the location on the HUD
+ RegisterPosition( pos, mHudMapIconID, true, HudMapIcon::ICON_MISSION, this );
+
+
+ //========================= SET UP THE ICON
+
+ rAssert( mAnimatedIcon == NULL );
+
+ mAnimatedIcon = new(gma) AnimatedIcon();
+
+ const char* iconName;
+
+ if ( mIconType == EXCLAMATION )
+ {
+ iconName = "exclamation";
+ }
+ else if ( mIconType == GIFT )
+ {
+ iconName = "gift";
+ }
+ else if ( mIconType == DOOR )
+ {
+ iconName = "interior_icon";
+ }
+ else
+ {
+ rAssert( false );
+ MEMTRACK_POP_GROUP("Mission - TalkToObjective");
+ return;
+ }
+
+ mAnimatedIcon->Init( iconName, pos );
+
+ LightPath( charPos, mArrowPath );
+
+
+ MEMTRACK_POP_GROUP("Mission - TalkToObjective");
+}
+
+//=============================================================================
+// TalkToObjective::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: virtual
+//
+//=============================================================================
+void TalkToObjective::OnFinalize()
+{
+ rAssert( mAnimatedIcon );
+
+ if(mActionID != -1)
+ {
+ GetCharacterManager()->GetCharacter(0)->RemoveActionButtonHandler(GetActionButtonManager()->GetActionByIndex(mActionID));
+ GetActionButtonManager()->RemoveActionByIndex(mActionID);
+ mActionID = -1;
+ }
+
+ if( mTalkToTarget != NULL )
+ {
+ TrafficManager::GetInstance()->RemoveCharacterToStopFor( mTalkToTarget );
+ }
+
+ if ( mAnimatedIcon )
+ {
+ delete mAnimatedIcon;
+ mAnimatedIcon = NULL;
+ }
+
+ TriggerVolumeTracker::GetInstance()->RemoveTrigger( mEvtLoc->GetTriggerVolume( 0 ) );
+ mEvtLoc->Release();
+ mEvtLoc = NULL;
+
+ if(m_PrevActiveGameState != Input::ACTIVE_NONE)
+ {
+ GetInputManager()->SetGameState(m_PrevActiveGameState);
+ }
+
+ GetEventManager()->RemoveListener( this, EVENT_TALK_TO_NPC );
+
+ UnregisterPosition( mHudMapIconID );
+
+ UnlightPath( mArrowPath.mPathRoute );
+}
+
+//=============================================================================
+// TalkToObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: virtual
+//
+//=============================================================================
+void TalkToObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ if ( id == EVENT_TALK_TO_NPC )
+ {
+ //Hey hey!
+
+ //Test the event data to make sure it's the right NPC
+
+ if ( mTalkToTarget == (Character*)pEventData )
+ {
+ TriggerVolumeTracker::GetInstance()->RemoveTrigger( mEvtLoc->GetTriggerVolume( 0 ) );
+ SetFinished( true );
+
+ //
+ // Disable controller input so that you can't pause the game
+ //
+ m_PrevActiveGameState = GetInputManager()->GetGameState();
+ GetInputManager()->SetGameState( Input::ACTIVE_NONE );
+ CGuiScreenLetterBox::UnSurpressSkipButton();
+ }
+ }
+}
+
+//=============================================================================
+// TalkToObjective::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void TalkToObjective::OnUpdate( unsigned int elapsedTime )
+{
+ //Update the position of the bv...
+ rmt::Vector charPos;
+ mTalkToTarget->GetPosition( charPos );
+
+ if ( mTalkToTarget->IsBusy() && !mIsDisabled )
+ {
+ //Turn off the trigger volume.
+ TriggerVolumeTracker::GetInstance()->RemoveTrigger( mEvtLoc->GetTriggerVolume( 0 ) );
+ mIsDisabled = true;
+ }
+ else if ( !mTalkToTarget->IsBusy() && mIsDisabled )
+ {
+ //Turn it back on.
+ TriggerVolumeTracker::GetInstance()->AddTrigger( mEvtLoc->GetTriggerVolume( 0 ) );
+ mIsDisabled = false;
+ }
+
+ if ( !mIsDisabled )
+ {
+ mEvtLoc->GetTriggerVolume( 0 )->SetPosition( charPos );
+ }
+
+ charPos.y += mYOffset;
+
+ mAnimatedIcon->Move( charPos );
+ mAnimatedIcon->Update( elapsedTime );
+ UpdateLightPath(mArrowPath);
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/mission/objectives/talktoobjective.h b/game/code/mission/objectives/talktoobjective.h
new file mode 100644
index 0000000..a8765c1
--- /dev/null
+++ b/game/code/mission/objectives/talktoobjective.h
@@ -0,0 +1,87 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: talktoobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 29/08/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef TALKTOOBJECTIVE_H
+#define TALKTOOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/objectives/missionobjective.h>
+
+#include <render/enums/renderenums.h>
+
+#include <presentation/gui/utility/hudmap.h>
+#include <input/inputmanager.h>
+//========================================
+// Forward References
+//========================================
+class Character;
+class EventLocator;
+class AnimatedIcon;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class TalkToObjective : public MissionObjective, public IHudMapIconLocator
+{
+public:
+ enum { MAX_CHARACTER_NAME = 64 };
+ enum IconType { EXCLAMATION, GIFT, DOOR };
+
+ TalkToObjective();
+ virtual ~TalkToObjective();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void SetTalkToTarget( const char* name, IconType type, float yOffset, float rad );
+
+ //Interface for IHudMapIconLocator
+ void GetPosition( rmt::Vector* currentLoc );
+ void GetHeading( rmt::Vector* heading );
+
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ PathStruct mArrowPath;
+
+private:
+ char mCharacterName[MAX_CHARACTER_NAME];
+ Character* mTalkToTarget;
+ int mHudMapIconID;
+ EventLocator* mEvtLoc;
+
+ AnimatedIcon* mAnimatedIcon;
+ IconType mIconType;
+ float mYOffset;
+ float mTriggerRadius;
+
+ RenderEnums::LayerEnum mRenderLayer;
+
+ bool mIsDisabled;
+
+ int mActionID;
+
+ //Prevent wasteful constructor creation.
+ TalkToObjective( const TalkToObjective& talktoobjective );
+ TalkToObjective& operator=( const TalkToObjective& talktoobjective );
+
+ Input::ActiveState m_PrevActiveGameState;
+};
+
+
+#endif //TALKTOOBJECTIVE_H
diff --git a/game/code/mission/objectives/timerobjective.cpp b/game/code/mission/objectives/timerobjective.cpp
new file mode 100644
index 0000000..78f7635
--- /dev/null
+++ b/game/code/mission/objectives/timerobjective.cpp
@@ -0,0 +1,142 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: TimerObjective.cpp
+//
+// Description: ImplementTimerObjective
+//
+// History: may 21,2003
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/objectives/timerobjective.h>
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <worldsim/coins/coinmanager.h>
+#include <events/eventmanager.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+//TimerObjective::TimerObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+TimerObjective::TimerObjective()
+
+{
+ mDurationTime =0;
+ mElapsedTime =0;
+
+}
+
+//=============================================================================
+//TimerObjective::~TimerObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+TimerObjective::~TimerObjective()
+{
+
+}
+
+void TimerObjective::SetTimer(unsigned int milliseconds)
+{
+ mDurationTime = milliseconds;
+}
+
+
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+//TimerObjective::OnInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TimerObjective::OnInitialize()
+{
+
+}
+
+//=============================================================================
+//TimerObjective::OnFinalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TimerObjective::OnFinalize()
+{
+
+}
+
+
+void TimerObjective::Update(unsigned int elaspedTime)
+{
+ OnUpdate(elaspedTime);
+}
+
+
+void TimerObjective::OnUpdate(unsigned int elapsedTime)
+{
+ mElapsedTime+=elapsedTime;
+
+ if (mElapsedTime>mDurationTime)
+ {
+ SetFinished(true);
+ }
+}
+
+
+
+
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/mission/objectives/timerobjective.h b/game/code/mission/objectives/timerobjective.h
new file mode 100644
index 0000000..382998f
--- /dev/null
+++ b/game/code/mission/objectives/timerobjective.h
@@ -0,0 +1,56 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: TimerObjective.h
+//
+// Description: Used as a dummy stage that needs to loiter for a few seconds
+//
+// History: May. 21. 2003 + Created -- Chuck C.
+//
+//=============================================================================
+
+#ifndef TIMEROBJECTIVE_H
+#define TIMEROBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/objectives/missionobjective.h>
+#include <events/eventdata.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Timer Objective
+//
+//=============================================================================
+
+class TimerObjective : public MissionObjective
+{
+public:
+
+
+ TimerObjective();
+ virtual ~TimerObjective();
+
+ void SetTimer(unsigned int milliseconds);
+
+protected:
+ virtual void OnInitialize();
+ virtual void OnFinalize();
+ virtual void OnUpdate(unsigned int elapsedTime);
+ virtual void Update(unsigned int elaspedTime);
+private:
+
+ unsigned int mDurationTime;
+ unsigned int mElapsedTime;
+ //Prevent wasteful constructor creation.
+ TimerObjective( const TimerObjective& TimerObjective );
+ TimerObjective& operator=( const TimerObjective& TimerObjective );
+};
+
+
+#endif //COINOBJECTIVE_H
diff --git a/game/code/mission/racepositionbonusobjective.cpp b/game/code/mission/racepositionbonusobjective.cpp
new file mode 100644
index 0000000..b2e0ca6
--- /dev/null
+++ b/game/code/mission/racepositionbonusobjective.cpp
@@ -0,0 +1,179 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: racepositionbonusobjective.cpp
+//
+// Description: Implement RacePositionBonusObjective
+//
+// History: 12/6/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/racepositionbonusobjective.h>
+#include <events/eventmanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// RacePositionBonusObjective::RacePositionBonusObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+RacePositionBonusObjective::RacePositionBonusObjective() :
+ mPosition( 1 ),
+ mDesiredPosition( 1 )
+{
+ SetType( BonusObjective::POSITION );
+}
+
+//=============================================================================
+// RacePositionBonusObjective::~RacePositionBonusObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+RacePositionBonusObjective::~RacePositionBonusObjective()
+{
+}
+
+//=============================================================================
+// RacePositionBonusObjective::Initialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RacePositionBonusObjective::Initialize()
+{
+ GetEventManager()->AddListener( this, EVENT_WAYAI_AT_DESTINATION );
+}
+
+//=============================================================================
+// RacePositionBonusObjective::Finalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RacePositionBonusObjective::Finalize()
+{
+ GetEventManager()->RemoveListener( this, EVENT_WAYAI_AT_DESTINATION );
+}
+
+//=============================================================================
+// RacePositionBonusObjective::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void RacePositionBonusObjective::HandleEvent( EventEnum id, void* pEventData )
+{
+ //We're handling this: EVENT_WAYAI_AT_DESTINATION
+ //Everytime an AI car sends this, we are permanently back one position.
+
+ if ( GetStarted() && GetSuccessful() )
+ {
+ ++mPosition;
+
+ if ( mPosition > mDesiredPosition )
+ {
+ SetSuccessful( false );
+ //TODO: Update the HUD!
+ }
+ }
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// RacePositionBonusObjective::OnReset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RacePositionBonusObjective::OnReset()
+{
+ SetSuccessful( true );
+ mPosition = 1;
+}
+
+//=============================================================================
+// RacePositionBonusObjective::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RacePositionBonusObjective::OnStart()
+{
+ //Turn on the HUD.
+}
+
+//=============================================================================
+// RacePositionBonusObjective::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void RacePositionBonusObjective::OnUpdate( unsigned int milliseconds )
+{
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/mission/racepositionbonusobjective.h b/game/code/mission/racepositionbonusobjective.h
new file mode 100644
index 0000000..78d0d84
--- /dev/null
+++ b/game/code/mission/racepositionbonusobjective.h
@@ -0,0 +1,95 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: racepositionbonusobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 12/6/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef RACEPOSITIONBONUSOBJECTIVE_H
+#define RACEPOSITIONBONUSOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/bonusobjective.h>
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class RacePositionBonusObjective : public BonusObjective, public EventListener
+{
+public:
+ RacePositionBonusObjective();
+ virtual ~RacePositionBonusObjective();
+
+ virtual void Initialize();
+ virtual void Finalize();
+ virtual unsigned int GetNumericData();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void SetDesiredPosition( unsigned int position );
+
+protected:
+ virtual void OnReset();
+ virtual void OnStart();
+ virtual void OnUpdate( unsigned int milliseconds );
+
+private:
+ unsigned int mPosition;
+ unsigned int mDesiredPosition;
+
+ //Prevent wasteful constructor creation.
+ RacePositionBonusObjective( const RacePositionBonusObjective& racepositionbonusobjective );
+ RacePositionBonusObjective& operator=( const RacePositionBonusObjective& racepositionbonusobjective );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// RacePositionBonusObjective::GetNumericData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+inline unsigned int RacePositionBonusObjective::GetNumericData()
+{
+ return mPosition;
+}
+
+//=============================================================================
+// RacePositionBonusObjective::SetDesiredPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int position )
+//
+// Return: void
+//
+//=============================================================================
+inline void RacePositionBonusObjective::SetDesiredPosition( unsigned int position )
+{
+ mDesiredPosition = position;
+}
+
+#endif //RACEPOSITIONBONUSOBJECTIVE_H
diff --git a/game/code/mission/respawnmanager/allrespawnmanager.cpp b/game/code/mission/respawnmanager/allrespawnmanager.cpp
new file mode 100644
index 0000000..2717e67
--- /dev/null
+++ b/game/code/mission/respawnmanager/allrespawnmanager.cpp
@@ -0,0 +1,3 @@
+#include <mission/respawnmanager/respawnmanager.cpp>
+#include <mission/respawnmanager/respawnentity.cpp>
+
diff --git a/game/code/mission/respawnmanager/respawnentity.cpp b/game/code/mission/respawnmanager/respawnentity.cpp
new file mode 100644
index 0000000..5648cc2
--- /dev/null
+++ b/game/code/mission/respawnmanager/respawnentity.cpp
@@ -0,0 +1,87 @@
+//Implementation of Respawn Entity.CPP
+
+#include <mission/respawnmanager/respawnentity.h>
+#include <mission/respawnmanager/respawnmanager.h>
+#include <mission/gameplaymanager.h>
+
+
+//default constructor, rarely used
+RespawnEntity::RespawnEntity()
+{
+ mCollected = false;
+ mCollectedTime = 0;
+ mRespawnTime = 30000; //default respawn of 30 seconds
+ mRespawnEntityType = RespawnEntity::eNull;
+}
+
+
+// the more commonly used constructor
+RespawnEntity::RespawnEntity(RespawnEntity::eRespawnEntity entitytype)
+{
+ mCollected =false;
+ mCollectedTime =0;
+ mRespawnTime = GetGameplayManager()->GetRespawnManager()->GetRespawnTime(entitytype);
+ mRespawnEntityType = entitytype;
+}
+
+RespawnEntity::~RespawnEntity()
+{
+
+}
+
+void RespawnEntity::Update(unsigned int milliseconds)
+{
+ //check if the item has been collected
+ if ( mCollected == true)
+ {
+ //item has been collected so we calculate how much time has passed since
+ //we collected the item
+ mCollectedTime += milliseconds;
+
+ }
+
+}
+
+
+void RespawnEntity::EntityCollected()
+{
+ mCollected=true;
+ mCollectedTime =0;
+}
+
+void RespawnEntity::SetRespawnTime(int milliseconds)
+{
+ mRespawnTime = milliseconds;
+}
+
+void RespawnEntity::SetCollectedTime(int milliseconds)
+{
+ mCollectedTime = milliseconds;
+}
+
+void RespawnEntity::SetRespawnEntity(RespawnEntity::eRespawnEntity respawnentitytype)
+{
+ mRespawnEntityType = respawnentitytype;
+}
+
+bool RespawnEntity::ShouldEntityRespawn()
+{
+ unsigned int respawntimedelta = mRespawnTime;
+
+ //check if enough time has passed
+ if (mCollectedTime >= respawntimedelta)
+ {
+ //enough time has pass , time passed greater or equal to respawntime set by designers
+ //reset states
+ mCollected = false;
+ mCollectedTime = 0;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+
diff --git a/game/code/mission/respawnmanager/respawnentity.h b/game/code/mission/respawnmanager/respawnentity.h
new file mode 100644
index 0000000..9231b49
--- /dev/null
+++ b/game/code/mission/respawnmanager/respawnentity.h
@@ -0,0 +1,58 @@
+#ifndef RESPAWN_ENTITY_H
+#define RESPAWN_ENTITY_H
+
+// Respawn Entity
+// Base Class for objects in the world that respawn in the world such at the wrenches and possibly Nitro for the Super Sprint mode.
+// wasps will either inherit from this class or be controlled by James H. Coin manager.
+
+
+class RespawnEntity
+{
+public:
+ enum eRespawnEntity
+ {
+ eNull,
+ eWrench,
+ eWasp,
+ eCoinBox,
+ eNitro,
+
+ NUMBER_OF_ENUMS
+ };
+
+
+ //update the internal timer
+ virtual void Update (unsigned int milliseconds);
+
+ //call this to decide if entity needs to respawn,
+ //this should probably get called after an update in the derived class
+ bool ShouldEntityRespawn();
+
+ //call this to updated the entity's collected state once player picks up or destroys entity
+ void EntityCollected();
+
+ void SetRespawnTime(int milliseconds);
+ void SetCollectedTime(int milliseconds);
+ void SetRespawnEntity(eRespawnEntity entitytype);
+
+
+ RespawnEntity();
+ //derived classes should use this constructor in their initialization list.
+ RespawnEntity(eRespawnEntity entitytype);
+ virtual ~RespawnEntity();
+private:
+
+ bool mCollected;
+ int mRespawnTime; //in milliseconds
+ unsigned int mCollectedTime; //this a counter used to tally the passing of time from when the object was collected.
+ //its set to 0 at the start then incremented upon update if the mCollected has been set.
+ eRespawnEntity mRespawnEntityType;
+
+
+ //declared by not defined.
+ RespawnEntity(const RespawnEntity&);
+ RespawnEntity& operator= (const RespawnEntity& );
+};
+
+
+#endif // RESPAWN_ENITITY_H
diff --git a/game/code/mission/respawnmanager/respawnmanager.cpp b/game/code/mission/respawnmanager/respawnmanager.cpp
new file mode 100644
index 0000000..6a5d241
--- /dev/null
+++ b/game/code/mission/respawnmanager/respawnmanager.cpp
@@ -0,0 +1,166 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: Respawnmanager.cpp
+//
+// Description: Implementation for the RespawnManager class.
+//
+// History: + Created -- Chuck Chow
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/respawnmanager/respawnmanager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+
+
+
+
+
+
+
+
+
+//==============================================================================
+// RespawnManager::RespawnManager
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+ RespawnManager::RespawnManager()
+{
+ //set some default vaules in milliseconds
+ mWrenchRespawnTime = 30000;
+ mWaspRespawnTime = 20000;
+ mNitroRespawnTime = 15000;
+
+}
+
+
+//==============================================================================
+// RespawnManager::~ReSpawnManager
+//==============================================================================
+//
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+RespawnManager::~RespawnManager()
+{
+
+
+}
+
+
+
+int RespawnManager::SetNitroRespawnTime(int milliseconds)
+{
+ mNitroRespawnTime = milliseconds;
+ return 0;
+}
+
+int RespawnManager::SetWrenchRespawnTime(int milliseconds)
+{
+ mWrenchRespawnTime = milliseconds;
+ return 0;
+}
+
+int RespawnManager::SetWaspRespawnTime(int milliseconds)
+{
+ mWaspRespawnTime =milliseconds;
+ return 0;
+}
+
+int RespawnManager::GetWrenchRespawnTime()
+{
+ return mWrenchRespawnTime;
+}
+
+int RespawnManager::GetNitroRespawnTime()
+{
+ return mNitroRespawnTime;
+}
+
+int RespawnManager::GetWaspRespawnTime()
+{
+ return mWaspRespawnTime;
+}
+
+
+int RespawnManager::GetRespawnTime(RespawnEntity::eRespawnEntity entitytype)
+{
+ switch (entitytype)
+ {
+ case RespawnEntity::eWrench:
+ {
+ return GetWrenchRespawnTime();
+ break;
+ }
+ case RespawnEntity::eNitro:
+ {
+ return GetNitroRespawnTime();
+ break;
+ }
+ case RespawnEntity::eWasp:
+ {
+ return GetWaspRespawnTime();
+ break;
+ }
+ default:
+ {
+ rTuneAssertMsg(0,"Unknown type of RespawnEntity passed in!\n");
+ return -666; //red flag this
+ break;
+ }
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ //******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/mission/respawnmanager/respawnmanager.h b/game/code/mission/respawnmanager/respawnmanager.h
new file mode 100644
index 0000000..981d621
--- /dev/null
+++ b/game/code/mission/respawnmanager/respawnmanager.h
@@ -0,0 +1,51 @@
+#ifndef RESPAWNMANAGER_H
+#define RESPAWNMANAGER_H
+
+#include <mission\respawnmanager\respawnentity.h>
+
+
+//ReSpawnManager.
+
+class RespawnManager
+ {
+ public:
+
+
+ //constructor and destructor
+ RespawnManager();
+ ~RespawnManager();
+
+
+ int SetWrenchRespawnTime(int milliseconds);
+ int SetWaspRespawnTime(int milliseconds);
+ int SetNitroRespawnTime(int milliseconds);
+
+ int GetWrenchRespawnTime();
+ int GetWaspRespawnTime();
+ int GetNitroRespawnTime();
+
+ //called in the constructor of the the RespawnEntity
+ int GetRespawnTime(RespawnEntity::eRespawnEntity entitytype);
+
+
+ private:
+
+ int mWrenchRespawnTime; //in milliseconds
+ int mWaspRespawnTime;
+ int mNitroRespawnTime;
+
+
+
+
+ //private methods
+
+
+ };
+
+
+
+
+#endif //RESPAWNMANAGER_H
+
+
+ \ No newline at end of file
diff --git a/game/code/mission/rewards/allrewards.cpp b/game/code/mission/rewards/allrewards.cpp
new file mode 100644
index 0000000..9097c31
--- /dev/null
+++ b/game/code/mission/rewards/allrewards.cpp
@@ -0,0 +1,3 @@
+#include <mission/rewards/reward.cpp>
+#include <mission/rewards/rewardsmanager.cpp>
+#include <mission/rewards/merchandise.cpp> \ No newline at end of file
diff --git a/game/code/mission/rewards/merchandise.cpp b/game/code/mission/rewards/merchandise.cpp
new file mode 100644
index 0000000..e400c40
--- /dev/null
+++ b/game/code/mission/rewards/merchandise.cpp
@@ -0,0 +1,23 @@
+
+#include <mission\rewards\merchandise.h>
+#include<string.h>
+
+Merchandise::Merchandise ( )
+: Reward(),
+ m_sellerType( INVALID_SELLER_TYPE )
+{
+ mCost =0;
+}
+
+Merchandise::Merchandise(char* name, int level, Reward::eRewardType type, eSellerType sellerType )
+: Reward(name,level,type,eBlank),
+ m_sellerType( sellerType )
+{
+ mCost=0;
+}
+
+
+Merchandise::~Merchandise ( )
+{
+}
+
diff --git a/game/code/mission/rewards/merchandise.h b/game/code/mission/rewards/merchandise.h
new file mode 100644
index 0000000..5200fd8
--- /dev/null
+++ b/game/code/mission/rewards/merchandise.h
@@ -0,0 +1,59 @@
+#ifndef MERCHANDISE_H
+#define MERCHANDISE_H
+
+#include <mission\rewards\reward.h>
+//derived from rewards
+class Merchandise: public Reward
+{
+public:
+ enum eSellerType
+ {
+ INVALID_SELLER_TYPE = -1,
+
+ SELLER_INTERIOR,
+ SELLER_SIMPSON,
+ SELLER_GIL,
+
+ NUM_SELLER_TYPES
+ };
+
+ Merchandise ();
+ Merchandise (char* name, int level, Reward::eRewardType type, eSellerType sellerType );
+ ~Merchandise();
+
+ void SetCost(int cost);
+ int GetCost() const;
+
+ void SetSellerType( eSellerType type );
+ eSellerType GetSellerType() const;
+private:
+ int mCost;
+ eSellerType m_sellerType;
+};
+
+inline void
+Merchandise::SetCost(int cost)
+{
+ mCost= cost;
+}
+
+inline int
+Merchandise::GetCost() const
+{
+ return mCost;
+}
+
+inline void
+Merchandise::SetSellerType( eSellerType type )
+{
+ m_sellerType = type;
+}
+
+inline Merchandise::eSellerType
+Merchandise::GetSellerType() const
+{
+ return m_sellerType;
+}
+
+#endif // MERCHANDISE_H
+
diff --git a/game/code/mission/rewards/reward.cpp b/game/code/mission/rewards/reward.cpp
new file mode 100644
index 0000000..c5ecd12
--- /dev/null
+++ b/game/code/mission/rewards/reward.cpp
@@ -0,0 +1,85 @@
+#include <events/eventmanager.h>
+#include <mission\rewards\reward.h>
+#include <mission\charactersheet\charactersheetmanager.h>
+#include <p3d/entity.hpp>
+#include<string.h>
+
+ Reward::Reward ( )
+ : mUID( 0 ),
+ mLevel( -1 ),
+ mRepairCost( 10 )
+ {
+ mRewardType = eNULL;
+ mQuestType = eBlank;
+ strcpy(mName,"NULL");
+ strcpy(mFile, "NULL");
+ mEarned = false;
+ }
+ Reward::Reward(char* name, int level, Reward::eRewardType type, Reward::eQuestType qtype)
+ : mLevel( level ),
+ mRepairCost( 10 )
+ {
+ mUID = tEntity::MakeUID( name );
+ strncpy(mName,name,16);
+ mName[15] = '\0';
+ mRewardType=type;
+ mQuestType =qtype;
+ strcpy(mFile, "NULL");
+ mEarned = false;
+ }
+
+
+Reward::~Reward ()
+ {
+
+ }
+
+void Reward::SetName (char* name)
+ {
+ mUID = tEntity::MakeUID( name );
+ strncpy(mName,name,16);
+ mName[15] = '\0';
+ }
+
+void Reward::SetRewardType(Reward::eRewardType type)
+ {
+ mRewardType=type;
+ }
+
+
+void Reward::SetQuestType(Reward::eQuestType type)
+ {
+ mQuestType =type;
+ }
+
+void Reward::SetFilename(char* file)
+ {
+ strncpy(mFile,file,64);
+ mName[63] = '\0';
+ }
+
+
+void Reward::UnlockReward()
+{
+ mEarned = true;
+ if( mRewardType == Reward::ALT_PLAYERCAR )
+ {
+ GetCharacterSheetManager()->AddCarToInventory( GetName() );
+
+ if( mQuestType != eDefaultCar )
+ {
+ //
+ // Fire off an event that a new car has been unlocked
+ //
+ GetEventManager()->TriggerEvent( EVENT_UNLOCKED_CAR );
+ }
+ }
+}
+
+
+
+
+
+
+
+
diff --git a/game/code/mission/rewards/reward.h b/game/code/mission/rewards/reward.h
new file mode 100644
index 0000000..4dd4860
--- /dev/null
+++ b/game/code/mission/rewards/reward.h
@@ -0,0 +1,125 @@
+#ifndef REWARD_H
+#define REWARD_H
+
+#include <p3d/p3dtypes.hpp>
+
+//base class for rewards
+class Reward
+ {
+ public:
+ enum eRewardType
+ {
+ eNULL,
+ ALT_SKIN_OTHER,
+ ALT_SKIN_GOOD,
+ ALT_PLAYERCAR,
+ FE_TOY,
+ NUM_REWARDS
+ };
+ enum eQuestType
+ {
+ eBlank,
+ eDefaultCar,
+ eDefaultSkin,
+ eCards,
+ eStreetRace,
+ eBonusMission,
+ eGoldCards, // TC: Is this obsolete now??
+ NUM_QUESTS
+ };
+
+ Reward ();
+ Reward (char* name, int level, Reward::eRewardType type, Reward::eQuestType qtype);
+ virtual ~Reward ();
+ void UnlockReward ();
+ void LockReward ();
+ bool RewardStatus ();
+ tUID GetUID() const;
+ void SetLevel( int level );
+ int GetLevel() const;
+ char* GetName ();
+ void SetName(char* name );
+ char* GetFile ();
+ void SetFilename (char* file );
+ void SetRewardType (Reward::eRewardType type);
+ void SetQuestType(Reward::eQuestType type);
+ eRewardType GetRewardType ( );
+ eQuestType GetQuestType ( );
+ void SetRepairCost( int cost );
+ int GetRepairCost() const;
+ private:
+ tUID mUID;
+ int mLevel;
+ char mName [16];
+ char mFile[64];
+ bool mEarned;
+ eQuestType mQuestType;
+ eRewardType mRewardType;
+ int mRepairCost;
+ };
+
+inline tUID Reward::GetUID() const
+{
+ return mUID;
+}
+
+inline void Reward::SetLevel( int level )
+{
+ mLevel = level;
+}
+
+inline int Reward::GetLevel() const
+{
+ return mLevel;
+}
+
+inline bool Reward::RewardStatus ()
+ {
+ return mEarned;
+ }
+
+inline Reward::eRewardType Reward::GetRewardType ()
+ {
+ return mRewardType;
+ }
+
+inline Reward::eQuestType Reward::GetQuestType ()
+ {
+ return mQuestType;
+ }
+
+
+inline char* Reward::GetName ()
+ {
+ return mName;
+
+ }
+/*
+ inline void Reward::UnlockReward ()
+ {
+ mEarned = true;
+ }
+
+*/
+
+inline void Reward::LockReward ()
+ {
+ mEarned = false;
+ }
+inline char* Reward::GetFile()
+ {
+ return mFile;
+ }
+
+inline void Reward::SetRepairCost( int cost )
+{
+ mRepairCost = cost;
+}
+
+inline int Reward::GetRepairCost() const
+{
+ return mRepairCost;
+}
+
+#endif // REWARD_H
+
diff --git a/game/code/mission/rewards/rewardsmanager.cpp b/game/code/mission/rewards/rewardsmanager.cpp
new file mode 100644
index 0000000..048635c
--- /dev/null
+++ b/game/code/mission/rewards/rewardsmanager.cpp
@@ -0,0 +1,748 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: Rewardsmanager.cpp
+//
+// Description: Implementation for the RewardsManager class.
+//
+// History: + Created -- Chuck Chow
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/rewards/reward.h>
+#include <mission/rewards/merchandise.h.>
+#include <mission/rewards/rewardsmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheet.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/missionscriptloader.h>
+
+#include <string.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+#ifdef RAD_E3
+const char* REWARDS_SCRIPT_FILE = "scripts\\missions\\e3rwrds.mfk";
+#else
+const char* REWARDS_SCRIPT_FILE = "scripts\\missions\\rewards.mfk";
+#endif
+
+// Static pointer to instance of singleton.
+
+RewardsManager* RewardsManager::spInstance = NULL;
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// RewardsManager::CreateInstance
+//==============================================================================
+//
+// Description: Creates the RewardsManager.
+//
+// Parameters: None.
+//
+// Return: Pointer to the RewardsManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+RewardsManager* RewardsManager::CreateInstance()
+{
+ rAssert( spInstance == NULL );
+MEMTRACK_PUSH_GROUP("RewardsManager");
+
+ spInstance = new (GMA_PERSISTENT) RewardsManager;
+ rAssert( spInstance );
+
+MEMTRACK_POP_GROUP("RewardsManager");
+ return spInstance;
+}
+
+//==============================================================================
+// RewardsManager::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the RewardsManager singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the RewardsManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+RewardsManager* RewardsManager::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// RewardsManager::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the RewardsManager.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void RewardsManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete (GMA_PERSISTENT,spInstance);
+ spInstance = NULL;
+}
+
+
+void RewardsManager::InitRewards()
+{
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ for (int i =0; i< MAX_LEVELS +1;i++)
+ {
+ mRewardsList[i].mStreetRace = new Reward;
+ mRewardsList[i].mBonusMission = new Reward;
+ mRewardsList[i].mCards = new Reward;
+ mRewardsList[i].mDefaultCar = new Reward;
+ mRewardsList[i].mDefaultSkin = new Reward;
+ mRewardsList[i].mGoldCards = new Reward;
+
+ mRewardsList[i].mMaxTokensInLevel = 200;
+ mRewardsList[i].mTotalGagsInLevel = 10;
+ mRewardsList[i].mTotalWaspsInLevel = 20;
+ }
+
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+}
+
+
+void RewardsManager::ClearRewards()
+{
+ //re-lock rewards
+ for (int i =0; i< MAX_LEVELS +1;i++)
+ {
+ mRewardsList[i].mStreetRace->LockReward();
+ mRewardsList[i].mBonusMission->LockReward();
+ mRewardsList[i].mCards->LockReward();
+ mRewardsList[i].mDefaultCar->LockReward();
+ mRewardsList[i].mDefaultSkin->LockReward();
+ mRewardsList[i].mGoldCards->LockReward();
+ }
+
+ //relock items from the tokenstore
+ for(int j=0; j<MAX_LEVELS;j++)
+ {
+ for (int k=0;k<MAX_INVENTORY;k++)
+ {
+ mLevelTokenStoreList[j].mInventoryList[k]->LockReward();
+ }
+ }
+
+
+}
+
+void RewardsManager::OnProcessRequestsComplete( void* pUserData )
+{
+ mScriptLoaded = true;
+}
+
+
+void RewardsManager::LoadScript()
+{
+ mScriptLoaded = false;
+ GetMissionScriptLoader()->LoadScriptAsync( const_cast<char*>( REWARDS_SCRIPT_FILE ), this );
+}
+
+
+int RewardsManager::BindReward(char* name,char* filename, Reward::eRewardType rtype, Reward::eQuestType qtype, int level)
+{
+ NameCheck(name);
+ FileNameCheck(filename);
+ LevelCheck(level);
+
+ switch (qtype)
+ {
+ case Reward::eStreetRace:
+ {
+ mRewardsList[level].mStreetRace->SetName(name);
+ mRewardsList[level].mStreetRace->SetLevel(level);
+ mRewardsList[level].mStreetRace->SetFilename(filename);
+ mRewardsList[level].mStreetRace->SetRewardType(rtype);
+ mRewardsList[level].mStreetRace->SetQuestType(qtype);
+
+ break;
+ }
+ case Reward::eBonusMission:
+ {
+ mRewardsList[level].mBonusMission->SetName(name);
+ mRewardsList[level].mBonusMission->SetLevel(level);
+ mRewardsList[level].mBonusMission->SetFilename(filename);
+ mRewardsList[level].mBonusMission->SetRewardType(rtype);
+ mRewardsList[level].mBonusMission->SetQuestType(qtype);
+
+ break;
+ }
+ case Reward::eCards:
+ {
+ mRewardsList[level].mCards->SetName(name);
+ mRewardsList[level].mCards->SetLevel(level);
+ mRewardsList[level].mCards->SetFilename(filename);
+ mRewardsList[level].mCards->SetRewardType(rtype);
+ mRewardsList[level].mCards->SetQuestType(qtype);
+
+ break;
+ }
+ case Reward::eDefaultCar:
+ {
+ mRewardsList[level].mDefaultCar->SetName(name);
+ mRewardsList[level].mDefaultCar->SetLevel(level);
+ mRewardsList[level].mStreetRace->SetLevel(level);
+ mRewardsList[level].mDefaultCar->SetFilename(filename);
+ mRewardsList[level].mDefaultCar->SetRewardType(rtype);
+ mRewardsList[level].mDefaultCar->SetQuestType(qtype);
+
+ break;
+ }
+ case Reward::eDefaultSkin:
+ {
+ mRewardsList[level].mDefaultSkin->SetName(name);
+ mRewardsList[level].mDefaultSkin->SetLevel(level);
+ mRewardsList[level].mDefaultSkin->SetFilename(filename);
+ mRewardsList[level].mDefaultSkin->SetRewardType(rtype);
+ mRewardsList[level].mDefaultSkin->SetQuestType(qtype);
+
+ break;
+ }
+ case Reward::eGoldCards:
+ {
+ mRewardsList[level].mCards->SetName(name);
+ mRewardsList[level].mCards->SetLevel(level);
+ mRewardsList[level].mCards->SetFilename(filename);
+ mRewardsList[level].mCards->SetRewardType(rtype);
+ mRewardsList[level].mCards->SetQuestType(qtype);
+
+ break;
+ }
+ default:
+ {
+ printf("Unknown eQuestType!\n");
+ break;
+ }
+
+ }
+
+ return 0;
+}
+
+
+Reward* RewardsManager::GetReward(int level, Reward::eQuestType qtype)
+{
+ rAssert( mScriptLoaded );
+
+ // MikeR - adding a check for bad input level and returning NULL if
+ // it fails
+ if ( LevelCheck(level) == false )
+ {
+ rTunePrintf( "RewardsManager - GetReward() INVALID LEVEL INDEX!!\n" );
+ return NULL;
+ }
+ switch (qtype)
+ {
+ case Reward::eBonusMission:
+ {
+ return mRewardsList[level].mBonusMission;
+ break;
+ }
+ case Reward::eStreetRace:
+ {
+ return mRewardsList[level].mStreetRace;
+ break;
+ }
+ case Reward::eCards:
+ {
+ return mRewardsList[level].mCards;
+ break;
+ }
+
+ case Reward::eDefaultCar:
+ {
+ return mRewardsList[level].mDefaultCar;
+ break;
+ }
+ case Reward::eDefaultSkin:
+ {
+ return mRewardsList[level].mDefaultSkin;
+ break;
+ }
+ case Reward::eGoldCards:
+ {
+ return mRewardsList[level].mGoldCards;
+ break;
+ }
+ default:
+ {
+ //you shouldnt get this unless you input an invalid quest type
+ rAssert(0);
+ return NULL;
+ }
+ }
+
+ return NULL;
+
+}//end of GetReward
+
+
+Reward* RewardsManager::GetReward( int level, tUID id )
+{
+ // search through all quest types for given level to find reward w/
+ // specified UID
+ //
+ for( int questType = (Reward::eBlank + 1); questType < Reward::NUM_QUESTS; questType++ )
+ {
+ Reward* pReward = this->GetReward( level, static_cast<Reward::eQuestType>( questType ) );
+ if( pReward != NULL && pReward->GetUID() == id )
+ {
+ // found it!
+ //
+ return pReward;
+ }
+ }
+
+ return NULL;
+}
+
+int RewardsManager::SetCarAttributes(char* carname,float speed,float acceleration,float toughness,float stability)
+{
+ //check if the array is full
+ if (mCarAttributeRecordIndex >= MAX_CAR_ATTRIBUTE_RECORDS)
+ {
+ rTuneAssertMsg(0,"Cant Set any more Car Attributes the Records Array is FULL!!!\n");
+ return -1;
+ }
+ else
+ {
+ strncpy(mCarAttributeList[mCarAttributeRecordIndex].mName,carname,16);
+ mCarAttributeList[mCarAttributeRecordIndex].mName[15]= '\0';
+ mCarAttributeList[mCarAttributeRecordIndex].mSpeed = speed;
+ mCarAttributeList[mCarAttributeRecordIndex].mAcceleration = acceleration;
+ mCarAttributeList[mCarAttributeRecordIndex].mToughness = toughness;
+ mCarAttributeList[mCarAttributeRecordIndex].mStability = stability;
+ mCarAttributeRecordIndex++;
+ return 0;
+ }
+}
+
+void RewardsManager::InitCarAttributeRecords( )
+{
+ for (int i=0;i<MAX_CAR_ATTRIBUTE_RECORDS;i++)
+ {
+ strcpy(mCarAttributeList[i].mName,"NULL");
+ mCarAttributeList[i].mSpeed = 0.0f;
+ mCarAttributeList[i].mAcceleration = 0.0f;
+ mCarAttributeList[i].mToughness = 0.0f;
+ mCarAttributeList[i].mStability = 0.0f;
+ }
+ mCarAttributeRecordIndex = 0;
+}
+
+
+
+CarAttributeRecord* RewardsManager::GetCarAttributeRecord(char* carname)
+{
+ rAssert( mScriptLoaded );
+
+ for (int i=0;i <MAX_CAR_ATTRIBUTE_RECORDS;i++)
+ {
+ if (strcmp(mCarAttributeList[i].mName,carname) == 0 )
+ {
+ return &(mCarAttributeList[i]);
+ }
+ }
+ return NULL;
+}
+
+float RewardsManager::ComputeOverallCarRating( CarAttributeRecord* carStats )
+{
+ rAssert( carStats != NULL );
+
+ // TC: [TODO] need to get a designer to come up w/ a proper formula
+ //
+ return (carStats->mAcceleration + carStats->mSpeed + carStats->mStability + carStats->mToughness) / 4.0f;
+}
+
+int RewardsManager::GetNumberOfTokensInLevel(int level)
+{
+ return spInstance->mRewardsList[level].mMaxTokensInLevel;
+}
+
+void RewardsManager::SetTokensInLevel(int level,int tokens)
+{
+ spInstance->mRewardsList[level].mMaxTokensInLevel = tokens;
+}
+
+//returns the number of tokens a event should generate. Ie breaking glass,bee cam etc.
+//I need to determine what to use as input. perhaps a event trigger is the best way.???
+int RewardsManager::GenerateTokens( )
+{
+ //
+ int tokens = 5;
+
+ return tokens;
+}
+
+void RewardsManager::InitTokenStore()
+{
+ for (int i =0; i<MAX_LEVELS;i++)
+ {
+ mLevelTokenStoreList[i].mCounter =0;
+ for (int j=0;j<MAX_INVENTORY;j++)
+ {
+ mLevelTokenStoreList[i].mInventoryList[j] = new (GMA_PERSISTENT) Merchandise;
+ }
+
+ mLevelTokenStoreSearchIndex[ i ] = 0;
+ }
+}
+
+
+int RewardsManager::AddMerchandise(char* name,char* filename,Reward::eRewardType rtype,int level,int cost,Merchandise::eSellerType sellerType)
+{
+
+ //check if the array of merchandise is full
+ if(mLevelTokenStoreList[level].mCounter < MAX_INVENTORY)
+ {
+ mLevelTokenStoreList[level].mInventoryList[mLevelTokenStoreList[level].mCounter]->SetName(name);
+ mLevelTokenStoreList[level].mInventoryList[mLevelTokenStoreList[level].mCounter]->SetLevel(level);
+ mLevelTokenStoreList[level].mInventoryList[mLevelTokenStoreList[level].mCounter]->SetFilename(filename);
+ mLevelTokenStoreList[level].mInventoryList[mLevelTokenStoreList[level].mCounter]->SetRewardType(rtype);
+ mLevelTokenStoreList[level].mInventoryList[mLevelTokenStoreList[level].mCounter]->SetCost(cost);
+ mLevelTokenStoreList[level].mInventoryList[mLevelTokenStoreList[level].mCounter]->SetSellerType(sellerType);
+ mLevelTokenStoreList[level].mCounter++;
+ return 0;
+ }
+ else
+ {
+ //list is full return -1
+ return -1;
+ }
+
+}
+
+
+TokenStoreInventory* RewardsManager::GetTokenStoreInventoryList(int level)
+{
+ LevelCheck(level);
+ return &(mLevelTokenStoreList[level]);
+}
+
+
+Merchandise* RewardsManager::GetMerchandise(int level,const char* name)
+{
+ LevelCheck(level);
+ for (int i=0; i< mLevelTokenStoreList[level].mCounter;i++)
+ {
+ if(strcmp (name,mLevelTokenStoreList[level].mInventoryList[i]->GetName())==0)
+ {
+ return mLevelTokenStoreList[level].mInventoryList[i];
+ }
+ }
+ //got to the end of the array and no match return NULL
+ return NULL;
+}
+
+
+Merchandise* RewardsManager::GetMerchandise(int level,int index)
+{
+ if ( (index >= 0 ) && (index < mLevelTokenStoreList[level].mCounter))
+ {
+ return mLevelTokenStoreList[level].mInventoryList[index];
+ }
+ else
+ {
+ //got to the end of the array and no match return NULL
+ return NULL;
+ }
+}
+
+
+
+int RewardsManager::GetMerchandiseIndex(int level,const char* name)
+{
+ LevelCheck(level);
+ for (int i=0; i< mLevelTokenStoreList[level].mCounter;i++)
+ {
+ if(strcmp (name,mLevelTokenStoreList[level].mInventoryList[i]->GetName())==0)
+ {
+ return i;
+ }
+ }
+ //got to the end of the array and no match return NULL
+ return -1;
+}
+
+Merchandise*
+RewardsManager::FindFirstMerchandise( int level, Merchandise::eSellerType sellerType )
+{
+ // reset search index
+ //
+ mLevelTokenStoreSearchIndex[ level ] = 0;
+
+ return( this->FindNextMerchandise( level, sellerType ) );
+}
+
+Merchandise*
+RewardsManager::FindNextMerchandise( int level, Merchandise::eSellerType sellerType )
+{
+ for( int i = mLevelTokenStoreSearchIndex[ level ]; i < mLevelTokenStoreList[level].mCounter; i++ )
+ {
+ // search for next merchandise w/ specified seller type
+ //
+ Merchandise* merchandise = this->GetMerchandise( level, i );
+ if( merchandise->GetSellerType() == sellerType )
+ {
+ // found it! update current search index
+ //
+ mLevelTokenStoreSearchIndex[ level ] = i + 1;
+
+ // return reference to merchandise
+ //
+ return merchandise;
+ }
+ }
+
+ // merchandise not found, return NULL
+ //
+ return NULL;
+}
+
+bool RewardsManager::BuyMerchandise(int level,const char* name)
+{
+ Merchandise* pMerchandise =NULL;
+ pMerchandise = GetMerchandise(level,name);
+
+ if ( pMerchandise == NULL)
+ {
+ //you asserted here because you are trying to buy an item that doesn't exist
+ rTuneAssert(0);
+ return false;
+ }
+
+ //check if the player has enough tokens to buy the item.
+ int index = -1;
+ index = GetMerchandiseIndex(level,name);
+
+ //if index is -1 we have a problem. chuck
+ rTuneAssert(index != -1);
+
+ if (GetCharacterSheetManager()->GetNumberOfTokens((RenderEnums::LevelEnum) level) < pMerchandise->GetCost() )
+ {
+ //player doesn't have enough tokens to buy the item
+ return false;
+ }
+ else
+ {
+ pMerchandise->UnlockReward();
+ GetCharacterSheetManager()->SubtractTokens((RenderEnums::LevelEnum) level,pMerchandise->GetCost());
+ GetCharacterSheetManager()->SetPurchasedRewards( (RenderEnums::LevelEnum) level,index);
+ return true;
+ }
+
+}
+
+/*
+bool RewardsManager::BuyMerchandise(int level,int index)
+{
+ Merchandise* pMerchandise =NULL;
+ pMerchandise = GetMerchandise(level,index);
+
+ if ( pMerchandise == NULL)
+ {
+ //you asserted here because you are trying to buy an item that doesn't exist
+ rTuneAssert(0);
+ return false;
+ }
+ else
+ {
+ //check if player has enough tokens to buy the object.
+ if (GetCharacterSheetManager()->GetNumberOfTokens((RenderEnums::LevelEnum) level) < pMerchandise->GetCost() )
+ {
+ //player doesn't have enough tokens to buy the item
+ return false;
+ }
+ else
+ {
+ pMerchandise->UnlockReward();
+ GetCharacterSheetManager()->SubtractTokens((RenderEnums::LevelEnum) level,pMerchandise->GetCost());
+ GetCharacterSheetManager()->SetPurchasedRewards( (RenderEnums::LevelEnum) level,index);
+ return true;
+ }
+ }
+}
+*/
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// RewardsManager::RewardsManager
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+RewardsManager::RewardsManager() :
+ mScriptLoaded( false )
+{
+
+ InitRewards();
+ InitCarAttributeRecords();
+ InitTokenStore();
+ mUpdateQue = 0;
+}
+
+
+//==============================================================================
+// RewardsManager::~RewardsManager
+//==============================================================================
+//
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+RewardsManager::~RewardsManager()
+{
+ for (int i = 0; i < MAX_LEVELS+1; i++)
+ {
+ delete mRewardsList[i].mBonusMission;
+ delete mRewardsList[i].mStreetRace;
+ delete mRewardsList[i].mCards;
+ delete mRewardsList[i].mDefaultCar;
+ delete mRewardsList[i].mDefaultSkin;
+ }
+
+ for (int i =0; i<MAX_LEVELS;i++)
+ {
+ spInstance->mLevelTokenStoreList[i].mCounter =0;
+ for (int j=0;j<MAX_INVENTORY;j++)
+ {
+ delete mLevelTokenStoreList[i].mInventoryList[j];
+ }
+ }
+
+
+}
+
+
+
+void RewardsManager::NameCheck(char* name)
+{
+ rTuneAssertMsg(strlen( name ) < 16,"ERROR: Name is greater than 16 chars! \n");
+}
+
+
+void RewardsManager::FileNameCheck(char* filename)
+{
+ rTuneAssertMsg(strlen( filename ) < 64,"ERROR: FileName is greater than 64 chars! \n");
+}
+
+bool RewardsManager::LevelCheck(int level)
+{
+ bool success;
+ if (level > -1 && level <= MAX_LEVELS)
+ success = true;
+ else
+ success = false;
+
+ rTuneAssertMsg(success,"Level is either less than 0 or Greater than 7! \n");
+
+ return success;
+}
+
+
+
+//Inc the Internal Update Counter. This will be called from the LoadChararctersheet complete method.
+void RewardsManager::IncUpdateQue()
+{
+ mUpdateQue++;
+}
+
+//The method is called from the FE context.
+int RewardsManager::SynchWithCharacterSheet()
+{
+ //if our que is greater than 0 than means the charactersheet has been loaded
+ //so we needed call the charactersheetsmanager, and update the RewardManager
+
+ if (mUpdateQue > 0)
+ {
+ GetCharacterSheetManager()->UpdateRewardsManager();
+ mUpdateQue--;
+ return 0;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+// Set/Get Methods for Total Gags in Level
+//
+void
+RewardsManager::SetTotalGags( int level, int numGags )
+{
+ spInstance->mRewardsList[ level ].mTotalGagsInLevel = numGags;
+}
+
+int
+RewardsManager::GetTotalGags( int level ) const
+{
+ return spInstance->mRewardsList[ level ].mTotalGagsInLevel;
+}
+
+// Set/Get Methods for Total Wasps in Level
+//
+void
+RewardsManager::SetTotalWasps( int level, int numWasps )
+{
+ spInstance->mRewardsList[ level ].mTotalWaspsInLevel = numWasps;
+}
+
+int
+RewardsManager::GetTotalWasps( int level ) const
+{
+ return spInstance->mRewardsList[ level ].mTotalWaspsInLevel;
+}
+
diff --git a/game/code/mission/rewards/rewardsmanager.h b/game/code/mission/rewards/rewardsmanager.h
new file mode 100644
index 0000000..23a3cbd
--- /dev/null
+++ b/game/code/mission/rewards/rewardsmanager.h
@@ -0,0 +1,181 @@
+#ifndef REWARDSMANAGER_H
+#define REWARDSMANAGER_H
+#include <mission/rewards/reward.h>
+#include <mission/rewards/merchandise.h>
+#include <render/enums/renderenums.h>
+#include <loading/loadingmanager.h>
+#include <mission/charactersheet/charactersheet.h>
+
+//maximum number of cars that can get purchase via coins.
+const int MAX_INVENTORY = 8;
+
+struct TokenStoreInventory
+{
+ Merchandise* mInventoryList[MAX_INVENTORY];
+ int mCounter;
+};
+
+
+
+
+struct LevelRewardRecord
+{
+ Reward* mStreetRace;
+ Reward* mBonusMission;
+ Reward* mCards;
+ Reward* mDefaultCar;
+ Reward* mDefaultSkin;
+ Reward* mGoldCards;
+
+ int mMaxTokensInLevel;
+
+ int mTotalGagsInLevel;
+ int mTotalWaspsInLevel;
+
+};
+
+struct CarAttributeRecord
+{
+ char mName [16];
+ float mSpeed;
+ float mAcceleration;
+ float mToughness;
+ float mStability;
+};
+
+const int MAX_CAR_ATTRIBUTE_RECORDS = 50;
+
+
+//Rewards Manager.
+
+class RewardsManager : public LoadingManager::ProcessRequestsCallback
+ {
+ public:
+
+ void OnProcessRequestsComplete( void* pUserData );
+
+ //Init Rewards with defaults.
+ void InitRewards();
+
+ //Init the CarAttribute records with defaults
+ void InitCarAttributeRecords ();
+
+ //Clear all rewards. FE should call this for new games.
+ void ClearRewards();
+
+ //Load rewards script file
+ void LoadScript();
+ bool ScriptLoaded() const { return mScriptLoaded; };
+
+ //Static Methods for getting access
+ static RewardsManager* GetInstance();
+ static RewardsManager* CreateInstance();
+ static void DestroyInstance ();
+
+
+ int BindReward (char* name,char* filename,Reward::eRewardType rtype, Reward::eQuestType qtype,int level);
+
+ //methods for getting the nubmer of tokens in a level and setting them
+ void SetTokensInLevel(int level,int tokens);
+ int GetNumberOfTokensInLevel(int level);
+
+
+ int GenerateTokens();
+
+ //Init the token store
+ void InitTokenStore();
+
+ int AddMerchandise(char* name,char* filename,Reward::eRewardType rtype,int level,int cost,Merchandise::eSellerType sellerType);
+
+ //Accessor methods for rewards purchased with coins/tokens
+
+ //get a complete list of rewards that can be purchased with tokens for an entire level.
+ TokenStoreInventory* GetTokenStoreInventoryList(int level);
+
+ //get a specific Reward you can query by name or index.
+ Merchandise* GetMerchandise(int level,const char* name);
+ Merchandise* GetMerchandise(int level,int index);
+ int GetMerchandiseIndex(int level,const char* name);
+
+ // Iteration Methods for Retieving Merchandise
+ //
+ Merchandise* FindFirstMerchandise( int level, Merchandise::eSellerType sellerType );
+ Merchandise* FindNextMerchandise( int level, Merchandise::eSellerType sellerType );
+
+ //Attempt to buy an item from the token store, method will return bool for sucess or failure
+ //failure will usually mean not enough coins.
+
+ bool BuyMerchandise(int level,const char * name);
+
+ //query methods, input the level and what kind of quest like eCards,eCoins,eBonusMission,eStreetRace,eCamera.
+ Reward* GetReward(int level,Reward::eQuestType qtype);
+ Reward* GetReward(int level,tUID id);
+
+ //query method input the car's name, it will either return a record or NULL
+ CarAttributeRecord* GetCarAttributeRecord(char* carname);
+ static float ComputeOverallCarRating( CarAttributeRecord* carStats );
+
+ int SetCarAttributes(char* carname,float speed,float acceleration,float toughness,float stability);
+
+ //updates the Rewards manager's rewards so it matches with the charactersheet.
+ int SynchWithCharacterSheet();
+ void IncUpdateQue();
+
+ void SetTotalGags( int level, int numGags );
+ int GetTotalGags( int level ) const;
+
+ void SetTotalWasps( int level, int numWasps );
+ int GetTotalWasps( int level ) const;
+
+ private:
+
+ //private constructor and destructor
+ RewardsManager();
+ virtual ~RewardsManager();
+
+ //private methods
+
+ void NameCheck(char* name);
+ void FileNameCheck(char* filename);
+ // Verify that the level is within the proper range. Returns true if it is, false if out of bounds
+ // (and bad-array deindex will almost certainly happen)
+ bool LevelCheck(int level);
+
+
+ //Array with all rewards, Note that index 0 is blank/dummy level
+
+ LevelRewardRecord mRewardsList [8];
+ CarAttributeRecord mCarAttributeList [MAX_CAR_ATTRIBUTE_RECORDS];
+ int mCarAttributeRecordIndex;
+
+ //Chuck: Creating a internal update que, whenever the character sheet get loaded/reloaded it will increment this
+ //que. in the FE update the Rewards manager will check this que, if que not empty then the RewardManager will update itself
+ //so it is synched with the character sheet.
+
+ int mUpdateQue;
+
+ TokenStoreInventory mLevelTokenStoreList [MAX_LEVELS];
+ int mLevelTokenStoreSearchIndex[ MAX_LEVELS ];
+
+ //declared but not defined.
+ RewardsManager ( const RewardsManager& );
+ RewardsManager& operator=( const RewardsManager& );
+
+ static RewardsManager* spInstance;
+
+ bool mScriptLoaded;
+ };
+
+
+//Global function to get access to the singleton
+inline RewardsManager* GetRewardsManager()
+ {
+ return ( RewardsManager::GetInstance() );
+ }
+
+
+
+#endif //RewardsMANAGER_H
+
+
+ \ No newline at end of file
diff --git a/game/code/mission/safezone/allsafezone.cpp b/game/code/mission/safezone/allsafezone.cpp
new file mode 100644
index 0000000..2e854a1
--- /dev/null
+++ b/game/code/mission/safezone/allsafezone.cpp
@@ -0,0 +1 @@
+#include <mission/safezone/safezone.cpp> \ No newline at end of file
diff --git a/game/code/mission/safezone/safezone.cpp b/game/code/mission/safezone/safezone.cpp
new file mode 100644
index 0000000..568d3b6
--- /dev/null
+++ b/game/code/mission/safezone/safezone.cpp
@@ -0,0 +1,90 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: safezone.cpp
+//
+// Description: Represent an area around a locator that if a player enters it will deactivate AI cars and trigger possibly other events
+//
+// History: 11/25/2002 + Created -- Chuck C.
+//
+//=============================================================================
+
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+#include <meta/carstartlocator.h>
+#include <mission/safezone/safezone.h>
+
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+
+
+//Default
+SafeZone::SafeZone ( )
+{
+ mLocator = NULL;
+ mRadius = 1;
+};
+
+SafeZone::~SafeZone ()
+{
+
+};
+
+//Use this Constructor
+SafeZone::SafeZone(CarStartLocator* locator,unsigned int radius)
+{
+ mLocator = locator;
+ mRadius = radius;
+};
+
+
+//pass in a vector and it will return true if its in the safzones radius, else false
+bool SafeZone::InsideZone( rmt::Vector vector)
+{
+ rmt::Vector temp;
+
+ mLocator->GetLocation( &temp );
+
+ if ((rmt::Sqr(vector.x -temp.x) + rmt::Sqr(vector.z- temp.z) )<= rmt::Sqr (mRadius) )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+
+
+};
+
+
+//returns a position of the safezone
+rmt::Vector SafeZone::GetPosition ()
+{
+
+ rmt::Vector temp;
+ mLocator->GetLocation( &temp );
+ return temp;
+};
+
+
+//returns the radius of the safezone
+unsigned int SafeZone::GetRadius()
+{
+ return mRadius;
+};
+
+
diff --git a/game/code/mission/safezone/safezone.h b/game/code/mission/safezone/safezone.h
new file mode 100644
index 0000000..111a1b2
--- /dev/null
+++ b/game/code/mission/safezone/safezone.h
@@ -0,0 +1,55 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: safezone.h
+//
+// Description: Represent an area around a locator that if a player enters it will deactivate AI cars and trigger possibly other events
+//
+// History: 11/25/2002 + Created -- Chuck C.
+//
+//=============================================================================
+
+#ifndef SAFEZONE_H
+#define SAFEZONE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+
+//========================================
+// Forward References
+//========================================
+class CarStartLocator;
+
+//=============================================================================
+//
+// Synopsis: SafeZone Class
+//
+//=============================================================================
+
+
+class SafeZone
+{
+public:
+ SafeZone ();
+ SafeZone(CarStartLocator* locator,unsigned int radius);
+ virtual ~SafeZone();
+
+ bool InsideZone( rmt::Vector vector);
+ rmt::Vector GetPosition ();
+ unsigned int GetRadius ();
+
+private:
+ CarStartLocator* mLocator;
+ unsigned int mRadius ;
+
+
+ //Prevent wasteful constructor creation.
+ SafeZone( const SafeZone& safezone );
+ SafeZone& operator=( const SafeZone& safezone );
+};
+
+
+#endif //SAFEZONE_H
diff --git a/game/code/mission/statepropcollectible.cpp b/game/code/mission/statepropcollectible.cpp
new file mode 100644
index 0000000..f1e9b36
--- /dev/null
+++ b/game/code/mission/statepropcollectible.cpp
@@ -0,0 +1,322 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: statepropcollectible
+//
+// Description: Exactly like a normal stateprop, except it will
+// identify low-speed collisions with the player vehicle and
+// then attach itself to the car
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <mission/statepropcollectible.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <render/rendermanager/rendermanager.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <ai/actor/intersectionlist.h>
+#include <events/eventenum.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <events/eventmanager.h>
+#include <constants/statepropenum.h>
+#include <worldsim/avatarmanager.h>
+#include <events/eventdata.h>
+#include <worldsim/avatar.h>
+#include <worldsim/character/character.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+// How long to hold a camera shake
+const unsigned int STATEPROP_COLLECTIBLE_CAMERA_SHAKE_TIME = 1000;
+// Shake intensity.
+float STATEPROP_SHAKE_FORCE = 300;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+StatePropCollectible::StatePropCollectible():
+m_HudIcon( -1 ),
+m_IsInDSG( false ),
+m_CollisionTestingEnabled( false ),
+m_ShakeStartTime( 0 ),
+m_CameraShaking( false )
+{
+#ifndef RAD_RELEASE
+ static bool wasAdded = false;
+ if ( wasAdded == false )
+ {
+ radDbgWatchAddFloat( &STATEPROP_SHAKE_FORCE, "Stateprop shake force", "Stateprop", NULL, NULL, 0, 400.0f );
+ wasAdded = true;
+ }
+#endif
+}
+
+StatePropCollectible::~StatePropCollectible()
+{
+ EnableHudIcon( false );
+ RemoveFromDSG();
+}
+
+
+sim::Solving_Answer
+StatePropCollectible::PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+{
+ // What we want to do is check to see if the collided object is a player vehicle. If it is, then
+ // check its velocity
+ // if the velocity is low
+ // abort the collision and attach the object to the vehicle.
+ sim::Solving_Answer answer = sim::Solving_Aborted;
+
+ bool computeStatePropPreReact = true;
+
+ if ( pCollidedObj->mAIRefIndex == PhysicsAIRef::redBrickVehicle )
+ {
+ rAssert( dynamic_cast< Vehicle* >( static_cast< tRefCounted* >( pCollidedObj->mAIRefPointer) ) );
+ Vehicle* vehicle = static_cast< Vehicle* >( pCollidedObj->mAIRefPointer );
+ if ( vehicle->IsUserDrivingCar() )
+ {
+ // Attach it to the vehicle, if there is room
+ // Remember to check return value, there may not be a slot left
+ // and it won't attach
+
+ // Lets not attach it the object is moving around
+ sim::SimState* pSimState = GetSimState();
+ const rmt::Vector& linearVelocity = pSimState->GetLinearVelocity();
+ if ( linearVelocity.MagnitudeSqr() < 100.0f )
+ {
+ if ( vehicle->AttachCollectible( this ) )
+ { // It attached ok.
+ rmt::Matrix origin;
+ origin.Identity();
+ // Send this thing to the origin, as when the vehicle
+ // draws it, it can't be translated out to worldspace
+ SetTransform( origin );
+ // Remove it from the DSG. No point in doing DSG stuff
+ // when the car is also in the DSG
+ computeStatePropPreReact = false;
+ answer = sim::Solving_Aborted;
+ }
+ }
+ }
+ }
+
+ if ( computeStatePropPreReact )
+ answer = StatePropDSG::PreReactToCollision( pCollidedObj, inCollision );
+
+ return answer;
+}
+
+// Overloaded AdvanceAnimation. Same as the normal statepropdsg::advanceanimation
+// but also keeps the object in sim mode
+void
+StatePropCollectible::AdvanceAnimation( float timeInMS )
+{
+ // Lets make sure that the collectibles never go out of sim mode
+ sim::SimState* pSimState = GetSimState();
+
+ // There is a problem with dynamic objects under physics that move outside the update
+ // sphere around vehicles or players. Namely they will happily
+
+ mpStateProp->Update( timeInMS );
+ if ( IsCollisionTestingEnabled() )
+ DoCollisionTesting();
+ // Update position
+ GetPosition( &m_PreviousPosition );
+
+ // Check camera shake
+ if ( m_CameraShaking )
+ {
+ // SHAKE!
+ CameraShake();
+
+ // Check that we should still be shaking
+ unsigned int currTime = radTimeGetMilliseconds();
+ if ( currTime > m_ShakeStartTime + STATEPROP_COLLECTIBLE_CAMERA_SHAKE_TIME )
+ m_CameraShaking = false;
+ }
+}
+
+void StatePropCollectible::SetTransform( const rmt::Matrix& matrix )
+{
+ m_PreviousPosition = matrix.Row(3);
+ if ( IsInDSG() )
+ {
+ StatePropDSG::SetTransform( matrix );
+ }
+}
+
+void StatePropCollectible::SetPosition( const rmt::Vector& position )
+{
+ m_PreviousPosition = position;
+ StatePropDSG::SetPosition( position );
+}
+
+void StatePropCollectible::LoadSetup( CStatePropData* statePropData,
+ int startState,
+ const rmt::Matrix& transform,
+ CollisionAttributes* ipCollAttr,
+ bool isDynaLoaded,
+ tEntityStore* ipSearchStore,
+ bool useSharedtPose,
+ sim::CollisionObject* collisionObject,
+ sim::PhysicsObject* physicsObject )
+{
+ StatePropDSG::LoadSetup( statePropData, startState, transform, ipCollAttr, isDynaLoaded, ipSearchStore, useSharedtPose, collisionObject, physicsObject );
+ m_PreviousPosition = transform.Row(3);
+ mOriginalLocation = transform;
+}
+
+
+void StatePropCollectible::AddToDSG()
+{
+ if ( m_IsInDSG == false )
+ {
+ WorldRenderLayer* wrl = static_cast< WorldRenderLayer* > ( GetRenderManager()->mpLayer( RenderEnums::LevelSlot ) );
+ wrl->pWorldScene()->Add( this );
+ m_IsInDSG = true;
+ }
+}
+
+void StatePropCollectible::RemoveFromDSG()
+{
+ if ( m_IsInDSG )
+ {
+ WorldRenderLayer* wrl = static_cast< WorldRenderLayer* > ( GetRenderManager()->mpLayer( RenderEnums::LevelSlot ) );
+ wrl->pWorldScene()->Remove( this );
+ m_IsInDSG = false;
+ }
+}
+
+void StatePropCollectible::Update( float dt )
+{
+ if ( IsInDSG() )
+ StatePropDSG::Update( dt );
+}
+
+void StatePropCollectible::RecieveEvent( int callback , CStateProp* stateProp )
+{
+ switch ( callback )
+ {
+ case StatePropEnum::eRemoveFromWorld:
+ RemoveFromDSG();
+ break;
+ case StatePropEnum::eObjectDestroyed:
+ GetEventManager()->TriggerEvent( EVENT_STATEPROP_COLLECTIBLE_DESTROYED );
+ break;
+ case StatePropEnum::eCamShake:
+
+ m_CameraShaking = true;
+ m_ShakeStartTime = radTimeGetMilliseconds();
+
+ break;
+ default:
+ StatePropDSG::RecieveEvent( callback, stateProp );
+ break;
+ }
+
+
+}
+
+void StatePropCollectible::Explode()
+{
+ SetState( 2 );
+ GetEventManager()->TriggerEvent( EVENT_BARREL_BLOWED_UP );
+}
+
+void StatePropCollectible::GetPosition( rmt::Vector* currentLoc )
+{
+ *currentLoc = mTransform.Row(3);
+}
+
+void StatePropCollectible::GetHeading( rmt::Vector* heading )
+{
+ *heading = mTransform.Row(2);
+}
+
+void StatePropCollectible::EnableHudIcon( bool enable )
+{
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if ( currentHud == NULL )
+ return;
+
+ if ( enable )
+ {
+ if ( m_HudIcon == -1 )
+ m_HudIcon = currentHud->GetHudMap( 0 )->RegisterIcon( HudMapIcon::ICON_COLLECTIBLE, mTransform.Row(3), this, true );
+ }
+ else
+ {
+ if ( m_HudIcon != -1 )
+ {
+ currentHud->GetHudMap( 0 )->UnregisterIcon( m_HudIcon );
+ m_HudIcon = -1;
+ }
+ }
+
+}
+
+
+void
+StatePropCollectible::DoCollisionTesting()
+{
+ return;
+ IntersectionList intersectList;
+ rmt::Vector currPosition;
+ GetPosition( &currPosition );
+ float radiusSqr = ( currPosition - m_PreviousPosition ).MagnitudeSqr();
+ // Lets use radiusSqr instead of doing the sqrt call
+ // It *should* be a small value anyway, < 1 meter.
+ intersectList.FillIntersectionListStatics( currPosition, radiusSqr );
+
+ bool los = intersectList.LineOfSight( currPosition, m_PreviousPosition );
+ if ( los == false )
+ {
+ // We just travelled through a static object ( staticphys or fence piece )
+ // Damn physics
+ // Lets kill any velocity that this thing has and set position to be m_PreviousPosition
+ GetSimState()->ResetVelocities();
+ GetSimState()->SetControl( sim::simAICtrl );
+ SetPosition( m_PreviousPosition );
+ }
+
+}
+void
+StatePropCollectible::CameraShake()
+{
+ // Shake the camera by a goodly amount
+ ShakeEventData shakeData;
+ shakeData.playerID = 0; //Hack...
+ shakeData.force = STATEPROP_SHAKE_FORCE; //
+
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ // Get direction, player to stateprop
+ if ( avatar )
+ {
+ rmt::Vector contactPointToCenter;
+
+ Character* character = avatar->GetCharacter();
+ rmt::Vector charPosition;
+ character->GetPosition( charPosition );
+
+ contactPointToCenter.Sub( charPosition, rPosition() );
+ contactPointToCenter.NormalizeSafe();
+ shakeData.direction = rmt::Vector(0,1,0);
+ GetEventManager()->TriggerEvent( EVENT_CAMERA_SHAKE, &shakeData );
+ }
+} \ No newline at end of file
diff --git a/game/code/mission/statepropcollectible.h b/game/code/mission/statepropcollectible.h
new file mode 100644
index 0000000..5e9be3d
--- /dev/null
+++ b/game/code/mission/statepropcollectible.h
@@ -0,0 +1,122 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: statepropcollectible
+//
+// Description: Exactly like a normal stateprop, except it will
+// identify low-speed collisions with the player vehicle and
+// then attach itself to the car
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef STATEPROPCOLLECTIBLE_H
+#define STATEPROPCOLLECTIBLE_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <render/DSG/statepropdsg.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <presentation/gui/utility/hudmap.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// A statepropdsg with a special postreacttocollision
+//
+// Constraints:
+//
+//
+//===========================================================================
+class StatePropCollectible : public StatePropDSG, public IHudMapIconLocator
+{
+ public:
+ StatePropCollectible();
+ virtual ~StatePropCollectible();
+ virtual sim::Solving_Answer PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision );
+ virtual void AdvanceAnimation( float timeInMS );
+ virtual void SetTransform( const rmt::Matrix& matrix );
+ virtual void SetPosition( const rmt::Vector& position );
+ virtual void LoadSetup( CStatePropData* statePropData,
+ int startState,
+ const rmt::Matrix& transform,
+ CollisionAttributes* ipCollAttr,
+ bool isDynaLoaded = true,
+ tEntityStore* ipSearchStore = NULL,
+ bool useSharedtPose = false,
+ sim::CollisionObject* collisionObject = NULL,
+ sim::PhysicsObject* physicsObject = NULL );
+
+ void AddToDSG();
+ bool IsInDSG()const { return m_IsInDSG; }
+ void RemoveFromDSG();
+ // Updates physics
+ virtual void Update( float dt );
+
+
+ virtual void RecieveEvent( int callback , CStateProp* stateProp );
+
+ // Collectible explodes and dies. Event fired off signalling mission failure
+ void Explode();
+
+ // Inherited from IHudMapIconLocator
+ virtual void GetPosition( rmt::Vector* currentLoc );
+ virtual void GetHeading( rmt::Vector* heading );
+
+ void EnableHudIcon( bool enable );
+ void EnableCollisionTesting( bool enable ) { m_CollisionTestingEnabled = enable; }
+ bool IsCollisionTestingEnabled()const { return m_CollisionTestingEnabled; }
+
+ const rmt::Matrix& GetStartingPosition()const { return mOriginalLocation; }
+
+ protected:
+
+ int m_HudIcon;
+ bool m_IsInDSG;
+ bool m_CollisionTestingEnabled;
+ rmt::Vector m_PreviousPosition;
+
+ // Location of the original start position
+ rmt::Matrix mOriginalLocation;
+
+ void DoCollisionTesting();
+
+ // Called by the camera shake callback
+ void CameraShake();
+
+ unsigned int m_ShakeStartTime;
+ bool m_CameraShaking;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow StatePropCollectible from being copied and assigned.
+ StatePropCollectible( const StatePropCollectible& );
+ StatePropCollectible& operator=( const StatePropCollectible& );
+
+};
+
+
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/mission/timeremainbonusobjective.cpp b/game/code/mission/timeremainbonusobjective.cpp
new file mode 100644
index 0000000..717178b
--- /dev/null
+++ b/game/code/mission/timeremainbonusobjective.cpp
@@ -0,0 +1,176 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: timeremainbonusobjective.cpp
+//
+// Description: Implement TimeRemainBonusObjective
+//
+// History: 12/6/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/timeremainbonusobjective.h>
+#include <mission/mission.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// TimeRemainBonusObjective::TimeRemainBonusObjective
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+TimeRemainBonusObjective::TimeRemainBonusObjective() : mMinTime( 0 )
+{
+ SetType( BonusObjective::TIME );
+ SetSuccessful( true );
+}
+
+//=============================================================================
+// TimeRemainBonusObjective::~TimeRemainBonusObjective
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+TimeRemainBonusObjective::~TimeRemainBonusObjective()
+{
+}
+
+//=============================================================================
+// TimeRemainBonusObjective::Initialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TimeRemainBonusObjective::Initialize()
+{
+}
+
+//=============================================================================
+// TimeRemainBonusObjective::Finalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TimeRemainBonusObjective::Finalize()
+{
+}
+
+//=============================================================================
+// TimeRemainBonusObjective::GetNumericData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+unsigned int TimeRemainBonusObjective::GetNumericData()
+{
+ rAssert( mMission );
+
+ return (static_cast<unsigned int>(mMission->GetMissionTimeLeftInMilliSeconds()) );
+}
+
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// TimeRemainBonusObjective::OnReset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TimeRemainBonusObjective::OnReset()
+{
+ SetSuccessful( true );
+}
+
+//=============================================================================
+// TimeRemainBonusObjective::OnStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TimeRemainBonusObjective::OnStart()
+{
+ //Turn on the HUD.
+}
+
+//=============================================================================
+// TimeRemainBonusObjective::OnUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void TimeRemainBonusObjective::OnUpdate( unsigned int milliseconds )
+{
+ if ( GetStarted() && GetSuccessful() )
+ {
+ if ( mMission->GetMissionTimeLeftInMilliSeconds() > 0 &&
+ static_cast<unsigned int>(mMission->GetMissionTimeLeftInMilliSeconds()) < mMinTime )
+ {
+ SetSuccessful( false );
+ //Update the HUD
+ }
+ }
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/mission/timeremainbonusobjective.h b/game/code/mission/timeremainbonusobjective.h
new file mode 100644
index 0000000..e754508
--- /dev/null
+++ b/game/code/mission/timeremainbonusobjective.h
@@ -0,0 +1,95 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: timeremainbonusobjective.h
+//
+// Description: Blahblahblah
+//
+// History: 12/6/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef TIMEREMAINBONUSOBJECTIVE_H
+#define TIMEREMAINBONUSOBJECTIVE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/bonusobjective.h>
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+class Mission;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class TimeRemainBonusObjective : public BonusObjective
+{
+public:
+ TimeRemainBonusObjective();
+ virtual ~TimeRemainBonusObjective();
+
+ virtual void Initialize();
+ virtual void Finalize();
+ virtual unsigned int GetNumericData();
+
+ void SetTime( unsigned int time );
+ void SetMission( Mission* mission );
+
+protected:
+ virtual void OnReset();
+ virtual void OnStart();
+ virtual void OnUpdate( unsigned int milliseconds );
+
+private:
+ unsigned int mMinTime;
+ Mission* mMission;
+
+ //Prevent wasteful constructor creation.
+ TimeRemainBonusObjective( const TimeRemainBonusObjective& timeremainbonusobjective );
+ TimeRemainBonusObjective& operator=( const TimeRemainBonusObjective& timeremainbonusobjective );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// TimeRemainBonusObjective::SetTime
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int time )
+//
+// Return: void
+//
+//=============================================================================
+inline void TimeRemainBonusObjective::SetTime( unsigned int time )
+{
+ mMinTime = time;
+}
+
+//=============================================================================
+// TimeRemainBonusObjective::SetMission
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Mission* mission )
+//
+// Return: void
+//
+//=============================================================================
+inline void TimeRemainBonusObjective::SetMission( Mission* mission )
+{
+ mMission = mission;
+}
+
+#endif //TIMEREMAINBONUSOBJECTIVE_H
diff --git a/game/code/mission/ufo/allufo.cpp b/game/code/mission/ufo/allufo.cpp
new file mode 100644
index 0000000..8e5cc9b
--- /dev/null
+++ b/game/code/mission/ufo/allufo.cpp
@@ -0,0 +1,3 @@
+#include <mission/ufo/ufo.cpp>
+#include <mission/ufo/tractorbeam.cpp>
+
diff --git a/game/code/mission/ufo/tractorbeam.cpp b/game/code/mission/ufo/tractorbeam.cpp
new file mode 100644
index 0000000..ccc3057
--- /dev/null
+++ b/game/code/mission/ufo/tractorbeam.cpp
@@ -0,0 +1,354 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Name of subsystem or component this class belongs to.
+//
+// Description: This file contains the implementation of...
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <mission/ufo/tractorbeam.h>
+#include <render/DSG/StatePropDSG.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/DSG/DynaPhysDSG.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <events/eventmanager.h>
+#include <p3d/context.hpp>
+#include <p3d/matrixstack.hpp>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+const float BEAM_SPEED = 2.50f;
+const float MAX_TRACTORBEAM_RANGE_SQR = 3.0f * 3.0f;
+const int MAX_CAUGHT_OBJECTS = 50;
+const float SWALLOW_RADIUS_SQR = 1.0f * 1.0f;
+const float MAX_BEAM_ANGLE = 0.707f;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+
+
+TractorBeam::TractorBeam():
+mpDrawable( NULL ),
+mNextTarget( NULL )
+{
+ mCaughtObjects.Allocate( MAX_CAUGHT_OBJECTS );
+ // Set the beam to point straight down
+ mTransform.Identity();
+}
+
+TractorBeam::~TractorBeam()
+{
+ if ( mpDrawable != NULL )
+ {
+ mpDrawable->Release();
+ mpDrawable = NULL;
+ }
+}
+
+bool
+TractorBeam::LoadSetup( const char* statePropDSGName )
+{
+ mStatePropName = statePropDSGName;
+
+ rAssert( mpDrawable == NULL );
+ mpDrawable = p3d::find< StatePropDSG >( statePropDSGName );
+ bool success;
+ if ( mpDrawable != NULL )
+ {
+ mpDrawable->AddRef();
+ // Insert it into the level layer in the dsg
+ WorldRenderLayer* pLayer = static_cast< WorldRenderLayer* >( GetRenderManager()->mpLayer( RenderEnums::LevelSlot ));
+ pLayer->pWorldScene()->Add( mpDrawable );
+ success = true;
+
+ // Place the object in the mission section
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( "Mission" );
+ p3d::inventory->Store( this );
+ p3d::inventory->PopSection();
+ }
+ else
+ {
+ success = false;
+ }
+
+ return success;
+}
+
+
+void
+TractorBeam::Update( float timeInMS )
+{
+ if ( mpDrawable != NULL )
+ {
+ mpDrawable->Update( timeInMS );
+ // Move the beam
+
+ // SLERP between the beam and the target
+ rmt::Matrix targetOrientation;
+ {
+ rmt::Vector up;
+ if ( mNextTarget != NULL )
+ {
+
+ rmt::Vector targetPos;
+ mNextTarget->GetPosition( &targetPos );
+ rmt::Vector toTarget = targetPos - mTransform.Row(3);
+ toTarget.Normalize();
+
+ if ( toTarget.Dot( rmt::Vector( 0, -1.0f ,0 )) > MAX_BEAM_ANGLE )
+ {
+ up = -toTarget;
+ }
+ else
+ {
+ up = rmt::Vector(0, 1.0f, 0);
+ }
+ }
+ else
+ {
+ up = rmt::Vector( 0, 1.0f, 0 );
+ }
+ rmt::Vector forward;
+ forward.CrossProduct( up, rmt::Vector(1,0,0) );
+ rmt::Vector right;
+ right.CrossProduct( up, forward );
+
+ targetOrientation.Row(0) = right;
+ targetOrientation.Row(1) = up;
+ targetOrientation.Row(2) = forward;
+
+ const float BEAM_ANGULAR_SPEED = 0.1f;
+ rmt::Quaternion orientation = Slerp( targetOrientation, mTransform, timeInMS ,BEAM_ANGULAR_SPEED);
+ mTransform.FillRotation( orientation );
+
+ mpDrawable->SetTransform( mTransform );
+ }
+
+ // Go through the list and remove the caught objects
+ for ( int i = 0 ; i < mCaughtObjects.mUseSize ; i++)
+ {
+
+ // Fetch object position
+ rmt::Vector objPosition;
+ mCaughtObjects[i]->GetPosition( &objPosition );
+
+
+ // Check to see if it is within range of the swallow emitter
+ float distanceToBeamEmitterSqr = (mTransform.Row(3) - objPosition).MagnitudeSqr();
+ if ( distanceToBeamEmitterSqr < SWALLOW_RADIUS_SQR )
+ {
+ SwallowObject( mCaughtObjects[i] );
+ mCaughtObjects[i]->Release();
+ mCaughtObjects.Remove( i );
+
+ // Remove the object from the list
+ }
+ else
+ {
+ // Move the object upwards
+ //rmt::Vector* pPosition = mCaughtObjects[i]->pPosition();
+ //pPosition->y -= timeInMS * BEAM_SPEED;
+ sim::SimState* pSimState = mCaughtObjects[ i ]->GetSimState();
+ //rmt::Matrix& rTransform = pSimState->GetTransform();
+ rmt::Vector& rVelocity = pSimState->GetLinearVelocity();
+ //Set the velocity to be constant toward the tractorbeam center
+
+ rmt::Vector toTractorBeam = (mTransform.Row(3) - objPosition);
+ toTractorBeam.Normalize();
+ toTractorBeam *= BEAM_SPEED;
+ rVelocity = toTractorBeam;
+ }
+ }
+ }
+ else
+ {
+ LoadSetup( mStatePropName.GetText() );
+ }
+}
+
+bool
+TractorBeam::IsValidTarget( DynaPhysDSG* pDSG )const
+{
+ bool valid;
+ int i;
+ for ( i = 0 ; i < mCaughtObjects.mUseSize ; i++)
+ {
+ if ( pDSG == mCaughtObjects.mpData[i] )
+ {
+ break;
+ }
+ }
+ if ( i == mCaughtObjects.mUseSize )
+ {
+ valid = true;
+ }
+ else
+ {
+ valid = false;
+ }
+ return valid;
+}
+
+bool
+TractorBeam::CanFire( DynaPhysDSG* pDSG )const
+{
+ bool canFireAtTarget;
+ // If the beam can't hold any more objects, return false
+ if ( mCaughtObjects.mUseSize < mCaughtObjects.mSize )
+ {
+ // If within max range, and not already caught, return true
+ rmt::Vector targetPos;
+ pDSG->GetPosition( &targetPos );
+ rmt::Vector targetVector = targetPos - mTransform.Row(3);
+ targetVector.Normalize();
+
+
+ if ( targetVector.Dot( rmt::Vector( 0, -1.0f, 0 ) ) > MAX_BEAM_ANGLE )
+ {
+ if ( IsValidTarget( pDSG ) )
+ {
+ canFireAtTarget = true;
+ }
+ else
+ {
+ canFireAtTarget = false;
+ }
+ }
+ else
+ canFireAtTarget = false;
+
+ }
+ else
+ {
+ canFireAtTarget = false;
+ }
+
+ return canFireAtTarget;
+}
+
+void
+TractorBeam::Fire( DynaPhysDSG* pDSG )
+{
+ pDSG->AddToSimulation();
+ pDSG->AddRef();
+ // Add the object to the mCaughtObjects list
+
+ mCaughtObjects.Add( pDSG );
+ mNextTarget = pDSG;
+}
+
+void
+TractorBeam::Display()
+{
+ rAssert( mpDrawable != NULL );
+
+
+ p3d::stack->PushMultiply( mTransform );
+ mpDrawable->Display();
+ p3d::stack->Pop();
+}
+
+void
+TractorBeam::SetPosition( const rmt::Vector& position )
+{
+ mTransform.Row(3) = position;
+ if ( mpDrawable != NULL )
+ {
+ rmt::Box3D oldBoundingBox;
+ mpDrawable->GetBoundingBox( &oldBoundingBox );
+ mpDrawable->SetPosition( position );
+
+ WorldRenderLayer* pLayer = static_cast< WorldRenderLayer* >( GetRenderManager()->mpLayer( RenderEnums::LevelSlot ));
+ pLayer->pWorldScene()->Move( oldBoundingBox, mpDrawable );
+ }
+}
+
+void
+TractorBeam::SwallowObject( DynaPhysDSG* pDSG )
+{
+ rAssert( pDSG != NULL );
+ // switch on AIref
+ // peds have to be removed
+ int aiRef = pDSG->GetAIRef();
+ switch( aiRef )
+ {
+ case PhysicsAIRef::PlayerCharacter:
+ break;
+ case PhysicsAIRef::NPCharacter:
+ break;
+ case PhysicsAIRef::redBrickVehicle:
+ {
+ // Remove the object via the traffic manager
+ rAssert( dynamic_cast< Vehicle* > ( pDSG ) );
+ Vehicle* pVehicle = static_cast< Vehicle* >( pDSG );
+ if ( pVehicle->mVehicleType == VT_TRAFFIC )
+ {
+ TrafficManager::GetInstance()->RemoveTraffic( static_cast< Vehicle* > ( pDSG ) );
+ }
+ }
+ GetEventManager()->TriggerEvent( EVENT_VEHICLE_DESTROYED, NULL );
+ break;
+ default:
+ WorldRenderLayer* wrl = static_cast< WorldRenderLayer* >( GetRenderManager()->mpLayer( RenderEnums::LevelSlot ) );
+ rAssert( dynamic_cast< WorldRenderLayer* > ( wrl ) );
+ wrl->RemoveGuts( pDSG );
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_DESTROYED, NULL );
+ break;
+ };
+}
+
+bool
+TractorBeam::IsWithinBeam( const rmt::Vector& targetVector )const
+{
+ rmt::Vector beamDir = -mTransform.Row(1);
+ float dot = targetVector.Dot( beamDir );
+ // cosine of the angle ( cosine (45 deg ) )
+ bool withinRange;
+ if ( dot > MAX_BEAM_ANGLE )
+ {
+ withinRange = true;
+ }
+ else
+ {
+ withinRange = false;
+ }
+ return withinRange;
+}
+
+rmt::Quaternion
+TractorBeam::Slerp( const rmt::Matrix m1, const rmt::Matrix m2, float deltaTime, float angularVelocity )
+{
+ rmt::Quaternion q1,q2;
+ q1.BuildFromMatrix( m1 );
+ q2.BuildFromMatrix( m2 );
+
+ rmt::Quaternion qresult;
+
+ float t = 0.5f;
+
+ qresult.Slerp( q1, q2, t );
+
+ return qresult;
+}
+
+
diff --git a/game/code/mission/ufo/tractorbeam.h b/game/code/mission/ufo/tractorbeam.h
new file mode 100644
index 0000000..f93d806
--- /dev/null
+++ b/game/code/mission/ufo/tractorbeam.h
@@ -0,0 +1,101 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Name of subsystem or component this class belongs to.
+//
+// Description: This file contains...
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef TRACTORBEAM_H
+#define TRACTORBEAM_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <mission/ufo/weapon.h>
+#include <vector>
+#include <render/culling/swaparray.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class StatePropDSG;
+class IEntityDSG;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+// Describe here what you require of the client which uses or
+// has this class - essentially, the converse of the description
+// above. A constraint is an expression of some semantic
+// condition that must be preserved, an invariant of a class or
+// relationship that must be preserved while the system is in a
+// steady state.
+//
+//===========================================================================
+class TractorBeam : public Weapon
+{
+ public:
+ TractorBeam();
+ virtual ~TractorBeam();
+
+ virtual bool LoadSetup( const char* statePropDSGName );
+ virtual void Update( float timeInMS );
+ virtual void Display();
+ virtual bool IsValidTarget( DynaPhysDSG* pDSG )const;
+ virtual bool CanFire( DynaPhysDSG* pTarget )const;
+ virtual void Fire( DynaPhysDSG* pTarget );
+ virtual void SetPosition( const rmt::Vector& position );
+
+ protected:
+
+ tName mStatePropName;
+
+ StatePropDSG* mpDrawable;
+
+ SwapArray< DynaPhysDSG* > mCaughtObjects;
+
+ rmt::Matrix mTransform;
+ DynaPhysDSG* mNextTarget;
+
+ protected:
+
+ // Removes the object from the world, accompanied by a particle effect
+ // on state change
+ void SwallowObject( DynaPhysDSG* );
+ bool IsWithinBeam( const rmt::Vector& targetVector )const;
+
+ rmt::Quaternion Slerp( const rmt::Matrix m1, const rmt::Matrix m2, float deltaTime, float angularVelocity );
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow TractorBeam from being copied and assigned.
+ TractorBeam( const TractorBeam& );
+ TractorBeam& operator=( const TractorBeam& );
+
+};
+
+
+#endif \ No newline at end of file
diff --git a/game/code/mission/ufo/ufo.cpp b/game/code/mission/ufo/ufo.cpp
new file mode 100644
index 0000000..16e52f3
--- /dev/null
+++ b/game/code/mission/ufo/ufo.cpp
@@ -0,0 +1,338 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Name of subsystem or component this class belongs to.
+//
+// Description: This file contains the implementation of...
+//
+// Authors: Name Here
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <mission/ufo/ufo.h>
+#include <render/DSG/StatePropDSG.h>
+#include <mission/ufo/weapon.h>
+#include <console/console.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/IntersectManager/IntersectManager.h>
+#include <render/DSG/DynaPhysDSG.h>
+#include <memory/map.h>
+#include <console/console.h>
+#include <events/eventmanager.h>
+
+//#include <iostream>
+//#include <stl.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+const float SCAN_RADIUS = 200.0f;
+const float UFO_SPEED = 0.005f;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// XxxClassName::MemberFunction2
+//===========================================================================
+// Description:
+// Generally, describe what behaviour this service possesses on
+// which clients can depend, i.e. the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+// Describe here what you require of the client which calls this
+// service, the behaviour of a client upon which this service
+// relies - essentially, the converse of description above.
+// Also, a constraint is an expression of some semantic
+// condition that must be preserved, an invariant. For example,
+// this service may not be re-entrant because it sets some
+// static variable on which it will depend the next time it is
+// called. Not required if there are no constaints.
+//
+// Parameters:
+// arg1 - brief description of the parameter
+// arg2 - brief description of the parameter
+// argName - brief description of the parameter
+//
+// Return:
+// Describe the return value here. Not required if void return
+// type.
+//
+//===========================================================================
+/*
+// Abstract base state class ctor and dtor
+UFOState::UFOState()
+{
+
+}
+UFOState::~UFOState()
+{
+
+}
+// Moving state, the state has a target and moves unerringly toward it
+UFOMovingState::UFOMovingState()
+{
+
+}
+
+UFOMovingState::~UFOMovingState()
+{
+
+}
+
+
+*/
+
+UFO::UFO():
+mpDrawable( NULL ),
+mVelocity( 0, 0, 0 ),
+mPosition( 25, 5, 307 )
+{
+ // This thing is gonna be a hovering arsenal
+ mWeaponList.reserve( 10 );
+ mTargets.Allocate( 200 );
+
+ GetEventManager()->AddListener( this, EVENT_OBJECT_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+}
+
+UFO::~UFO()
+{
+
+ if ( mpDrawable != NULL )
+ {
+ // Remove it from the DSG
+ WorldRenderLayer* pLayer = static_cast< WorldRenderLayer* >( GetRenderManager()->mpLayer( RenderEnums::LevelSlot ));
+ pLayer->pWorldScene()->Remove( mpDrawable );
+
+
+ mpDrawable->Release();
+ mpDrawable = NULL;
+ }
+ // Remove weapons
+ for (unsigned int i = 0 ; i < mWeaponList.size() ; i++)
+ {
+ mWeaponList[ i ].weapon->Release();
+ }
+
+}
+
+bool
+UFO::LoadSetup( const char* statePropDSGName, const rmt::Vector& position )
+{
+ mPosition = position;
+ mStatePropName = statePropDSGName;
+
+ rAssert( mpDrawable == NULL );
+ mpDrawable = p3d::find< StatePropDSG >( statePropDSGName );
+ bool success;
+ if ( mpDrawable != NULL )
+ {
+ mpDrawable->AddRef();
+ // Insert it into the level layer in the dsg
+ WorldRenderLayer* pLayer = static_cast< WorldRenderLayer* >( GetRenderManager()->mpLayer( RenderEnums::LevelSlot ));
+ pLayer->pWorldScene()->Add( mpDrawable );
+ success = true;
+
+
+
+ // Place the object in the mission section
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( "Mission" );
+ p3d::inventory->Store( this );
+ p3d::inventory->PopSection();
+ }
+ else
+ {
+ success = false;
+ }
+ // Need a mechanism for attaching weapons to the surface of the UFO
+ // Register a console function handler
+
+ return success;
+}
+
+
+void
+UFO::AddWeapon( Weapon* pWeapon, const rmt::Vector& offset )
+{
+ UFOWeapon ufoWeapon;
+ ufoWeapon.weapon = pWeapon;
+ pWeapon->SetPosition( mPosition + offset );
+ ufoWeapon.offset = offset;
+ mWeaponList.push_back( ufoWeapon );
+}
+
+
+void
+UFO::Update( float timeInMS )
+{
+
+ if ( mpDrawable != NULL )
+ {
+ // if ( mTargets.mUseSize == 0 )
+ {
+ ScanForTargets();
+ }
+
+ Navigate( timeInMS );
+ Move( timeInMS );
+
+ if ( mpDrawable )
+ {
+ // Iterate through all the weapons
+ for ( unsigned int i = 0 ; i < mWeaponList.size() ; i++)
+ {
+ mWeaponList[ i ].weapon->Update( timeInMS );
+ for ( int j = 0 ; j < mTargets.mUseSize; j++)
+ {
+ if ( mWeaponList[ i ].weapon->CanFire( mTargets[j] ) )
+ {
+ mWeaponList[ i ].weapon->Fire( mTargets[j] );
+ }
+ }
+ }
+ // Update the state prop animations
+ mpDrawable->Update( timeInMS );
+ }
+ }
+ else
+ {
+ LoadSetup( mStatePropName.GetText(), mPosition );
+ }
+}
+
+
+// Figure out the correct acceleration to get to where we want to go
+// Apply the acceleration to the velocity
+void
+UFO::Navigate( float timeInMS )
+{
+ // Calculate the acceleration required to get to the target
+ // Lets have simple little movement formula
+
+ if ( mTarget && 0 )
+ {
+ rmt::Vector toTarget;
+ rmt::Vector targetPos;
+ mTarget->GetPosition( &targetPos );
+ toTarget = targetPos - mPosition;
+ toTarget.Normalize();
+ toTarget.y = 0;
+ mVelocity = toTarget * UFO_SPEED;
+ }
+ else
+ {
+ mVelocity = rmt::Vector(0,0,0);
+ }
+
+}
+// Apply velocity and move the object, also take care of the DSG move call
+void
+UFO::Move( float timeInMS )
+{
+ mPosition += mVelocity * timeInMS;
+
+ rmt::Box3D oldBoundingBox;
+ mpDrawable->GetBoundingBox( &oldBoundingBox );
+
+ mpDrawable->SetPosition( mPosition );
+
+ // Insert it into the level layer in the dsg
+ WorldRenderLayer* pLayer = static_cast< WorldRenderLayer* >( GetRenderManager()->mpLayer( RenderEnums::LevelSlot ));
+ pLayer->pWorldScene()->Move( oldBoundingBox, mpDrawable );
+
+ // Move the weapons
+ for ( unsigned int i = 0 ; i < mWeaponList.size() ; i++ )
+ {
+ rmt::Vector position = mPosition + mWeaponList[ i ].offset;
+ mWeaponList[ i ].weapon->SetPosition( position );
+ }
+}
+
+
+void
+UFO::ScanForTargets()
+{
+ rmt::Vector position;
+ mpDrawable->GetPosition( &position );
+
+ if ( mTargets.mUseSize > 0 )
+ mTargets.ClearUse();
+
+ GetIntersectManager()->FindDynaPhysElems( position,
+ SCAN_RADIUS,
+ mTargets );
+
+ // Find the closest target
+ // Sort targets by distance
+ // Calc ranges and store into a multimap
+ Map< float, int > range;
+
+ for (int i = 0;i < mTargets.mUseSize ; i++)
+ {
+ // Check the weapons and see if any wants to fire at it
+ bool validTarget = true;
+ for ( unsigned int j = 0 ; j < mWeaponList.size() ; j++)
+ {
+ if ( mWeaponList[ j ].weapon->IsValidTarget( mTargets[i] ) == false )
+ {
+ validTarget = false;
+ break;
+ }
+ }
+ if ( validTarget )
+ {
+ rmt::Vector targetPos;
+ mTargets[i]->GetPosition( &targetPos );
+ float distanceSqr = ( targetPos - mPosition ).MagnitudeSqr();
+ range.insert( distanceSqr, i );
+ }
+ }
+ if ( range.size() > 0 )
+ {
+ int targetIndex = range.begin()->second;
+
+ mTarget = mTargets[ targetIndex ];
+ //std::cout << "Found " << mTargets.mUseSize << " targets." << std::endl;
+ }
+ else
+ {
+ mTarget = NULL;
+ }
+}
+
+UFO::State
+UFO::ChooseNextState()
+{
+ return UFO::eID4Attack;
+}
+
+void
+UFO::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ // An object was killed, trigger a rescan by clearing the target list
+ case EVENT_OBJECT_DESTROYED:
+ case EVENT_VEHICLE_DESTROYED:
+ mTargets.ClearUse();
+ break;
+ default:
+ break;
+ }
+}
+
+
diff --git a/game/code/mission/ufo/ufo.h b/game/code/mission/ufo/ufo.h
new file mode 100644
index 0000000..f22248f
--- /dev/null
+++ b/game/code/mission/ufo/ufo.h
@@ -0,0 +1,165 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Name of subsystem or component this class belongs to.
+//
+// Description: This file contains...
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef UFO_H
+#define UFO_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <p3d/refcounted.hpp>
+#include <radmath/radmath.hpp>
+#include <p3d/p3dtypes.hpp>
+
+#include <vector>
+#include <render/culling/ReserveArray.h>
+#include <mission/boss.h>
+#include <events/eventlistener.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class Weapon;
+class StatePropDSG;
+class DynaPhysDSG;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+// Describe here what you require of the client which uses or
+// has this class - essentially, the converse of the description
+// above. A constraint is an expression of some semantic
+// condition that must be preserved, an invariant of a class or
+// relationship that must be preserved while the system is in a
+// steady state.
+//
+//===========================================================================
+/*
+class UFOState
+{
+public:
+ UFOState();
+ ~UFOState();
+
+ bool IsStateCompleted()=0;
+ void Update( float timeInMS )=0;
+
+private:
+};
+
+class UFOMovingState
+{
+public:
+ UFOMovingState();
+ ~UFOMovingState();
+
+ bool IsStateCompleted();
+ void Update( float timeInMS );
+ void SetTarget( const rmt::Vector& target );
+
+
+private:
+ // Where the UFO should move to next
+ rmt::Vector mMoveTo;
+};
+
+class UFOID4AttackState
+{
+public:
+ UFOID4AttackState();
+ ~UFOID4AttackState();
+
+private:
+};*/
+
+struct UFOWeapon
+{
+ Weapon* weapon;
+ rmt::Vector offset;
+};
+
+
+class UFO : public Boss, public EventListener
+{
+ public:
+ UFO();
+ virtual ~UFO();
+
+ virtual void Update( float timeInMS );
+ virtual bool LoadSetup( const char* statePropDSGName, const rmt::Vector& position );
+
+ virtual void AddWeapon( Weapon* pWeapon, const rmt::Vector& offset );
+
+ void HandleEvent( EventEnum id, void* pEventData );
+
+
+ enum State { eScanning, eMoving, eID4Attack, eTractorBeam };
+
+ protected:
+
+ std::vector< UFOWeapon, s2alloc<UFOWeapon> > mWeaponList;
+ StatePropDSG* mpDrawable;
+
+
+ ReserveArray< DynaPhysDSG* > mTargets;
+ tName mStatePropName;
+
+ protected:
+
+ void ScanForTargets();
+ bool IsStateCompleted();
+ UFO::State ChooseNextState();
+
+ // Figure out the correct acceleration to get to where we want to go
+ // Apply the acceleration to the velocity
+ void Navigate( float timeInMS );
+ // Apply velocity and move the object, also take care of the DSG move call
+ void Move( float timeInMS );
+
+
+
+ protected:
+
+ // Wish I could reuse some of the vehicle stuff for this.
+ rmt::Vector mVelocity;
+ rmt::Vector mPosition;
+
+ DynaPhysDSG* mTarget;
+
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow UFO from being copied and assigned.
+ UFO( const UFO& );
+ UFO& operator=( const UFO& );
+};
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/mission/ufo/weapon.h b/game/code/mission/ufo/weapon.h
new file mode 100644
index 0000000..05f8700
--- /dev/null
+++ b/game/code/mission/ufo/weapon.h
@@ -0,0 +1,88 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Name of subsystem or component this class belongs to.
+//
+// Description: This file contains...
+//
+// Authors: Name Here
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef WEAPON_H
+#define WEAPON_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+class DynaPhysDSG;
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+// Describe here what you require of the client which uses or
+// has this class - essentially, the converse of the description
+// above. A constraint is an expression of some semantic
+// condition that must be preserved, an invariant of a class or
+// relationship that must be preserved while the system is in a
+// steady state.
+//
+//===========================================================================
+class Weapon : public tEntity
+{
+ public:
+ Weapon();
+ virtual ~Weapon();
+
+ virtual bool LoadSetup( const char* statePropDSGName )=0;
+ virtual void Update( float timeInMS ) = 0;
+ virtual void Display() = 0;
+ virtual bool IsValidTarget( DynaPhysDSG* pDSG )const = 0;
+ virtual bool CanFire( DynaPhysDSG* pDSG )const = 0;
+ virtual void Fire( DynaPhysDSG* pDSG ) = 0;
+ virtual void SetPosition( const rmt::Vector& position ) = 0;
+
+
+ protected:
+
+
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow Weapon from being copied and assigned.
+ Weapon( const Weapon& );
+ Weapon& operator=( const Weapon& );
+
+ };
+
+inline Weapon::Weapon()
+{
+
+}
+inline Weapon::~Weapon()
+{
+
+}
+
+#endif \ No newline at end of file
diff --git a/game/code/pedpaths/allpedpaths.cpp b/game/code/pedpaths/allpedpaths.cpp
new file mode 100644
index 0000000..fd156f6
--- /dev/null
+++ b/game/code/pedpaths/allpedpaths.cpp
@@ -0,0 +1,3 @@
+#include <pedpaths/path.cpp>
+#include <pedpaths/pathmanager.cpp>
+#include <pedpaths/pathsegment.cpp>
diff --git a/game/code/pedpaths/path.cpp b/game/code/pedpaths/path.cpp
new file mode 100644
index 0000000..57fd7cb
--- /dev/null
+++ b/game/code/pedpaths/path.cpp
@@ -0,0 +1,118 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: path.cpp
+//
+// Description: Implements Path class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+
+#include <pedpaths/path.h>
+#include <pedpaths/pathsegment.h>
+
+#include <raddebug.hpp>
+#include <memory/srrmemory.h>
+
+class PathSegment;
+
+
+Path::Path() :
+ mIsClosed( false ),
+ mNumPathSegments( 0 ),
+ mNumPeds( 0 ),
+ mPathSegments( NULL )
+{
+}
+
+Path::Path( bool isClosed, int nSegments )
+{
+ mIsClosed = isClosed;
+ AllocateSegments( nSegments );
+}
+
+void Path::AllocateSegments( int nSegments )
+{
+MEMTRACK_PUSH_GROUP( "Path" );
+ rAssert( nSegments > 0 );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ #endif
+
+ if( mPathSegments != NULL )
+ {
+ // make sure we're only allocating ONCE, so we don't
+ // fragment the memory.
+ rAssert( false );
+ delete[] mPathSegments;
+ }
+
+ mPathSegments = new PathSegment*[ nSegments ];
+ int i;
+ for( i=0; i<nSegments; i++ )
+ {
+ mPathSegments[i] = new PathSegment;
+ }
+ mNumPathSegments = nSegments;
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ #endif
+
+
+MEMTRACK_POP_GROUP( "Path" );
+}
+
+Path::~Path()
+{
+ if( mPathSegments != NULL )
+ {
+ delete [] mPathSegments;
+ }
+ mPathSegments = NULL;
+}
+
+
+PathSegment* Path::GetPathSegmentByIndex(int index)
+{
+ rAssert( mPathSegments != NULL );
+ rAssert( 0 <= index && index < mNumPathSegments );
+
+ return mPathSegments[index];
+
+}
+
+bool Path::AddPedestrian()
+{
+ if( mNumPeds >= Path::MAX_PEDESTRIANS )
+ {
+ return false;
+ }
+ mNumPeds++;
+ return true;
+}
+bool Path::RemovePedestrian()
+{
+ if( mNumPeds <= 0 )
+ {
+ return false;
+ }
+ mNumPeds--;
+ return true;
+}
+
+bool Path::IsFull() const
+{
+ if( mNumPeds >= Path::MAX_PEDESTRIANS )
+ {
+ return true;
+ }
+ return false;
+}
diff --git a/game/code/pedpaths/path.h b/game/code/pedpaths/path.h
new file mode 100644
index 0000000..61c89f2
--- /dev/null
+++ b/game/code/pedpaths/path.h
@@ -0,0 +1,91 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: path.h
+//
+// Description: Defines Path class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+
+#ifndef PATH_H
+#define PATH_H
+
+//#include <pedpaths/pathsegment.h>
+#include <roads/geometry.h>
+
+class PathSegment;
+class Pedestrian;
+
+
+class Path
+{
+//MEMBERS
+public:
+
+ enum
+ {
+ MAX_PEDESTRIANS = 3 // number of peds to allow on a single path
+ };
+
+
+//METHODS
+public:
+ Path();
+ Path( bool isClosed, int nSegments );
+ ~Path();
+
+
+ PathSegment* GetPathSegmentByIndex( int index );
+ void AllocateSegments( int nSegments );
+
+
+
+ //ACCESSORS
+ bool IsFull() const;
+ bool IsClosed() const;
+ void SetIsClosed( bool isClosed );
+ int GetNumPathSegments() const;
+
+ bool AddPedestrian();
+ bool RemovePedestrian();
+
+//MEMBERS
+private:
+ bool mIsClosed;
+ int mNumPathSegments;
+ int mNumPeds;
+
+ // pointer to dynamically allocated PathSegment array
+ PathSegment** mPathSegments;
+
+
+//METHODS:
+private:
+
+
+};
+
+
+// ********************************* INLINES *******************************
+
+
+inline bool Path::IsClosed() const
+{
+ return mIsClosed;
+}
+
+inline void Path::SetIsClosed( bool isClosed )
+{
+ mIsClosed = isClosed;
+}
+
+inline int Path::GetNumPathSegments() const
+{
+ return mNumPathSegments;
+}
+
+
+#endif //PATH_H \ No newline at end of file
diff --git a/game/code/pedpaths/pathmanager.cpp b/game/code/pedpaths/pathmanager.cpp
new file mode 100644
index 0000000..14a7427
--- /dev/null
+++ b/game/code/pedpaths/pathmanager.cpp
@@ -0,0 +1,98 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pathmanager.cpp
+//
+// Description: Implements PathManager class
+//
+// History: 09/20/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#include <pedpaths/pathmanager.h>
+#include <pedpaths/path.h>
+#include <memory/srrmemory.h>
+
+// ************************** STATICS *********************************
+PathManager* PathManager::mInstance = NULL;
+
+
+PathManager* PathManager::GetInstance()
+{
+MEMTRACK_PUSH_GROUP( "PathManager" );
+ if ( !mInstance )
+ {
+ mInstance = new PathManager();
+ }
+MEMTRACK_POP_GROUP( "PathManager" );
+
+ return mInstance;
+}
+
+void PathManager::Destroy()
+{
+ delete mInstance;
+ mInstance = NULL;
+}
+
+// **************************** NONSTATICS *******************************
+
+PathManager::PathManager()
+{
+ mPaths = NULL;
+ mnPaths = 0;
+
+ mNextFreeIndex = 0;
+ // TODO:
+ // Allow automatic detection of number of paths rather than use some
+ // static amount.
+ AllocatePaths( MAX_PATHS );
+
+}
+PathManager::~PathManager()
+{
+ if( mPaths != NULL )
+ {
+ delete[] mPaths;
+ mPaths = NULL;
+ }
+ mnPaths = 0;
+}
+
+
+void PathManager::AllocatePaths( int nPaths )
+{
+ MEMTRACK_PUSH_GROUP( "PathManager" );
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ #endif
+
+ if( mPaths != NULL )
+ {
+ // dont' allow allocate to be called more than once...
+ rAssert( false );
+ delete[] mPaths;
+ }
+
+ mnPaths = nPaths;
+ mPaths = new Path[ mnPaths ];
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER);
+ #endif
+
+ MEMTRACK_POP_GROUP( "PathManager" );
+}
+
+Path* PathManager::GetFreePath()
+{
+ rAssert( 0 <= mNextFreeIndex && mNextFreeIndex < mnPaths );
+ rAssert( mPaths != NULL );
+
+ mNextFreeIndex++;
+ return &mPaths[mNextFreeIndex-1];
+}
diff --git a/game/code/pedpaths/pathmanager.h b/game/code/pedpaths/pathmanager.h
new file mode 100644
index 0000000..083242a
--- /dev/null
+++ b/game/code/pedpaths/pathmanager.h
@@ -0,0 +1,72 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pathmanager.h
+//
+// Description: Defines PathManager class
+//
+// History: 09/20/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+
+
+
+#ifndef PATHMANAGER_H
+#define PATHMANAGER_H
+
+class Path;
+
+
+class PathManager
+{
+//METHODS
+public:
+ //STATICS
+ static PathManager* GetInstance();
+ static void Destroy();
+
+ //NONSTATICS
+ Path* GetFreePath();
+
+//MEMBERS
+public:
+ enum
+ {
+ MAX_PATHS = 125
+ };
+
+
+
+
+//METHODS
+private:
+
+ PathManager();
+ virtual ~PathManager();
+
+ void AllocatePaths( int nPaths );
+
+ // These copy constructors are wasteful
+ PathManager( const PathManager& pathmanager );
+ PathManager& operator=( const PathManager& pathmanager );
+
+
+//MEMBERS
+private:
+
+ //STATICS
+ static PathManager* mInstance;
+
+ //NONSTATICS
+ Path* mPaths; // static array of Path objects
+ int mnPaths; // total number of Path objects
+ int mNextFreeIndex; // next free path (initially 0)
+};
+
+
+
+// ***************************** INLINES *************************************
+
+
+#endif // PATHMANAGER_H \ No newline at end of file
diff --git a/game/code/pedpaths/pathsegment.cpp b/game/code/pedpaths/pathsegment.cpp
new file mode 100644
index 0000000..b830e3a
--- /dev/null
+++ b/game/code/pedpaths/pathsegment.cpp
@@ -0,0 +1,93 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pathsegment.cpp
+//
+// Description: Implements PathSegment class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#include <pedpaths/pathsegment.h>
+#include <render/Culling/Bounds.h>
+
+void PathSegment::Initialize( Path* parent, int index, rmt::Vector start, rmt::Vector end )
+{
+ rAssert( parent != NULL );
+ rAssert( 0 <= index && index < parent->GetNumPathSegments() );
+
+ mParentPath = parent;
+ mIndexToParentPath = index;
+ mStartPos = start;
+ mEndPos = end;
+ mRadius = (end - start).Length() / 2.0f;
+}
+
+
+
+PathSegment::PathSegment() :
+ mParentPath( NULL ),
+ mIndexToParentPath( -1 )
+{
+}
+
+PathSegment::~PathSegment()
+{
+ mParentPath = NULL;
+}
+
+
+PathSegment::PathSegment( Path* parent, int index, rmt::Vector start, rmt::Vector end )
+{
+ rAssert( parent != NULL );
+ rAssert( 0 < index && index <= parent->GetNumPathSegments() );
+
+ Initialize( parent, index, start, end );
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Implementing IEntityDSG
+//////////////////////////////////////////////////////////////////////////
+void PathSegment::DisplayBoundingBox(tColour colour)
+{
+ rAssert( false );
+}
+void PathSegment::DisplayBoundingSphere(tColour colour)
+{
+ rAssert( false );
+}
+void PathSegment::GetBoundingBox(rmt::Box3D* box)
+{
+ Bounds3f tempBounds;
+ tempBounds.mMin.SetTo(mStartPos);
+ tempBounds.mMax.SetTo(mStartPos);
+ tempBounds.Accumulate(mEndPos);
+
+ box->Set( tempBounds.mMin, tempBounds.mMax );
+}
+void PathSegment::GetBoundingSphere(rmt::Sphere* sphere)
+{
+ sphere->centre = (mStartPos + mEndPos) * 0.5f;
+ sphere->radius = mRadius;
+}
+void PathSegment::Display()
+{
+ rAssert( false );
+}
+rmt::Vector* PathSegment::pPosition()
+{
+ rAssert( false );
+ return NULL;
+}
+const rmt::Vector& PathSegment::rPosition()
+{
+ rAssert( false );
+ return mStartPos; // return some garbage
+}
+void PathSegment::GetPosition( rmt::Vector* ipPosn )
+{
+ rAssert( false );
+}
+///////////////////////////////////////////////////////////////
diff --git a/game/code/pedpaths/pathsegment.h b/game/code/pedpaths/pathsegment.h
new file mode 100644
index 0000000..d2c3cba
--- /dev/null
+++ b/game/code/pedpaths/pathsegment.h
@@ -0,0 +1,136 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pathsegment.h
+//
+// Description: Defines PathSegment class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#ifndef PATHSEGMENT_H
+#define PATHSEGMENT_H
+
+
+#ifdef WORLD_BUILDER
+#include "../render/DSG/IEntityDSG.h"
+#else
+#include <render/DSG/IEntityDSG.h>
+#endif //WORLD_BUILDER
+
+#include <radmath/radmath.hpp>
+#include <raddebug.hpp>
+
+#include <pedpaths/path.h>
+//class Path;
+
+// Hey, I'm just a glorified line segment!
+class PathSegment :
+ public IEntityDSG
+{
+
+//MEMBERS
+public:
+
+//METHODS
+public:
+
+ PathSegment();
+ PathSegment( Path* parent, int index, rmt::Vector start, rmt::Vector end );
+ ~PathSegment();
+
+ void Initialize( Path* parent, int index, rmt::Vector start, rmt::Vector end );
+
+ void GetStartPos( rmt::Vector& pos );
+
+ void GetEndPos( rmt::Vector& pos );
+
+ Path* GetParentPath();
+ void SetParentPath( Path* path );
+
+ int GetIndexToParentPath() const;
+ void SetIndexToParentPath( int index );
+
+ /////////////////////////////////////////////////////////////////////////
+ // IEntityDSG Interface
+ /////////////////////////////////////////////////////////////////////////
+ void DisplayBoundingBox(tColour colour = tColour(0,255,0));
+ void DisplayBoundingSphere(tColour colour = tColour(0,255,0));
+
+ virtual void GetBoundingBox(rmt::Box3D* box);
+ virtual void GetBoundingSphere(rmt::Sphere* sphere);
+ void Display();
+
+ rmt::Vector* pPosition();
+ const rmt::Vector& rPosition();
+ void GetPosition( rmt::Vector* ipPosn );
+ /////////////////////////////////////////////////////////////////////////
+
+
+//MEMBERS
+private:
+
+ rmt::Vector mStartPos;
+ rmt::Vector mEndPos;
+
+ Path* mParentPath;
+ int mIndexToParentPath;
+
+
+ // Absolutely needed for IEntityDSG queries
+ float mRadius;
+ /*
+ rmt::Vector mPosition; // center point of the path segment
+ rmt::Sphere mBoundingSphere; // sphere around path segment
+ rmt::Box3D mBoundingBox;
+ */
+
+
+//METHODS
+private:
+};
+
+
+
+
+// ******************************* INLINES ******************************
+
+
+inline void PathSegment::GetStartPos( rmt::Vector& pos )
+{
+ pos = mStartPos;
+}
+
+inline void PathSegment::GetEndPos( rmt::Vector& pos )
+{
+ pos = mEndPos;
+}
+
+inline Path* PathSegment::GetParentPath()
+{
+ return mParentPath;
+}
+
+inline void PathSegment::SetParentPath( Path* path )
+{
+ rAssert( path != NULL );
+ mParentPath = path;
+}
+
+inline int PathSegment::GetIndexToParentPath() const
+{
+ return mIndexToParentPath;
+}
+inline void PathSegment::SetIndexToParentPath( int index )
+{
+ rAssert( mParentPath != NULL );
+ rAssert( 0 <= index && index < mParentPath->GetNumPathSegments() );
+ mIndexToParentPath = index;
+}
+
+
+
+
+
+#endif //PATHSEGMENT_H \ No newline at end of file
diff --git a/game/code/presentation/allpresentation.cpp b/game/code/presentation/allpresentation.cpp
new file mode 100644
index 0000000..94e3a71
--- /dev/null
+++ b/game/code/presentation/allpresentation.cpp
@@ -0,0 +1,12 @@
+#include <presentation/animplayer.cpp>
+#include <presentation/blinker.cpp>
+#include <presentation/cameraplayer.cpp>
+#include <presentation/language.cpp>
+#include <presentation/mouthflapper.cpp>
+#include <presentation/nisplayer.cpp>
+#include <presentation/playerdrawable.cpp>
+#include <presentation/presentation.cpp>
+#include <presentation/presentationanimator.cpp>
+#include <presentation/simpleanimationplayer.cpp>
+#include <presentation/transitionplayer.cpp>
+#include <presentation/tutorialmanager.cpp> \ No newline at end of file
diff --git a/game/code/presentation/animplayer.cpp b/game/code/presentation/animplayer.cpp
new file mode 100644
index 0000000..3c1e244
--- /dev/null
+++ b/game/code/presentation/animplayer.cpp
@@ -0,0 +1,356 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: animplayer.cpp
+//
+// Description: Implement AnimationPlayer
+//
+// History: 22/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <memory/srrmemory.h>
+
+#include <presentation/animplayer.h>
+#include <interiors/interiormanager.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/RenderManager/RenderLayer.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// AnimationPlayer::AnimationPlayer
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AnimationPlayer::AnimationPlayer() :
+ mState( ANIM_IDLE ),
+ mbPlayAfterLoad( true ),
+ mbExclusive( true ),
+ mbShowAlways ( false ),
+ mbKeepLayersFrozen( false ),
+ mbIsSkippable(true),
+ mpLoadDataCallback( NULL )
+{
+}
+
+//==============================================================================
+// AnimationPlayer::~AnimationPlayer
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AnimationPlayer::~AnimationPlayer()
+{
+}
+
+
+//==============================================================================
+// AnimationPlayer::LoadData
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void AnimationPlayer::LoadData
+(
+ const char* fileName,
+ bool bInInventory,
+ void* pUserData
+)
+{
+ if( mState == ANIM_IDLE )
+ {
+ mState = ANIM_LOADING;
+
+ if( bInInventory )
+ {
+ OnProcessRequestsComplete( NULL );
+ mSection = 0;
+ }
+ else
+ {
+ if(!GetInteriorManager()->IsInside())
+ {
+ GetLoadingManager()->AddRequest( FILEHANDLER_ANIMATION,
+ fileName,
+ GMA_CHARS_AND_GAGS,
+ fileName,
+ "Animation Player",
+ this );
+ }
+ else
+ {
+ GetLoadingManager()->AddRequest( FILEHANDLER_ANIMATION,
+ fileName,
+ GMA_DEFAULT,
+ fileName,
+ "Animation Player",
+ this );
+ }
+
+ mSection = tEntity::MakeUID(fileName);
+ }
+ }
+}
+
+
+//=============================================================================
+// AnimationPlayer::LoadData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* fileName )
+//
+// Return: void
+//
+//=============================================================================
+void AnimationPlayer::LoadData
+(
+ const char* fileName,
+ LoadDataCallBack* pCallback,
+ bool bInInventory,
+ void* pUserData
+)
+{
+ mpLoadDataCallback = pCallback;
+
+ this->LoadData( fileName, bInInventory, pUserData );
+}
+
+//=============================================================================
+// AnimationPlayer::OnProcessRequestsComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void AnimationPlayer::OnProcessRequestsComplete( void* pUserData )
+{
+ p3d::inventory->PushSection();
+
+ p3d::inventory->SelectSection( mSection );
+ bool currentOnly = p3d::inventory->GetCurrentSectionOnly( );
+ p3d::inventory->SetCurrentSectionOnly( true );
+
+ mState = ANIM_LOADED;
+
+ this->DoLoaded();
+
+ if( mpLoadDataCallback != NULL )
+ {
+ mpLoadDataCallback->OnLoadDataComplete();
+ }
+
+ if( mbPlayAfterLoad )
+ {
+ Play();
+ }
+
+ p3d::inventory->SetCurrentSectionOnly( currentOnly );
+
+ p3d::inventory->PopSection();
+}
+
+//=============================================================================
+// AnimationPlayer::Play
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void AnimationPlayer::Play()
+{
+ if( mState == ANIM_STOPPED )
+ {
+ mState = ANIM_PLAYING;
+ }
+
+ if( mState == ANIM_LOADED )
+ {
+ mState = ANIM_PLAYING;
+
+ if( mbExclusive )
+ {
+ EnterExclusive();
+ }
+ }
+/* DARWIN TODO: Why was this necessary?
+ else
+ {
+ mbPlayAfterLoad = true;
+ }
+*/
+}
+
+
+/*
+//=============================================================================
+// AnimationPlayer::StopAndCleanUp
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void AnimationPlayer::StopAndCleanUp()
+{
+ Stop();
+}
+*/
+
+
+//=============================================================================
+// AnimationPlayer::Render
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void AnimationPlayer::Render()
+{
+ if( (mState == ANIM_PLAYING) || mbShowAlways )
+ {
+ DoRender();
+ }
+}
+
+//=============================================================================
+// AnimationPlayer::Stop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void AnimationPlayer::Stop()
+{
+ if( mbExclusive )
+ {
+ LeaveExclusive();
+ }
+
+ //
+ // Hope this makes sense. Calling Stop() after ClearData() was causing
+ // the player to be stuck in the stopped state, since LoadData() wouldn't
+ // do anything. Added check for idle state. -- DE
+ //
+ if( mState != ANIM_IDLE )
+ {
+ mState = ANIM_STOPPED;
+ }
+}
+
+//=============================================================================
+// AnimationPlayer::ClearData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void AnimationPlayer::ClearData()
+{
+ mState = ANIM_IDLE;
+ p3d::inventory->DeleteSection( mSection );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// AnimationPlayer::EnterExclusive
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void AnimationPlayer::EnterExclusive()
+{
+ GetRenderManager()->FreezeForPresentation();
+
+ RenderLayer* pLayer = GetRenderManager()->mpLayer( RenderEnums::PresentationSlot );
+
+ if ( pLayer->IsDead() )
+ {
+ pLayer->Resurrect();
+ }
+ pLayer->Thaw();
+}
+
+//=============================================================================
+// AnimationPlayer::LeaveExclusive
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void AnimationPlayer::LeaveExclusive()
+{
+ if( !mbKeepLayersFrozen )
+ {
+ GetRenderManager()->ThawFromPresentation();
+
+ RenderLayer* pLayer = GetRenderManager()->mpLayer( RenderEnums::PresentationSlot );
+
+ pLayer->Freeze();
+ }
+}
diff --git a/game/code/presentation/animplayer.h b/game/code/presentation/animplayer.h
new file mode 100644
index 0000000..f9d4088
--- /dev/null
+++ b/game/code/presentation/animplayer.h
@@ -0,0 +1,135 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 22/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef ANIMPLAYER_H
+#define ANIMPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/p3dtypes.hpp>
+#include <loading/loadingmanager.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Base class for all presentation players. A player is something
+// that keeps an non-interactive animation going.
+//
+//=============================================================================
+
+class AnimationPlayer : public LoadingManager::ProcessRequestsCallback
+{
+ public:
+ AnimationPlayer();
+ virtual ~AnimationPlayer();
+
+
+ // Loads the data for this animation and sits on it
+ // Set bInInventory to true if the data is already loaded and
+ // in the p3d inventory.
+ // Clients must implement the callback if they want notification when
+ // the load is complete.
+ struct LoadDataCallBack
+ {
+ virtual void OnLoadDataComplete() = 0;
+ };
+ virtual void LoadData( const char* fileName,
+ LoadDataCallBack* pCallback,
+ bool bInInventory,
+ void* pUserData );
+
+ virtual void LoadData( const char* fileName,
+ bool bInInventory,
+ void* pUserData );
+
+ // Tells it to start playing right away once it's done loading
+ void SetPlayAfterLoad( bool bPlay ) { mbPlayAfterLoad = bPlay; }
+
+ void SetShowAlways(bool b) { mbShowAlways = b; }
+
+ // Whee!
+ virtual void Play();
+ virtual void Stop();
+
+ // TC: This just calls Stop(), so why do we need another function??
+ //
+// void StopAndCleanUp();
+
+ // Tells the player to enter "Exclusive Mode", which sets the
+ // Render Level to Presentation and should stop everything else
+ // in the game.
+ void SetExclusive( bool bIsExclusive ) { mbExclusive = bIsExclusive; }
+
+ bool IsPlaying() { return((( mState != ANIM_IDLE ) && (mState != ANIM_STOPPED ))); }
+ bool IsFinished() { return mState == ANIM_STOPPED; }
+ virtual void Update( unsigned int elapsedTime ) = 0;
+ void Render();
+
+ virtual void ClearData();
+ void Reset() { mState = ANIM_IDLE; }
+
+ void OnProcessRequestsComplete( void* pUserData );
+ bool GetKeepLayersFrozen( void ) const { return mbKeepLayersFrozen; }
+ void SetKeepLayersFrozen( bool IsKeepFrozen ) { mbKeepLayersFrozen = IsKeepFrozen; }
+ bool GetSkippable(void) const {return mbIsSkippable;}
+ void SetSkippable(bool IsSkippable) {mbIsSkippable = IsSkippable;}
+
+ protected:
+
+ enum AnimState
+ {
+ ANIM_IDLE,
+ ANIM_LOADING,
+ ANIM_LOADED,
+ ANIM_PLAYING,
+ ANIM_STOPPED,
+ NUM_STATES
+ };
+
+ AnimState GetState() { return( mState ); }
+ void SetState( AnimState state ) { mState = state; }
+
+ // A sub-class must implement these
+ // DoLoaded is called once the data is ready (in the case on p3d
+ // stuff, this means it is in the inventory)
+ virtual void DoLoaded() = 0;
+
+ // A sub-class should draw stuff here
+ virtual void DoRender() = 0;
+
+ void EnterExclusive();
+ void LeaveExclusive();
+ private:
+
+ //Prevent wasteful constructor creation.
+ AnimationPlayer( const AnimationPlayer& animPlayer );
+ AnimationPlayer& operator=( const AnimationPlayer& animPlayer );
+
+ AnimState mState;
+
+ bool mbPlayAfterLoad : 1;
+ bool mbExclusive : 1;
+ bool mbShowAlways : 1;
+ bool mbKeepLayersFrozen : 1;
+ bool mbIsSkippable : 1;
+
+ tUID mSection;
+
+ LoadDataCallBack* mpLoadDataCallback;
+};
+
+
+#endif //ANIMPLAYER_H_H
+
diff --git a/game/code/presentation/blinker.cpp b/game/code/presentation/blinker.cpp
new file mode 100644
index 0000000..3ed4577
--- /dev/null
+++ b/game/code/presentation/blinker.cpp
@@ -0,0 +1,243 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: Blinker.cpp
+//
+// Description: Implement Blinker
+//
+// History: 9/24/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/anim/textureanimation.hpp>
+#include <stdlib.h>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <memory/srrmemory.h>
+#include <presentation/blinker.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <main/game.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+static const int MIN_TIME = 2000;
+static const int MAX_TIME = 4000;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Blinker::Blinker
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Blinker::Blinker():
+ mCharacter( NULL ),
+ mController( NULL ) ,
+ mState( STATE_BLINKING )
+{
+}
+
+//==============================================================================
+// Blinker::~Blinker
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Blinker::~Blinker()
+{
+ if( mController != NULL )
+ {
+ mController->Release();
+ }
+}
+
+//=============================================================================
+// Blinker:SetCharacter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter )
+//
+// Return: void
+//
+//=============================================================================
+void Blinker::SetCharacter( Character* pCharacter )
+{
+// if( mCharacter == pCharacter )
+// {
+// return;
+// }
+ mCharacter = pCharacter;
+
+ if (pCharacter == 0)
+ {
+ if (mController)
+ {
+ mController->Release ();
+ mController = 0;
+ }
+ }
+ else
+ {
+ const char* modelName = GetCharacterManager()->GetModelName( mCharacter );
+ tMultiController* multicontroller = 0;
+ if( tName::MakeUID( modelName ) != tName::MakeUID( "npd" ) )
+ {
+ multicontroller = p3d::find< tMultiController >( modelName );
+ }
+ tRefCounted::Assign( mController, multicontroller );
+ }
+
+ if( mController == NULL )
+ {
+ return;
+ }
+
+ mController->SetCycleMode( FORCE_NON_CYCLIC );
+}
+
+//=============================================================================
+// Blinker::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void Blinker::Update( int elapsedTime )
+{
+ mTimeSinceBlink += elapsedTime;
+
+ if( mController == NULL )
+ {
+ if( (mCharacter->GetActiveFrame() & 0xf) == (GetGame()->GetFrameCount() & 0xf))
+ {
+ mTimeSinceBlink = 0;
+ const char* modelName = GetCharacterManager()->GetModelName( mCharacter );
+ tMultiController* multicontroller = 0;
+ if(tName::MakeUID(modelName) != tName::MakeUID("npd"))
+ {
+ multicontroller = p3d::find< tMultiController >( modelName );
+ }
+ if( multicontroller != NULL )
+ {
+ tRefCounted::Assign( mController, multicontroller );
+ }
+ else
+ {
+ return;
+ }
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ switch( mState )
+ {
+ case STATE_WAITING:
+ {
+ rAssert( mController != NULL );
+ if( mTimeSinceBlink >= mTimeTarget )
+ {
+ mController->Reset();
+ mState = STATE_BLINKING;
+ }
+
+ break;
+ }
+ case STATE_BLINKING:
+ {
+ rAssert( mController != NULL );
+
+ mController->Advance( static_cast<float>( elapsedTime ), true );
+
+ float frames = mController->GetNumFrames();
+ float frame = mController->GetFrame();
+ if( frame >= frames )
+ {
+ mTimeTarget = MIN_TIME + (MAX_TIME - MIN_TIME) * rand() / RAND_MAX;
+ mTimeSinceBlink = 0;
+ mState = STATE_WAITING;
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// Blinker::StartBlinking
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Blinker::StartBlinking()
+{
+ if( mState == STATE_INVALID )
+ {
+ mState = STATE_BLINKING;
+ }
+}
+
+//=============================================================================
+// Blinker::StopBlinking
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Blinker::StopBlinking()
+{
+ if( mState != STATE_INVALID )
+ {
+ mState = STATE_INVALID;
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/presentation/blinker.h b/game/code/presentation/blinker.h
new file mode 100644
index 0000000..ecefb82
--- /dev/null
+++ b/game/code/presentation/blinker.h
@@ -0,0 +1,68 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: blinker.h
+//
+// Description: Blahblahblah
+//
+// History: 9/24/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef BLINKER_H
+#define BLINKER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+class Character;
+class tMultiController;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class Blinker
+{
+public:
+ Blinker();
+ virtual ~Blinker();
+
+ void SetCharacter( Character* pCharacter );
+
+ void Update( int elapsedTime );
+
+ void StartBlinking();
+ void StopBlinking();
+
+private:
+
+ //Prevent wasteful constructor creation.
+ Blinker( const Blinker& blinker );
+ Blinker& operator=( const Blinker& blinker );
+
+ Character* mCharacter;
+ tMultiController* mController;
+
+ int mTimeSinceBlink;
+ int mTimeTarget;
+
+ enum BlinkState
+ {
+ STATE_INVALID,
+ STATE_WAITING,
+ STATE_BLINKING
+ };
+
+ BlinkState mState;
+};
+
+
+#endif //BLINKER_H
diff --git a/game/code/presentation/cameraplayer.cpp b/game/code/presentation/cameraplayer.cpp
new file mode 100644
index 0000000..7abf052
--- /dev/null
+++ b/game/code/presentation/cameraplayer.cpp
@@ -0,0 +1,111 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: cameraplayer.cpp
+//
+// Description: Implement CameraPlayer
+//
+// History: 22/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <presentation/cameraplayer.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// CameraPlayer::CameraPlayer
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CameraPlayer::CameraPlayer() :
+ mpAnimation( NULL )
+{
+ SetExclusive( false );
+}
+
+//==============================================================================
+// CameraPlayer::~CameraPlayer
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CameraPlayer::~CameraPlayer()
+{
+}
+
+//=============================================================================
+// CameraPlayer::ClearData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void CameraPlayer::ClearData()
+{
+ SimpleAnimationPlayer::ClearData();
+
+ if( mpAnimation != NULL )
+ {
+ mpAnimation->Release();
+ mpAnimation = NULL;
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// CameraPlayer::DoLoaded
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void CameraPlayer::DoLoaded()
+{
+ SimpleAnimationPlayer::DoLoaded();
+
+ mpAnimation = p3d::find<tAnimation>( GetAnimationName() );
+ rAssert( mpAnimation );
+ mpAnimation->AddRef();
+}
+
diff --git a/game/code/presentation/cameraplayer.h b/game/code/presentation/cameraplayer.h
new file mode 100644
index 0000000..24a8f82
--- /dev/null
+++ b/game/code/presentation/cameraplayer.h
@@ -0,0 +1,55 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 22/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef CAMERAPLAYER_H
+#define CAMERAPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <presentation/simpleanimationplayer.h>
+
+//========================================
+// Forward References
+//========================================
+
+class tAnimation;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class CameraPlayer : public SimpleAnimationPlayer
+{
+ public:
+ CameraPlayer();
+ virtual ~CameraPlayer();
+
+ virtual void ClearData();
+
+ protected:
+ virtual void DoLoaded();
+
+ private:
+
+ //Prevent wasteful constructor creation.
+ CameraPlayer( const CameraPlayer& cameraPlayer );
+ CameraPlayer& operator=( const CameraPlayer& cameraPlayer );
+
+ tAnimation* mpAnimation;
+};
+
+
+#endif //CAMERAPLAYER_H
+
diff --git a/game/code/presentation/fmvplayer/allfmvplayergc.cpp b/game/code/presentation/fmvplayer/allfmvplayergc.cpp
new file mode 100644
index 0000000..e753799
--- /dev/null
+++ b/game/code/presentation/fmvplayer/allfmvplayergc.cpp
@@ -0,0 +1,2 @@
+#include <presentation/fmvplayer/fmvplayer.cpp>
+#include <presentation/fmvplayer/fmvuserinputhandler.cpp>
diff --git a/game/code/presentation/fmvplayer/allfmvplayerps2.cpp b/game/code/presentation/fmvplayer/allfmvplayerps2.cpp
new file mode 100644
index 0000000..e753799
--- /dev/null
+++ b/game/code/presentation/fmvplayer/allfmvplayerps2.cpp
@@ -0,0 +1,2 @@
+#include <presentation/fmvplayer/fmvplayer.cpp>
+#include <presentation/fmvplayer/fmvuserinputhandler.cpp>
diff --git a/game/code/presentation/fmvplayer/fmvplayer.cpp b/game/code/presentation/fmvplayer/fmvplayer.cpp
new file mode 100644
index 0000000..4db5dc7
--- /dev/null
+++ b/game/code/presentation/fmvplayer/fmvplayer.cpp
@@ -0,0 +1,526 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: fmvplayer.cpp
+//
+// Description: Implement FMVPlayer
+//
+// History: 17/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+#include <radplatform.hpp>
+#include <radsound.hpp>
+#include <raddebug.hpp>
+#include <radmovie2.hpp>
+#include <radcontroller.hpp>
+#include <radfile.hpp>
+#include <radmemory.hpp>
+//========================================
+// Project Includes
+//========================================
+#include <main/game.h>
+#include <main/platform.h>
+#include <gameflow/gameflow.h>
+#include <presentation/fmvplayer/FMVplayer.h>
+#include <input/inputmanager.h>
+#include <sound/soundmanager.h>
+#include <memory/srrmemory.h>
+#include <contexts/bootupcontext.h>
+#include <presentation/fmvplayer/fmvplayer.h>
+#include <presentation/fmvplayer/fmvuserinputhandler.h>
+#include <presentation/presevents/fmvevent.h>
+#include <presentation/gui/guitextbible.h>
+#include <events/eventmanager.h>
+
+#include <render/RenderManager/RenderManager.h>
+#include <p3d/utility.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/context.hpp>
+#include <radmemory.hpp>
+#include <radmemorymonitor.hpp>
+#include <pddi/pddi.hpp>
+
+#include <string.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+#define MOVIE_MAX_WIDTH 640
+#ifdef PAL
+ #define MOVIE_MAX_HEIGHT 512
+#else
+ #define MOVIE_MAX_HEIGHT 480
+#endif
+#define MOVIE_ENCODED_VIDEO_BUFFER_SIZE ( 512 * 1024 )
+#define MOVIE_PRIMARY_AUDIO_BUFFER_SIZE 2000
+#define MOVIE_SECONDARY_AUDIO_BUFFER_SIZE 1000
+#define MOVIE_AUDIO_BUFFER_SIZE_TYPE IRadSoundHalAudioFormat::Milliseconds
+
+#ifdef RAD_XBOX
+static const float VOLUME_MULTIPLIER = 0.75f;
+#else
+static const float VOLUME_MULTIPLIER = 1.0f;
+#endif
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// FMVPlayer::FMVPlayer
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+FMVPlayer::FMVPlayer() :
+ m_refIRadMoviePlayer( NULL ),
+ mElapsedTime( 0.0f ),
+ mFadeOut(-1.0f)
+{
+ m_UserInputHandler = new FMVUserInputHandler;
+ m_UserInputHandler->AddRef();
+}
+
+//==============================================================================
+// FMVPlayer::~FMVPlayer
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+FMVPlayer::~FMVPlayer()
+{
+ m_UserInputHandler->Release();
+ m_UserInputHandler = NULL;
+
+ if( m_refIRadMoviePlayer != NULL )
+ {
+ m_refIRadMoviePlayer->Unload( );
+ m_refIRadMoviePlayer = NULL;
+ }
+}
+
+//=============================================================================
+// FMVPlayer::LoadData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* fileName, bool bInInventory )
+//
+// Return: void
+//
+//=============================================================================
+void FMVPlayer::LoadData( const char* fileName, bool bInInventory, void* pUserData )
+{
+ if( GetState() == ANIM_IDLE || GetState() == ANIM_LOADING )
+ {
+ GetSoundManager()->StopForMovie();
+ while( !( GetSoundManager()->IsStoppedForMovie() ) )
+ {
+ ::radMovieService2( );
+ ::radFileService( );
+ SoundManager::GetInstance()->Update();
+ SoundManager::GetInstance()->UpdateOncePerFrame( 0, NUM_CONTEXTS, false );
+ }
+
+ GameMemoryAllocator allocator = GMA_LEVEL_MOVIE;
+ unsigned int audioIndex = 0;
+ FMVEvent::FMVEventData* data = reinterpret_cast<FMVEvent::FMVEventData*>(pUserData);
+ if( data )
+ {
+ allocator = data->Allocator;
+
+ if ( allocator >= GMA_ANYWHERE_IN_LEVEL )
+ {
+ ::SetupAllocatorSearch( allocator );
+ allocator = GMA_ALLOCATOR_SEARCH;
+ }
+
+ audioIndex = data->AudioIndex;
+
+ if( data->KillMusic )
+ {
+ GetEventManager()->TriggerEvent( EVENT_STOP_THE_MUSIC );
+ }
+ }
+ HeapMgr()->PushHeap( allocator );
+ Initialize( allocator );
+ m_refIRadMoviePlayer->Load( fileName, audioIndex );
+ mDriveFinished = false;
+ Game::GetInstance()->GetPlatform()->GetHostDrive()->AddCompletionCallback( this, 0 );
+
+ while( !mDriveFinished )
+ {
+ ::radMovieService2( );
+ ::radFileService( );
+ SoundManager::GetInstance()->Update();
+ SoundManager::GetInstance()->UpdateOncePerFrame( 0, NUM_CONTEXTS, false );
+ }
+ HeapMgr()->PopHeap(allocator);
+ SetState( ANIM_LOADED );
+ }
+}
+
+//=============================================================================
+// FMVPlayer::Play
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FMVPlayer::Play()
+{
+ if(( GetState() == ANIM_LOADED ) && ( m_refIRadMoviePlayer != NULL ))
+ {
+ AnimationPlayer::Play();
+ FadeScreen(0.0f);
+ p3d::context->SwapBuffers();
+ p3d::display->SetForceVSync(true);
+#ifdef FINAL
+ m_UserInputHandler->SetEnabled(GetSkippable());
+#endif
+ // register GUI user input handler for all controllers
+ for( unsigned i = 0; i < GetInputManager()->GetMaxControllers(); i++ )
+ {
+ GetInputManager()->RegisterMappable( i, m_UserInputHandler );
+ }
+ mElapsedTime = 0.0f;
+ mFadeOut = -1.0f;
+
+ mMovieVolume = VOLUME_MULTIPLIER;
+ if(GetGameFlow()->GetCurrentContext() != CONTEXT_BOOTUP)
+ {
+ mMovieVolume *= GetSoundManager()->GetDialogueVolume();
+ }
+
+ m_refIRadMoviePlayer->SetVolume(mMovieVolume);
+ m_refIRadMoviePlayer->Play( );
+ }
+}
+
+/*=============================================================================
+Causes the movie to begin fading out. When it's finished fading out it stops.
+Fade out time is hard coded right now to half a second.
+=============================================================================*/
+void FMVPlayer::Abort(void)
+{
+ if(mFadeOut == -1.0f)
+ {
+ mFadeOut = 1.0f;
+ }
+}
+
+//=============================================================================
+// FMVPlayer::Stop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FMVPlayer::Stop()
+{
+ // Force a clear screen.
+ FadeScreen(0.0f);
+ p3d::context->SwapBuffers();
+ p3d::display->SetForceVSync(false);
+ if( this->IsPlaying() && m_refIRadMoviePlayer != NULL )
+ {
+ for( unsigned i = 0; i < GetInputManager()->GetMaxControllers(); i++ )
+ {
+ GetInputManager()->UnregisterMappable( i, m_UserInputHandler );
+ }
+ m_UserInputHandler->SetEnabled(true);
+ GetSoundManager()->ResumeAfterMovie();
+ AnimationPlayer::Stop();
+
+ ClearData();
+ }
+ mFadeOut = -1.0f;
+}
+
+//=============================================================================
+// FMVPlayer::ForceStop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+#ifdef RAD_WIN32
+void FMVPlayer::ForceStop()
+{
+ // Force a clear screen.
+ if( this->IsPlaying() && m_refIRadMoviePlayer != NULL )
+ {
+ for( unsigned i = 0; i < GetInputManager()->GetMaxControllers(); i++ )
+ {
+ GetInputManager()->UnregisterMappable( i, m_UserInputHandler );
+ }
+ m_UserInputHandler->SetEnabled(true);
+ GetSoundManager()->ResumeAfterMovie();
+ AnimationPlayer::Stop();
+
+ ClearData();
+ }
+}
+#endif
+
+//=============================================================================
+// FMVPlayer::Pause
+//=============================================================================
+void FMVPlayer::Pause()
+{
+ if( this->IsPlaying() && m_refIRadMoviePlayer != NULL )
+ {
+ m_refIRadMoviePlayer->Pause( );
+ }
+}
+
+//=============================================================================
+// FMVPlayer::UnPause
+//=============================================================================
+void FMVPlayer::UnPause()
+{
+ if( this->IsPlaying() && m_refIRadMoviePlayer != NULL )
+ {
+ m_refIRadMoviePlayer->Play( );
+ }
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// FMVPlayer::Initialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FMVPlayer::Initialize( radMemoryAllocator Allocator )
+{
+ //
+ // Initialize the movie player.
+ //
+ // This is where all the memory will be allocated.
+ //
+ ref< IRadMovieRenderLoop > refIRadMovieRenderLoop = this;
+ ref< IRadMovieRenderStrategy > refIRadMovieRenderStrategy =
+ ::radMovieSimpleFullScreenRenderStrategyCreate( Allocator );
+
+ // Note that there is a problem if we start creating multiple movie players
+ //(why would we ever do this???). The allocator is stored as a single global
+ //value in the movieplayer (binkmovieplayer.cpp for instance) and it is set
+ //when you call ::radMoviePlayerCreate2(). So if you make multiple calls
+ //the most recent allocator you pass is the one used.
+ m_refIRadMoviePlayer = ::radMoviePlayerCreate2( Allocator );
+ rAssert( m_refIRadMoviePlayer != NULL );
+
+#if defined(RAD_XBOX) || defined(RAD_GAMECUBE) || defined(RAD_WIN32)
+ m_refIRadMoviePlayer->Initialize(
+ refIRadMovieRenderLoop,
+ refIRadMovieRenderStrategy );
+#else // PS2
+ m_refIRadMoviePlayer->Initialize(
+ refIRadMovieRenderLoop,
+ refIRadMovieRenderStrategy,
+ MOVIE_MAX_WIDTH, MOVIE_MAX_HEIGHT,
+ MOVIE_ENCODED_VIDEO_BUFFER_SIZE,
+ MOVIE_PRIMARY_AUDIO_BUFFER_SIZE,
+ MOVIE_SECONDARY_AUDIO_BUFFER_SIZE,
+ MOVIE_AUDIO_BUFFER_SIZE_TYPE );
+#endif
+}
+
+//=============================================================================
+// FMVPlayer::DoRender
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FMVPlayer::DoRender()
+{
+ IRadMoviePlayer2::State state = m_refIRadMoviePlayer->GetState( );
+
+ mFrameReady = false;
+
+ while( !mFrameReady && (state != IRadMoviePlayer2::NoData))
+ {
+ ::radMovieService2( );
+ ::radSoundHalSystemGet( )->Service( );
+// ::radSoundHalSystemGet( )->ServiceOncePerFrame( );
+ ::radFileService( );
+
+ state = m_refIRadMoviePlayer->GetState( );
+ if ( state == IRadMoviePlayer2::ReadyToPlay )
+ {
+ // [ps] Here we are paused, so fall out to the regular game.
+ break;
+ }
+ }
+
+ if((mFadeOut <= 0.0f) && (mFadeOut != -1.0f))
+ {
+ Stop();
+ }
+ if( (state == IRadMoviePlayer2::NoData) || ( m_refIRadMoviePlayer == NULL) )
+ {
+ Stop();
+ }
+}
+
+void FMVPlayer::IterateLoop( IRadMoviePlayer2* pIRadMoviePlayer )
+{
+ rAssert( pIRadMoviePlayer != NULL );
+
+ pIRadMoviePlayer->Render();
+ if(mFadeOut > 0.0f )
+ {
+ pIRadMoviePlayer->SetVolume(mMovieVolume * mFadeOut);
+ FadeScreen(mFadeOut);
+ }
+ float deltaTime = mElapsedTime;
+ mElapsedTime = pIRadMoviePlayer->GetCurrentFrameNumber() / pIRadMoviePlayer->GetFrameRate();
+ if(mFadeOut > 0.0f )
+ {
+ deltaTime = mElapsedTime - deltaTime;
+ mFadeOut -= deltaTime * 2.0f; // Half second fade.
+ }
+ mFrameReady = true;
+}
+
+//=============================================================================
+// FMVPlayer::Finalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void FMVPlayer::ClearData()
+{
+ //
+ // Free up the radmovie movie player stuff
+ //
+ if( m_refIRadMoviePlayer != NULL )
+ {
+ // I think there is a case where the movie player doesn't shut down properly because the
+ //decode buffer is still in use as a texture in the draw pipeline, preventing it from being
+ //freed.
+ p3d::pddi->DrawSync();
+ m_refIRadMoviePlayer->Unload( );
+ m_refIRadMoviePlayer = NULL;
+
+ mDriveFinished = false;
+ Game::GetInstance()->GetPlatform()->GetHostDrive()->AddCompletionCallback( this, 0 );
+
+ while( !mDriveFinished )
+ {
+ ::radMovieService2( );
+ ::radFileService( );
+ SoundManager::GetInstance()->Update();
+ SoundManager::GetInstance()->UpdateOncePerFrame( 0, NUM_CONTEXTS, false );
+ }
+ }
+
+ AnimationPlayer::ClearData();
+}
+
+void FMVPlayer::OnDriveOperationsComplete( void* pUserData )
+{
+ mDriveFinished = true;
+}
+
+void FMVPlayer::FadeScreen(float Alpha)
+{
+ tColour c;
+ c.Set( 0, 0, 0, int(0xFF * (1.0f - rmt::Clamp(Alpha, 0.0f, 1.0f))) );
+ p3d::stack->Push();
+ bool oldZWrite = p3d::pddi->GetZWrite();
+ pddiCompareMode oldZComp = p3d::pddi->GetZCompare();
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( false );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( PDDI_COMPARE_ALWAYS );
+ }
+ p3d::stack->LoadIdentity();
+ p3d::pddi->SetProjectionMode( PDDI_PROJECTION_ORTHOGRAPHIC );
+ pddiColour oldAmbient = p3d::pddi->GetAmbientLight();
+ p3d::pddi->SetAmbientLight( pddiColour( 255, 255, 255 ) );
+
+ pddiPrimStream* overlay = 0;
+
+ pddiShader* overlayShader = BootupContext::GetInstance()->GetSharedShader();
+ rAssert( overlayShader );
+
+ overlayShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ overlayShader->SetInt( PDDI_SP_ISLIT, 0 );
+ overlayShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_FLAT );
+
+ overlay = p3d::pddi->BeginPrims( overlayShader, PDDI_PRIM_TRISTRIP, PDDI_V_C, 4 );
+
+ overlay->Colour( c );
+ overlay->Coord( 0.5f, -0.5f, 1.0f );
+ overlay->Colour( c );
+ overlay->Coord( -0.5f, -0.5f, 1.0f );
+ overlay->Colour( c );
+ overlay->Coord( 0.5f, 0.5f, 1.0f );
+ overlay->Colour( c );
+ overlay->Coord( -0.5f, 0.5f, 1.0f );
+
+ p3d::pddi->EndPrims( overlay );
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_PERSPECTIVE);
+ p3d::pddi->SetAmbientLight( oldAmbient );
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( true );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( oldZComp );
+ }
+ p3d::stack->Pop();
+}
diff --git a/game/code/presentation/fmvplayer/fmvplayer.h b/game/code/presentation/fmvplayer/fmvplayer.h
new file mode 100644
index 0000000..663e091
--- /dev/null
+++ b/game/code/presentation/fmvplayer/fmvplayer.h
@@ -0,0 +1,108 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 17/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef FMVPLAYER_H
+#define FMVPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <presentation/animplayer.h>
+#include <radmovie2.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+class FMVUserInputHandler;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class FMVPlayer : public AnimationPlayer,
+ public IRadMovieRenderLoop,
+ public IRadDriveCompletionCallback,
+ public radRefCount
+
+{
+ IMPLEMENT_REFCOUNTED( "FMVPlayer" )
+
+public:
+
+ FMVPlayer();
+ virtual ~FMVPlayer();
+
+ // playback control
+ virtual void Play();
+ virtual void Abort(void);
+ virtual void Stop();
+ virtual void Pause();
+ virtual void UnPause();
+
+#ifdef RAD_WIN32
+ void ForceStop();
+#endif
+
+ // loading
+ virtual void PreLoad(void) { SetState(ANIM_LOADING);}
+ virtual void LoadData( const char* fileName, bool bInInventory, void* pUserData );
+
+ // animation updating (doesn't need any)
+ virtual void Update( unsigned int elapsedTime ) {};
+
+ // IRadMovieRenderLoop interface, called by radMovie service eac time a frame is ready
+ void IterateLoop( IRadMoviePlayer2* pIRadMoviePlayer );
+
+ // reset all internal data
+ virtual void ClearData();
+
+ // How long the FMV has played in seconds. 0 if the movie hasn't played yet.
+ float GetElapsedTime() { return mElapsedTime; }
+
+ inline FMVUserInputHandler* GetUserInputHandler() const
+ {
+ return m_UserInputHandler;
+ }
+
+protected:
+ void Initialize( radMemoryAllocator Allocator );
+
+ // AnimationPlayer interface
+ virtual void DoLoaded() {}; // Movies aren't loader by main game loader, so don't need to handle this
+ virtual void DoRender(); // render a frame (if one is avilible)
+
+ // Implements IRadDriveCompletionCallback. We'll be called this when the movie finishes streaming.
+ //We need to wait for the drive to finish so the internal memory in radmovie is freed.
+ virtual void OnDriveOperationsComplete( void* pUserData );
+ void FadeScreen(float Alpha);
+
+private:
+ //Prevent wasteful constructor creation.
+ FMVPlayer( const FMVPlayer& fmvPlayer );
+ FMVPlayer& operator=( const FMVPlayer& fmvPlayer );
+
+ FMVUserInputHandler* m_UserInputHandler;
+ ref< IRadMoviePlayer2 > m_refIRadMoviePlayer;
+
+ bool mFrameReady;
+ float mElapsedTime; // Elapsed playing time. So you know if you should let the player skip the movie.
+ bool mDriveFinished;
+ float mFadeOut;
+ float mMovieVolume;
+};
+
+
+#endif //FMVPLAYER_H
+
diff --git a/game/code/presentation/fmvplayer/fmvuserinputhandler.cpp b/game/code/presentation/fmvplayer/fmvuserinputhandler.cpp
new file mode 100644
index 0000000..08c0ddf
--- /dev/null
+++ b/game/code/presentation/fmvplayer/fmvuserinputhandler.cpp
@@ -0,0 +1,257 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: FMVUserInputHandler
+//
+// Description: Implementation of the FMVUserInputHandler class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/10/20 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+
+#include <presentation/fmvplayer/fmvplayer.h>
+#include <presentation/fmvplayer/fmvuserinputhandler.h>
+
+#include <presentation/presentation.h>
+#include <presentation/gui/guiscreenmessage.h>
+#include <input/inputmanager.h>
+#include <gameflow/gameflow.h>
+#include <contexts/context.h>
+#include <main/game.h>
+#include <main/platform.h>
+
+#include <raddebug.hpp>
+
+static const float MIN_MOVIE_TIME = 0.125f;
+
+// this will allow any buttons to skip FMV
+//
+//#define ANY_BUTTON_SKIP
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+struct ControlMap
+{
+ char* inputName;
+ FMVInput::FMVInputEnum inputID;
+};
+
+const ControlMap FMV_CONTROL_MAP[] =
+{
+#ifdef RAD_GAMECUBE
+ { "Menu", FMVInput::Skip },
+ { "A", FMVInput::Skip },
+ #ifdef ANY_BUTTON_SKIP
+ { "LeftStickX", FMVInput::Skip },
+ { "LeftStickY", FMVInput::Skip },
+ { "RightStickX", FMVInput::Skip },
+ { "RightStickY", FMVInput::Skip },
+ { "DPadLeft", FMVInput::Skip },
+ { "DPadRight", FMVInput::Skip },
+ { "DPadUp", FMVInput::Skip },
+ { "DPadDown", FMVInput::Skip },
+ { "B", FMVInput::Skip },
+ { "X", FMVInput::Skip },
+ { "Y", FMVInput::Skip },
+ { "Z", FMVInput::Skip },
+ { "TriggerL", FMVInput::Skip },
+ { "TriggerR", FMVInput::Skip },
+ #endif
+#endif // RAD_GAMECUBE
+
+#ifdef RAD_PS2
+ { "Start", FMVInput::Start},
+ { "X", FMVInput::Skip },
+ { "DPadLeft", FMVInput::UNKNOWN },
+ { "DPadRight", FMVInput::UNKNOWN },
+ { "DPadUp", FMVInput::UNKNOWN },
+ { "DPadDown", FMVInput::UNKNOWN },
+ { "Select", FMVInput::UNKNOWN },
+ { "Triangle", FMVInput::UNKNOWN },
+ { "Circle", FMVInput::UNKNOWN },
+ { "Square", FMVInput::UNKNOWN },
+ { "L1", FMVInput::UNKNOWN },
+ { "R1", FMVInput::UNKNOWN },
+ { "L2", FMVInput::UNKNOWN },
+ { "R2", FMVInput::UNKNOWN },
+ #ifdef ANY_BUTTON_SKIP
+ { "LeftStickX", FMVInput::Skip },
+ { "LeftStickY", FMVInput::Skip },
+ { "RightStickX", FMVInput::Skip },
+ { "RightStickY", FMVInput::Skip },
+ #endif
+#endif // RAD_PS2
+
+#ifdef RAD_XBOX
+ { "A", FMVInput::Skip },
+ { "Start", FMVInput::Start},
+ #ifdef ANY_BUTTON_SKIP
+ { "LeftStickX", FMVInput::Skip },
+ { "LeftStickY", FMVInput::Skip },
+ { "RightStickX", FMVInput::Skip },
+ { "RightStickY", FMVInput::Skip },
+ { "DPadLeft", FMVInput::Skip },
+ { "DPadRight", FMVInput::Skip },
+ { "DPadUp", FMVInput::Skip },
+ { "DPadDown", FMVInput::Skip },
+ { "Back", FMVInput::Skip },
+ { "B", FMVInput::Skip },
+ { "X", FMVInput::Skip },
+ { "Y", FMVInput::Skip },
+ { "White", FMVInput::Skip },
+ { "Black", FMVInput::Skip },
+ { "LeftTrigger", FMVInput::Skip },
+ { "RightTrigger", FMVInput::Skip },
+ #endif
+#endif // RAD_XBOX
+
+#ifdef RAD_WIN32
+ { "feStart", FMVInput::Skip },
+ { "feBack", FMVInput::Skip },
+ { "feKeyboardBack", FMVInput::Skip },
+ { "feSelect", FMVInput::Skip },
+#endif // RAD_WIN32
+
+ { "", FMVInput::UNKNOWN }
+};
+
+const int NUM_FMV_CONTROL_MAPPINGS = sizeof( FMV_CONTROL_MAP ) /
+ sizeof( FMV_CONTROL_MAP[ 0 ] );
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// FMVUserInputHandler::FMVUserInputHandler
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+FMVUserInputHandler::FMVUserInputHandler( void )
+: m_isEnabled( true ),
+ m_controllerPromptShown( false ),
+ m_controllerReconnect( false )
+{
+}
+
+
+//===========================================================================
+// FMVUserInputHandler::~FMVUserInputHandler
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+FMVUserInputHandler::~FMVUserInputHandler( void )
+{
+}
+
+
+
+//===========================================================================
+// FMVUserInputHandler::OnButton
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+//////////////////////////////////////////////////////////////////////////////
+// IButtonedObject declarations
+//
+void FMVUserInputHandler::OnButton( int controllerId, int buttonId, const IButton* pButton )
+{
+}
+
+void FMVUserInputHandler::OnButtonUp( int controllerId, int buttonId, const IButton* pButton )
+{
+}
+
+void FMVUserInputHandler::OnControllerDisconnect( int id )
+{
+#ifndef RAD_GAMECUBE
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( 0 );
+ if (GetGameFlow()->GetCurrentContext() == CONTEXT_GAMEPLAY
+ && controllerID == id )
+ {
+ m_controllerPromptShown = true;
+ char str_buffer[256];
+ CGuiScreenMessage::GetControllerDisconnectedMessage(controllerID, str_buffer, 255);
+ GetGame()->GetPlatform()->OnControllerError(str_buffer);
+ }
+#endif
+}
+void FMVUserInputHandler::OnControllerConnect( int id )
+{
+#ifndef RAD_GAMECUBE
+ if (m_controllerPromptShown && id==GetInputManager()->GetControllerIDforPlayer( 0 ))
+ {
+ m_controllerReconnect = true;
+ }
+#endif
+}
+
+
+void FMVUserInputHandler::OnButtonDown( int controllerId, int buttonId, const IButton* pButton )
+{
+ bool button_skip = buttonId == FMVInput::Skip;
+
+ if (buttonId==FMVInput::Start) // start also skip
+ button_skip = true;
+
+ if (buttonId==FMVInput::Start && m_controllerReconnect)
+ {
+ m_controllerReconnect = false;
+ GetGame()->GetPlatform()->ClearControllerError();
+ m_controllerPromptShown = false;
+ }
+ else if( m_isEnabled && button_skip )
+ {
+ if( GetPresentationManager()->GetFMVPlayer()->IsPlaying() &&
+ GetPresentationManager()->GetFMVPlayer()->GetElapsedTime() > MIN_MOVIE_TIME )
+ {
+ GetPresentationManager()->GetFMVPlayer()->Abort();
+ }
+ }
+}
+
+void FMVUserInputHandler::LoadControllerMappings( unsigned int controllerId )
+{
+ // now set controller mappings
+ for( int i = 0; i < NUM_FMV_CONTROL_MAPPINGS; i++ )
+ {
+ this->Map( FMV_CONTROL_MAP[ i ].inputName,
+ FMV_CONTROL_MAP[ i ].inputID,
+ 0,
+ controllerId );
+ }
+}
+
diff --git a/game/code/presentation/fmvplayer/fmvuserinputhandler.h b/game/code/presentation/fmvplayer/fmvuserinputhandler.h
new file mode 100644
index 0000000..aed8b8f
--- /dev/null
+++ b/game/code/presentation/fmvplayer/fmvuserinputhandler.h
@@ -0,0 +1,91 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: FMVUserInputHandler
+//
+// Description: This class feeds the inputs received by the controller
+// system into the GUI system.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/10/20 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef FMVUSERINPUTHANDLER_H
+#define FMVUSERINPUTHANDLER_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <input/mappable.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+namespace FMVInput
+{
+ // Definition of control points for an abstracted player controller
+ //
+ enum FMVInputEnum
+ {
+ UNKNOWN = -1,
+
+ Skip,
+ Start,
+
+ NUM_FMV_INPUTS
+ };
+};
+
+class FMVUserInputHandler : public Mappable
+{
+public:
+
+ FMVUserInputHandler( void );
+ virtual ~FMVUserInputHandler( void );
+
+ // Mappable interface declarations
+ //
+ virtual void OnButton( int controllerId, int buttonId, const IButton* pButton );
+ virtual void OnButtonUp( int controllerId, int buttonId, const IButton* pButton );
+ virtual void OnButtonDown( int controllerId, int buttonId, const IButton* pButton );
+
+ // Mappable interface declarations.
+ // Dispatch a message when controller is disconnected.
+ //
+ virtual void OnControllerDisconnect( int id );
+
+ // Mappable interface declarations.
+ // Dispatch a message when controller is connected.
+ //
+ virtual void OnControllerConnect( int id );
+
+ // Mappable interface declarations
+ //
+ virtual void LoadControllerMappings( unsigned int controllerId );
+
+ inline bool IsEnabled() const { return m_isEnabled; }
+ inline void SetEnabled( bool isEnabled ) { m_isEnabled = isEnabled; }
+
+
+private:
+ // Disallow object copying or assigning until we know we need it
+ //
+ FMVUserInputHandler( const FMVUserInputHandler& original );
+ FMVUserInputHandler& operator=( const FMVUserInputHandler& rhs );
+
+ bool m_isEnabled : 1;
+ bool m_controllerPromptShown : 1;
+ bool m_controllerReconnect;
+
+};
+
+#endif // FMVUSERINPUTHANDLER_H
diff --git a/game/code/presentation/gui/allgui.cpp b/game/code/presentation/gui/allgui.cpp
new file mode 100644
index 0000000..48e0374
--- /dev/null
+++ b/game/code/presentation/gui/allgui.cpp
@@ -0,0 +1,13 @@
+#include <presentation/gui/guientity.cpp>
+#include <presentation/gui/guimanager.cpp>
+#include <presentation/gui/guimenu.cpp>
+#include <presentation/gui/guimenuitem.cpp>
+#include <presentation/gui/guiscreen.cpp>
+#include <presentation/gui/guiscreenmemcardcheck.cpp>
+#include <presentation/gui/guiscreenmemorycard.cpp>
+#include <presentation/gui/guiscreenmessage.cpp>
+#include <presentation/gui/guiscreenprompt.cpp>
+#include <presentation/gui/guisystem.cpp>
+#include <presentation/gui/guitextbible.cpp>
+#include <presentation/gui/guiuserinputhandler.cpp>
+#include <presentation/gui/guiwindow.cpp>
diff --git a/game/code/presentation/gui/backend/allbackend.cpp b/game/code/presentation/gui/backend/allbackend.cpp
new file mode 100644
index 0000000..9ef2fe2
--- /dev/null
+++ b/game/code/presentation/gui/backend/allbackend.cpp
@@ -0,0 +1,4 @@
+#include <presentation/gui/backend/guimanagerbackend.cpp>
+#include <presentation/gui/backend/guiscreenloading.cpp>
+#include <presentation/gui/backend/guiscreenloadingfe.cpp>
+#include <presentation/gui/backend/guiscreendemo.cpp>
diff --git a/game/code/presentation/gui/backend/guiloadingbar.h b/game/code/presentation/gui/backend/guiloadingbar.h
new file mode 100644
index 0000000..7842105
--- /dev/null
+++ b/game/code/presentation/gui/backend/guiloadingbar.h
@@ -0,0 +1,61 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/22 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUILOADINGBAR_H
+#define GUILOADINGBAR_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+//===========================================================================
+// External Constants
+//===========================================================================
+
+const int MB = 1024 * 1024; // bytes
+
+#ifdef RAD_GAMECUBE
+ const float TOTAL_INGAME_MEMORY_USAGE = 9.6f * MB;
+ const float TOTAL_FE_MEMORY_USAGE = 8.8f * MB;
+ const float TOTAL_SUPERSPRINT_MEMORY_USAGE = 3.0f * MB;
+ const float TOTAL_DEMO_MEMORY_USAGE = TOTAL_INGAME_MEMORY_USAGE;
+#endif
+
+#ifdef RAD_PS2
+ const float TOTAL_INGAME_MEMORY_USAGE = 15.2f * MB;
+ const float TOTAL_FE_MEMORY_USAGE = 13.0f * MB;
+ const float TOTAL_SUPERSPRINT_MEMORY_USAGE = 5.6f * MB;
+ const float TOTAL_DEMO_MEMORY_USAGE = TOTAL_INGAME_MEMORY_USAGE;
+#endif
+
+#ifdef RAD_XBOX
+ const float TOTAL_INGAME_MEMORY_USAGE = 16.7f * MB;
+ const float TOTAL_FE_MEMORY_USAGE = 13.1f * MB;
+ const float TOTAL_SUPERSPRINT_MEMORY_USAGE = 5.5f * MB;
+ const float TOTAL_DEMO_MEMORY_USAGE = TOTAL_INGAME_MEMORY_USAGE;
+#endif
+
+#ifdef RAD_WIN32
+ // These settings are for release mode, which has difft memory behaviour than Tune,
+ // only because Tune has debug information.
+ const float TOTAL_INGAME_MEMORY_USAGE = 1.3f * MB;
+ const float TOTAL_FE_MEMORY_USAGE = 1.9f * MB;
+ const float TOTAL_SUPERSPRINT_MEMORY_USAGE = 1.3f * MB;
+ const float TOTAL_DEMO_MEMORY_USAGE = TOTAL_INGAME_MEMORY_USAGE;
+
+ // Our system for windows because the memory tracking doesn't work.
+ const float TOTAL_INGAME_FILES = 56;
+ // no fe.. it doesn't work with this system.
+ const float TOTAL_SUPERSPRINT_FILES = 34;
+ const float LOAD_BLEND_FACTOR = 20;
+ const float LOAD_MIN_SPEED = 0.003f;
+#endif
+
+#endif // GUILOADINGBAR_H
diff --git a/game/code/presentation/gui/backend/guimanagerbackend.cpp b/game/code/presentation/gui/backend/guimanagerbackend.cpp
new file mode 100644
index 0000000..7e65581
--- /dev/null
+++ b/game/code/presentation/gui/backend/guimanagerbackend.cpp
@@ -0,0 +1,296 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManagerBackEnd
+//
+// Description: Implementation of the CGuiManagerBackEnd class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/15 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/backend/guimanagerbackend.h>
+#include <presentation/gui/backend/guiscreenloading.h>
+#include <presentation/gui/backend/guiscreenloadingfe.h>
+#include <presentation/gui/backend/guiscreendemo.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiwindow.h> // for window IDs
+
+#include <contexts/bootupcontext.h>
+
+#include <gameflow/gameflow.h>
+
+#include <memory/srrmemory.h>
+
+#include <raddebug.hpp> // Foundation
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiManagerBackEnd::CGuiManagerBackEnd
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManagerBackEnd::CGuiManagerBackEnd
+(
+ Scrooby::Project* pProject,
+ CGuiEntity* pParent
+)
+: CGuiManager( pProject, pParent ),
+ m_isQuittingDemo( false ),
+ m_isBackendPreRun( false )
+{
+}
+
+
+//===========================================================================
+// CGuiManagerBackEnd::~CGuiManagerBackEnd
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManagerBackEnd::~CGuiManagerBackEnd()
+{
+ for( int i = 0; i < CGuiWindow::NUM_GUI_WINDOW_IDS; i++ )
+ {
+ if( m_windows[ i ] != NULL )
+ {
+ delete m_windows[ i ];
+ m_windows[ i ] = NULL;
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiManagerBackEnd::Populate
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManagerBackEnd::Populate()
+{
+MEMTRACK_PUSH_GROUP( "CGUIManagerBackEnd" );
+ Scrooby::Screen* pScroobyScreen;
+ CGuiScreen* pScreen;
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Loading" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenLoading( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_LOADING, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "LoadingFE" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenLoadingFE( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_LOADING_FE, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Demo" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenDemo( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_DEMO, pScreen );
+ }
+MEMTRACK_POP_GROUP( "CGUIManagerBackEnd" );
+}
+
+void
+CGuiManagerBackEnd::Start( CGuiWindow::eGuiWindowID initialWindow )
+{
+ rAssert( m_state == GUI_FE_UNINITIALIZED );
+ m_state = GUI_FE_SCREEN_RUNNING;
+/*
+ m_nextScreen = initialWindow != CGuiWindow::GUI_WINDOW_ID_UNDEFINED ?
+ initialWindow :
+ CGuiWindow::GUI_SCREEN_ID_LOADING;
+
+ m_state = GUI_FE_CHANGING_SCREENS; // must be set before calling GotoScreen()
+
+ CGuiScreen* nextScreen = static_cast< CGuiScreen* >( this->FindWindowByID( m_nextScreen ) );
+ rAssert( nextScreen != NULL );
+ m_pScroobyProject->GotoScreen( nextScreen->GetScroobyScreen(), this );
+*/
+}
+
+//===========================================================================
+// CGuiManagerBackEnd::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManagerBackEnd::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ switch( message )
+ {
+ case GUI_MSG_PRE_RUN_BACKEND:
+ {
+ this->GotoLoadingScreen( param1 );
+
+ m_isBackendPreRun = true;
+
+ break;
+ }
+
+ case GUI_MSG_RUN_BACKEND:
+ {
+ if( !m_isBackendPreRun )
+ {
+ this->GotoLoadingScreen( param1 );
+ }
+
+ // load dynamic resources for loading screen
+ //
+ CGuiWindow* loadingScreen = this->FindWindowByID( m_nextScreen );
+ rAssert( loadingScreen != NULL );
+ loadingScreen->HandleMessage( GUI_MSG_LOAD_RESOURCES );
+
+ break;
+ }
+
+ case GUI_MSG_QUIT_BACKEND:
+ {
+ m_isBackendPreRun = false;
+
+ // Tell the current screen to shut down.
+ //
+ this->FindWindowByID( m_currentScreen )->HandleMessage( GUI_MSG_WINDOW_EXIT );
+
+ // Go to a blank screen.
+ //
+ m_pScroobyProject->GotoScreen( "Blank", NULL );
+
+ break;
+ }
+
+ case GUI_MSG_RUN_DEMO:
+ {
+ CGuiManager::HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_DEMO,
+ CLEAR_WINDOW_HISTORY | FORCE_WINDOW_RELOAD | FORCE_WINDOW_CHANGE_IMMEDIATE );
+
+ break;
+ }
+
+ case GUI_MSG_QUIT_DEMO:
+ {
+ m_isQuittingDemo = true;
+
+ CGuiManager::HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_LOADING_FE,
+ CLEAR_WINDOW_HISTORY );
+
+ break;
+ }
+
+ case GUI_MSG_WINDOW_FINISHED:
+ {
+ if( GUI_FE_CHANGING_SCREENS == m_state )
+ {
+ if( m_isQuittingDemo )
+ {
+ m_isQuittingDemo = false;
+
+ // switch to frontend context
+ //
+ GetGameFlow()->SetContext( CONTEXT_FRONTEND );
+ }
+
+ m_currentScreen = m_nextScreen;
+ CGuiScreen* nextScreen = static_cast< CGuiScreen* >( this->FindWindowByID( m_nextScreen ) );
+ rAssert( nextScreen != NULL );
+ m_pScroobyProject->GotoScreen( nextScreen->GetScroobyScreen(), this );
+ }
+
+ break;
+ }
+
+ default:
+ {
+ if( m_state != GUI_FE_UNINITIALIZED &&
+ m_currentScreen != CGuiWindow::GUI_WINDOW_ID_UNDEFINED )
+ {
+ // Send the messages down to the current screen.
+ //
+ CGuiWindow* pScreen = this->FindWindowByID( m_currentScreen );
+ rAssert( pScreen );
+
+ pScreen->HandleMessage( message, param1, param2 );
+ }
+ }
+ }
+
+ // propogate message up the hierarchy
+ CGuiManager::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+void
+CGuiManagerBackEnd::GotoLoadingScreen( unsigned int param1 )
+{
+ bool isLoadingGameplay = (param1 == IS_LOADING_GAMEPLAY ||
+ GetGameFlow()->GetNextContext() == CONTEXT_LOADING_GAMEPLAY);
+
+ unsigned int loadingScreenID = isLoadingGameplay ?
+ CGuiWindow::GUI_SCREEN_ID_LOADING :
+ CGuiWindow::GUI_SCREEN_ID_LOADING_FE;
+/*
+#ifdef RAD_E3
+ // always show I&S loading screen for E3 build
+ //
+ loadingScreenID = CGuiWindow::GUI_SCREEN_ID_LOADING_FE;
+#endif
+*/
+ CGuiManager::HandleMessage( GUI_MSG_GOTO_SCREEN,
+ loadingScreenID,
+ CLEAR_WINDOW_HISTORY | FORCE_WINDOW_RELOAD | FORCE_WINDOW_CHANGE_IMMEDIATE );
+}
+
diff --git a/game/code/presentation/gui/backend/guimanagerbackend.h b/game/code/presentation/gui/backend/guimanagerbackend.h
new file mode 100644
index 0000000..f297343
--- /dev/null
+++ b/game/code/presentation/gui/backend/guimanagerbackend.h
@@ -0,0 +1,69 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManagerBackEnd
+//
+// Description: Interface for the CGuiManagerBackEnd class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/15 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUIMANAGERBACKEND_H
+#define GUIMANAGERBACKEND_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guimanager.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+const unsigned int IS_LOADING_GAMEPLAY = 1;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiManagerBackEnd : public CGuiManager
+{
+ public:
+
+ CGuiManagerBackEnd( Scrooby::Project* pProject,
+ CGuiEntity* pParent );
+
+ virtual ~CGuiManagerBackEnd();
+
+ virtual void Populate();
+ virtual void Start( CGuiWindow::eGuiWindowID initialWindow = CGuiWindow::GUI_WINDOW_ID_UNDEFINED );
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ private:
+
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CGuiManagerBackEnd( const CGuiManagerBackEnd& );
+ CGuiManagerBackEnd& operator= ( const CGuiManagerBackEnd& );
+
+ void GotoLoadingScreen( unsigned int param1 );
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ bool m_isQuittingDemo;
+ bool m_isBackendPreRun;
+
+};
+
+#endif // GUIMANAGERBACKEND_H
diff --git a/game/code/presentation/gui/backend/guiscreendemo.cpp b/game/code/presentation/gui/backend/guiscreendemo.cpp
new file mode 100644
index 0000000..770f4ce
--- /dev/null
+++ b/game/code/presentation/gui/backend/guiscreendemo.cpp
@@ -0,0 +1,210 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenDemo
+//
+// Description: Implementation of the CGuiScreenDemo class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/backend/guiscreendemo.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/guisystem.h>
+
+#include <contexts/demo/democontext.h>
+
+#include <raddebug.hpp> // Foundation
+#include <layer.h>
+#include <page.h>
+#include <screen.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenDemo::CGuiScreenDemo
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenDemo::CGuiScreenDemo
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_DEMO ),
+ m_demoText( NULL ),
+ m_elapsedTime( 0 )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage;
+ pPage = m_pScroobyScreen->GetPage( "Demo" );
+ rAssert( pPage );
+
+ Scrooby::Layer* foreground = pPage->GetLayer( "Foreground" );
+ rAssert( foreground != NULL );
+
+ m_demoText = foreground->GetText( "Demo" );
+ rAssert( m_demoText );
+}
+
+
+//===========================================================================
+// CGuiScreenDemo::~CGuiScreenDemo
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenDemo::~CGuiScreenDemo()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenDemo::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenDemo::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ m_elapsedTime += param1;
+
+ const unsigned int BLINK_PERIOD = 250;
+ bool blinked = GuiSFX::Blink( m_demoText,
+ (float)m_elapsedTime,
+ (float)BLINK_PERIOD );
+ if( blinked )
+ {
+ m_elapsedTime %= BLINK_PERIOD;
+ }
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_CONNECT:
+ case GUI_MSG_CONTROLLER_DISCONNECT:
+ case GUI_MSG_CONTROLLER_START:
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ GetDemoContext()->EndDemo();
+// m_pParent->HandleMessage( GUI_MSG_QUIT_DEMO );
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenDemo::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenDemo::InitIntro()
+{
+ rAssert( m_demoText );
+ m_demoText->SetVisible( true );
+
+ m_elapsedTime = 0;
+}
+
+//===========================================================================
+// CGuiScreenDemo::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenDemo::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenDemo::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenDemo::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/backend/guiscreendemo.h b/game/code/presentation/gui/backend/guiscreendemo.h
new file mode 100644
index 0000000..7f28c45
--- /dev/null
+++ b/game/code/presentation/gui/backend/guiscreendemo.h
@@ -0,0 +1,52 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenDemo
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENDEMO_H
+#define GUISCREENDEMO_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenDemo : public CGuiScreen
+{
+public:
+ CGuiScreenDemo( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenDemo();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ Scrooby::Text* m_demoText;
+ unsigned int m_elapsedTime;
+
+};
+
+#endif // GUISCREENDEMO_H
diff --git a/game/code/presentation/gui/backend/guiscreenloading.cpp b/game/code/presentation/gui/backend/guiscreenloading.cpp
new file mode 100644
index 0000000..486a72b
--- /dev/null
+++ b/game/code/presentation/gui/backend/guiscreenloading.cpp
@@ -0,0 +1,624 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLoading
+//
+// Description: Implementation of the CGuiScreenLoading class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <camera/animatedcam.h>
+#include <camera/supercammanager.h>
+#include <presentation/gui/backend/guiscreenloading.h>
+#include <presentation/gui/backend/guiloadingbar.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/guitextbible.h>
+
+#include <gameflow/gameflow.h>
+#include <memory/createheap.h>
+#include <memory/memoryutilities.h>
+#include <mission/gameplaymanager.h>
+#include <mission/missionmanager.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/renderlayer.h>
+#include <sound/soundmanager.h>
+
+#include <raddebug.hpp> // Foundation
+#include <p3d/sprite.hpp>
+#include <p3d/utility.hpp>
+#include <screen.h>
+#include <page.h>
+#include <layer.h>
+#include <group.h>
+#include <sprite.h>
+#include <polygon.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+#define ENABLE_DYNA_LOADED_IMAGES
+//#define LOADING_BAR_EXPLOSION
+
+#ifdef ENABLE_DYNA_LOADED_IMAGES
+ const char* DYNAMIC_RESOURCES_DIR = "art\\frontend\\dynaload\\images\\loading\\";
+ const char* DYNA_LOAD_INVENTORY_SECTION = "LoadingScreenImages";
+#endif
+
+// this is to correct the original reduced scale in the source image
+//
+#ifdef RAD_WIN32
+ const float LOADING_BGD0_CORRECTION_SCALE = 1.05f;
+#else
+ const float LOADING_BGD0_CORRECTION_SCALE = 4.2f;
+#endif
+
+#ifdef RAD_WIN32
+ const float LOADING_BGD1_CORRECTION_SCALE = 1.75f;
+#else
+ const float LOADING_BGD1_CORRECTION_SCALE = 8.4f;
+#endif
+
+#ifdef RAD_PS2
+ const float LOADING_IMAGE_CORRECTION_SCALE = 1.95f;
+#elif defined( RAD_WIN32 )
+ const float LOADING_IMAGE_CORRECTION_SCALE = 0.925f;
+#else
+ const float LOADING_IMAGE_CORRECTION_SCALE = 1.85f;
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenLoading::CGuiScreenLoading
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLoading::CGuiScreenLoading
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_LOADING ),
+ m_elapsedTime( 0 ),
+ m_elapsedSpiralTime( 0 ),
+ m_loadingBarGroup( NULL ),
+ m_currentMemoryUsage( 0.0f ),
+ m_startingMemoryAvailable( 0 ),
+ m_elapsedFireTime( 0 ),
+ m_loadingImage( NULL ),
+ m_isSpirallingDone( false ),
+ m_loadingImageSprite( NULL ),
+ m_explosionLayer( NULL ),
+ m_explosion( NULL ),
+ m_elapsedExplosionTime( 0 )
+{
+ memset( m_loadingImageName, 0, sizeof( m_loadingImageName ) );
+
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage;
+ pPage = m_pScroobyScreen->GetPage( "Loading" );
+ rAssert( pPage );
+
+ Scrooby::Layer* newspaperLayer = pPage->GetLayer( "Newspaper" );
+ rAssert( newspaperLayer != NULL );
+ m_loadingImage = newspaperLayer->GetSprite( "Loading" );
+ m_loadingImage->SetVisible( false );
+
+ if( this->IsWideScreenDisplay() )
+ {
+ newspaperLayer->ResetTransformation();
+ this->ApplyWideScreenCorrectionScale( newspaperLayer );
+ }
+
+ Scrooby::Layer* foregroundLayer = pPage->GetLayer( "Foreground" );
+
+ for( int i = 0; i < NUM_LOADING_OVERLAYS; i++ )
+ {
+ char overlayName[ 32 ];
+ sprintf( overlayName, "Overlay%d", i );
+ m_loadingOverlays[ i ] = foregroundLayer->GetSprite( overlayName );
+ rAssert( m_loadingOverlays[ i ] != NULL );
+ }
+
+ // get loading bar
+ //
+ m_loadingBarGroup = pPage->GetGroup( "LoadingBar" );
+ rAssert( m_loadingBarGroup != NULL );
+ m_loadingBar.m_type = Slider::HORIZONTAL_SLIDER_RIGHT;
+ m_loadingBar.SetScroobyPolygon( m_loadingBarGroup->GetPolygon( "Wick" ),
+ m_loadingBarGroup->GetSprite( "Fire" ) );
+
+ Scrooby::Sprite* loadingBgd = pPage->GetSprite( "Background" );
+ if( loadingBgd != NULL )
+ {
+ loadingBgd->ScaleAboutCenter( LOADING_BGD0_CORRECTION_SCALE );
+ }
+
+/*
+ // XBOX ONLY: show loading text to satisfy Xbox TCR requirement (C01-07)
+ //
+#ifndef RAD_XBOX
+ // otherwise, hide it for all other platforms
+ //
+ Scrooby::Text* loadingText = pPage->GetText( "Loading" );
+ if( loadingText != NULL )
+ {
+ loadingText->SetVisible( false );
+ }
+#endif // !RAD_XBOX
+*/
+
+ // get explosion overlay (from Explosion.pag)
+ //
+ pPage = m_pScroobyScreen->GetPage( "Explosion" );
+ if( pPage != NULL )
+ {
+ m_explosionLayer = pPage->GetLayer( "Explosion" );
+ rAssert( m_explosionLayer != NULL );
+
+ m_explosion = m_explosionLayer->GetPolygon( "Explosion0" );
+ rAssert( m_explosion != NULL );
+ }
+
+#ifdef ENABLE_DYNA_LOADED_IMAGES
+ // add inventory section for dynamically loaded resources
+ //
+ p3d::inventory->AddSection( DYNA_LOAD_INVENTORY_SECTION );
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenLoading::~CGuiScreenLoading
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLoading::~CGuiScreenLoading()
+{
+#ifdef ENABLE_DYNA_LOADED_IMAGES
+ // delete inventory section for dynamically loaded resources
+ //
+ p3d::inventory->DeleteSection( DYNA_LOAD_INVENTORY_SECTION );
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenLoading::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLoading::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ // update elapsed time
+ m_elapsedTime += param1;
+
+ if( !m_isSpirallingDone )
+ {
+ // update loading image
+ //
+ static float SPIRAL_DURATION_TIME = 1800.0f; // in msec
+ static float SPIRAL_ROTATION_TIME = 60.0f; // in msec
+
+ m_elapsedSpiralTime += param1;
+ m_isSpirallingDone = GuiSFX::Spiral( m_loadingImage,
+ (float)m_elapsedSpiralTime,
+ SPIRAL_DURATION_TIME,
+ SPIRAL_ROTATION_TIME,
+ 0.25f, // LOADING_IMAGE_CORRECTION_SCALE * 2.0f,
+ LOADING_IMAGE_CORRECTION_SCALE );
+ }
+
+ static float ROTATION_PERIOD = 20000; // in msec
+ float currentAngle = ((float)m_elapsedTime / ROTATION_PERIOD) * 360.0f;
+
+ for( int i = 0; i < NUM_LOADING_OVERLAYS; i++ )
+ {
+ rAssert( m_loadingOverlays[ i ] );
+ m_loadingOverlays[ i ]->ResetTransformation();
+ m_loadingOverlays[ i ]->ScaleAboutCenter( LOADING_BGD1_CORRECTION_SCALE );
+ m_loadingOverlays[ i ]->RotateAboutCenter( currentAngle );
+
+ currentAngle *= -1.0f; // reverse rotation direction
+ }
+
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_GAMEPLAY )
+ {
+ // update loading bar
+ //
+ rAssert( m_loadingBarGroup != NULL );
+ m_loadingBarGroup->SetVisible( true );
+
+#if defined( RAD_XBOX )
+ float newMemoryUsage = (m_startingMemoryAvailable - Memory::GetTotalMemoryFree()) / TOTAL_INGAME_MEMORY_USAGE;
+#elif defined( RAD_WIN32 )
+ // this sucks but i just want to finish it.
+ float memUsage = float( GetLoadingManager()->GetNumRequestsProcessed() ) / TOTAL_INGAME_FILES;
+ float newMemoryUsage = m_currentMemoryUsage;
+ if( memUsage - newMemoryUsage < LOAD_MIN_SPEED )
+ {
+ newMemoryUsage = memUsage;
+ }
+ else
+ {
+ float delta = (memUsage - newMemoryUsage) / LOAD_BLEND_FACTOR;
+ newMemoryUsage += delta > LOAD_MIN_SPEED ? delta : LOAD_MIN_SPEED;
+ }
+#else
+ float newMemoryUsage = (m_startingMemoryAvailable - GetTotalMemoryFreeInAllHeaps()) / TOTAL_INGAME_MEMORY_USAGE;
+#endif
+ // don't allow loading bar to go backwards
+ //
+ if( newMemoryUsage > m_currentMemoryUsage )
+ {
+ m_currentMemoryUsage = newMemoryUsage;
+
+ if( m_currentMemoryUsage < 1.0f )
+ {
+ m_loadingBar.SetValue( 1.0f - m_currentMemoryUsage );
+ }
+ else
+ {
+#ifdef LOADING_BAR_EXPLOSION
+ if( m_explosionLayer != NULL && m_explosion != NULL )
+ {
+ m_explosionLayer->SetVisible( true );
+
+ m_elapsedExplosionTime += param1;
+/*
+ float explosionScale = EXPLOSION_SCALE_START + m_elapsedExplosionTime / 1000.0f * EXPLOSION_SCALE_RATE;
+ m_explosion->ResetTransformation();
+ m_explosion->ScaleAboutCenter( explosionScale );
+*/
+ const unsigned int EXPLOSION_FLICKER_PERIOD = 50;
+ bool isBlinked = GuiSFX::Blink( m_explosion,
+ (float)m_elapsedExplosionTime,
+ (float)EXPLOSION_FLICKER_PERIOD );
+
+ if( isBlinked )
+ {
+ m_elapsedExplosionTime %= EXPLOSION_FLICKER_PERIOD;
+ }
+/*
+ tColour currentColour;
+ GuiSFX::ModulateColour( &currentColour,
+ (float)m_elapsedExplosionTime,
+ 100.0f,
+ tColour( 255, 255, 0 ),
+ tColour( 255, 0, 0 ) );
+
+ for( int i = 0; i < m_explosion->GetNumOfVertexes(); i++ )
+ {
+ m_explosion->SetVertexColour( i, currentColour );
+ }
+*/
+ }
+#else
+ #ifndef RAD_DEBUG
+ rReleaseWarningMsg( false, "Current memory usage for loading bar exceeds 100%!" );
+ #endif
+#endif // LOADING_BAR_EXPLOSION
+ }
+ }
+
+ // flicker loading bar flame
+ //
+ const unsigned int LOADING_BAR_FIRE_TIME = 50; // in msec
+ m_elapsedFireTime += param1;
+ if( m_elapsedFireTime > LOADING_BAR_FIRE_TIME )
+ {
+ rAssert( m_loadingBar.m_pImage != NULL );
+ m_loadingBar.m_pImage->SetIndex( 1 - m_loadingBar.m_pImage->GetIndex() );
+
+ m_elapsedFireTime %= LOADING_BAR_FIRE_TIME;
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_LOAD_RESOURCES:
+ {
+ this->LoadResources();
+
+#ifdef RAD_XBOX
+ m_startingMemoryAvailable = Memory::GetTotalMemoryFree();
+#else
+ m_startingMemoryAvailable = GetTotalMemoryFreeInAllHeaps();
+#endif
+ rReleasePrintf( "Starting Memory Available = %.2f MB\n", (float)m_startingMemoryAvailable / MB );
+
+#ifdef RAD_WIN32
+ GetLoadingManager()->ResetRequestsProcessed();
+#endif
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+void
+CGuiScreenLoading::LoadResources()
+{
+ int currentLevel = GetGameplayManager()->GetCurrentLevelIndex();
+
+#ifdef ENABLE_DYNA_LOADED_IMAGES
+ char languageDir[ 16 ];
+ languageDir[ 0 ] = '\0';
+
+#ifdef PAL
+ switch( CGuiTextBible::GetCurrentLanguage() )
+ {
+ case Scrooby::XL_FRENCH:
+ {
+ strcpy( languageDir, "french\\" );
+
+ break;
+ }
+ case Scrooby::XL_GERMAN:
+ {
+ strcpy( languageDir, "german\\" );
+
+ break;
+ }
+ case Scrooby::XL_SPANISH:
+ {
+ strcpy( languageDir, "spanish\\" );
+
+ break;
+ }
+ default:
+ {
+ rAssert( CGuiTextBible::GetCurrentLanguage() == Scrooby::XL_ENGLISH );
+
+ break;
+ }
+ }
+#endif // PAL
+
+ sprintf( m_loadingImageName, "loading%d.png", currentLevel + 1 );
+
+ char loadingImageFile[ 256 ];
+ sprintf( loadingImageFile, "%s%sloading%d.p3d",
+ DYNAMIC_RESOURCES_DIR,
+ languageDir,
+ currentLevel + 1 );
+
+ // add request to loading manager to load image file
+ //
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ loadingImageFile,
+ GMA_LEVEL_OTHER,
+ DYNA_LOAD_INVENTORY_SECTION,
+ DYNA_LOAD_INVENTORY_SECTION,
+ this );
+#else
+ // set the current level loading image
+ //
+ rAssert( m_loadingImage );
+ m_loadingImage->SetIndex( currentLevel );
+#endif
+
+ // reset elapsed spiralling time
+ //
+ m_elapsedSpiralTime = 0;
+}
+
+void
+CGuiScreenLoading::OnProcessRequestsComplete( void* pUserData )
+{
+#ifdef ENABLE_DYNA_LOADED_IMAGES
+ // push and select inventory section for searching
+ //
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( DYNA_LOAD_INVENTORY_SECTION );
+ bool currentSectionOnly = p3d::inventory->GetCurrentSectionOnly();
+ p3d::inventory->SetCurrentSectionOnly( true );
+
+ // search for the loading image sprite
+ //
+ rAssert( m_loadingImageSprite == NULL );
+ m_loadingImageSprite = p3d::find<tSprite>( m_loadingImageName );
+ rAssert( m_loadingImageSprite );
+ m_loadingImageSprite->AddRef();
+
+ // pop inventory section and restore states
+ //
+ p3d::inventory->SetCurrentSectionOnly( currentSectionOnly );
+ p3d::inventory->PopSection();
+
+ // set the raw sprite to the Scrooby loading image
+ //
+ rAssert( m_loadingImage );
+ m_loadingImage->SetRawSprite( m_loadingImageSprite, true );
+ m_loadingImage->SetVisible( true );
+ m_loadingImage->ScaleAboutCenter( 0.0f );
+
+ m_isSpirallingDone = false;
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenLoading::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLoading::InitIntro()
+{
+ RenderLayer* levelLayer = GetRenderManager()->mpLayer( RenderEnums::LevelSlot );
+ if( levelLayer != NULL )
+ {
+ levelLayer->Freeze();
+ }
+
+ // reset loading overlay scale
+ //
+ for( int i = 0; i < NUM_LOADING_OVERLAYS; i++ )
+ {
+ rAssert( m_loadingOverlays[ i ] );
+ m_loadingOverlays[ i ]->ResetTransformation();
+ m_loadingOverlays[ i ]->ScaleAboutCenter( LOADING_BGD1_CORRECTION_SCALE );
+ }
+
+ m_loadingBar.SetValue( 1.0f );
+
+ // reset current memory usage
+ //
+ m_currentMemoryUsage = 0.0f;
+
+ // hide loading bar by default
+ //
+ rAssert( m_loadingBarGroup != NULL );
+ m_loadingBarGroup->SetVisible( false );
+
+ // hide explosion overlay
+ //
+ if( m_explosionLayer != NULL && m_explosion != NULL )
+ {
+ m_explosionLayer->SetVisible( false );
+ m_explosion->SetVisible( true );
+
+ m_elapsedExplosionTime = 0;
+ }
+
+ //
+ // Sound ducking, but only for intra-mission stuff. If we're coming
+ // from the FE, we want to play the newspaper spin music.
+ //
+ if( GetGameFlow()->GetCurrentContext() != CONTEXT_FRONTEND )
+ {
+ GetSoundManager()->OnPauseStart();
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenLoading::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLoading::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenLoading::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLoading::InitOutro()
+{
+ RenderLayer* levelLayer = GetRenderManager()->mpLayer( RenderEnums::LevelSlot );
+ if( levelLayer != NULL && levelLayer->IsFrozen() )
+ {
+ levelLayer->Thaw();
+ }
+
+#ifdef ENABLE_DYNA_LOADED_IMAGES
+ p3d::pddi->DrawSync();
+
+ if( m_loadingImageSprite != NULL )
+ {
+ rAssert( m_loadingImage );
+ m_loadingImage->SetRawSprite( NULL, true );
+ m_loadingImage->SetVisible( false );
+
+ // remove and release the loading image sprite
+ //
+ p3d::inventory->RemoveSectionElements( DYNA_LOAD_INVENTORY_SECTION );
+ m_loadingImageSprite->ReleaseVerified();
+ m_loadingImageSprite = NULL;
+ }
+#endif
+
+ rReleasePrintf( "Final memory usage value for loading bar = %.3f\n", m_currentMemoryUsage );
+ m_loadingBar.SetValue( 0.0f );
+
+ m_isSpirallingDone = true;
+
+ // reset elapsed time for any loading animations
+ //
+ m_elapsedTime = 0;
+ m_elapsedFireTime = 0;
+
+ GetSoundManager()->ResetDucking();
+ AnimatedCam::CheckPendingCameraSwitch();
+ GetSuperCamManager()->GetSCC( 0 )->NoTransition();
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/backend/guiscreenloading.h b/game/code/presentation/gui/backend/guiscreenloading.h
new file mode 100644
index 0000000..1961864
--- /dev/null
+++ b/game/code/presentation/gui/backend/guiscreenloading.h
@@ -0,0 +1,84 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLoading
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENLOADING_H
+#define GUISCREENLOADING_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/utility/slider.h>
+
+#include <loading/loadingmanager.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class tSprite;
+namespace Scrooby
+{
+ class Group;
+}
+
+const int NUM_LOADING_OVERLAYS = 2;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenLoading : public CGuiScreen,
+ public LoadingManager::ProcessRequestsCallback
+{
+public:
+ CGuiScreenLoading( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenLoading();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ void LoadResources();
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ unsigned int m_elapsedTime;
+ unsigned int m_elapsedSpiralTime;
+
+ Scrooby::Group* m_loadingBarGroup;
+ Slider m_loadingBar;
+ float m_currentMemoryUsage; // in percent
+ int m_startingMemoryAvailable; // in bytes
+ unsigned int m_elapsedFireTime;
+
+ Scrooby::Sprite* m_loadingImage;
+ Scrooby::Sprite* m_loadingOverlays[ NUM_LOADING_OVERLAYS ];
+ bool m_isSpirallingDone : 1;
+
+ tSprite* m_loadingImageSprite;
+ char m_loadingImageName[ 32 ];
+
+ Scrooby::Layer* m_explosionLayer;
+ Scrooby::Polygon* m_explosion;
+ unsigned int m_elapsedExplosionTime;
+
+};
+
+#endif // GUISCREENLOADING_H
diff --git a/game/code/presentation/gui/backend/guiscreenloadingfe.cpp b/game/code/presentation/gui/backend/guiscreenloadingfe.cpp
new file mode 100644
index 0000000..7435003
--- /dev/null
+++ b/game/code/presentation/gui/backend/guiscreenloadingfe.cpp
@@ -0,0 +1,566 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLoadingFE
+//
+// Description: Implementation of the CGuiScreenLoadingFE class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/backend/guiscreenloadingfe.h>
+#include <presentation/gui/backend/guiloadingbar.h>
+#include <presentation/gui/utility/specialfx.h>
+
+#include <gameflow/gameflow.h>
+#include <memory/createheap.h>
+#include <memory/memoryutilities.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/renderlayer.h>
+#include <sound/soundmanager.h>
+
+#include <p3d/drawable.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/anim/multicontroller.hpp>
+
+#include <raddebug.hpp> // Foundation
+#include <screen.h>
+#include <layer.h>
+#include <page.h>
+#include <group.h>
+#include <text.h>
+#include <sprite.h>
+#include <polygon.h>
+#include <pure3dobject.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+#define ENABLE_DYNA_LOADED_RESOURCES
+//#define LOADING_BAR_FE_EXPLOSION
+
+#ifdef ENABLE_DYNA_LOADED_RESOURCES
+ const char* LOADING_FE_PURE3D_FILE = "art\\frontend\\scrooby\\resource\\pure3d\\loading.p3d";
+ const char* LOADING_FE_INVENTORY = "LoadingFEScreen";
+
+ const char* LOADING_FE_DRAWABLE = "loading_screen";
+ const char* LOADING_FE_CAMERA = "loading_screen_cameraShape";
+ const char* LOADING_FE_MULTICONTROLLER = "loading_screen_MasterController";
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenLoadingFE::CGuiScreenLoadingFE
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLoadingFE::CGuiScreenLoadingFE
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_LOADING_FE ),
+ m_loadingText( NULL ),
+ m_itchyAndScratchy( NULL ),
+ m_elapsedTime( 0 ),
+ m_loadingBarGroup( NULL ),
+ m_currentMemoryUsage( 0.0f ),
+ m_startingMemoryAvailable( 0 ),
+ m_elapsedFireTime( 0 ),
+ m_explosionLayer( NULL ),
+ m_explosion( NULL ),
+ m_elapsedExplosionTime( 0 )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "LoadingFE" );
+ rAssert( pPage != NULL );
+
+ Scrooby::Layer* foreground = pPage->GetLayer( "Foreground" );
+ Scrooby::Layer* background = pPage->GetLayer( "Background" );
+
+ m_loadingText = foreground->GetText( "Loading" );
+ rAssert( m_loadingText != NULL );
+
+ m_itchyAndScratchy = background->GetPure3dObject( "Loading" );
+ if( m_itchyAndScratchy != NULL )
+ {
+ m_itchyAndScratchy->SetDrawable( NULL );
+ m_itchyAndScratchy->SetCamera( NULL );
+ m_itchyAndScratchy->SetMultiController( NULL );
+
+ m_itchyAndScratchy->SetClearDepthBuffer( true );
+ }
+
+ // get loading bar
+ //
+ m_loadingBarGroup = pPage->GetGroup( "LoadingBar" );
+ rAssert( m_loadingBarGroup != NULL );
+ m_loadingBar.m_type = Slider::HORIZONTAL_SLIDER_RIGHT;
+ m_loadingBar.SetScroobyPolygon( m_loadingBarGroup->GetPolygon( "Wick" ),
+ m_loadingBarGroup->GetSprite( "Fire" ) );
+
+ // get explosion overlay (from Explosion.pag)
+ //
+ pPage = m_pScroobyScreen->GetPage( "Explosion" );
+ if( pPage != NULL )
+ {
+ m_explosionLayer = pPage->GetLayer( "Explosion" );
+ rAssert( m_explosionLayer != NULL );
+
+ m_explosion = m_explosionLayer->GetPolygon( "Explosion0" );
+ rAssert( m_explosion != NULL );
+ }
+
+#ifdef ENABLE_DYNA_LOADED_RESOURCES
+ p3d::inventory->AddSection( LOADING_FE_INVENTORY );
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenLoadingFE::~CGuiScreenLoadingFE
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLoadingFE::~CGuiScreenLoadingFE()
+{
+#ifdef ENABLE_DYNA_LOADED_RESOURCES
+ p3d::inventory->DeleteSection( LOADING_FE_INVENTORY );
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenLoadingFE::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLoadingFE::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ // update loading text
+ //
+ const unsigned int LOADING_TEXT_LOOP_TIME = 500; // in msec
+ m_elapsedTime += param1;
+ if( m_elapsedTime > LOADING_TEXT_LOOP_TIME )
+ {
+ rAssert( m_loadingText != NULL );
+ int nextIndex = (m_loadingText->GetIndex() + 1) % m_loadingText->GetNumOfStrings();
+ m_loadingText->SetIndex( nextIndex );
+
+ m_elapsedTime %= LOADING_TEXT_LOOP_TIME;
+ }
+
+ ContextEnum currentContext = GetGameFlow()->GetCurrentContext();
+ if( currentContext == CONTEXT_FRONTEND ||
+ currentContext == CONTEXT_LOADING_DEMO ||
+ currentContext == CONTEXT_LOADING_SUPERSPRINT ||
+ currentContext == CONTEXT_LOADING_GAMEPLAY )
+ {
+ // update loading bar
+ //
+ rAssert( m_loadingBarGroup != NULL );
+ m_loadingBarGroup->SetVisible( true );
+
+ float newMemoryUsage = this->GetCurrentMemoryUsage( currentContext );
+
+ // don't allow loading bar to go backwards
+ //
+ if( newMemoryUsage > m_currentMemoryUsage )
+ {
+ m_currentMemoryUsage = newMemoryUsage;
+
+ if( m_currentMemoryUsage < 1.0f )
+ {
+ m_loadingBar.SetValue( 1.0f - m_currentMemoryUsage );
+ }
+ else
+ {
+#ifdef LOADING_BAR_FE_EXPLOSION
+ if( m_explosionLayer != NULL && m_explosion != NULL )
+ {
+ m_explosionLayer->SetVisible( true );
+
+ m_elapsedExplosionTime += param1;
+/*
+ float explosionScale = EXPLOSION_SCALE_START + m_elapsedExplosionTime / 1000.0f * EXPLOSION_SCALE_RATE;
+ m_explosion->ResetTransformation();
+ m_explosion->ScaleAboutCenter( explosionScale );
+*/
+ const unsigned int EXPLOSION_FLICKER_PERIOD = 50;
+ bool isBlinked = GuiSFX::Blink( m_explosion,
+ (float)m_elapsedExplosionTime,
+ (float)EXPLOSION_FLICKER_PERIOD );
+
+ if( isBlinked )
+ {
+ m_elapsedExplosionTime %= EXPLOSION_FLICKER_PERIOD;
+ }
+/*
+ tColour currentColour;
+ GuiSFX::ModulateColour( &currentColour,
+ (float)m_elapsedExplosionTime,
+ 100.0f,
+ tColour( 255, 255, 0 ),
+ tColour( 255, 0, 0 ) );
+
+ for( int i = 0; i < m_explosion->GetNumOfVertexes(); i++ )
+ {
+ m_explosion->SetVertexColour( i, currentColour );
+ }
+*/
+ }
+#else
+ #ifndef RAD_DEBUG
+ rReleaseWarningMsg( false, "Current memory usage for loading bar exceeds 100%!" );
+ #endif
+#endif // LOADING_BAR_FE_EXPLOSION
+ }
+ }
+
+ // flicker loading bar flame
+ //
+ const unsigned int LOADING_BAR_FIRE_TIME = 50; // in msec
+ m_elapsedFireTime += param1;
+ if( m_elapsedFireTime > LOADING_BAR_FIRE_TIME )
+ {
+ rAssert( m_loadingBar.m_pImage != NULL );
+ m_loadingBar.m_pImage->SetIndex( 1 - m_loadingBar.m_pImage->GetIndex() );
+
+ m_elapsedFireTime %= LOADING_BAR_FIRE_TIME;
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_LOAD_RESOURCES:
+ {
+ this->LoadResources();
+
+#ifdef RAD_XBOX
+ m_startingMemoryAvailable = Memory::GetTotalMemoryFree();
+#else
+ m_startingMemoryAvailable = GetTotalMemoryFreeInAllHeaps();
+#endif
+ rReleasePrintf( "Starting Memory Available = %.2f MB\n", (float)m_startingMemoryAvailable / MB );
+
+#ifdef RAD_WIN32
+ GetLoadingManager()->ResetRequestsProcessed();
+#endif
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+void
+CGuiScreenLoadingFE::LoadResources()
+{
+#ifdef ENABLE_DYNA_LOADED_RESOURCES
+ if( m_itchyAndScratchy != NULL )
+ {
+ m_itchyAndScratchy->SetDrawable( NULL );
+ }
+
+ GameMemoryAllocator heapAllocator = GMA_DEFAULT;
+
+ ContextEnum nextContext = GetGameFlow()->GetNextContext();
+ switch( nextContext )
+ {
+ case CONTEXT_FRONTEND:
+ {
+ heapAllocator = GMA_LEVEL_MOVIE;
+
+ break;
+ }
+ case CONTEXT_LOADING_GAMEPLAY:
+ case CONTEXT_LOADING_DEMO:
+ {
+ heapAllocator = GMA_LEVEL_OTHER;
+
+ break;
+ }
+ case CONTEXT_LOADING_SUPERSPRINT:
+ case CONTEXT_SUPERSPRINT_FE:
+ {
+ heapAllocator = GMA_LEVEL_HUD;
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ rAssert( heapAllocator != GMA_DEFAULT );
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ LOADING_FE_PURE3D_FILE,
+ heapAllocator,
+ LOADING_FE_INVENTORY,
+ LOADING_FE_INVENTORY,
+ this );
+#endif
+}
+
+void
+CGuiScreenLoadingFE::OnProcessRequestsComplete( void* pUserData )
+{
+ if( m_itchyAndScratchy != NULL )
+ {
+ // push and select inventory section for searching
+ //
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( LOADING_FE_INVENTORY );
+ bool currentSectionOnly = p3d::inventory->GetCurrentSectionOnly();
+ p3d::inventory->SetCurrentSectionOnly( true );
+
+ // search for drawable, camera, and multicontroller, and assign
+ // them to the itchy & scratchy pure3d object
+ //
+ tDrawable* drawable = p3d::find<tDrawable>( LOADING_FE_DRAWABLE );
+ m_itchyAndScratchy->SetDrawable( drawable );
+
+ tCamera* camera = p3d::find<tCamera>( LOADING_FE_CAMERA );
+ m_itchyAndScratchy->SetCamera( camera );
+
+ tMultiController* multiController = p3d::find<tMultiController>( LOADING_FE_MULTICONTROLLER );
+ m_itchyAndScratchy->SetMultiController( multiController );
+
+ // pop inventory section and restore states
+ //
+ p3d::inventory->SetCurrentSectionOnly( currentSectionOnly );
+ p3d::inventory->PopSection();
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenLoadingFE::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLoadingFE::InitIntro()
+{
+ RenderLayer* levelLayer = GetRenderManager()->mpLayer( RenderEnums::LevelSlot );
+ if( levelLayer != NULL )
+ {
+ levelLayer->Freeze();
+ }
+
+ m_loadingBar.SetValue( 1.0f );
+
+ // reset current memory usage
+ //
+ m_currentMemoryUsage = 0.0f;
+
+ // hide loading bar by default
+ //
+ rAssert( m_loadingBarGroup != NULL );
+ m_loadingBarGroup->SetVisible( false );
+
+ // hide explosion overlay
+ //
+ if( m_explosionLayer != NULL && m_explosion != NULL )
+ {
+ m_explosionLayer->SetVisible( false );
+ m_explosion->SetVisible( true );
+
+ m_elapsedExplosionTime = 0;
+ }
+}
+
+//===========================================================================
+// CGuiScreenLoadingFE::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLoadingFE::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenLoadingFE::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLoadingFE::InitOutro()
+{
+ RenderLayer* levelLayer = GetRenderManager()->mpLayer( RenderEnums::LevelSlot );
+ if( levelLayer != NULL && levelLayer->IsFrozen() )
+ {
+ levelLayer->Thaw();
+ }
+
+#ifdef ENABLE_DYNA_LOADED_RESOURCES
+ p3d::pddi->DrawSync();
+
+ if( m_itchyAndScratchy != NULL )
+ {
+ m_itchyAndScratchy->SetDrawable( NULL );
+ m_itchyAndScratchy->SetCamera( NULL );
+ m_itchyAndScratchy->SetMultiController( NULL );
+ }
+
+ p3d::inventory->RemoveSectionElements( LOADING_FE_INVENTORY );
+#endif
+
+ rReleasePrintf( "Final memory usage value for loading bar = %.3f\n", m_currentMemoryUsage );
+ m_loadingBar.SetValue( 0.0f );
+
+ // reset loading text animation
+ //
+ m_elapsedTime = 0;
+ rAssert( m_loadingText != NULL );
+ m_loadingText->SetIndex( 0 );
+
+ m_elapsedFireTime = 0;
+
+ GetSoundManager()->ResetDucking();
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+float
+CGuiScreenLoadingFE::GetCurrentMemoryUsage( ContextEnum currentContext ) const
+{
+ float currentMemoryUsage = 0.0f;
+
+#ifdef RAD_XBOX
+ int totalMemoryFree = Memory::GetTotalMemoryFree();
+#else
+ int totalMemoryFree = GetTotalMemoryFreeInAllHeaps();
+#endif
+
+ if( totalMemoryFree > 0 )
+ {
+ switch( currentContext )
+ {
+ case CONTEXT_LOADING_GAMEPLAY:
+ {
+ currentMemoryUsage = (m_startingMemoryAvailable - totalMemoryFree) / TOTAL_INGAME_MEMORY_USAGE;
+
+ break;
+ }
+ case CONTEXT_LOADING_DEMO:
+ {
+ currentMemoryUsage = (m_startingMemoryAvailable - totalMemoryFree) / TOTAL_DEMO_MEMORY_USAGE;
+
+ break;
+ }
+ case CONTEXT_LOADING_SUPERSPRINT:
+ {
+#ifndef RAD_WIN32
+ currentMemoryUsage = (m_startingMemoryAvailable - totalMemoryFree) / TOTAL_SUPERSPRINT_MEMORY_USAGE;
+#else
+ // this sucks but i just want to finish it.
+ float memUsage = float( GetLoadingManager()->GetNumRequestsProcessed() ) / TOTAL_SUPERSPRINT_FILES;
+ currentMemoryUsage = m_currentMemoryUsage;
+ if( memUsage - currentMemoryUsage < LOAD_MIN_SPEED )
+ {
+ currentMemoryUsage = memUsage;
+ }
+ else
+ {
+ float delta = (memUsage - currentMemoryUsage) / LOAD_BLEND_FACTOR;
+ currentMemoryUsage += delta > LOAD_MIN_SPEED ? delta : LOAD_MIN_SPEED;
+ }
+#endif
+ break;
+ }
+ case CONTEXT_FRONTEND:
+ {
+ currentMemoryUsage = (m_startingMemoryAvailable - totalMemoryFree) / TOTAL_FE_MEMORY_USAGE;
+
+ break;
+ }
+ default:
+ {
+ rWarningMsg( false, "Invalid context!" );
+
+ break;
+ }
+ }
+ }
+ else
+ {
+ rWarningMsg( false, "Why is total memory free negative??" );
+ }
+
+ return currentMemoryUsage;
+}
+
diff --git a/game/code/presentation/gui/backend/guiscreenloadingfe.h b/game/code/presentation/gui/backend/guiscreenloadingfe.h
new file mode 100644
index 0000000..3800810
--- /dev/null
+++ b/game/code/presentation/gui/backend/guiscreenloadingfe.h
@@ -0,0 +1,79 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLoadingFE
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENLOADINGFE_H
+#define GUISCREENLOADINGFE_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/utility/slider.h>
+
+#include <contexts/contextenum.h>
+#include <loading/loadingmanager.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Text;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenLoadingFE : public CGuiScreen,
+ public LoadingManager::ProcessRequestsCallback
+{
+public:
+ CGuiScreenLoadingFE( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenLoadingFE();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ void LoadResources();
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ float GetCurrentMemoryUsage( ContextEnum currentContext ) const;
+
+ Scrooby::Text* m_loadingText;
+ Scrooby::Pure3dObject* m_itchyAndScratchy;
+
+ unsigned int m_elapsedTime;
+
+ Scrooby::Group* m_loadingBarGroup;
+ Slider m_loadingBar;
+ float m_currentMemoryUsage; // in percent
+ int m_startingMemoryAvailable; // in bytes
+ unsigned int m_elapsedFireTime;
+
+ Scrooby::Layer* m_explosionLayer;
+ Scrooby::Polygon* m_explosion;
+ unsigned int m_elapsedExplosionTime;
+
+};
+
+#endif // GUISCREENLOADINGFE_H
diff --git a/game/code/presentation/gui/bootup/allbootup.cpp b/game/code/presentation/gui/bootup/allbootup.cpp
new file mode 100644
index 0000000..4fffd7c
--- /dev/null
+++ b/game/code/presentation/gui/bootup/allbootup.cpp
@@ -0,0 +1,5 @@
+#include <presentation/gui/bootup/guimanagerbootup.cpp>
+#include <presentation/gui/bootup/guimanagerlanguage.cpp>
+#include <presentation/gui/bootup/guiscreenbootupload.cpp>
+#include <presentation/gui/bootup/guiscreenlicense.cpp>
+#include <presentation/gui/bootup/guiscreenlanguage.cpp>
diff --git a/game/code/presentation/gui/bootup/guimanagerbootup.cpp b/game/code/presentation/gui/bootup/guimanagerbootup.cpp
new file mode 100644
index 0000000..b42d853
--- /dev/null
+++ b/game/code/presentation/gui/bootup/guimanagerbootup.cpp
@@ -0,0 +1,403 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManagerBootUp
+//
+// Description: Implementation of the CGuiManagerBootUp class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/15 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/bootup/guimanagerbootup.h>
+#include <presentation/gui/bootup/guiscreenbootupload.h>
+#include <presentation/gui/bootup/guiscreenlicense.h>
+#include <presentation/gui/bootup/guiscreenlanguage.h>
+#include <presentation/gui/frontend/guiscreenloadgame.h>
+#include <presentation/gui/guiscreenmemcardcheck.h>
+#include <presentation/gui/guiscreenmessage.h>
+#include <presentation/gui/guiscreenprompt.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiwindow.h> // for window IDs
+
+#include <contexts/bootupcontext.h>
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+
+#ifdef RAD_PS2
+ #include <main/ps2platform.h>
+#endif
+
+#include <raddebug.hpp>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+#ifdef RAD_PS2
+ #ifndef PAL
+ #define ENABLE_PROGRESSIVE_SCAN_PROMPT
+ #endif
+#endif
+
+#ifdef RAD_PS2
+ #define ENABLE_MEMCARD_CHECK_DURING_BOOTUP
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiManagerBootUp::CGuiManagerBootUp
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManagerBootUp::CGuiManagerBootUp
+(
+ Scrooby::Project* pProject,
+ CGuiEntity* pParent
+)
+: CGuiManager( pProject, pParent )
+{
+ if( CommandLineOptions::Get( CLO_SKIP_MEMCHECK ) )
+ {
+ s_memcardCheckState = MEM_CARD_CHECK_COMPLETED;
+ }
+
+#ifdef RAD_DEMO
+ // no memory card checking for demos
+ //
+ s_memcardCheckState = MEM_CARD_CHECK_COMPLETED;
+#endif
+}
+
+
+//===========================================================================
+// CGuiManagerBootUp::~CGuiManagerBootUp
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManagerBootUp::~CGuiManagerBootUp()
+{
+ for( int i = 0; i < CGuiWindow::NUM_GUI_WINDOW_IDS; i++ )
+ {
+ if( m_windows[ i ] != NULL )
+ {
+ delete m_windows[ i ];
+ m_windows[ i ] = NULL;
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiManagerBootUp::Populate
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManagerBootUp::Populate()
+{
+MEMTRACK_PUSH_GROUP( "CGuiManagerBootUp" );
+ Scrooby::Screen* pScroobyScreen;
+ CGuiScreen* pScreen;
+
+#ifdef RAD_PS2
+ pScroobyScreen = m_pScroobyProject->GetScreen( "BootupLoad" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenBootupLoad( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_BOOTUP_LOAD, pScreen );
+ }
+#endif
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "License" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenLicense( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_LICENSE, pScreen );
+ }
+/*
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Language" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenLanguage( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_LANGUAGE, pScreen );
+ }
+*/
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MemCardCheck" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMemCardCheck( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MEMORY_CARD_CHECK, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Blank" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenAutoLoad( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_AUTO_LOAD, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Message" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMessage( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_GENERIC_MESSAGE, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Prompt" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPrompt( pScroobyScreen, this,
+ CGuiWindow::GUI_SCREEN_ID_GENERIC_PROMPT );
+
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_GENERIC_PROMPT, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "ErrorPrompt" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPrompt( pScroobyScreen, this,
+ CGuiWindow::GUI_SCREEN_ID_ERROR_PROMPT );
+
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_ERROR_PROMPT, pScreen );
+ }
+MEMTRACK_POP_GROUP("CGuiManagerBootUp");
+}
+
+void
+CGuiManagerBootUp::Start( CGuiWindow::eGuiWindowID initialWindow )
+{
+ rAssert( m_state == GUI_FE_UNINITIALIZED );
+
+ if( initialWindow != CGuiWindow::GUI_WINDOW_ID_UNDEFINED )
+ {
+ m_bootupScreenQueue.push( initialWindow );
+ }
+
+#ifdef ENABLE_PROGRESSIVE_SCAN_PROMPT
+ // add progressive scan screen to bootup screen queue
+ //
+ if( GetInputManager()->IsProScanButtonsPressed() )
+ {
+ m_bootupScreenQueue.push( CGuiWindow::GUI_SCREEN_ID_GENERIC_PROMPT );
+ }
+#endif
+
+#ifdef ENABLE_MEMCARD_CHECK_DURING_BOOTUP
+ // add memcard checking screen to bootup screen queue
+ //
+ if( s_memcardCheckState == MEM_CARD_CHECK_NOT_DONE )
+ {
+ m_bootupScreenQueue.push( CGuiWindow::GUI_SCREEN_ID_MEMORY_CARD_CHECK );
+ }
+#endif
+
+#ifdef RAD_PS2
+ // for PS2 only, start off w/ a loading screen
+ //
+ m_bootupScreenQueue.push( CGuiWindow::GUI_SCREEN_ID_BOOTUP_LOAD );
+#endif
+
+ // add license screen to bootup screen queue
+ //
+ m_bootupScreenQueue.push( CGuiWindow::GUI_SCREEN_ID_LICENSE );
+
+
+ // alright, letz bring up the first screen of the game!!
+ //
+ m_nextScreen = this->PopNextScreenInQueue();
+ if( m_nextScreen == CGuiWindow::GUI_SCREEN_ID_GENERIC_PROMPT )
+ {
+ // special case for progressive scan prompt
+ //
+ CGuiMenuPrompt::ePromptResponse responses[] =
+ {
+ CGuiMenuPrompt::RESPONSE_YES,
+ CGuiMenuPrompt::RESPONSE_NO
+ };
+
+ CGuiScreenPrompt::Display( PROMPT_DISPLAY_PROGRESSIVE_SCAN, this, 2, responses );
+ CGuiScreenPrompt::EnableDefaultToNo( false );
+ }
+
+ m_state = GUI_FE_CHANGING_SCREENS; // must be set before calling GotoScreen()
+
+ CGuiScreen* nextScreen = static_cast< CGuiScreen* >( this->FindWindowByID( m_nextScreen ) );
+ rAssert( nextScreen != NULL );
+ m_pScroobyProject->GotoScreen( nextScreen->GetScroobyScreen(), this );
+}
+
+//===========================================================================
+// CGuiManagerBootUp::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManagerBootUp::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ switch( message )
+ {
+ case GUI_MSG_QUIT_BOOTUP:
+ {
+ rAssert( GUI_FE_SCREEN_RUNNING == m_state );
+
+ rAssertMsg( m_bootupScreenQueue.empty(),
+ "Why are we quitting when there's still screens in the bootup queue??" );
+
+ m_state = GUI_FE_SHUTTING_DOWN;
+
+#ifdef RAD_WIN32
+ GetBootupContext()->LoadConfig();
+#endif
+
+ // Tell the current screen to shut down.
+ //
+ this->FindWindowByID( m_currentScreen )->HandleMessage( GUI_MSG_WINDOW_EXIT );
+
+ break;
+ }
+ case GUI_MSG_WINDOW_FINISHED:
+ {
+ if( GUI_FE_CHANGING_SCREENS == m_state )
+ {
+ m_currentScreen = m_nextScreen;
+ CGuiScreen* nextScreen = static_cast< CGuiScreen* >( this->FindWindowByID( m_nextScreen ) );
+ rAssert( nextScreen != NULL );
+ m_pScroobyProject->GotoScreen( nextScreen->GetScroobyScreen(), this );
+ }
+ else if( GUI_FE_SHUTTING_DOWN == m_state )
+ {
+ m_state = GUI_FE_TERMINATED;
+
+ // start intro movies
+ //
+ GetBootupContext()->StartMovies();
+ }
+
+ break;
+ }
+ case GUI_MSG_MENU_PROMPT_RESPONSE:
+ {
+ rAssert( param1 == PROMPT_DISPLAY_PROGRESSIVE_SCAN );
+
+ if( param2 == CGuiMenuPrompt::RESPONSE_YES )
+ {
+ // enable progressive scan
+ //
+#ifdef RAD_PS2
+ PS2Platform::GetInstance()->SetProgressiveMode( true );
+#endif
+ }
+
+ // follow-thru
+ //
+ }
+ case GUI_MSG_BOOTUP_LOAD_COMPLETED:
+ case GUI_MSG_LANGUAGE_SELECTION_DONE:
+ case GUI_MSG_MEMCARD_CHECK_COMPLETED:
+ {
+ CGuiWindow::eGuiWindowID nextScreen = this->PopNextScreenInQueue();
+
+ if( nextScreen == CGuiWindow::GUI_SCREEN_ID_BOOTUP_LOAD &&
+ GetGuiSystem()->GetCurrentState() == CGuiSystem::BOOTUP_ACTIVE )
+ {
+ // FE is already loaded, no need to display extra loading screen
+ //
+ nextScreen = this->PopNextScreenInQueue(); // pop off next screen in queue
+ }
+
+ if( nextScreen != CGuiWindow::GUI_WINDOW_ID_UNDEFINED )
+ {
+ CGuiManager::HandleMessage( GUI_MSG_GOTO_SCREEN,
+ nextScreen,
+ CLEAR_WINDOW_HISTORY );
+ }
+ else
+ {
+ this->HandleMessage( GUI_MSG_QUIT_BOOTUP );
+ }
+
+ break;
+ }
+ default:
+ {
+ if( m_state != GUI_FE_UNINITIALIZED &&
+ m_currentScreen != CGuiWindow::GUI_WINDOW_ID_UNDEFINED )
+ {
+ // Send the messages down to the current screen.
+ //
+ CGuiWindow* pScreen = this->FindWindowByID( m_currentScreen );
+ rAssert( pScreen );
+
+ pScreen->HandleMessage( message, param1, param2 );
+ }
+ }
+ }
+
+ // propogate message up the hierarchy
+ CGuiManager::HandleMessage( message, param1, param2 );
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+CGuiWindow::eGuiWindowID
+CGuiManagerBootUp::PopNextScreenInQueue()
+{
+ CGuiWindow::eGuiWindowID nextScreen = CGuiWindow::GUI_WINDOW_ID_UNDEFINED;
+
+ if( !m_bootupScreenQueue.empty() )
+ {
+ nextScreen = m_bootupScreenQueue.front();
+ m_bootupScreenQueue.pop();
+
+ if( nextScreen == CGuiWindow::GUI_SCREEN_ID_MEMORY_CARD_CHECK )
+ {
+ s_memcardCheckState = MEM_CARD_CHECK_IN_PROGRESS;
+ }
+ }
+
+ return nextScreen;
+}
+
diff --git a/game/code/presentation/gui/bootup/guimanagerbootup.h b/game/code/presentation/gui/bootup/guimanagerbootup.h
new file mode 100644
index 0000000..a307206
--- /dev/null
+++ b/game/code/presentation/gui/bootup/guimanagerbootup.h
@@ -0,0 +1,66 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManagerBootUp
+//
+// Description: Interface for the CGuiManagerBootUp class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/15 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUIMANAGERBOOTUP_H
+#define GUIMANAGERBOOTUP_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guimanager.h>
+#include <memory\srrmemory.h> // Needed for my STL allocations to go on the right heap
+
+#include <queue>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiManagerBootUp : public CGuiManager
+{
+public:
+ CGuiManagerBootUp( Scrooby::Project* pProject, CGuiEntity* pParent );
+ virtual ~CGuiManagerBootUp();
+
+ virtual void Populate();
+ virtual void Start( CGuiWindow::eGuiWindowID initialWindow = CGuiWindow::GUI_WINDOW_ID_UNDEFINED );
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+private:
+
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CGuiManagerBootUp( const CGuiManagerBootUp& );
+ CGuiManagerBootUp& operator= ( const CGuiManagerBootUp& );
+
+ CGuiWindow::eGuiWindowID PopNextScreenInQueue();
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+ typedef std::deque< CGuiWindow::eGuiWindowID, s2alloc<CGuiWindow::eGuiWindowID> > WindowIDVector;
+ std::queue<CGuiWindow::eGuiWindowID, WindowIDVector> m_bootupScreenQueue;
+
+};
+
+#endif // GUIMANAGERBOOTUP_H
diff --git a/game/code/presentation/gui/bootup/guimanagerlanguage.cpp b/game/code/presentation/gui/bootup/guimanagerlanguage.cpp
new file mode 100644
index 0000000..b70cb16
--- /dev/null
+++ b/game/code/presentation/gui/bootup/guimanagerlanguage.cpp
@@ -0,0 +1,287 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManagerLanguage
+//
+// Description: Implementation of the CGuiManagerLanguage class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/07/08 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/bootup/guimanagerlanguage.h>
+#include <presentation/gui/bootup/guiscreenlanguage.h>
+#include <presentation/gui/guiwindow.h> // for window IDs
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/language.h>
+
+#include <memory/srrmemory.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/renderlayer.h>
+
+#include <raddebug.hpp>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiManagerLanguage::CGuiManagerLanguage
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManagerLanguage::CGuiManagerLanguage
+(
+ Scrooby::Project* pProject,
+ CGuiEntity* pParent
+)
+: CGuiManager( pProject, pParent )
+{
+}
+
+
+//===========================================================================
+// CGuiManagerLanguage::~CGuiManagerLanguage
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManagerLanguage::~CGuiManagerLanguage()
+{
+ for( int i = 0; i < CGuiWindow::NUM_GUI_WINDOW_IDS; i++ )
+ {
+ if( m_windows[ i ] != NULL )
+ {
+ delete m_windows[ i ];
+ m_windows[ i ] = NULL;
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiManagerLanguage::Populate
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManagerLanguage::Populate()
+{
+MEMTRACK_PUSH_GROUP( "CGuiManagerLanguage" );
+ Scrooby::Screen* pScroobyScreen;
+ CGuiScreen* pScreen;
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Language" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenLanguage( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_LANGUAGE, pScreen );
+ }
+MEMTRACK_POP_GROUP("CGuiManagerLanguage");
+}
+
+void
+CGuiManagerLanguage::Start( CGuiWindow::eGuiWindowID initialWindow )
+{
+ rAssert( m_state == GUI_FE_UNINITIALIZED );
+
+ rAssertMsg( initialWindow == CGuiWindow::GUI_WINDOW_ID_UNDEFINED,
+ "Can't specify another initial window! Not supported by this manager." );
+
+ bool isLanguageSupported = this->CheckLanguage();
+
+ if( !isLanguageSupported || CommandLineOptions::Get( CLO_LANG_PROMPT ) )
+ {
+ m_nextScreen = CGuiWindow::GUI_SCREEN_ID_LANGUAGE;
+
+ m_state = GUI_FE_CHANGING_SCREENS; // must be set before calling GotoScreen()
+
+ CGuiScreen* nextScreen = static_cast< CGuiScreen* >( this->FindWindowByID( m_nextScreen ) );
+ rAssert( nextScreen != NULL );
+ m_pScroobyProject->GotoScreen( nextScreen->GetScroobyScreen(), this );
+
+ // thaw frontend render layer
+ //
+ GetRenderManager()->mpLayer(RenderEnums::GUI)->Thaw();
+ }
+ else
+ {
+ m_state = GUI_FE_TERMINATED;
+
+ GetGuiSystem()->HandleMessage( GUI_MSG_INIT_BOOTUP );
+ }
+}
+
+//===========================================================================
+// CGuiManagerLanguage::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManagerLanguage::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ switch( message )
+ {
+ case GUI_MSG_LANGUAGE_SELECTION_DONE:
+ {
+ rAssert( GUI_FE_SCREEN_RUNNING == m_state );
+
+ m_state = GUI_FE_SHUTTING_DOWN;
+
+ // Tell the current screen to shut down.
+ //
+ this->FindWindowByID( m_currentScreen )->HandleMessage( GUI_MSG_WINDOW_EXIT );
+
+ break;
+ }
+ case GUI_MSG_WINDOW_FINISHED:
+ {
+ if( GUI_FE_CHANGING_SCREENS == m_state )
+ {
+ m_currentScreen = m_nextScreen;
+ CGuiScreen* nextScreen = static_cast< CGuiScreen* >( this->FindWindowByID( m_nextScreen ) );
+ rAssert( nextScreen != NULL );
+ m_pScroobyProject->GotoScreen( nextScreen->GetScroobyScreen(), this );
+ }
+ else if( GUI_FE_SHUTTING_DOWN == m_state )
+ {
+ m_state = GUI_FE_TERMINATED;
+
+ GetGuiSystem()->HandleMessage( GUI_MSG_INIT_BOOTUP );
+ }
+
+ break;
+ }
+ default:
+ {
+ if( m_state != GUI_FE_UNINITIALIZED &&
+ m_currentScreen != CGuiWindow::GUI_WINDOW_ID_UNDEFINED )
+ {
+ // Send the messages down to the current screen.
+ //
+ CGuiWindow* pScreen = this->FindWindowByID( m_currentScreen );
+ rAssert( pScreen );
+
+ pScreen->HandleMessage( message, param1, param2 );
+ }
+ }
+ }
+
+ // propogate message up the hierarchy
+ CGuiManager::HandleMessage( message, param1, param2 );
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+bool
+CGuiManagerLanguage::CheckLanguage()
+{
+ bool isLanguageSupported = true;
+
+ switch( Language::GetHardwareLanguage() )
+ {
+ case Language::ENGLISH:
+ {
+ CGuiTextBible::SetCurrentLanguage( Scrooby::XL_ENGLISH );
+
+ rReleasePrintf( "Localization language automatically set to: ENGLISH\n" );
+
+ break;
+ }
+ case Language::FRENCH:
+ {
+ CGuiTextBible::SetCurrentLanguage( Scrooby::XL_FRENCH );
+
+ rReleasePrintf( "Localization language automatically set to: FRENCH\n" );
+
+ break;
+ }
+ case Language::GERMAN:
+ {
+ CGuiTextBible::SetCurrentLanguage( Scrooby::XL_GERMAN );
+
+ rReleasePrintf( "Localization language automatically set to: GERMAN\n" );
+
+ break;
+ }
+/*
+ case Language::ITALIAN:
+ {
+ CGuiTextBible::SetCurrentLanguage( Scrooby::XL_ITALIAN );
+
+ rReleasePrintf( "Localization language automatically set to: ITALIAN\n" );
+
+ break;
+ }
+*/
+#ifndef RAD_GAMECUBE
+ case Language::SPANISH:
+ {
+ CGuiTextBible::SetCurrentLanguage( Scrooby::XL_SPANISH );
+
+ rReleasePrintf( "Localization language automatically set to: SPANISH\n" );
+
+ break;
+ }
+#endif // !RAD_GAMECUBE
+ default:
+ {
+#ifdef RAD_XBOX
+ // default to English, if unsupported language detected
+ //
+ CGuiTextBible::SetCurrentLanguage( Scrooby::XL_ENGLISH );
+#else
+ isLanguageSupported = false;
+#endif
+ rReleasePrintf( "Unsupported hardware language detected.\n" );
+
+ break;
+ }
+ }
+
+ return isLanguageSupported;
+}
+
diff --git a/game/code/presentation/gui/bootup/guimanagerlanguage.h b/game/code/presentation/gui/bootup/guimanagerlanguage.h
new file mode 100644
index 0000000..d34b227
--- /dev/null
+++ b/game/code/presentation/gui/bootup/guimanagerlanguage.h
@@ -0,0 +1,60 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManagerLanguage
+//
+// Description: Interface for the CGuiManagerLanguage class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/07/08 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUIMANAGERLANGUAGE_H
+#define GUIMANAGERLANGUAGE_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guimanager.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiManagerLanguage : public CGuiManager
+{
+public:
+ CGuiManagerLanguage( Scrooby::Project* pProject, CGuiEntity* pParent );
+ virtual ~CGuiManagerLanguage();
+
+ virtual void Populate();
+ virtual void Start( CGuiWindow::eGuiWindowID initialWindow = CGuiWindow::GUI_WINDOW_ID_UNDEFINED );
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+private:
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CGuiManagerLanguage( const CGuiManagerLanguage& );
+ CGuiManagerLanguage& operator= ( const CGuiManagerLanguage& );
+
+ bool CheckLanguage();
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+};
+
+#endif // GUIMANAGERLANGUAGE_H
diff --git a/game/code/presentation/gui/bootup/guiscreenbootupload.cpp b/game/code/presentation/gui/bootup/guiscreenbootupload.cpp
new file mode 100644
index 0000000..2c4223c
--- /dev/null
+++ b/game/code/presentation/gui/bootup/guiscreenbootupload.cpp
@@ -0,0 +1,200 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenBootupLoad
+//
+// Description: Implementation of the CGuiScreenBootupLoad class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/bootup/guiscreenbootupload.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/guisystem.h>
+
+#include <raddebug.hpp> // Foundation
+
+#include <screen.h>
+#include <page.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenBootupLoad::CGuiScreenBootupLoad
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenBootupLoad::CGuiScreenBootupLoad
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_BOOTUP_LOAD ),
+ m_loadingText( NULL ),
+ m_elapsedIdleTime( 0 )
+{
+ // get loading text
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "LoadingText" );
+ if( pPage != NULL )
+ {
+ m_loadingText = pPage->GetText( "Loading" );
+ rAssert( m_loadingText != NULL );
+ m_loadingText->SetVisible( false ); // hide by default
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenBootupLoad::~CGuiScreenBootupLoad
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenBootupLoad::~CGuiScreenBootupLoad()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenBootupLoad::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenBootupLoad::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ if( message == GUI_MSG_UPDATE )
+ {
+ m_elapsedIdleTime += param1;
+
+ if( m_loadingText != NULL )
+ {
+ // blink loading text if idling here on this screen to satisfy
+ // TRC/TCR requirements
+ //
+ const unsigned int BLINKING_PERIOD = 250;
+ bool isBlinked = GuiSFX::Blink( m_loadingText,
+ static_cast<float>( m_elapsedIdleTime ),
+ static_cast<float>( BLINKING_PERIOD ) );
+ if( isBlinked )
+ {
+ m_elapsedIdleTime %= BLINKING_PERIOD;
+ }
+ }
+
+ if( GetGuiSystem()->GetCurrentState() == CGuiSystem::BOOTUP_ACTIVE )
+ {
+ m_pParent->HandleMessage( GUI_MSG_BOOTUP_LOAD_COMPLETED );
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenBootupLoad::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenBootupLoad::InitIntro()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenBootupLoad::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenBootupLoad::InitRunning()
+{
+ m_elapsedIdleTime = 0;
+}
+
+
+//===========================================================================
+// CGuiScreenBootupLoad::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenBootupLoad::InitOutro()
+{
+ if( m_loadingText != NULL )
+ {
+ // hide loading text
+ //
+ m_loadingText->SetVisible( false );
+ }
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/bootup/guiscreenbootupload.h b/game/code/presentation/gui/bootup/guiscreenbootupload.h
new file mode 100644
index 0000000..72c8b4f
--- /dev/null
+++ b/game/code/presentation/gui/bootup/guiscreenbootupload.h
@@ -0,0 +1,52 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenBootupLoad
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENBOOTUPLOAD_H
+#define GUISCREENBOOTUPLOAD_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenBootupLoad : public CGuiScreen
+{
+public:
+ CGuiScreenBootupLoad( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenBootupLoad();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ Scrooby::Text* m_loadingText;
+ unsigned int m_elapsedIdleTime;
+
+};
+
+#endif // GUISCREENBOOTUPLOAD_H
diff --git a/game/code/presentation/gui/bootup/guiscreenlanguage.cpp b/game/code/presentation/gui/bootup/guiscreenlanguage.cpp
new file mode 100644
index 0000000..9c18074
--- /dev/null
+++ b/game/code/presentation/gui/bootup/guiscreenlanguage.cpp
@@ -0,0 +1,224 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLanguage
+//
+// Description: Implementation of the CGuiScreenLanguage class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/bootup/guiscreenlanguage.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/guiscreenmessage.h>
+
+#include <raddebug.hpp> // Foundation
+
+#include <app.h>
+#include <group.h>
+#include <page.h>
+#include <screen.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenLanguage::CGuiScreenLanguage
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLanguage::CGuiScreenLanguage
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+:
+ CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_LANGUAGE ),
+ m_pMenu( NULL )
+{
+ // retrieve Scrooby drawing elements
+ //
+ rAssert( m_pScroobyScreen != NULL );
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "Language" );
+ rAssert( pPage != NULL );
+
+ m_pMenu = new CGuiMenu( this, NUM_SRR2_LANGUAGES );
+ rAssert( m_pMenu != NULL );
+
+ Scrooby::Group* menu = pPage->GetGroup( "Menu" );
+ rAssert( menu != NULL );
+
+ for( int i = 0; i < NUM_SRR2_LANGUAGES; i++ )
+ {
+ char name[ 32 ];
+ sprintf( name, "Language%d", i );
+ m_pMenu->AddMenuItem( menu->GetText( name ) );
+ }
+
+#ifdef RAD_GAMECUBE
+ m_pMenu->SetMenuItemEnabled( 3, false, true ); // no Spanish on GC PAL
+#endif // RAD_GAMECUBE
+
+ Scrooby::Page* messageBoxPage = m_pScroobyScreen->GetPage( "MessageBox" );
+ if( messageBoxPage != NULL )
+ {
+ this->AutoScaleFrame( messageBoxPage );
+
+ Scrooby::Sprite* messageIcon = messageBoxPage->GetSprite( "ErrorIcon" );
+ if( messageIcon != NULL )
+ {
+ messageIcon->ResetTransformation();
+ messageIcon->ScaleAboutCenter( MESSAGE_ICON_CORRECTION_SCALE );
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenLanguage::~CGuiScreenLanguage
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLanguage::~CGuiScreenLanguage()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenLanguage::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLanguage::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ rAssert( static_cast<int>( param1 ) < NUM_SRR2_LANGUAGES );
+ CGuiTextBible::SetCurrentLanguage( SRR2_LANGUAGE[ param1 ] );
+
+ m_pParent->HandleMessage( GUI_MSG_LANGUAGE_SELECTION_DONE );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenLanguage::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLanguage::InitIntro()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenLanguage::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLanguage::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenLanguage::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLanguage::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/bootup/guiscreenlanguage.h b/game/code/presentation/gui/bootup/guiscreenlanguage.h
new file mode 100644
index 0000000..c12d928
--- /dev/null
+++ b/game/code/presentation/gui/bootup/guiscreenlanguage.h
@@ -0,0 +1,55 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLanguage
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENLANGUAGE_H
+#define GUISCREENLANGUAGE_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenLanguage : public CGuiScreen
+{
+public:
+ CGuiScreenLanguage( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenLanguage();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ CGuiMenu* m_pMenu;
+
+};
+
+#endif // GUISCREENLANGUAGE_H
diff --git a/game/code/presentation/gui/bootup/guiscreenlicense.cpp b/game/code/presentation/gui/bootup/guiscreenlicense.cpp
new file mode 100644
index 0000000..e6262e4
--- /dev/null
+++ b/game/code/presentation/gui/bootup/guiscreenlicense.cpp
@@ -0,0 +1,202 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLicense
+//
+// Description: Implementation of the CGuiScreenLicense class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/bootup/guiscreenlicense.h>
+
+#include <contexts/bootupcontext.h>
+
+#include <screen.h>
+#include <page.h>
+#include <group.h>
+
+#include <p3d/utility.hpp>
+#include <p3d/sprite.hpp>
+#include <raddebug.hpp> // Foundation
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const float LICENSE_SCREEN_FADE_TIME = 500.0f;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenLicense::CGuiScreenLicense
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLicense::CGuiScreenLicense
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_LICENSE )
+{
+ // retrieve Scrooby drawing elements
+ //
+ rAssert( m_pScroobyScreen != NULL );
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "License" );
+ rAssert( pPage != NULL );
+
+ // set platform-specific license screen image
+ //
+#ifdef RAD_GAMECUBE
+ tSprite* pSprite = p3d::find<tSprite>( "licenseG.png" );
+#endif
+#ifdef RAD_PS2
+ tSprite* pSprite = p3d::find<tSprite>( "licenseP.png" );
+#endif
+#ifdef RAD_XBOX
+ tSprite* pSprite = p3d::find<tSprite>( "licenseX.png" );
+#endif
+#ifdef RAD_WIN32
+ tSprite* pSprite = p3d::find<tSprite>( "licensePC.png" );
+#endif
+ rAssert( pSprite != NULL );
+
+ Scrooby::Sprite* licenseImage = pPage->GetSprite( "License" );
+ rAssert( licenseImage != NULL );
+ licenseImage->SetRawSprite( pSprite );
+
+ // set fade time for license screen (in milliseconds)
+ //
+ this->SetFadeTime( LICENSE_SCREEN_FADE_TIME );
+
+#ifdef RAD_GAMECUBE
+ // skip screen fade in (on Gamecube only)
+ //
+ this->SetFadeTime( 0.0f );
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenLicense::~CGuiScreenLicense
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLicense::~CGuiScreenLicense()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenLicense::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLicense::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenLicense::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLicense::InitIntro()
+{
+ // start loading sound files
+ //
+ GetBootupContext()->StartLoadingSound();
+}
+
+
+//===========================================================================
+// CGuiScreenLicense::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLicense::InitRunning()
+{
+#ifdef RAD_GAMECUBE
+ // allow screen fade out (on Gamecube only)
+ this->SetFadeTime( LICENSE_SCREEN_FADE_TIME );
+#endif
+
+ GetBootupContext()->ResetLicenseScreenDisplayTime();
+}
+
+
+//===========================================================================
+// CGuiScreenLicense::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLicense::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/bootup/guiscreenlicense.h b/game/code/presentation/gui/bootup/guiscreenlicense.h
new file mode 100644
index 0000000..219c82c
--- /dev/null
+++ b/game/code/presentation/gui/bootup/guiscreenlicense.h
@@ -0,0 +1,48 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLicense
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENLICENSE_H
+#define GUISCREENLICENSE_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenLicense : public CGuiScreen
+{
+public:
+ CGuiScreenLicense( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenLicense();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+};
+
+#endif // GUISCREENLICENSE_H
diff --git a/game/code/presentation/gui/frontend/allfrontend.cpp b/game/code/presentation/gui/frontend/allfrontend.cpp
new file mode 100644
index 0000000..57979b6
--- /dev/null
+++ b/game/code/presentation/gui/frontend/allfrontend.cpp
@@ -0,0 +1,19 @@
+#include <presentation/gui/frontend/guimanagerfrontend.cpp>
+#include <presentation/gui/frontend/guiscreencardgallery.cpp>
+#include <presentation/gui/frontend/guiscreencontroller.cpp>
+#include <presentation/gui/frontend/guiscreenloadgame.cpp>
+#include <presentation/gui/frontend/guiscreenmainmenu.cpp>
+#include <presentation/gui/frontend/guiscreenmissiongallery.cpp>
+#include <presentation/gui/frontend/guiscreenskingallery.cpp>
+#include <presentation/gui/frontend/guiscreenvehiclegallery.cpp>
+//#include <presentation/gui/frontend/guiscreenmultichoosechar.cpp>
+//#include <presentation/gui/frontend/guiscreenmultisetup.cpp>
+#include <presentation/gui/frontend/guiscreenoptions.cpp>
+#include <presentation/gui/frontend/guiscreenplaymovie.cpp>
+#include <presentation/gui/frontend/guiscreenscrapbook.cpp>
+#include <presentation/gui/frontend/guiscreenscrapbookcontents.cpp>
+#include <presentation/gui/frontend/guiscreenscrapbookstats.cpp>
+#include <presentation/gui/frontend/guiscreensound.cpp>
+#include <presentation/gui/frontend/guiscreensplash.cpp>
+#include <presentation/gui/frontend/guiscreenviewcredits.cpp>
+#include <presentation/gui/frontend/guiscreenviewmovies.cpp>
diff --git a/game/code/presentation/gui/frontend/guimanagerfrontend.cpp b/game/code/presentation/gui/frontend/guimanagerfrontend.cpp
new file mode 100644
index 0000000..1a7e146
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guimanagerfrontend.cpp
@@ -0,0 +1,829 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManagerFrontEnd
+//
+// Description: Implementation of the CGuiManagerFrontEnd class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/21 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guimanagerfrontend.h>
+#include <presentation/gui/guiwindow.h> // for window IDs
+#include <presentation/gui/guisystem.h>
+
+#include <presentation/gui/guiscreenmessage.h>
+#include <presentation/gui/guiscreenprompt.h>
+#include <presentation/gui/guiscreenmemorycard.h>
+#include <presentation/gui/guiscreenmemcardcheck.h>
+
+#include <presentation/gui/frontend/guiscreensplash.h>
+#include <presentation/gui/frontend/guiscreenmainmenu.h>
+#include <presentation/gui/frontend/guiscreenloadgame.h>
+#include <presentation/gui/frontend/guiscreenscrapbook.h>
+#include <presentation/gui/frontend/guiscreenscrapbookcontents.h>
+#include <presentation/gui/frontend/guiscreenscrapbookstats.h>
+#include <presentation/gui/frontend/guiscreencardgallery.h>
+#include <presentation/gui/frontend/guiscreenmissiongallery.h>
+#include <presentation/gui/frontend/guiscreenskingallery.h>
+#include <presentation/gui/frontend/guiscreenvehiclegallery.h>
+#include <presentation/gui/frontend/guiscreenmultisetup.h>
+#include <presentation/gui/frontend/guiscreenmultichoosechar.h>
+#include <presentation/gui/frontend/guiscreenoptions.h>
+#ifdef RAD_WIN32
+#include <presentation/gui/frontend/guiscreencontrollerWin32.h>
+#else
+#include <presentation/gui/frontend/guiscreencontroller.h>
+#endif
+#include <presentation/gui/frontend/guiscreensound.h>
+#include <presentation/gui/frontend/guiscreenviewcredits.h>
+#include <presentation/gui/frontend/guiscreenviewmovies.h>
+#include <presentation/gui/frontend/guiscreenplaymovie.h>
+#include <presentation/gui/frontend/guiscreendisplay.h>
+
+#include <presentation/presentation.h>
+#include <presentation/fmvplayer/fmvplayer.h>
+#include <presentation/fmvplayer/fmvuserinputhandler.h>
+
+#include <data/gamedatamanager.h>
+#include <events/eventmanager.h>
+#include <gameflow/gameflow.h>
+#include <input/inputmanager.h>
+#include <mission/gameplaymanager.h>
+//#include <mission/headtoheadmanager.h>
+#include <mission/missionmanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <supersprint/supersprintmanager.h>
+#include <main/platform.h>
+#include <main/game.h>
+#include <sound/soundmanager.h>
+
+#include <memory/srrmemory.h>
+
+#include <raddebug.hpp> // Foundation
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+#define SHOW_SPLASH_SCREEN
+
+#ifdef RAD_RELEASE
+ #ifndef RAD_E3
+ #define SHOW_INTRO_MOVIE
+ #endif
+#endif
+
+//#define SKIP_INTRO_MOVIE_IF_GAME_LOADED
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiManagerFrontEnd::CGuiManagerFrontEnd
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManagerFrontEnd::CGuiManagerFrontEnd
+(
+ Scrooby::Project* pProject,
+ CGuiEntity* pParent
+)
+: CGuiManager( pProject, pParent ),
+ m_levelNum( 1 ),
+ m_disconnectedController(-1),
+ m_quittingToDemo( false ),
+ m_controllerPromptShown ( false ),
+ m_quittingToMiniGame( false ),
+ m_isControllerReconnected( false ),
+ m_wasFMVInputHandlerEnabled( false )
+{
+#ifdef RAD_WIN32
+ m_quittingGame = false;
+#endif
+}
+
+
+//===========================================================================
+// CGuiManagerFrontEnd::~CGuiManagerFrontEnd
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManagerFrontEnd::~CGuiManagerFrontEnd()
+{
+}
+
+
+
+//===========================================================================
+// CGuiManagerFrontEnd::Populate
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManagerFrontEnd::Populate()
+{
+MEMTRACK_PUSH_GROUP( "CGUIManagerFrontEnd" );
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+
+ Scrooby::Screen* pScroobyScreen;
+ CGuiScreen* pScreen;
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Message3D" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMessage( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_GENERIC_MESSAGE, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Prompt3D" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPrompt( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_GENERIC_PROMPT, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "ErrorPrompt3D" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPrompt( pScroobyScreen, this,
+ CGuiWindow::GUI_SCREEN_ID_ERROR_PROMPT );
+
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_ERROR_PROMPT, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MemoryCard3D" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMemoryCard( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MEMORY_CARD, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Splash" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenSplash( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_SPLASH, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MemCardCheck" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMemCardCheck( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MEMORY_CARD_CHECK, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MainMenu" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenIntroTransition( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_INTRO_TRANSITION, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MainMenu" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMainMenu( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MAIN_MENU, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "LoadGame" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenLoadGame( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_LOAD_GAME, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Blank" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenAutoLoad( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_AUTO_LOAD, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "ScrapBook" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenScrapBook( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_SCRAP_BOOK, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "ScrapBookContents" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenScrapBookContents( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "ScrapBookStats" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenScrapBookStats( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_SCRAP_BOOK_STATS, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "CardGallery" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenCardGallery( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_CARD_GALLERY, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MissionGallery" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMissionGallery( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MISSION_GALLERY, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "SkinGallery" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenSkinGallery( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_SKIN_GALLERY, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "VehicleGallery" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenVehicleGallery( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_VEHICLE_GALLERY, pScreen );
+ }
+/*
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MultiSetup" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMultiSetup( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MULTIPLAYER_SETUP, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MultiChooseCharacter" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMultiChooseChar( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MULTIPLAYER_CHOOSE_CHARACTER, pScreen );
+ }
+*/
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Options" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenOptions( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_OPTIONS, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Controller" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenController( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_CONTROLLER, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Sound" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenSound( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_SOUND, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "ViewCredits" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenViewCredits( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_VIEW_CREDITS, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "ViewMovies" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenViewMovies( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_VIEW_MOVIES, pScreen );
+ }
+
+#ifdef RAD_WIN32
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Display" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenDisplay( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_DISPLAY, pScreen );
+ }
+#endif
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "PlayMovie" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPlayMovie( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_PLAY_MOVIE, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Demo" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPlayMovie( pScroobyScreen, this,
+ CGuiWindow::GUI_SCREEN_ID_PLAY_MOVIE_DEMO );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_PLAY_MOVIE_DEMO, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Blank" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPlayMovie( pScroobyScreen, this,
+ CGuiWindow::GUI_SCREEN_ID_PLAY_MOVIE_INTRO );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_PLAY_MOVIE_INTRO, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Blank" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPlayMovie( pScroobyScreen, this,
+ CGuiWindow::GUI_SCREEN_ID_PLAY_MOVIE_NEW_GAME );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_PLAY_MOVIE_NEW_GAME, pScreen );
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_FE );
+MEMTRACK_POP_GROUP( "CGUIManagerFrontEnd" );
+}
+
+void
+CGuiManagerFrontEnd::Start( CGuiWindow::eGuiWindowID initialWindow )
+{
+ rAssert( GUI_FE_UNINITIALIZED == m_state );
+
+ m_nextScreen = initialWindow != CGuiWindow::GUI_WINDOW_ID_UNDEFINED ?
+ initialWindow :
+ CGuiWindow::GUI_SCREEN_ID_MAIN_MENU;
+
+// On PC never show the splash screen.. it is very console-ish...
+#if defined(SHOW_SPLASH_SCREEN) && !defined(RAD_WIN32)
+ bool skipSplashScreen = CommandLineOptions::Get( CLO_NO_SPLASH );
+#else
+ bool skipSplashScreen = true;
+#endif
+ if( skipSplashScreen &&
+ initialWindow == CGuiWindow::GUI_SCREEN_ID_SPLASH )
+ {
+ if( s_memcardCheckState != MEM_CARD_CHECK_COMPLETED )
+ {
+ m_nextScreen = CGuiWindow::GUI_SCREEN_ID_MEMORY_CARD_CHECK;
+ }
+ else
+ {
+ m_nextScreen = CGuiWindow::GUI_SCREEN_ID_INTRO_TRANSITION;
+ }
+
+ GetGuiSystem()->SetSplashScreenFinished();
+ }
+
+ if( s_memcardCheckState != MEM_CARD_CHECK_COMPLETED )
+ {
+ CGuiScreen* messageScreen = static_cast<CGuiScreen*>( this->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_GENERIC_MESSAGE ) );
+ if( messageScreen != NULL )
+ {
+ messageScreen->SetScroobyScreen( m_pScroobyProject->GetScreen( "Message" ) );
+ }
+
+ CGuiScreen* errorPromptScreen = static_cast<CGuiScreen*>( this->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_ERROR_PROMPT ) );
+ if( errorPromptScreen != NULL )
+ {
+ errorPromptScreen->SetScroobyScreen( m_pScroobyProject->GetScreen( "ErrorPrompt" ) );
+ }
+
+ CGuiScreen* genericPromptScreen = static_cast<CGuiScreen*>( this->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_GENERIC_PROMPT ) );
+ if( genericPromptScreen != NULL )
+ {
+ genericPromptScreen->SetScroobyScreen( m_pScroobyProject->GetScreen( "Prompt" ) );
+ }
+
+ }
+
+ m_state = GUI_FE_CHANGING_SCREENS; // must be set before calling GotoScreen()
+
+ CGuiScreen* nextScreen = static_cast< CGuiScreen* >( this->FindWindowByID( m_nextScreen ) );
+ rAssert( nextScreen != NULL );
+ m_pScroobyProject->GotoScreen( nextScreen->GetScroobyScreen(), this );
+}
+
+void
+CGuiManagerFrontEnd::OnControllerConnected( int controllerID )
+{
+#ifdef RAD_PS2
+ if( m_controllerPromptShown
+ && m_disconnectedController == controllerID )
+#else
+ if( m_controllerPromptShown
+ && GetGuiSystem()->GetPrimaryController() == controllerID )
+#endif
+ {
+ m_isControllerReconnected = true;
+ m_disconnectedController = -1;
+
+ GetGame()->GetPlatform()->ClearControllerError();
+ }
+}
+
+void
+CGuiManagerFrontEnd::OnControllerDisconnected( int controllerID )
+{
+ if( !m_controllerPromptShown )
+ {
+ m_controllerPromptShown = true;
+
+ // disable FMV user input handler
+ //
+ FMVUserInputHandler* userInputHandler = GetPresentationManager()->GetFMVPlayer()->GetUserInputHandler();
+ rAssert( userInputHandler != NULL );
+ m_wasFMVInputHandlerEnabled = userInputHandler->IsEnabled();
+ userInputHandler->SetEnabled( false );
+
+ // this->DisplayMessage( CGuiScreenMessage::MSG_ID_CONTROLLER_DISCONNECTED_GC + PLATFORM_TEXT_INDEX );
+ char str_buffer[256];
+ CGuiScreenMessage::GetControllerDisconnectedMessage(controllerID, str_buffer, 255);
+ GetGame()->GetPlatform()->OnControllerError(str_buffer);
+ }
+}
+
+//===========================================================================
+// CGuiManagerFrontEnd::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManagerFrontEnd::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ switch( message )
+ {
+ case GUI_MSG_WINDOW_FINISHED:
+ {
+ if( GUI_FE_CHANGING_SCREENS == m_state )
+ {
+ m_currentScreen = m_nextScreen;
+
+ CGuiScreen* nextScreen = static_cast< CGuiScreen* >( this->FindWindowByID( m_nextScreen ) );
+ rAssert( nextScreen != NULL );
+ m_pScroobyProject->GotoScreen( nextScreen->GetScroobyScreen(), this );
+ }
+/*
+ else if( GUI_FE_HIDING_POPUP == m_state )
+ {
+ m_state = GUI_FE_SCREEN_RUNNING;
+
+ // Tell the current screen to resume.
+ //
+ this->FindWindowByID( m_currentScreen )->HandleMessage( GUI_MSG_WINDOW_RESUME );
+ }
+*/
+ else if( GUI_FE_SHUTTING_DOWN == m_state )
+ {
+ m_state = GUI_FE_TERMINATED;
+
+ CGuiScreen::Reset3dFEMultiController();
+
+ if( m_quittingToDemo )
+ {
+ if ( CommandLineOptions::Get( CLO_SEQUENTIAL_DEMO ) )
+ {
+ RenderEnums::LevelEnum oldLevel = GetGameplayManager()->GetCurrentLevelIndex();
+
+ unsigned int whichLevel = (oldLevel + 1) % RenderEnums::numLevels;
+ GetGameplayManager()->SetLevelIndex( (RenderEnums::LevelEnum)whichLevel );
+
+ if ( whichLevel == 0 )
+ {
+ RenderEnums::MissionEnum oldMisssion = GetGameplayManager()->GetCurrentMissionForDemo();
+ unsigned int whichMission = (oldMisssion + 1) % RenderEnums::numMissions;
+ GetGameplayManager()->SetMissionIndex( (RenderEnums::MissionEnum)whichMission );
+ }
+ }
+ else
+ {
+
+#ifdef NEALL_DEMOTEST
+ unsigned int whichLevel = RenderEnums::L1; //All seven levels
+ GetGameplayManager()->SetLevelIndex( (RenderEnums::LevelEnum)whichLevel );
+
+ unsigned int whichMission = RenderEnums::M1; //Why not.
+ GetGameplayManager()->SetMissionIndex( (RenderEnums::MissionEnum)whichMission );
+#else
+ unsigned int whichLevel = rand() % RenderEnums::numLevels; //All seven levels
+ GetGameplayManager()->SetLevelIndex( (RenderEnums::LevelEnum)whichLevel );
+
+ unsigned int whichMission = rand() % RenderEnums::numMissions; //Why not.
+ GetGameplayManager()->SetMissionIndex( (RenderEnums::MissionEnum)whichMission );
+#endif
+ }
+
+ GetInputManager()->RegisterControllerID( 0, 0 );
+
+ // let's go to demo mode
+ //
+ GetGameFlow()->SetContext( CONTEXT_LOADING_DEMO );
+ }
+ else if( m_quittingToMiniGame )
+ {
+ // let's go to mini-game mode
+ //
+ GetGameFlow()->SetContext( CONTEXT_SUPERSPRINT_FE );
+ }
+#ifdef RAD_WIN32
+ else if( m_quittingGame )
+ {
+ // let's begin the quit procedure
+ //
+ GetGameFlow()->SetContext( CONTEXT_EXIT );
+ }
+#endif
+ else
+ {
+ CurrentMissionStruct currentMission = GetCharacterSheetManager()->QueryCurrentMission();
+
+ // Set the current level and mission to load
+ //
+ GetGameplayManager()->SetLevelIndex( currentMission.mLevel );
+ GetGameplayManager()->SetMissionIndex( static_cast<RenderEnums::MissionEnum>( currentMission.mMissionNumber ) );
+
+ // let's go to normal gameplay mode
+ //
+ GetGameFlow()->SetContext( CONTEXT_LOADING_GAMEPLAY );
+ }
+ }
+
+ break;
+ }
+
+ case GUI_MSG_SPLASH_SCREEN_DONE:
+ {
+ GetGuiSystem()->SetSplashScreenFinished();
+
+ if( s_memcardCheckState == CGuiManager::MEM_CARD_CHECK_NOT_DONE )
+ {
+ CGuiManager::HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_MEMORY_CARD_CHECK,
+ CLEAR_WINDOW_HISTORY );
+ }
+ else
+ {
+ rAssert( s_memcardCheckState == CGuiManager::MEM_CARD_CHECK_COMPLETED );
+
+ this->StartIntroMovie();
+ }
+
+ break;
+ }
+
+ case GUI_MSG_MEMCARD_CHECK_COMPLETED:
+ {
+ CGuiScreen* messageScreen = static_cast<CGuiScreen*>( this->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_GENERIC_MESSAGE ) );
+ if( messageScreen != NULL )
+ {
+ messageScreen->SetScroobyScreen( m_pScroobyProject->GetScreen( "Message3D" ) );
+ }
+
+ CGuiScreen* errorPromptScreen = static_cast<CGuiScreen*>( this->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_ERROR_PROMPT ) );
+ if( errorPromptScreen != NULL )
+ {
+ errorPromptScreen->SetScroobyScreen( m_pScroobyProject->GetScreen( "ErrorPrompt3D" ) );
+ }
+
+ CGuiScreen* genericPromptScreen = static_cast<CGuiScreen*>( this->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_GENERIC_PROMPT ) );
+ if( genericPromptScreen != NULL )
+ {
+ genericPromptScreen->SetScroobyScreen( m_pScroobyProject->GetScreen( "Prompt3D" ) );
+ }
+
+ this->StartIntroMovie();
+
+ break;
+ }
+
+ case GUI_MSG_QUIT_FRONTEND:
+ {
+ rAssert( GUI_FE_SCREEN_RUNNING == m_state );
+
+ GetEventManager()->TriggerEvent( EVENT_FE_START_GAME_SELECTED );
+ GetSoundManager()->DuckEverythingButMusicBegin();
+
+ // Set appropriate gameplay manager depending on number of players
+ //
+ switch( param1 )
+ {
+ case 0: // single player demo mode
+ {
+ m_quittingToDemo = true;
+
+ // follow-through
+ }
+ case 1: // single player story mode
+ {
+ SetGameplayManager( MissionManager::GetInstance() );
+
+ break;
+ }
+ case 2:
+ {
+ m_quittingToMiniGame = true;
+
+ SetGameplayManager( SuperSprintManager::GetInstance() );
+
+ break;
+ }
+ /*
+ case 2: // two players head-to-head
+ {
+ SetGameplayManager( HeadToHeadManager::GetInstance() );
+
+ break;
+ }
+ */
+ default:
+ {
+ rAssertMsg( 0, "ERROR: *** Invalid Number of Players!\n" );
+
+ break;
+ }
+ }
+
+ m_state = GUI_FE_SHUTTING_DOWN;
+
+ // Tell the current screen to shut down.
+ //
+ this->FindWindowByID( m_currentScreen )->HandleMessage( GUI_MSG_WINDOW_EXIT );
+
+ break;
+ }
+
+ case GUI_MSG_QUIT_GAME:
+ {
+#ifdef RAD_WIN32
+ rAssert( GUI_FE_SCREEN_RUNNING == m_state );
+
+ m_state = GUI_FE_SHUTTING_DOWN;
+
+ m_quittingGame = true;
+
+ // Tell the current screen to shut down.
+ //
+ this->FindWindowByID( m_currentScreen )->HandleMessage( GUI_MSG_WINDOW_EXIT );
+#endif // RAD_WIN32
+
+ break;
+ }
+
+
+ case GUI_MSG_CONTROLLER_DISCONNECT:
+ {
+#ifdef RAD_PS2
+ if( !m_controllerPromptShown )
+ {
+ // show controller disconnected message on PS2 only if:
+ //
+ // - primary controller hasn't been established yet and ANY controller is disconnected; OR
+ // - the controller disconnected is the primary controller
+ //
+ int primaryControllerID = GetGuiSystem()->GetPrimaryController();
+ if( primaryControllerID == -1 || primaryControllerID == static_cast<int>( param1 ) )
+ {
+ m_disconnectedController = static_cast<int>( param1 );
+ }
+ }
+#endif // RAD_PS2
+
+ break;
+ }
+
+ default:
+ {
+ if( message == GUI_MSG_UPDATE && m_isControllerReconnected )
+ {
+ m_isControllerReconnected = false;
+ m_controllerPromptShown = false;
+
+ // re-enable FMV user input handler
+ //
+ FMVUserInputHandler* userInputHandler = GetPresentationManager()->GetFMVPlayer()->GetUserInputHandler();
+ rAssert( userInputHandler != NULL );
+ userInputHandler->SetEnabled( m_wasFMVInputHandlerEnabled );
+ }
+
+ if( m_controllerPromptShown )
+ {
+ if (message==GUI_MSG_CONTROLLER_START) // start trigger reconnection
+ {
+ this->OnControllerConnected( static_cast<int>( param1 ) );
+ }
+ break; // don't pass event if controller error
+ }
+
+ if( m_state != GUI_FE_UNINITIALIZED && m_currentScreen != CGuiWindow::GUI_WINDOW_ID_UNDEFINED )
+ {
+ // Send the messages down to the current screen.
+ //
+ CGuiScreen* pScreen = static_cast<CGuiScreen*>(this->FindWindowByID( m_currentScreen ));
+ rAssert( pScreen );
+ if (pScreen->IsIgnoringControllerInputs() && pScreen->IsControllerMessage(message))
+ ; // don't pass to screen
+ else
+ pScreen->HandleMessage( message, param1, param2 );
+ }
+
+ if( message == GUI_MSG_UPDATE )
+ {
+#ifndef RAD_GAMECUBE
+#ifdef RAD_PS2
+ int controllerID = m_disconnectedController;
+#else
+ int controllerID = GetGuiSystem()->GetPrimaryController();
+#endif
+ if( controllerID >=0 && !GetInputManager()->GetController( controllerID )->IsConnected() )
+ {
+ this->OnControllerDisconnected( controllerID );
+ }
+#endif
+ }
+
+ break;
+ }
+ }
+
+ // propogate message up the hierarchy
+ CGuiManager::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+void
+CGuiManagerFrontEnd::StartIntroMovie()
+{
+#ifdef SKIP_INTRO_MOVIE_IF_GAME_LOADED
+ if( GetGameDataManager()->IsGameLoaded() )
+ {
+ // skip intro fmv and go straight to main menu
+ //
+ CGuiManager::HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_INTRO_TRANSITION,
+ CLEAR_WINDOW_HISTORY );
+ }
+ else
+#endif // SKIP_INTRO_MOVIE_IF_GAME_LOADED
+ {
+#ifdef SHOW_INTRO_MOVIE
+ // start the intro fmv
+ //
+ CGuiScreenPlayMovie* playMovieScreen = static_cast<CGuiScreenPlayMovie*>( this->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_PLAY_MOVIE_INTRO ) );
+ rAssert( playMovieScreen != NULL );
+ playMovieScreen->SetMovieToPlay( MovieNames::INTROFMV, true, false, false );
+
+ CGuiManager::HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_PLAY_MOVIE_INTRO,
+ CLEAR_WINDOW_HISTORY );
+#else
+ CGuiManager::HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_INTRO_TRANSITION,
+ CLEAR_WINDOW_HISTORY );
+#endif // SHOW_INTRO_MOVIE
+ }
+}
+
diff --git a/game/code/presentation/gui/frontend/guimanagerfrontend.h b/game/code/presentation/gui/frontend/guimanagerfrontend.h
new file mode 100644
index 0000000..dbd1460
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guimanagerfrontend.h
@@ -0,0 +1,82 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManagerFrontEnd
+//
+// Description: Interface for the CGuiManagerFrontEnd class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/20 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUIMANAGERFRONTEND_H
+#define GUIMANAGERFRONTEND_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guimanager.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+const unsigned int FOR_THE_FIRST_TIME = 1;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiManagerFrontEnd : public CGuiManager
+{
+public:
+ CGuiManagerFrontEnd( Scrooby::Project* pProject,
+ CGuiEntity* pParent );
+
+ virtual ~CGuiManagerFrontEnd();
+
+ virtual void Populate();
+ virtual void Start( CGuiWindow::eGuiWindowID initialWindow = CGuiWindow::GUI_WINDOW_ID_UNDEFINED );
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+private:
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CGuiManagerFrontEnd( const CGuiManagerFrontEnd& );
+ CGuiManagerFrontEnd& operator= ( const CGuiManagerFrontEnd& );
+
+ void OnControllerDisconnected( int controllerID );
+ void OnControllerConnected( int controllerID );
+
+ void StartIntroMovie();
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ int m_levelNum;
+ int m_disconnectedController;
+ bool m_quittingToDemo : 1;
+ bool m_controllerPromptShown : 1;
+ bool m_quittingToMiniGame : 1;
+ bool m_isControllerReconnected : 1;
+ bool m_wasFMVInputHandlerEnabled : 1;
+
+#ifdef RAD_WIN32
+ bool m_quittingGame : 1;
+#endif
+
+};
+
+#endif // GUIMANAGERFRONTEND_H
diff --git a/game/code/presentation/gui/frontend/guiscreencardgallery.cpp b/game/code/presentation/gui/frontend/guiscreencardgallery.cpp
new file mode 100644
index 0000000..c14a76e
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreencardgallery.cpp
@@ -0,0 +1,767 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenCardGallery
+//
+// Description: Implementation of the CGuiScreenCardGallery class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/21 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreencardgallery.h>
+#include <presentation/gui/frontend/guiscreenscrapbookcontents.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/utility/scrollingtext.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guimenu.h>
+
+#include <cards/card.h>
+#include <cards/cardsdb.h>
+#include <events/eventmanager.h>
+#include <memory/srrmemory.h>
+
+#include <raddebug.hpp> // Foundation
+
+#include <screen.h>
+#include <page.h>
+#include <layer.h>
+#include <group.h>
+#include <sprite.h>
+#include <text.h>
+#include <pure3dobject.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const unsigned int NUM_CARD_ROWS = 2;
+const unsigned int NUM_CARD_COLUMNS = 4;
+const float CARD_TRANSITION_TIME = 250.0f; // in msec
+const float CARD_PROJECTILE_GRAVITY = 0.005f; // in m/ms/ms
+
+#ifdef RAD_WIN32
+const float CARD_THUMBNAIL_SCALE = 0.44f;
+const float CARD_DESCRIPTION_TEXT_SCALE = 0.9f;
+const float CARD_QUESTION_CORRECTION_SCALE = 2.0f / 3.0f;
+const float CARD_GREY_CORRECTION_SCALE = 2.0f / 3.0f;
+#else
+const float CARD_THUMBNAIL_SCALE = 0.44f;
+const float CARD_DESCRIPTION_TEXT_SCALE = 0.9f;
+const float CARD_QUESTION_CORRECTION_SCALE = 2.0f;
+const float CARD_GREY_CORRECTION_SCALE = 2.0f;
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenCardGallery::CGuiScreenCardGallery
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenCardGallery::CGuiScreenCardGallery
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent,
+ eGuiWindowID windowID
+)
+: CGuiScreen( pScreen, pParent, windowID ),
+ m_cardGalleryState( STATE_BROWSING_CARDS ),
+ m_cardScaleSmall( CARD_THUMBNAIL_SCALE ),
+ m_cardScaleLarge( 1.0f ),
+ m_pMenu( NULL ),
+ m_viewCard( NULL ),
+ m_viewCardDistX( 0 ),
+ m_viewCardDistY( 0 ),
+ m_cardBrowseLayer( NULL ),
+ m_cardViewLayer( NULL ),
+ m_cardSFXLayer( NULL ),
+ m_elapsedTime( 0 ),
+ m_currentCard( NULL ),
+ m_cardTitle( NULL ),
+ m_cardEpisode( NULL ),
+ m_cardDescription( NULL ),
+ m_currentQuote( -1 )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "CardGallery" );
+ if( windowID == GUI_SCREEN_ID_VIEW_CARDS )
+ {
+ pPage = m_pScroobyScreen->GetPage( "PauseViewCards" );
+ }
+ rAssert( pPage != NULL );
+
+ char cardName[ 8 ];
+
+ // create a 2D sprite menu
+ //
+ m_pMenu = new CGuiMenu2D( this, NUM_CARDS_PER_LEVEL, 4, GUI_SPRITE_MENU, MENU_SFX_NONE );
+ rAssert( m_pMenu != NULL );
+ m_pMenu->SetGreyOutEnabled( false );
+
+ Scrooby::Group* pGroup = pPage->GetGroup( "CollectedCards" );
+ rAssert( pGroup != NULL );
+
+ // add sprites to menu
+ //
+ for( unsigned int i = 0; i < NUM_CARDS_PER_LEVEL; i++ )
+ {
+ sprintf( cardName, "Card%d", i );
+ m_pMenu->AddMenuItem( pGroup->GetSprite( cardName ) );
+ }
+
+ // add menu cursor
+ //
+ m_pMenu->SetCursor( pGroup->GetSprite( "CardFrame" ) );
+
+ m_cardBrowseLayer = pPage->GetLayer( "Foreground" );
+ rAssert( m_cardBrowseLayer );
+
+ // from ViewCard page
+ //
+ pPage = m_pScroobyScreen->GetPage( "ViewCard" );
+ rAssert( pPage != NULL );
+
+ this->AutoScaleFrame( pPage );
+
+ for( unsigned int j = 0; j < NUM_CARDS_PER_LEVEL; j++ )
+ {
+ sprintf( cardName, "Card%d", j );
+ Scrooby::Sprite* greyCard = pPage->GetSprite( cardName );
+ if( greyCard != NULL )
+ {
+ greyCard->ResetTransformation();
+ greyCard->ScaleAboutCenter( CARD_GREY_CORRECTION_SCALE );
+ }
+ }
+
+ m_cardViewLayer = pPage->GetLayer( "ViewCard" );
+ rAssert( m_cardViewLayer );
+ m_cardViewLayer->SetVisible( false );
+
+ m_cardTitle = pPage->GetText( "CardTitle" );
+ rAssert( m_cardTitle );
+ m_cardTitle->SetTextMode( Scrooby::TEXT_WRAP );
+
+ m_cardEpisode = pPage->GetText( "CardEpisode" );
+ rAssert( m_cardEpisode );
+ m_cardEpisode->SetTextMode( Scrooby::TEXT_WRAP );
+
+ m_cardDescription = pPage->GetText( "CardDescription" );
+ rAssert( m_cardDescription );
+ m_cardDescription->SetTextMode( Scrooby::TEXT_WRAP );
+ m_cardDescription->ScaleAboutPoint( CARD_DESCRIPTION_TEXT_SCALE, 0, 0 );
+
+ for( unsigned int i = 0; i < MAX_NUM_QUOTES; i++ )
+ {
+ char name[ 32 ];
+ sprintf( name, "Quote%d", i );
+
+ m_quoteIcon[ i ] = pPage->GetSprite( name );
+ rAssert( m_quoteIcon[ i ] );
+
+ m_quote[ i ] = new( GMA_LEVEL_FE ) ScrollingText( pPage->GetText( name ) );
+ rAssert( m_quote[ i ] );
+ }
+
+ if( windowID == GUI_SCREEN_ID_CARD_GALLERY )
+ {
+ // from HighResCard page
+ //
+ pPage = m_pScroobyScreen->GetPage( "HighResCard" );
+ }
+ else
+ {
+ // from LowResCard page
+ //
+ pPage = m_pScroobyScreen->GetPage( "LowResCard" );
+ }
+
+ rAssert( pPage != NULL );
+ Scrooby::Layer* cardLayer = pPage->GetLayer( "Card" );
+ rAssert( cardLayer != NULL );
+
+ m_viewCard = cardLayer->GetSprite( "Card" );
+ rAssert( m_viewCard );
+ m_viewCard->SetVisible( false );
+
+ m_cardSFXLayer = pPage->GetLayer( "SFX" );
+ if( m_cardSFXLayer != NULL )
+ {
+ m_cardSFXLayer->SetVisible( false );
+ }
+
+ if( this->IsWideScreenDisplay() )
+ {
+ if( m_cardSFXLayer != NULL )
+ {
+ Scrooby::Pure3dObject* cardSparkle = m_cardSFXLayer->GetPure3dObject( "CardSparkle" );
+ if( cardSparkle != NULL )
+ {
+ cardSparkle->SetWideScreenCorrectionEnabled( true );
+ }
+ }
+
+ cardLayer->ResetTransformation();
+ this->ApplyWideScreenCorrectionScale( cardLayer );
+
+ m_cardViewLayer->ResetTransformation();
+ this->ApplyWideScreenCorrectionScale( m_cardViewLayer );
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenCardGallery::~CGuiScreenCardGallery
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenCardGallery::~CGuiScreenCardGallery()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+
+ for( unsigned int i = 0; i < MAX_NUM_QUOTES; i++ )
+ {
+ if( m_quote[ i ] != NULL )
+ {
+ delete m_quote[ i ];
+ m_quote[ i ] = NULL;
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenCardGallery::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenCardGallery::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_cardGalleryState != STATE_BROWSING_CARDS )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ if( m_cardGalleryState == STATE_GOTO_VIEW_CARD )
+ {
+ this->UpdateCardTransition( param1, true );
+ }
+ else if( m_cardGalleryState == STATE_BACK_VIEW_CARD )
+ {
+ this->UpdateCardTransition( param1, false );
+ }
+ else
+ {
+ rAssert( m_cardGalleryState == STATE_VIEWING_CARD );
+ this->UpdateViewCard( param1 );
+ }
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ if( m_cardGalleryState == STATE_VIEWING_CARD )
+ {
+ if( m_cardSFXLayer != NULL )
+ {
+ m_cardSFXLayer->SetVisible( false );
+ }
+
+ m_cardGalleryState = STATE_BACK_VIEW_CARD;
+ m_elapsedTime = 0;
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_BACK ); // sound effect
+ }
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ return;
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ // pulse cursor alpha
+ //
+ Scrooby::Drawable* cursor = m_pMenu->GetCursor();
+ if( cursor != NULL )
+ {
+ const unsigned int PULSE_PERIOD = 1000;
+
+ float alpha = GuiSFX::Pulse( (float)m_elapsedTime,
+ (float)PULSE_PERIOD,
+ 0.75f,
+ 0.25f,
+ -rmt::PI_BY2 );
+
+ cursor->SetAlpha( alpha );
+
+ m_elapsedTime += param1;
+ m_elapsedTime %= PULSE_PERIOD;
+ }
+
+ break;
+ }
+/*
+ case GUI_MSG_CONTROLLER_LEFT:
+ {
+ int newSelection = m_currentSelection - 1;
+ if( newSelection < 0 )
+ {
+ newSelection += NUM_CARDS_PER_LEVEL;
+ }
+
+ this->MoveCursor( m_currentSelection, newSelection );
+ m_currentSelection = newSelection;
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_RIGHT:
+ {
+ int newSelection = (m_currentSelection + 1) % NUM_CARDS_PER_LEVEL;
+ this->MoveCursor( m_currentSelection, newSelection );
+ m_currentSelection = newSelection;
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_UP:
+ {
+ int newSelection = m_currentSelection - NUM_CARD_COLUMNS;
+ if( newSelection < 0 )
+ {
+ newSelection += NUM_CARDS_PER_LEVEL;
+ }
+
+ this->MoveCursor( m_currentSelection, newSelection );
+ m_currentSelection = newSelection;
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_DOWN:
+ {
+ int newSelection = (m_currentSelection + NUM_CARD_COLUMNS) % NUM_CARDS_PER_LEVEL;
+
+ this->MoveCursor( m_currentSelection, newSelection );
+ m_currentSelection = newSelection;
+
+ break;
+ }
+*/
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ Scrooby::Sprite* currentCardSelection = dynamic_cast<Scrooby::Sprite*>( m_pMenu->GetMenuItem( param1 )->GetItem() );
+ rAssert( currentCardSelection != NULL );
+
+ int cardID = currentCardSelection->GetIndex() - 1;
+ if( cardID != -1 )
+ {
+ this->SetCurrentViewCard( cardID );
+
+ int smallPosX, smallPosY;
+ currentCardSelection->GetOriginPosition( smallPosX, smallPosY );
+
+ int bigPosX, bigPosY;
+ m_viewCard->GetOriginPosition( bigPosX, bigPosY );
+
+ m_viewCardDistX = smallPosX - bigPosX;
+ m_viewCardDistY = smallPosY - bigPosY;
+
+ m_cardVelocity.x = m_viewCardDistX / CARD_TRANSITION_TIME;
+ m_cardVelocity.y = (m_viewCardDistY - 0.5f * CARD_PROJECTILE_GRAVITY * CARD_TRANSITION_TIME * CARD_TRANSITION_TIME) / CARD_TRANSITION_TIME;
+ m_cardVelocity.z = 0.0f;
+
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, false );
+
+ // hide level bar
+ //
+ CGuiScreenScrapBookContents* scrapBookContents = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ if( scrapBookContents != NULL )
+ {
+ scrapBookContents->SetLevelBarVisible( false );
+ }
+
+ m_viewCard->SetVisible( true );
+ m_cardViewLayer->SetVisible( true );
+ m_cardGalleryState = STATE_GOTO_VIEW_CARD;
+ m_elapsedTime = 0;
+
+ this->UpdateCardTransition( 0, true );
+ }
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ //
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenCardGallery::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenCardGallery::InitIntro()
+{
+ CGuiScreenScrapBookContents* scrapBookContents = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( scrapBookContents != NULL );
+ this->UpdateCards( scrapBookContents->GetCurrentLevel() );
+}
+
+
+//===========================================================================
+// CGuiScreenCardGallery::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenCardGallery::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenCardGallery::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenCardGallery::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+/*
+void
+CGuiScreenCardGallery::MoveCursor( unsigned int from,
+ unsigned int to )
+{
+ if( m_cursor != NULL )
+ {
+ rAssert( from < NUM_CARDS_PER_LEVEL && to < NUM_CARDS_PER_LEVEL );
+
+ int posX = 0;
+ int posY = 0;
+ m_collectedCards[ to ]->GetOriginPosition( posX, posY );
+
+ m_cursor->SetPosition( posX, posY );
+ }
+
+ // turn on/off 'accept' button
+ //
+ bool isSelectable = (m_collectedCards[ to ]->GetIndex() != 0);
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, isSelectable );
+}
+*/
+
+void
+CGuiScreenCardGallery::UpdateCards( unsigned int currentLevel )
+{
+ // update the cards
+ //
+ const CardList* collectedCards = GetCardGallery()->GetCollectedCards( currentLevel );
+ rAssert( collectedCards != NULL );
+ rAssert( m_pMenu != NULL );
+
+ // hide accept button by default (unless there is at least one collected card to view)
+ //
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, false );
+
+ for( unsigned int i = 0; i < NUM_CARDS_PER_LEVEL; i++ )
+ {
+ Scrooby::Sprite* cardSelection = dynamic_cast<Scrooby::Sprite*>( m_pMenu->GetMenuItem( i )->GetItem() );
+ rAssert( cardSelection != NULL );
+ cardSelection->ResetTransformation();
+
+ bool isCardCollected = ( collectedCards->m_cards[ i ] != NULL );
+ m_pMenu->SetMenuItemEnabled( i, isCardCollected );
+
+ if( isCardCollected )
+ {
+ unsigned int cardID = collectedCards->m_cards[ i ]->GetID();
+ rAssert( cardID < static_cast<unsigned int>( cardSelection->GetNumOfImages() ) );
+ cardSelection->SetIndex( cardID + 1 );
+ cardSelection->ScaleAboutCenter( m_cardScaleSmall );
+
+// m_pMenu->Reset();
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+ }
+ else
+ {
+ cardSelection->SetIndex( 0 );
+ cardSelection->ScaleAboutCenter( CARD_QUESTION_CORRECTION_SCALE );
+ }
+ }
+}
+
+void
+CGuiScreenCardGallery::UpdateCardTransition( unsigned int elapsedTime, bool toViewCard )
+{
+ rAssert( m_viewCard );
+ m_viewCard->ResetTransformation();
+
+ if( m_elapsedTime < CARD_TRANSITION_TIME )
+ {
+ float t = toViewCard ? (float)m_elapsedTime / CARD_TRANSITION_TIME :
+ 1.0f - (float)m_elapsedTime / CARD_TRANSITION_TIME;
+
+ // scale card
+ //
+ float scale = t * (m_cardScaleLarge - m_cardScaleSmall) + m_cardScaleSmall;
+ m_viewCard->ScaleAboutCenter( scale );
+
+/*
+ // translate card
+ //
+ int translateX = static_cast<int>( (1.0f - t) * m_viewCardDistX );
+ int translateY = static_cast<int>( (1.0f - t) * m_viewCardDistY );
+ m_viewCard->Translate( translateX, translateY );
+*/
+
+ GuiSFX::Projectile( m_viewCard,
+ (float)m_elapsedTime,
+ CARD_TRANSITION_TIME,
+ m_cardVelocity,
+ toViewCard,
+ CARD_PROJECTILE_GRAVITY );
+
+ // fade in/out foreground layers (non-linearly)
+ //
+ m_cardBrowseLayer->SetAlpha( (1.0f - t) * (1.0f - t) );
+ m_cardViewLayer->SetAlpha( t * t );
+
+ m_elapsedTime += elapsedTime;
+ }
+ else
+ {
+ // transition completed
+ //
+ if( toViewCard )
+ {
+ m_viewCard->ScaleAboutCenter( m_cardScaleLarge );
+
+ m_cardGalleryState = STATE_VIEWING_CARD;
+
+ m_cardBrowseLayer->SetAlpha( 0.0f );
+ m_cardViewLayer->SetAlpha( 1.0f );
+
+ if( m_cardSFXLayer != NULL )
+ {
+ m_cardSFXLayer->SetVisible( true );
+ }
+ }
+ else
+ {
+ m_viewCard->ScaleAboutCenter( m_cardScaleSmall );
+
+ m_cardViewLayer->SetVisible( false );
+ m_viewCard->SetVisible( false );
+ m_cardGalleryState = STATE_BROWSING_CARDS;
+
+ m_cardBrowseLayer->SetAlpha( 1.0f );
+ m_cardViewLayer->SetAlpha( 0.0f );
+
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+
+ // show level bar
+ //
+ CGuiScreenScrapBookContents* scrapBookContents = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ if( scrapBookContents != NULL )
+ {
+ scrapBookContents->SetLevelBarVisible( true );
+ }
+ }
+ }
+}
+
+void
+CGuiScreenCardGallery::UpdateViewCard( unsigned int elapsedTime )
+{
+ rAssert( m_currentCard );
+
+ if( m_currentQuote != -1 )
+ {
+ // update current scrolling quote
+ //
+ rAssert( m_quote[ m_currentQuote ] );
+ m_quote[ m_currentQuote ]->Update( elapsedTime );
+
+ if( m_quote[ m_currentQuote ]->GetCurrentState() == ScrollingText::STATE_IDLE )
+ {
+ // turn off current quote icon
+ //
+ m_quoteIcon[ m_currentQuote ]->SetVisible( false );
+
+ // start next quote (and wrap to first one)
+ //
+ m_currentQuote = (m_currentQuote + 1) % m_currentCard->GetNumQuotes();
+ m_quote[ m_currentQuote ]->Start();
+
+ // turn on new quote icon
+ //
+ m_quoteIcon[ m_currentQuote ]->SetVisible( true );
+ }
+ }
+}
+
+void
+CGuiScreenCardGallery::SetCurrentViewCard( unsigned int cardID )
+{
+ // set current card reference
+ //
+ m_currentCard = GetCardGallery()->GetCardsDB()->GetCardByID( cardID );
+ rAssert( m_currentCard );
+
+ // set card image
+ //
+ rAssert( m_viewCard );
+ rAssert( cardID < static_cast<unsigned int>( m_viewCard->GetNumOfImages() ) );
+ m_viewCard->SetIndex( cardID );
+
+ // set card title
+ //
+ rAssert( m_cardTitle );
+ rAssert( cardID < static_cast<unsigned int>( m_cardTitle->GetNumOfStrings() ) );
+ m_cardTitle->SetIndex( cardID );
+
+ // set card episode
+ //
+ rAssert( m_cardEpisode );
+ rAssert( cardID < static_cast<unsigned int>( m_cardEpisode->GetNumOfStrings() ) );
+ m_cardEpisode->SetIndex( cardID );
+
+ // set card description
+ //
+ rAssert( m_cardDescription );
+ rAssert( cardID < static_cast<unsigned int>( m_cardDescription->GetNumOfStrings() ) );
+ m_cardDescription->SetIndex( cardID );
+
+ // stop current quote
+ //
+ if( m_currentQuote != -1 )
+ {
+ m_quote[ m_currentQuote ]->Stop();
+ }
+
+ // set card quotes
+ //
+ for( unsigned int i = 0; i < MAX_NUM_QUOTES; i++ )
+ {
+ m_quoteIcon[ i ]->SetVisible( false );
+
+ eQuoteID quoteID = m_currentCard->GetQuoteID( i );
+ if( quoteID != EMPTY_QUOTE )
+ {
+ m_quoteIcon[ i ]->SetIndex( static_cast<int>( quoteID ) );
+ }
+
+ m_quote[ i ]->SetTextIndex( cardID );
+ }
+
+ if( m_currentCard->GetNumQuotes() > 0 )
+ {
+ // start scrolling first quote
+ //
+ m_currentQuote = 0;
+ m_quote[ m_currentQuote ]->Start();
+ m_quoteIcon[ m_currentQuote ]->SetVisible( true );
+ }
+ else
+ {
+ m_currentQuote = -1;
+ }
+}
+
diff --git a/game/code/presentation/gui/frontend/guiscreencardgallery.h b/game/code/presentation/gui/frontend/guiscreencardgallery.h
new file mode 100644
index 0000000..d3284fe
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreencardgallery.h
@@ -0,0 +1,104 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenCardGallery
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/21 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENCARDGALLERY_H
+#define GUISCREENCARDGALLERY_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <cards/cardgallery.h>
+#include <cards/card.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CGuiMenu2D;
+class ScrollingText;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenCardGallery : public CGuiScreen
+{
+public:
+ CGuiScreenCardGallery( Scrooby::Screen* pScreen, CGuiEntity* pParent,
+ eGuiWindowID windowID = GUI_SCREEN_ID_CARD_GALLERY );
+ virtual ~CGuiScreenCardGallery();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ void UpdateCards( unsigned int currentLevel );
+
+ enum eScreenState
+ {
+ STATE_BROWSING_CARDS,
+ STATE_GOTO_VIEW_CARD,
+ STATE_VIEWING_CARD,
+ STATE_BACK_VIEW_CARD,
+
+ NUM_SCREEN_STATES
+ };
+
+ eScreenState m_cardGalleryState;
+
+ float m_cardScaleSmall;
+ float m_cardScaleLarge;
+
+ CGuiMenu2D* m_pMenu;
+
+private:
+// void MoveCursor( unsigned int from, unsigned int to );
+ void UpdateCardTransition( unsigned int elapsedTime, bool toViewCard );
+ void UpdateViewCard( unsigned int elapsedTime );
+ void SetCurrentViewCard( unsigned int cardID );
+
+ Scrooby::Sprite* m_viewCard;
+ int m_viewCardDistX;
+ int m_viewCardDistY;
+ rmt::Vector m_cardVelocity;
+
+ Scrooby::Layer* m_cardBrowseLayer;
+ Scrooby::Layer* m_cardViewLayer;
+ Scrooby::Layer* m_cardSFXLayer;
+
+ unsigned int m_elapsedTime;
+
+ // for viewing card
+ //
+ Card* m_currentCard;
+
+ Scrooby::Text* m_cardTitle;
+ Scrooby::Text* m_cardEpisode;
+ Scrooby::Text* m_cardDescription;
+
+ Scrooby::Sprite* m_quoteIcon[ MAX_NUM_QUOTES ];
+ ScrollingText* m_quote[ MAX_NUM_QUOTES ];
+ int m_currentQuote;
+
+};
+
+#endif // GUISCREENCARDGALLERY_H
diff --git a/game/code/presentation/gui/frontend/guiscreencontroller.cpp b/game/code/presentation/gui/frontend/guiscreencontroller.cpp
new file mode 100644
index 0000000..3ffd076
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreencontroller.cpp
@@ -0,0 +1,401 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenController
+//
+// Description: Implementation of the CGuiScreenController class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreencontroller.h>
+#include <presentation/gui/guimenu.h>
+
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+#include <raddebug.hpp> // Foundation
+#include <group.h>
+#include <layer.h>
+#include <page.h>
+#include <screen.h>
+#include <text.h>
+
+#include <strings/unicodestring.h>
+#include <p3d/unicode.hpp>
+#include <string.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+enum eMenuItem
+{
+ MENU_ITEM_CONFIGURATION,
+ MENU_ITEM_DISPLAY,
+ MENU_ITEM_VIBRATION,
+
+ NUM_MENU_ITEMS
+};
+
+static const char* MENU_ITEMS[] =
+{
+ "Configuration",
+ "Display",
+ "Vibration"
+};
+
+const int NUM_DISPLAY_MODES = 2;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenController::CGuiScreenController
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenController::CGuiScreenController
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_CONTROLLER ),
+ m_pMenu( NULL ),
+ m_numLabels( 0 ),
+ m_currentConfig( 0 ),
+ m_currentDisplay( 0 ),
+ m_currentControllerID( 0 )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage;
+ pPage = m_pScroobyScreen->GetPage( "Controller" );
+ rAssert( pPage );
+
+ // create a menu
+ //
+ m_pMenu = new(GMA_LEVEL_FE) CGuiMenu( this, NUM_MENU_ITEMS );
+ rAssert( m_pMenu != NULL );
+
+ // add menu items
+ //
+ Scrooby::Group* menu = pPage->GetGroup( "Menu" );
+ for( int j = 0; j < NUM_MENU_ITEMS; j++ )
+ {
+ char objectName[ 32 ];
+ sprintf( objectName, "%s_Value", MENU_ITEMS[ j ] );
+ m_pMenu->AddMenuItem( menu->GetText( MENU_ITEMS[ j ] ),
+ menu->GetText( objectName ),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ SELECTION_ENABLED | VALUES_WRAPPED | TEXT_OUTLINE_ENABLED );
+ }
+
+#ifdef RAD_GAMECUBE
+ // change "Vibration" text to "Rumble"
+ //
+ Scrooby::Text* vibrationText = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( MENU_ITEM_VIBRATION )->GetItem() );
+ rAssert( vibrationText );
+ vibrationText->SetIndex( 1 );
+#endif // RAD_GAMECUBE
+
+ // disable configuration until there are multiple configurations from
+ // which to choose
+ //
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_CONFIGURATION, false, true );
+
+ // get the platform-specific Controller page
+ //
+#ifdef RAD_GAMECUBE
+ pPage = m_pScroobyScreen->GetPage( "ControllerGC" );
+ rAssert( pPage );
+#endif // RAD_GAMECUBE
+
+#ifdef RAD_PS2
+ pPage = m_pScroobyScreen->GetPage( "ControllerPS2" );
+ rAssert( pPage );
+#endif // RAD_PS2
+
+#ifdef RAD_XBOX
+ pPage = m_pScroobyScreen->GetPage( "ControllerXBOX" );
+ rAssert( pPage );
+#endif // RAD_XBOX
+
+#ifdef RAD_WIN32
+ pPage = m_pScroobyScreen->GetPage( "ControllerPC" );
+ rAssert( pPage );
+#endif // RAD_WIN32
+
+ // and make it visible
+ //
+ pPage->GetLayerByIndex( 0 )->SetVisible( true );
+
+ // get text labels
+ //
+ Scrooby::Group* textLabels = pPage->GetGroup( "TextLabels" );
+ rAssert( textLabels != NULL );
+ for( int i = 0; i < MAX_NUM_LABELS; i++ )
+ {
+ char objectName[ 32 ];
+ sprintf( objectName, "Label%02d", i );
+
+ Scrooby::Text* textLabel = textLabels->GetText( objectName );
+ if( textLabel != NULL )
+ {
+#ifdef PAL
+ textLabel->StretchBoundingBox( 30, 0 );
+#endif // PAL
+
+ textLabel->SetTextMode( Scrooby::TEXT_WRAP );
+ m_labels[ i ] = textLabel;
+ m_numLabels++;
+
+ // TC: this is now done in the text bible
+ //
+/*
+ // convert text label strings to all caps
+ //
+ for( int j = 0; j < NUM_DISPLAY_MODES; j++ )
+ {
+ rAssert( j < textLabel->GetNumOfStrings() );
+
+ p3d::UnicodeStrUpr( textLabel->GetStringBuffer( j ) );
+ }
+*/
+ }
+ else
+ {
+ // assume no more labels, which means they all must have
+ // been numbered sequentially without any gaps
+ //
+ break;
+ }
+ }
+
+#ifdef PAL
+ // scale down controller image and text labels a bit so that
+ // all localized versions will fit on screen
+ //
+ #ifdef RAD_PS2
+ const float SCALE_FACTOR = 0.95f;
+ #else
+ const float SCALE_FACTOR = 0.9f;
+ #endif
+
+ textLabels->ResetTransformation();
+ textLabels->ScaleAboutCenter( SCALE_FACTOR );
+
+ pPage = m_pScroobyScreen->GetPage( "ControllerImage" );
+ if( pPage != NULL )
+ {
+ Scrooby::Sprite* controllerImage = pPage->GetSprite( "Controller" );
+ if( controllerImage != NULL )
+ {
+ controllerImage->ResetTransformation();
+ controllerImage->ScaleAboutCenter( SCALE_FACTOR );
+ }
+ }
+#endif // PAL
+}
+
+
+//===========================================================================
+// CGuiScreenController::~CGuiScreenController
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenController::~CGuiScreenController()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenController::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenController::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_LEFT:
+ case GUI_MSG_CONTROLLER_RIGHT:
+ {
+ if( m_pMenu->GetSelection() == MENU_ITEM_VIBRATION )
+ {
+ m_currentControllerID = static_cast<int>( param1 );
+ }
+
+ break;
+ }
+
+ case GUI_MSG_MENU_SELECTION_VALUE_CHANGED:
+ {
+ if( param1 == MENU_ITEM_CONFIGURATION )
+ {
+ m_currentConfig = param2;
+
+ this->UpdateLabels();
+ }
+ else if( param1 == MENU_ITEM_DISPLAY )
+ {
+ m_currentDisplay = param2;
+
+ this->UpdateLabels();
+ }
+ else if( param1 == MENU_ITEM_VIBRATION )
+ {
+ if( param2 == 0 ) // vibration turned ON
+ {
+ // send vibration pulse to controller
+ //
+ GetInputManager()->TriggerRumblePulse( m_currentControllerID );
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ this->StartTransitionAnimation( 560, 590 );
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenController::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenController::InitIntro()
+{
+ // update controller settings on menu
+ //
+ bool vibrationOn = GetInputManager()->IsRumbleEnabled();
+ m_pMenu->SetSelectionValue( MENU_ITEM_VIBRATION,
+ vibrationOn ? 0 : 1 );
+}
+
+
+//===========================================================================
+// CGuiScreenController::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenController::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenController::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenController::InitOutro()
+{
+ bool vibrationOn = (m_pMenu->GetSelectionValue( MENU_ITEM_VIBRATION ) == 0);
+ GetInputManager()->SetRumbleEnabled( vibrationOn );
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenController::UpdateLabels()
+{
+ int labelIndex = m_currentConfig * NUM_DISPLAY_MODES + m_currentDisplay;
+ for( int i = 0; i < m_numLabels; i++ )
+ {
+ rAssert( m_labels[ i ] );
+ m_labels[ i ]->SetIndex( labelIndex );
+ }
+}
+
diff --git a/game/code/presentation/gui/frontend/guiscreencontroller.h b/game/code/presentation/gui/frontend/guiscreencontroller.h
new file mode 100644
index 0000000..9c56c19
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreencontroller.h
@@ -0,0 +1,65 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenController
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/30 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENCONTROLLER_H
+#define GUISCREENCONTROLLER_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenController : public CGuiScreen
+{
+public:
+ CGuiScreenController( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenController();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void UpdateLabels();
+
+ static const int MAX_NUM_LABELS = 32;
+
+ CGuiMenu* m_pMenu;
+
+ Scrooby::Text* m_labels[ MAX_NUM_LABELS ];
+ int m_numLabels;
+
+ int m_currentConfig;
+ int m_currentDisplay;
+ int m_currentControllerID;
+
+};
+
+#endif // GUISCREENCONTROLLER_H
diff --git a/game/code/presentation/gui/frontend/guiscreencontrollerWin32.cpp b/game/code/presentation/gui/frontend/guiscreencontrollerWin32.cpp
new file mode 100644
index 0000000..eb17100
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreencontrollerWin32.cpp
@@ -0,0 +1,1200 @@
+/******************************************************************************
+ File: GuiScreenControllerWin32.cpp
+ Desc: Defines the CGuiScreenController class.
+ Author: Neil Haran
+ Date: July 31, 2003
+ History:
+*****************************************************************************/
+#include <presentation/gui/frontend/guiscreencontrollerWin32.h>
+#include <presentation/gui/guiscreenprompt.h>
+#include <presentation/gui/guimanager.h>
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+#include <data/config/gameconfigmanager.h>
+#include <presentation/tutorialmanager.h>
+#include <events/eventmanager.h>
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+
+#include <group.h>
+#include <layer.h>
+#include <page.h>
+#include <screen.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const char* szMainControllerPage = "ControllerPC";
+const char* szCCPage = "CharacterControls";
+const char* szVCPage = "VehicleControls";
+const char* szGSPage = "GameSettings";
+const char* szNotAssigned = "---";
+
+const int NUM_DISPLAY_MODES = 2;
+const int MAX_INPUTNAME_LENGTH = 10;
+
+const tColour DEFAULT_SELECTED_ITEM_COLOUR( 255, 255, 0 );
+const tColour DEFAULT_INPUTMAPPED_ITEM_COLOUR( 255, 0, 0 ); // the same as in guiscreenmemorycard.cpp
+
+const float SLIDER_ICON_SCALE = 0.5f;
+
+/******************************************************************************
+ Construction/Destruction
+*****************************************************************************/
+CGuiScreenController::CGuiScreenController( Scrooby::Screen* pScreen, CGuiEntity* pParent )
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_CONTROLLER ),
+ m_pMenu( NULL ),
+ m_currentPage( MENU_PAGE_MAIN ),
+ m_currentControllerID( 0 ),
+ m_bMapInput( false ),
+ m_currentTextLabel(NULL),
+ m_bDisableBack(false),
+ m_numControllerGroups(0)
+{
+ memset( m_menuGroupStartIndex, 0 , sizeof( m_menuGroupStartIndex ) );
+ m_pMenu = new(GMA_LEVEL_FE) CGuiMenu( this, NUM_MENU_ITEMS );
+ rAssert( m_pMenu != NULL );
+
+ // Add main menu items
+ SetGroups( m_pMenuLabels, NUM_MAINMENU_LABELS, szMainControllerPage, "Menu", "Label", ALL_ATTRIBUTES_ON );
+ m_numControllerGroups = 1; // The first page is the MENU_PAGE_MAIN with an index of 0.
+
+ // Add map items
+ SetGroups( m_pCCLabels[MAP_PRIMARY], NUM_CHARACTERCONTROL_LABELS, szCCPage, "Map1", "Map1Label" );
+ SetGroups( m_pCCLabels[MAP_SECONDARY], NUM_CHARACTERCONTROL_LABELS, szCCPage, "Map2", "Map2Label" );
+ SetGroups( m_pVCLabels[MAP_PRIMARY], NUM_VEHICLECONTROL_LABELS, szVCPage, "Map1", "Map1Label" );
+ SetGroups( m_pVCLabels[MAP_SECONDARY], NUM_VEHICLECONTROL_LABELS, szVCPage, "Map2", "Map2Label" );
+
+ // Add game setting items
+ SetGroups( m_pMSLabels, NUM_GAMESETTING_LABELS, szGSPage, "Menu",
+ "Label", (SELECTION_ENABLED | VALUES_WRAPPED | TEXT_OUTLINE_ENABLED) );
+
+ //Turn off the controller page that shows up for the consoles.
+ SetPageVisiblility( "Controller", false );
+ SetPageVisiblility( szMainControllerPage, true );
+ SetPageVisiblility( szCCPage, false );
+ SetPageVisiblility( szVCPage, false );
+ SetPageVisiblility( szGSPage, false );
+
+ // Size the slider icons in the game settings page.
+ Scrooby::Group* group = pScreen->GetPage( szGSPage )->GetGroup( "Menu" );
+ if( group != NULL )
+ {
+ group->GetSprite( "MouseXSliderIcon" )->ScaleAboutCenter( SLIDER_ICON_SCALE );
+ group->GetSprite( "MouseYSliderIcon" )->ScaleAboutCenter( SLIDER_ICON_SCALE );
+ group->GetSprite( "WheelSliderIcon" )->ScaleAboutCenter( SLIDER_ICON_SCALE );
+ }
+}
+
+CGuiScreenController::~CGuiScreenController()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+//===========================================================================
+// CGuiScreenController::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenController::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ static bool bRelayMessage = true;
+
+ if( message == GUI_MSG_MENU_PROMPT_RESPONSE )
+ {
+ switch( param1 )
+ {
+ case PROMPT_CONFIRM_RESTOREALLDEFAULTS:
+ {
+ switch( param2 )
+ {
+ case (CGuiMenuPrompt::RESPONSE_YES):
+ {
+ GetInputManager()->GetController(m_currentControllerID)->LoadDefaults();
+ this->InitIntro();
+ this->ReloadScreen();
+
+ break;
+ }
+
+ case (CGuiMenuPrompt::RESPONSE_NO):
+ {
+ this->ReloadScreen();
+
+ break;
+ }
+
+ default:
+ {
+ rAssertMsg( 0, "WARNING: *** Invalid prompt response!\n" );
+
+ break;
+ }
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_MENU_SELECTION_VALUE_CHANGED:
+ {
+ rAssert( m_pMenu );
+ GuiMenuItem* currentItem = m_pMenu->GetMenuItem( param1 );
+ rAssert( currentItem );
+
+ switch( m_currentPage )
+ {
+ case MENU_PAGE_GAMESETTINGS:
+ {
+ // convert to Gamesetting specific items.
+ int menuItem = param1-NUM_CHARACTERCONTROL_LABELS-NUM_VEHICLECONTROL_LABELS;
+ UserController* pController = GetInputManager()->GetController( m_currentControllerID );
+
+ switch( menuItem )
+ {
+ case GS_MOUSELOOK:
+ {
+ pController->SetMouseLook( m_pMenu->GetSelectionValue( param1 ) == PROMPT_YES );
+
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( SuperCam::ON_FOOT_CAM, SuperCamCentral::CUT, 0 );
+ GetSuperCamManager()->GetSCC( 0 )->Update( 0 );
+ }
+ break;
+ case GS_MOUSESENSITIVITY_X:
+ {
+ pController->SetMouseSensitivityX( m_pMenu->GetMenuItem( param1 )->m_slider.m_value );
+ }
+ break;
+ case GS_MOUSESENSITIVITY_Y:
+ {
+ pController->SetMouseSensitivityY( m_pMenu->GetMenuItem( param1 )->m_slider.m_value );
+ }
+ break;
+ case GS_INVERTMOUSEX:
+ {
+ pController->SetInvertMouseX( m_pMenu->GetSelectionValue( param1 ) == PROMPT_YES );
+ }
+ break;
+ case GS_INVERTMOUSEY:
+ {
+ pController->SetInvertMouseY( m_pMenu->GetSelectionValue( param1 ) == PROMPT_YES );
+ }
+ break;
+ case GS_FORCEFEEDBACK:
+ {
+ pController->SetForceFeedback( m_pMenu->GetSelectionValue( param1 ) == PROMPT_YES );
+ }
+ break;
+ case GS_WHEELSENSITIVITY:
+ {
+ pController->SetWheelSensitivityX( m_pMenu->GetMenuItem( param1 )->m_slider.m_value );
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_UP:
+ {
+ // Basically a wrap around system when you reach the top.
+ // Just jumps to the last element.
+ switch(m_currentPage)
+ {
+ case MENU_PAGE_MAIN:
+ if( m_pMenu->GetSelection() == m_menuGroupStartIndex[m_currentPage] )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+(NUM_MAINMENU_LABELS-1));
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ break;
+ case MENU_PAGE_CHARACTERCONTROLS:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On the first element on keyboard Map 1
+ if( menuItem == m_menuGroupStartIndex[m_currentPage] )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+(NUM_CHARACTERCONTROL_LABELS-1));
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ else if( menuItem == m_menuGroupStartIndex[m_currentPage]+NUM_CHARACTERCONTROL_LABELS )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+(NUM_CHARACTERCONTROL_LABELS*2)-1);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+
+ break;
+ case MENU_PAGE_VEHICLECONTROLS:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On the first element on gamepad Map 1
+ if( menuItem == m_menuGroupStartIndex[m_currentPage] )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+(NUM_VEHICLECONTROL_LABELS-1));
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ else if( menuItem == m_menuGroupStartIndex[m_currentPage]+NUM_VEHICLECONTROL_LABELS )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+(NUM_VEHICLECONTROL_LABELS*2)-1);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+ case MENU_PAGE_GAMESETTINGS:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On the first element on gamepad Map 1
+ if( menuItem == m_menuGroupStartIndex[m_currentPage] )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+(NUM_GAMESETTING_LABELS-1));
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case GUI_MSG_CONTROLLER_DOWN:
+ {
+ // Basically a wrap around system when you reach the bottom.
+ // Just jumps to the first element.
+ switch(m_currentPage)
+ {
+ case MENU_PAGE_MAIN:
+ if( m_pMenu->GetSelection() == m_menuGroupStartIndex[m_currentPage]+(NUM_MAINMENU_LABELS-1) )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ break;
+ case MENU_PAGE_CHARACTERCONTROLS:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On the first element on keyboard Map 1
+ if( menuItem == m_menuGroupStartIndex[m_currentPage]+(NUM_CHARACTERCONTROL_LABELS-1) )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ else if( menuItem == m_menuGroupStartIndex[m_currentPage]+(NUM_CHARACTERCONTROL_LABELS*2)-1 )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+NUM_CHARACTERCONTROL_LABELS);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+
+ break;
+ case MENU_PAGE_VEHICLECONTROLS:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On the first element on gamepad Map 1
+ if( menuItem == m_menuGroupStartIndex[m_currentPage]+(NUM_VEHICLECONTROL_LABELS-1) )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ else if( menuItem == m_menuGroupStartIndex[m_currentPage]+(NUM_VEHICLECONTROL_LABELS*2)-1 )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+NUM_VEHICLECONTROL_LABELS);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+
+ break;
+
+ case MENU_PAGE_GAMESETTINGS:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On the first element on gamepad Map 1
+ if( menuItem == m_menuGroupStartIndex[m_currentPage]+NUM_GAMESETTING_LABELS-1 )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case GUI_MSG_CONTROLLER_LEFT:
+ case GUI_MSG_CONTROLLER_RIGHT:
+ {
+ // Basically a wrap around system when you go left or right.
+ switch(m_currentPage)
+ {
+ case MENU_PAGE_CHARACTERCONTROLS:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On Map 1
+ if( menuItem < m_menuGroupStartIndex[m_currentPage] + NUM_CHARACTERCONTROL_LABELS )
+ {
+ m_pMenu->Reset(menuItem+NUM_CHARACTERCONTROL_LABELS);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ else // On Map 2
+ {
+ m_pMenu->Reset(menuItem-NUM_CHARACTERCONTROL_LABELS);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+
+ break;
+ case MENU_PAGE_VEHICLECONTROLS:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On Map 1
+ if( menuItem < m_menuGroupStartIndex[m_currentPage] + NUM_VEHICLECONTROL_LABELS )
+ {
+ m_pMenu->Reset(menuItem+NUM_VEHICLECONTROL_LABELS);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ else // On Map 2
+ {
+ m_pMenu->Reset(menuItem-NUM_VEHICLECONTROL_LABELS);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ // guiscreencontrollerwin32 is a circumvention of normal ENTER/EXIT
+ // WINDOW front-end scheme. Since we are disabling the mouse
+ // each time a selection has been made, we need to re-enable
+ // it for this situation, when you transit from some screen to
+ // one of these screens, so the mouse continues to function normally.
+ //
+ GetInputManager()->GetFEMouse()->SetSelectable( true );
+
+ switch( m_currentPage )
+ {
+ case MENU_PAGE_MAIN:
+ {
+ switch( param1 )
+ {
+ case MENU_ITEM_CHARACTERCONTROLS:
+ {
+ GetInputManager()->GetFEMouse()->SetClickStopMode( true );
+ SetPageVisiblility( szMainControllerPage, false );
+ SetPageVisiblility( szCCPage, true );
+ m_currentPage = MENU_PAGE_CHARACTERCONTROLS;
+ break;
+ }
+ case MENU_ITEM_VEHICLECONTROLS:
+ {
+ GetInputManager()->GetFEMouse()->SetClickStopMode( true );
+ SetPageVisiblility( szMainControllerPage, false );
+ SetPageVisiblility( szVCPage, true );
+ m_currentPage = MENU_PAGE_VEHICLECONTROLS;
+ break;
+ }
+ case MENU_ITEM_GAMESETTINGS:
+ {
+ SetPageVisiblility( szMainControllerPage, false );
+ SetPageVisiblility( szGSPage, true );
+ m_currentPage = MENU_PAGE_GAMESETTINGS;
+ break;
+ }
+ case MENU_ITEM_RESTOREALLDEFAULTS:
+ {
+ rAssert( m_guiManager );
+ GetInputManager()->GetFEMouse()->SetSelectable( true );
+ m_guiManager->DisplayPrompt( PROMPT_CONFIRM_RESTOREALLDEFAULTS, this );
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ } break;
+ case MENU_PAGE_CHARACTERCONTROLS:
+ {
+ // if we're on Map 1 or Map 2
+ if( param1 >= (unsigned int)m_menuGroupStartIndex[m_currentPage] )
+ {
+ int menuItem = param1;
+ // On Map 1
+ if( menuItem < m_menuGroupStartIndex[m_currentPage] + NUM_CHARACTERCONTROL_LABELS )
+ {
+ // We know what map it is so just convert this into a value between 0 - NUM_CHARACTERCONTROL_LABELS-1
+ menuItem = menuItem - m_menuGroupStartIndex[m_currentPage];
+ RemapButton( MAP_PRIMARY, menuItem );
+ break;
+ }
+ else // On Map 2
+ {
+ menuItem = menuItem - m_menuGroupStartIndex[m_currentPage] - NUM_CHARACTERCONTROL_LABELS;
+ RemapButton( MAP_SECONDARY, menuItem );
+ break;
+ }
+ }
+ } break;
+ case MENU_PAGE_VEHICLECONTROLS:
+ {
+ if( param1 >= (unsigned int)m_menuGroupStartIndex[m_currentPage] )
+ {
+ int menuItem = param1;
+ // On Map 1
+ if( menuItem < m_menuGroupStartIndex[m_currentPage] + NUM_VEHICLECONTROL_LABELS )
+ {
+ // We know what map it is so just convert this into a value between 0 - NUM_CHARACTERCONTROL_LABELS-1
+ menuItem = menuItem - m_menuGroupStartIndex[m_currentPage];
+ RemapButton( MAP_PRIMARY, menuItem );
+ break;
+ }
+ else // On Map 2
+ {
+ menuItem = menuItem - m_menuGroupStartIndex[m_currentPage] - NUM_VEHICLECONTROL_LABELS;
+ RemapButton( MAP_SECONDARY, menuItem );
+ break;
+ }
+ }
+ } break;
+ default: break;
+ }
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ // mapping an input, so any key to trigger this event should be ignored.
+ if( !m_bDisableBack )
+ {
+ switch( m_currentPage )
+ {
+ case MENU_PAGE_CHARACTERCONTROLS:
+ GetInputManager()->GetFEMouse()->SetClickStopMode( false );
+ SetPageVisiblility( szCCPage, false );
+ SetPageVisiblility( szMainControllerPage, true );
+ m_currentPage = MENU_PAGE_MAIN;
+ bRelayMessage = false;
+ break;
+ case MENU_PAGE_VEHICLECONTROLS:
+ GetInputManager()->GetFEMouse()->SetClickStopMode( false );
+ SetPageVisiblility( szVCPage, false );
+ SetPageVisiblility( szMainControllerPage, true );
+ m_currentPage = MENU_PAGE_MAIN;
+ bRelayMessage = false;
+ break;
+ case MENU_PAGE_GAMESETTINGS:
+ SetPageVisiblility( szGSPage, false );
+ SetPageVisiblility( szMainControllerPage, true );
+ m_currentPage = MENU_PAGE_MAIN;
+ bRelayMessage = false;
+ break;
+
+ default:
+ this->StartTransitionAnimation( 560, 590 );
+ bRelayMessage = true;
+ }
+ }
+ else
+ {
+ m_bDisableBack = false;
+ bRelayMessage = false;
+ }
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL && !m_bMapInput )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ if( bRelayMessage )
+ CGuiScreen::HandleMessage( message, param1, param2 );
+ else
+ bRelayMessage = true;
+}
+
+//===========================================================================
+// CGuiScreen::CheckCursorAgainstHotspots
+//===========================================================================
+// Description: Checks cursor position against its list of hotspots.
+//
+// Constraints: None.
+//
+// Parameters: float x - The x position of cursor in P3D coordinates.
+// float y - The y position of cursor in P3D coordinates.
+//
+// Return: N/A.
+//
+//===========================================================================
+eFEHotspotType CGuiScreenController::CheckCursorAgainstHotspots( float x, float y )
+{
+ if( m_bMapInput ) return HOTSPOT_NONE;
+ else return CGuiScreen::CheckCursorAgainstHotspots( x, y );
+}
+
+void CGuiScreenController::RemapButton( eControllerColumn column, int menuItem )
+{
+ switch(m_currentPage)
+ {
+ case MENU_PAGE_CHARACTERCONTROLS:
+ m_currentTextLabel = m_pCCLabels[column][menuItem];
+ break;
+ case MENU_PAGE_VEHICLECONTROLS:
+ m_currentTextLabel = m_pVCLabels[column][menuItem];
+ break;
+ default:
+ break;
+ }
+ if( m_currentTextLabel )
+ m_currentTextLabel->SetColour(DEFAULT_INPUTMAPPED_ITEM_COLOUR);
+
+ GetInputManager()->GetController(m_currentControllerID)->RemapButton( column,
+ GetVirtualKey(m_currentPage, menuItem),
+ this );
+ GetInputManager()->GetFEMouse()->SetSelectable( false );
+ m_bMapInput = true;
+}
+
+//===========================================================================
+// CGuiScreenController::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenController::InitIntro()
+{
+ // Init the page labels.
+ InitPageLabels(MENU_PAGE_CHARACTERCONTROLS);
+ InitPageLabels(MENU_PAGE_VEHICLECONTROLS);
+
+ UserController* pController = GetInputManager()->GetController( m_currentControllerID );
+ GuiMenuItem* pMenuItem = NULL;
+ // This is the real menuItem index.
+ int menuItem = m_menuGroupStartIndex[MENU_PAGE_GAMESETTINGS];
+
+ // This is relative to the enum section for GAME SETTINGS.
+ int relMenuItem = m_menuGroupStartIndex[MENU_PAGE_GAMESETTINGS] -
+ NUM_CHARACTERCONTROL_LABELS -
+ NUM_VEHICLECONTROL_LABELS;
+
+ rAssert( m_pMenu );
+
+ for( int i = 0; i < NUM_GAMESETTING_LABELS; i++, menuItem++, relMenuItem++ )
+ {
+ switch( relMenuItem )
+ {
+ case GS_MOUSELOOK:
+ {
+ m_pMenu->SetSelectionValue( menuItem, pController->IsMouseLookOn() == PROMPT_YES );
+ }
+ break;
+ case GS_MOUSESENSITIVITY_X:
+ {
+ pMenuItem = m_pMenu->GetMenuItem( menuItem );
+ rAssert( pMenuItem );
+ pMenuItem->m_slider.SetValue( pController->GetMouseSensitivityX() );
+ }
+ break;
+ case GS_MOUSESENSITIVITY_Y:
+ {
+ pMenuItem = m_pMenu->GetMenuItem( menuItem );
+ rAssert( pMenuItem );
+ pMenuItem->m_slider.SetValue( pController->GetMouseSensitivityY() );
+ }
+ break;
+ case GS_INVERTMOUSEX:
+ {
+ m_pMenu->SetSelectionValue( menuItem, pController->IsMouseXInverted() == PROMPT_YES );
+ }
+ break;
+ case GS_INVERTMOUSEY:
+ {
+ m_pMenu->SetSelectionValue( menuItem, pController->IsMouseYInverted() == PROMPT_YES );
+ }
+ break;
+ case GS_FORCEFEEDBACK:
+ {
+ m_pMenu->SetSelectionValue( menuItem, pController->IsForceFeedbackOn() == PROMPT_YES );
+ }
+ break;
+ case GS_WHEELSENSITIVITY:
+ {
+ pMenuItem = m_pMenu->GetMenuItem( menuItem );
+ rAssert( pMenuItem );
+ pMenuItem->m_slider.SetValue( pController->GetWheelSensitivityX() );
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenController::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenController::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenController::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenController::InitOutro()
+{
+ GetInputManager()->GetFEMouse()->SetClickStopMode( false );
+
+ // Save the new controller mappings to the config file.
+ GetGameConfigManager()->SaveConfigFile();
+}
+
+//===========================================================================
+// CGuiScreenController::OnButtonMapped
+//===========================================================================
+// Description: Gets called back by the UserController when a button is
+// mapped.
+//
+// Constraints: None.
+//
+// Parameters: szNewInput - The new input that just got mapped.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenController::OnButtonMapped( const char* szNewInput, eControllerType cont, int num_dirs, eDirectionType direction )
+{
+ char szText[255];
+ memset( szText, 0, sizeof( szText) );
+
+ if( !szNewInput ) //user cancelled the mapper event.
+ {
+ m_bDisableBack = true;
+ }
+ else
+ {
+ // Reset flags we changed.
+ if( m_currentTextLabel )
+ m_currentTextLabel->SetColour(DEFAULT_SELECTED_ITEM_COLOUR);
+
+ strcpy( szText, szNewInput );
+ GetAppropriateInputName( szText, cont, direction, num_dirs );
+
+ //Update the page labels.
+ UpdatePageLabels( m_currentPage, szText );
+
+ //Disable the tutorials if controls are changed.
+ GetInputManager()->GetController(0)->SetTutorialDisabled( true );
+ GetTutorialManager()->EnableTutorialMode( false );
+ }
+
+ GetInputManager()->GetFEMouse()->SetSelectable( true );
+ m_bMapInput = false;
+ m_currentTextLabel = NULL;
+}
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+void CGuiScreenController::UpdatePageLabels( eMenuPages page, const char* szNewInput )
+{
+ switch( page )
+ {
+ case MENU_PAGE_CHARACTERCONTROLS:
+ if( szNewInput )
+ {
+ //Search for this text in the array of texts to see if it already exists.
+ for( int mapCount = 0; mapCount < NUM_COLUMNS; mapCount++ )
+ {
+ for( int itemCount = 0; itemCount < NUM_CHARACTERCONTROL_LABELS; itemCount++ )
+ {
+ UnicodeString uniStrTemp = m_pCCLabels[mapCount][itemCount]->GetString();
+ char tempStr[255];
+ memset( tempStr, 0, sizeof( tempStr ) );
+ uniStrTemp.MakeAscii( tempStr, uniStrTemp.Length() + 1 );
+ if( strcmp( szNewInput, tempStr ) == 0 )
+ {
+ m_pCCLabels[mapCount][itemCount]->SetString(0, szNotAssigned);
+ break;
+ }
+ }
+ }
+ }
+
+ break;
+ case MENU_PAGE_VEHICLECONTROLS:
+ if( szNewInput )
+ {
+ //Search for this text in the array of texts to see if it already exists.
+ for( int mapCount = 0; mapCount < NUM_COLUMNS; mapCount++ )
+ {
+ for( int itemCount = 0; itemCount < NUM_VEHICLECONTROL_LABELS; itemCount++ )
+ {
+ UnicodeString uniStrTemp = m_pVCLabels[mapCount][itemCount]->GetString();
+ char tempStr[255];
+ memset( tempStr, 0, sizeof( tempStr ) );
+ uniStrTemp.MakeAscii( tempStr, uniStrTemp.Length() + 1 );
+ if( strcmp( szNewInput, tempStr ) == 0 )
+ {
+ m_pVCLabels[mapCount][itemCount]->SetString(0, szNotAssigned);
+ break;
+ }
+ }
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ if( m_currentTextLabel )
+ m_currentTextLabel->SetString(0, szNewInput);
+}
+
+void CGuiScreenController::InitPageLabels( eMenuPages page )
+{
+ UserController* pController = GetInputManager()->GetController(m_currentControllerID);
+
+ switch( page )
+ {
+ case MENU_PAGE_CHARACTERCONTROLS:
+ {
+ // Go through the elements and set the maps.
+ for( int mapCount = 0; mapCount < NUM_COLUMNS; mapCount++ )
+ {
+ eControllerType controllerType;
+ int ndirections;
+ eDirectionType dir;
+
+ for( int i = 0; i < NUM_CONTROLLERTYPES; i++ )
+ {
+ for( int ccItem = 0; ccItem < NUM_CHARACTERCONTROL_LABELS; ccItem++ )
+ {
+ const char* szName = pController->GetMap( mapCount,
+ GetVirtualKey(MENU_PAGE_CHARACTERCONTROLS,ccItem),
+ ndirections, controllerType, dir );
+ if( szName )
+ {
+ char szText[255];
+ memset( szText, 0, sizeof( szText) );
+
+ strcpy( szText, szName );
+ GetAppropriateInputName( szText, controllerType, dir, ndirections );
+ m_pCCLabels[mapCount][ccItem]->SetString(0, szText);
+ }
+ else
+ {
+ m_pCCLabels[mapCount][ccItem]->SetString(0, szNotAssigned);
+ }
+ }
+ }
+ }
+ } break;
+ case MENU_PAGE_VEHICLECONTROLS:
+ {
+ // Go through the elements and set the maps.
+ for( int mapCount = 0; mapCount < NUM_COLUMNS; mapCount++ )
+ {
+ eControllerType controllerType;
+ int ndirections;
+ eDirectionType dir;
+
+ for( int i = 0; i < NUM_CONTROLLERTYPES; i++ )
+ {
+ for( int ccItem = 0; ccItem < NUM_VEHICLECONTROL_LABELS; ccItem++ )
+ {
+ const char* szName = pController->GetMap( mapCount,
+ GetVirtualKey(MENU_PAGE_VEHICLECONTROLS,ccItem),
+ ndirections, controllerType, dir );
+ if( szName )
+ {
+ char szText[255];
+ memset( szText, 0, sizeof( szText) );
+
+ strcpy( szText, szName );
+ GetAppropriateInputName( szText, controllerType, dir, ndirections );
+ m_pVCLabels[mapCount][ccItem]->SetString(0, szText);
+ }
+ else
+ {
+ m_pVCLabels[mapCount][ccItem]->SetString(0, szNotAssigned);
+ }
+ }
+ }
+ }
+ } break;
+ default:
+ break;
+ }
+
+}
+
+void CGuiScreenController::SetGroups( Scrooby::Text** pLabels,
+ int numMenuItems,
+ const char* strPage,
+ char* strGroup,
+ char* szLabel,
+ int attributes )
+{
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( strPage );
+ rAssert( pPage );
+
+
+ // if this is a controller page set the start index of its menu items.
+ if( m_numControllerGroups < NUM_MENU_PAGES )
+ {
+ if( strcmp( strGroup, "Map1" ) == 0 || strcmp( strPage, szGSPage ) == 0 )
+ {
+ m_menuGroupStartIndex[m_numControllerGroups++] = m_pMenu->GetNumItems();
+ }
+ }
+
+ // Add main menu items
+ Scrooby::Group* pMenuGroup = pPage->GetGroup( strGroup );
+ for( int itemCount = 0; itemCount < numMenuItems; itemCount++ )
+ {
+ char objectName[ 32 ];
+ sprintf( objectName, "%s%02d", szLabel, itemCount );
+ Scrooby::Text* pMenuItemText = pMenuGroup->GetText( objectName );
+ if( pMenuItemText != NULL )
+ {
+ pMenuItemText->SetTextMode( Scrooby::TEXT_WRAP );
+
+ char itemName[32];
+ sprintf( itemName, "%s%02d_Value", szLabel, itemCount );
+ Scrooby::Text* pTextValue = pMenuGroup->GetText( itemName );
+
+ sprintf( itemName, "%s%02d_ArrowL", szLabel, itemCount );
+ Scrooby::Sprite* pLArrow = pMenuGroup->GetSprite( itemName );
+
+ sprintf( itemName, "%s%02d_ArrowR", szLabel, itemCount );
+ Scrooby::Sprite* pRArrow = pMenuGroup->GetSprite( itemName );
+
+ sprintf( itemName, "%s%02d_Slider", szLabel, itemCount );
+ Scrooby::Sprite* pSlider = pMenuGroup->GetSprite( itemName );
+
+ GuiMenuItem* pMenuItem = m_pMenu->AddMenuItem( pMenuItemText,
+ pTextValue,
+ NULL,
+ pSlider,
+ pLArrow,
+ pRArrow,
+ attributes );
+ pLabels[itemCount] = pMenuItemText;
+
+ if( pSlider )
+ pMenuItem->m_slider.m_type = Slider::HORIZONTAL_SLIDER_ABOUT_CENTER;
+ }
+ else
+ {
+ break;
+ }
+ }
+}
+
+void CGuiScreenController::SetPageVisiblility( const char* strPage, bool bVisible )
+{
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( strPage );
+ rAssert( pPage );
+ pPage->GetLayerByIndex( 0 )->SetVisible( bVisible );
+
+ //if it is any of our pages, set all their menu items to bVisible.
+ if( strcmp( strPage, szMainControllerPage ) == 0 )
+ {
+ for( int i = 0; i < NUM_MAINMENU_LABELS; i++ )
+ m_pMenuLabels[i]->SetVisible( bVisible );
+ if( bVisible )
+ m_pMenu->Reset( MENU_ITEM_CHARACTERCONTROLS );
+ }
+ else if( strcmp( strPage, szGSPage ) == 0 )
+ {
+ for( int i = 0; i < NUM_GAMESETTING_LABELS; i++ )
+ m_pMSLabels[i]->SetVisible( bVisible );
+ if( bVisible )
+ m_pMenu->Reset( m_menuGroupStartIndex[MENU_PAGE_GAMESETTINGS] );
+ }
+ else if( strcmp( strPage, szCCPage ) == 0 )
+ {
+ for( int columnCount = 0; columnCount < NUM_COLUMNS; columnCount++ )
+ for( int labelCount = 0; labelCount < NUM_CHARACTERCONTROL_LABELS; labelCount++ )
+ m_pCCLabels[columnCount][labelCount]->SetVisible( bVisible );
+ if( bVisible )
+ m_pMenu->Reset( m_menuGroupStartIndex[MENU_PAGE_CHARACTERCONTROLS] );
+ }
+ else if( strcmp( strPage, szVCPage ) == 0 )
+ {
+ for( int columnCount = 0; columnCount < NUM_COLUMNS; columnCount++ )
+ for( int labelCount = 0; labelCount < NUM_VEHICLECONTROL_LABELS; labelCount++ )
+ m_pVCLabels[columnCount][labelCount]->SetVisible( bVisible );
+ if( bVisible )
+ m_pMenu->Reset( m_menuGroupStartIndex[MENU_PAGE_VEHICLECONTROLS] );
+ }
+}
+
+int CGuiScreenController::GetVirtualKey( eMenuPages page, int menuItem )
+{
+ // We have to start the menuitem off at the correct index.
+ // This is simple, just add the menuitem with the correct start index.
+ switch(page)
+ {
+ case MENU_PAGE_CHARACTERCONTROLS:
+ menuItem += CC_MOVEUP;
+ break;
+ case MENU_PAGE_VEHICLECONTROLS:
+ menuItem += VC_ACCELERATE;
+ break;
+ default:
+ break;
+ }
+
+ switch(menuItem)
+ {
+ case CC_MOVEUP:
+ return InputManager::MoveUp;
+ case CC_MOVEDOWN:
+ return InputManager::MoveDown;
+ case CC_MOVELEFT:
+ return InputManager::MoveLeft;
+ case CC_MOVERIGHT:
+ return InputManager::MoveRight;
+ case CC_ATTACK:
+ return InputManager::Attack;
+ case CC_JUMP:
+ return InputManager::Jump;
+ case CC_SPRINT:
+ return InputManager::Sprint;
+ case CC_ACTION:
+ return InputManager::DoAction;
+ case CC_CAMERALEFT:
+ return InputManager::CameraLeft;
+ case CC_CAMERARIGHT:
+ return InputManager::CameraRight;
+ case CC_CAMERAMOVEIN:
+ return InputManager::CameraMoveIn;
+ case CC_CAMERAMOVEOUT:
+ return InputManager::CameraMoveOut;
+ case CC_LOOKUP:
+ return InputManager::CameraLookUp;
+ case CC_ZOOM:
+ return InputManager::CameraZoom;
+
+ case VC_ACCELERATE:
+ return InputManager::Accelerate;
+ case VC_REVERSE:
+ return InputManager::Reverse;
+ case VC_STEERLEFT:
+ return InputManager::SteerLeft;
+ case VC_STEERRIGHT:
+ return InputManager::SteerRight;
+ case VC_EBRAKE:
+ return InputManager::HandBrake;
+ case VC_ACTION:
+ return InputManager::GetOutCar;
+ case VC_HORN:
+ return InputManager::Horn;
+ case VC_RESET:
+ return InputManager::ResetCar;
+ case VC_LOOKLEFT:
+ return InputManager::CameraCarLeft;
+ case VC_LOOKRIGHT:
+ return InputManager::CameraCarRight;
+ case VC_LOOKUP:
+ return InputManager::CameraCarLookUp;
+ case VC_LOOKBACK:
+ return InputManager::CameraCarLookBack;
+ case VC_CHANGECAMERA:
+ return InputManager::CameraToggle;
+ default:
+ return 0;
+ }
+}
+
+void CGuiScreenController::GetAppropriateInputName( char* szInputName,
+ eControllerType controllerType,
+ eDirectionType direction,
+ int numDirections )
+{
+ char szText[255];
+ memset( szText, 0, sizeof( szText) );
+
+ if( szInputName )
+ {
+ // Do some hacky special casing here.
+ // Only done for the keyboard atm.
+ switch( controllerType )
+ {
+ case KEYBOARD:
+ {
+ // Special cases.
+ if( strcmp( szInputName, "Right Shift" ) == 0 )
+ strcpy( szInputName, "R. Shift" );
+ else if( strcmp( szInputName, "Left Shift" ) == 0 )
+ strcpy( szInputName, "L. Shift" );
+ }
+
+ break;
+ }
+ // truncate the name to the max allowed letters.
+ if( strlen(szInputName) > MAX_INPUTNAME_LENGTH )
+ {
+ szInputName[MAX_INPUTNAME_LENGTH-1] = '\0';
+ }
+ switch( controllerType )
+ {
+ case GAMEPAD:
+ case STEERINGWHEEL:
+ {
+ switch( numDirections )
+ {
+ case 2:
+ switch( direction )
+ {
+ case DIR_UP:
+ strcat( szInputName, " +");
+ break;
+ case DIR_DOWN:
+ strcat( szInputName, " -");
+ break;
+ default:
+ break;
+ }
+ break;
+ case 4: // It is a POV hat...
+ switch( direction )
+ {
+ case DIR_UP:
+ strcpy( szInputName,"POV Up" );
+ break;
+ case DIR_DOWN:
+ strcpy( szInputName,"POV Down" );
+ break;
+ case DIR_LEFT:
+ strcpy( szInputName,"POV Left" );
+ break;
+ case DIR_RIGHT:
+ strcpy( szInputName,"POV Right" );
+ break;
+ }
+ break;
+ }
+
+ switch( controllerType )
+ {
+ case GAMEPAD:
+ sprintf( szText,"J %s", szInputName );
+ strcpy( szInputName, szText );
+ break;
+ case STEERINGWHEEL:
+ sprintf( szText,"S %s", szInputName );
+ strcpy( szInputName, szText );
+ break;
+ }
+
+ break;
+ }
+ case MOUSE:
+ {
+ if( strchr( szInputName, '0' ) )
+ strcpy( szInputName,"Left Mouse" );
+ else if( strchr( szInputName, '1' ) )
+ strcpy( szInputName,"Right Mouse" );
+ else if( strchr( szInputName, '2' ) )
+ strcpy( szInputName,"Mid Mouse" );
+ else if( strchr( szInputName, '3' ) )
+ strcpy( szInputName,"Shoulder L" );
+ else if( strchr( szInputName, '4' ) )
+ strcpy( szInputName,"Shoulder R" );
+ else if( strchr( szInputName, '5' ) )
+ strcpy( szInputName,"Side Left" );
+ else if( strchr( szInputName, '6' ) )
+ strcpy( szInputName,"Side Right" );
+ }
+
+ break;
+ default:
+ break;
+ }
+ }
+} \ No newline at end of file
diff --git a/game/code/presentation/gui/frontend/guiscreencontrollerWin32.h b/game/code/presentation/gui/frontend/guiscreencontrollerWin32.h
new file mode 100644
index 0000000..d90a005
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreencontrollerWin32.h
@@ -0,0 +1,163 @@
+/******************************************************************************
+
+ File: GuiScreenControllerWin32.h
+ Desc: Interface for the CGuiScreenController class.
+
+ Date: July 31, 2003
+ Author: Neil Haran
+ History:
+
+*****************************************************************************/
+
+#ifndef GUISCREENCONTROLLER_H
+#define GUISCREENCONTROLLER_H
+
+#include <presentation/gui/guiscreen.h>
+#include <input/usercontrollerWin32.h>
+
+class CGuiScreenController : public CGuiScreen,
+ public ButtonMappedCallback
+{
+public:
+ CGuiScreenController( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenController();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+ virtual eFEHotspotType CheckCursorAgainstHotspots( float x, float y );
+ virtual void OnButtonMapped( const char* InputName, eControllerType cont, int num_dirs, eDirectionType direction );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ enum eControllerPromptResponse
+ {
+ PROMPT_NO,
+ PROMPT_YES,
+ NUM_CONTROLLER_PROMPT_RESPONSES
+ };
+
+ enum eControllerColumn
+ {
+ MAP_PRIMARY = 0,
+ MAP_SECONDARY,
+ NUM_COLUMNS,
+ };
+
+ enum eMenuPages
+ {
+ MENU_PAGE_MAIN,
+ MENU_PAGE_CHARACTERCONTROLS,
+ MENU_PAGE_VEHICLECONTROLS,
+ MENU_PAGE_GAMESETTINGS,
+ NUM_MENU_PAGES,
+ NUM_CONTROLLER_PAGES = NUM_MENU_PAGES - 1,
+ MENU_PAGE_NONE
+ };
+
+ enum eMenuItem
+ {
+ // MAIN CONTROLLER MENU ITEMS
+ MENU_ITEM_CHARACTERCONTROLS = 0,
+ MENU_ITEM_VEHICLECONTROLS,
+ MENU_ITEM_GAMESETTINGS,
+ MENU_ITEM_RESTOREALLDEFAULTS,
+ MENU_ITEM_NONE,
+ NUM_MAINMENU_LABELS = MENU_ITEM_NONE,
+
+ // Character Control ITEMS
+ CC_MOVEUP = MENU_ITEM_NONE,
+ CC_MOVEDOWN,
+ CC_MOVELEFT,
+ CC_MOVERIGHT,
+ CC_ATTACK,
+ CC_JUMP,
+ CC_SPRINT,
+ CC_ACTION,
+ CC_CAMERALEFT,
+ CC_CAMERARIGHT,
+ CC_CAMERAMOVEIN,
+ CC_CAMERAMOVEOUT,
+ CC_ZOOM,
+ CC_LOOKUP,
+ CC_NONE,
+ NUM_CHARACTERCONTROL_LABELS = CC_NONE - MENU_ITEM_NONE,
+
+ // Vehicle Control ITEMS
+ VC_ACCELERATE = CC_NONE,
+ VC_REVERSE,
+ VC_STEERLEFT,
+ VC_STEERRIGHT,
+ VC_EBRAKE,
+ VC_ACTION,
+ VC_HORN,
+ VC_RESET,
+ VC_LOOKLEFT,
+ VC_LOOKRIGHT,
+ VC_LOOKUP,
+ VC_LOOKBACK,
+ VC_CHANGECAMERA,
+
+ VC_NONE,
+ NUM_VEHICLECONTROL_LABELS = VC_NONE - CC_NONE,
+
+ // Game Setting Items
+ GS_MOUSELOOK = VC_NONE,
+ GS_MOUSESENSITIVITY_X,
+ GS_MOUSESENSITIVITY_Y,
+ GS_INVERTMOUSEX,
+ GS_INVERTMOUSEY,
+ GS_FORCEFEEDBACK,
+ GS_WHEELSENSITIVITY,
+ GS_NONE,
+ NUM_GAMESETTING_LABELS = GS_NONE - VC_NONE,
+
+ NUM_MENU_ITEMS = NUM_MAINMENU_LABELS +
+ NUM_CHARACTERCONTROL_LABELS*NUM_COLUMNS +
+ NUM_VEHICLECONTROL_LABELS*NUM_COLUMNS +
+ NUM_GAMESETTING_LABELS
+ };
+
+private:
+ void InitPageLabels( eMenuPages page );
+ void UpdatePageLabels( eMenuPages page, const char* szNewInput );
+ void SetGroups( Scrooby::Text** pLabels,
+ int numMenuItems,
+ const char* strPage,
+ char* strGroup = "Menu",
+ char* szLabel = "Label",
+ int attributes = SELECTION_ENABLED | SELECTABLE | VALUES_WRAPPED );
+
+ void SetPageVisiblility( const char* strPage, bool bVisible );
+ int GetVirtualKey( eMenuPages page, int menuItem );
+ void RemapButton( eControllerColumn column, int menuItem );
+ void GetAppropriateInputName( char* szInputName,
+ eControllerType controllerType,
+ eDirectionType direction,
+ int numDirections );
+
+private:
+ CGuiMenu* m_pMenu;
+ Scrooby::Text* m_pMenuLabels[ NUM_MAINMENU_LABELS ];
+ Scrooby::Text* m_pCCLabels[ NUM_COLUMNS ][ NUM_CHARACTERCONTROL_LABELS ];
+ Scrooby::Text* m_pVCLabels[ NUM_COLUMNS ][ NUM_VEHICLECONTROL_LABELS ];
+ Scrooby::Text* m_pMSLabels[ NUM_GAMESETTING_LABELS ];
+ Scrooby::Text* m_currentTextLabel;
+
+ // These arrays are for the screen mappings.
+ eMenuPages m_currentPage;
+ int m_currentControllerID;
+ bool m_bMapInput;
+ bool m_bDisableBack;
+ int m_menuGroupStartIndex[NUM_MENU_PAGES];
+ int m_numControllerGroups;
+};
+
+#endif
diff --git a/game/code/presentation/gui/frontend/guiscreencontrollerWin32old.cpp b/game/code/presentation/gui/frontend/guiscreencontrollerWin32old.cpp
new file mode 100644
index 0000000..5309702
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreencontrollerWin32old.cpp
@@ -0,0 +1,867 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenController
+//
+// Description: Implementation of the CGuiScreenController class.
+//
+// Authors: Tony Chu,
+// Neil Haran
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreencontrollerWin32.h>
+#include <presentation/gui/guimenu.h>
+
+#include <data/config/gameconfigmanager.h>
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <events/eventmanager.h>
+
+#include <raddebug.hpp> // Foundation
+#include <group.h>
+#include <layer.h>
+#include <page.h>
+#include <screen.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const char* szMainControllerPage = "ControllerPC";
+const char* szKeyboardMousePage = "PCKeyboardConfig";
+const char* szGamepadPage = "PCGamepadConfig";
+
+const int NUM_DISPLAY_MODES = 2;
+extern const char* gVirtualInputs[];
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+const tColour DEFAULT_SELECTED_ITEM_COLOUR( 255, 255, 0 );
+const tColour DEFAULT_INPUTMAPPED_ITEM_COLOUR( 255, 0, 0 ); // the same as in guiscreenmemorycard.cpp
+
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenController::CGuiScreenController
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenController::CGuiScreenController
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_CONTROLLER ),
+ m_pMenu( NULL ),
+ m_currentPage( MENU_PAGE_MAIN ),
+ m_currentControllerID( 0 ),
+ m_bMapInput( false ),
+ m_currentTextLabel(NULL),
+ m_bDisableBack(false),
+ m_numControllerGroups(0)
+{
+ memset( m_menuGroupStartIndex, 0 , sizeof( m_menuGroupStartIndex ) );
+ m_pMenu = new(GMA_LEVEL_FE) CGuiMenu( this, NUM_MENU_ITEMS );
+ rAssert( m_pMenu != NULL );
+
+ // Add main menu items
+ SetSingleItemMenuPage( m_pMenuLabels, NUM_MAINMENU_LABELS, szMainControllerPage, "Menu", "Label", ALL_ATTRIBUTES_ON );
+ m_numControllerGroups = 1; // The first page is the MENU_PAGE_MAIN with an index of 0.
+
+ // Add master list items
+ SetSingleItemMenuPage( m_pKMLabels[MASTER_LIST], NUM_KEYBOARDMOUSE_LABELS, szKeyboardMousePage, "Menu", "Label", ALL_ATTRIBUTES_OFF );
+ SetSingleItemMenuPage( m_pGamepadLabels[MASTER_LIST], NUM_GAMEPAD_LABELS, szGamepadPage, "Menu", "Label", ALL_ATTRIBUTES_OFF );
+ // Add map items
+ SetSingleItemMenuPage( m_pKMLabels[MAP_PRIMARY], NUM_KEYBOARDMOUSE_LABELS, szKeyboardMousePage, "Map1", "Map1Label" );
+ SetSingleItemMenuPage( m_pKMLabels[MAP_SECONDARY], NUM_KEYBOARDMOUSE_LABELS, szKeyboardMousePage, "Map2", "Map2Label" );
+ SetSingleItemMenuPage( m_pGamepadLabels[MAP_PRIMARY], NUM_GAMEPAD_LABELS, szGamepadPage, "Map1", "Map1Label" );
+ SetSingleItemMenuPage( m_pGamepadLabels[MAP_SECONDARY], NUM_GAMEPAD_LABELS, szGamepadPage, "Map2", "Map2Label" );
+
+
+ //Turn off the controller page that shows up for the consoles.
+ SetPageVisiblility( "Controller", false );
+ SetPageVisiblility( szMainControllerPage, true );
+}
+
+
+//===========================================================================
+// CGuiScreenController::~CGuiScreenController
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenController::~CGuiScreenController()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenController::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenController::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ static bool bRelayMessage = true;
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_UP:
+ {
+ // Basically a wrap around system when you reach the top.
+ // Just jumps to the last element.
+ switch(m_currentPage)
+ {
+ case MENU_PAGE_MAIN:
+ if( m_pMenu->GetSelection() == m_menuGroupStartIndex[m_currentPage] )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+(NUM_MAINMENU_LABELS-1));
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ break;
+ case MENU_PAGE_KM:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On the first element on keyboard Map 1
+ if( menuItem == m_menuGroupStartIndex[m_currentPage] )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+(NUM_KEYBOARDMOUSE_LABELS-1));
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ else if( menuItem == m_menuGroupStartIndex[m_currentPage]+NUM_KEYBOARDMOUSE_LABELS )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+(NUM_KEYBOARDMOUSE_LABELS*2)-1);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+
+ break;
+ case MENU_PAGE_GAMEPAD:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On the first element on gamepad Map 1
+ if( menuItem == m_menuGroupStartIndex[m_currentPage] )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+(NUM_GAMEPAD_LABELS-1));
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ else if( menuItem == m_menuGroupStartIndex[m_currentPage]+NUM_GAMEPAD_LABELS )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+(NUM_GAMEPAD_LABELS*2)-1);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case GUI_MSG_CONTROLLER_DOWN:
+ {
+ // Basically a wrap around system when you reach the bottom.
+ // Just jumps to the first element.
+ switch(m_currentPage)
+ {
+ case MENU_PAGE_MAIN:
+ if( m_pMenu->GetSelection() == m_menuGroupStartIndex[m_currentPage]+(NUM_MAINMENU_LABELS-1) )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ break;
+ case MENU_PAGE_KM:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On the first element on keyboard Map 1
+ if( menuItem == m_menuGroupStartIndex[m_currentPage]+(NUM_KEYBOARDMOUSE_LABELS-1) )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ else if( menuItem == m_menuGroupStartIndex[m_currentPage]+(NUM_KEYBOARDMOUSE_LABELS*2)-1 )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+NUM_KEYBOARDMOUSE_LABELS);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+
+ break;
+ case MENU_PAGE_GAMEPAD:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On the first element on gamepad Map 1
+ if( menuItem == m_menuGroupStartIndex[m_currentPage]+(NUM_GAMEPAD_LABELS-1) )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ else if( menuItem == m_menuGroupStartIndex[m_currentPage]+(NUM_GAMEPAD_LABELS*2)-1 )
+ {
+ m_pMenu->Reset(m_menuGroupStartIndex[m_currentPage]+NUM_GAMEPAD_LABELS);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case GUI_MSG_CONTROLLER_LEFT:
+ case GUI_MSG_CONTROLLER_RIGHT:
+ {
+ // Basically a wrap around system when you go left or right.
+ switch(m_currentPage)
+ {
+ case MENU_PAGE_KM:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On Map 1
+ if( menuItem < m_menuGroupStartIndex[m_currentPage] + NUM_KEYBOARDMOUSE_LABELS )
+ {
+ m_pMenu->Reset(menuItem+NUM_KEYBOARDMOUSE_LABELS);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ else // On Map 2
+ {
+ m_pMenu->Reset(menuItem-NUM_KEYBOARDMOUSE_LABELS);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+
+ break;
+ case MENU_PAGE_GAMEPAD:
+ {
+ int menuItem = m_pMenu->GetSelection();
+ // On Map 1
+ if( menuItem < m_menuGroupStartIndex[m_currentPage] + NUM_GAMEPAD_LABELS )
+ {
+ m_pMenu->Reset(menuItem+NUM_GAMEPAD_LABELS);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ else // On Map 2
+ {
+ m_pMenu->Reset(menuItem-NUM_GAMEPAD_LABELS);
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ return;
+ }
+ }
+
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ switch( m_currentPage )
+ {
+ case MENU_PAGE_MAIN:
+ {
+ switch( param1 )
+ {
+ case MENU_ITEM_KEYMOUSECONFIG:
+ {
+ GetInputManager()->GetFEMouse()->SetClickStopMode( true );
+ SetPageVisiblility( szMainControllerPage, false );
+ SetPageVisiblility( szKeyboardMousePage, true );
+ m_currentPage = MENU_PAGE_KM;
+ break;
+ }
+ case MENU_ITEM_GAMEPADCONFIG:
+ {
+ GetInputManager()->GetFEMouse()->SetClickStopMode( true );
+ SetPageVisiblility( szMainControllerPage, false );
+ SetPageVisiblility( szGamepadPage, true );
+ m_currentPage = MENU_PAGE_GAMEPAD;
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ } break;
+ case MENU_PAGE_KM:
+ {
+ // if we're on Map 1 or Map 2
+ if( param1 >= (unsigned int)m_menuGroupStartIndex[m_currentPage] )
+ {
+ int menuItem = param1;
+ // On Map 1
+ if( menuItem < m_menuGroupStartIndex[m_currentPage] + NUM_KEYBOARDMOUSE_LABELS )
+ {
+ // We know what map it is so just convert this into a value between 0 - NUM_KEYBOARDMOUSE_LABELS-1
+ menuItem = menuItem - m_menuGroupStartIndex[m_currentPage];
+ RemapButton( MAP_PRIMARY, KEYBOARD, menuItem );
+ break;
+ }
+ else // On Map 2
+ {
+ menuItem = menuItem - m_menuGroupStartIndex[m_currentPage] - NUM_KEYBOARDMOUSE_LABELS;
+ RemapButton( MAP_SECONDARY, KEYBOARD, menuItem );
+ break;
+ }
+ }
+ } break;
+ case MENU_PAGE_GAMEPAD:
+ {
+ if( param1 >= (unsigned int)m_menuGroupStartIndex[m_currentPage] )
+ {
+ int menuItem = param1;
+ // On Map 1
+ if( menuItem < m_menuGroupStartIndex[m_currentPage] + NUM_GAMEPAD_LABELS )
+ {
+ // We know what map it is so just convert this into a value between 0 - NUM_KEYBOARDMOUSE_LABELS-1
+ menuItem = menuItem - m_menuGroupStartIndex[m_currentPage];
+ RemapButton( MAP_PRIMARY, GAMEPAD, menuItem );
+ break;
+ }
+ else // On Map 2
+ {
+ menuItem = menuItem - m_menuGroupStartIndex[m_currentPage] - NUM_GAMEPAD_LABELS;
+ RemapButton( MAP_SECONDARY, GAMEPAD, menuItem );
+ break;
+ }
+ }
+ } break;
+ default: break;
+ }
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ // mapping an input, so any key to trigger this event should be ignored.
+ if( !m_bDisableBack )
+ {
+ switch( m_currentPage )
+ {
+ case MENU_PAGE_KM:
+ GetInputManager()->GetFEMouse()->SetClickStopMode( false );
+ SetPageVisiblility( szKeyboardMousePage, false );
+ SetPageVisiblility( szMainControllerPage, true );
+ m_currentPage = MENU_PAGE_MAIN;
+ bRelayMessage = false;
+ break;
+ case MENU_PAGE_GAMEPAD:
+ GetInputManager()->GetFEMouse()->SetClickStopMode( false );
+ SetPageVisiblility( szGamepadPage, false );
+ SetPageVisiblility( szMainControllerPage, true );
+ m_currentPage = MENU_PAGE_MAIN;
+ bRelayMessage = false;
+ break;
+
+ default:
+ this->StartTransitionAnimation( 560, 590 );
+ bRelayMessage = true;
+ }
+ }
+ else
+ {
+ m_bDisableBack = false;
+ bRelayMessage = false;
+ }
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL && !m_bMapInput )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ if( bRelayMessage)
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+void CGuiScreenController::RemapButton( eControllerColumn column, eControllerType type, int menuItem )
+{
+ switch(m_currentPage)
+ {
+ case MENU_PAGE_KM:
+ m_currentTextLabel = m_pKMLabels[column][menuItem];
+ break;
+ case MENU_PAGE_GAMEPAD:
+ m_currentTextLabel = m_pGamepadLabels[column][menuItem];
+ break;
+ default:
+ break;
+ }
+ if( m_currentTextLabel )
+ m_currentTextLabel->SetColour(DEFAULT_INPUTMAPPED_ITEM_COLOUR);
+ GetInputManager()->GetController(m_currentControllerID)->RemapButton( type,
+ (column == MAP_PRIMARY) ? SLOT_PRIMARY : SLOT_SECONDARY,
+ GetVirtualKey(menuItem),
+ this );
+ GetInputManager()->GetFEMouse()->SetSelectable( false );
+ m_bMapInput = true;
+}
+
+//===========================================================================
+// CGuiScreenController::CheckCursorAgainstHotspots
+//===========================================================================
+// Description: Checks cursor position against its list of hotspots.
+//
+// Constraints: None.
+//
+// Parameters: float x - The x position of cursor in P3D coordinates.
+// float y - The y position of cursor in P3D coordinates.
+//
+// Return: N/A.
+//
+//===========================================================================
+eFEHotspotType CGuiScreenController::CheckCursorAgainstHotspots( float x, float y )
+{
+ eFEHotspotType hotSpotType = HOTSPOT_NONE;
+ CGuiMenu* pCurrentMenu = HasMenu();
+ int numMenuItems = 0;
+ GuiMenuItem* pMenuItem = NULL;
+
+
+ if( pCurrentMenu )
+ {
+ numMenuItems = pCurrentMenu->GetNumItems();
+
+ for( int i = 0; i < numMenuItems; i++ )
+ {
+ bool bIsMenuItemEnabled = pCurrentMenu->IsMenuItemEnabled(i);
+ pMenuItem = pCurrentMenu->GetMenuItem( i );
+
+ if( pMenuItem && bIsMenuItemEnabled )
+ {
+ if( pMenuItem->GetItem()->IsVisible() )
+ {
+ // Just tests if the point is in the bounding rect of the sprite.
+ if( pMenuItem->GetItem()->IsPointInBoundingRect( x, y ) )
+ {
+ //rDebugPrintf( "Cursor is inside Sprite %d rectangle!\n", i );
+ pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_OVER, i );
+ hotSpotType = HOTSPOT_BUTTON;
+
+ //After taking care of all the events for this menu item, just bail out.
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return hotSpotType;
+}
+
+void CGuiScreenController::OnButtonMapped( const char* szNewInput )
+{
+ if( szNewInput )
+ {
+ if( m_currentTextLabel )
+ m_currentTextLabel->SetString(0, szNewInput);
+ }
+ else //user cancelled the mapper event.
+ {
+ m_bDisableBack = true;
+ }
+
+ // Reset flags we changed.
+ if( m_currentTextLabel )
+ m_currentTextLabel->SetColour(DEFAULT_SELECTED_ITEM_COLOUR);
+
+ //Update the page labels.
+ UpdatePageLabels(m_currentPage);
+ GetInputManager()->GetFEMouse()->SetSelectable( true );
+ m_bMapInput = false;
+ m_currentTextLabel = NULL;
+}
+
+//===========================================================================
+// CGuiScreenController::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenController::InitIntro()
+{
+ // Init the page labels.
+ UpdatePageLabels(MENU_PAGE_KM);
+ UpdatePageLabels(MENU_PAGE_GAMEPAD);
+}
+
+
+//===========================================================================
+// CGuiScreenController::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenController::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenController::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenController::InitOutro()
+{
+ GetInputManager()->GetFEMouse()->SetClickStopMode( false );
+
+ // Save the new controller mappings to the config file.
+ GetGameConfigManager()->SaveConfigFile();
+}
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void CGuiScreenController::UpdatePageLabels( eMenuPages page )
+{
+ UserController* pController = GetInputManager()->GetController(m_currentControllerID);
+ const char* szNotAssigned = "---";
+ switch( page )
+ {
+ case MENU_PAGE_KM:
+ {
+ RealController* pKeyBoard = pController->GetRealController( KEYBOARD );
+
+ int diMap[NUM_MAPS][NUM_KEYBOARDMOUSE_LABELS] =
+ {
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::MoveUp ),
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::MoveDown ),
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::MoveLeft ),
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::MoveRight ),
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::CameraFirstPerson ),
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::Attack ),
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::Jump ),
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::Sprint ),
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::DoAction ),
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::CameraZoom ),
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::Accelerate ),
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::HandBrake ),
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::Horn ),
+ pKeyBoard->GetMap( SLOT_PRIMARY, InputManager::ResetCar ),
+
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::MoveUp ),
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::MoveDown ),
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::MoveLeft ),
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::MoveRight ),
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::CameraFirstPerson ),
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::Attack ),
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::Jump ),
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::Sprint ),
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::DoAction ),
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::CameraZoom ),
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::Accelerate ),
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::HandBrake ),
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::Horn ),
+ pKeyBoard->GetMap( SLOT_SECONDARY, InputManager::ResetCar ),
+ };
+
+ //Input::INVALID_CONTROLLERID
+
+ for( int column = MAP_PRIMARY, mapCount = 0; column < NUM_COLUMNS; column++, mapCount++ )
+ {
+ for( int kmItem = 0; kmItem < NUM_KEYBOARDMOUSE_LABELS; kmItem++ )
+ {
+ RADCONTROLLER pRadController = pKeyBoard->getController();
+ IRadControllerInputPoint* pInputPoint = NULL;
+ const char* szText = szNotAssigned;
+ int vKey = diMap[mapCount][kmItem];
+ if( vKey != Input::INVALID_CONTROLLERID )
+ {
+ pInputPoint = pRadController->GetInputPointByTypeAndIndex("Button", VirtualKeyToIndex[vKey]);
+ if( pInputPoint ) szText = pInputPoint->GetName();
+ }
+
+ m_pKMLabels[column][kmItem]->SetString(0, szText);
+ }
+ }
+ } break;
+ case MENU_PAGE_GAMEPAD:
+ {
+ RealController* pGamepad = pController->GetRealController( GAMEPAD );
+ RADCONTROLLER pRadController = pGamepad->getController();
+ if( pGamepad && pRadController )
+ {
+ int diMap[NUM_MAPS][NUM_GAMEPAD_LABELS] =
+ {
+ pGamepad->GetMap( SLOT_PRIMARY, InputManager::CameraFirstPerson ),
+ pGamepad->GetMap( SLOT_PRIMARY, InputManager::Attack ),
+ pGamepad->GetMap( SLOT_PRIMARY, InputManager::Jump ),
+ pGamepad->GetMap( SLOT_PRIMARY, InputManager::Sprint ),
+ pGamepad->GetMap( SLOT_PRIMARY, InputManager::DoAction ),
+ pGamepad->GetMap( SLOT_PRIMARY, InputManager::CameraZoom ),
+ pGamepad->GetMap( SLOT_PRIMARY, InputManager::Accelerate ),
+ pGamepad->GetMap( SLOT_PRIMARY, InputManager::HandBrake ),
+ pGamepad->GetMap( SLOT_PRIMARY, InputManager::Horn ),
+ pGamepad->GetMap( SLOT_PRIMARY, InputManager::ResetCar ),
+
+ pGamepad->GetMap( SLOT_SECONDARY, InputManager::CameraFirstPerson ),
+ pGamepad->GetMap( SLOT_SECONDARY, InputManager::Attack ),
+ pGamepad->GetMap( SLOT_SECONDARY, InputManager::Jump ),
+ pGamepad->GetMap( SLOT_SECONDARY, InputManager::Sprint ),
+ pGamepad->GetMap( SLOT_SECONDARY, InputManager::DoAction ),
+ pGamepad->GetMap( SLOT_SECONDARY, InputManager::CameraZoom ),
+ pGamepad->GetMap( SLOT_SECONDARY, InputManager::Accelerate ),
+ pGamepad->GetMap( SLOT_SECONDARY, InputManager::HandBrake ),
+ pGamepad->GetMap( SLOT_SECONDARY, InputManager::Horn ),
+ pGamepad->GetMap( SLOT_SECONDARY, InputManager::ResetCar ),
+ };
+
+ //Input::INVALID_CONTROLLERID
+
+ for( int column = MAP_PRIMARY, mapCount = 0; column < NUM_COLUMNS; column++, mapCount++ )
+ {
+ for( int gamepadItem = 0; gamepadItem < NUM_GAMEPAD_LABELS; gamepadItem++ )
+ {
+ IRadControllerInputPoint* pInputPoint = NULL;
+ const char* szText = szNotAssigned;
+ int vKey = diMap[mapCount][gamepadItem];
+ if( vKey != Input::INVALID_CONTROLLERID )
+ {
+ pInputPoint = pRadController->GetInputPointByTypeAndIndex("Button", VirtualJoyKeyToIndex[vKey]);
+ if( pInputPoint ) szText = pInputPoint->GetName();
+ }
+
+ m_pGamepadLabels[column][gamepadItem]->SetString(0, szText);
+ }
+ }
+ }
+ else
+ {
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_GAMEPADCONFIG, false );
+ }
+ } break;
+ default:
+ break;
+ }
+
+}
+
+void CGuiScreenController::SetSingleItemMenuPage( Scrooby::Text** pLabels,
+ int numMenuItems,
+ const char* strPage,
+ char* strGroup,
+ char* szLabel,
+ int attributes )
+{
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( strPage );
+ rAssert( pPage );
+
+
+ // if this is a controller page set the start index of its menu items.
+ if( !strcmp( strGroup, "Map1" ) && m_numControllerGroups < NUM_MENU_PAGES )
+ {
+ m_menuGroupStartIndex[m_numControllerGroups++] = m_pMenu->GetNumItems();
+ }
+
+ // Add main menu items
+ Scrooby::Group* pMenuGroup = pPage->GetGroup( strGroup );
+ for( int itemCount = 0; itemCount < numMenuItems; itemCount++ )
+ {
+ char objectName[ 32 ];
+ sprintf( objectName, "%s%02d", szLabel, itemCount );
+ Scrooby::Text* pMenuItemText = pMenuGroup->GetText( objectName );
+ if( pMenuItemText != NULL )
+ {
+ pMenuItemText->SetTextMode( Scrooby::TEXT_WRAP );
+ m_pMenu->AddMenuItem( pMenuItemText,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ attributes );
+ pLabels[itemCount] = pMenuItemText;
+ }
+ else
+ {
+ break;
+ }
+ }
+}
+
+void CGuiScreenController::SetPageVisiblility( const char* strPage, bool bVisible )
+{
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( strPage );
+ rAssert( pPage );
+ pPage->GetLayerByIndex( 0 )->SetVisible( bVisible );
+
+ //if it is any of our pages, set all their menu items to bVisible.
+ if( !strcmp( strPage, szMainControllerPage ) )
+ {
+ for( int i = 0; i < NUM_MAINMENU_LABELS; i++ )
+ m_pMenuLabels[i]->SetVisible( bVisible );
+ if( bVisible )
+ m_pMenu->Reset( MENU_ITEM_KEYMOUSECONFIG );
+ }
+ else if( !strcmp( strPage, szKeyboardMousePage ) )
+ {
+ for( int columnCount = 0; columnCount < NUM_COLUMNS; columnCount++ )
+ for( int labelCount = 0; labelCount < NUM_KEYBOARDMOUSE_LABELS; labelCount++ )
+ m_pKMLabels[columnCount][labelCount]->SetVisible( bVisible );
+ if( bVisible )
+ m_pMenu->Reset( m_menuGroupStartIndex[MENU_PAGE_KM] );
+ }
+ else if( !strcmp( strPage, szGamepadPage ) )
+ {
+ for( int columnCount = 0; columnCount < NUM_COLUMNS; columnCount++ )
+ for( int labelCount = 0; labelCount < NUM_GAMEPAD_LABELS; labelCount++ )
+ m_pGamepadLabels[columnCount][labelCount]->SetVisible( bVisible );
+ if( bVisible )
+ m_pMenu->Reset( m_menuGroupStartIndex[MENU_PAGE_GAMEPAD] );
+ }
+}
+
+int CGuiScreenController::GetVirtualKey( int menuItem )
+{
+ // We have to start the menuitem off at the correct index.
+ // This is simple, just add the menuitem with the correct start index.
+ switch(m_currentPage)
+ {
+ case MENU_PAGE_KM:
+ menuItem += KM_MOVEUP;
+ break;
+ case MENU_PAGE_GAMEPAD:
+ menuItem += GAMEPAD_FIRSTPERSON;
+ break;
+ default:
+ break;
+ }
+
+ switch(menuItem)
+ {
+ case KM_MOVEUP:
+ return InputManager::MoveUp;
+ case KM_MOVEDOWN:
+ return InputManager::MoveDown;
+ case KM_MOVELEFT:
+ return InputManager::MoveLeft;
+ case KM_MOVERIGHT:
+ return InputManager::MoveRight;
+ case KM_FIRSTPERSON:
+ case GAMEPAD_FIRSTPERSON:
+ return InputManager::CameraFirstPerson;
+ case KM_ATTACK:
+ case GAMEPAD_ATTACK:
+ return InputManager::Attack;
+ case KM_JUMP:
+ case GAMEPAD_JUMP:
+ return InputManager::Jump;
+ case KM_SPRINT:
+ case GAMEPAD_SPRINT:
+ return InputManager::Sprint;
+ case KM_DOACTION:
+ case GAMEPAD_DOACTION:
+ return InputManager::DoAction;
+ case KM_CAMERAZOOM:
+ case GAMEPAD_CAMERAZOOM:
+ return InputManager::CameraZoom;
+ case KM_ACCELERATE:
+ case GAMEPAD_ACCELERATE:
+ return InputManager::Accelerate;
+ case KM_EBRAKE:
+ case GAMEPAD_EBRAKE:
+ return InputManager::HandBrake;
+ case KM_HORN:
+ case GAMEPAD_HORN:
+ return InputManager::Horn;
+ case KM_RESETCAR:
+ case GAMEPAD_RESETCAR:
+ return InputManager::ResetCar;
+ default:
+ return 0;
+ }
+} \ No newline at end of file
diff --git a/game/code/presentation/gui/frontend/guiscreencontrollerWin32old.h b/game/code/presentation/gui/frontend/guiscreencontrollerWin32old.h
new file mode 100644
index 0000000..2328afc
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreencontrollerWin32old.h
@@ -0,0 +1,147 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenController
+//
+// Description:
+//
+//
+// Authors: Tony Chu,
+// Neil Haran
+//
+// Revisions Date Author Revision
+// 2002/07/30 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENCONTROLLER_H
+#define GUISCREENCONTROLLER_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <input/usercontrollerWin32.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenController : public CGuiScreen,
+ public ButtonMappedCallback
+{
+public:
+ CGuiScreenController( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenController();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+ virtual eFEHotspotType CheckCursorAgainstHotspots( float x, float y );
+ virtual void OnButtonMapped( const char* szNewInput );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ enum eControllerColumn
+ {
+ MASTER_LIST = 0,
+ MAP_PRIMARY,
+ MAP_SECONDARY,
+ NUM_COLUMNS,
+ NUM_MAPS = NUM_COLUMNS - 1
+ };
+
+ enum eMenuPages
+ {
+ MENU_PAGE_MAIN,
+ MENU_PAGE_KM,
+ MENU_PAGE_GAMEPAD,
+ NUM_MENU_PAGES,
+ NUM_CONTROLLER_PAGES = NUM_MENU_PAGES - 1,
+ MENU_PAGE_NONE
+ };
+
+ enum eMenuItem
+ {
+ // MAIN CONTROLLER MENU ITEMS
+ MENU_ITEM_KEYMOUSECONFIG = 0,
+ MENU_ITEM_GAMEPADCONFIG,
+ MENU_ITEM_NONE,
+ NUM_MAINMENU_LABELS = MENU_ITEM_NONE,
+
+ // KEYBOARD/MOUSE ITEMS
+ KM_MOVEUP = MENU_ITEM_NONE,
+ KM_MOVEDOWN,
+ KM_MOVELEFT,
+ KM_MOVERIGHT,
+ KM_FIRSTPERSON,
+ KM_ATTACK,
+ KM_JUMP,
+ KM_SPRINT,
+ KM_DOACTION,
+ KM_CAMERAZOOM,
+ KM_ACCELERATE,
+ KM_EBRAKE,
+ KM_HORN,
+ KM_RESETCAR,
+ KM_NONE,
+ NUM_KEYBOARDMOUSE_LABELS = KM_NONE - MENU_ITEM_NONE,
+
+ // GAMEPAD ITEMS
+ GAMEPAD_FIRSTPERSON = KM_NONE,
+ GAMEPAD_ATTACK,
+ GAMEPAD_JUMP,
+ GAMEPAD_SPRINT,
+ GAMEPAD_DOACTION,
+ GAMEPAD_CAMERAZOOM,
+ GAMEPAD_ACCELERATE,
+ GAMEPAD_EBRAKE,
+ GAMEPAD_HORN,
+ GAMEPAD_RESETCAR,
+ GAMEPAD_NONE,
+ NUM_GAMEPAD_LABELS = GAMEPAD_NONE - KM_NONE,
+
+ NUM_LABELS = NUM_MAINMENU_LABELS + NUM_KEYBOARDMOUSE_LABELS + NUM_GAMEPAD_LABELS,
+ NUM_MENU_ITEMS = NUM_MAINMENU_LABELS + NUM_KEYBOARDMOUSE_LABELS*NUM_COLUMNS + NUM_GAMEPAD_LABELS*NUM_COLUMNS
+ };
+
+private:
+ void UpdatePageLabels( eMenuPages page );
+ void SetSingleItemMenuPage( Scrooby::Text** pLabels,
+ int numMenuItems,
+ const char* strPage,
+ char* strGroup = "Menu",
+ char* szLabel = "Label",
+ int attributes = SELECTION_ENABLED | SELECTABLE | VALUES_WRAPPED );
+
+ void SetPageVisiblility( const char* strPage, bool bVisible );
+ int GetVirtualKey( int menuItem );
+ void RemapButton( eControllerColumn column, eControllerType type, int menuItem );
+
+private:
+ CGuiMenu* m_pMenu;
+ Scrooby::Text* m_pMenuLabels[ NUM_MAINMENU_LABELS ];
+ Scrooby::Text* m_pKMLabels[ NUM_COLUMNS ][ NUM_KEYBOARDMOUSE_LABELS ];
+ Scrooby::Text* m_pGamepadLabels[ NUM_COLUMNS ][ NUM_GAMEPAD_LABELS ];
+ Scrooby::Text* m_currentTextLabel;
+
+ eMenuPages m_currentPage;
+ int m_currentControllerID;
+ bool m_bMapInput;
+ bool m_bDisableBack;
+ int m_menuGroupStartIndex[NUM_MENU_PAGES];
+ int m_numControllerGroups;
+};
+
+#endif // GUISCREENCONTROLLER_H
diff --git a/game/code/presentation/gui/frontend/guiscreendisplay.cpp b/game/code/presentation/gui/frontend/guiscreendisplay.cpp
new file mode 100644
index 0000000..6a92fd4
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreendisplay.cpp
@@ -0,0 +1,346 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenDisplay
+//
+// Description: Implementation of the CGuiScreenDisplay class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/06/16 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreendisplay.h>
+#include <presentation/gui/guimenu.h>
+
+#include <data/config/gameconfigmanager.h>
+#include <main/win32platform.h>
+#include <memory/srrmemory.h>
+#include <render/RenderFlow/renderflow.h>
+
+#include <raddebug.hpp> // Foundation
+#include <screen.h>
+#include <page.h>
+#include <group.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+const char* DISPLAY_MENU_ITEMS[] =
+{
+ "Resolution",
+ "ColourDepth",
+ "DisplayMode",
+ "Gamma",
+ "ApplyChanges",
+
+ ""
+};
+
+const float SLIDER_ICON_SCALE = 0.5f;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenDisplay::CGuiScreenDisplay
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenDisplay::CGuiScreenDisplay
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_DISPLAY ),
+ m_pMenu( NULL ),
+ m_changedGamma( false )
+{
+MEMTRACK_PUSH_GROUP( "CGuiScreenDisplay" );
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "Display" );
+ rAssert( pPage != NULL );
+
+ // Create a menu.
+ //
+ m_pMenu = new CGuiMenu( this, NUM_MENU_ITEMS );
+ rAssert( m_pMenu != NULL );
+
+ // Add menu items
+ //
+ char itemName[ 32 ];
+
+ for( int i = 0; i < MENU_ITEM_GAMMA; i++ )
+ {
+ Scrooby::Group* group = pPage->GetGroup( DISPLAY_MENU_ITEMS[ i ] );
+ rAssert( group != NULL );
+
+ sprintf( itemName, "%s_Value", DISPLAY_MENU_ITEMS[ i ] );
+ Scrooby::Text* pTextValue = group->GetText( itemName );
+
+ sprintf( itemName, "%s_ArrowL", DISPLAY_MENU_ITEMS[ i ] );
+ Scrooby::Sprite* pLArrow = group->GetSprite( itemName );
+
+ sprintf( itemName, "%s_ArrowR", DISPLAY_MENU_ITEMS[ i ] );
+ Scrooby::Sprite* pRArrow = group->GetSprite( itemName );
+
+ m_pMenu->AddMenuItem( group->GetText( DISPLAY_MENU_ITEMS[ i ] ),
+ pTextValue,
+ NULL,
+ NULL,
+ pLArrow,
+ pRArrow,
+ SELECTION_ENABLED | VALUES_WRAPPED | TEXT_OUTLINE_ENABLED );
+ }
+
+ // Add the gamma slider
+ Scrooby::Group* pgroup = pPage->GetGroup( "Gamma" );
+ rAssert(pgroup != NULL );
+
+ Scrooby::Text* pText = pgroup->GetText( "Gamma" );
+
+ Scrooby::Group* sliderGroup = pgroup->GetGroup( "Gamma_Slider" );
+ rAssert( sliderGroup != NULL );
+
+ sliderGroup->ResetTransformation();
+
+ m_pMenu->AddMenuItem( pText,
+ NULL,
+ NULL,
+ sliderGroup->GetSprite( "Gamma_Slider" ),
+ NULL,
+ NULL,
+ SELECTION_ENABLED | VALUES_WRAPPED | TEXT_OUTLINE_ENABLED );
+
+ m_pMenu->GetMenuItem( MENU_ITEM_GAMMA )->m_slider.m_type = Slider::HORIZONTAL_SLIDER_ABOUT_CENTER;
+
+ Scrooby::Sprite* soundOnIcon = pgroup->GetSprite( "Gamma_Icon" );
+ soundOnIcon->ScaleAboutCenter( SLIDER_ICON_SCALE );
+
+ // Add the apply changes button
+
+ pgroup = pPage->GetGroup( "Menu" );
+ rAssert( pgroup != NULL );
+
+ m_pMenu->AddMenuItem( pgroup->GetText( "ApplyChanges" ) );
+
+MEMTRACK_POP_GROUP("CGuiScreenDisplay");
+}
+
+
+//===========================================================================
+// CGuiScreenDisplay::~CGuiScreenDisplay
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenDisplay::~CGuiScreenDisplay()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenDisplay::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenDisplay::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ switch( param1 )
+ {
+ case MENU_ITEM_APPLY_CHANGES:
+ {
+ ApplySettings();
+ break;
+ }
+ }
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_VALUE_CHANGED:
+ {
+ rAssert( m_pMenu );
+ GuiMenuItem* currentItem = m_pMenu->GetMenuItem( param1 );
+ rAssert( currentItem );
+
+ switch( param1 )
+ {
+ case MENU_ITEM_GAMMA:
+ {
+ float gamma = 2 * currentItem->m_slider.m_value + 0.5f;
+ GetRenderFlow()->SetGamma( gamma );
+ m_changedGamma = true;
+
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+//===========================================================================
+// CGuiScreenDisplay::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenDisplay::InitIntro()
+{
+ // update settings
+ //
+ Win32Platform* plat = Win32Platform::GetInstance();
+
+ Win32Platform::Resolution res = plat->GetResolution();
+ m_pMenu->SetSelectionValue( MENU_ITEM_RESOLUTION,
+ res );
+
+ int bpp = plat->GetBPP();
+ m_pMenu->SetSelectionValue( MENU_ITEM_COLOUR_DEPTH,
+ bpp == 16 ? 0: 1 );
+
+ bool fullscreen = plat->IsFullscreen();
+ m_pMenu->SetSelectionValue( MENU_ITEM_DISPLAY_MODE,
+ fullscreen ? 1 : 0 );
+
+ GuiMenuItem* menuItem = m_pMenu->GetMenuItem( MENU_ITEM_GAMMA );
+ rAssert( menuItem );
+ menuItem->m_slider.SetValue( ( GetRenderFlow()->GetGamma() - 0.5f ) / 2.0f );
+}
+
+
+//===========================================================================
+// CGuiScreenDisplay::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenDisplay::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenDisplay::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenDisplay::InitOutro()
+{
+ // Save the config if we've changed the gamma settings
+ if( m_changedGamma )
+ {
+ GetGameConfigManager()->SaveConfigFile();
+ m_changedGamma = false;
+ }
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+//===========================================================================
+// CGuiScreenDisplay::ApplySettings
+//===========================================================================
+// Description: Applies the current display settings to teh game.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenDisplay::ApplySettings()
+{
+ // Retrieve the settings.
+ //
+ Win32Platform::Resolution res = static_cast< Win32Platform::Resolution >( m_pMenu->GetSelectionValue( MENU_ITEM_RESOLUTION ) );
+
+ int bpp = m_pMenu->GetSelectionValue( MENU_ITEM_COLOUR_DEPTH ) ? 32: 16;
+
+ bool fullscreen = m_pMenu->GetSelectionValue( MENU_ITEM_DISPLAY_MODE ) == 1;
+
+ // Set the resolution.
+ Win32Platform::GetInstance()->SetResolution( res, bpp, fullscreen );
+
+ // Save the change to the config file.
+ GetGameConfigManager()->SaveConfigFile();
+ m_changedGamma = false;
+}
diff --git a/game/code/presentation/gui/frontend/guiscreendisplay.h b/game/code/presentation/gui/frontend/guiscreendisplay.h
new file mode 100644
index 0000000..8b56a72
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreendisplay.h
@@ -0,0 +1,68 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenDisplay
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/06/16 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENDISPLAY_H
+#define GUISCREENDISPLAY_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenDisplay : public CGuiScreen
+{
+public:
+ CGuiScreenDisplay( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenDisplay();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void ApplySettings();
+
+private:
+ enum eMenuItem
+ {
+ MENU_ITEM_RESOLUTION,
+ MENU_ITEM_COLOUR_DEPTH,
+ MENU_ITEM_DISPLAY_MODE,
+ MENU_ITEM_GAMMA,
+ MENU_ITEM_APPLY_CHANGES,
+
+ NUM_MENU_ITEMS
+ };
+
+ CGuiMenu* m_pMenu;
+ bool m_changedGamma;
+};
+
+#endif // GUISCREENDISPLAY_H
diff --git a/game/code/presentation/gui/frontend/guiscreenloadgame.cpp b/game/code/presentation/gui/frontend/guiscreenloadgame.cpp
new file mode 100644
index 0000000..0e49bc4
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenloadgame.cpp
@@ -0,0 +1,1034 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLoadGame
+//
+// Description: Implementation of the CGuiScreenLoadGame class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenloadgame.h>
+#include <presentation/gui/frontend/guiscreenmainmenu.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/guiscreenmessage.h>
+#include <presentation/gui/guiscreenprompt.h>
+
+#include <data/gamedatamanager.h>
+#include <data/savegameinfo.h>
+#include <data/memcard/memorycardmanager.h>
+#include <memory/srrmemory.h>
+
+#include <raddebug.hpp> // Foundation
+#include <page.h>
+#include <screen.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+#ifdef RAD_XBOX
+ char gGameFileName[NUM_GAME_SLOTS][radFileFilenameMax+1];
+#endif
+
+#ifdef RAD_PS2
+ const unsigned int AUTO_LOAD_MINIMUM_DISPLAY_TIME = 5000; // in msec
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenLoadGame::CGuiScreenLoadGame
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLoadGame::CGuiScreenLoadGame
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent,
+ eGuiWindowID windowID
+)
+: CGuiScreen( pScreen, pParent, windowID ),
+ CGuiScreenLoadSave( pScreen ),
+ m_pMenu( NULL ),
+ m_pFullText( NULL ),
+ m_StatusPromptShown(false)
+
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenLoadGame" );
+ if( windowID == GUI_SCREEN_ID_LOAD_GAME )
+ {
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "GameSlots" );
+ rAssert( pPage );
+
+ // hide full text field
+ m_pFullText = pPage->GetText( "FullMessage" );
+ rAssert( m_pFullText != NULL );
+ m_pFullText->SetVisible(false);
+ m_pFullText->SetTextMode( Scrooby::TEXT_WRAP );
+
+ // Create a menu.
+ //
+ m_pMenu = new(GMA_LEVEL_FE) CGuiMenu( this, NUM_GAME_SLOTS );
+ rAssert( m_pMenu != NULL );
+
+ // Add menu items
+ //
+ for( unsigned int i = 0; i < NUM_GAME_SLOTS; i++ )
+ {
+ char objectName[ 32 ];
+ sprintf( objectName, "Slot%d", i );
+
+ m_pMenu->AddMenuItem( pPage->GetText( objectName ) );
+ }
+ }
+MEMTRACK_POP_GROUP( "CGUIScreenLoadGame" );
+}
+
+
+//===========================================================================
+// CGuiScreenLoadGame::~CGuiScreenLoadGame
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLoadGame::~CGuiScreenLoadGame()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenLoadGame::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLoadGame::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if (message == GUI_MSG_MESSAGE_UPDATE)
+ {
+ if (m_formatState)
+ {
+ m_elapsedFormatTime += param1;
+
+ if (m_elapsedFormatTime > m_minimumFormatTime && m_formatDone)
+ {
+ m_StatusPromptShown = true;
+ if( m_formatResult == Success )
+ {
+ m_guiManager->DisplayPrompt(PROMPT_FORMAT_SUCCESS_GC + PLATFORM_TEXT_INDEX, this, PROMPT_TYPE_CONTINUE); // format success
+ m_formatState = false;
+ }
+ else
+ {
+ m_guiManager->DisplayPrompt(PROMPT_FORMAT_FAIL_GC + PLATFORM_TEXT_INDEX, this, PROMPT_TYPE_CONTINUE); // format fail
+ m_formatState = false;
+ }
+ }
+ }
+ }
+ else if (message == GUI_MSG_PROMPT_UPDATE)
+ {
+ // update so status up to date
+ GetMemoryCardManager()->Update( param1 );
+ if (m_StatusPromptShown==false) { // check for user unplugging memcard if not showing status
+ int currentDrive = GetMemoryCardManager()->GetCurrentDriveIndex();
+ if( !GetMemoryCardManager()->IsCurrentDrivePresent(currentDrive) )
+ ReloadScreen();
+ }
+ }
+ else if( message == GUI_MSG_ON_DISPLAY_MESSAGE )
+ {
+ if (m_operation==LOAD)
+ {
+ // start the load game process
+ //
+ rAssert( m_currentSlot != -1 );
+#ifdef RAD_XBOX
+ GetGameDataManager()->LoadGame( m_currentSlot, this, gGameFileName[m_currentSlot] );
+#else
+ GetGameDataManager()->LoadGame( m_currentSlot, this );
+#endif
+ }
+ else
+ {
+ FormatCurrentDrive();
+ }
+ }
+ else if ( message == GUI_MSG_ERROR_PROMPT_RESPONSE )
+ {
+ this->HandleErrorResponse( static_cast<CGuiMenuPrompt::ePromptResponse>( param2 ) );
+ }
+ else if( message == GUI_MSG_MENU_PROMPT_RESPONSE )
+ {
+ switch( param1 )
+ {
+ case PROMPT_LOAD_CARD_EMPTY_GC:
+ case PROMPT_LOAD_CARD_EMPTY_PS2:
+ case PROMPT_LOAD_CARD_EMPTY_XBOX:
+ case PROMPT_LOAD_CARD_EMPTY_XBOX_HD:
+ this->GotoMemoryCardScreen( true );
+ break;
+
+ case PROMPT_LOAD_DELETE_CORRUPT_GC:
+ case PROMPT_LOAD_DELETE_CORRUPT_PS2:
+ case PROMPT_LOAD_DELETE_CORRUPT_XBOX:
+ case PROMPT_LOAD_DELETE_CORRUPT_XBOX_HD:
+ {
+ if (param2==CGuiMenuPrompt::RESPONSE_NO)
+ {
+ this->ReloadScreen();
+ }
+ else if (param2==CGuiMenuPrompt::RESPONSE_YES)
+ {
+ // get the filename
+ char filename[ radFileFilenameMax + 1 ];
+#ifdef RAD_XBOX
+ strcpy(filename, gGameFileName[m_currentSlot]);
+#else
+ GetGameDataManager()->FormatSavedGameFilename( filename,
+ sizeof( filename ),
+ m_currentSlot );
+#endif
+ radFileError err = GetGameDataManager()->DeleteGame(filename);
+ if (err==Success)
+ m_guiManager->DisplayPrompt(PROMPT_DELETE_CORRUPT_SUCCESS_GC + PLATFORM_TEXT_INDEX, this,
+ PROMPT_TYPE_CONTINUE);
+ else
+ m_guiManager->DisplayPrompt(PROMPT_DELETE_CORRUPT_FAIL_GC + PLATFORM_TEXT_INDEX, this,
+ PROMPT_TYPE_CONTINUE);
+
+ }
+
+ break;
+ }
+ case PROMPT_DELETE_CORRUPT_SUCCESS_GC:
+ case PROMPT_DELETE_CORRUPT_SUCCESS_PS2:
+ case PROMPT_DELETE_CORRUPT_SUCCESS_XBOX:
+
+ case PROMPT_DELETE_CORRUPT_FAIL_GC:
+ case PROMPT_DELETE_CORRUPT_FAIL_PS2:
+ case PROMPT_DELETE_CORRUPT_FAIL_XBOX:
+ this->ReloadScreen();
+ break;
+
+ case PROMPT_FORMAT_CONFIRM2_GC:
+ case PROMPT_FORMAT_CONFIRM2_PS2:
+ case PROMPT_FORMAT_CONFIRM2_XBOX: // really format
+ {
+ if (param2==CGuiMenuPrompt::RESPONSE_YES)
+ {
+ m_operation = FORMAT;
+ m_guiManager->DisplayMessage(CGuiScreenMessage::MSG_ID_FORMATTING_GC + PLATFORM_TEXT_INDEX, this);
+ }
+ else
+ {
+ this->GotoMemoryCardScreen( true );
+ }
+
+ break;
+ }
+ case PROMPT_FORMAT_SUCCESS_GC:
+ case PROMPT_FORMAT_SUCCESS_PS2:
+ case PROMPT_FORMAT_SUCCESS_XBOX: // format ok
+ {
+ this->GotoMemoryCardScreen( true );
+
+ break;
+ }
+ case PROMPT_FORMAT_FAIL_GC:
+ case PROMPT_FORMAT_FAIL_PS2:
+ case PROMPT_FORMAT_FAIL_XBOX: // format fail
+ {
+ GetMemoryCardManager()->ClearCurrentDrive();
+ this->GotoMemoryCardScreen( true );
+
+ break;
+ }
+ case PROMPT_LOAD_CONFIRM_GC:
+ case PROMPT_LOAD_CONFIRM_PS2:
+ case PROMPT_LOAD_CONFIRM_XBOX:
+ {
+ if( param2 == CGuiMenuPrompt::RESPONSE_YES )
+ {
+ this->LoadGame();
+ }
+ else
+ {
+ rAssert( param2 == CGuiMenuPrompt::RESPONSE_NO );
+
+ this->ReloadScreen();
+ }
+
+ break;
+ }
+
+ case PROMPT_LOAD_SUCCESSFUL:
+ {
+ CGuiScreen* promptScreen = (CGuiScreen*)m_guiManager->FindWindowByID( GUI_SCREEN_ID_GENERIC_PROMPT );
+ rAssert( promptScreen );
+ promptScreen->StartTransitionAnimation( 230, 260 );
+
+ // re-init main menu and default to "resume game" selection
+ //
+ CGuiScreenMainMenu* pScreen =
+ static_cast<CGuiScreenMainMenu*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_MAIN_MENU ) );
+ rAssert( pScreen != NULL );
+ pScreen->InitMenu();
+
+ m_pParent->HandleMessage( GUI_MSG_BACK_SCREEN );
+
+ break;
+ }
+
+ default:
+ {
+ // handle normal menu condition, "continue", "retry"
+ this->HandleErrorResponse( static_cast<CGuiMenuPrompt::ePromptResponse>( param2 ) );
+
+ break;
+ }
+ }
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ m_currentSlot = param1; // // param1 = slot
+
+ SaveGameInfo saveGameInfo;
+ bool corrupt;
+ IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
+ bool saveGameExists = GetGameDataManager()->GetSaveGameInfo( currentDrive, m_currentSlot, &saveGameInfo, &corrupt );
+
+ if (corrupt)
+ {
+#ifdef RAD_GAMECUBE
+ int errorMessage = GetErrorMessageIndex( DataCorrupt, ERROR_DURING_LOADING );
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE | ERROR_RESPONSE_RETRY | ERROR_RESPONSE_DELETE );
+ m_operation = LOAD;
+#endif
+#ifdef RAD_PS2
+ rAssertMsg( false, "Corrupted save games should not have been selectable!" );
+#endif
+#ifdef RAD_XBOX
+ // for xbox don't ask to delete just put up a message
+ int errorMessage = GetErrorMessageIndex( DataCorrupt, ERROR_DURING_LOADING );
+ m_guiManager->DisplayErrorPrompt( errorMessage, this, ERROR_RESPONSE_CONTINUE );
+#endif
+ }
+ else
+ {
+ #ifdef RAD_GAMECUBE
+ m_guiManager->DisplayPrompt( PROMPT_LOAD_CONFIRM_GC, this );
+ #endif
+
+ #ifdef RAD_PS2
+ m_guiManager->DisplayPrompt( PROMPT_LOAD_CONFIRM_PS2, this );
+ #endif
+
+ #ifdef RAD_XBOX
+ m_guiManager->DisplayPrompt( PROMPT_LOAD_CONFIRM_XBOX, this );
+ #endif
+
+ #ifdef RAD_WIN32
+ m_guiManager->DisplayPrompt( PROMPT_LOAD_CONFIRM_XBOX, this ); // parallel xbox for now.
+ #endif
+ }
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+#ifdef RAD_XBOX
+ s_forceGotoMemoryCardScreen = true;
+ this->GotoMemoryCardScreen();
+#else
+ this->StartTransitionAnimation( 230, 260 );
+#endif
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+
+ if( m_ID == GUI_SCREEN_ID_LOAD_GAME )
+ {
+ CGuiScreenLoadSave::HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+void
+CGuiScreenLoadGame::OnLoadGameComplete( radFileError errorCode )
+{
+ m_lastError = errorCode;
+ m_StatusPromptShown = true;
+
+ if( errorCode == Success )
+ {
+ m_guiManager->DisplayPrompt( PROMPT_LOAD_SUCCESSFUL, this, PROMPT_TYPE_CONTINUE );
+ }
+ else
+ {
+ int errorMessage = GetErrorMessageIndex( errorCode, ERROR_DURING_LOADING );
+
+#ifdef RAD_GAMECUBE
+ switch( errorCode )
+ {
+ case Success:
+ {
+ rAssert( false );
+ break;
+ }
+ case MediaEncodingErr:
+ case MediaNotFormatted:
+ {
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE | ERROR_RESPONSE_RETRY | ERROR_RESPONSE_FORMAT );
+
+ }
+ case DataCorrupt:
+ {
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE | ERROR_RESPONSE_RETRY | ERROR_RESPONSE_DELETE );
+
+ break;
+ }
+ default:
+ {
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE | ERROR_RESPONSE_RETRY );
+
+ break;
+ }
+ }
+#endif // RAD_GAMECUBE
+
+#ifdef RAD_PS2
+ switch( errorCode )
+ {
+ case Success:
+ {
+ rAssert( false );
+ break;
+ }
+/*
+ case DataCorrupt:
+ {
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_YES | ERROR_RESPONSE_NO );
+
+ break;
+ }
+*/
+ default:
+ {
+ m_guiManager->DisplayErrorPrompt( errorMessage, this, ERROR_RESPONSE_CONTINUE );
+
+ break;
+ }
+ }
+#endif // RAD_PS2
+
+#ifdef RAD_XBOX
+ switch( errorCode )
+ {
+ case Success:
+ {
+ rAssert( false );
+ break;
+ }
+ default:
+ {
+ if (errorCode==DataCorrupt) // no delete corrupt for xbox loading
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE );
+ else
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE );
+
+ break;
+ }
+ }
+#endif // RAD_XBOX
+
+#ifdef RAD_WIN32
+ switch( errorCode )
+ {
+ case Success:
+ {
+ rAssert( false );
+ break;
+ }
+ default:
+ {
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE );
+ break;
+ }
+ }
+#endif // RAD_WIN32
+ }
+}
+
+
+void
+CGuiScreenLoadGame::HandleErrorResponse( CGuiMenuPrompt::ePromptResponse response )
+{
+ switch( response )
+ {
+ case (CGuiMenuPrompt::RESPONSE_CONTINUE):
+ {
+ if( m_operation == LOAD )
+ {
+ this->ReloadScreen();
+ }
+ else if( m_operation == FORMAT )
+ {
+ this->ReloadScreen();
+ }
+ else
+ {
+ this->GotoMemoryCardScreen( true );
+ }
+
+ break;
+ }
+ case (CGuiMenuPrompt::RESPONSE_RETRY):
+ {
+ if( m_operation == LOAD )
+ {
+ this->LoadGame();
+ }
+ else if( m_operation == FORMAT )
+ {
+ m_guiManager->DisplayMessage(CGuiScreenMessage::MSG_ID_FORMATTING_GC + PLATFORM_TEXT_INDEX, this);
+ }
+ else
+ {
+ this->ReloadScreen();
+ }
+
+ break;
+ }
+
+#ifdef RAD_GAMECUBE
+ case (CGuiMenuPrompt::RESPONSE_DELETE):
+ {
+ m_guiManager->DisplayPrompt( PROMPT_LOAD_DELETE_CORRUPT_GC, this );
+
+ break;
+ }
+#endif // RAD_GAMECUBE
+
+#if defined( RAD_GAMECUBE ) || defined( RAD_PS2 )
+ case (CGuiMenuPrompt::RESPONSE_YES):
+ {
+ // YES to delete corrupted file
+ //
+ char filename[ radFileFilenameMax + 1 ];
+#ifdef RAD_XBOX
+ strcpy( filename, gGameFileName[ m_currentSlot ] );
+#else
+ GetGameDataManager()->FormatSavedGameFilename( filename,
+ sizeof( filename ),
+ m_currentSlot );
+#endif
+ radFileError err = GetGameDataManager()->DeleteGame( filename );
+ if( err == Success )
+ {
+ m_guiManager->DisplayPrompt( PROMPT_DELETE_CORRUPT_SUCCESS_GC + PLATFORM_TEXT_INDEX,
+ this, PROMPT_TYPE_CONTINUE );
+ }
+ else
+ {
+ m_guiManager->DisplayPrompt( PROMPT_DELETE_CORRUPT_FAIL_GC + PLATFORM_TEXT_INDEX,
+ this, PROMPT_TYPE_CONTINUE );
+ }
+
+ break;
+ }
+ case (CGuiMenuPrompt::RESPONSE_NO):
+ {
+ // NO to delete corrupted file
+ //
+ this->ReloadScreen();
+
+ break;
+ }
+#endif // RAD_GAMECUBE || RAD_PS2
+
+ case (CGuiMenuPrompt::RESPONSE_FORMAT_GC):
+ case (CGuiMenuPrompt::RESPONSE_FORMAT_XBOX):
+ case (CGuiMenuPrompt::RESPONSE_FORMAT_PS2):
+ {
+ m_guiManager->DisplayPrompt(PROMPT_FORMAT_CONFIRM2_GC + PLATFORM_TEXT_INDEX,this);
+
+ break;
+ }
+ default:
+ {
+ rTunePrintf( "*** WARNING: Unhandled response for error [%d]!\n", m_lastError );
+ rAssert( false );
+
+ this->ReloadScreen();
+
+ break;
+ }
+ }
+
+ CGuiScreenLoadSave::HandleErrorResponse( response );
+}
+
+//===========================================================================
+// CGuiScreenLoadGame::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLoadGame::InitIntro()
+{
+ bool unformatted = false;
+ bool file_corrupt = false;
+ SaveGameInfo saveGameInfo;
+ m_StatusPromptShown = false;
+ m_operation = SCREEN_OP_IDLE;
+
+ m_pFullText->SetVisible(false);
+
+ IRadDrive::MediaInfo::MediaState mediaState;
+ if( s_forceGotoMemoryCardScreen || !GetMemoryCardManager()->IsCurrentDriveReady( true, &unformatted, &mediaState ) )
+ {
+ if (unformatted && !s_forceGotoMemoryCardScreen)
+ {
+#ifdef RAD_GAMECUBE
+ int errorMessage = GetErrorMessageIndex( mediaState );
+ m_guiManager->DisplayErrorPrompt( errorMessage,
+ this,
+ ERROR_RESPONSE_CONTINUE | ERROR_RESPONSE_RETRY | ERROR_RESPONSE_FORMAT );
+
+ m_numTransitionsPending = -1; // disable all transitions
+
+ return;
+#endif
+ }
+ else
+ {
+ this->GotoMemoryCardScreen();
+ m_numTransitionsPending = -1; // disable all transitions
+
+ return;
+ }
+ }
+
+ this->UpdateCurrentMemoryDevice();
+
+ rAssert( m_pMenu );
+ m_pMenu->Reset();
+
+ IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
+
+ radDate mostRecentTimestamp;
+ mostRecentTimestamp.m_Year = 0;
+ bool has_savegame = false;
+ bool has_4_saves = false;
+
+ // update all save game slots display info
+ //
+ for( unsigned int i = 0; i < NUM_GAME_SLOTS; i++ )
+ {
+ bool saveGameExists = GetGameDataManager()->GetSaveGameInfo( currentDrive, i, &saveGameInfo, &file_corrupt );
+// saveGameExists = saveGameExists && saveGameInfo.CheckData();
+
+ Scrooby::Text* slotText = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( i )->GetItem() );
+ rAssert( slotText != NULL );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+
+ if( saveGameExists )
+ {
+ if (i == NUM_GAME_SLOTS-1)
+ {
+ has_4_saves = true;
+ }
+ has_savegame = true;
+ if (file_corrupt)
+ {
+ UnicodeString corruptSlot;
+#ifdef RAD_XBOX
+ corruptSlot.ReadUnicode( GetTextBibleString( "CORRUPT_SLOT_(XBOX)" ) );
+#endif
+#ifdef RAD_WIN32
+ corruptSlot.ReadUnicode( GetTextBibleString( "CORRUPT_SLOT_(XBOX)" ) );
+#endif
+#ifdef RAD_PS2
+ corruptSlot.ReadUnicode( GetTextBibleString( "CORRUPT_SLOT_(PS2)" ) );
+#endif
+#ifdef RAD_GAMECUBE
+ corruptSlot.ReadUnicode( GetTextBibleString( "CORRUPT_SLOT_(GC)" ) );
+#endif
+ slotText->SetString(0,corruptSlot);
+ }
+ else
+ {
+ #ifdef RAD_XBOX
+ strcpy(gGameFileName[i], saveGameInfo.m_displayFilename); // cache the slot filename
+ #endif
+ slotText->SetString( 0, saveGameInfo.m_displayFilename );
+ }
+
+ // default to slot with most recent saved game file
+ //
+ const SaveGameInfoData* pData = saveGameInfo.GetData();
+ rAssert( pData != NULL );
+ if( SaveGameInfo::CompareTimeStamps( pData->m_timeStamp, mostRecentTimestamp ) > 0 )
+ {
+ memcpy( &mostRecentTimestamp, &pData->m_timeStamp, sizeof( radDate ) );
+
+ m_pMenu->Reset( i );
+ }
+ }
+ else
+ {
+ UnicodeString emptySlot;
+ emptySlot.ReadUnicode( GetTextBibleString( "EMPTY_SLOT" ) );
+ slotText->SetString( 0, emptySlot );
+ }
+ HeapMgr()->PopHeap(GMA_LEVEL_FE);
+
+ // enable slot selection only if save game exists
+ //
+#ifdef RAD_PS2
+ m_pMenu->SetMenuItemEnabled( i, saveGameExists && !file_corrupt );
+#else
+ m_pMenu->SetMenuItemEnabled( i, saveGameExists );
+#endif // RAD_PS2
+ }
+
+ if (has_savegame==false) // no file in card
+ {
+ int prompt_id = PROMPT_LOAD_CARD_EMPTY_GC+PLATFORM_TEXT_INDEX;
+#ifdef RAD_XBOX
+ if (GetMemoryCardManager()->GetCurrentDriveIndex()==0)
+ prompt_id = PROMPT_LOAD_CARD_EMPTY_XBOX_HD;
+#endif
+ m_guiManager->DisplayPrompt(prompt_id,this,PROMPT_TYPE_CONTINUE);
+ m_numTransitionsPending = -1; // disable all transitions
+ }
+ else
+ {
+#ifdef RAD_XBOX
+ // check if there are more than 4 files on hd
+ if ( has_4_saves
+ && GetGameDataManager()->GetSaveGameInfo( currentDrive, NUM_GAME_SLOTS, &saveGameInfo, &file_corrupt )
+ )
+ {
+ m_pFullText->SetIndex( 8 );
+ m_pFullText->SetVisible(true);
+
+ }
+#endif
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+ }
+}
+
+//===========================================================================
+// CGuiScreenLoadGame::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLoadGame::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenLoadGame::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLoadGame::InitOutro()
+{
+}
+
+void
+CGuiScreenLoadGame::GotoMemoryCardScreen( bool isFromPrompt )
+{
+#ifdef RAD_WIN32
+ if( isFromPrompt )
+ {
+ CGuiScreen* pScreen = static_cast<CGuiScreen*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_GENERIC_PROMPT ) );
+ rAssert( pScreen != NULL );
+ pScreen->StartTransitionAnimation( 230, 260 );
+ }
+ else
+ {
+ this->StartTransitionAnimation( 230, 260 );
+ }
+
+ m_pParent->HandleMessage( GUI_MSG_BACK_SCREEN );
+#else
+ if( isFromPrompt )
+ {
+ s_forceGotoMemoryCardScreen = true;
+ this->ReloadScreen();
+ }
+ else
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_MEMORY_CARD );
+ }
+#endif // RAD_WIN32
+}
+
+void CGuiScreenLoadGame::LoadGame()
+{
+ m_operation = LOAD;
+
+#ifdef RAD_GAMECUBE
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_LOADING_GAME_GC, this );
+#endif
+
+#ifdef RAD_PS2
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_LOADING_GAME_PS2, this );
+#endif
+
+#ifdef RAD_XBOX
+ if( m_currentDriveIndex == 0 ) // xbox hard disk
+ {
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_LOADING_GAME_XBOX_HD, this );
+ }
+ else // xbox memory unit
+ {
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_LOADING_GAME_XBOX, this );
+ }
+#endif
+
+#ifdef RAD_WIN32
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_LOADING_GAME_PC, this );
+#endif
+}
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+
+//===========================================================================
+// Public Member Functions (for CGuiScreenAutoLoad)
+//===========================================================================
+
+int CGuiScreenAutoLoad::s_autoLoadGameSlot = -1;
+
+CGuiScreenAutoLoad::CGuiScreenAutoLoad( Scrooby::Screen* pScreen, CGuiEntity* pParent )
+: CGuiScreenLoadGame( pScreen, pParent, GUI_SCREEN_ID_AUTO_LOAD )
+{
+}
+
+CGuiScreenAutoLoad::~CGuiScreenAutoLoad()
+{
+}
+
+void
+CGuiScreenAutoLoad::OnLoadGameComplete( radFileError errorCode )
+{
+ GetGameDataManager()->RestoreDefaultMinimumLoadSaveTime();
+
+ if( errorCode == Success )
+ {
+ m_pParent->HandleMessage( GUI_MSG_MEMCARD_CHECK_COMPLETED );
+ }
+ else
+ {
+ CGuiScreenLoadGame::OnLoadGameComplete( errorCode );
+ }
+}
+
+void
+CGuiScreenAutoLoad::InitIntro()
+{
+}
+
+void
+CGuiScreenAutoLoad::InitRunning()
+{
+ m_currentDriveIndex = GetMemoryCardManager()->GetCurrentDriveIndex();
+ rAssert( m_currentDriveIndex != -1 );
+
+ IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
+ if( currentDrive != NULL )
+ {
+ rReleasePrintf( "Auto-loading most recent saved game from drive %s\n",
+ currentDrive->GetDriveName() );
+ }
+
+ rAssert( s_autoLoadGameSlot != -1 );
+ m_currentSlot = s_autoLoadGameSlot;
+#ifdef RAD_XBOX
+ // for xbox we need to get the filename from slot information first before loading
+ SaveGameInfo saveGameInfo;
+ gGameFileName[m_currentSlot][0] = 0; // initialize to empty string
+ rAssert(m_currentSlot < NUM_GAME_SLOTS);
+ bool saveGameExists = GetGameDataManager()->GetSaveGameInfo( currentDrive, m_currentSlot, &saveGameInfo );
+ if( saveGameExists ) // game filename is cached in gGameFileName global
+ {
+ strcpy( gGameFileName[m_currentSlot], saveGameInfo.m_displayFilename);
+ }
+
+#endif
+
+ this->LoadGame();
+}
+
+void
+CGuiScreenAutoLoad::InitOutro()
+{
+}
+
+void
+CGuiScreenAutoLoad::HandleErrorResponse( CGuiMenuPrompt::ePromptResponse response )
+{
+ switch( response )
+ {
+ case (CGuiMenuPrompt::RESPONSE_CONTINUE):
+ {
+ m_pParent->HandleMessage( GUI_MSG_MEMCARD_CHECK_COMPLETED );
+
+ break;
+ }
+ case (CGuiMenuPrompt::RESPONSE_RETRY):
+ {
+ this->LoadGame();
+
+ break;
+ }
+ default:
+ {
+ rTunePrintf( "*** WARNING: Unhandled response for error [%d]!\n", m_lastError );
+ rAssert( false );
+
+ m_pParent->HandleMessage( GUI_MSG_MEMCARD_CHECK_COMPLETED );
+
+ break;
+ }
+ }
+
+ CGuiScreenLoadSave::HandleErrorResponse( response );
+}
+
+void
+CGuiScreenAutoLoad::LoadGame()
+{
+ m_operation = LOAD;
+
+#ifdef RAD_GAMECUBE
+ // TC: GC does not require an auto-loading screen
+ //
+// m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_AUTO_LOADING_GAME_GC, this );
+ GetGameDataManager()->LoadGame( m_currentSlot, this );
+ GetGameDataManager()->SetMinimumLoadSaveTime( 0 );
+#endif
+
+#ifdef RAD_PS2
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_AUTO_LOADING_GAME_PS2, this );
+ GetGameDataManager()->SetMinimumLoadSaveTime( AUTO_LOAD_MINIMUM_DISPLAY_TIME );
+#endif
+
+#ifdef RAD_XBOX
+ if( m_currentDriveIndex == 0 ) // xbox hard disk
+ {
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_AUTO_LOADING_GAME_XBOX_HD, this );
+ }
+ else // xbox memory unit
+ {
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_AUTO_LOADING_GAME_XBOX, this );
+ }
+#endif
+
+#ifdef RAD_WIN32
+ rAssert( m_currentDriveIndex == 0 );
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_AUTO_LOADING_GAME_PC, this );
+#endif
+}
+
diff --git a/game/code/presentation/gui/frontend/guiscreenloadgame.h b/game/code/presentation/gui/frontend/guiscreenloadgame.h
new file mode 100644
index 0000000..f7902f6
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenloadgame.h
@@ -0,0 +1,92 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLoadGame
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/30 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENLOADGAME_H
+#define GUISCREENLOADGAME_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/guiscreenmemorycard.h>
+#include <data/gamedatamanager.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenLoadGame : public CGuiScreen,
+ public CGuiScreenLoadSave,
+ public GameDataLoadCallback
+
+{
+public:
+ CGuiScreenLoadGame( Scrooby::Screen* pScreen, CGuiEntity* pParent,
+ eGuiWindowID windowID = GUI_SCREEN_ID_LOAD_GAME );
+ virtual ~CGuiScreenLoadGame();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ // Implements GameDataLoadCallback
+ //
+ virtual void OnLoadGameComplete( radFileError errorCode );
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ void GotoMemoryCardScreen( bool isFromPrompt = false );
+ virtual void HandleErrorResponse( CGuiMenuPrompt::ePromptResponse response );
+ virtual void LoadGame();
+
+private:
+ CGuiMenu* m_pMenu;
+ Scrooby::Text* m_pFullText;
+ bool m_StatusPromptShown;
+};
+
+class CGuiScreenAutoLoad : public CGuiScreenLoadGame
+{
+public:
+ CGuiScreenAutoLoad( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenAutoLoad();
+
+ virtual void OnLoadGameComplete( radFileError errorCode );
+
+ static void SetGameSlot( int slot ) { s_autoLoadGameSlot = slot; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ virtual void HandleErrorResponse( CGuiMenuPrompt::ePromptResponse response );
+ virtual void LoadGame();
+
+private:
+ static int s_autoLoadGameSlot;
+
+};
+
+#endif // GUISCREENLOADGAME_H
diff --git a/game/code/presentation/gui/frontend/guiscreenmainmenu.cpp b/game/code/presentation/gui/frontend/guiscreenmainmenu.cpp
new file mode 100644
index 0000000..405ed42
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenmainmenu.cpp
@@ -0,0 +1,1649 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMainMenu
+//
+// Description: Implementation of the CGuiScreenMainMenu class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/11/20 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenmainmenu.h>
+#include <presentation/gui/frontend/guiscreenplaymovie.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiscreenmemorycard.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guiscreenprompt.h>
+
+#include <constants/movienames.h>
+#include <data/gamedatamanager.h>
+#include <data/persistentworldmanager.h>
+#include <events/eventmanager.h>
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/rewards/rewardsmanager.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/renderlayer.h>
+
+#include <p3d/view.hpp>
+
+#include <raddebug.hpp> // Foundation
+#include <radtime.hpp>
+#include <radkey.hpp>
+#include <radmath/random.hpp>
+#include <p3d/anim/multicontroller.hpp>
+
+#include <stdlib.h>
+#include <group.h>
+#include <layer.h>
+#include <page.h>
+#include <pure3dobject.h>
+#include <screen.h>
+#include <text.h>
+
+#include <string.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//#define PLAY_GAGS_SEQUENTIALLY
+//#define PLAY_ONLY_HOMER_GAGS
+
+const float FIRST_TIME_FADE_IN_TIME = 500.0f;
+
+const unsigned int MIN_GAGS_CYCLE_TIME = 15; // in seconds
+const unsigned int MAX_GAGS_CYCLE_TIME = 30; // in seconds
+const unsigned int GAGS_RANDOM_NUMBER_MODULO = 97;
+
+const tColour FE_BLUE_SKY_COLOUR( 128, 255, 255 );
+
+struct HomerGagAnimation
+{
+ float startFrame;
+ float endFrame;
+};
+
+HomerGagAnimation HOMER_GAG_ANIMATION[] =
+{
+ { 0.0f, 75.0f },
+ { 75.0f, 145.0f },
+ { 145.0f, 215.0f },
+ { 215.0f, 355.0f },
+ { 355.0f, 475.0f },
+
+ { -1.0f, -1.0f } // dummy terminator
+};
+
+int NUM_HOMER_GAG_ANIMATIONS =
+ sizeof( HOMER_GAG_ANIMATION ) / sizeof( HOMER_GAG_ANIMATION[ 0 ] ) - 1;
+
+const radKey32 FE_GAGS[] =
+{
+ ::radMakeKey32( "FE_Gag_Homer" ),
+ ::radMakeKey32( "FE_Gag_Grandpa" ),
+ ::radMakeKey32( "FE_Gag_Moleman" ),
+ ::radMakeKey32( "FE_Gag_Frink" ),
+ ::radMakeKey32( "FE_Gag_Barney" ),
+ ::radMakeKey32( "FE_Gag_Nick" ),
+ ::radMakeKey32( "FE_Gag_Snake" ),
+ ::radMakeKey32( "FE_Gag_Maggie" ),
+
+ ::radMakeKey32( "FE_Gag_UNKNOWN" )
+};
+
+const radKey32 FE_GAGS_FOR_HOMER[] =
+{
+ ::radMakeKey32( "FE_Gag_Homer_ScratchHead" ),
+ ::radMakeKey32( "FE_Gag_Homer_ScratchBum" ),
+ ::radMakeKey32( "FE_Gag_Homer_Yawn" ),
+ ::radMakeKey32( "FE_Gag_Homer_Nightmare" ),
+ ::radMakeKey32( "FE_Gag_Homer_Stretch" ),
+
+ ::radMakeKey32( "FE_Gag_UNKNOWN" )
+};
+
+enum eMainMenuItem
+{
+ MENU_ITEM_MAIN_MENU,
+/*
+ MENU_ITEM_NEW_GAME,
+ MENU_ITEM_RESUME_GAME,
+ MENU_ITEM_LOAD_GAME,
+ MENU_ITEM_COLLECTIBLE_CARDS,
+ MENU_ITEM_OPTIONS,
+*/
+
+#ifdef SRR2_LEVEL_SELECTION
+ MENU_ITEM_LEVEL,
+#endif
+
+ NUM_MAIN_MENU_ITEMS
+};
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMainMenu::CGuiScreenMainMenu
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMainMenu::CGuiScreenMainMenu
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+:
+ CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_MAIN_MENU ),
+ m_pMenu( NULL ),
+ m_gags( NULL ),
+ m_currentGagIndex( -1 ),
+ m_nextGagIndex( -1 ),
+ m_nextHomerGagIndex( -1 ),
+ m_gagsElapsedTime( 0 ),
+ m_gagsCycleTime( 1000 * MIN_GAGS_CYCLE_TIME ),
+ m_homer( NULL ),
+ m_raceCar( NULL ),
+ m_homerIdleAnim( NULL ),
+ m_tvFrame( NULL )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenMainMenu" );
+ memset( m_glowingItems, 0, sizeof( m_glowingItems ) );
+
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "MainMenu" );
+ rAssert( pPage != NULL );
+
+/*
+ // get accept button icon
+ //
+ if( m_buttonIcons[ BUTTON_ICON_ACCEPT ] == NULL )
+ {
+ m_buttonIcons[ BUTTON_ICON_ACCEPT ] = pPage->GetGroup( "AcceptLabel" );
+ rAssert( m_buttonIcons[ BUTTON_ICON_ACCEPT ] != NULL );
+ }
+*/
+
+ // Create a menu.
+ //
+ m_pMenu = new CGuiMenu( this, NUM_MAIN_MENU_ITEMS, GUI_TEXT_MENU );
+ rAssert( m_pMenu != NULL );
+
+ // Add menu items
+ //
+ Scrooby::Text* otherMainMenu = NULL;
+
+#ifdef RAD_WIN32
+ m_pMenu->AddMenuItem( pPage->GetText( "MainMenu_PC" ),
+ pPage->GetText( "MainMenu_PC" ),
+ NULL,
+ NULL,
+ pPage->GetSprite( "MainMenu_LArrow" ),
+ pPage->GetSprite( "MainMenu_RArrow" ) );
+
+ otherMainMenu = pPage->GetText( "MainMenu" );
+#else
+ m_pMenu->AddMenuItem( pPage->GetText( "MainMenu" ),
+ pPage->GetText( "MainMenu" ),
+ NULL,
+ NULL,
+ pPage->GetSprite( "MainMenu_LArrow" ),
+ pPage->GetSprite( "MainMenu_RArrow" ) );
+
+ otherMainMenu = pPage->GetText( "MainMenu_PC" );
+#endif
+
+ if( otherMainMenu != NULL )
+ {
+ otherMainMenu->SetVisible( false );
+ }
+
+ // wrap main menu text
+ //
+ Scrooby::Text* mainMenuText = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( MENU_ITEM_MAIN_MENU )->GetItem() );
+ rAssert( mainMenuText != NULL );
+ mainMenuText->SetTextMode( Scrooby::TEXT_WRAP );
+
+#ifdef RAD_DEMO
+ m_pMenu->SetSelectionValueCount( MENU_ITEM_MAIN_MENU, NUM_MAIN_MENU_SELECTIONS );
+ m_pMenu->SetSelectionValue( MENU_ITEM_MAIN_MENU, MAIN_MENU_PLAY_LEVEL_2 );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+ mainMenuText->SetString( MAIN_MENU_PLAY_LEVEL_2, "PLAY LEVEL 2" ); // TC: [TODO] localization??
+ mainMenuText->SetString( MAIN_MENU_PLAY_LEVEL_7, "PLAY LEVEL 7" ); // TC: [TODO] localization??
+ HeapMgr()->PopHeap( GMA_LEVEL_FE );
+#endif
+
+ // scale up main menu text a bit
+ //
+// m_pMenu->GetMenuItem( MENU_ITEM_MAIN_MENU )->GetItem()->ScaleAboutCenter( 1.2f );
+
+ Scrooby::Page* levelPage = m_pScroobyScreen->GetPage( "Level" );
+ if( levelPage != NULL )
+ {
+#ifdef SRR2_LEVEL_SELECTION
+ m_pMenu->AddMenuItem( levelPage->GetText( "Level" ),
+ levelPage->GetText( "Level" ),
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ SELECTION_ENABLED | VALUES_WRAPPED | TEXT_OUTLINE_ENABLED );
+
+ #ifndef RAD_DEBUG
+ // hide level/car selection menu by default
+ // in Tune and Release builds
+ //
+ this->ToggleLevelMenu();
+ #endif
+#else
+ Scrooby::Group* levelMenu = levelPage->GetGroup( "Menu" );
+ rAssert( levelMenu != NULL );
+ levelMenu->SetVisible( false );
+#endif
+ }
+
+ // Retrieve drawing elements from 3dFE page
+ //
+ pPage = m_pScroobyScreen->GetPage( "3dFE" );
+ rAssert( pPage != NULL );
+ m_homer = pPage->GetPure3dObject( "Homer" );
+ m_raceCar = pPage->GetPure3dObject( "RaceCar" );
+
+ // get glowing items
+ //
+#ifdef RAD_DEMO
+ m_glowingItems[ MAIN_MENU_PLAY_LEVEL_2 ] = pPage->GetPure3dObject( "Glow4" );
+ m_glowingItems[ MAIN_MENU_PLAY_LEVEL_7 ] = pPage->GetPure3dObject( "Glow4" );
+ m_glowingItems[ MAIN_MENU_OPTIONS ] = pPage->GetPure3dObject( "Glow2" );
+
+ Scrooby::Pure3dObject* glowingItem = NULL;
+
+ glowingItem = pPage->GetPure3dObject( "Glow0" );
+ rAssert( glowingItem != NULL );
+ glowingItem->SetVisible( false );
+
+ glowingItem = pPage->GetPure3dObject( "Glow1" );
+ rAssert( glowingItem != NULL );
+ glowingItem->SetVisible( false );
+
+ glowingItem = pPage->GetPure3dObject( "Glow3" );
+ rAssert( glowingItem != NULL );
+ glowingItem->SetVisible( false );
+
+ glowingItem = pPage->GetPure3dObject( "Glow5" );
+ rAssert( glowingItem != NULL );
+ glowingItem->SetVisible( false );
+#else
+ for( int i = 0; i < NUM_MAIN_MENU_SELECTIONS; i++ )
+ {
+ char name[ 16 ];
+ sprintf( name, "Glow%d", i );
+ m_glowingItems[ i ] = pPage->GetPure3dObject( name );
+ }
+#endif
+
+ this->TurnOnGlowItems( 0 ); // all off, by default
+
+ // 3D FE Gags
+ //
+ m_gags = m_pScroobyScreen->GetPage( "3dFEGags" );
+ rAssert( m_gags );
+
+ // Retrieve drawing elements from TVFrame page
+ //
+ pPage = m_pScroobyScreen->GetPage( "TVFrame" );
+ rAssert( pPage );
+ m_tvFrame = pPage->GetLayer( "TVFrame" );
+
+ // correct TV frame proportion and size on GC and PS2 and pc
+ //
+ Scrooby::Sprite* tvFrame = pPage->GetSprite( "TVFrame" );
+ if( tvFrame != NULL )
+ {
+ tvFrame->ResetTransformation();
+
+#ifdef RAD_GAMECUBE
+ tvFrame->ScaleAboutCenter( 1.1f, 1.0f, 1.0f );
+#endif
+
+#ifdef RAD_PS2
+ tvFrame->ScaleAboutCenter( 1.07f );
+#endif
+
+#ifdef RAD_WIN32
+ tvFrame->ScaleAboutCenter( 1.03f );
+#endif
+ }
+MEMTRACK_POP_GROUP( "CGUIScreenMainMenu" );
+}
+
+
+//===========================================================================
+// CGuiScreenMainMenu::~CGuiScreenMainMenu
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMainMenu::~CGuiScreenMainMenu()
+{
+ if( m_nextGagIndex == -1 )
+ {
+ // stop any FE gag dialog that was triggered and may still be playing
+ //
+ GetEventManager()->TriggerEvent( EVENT_FE_GAG_STOP );
+ }
+
+ if( m_homerIdleAnim != NULL )
+ {
+ m_homerIdleAnim->Release();
+ m_homerIdleAnim = NULL;
+ }
+
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+
+ // restore black clear colour
+ //
+ GetRenderManager()->mpLayer(RenderEnums::GUI)->pView( 0 )->SetClearColour( tColour( 0, 0, 0 ) );
+}
+
+
+//===========================================================================
+// CGuiScreenMainMenu::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMainMenu::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( message == GUI_MSG_MENU_PROMPT_RESPONSE )
+ {
+#ifndef RAD_WIN32
+ rAssert( param1 == PROMPT_CONFIRM_NEW_GAME );
+#endif
+
+ if( param1 == PROMPT_CONFIRM_NEW_GAME )
+ {
+ switch( param2 )
+ {
+ case CGuiMenuPrompt::RESPONSE_YES:
+ {
+#ifdef SRR2_LEVEL_SELECTION
+ int level = m_pMenu->GetSelectionValue( MENU_ITEM_LEVEL );
+ this->OnNewGameSelected( static_cast<RenderEnums::LevelEnum>( level ) );
+#else
+ this->OnNewGameSelected();
+#endif // SRR2_LEVEL_SELECTION
+
+ CGuiScreen* pScreen = static_cast<CGuiScreen*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_GENERIC_PROMPT ) );
+ rAssert( pScreen != NULL );
+ pScreen->StartTransitionAnimation( 0, 53 );
+
+ break;
+ }
+ case CGuiMenuPrompt::RESPONSE_NO:
+ {
+ this->ReloadScreen();
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Invalid prompt response!" );
+
+ break;
+ }
+ }
+ }
+#ifdef RAD_WIN32
+ else if( param1 == PROMPT_CONFIRM_QUIT )
+ {
+ switch( param2 )
+ {
+ case (CGuiMenuPrompt::RESPONSE_YES):
+ {
+ // send message to front-end manager to quit front-end and
+ // exit the game.
+ //
+ m_pParent->HandleMessage( GUI_MSG_QUIT_GAME );
+
+ break;
+ }
+
+ case (CGuiMenuPrompt::RESPONSE_NO):
+ {
+ this->ReloadScreen();
+
+ break;
+ }
+
+ default:
+ {
+ rAssertMsg( 0, "WARNING: *** Invalid prompt response!\n" );
+
+ break;
+ }
+ }
+ }
+#endif
+ }
+
+ if( m_firstTimeEntered &&
+ message == GUI_MSG_WINDOW_ENTER )
+ {
+ if( CGuiScreenIntroTransition::s_introTransitionPlayed )
+ {
+ this->SetFadingEnabled( false );
+ }
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ this->UpdateRunning( param1 );
+
+ break;
+ }
+#ifdef SRR2_LEVEL_SELECTION
+ // show/hide level and car selection menu (for Tune and Release)
+ //
+ case GUI_MSG_CONTROLLER_L1:
+ case GUI_MSG_CONTROLLER_R1:
+ {
+ this->ToggleLevelMenu();
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_START:
+ {
+ if( m_pMenu->GetSelection() == MENU_ITEM_LEVEL )
+ {
+ rAssert( m_pMenu != NULL );
+ m_pMenu->Reset();
+ m_pMenu->SetSelectionValue( 0, MAIN_MENU_NEW_GAME );
+ }
+
+ break;
+ }
+#endif
+ case GUI_MSG_MENU_SELECTION_VALUE_CHANGED:
+ {
+ if( param1 == MENU_ITEM_MAIN_MENU )
+ {
+ // turn on new glow item
+ //
+ this->TurnOnGlowItems( (1 << param2) );
+
+#ifndef RAD_DEMO
+ if( param2 == MAIN_MENU_MINI_GAME ) // special case for mini-game
+ {
+ if( !GetCharacterSheetManager()->IsMiniGameUnlocked() &&
+ !CommandLineOptions::Get( CLO_SKIP_FE ) )
+ {
+ // don't turn on glowing track
+ //
+ this->TurnOnGlowItems( 0 );
+
+ // grey out selection
+ //
+ m_pMenu->GetMenuItem( MENU_ITEM_MAIN_MENU )->GetItemValue()->SetColour( tColour( 128, 128, 128 ) );
+
+ // hide accept button icon
+ //
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, false );
+#ifdef RAD_WIN32
+ GetInputManager()->GetFEMouse()->SetClickable( false );
+#endif
+
+ }
+ }
+ else
+ {
+ // restore menu selection colour and accept button icon
+ //
+ tColour menuHighlightColour = m_pMenu->GetHighlightColour();
+ m_pMenu->GetMenuItem( MENU_ITEM_MAIN_MENU )->GetItemValue()->SetColour( menuHighlightColour );
+
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+#ifdef RAD_WIN32
+ GetInputManager()->GetFEMouse()->SetClickable( true );
+#endif
+ }
+#endif // !RAD_DEMO
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ if( !this->IsButtonVisible( BUTTON_ICON_ACCEPT ) )
+ {
+ return;
+ }
+#ifdef RAD_DEMO
+ if( m_pMenu->GetSelectionValue( MENU_ITEM_MAIN_MENU ) == MAIN_MENU_PLAY_LEVEL_2 ||
+ m_pMenu->GetSelectionValue( MENU_ITEM_MAIN_MENU ) == MAIN_MENU_PLAY_LEVEL_7 )
+#else
+ if( m_pMenu->GetSelectionValue( MENU_ITEM_MAIN_MENU ) == MAIN_MENU_NEW_GAME ||
+ m_pMenu->GetSelectionValue( MENU_ITEM_MAIN_MENU ) == MAIN_MENU_RESUME_GAME )
+#endif
+ {
+ GetInputManager()->RegisterControllerID( 0, param1 );
+ GetGuiSystem()->SetPrimaryController(param1);
+ }
+
+#ifndef RAD_DEMO
+ if( m_pMenu->GetSelectionValue( MENU_ITEM_MAIN_MENU ) == MAIN_MENU_MINI_GAME )
+ {
+ GetGuiSystem()->SetPrimaryController(param1);
+ }
+#endif // !RAD_DEMO
+
+ break;
+ }
+#ifdef RAD_WIN32
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ this->OnQuitGameSelected();
+
+ break;
+ }
+#endif
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ rAssert( param1 == MENU_ITEM_MAIN_MENU );
+ rAssert( m_pMenu != NULL );
+ int menuSelection = m_pMenu->GetSelectionValue( param1 );
+
+ switch( menuSelection )
+ {
+#ifdef RAD_DEMO
+ case MAIN_MENU_PLAY_LEVEL_2:
+ {
+ this->OnNewGameSelected( RenderEnums::L2, RenderEnums::M1 );
+
+ break;
+ }
+ case MAIN_MENU_PLAY_LEVEL_7:
+ {
+ this->OnNewGameSelected( RenderEnums::L7, RenderEnums::M1 );
+
+ break;
+ }
+#else
+ case MAIN_MENU_NEW_GAME:
+ {
+ if( GetGameDataManager()->IsGameLoaded() )
+ {
+ // prompt user to confirm first
+ //
+ m_guiManager->DisplayPrompt( PROMPT_CONFIRM_NEW_GAME, this );
+ }
+ else
+ {
+#ifdef SRR2_LEVEL_SELECTION
+ int level = m_pMenu->GetSelectionValue( MENU_ITEM_LEVEL );
+ this->OnNewGameSelected( static_cast<RenderEnums::LevelEnum>( level ) );
+#else
+ this->OnNewGameSelected();
+#endif // SRR2_LEVEL_SELECTION
+ }
+
+ break;
+ }
+ case MAIN_MENU_RESUME_GAME:
+ {
+ this->OnResumeGameSelected();
+
+ break;
+ }
+ case MAIN_MENU_MINI_GAME:
+ {
+ this->OnMiniGameSelected();
+
+ break;
+ }
+ case MAIN_MENU_LOAD_GAME:
+ {
+#ifdef RAD_XBOX
+ // Xbox TCR Requirement: always prompt user to select memory
+ // device before loading/saving
+ //
+ CGuiScreenLoadSave::s_forceGotoMemoryCardScreen = true;
+#endif // RAD_XBOX
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_LOAD_GAME );
+
+ this->StartTransitionAnimation( 200, 230 );
+
+ break;
+ }
+ case MAIN_MENU_CARD_GALLERY:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_SCRAP_BOOK );
+
+ this->StartTransitionAnimation( 320, 350 );
+
+ break;
+ }
+#endif // RAD_DEMO
+ case MAIN_MENU_OPTIONS:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_OPTIONS );
+
+ this->StartTransitionAnimation( 80, 110 );
+
+ break;
+ }
+#ifdef RAD_WIN32
+ case MAIN_MENU_QUIT_GAME:
+ {
+ this->OnQuitGameSelected();
+
+ break;
+ }
+#endif
+ default:
+ {
+ rAssertMsg( false, "Invalid main menu selection!" );
+
+ break;
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ if( m_pMenu != NULL )
+ {
+ // relay message to menu
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+ else if( m_state == GUI_WINDOW_STATE_INTRO )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ this->UpdateIntro( param1 );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ else if( m_state == GUI_WINDOW_STATE_OUTRO )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ this->UpdateOutro( param1 );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+void
+CGuiScreenMainMenu::InitMenu()
+{
+#ifndef RAD_DEMO
+ bool isGameLoaded = GetGameDataManager()->IsGameLoaded();
+
+ rAssert( m_pMenu != NULL );
+ m_pMenu->SetSelectionValueCount( MENU_ITEM_MAIN_MENU,
+ isGameLoaded ? NUM_MAIN_MENU_SELECTIONS : NUM_MAIN_MENU_SELECTIONS - 1 );
+
+ m_pMenu->SetSelectionValue( MENU_ITEM_MAIN_MENU,
+ isGameLoaded ? MAIN_MENU_RESUME_GAME : MAIN_MENU_NEW_GAME );
+#endif // !RAD_DEMO
+}
+
+//===========================================================================
+// CGuiScreenMainMenu::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMainMenu::InitIntro()
+{
+ if( m_firstTimeEntered )
+ {
+ this->SetFadeTime( FIRST_TIME_FADE_IN_TIME );
+ this->InitMenu();
+
+ // set clear colour to a blue sky colour
+ //
+ GetRenderManager()->mpLayer(RenderEnums::GUI)->pView( 0 )->SetClearColour( FE_BLUE_SKY_COLOUR );
+ }
+}
+
+//===========================================================================
+// CGuiScreenMainMenu::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMainMenu::InitRunning()
+{
+ if( m_firstTimeEntered )
+ {
+ this->SetFadingEnabled( true );
+ this->RestoreDefaultFadeTime();
+
+ m_screenCover = NULL;
+ }
+
+ // turn on current glow item
+ //
+ rAssert( m_pMenu != NULL );
+ this->TurnOnGlowItems( (1 << m_pMenu->GetSelectionValue( MENU_ITEM_MAIN_MENU )) );
+}
+
+//===========================================================================
+// CGuiScreenMainMenu::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMainMenu::InitOutro()
+{
+ if( m_nextGagIndex == -1 )
+ {
+ // stop any FE gag dialog that was triggered and may still be playing
+ //
+ GetEventManager()->TriggerEvent( EVENT_FE_GAG_STOP );
+ }
+
+ // turn off all glow items
+ //
+ this->TurnOnGlowItems( 0 );
+}
+
+//===========================================================================
+// CGuiScreenMainMenu::UpdateIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMainMenu::UpdateIntro( unsigned int elapsedTime )
+{
+ if( m_firstTimeEntered )
+ {
+ // hide duplicate homer
+ //
+ if( m_gags != NULL )
+ {
+ Scrooby::Pure3dObject* p3dGag = m_gags->GetPure3dObject( "Gag0" );
+ rAssert( p3dGag );
+ if( p3dGag->GetMultiController() != NULL )
+ {
+ p3dGag->SetVisible( false );
+ }
+ }
+ }
+}
+
+//===========================================================================
+// CGuiScreenMainMenu::UpdateRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMainMenu::UpdateRunning( unsigned int elapsedTime )
+{
+ GetMemoryCardManager()->Update( elapsedTime );
+ this->UpdateGags( elapsedTime );
+}
+
+//===========================================================================
+// CGuiScreenMainMenu::UpdateOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMainMenu::UpdateOutro( unsigned int elapsedTime )
+{
+ int currentMenuSelection = m_pMenu->GetSelectionValue( MENU_ITEM_MAIN_MENU );
+
+#ifdef RAD_DEMO
+ if( currentMenuSelection == MAIN_MENU_PLAY_LEVEL_2 ||
+ currentMenuSelection == MAIN_MENU_PLAY_LEVEL_7 )
+#else
+ if( currentMenuSelection == MAIN_MENU_NEW_GAME ||
+ currentMenuSelection == MAIN_MENU_RESUME_GAME ||
+ currentMenuSelection == MAIN_MENU_MINI_GAME )
+#endif
+ {
+ rAssert( m_p3dObject != NULL );
+
+ tMultiController* multiController = m_p3dObject->GetMultiController();
+ if( multiController != NULL )
+ {
+ const float NUM_FADE_OUT_FRAMES = 15.0f;
+ float currentFrame = multiController->GetFrame();
+ float numRemainingFrames = multiController->GetNumFrames() - currentFrame;
+
+ if( numRemainingFrames < NUM_FADE_OUT_FRAMES )
+ {
+ // fade out TV frame
+ //
+ if( m_tvFrame != NULL )
+ {
+ float alpha = numRemainingFrames / NUM_FADE_OUT_FRAMES;
+
+ // decrease fade rate for low alpha values
+ alpha *= alpha;
+
+ if( alpha > 0.0f && alpha < 1.0f )
+ {
+ m_tvFrame->SetAlpha( alpha );
+ }
+ else
+ {
+ m_tvFrame->SetAlpha( 0.0f );
+ }
+ }
+
+ // TC [HACK]: To prevent any clipping in homer's mouth
+ // in the last few frames.
+ //
+ if( numRemainingFrames < 1.0f )
+ {
+ this->RestoreScreenCover();
+ }
+ }
+
+#ifndef RAD_DEMO
+ if( currentMenuSelection == MAIN_MENU_MINI_GAME )
+ {
+ const float NUM_RACE_CAR_FRAMES_FOR_BLENDING = 11.0f;
+
+ rAssert( m_raceCar != NULL );
+ if( currentFrame < 1.0f )
+ {
+ // get number of frames remaining in race car animation
+ //
+ tMultiController* raceMultiController = m_raceCar->GetMultiController();
+ rAssert( raceMultiController != NULL );
+
+ int currentRaceFrame = (int)raceMultiController->GetFrame() % (int)raceMultiController->GetNumFrames();
+ float numRemainingRaceFrames = raceMultiController->GetNumFrames() - currentRaceFrame;
+ raceMultiController->SetRelativeSpeed( numRemainingRaceFrames / NUM_RACE_CAR_FRAMES_FOR_BLENDING );
+ }
+ else if( currentFrame > NUM_RACE_CAR_FRAMES_FOR_BLENDING )
+ {
+ // hide duplicate race car
+ //
+ m_raceCar->SetVisible( false );
+ }
+ }
+#endif // !RAD_DEMO
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+//===========================================================================
+// CGuiScreenMainMenu::UpdateGags
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMainMenu::UpdateGags( unsigned int elapsedTime )
+{
+ static float NUM_BLEND_FRAMES = 10.0f;
+
+ // if the homer gag animation is running
+ //
+ if( m_homerIdleAnim != NULL && m_homer != NULL )
+ {
+ // check if homer gag animation is done
+ //
+ tMultiController* homerMultiController = m_homer->GetMultiController();
+ if( homerMultiController != NULL )
+ {
+ tAnimationFrameController* homerFrameController =
+ (tAnimationFrameController*)homerMultiController->GetTrack( 0 );
+ rAssert( homerFrameController );
+
+ if( homerFrameController->LastFrameReached() > 0 )
+ {
+ // ok, last frame has been reached on homer gag animation;
+ // restore the homer idle animation
+ //
+ if( m_homerIdleAnim != NULL )
+ {
+ homerFrameController->SetAnimation( m_homerIdleAnim, 0.0f, 0.0f );
+
+ m_homerIdleAnim->Release();
+ m_homerIdleAnim = NULL;
+ }
+
+ // restore homer idle animation frame range
+ //
+ homerMultiController->SetFrameRange( 0.0f, 60.0f );
+ homerMultiController->Reset();
+ }
+ }
+ }
+
+ // advance gag timer
+ //
+ m_gagsElapsedTime += elapsedTime;
+
+ const unsigned int FE_GAG_PREP_TIME = 2000; // in msec
+ if( m_gagsElapsedTime > (m_gagsCycleTime - FE_GAG_PREP_TIME) )
+ {
+ if( m_nextGagIndex == -1 )
+ {
+ // prepare for next gag
+ //
+#ifdef PLAY_ONLY_HOMER_GAGS
+ m_nextGagIndex = 0;
+#else
+ #ifdef PLAY_GAGS_SEQUENTIALLY
+ m_nextGagIndex = (m_currentGagIndex + 1) % m_gags->GetNumberOfLayers();
+ #else
+ int random = (rand() / 10) % GAGS_RANDOM_NUMBER_MODULO;
+ m_nextGagIndex = random % m_gags->GetNumberOfLayers();
+
+ if( CommandLineOptions::Get( CLO_FE_GAGS_TEST ) )
+ {
+ rReleasePrintf( "Next FE Gag Index = %d (Random Number = %d)\n", m_nextGagIndex, random );
+ }
+ #endif
+#endif
+ if( m_nextGagIndex != 0 )
+ {
+ GetEventManager()->TriggerEvent( EVENT_FE_GAG_INIT, reinterpret_cast<void*>( FE_GAGS[ m_nextGagIndex ] ) );
+ }
+ else // multiple homer gags
+ {
+ // pick a random homer animation
+ //
+#ifdef PLAY_GAGS_SEQUENTIALLY
+ m_nextHomerGagIndex = (m_nextHomerGagIndex + 1) % NUM_HOMER_GAG_ANIMATIONS;
+#else
+ int random = (rand() / 10) % GAGS_RANDOM_NUMBER_MODULO;
+ m_nextHomerGagIndex = random % NUM_HOMER_GAG_ANIMATIONS;
+
+ if( CommandLineOptions::Get( CLO_FE_GAGS_TEST ) )
+ {
+ rReleasePrintf( " Next Homer Gag Index = %d (Random Number = %d)\n", m_nextHomerGagIndex, random );
+ }
+#endif
+
+ GetEventManager()->TriggerEvent( EVENT_FE_GAG_INIT, reinterpret_cast<void*>( FE_GAGS_FOR_HOMER[ m_nextHomerGagIndex ] ) );
+ }
+ }
+ }
+
+ if( m_gagsElapsedTime > m_gagsCycleTime )
+ {
+ if( m_gags != NULL )
+ {
+ Scrooby::Layer* currentGagLayer = NULL;
+
+ // hide previous gag layer, if exists
+ //
+ if( m_currentGagIndex != -1 )
+ {
+ currentGagLayer = m_gags->GetLayerByIndex( m_currentGagIndex );
+ currentGagLayer->SetVisible( false );
+ }
+
+ m_currentGagIndex = m_nextGagIndex;
+ m_nextGagIndex = -1;
+
+ // get the pure3d gag
+ //
+ char objectName[ 8 ];
+ sprintf( objectName, "Gag%d", m_currentGagIndex );
+ Scrooby::Pure3dObject* p3dGag = m_gags->GetPure3dObject( objectName );
+
+ // reset gag animation
+ //
+ rAssert( p3dGag );
+ tMultiController* multiController = p3dGag->GetMultiController();
+ if( multiController != NULL )
+ {
+ multiController->Reset();
+ }
+
+ // show current gag layer
+ //
+ currentGagLayer = m_gags->GetLayerByIndex( m_currentGagIndex );
+ rAssert( currentGagLayer );
+ currentGagLayer->SetVisible( true );
+
+ // start FE gag dialog
+ //
+ GetEventManager()->TriggerEvent( EVENT_FE_GAG_START );
+
+ if( m_currentGagIndex == 0 )
+ {
+ // special case for homer gag animation
+ //
+ if( m_homer != NULL && multiController != NULL )
+ {
+ tAnimationFrameController* frameController =
+ (tAnimationFrameController*)multiController->GetTrack( 0 );
+ rAssert( frameController );
+
+ // get homer gag animation
+ tAnimation* homerAnim = frameController->GetAnimation();
+ rAssert( homerAnim );
+ homerAnim->SetCyclic( false ); // don't cycle when done
+
+ tMultiController* homerMultiController = m_homer->GetMultiController();
+ if( homerMultiController != NULL )
+ {
+ homerMultiController->SetFrameRange( HOMER_GAG_ANIMATION[ m_nextHomerGagIndex ].startFrame,
+ HOMER_GAG_ANIMATION[ m_nextHomerGagIndex ].endFrame );
+
+ tAnimationFrameController* homerFrameController =
+ (tAnimationFrameController*)homerMultiController->GetTrack( 0 );
+ rAssert( homerFrameController );
+
+ // save reference to homer idle animation
+ //
+ m_homerIdleAnim = homerFrameController->GetAnimation();
+ rAssert( m_homerIdleAnim != NULL );
+ m_homerIdleAnim->AddRef();
+
+ // switch to the homer gag animation
+ homerFrameController->SetAnimation( homerAnim, 0.0f, NUM_BLEND_FRAMES );
+ }
+ }
+ }
+ }
+
+ // set new random gag cycle time
+ //
+ if( CommandLineOptions::Get( CLO_FE_GAGS_TEST ) )
+ {
+ m_gagsCycleTime = 1000 * MIN_GAGS_CYCLE_TIME;
+ }
+ else
+ {
+ m_gagsCycleTime = 1000 * (MIN_GAGS_CYCLE_TIME + rand() % (MAX_GAGS_CYCLE_TIME - MIN_GAGS_CYCLE_TIME + 1));
+ }
+
+ // reset gag timer
+ //
+ m_gagsElapsedTime = 0;
+ }
+}
+
+//===========================================================================
+// CGuiScreenMainMenu::StopHomerIdleAnimation
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMainMenu::StopHomerIdleAnimation()
+{
+/*
+ if( m_homer != NULL )
+ {
+ // reset Homer's snoring animation so that the camera
+ // can pan right into his mouth
+ //
+ tMultiController* multiController = m_homer->GetMultiController();
+ if( multiController != NULL )
+ {
+ tAnimationFrameController* frameController = (tAnimationFrameController*)multiController->GetTrack( 0 );
+ rAssert( frameController );
+
+ if( m_homerIdleAnim == NULL )
+ {
+ m_homerIdleAnim = frameController->GetAnimation();
+ rAssert( m_homerIdleAnim );
+ }
+
+ m_homerIdleAnim->SetCyclic( false );
+
+ static float NUM_BLEND_FRAMES = 10.0f;
+ frameController->SetAnimation( m_homerIdleAnim,
+ m_homerIdleAnim->GetNumFrames() - NUM_BLEND_FRAMES,
+ NUM_BLEND_FRAMES );
+
+ m_homerIdleAnim = NULL;
+ }
+ }
+*/
+}
+
+//===========================================================================
+// CGuiScreenMainMenu::TurnOnGlowItems
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: items - bitmask representing which ones to turn on glowing
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMainMenu::TurnOnGlowItems( unsigned int items )
+{
+#ifdef RAD_DEMO
+ if( items > 0 )
+ {
+ bool isOptionsSelected = ( (items & (1 << MAIN_MENU_OPTIONS)) > 0 );
+ m_glowingItems[ MAIN_MENU_PLAY_LEVEL_2 ]->SetVisible( !isOptionsSelected );
+ m_glowingItems[ MAIN_MENU_OPTIONS ]->SetVisible( isOptionsSelected );
+
+ return;
+ }
+#endif
+
+#ifdef RAD_WIN32
+ for( int i = 0; i < NUM_MAIN_MENU_SELECTIONS; i++ )
+ {
+ bool isOn = (items & (1 << i)) > 0;
+ int which = i < MAIN_MENU_QUIT_GAME ? i : i - 1;
+ if( m_glowingItems[ which ] != NULL && i != MAIN_MENU_QUIT_GAME )
+ {
+ m_glowingItems[ which ]->SetVisible( isOn );
+ }
+ }
+#else
+ for( int i = 0; i < NUM_MAIN_MENU_SELECTIONS; i++ )
+ {
+ bool isOn = (items & (1 << i)) > 0;
+ if( m_glowingItems[ i ] != NULL )
+ {
+ m_glowingItems[ i ]->SetVisible( isOn );
+ }
+ }
+#endif
+}
+
+void
+CGuiScreenMainMenu::OnNewGameSelected( RenderEnums::LevelEnum level,
+ RenderEnums::MissionEnum mission )
+{
+ bool showNewGameMovie = false;
+
+#ifdef RAD_RELEASE
+ #ifndef RAD_DEMO
+ showNewGameMovie = !CommandLineOptions::Get( CLO_SKIP_MOVIE );
+ #endif
+#endif
+
+ if( showNewGameMovie )
+ {
+ // play new game movie
+ //
+ rAssert( m_guiManager );
+ CGuiScreenPlayMovie* playMovieScreen = static_cast<CGuiScreenPlayMovie*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_PLAY_MOVIE_NEW_GAME ) );
+ rAssert( playMovieScreen );
+
+ playMovieScreen->SetMovieToPlay( MovieNames::MOVIE1, false, true );
+
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_PLAY_MOVIE_NEW_GAME );
+ }
+ else
+ {
+ // send message to front-end manager to quit front-end and
+ // start single player story mode
+ //
+ m_pParent->HandleMessage( GUI_MSG_QUIT_FRONTEND, 1 ); // 1 = one player
+ }
+
+ this->StartTransitionAnimation( 0, 53 );
+
+ this->StopHomerIdleAnimation();
+
+ if( GetGameDataManager()->IsGameLoaded() )
+ {
+ // reset all game data
+ //
+ GetGameDataManager()->ResetGame();
+
+ //reset the rewards manager and relock the rewards
+ //
+ GetRewardsManager()->ClearRewards();
+ }
+
+ GetCharacterSheetManager()->SetCurrentMission( level, mission );
+
+ // restore black clear colour
+ //
+ GetRenderManager()->mpLayer(RenderEnums::GUI)->pView( 0 )->SetClearColour( tColour( 0, 0, 0 ) );
+}
+
+void
+CGuiScreenMainMenu::OnResumeGameSelected()
+{
+ // send message to front-end manager to quit front-end and
+ // start single player story mode
+ //
+ m_pParent->HandleMessage( GUI_MSG_QUIT_FRONTEND, 1 ); // 1 = one player
+
+ this->StartTransitionAnimation( 140, 170 );
+}
+
+void
+CGuiScreenMainMenu::OnMiniGameSelected()
+{
+ // send message to front-end manager to quit front-end and
+ // start mini-game mode
+ //
+ m_pParent->HandleMessage( GUI_MSG_QUIT_FRONTEND, 2 ); // 2 = mini-game
+
+ this->StartTransitionAnimation( 880, 913 );
+}
+
+#ifdef RAD_WIN32
+void
+CGuiScreenMainMenu::OnQuitGameSelected()
+{
+ rAssert( m_guiManager != NULL );
+ m_guiManager->DisplayPrompt( PROMPT_CONFIRM_QUIT, this );
+}
+#endif
+
+#ifdef SRR2_LEVEL_SELECTION
+void
+CGuiScreenMainMenu::ToggleLevelMenu()
+{
+ Scrooby::Page* levelPage = m_pScroobyScreen->GetPage( "Level" );
+ if( levelPage != NULL )
+ {
+ Scrooby::Group* levelMenu = levelPage->GetGroup( "Menu" );
+ rAssert( levelMenu );
+
+ bool toggle = !levelMenu->IsVisible();
+
+ // display menu on/off
+ levelMenu->SetVisible( toggle );
+
+ // enable/disable menu selections
+ for( int i = MENU_ITEM_LEVEL; i <= MENU_ITEM_LEVEL; i++ )
+ {
+ m_pMenu->SetMenuItemEnabled( i, toggle );
+ }
+ }
+}
+#endif // SRR2_LEVEL_SELECTION
+
+//===========================================================================
+// Public Member Functions (for CGuiScreenIntroTransition)
+//===========================================================================
+
+bool CGuiScreenIntroTransition::s_introTransitionPlayed = false;
+
+//===========================================================================
+// CGuiScreenIntroTransition::CGuiScreenIntroTransition
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenIntroTransition::CGuiScreenIntroTransition
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_INTRO_TRANSITION ),
+ m_introState( WAITING_FOR_MULTICONTROLLER ),
+ m_homer( NULL ),
+ m_tvFrame( NULL ),
+ m_gags( NULL )
+{
+ this->SetFadingEnabled( false );
+
+ // Retrieve drawing elements from 3dFE page
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "3dFE" );
+ rAssert( pPage );
+ m_homer = pPage->GetPure3dObject( "Homer" );
+
+ // Retrieve drawing elements from TVFrame page
+ //
+ pPage = m_pScroobyScreen->GetPage( "TVFrame" );
+ rAssert( pPage );
+ m_tvFrame = pPage->GetLayer( "TVFrame" );
+
+ // 3D FE Gags
+ //
+ m_gags = m_pScroobyScreen->GetPage( "3dFEGags" );
+ rAssert( m_gags );
+
+ s_introTransitionPlayed = false;
+}
+
+
+//===========================================================================
+// CGuiScreenIntroTransition::~CGuiScreenIntroTransition
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenIntroTransition::~CGuiScreenIntroTransition()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenIntroTransition::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenIntroTransition::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+
+ if( m_state == GUI_WINDOW_STATE_INTRO &&
+ message == GUI_MSG_UPDATE )
+ {
+ switch( m_introState )
+ {
+ case WAITING_FOR_MULTICONTROLLER:
+ {
+ if( s_p3dMultiController != NULL )
+ {
+ m_numTransitionsPending--;
+
+ this->StartTransitionAnimation( 721, 770, false );
+
+ if( m_screenCover != NULL )
+ {
+ m_screenCover->SetVisible( false );
+ }
+
+ m_introState = RUNNING_INTRO_ANIMATION;
+ }
+ else
+ {
+ if( m_screenCover != NULL )
+ {
+ m_screenCover->SetAlpha( 1.0f );
+ }
+ }
+
+ break;
+ }
+
+ default:
+ {
+ if( m_tvFrame != NULL )
+ {
+ rAssert( m_p3dObject );
+
+ tMultiController* multiController = m_p3dObject->GetMultiController();
+ rAssert( multiController );
+
+ const float NUM_FADE_IN_FRAMES = 20.0f;
+ float numRemainingFrames = multiController->GetNumFrames() -
+ multiController->GetFrame();
+
+ if( numRemainingFrames < NUM_FADE_IN_FRAMES )
+ {
+ float alpha = 1.0f - numRemainingFrames / NUM_FADE_IN_FRAMES;
+
+ // decrease fade rate for low alpha values
+ alpha *= alpha;
+
+ if( alpha < 1.0f )
+ {
+ // fade in TV frame
+ //
+ if( m_tvFrame != NULL )
+ {
+ m_tvFrame->SetAlpha( alpha );
+ }
+
+ // fade in foreground layers
+ //
+ this->SetAlphaForLayers( alpha,
+ m_foregroundLayers,
+ m_numForegroundLayers );
+ }
+ }
+ }
+
+ break;
+ }
+ }
+
+ // hide duplicate homer
+ //
+ if( m_gags != NULL )
+ {
+ Scrooby::Pure3dObject* p3dGag = m_gags->GetPure3dObject( "Gag0" );
+ rAssert( p3dGag );
+ if( p3dGag->GetMultiController() != NULL )
+ {
+ p3dGag->SetVisible( false );
+ }
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenIntroTransition::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenIntroTransition::InitIntro()
+{
+ // wait for retrieval of pure3d multicontroller
+ m_numTransitionsPending++;
+
+ if( m_tvFrame != NULL )
+ {
+ m_tvFrame->SetAlpha( 0.0f );
+
+ this->SetAlphaForLayers( 0.0f,
+ m_foregroundLayers,
+ m_numForegroundLayers );
+ }
+
+ CGuiScreenMainMenu* pScreen = static_cast<CGuiScreenMainMenu*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_MAIN_MENU ) );
+ rAssert( pScreen != NULL );
+ pScreen->InitMenu();
+}
+
+
+//===========================================================================
+// CGuiScreenIntroTransition::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenIntroTransition::InitRunning()
+{
+ s_introTransitionPlayed = true;
+
+ if( m_tvFrame != NULL )
+ {
+ m_tvFrame->SetAlpha( 1.0f );
+
+ this->SetAlphaForLayers( 1.0f,
+ m_foregroundLayers,
+ m_numForegroundLayers );
+ }
+
+ // goto main menu
+ //
+ rAssert( m_pParent );
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ GUI_SCREEN_ID_MAIN_MENU,
+ CLEAR_WINDOW_HISTORY );
+}
+
+
+//===========================================================================
+// CGuiScreenIntroTransition::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenIntroTransition::InitOutro()
+{
+}
+
diff --git a/game/code/presentation/gui/frontend/guiscreenmainmenu.h b/game/code/presentation/gui/frontend/guiscreenmainmenu.h
new file mode 100644
index 0000000..733fa58
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenmainmenu.h
@@ -0,0 +1,157 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMainMenu
+//
+// Description:
+//
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/11/20 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMAINMENU_H
+#define GUISCREENMAINMENU_H
+
+#ifndef FINAL
+ #ifndef RAD_DEMO
+ #define SRR2_LEVEL_SELECTION // for SRR2 level selection
+ #endif
+#endif
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+#include <render/enums/renderenums.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+class tAnimation;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMainMenu : public CGuiScreen
+{
+public:
+ CGuiScreenMainMenu( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMainMenu();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ void InitMenu();
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ void UpdateIntro( unsigned int elapsedTime );
+ void UpdateRunning( unsigned int elapsedTime );
+ void UpdateOutro( unsigned int elapsedTime );
+
+private:
+ enum eMainMenuSelection
+ {
+#ifdef RAD_DEMO
+ MAIN_MENU_PLAY_LEVEL_2,
+ MAIN_MENU_PLAY_LEVEL_7,
+ MAIN_MENU_OPTIONS,
+#else
+ MAIN_MENU_LOAD_GAME,
+ MAIN_MENU_CARD_GALLERY,
+ MAIN_MENU_OPTIONS,
+ MAIN_MENU_MINI_GAME,
+ #ifdef RAD_WIN32
+ MAIN_MENU_QUIT_GAME,
+ #endif
+ MAIN_MENU_NEW_GAME,
+ MAIN_MENU_RESUME_GAME,
+#endif
+
+ NUM_MAIN_MENU_SELECTIONS
+ };
+
+ void UpdateGags( unsigned int elapsedTime );
+ void StopHomerIdleAnimation();
+ void TurnOnGlowItems( unsigned int items );
+
+ void OnNewGameSelected( RenderEnums::LevelEnum level = RenderEnums::L1,
+ RenderEnums::MissionEnum mission = RenderEnums::M1 );
+
+ void OnResumeGameSelected();
+ void OnMiniGameSelected();
+
+#ifdef RAD_WIN32
+ void OnQuitGameSelected();
+#endif
+
+#ifdef SRR2_LEVEL_SELECTION
+ void ToggleLevelMenu();
+#endif
+
+ CGuiMenu* m_pMenu;
+
+ Scrooby::Page* m_gags;
+ int m_currentGagIndex;
+ int m_nextGagIndex;
+ int m_nextHomerGagIndex;
+ unsigned int m_gagsElapsedTime;
+ unsigned int m_gagsCycleTime;
+
+ Scrooby::Pure3dObject* m_homer;
+ Scrooby::Pure3dObject* m_raceCar;
+ tAnimation* m_homerIdleAnim;
+ Scrooby::Layer* m_tvFrame;
+
+ Scrooby::Pure3dObject* m_glowingItems[ NUM_MAIN_MENU_SELECTIONS ];
+
+};
+
+
+class CGuiScreenIntroTransition: public CGuiScreen
+{
+public:
+ CGuiScreenIntroTransition( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenIntroTransition();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ static bool s_introTransitionPlayed;
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ enum eState
+ {
+ WAITING_FOR_MULTICONTROLLER,
+ RUNNING_INTRO_ANIMATION,
+
+ NUM_STATES
+ };
+
+ eState m_introState;
+ Scrooby::Pure3dObject* m_homer;
+ Scrooby::Layer* m_tvFrame;
+ Scrooby::Page* m_gags;
+
+};
+
+#endif // GUISCREENMAINMENU_H
diff --git a/game/code/presentation/gui/frontend/guiscreenmissiongallery.cpp b/game/code/presentation/gui/frontend/guiscreenmissiongallery.cpp
new file mode 100644
index 0000000..e0d9113
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenmissiongallery.cpp
@@ -0,0 +1,860 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMissionGallery
+//
+// Description: Implementation of the CGuiScreenMissionGallery class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/19 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenmissiongallery.h>
+#include <presentation/gui/frontend/guiscreenscrapbookcontents.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guiscreenprompt.h>
+
+#include <cheats/cheatinputsystem.h>
+#include <events/eventmanager.h>
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+#include <p3d/inventory.hpp>
+#include <raddebug.hpp> // Foundation
+
+#include <screen.h>
+#include <page.h>
+#include <layer.h>
+#include <group.h>
+#include <sprite.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const char* MISSION_GALLERY_IMAGES_DIR = "art\\frontend\\dynaload\\images\\scrapbook\\";
+const char* MISSION_GALLERY_INVENTORY_SECTION = "FE_MissionGallery";
+const float MISSION_IMAGE_SCALE = 0.4f;
+const float MISSION_VIEW_TRANSITION_TIME = 250.0f; // in msec
+const float MISSION_VIEW_PROJECTILE_GRAVITY = 0.005f; // in m/ms/ms
+const int MISSION_VIEW_POS_X = 180;
+const int MISSION_VIEW_POS_Y = 120;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMissionGallery::CGuiScreenMissionGallery
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMissionGallery::CGuiScreenMissionGallery
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_MISSION_GALLERY ),
+ m_pMenu( NULL ),
+ m_menuGroup( NULL ),
+ m_isMissionImagesLoaded( false ),
+ m_screenState( SCREEN_STATE_NORMAL ),
+ m_elapsedTime( 0 ),
+ m_projectileVelocity( 0.0f, 0.0f, 0.0f ),
+ m_missionInfo( NULL ),
+ m_missionNum( NULL ),
+ m_missionTitle( NULL ),
+ m_selectedMission( -1 )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "MissionGallery" );
+ rAssert( pPage != NULL );
+
+ // create a 2D sprite menu
+ //
+ m_pMenu = new CGuiMenu2D( this, NUM_MISSIONS_PER_LEVEL, 4, GUI_SPRITE_MENU, MENU_SFX_NONE );
+ rAssert( m_pMenu != NULL );
+ m_pMenu->SetGreyOutEnabled( false );
+
+ m_menuGroup = pPage->GetGroup( "Menu" );
+ rAssert( m_menuGroup != NULL );
+
+ // add sprites to menu
+ //
+ for( int i = 0; i < NUM_MISSIONS_PER_LEVEL; i++ )
+ {
+ char name[ 16 ];
+ sprintf( name, "Mission%d", i );
+
+ m_pMenu->AddMenuItem( m_menuGroup->GetSprite( name ) );
+ }
+
+ // add menu cursor
+ //
+ m_pMenu->SetCursor( m_menuGroup->GetSprite( "MissionFrame" ) );
+
+ // TC: [TEMP] due to placeholder image
+ //
+ m_menuGroup->GetSprite( "MissionFrame" )->RotateAboutCenter( 90.0f );
+
+ // get view mission layer and text drawables
+ //
+ m_missionInfo = pPage->GetLayer( "ViewMission" );
+ m_missionNum = pPage->GetText( "MissionNum" );
+ m_missionTitle = pPage->GetText( "MissionTitle" );
+
+ // add view mission layer to list of foreground layers
+ //
+ if( m_numForegroundLayers < MAX_FOREGROUND_LAYERS )
+ {
+ m_foregroundLayers[ m_numForegroundLayers ] = m_missionInfo;
+ m_numForegroundLayers++;
+ }
+
+ if( this->IsWideScreenDisplay() )
+ {
+ m_missionInfo->ResetTransformation();
+ this->ApplyWideScreenCorrectionScale( m_missionInfo );
+ }
+
+ // wrap mission title
+ //
+ rAssert( m_missionTitle != NULL );
+ m_missionTitle->SetTextMode( Scrooby::TEXT_WRAP );
+
+ // add outline to mission number and title
+ //
+ Scrooby::Text* missionNum = pPage->GetText( "MissionNum" );
+ if( missionNum != NULL )
+ {
+ missionNum->SetDisplayOutline( true );
+ }
+ m_missionTitle->SetDisplayOutline( true );
+
+ // create inventory section for mission galllery resources
+ //
+ p3d::inventory->AddSection( MISSION_GALLERY_INVENTORY_SECTION );
+}
+
+
+//===========================================================================
+// CGuiScreenMissionGallery::~CGuiScreenMissionGallery
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMissionGallery::~CGuiScreenMissionGallery()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+
+ // destroy mission gallery inventory section
+ //
+ p3d::inventory->DeleteSection( MISSION_GALLERY_INVENTORY_SECTION );
+}
+
+
+//===========================================================================
+// CGuiScreenMissionGallery::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionGallery::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( message == GUI_MSG_MENU_PROMPT_RESPONSE )
+ {
+ rAssert( param1 == PROMPT_CONFIRM_START_MISSION );
+
+ if( param2 == CGuiMenuPrompt::RESPONSE_YES )
+ {
+ this->OnStartMission();
+ }
+ else
+ {
+ rAssert( param2 == CGuiMenuPrompt::RESPONSE_NO );
+
+// m_selectedMission = -1;
+ GetInputManager()->UnregisterControllerID( 0 );
+ this->ReloadScreen();
+ }
+ }
+
+ if( m_screenState == SCREEN_STATE_GOTO_VIEW ||
+ m_screenState == SCREEN_STATE_BACK_VIEW )
+ {
+ if( message == GUI_MSG_UPDATE )
+ {
+ this->OnUpdate( param1 );
+ }
+
+ return;
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ if( m_screenState == SCREEN_STATE_VIEWING )
+ {
+ if( message == GUI_MSG_CONTROLLER_SELECT )
+ {
+ if( this->IsButtonVisible( BUTTON_ICON_ACCEPT ) )
+ {
+ GetInputManager()->RegisterControllerID( 0, param1 );
+
+ m_guiManager->DisplayPrompt( PROMPT_CONFIRM_START_MISSION, this );
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_SELECT ); // sound effect
+ }
+ }
+ else if( message == GUI_MSG_CONTROLLER_BACK )
+ {
+ m_screenState = SCREEN_STATE_BACK_VIEW;
+ m_elapsedTime = 0;
+
+ m_selectedMission = -1;
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_BACK ); // sound effect
+ }
+
+ if( this->IsControllerMessage( message ) )
+ {
+ return;
+ }
+ }
+
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ this->OnUpdate( param1 );
+
+ break;
+ }
+/*
+ case GUI_MSG_MENU_SELECTION_CHANGED:
+ {
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, static_cast<int>( param1 ) != MISSION_8 );
+
+ break;
+ }
+*/
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ m_selectedMission = static_cast<int>( param1 );
+
+// m_guiManager->DisplayPrompt( PROMPT_CONFIRM_START_MISSION, this );
+ this->OnMenuSelectionMade( param1 );
+#ifdef RAD_WIN32
+ // Hide/disable all other menu items.
+ this->SetVisibilityForAllOtherMenuItems( false );
+#endif
+ break;
+ }
+/*
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ if( this->IsButtonVisible( BUTTON_ICON_ACCEPT ) )
+ {
+ GetInputManager()->RegisterControllerID( 0, param1 );
+ }
+ else
+ {
+ return;
+ }
+
+ break;
+ }
+*/
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ //
+ if( m_pMenu != NULL && m_screenState == SCREEN_STATE_NORMAL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+ else if( m_state == GUI_WINDOW_STATE_OUTRO )
+ {
+ if( message == GUI_MSG_UPDATE && m_numTransitionsPending <= 0 )
+ {
+ if( m_isMissionImagesLoaded && m_selectedMission == -1 )
+ {
+ this->UnloadMissionImages();
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenMissionGallery::OnProcessRequestsComplete
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void
+CGuiScreenMissionGallery::OnProcessRequestsComplete( void* pUserData )
+{
+ m_numTransitionsPending--;
+
+ // show mission images
+ //
+ m_menuGroup->SetAlpha( 1.0f );
+
+ p3d::pddi->DrawSync();
+
+ // push and select inventory section for searching
+ //
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( MISSION_GALLERY_INVENTORY_SECTION );
+ bool currentSectionOnly = p3d::inventory->GetCurrentSectionOnly();
+ p3d::inventory->SetCurrentSectionOnly( true );
+
+ // update all mission images
+ //
+ CGuiScreenScrapBookContents* scrapBookContents = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( scrapBookContents != NULL );
+
+ rAssert( m_pMenu != NULL );
+ for( int i = 0; i < NUM_MISSIONS_PER_LEVEL; i++ )
+ {
+ if( (m_pMenu->GetMenuItem( i )->m_attributes & SELECTION_ENABLED) > 0 )
+ {
+ char name[ 16 ];
+ sprintf( name, "mis%02d_%02d.png", scrapBookContents->GetCurrentLevel() + 1, i + 1 );
+ tSprite* pSprite = p3d::find<tSprite>( name );
+ if( pSprite != NULL )
+ {
+ Scrooby::Sprite* missionImage = dynamic_cast<Scrooby::Sprite*>( m_pMenu->GetMenuItem( i )->GetItem() );
+ rAssert( missionImage != NULL );
+ missionImage->SetRawSprite( pSprite, true );
+
+ missionImage->ResetTransformation();
+ missionImage->ScaleAboutCenter( MISSION_IMAGE_SCALE );
+ }
+ else
+ {
+ rAssertMsg( false, "Mission bitmap not found!" );
+ }
+ }
+ }
+
+ // pop inventory section and restore states
+ //
+ p3d::inventory->SetCurrentSectionOnly( currentSectionOnly );
+ p3d::inventory->PopSection();
+
+ m_isMissionImagesLoaded = true;
+}
+
+//===========================================================================
+// CGuiScreenMissionGallery::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionGallery::InitIntro()
+{
+ if( !m_isMissionImagesLoaded )
+ {
+ int numImagesToLoad = this->LoadMissionImages();
+ }
+/*
+ // reset menu to first selection
+ //
+ if( numImagesToLoad > 0 )
+ {
+ rAssert( m_pMenu != NULL );
+ m_pMenu->Reset();
+ }
+*/
+ m_elapsedTime = 0;
+}
+
+
+//===========================================================================
+// CGuiScreenMissionGallery::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionGallery::InitRunning()
+{
+ if( m_screenState != SCREEN_STATE_VIEWING )
+ {
+ // hide mission info layer by default
+ //
+ rAssert( m_missionInfo != NULL );
+ m_missionInfo->SetVisible( false );
+ m_missionInfo->SetAlpha( 0.0f );
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMissionGallery::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionGallery::InitOutro()
+{
+// this->UnloadMissionImages();
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenMissionGallery::OnUpdate( unsigned int elapsedTime )
+{
+ if( m_screenState == SCREEN_STATE_NORMAL )
+ {
+ // pulse cursor alpha
+ //
+ Scrooby::Drawable* cursor = m_pMenu->GetCursor();
+ if( cursor != NULL )
+ {
+ const unsigned int PULSE_PERIOD = 1000;
+
+ float alpha = GuiSFX::Pulse( (float)m_elapsedTime,
+ (float)PULSE_PERIOD,
+ 0.75f,
+ 0.25f,
+ -rmt::PI_BY2 );
+
+ cursor->SetAlpha( alpha );
+
+ m_elapsedTime += elapsedTime;
+ m_elapsedTime %= PULSE_PERIOD;
+ }
+ }
+ else if( m_screenState == SCREEN_STATE_GOTO_VIEW )
+ {
+ // transition mission image from normal menu position
+ // to viewing position
+ //
+ m_elapsedTime += elapsedTime;
+
+ rAssert( m_pMenu != NULL );
+ int currentSelection = m_pMenu->GetSelection();
+
+ Scrooby::BoundedDrawable* pDrawable = m_pMenu->GetMenuItem( currentSelection )->GetItem();
+ rAssert( pDrawable != NULL );
+
+ pDrawable->ResetTransformation();
+
+ if( m_elapsedTime < (unsigned int)MISSION_VIEW_TRANSITION_TIME )
+ {
+ float percentageDone = m_elapsedTime / MISSION_VIEW_TRANSITION_TIME;
+
+ pDrawable->ScaleAboutCenter( MISSION_IMAGE_SCALE + percentageDone * (1.0f - MISSION_IMAGE_SCALE) );
+
+ GuiSFX::Projectile( pDrawable,
+ (float)m_elapsedTime,
+ MISSION_VIEW_TRANSITION_TIME,
+ m_projectileVelocity,
+ false,
+ MISSION_VIEW_PROJECTILE_GRAVITY );
+
+ // fade out rest of the menu items
+ //
+ this->SetMenuAlpha( 1.0f - rmt::Sqrt( percentageDone ) );
+
+ // fade in mission info layer
+ //
+ rAssert( m_missionInfo != NULL );
+ m_missionInfo->SetAlpha( percentageDone * percentageDone );
+ }
+ else
+ {
+ GuiSFX::Projectile( pDrawable,
+ MISSION_VIEW_TRANSITION_TIME,
+ MISSION_VIEW_TRANSITION_TIME,
+ m_projectileVelocity,
+ false,
+ MISSION_VIEW_PROJECTILE_GRAVITY );
+
+ this->SetMenuAlpha( 0.0f );
+
+ rAssert( m_missionInfo != NULL );
+ m_missionInfo->SetAlpha( 1.0f );
+
+ m_screenState = SCREEN_STATE_VIEWING;
+ }
+ }
+ else if( m_screenState == SCREEN_STATE_BACK_VIEW )
+ {
+ // transition mission image from viewing position back to
+ // normal menu position
+ //
+ m_elapsedTime += elapsedTime;
+
+ rAssert( m_pMenu != NULL );
+ int currentSelection = m_pMenu->GetSelection();
+
+ Scrooby::BoundedDrawable* pDrawable = m_pMenu->GetMenuItem( currentSelection )->GetItem();
+ rAssert( pDrawable != NULL );
+
+ pDrawable->ResetTransformation();
+
+ if( m_elapsedTime < (unsigned int)MISSION_VIEW_TRANSITION_TIME )
+ {
+ float percentageDone = m_elapsedTime / MISSION_VIEW_TRANSITION_TIME;
+
+ pDrawable->ScaleAboutCenter( MISSION_IMAGE_SCALE + (1.0f - percentageDone) * (1.0f - MISSION_IMAGE_SCALE) );
+
+ GuiSFX::Projectile( pDrawable,
+ (float)m_elapsedTime,
+ MISSION_VIEW_TRANSITION_TIME,
+ m_projectileVelocity,
+ true,
+ MISSION_VIEW_PROJECTILE_GRAVITY );
+
+ // fade back in rest of the menu items
+ //
+ this->SetMenuAlpha( percentageDone * percentageDone );
+
+ // fade in mission info layer
+ //
+ rAssert( m_missionInfo != NULL );
+ m_missionInfo->SetAlpha( 1.0f - rmt::Sqrt( percentageDone ) );
+ }
+ else
+ {
+ pDrawable->ScaleAboutCenter( MISSION_IMAGE_SCALE );
+
+ GuiSFX::Projectile( pDrawable,
+ MISSION_VIEW_TRANSITION_TIME,
+ MISSION_VIEW_TRANSITION_TIME,
+ m_projectileVelocity,
+ true,
+ MISSION_VIEW_PROJECTILE_GRAVITY );
+
+ // show menu cursor
+ //
+ rAssert( m_pMenu != NULL );
+ m_pMenu->GetCursor()->SetVisible( true );
+
+ // show level bar
+ //
+ CGuiScreenScrapBookContents* scrapBookContents = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( scrapBookContents != NULL );
+ scrapBookContents->SetLevelBarVisible( true );
+
+ this->SetMenuAlpha( 1.0f );
+
+ rAssert( m_missionInfo != NULL );
+ m_missionInfo->SetAlpha( 0.0f );
+
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+
+ // hide mission info layer
+ //
+ rAssert( m_missionInfo != NULL );
+ m_missionInfo->SetVisible( false );
+
+ m_elapsedTime = 0;
+ m_screenState = SCREEN_STATE_NORMAL;
+
+#ifdef RAD_WIN32
+ // Show/enable all hidden menu items.
+ this->SetVisibilityForAllOtherMenuItems( true );
+#endif
+ }
+ }
+}
+
+void
+CGuiScreenMissionGallery::SetMenuAlpha( float alpha )
+{
+ rAssert( m_pMenu != NULL );
+ int currentSelection = m_pMenu->GetSelection();
+
+ for( int i = 0; i < NUM_MISSIONS_PER_LEVEL; i++ )
+ {
+ if( i != currentSelection )
+ {
+ Scrooby::Drawable* missionImage = m_pMenu->GetMenuItem( i )->GetItem();
+ rAssert( missionImage != NULL );
+ missionImage->SetAlpha( alpha );
+ }
+ }
+}
+
+void
+CGuiScreenMissionGallery::OnMenuSelectionMade( int selection )
+{
+ m_screenState = SCREEN_STATE_GOTO_VIEW;
+ m_elapsedTime = 0;
+
+ CGuiScreenScrapBookContents* scrapBookContents = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( scrapBookContents != NULL );
+
+ // hide menu cursor
+ //
+ rAssert( m_pMenu != NULL );
+ m_pMenu->GetCursor()->SetVisible( false );
+
+ // hide level bar
+ //
+ scrapBookContents->SetLevelBarVisible( false );
+
+ // show mission info layer
+ //
+ rAssert( m_missionInfo != NULL );
+ m_missionInfo->SetVisible( true );
+
+ if( selection == MISSION_8 ) // bonus mission
+ {
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, false );
+ }
+
+ // update mission number and title
+ //
+ rAssert( m_missionNum != NULL );
+ m_missionNum->SetIndex( selection );
+
+ int missionTitleIndex = scrapBookContents->GetCurrentLevel() * NUM_MISSIONS_PER_LEVEL + selection;
+ rAssert( m_missionTitle != NULL );
+ m_missionTitle->SetIndex( missionTitleIndex );
+
+ // calculate the initial projectile velocity
+ //
+ Scrooby::BoundedDrawable* pDrawable = m_pMenu->GetMenuItem( selection )->GetItem();
+ rAssert( pDrawable != NULL );
+
+ int startPosX = 0;
+ int startPosY = 0;
+ pDrawable->GetOriginPosition( startPosX, startPosY );
+
+ m_projectileVelocity.x = (MISSION_VIEW_POS_X - startPosX) / MISSION_VIEW_TRANSITION_TIME;
+ m_projectileVelocity.y = (MISSION_VIEW_POS_Y - startPosY - 0.5f * MISSION_VIEW_PROJECTILE_GRAVITY * MISSION_VIEW_TRANSITION_TIME * MISSION_VIEW_TRANSITION_TIME) / MISSION_VIEW_TRANSITION_TIME;
+}
+
+void
+CGuiScreenMissionGallery::OnStartMission()
+{
+ CGuiScreenScrapBookContents* scrapBookContents = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( scrapBookContents != NULL );
+ unsigned int level = scrapBookContents->GetCurrentLevel();
+
+ int mission = m_selectedMission;
+
+ // special case for level 1 due to tutorial mission being m0
+ //
+ if( level == 0 )
+ {
+ mission++;
+ }
+
+ GetCharacterSheetManager()->SetCurrentMission( static_cast<RenderEnums::LevelEnum>( level ),
+ static_cast<RenderEnums::MissionEnum>( mission ) );
+
+ // hide mission info layer
+ //
+ rAssert( m_missionInfo != NULL );
+ m_missionInfo->SetVisible( false );
+
+ if( m_isMissionImagesLoaded )
+ {
+ this->UnloadMissionImages();
+ }
+
+ // send message to front-end manager to quit front-end and
+ // start single player story mode
+ //
+ m_pParent->HandleMessage( GUI_MSG_QUIT_FRONTEND, 1 ); // 1 = one player
+}
+
+int
+CGuiScreenMissionGallery::LoadMissionImages()
+{
+ // load mission bitmaps for current level
+ //
+ CGuiScreenScrapBookContents* scrapBookContents = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( scrapBookContents != NULL );
+
+ int numImagesToLoad = 0;
+
+ rAssert( m_pMenu != NULL );
+
+ for( int i = 0; i < NUM_MISSIONS_PER_LEVEL; i++ )
+ {
+ Scrooby::BoundedDrawable* missionImage = m_pMenu->GetMenuItem( i )->GetItem();
+ rAssert( missionImage != NULL );
+ missionImage->SetAlpha( 1.0f ); // restore alpha by default
+
+ bool isUnlocked = false;
+
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_MISSIONS ) )
+ {
+ isUnlocked = true;
+ }
+ else
+ {
+ if( i == NUM_MISSIONS_PER_LEVEL - 1 )
+ {
+ isUnlocked = GetCharacterSheetManager()->QueryBonusMissionCompleted( static_cast<RenderEnums::LevelEnum>( scrapBookContents->GetCurrentLevel() ) );
+ }
+ else
+ {
+ int missionIndex = scrapBookContents->GetCurrentLevel() == 0 ? i + 1 : i;
+ MissionRecord* pMissionRecord = GetCharacterSheetManager()->QueryMissionStatus( static_cast<RenderEnums::LevelEnum>( scrapBookContents->GetCurrentLevel() ), missionIndex );
+ rAssert( pMissionRecord != NULL );
+ if( pMissionRecord->mCompleted )
+ {
+ isUnlocked = true;
+ }
+ else if( pMissionRecord->mSkippedMission )
+ {
+ isUnlocked = true;
+
+ missionImage->SetAlpha( 0.5f ); // 50% alpha for skipped missions
+ }
+ }
+ }
+
+ m_pMenu->SetMenuItemEnabled( i, isUnlocked );
+
+ if( isUnlocked )
+ {
+ char filename[ 64 ];
+ sprintf( filename, "%smis%02d_%02d.p3d",
+ MISSION_GALLERY_IMAGES_DIR,
+ scrapBookContents->GetCurrentLevel() + 1,
+ i + 1 );
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ filename,
+ GMA_LEVEL_FE,
+ MISSION_GALLERY_INVENTORY_SECTION,
+ MISSION_GALLERY_INVENTORY_SECTION );
+
+ numImagesToLoad++;
+ }
+ }
+
+ if( numImagesToLoad > 0 )
+ {
+ GetLoadingManager()->AddCallback( this );
+
+ // hide images until they're loaded
+ //
+ m_menuGroup->SetAlpha( 0.0f );
+
+ m_numTransitionsPending++;
+ }
+
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, numImagesToLoad > 0 );
+
+ return numImagesToLoad;
+}
+
+void
+CGuiScreenMissionGallery::UnloadMissionImages()
+{
+ p3d::pddi->DrawSync();
+
+ // clear all mission images
+ //
+ rAssert( m_pMenu != NULL );
+ for( int i = 0; i < NUM_MISSIONS_PER_LEVEL; i++ )
+ {
+ Scrooby::Sprite* missionImage = dynamic_cast<Scrooby::Sprite*>( m_pMenu->GetMenuItem( i )->GetItem() );
+ rAssert( missionImage != NULL );
+ missionImage->SetRawSprite( NULL, true );
+ missionImage->ResetTransformation();
+
+ m_pMenu->SetMenuItemEnabled( i, false );
+ }
+
+ // unload mission bitmaps
+ //
+ p3d::inventory->RemoveSectionElements( MISSION_GALLERY_INVENTORY_SECTION );
+
+ m_isMissionImagesLoaded = false;
+}
+
+#ifdef RAD_WIN32
+void CGuiScreenMissionGallery::SetVisibilityForAllOtherMenuItems( bool bVisible )
+{
+ for( int i = 0; i < NUM_MISSIONS_PER_LEVEL; i++ )
+ {
+ if( i != m_selectedMission )
+ m_pMenu->GetMenuItem(i)->GetItem()->SetVisible( bVisible );
+ }
+}
+#endif
diff --git a/game/code/presentation/gui/frontend/guiscreenmissiongallery.h b/game/code/presentation/gui/frontend/guiscreenmissiongallery.h
new file mode 100644
index 0000000..7f1a112
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenmissiongallery.h
@@ -0,0 +1,108 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMissionGallery
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/19 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMISSIONGALLERY_H
+#define GUISCREENMISSIONGALLERY_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <loading/loadingmanager.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu2D;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMissionGallery : public CGuiScreen,
+ public LoadingManager::ProcessRequestsCallback
+{
+public:
+ CGuiScreenMissionGallery( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMissionGallery();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+ // Implements LoadingManager::ProcessRequestsCallback
+ //
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void OnUpdate( unsigned int elapsedTime );
+ void SetMenuAlpha( float alpha );
+ void OnMenuSelectionMade( int selection );
+ void OnStartMission();
+#ifdef RAD_WIN32
+ void SetVisibilityForAllOtherMenuItems( bool bDisable );
+#endif
+
+ int LoadMissionImages();
+ void UnloadMissionImages();
+
+ enum eMenuItem
+ {
+ MISSION_1,
+ MISSION_2,
+ MISSION_3,
+ MISSION_4,
+ MISSION_5,
+ MISSION_6,
+ MISSION_7,
+ MISSION_8,
+
+ NUM_MISSIONS_PER_LEVEL
+ };
+
+ CGuiMenu2D* m_pMenu;
+ Scrooby::Group* m_menuGroup;
+
+ bool m_isMissionImagesLoaded : 1;
+
+ enum eScreenState
+ {
+ SCREEN_STATE_NORMAL,
+ SCREEN_STATE_GOTO_VIEW,
+ SCREEN_STATE_VIEWING,
+ SCREEN_STATE_BACK_VIEW,
+
+ NUM_SCREEN_STATES
+ };
+
+ eScreenState m_screenState;
+ unsigned int m_elapsedTime;
+ rmt::Vector m_projectileVelocity;
+
+ Scrooby::Layer* m_missionInfo;
+ Scrooby::Text* m_missionNum;
+ Scrooby::Text* m_missionTitle;
+
+ int m_selectedMission;
+
+};
+
+#endif // GUISCREENMISSIONGALLERY_H
diff --git a/game/code/presentation/gui/frontend/guiscreenmultichoosechar.cpp b/game/code/presentation/gui/frontend/guiscreenmultichoosechar.cpp
new file mode 100644
index 0000000..3370019
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenmultichoosechar.cpp
@@ -0,0 +1,349 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMultiChooseChar
+//
+// Description: Implementation of the CGuiScreenMultiChooseChar class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenmultichoosechar.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guisystem.h>
+
+#include <memory/srrmemory.h>
+
+#include <raddebug.hpp> // Foundation
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+/*
+static const char* MENU_ITEMS[] =
+{
+ "Character"
+};
+
+static const int NUM_MENU_ITEMS = sizeof( MENU_ITEMS ) / sizeof( MENU_ITEMS[ 0 ] );
+
+/*
+static const char* MENU_CURSOR = "Cursor";
+*/
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMultiChooseChar::CGuiScreenMultiChooseChar
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMultiChooseChar::CGuiScreenMultiChooseChar
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+:
+ CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_MULTIPLAYER_CHOOSE_CHARACTER )
+{
+ memset( m_pMenu, 0, sizeof( m_pMenu ) );
+
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage;
+ pPage = m_pScroobyScreen->GetPage( "MultiChooseCharacter" );
+ rAssert( pPage );
+
+ // Get character selection for all players
+ //
+ char itemName[ 32 ];
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ sprintf( itemName, "Character%d", i );
+ m_pCharacter[ i ] = pPage->GetSprite( itemName );
+
+ rAssert( m_pCharacter[ i ] );
+ }
+
+/*
+ Scrooby::Text* pText = NULL;
+ char menuItemName[ 32 ];
+
+ // Create and add menu items for all players
+ //
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ m_pMenu[ i] = new(GMA_LEVEL_FE) CGuiMenu( this, NUM_MENU_ITEMS );
+ rAssert( m_pMenu[ i ] != NULL );
+
+ for( int j = 0; j < NUM_MENU_ITEMS; j++ )
+ {
+ sprintf( menuItemName, "%s%d", MENU_ITEMS[ j ], i );
+ pText = pPage->GetText( menuItemName );
+ rAssert( pText );
+
+ m_pMenu[ i ]->AddMenuItem( pText );
+ }
+ }
+
+/*
+ // Set menu cursor
+ //
+ Scrooby::Sprite* pSprite = pPage->GetSprite( MENU_CURSOR );
+ rAssert( pSprite != NULL );
+ m_pMenu->SetCursor( pSprite );
+*/
+}
+
+
+//===========================================================================
+// CGuiScreenMultiChooseChar::~CGuiScreenMultiChooseChar
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMultiChooseChar::~CGuiScreenMultiChooseChar()
+{
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ if( m_pMenu[ i ] != NULL )
+ {
+ delete m_pMenu[ i ];
+ m_pMenu[ i ] = NULL;
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMultiChooseChar::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiChooseChar::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ // send message to front-end manager to quit front-end and
+ // start multiplayer head-to-head
+ //
+ m_pParent->HandleMessage( GUI_MSG_QUIT_FRONTEND, 2 ); // 2 = two players
+
+ this->StartTransitionAnimation( 600, 640 );
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ this->StartTransitionAnimation( 420, 450 );
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( this->IsControllerMessage( message ) )
+ {
+ int controllerId = param1;
+/*
+ // register player 2, if not already done so and if controller ID different from player 1
+ if( GetGuiSystem()->GetControllerId( PLAYER_2 ) == -1 &&
+ GetGuiSystem()->GetControllerId( PLAYER_1 ) != controllerId )
+ {
+ GetGuiSystem()->RegisterControllerId( PLAYER_2, controllerId );
+ }
+*/
+ // send controller messages only to corresponding player menu
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ if( GetGuiSystem()->GetControllerId( static_cast<ePlayer>( i ) ) == controllerId )
+ {
+/*
+ rAssert( m_pMenu[ i ] != NULL );
+ m_pMenu[ i ]->HandleMessage( message, param1, param2 );
+*/
+ this->HandleControllerMessage( i, message );
+
+ break;
+ }
+ }
+ }
+/*
+ else
+ {
+ // send all other messages to all menus
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ rAssert( m_pMenu[ i ] != NULL );
+ m_pMenu[ i ]->HandleMessage( message, param1, param2 );
+ }
+ }
+*/
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenMultiChooseChar::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiChooseChar::InitIntro()
+{
+ // get character selection (for all players)
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMultiChooseChar::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiChooseChar::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenMultiChooseChar::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiChooseChar::InitOutro()
+{
+ // set character selection (for all players)
+ for( int i = 0; i < MAX_PLAYERS; i++ )
+ {
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMultiChooseChar::HandleControllerMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiChooseChar::HandleControllerMessage( int player, eGuiMessage message )
+{
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_LEFT:
+ {
+ // decrement character selection
+ rAssert( m_pCharacter[ player ] );
+
+ int newIndex = m_pCharacter[ player ]->GetIndex() - 1;
+ if( newIndex < 0 )
+ {
+ newIndex = m_pCharacter[ player ]->GetNumOfImages() - 1;
+ }
+
+ m_pCharacter[ player ]->SetIndex( newIndex );
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_RIGHT:
+ {
+ // increment character selection
+ rAssert( m_pCharacter[ player ] );
+
+ int newIndex = (m_pCharacter[ player ]->GetIndex() + 1) % m_pCharacter[ player ]->GetNumOfImages();
+
+ m_pCharacter[ player ]->SetIndex( newIndex );
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/frontend/guiscreenmultichoosechar.h b/game/code/presentation/gui/frontend/guiscreenmultichoosechar.h
new file mode 100644
index 0000000..b206e2c
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenmultichoosechar.h
@@ -0,0 +1,62 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMultiChooseChar
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMULTICHOOSECHAR_H
+#define GUISCREENMULTICHOOSECHAR_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+#include <constants/maxplayers.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+namespace Scrooby
+{
+ class Screen;
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMultiChooseChar : public CGuiScreen
+{
+public:
+ CGuiScreenMultiChooseChar( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMultiChooseChar();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ void HandleControllerMessage( int player, eGuiMessage message );
+
+private:
+ CGuiMenu* m_pMenu[ MAX_PLAYERS ];
+ Scrooby::Sprite* m_pCharacter[ MAX_PLAYERS ];
+
+};
+
+#endif // GUISCREENMULTICHOOSECHAR_H
diff --git a/game/code/presentation/gui/frontend/guiscreenmultisetup.cpp b/game/code/presentation/gui/frontend/guiscreenmultisetup.cpp
new file mode 100644
index 0000000..1fd3aff
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenmultisetup.cpp
@@ -0,0 +1,235 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMultiSetup
+//
+// Description: Implementation of the CGuiScreenMultiSetup class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/16 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenmultisetup.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guisystem.h>
+
+#include <memory/srrmemory.h>
+
+#include <raddebug.hpp> // Foundation
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+static const char* MULTI_SETUP_MENU_ITEMS[] =
+{
+ "NumFlags",
+ "Base"
+};
+
+static const int NUM_MULTI_SETUP_MENU_ITEMS = sizeof( MULTI_SETUP_MENU_ITEMS ) / sizeof( MULTI_SETUP_MENU_ITEMS[ 0 ] );
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMultiSetup::CGuiScreenMultiSetup
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMultiSetup::CGuiScreenMultiSetup
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+:
+ CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_MULTIPLAYER_SETUP ),
+ m_pMenu( NULL )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenMultiSetup" );
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage;
+ pPage = m_pScroobyScreen->GetPage( "MultiSetup" );
+ rAssert( pPage );
+
+ // Create a menu.
+ //
+ m_pMenu = new(GMA_LEVEL_FE) CGuiMenu( this, NUM_MULTI_SETUP_MENU_ITEMS );
+ rAssert( m_pMenu != NULL );
+
+ // Add menu items
+ //
+ Scrooby::Text* pText = NULL;
+ Scrooby::Text* pTextValue = NULL;
+ char itemValue[ 32 ];
+
+ for( int i = 0; i < NUM_MULTI_SETUP_MENU_ITEMS; i++ )
+ {
+ pText = pPage->GetText( MULTI_SETUP_MENU_ITEMS[ i ] );
+ rAssert( pText );
+
+ // if text value exists
+ sprintf( itemValue, "%s_Value", MULTI_SETUP_MENU_ITEMS[ i ] );
+ pTextValue = pPage->GetText( itemValue );
+
+ m_pMenu->AddMenuItem( pText, pTextValue );
+ }
+MEMTRACK_POP_GROUP();
+}
+
+
+//===========================================================================
+// CGuiScreenMultiSetup::~CGuiScreenMultiSetup
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMultiSetup::~CGuiScreenMultiSetup()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMultiSetup::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiSetup::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_MULTIPLAYER_CHOOSE_CHARACTER );
+
+ this->StartTransitionAnimation( 390, 420 );
+
+ // register controller ID for player 1
+ GetGuiSystem()->RegisterControllerId( PLAYER_1, param1 );
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ this->StartTransitionAnimation( 290, 320 );
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenMultiSetup::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiSetup::InitIntro()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenMultiSetup::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiSetup::InitRunning()
+{
+ // unregister all player controller ID's
+ GetGuiSystem()->UnregisterControllerId( ALL_PLAYERS );
+}
+
+
+//===========================================================================
+// CGuiScreenMultiSetup::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiSetup::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/frontend/guiscreenmultisetup.h b/game/code/presentation/gui/frontend/guiscreenmultisetup.h
new file mode 100644
index 0000000..41287fb
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenmultisetup.h
@@ -0,0 +1,58 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMultiSetup
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/16 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMULTISETUP_H
+#define GUISCREENMULTISETUP_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+namespace Scrooby
+{
+ class Screen;
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMultiSetup : public CGuiScreen
+{
+public:
+ CGuiScreenMultiSetup( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMultiSetup();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ CGuiMenu* m_pMenu;
+
+};
+
+#endif // GUISCREENMULTISETUP_H
diff --git a/game/code/presentation/gui/frontend/guiscreenoptions.cpp b/game/code/presentation/gui/frontend/guiscreenoptions.cpp
new file mode 100644
index 0000000..261b030
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenoptions.cpp
@@ -0,0 +1,609 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenOptions
+//
+// Description: Implementation of the CGuiScreenOptions class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenoptions.h>
+#include <presentation/gui/frontend/guiscreenplaymovie.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guiscreenmessage.h>
+#include <presentation/gui/guiscreenprompt.h>
+
+#include <cheats/cheatinputsystem.h>
+#include <cheats/cheats.h>
+#include <memory/srrmemory.h>
+
+#include <raddebug.hpp> // Foundation
+#include <layer.h>
+#include <page.h>
+#include <screen.h>
+#include <group.h>
+#include <text.h>
+
+#ifdef RAD_PS2
+ #include <main/ps2platform.h>
+#endif
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const unsigned int NUM_CHEAT_DISPLAY_LINES = 16;
+const int PROGRESSIVE_MODE_TEST_TIME = 5000; // in msec
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenOptions::CGuiScreenOptions
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenOptions::CGuiScreenOptions
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_OPTIONS ),
+ m_pMenu( NULL ),
+ m_cheatsPage( NULL ),
+ m_cheatsOverlay( NULL ),
+ m_startingCheatID( 0 ),
+ m_elapsedProgressiveModeTestTime( -1 )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenOptions" );
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "Options" );
+ rAssert( pPage != NULL );
+
+ // Create a menu.
+ //
+ m_pMenu = new CGuiMenu( this, NUM_MENU_ITEMS );
+ rAssert( m_pMenu != NULL );
+
+ // Add menu items
+ //
+ Scrooby::Group* pGroup = pPage->GetGroup( "Menu" );
+ rAssert( pGroup != NULL );
+
+#ifdef RAD_WIN32
+ m_pMenu->AddMenuItem( pGroup->GetText( "Display" ) );
+#endif
+ m_pMenu->AddMenuItem( pGroup->GetText( "Controller" ) );
+ m_pMenu->AddMenuItem( pGroup->GetText( "Sound" ) );
+ m_pMenu->AddMenuItem( pGroup->GetText( "ViewMovies" ) );
+ m_pMenu->AddMenuItem( pGroup->GetText( "ViewCredits" ) );
+
+#ifndef RAD_WIN32
+ Scrooby::Text* display = pGroup->GetText( "Display" );
+ if( display != NULL )
+ {
+ display->SetVisible( false );
+ }
+#endif
+
+ m_pMenu->AddMenuItem( pGroup->GetText( "DisplayMode" ),
+ pGroup->GetText( "DisplayMode_Value" ),
+ NULL,
+ NULL,
+ pGroup->GetSprite( "DisplayMode_ArrowL" ),
+ pGroup->GetSprite( "DisplayMode_ArrowR" ),
+ SELECTION_ENABLED | VALUES_WRAPPED | TEXT_OUTLINE_ENABLED );
+
+#ifdef RAD_DEMO
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_VIEW_MOVIES, false );
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_VIEW_CREDITS, false );
+#endif
+
+/*
+#ifdef RAD_RELEASE
+ // hide 'view movies' in release build
+ //
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_VIEW_MOVIES, false, true );
+#else
+ // center menu items
+ //
+ pGroup->ResetTransformation();
+ pGroup->Translate( 0, 30 );
+#endif
+*/
+
+ // TC: no more display mode menu item on PS2, this is now done during bootup
+ // w/ specific button combo pressed and a display prompt
+ //
+//#ifndef RAD_PS2
+ // hide display mode menu item for non-PS2 platforms
+ //
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_DISPLAY_MODE, false, true );
+
+ #ifndef RAD_WIN32
+ // re-center menu items
+ //
+ pGroup->ResetTransformation();
+ pGroup->Translate( 0, -30 );
+ #endif
+//#endif // !RAD_PS2
+
+ // get cheats page, if included
+ //
+ m_cheatsPage = m_pScroobyScreen->GetPage( "Cheats" );
+ if( m_cheatsPage != NULL )
+ {
+ m_cheatsOverlay = m_cheatsPage->GetLayerByIndex( 0 );
+ }
+
+ // register self as cheat callback
+ //
+ GetCheatInputSystem()->RegisterCallback( this );
+MEMTRACK_POP_GROUP( "CGUIScreenOptions" );
+}
+
+
+//===========================================================================
+// CGuiScreenOptions::~CGuiScreenOptions
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenOptions::~CGuiScreenOptions()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+
+ // unregister self as cheat callback
+ //
+ GetCheatInputSystem()->UnregisterCallback( this );
+}
+
+
+//===========================================================================
+// CGuiScreenOptions::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenOptions::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_MOTHER_OF_ALL_CHEATS ) )
+ {
+ if( m_cheatsOverlay != NULL )
+ {
+ // show/hide cheats overlay, depending on whether or not
+ // cheat input is activated on any of the controllers
+ //
+ bool isCheatInputActivated = false;
+ int numUserInputHandlers = GetGuiSystem()->GetNumUserInputHandlers();
+ for( int i = 0; i < numUserInputHandlers; i++ )
+ {
+ if( GetGuiSystem()->GetUserInputHandler( i ) != NULL &&
+ GetCheatInputSystem()->IsActivated( i ) )
+ {
+ isCheatInputActivated = true;
+
+ break;
+ }
+ }
+
+ m_cheatsOverlay->SetVisible( isCheatInputActivated );
+ }
+
+ if( message == GUI_MSG_CONTROLLER_RIGHT &&
+ GetCheatInputSystem()->IsActivated( param1 ) )
+ {
+ // toggle cheat list
+ //
+ m_startingCheatID += NUM_CHEAT_DISPLAY_LINES;
+ if( m_startingCheatID >= NUM_CHEATS )
+ {
+ // wrap to the beginning
+ m_startingCheatID = 0;
+ }
+
+ this->UpdateCheatsDisplay( m_startingCheatID );
+ }
+ }
+
+ if( this->IsControllerMessage( message ) &&
+ GetCheatInputSystem()->IsActivated( param1 ) )
+ {
+ // ignore all controller inputs when cheat input system is activated
+ return;
+ }
+
+ if( message == GUI_MSG_MENU_PROMPT_RESPONSE )
+ {
+ if( param1 == PROMPT_DISPLAY_PROGRESSIVE_SCAN_PS2 )
+ {
+ if( param2 == CGuiMenuPrompt::RESPONSE_YES )
+ {
+ // begin progressive scan test
+ //
+ this->ProgressiveModeTestBegin();
+ }
+ else
+ {
+ // don't switch display mode and return to options screen
+ //
+ this->ReloadScreen();
+ }
+ }
+ else if( param1 == PROMPT_DISPLAY_PROGRESSIVE_SCAN_CONFIRM )
+ {
+ if( param2 == CGuiMenuPrompt::RESPONSE_YES )
+ {
+#ifdef RAD_PS2
+ PS2Platform::GetInstance()->SetProgressiveMode( true );
+#endif
+ rTunePrintf( "Current display mode confirmed: PROGRESSIVE.\n" );
+ }
+ else
+ {
+ rTunePrintf( "Current display mode confirmed: INTERLACED.\n" );
+ }
+
+ // return to options screen
+ //
+ this->ReloadScreen();
+ }
+ else
+ rTuneAssert(!"not handled");
+ }
+ else if( message == GUI_MSG_MESSAGE_UPDATE || message == GUI_MSG_PROMPT_UPDATE ) // update from message/prompt screen
+ {
+ if( m_elapsedProgressiveModeTestTime != -1 )
+ {
+ m_elapsedProgressiveModeTestTime += static_cast<int>( param1 );
+
+ if( m_elapsedProgressiveModeTestTime > PROGRESSIVE_MODE_TEST_TIME )
+ {
+ this->OnProgressiveModeTestEnd();
+ }
+ }
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_MENU_SELECTION_CHANGED:
+ {
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, param1 != MENU_ITEM_DISPLAY_MODE );
+
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_VALUE_CHANGED:
+ {
+ if( param1 == MENU_ITEM_DISPLAY_MODE )
+ {
+ if( param2 == 1 ) // progressive mode
+ {
+ // display confirmation prompt
+ //
+ m_guiManager->DisplayPrompt( PROMPT_DISPLAY_PROGRESSIVE_SCAN_PS2, this );
+ }
+ else // interlaced mode
+ {
+#ifdef RAD_PS2
+ PS2Platform::GetInstance()->SetProgressiveMode( false );
+#endif
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ switch( param1 )
+ {
+ case MENU_ITEM_CONTROLLER:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_CONTROLLER );
+
+ this->StartTransitionAnimation( 530, 560 );
+
+ break;
+ }
+ case MENU_ITEM_SOUND:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_SOUND );
+
+ this->StartTransitionAnimation( 660, 690 );
+
+ break;
+ }
+ case MENU_ITEM_VIEW_MOVIES:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_VIEW_MOVIES );
+
+ this->StartTransitionAnimation( 914, 944 );
+
+ break;
+ }
+ case MENU_ITEM_VIEW_CREDITS:
+ {
+/*
+ rAssert( m_guiManager );
+ CGuiScreenPlayMovie* playMovieScreen = static_cast<CGuiScreenPlayMovie*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_VIEW_CREDITS ) );
+ rAssert( playMovieScreen );
+ playMovieScreen->SetMovieToPlay( MovieNames::CREDITS );
+*/
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_VIEW_CREDITS );
+
+ this->StartTransitionAnimation( 600, 630 );
+
+ break;
+ }
+#ifdef RAD_WIN32
+ case MENU_ITEM_DISPLAY:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_DISPLAY );
+
+ break;
+ }
+#endif
+ default:
+ {
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ this->StartTransitionAnimation( 110, 140 );
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenOptions::OnCheatEntered
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void
+CGuiScreenOptions::OnCheatEntered( eCheatID cheatID, bool isEnabled )
+{
+ this->UpdateCheatsDisplay( m_startingCheatID );
+
+ if( cheatID == CHEAT_ID_MOTHER_OF_ALL_CHEATS && !isEnabled )
+ {
+ if( m_cheatsOverlay != NULL )
+ {
+ m_cheatsOverlay->SetVisible( false );
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenOptions::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenOptions::InitIntro()
+{
+#ifndef RAD_E3
+ GetCheatInputSystem()->SetEnabled( true );
+#endif
+
+ rAssert( m_pMenu != NULL );
+ if( m_pMenu->GetSelection() == MENU_ITEM_DISPLAY_MODE )
+ {
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, false );
+ }
+
+#ifdef RAD_PS2
+ // update current display mode
+ //
+ m_pMenu->SetSelectionValue( MENU_ITEM_DISPLAY_MODE,
+ PS2Platform::GetInstance()->GetProgressiveMode() ? 1 : 0 );
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenOptions::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenOptions::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenOptions::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenOptions::InitOutro()
+{
+#ifndef RAD_E3
+ GetCheatInputSystem()->SetEnabled( false );
+#endif
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenOptions::UpdateCheatsDisplay( int cheatID )
+{
+ if( m_cheatsPage != NULL )
+ {
+ // update cheats info
+ //
+ CheatsDB* cheatsDB = GetCheatInputSystem()->GetCheatsDB();
+ rAssert( cheatsDB != NULL );
+
+ for( unsigned int i = 0; i < NUM_CHEAT_DISPLAY_LINES; i++ )
+ {
+ char name[ 32 ];
+ sprintf( name, "Cheat%d", i );
+ Scrooby::Text* pText = m_cheatsPage->GetText( name );
+ rAssert( pText != NULL );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+
+ if( cheatID < NUM_CHEATS )
+ {
+ char cheatInfo[ 64 ];
+
+ const Cheat* cheat = cheatsDB->GetCheat( static_cast<eCheatID>( cheatID ) );
+ if( cheat != NULL )
+ {
+ CheatsDB::PrintCheatInfo( cheat, cheatInfo );
+
+ if( GetCheatInputSystem()->IsCheatEnabled( static_cast<eCheatID>( cheatID ) ) )
+ {
+ strcat( cheatInfo, " (!)" ); // exclamation mark denotes cheat enabled
+ }
+ }
+ else
+ {
+ // display un-registered cheat
+ //
+ sprintf( cheatInfo,
+ "Cheat ID [%02d]: *** Not Registered! ***", cheatID );
+/*
+ // ignore/skip un-registered cheat and don't display anything
+ //
+ pText->SetString( 0, " " );
+ i--;
+*/
+ }
+
+ pText->SetString( 0, cheatInfo );
+ }
+ else
+ {
+ pText->SetString( 0, " " );
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_FE );
+
+ cheatID++;
+ }
+ }
+}
+
+void
+CGuiScreenOptions::ProgressiveModeTestBegin()
+{
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_PROGRESSIVE_SCAN_TEST, this );
+ m_elapsedProgressiveModeTestTime = 0;
+
+ rTunePrintf( "Testing progressive scan display ... ...\n" );
+
+#ifdef RAD_PS2
+ PS2Platform::GetInstance()->SetProgressiveMode( true );
+#endif
+}
+
+void
+CGuiScreenOptions::OnProgressiveModeTestEnd()
+{
+ m_guiManager->DisplayPrompt( PROMPT_DISPLAY_PROGRESSIVE_SCAN_CONFIRM, this );
+ m_elapsedProgressiveModeTestTime = -1;
+
+ rTunePrintf( "Progressive scan test completed.\n" );
+
+#ifdef RAD_PS2
+ PS2Platform::GetInstance()->SetProgressiveMode( false );
+#endif
+}
+
diff --git a/game/code/presentation/gui/frontend/guiscreenoptions.h b/game/code/presentation/gui/frontend/guiscreenoptions.h
new file mode 100644
index 0000000..a625163
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenoptions.h
@@ -0,0 +1,82 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenOptions
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENOPTIONS_H
+#define GUISCREENOPTIONS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <cheats/cheatinputsystem.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenOptions : public CGuiScreen,
+ public ICheatEnteredCallback
+{
+public:
+ CGuiScreenOptions( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenOptions();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual void OnCheatEntered( eCheatID cheatID, bool isEnabled );
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void UpdateCheatsDisplay( int cheatID );
+
+ void ProgressiveModeTestBegin();
+ void OnProgressiveModeTestEnd();
+
+ enum eMenuItem
+ {
+#ifdef RAD_WIN32
+ MENU_ITEM_DISPLAY,
+#endif
+ MENU_ITEM_CONTROLLER,
+ MENU_ITEM_SOUND,
+ MENU_ITEM_VIEW_MOVIES,
+ MENU_ITEM_VIEW_CREDITS,
+ MENU_ITEM_DISPLAY_MODE,
+
+ NUM_MENU_ITEMS
+ };
+
+ CGuiMenu* m_pMenu;
+
+ Scrooby::Page* m_cheatsPage;
+ Scrooby::Layer* m_cheatsOverlay;
+ int m_startingCheatID;
+
+ int m_elapsedProgressiveModeTestTime;
+
+};
+
+#endif // GUISCREENOPTIONS_H
diff --git a/game/code/presentation/gui/frontend/guiscreenplaymovie.cpp b/game/code/presentation/gui/frontend/guiscreenplaymovie.cpp
new file mode 100644
index 0000000..ca74357
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenplaymovie.cpp
@@ -0,0 +1,460 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPlayMovie
+//
+// Description: Implementation of the CGuiScreenPlayMovie class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenplaymovie.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/utility/specialfx.h>
+
+#include <presentation/presentation.h>
+#include <presentation/fmvplayer/fmvplayer.h>
+#include <presentation/fmvplayer/fmvuserinputhandler.h>
+
+#include <memory/srrmemory.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/renderlayer.h>
+
+#include <screen.h>
+#include <page.h>
+#include <text.h>
+
+#include <p3d/view.hpp>
+
+#include <raddebug.hpp> // Foundation
+#include <radmovie2.hpp>
+
+#include <sound/soundmanager.h>
+
+#include <input/inputmanager.h>
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenPlayMovie::CGuiScreenPlayMovie
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPlayMovie::CGuiScreenPlayMovie
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent,
+ eGuiWindowID windowID
+)
+: CGuiScreen( pScreen, pParent, windowID ),
+ m_demoText( NULL ),
+ m_elapsedTime( 0 ),
+ m_IsSkippable(true)
+// m_tvFrame( NULL )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "Demo" );
+ if( pPage != NULL )
+ {
+ m_demoText = pPage->GetText( "Demo" );
+ rAssert( m_demoText != NULL );
+ }
+
+/*
+ // background TV frame layer
+ //
+ m_tvFrame = pPage->GetLayer( "TVFrame" );
+*/
+
+ if( windowID == GUI_SCREEN_ID_PLAY_MOVIE )
+ {
+ // invert fading; ie. fade out during transition in, and vice versa
+ //
+ m_inverseFading = true;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenPlayMovie::~CGuiScreenPlayMovie
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPlayMovie::~CGuiScreenPlayMovie()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenPlayMovie::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPlayMovie::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ GetPresentationManager()->Update( param1 );
+
+ if( m_demoText != NULL )
+ {
+ m_elapsedTime += param1;
+
+ const unsigned int BLINK_PERIOD = 250;
+ bool blinked = GuiSFX::Blink( m_demoText,
+ (float)m_elapsedTime,
+ (float)BLINK_PERIOD );
+ if( blinked )
+ {
+ m_elapsedTime %= BLINK_PERIOD;
+ }
+ }
+/*
+ if( m_state == GUI_WINDOW_STATE_OUTRO )
+ {
+ if( m_numTransitionsPending <= 0 )
+ {
+ // restore the background TV frame
+ //
+ if( m_tvFrame != NULL )
+ {
+ m_tvFrame->SetVisible( true );
+ }
+ }
+ }
+*/
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ // don't allow user to back out of screen, must be done
+ // by skipping movie
+ //
+ return;
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+void
+CGuiScreenPlayMovie::SetMovieToPlay( const char* movieName, bool IsSkippable, bool KillMusic, bool IsLocalized )
+{
+ rAssert( movieName != NULL );
+
+ FMVEvent* pEvent = NULL;
+ GetPresentationManager()->QueueFMV( &pEvent, this );
+
+ strcpy( pEvent->fileName, movieName );
+ pEvent->SetRenderLayer( RenderEnums::PresentationSlot );
+ pEvent->SetAutoPlay( false );
+ pEvent->SetClearWhenDone( true );
+ pEvent->SetSkippable(IsSkippable);
+
+#ifdef PAL
+ if( IsLocalized )
+ {
+ switch( CGuiTextBible::GetCurrentLanguage() )
+ {
+ case Scrooby::XL_FRENCH:
+ {
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_FRENCH );
+
+ break;
+ }
+ case Scrooby::XL_GERMAN:
+ {
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_GERMAN );
+
+ break;
+ }
+ case Scrooby::XL_SPANISH:
+ {
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_SPANISH );
+
+ break;
+ }
+ default:
+ {
+ rAssert( CGuiTextBible::GetCurrentLanguage() == Scrooby::XL_ENGLISH );
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_ENGLISH );
+
+ break;
+ }
+ }
+ }
+ else
+#endif // PAL
+ {
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_ENGLISH );
+ }
+
+ if( KillMusic )
+ {
+ pEvent->KillMusic();
+ }
+}
+
+
+//=============================================================================
+// CGuiScreenPlayMovie::OnPresentationEventBegin
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( PresentationEvent* pEvent )
+//
+// Return: void
+//
+//=============================================================================
+void CGuiScreenPlayMovie::OnPresentationEventBegin( PresentationEvent* pEvent )
+{
+ GetSoundManager()->StopForMovie();
+}
+
+
+//=============================================================================
+// CGuiScreenPlayMovie::OnPresentationEventLoadComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( PresentationEvent* pEvent )
+//
+// Return: void
+//
+//=============================================================================
+void CGuiScreenPlayMovie::OnPresentationEventLoadComplete( PresentationEvent* pEvent )
+{
+}
+
+
+//=============================================================================
+// CGuiScreenPlayMovie::OnPresentationEventEnd
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( PresentationEvent* pEvent )
+//
+// Return: void
+//
+//=============================================================================
+void CGuiScreenPlayMovie::OnPresentationEventEnd( PresentationEvent* pEvent )
+{
+ if( GetPresentationManager()->IsQueueEmpty() )
+ {
+ GetSoundManager()->ResumeAfterMovie();
+
+ if( m_ID == GUI_SCREEN_ID_PLAY_MOVIE_INTRO )
+ {
+ // finished playing intro movie, now go to main menu
+ //
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ GUI_SCREEN_ID_INTRO_TRANSITION,
+ CLEAR_WINDOW_HISTORY );
+ }
+ else if( m_ID == GUI_SCREEN_ID_PLAY_MOVIE_NEW_GAME )
+ {
+ // finished playing new game movie, now quit the FE
+ //
+ m_pParent->HandleMessage( GUI_MSG_QUIT_FRONTEND, 1 ); // 1 = one player
+ }
+ else
+ {
+ rAssert( m_guiManager );
+ eGuiWindowID previousScreen = m_guiManager->GetPreviousScreen();
+ if( previousScreen == GUI_SCREEN_ID_VIEW_MOVIES )
+ {
+ this->StartTransitionAnimation( 1005, 1035 );
+ }
+ else if( previousScreen == GUI_SCREEN_ID_OPTIONS )
+ {
+ this->StartTransitionAnimation( 630, 660 );
+ }
+
+ m_pParent->HandleMessage( GUI_MSG_BACK_SCREEN );
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenPlayMovie::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPlayMovie::InitIntro()
+{
+ rAssertMsg( !GetPresentationManager()->IsQueueEmpty(),
+ "ERROR: *** No movies were queued for playing!" );
+
+ if( m_demoText != NULL )
+ {
+ m_demoText->SetVisible( false );
+ m_elapsedTime = 0;
+ }
+
+#ifdef RAD_WIN32
+ GetInputManager()->GetFEMouse()->SetInGameMode( true );
+#endif
+
+/*
+ // hide background TV frame so we can fade in/out the foreground one
+ //
+ if( m_tvFrame != NULL )
+ {
+ m_tvFrame->SetVisible( false );
+ }
+*/
+}
+
+
+//===========================================================================
+// CGuiScreenPlayMovie::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPlayMovie::InitRunning()
+{
+ if( m_ID == GUI_SCREEN_ID_PLAY_MOVIE_DEMO )
+ {
+ // disable screen clearing for GUI render layer
+ //
+ GetRenderManager()->mpLayer( RenderEnums::GUI )->pView( 0 )->SetClearMask( PDDI_BUFFER_DEPTH | PDDI_BUFFER_STENCIL );
+ }
+ else
+ {
+ GetRenderManager()->mpLayer( RenderEnums::GUI )->Chill();
+ }
+
+ GetPresentationManager()->GetFMVPlayer()->Play();
+
+#ifdef FINAL
+ if( m_ID == GUI_SCREEN_ID_PLAY_MOVIE_NEW_GAME )
+ {
+ // disable skipping of new game movie
+ //
+ FMVUserInputHandler* userInputHandler = GetPresentationManager()->GetFMVPlayer()->GetUserInputHandler();
+ rAssert( userInputHandler != NULL );
+ userInputHandler->SetEnabled( false );
+ }
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenPlayMovie::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPlayMovie::InitOutro()
+{
+ if( m_ID == GUI_SCREEN_ID_PLAY_MOVIE_DEMO )
+ {
+ // enable screen clearing
+ //
+ GetRenderManager()->mpLayer(RenderEnums::GUI)->pView( 0 )->SetClearMask( PDDI_BUFFER_ALL );
+ }
+ else
+ {
+ GetRenderManager()->mpLayer( RenderEnums::GUI )->Warm();
+ }
+
+#ifdef RAD_WIN32
+ GetInputManager()->GetFEMouse()->SetInGameMode( false );
+#endif
+
+#ifdef FINAL
+ if( m_ID == GUI_SCREEN_ID_PLAY_MOVIE_NEW_GAME )
+ {
+ // re-enable FMV user inputs
+ //
+ FMVUserInputHandler* userInputHandler = GetPresentationManager()->GetFMVPlayer()->GetUserInputHandler();
+ rAssert( userInputHandler != NULL );
+ userInputHandler->SetEnabled( true );
+ }
+#endif
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/frontend/guiscreenplaymovie.h b/game/code/presentation/gui/frontend/guiscreenplaymovie.h
new file mode 100644
index 0000000..40c8c3a
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenplaymovie.h
@@ -0,0 +1,70 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPlayMovie
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENPLAYMOVIE_H
+#define GUISCREENPLAYMOVIE_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <presentation/presevents/presentationevent.h>
+#include <constants/movienames.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenPlayMovie : public CGuiScreen,
+ public PresentationEvent::PresentationEventCallBack
+{
+public:
+ CGuiScreenPlayMovie( Scrooby::Screen* pScreen, CGuiEntity* pParent,
+ eGuiWindowID windowID = GUI_SCREEN_ID_PLAY_MOVIE );
+ virtual ~CGuiScreenPlayMovie();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ void SetMovieToPlay( const char* movieName, bool IsSkippable = true,
+ bool KillMusic = false,
+ bool IsLocalized = true );
+
+ // Implements PresentationEvent::PresentationEventCallBack
+ //
+ virtual void OnPresentationEventBegin( PresentationEvent* pEvent );
+ virtual void OnPresentationEventLoadComplete( PresentationEvent* pEvent );
+ virtual void OnPresentationEventEnd( PresentationEvent* pEvent );
+
+ void SetSkippable(bool IsSkippable) {m_IsSkippable = IsSkippable;}
+ bool GetSkippable(void) const {return m_IsSkippable;}
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ Scrooby::Text* m_demoText;
+ unsigned int m_elapsedTime;
+ bool m_IsSkippable : 1;
+};
+
+#endif // GUISCREENPLAYMOVIE_H
diff --git a/game/code/presentation/gui/frontend/guiscreenscrapbook.cpp b/game/code/presentation/gui/frontend/guiscreenscrapbook.cpp
new file mode 100644
index 0000000..c41b52d
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenscrapbook.cpp
@@ -0,0 +1,266 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenScrapBook
+//
+// Description: Implementation of the CGuiScreenScrapBook class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenscrapbook.h>
+#include <presentation/gui/guimenu.h>
+
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+// Scrooby
+//
+#include <screen.h>
+#include <page.h>
+#include <group.h>
+#include <text.h>
+
+// ATG
+//
+#include <raddebug.hpp>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenScrapBook::CGuiScreenScrapBook
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenScrapBook::CGuiScreenScrapBook
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_SCRAP_BOOK ),
+ m_pMenu( NULL ),
+ m_krustySticker( NULL )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "ScrapBook" );
+ rAssert( pPage != NULL );
+
+ // get krusty sticker overlay
+ //
+ m_krustySticker = pPage->GetGroup( "KrustySticker" );
+ rAssert( m_krustySticker != NULL );
+ m_krustySticker->SetVisible( false ); // hide by default
+
+#ifdef PAL
+ m_krustySticker->ScaleAboutPoint( 0.9f, 0, 0 );
+#endif
+
+ // create menu
+ //
+ m_pMenu = new CGuiMenu( this, NUM_MENU_ITEMS );
+ rAssert( m_pMenu != NULL );
+
+ // add menu items
+ //
+ Scrooby::Group* pGroup = pPage->GetGroup( "Menu" );
+ rAssert( pGroup != NULL );
+ m_pMenu->AddMenuItem( pGroup->GetText( "OpenBook" ) );
+ Scrooby::Text* gameStats = pGroup->GetText( "GameStats" );
+ m_pMenu->AddMenuItem( gameStats );
+
+#ifdef PAL
+ rAssert( gameStats != NULL );
+ gameStats->SetTextMode( Scrooby::TEXT_WRAP );
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenScrapBook::~CGuiScreenScrapBook
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenScrapBook::~CGuiScreenScrapBook()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenScrapBook::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenScrapBook::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ this->OnMenuSelectionMade( static_cast<eMenuItem>( param1 ) );
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ this->StartTransitionAnimation( 350, 380 );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ //
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenScrapBook::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenScrapBook::InitIntro()
+{
+ // update percent game complete
+ //
+ float percentComplete = GetCharacterSheetManager()->QueryPercentGameCompleted();
+
+ // show krusty sticker only if game percent complete is 100%
+ //
+ rAssert( m_krustySticker != NULL );
+ m_krustySticker->SetVisible( percentComplete > 99.999f );
+}
+
+
+//===========================================================================
+// CGuiScreenScrapBook::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenScrapBook::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenScrapBook::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenScrapBook::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenScrapBook::OnMenuSelectionMade( eMenuItem selection )
+{
+ switch( selection )
+ {
+ case MENU_OPEN_BOOK:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS );
+
+ break;
+ }
+ case MENU_GAME_STATS:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_SCRAP_BOOK_STATS );
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Invalid menu selection!" );
+
+ break;
+ }
+ }
+
+ this->StartTransitionAnimation( 810, 830 );
+}
+
diff --git a/game/code/presentation/gui/frontend/guiscreenscrapbook.h b/game/code/presentation/gui/frontend/guiscreenscrapbook.h
new file mode 100644
index 0000000..d18d988
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenscrapbook.h
@@ -0,0 +1,64 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenScrapBook
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENSCRAPBOOK_H
+#define GUISCREENSCRAPBOOK_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenScrapBook : public CGuiScreen
+{
+public:
+ CGuiScreenScrapBook( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenScrapBook();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ enum eMenuItem
+ {
+ MENU_OPEN_BOOK,
+ MENU_GAME_STATS,
+
+ NUM_MENU_ITEMS
+ };
+
+ void OnMenuSelectionMade( eMenuItem selection );
+
+ CGuiMenu* m_pMenu;
+ Scrooby::Group* m_krustySticker;
+
+};
+
+#endif // GUISCREENSCRAPBOOK_H
diff --git a/game/code/presentation/gui/frontend/guiscreenscrapbookcontents.cpp b/game/code/presentation/gui/frontend/guiscreenscrapbookcontents.cpp
new file mode 100644
index 0000000..b1daa4d
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenscrapbookcontents.cpp
@@ -0,0 +1,674 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenScrapBookContents
+//
+// Description: Implementation of the CGuiScreenScrapBookContents class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenscrapbookcontents.h>
+#include <presentation/gui/frontend/guiscreenplaymovie.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiuserinputhandler.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guitextbible.h>
+
+#include <cards/cardgallery.h>
+#include <events/eventmanager.h>
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/rewards/rewardsmanager.h>
+#include <render/enums/renderenums.h>
+
+// Scrooby
+//
+#include <screen.h>
+#include <page.h>
+#include <layer.h>
+#include <group.h>
+#include <text.h>
+#include <sprite.h>
+
+// ATG
+//
+#include <raddebug.hpp>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const char* MOVIES_INGAME[] =
+{
+ MovieNames::MOVIE2, // level 1
+ MovieNames::MOVIE3, // level 2
+ MovieNames::MOVIE8, // level 3 - I & S movie!
+ MovieNames::MOVIE4, // level 4
+ MovieNames::MOVIE5, // level 5
+ MovieNames::MOVIE6, // level 6
+ MovieNames::MOVIE7, // level 7
+
+ "" // dummy terminator
+};
+
+const float LEVEL_BAR_CORRECTION_SCALE = 2.0f;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenScrapBookContents::CGuiScreenScrapBookContents
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenScrapBookContents::CGuiScreenScrapBookContents
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ),
+ m_pMenu( NULL ),
+ m_levelBarGroup( NULL ),
+ m_levelSelection( NULL ),
+ m_LTrigger( NULL ),
+ m_RTrigger( NULL ),
+ m_LTriggerBgd( NULL ),
+ m_RTriggerBgd( NULL ),
+ m_currentLevel( 0 ),
+ m_sparkles( NULL )
+{
+ memset( m_menuImages, 0, sizeof( m_menuImages ) );
+
+ // Retrieve the Scrooby drawing elements (from LevelBar.pag)
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "LevelBar" );
+ rAssert( pPage != NULL );
+
+ m_levelBarGroup = pPage->GetGroup( "LevelBar" );
+ rAssert( m_levelBarGroup != NULL );
+
+ // apply image correction scale
+ //
+ Scrooby::Sprite* levelBarBgd = m_levelBarGroup->GetSprite( "LevelBar" );
+ if( levelBarBgd != NULL )
+ {
+ levelBarBgd->ResetTransformation();
+ levelBarBgd->ScaleAboutCenter( LEVEL_BAR_CORRECTION_SCALE );
+ }
+
+ // get level bar elements
+ //
+ m_levelSelection = m_levelBarGroup->GetText( "Level" );
+ rAssert( m_levelSelection != NULL );
+ m_levelSelection->ResetTransformation();
+ m_levelSelection->ScaleAboutCenter( 0.75f );
+
+ m_LTrigger = m_levelBarGroup->GetGroup( "LTrigger" );
+ rAssert( m_LTrigger != NULL );
+
+ m_RTrigger = m_levelBarGroup->GetGroup( "RTrigger" );
+ rAssert( m_RTrigger != NULL );
+
+#ifdef RAD_PS2
+ // PS2 only, scale up L1 and R1 button images
+ //
+ m_LTrigger->ResetTransformation();
+ m_LTrigger->ScaleAboutCenter( 1.25f, 1.15f, 1.0f );
+ m_RTrigger->ResetTransformation();
+ m_RTrigger->ScaleAboutCenter( 1.25f, 1.15f, 1.0f );
+#endif
+
+#ifdef RAD_XBOX
+ switch( CGuiTextBible::GetCurrentLanguage() )
+ {
+ case Scrooby::XL_FRENCH:
+ {
+ m_LTrigger->GetSprite( "LTrigger" )->SetIndex( 2 );
+ m_RTrigger->GetSprite( "RTrigger" )->SetIndex( 1 );
+
+ break;
+ }
+ case Scrooby::XL_SPANISH:
+ {
+ m_LTrigger->GetSprite( "LTrigger" )->SetIndex( 1 );
+ m_RTrigger->GetSprite( "RTrigger" )->SetIndex( 1 );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+#endif // RAD_XBOX
+
+ m_LTriggerBgd = m_LTrigger->GetSprite( "LTriggerBgd" );
+ m_RTriggerBgd = m_RTrigger->GetSprite( "RTriggerBgd" );
+
+#ifdef RAD_WIN32
+ m_LTriggerBgd->ScaleAboutCenter( 0.5 );
+ m_RTriggerBgd->ScaleAboutCenter( 0.5 );
+#endif
+
+ // Retrieve the Scrooby drawing elements (from ScrapBookContents.pag)
+ //
+ pPage = m_pScroobyScreen->GetPage( "ScrapBookContents" );
+ rAssert( pPage != NULL );
+
+ // create a 2D menu
+ //
+ m_pMenu = new CGuiMenu2D( this, NUM_MENU_ITEMS, 3 );
+ rAssert( m_pMenu != NULL );
+
+ // add menu items
+ //
+ Scrooby::Group* pGroup = pPage->GetGroup( "Menu" );
+ rAssert( pGroup != NULL );
+ m_pMenu->AddMenuItem( pGroup->GetText( "StoryMissions" ),
+ pGroup->GetText( "StoryMissions_Unlocked" ) );
+ m_pMenu->AddMenuItem( pGroup->GetText( "CharacterClothing" ),
+ pGroup->GetText( "CharacterClothing_Unlocked" ) );
+ m_pMenu->AddMenuItem( pGroup->GetText( "Vehicles" ),
+ pGroup->GetText( "Vehicles_Unlocked" ) );
+ m_pMenu->AddMenuItem( pGroup->GetText( "Dummy" ),
+ NULL, NULL, NULL, NULL, NULL, ALL_ATTRIBUTES_OFF );
+ m_pMenu->AddMenuItem( pGroup->GetText( "CollectorCards" ),
+ pGroup->GetText( "CollectorCards_Unlocked" ) );
+ m_pMenu->AddMenuItem( pGroup->GetText( "Dummy" ),
+ NULL, NULL, NULL, NULL, NULL, ALL_ATTRIBUTES_OFF );
+// m_pMenu->AddMenuItem( pGroup->GetText( "Movies" ),
+// pGroup->GetText( "Movies_Unlocked" ) );
+
+ // get menu images
+ //
+ m_menuImages[ MENU_STORY_MISSIONS ] = pGroup->GetSprite( "StoryMissions" );
+ m_menuImages[ MENU_CHARACTER_CLOTHING ] = pGroup->GetSprite( "CharacterClothing" );
+ m_menuImages[ MENU_VEHICLES ] = pGroup->GetSprite( "Vehicles" );
+ m_menuImages[ MENU_COLLECTOR_CARDS ] = pGroup->GetSprite( "CollectorCards" );
+// m_menuImages[ MENU_MOVIES ] = pGroup->GetSprite( "Movies" );
+
+ // shrink down bottom row images
+ //
+ const float MENU_IMAGE_SCALE = 0.75f;
+ rAssert( m_menuImages[ MENU_COLLECTOR_CARDS ] != NULL );
+ m_menuImages[ MENU_COLLECTOR_CARDS ]->ResetTransformation();
+ m_menuImages[ MENU_COLLECTOR_CARDS ]->ScaleAboutCenter( MENU_IMAGE_SCALE );
+
+ // get sparkles overlay
+ //
+ pPage = m_pScroobyScreen->GetPage( "3dFE" );
+ rAssert( pPage != NULL );
+ m_sparkles = pPage->GetLayer( "ScrapBookSparkles" );
+}
+
+//===========================================================================
+// CGuiScreenScrapBookContents::~CGuiScreenScrapBookContents
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenScrapBookContents::~CGuiScreenScrapBookContents()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+#ifdef RAD_WIN32
+//===========================================================================
+// CGuiScreenScrapBookContents::CheckCursorAgainstHotspots
+//===========================================================================
+// Description: Checks cursor position against its list of hotspots.
+//
+// Constraints: None.
+//
+// Parameters: float x - The x position of cursor in P3D coordinates.
+// float y - The y position of cursor in P3D coordinates.
+//
+// Return: N/A.
+//
+//===========================================================================
+eFEHotspotType CGuiScreenScrapBookContents::CheckCursorAgainstHotspots( float x, float y )
+{
+ eFEHotspotType hotSpotType = CGuiScreen::CheckCursorAgainstHotspots( x, y );
+ if( hotSpotType == HOTSPOT_NONE )
+ {
+ if( m_LTriggerBgd )
+ {
+ if( m_LTriggerBgd->IsPointInBoundingRect( x, y ) )
+ {
+ hotSpotType = HOTSPOT_LTRIGGER;
+ }
+ }
+ if( m_RTriggerBgd )
+ {
+ if( m_RTriggerBgd->IsPointInBoundingRect( x, y ) )
+ {
+ hotSpotType = HOTSPOT_RTRIGGER;
+ }
+ }
+
+ }
+ return hotSpotType;
+}
+#endif
+
+//===========================================================================
+// CGuiScreenScrapBookContents::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenScrapBookContents::HandleMessage( eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2 )
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ // toggle L/R trigger icon backgrounds
+ //
+ if( m_LTriggerBgd != NULL && m_RTriggerBgd != NULL )
+ {
+ m_LTriggerBgd->SetIndex( 0 );
+ m_RTriggerBgd->SetIndex( 0 );
+
+ if( !m_pMenu->HasSelectionBeenMade() )
+ {
+ int numUserInputHandlers = GetGuiSystem()->GetNumUserInputHandlers();
+ for( int i = 0; i < numUserInputHandlers; i++ )
+ {
+ CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( i );
+ if( userInputHandler != NULL )
+ {
+ if( userInputHandler->IsButtonDown( GuiInput::L1 )
+#ifdef RAD_WIN32
+ || GetInputManager()->GetFEMouse()->LeftButtonDownOn() == HOTSPOT_LTRIGGER
+#endif
+ )
+ {
+ rAssert( m_LTriggerBgd->GetNumOfImages() > 1 );
+ m_LTriggerBgd->SetIndex( 1 );
+ }
+
+ if( userInputHandler->IsButtonDown( GuiInput::R1 )
+#ifdef RAD_WIN32
+ || GetInputManager()->GetFEMouse()->LeftButtonDownOn() == HOTSPOT_RTRIGGER
+#endif
+ )
+ {
+ rAssert( m_RTriggerBgd->GetNumOfImages() > 1 );
+ m_RTriggerBgd->SetIndex( 1 );
+ }
+ }
+ }
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_L1:
+ {
+ if( !m_pMenu->HasSelectionBeenMade() )
+ {
+ // decrement level selection
+ //
+ this->OnLevelSelectionChange( -1 );
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_R1:
+ {
+ if( !m_pMenu->HasSelectionBeenMade() )
+ {
+ // increment level selection
+ //
+ this->OnLevelSelectionChange( +1 );
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ }
+
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ this->OnMenuSelectionMade( static_cast<eMenuItem>( param1 ) );
+
+ break;
+ }
+/*
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ if( m_currentLevel == RenderEnums::L3 &&
+ m_pMenu->GetSelection() == MENU_MOVIES )
+ {
+ GetInputManager()->RegisterControllerID( 0, param1 );
+ }
+
+ break;
+ }
+*/
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ this->StartTransitionAnimation( 830, 850 );
+
+ // hide sparkles
+ //
+ if( m_sparkles != NULL )
+ {
+ m_sparkles->SetVisible( false );
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ //
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+//===========================================================================
+// CGuiScreenScrapBookContents::SetLevelBarVisible
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenScrapBookContents::SetLevelBarVisible( bool isVisible )
+{
+ rAssert( m_levelBarGroup != NULL );
+ m_levelBarGroup->SetVisible( isVisible );
+}
+
+//===========================================================================
+// CGuiScreenScrapBookContents::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenScrapBookContents::InitIntro()
+{
+ // this is needed here to update all the unlocked n/N values
+ //
+ this->OnLevelSelectionChange( 0 );
+
+ // show L/R trigger buttons
+ //
+ rAssert( m_LTrigger != NULL && m_RTrigger != NULL );
+ m_LTrigger->SetVisible( true );
+ m_RTrigger->SetVisible( true );
+}
+
+//===========================================================================
+// CGuiScreenScrapBookContents::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenScrapBookContents::InitRunning()
+{
+ // show sparkles only if game percent complete is 100%
+ //
+ float percentComplete = GetCharacterSheetManager()->QueryPercentGameCompleted();
+ if( m_sparkles != NULL )
+ {
+ // TC: [TODO] verify sparkles look OK
+ //
+// m_sparkles->SetVisible( percentComplete == 100.0f );
+ }
+}
+
+//===========================================================================
+// CGuiScreenScrapBookContents::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenScrapBookContents::InitOutro()
+{
+}
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenScrapBookContents::OnLevelSelectionChange( int delta )
+{
+ m_currentLevel = (m_currentLevel + RenderEnums::numLevels + delta) % RenderEnums::numLevels;
+ rAssert( m_levelSelection );
+
+ // update level selection bar
+ //
+ m_levelSelection->SetIndex( m_currentLevel );
+
+ // update menu images and unlocked values
+ //
+ for( int i = 0; i < NUM_MENU_ITEMS; i++ )
+ {
+ if( m_menuImages[ i ] != NULL )
+ {
+ m_menuImages[ i ]->SetIndex( m_currentLevel );
+ }
+ }
+
+ // update unlocked values
+ //
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+
+ char buffer[ 8 ];
+ Scrooby::Text* unlockedText = NULL;
+ rAssert( m_pMenu != NULL );
+
+ // missions
+ int numMissionsCompleted = GetCharacterSheetManager()->QueryNumMissionsCompleted( static_cast<RenderEnums::LevelEnum>( m_currentLevel ) );
+ if( GetCharacterSheetManager()->QueryBonusMissionCompleted( static_cast<RenderEnums::LevelEnum>( m_currentLevel ) ) )
+ {
+ numMissionsCompleted++;
+ }
+
+ sprintf( buffer, "%d / %d",
+ numMissionsCompleted,
+ 8 ); // m_currentLevel == 6 ? 5 : 7 );
+ unlockedText = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( MENU_STORY_MISSIONS )->GetItemValue() );
+ rAssert( unlockedText != NULL );
+ unlockedText->SetString( 0, buffer );
+
+ // clothing
+ sprintf( buffer, "%d / %d",
+ GetCharacterSheetManager()->QueryNumSkinsUnlocked( static_cast<RenderEnums::LevelEnum>( m_currentLevel ) ),
+ 3 );
+ unlockedText = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( MENU_CHARACTER_CLOTHING )->GetItemValue() );
+ rAssert( unlockedText != NULL );
+ unlockedText->SetString( 0, buffer );
+
+ // vehicles
+ int numVehiclesUnlocked = GetCharacterSheetManager()->QueryNumCarUnlocked( static_cast<RenderEnums::LevelEnum>( m_currentLevel ) );
+ if( GetRewardsManager()->GetReward( m_currentLevel, Reward::eDefaultCar )->RewardStatus() )
+ {
+ numVehiclesUnlocked++;
+ }
+ sprintf( buffer, "%d / %d",
+ numVehiclesUnlocked,
+ 6 );
+ unlockedText = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( MENU_VEHICLES )->GetItemValue() );
+ rAssert( unlockedText != NULL );
+ unlockedText->SetString( 0, buffer );
+
+ // cards
+ sprintf( buffer, "%d / %d",
+ GetCharacterSheetManager()->QueryNumCardsCollected( static_cast<RenderEnums::LevelEnum>( m_currentLevel ) ),
+ NUM_CARDS_PER_LEVEL );
+ unlockedText = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( MENU_COLLECTOR_CARDS )->GetItemValue() );
+ rAssert( unlockedText != NULL );
+ unlockedText->SetString( 0, buffer );
+
+/*
+ // movies
+ bool isMovieUnlocked = GetCharacterSheetManager()->QueryFMVUnlocked( static_cast<RenderEnums::LevelEnum>( m_currentLevel ) );
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_MOVIES ) )
+ {
+ isMovieUnlocked = true;
+ }
+
+ sprintf( buffer, "%d / %d",
+ isMovieUnlocked ? 1 : 0,
+ 1 );
+ unlockedText = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( MENU_MOVIES )->GetItemValue() );
+ rAssert( unlockedText != NULL );
+ unlockedText->SetString( 0, buffer );
+
+ m_pMenu->SetMenuItemEnabled( MENU_MOVIES, isMovieUnlocked );
+*/
+
+ HeapMgr()->PopHeap( GMA_LEVEL_FE );
+}
+
+void
+CGuiScreenScrapBookContents::OnMenuSelectionMade( eMenuItem selection )
+{
+ switch( selection )
+ {
+ case MENU_STORY_MISSIONS:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_MISSION_GALLERY );
+
+ break;
+ }
+ case MENU_CHARACTER_CLOTHING:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_SKIN_GALLERY );
+
+ break;
+ }
+ case MENU_VEHICLES:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_VEHICLE_GALLERY );
+
+ break;
+ }
+ case MENU_COLLECTOR_CARDS:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_CARD_GALLERY );
+
+ break;
+ }
+/*
+ case MENU_MOVIES:
+ {
+ if( m_currentLevel != RenderEnums::L3 )
+ {
+ rAssert( m_guiManager );
+ CGuiScreenPlayMovie* playMovieScreen = static_cast<CGuiScreenPlayMovie*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_PLAY_MOVIE ) );
+ rAssert( playMovieScreen );
+
+ rAssert( MOVIES_INGAME[ m_currentLevel ] != "" );
+ playMovieScreen->SetMovieToPlay( MOVIES_INGAME[ m_currentLevel ] );
+
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_PLAY_MOVIE );
+ }
+ else // special case for level 3
+ {
+ // TC: [TODO] Need to find out from D.Evenson which mission in L3 to start
+ //
+ GetCharacterSheetManager()->SetCurrentMission( static_cast<RenderEnums::LevelEnum>( m_currentLevel ),
+ static_cast<RenderEnums::MissionEnum>( RenderEnums::M1 ) );
+
+ // send message to front-end manager to quit front-end and
+ // start single player story mode
+ //
+ m_pParent->HandleMessage( GUI_MSG_QUIT_FRONTEND, 1 ); // 1 = one player
+ }
+
+ break;
+ }
+*/
+ default:
+ {
+ rAssertMsg( false, "Invalid menu selection!" );
+
+ break;
+ }
+ }
+
+ // hide L/R trigger buttons
+ //
+ rAssert( m_LTrigger != NULL && m_RTrigger != NULL );
+ m_LTrigger->SetVisible( false );
+ m_RTrigger->SetVisible( false );
+}
+
diff --git a/game/code/presentation/gui/frontend/guiscreenscrapbookcontents.h b/game/code/presentation/gui/frontend/guiscreenscrapbookcontents.h
new file mode 100644
index 0000000..d3a6d0f
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenscrapbookcontents.h
@@ -0,0 +1,97 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenScrapBookContents
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENSCRAPBOOKCONTENTS_H
+#define GUISCREENSCRAPBOOKCONTENTS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu2D;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenScrapBookContents : public CGuiScreen
+{
+public:
+ CGuiScreenScrapBookContents( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenScrapBookContents();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+ unsigned int GetCurrentLevel() const;
+ void SetLevelBarVisible( bool isVisible );
+#ifdef RAD_WIN32
+ virtual eFEHotspotType CheckCursorAgainstHotspots( float x, float y );
+#endif
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ enum eMenuItem
+ {
+ MENU_STORY_MISSIONS,
+ MENU_CHARACTER_CLOTHING,
+ MENU_VEHICLES,
+ MENU_DUMMY_3,
+ MENU_COLLECTOR_CARDS,
+ MENU_DUMMY_5,
+// MENU_MOVIES,
+
+ NUM_MENU_ITEMS
+ };
+
+ void OnLevelSelectionChange( int delta );
+ void OnMenuSelectionMade( eMenuItem selection );
+
+ CGuiMenu2D* m_pMenu;
+
+ Scrooby::Sprite* m_menuImages[ NUM_MENU_ITEMS ];
+
+ Scrooby::Group* m_levelBarGroup;
+ Scrooby::Text* m_levelSelection;
+ Scrooby::Group* m_LTrigger;
+ Scrooby::Group* m_RTrigger;
+ Scrooby::Sprite* m_LTriggerBgd;
+ Scrooby::Sprite* m_RTriggerBgd;
+ unsigned int m_currentLevel;
+
+ Scrooby::Layer* m_sparkles;
+
+};
+
+//===========================================================================
+// Inlines
+//===========================================================================
+
+inline unsigned int CGuiScreenScrapBookContents::GetCurrentLevel() const
+{
+ return m_currentLevel;
+}
+
+#endif // GUISCREENSCRAPBOOKCONTENTS_H
diff --git a/game/code/presentation/gui/frontend/guiscreenscrapbookstats.cpp b/game/code/presentation/gui/frontend/guiscreenscrapbookstats.cpp
new file mode 100644
index 0000000..857dc2f
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenscrapbookstats.cpp
@@ -0,0 +1,284 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenScrapBookStats
+//
+// Description: Implementation of the CGuiScreenScrapBookStats class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenscrapbookstats.h>
+
+#include <cards/cardgallery.h>
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/rewards/rewardsmanager.h>
+#include <render/enums/renderenums.h>
+
+// Scrooby
+//
+#include <screen.h>
+#include <page.h>
+#include <group.h>
+#include <text.h>
+
+// ATG
+//
+#include <raddebug.hpp>
+
+#include <string.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenScrapBookStats::CGuiScreenScrapBookStats
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenScrapBookStats::CGuiScreenScrapBookStats
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_SCRAP_BOOK_STATS ),
+ m_percentGameComplete( NULL )
+{
+ memset( m_gameStats, 0, sizeof( m_gameStats ) );
+
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "ScrapBookStats" );
+ rAssert( pPage != NULL );
+
+ // scale down game stats text bit
+ //
+ Scrooby::Text* gameStats = pPage->GetText( "GameStats" );
+ if( gameStats != NULL )
+ {
+ gameStats->ResetTransformation();
+ gameStats->ScaleAboutCenter( 0.75f );
+ }
+
+ Scrooby::Group* pGroup = pPage->GetGroup( "Stats" );
+ rAssert( pGroup != NULL );
+
+ m_gameStats[ STAT_STORY_MISSIONS ] = pGroup->GetText( "StoryMissions_Unlocked" );
+ m_gameStats[ STAT_BONUS_MISSIONS ] = pGroup->GetText( "BonusMissions_Unlocked" );
+ m_gameStats[ STAT_STREET_RACES ] = pGroup->GetText( "StreetRaces_Unlocked" );
+ m_gameStats[ STAT_CARDS ] = pGroup->GetText( "Cards_Unlocked" );
+ m_gameStats[ STAT_CLOTHING ] = pGroup->GetText( "Clothing_Unlocked" );
+ m_gameStats[ STAT_VEHICLES ] = pGroup->GetText( "Vehicles_Unlocked" );
+ m_gameStats[ STAT_WASPS ] = pGroup->GetText( "Wasps_Unlocked" );
+ m_gameStats[ STAT_GAGS ] = pGroup->GetText( "Gags_Unlocked" );
+ m_gameStats[ STAT_MOVIES ] = pGroup->GetText( "Movies_Unlocked" );
+
+ // get game complete value
+ //
+ m_percentGameComplete = pGroup->GetText( "GameComplete_Value" );
+ rAssert( m_percentGameComplete != NULL );
+
+#ifdef RAD_DEBUG
+ for( int i = 0; i < NUM_GAME_STATS; i++ )
+ {
+ rAssert( m_gameStats[ i ] != NULL );
+ }
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenScrapBookStats::~CGuiScreenScrapBookStats
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenScrapBookStats::~CGuiScreenScrapBookStats()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenScrapBookStats::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenScrapBookStats::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ this->StartTransitionAnimation( 830, 850 );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenScrapBookStats::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenScrapBookStats::InitIntro()
+{
+ // sum game stats totals for all levels
+ //
+ int numUnlocked[ NUM_GAME_STATS ];
+ memset( numUnlocked, 0, sizeof( numUnlocked ) );
+
+ int numTotalGagsInGame = 0;
+ int numTotalWaspsInGame = 0;
+
+ for( int i = 0; i < RenderEnums::numLevels; i++ )
+ {
+ numUnlocked[ STAT_STORY_MISSIONS ] += GetCharacterSheetManager()->QueryNumMissionsCompleted( static_cast<RenderEnums::LevelEnum>( i ) );
+ numUnlocked[ STAT_BONUS_MISSIONS ] += GetCharacterSheetManager()->QueryBonusMissionCompleted( static_cast<RenderEnums::LevelEnum>( i ) ) ? 1 : 0;
+ numUnlocked[ STAT_STREET_RACES ] += GetCharacterSheetManager()->QueryNumStreetRacesCompleted( static_cast<RenderEnums::LevelEnum>( i ) );
+ numUnlocked[ STAT_CARDS ] += GetCardGallery()->GetCollectedCards( i )->m_numCards;
+ numUnlocked[ STAT_CLOTHING ] += GetCharacterSheetManager()->QueryNumSkinsUnlocked( static_cast<RenderEnums::LevelEnum>( i ) );
+ numUnlocked[ STAT_VEHICLES ] += GetCharacterSheetManager()->QueryNumCarUnlocked( static_cast<RenderEnums::LevelEnum>( i ) );
+ numUnlocked[ STAT_WASPS ] += GetCharacterSheetManager()->QueryNumWaspsDestroyed( static_cast<RenderEnums::LevelEnum>( i ) );
+ numUnlocked[ STAT_GAGS ] += GetCharacterSheetManager()->QueryNumGagsViewed( static_cast<RenderEnums::LevelEnum>( i ) );
+ numUnlocked[ STAT_MOVIES ] += GetCharacterSheetManager()->QueryFMVUnlocked( static_cast<RenderEnums::LevelEnum>( i ) ) ? 1 : 0;
+
+ numTotalGagsInGame += GetRewardsManager()->GetTotalGags( i );
+ numTotalWaspsInGame += GetRewardsManager()->GetTotalWasps( i );
+ }
+
+ // update game stats
+ //
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+
+ char buffer[ 32 ];
+
+ sprintf( buffer, "%d / %d", numUnlocked[ STAT_STORY_MISSIONS ], 49 );
+ m_gameStats[ STAT_STORY_MISSIONS ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", numUnlocked[ STAT_BONUS_MISSIONS ], 7 );
+ m_gameStats[ STAT_BONUS_MISSIONS ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", numUnlocked[ STAT_STREET_RACES ], 21 );
+ m_gameStats[ STAT_STREET_RACES ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", numUnlocked[ STAT_CARDS ], 49 );
+ m_gameStats[ STAT_CARDS ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", numUnlocked[ STAT_CLOTHING ], 21 );
+ m_gameStats[ STAT_CLOTHING ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", numUnlocked[ STAT_VEHICLES ], 35 );
+ m_gameStats[ STAT_VEHICLES ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", numUnlocked[ STAT_WASPS ], numTotalWaspsInGame );
+ m_gameStats[ STAT_WASPS ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", numUnlocked[ STAT_GAGS ], numTotalGagsInGame );
+ m_gameStats[ STAT_GAGS ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", numUnlocked[ STAT_MOVIES ], 7 );
+ m_gameStats[ STAT_MOVIES ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%.1f %%", GetCharacterSheetManager()->QueryPercentGameCompleted() );
+ m_percentGameComplete->SetString( 0, buffer );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_FE );
+}
+
+
+//===========================================================================
+// CGuiScreenScrapBookStats::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenScrapBookStats::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenScrapBookStats::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenScrapBookStats::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/frontend/guiscreenscrapbookstats.h b/game/code/presentation/gui/frontend/guiscreenscrapbookstats.h
new file mode 100644
index 0000000..ce2f706
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenscrapbookstats.h
@@ -0,0 +1,67 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenScrapBookStats
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENSCRAPBOOKSTATS_H
+#define GUISCREENSCRAPBOOKSTATS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenScrapBookStats : public CGuiScreen
+{
+public:
+ CGuiScreenScrapBookStats( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenScrapBookStats();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ enum eGameStats
+ {
+ STAT_STORY_MISSIONS,
+ STAT_BONUS_MISSIONS,
+ STAT_STREET_RACES,
+ STAT_CARDS,
+ STAT_CLOTHING,
+ STAT_VEHICLES,
+ STAT_WASPS,
+ STAT_GAGS,
+ STAT_MOVIES,
+
+ NUM_GAME_STATS
+ };
+
+ Scrooby::Text* m_gameStats[ NUM_GAME_STATS ];
+ Scrooby::Text* m_percentGameComplete;
+
+};
+
+#endif // GUISCREENSCRAPBOOKSTATS_H
diff --git a/game/code/presentation/gui/frontend/guiscreenskingallery.cpp b/game/code/presentation/gui/frontend/guiscreenskingallery.cpp
new file mode 100644
index 0000000..3d79266
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenskingallery.cpp
@@ -0,0 +1,785 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenSkinGallery
+//
+// Description: Implementation of the CGuiScreenSkinGallery class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/21 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenskingallery.h>
+#include <presentation/gui/frontend/guiscreenscrapbookcontents.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/guimenu.h>
+
+#include <cheats/cheatinputsystem.h>
+#include <memory/srrmemory.h>
+#include <mission/rewards/rewardsmanager.h>
+#include <mission/rewards/reward.h>
+#include <events/eventmanager.h>
+
+#include <p3d/inventory.hpp>
+
+#include <raddebug.hpp> // Foundation
+
+#include <screen.h>
+#include <page.h>
+#include <layer.h>
+#include <group.h>
+#include <text.h>
+
+#include <string.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const char* SKIN_GALLERY_INVENTORY_SECTION = "FE_SkinGallery";
+const char* SKIN_GALLERY_IMAGES_DIR = "art\\frontend\\dynaload\\images\\skins2d\\";
+
+const float SKIN_VIEW_TRANSITION_TIME = 250.0f; // in msec
+const float SKIN_VIEW_PROJECTILE_GRAVITY = 0.005f; // in m/ms/ms
+const int SKIN_VIEW_POS_X = 250;
+const int SKIN_VIEW_POS_Y = 130;
+
+#ifdef RAD_WIN32
+const float SKIN_BASE_SCALE = 1.0f / 2.5f;
+const float SKIN_IMAGE_SCALE = 0.5f / 2.5f;
+#else
+const float SKIN_BASE_SCALE = 1.0f;
+const float SKIN_IMAGE_SCALE = 1.5f - 1.0f;
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenSkinGallery::CGuiScreenSkinGallery
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenSkinGallery::CGuiScreenSkinGallery
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_SKIN_GALLERY ),
+ m_pMenu( NULL ),
+ m_numSelections( 0 ),
+ m_isSkinsLoaded( false ),
+ m_screenState( SCREEN_STATE_NORMAL ),
+ m_elapsedTime( 0 ),
+ m_projectileVelocity( 0.0f, 0.0f, 0.0f ),
+ m_skinInfo( NULL ),
+#ifdef RAD_WIN32
+ m_selectedSkin(0),
+#endif
+ m_skinName( NULL )
+{
+ memset( m_rewardSelections, 0, sizeof( m_rewardSelections ) );
+
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "SkinGallery" );
+ rAssert( pPage != NULL );
+
+ // create a 2D sprite menu
+ //
+ m_pMenu = new CGuiMenu2D( this, MAX_NUM_SKINS_PER_LEVEL, 3, GUI_SPRITE_MENU, MENU_SFX_NONE );
+ rAssert( m_pMenu != NULL );
+ m_pMenu->SetGreyOutEnabled( false );
+
+ Scrooby::Group* pGroup = pPage->GetGroup( "Menu" );
+ rAssert( pGroup != NULL );
+
+ // add sprites to menu
+ //
+ for( int i = 0; i < MAX_NUM_SKINS_PER_LEVEL; i++ )
+ {
+ char name[ 16 ];
+ sprintf( name, "Skin%d", i );
+
+ m_pMenu->AddMenuItem( pGroup->GetSprite( name ) );
+ }
+
+ // add menu cursor
+ //
+ m_pMenu->SetCursor( pGroup->GetSprite( "SkinCursor" ) );
+
+ // get skin info layer and name
+ //
+ m_skinInfo = pPage->GetLayer( "ViewSkin" );
+ m_skinName = pPage->GetText( "SkinName" );
+
+ // hide skin info layer by default
+ //
+ rAssert( m_skinInfo != NULL );
+ m_skinInfo->SetVisible( false );
+ m_skinInfo->SetAlpha( 0.0f );
+
+ if( this->IsWideScreenDisplay() )
+ {
+ m_skinInfo->ResetTransformation();
+ this->ApplyWideScreenCorrectionScale( m_skinInfo );
+ }
+
+ // wrap skin name
+ //
+// rAssert( m_skinName != NULL );
+// m_skinName->SetTextMode( Scrooby::TEXT_WRAP );
+
+ // add outline to skin name
+ //
+ m_skinName->SetDisplayOutline( true );
+
+ // create inventory section for skin galllery resources
+ //
+ p3d::inventory->AddSection( SKIN_GALLERY_INVENTORY_SECTION );
+}
+
+
+//===========================================================================
+// CGuiScreenSkinGallery::~CGuiScreenSkinGallery
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenSkinGallery::~CGuiScreenSkinGallery()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+
+ // destroy skin gallery inventory section
+ //
+ p3d::inventory->DeleteSection( SKIN_GALLERY_INVENTORY_SECTION );
+}
+
+
+//===========================================================================
+// CGuiScreenSkinGallery::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSkinGallery::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_screenState == SCREEN_STATE_GOTO_VIEW ||
+ m_screenState == SCREEN_STATE_BACK_VIEW )
+ {
+ if( message == GUI_MSG_UPDATE )
+ {
+ this->OnUpdate( param1 );
+ }
+
+ return;
+ }
+
+ if( m_screenState == SCREEN_STATE_VIEWING )
+ {
+ if( message == GUI_MSG_UPDATE )
+ {
+ this->OnUpdate( param1 );
+ }
+ else if( message == GUI_MSG_CONTROLLER_BACK )
+ {
+ m_screenState = SCREEN_STATE_BACK_VIEW;
+ m_elapsedTime = 0;
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_BACK ); // sound effect
+ }
+
+ return;
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ this->OnUpdate( param1 );
+
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_CHANGED:
+ {
+ this->OnMenuSelectionChange( static_cast<int>( param1 ) );
+
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ this->OnMenuSelectionMade( static_cast<int>( param1 ) );
+#ifdef RAD_WIN32
+ m_selectedSkin = static_cast<int>( param1 );
+ // Hide/disable all other menu items.
+ this->SetVisibilityForAllOtherMenuItems( false );
+#endif
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ //
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenSkinGallery::OnProcessRequestsComplete
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void
+CGuiScreenSkinGallery::OnProcessRequestsComplete( void* pUserData )
+{
+ m_numTransitionsPending--;
+
+ p3d::pddi->DrawSync();
+
+ // push and select inventory section for searching
+ //
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( SKIN_GALLERY_INVENTORY_SECTION );
+ bool currentSectionOnly = p3d::inventory->GetCurrentSectionOnly();
+ p3d::inventory->SetCurrentSectionOnly( true );
+
+ // update all 3D models
+ //
+ CGuiScreenScrapBookContents* pScreen = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( pScreen != NULL );
+
+ for( int i = 0; i < m_numSelections; i++ )
+ {
+ if( (m_pMenu->GetMenuItem( i )->m_attributes & SELECTION_ENABLED) > 0 )
+ {
+ char name[ 16 ];
+ sprintf( name, "%s.png", m_rewardSelections[ i ]->GetName() );
+ tSprite* pSprite = p3d::find<tSprite>( name );
+ if( pSprite != NULL )
+ {
+ Scrooby::Sprite* skinImage = dynamic_cast<Scrooby::Sprite*>( m_pMenu->GetMenuItem( i )->GetItem() );
+ rAssert( skinImage != NULL );
+ skinImage->SetRawSprite( pSprite, true );
+ }
+ else
+ {
+ rAssertMsg( false, "Skin image not found!" );
+ }
+ }
+ }
+
+ // pop inventory section and restore states
+ //
+ p3d::inventory->SetCurrentSectionOnly( currentSectionOnly );
+ p3d::inventory->PopSection();
+
+ m_isSkinsLoaded = true;
+}
+
+//===========================================================================
+// CGuiScreenSkinGallery::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSkinGallery::InitIntro()
+{
+ if( !m_isSkinsLoaded )
+ {
+ this->Load2DImages();
+ }
+
+ // set level-specific silhouettes
+ //
+ CGuiScreenScrapBookContents* scrapBookContents = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( scrapBookContents != NULL );
+ for( int i = 0; i < MAX_NUM_SKINS_PER_LEVEL; i++ )
+ {
+ Scrooby::Sprite* skinImage = dynamic_cast<Scrooby::Sprite*>( m_pMenu->GetMenuItem( i )->GetItem() );
+ rAssert( skinImage != NULL );
+ skinImage->SetIndex( scrapBookContents->GetCurrentLevel() );
+ }
+
+ this->OnMenuSelectionChange( m_pMenu->GetSelection() );
+
+ GetEventManager()->TriggerEvent( EVENT_PLAY_MUZAK );
+}
+
+
+//===========================================================================
+// CGuiScreenSkinGallery::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSkinGallery::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenSkinGallery::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSkinGallery::InitOutro()
+{
+ if( m_isSkinsLoaded )
+ {
+ this->Unload2DImages();
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_PLAY_FE_MUSIC );
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenSkinGallery::OnUpdate( unsigned int elapsedTime )
+{
+ switch( m_screenState )
+ {
+ case SCREEN_STATE_NORMAL:
+ {
+ // pulse cursor alpha
+ //
+ Scrooby::Drawable* cursor = m_pMenu->GetCursor();
+ if( cursor != NULL )
+ {
+ const unsigned int PULSE_PERIOD = 1000;
+
+ float alpha = GuiSFX::Pulse( (float)m_elapsedTime,
+ (float)PULSE_PERIOD,
+ 0.75f,
+ 0.25f,
+ -rmt::PI_BY2 );
+
+ cursor->SetAlpha( alpha );
+
+ m_elapsedTime += elapsedTime;
+ m_elapsedTime %= PULSE_PERIOD;
+ }
+
+ break;
+ }
+ case SCREEN_STATE_GOTO_VIEW:
+ {
+ m_elapsedTime += elapsedTime;
+
+ rAssert( m_pMenu != NULL );
+ int currentSelection = m_pMenu->GetSelection();
+
+ Scrooby::BoundedDrawable* pDrawable = m_pMenu->GetMenuItem( currentSelection )->GetItem();
+ rAssert( pDrawable != NULL );
+
+ pDrawable->ResetTransformation();
+
+ if( m_elapsedTime < (unsigned int)SKIN_VIEW_TRANSITION_TIME )
+ {
+ float percentageDone = m_elapsedTime / SKIN_VIEW_TRANSITION_TIME;
+
+ pDrawable->ScaleAboutCenter( SKIN_BASE_SCALE + percentageDone * SKIN_IMAGE_SCALE );
+
+ GuiSFX::Projectile( pDrawable,
+ (float)m_elapsedTime,
+ SKIN_VIEW_TRANSITION_TIME,
+ m_projectileVelocity,
+ false,
+ SKIN_VIEW_PROJECTILE_GRAVITY );
+
+ // fade out rest of the menu items
+ //
+ this->SetMenuAlpha( 1.0f - rmt::Sqrt( percentageDone ) );
+
+ // fade in skin info layer
+ //
+ rAssert( m_skinInfo != NULL );
+ m_skinInfo->SetAlpha( percentageDone * percentageDone );
+ }
+ else
+ {
+ pDrawable->ScaleAboutCenter( SKIN_BASE_SCALE + SKIN_IMAGE_SCALE );
+
+ GuiSFX::Projectile( pDrawable,
+ SKIN_VIEW_TRANSITION_TIME,
+ SKIN_VIEW_TRANSITION_TIME,
+ m_projectileVelocity,
+ false,
+ SKIN_VIEW_PROJECTILE_GRAVITY );
+
+ this->SetMenuAlpha( 0.0f );
+
+ rAssert( m_skinInfo != NULL );
+ m_skinInfo->SetAlpha( 1.0f );
+
+ m_screenState = SCREEN_STATE_VIEWING;
+ }
+
+ break;
+ }
+ case SCREEN_STATE_VIEWING:
+ {
+
+ break;
+ }
+ case SCREEN_STATE_BACK_VIEW:
+ {
+ m_elapsedTime += elapsedTime;
+
+ rAssert( m_pMenu != NULL );
+ int currentSelection = m_pMenu->GetSelection();
+
+ Scrooby::BoundedDrawable* pDrawable = m_pMenu->GetMenuItem( currentSelection )->GetItem();
+ rAssert( pDrawable != NULL );
+
+ pDrawable->ResetTransformation();
+
+ if( m_elapsedTime < (unsigned int)SKIN_VIEW_TRANSITION_TIME )
+ {
+ float percentageDone = m_elapsedTime / SKIN_VIEW_TRANSITION_TIME;
+
+ pDrawable->ScaleAboutCenter( SKIN_BASE_SCALE + (1.0f - percentageDone) * SKIN_IMAGE_SCALE );
+
+ GuiSFX::Projectile( pDrawable,
+ (float)m_elapsedTime,
+ SKIN_VIEW_TRANSITION_TIME,
+ m_projectileVelocity,
+ true,
+ SKIN_VIEW_PROJECTILE_GRAVITY );
+
+ // fade back in rest of the menu items
+ //
+ this->SetMenuAlpha( percentageDone * percentageDone );
+
+ // fade in skin info layer
+ //
+ rAssert( m_skinInfo != NULL );
+ m_skinInfo->SetAlpha( 1.0f - rmt::Sqrt( percentageDone ) );
+ }
+ else
+ {
+ pDrawable->ScaleAboutCenter( SKIN_BASE_SCALE );
+
+ GuiSFX::Projectile( pDrawable,
+ SKIN_VIEW_TRANSITION_TIME,
+ SKIN_VIEW_TRANSITION_TIME,
+ m_projectileVelocity,
+ true,
+ SKIN_VIEW_PROJECTILE_GRAVITY );
+
+ // show menu cursor
+ //
+ rAssert( m_pMenu != NULL );
+ m_pMenu->GetCursor()->SetVisible( true );
+
+ // show level bar
+ //
+ CGuiScreenScrapBookContents* scrapBookContents = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( scrapBookContents != NULL );
+ scrapBookContents->SetLevelBarVisible( true );
+
+ this->SetMenuAlpha( 1.0f );
+
+ rAssert( m_skinInfo != NULL );
+ m_skinInfo->SetAlpha( 0.0f );
+
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+
+ // hide skin info layer
+ //
+ rAssert( m_skinInfo != NULL );
+ m_skinInfo->SetVisible( false );
+
+ m_elapsedTime = 0;
+ m_screenState = SCREEN_STATE_NORMAL;
+#ifdef RAD_WIN32
+ // Show/enable all hidden menu items.
+ this->SetVisibilityForAllOtherMenuItems( true );
+#endif
+ }
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Invalid screen state!" );
+
+ break;
+ }
+ }
+}
+
+void
+CGuiScreenSkinGallery::OnMenuSelectionChange( int selection )
+{
+ // scale up new selection
+ //
+ for( int i = 0; i < m_numSelections; i++ )
+ {
+ Scrooby::BoundedDrawable* drawable = m_pMenu->GetMenuItem( i )->GetItem();
+ rAssert( drawable != NULL );
+ drawable->ResetTransformation();
+ drawable->ScaleAboutCenter( SKIN_BASE_SCALE );
+
+ if( i != selection )
+ {
+ drawable->ScaleAboutCenter( 0.9f );
+ }
+ }
+}
+
+void
+CGuiScreenSkinGallery::OnMenuSelectionMade( int selection )
+{
+ if( m_numSelections > 0 )
+ {
+ // hide level bar
+ //
+ CGuiScreenScrapBookContents* scrapBookContents = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( scrapBookContents != NULL );
+ scrapBookContents->SetLevelBarVisible( false );
+
+ // hide menu cursor
+ //
+ rAssert( m_pMenu != NULL );
+ m_pMenu->GetCursor()->SetVisible( false );
+
+ // show skin info layer
+ //
+ rAssert( m_skinInfo != NULL );
+ m_skinInfo->SetVisible( true );
+
+ // update skin name
+ //
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+
+ char stringID[ 16 ];
+ rAssert( m_rewardSelections[ m_pMenu->GetSelection() ] != NULL );
+ strcpy( stringID, m_rewardSelections[ m_pMenu->GetSelection() ]->GetName() );
+
+ UnicodeString unicodeString;
+ unicodeString.ReadUnicode( GetTextBibleString( strupr( stringID ) ) );
+
+ rAssert( m_skinName != NULL );
+ m_skinName->SetString( 0, unicodeString );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_FE );
+
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, false );
+
+ // calculate the initial projectile velocity
+ //
+ Scrooby::BoundedDrawable* pDrawable = m_pMenu->GetMenuItem( selection )->GetItem();
+ rAssert( pDrawable != NULL );
+
+ int startPosX = 0;
+ int startPosY = 0;
+ pDrawable->GetOriginPosition( startPosX, startPosY );
+
+ m_projectileVelocity.x = (SKIN_VIEW_POS_X - startPosX) / SKIN_VIEW_TRANSITION_TIME;
+ m_projectileVelocity.y = (SKIN_VIEW_POS_Y - startPosY - 0.5f * SKIN_VIEW_PROJECTILE_GRAVITY * SKIN_VIEW_TRANSITION_TIME * SKIN_VIEW_TRANSITION_TIME) / SKIN_VIEW_TRANSITION_TIME;
+
+ m_screenState = SCREEN_STATE_GOTO_VIEW;
+ m_elapsedTime = 0;
+ }
+}
+
+void
+CGuiScreenSkinGallery::SetMenuAlpha( float alpha )
+{
+ rAssert( m_pMenu != NULL );
+ int currentSelection = m_pMenu->GetSelection();
+
+ for( int i = 0; i < MAX_NUM_SKINS_PER_LEVEL; i++ )
+ {
+ if( i != currentSelection )
+ {
+ Scrooby::Drawable* skinImage = m_pMenu->GetMenuItem( i )->GetItem();
+ rAssert( skinImage != NULL );
+ skinImage->SetAlpha( alpha );
+ }
+ }
+}
+
+void
+CGuiScreenSkinGallery::Load2DImages()
+{
+ // load 2D images for current level
+ //
+ CGuiScreenScrapBookContents* pScreen = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( pScreen != NULL );
+
+ m_numSelections = 0;
+
+ for( Reward* pReward = GetRewardsManager()->FindFirstMerchandise( pScreen->GetCurrentLevel(), Merchandise::SELLER_INTERIOR );
+ pReward != NULL;
+ pReward = GetRewardsManager()->FindNextMerchandise( pScreen->GetCurrentLevel(), Merchandise::SELLER_INTERIOR ) )
+ {
+ rAssert( m_numSelections < MAX_NUM_SKINS_PER_LEVEL );
+
+ // store reference to reward
+ //
+ m_rewardSelections[ m_numSelections ] = pReward;
+
+ char filename[ 64 ];
+ sprintf( filename, "%s%s.p3d", SKIN_GALLERY_IMAGES_DIR, pReward->GetName() );
+ rAssert( strlen( filename) < sizeof( filename ) );
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ filename,
+ GMA_LEVEL_FE,
+ SKIN_GALLERY_INVENTORY_SECTION,
+ SKIN_GALLERY_INVENTORY_SECTION );
+
+ m_numSelections++;
+ }
+
+ rWarningMsg( m_numSelections > 0, "No model selections available!" );
+
+ if( m_numSelections > 0 )
+ {
+ GetLoadingManager()->AddCallback( this );
+
+ m_numTransitionsPending++;
+ }
+
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, false ); // hide by default
+
+ for( int i = 0; i < MAX_NUM_SKINS_PER_LEVEL; i++ )
+ {
+ if( i < m_numSelections )
+ {
+ bool isUnlocked = m_rewardSelections[ i ]->RewardStatus();
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_SKINS ) )
+ {
+ isUnlocked = true;
+ }
+
+ m_pMenu->SetMenuItemEnabled( i, isUnlocked );
+
+ if( isUnlocked )
+ {
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+ }
+ }
+ else
+ {
+ m_pMenu->SetMenuItemEnabled( i, false );
+ }
+ }
+}
+
+void
+CGuiScreenSkinGallery::Unload2DImages()
+{
+ p3d::pddi->DrawSync();
+
+ // clear all drawables
+ //
+ for( int i = 0; i < MAX_NUM_SKINS_PER_LEVEL; i++ )
+ {
+ Scrooby::Sprite* skinImage = dynamic_cast<Scrooby::Sprite*>( m_pMenu->GetMenuItem( i )->GetItem() );
+ rAssert( skinImage != NULL );
+ skinImage->SetRawSprite( NULL, true );
+
+ m_pMenu->SetMenuItemEnabled( i, false );
+ }
+
+ // unload 2D images
+ //
+ p3d::inventory->RemoveSectionElements( SKIN_GALLERY_INVENTORY_SECTION );
+
+ m_isSkinsLoaded = false;
+}
+
+#ifdef RAD_WIN32
+void CGuiScreenSkinGallery::SetVisibilityForAllOtherMenuItems( bool bVisible )
+{
+ for( int i = 0; i < MAX_NUM_SKINS_PER_LEVEL; i++ )
+ {
+ if( i != m_selectedSkin )
+ m_pMenu->GetMenuItem(i)->GetItem()->SetVisible( bVisible );
+ }
+}
+#endif \ No newline at end of file
diff --git a/game/code/presentation/gui/frontend/guiscreenskingallery.h b/game/code/presentation/gui/frontend/guiscreenskingallery.h
new file mode 100644
index 0000000..c082306
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenskingallery.h
@@ -0,0 +1,100 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenSkinGallery
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/21 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENSKINGALLERY_H
+#define GUISCREENSKINGALLERY_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <loading/loadingmanager.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CGuiMenu2D;
+class Reward;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenSkinGallery : public CGuiScreen,
+ public LoadingManager::ProcessRequestsCallback
+{
+public:
+ CGuiScreenSkinGallery( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenSkinGallery();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+ // Implements LoadingManager::ProcessRequestsCallback
+ //
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void OnUpdate( unsigned int elapsedTime );
+ void OnMenuSelectionChange( int selection );
+ void OnMenuSelectionMade( int selection );
+ void SetMenuAlpha( float alpha );
+#ifdef RAD_WIN32
+ void SetVisibilityForAllOtherMenuItems( bool bDisable );
+#endif
+
+ void Load2DImages();
+ void Unload2DImages();
+
+ static const int MAX_NUM_SKINS_PER_LEVEL = 3;
+
+ CGuiMenu2D* m_pMenu;
+
+ Reward* m_rewardSelections[ MAX_NUM_SKINS_PER_LEVEL ];
+ int m_numSelections;
+
+ bool m_isSkinsLoaded : 1;
+
+ enum eScreenState
+ {
+ SCREEN_STATE_NORMAL,
+ SCREEN_STATE_GOTO_VIEW,
+ SCREEN_STATE_VIEWING,
+ SCREEN_STATE_BACK_VIEW,
+
+ NUM_SCREEN_STATES
+ };
+
+ eScreenState m_screenState;
+ unsigned int m_elapsedTime;
+ rmt::Vector m_projectileVelocity;
+
+ Scrooby::Layer* m_skinInfo;
+ Scrooby::Text* m_skinName;
+
+#ifdef RAD_WIN32
+ int m_selectedSkin;
+#endif
+};
+
+#endif // GUISCREENSKINGALLERY_H
diff --git a/game/code/presentation/gui/frontend/guiscreensound.cpp b/game/code/presentation/gui/frontend/guiscreensound.cpp
new file mode 100644
index 0000000..a8f4b81
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreensound.cpp
@@ -0,0 +1,541 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenSound
+//
+// Description: Implementation of the CGuiScreenSound class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreensound.h>
+#include <presentation/gui/guimenu.h>
+
+#include <memory/srrmemory.h>
+#include <sound/soundmanager.h>
+
+#include <raddebug.hpp> // Foundation
+#include <page.h>
+#include <screen.h>
+#include <group.h>
+#include <sprite.h>
+#include <text.h>
+
+#include <string.h>
+
+#ifdef RAD_WIN32
+#include <data/config/gameconfigmanager.h>
+#endif
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const char* SOUND_MENU_ITEMS[] =
+{
+ "Music",
+ "Effects",
+ "Engine",
+ "Voice",
+
+#ifdef INCLUDE_SOUND_MODE
+ "SurroundSound",
+#endif
+
+ "" // dummy terminator
+};
+
+const float SLIDER_CORRECTION_SCALE = 4.0f; // in x-direction only
+
+#ifdef RAD_WIN32
+const float SLIDER_ICON_SCALE = 0.5f;
+#endif
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenSound::CGuiScreenSound
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenSound::CGuiScreenSound
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_SOUND ),
+ m_pMenu( NULL )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenSound" );
+ memset( m_soundOffIcons, 0, sizeof( m_soundOffIcons ) );
+
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage;
+ pPage = m_pScroobyScreen->GetPage( "Sound" );
+ rAssert( pPage );
+
+ // Create a menu.
+ //
+ m_pMenu = new CGuiMenu( this, NUM_MENU_ITEMS );
+ rAssert( m_pMenu != NULL );
+
+ // Add menu items
+ //
+ Scrooby::Text* pText = NULL;
+ Scrooby::Text* pTextValue = NULL;
+ Scrooby::Sprite* pArrowL = NULL;
+ Scrooby::Sprite* pArrowR = NULL;
+
+ char itemName[ 32 ];
+
+ for( int i = 0; i < NUM_MENU_ITEMS; i++ )
+ {
+ Scrooby::Group* group = pPage->GetGroup( SOUND_MENU_ITEMS[ i ] );
+ rAssert( group != NULL );
+
+ pText = group->GetText( SOUND_MENU_ITEMS[ i ] );
+
+ // if text value exists
+ //
+ sprintf( itemName, "%s_Value", SOUND_MENU_ITEMS[ i ] );
+ pTextValue = group->GetText( itemName );
+
+#ifdef INCLUDE_SOUND_MODE
+ if( i == MENU_ITEM_SURROUND_SOUND )
+ {
+/*
+ // get "dolby prologic #" text and set: # = 1 for GC
+ // # = 2 for PS2
+ //
+ P3D_UNICODE* unicodeString = static_cast<P3D_UNICODE*>( pTextValue->GetStringBuffer( SURROUND_SOUND ) );
+ if( unicodeString != NULL )
+ {
+ int stringLength = p3d::UnicodeStrLen( unicodeString );
+ for( int j = 0; j < stringLength; j++ )
+ {
+ if( unicodeString[ j ] == '#' )
+ {
+#ifdef RAD_GAMECUBE
+ unicodeString[ j ] = '1';
+#endif
+#ifdef RAD_PS2
+ unicodeString[ j ] = '2';
+#endif
+ }
+ }
+ }
+*/
+
+ // add menu item for sound mode setting
+ //
+ pText = pTextValue;
+
+ sprintf( itemName, "%s_LArrow", SOUND_MENU_ITEMS[ i ] );
+ pArrowL = group->GetSprite( itemName );
+
+ sprintf( itemName, "%s_RArrow", SOUND_MENU_ITEMS[ i ] );
+ pArrowR = group->GetSprite( itemName );
+
+ m_pMenu->AddMenuItem( pText,
+ pTextValue,
+ NULL,
+ NULL,
+ pArrowL,
+ pArrowR,
+ SELECTION_ENABLED | VALUES_WRAPPED | TEXT_OUTLINE_ENABLED );
+ }
+#endif
+
+ // if slider exists
+ //
+ sprintf( itemName, "%s_Slider", SOUND_MENU_ITEMS[ i ] );
+ Scrooby::Group* sliderGroup = group->GetGroup( itemName );
+ if( sliderGroup != NULL )
+ {
+ sliderGroup->ResetTransformation();
+ sliderGroup->ScaleAboutCenter( SLIDER_CORRECTION_SCALE, 1.0f, 1.0f );
+
+ m_pMenu->AddMenuItem( pText,
+ pTextValue,
+ NULL,
+ sliderGroup->GetSprite( itemName ),
+ pArrowL,
+ pArrowR,
+ SELECTION_ENABLED | VALUES_WRAPPED | TEXT_OUTLINE_ENABLED );
+
+ m_pMenu->GetMenuItem( i )->m_slider.m_type = Slider::HORIZONTAL_SLIDER_ABOUT_CENTER;
+
+ // get sound off icon
+ //
+ sprintf( itemName, "%s_Off", SOUND_MENU_ITEMS[ i ] );
+ m_soundOffIcons[ i ] = group->GetSprite( itemName );
+#ifdef RAD_WIN32
+ m_soundOffIcons[ i ]->ScaleAboutCenter( SLIDER_ICON_SCALE );
+
+ sprintf( itemName, "%s_Icon", SOUND_MENU_ITEMS[ i ] );
+ Scrooby::Sprite* soundOnIcon = group->GetSprite( itemName );
+ soundOnIcon->ScaleAboutCenter( SLIDER_ICON_SCALE );
+#endif
+ }
+ }
+
+#ifdef INCLUDE_SOUND_MODE
+ m_pMenu->SetSelectionValueCount( MENU_ITEM_SURROUND_SOUND, NUM_SOUND_SETTINGS );
+#else
+ // hide surround sound setting
+ //
+ Scrooby::Group* surroundSoundSetting = pPage->GetGroup( "SurroundSound" );
+ if( surroundSoundSetting != NULL )
+ {
+ surroundSoundSetting->SetVisible( false );
+ }
+
+ #ifndef RAD_WIN32 // for PC don't shift the pixels... essential for the mouse cursor.
+ // and move regular sound menu down a bit to re-center vertically
+ //
+ Scrooby::Group* soundMenu = pPage->GetGroup( "Menu" );
+ rAssert( soundMenu != NULL );
+ soundMenu->Translate( 0, -30 );
+ #endif
+#endif
+
+MEMTRACK_POP_GROUP("CGUIScreenSound");
+}
+
+
+//===========================================================================
+// CGuiScreenSound::~CGuiScreenSound
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenSound::~CGuiScreenSound()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenSound::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSound::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_MENU_SELECTION_VALUE_CHANGED:
+ {
+ rAssert( m_pMenu );
+ GuiMenuItem* currentItem = m_pMenu->GetMenuItem( param1 );
+ rAssert( currentItem );
+
+ switch( param1 )
+ {
+ case MENU_ITEM_MUSIC:
+ {
+ GetSoundManager()->SetMusicVolume( currentItem->m_slider.m_value );
+
+ break;
+ }
+ case MENU_ITEM_EFFECTS:
+ {
+ GetSoundManager()->SetSfxVolume( currentItem->m_slider.m_value );
+
+ //
+ // HACK!!
+ //
+ // Oh boy, this is ugly. We've got four sliders and five sets of volumes.
+ // Sfx needs to adjust ambience as well. In a stroke of genius, I didn't
+ // provide a separate set of volumes for the designers. So, the sfx needs
+ // to adjust ambience without upsetting whatever ambience volume they choose
+ // in the scripts. I'm not proud of this.
+ //
+ // Esan
+ //
+ GetSoundManager()->SetAmbienceVolume( GetSoundManager()->GetCalculatedAmbienceVolume() );
+
+ break;
+ }
+ case MENU_ITEM_ENGINE:
+ {
+ GetSoundManager()->SetCarVolume( currentItem->m_slider.m_value );
+
+ break;
+ }
+ case MENU_ITEM_VOICE:
+ {
+ GetSoundManager()->SetDialogueVolume( currentItem->m_slider.m_value );
+
+ break;
+ }
+#ifdef INCLUDE_SOUND_MODE
+ case MENU_ITEM_SURROUND_SOUND:
+ {
+ if( param2 == MONO_SOUND )
+ {
+ GetSoundManager()->SetSoundMode( SOUND_MONO );
+ }
+ else if( param2 == STEREO_SOUND )
+ {
+ GetSoundManager()->SetSoundMode( SOUND_STEREO );
+ }
+ else if( param2 == SURROUND_SOUND )
+ {
+ GetSoundManager()->SetSoundMode( SOUND_SURROUND );
+ }
+ else
+ {
+ rAssert( false );
+ }
+
+ break;
+ }
+#endif
+ default:
+ {
+ break;
+ }
+ }
+
+ // show 'sound off' icon if volume is completely turn off
+ //
+ if( m_soundOffIcons[ param1 ] != NULL )
+ {
+ m_soundOffIcons[ param1 ]->SetVisible( currentItem->m_slider.m_value == 0 );
+ }
+
+ // set flag indicating slider value has changed
+ //
+ m_hasSliderValueChanged[ param1 ] = true;
+
+ break;
+ }
+ case GUI_MSG_MENU_SLIDER_NOT_CHANGING:
+ {
+ // play stinger if music slider has recently changed
+ //
+ if( m_hasSliderValueChanged[ MENU_ITEM_MUSIC ] )
+ {
+ m_hasSliderValueChanged[ MENU_ITEM_MUSIC ] = false;
+
+ GetSoundManager()->PlayMusicOptionMenuStinger();
+ }
+
+ // play stinger if effects slider has recently changed
+ //
+ if( m_hasSliderValueChanged[ MENU_ITEM_EFFECTS ] )
+ {
+ m_hasSliderValueChanged[ MENU_ITEM_EFFECTS ] = false;
+
+ GetSoundManager()->PlaySfxOptionMenuStinger();
+ }
+
+ // play stinger if engine slider has recently changed
+ //
+ if( m_hasSliderValueChanged[ MENU_ITEM_ENGINE ] )
+ {
+ m_hasSliderValueChanged[ MENU_ITEM_ENGINE ] = false;
+
+ GetSoundManager()->PlayCarOptionMenuStinger();
+ }
+
+ // play stinger if voic slider has recently changed
+ //
+ if( m_hasSliderValueChanged[ MENU_ITEM_VOICE ] )
+ {
+ m_hasSliderValueChanged[ MENU_ITEM_VOICE ] = false;
+
+ GetSoundManager()->PlayDialogueOptionMenuStinger();
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ this->StartTransitionAnimation( 690, 720 );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ //
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+//===========================================================================
+// CGuiScreenSound::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSound::InitIntro()
+{
+ // Set slider values to current volume settings
+ //
+ GuiMenuItem* menuItem = NULL;
+ rAssert( m_pMenu );
+
+ // music
+ //
+ menuItem = m_pMenu->GetMenuItem( MENU_ITEM_MUSIC );
+ rAssert( menuItem );
+ menuItem->m_slider.SetValue( GetSoundManager()->GetMusicVolume() );
+
+ rAssert( m_soundOffIcons[ MENU_ITEM_MUSIC ] != NULL );
+ m_soundOffIcons[ MENU_ITEM_MUSIC ]->SetVisible( menuItem->m_slider.m_value == 0 );
+
+ // effects
+ //
+ menuItem = m_pMenu->GetMenuItem( MENU_ITEM_EFFECTS );
+ rAssert( menuItem );
+ menuItem->m_slider.SetValue( GetSoundManager()->GetSfxVolume() );
+
+ rAssert( m_soundOffIcons[ MENU_ITEM_EFFECTS ] != NULL );
+ m_soundOffIcons[ MENU_ITEM_EFFECTS ]->SetVisible( menuItem->m_slider.m_value == 0 );
+
+ // engine
+ //
+ menuItem = m_pMenu->GetMenuItem( MENU_ITEM_ENGINE );
+ rAssert( menuItem );
+ menuItem->m_slider.SetValue( GetSoundManager()->GetCarVolume() );
+
+ rAssert( m_soundOffIcons[ MENU_ITEM_ENGINE ] != NULL );
+ m_soundOffIcons[ MENU_ITEM_ENGINE ]->SetVisible( menuItem->m_slider.m_value == 0 );
+
+ // voice
+ //
+ menuItem = m_pMenu->GetMenuItem( MENU_ITEM_VOICE );
+ rAssert( menuItem );
+ menuItem->m_slider.SetValue( GetSoundManager()->GetDialogueVolume() );
+
+ rAssert( m_soundOffIcons[ MENU_ITEM_VOICE ] != NULL );
+ m_soundOffIcons[ MENU_ITEM_VOICE ]->SetVisible( menuItem->m_slider.m_value == 0 );
+
+ // surround sound
+ //
+#ifdef INCLUDE_SOUND_MODE
+ if( GetSoundManager()->GetSoundMode() == SOUND_MONO )
+ {
+ m_pMenu->SetSelectionValue( MENU_ITEM_SURROUND_SOUND, MONO_SOUND );
+ }
+ else if( GetSoundManager()->GetSoundMode() == SOUND_STEREO )
+ {
+ m_pMenu->SetSelectionValue( MENU_ITEM_SURROUND_SOUND, STEREO_SOUND );
+ }
+ else if( GetSoundManager()->GetSoundMode() == SOUND_SURROUND )
+ {
+ m_pMenu->SetSelectionValue( MENU_ITEM_SURROUND_SOUND, SURROUND_SOUND );
+ }
+#endif
+
+ // reset slider value changed flags
+ //
+ for( int i = 0; i < NUM_MENU_ITEMS; i++ )
+ {
+ m_hasSliderValueChanged[ i ] = false;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenSound::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSound::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenSound::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSound::InitOutro()
+{
+#ifdef RAD_WIN32
+ // Save the new controller mappings to the config file.
+ GetGameConfigManager()->SaveConfigFile();
+#endif
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/frontend/guiscreensound.h b/game/code/presentation/gui/frontend/guiscreensound.h
new file mode 100644
index 0000000..a263ef7
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreensound.h
@@ -0,0 +1,94 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenSound
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENSOUND_H
+#define GUISCREENSOUND_H
+
+#if defined( RAD_GAMECUBE ) || defined( RAD_PS2 )
+ #define INCLUDE_SOUND_MODE
+#endif
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+namespace Scrooby
+{
+ class Screen;
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenSound : public CGuiScreen
+{
+public:
+ CGuiScreenSound( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenSound();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+#ifdef RAD_WIN32
+ //virtual eFEHotspotType CheckCursorAgainstHotspots( float x, float y );
+#endif
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ enum eMenuItem
+ {
+ MENU_ITEM_MUSIC,
+ MENU_ITEM_EFFECTS,
+ MENU_ITEM_ENGINE,
+ MENU_ITEM_VOICE,
+
+#ifdef INCLUDE_SOUND_MODE
+ MENU_ITEM_SURROUND_SOUND,
+#endif
+
+ NUM_MENU_ITEMS
+ };
+
+#ifdef INCLUDE_SOUND_MODE
+ enum eMenuItemSurroundSound
+ {
+ MONO_SOUND,
+ STEREO_SOUND,
+ SURROUND_SOUND,
+
+ NUM_SOUND_SETTINGS
+ };
+#endif
+
+ CGuiMenu* m_pMenu;
+
+ Scrooby::Sprite* m_soundOffIcons[ NUM_MENU_ITEMS ];
+ bool m_hasSliderValueChanged[ NUM_MENU_ITEMS ];
+
+};
+
+#endif // GUISCREENSOUND_H
diff --git a/game/code/presentation/gui/frontend/guiscreensplash.cpp b/game/code/presentation/gui/frontend/guiscreensplash.cpp
new file mode 100644
index 0000000..5b8cec7
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreensplash.cpp
@@ -0,0 +1,344 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenSplash
+//
+// Description: Implementation of the CGuiScreenSplash class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreensplash.h>
+#include <presentation/gui/frontend/guiscreenplaymovie.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/frontend/guimanagerfrontend.h>
+
+#include <cheats/cheatinputsystem.h>
+#include <loading/loadingmanager.h>
+#include <main/commandlineoptions.h>
+
+#include <raddebug.hpp> // Foundation
+#include <page.h>
+#include <screen.h>
+#include <sprite.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+short CGuiScreenSplash::s_demoPlaybackToggle = 0;
+
+const unsigned int SPLASH_SCREEN_DEMO_WAIT_TIME = 30000; // in msec
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenSplash::CGuiScreenSplash
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenSplash::CGuiScreenSplash
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_SPLASH ),
+ m_pMenu( NULL ),
+ m_pressStart( NULL ),
+ m_demoLoopTime( SPLASH_SCREEN_DEMO_WAIT_TIME ),
+ m_elapsedTime( 0 )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "Splash" );
+ rAssert( pPage != NULL );
+/*
+ Scrooby::Sprite* splashImage = pPage->GetSprite( "GameLogo" );
+ if( splashImage != NULL )
+ {
+ const float SPLASH_SCREEN_IMAGE_CORRECTION_SCALE = 2.0f;
+ splashImage->ScaleAboutCenter( SPLASH_SCREEN_IMAGE_CORRECTION_SCALE );
+ }
+*/
+ // Create a menu.
+ //
+ m_pMenu = new CGuiMenu( this );
+ rAssert( m_pMenu != NULL );
+
+ m_pressStart = pPage->GetText( "PressStart" );
+ rAssert( m_pressStart != NULL );
+
+ m_pMenu->AddMenuItem( m_pressStart,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ SELECTION_ENABLED | TEXT_OUTLINE_ENABLED );
+
+ // set "press start" text to "loading ...", by default
+ //
+ m_pressStart->SetIndex( TEXT_LOADING );
+ m_pressStart->SetColour( tColour( 255, 255, 255 ) );
+
+ if( CommandLineOptions::Get( CLO_DEMO_TEST ) ||
+ GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_DEMO_TEST ) )
+ {
+ m_demoLoopTime = 1000; // in msec
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenSplash::~CGuiScreenSplash
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenSplash::~CGuiScreenSplash()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenSplash::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSplash::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ if( !m_pMenu->HasSelectionBeenMade() )
+ {
+ m_elapsedTime += param1;
+ if( m_elapsedTime > m_demoLoopTime )
+ {
+ if( s_demoPlaybackToggle != 0 )
+ {
+ if ( CommandLineOptions::Get( CLO_DEMO_TEST ) ||
+ GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_DEMO_TEST ) )
+ {
+ this->StartDemoInRuntime();
+ }
+ else
+ {
+ this->StartDemoAsMovie();
+ }
+ }
+ else
+ {
+ if ( CommandLineOptions::Get( CLO_DEMO_TEST ) ||
+ GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_DEMO_TEST ) )
+ {
+ this->StartDemoInRuntime();
+ }
+ else
+ {
+ this->StartDemoAsMovie();
+ }
+ }
+
+ // toggle demo playback format (btw. movie and runtime)
+ //
+ s_demoPlaybackToggle = 1 - s_demoPlaybackToggle;
+
+ m_elapsedTime = 0;
+ }
+ }
+
+ if( m_pressStart != NULL && !GetLoadingManager()->IsLoading() ) // sound loading is done
+ {
+ rAssert( m_pMenu != NULL );
+
+ m_pressStart->SetIndex( TEXT_PRESS_START_GC + PLATFORM_TEXT_INDEX);
+ m_pressStart->SetColour( m_pMenu->GetHighlightColour() );
+ m_pMenu->GetMenuItem( 0 )->m_attributes |= SELECTABLE;
+
+ // set this to NULL cuz we don't need to change it anymore
+ //
+ m_pressStart = NULL;
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ // ignore controller select inputs
+ //
+ return;
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_START:
+ {
+
+ GetGuiSystem()->SetPrimaryController((int)param1);
+
+ if( GetLoadingManager()->IsLoading() )
+ {
+ // ignore start input until all loading is done
+ //
+ return;
+ }
+
+ if( !m_pMenu->HasSelectionBeenMade() )
+ {
+ m_pMenu->MakeSelection();
+ }
+
+ break;
+ }
+
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ m_pParent->HandleMessage( GUI_MSG_SPLASH_SCREEN_DONE );
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ //
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+//===========================================================================
+// CGuiScreenSplash::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSplash::InitIntro()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenSplash::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSplash::InitRunning()
+{
+ m_elapsedTime = 0;
+}
+
+
+//===========================================================================
+// CGuiScreenSplash::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSplash::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenSplash::StartDemoInRuntime()
+{
+ // Quit FE and start the demo in runtime.
+ //
+ m_pParent->HandleMessage( GUI_MSG_QUIT_FRONTEND, 0 ); // 0 = demo mode
+}
+
+void
+CGuiScreenSplash::StartDemoAsMovie()
+{
+#ifndef RAD_WIN32
+ // Play demo movie.
+ //
+ rAssert( m_guiManager );
+ CGuiScreenPlayMovie* playMovieScreen = static_cast<CGuiScreenPlayMovie*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_PLAY_MOVIE_DEMO ) );
+ rAssert( playMovieScreen );
+ playMovieScreen->SetMovieToPlay( MovieNames::DEMO );
+
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ GUI_SCREEN_ID_PLAY_MOVIE_DEMO );
+#endif
+}
+
diff --git a/game/code/presentation/gui/frontend/guiscreensplash.h b/game/code/presentation/gui/frontend/guiscreensplash.h
new file mode 100644
index 0000000..86568ce
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreensplash.h
@@ -0,0 +1,73 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenSplash
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENSPLASH_H
+#define GUISCREENSPLASH_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenSplash : public CGuiScreen
+{
+public:
+ CGuiScreenSplash( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenSplash();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void StartDemoAsMovie();
+ void StartDemoInRuntime();
+
+ enum ePressStartText
+ {
+ TEXT_LOADING,
+ TEXT_PRESS_START_GC,
+ TEXT_PRESS_START_PS2,
+ TEXT_PRESS_START_XBOX,
+
+ NUM_PRESS_START_TEXTS
+ };
+
+ CGuiMenu* m_pMenu;
+ Scrooby::Text* m_pressStart;
+
+ unsigned int m_demoLoopTime;
+ unsigned int m_elapsedTime;
+
+ static short s_demoPlaybackToggle;
+
+};
+
+#endif // GUISCREENSPLASH_H
diff --git a/game/code/presentation/gui/frontend/guiscreenvehiclegallery.cpp b/game/code/presentation/gui/frontend/guiscreenvehiclegallery.cpp
new file mode 100644
index 0000000..7619c94
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenvehiclegallery.cpp
@@ -0,0 +1,837 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenVehicleGallery
+//
+// Description: Implementation of the CGuiScreenVehicleGallery class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/21 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenvehiclegallery.h>
+#include <presentation/gui/frontend/guiscreenscrapbookcontents.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/guimenu.h>
+
+#include <cheats/cheatinputsystem.h>
+#include <memory/srrmemory.h>
+#include <mission/rewards/rewardsmanager.h>
+#include <mission/rewards/reward.h>
+#include <events/eventmanager.h>
+
+#include <p3d/inventory.hpp>
+
+#include <raddebug.hpp> // Foundation
+
+#include <screen.h>
+#include <page.h>
+#include <layer.h>
+#include <group.h>
+#include <text.h>
+
+#include <string.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const char* VEHICLE_GALLERY_INVENTORY_SECTION = "FE_VehicleGallery";
+const char* VEHICLE_GALLERY_IMAGES_DIR = "art\\frontend\\dynaload\\images\\cars2d\\";
+
+const float VEHICLE_VIEW_TRANSITION_TIME = 250.0f; // in msec
+const float VEHICLE_VIEW_PROJECTILE_GRAVITY = 0.005f; // in m/ms/ms
+const int VEHICLE_VIEW_POS_X = 250;
+const int VEHICLE_VIEW_POS_Y = 140;
+#ifdef RAD_WIN32
+const float VEHICLE_BASE_SCALE = 0.66f;
+const float VEHICLE_IMAGE_SCALE = 1.33f - 0.66f;
+#else
+const float VEHICLE_BASE_SCALE = 1.0f;
+const float VEHICLE_IMAGE_SCALE = 2.0f - 1.0f;
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenVehicleGallery::CGuiScreenVehicleGallery
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenVehicleGallery::CGuiScreenVehicleGallery
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_VEHICLE_GALLERY ),
+ m_pMenu( NULL ),
+ m_numSelections( 0 ),
+ m_isVehiclesLoaded( false ),
+ m_screenState( SCREEN_STATE_NORMAL ),
+ m_elapsedTime( 0 ),
+ m_projectileVelocity( 0.0f, 0.0f, 0.0f ),
+ m_vehicleInfo( NULL ),
+#ifdef RAD_WIN32
+ m_selectedVehicle(0),
+#endif
+ m_vehicleName( NULL )
+{
+ memset( m_rewardSelections, 0, sizeof( m_rewardSelections ) );
+
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "VehicleGallery" );
+ rAssert( pPage != NULL );
+
+ // create a 2D sprite menu
+ //
+ m_pMenu = new CGuiMenu2D( this, MAX_NUM_VEHICLES_PER_LEVEL, 3, GUI_SPRITE_MENU, MENU_SFX_NONE );
+ rAssert( m_pMenu != NULL );
+ m_pMenu->SetGreyOutEnabled( false );
+
+ Scrooby::Group* pGroup = pPage->GetGroup( "Menu" );
+ rAssert( pGroup != NULL );
+
+ // add sprites to menu
+ //
+ for( int i = 0; i < MAX_NUM_VEHICLES_PER_LEVEL; i++ )
+ {
+ char name[ 16 ];
+ sprintf( name, "Vehicle%d", i );
+
+ m_pMenu->AddMenuItem( pGroup->GetSprite( name ) );
+ }
+
+ // add menu cursor
+ //
+ Scrooby::Sprite* pCursor = pGroup->GetSprite( "VehicleCursor" );
+ if( pCursor != NULL )
+ {
+ // scale up cursor a bit for vehicles
+ //
+ pCursor->ScaleAboutCenter( 2.0f );
+
+ m_pMenu->SetCursor( pCursor );
+ }
+
+ // get vehicle info layer and name
+ //
+ m_vehicleInfo = pPage->GetLayer( "ViewVehicle" );
+ m_vehicleName = pPage->GetText( "VehicleName" );
+
+ // hide vehicle info layer by default
+ //
+ rAssert( m_vehicleInfo != NULL );
+ m_vehicleInfo->SetVisible( false );
+ m_vehicleInfo->SetAlpha( 0.0f );
+
+ if( this->IsWideScreenDisplay() )
+ {
+ m_vehicleInfo->ResetTransformation();
+ this->ApplyWideScreenCorrectionScale( m_vehicleInfo );
+ }
+
+ // wrap vehicle name
+ //
+// rAssert( m_vehicleName != NULL );
+// m_vehicleName->SetTextMode( Scrooby::TEXT_WRAP );
+
+ // add outline to vehicle name
+ //
+ m_vehicleName->SetDisplayOutline( true );
+
+ // create inventory section for vehicle galllery resources
+ //
+ p3d::inventory->AddSection( VEHICLE_GALLERY_INVENTORY_SECTION );
+}
+
+
+//===========================================================================
+// CGuiScreenVehicleGallery::~CGuiScreenVehicleGallery
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenVehicleGallery::~CGuiScreenVehicleGallery()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+
+ // destroy vehicle gallery inventory section
+ //
+ p3d::inventory->DeleteSection( VEHICLE_GALLERY_INVENTORY_SECTION );
+}
+
+
+//===========================================================================
+// CGuiScreenVehicleGallery::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenVehicleGallery::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_screenState == SCREEN_STATE_GOTO_VIEW ||
+ m_screenState == SCREEN_STATE_BACK_VIEW )
+ {
+ if( message == GUI_MSG_UPDATE )
+ {
+ this->OnUpdate( param1 );
+ }
+
+ return;
+ }
+
+ if( m_screenState == SCREEN_STATE_VIEWING )
+ {
+ if( message == GUI_MSG_UPDATE )
+ {
+ this->OnUpdate( param1 );
+ }
+ else if( message == GUI_MSG_CONTROLLER_BACK )
+ {
+ m_screenState = SCREEN_STATE_BACK_VIEW;
+ m_elapsedTime = 0;
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_BACK ); // sound effect
+ }
+
+ return;
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ this->OnUpdate( param1 );
+
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_CHANGED:
+ {
+ this->OnMenuSelectionChange( static_cast<int>( param1 ) );
+
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ this->OnMenuSelectionMade( static_cast<int>( param1 ) );
+
+#ifdef RAD_WIN32
+ m_selectedVehicle = static_cast<int>( param1 );
+ // Hide/disable all other menu items.
+ this->SetVisibilityForAllOtherMenuItems( false );
+#endif
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ //
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenVehicleGallery::OnProcessRequestsComplete
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void
+CGuiScreenVehicleGallery::OnProcessRequestsComplete( void* pUserData )
+{
+ m_numTransitionsPending--;
+
+ p3d::pddi->DrawSync();
+
+ // push and select inventory section for searching
+ //
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( VEHICLE_GALLERY_INVENTORY_SECTION );
+ bool currentSectionOnly = p3d::inventory->GetCurrentSectionOnly();
+ p3d::inventory->SetCurrentSectionOnly( true );
+
+ // update all 3D models
+ //
+ CGuiScreenScrapBookContents* pScreen = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( pScreen != NULL );
+
+ for( int i = 0; i < m_numSelections; i++ )
+ {
+ if( (m_pMenu->GetMenuItem( i )->m_attributes & SELECTION_ENABLED) > 0 )
+ {
+ char name[ 16 ];
+ sprintf( name, "%s.png", m_rewardSelections[ i ]->GetName() );
+ tSprite* pSprite = p3d::find<tSprite>( name );
+ if( pSprite != NULL )
+ {
+ Scrooby::Sprite* vehicleImage = dynamic_cast<Scrooby::Sprite*>( m_pMenu->GetMenuItem( i )->GetItem() );
+ rAssert( vehicleImage != NULL );
+ vehicleImage->SetRawSprite( pSprite, true );
+ }
+ else
+ {
+ rAssertMsg( false, "Vehicle image not found!" );
+ }
+ }
+ }
+
+ // pop inventory section and restore states
+ //
+ p3d::inventory->SetCurrentSectionOnly( currentSectionOnly );
+ p3d::inventory->PopSection();
+
+ m_isVehiclesLoaded = true;
+}
+
+//===========================================================================
+// CGuiScreenVehicleGallery::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenVehicleGallery::InitIntro()
+{
+ if( !m_isVehiclesLoaded )
+ {
+ this->Load2DImages();
+ }
+
+ this->OnMenuSelectionChange( m_pMenu->GetSelection() );
+
+ GetEventManager()->TriggerEvent( EVENT_PLAY_MUZAK );
+}
+
+
+//===========================================================================
+// CGuiScreenVehicleGallery::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenVehicleGallery::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenVehicleGallery::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenVehicleGallery::InitOutro()
+{
+ if( m_isVehiclesLoaded )
+ {
+ this->Unload2DImages();
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_PLAY_FE_MUSIC );
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenVehicleGallery::OnUpdate( unsigned int elapsedTime )
+{
+ switch( m_screenState )
+ {
+ case SCREEN_STATE_NORMAL:
+ {
+ // pulse cursor alpha
+ //
+ Scrooby::Drawable* cursor = m_pMenu->GetCursor();
+ if( cursor != NULL )
+ {
+ const unsigned int PULSE_PERIOD = 1000;
+
+ float alpha = GuiSFX::Pulse( (float)m_elapsedTime,
+ (float)PULSE_PERIOD,
+ 0.75f,
+ 0.25f,
+ -rmt::PI_BY2 );
+
+ cursor->SetAlpha( alpha );
+
+ m_elapsedTime += elapsedTime;
+ m_elapsedTime %= PULSE_PERIOD;
+ }
+
+ break;
+ }
+ case SCREEN_STATE_GOTO_VIEW:
+ {
+ m_elapsedTime += elapsedTime;
+
+ rAssert( m_pMenu != NULL );
+ int currentSelection = m_pMenu->GetSelection();
+
+ Scrooby::BoundedDrawable* pDrawable = m_pMenu->GetMenuItem( currentSelection )->GetItem();
+ rAssert( pDrawable != NULL );
+
+ pDrawable->ResetTransformation();
+
+ if( m_elapsedTime < (unsigned int)VEHICLE_VIEW_TRANSITION_TIME )
+ {
+ float percentageDone = m_elapsedTime / VEHICLE_VIEW_TRANSITION_TIME;
+
+ pDrawable->ScaleAboutCenter( VEHICLE_BASE_SCALE + percentageDone * VEHICLE_IMAGE_SCALE );
+
+ GuiSFX::Projectile( pDrawable,
+ (float)m_elapsedTime,
+ VEHICLE_VIEW_TRANSITION_TIME,
+ m_projectileVelocity,
+ false,
+ VEHICLE_VIEW_PROJECTILE_GRAVITY );
+
+ // fade out rest of the menu items
+ //
+ this->SetMenuAlpha( 1.0f - rmt::Sqrt( percentageDone ) );
+
+ // fade in vehicle info layer
+ //
+ rAssert( m_vehicleInfo != NULL );
+ m_vehicleInfo->SetAlpha( percentageDone * percentageDone );
+ }
+ else
+ {
+ pDrawable->ScaleAboutCenter( VEHICLE_BASE_SCALE + VEHICLE_IMAGE_SCALE );
+
+ GuiSFX::Projectile( pDrawable,
+ VEHICLE_VIEW_TRANSITION_TIME,
+ VEHICLE_VIEW_TRANSITION_TIME,
+ m_projectileVelocity,
+ false,
+ VEHICLE_VIEW_PROJECTILE_GRAVITY );
+
+ this->SetMenuAlpha( 0.0f );
+
+ rAssert( m_vehicleInfo != NULL );
+ m_vehicleInfo->SetAlpha( 1.0f );
+
+ m_screenState = SCREEN_STATE_VIEWING;
+ }
+
+ break;
+ }
+ case SCREEN_STATE_VIEWING:
+ {
+
+ break;
+ }
+ case SCREEN_STATE_BACK_VIEW:
+ {
+ m_elapsedTime += elapsedTime;
+
+ rAssert( m_pMenu != NULL );
+ int currentSelection = m_pMenu->GetSelection();
+
+ Scrooby::BoundedDrawable* pDrawable = m_pMenu->GetMenuItem( currentSelection )->GetItem();
+ rAssert( pDrawable != NULL );
+
+ pDrawable->ResetTransformation();
+
+ if( m_elapsedTime < (unsigned int)VEHICLE_VIEW_TRANSITION_TIME )
+ {
+ float percentageDone = m_elapsedTime / VEHICLE_VIEW_TRANSITION_TIME;
+
+ pDrawable->ScaleAboutCenter( VEHICLE_BASE_SCALE + (1.0f - percentageDone) * VEHICLE_IMAGE_SCALE );
+
+ GuiSFX::Projectile( pDrawable,
+ (float)m_elapsedTime,
+ VEHICLE_VIEW_TRANSITION_TIME,
+ m_projectileVelocity,
+ true,
+ VEHICLE_VIEW_PROJECTILE_GRAVITY );
+
+ // fade back in rest of the menu items
+ //
+ this->SetMenuAlpha( percentageDone * percentageDone );
+
+ // fade in vehicle info layer
+ //
+ rAssert( m_vehicleInfo != NULL );
+ m_vehicleInfo->SetAlpha( 1.0f - rmt::Sqrt( percentageDone ) );
+ }
+ else
+ {
+ pDrawable->ScaleAboutCenter( VEHICLE_BASE_SCALE );
+
+ GuiSFX::Projectile( pDrawable,
+ VEHICLE_VIEW_TRANSITION_TIME,
+ VEHICLE_VIEW_TRANSITION_TIME,
+ m_projectileVelocity,
+ true,
+ VEHICLE_VIEW_PROJECTILE_GRAVITY );
+
+ // show menu cursor
+ //
+ rAssert( m_pMenu != NULL );
+ m_pMenu->GetCursor()->SetVisible( true );
+
+ // show level bar
+ //
+ CGuiScreenScrapBookContents* scrapBookContents = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( scrapBookContents != NULL );
+ scrapBookContents->SetLevelBarVisible( true );
+
+ this->SetMenuAlpha( 1.0f );
+
+ rAssert( m_vehicleInfo != NULL );
+ m_vehicleInfo->SetAlpha( 0.0f );
+
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+
+ // hide vehicle info layer
+ //
+ rAssert( m_vehicleInfo != NULL );
+ m_vehicleInfo->SetVisible( false );
+
+ m_elapsedTime = 0;
+ m_screenState = SCREEN_STATE_NORMAL;
+#ifdef RAD_WIN32
+ // Show/enable all hidden menu items.
+ this->SetVisibilityForAllOtherMenuItems( true );
+#endif
+ }
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Invalid screen state!" );
+
+ break;
+ }
+ }
+}
+
+void
+CGuiScreenVehicleGallery::OnMenuSelectionChange( int selection )
+{
+ // scale up new selection
+ //
+ for( int i = 0; i < m_numSelections; i++ )
+ {
+ Scrooby::BoundedDrawable* drawable = m_pMenu->GetMenuItem( i )->GetItem();
+ rAssert( drawable != NULL );
+ drawable->ResetTransformation();
+ drawable->ScaleAboutCenter( VEHICLE_BASE_SCALE );
+
+ if( i != selection )
+ {
+ drawable->ScaleAboutCenter( 0.9f );
+ }
+ }
+}
+
+void
+CGuiScreenVehicleGallery::OnMenuSelectionMade( int selection )
+{
+ if( m_numSelections > 0 )
+ {
+ // hide level bar
+ //
+ CGuiScreenScrapBookContents* scrapBookContents = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( scrapBookContents != NULL );
+ scrapBookContents->SetLevelBarVisible( false );
+
+ // hide menu cursor
+ //
+ rAssert( m_pMenu != NULL );
+ m_pMenu->GetCursor()->SetVisible( false );
+
+ // show vehicle info layer
+ //
+ rAssert( m_vehicleInfo != NULL );
+ m_vehicleInfo->SetVisible( true );
+
+ // update vehicle name
+ //
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+
+ char stringID[ 16 ];
+ rAssert( m_rewardSelections[ m_pMenu->GetSelection() ] != NULL );
+ strcpy( stringID, m_rewardSelections[ m_pMenu->GetSelection() ]->GetName() );
+
+ UnicodeString unicodeString;
+ unicodeString.ReadUnicode( GetTextBibleString( strupr( stringID ) ) );
+
+ rAssert( m_vehicleName != NULL );
+ m_vehicleName->SetString( 0, unicodeString );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_FE );
+
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, false );
+
+ // calculate the initial projectile velocity
+ //
+ Scrooby::BoundedDrawable* pDrawable = m_pMenu->GetMenuItem( selection )->GetItem();
+ rAssert( pDrawable != NULL );
+
+ int startPosX = 0;
+ int startPosY = 0;
+ pDrawable->GetOriginPosition( startPosX, startPosY );
+
+ m_projectileVelocity.x = (VEHICLE_VIEW_POS_X - startPosX) / VEHICLE_VIEW_TRANSITION_TIME;
+ m_projectileVelocity.y = (VEHICLE_VIEW_POS_Y - startPosY - 0.5f * VEHICLE_VIEW_PROJECTILE_GRAVITY * VEHICLE_VIEW_TRANSITION_TIME * VEHICLE_VIEW_TRANSITION_TIME) / VEHICLE_VIEW_TRANSITION_TIME;
+
+ m_screenState = SCREEN_STATE_GOTO_VIEW;
+ m_elapsedTime = 0;
+ }
+}
+
+void
+CGuiScreenVehicleGallery::SetMenuAlpha( float alpha )
+{
+ rAssert( m_pMenu != NULL );
+ int currentSelection = m_pMenu->GetSelection();
+
+ for( int i = 0; i < MAX_NUM_VEHICLES_PER_LEVEL; i++ )
+ {
+ if( i != currentSelection )
+ {
+ Scrooby::Drawable* vehicleImage = m_pMenu->GetMenuItem( i )->GetItem();
+ rAssert( vehicleImage != NULL );
+ vehicleImage->SetAlpha( alpha );
+ }
+ }
+}
+
+void
+CGuiScreenVehicleGallery::Load2DImages()
+{
+ // load 2D images for current level
+ //
+ CGuiScreenScrapBookContents* pScreen = static_cast<CGuiScreenScrapBookContents*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS ) );
+ rAssert( pScreen != NULL );
+
+ Reward* pReward = NULL;
+ char filename[ 64 ];
+ m_numSelections = 0;
+
+ // load earnable reward and default vehicles
+ //
+ for( int i = Reward::eBlank + 1; i < Reward::NUM_QUESTS; i++ )
+ {
+ pReward = GetRewardsManager()->GetReward( pScreen->GetCurrentLevel(), static_cast<Reward::eQuestType>( i ) );
+ if( pReward != NULL )
+ {
+ if( pReward->GetRewardType() == Reward::ALT_PLAYERCAR )
+ {
+ rAssert( m_numSelections < MAX_NUM_VEHICLES_PER_LEVEL );
+
+ // store reference to reward
+ //
+ m_rewardSelections[ m_numSelections ] = pReward;
+
+ sprintf( filename, "%s%s.p3d", VEHICLE_GALLERY_IMAGES_DIR, pReward->GetName() );
+ rAssert( strlen( filename) < sizeof( filename ) );
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ filename,
+ GMA_LEVEL_FE,
+ VEHICLE_GALLERY_INVENTORY_SECTION,
+ VEHICLE_GALLERY_INVENTORY_SECTION );
+
+ m_numSelections++;
+ }
+ }
+ }
+
+ // load merchandise vehicles (from GIL)
+ //
+ for( pReward = GetRewardsManager()->FindFirstMerchandise( pScreen->GetCurrentLevel(), Merchandise::SELLER_GIL );
+ pReward != NULL;
+ pReward = GetRewardsManager()->FindNextMerchandise( pScreen->GetCurrentLevel(), Merchandise::SELLER_GIL ) )
+ {
+ rAssert( m_numSelections < MAX_NUM_VEHICLES_PER_LEVEL );
+
+ // store reference to reward
+ //
+ m_rewardSelections[ m_numSelections ] = pReward;
+
+ sprintf( filename, "%s%s.p3d", VEHICLE_GALLERY_IMAGES_DIR, pReward->GetName() );
+ rAssert( strlen( filename) < sizeof( filename ) );
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ filename,
+ GMA_LEVEL_FE,
+ VEHICLE_GALLERY_INVENTORY_SECTION,
+ VEHICLE_GALLERY_INVENTORY_SECTION );
+
+ m_numSelections++;
+ }
+
+ // load merchandise vehicles (from SIMPSON CHARACTER)
+ //
+ for( pReward = GetRewardsManager()->FindFirstMerchandise( pScreen->GetCurrentLevel(), Merchandise::SELLER_SIMPSON );
+ pReward != NULL;
+ pReward = GetRewardsManager()->FindNextMerchandise( pScreen->GetCurrentLevel(), Merchandise::SELLER_SIMPSON ) )
+ {
+ rAssert( m_numSelections < MAX_NUM_VEHICLES_PER_LEVEL );
+
+ // store reference to reward
+ //
+ m_rewardSelections[ m_numSelections ] = pReward;
+
+ sprintf( filename, "%s%s.p3d", VEHICLE_GALLERY_IMAGES_DIR, pReward->GetName() );
+ rAssert( strlen( filename) < sizeof( filename ) );
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ filename,
+ GMA_LEVEL_FE,
+ VEHICLE_GALLERY_INVENTORY_SECTION,
+ VEHICLE_GALLERY_INVENTORY_SECTION );
+
+ m_numSelections++;
+ }
+
+ rWarningMsg( m_numSelections > 0, "No model selections available!" );
+
+ if( m_numSelections > 0 )
+ {
+ GetLoadingManager()->AddCallback( this );
+
+ m_numTransitionsPending++;
+ }
+
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, false ); // hide by default
+
+ for( int i = 0; i < MAX_NUM_VEHICLES_PER_LEVEL; i++ )
+ {
+ if( i < m_numSelections )
+ {
+ bool isUnlocked = m_rewardSelections[ i ]->RewardStatus();
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_VEHICLES ) )
+ {
+ isUnlocked = true;
+ }
+
+ m_pMenu->SetMenuItemEnabled( i, isUnlocked );
+
+ if( isUnlocked )
+ {
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+ }
+ }
+ else
+ {
+ m_pMenu->SetMenuItemEnabled( i, false );
+ }
+ }
+}
+
+void
+CGuiScreenVehicleGallery::Unload2DImages()
+{
+ p3d::pddi->DrawSync();
+
+ // clear all drawables
+ //
+ for( int i = 0; i < MAX_NUM_VEHICLES_PER_LEVEL; i++ )
+ {
+ Scrooby::Sprite* vehicleImage = dynamic_cast<Scrooby::Sprite*>( m_pMenu->GetMenuItem( i )->GetItem() );
+ rAssert( vehicleImage != NULL );
+ vehicleImage->SetRawSprite( NULL, true );
+
+ m_pMenu->SetMenuItemEnabled( i, false );
+ }
+
+ // unload 2D images
+ //
+ p3d::inventory->RemoveSectionElements( VEHICLE_GALLERY_INVENTORY_SECTION );
+
+ m_isVehiclesLoaded = false;
+}
+#ifdef RAD_WIN32
+void CGuiScreenVehicleGallery::SetVisibilityForAllOtherMenuItems( bool bVisible )
+{
+ for( int i = 0; i < MAX_NUM_VEHICLES_PER_LEVEL; i++ )
+ {
+ if( i != m_selectedVehicle )
+ m_pMenu->GetMenuItem(i)->GetItem()->SetVisible( bVisible );
+ }
+}
+#endif
diff --git a/game/code/presentation/gui/frontend/guiscreenvehiclegallery.h b/game/code/presentation/gui/frontend/guiscreenvehiclegallery.h
new file mode 100644
index 0000000..6ddc500
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenvehiclegallery.h
@@ -0,0 +1,100 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenVehicleGallery
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/21 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENVEHICLEGALLERY_H
+#define GUISCREENVEHICLEGALLERY_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <loading/loadingmanager.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CGuiMenu2D;
+class Reward;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenVehicleGallery : public CGuiScreen,
+ public LoadingManager::ProcessRequestsCallback
+{
+public:
+ CGuiScreenVehicleGallery( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenVehicleGallery();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+ // Implements LoadingManager::ProcessRequestsCallback
+ //
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void OnUpdate( unsigned int elapsedTime );
+ void OnMenuSelectionChange( int selection );
+ void OnMenuSelectionMade( int selection );
+ void SetMenuAlpha( float alpha );
+#ifdef RAD_WIN32
+ void SetVisibilityForAllOtherMenuItems( bool bDisable );
+#endif
+
+ void Load2DImages();
+ void Unload2DImages();
+
+ static const int MAX_NUM_VEHICLES_PER_LEVEL = 6;
+
+ CGuiMenu2D* m_pMenu;
+
+ Reward* m_rewardSelections[ MAX_NUM_VEHICLES_PER_LEVEL ];
+ int m_numSelections;
+
+ bool m_isVehiclesLoaded : 1;
+
+ enum eScreenState
+ {
+ SCREEN_STATE_NORMAL,
+ SCREEN_STATE_GOTO_VIEW,
+ SCREEN_STATE_VIEWING,
+ SCREEN_STATE_BACK_VIEW,
+
+ NUM_SCREEN_STATES
+ };
+
+ eScreenState m_screenState;
+ unsigned int m_elapsedTime;
+ rmt::Vector m_projectileVelocity;
+
+ Scrooby::Layer* m_vehicleInfo;
+ Scrooby::Text* m_vehicleName;
+
+#ifdef RAD_WIN32
+ int m_selectedVehicle;
+#endif
+};
+
+#endif // GUISCREENVEHICLEGALLERY_H
diff --git a/game/code/presentation/gui/frontend/guiscreenviewcredits.cpp b/game/code/presentation/gui/frontend/guiscreenviewcredits.cpp
new file mode 100644
index 0000000..ed3a614
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenviewcredits.cpp
@@ -0,0 +1,503 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenViewCredits
+//
+// Description: Implementation of the CGuiScreenViewCredits class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/05/14 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenviewcredits.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guiuserinputhandler.h>
+
+#include <cheats/cheatinputsystem.h>
+#include <events/eventmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+// Scrooby
+#include <app.h>
+#include <page.h>
+#include <screen.h>
+#include <group.h>
+#include <text.h>
+#include <sprite.h>
+
+// ATG
+#include <raddebug.hpp>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const float CREDITS_SCROLL_RATE = 20.0f; // in pixels per second
+const float CREDITS_MANUAL_SCROLL_FACTOR = 5.0f;
+const float CREDITS_FGD_CORRECTION_SCALE = 2.0f;
+
+const int CREDITS_PARTITION_FOR_LOCALIZATION = 13;
+const int CREDITS_PARTITION_FOR_LOGITECH = 30;
+
+float CGuiScreenViewCredits::s_totalTranslateY = 0.0f;
+float CGuiScreenViewCredits::s_numPixelsPerLine = 0.0f;
+float CGuiScreenViewCredits::s_creditsOffsets[ CGuiScreenViewCredits::MAX_NUM_CREDITS_PARTITIONS ];
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenViewCredits::CGuiScreenViewCredits
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenViewCredits::CGuiScreenViewCredits
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_VIEW_CREDITS ),
+ m_playKKDialog( false ),
+ m_creditsGroup( NULL ),
+ m_currentTranslateY( 0.0f ),
+ m_lastLineDisplayed( 0 ),
+ m_elapsedIdleTime( 0 )
+{
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "ViewCredits" );
+ rAssert( pPage != NULL );
+
+ // get credits group drawable
+ //
+ m_creditsGroup = pPage->GetGroup( "Credits" );
+ rAssert( m_creditsGroup != NULL );
+
+ bool isFirstTime = ( s_totalTranslateY == 0.0f );
+ float currentTranslateY = 0.0f;
+
+ // get all credits (partitioned) text
+ //
+ for( int i = 0; i < MAX_NUM_CREDITS_PARTITIONS; i++ )
+ {
+ char name[ 16 ];
+ sprintf( name, "Credits%d", i );
+ m_credits[ i ] = m_creditsGroup->GetText( name );
+
+ if( m_credits[ i ] != NULL )
+ {
+#ifndef PAL
+ // hide localization partition from credits for NTSC builds
+ //
+ if( i == CREDITS_PARTITION_FOR_LOCALIZATION )
+ {
+ m_credits[ i ]->SetVisible( false );
+
+ continue;
+ }
+#endif // !PAL
+
+#ifndef RAD_GAMECUBE
+ #ifndef RAD_PS2
+ // hide logitech line from credits for non-GC and non-PS2 platforms
+ //
+ if( i == CREDITS_PARTITION_FOR_LOGITECH )
+ {
+ m_credits[ i ]->SetVisible( false );
+
+ continue;
+ }
+ #endif // !RAD_PS2
+#endif // !RAD_GAMECUBE
+
+ if( isFirstTime )
+ {
+ currentTranslateY = this->FormatTextWithLineBreaks( m_credits[ i ] );
+
+ s_creditsOffsets[ i ] = -s_totalTranslateY;
+ s_totalTranslateY += currentTranslateY;
+ }
+
+ // offset current credits partition by total translation so far
+ //
+ m_credits[ i ]->ResetTransformation();
+ m_credits[ i ]->Translate( 0, (int)s_creditsOffsets[ i ] );
+
+ // disable any text outlining or drop shadow effects
+ //
+ m_credits[ i ]->SetDisplayOutline( false );
+ m_credits[ i ]->SetDisplayShadow( false );
+ }
+ else
+ {
+ // assuming there's no more partitions to follow
+ //
+ break;
+ }
+ }
+
+ // increment total translation for the screen area
+ //
+ if( isFirstTime )
+ {
+ s_totalTranslateY += Scrooby::App::GetInstance()->GetScreenHeight();
+ }
+
+/*
+ // get skip label
+ //
+ m_buttonIcons[ BUTTON_ICON_ACCEPT ] = pPage->GetGroup( "SkipLabel" );
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, false );
+*/
+
+ // apply correction scale to credits foreground overlay image (from CreditsFgd.pag)
+ //
+ pPage = m_pScroobyScreen->GetPage( "CreditsFgd" );
+ if( pPage != NULL )
+ {
+ Scrooby::Sprite* creditsFgd = pPage->GetSprite( "CreditsFgd" );
+ if( creditsFgd != NULL )
+ {
+ creditsFgd->ResetTransformation();
+ creditsFgd->ScaleAboutCenter( CREDITS_FGD_CORRECTION_SCALE );
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenViewCredits::~CGuiScreenViewCredits
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenViewCredits::~CGuiScreenViewCredits()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenViewCredits::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenViewCredits::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( message == GUI_MSG_WINDOW_ENTER )
+ {
+ this->SetFadingEnabled( !GetGuiSystem()->IsShowCreditsUponReturnToFE() );
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ float currentScrollAmount = param1 / 1000.0f * CREDITS_SCROLL_RATE;
+
+ // auto-scrolling
+ //
+ bool isScrollingDone = this->ScrollCredits( currentScrollAmount );
+
+ // check for up/down controller inputs for manual scroll control
+ //
+ const int NUM_USER_INPUT_HANDLERS = GetGuiSystem()->GetNumUserInputHandlers();
+
+ for( int i = 0; i < NUM_USER_INPUT_HANDLERS; i++ )
+ {
+ CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( i );
+ if( userInputHandler != NULL )
+ {
+#ifdef RAD_WIN32
+ if( userInputHandler->IsYAxisOnUp() )
+#else
+ if( userInputHandler->IsButtonDown( GuiInput::Up ) ||
+ userInputHandler->IsYAxisOnUp() )
+#endif
+ {
+ // scroll upwards at opposite rate
+ //
+ isScrollingDone = this->ScrollCredits( -(1.0f + CREDITS_MANUAL_SCROLL_FACTOR) * currentScrollAmount );
+ }
+
+ if( !m_playKKDialog ) // only allow speed-up if dialog is not playing
+ {
+#ifdef RAD_WIN32
+ if( userInputHandler->IsYAxisOnDown() )
+#else
+ if( userInputHandler->IsButtonDown( GuiInput::Down ) ||
+ userInputHandler->IsYAxisOnDown() )
+#endif
+ {
+ // double the scroll rate
+ //
+ isScrollingDone = this->ScrollCredits( (CREDITS_MANUAL_SCROLL_FACTOR - 1.0f) * currentScrollAmount );
+ }
+ }
+ }
+ }
+
+ // check if scroll idle time has expired
+ //
+ if( m_elapsedIdleTime > SCROLL_IDLE_TIME_BEFORE_RESET )
+ {
+ this->OnScrollingDone();
+ }
+ else if( isScrollingDone )
+ {
+ m_elapsedIdleTime += param1;
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ this->OnScrollingDone();
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ // ignore back controller inputs
+ //
+ return;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenViewCredits::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenViewCredits::InitIntro()
+{
+ // only play K&K credits dialog if either cheat is enabled or user has completed
+ // the last mission of the game
+ //
+ m_playKKDialog = GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_PLAY_CREDITS_DIALOG ) ||
+ GetCharacterSheetManager()->QueryMissionStatus( RenderEnums::L7, RenderEnums::M7 )->mCompleted;
+
+ this->ResetScrolling();
+
+ // hide back button label
+ //
+ this->SetButtonVisible( BUTTON_ICON_BACK, false );
+
+ GetEventManager()->TriggerEvent( EVENT_PLAY_CREDITS );
+}
+
+
+//===========================================================================
+// CGuiScreenViewCredits::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenViewCredits::InitRunning()
+{
+ if( GetGuiSystem()->IsShowCreditsUponReturnToFE() )
+ {
+ this->RestoreDefaultFadeTime();
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenViewCredits::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenViewCredits::InitOutro()
+{
+ if( GetGuiSystem()->IsShowCreditsUponReturnToFE() )
+ {
+ GetGuiSystem()->ShowCreditsUponReturnToFE( false );
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_DIALOG_SHUTUP );
+ GetEventManager()->TriggerEvent( EVENT_PLAY_FE_MUSIC );
+}
+
+
+void
+CGuiScreenViewCredits::ResetScrolling()
+{
+ rAssert( m_creditsGroup != NULL );
+ m_creditsGroup->ResetTransformation();
+
+ m_currentTranslateY = 0.0f;
+
+ m_lastLineDisplayed = 0;
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+float
+CGuiScreenViewCredits::FormatTextWithLineBreaks( Scrooby::Text* pText )
+{
+ rAssert( pText != NULL );
+
+ float totalTranslateY = 0.0f;
+
+ int width = 0;
+ int height = 0;
+ pText->GetBoundingBoxSize( width, height );
+
+#ifdef RAD_WIN32
+ const float SPACING_FUDGE_FACTOR = 1.20f;
+#else
+ const float SPACING_FUDGE_FACTOR = 1.15f;
+#endif
+
+ s_numPixelsPerLine = height * SPACING_FUDGE_FACTOR;
+
+ P3D_UNICODE* textBuffer = static_cast<P3D_UNICODE*>( pText->GetStringBuffer() );
+ rAssert( textBuffer != NULL );
+
+ for( int i = 0; textBuffer[ i ] != '\0'; i++ )
+ {
+ if( textBuffer[ i ] == '\\' ) // replace backslashes with line breaks
+ {
+ textBuffer[ i ] = '\n';
+
+ // increment total translation by number of pixels per line
+ //
+ totalTranslateY += s_numPixelsPerLine;
+ }
+ }
+
+ return totalTranslateY;
+}
+
+bool
+CGuiScreenViewCredits::ScrollCredits( float pixels )
+{
+ float newTranslateY = m_currentTranslateY + pixels;
+
+ bool isScrollingDone = ( newTranslateY > s_totalTranslateY );
+
+ if( newTranslateY >= 0 && newTranslateY <= s_totalTranslateY )
+ {
+ m_currentTranslateY = newTranslateY;
+
+ rAssert( m_creditsGroup != NULL );
+ m_creditsGroup->ResetTransformation();
+ m_creditsGroup->Translate( 0, (int)m_currentTranslateY );
+
+ // check if new line is displayed
+ //
+ const float BOTTOM_OFFSET = 80.0f; // in pixels
+ if( (m_currentTranslateY - BOTTOM_OFFSET) >= (m_lastLineDisplayed * s_numPixelsPerLine) )
+ {
+ m_lastLineDisplayed++;
+
+ this->OnNewLineDisplayed( m_lastLineDisplayed );
+ }
+
+ m_elapsedIdleTime = 0; // reset idle time
+ }
+ else
+ {
+ rDebugPrintf( "Credits not scrolling.\n" );
+ }
+
+ return isScrollingDone;
+}
+
+void
+CGuiScreenViewCredits::OnScrollingDone()
+{
+ if( GetGuiSystem()->IsShowCreditsUponReturnToFE() )
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ GUI_SCREEN_ID_MAIN_MENU,
+ CLEAR_WINDOW_HISTORY );
+ }
+ else if( m_guiManager->GetPreviousScreen() != GUI_WINDOW_ID_UNDEFINED )
+ {
+ this->StartTransitionAnimation( 630, 660 );
+
+ m_pParent->HandleMessage( GUI_MSG_BACK_SCREEN );
+ }
+}
+
+void
+CGuiScreenViewCredits::OnNewLineDisplayed( int lineNumber )
+{
+ rTunePrintf( "Credits Line #%d\n", lineNumber );
+
+ if( m_playKKDialog )
+ {
+ // trigger this event for the sound manager to play line-specific dialog
+ //
+ GetEventManager()->TriggerEvent( EVENT_FE_CREDITS_NEW_LINE, (void*)lineNumber );
+ }
+}
+
diff --git a/game/code/presentation/gui/frontend/guiscreenviewcredits.h b/game/code/presentation/gui/frontend/guiscreenviewcredits.h
new file mode 100644
index 0000000..b1899bf
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenviewcredits.h
@@ -0,0 +1,74 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenViewCredits
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/05/14 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENVIEWCREDITS_H
+#define GUISCREENVIEWCREDITS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenViewCredits : public CGuiScreen
+{
+public:
+ CGuiScreenViewCredits( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenViewCredits();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ void ResetScrolling();
+ bool m_playKKDialog : 1;
+
+ Scrooby::Group* m_creditsGroup;
+
+private:
+ float FormatTextWithLineBreaks( Scrooby::Text* pText );
+
+ bool ScrollCredits( float pixels );
+ virtual void OnScrollingDone();
+
+ void OnNewLineDisplayed( int lineNumber );
+
+ static const int MAX_NUM_CREDITS_PARTITIONS = 32;
+
+ static float s_totalTranslateY;
+ static float s_numPixelsPerLine;
+ static float s_creditsOffsets[ MAX_NUM_CREDITS_PARTITIONS ];
+
+ Scrooby::Text* m_credits[ MAX_NUM_CREDITS_PARTITIONS ];
+ float m_currentTranslateY;
+ int m_lastLineDisplayed;
+
+ static const unsigned int SCROLL_IDLE_TIME_BEFORE_RESET = 0; // in msec
+ unsigned int m_elapsedIdleTime;
+
+};
+
+#endif // GUISCREENVIEWCREDITS_H
diff --git a/game/code/presentation/gui/frontend/guiscreenviewmovies.cpp b/game/code/presentation/gui/frontend/guiscreenviewmovies.cpp
new file mode 100644
index 0000000..5573f98
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenviewmovies.cpp
@@ -0,0 +1,287 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenViewMovies
+//
+// Description: Implementation of the CGuiScreenViewMovies class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenviewmovies.h>
+#include <presentation/gui/frontend/guiscreenplaymovie.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guimanager.h>
+
+#include <cheats/cheatinputsystem.h>
+#include <constants/movienames.h>
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+#include <raddebug.hpp> // Foundation
+#include <page.h>
+#include <screen.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const char* MOVIES_MENU_ITEMS[] =
+{
+ "IntroMovie",
+ "Movie1",
+ "Movie2",
+ "Movie3",
+ "Movie4",
+ "Movie5",
+ "Movie6",
+ "Movie7",
+ "Movie8",
+
+ "" // dummy terminator
+};
+
+const char* MOVIES_SELECTION[] =
+{
+ MovieNames::INTROFMV,
+ MovieNames::MOVIE1,
+ MovieNames::MOVIE2,
+ MovieNames::MOVIE3,
+ MovieNames::MOVIE4,
+ MovieNames::MOVIE5,
+ MovieNames::MOVIE6,
+ MovieNames::MOVIE7,
+ MovieNames::MOVIE8,
+
+ "" // dummy terminator
+};
+
+const int NUM_MOVIE_SELECTIONS = sizeof( MOVIES_SELECTION ) / sizeof( MOVIES_SELECTION[ 0 ] ) - 1;
+
+const int MOVIE_INDEX_FOR_LEVEL[] =
+{
+ 2, // level 1
+ 3, // level 2
+ 8, // level 3
+ 4, // level 4
+ 5, // level 5
+ 6, // level 6
+ 7, // level 7
+
+ -1
+};
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenViewMovies::CGuiScreenViewMovies
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenViewMovies::CGuiScreenViewMovies
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_VIEW_MOVIES ),
+ m_pMenu( NULL )
+{
+MEMTRACK_PUSH_GROUP( "CGuiScreenViewMovies" );
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage;
+ pPage = m_pScroobyScreen->GetPage( "ViewMovies" );
+ rAssert( pPage );
+
+ // Create a menu.
+ //
+ m_pMenu = new(GMA_LEVEL_FE) CGuiMenu( this, NUM_MOVIE_SELECTIONS );
+ rAssert( m_pMenu != NULL );
+
+ // Add menu items
+ //
+ for( int i = 0; i < NUM_MOVIE_SELECTIONS; i++ )
+ {
+ m_pMenu->AddMenuItem( pPage->GetText( MOVIES_MENU_ITEMS[ i ] ) );
+ }
+
+ this->AutoScaleFrame( m_pScroobyScreen->GetPage( "BigBoard" ) );
+MEMTRACK_POP_GROUP("CGuiScreenViewMovies");
+}
+
+
+//===========================================================================
+// CGuiScreenViewMovies::~CGuiScreenViewMovies
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenViewMovies::~CGuiScreenViewMovies()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenViewMovies::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenViewMovies::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ rAssert( m_guiManager );
+ CGuiScreenPlayMovie* playMovieScreen = static_cast<CGuiScreenPlayMovie*>( m_guiManager->FindWindowByID( GUI_SCREEN_ID_PLAY_MOVIE ) );
+ rAssert( playMovieScreen );
+
+ rAssert( static_cast<int>( param1 ) < NUM_MOVIE_SELECTIONS );
+ if( param1 == 0 ) // 0 = Intro FMV
+ {
+ // Intro FMV does not have localized audio.
+ //
+ playMovieScreen->SetMovieToPlay( MOVIES_SELECTION[ param1 ], true, false, false );
+ }
+ else
+ {
+ playMovieScreen->SetMovieToPlay( MOVIES_SELECTION[ param1 ] );
+ }
+
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_PLAY_MOVIE );
+ this->StartTransitionAnimation( 975, 1005 );
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ this->StartTransitionAnimation( 944, 974 );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenViewMovies::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenViewMovies::InitIntro()
+{
+ // update movies unlocked status
+ //
+ rAssert( m_pMenu != NULL );
+ for( int i = 0; i < RenderEnums::numLevels; i++ )
+ {
+ bool isUnlocked = GetCharacterSheetManager()->QueryFMVUnlocked( static_cast<RenderEnums::LevelEnum>( i ) ) ||
+ GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_MOVIES );
+
+ m_pMenu->SetMenuItemEnabled( MOVIE_INDEX_FOR_LEVEL[ i ], isUnlocked );
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenViewMovies::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenViewMovies::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenViewMovies::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenViewMovies::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/frontend/guiscreenviewmovies.h b/game/code/presentation/gui/frontend/guiscreenviewmovies.h
new file mode 100644
index 0000000..5513f34
--- /dev/null
+++ b/game/code/presentation/gui/frontend/guiscreenviewmovies.h
@@ -0,0 +1,53 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenViewMovies
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENVIEWMOVIES_H
+#define GUISCREENVIEWMOVIES_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenViewMovies : public CGuiScreen
+{
+public:
+ CGuiScreenViewMovies( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenViewMovies();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ CGuiMenu* m_pMenu;
+
+};
+
+#endif // GUISCREENVIEWMOVIES_H
diff --git a/game/code/presentation/gui/guientity.cpp b/game/code/presentation/gui/guientity.cpp
new file mode 100644
index 0000000..d2a1a6a
--- /dev/null
+++ b/game/code/presentation/gui/guientity.cpp
@@ -0,0 +1,61 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiEntity
+//
+// Description: Implementation of the CGuiEntity class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/21 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/guientity.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiEntity::CGuiEntity
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiEntity::CGuiEntity( CGuiEntity* pParent )
+: m_pParent( pParent )
+{
+}
+
+//===========================================================================
+// CGuiEntity::~CGuiEntity
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiEntity::~CGuiEntity()
+{
+}
diff --git a/game/code/presentation/gui/guientity.h b/game/code/presentation/gui/guientity.h
new file mode 100644
index 0000000..8fc7afb
--- /dev/null
+++ b/game/code/presentation/gui/guientity.h
@@ -0,0 +1,184 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiEntity
+//
+// Description: Interface for the CGuiEntity class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/20 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUIENTITY_H
+#define GUIENTITY_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+#ifdef RAD_GAMECUBE
+ const int PLATFORM_TEXT_INDEX = 0;
+#endif
+#ifdef RAD_PS2
+ const int PLATFORM_TEXT_INDEX = 1;
+#endif
+#ifdef RAD_XBOX
+ const int PLATFORM_TEXT_INDEX = 2;
+#endif
+#ifdef RAD_WIN32 // parallel the xbox for now.
+ const int PLATFORM_TEXT_INDEX = 2;
+#endif
+
+enum eGuiMessage
+{
+ GUI_MSG_UPDATE, // GUI system -> Manager -> current screen
+ GUI_MSG_CONTROLLER_ALPHA, // begin controller messages
+ GUI_MSG_CONTROLLER_UP, //
+ GUI_MSG_CONTROLLER_DOWN,
+ GUI_MSG_CONTROLLER_LEFT,
+ GUI_MSG_CONTROLLER_RIGHT,
+ GUI_MSG_CONTROLLER_AUX_UP, // on right analog stick
+ GUI_MSG_CONTROLLER_AUX_DOWN, // on right analog stick
+ GUI_MSG_CONTROLLER_AUX_LEFT, // on right analog stick
+ GUI_MSG_CONTROLLER_AUX_RIGHT, // on right analog stick
+ GUI_MSG_CONTROLLER_START,
+ GUI_MSG_CONTROLLER_SELECT,
+ GUI_MSG_CONTROLLER_BACK,
+ GUI_MSG_CONTROLLER_AUX_X,
+ GUI_MSG_CONTROLLER_AUX_Y,
+ GUI_MSG_CONTROLLER_L1,
+ GUI_MSG_CONTROLLER_R1,
+ GUI_MSG_CONTROLLER_CONNECT,
+ GUI_MSG_CONTROLLER_DISCONNECT, //
+ GUI_MSG_CONTROLLER_OMEGA, // end controller messages
+ GUI_MSG_PROJECT_LOAD_COMPLETE, // Scrooby File Handler -> GUI System
+ GUI_MSG_INIT_BOOTUP, // client -> System
+ GUI_MSG_RELEASE_BOOTUP, // client -> System
+ GUI_MSG_INIT_FRONTEND, // client -> System
+ GUI_MSG_RELEASE_FRONTEND, // client -> System
+ GUI_MSG_INIT_MINIGAME, // client -> System
+ GUI_MSG_RELEASE_MINIGAME, // client -> System
+ GUI_MSG_INIT_INGAME, // client -> System
+ GUI_MSG_RELEASE_INGAME, // client -> System
+ GUI_MSG_PRE_RUN_BACKEND, // ManagerInGame -> ManagerBackEnd
+ GUI_MSG_RUN_BACKEND, // client -> System
+ GUI_MSG_RUN_FRONTEND, // client -> System
+ GUI_MSG_RUN_MINIGAME, // client -> System
+ GUI_MSG_RUN_INGAME, // client -> System
+ GUI_MSG_RUN_DEMO, // client -> System
+ GUI_MSG_PAUSE_INGAME, // System -> ManagerInGame
+ GUI_MSG_UNPAUSE_INGAME, // Pause Menu Screen -> ManagerInGame
+ GUI_MSG_RESUME_INGAME, // System -> ManagerInGame
+ GUI_MSG_QUIT_BOOTUP, // client -> ManagerBootUp
+ GUI_MSG_QUIT_BACKEND, // client -> ManagerBackEnd
+ GUI_MSG_QUIT_FRONTEND, // Screen -> ManagerFrontEnd
+ GUI_MSG_QUIT_MINIGAME, // Screen -> ManagerMiniGame
+ GUI_MSG_QUIT_INGAME, // PauseMenu -> ManagerInGame
+ GUI_MSG_QUIT_INGAME_FOR_RELOAD, // MissionSelect -> ManagerInGame
+ GUI_MSG_QUIT_DEMO, // client/screen -> ManagerBackEnd
+ GUI_MSG_QUIT_GAME, // pc. Screen -> ManagerFrontEnd
+ GUI_MSG_BOOTUP_LOAD_COMPLETED, // screen -> ManagerBootUp
+ GUI_MSG_LANGUAGE_SELECTION_DONE, // screen -> ManagerBootUp
+ GUI_MSG_MEMCARD_CHECK_COMPLETED, // screen -> ManagerBootUp / ManagerFrontEnd
+ GUI_MSG_SPLASH_SCREEN_DONE, // screen -> ManagerFrontEnd
+ GUI_MSG_GOTO_SCREEN, // screen -> ManagerFrontEnd (param1 = screen id)
+ GUI_MSG_BACK_SCREEN, // screen -> Manager
+ GUI_MSG_SHOW_HUD_OVERLAY, // client -> In-game HUD (param1 = overlay id)
+ GUI_MSG_HIDE_HUD_OVERLAY, // client -> In-game HUD (param1 = overlay id)
+ GUI_MSG_INGAME_MISSION_COMPLETE, // client -> ManagerInGame
+ GUI_MSG_INGAME_MISSION_FAILED, // client -> ManagerInGame
+ GUI_MSG_INGAME_MISSION_LOAD_BEGIN, // client -> ManagerInGame
+ GUI_MSG_INGAME_MISSION_LOAD_END, // client -> ManagerInGame
+ GUI_MSG_INGAME_DISPLAY_PROMPT, // client -> ManagerInGame
+ GUI_MSG_WINDOW_ENTER, // Manager -> Window
+ GUI_MSG_WINDOW_EXIT, // Manager -> Window
+ GUI_MSG_WINDOW_FINISHED, // Window -> Manager
+ GUI_MSG_WINDOW_PAUSE, // client -> Window
+ GUI_MSG_WINDOW_RESUME, // client -> Window
+ GUI_MSG_DEMOLOOP_BEGIN, // ManagerFrontEnd->Screen
+ GUI_MSG_DEMOLOOP_END, // ManagerFrontEnd->Screen
+ GUI_MSG_RESTART_GAME, // Pause Menu -> ManagerInGame
+ GUI_MSG_MENU_SELECTION_CHANGED, // Menu -> Screen
+ GUI_MSG_MENU_SELECTION_VALUE_CHANGED, // Menu -> Screen
+ GUI_MSG_MENU_SELECTION_MADE, // Menu -> Screen
+ GUI_MSG_MENU_SLIDER_NOT_CHANGING, // Menu -> Screen
+ GUI_MSG_ON_DISPLAY_MESSAGE, // Message Screen -> Screen
+ GUI_MSG_MENU_PROMPT_RESPONSE, // Prompt Screen -> Screen
+ GUI_MSG_ERROR_PROMPT_RESPONSE, // Prompt Screen -> Screen
+ GUI_MSG_PROMPT_START_RESPONSE, // Prompt Screen -> Screen, only on ps2
+ GUI_MSG_DEATH_VOLUME_START, // Play the death volume transition
+ GUI_MSG_MANUAL_RESET, // Play the death volume transition
+ GUI_MSG_START_IRIS_WIPE_CLOSE, // Do an iris wipe closing
+ GUI_MSG_START_IRIS_WIPE_OPEN, // Do an iris wipe opening
+ GUI_MSG_INGAME_FADE_OUT, // Fade from IN-GAME to BLACK
+ GUI_MSG_INGAME_FADE_IN, // Fade from BLACK to IN-GAME
+ GUI_MSG_INGAME_FADE_OUT_CANCEL, // Cancel fading from IN-GAME to BLACK
+ GUI_MSG_INGAME_FADE_IN_CANCEL, // Cancel fading from BLACK to IN-GAME
+ GUI_MSG_LOAD_RESOURCES, // GUI manager -> Screen
+ GUI_MSG_SET_GOTO_SCREEN_PARAMETERS, // GUI system -> GUI manager
+ GUI_MSG_ON_SAVE_GAME_COMPLETE, // save game screen -> GUI in-game manager
+ GUI_MSG_PROMPT_UPDATE, // Prompt Screen -> Screen called during CGuiScreenPrompt::HandleMessage GUI_MSG_UPDATE
+ GUI_MSG_MESSAGE_UPDATE, // Message Screen -> Screen called during CGuiScreenMessage::HandleMessage GUI_MSG_UPDATE
+#ifdef RAD_WIN32
+ GUI_MSG_MOUSE_OVER, // Called when a mouse cursor is on a hotspot.
+ GUI_MSG_MOUSE_LCLICK, // Called when a mouse clicks the left button and releases.
+ GUI_MSG_MOUSE_RCLICK, // Called when a mouse clicks the right button and releases.
+ GUI_MSG_MOUSE_LCLICKHOLD, // Called when a mouse clicks a hotspot and holds with the left button.
+ GUI_MSG_MOUSE_RCLICKHOLD, // Called when a mouse clicks a hotspot and holds with the right button.
+ GUI_MSG_MOUSE_LCLICKDRAG, // Called when a mouse left clicks on a hotspot and drags.
+ GUI_MSG_QUIT_TO_SYSTEM,
+#endif
+
+ GUI_MSG_TOTAL
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiEntity
+{
+public:
+ CGuiEntity( CGuiEntity* pParent );
+ virtual ~CGuiEntity();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 ) = 0;
+
+ static bool IsControllerMessage( eGuiMessage message );
+
+protected:
+ CGuiEntity* m_pParent;
+
+private:
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignement. Declare but don't define.
+ //
+ CGuiEntity( const CGuiEntity& );
+ CGuiEntity& operator= ( const CGuiEntity& );
+
+};
+
+inline bool
+CGuiEntity::IsControllerMessage( eGuiMessage message )
+{
+ return (message > GUI_MSG_CONTROLLER_ALPHA && message < GUI_MSG_CONTROLLER_OMEGA);
+}
+
+#endif // GUIENTITY_H
diff --git a/game/code/presentation/gui/guimanager.cpp b/game/code/presentation/gui/guimanager.cpp
new file mode 100644
index 0000000..8a0ab88
--- /dev/null
+++ b/game/code/presentation/gui/guimanager.cpp
@@ -0,0 +1,650 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManager
+//
+// Description: Implementation of the CGuiManager class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/21 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guisystem.h> // for loading callback
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/guiscreenmessage.h>
+#include <presentation/gui/guiscreenprompt.h>
+#include <input/inputmanager.h>
+
+#include <gameflow/gameflow.h>
+#include <memory/srrmemory.h>
+
+#include <raddebug.hpp> // Foundation
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+CGuiManager::eMemoryCardCheckingState
+CGuiManager::s_memcardCheckState = CGuiManager::MEM_CARD_CHECK_NOT_DONE;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiManager::CGuiManager
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManager::CGuiManager
+(
+ Scrooby::Project* pProject,
+ CGuiEntity* pParent
+)
+: CGuiEntity( pParent ),
+ m_numWindows( 0 ),
+ m_pScroobyProject( pProject ),
+ m_currentScreen( CGuiWindow::GUI_WINDOW_ID_UNDEFINED ),
+ m_nextScreen( CGuiWindow::GUI_WINDOW_ID_UNDEFINED ),
+ m_state( GUI_FE_UNINITIALIZED ),
+ m_windowHistoryCount( 0 ),
+ m_gotoScreenUserParam1( 0 ),
+ m_gotoScreenUserParam2( 0 )
+{
+ for( unsigned int i = 0; i < sizeof( m_windows ) /
+ sizeof( m_windows[ 0 ] ); i++ )
+ {
+ m_windows[ i ] = NULL;
+ }
+
+ for( unsigned int j = 0; j < sizeof( m_windowHistory ) /
+ sizeof( m_windowHistory[ 0 ] ); j++ )
+ {
+ m_windowHistory[ j ] = CGuiWindow::GUI_WINDOW_ID_UNDEFINED;
+ }
+ //Load the mouse cursor.
+#ifdef RAD_WIN32
+ tDrawable* pMouseCursor = p3d::find<tDrawable>("mouse_cursor.png");
+ GetInputManager()->GetFEMouse()->InitMouseCursor( pMouseCursor );
+#endif
+}
+
+
+//===========================================================================
+// CGuiManager::~CGuiManager
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManager::~CGuiManager()
+{
+ // Remove all existing screens.
+ //
+ this->RemoveAllWindows();
+}
+
+
+//===========================================================================
+// CGuiManager::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManager::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ switch( message )
+ {
+ case GUI_MSG_SET_GOTO_SCREEN_PARAMETERS:
+ {
+ m_gotoScreenUserParam1 = param1;
+ m_gotoScreenUserParam2 = param2;
+
+ break;
+ }
+
+ case GUI_MSG_GOTO_SCREEN:
+ {
+ CGuiWindow::eGuiWindowID nextScreen = static_cast<CGuiWindow::eGuiWindowID>( param1 );
+
+ if( this->FindWindowByID( nextScreen ) == NULL )
+ {
+ // ignore if screen not found
+ //
+ break;
+ }
+
+ if( (nextScreen == m_currentScreen) &&
+ (param2 & FORCE_WINDOW_RELOAD) == 0 )
+ {
+ if( m_nextScreen != CGuiWindow::GUI_WINDOW_ID_UNDEFINED )
+ {
+ m_nextScreen = nextScreen;
+ }
+
+ // ignore if we are already on the requested screen,
+ // unless we're trying to force a screen reload
+ //
+ break;
+ }
+
+ m_nextScreen = nextScreen;
+
+ rDebugPrintf( "GUI GoTo Screen Request: (next screen ID = %d)\n", m_nextScreen );
+
+ // push current screen onto history stack, unless requested
+ // to keep existing history
+ //
+ if( (param2 & KEEP_WINDOW_HISTORY) == 0 )
+ {
+ this->PushScreenHistory( m_currentScreen );
+ }
+
+ // clear screen history, if requested
+ //
+ if( (param2 & CLEAR_WINDOW_HISTORY) > 0 )
+ {
+ this->ClearScreenHistory();
+ }
+
+ m_state = GUI_FE_CHANGING_SCREENS;
+
+ // Tell the current screen to shut down.
+ //
+ if( m_currentScreen != CGuiWindow::GUI_WINDOW_ID_UNDEFINED )
+ {
+ this->FindWindowByID( m_currentScreen )->HandleMessage( GUI_MSG_WINDOW_EXIT );
+ }
+
+ if( (param2 & FORCE_WINDOW_CHANGE_IMMEDIATE) > 0 )
+ {
+ m_currentScreen = m_nextScreen;
+
+ CGuiScreen* nextScreen = static_cast< CGuiScreen* >( this->FindWindowByID( m_nextScreen ) );
+ rAssert( nextScreen != NULL );
+ m_pScroobyProject->GotoScreen( nextScreen->GetScroobyScreen(), this );
+ }
+
+ break;
+ }
+
+ case GUI_MSG_BACK_SCREEN:
+ {
+ CGuiWindow::eGuiWindowID nextScreen = CGuiWindow::GUI_WINDOW_ID_UNDEFINED;
+
+ // param1 = number of extra screens to go back
+ //
+ for( unsigned int numBackScreens = 0; numBackScreens < (param1 + 1); numBackScreens++ )
+ {
+ nextScreen = this->PopScreenHistory();
+ }
+
+ if( nextScreen != CGuiWindow::GUI_WINDOW_ID_UNDEFINED )
+ {
+ m_nextScreen = nextScreen;
+
+ m_state = GUI_FE_CHANGING_SCREENS;
+
+ // Tell the current screen to shut down.
+ //
+ rAssert( m_currentScreen != CGuiWindow::GUI_WINDOW_ID_UNDEFINED );
+ this->FindWindowByID( m_currentScreen )->HandleMessage( GUI_MSG_WINDOW_EXIT );
+ }
+
+ break;
+ }
+
+ case GUI_MSG_MEMCARD_CHECK_COMPLETED:
+ {
+ s_memcardCheckState = MEM_CARD_CHECK_COMPLETED;
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiManager::OnGotoScreenComplete
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManager::OnGotoScreenComplete()
+{
+ m_state = GUI_FE_SCREEN_RUNNING;
+
+ rAssert( CGuiWindow::GUI_WINDOW_ID_UNDEFINED != m_nextScreen );
+
+ m_currentScreen = m_nextScreen;
+
+ this->FindWindowByID( m_currentScreen )->HandleMessage( GUI_MSG_WINDOW_ENTER,
+ m_gotoScreenUserParam1,
+ m_gotoScreenUserParam2 );
+
+ // reset goto screen user parameters
+ //
+ m_gotoScreenUserParam1 = 0;
+ m_gotoScreenUserParam2 = 0;
+}
+
+
+//===========================================================================
+// CGuiManager::FindWindowByID
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiWindow* CGuiManager::FindWindowByID
+(
+ CGuiWindow::eGuiWindowID id
+)
+{
+/*
+ for( int i = 0; i < m_windows.mSize; ++i )
+ {
+ if( m_windows[i]->GetWindowID() == id )
+ {
+ return( m_windows[i] );
+ }
+ }
+*/
+ CGuiWindow* window = NULL;
+
+ if( this->IsValidWindowID( id ) )
+ {
+ window = m_windows[ id ];
+ }
+
+ return window;
+}
+
+//===========================================================================
+// CGuiManager::GetCurrentWindow
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiWindow* CGuiManager::GetCurrentWindow()
+{
+ return FindWindowByID( m_currentScreen );
+}
+
+
+//===========================================================================
+// CGuiManager::DisplayMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiManager::DisplayMessage
+(
+ int messageIndex,
+ CGuiEntity* pCallback
+)
+{
+ CGuiScreenMessage::Display( messageIndex, pCallback );
+
+ this->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_GENERIC_MESSAGE,
+ KEEP_WINDOW_HISTORY | FORCE_WINDOW_RELOAD );
+}
+
+
+//===========================================================================
+// CGuiManager::DisplayPrompt
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiManager::DisplayPrompt
+(
+ int messageIndex,
+ CGuiEntity* pCallback,
+ eGenericPromptType promptType,
+ bool enableDefaultToNo
+)
+{
+ switch( promptType )
+ {
+ case PROMPT_TYPE_YES_NO:
+ {
+ CGuiMenuPrompt::ePromptResponse responses[] =
+ {
+ CGuiMenuPrompt::RESPONSE_YES,
+ CGuiMenuPrompt::RESPONSE_NO
+ };
+
+ CGuiScreenPrompt::Display( messageIndex, pCallback, 2, responses );
+ CGuiScreenPrompt::EnableDefaultToNo( enableDefaultToNo );
+
+ break;
+ }
+ case PROMPT_TYPE_CONTINUE:
+ {
+ CGuiMenuPrompt::ePromptResponse responses[] =
+ {
+ CGuiMenuPrompt::RESPONSE_CONTINUE
+ };
+
+ CGuiScreenPrompt::Display( messageIndex, pCallback, 1, responses );
+
+ break;
+ }
+ case PROMPT_TYPE_OK:
+ {
+ CGuiMenuPrompt::ePromptResponse responses[] =
+ {
+ CGuiMenuPrompt::RESPONSE_OK
+ };
+
+ CGuiScreenPrompt::Display( messageIndex, pCallback, 1, responses );
+
+ break;
+ }
+ case PROMPT_TYPE_DELETE:
+ {
+ CGuiMenuPrompt::ePromptResponse responses[] =
+ {
+ CGuiMenuPrompt::RESPONSE_DELETE
+ };
+
+ CGuiScreenPrompt::Display( messageIndex, pCallback, 1, responses );
+
+ break;
+ }
+ case PROMPT_TYPE_LOAD:
+ {
+ CGuiMenuPrompt::ePromptResponse responses[] =
+ {
+ CGuiMenuPrompt::RESPONSE_LOAD
+ };
+
+ CGuiScreenPrompt::Display( messageIndex, pCallback, 1, responses );
+
+ break;
+ }
+ case PROMPT_TYPE_SAVE:
+ {
+ CGuiMenuPrompt::ePromptResponse responses[] =
+ {
+ CGuiMenuPrompt::RESPONSE_SAVE
+ };
+
+ CGuiScreenPrompt::Display( messageIndex, pCallback, 1, responses );
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( 0, "*** ERROR: Invalid prompt type!" );
+
+ break;
+ }
+ }
+
+ this->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_GENERIC_PROMPT,
+ KEEP_WINDOW_HISTORY | FORCE_WINDOW_RELOAD );
+}
+
+
+//===========================================================================
+// CGuiManager::DisplayErrorPrompt
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiManager::DisplayErrorPrompt
+(
+ int messageIndex,
+ CGuiEntity* pCallback,
+ int promptResponses
+)
+{
+ int numResponses = 0;
+ CGuiMenuPrompt::ePromptResponse responses[ CGuiMenuPrompt::MAX_NUM_RESPONSES ];
+
+
+ if( promptResponses & ERROR_RESPONSE_CONTINUE )
+ {
+ responses[ numResponses++ ] = CGuiMenuPrompt::RESPONSE_CONTINUE;
+ }
+
+ if( promptResponses & ERROR_RESPONSE_CONTINUE_WITHOUT_SAVE )
+ {
+ responses[ numResponses++ ] = CGuiMenuPrompt::RESPONSE_CONTINUE_WITHOUT_SAVE;
+ }
+
+ if( promptResponses & ERROR_RESPONSE_RETRY )
+ {
+ responses[ numResponses++ ] = CGuiMenuPrompt::RESPONSE_RETRY;
+ }
+
+ if( promptResponses & ERROR_RESPONSE_DELETE )
+ {
+ responses[ numResponses++ ] = CGuiMenuPrompt::RESPONSE_DELETE;
+ }
+
+ if( promptResponses & ERROR_RESPONSE_YES )
+ {
+ responses[ numResponses++ ] = CGuiMenuPrompt::RESPONSE_YES;
+ }
+
+ if( promptResponses & ERROR_RESPONSE_NO )
+ {
+ responses[ numResponses++ ] = CGuiMenuPrompt::RESPONSE_NO;
+ }
+
+ if( promptResponses & ERROR_RESPONSE_FORMAT )
+ {
+#ifdef RAD_XBOX
+ responses[ numResponses++ ] = CGuiMenuPrompt::RESPONSE_FORMAT_XBOX;
+#endif
+#ifdef RAD_WIN32
+ responses[ numResponses++ ] = CGuiMenuPrompt::RESPONSE_FORMAT_XBOX;
+#endif
+#ifdef RAD_GAMECUBE
+ responses[ numResponses++ ] = CGuiMenuPrompt::RESPONSE_FORMAT_GC;
+#endif
+#ifdef RAD_PS2
+ responses[ numResponses++ ] = CGuiMenuPrompt::RESPONSE_FORMAT_PS2;
+#endif
+ }
+
+ if( promptResponses & ERROR_RESPONSE_MANAGE )
+ {
+ responses[ numResponses++ ] = CGuiMenuPrompt::RESPONSE_MANAGE_MEMCARDS;
+ }
+
+ CGuiScreenPrompt::Display( messageIndex, pCallback, numResponses, responses );
+ CGuiScreenPrompt::EnableDefaultToNo( false );
+
+ this->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_ERROR_PROMPT,
+ KEEP_WINDOW_HISTORY | FORCE_WINDOW_RELOAD );
+}
+
+
+CGuiWindow::eGuiWindowID
+CGuiManager::GetPreviousScreen( int fromCurrentScreen ) const
+{
+ CGuiWindow::eGuiWindowID previousScreenID = CGuiWindow::GUI_WINDOW_ID_UNDEFINED;
+
+ int windowHistoryCount = m_windowHistoryCount - fromCurrentScreen;
+ if( windowHistoryCount > 0 )
+ {
+ previousScreenID = m_windowHistory[ windowHistoryCount - 1 ];
+ }
+
+ return previousScreenID;
+}
+
+CGuiWindow::eGuiWindowID CGuiManager::GetCurrentScreen() const
+{
+ return m_currentScreen;
+}
+
+//===========================================================================
+// Protected Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiManager::AddWindow
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManager::AddWindow
+(
+ CGuiWindow::eGuiWindowID windowID,
+ CGuiWindow* pWindow
+)
+{
+ rAssert( this->IsValidWindowID( windowID ) && pWindow != NULL );
+
+ rAssert( m_windows[ windowID ] == NULL );
+ m_windows[ windowID ] = pWindow;
+
+ m_numWindows++;
+}
+
+void
+CGuiManager::RemoveWindow( CGuiWindow::eGuiWindowID windowID )
+{
+ rAssert( this->IsValidWindowID( windowID ) );
+
+ if( m_windows[ windowID ] != NULL )
+ {
+ delete m_windows[ windowID ];
+ m_windows[ windowID ] = NULL;
+
+ m_numWindows--;
+ }
+}
+
+void
+CGuiManager::RemoveAllWindows()
+{
+ for( int i = 0; i < CGuiWindow::NUM_GUI_WINDOW_IDS; i++ )
+ {
+ if( m_windows[ i ] != NULL )
+ {
+ delete m_windows[ i ];
+ m_windows[ i ] = NULL;
+ }
+ }
+
+ m_numWindows = 0;
+}
+
+void
+CGuiManager::PushScreenHistory( CGuiWindow::eGuiWindowID windowID )
+{
+ if( this->IsValidWindowID( windowID ) )
+ {
+ rAssert( m_windowHistoryCount < MAX_WINDOW_HISTORY );
+
+ m_windowHistory[ m_windowHistoryCount ] = windowID;
+ m_windowHistoryCount++;
+ }
+}
+
+CGuiWindow::eGuiWindowID
+CGuiManager::PopScreenHistory()
+{
+ CGuiWindow::eGuiWindowID windowID = CGuiWindow::GUI_WINDOW_ID_UNDEFINED;
+
+ if( m_windowHistoryCount > 0 )
+ {
+ m_windowHistoryCount--;
+
+ windowID = m_windowHistory[ m_windowHistoryCount ];
+ }
+
+ return windowID;
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
diff --git a/game/code/presentation/gui/guimanager.h b/game/code/presentation/gui/guimanager.h
new file mode 100644
index 0000000..6e21afa
--- /dev/null
+++ b/game/code/presentation/gui/guimanager.h
@@ -0,0 +1,207 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManager
+//
+// Description: Interface for the CGuiManager class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/20 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUIMANAGER_H
+#define GUIMANAGER_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guientity.h>
+#include <presentation/gui/guiwindow.h>
+#include <events/eventlistener.h>
+#include <project.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+struct IGuiLoadingCallback;
+
+//===========================================================================
+// Constants
+//===========================================================================
+
+const int MAX_WINDOW_HISTORY = 8;
+
+enum eWindowCommand
+{
+ CLEAR_WINDOW_HISTORY = 1,
+ KEEP_WINDOW_HISTORY = 2,
+ FORCE_WINDOW_CHANGE_IMMEDIATE = 4,
+ FORCE_WINDOW_RELOAD = 8,
+
+ NUM_WINDOW_COMMANDS
+};
+
+enum eGenericPromptType
+{
+ PROMPT_TYPE_YES_NO,
+ PROMPT_TYPE_CONTINUE,
+ PROMPT_TYPE_OK,
+ PROMPT_TYPE_DELETE,
+ PROMPT_TYPE_LOAD,
+ PROMPT_TYPE_SAVE,
+
+ NUM_GENERIC_PROMPT_TYPES
+};
+
+enum eErrorPromptResponse
+{
+ ERROR_RESPONSE_NONE = 0,
+
+ ERROR_RESPONSE_CONTINUE = 1,
+ ERROR_RESPONSE_RETRY = 2,
+ ERROR_RESPONSE_YES = 4,
+ ERROR_RESPONSE_NO = 8,
+ ERROR_RESPONSE_FORMAT = 16,
+ ERROR_RESPONSE_MANAGE = 32,
+ ERROR_RESPONSE_CONTINUE_WITHOUT_SAVE = 64,
+ ERROR_RESPONSE_DELETE = 128,
+ ERROR_RESPONSE_ALL = ~0
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiManager : public CGuiEntity,
+ public Scrooby::GotoScreenCallback,
+ public EventListener
+{
+ public:
+
+ CGuiManager( Scrooby::Project* pProject,
+ CGuiEntity* pParent );
+
+ virtual ~CGuiManager();
+
+ virtual void Populate() = 0;
+ virtual void Start( CGuiWindow::eGuiWindowID initialWindow = CGuiWindow::GUI_WINDOW_ID_UNDEFINED ) = 0;
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ Scrooby::Project* GetScroobyProject() const { return m_pScroobyProject; }
+
+ virtual void OnGotoScreenComplete();
+
+ CGuiWindow* FindWindowByID( CGuiWindow::eGuiWindowID id );
+
+ CGuiWindow* GetCurrentWindow();
+
+ void DisplayMessage( int messageIndex,
+ CGuiEntity* pCallback = NULL );
+
+ void DisplayPrompt( int messageIndex,
+ CGuiEntity* pCallback,
+ eGenericPromptType promptType = PROMPT_TYPE_YES_NO,
+ bool enableDefaultToNo = true );
+
+ void DisplayErrorPrompt( int messageIndex,
+ CGuiEntity* pCallback,
+ int promptResponses = ERROR_RESPONSE_NONE );
+
+ CGuiWindow::eGuiWindowID GetPreviousScreen( int fromCurrentScreen = 0 ) const;
+ CGuiWindow::eGuiWindowID GetCurrentScreen() const;
+
+ // Implements EventListener
+ virtual void HandleEvent( EventEnum id, void* pEventData ) {};
+
+ enum eMemoryCardCheckingState
+ {
+ MEM_CARD_CHECK_NOT_DONE,
+ MEM_CARD_CHECK_IN_PROGRESS,
+ MEM_CARD_CHECK_COMPLETED,
+
+ NUM_MEM_CARD_CHECK_STATES
+ };
+
+ static eMemoryCardCheckingState s_memcardCheckState;
+
+ protected:
+
+ //---------------------------------------------------------------------
+ // Protected Functions
+ //---------------------------------------------------------------------
+
+ void AddWindow( CGuiWindow::eGuiWindowID windowID, CGuiWindow* pWindow );
+ void RemoveWindow( CGuiWindow::eGuiWindowID windowID );
+ void RemoveAllWindows();
+
+ void PushScreenHistory( CGuiWindow::eGuiWindowID windowID );
+ CGuiWindow::eGuiWindowID PopScreenHistory();
+ void ClearScreenHistory();
+
+ //---------------------------------------------------------------------
+ // Protected Data
+ //---------------------------------------------------------------------
+
+ CGuiWindow* m_windows[ CGuiWindow::NUM_GUI_WINDOW_IDS ];
+ int m_numWindows;
+
+ Scrooby::Project* m_pScroobyProject;
+
+ CGuiWindow::eGuiWindowID m_currentScreen;
+ CGuiWindow::eGuiWindowID m_nextScreen;
+
+ enum eGuiFrontEndState
+ {
+ GUI_FE_UNINITIALIZED,
+ GUI_FE_SCREEN_RUNNING,
+ GUI_FE_CHANGING_SCREENS,
+ GUI_FE_DYNAMIC_LOADING,
+ GUI_FE_SHUTTING_DOWN,
+ GUI_FE_TERMINATED
+ };
+
+ eGuiFrontEndState m_state;
+
+ private:
+
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CGuiManager( const CGuiManager& );
+ CGuiManager& operator= ( const CGuiManager& );
+
+ bool IsValidWindowID( CGuiWindow::eGuiWindowID windowID ) const;
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+ CGuiWindow::eGuiWindowID m_windowHistory[ MAX_WINDOW_HISTORY ];
+ int m_windowHistoryCount;
+
+ unsigned int m_gotoScreenUserParam1;
+ unsigned int m_gotoScreenUserParam2;
+};
+
+inline void
+CGuiManager::ClearScreenHistory()
+{
+ m_windowHistoryCount = 0;
+}
+
+inline bool
+CGuiManager::IsValidWindowID( CGuiWindow::eGuiWindowID windowID ) const
+{
+ return (windowID >= 0 && windowID < CGuiWindow::NUM_GUI_WINDOW_IDS);
+}
+
+#endif // GUIMANAGER_H
diff --git a/game/code/presentation/gui/guimenu.cpp b/game/code/presentation/gui/guimenu.cpp
new file mode 100644
index 0000000..5c52b35
--- /dev/null
+++ b/game/code/presentation/gui/guimenu.cpp
@@ -0,0 +1,1367 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiMenu
+//
+// Description: Implementation of the CGuiMenu class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/12/4 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiuserinputhandler.h>
+#include <presentation/gui/utility/specialfx.h>
+
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+#include <events/eventmanager.h>
+
+#include <raddebug.hpp> // Foundation
+#include <radmath/trig.hpp> // RadMath
+
+// Scrooby
+//
+#include <group.h>
+#include <page.h>
+#include <sprite.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+const tColour DEFAULT_SELECTED_ITEM_COLOUR( 255, 255, 0 );
+const tColour DEFAULT_DISABLED_ITEM_COLOUR( 128, 128, 128 ); // the same as in guiscreenmemorycard.cpp
+
+const tColour DEFAULT_OUTLINE_COLOUR( 0, 0, 0, 192 );
+
+const float SLIDER_FULL_RANGE_TIME = 2000; // in milliseconds
+const int SELECTION_MADE_DURATION = 250; // in milliseconds
+
+#ifdef RAD_WIN32
+const float ARROW_SCALE = 0.5f;
+#endif
+
+//===========================================================================
+// Public Member Functions - CGuiMenu
+//===========================================================================
+
+//===========================================================================
+// CGuiMenu::CGuiMenu
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiMenu::CGuiMenu( CGuiEntity* pParent, int maxNumItems,
+ eMenuType menuType,
+ int specialEffects )
+: CGuiEntity( pParent ),
+ m_menuType( menuType ),
+ m_specialEffects( static_cast<short>( specialEffects ) ),
+ m_menuItems( NULL ),
+ m_numItems( 0 ),
+ m_selection( NO_SELECTION ),
+ m_pCursor( NULL ),
+ m_isSelectionMade( false ),
+ m_isHighlightEnabled( true ),
+ m_highlightColour( DEFAULT_SELECTED_ITEM_COLOUR ),
+ m_selectionMadeOutlineColour( tColour( 255, 0, 0, 192 ) ),
+ m_isGreyOutEnabled( true ),
+ m_elapsedTime( 0 ),
+ m_selectionMadeElapsedTime( 0 ),
+#ifdef RAD_WIN32
+ m_bIsSelectionOutlined( false ),
+ m_selectionOutlineColour( tColour( 0, 0, 255, 192 ) ),
+#endif
+ m_controllerID( -1 )
+{
+MEMTRACK_PUSH_GROUP( "GUIMenu" );
+ rAssertMsg( ( specialEffects & 0xFFFF0000 ) == 0, "Bitmask Overflow!" );
+
+ // create array of 'maxNumItems' menu items
+ //
+ rAssert( maxNumItems > 0 );
+ m_menuItems = new GuiMenuItem*[ maxNumItems ];
+ rAssert( m_menuItems != NULL );
+
+ for( int i = 0; i < maxNumItems; i++ )
+ {
+ m_menuItems[ i ] = NULL;
+ }
+MEMTRACK_POP_GROUP( "GUIMenu" );
+}
+
+//===========================================================================
+// CGuiMenu::~CGuiMenu
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiMenu::~CGuiMenu()
+{
+ if( m_menuItems != NULL )
+ {
+ for( int i = 0; i < m_numItems; i++ )
+ {
+ if( m_menuItems[ i ] != NULL )
+ {
+ delete m_menuItems[ i ];
+ m_menuItems[ i ] = NULL;
+ }
+ }
+
+ delete [] m_menuItems;
+ m_menuItems = NULL;
+ }
+}
+
+//===========================================================================
+// CGuiMenu::HandleMessage
+//===========================================================================
+// Description: Message handler for this class.
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiMenu::HandleMessage( eGuiMessage message, unsigned int param1,
+ unsigned int param2 )
+{
+ if( m_isSelectionMade && this->IsControllerMessage( message ) )
+ {
+ // selection has already been made, ignore all controller messages
+ //
+ return;
+ }
+
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ if( m_selection != NO_SELECTION )
+ {
+ this->UpdateCurrentSelection( param1 );
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_UP:
+ {
+ this->ChangeSelection( -1 );
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_DOWN:
+ {
+ this->ChangeSelection( +1 );
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_LEFT:
+ {
+ this->DecrementSelectionValue();
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_RIGHT:
+ {
+ this->IncrementSelectionValue();
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ this->MakeSelection();
+
+ break;
+ }
+#ifdef RAD_WIN32
+ case GUI_MSG_MOUSE_OVER:
+ {
+ this->SetNewSelection(param1);
+
+ break;
+ }
+ case GUI_MSG_MOUSE_LCLICKHOLD:
+ {
+ this->OutlineSelection( (param1!=0) );
+ break;
+ }
+#endif
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//===========================================================================
+// CGuiMenu::AddMenuItem
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return: N/A.
+//
+//===========================================================================
+GuiMenuItem* CGuiMenu::AddMenuItem( Scrooby::BoundedDrawable* pItem,
+ Scrooby::BoundedDrawable* pItemValue,
+ Scrooby::Polygon* pSlider,
+ Scrooby::Sprite* pSliderImage,
+ Scrooby::Sprite* pItemValueArrowL,
+ Scrooby::Sprite* pItemValueArrowR,
+ int attributes )
+{
+ rAssert( m_menuItems[ m_numItems ] == NULL );
+
+ // create a new menu item based on menu type
+ //
+ switch( m_menuType )
+ {
+ case GUI_TEXT_MENU:
+ {
+ m_menuItems[ m_numItems ] = new GuiMenuItemText;
+
+ break;
+ }
+ case GUI_SPRITE_MENU:
+ {
+ m_menuItems[ m_numItems ] = new GuiMenuItemSprite;
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Invalid menu type!" );
+
+ break;
+ }
+ }
+
+ rAssert( m_menuItems[ m_numItems ] != NULL );
+
+ GuiMenuItem* pMenuItem = m_menuItems[ m_numItems ];
+
+ m_menuItems[ m_numItems ]->SetItem( pItem );
+
+ // for items w/ value toggle
+ //
+ if( pItemValue != NULL )
+ {
+ m_menuItems[ m_numItems ]->SetItemValue( pItemValue );
+
+ // hide arrows by default, if exists
+ //
+ if( pItemValueArrowL != NULL )
+ {
+ pItemValueArrowL->SetVisible( false );
+#ifdef RAD_WIN32
+ pItemValueArrowL->ScaleAboutCenter( ARROW_SCALE );
+#endif
+ m_menuItems[ m_numItems ]->m_itemValueArrowL = pItemValueArrowL;
+ }
+ if( pItemValueArrowR != NULL )
+ {
+ pItemValueArrowR->SetVisible( false );
+#ifdef RAD_WIN32
+ pItemValueArrowR->ScaleAboutCenter( ARROW_SCALE );
+#endif
+ m_menuItems[ m_numItems ]->m_itemValueArrowR = pItemValueArrowR;
+ }
+ }
+
+ if( m_menuType == GUI_TEXT_MENU )
+ {
+ if( (attributes & TEXT_OUTLINE_ENABLED) > 0 )
+ {
+ // turn on text outlining
+ //
+ m_menuItems[ m_numItems ]->SetDisplayOutline( true );
+ }
+#ifdef RAD_WIN32
+ pItem->SetVerticalJustification( Scrooby::Top );
+#endif
+ }
+
+ // for items w/ sliders
+ //
+ if( pSliderImage != NULL )
+ {
+ m_menuItems[ m_numItems ]->m_slider.SetScroobyImage( pSliderImage );
+ }
+
+ m_menuItems[ m_numItems ]->m_attributes = attributes;
+ m_menuItems[ m_numItems ]->m_defaultColour = pItem->GetColour();
+ m_numItems++;
+
+ // select first item by default
+ //
+ if( m_numItems == 1 )
+ {
+ SelectItem( 0 );
+ }
+ return pMenuItem;
+}
+
+//===========================================================================
+// CGuiMenu::SetMenuItemEnabled
+//===========================================================================
+// Description: Enable/disable menu item.
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiMenu::SetMenuItemEnabled( int index, bool enabled, bool changeVisibility )
+{
+ GuiMenuItem* currentMenuItem = this->GetMenuItem( index );
+ rAssert( currentMenuItem != NULL );
+
+ Scrooby::BoundedDrawable* currentItem = currentMenuItem->GetItem();
+ rAssert( currentItem != NULL );
+
+ Scrooby::BoundedDrawable* currentItemValue = currentMenuItem->GetItemValue();
+
+ if( enabled )
+ {
+ currentMenuItem->m_attributes |= SELECTION_ENABLED;
+
+ if( index != m_selection )
+ {
+ if( m_isGreyOutEnabled )
+ {
+ // restore default item colour
+ //
+ currentItem->SetColour( currentMenuItem->m_defaultColour );
+ if( currentItemValue != NULL )
+ {
+ currentItemValue->SetColour( currentMenuItem->m_defaultColour );
+ }
+ }
+
+ // show cursor
+ //
+ if( m_pCursor != NULL )
+ {
+ m_pCursor->SetVisible( true );
+ }
+
+ if( m_selection == NO_SELECTION )
+ {
+ m_selection = 0;
+ this->SelectItem( index );
+ }
+ }
+ }
+ else // !enabled
+ {
+ currentMenuItem->m_attributes &= ~SELECTION_ENABLED;
+
+ // if item disabled is current selection, select next enabled item
+ //
+ if( index == m_selection )
+ {
+ this->ChangeSelection( +1, false );
+
+ if( index == m_selection )
+ {
+ // reset cursor to first selection, and hide it
+ //
+ this->MoveCursor( m_selection, 0 );
+ if( m_pCursor != NULL )
+ {
+ m_pCursor->SetVisible( false );
+ }
+
+ // this means all selections have been disabled
+ //
+ m_selection = NO_SELECTION;
+ }
+ }
+
+ if( m_isGreyOutEnabled )
+ {
+ // grey out item colour
+ //
+ currentItem->SetColour( DEFAULT_DISABLED_ITEM_COLOUR );
+
+ if( currentItemValue != NULL )
+ {
+ currentItemValue->SetColour( DEFAULT_DISABLED_ITEM_COLOUR );
+ }
+ }
+ }
+
+ if( changeVisibility )
+ {
+ currentItem->SetVisible( enabled );
+
+ if( currentItemValue != NULL )
+ {
+ currentItemValue->SetVisible( enabled );
+ }
+ }
+}
+
+bool CGuiMenu::IsMenuItemEnabled( int index )
+{
+ GuiMenuItem* currentMenuItem = this->GetMenuItem( index );
+ rAssert( currentMenuItem != NULL );
+ return (currentMenuItem->m_attributes & SELECTION_ENABLED);
+}
+
+//===========================================================================
+// CGuiMenu::Reset
+//===========================================================================
+// Description: Unselect all menu items.
+//
+// Constraints: None.
+//
+// Parameters: N/A.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiMenu::Reset( int defaultSelection )
+{
+ // un-select current item
+ if( m_selection != NO_SELECTION )
+ {
+ this->UnselectItem( m_selection );
+ }
+
+ // select first item
+ this->SelectItem( defaultSelection );
+}
+
+//===========================================================================
+// CGuiMenu::SetHighlightColour
+//===========================================================================
+// Description: Sets the menu highlight colour.
+//
+// Constraints: None.
+//
+// Parameters: N/A.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiMenu::SetHighlightColour( bool isEnabled, tColour colour )
+{
+ m_isHighlightEnabled = isEnabled;
+ m_highlightColour = colour;
+}
+
+//===========================================================================
+// CGuiMenu::SetSelectionValue
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: N/A.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiMenu::SetSelectionValue( int index, int value )
+{
+ if( value >= 0 && value < m_menuItems[ index ]->m_itemValueCount )
+ {
+ m_menuItems[ index ]->SetItemValueIndex( value );
+
+ // Notify screen that selection value has changed
+ m_pParent->HandleMessage( GUI_MSG_MENU_SELECTION_VALUE_CHANGED, index, value );
+ }
+}
+
+//===========================================================================
+// CGuiMenu::SetSelectionValueCount
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: N/A.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiMenu::SetSelectionValueCount( int index, int count )
+{
+ m_menuItems[ index ]->m_itemValueCount = count;
+
+// if( index == m_selection )
+ {
+ // show arrows if more than one value to toggle; otherwise, hide them (partially)
+ //
+ bool showArrows = (m_menuItems[ index ]->m_itemValueCount > 1);
+ if( m_menuItems[ index ]->m_itemValueArrowL != NULL &&
+ m_menuItems[ index ]->m_itemValueArrowR != NULL )
+ {
+ m_menuItems[ index ]->m_itemValueArrowL->SetAlpha( showArrows ? 1.0f : 0.4f );
+ m_menuItems[ index ]->m_itemValueArrowR->SetAlpha( showArrows ? 1.0f : 0.4f );
+ }
+ }
+}
+
+//===========================================================================
+// CGuiMenu::MakeSelection
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: N/A.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiMenu::MakeSelection( bool isSelectionMade )
+{
+ if (m_selection == NO_SELECTION) return;//------------->
+
+ if( (m_menuItems[ m_selection ]->m_attributes & SELECTABLE) > 0 )
+ {
+ if( isSelectionMade )
+ {
+ if( m_selection != NO_SELECTION )
+ {
+ if( m_menuType == GUI_TEXT_MENU )
+ {
+ m_menuItems[ m_selection ]->SetOutlineColour( m_selectionMadeOutlineColour );
+#ifdef RAD_WIN32
+ if( m_bIsSelectionOutlined )
+ m_menuItems[ m_selection ]->GetItem()->SetColour( DEFAULT_SELECTED_ITEM_COLOUR );
+#endif
+ }
+
+ m_selectionMadeElapsedTime = 0;
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_SELECT );
+
+ // tell parent screen to ignore all controller inputs until
+ // selection-made effect is completed
+ //
+ CGuiScreen* parentScreen = static_cast<CGuiScreen*>( m_pParent );
+ rAssert( parentScreen );
+ parentScreen->SetIngoreControllerInputs( true );
+
+ m_isSelectionMade = true;
+ }
+ }
+ else
+ {
+ CGuiScreen* parentScreen = static_cast<CGuiScreen*>( m_pParent );
+ rAssert( parentScreen );
+ parentScreen->SetIngoreControllerInputs( false );
+
+ if( m_menuType == GUI_TEXT_MENU )
+ {
+ m_menuItems[ m_selection ]->SetOutlineColour( DEFAULT_OUTLINE_COLOUR );
+ }
+
+ // restore current item's regular L/R arrows, if exist
+ //
+ if( m_menuItems[ m_selection ]->m_itemValueArrowL != NULL &&
+ m_menuItems[ m_selection ]->m_itemValueArrowR != NULL )
+ {
+ m_menuItems[ m_selection ]->m_itemValueArrowL->SetIndex( 0 );
+ m_menuItems[ m_selection ]->m_itemValueArrowR->SetIndex( 0 );
+ }
+
+ m_pParent->HandleMessage( GUI_MSG_MENU_SELECTION_MADE, m_selection );
+
+ m_isSelectionMade = false;
+ }
+ }
+}
+
+//===========================================================================
+// Protected Member Functions - CGuiMenu
+//===========================================================================
+
+//===========================================================================
+// CGuiMenu::SelectItem
+//===========================================================================
+// Description: Select specified item.
+//
+// Constraints: None.
+//
+// Parameters: N/A.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiMenu::SelectItem( int index )
+{
+ rAssert( index >= 0 && index < m_numItems );
+ rAssert( m_menuItems[ index ] != NULL );
+
+ // move cursor
+ if( index != m_selection )
+ {
+ this->MoveCursor( m_selection, index );
+ }
+
+ // reset elapsed time
+ m_elapsedTime = 0;
+
+ if( m_isHighlightEnabled && m_menuType == GUI_TEXT_MENU )
+ {
+ m_menuItems[ index ]->GetItem()->SetColour( m_highlightColour );
+ }
+
+ // apply to item value also, if exists
+ if( m_menuItems[ index ]->GetItemValue() != NULL )
+ {
+ if( m_isHighlightEnabled && m_menuType == GUI_TEXT_MENU )
+ {
+ m_menuItems[ index ]->GetItemValue()->SetColour( m_highlightColour );
+ }
+
+ // show arrows, if exists
+ //
+ if( m_menuItems[ index ]->m_itemValueArrowL != NULL &&
+ m_menuItems[ index ]->m_itemValueArrowR != NULL )
+ {
+ m_menuItems[ index ]->m_itemValueArrowL->SetVisible( true );
+ m_menuItems[ index ]->m_itemValueArrowR->SetVisible( true );
+ }
+ }
+/*
+ if( m_specialEffects & MENU_SFX_DROP_SHADOW )
+ {
+ // turn on drop shadow
+ m_menuItems[ index ]->m_item->SetDisplayShadow( true );
+
+ // apply to item value also, if exists
+ if( m_menuItems[ index ]->m_itemValue != NULL )
+ {
+ m_menuItems[ index ]->m_itemValue->SetDisplayShadow( true );
+ }
+ }
+*/
+ m_selection = index;
+}
+
+//===========================================================================
+// CGuiMenu::UnselectItem
+//===========================================================================
+// Description: Unselect specified item.
+//
+// Constraints: None.
+//
+// Parameters: N/A.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiMenu::UnselectItem( int index )
+{
+ rAssert( index >= 0 && index < m_numItems );
+ rAssert( m_menuItems[ index ] != NULL );
+
+ if( m_menuType == GUI_TEXT_MENU )
+ {
+ m_menuItems[ index ]->GetItem()->SetColour( m_menuItems[ index ]->m_defaultColour );
+ }
+
+ // apply to item value also, if exists
+ if( m_menuItems[ index ]->GetItemValue() != NULL )
+ {
+ if( m_menuType == GUI_TEXT_MENU )
+ {
+ m_menuItems[ index ]->GetItemValue()->SetColour( m_menuItems[ index ]->m_defaultColour );
+ }
+
+ // hide arrows until item is selected
+ //
+ if( m_menuItems[ index ]->m_itemValueArrowL != NULL &&
+ m_menuItems[ index ]->m_itemValueArrowR != NULL )
+ {
+ m_menuItems[ index ]->m_itemValueArrowL->SetVisible( false );
+ m_menuItems[ index ]->m_itemValueArrowR->SetVisible( false );
+ }
+ }
+
+ if( m_specialEffects & MENU_SFX_SIZE_PULSE )
+ {
+ // reset scale
+ m_menuItems[ index ]->GetItem()->ResetTransformation();
+
+ // apply to item value also, if exists
+ if( m_menuItems[ index ]->GetItemValue() != NULL )
+ {
+ m_menuItems[ index ]->GetItemValue()->ResetTransformation();
+ }
+ }
+/*
+ if( m_specialEffects & MENU_SFX_DROP_SHADOW )
+ {
+ // turn off drop shadow
+ m_menuItems[ index ]->m_item->SetDisplayShadow( false );
+
+ // apply to item value also, if exists
+ if( m_menuItems[ index ]->m_itemValue != NULL )
+ {
+ m_menuItems[ index ]->m_itemValue->SetDisplayShadow( false );
+ }
+ }
+*/
+}
+
+//===========================================================================
+// CGuiMenu::ChangeSelection
+//===========================================================================
+// Description: Select the next menu item.
+//
+// Constraints: None.
+//
+// Parameters: N/A.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiMenu::ChangeSelection( int deltaItems, bool isUserInput )
+{
+ if( m_selection != NO_SELECTION )
+ {
+ rAssert( m_numItems > 0 );
+
+ // Increment and check for wrap around.
+ //
+ int newSelection = (m_selection + deltaItems + m_numItems) % m_numItems;
+
+ // Skip to next enabled selection if new selection is disabled
+ while( newSelection != m_selection )
+ {
+ if( m_menuItems[ newSelection ]->m_attributes & SELECTION_ENABLED )
+ {
+ // Unselect the current item.
+ //
+ this->UnselectItem( m_selection );
+
+ // Notify screen that menu selection has changed
+ // (param1 = new selection, param2 = old selection)
+ //
+ m_pParent->HandleMessage( GUI_MSG_MENU_SELECTION_CHANGED, newSelection, m_selection );
+
+ // Make the new selection.
+ //
+ this->SelectItem( newSelection );
+
+ if( isUserInput )
+ {
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ }
+
+ break;
+ }
+
+ newSelection = (newSelection + deltaItems + m_numItems) % m_numItems;
+ }
+ }
+}
+
+#ifdef RAD_WIN32
+//===========================================================================
+// CGuiMenu::ChangeSelection
+//===========================================================================
+// Description: Select a specific selection.
+//
+// Constraints: None.
+//
+// Parameters: N/A.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiMenu::SetNewSelection( int newSelection, bool isUserInput )
+{
+ if( m_selection != NO_SELECTION )
+ {
+ rAssert( m_numItems > 0 );
+
+ if( newSelection != m_selection )
+ {
+ if( m_menuItems[ newSelection ]->m_attributes & SELECTION_ENABLED )
+ {
+ // Unselect the current item.
+ //
+ this->UnselectItem( m_selection );
+
+ // Notify screen that menu selection has changed
+ // (param1 = new selection, param2 = old selection)
+ //
+ m_pParent->HandleMessage( GUI_MSG_MENU_SELECTION_CHANGED, newSelection, m_selection );
+
+ // Make the new selection.
+ //
+ this->SelectItem( newSelection );
+
+ if( isUserInput )
+ {
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ }
+ }
+ }
+ }
+}
+//===========================================================================
+// CGuiMenu::OutlineSelection
+//===========================================================================
+// Description: Outlines a specific selection.
+//
+// Constraints: None.
+//
+// Parameters: N/A.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiMenu::OutlineSelection( bool bOutline )
+{
+ if (m_selection == NO_SELECTION) return;//------------->
+
+ if( (m_menuItems[ m_selection ]->m_attributes & SELECTABLE) > 0 )
+ {
+ if( m_menuType == GUI_TEXT_MENU )
+ {
+ if( bOutline )
+ m_menuItems[ m_selection ]->GetItem()->SetColour( m_selectionOutlineColour );
+ else
+ m_menuItems[ m_selection ]->GetItem()->SetColour( DEFAULT_SELECTED_ITEM_COLOUR );
+
+ m_bIsSelectionOutlined = bOutline;
+ }
+ }
+}
+
+#endif
+
+//===========================================================================
+// CGuiMenu::IncrementSelectionValue
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: N/A.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiMenu::IncrementSelectionValue( bool isUserInput )
+{
+ if( m_selection != NO_SELECTION &&
+ m_menuItems[ m_selection ]->GetItemValue() != NULL )
+ {
+ int currentIndex = m_menuItems[ m_selection ]->GetItemValueIndex();
+ int newIndex = currentIndex;
+
+ if( currentIndex < m_menuItems[ m_selection ]->m_itemValueCount - 1 )
+ {
+ newIndex = currentIndex + 1;
+ }
+ else if( m_menuItems[ m_selection ]->m_attributes & VALUES_WRAPPED )
+ {
+ // wrap around to beginning
+ newIndex = 0;
+ }
+
+ if( newIndex != currentIndex )
+ {
+ m_menuItems[ m_selection ]->SetItemValueIndex( newIndex );
+
+ // Notify screen that selection value has changed
+ m_pParent->HandleMessage( GUI_MSG_MENU_SELECTION_VALUE_CHANGED, m_selection, newIndex );
+
+ if( isUserInput )
+ {
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ }
+ }
+ }
+}
+
+//===========================================================================
+// CGuiMenu::DecrementSelectionValue
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: N/A.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiMenu::DecrementSelectionValue( bool isUserInput )
+{
+ if( m_selection != NO_SELECTION &&
+ m_menuItems[ m_selection ]->GetItemValue() != NULL )
+ {
+ int currentIndex = m_menuItems[ m_selection ]->GetItemValueIndex();
+ int newIndex = currentIndex;
+
+ if( currentIndex > 0 )
+ {
+ newIndex = currentIndex - 1;
+ }
+ else if( m_menuItems[ m_selection ]->m_attributes & VALUES_WRAPPED )
+ {
+ // wrap around to end
+ newIndex = m_menuItems[ m_selection ]->m_itemValueCount - 1;
+ }
+
+ if( newIndex != currentIndex )
+ {
+ rAssertMsg( newIndex != -1, "This means item value count is zero!" );
+
+ m_menuItems[ m_selection ]->SetItemValueIndex( newIndex );
+
+ // Notify screen that selection value has changed
+ m_pParent->HandleMessage( GUI_MSG_MENU_SELECTION_VALUE_CHANGED, m_selection, newIndex );
+
+ if( isUserInput )
+ {
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ }
+ }
+ }
+}
+
+//===========================================================================
+// Private Member Functions - CGuiMenu
+//===========================================================================
+
+//===========================================================================
+// CGuiMenu::UpdateCurrentSelection
+//===========================================================================
+// Description: Updates current selection.
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiMenu::UpdateCurrentSelection( int elapsedTime )
+{
+ rAssert( m_selection >= 0 && m_selection < m_numItems );
+
+ Scrooby::BoundedDrawable* currentItem = m_menuItems[ m_selection ]->GetItem();
+ rAssert( currentItem != NULL );
+
+ if( m_isSelectionMade ) // update "selection made" effect
+ {
+ m_selectionMadeElapsedTime += elapsedTime;
+
+ if( m_selectionMadeElapsedTime > SELECTION_MADE_DURATION )
+ {
+ this->MakeSelection( false );
+ }
+ }
+ else // update current selection
+ {
+ const unsigned int ITEM_PULSE_PERIOD = 600; // in milliseconds
+
+ if( m_specialEffects & MENU_SFX_COLOUR_PULSE )
+ {
+ // pulse text colour
+ //
+ tColour textColour;
+ GuiSFX::ModulateColour( &textColour,
+ (float)m_elapsedTime,
+ (float)ITEM_PULSE_PERIOD,
+ tColour( 255, 224, 32 ), // m_menuItems[ m_selection ]->m_defaultColour,
+ m_highlightColour );
+
+ currentItem->SetColour( textColour );
+ }
+
+ if( m_specialEffects & MENU_SFX_SIZE_PULSE )
+ {
+ // pulse text size
+ //
+ const float ITEM_SCALE_FACTOR = 0.10f;
+
+ currentItem->ResetTransformation();
+
+// if( m_elapsedTime < ITEM_PULSE_PERIOD )
+ {
+ // TC: [IMPROVE] Man, this looks ugly!
+ //
+ static const float THETA_OFFSET = -rmt::ASin( -0.5f ); // = -ASin( (1 - center) / amplitude )
+
+ float scale = GuiSFX::Pulse( (float)m_elapsedTime,
+ (float)ITEM_PULSE_PERIOD,
+ 1.0f + ITEM_SCALE_FACTOR / 2,
+ ITEM_SCALE_FACTOR,
+ THETA_OFFSET );
+
+ int width = 0;
+ int height = 0;
+ currentItem->GetBoundingBoxSize( width, height );
+
+ // scale text about (left center, right center, or middle center)
+ //
+ if( currentItem->GetHorizontalJustification() == Scrooby::Left )
+ {
+ currentItem->ScaleAboutPoint( scale, 0, height / 2 );
+ }
+ else if( currentItem->GetHorizontalJustification() == Scrooby::Right )
+ {
+ currentItem->ScaleAboutPoint( scale, width, height / 2 );
+ }
+ else
+ {
+ currentItem->ScaleAboutCenter( scale );
+ }
+ }
+ }
+
+ // update slider, if exists on current selection
+ //
+ ImageSlider* currentItemSlider = &(m_menuItems[ m_selection ]->m_slider);
+ if( currentItemSlider->m_pImage != NULL )
+ {
+ bool hasSliderValueChanged = false;
+
+ int numUserInputHandlers = GetGuiSystem()->GetNumUserInputHandlers();
+ for( int i = 0; i < numUserInputHandlers; i++ )
+ {
+ if( m_controllerID != -1 && m_controllerID != static_cast<short>( i ) )
+ {
+ // ignore other controller inputs
+ //
+ continue;
+ }
+
+ CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( i );
+ if( userInputHandler != NULL )
+ {
+#ifdef RAD_WIN32
+ if( userInputHandler->IsXAxisOnLeft() ||
+ GetInputManager()->GetFEMouse()->OnSliderHorizontalClickDrag() == MDIR_LEFT )
+#else
+ if( userInputHandler->IsButtonDown( GuiInput::Left ) ||
+ userInputHandler->IsXAxisOnLeft() )
+#endif
+ {
+ float newValue = currentItemSlider->m_value - elapsedTime / SLIDER_FULL_RANGE_TIME;
+ if( newValue < 0.0f )
+ {
+ newValue = 0.0f; // clamp lower-end at 0.0
+ }
+
+ if( newValue != currentItemSlider->m_value )
+ {
+ currentItemSlider->SetValue( newValue );
+ hasSliderValueChanged = true;
+ }
+ }
+
+#ifdef RAD_WIN32
+ if( userInputHandler->IsXAxisOnRight() ||
+ GetInputManager()->GetFEMouse()->OnSliderHorizontalClickDrag() == MDIR_RIGHT )
+#else
+ if( userInputHandler->IsButtonDown( GuiInput::Right ) ||
+ userInputHandler->IsXAxisOnRight() )
+#endif
+ {
+ float newValue = currentItemSlider->m_value + elapsedTime / SLIDER_FULL_RANGE_TIME;
+ if( newValue > 1.0f )
+ {
+ newValue = 1.0f; // clamp higher-end at 1.0
+ }
+
+ if( newValue != currentItemSlider->m_value )
+ {
+ currentItemSlider->SetValue( newValue );
+ hasSliderValueChanged = true;
+ }
+ }
+ }
+ }
+
+ if( hasSliderValueChanged )
+ {
+ // notify screen that slider value has changed
+ //
+ m_pParent->HandleMessage( GUI_MSG_MENU_SELECTION_VALUE_CHANGED, m_selection );
+ }
+ else
+ {
+ // otherwise, notify screen that slider value is currently not changing
+ //
+ m_pParent->HandleMessage( GUI_MSG_MENU_SLIDER_NOT_CHANGING, m_selection );
+ }
+ }
+
+ // update L/R arrows (if exists for current selection)
+ //
+ if( m_menuItems[ m_selection ]->m_itemValueArrowL != NULL &&
+ m_menuItems[ m_selection ]->m_itemValueArrowR != NULL )
+ {
+ m_menuItems[ m_selection ]->m_itemValueArrowL->SetIndex( 0 );
+ m_menuItems[ m_selection ]->m_itemValueArrowR->SetIndex( 0 );
+
+ // if there are more than one item to choose from
+ //
+ if( m_menuItems[ m_selection ]->m_itemValueCount > 1 )
+ {
+ int numUserInputHandlers = GetGuiSystem()->GetNumUserInputHandlers();
+ for( int i = 0; i < numUserInputHandlers; i++ )
+ {
+ if( m_controllerID != -1 && m_controllerID != static_cast<short>( i ) )
+ {
+ // ignore other controller inputs
+ //
+ continue;
+ }
+
+ CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( i );
+ if( userInputHandler != NULL )
+ {
+#ifdef RAD_WIN32
+ if( userInputHandler->IsXAxisOnLeft() ||
+ GetInputManager()->GetFEMouse()->LeftButtonDownOn() == HOTSPOT_ARROWLEFT )
+#else
+ if( userInputHandler->IsButtonDown( GuiInput::Left ) ||
+ userInputHandler->IsXAxisOnLeft() )
+#endif
+ {
+ rAssert( m_menuItems[ m_selection ]->m_itemValueArrowL->GetNumOfImages() > 1 );
+ m_menuItems[ m_selection ]->m_itemValueArrowL->SetIndex( 1 );
+ }
+
+#ifdef RAD_WIN32
+ if( userInputHandler->IsXAxisOnRight() ||
+ GetInputManager()->GetFEMouse()->LeftButtonDownOn() == HOTSPOT_ARROWRIGHT )
+#else
+ if( userInputHandler->IsButtonDown( GuiInput::Right ) ||
+ userInputHandler->IsXAxisOnRight() )
+#endif
+ {
+ rAssert( m_menuItems[ m_selection ]->m_itemValueArrowR->GetNumOfImages() > 1 );
+ m_menuItems[ m_selection ]->m_itemValueArrowR->SetIndex( 1 );
+ }
+ }
+ }
+ }
+ }
+
+ // update elapsed time
+ //
+ m_elapsedTime = (m_elapsedTime + elapsedTime) % ITEM_PULSE_PERIOD;
+ }
+}
+
+//===========================================================================
+// CGuiMenu::MoveCursor
+//===========================================================================
+// Description: Move cursor from previous selection to next selection.
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiMenu::MoveCursor( int previousIndex, int nextIndex )
+{
+ // if cursor exists
+ //
+ if( m_pCursor != NULL && previousIndex != NO_SELECTION )
+ {
+ Scrooby::BoundedDrawable* item = NULL;
+ int x1, y1, x2, y2;
+
+ // get location of previous item
+ item = m_menuItems[ previousIndex ]->GetItem();
+ rAssert( item != NULL );
+ item->GetOriginPosition( x1, y1 );
+
+ // get location of next item
+ item = m_menuItems[ nextIndex ]->GetItem();
+ rAssert( item != NULL );
+ item->GetOriginPosition( x2, y2 );
+
+ // translate cursor
+ m_pCursor->Translate( x2 - x1, y2 - y1 );
+ }
+}
+
+
+//===========================================================================
+// Public Member Functions - CGuiMenu2D
+//===========================================================================
+
+CGuiMenu2D::CGuiMenu2D( CGuiEntity* pParent,
+ int numItems,
+ int numColumns,
+ eMenuType menuType,
+ int specialEffects )
+: CGuiMenu( pParent, numItems, menuType, specialEffects ),
+ m_numColumns( static_cast<short>( numColumns ) )
+{
+}
+
+CGuiMenu2D::~CGuiMenu2D()
+{
+}
+
+void
+CGuiMenu2D::HandleMessage( eGuiMessage message, unsigned int param1,
+ unsigned int param2 )
+{
+ if( m_isSelectionMade && this->IsControllerMessage( message ) )
+ {
+ // selection has already been made, ignore all controller messages
+ //
+ return;
+ }
+
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_LEFT:
+ {
+ this->ChangeSelection( -1 );
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_RIGHT:
+ {
+ this->ChangeSelection( +1 );
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_UP:
+ {
+ this->ChangeSelection( -m_numColumns );
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_DOWN:
+ {
+ this->ChangeSelection( +m_numColumns );
+
+ break;
+ }
+ default:
+ {
+ CGuiMenu::HandleMessage( message, param1, param2 );
+
+ break;
+ }
+ }
+}
+
+
+//===========================================================================
+// Public Member Functions - CGuiMenuPrompt
+//===========================================================================
+
+CGuiMenuPrompt::CGuiMenuPrompt( CGuiEntity* pParent,
+ Scrooby::Page* pPage,
+ int numResponses,
+ int specialEffects )
+:
+ CGuiMenu( pParent, numResponses, GUI_TEXT_MENU, specialEffects ),
+ m_numResponses( static_cast<short>( numResponses ) )
+{
+ rAssert( pPage != NULL );
+
+ Scrooby::Group* menu = pPage->GetGroup( "Menu" );
+ rAssert( menu != NULL );
+ // add response menu items
+ //
+ for( short i = 0; i < m_numResponses; i++ )
+ {
+ char textName[ 32 ];
+ sprintf( textName, "Response%d", i );
+ this->AddMenuItem( menu->GetText( textName ) );
+ }
+}
+
+CGuiMenuPrompt::~CGuiMenuPrompt()
+{
+}
+
+void
+CGuiMenuPrompt::SetNumResponses( int numResponses )
+{
+ for( short i = 0; i < m_numResponses; i++ )
+ {
+ this->SetMenuItemEnabled( i, (i < numResponses), true );
+ }
+}
+
+void
+CGuiMenuPrompt::SetResponse( int index, ePromptResponse response )
+{
+ rAssert( static_cast<short>( index ) >= 0 && static_cast<short>( index ) < m_numResponses );
+
+ GuiMenuItem* menuItem = this->GetMenuItem( index );
+ rAssert( menuItem != NULL );
+
+ Scrooby::Text* textItem = dynamic_cast<Scrooby::Text*>( menuItem->GetItem() );
+ rAssert( textItem != NULL );
+ textItem->SetIndex( response );
+}
+
+CGuiMenuPrompt::ePromptResponse
+CGuiMenuPrompt::GetResponse( int index ) const
+{
+ rAssert( static_cast<short>( index ) >= 0 && static_cast<short>( index ) < m_numResponses );
+
+ GuiMenuItem* menuItem = this->GetMenuItem( index );
+ rAssert( menuItem != NULL );
+
+ Scrooby::Text* textItem = dynamic_cast<Scrooby::Text*>( menuItem->GetItem() );
+ rAssert( textItem != NULL );
+
+ return static_cast<ePromptResponse>( textItem->GetIndex() );
+}
+
+CGuiMenuPrompt::ePromptResponse
+CGuiMenuPrompt::GetCurrentResponse() const
+{
+ return this->GetResponse( m_selection );
+}
+
diff --git a/game/code/presentation/gui/guimenu.h b/game/code/presentation/gui/guimenu.h
new file mode 100644
index 0000000..0361079
--- /dev/null
+++ b/game/code/presentation/gui/guimenu.h
@@ -0,0 +1,251 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiMenu
+//
+// Description:
+//
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/12/4 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUIMENU_H
+#define GUIMENU_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guientity.h>
+#include <presentation/gui/guimenuitem.h>
+
+namespace Scrooby
+{
+ class Page;
+ class Drawable;
+ class BoundedDrawable;
+ class Sprite;
+}
+
+enum eMenuType
+{
+ GUI_TEXT_MENU,
+ GUI_SPRITE_MENU,
+
+ NUM_MENU_TYPES
+};
+
+enum eMenuSpecialEffect
+{
+ MENU_SFX_NONE = 0,
+
+ MENU_SFX_COLOUR_PULSE = 1,
+ MENU_SFX_SIZE_PULSE = 2,
+
+ MENU_SFX_ALL = ~0
+};
+
+//===========================================================================
+// Interface Definitions - CGuiMenu
+//===========================================================================
+class CGuiMenu : public CGuiEntity
+{
+public:
+ CGuiMenu( CGuiEntity* pParent,
+ int maxNumItems = 1,
+ eMenuType menuType = GUI_TEXT_MENU,
+ int specialEffects = MENU_SFX_SIZE_PULSE );
+
+ virtual ~CGuiMenu();
+
+ void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ GuiMenuItem* AddMenuItem( Scrooby::BoundedDrawable* pItem,
+ Scrooby::BoundedDrawable* pItemValue = NULL,
+ Scrooby::Polygon* pSlider = NULL,
+ Scrooby::Sprite* pSliderImage = NULL,
+ Scrooby::Sprite* pItemValueArrowL = NULL,
+ Scrooby::Sprite* pItemValueArrowR = NULL,
+ int attributes = ALL_ATTRIBUTES_ON );
+
+ void SetMenuItemEnabled( int index, bool enabled,
+ bool changeVisibility = false );
+
+ bool IsMenuItemEnabled( int index );
+
+ void SetCursor( Scrooby::Drawable* pCursor ) { m_pCursor = pCursor; }
+ Scrooby::Drawable* GetCursor() const { return m_pCursor; }
+
+ // Reset Menu
+ //
+ void Reset( int defaultSelection = 0 );
+
+ // Menu Highlight Colour
+ //
+ void SetHighlightColour( bool isEnabled, tColour colour );
+ tColour GetHighlightColour() const { return m_highlightColour; }
+
+ void SetGreyOutEnabled( bool isEnabled ) { m_isGreyOutEnabled = isEnabled; }
+
+ // Selection Made Outline Colour
+ //
+ void SetSelectionMadeOutlineColour( tColour colour ) { m_selectionMadeOutlineColour = colour; }
+
+ // Set Selection Values and Value Counts
+ //
+ void SetSelectionValue( int index, int value );
+ void SetSelectionValueCount( int index, int count );
+
+ // Make Menu Selection and Query Menu Selection Made
+ //
+ void MakeSelection( bool isSelectionMade = true );
+ bool HasSelectionBeenMade() const { return m_isSelectionMade; }
+
+ // Set Specific Controller ID
+ //
+ void SetControllerID( int controllerID );
+
+ // Menu Accessors
+ //
+ GuiMenuItem* GetMenuItem( int index ) const;
+ int GetNumItems() const { return m_numItems; }
+ int GetSelection() const { return m_selection; }
+ int GetSelectionValue( int index ) const;
+
+protected:
+ enum eSelection { NO_SELECTION = -1 };
+
+ void SelectItem( int index );
+ void UnselectItem( int index );
+
+ void ChangeSelection( int deltaItems, bool isUserInput = true );
+#ifdef RAD_WIN32
+ void SetNewSelection( int newSelection, bool isUserInput = false );
+ void OutlineSelection( bool bOutline = true );
+#endif
+ void IncrementSelectionValue( bool isUserInput = true );
+ void DecrementSelectionValue( bool isUserInput = true );
+
+ eMenuType m_menuType;
+ short m_specialEffects; // bitmask
+
+ GuiMenuItem** m_menuItems;
+ int m_numItems;
+
+ int m_selection;
+#ifdef RAD_WIN32
+ bool m_bIsSelectionOutlined;
+ tColour m_selectionOutlineColour;
+#endif
+ Scrooby::Drawable* m_pCursor;
+ bool m_isSelectionMade : 1;
+
+private:
+ void UpdateCurrentSelection( int elapsedTime );
+ void MoveCursor( int previousIndex, int nextIndex );
+
+ bool m_isHighlightEnabled : 1;
+ tColour m_highlightColour;
+ tColour m_selectionMadeOutlineColour;
+
+ bool m_isGreyOutEnabled : 1;
+
+ unsigned int m_elapsedTime;
+ int m_selectionMadeElapsedTime;
+
+ short m_controllerID;
+};
+
+inline void CGuiMenu::SetControllerID( int controllerID )
+{
+ m_controllerID = static_cast<short>( controllerID );
+}
+
+inline GuiMenuItem* CGuiMenu::GetMenuItem( int index ) const
+{
+ rAssert( index >= 0 && index < m_numItems );
+ return m_menuItems[ index ];
+}
+
+inline int CGuiMenu::GetSelectionValue( int index ) const
+{
+ rAssert( index >= 0 && index < m_numItems );
+ return m_menuItems[ index ]->GetItemValueIndex();
+}
+
+//===========================================================================
+// Interface Definitions - CGuiMenu2D
+//===========================================================================
+class CGuiMenu2D : public CGuiMenu
+{
+public:
+ CGuiMenu2D( CGuiEntity* pParent,
+ int numItems,
+ int numColumns,
+ eMenuType menuType = GUI_TEXT_MENU,
+ int specialEffects = MENU_SFX_SIZE_PULSE );
+
+ virtual ~CGuiMenu2D();
+
+ void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+private:
+ short m_numColumns;
+
+};
+
+//===========================================================================
+// Interface Definitions - CGuiMenuPrompt
+//===========================================================================
+class CGuiMenuPrompt : public CGuiMenu
+{
+public:
+ enum ePromptResponse
+ {
+ RESPONSE_NO,
+ RESPONSE_YES,
+ RESPONSE_CONTINUE,
+ RESPONSE_CONTINUE_WITHOUT_SAVE,
+ RESPONSE_RETRY,
+ RESPONSE_MANAGE_MEMCARDS,
+ RESPONSE_FORMAT_GC,
+ RESPONSE_FORMAT_PS2,
+ RESPONSE_FORMAT_XBOX,
+ RESPONSE_CONTINUE_WITHOUT_FORMAT,
+ RESPONSE_DELETE,
+ RESPONSE_OK,
+ RESPONSE_LOAD,
+ RESPONSE_SAVE,
+
+ NUM_PROMPT_RESPONSES
+ };
+
+ static const int MAX_NUM_RESPONSES = 3;
+
+ CGuiMenuPrompt( CGuiEntity* pParent,
+ Scrooby::Page* pPage,
+ int numResponses = MAX_NUM_RESPONSES,
+ int specialEffects = MENU_SFX_SIZE_PULSE );
+
+ virtual ~CGuiMenuPrompt();
+
+ void SetNumResponses( int numResponses );
+ void SetResponse( int index, ePromptResponse response );
+ ePromptResponse GetResponse( int index ) const;
+ ePromptResponse GetCurrentResponse() const;
+
+private:
+ short m_numResponses;
+
+};
+
+#endif // GUIMENU_H
diff --git a/game/code/presentation/gui/guimenuitem.cpp b/game/code/presentation/gui/guimenuitem.cpp
new file mode 100644
index 0000000..daf6490
--- /dev/null
+++ b/game/code/presentation/gui/guimenuitem.cpp
@@ -0,0 +1,318 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: GuiMenuItem
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/19 TChu Created
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/guimenuitem.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions - GuiMenuItem
+//===========================================================================
+
+GuiMenuItem::GuiMenuItem()
+: m_attributes( ALL_ATTRIBUTES_OFF ),
+ m_defaultColour( 0, 0, 0 ),
+ m_itemValueArrowL( NULL ),
+ m_itemValueArrowR( NULL ),
+ m_itemValueCount( 0 )
+{
+}
+
+GuiMenuItem::~GuiMenuItem()
+{
+}
+
+//===========================================================================
+// Public Member Functions - GuiMenuItemText
+//===========================================================================
+
+GuiMenuItemText::GuiMenuItemText()
+: GuiMenuItem(),
+ m_item( NULL ),
+ m_itemValue( NULL )
+{
+}
+
+//===========================================================================
+// GuiMenuItemText::SetItem
+//===========================================================================
+// Description: Sets the text drawable menu item.
+//
+// Parameters: pointer to a Scrooby text drawable
+//
+// Return: n/a
+//
+//===========================================================================
+void
+GuiMenuItemText::SetItem( Scrooby::BoundedDrawable* item )
+{
+ m_item = dynamic_cast<Scrooby::Text*>( item );
+ rAssert( m_item != NULL );
+}
+
+//===========================================================================
+// GuiMenuItemText::SetItemValue
+//===========================================================================
+// Description: Sets the text drawable menu item value.
+//
+// Parameters: pointer to a Scrooby text drawable
+//
+// Return: n/a
+//
+//===========================================================================
+void
+GuiMenuItemText::SetItemValue( Scrooby::BoundedDrawable* itemValue )
+{
+ m_itemValue = dynamic_cast<Scrooby::Text*>( itemValue );
+ rAssert( m_itemValue != NULL );
+
+ m_itemValueCount = m_itemValue->GetNumOfStrings();
+}
+
+//===========================================================================
+// GuiMenuItemText::SetItemValueIndex
+//===========================================================================
+// Description: Sets the current item value index.
+//
+// Parameters: text index
+//
+// Return: n/a
+//
+//===========================================================================
+void
+GuiMenuItemText::SetItemValueIndex( int index )
+{
+ rAssert( m_itemValue != NULL );
+ m_itemValue->SetIndex( index );
+}
+
+//===========================================================================
+// GuiMenuItemText::GetItemValueIndex
+//===========================================================================
+// Description: Gets the current item value index.
+//
+// Parameters: n/a
+//
+// Return: text index
+//
+//===========================================================================
+int
+GuiMenuItemText::GetItemValueIndex() const
+{
+ rAssert( m_itemValue != NULL );
+ return m_itemValue->GetIndex();
+}
+
+//===========================================================================
+// GuiMenuItemText::SetDisplayOutline
+//===========================================================================
+// Description: Enables/Disables text outlining.
+//
+// Parameters: enable/disable flag
+//
+// Return: n/a
+//
+//===========================================================================
+void
+GuiMenuItemText::SetDisplayOutline( bool enable )
+{
+ rAssert( m_item != NULL );
+ m_item->SetDisplayOutline( enable );
+
+ if( m_itemValue != NULL )
+ {
+ m_itemValue->SetDisplayOutline( enable );
+ }
+}
+
+//===========================================================================
+// GuiMenuItemText::SetOutlineColour
+//===========================================================================
+// Description: Sets the current text outline colour.
+//
+// Parameters: colour
+//
+// Return: n/a
+//
+//===========================================================================
+void
+GuiMenuItemText::SetOutlineColour( tColour colour )
+{
+ rAssert( m_item != NULL );
+ m_item->SetOutlineColour( colour );
+
+ if( m_itemValue != NULL )
+ {
+ m_itemValue->SetOutlineColour( colour );
+ }
+}
+
+//===========================================================================
+// GuiMenuItemText::GetOutlineColour
+//===========================================================================
+// Description: Gets the current text outline colour.
+//
+// Parameters: n/a
+//
+// Return: colour
+//
+//===========================================================================
+tColour
+GuiMenuItemText::GetOutlineColour() const
+{
+ rAssert( m_item != NULL );
+ return m_item->GetOutlineColour();
+}
+
+
+//===========================================================================
+// Public Member Functions - GuiMenuItemSprite
+//===========================================================================
+
+GuiMenuItemSprite::GuiMenuItemSprite()
+: GuiMenuItem(),
+ m_item( NULL ),
+ m_itemValue( NULL )
+{
+}
+
+//===========================================================================
+// GuiMenuItemSprite::SetItem
+//===========================================================================
+// Description: Sets the text drawable menu item.
+//
+// Parameters: pointer to a Scrooby sprite drawable
+//
+// Return: n/a
+//
+//===========================================================================
+void
+GuiMenuItemSprite::SetItem( Scrooby::BoundedDrawable* item )
+{
+ m_item = dynamic_cast<Scrooby::Sprite*>( item );
+ rAssert( m_item != NULL );
+}
+
+//===========================================================================
+// GuiMenuItemSprite::SetItemValue
+//===========================================================================
+// Description: Sets the text drawable menu item value.
+//
+// Parameters: pointer to a Scrooby sprite drawable
+//
+// Return: n/a
+//
+//===========================================================================
+void
+GuiMenuItemSprite::SetItemValue( Scrooby::BoundedDrawable* itemValue )
+{
+ m_itemValue = dynamic_cast<Scrooby::Sprite*>( itemValue );
+ rAssert( m_itemValue != NULL );
+
+ m_itemValueCount = m_itemValue->GetNumOfImages();
+}
+
+//===========================================================================
+// GuiMenuItemSprite::SetItemValueIndex
+//===========================================================================
+// Description: Sets the current item value index.
+//
+// Parameters: sprite index
+//
+// Return: n/a
+//
+//===========================================================================
+void
+GuiMenuItemSprite::SetItemValueIndex( int index )
+{
+ rAssert( m_itemValue != NULL );
+ m_itemValue->SetIndex( index );
+}
+
+//===========================================================================
+// GuiMenuItemSprite::GetItemValueIndex
+//===========================================================================
+// Description: Gets the current item value index.
+//
+// Parameters: n/a
+//
+// Return: sprite index
+//
+//===========================================================================
+int
+GuiMenuItemSprite::GetItemValueIndex() const
+{
+ rAssert( m_itemValue != NULL );
+ return m_itemValue->GetIndex();
+}
+
+//===========================================================================
+// GuiMenuItemSprite::SetDisplayOutline
+//===========================================================================
+// Description: Not applicable for GuiMenuItemSprite's and should never be
+// invoked.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//===========================================================================
+void
+GuiMenuItemSprite::SetDisplayOutline( bool enable )
+{
+ rAssertMsg( false, "No outlining on sprites!" );
+}
+
+//===========================================================================
+// GuiMenuItemSprite::SetOutlineColour
+//===========================================================================
+// Description: Not applicable for GuiMenuItemSprite's and should never be
+// invoked.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//===========================================================================
+void
+GuiMenuItemSprite::SetOutlineColour( tColour colour )
+{
+ rAssertMsg( false, "No outlining on sprites!" );
+}
+
+//===========================================================================
+// GuiMenuItemSprite::SetOutlineColour
+//===========================================================================
+// Description: Not applicable for GuiMenuItemSprite's and should never be
+// invoked.
+//
+// Parameters: n/a
+//
+// Return: n/a
+//
+//===========================================================================
+tColour
+GuiMenuItemSprite::GetOutlineColour() const
+{
+ rAssertMsg( false, "No outlining on sprites!" );
+
+ return tColour( 0, 0, 0 );
+}
+
diff --git a/game/code/presentation/gui/guimenuitem.h b/game/code/presentation/gui/guimenuitem.h
new file mode 100644
index 0000000..6d10730
--- /dev/null
+++ b/game/code/presentation/gui/guimenuitem.h
@@ -0,0 +1,164 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: GuiMenuItem
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/19 TChu Created
+//
+//===========================================================================
+
+#ifndef GUIMENUITEM_H
+#define GUIMENUITEM_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/utility/slider.h>
+
+#include <p3d/p3dtypes.hpp>
+
+// Scrooby
+//
+#include <text.h>
+#include <sprite.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+namespace Scrooby
+{
+ class BoundedDrawable;
+ class Sprite;
+ class Text;
+}
+
+enum eMenuAttribute
+{
+ ALL_ATTRIBUTES_OFF = 0,
+
+ SELECTION_ENABLED = 1,
+ SELECTABLE = 2,
+ VALUES_WRAPPED = 4,
+ TEXT_OUTLINE_ENABLED = 8,
+
+ ALL_ATTRIBUTES_ON = -1
+};
+
+//===========================================================================
+// Interface Definitions - GuiMenuItem (Abstract Base Class)
+//===========================================================================
+struct GuiMenuItem
+{
+ GuiMenuItem();
+ virtual ~GuiMenuItem();
+
+ virtual void SetItem( Scrooby::BoundedDrawable* item ) = 0;
+ virtual Scrooby::BoundedDrawable* GetItem() const = 0;
+
+ virtual void SetItemValue( Scrooby::BoundedDrawable* itemValue ) = 0;
+ virtual Scrooby::BoundedDrawable* GetItemValue() const = 0;
+
+ virtual void SetItemValueIndex( int index ) = 0;
+ virtual int GetItemValueIndex() const = 0;
+
+ virtual void SetDisplayOutline( bool enable ) = 0;
+ virtual void SetOutlineColour( tColour colour ) = 0;
+ virtual tColour GetOutlineColour() const = 0;
+
+ int m_attributes;
+ tColour m_defaultColour;
+
+ Scrooby::Sprite* m_itemValueArrowL;
+ Scrooby::Sprite* m_itemValueArrowR;
+ int m_itemValueCount;
+
+ ImageSlider m_slider;
+
+};
+
+//===========================================================================
+// Interface Definitions - GuiMenuItemText
+//===========================================================================
+struct GuiMenuItemText : public GuiMenuItem
+{
+ GuiMenuItemText();
+
+ virtual void SetItem( Scrooby::BoundedDrawable* item );
+ virtual Scrooby::BoundedDrawable* GetItem() const;
+
+ virtual void SetItemValue( Scrooby::BoundedDrawable* itemValue );
+ virtual Scrooby::BoundedDrawable* GetItemValue() const;
+
+ virtual void SetItemValueIndex( int index );
+ virtual int GetItemValueIndex() const;
+
+ virtual void SetDisplayOutline( bool enable );
+ virtual void SetOutlineColour( tColour colour );
+ virtual tColour GetOutlineColour() const;
+
+ Scrooby::Text* m_item;
+ Scrooby::Text* m_itemValue;
+
+};
+
+//===========================================================================
+// Inlines - GuiMenuItemText
+//===========================================================================
+
+inline Scrooby::BoundedDrawable* GuiMenuItemText::GetItem() const
+{
+ return m_item;
+}
+
+inline Scrooby::BoundedDrawable* GuiMenuItemText::GetItemValue() const
+{
+ return m_itemValue;
+}
+
+//===========================================================================
+// Interface Definitions - GuiMenuItemSprite
+//===========================================================================
+struct GuiMenuItemSprite : public GuiMenuItem
+{
+ GuiMenuItemSprite();
+
+ virtual void SetItem( Scrooby::BoundedDrawable* item );
+ virtual Scrooby::BoundedDrawable* GetItem() const;
+
+ virtual void SetItemValue( Scrooby::BoundedDrawable* itemValue );
+ virtual Scrooby::BoundedDrawable* GetItemValue() const;
+
+ virtual void SetItemValueIndex( int index );
+ virtual int GetItemValueIndex() const;
+
+ virtual void SetDisplayOutline( bool enable );
+ virtual void SetOutlineColour( tColour colour );
+ virtual tColour GetOutlineColour() const;
+
+ Scrooby::Sprite* m_item;
+ Scrooby::Sprite* m_itemValue;
+
+};
+
+//===========================================================================
+// Inlines - GuiMenuItemSprite
+//===========================================================================
+
+inline Scrooby::BoundedDrawable* GuiMenuItemSprite::GetItem() const
+{
+ return m_item;
+}
+
+inline Scrooby::BoundedDrawable* GuiMenuItemSprite::GetItemValue() const
+{
+ return m_itemValue;
+}
+
+
+#endif // GUIMENUITEM_H
diff --git a/game/code/presentation/gui/guiscreen.cpp b/game/code/presentation/gui/guiscreen.cpp
new file mode 100644
index 0000000..aee9b0e
--- /dev/null
+++ b/game/code/presentation/gui/guiscreen.cpp
@@ -0,0 +1,1314 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreen
+//
+// Description: Implementation of the CGuiScreen class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/21 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <memory/classsizetracker.h>
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/utility/specialfx.h>
+
+#include <events/eventmanager.h>
+
+#include <app.h>
+#include <screen.h>
+#include <page.h>
+#include <layer.h>
+#include <group.h>
+#include <pure3dobject.h>
+#include <sprite.h>
+#include <text.h>
+
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/utility.hpp>
+#include <pddi/pddi.hpp>
+
+#include <raddebug.hpp>
+
+#include <string.h>
+
+#ifdef RAD_WIN32
+#include <input/inputmanager.h>
+#endif
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//#define PRINTSCREENUPDATES
+
+const char* FOREGROUND_LAYER = "Foreground";
+const char* BACKGROUND_LAYER = "Background";
+const char* FE_PURE3D_OBJECT = "CamAndSet";
+
+const float DEFAULT_SCREEN_FADE_TIME = 250.0f; // in msec
+const float DEFAULT_SCREEN_ZOOM_TIME = 250.0f; // in msec
+const float DEFAULT_SCREEN_SLIDE_TIME = 250.0f; // in msec
+const float DEFAULT_IRIS_WIPE_SPEED = 0.5f;
+
+const float WIDE_SCREEN_CORRECTION_SCALE = (4.0f / 3.0f) * (9.0f / 16.0f);
+
+const char* ACCEPT_PAGES[] =
+{
+ "Accept",
+ "Accept2",
+ "Accept3",
+ "Buy",
+ "Continue",
+
+ "" // dummy terminator
+};
+
+const int NUM_ACCEPT_PAGES = sizeof( ACCEPT_PAGES ) / sizeof( ACCEPT_PAGES[ 0 ] );
+
+const char* BACK_PAGES[] =
+{
+ "Back",
+ "Back2",
+ "Cancel",
+
+ "" // dummy terminator
+};
+
+const int NUM_BACK_PAGES = sizeof( BACK_PAGES ) / sizeof( BACK_PAGES[ 0 ] );
+
+tMultiController* CGuiScreen::s_p3dMultiController = NULL;
+float CGuiScreen::s_numIrisFrames = 0.0f;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreen::CGuiScreen
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreen::CGuiScreen
+(
+ Scrooby::Screen* pScroobyScreen,
+ CGuiEntity* pParent,
+ eGuiWindowID id,
+ unsigned int screenFX
+)
+: CGuiWindow( id, pParent ),
+ m_pScroobyScreen( pScroobyScreen ),
+ m_screenCover( NULL ),
+ m_p3dObject( NULL ),
+ m_p3dIris( NULL ),
+ m_irisController( NULL ),
+ m_currentIrisState( IRIS_STATE_IDLE ),
+ m_autoOpenIris( false ),
+ m_numForegroundLayers( 0 ),
+ m_numBackgroundLayers( 0 ),
+ m_ignoreControllerInputs( false ),
+ m_inverseFading( false ),
+ m_screenFX( screenFX ),
+ m_fadeTime( DEFAULT_SCREEN_FADE_TIME ),
+ m_elapsedFadeTime( -1 ),
+ m_zoomTime( DEFAULT_SCREEN_ZOOM_TIME ),
+ m_elapsedZoomTime( -1 ),
+ m_slideTime( DEFAULT_SCREEN_SLIDE_TIME ),
+ m_elapsedSlideTime( -1 ),
+ m_playTransitionAnimationLast( false )
+{
+ CLASSTRACKER_CREATE( CGuiScreen );
+ memset( m_foregroundLayers, 0, sizeof( m_foregroundLayers ) );
+ memset( m_backgroundLayers, 0, sizeof( m_backgroundLayers ) );
+ memset( m_buttonIcons, 0, sizeof( m_buttonIcons ) );
+
+ m_guiManager = static_cast<CGuiManager*>( m_pParent );
+ rAssert( m_guiManager );
+
+ rAssert( m_pScroobyScreen != NULL );
+
+ Scrooby::Page* pPage = NULL;
+
+ // Get foreground and background layers of all pages in screen
+ //
+ int numPages = m_pScroobyScreen->GetNumberOfPages();
+ for( int i = 0; i < numPages; i++ )
+ {
+ pPage = m_pScroobyScreen->GetPageByIndex( i );
+ rAssert( pPage );
+
+ Scrooby::Layer* pLayer = pPage->GetLayer( FOREGROUND_LAYER );
+ if( pLayer != NULL )
+ {
+ rAssert( m_numForegroundLayers < MAX_FOREGROUND_LAYERS );
+ m_foregroundLayers[ m_numForegroundLayers ] = pLayer;
+ m_numForegroundLayers++;
+
+ if( this->IsWideScreenDisplay() )
+ {
+ pLayer->ResetTransformation();
+ this->ApplyWideScreenCorrectionScale( pLayer );
+ }
+ }
+
+ pLayer = pPage->GetLayer( BACKGROUND_LAYER );
+ if( pLayer != NULL )
+ {
+ rAssert( m_numBackgroundLayers < MAX_BACKGROUND_LAYERS );
+ m_backgroundLayers[ m_numBackgroundLayers ] = pLayer;
+ m_numBackgroundLayers++;
+ }
+ }
+
+ this->RestoreScreenCover();
+
+ // Get pure3d object from "3dFE" page, if included
+ //
+ pPage = m_pScroobyScreen->GetPage( "3dFE" );
+ if( pPage != NULL )
+ {
+ m_p3dObject = pPage->GetPure3dObject( FE_PURE3D_OBJECT );
+ rAssert( m_p3dObject );
+ }
+
+ // Get iris pure3d object from "IrisCover" page, if included
+ //
+ pPage = m_pScroobyScreen->GetPage( "IrisCover" );
+ if( pPage != NULL )
+ {
+ // set iris layer visible
+ //
+ Scrooby::Layer* irisLayer = pPage->GetLayer( "IrisCover" );
+ rAssert( irisLayer != NULL );
+ irisLayer->SetVisible( true );
+
+ m_p3dIris = pPage->GetPure3dObject( "3dIris" );
+ rAssert( m_p3dIris != NULL );
+ m_p3dIris->SetZBufferEnabled( false );
+
+ m_irisController = p3d::find<tMultiController>( "IrisController" );
+ rAssert( m_irisController != NULL );
+ m_irisController->AddRef();
+
+ s_numIrisFrames = m_irisController->GetNumFrames();
+ }
+
+ // Get button icons
+ //
+ for( int j = 0; j < NUM_ACCEPT_PAGES; j++ )
+ {
+ pPage = m_pScroobyScreen->GetPage( ACCEPT_PAGES[ j ] );
+ if( pPage != NULL )
+ {
+ m_buttonIcons[ BUTTON_ICON_ACCEPT ] = pPage->GetGroup( "AcceptLabel" );
+ rAssert( m_buttonIcons[ BUTTON_ICON_ACCEPT ] != NULL );
+
+#ifndef RAD_WIN32
+ // add text outline to accept text
+ //
+ Scrooby::Text* accept = m_buttonIcons[ BUTTON_ICON_ACCEPT ]->GetText( "Accept" );
+ rAssert( accept != NULL );
+ accept->SetDisplayOutline( true );
+#endif
+
+ break;
+ }
+ }
+
+ for( int k = 0; k < NUM_BACK_PAGES; k++ )
+ {
+ pPage = m_pScroobyScreen->GetPage( BACK_PAGES[ k ] );
+ if( pPage != NULL )
+ {
+ m_buttonIcons[ BUTTON_ICON_BACK ] = pPage->GetGroup( "BackLabel" );
+ rAssert( m_buttonIcons[ BUTTON_ICON_BACK ] != NULL );
+
+#ifndef RAD_WIN32
+ // add text outline to accept text
+ //
+ Scrooby::Text* back = m_buttonIcons[ BUTTON_ICON_BACK ]->GetText( "Back" );
+ rAssert( back != NULL );
+ back->SetDisplayOutline( true );
+#endif
+
+ break;
+ }
+ }
+}
+
+//===========================================================================
+// CGuiScreen::~CGuiScreen
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreen::~CGuiScreen()
+{
+ CLASSTRACKER_DESTROY( CGuiScreen );
+ if( m_irisController != NULL )
+ {
+ m_irisController->Release();
+ m_irisController = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreen::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiScreen::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_ignoreControllerInputs &&
+ this->IsControllerMessage( message ) )
+ {
+ // ignore controller messages
+ return;
+ }
+
+
+#ifdef RAD_WIN32
+ if( message == GUI_MSG_WINDOW_ENTER )
+ {
+ // just entered screen, so re-enable mouse
+ GetInputManager()->GetFEMouse()->SetSelectable( true );
+ }
+ else if( message == GUI_MSG_WINDOW_EXIT )
+ {
+ // exiting screen, so disable mouse
+ GetInputManager()->GetFEMouse()->SetSelectable( false );
+ }
+#endif
+
+ switch( message )
+ {
+ case GUI_MSG_WINDOW_ENTER:
+ {
+ // reset any pending transitions
+ m_numTransitionsPending = 0;
+
+ if( !m_inverseFading )
+ {
+ if( m_screenFX & SCREEN_FX_FADE )
+ {
+ this->SetAlphaForLayers( 0.0f,
+ m_foregroundLayers,
+ m_numForegroundLayers );
+ }
+ }
+
+ if( m_screenFX & SCREEN_FX_ZOOM )
+ {
+ this->SetAlphaForLayers( 0.0f,
+ m_backgroundLayers,
+ m_numBackgroundLayers );
+ }
+
+ if( m_screenFX & SCREEN_FX_IRIS )
+ {
+ this->SetAlphaForLayers( 0.0f,
+ m_foregroundLayers,
+ m_numForegroundLayers );
+
+ this->SetAlphaForLayers( 0.0f,
+ m_backgroundLayers,
+ m_numBackgroundLayers );
+ }
+
+ // restore all button icon visibilities
+ this->RestoreButtons();
+
+ // follow-through
+ //
+ }
+ case GUI_MSG_WINDOW_EXIT:
+ {
+ if( m_screenFX & SCREEN_FX_FADE )
+ {
+ if( m_numForegroundLayers > 0 || m_screenCover != NULL )
+ {
+ // reset fade elapsed time
+ m_elapsedFadeTime = 0;
+
+ // increment number of pending transitions
+ m_numTransitionsPending++;
+ }
+ }
+
+ if( m_screenFX & SCREEN_FX_ZOOM )
+ {
+ // reset zoom elapsed time
+ m_elapsedZoomTime = 0;
+
+ // increment number of pending transitions
+ m_numTransitionsPending++;
+ }
+
+ if( m_screenFX & SCREEN_FX_SLIDE_X ||
+ m_screenFX & SCREEN_FX_SLIDE_Y )
+ {
+ m_elapsedSlideTime = 0;
+ m_numTransitionsPending++;
+ }
+
+ if( m_screenFX & SCREEN_FX_IRIS )
+ {
+ rAssert( m_irisController != NULL );
+ m_irisController->Reset();
+ m_irisController->SetRelativeSpeed( DEFAULT_IRIS_WIPE_SPEED );
+ m_irisController->SetFrameRange( 0.0f, s_numIrisFrames );
+ m_irisController->SetFrame( 0.0f );
+
+ m_currentIrisState = IRIS_STATE_CLOSING;
+
+ m_numTransitionsPending++;
+ }
+ break;
+ }
+
+ case GUI_MSG_UPDATE:
+ {
+ #ifdef PRINTSCREENUPDATES
+ const char* screenName = GetWatcherName();
+ rDebugPrintf( "Screen - %s\n", screenName );
+ #endif
+
+ switch( m_state )
+ {
+ case GUI_WINDOW_STATE_INTRO:
+ {
+ // update screen fade in
+ //
+ if( (m_screenFX & SCREEN_FX_FADE) &&
+ m_elapsedFadeTime != -1 )
+ {
+ if( m_inverseFading )
+ {
+ this->FadeOut( static_cast<float>( param1 ) );
+ }
+ else
+ {
+ this->FadeIn( static_cast<float>( param1 ) );
+ }
+ }
+
+ // update screen zoom in
+ //
+ if( (m_screenFX & SCREEN_FX_ZOOM) &&
+ m_elapsedZoomTime != -1 )
+ {
+ this->ZoomIn( static_cast<float>( param1 ) );
+ }
+
+ // update screen slide in
+ //
+ if( ((m_screenFX & SCREEN_FX_SLIDE_X) || (m_screenFX & SCREEN_FX_SLIDE_Y)) &&
+ m_elapsedSlideTime != -1 )
+ {
+ this->SlideIn( static_cast<float>( param1 ) );
+ }
+
+ // update transition in animation
+ //
+ if( m_p3dObject != NULL )
+ {
+ tMultiController* controller = m_p3dObject->GetMultiController();
+ if( controller != NULL )
+ {
+ if( controller->GetFrame() >= controller->GetNumFrames() )
+ {
+ // nullify multicontroller
+ m_p3dObject->SetMultiController( NULL );
+
+ // decrement number of pending transitions
+ m_numTransitionsPending--;
+ }
+ }
+ }
+
+ // update iris wipe
+ //
+ if( m_screenFX & SCREEN_FX_IRIS )
+ {
+ rAssert( m_irisController != NULL );
+ if( m_currentIrisState == IRIS_STATE_CLOSING )
+ {
+ if( m_irisController->GetFrame() > m_irisController->GetNumFrames() / 2 )
+ {
+ this->OnIrisWipeClosed();
+
+ this->SetAlphaForLayers( 1.0f,
+ m_foregroundLayers,
+ m_numForegroundLayers );
+
+ this->SetAlphaForLayers( 1.0f,
+ m_backgroundLayers,
+ m_numBackgroundLayers );
+ }
+ }
+ else if( m_currentIrisState == IRIS_STATE_OPENING )
+ {
+ if( m_irisController->LastFrameReached() )
+ {
+ m_numTransitionsPending--;
+
+ m_currentIrisState = IRIS_STATE_IDLE;
+ }
+ }
+ }
+
+ // get p3d multicontroller upon first time entering screen
+ // and store reference to it
+ //
+ if( m_firstTimeEntered )
+ {
+ if( m_p3dObject != NULL &&
+ s_p3dMultiController == NULL )
+ {
+ s_p3dMultiController = m_p3dObject->GetMultiController();
+ if( s_p3dMultiController != NULL )
+ {
+ s_p3dMultiController->AddRef();
+
+ m_p3dObject->SetMultiController( NULL );
+ }
+ }
+ }
+
+ break;
+ }
+ case GUI_WINDOW_STATE_OUTRO:
+ {
+ // update screen fade out
+ //
+ if( (m_screenFX & SCREEN_FX_FADE) &&
+ m_elapsedFadeTime != -1 )
+ {
+ if( m_inverseFading )
+ {
+ this->FadeIn( static_cast<float>( param1 ) );
+ }
+ else
+ {
+ this->FadeOut( static_cast<float>( param1 ) );
+ }
+ }
+
+ // update screen zoom out
+ //
+ if( (m_screenFX & SCREEN_FX_ZOOM) &&
+ m_elapsedZoomTime != -1 )
+ {
+ this->ZoomOut( static_cast<float>( param1 ) );
+ }
+
+ // update screen slide out
+ //
+ if( ((m_screenFX & SCREEN_FX_SLIDE_X) || (m_screenFX & SCREEN_FX_SLIDE_Y)) &&
+ m_elapsedSlideTime != -1 )
+ {
+ this->SlideOut( static_cast<float>( param1 ) );
+ }
+
+ // update transition out animation
+ //
+ if( m_p3dObject != NULL )
+ {
+ if( m_playTransitionAnimationLast )
+ {
+ if( m_numTransitionsPending == 1 )
+ {
+ s_p3dMultiController->Reset();
+ m_p3dObject->SetMultiController( s_p3dMultiController );
+ m_playTransitionAnimationLast = false;
+ }
+ }
+ else
+ {
+ tMultiController* controller = m_p3dObject->GetMultiController();
+ if( controller != NULL )
+ {
+ if( controller->GetFrame() >= controller->GetNumFrames() )
+ {
+ // nullify multicontroller
+ m_p3dObject->SetMultiController( NULL );
+
+ // decrement number of pending transitions
+ m_numTransitionsPending--;
+ }
+ }
+ }
+ }
+
+ // update iris wipe
+ //
+ if( m_screenFX & SCREEN_FX_IRIS )
+ {
+ rAssert( m_irisController != NULL );
+ if( m_currentIrisState == IRIS_STATE_CLOSING )
+ {
+ if( m_irisController->GetFrame() > m_irisController->GetNumFrames() / 2 )
+ {
+ this->OnIrisWipeClosed();
+
+ this->SetAlphaForLayers( 0.0f,
+ m_foregroundLayers,
+ m_numForegroundLayers );
+
+ this->SetAlphaForLayers( 0.0f,
+ m_backgroundLayers,
+ m_numBackgroundLayers );
+
+ m_currentIrisState = IRIS_STATE_CLOSED;
+ }
+ }
+ else if( m_currentIrisState == IRIS_STATE_OPENING )
+ {
+ if( m_irisController->LastFrameReached() )
+ {
+ m_numTransitionsPending--;
+
+ m_currentIrisState = IRIS_STATE_IDLE;
+ }
+ }
+ }
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ if( m_guiManager->GetPreviousScreen() != GUI_WINDOW_ID_UNDEFINED )
+ {
+ m_pParent->HandleMessage( GUI_MSG_BACK_SCREEN );
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_BACK );
+ }
+ }
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ };
+
+ // Pass the message up the heirarchy.
+ //
+ CGuiWindow::HandleMessage( message, param1, param2 );
+}
+
+void
+CGuiScreen::SetFadingEnabled( bool enable )
+{
+ if( enable )
+ {
+ m_screenFX |= SCREEN_FX_FADE;
+ }
+ else
+ {
+ m_screenFX &= ~SCREEN_FX_FADE;
+
+ this->SetAlphaForLayers( 1.0f,
+ m_foregroundLayers,
+ m_numForegroundLayers );
+
+ if( m_screenCover != NULL )
+ {
+ m_screenCover->SetVisible( false );
+ }
+ }
+}
+
+void
+CGuiScreen::SetZoomingEnabled( bool enable )
+{
+ if( enable )
+ {
+ m_screenFX |= SCREEN_FX_ZOOM;
+ }
+ else
+ {
+ m_screenFX &= ~SCREEN_FX_ZOOM;
+
+ m_pScroobyScreen->SetScale( 1.0f );
+
+ this->SetAlphaForLayers( 1.0f,
+ m_backgroundLayers,
+ m_numBackgroundLayers );
+ }
+}
+
+void
+CGuiScreen::SetSlidingEnabled( eScreenEffect slideType, bool enable )
+{
+ rAssert( slideType == SCREEN_FX_SLIDE_X ||
+ slideType == SCREEN_FX_SLIDE_Y );
+
+ rAssertMsg( !this->IsWideScreenDisplay() || slideType != SCREEN_FX_SLIDE_X,
+ "Horizontal screen sliding currently not supported for widescreen display!" );
+
+ if( enable )
+ {
+ m_screenFX |= slideType;
+ }
+ else
+ {
+ m_screenFX &= ~slideType;
+ }
+}
+
+void
+CGuiScreen::SetIrisWipeEnabled( bool enable, bool autoOpenIris )
+{
+ if( enable )
+ {
+ m_screenFX |= SCREEN_FX_IRIS;
+
+ m_autoOpenIris = autoOpenIris;
+ }
+ else
+ {
+ m_screenFX &= ~SCREEN_FX_IRIS;
+ }
+}
+
+bool
+CGuiScreen::IsEffectEnabled( eScreenEffect effect ) const
+{
+ return ((m_screenFX & effect) > 0);
+}
+
+void
+CGuiScreen::Reset3dFEMultiController()
+{
+ if( s_p3dMultiController != NULL )
+ {
+ s_p3dMultiController->Release();
+ s_p3dMultiController = NULL;
+ }
+}
+
+void
+CGuiScreen::SetButtonVisible( eButtonIcon button, bool isVisible )
+{
+ if( m_buttonIcons[ button ] != NULL )
+ {
+ m_buttonIcons[ button ]->SetVisible( isVisible );
+ }
+}
+
+bool
+CGuiScreen::IsButtonVisible( eButtonIcon button ) const
+{
+ if( m_buttonIcons[ button ] != NULL )
+ {
+ return m_buttonIcons[ button ]->IsVisible();
+ }
+
+ return false;
+}
+
+void
+CGuiScreen::StartTransitionAnimation( int startFrame,
+ int endFrame,
+ bool lastTransition )
+{
+ if( m_ignoreControllerInputs )
+ {
+ return;
+ }
+
+ if( m_p3dObject != NULL )
+ {
+ rAssert( s_p3dMultiController != NULL );
+
+ // if start and end frames are specified, set them for multicontroller
+ if( startFrame > -1 && endFrame > -1 )
+ {
+ s_p3dMultiController->SetFrameRange( (float)startFrame, (float)endFrame );
+ }
+
+ m_playTransitionAnimationLast = lastTransition;
+ if( !m_playTransitionAnimationLast )
+ {
+ // set multicontroller for p3dobject
+ s_p3dMultiController->Reset();
+ m_p3dObject->SetMultiController( s_p3dMultiController );
+ }
+
+ // increment number of pending transitions
+ m_numTransitionsPending++;
+ }
+}
+
+void
+CGuiScreen::ReloadScreen()
+{
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ this->m_ID,
+ KEEP_WINDOW_HISTORY );
+}
+
+void
+CGuiScreen::RestoreScreenCover()
+{
+ // Get screen cover, if Cover page included
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "COVER" );
+ if( pPage != NULL )
+ {
+ m_screenCover = pPage->GetLayerByIndex( 0 );
+ rAssert( m_screenCover != NULL );
+
+ m_screenCover->SetVisible( true );
+ m_screenCover->SetAlpha( 1.0f );
+ }
+}
+
+void
+CGuiScreen::RestoreButtons()
+{
+ for( int i = 0; i < NUM_BUTTON_ICONS; i++ )
+ {
+ if( m_buttonIcons[ i ] != NULL )
+ {
+ m_buttonIcons[ i ]->SetVisible( true );
+ }
+ }
+}
+
+bool
+CGuiScreen::IsWideScreenDisplay()
+{
+#ifdef RAD_XBOX
+ return p3d::display->IsWidescreen();
+#else
+ return false;
+#endif
+}
+
+void
+CGuiScreen::ApplyWideScreenCorrectionScale( Scrooby::Drawable* drawable )
+{
+ // Assumes drawable is currently screen-centered!
+ //
+ int screenWidthBy2 = static_cast<int>( Scrooby::App::GetInstance()->GetScreenWidth() ) / 2;
+
+ rAssert( drawable != NULL );
+ drawable->Translate( -screenWidthBy2, 0 );
+ drawable->Scale( WIDE_SCREEN_CORRECTION_SCALE, 1.0f, 1.0f );
+ drawable->Translate( screenWidthBy2, 0 );
+}
+
+#ifdef RAD_WIN32
+//===========================================================================
+// CGuiScreen::CheckCursorAgainstHotspots
+//===========================================================================
+// Description: Checks cursor position against its list of hotspots.
+//
+// Constraints: None.
+//
+// Parameters: float x - The x position of cursor in P3D coordinates.
+// float y - The y position of cursor in P3D coordinates.
+//
+// Return: N/A.
+//
+//===========================================================================
+eFEHotspotType CGuiScreen::CheckCursorAgainstHotspots( float x, float y )
+{
+ eFEHotspotType hotSpotType = HOTSPOT_NONE;
+ CGuiMenu* pCurrentMenu = HasMenu();
+ int numMenuItems = 0;
+ GuiMenuItem* pMenuItem = NULL;
+
+
+ if( pCurrentMenu )
+ {
+ numMenuItems = pCurrentMenu->GetNumItems();
+
+ for( int i = 0; i < numMenuItems; i++ )
+ {
+ bool bIsMenuItemEnabled = pCurrentMenu->IsMenuItemEnabled(i);
+ pMenuItem = pCurrentMenu->GetMenuItem( i );
+
+ if( pMenuItem && bIsMenuItemEnabled )
+ {
+ if( pMenuItem->GetItem()->IsVisible() )
+ {
+ // Just tests if the point is in the bounding rect of the sprite.
+ if( pMenuItem->GetItem()->IsPointInBoundingRect( x, y ) )
+ {
+ //rDebugPrintf( "Cursor is inside Sprite %d rectangle!\n", i );
+ pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_OVER, i );
+ hotSpotType = HOTSPOT_BUTTON;
+
+ //After taking care of all the events for this menu item, just bail out.
+ break;
+ }
+ else if( pCurrentMenu->GetSelection() == i )
+ {
+ if( pMenuItem->m_itemValueArrowL )
+ {
+ if( pMenuItem->m_itemValueArrowL->IsPointInBoundingRect( x, y ) )
+ {
+ hotSpotType = HOTSPOT_ARROWLEFT;
+ break;
+ }
+ }
+
+ if( pMenuItem->m_itemValueArrowR )
+ {
+ if( pMenuItem->m_itemValueArrowR->IsPointInBoundingRect( x, y ) )
+ {
+ hotSpotType = HOTSPOT_ARROWRIGHT;
+ break;
+ }
+ }
+
+ if( pMenuItem->m_slider.m_pImage )
+ {
+ if( pMenuItem->m_slider.m_pImage->IsPointInBoundingRect( x, y ) )
+ {
+ hotSpotType = HOTSPOT_SLIDER;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return hotSpotType;
+}
+#endif
+
+//===========================================================================
+// Protected Member Functions
+//===========================================================================
+
+void
+CGuiScreen::RestoreDefaultFadeTime()
+{
+ m_fadeTime = DEFAULT_SCREEN_FADE_TIME;
+}
+
+void
+CGuiScreen::RestoreDefaultZoomTime()
+{
+ m_zoomTime = DEFAULT_SCREEN_ZOOM_TIME;
+}
+
+void
+CGuiScreen::IrisWipeOpen()
+{
+ rAssert( !m_autoOpenIris );
+
+ rAssert( m_irisController != NULL );
+ m_irisController->SetRelativeSpeed( DEFAULT_IRIS_WIPE_SPEED );
+
+ m_currentIrisState = IRIS_STATE_OPENING;
+}
+
+void CGuiScreen::SetAlphaForLayers( float alpha, Scrooby::Layer** layers, int numLayers )
+{
+ rAssert( layers );
+ rAssert( numLayers >= 0 );
+
+ for( int i = 0; i < numLayers; i++ )
+ {
+ layers[ i ]->SetAlpha( alpha );
+ }
+}
+
+void
+CGuiScreen::AutoScaleFrame( Scrooby::Page* pPage )
+{
+ if( pPage != NULL )
+ {
+ Scrooby::Group* pFrameGroup = pPage->GetGroup( "Frame" );
+ if( pFrameGroup != NULL )
+ {
+ const int SEGMENT_LENGTH = 40; // in pixels
+
+ Scrooby::Sprite* pFrameTop = pFrameGroup->GetSprite( "Frame_Top" );
+ rAssert( pFrameTop != NULL );
+ Scrooby::Sprite* pFrameBottom = pFrameGroup->GetSprite( "Frame_Bottom" );
+ rAssert( pFrameBottom != NULL );
+ Scrooby::Sprite* pFrameLeft = pFrameGroup->GetSprite( "Frame_Left" );
+ rAssert( pFrameLeft != NULL );
+ Scrooby::Sprite* pFrameRight = pFrameGroup->GetSprite( "Frame_Right" );
+ rAssert( pFrameRight != NULL );
+
+ int width = 0;
+ int height = 0;
+
+ // scale top and bottom frames horizontally
+ //
+ pFrameTop->GetBoundingBoxSize( width, height );
+ float scaleX = (width - SEGMENT_LENGTH) / (float)SEGMENT_LENGTH;
+
+ pFrameTop->ResetTransformation();
+ pFrameTop->ScaleAboutCenter( scaleX, 1.0f, 1.0f );
+ pFrameTop->Translate( +5, 0 );
+
+ pFrameBottom->ResetTransformation();
+ pFrameBottom->ScaleAboutCenter( scaleX, 1.0f, 1.0f );
+ pFrameBottom->Translate( +5, 0 );
+
+ // scale left and right frames vertically
+ //
+ pFrameLeft->GetBoundingBoxSize( width, height );
+ float scaleY = (height - SEGMENT_LENGTH) / (float)SEGMENT_LENGTH;
+
+ pFrameLeft->ResetTransformation();
+ pFrameLeft->ScaleAboutCenter( 1.0f, scaleY, 1.0f );
+
+ pFrameRight->ResetTransformation();
+ pFrameRight->ScaleAboutCenter( 1.0f, scaleY, 1.0f );
+
+#ifdef PAL
+ if( pPage == m_pScroobyScreen->GetPage( "BigBoard" ) )
+ {
+ pFrameGroup->ResetTransformation();
+ pFrameGroup->ScaleAboutCenter( 1.035f, 1.0f, 1.0f );
+ }
+#endif // PAL
+ }
+ }
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+void
+CGuiScreen::FadeIn( float elapsedTime )
+{
+ float alpha = (m_elapsedFadeTime + elapsedTime) / m_fadeTime;
+
+ if( alpha < 1.0f )
+ {
+ // for non-linear fading
+ //
+ alpha *= alpha;
+
+ this->SetAlphaForLayers( alpha,
+ m_foregroundLayers,
+ m_numForegroundLayers );
+
+ if( m_screenCover != NULL )
+ {
+ m_screenCover->SetVisible( true );
+ m_screenCover->SetAlpha( 1.0f - alpha );
+ }
+ }
+ else
+ {
+ this->SetAlphaForLayers( 1.0f,
+ m_foregroundLayers,
+ m_numForegroundLayers );
+
+ if( m_screenCover != NULL )
+ {
+ m_screenCover->SetAlpha( 0.0f );
+ }
+ }
+
+ if( m_elapsedFadeTime < m_fadeTime )
+ {
+ m_elapsedFadeTime += elapsedTime;
+ }
+ else
+ {
+ m_elapsedFadeTime = -1;
+
+ // decrement number of pending transitions
+ m_numTransitionsPending--;
+ }
+}
+
+void
+CGuiScreen::FadeOut( float elapsedTime )
+{
+ float alpha = 1.0f - (m_elapsedFadeTime + elapsedTime) / m_fadeTime;
+
+ if( alpha > 0.0f )
+ {
+ // for non-linear fading
+ //
+ alpha *= alpha;
+
+ this->SetAlphaForLayers( alpha,
+ m_foregroundLayers,
+ m_numForegroundLayers );
+
+ if( m_screenCover != NULL )
+ {
+ m_screenCover->SetVisible( true );
+ m_screenCover->SetAlpha( 1.0f - alpha );
+ }
+ }
+ else
+ {
+ this->SetAlphaForLayers( 0.0f,
+ m_foregroundLayers,
+ m_numForegroundLayers );
+
+ if( m_screenCover != NULL )
+ {
+ m_screenCover->SetAlpha( 1.0f );
+ }
+ }
+
+ if( m_elapsedFadeTime < m_fadeTime )
+ {
+ m_elapsedFadeTime += elapsedTime;
+ }
+ else
+ {
+ m_elapsedFadeTime = -1;
+
+ // decrement number of pending transitions
+ m_numTransitionsPending--;
+ }
+}
+
+void
+CGuiScreen::ZoomIn( float elapsedTime )
+{
+ rAssert( m_pScroobyScreen );
+
+ float zoomValue = (m_elapsedZoomTime + elapsedTime) / m_zoomTime;
+ rAssert( zoomValue >= 0.0f );
+
+ if( zoomValue < 1.0f )
+ {
+ // scale screen
+ m_pScroobyScreen->SetScale( zoomValue );
+
+ // apply alpha to background layers
+ this->SetAlphaForLayers( zoomValue,
+ m_backgroundLayers,
+ m_numBackgroundLayers );
+ }
+ else
+ {
+ m_pScroobyScreen->SetScale( 1.0f );
+
+ this->SetAlphaForLayers( 1.0f,
+ m_backgroundLayers,
+ m_numBackgroundLayers );
+ }
+
+ if( m_elapsedZoomTime < m_zoomTime )
+ {
+ m_elapsedZoomTime += elapsedTime;
+ }
+ else
+ {
+ m_elapsedZoomTime = -1;
+
+ // decrement number of pending transitions
+ m_numTransitionsPending--;
+ }
+}
+
+void
+CGuiScreen::ZoomOut( float elapsedTime )
+{
+ rAssert( m_pScroobyScreen );
+
+ float zoomValue = 1.0f - (m_elapsedZoomTime + elapsedTime) / m_zoomTime;
+ rAssert( zoomValue <= 1.0f );
+
+ if( zoomValue > 0.0f )
+ {
+ // scale screen
+ m_pScroobyScreen->SetScale( zoomValue );
+
+ // apply alpha to background layers
+ this->SetAlphaForLayers( zoomValue,
+ m_backgroundLayers,
+ m_numBackgroundLayers );
+ }
+ else
+ {
+ m_pScroobyScreen->SetScale( 0.0f );
+
+ this->SetAlphaForLayers( 0.0f,
+ m_backgroundLayers,
+ m_numBackgroundLayers );
+ }
+
+ if( m_elapsedZoomTime < m_zoomTime )
+ {
+ m_elapsedZoomTime += elapsedTime;
+ }
+ else
+ {
+ m_elapsedZoomTime = -1;
+
+ // decrement number of pending transitions
+ m_numTransitionsPending--;
+ }
+}
+
+void
+CGuiScreen::SlideIn( float elapsedTime )
+{
+ bool done = false;
+
+ for( int i = 0; i < m_numForegroundLayers; i++ )
+ {
+ rAssert( m_foregroundLayers[ i ] );
+
+ if( m_screenFX & SCREEN_FX_SLIDE_X )
+ {
+ done = GuiSFX::SlideX( m_foregroundLayers[ i ],
+ m_elapsedSlideTime,
+ m_slideTime,
+ true,
+ GuiSFX::SLIDE_BORDER_RIGHT );
+ }
+
+ if( m_screenFX & SCREEN_FX_SLIDE_Y )
+ {
+ done = GuiSFX::SlideY( m_foregroundLayers[ i ],
+ m_elapsedSlideTime,
+ m_slideTime,
+ true,
+ GuiSFX::SLIDE_BORDER_TOP );
+
+ if( this->IsWideScreenDisplay() )
+ {
+ this->ApplyWideScreenCorrectionScale( m_foregroundLayers[ i ] );
+ }
+ }
+ }
+
+ if( done )
+ {
+ m_elapsedSlideTime = -1;
+ m_numTransitionsPending--;
+ }
+ else
+ {
+ m_elapsedSlideTime += elapsedTime;
+ }
+}
+
+void
+CGuiScreen::SlideOut( float elapsedTime )
+{
+ bool done = false;
+
+ for( int i = 0; i < m_numForegroundLayers; i++ )
+ {
+ rAssert( m_foregroundLayers[ i ] );
+
+ if( m_screenFX & SCREEN_FX_SLIDE_X )
+ {
+ done = GuiSFX::SlideX( m_foregroundLayers[ i ],
+ m_elapsedSlideTime,
+ m_slideTime,
+ false,
+ GuiSFX::SLIDE_BORDER_LEFT );
+ }
+
+ if( m_screenFX & SCREEN_FX_SLIDE_Y )
+ {
+ done = GuiSFX::SlideY( m_foregroundLayers[ i ],
+ m_elapsedSlideTime,
+ m_slideTime,
+ false,
+ GuiSFX::SLIDE_BORDER_TOP );
+
+ if( this->IsWideScreenDisplay() )
+ {
+ this->ApplyWideScreenCorrectionScale( m_foregroundLayers[ i ] );
+ }
+ }
+ }
+
+ if( done )
+ {
+ m_elapsedSlideTime = -1;
+ m_numTransitionsPending--;
+ }
+ else
+ {
+ m_elapsedSlideTime += elapsedTime;
+ }
+}
+
+void
+CGuiScreen::OnIrisWipeClosed()
+{
+ if( m_autoOpenIris )
+ {
+ this->IrisWipeOpen();
+ }
+ else
+ {
+ rAssert( m_irisController != NULL );
+ m_irisController->SetRelativeSpeed( 0.0f );
+
+ m_currentIrisState = IRIS_STATE_CLOSED;
+ }
+}
+
+#ifdef DEBUGWATCH
+const char* CGuiScreen::GetWatcherName() const
+{
+ return "No Name";
+}
+#endif \ No newline at end of file
diff --git a/game/code/presentation/gui/guiscreen.h b/game/code/presentation/gui/guiscreen.h
new file mode 100644
index 0000000..3d82cd2
--- /dev/null
+++ b/game/code/presentation/gui/guiscreen.h
@@ -0,0 +1,234 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreen
+//
+// Description:
+//
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/20 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREEN_H
+#define GUISCREEN_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiwindow.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiManager;
+class tMultiController;
+
+namespace Scrooby
+{
+ class Screen;
+ class Page;
+ class Layer;
+ class Group;
+ class Drawable;
+ class Sprite;
+ class Text;
+ class Polygon;
+ class Pure3dObject;
+}
+
+//class tCameraAnimationController;
+
+const int MAX_FOREGROUND_LAYERS = 8;
+const int MAX_BACKGROUND_LAYERS = 2;
+
+enum eScreenEffect
+{
+ SCREEN_FX_ALL = ~0,
+ SCREEN_FX_NONE = 0,
+
+ SCREEN_FX_FADE = 1,
+ SCREEN_FX_ZOOM = 2,
+ SCREEN_FX_SLIDE_X = 4,
+ SCREEN_FX_SLIDE_Y = 8,
+ SCREEN_FX_IRIS = 16,
+
+ NUM_SCREEN_FX
+};
+
+enum eButtonIcon
+{
+ BUTTON_ICON_ACCEPT,
+ BUTTON_ICON_BACK,
+
+ NUM_BUTTON_ICONS
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreen : public CGuiWindow
+{
+ public:
+
+ CGuiScreen( Scrooby::Screen* pScroobyScreen,
+ CGuiEntity* pParent,
+ eGuiWindowID id,
+ unsigned int screenFX = SCREEN_FX_FADE );
+
+ virtual ~CGuiScreen();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ void SetScroobyScreen( Scrooby::Screen* pScreen ) { m_pScroobyScreen = pScreen; }
+ Scrooby::Screen* GetScroobyScreen() const { return m_pScroobyScreen; }
+
+ virtual CGuiMenu* HasMenu() { return NULL; }
+
+ void SetFadingEnabled( bool enable );
+ void SetZoomingEnabled( bool enable );
+ void SetSlidingEnabled( eScreenEffect slideType, bool enable );
+ void SetIrisWipeEnabled( bool enable, bool autoOpenIris = false );
+
+ bool IsEffectEnabled( eScreenEffect effect ) const;
+
+ static void Reset3dFEMultiController();
+
+ void SetIngoreControllerInputs( bool ignore ) { m_ignoreControllerInputs = ignore; }
+ bool IsIgnoringControllerInputs() const { return m_ignoreControllerInputs; }
+
+ void SetButtonVisible( eButtonIcon button, bool isVisible );
+ bool IsButtonVisible( eButtonIcon button ) const;
+
+ void StartTransitionAnimation( int startFrame = -1,
+ int endFrame = -1,
+ bool lastTransition = true );
+
+ void ReloadScreen();
+ void RestoreScreenCover();
+ void RestoreButtons();
+
+ static bool IsWideScreenDisplay();
+ static void ApplyWideScreenCorrectionScale( Scrooby::Drawable* drawable );
+
+#ifdef RAD_WIN32
+ virtual eFEHotspotType CheckCursorAgainstHotspots( float x, float y );
+#endif
+
+ protected:
+
+ //---------------------------------------------------------------------
+ // Protected Functions
+ //---------------------------------------------------------------------
+
+ void SetFadeTime( float fadeTime ) { m_fadeTime = fadeTime; }
+ void RestoreDefaultFadeTime();
+
+ void SetZoomTime( float zoomTime ) { m_zoomTime = zoomTime; }
+ void RestoreDefaultZoomTime();
+
+ void IrisWipeOpen();
+
+ void SetAlphaForLayers( float alpha,
+ Scrooby::Layer** layers,
+ int numLayers );
+
+ void AutoScaleFrame( Scrooby::Page* pPage );
+ #ifdef DEBUGWATCH
+ virtual const char* GetWatcherName() const;
+ #endif
+
+ //---------------------------------------------------------------------
+ // Protected Data
+ //---------------------------------------------------------------------
+
+ CGuiManager* m_guiManager;
+
+ Scrooby::Screen* m_pScroobyScreen;
+ Scrooby::Layer* m_screenCover;
+ Scrooby::Pure3dObject* m_p3dObject;
+
+ static tMultiController* s_p3dMultiController;
+
+ enum eIrisState
+ {
+ IRIS_STATE_IDLE,
+ IRIS_STATE_CLOSING,
+ IRIS_STATE_CLOSED,
+ IRIS_STATE_OPENING,
+
+ NUM_IRIS_STATES
+ };
+
+ Scrooby::Pure3dObject* m_p3dIris;
+ tMultiController* m_irisController;
+ eIrisState m_currentIrisState;
+ bool m_autoOpenIris : 1;
+
+ Scrooby::Layer* m_foregroundLayers[ MAX_FOREGROUND_LAYERS ];
+ int m_numForegroundLayers;
+
+ Scrooby::Layer* m_backgroundLayers[ MAX_BACKGROUND_LAYERS ];
+ int m_numBackgroundLayers;
+
+ Scrooby::Group* m_buttonIcons[ NUM_BUTTON_ICONS ];
+
+ bool m_ignoreControllerInputs : 1;
+ bool m_inverseFading : 1;
+
+ private:
+
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or asignment. Declare but don't define.
+ //
+ CGuiScreen( const CGuiScreen& );
+ CGuiScreen& operator= ( const CGuiScreen& );
+
+ // Screen Fade In/Out Effects
+ void FadeIn( float elapsedTime );
+ void FadeOut( float elapsedTime );
+
+ // Screen Zoom In/Out Effects
+ void ZoomIn( float elapsedTime );
+ void ZoomOut( float elapsedTime );
+
+ // Screen Slide In/Out Effects
+ void SlideIn( float elapsedTime );
+ void SlideOut( float elapsedTime );
+
+ // Iris Wipe Closed
+ //
+ void OnIrisWipeClosed();
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ static float s_numIrisFrames;
+
+ unsigned int m_screenFX; // bit mask for screen effects
+
+ float m_fadeTime;
+ float m_elapsedFadeTime;
+
+ float m_zoomTime;
+ float m_elapsedZoomTime;
+
+ float m_slideTime;
+ float m_elapsedSlideTime;
+
+ bool m_playTransitionAnimationLast : 1;
+
+};
+
+#endif // GUISCREEN_H
diff --git a/game/code/presentation/gui/guiscreenmemcardcheck.cpp b/game/code/presentation/gui/guiscreenmemcardcheck.cpp
new file mode 100644
index 0000000..71ccab6
--- /dev/null
+++ b/game/code/presentation/gui/guiscreenmemcardcheck.cpp
@@ -0,0 +1,518 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMemCardCheck
+//
+// Description: Implementation of the CGuiScreenMemCardCheck class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/guiscreenmemcardcheck.h>
+#include <presentation/gui/guiscreenmemorycard.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/frontend/guiscreenloadgame.h>
+#include <presentation/gui/guiscreenprompt.h>
+
+#include <main/game.h>
+#include <main/platform.h>
+#include <memory/createheap.h>
+
+#include <raddebug.hpp> // Foundation
+#include <layer.h>
+#include <page.h>
+#include <screen.h>
+#include <text.h>
+
+#ifdef RAD_GAMECUBE
+#include <main/gamecube_extras/gcmanager.h>
+#endif
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+#ifndef RAD_E3
+ // This will enable auto-loading the most recent saved game immediately
+ // after the memory card check.
+ //
+ #define ENABLE_AUTOLOAD_AFTER_MEMCHECK
+#endif
+
+enum eMemCardCheckMessage
+{
+ CHECKING_MEMCARDS_GC,
+ CHECKING_MEMCARD_B_GC,
+ CHECKING_MEMCARDS_PS2,
+ CHECKING_MEMCARD_2_PS2,
+ CHECKING_HARDDISK_XBOX,
+ CHECKING_HARDDISK_PC,
+
+ NUM_MEMCARD_CHECK_MESSAGES
+};
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMemCardCheck::CGuiScreenMemCardCheck
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMemCardCheck::CGuiScreenMemCardCheck
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_MEMORY_CARD_CHECK ),
+ m_messageText( NULL ),
+ m_minimumFormatTime(1000),
+ m_formatState(false),
+ m_StatusPromptShown(false),
+ m_isAutoLoadPending( false )
+{
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "MemCardCheck" );
+ rAssert( pPage );
+
+ Scrooby::Layer* foreground = pPage->GetLayer( "Foreground" );
+
+ m_messageText = foreground->GetText( "CheckingMemoryCards" );
+ rAssert( m_messageText );
+ m_messageText->SetTextMode( Scrooby::TEXT_WRAP );
+
+#ifdef RAD_GAMECUBE
+ m_messageText->SetIndex( CHECKING_MEMCARDS_GC );
+#endif
+
+#ifdef RAD_PS2
+ m_messageText->SetIndex( CHECKING_MEMCARDS_PS2 );
+
+ #ifndef PAL
+ // no need to display checking message on PS2
+ //
+ m_messageText->SetVisible( false );
+ this->SetFadingEnabled( false );
+ #endif
+#endif
+
+#ifdef RAD_XBOX
+ m_messageText->SetIndex( CHECKING_HARDDISK_XBOX );
+#endif
+
+#ifdef RAD_WIN32
+ m_messageText->SetIndex( CHECKING_HARDDISK_PC );
+ // no need to display checking message on WIN32
+ m_messageText->SetVisible( false );
+ this->SetFadingEnabled( false );
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenMemCardCheck::~CGuiScreenMemCardCheck
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMemCardCheck::~CGuiScreenMemCardCheck()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenMemCardCheck::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMemCardCheck::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if (message == GUI_MSG_MESSAGE_UPDATE)
+ {
+ if (m_formatState)
+ {
+ m_elapsedFormatTime += param1;
+
+ if (m_elapsedFormatTime > m_minimumFormatTime && m_formatDone)
+ {
+ m_StatusPromptShown = true;
+ if( m_formatResult == Success )
+ {
+ m_guiManager->DisplayPrompt(PROMPT_FORMAT_SUCCESS_GC + PLATFORM_TEXT_INDEX, this, PROMPT_TYPE_CONTINUE); // format success
+ m_formatState = false;
+ }
+ else if (m_formatResult)
+ {
+ m_guiManager->DisplayPrompt(PROMPT_FORMAT_FAIL_GC + PLATFORM_TEXT_INDEX, this, PROMPT_TYPE_CONTINUE); // format fail
+ m_formatState = false;
+ }
+ }
+ }
+ }
+ else if (message == GUI_MSG_PROMPT_UPDATE)
+ {
+#if defined( RAD_GAMECUBE ) || defined( RAD_PS2 )
+ // detect memcard unplugged in prompt
+ // update so status up to date
+ GetMemoryCardManager()->Update( param1 );
+ if (m_StatusPromptShown==false) { // check for user unplugging memcard if not showing status
+
+ if( !GetMemoryCardManager()->IsCurrentDrivePresent(CGuiScreenMemoryCard::s_currentMemoryCardSlot) )
+ ReloadScreen();
+ }
+#endif
+ }
+ else if ( message == GUI_MSG_MENU_PROMPT_RESPONSE || message == GUI_MSG_ERROR_PROMPT_RESPONSE)
+ {
+ switch ( param1 )
+ {
+ case PROMPT_FORMAT_SUCCESS_GC:
+ case PROMPT_FORMAT_SUCCESS_PS2:
+ case PROMPT_FORMAT_SUCCESS_XBOX: // format ok
+ this->OnContinue();
+ break;
+
+ case PROMPT_FORMAT_FAIL_GC:
+ case PROMPT_FORMAT_FAIL_PS2:
+ case PROMPT_FORMAT_FAIL_XBOX: // format fail
+ this->ReloadScreen(); // user has to retry
+
+ GetMemoryCardManager()->ClearCurrentDrive();
+ break;
+ case PROMPT_FORMAT_CONFIRM2_GC:
+ case PROMPT_FORMAT_CONFIRM2_PS2:
+ case PROMPT_FORMAT_CONFIRM2_XBOX: // really format
+ if (param2==CGuiMenuPrompt::RESPONSE_YES)
+ {
+ m_guiManager->DisplayMessage(CGuiScreenMessage::MSG_ID_FORMATTING_GC + PLATFORM_TEXT_INDEX, this);
+ m_formatState = true;
+ m_elapsedFormatTime = 0;
+ m_formatDone = false;
+ GetMemoryCardManager()->FormatDrive(CGuiScreenMemoryCard::s_currentMemoryCardSlot, this);
+ }
+ else
+ {
+ this->ReloadScreen(); // user has to retry
+ GetMemoryCardManager()->ClearCurrentDrive();
+ }
+ break;
+
+ default:
+ switch( param2 )
+ {
+ case (CGuiMenuPrompt::RESPONSE_YES):
+ case (CGuiMenuPrompt::RESPONSE_CONTINUE):
+ case (CGuiMenuPrompt::RESPONSE_CONTINUE_WITHOUT_SAVE):
+ {
+ /*
+ if( GetMemoryCardManager()->GetMemcardCheckingState() != MemoryCardManager::MEMCARD_CHECKING_DONE )
+ {
+ rAssert( m_messageText );
+ #ifdef RAD_GAMECUBE
+ m_messageText->SetIndex( CHECKING_MEMCARD_B_GC );
+ #endif
+ #ifdef RAD_PS2
+ m_messageText->SetIndex( CHECKING_MEMCARD_2_PS2 );
+ #endif
+ this->ReloadScreen();
+ }
+ else
+ */
+ this->OnContinue();
+
+ break;
+ }
+ case (CGuiMenuPrompt::RESPONSE_NO):
+ case (CGuiMenuPrompt::RESPONSE_RETRY):
+ {
+ /*
+ GetMemoryCardManager()->ResetMemoryCardCheck();
+
+ rAssert( m_messageText );
+ #ifdef RAD_GAMECUBE
+ m_messageText->SetIndex( CHECKING_MEMCARDS_GC );
+ #endif
+ #ifdef RAD_PS2
+ m_messageText->SetIndex( CHECKING_MEMCARDS_PS2 );
+ #endif
+ */
+ this->ReloadScreen();
+
+ break;
+ }
+ case (CGuiMenuPrompt::RESPONSE_FORMAT_GC):
+ case (CGuiMenuPrompt::RESPONSE_FORMAT_XBOX):
+ case (CGuiMenuPrompt::RESPONSE_FORMAT_PS2):
+ {
+ m_StatusPromptShown = false;
+ m_guiManager->DisplayPrompt(PROMPT_FORMAT_CONFIRM2_GC + PLATFORM_TEXT_INDEX,this);
+ break;
+ }
+ case (CGuiMenuPrompt::RESPONSE_MANAGE_MEMCARDS):
+ {
+ #ifdef RAD_GAMECUBE
+ GCManager::GetInstance()->Reset();
+ GCManager::GetInstance()->PerformReset( true, true );
+ #else
+ IRadMemoryAllocator* allocator = GetAllocator( GMA_PERSISTENT );
+ IRadMemoryHeap* staticHeap = dynamic_cast< IRadMemoryHeap* >( allocator );
+ staticHeap->AllowFreeing( true );
+ GetGame()->GetPlatform()->LaunchDashboard();
+ #endif
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ GetMemoryCardManager()->UpdateMemoryCardCheck( param1 );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+void
+CGuiScreenMemCardCheck::OnFormatOperationComplete( radFileError errorCode )
+{
+ if (m_formatDone != true) // check if we already received an error or not
+ m_formatResult = errorCode;
+ m_formatDone = true;
+}
+
+void
+CGuiScreenMemCardCheck::OnMemoryCardCheckDone( radFileError errorCode,
+ IRadDrive::MediaInfo::MediaState mediaState,
+ int driveIndex,
+ int mostRecentSaveGameDriveIndex,
+ int mostRecentSaveGameSlot )
+{
+#ifdef RAD_WIN32
+ GetMemoryCardManager()->SetCurrentDrive( static_cast<unsigned int>( 0 ) );
+#endif
+
+#ifdef ENABLE_AUTOLOAD_AFTER_MEMCHECK
+ if( mostRecentSaveGameSlot != -1 )
+ {
+ CGuiScreenMemoryCard::s_currentMemoryCardSlot = mostRecentSaveGameDriveIndex;
+ GetMemoryCardManager()->SetCurrentDrive( static_cast<unsigned int>( mostRecentSaveGameDriveIndex ) );
+
+ CGuiScreenAutoLoad::SetGameSlot( mostRecentSaveGameSlot );
+
+ m_isAutoLoadPending = true;
+ }
+#endif
+
+ if( errorCode == Success && mediaState == IRadDrive::MediaInfo::MediaPresent )
+ {
+ this->OnContinue();
+ }
+ else
+ {
+ CGuiScreenMemoryCard::s_currentMemoryCardSlot = driveIndex;
+
+ m_StatusPromptShown = true;
+
+ int errorMessage = -1;
+ if( mediaState != IRadDrive::MediaInfo::MediaPresent )
+ {
+ errorMessage = GetErrorMessageIndex( mediaState );
+ }
+ else if( errorCode != Success )
+ {
+ errorMessage = GetErrorMessageIndex( errorCode, ERROR_DURING_CHECKING );
+ }
+
+ int format_response = 0;
+
+ // TC: (PS2 TRC) formatting should NOT be offered to the user during the
+ // boot-up check, but only during save contexts
+ //
+#ifndef RAD_PS2
+ if (mediaState==IRadDrive::MediaInfo::MediaNotFormatted)
+ format_response = ERROR_RESPONSE_FORMAT;
+#endif
+
+#ifdef RAD_GAMECUBE
+ if( mediaState == IRadDrive::MediaInfo::MediaEncodingErr )
+ {
+ format_response = ERROR_RESPONSE_FORMAT;
+ }
+#endif
+
+ rAssert( errorMessage != -1 );
+
+#ifdef RAD_GAMECUBE
+ int manage_response = ERROR_RESPONSE_MANAGE;
+
+ if (mediaState == IRadDrive::MediaInfo::MediaNotPresent
+ || mediaState == IRadDrive::MediaInfo::MediaInvalid
+ || mediaState == IRadDrive::MediaInfo::MediaDamaged)
+ manage_response = 0; // no manage when no memory card
+ if (format_response)
+ manage_response = 0; // don't show both format and manage
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ format_response | manage_response | ERROR_RESPONSE_CONTINUE_WITHOUT_SAVE | ERROR_RESPONSE_RETRY );
+#endif // RAD_GAMECUBE
+
+#ifdef RAD_PS2
+ // add retry if card full, or no card
+ if( errorCode == NoFreeSpace || mediaState == IRadDrive::MediaInfo::MediaNotPresent)
+ {
+ m_guiManager->DisplayErrorPrompt( errorMessage, this, ERROR_RESPONSE_CONTINUE_WITHOUT_SAVE | ERROR_RESPONSE_RETRY );
+ }
+ else
+ {
+ if (format_response)
+ { // detect unplugged if we are asking for format
+ m_StatusPromptShown = false;
+ }
+ m_guiManager->DisplayErrorPrompt( errorMessage, this, format_response | ERROR_RESPONSE_CONTINUE | ERROR_RESPONSE_RETRY);
+ }
+#endif // RAD_PS2
+
+#ifdef RAD_XBOX
+ m_guiManager->DisplayErrorPrompt( errorMessage, this, ERROR_RESPONSE_CONTINUE );
+#endif // RAD_XBOX
+
+#ifdef RAD_WIN32 //parallel the xbox for now.
+ m_guiManager->DisplayErrorPrompt( errorMessage, this, ERROR_RESPONSE_CONTINUE );
+#endif // RAD_WIN32
+ }
+}
+
+//===========================================================================
+// CGuiScreenMemCardCheck::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMemCardCheck::InitIntro()
+{
+ if( m_firstTimeEntered )
+ {
+ GetMemoryCardManager()->LoadMemcardInfo();
+ }
+ m_StatusPromptShown = false;
+}
+
+
+//===========================================================================
+// CGuiScreenMemCardCheck::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMemCardCheck::InitRunning()
+{
+ GetMemoryCardManager()->StartMemoryCardCheck( this );
+}
+
+
+//===========================================================================
+// CGuiScreenMemCardCheck::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMemCardCheck::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenMemCardCheck::OnContinue()
+{
+ GetMemoryCardManager()->UnloadMemcardInfo();
+
+#ifdef RAD_PS2
+ bool isCurrentDriveReady = true;
+#else
+ bool isCurrentDriveReady = GetMemoryCardManager()->IsCurrentDriveReady( true );
+#endif
+
+ if( m_isAutoLoadPending && isCurrentDriveReady )
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_AUTO_LOAD, CLEAR_WINDOW_HISTORY );
+ }
+ else
+ {
+ m_pParent->HandleMessage( GUI_MSG_MEMCARD_CHECK_COMPLETED );
+ }
+
+ m_isAutoLoadPending = false;
+}
+
diff --git a/game/code/presentation/gui/guiscreenmemcardcheck.h b/game/code/presentation/gui/guiscreenmemcardcheck.h
new file mode 100644
index 0000000..4183829
--- /dev/null
+++ b/game/code/presentation/gui/guiscreenmemcardcheck.h
@@ -0,0 +1,74 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMemCardCheck
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/23 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMEMCARDCHECK_H
+#define GUISCREENMEMCARDCHECK_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <data/memcard/memorycardmanager.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMemCardCheck : public CGuiScreen,
+ public IMemoryCardCheckCallback,
+ public IMemoryCardFormatCallback
+{
+public:
+ CGuiScreenMemCardCheck( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMemCardCheck();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual void OnMemoryCardCheckDone( radFileError errorCode,
+ IRadDrive::MediaInfo::MediaState mediaState,
+ int driveIndex,
+ int mostRecentSaveGameDriveIndex,
+ int mostRecentSaveGameSlot );
+
+ // Implements IMemoryCardFormatCallback
+ //
+ virtual void OnFormatOperationComplete(radFileError err);
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void OnContinue();
+
+ Scrooby::Text* m_messageText;
+
+ unsigned int m_minimumFormatTime;
+ unsigned int m_elapsedFormatTime;
+ radFileError m_formatResult;
+ bool m_formatState : 1;
+ bool m_formatDone : 1;
+ bool m_StatusPromptShown : 1;
+ bool m_isAutoLoadPending : 1;
+
+};
+
+#endif // GUISCREENMEMCARDCHECK_H
diff --git a/game/code/presentation/gui/guiscreenmemorycard.cpp b/game/code/presentation/gui/guiscreenmemorycard.cpp
new file mode 100644
index 0000000..fe85922
--- /dev/null
+++ b/game/code/presentation/gui/guiscreenmemorycard.cpp
@@ -0,0 +1,920 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMemoryCard
+//
+// Description: Implementation of the CGuiScreenMemoryCard class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/guiscreenmemorycard.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/guiscreenprompt.h>
+#include <presentation/gui/guimanager.h>
+
+#include <data/gamedatamanager.h>
+#include <data/memcard/memorycardmanager.h>
+#include <events/eventmanager.h>
+#include <gameflow/gameflow.h>
+#include <memory/srrmemory.h>
+
+#include <raddebug.hpp> // Foundation
+#include <layer.h>
+#include <group.h>
+#include <page.h>
+#include <screen.h>
+#include <strings/unicodestring.h>
+#include <text.h>
+#ifdef RAD_PS2
+#include <libmtap.h>
+#endif
+
+const tColour DEFAULT_DISABLED_ITEM_COLOUR_GREY( 128, 128, 128 ); // the same as in guimenu.cpp
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+enum eMemoryCardMenuItem
+{
+ MENU_ITEM_MEMORY_DEVICE,
+
+ NUM_MEMORY_CARD_MENU_ITEMS
+};
+
+bool CGuiScreenLoadSave::s_forceGotoMemoryCardScreen = false;
+
+int CGuiScreenMemoryCard::s_currentMemoryCardSlot = 0;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+CGuiScreenLoadSave::CGuiScreenLoadSave( Scrooby::Screen* pScreen )
+: m_currentSlot( -1 ),
+ m_lastError( Success ),
+ m_currentMemoryDevice( NULL ),
+ m_currentDriveIndex( -1 ),
+ m_minimumFormatTime(1000),
+ m_formatState(false),
+ m_operation( SCREEN_OP_IDLE )
+{
+ rAssert( pScreen != NULL );
+#ifdef RAD_WIN32
+ Scrooby::Page* pPage = pScreen->GetPage( "GameSlots" );
+ if( pPage != NULL )
+ {
+ Scrooby::Text* pText = pPage->GetText( "LoadSaveMessage" );
+ if( pText != NULL )
+ {
+ pText->SetTextMode( Scrooby::TEXT_WRAP );
+ }
+ }
+#else
+ Scrooby::Page* pPage = pScreen->GetPage( "SelectMemoryDevice" );
+ if( pPage != NULL )
+ {
+ Scrooby::Layer* foreground = pPage->GetLayer( "Foreground" );
+ Scrooby::Text* pText = foreground->GetText( "LoadSave" );
+ if( pText != NULL )
+ {
+ pText->SetTextMode( Scrooby::TEXT_WRAP );
+ }
+
+ m_currentMemoryDevice = foreground->GetText( "CurrentMemoryDevice" );
+ rAssert( m_currentMemoryDevice != NULL );
+ m_currentMemoryDevice->SetTextMode( Scrooby::TEXT_WRAP );
+
+ // set platform-specific text
+ //
+ Scrooby::Group* selectMemoryDevice = foreground->GetGroup( "SelectMemoryDevice" );
+ rAssert( selectMemoryDevice != NULL );
+
+#ifdef RAD_XBOX
+ selectMemoryDevice->SetVisible( false );
+#endif
+
+ pText = selectMemoryDevice->GetText( "SelectMemoryDevice" );
+ if( pText != NULL )
+ {
+ pText->SetIndex( PLATFORM_TEXT_INDEX );
+
+ // and apply wrapping
+ //
+ pText->SetTextMode( Scrooby::TEXT_WRAP );
+ }
+ }
+#endif
+}
+
+CGuiScreenLoadSave::~CGuiScreenLoadSave()
+{
+}
+
+void
+CGuiScreenLoadSave::HandleMessage( eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2 )
+{
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ GetMemoryCardManager()->Update( param1 );
+
+ if( !GetMemoryCardManager()->IsCurrentDriveReady() )
+ {
+ this->GotoMemoryCardScreen();
+
+ return;
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_AUX_X:
+ {
+#if defined( RAD_GAMECUBE ) || defined( RAD_PS2 )
+ this->GotoMemoryCardScreen();
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_SELECT );
+#endif
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+void
+CGuiScreenLoadSave::FormatCurrentDrive()
+{
+ m_formatState = true;
+ m_elapsedFormatTime = 0;
+ int currentDrive = GetMemoryCardManager()->GetCurrentDriveIndex();
+ m_formatDone = false;
+ GetMemoryCardManager()->FormatDrive(currentDrive, this);
+}
+
+void
+CGuiScreenLoadSave::OnFormatOperationComplete( radFileError errorCode )
+{
+ if (m_formatDone != true) // check if we already received an error or not
+ m_formatResult = errorCode;
+ m_formatDone = true;
+}
+
+void
+CGuiScreenLoadSave::UpdateCurrentMemoryDevice()
+{
+#ifndef RAD_WIN32
+ IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
+ if( currentDrive != NULL )
+ {
+ char textBibleEntry[ 32 ];
+#ifdef RAD_GAMECUBE
+ sprintf( textBibleEntry, "GC_%s", currentDrive->GetDriveName() );
+#endif
+#ifdef RAD_PS2
+ sprintf( textBibleEntry, "PS2_MEMCARD1A:" );
+#endif
+#ifdef RAD_XBOX
+ sprintf( textBibleEntry, "XBOX_%s", currentDrive->GetDriveName() );
+#endif
+
+ HeapMgr()->PushHeap( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ?
+ GMA_LEVEL_FE : GMA_LEVEL_HUD );
+
+ UnicodeString deviceName;
+ deviceName.ReadUnicode( GetTextBibleString( textBibleEntry ) );
+
+#ifdef RAD_PS2
+ int port_number = CGuiScreenMemoryCard::s_currentMemoryCardSlot/4;
+ if (sceMtapGetConnection(port_number)==1) { // is multitap
+ deviceName.Append('1' + (CGuiScreenMemoryCard::s_currentMemoryCardSlot/4));
+ deviceName.Append('-' );
+ deviceName.Append('A' + (CGuiScreenMemoryCard::s_currentMemoryCardSlot%4));
+ }
+ else {
+ deviceName.Append('1' + (CGuiScreenMemoryCard::s_currentMemoryCardSlot/4));
+ }
+#endif
+#ifdef RAD_XBOX
+ const char *volname = GetMemoryCardManager()->GetCurrentDriveVolumeName();
+ UnicodeString muName;
+ if (volname[0]!=0)
+ {
+ if (p3d::UnicodeStrLen((P3D_UNICODE*)( volname ) ) < MAX_MEM_CARD_NAME)
+ muName.ReadUnicode((P3D_UNICODE*) volname );
+ else
+ {
+ muName.ReadUnicode((P3D_UNICODE*) volname, MAX_MEM_CARD_NAME-1);
+ UnicodeString ellipsis;
+ ellipsis.ReadAscii("...");
+ muName += ellipsis;
+ }
+ deviceName.Append(' ');
+ deviceName.Append('(');
+ deviceName += muName;
+ deviceName.Append(')');
+ }
+#endif
+
+ rAssert( m_currentMemoryDevice != NULL );
+ m_currentMemoryDevice->SetString( 0, deviceName );
+
+ HeapMgr()->PopHeap(GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ?
+ GMA_LEVEL_FE : GMA_LEVEL_HUD);
+ }
+#endif
+
+ // update current drive index
+ //
+ m_currentDriveIndex = GetMemoryCardManager()->GetCurrentDriveIndex();
+}
+
+void
+CGuiScreenLoadSave::HandleErrorResponse( CGuiMenuPrompt::ePromptResponse response )
+{
+}
+
+
+//===========================================================================
+// CGuiScreenMemoryCard::CGuiScreenMemoryCard
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMemoryCard::CGuiScreenMemoryCard
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_MEMORY_CARD,
+ SCREEN_FX_FADE | SCREEN_FX_SLIDE_Y ),
+ m_layerSelectMemoryDevice( NULL ),
+ m_layerNoMemoryDevice( NULL ),
+#ifdef RAD_XBOX
+ m_numFreeBlocks( NULL ),
+#endif
+ m_pMenu( NULL ),
+ m_numAttachedDevices( -1 )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage;
+ pPage = m_pScroobyScreen->GetPage( "MemoryCard" );
+ rAssert( pPage );
+
+ m_layerSelectMemoryDevice = pPage->GetGroup( "SelectMemoryDevice" );
+ rAssert( m_layerSelectMemoryDevice );
+ m_layerSelectMemoryDevice->SetVisible( false );
+
+ m_layerNoMemoryDevice = pPage->GetGroup( "NoMemoryDevice" );
+ rAssert( m_layerNoMemoryDevice );
+ m_layerNoMemoryDevice->SetVisible( false );
+
+ m_memStatusText = pPage->GetText("Status");
+ rAssert(m_memStatusText);
+
+ m_memStatusText->SetTextMode( Scrooby::TEXT_WRAP );
+
+#ifdef RAD_XBOX
+ Scrooby::Group* freeSpace = pPage->GetGroup( "FreeSpace" );
+ rAssert( freeSpace != NULL );
+ m_numFreeBlocks = freeSpace->GetText( "NumFreeBlocks" );
+#else
+ // hide free space display for non-Xbox platforms
+ //
+ Scrooby::Group* freeSpace = pPage->GetGroup( "FreeSpace" );
+ rAssert( freeSpace != NULL );
+ freeSpace->SetVisible( false );
+#endif
+
+ // set platform-specific text
+ //
+ Scrooby::Text* pText = m_layerSelectMemoryDevice->GetText( "SelectMemoryDevice" );
+ if( pText != NULL )
+ {
+ pText->SetIndex( PLATFORM_TEXT_INDEX );
+
+ // and apply wrapping
+ //
+ pText->SetTextMode( Scrooby::TEXT_WRAP );
+ }
+
+ pText = m_layerNoMemoryDevice->GetText( "NoMemoryDevice" );
+ if( pText != NULL )
+ {
+ pText->SetIndex( PLATFORM_TEXT_INDEX );
+
+ // and apply wrapping
+ //
+ pText->SetTextMode( Scrooby::TEXT_WRAP );
+ }
+
+ pText = m_layerSelectMemoryDevice->GetText( "MemoryDevice" );
+ if( pText != NULL )
+ {
+ pText->SetTextMode( Scrooby::TEXT_WRAP );
+ }
+
+ // Create a menu.
+ //
+ m_pMenu = new CGuiMenu( this, NUM_MEMORY_CARD_MENU_ITEMS, GUI_TEXT_MENU, MENU_SFX_NONE );
+ rAssert( m_pMenu != NULL );
+
+ // Add menu items
+ //
+ Scrooby::Group* selectMemoryDevice = pPage->GetGroup( "SelectMemoryDevice" );
+ m_pMenu->AddMenuItem( selectMemoryDevice->GetText( "SelectMemoryDevice" ),
+ selectMemoryDevice->GetText( "MemoryDevice" ),
+ NULL,
+ NULL,
+ selectMemoryDevice->GetSprite( "LArrow" ),
+ selectMemoryDevice->GetSprite( "RArrow" ) );
+
+ Scrooby::Text * selectDeviceText = selectMemoryDevice->GetText( "SelectMemoryDevice" );
+ selectDeviceText->SetTextMode( Scrooby::TEXT_WRAP );
+
+ this->AutoScaleFrame( m_pScroobyScreen->GetPage( "BigBoard" ) );
+#ifdef RAD_XBOX
+ for(int j = 0; j < radFileDriveMax; j++ )
+ {
+ m_driveMountedFlag[j] = NULL;
+ }
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenMemoryCard::~CGuiScreenMemoryCard
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMemoryCard::~CGuiScreenMemoryCard()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMemoryCard::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMemoryCard::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if ( message==GUI_MSG_PROMPT_START_RESPONSE )
+ {
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+ }
+ else if( m_state == GUI_WINDOW_STATE_RUNNING || message == GUI_MSG_MENU_PROMPT_RESPONSE)
+ {
+ switch( message )
+ {
+ #ifdef RAD_PS2
+ case GUI_MSG_CONTROLLER_START:
+ {
+ if ( GetGameFlow()->GetCurrentContext() == CONTEXT_PAUSE )
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+
+ break;
+ }
+ #endif
+ case GUI_MSG_UPDATE:
+ {
+ GetMemoryCardManager()->Update( param1 );
+
+ this->UpdateDeviceList();
+
+ break;
+ }
+
+ case GUI_MSG_MENU_SELECTION_VALUE_CHANGED:
+ {
+ rAssert( param1 == MENU_ITEM_MEMORY_DEVICE );
+
+ this->UpdateFreeSpace( param2 );
+
+ break;
+ }
+
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ rAssert( param1 == MENU_ITEM_MEMORY_DEVICE );
+
+ int selectedDevice = m_pMenu->GetSelectionValue( MENU_ITEM_MEMORY_DEVICE );
+
+#ifdef RAD_XBOX
+ // check if user selected full xbox hd
+ if ( selectedDevice==0
+ && !GetMemoryCardManager()->EnoughFreeSpace( 0 )
+ && !GetGameDataManager()->DoesSaveGameExist(m_availableDrives[ 0 ], false) )
+ {
+ m_guiManager->DisplayPrompt(PROMPT_HD_FULL_XBOX, this, PROMPT_TYPE_CONTINUE);
+
+ break;
+ }
+#endif
+ GetMemoryCardManager()->SetCurrentDrive( m_availableDrives[ selectedDevice ] );
+
+ s_currentMemoryCardSlot = GetMemoryCardManager()->GetCurrentDriveIndex();
+
+ CGuiScreenLoadSave::s_forceGotoMemoryCardScreen = false;
+
+ m_pParent->HandleMessage( GUI_MSG_BACK_SCREEN );
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ if( !GetMemoryCardManager()->IsCurrentDriveReady( true ) ||
+ CGuiScreenLoadSave::s_forceGotoMemoryCardScreen )
+ {
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND )
+ {
+ this->StartTransitionAnimation( 230, 260 );
+ }
+
+ m_pParent->HandleMessage( GUI_MSG_BACK_SCREEN, 1 );
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_BACK );
+ }
+
+ break;
+ }
+ case GUI_MSG_MENU_PROMPT_RESPONSE:
+ {
+ ReloadScreen();
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenMemoryCard::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMemoryCard::InitIntro()
+{
+
+
+ this->UpdateDeviceList( true );
+
+ // set selection to current device, if still attached
+ //
+ IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
+ if( currentDrive != NULL )
+ {
+ if( GetMemoryCardManager()->IsCurrentDriveReady( true ) )
+ {
+ for( int i = 0; i < m_numAttachedDevices; i++ )
+ {
+ if( m_availableDrives[ i ] == currentDrive )
+ {
+ rAssert( m_pMenu );
+ m_pMenu->SetSelectionValue( MENU_ITEM_MEMORY_DEVICE, i );
+
+ break;
+ }
+ }
+ }
+ }
+ this->UpdateFreeSpace( m_pMenu->GetSelectionValue( MENU_ITEM_MEMORY_DEVICE ) );
+}
+
+
+//===========================================================================
+// CGuiScreenMemoryCard::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMemoryCard::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenMemoryCard::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMemoryCard::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenMemoryCard::UpdateDeviceList( bool forceUpdate )
+{
+ IRadDrive* currentSelectedDrive = NULL;
+
+#ifdef RAD_XBOX
+ IRadDrive *driveMountedFlag[radFileDriveMax];
+ int numAvailableDrives = GetMemoryCardManager()->GetAvailableDrives( m_availableDrives,
+ m_mediaInfos,
+ driveMountedFlag);
+#else
+ if( m_numAttachedDevices > 0 )
+ {
+ currentSelectedDrive = m_availableDrives[ m_pMenu->GetSelectionValue( MENU_ITEM_MEMORY_DEVICE ) ];
+ }
+
+ int numAvailableDrives = GetMemoryCardManager()->GetAvailableDrives( m_availableDrives,
+ m_mediaInfos);
+#endif
+
+ bool hasMoreDrive = false;
+ bool memoryDevicesAvailable = (numAvailableDrives > 0);
+
+// this->SetButtonVisible( BUTTON_ICON_BACK, !memoryDevicesAvailable );
+
+ if( numAvailableDrives == m_numAttachedDevices &&
+ !forceUpdate )
+ {
+ // since number of attached memory devices remain the same,
+ // assume no other changes occurred since last poll
+ //
+ return;
+ }
+ else
+ {
+ if (numAvailableDrives > m_numAttachedDevices)
+ hasMoreDrive = true;
+ // update number of attached memory devices
+ //
+ m_numAttachedDevices = numAvailableDrives;
+ }
+
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, memoryDevicesAvailable );
+
+ for( int i = 0; i < numAvailableDrives; i++ )
+ {
+ rAssert( m_availableDrives[ i ] );
+ rAssert( m_mediaInfos[ i ] );
+
+ char textBibleEntry[ 32 ];
+#ifdef RAD_GAMECUBE
+ sprintf( textBibleEntry, "GC_%s", m_availableDrives[ i ]->GetDriveName() );
+#endif
+#ifdef RAD_PS2
+ sprintf( textBibleEntry, "PS2_MEMCARD1A:" );
+#endif
+#ifdef RAD_XBOX
+ sprintf( textBibleEntry, "XBOX_%s", m_availableDrives[ i ]->GetDriveName() );
+#endif
+#ifdef RAD_WIN32
+ sprintf( textBibleEntry, "XBOX_U:" ); // Temp.
+#endif
+
+ HeapMgr()->PushHeap( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ?
+ GMA_LEVEL_FE : GMA_LEVEL_HUD );
+
+ UnicodeString deviceName;
+ deviceName.ReadUnicode( GetTextBibleString( textBibleEntry ) );
+
+#ifdef RAD_PS2
+ int drive_index = GetMemoryCardManager()->GetDriveIndex(m_availableDrives[ i ]);
+ int port_number = drive_index/4;
+ if (sceMtapGetConnection(port_number)==1) { // is multitap
+ deviceName.Append('1' + (drive_index/4));
+ deviceName.Append('-' );
+ deviceName.Append('A' + (drive_index%4));
+ }
+ else {
+ deviceName.Append('1' + drive_index/4);
+ }
+#endif
+
+#ifdef RAD_XBOX
+ if (i > 0 && m_mediaInfos[ i ]->m_VolumeName[0]!=0) // i==0 is the hard disk
+ {
+ UnicodeString muName;
+ if (p3d::UnicodeStrLen((P3D_UNICODE*)( m_mediaInfos[ i ]->m_VolumeName) ) < MAX_MEM_CARD_NAME)
+ muName.ReadUnicode((P3D_UNICODE*) m_mediaInfos[ i ]->m_VolumeName);
+ else
+ {
+ muName.ReadUnicode((P3D_UNICODE*) m_mediaInfos[ i ]->m_VolumeName, MAX_MEM_CARD_NAME-1);
+ UnicodeString ellipsis;
+ ellipsis.ReadAscii("...");
+ muName += ellipsis;
+ }
+ deviceName.Append(' ');
+ deviceName.Append('(');
+ deviceName += muName;
+ deviceName.Append(')');
+
+
+ }
+#endif
+
+
+
+
+ Scrooby::Text* memoryDeviceText = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( MENU_ITEM_MEMORY_DEVICE )->GetItemValue() );
+ rAssert( memoryDeviceText != NULL );
+ memoryDeviceText->SetString( i, deviceName );
+
+ HeapMgr()->PopHeap(GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ?
+ GMA_LEVEL_FE : GMA_LEVEL_HUD);
+
+ }
+
+ m_layerSelectMemoryDevice->SetVisible( memoryDevicesAvailable );
+ m_layerNoMemoryDevice->SetVisible( !memoryDevicesAvailable );
+
+ rAssert( m_pMenu );
+ if( memoryDevicesAvailable )
+ {
+ m_pMenu->GetMenuItem( MENU_ITEM_MEMORY_DEVICE )->m_attributes |= SELECTABLE;
+ }
+ else
+ {
+ m_pMenu->GetMenuItem( MENU_ITEM_MEMORY_DEVICE )->m_attributes &= ~SELECTABLE;
+ m_pMenu->SetSelectionValueCount( MENU_ITEM_MEMORY_DEVICE, 1 ); // to disable left/right scrolling
+ }
+// m_pMenu->SetMenuItemEnabled( MENU_ITEM_MEMORY_DEVICE, memoryDevicesAvailable );
+
+ if( memoryDevicesAvailable )
+ {
+ m_pMenu->SetSelectionValueCount( MENU_ITEM_MEMORY_DEVICE, numAvailableDrives );
+
+ m_pMenu->SetSelectionValue( MENU_ITEM_MEMORY_DEVICE, 0 );
+
+#ifndef RAD_XBOX
+ for( int i = 0; i < m_numAttachedDevices; i++ )
+ {
+ if( m_availableDrives[ i ] == currentSelectedDrive )
+ {
+ m_pMenu->SetSelectionValue( MENU_ITEM_MEMORY_DEVICE, i );
+
+ break;
+ }
+ }
+#endif
+ }
+#ifdef RAD_XBOX
+ int j;
+ if (hasMoreDrive) // find which drive is new and select it
+ {
+ for(j = 0; j < radFileDriveMax; j++ )
+ {
+ if (driveMountedFlag[j] && m_driveMountedFlag[j]==NULL)
+ {
+ for (int x = 0; x < numAvailableDrives; x++)
+ {
+ if (m_availableDrives[x] == driveMountedFlag[j])
+ {
+ m_pMenu->SetSelectionValue( MENU_ITEM_MEMORY_DEVICE, x );
+ break;
+ }
+ }
+ break;
+ }
+ }
+ rAssert(j<radFileDriveMax); // we must find the different drive
+ }
+
+ for(j = 0; j < radFileDriveMax; j++ )
+ {
+ m_driveMountedFlag[j] = driveMountedFlag[j];
+ }
+#endif
+}
+
+void
+CGuiScreenMemoryCard::UpdateFreeSpace( unsigned int currentDriveIndex )
+{
+ Scrooby::BoundedDrawable* menu_drawable
+ = m_pMenu->GetMenuItem(MENU_ITEM_MEMORY_DEVICE)->GetItemValue();
+
+ menu_drawable->SetColour(m_pMenu->GetHighlightColour());
+
+ m_memStatusText->SetVisible(false);
+
+ // enable selection
+ //
+ if( m_numAttachedDevices > 0 )
+ {
+ m_pMenu->GetMenuItem( MENU_ITEM_MEMORY_DEVICE )->m_attributes |= SELECTABLE;
+ SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+ }
+
+ HeapMgr()->PushHeap( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ?
+ GMA_LEVEL_FE : GMA_LEVEL_HUD );
+
+#ifdef RAD_XBOX
+ if( static_cast<int>( currentDriveIndex ) < m_numAttachedDevices )
+ {
+ const unsigned int MAX_NUM_FREE_BLOCKS = 50000;
+ unsigned int numFreeBlocks = m_mediaInfos[ currentDriveIndex ]->m_FreeSpace /
+ NUM_BYTES_PER_BLOCK;
+
+ char buffer[ 16 ];
+ if( numFreeBlocks > MAX_NUM_FREE_BLOCKS )
+ {
+ sprintf( buffer, "50,000+" );
+ }
+ else
+ {
+ if( numFreeBlocks < 1000 )
+ {
+ sprintf( buffer, "%d", numFreeBlocks );
+ }
+ else
+ {
+ sprintf( buffer, "%d,%03d", numFreeBlocks / 1000, numFreeBlocks % 1000 );
+ }
+ }
+
+ rAssert( m_numFreeBlocks != NULL );
+ m_numFreeBlocks->SetString( 0, buffer );
+
+ m_pMenu->GetMenuItem( MENU_ITEM_MEMORY_DEVICE )->m_attributes |= SELECTABLE;
+ SetButtonVisible(BUTTON_ICON_ACCEPT , true);
+ }
+#endif // RAD_XBOX
+
+ if (((int)currentDriveIndex < m_numAttachedDevices) && m_mediaInfos[ currentDriveIndex ]->m_MediaState != IRadDrive::MediaInfo::MediaPresent)
+ {
+ m_memStatusText->SetVisible(true);
+ m_memStatusText->SetIndex(
+ (m_mediaInfos[ currentDriveIndex ]->m_MediaState - IRadDrive::MediaInfo::MediaNotFormatted) * 3
+ + PLATFORM_TEXT_INDEX);
+ bool disable = true;
+
+#ifdef RAD_GAMECUBE
+ // on GameCube, if memory card is unformatted or formatted for another market, allow it to be
+ // selected so that if can be formatted
+ //
+ if( m_mediaInfos[ currentDriveIndex ]->m_MediaState == IRadDrive::MediaInfo::MediaNotFormatted ||
+ m_mediaInfos[ currentDriveIndex ]->m_MediaState == IRadDrive::MediaInfo::MediaEncodingErr )
+ {
+ disable = false;
+
+ m_memStatusText->SetVisible( false ); // no need to display status text
+ }
+#endif // RAD_GAMECUBE
+
+#ifdef RAD_PS2
+ // on PS2, if memory card is unformatted, allow it to be selected so that it can be formatted
+ //
+ if( m_mediaInfos[ currentDriveIndex ]->m_MediaState == IRadDrive::MediaInfo::MediaNotFormatted )
+ {
+ disable = false;
+
+ m_memStatusText->SetVisible( false ); // no need to display status text
+ }
+#endif // RAD_PS2
+
+ if (disable)
+ {
+ // disable selection
+ m_pMenu->GetMenuItem( MENU_ITEM_MEMORY_DEVICE )->m_attributes &= ~SELECTABLE;
+ SetButtonVisible(BUTTON_ICON_ACCEPT , false);
+ // change color
+ menu_drawable->SetColour( DEFAULT_DISABLED_ITEM_COLOUR_GREY );
+ }
+ }
+ else // no error
+ {
+ if ( ((int)currentDriveIndex < m_numAttachedDevices) && GetMemoryCardManager()->IsMemcardInfoLoaded() )
+ {
+ // this drive index is different than currentDriveIndex
+ int memcard_driveindex = GetMemoryCardManager()->GetDriveIndex(m_availableDrives[ currentDriveIndex ]);
+ rTuneAssert(memcard_driveindex >= 0);
+ if ( GetMemoryCardManager()->EnoughFreeSpace( memcard_driveindex )==false
+ && GetGameDataManager()->DoesSaveGameExist(m_availableDrives[ currentDriveIndex ], false)==false ) // check for full without save game
+ {
+ int message_index = 5 * 3 + PLATFORM_TEXT_INDEX;
+ #ifdef RAD_XBOX
+ if (currentDriveIndex==0) // hd
+ message_index ++;
+ #endif
+ m_memStatusText->SetVisible(true);
+ m_memStatusText->SetIndex( message_index );
+
+#ifdef RAD_GAMECUBE
+ // append "Use Memory Card Screen" text to message; this is done in
+ // code because the text bible compiler can't handle strings with
+ // more than 255 characters
+ //
+ UnicodeString useMemCardScreen;
+ useMemCardScreen.ReadUnicode( GetTextBibleString( "USE_MEMORY_CARD_SCREEN" ) );
+
+ UnicodeString newString;
+ newString.ReadUnicode( GetTextBibleString( "MU_FULL_STATUS_(GC)" ) );
+ newString.Append( ' ' );
+ newString += useMemCardScreen;
+
+ m_memStatusText->SetString( message_index, newString );
+#endif // RAD_GAMECUBE
+
+ bool disable = true;
+#ifdef RAD_XBOX
+ if (currentDriveIndex==0) // hd
+ disable = false;
+#endif
+
+ if (disable)
+ {
+ // change color
+ menu_drawable->SetColour( DEFAULT_DISABLED_ITEM_COLOUR_GREY );
+
+ // disable selection
+ m_pMenu->GetMenuItem( MENU_ITEM_MEMORY_DEVICE )->m_attributes &= ~SELECTABLE;
+ SetButtonVisible(BUTTON_ICON_ACCEPT , false);
+ }
+
+ }
+ }
+ }
+
+ HeapMgr()->PopHeap( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ?
+ GMA_LEVEL_FE : GMA_LEVEL_HUD );
+
+}
+
diff --git a/game/code/presentation/gui/guiscreenmemorycard.h b/game/code/presentation/gui/guiscreenmemorycard.h
new file mode 100644
index 0000000..29c5d1a
--- /dev/null
+++ b/game/code/presentation/gui/guiscreenmemorycard.h
@@ -0,0 +1,206 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMemoryCard
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/30 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMEMORYCARD_H
+#define GUISCREENMEMORYCARD_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guiscreenmessage.h>
+#include <data/memcard/memorycardmanager.h>
+
+#include <radfile.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+#ifdef RAD_XBOX
+ const unsigned int NUM_BYTES_PER_BLOCK = 16 * 1024;
+#endif
+
+class CGuiMenu;
+struct IRadDrive;
+
+namespace Scrooby
+{
+ class Group;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenLoadSave : public IMemoryCardFormatCallback
+{
+public:
+ CGuiScreenLoadSave( Scrooby::Screen* pScreen );
+ virtual ~CGuiScreenLoadSave();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ static bool s_forceGotoMemoryCardScreen;
+ // Implements IMemoryCardFormatCallback
+ //
+ virtual void OnFormatOperationComplete(radFileError err);
+
+protected:
+ enum ScreenOperation
+ {
+ SCREEN_OP_IDLE,
+ LOAD,
+ SAVE,
+ FORMAT,
+ DELETE_GAME,
+
+ NUM_SCREEN_OPS
+ };
+
+ void UpdateCurrentMemoryDevice();
+ virtual void GotoMemoryCardScreen( bool isFromPrompt = false ) = 0;
+ virtual void HandleErrorResponse( CGuiMenuPrompt::ePromptResponse response );
+
+ void FormatCurrentDrive();
+
+ int m_currentSlot;
+ radFileError m_lastError;
+
+ Scrooby::Text* m_currentMemoryDevice;
+ int m_currentDriveIndex;
+
+ unsigned int m_minimumFormatTime;
+ unsigned int m_elapsedFormatTime;
+ radFileError m_formatResult;
+ bool m_formatState;
+ bool m_formatDone;
+
+
+ ScreenOperation m_operation;
+
+
+};
+
+class CGuiScreenMemoryCard : public CGuiScreen
+{
+public:
+ CGuiScreenMemoryCard( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMemoryCard();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+ static int s_currentMemoryCardSlot;
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void UpdateDeviceList( bool forceUpdate = false );
+ void UpdateFreeSpace( unsigned int currentDriveIndex );
+
+ Scrooby::Group* m_layerSelectMemoryDevice;
+ Scrooby::Group* m_layerNoMemoryDevice;
+
+#ifdef RAD_XBOX
+ Scrooby::Text* m_numFreeBlocks;
+ IRadDrive *m_driveMountedFlag[radFileDriveMax];
+#endif
+ Scrooby::Text* m_memStatusText;
+
+ CGuiMenu* m_pMenu;
+
+ IRadDrive* m_availableDrives[ radFileDriveMax ];
+ IRadDrive::MediaInfo* m_mediaInfos[ radFileDriveMax ];
+ int m_numAttachedDevices;
+
+
+};
+
+const int NUM_RADFILE_ERROR_MESSAGES = DataCorrupt; // should be last enum
+const int NUM_MEDIA_INFO_STATES = IRadDrive::MediaInfo::MediaDamaged; // should be last enum
+
+
+const int MAX_MEM_CARD_NAME = 16; // max display for memory card
+
+enum eErrorMessageMode
+{
+ ERROR_DURING_CHECKING,
+ ERROR_DURING_LOADING,
+ ERROR_DURING_SAVING,
+
+ NUM_ERROR_MESSAGE_MODES
+};
+
+inline int GetErrorMessageIndex( radFileError errorCode, eErrorMessageMode mode )
+{
+ const int NUM_PLATFORMS = 3;
+
+ // radfile error messages are offset by media info states
+ //
+ int messageIndex = NUM_MEDIA_INFO_STATES;
+
+ switch( mode )
+ {
+ case ERROR_DURING_CHECKING:
+ {
+ messageIndex += (errorCode - 1);
+
+ break;
+ }
+ case ERROR_DURING_LOADING:
+ {
+ messageIndex += (errorCode - 1) + NUM_RADFILE_ERROR_MESSAGES;
+
+ break;
+ }
+ case ERROR_DURING_SAVING:
+ {
+ messageIndex += (errorCode - 1) + NUM_RADFILE_ERROR_MESSAGES * 2;
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "ERROR: *** Invalid error message mode!" );
+ break;
+ }
+ }
+
+ // return platform-specific message index
+ //
+ return( messageIndex * NUM_PLATFORMS + PLATFORM_TEXT_INDEX );
+};
+
+inline int GetErrorMessageIndex( IRadDrive::MediaInfo::MediaState mediaState )
+{
+ const int NUM_PLATFORMS = 3;
+
+ int messageIndex = mediaState - 1;
+
+ // return platform-specific message index
+ //
+ return( messageIndex * NUM_PLATFORMS + PLATFORM_TEXT_INDEX );
+};
+
+#endif // GUISCREENMEMORYCARD_H
diff --git a/game/code/presentation/gui/guiscreenmessage.cpp b/game/code/presentation/gui/guiscreenmessage.cpp
new file mode 100644
index 0000000..4f47623
--- /dev/null
+++ b/game/code/presentation/gui/guiscreenmessage.cpp
@@ -0,0 +1,629 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMessage
+//
+// Description: Implementation of the CGuiScreenMessage class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/guiscreenmessage.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/guiscreenmemorycard.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+
+#include <data/memcard/memorycardmanager.h>
+#include <gameflow/gameflow.h>
+#include <memory/srrmemory.h>
+
+#include <p3d/unicode.hpp>
+#include <raddebug.hpp> // Foundation
+#include <layer.h>
+#include <page.h>
+#include <screen.h>
+#include <sprite.h>
+#include <text.h>
+
+#ifdef RAD_PS2
+#include <libmtap.h>
+#endif
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+int CGuiScreenMessage::s_ControllerDisconnectedPort = 0;
+
+int CGuiScreenMessage::s_messageIndex = -1;
+CGuiEntity* CGuiScreenMessage::s_pMessageCallback = NULL;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMessage::CGuiScreenMessage
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMessage::CGuiScreenMessage
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_GENERIC_MESSAGE ),
+ m_pMenu( NULL ),
+ m_messageText( NULL ),
+ m_messageIcon( NULL ),
+ m_elapsedTime( 0 )
+{
+ m_originalStringBuffer[ 0 ] = '\0';
+
+MEMTRACK_PUSH_GROUP( "GUIScreenMessage" );
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "Message" );
+ rAssert( pPage != NULL );
+
+ Scrooby::Layer* pLayer = pPage->GetLayer( "Foreground" );
+ rAssert( pLayer != NULL );
+
+ m_messageText = pLayer->GetText( "Message" );
+ rAssert( m_messageText );
+
+ // wrap text message
+ //
+ m_messageText->SetTextMode( Scrooby::TEXT_WRAP );
+ m_messageText->ResetTransformation();
+ m_messageText->ScaleAboutCenter(0.9f);
+
+ // Retrieve the Scrooby drawing elements (from MessageBox page).
+ //
+ pPage = m_pScroobyScreen->GetPage( "MessageBox" );
+ rAssert( pPage != NULL );
+
+ m_messageIcon = pPage->GetSprite( "ErrorIcon" );
+
+ this->AutoScaleFrame( pPage );
+
+ this->SetFadeTime( 0.0f );
+MEMTRACK_POP_GROUP( "GUIScreenMessage" );
+}
+
+
+//===========================================================================
+// CGuiScreenMessage::~CGuiScreenMessage
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMessage::~CGuiScreenMessage()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMessage::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMessage::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ m_elapsedTime += param1;
+
+ // pulse message icon
+ //
+ float scale = GuiSFX::Pulse( (float)m_elapsedTime,
+ 500.0f,
+ 1.0f * MESSAGE_ICON_CORRECTION_SCALE,
+ 0.1f * MESSAGE_ICON_CORRECTION_SCALE );
+
+ rAssert( m_messageIcon );
+ m_messageIcon->ResetTransformation();
+ m_messageIcon->ScaleAboutCenter( scale );
+
+ if( s_pMessageCallback != NULL )
+ {
+ s_pMessageCallback->HandleMessage( GUI_MSG_MESSAGE_UPDATE, param1 );
+ }
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ // ignore BACK inputs, thereby, not allowing users to back
+ // out of prompt
+ return;
+
+ break;
+ }
+/*
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ rAssert( s_pMessageCallback );
+ s_pMessageCallback->HandleMessage( GUI_MSG_MENU_MESSAGE_RESPONSE,
+ param1, param2 );
+
+ break;
+ }
+*/
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+ else if( m_state == GUI_WINDOW_STATE_OUTRO )
+ {
+ if( m_numTransitionsPending < 0 )
+ {
+ // restore original string buffer
+ //
+ if( m_originalStringBuffer[ 0 ] != '\0' )
+ {
+ rAssert( m_messageText != NULL );
+ UnicodeChar* stringBuffer = m_messageText->GetStringBuffer();
+ rAssert( stringBuffer != NULL );
+
+ p3d::UnicodeStrCpy( static_cast<P3D_UNICODE*>( m_originalStringBuffer ),
+ static_cast<P3D_UNICODE*>( stringBuffer ),
+ sizeof( m_originalStringBuffer ) );
+
+ m_originalStringBuffer[ 0 ] = '\0';
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+void
+CGuiScreenMessage::Display( int messageIndex, CGuiEntity* pCallback )
+{
+ s_messageIndex = messageIndex;
+ s_pMessageCallback = pCallback;
+}
+void
+CGuiScreenMessage::GetControllerDisconnectedMessage(int controller_id, char *str_buffer, int max_char)
+{
+ P3D_UNICODE* multitapString = NULL;
+
+ s_ControllerDisconnectedPort = controller_id;
+
+#ifdef RAD_GAMECUBE
+ P3D_UNICODE* uni_string = GetTextBibleString( "MSG_CONTROLLER_DISCONNECTED_(GC)" );
+#endif
+
+#ifdef RAD_XBOX
+ P3D_UNICODE* uni_string = GetTextBibleString( "MSG_CONTROLLER_DISCONNECTED_(XBOX)" );
+#endif
+
+#ifdef RAD_PS2
+ P3D_UNICODE* uni_string = GetTextBibleString( "MSG_CONTROLLER_DISCONNECTED_(PS2)" );
+ if (s_ControllerDisconnectedPort > 7)
+ uni_string = GetTextBibleString( "MSG_CONTROLLER_DISCONNECTED_WHEEL" ); // usb
+ int port = controller_id/4;
+ if ( controller_id < 8 &&
+ GetInputManager()->GetLastMultitapStatus( port ) != 0 &&
+ sceMtapGetConnection( port ) == 0 )
+ {
+ uni_string = GetTextBibleString( "MSG_CONTROLLER_DISCONNECTED_MULTITAP" ); // multitap
+
+ // convert the first controller port wildcard character to either 1 or 2
+ //
+ const UnicodeChar CONTROLLER_PORT_WILDCARD_CHARACTER = 0xa5;
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+
+ multitapString = new P3D_UNICODE[ max_char + 1 ];
+ rAssert( multitapString != NULL );
+ p3d::UnicodeStrCpy( uni_string, multitapString, max_char );
+ for( int i = 0; multitapString[ i ] != '\0'; i++ )
+ {
+ if( multitapString[ i ] == CONTROLLER_PORT_WILDCARD_CHARACTER )
+ {
+ // found it!
+ //
+ multitapString[ i ] = '1' + port;
+ break;
+ }
+ }
+
+ uni_string = multitapString;
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+ }
+#endif
+
+#ifdef RAD_WIN32
+ P3D_UNICODE* uni_string = GetTextBibleString( "MSG_CONTROLLER_DISCONNECTED_(XBOX)" );
+#endif
+
+ CGuiScreenMessage::ConvertUnicodeToChar(str_buffer, uni_string, max_char);
+
+ if( multitapString != NULL )
+ {
+ delete multitapString;
+ multitapString = NULL;
+ }
+}
+void
+CGuiScreenMessage::ConvertUnicodeToChar(char *str, P3D_UNICODE* uni_str, int max_char)
+{
+ const UnicodeChar CONTROLLER_PORT_WILDCARD_CHARACTER = 0xa5;// since > 0x7f, '¥' wont work;
+ const UnicodeChar NEWLINE_CHARACTER = 0x5c; //'\';
+ int i = 0;
+ while (*uni_str && i < max_char)
+ {
+ if (*uni_str==NEWLINE_CHARACTER)
+ *str = '\n';
+ else if (*uni_str==CONTROLLER_PORT_WILDCARD_CHARACTER)
+ {
+#ifdef RAD_PS2
+ int port_number = s_ControllerDisconnectedPort/4;
+ if (sceMtapGetConnection(port_number)==1
+ || GetInputManager()->GetLastMultitapStatus(port_number) == 1) { // is multitap
+
+ UnicodeString unicodeString; // splice the mu name inside
+ UnicodeString restofString;
+ UnicodeString deviceName;
+
+ *str++ = '1' + (s_ControllerDisconnectedPort/4);
+ i++;
+ if (i >= max_char) break;
+ *str++ = '-';
+ i++;
+ if (i >= max_char) break;
+ *str = 'A' + (s_ControllerDisconnectedPort%4);
+ }
+ else // no multitap
+ {
+ *str = '1' + (s_ControllerDisconnectedPort/4);
+ if (s_ControllerDisconnectedPort==8)
+ *str = '1';
+ else if (s_ControllerDisconnectedPort==9)
+ *str = '2';
+ }
+
+#else
+ *str = '1' + s_ControllerDisconnectedPort;
+#endif
+ }
+ else
+ *str = (char)*uni_str;
+
+
+ i++;
+ str++;
+ uni_str++;
+ }
+ *str++ = 0;
+}
+void
+CGuiScreenMessage::FormatMessage( Scrooby::Text* pText,
+ UnicodeChar* originalStringBuffer,
+ int stringBufferLength )
+{
+ const UnicodeChar SLOT_WILDCARD_CHARACTER = '$';
+ const UnicodeChar BLOCKS_WILDCARD_CHARACTER = '#';
+ const UnicodeChar CONTROLLER_PORT_WILDCARD_CHARACTER = 0xa5;// since > 0x7f, '¥' wont work;
+
+ rAssert( pText != NULL );
+ UnicodeChar* stringBuffer = pText->GetStringBuffer();
+ rAssert( stringBuffer != NULL );
+
+ // save copy of original string buffer
+ //
+ if( originalStringBuffer != NULL )
+ {
+ p3d::UnicodeStrCpy( static_cast<P3D_UNICODE*>( stringBuffer ),
+ static_cast<P3D_UNICODE*>( originalStringBuffer ),
+ stringBufferLength );
+ }
+
+ for( int i = 0; stringBuffer[ i ] != '\0'; i++ )
+ {
+ // search for slot wildcard character
+ //
+ if( stringBuffer[ i ] == CONTROLLER_PORT_WILDCARD_CHARACTER )
+ {
+#ifdef RAD_PS2
+ int port_number = s_ControllerDisconnectedPort/4;
+ if (sceMtapGetConnection(port_number)==1) { // is multitap
+ HeapMgr()->PushHeap( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ?
+ GMA_LEVEL_FE : GMA_LEVEL_HUD );
+
+ UnicodeString unicodeString; // splice the mu name inside
+ UnicodeString restofString;
+ UnicodeString deviceName;
+
+ deviceName.Append('1' + (s_ControllerDisconnectedPort/4));
+ deviceName.Append('-' );
+ deviceName.Append('A' + (s_ControllerDisconnectedPort%4));
+
+ unicodeString.ReadUnicode(stringBuffer, i);
+ restofString.ReadUnicode(&stringBuffer[i+1]);
+ unicodeString += deviceName;
+ unicodeString += restofString;
+ pText->SetString(pText->GetIndex(),unicodeString);
+
+ HeapMgr()->PopHeap(GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ?
+ GMA_LEVEL_FE : GMA_LEVEL_HUD);
+ }
+ else // no multitap
+ stringBuffer[ i ] = '1' + (s_ControllerDisconnectedPort/4);
+
+#else
+ stringBuffer[ i ] = '1' + s_ControllerDisconnectedPort;
+#endif
+ break;
+ }
+ }
+// update slot wild card character---------------------
+
+#ifdef RAD_GAMECUBE
+ for( int i = 0; stringBuffer[ i ] != '\0'; i++ )
+ {
+ // search for slot wildcard character
+ //
+ if( stringBuffer[ i ] == SLOT_WILDCARD_CHARACTER )
+ {
+ stringBuffer[ i ] = 'A' + CGuiScreenMemoryCard::s_currentMemoryCardSlot;
+
+ break;
+ }
+ }
+#endif // RAD_GAMECUBE
+
+#ifdef RAD_PS2
+ for( int i = 0; stringBuffer[ i ] != '\0'; i++ )
+ {
+ // search for slot wildcard character
+ //
+ if( stringBuffer[ i ] == SLOT_WILDCARD_CHARACTER )
+ {
+ int port_number = CGuiScreenMemoryCard::s_currentMemoryCardSlot/4;
+ if (sceMtapGetConnection(port_number)==1) { // is multitap
+
+ HeapMgr()->PushHeap( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ?
+ GMA_LEVEL_FE : GMA_LEVEL_HUD );
+
+ UnicodeString unicodeString; // splice the mu name inside
+ UnicodeString restofString;
+ UnicodeString deviceName;
+
+ deviceName.Append('1' + (CGuiScreenMemoryCard::s_currentMemoryCardSlot/4));
+ deviceName.Append('-' );
+ deviceName.Append('A' + (CGuiScreenMemoryCard::s_currentMemoryCardSlot%4));
+
+ unicodeString.ReadUnicode(stringBuffer, i);
+ restofString.ReadUnicode(&stringBuffer[i+1]);
+ unicodeString += deviceName;
+ unicodeString += restofString;
+ pText->SetString(pText->GetIndex(),unicodeString);
+
+ HeapMgr()->PopHeap(GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ?
+ GMA_LEVEL_FE : GMA_LEVEL_HUD);
+ }
+ else
+ {
+ stringBuffer[ i ] = '1' + CGuiScreenMemoryCard::s_currentMemoryCardSlot/4;
+ }
+ break;
+ }
+
+ }
+#endif // RAD_PS2
+
+#ifdef RAD_XBOX
+ for( int i = 0; stringBuffer[ i ] != '\0'; i++ )
+ {
+ // search for num blocks wildcard character
+ //
+ if( stringBuffer[ i ] == BLOCKS_WILDCARD_CHARACTER )
+ {
+ const IRadDrive::MediaInfo* mediaInfo = GetMemoryCardManager()->GetMediaInfo( static_cast<unsigned int>( CGuiScreenMemoryCard::s_currentMemoryCardSlot ) );
+ rAssert( mediaInfo != NULL );
+
+ unsigned int savedGameSize = GetMemoryCardManager()->GetSavedGameCreationSize( static_cast<unsigned int>( CGuiScreenMemoryCard::s_currentMemoryCardSlot ) );
+
+ unsigned int numBlocksRequired = (savedGameSize - mediaInfo->m_FreeSpace) / NUM_BYTES_PER_BLOCK;
+ rAssert( numBlocksRequired > 0 );
+ rWarningMsg( numBlocksRequired < 10, "WARNING: *** Number of free blocks required is more than one digit long!" );
+
+ stringBuffer[ i ] = '0' + numBlocksRequired;
+
+ break;
+ }
+ }
+ for( int i = 0; stringBuffer[ i ] != '\0'; i++ )
+ {
+ // search for slot wildcard character
+ //
+ if( stringBuffer[ i ] == SLOT_WILDCARD_CHARACTER )
+ {
+ HeapMgr()->PushHeap( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ?
+ GMA_LEVEL_FE : GMA_LEVEL_HUD );
+ UnicodeString deviceName;
+
+ const IRadDrive::MediaInfo* mediaInfo =
+ GetMemoryCardManager()->GetMediaInfo( static_cast<unsigned int>(
+ CGuiScreenMemoryCard::s_currentMemoryCardSlot ) );
+ char textBibleEntry[ 32 ];
+ sprintf( textBibleEntry, "XBOX_%s",
+ GetMemoryCardManager()->GetDriveName( static_cast<unsigned int>(
+ CGuiScreenMemoryCard::s_currentMemoryCardSlot ) ) );
+ deviceName.ReadUnicode( GetTextBibleString( textBibleEntry ) );
+
+ if (mediaInfo->m_VolumeName[0]!=0) // append personalized name
+ {
+ UnicodeString muName;
+ if (p3d::UnicodeStrLen((P3D_UNICODE*)( mediaInfo->m_VolumeName) ) < MAX_MEM_CARD_NAME)
+ muName.ReadUnicode((P3D_UNICODE*) mediaInfo->m_VolumeName);
+ else
+ {
+ muName.ReadUnicode((P3D_UNICODE*) mediaInfo->m_VolumeName, MAX_MEM_CARD_NAME-1);
+ UnicodeString ellipsis;
+ ellipsis.ReadAscii("...");
+ muName += ellipsis;
+ }
+ deviceName.Append(' ');
+ deviceName.Append('(');
+ deviceName += muName;
+ deviceName.Append(')');
+ }
+ UnicodeString unicodeString; // splice the mu name inside
+ UnicodeString restofString;
+ unicodeString.ReadUnicode(stringBuffer, i);
+ restofString.ReadUnicode(&stringBuffer[i+1]);
+ unicodeString += deviceName;
+ unicodeString += restofString;
+
+ pText->SetString(pText->GetIndex(),unicodeString);
+
+ HeapMgr()->PopHeap(GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ?
+ GMA_LEVEL_FE : GMA_LEVEL_HUD);
+ }
+ }
+
+
+#endif // RAD_XBOX
+}
+
+//===========================================================================
+// CGuiScreenMessage::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMessage::InitIntro()
+{
+ rAssert( m_messageText );
+ rAssert( s_messageIndex >= 0 &&
+ s_messageIndex < m_messageText->GetNumOfStrings() );
+
+ // set message text
+ //
+ m_messageText->SetIndex( s_messageIndex );
+
+ CGuiScreenMessage::FormatMessage( m_messageText,
+ m_originalStringBuffer,
+ sizeof( m_originalStringBuffer ) );
+
+ rAssert( m_messageIcon );
+ m_messageIcon->ResetTransformation();
+
+ m_elapsedTime = 0;
+
+/*
+ // show/hide accept button depending on whether menu is visible or not
+ //
+ rAssert( s_menuGroup );
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, s_menuGroup->IsVisible() );
+*/
+}
+
+
+//===========================================================================
+// CGuiScreenMessage::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMessage::InitRunning()
+{
+ if( s_pMessageCallback != NULL )
+ {
+ s_pMessageCallback->HandleMessage( GUI_MSG_ON_DISPLAY_MESSAGE );
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMessage::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMessage::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/guiscreenmessage.h b/game/code/presentation/gui/guiscreenmessage.h
new file mode 100644
index 0000000..dec532d
--- /dev/null
+++ b/game/code/presentation/gui/guiscreenmessage.h
@@ -0,0 +1,114 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMessage
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/10/21 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMESSAGE_H
+#define GUISCREENMESSAGE_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+#include <strings/unicodestring.h>
+
+#ifdef RAD_WIN32
+const float MESSAGE_ICON_CORRECTION_SCALE = 0.875f;
+#else
+const float MESSAGE_ICON_CORRECTION_SCALE = 1.75f;
+#endif
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMessage : public CGuiScreen
+{
+public:
+ enum eMessageID
+ {
+ MSG_ID_CONTROLLER_DISCONNECTED_GC,
+ MSG_ID_CONTROLLER_DISCONNECTED_PS2,
+ MSG_ID_CONTROLLER_DISCONNECTED_XBOX,
+ MSG_ID_AUTO_LOADING_GAME_GC,
+ MSG_ID_AUTO_LOADING_GAME_PS2,
+ MSG_ID_AUTO_LOADING_GAME_XBOX,
+ MSG_ID_AUTO_LOADING_GAME_XBOX_HD,
+ MSG_ID_LOADING_GAME_GC,
+ MSG_ID_LOADING_GAME_PS2,
+ MSG_ID_LOADING_GAME_XBOX,
+ MSG_ID_LOADING_GAME_XBOX_HD,
+ MSG_ID_SAVING_GAME_GC,
+ MSG_ID_SAVING_GAME_PS2,
+ MSG_ID_SAVING_GAME_XBOX,
+ MSG_ID_SAVING_GAME_XBOX_HD,
+ MSG_ID_FORMATTING_GC,
+ MSG_ID_FORMATTING_PS2,
+ MSG_ID_FORMATTING_XBOX,
+ MSG_ID_DELETING_GC,
+ MSG_ID_DELETING_PS2,
+ MSG_ID_DELETING_XBOX,
+ MSG_ID_LOADING_MISSION,
+ MSG_ID_PROGRESSIVE_SCAN_TEST,
+#ifdef RAD_WIN32
+ MSG_ID_LOADING_GAME_PC,
+ MSG_ID_SAVING_GAME_PC,
+ MSG_ID_AUTO_LOADING_GAME_PC,
+#endif
+
+ NUM_MESSAGE_ID
+ };
+
+ CGuiScreenMessage( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMessage();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+ static void Display( int messageIndex, CGuiEntity* pCallback = NULL );
+
+ static void FormatMessage( Scrooby::Text* pText,
+ UnicodeChar* originalStringBuffer = NULL,
+ int stringBufferLength = -1 );
+ static void GetControllerDisconnectedMessage(int controllerId, char *str, int max_char);
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ CGuiMenu* m_pMenu;
+
+ Scrooby::Text* m_messageText;
+ static int s_messageIndex;
+ static CGuiEntity* s_pMessageCallback;
+ static int s_ControllerDisconnectedPort;
+
+ Scrooby::Sprite* m_messageIcon;
+ unsigned int m_elapsedTime;
+
+ UnicodeChar m_originalStringBuffer[ 256 ];
+ static void ConvertUnicodeToChar(char *str, P3D_UNICODE* uni_str, int max_char);
+
+};
+
+#endif // GUISCREENMESSAGE_H
diff --git a/game/code/presentation/gui/guiscreenprompt.cpp b/game/code/presentation/gui/guiscreenprompt.cpp
new file mode 100644
index 0000000..a85329c
--- /dev/null
+++ b/game/code/presentation/gui/guiscreenprompt.cpp
@@ -0,0 +1,498 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPrompt
+//
+// Description: Implementation of the CGuiScreenPrompt class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/guiscreenprompt.h>
+#include <presentation/gui/guiscreenmessage.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guiscreenmemorycard.h>
+#include <presentation/gui/utility/specialfx.h>
+
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <mission/mission.h>
+#include <gameflow/gameflow.h>
+
+#include <p3d/unicode.hpp>
+#include <raddebug.hpp> // Foundation
+#include <layer.h>
+#include <page.h>
+#include <screen.h>
+#include <text.h>
+#include <sprite.h>
+#include <group.h>
+#include <pure3dobject.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+int CGuiScreenPrompt::s_messageIndex = 0;
+int CGuiScreenPrompt::s_numResponses = 0;
+CGuiMenuPrompt::ePromptResponse CGuiScreenPrompt::s_responses[ CGuiMenuPrompt::MAX_NUM_RESPONSES ];
+CGuiEntity* CGuiScreenPrompt::s_pPromptCallback = NULL;
+bool CGuiScreenPrompt::s_defaultToNo = true;
+
+const float ERROR_PROMPT_FADE_TIME = 100.0f;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenPrompt::CGuiScreenPrompt
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPrompt::CGuiScreenPrompt
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent,
+ eGuiWindowID windowID
+)
+: CGuiScreen( pScreen, pParent, windowID ),
+ m_pMenu( NULL ),
+ m_messageIcon( NULL ),
+ m_elapsedTime( 0 ),
+ m_xboxDashboardLabel( NULL ),
+ m_promptMessage( NULL ),
+ m_tvFrame( NULL )
+{
+MEMTRACK_PUSH_GROUP( "GUIScreenPrompt" );
+ if( m_screenCover != NULL )
+ {
+ m_screenCover->SetAlpha( 0.0f );
+ m_screenCover = NULL;
+ }
+
+ m_originalStringBuffer[ 0 ] = '\0';
+
+ // Create a menu.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "Prompt" );
+ m_pMenu = new CGuiMenuPrompt( this, pPage );
+ rAssert( m_pMenu != NULL );
+
+ // TC: quick & dirty hack, no time to make this pretty!
+ //
+ m_pMenu->GetMenuItem( 0 )->m_defaultColour = tColour( 255, 255, 255 );
+
+ if( windowID == GUI_SCREEN_ID_ERROR_PROMPT )
+ {
+ pPage = m_pScroobyScreen->GetPage( "ErrorPrompt" );
+ rAssert( pPage != NULL );
+
+// m_messageIcon = pPage->GetSprite( "ErrorIcon" );
+// rAssert( m_messageIcon );
+
+ m_xboxDashboardLabel = pPage->GetGroup( "XboxDashboard" );
+ rAssert( m_xboxDashboardLabel != NULL );
+ m_xboxDashboardLabel->SetVisible( false );
+
+ this->SetFadeTime( ERROR_PROMPT_FADE_TIME );
+
+#ifdef RAD_XBOX
+ // wrap "Go to Xbox Dashboard" text
+ //
+ Scrooby::Group* xboxDashboard = pPage->GetGroup( "XboxDashboard" );
+ rAssert( xboxDashboard != NULL );
+
+ Scrooby::Text* pText = xboxDashboard->GetText( "GotoDashboard" );
+ if( pText != NULL )
+ {
+ pText->SetTextMode( Scrooby::TEXT_WRAP );
+ }
+#endif
+ }
+ else
+ {
+ this->SetSlidingEnabled( SCREEN_FX_SLIDE_Y, true );
+
+ pPage = m_pScroobyScreen->GetPage( "GenericPrompt" );
+ rAssert( pPage != NULL );
+ }
+
+ Scrooby::Page* messageBoxPage = m_pScroobyScreen->GetPage( "MessageBox" );
+ if( messageBoxPage != NULL )
+ {
+ this->AutoScaleFrame( messageBoxPage );
+
+ Scrooby::Sprite* messageIcon = messageBoxPage->GetSprite( "ErrorIcon" );
+ if( messageIcon != NULL )
+ {
+ messageIcon->ResetTransformation();
+ messageIcon->ScaleAboutCenter( MESSAGE_ICON_CORRECTION_SCALE );
+ }
+ }
+
+ m_promptMessage = pPage->GetText( "Message" );
+ rAssert( m_promptMessage );
+ m_promptMessage->SetTextMode( Scrooby::TEXT_WRAP );
+ m_promptMessage->ResetTransformation();
+ m_promptMessage->ScaleAboutCenter(0.9f);
+
+
+ pPage = m_pScroobyScreen->GetPage( "TVFrame" );
+ if( pPage != NULL )
+ {
+ m_tvFrame = pPage->GetLayer( "TVFrame" );
+ }
+MEMTRACK_POP_GROUP( "GUIScreenPrompt" );
+}
+
+
+//===========================================================================
+// CGuiScreenPrompt::~CGuiScreenPrompt
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPrompt::~CGuiScreenPrompt()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenPrompt::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPrompt::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+#ifdef RAD_PS2
+ case GUI_MSG_CONTROLLER_START:
+ {
+ if ( GetGameFlow()->GetCurrentContext() == CONTEXT_PAUSE )
+ if ( s_pPromptCallback )
+ s_pPromptCallback->HandleMessage( GUI_MSG_PROMPT_START_RESPONSE );
+
+ break;
+ }
+#endif
+ case GUI_MSG_UPDATE:
+ {
+ m_elapsedTime += param1;
+
+ // pulse message icon
+ //
+ float scale = GuiSFX::Pulse( (float)m_elapsedTime,
+ 500.0f,
+ 1.0f,
+ 0.1f );
+
+ if( m_messageIcon != NULL )
+ {
+ m_messageIcon->ResetTransformation();
+ m_messageIcon->ScaleAboutCenter( scale );
+ }
+
+ if (!m_pMenu->HasSelectionBeenMade())
+ {
+ rAssert( s_pPromptCallback );
+ s_pPromptCallback->HandleMessage( GUI_MSG_PROMPT_UPDATE, param1, param2);
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+#ifdef RAD_XBOX
+ if( m_ID == GUI_SCREEN_ID_ERROR_PROMPT )
+ {
+ // special case for going to xbox dashboard on the
+ // "full xbox hard disk" error message
+ //
+ rAssert( s_pPromptCallback );
+ rAssert( m_promptMessage );
+ s_pPromptCallback->HandleMessage( GUI_MSG_MENU_PROMPT_RESPONSE,
+ m_promptMessage->GetIndex(),
+ CGuiMenuPrompt::RESPONSE_MANAGE_MEMCARDS );
+ }
+#endif
+ // ignore BACK inputs, thereby, not allowing users to back
+ // out of prompt
+ return;
+
+ break;
+ }
+
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ rAssert( m_pMenu );
+ CGuiMenuPrompt::ePromptResponse response = m_pMenu->GetResponse( param1 );
+
+ rAssert( s_pPromptCallback );
+ rAssert( m_promptMessage );
+ enum eGuiMessage message;
+ if (m_ID==GUI_SCREEN_ID_GENERIC_PROMPT)
+ message = GUI_MSG_MENU_PROMPT_RESPONSE;
+ else if (m_ID==GUI_SCREEN_ID_ERROR_PROMPT)
+ message = GUI_MSG_ERROR_PROMPT_RESPONSE;
+ else
+ {
+ message = GUI_MSG_MENU_PROMPT_RESPONSE;
+ rTuneAssert(!"not reached");
+ }
+ s_pPromptCallback->HandleMessage( message,
+ m_promptMessage->GetIndex(),
+ response );
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+ else if( m_state == GUI_WINDOW_STATE_OUTRO )
+ {
+ // TC: This is a nasty hack! GOD, please forgive me, as this is the
+ // easiest thing for me to do w/ just a few days left before beta.
+ //
+ if( m_ID == GUI_SCREEN_ID_GENERIC_PROMPT && s_messageIndex == PROMPT_CONFIRM_NEW_GAME )
+ {
+ rAssert( m_p3dObject != NULL );
+
+ tMultiController* multiController = m_p3dObject->GetMultiController();
+ if( multiController != NULL )
+ {
+ const float NUM_FADE_OUT_FRAMES = 15.0f;
+ float currentFrame = multiController->GetFrame();
+ float numRemainingFrames = multiController->GetNumFrames() - currentFrame;
+
+ if( numRemainingFrames < NUM_FADE_OUT_FRAMES )
+ {
+ // fade out TV frame
+ //
+ if( m_tvFrame != NULL )
+ {
+ float alpha = numRemainingFrames / NUM_FADE_OUT_FRAMES;
+
+ // decrease fade rate for low alpha values
+ alpha *= alpha;
+
+ if( alpha > 0.0f && alpha < 1.0f )
+ {
+ m_tvFrame->SetAlpha( alpha );
+ }
+ else
+ {
+ m_tvFrame->SetAlpha( 0.0f );
+ }
+ }
+
+ // TC [HACK]: To prevent any clipping in homer's mouth
+ // in the last few frames.
+ //
+ if( numRemainingFrames < 1.0f )
+ {
+ this->RestoreScreenCover();
+ }
+ }
+ }
+ }
+
+ if( m_numTransitionsPending < 0 )
+ {
+ // restore original string buffer
+ //
+ if( m_originalStringBuffer[ 0 ] != '\0' )
+ {
+ rAssert( m_promptMessage != NULL );
+ UnicodeChar* stringBuffer = m_promptMessage->GetStringBuffer();
+ rAssert( stringBuffer != NULL );
+
+ p3d::UnicodeStrCpy( static_cast<P3D_UNICODE*>( m_originalStringBuffer ),
+ static_cast<P3D_UNICODE*>( stringBuffer ),
+ sizeof( m_originalStringBuffer ) );
+
+ m_originalStringBuffer[ 0 ] = '\0';
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenPrompt::Display
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPrompt::Display( int messageIndex,
+ CGuiEntity* pCallback,
+ int numResponses,
+ CGuiMenuPrompt::ePromptResponse* responses )
+{
+ s_messageIndex = messageIndex;
+ s_pPromptCallback = pCallback;
+ s_numResponses = numResponses;
+
+ for( int i = 0; i < numResponses; i++ )
+ {
+ s_responses[ i ] = responses[ i ];
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenPrompt::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPrompt::InitIntro()
+{
+ rAssert( m_promptMessage );
+ rAssert( s_messageIndex >= 0 &&
+ s_messageIndex < m_promptMessage->GetNumOfStrings() );
+
+#ifdef RAD_XBOX
+ if( m_ID == GUI_SCREEN_ID_ERROR_PROMPT )
+ {
+ // special case for "full xbox hard disk" error message
+ //
+ bool showLabel = (s_messageIndex == GetErrorMessageIndex( NoFreeSpace, ERROR_DURING_CHECKING ));
+ rAssert( m_xboxDashboardLabel != NULL );
+ m_xboxDashboardLabel->SetVisible( showLabel );
+ }
+#endif
+
+ m_promptMessage->SetIndex( s_messageIndex );
+
+ CGuiScreenMessage::FormatMessage( m_promptMessage,
+ m_originalStringBuffer,
+ sizeof( m_originalStringBuffer ) );
+
+// rAssert( m_messageIcon );
+// m_messageIcon->ResetTransformation();
+
+ rAssert( m_pMenu );
+ m_pMenu->SetNumResponses( s_numResponses );
+ m_pMenu->Reset();
+
+ for( int i = 0; i < s_numResponses; i++ )
+ {
+ m_pMenu->SetResponse( i, s_responses[ i ] );
+
+ if( s_defaultToNo && s_responses[ i ] == CGuiMenuPrompt::RESPONSE_NO )
+ {
+ // set NO as default response
+ //
+ m_pMenu->Reset( i );
+ }
+ }
+
+ m_elapsedTime = 0;
+}
+
+
+//===========================================================================
+// CGuiScreenPrompt::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPrompt::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenPrompt::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPrompt::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/guiscreenprompt.h b/game/code/presentation/gui/guiscreenprompt.h
new file mode 100644
index 0000000..b39e485
--- /dev/null
+++ b/game/code/presentation/gui/guiscreenprompt.h
@@ -0,0 +1,166 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPrompt
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENPROMPT_H
+#define GUISCREENPROMPT_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/guimenu.h>
+
+#include <strings/unicodestring.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Group;
+}
+
+enum ePromptQuestion
+{
+ PROMPT_CONFIRM_QUIT,
+ PROMPT_CONFIRM_SAVE_BEFORE_QUIT,
+ PROMPT_CONFIRM_RESTART,
+ PROMPT_CONFIRM_ABORT,
+ PROMPT_CONFIRM_RACE_AGAIN,
+ PROMPT_CONFIRM_NEW_GAME,
+ PROMPT_CONFIRM_START_MISSION,
+ PROMPT_LOAD_CONFIRM_GC,
+ PROMPT_LOAD_CONFIRM_PS2,
+ PROMPT_LOAD_CONFIRM_XBOX,
+
+ PROMPT_SAVE_CONFIRM_GC,
+ PROMPT_SAVE_CONFIRM_PS2,
+ PROMPT_SAVE_CONFIRM_XBOX,
+ PROMPT_SAVE_CONFIRM_OVERWRITE_GC,
+ PROMPT_SAVE_CONFIRM_OVERWRITE_PS2,
+ PROMPT_SAVE_CONFIRM_OVERWRITE_XBOX,
+
+ PROMPT_LOAD_SUCCESSFUL,
+ PROMPT_SAVE_SUCCESSFUL,
+
+ PROMPT_FORMAT_CONFIRM_GC,
+ PROMPT_FORMAT_CONFIRM_PS2,
+ PROMPT_FORMAT_CONFIRM_XBOX,
+
+ PROMPT_FORMAT_CONFIRM2_GC,
+ PROMPT_FORMAT_CONFIRM2_PS2,
+ PROMPT_FORMAT_CONFIRM2_XBOX,
+
+ PROMPT_FORMAT_SUCCESS_GC,
+ PROMPT_FORMAT_SUCCESS_PS2,
+ PROMPT_FORMAT_SUCCESS_XBOX,
+
+ PROMPT_FORMAT_FAIL_GC,
+ PROMPT_FORMAT_FAIL_PS2,
+ PROMPT_FORMAT_FAIL_XBOX,
+
+ PROMPT_DELETE_CORRUPT_SUCCESS_GC,
+ PROMPT_DELETE_CORRUPT_SUCCESS_PS2,
+ PROMPT_DELETE_CORRUPT_SUCCESS_XBOX,
+
+ PROMPT_DELETE_CORRUPT_FAIL_GC,
+ PROMPT_DELETE_CORRUPT_FAIL_PS2,
+ PROMPT_DELETE_CORRUPT_FAIL_XBOX,
+
+ PROMPT_LOAD_CARD_EMPTY_GC,
+ PROMPT_LOAD_CARD_EMPTY_PS2,
+ PROMPT_LOAD_CARD_EMPTY_XBOX,
+ PROMPT_LOAD_CARD_EMPTY_XBOX_HD,
+#ifdef RAD_WIN32
+ PROMPT_LOAD_CARD_EMPTY_PC,
+#endif
+
+ PROMPT_DISPLAY_PROGRESSIVE_SCAN,
+ PROMPT_DISPLAY_PROGRESSIVE_SCAN_PS2,
+ PROMPT_DISPLAY_PROGRESSIVE_SCAN_CONFIRM,
+ PROMPT_DISPLAY_PROGRESSIVE_SCAN_TEST,
+
+ PROMPT_LOAD_DELETE_CORRUPT_GC,
+ PROMPT_LOAD_DELETE_CORRUPT_PS2,
+ PROMPT_LOAD_DELETE_CORRUPT_XBOX,
+ PROMPT_LOAD_DELETE_CORRUPT_XBOX_HD,
+
+ PROMPT_HD_FULL_XBOX,
+#ifdef RAD_WIN32
+ PROMPT_CONFIRM_RESTOREALLDEFAULTS,
+ PROMPT_CONFIRM_QUIT_TO_SYSTEM,
+ PROMPT_CONFIRM_SAVE_BEFORE_QUITTOSYSTEM,
+#endif
+ NUM_PROMPT_QUESTIONS
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenPrompt : public CGuiScreen
+{
+public:
+ CGuiScreenPrompt( Scrooby::Screen* pScreen,
+ CGuiEntity* pParent,
+ eGuiWindowID windowID = GUI_SCREEN_ID_GENERIC_PROMPT );
+
+ virtual ~CGuiScreenPrompt();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+ static void Display( int messageIndex,
+ CGuiEntity* pCallback,
+ int numResponses,
+ CGuiMenuPrompt::ePromptResponse* responses );
+
+ static void EnableDefaultToNo( bool enable );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ CGuiMenuPrompt* m_pMenu;
+
+ Scrooby::Sprite* m_messageIcon;
+ unsigned int m_elapsedTime;
+
+ Scrooby::Group* m_xboxDashboardLabel;
+
+ Scrooby::Text* m_promptMessage;
+ static int s_messageIndex;
+ static int s_numResponses;
+ static CGuiMenuPrompt::ePromptResponse s_responses[ CGuiMenuPrompt::MAX_NUM_RESPONSES ];
+ static CGuiEntity* s_pPromptCallback;
+ static bool s_defaultToNo;
+
+ UnicodeChar m_originalStringBuffer[ 256 ];
+
+ Scrooby::Layer* m_tvFrame;
+
+};
+
+inline void CGuiScreenPrompt::EnableDefaultToNo( bool enable )
+{
+ s_defaultToNo = enable;
+}
+
+#endif // GUISCREENPROMPT_H
diff --git a/game/code/presentation/gui/guisystem.cpp b/game/code/presentation/gui/guisystem.cpp
new file mode 100644
index 0000000..f7523e6
--- /dev/null
+++ b/game/code/presentation/gui/guisystem.cpp
@@ -0,0 +1,1845 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiSystem
+//
+// Description: Implementation of the CGuiSystem class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/21 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/bootup/guimanagerlanguage.h>
+#include <presentation/gui/bootup/guimanagerbootup.h>
+#include <presentation/gui/bootup/guiscreenlanguage.h>
+#include <presentation/gui/backend/guimanagerbackend.h>
+#include <presentation/gui/frontend/guimanagerfrontend.h>
+#include <presentation/gui/minigame/guimanagerminigame.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/guiuserinputhandler.h>
+#include <presentation/tutorialmode.h>
+
+#include <data/gamedatamanager.h>
+#include <main/game.h>
+#include <main/platform.h>
+#include <main/commandlineoptions.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <mission/missionscriptloader.h>
+#include <input/inputmanager.h>
+#include <gameflow/gameflow.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/renderlayer.h>
+
+#include <p3d/view.hpp>
+#include <raddebug.hpp> // Foundation
+#include <raddebugwatch.hpp>
+#include <page.h>
+#include <polygon.h>
+#include <resourcemanager.h>
+#include <screen.h>
+#include <sprite.h>
+#include <text.h>
+#include <feloaders.h>
+
+// Static pointer to instance of singleton.
+CGuiSystem* CGuiSystem::spInstance = NULL;
+
+// Scrooby simulation time, needed for DrawFrame() render loop
+//
+unsigned int g_scroobySimulationTime = 0;
+
+//===========================================================================
+// Local Constants
+//===========================================================================
+
+#define LOAD_LEVEL_SPECIFIC_PROJECT
+
+/*
+#ifdef RAD_GAMECUBE
+ float g_gcnScreenScaleX = 1.1f;
+ float g_gcnScreenScaleY = 1.0f;
+#endif
+
+#ifdef RAD_PS2
+ float g_ps2screenScale = 1.096f;
+#endif
+*/
+
+#ifdef DEBUGWATCH
+ const char* GUI_WATCHER_NAMESPACE = "GUI System";
+ float g_wGuiSimulationTimeFactor = 1.0f;
+ int g_wGuiMessage = 0;
+ int g_wGuiMessageParam1 = 0;
+ int g_wGuiMessageParam2 = 0;
+
+ static void SendMsgToGuiSystem()
+ {
+ GetGuiSystem()->HandleMessage( static_cast<eGuiMessage>( g_wGuiMessage ),
+ g_wGuiMessageParam1,
+ g_wGuiMessageParam2 );
+ }
+
+ namespace Scrooby
+ {
+ extern float g_CameraNearPlane;
+ extern float g_CameraFarPlane;
+ }
+
+/*
+#ifdef RAD_PS2
+ static void UpdatePS2ScreenScale()
+ {
+ Scrooby::App::GetInstance()->EnableScreenScaling( true,
+ g_ps2screenScale,
+ g_ps2screenScale );
+ }
+#endif
+*/
+
+ // for Formatting Scrooby Drawables in Run-time
+ //
+ static const char* WATCHER_NAMESPACE_SCROOBY = "GUI System - Scrooby";
+
+ static Scrooby::Page* g_wCurrentScroobyPage = NULL;
+ static char g_wScroobyPageName[ 32 ];
+ static void SetCurrentScroobyPage()
+ {
+ Scrooby::Project* currentProject = Scrooby::App::GetInstance()->GetProject();
+ rAssert( currentProject );
+
+ Scrooby::Screen* currentScreen = currentProject->GetCurrentScreen();
+ rAssert( currentScreen );
+
+ g_wCurrentScroobyPage = currentScreen->GetPage( g_wScroobyPageName );
+ rWarning( g_wCurrentScroobyPage );
+ }
+
+ static Scrooby::Drawable* g_wCurrentScroobyDrawable = NULL;
+
+ static int g_wScroobyPosX = 0;
+ static int g_wScroobyPosY = 0;
+ static void SetScroobyDrawablePosition()
+ {
+ if( g_wCurrentScroobyDrawable != NULL )
+ {
+ g_wCurrentScroobyDrawable->SetPosition( g_wScroobyPosX, g_wScroobyPosY );
+ }
+ }
+
+ static float g_wScroobyAlpha = 0.0f;
+ static void SetScroobyDrawableAlpha()
+ {
+ if( g_wCurrentScroobyDrawable != NULL )
+ {
+ g_wCurrentScroobyDrawable->SetAlpha( g_wScroobyAlpha );
+ }
+ }
+
+ static void UpdateScroobyDrawableAttributes()
+ {
+ if( g_wCurrentScroobyDrawable != NULL )
+ {
+ g_wCurrentScroobyDrawable->GetOriginPosition( g_wScroobyPosX,
+ g_wScroobyPosY );
+ g_wScroobyAlpha = g_wCurrentScroobyDrawable->GetAlpha();
+ }
+ }
+
+ static char g_wScroobyTextName[ 32 ];
+ static void SetCurrentScroobyDrawableAsText()
+ {
+ if( g_wCurrentScroobyPage != NULL )
+ {
+ g_wCurrentScroobyDrawable = g_wCurrentScroobyPage->GetText( g_wScroobyTextName );
+ UpdateScroobyDrawableAttributes();
+ }
+
+ rWarning( g_wCurrentScroobyDrawable );
+ }
+
+ static char g_wScroobySpriteName[ 32 ];
+ static void SetCurrentScroobyDrawableAsSprite()
+ {
+ if( g_wCurrentScroobyPage != NULL )
+ {
+ g_wCurrentScroobyDrawable = g_wCurrentScroobyPage->GetSprite( g_wScroobySpriteName );
+ UpdateScroobyDrawableAttributes();
+ }
+
+ rWarning( g_wCurrentScroobyDrawable );
+ }
+
+ static char g_wScroobyPolygonName[ 32 ];
+ static void SetCurrentScroobyDrawableAsPolygon()
+ {
+ if( g_wCurrentScroobyPage != NULL )
+ {
+ g_wCurrentScroobyDrawable = g_wCurrentScroobyPage->GetPolygon( g_wScroobyPolygonName );
+ UpdateScroobyDrawableAttributes();
+ }
+
+ rWarning( g_wCurrentScroobyDrawable );
+ }
+
+ int g_currentLanguage = 0;
+
+ static void ToggleNextLanguage()
+ {
+ g_currentLanguage = (g_currentLanguage + 1) % NUM_SRR2_LANGUAGES;
+ CGuiTextBible::SetCurrentLanguage( SRR2_LANGUAGE[ g_currentLanguage ] );
+ }
+
+#endif // DEBUGWATCH
+
+const char* PROJECT_FILE_LANGUAGE = "art\\frontend\\scrooby\\language.p3d";
+const char* PROJECT_FILE_BOOTUP = "art\\frontend\\scrooby\\bootup.p3d";
+const char* PROJECT_FILE_BACKEND = "art\\frontend\\scrooby\\backend.p3d";
+const char* PROJECT_FILE_FRONTEND = "art\\frontend\\scrooby\\frontend.p3d";
+const char* PROJECT_FILE_MINIGAME = "art\\frontend\\scrooby\\minigame.p3d";
+const char* PROJECT_FILE_INGAME = "art\\frontend\\scrooby\\ingame.p3d";
+
+const char* LICENSE_SCREEN_IMAGE_DIR = "art\\frontend\\dynaload\\images\\license\\";
+#ifdef RAD_WIN32
+const char* MOUSE_CURSOR_DIR = "art\\frontend\\dynaload\\images\\";
+#endif
+
+const char* INGAME_LEVEL_PROJECT_FILES[] =
+{
+ "art\\frontend\\scrooby\\ingamel1.p3d",
+ "art\\frontend\\scrooby\\ingamel2.p3d",
+ "art\\frontend\\scrooby\\ingamel3.p3d",
+ "art\\frontend\\scrooby\\ingamel4.p3d",
+ "art\\frontend\\scrooby\\ingamel5.p3d",
+ "art\\frontend\\scrooby\\ingamel6.p3d",
+ "art\\frontend\\scrooby\\ingamel7.p3d"
+};
+
+const char* TEXT_BIBLE_NAME = "srr2";
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//==============================================================================
+// CGuiSystem::CreateInstance
+//==============================================================================
+//
+// Description: - Creates the CGuiSystem.
+//
+// Parameters: None.
+//
+// Return: Pointer to the CGuiSystem.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+CGuiSystem* CGuiSystem::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "CGUISystem" );
+ spInstance = new CGuiSystem;
+ rAssert( spInstance != NULL );
+MEMTRACK_POP_GROUP( "CGUISystem" );
+
+ return spInstance;
+}
+
+//==============================================================================
+// CGuiSystem::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the GUI system.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void CGuiSystem::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete spInstance;
+ spInstance = NULL;
+}
+
+//==============================================================================
+// CGuiSystem::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the CGuiSystem singleton.
+// - Creates the CGuiSystem if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the CGuiSystem.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+CGuiSystem* CGuiSystem::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+//===========================================================================
+// CGuiSystem::CGuiSystem
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CGuiSystem::CGuiSystem()
+: CGuiEntity( NULL ),
+ m_state( GUI_UNINITIALIZED ),
+ m_pTextBible( NULL ),
+ m_pManagerLanguage( NULL ),
+ m_pManagerBootUp( NULL ),
+ m_pManagerBackEnd( NULL ),
+ m_pManagerFrontEnd( NULL ),
+ m_pManagerMiniGame( NULL ),
+ m_pManagerInGame( NULL ),
+ m_pUserInputHandlers( NULL ),
+ m_numUserInputHandlers( 0 ),
+ m_registeredUserInputHandlers( 0 ),
+ m_primaryController(-1),
+ m_pApp( NULL ),
+ m_pProject( NULL ),
+ m_pLevelProject( NULL ),
+ m_pBackendProject( NULL ),
+ m_pLoadingCallback( NULL ),
+ m_isSplashScreenFinished( false ),
+ m_isRadarEnabled( true ),
+ m_isShowCreditsUponReturnToFE( false )
+{
+ m_pTextBible = new CGuiTextBible;
+ rAssert( m_pTextBible != NULL );
+}
+
+//===========================================================================
+// CGuiSystem::~CGuiSystem
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CGuiSystem::~CGuiSystem()
+{
+ // unload backend Scrooby project
+ //
+ if( m_pBackendProject != NULL )
+ {
+ m_pApp->UnloadProject( m_pBackendProject );
+ m_pBackendProject = NULL;
+ }
+
+ // delete text bible
+ //
+ if( m_pTextBible != NULL )
+ {
+ delete m_pTextBible;
+ m_pTextBible = NULL;
+ }
+
+ // delete all GUI managers (if not already deleted)
+ //
+ if( m_pManagerFrontEnd != NULL )
+ {
+ delete m_pManagerFrontEnd;
+ m_pManagerFrontEnd = NULL;
+ }
+
+ if( m_pManagerMiniGame != NULL )
+ {
+ delete m_pManagerMiniGame;
+ m_pManagerMiniGame = NULL;
+ }
+
+ if( m_pManagerInGame != NULL )
+ {
+ delete m_pManagerInGame;
+ m_pManagerInGame = NULL;
+ }
+
+ if( m_pManagerBackEnd != NULL )
+ {
+ delete m_pManagerBackEnd;
+ m_pManagerBackEnd = NULL;
+ }
+
+ if( m_pManagerBootUp != NULL )
+ {
+ delete m_pManagerBootUp;
+ m_pManagerBootUp = NULL;
+ }
+
+ if( m_pManagerLanguage != NULL )
+ {
+ delete m_pManagerLanguage;
+ m_pManagerLanguage = NULL;
+ }
+
+ // delete all GUI user input handlers
+ //
+ if(m_pUserInputHandlers != NULL )
+ {
+ for( int i = 0; i < m_numUserInputHandlers; i++ )
+ {
+ if( m_pUserInputHandlers[ i ] != NULL )
+ {
+ m_pUserInputHandlers[ i ]->Release();
+ m_pUserInputHandlers[ i ] = NULL;
+ }
+ }
+
+ delete [] m_pUserInputHandlers;
+ m_pUserInputHandlers = NULL;
+ }
+
+ // delete Scrooby App singleton
+ //
+ if( m_pApp != NULL )
+ {
+ Scrooby::App::DeleteInstance();
+ m_pApp = NULL;
+ }
+}
+
+//===========================================================================
+// CGuiSystem::Init
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::Init()
+{
+MEMTRACK_PUSH_GROUP( "CGUISystem" );
+ rAssert( m_state == GUI_UNINITIALIZED );
+
+ m_pApp = Scrooby::App::GetInstance();
+ rAssert( m_pApp != NULL );
+
+ // set Scrooby to use full image names depending on whether or not
+ // resource files are joined together into single P3D project file
+ //
+ m_pApp->SetFullImageNames( CommandLineOptions::Get( CLO_FE_UNJOINED ) );
+
+ // set FE text bible loader to only load one string table for the
+ // current language
+ //
+ FeTextBibleLoader::SetOnlyLoadCurrentLanguage( true );
+
+#if defined( PAL ) && defined( RAD_XBOX ) && !defined( RAD_RELEASE )
+ // TC: disable this for Xbox non-release PAL builds so that I can
+ // change languages on-the-fly w/ the Watcher
+ //
+ FeTextBibleLoader::SetOnlyLoadCurrentLanguage( false );
+#endif
+
+ // setup the GUI render layer
+ //
+ GetRenderManager()->mpLayer(RenderEnums::GUI)->AddGuts( m_pApp );
+ GetRenderManager()->mpLayer(RenderEnums::GUI)->FreezeCorpse();
+ GetRenderManager()->mpLayer(RenderEnums::GUI)->pView( 0 )->SetClearColour( tColour( 0, 0, 0 ) );
+
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ // create array of user input handlers
+ //
+ m_numUserInputHandlers = GetInputManager()->GetMaxControllers();
+ m_pUserInputHandlers = new CGuiUserInputHandler*[ m_numUserInputHandlers ];
+ rAssert( m_pUserInputHandlers != NULL );
+
+ // create each user input handler
+ //
+ for( int i = 0; i < m_numUserInputHandlers; i++ )
+ {
+ m_pUserInputHandlers[ i ] = new CGuiUserInputHandler;
+ rAssert( m_pUserInputHandlers[ i ] != NULL );
+ m_pUserInputHandlers[ i ]->AddRef();
+ }
+
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+
+ // register GUI as game data handler
+ //
+ GetGameDataManager()->RegisterGameData( this, 1, "GUI System" );
+
+#if defined( PAL ) && !defined( RAD_DEMO )
+/*
+ #ifdef RAD_GAMECUBE
+ // GC ONLY: load language configuration file
+ //
+ HeapMgr()->PushHeap( GMA_TEMP );
+ GetMissionScriptLoader()->LoadScriptAsync( "language.ini" );
+ HeapMgr()->PopHeap( GMA_TEMP );
+ #endif
+*/
+ // load language Scrooby project (into FE heap)
+ //
+ GetLoadingManager()->AddRequest( FILEHANDLER_SCROOBY,
+ PROJECT_FILE_LANGUAGE,
+ GMA_LEVEL_FE,
+ SCROOBY_INVENTORY_LANGUAGE,
+ SCROOBY_INVENTORY_LANGUAGE );
+
+ m_state = LANGUAGE_LOADING;
+#else
+ this->OnInitBootUp();
+#endif // PAL
+
+#ifdef DEBUGWATCH
+ this->RegisterWatcherStuff();
+#endif
+ MEMTRACK_POP_GROUP( "CGUISystem" );
+}
+
+//===========================================================================
+// CGuiSystem::Update
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::Update( unsigned int elapsedTime )
+{
+#ifdef RAD_RELEASE
+ // TC [HACK]: To avoid any choppiness in FE animations
+ //
+ const unsigned int MAX_ELAPSED_TIME = 100; // in msec
+ if( elapsedTime > MAX_ELAPSED_TIME )
+ {
+ rTunePrintf( "*** WARNING: GUI System elapsed time (%d ms) exceeded MAX_ELAPSED_TIME (%d ms)!\n",
+ elapsedTime, MAX_ELAPSED_TIME );
+
+ elapsedTime = 20;
+ }
+#endif
+
+#ifdef DEBUGWATCH
+ elapsedTime = static_cast<unsigned int>( g_wGuiSimulationTimeFactor *
+ elapsedTime );
+#endif
+
+ // check for repeated inputs
+ for( int i = 0; i < m_numUserInputHandlers; i++ )
+ {
+ m_pUserInputHandlers[ i ]->Update( elapsedTime, i );
+ }
+
+ // set scrooby simulation time for use in the rendering loop
+ //
+ g_scroobySimulationTime = elapsedTime;
+
+ // send update message to current GUI manager
+ //
+ CGuiManager* currentManager = this->GetCurrentManager();
+ if( currentManager != NULL )
+ {
+ currentManager->HandleMessage( GUI_MSG_UPDATE, elapsedTime );
+ }
+
+ if( m_state == DEMO_ACTIVE )
+ {
+ if( m_pManagerInGame != NULL )
+ {
+ m_pManagerInGame->HandleMessage( GUI_MSG_UPDATE, elapsedTime );
+ }
+ }
+}
+
+//===========================================================================
+// CGuiSystem::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiSystem::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ rAssertMsg( 0, "*** Call GuiSystem::Update() instead! ***" );
+
+ break;
+ }
+
+ case GUI_MSG_INIT_FRONTEND:
+ {
+ this->OnInitFrontEnd();
+
+ break;
+ }
+ case GUI_MSG_RELEASE_FRONTEND:
+ {
+ this->OnReleaseFrontEnd();
+
+ break;
+ }
+ case GUI_MSG_RUN_FRONTEND:
+ {
+ // thaw frontend render layer
+ GetRenderManager()->mpLayer(RenderEnums::GUI)->Thaw();
+
+ m_pApp->SetProject( m_pProject );
+
+ m_state = FRONTEND_ACTIVE;
+
+ // start the frontend manager
+ rAssert( m_pManagerFrontEnd );
+ if( param1 != 0 )
+ {
+ m_pManagerFrontEnd->Start( static_cast<CGuiWindow::eGuiWindowID>( param1 ) );
+ }
+ else
+ {
+ m_pManagerFrontEnd->Start();
+ }
+
+/*
+#ifdef RAD_GAMECUBE
+ // enable screen scaling (for GameCube)
+ m_pApp->EnableScreenScaling( true, g_gcnScreenScaleX, g_gcnScreenScaleY );
+#endif
+
+#ifdef RAD_PS2
+ // enable screen scaling (for PS2)
+ m_pApp->EnableScreenScaling( true, g_ps2screenScale, g_ps2screenScale );
+#endif
+*/
+
+ break;
+ }
+
+ case GUI_MSG_INIT_MINIGAME:
+ {
+ this->OnInitMiniGame();
+
+ break;
+ }
+ case GUI_MSG_RELEASE_MINIGAME:
+ {
+ this->OnReleaseMiniGame();
+
+ break;
+ }
+ case GUI_MSG_RUN_MINIGAME:
+ {
+ m_pApp->SetProject( m_pProject );
+
+ m_state = MINIGAME_ACTIVE;
+
+ // start the minigame manager
+ //
+ rAssert( m_pManagerMiniGame != NULL );
+ if( param1 != 0 )
+ {
+ m_pManagerMiniGame->Start( static_cast<CGuiWindow::eGuiWindowID>( param1 ) );
+ }
+ else
+ {
+ m_pManagerMiniGame->Start();
+ }
+
+ break;
+ }
+
+ case GUI_MSG_INIT_INGAME:
+ {
+ this->OnInitInGame();
+
+ break;
+ }
+ case GUI_MSG_RELEASE_INGAME:
+ {
+ this->OnReleaseInGame();
+
+ break;
+ }
+ case GUI_MSG_RUN_INGAME:
+ {
+ m_pApp->SetProject( m_pProject );
+
+ m_state = INGAME_ACTIVE;
+
+ // start the in-game manager
+ rAssert( m_pManagerInGame );
+ m_pManagerInGame->Start();
+
+ break;
+ }
+
+ case GUI_MSG_INIT_BOOTUP:
+ {
+ this->OnInitBootUp();
+
+ break;
+ }
+ case GUI_MSG_RELEASE_BOOTUP:
+ {
+ this->OnReleaseBootUp();
+
+ break;
+ }
+
+ case GUI_MSG_RUN_BACKEND:
+ {
+ m_pApp->SetProject( m_pBackendProject );
+
+ m_state = GUI_IDLE;
+
+ rAssert( m_pManagerBackEnd );
+ m_pManagerBackEnd->HandleMessage( message, param1, param2 );
+
+ break;
+ }
+ case GUI_MSG_RUN_DEMO:
+ {
+ m_state = DEMO_ACTIVE;
+
+ rAssert( m_pManagerBackEnd );
+ m_pManagerBackEnd->HandleMessage( message, param1, param2 );
+
+ break;
+ }
+ case GUI_MSG_PROJECT_LOAD_COMPLETE:
+ {
+ this->OnProjectLoadComplete( (Scrooby::Project*)param1 );
+
+ break;
+ }
+ default:
+ {
+ // relay message to current GUI manager
+ //
+ CGuiManager* currentManager = this->GetCurrentManager();
+ if( currentManager != NULL )
+ {
+ currentManager->HandleMessage( message, param1, param2 );
+ }
+
+ break;
+ }
+ }
+}
+
+//===========================================================================
+// CGuiSystem::OnProjectLoadComplete
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::OnProjectLoadComplete( Scrooby::Project* pProject )
+{
+MEMTRACK_PUSH_GROUP( "CGUISystem" );
+ rAssert( pProject != NULL );
+ m_pProject = pProject;
+
+ // update reference to text bible
+ //
+ rAssert( m_pTextBible != NULL );
+ m_pTextBible->SetTextBible( TEXT_BIBLE_NAME );
+
+ switch( m_state )
+ {
+ case LANGUAGE_LOADING:
+ {
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+
+ m_pManagerLanguage = new CGuiManagerLanguage( m_pProject, this );
+ rAssert( m_pManagerLanguage != NULL );
+
+ // Populate screens
+ //
+ m_pManagerLanguage->Populate();
+
+ HeapMgr()->PopHeap( GMA_LEVEL_FE );
+
+ m_state = LANGUAGE_ACTIVE;
+
+ // Start it up!
+ //
+ m_pManagerLanguage->Start();
+
+ break;
+ }
+ case BOOTUP_LOADING:
+ {
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+
+ // Create bootup manager (for license screen, etc.)
+ //
+ m_pManagerBootUp = new CGuiManagerBootUp( m_pProject, this );
+ rAssert( m_pManagerBootUp );
+
+ // Populate screens
+ //
+ m_pManagerBootUp->Populate();
+
+ // Start it up!
+ //
+ m_pManagerBootUp->Start();
+
+ m_pApp->SetProject( m_pProject );
+
+ // thaw frontend render layer
+ GetRenderManager()->mpLayer(RenderEnums::GUI)->Thaw();
+
+ m_state = BACKEND_LOADING;
+
+ HeapMgr()->PopHeap( GMA_LEVEL_FE );
+
+ break;
+ }
+ case BACKEND_LOADING:
+ {
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ // now that the text bible loaded from the backend project is residing
+ // in the persistend heap, there is no need to load any more instances
+ // of the text bible from subsequent scrooby project loads
+ //
+ FeTextBibleLoader::SetUseLastLoadedTextBible( true );
+
+ m_pBackendProject = pProject;
+
+ // Create backend manager (for loading screens)
+ //
+ m_pManagerBackEnd = new CGuiManagerBackEnd( m_pProject, this );
+ rAssert( m_pManagerBackEnd );
+
+ // Populate screens
+ //
+ m_pManagerBackEnd->Populate();
+
+ m_pManagerBackEnd->Start();
+
+ if( !CommandLineOptions::Get( CLO_SKIP_FE ) )
+ {
+ m_state = FRONTEND_LOADING_DURING_BOOTUP;
+ }
+ else
+ {
+ m_state = BOOTUP_ACTIVE;
+ }
+
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+
+ break;
+ }
+ case FRONTEND_LOADING_DURING_BOOTUP:
+ {
+ m_state = BOOTUP_ACTIVE;
+
+ // follow-through
+ }
+ case FRONTEND_LOADING:
+ {
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+
+ // Create FE screen manager
+ //
+ m_pManagerFrontEnd = new CGuiManagerFrontEnd( m_pProject, this );
+ rAssert( m_pManagerFrontEnd );
+
+ // Populate screens
+ //
+ m_pManagerFrontEnd->Populate();
+
+ // notify callback
+ //
+ if( m_pLoadingCallback != NULL )
+ {
+ m_pLoadingCallback->OnGuiLoadComplete( IGuiLoadingCallback::GUI_LOADED_FRONT_END );
+ }
+
+ HeapMgr()->PopHeap(GMA_LEVEL_FE);
+
+ break;
+ }
+ case MINIGAME_LOADING:
+ {
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ // create mini-game manager
+ //
+ m_pManagerMiniGame = new CGuiManagerMiniGame( m_pProject, this );
+ rAssert( m_pManagerMiniGame != NULL );
+
+ // Populate screens
+ //
+ m_pManagerMiniGame->Populate();
+
+ // notify callback
+ //
+ if( m_pLoadingCallback != NULL )
+ {
+ m_pLoadingCallback->OnGuiLoadComplete( IGuiLoadingCallback::GUI_LOADED_MINI_GAME );
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+
+ break;
+ }
+ case INGAME_LOADING:
+ {
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ // Create InGame manager
+ //
+#ifdef LOAD_LEVEL_SPECIFIC_PROJECT
+ if( m_pLevelProject == NULL )
+ {
+ m_pLevelProject = m_pProject;
+ }
+ else
+#endif // LOAD_LEVEL_SPECIFIC_PROJECT
+ {
+ m_pManagerInGame = new CGuiManagerInGame( m_pProject, this );
+ rAssert( m_pManagerInGame );
+
+ // Populate screens
+ //
+ m_pManagerInGame->Populate();
+
+ // notify callback
+ //
+ if( m_pLoadingCallback != NULL )
+ {
+ m_pLoadingCallback->OnGuiLoadComplete( IGuiLoadingCallback::GUI_LOADED_IN_GAME );
+ }
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Invalid state!" );
+
+ break;
+ }
+ }
+MEMTRACK_POP_GROUP("CGUISystem");
+}
+
+//===========================================================================
+// CGuiSystem::GotoScreen
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::GotoScreen( unsigned int screenID,
+ unsigned int param1,
+ unsigned int param2,
+ unsigned int windowOptions )
+{
+ CGuiManager* currentManager = this->GetCurrentManager();
+ rAssert( currentManager != NULL );
+
+ if( param1 != 0 || param2 != 0 )
+ {
+ currentManager->HandleMessage( GUI_MSG_SET_GOTO_SCREEN_PARAMETERS,
+ param1, param2 );
+ }
+
+ currentManager->HandleMessage( GUI_MSG_GOTO_SCREEN, screenID, windowOptions );
+}
+
+//===========================================================================
+// CGuiSystem::GetCurrentManager
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CGuiManager* CGuiSystem::GetCurrentManager() const
+{
+ CGuiManager* currentManager = NULL;
+
+ switch( m_state )
+ {
+ case FRONTEND_ACTIVE:
+ {
+ currentManager = m_pManagerFrontEnd;
+
+ break;
+ }
+ case MINIGAME_ACTIVE:
+ {
+ currentManager = m_pManagerMiniGame;
+
+ break;
+ }
+ case INGAME_ACTIVE:
+ {
+ currentManager = m_pManagerInGame;
+
+ break;
+ }
+ case BOOTUP_ACTIVE:
+ case BACKEND_LOADING:
+ case FRONTEND_LOADING_DURING_BOOTUP:
+ {
+ currentManager = m_pManagerBootUp;
+
+ break;
+ }
+ case LANGUAGE_ACTIVE:
+ {
+ currentManager = m_pManagerLanguage;
+
+ break;
+ }
+ case GUI_IDLE:
+ case DEMO_ACTIVE:
+ case FRONTEND_LOADING:
+ case MINIGAME_LOADING:
+ case INGAME_LOADING:
+ {
+ currentManager = m_pManagerBackEnd;
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ return currentManager;
+}
+
+//===========================================================================
+// CGuiSystem::RegisterUserInputHandlers
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::RegisterUserInputHandlers( int controllerIDs )
+{
+ for( int i = 0; i < m_numUserInputHandlers; i++ )
+ {
+ if( (controllerIDs & (1 << i)) > 0 )
+ {
+ GetInputManager()->RegisterMappable( i, m_pUserInputHandlers[ i ] );
+
+ m_registeredUserInputHandlers |= (1 << i);
+ }
+ }
+}
+
+//===========================================================================
+// CGuiSystem::UnregisterUserInputHandlers
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::UnregisterUserInputHandlers( int controllerIDs )
+{
+ for( int i = 0; i < m_numUserInputHandlers; i++ )
+ {
+ if( (controllerIDs & (1 << i)) > 0 )
+ {
+ GetInputManager()->UnregisterMappable( i, m_pUserInputHandlers[ i ] );
+
+ m_registeredUserInputHandlers &= ~(1 << i);
+ }
+ }
+
+ rAssertMsg( m_registeredUserInputHandlers == 0,
+ "*** WARNING: Not all GUI user input handlers were un-registered!" );
+}
+
+//===========================================================================
+// CGuiSystem::GetUserInputHandler
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+CGuiUserInputHandler*
+CGuiSystem::GetUserInputHandler( int controllerId ) const
+{
+ rAssert( controllerId >= 0 && controllerId < m_numUserInputHandlers );
+ rAssert( m_pUserInputHandlers != NULL );
+
+ if( (m_registeredUserInputHandlers & (1 << controllerId)) > 0 )
+ {
+ return m_pUserInputHandlers[ controllerId ];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+//===========================================================================
+// CGuiSystem::LoadData
+//===========================================================================
+// Description: Load GUI data from memory card.
+//
+//===========================================================================
+void
+CGuiSystem::LoadData( const GameDataByte* dataBuffer, unsigned int numBytes )
+{
+ rAssert( dataBuffer != NULL && numBytes == 1 );
+ m_isRadarEnabled = ( dataBuffer[ 0 ] != 0 );
+}
+
+//===========================================================================
+// CGuiSystem::SaveData
+//===========================================================================
+// Description: Save GUI data to memory card.
+//
+//===========================================================================
+void
+CGuiSystem::SaveData( GameDataByte* dataBuffer, unsigned int numBytes )
+{
+ rAssert( dataBuffer != NULL && numBytes == 1 );
+ dataBuffer[ 0 ] = m_isRadarEnabled ? 1 : 0;
+}
+
+//===========================================================================
+// CGuiSystem::ResetData
+//===========================================================================
+// Description: Reset GUI data to defaults.
+//
+//===========================================================================
+void
+CGuiSystem::ResetData()
+{
+ m_isRadarEnabled = true;
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiSystem::OnInitBootUp
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::OnInitBootUp()
+{
+ char languageDir[ 16 ];
+ languageDir[ 0 ] = '\0';
+
+#ifdef PAL
+ switch( CGuiTextBible::GetCurrentLanguage() )
+ {
+ case Scrooby::XL_FRENCH:
+ {
+ strcpy( languageDir, "french\\" );
+
+ break;
+ }
+ case Scrooby::XL_GERMAN:
+ {
+ strcpy( languageDir, "german\\" );
+
+ break;
+ }
+ case Scrooby::XL_SPANISH:
+ {
+ strcpy( languageDir, "spanish\\" );
+
+ break;
+ }
+ default:
+ {
+ rAssert( CGuiTextBible::GetCurrentLanguage() == Scrooby::XL_ENGLISH );
+
+ break;
+ }
+ }
+#endif // PAL
+
+ char licenseImageFile[ 256 ];
+ sprintf( licenseImageFile,
+#ifdef RAD_GAMECUBE
+ "%s%slicenseG.p3d",
+#endif
+#ifdef RAD_PS2
+ "%s%slicenseP.p3d",
+#endif
+#ifdef RAD_XBOX
+ "%s%slicenseX.p3d",
+#endif
+#ifdef RAD_WIN32
+ "%s%slicensePC.p3d",
+#endif
+ LICENSE_SCREEN_IMAGE_DIR,
+ languageDir );
+
+ //Load the mouse cursor
+#ifdef RAD_WIN32
+ char cursorImageFile[256];
+ sprintf( cursorImageFile, "%smouse_cursor.p3d", MOUSE_CURSOR_DIR );
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ cursorImageFile,
+ GMA_PERSISTENT,
+ "Default",
+ "Default" );
+#endif
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ licenseImageFile,
+ GMA_LEVEL_FE,
+ SCROOBY_INVENTORY_BOOTUP,
+ SCROOBY_INVENTORY_BOOTUP );
+
+ // load bootup Scrooby project (into FE heap)
+ //
+ GetLoadingManager()->AddRequest( FILEHANDLER_SCROOBY,
+ PROJECT_FILE_BOOTUP,
+ GMA_LEVEL_FE,
+ SCROOBY_INVENTORY_BOOTUP,
+ SCROOBY_INVENTORY_BOOTUP );
+
+ // load backend Scrooby project (into PERSISTENT heap)
+ //
+ GetLoadingManager()->AddRequest( FILEHANDLER_SCROOBY,
+ PROJECT_FILE_BACKEND,
+ GMA_PERSISTENT, // This is because the loading screen always stays around
+ SCROOBY_INVENTORY_BACKEND,
+ SCROOBY_INVENTORY_BACKEND );
+
+ if( !CommandLineOptions::Get( CLO_SKIP_FE ) )
+ {
+ // load frontend Scrooby project (into FE heap)
+ //
+ GetLoadingManager()->AddRequest( FILEHANDLER_SCROOBY,
+ PROJECT_FILE_FRONTEND,
+ GMA_LEVEL_FE,
+ SCROOBY_INVENTORY_FRONTEND,
+ SCROOBY_INVENTORY_FRONTEND );
+ }
+
+ m_state = BOOTUP_LOADING;
+}
+
+//===========================================================================
+// CGuiSystem::OnReleaseBootUp
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::OnReleaseBootUp()
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+
+ Scrooby::Project* pScroobyProject = NULL;
+
+ if( m_pManagerLanguage != NULL )
+ {
+ pScroobyProject = m_pManagerLanguage->GetScroobyProject();
+
+ // destroy language manager
+ //
+ delete m_pManagerLanguage;
+ m_pManagerLanguage = NULL;
+
+ if( pScroobyProject != NULL )
+ {
+ m_pApp->UnloadProject( pScroobyProject );
+ }
+ }
+
+ if( m_pManagerBootUp != NULL )
+ {
+ pScroobyProject = m_pManagerBootUp->GetScroobyProject();
+
+ // destroy bootup manager
+ delete m_pManagerBootUp;
+ m_pManagerBootUp = NULL;
+
+ if( pScroobyProject != NULL )
+ {
+ m_pApp->UnloadProject( pScroobyProject );
+ }
+ }
+
+ // update reference to text bible
+ //
+ rAssert( m_pTextBible != NULL );
+ m_pTextBible->SetTextBible( TEXT_BIBLE_NAME );
+
+ HeapMgr()->PopHeap(GMA_LEVEL_FE);
+
+#ifdef PAL
+ this->FormatTutorialTextWithLineBreaks();
+#endif
+
+ m_state = GUI_IDLE;
+}
+
+//===========================================================================
+// CGuiSystem::OnInitFrontEnd
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::OnInitFrontEnd()
+{
+ // load frontend Scrooby project (into FE heap)
+ GetLoadingManager()->AddRequest( FILEHANDLER_SCROOBY,
+ PROJECT_FILE_FRONTEND,
+ GMA_LEVEL_FE,
+ SCROOBY_INVENTORY_FRONTEND,
+ SCROOBY_INVENTORY_FRONTEND );
+
+ m_state = FRONTEND_LOADING;
+}
+
+//===========================================================================
+// CGuiSystem::OnReleaseFrontEnd
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::OnReleaseFrontEnd()
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_FE );
+
+ if( m_pManagerFrontEnd != NULL )
+ {
+ // destroy frontend manager
+ delete m_pManagerFrontEnd;
+ m_pManagerFrontEnd = NULL;
+ }
+
+ // unload frontend project resources
+ if( m_pProject != NULL )
+ {
+ m_pApp->UnloadProject( m_pProject );
+ m_pProject = NULL;
+ }
+
+/*
+#ifdef RAD_GAMECUBE
+ // disable screen scaling (for GameCube)
+ m_pApp->EnableScreenScaling( false );
+#endif
+
+#ifdef RAD_PS2
+ // disable screen scaling (for PS2)
+ m_pApp->EnableScreenScaling( false );
+#endif
+*/
+
+ HeapMgr()->PopHeap(GMA_LEVEL_FE);
+
+ m_state = GUI_IDLE;
+}
+
+//===========================================================================
+// CGuiSystem::OnInitMiniGame
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::OnInitMiniGame()
+{
+ // load mini-game Scrooby project
+ //
+ GetLoadingManager()->AddRequest( FILEHANDLER_SCROOBY,
+ PROJECT_FILE_MINIGAME,
+ GMA_LEVEL_HUD,
+ SCROOBY_INVENTORY_MINIGAME,
+ SCROOBY_INVENTORY_MINIGAME );
+
+ // load 3D characters
+ //
+// CGuiManagerMiniGame::LoadCharacters();
+
+ m_state = MINIGAME_LOADING;
+}
+
+//===========================================================================
+// CGuiSystem::OnReleaseMiniGame
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::OnReleaseMiniGame()
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ // unload 3D characters
+ //
+// CGuiManagerMiniGame::UnloadCharacters();
+
+ // destroy MiniGame manager
+ //
+ if( m_pManagerMiniGame != NULL )
+ {
+ delete m_pManagerMiniGame;
+ m_pManagerMiniGame = NULL;
+ }
+
+ // unload mini-game project resources
+ //
+ if( m_pProject != NULL )
+ {
+ m_pApp->UnloadProject( m_pProject );
+ m_pProject = NULL;
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+
+ m_state = GUI_IDLE;
+}
+
+//===========================================================================
+// CGuiSystem::OnInitInGame
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::OnInitInGame()
+{
+ int currentLevel = GetGameplayManager()->GetCurrentLevelIndex();
+ rAssert( currentLevel >= 0 );
+
+ // substitute levels > 7 with level 1
+ //
+ if( static_cast<unsigned int>( currentLevel ) >= sizeof( INGAME_LEVEL_PROJECT_FILES ) /
+ sizeof( INGAME_LEVEL_PROJECT_FILES[ 0 ] ) )
+ {
+ currentLevel = 0;
+ }
+
+#ifdef LOAD_LEVEL_SPECIFIC_PROJECT
+ // load level-specific ingame Scrooby project (into HUD heap)
+ GetLoadingManager()->AddRequest( FILEHANDLER_SCROOBY,
+ INGAME_LEVEL_PROJECT_FILES[ currentLevel ],
+ GMA_LEVEL_HUD,
+ SCROOBY_INVENTORY_INGAME_LEVEL,
+ SCROOBY_INVENTORY_INGAME_LEVEL );
+#endif // LOAD_LEVEL_SPECIFIC_PROJECT
+
+ // load ingame Scrooby project (into HUD heap)
+ GetLoadingManager()->AddRequest( FILEHANDLER_SCROOBY,
+ PROJECT_FILE_INGAME,
+ GMA_LEVEL_HUD,
+ SCROOBY_INVENTORY_INGAME,
+ SCROOBY_INVENTORY_INGAME );
+
+ rAssert( m_pApp );
+ m_pApp->GetResourceManager().SetSecondaryInventorySection( SCROOBY_INVENTORY_INGAME_LEVEL );
+
+ m_state = INGAME_LOADING;
+}
+
+
+//===========================================================================
+// CGuiSystem::OnReleaseInGame
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::OnReleaseInGame()
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ if( m_pManagerInGame != NULL )
+ {
+ // destroy ingame manager
+ delete m_pManagerInGame;
+ m_pManagerInGame = NULL;
+ }
+
+ // unload ingame project resources
+ if( m_pProject != NULL )
+ {
+ m_pApp->UnloadProject( m_pProject );
+ m_pProject = NULL;
+ }
+
+ // as well as level-specific ingame resources
+ if( m_pLevelProject != NULL )
+ {
+ m_pApp->UnloadProject( m_pLevelProject );
+ m_pLevelProject = NULL;
+ }
+
+ // update reference to text bible
+ //
+ rAssert( m_pTextBible != NULL );
+ m_pTextBible->SetTextBible( TEXT_BIBLE_NAME );
+
+ rAssert( m_pApp );
+ m_pApp->GetResourceManager().SetSecondaryInventorySection( NULL );
+
+ HeapMgr()->PopHeap(GMA_LEVEL_HUD);
+
+ m_state = GUI_IDLE;
+}
+
+
+//===========================================================================
+// CGuiSystem::FormatTutorialTextWithLineBreaks
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiSystem::FormatTutorialTextWithLineBreaks()
+{
+#ifdef PAL
+ const int MAX_NUM_CHARS_PER_LINE = 35;
+ const P3D_UNICODE TEXT_LINE_BREAK = '\\';
+
+ for( int tutorialID = 0; tutorialID < TUTORIAL_MAX; tutorialID++ )
+ {
+ char textBibleID[ 32 ];
+ sprintf( textBibleID, "TUTORIAL_%03d", tutorialID );
+ P3D_UNICODE* tutorialText = GetTextBibleString( textBibleID );
+
+ // check for platform-specific text; if found, override default text
+ //
+ P3D_UNICODE* platformText = NULL;
+
+#ifdef RAD_GAMECUBE
+ strcat( textBibleID, "_(GC)" );
+ platformText = GetTextBibleString( textBibleID );
+#endif
+#ifdef RAD_PS2
+ strcat( textBibleID, "_(PS2)" );
+ platformText = GetTextBibleString( textBibleID );
+#endif
+#ifdef RAD_XBOX
+ strcat( textBibleID, "_(XBOX)" );
+ platformText = GetTextBibleString( textBibleID );
+#endif
+ if( platformText != NULL )
+ {
+ tutorialText = platformText;
+ }
+
+ // wrap tutorial text by inserting line breaks into text
+ //
+ int numCharsInCurrentLine = 0;
+ int lastSpaceCharIndex = -1;
+
+#ifdef RAD_DEBUG
+ const int MAX_NUM_LINES_PER_MESSAGE = 5;
+ int numLineBreaks = 0;
+#endif
+
+ int tutorialTextLength = p3d::UnicodeStrLen( tutorialText );
+ for( int i = 0; i < tutorialTextLength; i++ )
+ {
+ if( tutorialText[ i ] == TEXT_LINE_BREAK ) // line break character
+ {
+#ifdef RAD_DEBUG
+ numLineBreaks++;
+ rAssert( numLineBreaks < MAX_NUM_LINES_PER_MESSAGE );
+#endif
+ numCharsInCurrentLine = 0;
+
+ continue;
+ }
+
+ if( tutorialText[ i ] == ' ' ) // space character
+ {
+ lastSpaceCharIndex = i;
+ }
+
+ numCharsInCurrentLine++;
+
+ if( numCharsInCurrentLine > MAX_NUM_CHARS_PER_LINE )
+ {
+ rAssertMsg( lastSpaceCharIndex != -1, "We might encounter this w/ Japanese text!" );
+
+ // replace last space character w/ line break
+ //
+ tutorialText[ lastSpaceCharIndex ] = TEXT_LINE_BREAK;
+ numCharsInCurrentLine = i - lastSpaceCharIndex;
+
+#ifdef RAD_DEBUG
+ numLineBreaks++;
+ rAssertMsg( numLineBreaks < MAX_NUM_LINES_PER_MESSAGE, "Too many lines in tutorial message! Tell Tony." );
+#endif
+
+ rAssertMsg( numCharsInCurrentLine <= MAX_NUM_CHARS_PER_LINE, "We might encounter this w/ Japanese text!" );
+ }
+ }
+ }
+#endif // PAL
+}
+
+
+int CGuiSystem::GetPrimaryController() const
+{
+ return m_primaryController;
+}
+
+void CGuiSystem::SetPrimaryController(int id)
+{
+ m_primaryController = id;
+}
+
+
+#ifdef DEBUGWATCH
+
+void CGuiSystem::RegisterWatcherStuff()
+{
+ static bool s_watcherRegistered = false;
+
+ if( s_watcherRegistered )
+ {
+ // delete previously registered stuff
+ //
+ radDbgWatchDelete( &g_wGuiSimulationTimeFactor );
+ radDbgWatchDelete( &g_wGuiMessage );
+ radDbgWatchDelete( &g_wGuiMessageParam1 );
+ radDbgWatchDelete( &g_wGuiMessageParam2 );
+ radDbgWatchDelete( &(Scrooby::g_CameraNearPlane) );
+ radDbgWatchDelete( &(Scrooby::g_CameraFarPlane) );
+/*
+#ifdef RAD_PS2
+ radDbgWatchDelete( &g_ps2screenScale );
+#endif
+*/
+ radDbgWatchDelete( &g_wScroobyPageName );
+ radDbgWatchDelete( &g_wScroobyPosX );
+ radDbgWatchDelete( &g_wScroobyPosY );
+ radDbgWatchDelete( &g_wScroobyAlpha );
+ radDbgWatchDelete( &g_wScroobyTextName );
+ radDbgWatchDelete( &g_wScroobySpriteName );
+ radDbgWatchDelete( &g_wScroobyPolygonName );
+ }
+
+ // and re-register again
+ //
+ radDbgWatchAddFloat( &g_wGuiSimulationTimeFactor,
+ "GUI Simulation Time Factor",
+ GUI_WATCHER_NAMESPACE,
+ NULL,
+ NULL,
+ 0.0f,
+ 2.0f );
+
+ radDbgWatchAddInt( &g_wGuiMessage,
+ "GUI Message",
+ GUI_WATCHER_NAMESPACE,
+ (RADDEBUGWATCH_CALLBACK)SendMsgToGuiSystem );
+
+ radDbgWatchAddInt( &g_wGuiMessageParam1,
+ "GUI Message Param1",
+ GUI_WATCHER_NAMESPACE );
+
+ radDbgWatchAddInt( &g_wGuiMessageParam2,
+ "GUI Message Param2",
+ GUI_WATCHER_NAMESPACE );
+
+ radDbgWatchAddFloat( &(Scrooby::g_CameraNearPlane),
+ "Override Camera Near Plane",
+ GUI_WATCHER_NAMESPACE,
+ NULL, NULL, 0.01f, 1.0f );
+
+ radDbgWatchAddFloat( &(Scrooby::g_CameraFarPlane),
+ "Override Camera Far Plane",
+ GUI_WATCHER_NAMESPACE,
+ NULL, NULL, 10.0f, 1000.0f );
+
+ radDbgWatchAddFunction( "Toggle Next Language",
+ (RADDEBUGWATCH_CALLBACK)ToggleNextLanguage,
+ NULL,
+ GUI_WATCHER_NAMESPACE );
+/*
+#ifdef RAD_PS2
+ radDbgWatchAddFloat( &g_ps2screenScale,
+ "PS2 Screen Scale",
+ GUI_WATCHER_NAMESPACE,
+ (RADDEBUGWATCH_CALLBACK)UpdatePS2ScreenScale,
+ NULL, 0.5f, 1.5f );
+#endif
+*/
+ radDbgWatchAddString( g_wScroobyPageName,
+ sizeof( g_wScroobyPageName ),
+ "Scrooby Page",
+ WATCHER_NAMESPACE_SCROOBY,
+ (RADDEBUGWATCH_CALLBACK)SetCurrentScroobyPage );
+
+ radDbgWatchAddInt( &g_wScroobyPosX,
+ "X-Position",
+ WATCHER_NAMESPACE_SCROOBY,
+ (RADDEBUGWATCH_CALLBACK)SetScroobyDrawablePosition,
+ NULL,
+ 0,
+ (int)Scrooby::App::GetInstance()->GetScreenWidth() );
+
+ radDbgWatchAddInt( &g_wScroobyPosY,
+ "Y-Position",
+ WATCHER_NAMESPACE_SCROOBY,
+ (RADDEBUGWATCH_CALLBACK)SetScroobyDrawablePosition,
+ NULL,
+ 0,
+ (int)Scrooby::App::GetInstance()->GetScreenHeight() );
+
+ radDbgWatchAddFloat( &g_wScroobyAlpha,
+ "Alpha",
+ WATCHER_NAMESPACE_SCROOBY,
+ (RADDEBUGWATCH_CALLBACK)SetScroobyDrawableAlpha );
+
+ radDbgWatchAddString( g_wScroobyTextName,
+ sizeof( g_wScroobyTextName ),
+ "Scrooby Text",
+ WATCHER_NAMESPACE_SCROOBY,
+ (RADDEBUGWATCH_CALLBACK)SetCurrentScroobyDrawableAsText );
+
+ radDbgWatchAddString( g_wScroobySpriteName,
+ sizeof( g_wScroobySpriteName ),
+ "Scrooby Sprite",
+ WATCHER_NAMESPACE_SCROOBY,
+ (RADDEBUGWATCH_CALLBACK)SetCurrentScroobyDrawableAsSprite );
+
+ radDbgWatchAddString( g_wScroobyPolygonName,
+ sizeof( g_wScroobyPolygonName ),
+ "Scrooby Polygon",
+ WATCHER_NAMESPACE_SCROOBY,
+ (RADDEBUGWATCH_CALLBACK)SetCurrentScroobyDrawableAsPolygon );
+
+ s_watcherRegistered = true;
+}
+
+#endif // DEBUGWATCH
diff --git a/game/code/presentation/gui/guisystem.h b/game/code/presentation/gui/guisystem.h
new file mode 100644
index 0000000..c56e4bc
--- /dev/null
+++ b/game/code/presentation/gui/guisystem.h
@@ -0,0 +1,320 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiSystem
+//
+// Description: Interface for the CGuiSystem class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/20 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUISYSTEM_H
+#define GUISYSTEM_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guientity.h>
+#include <data/gamedata.h>
+#include <app.h>
+
+//===========================================================================
+// External Constants
+//===========================================================================
+
+extern unsigned int g_scroobySimulationTime;
+
+static const char* SCROOBY_INVENTORY_LANGUAGE = "ScroobyLanguage";
+static const char* SCROOBY_INVENTORY_BOOTUP = "ScroobyBootup";
+static const char* SCROOBY_INVENTORY_BACKEND = "ScroobyBackend";
+static const char* SCROOBY_INVENTORY_FRONTEND = "ScroobyFrontend";
+static const char* SCROOBY_INVENTORY_MINIGAME = "ScroobyMiniGame";
+static const char* SCROOBY_INVENTORY_INGAME = "ScroobyIngame";
+static const char* SCROOBY_INVENTORY_INGAME_LEVEL = "ScroobyIngameLevel";
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CGuiTextBible;
+class CGuiManager;
+class CGuiManagerLanguage;
+class CGuiManagerBootUp;
+class CGuiManagerBackEnd;
+class CGuiManagerFrontEnd;
+class CGuiManagerMiniGame;
+class CGuiManagerInGame;
+class CGuiUserInputHandler;
+class CGuiScreenMultiHud;
+
+// Implement this interface to be called back when the
+// async loading of the GUI system is complete.
+//
+struct IGuiLoadingCallback
+{
+ enum eGameMode
+ {
+ GUI_LOADED_FRONT_END,
+ GUI_LOADED_IN_GAME,
+ GUI_LOADED_MINI_GAME,
+
+ NUM_GAME_MODES
+ };
+
+ virtual void OnGuiLoadComplete( eGameMode mode ) = 0;
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class CGuiSystem : public CGuiEntity,
+ public Scrooby::LoadProjectCallback,
+ public GameDataHandler
+{
+public:
+ // Static Methods for accessing this singleton.
+ static CGuiSystem* CreateInstance();
+ static void DestroyInstance();
+ static CGuiSystem* GetInstance();
+
+ void Init();
+ void Update( unsigned int elapsedTime );
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ // Implements Scrooby::LoadProjectCallback interface.
+ //
+ virtual void OnProjectLoadComplete( Scrooby::Project* pProject );
+
+ void GotoScreen( unsigned int screenID,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0,
+ unsigned int windowOptions = 0 );
+
+ Scrooby::Project* GetCurrentScroobyProject() const { return m_pProject; }
+ void SetCurrentScroobyProject( Scrooby::Project* pProject ) { m_pProject = pProject; }
+ Scrooby::Project* GetScroobyLevelProject() const { return m_pLevelProject; }
+
+ void SwitchToCurrentProject();
+ void SwitchToLevelProject();
+ void SwitchToBackendProject();
+
+ // Registration of Client Loading Callback Routine
+ //
+ void RegisterLoadingCallback( IGuiLoadingCallback* pCallback );
+ void UnregisterLoadingCallback();
+
+ // Accessors to GUI System Managers
+ //
+ CGuiManager* GetCurrentManager() const;
+
+ inline CGuiManagerBootUp* GetBootupManager() const { return m_pManagerBootUp; }
+ inline CGuiManagerBackEnd* GetBackendManager() const { return m_pManagerBackEnd; }
+ inline CGuiManagerFrontEnd* GetFrontEndManager() const { return m_pManagerFrontEnd; }
+ inline CGuiManagerInGame* GetInGameManager() const { return m_pManagerInGame; }
+ inline CGuiManagerMiniGame* GetMiniGameManager() const { return m_pManagerMiniGame; }
+
+ // Registration of GUI User Input Handlers
+ //
+ void RegisterUserInputHandlers( int controllerIDs = ~0 );
+ void UnregisterUserInputHandlers( int controllerIDs = ~0 );
+
+ // Accessor to GUI User Input Handlers
+ //
+ CGuiUserInputHandler* GetUserInputHandler( int controllerId ) const;
+ int GetNumUserInputHandlers() const { return m_numUserInputHandlers; }
+
+ // Splash Screen
+ //
+ inline void SetSplashScreenFinished() { m_isSplashScreenFinished = true; }
+ inline bool IsSplashScreenFinished() const { return m_isSplashScreenFinished; }
+
+ // Credits Screen
+ //
+ void ShowCreditsUponReturnToFE( bool enable );
+ bool IsShowCreditsUponReturnToFE() const;
+
+ // Enabling/Disabling HUD Radar
+ //
+ void SetRadarEnabled( bool isEnabled );
+ bool IsRadarEnabled() const;
+
+ // Implements GameDataHandler
+ //
+ virtual void LoadData( const GameDataByte* dataBuffer, unsigned int numBytes );
+ virtual void SaveData( GameDataByte* dataBuffer, unsigned int numBytes );
+ virtual void ResetData();
+
+ int GetPrimaryController() const;
+ void SetPrimaryController(int id);
+
+ enum eGuiSystemState
+ {
+ GUI_UNINITIALIZED,
+ GUI_IDLE,
+
+ LANGUAGE_LOADING,
+ LANGUAGE_ACTIVE,
+
+ BOOTUP_LOADING,
+ BOOTUP_ACTIVE,
+
+ BACKEND_LOADING,
+ FRONTEND_LOADING_DURING_BOOTUP,
+
+ FRONTEND_LOADING,
+ FRONTEND_ACTIVE,
+
+ MINIGAME_LOADING,
+ MINIGAME_ACTIVE,
+
+ INGAME_LOADING,
+ INGAME_ACTIVE,
+
+ DEMO_ACTIVE,
+
+ NUM_GUI_STATES
+ };
+
+ eGuiSystemState GetCurrentState() const { return m_state; }
+
+private:
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No public access; use singleton interface.
+ //
+ CGuiSystem();
+ virtual ~CGuiSystem();
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CGuiSystem( const CGuiSystem& );
+ CGuiSystem& operator= ( const CGuiSystem& );
+
+ void OnInitBootUp();
+ void OnReleaseBootUp();
+
+ void OnInitFrontEnd();
+ void OnReleaseFrontEnd();
+
+ void OnInitMiniGame();
+ void OnReleaseMiniGame();
+
+ void OnInitInGame();
+ void OnReleaseInGame();
+
+ void FormatTutorialTextWithLineBreaks();
+
+#ifdef DEBUGWATCH
+ void RegisterWatcherStuff();
+#endif
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ // Pointer to the one and only instance of this singleton.
+ static CGuiSystem* spInstance;
+
+ eGuiSystemState m_state;
+
+ CGuiTextBible* m_pTextBible;
+
+ CGuiManagerLanguage* m_pManagerLanguage;
+ CGuiManagerBootUp* m_pManagerBootUp;
+ CGuiManagerBackEnd* m_pManagerBackEnd;
+ CGuiManagerFrontEnd* m_pManagerFrontEnd;
+ CGuiManagerMiniGame* m_pManagerMiniGame;
+ CGuiManagerInGame* m_pManagerInGame;
+
+ CGuiUserInputHandler** m_pUserInputHandlers;
+ int m_numUserInputHandlers;
+ int m_registeredUserInputHandlers;
+ int m_primaryController;
+
+ Scrooby::App* m_pApp;
+ Scrooby::Project* m_pProject;
+ Scrooby::Project* m_pLevelProject;
+ Scrooby::Project* m_pBackendProject;
+
+ IGuiLoadingCallback* m_pLoadingCallback;
+
+ bool m_isSplashScreenFinished : 1;
+ bool m_isRadarEnabled : 1;
+ bool m_isShowCreditsUponReturnToFE : 1;
+
+};
+
+//===========================================================================
+// Inline Methods
+//===========================================================================
+
+// A little syntactic sugar for getting at this singleton.
+//
+inline CGuiSystem* GetGuiSystem() { return( CGuiSystem::GetInstance() ); }
+
+inline void CGuiSystem::SwitchToCurrentProject()
+{
+ rAssert( m_pProject != NULL );
+ rAssert( m_pApp != NULL );
+ m_pApp->SetProject( m_pProject );
+}
+
+inline void CGuiSystem::SwitchToLevelProject()
+{
+ if( m_pLevelProject != NULL )
+ {
+ rAssert( m_pApp != NULL );
+ m_pApp->SetProject( m_pLevelProject );
+ }
+}
+
+inline void CGuiSystem::SwitchToBackendProject()
+{
+ rAssert( m_pBackendProject != NULL );
+ rAssert( m_pApp != NULL );
+ m_pApp->SetProject( m_pBackendProject );
+}
+
+inline void CGuiSystem::RegisterLoadingCallback( IGuiLoadingCallback* pCallback )
+{
+ m_pLoadingCallback = pCallback;
+}
+
+inline void CGuiSystem::UnregisterLoadingCallback()
+{
+ m_pLoadingCallback = NULL;
+}
+
+inline void CGuiSystem::SetRadarEnabled( bool isEnabled )
+{
+ m_isRadarEnabled = isEnabled;
+}
+
+inline bool CGuiSystem::IsRadarEnabled() const
+{
+ return m_isRadarEnabled;
+}
+
+inline void CGuiSystem::ShowCreditsUponReturnToFE( bool enable )
+{
+ m_isShowCreditsUponReturnToFE = enable;
+}
+
+inline bool CGuiSystem::IsShowCreditsUponReturnToFE() const
+{
+ return m_isShowCreditsUponReturnToFE;
+}
+
+#endif // GUISYSTEM_H
diff --git a/game/code/presentation/gui/guitextbible.cpp b/game/code/presentation/gui/guitextbible.cpp
new file mode 100644
index 0000000..d060cb9
--- /dev/null
+++ b/game/code/presentation/gui/guitextbible.cpp
@@ -0,0 +1,89 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/26 TChu Created
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+
+#include <presentation/gui/guitextbible.h>
+
+#include <app.h>
+#include <textbible.h>
+#include <raddebug.hpp>
+
+#include <sound/soundmanager.h>
+
+Scrooby::TextBible* CGuiTextBible::s_textBible = NULL;
+Scrooby::XLLanguage CGuiTextBible::s_currentLanguage = Scrooby::XL_ENGLISH;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+CGuiTextBible::CGuiTextBible()
+{
+}
+
+CGuiTextBible::~CGuiTextBible()
+{
+ s_textBible = NULL;
+}
+
+void
+CGuiTextBible::SetTextBible( const char* textBible )
+{
+ rAssert( textBible != NULL );
+ s_textBible = Scrooby::App::GetInstance()->GetTextBible( textBible );
+}
+
+P3D_UNICODE*
+CGuiTextBible::GetLocalizedText( const char* stringID )
+{
+ rAssert( stringID != NULL );
+ rAssert( s_textBible != NULL );
+
+ P3D_UNICODE* localizedText = static_cast<P3D_UNICODE*>( s_textBible->GetWChar( stringID ) );
+
+/*
+#ifndef RAD_RELEASE
+ if( localizedText == NULL )
+ {
+ char msg[ 256 ];
+ sprintf( msg, "Can't find text bible string for entry: %s!", stringID );
+ rTuneWarningMsg( false, msg );
+ }
+#endif
+*/
+
+ return localizedText;
+}
+
+void
+CGuiTextBible::SetCurrentLanguage( const Scrooby::XLLanguage language )
+{
+ Scrooby::App::GetInstance()->SetLocalizationLanguage( language );
+
+ s_currentLanguage = language;
+
+ GetSoundManager()->SetDialogueLanguage( language );
+}
+
+Scrooby::XLLanguage
+CGuiTextBible::GetCurrentLanguage()
+{
+ return s_currentLanguage;
+}
+
+bool CGuiTextBible::IsTextBibleLoaded()
+{
+ return s_textBible != NULL;
+}
+
+
diff --git a/game/code/presentation/gui/guitextbible.h b/game/code/presentation/gui/guitextbible.h
new file mode 100644
index 0000000..87a8388
--- /dev/null
+++ b/game/code/presentation/gui/guitextbible.h
@@ -0,0 +1,86 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/26 TChu Created
+//
+//===========================================================================
+
+#ifndef GUITEXTBIBLE_H
+#define GUITEXTBIBLE_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <p3d/plat_types.hpp>
+
+// Scrooby
+//
+#include <enums.h>
+
+//===========================================================================
+// External Constants
+//===========================================================================
+
+const Scrooby::XLLanguage SRR2_LANGUAGE[] =
+{
+ Scrooby::XL_ENGLISH,
+ Scrooby::XL_FRENCH,
+ Scrooby::XL_GERMAN,
+// Scrooby::XL_ITALIAN,
+ Scrooby::XL_SPANISH,
+
+ Scrooby::XL_LAST_LANGUAGE
+};
+
+const int NUM_SRR2_LANGUAGES =
+ sizeof( SRR2_LANGUAGE ) / sizeof( SRR2_LANGUAGE[ 0 ] ) - 1;
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class TextBible;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class CGuiTextBible
+{
+public:
+ CGuiTextBible();
+ virtual ~CGuiTextBible();
+
+ // update reference to Scrooby text bible
+ //
+ void SetTextBible( const char* textBible );
+
+ // get localized text for string ID
+ //
+ static P3D_UNICODE* GetLocalizedText( const char* stringID );
+
+ // get/set current locale language
+ //
+ static void SetCurrentLanguage( const Scrooby::XLLanguage language );
+ static Scrooby::XLLanguage GetCurrentLanguage();
+ static bool IsTextBibleLoaded();
+
+private:
+ static Scrooby::TextBible* s_textBible;
+ static Scrooby::XLLanguage s_currentLanguage;
+
+};
+
+inline P3D_UNICODE* GetTextBibleString( const char* stringID )
+{
+ return CGuiTextBible::GetLocalizedText( stringID );
+}
+
+#endif // GUITEXTBIBLE_H
diff --git a/game/code/presentation/gui/guiuserinputhandler.cpp b/game/code/presentation/gui/guiuserinputhandler.cpp
new file mode 100644
index 0000000..f53b3b6
--- /dev/null
+++ b/game/code/presentation/gui/guiuserinputhandler.cpp
@@ -0,0 +1,938 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiUserInputHandler
+//
+// Description: Implementation of the CGuiUserInputHandler class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/10/20 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/guiuserinputhandler.h>
+#include <presentation/gui/guisystem.h>
+
+#include <gameflow/gameflow.h>
+
+#include <raddebug.hpp>
+
+#include <contexts/context.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+struct ControlMap
+{
+ char* inputName;
+ GuiInput::eGuiInput inputID;
+};
+
+const ControlMap GUI_CONTROL_MAP[] =
+{
+#ifdef RAD_GAMECUBE
+ { "LeftStickX", GuiInput::XAxis },
+ { "LeftStickY", GuiInput::YAxis },
+ { "RightStickX", GuiInput::XAxisRight },
+ { "RightStickY", GuiInput::YAxisRight },
+ { "DPadLeft", GuiInput::Left },
+ { "DPadRight", GuiInput::Right },
+ { "DPadUp", GuiInput::Up },
+ { "DPadDown", GuiInput::Down },
+ { "Menu", GuiInput::Start },
+ { "A", GuiInput::Select },
+ { "B", GuiInput::Back },
+ { "X", GuiInput::AuxX },
+ { "Y", GuiInput::AuxY },
+ { "TriggerL", GuiInput::L1 },
+ { "TriggerR", GuiInput::R1 },
+#endif
+
+#ifdef RAD_PS2
+ { "LeftStickX", GuiInput::XAxis },
+ { "LeftStickY", GuiInput::YAxis },
+ { "RightStickX", GuiInput::XAxisRight },
+ { "RightStickY", GuiInput::YAxisRight },
+ { "DPadLeft", GuiInput::Left },
+ { "DPadRight", GuiInput::Right },
+ { "DPadUp", GuiInput::Up },
+ { "DPadDown", GuiInput::Down },
+ { "Start", GuiInput::Start },
+ { "X", GuiInput::Select },
+ { "Triangle", GuiInput::Back },
+ { "Square", GuiInput::AuxX },
+ { "Circle", GuiInput::AuxY },
+ { "L1", GuiInput::L1 },
+ { "R1", GuiInput::R1 },
+ { "LGA", GuiInput::AuxStart }, //Only on the GT Wheel
+ { "LGX", GuiInput::AuxSelect }, //Only on the GT Wheel
+ { "LGY", GuiInput::AuxBack }, //Only on the GT Wheel
+ { "Wheel", GuiInput::AuxXAxis }, //Only on the GT Wheel
+ { "LGR1", GuiInput::AuxUp }, //Only on the GT Wheel
+ { "LGL1", GuiInput::AuxDown }, //Only on the GT Wheel
+
+#endif
+
+#ifdef RAD_XBOX
+ { "LeftStickX", GuiInput::XAxis },
+ { "LeftStickY", GuiInput::YAxis },
+ { "RightStickX", GuiInput::XAxisRight },
+ { "RightStickY", GuiInput::YAxisRight },
+ { "DPadLeft", GuiInput::Left },
+ { "DPadRight", GuiInput::Right },
+ { "DPadUp", GuiInput::Up },
+ { "DPadDown", GuiInput::Down },
+ { "Start", GuiInput::Start },
+ { "Back", GuiInput::Back },
+ { "A", GuiInput::Select },
+ { "B", GuiInput::Back },
+ { "X", GuiInput::AuxX },
+ { "Y", GuiInput::AuxY },
+ { "LeftTrigger", GuiInput::L1 },
+ { "RightTrigger", GuiInput::R1 },
+#endif
+
+#ifdef RAD_WIN32
+ { "feMoveLeft", GuiInput::Left },
+ { "feMoveRight", GuiInput::Right },
+ { "feMoveUp", GuiInput::Up },
+ { "feMoveDown", GuiInput::Down },
+ { "feStart", GuiInput::Start },
+ { "feBack", GuiInput::Back },
+ { "feSelect", GuiInput::Select },
+ { "feFunction1", GuiInput::AuxX },
+ { "feFunction2", GuiInput::L1 },
+
+ { "P1_KBD_Start", GuiInput::P1_KBD_Start },
+ { "P1_KBD_Gas", GuiInput::P1_KBD_Select },
+ { "P1_KBD_Brake", GuiInput::P1_KBD_Back },
+ { "P1_KBD_Left", GuiInput::P1_KBD_Left },
+ { "P1_KBD_Right", GuiInput::P1_KBD_Right },
+#endif
+
+ { "", GuiInput::UNKNOWN }
+};
+
+const int NUM_GUI_CONTROL_MAPPINGS = sizeof( GUI_CONTROL_MAP ) /
+ sizeof( GUI_CONTROL_MAP[ 0 ] );
+
+// time between repeated inputs
+const int INPUT_REPEAT_PERIOD = 166; // in milliseconds
+
+// time before first repeated input
+const int INPUT_REPEAT_WAIT = INPUT_REPEAT_PERIOD; // in milliseconds
+
+const float ANALOG_BUTTON_THRESHOLD = 0.5f;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiUserInputHandler::CGuiUserInputHandler
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiUserInputHandler::CGuiUserInputHandler( void )
+: Mappable( Input::ACTIVE_ALL ),
+ m_XAxisValue( 0.0f ),
+ m_YAxisValue( 0.0f ),
+ m_XAxisDuration( 0 ),
+ m_YAxisDuration( 0 ),
+#ifdef RAD_WIN32
+ m_RightValue( 0 ),
+ m_LeftValue( 0 ),
+ m_UpValue( 0 ),
+ m_DownValue( 0 ),
+ m_RightDuration( 0 ),
+ m_LeftDuration( 0 ),
+ m_UpDuration( 0 ),
+ m_DownDuration( 0 ),
+#endif
+ m_isStartToSelectMappingEnabled( true )
+{
+ this->ResetRepeatableButtons();
+}
+
+
+//===========================================================================
+// CGuiUserInputHandler::~CGuiUserInputHandler
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiUserInputHandler::~CGuiUserInputHandler( void )
+{
+}
+
+
+//===========================================================================
+// CGuiUserInputHandler::Left
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiUserInputHandler::Left( int controllerId )
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_LEFT, controllerId );
+}
+
+
+//===========================================================================
+// CGuiUserInputHandler::Right
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiUserInputHandler::Right( int controllerId )
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_RIGHT, controllerId );
+}
+
+
+//===========================================================================
+// CGuiUserInputHandler::Up
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiUserInputHandler::Up( int controllerId )
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_UP, controllerId );
+}
+
+
+//===========================================================================
+// CGuiUserInputHandler::Down
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiUserInputHandler::Down( int controllerId )
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_DOWN, controllerId );
+}
+
+
+//===========================================================================
+// CGuiUserInputHandler::Start
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiUserInputHandler::Start( int controllerId )
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_START, controllerId );
+}
+
+
+//===========================================================================
+// CGuiUserInputHandler::Select
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiUserInputHandler::Select( int controllerId )
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_SELECT, controllerId );
+}
+
+
+//===========================================================================
+// CGuiUserInputHandler::Back
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiUserInputHandler::Back( int controllerId )
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_BACK, controllerId );
+}
+
+
+//===========================================================================
+// CGuiUserInputHandler::AuxX
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiUserInputHandler::AuxX( int controllerId )
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_AUX_X, controllerId );
+}
+
+
+//===========================================================================
+// CGuiUserInputHandler::AuxY
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiUserInputHandler::AuxY( int controllerId )
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_AUX_Y, controllerId );
+}
+
+
+//===========================================================================
+// CGuiUserInputHandler::L1
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiUserInputHandler::L1( int controllerId )
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_L1, controllerId );
+}
+
+
+//===========================================================================
+// CGuiUserInputHandler::R1
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void CGuiUserInputHandler::R1( int controllerId )
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_R1, controllerId );
+}
+
+
+void CGuiUserInputHandler::OnControllerDisconnect( int id )
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_DISCONNECT, id );
+
+ this->ResetRepeatableButtons();
+
+ Mappable::OnControllerDisconnect( id );
+}
+
+
+void CGuiUserInputHandler::OnControllerConnect( int id )
+{
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_CONNECT, id );
+
+ Mappable::OnControllerConnect( id );
+}
+
+//===========================================================================
+// CGuiUserInputHandler::OnButton
+//===========================================================================
+// Description:
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+//////////////////////////////////////////////////////////////////////////////
+// IButtonedObject declarations
+//
+void CGuiUserInputHandler::OnButton( int controllerId, int buttonId, const IButton* pButton )
+{
+ rAssert( pButton != NULL );
+
+ switch ( buttonId )
+ {
+ case GuiInput::AuxXAxis:
+ case GuiInput::XAxis:
+ {
+ if ( pButton->GetValue() > ANALOG_BUTTON_THRESHOLD && !( m_XAxisValue > ANALOG_BUTTON_THRESHOLD ) )
+ {
+ Right( controllerId );
+
+ m_XAxisDuration = -INPUT_REPEAT_WAIT;
+ }
+
+ if ( pButton->GetValue() < -ANALOG_BUTTON_THRESHOLD && !( m_XAxisValue < -ANALOG_BUTTON_THRESHOLD ) )
+ {
+ Left( controllerId );
+
+ m_XAxisDuration = -INPUT_REPEAT_WAIT;
+ }
+
+ m_XAxisValue = pButton->GetValue();
+
+ break;
+ }
+ case GuiInput::YAxis:
+ {
+ if ( pButton->GetValue() > ANALOG_BUTTON_THRESHOLD && !( m_YAxisValue > ANALOG_BUTTON_THRESHOLD ) )
+ {
+ Up( controllerId );
+
+ m_YAxisDuration = -INPUT_REPEAT_WAIT;
+ }
+
+ if ( pButton->GetValue() < -ANALOG_BUTTON_THRESHOLD && !( m_YAxisValue < -ANALOG_BUTTON_THRESHOLD ) )
+ {
+ Down( controllerId );
+
+ m_YAxisDuration = -INPUT_REPEAT_WAIT;
+ }
+
+ m_YAxisValue = pButton->GetValue();
+
+ break;
+ }
+ case GuiInput::XAxisRight:
+ case GuiInput::YAxisRight:
+ {
+ // TC: *** temporary for now ***
+ //
+ GetGuiSystem()->HandleMessage( GUI_MSG_CONTROLLER_AUX_LEFT, controllerId );
+
+ break;
+ }
+#ifdef RAD_WIN32
+ case GuiInput::Left:
+ {
+ if ( pButton->GetValue() > ANALOG_BUTTON_THRESHOLD && !( m_LeftValue > ANALOG_BUTTON_THRESHOLD ) )
+ {
+ Left( controllerId );
+
+ m_LeftDuration = -INPUT_REPEAT_WAIT;
+ }
+ m_LeftValue = pButton->GetValue();
+ break;
+ }
+ case GuiInput::Right:
+ {
+ if ( pButton->GetValue() > ANALOG_BUTTON_THRESHOLD && !( m_RightValue > ANALOG_BUTTON_THRESHOLD ) )
+ {
+ Right( controllerId );
+
+ m_RightDuration = -INPUT_REPEAT_WAIT;
+ }
+ m_RightValue = pButton->GetValue();
+ break;
+ }
+ case GuiInput::Up:
+ {
+ if ( pButton->GetValue() > ANALOG_BUTTON_THRESHOLD && !( m_UpValue > ANALOG_BUTTON_THRESHOLD ) )
+ {
+ Up( controllerId );
+
+ m_UpDuration = -INPUT_REPEAT_WAIT;
+ }
+ m_UpValue = pButton->GetValue();
+ break;
+ }
+ case GuiInput::Down:
+ {
+ if ( pButton->GetValue() > ANALOG_BUTTON_THRESHOLD && !( m_DownValue > ANALOG_BUTTON_THRESHOLD ) )
+ {
+ Down( controllerId );
+
+ m_DownDuration = -INPUT_REPEAT_WAIT;
+ }
+ m_DownValue = pButton->GetValue();
+ break;
+ }
+#endif
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void CGuiUserInputHandler::OnButtonUp( int controllerId, int buttonId, const IButton* pButton )
+{
+ rAssert( buttonId >= 0 && buttonId < GuiInput::NUM_GUI_INPUTS );
+
+ if ( static_cast<unsigned int>( buttonId ) < sizeof( m_buttonDownDuration ) /
+ sizeof( m_buttonDownDuration[ 0 ] ) )
+ {
+ // reset button down duration time
+ m_buttonDownDuration[ buttonId ] = -INPUT_REPEAT_WAIT;
+ }
+}
+
+void CGuiUserInputHandler::OnButtonDown( int controllerId, int buttonId, const IButton* pButton )
+{
+ ContextEnum context = GetGameFlow()->GetCurrentContext();
+
+ switch( buttonId )
+ {
+#ifndef RAD_WIN32 // for windows we handle them in onbutton()
+ case GuiInput::Left:
+ {
+ this->Left( controllerId );
+
+ break;
+ }
+ case GuiInput::Right:
+ {
+ this->Right( controllerId );
+
+ break;
+ }
+ case GuiInput::AuxUp:
+ {
+ if ( context != CONTEXT_SUPERSPRINT_FE &&
+ context != CONTEXT_SUPERSPRINT )
+ {
+ break;
+ }
+
+ //Fall through
+ }
+ case GuiInput::Up:
+ {
+ this->Up( controllerId );
+
+ break;
+ }
+ case GuiInput::AuxDown:
+ {
+ if ( context != CONTEXT_SUPERSPRINT_FE &&
+ context != CONTEXT_SUPERSPRINT )
+ {
+ break;
+ }
+
+ //Fall through
+ }
+ case GuiInput::Down:
+ {
+ this->Down( controllerId );
+
+ break;
+ }
+#endif
+ case GuiInput::AuxStart:
+ {
+ if ( context != CONTEXT_SUPERSPRINT_FE &&
+ context != CONTEXT_SUPERSPRINT )
+ {
+ break;
+ }
+
+ //Fall through
+ }
+ case GuiInput::Start:
+ {
+ this->Start( controllerId );
+
+#ifdef RAD_XBOX
+ if( m_isStartToSelectMappingEnabled )
+ {
+ // for Xbox only, START is mapped to same functionality as SELECT
+ this->Select( controllerId );
+ }
+#endif
+
+ break;
+ }
+ case GuiInput::AuxSelect:
+ {
+ if ( context != CONTEXT_SUPERSPRINT_FE &&
+ context != CONTEXT_SUPERSPRINT )
+ {
+ break;
+ }
+
+ //Fall through
+ }
+ case GuiInput::Select:
+ {
+ this->Select( controllerId );
+
+ break;
+ }
+ case GuiInput::AuxBack:
+ {
+ if ( context != CONTEXT_SUPERSPRINT_FE &&
+ context != CONTEXT_SUPERSPRINT )
+ {
+ break;
+ }
+
+ //Fall through
+ }
+ case GuiInput::Back:
+ {
+ this->Back( controllerId );
+
+ break;
+ }
+ case GuiInput::AuxX:
+ {
+ this->AuxX( controllerId );
+
+ break;
+ }
+ case GuiInput::AuxY:
+ {
+ this->AuxY( controllerId );
+
+ break;
+ }
+ case GuiInput::L1:
+ {
+ this->L1( controllerId );
+
+ break;
+ }
+ case GuiInput::R1:
+ {
+ this->R1( controllerId );
+
+ break;
+ }
+ default:
+ {
+#ifdef RAD_WIN32
+ if ( buttonId >= GuiInput::P1_KBD_Start && buttonId <= GuiInput::P1_KBD_Right )
+ {
+ //This is a super sprint Key.
+ if ( context == CONTEXT_SUPERSPRINT_FE || context == CONTEXT_SUPERSPRINT )
+ {
+ unsigned int button = (buttonId - GuiInput::P1_KBD_Start);
+ int player = 3;
+
+ enum
+ {
+ Start,
+ Select,
+ Back,
+ Left,
+ Right
+ };
+
+ switch( button )
+ {
+ case Start:
+ this->Start( player );
+ break;
+ case Select:
+ this->Select( player );
+ break;
+ case Back:
+ this->Back( player );
+ break;
+ case Left:
+ this->Left( player );
+ break;
+ case Right:
+ this->Right( player );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+#endif
+ break;
+ }
+ }
+}
+
+void CGuiUserInputHandler::LoadControllerMappings( unsigned int controllerId )
+{
+ // now set controller mappings
+ for( int i = 0; i < NUM_GUI_CONTROL_MAPPINGS; i++ )
+ {
+ this->Map( GUI_CONTROL_MAP[ i ].inputName,
+ GUI_CONTROL_MAP[ i ].inputID,
+ 0,
+ controllerId );
+ }
+}
+
+void CGuiUserInputHandler::Update( unsigned int elapsedTime, unsigned int controllerId )
+{
+ if( !this->IsActive() )
+ {
+ this->ResetRepeatableButtons();
+ }
+
+#ifndef RAD_WIN32
+ // check for repeated DPad inputs
+ //
+ for( unsigned int i = 0; i < sizeof( m_buttonDownDuration ) /
+ sizeof( m_buttonDownDuration[ 0 ] ); i++ )
+ {
+ // check if button is still down
+ if( this->IsButtonDown( i ) )
+ {
+ m_buttonDownDuration[ i ] += elapsedTime;
+
+ if( m_buttonDownDuration[ i ] > INPUT_REPEAT_PERIOD )
+ {
+ // repeat button down event
+ this->OnButtonDown( controllerId, i, NULL );
+
+ m_buttonDownDuration[ i ] = (m_buttonDownDuration[ i ] + elapsedTime ) % INPUT_REPEAT_PERIOD;
+ }
+ }
+ }
+#endif
+
+ // check for repeated Thumbstick inputs
+ //
+ if( m_XAxisValue > ANALOG_BUTTON_THRESHOLD )
+ {
+ m_XAxisDuration += elapsedTime;
+
+ if( m_XAxisDuration > INPUT_REPEAT_PERIOD )
+ {
+ // repeat right input
+ this->Right( controllerId );
+
+ m_XAxisDuration = (m_XAxisDuration + elapsedTime ) % INPUT_REPEAT_PERIOD;
+ }
+ }
+
+ if( m_XAxisValue < -ANALOG_BUTTON_THRESHOLD )
+ {
+ m_XAxisDuration += elapsedTime;
+
+ if( m_XAxisDuration > INPUT_REPEAT_PERIOD )
+ {
+ // repeat left input
+ this->Left( controllerId );
+
+ m_XAxisDuration = (m_XAxisDuration + elapsedTime ) % INPUT_REPEAT_PERIOD;
+ }
+ }
+
+ if( m_YAxisValue > ANALOG_BUTTON_THRESHOLD )
+ {
+ m_YAxisDuration += elapsedTime;
+
+ if( m_YAxisDuration > INPUT_REPEAT_PERIOD )
+ {
+ // repeat up input
+ this->Up( controllerId );
+
+ m_YAxisDuration = (m_YAxisDuration + elapsedTime ) % INPUT_REPEAT_PERIOD;
+ }
+ }
+
+ if( m_YAxisValue < -ANALOG_BUTTON_THRESHOLD )
+ {
+ m_YAxisDuration += elapsedTime;
+
+ if( m_YAxisDuration > INPUT_REPEAT_PERIOD )
+ {
+ // repeat down input
+ this->Down( controllerId );
+
+ m_YAxisDuration = (m_YAxisDuration + elapsedTime ) % INPUT_REPEAT_PERIOD;
+ }
+ }
+
+#ifdef RAD_WIN32
+ if( m_LeftValue > ANALOG_BUTTON_THRESHOLD )
+ {
+ m_LeftDuration += elapsedTime;
+
+ if( m_LeftDuration > INPUT_REPEAT_PERIOD )
+ {
+ // repeat down input
+ Left( controllerId );
+
+ m_LeftDuration = (m_LeftDuration + elapsedTime ) % INPUT_REPEAT_PERIOD;
+ }
+ }
+ if( m_RightValue > ANALOG_BUTTON_THRESHOLD )
+ {
+ m_RightDuration += elapsedTime;
+
+ if( m_RightDuration > INPUT_REPEAT_PERIOD )
+ {
+ // repeat down input
+ Right( controllerId );
+
+ m_RightDuration = (m_RightDuration + elapsedTime ) % INPUT_REPEAT_PERIOD;
+ }
+ }
+ if( m_UpValue > ANALOG_BUTTON_THRESHOLD )
+ {
+ m_UpDuration += elapsedTime;
+
+ if( m_UpDuration > INPUT_REPEAT_PERIOD )
+ {
+ // repeat down input
+ Up( controllerId );
+
+ m_UpDuration = (m_UpDuration + elapsedTime ) % INPUT_REPEAT_PERIOD;
+ }
+ }
+ if( m_DownValue > ANALOG_BUTTON_THRESHOLD )
+ {
+ m_DownDuration += elapsedTime;
+
+ if( m_DownDuration > INPUT_REPEAT_PERIOD )
+ {
+ // repeat down input
+ Down( controllerId );
+
+ m_DownDuration = (m_DownDuration + elapsedTime ) % INPUT_REPEAT_PERIOD;
+ }
+ }
+#endif
+}
+
+bool
+CGuiUserInputHandler::IsXAxisOnLeft() const
+{
+#ifdef RAD_WIN32
+ return( m_LeftValue > ANALOG_BUTTON_THRESHOLD );
+#else
+ return( m_XAxisValue < -ANALOG_BUTTON_THRESHOLD );
+#endif
+}
+
+bool
+CGuiUserInputHandler::IsXAxisOnRight() const
+{
+#ifdef RAD_WIN32
+ return( m_RightValue > ANALOG_BUTTON_THRESHOLD );
+#else
+ return( m_XAxisValue > ANALOG_BUTTON_THRESHOLD );
+#endif
+}
+
+bool
+CGuiUserInputHandler::IsYAxisOnUp() const
+{
+#ifdef RAD_WIN32
+ return( m_UpValue > ANALOG_BUTTON_THRESHOLD );
+#else
+ return( m_YAxisValue > ANALOG_BUTTON_THRESHOLD );
+#endif
+}
+
+bool
+CGuiUserInputHandler::IsYAxisOnDown() const
+{
+#ifdef RAD_WIN32
+ return( m_DownValue > ANALOG_BUTTON_THRESHOLD );
+#else
+ return( m_YAxisValue < -ANALOG_BUTTON_THRESHOLD );
+#endif
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+void
+CGuiUserInputHandler::ResetRepeatableButtons()
+{
+ for( unsigned int i = 0; i < sizeof( m_buttonDownDuration ) /
+ sizeof( m_buttonDownDuration[ 0 ] ); i++ )
+ {
+ m_buttonDownDuration[ i ] = -INPUT_REPEAT_WAIT;
+ }
+
+ m_XAxisValue = 0.0f;
+ m_YAxisValue = 0.0f;
+ m_XAxisDuration = -INPUT_REPEAT_WAIT;
+ m_YAxisDuration = -INPUT_REPEAT_WAIT;
+
+#ifdef RAD_WIN32
+ m_RightValue = 0;
+ m_LeftValue = 0;
+ m_UpValue = 0;
+ m_DownValue = 0;
+ m_RightDuration = 0;
+ m_LeftDuration = 0;
+ m_UpDuration = 0;
+ m_DownDuration = 0;
+#endif
+}
+
diff --git a/game/code/presentation/gui/guiuserinputhandler.h b/game/code/presentation/gui/guiuserinputhandler.h
new file mode 100644
index 0000000..af55835
--- /dev/null
+++ b/game/code/presentation/gui/guiuserinputhandler.h
@@ -0,0 +1,162 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiUserInputHandler
+//
+// Description: This class feeds the inputs received by the controller
+// system into the GUI system.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/10/20 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUIUSERINPUTHANDLER_H
+#define GUIUSERINPUTHANDLER_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <input/mappable.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiSystem;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+namespace GuiInput
+{
+ // Definition of control points for an abstracted player controller
+ //
+ enum eGuiInput
+ {
+ UNKNOWN = -1,
+
+ Left,
+ Right,
+ Up,
+ Down,
+
+ NUM_DPAD_INPUTS,
+
+ L1,
+ R1,
+ XAxis,
+ YAxis,
+ XAxisRight,
+ YAxisRight,
+ Start,
+ Select,
+ Back,
+ AuxX,
+ AuxY,
+
+ //These are just for the PS2 GT Wheel.
+ AuxStart,
+ AuxSelect,
+ AuxBack,
+ AuxUp,
+ AuxDown,
+ AuxXAxis,
+
+#ifdef RAD_WIN32
+ P1_KBD_Start,
+ P1_KBD_Select,
+ P1_KBD_Back,
+ P1_KBD_Left,
+ P1_KBD_Right,
+#endif
+
+ NUM_GUI_INPUTS
+ };
+};
+
+class CGuiUserInputHandler : public Mappable
+{
+public:
+
+ CGuiUserInputHandler( void );
+ virtual ~CGuiUserInputHandler( void );
+
+ void Left( int controllerId = 0 );
+ void Right( int controllerId = 0 );
+ void Up( int controllerId = 0 );
+ void Down( int controllerId = 0 );
+ void Start( int controllerId = 0 );
+ void Select( int controllerId = 0 );
+ void Back( int controllerId = 0 );
+ void AuxX( int controllerId = 0 );
+ void AuxY( int controllerId = 0 );
+ void L1( int controllerId = 0 );
+ void R1( int controllerId = 0 );
+
+ // Mappable interface declarations
+ //
+ virtual void OnButton( int controllerId, int buttonId, const IButton* pButton );
+ virtual void OnButtonUp( int controllerId, int buttonId, const IButton* pButton );
+ virtual void OnButtonDown( int controllerId, int buttonId, const IButton* pButton );
+
+ // Mappable interface declarations.
+ // Dispatch a message when controller is disconnected.
+ //
+ virtual void OnControllerDisconnect( int id );
+
+ // Mappable interface declarations.
+ // Dispatch a message when controller is connected.
+ //
+ virtual void OnControllerConnect( int id );
+
+ // Mappable interface declarations
+ //
+ virtual void LoadControllerMappings( unsigned int controllerId );
+
+ // Update repeated input states
+ void Update( unsigned int elapsedTime, unsigned int controllerId );
+
+ bool IsXAxisOnLeft() const;
+ bool IsXAxisOnRight() const;
+ bool IsYAxisOnUp() const;
+ bool IsYAxisOnDown() const;
+
+ void EnableStartToSelectMapping( bool isEnabled ) { m_isStartToSelectMappingEnabled = isEnabled; }
+
+private:
+ // Disallow object copying or assigning until we know we need it
+ //
+ CGuiUserInputHandler( const CGuiUserInputHandler& original );
+ CGuiUserInputHandler& operator=( const CGuiUserInputHandler& rhs );
+
+ void ResetRepeatableButtons();
+
+ int m_buttonDownDuration[ GuiInput::NUM_DPAD_INPUTS ];
+
+ float m_XAxisValue;
+ float m_YAxisValue;
+
+ int m_XAxisDuration;
+ int m_YAxisDuration;
+
+#ifdef RAD_WIN32
+ float m_RightValue;
+ float m_LeftValue;
+ float m_UpValue;
+ float m_DownValue;
+
+ int m_RightDuration;
+ int m_LeftDuration;
+ int m_UpDuration;
+ int m_DownDuration;
+#endif
+
+ bool m_isStartToSelectMappingEnabled : 1;
+
+};
+
+#endif // GUIUSERINPUTHANDLER_H
diff --git a/game/code/presentation/gui/guiwindow.cpp b/game/code/presentation/gui/guiwindow.cpp
new file mode 100644
index 0000000..19c2193
--- /dev/null
+++ b/game/code/presentation/gui/guiwindow.cpp
@@ -0,0 +1,241 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiWindow
+//
+// Description: Implementation of the CGuiWindow class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/21 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <memory/classsizetracker.h>
+#include <presentation/gui/guiwindow.h>
+#include <presentation/gui/guimanager.h>
+#include <raddebug.hpp> // Foundation
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiWindow::CGuiWindow
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiWindow::CGuiWindow
+(
+ eGuiWindowID id,
+ CGuiEntity* pParent
+)
+:
+ CGuiEntity( pParent ),
+ m_state( GUI_WINDOW_STATE_UNITIALIZED ),
+ m_ID( id ),
+ m_numTransitionsPending( 0 ),
+ m_firstTimeEntered( true )
+{
+ CLASSTRACKER_CREATE( CGuiWindow );
+}
+
+
+//===========================================================================
+// CGuiWindow::~CGuiWindow
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiWindow::~CGuiWindow()
+{
+ CLASSTRACKER_CREATE( CGuiWindow );
+}
+
+
+//===========================================================================
+// CGuiWindow::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiWindow::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ switch( message )
+ {
+ case GUI_MSG_WINDOW_ENTER:
+ {
+/*
+ // Ignore multiple enter requests.
+ //
+ if( GUI_WINDOW_STATE_UNITIALIZED != m_state )
+ {
+ break;
+ }
+*/
+ m_state = GUI_WINDOW_STATE_INTRO;
+ this->InitIntro();
+
+ break;
+ }
+
+ case GUI_MSG_WINDOW_EXIT:
+ {
+/*
+ if( GUI_WINDOW_STATE_OUTRO == m_state ||
+ GUI_WINDOW_STATE_UNITIALIZED == m_state )
+ {
+ // Ignore multiple exit requests.
+ //
+ break;
+ }
+*/
+ m_state = GUI_WINDOW_STATE_OUTRO;
+ this->InitOutro();
+
+ break;
+ }
+
+ case GUI_MSG_WINDOW_PAUSE:
+ {
+ m_prevState = m_state;
+ m_state = GUI_WINDOW_STATE_PAUSED;
+ break;
+ }
+
+ case GUI_MSG_WINDOW_RESUME:
+ {
+ m_state = m_prevState;
+ break;
+ }
+
+ case GUI_MSG_UPDATE:
+ {
+ if( GUI_WINDOW_STATE_UNITIALIZED == m_state
+ || GUI_WINDOW_STATE_PAUSED == m_state )
+ {
+ return;
+ }
+
+ switch( m_state )
+ {
+ case GUI_WINDOW_STATE_INTRO:
+ {
+ // The intro transition is complete if there are no more
+ // non-persistent sequencers running.
+ //
+ if( m_numTransitionsPending == 0 )
+ {
+ m_state = GUI_WINDOW_STATE_RUNNING;
+ this->InitRunning();
+ }
+
+ break;
+ }
+
+ case GUI_WINDOW_STATE_OUTRO:
+ {
+ // The outro transition is complete if there are no more
+ // non-persistent sequencers running.
+ //
+ if( m_numTransitionsPending == 0 )
+ {
+ // wait one more frame for last transition update to get rendered
+ //
+ m_numTransitionsPending--;
+ CleanUp();
+ }
+ else if( m_numTransitionsPending < 0 )
+ {
+ m_state = GUI_WINDOW_STATE_UNITIALIZED;
+ m_firstTimeEntered = false;
+
+ m_pParent->HandleMessage( GUI_MSG_WINDOW_FINISHED );
+ }
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//===========================================================================
+// CGuiWindow::CleanUp
+//===========================================================================
+// Description: Called after the last outro transition is done
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiWindow::CleanUp()
+{
+ //do nothing
+}
+
+//===========================================================================
+// CGuiWindow::ForceClearTransitions
+//===========================================================================
+// Description: Forces all transitions to think they are done
+//
+// Constraints: None.
+//
+// Parameters:
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiWindow::ForceClearTransitions()
+{
+ m_numTransitionsPending = 0;
+} \ No newline at end of file
diff --git a/game/code/presentation/gui/guiwindow.h b/game/code/presentation/gui/guiwindow.h
new file mode 100644
index 0000000..c7d3aa1
--- /dev/null
+++ b/game/code/presentation/gui/guiwindow.h
@@ -0,0 +1,188 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiWindow
+//
+// Description: Interface for the CGuiWindow class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/20 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUIWINDOW_H
+#define GUIWINDOW_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guientity.h>
+#include <presentation/gui/guimenu.h>
+#ifdef RAD_WIN32
+#include <input/FEMouse.h>
+#endif
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiManager;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiWindow : public CGuiEntity
+{
+ public:
+ enum eGuiWindowID
+ {
+ GUI_WINDOW_ID_UNDEFINED = -1,
+
+ // generic screens
+ //
+ GUI_SCREEN_ID_GENERIC_MESSAGE,
+ GUI_SCREEN_ID_GENERIC_PROMPT,
+ GUI_SCREEN_ID_ERROR_PROMPT,
+
+ // bootup and backend screens
+ //
+ GUI_SCREEN_ID_BOOTUP_LOAD,
+ GUI_SCREEN_ID_LICENSE,
+ GUI_SCREEN_ID_LANGUAGE,
+ GUI_SCREEN_ID_LOADING,
+ GUI_SCREEN_ID_LOADING_FE,
+ GUI_SCREEN_ID_DEMO,
+
+ GUI_SCREEN_ID_MEMORY_CARD,
+ GUI_SCREEN_ID_MEMORY_CARD_CHECK,
+ GUI_SCREEN_ID_AUTO_LOAD,
+
+ // front-end screens
+ //
+ GUI_SCREEN_ID_SPLASH,
+ GUI_SCREEN_ID_INTRO_TRANSITION,
+ GUI_SCREEN_ID_MAIN_MENU,
+ GUI_SCREEN_ID_LOAD_GAME,
+ GUI_SCREEN_ID_SCRAP_BOOK,
+ GUI_SCREEN_ID_SCRAP_BOOK_CONTENTS,
+ GUI_SCREEN_ID_SCRAP_BOOK_STATS,
+ GUI_SCREEN_ID_CARD_GALLERY,
+ GUI_SCREEN_ID_MISSION_GALLERY,
+ GUI_SCREEN_ID_SKIN_GALLERY,
+ GUI_SCREEN_ID_VEHICLE_GALLERY,
+// GUI_SCREEN_ID_MULTIPLAYER_SETUP,
+// GUI_SCREEN_ID_MULTIPLAYER_CHOOSE_CHARACTER,
+ GUI_SCREEN_ID_OPTIONS,
+ GUI_SCREEN_ID_CONTROLLER,
+ GUI_SCREEN_ID_SOUND,
+ GUI_SCREEN_ID_VIEW_MOVIES,
+ GUI_SCREEN_ID_VIEW_CREDITS,
+#ifdef RAD_WIN32
+ GUI_SCREEN_ID_DISPLAY,
+#endif
+ GUI_SCREEN_ID_PLAY_MOVIE,
+ GUI_SCREEN_ID_PLAY_MOVIE_DEMO,
+ GUI_SCREEN_ID_PLAY_MOVIE_INTRO,
+ GUI_SCREEN_ID_PLAY_MOVIE_NEW_GAME,
+
+ // in-game screens
+ //
+ GUI_SCREEN_ID_HUD,
+ GUI_SCREEN_ID_MULTI_HUD,
+ GUI_SCREEN_ID_PAUSE_SUNDAY,
+ GUI_SCREEN_ID_PAUSE_MISSION,
+ GUI_SCREEN_ID_MISSION_SELECT,
+ GUI_SCREEN_ID_SETTINGS,
+ GUI_SCREEN_ID_LEVEL_STATS,
+ GUI_SCREEN_ID_VIEW_CARDS,
+ GUI_SCREEN_ID_SAVE_GAME,
+ GUI_SCREEN_ID_MISSION_LOAD,
+ GUI_SCREEN_ID_MISSION_OVER,
+ GUI_SCREEN_ID_MISSION_SUCCESS,
+ GUI_SCREEN_ID_LETTER_BOX,
+ GUI_SCREEN_ID_PHONE_BOOTH,
+ GUI_SCREEN_ID_PURCHASE_REWARDS,
+ GUI_SCREEN_ID_IRIS_WIPE,
+ GUI_SCREEN_ID_LEVEL_END,
+ GUI_SCREEN_ID_TUTORIAL,
+
+ // mini-game screens
+ //
+ GUI_SCREEN_ID_MINI_MENU,
+ GUI_SCREEN_ID_MINI_HUD,
+ GUI_SCREEN_ID_MINI_PAUSE,
+ GUI_SCREEN_ID_MINI_SUMMARY,
+
+ NUM_GUI_WINDOW_IDS
+ };
+
+ CGuiWindow( eGuiWindowID id, CGuiEntity* pParent );
+ virtual ~CGuiWindow();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return NULL; }
+ eGuiWindowID GetWindowID( void ) const { return( m_ID ); };
+ void ForceClearTransitions();
+
+ bool IsRunning(void) {return m_state == GUI_WINDOW_STATE_RUNNING;}
+
+#ifdef RAD_WIN32
+ virtual eFEHotspotType CheckCursorAgainstHotspots( float x, float y )
+ {
+ return HOTSPOT_NONE;
+ }
+#endif
+ protected:
+
+ //---------------------------------------------------------------------
+ // Protected Functions
+ //---------------------------------------------------------------------
+
+ virtual void InitIntro() = 0;
+ virtual void InitRunning() = 0;
+ virtual void InitOutro() = 0;
+ virtual void CleanUp();
+
+ enum eGuiWindowState
+ {
+ GUI_WINDOW_STATE_UNITIALIZED,
+ GUI_WINDOW_STATE_INTRO,
+ GUI_WINDOW_STATE_RUNNING,
+ GUI_WINDOW_STATE_PAUSED,
+ GUI_WINDOW_STATE_IDLE,
+ GUI_WINDOW_STATE_OUTRO,
+
+ GUI_WINDOW_STATE_DISABLED,
+
+ GUI_WINDOW_STATE_COUNT
+ };
+
+ eGuiWindowState m_state;
+ eGuiWindowState m_prevState;
+
+ eGuiWindowID m_ID;
+
+ int m_numTransitionsPending;
+
+ bool m_firstTimeEntered;
+
+ private:
+
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignement. Declare but don't define.
+ //
+ CGuiWindow( const CGuiWindow& );
+ CGuiWindow& operator= ( const CGuiWindow& );
+
+};
+
+#endif // GUIWINDOW_H
diff --git a/game/code/presentation/gui/ingame/allingame.cpp b/game/code/presentation/gui/ingame/allingame.cpp
new file mode 100644
index 0000000..ee04a6f
--- /dev/null
+++ b/game/code/presentation/gui/ingame/allingame.cpp
@@ -0,0 +1,27 @@
+#include <presentation/gui/ingame/guimanageringame.cpp>
+#include <presentation/gui/ingame/guiscreenhastransitions.cpp>
+#include <presentation/gui/ingame/guiscreenhud.cpp>
+#include <presentation/gui/ingame/guiscreeniriswipe.cpp>
+#include <presentation/gui/ingame/guiscreenlevelstats.cpp>
+#include <presentation/gui/ingame/guiscreenlevelend.cpp>
+#include <presentation/gui/ingame/guiscreenletterbox.cpp>
+#include <presentation/gui/ingame/guiscreenmissionbase.cpp>
+#include <presentation/gui/ingame/guiscreenmissionload.cpp>
+#include <presentation/gui/ingame/guiscreenmissionover.cpp>
+#include <presentation/gui/ingame/guiscreenmissionsuccess.cpp>
+#include <presentation/gui/ingame/guiscreenmultihud.cpp>
+#include <presentation/gui/ingame/guiscreenpause.cpp>
+#include <presentation/gui/ingame/guiscreenpausemission.cpp>
+#include <presentation/gui/ingame/guiscreenpauseoptions.cpp>
+#include <presentation/gui/ingame/guiscreenpausecontroller.cpp>
+#include <presentation/gui/ingame/guiscreenpausesound.cpp>
+#include <presentation/gui/ingame/guiscreenpausesettings.cpp>
+#include <presentation/gui/ingame/guiscreenpausesunday.cpp>
+#include <presentation/gui/ingame/guiscreenrewards.cpp>
+#include <presentation/gui/ingame/guiscreenphonebooth.cpp>
+#include <presentation/gui/ingame/guiscreenpurchaserewards.cpp>
+#include <presentation/gui/ingame/guiscreenmissionselect.cpp>
+#include <presentation/gui/ingame/guiscreensavegame.cpp>
+#include <presentation/gui/ingame/guiscreenviewcards.cpp>
+#include <presentation/gui/ingame/guiscreentutorial.cpp>
+#include <presentation/gui/ingame/guiscreencreditspostfmv.cpp>
diff --git a/game/code/presentation/gui/ingame/guihudtextbox.h b/game/code/presentation/gui/ingame/guihudtextbox.h
new file mode 100644
index 0000000..4645818
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guihudtextbox.h
@@ -0,0 +1,41 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/07/27 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUIHUDTEXTBOX_H
+#define GUIHUDTEXTBOX_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+//===========================================================================
+// External Constants
+//===========================================================================
+
+const float MESSAGE_TEXT_SCALE = 0.8f;
+const float MESSGAE_TEXT_HORIZONTAL_STRETCH = 1.1f;
+
+#ifdef RAD_WIN32
+const float MESSAGE_BOX_CORRECTION_SCALE = 1.0f;
+const float MESSAGE_BOX_HORIZONTAL_STRETCH = 1.1f;
+#else
+const float MESSAGE_BOX_CORRECTION_SCALE = 2.0f;
+const float MESSAGE_BOX_HORIZONTAL_STRETCH = 1.1f;
+#endif
+
+#ifdef PAL
+ const int MESSAGE_TEXT_VERTICAL_TRANSLATION = +10;
+ const float MESSAGE_BOX_VERTICAL_STRETCH = 1.2f;
+#else
+ const int MESSAGE_TEXT_VERTICAL_TRANSLATION = 0;
+ const float MESSAGE_BOX_VERTICAL_STRETCH = 1.0f;
+#endif // PAL
+
+#endif // GUIHUDTEXTBOX_H
diff --git a/game/code/presentation/gui/ingame/guimanageringame.cpp b/game/code/presentation/gui/ingame/guimanageringame.cpp
new file mode 100644
index 0000000..0a32ae3
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guimanageringame.cpp
@@ -0,0 +1,1646 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManagerInGame
+//
+// Description: Implementation of the CGuiManagerInGame class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/21 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/guiwindow.h> // for window IDs
+#include <presentation/gui/guisystem.h>
+
+#include <presentation/gui/guiscreenmessage.h>
+#include <presentation/gui/guiscreenprompt.h>
+#include <presentation/gui/guiscreenmemorycard.h>
+
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guiscreenmultihud.h>
+#include <presentation/gui/ingame/guiscreenpausesunday.h>
+#include <presentation/gui/ingame/guiscreenpausemission.h>
+#include <presentation/gui/ingame/guiscreenmissionselect.h>
+#include <presentation/gui/ingame/guiscreenhudmap.h>
+#include <presentation/gui/ingame/guiscreenpauseoptions.h>
+#ifdef RAD_WIN32
+#include <presentation/gui/ingame/guiscreenpausedisplay.h>
+#endif
+#include <presentation/gui/ingame/guiscreenpausecontroller.h>
+#include <presentation/gui/ingame/guiscreenpausesound.h>
+#include <presentation/gui/ingame/guiscreenpausesettings.h>
+#include <presentation/gui/ingame/guiscreenmissionload.h>
+#include <presentation/gui/ingame/guiscreenmissionover.h>
+#include <presentation/gui/ingame/guiscreenmissionsuccess.h>
+#include <presentation/gui/ingame/guiscreenlevelstats.h>
+#include <presentation/gui/ingame/guiscreenlevelend.h>
+#include <presentation/gui/ingame/guiscreenviewcards.h>
+#include <presentation/gui/ingame/guiscreenletterbox.h>
+#include <presentation/gui/ingame/guiscreeniriswipe.h>
+#include <presentation/gui/ingame/guiscreenphonebooth.h>
+#include <presentation/gui/ingame/guiscreenpurchaserewards.h>
+#include <presentation/gui/ingame/guiscreensavegame.h>
+#include <presentation/gui/ingame/guiscreentutorial.h>
+#include <presentation/gui/ingame/guiscreencreditspostfmv.h>
+#include <presentation/gui/backend/guimanagerbackend.h>
+#include <presentation/gui/backend/guiscreenloadingfe.h>
+#include <presentation/gui/utility/hudmap.h>
+
+#include <contexts/context.h>
+#include <contexts/pausecontext.h>
+#include <contexts/gameplay/gameplaycontext.h>
+#include <data/memcard/memorycardmanager.h>
+#include <events/eventmanager.h>
+#include <gameflow/gameflow.h>
+#include <input/inputmanager.h>
+#include <interiors/interiormanager.h>
+#include <main/commandlineoptions.h>
+#include <memory/srrmemory.h>
+#include <mission/missionmanager.h>
+#include <mission/objectives/missionobjective.h>
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <presentation/presentation.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/renderlayer.h>
+#include <sound/soundmanager.h>
+#include <meta/eventlocator.h>
+
+#include <p3d/fileftt.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/view.hpp>
+
+#include <main/platform.h>
+#include <main/game.h>
+
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <layer.h>
+#include <page.h>
+#include <screen.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const unsigned int MINIMUM_DYNA_LOAD_TIME = 250; // in msec
+
+#ifdef DEBUGWATCH
+ static const char* WATCHER_NAMESPACE = "GUI System - Ingame";
+ bool g_wReversePauseMenus;
+#endif
+
+const char* INGAME_PROJECT_FILES[] =
+{
+ "art\\frontend\\scrooby\\ingame.p3d",
+ "art\\frontend\\scrooby\\pause.p3d",
+ "art\\frontend\\scrooby\\rewards.p3d",
+
+ "" // dummy terminator
+};
+
+CGuiScreenHud* CGuiManagerInGame::s_currentHUD = NULL;
+bool cGuiManagerInGameActive = false;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiManagerInGame::CGuiManagerInGame
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManagerInGame::CGuiManagerInGame
+(
+ Scrooby::Project* pProject,
+ CGuiEntity* pParent
+)
+: CGuiManager( pProject, pParent ),
+ m_nextLevelIndex( -1 ),
+ m_nextMissionIndex( -1 ),
+ m_isLoadingNewMission( false ),
+ m_quitAndReload( false ),
+ m_controllerPromptShown( false ),
+ m_enteringPauseMenu( false ),
+ m_exitingPauseMenu( false ),
+ m_onHudEnterCommand( ON_HUD_ENTER_NO_COMMAND ),
+ m_levelScreen( NULL ),
+ m_levelLayer( NULL ),
+ m_elapsedDynaLoadTime( 0 ),
+ m_pRewardsProject( NULL ),
+ m_unloadMemcardInfoWhenLoaded( false ),
+ m_promptSaveBeforeQuit( true ),
+ m_quitAfterSave( false ),
+#ifdef RAD_WIN32
+ m_quitToSystemAfterSave(false),
+#endif
+ m_isControllerReconnected( false ),
+ m_RecieveIrisClosed( "Recieve Iris Closed" ),
+ m_resumeGameScreenID( CGuiWindow::GUI_WINDOW_ID_UNDEFINED )
+{
+ //
+ // Set Up Transitions
+ //
+ m_RecieveIrisClosed.SetEvent( EVENT_GUI_IRIS_WIPE_CLOSED );
+
+ m_RestartMissionTransition.SetNextTransition( m_RecieveIrisClosed );
+ m_RecieveIrisClosed. SetNextTransition( m_RestartMission );
+ m_RestartMission. SetNextTransition( NULL );
+
+ m_AbortMissionTransition. SetNextTransition( m_AbortMission );
+ m_AbortMission. SetNextTransition( NULL );
+
+ cGuiManagerInGameActive = true;
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_INIT );
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_DONE_AND_FINISHED );
+ GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_TRANSITION_START );
+ GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_TRANSITION_END );
+ GetEventManager()->AddListener( this, EVENT_EXIT_INTERIOR_START );
+ GetEventManager()->AddListener( this, EVENT_EXIT_INTERIOR_END );
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_LOCATOR + LocatorEvent::DEATH) );
+
+ Scrooby::Project* pLevelProject = GetGuiSystem()->GetScroobyLevelProject();
+ if( pLevelProject != NULL )
+ {
+ m_levelScreen = pLevelProject->GetCurrentScreen();
+ rAssert( m_levelScreen );
+
+ // get layer
+ //
+ Scrooby::Page* pPage = m_levelScreen->GetPage( "PauseBgd" );
+ rAssert( pPage );
+ m_levelLayer = pPage->GetLayerByIndex( 0 );
+ rAssert( m_levelLayer );
+ m_levelLayer->SetAlpha( 0.0f );
+ }
+
+#ifdef DEBUGWATCH
+ radDbgWatchAddBoolean( &g_wReversePauseMenus,
+ "Reverse Pause Menus",
+ WATCHER_NAMESPACE );
+#endif
+}
+
+
+//===========================================================================
+// CGuiManagerInGame::~CGuiManagerInGame
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManagerInGame::~CGuiManagerInGame()
+{
+ cGuiManagerInGameActive = false;
+ GetEventManager()->RemoveAll( this );
+
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &g_wReversePauseMenus );
+#endif
+ s_currentHUD = NULL;
+}
+
+
+//===========================================================================
+// CGuiManagerInGame::Popluate
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManagerInGame::Populate()
+{
+MEMTRACK_PUSH_GROUP( "CGUIManagerInGame" );
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ Scrooby::Screen* pScroobyScreen = NULL;
+ CGuiScreen* pScreen = NULL;
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Message" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMessage( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_GENERIC_MESSAGE, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Prompt" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPrompt( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_GENERIC_PROMPT, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "ErrorPrompt" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPrompt( pScroobyScreen, this,
+ CGuiWindow::GUI_SCREEN_ID_ERROR_PROMPT );
+
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_ERROR_PROMPT, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Hud" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenHud( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_HUD, pScreen );
+
+ // store current HUD reference
+ //
+ s_currentHUD = dynamic_cast<CGuiScreenHud*>( pScreen );
+ rAssert( s_currentHUD != NULL );
+ }
+/*
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MultiHud" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMultiHud( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MULTI_HUD, pScreen );
+ }
+*/
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MissionLoad" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMissionLoad( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MISSION_LOAD, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MissionOver" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMissionOver( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MISSION_OVER, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MissionLoad" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMissionSuccess( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MISSION_SUCCESS, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "LevelStats" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenLevelStats( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_LEVEL_STATS, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "LevelEnd" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenLevelEnd( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_LEVEL_END, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "PauseViewCards" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenViewCards( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_VIEW_CARDS, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "LetterBox" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenLetterBox( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_LETTER_BOX, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "IrisWipe" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenIrisWipe( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_IRIS_WIPE, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "PhoneBooth" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPhoneBooth( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_PHONE_BOOTH, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "PurchaseRewards" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPurchaseRewards( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_PURCHASE_REWARDS, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "PauseSunday" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPauseSunday( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_PAUSE_SUNDAY, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "PauseMission" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPauseMission( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_PAUSE_MISSION, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "PauseOptions" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPauseOptions( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_OPTIONS, pScreen );
+ }
+#ifdef RAD_WIN32
+ pScroobyScreen = m_pScroobyProject->GetScreen( "PauseDisplay" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPauseDisplay( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_DISPLAY, pScreen );
+ }
+#endif
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "PauseController" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPauseController( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_CONTROLLER, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "PauseSound" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPauseSound( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_SOUND, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "PauseSettings" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenPauseSettings( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_SETTINGS, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MissionSelect" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMissionSelect( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MISSION_SELECT, pScreen );
+ }
+/*
+ pScroobyScreen = m_pScroobyProject->GetScreen( "ViewMap" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenHudMap( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_HUD_MAP, pScreen );
+ }
+*/
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MemoryCard" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenMemoryCard( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MEMORY_CARD, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "SaveGame" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenSaveGame( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_SAVE_GAME, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Tutorial" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenTutorial( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_TUTORIAL, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "CreditsPostFMV" );
+ if( pScroobyScreen != NULL )
+ {
+ pScreen = new CGuiScreenCreditsPostFMV( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_VIEW_CREDITS, pScreen );
+ }
+
+ // hide HUD if specified in commandline options
+ //
+ if( CommandLineOptions::Get( CLO_NO_HUD ) )
+ {
+ if( s_currentHUD != NULL )
+ {
+ s_currentHUD->SetVisible( false );
+ }
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+MEMTRACK_POP_GROUP("CGUIManagerInGame");
+}
+
+void
+CGuiManagerInGame::Start( CGuiWindow::eGuiWindowID initialWindow )
+{
+ rAssert( GUI_FE_UNINITIALIZED == m_state || m_isLoadingNewMission );
+
+ m_nextScreen = initialWindow != CGuiWindow::GUI_WINDOW_ID_UNDEFINED ?
+ initialWindow :
+ CGuiWindow::GUI_SCREEN_ID_HUD;
+
+ m_state = GUI_FE_CHANGING_SCREENS; // must be set before calling GotoScreen()
+
+ CGuiScreen* nextScreen = static_cast< CGuiScreen* >( this->FindWindowByID( m_nextScreen ) );
+ rAssert( nextScreen != NULL );
+ m_pScroobyProject->GotoScreen( nextScreen->GetScroobyScreen(), this );
+}
+
+//===========================================================================
+// CGuiManagerInGame::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManagerInGame::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_FE_DYNAMIC_LOADING )
+ {
+ rAssertMsg( false, "Dynamic loading of pause menu and HUD is no longer implemented!" );
+
+ return;
+ }
+
+ if( m_isLoadingNewMission )
+ {
+ if( message == GUI_MSG_UPDATE )
+ {
+ this->UpdateDuringMissionLoading( param1 );
+ }
+ else if( message == GUI_MSG_RESUME_INGAME )
+ {
+ this->OnNewMissionLoadEnd();
+ }
+
+ CGuiManager::HandleMessage( message, param1, param2 );
+
+ // and ignore all other messages
+ //
+ return;
+ }
+
+ if( m_state == GUI_FE_TERMINATED )
+ {
+ if( message == GUI_MSG_UPDATE )
+ {
+ this->UpdateWhileLoadingNotDone( param1 );
+ }
+
+ // and ignore all other messages
+ //
+ return;
+ }
+
+#ifdef RAD_DEMO
+ if( this->IsControllerMessage( message ) )
+ {
+ GetGameplayManager()->ResetIdleTime();
+ }
+#endif
+
+ switch( message )
+ {
+ case GUI_MSG_PROMPT_START_RESPONSE:
+ {
+ HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+ break;
+ }
+ case GUI_MSG_QUIT_INGAME_FOR_RELOAD:
+ {
+ m_quitAndReload = true;
+ m_nextLevelIndex = static_cast<int>( param1 );
+ m_nextMissionIndex = static_cast<int>( param2 );
+
+ if( GetGameplayManager()->GetLevelComplete() )
+ {
+ if( GetGameplayManager()->GetGameComplete() )
+ {
+ // wrap back to level 1, mission 1 (not the tutorial mission)
+ //
+ GetCharacterSheetManager()->SetCurrentMission( RenderEnums::L1, RenderEnums::M2 ); // M2 = mission 1
+
+ // tell GUI system to show credits upon returning to FE
+ //
+// GetGuiSystem()->ShowCreditsUponReturnToFE( true );
+
+ m_quitAndReload = false;
+ }
+ else
+ {
+ GetCharacterSheetManager()->SetCurrentMission( static_cast<RenderEnums::LevelEnum>( m_nextLevelIndex ),
+ static_cast<RenderEnums::MissionEnum>( m_nextMissionIndex ) );
+ }
+
+#ifdef RAD_DEMO
+ m_promptSaveBeforeQuit = false;
+ m_quitAndReload = false;
+#else
+ this->DisplayPrompt( PROMPT_CONFIRM_SAVE_BEFORE_QUIT,
+ this,
+ PROMPT_TYPE_YES_NO,
+ false );
+
+ break;
+#endif // RAD_DEMO
+ }
+ else
+ {
+ // don't prompt to save unless we're advancing to a new level
+ //
+ m_promptSaveBeforeQuit = false;
+ }
+
+ // follow-thru ...
+ //
+ }
+
+ case GUI_MSG_QUIT_INGAME:
+ {
+ if( GUI_FE_SCREEN_RUNNING == m_state )
+ {
+#ifndef RAD_DEMO
+ if( m_promptSaveBeforeQuit && GetGameplayManager()->IsSundayDrive() )
+ {
+ this->DisplayPrompt( PROMPT_CONFIRM_SAVE_BEFORE_QUIT,
+ this,
+ PROMPT_TYPE_YES_NO,
+ false );
+ }
+ else
+#endif
+ {
+ m_state = GUI_FE_SHUTTING_DOWN;
+
+ // if paused in sunday drive mode, unload memory card info
+ //
+ if( GetGameplayManager()->IsSundayDrive() )
+ {
+ if( GetMemoryCardManager()->IsMemcardInfoLoaded() )
+ {
+ // unload it now
+ //
+ GetMemoryCardManager()->UnloadMemcardInfo();
+ }
+ else
+ {
+ // wait until loading is complete, then unload it
+ //
+ m_unloadMemcardInfoWhenLoaded = true;
+ }
+ }
+/*
+ CGuiScreenPrompt* promptScreen = static_cast<CGuiScreenPrompt*>( this->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_GENERIC_PROMPT ) );
+ rAssert( promptScreen != NULL );
+ promptScreen->RestoreScreenCover();
+*/
+ // Tell the current screen to shut down.
+ //
+ this->FindWindowByID( m_currentScreen )->HandleMessage( GUI_MSG_WINDOW_EXIT );
+ }
+ }
+
+ break;
+ }
+#ifdef RAD_WIN32
+ case GUI_MSG_QUIT_TO_SYSTEM:
+ {
+ if( GUI_FE_SCREEN_RUNNING == m_state )
+ {
+ if( m_promptSaveBeforeQuit && GetGameplayManager()->IsSundayDrive() )
+ {
+ this->DisplayPrompt( PROMPT_CONFIRM_SAVE_BEFORE_QUITTOSYSTEM,
+ this,
+ PROMPT_TYPE_YES_NO,
+ false );
+ }
+ else
+ {
+ m_state = GUI_FE_SHUTTING_DOWN;
+
+ // if paused in sunday drive mode, unload memory card info
+ //
+ if( GetGameplayManager()->IsSundayDrive() )
+ {
+ if( GetMemoryCardManager()->IsMemcardInfoLoaded() )
+ {
+ // unload it now
+ //
+ GetMemoryCardManager()->UnloadMemcardInfo();
+ }
+ else
+ {
+ // wait until loading is complete, then unload it
+ //
+ m_unloadMemcardInfoWhenLoaded = true;
+ }
+ }
+ /*
+ CGuiScreenPrompt* promptScreen = static_cast<CGuiScreenPrompt*>( this->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_GENERIC_PROMPT ) );
+ rAssert( promptScreen != NULL );
+ promptScreen->RestoreScreenCover();
+ */
+ // Tell the current screen to shut down.
+ //
+ this->FindWindowByID( m_currentScreen )->HandleMessage( GUI_MSG_WINDOW_EXIT );
+
+ // let's begin the quit procedure
+ //
+ GetGameFlow()->SetContext( CONTEXT_EXIT );
+ }
+ }
+
+ break;
+ }
+#endif
+ case GUI_MSG_MENU_PROMPT_RESPONSE:
+ {
+#ifdef RAD_WIN32
+ if( param1 == PROMPT_CONFIRM_SAVE_BEFORE_QUITTOSYSTEM )
+ {
+ if( param2 == CGuiMenuPrompt::RESPONSE_YES )
+ {
+ this->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_SAVE_GAME );
+
+ m_quitToSystemAfterSave = true;
+ }
+ else
+ {
+ rAssert( param2 == CGuiMenuPrompt::RESPONSE_NO );
+
+ m_promptSaveBeforeQuit = false;
+
+ this->HandleMessage( GUI_MSG_QUIT_TO_SYSTEM );
+ }
+
+ }
+#endif
+ if( param1 == PROMPT_CONFIRM_SAVE_BEFORE_QUIT )
+ {
+ if( param2 == CGuiMenuPrompt::RESPONSE_YES )
+ {
+#ifdef RAD_XBOX
+ // Xbox TCR Requirement: always prompt user to select memory
+ // device before loading/saving
+ //
+ CGuiScreenLoadSave::s_forceGotoMemoryCardScreen = true;
+#endif
+// this->PushScreenHistory( GetGameplayManager()->IsSundayDrive() ?
+// CGuiWindow::GUI_SCREEN_ID_PAUSE_SUNDAY :
+// CGuiWindow::GUI_SCREEN_ID_PAUSE_MISSION );
+
+ this->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_SAVE_GAME );
+
+ m_quitAfterSave = true;
+ }
+ else
+ {
+ rAssert( param2 == CGuiMenuPrompt::RESPONSE_NO );
+
+ m_promptSaveBeforeQuit = false;
+
+ this->HandleMessage( GUI_MSG_QUIT_INGAME );
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_PAUSE_INGAME:
+ {
+ if( !this->IsPausingAllowed() )
+ {
+ rTuneWarningMsg( false, "*** Pause request ingored! ***" );
+ break;
+ }
+
+ // switch to pause context
+ //
+ GetGameFlow()->SetContext( CONTEXT_PAUSE );
+
+ bool inSundayDriveMode = GetGameplayManager()->IsSundayDrive();
+#ifdef DEBUGWATCH
+ if( g_wReversePauseMenus )
+ {
+ inSundayDriveMode = !inSundayDriveMode;
+ }
+#endif
+ CGuiWindow::eGuiWindowID pauseWindow = inSundayDriveMode ?
+ CGuiWindow::GUI_SCREEN_ID_PAUSE_SUNDAY :
+ CGuiWindow::GUI_SCREEN_ID_PAUSE_MISSION;
+
+ // remember current screen before going to pause menu
+ //
+ m_resumeGameScreenID = m_currentScreen;
+
+ CGuiManager::HandleMessage( GUI_MSG_GOTO_SCREEN,
+ pauseWindow,
+ CLEAR_WINDOW_HISTORY );
+
+ // if pausing in sunday drive mode, load memory card info
+ // for saving games
+ //
+ if( GetGameplayManager()->IsSundayDrive() )
+ {
+ m_unloadMemcardInfoWhenLoaded = false;
+ GetMemoryCardManager()->LoadMemcardInfo( this );
+ }
+
+ GetSoundManager()->OnPauseStart();
+
+ m_enteringPauseMenu = true;
+ GetGameplayContext()->PauseAllButPresentation( true );
+
+ m_promptSaveBeforeQuit = true;
+
+ break;
+ }
+ case GUI_MSG_UNPAUSE_INGAME:
+ {
+ if( GetGameplayManager()->GetLevelComplete() || GetGameplayManager()->GetGameComplete() )
+ {
+ // can't un-pause the game if either level or game is just completed (i.e. just
+ // finished the last mission of the level)
+ //
+ break;
+ }
+
+ GetSoundManager()->OnPauseEnd();
+
+ // if paused in sunday drive mode, unload memory card info
+ //
+ if( GetGameplayManager()->IsSundayDrive() )
+ {
+ if( GetMemoryCardManager()->IsMemcardInfoLoaded() )
+ {
+ // unload it now
+ //
+ GetMemoryCardManager()->UnloadMemcardInfo();
+ }
+ else
+ {
+ // wait until loading is complete, then unload it
+ //
+ m_unloadMemcardInfoWhenLoaded = true;
+ }
+ }
+
+ m_exitingPauseMenu = true;
+
+ // follow-thru ...
+ //
+ }
+ case GUI_MSG_RESUME_INGAME:
+ {
+ if( GetInteriorManager()->IsEntering() || GetInteriorManager()->IsExiting() )
+ {
+ // ignore this message, cuz if we're either entering or exiting an interior,
+ // we're waiting for either the EVENT_ENTER_INTERIOR_TRANSITION_END or
+ // EVENT_EXIT_INTERIOR_END event from the InteriorManager to resume the game
+ // screen
+ //
+ break;
+ }
+
+ this->ResumeGame( param1, param2 );
+
+ break;
+ }
+ case GUI_MSG_ON_SAVE_GAME_COMPLETE:
+ {
+ m_promptSaveBeforeQuit = false;
+
+ if( m_quitAfterSave )
+ {
+ this->HandleMessage( GUI_MSG_QUIT_INGAME );
+ }
+#ifdef RAD_WIN32
+ else if( m_quitToSystemAfterSave )
+ {
+ this->HandleMessage( GUI_MSG_QUIT_TO_SYSTEM );
+ }
+#endif
+ else
+ {
+ this->HandleMessage( GUI_MSG_BACK_SCREEN );
+ }
+
+ break;
+ }
+ case GUI_MSG_BACK_SCREEN:
+ {
+ if( this->GetPreviousScreen( param1 ) == CGuiWindow::GUI_SCREEN_ID_GENERIC_PROMPT )
+ {
+ m_quitAfterSave = false;
+
+ CGuiMenuPrompt::ePromptResponse responses[] =
+ {
+ CGuiMenuPrompt::RESPONSE_YES,
+ CGuiMenuPrompt::RESPONSE_NO
+ };
+
+ CGuiScreenPrompt::Display( PROMPT_CONFIRM_SAVE_BEFORE_QUIT, this, 2, responses );
+ CGuiScreenPrompt::EnableDefaultToNo( false );
+ }
+
+ break;
+ }
+ case GUI_MSG_WINDOW_FINISHED:
+ {
+ if( GUI_FE_CHANGING_SCREENS == m_state )
+ {
+ m_currentScreen = m_nextScreen;
+
+ if( m_exitingPauseMenu )
+ {
+ this->GotoHUDScreen();
+ }
+ else if( m_enteringPauseMenu )
+ {
+ this->GotoPauseScreen();
+ }
+ else
+ {
+ if( this->IsHudScreen( m_nextScreen ) )
+ {
+ this->GotoHUDScreen();
+ }
+ else
+ {
+ CGuiScreen* nextScreen = static_cast< CGuiScreen* >( this->FindWindowByID( m_nextScreen ) );
+ rAssert( nextScreen != NULL );
+ m_pScroobyProject->GotoScreen( nextScreen->GetScroobyScreen(), this );
+ }
+ }
+ }
+ else if( GUI_FE_SHUTTING_DOWN == m_state )
+ {
+ m_state = GUI_FE_TERMINATED;
+
+ // set backend scrooby project as active project
+ //
+ GetGuiSystem()->SwitchToBackendProject();
+
+ // pre-run backend loading screen
+ //
+ CGuiManagerBackEnd* backendManager = GetGuiSystem()->GetBackendManager();
+ rAssert( backendManager != NULL );
+ backendManager->HandleMessage( GUI_MSG_PRE_RUN_BACKEND,
+ m_quitAndReload ? IS_LOADING_GAMEPLAY : 0 );
+
+ // enable screen clearing
+ //
+ GetRenderManager()->mpLayer(RenderEnums::GUI)->pView( 0 )->SetClearMask( PDDI_BUFFER_ALL );
+ }
+
+ break;
+ }
+ case GUI_MSG_INGAME_MISSION_COMPLETE:
+ {
+ // param1 = 1 --> New Best Time!
+ //
+ if( s_currentHUD != NULL )
+ {
+ s_currentHUD->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY,
+ HUD_MISSION_COMPLETE, param1 );
+ }
+
+ break;
+ }
+ case GUI_MSG_INGAME_MISSION_FAILED:
+ {
+ CGuiManager::HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_MISSION_OVER,
+ CLEAR_WINDOW_HISTORY );
+
+ // switch to pause context
+ GetGameFlow()->SetContext( CONTEXT_PAUSE );
+ break;
+ }
+ case GUI_MSG_INGAME_MISSION_LOAD_BEGIN:
+ {
+ //
+ // if we're on the iris screen, then we don't want to open the iris
+ //
+ CGuiWindow::eGuiWindowID current = CGuiManager::GetCurrentScreen();
+ if( current == CGuiWindow::GUI_SCREEN_ID_IRIS_WIPE )
+ {
+ CGuiScreenIrisWipe::DoNotOpenOnNextOutro();
+ }
+ CGuiManager::HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_MISSION_LOAD,
+ CLEAR_WINDOW_HISTORY );
+
+ // switch to pause context
+ GetGameFlow()->SetContext( CONTEXT_PAUSE );
+
+ break;
+ }
+ case GUI_MSG_INGAME_MISSION_LOAD_END:
+ {
+ // notify MissionLoad screen that mission load has completed
+ //
+ if( !GetGameplayManager()->GetCurrentMission()->IsSundayDrive() )
+ {
+ GetEventManager()->TriggerEvent( EVENT_GUI_MISSION_LOAD_COMPLETE );
+ }
+
+ break;
+ }
+ case GUI_MSG_SHOW_HUD_OVERLAY:
+ case GUI_MSG_HIDE_HUD_OVERLAY:
+ {
+ // send HUD overlay messages to current HUD
+ //
+ if( s_currentHUD != NULL )
+ {
+ s_currentHUD->HandleMessage( message, param1, param2 );
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_CONNECT:
+ {
+#ifndef RAD_GAMECUBE
+ if( m_oldControllerState == Input::ACTIVE_ANIM_CAM )
+ {
+ // deactivate anim cam state first, since the input manager
+ // won't let us set the game state to anything else prior
+ // to that
+ //
+ GetInputManager()->SetGameState( Input::DEACTIVE_ANIM_CAM );
+ }
+ GetInputManager()->SetGameState( Input::ACTIVE_FRONTEND );
+#endif // !RAD_GAMECUBE
+
+ break;
+ }
+ case GUI_MSG_START_IRIS_WIPE_OPEN:
+ {
+ this->HandleMessage( GUI_MSG_RESUME_INGAME );
+
+ break;
+ }
+ case GUI_MSG_START_IRIS_WIPE_CLOSE:
+ {
+ this->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_IRIS_WIPE,
+ CLEAR_WINDOW_HISTORY );
+
+ break;
+ }
+ case GUI_MSG_INGAME_DISPLAY_PROMPT:
+ {
+ int ingameMessageIndex = NUM_PROMPT_QUESTIONS + static_cast<int>( param1 );
+ this->DisplayPrompt( ingameMessageIndex, s_currentHUD, PROMPT_TYPE_CONTINUE );
+
+ rAssert( GetGameFlow()->GetCurrentContext() == CONTEXT_GAMEPLAY );
+ GetGameFlow()->SetContext( CONTEXT_PAUSE );
+
+ break;
+ }
+ default:
+ {
+ if( message == GUI_MSG_UPDATE && m_isControllerReconnected )
+ {
+ m_isControllerReconnected = false;
+ m_controllerPromptShown = false;
+
+ }
+
+ if (m_controllerPromptShown) // don't pass event if controller error
+ {
+ if (message==GUI_MSG_CONTROLLER_START) // start trigger reconnection
+ {
+ this->OnControllerConnected( static_cast<int>( param1 ) );
+ }
+
+ break;
+ }
+
+ if( m_state != GUI_FE_UNINITIALIZED &&
+ m_currentScreen != CGuiWindow::GUI_WINDOW_ID_UNDEFINED )
+ {
+ // Send the messages down to the current screen.
+ //
+ CGuiWindow* pScreen = this->FindWindowByID( m_currentScreen );
+ rAssert( pScreen );
+
+ pScreen->HandleMessage( message, param1, param2 );
+ }
+
+#ifndef RAD_GAMECUBE
+ // poll controller connection status
+ //
+ if( message == GUI_MSG_UPDATE )
+ {
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( 0 );
+ if( !GetInputManager()->GetController( controllerID )->IsConnected() )
+ {
+ bool setState = !m_controllerPromptShown;
+ this->OnControllerDisconnected( controllerID );
+
+ if ( setState )
+ {
+ m_oldControllerState = InputManager::GetInstance()->GetGameState();
+ }
+ }
+ }
+#endif // !RAD_GAMECUBE
+
+ break;
+ }
+ }
+
+ // propogate message up the hierarchy
+ CGuiManager::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiManagerInGame::HandleEvent
+//===========================================================================
+// Description: This function translates events from to GUI messages
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void
+CGuiManagerInGame::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_CONVERSATION_INIT:
+ {
+ this->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_LETTER_BOX,
+ CLEAR_WINDOW_HISTORY );
+
+ GetInputManager()->SetGameState(Input::ACTIVE_FRONTEND);
+
+ break;
+ }
+ case EVENT_CONVERSATION_DONE_AND_FINISHED:
+ {
+ //
+ // Was this mission a patty and selma conversation
+ //
+ GameplayManager* gpm = GetGameplayManager();
+ Mission* mission = gpm->GetCurrentMission();
+ bool pattyAndSelma = mission->GetCurrentStage()->GetObjective()->IsPattyAndSelmaDialog();
+ if( pattyAndSelma )
+ {
+
+ GetInputManager()->SetGameState(Input::ACTIVE_GAMEPLAY);
+ this->HandleMessage( GUI_MSG_RESUME_INGAME );
+ GetEventManager()->TriggerEvent( EVENT_GUI_TRIGGER_PATTY_AND_SELMA_SCREEN );
+
+ /*
+ GetGuiSystem()->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_MISSION_SUCCESS,
+ CLEAR_WINDOW_HISTORY );
+ GetEventManager()->TriggerEvent( EVENT_GUI_ENTERING_MISSION_SUCCESS_SCREEN );
+ GetGameFlow()->SetContext( CONTEXT_PAUSE );
+ */
+ }
+ else
+ {
+ GetInputManager()->SetGameState(Input::ACTIVE_GAMEPLAY);
+ this->HandleMessage( GUI_MSG_RESUME_INGAME );
+ }
+
+ break;
+ }
+ case EVENT_ENTER_INTERIOR_TRANSITION_START:
+ case EVENT_EXIT_INTERIOR_START:
+ {
+/*
+ if( m_currentScreen == CGuiWindow::GUI_SCREEN_ID_IRIS_WIPE )
+ {
+ // ignore, if we're already on the iris wipe screen
+ //
+ break;
+ }
+*/
+ if( id == EVENT_ENTER_INTERIOR_TRANSITION_START )
+ {
+ rReleasePrintf( "CGuiManagerInGame <= EVENT_ENTER_INTERIOR_TRANSITION_START.\n" );
+ }
+ else
+ {
+ rReleasePrintf( "CGuiManagerInGame <= EVENT_EXIT_INTERIOR_START.\n" );
+ }
+
+ this->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_IRIS_WIPE,
+ CLEAR_WINDOW_HISTORY );
+
+ break;
+ }
+ case EVENT_ENTER_INTERIOR_TRANSITION_END:
+ case EVENT_EXIT_INTERIOR_END:
+ {
+ if( id == EVENT_ENTER_INTERIOR_TRANSITION_END )
+ {
+ rReleasePrintf( "CGuiManagerInGame <= EVENT_ENTER_INTERIOR_TRANSITION_END.\n" );
+ }
+ else
+ {
+ rReleasePrintf( "CGuiManagerInGame <= EVENT_EXIT_INTERIOR_END.\n" );
+ }
+
+ this->ResumeGame();
+
+ break;
+ }
+ case (EventEnum)(EVENT_LOCATOR + LocatorEvent::DEATH):
+ {
+ EventLocator* evtLoc = static_cast<EventLocator*>(pEventData);
+ if ( evtLoc->GetPlayerID() < static_cast<unsigned int>(MAX_PLAYERS) )
+ {
+ //chuck: check if the current hud is active, if it's not then it may not generate the
+ //EVENT_DEATH_VOLUME_SCREEN_BLANK, so we should artifically trigger it
+ if( s_currentHUD != NULL && s_currentHUD->IsActive() )
+ {
+ //This is a player who triggered this.
+ s_currentHUD->HandleMessage( GUI_MSG_DEATH_VOLUME_START, reinterpret_cast< unsigned int >( pEventData ) );
+ }
+ else
+ {
+ // current HUD is not active, so letz just fire this event back to whoever is
+ // waiting for it and ditch the presentation
+ //
+ GetEventManager()->TriggerEvent(EVENT_DEATH_VOLUME_SCREEN_BLANK,pEventData);
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ rWarningMsg( false, "Why are we receiving messages we care nothing about?" );
+
+ break;
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiManagerInGame::OnProjectLoadComplete
+//===========================================================================
+void
+CGuiManagerInGame::OnProjectLoadComplete( Scrooby::Project* pProject )
+{
+ m_pScroobyProject = pProject;
+ GetGuiSystem()->SetCurrentScroobyProject( m_pScroobyProject );
+
+ rReleasePrintf( "Scrooby dynamic loading completed. (%d msec)\n", m_elapsedDynaLoadTime );
+}
+
+void
+CGuiManagerInGame::OnMemcardInfoLoadComplete()
+{
+ if( m_unloadMemcardInfoWhenLoaded )
+ {
+ GetMemoryCardManager()->UnloadMemcardInfo();
+ }
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+void
+CGuiManagerInGame::UpdateDuringMissionLoading( unsigned int elapsedTime )
+{
+ // update backend loading screen
+ //
+ CGuiManagerBackEnd* backendManager = GetGuiSystem()->GetBackendManager();
+ rAssert( backendManager != NULL );
+ backendManager->HandleMessage( GUI_MSG_UPDATE, elapsedTime );
+}
+
+void
+CGuiManagerInGame::UpdateWhileLoadingNotDone( unsigned int elapsedTime )
+{
+ // update backend loading screen
+ //
+ CGuiManagerBackEnd* backendManager = GetGuiSystem()->GetBackendManager();
+ rAssert( backendManager != NULL );
+ backendManager->HandleMessage( GUI_MSG_UPDATE, elapsedTime );
+
+ // waiting for loading manger queue to empty before quitting
+ //
+ if( GetLoadingManager()->IsLoading() )
+ {
+ rTunePrintf( ">> Waiting for loading manager to finish loading ... ...\n" );
+ }
+ else
+ {
+ // ok, let's quit outa here!
+ //
+ this->QuitGame();
+ }
+}
+
+void
+CGuiManagerInGame::GotoPauseScreen()
+{
+ m_enteringPauseMenu = false;
+ GetGameplayContext()->PauseAllButPresentation( false );
+
+ rAssert( m_nextScreen == CGuiWindow::GUI_SCREEN_ID_PAUSE_SUNDAY ||
+ m_nextScreen == CGuiWindow::GUI_SCREEN_ID_PAUSE_MISSION );
+
+ CGuiScreen* pauseScreen = (CGuiScreen*)CGuiManager::FindWindowByID( m_nextScreen );
+ rAssert( pauseScreen );
+ pauseScreen->SetZoomingEnabled( true );
+
+ m_pScroobyProject->GotoScreen( pauseScreen->GetScroobyScreen(), this );
+}
+
+void
+CGuiManagerInGame::GotoHUDScreen()
+{
+ m_exitingPauseMenu = false;
+
+ GetEventManager()->TriggerEvent( EVENT_GUI_LEAVING_PAUSE_MENU );
+
+ CGuiScreen* hudScreen = static_cast< CGuiScreen* >( this->FindWindowByID( m_nextScreen ) );
+ rAssert( hudScreen != NULL );
+ m_pScroobyProject->GotoScreen( hudScreen->GetScroobyScreen(), this );
+
+ // handle any commands specified for upon entering HUD
+ //
+ switch( m_onHudEnterCommand )
+ {
+ case ON_HUD_ENTER_RESTART_MISSION:
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_START_IRIS_WIPE_CLOSE, 0x00 );
+ m_RestartMissionTransition.Activate();
+
+ // stop any dialog that may still be in progress
+ //
+ GetEventManager()->TriggerEvent( EVENT_DIALOG_SHUTUP );
+
+ break;
+ }
+ case ON_HUD_ENTER_ABORT_MISSION:
+ {
+ m_AbortMissionTransition.Activate();
+
+ // stop any dialog that may still be in progress
+ //
+ GetEventManager()->TriggerEvent( EVENT_DIALOG_SHUTUP );
+
+ break;
+ }
+ case ON_HUD_ENTER_SKIP_MISSION:
+ {
+ GetGameFlow()->SetContext( CONTEXT_GAMEPLAY );
+ GetGameplayManager()->AbortCurrentMission();
+ GetGameplayManager()->NextMission();
+
+ RenderEnums::LevelEnum currentLevel = GetCharacterSheetManager()->QueryCurrentMission().mLevel;
+ RenderEnums::MissionEnum currentMission = GetCharacterSheetManager()->QueryCurrentMission().mMissionNumber;
+ GetCharacterSheetManager()->SetMissionSkipped( currentLevel, currentMission );
+
+ break;
+ }
+ default:
+ {
+ // don't switch to gameplay context if returning to tutorial screen
+ //
+ if(m_nextScreen == CGuiWindow::GUI_SCREEN_ID_HUD)
+ {
+ GetGameFlow()->SetContext( CONTEXT_GAMEPLAY );
+ }
+
+ break;
+ }
+ }
+
+ m_onHudEnterCommand = ON_HUD_ENTER_NO_COMMAND;
+}
+
+void
+CGuiManagerInGame::ResumeGame( unsigned int param1, unsigned int param2 )
+{
+ if( m_resumeGameScreenID != CGuiWindow::GUI_WINDOW_ID_UNDEFINED )
+ {
+ CGuiManager::HandleMessage( GUI_MSG_GOTO_SCREEN,
+ m_resumeGameScreenID,
+ CLEAR_WINDOW_HISTORY );
+ }
+ else
+ {
+ CGuiManager::HandleMessage( GUI_MSG_GOTO_SCREEN,
+ CGuiWindow::GUI_SCREEN_ID_HUD,
+ CLEAR_WINDOW_HISTORY );
+ }
+
+ m_resumeGameScreenID = CGuiWindow::GUI_WINDOW_ID_UNDEFINED;
+
+ m_onHudEnterCommand = param1;
+ m_nextMissionIndex = param2;
+}
+
+void
+CGuiManagerInGame::QuitGame()
+{
+ if( m_quitAndReload )
+ {
+ rAssert( m_nextLevelIndex != -1 && m_nextMissionIndex != -1 );
+
+ // switch to loading context
+ //
+ if( m_nextLevelIndex != GetGameplayManager()->GetCurrentLevelIndex() )
+ {
+ if( GetGameplayManager()->GetGameComplete() ) // game finished
+ {
+ GetGameFlow()->SetContext( CONTEXT_FRONTEND );
+ }
+ else
+ {
+ GetGameFlow()->SetContext( CONTEXT_LOADING_GAMEPLAY );
+ }
+ }
+ else
+ {
+ this->OnNewMissionLoadBegin();
+ }
+
+ m_quitAndReload = false;
+ }
+ else
+ {
+ // switch to frontend context
+ GetGameFlow()->SetContext( CONTEXT_FRONTEND );
+ }
+}
+
+void
+CGuiManagerInGame::OnControllerDisconnected( int controllerID )
+{
+/*
+ if( s_currentHUD != NULL )
+ {
+ s_currentHUD->SetFadingEnabled( false );
+ }
+*/
+
+ m_controllerPromptShown = true;
+ char str_buffer[256];
+ CGuiScreenMessage::GetControllerDisconnectedMessage(controllerID, str_buffer, 255);
+ GetGame()->GetPlatform()->OnControllerError(str_buffer);
+
+}
+
+void
+CGuiManagerInGame::OnControllerConnected( int controllerID )
+{
+ if( m_controllerPromptShown
+ && GetInputManager()->GetControllerIDforPlayer( 0 ) == controllerID )
+ {
+
+#ifdef RAD_XBOX
+// go to pause screen after disconnection
+ rAssert(s_currentHUD);
+ if( s_currentHUD && s_currentHUD->IsActive() )
+ this->HandleMessage( GUI_MSG_PAUSE_INGAME );
+#endif
+ GetGame()->GetPlatform()->ClearControllerError();
+
+ InputManager::GetInstance()->SetGameState(m_oldControllerState);
+
+ m_isControllerReconnected = true;
+
+ }
+}
+
+//===========================================================================
+// CGuiManagerInGame::OnNewMissionLoadBegin
+//===========================================================================
+// Description: Starting point for loading a new mission (within the same
+// level) from the Mission Select screen.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void
+CGuiManagerInGame::OnNewMissionLoadBegin()
+{
+ GetGameplayManager()->RestartToMission( static_cast<RenderEnums::MissionEnum>( m_nextMissionIndex ) );
+
+ GetPauseContext()->SetWaitingForContextSwitch( true );
+
+ m_resumeGameScreenID = CGuiWindow::GUI_WINDOW_ID_UNDEFINED;
+
+ // run backend loading screen
+ //
+ CGuiManagerBackEnd* backendManager = GetGuiSystem()->GetBackendManager();
+ rAssert( backendManager != NULL );
+ backendManager->HandleMessage( GUI_MSG_RUN_BACKEND, IS_LOADING_GAMEPLAY );
+
+ m_state = GUI_FE_UNINITIALIZED;
+
+ m_isLoadingNewMission = true;
+}
+
+//===========================================================================
+// CGuiManagerInGame::OnNewMissionLoadEnd
+//===========================================================================
+// Description: Finishing point for loading a new mission (within the same
+// level) from the Mission Select screen.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void
+CGuiManagerInGame::OnNewMissionLoadEnd()
+{
+ this->Start();
+ this->ClearScreenHistory();
+
+ // quit backend loading screen
+ //
+ CGuiManagerBackEnd* backendManager = GetGuiSystem()->GetBackendManager();
+ rAssert( backendManager != NULL );
+ backendManager->HandleMessage( GUI_MSG_QUIT_BACKEND );
+
+ GetGuiSystem()->SwitchToCurrentProject();
+
+ m_isLoadingNewMission = false;
+}
+
+//===========================================================================
+// CGuiManagerInGame::IsPausingAllowed
+//===========================================================================
+// Description: returns FALSE if in-game pausing is not allowed due to
+// at least one condition; otherwise, returns TRUE
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+bool
+CGuiManagerInGame::IsPausingAllowed() const
+{
+ if( m_state == GUI_FE_CHANGING_SCREENS )
+ {
+ // don't allow pausing if we're currently in the middle of changing screens
+ //
+ return false;
+ }
+
+ if( GameplayContext::GetInstance()->IsPaused() || GetPresentationManager()->IsBusy() )
+ {
+ return false;
+ }
+
+ if( GetGameplayManager()->GetCurrentMessage() != GameplayManager::NONE )
+ {
+ // don't allow pausing if the gameplay manager is in the middle of changing missions
+ //
+ rAssert( GetGameplayManager()->GetCurrentMessage() == GameplayManager::PREV_MISSION ||
+ GetGameplayManager()->GetCurrentMessage() == GameplayManager::NEXT_MISSION );
+
+ return false;
+ }
+
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ if( currentMission->IsChangingStages() )
+ {
+ // don't allow pausing if the current mission is in the middle of changing stages
+ //
+ return false;
+ }
+
+ if( GetGameplayManager()->GetLevelComplete() || GetGameplayManager()->GetGameComplete() )
+ {
+ // don't allow pausing if level/game has just been completed
+ //
+ return false;
+ }
+
+ return true;
+}
+
+
+//===========================================================================
+// AbortCurrentMission::Activate
+//===========================================================================
+// Description: cancels a mission
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void GuiSFX::AbortCurrentMission::Activate()
+{
+ GetGameFlow()->SetContext( CONTEXT_GAMEPLAY );
+ GetGameplayManager()->AbortCurrentMission();
+ ContinueChain();
+};
+
+//===========================================================================
+// RestartCurrentMission::OpenIris
+//===========================================================================
+// Description: reactivates a paused mission
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void GuiSFX::OpenIris::Activate()
+{
+ bool irisClosed = CGuiScreenIrisWipe::IsIrisClosed();
+ if( irisClosed )
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_START_IRIS_WIPE_OPEN, 0x00 );
+ }
+ ContinueChain();
+};
+
+//===========================================================================
+// RestartCurrentMission::Activate
+//===========================================================================
+// Description: reactivates a paused mission
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void GuiSFX::RestartCurrentMission::Activate()
+{
+ GetGameplayManager()->RestartCurrentMission();
+ ContinueChain();
+};
diff --git a/game/code/presentation/gui/ingame/guimanageringame.h b/game/code/presentation/gui/ingame/guimanageringame.h
new file mode 100644
index 0000000..7356585
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guimanageringame.h
@@ -0,0 +1,231 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManagerInGame
+//
+// Description: Interface for the CGuiManagerInGame class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/09/20 DChau Created
+// 2002/05/29 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUIMANAGERINGAME_H
+#define GUIMANAGERINGAME_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/utility/transitions.h>
+#include <data/memcard/memorycardmanager.h>
+#include <input/inputmanager.h>
+
+#include <app.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CGuiScreenHud;
+namespace GuiSFX
+{
+ class RecieveEvent;
+
+ class AbortCurrentMission : public GuiSFX::Chainable1
+ {
+ public:
+ virtual void Activate();
+ }; //class AbortCurrentMission;
+
+ class OpenIris : public GuiSFX::Chainable1
+ {
+ public:
+ virtual void Activate();
+ }; //class RestartCurrentMission;
+
+ class RestartCurrentMission : public GuiSFX::Chainable1
+ {
+ public:
+ virtual void Activate();
+ }; //class RestartCurrentMission;
+}; //namespace GuiSFX
+
+namespace Scrooby
+{
+ class Layer;
+}
+
+enum eOnHudEnterCommand
+{
+ ON_HUD_ENTER_NO_COMMAND = 0,
+
+ ON_HUD_ENTER_RESTART_MISSION,
+ ON_HUD_ENTER_ABORT_MISSION,
+ ON_HUD_ENTER_SKIP_MISSION,
+
+ NUM_ON_HUD_ENTER_COMMANDS
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class CGuiManagerInGame : public CGuiManager,
+ public Scrooby::LoadProjectCallback,
+ public IMemoryCardInfoLoadCallback
+{
+public:
+ CGuiManagerInGame( Scrooby::Project* pProject, CGuiEntity* pParent );
+ virtual ~CGuiManagerInGame();
+
+ virtual void Populate();
+ virtual void Start( CGuiWindow::eGuiWindowID initialWindow = CGuiWindow::GUI_WINDOW_ID_UNDEFINED );
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ int GetNextLevelToLoad() const { return m_nextLevelIndex; }
+ int GetNextMissionToLoad() const { return m_nextMissionIndex; }
+
+ bool IsEnteringPauseMenu() const { return m_enteringPauseMenu; }
+ bool isExitingPauseMenu() const { return m_exitingPauseMenu; }
+
+ bool IsLoadingNewMissionInSundayDrive() const; // newspaper loading screen displayed
+ bool IsLoadingNewMission() const; // mission briefing screen displayed
+
+ CGuiWindow::eGuiWindowID GetResumeGameScreenID() const { return m_resumeGameScreenID; }
+
+ //
+ // Implements Scrooby::LoadProjectCallback interface.
+ //
+ virtual void OnProjectLoadComplete( Scrooby::Project* pProject );
+
+ // Implements IMemoryCardInfoLoadCallback interface.
+ //
+ virtual void OnMemcardInfoLoadComplete();
+
+ // Pointer to current HUD.
+ //
+ static CGuiScreenHud* s_currentHUD;
+
+private:
+
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CGuiManagerInGame( const CGuiManagerInGame& );
+ CGuiManagerInGame& operator= ( const CGuiManagerInGame& );
+
+ bool IsHudScreen( CGuiWindow::eGuiWindowID windowID ) const;
+ bool IsRewardsScreen( CGuiWindow::eGuiWindowID windowID ) const;
+
+ enum eInGameProject
+ {
+ PROJECT_INGAME,
+ PROJECT_PAUSE,
+ PROJECT_REWARDS,
+
+ NUM_INGAME_PROJECTS
+ };
+
+ void PopulateInGame( eInGameProject project = PROJECT_INGAME );
+
+ void UpdateDuringDynamicLoading( unsigned int elapsedTime );
+ void UpdateDuringMissionLoading( unsigned int elapsedTime );
+ void UpdateWhileLoadingNotDone( unsigned int elapsedTime );
+
+ void GotoPauseScreen();
+ void GotoHUDScreen();
+
+ void ResumeGame( unsigned int param1 = 0, unsigned int param2 = 0 );
+ void QuitGame();
+
+ void OnControllerDisconnected( int controllerID );
+ void OnControllerConnected( int controllerID );
+
+ void OnNewMissionLoadBegin();
+ void OnNewMissionLoadEnd();
+
+ bool IsPausingAllowed() const;
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ int m_nextLevelIndex;
+ int m_nextMissionIndex;
+
+ bool m_isLoadingNewMission : 1;
+ bool m_quitAndReload : 1;
+ bool m_controllerPromptShown : 1;
+ bool m_enteringPauseMenu : 1;
+ bool m_exitingPauseMenu : 1;
+
+ unsigned int m_onHudEnterCommand;
+
+ Scrooby::Screen* m_levelScreen;
+ Scrooby::Layer* m_levelLayer;
+ unsigned int m_elapsedDynaLoadTime;
+
+ Scrooby::Project* m_pRewardsProject;
+
+ bool m_unloadMemcardInfoWhenLoaded : 1;
+ bool m_promptSaveBeforeQuit : 1;
+ bool m_quitAfterSave : 1;
+#ifdef RAD_WIN32
+ bool m_quitToSystemAfterSave : 1;
+#endif
+ bool m_isControllerReconnected : 1;
+
+ GuiSFX::Dummy m_RestartMissionTransition;
+ GuiSFX::RecieveEvent m_RecieveIrisClosed;
+ GuiSFX::RestartCurrentMission m_RestartMission;
+
+ GuiSFX::Dummy m_AbortMissionTransition;
+ GuiSFX::AbortCurrentMission m_AbortMission;
+
+ Input::ActiveState m_oldControllerState;
+
+ CGuiWindow::eGuiWindowID m_resumeGameScreenID;
+
+};
+
+inline CGuiScreenHud* GetCurrentHud()
+{
+ // return reference to current in-game HUD
+ //
+ return CGuiManagerInGame::s_currentHUD;
+}
+
+inline bool CGuiManagerInGame::IsLoadingNewMissionInSundayDrive() const
+{
+ return m_isLoadingNewMission;
+}
+
+inline bool CGuiManagerInGame::IsLoadingNewMission() const
+{
+ return ( m_currentScreen == CGuiWindow::GUI_SCREEN_ID_MISSION_LOAD );
+}
+
+inline bool CGuiManagerInGame::IsHudScreen( CGuiWindow::eGuiWindowID windowID ) const
+{
+ return( windowID == CGuiWindow::GUI_SCREEN_ID_HUD ||
+ windowID == CGuiWindow::GUI_SCREEN_ID_MULTI_HUD );
+}
+
+inline bool CGuiManagerInGame::IsRewardsScreen( CGuiWindow::eGuiWindowID windowID ) const
+{
+ return( windowID == CGuiWindow::GUI_SCREEN_ID_PURCHASE_REWARDS );
+}
+
+#endif // GUIMANAGERINGAME_H
diff --git a/game/code/presentation/gui/ingame/guiscreencreditspostfmv.cpp b/game/code/presentation/gui/ingame/guiscreencreditspostfmv.cpp
new file mode 100644
index 0000000..1d8e0e8
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreencreditspostfmv.cpp
@@ -0,0 +1,267 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenCreditsPostFMV
+//
+// Description: Implementation of the CGuiScreenCreditsPostFMV class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/06/02 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreencreditspostfmv.h>
+#include <presentation/gui/guimanager.h>
+
+#include <events/eventmanager.h>
+#include <sound/soundmanager.h>
+
+// Scrooby
+#include <screen.h>
+#include <page.h>
+#include <group.h>
+#include <sprite.h>
+
+// ATG
+#include <p3d/utility.hpp>
+#include <raddebug.hpp>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const float KANG_KODOS_IMAGE_CORRECTION_SCALE = 1.5f;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenCreditsPostFMV::CGuiScreenCreditsPostFMV
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenCreditsPostFMV::CGuiScreenCreditsPostFMV
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreenViewCredits( pScreen, pParent ),
+ m_kang( NULL ),
+ m_kodos( NULL )
+{
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "CreditsKK" );
+ rAssert( pPage != NULL );
+
+ m_kang = pPage->GetSprite( "Kang" );
+ rAssert( m_kang != NULL );
+ m_kang->SetVisible( false ); // hide by default
+
+ m_kodos = pPage->GetSprite( "Kodos" );
+ rAssert( m_kodos != NULL );
+ m_kodos->SetVisible( false ); // hide by default
+}
+
+
+//===========================================================================
+// CGuiScreenCreditsPostFMV::~CGuiScreenCreditsPostFMV
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenCreditsPostFMV::~CGuiScreenCreditsPostFMV()
+{
+ if( m_kang != NULL )
+ {
+ m_kang->SetRawSprite( NULL );
+ }
+
+ if( m_kodos != NULL )
+ {
+ m_kodos->SetRawSprite( NULL );
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenCreditsPostFMV::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenCreditsPostFMV::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ this->OnScrollingDone();
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ // ignore back controller inputs
+ //
+ return;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreenViewCredits::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenCreditsPostFMV::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenCreditsPostFMV::InitIntro()
+{
+ GetEventManager()->TriggerEvent( EVENT_PLAY_CREDITS );
+
+ m_playKKDialog = true;
+
+ this->ResetScrolling();
+
+ GetSoundManager()->DuckForInGameCredits();
+
+ rAssert( m_creditsGroup != NULL );
+ m_creditsGroup->SetVisible( false );
+
+ //
+ // TC: [TODO] remove the loading of these images from the mission script
+ // for level 7 - mission 8 - sunday drive
+ //
+/*
+ // search for kang and kodos images
+ //
+ tSprite* pSprite = NULL;
+
+ pSprite = p3d::find<tSprite>( "kang.png" );
+ if( pSprite != NULL )
+ {
+ rAssert( m_kang != NULL );
+ m_kang->SetRawSprite( pSprite );
+ m_kang->SetVisible( true );
+
+ m_kang->ResetTransformation();
+ m_kang->ScaleAboutCenter( KANG_KODOS_IMAGE_CORRECTION_SCALE );
+ }
+ else
+ {
+ rAssertMsg( false, "Can't find kang image!" );
+ }
+
+ pSprite = p3d::find<tSprite>( "kodos.png" );
+ if( pSprite != NULL )
+ {
+ rAssert( m_kodos != NULL );
+ m_kodos->SetRawSprite( pSprite );
+ m_kodos->SetVisible( true );
+
+ m_kodos->ResetTransformation();
+ m_kodos->ScaleAboutCenter( KANG_KODOS_IMAGE_CORRECTION_SCALE );
+ }
+ else
+ {
+ rAssertMsg( false, "Can't find kodos image!" );
+ }
+*/
+}
+
+
+//===========================================================================
+// CGuiScreenCreditsPostFMV::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenCreditsPostFMV::InitRunning()
+{
+ rAssert( m_creditsGroup != NULL );
+ m_creditsGroup->SetVisible( true );
+}
+
+
+//===========================================================================
+// CGuiScreenCreditsPostFMV::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenCreditsPostFMV::InitOutro()
+{
+ GetEventManager()->TriggerEvent( EVENT_DIALOG_SHUTUP );
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenCreditsPostFMV::OnScrollingDone()
+{
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ GUI_SCREEN_ID_LEVEL_END,
+ CLEAR_WINDOW_HISTORY );
+}
+
diff --git a/game/code/presentation/gui/ingame/guiscreencreditspostfmv.h b/game/code/presentation/gui/ingame/guiscreencreditspostfmv.h
new file mode 100644
index 0000000..5a2d523
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreencreditspostfmv.h
@@ -0,0 +1,54 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenCreditsPostFMV
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/06/02 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENCREDITSPOSTFMV_H
+#define GUISCREENCREDITSPOSTFMV_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreenviewcredits.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenCreditsPostFMV : public CGuiScreenViewCredits
+{
+public:
+ CGuiScreenCreditsPostFMV( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenCreditsPostFMV();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ virtual void OnScrollingDone();
+
+ Scrooby::Sprite* m_kang;
+ Scrooby::Sprite* m_kodos;
+
+};
+
+#endif // GUISCREENCREDITSPOSTFMV_H
diff --git a/game/code/presentation/gui/ingame/guiscreenhastransitions.cpp b/game/code/presentation/gui/ingame/guiscreenhastransitions.cpp
new file mode 100644
index 0000000..95eaeba
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenhastransitions.cpp
@@ -0,0 +1,213 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLetterBox
+//
+// Description: Implementation of the CGuiScreenLetterBox class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/21 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenhastransitions.h>
+#include <presentation/gui/utility/transitions.h>
+#include <drawable.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenHasTransitions::CGuiScreenHasTransitions
+//===========================================================================
+// Description: Constructor
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenHasTransitions::CGuiScreenHasTransitions():
+ mDoneAddingTransitions( false )
+{
+ // zero out the array of transitions (use a memset instead)?
+ int i;
+ for( i = 0; i < MAX_TRANSITIONS; ++i )
+ {
+ m_Transitions[ i ] = NULL;
+ }
+}
+
+//===========================================================================
+// CGuiScreenHasTransitions::AddTransition
+//===========================================================================
+// Description: Adds the transition to the array of transitions
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHasTransitions::AddTransition( GuiSFX::Transition* transition )
+{
+ int i;
+ for( i = 0; i < MAX_TRANSITIONS; ++i )
+ {
+ if( m_Transitions[ i ] == NULL )
+ {
+ m_Transitions[ i ] = transition;
+ return;
+ }
+ }
+ rAssertMsg( false, "Need to bump up the size of the array of transitions" );
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::AddTransition
+//===========================================================================
+// Description: Adds the transition to the array of transitions
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHasTransitions::AddTransition( GuiSFX::Transition& transition )
+{
+ int i;
+ for( i = 0; i < MAX_TRANSITIONS; ++i )
+ {
+ if( m_Transitions[ i ] == NULL )
+ {
+ m_Transitions[ i ] = &transition;
+ return;
+ }
+ }
+ rAssertMsg( false, "Need to bump up the size of the array of transitions" );
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::DoneAddingTransitions
+//===========================================================================
+// Description: tells the class that we're done adding transitions to it. Used
+// for debugging the size of the array
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHasTransitions::DoneAddingTransitions()
+{
+ mDoneAddingTransitions = true;
+}
+
+//===========================================================================
+// CGuiScreenHasTransitions::ResetMovableObjects
+//===========================================================================
+// Description: resets transformations for all movable objects
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHasTransitions::ResetMovableObjects()
+{
+ int i;
+ for( i = 0; i < MAX_TRANSITIONS; ++i )
+ {
+ GuiSFX::Transition* t = m_Transitions[ i ];
+ if( t != NULL )
+ {
+ bool active = t->IsActive();
+ if( active )
+ {
+ bool movable = t->MovesDrawable();
+ if( movable )
+ {
+ Scrooby::Drawable* drawable = t->GetDrawable();
+ if( drawable != NULL )
+ {
+ drawable->ResetTransformation();
+ }
+ }
+ }
+ }
+ }
+}
+
+//===========================================================================
+// CGuiScreenHasTransitions::ResetTransitions
+//===========================================================================
+// Description: resets all of the transitions on the screen
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHasTransitions::ResetTransitions()
+{
+ rAssert( mDoneAddingTransitions );
+ int i;
+ for( i = 0; i < MAX_TRANSITIONS; ++i )
+ {
+ if( m_Transitions[ i ] == NULL )
+ {
+ return;
+ }
+ m_Transitions[ i ]->Reset();
+ }
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::UpdateTransitions
+//===========================================================================
+// Description: updates all the transitions
+//
+// Constraints: None.
+//
+// Parameters: deltaT - how much time has elapsed
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHasTransitions::UpdateTransitions( const float deltaT )
+{
+ rAssert( mDoneAddingTransitions );
+ int i;
+ for( i = 0; i < MAX_TRANSITIONS; ++i )
+ {
+ if( m_Transitions[ i ] == NULL )
+ {
+ return;
+ }
+ if( m_Transitions[ i ]->IsActive() )
+ {
+ m_Transitions[ i ]->Update( deltaT );
+ }
+ }
+} \ No newline at end of file
diff --git a/game/code/presentation/gui/ingame/guiscreenhastransitions.h b/game/code/presentation/gui/ingame/guiscreenhastransitions.h
new file mode 100644
index 0000000..d175896
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenhastransitions.h
@@ -0,0 +1,54 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenHasTransitions
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/19 Ian Gipson Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENHASTRANSITIONS_H
+#define GUISCREENHASTRANSITIONS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+#define MAX_TRANSITIONS 64
+
+namespace GuiSFX
+{
+ class Transition;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class CGuiScreenHasTransitions
+{
+public:
+ CGuiScreenHasTransitions();
+protected:
+ void AddTransition( GuiSFX::Transition* transition );
+ void AddTransition( GuiSFX::Transition& transition );
+ void DoneAddingTransitions();
+ void ResetMovableObjects();
+ void ResetTransitions();
+ void UpdateTransitions( const float deltaT );
+private:
+ GuiSFX::Transition* m_Transitions[ MAX_TRANSITIONS ];
+ bool mDoneAddingTransitions;
+};
+
+#endif // GUISCREENHASTRANSITIONS_H
diff --git a/game/code/presentation/gui/ingame/guiscreenhud.cpp b/game/code/presentation/gui/ingame/guiscreenhud.cpp
new file mode 100644
index 0000000..b138d75
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenhud.cpp
@@ -0,0 +1,2106 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenHud
+//
+// Description: Implementation of the CGuiScreenHud class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/11/20 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guihudtextbox.h>
+#include <presentation/gui/ingame/hudevents/hudcardcollected.h>
+#include <presentation/gui/ingame/hudevents/hudcoincollected.h>
+#include <presentation/gui/ingame/hudevents/hudmissionprogress.h>
+#include <presentation/gui/ingame/hudevents/hudmissionobjective.h>
+#include <presentation/gui/ingame/hudevents/hudcountdown.h>
+#include <presentation/gui/ingame/hudevents/hudhitnrun.h>
+#include <presentation/gui/ingame/hudevents/hudwaspdestroyed.h>
+#include <presentation/gui/ingame/hudevents/huditemdropped.h>
+#include <presentation/gui/utility/hudmap.h>
+#include <presentation/gui/utility/scrollingtext.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/guisystem.h>
+
+#ifdef SRR2_MOVABLE_HUD_MAP
+ #include <presentation/gui/guisystem.h>
+ #include <presentation/gui/guiuserinputhandler.h>
+#endif
+
+#include <cards/card.h>
+#include <contexts/context.h>
+#include <contexts/gameplay/gameplaycontext.h>
+#include <events/eventmanager.h>
+#include <gameflow/gameflow.h>
+#include <interiors/interiormanager.h>
+#include <mission/gameplaymanager.h>
+#include <mission/mission.h>
+#include <mission/missionmanager.h>
+#include <mission/conditions/getoutofcarcondition.h>
+#include <memory/srrmemory.h>
+#include <worldsim/hitnrunmanager.h>
+#include <worldsim/coins/coinmanager.h>
+
+#include <p3d/unicode.hpp>
+
+// FTech
+//
+#include <raddebug.hpp>
+
+// Scrooby
+//
+#include <app.h>
+#include <group.h>
+#include <layer.h>
+#include <page.h>
+#include <pure3dobject.h>
+#include <screen.h>
+#include <sprite.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+// radar fade in/out transition time
+//
+const unsigned int RADAR_FADE_TIME = 250; // in msec
+
+const float RADAR_SCALE = 0.9f;
+
+//const tColour HNR_METER_EMPTY_COLOUR( 0x02, 0x45, 0x39 );
+const short NUMERIC_TEXT_SPACING = -12;
+
+const float HNR_METER_BLINKING_THRESHOLD = 0.78f;
+const tColour HNR_METER_DEFAULT_COLOUR( 255, 234, 2 );
+const tColour HNR_METER_WARNING_COLOUR( 215, 16, 12 );
+
+const tColour PROXIMITY_METER_RED( 255, 0, 0 );
+const tColour PROXIMITY_METER_GREEN( 0, 255, 0 );
+
+// minimum time required for avatar to be off road before updating
+// race position to a ?/N display
+//
+const unsigned int MIN_AVATAR_OFF_ROAD_TIME = 0; // in msec
+
+const unsigned int MESSAGE_DISPLAY_TIME = 3000; // in msec
+const unsigned int MESSAGE_TRANSITION_TIME = 200; // in msec
+
+Scrooby::Sprite* CGuiScreenHud::s_numCoinsDisplay = NULL;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+MessageQueue::MessageQueue()
+: m_front( 0 ),
+ m_back( 0 ),
+ m_isEmpty( true ),
+ m_isFull( false )
+{
+}
+
+MessageQueue::~MessageQueue()
+{
+}
+
+void
+MessageQueue::Enqueue( unsigned int messageID )
+{
+ rAssertMsg( !m_isFull, "Queue is full!" );
+
+ m_messages[ m_back ] = messageID;
+ m_back = (m_back + 1) % MESSAGE_QUEUE_CAPACITY;
+
+ m_isEmpty = false;
+ m_isFull = (m_front == m_back);
+}
+
+unsigned int
+MessageQueue::Dequeue()
+{
+ // assumes queue is not empty
+ //
+ rAssertMsg( !m_isEmpty, "Queue is empty!" );
+
+ unsigned int messageID = m_messages[ m_front ];
+ m_front = (m_front + 1) % MESSAGE_QUEUE_CAPACITY;
+
+ m_isEmpty = (m_front == m_back);
+ m_isFull = false;
+
+ return messageID;
+}
+
+void
+MessageQueue::Clear()
+{
+ m_front = 0;
+ m_back = 0;
+ m_isEmpty = true;
+ m_isFull= false;
+}
+
+//===========================================================================
+// CGuiScreenHud::CGuiScreenHud
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenHud::CGuiScreenHud
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreenMultiHud( pScreen, pParent, GUI_SCREEN_ID_HUD, 1 ),
+ m_missionOverlays( NULL ),
+ m_helpMessage( NULL ),
+ m_messageBox( NULL ),
+ m_actionButton( NULL ),
+ m_missionComplete( NULL ),
+ m_timer( NULL ),
+ m_defaultTimerColour( 255, 255, 255 ),
+ m_timerBlinkingStartTime( 10000 ),
+ m_timerBlinkingEndTime( 0 ),
+ m_parTime( NULL ),
+ m_collectibles( NULL ),
+ m_collectiblesUpdated( 0 ),
+ m_position( NULL ),
+ m_positionOrdinal( NULL ),
+ m_lap( NULL ),
+ m_lapUpdated( 0 ),
+ m_isSlidingDamageMeter( false ),
+ m_isFadingInRadar( false ),
+ m_isFadingOutRadar( false ),
+ m_hnrLights( NULL ),
+ m_hnrMeter( NULL ),
+ m_hnrMeterBgd( NULL ),
+ m_hnrElapsedTime( 0 ),
+ m_isFadingIn( false ),
+ m_isFadingOut( false ),
+ m_elapsedFadeTime( 0.0f ),
+ m_isBonusMissionJustCompleted( false ),
+ m_isAvatarOffRoad( false ),
+ m_avatarOffRoadDurationTime( 0 ),
+ m_pGetOutOfCarCondition( NULL ),
+ m_elapsedFgdFadeInTime( -1.0f )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenHud" );
+ memset( m_overlays, 0, sizeof( m_overlays ) );
+ memset( m_hudEventHandlers, 0, sizeof( m_hudEventHandlers ) );
+ memset( m_hnrSectors, 0, sizeof( m_hnrSectors ) );
+
+ int i = 0;
+/*
+ bool enableFadeIn = (GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_GAMEPLAY );
+ this->SetFadingEnabled( enableFadeIn );
+*/
+ this->SetFadingEnabled( false );
+
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "Hud" );
+ rAssert( pPage != NULL );
+
+ // Get dynamic elements
+ this->RetrieveElements( pPage );
+
+ m_missionOverlays = pPage->GetGroup( "MissionOverlays" );
+ rAssert( m_missionOverlays != NULL );
+
+ Scrooby::Group* pGroup = NULL;
+
+ // Retrieve HUD overlays
+ //
+ pGroup = m_missionOverlays->GetGroup( "Timer" );
+ rAssert( pGroup != NULL );
+ m_overlays[ HUD_TIMER ] = pGroup;
+ m_overlays[ HUD_TIMER_TEMP ] = pGroup;
+ m_timer = pGroup->GetSprite( "Timer" );
+ rAssert( m_timer != NULL );
+ m_timer->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ m_timer->CreateBitmapTextBuffer( BITMAP_TEXT_BUFFER_SIZE );
+ m_timer->SetBitmapTextSpacing( NUMERIC_TEXT_SPACING );
+ m_defaultTimerColour = m_timer->GetColour();
+#ifdef RAD_WIN32
+ m_timer->Translate( -25, 0 );
+ m_timer->ScaleAboutCenter( 0.5f );
+#endif
+
+ pGroup = m_missionOverlays->GetGroup( "ParTime" );
+ rAssert( pGroup != NULL );
+ m_overlays[ HUD_PAR_TIME ] = pGroup;
+ m_parTime = pGroup->GetSprite( "ParTime" );
+ rAssert( m_parTime != NULL );
+ m_parTime->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ m_parTime->CreateBitmapTextBuffer( BITMAP_TEXT_BUFFER_SIZE );
+ m_parTime->SetBitmapTextSpacing( NUMERIC_TEXT_SPACING );
+#ifdef RAD_WIN32
+ m_parTime->Translate( -25, 0 );
+ m_parTime->ScaleAboutCenter( 0.5f );
+#endif
+
+ pGroup = m_missionOverlays->GetGroup( "Collectibles" );
+ rAssert( pGroup != NULL );
+ m_overlays[ HUD_COLLECTIBLES ] = pGroup;
+ m_collectibles = pGroup->GetSprite( "Collectibles" );
+ rAssert( m_collectibles != NULL );
+ m_collectibles->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ m_collectibles->CreateBitmapTextBuffer( BITMAP_TEXT_BUFFER_SIZE );
+ m_collectibles->SetBitmapTextSpacing( NUMERIC_TEXT_SPACING );
+#ifdef RAD_WIN32
+ m_collectibles->Translate( -25, 0 );
+ m_collectibles->ScaleAboutCenter( 0.5f );
+#endif
+
+ pGroup = m_missionOverlays->GetGroup( "Position" );
+ rAssert( pGroup != NULL );
+ m_overlays[ HUD_RACE_POSITION ] = pGroup;
+ m_positionOrdinal = pGroup->GetText( "PositionOrdinal" );
+ m_position = pGroup->GetSprite( "Position" );
+ rAssert( m_position != NULL );
+ m_position->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ m_position->CreateBitmapTextBuffer( BITMAP_TEXT_BUFFER_SIZE );
+ m_position->SetBitmapTextSpacing( NUMERIC_TEXT_SPACING );
+#ifdef RAD_WIN32
+ m_position->Translate( -25, 0 );
+ m_position->ScaleAboutCenter( 0.5f );
+#endif
+
+ pGroup = m_missionOverlays->GetGroup( "Lap" );
+ rAssert( pGroup != NULL );
+ m_overlays[ HUD_LAP_COUNTER ] = pGroup;
+ m_lap = pGroup->GetSprite( "Lap" );
+ rAssert( m_lap != NULL );
+ m_lap->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ m_lap->CreateBitmapTextBuffer( BITMAP_TEXT_BUFFER_SIZE );
+ m_lap->SetBitmapTextSpacing( NUMERIC_TEXT_SPACING );
+#ifdef RAD_WIN32
+ m_lap->Translate( -25, 0 );
+ m_lap->ScaleAboutCenter( 0.5f );
+#endif
+
+ pGroup = m_missionOverlays->GetGroup( "DamageMeter" );
+ rAssert( pGroup != NULL );
+ m_overlays[ HUD_DAMAGE_METER ] = pGroup;
+ m_damageMeter.SetScroobyImage( pGroup->GetSprite( "DamageBar" ) );
+
+ pGroup = m_missionOverlays->GetGroup( "ProximityMeter" );
+ rAssert( pGroup != NULL );
+ m_overlays[ HUD_PROXIMITY_METER ] = pGroup;
+ m_proximityMeter.SetScroobyImage( pGroup->GetSprite( "ProximityBar" ) );
+
+ pGroup = m_missionOverlays->GetGroup( "MissionComplete" );
+ rAssert( pGroup != NULL );
+ m_overlays[ HUD_MISSION_COMPLETE ] = pGroup;
+
+ // get 'mission complete' text
+ //
+ m_missionComplete = pGroup->GetSprite( "MissionComplete" );
+ rAssert( m_missionComplete != NULL );
+ m_missionComplete->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ m_missionComplete->CreateBitmapTextBuffer( 256 );
+ m_missionComplete->SetBitmapText( GetTextBibleString( "MISSION_COMPLETE" ) );
+ m_missionComplete->SetBitmapTextLineSpacing( 10 );
+
+ // get 'mission failed' text
+ //
+ m_missionFailedSprite = pGroup->GetSprite( "MissionFailed" );
+ rAssert( m_missionFailedSprite != NULL );
+ m_missionFailedSprite->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ m_missionFailedSprite->CreateBitmapTextBuffer( 256 );
+ m_missionFailedSprite->SetBitmapText( GetTextBibleString( "MISSION_FAILED" ) );
+ m_missionFailedSprite->SetBitmapTextLineSpacing( 10 );
+#ifdef RAD_WIN32
+ m_missionFailedSprite->ScaleAboutCenter( 0.5f );
+#endif
+
+ pGroup = pPage->GetGroup( "HudMap0" );
+ rAssert( pGroup != NULL );
+ m_overlays[ HUD_MAP ] = pGroup;
+
+ // scale entire HUD map (or RADAR, if you'd prefer to call it that)
+ //
+ m_overlays[ HUD_MAP ]->ResetTransformation();
+ m_overlays[ HUD_MAP ]->ScaleAboutCenter( RADAR_SCALE );
+
+ Scrooby::Group* pGroupHNR = pGroup->GetGroup( "HitNRunMeter" );
+ if( pGroupHNR != NULL )
+ {
+ for( i = 0; i < NUM_HIT_N_RUN_SECTORS; i++ )
+ {
+ char name[ 16 ];
+ sprintf( name, "HnRSector%d", i );
+ m_hnrSectors[ i ] = pGroupHNR->GetSprite( name );
+
+// rAssert( m_hnrSectors[ i ] != NULL );
+// m_hnrSectors[ i ]->SetColour( HNR_METER_EMPTY_COLOUR );
+
+ }
+
+ m_hnrLights = pGroupHNR->GetSprite( "HitNRun" );
+ m_hnrMeter = pGroupHNR->GetSprite( "HnRMeter" );
+ m_hnrMeterBgd = pGroupHNR->GetSprite( "HnRMeterBgd" );
+ }
+
+ pGroup = pPage->GetGroup( "Message" );
+ rAssert( pGroup != NULL );
+ m_overlays[ HUD_MESSAGE ] = pGroup;
+ m_helpMessage = pGroup->GetText( "Message" );
+ rAssert( m_helpMessage != NULL );
+ m_helpMessage->SetTextMode( Scrooby::TEXT_WRAP );
+ m_helpMessage->ResetTransformation();
+ m_helpMessage->Translate( 0, MESSAGE_TEXT_VERTICAL_TRANSLATION );
+ m_helpMessage->ScaleAboutPoint( MESSAGE_TEXT_SCALE * MESSGAE_TEXT_HORIZONTAL_STRETCH,
+ MESSAGE_TEXT_SCALE,
+ 1.0f, 0, 0 );
+
+ m_messageBox = pGroup->GetSprite( "MessageBox" );
+ rAssert( m_messageBox != NULL );
+ m_messageBox->ResetTransformation();
+ m_messageBox->ScaleAboutCenter( MESSAGE_BOX_CORRECTION_SCALE * MESSAGE_BOX_HORIZONTAL_STRETCH,
+ MESSAGE_BOX_CORRECTION_SCALE * MESSAGE_BOX_VERTICAL_STRETCH,
+ 1.0f );
+
+ pGroup = pPage->GetGroup( "ActionButton" );
+ rAssert( pGroup != NULL );
+ m_overlays[ HUD_ACTION_BUTTON ] = pGroup;
+#ifdef RAD_WIN32
+ m_actionButton = pGroup->GetText( "ActionTextButton" );
+ m_actionLabel = pGroup->GetText( "ActionTextLabel" );
+#else
+ m_actionButton = pGroup->GetSprite( "ActionButton" );
+#endif
+
+ // hide all HUD overlays by default
+ //
+ for( i = 0; i < NUM_HUD_OVERLAYS; i++ )
+ {
+ if( i == NUM_HUD_MISSION_OVERLAYS )
+ {
+ continue;
+ }
+
+ rAssert( m_overlays[ i ] );
+ m_overlays[ i ]->SetVisible( false );
+
+ m_elapsedTime[ i ] = 0;
+ }
+
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ if( currentMission != NULL )
+ {
+ this->SetVisible( currentMission->IsHUDVisible() );
+ }
+
+ // create HUD event handlers
+ //
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_CARD_COLLECTED ] = new HudCardCollected( pPage );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_COIN_COLLECTED ] = new HudCoinCollected( pPage );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_MISSION_PROGRESS ] = new HudMissionProgress( pPage );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_MISSION_OBJECTIVE ] = new HudMissionObjective( pPage );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_COUNTDOWN ] = new HudCountDown( pPage );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_HIT_N_RUN ] = new HudHitNRun( pPage );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_WASP_DESTROYED ] = new HudWaspDestroyed( pPage );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_ITEM_DROPPED ] = new HudItemDropped( pPage );
+
+ // register event listeners
+ //
+ GetEventManager()->AddListener( this, EVENT_CARD_COLLECTED );
+ GetEventManager()->AddListener( this, EVENT_COLLECTED_COINS );
+ GetEventManager()->AddListener( this, EVENT_LOST_COINS );
+ GetEventManager()->AddListener( this, EVENT_STAGE_COMPLETE );
+ GetEventManager()->AddListener( this, EVENT_SHOW_MISSION_OBJECTIVE );
+ GetEventManager()->AddListener( this, EVENT_GUI_COUNTDOWN_START );
+ GetEventManager()->AddListener( this, EVENT_HIT_AND_RUN_START );
+ GetEventManager()->AddListener( this, EVENT_HIT_AND_RUN_CAUGHT );
+ GetEventManager()->AddListener( this, EVENT_GAMBLERACE_SUCCESS );
+ GetEventManager()->AddListener( this, EVENT_GAMBLERACE_FAILURE );
+ GetEventManager()->AddListener( this, EVENT_WASP_BLOWED_UP );
+ GetEventManager()->AddListener( this, EVENT_GAG_FOUND );
+ GetEventManager()->AddListener( this, EVENT_DUMP_STATUS );
+ GetEventManager()->AddListener( this, EVENT_AVATAR_ON_ROAD );
+ GetEventManager()->AddListener( this, EVENT_AVATAR_OFF_ROAD );
+ GetEventManager()->AddListener( this, EVENT_OUTOFCAR_CONDITION_ACTIVE );
+ GetEventManager()->AddListener( this, EVENT_OUTOFCAR_CONDITION_INACTIVE );
+
+MEMTRACK_POP_GROUP("CGUIScreenHud");
+}
+
+
+//===========================================================================
+// CGuiScreenHud::~CGuiScreenHud
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenHud::~CGuiScreenHud()
+{
+ // destroy hud event handlers
+ //
+ for( int i = 0; i < NUM_HUD_EVENT_HANDLERS; i++ )
+ {
+ if( m_hudEventHandlers[ i ] != NULL )
+ {
+ // stop them first in case they're still in progress
+ //
+ m_hudEventHandlers[ i ]->Stop();
+
+ delete m_hudEventHandlers[ i ];
+ m_hudEventHandlers[ i ] = NULL;
+ }
+ }
+
+ // unregister event listeners
+ //
+ GetEventManager()->RemoveAll( this );
+
+ s_numCoinsDisplay = NULL;
+}
+
+
+//===========================================================================
+// CGuiScreenHud::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( message == GUI_MSG_MENU_PROMPT_RESPONSE )
+ {
+ m_pParent->HandleMessage( GUI_MSG_RESUME_INGAME );
+ }
+
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ this->Update( param1 );
+
+ break;
+ }
+ case GUI_MSG_SHOW_HUD_OVERLAY:
+ {
+ if( param1 == HUD_TIMER_TEMP && m_overlays[ HUD_TIMER ]->IsVisible() )
+ {
+ // ignore, cuz these are both the same overlay
+ //
+ break;
+ }
+
+ // follow-through
+ //
+ }
+ case GUI_MSG_HIDE_HUD_OVERLAY:
+ {
+ rAssert( param1 < NUM_HUD_OVERLAYS );
+ rAssert( m_overlays[ param1 ] != NULL );
+
+ // show/hide HUD overlay
+ //
+ m_overlays[ param1 ]->SetVisible( message == GUI_MSG_SHOW_HUD_OVERLAY );
+ m_elapsedTime[ param1 ] = 0;
+
+ rAssertMsg( param1 != HUD_MESSAGE, "Call CGuiScreenHud::DisplayMessage() instead!" );
+
+ // for mission-related overlays
+ //
+ if( message == GUI_MSG_SHOW_HUD_OVERLAY &&
+ this->IsMissionOverlay( param1 ) )
+ {
+ const float SECONDARY_MISSION_OVERLAY_SCALE = 0.75f;
+ const int SECONDARY_MISSION_OVERLAY_OFFSET_Y = -45;
+
+ m_overlays[ param1 ]->ResetTransformation();
+
+ unsigned int otherOverlayIndex = 0;
+ int numOverlaysEnabled = 0;
+
+ for( unsigned int i = 0; this->IsMissionOverlay( i ); i++ )
+ {
+ if( m_overlays[ i ]->IsVisible() && m_overlays[ i ] != m_overlays[ param1 ] )
+ {
+ otherOverlayIndex = i;
+ numOverlaysEnabled++;
+ }
+ }
+
+ if( numOverlaysEnabled >= 2 )
+ {
+ rAssertMsg( numOverlaysEnabled == 2, "More than two is not handled properly yet!" );
+
+ // if already two overlays enabled, just push this new one down as the third one
+ // w/out re-ordering anything
+ //
+ m_overlays[ param1 ]->ScaleAboutPoint( SECONDARY_MISSION_OVERLAY_SCALE, 0, 0 );
+ m_overlays[ param1 ]->Translate( 0, SECONDARY_MISSION_OVERLAY_OFFSET_Y * 2 );
+ }
+ else if( numOverlaysEnabled == 1 )
+ {
+ // already another mission overlay enabled, need to
+ // scale and translate the secondary one
+ //
+ unsigned int secondaryOverlay = rmt::Max( otherOverlayIndex, param1 );
+
+ m_overlays[ secondaryOverlay ]->ResetTransformation();
+ m_overlays[ secondaryOverlay ]->ScaleAboutPoint( SECONDARY_MISSION_OVERLAY_SCALE, 0, 0 );
+ m_overlays[ secondaryOverlay ]->Translate( 0, SECONDARY_MISSION_OVERLAY_OFFSET_Y );
+ }
+ }
+
+ // special cases
+ //
+ if( param1 == HUD_PROXIMITY_METER )
+ {
+ rAssert( m_proximityMeter.m_pImage != NULL );
+ if( param2 == 0 ) // 0 = red bar
+ {
+ m_proximityMeter.m_pImage->SetColour( PROXIMITY_METER_RED );
+ }
+ else // 1 = green bar
+ {
+ m_proximityMeter.m_pImage->SetColour( PROXIMITY_METER_GREEN );
+ }
+ }
+ else if( param1 == HUD_MISSION_COMPLETE )
+ {
+ m_isBonusMissionJustCompleted = false;
+
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ rAssert( currentMission != NULL );
+ if( !currentMission->IsWagerMission() ) // not a wager race
+ {
+ if( currentMission->IsBonusMission() )
+ {
+ rAssert( m_missionComplete != NULL );
+ m_missionComplete->SetBitmapText( GetTextBibleString( "BONUS_MISSION_COMPLETE" ) );
+
+ m_isBonusMissionJustCompleted = true;
+ }
+ else
+ {
+ rAssert( m_missionComplete != NULL );
+ m_missionComplete->SetBitmapText( GetTextBibleString( "MISSION_COMPLETE" ) );
+ }
+ }
+ }
+ else if( param1 == HUD_LAP_COUNTER )
+ {
+ m_lapUpdated = 0;
+ }
+/*
+ else if( param1 == HUD_MAP )
+ {
+ if( param2 != 0 ) // != 0 means w/ fading
+ {
+ if( message == GUI_MSG_SHOW_HUD_OVERLAY )
+ {
+ m_isFadingInRadar = true;
+ m_isFadingOutRadar = false;
+ }
+ else
+ {
+ m_isFadingOutRadar = true;
+ m_isFadingInRadar = false;
+
+ // wait for fade out transition to finish
+ // before turning off radar
+ //
+ m_overlays[ HUD_MAP ]->SetVisible( true );
+ }
+ }
+ }
+ else if( param1 == HUD_DAMAGE_METER )
+ {
+ m_isSlidingDamageMeter = (message == GUI_MSG_SHOW_HUD_OVERLAY);
+ }
+*/
+
+ break;
+ }
+ case GUI_MSG_INGAME_FADE_OUT:
+ {
+ if( !m_isFadingOut )
+ {
+ rAssertMsg( !m_isFadingIn, "Fade in currently in progress!" );
+
+ m_isFadingOut = true;
+ m_elapsedFadeTime = 0.0f;
+ ++m_numTransitionsPending;
+ }
+ else
+ {
+ rWarningMsg( false, "Fade out currently in progress! Ignoring redundant request." );
+ }
+
+ break;
+ }
+ case GUI_MSG_INGAME_FADE_IN:
+ {
+ if( !m_isFadingIn )
+ {
+ rAssertMsg( !m_isFadingOut, "Fade out currently in progress!" );
+ ++m_numTransitionsPending;
+ m_isFadingIn = true;
+ m_elapsedFadeTime = 0.0f;
+ }
+ else
+ {
+ rWarningMsg( false, "Fade in currently in progress! Ignoring redundant request." );
+ }
+
+ break;
+ }
+ case GUI_MSG_INGAME_FADE_OUT_CANCEL:
+ {
+ if( m_isFadingOut )
+ {
+ rAssert( m_screenCover != NULL );
+ m_screenCover->SetAlpha( 1.0f );
+
+ m_isFadingOut = false;
+ --m_numTransitionsPending;
+
+ GetEventManager()->TriggerEvent( EVENT_GUI_FADE_OUT_DONE );
+ }
+
+ break;
+ }
+ case GUI_MSG_INGAME_FADE_IN_CANCEL:
+ {
+ if( m_isFadingIn )
+ {
+ rAssert( m_screenCover != NULL );
+ m_screenCover->SetAlpha( 0.0f );
+
+ m_isFadingIn = false;
+ --m_numTransitionsPending;
+
+ GetEventManager()->TriggerEvent( EVENT_GUI_FADE_IN_DONE );
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_START:
+ {
+ if( m_screenCover != NULL && m_screenCover->GetAlpha() > 0.0f )
+ {
+ // don't allow pausing during fade in/out
+ //
+ return;
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreenMultiHud::HandleMessage( message, param1, param2 );
+}
+
+//===========================================================================
+// CGuiScreenHud::Update
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: elapsed time
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::Update( unsigned int elapsedTime )
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ if( m_elapsedFgdFadeInTime != -1.0f )
+ {
+ // fade in entire HUD foreground layer(s) to prevent sudden flashes
+ // on screen during situations where the HUD gets displayed very
+ // briefly before transitioning out to another screen
+ //
+ const float HUD_FGD_FADE_IN_TIME = 100.0f;
+
+ m_elapsedFgdFadeInTime += elapsedTime;
+ if( m_elapsedFgdFadeInTime < HUD_FGD_FADE_IN_TIME )
+ {
+// float alpha = m_elapsedFgdFadeInTime / HUD_FGD_FADE_IN_TIME;
+ this->SetAlphaForLayers( 0.0f, m_foregroundLayers, m_numForegroundLayers );
+ }
+ else
+ {
+ this->SetAlphaForLayers( 1.0f, m_foregroundLayers, m_numForegroundLayers );
+
+ rAssert( m_overlays[ HUD_MAP ] != NULL );
+ m_overlays[ HUD_MAP ]->SetAlpha( 1.0f );
+
+ rAssert( m_hudMap[ 0 ] != NULL );
+ m_hudMap[ 0 ]->SetVisible( GetGuiSystem()->IsRadarEnabled() );
+
+ m_elapsedFgdFadeInTime = -1.0f; // fade in is done
+ }
+ }
+ else
+ {
+ rAssert( m_hudMap[ 0 ] != NULL );
+ m_hudMap[ 0 ]->SetVisible( GetGuiSystem()->IsRadarEnabled() );
+ }
+ }
+
+ static float INGAME_FADE_TIME = 250.0f; // in msec
+
+ if( m_isFadingOut )
+ {
+ rAssert( m_screenCover != NULL );
+
+ m_elapsedFadeTime += (float)elapsedTime;
+ if( m_elapsedFadeTime < INGAME_FADE_TIME )
+ {
+ m_screenCover->SetAlpha( m_elapsedFadeTime / INGAME_FADE_TIME );
+ }
+ else
+ {
+ m_screenCover->SetAlpha( 1.0f );
+
+ --m_numTransitionsPending;
+ m_isFadingOut = false;
+
+ GetEventManager()->TriggerEvent( EVENT_GUI_FADE_OUT_DONE );
+ }
+ }
+
+ if( m_isFadingIn )
+ {
+ rAssert( m_screenCover != NULL );
+
+ m_elapsedFadeTime += (float)elapsedTime;
+ if( m_elapsedFadeTime < INGAME_FADE_TIME )
+ {
+ m_screenCover->SetAlpha( 1.0f - m_elapsedFadeTime / INGAME_FADE_TIME );
+ }
+ else
+ {
+ m_screenCover->SetAlpha( 0.0f );
+
+ --m_numTransitionsPending;
+ m_isFadingIn = false;
+
+ GetEventManager()->TriggerEvent( EVENT_GUI_FADE_IN_DONE );
+ }
+ }
+
+ this->UpdateOverlays( elapsedTime );
+ this->UpdateEventHandlers( elapsedTime );
+ this->UpdateTutorialMode( static_cast<float>( elapsedTime ) );
+
+#ifdef SRR2_MOVABLE_HUD_MAP
+ this->UpdateHudMapPosition( elapsedTime );
+#endif
+
+/*
+ // TC: *** testing only ***
+ //
+ static float s_elapsedTime = 0.0f;
+ s_elapsedTime += static_cast<float>( elapsedTime );
+ float value = 0.5f * rmt::Sin( rmt::PI_2 * s_elapsedTime / 2000.0f ) + 0.5f;
+ this->SetHitAndRunMeter( value );
+*/
+ HitnRunManager* hnrManager = GetHitnRunManager();
+ rAssert( hnrManager != NULL );
+
+ // blink H&R Meter lights
+ //
+ const unsigned int HNR_BLINK_PERIOD = 500;
+
+ rAssert( m_hnrLights != NULL );
+ rAssert( m_hnrMeterBgd != NULL );
+ if( hnrManager->IsHitnRunActive() )
+ {
+ m_hnrMeterBgd->SetColour( tColour( 17, 31, 161 ) );
+ }
+ else
+ {
+ m_hnrMeterBgd->SetColour( tColour( 0, 0, 0 ) );
+
+ m_hnrLights->SetIndex( 2 ); // 2 = lights off
+ }
+
+ m_hnrElapsedTime += elapsedTime;
+ if( m_hnrElapsedTime > HNR_BLINK_PERIOD / 2 )
+ {
+ if( hnrManager->IsHitnRunActive() )
+ {
+ int currentIndex = m_hnrLights->GetIndex();
+ m_hnrLights->SetIndex( currentIndex > 1 ? 0 : 1 - currentIndex );
+
+ rAssert( m_hnrMeter != NULL );
+ m_hnrMeter->SetVisible( !m_hnrMeter->IsVisible() );
+ }
+ else if( (hnrManager->GetHitnRunValue() / 100.0f) > HNR_METER_BLINKING_THRESHOLD )
+ {
+ rAssert( m_hnrMeter != NULL );
+ m_hnrMeter->SetVisible( !m_hnrMeter->IsVisible() );
+
+ // TC: [TODO] throw a sound event to go w/ this
+ //
+ }
+
+ m_hnrElapsedTime %= HNR_BLINK_PERIOD / 2;
+ }
+
+ if( m_isAvatarOffRoad )
+ {
+ // update avatar off-road duration time
+ //
+ m_avatarOffRoadDurationTime += elapsedTime;
+ }
+}
+
+//===========================================================================
+// CGuiScreenHud::SetVisible
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::SetVisible( bool isVisible )
+{
+ rAssert( m_pScroobyScreen != NULL );
+
+ // for each page in screen
+ //
+ int numPages = m_pScroobyScreen->GetNumberOfPages();
+ for( int i = 0; i < numPages; i++ )
+ {
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPageByIndex( i );
+ rAssert( pPage != NULL );
+
+ // for each layer in page
+ //
+ int numLayers = pPage->GetNumberOfLayers();
+ for( int j = 0; j < numLayers; j++ )
+ {
+ Scrooby::Layer* pLayer = pPage->GetLayerByIndex( j );
+ rAssert( pLayer );
+
+ pLayer->SetVisible( isVisible );
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenHud::SetTimerBlinkingInterval
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::SetTimerBlinkingInterval( int startTime, int endTime )
+{
+ m_timerBlinkingStartTime = startTime;
+ m_timerBlinkingEndTime = endTime;
+}
+
+
+//===========================================================================
+// CGuiScreenHud::SetParTime
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::SetParTime( int parTime ) // in seconds
+{
+ char buffer[ BITMAP_TEXT_BUFFER_SIZE ];
+
+ sprintf( buffer, "%d:%02d", parTime / 60, parTime % 60 );
+
+ rAssert( m_parTime != NULL );
+ m_parTime->SetBitmapText( buffer );
+}
+
+
+//===========================================================================
+// CGuiScreenHud::SetCollectibles
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::SetCollectibles( int numCollected, int numToCollect )
+{
+// rAssert( m_collectibles != NULL );
+// m_collectibles->SetVisible( numToCollect > 1 ); // only show collectibles if more than 1 to collect
+
+ char buffer[ BITMAP_TEXT_BUFFER_SIZE ];
+ sprintf( buffer, "%d/%d", numCollected, numToCollect );
+
+ m_collectibles->SetBitmapText( buffer );
+/*
+ m_playerCollected.SetValue( numCollected );
+ m_numToCollect.SetValue( numToCollect );
+*/
+ if( numCollected > 0 )
+ {
+ m_collectiblesUpdated = 1;
+
+ m_elapsedTime[ HUD_COLLECTIBLES ] = 0;
+
+ // TC [TODO]: play sound effect
+ //
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenHud::SetRacePosition
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::SetRacePosition( int currentPosition, int numPositions )
+{
+ char buffer[ BITMAP_TEXT_BUFFER_SIZE ];
+
+ // extra spaces are needed to make more room for the ordinal text
+ //
+ char extraSpaces[ 4 ];
+ strcpy( extraSpaces, " " );
+
+#ifdef PAL
+ if( CGuiTextBible::GetCurrentLanguage() != Scrooby::XL_ENGLISH )
+ {
+ extraSpaces[ 0 ] = '\0';
+ }
+#endif // PAL
+
+ if( m_isAvatarOffRoad && m_avatarOffRoadDurationTime > MIN_AVATAR_OFF_ROAD_TIME )
+ {
+ sprintf( buffer, "?%s/%d", extraSpaces, numPositions );
+ m_positionOrdinal->SetVisible( false );
+ }
+ else
+ {
+ sprintf( buffer, "%d%s/%d", currentPosition, extraSpaces, numPositions );
+ m_positionOrdinal->SetVisible( true );
+ }
+
+ rAssert( m_position != NULL );
+ m_position->SetBitmapText( buffer );
+/*
+ m_currentPosition.SetValue( currentPosition );
+ m_numPositions.SetValue( numPositions );
+*/
+ m_positionOrdinal->SetIndex( currentPosition > 3 ? 3 : currentPosition - 1 );
+}
+
+
+//===========================================================================
+// CGuiScreenHud::SetLap
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::SetLap( int currentLap, int numLaps )
+{
+ char buffer[ BITMAP_TEXT_BUFFER_SIZE ];
+ sprintf( buffer, "%d/%d", currentLap, numLaps );
+
+ rAssert( m_lap != NULL );
+ m_lap->SetBitmapText( buffer );
+/*
+ m_currentLap.SetValue( currentLap );
+ m_numLaps.SetValue( numLaps );
+*/
+ if( currentLap > 1 )
+ {
+ m_lapUpdated = 8;
+
+ m_elapsedTime[ HUD_LAP_COUNTER ] = 0;
+
+ // play sound effect
+ //
+ GetEventManager()->TriggerEvent( EVENT_HUD_LAP_UPDATED );
+ }
+ else
+ {
+ m_lapUpdated = 0;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenHud::SetDamageMeter
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::SetDamageMeter( float value )
+{
+ m_damageMeter.SetValue( value );
+}
+
+
+//===========================================================================
+// CGuiScreenHud::SetProximityMeter
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::SetProximityMeter( float value )
+{
+ m_proximityMeter.SetValue( value );
+}
+
+
+//===========================================================================
+// CGuiScreenHud::SetHitAndRunMeter
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::SetHitAndRunMeter( float value )
+{
+ rAssert( value >= 0.0f && value <= 1.0f );
+
+ rAssert( m_hnrMeter != NULL );
+ if( value > HNR_METER_BLINKING_THRESHOLD || GetHitnRunManager()->IsHitnRunActive() )
+ {
+ m_hnrMeter->SetColour( HNR_METER_WARNING_COLOUR );
+ }
+ else
+ {
+ m_hnrMeter->SetColour( HNR_METER_DEFAULT_COLOUR ); // restore default colour
+ m_hnrMeter->SetVisible( true );
+ }
+
+ const float TOTAL_AMOUNT_COVERED = 1.0f - value;
+ const float SECTOR_AMOUNT = 1.0f / NUM_HIT_N_RUN_SECTORS;
+ const float TOTAL_ANGLE = (1.0f - 1 / (float)NUM_HIT_N_RUN_SECTORS) * 360.0f;
+
+ float amountCovered = 0.0f;
+
+ for( int i = 0; i < NUM_HIT_N_RUN_SECTORS; i++ )
+ {
+ rAssert( m_hnrSectors[ i ] != NULL );
+ m_hnrSectors[ i ]->ResetTransformation();
+
+ if( amountCovered < TOTAL_AMOUNT_COVERED )
+ {
+ float currentCovered = TOTAL_AMOUNT_COVERED - amountCovered;
+ if( currentCovered > SECTOR_AMOUNT )
+ {
+ currentCovered = SECTOR_AMOUNT;
+ }
+
+ amountCovered += currentCovered;
+
+ m_hnrSectors[ i ]->RotateAboutCenter( -amountCovered * TOTAL_ANGLE );
+
+ static float correctionScale = 1.02f;
+ m_hnrSectors[ i ]->ScaleAboutCenter( correctionScale );
+/*
+#ifdef RAD_DEBUG
+ static tColour sectorColour( HNR_METER_EMPTY_COLOUR );
+ m_hnrSectors[ i ]->SetColour( sectorColour );
+#endif
+*/
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenHud::DisplayMessage
+//===========================================================================
+// Description: adds some scrolling text to the queue of text to show
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::DisplayMessage( bool show, const int index )
+{
+ rAssert( m_overlays[ HUD_MESSAGE ] != NULL );
+
+ if( show )
+ {
+ if( !m_helpMessageQueue.IsEmpty() )
+ {
+ m_helpMessageQueue.Enqueue( index );
+ }
+ else
+ {
+ if( m_overlays[ HUD_MESSAGE ]->IsVisible() )
+ {
+ if( m_elapsedTime[ HUD_MESSAGE ] > MESSAGE_DISPLAY_TIME - MESSAGE_TRANSITION_TIME )
+ {
+ m_elapsedTime[ HUD_MESSAGE ] = MESSAGE_DISPLAY_TIME - m_elapsedTime[ HUD_MESSAGE ];
+ }
+ else if( m_elapsedTime[ HUD_MESSAGE ] > MESSAGE_TRANSITION_TIME )
+ {
+ m_elapsedTime[ HUD_MESSAGE ] = MESSAGE_TRANSITION_TIME;
+ }
+ }
+ else
+ {
+ m_overlays[ HUD_MESSAGE ]->SetVisible( true );
+ m_overlays[ HUD_MESSAGE ]->ScaleAboutCenter( 0.0f );
+ m_elapsedTime[ HUD_MESSAGE ] = 0;
+ }
+
+ rAssert( index >= 0 );
+ m_helpMessage->SetIndex( index );
+ }
+ }
+ else
+ {
+ m_overlays[ HUD_MESSAGE ]->SetVisible( false );
+ }
+}
+
+
+void
+CGuiScreenHud::DisplayMissionOverlays( bool show )
+{
+ rAssert( m_missionOverlays != NULL );
+ m_missionOverlays->SetVisible( show );
+}
+
+
+void
+CGuiScreenHud::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_CARD_COLLECTED:
+ {
+ // stop coin presentation in case it's currently active
+ //
+ rAssert( m_hudEventHandlers[ HUD_EVENT_HANDLER_COIN_COLLECTED ] != NULL );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_COIN_COLLECTED ]->Stop();
+
+ rAssert( m_hudEventHandlers[ HUD_EVENT_HANDLER_CARD_COLLECTED ] != NULL );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_CARD_COLLECTED ]->Start();
+
+ Card* collectedCard = reinterpret_cast<Card*>( pEventData );
+ rAssert( collectedCard != NULL );
+ HudCardCollected* hudCardCollected = static_cast<HudCardCollected*>( m_hudEventHandlers[ HUD_EVENT_HANDLER_CARD_COLLECTED ] );
+ hudCardCollected->SetCurrentCard( collectedCard->GetID() );
+
+ break;
+ }
+ case EVENT_COLLECTED_COINS:
+ case EVENT_LOST_COINS:
+ {
+ rAssert( m_hudEventHandlers[ HUD_EVENT_HANDLER_CARD_COLLECTED ] != NULL );
+ if( !m_hudEventHandlers[ HUD_EVENT_HANDLER_CARD_COLLECTED ]->IsActive() )
+ {
+ // only show coin presentation if card presentation is not active
+ //
+ rAssert( m_hudEventHandlers[ HUD_EVENT_HANDLER_COIN_COLLECTED ] != NULL );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_COIN_COLLECTED ]->Start();
+ }
+
+ break;
+ }
+ case EVENT_STAGE_COMPLETE:
+ {
+ unsigned int showStageComplete = reinterpret_cast<unsigned int>( pEventData );
+ if( showStageComplete > 0 )
+ {
+ rAssert( m_hudEventHandlers[ HUD_EVENT_HANDLER_MISSION_PROGRESS ] != NULL );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_MISSION_PROGRESS ]->Start();
+ }
+
+ break;
+ }
+ case EVENT_SHOW_MISSION_OBJECTIVE:
+ {
+ int messageID = reinterpret_cast<unsigned int>( pEventData );
+
+ HudMissionObjective* hudMissionObjective = static_cast<HudMissionObjective*>( m_hudEventHandlers[ HUD_EVENT_HANDLER_MISSION_OBJECTIVE ] );
+ if( messageID != -1 )
+ {
+ rAssert( m_hudEventHandlers[ HUD_EVENT_HANDLER_MISSION_OBJECTIVE ] != NULL );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_MISSION_OBJECTIVE ]->Start();
+
+// this->DisplayMessage( true, messageID );
+
+ hudMissionObjective->SetMessageID( messageID );
+ }
+ else
+ {
+ hudMissionObjective->UpdateIcon();
+ }
+
+ break;
+ }
+ case EVENT_GUI_COUNTDOWN_START:
+ {
+ rAssert( m_hudEventHandlers[ HUD_EVENT_HANDLER_COUNTDOWN ] != NULL );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_COUNTDOWN ]->Start();
+
+ break;
+ }
+ case EVENT_HIT_AND_RUN_START:
+ case EVENT_HIT_AND_RUN_CAUGHT:
+ {
+ HudHitNRun* hudHitNRun = static_cast<HudHitNRun*>( m_hudEventHandlers[ HUD_EVENT_HANDLER_HIT_N_RUN] );
+ rAssert( hudHitNRun != NULL );
+ if( id == EVENT_HIT_AND_RUN_START )
+ {
+ hudHitNRun->SetMessage( HudHitNRun::MSG_HIT_N_RUN );
+ }
+ else
+ {
+ rAssert( id == EVENT_HIT_AND_RUN_CAUGHT );
+ hudHitNRun->SetMessage( HudHitNRun::MSG_BUSTED );
+ }
+
+ rAssert( m_hudEventHandlers[ HUD_EVENT_HANDLER_HIT_N_RUN ] != NULL );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_HIT_N_RUN ]->Start();
+
+ break;
+ }
+ case EVENT_GAMBLERACE_SUCCESS: // (pEventData != 0) means new best time been set
+ {
+ if( pEventData != 0 )
+ {
+ // display "Congratulations! New Best Time!"
+ //
+ rAssert( m_missionComplete != NULL );
+ m_missionComplete->SetBitmapText( GetTextBibleString( "GAMBLING_RACE_SUCCESS_NBT" ) );
+ }
+ else
+ {
+ // display "Congratulations!"
+ //
+ rAssert( m_missionComplete != NULL );
+ m_missionComplete->SetBitmapText( GetTextBibleString( "GAMBLING_RACE_SUCCESS" ) );
+ }
+
+ break;
+ }
+ case EVENT_GAMBLERACE_FAILURE:
+ {
+ rAssert( m_missionComplete != NULL );
+ m_missionComplete->SetBitmapText( GetTextBibleString( "GAMBLING_RACE_FAILURE" ) );
+
+ break;
+ }
+ case EVENT_GAG_FOUND:
+ case EVENT_WASP_BLOWED_UP:
+ {
+ HudWaspDestroyed* hudWaspDestroyed = static_cast<HudWaspDestroyed*>( m_hudEventHandlers[ HUD_EVENT_HANDLER_WASP_DESTROYED] );
+ rAssert( hudWaspDestroyed != NULL );
+ hudWaspDestroyed->SetGagInsteadOfWasp( id == EVENT_GAG_FOUND );
+
+ rAssert( m_hudEventHandlers[ HUD_EVENT_HANDLER_WASP_DESTROYED ] != NULL );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_WASP_DESTROYED ]->Start();
+
+ break;
+ }
+ case EVENT_DUMP_STATUS:
+ {
+ rAssert( m_hudEventHandlers[ HUD_EVENT_HANDLER_ITEM_DROPPED ] != NULL );
+
+ int numActiveItems = reinterpret_cast<int>( pEventData );
+ if( numActiveItems > 0 )
+ {
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_ITEM_DROPPED ]->Start();
+ }
+ else
+ {
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_ITEM_DROPPED ]->Stop();
+ }
+
+ break;
+ }
+ case EVENT_AVATAR_ON_ROAD:
+ {
+ m_isAvatarOffRoad = false;
+
+ break;
+ }
+ case EVENT_AVATAR_OFF_ROAD:
+ {
+ if( !m_isAvatarOffRoad ) // if previously on-road
+ {
+ m_avatarOffRoadDurationTime = 0; // reset off-road duration time
+ }
+
+ m_isAvatarOffRoad = true;
+
+ break;
+ }
+ case EVENT_OUTOFCAR_CONDITION_ACTIVE:
+ {
+ m_pGetOutOfCarCondition = static_cast<GetOutOfCarCondition*> (pEventData);
+ rAssert(m_pGetOutOfCarCondition != NULL);
+
+ break;
+ }
+
+ case EVENT_OUTOFCAR_CONDITION_INACTIVE:
+ {
+ m_pGetOutOfCarCondition = NULL;
+
+ break;
+ }
+
+
+ default:
+ {
+ rWarningMsg( false, "Unhandled event caught by GUI HUD!" );
+
+ break;
+ }
+ }
+}
+
+void
+CGuiScreenHud::SetNumCoinsDisplay( Scrooby::Sprite* pSprite )
+{
+ if( s_numCoinsDisplay == NULL )
+ {
+ rAssert( pSprite != NULL );
+ pSprite->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ pSprite->CreateBitmapTextBuffer( MAX_NUM_COIN_DIGITS );
+ pSprite->SetBitmapTextSpacing( NUMERIC_TEXT_SPACING );
+ pSprite->SetVisible( false ); // hide by default
+
+#ifdef RAD_WIN32
+ pSprite->ResetTransformation();
+ pSprite->Translate( 70, 0 );
+ pSprite->ScaleAboutCenter( 0.5f );
+#endif
+
+ s_numCoinsDisplay = pSprite;
+ }
+}
+
+void
+CGuiScreenHud::UpdateNumCoinsDisplay( int numCoins, bool show )
+{
+ rAssert( s_numCoinsDisplay != NULL );
+ s_numCoinsDisplay->SetVisible( show );
+
+ static int coinPosX = CGuiScreen::IsWideScreenDisplay() ? 540 : 605;
+ static int coinPosY = 432;
+ GetCoinManager()->SetHUDCoin( coinPosX, coinPosY, show );
+
+ if( show )
+ {
+ char buffer[ MAX_NUM_COIN_DIGITS ];
+ sprintf( buffer, "%d", numCoins );
+ s_numCoinsDisplay->SetBitmapText( buffer );
+ }
+}
+
+//===========================================================================
+// CGuiScreenHud::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::InitIntro()
+{
+ Scrooby::Screen* screen = GetScroobyScreen();
+ screen->SetAlpha( 1.0f );
+
+ m_missionFailedSprite->SetVisible( false );
+
+/*
+ if( GetInteriorManager()->IsInside() )
+ {
+ this->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_MAP );
+ }
+ else
+*/
+ {
+ this->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_MAP );
+ }
+
+ // hide hud map, by default
+ //
+ m_overlays[ HUD_MAP ]->SetAlpha( 0.0f );
+ rAssert( m_hudMap[ 0 ] != NULL );
+ m_hudMap[ 0 ]->SetVisible( false );
+
+ this->SetAlphaForLayers( 0.0f, m_foregroundLayers, m_numForegroundLayers );
+
+ // force update on hit & run meter
+ //
+ HitnRunManager* hnrManager = GetHitnRunManager();
+ rAssert( hnrManager != NULL );
+ this->SetHitAndRunMeter( hnrManager->GetHitnRunValue() / 100.0f );
+
+ CGuiScreenMultiHud::InitIntro();
+}
+
+
+//===========================================================================
+// CGuiScreenHud::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::InitRunning()
+{
+// this->SetFadingEnabled( false );
+
+ // for fading in/out ingame
+ //
+ rAssert( m_screenCover != NULL );
+ m_screenCover->SetVisible( true );
+ m_screenCover->SetAlpha( 0.0f );
+
+ // reset HUD foreground fade in time
+ //
+ m_elapsedFgdFadeInTime = 0.0f;
+
+ CGuiScreenMultiHud::InitRunning();
+}
+
+
+//===========================================================================
+// CGuiScreenHud::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::InitOutro()
+{
+/*
+ this->HideMissionObjective();
+
+ rAssert( m_overlays[ HUD_MESSAGE ] );
+ m_overlays[ HUD_MESSAGE ]->SetVisible( false );
+
+ m_helpMessageQueue.Clear();
+*/
+ // stop coin collected presentation
+ //
+ rAssert( m_hudEventHandlers[ HUD_EVENT_HANDLER_COIN_COLLECTED ] != NULL );
+ m_hudEventHandlers[ HUD_EVENT_HANDLER_COIN_COLLECTED ]->Stop();
+
+ if( m_isFadingIn ) // if we're in the middle of fading back in from blackness, just stop it right here
+ {
+ this->HandleMessage( GUI_MSG_INGAME_FADE_IN_CANCEL );
+ }
+
+ CGuiScreenMultiHud::InitOutro();
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+/*
+//===========================================================================
+// CGuiScreenHud::DisplayMissionObjective
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::DisplayMissionObjective( unsigned int messageID )
+{
+ rAssert( m_helpMessage );
+
+ // display mission objective message
+ //
+ m_helpMessage->SetTextIndex( messageID );
+ m_helpMessage->Start();
+}
+
+
+//===========================================================================
+// CGuiScreenHud::HideMissionObjective
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHud::HideMissionObjective()
+{
+ // hide mission objective message
+ //
+ rAssert( m_helpMessage );
+ if( m_helpMessage->GetCurrentState() != ScrollingText::STATE_IDLE )
+ {
+ m_helpMessage->Stop();
+ }
+}
+*/
+
+void
+CGuiScreenHud::UpdateOverlays( unsigned int elapsedTime )
+{
+ // update 'timer' overlay
+ //
+ if( m_overlays[ HUD_TIMER ]->IsVisible() )
+ {
+ this->UpdateTimer( elapsedTime );
+ }
+ else
+ {
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ rAssert( currentMission != NULL );
+ {
+ // TC: Temporary work-around until I get Cary's help to fix
+ // this properly
+ //
+ if( currentMission->GetMissionTimeLeftInMilliSeconds() > 1000 )
+ {
+ this->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_TIMER );
+
+ this->UpdateTimer( elapsedTime );
+ }
+
+ rWarningMsg( currentMission->GetMissionTimeLeftInMilliSeconds() <= 0,
+ "Why is there mission time left and the HUD timer is not enabled??" );
+ }
+ }
+
+ // update 'message' overlay
+ //
+ if( m_overlays[ HUD_MESSAGE ]->IsVisible() )
+ {
+ m_elapsedTime[ HUD_MESSAGE ] += elapsedTime;
+
+ if( m_elapsedTime[ HUD_MESSAGE ] > MESSAGE_DISPLAY_TIME )
+ {
+ if( !m_helpMessageQueue.IsEmpty() )
+ {
+ unsigned int messageIndex = m_helpMessageQueue.Dequeue();
+ m_helpMessage->SetIndex( static_cast<int>( messageIndex ) );
+
+ // reset elapsed message display time
+ //
+ m_elapsedTime[ HUD_MESSAGE ] = 0;
+ }
+ else
+ {
+ m_overlays[ HUD_MESSAGE ]->SetVisible( false );
+ }
+ }
+ else
+ {
+ m_overlays[ HUD_MESSAGE ]->ResetTransformation();
+
+ if( m_elapsedTime[ HUD_MESSAGE ] < MESSAGE_TRANSITION_TIME )
+ {
+ // transition in
+ //
+ float scaleY = m_elapsedTime[ HUD_MESSAGE ] / (float)MESSAGE_TRANSITION_TIME;
+ m_overlays[ HUD_MESSAGE ]->ScaleAboutCenter( 1.0f, scaleY, 1.0f );
+ }
+ else if( m_elapsedTime[ HUD_MESSAGE ] > MESSAGE_DISPLAY_TIME - MESSAGE_TRANSITION_TIME )
+ {
+ // transition out
+ //
+ float scaleY = (MESSAGE_DISPLAY_TIME - m_elapsedTime[ HUD_MESSAGE ]) / (float)MESSAGE_TRANSITION_TIME;
+ m_overlays[ HUD_MESSAGE ]->ScaleAboutCenter( 1.0f, scaleY, 1.0f );
+ }
+ }
+ }
+
+ // update 'action button' overlay
+ //
+ if( m_overlays[ HUD_ACTION_BUTTON ]->IsVisible() )
+ {
+#ifdef RAD_WIN32
+ const float PULSE_AMPLITUDE = 0.10f;
+ const float PULSE_PERIOD = 600.0f; // in milliseconds
+#else
+ const float PULSE_AMPLITUDE = 0.25f;
+ const float PULSE_PERIOD = 500.0f; // in milliseconds
+#endif
+
+ float scale = GuiSFX::Pulse( (float)m_elapsedTime[ HUD_ACTION_BUTTON ],
+ PULSE_PERIOD,
+ 1.0f,
+ PULSE_AMPLITUDE );
+
+ m_actionButton->ResetTransformation();
+ m_actionButton->ScaleAboutCenter( scale, scale, 1.0f );
+
+#ifdef RAD_WIN32
+ m_actionLabel->ResetTransformation();
+ m_actionLabel->ScaleAboutCenter( 0.85f, 0.85f, 1.0f );
+#endif
+
+ m_elapsedTime[ HUD_ACTION_BUTTON ] += elapsedTime;
+ }
+
+ // update 'mission complete' overlay
+ //
+ if( m_overlays[ HUD_MISSION_COMPLETE ]->IsVisible() )
+ {
+ static float MISSION_COMPLETE_DISPLAY_TIME = 2500; // in milliseconds
+
+#ifdef RAD_WIN32
+ bool done = GuiSFX::Flash( m_missionComplete,
+ (float)m_elapsedTime[ HUD_MISSION_COMPLETE ],
+ MISSION_COMPLETE_DISPLAY_TIME,
+ 3, 0.625f, 0.6f );
+#else
+ bool done = GuiSFX::Flash( m_missionComplete,
+ (float)m_elapsedTime[ HUD_MISSION_COMPLETE ],
+ MISSION_COMPLETE_DISPLAY_TIME,
+ 3, 1.25f, 1.2f );
+#endif
+
+ if( done )
+ {
+ m_overlays[ HUD_MISSION_COMPLETE ]->SetVisible( false );
+
+ if( m_isBonusMissionJustCompleted )
+ {
+ //
+ // Different prompt for different levels
+ //
+ RenderEnums::LevelEnum level = GetGameplayManager()->GetCurrentLevelIndex();
+ int levelInt = static_cast< int >( level );
+ m_pParent->HandleMessage( GUI_MSG_INGAME_DISPLAY_PROMPT, 12 + levelInt );
+ }
+ }
+
+ m_elapsedTime[ HUD_MISSION_COMPLETE ] += elapsedTime;
+ }
+
+ // update 'damage meter' overlay
+ //
+ if( m_overlays[ HUD_DAMAGE_METER ]->IsVisible() )
+ {
+/*
+ if( m_isSlidingDamageMeter )
+ {
+ const float SLIDING_TIME = 1000.0f;
+
+ if( m_elapsedTime[ HUD_DAMAGE_METER ] < SLIDING_TIME )
+ {
+ int xPos, yPos;
+ m_overlays[ HUD_DAMAGE_METER ]->GetOriginPosition( xPos, yPos );
+
+ int screenWidth = (int)Scrooby::App::GetInstance()->GetScreenWidth();
+
+ int xTranslation = (int)( (1.0f - m_elapsedTime[ HUD_DAMAGE_METER ] / SLIDING_TIME) *
+ (screenWidth - xPos) );
+
+ m_overlays[ HUD_DAMAGE_METER ]->ResetTransformation();
+ m_overlays[ HUD_DAMAGE_METER ]->Translate( xTranslation, 0 );
+
+ m_elapsedTime[ HUD_DAMAGE_METER ] += elapsedTime;
+ }
+ else
+ {
+ m_overlays[ HUD_DAMAGE_METER ]->ResetTransformation();
+
+ m_isSlidingDamageMeter = false;
+ }
+ }
+*/
+ const float DAMAGE_METER_BLINK_THRESHOLD = 0.75f;
+
+ if( m_damageMeter.m_value > DAMAGE_METER_BLINK_THRESHOLD )
+ {
+ const unsigned int BLINK_PERIOD = 250;
+
+ bool blinked = GuiSFX::Blink( m_damageMeter.m_pImage,
+ (float)m_elapsedTime[ HUD_DAMAGE_METER ],
+ (float)BLINK_PERIOD );
+ if( blinked )
+ {
+ m_elapsedTime[ HUD_DAMAGE_METER ] %= BLINK_PERIOD;
+ }
+
+ m_elapsedTime[ HUD_DAMAGE_METER ] += elapsedTime;
+ }
+ else
+ {
+ m_damageMeter.m_pImage->SetVisible( true );
+ }
+ }
+
+ // update 'lap counter' overlay
+ //
+ if( m_overlays[ HUD_LAP_COUNTER ]->IsVisible() || m_lapUpdated > 0 )
+ {
+ if( m_lapUpdated > 0 )
+ {
+ const int BLINK_PERIOD = 250; // msec
+ bool blinked = GuiSFX::Blink( m_overlays[ HUD_LAP_COUNTER ],
+ (float)m_elapsedTime[ HUD_LAP_COUNTER ],
+ (float)BLINK_PERIOD );
+
+ if( blinked )
+ {
+ m_elapsedTime[ HUD_LAP_COUNTER ] %= BLINK_PERIOD;
+
+ m_lapUpdated--;
+ }
+
+ m_elapsedTime[ HUD_LAP_COUNTER ] += elapsedTime;
+ }
+ }
+/*
+ // update 'hud map' overlay
+ //
+ if( m_overlays[ HUD_MAP ]->IsVisible() )
+ {
+ if( m_isFadingInRadar )
+ {
+ float radarAlpha = m_overlays[ HUD_MAP ]->GetAlpha() + (elapsedTime / (float)RADAR_FADE_TIME);
+
+ if( radarAlpha < 1.0f )
+ {
+ m_overlays[ HUD_MAP ]->SetAlpha( radarAlpha );
+ }
+ else
+ {
+ m_overlays[ HUD_MAP ]->SetAlpha( 1.0f );
+
+ m_isFadingInRadar = false;
+ }
+ }
+
+ if( m_isFadingOutRadar )
+ {
+ float radarAlpha = m_overlays[ HUD_MAP ]->GetAlpha() - (elapsedTime / (float)RADAR_FADE_TIME);
+
+ if( radarAlpha > 0.0f )
+ {
+ m_overlays[ HUD_MAP ]->SetAlpha( radarAlpha );
+ }
+ else
+ {
+ m_overlays[ HUD_MAP ]->SetAlpha( 0.0f );
+
+ m_isFadingInRadar = false;
+
+ // turn off radar
+ //
+ m_overlays[ HUD_MAP ]->SetVisible( false );
+ }
+ }
+ }
+*/
+}
+
+void
+CGuiScreenHud::UpdateEventHandlers( unsigned int elapsedTime )
+{
+ // update HUD event handlers
+ //
+ for( int j = 0; j < NUM_HUD_EVENT_HANDLERS; j++ )
+ {
+ if( m_hudEventHandlers[ j ] != NULL )
+ {
+ m_hudEventHandlers[ j ]->Update( (float)elapsedTime );
+ }
+ }
+}
+
+void
+CGuiScreenHud::UpdateTimer( unsigned int elapsedTime )
+{
+ const int TIMER_BLINKING_STARTING_PERIOD = 500;
+ const tColour TIMER_BLINKING_COLOUR( 217, 2, 5 );
+
+ int numMillisecondsRemaining = 0;
+
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ if( currentMission != NULL )
+ {
+ if (m_pGetOutOfCarCondition != NULL)
+ {
+ //use the condition timer
+ numMillisecondsRemaining = m_pGetOutOfCarCondition->GetTimeRemainingTilFailuremilliseconds();
+ }
+ else
+ {
+ numMillisecondsRemaining = currentMission->GetMissionTimeLeftInMilliSeconds();
+ }
+ }
+
+ if( numMillisecondsRemaining > 1000 )
+ {
+ rAssert( m_timer != NULL );
+
+ float blinkPercentage = (numMillisecondsRemaining - m_timerBlinkingStartTime) /
+ (float)(m_timerBlinkingEndTime - m_timerBlinkingStartTime);
+
+ // blink timer if remaining time is btw. timer blinking start
+ // and end time
+ //
+ if( blinkPercentage > 0.0f )
+ {
+ float blinkingPeriod = (float)TIMER_BLINKING_STARTING_PERIOD;
+ if( blinkPercentage > 0.8f )
+ {
+ blinkingPeriod *= 0.50f;
+ }
+ else if( blinkPercentage > 0.5f )
+ {
+ blinkingPeriod *= 0.67f;
+ }
+
+ tColour currentColour;
+ GuiSFX::ModulateColour( &currentColour,
+ (float)numMillisecondsRemaining,
+ blinkingPeriod,
+ m_defaultTimerColour,
+ TIMER_BLINKING_COLOUR,
+ rmt::PI_BY2 );
+
+ m_timer->SetColour( currentColour );
+
+ if( m_elapsedTime[ HUD_TIMER ] >= blinkingPeriod )
+ {
+ GetEventManager()->TriggerEvent( EVENT_HUD_TIMER_BLINK );
+
+ m_elapsedTime[ HUD_TIMER ] %= (int)blinkingPeriod;
+ }
+
+ m_elapsedTime[ HUD_TIMER ] += elapsedTime;
+ }
+ else
+ {
+ // restore timer's default colours
+ //
+ m_timer->SetColour( m_defaultTimerColour );
+
+ m_elapsedTime[ HUD_TIMER ] = TIMER_BLINKING_STARTING_PERIOD;
+ }
+
+ int numSecondsRemaining = numMillisecondsRemaining / 1000;
+
+ char buffer[ BITMAP_TEXT_BUFFER_SIZE ];
+ sprintf( buffer, "%d:%02d", numSecondsRemaining / 60, numSecondsRemaining % 60 );
+
+ m_timer->SetBitmapText( buffer );
+ }
+ else
+ {
+ this->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_TIMER );
+ }
+}
+
+#ifdef SRR2_MOVABLE_HUD_MAP
+
+void
+CGuiScreenHud::UpdateHudMapPosition( unsigned int elapsedTime )
+{
+ // update HUD map screen position
+ //
+ int numUserInputHandlers = GetGuiSystem()->GetNumUserInputHandlers();
+ for( int i = 0; i < numUserInputHandlers; i++ )
+ {
+ CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( i );
+ if( userInputHandler != NULL )
+ {
+#ifdef RAD_PS2
+ if( userInputHandler->IsButtonDown( GuiInput::Back ) ) // action button (for PS2)
+#else
+ if( userInputHandler->IsButtonDown( GuiInput::AuxY ) ) // action button (for Xbox and GC)
+#endif
+ {
+ static int NUM_PIXELS_PER_SECOND = 160;
+ int translationAmount = (int)( (elapsedTime / 1000.0f) * (float)NUM_PIXELS_PER_SECOND );
+
+ if( userInputHandler->IsButtonDown( GuiInput::Left ) )
+ {
+ this->MoveHudMap( -translationAmount, 0 );
+ }
+
+ if( userInputHandler->IsButtonDown( GuiInput::Right ) )
+ {
+ this->MoveHudMap( translationAmount, 0 );
+ }
+
+ if( userInputHandler->IsButtonDown( GuiInput::Up ) )
+ {
+ this->MoveHudMap( 0, translationAmount );
+ }
+
+ if( userInputHandler->IsButtonDown( GuiInput::Down ) )
+ {
+ this->MoveHudMap( 0, -translationAmount );
+ }
+ }
+ }
+ }
+}
+
+void
+CGuiScreenHud::MoveHudMap( int x, int y )
+{
+ rAssert( m_hudMap[ 0 ] );
+
+ // clamp HUD map within screen boundaries
+ //
+ int mapPosX = 0;
+ int mapPosY = 0;
+ int mapWidth = 0;
+ int mapHeight = 0;
+ m_hudMap[ 0 ]->GetPure3dObject()->GetOriginPosition( mapPosX, mapPosY );
+ m_hudMap[ 0 ]->GetPure3dObject()->GetBoundingBoxSize( mapWidth, mapHeight );
+
+ if( (mapPosX + x) < 0 ||
+ (mapPosX + x) > (static_cast<int>( Scrooby::App::GetInstance()->GetScreenWidth() ) - mapWidth) )
+ {
+ x = 0;
+ }
+
+ if( (mapPosY + y) < 0 ||
+ (mapPosY + y) > (static_cast<int>( Scrooby::App::GetInstance()->GetScreenHeight() ) - mapHeight) )
+ {
+ y = 0;
+ }
+
+ // move the HUD map
+ //
+ m_hudMap[ 0 ]->Translate( x, y );
+}
+
+#endif
+
+void CGuiScreenHud::AbortFade()
+{
+ m_elapsedFadeTime = 250.0f;
+ m_screenCover->SetAlpha( 0.0f );
+ Update( 0 );
+}
+
+#ifdef DEBUGWATCH
+const char* CGuiScreenHud::GetWatcherName() const
+{
+ return "CGuiScreenHud";
+}
+#endif \ No newline at end of file
diff --git a/game/code/presentation/gui/ingame/guiscreenhud.h b/game/code/presentation/gui/ingame/guiscreenhud.h
new file mode 100644
index 0000000..3b4f6a0
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenhud.h
@@ -0,0 +1,271 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenHud
+//
+// Description:
+//
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/11/20 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENHUD_H
+#define GUISCREENHUD_H
+
+//#define SRR2_MOVABLE_HUD_MAP // enable user to move HUD map
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenmultihud.h>
+#include <presentation/gui/utility/slider.h>
+
+#include <events/eventlistener.h>
+#include <p3d/p3dtypes.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class HudEventHandler;
+class GetOutOfCarCondition;
+
+class MessageQueue
+{
+ // This is a very simple queue data structure that uses a circular
+ // array to store unsigned integer values.
+ //
+ // m_front - points to the front slot of the queue
+ // m_back - points to the next free slot right behind the back of the queue
+ //
+public:
+ MessageQueue();
+ virtual ~MessageQueue();
+
+ void Enqueue( unsigned int messageID );
+ unsigned int Dequeue();
+
+ void Clear();
+ bool IsEmpty() const { return m_isEmpty; }
+ bool IsFull() const { return m_isFull; }
+
+private:
+ static const unsigned short MESSAGE_QUEUE_CAPACITY = 8;
+
+ unsigned int m_messages[ MESSAGE_QUEUE_CAPACITY ];
+ unsigned short m_front;
+ unsigned short m_back;
+ bool m_isEmpty : 1;
+ bool m_isFull : 1;
+
+
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+enum eHudOverlay
+{
+ // The following are ordered in precendence priority when more than
+ // one mission-related overlay is enabled (w/ the first one being
+ // the highest priority).
+ //
+ HUD_DAMAGE_METER,
+ HUD_PROXIMITY_METER,
+ HUD_COLLECTIBLES,
+ HUD_TIMER,
+ HUD_RACE_POSITION,
+ HUD_LAP_COUNTER,
+ HUD_TIMER_TEMP,
+ HUD_PAR_TIME,
+
+ NUM_HUD_MISSION_OVERLAYS, // not really an overlay
+
+ HUD_MISSION_COMPLETE,
+ HUD_ACTION_BUTTON,
+ HUD_MESSAGE,
+ HUD_MAP,
+
+ NUM_HUD_OVERLAYS
+};
+
+class CGuiScreenHud : public CGuiScreenMultiHud,
+ public EventListener
+{
+public:
+ CGuiScreenHud( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenHud();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ void Update( unsigned int elapsedTime );
+
+ inline bool IsActive() const;
+
+ // Turns Entire HUD On/Off
+ //
+ void SetVisible( bool isVisible );
+
+ // Set HUD Overlay Info
+ //
+ void SetTimerBlinkingInterval( int startTime, int endTime );
+ void SetParTime( int parTime );
+ void SetCollectibles( int numCollected, int numToCollect );
+ void SetRacePosition( int currentPosition, int numPositions );
+ void SetLap( int currentLap, int numLaps );
+ void SetDamageMeter( float value );
+ void SetProximityMeter( float value );
+ void SetHitAndRunMeter( float value );
+
+ // Show/Hide Text Message
+ //
+ void DisplayMessage( bool show, const int index = 0 );
+
+ // Show/Hide All Mission Overlays
+ //
+ void DisplayMissionOverlays( bool show );
+
+ enum eHudEventHandler
+ {
+ HUD_EVENT_HANDLER_CARD_COLLECTED,
+ HUD_EVENT_HANDLER_COIN_COLLECTED,
+ HUD_EVENT_HANDLER_MISSION_PROGRESS,
+ HUD_EVENT_HANDLER_MISSION_OBJECTIVE,
+ HUD_EVENT_HANDLER_COUNTDOWN,
+ HUD_EVENT_HANDLER_HIT_N_RUN,
+ HUD_EVENT_HANDLER_WASP_DESTROYED,
+ HUD_EVENT_HANDLER_ITEM_DROPPED,
+
+ NUM_HUD_EVENT_HANDLERS
+ };
+
+ HudEventHandler* GetEventHandler( eHudEventHandler eventHandler ) const;
+
+ // Implements EventListener
+ //
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ static void SetNumCoinsDisplay( Scrooby::Sprite* pSprite );
+ static void UpdateNumCoinsDisplay( int numCoins, bool show = true );
+ void AbortFade();
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+ #ifdef DEBUGWATCH
+ virtual const char* GetWatcherName() const;
+ #endif
+
+private:
+ static bool IsMissionOverlay( unsigned int overlayIndex );
+
+ void UpdateOverlays( unsigned int elapsedTime );
+ void UpdateEventHandlers( unsigned int elapsedTime );
+ void UpdateTimer( unsigned int elapsedTime );
+
+#ifdef SRR2_MOVABLE_HUD_MAP
+ void UpdateHudMapPosition( unsigned int elapsedTime );
+ void MoveHudMap( int x, int y );
+#endif
+
+ static const unsigned int BITMAP_TEXT_BUFFER_SIZE = 8;
+ static const int NUM_HIT_N_RUN_SECTORS = 8;
+
+ static const int MAX_NUM_COIN_DIGITS = 8;
+ static Scrooby::Sprite* s_numCoinsDisplay;
+
+ Scrooby::Group* m_overlays[ NUM_HUD_OVERLAYS ];
+ unsigned int m_elapsedTime[ NUM_HUD_OVERLAYS ];
+
+ Scrooby::Group* m_missionOverlays;
+ Scrooby::Sprite* m_missionFailedSprite;
+
+ Scrooby::Text* m_helpMessage;
+ Scrooby::Sprite* m_messageBox;
+ MessageQueue m_helpMessageQueue;
+
+#ifdef RAD_WIN32
+ Scrooby::Text* m_actionButton;
+ Scrooby::Text* m_actionLabel;
+#else
+ Scrooby::Sprite* m_actionButton;
+#endif
+
+ Scrooby::Sprite* m_missionComplete;
+
+ Scrooby::Sprite* m_timer;
+ tColour m_defaultTimerColour;
+ int m_timerBlinkingStartTime;
+ int m_timerBlinkingEndTime;
+
+ Scrooby::Sprite* m_parTime;
+
+ Scrooby::Sprite* m_collectibles;
+ int m_collectiblesUpdated;
+
+ Scrooby::Sprite* m_position;
+ Scrooby::Text* m_positionOrdinal;
+
+ Scrooby::Sprite* m_lap;
+ int m_lapUpdated;
+
+ ImageSlider m_damageMeter;
+ bool m_isSlidingDamageMeter;
+
+ ImageSlider m_proximityMeter;
+
+ bool m_isFadingInRadar : 1;
+ bool m_isFadingOutRadar : 1;
+
+ Scrooby::Sprite* m_hnrSectors[ NUM_HIT_N_RUN_SECTORS ];
+ Scrooby::Sprite* m_hnrLights;
+ Scrooby::Sprite* m_hnrMeter;
+ Scrooby::Sprite* m_hnrMeterBgd;
+ unsigned int m_hnrElapsedTime;
+
+ HudEventHandler* m_hudEventHandlers[ NUM_HUD_EVENT_HANDLERS ];
+
+ bool m_isFadingIn : 1;
+ bool m_isFadingOut : 1;
+ float m_elapsedFadeTime;
+
+ bool m_isBonusMissionJustCompleted : 1;
+
+ bool m_isAvatarOffRoad : 1;
+ unsigned int m_avatarOffRoadDurationTime;
+
+ GetOutOfCarCondition* m_pGetOutOfCarCondition;
+
+ float m_elapsedFgdFadeInTime;
+
+};
+
+inline bool CGuiScreenHud::IsActive() const
+{
+ // TC: if the start button was just pressed, we'll treat the screen
+ // as in-active, since it's about to transition out anyways
+ //
+ return ( m_state == GUI_WINDOW_STATE_RUNNING && !m_isStartButtonPressed );
+}
+
+inline HudEventHandler* CGuiScreenHud::GetEventHandler( eHudEventHandler eventHandler ) const
+{
+ return m_hudEventHandlers[ eventHandler ];
+}
+
+inline bool CGuiScreenHud::IsMissionOverlay( unsigned int overlayIndex )
+{
+ return( static_cast<int>( overlayIndex ) < NUM_HUD_MISSION_OVERLAYS );
+}
+
+#endif // GUISCREENHUD_H
diff --git a/game/code/presentation/gui/ingame/guiscreenhudmap.cpp b/game/code/presentation/gui/ingame/guiscreenhudmap.cpp
new file mode 100644
index 0000000..ebeb36c
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenhudmap.cpp
@@ -0,0 +1,279 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenHudMap
+//
+// Description: Implementation of the CGuiScreenHudMap class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guiscreenhudmap.h>
+#include <presentation/gui/utility/hudmap.h>
+
+#include <memory/srrmemory.h>
+
+#include <p3d/pointcamera.hpp>
+
+#include <raddebug.hpp> // Foundation
+#include <raddebugwatch.hpp>
+#include <page.h>
+#include <polygon.h>
+#include <pure3dobject.h>
+#include <screen.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+#ifdef DEBUGWATCH
+ static const char* HUD_MAP_WATCHER_NAMESPACE = "GUI System - HUD Map Screen";
+ float g_wCamPosY = 700.0f;
+ float g_wCamTargetX = 0.0f;
+ float g_wCamTargetZ = 0.0f;
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenHudMap::CGuiScreenHudMap
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenHudMap::CGuiScreenHudMap
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+:
+ CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_HUD_MAP ),
+ m_largeHudMap( NULL ),
+ m_posX( 0 ),
+ m_posY( 0 ),
+ m_width( 0 ),
+ m_height( 0 ),
+ m_camera( NULL )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenHudMap" );
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage;
+ pPage = m_pScroobyScreen->GetPage( "ViewMap" );
+ rAssert( pPage );
+
+ Scrooby::Polygon* mapBgd = pPage->GetPolygon( "MapBgd" );
+ rAssert( mapBgd );
+
+ // Get map position and size (based on background polygon)
+ //
+ mapBgd->GetVertexLocation( 0, m_posX, m_posY ); // assuming first vertex
+ // is bottom-left co-ord
+ mapBgd->GetBoundingBoxSize( m_width, m_height );
+
+ // Create an overhead camera
+ //
+ m_camera = new(GMA_LEVEL_HUD) tPointCamera;
+ rAssert( m_camera );
+ m_camera->AddRef();
+ m_camera->SetVUp( rmt::Vector( 1, 0, 0 ) );
+ m_camera->SetPosition( rmt::Vector( 0, 700, 0 ) );
+ m_camera->SetTarget( rmt::Vector( 0, 0, 0 ) );
+
+#ifdef DEBUGWATCH
+ radDbgWatchAddFloat( &g_wCamPosY,
+ "Camera Height (Y)",
+ HUD_MAP_WATCHER_NAMESPACE,
+ NULL,
+ NULL,
+ 100.0f,
+ 1000.0f );
+
+ radDbgWatchAddFloat( &g_wCamTargetX,
+ "Camera Target (X)",
+ HUD_MAP_WATCHER_NAMESPACE,
+ NULL,
+ NULL,
+ -1000.0f,
+ 1000.0f );
+
+ radDbgWatchAddFloat( &g_wCamTargetZ,
+ "Camera Target (Z)",
+ HUD_MAP_WATCHER_NAMESPACE,
+ NULL,
+ NULL,
+ -1000.0f,
+ 1000.0f );
+#endif
+MEMTRACK_POP_GROUP();
+}
+
+
+//===========================================================================
+// CGuiScreenHudMap::~CGuiScreenHudMap
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenHudMap::~CGuiScreenHudMap()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &g_wCamPosY );
+ radDbgWatchDelete( &g_wCamTargetX );
+ radDbgWatchDelete( &g_wCamTargetZ );
+#endif
+
+ if( m_camera != NULL )
+ {
+ m_camera->Release();
+ m_camera = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenHudMap::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHudMap::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+#ifdef DEBUGWATCH
+ rAssert( m_camera );
+ m_camera->SetPosition( rmt::Vector( g_wCamTargetX, g_wCamPosY, g_wCamTargetZ ) );
+ m_camera->SetTarget( rmt::Vector( g_wCamTargetX, 0, g_wCamTargetZ ) );
+#endif
+ rAssert( m_largeHudMap );
+ m_largeHudMap->Update( param1 );
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_START:
+ {
+ // resume game
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenHudMap::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHudMap::InitIntro()
+{
+ m_largeHudMap = GetCurrentHud()->GetHudMap( 0 );
+ rAssert( m_largeHudMap );
+
+ Scrooby::Pure3dObject* p3dMap = m_largeHudMap->GetPure3dObject();
+ rAssert( p3dMap );
+
+ p3dMap->SetPosition( m_posX, m_posY );
+ p3dMap->SetBoundingBoxSize( m_width, m_height );
+
+ p3dMap->SetCamera( m_camera );
+
+ m_largeHudMap->Update( 0 );
+}
+
+
+//===========================================================================
+// CGuiScreenHudMap::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHudMap::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenHudMap::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenHudMap::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/ingame/guiscreenhudmap.h b/game/code/presentation/gui/ingame/guiscreenhudmap.h
new file mode 100644
index 0000000..ea21d2b
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenhudmap.h
@@ -0,0 +1,59 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenHudMap
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENHUDMAP_H
+#define GUISCREENHUDMAP_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CHudMap;
+class tPointCamera;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenHudMap : public CGuiScreen
+{
+public:
+ CGuiScreenHudMap( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenHudMap();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ CHudMap* m_largeHudMap;
+ int m_posX;
+ int m_posY;
+ int m_width;
+ int m_height;
+
+ tPointCamera* m_camera;
+
+};
+
+#endif // GUISCREENHUDMAP_H
diff --git a/game/code/presentation/gui/ingame/guiscreeniriswipe.cpp b/game/code/presentation/gui/ingame/guiscreeniriswipe.cpp
new file mode 100644
index 0000000..8548f3a
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreeniriswipe.cpp
@@ -0,0 +1,366 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenIrisWipe
+//
+// Description: Implementation of the CGuiScreenIrisWipe class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/21 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreeniriswipe.h>
+#include <presentation/gui/utility/specialfx.h>
+
+#include <events/eventmanager.h>
+
+#include <screen.h>
+#include <page.h>
+#include <pure3dobject.h>
+#include <text.h>
+
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/utility.hpp>
+
+#include <raddebug.hpp> // Foundation
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+const float DEFAULT_REL_SPEED = 0.5f;
+static bool g_doNotOpenOnNextOutro = false;
+bool CGuiScreenIrisWipe::g_IsIrisClosed = false;
+
+const unsigned int MAX_IDLE_TIME_OF_BLACKNESS = 3000; // in msec
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenIrisWipe::CGuiScreenIrisWipe
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenIrisWipe::CGuiScreenIrisWipe
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_IRIS_WIPE ),
+ m_pIris( 0 ),
+ m_pMultiController( 0 ),
+ m_isIrisActive( false ),
+ m_loadingText( NULL ),
+ m_elapsedIdleTime( 0 ),
+ m_elapsedBlinkTime( 0 )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "3dIris" );
+ rAssert( pPage != NULL );
+
+ m_pIris = pPage->GetPure3dObject( "p3d_iris" );
+ rAssert( m_pIris );
+
+ // Have to find the multicontroller ourselves because for some stupid reason
+ // Scrooby doesn't make it accessable via the Pure3dObject until the first render.
+ //
+ m_pMultiController = p3d::find<tMultiController>( "IrisController" );
+ rAssert( m_pMultiController );
+
+ m_numFrames = m_pMultiController->GetNumFrames();
+
+ // get loading text
+ //
+ pPage = m_pScroobyScreen->GetPage( "LoadingText" );
+ if( pPage != NULL )
+ {
+ m_loadingText = pPage->GetText( "Loading" );
+ rAssert( m_loadingText != NULL );
+ m_loadingText->SetVisible( false ); // hide by default
+ }
+
+ m_relativeSpeed = DEFAULT_REL_SPEED;
+}
+
+
+//===========================================================================
+// CGuiScreenIrisWipe::~CGuiScreenIrisWipe
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenIrisWipe::~CGuiScreenIrisWipe()
+{
+}
+
+//===========================================================================
+// CGuiScreenIrisWipe::DoNotOpenOnNextOutro
+//===========================================================================
+// Description: tells the screen not to transition out next time
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenIrisWipe::DoNotOpenOnNextOutro()
+{
+ g_doNotOpenOnNextOutro = true;
+}
+
+//===========================================================================
+// CGuiScreenIrisWipe::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenIrisWipe::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ if( message == GUI_MSG_UPDATE )
+ {
+ m_elapsedIdleTime += param1;
+
+ if( m_elapsedIdleTime > MAX_IDLE_TIME_OF_BLACKNESS )
+ {
+ m_elapsedBlinkTime += param1;
+
+ if( m_loadingText != NULL )
+ {
+ // blink loading text if idling here on this screen to satisfy
+ // TRC/TCR requirements
+ //
+ const unsigned int BLINKING_PERIOD = 250;
+ bool isBlinked = GuiSFX::Blink( m_loadingText,
+ static_cast<float>( m_elapsedBlinkTime ),
+ static_cast<float>( BLINKING_PERIOD ) );
+ if( isBlinked )
+ {
+ m_elapsedBlinkTime %= BLINKING_PERIOD;
+ }
+ }
+ }
+ }
+ }
+ else if( m_state == GUI_WINDOW_STATE_INTRO )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ if( m_isIrisActive && m_pMultiController->LastFrameReached() )
+ {
+ m_isIrisActive = false;
+
+ m_numTransitionsPending--;
+ rAssert( m_numTransitionsPending == 0 );
+
+ GetEventManager()->TriggerEvent( EVENT_GUI_IRIS_WIPE_CLOSED );
+ rReleasePrintf( "CGuiScreenIrisWipe => EVENT_GUI_IRIS_WIPE_CLOSED.\n" );
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ else if( m_state == GUI_WINDOW_STATE_OUTRO )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ if( m_isIrisActive && m_pMultiController->LastFrameReached() )
+ {
+ m_isIrisActive = false;
+
+ m_numTransitionsPending--;
+ rAssert( m_numTransitionsPending == 0 );
+
+ GetEventManager()->TriggerEvent( EVENT_GUI_IRIS_WIPE_OPEN );
+ rReleasePrintf( "CGuiScreenIrisWipe => EVENT_GUI_IRIS_WIPE_OPEN.\n" );
+ }
+
+ break;
+ }
+ case GUI_MSG_WINDOW_EXIT:
+ {
+ // ignore multiple exit requests
+ //
+ return;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenIrisWipe::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenIrisWipe::InitIntro()
+{
+ if( m_loadingText != NULL )
+ {
+ // hide loading text
+ //
+ m_loadingText->SetVisible( false );
+ }
+
+ if( !m_isIrisActive )
+ {
+ m_isIrisActive = true;
+
+ g_IsIrisClosed = true;
+ m_pIris->SetVisible( true );
+ m_pMultiController->SetRelativeSpeed( m_relativeSpeed );
+
+ rAssertMsg( m_numTransitionsPending == 0, "Bad news if you hit this assert! Please go tell Tony." );
+ m_numTransitionsPending++;
+
+ m_pMultiController->Reset();
+ m_pMultiController->SetFrameRange( 0.0f, m_numFrames * 0.5f );
+ m_pMultiController->SetFrame( 0.0f );
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenIrisWipe::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenIrisWipe::InitRunning()
+{
+ m_elapsedIdleTime = 0;
+ m_elapsedBlinkTime = 0;
+}
+
+
+//===========================================================================
+// CGuiScreenIrisWipe::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenIrisWipe::InitOutro()
+{
+ if( m_loadingText != NULL )
+ {
+ // hide loading text
+ //
+ m_loadingText->SetVisible( false );
+ }
+
+ if( g_doNotOpenOnNextOutro )
+ {
+ g_doNotOpenOnNextOutro = false;
+ }
+ else
+ {
+ g_IsIrisClosed = false;
+ if( !m_isIrisActive )
+ {
+ m_isIrisActive = true;
+
+ rAssertMsg( m_numTransitionsPending == 0, "Bad news if you hit this assert! Please go tell Tony." );
+ m_numTransitionsPending++;
+
+ m_pMultiController->Reset();
+ m_pMultiController->SetFrameRange( m_numFrames * 0.5f, m_numFrames );
+ m_pMultiController->SetFrame( m_numFrames * 0.5f );
+
+ //Reset the relative speed to the default
+ m_relativeSpeed = DEFAULT_REL_SPEED;
+ }
+ }
+}
+
+//===========================================================================
+// CGuiScreenIrisWipe::IsIrisClosed
+//===========================================================================
+// Description: Lets you know if the iris is closed at the moment
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+bool CGuiScreenIrisWipe::IsIrisClosed()
+{
+ return g_IsIrisClosed;
+}
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/ingame/guiscreeniriswipe.h b/game/code/presentation/gui/ingame/guiscreeniriswipe.h
new file mode 100644
index 0000000..c9a24a4
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreeniriswipe.h
@@ -0,0 +1,70 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenIrisWipe
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/21 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENIRISWIPE_H
+#define GUISCREENIRISWIPE_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+namespace Scrooby
+{
+ class Pure3dObject;
+};
+class tMultiController;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenIrisWipe : public CGuiScreen
+{
+public:
+ CGuiScreenIrisWipe( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenIrisWipe();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ inline void SetRelativeSpeed( float speed ) { m_relativeSpeed = speed; };
+ static void DoNotOpenOnNextOutro();
+ static bool IsIrisClosed();
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ Scrooby::Pure3dObject* m_pIris;
+ tMultiController* m_pMultiController;
+ float m_numFrames;
+ float m_relativeSpeed;
+ static bool g_IsIrisClosed;
+
+ bool m_isIrisActive : 1;
+
+ Scrooby::Text* m_loadingText;
+ unsigned int m_elapsedIdleTime;
+ unsigned int m_elapsedBlinkTime;
+
+};
+
+#endif // GUISCREENIRISWIPE_H
diff --git a/game/code/presentation/gui/ingame/guiscreenletterbox.cpp b/game/code/presentation/gui/ingame/guiscreenletterbox.cpp
new file mode 100644
index 0000000..164b1c4
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenletterbox.cpp
@@ -0,0 +1,688 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLetterBox
+//
+// Description: Implementation of the CGuiScreenLetterBox class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/21 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenletterbox.h>
+
+#include <events/eventmanager.h>
+#include <input/inputmanager.h>
+#include <interiors/interiormanager.h>
+#include <mission/missionmanager.h>
+#include <mission/missionstage.h>
+#include <mission/objectives/missionobjective.h>
+
+#include <raddebug.hpp> // Foundation
+#include <group.h>
+#include <page.h>
+#include <pure3dobject.h>
+#include <screen.h>
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const float BAR_SLIDING_TIME = 500.0f; // in msec
+bool CGuiScreenLetterBox::m_enableReopen = false;
+bool CGuiScreenLetterBox::m_forceOpen = false;
+static bool g_SurpressNextSkipButton = false;
+GuiSFX::Translator g_TopIn( "Top In" );
+GuiSFX::Translator g_BottomIn( "Bottom In" );
+GuiSFX::Translator g_TopOut( "Top Out" );
+GuiSFX::Translator g_BottomOut( "Bottom Out" );
+GuiSFX::Translator g_TopClose( "Top Close" );
+GuiSFX::Translator g_BottomClose( "Outro bottom close" );
+GuiSFX::SendEvent g_ClosedEvent( "Outro closed event" );
+GuiSFX::Pause s_OutroPauseBetweenCloseAndIris( "Outro pause between close and open" );
+GuiSFX::IrisWipeOpen s_OutroIrisOpen( "Outro Iris Open" );
+bool CGuiScreenLetterBox::s_suppressAcceptCancelButtons = false;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenLetterBox::CGuiScreenLetterBox
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLetterBox::CGuiScreenLetterBox
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_LETTER_BOX ),
+ m_topBar( NULL ),
+ m_bottomBar( NULL ),
+ m_skipButton( NULL ),
+ m_skipLabel( NULL ),
+ m_elapsedTime( 0 ),
+ m_OutroPending( false )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ m_Page = m_pScroobyScreen->GetPage( "LetterBox" ); rAssert( m_Page != NULL );
+ m_IrisPage = m_pScroobyScreen->GetPage( "3dIris" ); rAssert( m_IrisPage != NULL );
+ m_Iris = m_IrisPage->GetPure3dObject( "p3d_iris" );
+
+ // get the top and bottom letterbox bars
+ //
+ m_topBar = m_Page->GetGroup( "TopBar" );
+ m_bottomBar = m_Page->GetGroup( "BottomBar" );
+
+ // get letter box buttons from 'LetterBoxButtons.pag'
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "LetterBoxButtons" );
+ rAssert( pPage != NULL );
+
+ m_skipButton = pPage->GetGroup( "Skip" );
+ rAssert( m_skipButton != NULL );
+
+ m_skipLabel = m_skipButton->GetGroup( "SkipLabel" );
+ m_buttonIcons[ BUTTON_ICON_ACCEPT ] = m_skipButton->GetGroup( "AcceptLabel" );
+ m_buttonIcons[ BUTTON_ICON_BACK ] = m_skipButton->GetGroup( "BackLabel" );
+
+ if( this->IsWideScreenDisplay() )
+ {
+ m_skipButton->ResetTransformation();
+ this->ApplyWideScreenCorrectionScale( m_skipButton );
+ }
+
+ AddTransition( g_TopIn );
+ AddTransition( g_BottomIn );
+ AddTransition( g_TopOut );
+ AddTransition( g_BottomOut );
+ AddTransition( g_TopClose );
+ AddTransition( g_BottomClose );
+ AddTransition( s_OutroIrisOpen );
+ AddTransition( s_OutroPauseBetweenCloseAndIris );
+ DoneAddingTransitions();
+
+ g_TopIn.SetDrawable( m_topBar );
+ g_BottomIn.SetDrawable( m_bottomBar );
+ g_TopOut.SetDrawable( m_topBar );
+ g_BottomOut.SetDrawable( m_bottomBar );
+ g_TopClose.SetDrawable( m_topBar );
+ g_BottomClose.SetDrawable( m_bottomBar );
+ g_ClosedEvent.SetEvent( EVENT_LETTERBOX_CLOSED );
+
+ SetIntroFromOpen();
+ g_TopOut.SetCoordsStart( 0, 240 - 70 );
+ g_TopOut.SetCoordsEnd( 0, 240 );
+ g_TopOut.SetTimeInterval( BAR_SLIDING_TIME );
+ g_BottomOut.SetCoordsStart( 0, -240 + 70 );
+ g_BottomOut.SetCoordsEnd( 0, -240 );
+ g_BottomOut.SetTimeInterval( BAR_SLIDING_TIME );
+ g_TopClose.SetCoordsStart( 0, 240 - 70 );
+ g_TopClose.SetCoordsEnd( 0, 0 );
+ g_TopClose.SetTimeInterval( BAR_SLIDING_TIME );
+ g_BottomClose.SetCoordsStart( 0, -240 + 70 );
+ g_BottomClose.SetCoordsEnd( 0, 0 );
+ g_BottomClose.SetTimeInterval( BAR_SLIDING_TIME );
+ s_OutroPauseBetweenCloseAndIris.SetTimeInterval( 500 );
+ s_OutroIrisShow.SetDrawable( m_Iris );
+ s_OutroLetterBoxHide.SetDrawable( m_Page );
+
+ g_BottomClose.SetNextTransition( s_OutroPauseBetweenCloseAndIris );
+ s_OutroPauseBetweenCloseAndIris.SetNextTransition( s_OutroIrisShow );
+ s_OutroIrisShow.SetNextTransition( s_OutroLetterBoxHide );
+ s_OutroLetterBoxHide.SetNextTransition( g_ClosedEvent );
+ g_ClosedEvent.SetNextTransition( s_OutroIrisOpen );
+}
+
+
+//===========================================================================
+// CGuiScreenLetterBox::~CGuiScreenLetterBox
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLetterBox::~CGuiScreenLetterBox()
+{
+}
+
+//===========================================================================
+// CGuiScreenLetterBox::CheckIfScreenShouldBeBlank
+//===========================================================================
+// Description: this is a huge hack for the one frame glitch you get when
+// switching screens sometimes
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLetterBox::CheckIfScreenShouldBeBlank()
+{
+ bool irisClosed = GetGameplayManager()->IsIrisClosed();
+ if( irisClosed )
+ {
+ m_topBar->ResetTransformation();
+ m_topBar->Translate( 0, 0 );
+ m_bottomBar->ResetTransformation();
+ m_bottomBar->Translate( 0, 0 );
+ }
+ else
+ {
+ m_topBar->ResetTransformation();
+ m_topBar->Translate( 0, 240 );
+ m_bottomBar->ResetTransformation();
+ m_bottomBar->Translate( 0, -240 );
+ }
+}
+
+//===========================================================================
+// CGuiScreenLetterBox::ForceOpen
+//===========================================================================
+// Description: forces the screen to open next time
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLetterBox::ForceOpen()
+{
+ m_forceOpen = true;
+}
+
+//===========================================================================
+// CGuiScreenLetterBox::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLetterBox::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( message == GUI_MSG_UPDATE )
+ {
+ ResetMovableObjects();
+ float deltaT = static_cast< float >( param1 );
+ UpdateTransitions( deltaT );
+
+#ifdef RAD_DEMO
+ // reset idle timer when we're in a conversation
+ //
+ GetGameplayManager()->ResetIdleTime();
+#endif
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ if( m_skipButton->IsVisible() )
+ {
+ if( this->IsButtonVisible( BUTTON_ICON_ACCEPT ) )
+ {
+ this->OnAccept();
+ }
+ else
+ {
+ this->OnSkip();
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ if( m_skipButton->IsVisible() && this->IsButtonVisible( BUTTON_ICON_BACK ) )
+ {
+ this->OnCancel();
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ else if( m_state == GUI_WINDOW_STATE_INTRO )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ if( g_TopIn.IsChainDone() )
+ {
+ --m_numTransitionsPending;
+ }
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+ else if( m_state == GUI_WINDOW_STATE_OUTRO )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ if( m_enableReopen )
+ {
+ }
+ else
+ {
+ if( g_BottomClose.IsChainDone() || g_BottomOut.IsChainDone() )
+ {
+ --m_numTransitionsPending;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+void
+CGuiScreenLetterBox::HandleEvent( EventEnum id, void* pEventData )
+{
+ rAssert( id == EVENT_CONVERSATION_DONE );
+
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_DONE_AND_FINISHED );
+}
+
+
+//===========================================================================
+// CGuiScreenLetterBox::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLetterBox::InitIntro()
+{
+ s_OutroIrisOpen.Reset();
+
+ m_Page->SetVisible( true );
+ m_Iris->SetVisible( false );
+ m_topBar->SetVisible( true );
+ m_bottomBar->SetVisible( true );
+ ResetTransitions();
+ m_topBar->Translate( 0, 240 );
+ m_bottomBar->Translate( 0, -240 );
+ g_TopIn.Activate();
+ g_BottomIn.Activate();
+ m_elapsedTime = 0;
+ m_numTransitionsPending++;
+ m_OutroPending = false;
+
+ //
+ // Is the screen already black from some sort of fade?
+ //
+ bool irisClosed = GetGameplayManager()->IsIrisClosed();
+ if( irisClosed )
+ {
+ SetIntroFromClosed();
+ }
+ else
+ {
+ SetIntroFromOpen();
+ }
+
+
+ rAssert( m_skipButton != NULL );
+ if( g_SurpressNextSkipButton )
+ {
+ m_skipButton->SetVisible( false );
+ }
+ else
+ {
+ m_skipButton->SetVisible( true );
+
+ MissionStage* currentStage = GetGameplayManager()->GetCurrentMission()->GetCurrentStage();
+ rAssert( currentStage != NULL );
+ bool showAcceptCancel = GetGameplayManager()->IsSundayDrive() &&
+ currentStage->IsMissionAbortAllowed() &&
+ !GetInteriorManager()->IsPlayingISMovieDialog();
+
+ if( s_suppressAcceptCancelButtons )
+ {
+ showAcceptCancel = false;
+ }
+
+ rAssert( m_skipLabel != NULL );
+ m_skipLabel->SetVisible( !showAcceptCancel );
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, showAcceptCancel );
+ this->SetButtonVisible( BUTTON_ICON_BACK, showAcceptCancel );
+ }
+
+ s_suppressAcceptCancelButtons = false; // reset this flag
+
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_DONE );
+
+ //
+ // Double check if we were doing a "fade in" beforehand, and abort it
+ //
+ bool fadeInProgress = GetGameplayManager()->FadeInProgress();
+ GetGameplayManager()->AbortFade();
+}
+
+
+//===========================================================================
+// CGuiScreenLetterBox::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLetterBox::InitRunning()
+{
+}
+
+//===========================================================================
+// CGuiScreenLetterBox::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLetterBox::InitOutro()
+{
+ if( m_OutroPending )
+ {
+ return;
+ }
+ m_OutroPending = true;
+ g_TopIn.Deactivate();
+ g_BottomIn.Deactivate();
+ bool sundayDrive = GetGameplayManager()->IsSundayDrive();
+ int numStages = GetGameplayManager()->GetCurrentMission()->GetNumStages();
+ int currentIndex = GetGameplayManager()->GetCurrentMission()->GetCurrentStageIndex();
+ Mission* mission = GetGameplayManager()->GetCurrentMission();
+ MissionStage* stage = mission->GetCurrentStage();
+ MissionObjective* obj = stage->GetObjective();
+ bool pattyAndSelma = obj->IsPattyAndSelmaDialog();
+ bool raceDialog = obj->IsRaceDialog();
+
+ g_BottomClose.SetNextTransition( NULL );
+
+ //
+ // Sometimes we close the letterbox, sometimes it just opens up. when does
+ // it do what? If we're going to a hud screen, then it closes first, if
+ // we're staying in gameplay, then it just opens up
+ //
+ if( m_forceOpen )
+ {
+ g_TopOut.Activate();
+ g_BottomOut.Activate();
+ m_forceOpen = false;
+ }
+ else
+ {
+ if( ( sundayDrive && (currentIndex >= numStages - 1) ) || pattyAndSelma || raceDialog )
+ {
+ g_TopClose.Activate();
+ g_BottomClose.Activate();
+ }
+ else
+ {
+ g_TopOut.Activate();
+ g_BottomOut.Activate();
+ }
+ }
+
+ rAssert( m_skipButton != NULL );
+ m_skipButton->SetVisible( false );
+
+ m_elapsedTime = 0;
+ m_numTransitionsPending++;
+ g_SurpressNextSkipButton = false;
+
+ GetEventManager()->RemoveListener( this, EVENT_CONVERSATION_DONE );
+}
+
+//===========================================================================
+// CGuiScreenLetterBox::SurpressSkipButton
+//===========================================================================
+// Description: Surpresses showing
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLetterBox::SurpressSkipButton()
+{
+ g_SurpressNextSkipButton = true;
+}
+
+//===========================================================================
+// CGuiScreenLetterBox::UnSurpressSkipButton
+//===========================================================================
+// Description: The skip button will be visible again
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLetterBox::UnSurpressSkipButton()
+{
+ g_SurpressNextSkipButton = false;
+}
+
+//===========================================================================
+// CGuiScreenLetterBox::OnAccept
+//===========================================================================
+// Description: If necessary, stop conversation. Then, start mission.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLetterBox::OnAccept()
+{
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_SELECT ); // sound effect
+
+ // skip conversation in case it's still playing
+ //
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_SKIP );
+/*
+ // send event to dialog objective to indicate that user has accepted
+ // the mission
+ //
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_DONE_AND_FINISHED );
+*/
+}
+
+//===========================================================================
+// CGuiScreenLetterBox::OnCancel
+//===========================================================================
+// Description: If necessary, stop conversation. Then, return to sunday
+// drive.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLetterBox::OnCancel()
+{
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_BACK ); // sound effect
+
+ // don't listen to this event on cancelling
+ //
+ GetEventManager()->RemoveListener( this, EVENT_CONVERSATION_DONE );
+
+ // skip conversation in case it's still playing
+ //
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_SKIP );
+
+ if( GetGameplayManager()->IsBonusMissionDesired() )
+ {
+ // cancel bonus mission
+ //
+ GetGameplayManager()->CancelBonusMission();
+ }
+ else
+ {
+ // go back a stage in sunday drive mode
+ //
+ GetGameplayManager()->GetCurrentMission()->PrevStage();
+ GetGameplayManager()->GetCurrentMission()->GetCurrentStage()->Start();
+ }
+
+ this->ForceOpen();
+
+ GetInputManager()->SetGameState(Input::ACTIVE_GAMEPLAY);
+ m_pParent->HandleMessage( GUI_MSG_RESUME_INGAME );
+}
+
+//===========================================================================
+// CGuiScreenLetterBox::OnSkip
+//===========================================================================
+// Description: Skip NIS conversation.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLetterBox::OnSkip()
+{
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_SELECT ); // sound effect
+
+ // skip conversation in case it's still playing
+ //
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_SKIP );
+}
+
+//===========================================================================
+// CGuiScreenLetterBox::SetIntroFromClosed
+//===========================================================================
+// Description: sets things up so the letterbox opens from the start
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLetterBox::SetIntroFromClosed()
+{
+ g_TopIn.SetCoordsStart( 0, 0 );
+ g_TopIn.SetCoordsEnd( 0, 240 - 70 );
+ g_TopIn.SetTimeInterval( BAR_SLIDING_TIME );
+ g_BottomIn.SetCoordsStart( 0, 0 );
+ g_BottomIn.SetCoordsEnd( 0, -240 + 70 );
+ g_BottomIn.SetTimeInterval( BAR_SLIDING_TIME );
+ m_topBar->ResetTransformation();
+ m_topBar->Translate( 0, 0 );
+ m_bottomBar->ResetTransformation();
+ m_bottomBar->Translate( 0, 0 );
+}
+
+//===========================================================================
+// CGuiScreenLetterBox::SetIntroFromOpen
+//===========================================================================
+// Description: sets things up so the letterbox closes a little from the start
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLetterBox::SetIntroFromOpen()
+{
+ g_TopIn.SetCoordsStart( 0, 240 );
+ g_TopIn.SetCoordsEnd( 0, 240 - 70 );
+ g_TopIn.SetTimeInterval( BAR_SLIDING_TIME );
+ g_BottomIn.SetCoordsStart( 0, -240 );
+ g_BottomIn.SetCoordsEnd( 0, -240 + 70 );
+ g_BottomIn.SetTimeInterval( BAR_SLIDING_TIME );
+}
+
+#ifdef DEBUGWATCH
+const char* CGuiScreenLetterBox::GetWatcherName() const
+{
+ return "CGuiScreenLetterBox";
+}
+#endif \ No newline at end of file
diff --git a/game/code/presentation/gui/ingame/guiscreenletterbox.h b/game/code/presentation/gui/ingame/guiscreenletterbox.h
new file mode 100644
index 0000000..f7ddc46
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenletterbox.h
@@ -0,0 +1,101 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLetterBox
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/21 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENLETTERBOX_H
+#define GUISCREENLETTERBOX_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/ingame/guiscreenhastransitions.h>
+#include <presentation/gui/utility/transitions.h>
+
+#include <events/eventlistener.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+namespace Scrooby
+{
+ class Group;
+}
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenLetterBox :
+ public CGuiScreen,
+ public CGuiScreenHasTransitions,
+ public EventListener
+{
+public:
+ CGuiScreenLetterBox( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenLetterBox();
+ static void ForceOpen();
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ static void SurpressSkipButton();
+ static void UnSurpressSkipButton();
+
+ static void SuppressAcceptCancelButtons( bool suppress = true );
+ void CheckIfScreenShouldBeBlank();
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+ void SetIntroFromClosed();
+ void SetIntroFromOpen();
+ #ifdef DEBUGWATCH
+ virtual const char* GetWatcherName() const;
+ #endif
+
+
+private:
+ void OnAccept();
+ void OnCancel();
+ void OnSkip();
+
+ Scrooby::Page* m_Page;
+ Scrooby::Page* m_IrisPage;
+ Scrooby::Group* m_topBar;
+ Scrooby::Group* m_bottomBar;
+ Scrooby::Group* m_skipButton;
+ Scrooby::Group* m_skipLabel;
+ Scrooby::Pure3dObject* m_Iris;
+
+ unsigned int m_elapsedTime;
+ static bool m_enableReopen;
+ static bool m_forceOpen;
+ bool m_OutroPending : 1;
+ GuiSFX::Pause s_OutroPauseBetweenCloseAndIris;
+ GuiSFX::Show s_OutroIrisShow;
+ GuiSFX::Hide s_OutroLetterBoxHide;
+ GuiSFX::IrisWipeOpen s_OutroIrisOpen;
+
+ static bool s_suppressAcceptCancelButtons;
+
+};
+
+inline void CGuiScreenLetterBox::SuppressAcceptCancelButtons( bool suppress )
+{
+ s_suppressAcceptCancelButtons = suppress;
+}
+
+#endif // GUISCREENLETTERBOX_H
diff --git a/game/code/presentation/gui/ingame/guiscreenlevelend.cpp b/game/code/presentation/gui/ingame/guiscreenlevelend.cpp
new file mode 100644
index 0000000..ff5a557
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenlevelend.cpp
@@ -0,0 +1,202 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLevelEnd
+//
+// Description: Implementation of the CGuiScreenLevelEnd class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenlevelend.h>
+
+#include <data/memcard/memorycardmanager.h>
+#include <events/eventmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+
+// Scrooby
+//
+#include <screen.h>
+#include <page.h>
+#include <text.h>
+
+// ATG
+//
+#include <raddebug.hpp>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenLevelEnd::CGuiScreenLevelEnd
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLevelEnd::CGuiScreenLevelEnd
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreenLevelStats( pScreen, pParent, GUI_SCREEN_ID_LEVEL_END )
+{
+}
+
+
+//===========================================================================
+// CGuiScreenLevelEnd::~CGuiScreenLevelEnd
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLevelEnd::~CGuiScreenLevelEnd()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenLevelEnd::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLevelEnd::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ m_pParent->HandleMessage( GUI_MSG_QUIT_INGAME_FOR_RELOAD,
+ GetGameplayManager()->GetCurrentLevelIndex() + 1,
+ 0 );
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_SELECT );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenLevelEnd::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLevelEnd::InitIntro()
+{
+ // set heading to "Level Complete"
+ //
+ rAssert( m_levelStatsHeading != NULL );
+ m_levelStatsHeading->SetIndex( 1 );
+
+ // show "use mission select" info text
+ //
+ if( m_useMissionSelect != NULL )
+ {
+ m_useMissionSelect->SetVisible( true );
+ }
+
+ // update current level stats
+ //
+ this->UpdateLevelStats();
+
+ // load memory card info (in case user decides to save before advancing
+ // to next level)
+ //
+ GetMemoryCardManager()->LoadMemcardInfo( dynamic_cast<IMemoryCardInfoLoadCallback*>( m_pParent ) );
+}
+
+
+//===========================================================================
+// CGuiScreenLevelEnd::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLevelEnd::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenLevelEnd::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLevelEnd::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/ingame/guiscreenlevelend.h b/game/code/presentation/gui/ingame/guiscreenlevelend.h
new file mode 100644
index 0000000..861b2e2
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenlevelend.h
@@ -0,0 +1,48 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLevelEnd
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENLEVELEND_H
+#define GUISCREENLEVELEND_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenlevelstats.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenLevelEnd : public CGuiScreenLevelStats
+{
+public:
+ CGuiScreenLevelEnd( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenLevelEnd();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+};
+
+#endif // GUISCREENLEVELEND_H
diff --git a/game/code/presentation/gui/ingame/guiscreenlevelstats.cpp b/game/code/presentation/gui/ingame/guiscreenlevelstats.cpp
new file mode 100644
index 0000000..13f7ae3
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenlevelstats.cpp
@@ -0,0 +1,270 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLevelStats
+//
+// Description: Implementation of the CGuiScreenLevelStats class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenlevelstats.h>
+
+#include <cards/cardgallery.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/rewards/rewardsmanager.h>
+#include <render/enums/renderenums.h>
+
+// Scrooby
+//
+#include <screen.h>
+#include <page.h>
+#include <group.h>
+#include <text.h>
+
+// ATG
+//
+#include <raddebug.hpp>
+
+#include <string.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenLevelStats::CGuiScreenLevelStats
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLevelStats::CGuiScreenLevelStats
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent,
+ eGuiWindowID windowID
+)
+: CGuiScreen( pScreen, pParent, windowID ),
+ m_levelStatsHeading( NULL ),
+ m_useMissionSelect( NULL )
+{
+ memset( m_levelStats, 0, sizeof( m_levelStats ) );
+
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "LevelStats" );
+ rAssert( pPage != NULL );
+
+ this->AutoScaleFrame( pPage );
+
+ m_levelStatsHeading = pPage->GetText( "LevelProgress" );
+ rAssert( m_levelStatsHeading != NULL );
+
+ Scrooby::Group* pGroup = pPage->GetGroup( "Stats" );
+ rAssert( pGroup != NULL );
+
+ m_levelStats[ STAT_STORY_MISSIONS ] = pGroup->GetText( "StoryMissions_Unlocked" );
+ m_levelStats[ STAT_BONUS_MISSIONS ] = pGroup->GetText( "BonusMissions_Unlocked" );
+ m_levelStats[ STAT_STREET_RACES ] = pGroup->GetText( "StreetRaces_Unlocked" );
+ m_levelStats[ STAT_CARDS ] = pGroup->GetText( "Cards_Unlocked" );
+ m_levelStats[ STAT_CLOTHING ] = pGroup->GetText( "Clothing_Unlocked" );
+ m_levelStats[ STAT_VEHICLES ] = pGroup->GetText( "Vehicles_Unlocked" );
+ m_levelStats[ STAT_WASPS ] = pGroup->GetText( "Wasps_Unlocked" );
+ m_levelStats[ STAT_GAGS ] = pGroup->GetText( "Gags_Unlocked" );
+ m_levelStats[ STAT_LEVEL_COMPLETE ] = pGroup->GetText( "LevelComplete_Value" );
+
+#ifdef RAD_DEBUG
+ for( int i = 0; i < NUM_LEVEL_STATS; i++ )
+ {
+ rAssert( m_levelStats[ i ] != NULL );
+ }
+#endif
+
+ m_useMissionSelect = pPage->GetText( "UseMissionSelect" );
+ if( m_useMissionSelect != NULL )
+ {
+ m_useMissionSelect->SetVisible( false ); // hide by default
+
+ m_useMissionSelect->SetTextMode( Scrooby::TEXT_WRAP );
+ m_useMissionSelect->SetDisplayOutline( true );
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenLevelStats::~CGuiScreenLevelStats
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenLevelStats::~CGuiScreenLevelStats()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenLevelStats::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLevelStats::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_START:
+ {
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenLevelStats::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLevelStats::InitIntro()
+{
+ // update current level stats
+ //
+ this->UpdateLevelStats();
+}
+
+
+//===========================================================================
+// CGuiScreenLevelStats::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLevelStats::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenLevelStats::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenLevelStats::InitOutro()
+{
+}
+
+
+void
+CGuiScreenLevelStats::UpdateLevelStats()
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ char buffer[ 32 ];
+ RenderEnums::LevelEnum currentLevel = GetGameplayManager()->GetCurrentLevelIndex();
+
+ sprintf( buffer, "%d / %d", GetCharacterSheetManager()->QueryNumMissionsCompleted( currentLevel ), 7 );
+ m_levelStats[ STAT_STORY_MISSIONS ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", GetCharacterSheetManager()->QueryBonusMissionCompleted( currentLevel ) ? 1 : 0, 1 );
+ m_levelStats[ STAT_BONUS_MISSIONS ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", GetCharacterSheetManager()->QueryNumStreetRacesCompleted( currentLevel ), 3 );
+ m_levelStats[ STAT_STREET_RACES ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", GetCardGallery()->GetCollectedCards( currentLevel )->m_numCards, 7 );
+ m_levelStats[ STAT_CARDS ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", GetCharacterSheetManager()->QueryNumSkinsUnlocked( currentLevel ), 3 );
+ m_levelStats[ STAT_CLOTHING ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", GetCharacterSheetManager()->QueryNumCarUnlocked( currentLevel ), 5 );
+ m_levelStats[ STAT_VEHICLES ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", GetCharacterSheetManager()->QueryNumWaspsDestroyed( currentLevel ),
+ GetRewardsManager()->GetTotalWasps( currentLevel ) );
+ m_levelStats[ STAT_WASPS ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%d / %d", GetCharacterSheetManager()->QueryNumGagsViewed( currentLevel ),
+ GetRewardsManager()->GetTotalGags( currentLevel ) );
+ m_levelStats[ STAT_GAGS ]->SetString( 0, buffer );
+
+ sprintf( buffer, "%.1f %%", GetCharacterSheetManager()->QueryPercentLevelCompleted( currentLevel ) );
+ m_levelStats[ STAT_LEVEL_COMPLETE ]->SetString( 0, buffer );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/ingame/guiscreenlevelstats.h b/game/code/presentation/gui/ingame/guiscreenlevelstats.h
new file mode 100644
index 0000000..8c1051d
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenlevelstats.h
@@ -0,0 +1,71 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenLevelStats
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENLEVELSTATS_H
+#define GUISCREENLEVELSTATS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenLevelStats : public CGuiScreen
+{
+public:
+ CGuiScreenLevelStats( Scrooby::Screen* pScreen, CGuiEntity* pParent,
+ eGuiWindowID windowID = GUI_SCREEN_ID_LEVEL_STATS );
+ virtual ~CGuiScreenLevelStats();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ void UpdateLevelStats();
+
+ enum eLevelStats
+ {
+ STAT_STORY_MISSIONS,
+ STAT_BONUS_MISSIONS,
+ STAT_STREET_RACES,
+ STAT_CARDS,
+ STAT_CLOTHING,
+ STAT_VEHICLES,
+ STAT_WASPS,
+ STAT_GAGS,
+ STAT_LEVEL_COMPLETE,
+
+ NUM_LEVEL_STATS
+ };
+
+ Scrooby::Text* m_levelStatsHeading;
+ Scrooby::Text* m_levelStats[ NUM_LEVEL_STATS ];
+
+ Scrooby::Text* m_useMissionSelect;
+
+};
+
+#endif // GUISCREENLEVELSTATS_H
diff --git a/game/code/presentation/gui/ingame/guiscreenmissionbase.cpp b/game/code/presentation/gui/ingame/guiscreenmissionbase.cpp
new file mode 100644
index 0000000..bfdcf48
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenmissionbase.cpp
@@ -0,0 +1,1419 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMissionLoad
+//
+// Description: Implementation of the CGuiScreenMissionSuccess class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/04/07 ian gipson Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <camera/animatedcam.h>
+#include <camera/supercammanager.h>
+#include <events/eventmanager.h>
+#include <gameflow/gameflow.h>
+#include <memory/classsizetracker.h>
+#include <mission/gameplaymanager.h>
+#include <mission/objectives/coinobjective.h>
+#include <mission/objectives/raceobjective.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/missionmanager.h>
+#include <p3d/imagefactory.hpp>
+#include <p3d/sprite.hpp>
+#include <presentation/gui/ingame/guiscreenmissionbase.h>
+#include <presentation/gui/ingame/guiscreenmissionselect.h>
+#include <presentation/gui/utility/transitions.h>
+#include <sound/soundmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/character/charactermanager.h>
+
+#include <ai/actor/actormanager.h>
+
+#include <group.h>
+#include <layer.h>
+#include <page.h>
+#include <polygon.h>
+#include <pure3dobject.h>
+#include <screen.h>
+#include <sprite.h>
+#include <text.h>
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+#define CLIP_PAUSE_TIME 300.0f
+#define CLIP_MOVE_TIME 300.0f
+
+
+//Chuck
+//Intialize the special case patty and selma screen pointer
+tSprite* CGuiScreenMissionBase::sp_PattyAndSelmaScreenPNG = NULL;
+
+char CGuiScreenMissionBase::s_AnimatedBitmapName[ 256 ] = "";
+char CGuiScreenMissionBase::s_AnimatedBitmapShortName[ 32 ] = "";
+tSprite* CGuiScreenMissionBase::s_AnimatedBitmapSprite = NULL;
+bool CGuiScreenMissionBase::s_BitmapLoadPending = false;
+
+static tColour g_BackgroundColorLeft( 255, 255, 255, 255 );
+static tColour g_BackgroundColorRight( 255, 255, 255, 255 );
+
+const tColour g_BackgroundBlue ( 59, 76, 129 );
+const tColour g_BackgroundRed ( 100, 6, 6 );
+const tColour g_BackgroundGreen( 7, 73, 30 );
+
+const tColour g_ColorNormalLeft ( 255, 255, 255, 255 );
+const tColour g_ColorNormalRight( 255, 255, 255, 255 );
+const tColour g_ColorBonusLeft ( 255, 255, 255, 255 );
+const tColour g_ColorBonusRight ( 255, 255, 255, 255 );
+const tColour g_ColorRaceLeft ( 255, 255, 255, 255 );
+const tColour g_ColorRaceRight ( 255, 255, 255, 255 );
+const tColour g_ColorWagerLeft ( 255, 255, 255, 255 );
+const tColour g_ColorWagerRight ( 255, 255, 255, 255 );
+
+GuiSFX::Dummy g_IntroStart( "IntroStart" );
+GuiSFX::Junction3 g_IntroJunction;
+GuiSFX::Show g_ForegroundShow( "ForeGroundShow" );
+GuiSFX::Show g_ClipLeftShow( "ClipLeftShow" );
+GuiSFX::PauseInFrames g_IntroPause( "Pause" );
+GuiSFX::Translator g_ScreenSlideIn( "ScreenSlideIn" );
+GuiSFX::Pause g_ClipLeftPause( "ClipLeftPause" );
+GuiSFX::Translator g_ClipLeftSlideOut( "ClipLeftSlideOut" );
+GuiSFX::Hide g_ClipLeftHide( "ClipLeftHide" );
+GuiSFX::Show g_BackgroundShow( "BackgroundShow" );
+GuiSFX::ColorChange g_DarkenPolyFade( "DarkenPolyFade" );
+
+GuiSFX::Dummy g_OutroStart( "OutroStart" );
+GuiSFX::Junction3 g_OutroJunction;
+GuiSFX::Translator g_OutroBottomOut( "OutroBottomOut" );
+GuiSFX::Translator g_OutroTopOut( "OutroTopOut" );
+GuiSFX::Show g_ClipRightShow( "ClipRightShow" );
+GuiSFX::Pause g_ClipRightPause( "ClipRightPause" );
+GuiSFX::Translator g_ClipRightSlideIn( "ClipRightSlideIn" );
+GuiSFX::Translator g_ScreenSlideOut( "ScreenSlideOut" );
+GuiSFX::Hide g_OutroHideEverything( "OutroHideEverything" );
+GuiSFX::Dummy g_OutroDone( "OutroDone" );
+
+GuiSFX::IrisWipeOpen g_IrisOpen( "IrisOpen" );
+GuiSFX::IrisWipeOpen g_IrisClose( "IrisClose" );
+
+GuiSFX::PulseScale g_TitlePulse( "TitlePulse" );
+
+const float VEHICLE_ODDS_HARD_THRESHOLD = 3.0f;
+const float VEHICLE_ODDS_MEDIUM_THRESHOLD = 2.0f;
+
+#ifdef RAD_WIN32
+const float MISSION_BITMAP_CORRECTION_SCALE = 0.67f;
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMissionSuccess::CGuiScreenMissionSuccess
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMissionBase::CGuiScreenMissionBase( Scrooby::Screen* pScreen, CGuiEntity* pParent, eGuiWindowID id ):
+ CGuiScreen( pScreen, pParent, id ),
+ m_missionTitle( NULL ),
+ m_loadCompleted( NULL ),
+ m_PlayAnimatedCamera( false ),
+ m_gamblingInfo( NULL ),
+ m_gamblingEntryFee( NULL ),
+ m_gamblingTimeToBeat( NULL ),
+ m_gamblingBestTime( NULL ),
+ m_gamblingVehicleOdds( NULL ),
+ m_gamblingPayout( NULL ),
+ m_ReadyToExitScreen( false )
+{
+ CLASSTRACKER_CREATE( CGuiScreenMissionBase );
+
+ unsigned char* leftChar = reinterpret_cast< unsigned char* >( &g_BackgroundColorLeft );
+ radDbgWatchAddUnsignedChar( leftChar + 2, "Red", "gui\\missionloading\\left" );
+ radDbgWatchAddUnsignedChar( leftChar + 1, "Green", "gui\\missionloading\\left" );
+ radDbgWatchAddUnsignedChar( leftChar + 0, "Blue", "gui\\missionloading\\left" );
+ //radDbgWatchAddUnsignedChar( leftChar + 3, "Alpha", "gui\\missionloading\\left" );
+
+ unsigned char* rightChar = reinterpret_cast< unsigned char* >( &g_BackgroundColorRight );
+ radDbgWatchAddUnsignedChar( rightChar + 2, "Red", "gui\\missionloading\\right" );
+ radDbgWatchAddUnsignedChar( rightChar + 1, "Green", "gui\\missionloading\\right" );
+ radDbgWatchAddUnsignedChar( rightChar + 0, "Blue", "gui\\missionloading\\right" );
+ //radDbgWatchAddUnsignedChar( rightChar + 3, "Alpha", "gui\\missionloading\\right" );
+
+
+
+
+ //
+ // Add All the transitions to the screen
+ //
+ AddTransition( g_DarkenPolyFade );
+ AddTransition( g_TitlePulse );
+ AddTransition( g_ScreenSlideIn );
+ AddTransition( g_ScreenSlideOut );
+ AddTransition( g_ClipLeftSlideOut );
+ AddTransition( g_ClipRightSlideIn );
+ AddTransition( g_OutroBottomOut );
+ AddTransition( g_OutroTopOut );
+ AddTransition( g_IntroPause );
+ AddTransition( g_ClipLeftPause );
+ AddTransition( g_ClipRightPause );
+ DoneAddingTransitions();
+
+ //
+ // Retrieve the Scrooby drawing elements.
+ //
+
+ //
+ // Iris wipe stuff
+ //
+ Scrooby::Page* irisPage;
+ irisPage = m_pScroobyScreen->GetPage( "3dIris" );
+ rAssert( irisPage != NULL );
+
+ m_Iris = irisPage->GetPure3dObject( "p3d_iris" );
+ rAssert( m_Iris != NULL );
+
+ m_MultiController = p3d::find< tMultiController >( "IrisController" );
+ rAssert( m_MultiController );
+ m_irisWipeNumFrames = m_MultiController->GetNumFrames();
+
+ //
+ // Functional page stuff
+ //
+
+ Scrooby::Layer* overlay;
+
+ //
+ // Get the elements out of scrooby
+ //
+ m_Page = m_pScroobyScreen->GetPage( "MissionLoad" ); rAssert( m_Page != NULL );
+ m_LetterboxPage = m_pScroobyScreen->GetPage( "LetterBox" ); rAssert( m_LetterboxPage != NULL );
+ m_FgLayer = m_Page-> GetGroup( "foreground" ); rAssert( m_FgLayer != NULL );
+ m_loadCompleted = m_Page->GetGroup( "LoadCompleted" ); rAssert( m_loadCompleted != NULL );
+ m_Line0 = m_FgLayer->GetText( "MissionInfo" ); rAssert( m_Line0 != NULL );
+ m_missionTitle = m_FgLayer->GetText( "MissionTitle" ); rAssert( m_missionTitle != NULL );
+ m_BgLayer = m_Page->GetLayer( "Background" ); rAssert( m_BgLayer != NULL );
+ m_missionStartBitmap = m_Page->GetSprite( "MissionStartBitmap" ); rAssert( m_missionStartBitmap != NULL);
+ overlay = m_Page->GetLayer( "Overlay" ); rAssert( overlay != NULL );
+ m_darkenPolys = overlay->GetGroup( "dark_polys" ); rAssert( m_darkenPolys != NULL );
+ m_darkenTop = m_LetterboxPage->GetGroup( "TopBar" ); rAssert( m_darkenTop != NULL );
+ m_darkenBottom = m_LetterboxPage->GetGroup( "BottomBar" ); rAssert( m_darkenBottom != NULL );
+ m_backgroundPoly = m_Page->GetPolygon( "background" ); rAssert( m_backgroundPoly!= NULL );
+ m_textOverlays = m_Page->GetGroup( "Text" ); rAssert( m_textOverlays != NULL );
+ m_Flag = m_Page->GetSprite( "flag" ); rAssert( m_Flag != NULL );
+ m_ClipLeftGroup = m_Page->GetGroup( "ClipLeftGroup"); rAssert( m_ClipLeftGroup != NULL );
+ m_ClipRightGroup= m_Page->GetGroup( "ClipRightGroup"); rAssert( m_ClipRightGroup!= NULL );
+ m_ClipLeft = m_Page->GetSprite( "clipLeft" ); rAssert( m_ClipLeft != NULL );
+ m_ClipRight = m_Page->GetSprite( "clipRight1"); rAssert( m_ClipRight != NULL );
+ m_ClipLeftArm = m_Page->GetSprite( "clipArmLeft1" ); rAssert( m_ClipLeftArm != NULL );
+ m_ClipRightArm = m_Page->GetSprite( "clipArmRight1" ); rAssert( m_ClipRightArm != NULL );
+ m_Foreground = m_Page->GetLayer( "Background" ); rAssert( m_Foreground != NULL );
+ m_LetterboxLayer=m_LetterboxPage->GetLayer( "Background" ); rAssert( m_LetterboxLayer!= NULL );
+
+ if( IsWideScreenDisplay() )
+ {
+ m_Page->ResetTransformation();
+ ApplyWideScreenCorrectionScale( m_Page );
+ }
+
+
+ m_buttonIcons[ BUTTON_ICON_ACCEPT ] = m_loadCompleted->GetGroup( "Continue" );
+ m_buttonIcons[ BUTTON_ICON_BACK ] = m_loadCompleted->GetGroup( "Abort" );
+
+ //
+ // add some scrooby elements to the watcher
+ //
+ const char* screenName = GetWatcherName();
+
+ #ifdef DEBUGWATCH
+ m_missionStartBitmap-> WatchAll( screenName );
+ m_missionTitle-> WatchAll( screenName );
+ m_Line0-> WatchAll( screenName );
+ m_LetterboxPage-> WatchAll( screenName );
+ m_LetterboxLayer-> WatchAll( screenName );
+ m_darkenTop-> WatchAll( screenName );
+ m_darkenBottom-> WatchAll( screenName );
+ m_Flag-> WatchAll( screenName );
+ m_ClipLeft-> WatchAll( screenName );
+ m_ClipRight-> WatchAll( screenName );
+ m_ClipLeftArm-> WatchAll( screenName );
+ m_ClipRightArm-> WatchAll( screenName );
+ m_ClipLeftGroup-> WatchAll( screenName );
+ m_ClipRightGroup-> WatchAll( screenName );
+ #endif
+
+ this->SetFadingEnabled( false );
+ m_ClipRight->Scale( -1.0f, 1.0f, 1.0f );
+
+ // Get gambling info group and text
+ //
+ m_gamblingInfo = m_Page->GetGroup( "GamblingInfo" );
+ rAssert( m_gamblingInfo != NULL );
+
+ m_gamblingEntryFee = m_gamblingInfo->GetText( "EntryFee_Value" );
+ m_gamblingTimeToBeat = m_gamblingInfo->GetText( "TimeToBeat_Value" );
+ m_gamblingBestTime = m_gamblingInfo->GetText( "BestTime_Value" );
+ m_gamblingVehicleOdds = m_gamblingInfo->GetText( "VehicleOdds_Value" );
+ m_gamblingPayout = m_gamblingInfo->GetText( "Payout_Value" );
+
+ g_BackgroundColorLeft = m_backgroundPoly->GetVertexColour( 0 );
+ g_BackgroundColorRight = m_backgroundPoly->GetVertexColour( 2 );
+
+ m_Flag->ResizeToBoundingBox();
+
+ //
+ // Set Up Transitions
+ //
+
+ // Continuous
+ g_TitlePulse.SetDrawable( m_textOverlays );
+ g_TitlePulse.Activate();
+ g_TitlePulse.SetAmplitude( 0.05f );
+ g_TitlePulse.SetFrequency( 2.0f );
+ WATCH( g_TitlePulse, GetWatcherName() );
+
+ // Intro
+ WATCH( g_IntroStart, GetWatcherName() );
+
+ g_ForegroundShow.SetDrawable( m_Foreground );
+
+ g_ClipLeftShow.SetDrawable( m_ClipLeftGroup );
+
+ g_IntroPause.SetNumberOfFrames( 1 );
+
+ g_ScreenSlideIn.SetDrawable( m_Foreground );
+ g_ScreenSlideIn.SetStartOffscreenLeft( m_Foreground );
+ g_ScreenSlideIn.SetTimeInterval( 400.0f );
+ WATCH( g_ScreenSlideIn, GetWatcherName() );
+
+ g_ClipLeftPause.SetTimeInterval( CLIP_PAUSE_TIME );
+ WATCH( g_ClipLeftPause, GetWatcherName() );
+
+ g_ClipLeftSlideOut.SetDrawable( m_ClipLeftGroup );
+ g_ClipLeftSlideOut.SetEndOffscreenLeft( m_ClipLeftGroup );
+ g_ClipLeftSlideOut.SetTimeInterval( CLIP_MOVE_TIME );
+ WATCH( g_ClipLeftSlideOut, GetWatcherName() );
+
+ g_ClipLeftHide.SetDrawable( m_ClipLeftGroup );
+
+ // Outro
+ WATCH( g_OutroStart, GetWatcherName() );
+
+ g_ClipRightShow.SetDrawable( m_ClipRightGroup );
+
+ g_OutroTopOut.SetDrawable( m_darkenTop );
+ g_OutroTopOut.SetEndOffscreenTop( m_darkenTop );
+ g_OutroTopOut.SetTimeInterval( 1000.0f );
+ WATCH( g_OutroTopOut, GetWatcherName() );
+
+ g_OutroBottomOut.SetDrawable( m_darkenBottom );
+ g_OutroBottomOut.SetEndOffscreenBottom( m_darkenBottom );
+ g_OutroBottomOut.SetTimeInterval( 1000.0f );
+ WATCH( g_OutroBottomOut, GetWatcherName() );
+
+ g_ClipRightSlideIn.SetDrawable( m_ClipRightGroup );
+ g_ClipRightSlideIn.SetStartOffscreenRight( m_ClipRightGroup );
+ g_ClipRightSlideIn.SetTimeInterval( CLIP_MOVE_TIME );
+ WATCH( g_ClipRightSlideIn, GetWatcherName() );
+
+ g_ClipRightPause.SetTimeInterval( CLIP_PAUSE_TIME );
+ WATCH( g_ClipRightPause, GetWatcherName() );
+
+ g_ScreenSlideOut.SetDrawable( m_Foreground );
+ g_ScreenSlideOut.SetEndOffscreenRight( m_Foreground );
+ g_ScreenSlideOut.SetTimeInterval( 400.0f );
+ WATCH( g_ScreenSlideOut, GetWatcherName() );
+
+ g_BackgroundShow.SetDrawable( m_BgLayer );
+
+ g_DarkenPolyFade.SetDrawable( m_darkenPolys );
+ g_DarkenPolyFade.SetStartColour( tColour( 255, 255, 255, 255 ) );
+ g_DarkenPolyFade.SetEndColour( tColour( 255, 255, 255, 0 ) );
+ g_DarkenPolyFade.SetTimeInterval( 100.0f );
+
+ g_OutroHideEverything.SetDrawable( m_Foreground );
+
+ m_missionStartBitmap->SetRawSprite( NULL );
+#ifdef RAD_WIN32
+ m_missionStartBitmap->ResetTransformation();
+ m_missionStartBitmap->ScaleAboutCenter( MISSION_BITMAP_CORRECTION_SCALE );
+ m_missionStartBitmap->Translate( -71, -32 ); // These are trial & error numbers that hopefully work.
+#endif
+
+ // text wrap mission info title and description
+ //
+ m_missionTitle->SetTextMode( Scrooby::TEXT_WRAP );
+ m_Line0->SetTextMode( Scrooby::TEXT_WRAP );
+
+ //
+ // Set up transitions for lines of text
+ //
+
+ //
+ // Transition sequencing
+ //
+ g_IntroStart. SetNextTransition( g_IntroJunction );
+ g_IntroJunction. SetNextTransition( 0, g_IntroPause );
+ g_IntroJunction. SetNextTransition( 1, g_ClipLeftShow );
+ g_IntroJunction. SetNextTransition( 2, g_ScreenSlideIn );
+ g_ForegroundShow. SetNextTransition( NULL );
+ g_ClipLeftShow. SetNextTransition( NULL );
+ g_IntroPause. SetNextTransition( g_ForegroundShow );
+ g_ScreenSlideIn. SetNextTransition( g_ClipLeftPause );
+ g_ClipLeftPause. SetNextTransition( g_ClipLeftSlideOut );
+ g_ClipLeftSlideOut. SetNextTransition( g_ClipLeftHide );
+ g_ClipLeftHide. SetNextTransition( g_BackgroundShow );
+ g_BackgroundShow. SetNextTransition( g_DarkenPolyFade );
+ g_DarkenPolyFade. SetNextTransition( NULL );
+
+ g_OutroStart. SetNextTransition( g_OutroJunction );
+ g_OutroJunction. SetNextTransition( 0, g_ClipRightShow );
+ g_OutroJunction. SetNextTransition( 1, g_OutroBottomOut );
+ g_OutroJunction. SetNextTransition( 2, g_OutroTopOut );
+ g_OutroBottomOut. SetNextTransition( NULL );
+ g_OutroTopOut. SetNextTransition( NULL );
+ g_ClipRightShow. SetNextTransition( g_ClipRightSlideIn );
+ g_ClipRightSlideIn. SetNextTransition( g_ClipRightPause );
+ g_ClipRightPause. SetNextTransition( g_ScreenSlideOut );
+ g_ScreenSlideOut. SetNextTransition( g_OutroHideEverything );
+ g_OutroHideEverything. SetNextTransition( g_OutroDone );
+ g_OutroDone. SetNextTransition( NULL );
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::~CGuiScreenMissionBase
+//===========================================================================
+// Description: destructor
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMissionBase::~CGuiScreenMissionBase()
+{
+ CLASSTRACKER_DESTROY( CGuiScreenMissionBase );
+ if( s_AnimatedBitmapSprite)
+ {
+ tRefCounted::Release(s_AnimatedBitmapSprite);
+ }
+
+ //Chuck release the special patty and selma screen if we are still holding on to it.
+ if(sp_PattyAndSelmaScreenPNG)
+ {
+ tRefCounted::Release(sp_PattyAndSelmaScreenPNG);
+ sp_PattyAndSelmaScreenPNG = NULL;
+ }
+ s_BitmapLoadPending = false;
+ p3d::inventory->RemoveSectionElements( tEntity::MakeUID( "DynamicHud" ) );
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::ClearBitmap
+//===========================================================================
+// Description: clears the currently loaded animated bitmap
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: pointer to the abort bitmap group
+//
+//===========================================================================
+void CGuiScreenMissionBase::ClearBitmap()
+{
+ s_AnimatedBitmapSprite = NULL;
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::GetAbortBitmap
+//===========================================================================
+// Description: Allows access to the stored group pointer
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: pointer to the abort bitmap group
+//
+//===========================================================================
+Scrooby::Group* CGuiScreenMissionBase::GetAbortBitmap()
+{
+ return m_buttonIcons[ BUTTON_ICON_BACK ];
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::GetBitmapName
+//===========================================================================
+// Description: Allows access to the name of the mission briefing pic
+//
+// Constraints: None.
+//
+// Parameters: buffer - filled in with the name
+//
+// Return: pointer to the flag
+//
+//===========================================================================
+void CGuiScreenMissionBase::GetBitmapName( char* buffer )
+{
+ if( buffer != NULL )
+ {
+ ::strcpy( buffer, s_AnimatedBitmapName );
+ }
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::GetFlag
+//===========================================================================
+// Description: Allows access to the flag bitmap
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: pointer to the flag
+//
+//===========================================================================
+Scrooby::Drawable* CGuiScreenMissionBase::GetFlag()
+{
+ return m_Flag;
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::GetLoadCompletedGroup
+//===========================================================================
+// Description: Allows access to the stored group pointer
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: pointer to the pace
+//
+//===========================================================================
+Scrooby::Group* CGuiScreenMissionBase::GetLoadCompletedGroup()
+{
+ return m_loadCompleted;
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::GetMissionInfoText
+//===========================================================================
+// Description: Allows access to the stored text pointer
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: pointer to the text
+//
+//===========================================================================
+Scrooby::Text* CGuiScreenMissionBase::GetMissionInfoText()
+{
+ return m_Line0;
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::GetMissionStartBitmap
+//===========================================================================
+// Description: Allows access to the stored mission start bitmap
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: pointer to the drawable
+//
+//===========================================================================
+Scrooby::BoundedDrawable* CGuiScreenMissionBase::GetMissionStartBitmap()
+{
+ return m_missionStartBitmap;
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::GetPage
+//===========================================================================
+// Description: Allows access to the stored page pointer
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: pointer to the pace
+//
+//===========================================================================
+Scrooby::Page* CGuiScreenMissionBase::GetPage()
+{
+ return m_Page;
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::GetPage
+//===========================================================================
+// Description: Allows access to the stored page pointer
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: pointer to the pace
+//
+//===========================================================================
+Scrooby::Text* CGuiScreenMissionBase::GetTitleText()
+{
+ return m_missionTitle;
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::GetWatcherName
+//===========================================================================
+// Description: Allows access to the stored page pointer
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: pointer to the pace
+//
+//===========================================================================
+const char* CGuiScreenMissionBase::GetWatcherName() const
+{
+ return "GuiScreenMissionBase";
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( message == GUI_MSG_UPDATE )
+ {
+ float deltaT = static_cast< float >( param1 );
+ ResetMovableObjects();
+ m_ClipLeftArm->ResizeToBoundingBox();
+ m_ClipRightArm->ResizeToBoundingBox();
+ m_ClipLeftGroup->ResetTransformation();
+ UpdateTransitions( deltaT );
+ m_FgLayer->SetColour( tColour( 255, 255, 255, 255 ) );
+ m_missionStartBitmap->SetColour( tColour( 255, 255, 255, 255 ) );
+ m_backgroundPoly->SetVertexColour( 0, g_BackgroundColorLeft );
+ m_backgroundPoly->SetVertexColour( 1, g_BackgroundColorLeft );
+ m_backgroundPoly->SetVertexColour( 2, g_BackgroundColorRight );
+ m_backgroundPoly->SetVertexColour( 3, g_BackgroundColorRight );
+ }
+
+ if( message == GUI_MSG_WINDOW_EXIT )
+ {
+ }
+
+ if( m_state == GUI_WINDOW_STATE_INTRO )
+ {
+ if( message == GUI_MSG_UPDATE )
+ {
+// --m_numTransitionsPending; //get us out of the intro state
+ float deltaT = static_cast< float >( param1 );
+ UpdateAnimatedBitmap( deltaT );
+ }
+ }
+
+ if( m_state == GUI_WINDOW_STATE_OUTRO )
+ {
+ if( message == GUI_MSG_UPDATE )
+ {
+ if( g_OutroDone.IsDone() )
+ {
+ --m_numTransitionsPending;
+ OutroDone();
+ }
+ }
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ UpdateAnimatedBitmap( static_cast< float >( param1 ) );
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ };
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::InitIntro()
+{
+ m_ReadyToExitScreen = false;
+ if( ( !s_BitmapLoadPending ) && ( s_AnimatedBitmapSprite == NULL) )
+ {
+ ReplaceBitmap();
+ }
+
+ //
+ // Need to reset all the transitions
+ //
+ this->SetButtonVisible( BUTTON_ICON_BACK, true );
+ m_LetterboxPage->SetVisible( true );
+ m_Page-> SetVisible( true );
+ m_Line0-> SetVisible( false );
+ m_BgLayer-> SetVisible( true );
+ m_FgLayer-> SetVisible( true );
+ m_Iris-> SetVisible( false );
+ m_missionTitle-> SetVisible( false );
+ m_Flag-> SetVisible( false );
+ m_ClipLeftGroup->SetVisible( true );
+ m_ClipRightGroup->SetVisible( false );
+ m_Foreground-> SetVisible( false );
+ m_darkenPolys-> SetColour( tColour( 255, 255, 255, 255 ) );
+ m_darkenBottom-> ResetTransformation();
+ m_darkenTop-> ResetTransformation();
+ m_ClipLeft-> ResetTransformation();
+
+
+ m_MultiController->Reset();
+ m_MultiController->SetRelativeSpeed( 1.0f );
+ m_MultiController->SetFrameRange( 0.0f, m_irisWipeNumFrames );
+ m_MultiController->SetFrame( 0.0f );
+ unsigned int size = m_textOverlays->Size();
+ unsigned int i;
+ for( i = 0; i < size; ++i )
+ {
+ Scrooby::Drawable* drawable = m_textOverlays->GetChildDrawable( i );
+ drawable->SetVisible( false );
+ }
+
+ ResetTransitions();
+
+ g_IntroStart.Activate();
+ g_OutroStart.DeactivateChain();
+
+ const int currentLevelIndex = GetGameplayManager()->GetCurrentLevelIndex();
+ int currentMissionIndex = GetGameplayManager()->GetCurrentMissionIndex();
+
+ int currentMissionNum = GetGameplayManager()->GetCurrentMissionNum();
+ if( currentMissionNum >= GameplayManager::MAX_MISSIONS )
+ {
+ // current mission must be either a street race or a bonus mission
+ //
+ currentMissionIndex = currentMissionNum - GameplayManager::MAX_MISSIONS +
+ MAX_NUM_REGULAR_MISSIONS;
+ }
+ else
+ {
+ // special case for level 1 due to tutorial mission
+ //
+ if( currentLevelIndex == RenderEnums::L1 )
+ {
+ currentMissionIndex--;
+ }
+
+#ifdef RAD_E3
+ // TC: *** quick and dirty E3 hack!
+ //
+ if( currentLevelIndex == RenderEnums::L2 )
+ {
+ currentMissionIndex = RenderEnums::M5;
+ }
+#endif
+ }
+
+ int textIndex = currentLevelIndex * MAX_NUM_MISSIONS_PER_LEVEL + currentMissionIndex;
+ int missionNumber = textIndex % MAX_NUM_MISSIONS_PER_LEVEL ;
+
+ // special case for level 1 due to tutorial mission
+ //
+ if( currentLevelIndex == 0 )
+ {
+ if( textIndex < 0 ) // meaning it must be the tutorial mission
+ {
+ const int NUM_LEVELS = 7;
+ textIndex = MAX_NUM_MISSIONS_PER_LEVEL * NUM_LEVELS;
+ missionNumber = MAX_NUM_MISSIONS_PER_LEVEL;
+ }
+ }
+
+ m_missionTitle->SetIndex( textIndex );
+ m_Line0->SetIndex( textIndex );
+
+ //
+ // Set the Background Color of the screen
+ //
+ bool bonusMission = GetGameplayManager()->GetCurrentMission()->IsBonusMission();
+ bool raceMission = GetGameplayManager()->GetCurrentMission()->IsRaceMission();
+ bool wagerMission = GetGameplayManager()->GetCurrentMission()->IsWagerMission();
+
+ if( wagerMission )
+ {
+ m_backgroundPoly->SetColour( g_BackgroundGreen );
+ g_BackgroundColorLeft = g_ColorWagerLeft;
+ g_BackgroundColorRight = g_ColorWagerRight;
+ }
+ else if( bonusMission )
+ {
+ m_backgroundPoly->SetColour( g_BackgroundRed );
+ g_BackgroundColorLeft = g_ColorBonusLeft;
+ g_BackgroundColorRight = g_ColorBonusRight;
+ }
+ else if( raceMission )
+ {
+ m_backgroundPoly->SetColour( g_BackgroundBlue );
+ g_BackgroundColorLeft = g_ColorRaceLeft;
+ g_BackgroundColorRight = g_ColorRaceRight;
+ }
+ else
+ {
+ m_backgroundPoly->SetColour( g_BackgroundBlue );
+ g_BackgroundColorLeft = g_ColorNormalLeft;
+ g_BackgroundColorRight = g_ColorNormalRight;
+ }
+
+ if( wagerMission || bonusMission || raceMission )
+ {
+ s_AnimatedBitmapSprite = NULL;
+ //SetBitmapName( NULL );
+ }
+
+ rAssert( m_gamblingInfo != NULL );
+ m_gamblingInfo->SetVisible( false ); // hide by default
+
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true ); // show by default
+
+ //
+ // Inform the sound manager that it's time to turn the sound down a bit
+ //
+ GetSoundManager()->OnMissionBriefingStart();
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::InitIntroWagerMission
+//===========================================================================
+// Description: sets up stuff for the wager mission screen
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::InitIntroWagerMission()
+{
+ int textIndex = m_Line0->GetNumOfStrings() - 2;
+ int entryFee = 0; // in coins
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ rAssert( currentMission != NULL );
+ MissionStage* ms = currentMission->GetStage( 0 );
+ rAssert( ms != NULL );
+ CoinObjective* coinObjective = dynamic_cast<CoinObjective*>( ms->GetObjective() );
+ rAssertMsg( coinObjective != NULL, "No coin objective in first stage!" );
+ entryFee = coinObjective->GetCoinAmount();
+
+ if( entryFee > GetCoinManager()->GetBankValue() )
+ {
+ //
+ // Disable accept button
+ //
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, false );
+
+ //
+ // Change the info text to the "You need (x) coins to continue" message
+ //
+ m_Line0->SetIndex( textIndex );
+ UnicodeString us = m_Line0->GetString( textIndex );
+ char number[ 256 ] = "";
+ sprintf( number, "%d", entryFee );
+ us.Replace( "%D", number );
+
+ m_Line0->SetString( textIndex + 1, us );
+ m_Line0->SetIndex ( textIndex + 1 );
+ }
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::InitOutro()
+{
+ //This is a hack to get the stage setup before we start showing the world after
+ //the mission briefing screen. This simulates a one frame step that sets up and
+ //"starts" the mission before we leave the pause context. NOTE: There may be
+ //seom other managers that need to be run here.
+ GetMissionManager()->Update( 16 );
+ GetSuperCamManager()->GetSCC( 0 )->NoTransition();
+ GetSuperCamManager()->Update( 16, true );
+ GetCharacterManager()->PreSimUpdate( 0.0001f );
+ GetCharacterManager()->PreSubstepUpdate( 0.0001f );
+ GetCharacterManager()->Update( 0.0001f );
+ GetCharacterManager()->PostSubstepUpdate( 0.0001f );
+ GetCharacterManager()->PostSimUpdate( 0.0001f );
+
+ g_OutroDone.Reset();
+ g_OutroStart.Activate();
+ ++m_numTransitionsPending;
+
+ //
+ // Turn the sound back up
+ //
+ GetSoundManager()->OnMissionBriefingEnd();
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::InitRunning()
+{
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::IsCurrentBitmap
+//===========================================================================
+// Description: deetermines if the current bitmap loaded corresponds to this
+// name
+//
+// Constraints: None.
+//
+// Parameters: name - the string name to check.
+//
+// Return: N/A.
+//
+//===========================================================================
+bool CGuiScreenMissionBase::IsCurrentBitmap( const char* name )
+{
+ bool returnMe = ( strcmp( name, s_AnimatedBitmapName ) == 0 );
+ return returnMe;
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::OutroDone
+//===========================================================================
+// Description: called when the outro is done and we're ready to leave this
+// screen
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::OutroDone()
+{
+ UnloadBitmap();
+
+ //
+ // If this is a race, we need to play a camera
+ //
+ if( m_PlayAnimatedCamera )
+ {
+ //
+ // If it's a race mission, don't allow skipping of the animated cam
+ //
+ bool raceMission = GetGameplayManager()->GetCurrentMission()->IsRaceMission();
+ if( raceMission )
+ {
+ AnimatedCam::AllowSkipping( false );
+ AnimatedCam::CheckPendingCameraSwitch();
+
+ //
+ // we need to go to the gameplay context. This is so not cool it almost makes me sick - IAN
+ //
+ GetGameFlow()->SetContext( CONTEXT_GAMEPLAY );
+ }
+ m_PlayAnimatedCamera = false;
+ }
+ else
+ {
+ AnimatedCam::SetCamera( "" );
+ AnimatedCam::SetMulticontroller( "" );
+ }
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::RemoveAnimatedBitmap
+//===========================================================================
+// Description: gets rid of the animated bitmap
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::RemoveAnimatedBitmap()
+{
+ if( s_AnimatedBitmapSprite != NULL )
+ {
+ s_AnimatedBitmapSprite->Release();
+ s_AnimatedBitmapSprite = NULL;
+ }
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::ReplaceBitmap
+//===========================================================================
+// Description: replaces the bitmap with a sprite from a file
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::ReplaceBitmap()
+{
+ p3d::pddi->DrawSync();
+ p3d::inventory->RemoveSectionElements( tEntity::MakeUID( "DynamicHud" ) );
+ RemoveAnimatedBitmap();
+
+ rWarning( s_BitmapLoadPending != true );
+ s_BitmapLoadPending = true;
+ rAssert( strlen( s_AnimatedBitmapName ) != 0 );
+ if( strlen( s_AnimatedBitmapName ) != 0 )
+ {
+ tRefCounted::Release( s_AnimatedBitmapSprite );
+ tFileHandler* fileHandler = p3d::loadManager->GetHandler( "png" );
+ rAssert( fileHandler != NULL );
+ tImageHandler* pngHandler = dynamic_cast< tImageHandler* >( fileHandler );
+ rAssert( pngHandler != NULL );
+ pngHandler->SetLoadType( tImageHandler::SPRITE );
+ LoadingManager::GetInstance()->AddRequest
+ (
+ FILEHANDLER_PURE3D,
+ s_AnimatedBitmapName,
+ GMA_LEVEL_OTHER,
+ "DynamicHud"
+ );
+ }
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::SetBitmapName
+//===========================================================================
+// Description: sets up the name of the animated bitmap that will be loaded
+// for this mission
+//
+// Constraints: None.
+//
+// Parameters: name - string representing the name of this bitmap on disk
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::SetBitmapName( const char* name )
+{
+ //
+ // If we're erasing the bitmap
+ //
+ if( name == NULL )
+ {
+ strcpy( s_AnimatedBitmapName, "" );
+ strcpy( s_AnimatedBitmapShortName, "" );
+ return;
+ }
+
+ //
+ // If we're not really changing anything, early out
+ //
+ if( strcmp( name, s_AnimatedBitmapName ) == 0 )
+ {
+ return;
+ }
+
+ //save the whole filename
+
+ //chuck: we need to somehow release the sprite or
+ //not set the s_AnimatedBitmapSprite = NULL because it
+ //will leak the the patty and selma screen.
+ //tRefCounted::Release(s_AnimatedBitmapSprite);
+
+ //check if our static pointer is to the patty and selma screen
+ if (s_AnimatedBitmapSprite != NULL)
+ {
+// s_AnimatedBitmapSprite->Release();
+
+ if (
+ s_AnimatedBitmapSprite->GetUID() == tName::MakeUID("misXX_PS.png")
+ ||
+ s_AnimatedBitmapSprite->GetUID() == tName::MakeUID("misXX_HW.png")
+ )
+ {
+ //if it is then we make a switch
+ //tRefCounted::Assign(sp_PattyAndSelmaScreenPNG,s_AnimatedBitmapSprite);
+ rAssert(sp_PattyAndSelmaScreenPNG == NULL);
+ sp_PattyAndSelmaScreenPNG = s_AnimatedBitmapSprite;
+ }
+ }
+
+ s_AnimatedBitmapSprite = NULL;
+ size_t length = strlen( name );
+ rAssert( length < sizeof( s_AnimatedBitmapName ) );
+
+ //
+ // If there's no text just return
+ //
+ if( length == 0 )
+ {
+ return;
+ }
+
+ //
+ // if we're not actually changing anything, return
+ //
+ if( strcmp( s_AnimatedBitmapName, name ) == 0 )
+ {
+ return;
+ }
+ strcpy( s_AnimatedBitmapName, name );
+
+ //figure out and save the name of the sprite from the filename
+ if( length >= 12 )
+ {
+ const char* spriteName = name + length - 12;
+ strcpy( s_AnimatedBitmapShortName, spriteName );
+ //s_AnimatedBitmapShortName[ 09 ] = 'p';
+ s_AnimatedBitmapShortName[ 10 ] = 'n';
+ s_AnimatedBitmapShortName[ 11 ] = 'g';
+ }
+ else
+ {
+ strcpy( s_AnimatedBitmapShortName, "" );
+ }
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::SetPlayAnimatedCamera
+//===========================================================================
+// Description: sets a flag to tell us whether or not to play the animated
+// camera after we exit this screen
+//
+// Constraints: None.
+//
+// Parameters: None
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::SetPlayAnimatedCamera( bool play )
+{
+ m_PlayAnimatedCamera = play;
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::StartIrisWipeClose
+//===========================================================================
+// Description: starts the transition to close the iris
+//
+// Constraints: None.
+//
+// Parameters: None
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::StartIrisWipeClose()
+{
+// ++m_numTransitionsPending;
+ m_MultiController->Reset();
+ float frames = m_MultiController->GetNumFrames();
+ m_MultiController->SetFrameRange( 0.0f, frames * 0.5f );
+ m_MultiController->SetFrame( 0.0f );
+}
+//===========================================================================
+// CGuiScreenMissionBase::StartIrisWipeOpen
+//===========================================================================
+// Description: starts the transition to open the iris
+//
+// Constraints: None.
+//
+// Parameters: None
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::StartIrisWipeOpen()
+{
+// ++m_numTransitionsPending;
+ m_MultiController->Reset();
+ float frames = m_MultiController->GetNumFrames();
+ m_MultiController->SetFrameRange( frames * 0.5f, frames );
+ m_MultiController->SetFrame( frames * 0.5f );
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::UnloadBitmap
+//===========================================================================
+// Description: unloads the bitmap, and reclaims all lost memory
+//
+// Constraints: None.
+//
+// Parameters: None
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::UnloadBitmap()
+{
+ p3d::pddi->DrawSync();
+ //::radThreadSleep (500); // Hmmmm, this shouldn't be required
+ tRefCounted::Release( s_AnimatedBitmapSprite );
+ m_missionStartBitmap->SetRawSprite( NULL );
+ m_missionStartBitmap->SetVisible( false );
+}
+
+//===========================================================================
+// CGuiScreenMissionBase::UpdateAnimatedBitmap
+//===========================================================================
+// Description: Updates animation of the bitmap
+//
+// Constraints: None.
+//
+// Parameters: deltaT - how much time has elapsed
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionBase::UpdateAnimatedBitmap( const float deltaT )
+{
+ if( s_AnimatedBitmapSprite == NULL )
+ {
+ p3d::inventory->PushSection();
+
+ p3d::pddi->DrawSync();
+ p3d::inventory->SelectSection( "DynamicHud" );
+ p3d::inventory->SetCurrentSectionOnly( false );
+ tEntityTable* currentSection = p3d::inventory->GetSection( "DynamicHud" );
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+ tInventory::Iterator< tEntity > it( p3d::inventory );
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ tEntity* entity = it.First();
+ while( entity != NULL )
+ {
+ if( entity->GetNameObject() == s_AnimatedBitmapShortName )
+ {
+ tSprite* sprite = dynamic_cast< tSprite* >( entity );
+ tRefCounted::Assign( s_AnimatedBitmapSprite, sprite );
+ }
+ entity = it.Next();
+ }
+
+ if( s_AnimatedBitmapSprite == NULL )
+ {
+ tRefCounted::Assign( s_AnimatedBitmapSprite, p3d::find< tSprite >( s_AnimatedBitmapShortName ) );
+ }
+ if( s_AnimatedBitmapSprite == NULL )
+ {
+ //loading is not yet complete
+ m_missionStartBitmap->SetVisible( false );
+ }
+ else
+ {
+ p3d::inventory->RemoveSectionElements( tEntity::MakeUID( "DynamicHud" ) );
+ #ifdef RAD_DEBUG
+ tSprite* stillThere = p3d::find< tSprite >( s_AnimatedBitmapShortName );
+ rWarningMsg( stillThere == NULL, "We're leaking memory if we can't remove this" );
+ #endif
+ m_missionStartBitmap->SetVisible( true );
+ s_BitmapLoadPending = false;
+ }
+
+ p3d::inventory->PopSection();
+
+ }
+ m_missionStartBitmap->SetRawSprite( s_AnimatedBitmapSprite );
+
+ //Chuck I hope this a safe place to release the patty and selma screen
+ if (sp_PattyAndSelmaScreenPNG != NULL)
+ {
+ tRefCounted::Release(sp_PattyAndSelmaScreenPNG);
+ sp_PattyAndSelmaScreenPNG= NULL;
+ }
+}
+
+void
+CGuiScreenMissionBase::UpdateGamblingInfo()
+{
+ rAssert( m_gamblingInfo != NULL );
+ m_gamblingInfo->SetVisible( true );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ char buffer[ 32 ];
+
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ rAssert( currentMission != NULL );
+
+ // set entry fee
+ //
+ int entryFee = 0; // in coins
+
+ CoinObjective* coinObjective = dynamic_cast<CoinObjective*>( currentMission->GetStage( 0 )->GetObjective() );
+ if( coinObjective != NULL )
+ {
+ entryFee = coinObjective->GetCoinAmount();
+ }
+ else
+ {
+ rAssertMsg( false, "No coin objective in first stage!" );
+ }
+
+ sprintf( buffer, "%d", entryFee );
+ rAssert( m_gamblingEntryFee != NULL );
+ m_gamblingEntryFee->SetString( 0, buffer );
+
+ // if not enough coins to gamble, disable 'accept button'\
+ //
+ InitIntroWagerMission();
+
+ // set time to beat
+ //
+ int timeToBeat = 0; // in seconds
+
+ RaceObjective* raceObjective = dynamic_cast<RaceObjective*>( currentMission->GetStage( 1 )->GetObjective() );
+ if( raceObjective != NULL )
+ {
+ timeToBeat = raceObjective->GetParTime();
+ }
+ else
+ {
+ rAssertMsg( false, "No race objective in second stage!" );
+ }
+
+ sprintf( buffer, "%d:%02d", timeToBeat / 60, timeToBeat % 60 );
+ rAssert( m_gamblingTimeToBeat != NULL );
+ m_gamblingTimeToBeat->SetString( 0, buffer );
+
+ // set best time
+ //
+ RenderEnums::LevelEnum currentLevel = GetGameplayManager()->GetCurrentLevelIndex();
+ int bestTime = GetCharacterSheetManager()->GetGambleRaceBestTime( currentLevel );
+ if( bestTime < 0 )
+ {
+ // if no best time set yet, just set it to the time to beat
+ //
+ bestTime = timeToBeat;
+ }
+
+ sprintf( buffer, "%d:%02d", bestTime / 60, bestTime % 60 );
+ rAssert( m_gamblingBestTime != NULL );
+ m_gamblingBestTime->SetString( 0, buffer );
+
+ // set vehicle odds
+ //
+ float oddsRatio = 1.0f;
+
+ Vehicle* currentVehicle = GetGameplayManager()->GetCurrentVehicle();
+ if( currentVehicle != NULL )
+ {
+ oddsRatio = currentVehicle->GetGamblingOdds();
+ }
+ else
+ {
+ rAssertMsg( false, "What? How do you expect to race w/out a vehicle??" );
+ }
+
+ rAssert( m_gamblingVehicleOdds != NULL );
+ if( oddsRatio >= VEHICLE_ODDS_HARD_THRESHOLD ) // hard
+ {
+ m_gamblingVehicleOdds->SetIndex( VEHICLE_ODDS_HARD );
+ }
+ else if( oddsRatio >= VEHICLE_ODDS_MEDIUM_THRESHOLD ) // medium
+ {
+ m_gamblingVehicleOdds->SetIndex( VEHICLE_ODDS_MEDIUM );
+ }
+ else // easy
+ {
+ m_gamblingVehicleOdds->SetIndex( VEHICLE_ODDS_EASY );
+ }
+/*
+ sprintf( buffer, "%.1f : 1", oddsRatio );
+ rAssert( m_gamblingVehicleOdds != NULL );
+ m_gamblingVehicleOdds->SetString( 0, buffer );
+*/
+
+ // set payout amount
+ //
+ int payoutAmount = entryFee + (int)( entryFee * oddsRatio );
+
+ sprintf( buffer, "%d", payoutAmount );
+ rAssert( m_gamblingPayout != NULL );
+ m_gamblingPayout->SetString( 0, buffer );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+}
+
diff --git a/game/code/presentation/gui/ingame/guiscreenmissionbase.h b/game/code/presentation/gui/ingame/guiscreenmissionbase.h
new file mode 100644
index 0000000..3ff6ec6
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenmissionbase.h
@@ -0,0 +1,132 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMissionSuccess
+//
+// Description:
+//
+//
+// Authors: Ian Gipson
+//
+// Revisions Date Author Revision
+// 2003/04/07 igipson Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMISSIONBASE_H
+#define GUISCREENMISSIONBASE_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <events/eventlistener.h>
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/ingame/guiscreenhastransitions.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class tSprite;
+namespace Scrooby
+{
+ class BoundedDrawable;
+ class Drawable;
+}
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMissionBase:
+ public CGuiScreen,
+ public CGuiScreenHasTransitions
+{
+public:
+ CGuiScreenMissionBase( Scrooby::Screen* pScreen, CGuiEntity* pParent, eGuiWindowID id );
+ ~CGuiScreenMissionBase();
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+ virtual void InitIntro();
+ virtual void InitRunning();
+ virtual void InitOutro();
+ static void ClearBitmap();
+ static void GetBitmapName( char* buffer );
+ static bool IsCurrentBitmap( const char* name );
+ static void ReplaceBitmap();
+ static void SetBitmapName( const char* name );
+
+protected:
+ Scrooby::Group* GetAbortBitmap();
+ Scrooby::Drawable* GetFlag();
+ Scrooby::Group* GetLoadCompletedGroup();
+ Scrooby::Text* GetMissionInfoText();
+ Scrooby::BoundedDrawable* GetMissionStartBitmap();
+ Scrooby::Page* GetPage();
+ Scrooby::Text* GetTitleText();
+ void InitIntroWagerMission();
+ virtual void OutroDone();
+ static void RemoveAnimatedBitmap();
+ void StartIrisWipeClose();
+ void StartIrisWipeOpen();
+ void UpdateAnimatedBitmap( const float deltaT );
+ void UnloadBitmap();
+ void UpdateGamblingInfo();
+ void SetPlayAnimatedCamera( bool play );
+ const char* GetWatcherName() const;
+
+private:
+ Scrooby::Text* m_missionTitle;
+ Scrooby::Page* m_Page;
+ Scrooby::Page* m_LetterboxPage;
+ Scrooby::Sprite* m_missionStartBitmap;
+ Scrooby::Sprite* m_background;
+ Scrooby::Sprite* m_Flag;
+ Scrooby::Drawable* m_ClipLeftGroup;
+ Scrooby::Drawable* m_ClipRightGroup;
+ Scrooby::Drawable* m_ClipLeft;
+ Scrooby::Drawable* m_ClipRight;
+ Scrooby::Sprite* m_ClipLeftArm;
+ Scrooby::Sprite* m_ClipRightArm;
+ Scrooby::Drawable* m_Foreground;
+ Scrooby::Group* m_darkenPolys;
+ Scrooby::Group* m_loadCompleted;
+ Scrooby::Group* m_textOverlays;
+ Scrooby::Group* m_darkenTop;
+ Scrooby::Group* m_darkenBottom;
+ Scrooby::Polygon* m_backgroundPoly;
+ Scrooby::Layer* m_LetterboxLayer;
+ Scrooby::Layer* m_BgLayer;
+ Scrooby::Group* m_FgLayer;
+ Scrooby::Pure3dObject* m_Iris;
+ tMultiController* m_MultiController;
+ float m_irisWipeNumFrames;
+ Scrooby::Text* m_Line0;
+ static tSprite* s_AnimatedBitmapSprite;
+ static char s_AnimatedBitmapName[ 256 ];
+ static char s_AnimatedBitmapShortName[ 32 ];
+ static bool s_BitmapLoadPending;
+ static tSprite* sp_PattyAndSelmaScreenPNG;
+ bool m_PlayAnimatedCamera;
+
+ enum eGamblingVehicleOdds
+ {
+ VEHICLE_ODDS_EASY,
+ VEHICLE_ODDS_MEDIUM,
+ VEHICLE_ODDS_HARD,
+
+ NUM_VEHICLE_ODDS
+ };
+
+ Scrooby::Group* m_gamblingInfo;
+ Scrooby::Text* m_gamblingEntryFee;
+ Scrooby::Text* m_gamblingTimeToBeat;
+ Scrooby::Text* m_gamblingBestTime;
+ Scrooby::Text* m_gamblingVehicleOdds;
+ Scrooby::Text* m_gamblingPayout;
+
+ bool m_ReadyToExitScreen : 1;
+
+};
+
+#endif // GUISCREENMISSIONBASE_H
diff --git a/game/code/presentation/gui/ingame/guiscreenmissionload.cpp b/game/code/presentation/gui/ingame/guiscreenmissionload.cpp
new file mode 100644
index 0000000..2a6c4e2
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenmissionload.cpp
@@ -0,0 +1,511 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMissionLoad
+//
+// Description: Implementation of the CGuiScreenMissionLoad class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <camera/animatedcam.h>
+#include <events/eventmanager.h>
+#include <mission/gameplaymanager.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenmissionload.h>
+#include <presentation/gui/ingame/guiscreenmissionselect.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <p3d/utility.hpp>
+#include <raddebug.hpp> // Foundation
+#include <radmath/matrix.hpp>
+#include <radmath/util.hpp>
+#include <loading/loadingmanager.h>
+#include <strings/unicodestring.h>
+
+#include <screen.h>
+#include <page.h>
+
+#include <p3d/unicode.hpp>
+#include <string.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+#ifndef PAL
+ // convert mission title string to all caps
+ //
+ #define MISSION_TITLE_ALL_CAPS
+#endif
+
+#define TRANSITION_IN_TIME 250.0f
+
+const UnicodeChar EMPTY_MISSION_HINT = '*';
+const float startDelayTimeMs = 500.0f;
+const float DARKEN_ANIMATION_DELAY_BEFORE_STARTING = startDelayTimeMs;
+const float DARKEN_ANIMATION_INTRO_TIME = 1000.0f;
+int g_BitmapPositionX;
+int g_BitmapPositionY;
+int g_BitmapSizeX;
+int g_BitmapSizeY;
+int g_TitlePositionX;
+int g_TitlePositionY;
+
+int g_BitmapPositionWagerX = 0;
+int g_BitmapPositionWagerY = 200;
+int g_BitmapSizeWagerX = 20;
+int g_BitmapSizeWagerY = 20;
+int g_TitlePositionWagerX = 200;
+int g_TitlePositionWagerY = 370;
+
+GuiSFX::Show g_LoadCompletedShow( "LoadCompletedShow" );
+GuiSFX::ColorChange g_LoadCompletedTransitionIn( "LoadCompletedTransitionIn" );
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMissionLoad::CGuiScreenMissionLoad
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMissionLoad::CGuiScreenMissionLoad
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+):
+ CGuiScreenMissionBase( pScreen, pParent, GUI_SCREEN_ID_MISSION_LOAD ),
+ m_LoadIsDone( true ),
+ m_loadingText( NULL ),
+ m_elapsedIdleTime( 0 )
+{
+ ExtractNormalPositions();
+ Scrooby::Group* loadCompleted = GetLoadCompletedGroup();
+ g_LoadCompletedShow.SetDrawable( loadCompleted );
+
+ g_LoadCompletedTransitionIn.SetDrawable( loadCompleted );
+ g_LoadCompletedTransitionIn.SetStartColour( tColour( 255, 255, 255, 0 ) );
+ g_LoadCompletedTransitionIn.SetEndColour ( tColour( 255, 255, 255, 255 ) );
+ g_LoadCompletedTransitionIn.SetTimeInterval( TRANSITION_IN_TIME );
+ g_LoadCompletedTransitionIn.Deactivate();
+ WATCH( g_LoadCompletedTransitionIn, GetWatcherName() );
+
+ g_LoadCompletedShow. SetNextTransition( g_LoadCompletedTransitionIn );
+ g_LoadCompletedTransitionIn.SetNextTransition( NULL );
+
+ AddTransition( g_LoadCompletedTransitionIn );
+
+ AddListeners();
+
+ // get loading text
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "LoadingText" );
+ if( pPage != NULL )
+ {
+ m_loadingText = pPage->GetText( "Loading" );
+ rAssert( m_loadingText != NULL );
+ m_loadingText->SetVisible( false ); // hide by default
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMissionLoad::~CGuiScreenMissionLoad
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMissionLoad::~CGuiScreenMissionLoad()
+{
+ RemoveListeners();
+ p3d::pddi->DrawSync();
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::AddListeners
+//===========================================================================
+// Description: Adds all the listeners this class is ever going to need
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionLoad::AddListeners()
+{
+ GetEventManager()->AddListener( this, EVENT_GUI_MISSION_LOAD_COMPLETE );
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::ExtractNormalPositions
+//===========================================================================
+// Description: the default positions of the objects are taken out of scrooby
+// this function actually goes and gets them
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionLoad::ExtractNormalPositions()
+{
+ // extract the position of the mission loading bitmap
+ Scrooby::Drawable* missionStartBitmap = GetMissionStartBitmap();
+ missionStartBitmap->GetOriginPosition( g_BitmapPositionX, g_BitmapPositionY );
+ missionStartBitmap->GetBoundingBoxSize( g_BitmapSizeX, g_BitmapSizeY );
+
+ // extract the position of the title
+ Scrooby::Text* text = GetTitleText();
+ text->GetOriginPosition( g_TitlePositionX, g_TitlePositionY );
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::HandleEvent
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void
+CGuiScreenMissionLoad::HandleEvent( EventEnum id, void* pEventData )
+{
+ if( id == EVENT_GUI_MISSION_LOAD_COMPLETE )
+ {
+ // mission load completed, show "Press Start" message
+ //
+ m_LoadIsDone = true;
+ g_LoadCompletedShow.Activate();
+
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ rAssert( currentMission != NULL );
+ if( currentMission->IsWagerMission() )
+ {
+ this->UpdateGamblingInfo();
+ }
+
+ // hide loading text
+ //
+ if( m_loadingText != NULL )
+ {
+ m_loadingText->SetVisible( false );
+ }
+ }
+}
+
+//===========================================================================
+// CGuiScreenMissionSuccess::HandleMessage
+//===========================================================================
+// Description: Handles messages, and passes them up the hierarchy when done
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionLoad::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ m_elapsedIdleTime += param1;
+
+ if( m_loadingText != NULL && !m_LoadIsDone )
+ {
+ // blink loading text if idling here on this screen to satisfy
+ // TRC/TCR requirements
+ //
+ const unsigned int BLINKING_PERIOD = 250;
+ bool isBlinked = GuiSFX::Blink( m_loadingText,
+ static_cast<float>( m_elapsedIdleTime ),
+ static_cast<float>( BLINKING_PERIOD ) );
+ if( isBlinked )
+ {
+ m_elapsedIdleTime %= BLINKING_PERIOD;
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ if( m_LoadIsDone && this->IsButtonVisible( BUTTON_ICON_ACCEPT ) )
+ {
+ SetPlayAnimatedCamera( true );
+
+ // resume game and start mission
+ //
+ m_pParent->HandleMessage( GUI_MSG_RESUME_INGAME );
+
+ // trigger this event ONLY for wager race missions
+ //
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ rAssert( currentMission != NULL );
+ if( currentMission->IsWagerMission() )
+ {
+ GetEventManager()->TriggerEvent( EVENT_ATTEMPT_TO_ENTER_GAMBLERACE );
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_FE_CONTINUE );
+ GetEventManager()->TriggerEvent( EVENT_MISSION_BRIEFING_ACCEPTED );
+ }
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ if( m_LoadIsDone && this->IsButtonVisible( BUTTON_ICON_BACK ) )
+ {
+ // resume game and abort mission
+ //
+ SetPlayAnimatedCamera( false );
+ AnimatedCam::SetCamera( "" );
+ m_pParent->HandleMessage( GUI_MSG_RESUME_INGAME, ON_HUD_ENTER_ABORT_MISSION );
+
+ GetEventManager()->TriggerEvent(EVENT_USER_CANCEL_MISSION_BRIEFING);
+ GetEventManager()->TriggerEvent( EVENT_FE_CANCEL );
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ CGuiScreenMissionBase::HandleMessage( message, param1, param2 );
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::InitializePermanentVariables
+//===========================================================================
+// Description: Stuff that goes into permanent memory gets initialized in
+// here
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionLoad::InitializePermanentVariables()
+{
+ p3d::inventory->AddSection( "DynamicHud" );
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionLoad::InitIntro()
+{
+ CGuiScreenMissionBase::InitIntro();
+
+ Scrooby::Text* missionTitle = GetTitleText();
+ rAssert( missionTitle != NULL );
+
+#ifdef MISSION_TITLE_ALL_CAPS
+ // convert mission title to all caps
+ //
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ UnicodeString unicodeString;
+ unicodeString.ReadUnicode( missionTitle->GetStringBuffer() );
+ p3d::UnicodeStrUpr( unicodeString.GetBuffer() );
+
+ missionTitle->SetString( missionTitle->GetIndex(), unicodeString );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+#endif // MISSION_TITLE_ALL_CAPS
+
+ //
+ // Turn on the titles
+ //
+ missionTitle->SetVisible( true );
+ GetMissionInfoText()->SetVisible( true );
+
+ Scrooby::Group* loadCompleted = GetLoadCompletedGroup();
+ loadCompleted->SetVisible( false );
+ m_LoadIsDone = false;
+
+ if( m_loadingText != NULL )
+ {
+ // hide loading text
+ //
+ m_loadingText->SetVisible( false );
+
+ this->SetAlphaForLayers( 1.0f, m_foregroundLayers, m_numForegroundLayers );
+ }
+
+ m_elapsedIdleTime = 0;
+
+ //
+ // is this a normal mission or a wager mission
+ //
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ rAssert( currentMission != NULL );
+ if( currentMission->IsWagerMission() )
+ {
+ InitPositionsWager();
+ }
+ else
+ {
+ InitPositionsNormal();
+ }
+
+ // only show "cancel" button for wager missions
+ //
+ this->SetButtonVisible( BUTTON_ICON_BACK, currentMission->IsWagerMission() );
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionLoad::InitOutro()
+{
+ CGuiScreenMissionBase::InitOutro();
+ rWarning( m_LoadIsDone );
+ m_LoadIsDone = false;
+
+ if( m_loadingText != NULL )
+ {
+ // hide loading text
+ //
+ m_loadingText->SetVisible( false );
+ }
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::InitPositionsNormal
+//===========================================================================
+// Description: Move scroobyelements to where they need to be for normal
+// missions
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionLoad::InitPositionsNormal()
+{
+ Scrooby::BoundedDrawable* missionStart = GetMissionStartBitmap();
+ missionStart->SetPosition ( g_BitmapPositionX, g_BitmapPositionY );
+ missionStart->SetBoundingBoxSize( g_BitmapSizeX, g_BitmapSizeY );
+
+ Scrooby::Text* title = GetTitleText();
+#ifndef RAD_WIN32 // temp fix.
+ title->SetPosition( g_TitlePositionX, g_TitlePositionY );
+#endif
+
+ Scrooby::Text* info = GetMissionInfoText();
+ info->SetHorizontalJustification( Scrooby::Center );
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::InitPositionsWager
+//===========================================================================
+// Description: Move scrooby elements to where they need to be for wager
+// missions
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionLoad::InitPositionsWager()
+{
+ Scrooby::BoundedDrawable* missionStart = GetMissionStartBitmap();
+ missionStart->SetPosition( g_BitmapPositionWagerX, g_BitmapPositionWagerY );
+ missionStart->SetBoundingBoxSize( g_BitmapSizeWagerX, g_BitmapSizeWagerY );
+
+ Scrooby::Text* title = GetTitleText();
+ title->SetPosition( g_TitlePositionWagerX, g_TitlePositionWagerY );
+
+ // turn on the flag bitmap
+ Scrooby::Drawable* flag = GetFlag();
+ flag->SetVisible( true );
+
+ Scrooby::Text* info = GetMissionInfoText();
+ info->SetHorizontalJustification( Scrooby::Left );
+}
+
+//===========================================================================
+// CGuiScreenMissionLoad::RemoveListeners
+//===========================================================================
+// Description: removes all the listeners that have been set up in this class
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionLoad::RemoveListeners()
+{
+}
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/ingame/guiscreenmissionload.h b/game/code/presentation/gui/ingame/guiscreenmissionload.h
new file mode 100644
index 0000000..8193b3c
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenmissionload.h
@@ -0,0 +1,73 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMissionLoad
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMISSIONLOAD_H
+#define GUISCREENMISSIONLOAD_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenmissionbase.h>
+#include <presentation/gui/utility/transitions.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CGuiMenu;
+class tSprite;
+namespace Scrooby
+{
+ class Group;
+}
+
+const int MAX_NUM_BULLETS = 2;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMissionLoad :
+ public CGuiScreenMissionBase,
+ public EventListener
+{
+public:
+ CGuiScreenMissionLoad( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMissionLoad();
+
+ // Implements EventListener
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ static void InitializePermanentVariables();
+ virtual void InitIntro();
+ virtual void InitOutro();
+
+protected:
+ virtual void AddListeners();
+ void ExtractNormalPositions();
+ void InitPositionsNormal();
+ void InitPositionsWager();
+ virtual void RemoveListeners();
+
+private:
+ bool m_LoadIsDone : 1; //the mission load is done
+
+ Scrooby::Text* m_loadingText;
+ unsigned int m_elapsedIdleTime;
+
+};
+
+#endif // GUISCREENMISSIONLOAD_H
diff --git a/game/code/presentation/gui/ingame/guiscreenmissionover.cpp b/game/code/presentation/gui/ingame/guiscreenmissionover.cpp
new file mode 100644
index 0000000..64ad4f4
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenmissionover.cpp
@@ -0,0 +1,450 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMissionOver
+//
+// Description: Implementation of the CGuiScreenMissionOver class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenmissionover.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guitextbible.h>
+
+#include <events/eventmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <mission/mission.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <sound/soundmanager.h>
+
+#include <p3d/unicode.hpp>
+#include <raddebug.hpp> // Foundation
+#include <layer.h>
+#include <page.h>
+#include <screen.h>
+#include <text.h>
+
+#include <stdlib.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const int NUM_ATTEMPTS_REQUIRED_FOR_SKIPPING = 7;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMissionOver::CGuiScreenMissionOver
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMissionOver::CGuiScreenMissionOver
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_MISSION_OVER ),
+ m_pMenu( NULL ),
+ m_failureReason( NULL ),
+ m_failureHint( NULL )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenMissionOver" );
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage;
+ pPage = m_pScroobyScreen->GetPage( "MissionOver" );
+ rAssert( pPage );
+
+ Scrooby::Layer* foreground = pPage->GetLayer( "Foreground" );
+ rAssert( foreground != NULL );
+
+ // get 'mission failed' text bible string
+ //
+ P3D_UNICODE* text = GetTextBibleString( "MISSION_FAILED" );
+ rAssert( text != NULL );
+ int textLength = p3d::UnicodeStrLen( text ) + 1;
+
+ Scrooby::Sprite* missionFailedText = foreground->GetSprite( "MissionFailed" );
+ if( missionFailedText != NULL )
+ {
+ missionFailedText->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ missionFailedText->CreateBitmapTextBuffer( textLength );
+ missionFailedText->SetBitmapText( text );
+ missionFailedText->SetBitmapTextLineSpacing( 10 );
+#ifdef RAD_WIN32
+ missionFailedText->ResetTransformation();
+ missionFailedText->ScaleAboutCenter( 0.5f );
+#endif
+ }
+
+ m_failureReason = foreground->GetText( "MissionFailureReason" );
+ rAssert( m_failureReason != NULL );
+ m_failureReason->SetTextMode( Scrooby::TEXT_WRAP );
+
+ m_failureHint = foreground->GetText( "MissionFailureHint" );
+ rAssert( m_failureHint != NULL );
+ m_failureHint->SetTextMode( Scrooby::TEXT_WRAP );
+
+ // Create a menu.
+ //
+ m_pMenu = new CGuiMenuPrompt( this, pPage, 3 );
+ rAssert( m_pMenu != NULL );
+
+ // register events to listen for
+ //
+ GetEventManager()->AddListener( this, EVENT_MISSION_FAILURE );
+
+MEMTRACK_POP_GROUP("CGUIScreenMissionOver");
+}
+
+
+//===========================================================================
+// CGuiScreenMissionOver::~CGuiScreenMissionOver
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMissionOver::~CGuiScreenMissionOver()
+{
+ // unregister events
+ //
+ GetEventManager()->RemoveListener( this, EVENT_MISSION_FAILURE );
+
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMissionOver::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionOver::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ if( param1 == 0 ) // 'yes' response
+ {
+ // restart mission
+ //
+ m_pParent->HandleMessage( GUI_MSG_RESUME_INGAME,
+ ON_HUD_ENTER_RESTART_MISSION );
+ }
+ else if( param1 == 1 ) // 'no' response
+ {
+ // abort mission
+ //
+ m_pParent->HandleMessage( GUI_MSG_RESUME_INGAME,
+ ON_HUD_ENTER_ABORT_MISSION );
+ }
+ else if( param1 == 2 ) // 'skip' response
+ {
+ // skip to next mission
+ //
+ m_pParent->HandleMessage( GUI_MSG_RESUME_INGAME,
+ ON_HUD_ENTER_SKIP_MISSION );
+/*
+ CurrentMissionStruct currentMission = GetCharacterSheetManager()->QueryCurrentMission();
+
+ unsigned int nextLevel = currentMission.mLevel;
+ unsigned int nextMission = currentMission.mMissionNumber + 1;
+
+ if( currentMission.mMissionNumber == RenderEnums::M7 ) // last mission
+ {
+ // go to next level's first mission
+ //
+ nextLevel++;
+ nextMission = RenderEnums::M1;
+
+ rAssert( nextLevel < RenderEnums::numLevels );
+ }
+*/
+ }
+ else
+ {
+ rAssertMsg( 0, "WARNING: *** Unexpected response!\n" );
+ }
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+void
+CGuiScreenMissionOver::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_MISSION_FAILURE:
+ {
+ MissionCondition* failureCondition = reinterpret_cast<MissionCondition*>( pEventData );
+ rAssert( failureCondition != NULL );
+
+ this->SetFailureMessage( failureCondition->GetType() );
+
+ break;
+ }
+ default:
+ {
+ rWarningMsg( false, "Unhandled event ID!" );
+
+ break;
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMissionOver::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionOver::InitIntro()
+{
+ rAssert( m_pMenu != NULL );
+
+ // reset menu to "Yes" selection
+ //
+ m_pMenu->Reset();
+
+ // show/hide 'skip' menu response
+ //
+ CurrentMissionStruct currentMission = GetCharacterSheetManager()->QueryCurrentMission();
+ int numAttemps = GetCharacterSheetManager()->QueryNumberOfAttempts( currentMission.mLevel, currentMission.mMissionNumber );
+ bool isSkipAllowed = (numAttemps >= NUM_ATTEMPTS_REQUIRED_FOR_SKIPPING);
+
+ if( currentMission.mLevel == RenderEnums::L7 && static_cast<int>( currentMission.mMissionNumber ) >= RenderEnums::M5 )
+ {
+ // can't skip the last mission of the game
+ //
+ isSkipAllowed = false;
+ }
+
+#ifdef RAD_E3
+ // no skipping mission allowed on E3 build
+ //
+ isSkipAllowed = false;
+#endif
+
+ m_pMenu->SetMenuItemEnabled( 2, isSkipAllowed, true );
+
+ GetSoundManager()->OnStoreScreenStart( false );
+}
+
+
+//===========================================================================
+// CGuiScreenMissionOver::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionOver::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenMissionOver::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionOver::InitOutro()
+{
+ GetEventManager()->TriggerEvent( EVENT_DIALOG_SHUTUP );
+ GetSoundManager()->OnStoreScreenEnd();
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenMissionOver::SetFailureMessage( MissionCondition::ConditionTypeEnum conditionType )
+{
+ int failureMessageIndex = 0;
+
+ switch( conditionType )
+ {
+ case MissionCondition::COND_VEHICLE_DAMAGE:
+ {
+ failureMessageIndex = 1;
+
+ break;
+ }
+ case MissionCondition::COND_PLAYER_HIT:
+ {
+ failureMessageIndex = 2;
+
+ break;
+ }
+ case MissionCondition::COND_TIME_OUT:
+ {
+ failureMessageIndex = 3;
+
+ break;
+ }
+ case MissionCondition::COND_PLAYER_OUT_OF_VEHICLE:
+ {
+ failureMessageIndex = 4;
+
+ break;
+ }
+ case MissionCondition::COND_FOLLOW_DISTANCE:
+ {
+ failureMessageIndex = 5;
+
+ break;
+ }
+ case MissionCondition::COND_OUT_OF_BOUNDS:
+ {
+ failureMessageIndex = 6;
+
+ break;
+ }
+ case MissionCondition::COND_RACE:
+ {
+ failureMessageIndex = 7;
+
+ break;
+ }
+ case MissionCondition::COND_NOT_ABDUCTED:
+ {
+ failureMessageIndex = 8;
+
+ break;
+ }
+ case MissionCondition::COND_POSITION:
+ {
+ failureMessageIndex = 9;
+
+ break;
+ }
+ case MissionCondition::COND_HIT_AND_RUN_CAUGHT:
+ {
+ failureMessageIndex = 10;
+
+ break;
+ }
+ case MissionCondition::COND_GET_COLLECTIBLES:
+ {
+ failureMessageIndex = 11;
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Unknown failure reason!" );
+
+ break;
+ }
+ }
+
+ rAssert( m_failureReason != NULL );
+ m_failureReason->SetIndex( failureMessageIndex );
+
+ // set associated mission failure hint (randomly chosen)
+ //
+ int hintMessageIndex = 0;
+
+ if( failureMessageIndex > 0 )
+ {
+ rAssert( GetGameplayManager()->GetCurrentMission() != NULL );
+
+ int numValidFailureHints = GetGameplayManager()->GetCurrentMission()->GetNumValidFailureHints();
+ if( numValidFailureHints < 0 )
+ {
+ numValidFailureHints = MAX_NUM_HINTS_PER_FAILURE;
+ }
+
+ int randomHintIndex = rand() % numValidFailureHints;
+
+ hintMessageIndex = (failureMessageIndex - 1) * MAX_NUM_HINTS_PER_FAILURE + 1 + randomHintIndex;
+ }
+
+ rAssert( m_failureHint != NULL );
+ m_failureHint->SetIndex( hintMessageIndex );
+}
+
diff --git a/game/code/presentation/gui/ingame/guiscreenmissionover.h b/game/code/presentation/gui/ingame/guiscreenmissionover.h
new file mode 100644
index 0000000..82a9ce6
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenmissionover.h
@@ -0,0 +1,72 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMissionOver
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMISSIONOVER_H
+#define GUISCREENMISSIONOVER_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+#include <events/eventlistener.h>
+#include <mission/conditions/missioncondition.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+namespace Scrooby
+{
+ class Screen;
+ class Text;
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMissionOver : public CGuiScreen,
+ public EventListener
+{
+public:
+ CGuiScreenMissionOver( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMissionOver();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ static const int MAX_NUM_HINTS_PER_FAILURE = 8;
+
+ void SetFailureMessage( MissionCondition::ConditionTypeEnum conditionType );
+
+ CGuiMenu* m_pMenu;
+
+ Scrooby::Text* m_failureReason;
+ Scrooby::Text* m_failureHint;
+
+};
+
+#endif // GUISCREENMISSIONOVER_H
diff --git a/game/code/presentation/gui/ingame/guiscreenmissionselect.cpp b/game/code/presentation/gui/ingame/guiscreenmissionselect.cpp
new file mode 100644
index 0000000..b043188
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenmissionselect.cpp
@@ -0,0 +1,493 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMissionSelect
+//
+// Description: Implementation of the CGuiScreenMissionSelect class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenmissionselect.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiuserinputhandler.h>
+
+#include <cheats/cheatinputsystem.h>
+#include <events/eventmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/charactersheet/charactersheet.h>
+#include <render/enums/renderenums.h>
+
+#include <raddebug.hpp> // Foundation
+#include <page.h>
+#include <polygon.h>
+#include <screen.h>
+#include <sprite.h>
+#include <string.h>
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+#ifdef RAD_WIN32
+const float LEVEL_BAR_CORRECTION_SCALE = 1.0f;
+#else
+const float LEVEL_BAR_CORRECTION_SCALE = 2.0f;
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMissionSelect::CGuiScreenMissionSelect
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMissionSelect::CGuiScreenMissionSelect
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_MISSION_SELECT ),
+ m_pMenuLevel( NULL ),
+ m_pMenu( NULL ),
+ m_numLevelSelections( 0 )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenMissionSelect" );
+ HeapMgr()->PushHeap (GMA_LEVEL_HUD);
+
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "MissionSelect" );
+ rAssert( pPage );
+
+ Scrooby::Group* levelBar = pPage->GetGroup( "Level" );
+ rAssert( levelBar != NULL );
+
+#ifdef RAD_WIN32
+ m_leftArrow = levelBar->GetSprite( "LArrowBgd" );
+ rAssert( m_leftArrow != NULL );
+ m_leftArrow->ScaleAboutCenter( 1.7f );
+ m_leftArrow->Translate( -5, 3 );
+
+ m_rightArrow = levelBar->GetSprite( "RArrowBgd" );
+ rAssert( m_rightArrow != NULL );
+ m_rightArrow->ScaleAboutCenter( 1.7f );
+ m_rightArrow->Translate( -6, 3 );
+#endif
+
+ Scrooby::Sprite* levelBarBgd = levelBar->GetSprite( "LevelBar" );
+ if( levelBarBgd != NULL )
+ {
+ levelBarBgd->ResetTransformation();
+ levelBarBgd->ScaleAboutCenter( LEVEL_BAR_CORRECTION_SCALE );
+ }
+
+ // Create menu for level selection.
+ //
+ m_pMenuLevel = new CGuiMenu( this, 1, GUI_TEXT_MENU, MENU_SFX_NONE );
+ rAssert( m_pMenuLevel != NULL );
+ m_pMenuLevel->SetHighlightColour( false, tColour( 0, 0, 0 ) );
+
+ m_pMenuLevel->AddMenuItem( levelBar->GetText( "Level" ),
+ levelBar->GetText( "Level" ),
+ NULL,
+ NULL,
+ levelBar->GetSprite( "LArrowBgd" ),
+ levelBar->GetSprite( "RArrowBgd" ),
+ SELECTION_ENABLED | VALUES_WRAPPED );
+
+ // Create a menu.
+ //
+ m_pMenu = new CGuiMenu( this, MAX_NUM_REGULAR_MISSIONS, GUI_TEXT_MENU, MENU_SFX_NONE );
+ rAssert( m_pMenu != NULL );
+
+ // Add menu items
+ //
+ Scrooby::Group* missions = pPage->GetGroup( "Missions" );
+ Scrooby::Group* missionMenu = pPage->GetGroup( "Menu" );
+ Scrooby::Group* status = pPage->GetGroup( "Status" );
+ Scrooby::Group* initials = pPage->GetGroup( "Initials" );
+ Scrooby::Group* times = pPage->GetGroup( "Times" );
+ rAssert( missions != NULL );
+ rAssert( missionMenu != NULL );
+ rAssert( status != NULL );
+
+ for( int i = 0; i < MAX_NUM_REGULAR_MISSIONS; i++ )
+ {
+ char name[ 32 ];
+
+ // mission number and titles
+ //
+ sprintf( name, "MissionNum%d", i );
+ m_missionInfo[ i ].m_number = missions->GetText( name );
+
+ sprintf( name, "Mission%d", i );
+ Scrooby::Text* pText = missionMenu->GetText( name );
+ if( pText != NULL )
+ {
+ m_pMenu->AddMenuItem( pText );
+ }
+
+ m_missionInfo[ i ].m_title = pText;
+
+ // mission status
+ //
+ sprintf( name, "MissionStatus%d", i );
+ m_missionInfo[ i ].m_status = status->GetSprite( name );
+ }
+
+ Scrooby::Group* highlightBar = pPage->GetGroup( "HighlightBar" );
+ if( highlightBar != NULL )
+ {
+ m_pMenu->SetCursor( highlightBar );
+
+#ifdef PAL
+ highlightBar->ResetTransformation();
+ highlightBar->ScaleAboutCenter( 1.035f, 1.0f, 1.0f );
+#endif // PAL
+ }
+
+ this->AutoScaleFrame( m_pScroobyScreen->GetPage( "BigBoard" ) );
+
+ HeapMgr()->PopHeap (GMA_LEVEL_HUD);
+MEMTRACK_POP_GROUP("CGUIScreenMissionSelect");
+}
+
+
+//===========================================================================
+// CGuiScreenMissionSelect::~CGuiScreenMissionSelect
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMissionSelect::~CGuiScreenMissionSelect()
+{
+ if( m_pMenuLevel != NULL )
+ {
+ delete m_pMenuLevel;
+ m_pMenuLevel = NULL;
+ }
+
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMissionSelect::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionSelect::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_START:
+ {
+ if( !m_pMenu->HasSelectionBeenMade() )
+ {
+ // resume game
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+ }
+
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_VALUE_CHANGED:
+ {
+ rAssert( param1 == 0 );
+
+ this->OnLevelSelectionChange( static_cast<int>( param2 ) );
+
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ rAssert( m_pMenuLevel != NULL );
+ int currentLevel = m_pMenuLevel->GetSelectionValue( 0 );
+
+ // special case for level 1: all missions are offset by 1
+ // due to the tutorial mission treated as mission 0
+ //
+ unsigned int selectedMission = currentLevel == 0 ? param1 + 1 : param1;
+
+ m_pParent->HandleMessage( GUI_MSG_QUIT_INGAME_FOR_RELOAD,
+ currentLevel,
+ selectedMission );
+
+ // stop any dialog that may still be in progress
+ //
+ GetEventManager()->TriggerEvent( EVENT_DIALOG_SHUTUP );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+#ifdef RAD_DEMO
+ // can't change level in demo builds; only missions in current level
+ // are selectable
+ //
+ if( message == GUI_MSG_CONTROLLER_LEFT || message == GUI_MSG_CONTROLLER_RIGHT )
+ {
+ // ignore left/right controller inputs
+ //
+ return;
+ }
+#endif
+
+ // relay message to level menu
+ //
+ if( m_pMenuLevel != NULL )
+ {
+ m_pMenuLevel->HandleMessage( message, param1, param2 );
+ }
+
+ // relay message to menu
+ //
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+#ifdef RAD_WIN32
+//===========================================================================
+// CGuiScreenMissionSelect::CheckCursorAgainstHotspots
+//===========================================================================
+// Description: Checks cursor position against its list of hotspots.
+//
+// Constraints: None.
+//
+// Parameters: float x - The x position of cursor in P3D coordinates.
+// float y - The y position of cursor in P3D coordinates.
+//
+// Return: N/A.
+//
+//===========================================================================
+eFEHotspotType CGuiScreenMissionSelect::CheckCursorAgainstHotspots( float x, float y )
+{
+ eFEHotspotType hotSpotType = CGuiScreen::CheckCursorAgainstHotspots( x, y );
+ if( hotSpotType == HOTSPOT_NONE )
+ {
+ if( m_leftArrow )
+ {
+ if( m_leftArrow->IsPointInBoundingRect( x, y ) )
+ {
+ hotSpotType = HOTSPOT_ARROWLEFT;
+ }
+ }
+ if( m_rightArrow )
+ {
+ if( m_rightArrow->IsPointInBoundingRect( x, y ) )
+ {
+ hotSpotType = HOTSPOT_ARROWRIGHT;
+ }
+ }
+
+ }
+ return hotSpotType;
+}
+#endif
+
+//===========================================================================
+// CGuiScreenMissionSelect::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionSelect::InitIntro()
+{
+ m_numLevelSelections = GetCharacterSheetManager()->QueryHighestMission().mLevel + 1;
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_MISSIONS ) )
+ {
+ m_numLevelSelections = RenderEnums::numLevels;
+ }
+
+ rAssert( m_pMenuLevel != NULL );
+ m_pMenuLevel->SetSelectionValueCount( 0, m_numLevelSelections );
+
+ int currentLevel = GetGameplayManager()->GetCurrentLevelIndex();
+ m_pMenuLevel->SetSelectionValue( 0, currentLevel );
+ this->OnLevelSelectionChange( currentLevel );
+}
+
+
+//===========================================================================
+// CGuiScreenMissionSelect::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionSelect::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenMissionSelect::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionSelect::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenMissionSelect::OnLevelSelectionChange( int currentLevel )
+{
+ rAssert( m_pMenu != NULL );
+ m_pMenu->Reset();
+
+ // update mission info for new level
+ //
+ for( int i = 0; i < MAX_NUM_REGULAR_MISSIONS; i++ )
+ {
+ // mission title
+ //
+ Scrooby::Text* missionTitle = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( i )->GetItem() );
+ rAssert( missionTitle != NULL );
+ missionTitle->SetIndex( currentLevel );
+
+ // mission info (status, best time, initials)
+ //
+ MissionRecord* missionRecord = GetCharacterSheetManager()->QueryMissionStatus( static_cast<RenderEnums::LevelEnum>( currentLevel ),
+ currentLevel == 0 ? i + 1 : i );
+ rAssert( missionRecord != NULL );
+
+ bool isMissionUnlocked = true;
+ if( currentLevel == (m_numLevelSelections - 1) )
+ {
+ int highestMissionPlayed = GetCharacterSheetManager()->QueryHighestMission().mMissionNumber;
+ if( currentLevel == 0 )
+ {
+ highestMissionPlayed--;
+ }
+
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_MISSIONS ) )
+ {
+ highestMissionPlayed = RenderEnums::M7;
+ }
+
+#ifdef RAD_DEMO
+ highestMissionPlayed = (currentLevel == RenderEnums::L7) ? RenderEnums::M4 : RenderEnums::M7;
+#endif // RAD_DEMO
+
+ isMissionUnlocked = ( i <= highestMissionPlayed );
+ }
+
+ if( isMissionUnlocked )
+ {
+ this->UpdateMissionStatus( i, missionRecord );
+ }
+
+ // show unlocked missions only
+ //
+ m_pMenu->SetMenuItemEnabled( i, isMissionUnlocked );
+
+ m_missionInfo[ i ].m_number->SetVisible( isMissionUnlocked );
+ m_missionInfo[ i ].m_title->SetVisible( isMissionUnlocked );
+ m_missionInfo[ i ].m_status->SetVisible( isMissionUnlocked );
+ }
+}
+
+void
+CGuiScreenMissionSelect::UpdateMissionStatus( int index,
+ MissionRecord* missionRecord )
+{
+ rAssert( missionRecord != NULL );
+ rAssert( m_missionInfo[ index ].m_status );
+
+ if( missionRecord->mCompleted )
+ {
+ m_missionInfo[ index ].m_status->SetIndex( 2 ); // 2 = green check
+ }
+ else
+ {
+ if( missionRecord->mNumAttempts > 0 &&
+ !missionRecord->mSkippedMission )
+ {
+ m_missionInfo[ index ].m_status->SetIndex( 1 ); // 1 = red cross
+ }
+ else
+ {
+ m_missionInfo[ index ].m_status->SetIndex( 0 ); // 0 = yellow dash
+ }
+ }
+}
+
diff --git a/game/code/presentation/gui/ingame/guiscreenmissionselect.h b/game/code/presentation/gui/ingame/guiscreenmissionselect.h
new file mode 100644
index 0000000..9db8041
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenmissionselect.h
@@ -0,0 +1,83 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMissionSelect
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/09/20 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMISSIONSELECT_H
+#define GUISCREENMISSIONSELECT_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/utility/numerictext.h>
+
+const int MAX_NUM_REGULAR_MISSIONS = 7;
+const int MAX_NUM_MISSIONS_PER_LEVEL = MAX_NUM_REGULAR_MISSIONS + 4 + 1; // +4 street races, +1 bonus
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+struct MissionRecord;
+
+struct MissionDisplayInfo
+{
+ static const unsigned int MAX_NUM_INITIALS = 3;
+
+ Scrooby::Text* m_number;
+ Scrooby::Text* m_title;
+ Scrooby::Sprite* m_status;
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMissionSelect : public CGuiScreen
+{
+public:
+ CGuiScreenMissionSelect( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMissionSelect();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+#ifdef RAD_WIN32
+ virtual eFEHotspotType CheckCursorAgainstHotspots( float x, float y );
+#endif
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void OnLevelSelectionChange( int currentLevel );
+ void UpdateMissionStatus( int index, MissionRecord* missionRecord );
+
+ CGuiMenu* m_pMenuLevel;
+ CGuiMenu* m_pMenu;
+ int m_numLevelSelections;
+
+#ifdef RAD_WIN32
+ Scrooby::Sprite* m_leftArrow;
+ Scrooby::Sprite* m_rightArrow;
+#endif
+
+ MissionDisplayInfo m_missionInfo[ MAX_NUM_REGULAR_MISSIONS ];
+
+};
+
+#endif // GUISCREENMISSIONSELECT_H
diff --git a/game/code/presentation/gui/ingame/guiscreenmissionsuccess.cpp b/game/code/presentation/gui/ingame/guiscreenmissionsuccess.cpp
new file mode 100644
index 0000000..a7879ee
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenmissionsuccess.cpp
@@ -0,0 +1,211 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMissionLoad
+//
+// Description: Implementation of the CGuiScreenMissionSuccess class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/04/07 ian gipson Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <mission/charactersheet/charactersheet.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/gameplaymanager.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenmissionsuccess.h>
+#include <group.h>
+#include <page.h>
+#include <screen.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMissionSuccess::CGuiScreenMissionSuccess
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMissionSuccess::CGuiScreenMissionSuccess
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+):
+ CGuiScreenMissionBase( pScreen, pParent, GUI_SCREEN_ID_MISSION_SUCCESS )
+{
+ Scrooby::Page* page = pScreen->GetPage( "MissionLoad" ); rAssert( page != NULL );
+ m_PattyAndSelmaTitle = page->GetText( "PattyAndSelmaTitle" ); rAssert( m_PattyAndSelmaTitle != NULL );
+ m_PattyAndSelmaTitle->SetTextMode( Scrooby::TEXT_WRAP );
+ m_PattyAndSelmaInfo = page->GetText( "PattyAndSelmaInfo" ); rAssert( m_PattyAndSelmaInfo != NULL );
+ m_PattyAndSelmaInfo->SetTextMode( Scrooby::TEXT_WRAP );
+ m_PattyAndSelmaTitle->SetVisible( false );
+ m_PattyAndSelmaInfo->SetVisible( false );
+}
+
+//===========================================================================
+// CGuiScreenMissionSuccess::HandleMessage
+//===========================================================================
+// Description: Handles messages, and passes them up the hierarchy when done
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionSuccess::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ // resume game and start mission
+ //
+ m_pParent->HandleMessage( GUI_MSG_RESUME_INGAME );
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ // resume game and abort mission
+ //
+ //m_pParent->HandleMessage( GUI_MSG_RESUME_INGAME, ON_HUD_ENTER_ABORT_MISSION );
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ CGuiScreenMissionBase::HandleMessage( message, param1, param2 );
+}
+
+//===========================================================================
+// CGuiScreenMissionSuccess::InitIntro
+//===========================================================================
+// Description: sets up the screen when you enter it
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionSuccess::InitIntro()
+{
+ //
+ // Check the level - level 7 rerquires the zombie picture
+ //
+ int level = GetGameplayManager()->GetCurrentLevelIndex();
+ if( level == 6 )
+ {
+ SetBitmapName( "art/frontend/dynaload/images/misXX_HW.p3d" );
+ }
+ else
+ {
+ SetBitmapName( "art/frontend/dynaload/images/misXX_PS.p3d" );
+ }
+ ReplaceBitmap();
+
+ //
+ // Call the parent's initIntro function
+ //
+ CGuiScreenMissionBase::InitIntro();
+
+ //
+ // Hide the "cancel bitmap"
+ //
+ Scrooby::Group* abortBitmap = GetAbortBitmap();
+ abortBitmap->SetVisible( false );
+
+ //
+ // Show the correct titles
+ //
+ m_PattyAndSelmaTitle->SetVisible( true );
+ m_PattyAndSelmaInfo->SetVisible( true );
+
+ //
+ // Figure out how many races have been completed, and display it
+ //
+ CurrentMissionStruct mission = GetCharacterSheetManager()->QueryCurrentMission();
+ int racesComplete = GetCharacterSheetManager()->QueryNumStreetRacesCompleted( mission.mLevel );
+ char outputString[ 256 ] = "";
+ if( racesComplete < 3 )
+ {
+ m_PattyAndSelmaTitle->SetIndex( 0 );
+ m_PattyAndSelmaInfo->SetIndex( 0 );
+ UnicodeString title = m_PattyAndSelmaTitle->GetString();
+ char numberString[ 256 ] = "";
+ sprintf( numberString, "%d", racesComplete );
+ title.Replace( "%d", numberString );
+ title.Replace( "%d", "3" );
+ m_PattyAndSelmaTitle->SetString( 2, title );
+ m_PattyAndSelmaTitle->SetIndex( 2 );
+
+ //
+ // Check if the info just says "*" if so, then hide it
+ //
+ UnicodeChar* uc = m_PattyAndSelmaInfo->GetStringBuffer();
+ if( uc[ 0 ] == '*' )
+ {
+ m_PattyAndSelmaInfo->SetVisible( false );
+ }
+ else
+ {
+ m_PattyAndSelmaInfo->SetVisible( true );
+ }
+ }
+ else
+ {
+ int level = GetGameplayManager()->GetCurrentLevelIndex();
+ m_PattyAndSelmaTitle->SetIndex( 1 );
+ m_PattyAndSelmaInfo->SetIndex( 1 + level );
+ }
+}
+
+//===========================================================================
+// CGuiScreenMissionSuccess::OutroDone
+//===========================================================================
+// Description: sets up the screen when you enter it
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMissionSuccess::OutroDone()
+{
+ m_PattyAndSelmaTitle->SetVisible( false );
+ m_PattyAndSelmaInfo->SetVisible( false );
+}
diff --git a/game/code/presentation/gui/ingame/guiscreenmissionsuccess.h b/game/code/presentation/gui/ingame/guiscreenmissionsuccess.h
new file mode 100644
index 0000000..fd79194
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenmissionsuccess.h
@@ -0,0 +1,47 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMissionSuccess
+//
+// Description:
+//
+//
+// Authors: Ian Gipson
+//
+// Revisions Date Author Revision
+// 2003/04/07 igipson Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMISSIONSUCCESS_H
+#define GUISCREENMISSIONSUCCESS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenmissionload.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMissionSuccess :
+ public CGuiScreenMissionBase
+{
+public:
+ CGuiScreenMissionSuccess( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+ virtual void InitIntro();
+protected:
+ virtual void OutroDone();
+private:
+ Scrooby::Text* m_PattyAndSelmaTitle;
+ Scrooby::Text* m_PattyAndSelmaInfo;
+};
+
+#endif // GUISCREENMISSIONSUCCESS_H
diff --git a/game/code/presentation/gui/ingame/guiscreenmultihud.cpp b/game/code/presentation/gui/ingame/guiscreenmultihud.cpp
new file mode 100644
index 0000000..3f76788
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenmultihud.cpp
@@ -0,0 +1,825 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMultiHud
+//
+// Description: Implementation of the CGuiScreenMultiHud class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/11/20 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/tutorialmanager.h>
+#include <presentation/gui/ingame/guiscreenmultihud.h>
+#include <presentation/gui/utility/hudmap.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/utility/transitions.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/presentation.h>
+#include <contexts/gameplay/gameplaycontext.h>
+#include <input/inputmanager.h>
+#include <gameflow/gameflow.h>
+#include <main/commandlineoptions.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactertarget.h>
+#include <worldsim/redbrick/vehicle.h>
+
+#include <raddebug.hpp> // Foundation
+
+#include <group.h>
+#include <screen.h>
+#include <page.h>
+#include <layer.h>
+#include <polygon.h>
+#include <sprite.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+#define SRR2_SHOW_SPEEDOMETER
+#define WHITE_WASH_IN_TIME 1000.0f
+
+const float ENTER_INGAME_FADE_IN_TIME = 500.0f; // in milliseconds
+const float transitionIn = 250.0f;
+const float minDisplayTime = 3000.0f;
+const float maxDisplayTime = 50000.0f;
+const float transitionOut = transitionIn;
+Scrooby::Polygon* g_WhiteWash = NULL;
+GuiSFX::Show g_WhiteWashShow( "WhiteWashShow" );
+GuiSFX::ColorChange g_WhiteWashIn( "WhiteWashIn" );
+GuiSFX::Pause g_WhiteWashPause( "WhiteWashPause" );
+GuiSFX::Hide g_WhiteWashHide( "WhiteWashHide" );
+GuiSFX::ResumeGame g_WhiteWashResumeGame( "WhiteWashResumeGame" );
+GuiSFX::PulseScale g_TutorialPulse( "TutorialPulse" );
+float g_TimeSinceLastDeathVolume = 0.0f;
+Vehicle* g_Vehicle = NULL;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+class ResetCar : public GuiSFX::SendEvent
+{
+public:
+ ResetCar();
+ void Activate();
+ void SetVehicle( Vehicle* vehicle );
+
+protected:
+ Vehicle* m_Vehicle;
+};
+ResetCar g_ResetCar;
+
+//===========================================================================
+// CGuiScreenMultiHud::CGuiScreenMultiHud
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMultiHud::CGuiScreenMultiHud
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent,
+ eGuiWindowID id,
+ int numActivePlayers
+)
+:
+ CGuiScreen( pScreen, pParent, id ),
+ m_numActivePlayers( numActivePlayers ),
+ m_tutorialWalkie( NULL ),
+ m_tutorialMessage( NULL ),
+ m_isStartButtonPressed( false ),
+ m_TutorialBitmapTimeShown( 0.0f )
+{
+ for( unsigned int i = 0; i < sizeof( m_hudMap ) /
+ sizeof( m_hudMap[ 0 ] ); i++ )
+ {
+ m_hudMap[ i ] = NULL;
+ }
+
+ Scrooby::Page* pPage;
+ pPage = m_pScroobyScreen->GetPage( "Hud" );
+ rAssert( pPage );
+
+ m_LetterboxPage = m_pScroobyScreen->GetPage( "LetterBox" );
+ m_LetterboxPage->SetVisible( false );
+ rAssert( m_LetterboxPage != NULL );
+
+ m_tutorialWalkie = pPage->GetGroup( "walkietalkie" );
+ rAssert( m_tutorialWalkie != NULL );
+ m_tutorialWalkie->SetVisible( false );
+
+ Scrooby::Sprite* bartImage = m_tutorialWalkie->GetSprite( "walkietalkie" );
+ if( bartImage != NULL )
+ {
+ bartImage->ResetTransformation();
+ bartImage->ScaleAboutCenter( 0.75f );
+ }
+
+ m_tutorialMessage = m_tutorialWalkie->GetSprite( "TutorialMessage" );
+ rAssert( m_tutorialMessage != NULL );
+ m_tutorialMessage->ScaleAboutCenter( 0.4f );
+ m_tutorialMessage->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ m_tutorialMessage->CreateBitmapTextBuffer( MAX_TUTORIAL_MESSAGE_LENGTH );
+
+ pPage = m_pScroobyScreen->GetPage( "WhiteCover" );
+ rAssert( pPage != NULL );
+
+ Scrooby::Layer* whiteOverlay = pPage->GetLayer( "WhiteOverlay" );
+ rAssert( whiteOverlay != NULL );
+ whiteOverlay->SetVisible( true );
+
+ g_WhiteWash = pPage->GetPolygon( "WhiteOverlay" );
+ rAssert( g_WhiteWash != NULL );
+ g_WhiteWash->SetVisible( false );
+
+ g_TutorialPulse.SetDrawable( m_tutorialWalkie );
+ g_TutorialPulse.SetAmplitude( 0.1f );
+ g_TutorialPulse.SetFrequency( 2.5f );
+ g_TutorialPulse.Activate();
+ g_WhiteWashShow.SetDrawable( g_WhiteWash );
+ g_WhiteWashShow.SetNextTransition( g_WhiteWashIn );
+ g_WhiteWashIn.SetDrawable( g_WhiteWash );
+ g_WhiteWashIn.SetTimeInterval( WHITE_WASH_IN_TIME );
+ g_WhiteWashIn.SetStartColour( tColour( 255, 255, 255, 0 ) );
+ g_WhiteWashIn.SetEndColour( tColour( 255, 255, 255, 255 ) );
+ g_ResetCar.SetEvent( EVENT_DEATH_VOLUME_SCREEN_BLANK );
+ g_ResetCar.SetEventData( NULL );
+
+
+
+ g_WhiteWashIn.SetNextTransition( g_ResetCar );
+ g_ResetCar.SetNextTransition( g_WhiteWashResumeGame );
+ g_WhiteWashResumeGame.SetNextTransition( g_WhiteWashPause );
+ g_WhiteWashPause.SetTimeInterval( 250.0f );
+ g_WhiteWashPause.SetNextTransition( g_WhiteWashHide );
+ g_WhiteWashHide.SetDrawable( g_WhiteWash );
+ g_WhiteWashHide.SetNextTransition( NULL );
+
+ int xMin;
+ int xMax;
+ int yMin;
+ int yMax;
+ m_tutorialWalkie->GetBoundingBox( xMin, yMin, xMax, yMax );
+ int height = yMax - yMin;
+
+ //
+ // Set up transitions for the tutorial bitmap
+ //
+ m_TutorialBitmapTransitionIn.SetDrawable( m_tutorialWalkie );
+ m_TutorialBitmapTransitionIn.SetFrequency( 5.0f );
+ m_TutorialBitmapTransitionIn.SetTimeInterval( transitionIn * 2.0f );
+ m_TutorialBitmapTransitionIn.SetStartOffscreenTop( m_tutorialWalkie );
+ m_TutorialBitmapTransitionIn.SetCoordsEnd( 0, 0 );
+
+ m_TutorialBitmapSteadyState.SetDrawable( m_tutorialWalkie );
+ m_TutorialBitmapSteadyState.SetTimeInterval( maxDisplayTime );
+ m_TutorialBitmapSteadyState.SetCoordsStart( 0, 0 );
+ m_TutorialBitmapSteadyState.SetCoordsEnd( 0, 0 );
+
+ m_TutorialBitmapTransitionOut.SetDrawable( m_tutorialWalkie );
+ m_TutorialBitmapTransitionOut.SetTimeInterval( transitionOut );
+ m_TutorialBitmapTransitionOut.SetCoordsStart( 0, 0 );
+ m_TutorialBitmapTransitionOut.SetEndOffscreenTop( m_tutorialWalkie );
+
+ m_TurorialBitmapStayOut.SetDrawable( m_tutorialWalkie );
+ m_TurorialBitmapStayOut.SetTimeInterval( FLT_MAX );
+ m_TurorialBitmapStayOut.SetCoordsStart( 0, -height * 2 - yMin );
+ m_TurorialBitmapStayOut.SetCoordsEnd( 0, -height * 2 - yMin );
+
+ //
+ // Chain the transitions together
+ //
+ m_TutorialBitmapTransitionIn.SetNextTransition( m_TutorialBitmapSteadyState );
+ m_TutorialBitmapSteadyState.SetNextTransition( m_TutorialBitmapTransitionOut );
+ m_TutorialBitmapTransitionOut.SetNextTransition( m_TurorialBitmapStayOut );
+
+ // override default fade time
+ this->SetFadeTime( ENTER_INGAME_FADE_IN_TIME );
+}
+
+CGuiScreenMultiHud::CGuiScreenMultiHud
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+:
+ CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_MULTI_HUD ),
+ m_numActivePlayers( MAX_PLAYERS )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage;
+ pPage = m_pScroobyScreen->GetPage( "MultiHud" );
+ rAssert( pPage );
+
+ // Get dynamic elements
+ this->RetrieveElements( pPage );
+}
+
+
+//===========================================================================
+// CGuiScreenMultiHud::~CGuiScreenMultiHud
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMultiHud::~CGuiScreenMultiHud()
+{
+ for( unsigned int i = 0; i < sizeof( m_hudMap ) /
+ sizeof( m_hudMap[ 0 ] ); i++ )
+ {
+ if( m_hudMap[ i ] != NULL )
+ {
+ delete m_hudMap[ i ];
+ m_hudMap[ i ] = NULL;
+ }
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMultiHud::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiHud::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ // update HUD elements for all players
+ this->UpdateElements( param1 );
+
+ if( m_isStartButtonPressed )
+ {
+ m_isStartButtonPressed = false;
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ m_pParent->HandleMessage( GUI_MSG_PAUSE_INGAME );
+ }
+ }
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_START:
+ {
+ // queue this up to be processed in the next update frame
+ //
+ m_isStartButtonPressed = true;
+
+ break;
+ }
+
+#ifdef RAD_WIN32
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ // This is our start button for PC.
+ if( GetInputManager()->GetValue( 0, InputManager::KeyboardEsc ) > 0.0f )
+ {
+ m_isStartButtonPressed = true;
+ }
+ break;
+ }
+#endif
+
+ case GUI_MSG_DEATH_VOLUME_START:
+ {
+ //
+ // Need to start the transition for the death volume
+ //
+ if( g_TimeSinceLastDeathVolume > WHITE_WASH_IN_TIME + 1000.0 && GetGameFlow()->GetCurrentContext() != CONTEXT_PAUSE && GetGameFlow()->GetNextContext() != CONTEXT_PAUSE)
+ {
+ g_ResetCar.SetEventData( reinterpret_cast< void* >( param1 ) );
+ GameplayContext::GetInstance()->PauseAllButPresentation( true );
+ g_WhiteWashShow.Reset();
+ g_WhiteWashIn.Reset();
+ g_WhiteWashHide.Reset();
+ g_WhiteWashPause.Reset();
+ g_WhiteWashShow.Activate();
+ g_TimeSinceLastDeathVolume = 0.0f;
+ }
+ break;
+ }
+
+ case GUI_MSG_MANUAL_RESET:
+ {
+ //
+ // Need to start the transition for the death volume
+ //
+ if( g_TimeSinceLastDeathVolume > WHITE_WASH_IN_TIME + 1000.0 && GetGameFlow()->GetCurrentContext() != CONTEXT_PAUSE && GetGameFlow()->GetNextContext() != CONTEXT_PAUSE)
+ {
+ g_ResetCar.SetVehicle( reinterpret_cast< Vehicle* >( param1 ) );
+ g_ResetCar.SetEventData( NULL );
+ GameplayContext::GetInstance()->PauseAllButPresentation( true );
+ g_WhiteWashShow.Reset();
+ g_WhiteWashIn.Reset();
+ g_WhiteWashHide.Reset();
+ g_WhiteWashPause.Reset();
+ g_WhiteWashShow.Activate();
+ g_TimeSinceLastDeathVolume = 0.0f;
+ }
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenMultiHud::GetHudMap
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CHudMap*
+CGuiScreenMultiHud::GetHudMap( unsigned int playerID ) const
+{
+ rAssert( playerID < static_cast<unsigned int>( m_numActivePlayers ) );
+
+ return m_hudMap[ playerID ];
+}
+
+
+//===========================================================================
+// CGuiScreenMultiHud::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiHud::InitIntro()
+{
+ for( int i = 0; i < m_numActivePlayers; i++ )
+ {
+ Avatar* pAvatar = GetAvatarManager()->GetAvatarForPlayer( i );
+ rAssert( pAvatar );
+
+ rAssert( m_hudMap[ i ] );
+ m_hudMap[ i ]->SetCameraTarget( pAvatar->GetCharacter()->GetTarget() );
+ m_hudMap[ i ]->Reset();
+/*
+ m_hudMap[ i ]->RegisterIcon( HudMapIcon::ICON_PLAYER,
+ rmt::Vector( 0, 0, 0 ),
+ pAvatar->GetCharacter()->GetTarget() );
+*/
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMultiHud::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiHud::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenMultiHud::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiHud::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenMultiHud::RetrieveElements( Scrooby::Page* pPage )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenMultiHud" );
+ char objectName[ 32 ];
+ for( int i = 0; i < m_numActivePlayers; i++ )
+ {
+#ifdef SRR2_SHOW_SPEEDOMETER
+ sprintf( objectName, "Speed%d", i );
+ Scrooby::Group* speedGroup = pPage->GetGroup( objectName );
+
+ rAssert( speedGroup != NULL );
+ m_speedDigital[ i ].SetScroobyText( speedGroup, objectName );
+// m_speedDigital[ i ].SetColour( tColour( 255, 255, 255 ) );
+
+ // speedometer is turned on w/ commandline option "speedometer"
+ //
+ m_speedDigital[ i ].SetVisible( CommandLineOptions::Get( CLO_SHOW_SPEED ) );
+
+ // hide speed units in final build
+ #ifdef FINAL
+ sprintf( objectName, "SpeedUnit%d", i );
+ Scrooby::Text* speedUnit = speedGroup->GetText( objectName );
+ if( speedUnit != NULL )
+ {
+ speedUnit->SetVisible( false );
+ }
+ #endif
+#endif
+
+ char p3dFile[ 16 ];
+ sprintf( p3dFile, "l%dhudmap", GetGameplayManager()->GetCurrentLevelIndex() + 1 );
+ m_hudMap[ i ] = new CHudMap( pPage, i, p3dFile );
+ rAssert( m_hudMap[ i ] != NULL );
+
+ // Get Hud Map Icons
+ //
+ Scrooby::Group* playerCars = pPage->GetGroup( "PlayerCars" );
+ Scrooby::Group* aiCarsGroup = pPage->GetGroup( "AICars" );
+ Scrooby::Group* aiHarassCars = pPage->GetGroup( "AIHarassCars" );
+ Scrooby::Group* waypointFlags = pPage->GetGroup( "WaypointFlags" );
+ Scrooby::Group* checkeredFlags = pPage->GetGroup( "CheckeredFlags" );
+ Scrooby::Group* collectables = pPage->GetGroup( "Collectibles" );
+ Scrooby::Group* icons0 = pPage->GetGroup( "Icons0" );
+ Scrooby::Group* missions = pPage->GetGroup( "Missions" );
+ Scrooby::Group* bonusMissions = pPage->GetGroup( "BonusMissions" );
+ Scrooby::Group* streetRaces = pPage->GetGroup( "StreetRaces" );
+ Scrooby::Group* wagerRaces = pPage->GetGroup( "WagerRaces" );
+ Scrooby::Group* purchaseCentres = pPage->GetGroup( "PurchaseCentres" );
+ Scrooby::Group* phoneBooths = pPage->GetGroup( "PhoneBooths" );
+ rAssert( playerCars != NULL );
+ rAssert( aiCarsGroup != NULL );
+ rAssert( aiHarassCars != NULL );
+ rAssert( waypointFlags != NULL );
+ rAssert( checkeredFlags != NULL );
+ rAssert( collectables != NULL );
+ rAssert( icons0 != NULL );
+ rAssert( missions != NULL );
+ rAssert( bonusMissions != NULL );
+ rAssert( streetRaces != NULL );
+ rAssert( wagerRaces != NULL );
+ rAssert( purchaseCentres != NULL );
+ rAssert( phoneBooths != NULL );
+
+ sprintf( objectName, "IconPlayer%d", i );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_PLAYER, icons0->GetSprite( objectName ) );
+
+ for( unsigned int j = 0; j < MAX_NUM_ICONS_PER_TYPE; j++ )
+ {
+ sprintf( objectName, "IconMission%d_%d", i, j );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_MISSION, missions->GetSprite( objectName ) );
+
+ sprintf( objectName, "IconCollectible%d_%d", i, j );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_COLLECTIBLE, collectables->GetSprite( objectName ) );
+
+ sprintf( objectName, "IconPlayerCar%d_%d", i, j );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_PLAYER_CAR, playerCars->GetSprite( objectName ) );
+
+ sprintf( objectName, "IconAICar%d_%d", i, j );
+ Scrooby::Sprite* aiCarIcon = aiCarsGroup->GetSprite( objectName );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_AI_CHASE, aiCarIcon );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_AI_RACE, aiCarIcon );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_AI_EVADE, aiCarIcon );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_AI_TARGET, aiCarIcon );
+
+ sprintf( objectName, "IconHarass%d_%d", i, j );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_AI_HIT_N_RUN, aiHarassCars->GetSprite( objectName ) );
+
+ sprintf( objectName, "IconWaypoint%d_%d", i, j );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_FLAG_WAYPOINT, waypointFlags->GetSprite( objectName ) );
+
+ sprintf( objectName, "IconChecker%d_%d", i, j );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_FLAG_CHECKERED, checkeredFlags->GetSprite( objectName ) );
+
+ sprintf( objectName, "BonusMission%d_%d", i, j );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_BONUS_MISSION, bonusMissions->GetSprite( objectName ) );
+
+ sprintf( objectName, "StreetRace%d_%d", i, j );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_STREET_RACE, streetRaces->GetSprite( objectName ) );
+
+ sprintf( objectName, "WagerRace%d_%d", i, j );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_WAGER_RACE, wagerRaces->GetSprite( objectName ) );
+
+ sprintf( objectName, "PhoneBooth%d_%d", i, j );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_PHONE_BOOTH, phoneBooths->GetSprite( objectName ) );
+
+ sprintf( objectName, "PurchaseCentre%d_%d", i, j );
+ m_hudMap[ i ]->AddIconToInventory( HudMapIcon::ICON_PURCHASE_CENTRE, purchaseCentres->GetSprite( objectName ) );
+ }
+
+ m_hudMap[ i ]->RestoreAllRegisteredIcons();
+ }
+MEMTRACK_POP_GROUP("CGUIScreenMultiHud");
+}
+
+void
+CGuiScreenMultiHud::UpdateElements( unsigned int elapsedTime )
+{
+ float elapsedTimeFloat = static_cast< float >( elapsedTime );
+ g_TimeSinceLastDeathVolume += elapsedTimeFloat;
+ g_WhiteWashIn.Update( elapsedTimeFloat );
+ g_WhiteWashPause.Update( elapsedTimeFloat );
+ g_TutorialPulse.Update( elapsedTimeFloat );
+
+ for( int i = 0; i < m_numActivePlayers; i++ )
+ {
+#ifdef SRR2_SHOW_SPEEDOMETER
+ // update speedometer
+ //
+ if( CommandLineOptions::Get( CLO_SHOW_SPEED ) ||
+ GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_SHOW_SPEEDOMETER ) )
+ {
+ unsigned int speed = 0;
+
+ Avatar* pAvatar = GetAvatarManager()->GetAvatarForPlayer( i );
+ rAssert( pAvatar );
+ if( pAvatar->IsInCar() )
+ {
+ Vehicle* vehicle = pAvatar->GetVehicle();
+ rAssert( vehicle );
+
+ speed = (unsigned int)vehicle->GetSpeedKmh();
+ }
+ else
+ {
+ Character* pCharacter = pAvatar->GetCharacter();
+ rAssert( pCharacter );
+
+ speed = (unsigned int)pCharacter->GetSpeed();
+ }
+
+ m_speedDigital[ i ].SetVisible( true );
+ m_speedDigital[ i ].SetValue( speed );
+ }
+ else
+ {
+ m_speedDigital[ i ].SetVisible( false );
+ }
+#endif // SRR2_SHOW_SPEEDOMETER
+
+ // update hud map
+ //
+ rAssert( m_hudMap[ i ] );
+ m_hudMap[ i ]->Update( elapsedTime );
+ }
+}
+
+//===========================================================================
+// CGuiScreenHud::TutorialBitmapHide
+//===========================================================================
+// Description: hides the tutorial mode bitmap
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiHud::TutorialBitmapHide()
+{
+ m_tutorialWalkie->SetVisible( false );
+ m_TutorialBitmapVisible = false;
+}
+
+//===========================================================================
+// CGuiScreenHud::TutorialBitmapInitOutro
+//===========================================================================
+// Description: starts the outro transition of the tutorial bitmap
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiHud::TutorialBitmapInitOutro()
+{
+ m_TutorialBitmapTransitionIn.Deactivate();
+ m_TutorialBitmapSteadyState.Deactivate();
+ m_TutorialBitmapTransitionOut.Activate();
+ m_TurorialBitmapStayOut.Deactivate();
+ m_TutorialBitmapTimeShown = maxDisplayTime;
+}
+//===========================================================================
+// CGuiScreenHud::TutorialBitmapShow
+//===========================================================================
+// Description: shows the tutorial mode bitmap.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiHud::TutorialBitmapShow()
+{
+ m_tutorialWalkie->SetVisible( true );
+ m_TutorialBitmapVisible = true;
+ m_TutorialBitmapTimeShown = 0.0f;
+ m_TutorialBitmapTransitionIn.Reset();
+ m_TutorialBitmapTransitionOut.Reset();
+ m_TutorialBitmapSteadyState.Reset();
+ m_TutorialBitmapTransitionIn.Activate();
+ m_TutorialBitmapTransitionOut.Deactivate();
+ m_TutorialBitmapSteadyState.Deactivate();
+ m_TurorialBitmapStayOut.Deactivate();
+}
+
+//===========================================================================
+// CGuiScreenHud::TutorialBitmapHide
+//===========================================================================
+// Description: hides the tutorial mode bitmap
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiHud::UpdateTutorialMode( const float deltaT )
+{
+ m_tutorialWalkie->ResetTransformation();
+ m_TutorialBitmapTransitionIn.Update( deltaT );
+ m_TutorialBitmapSteadyState.Update( deltaT );
+ if( m_TutorialBitmapTransitionOut.IsActive() )
+ {
+ m_TutorialBitmapTransitionOut.Update( deltaT );
+ if( m_TutorialBitmapTransitionOut.IsDone() )
+ {
+ this->TutorialBitmapHide();
+ }
+ }
+ m_TurorialBitmapStayOut.Update( deltaT );
+}
+
+//===========================================================================
+// CGuiScreenMultiHud::SetTutorialMessage
+//===========================================================================
+// Description: hides the tutorial mode bitmap
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiHud::SetTutorialMessage( int index )
+{
+ char textBibleID[ 32 ];
+ sprintf( textBibleID, "TUTORIAL_%03d", index );
+
+ rAssert( m_tutorialMessage != NULL );
+ m_tutorialMessage->SetBitmapText( GetTextBibleString( textBibleID ) );
+}
+
+//===========================================================================
+// CGuiScreenMultiHud::ShowLetterBox
+//===========================================================================
+// Description: shows the letterbox
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMultiHud::ShowLetterBox()
+{
+ m_LetterboxPage->SetVisible( true );
+}
+
+//===========================================================================
+// ResetCar::ResetCar
+//===========================================================================
+// Description: constructor for the reset car transition
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+ResetCar::ResetCar()
+{
+}
+
+//===========================================================================
+// ResetCar::Activate
+//===========================================================================
+// Description: Activate - does the work of the reset car trasntition
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void ResetCar::Activate()
+{
+ if( this->mEventData == NULL )
+ {
+ bool manualDamageReset = CommandLineOptions::Get( CLO_MANUAL_RESET_DAMAGE );
+ rAssert( m_Vehicle != NULL );
+ if( m_Vehicle != NULL )
+ {
+ m_Vehicle->ResetOnSpot( manualDamageReset );
+ m_Vehicle = NULL;
+ }
+ }
+ else
+ {
+ SendEvent::Activate();
+ }
+ ContinueChain();
+}
+
+//===========================================================================
+// ResetCar::SetVehicle
+//===========================================================================
+// Description: sets the vehicle that's going to get reset
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void ResetCar::SetVehicle( Vehicle* vehicle )
+{
+ m_Vehicle = vehicle;
+}
diff --git a/game/code/presentation/gui/ingame/guiscreenmultihud.h b/game/code/presentation/gui/ingame/guiscreenmultihud.h
new file mode 100644
index 0000000..b6aff7c
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenmultihud.h
@@ -0,0 +1,91 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMultiHud
+//
+// Description:
+//
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/11/20 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMULTIHUD_H
+#define GUISCREENMULTIHUD_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/utility/numerictext.h>
+#include <presentation/gui/utility/slider.h>
+#include <presentation/gui/utility/transitions.h>
+#include <constants/maxplayers.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CHudMap;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMultiHud : public CGuiScreen
+{
+public:
+ CGuiScreenMultiHud( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMultiHud();
+
+ CGuiScreenMultiHud( Scrooby::Screen* pScreen, CGuiEntity* pParent,
+ eGuiWindowID id, int numActivePlayers );
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CHudMap* GetHudMap( unsigned int playerID ) const;
+
+ void TutorialBitmapShow();
+ void TutorialBitmapHide();
+ void TutorialBitmapInitOutro();
+ void SetTutorialMessage( int index );
+ void ShowLetterBox();
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ void RetrieveElements( Scrooby::Page* pPage );
+ void UpdateElements( unsigned int elapsedTime );
+ void UpdateTutorialMode( const float deltaT );
+
+ int m_numActivePlayers;
+
+ NumericText<Scrooby::Text> m_speedDigital[ MAX_PLAYERS ];
+
+ static const int MAX_TUTORIAL_MESSAGE_LENGTH = 256;
+
+ ImageSlider m_gasMeter[ MAX_PLAYERS ];
+ CHudMap* m_hudMap[ MAX_PLAYERS ];
+ Scrooby::Group* m_tutorialWalkie;
+ Scrooby::Sprite* m_tutorialMessage;
+ bool m_TutorialBitmapVisible : 1;
+ bool m_isStartButtonPressed : 1;
+ float m_TutorialBitmapTimeShown;
+ GuiSFX::UnderdampedTranslator m_TutorialBitmapTransitionIn;
+ GuiSFX::Translator m_TutorialBitmapSteadyState;
+ GuiSFX::Translator m_TutorialBitmapTransitionOut;
+ GuiSFX::Translator m_TurorialBitmapStayOut;
+ Scrooby::Page* m_LetterboxPage;
+
+};
+
+
+#endif // GUISCREENMULTIHUD_H
diff --git a/game/code/presentation/gui/ingame/guiscreenpause.cpp b/game/code/presentation/gui/ingame/guiscreenpause.cpp
new file mode 100644
index 0000000..1962dbc
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpause.cpp
@@ -0,0 +1,646 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPause
+//
+// Description: Implementation of the CGuiScreenPause class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/11/20 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenpause.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guihudtextbox.h>
+#include <presentation/gui/ingame/hudevents/hudmissionobjective.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guiscreenprompt.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <data/memcard/memorycardmanager.h>
+
+#include <cards/card.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <worldsim/coins/coinmanager.h>
+
+// Pure3D
+#include <p3d/utility.hpp>
+#include <p3d/sprite.hpp>
+
+#include <raddebug.hpp> // Foundation
+#include <radtime.hpp>
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <app.h>
+#include <group.h>
+#include <layer.h>
+#include <page.h>
+#include <screen.h>
+#include <sprite.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenPause::CGuiScreenPause
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPause::CGuiScreenPause
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent,
+ eGuiWindowID id
+)
+: CGuiScreen( pScreen, pParent, id ),
+ m_pMenu( NULL ),
+ m_MissionObjectiveBox( NULL ),
+ m_pressStart( NULL ),
+ m_missionObjective( NULL ),
+ m_objectiveIcon( NULL ),
+ m_numVisibleCards( NUM_CARDS_PER_LEVEL ),
+ m_elapsedTime( 0 )
+{
+ memset( m_collectedCards, 0, sizeof( m_collectedCards ) );
+/*
+ // Retrieve the Scrooby drawing elements (from Pause Cards page).
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "PauseCards" );
+ rAssert( pPage );
+
+ Scrooby::Group* collectableCards = pPage->GetGroup( "CollectedCards" );
+ rAssert( collectableCards != NULL );
+
+ char cardName[ 32 ];
+ for( unsigned int i = 0; i < sizeof( m_collectedCards ) /
+ sizeof( m_collectedCards[ 0 ] ); i++ )
+ {
+ sprintf( cardName, "Card%d", i );
+ m_collectedCards[ i ] = collectableCards->GetSprite( cardName );
+ rAssert( m_collectedCards[ i ] );
+ }
+*/
+ // Retrieve the Scrooby drawing elements (from Coins page).
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "Coins" );
+ rAssert( pPage != NULL );
+ CGuiScreenHud::SetNumCoinsDisplay( pPage->GetSprite( "NumCoins" ) );
+
+ // Retrieve the Scrooby drawing elements (from PauseMissionObjective page).
+ //
+ pPage = m_pScroobyScreen->GetPage( "PauseMissionObjective" );
+ rAssert( pPage != NULL );
+
+ Scrooby::Group* pGroup = pPage->GetGroup( "MissionObjective" );
+ m_MissionObjectiveBox = pGroup;
+ rAssert( pGroup != NULL );
+
+ m_missionObjective = pGroup->GetText( "MissionObjective" );
+ rAssert( m_missionObjective != NULL );
+ m_missionObjective->SetTextMode( Scrooby::TEXT_WRAP );
+ m_missionObjective->ResetTransformation();
+ m_missionObjective->Translate( 0, MESSAGE_TEXT_VERTICAL_TRANSLATION );
+ m_missionObjective->ScaleAboutPoint( MESSAGE_TEXT_SCALE * MESSGAE_TEXT_HORIZONTAL_STRETCH,
+ MESSAGE_TEXT_SCALE,
+ 1.0f, 0, 0 );
+
+ m_objectiveIcon = pGroup->GetSprite( "ObjectiveIcon" );
+ rAssert( m_objectiveIcon != NULL );
+ m_objectiveIcon->SetVisible( false );
+ m_objectiveIcon->ResetTransformation();
+ m_objectiveIcon->ScaleAboutCenter( MISSION_ICON_SCALE );
+
+ // apply correction scale to message box
+ //
+ Scrooby::Sprite* messageBox = pGroup->GetSprite( "MessageBox" );
+ rAssert( messageBox != NULL );
+ messageBox->ResetTransformation();
+ messageBox->ScaleAboutCenter( MESSAGE_BOX_CORRECTION_SCALE * MESSAGE_BOX_HORIZONTAL_STRETCH,
+ MESSAGE_BOX_CORRECTION_SCALE * MESSAGE_BOX_VERTICAL_STRETCH,
+ 1.0f );
+
+ // Retrieve the Scrooby drawing elements (from Pause Foreground page).
+ //
+ pPage = m_pScroobyScreen->GetPage( "PauseFgd" );
+ if( pPage != NULL )
+ {
+#ifdef RAD_WIN32
+ pPage->SetVisible( false );
+#endif
+
+ // Wrap "Press Start" help text
+ //
+ m_pressStart = pPage->GetText( "PressStartResumePlay" );
+ if( m_pressStart != NULL )
+ {
+ m_pressStart->SetTextMode( Scrooby::TEXT_WRAP );
+
+ // add text outline
+ //
+ m_pressStart->SetDisplayOutline( true );
+
+ // set platform-specific text
+ //
+ m_pressStart->SetIndex( PLATFORM_TEXT_INDEX );
+ }
+ }
+
+ this->AutoScaleFrame( m_pScroobyScreen->GetPage( "XSmallBoard" ) );
+
+ this->SetZoomingEnabled( true );
+
+ #ifdef DEBUGWATCH
+ const char* screenName = GetWatcherName();
+ m_MissionObjectiveBox->WatchAll( screenName );
+ m_objectiveIcon-> WatchAll( screenName );
+ m_missionObjective-> WatchAll( screenName );
+ m_pressStart-> WatchAll( screenName );
+ #endif
+}
+
+
+//===========================================================================
+// CGuiScreenPause::~CGuiScreenPause
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPause::~CGuiScreenPause()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenPause::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPause::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+#ifdef RAD_WIN32
+ if( message == GUI_MSG_MENU_PROMPT_RESPONSE &&
+ param1 == PROMPT_CONFIRM_QUIT_TO_SYSTEM )
+ {
+ switch( param2 )
+ {
+ case (CGuiMenuPrompt::RESPONSE_YES):
+ {
+ // Tell the current screen to shut down.
+ //
+ m_pParent->HandleMessage( GUI_MSG_QUIT_TO_SYSTEM );
+
+ break;
+ }
+
+ case (CGuiMenuPrompt::RESPONSE_NO):
+ {
+ this->ReloadScreen();
+
+ break;
+ }
+
+ default:
+ {
+ rAssertMsg( 0, "WARNING: *** Invalid prompt response!\n" );
+
+ break;
+ }
+ }
+ }
+#endif
+
+ if( message == GUI_MSG_MENU_PROMPT_RESPONSE &&
+ param1 == PROMPT_CONFIRM_QUIT )
+ {
+ switch( param2 )
+ {
+ case (CGuiMenuPrompt::RESPONSE_YES):
+ {
+ m_pParent->HandleMessage( GUI_MSG_QUIT_INGAME );
+
+ break;
+ }
+
+ case (CGuiMenuPrompt::RESPONSE_NO):
+ {
+ this->ReloadScreen();
+
+ break;
+ }
+
+ default:
+ {
+ rAssertMsg( 0, "WARNING: *** Invalid prompt response!\n" );
+
+ break;
+ }
+ }
+ }
+
+ if ( message==GUI_MSG_PROMPT_START_RESPONSE )
+ {
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+ }
+
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+
+ case GUI_MSG_UPDATE:
+ {
+ GetMemoryCardManager()->Update( param1 );
+ HudMissionObjective* hudMissionObjective = static_cast<HudMissionObjective*>( GetCurrentHud()->GetEventHandler( CGuiScreenHud::HUD_EVENT_HANDLER_MISSION_OBJECTIVE ) );
+ hudMissionObjective->UpdateIcon();
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_START:
+ {
+ if( !m_pMenu->HasSelectionBeenMade() )
+ {
+ this->HandleResumeGame();
+ }
+
+ break;
+ }
+
+#ifdef RAD_WIN32
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ // This is our start button for PC.
+ this->HandleResumeGame();
+ break;
+ }
+#endif
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ //
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+ else if( m_state == GUI_WINDOW_STATE_OUTRO )
+ {
+ if( m_numTransitionsPending <= 0 )
+ {
+ // restore press start text
+ //
+ if( m_pressStart != NULL )
+ {
+ m_pressStart->ResetTransformation();
+ m_pressStart->SetHorizontalJustification( Scrooby::Left );
+ }
+
+ rAssert( m_objectiveIcon != NULL );
+ m_objectiveIcon->SetRawSprite( NULL );
+ m_objectiveIcon->SetVisible( false );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenPause::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPause::InitIntro()
+{
+/*
+ // update collect cards display for current level
+ //
+ unsigned int currentLevel = GetGameplayManager()->GetCurrentLevelIndex();
+ const CardList* collectedCards = GetCardGallery()->GetCollectedCards( currentLevel );
+ rAssert( collectedCards );
+
+ for( unsigned int i = 0; i < sizeof( m_collectedCards ) /
+ sizeof( m_collectedCards[ 0 ] ); i++ )
+ {
+ rAssert( m_collectedCards[ i ] );
+
+ if( collectedCards->m_cards[ i ] != NULL )
+ {
+ unsigned int cardID = collectedCards->m_cards[ i ]->GetID();
+ rAssert( cardID < static_cast<unsigned int>( m_collectedCards[ i ]->GetNumOfImages() ) );
+ m_collectedCards[ i ]->SetIndex( cardID + 1 );
+ }
+ else
+ {
+ m_collectedCards[ i ]->SetIndex( 0 );
+ }
+ }
+
+ this->HideCards();
+*/
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ if( currentMission != NULL )
+ {
+ MissionStage* currentStage = currentMission->GetCurrentStage();
+ if( currentStage != NULL )
+ {
+ // update mission objective text
+ //
+ rAssert( m_missionObjective != NULL );
+
+ int currentMissionObjective = currentStage->GetStartMessageIndex();
+ if( currentMissionObjective >= 0 ) // && currentStage->GetMissionLocked() )
+ {
+ rAssert( currentMissionObjective < m_missionObjective->GetNumOfStrings() );
+ m_missionObjective->SetIndex( currentMissionObjective );
+ m_missionObjective->SetVisible( true );
+ m_MissionObjectiveBox->SetVisible( true );
+ }
+ else
+ {
+ rWarningMsg( false, "Invalid mission objective message index!" );
+ m_missionObjective->SetVisible( false );
+ m_MissionObjectiveBox->SetVisible( false );
+ }
+
+ // update mission objective icon
+ //
+ tSprite* pSprite = NULL;
+ const char* iconName = currentStage->GetHUDIcon();
+ if( iconName[ 0 ] != '\0' )
+ {
+ pSprite = p3d::find<tSprite>( iconName );
+ rTuneWarningMsg( pSprite != NULL, "Can't find mission objective icon!" );
+ }
+
+ rAssert( m_objectiveIcon != NULL );
+ m_objectiveIcon->SetVisible( pSprite != NULL );
+ m_objectiveIcon->SetRawSprite( pSprite, true );
+ }
+ else
+ {
+ m_MissionObjectiveBox->SetVisible( false );
+ }
+ }
+ else
+ {
+ m_MissionObjectiveBox->SetVisible( false );
+ }
+
+ // move press start text to bottom center of pause menu box
+ //
+ if( m_pressStart != NULL )
+ {
+ int centerX, centerY;
+ m_pressStart->GetBoundingBoxCenter( centerX, centerY );
+
+ int screenCenterX = static_cast<int>( Scrooby::App::GetInstance()->GetScreenWidth() / 2 );
+
+ m_pressStart->ResetTransformation();
+ m_pressStart->Translate( screenCenterX - centerX, 85 );
+ m_pressStart->SetHorizontalJustification( Scrooby::Centre );
+ }
+
+ // reset pause menu to default selection
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->Reset();
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenPause::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPause::InitRunning()
+{
+ // show coins
+ //
+ CGuiScreenHud::UpdateNumCoinsDisplay( GetCoinManager()->GetBankValue() );
+
+ // disable screen zooming
+ //
+ this->SetZoomingEnabled( false );
+}
+
+
+//===========================================================================
+// CGuiScreenPause::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPause::InitOutro()
+{
+ // hide coins
+ //
+ CGuiScreenHud::UpdateNumCoinsDisplay( 0, false );
+}
+
+
+//===========================================================================
+// CGuiScreenPause::HandleResumeGame
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPause::HandleResumeGame( unsigned int param1,
+ unsigned int param2 )
+{
+ // enable zoom-out when returning to game
+ //
+ this->SetZoomingEnabled( true );
+
+ // resume game
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenPause::HandleQuitGame
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPause::HandleQuitGame()
+{
+ rAssert( m_guiManager );
+ m_guiManager->DisplayPrompt( PROMPT_CONFIRM_QUIT, this );
+}
+
+#ifdef RAD_WIN32
+//===========================================================================
+// CGuiScreenPause::HandleQuitToSystem
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPause::HandleQuitToSystem()
+{
+ rAssert( m_guiManager );
+ m_guiManager->DisplayPrompt( PROMPT_CONFIRM_QUIT_TO_SYSTEM, this );
+}
+#endif
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+/*
+void
+CGuiScreenPause::HideCards()
+{
+ // hide all cards
+ //
+ for( unsigned int i = 0; i < NUM_CARDS_PER_LEVEL; i++ )
+ {
+ rAssert( m_collectedCards[ i ] );
+ m_collectedCards[ i ]->SetVisible( false );
+ }
+
+ // set num visible cards to zero
+ //
+ m_numVisibleCards = 0;
+}
+
+void
+CGuiScreenPause::ShowNextCard()
+{
+ // randomize next card to show
+ //
+ int nextCard = 0;
+
+ int numHiddenCards = NUM_CARDS_PER_LEVEL - m_numVisibleCards;
+ if( numHiddenCards > 1 )
+ {
+ nextCard = rand() % numHiddenCards;
+ }
+
+ // show next card
+ //
+ for( unsigned int i = 0; i < NUM_CARDS_PER_LEVEL; i++ )
+ {
+ rAssert( m_collectedCards[ i ] );
+
+ if( m_collectedCards[ i ]->IsVisible() )
+ {
+ continue;
+ }
+ else
+ {
+ if( nextCard == 0 )
+ {
+ m_collectedCards[ i ]->SetVisible( true );
+ break;
+ }
+ else
+ {
+ nextCard--;
+ }
+ }
+ }
+
+ m_numVisibleCards++;
+}
+*/
+
+#ifdef DEBUGWATCH
+const char* CGuiScreenPause::GetWatcherName() const
+{
+ return "GuiScreenPause";
+}
+#endif
diff --git a/game/code/presentation/gui/ingame/guiscreenpause.h b/game/code/presentation/gui/ingame/guiscreenpause.h
new file mode 100644
index 0000000..1082f23
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpause.h
@@ -0,0 +1,83 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPause
+//
+// Description:
+//
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/11/20 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENPAUSE_H
+#define GUISCREENPAUSE_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <cards/cardgallery.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenPause : public CGuiScreen
+{
+public:
+ CGuiScreenPause( Scrooby::Screen* pScreen,
+ CGuiEntity* pParent,
+ eGuiWindowID id );
+ virtual ~CGuiScreenPause();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ void HandleResumeGame( unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+ void HandleQuitGame();
+#ifdef RAD_WIN32
+ void HandleQuitToSystem();
+#endif
+ #ifdef DEBUGWATCH
+ virtual const char* GetWatcherName() const;
+ #endif
+
+
+ CGuiMenu* m_pMenu;
+ Scrooby::Group* m_MissionObjectiveBox;
+ Scrooby::Text* m_pressStart;
+ Scrooby::Text* m_missionObjective;
+ Scrooby::Sprite* m_objectiveIcon;
+
+private:
+/*
+ void HideCards();
+ void ShowNextCard();
+*/
+
+ Scrooby::Sprite* m_collectedCards[ NUM_CARDS_PER_LEVEL ];
+ int m_numVisibleCards;
+ unsigned int m_elapsedTime;
+
+};
+
+#endif // GUISCREENPAUSE_H
diff --git a/game/code/presentation/gui/ingame/guiscreenpausecontroller.cpp b/game/code/presentation/gui/ingame/guiscreenpausecontroller.cpp
new file mode 100644
index 0000000..b175d81
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpausecontroller.cpp
@@ -0,0 +1,224 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPauseController
+//
+// Description: Implementation of the CGuiScreenPauseController class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenpausecontroller.h>
+
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/gameplaymanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+
+#include <raddebug.hpp> // Foundation
+
+#include <screen.h>
+#include <layer.h>
+#include <page.h>
+#include <sprite.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const float CONTROLLER_IMAGE_CORRECTION_SCALE = 2.0f;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenPauseController::CGuiScreenPauseController
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseController::CGuiScreenPauseController
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreenController( pScreen, pParent )
+{
+#ifndef RAD_WIN32
+ rAssert( pScreen != NULL );
+ Scrooby::Page* pPage = pScreen->GetPage( "ControllerImageS" );
+ rAssert( pPage != NULL );
+ Scrooby::Layer* foreground = pPage->GetLayer( "Foreground" );
+ rAssert( foreground != NULL );
+
+ Scrooby::Sprite* controllerImage = foreground->GetSprite( "Controller" );
+ if( controllerImage != NULL )
+ {
+ controllerImage->ScaleAboutCenter( CONTROLLER_IMAGE_CORRECTION_SCALE );
+ }
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenPauseController::~CGuiScreenPauseController
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseController::~CGuiScreenPauseController()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenPauseController::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseController::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_START:
+ {
+ // resume game
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreenController::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenPauseController::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseController::InitIntro()
+{
+ CGuiScreenController::InitIntro();
+}
+
+
+//===========================================================================
+// CGuiScreenPauseController::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseController::InitRunning()
+{
+ CGuiScreenController::InitRunning();
+}
+
+
+//===========================================================================
+// CGuiScreenPauseController::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseController::InitOutro()
+{
+ CGuiScreenController::InitOutro();
+
+ // apply vibration/rumble setting right away
+ //
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( 0 );
+ bool vibrationOn = GetInputManager()->IsRumbleEnabled();
+
+ if ( GetGameplayManager()->GetGameType() == GameplayManager::GT_NORMAL &&
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetVehicle() )
+ {
+#ifdef RAD_PS2
+ if ( GetInputManager()->GetController( Input::USB0 )->IsConnected() )
+ {
+ GetInputManager()->SetRumbleForDevice( Input::USB0, vibrationOn );
+ }
+ else if ( GetInputManager()->GetController( Input::USB1 )->IsConnected() )
+ {
+ GetInputManager()->SetRumbleForDevice( Input::USB1, vibrationOn );
+ }
+ else
+ {
+ GetInputManager()->SetRumbleForDevice( controllerID, vibrationOn );
+ }
+#else
+ GetInputManager()->SetRumbleForDevice( controllerID, vibrationOn );
+#endif
+
+ }
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/ingame/guiscreenpausecontroller.h b/game/code/presentation/gui/ingame/guiscreenpausecontroller.h
new file mode 100644
index 0000000..e1b46bf
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpausecontroller.h
@@ -0,0 +1,52 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPauseController
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENPAUSECONTROLLER_H
+#define GUISCREENPAUSECONTROLLER_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#ifdef RAD_WIN32
+#include <presentation/gui/frontend/guiscreencontrollerWin32.h>
+#else
+#include <presentation/gui/frontend/guiscreencontroller.h>
+#endif
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenPauseController : public CGuiScreenController
+{
+public:
+ CGuiScreenPauseController( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenPauseController();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+};
+
+#endif // GUISCREENPAUSECONTROLLER_H
diff --git a/game/code/presentation/gui/ingame/guiscreenpausedisplay.cpp b/game/code/presentation/gui/ingame/guiscreenpausedisplay.cpp
new file mode 100644
index 0000000..a6cd844
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpausedisplay.cpp
@@ -0,0 +1,164 @@
+/******************************************************************************
+ File: CGuiScreenPauseDisplay.cpp
+ Desc: Defines the CGuiScreenPauseDisplay class.
+ Authors: Tony Chu, Neil Haran
+ Date: August 14, 2003
+ History:
+*****************************************************************************/
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenpausedisplay.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const float CONTROLLER_IMAGE_CORRECTION_SCALE = 2.0f;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenPauseDisplay::CGuiScreenPauseDisplay
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseDisplay::CGuiScreenPauseDisplay
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreenDisplay( pScreen, pParent )
+{
+}
+
+
+//===========================================================================
+// CGuiScreenPauseDisplay::~CGuiScreenPauseDisplay
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseDisplay::~CGuiScreenPauseDisplay()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenPauseDisplay::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseDisplay::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_START:
+ {
+ // resume game
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreenDisplay::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenPauseDisplay::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseDisplay::InitIntro()
+{
+ CGuiScreenDisplay::InitIntro();
+}
+
+
+//===========================================================================
+// CGuiScreenPauseDisplay::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseDisplay::InitRunning()
+{
+ CGuiScreenDisplay::InitRunning();
+}
+
+
+//===========================================================================
+// CGuiScreenPauseDisplay::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseDisplay::InitOutro()
+{
+ CGuiScreenDisplay::InitOutro();
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/ingame/guiscreenpausedisplay.h b/game/code/presentation/gui/ingame/guiscreenpausedisplay.h
new file mode 100644
index 0000000..200be24
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpausedisplay.h
@@ -0,0 +1,44 @@
+/******************************************************************************
+
+ File: CGuiScreenPauseDisplay.h
+ Desc: Interface for the CGuiScreenController class.
+
+ Date: August 14, 2003
+ Authors: Tony Chu, Neil Haran
+ History:
+
+*****************************************************************************/
+
+#ifndef GUISCREENPAUSEDISPLAY_H
+#define GUISCREENPAUSEDISPLAY_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreendisplay.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenPauseDisplay : public CGuiScreenDisplay
+{
+public:
+ CGuiScreenPauseDisplay( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenPauseDisplay();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+};
+
+#endif // GUISCREENPAUSEDISPLAY_H
diff --git a/game/code/presentation/gui/ingame/guiscreenpausemission.cpp b/game/code/presentation/gui/ingame/guiscreenpausemission.cpp
new file mode 100644
index 0000000..368c83b
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpausemission.cpp
@@ -0,0 +1,366 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPauseMission
+//
+// Description: Implementation of the CGuiScreenPauseMission class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/11/20 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenmissionload.h>
+#include <presentation/gui/ingame/guiscreenpausemission.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guiscreenprompt.h>
+#include <presentation/gui/guisystem.h>
+#include <events/eventmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <mission/mission.h>
+#include <worldsim/hitnrunmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <raddebug.hpp> // Foundation
+#include <group.h>
+#include <page.h>
+#include <screen.h>
+#include <text.h>
+#include <sprite.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+enum eMenuItem
+{
+ MENU_ITEM_CONTINUE,
+// MENU_ITEM_VIEW_MAP,
+ MENU_ITEM_RESTART_MISSION,
+ MENU_ITEM_ABORT_MISSION,
+ MENU_ITEM_OPTIONS,
+ MENU_ITEM_QUIT_GAME,
+
+ NUM_MENU_ITEMS
+};
+
+static const char* MENU_ITEMS[] =
+{
+ "Continue",
+// "ViewMap",
+ "RestartMission",
+ "AbortMission",
+ "Options",
+ "QuitGame"
+};
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenPauseMission::CGuiScreenPauseMission
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseMission::CGuiScreenPauseMission
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+:
+ CGuiScreenPause( pScreen, pParent, GUI_SCREEN_ID_PAUSE_MISSION )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenPauseMission" );
+ // Create a menu.
+ //
+ m_pMenu = new(GMA_LEVEL_HUD) CGuiMenu( this, NUM_MENU_ITEMS );
+ rAssert( m_pMenu != NULL );
+
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "PauseMission" );
+ rAssert( pPage != NULL );
+
+ // Add menu items
+ //
+ Scrooby::Group* menu = pPage->GetGroup( "Menu" );
+ rAssert( menu != NULL );
+ Scrooby::Text* pText = NULL;
+ for( int i = 0; i < NUM_MENU_ITEMS; i++ )
+ {
+ pText = menu->GetText( MENU_ITEMS[ i ] );
+ rAssert( pText );
+
+ m_pMenu->AddMenuItem( pText );
+ }
+MEMTRACK_POP_GROUP("CGUIScreenPauseMission" );
+}
+
+
+//===========================================================================
+// CGuiScreenPauseMission::~CGuiScreenPauseMission
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseMission::~CGuiScreenPauseMission()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenPauseMission::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseMission::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( message == GUI_MSG_MENU_PROMPT_RESPONSE )
+ {
+ if( param1 == PROMPT_CONFIRM_RESTART )
+ {
+ switch( param2 )
+ {
+ case (CGuiMenuPrompt::RESPONSE_YES):
+ {
+ CGuiScreenMissionLoad::ReplaceBitmap();
+ this->HandleResumeGame( ON_HUD_ENTER_RESTART_MISSION );
+ break;
+ }
+
+ case (CGuiMenuPrompt::RESPONSE_NO):
+ {
+ this->ReloadScreen();
+
+ break;
+ }
+
+ default:
+ {
+ rAssertMsg( 0, "WARNING: *** Invalid prompt response!\n" );
+
+ break;
+ }
+ }
+ }
+ else if( param1 == PROMPT_CONFIRM_ABORT )
+ {
+ switch( param2 )
+ {
+ case (CGuiMenuPrompt::RESPONSE_YES):
+ {
+ this->HandleResumeGame( ON_HUD_ENTER_ABORT_MISSION );
+ GetEventManager()->TriggerEvent(EVENT_USER_CANCEL_PAUSE_MENU);
+
+ break;
+ }
+
+ case (CGuiMenuPrompt::RESPONSE_NO):
+ {
+ this->ReloadScreen();
+
+ break;
+ }
+
+ default:
+ {
+ rAssertMsg( 0, "WARNING: *** Invalid prompt response!\n" );
+
+ break;
+ }
+ }
+ }
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ switch( param1 )
+ {
+ case MENU_ITEM_CONTINUE:
+ {
+ this->HandleResumeGame();
+
+ break;
+ }
+/*
+ case MENU_ITEM_VIEW_MAP:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_HUD_MAP );
+
+ break;
+ }
+*/
+ case MENU_ITEM_RESTART_MISSION:
+ {
+ rAssert( m_guiManager );
+ m_guiManager->DisplayPrompt( PROMPT_CONFIRM_RESTART, this );
+
+// this->HandleResumeGame( ON_HUD_ENTER_RESTART_MISSION );
+
+ break;
+ }
+ case MENU_ITEM_ABORT_MISSION:
+ {
+ rAssert( m_guiManager );
+ m_guiManager->DisplayPrompt( PROMPT_CONFIRM_ABORT, this );
+
+// this->HandleResumeGame( ON_HUD_ENTER_ABORT_MISSION );
+
+ break;
+ }
+ case MENU_ITEM_OPTIONS:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_OPTIONS );
+
+ break;
+ }
+ case MENU_ITEM_QUIT_GAME:
+ {
+ this->HandleQuitGame();
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( 0, "WARNING: Invalid case for switch statement!\n" );
+ break;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreenPause::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenPauseMission::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseMission::InitIntro()
+{
+ CGuiScreenPause::InitIntro();
+
+ // enable 'restart mission' and 'abort mission' only if current mission
+ // isn't completed yet; otherwise, disable these selections
+ //
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ rAssert( currentMission != NULL );
+
+ HitnRunManager* hnrm = GetHitnRunManager();
+ rAssert( hnrm != NULL );
+
+ AvatarManager* pAvatarManager= GetAvatarManager();
+ rAssert( pAvatarManager != NULL );
+
+ CGuiManagerInGame* guiManagerIngame = static_cast<CGuiManagerInGame*>( m_pParent );
+ rAssert( guiManagerIngame != NULL );
+
+ bool isRestartAndAbortAllowed = !currentMission->IsSundayDrive() &&
+ !currentMission->IsComplete() &&
+ !hnrm->BustingPlayer() &&
+ !pAvatarManager->IsAvatarGettingInOrOutOfCar(0) &&
+ (guiManagerIngame->GetResumeGameScreenID() != GUI_SCREEN_ID_TUTORIAL);
+
+ rAssert( m_pMenu != NULL );
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_RESTART_MISSION, isRestartAndAbortAllowed );
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_ABORT_MISSION, isRestartAndAbortAllowed );
+}
+
+
+//===========================================================================
+// CGuiScreenPauseMission::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseMission::InitRunning()
+{
+ CGuiScreenPause::InitRunning();
+}
+
+
+//===========================================================================
+// CGuiScreenPauseMission::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseMission::InitOutro()
+{
+ CGuiScreenPause::InitOutro();
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
diff --git a/game/code/presentation/gui/ingame/guiscreenpausemission.h b/game/code/presentation/gui/ingame/guiscreenpausemission.h
new file mode 100644
index 0000000..f19fa6c
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpausemission.h
@@ -0,0 +1,50 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPauseMission
+//
+// Description:
+//
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/11/20 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENPAUSEMISSION_H
+#define GUISCREENPAUSEMISSION_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenpause.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenPauseMission : public CGuiScreenPause
+{
+public:
+ CGuiScreenPauseMission( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenPauseMission();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+};
+
+#endif // GUISCREENPAUSEMISSION_H
diff --git a/game/code/presentation/gui/ingame/guiscreenpauseoptions.cpp b/game/code/presentation/gui/ingame/guiscreenpauseoptions.cpp
new file mode 100644
index 0000000..99f90e4
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpauseoptions.cpp
@@ -0,0 +1,315 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPauseOptions
+//
+// Description: Implementation of the CGuiScreenPauseOptions class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenpauseoptions.h>
+#include <presentation/gui/guimenu.h>
+
+#include <cheats/cheatinputsystem.h>
+#include <memory/srrmemory.h>
+
+#include <raddebug.hpp> // Foundation
+#include <group.h>
+#include <screen.h>
+#include <page.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+enum ePauseMenuItem
+{
+#ifdef RAD_WIN32
+ MENU_ITEM_DISPLAY,
+#endif
+ MENU_ITEM_CONTROLLER,
+ MENU_ITEM_SOUND,
+ MENU_ITEM_SETTINGS,
+// MENU_ITEM_CAMERA,
+
+ NUM_PAUSE_MENU_ITEMS
+};
+
+
+static const char* PAUSE_MENU_ITEMS[] =
+{
+#ifdef RAD_WIN32
+ "Display",
+#endif
+ "Controller",
+ "Sound",
+ "Settings",
+// "Camera",
+
+ ""
+};
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenPauseOptions::CGuiScreenPauseOptions
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseOptions::CGuiScreenPauseOptions
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_OPTIONS ),
+ m_pMenu( NULL )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenPauseOptions" );
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage;
+ pPage = m_pScroobyScreen->GetPage( "PauseOptions" );
+ rAssert( pPage );
+
+ // Create a menu.
+ //
+ m_pMenu = new(GMA_LEVEL_HUD) CGuiMenu( this, NUM_PAUSE_MENU_ITEMS );
+ rAssert( m_pMenu != NULL );
+
+ // Add menu items
+ //
+ Scrooby::Group* menu = pPage->GetGroup( "Menu" );
+ rAssert( menu != NULL );
+ char itemName[ 32 ];
+ for( int i = 0; i < NUM_PAUSE_MENU_ITEMS; i++ )
+ {
+ sprintf( itemName, "%s_Value", PAUSE_MENU_ITEMS[ i ] );
+ Scrooby::Text* pTextValue = pPage->GetText( itemName );
+
+ sprintf( itemName, "%s_LArrow", PAUSE_MENU_ITEMS[ i ] );
+ Scrooby::Sprite* pLArrow = pPage->GetSprite( itemName );
+
+ sprintf( itemName, "%s_RArrow", PAUSE_MENU_ITEMS[ i ] );
+ Scrooby::Sprite* pRArrow = pPage->GetSprite( itemName );
+
+ m_pMenu->AddMenuItem( menu->GetText( PAUSE_MENU_ITEMS[ i ] ),
+ pTextValue,
+ NULL,
+ NULL,
+ pLArrow,
+ pRArrow );
+ }
+
+ // TC: [TEMP] disable controller screen for now to free up some memory for HUD map
+ //
+#ifndef RAD_WIN32
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_CONTROLLER, false, true );
+#endif
+
+#ifdef RAD_E3
+ // disable pause menu settings for E3 build
+ //
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_SETTINGS, false );
+#endif
+MEMTRACK_POP_GROUP("CGUIScreenPauseOptions");
+}
+
+
+//===========================================================================
+// CGuiScreenPauseOptions::~CGuiScreenPauseOptions
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseOptions::~CGuiScreenPauseOptions()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenPauseOptions::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseOptions::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( this->IsControllerMessage( message ) &&
+ GetCheatInputSystem()->IsActivated( param1 ) )
+ {
+ // ignore all controller inputs when cheat input system is activated
+ return;
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_START:
+ {
+ if( !m_pMenu->HasSelectionBeenMade() )
+ {
+ // resume game
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+ }
+
+ break;
+ }
+
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ if( param1 == MENU_ITEM_CONTROLLER )
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_CONTROLLER );
+ }
+ else if( param1 == MENU_ITEM_SOUND )
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_SOUND );
+ }
+ else if( param1 == MENU_ITEM_SETTINGS )
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_SETTINGS );
+ }
+#ifdef RAD_WIN32
+ else if( param1 == MENU_ITEM_DISPLAY )
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_DISPLAY );
+ }
+#endif
+ else
+ {
+ rAssertMsg( false, "Invalid menu selection!" );
+ }
+
+ break;
+ }
+/*
+ case GUI_MSG_MENU_SELECTION_CHANGED:
+ {
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, (param1 != MENU_ITEM_CAMERA) );
+
+ break;
+ }
+*/
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenPauseOptions::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseOptions::InitIntro()
+{
+// this->SetButtonVisible( BUTTON_ICON_ACCEPT, (m_pMenu->GetSelection() != MENU_ITEM_CAMERA) );
+
+#ifndef RAD_E3
+ GetCheatInputSystem()->SetEnabled( true );
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenPauseOptions::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseOptions::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenPauseOptions::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseOptions::InitOutro()
+{
+#ifndef RAD_E3
+ GetCheatInputSystem()->SetEnabled( false );
+#endif
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/ingame/guiscreenpauseoptions.h b/game/code/presentation/gui/ingame/guiscreenpauseoptions.h
new file mode 100644
index 0000000..49f43b1
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpauseoptions.h
@@ -0,0 +1,59 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPauseOptions
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENPAUSEOPTIONS_H
+#define GUISCREENPAUSEOPTIONS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+namespace Scrooby
+{
+ class Screen;
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenPauseOptions : public CGuiScreen
+{
+public:
+ CGuiScreenPauseOptions( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenPauseOptions();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ CGuiMenu* m_pMenu;
+
+};
+
+#endif // GUISCREENPAUSEOPTIONS_H
diff --git a/game/code/presentation/gui/ingame/guiscreenpausesettings.cpp b/game/code/presentation/gui/ingame/guiscreenpausesettings.cpp
new file mode 100644
index 0000000..479efb4
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpausesettings.cpp
@@ -0,0 +1,557 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPauseSettings
+//
+// Description: Implementation of the CGuiScreenPauseSettings class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenpausesettings.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/guisystem.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <presentation/tutorialmanager.h>
+#include <worldsim/avatarmanager.h>
+
+#include <raddebug.hpp> // Foundation
+#include <group.h>
+#include <screen.h>
+#include <page.h>
+#include <text.h>
+#include <strings/unicodestring.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+enum ePauseSettingsMenuItem
+{
+ MENU_ITEM_CAMERA,
+ MENU_ITEM_JUMP_CAMERAS,
+#ifndef RAD_WIN32
+ MENU_ITEM_INVERT_CAM_CONTROL,
+#endif
+ MENU_ITEM_INTERSECT_NAV_SYSTEM,
+ MENU_ITEM_RADAR,
+#ifndef RAD_WIN32
+ MENU_ITEM_VIBRATION,
+#endif
+ MENU_ITEM_TUTORIAL,
+
+ NUM_PAUSE_SETTINGS_MENU_ITEMS
+};
+
+
+const char* PAUSE_SETTINGS_MENU_ITEMS[] =
+{
+ "Camera",
+ "JumpCamera",
+#ifndef RAD_WIN32
+ "InvertCamControl",
+#endif
+ "IntersectNavSystem",
+ "Radar",
+#ifndef RAD_WIN32
+ "Vibration",
+#endif
+ "Tutorial",
+
+ ""
+};
+
+#ifdef RAD_WIN32
+SuperCam::Type PC_CAMERAS_FOR_WALKING[] =
+{
+ SuperCam::WALKER_CAM,
+ SuperCam::DEBUG_CAM,
+ SuperCam::KULL_CAM
+};
+
+const int NUM_PC_CAMERAS_FOR_WALKING = sizeof(PC_CAMERAS_FOR_WALKING)/sizeof(SuperCam::Type);
+const int NUM_PC_CAMERAS_FOR_WALKING_WITHOUT_CHEAT = 1;
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenPauseSettings::CGuiScreenPauseSettings
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseSettings::CGuiScreenPauseSettings
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_SETTINGS ),
+ m_pMenu( NULL ),
+ m_currentCameraSelectionMode( CAMERA_SELECTION_NOT_AVAILABLE )
+{
+MEMTRACK_PUSH_GROUP( "CGuiScreenPauseSettings" );
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "PauseSettings" );
+ rAssert( pPage );
+
+ // Create a menu.
+ //
+ m_pMenu = new CGuiMenu( this, NUM_PAUSE_SETTINGS_MENU_ITEMS, GUI_TEXT_MENU, MENU_SFX_NONE );
+ rAssert( m_pMenu != NULL );
+
+ // Add menu items
+ //
+ char itemName[ 32 ];
+
+ for( int i = 0; i < NUM_PAUSE_SETTINGS_MENU_ITEMS; i++ )
+ {
+ Scrooby::Group* group = pPage->GetGroup( PAUSE_SETTINGS_MENU_ITEMS[ i ] );
+ rAssert( group != NULL );
+
+ Scrooby::Text* pText = group->GetText( PAUSE_SETTINGS_MENU_ITEMS[ i ] );
+ rAssert( pText != NULL );
+ pText->SetTextMode( Scrooby::TEXT_WRAP );
+
+ sprintf( itemName, "%s_Value", PAUSE_SETTINGS_MENU_ITEMS[ i ] );
+ Scrooby::Text* pTextValue = group->GetText( itemName );
+ rAssert( pTextValue != NULL );
+ pTextValue->SetTextMode( Scrooby::TEXT_WRAP );
+
+ sprintf( itemName, "%s_LArrow", PAUSE_SETTINGS_MENU_ITEMS[ i ] );
+ Scrooby::Sprite* pLArrow = group->GetSprite( itemName );
+
+ sprintf( itemName, "%s_RArrow", PAUSE_SETTINGS_MENU_ITEMS[ i ] );
+ Scrooby::Sprite* pRArrow = group->GetSprite( itemName );
+
+ m_pMenu->AddMenuItem( pText,
+ pTextValue,
+ NULL,
+ NULL,
+ pLArrow,
+ pRArrow,
+ SELECTION_ENABLED | VALUES_WRAPPED | TEXT_OUTLINE_ENABLED );
+ }
+
+#ifdef RAD_GAMECUBE
+ // change "Vibration" text to "Rumble"
+ //
+ Scrooby::Text* vibrationText = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( MENU_ITEM_VIBRATION )->GetItem() );
+ rAssert( vibrationText != NULL );
+ vibrationText->SetIndex( 1 );
+#endif // RAD_GAMECUBE
+
+ for( int j = 0; j < NUM_CAMERA_SELECTION_MODES; j++ )
+ {
+ m_cameraSelections[ j ] = NULL;
+ m_numCameraSelections[ j ] = 0;
+ }
+
+ m_cameraSelections[ CAMERA_SELECTION_FOR_DRIVING ] = CAMERAS_FOR_DRIVING;
+#ifdef RAD_WIN32
+ m_cameraSelections[ CAMERA_SELECTION_FOR_WALKING ] = PC_CAMERAS_FOR_WALKING;
+#else
+ m_cameraSelections[ CAMERA_SELECTION_FOR_WALKING ] = CAMERAS_FOR_WALKING;
+#endif
+
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_CAMERAS ) )
+ {
+ m_numCameraSelections[ CAMERA_SELECTION_FOR_DRIVING ] = NUM_CAMERAS_FOR_DRIVING;
+
+#ifdef RAD_WIN32
+ m_numCameraSelections[ CAMERA_SELECTION_FOR_WALKING ] = NUM_PC_CAMERAS_FOR_WALKING;
+#else
+ m_numCameraSelections[ CAMERA_SELECTION_FOR_WALKING ] = NUM_CAMERAS_FOR_WALKING;
+#endif
+ }
+ else
+ {
+ m_numCameraSelections[ CAMERA_SELECTION_FOR_DRIVING ] = NUM_CAMERAS_FOR_DRIVING_WITHOUT_CHEAT;
+#ifdef RAD_WIN32
+ m_numCameraSelections[ CAMERA_SELECTION_FOR_WALKING ] = NUM_PC_CAMERAS_FOR_WALKING_WITHOUT_CHEAT;
+#else
+ m_numCameraSelections[ CAMERA_SELECTION_FOR_WALKING ] = NUM_CAMERAS_FOR_WALKING_WITHOUT_CHEAT;
+#endif
+ }
+
+ GetCheatInputSystem()->RegisterCallback( this );
+MEMTRACK_POP_GROUP("CGuiScreenPauseSettings");
+}
+
+
+//===========================================================================
+// CGuiScreenPauseSettings::~CGuiScreenPauseSettings
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseSettings::~CGuiScreenPauseSettings()
+{
+ GetCheatInputSystem()->UnregisterCallback( this );
+
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenPauseSettings::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseSettings::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_START:
+ {
+ // resume game
+ //
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+
+ break;
+ }
+
+ case GUI_MSG_MENU_SELECTION_VALUE_CHANGED:
+ {
+ if( param1 == MENU_ITEM_CAMERA )
+ {
+ // set new super cam
+ //
+ SuperCam::Type superCamType = m_cameraSelections[ m_currentCameraSelectionMode ][ param2 ];
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( superCamType, SuperCamCentral::CUT, 0 );
+
+ // and apply it right away
+ //
+ GetSuperCamManager()->GetSCC( 0 )->Update( 0 );
+ }
+#ifndef RAD_WIN32
+ else if( param1 == MENU_ITEM_VIBRATION )
+ {
+ if( param2 == 1 ) // vibration turned ON
+ {
+ // send vibration pulse to controller
+ //
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( 0 );
+#ifdef RAD_PS2
+ if ( GetInputManager()->IsControllerInPort( Input::USB0 ) )
+ {
+ GetInputManager()->TriggerRumblePulse( Input::USB0 );
+ }
+ else if ( GetInputManager()->IsControllerInPort( Input::USB1 ) )
+ {
+ GetInputManager()->TriggerRumblePulse( Input::USB1 );
+ }
+ else
+#endif
+ {
+ GetInputManager()->TriggerRumblePulse( controllerID );
+ }
+
+ }
+ }
+#endif
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+void
+CGuiScreenPauseSettings::OnCheatEntered( eCheatID cheatID, bool isEnabled )
+{
+ if( cheatID == CHEAT_ID_UNLOCK_CAMERAS && isEnabled )
+ {
+ m_numCameraSelections[ CAMERA_SELECTION_FOR_DRIVING ] = NUM_CAMERAS_FOR_DRIVING;
+ m_numCameraSelections[ CAMERA_SELECTION_FOR_WALKING ] = NUM_CAMERAS_FOR_WALKING;
+ }
+}
+
+//===========================================================================
+// CGuiScreenPauseSettings::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseSettings::InitIntro()
+{
+ // update camera setting
+ //
+ bool allowCameraToggle = GetSuperCamManager()->GetSCC( 0 )->AllowCameraToggle();
+
+ rAssert( m_pMenu );
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_CAMERA, allowCameraToggle );
+
+ if( allowCameraToggle )
+ {
+ // update camera selections
+ //
+ this->UpdateCameraSelections();
+ }
+ else
+ {
+ m_currentCameraSelectionMode = CAMERA_SELECTION_NOT_AVAILABLE;
+ }
+
+ // update other gameplay settings
+ //
+ bool isSettingOn = false;
+
+#ifndef RAD_WIN32
+ isSettingOn = GetSuperCamManager()->GetSCC( 0 )->IsInvertedCameraEnabled();
+ m_pMenu->SetSelectionValue( MENU_ITEM_INVERT_CAM_CONTROL,
+ isSettingOn ? 1 : 0 );
+#endif
+
+ isSettingOn = GetSuperCamManager()->GetSCC( 0 )->JumpCamsEnabled();
+ m_pMenu->SetSelectionValue( MENU_ITEM_JUMP_CAMERAS,
+ isSettingOn ? 1: 0 );
+
+ isSettingOn = GetCharacterSheetManager()->QueryNavSystemSetting();
+ m_pMenu->SetSelectionValue( MENU_ITEM_INTERSECT_NAV_SYSTEM,
+ isSettingOn ? 1 : 0 );
+
+ isSettingOn = GetGuiSystem()->IsRadarEnabled();
+ m_pMenu->SetSelectionValue( MENU_ITEM_RADAR,
+ isSettingOn ? 1 : 0 );
+#ifndef RAD_WIN32
+ isSettingOn = GetInputManager()->IsRumbleEnabled();
+ m_pMenu->SetSelectionValue( MENU_ITEM_VIBRATION,
+ isSettingOn ? 1 : 0 );
+#endif
+
+ isSettingOn = GetTutorialManager()->IsTutorialEventsEnabled();
+ m_pMenu->SetSelectionValue( MENU_ITEM_TUTORIAL,
+ isSettingOn ? 1 : 0 );
+
+ // show/hide tutorial setting depending on whether or not tutorial mode is enabled
+ //
+ if( GetTutorialManager()->IsTutorialModeEnabled() )
+ {
+ CGuiManagerInGame* guiManagerIngame = static_cast<CGuiManagerInGame*>( m_pParent );
+ rAssert( guiManagerIngame != NULL );
+
+ bool isTutorialToggleAllowed = (guiManagerIngame->GetResumeGameScreenID() != GUI_SCREEN_ID_TUTORIAL);
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_TUTORIAL, isTutorialToggleAllowed );
+ }
+ else
+ {
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_TUTORIAL, false, true );
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenPauseSettings::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseSettings::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenPauseSettings::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseSettings::InitOutro()
+{
+ // apply new gameplay settings
+ //
+ rAssert( m_pMenu != NULL );
+ bool isSettingOn = false;
+
+#ifndef RAD_WIN32
+ isSettingOn = (m_pMenu->GetSelectionValue( MENU_ITEM_INVERT_CAM_CONTROL ) == 1);
+ GetSuperCamManager()->GetSCC( 0 )->EnableInvertedCamera( isSettingOn );
+#endif
+
+ isSettingOn = (m_pMenu->GetSelectionValue( MENU_ITEM_JUMP_CAMERAS ) == 1);
+ GetSuperCamManager()->GetSCC( 0 )->EnableJumpCams( isSettingOn );
+
+ isSettingOn = (m_pMenu->GetSelectionValue( MENU_ITEM_INTERSECT_NAV_SYSTEM ) == 1);
+ GetCharacterSheetManager()->SetNavSystemOn( isSettingOn );
+
+ isSettingOn = (m_pMenu->GetSelectionValue( MENU_ITEM_RADAR ) == 1);
+ GetGuiSystem()->SetRadarEnabled( isSettingOn );
+
+#ifndef RAD_WIN32
+ isSettingOn = (m_pMenu->GetSelectionValue( MENU_ITEM_VIBRATION ) == 1);
+ GetInputManager()->SetRumbleEnabled( isSettingOn );
+#endif
+
+ isSettingOn = (m_pMenu->GetSelectionValue( MENU_ITEM_TUTORIAL ) == 1)
+#ifdef RAD_WIN32
+ && !(GetInputManager()->GetController(0)->IsTutorialDisabled())
+#endif
+ ;
+ GetTutorialManager()->EnableTutorialEvents( isSettingOn );
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenPauseSettings::UpdateCameraSelections()
+{
+ rAssert( m_pMenu );
+
+ // set current camera selection mode
+ //
+ bool isPlayerInCar = GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar();
+ m_currentCameraSelectionMode = isPlayerInCar ? CAMERA_SELECTION_FOR_DRIVING : CAMERA_SELECTION_FOR_WALKING;
+
+ // get current active camera
+ //
+ SuperCam* currentSuperCam = GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam();
+ rAssert( currentSuperCam );
+ SuperCam::Type currentSuperCamType = currentSuperCam->GetType();
+
+ int currentCameraIndex = -1;
+
+ // check to see if current camera is in the selection list
+ //
+ for( int i = 0; i < m_numCameraSelections[ m_currentCameraSelectionMode ]; i++ )
+ {
+ if( m_cameraSelections[ m_currentCameraSelectionMode ][ i ] == currentSuperCamType )
+ {
+ currentCameraIndex = i;
+
+ break;
+ }
+ }
+
+ if( currentCameraIndex == -1 )
+ {
+ // current camera not found in selection list, disable camera selection
+ //
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_CAMERA, false );
+
+ m_currentCameraSelectionMode = CAMERA_SELECTION_NOT_AVAILABLE;
+
+ return;
+ }
+
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_CAMERA, true );
+
+ // set number of camera selections available
+ //
+ m_pMenu->SetSelectionValueCount( MENU_ITEM_CAMERA,
+ m_numCameraSelections[ m_currentCameraSelectionMode ] );
+
+ // update camera names
+ //
+ for( int i = 0; i < m_numCameraSelections[ m_currentCameraSelectionMode ]; i++ )
+ {
+ GuiMenuItem* menuItemCamera = m_pMenu->GetMenuItem( MENU_ITEM_CAMERA );
+ rAssert( menuItemCamera != NULL );
+
+ SuperCam* superCam = GetSuperCamManager()->GetSCC( 0 )->GetSuperCam( m_cameraSelections[ m_currentCameraSelectionMode ][ i ] );
+ rAssert( superCam );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ P3D_UNICODE* stringBuffer = GetTextBibleString( superCam->GetName() );
+ rAssert( stringBuffer );
+
+ UnicodeString unicodeString;
+ unicodeString.ReadUnicode( static_cast<UnicodeChar*>( stringBuffer ) );
+
+ Scrooby::Text* menuItemValue = dynamic_cast<Scrooby::Text*>( menuItemCamera->GetItemValue() );
+ rAssert( menuItemValue != NULL );
+ menuItemValue->SetString( i, unicodeString );
+
+ HeapMgr()->PopHeap(GMA_LEVEL_HUD);
+ }
+
+ // set current camera selection
+ //
+ m_pMenu->SetSelectionValue( MENU_ITEM_CAMERA, currentCameraIndex );
+}
+
diff --git a/game/code/presentation/gui/ingame/guiscreenpausesettings.h b/game/code/presentation/gui/ingame/guiscreenpausesettings.h
new file mode 100644
index 0000000..9518687
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpausesettings.h
@@ -0,0 +1,81 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPauseSettings
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENPAUSESETTINGS_H
+#define GUISCREENPAUSESETTINGS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+#include <camera/supercam.h>
+#include <cheats/cheatinputsystem.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+namespace Scrooby
+{
+ class Screen;
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenPauseSettings : public CGuiScreen,
+ public ICheatEnteredCallback
+{
+public:
+ CGuiScreenPauseSettings( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenPauseSettings();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+ virtual void OnCheatEntered( eCheatID cheatID, bool isEnabled );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void UpdateCameraSelections();
+
+ CGuiMenu* m_pMenu;
+
+ enum eCameraSelectionMode
+ {
+ CAMERA_SELECTION_NOT_AVAILABLE = -1,
+
+ CAMERA_SELECTION_FOR_DRIVING,
+ CAMERA_SELECTION_FOR_WALKING,
+
+ NUM_CAMERA_SELECTION_MODES
+ };
+
+ eCameraSelectionMode m_currentCameraSelectionMode;
+
+ SuperCam::Type* m_cameraSelections[ NUM_CAMERA_SELECTION_MODES ];
+ int m_numCameraSelections[ NUM_CAMERA_SELECTION_MODES ];
+
+};
+
+#endif // GUISCREENPAUSEOPTIONS_H
diff --git a/game/code/presentation/gui/ingame/guiscreenpausesound.cpp b/game/code/presentation/gui/ingame/guiscreenpausesound.cpp
new file mode 100644
index 0000000..92ff581
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpausesound.cpp
@@ -0,0 +1,173 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPauseSound
+//
+// Description: Implementation of the CGuiScreenPauseSound class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenpausesound.h>
+
+#include <memory/srrmemory.h>
+
+#include <raddebug.hpp> // Foundation
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenPauseSound::CGuiScreenPauseSound
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseSound::CGuiScreenPauseSound
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+:
+ CGuiScreenSound( pScreen, pParent )
+{
+}
+
+
+//===========================================================================
+// CGuiScreenPauseSound::~CGuiScreenPauseSound
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseSound::~CGuiScreenPauseSound()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenPauseSound::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseSound::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_START:
+ {
+ // resume game
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreenSound::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenPauseSound::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseSound::InitIntro()
+{
+ CGuiScreenSound::InitIntro();
+}
+
+
+//===========================================================================
+// CGuiScreenPauseSound::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseSound::InitRunning()
+{
+ CGuiScreenSound::InitRunning();
+}
+
+
+//===========================================================================
+// CGuiScreenPauseSound::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseSound::InitOutro()
+{
+ CGuiScreenSound::InitOutro();
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/ingame/guiscreenpausesound.h b/game/code/presentation/gui/ingame/guiscreenpausesound.h
new file mode 100644
index 0000000..8215dd2
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpausesound.h
@@ -0,0 +1,48 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPauseSound
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENPAUSESOUND_H
+#define GUISCREENPAUSESOUND_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreensound.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenPauseSound : public CGuiScreenSound
+{
+public:
+ CGuiScreenPauseSound( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenPauseSound();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+};
+
+#endif // GUISCREENPAUSESOUND_H
diff --git a/game/code/presentation/gui/ingame/guiscreenpausesunday.cpp b/game/code/presentation/gui/ingame/guiscreenpausesunday.cpp
new file mode 100644
index 0000000..a817094
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpausesunday.cpp
@@ -0,0 +1,331 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPauseSunday
+//
+// Description: Implementation of the CGuiScreenPauseSunday class.
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/11/20 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenpausesunday.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guiscreenmemorycard.h>
+
+#include <memory/srrmemory.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+#include <raddebug.hpp> // Foundation
+#include <group.h>
+#include <page.h>
+#include <screen.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+enum ePauseSundayMenuItem
+{
+ MENU_ITEM_PAUSE_SUNDAY_CONTINUE,
+ MENU_ITEM_MISSION_SELECT,
+ MENU_ITEM_PAUSE_SUNDAY_LEVEL_PROGRESS,
+ MENU_ITEM_PAUSE_SUNDAY_VIEW_CARDS,
+ MENU_ITEM_PAUSE_SUNDAY_OPTIONS,
+ MENU_ITEM_SAVE_GAME,
+ MENU_ITEM_PAUSE_SUNDAY_QUIT_GAME,
+#ifdef RAD_WIN32
+ MENU_ITEM_PAUSE_SUNDAY_EXIT_GAME,
+#endif
+
+ NUM_PAUSE_SUNDAY_MENU_ITEMS
+};
+
+static const char* PAUSE_SUNDAY_MENU_ITEMS[] =
+{
+ "Continue",
+ "MissionSelect",
+ "LevelProgress",
+ "ViewCards",
+ "Options",
+ "SaveGame",
+ "QuitGame"
+#ifdef RAD_WIN32
+ ,"ExitToSystem"
+#endif
+};
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenPauseSunday::CGuiScreenPauseSunday
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseSunday::CGuiScreenPauseSunday
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+:
+ CGuiScreenPause( pScreen, pParent, GUI_SCREEN_ID_PAUSE_SUNDAY )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenPauseSunday" );
+ // Create a menu.
+ //
+ m_pMenu = new(GMA_LEVEL_HUD) CGuiMenu( this, NUM_PAUSE_SUNDAY_MENU_ITEMS );
+ rAssert( m_pMenu != NULL );
+
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "PauseSunday" );
+ rAssert( pPage != NULL );
+
+ // Add menu items
+ //
+ Scrooby::Group* menu = pPage->GetGroup( "Menu" );
+ rAssert( menu != NULL );
+ Scrooby::Text* pText = NULL;
+ for( int i = 0; i < NUM_PAUSE_SUNDAY_MENU_ITEMS; i++ )
+ {
+ pText = menu->GetText( PAUSE_SUNDAY_MENU_ITEMS[ i ] );
+ rAssert( pText );
+
+ m_pMenu->AddMenuItem( pText );
+ }
+
+#ifdef RAD_DEMO
+ // disable certain menu items for demos
+ //
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_SAVE_GAME, false );
+#endif
+MEMTRACK_POP_GROUP("CGUIScreenPauseSunday");
+}
+
+
+//===========================================================================
+// CGuiScreenPauseSunday::~CGuiScreenPauseSunday
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPauseSunday::~CGuiScreenPauseSunday()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenPauseSunday::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseSunday::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+/*
+ if( !GetMemoryCardManager()->IsMemcardInfoLoaded() )
+ {
+ if( message == GUI_MSG_CONTROLLER_SELECT ||
+ message == GUI_MSG_CONTROLLER_START )
+ {
+ // ignore all menu selection inputs and start input
+ // until memcard info is loaded
+ //
+ return;
+ }
+ }
+*/
+ switch( message )
+ {
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ switch( param1 )
+ {
+ case MENU_ITEM_PAUSE_SUNDAY_CONTINUE:
+ {
+ this->HandleResumeGame();
+
+ break;
+ }
+ case MENU_ITEM_MISSION_SELECT:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_MISSION_SELECT );
+
+ break;
+ }
+ case MENU_ITEM_PAUSE_SUNDAY_LEVEL_PROGRESS:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_LEVEL_STATS );
+
+ break;
+ }
+ case MENU_ITEM_PAUSE_SUNDAY_VIEW_CARDS:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_VIEW_CARDS );
+
+ break;
+ }
+ case MENU_ITEM_PAUSE_SUNDAY_OPTIONS:
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_OPTIONS );
+
+ break;
+ }
+ case MENU_ITEM_SAVE_GAME:
+ {
+#ifdef RAD_XBOX
+ // Xbox TCR Requirement: always prompt user to select memory
+ // device before loading/saving
+ //
+ CGuiScreenLoadSave::s_forceGotoMemoryCardScreen = true;
+#endif
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_SAVE_GAME );
+
+ break;
+ }
+ case MENU_ITEM_PAUSE_SUNDAY_QUIT_GAME:
+ {
+ this->HandleQuitGame();
+
+ break;
+ }
+#ifdef RAD_WIN32
+ case MENU_ITEM_PAUSE_SUNDAY_EXIT_GAME:
+ {
+ this->HandleQuitToSystem();
+
+ break;
+ }
+#endif
+ default:
+ {
+ rAssertMsg( 0, "WARNING: Invalid case for switch statement!\n" );
+
+ break;
+ }
+ }
+
+ break;
+ }
+
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreenPause::HandleMessage( message, param1, param2 );
+}
+
+//===========================================================================
+// CGuiScreenPauseSunday::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseSunday::InitIntro()
+{
+ if( GetCharacterSheetManager()->QueryHighestMission().mLevel > 0 ||
+ GetCharacterSheetManager()->QueryHighestMission().mMissionNumber > 0 ||
+ GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_MISSIONS ) )
+ {
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_MISSION_SELECT, true );
+ }
+ else
+ {
+ // this means the user's just started a new game and is
+ // currently on the level 1 tutorial mission; hence, cannot
+ // select any other missions
+ //
+ m_pMenu->SetMenuItemEnabled( MENU_ITEM_MISSION_SELECT, false );
+ }
+
+ CGuiScreenPause::InitIntro();
+}
+
+
+//===========================================================================
+// CGuiScreenPauseSunday::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseSunday::InitRunning()
+{
+ CGuiScreenPause::InitRunning();
+}
+
+
+//===========================================================================
+// CGuiScreenPauseSunday::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPauseSunday::InitOutro()
+{
+ CGuiScreenPause::InitOutro();
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
diff --git a/game/code/presentation/gui/ingame/guiscreenpausesunday.h b/game/code/presentation/gui/ingame/guiscreenpausesunday.h
new file mode 100644
index 0000000..38ef85b
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpausesunday.h
@@ -0,0 +1,50 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPauseSunday
+//
+// Description:
+//
+//
+// Authors: Darwin Chau
+// Tony Chu
+//
+// Revisions Date Author Revision
+// 2000/11/20 DChau Created
+// 2002/06/06 TChu Modified for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENPAUSESUNDAY_H
+#define GUISCREENPAUSESUNDAY_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenpause.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenPauseSunday : public CGuiScreenPause
+{
+public:
+ CGuiScreenPauseSunday( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenPauseSunday();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+};
+
+#endif // GUISCREENPAUSESUNDAY_H
diff --git a/game/code/presentation/gui/ingame/guiscreenphonebooth.cpp b/game/code/presentation/gui/ingame/guiscreenphonebooth.cpp
new file mode 100644
index 0000000..addef2c
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenphonebooth.cpp
@@ -0,0 +1,1104 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPhoneBooth
+//
+// Description: Implementation of the CGuiScreenPhoneBooth class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/21 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenphonebooth.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guitextbible.h>
+
+#include <ai/actionbuttonhandler.h>
+#include <camera/relativeanimatedcam.h>
+#include <camera/supercam.h>
+#include <camera/supercammanager.h>
+#include <cheats/cheatinputsystem.h>
+#include <events/eventmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <mission/missionmanager.h>
+#include <mission/rewards/rewardsmanager.h>
+#include <mission/rewards/reward.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <sound/soundmanager.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/vehiclecentral.h>
+
+#include <screen.h>
+#include <page.h>
+#include <layer.h>
+#include <group.h>
+#include <sprite.h>
+#include <text.h>
+#include <pure3dobject.h>
+
+#include <p3d/anim/cameraanimation.hpp>
+
+#include <raddebug.hpp> // Foundation
+
+#include <string.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+#ifdef RAD_WIN32
+const float VEHICLE_CORRECTION_SCALE = 0.93f;
+const float VEHICLE_DAMAGED_CORRECTION_SCALE = 0.93f;
+const float HUSK_CORRECTION_SCALE = 1.4f;
+#else
+const float VEHICLE_CORRECTION_SCALE = 1.0f / 0.75f;
+const float VEHICLE_DAMAGED_CORRECTION_SCALE = 1.0f / 0.5f;
+const float HUSK_CORRECTION_SCALE = 1.0f / 0.5f;
+#endif
+
+const float VEHICLE_LOCKED_IMAGE_ALPHA = 0.5f;
+
+const tUID CHARACTER_VEHICLE_NAMES[] =
+{
+ tEntity::MakeUID( "apu_v" ),
+ tEntity::MakeUID( "bart_v" ),
+ tEntity::MakeUID( "comic_v" ),
+ tEntity::MakeUID( "cletu_v" ),
+ tEntity::MakeUID( "frink_v" ),
+ tEntity::MakeUID( "gramp_v" ),
+ tEntity::MakeUID( "homer_v" ),
+ tEntity::MakeUID( "lisa_v" ),
+ tEntity::MakeUID( "marge_v" ),
+ tEntity::MakeUID( "skinn_v" ),
+ tEntity::MakeUID( "smith_v" ),
+ tEntity::MakeUID( "snake_v" ),
+ tEntity::MakeUID( "wiggu_v" ),
+
+ 0 // dummy terminator
+};
+
+const int NUM_CHARACTER_VEHICLE_NAMES =
+ sizeof( CHARACTER_VEHICLE_NAMES ) / sizeof( CHARACTER_VEHICLE_NAMES[ 0 ] ) - 1;
+
+static int GetVehicleIndex( const char* vehicleName )
+{
+ int vehicleIndex = -1;
+
+ tUID vehicleNameUID = tEntity::MakeUID( vehicleName );
+
+ for( int i = 0; i < NUM_CHARACTER_VEHICLE_NAMES; i++ )
+ {
+ if( CHARACTER_VEHICLE_NAMES[ i ] == vehicleNameUID )
+ {
+ // found it!
+ //
+ vehicleIndex = i;
+
+ // stop searching
+ //
+ break;
+ }
+ }
+
+ return vehicleIndex;
+}
+
+#ifdef SRR2_OVERRIDE_CAR_SELECTION
+ const char* OVERRIDE_VEHICLE_NAMES[] =
+ {
+ "snake_v",
+ "bookb_v",
+ "marge_v",
+ "carhom_v",
+ "krust_v",
+ "bbman_v",
+ "elect_v",
+ "famil_v",
+ "bart_v",
+ "scorp_v",
+ "honor_v",
+ "hbike_v",
+ "frink_v",
+ "comic_v",
+ "lisa_v",
+ "smith_v",
+ "mrplo_v",
+ "fone_v",
+ "cletu_v",
+ "apu_v",
+ "plowk_v",
+ "wiggu_v",
+ "otto_v",
+ "moe_v",
+ "skinn_v",
+ "homer_v",
+ "zombi_v",
+ "burns_v",
+ "willi_v",
+ "gramp_v",
+ "gramR_v",
+
+ "atv_v",
+ "knigh_v",
+ "mono_v",
+ "oblit_v",
+ "hype_v",
+ "dune_v",
+ "rocke_v",
+
+ "cArmor",
+ "cCellA",
+ "cCellB",
+ "cCellC",
+ "cCellD",
+ "cSedan",
+ "cCola",
+ "cCube",
+ "cCurator",
+ "cDonut",
+ "cDuff",
+ "cBlbart",
+ "cHears",
+ "cKlimo",
+ "cLimo",
+ "cMilk",
+ "cNerd",
+ "cNonup",
+ "cPolice",
+ "cVan",
+
+ //"huskA",
+ "compactA",
+ "minivanA",
+ "pickupA",
+ "sedanA",
+ "sedanB",
+ "sportsA",
+ "sportsB",
+ "wagonA",
+ "SUVA",
+ "taxiA",
+ "coffin",
+ "ship",
+ "hallo",
+ "witchcar",
+
+ "ambul",
+ "burnsarm",
+ "fishtruc",
+ "garbage",
+ "icecream",
+ "IStruck",
+ "nuctruck",
+ "pizza",
+ "schoolbu",
+ "votetruc",
+ "glastruc",
+ "cFire_v",
+ "cBone",
+ "redbrick",
+
+ "" // dummy terminator
+ };
+
+ const unsigned int NUM_OVERRIDE_VEHICLES =
+ sizeof( OVERRIDE_VEHICLE_NAMES ) / sizeof( OVERRIDE_VEHICLE_NAMES[ 0 ] ) - 1;
+
+ const char* VEHICLES_DIR = "art\\cars\\";
+
+ int CGuiScreenPhoneBooth::s_currentDebugVehicleSelection = 0;
+ unsigned CGuiScreenPhoneBooth::s_currentTeleportSelection = 0;
+#endif
+
+//===========================================================================
+// CGuiScreenPhoneBooth::
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenPhoneBooth::CGuiScreenPhoneBooth
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPhoneBooth::CGuiScreenPhoneBooth
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: IGuiScreenRewards( pScreen,
+ pScreen->GetPage( "PhoneBooth" ),
+ pParent,
+ GUI_SCREEN_ID_PHONE_BOOTH ),
+ m_damagedInfo( NULL ),
+ m_vehicleDamaged( NULL ),
+ m_repairCostInfo( NULL ),
+ m_vehicleRepairCost( NULL )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenPhoneBooth" );
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "PhoneBooth" );
+ rAssert( pPage != NULL );
+
+#ifdef RAD_WIN32
+ m_leftArrow = pPage->GetGroup( "Arrows" )->GetSprite( "LeftArrow" );
+ m_rightArrow = pPage->GetGroup( "Arrows" )->GetSprite( "RightArrow" );
+ m_leftArrow->ScaleAboutCenter( 1.3f );
+ m_rightArrow->ScaleAboutCenter( 1.3f );
+#endif
+
+ // get vehicle damaged info
+ //
+ m_damagedInfo = pPage->GetGroup( "DamagedInfo" );
+ rAssert( m_damagedInfo != NULL );
+ m_vehicleDamaged = m_damagedInfo->GetText( "VehicleDamaged_Value" );
+
+ m_repairCostInfo = m_damagedInfo->GetGroup( "RepairCostInfo" );
+ rAssert( m_repairCostInfo != NULL );
+ m_vehicleRepairCost = m_repairCostInfo->GetText( "CostToFix_Value" );
+
+ // always show vehicle stats
+ //
+ rAssert( m_statsOverlay != NULL );
+ m_statsOverlay->SetVisible( true );
+
+#ifdef PAL
+ // re-position damaged info text
+ //
+ const int DAMAGED_INFO_TRANSLATE_X = +60;
+
+ m_vehicleDamaged->ResetTransformation();
+ m_vehicleDamaged->Translate( DAMAGED_INFO_TRANSLATE_X, 0 );
+
+ m_vehicleRepairCost->ResetTransformation();
+ m_vehicleRepairCost->Translate( DAMAGED_INFO_TRANSLATE_X, 0 );
+
+ Scrooby::Text* repairCostCoins = m_repairCostInfo->GetText( "Coins" );
+ if( repairCostCoins != NULL )
+ {
+ repairCostCoins->ResetTransformation();
+ repairCostCoins->Translate( DAMAGED_INFO_TRANSLATE_X, 0 );
+#ifdef RAD_WIN32
+ repairCostCoins->Translate( 70, 0 );
+ repairCostCoins->ScaleAboutCenter( 0.5f );
+#endif
+ }
+
+ // move stats over to the left a bit to avoid safe zone
+ // boundary clipping
+ //
+ m_statsOverlay->ResetTransformation();
+ m_statsOverlay->Translate( -15, 0 );
+
+ // scale-down vehicle name a bit
+ //
+ m_previewName->ResetTransformation();
+ m_previewName->ScaleAboutCenter( 0.9f );
+#endif // PAL
+
+ // apply correction scale to spotlight
+ //
+ Scrooby::Sprite* spotLight = pPage->GetSprite( "Spotlight" );
+ if( spotLight != NULL )
+ {
+ spotLight->ResetTransformation();
+ spotLight->ScaleAboutCenter( 4.0f );
+ }
+
+ // apply correction scale to spotlight overlay
+ //
+ spotLight = pPage->GetSprite( "Overlay" );
+ if( spotLight != NULL )
+ {
+ spotLight->ResetTransformation();
+ spotLight->ScaleAboutCenter( 4.0f );
+ }
+
+ rAssert( m_pRewardsMenu != NULL );
+ m_pRewardsMenu->SetHighlightColour( false, tColour( 255, 255, 255 ) );
+ m_pRewardsMenu->Reset();
+
+ this->AutoScaleFrame( pPage );
+
+#ifdef SRR2_OVERRIDE_CAR_SELECTION
+ pPage = m_pScroobyScreen->GetPage( "CarSelect" );
+ if( pPage != NULL )
+ {
+ m_carSelectOverlay = pPage->GetLayerByIndex( 0 );
+ rAssert( m_carSelectOverlay );
+ }
+
+ // Create a menu.
+ //
+ m_pMenu = new CGuiMenu( this );
+ rAssert( m_pMenu != NULL );
+
+ // Add menu items
+ //
+ m_pMenu->AddMenuItem( pPage->GetText( "Car" ),
+ pPage->GetText( "Car_Value" ) );
+
+ // clamp number of vehicle selections w/ only what's available
+ //
+ m_pMenu->SetSelectionValueCount( 0, NUM_OVERRIDE_VEHICLES );
+
+ // wrap vehicle names
+ //
+ Scrooby::Text* vehicleNames = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( 0 )->GetItemValue() );
+ rAssert( vehicleNames != NULL );
+ vehicleNames->SetTextMode( Scrooby::TEXT_WRAP );
+
+ m_menuTeleport = false;
+#endif
+MEMTRACK_POP_GROUP("CGUIScreenPhoneBooth");
+}
+
+//===========================================================================
+// CGuiScreenPhoneBooth::~CGuiScreenPhoneBooth
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPhoneBooth::~CGuiScreenPhoneBooth()
+{
+#ifdef SRR2_OVERRIDE_CAR_SELECTION
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+#endif
+}
+
+//===========================================================================
+// CGuiScreenPhoneBooth::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPhoneBooth::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_isLoading )
+ {
+ if( message == GUI_MSG_CONTROLLER_SELECT ||
+ message == GUI_MSG_CONTROLLER_BACK )
+ {
+ // don't allow user to select or back out of screen
+ // during loading
+ //
+ return;
+ }
+ }
+
+ rAssert( m_pRewardsMenu != NULL );
+ if( m_pRewardsMenu->HasSelectionBeenMade() || m_isLoadingReward )
+ {
+ if( this->IsControllerMessage( message ) )
+ {
+ // selection has already been made or reward is currently loading,
+ // ignore all controller inputs
+ //
+ return;
+ }
+ }
+
+#ifdef SRR2_OVERRIDE_CAR_SELECTION
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ if( message == GUI_MSG_CONTROLLER_L1 || message == GUI_MSG_CONTROLLER_R1 )
+ {
+#ifdef FINAL
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_VEHICLES ) &&
+ GetCharacterSheetManager()->QueryPercentGameCompleted() > 99.999f )
+#endif
+ {
+ rAssert( m_carSelectOverlay != NULL );
+
+ // toggle visibility of car selection overlay
+ //
+ m_carSelectOverlay->SetVisible( !m_carSelectOverlay->IsVisible() );
+
+ if( m_carSelectOverlay->IsVisible() )
+ {
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+
+// Teleportation menu disabled in final build
+#ifndef FINAL
+ m_menuTeleport = (message == GUI_MSG_CONTROLLER_L1);
+#endif
+ Scrooby::Text* menuLabel = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( 0 )->GetItem() );
+ rAssert( menuLabel != NULL );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+ if( !m_menuTeleport )
+ {
+ menuLabel->SetString( 0, "Vehicle:" );
+ m_pMenu->SetSelectionValue( 0, s_currentDebugVehicleSelection );
+ }
+ else
+ {
+ menuLabel->SetString( 0, "Teleport:" );
+ m_pMenu->SetSelectionValue( 0, s_currentTeleportSelection );
+ }
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+ }
+ else
+ {
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT,
+ this->GetCurrentPreviewObject()->isUnlocked );
+ }
+ }
+ }
+ }
+
+ if( m_carSelectOverlay->IsVisible() )
+ {
+ this->HandleMessageForCar( message, param1, param2 );
+ }
+ else
+#endif
+ {
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ const PreviewObject* currentPreviewObject = this->GetCurrentPreviewObject();
+
+ if( currentPreviewObject->isUnlocked )
+ {
+ // check if selecting a damaged out vehicle; if so, reduce current bank
+ // value by repair cost amount
+ //
+ int carIndex = GetCharacterSheetManager()->GetCarIndex( currentPreviewObject->name );
+ if( carIndex >= 0 )
+ {
+ bool isVehicleDamagedOut = ( GetCharacterSheetManager()->GetCarDamageState( carIndex ) == eHusk );
+ if( isVehicleDamagedOut )
+ {
+ int repairCost = currentPreviewObject->pReward->GetRepairCost();
+
+ // TC: *** Joe says to always allow car repair, even if user
+ // doesn't quite have enough money
+/*
+ if( GetCoinManager()->GetBankValue() < repairCost )
+ {
+ // doh... not enough money in the bank to repair damaged vehicle
+ //
+ return;
+ }
+*/
+ GetCoinManager()->AdjustBankValue( -repairCost );
+
+ // trigger HUD event
+ //
+ GetEventManager()->TriggerEvent( EVENT_LOST_COINS );
+
+ //Chuck: Adding Record that the car has full hitpoints restored
+ GetCharacterSheetManager()->UpdateCarHealth(carIndex,1.0f);
+ }
+ }
+
+ Character* pWalkerCharacter = GetCharacterManager()->GetCharacter( 0 );
+ rWarning( pWalkerCharacter != NULL );
+
+ GetEventManager()->TriggerEvent( EVENT_PHONE_BOOTH_RIDE_REQUEST, reinterpret_cast<void*>( pWalkerCharacter ) );
+
+ bool isBonusVehicle = (m_previewVehicles[ m_currentPreviewVehicle ].pReward->GetQuestType() == Reward::eBonusMission);
+ if( isBonusVehicle )
+ {
+ Character* pDriverCharacter = NULL;
+
+ Vehicle* pCurrentVehicle = GetGameplayManager()->GetCurrentVehicle();
+ rAssert( pCurrentVehicle != NULL );
+
+ bool isNewVehicle = ( strcmp( pCurrentVehicle->GetName(), currentPreviewObject->name ) != 0 );
+ if( !isNewVehicle )
+ {
+ //
+ // Re-requesting old vehicle. Send some dialogue if there's a driver.
+ //
+ pDriverCharacter = pCurrentVehicle->GetDriver();
+ if( pDriverCharacter != NULL )
+ {
+ GetEventManager()->TriggerEvent( EVENT_PHONE_BOOTH_OLD_VEHICLE_RESELECTED, pDriverCharacter );
+ }
+ }
+ }
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_FE_LOCKED_OUT );
+ return;
+ }
+
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ // load selected reward item
+ //
+ this->LoadSelectedReward();
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_LEFT:
+ {
+ m_currentPreviewVehicle = (m_currentPreviewVehicle - 1 + m_numPreviewVehicles) % m_numPreviewVehicles;
+
+ this->On3DModelSelectionChange( &m_previewVehicles[ m_currentPreviewVehicle ] );
+ this->UpdateDamagedInfo();
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_RIGHT:
+ {
+ m_currentPreviewVehicle = (m_currentPreviewVehicle + 1 + m_numPreviewVehicles) % m_numPreviewVehicles;
+
+ this->On3DModelSelectionChange( &m_previewVehicles[ m_currentPreviewVehicle ] );
+ this->UpdateDamagedInfo();
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ IGuiScreenRewards::HandleMessage( message, param1, param2 );
+ }
+}
+
+#ifdef RAD_WIN32
+//===========================================================================
+// CGuiScreenPhoneBooth::CheckCursorAgainstHotspots
+//===========================================================================
+// Description: Checks cursor position against its list of hotspots.
+//
+// Constraints: None.
+//
+// Parameters: float x - The x position of cursor in P3D coordinates.
+// float y - The y position of cursor in P3D coordinates.
+//
+// Return: N/A.
+//
+//===========================================================================
+eFEHotspotType CGuiScreenPhoneBooth::CheckCursorAgainstHotspots( float x, float y )
+{
+ eFEHotspotType hotSpotType = CGuiScreen::CheckCursorAgainstHotspots( x, y );
+ if( hotSpotType == HOTSPOT_NONE )
+ {
+ if( m_leftArrow )
+ {
+ if( m_leftArrow->IsPointInBoundingRect( x, y ) )
+ {
+ hotSpotType = HOTSPOT_ARROWLEFT;
+ }
+ }
+ if( m_rightArrow )
+ {
+ if( m_rightArrow->IsPointInBoundingRect( x, y ) )
+ {
+ hotSpotType = HOTSPOT_ARROWRIGHT;
+ }
+ }
+
+ }
+ return hotSpotType;
+}
+#endif
+
+//===========================================================================
+// CGuiScreenPhoneBooth::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPhoneBooth::InitIntro()
+{
+ IGuiScreenRewards::InitIntro();
+
+ // hide lock by default
+ //
+ rAssert( m_lockedOverlay != NULL );
+ m_lockedOverlay->SetVisible( false );
+
+ // set level default vehicle as default garage vehicle selection
+ //
+ Reward* pDefaultCar = GetRewardsManager()->GetReward( GetGameplayManager()->GetCurrentLevelIndex(),
+ Reward::eDefaultCar );
+ if( pDefaultCar != NULL )
+ {
+ for( int i = 0; i < m_numPreviewVehicles; i++ )
+ {
+ if( m_previewVehicles[ i ].pReward == pDefaultCar )
+ {
+ m_currentPreviewVehicle = i;
+
+ break;
+ }
+ }
+ }
+
+ this->UpdateDamagedInfo();
+
+ // load current 3D model object
+ //
+ this->On3DModelSelectionChange( this->GetCurrentPreviewObject() );
+
+ // notify sound manager that game is paused
+ //
+ GetSoundManager()->OnStoreScreenStart();
+}
+
+//===========================================================================
+// CGuiScreenPhoneBooth::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPhoneBooth::InitRunning()
+{
+ IGuiScreenRewards::InitRunning();
+
+ CGuiScreenHud::UpdateNumCoinsDisplay( GetCoinManager()->GetBankValue() );
+}
+
+//===========================================================================
+// CGuiScreenPhoneBooth::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPhoneBooth::InitOutro()
+{
+ if( m_isLoadingReward )
+ {
+ RelativeAnimatedCam::AllowSkipping( true );
+ RelativeAnimatedCam::Reset();
+ RelativeAnimatedCam::SetCamera( "phonecameraShape" );
+ RelativeAnimatedCam::SetMulticontroller( "phonecamera" );
+ tCameraAnimationController* camController = p3d::find< tCameraAnimationController >( "CAM_phonecameraShape" );
+
+ SuperCam* sc = GetSuperCamManager()->GetSCC( 0 )->GetSuperCam( SuperCam::RELATIVE_ANIMATED_CAM );
+ RelativeAnimatedCam* rac = dynamic_cast< RelativeAnimatedCam* >( sc );
+ rac->SetCameraAnimationController( camController );
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( SuperCam::RELATIVE_ANIMATED_CAM );
+ RelativeAnimatedCam::CheckPendingCameraSwitch();
+
+ GetMissionManager()->Update( 16 );
+ GetSuperCamManager()->GetSCC( 0 )->NoTransition();
+ GetSuperCamManager()->Update( 16, true );
+ }
+
+ CGuiScreenHud::UpdateNumCoinsDisplay( 0, false );
+
+ IGuiScreenRewards::InitOutro();
+
+ // notify sound manager that game is un-paused
+ //
+ GetSoundManager()->OnStoreScreenEnd();
+}
+
+void
+CGuiScreenPhoneBooth::On3DModelLoaded( const PreviewObject* previewObject )
+{
+ p3d::pddi->DrawSync();
+
+ rAssert( m_previewImage != NULL );
+ m_previewImage->SetVisible( true );
+ m_previewImage->ResetTransformation();
+
+ // apply alpha to locked vehicles
+ //
+ m_previewImage->SetAlpha( previewObject->isUnlocked ? 1.0f : VEHICLE_LOCKED_IMAGE_ALPHA );
+
+ CarDamageStateEnum damageState = eNull;
+ if( previewObject->isUnlocked )
+ {
+ int carIndex = GetCharacterSheetManager()->GetCarIndex( previewObject->name );
+ if( carIndex >= 0 )
+ {
+ damageState = GetCharacterSheetManager()->GetCarDamageState( carIndex );
+ }
+ }
+
+ if( damageState == eHusk )
+ {
+ m_previewImage->ScaleAboutCenter( HUSK_CORRECTION_SCALE );
+
+ // show generic husk
+ //
+ m_previewImage->SetRawSprite( NULL, true );
+ }
+ else
+ {
+ // search for 2D image(s) in inventory
+ //
+ char name[ 16 ];
+
+ if( damageState == eDamaged )
+ {
+ // show damaged vehicle
+ //
+#ifdef LOAD_DAMAGED_VEHICLE_IMAGES
+ sprintf( name, "%sD.png", previewObject->nameModel );
+ m_previewImage->ScaleAboutCenter( VEHICLE_DAMAGED_CORRECTION_SCALE );
+#else
+ sprintf( name, "%s.png", previewObject->nameModel );
+ m_previewImage->ScaleAboutCenter( VEHICLE_CORRECTION_SCALE );
+#endif
+ }
+ else
+ {
+ // show normal vehicle
+ //
+ sprintf( name, "%s.png", previewObject->nameModel );
+
+ m_previewImage->ScaleAboutCenter( VEHICLE_CORRECTION_SCALE );
+ }
+
+ tSprite* pSprite = p3d::find<tSprite>( name );
+ if( pSprite != NULL )
+ {
+ m_previewImage->SetRawSprite( pSprite, true );
+ }
+ else
+ {
+ rAssertMsg( false, "*** Can't find 2D vehicle image!!" );
+ }
+ }
+}
+
+const PreviewObject*
+CGuiScreenPhoneBooth::GetCurrentPreviewObject() const
+{
+ const PreviewObject* previewObject = NULL;
+
+ if( m_numPreviewVehicles > 0 )
+ {
+ previewObject = &m_previewVehicles[ m_currentPreviewVehicle ];
+ }
+
+ return previewObject;
+}
+
+void
+CGuiScreenPhoneBooth::InitMenu()
+{
+ const int highestLevelPlayed = GetCharacterSheetManager()->QueryHighestMission().mLevel;
+
+ for( int i = 0; i <= highestLevelPlayed; i++ )
+ {
+ Reward* pReward = NULL;
+
+ for( int j = (Reward::eBlank + 1); j < Reward::NUM_QUESTS; j++ )
+ {
+ pReward = GetRewardsManager()->GetReward( i, (Reward::eQuestType)j );
+ if( pReward != NULL &&
+ pReward->GetRewardType() == Reward::ALT_PLAYERCAR )
+ {
+ rAssert( m_numPreviewVehicles < MAX_NUM_PREVIEW_VEHICLES - 1 );
+ m_numPreviewVehicles = this->InsertPreviewObject( m_previewVehicles,
+ m_numPreviewVehicles,
+ pReward,
+ true );
+ }
+ }
+
+ for( pReward = GetRewardsManager()->FindFirstMerchandise( i, Merchandise::SELLER_SIMPSON );
+ pReward != NULL;
+ pReward = GetRewardsManager()->FindNextMerchandise( i, Merchandise::SELLER_SIMPSON ) )
+ {
+ rAssert( m_numPreviewVehicles < MAX_NUM_PREVIEW_VEHICLES - 1 );
+ m_numPreviewVehicles = this->InsertPreviewObject( m_previewVehicles,
+ m_numPreviewVehicles,
+ pReward,
+ true );
+ }
+
+ for( pReward = GetRewardsManager()->FindFirstMerchandise( i, Merchandise::SELLER_GIL );
+ pReward != NULL;
+ pReward = GetRewardsManager()->FindNextMerchandise( i, Merchandise::SELLER_GIL ) )
+ {
+ rAssert( m_numPreviewVehicles < MAX_NUM_PREVIEW_VEHICLES - 1 );
+ m_numPreviewVehicles = this->InsertPreviewObject( m_previewVehicles,
+ m_numPreviewVehicles,
+ pReward,
+ true );
+ }
+ }
+
+ rAssertMsg( m_numPreviewVehicles > 0, "No vehicle selections!" );
+
+ // apply cheats, if enabled
+ //
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_VEHICLES ) )
+ {
+ for( int i = 0; i < m_numPreviewVehicles; i++ )
+ {
+ m_previewVehicles[ i ].isUnlocked = true;
+ }
+ }
+}
+
+void CGuiScreenPhoneBooth::OnUpdate( unsigned int elapsedTime )
+{
+ IGuiScreenRewards::OnUpdate( elapsedTime );
+}
+
+void
+CGuiScreenPhoneBooth::LoadSelectedReward()
+{
+ // unload 3D model first before loading reward
+ //
+ this->Unload3DModel();
+
+ m_isLoading = true; // set loading flag
+ m_isLoadingReward = true; // we're loading the selected reward
+
+ const PreviewObject* currentPreviewObject = this->GetCurrentPreviewObject();
+
+ VehicleCentral::DriverInit driverInit = VehicleCentral::ALLOW_DRIVER;
+ ActionButton::SummonVehiclePhone::LoadVehicle( currentPreviewObject->name,
+ currentPreviewObject->filename,
+ driverInit );
+}
+
+void
+CGuiScreenPhoneBooth::UpdateDamagedInfo()
+{
+ if( this->GetCurrentPreviewObject()->isUnlocked )
+ {
+ m_damagedInfo->SetVisible( true );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ Reward* pReward = this->GetCurrentPreviewObject()->pReward;
+ rAssert( pReward != NULL );
+
+ char buffer[ 16 ];
+
+ int carIndex = GetCharacterSheetManager()->GetCarIndex( pReward->GetName() );
+ if( carIndex != -1 )
+ {
+ float damagedAmount = 1.0f - GetCharacterSheetManager()->GetCarHealth( carIndex );
+ if( damagedAmount >= 0.0f && damagedAmount <= 1.0f )
+ {
+ sprintf( buffer, "%.0f %%", damagedAmount * 100 );
+ rAssert( m_vehicleDamaged != NULL );
+ m_vehicleDamaged->SetString( 0, buffer );
+ }
+ else
+ {
+ rAssertMsg( false, "Why is the damage amount not between 0 and 1??" );
+ }
+
+ rAssert( m_repairCostInfo != NULL );
+ m_repairCostInfo->SetVisible( damagedAmount == 1.0f );
+
+ sprintf( buffer, "%d", pReward->GetRepairCost() );
+ rAssert( m_vehicleRepairCost != NULL );
+ m_vehicleRepairCost->SetString( 0, buffer );
+ }
+ else
+ {
+ rAssertMsg( false, "Why is the car index -1??" );
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+ }
+ else
+ {
+ m_damagedInfo->SetVisible( false );
+ }
+}
+
+#ifdef SRR2_OVERRIDE_CAR_SELECTION
+
+void
+CGuiScreenPhoneBooth::HandleMessageForCar( eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2 )
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ rAssert( param1 == 0 );
+
+ if( m_menuTeleport )
+ {
+ if( s_currentTeleportSelection >= 0 &&
+ s_currentTeleportSelection < CharacterManager::GetNumTeleportDests() )
+ {
+ CharacterManager::Teleport(s_currentTeleportSelection );
+
+ m_pParent->HandleMessage( GUI_MSG_BACK_SCREEN );
+ }
+ else
+ {
+ rAssertMsg( 0, "WARNING: *** Invalid teleport destination selection!" );
+ }
+ }
+ else
+ {
+ if( s_currentDebugVehicleSelection >= 0 &&
+ s_currentDebugVehicleSelection < static_cast<int>( NUM_OVERRIDE_VEHICLES ) )
+ {
+ char vehicleFileName[ 32 ];
+ sprintf( vehicleFileName, "%s%s.p3d",
+ VEHICLES_DIR,
+ OVERRIDE_VEHICLE_NAMES[ s_currentDebugVehicleSelection ] );
+
+ ActionButton::SummonVehiclePhone::CarSelectionInfo* pInfo =
+ ActionButton::SummonVehiclePhone::GetDebugCarSelectInfo();
+
+ rAssert( pInfo );
+ pInfo->AddDebugVehicleSelectionInfo( vehicleFileName,
+ OVERRIDE_VEHICLE_NAMES[ s_currentDebugVehicleSelection ],
+ "" );
+
+ this->Unload3DModel(); // unload 3D model first
+
+ ActionButton::SummonVehiclePhone::LoadDebugVehicle();
+
+ m_state = GUI_WINDOW_STATE_IDLE;
+ }
+ else
+ {
+ rAssertMsg( 0, "WARNING: *** Invalid vehicle selection!" );
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_VALUE_CHANGED:
+ {
+ rAssert( param1 == 0 );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ Scrooby::Text* vehicleNames = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( 0 )->GetItemValue() );
+ rAssert( vehicleNames != NULL );
+
+ if( m_menuTeleport )
+ {
+ s_currentTeleportSelection = static_cast<int>( param2 );
+
+ if( s_currentTeleportSelection == 99 )
+ {
+ s_currentTeleportSelection = CharacterManager::GetNumTeleportDests() - 1;
+ m_pMenu->SetSelectionValue( 0, s_currentTeleportSelection);
+ }
+
+ if( s_currentTeleportSelection >= CharacterManager::GetNumTeleportDests() )
+ {
+ s_currentTeleportSelection = 0;
+ m_pMenu->SetSelectionValue( 0, s_currentTeleportSelection);
+ }
+
+ vehicleNames->SetString( s_currentTeleportSelection, CharacterManager::GetTeleportDest(s_currentTeleportSelection) );
+ }
+ else
+ {
+ s_currentDebugVehicleSelection = static_cast<int>( param2 );
+
+ if( param2 < NUM_OVERRIDE_VEHICLES )
+ {
+ char textBibleEntry[ 16 ];
+ strcpy( textBibleEntry, OVERRIDE_VEHICLE_NAMES[ s_currentDebugVehicleSelection ] );
+ UnicodeChar* textBibleString = GetTextBibleString( strupr( textBibleEntry ) );
+ UnicodeString unicodeString;
+ if( textBibleString != NULL )
+ {
+ unicodeString.ReadUnicode( textBibleString );
+ }
+ else
+ {
+ unicodeString.ReadAscii( OVERRIDE_VEHICLE_NAMES[ s_currentDebugVehicleSelection ] );
+ }
+
+ vehicleNames->SetString( s_currentDebugVehicleSelection, unicodeString );
+ }
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+ }
+
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+#endif
+
diff --git a/game/code/presentation/gui/ingame/guiscreenphonebooth.h b/game/code/presentation/gui/ingame/guiscreenphonebooth.h
new file mode 100644
index 0000000..c37fd57
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenphonebooth.h
@@ -0,0 +1,94 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPhoneBooth
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/21 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENPHONEBOOTH_H
+#define GUISCREENPHONEBOOTH_H
+
+#ifndef RAD_E3
+ // enable car selection overlay for selecting any car
+ //
+ #define SRR2_OVERRIDE_CAR_SELECTION
+#endif
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenrewards.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenPhoneBooth : public IGuiScreenRewards
+{
+public:
+ CGuiScreenPhoneBooth( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenPhoneBooth();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+#ifdef RAD_WIN32
+ virtual eFEHotspotType CheckCursorAgainstHotspots( float x, float y );
+#endif
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ virtual void On3DModelLoaded( const PreviewObject* previewObject );
+ virtual const PreviewObject* GetCurrentPreviewObject() const;
+ virtual void InitMenu();
+
+private:
+ virtual void OnUpdate( unsigned int elapsedTime );
+ void LoadSelectedReward();
+ void UpdateDamagedInfo();
+
+ Scrooby::Group* m_damagedInfo;
+ Scrooby::Text* m_vehicleDamaged;
+ Scrooby::Group* m_repairCostInfo;
+ Scrooby::Text* m_vehicleRepairCost;
+
+#ifdef RAD_WIN32
+ Scrooby::Sprite* m_leftArrow;
+ Scrooby::Sprite* m_rightArrow;
+#endif
+
+#ifdef SRR2_OVERRIDE_CAR_SELECTION
+ void HandleMessageForCar( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ CGuiMenu* m_pMenu;
+ Scrooby::Layer* m_carSelectOverlay;
+ bool m_menuTeleport;
+
+ static int s_currentDebugVehicleSelection;
+ static unsigned s_currentTeleportSelection;
+#endif
+
+};
+
+#endif // GUISCREENPHONEBOOTH_H
diff --git a/game/code/presentation/gui/ingame/guiscreenpurchaserewards.cpp b/game/code/presentation/gui/ingame/guiscreenpurchaserewards.cpp
new file mode 100644
index 0000000..ccec82a
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpurchaserewards.cpp
@@ -0,0 +1,1078 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPurchaseRewards
+//
+// Description: Implementation of the CGuiScreenPurchaseRewards class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/21 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenpurchaserewards.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/hudevents/hudcoincollected.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guitextbible.h>
+
+#include <ai/actionbuttonhandler.h>
+#include <cheats/cheatinputsystem.h>
+#include <mission/gameplaymanager.h>
+#include <mission/rewards/rewardsmanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/rewards/reward.h>
+#include <sound/soundmanager.h>
+
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/coins/coinmanager.h>
+
+#include <p3d/anim/animate.hpp>
+#include <p3d/anim/poseanimation.hpp>
+#include <p3d/anim/skeleton.hpp>
+#include <raddebug.hpp> // Foundation
+
+// Scrooby Header Files
+//
+#include <screen.h>
+#include <page.h>
+#include <group.h>
+#include <sprite.h>
+#include <pure3dobject.h>
+
+#include <string.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const float PREVIEW_CHARACTER_SCALE = 2.0f;
+const float PREVIEW_PEDESTAL_SCALE_FOR_SKINS = 0.75f;
+
+const float PREVIEW_VEHICLE_RADIUS = 3.6f;
+
+const tUID CHARACTER_NAME_HOMER = tEntity::MakeUID( "homer" );
+const tUID CHARACTER_NAME_BART = tEntity::MakeUID( "bart" );
+const tUID CHARACTER_NAME_LISA = tEntity::MakeUID( "lisa" );
+const tUID CHARACTER_NAME_MARGE = tEntity::MakeUID( "marge" );
+const tUID CHARACTER_NAME_APU = tEntity::MakeUID( "apu" );
+
+const char* PURCHASE_REWARDS_INVENTORY_SECTION = "FE_PurchaseRewardsScreen";
+const char* PURCHASE_REWARDS_BGD = "art\\frontend\\scrooby\\resource\\pure3d\\rewardbg.p3d";
+const char* PURCHASE_REWARDS_BGD_DRAWABLE = "Pedestal_Scenegraph";
+const char* PURCHASE_REWARDS_BGD_CAMERA = "Pedestal_Camera";
+const char* PURCHASE_REWARDS_BGD_MULTICONTROLLER = "Pedestal_MasterController";
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenPurchaseRewards::CGuiScreenPurchaseRewards
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPurchaseRewards::CGuiScreenPurchaseRewards
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: IGuiScreenRewards( pScreen,
+ pScreen->GetPage( "Rewards" ),
+ pParent,
+ GUI_SCREEN_ID_PURCHASE_REWARDS ),
+ m_currentType( Merchandise::INVALID_SELLER_TYPE ),
+ m_isPurchasingReward( false ),
+ m_elapsedCoinDecrementTotalTime( 0 ),
+ m_elapsedCoinDecrementTime( 0 ),
+ m_bankValueBeforePurchase( 0 ),
+ m_purchaseLabel( NULL ),
+ m_skinMultiController( NULL ),
+ m_skinAnimController( NULL )
+{
+ this->SetFadingEnabled( false );
+ this->SetIrisWipeEnabled( true );
+
+ // Retrieve the Scrooby drawing elements (from Rewards page).
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "Rewards" );
+ rAssert( pPage != NULL );
+
+#ifdef RAD_WIN32
+ m_leftArrow = pPage->GetGroup( "Arrows" )->GetSprite( "LeftArrow" );
+ m_rightArrow = pPage->GetGroup( "Arrows" )->GetSprite( "RightArrow" );
+ m_leftArrow->ScaleAboutCenter( 1.3f );
+ m_rightArrow->ScaleAboutCenter( 1.3f );
+#endif
+
+ // Retrieve the Scrooby drawing elements (from Coins page).
+ //
+ pPage = m_pScroobyScreen->GetPage( "Coins" );
+ rAssert( pPage != NULL );
+ CGuiScreenHud::SetNumCoinsDisplay( pPage->GetSprite( "NumCoins" ) );
+
+// m_numCoins.SetScroobyText( pPage->GetGroup( "NumCoins" ), "NumCoins" );
+
+ // get purchase button label
+ //
+ rAssert( m_buttonIcons[ BUTTON_ICON_ACCEPT ] != NULL );
+ m_purchaseLabel = m_buttonIcons[ BUTTON_ICON_ACCEPT ]->GetText( "Accept" );
+ rAssert( m_purchaseLabel != NULL );
+/*
+ pPage = m_pScroobyScreen->GetPage( "Buy" );
+ if( pPage != NULL )
+ {
+ m_buttonIcons[ BUTTON_ICON_ACCEPT ] = pPage->GetGroup( "AcceptLabel" );
+ rAssert( m_buttonIcons[ BUTTON_ICON_ACCEPT ] != NULL );
+
+ m_purchaseLabel = m_buttonIcons[ BUTTON_ICON_ACCEPT ]->GetText( "Accept" );
+ rAssert( m_purchaseLabel != NULL );
+ }
+*/
+
+ // create and setup controllers for character animations
+ //
+ m_skinMultiController = new tMultiController( 1, 90.0f );
+ rAssert( m_skinMultiController != NULL );
+ m_skinMultiController->AddRef();
+ m_skinMultiController->SetFramerate( 30.0f );
+
+ m_skinAnimController = new tPoseAnimationController;
+ rAssert( m_skinAnimController != NULL );
+ m_skinAnimController->AddRef();
+ m_skinMultiController->SetTrack( 0, m_skinAnimController );
+
+ m_skinTrackInfo.startTime = 0.0f;
+ m_skinTrackInfo.endTime = 0.0f;
+ m_skinTrackInfo.offset = 0.0f;
+ m_skinTrackInfo.scale = 1.0f;
+ m_skinMultiController->SetTrackInfo( 0, &m_skinTrackInfo );
+
+ rAssert( m_previewWindow != NULL );
+ m_previewWindow->SetMultiController( m_skinMultiController );
+
+ p3d::inventory->AddSection( PURCHASE_REWARDS_INVENTORY_SECTION );
+}
+
+
+//===========================================================================
+// CGuiScreenPurchaseRewards::~CGuiScreenPurchaseRewards
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenPurchaseRewards::~CGuiScreenPurchaseRewards()
+{
+ p3d::pddi->DrawSync();
+
+ p3d::inventory->DeleteSection( PURCHASE_REWARDS_INVENTORY_SECTION );
+
+ if( m_skinAnimController != NULL )
+ {
+ m_skinAnimController->Release();
+ m_skinAnimController = NULL;
+ }
+
+ if( m_skinMultiController != NULL )
+ {
+ m_skinMultiController->Release();
+ m_skinMultiController = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenPurchaseRewards::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+
+void CGuiScreenPurchaseRewards::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( message == GUI_MSG_WINDOW_ENTER )
+ {
+ // set current purchase center type
+ //
+ m_currentType = static_cast<Merchandise::eSellerType>( param1 );
+ rAssert( m_currentType != Merchandise::INVALID_SELLER_TYPE );
+ }
+
+ if( m_isLoading )
+ {
+ if( message == GUI_MSG_CONTROLLER_SELECT ||
+ message == GUI_MSG_CONTROLLER_BACK )
+ {
+ // don't allow user to select or back out of screen
+ // during loading
+ //
+ return;
+ }
+ }
+
+ rAssert( m_pRewardsMenu != NULL );
+ if( m_pRewardsMenu->HasSelectionBeenMade() || m_isLoadingReward || m_isPurchasingReward )
+ {
+ if( this->IsControllerMessage( message ) )
+ {
+ // selection has already been made or reward is currently loading,
+ // ignore all controller inputs
+ //
+ return;
+ }
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ int selectionDelta = 0;
+
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ if( !this->IsButtonVisible( BUTTON_ICON_ACCEPT ) )
+ {
+ // not enough coins to purchase reward
+ //
+ GetEventManager()->TriggerEvent( EVENT_FE_LOCKED_OUT );
+
+ return;
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ if( !m_pRewardsMenu->HasSelectionBeenMade() )
+ {
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_BACK );
+
+ m_pParent->HandleMessage( GUI_MSG_RESUME_INGAME );
+ }
+
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ // load selected reward item
+ //
+ this->PurchaseReward();
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_LEFT:
+ {
+ selectionDelta = -2;
+
+ // follow-thru
+ //
+ }
+ case GUI_MSG_CONTROLLER_RIGHT:
+ {
+ selectionDelta++;
+
+ int previousSelection = 0;
+
+ if( m_currentType == Merchandise::SELLER_INTERIOR )
+ {
+ if( m_numPreviewClothing > 1 )
+ {
+ previousSelection = m_currentPreviewClothing;
+
+ m_currentPreviewClothing = (m_currentPreviewClothing + m_numPreviewClothing + selectionDelta) % m_numPreviewClothing;
+
+ this->On3DModelSelectionChange( &m_previewClothing[ m_currentPreviewClothing ] );
+ }
+ }
+ else
+ {
+ if( m_numPreviewVehicles > 1 )
+ {
+ previousSelection = m_currentPreviewVehicle;
+
+ m_currentPreviewVehicle = (m_currentPreviewVehicle + m_numPreviewVehicles + selectionDelta) % m_numPreviewVehicles;
+
+ this->On3DModelSelectionChange( &m_previewVehicles[ m_currentPreviewVehicle ] );
+ }
+ }
+
+ this->UpdateRewardPrice();
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ else if( m_state == GUI_WINDOW_STATE_INTRO )
+ {
+ if( message == GUI_MSG_UPDATE )
+ {
+ if( m_numTransitionsPending == 1 && m_currentIrisState == IRIS_STATE_CLOSED )
+ {
+ // resume iris wipe
+ //
+ this->IrisWipeOpen();
+
+ // show the preview pedestal
+ //
+ rAssert( m_previewPedestal != NULL );
+ m_previewPedestal->SetVisible( true );
+ rAssert( m_previewBgd != NULL );
+ m_previewBgd->SetVisible( true );
+ }
+ }
+ }
+ else if( m_state == GUI_WINDOW_STATE_OUTRO )
+ {
+ if( message == GUI_MSG_UPDATE )
+ {
+ if( m_numTransitionsPending == 1 && m_currentIrisState == IRIS_STATE_CLOSED )
+ {
+ // resume iris wipe
+ //
+ this->IrisWipeOpen();
+
+ p3d::pddi->DrawSync();
+
+ // hide the preview pedestal
+ //
+ rAssert( m_previewPedestal != NULL );
+ m_previewPedestal->SetVisible( false );
+ rAssert( m_previewBgd != NULL );
+ m_previewBgd->SetVisible( false );
+
+ m_previewBgd->SetDrawable( NULL );
+ m_previewBgd->SetCamera( NULL );
+ m_previewBgd->SetMultiController( NULL );
+
+ p3d::inventory->RemoveSectionElements( PURCHASE_REWARDS_INVENTORY_SECTION );
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ IGuiScreenRewards::HandleMessage( message, param1, param2 );
+}
+
+void
+CGuiScreenPurchaseRewards::OnProcessRequestsComplete( void* pUserData )
+{
+ if( reinterpret_cast<Scrooby::Pure3dObject*>( pUserData ) == m_previewBgd )
+ {
+ m_numTransitionsPending--;
+
+ // push and select inventory section for searching
+ //
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( PURCHASE_REWARDS_INVENTORY_SECTION );
+ bool currentSectionOnly = p3d::inventory->GetCurrentSectionOnly();
+ p3d::inventory->SetCurrentSectionOnly( true );
+
+ // search for pedestal drawable
+ //
+ tDrawable* drawable = p3d::find<tDrawable>( PURCHASE_REWARDS_BGD_DRAWABLE );
+ if( drawable != NULL )
+ {
+ rAssert( m_previewBgd != NULL );
+ m_previewBgd->SetDrawable( drawable );
+
+ tCamera* camera = p3d::find<tCamera>( PURCHASE_REWARDS_BGD_CAMERA );
+ rAssert( camera != NULL );
+ m_previewBgd->SetCamera( camera );
+ m_previewPedestal->SetCamera( camera );
+ m_previewWindow->SetCamera( camera );
+
+ tMultiController* multiController = p3d::find<tMultiController>( PURCHASE_REWARDS_BGD_MULTICONTROLLER );
+ m_previewBgd->SetMultiController( multiController );
+ }
+ else
+ {
+ rAssertMsg( false, "*** Can't find 3D pedestal drawable!" );
+ }
+
+ // pop inventory section and restore states
+ //
+ p3d::inventory->SetCurrentSectionOnly( currentSectionOnly );
+ p3d::inventory->PopSection();
+ }
+ else if( m_isLoadingReward )
+ {
+ // this means we were waiting for the selected character skin to load;
+ // so, now that it's loaded, let's exit outa here
+ //
+ m_pParent->HandleMessage( GUI_MSG_RESUME_INGAME );
+ }
+ else
+ {
+ IGuiScreenRewards::OnProcessRequestsComplete( pUserData );
+ }
+}
+
+#ifdef RAD_WIN32
+//===========================================================================
+// CGuiScreenPurchaseRewards::CheckCursorAgainstHotspots
+//===========================================================================
+// Description: Checks cursor position against its list of hotspots.
+//
+// Constraints: None.
+//
+// Parameters: float x - The x position of cursor in P3D coordinates.
+// float y - The y position of cursor in P3D coordinates.
+//
+// Return: N/A.
+//
+//===========================================================================
+eFEHotspotType CGuiScreenPurchaseRewards::CheckCursorAgainstHotspots( float x, float y )
+{
+ eFEHotspotType hotSpotType = CGuiScreen::CheckCursorAgainstHotspots( x, y );
+ if( hotSpotType == HOTSPOT_NONE )
+ {
+ if( m_leftArrow )
+ {
+ if( m_leftArrow->IsPointInBoundingRect( x, y ) )
+ {
+ hotSpotType = HOTSPOT_ARROWLEFT;
+ }
+ }
+ if( m_rightArrow )
+ {
+ if( m_rightArrow->IsPointInBoundingRect( x, y ) )
+ {
+ hotSpotType = HOTSPOT_ARROWRIGHT;
+ }
+ }
+
+ }
+ return hotSpotType;
+}
+#endif
+
+//===========================================================================
+// CGuiScreenPurchaseRewards::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPurchaseRewards::InitIntro()
+{
+ // load 3D pedestal
+ //
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ PURCHASE_REWARDS_BGD,
+ GMA_LEVEL_MISSION,
+ PURCHASE_REWARDS_INVENTORY_SECTION,
+ PURCHASE_REWARDS_INVENTORY_SECTION,
+ this,
+ reinterpret_cast<void*>( m_previewBgd ) );
+ m_numTransitionsPending++;
+
+ IGuiScreenRewards::InitIntro();
+
+ // load current 3D model object
+ //
+ this->On3DModelSelectionChange( this->GetCurrentPreviewObject() );
+
+ // show and update reward price
+ //
+ rAssert( m_rewardPrice != NULL );
+ m_rewardPrice->SetVisible( true );
+ this->UpdateRewardPrice();
+
+ // show/hide stats button label
+ //
+ rAssert( m_statsOverlayButton );
+ m_statsOverlayButton->SetVisible( m_currentType != Merchandise::SELLER_INTERIOR );
+
+ // hide vehicle stats by default
+ //
+ this->SetVehicleStatsVisible( false );
+
+ // adjust pedestal scale
+ //
+ rAssert( m_previewPedestal != NULL );
+ if( m_currentType == Merchandise::SELLER_INTERIOR )
+ {
+ // scale down pedestal a bit
+ //
+ m_previewPedestal->SetDrawableScale( PREVIEW_PEDESTAL_SCALE_FOR_SKINS );
+
+ // notify sound manager that game is paused, include dialogue in
+ // ducking to kill gag sounds -- Esan
+ //
+ GetSoundManager()->DuckEverythingButMusicBegin( true );
+ }
+ else
+ {
+ m_previewPedestal->SetDrawableScale( 1.0f );
+
+ // notify sound manager that game is paused, don't duck dialogue
+ // so we can still hear ol' Gil -- Esan
+ //
+ GetSoundManager()->OnStoreScreenStart( true );
+ }
+
+ // enable/disable L/R arrows
+ //
+ if( m_numPreviewClothing > 1 || m_numPreviewVehicles > 1 )
+ {
+ m_pRewardsMenu->SetSelectionValueCount( 0, 2 );
+ }
+ else
+ {
+ m_pRewardsMenu->SetSelectionValueCount( 0, 1 );
+ }
+
+ m_bankValueBeforePurchase = GetCoinManager()->GetBankValue();
+}
+
+//===========================================================================
+// CGuiScreenPurchaseRewards::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPurchaseRewards::InitRunning()
+{
+ IGuiScreenRewards::InitRunning();
+
+ CGuiScreenHud::UpdateNumCoinsDisplay( m_bankValueBeforePurchase );
+}
+
+//===========================================================================
+// CGuiScreenPurchaseRewards::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenPurchaseRewards::InitOutro()
+{
+ // hide 3D coin on FE render layer
+ //
+ CGuiScreenHud::UpdateNumCoinsDisplay( 0, false );
+
+ IGuiScreenRewards::InitOutro();
+
+ if( m_currentType == Merchandise::SELLER_INTERIOR )
+ {
+ // notify sound manager that game is paused
+ //
+ GetSoundManager()->DuckEverythingButMusicEnd( true );
+ }
+ else
+ {
+ GetSoundManager()->OnStoreScreenEnd();
+ }
+}
+
+void
+CGuiScreenPurchaseRewards::On3DModelLoaded( const PreviewObject* previewObject )
+{
+ p3d::pddi->DrawSync();
+
+ // search for 3D model in inventory
+ //
+ tDrawable* drawable = p3d::find<tDrawable>( previewObject->nameModel );
+ if( drawable != NULL )
+ {
+ rAssert( m_previewWindow != NULL );
+ m_previewWindow->SetDrawable( drawable );
+
+ if( m_currentType != Merchandise::SELLER_INTERIOR )
+ {
+ // place vehicles (w/ wheels) on the ground (y = 0)
+ //
+ rmt::Box3D box3D;
+ drawable->GetBoundingBox( &box3D );
+ float groundOffset = box3D.low.y;
+ m_previewWindow->SetDrawableTranslation( 0.0f, -groundOffset, 0.0f );
+
+ // scale vehicles so that they fit nicely on pedestal
+ //
+ rmt::Sphere sphere;
+ drawable->GetBoundingSphere( &sphere );
+ m_previewWindow->SetDrawableScale( PREVIEW_VEHICLE_RADIUS / sphere.radius );
+ }
+ else
+ {
+ // assume characters are already placed on ground (y = 0)
+ //
+ m_previewWindow->SetDrawableTranslation( 0.0f, 0.0f, 0.0f );
+
+ // scale characters larger to fill the screen
+ //
+ m_previewWindow->SetDrawableScale( PREVIEW_CHARACTER_SCALE );
+
+ // set character animation
+ //
+ HeapMgr()->PushHeap( GMA_LEVEL_MISSION );
+ tDrawablePose *drawable_pose = dynamic_cast<tDrawablePose *>(drawable);
+ rAssert( drawable_pose != NULL );
+
+ m_skinAnimController->SetPose( drawable_pose->GetPose() );
+
+ p3d::inventory->SetCurrentSectionOnly( false );
+ char anim_name[32];
+ switch(GetGameplayManager()->GetCurrentLevelIndex())
+ {
+ case 0:
+ case 6:
+ strcpy(anim_name,"hom");
+ break;
+ case 1:
+ case 5:
+ strcpy(anim_name,"brt");
+ break;
+ case 2:
+ strcpy(anim_name,"lsa");
+ break;
+ case 3:
+ strcpy(anim_name,"mrg");
+ break;
+ case 4:
+ strcpy(anim_name,"apu");
+ break;
+ default:
+ strcpy( anim_name,"hom" );
+ break;
+ }
+ strcat(anim_name, "_loco_idle_rest");
+ tAnimation* anim = p3d::find<tAnimation>( anim_name );
+ rAssert( anim != NULL );
+ anim->SetCyclic( true );
+
+ rAssert( m_skinAnimController != NULL );
+ m_skinAnimController->SetAnimation( anim, 0.0f, 0.0f );
+
+ m_skinTrackInfo.endTime = anim->GetNumFrames();
+
+ rAssert( m_skinMultiController != NULL );
+ m_skinMultiController->Reset();
+ m_skinMultiController->SetFrame( 0.0f );
+ m_skinMultiController->SetTrackInfo( 0, &m_skinTrackInfo );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_MISSION );
+
+ }
+ }
+ else
+ {
+ rAssertMsg( false, "*** Can't find drawable for 3D model!!" );
+ }
+}
+
+const PreviewObject*
+CGuiScreenPurchaseRewards::GetCurrentPreviewObject() const
+{
+ const PreviewObject* previewObject = NULL;
+
+ switch( m_currentType )
+ {
+ case Merchandise::SELLER_INTERIOR:
+ {
+ if( m_numPreviewClothing > 0 )
+ {
+ previewObject = &m_previewClothing[ m_currentPreviewClothing ];
+ }
+
+ break;
+ }
+ case Merchandise::SELLER_SIMPSON:
+ case Merchandise::SELLER_GIL:
+ {
+ if( m_numPreviewVehicles > 0 )
+ {
+ previewObject = &m_previewVehicles[ m_currentPreviewVehicle ];
+ }
+
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ break;
+ }
+ }
+
+ return previewObject;
+}
+
+void
+CGuiScreenPurchaseRewards::InitMenu()
+{
+ int currentLevel = GetGameplayManager()->GetCurrentLevelIndex();
+ Reward* pReward = NULL;
+
+ m_numPreviewClothing = 0;
+ m_numPreviewVehicles = 0;
+
+ switch( m_currentType )
+ {
+ case Merchandise::SELLER_INTERIOR:
+ {
+ Character* currentCharacter = GetCharacterManager()->GetCharacter( 0 );
+ rAssert( currentCharacter != NULL );
+ const char* currentSkinName = GetCharacterManager()->GetModelName( currentCharacter );
+
+ // insert default character skin first
+ //
+ pReward = GetRewardsManager()->GetReward( currentLevel, Reward::eDefaultSkin );
+ if( pReward != NULL )
+ {
+ if( strcmp( currentSkinName, pReward->GetName() ) != 0 )
+ {
+ m_numPreviewClothing = this->InsertPreviewObject( m_previewClothing,
+ m_numPreviewClothing,
+ pReward );
+ }
+ }
+
+ // insert all other purchasable skins
+ //
+ for( pReward = GetRewardsManager()->FindFirstMerchandise( currentLevel, m_currentType );
+ pReward != NULL;
+ pReward = GetRewardsManager()->FindNextMerchandise( currentLevel, m_currentType ) )
+ {
+ if( strcmp( currentSkinName, pReward->GetName() ) != 0 )
+ {
+ rAssert( m_numPreviewClothing < MAX_NUM_PREVIEW_CLOTHING );
+ m_numPreviewClothing = this->InsertPreviewObject( m_previewClothing,
+ m_numPreviewClothing,
+ pReward );
+ }
+ }
+
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_SKINS ) )
+ {
+ for( int i = 0; i < m_numPreviewClothing; i++ )
+ {
+ m_previewClothing[ i ].isUnlocked = true;
+ }
+ }
+
+ break;
+ }
+ case Merchandise::SELLER_SIMPSON:
+ case Merchandise::SELLER_GIL:
+ {
+ for( pReward = GetRewardsManager()->FindFirstMerchandise( currentLevel, m_currentType );
+ pReward != NULL;
+ pReward = GetRewardsManager()->FindNextMerchandise( currentLevel, m_currentType ) )
+ {
+ // only display locked rewards
+ //
+ if( !pReward->RewardStatus() )
+ {
+ rAssert( m_numPreviewVehicles < MAX_NUM_PREVIEW_VEHICLES );
+ m_numPreviewVehicles = this->InsertPreviewObject( m_previewVehicles,
+ m_numPreviewVehicles,
+ pReward,
+ true );
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ break;
+ }
+ }
+}
+
+void
+CGuiScreenPurchaseRewards::OnUpdate( unsigned int elapsedTime )
+{
+/*
+ // always show accept button
+ //
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+*/
+/*
+ // rotate reward object
+ //
+ const unsigned int ROTATION_PERIOD = 3000;
+
+ m_elapsedRotationTime = (m_elapsedRotationTime + elapsedTime) % ROTATION_PERIOD;
+
+ rAssert( m_previewWindow != NULL );
+ m_previewWindow->ResetTransformation();
+
+ float rotation = (m_elapsedRotationTime / (float)ROTATION_PERIOD) * 360.0f;
+ m_previewWindow->RotateAboutCenter( rotation,
+ rmt::Vector( 0.0f, 1.0f, 0.0f ) );
+*/
+ if( m_isPurchasingReward )
+ {
+ const unsigned int COINS_DECREMENT_DURATION = 1000; // in msec
+ const unsigned int COINS_DECREMENT_PERIOD = 100; // in msec
+
+ m_elapsedCoinDecrementTotalTime += elapsedTime;
+ if( m_elapsedCoinDecrementTotalTime < COINS_DECREMENT_DURATION )
+ {
+ m_elapsedCoinDecrementTime += elapsedTime;
+ if( m_elapsedCoinDecrementTime > COINS_DECREMENT_PERIOD )
+ {
+ int deltaCoins = m_bankValueBeforePurchase - GetCoinManager()->GetBankValue();
+ int bankValue = m_bankValueBeforePurchase - (int)( m_elapsedCoinDecrementTotalTime / (float)COINS_DECREMENT_DURATION * deltaCoins );
+
+ CGuiScreenHud::UpdateNumCoinsDisplay( bankValue );
+
+ GetCoinManager()->AddFlyDownCoin();
+
+ m_elapsedCoinDecrementTime %= COINS_DECREMENT_PERIOD;
+ }
+ }
+ else
+ {
+ CGuiScreenHud::UpdateNumCoinsDisplay( GetCoinManager()->GetBankValue() );
+
+ m_isPurchasingReward = false;
+
+ // ok, we've reached the real current bank value;
+ // now load the purchased reward now
+ //
+ this->LoadSelectedReward();
+/*
+ if( m_currentType == Merchandise::SELLER_INTERIOR )
+ {
+ this->LoadSelectedReward();
+ }
+ else
+ {
+ // don't load vehicles, user must go to the phone
+ // booth to do that
+ //
+ m_pParent->HandleMessage( GUI_MSG_BACK_SCREEN );
+ }
+*/
+ }
+ }
+
+ IGuiScreenRewards::OnUpdate( elapsedTime );
+}
+
+void
+CGuiScreenPurchaseRewards::UpdateRewardPrice()
+{
+ const PreviewObject* currentPreviewObject = this->GetCurrentPreviewObject();
+ rAssert( currentPreviewObject != NULL );
+
+ if( currentPreviewObject->isUnlocked )
+ {
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+ }
+ else
+ {
+ Merchandise* pMerchandise = GetRewardsManager()->GetMerchandise( GetGameplayManager()->GetCurrentLevelIndex(),
+ currentPreviewObject->name );
+ if( pMerchandise != NULL )
+ {
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ int rewardCost = pMerchandise->GetCost();
+ rAssertMsg( rewardCost > 0, "Reward price should be a positive integer!" );
+
+ // get "coins" text
+ //
+ char coinsText[ 32 ];
+ p3d::UnicodeToAscii( GetTextBibleString( "COINS" ), coinsText, sizeof( coinsText ) );
+
+ char buffer[ 32 ];
+#ifdef PAL
+ if( CGuiTextBible::GetCurrentLanguage() == Scrooby::XL_GERMAN )
+ {
+ // special case for German for proper grammar
+ //
+ sprintf( buffer, "Für %d %s", rewardCost, coinsText );
+ }
+ else
+#endif // PAL
+ {
+ sprintf( buffer, "%d %s", rewardCost, coinsText );
+ }
+
+ rAssert( strlen( buffer ) < sizeof( buffer ) );
+
+ rAssert( m_rewardPrice != NULL );
+ m_rewardPrice->SetString( 0, buffer );
+
+ // only show accept label if user has enough money to purchase OR
+ // if reward is already unlocked
+ //
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, GetCoinManager()->GetBankValue() >= rewardCost );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+ }
+ }
+
+ // update purchase label
+ //
+ rAssert( m_purchaseLabel != NULL );
+ m_purchaseLabel->SetIndex( currentPreviewObject->isUnlocked ? 1 : 0 );
+}
+
+void
+CGuiScreenPurchaseRewards::PurchaseReward()
+{
+ const PreviewObject* currentPreviewObject = this->GetCurrentPreviewObject();
+ rAssert( currentPreviewObject != NULL );
+
+ if( currentPreviewObject->isUnlocked )
+ {
+ // reward is already unlocked, so just load it
+ //
+ this->LoadSelectedReward();
+ }
+ else
+ {
+ bool boughtReward = GetRewardsManager()->BuyMerchandise( GetGameplayManager()->GetCurrentLevelIndex(),
+ currentPreviewObject->name );
+
+ if( boughtReward )
+ {
+ // hide lock overlay
+ //
+ rAssert( m_lockedOverlay != NULL );
+ m_lockedOverlay->SetVisible( false );
+
+ // update hud coins current item count
+ //
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ HudCoinCollected* hudCoinCollected = static_cast<HudCoinCollected*>( currentHud->GetEventHandler( CGuiScreenHud::HUD_EVENT_HANDLER_COIN_COLLECTED ) );
+ rAssert( hudCoinCollected != NULL );
+ hudCoinCollected->SetCurrentItemCount( GetCoinManager()->GetBankValue() );
+ hudCoinCollected->Start();
+ }
+
+ m_isPurchasingReward = true;
+ m_elapsedCoinDecrementTotalTime = 0;
+ m_elapsedCoinDecrementTime = 0;
+ }
+ else
+ {
+ rAssertMsg( false, "This should not happen!" );
+ }
+ }
+}
+
+void
+CGuiScreenPurchaseRewards::LoadSelectedReward()
+{
+ // unload 3D model first before loading reward
+ //
+ this->Unload3DModel();
+
+ m_isLoading = true; // set loading flag
+ m_isLoadingReward = true; // we're loading the selected reward
+
+ if( m_currentType == Merchandise::SELLER_INTERIOR )
+ {
+ Character* currentCharacter = GetCharacterManager()->GetCharacter( 0 );
+
+ char characterName[ 16 ];
+ tUID characterUID = currentCharacter->GetUID();
+
+ if( characterUID == CHARACTER_NAME_HOMER )
+ {
+ strcpy( characterName, "homer" );
+ }
+ else if( characterUID == CHARACTER_NAME_BART )
+ {
+ strcpy( characterName, "bart" );
+ }
+ else if( characterUID == CHARACTER_NAME_LISA )
+ {
+ strcpy( characterName, "lisa" );
+ }
+ else if( characterUID == CHARACTER_NAME_MARGE )
+ {
+ strcpy( characterName, "marge" );
+ }
+ else if( characterUID == CHARACTER_NAME_APU )
+ {
+ strcpy( characterName, "apu" );
+ }
+ else
+ {
+ rAssert( false );
+ }
+
+ GetCharacterManager()->SwapData( currentCharacter,
+ m_previewClothing[ m_currentPreviewClothing ].name,
+ characterName );
+
+ //Chuck: Save the skin they are buy/changing into as the current skin
+ GetCharacterSheetManager()->SetCurrentSkin(GetGameplayManager()->GetCurrentLevelIndex(),m_previewClothing[ m_currentPreviewClothing ].name);
+
+ GetCharacterManager()->PreloadCharacter( m_previewClothing[ m_currentPreviewClothing ].name,
+ characterName,
+ this );
+
+ //for the HitnRun system
+ GetEventManager()->TriggerEvent( EVENT_SWITCH_SKIN, NULL );
+
+ }
+ else
+ {
+ ActionButton::SummonVehiclePhone::LoadVehicle( m_previewVehicles[ m_currentPreviewVehicle ].name,
+ m_previewVehicles[ m_currentPreviewVehicle ].filename,
+ VehicleCentral::ALLOW_DRIVER );
+ }
+}
+
diff --git a/game/code/presentation/gui/ingame/guiscreenpurchaserewards.h b/game/code/presentation/gui/ingame/guiscreenpurchaserewards.h
new file mode 100644
index 0000000..6b972ca
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenpurchaserewards.h
@@ -0,0 +1,87 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenPurchaseRewards
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/21 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENPURCHASEREWARDS_H
+#define GUISCREENPURCHASEREWARDS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenrewards.h>
+#include <mission/rewards/merchandise.h>
+#include <p3d/anim/multicontroller.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class tMultiController;
+class tPoseAnimationController;
+class tPose;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenPurchaseRewards : public IGuiScreenRewards
+{
+public:
+ CGuiScreenPurchaseRewards( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenPurchaseRewards();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ void OnProcessRequestsComplete( void* pUserData );
+
+#ifdef RAD_WIN32
+ virtual eFEHotspotType CheckCursorAgainstHotspots( float x, float y );
+#endif
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ virtual void On3DModelLoaded( const PreviewObject* previewObject );
+ virtual const PreviewObject* GetCurrentPreviewObject() const;
+ virtual void InitMenu();
+
+private:
+ void OnUpdate( unsigned int elapsedTime );
+ void UpdateRewardPrice();
+ void UpdateBankValue();
+ void PurchaseReward();
+ void LoadSelectedReward();
+
+ Merchandise::eSellerType m_currentType;
+ bool m_isPurchasingReward;
+ unsigned int m_elapsedCoinDecrementTotalTime;
+ unsigned int m_elapsedCoinDecrementTime;
+ int m_bankValueBeforePurchase;
+ Scrooby::Text* m_purchaseLabel;
+
+#ifdef RAD_WIN32
+ Scrooby::Sprite* m_leftArrow;
+ Scrooby::Sprite* m_rightArrow;
+#endif
+
+ tMultiController::TrackInfo m_skinTrackInfo;
+ tMultiController* m_skinMultiController;
+ tPoseAnimationController* m_skinAnimController;
+
+};
+
+#endif // GUISCREENPURCHASEREWARDS_H
diff --git a/game/code/presentation/gui/ingame/guiscreenrewards.cpp b/game/code/presentation/gui/ingame/guiscreenrewards.cpp
new file mode 100644
index 0000000..997c67e
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenrewards.cpp
@@ -0,0 +1,854 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: IGuiScreenRewards
+//
+// Description: Implementation of the IGuiScreenRewards class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/21 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenrewards.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guitextbible.h>
+
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <mission/rewards/rewardsmanager.h>
+#include <mission/rewards/reward.h>
+#include <worldsim/coins/coinmanager.h>
+
+#include <screen.h>
+#include <page.h>
+#include <sprite.h>
+#include <text.h>
+#include <pure3dobject.h>
+#include <group.h>
+#include <polygon.h>
+
+#include <p3d/unicode.hpp>
+#include <raddebug.hpp> // Foundation
+
+#include <string.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const char* REWARDS_INVENTORY_SECTION = "FE_RewardsScreen";
+
+#ifdef RAD_XBOX
+ const float PHONE_BOOTH_BGD_CORRECTION_SCALE = 8.0f;
+#else
+ const float PHONE_BOOTH_BGD_CORRECTION_SCALE = 8.8f;
+#endif
+
+const float LIGHT_OPEN_CLOSE_TRANSITION_TIME = 500.0f; // in msec
+
+const char* PHONE_BOOTH_3DMODEL_CARS_DIR = "art\\frontend\\dynaload\\cars\\";
+const char* PHONE_BOOTH_2DMODEL_CARS_DIR = "art\\frontend\\dynaload\\images\\cars2D\\";
+
+//===========================================================================
+// PhoneBoothStars::
+//===========================================================================
+
+PhoneBoothStars::PhoneBoothStars( Scrooby::Page* pPage, const char* namePrefix )
+{
+ rAssert( pPage );
+ for( int i = 0; i < MAX_NUM_STARS; i++ )
+ {
+ char name[ 32 ];
+ sprintf( name, "%s_Stars%d", namePrefix, i );
+ m_stars[ i ] = pPage->GetSprite( name );
+ }
+}
+
+void
+PhoneBoothStars::SetNumStars( float numStars )
+{
+ rWarningMsg( numStars >= 0.0f && numStars <= static_cast<float>( MAX_NUM_STARS ),
+ "Number of stars exceeds boundaries!" );
+
+ for( int i = 0; i < MAX_NUM_STARS; i++ )
+ {
+ bool isOn = (i < static_cast<int>( numStars ));
+
+ // check if half on
+ //
+ bool isHalfOn = !isOn && (static_cast<float>( i ) < numStars);
+
+ rAssert( m_stars[ i ] != NULL );
+ if( isOn )
+ {
+ m_stars[ i ]->SetIndex( 2 );
+// m_stars[ i ]->SetAlpha( 1.0f );
+ }
+ else if( isHalfOn )
+ {
+ m_stars[ i ]->SetIndex( 1 );
+// m_stars[ i ]->SetAlpha( 1.0f );
+ }
+ else
+ {
+ m_stars[ i ]->SetIndex( 0 );
+// m_stars[ i ]->SetAlpha( 0.5f );
+ }
+ }
+}
+
+
+//===========================================================================
+// IGuiScreenRewards::
+//===========================================================================
+
+IGuiScreenRewards::IGuiScreenRewards( Scrooby::Screen* pScreen,
+ Scrooby::Page* pPage,
+ CGuiEntity* pParent,
+ eGuiWindowID windowID )
+: CGuiScreen( pScreen, pParent, windowID ),
+ m_pRewardsMenu( NULL ),
+ m_currentState( REWARDS_SCREEN_LIGHT_CLOSED ),
+ m_elapsedTime( 0.0f ),
+ m_previewLightCover( NULL ),
+ m_previewWindow( NULL ),
+ m_previewPedestal( NULL ),
+ m_previewBgd( NULL ),
+ m_previewImage( NULL ),
+ m_previewName( NULL ),
+ m_numPreviewVehicles( 0 ),
+ m_currentPreviewVehicle( 0 ),
+ m_numPreviewClothing( 0 ),
+ m_currentPreviewClothing( 0 ),
+ m_isLoading( false ),
+ m_isLoadingReward( false ),
+ m_lockedOverlay( NULL ),
+ m_lockedLevel( NULL ),
+ m_rewardPrice( NULL ),
+ m_statsOverlay( NULL ),
+ m_statsOverlayButton( NULL ),
+ m_statsOverlayButtonLabel( NULL ),
+ m_statsOverlayToggle( false )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ rAssert( pPage != NULL );
+
+ Scrooby::Sprite* previewLightL = pPage->GetSprite( "PreviewLightL" );
+ Scrooby::Sprite* previewLightR = pPage->GetSprite( "PreviewLightR" );
+ if( previewLightL != NULL && previewLightR != NULL )
+ {
+ previewLightL->ResetTransformation();
+ previewLightR->ResetTransformation();
+
+ // apply image correction scales
+ //
+ previewLightL->ScaleAboutCenter( PHONE_BOOTH_BGD_CORRECTION_SCALE );
+ previewLightR->ScaleAboutCenter( PHONE_BOOTH_BGD_CORRECTION_SCALE );
+
+ // flip the right light image horizontally to mirror the
+ // left light image
+ //
+ previewLightR->RotateAboutCenter( 180.0f, rmt::Vector( 0.0f, 1.0f, 0.0f ) );
+ }
+
+ // get preview light cover (for opening/closing light)
+ //
+ m_previewLightCover = pPage->GetPolygon( "PreviewLightCover" );
+
+ // get preview image
+ //
+ m_previewImage = pPage->GetSprite( "PreviewImage" );
+
+ // create rewards menu w/ preview name
+ //
+ m_previewName = pPage->GetText( "PreviewName" );
+ rAssert( m_previewName );
+ m_previewName->SetTextMode( Scrooby::TEXT_WRAP );
+
+ m_pRewardsMenu = new CGuiMenu( this, 1, GUI_TEXT_MENU, MENU_SFX_NONE );
+ rAssert( m_pRewardsMenu != NULL );
+
+ m_pRewardsMenu->AddMenuItem( m_previewName,
+ pPage->GetText( "PreviewNameDummy" ),
+ NULL,
+ NULL,
+ pPage->GetSprite( "LeftArrow" ),
+ pPage->GetSprite( "RightArrow" ) );
+
+ // get preview window and pedestal
+ //
+ m_previewWindow = pPage->GetPure3dObject( "PreviewWindow" );
+ if( m_previewWindow != NULL )
+ {
+ m_previewWindow->SetVisible( false );
+ m_previewWindow->SetClearDepthBuffer( true );
+ }
+
+ m_previewPedestal = pPage->GetPure3dObject( "RewardFG" );
+ if ( m_previewPedestal != NULL )
+ {
+ m_previewPedestal->SetVisible( false );
+ }
+
+ m_previewBgd = pPage->GetPure3dObject( "RewardBG" );
+ if( m_previewBgd != NULL )
+ {
+ m_previewBgd->SetVisible( false );
+ m_previewBgd->SetClearDepthBuffer( true );
+ }
+
+ // get lock overlay
+ //
+ m_lockedOverlay = pPage->GetGroup( "Locked" );
+ rAssert( m_lockedOverlay );
+
+ // get locked level
+ //
+ m_lockedLevel = pPage->GetText( "Level" );
+
+#ifdef RAD_E3
+ if( m_lockedLevel != NULL )
+ {
+ m_lockedLevel->SetVisible( false ); // hide level display on E3 build
+ }
+#endif
+
+ // get reward price
+ //
+ m_rewardPrice = m_lockedOverlay->GetText( "Price" );
+ if( m_rewardPrice != NULL )
+ {
+ m_rewardPrice->SetDisplayOutline( true );
+
+ Scrooby::Text* toPurchase = m_lockedOverlay->GetText( "ToPurchase" );
+ if( toPurchase != NULL )
+ {
+ toPurchase->SetDisplayOutline( true );
+ }
+ }
+
+ // get stats overlay and button label
+ //
+ m_statsOverlay = pPage->GetGroup( "VehicleStats" );
+ m_statsOverlayButton = pPage->GetGroup( "ToggleView" );
+ m_statsOverlayButtonLabel = pPage->GetText( "ToggleView" );
+
+ // get vehicle star ratings
+ //
+ for( int i = 0; i < NUM_VEHICLE_RATINGS; i++ )
+ {
+ m_vehicleRatings[ i ] = NULL;
+ }
+
+ m_vehicleRatings[ VEHICLE_RATING_SPEED ] = new PhoneBoothStars( pPage, "Spe" );
+ m_vehicleRatings[ VEHICLE_RATING_ACCELERATION ] = new PhoneBoothStars( pPage, "Acc" );
+ m_vehicleRatings[ VEHICLE_RATING_TOUGHNESS ] = new PhoneBoothStars( pPage, "Tou" );
+ m_vehicleRatings[ VEHICLE_RATING_STABILITY ] = new PhoneBoothStars( pPage, "Sta" );
+
+ // create inventory section for loading rewards
+ //
+ p3d::inventory->AddSection( REWARDS_INVENTORY_SECTION );
+}
+
+IGuiScreenRewards::~IGuiScreenRewards()
+{
+ for( int i = 0; i < NUM_VEHICLE_RATINGS; i++ )
+ {
+ if( m_vehicleRatings[ i ] != NULL )
+ {
+ delete m_vehicleRatings[ i ];
+ m_vehicleRatings[ i ] = NULL;
+ }
+ }
+
+ if( m_pRewardsMenu != NULL )
+ {
+ delete m_pRewardsMenu;
+ m_pRewardsMenu = NULL;
+ }
+
+ // destroy inventory section for loading rewards
+ //
+ p3d::pddi->DrawSync();
+
+ p3d::inventory->DeleteSection( REWARDS_INVENTORY_SECTION );
+}
+
+void
+IGuiScreenRewards::HandleMessage( eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2 )
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ this->OnUpdate( param1 );
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_AUX_X:
+ {
+ // toggle vehicle stats overlay
+ //
+ if( m_statsOverlayButton != NULL && m_statsOverlayButton->IsVisible() )
+ {
+ this->SetVehicleStatsVisible( !m_statsOverlayToggle );
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ rAssert( m_pRewardsMenu != NULL );
+ m_pRewardsMenu->HandleMessage( message, param1, param2 );
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+void
+IGuiScreenRewards::OnProcessRequestsComplete( void* pUserData )
+{
+ m_isLoading = false; // reset loading flag
+
+ const PreviewObject* currentPreviewObject = this->GetCurrentPreviewObject();
+/*
+ if( m_state != GUI_WINDOW_STATE_RUNNING )
+ {
+ // hmmm... the user must have backed out of this screen before
+ // the preview object has been loaded; fine, we'll just unload it then
+ //
+ this->Unload3DModel();
+ }
+ else
+*/
+ {
+ if( reinterpret_cast<PreviewObject*>( pUserData ) != currentPreviewObject )
+ {
+ // selection must have changed while loading, so let's try
+ // to catch up
+ //
+ this->On3DModelSelectionChange( currentPreviewObject );
+ }
+ else
+ {
+ // push and select inventory section for searching
+ //
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( REWARDS_INVENTORY_SECTION );
+ bool currentSectionOnly = p3d::inventory->GetCurrentSectionOnly();
+ p3d::inventory->SetCurrentSectionOnly( true );
+
+ this->On3DModelLoaded( currentPreviewObject );
+
+ // pop inventory section and restore states
+ //
+ p3d::inventory->SetCurrentSectionOnly( currentSectionOnly );
+ p3d::inventory->PopSection();
+/*
+ // turn on/off lights depending on whether or not reward is unlocked
+ //
+ rAssert( m_previewBgd );
+ tView* pView = m_previewBgd->GetView();
+ if( pView != NULL )
+ {
+ for( unsigned int i = 0; i < MAX_VIEW_LIGHTS; i++ )
+ {
+ tLight* pLight = pView->GetLight( i );
+ if( pLight != NULL )
+ {
+ pLight->Enable( currentPreviewObject->isUnlocked );
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+*/
+ }
+ }
+}
+
+void
+IGuiScreenRewards::InitIntro()
+{
+ m_numPreviewVehicles = 0;
+ m_currentPreviewVehicle = 0;
+
+ m_numPreviewClothing = 0;
+ m_currentPreviewClothing = 0;
+
+ // close light by default
+ //
+ this->SetLightOpening( 0.0f );
+ m_currentState = REWARDS_SCREEN_LIGHT_CLOSED;
+
+ // initialize menu w/ level-specific selections
+ //
+ this->InitMenu();
+
+ GetCoinManager()->SetDrawAfterGui(true);
+
+}
+
+void
+IGuiScreenRewards::InitRunning()
+{
+}
+
+void
+IGuiScreenRewards::InitOutro()
+{
+ m_isLoading = false;
+ m_isLoadingReward = false;
+
+ // unload current 3D vehicle
+ //
+ this->Unload3DModel();
+
+ GetCoinManager()->SetDrawAfterGui(false);
+}
+
+int
+IGuiScreenRewards::InsertPreviewObject( PreviewObject* previewObjects,
+ int numPreviewObjects,
+ Reward* pReward,
+ bool isSorted )
+{
+ rAssert( previewObjects != NULL );
+ rAssert( pReward != NULL );
+
+ int newSlot = numPreviewObjects;
+
+ if( isSorted )
+ {
+ // *** assumes array is already sorted ***
+
+ // sort by unlock status first (unlocked rewards before locked rewards);
+ // then sort unlocked rewards alphabetically, and sort locked rewards by level
+ //
+ for( int i = 0; i < numPreviewObjects; i++ )
+ {
+ bool insertHere = false;
+
+ if( static_cast<int>( pReward->RewardStatus() ) > static_cast<int>( previewObjects[ i ].isUnlocked ) )
+ {
+ insertHere = true;
+ }
+ else if( pReward->RewardStatus() && previewObjects[ i ].isUnlocked )
+ {
+ char stringID[ 16 ];
+ char string1[ 64 ];
+ char string2[ 64 ];
+
+ strcpy( stringID, pReward->GetName() );
+ p3d::UnicodeToAscii( GetTextBibleString( strupr( stringID ) ), string1, sizeof( string1 ) );
+
+ strcpy( stringID, previewObjects[ i ].name );
+ p3d::UnicodeToAscii( GetTextBibleString( strupr( stringID ) ), string2, sizeof( string2 ) );
+
+ insertHere = ( strcmp( string1, string2 ) < 0 );
+ }
+ else if( !pReward->RewardStatus() && !previewObjects[ i ].isUnlocked )
+ {
+ insertHere = ( pReward->GetLevel() < previewObjects[ i ].pReward->GetLevel() );
+ }
+
+ if( insertHere )
+ {
+ // OK, found it!
+ //
+ newSlot = i;
+
+ // move all remaining preview objects up a slot to make room
+ // for the new guy
+ //
+ for( int j = numPreviewObjects - 1; j >= i; j-- )
+ {
+ strcpy( previewObjects[ j + 1 ].name, previewObjects[ j ].name );
+ strcpy( previewObjects[ j + 1 ].filename, previewObjects[ j ].filename );
+ strcpy( previewObjects[ j + 1 ].nameModel, previewObjects[ j ].nameModel );
+ strcpy( previewObjects[ j + 1 ].filenameModel, previewObjects[ j ].filenameModel );
+ previewObjects[ j + 1 ].isUnlocked = previewObjects[ j ].isUnlocked;
+ previewObjects[ j + 1 ].pReward = previewObjects[ j ].pReward;
+ }
+
+ // we're done
+ //
+ break;
+ }
+ }
+ }
+
+ // insert new object into the available slot
+ //
+ strncpy( previewObjects[ newSlot ].name, pReward->GetName(),
+ sizeof( previewObjects[ newSlot ].name ) );
+
+ strncpy( previewObjects[ newSlot ].filename, pReward->GetFile(),
+ sizeof( previewObjects[ newSlot ].filename ) );
+
+ if( m_ID == GUI_SCREEN_ID_PHONE_BOOTH )
+ {
+ // use 2D images for phone booth
+ //
+ strcpy( previewObjects[ newSlot ].nameModel, pReward->GetName() );
+
+ sprintf( previewObjects[ newSlot ].filenameModel, "%s%s",
+ PHONE_BOOTH_2DMODEL_CARS_DIR,
+ pReward->GetName() );
+ }
+ else
+ {
+ if( pReward->GetRewardType() == Reward::ALT_PLAYERCAR )
+ {
+ sprintf( previewObjects[ newSlot ].nameModel, "%s",
+ pReward->GetName() );
+
+ // 3D models for cars are not the same as the in-game ones
+ // (they use less memory than the in-game ones)
+ //
+ sprintf( previewObjects[ newSlot ].filenameModel, "%s%s.p3d",
+ PHONE_BOOTH_3DMODEL_CARS_DIR,
+ pReward->GetName() );
+ }
+ else
+ {
+ sprintf( previewObjects[ newSlot ].nameModel, "%s_h",
+ pReward->GetName() );
+
+ strncpy( previewObjects[ newSlot ].filenameModel, pReward->GetFile(),
+ sizeof( previewObjects[ newSlot ].filenameModel ) );
+ }
+ }
+
+ previewObjects[ newSlot ].isUnlocked = pReward->RewardStatus();
+ previewObjects[ newSlot ].pReward = pReward;
+
+ // return the new number of preview objects
+ //
+ return (numPreviewObjects + 1);
+}
+
+void
+IGuiScreenRewards::OnUpdate( unsigned int elapsedTime )
+{
+ switch( m_currentState )
+ {
+ case REWARDS_SCREEN_OPENING_LIGHT:
+ {
+ if( m_elapsedTime < LIGHT_OPEN_CLOSE_TRANSITION_TIME )
+ {
+ float lightOpening = m_elapsedTime / LIGHT_OPEN_CLOSE_TRANSITION_TIME;
+
+ this->SetLightOpening( lightOpening );
+ }
+ else
+ {
+ // set light fully opened
+ //
+ this->SetLightOpening( 1.0f );
+
+ m_currentState = REWARDS_SCREEN_LIGHT_OPENED;
+ }
+
+ break;
+ }
+ case REWARDS_SCREEN_LIGHT_OPENED:
+ {
+ // do nothing
+
+ break;
+ }
+ case REWARDS_SCREEN_CLOSING_LIGHT:
+ {
+ if( m_elapsedTime < LIGHT_OPEN_CLOSE_TRANSITION_TIME )
+ {
+ float lightOpening = 1.0f - m_elapsedTime / LIGHT_OPEN_CLOSE_TRANSITION_TIME;
+
+ this->SetLightOpening( lightOpening );
+ }
+ else
+ {
+ // set light fully closed
+ //
+ this->SetLightOpening( 0.0f );
+
+ m_currentState = REWARDS_SCREEN_LIGHT_CLOSED;
+ }
+
+ break;
+ }
+ case REWARDS_SCREEN_LIGHT_CLOSED:
+ {
+ this->SetLightOpening( 0.0f );
+
+ // check if loading is done yet
+ //
+ if( !m_isLoading )
+ {
+ // ok, re-open light
+ //
+ m_currentState = REWARDS_SCREEN_OPENING_LIGHT;
+ m_elapsedTime = 0.0f;
+
+ // show 3D model
+ //
+ if( m_previewWindow != NULL )
+ {
+ m_previewWindow->SetVisible( true );
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ m_elapsedTime += static_cast<float>( elapsedTime );
+}
+
+void
+IGuiScreenRewards::SetLightOpening( float amount )
+{
+ if( m_previewLightCover != NULL )
+ {
+ const float MAX_LIGHT_AMOUNT_CLOSED = 0.85f;
+ float amountClosed = (1.0f - amount) * MAX_LIGHT_AMOUNT_CLOSED;
+ amount = 1.0f - amountClosed;
+
+ m_previewLightCover->SetAlpha( amountClosed );
+ }
+}
+
+void
+IGuiScreenRewards::On3DModelSelectionChange( const PreviewObject* nextModel )
+{
+ if( nextModel != NULL )
+ {
+ // update preview object display name
+ //
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ char textBibleEntry[ sizeof( nextModel->name ) ];
+ strcpy( textBibleEntry, nextModel->name );
+ UnicodeChar* textBibleString = GetTextBibleString( strupr( textBibleEntry ) );
+ UnicodeString unicodeString;
+ if( textBibleString != NULL )
+ {
+ unicodeString.ReadUnicode( textBibleString );
+ }
+ else
+ {
+ rAssertMsg( false, "Skin/Vehicle name not found in text bible!" );
+ unicodeString.ReadAscii( nextModel->name );
+ }
+
+#ifdef PAL
+ if( m_ID == GUI_SCREEN_ID_PHONE_BOOTH &&
+ CGuiTextBible::GetCurrentLanguage() == Scrooby::XL_GERMAN )
+ {
+ if( strcmp( textBibleEntry, "BOOKB_V" ) == 0 )
+ {
+ UnicodeString str1;
+ str1.ReadAscii( "mobil" );
+
+ UnicodeString str2;
+ str2.ReadAscii( "-\nmobil" );
+
+ unicodeString.Replace( str1, str2 );
+ }
+
+ if( strcmp( textBibleEntry, "SCORP_V") == 0 )
+ {
+ UnicodeString str1;
+ str1.ReadAscii( "Auto" );
+
+ UnicodeString str2;
+ str2.ReadAscii( "\nAuto" );
+
+ unicodeString.Replace( str1, str2 );
+ }
+ }
+#endif // PAL
+
+ rAssert( m_previewName );
+ m_previewName->SetString( 0, unicodeString );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+
+ // update locked/unlocked status
+ //
+ rAssert( m_lockedOverlay );
+ m_lockedOverlay->SetVisible( !nextModel->isUnlocked );
+
+ if( m_ID == GUI_SCREEN_ID_PHONE_BOOTH )
+ {
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, nextModel->isUnlocked );
+ }
+
+ // update locked level display
+ //
+ if( m_lockedLevel != NULL )
+ {
+ m_lockedLevel->SetIndex( nextModel->pReward->GetLevel() );
+ }
+
+ // update vehicle stats
+ //
+ this->UpdateVehicleStats();
+
+ if( !m_isLoading )
+ {
+ // unload current 3D model
+ //
+ this->Unload3DModel();
+
+ // load next 3D model
+ //
+ this->Load3DModel( nextModel );
+ }
+ }
+}
+
+void
+IGuiScreenRewards::Load3DModel( const PreviewObject* previewObject )
+{
+ m_isLoading = true; // set loading flag
+ m_isLoadingReward = false; // not loading reward
+
+ rAssert( previewObject != NULL );
+
+ if( m_ID == GUI_SCREEN_ID_PHONE_BOOTH )
+ {
+ char filename[ 64 ];
+
+ // load normal 2D car image
+ //
+ sprintf( filename, "%s.p3d", previewObject->filenameModel );
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ filename,
+ GMA_LEVEL_OTHER,
+ REWARDS_INVENTORY_SECTION,
+ REWARDS_INVENTORY_SECTION );
+
+#ifdef LOAD_DAMAGED_VEHICLE_IMAGES
+ // load damaged 2D car image
+ //
+ sprintf( filename, "%sD.p3d", previewObject->filenameModel );
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ filename,
+ GMA_LEVEL_OTHER,
+ REWARDS_INVENTORY_SECTION,
+ REWARDS_INVENTORY_SECTION );
+#endif
+ }
+ else
+ {
+ // add request to load 3D model
+ //
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ previewObject->filenameModel,
+ GMA_LEVEL_MISSION,
+ REWARDS_INVENTORY_SECTION,
+ REWARDS_INVENTORY_SECTION );
+ }
+
+ GetLoadingManager()->AddCallback( this, (void*)previewObject );
+}
+
+void
+IGuiScreenRewards::Unload3DModel()
+{
+ p3d::pddi->DrawSync();
+
+ if( m_previewWindow != NULL )
+ {
+ m_previewWindow->SetDrawable( NULL );
+ m_previewWindow->SetVisible( false );
+ }
+
+ if( m_previewImage != NULL )
+ {
+ m_previewImage->SetRawSprite( NULL );
+ m_previewImage->SetVisible( false );
+ }
+
+ // remove 3D model from inventory
+ //
+ p3d::inventory->RemoveSectionElements( REWARDS_INVENTORY_SECTION );
+
+ // change current state to closing light, if not already closed
+ // or in the progress of closing
+ //
+ if( m_currentState != REWARDS_SCREEN_CLOSING_LIGHT &&
+ m_currentState != REWARDS_SCREEN_LIGHT_CLOSED )
+ {
+ if( m_currentState == REWARDS_SCREEN_OPENING_LIGHT &&
+ m_elapsedTime < LIGHT_OPEN_CLOSE_TRANSITION_TIME )
+ {
+ m_elapsedTime = LIGHT_OPEN_CLOSE_TRANSITION_TIME - m_elapsedTime;
+ }
+ else
+ {
+ m_elapsedTime = 0.0f;
+ }
+
+ m_currentState = REWARDS_SCREEN_CLOSING_LIGHT;
+ }
+}
+
+void
+IGuiScreenRewards::SetVehicleStatsVisible( bool isVisible )
+{
+ m_statsOverlayToggle = isVisible;
+
+ rAssert( m_statsOverlay );
+ m_statsOverlay->SetVisible( m_statsOverlayToggle );
+
+ rAssert( m_statsOverlayButtonLabel );
+ m_statsOverlayButtonLabel->SetIndex( m_statsOverlayToggle ? 1 : 0 );
+}
+
+void
+IGuiScreenRewards::UpdateVehicleStats()
+{
+ const PreviewObject* currentPreviewObject = this->GetCurrentPreviewObject();
+ CarAttributeRecord* carAttributes = GetRewardsManager()->GetCarAttributeRecord( const_cast<char*>( currentPreviewObject->name ) );
+ if( carAttributes != NULL )
+ {
+ m_vehicleRatings[ VEHICLE_RATING_SPEED ]->SetNumStars( carAttributes->mSpeed );
+ m_vehicleRatings[ VEHICLE_RATING_ACCELERATION ]->SetNumStars( carAttributes->mAcceleration );
+ m_vehicleRatings[ VEHICLE_RATING_TOUGHNESS ]->SetNumStars( carAttributes->mToughness );
+ m_vehicleRatings[ VEHICLE_RATING_STABILITY ]->SetNumStars( carAttributes->mStability );
+ }
+/*
+ else
+ {
+ rAssert( false );
+ rTunePrintf( "*** WARNING: No car attributes found for [%s]!\n",
+ currentPreviewObject->name );
+ }
+*/
+}
+
diff --git a/game/code/presentation/gui/ingame/guiscreenrewards.h b/game/code/presentation/gui/ingame/guiscreenrewards.h
new file mode 100644
index 0000000..0251113
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenrewards.h
@@ -0,0 +1,171 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: IGuiScreenRewards
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/21 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENREWARDS_H
+#define GUISCREENREWARDS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+#include <loading/loadingmanager.h>
+#include <memory/srrmemory.h>
+
+#ifdef RAD_WIN32
+#define LOAD_DAMAGED_VEHICLE_IMAGES
+#endif
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CGuiMenu;
+class Reward;
+
+struct PreviewObject
+{
+ char name[ 16 ]; // object name
+ char filename[ 64 ]; // object filename
+
+ char nameModel[ 16 ]; // model drawable name
+ char filenameModel[ 64 ]; // model filename
+
+ bool isUnlocked : 1; // unlocked flag
+ Reward* pReward; // reference to reward object
+};
+
+class PhoneBoothStars
+{
+public:
+ PhoneBoothStars( Scrooby::Page* pPage, const char* namePrefix );
+ void SetNumStars( float numStars );
+
+private:
+ static const int MAX_NUM_STARS = 5;
+ Scrooby::Sprite* m_stars[ MAX_NUM_STARS ];
+
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class IGuiScreenRewards : public CGuiScreen,
+ public LoadingManager::ProcessRequestsCallback
+{
+public:
+ IGuiScreenRewards( Scrooby::Screen* pScreen,
+ Scrooby::Page* pPage,
+ CGuiEntity* pParent,
+ eGuiWindowID windowID );
+
+ virtual ~IGuiScreenRewards();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ void OnProcessRequestsComplete( void* pUserData );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ virtual void On3DModelLoaded( const PreviewObject* previewObject ) = 0;
+ virtual const PreviewObject* GetCurrentPreviewObject() const = 0;
+ virtual void InitMenu() = 0;
+
+ int InsertPreviewObject( PreviewObject* previewObjects,
+ int numPreviewObjects,
+ Reward* pReward,
+ bool isSorted = false );
+
+ virtual void OnUpdate( unsigned int elapsedTime );
+
+ void SetLightOpening( float amount ); // 0.0f <= amount <= 1.0f
+
+ void On3DModelSelectionChange( const PreviewObject* nextModel );
+
+ void Load3DModel( const PreviewObject* previewObject );
+ void Unload3DModel();
+
+ void SetVehicleStatsVisible( bool isVisible );
+ void UpdateVehicleStats();
+
+ static const int MAX_NUM_PREVIEW_VEHICLES = 64;
+ static const int MAX_NUM_PREVIEW_CLOTHING = 4;
+
+ CGuiMenu* m_pRewardsMenu;
+
+ enum eRewardsScreenState
+ {
+ REWARDS_SCREEN_OPENING_LIGHT,
+ REWARDS_SCREEN_LIGHT_OPENED,
+ REWARDS_SCREEN_CLOSING_LIGHT,
+ REWARDS_SCREEN_LIGHT_CLOSED,
+
+ NUM_REWARDS_SCREEN_STATES
+ };
+
+ eRewardsScreenState m_currentState;
+ float m_elapsedTime;
+
+ Scrooby::Polygon* m_previewLightCover;
+ Scrooby::Pure3dObject* m_previewWindow;
+ Scrooby::Pure3dObject* m_previewPedestal;
+ Scrooby::Pure3dObject* m_previewBgd;
+ Scrooby::Sprite* m_previewImage;
+ Scrooby::Text* m_previewName;
+
+ // list of garage vehicles
+ //
+ PreviewObject m_previewVehicles[ MAX_NUM_PREVIEW_VEHICLES ];
+ int m_numPreviewVehicles;
+ int m_currentPreviewVehicle;
+
+ // list of character skins
+ //
+ PreviewObject m_previewClothing[ MAX_NUM_PREVIEW_CLOTHING ];
+ int m_numPreviewClothing;
+ int m_currentPreviewClothing;
+
+ bool m_isLoading : 1;
+ bool m_isLoadingReward : 1;
+
+ Scrooby::Group* m_lockedOverlay;
+ Scrooby::Text* m_lockedLevel;
+ Scrooby::Text* m_rewardPrice;
+
+ Scrooby::Group* m_statsOverlay;
+ Scrooby::Group* m_statsOverlayButton;
+ Scrooby::Text* m_statsOverlayButtonLabel;
+ bool m_statsOverlayToggle;
+
+ enum eVehicleRatingType
+ {
+ VEHICLE_RATING_SPEED,
+ VEHICLE_RATING_ACCELERATION,
+ VEHICLE_RATING_TOUGHNESS,
+ VEHICLE_RATING_STABILITY,
+
+ NUM_VEHICLE_RATINGS
+ };
+
+ PhoneBoothStars* m_vehicleRatings[ NUM_VEHICLE_RATINGS ];
+
+};
+
+#endif // GUISCREENREWARDS_H
diff --git a/game/code/presentation/gui/ingame/guiscreensavegame.cpp b/game/code/presentation/gui/ingame/guiscreensavegame.cpp
new file mode 100644
index 0000000..48dd64a
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreensavegame.cpp
@@ -0,0 +1,1011 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenSaveGame
+//
+// Description: Implementation of the CGuiScreenSaveGame class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/04 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreensavegame.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/guiscreenmessage.h>
+#include <presentation/gui/guiscreenprompt.h>
+
+#include <data/gamedatamanager.h>
+#include <data/savegameinfo.h>
+#include <data/memcard/memorycardmanager.h>
+#include <memory/srrmemory.h>
+#include <gameflow/gameflow.h>
+
+#include <raddebug.hpp> // Foundation
+#include <group.h>
+#include <layer.h>
+#include <page.h>
+#include <screen.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+#ifdef RAD_XBOX
+extern char gGameFileName[NUM_GAME_SLOTS][radFileFilenameMax+1];
+#endif
+//===========================================================================
+// CGuiScreenSaveGame::CGuiScreenSaveGame
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenSaveGame::CGuiScreenSaveGame
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+:
+ CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_SAVE_GAME ),
+ CGuiScreenLoadSave( pScreen ),
+ m_pMenu( NULL ),
+ m_pFullText( NULL ),
+ m_StatusPromptShown(false),
+ m_nonEmptySlots( 0 )
+{
+MEMTRACK_PUSH_GROUP( "CGUIScreenSaveGame" );
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "GameSlots" );
+ rAssert( pPage );
+
+ Scrooby::Group* menu = pPage->GetGroup( "Menu" );
+ rAssert( menu != NULL );
+
+ m_pFullText = pPage->GetText( "FullMessage" );
+ rAssert( m_pFullText != NULL );
+ m_pFullText->SetVisible(false);
+ m_pFullText->SetTextMode( Scrooby::TEXT_WRAP );
+ // Create a menu.
+ //
+ m_pMenu = new(GMA_LEVEL_HUD) CGuiMenu( this, NUM_GAME_SLOTS );
+ rAssert( m_pMenu != NULL );
+
+ // Add menu items
+ //
+ for( unsigned int i = 0; i < NUM_GAME_SLOTS; i++ )
+ {
+ char objectName[ 32 ];
+ sprintf( objectName, "Slot%d", i );
+
+ m_pMenu->AddMenuItem( menu->GetText( objectName ) );
+ }
+
+#ifdef RAD_WIN32
+ Scrooby::Text* pText = pPage->GetText( "LoadSaveMessage" );
+ if( pText != NULL )
+ {
+ pText->SetIndex( 1 );
+ }
+#else
+ pPage = m_pScroobyScreen->GetPage( "SelectMemoryDevice" );
+ rAssert( pPage != NULL );
+ Scrooby::Layer* foreground = pPage->GetLayer( "Foreground" );
+ rAssert( foreground != NULL );
+
+ Scrooby::Text* pText = foreground->GetText( "LoadSave" );
+ if( pText != NULL )
+ {
+ pText->SetIndex( 1 );
+ }
+#endif
+
+ this->AutoScaleFrame( m_pScroobyScreen->GetPage( "BigBoard" ) );
+MEMTRACK_POP_GROUP("CGUIScreenSaveGame");
+}
+
+
+//===========================================================================
+// CGuiScreenSaveGame::~CGuiScreenSaveGame
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenSaveGame::~CGuiScreenSaveGame()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenSaveGame::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSaveGame::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if (message == GUI_MSG_MESSAGE_UPDATE)
+ {
+ if (m_formatState)
+ {
+ m_elapsedFormatTime += param1;
+ if (m_elapsedFormatTime > m_minimumFormatTime && m_formatDone)
+ {
+ m_StatusPromptShown = true;
+ m_formatState = false;
+
+ if( m_formatResult == Success )
+ {
+#ifdef RAD_PS2
+ m_currentSlot = 0; // on ps2 continue to save
+ this->SaveGame();
+#else
+ m_guiManager->DisplayPrompt(PROMPT_FORMAT_SUCCESS_GC + PLATFORM_TEXT_INDEX, this, PROMPT_TYPE_CONTINUE); // format success
+#endif
+ }
+ else
+ {
+ m_guiManager->DisplayPrompt(PROMPT_FORMAT_FAIL_GC + PLATFORM_TEXT_INDEX, this, PROMPT_TYPE_CONTINUE); // format fail
+ }
+ }
+ }
+ }
+ else if (message == GUI_MSG_PROMPT_UPDATE)
+ {
+ GetMemoryCardManager()->Update( param1 ); // update so we know the status
+
+ if (m_StatusPromptShown==false) { // check for user unplugging memcard if not showing status
+ int current_drive = GetMemoryCardManager()->GetCurrentDriveIndex();
+
+ if( !GetMemoryCardManager()->IsCurrentDrivePresent(current_drive) )
+ ReloadScreen();
+ }
+ }
+ else if( message == GUI_MSG_ON_DISPLAY_MESSAGE )
+ {
+ if( m_operation == SAVE )
+ {
+ // start the save game process
+ //
+ rAssert( m_currentSlot != -1 );
+#ifdef RAD_XBOX
+ // has existing filename
+ radFileError err = Success;
+ if (gGameFileName[m_currentSlot][0]!=0) // delete existing file if overwriting (on xbox each filename is unique)
+ {
+ err = GetGameDataManager()->DeleteGame(gGameFileName[m_currentSlot]);
+ }
+ if (err!=Success)
+ OnSaveGameComplete(err);
+ else
+ GetGameDataManager()->SaveGame( m_currentSlot, this );
+#else
+ GetGameDataManager()->SaveGame( m_currentSlot, this );
+#endif
+ }
+ else if( m_operation == DELETE_GAME )
+ {
+ // get the filename
+ char filename[ radFileFilenameMax + 1 ];
+#ifdef RAD_XBOX
+ strcpy(filename, gGameFileName[m_currentSlot]);
+#else
+ GetGameDataManager()->FormatSavedGameFilename( filename,
+ sizeof( filename ),
+ m_currentSlot );
+#endif
+ radFileError err = GetGameDataManager()->DeleteGame( filename, true, this );
+ }
+ else
+ {
+ FormatCurrentDrive();
+ }
+ }
+ else if ( message==GUI_MSG_PROMPT_START_RESPONSE )
+ {
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+ }
+ else if (message == GUI_MSG_ERROR_PROMPT_RESPONSE )
+ {
+ this->HandleErrorResponse( static_cast<CGuiMenuPrompt::ePromptResponse>( param2 ) );
+ }
+ else if( message == GUI_MSG_MENU_PROMPT_RESPONSE )
+ {
+ switch( param1 )
+ {
+
+ case PROMPT_FORMAT_CONFIRM_GC:
+ case PROMPT_FORMAT_CONFIRM_PS2:
+ case PROMPT_FORMAT_CONFIRM_XBOX: // do you really want to format
+ {
+ if (param2==CGuiMenuPrompt::RESPONSE_YES)
+ m_guiManager->DisplayPrompt(PROMPT_FORMAT_CONFIRM2_GC + PLATFORM_TEXT_INDEX,this);
+ else
+ {
+ this->GotoMemoryCardScreen( true );
+ }
+ break;
+ }
+ case PROMPT_FORMAT_CONFIRM2_GC:
+ case PROMPT_FORMAT_CONFIRM2_PS2:
+ case PROMPT_FORMAT_CONFIRM2_XBOX: // really format
+ {
+ if (param2==CGuiMenuPrompt::RESPONSE_YES)
+ {
+ m_operation = FORMAT;
+ m_guiManager->DisplayMessage(CGuiScreenMessage::MSG_ID_FORMATTING_GC + PLATFORM_TEXT_INDEX, this);
+ }
+ else
+ {
+ this->GotoMemoryCardScreen( true );
+ }
+ break;
+ }
+ case PROMPT_LOAD_DELETE_CORRUPT_GC:
+ case PROMPT_LOAD_DELETE_CORRUPT_PS2:
+ case PROMPT_LOAD_DELETE_CORRUPT_XBOX:
+ case PROMPT_LOAD_DELETE_CORRUPT_XBOX_HD:
+ {
+ if (param2==CGuiMenuPrompt::RESPONSE_NO)
+ {
+ this->ReloadScreen();
+ }
+ else if (param2==CGuiMenuPrompt::RESPONSE_YES)
+ {
+#ifdef RAD_PS2
+ m_operation = DELETE_GAME;
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_DELETING_GC + PLATFORM_TEXT_INDEX, this );
+#else
+ // get the filename
+ char filename[ radFileFilenameMax + 1 ];
+ #ifdef RAD_XBOX
+ strcpy(filename, gGameFileName[m_currentSlot]);
+ #else
+ GetGameDataManager()->FormatSavedGameFilename( filename,
+ sizeof( filename ),
+ m_currentSlot );
+ #endif
+ radFileError err = GetGameDataManager()->DeleteGame(filename);
+ this->OnDeleteGameComplete( err );
+#endif // RAD_PS2
+ }
+ else
+ {
+ rTuneAssert(!"not reached");
+ }
+
+ break;
+ }
+
+ case PROMPT_FORMAT_SUCCESS_GC:
+ case PROMPT_FORMAT_SUCCESS_PS2:
+ case PROMPT_FORMAT_SUCCESS_XBOX: // format ok
+ {
+ // m_currentSlot = 0; on ps2 we don't come here
+ // this->SaveGame();
+ this->ReloadScreen();
+ break;
+ }
+
+ case PROMPT_FORMAT_FAIL_GC:
+ case PROMPT_FORMAT_FAIL_PS2:
+ case PROMPT_FORMAT_FAIL_XBOX: // format fail
+ {
+ GetMemoryCardManager()->ClearCurrentDrive();
+ this->GotoMemoryCardScreen( true );
+
+ break;
+ }
+
+ case PROMPT_DELETE_CORRUPT_SUCCESS_GC:
+ case PROMPT_DELETE_CORRUPT_SUCCESS_PS2:
+ case PROMPT_DELETE_CORRUPT_SUCCESS_XBOX:
+
+ case PROMPT_DELETE_CORRUPT_FAIL_GC:
+ case PROMPT_DELETE_CORRUPT_FAIL_PS2:
+ case PROMPT_DELETE_CORRUPT_FAIL_XBOX:
+ {
+ this->ReloadScreen();
+ break;
+ }
+
+ case PROMPT_SAVE_CONFIRM_OVERWRITE_GC:
+ {
+ if( param2 == CGuiMenuPrompt::RESPONSE_YES )
+ {
+ m_guiManager->DisplayPrompt( PROMPT_SAVE_CONFIRM_GC, this, PROMPT_TYPE_SAVE );
+ }
+ else
+ {
+ rAssert( param2 == CGuiMenuPrompt::RESPONSE_NO );
+
+ this->ReloadScreen();
+ }
+
+ break;
+ }
+ case PROMPT_SAVE_CONFIRM_GC:
+ case PROMPT_SAVE_CONFIRM_PS2:
+ case PROMPT_SAVE_CONFIRM_XBOX:
+ case PROMPT_SAVE_CONFIRM_OVERWRITE_PS2:
+ case PROMPT_SAVE_CONFIRM_OVERWRITE_XBOX:
+ {
+ if( param2 == CGuiMenuPrompt::RESPONSE_YES ||
+ param2 == CGuiMenuPrompt::RESPONSE_SAVE )
+ {
+ this->SaveGame();
+ }
+ else
+ {
+ rAssert( param2 == CGuiMenuPrompt::RESPONSE_NO );
+
+ this->ReloadScreen();
+ }
+
+ break;
+ }
+
+ case PROMPT_SAVE_SUCCESSFUL:
+ {
+ m_pParent->HandleMessage( GUI_MSG_ON_SAVE_GAME_COMPLETE );
+
+ break;
+ }
+
+ default:
+ {
+ // handle normal error response, "continue", etc
+ this->HandleErrorResponse( static_cast<CGuiMenuPrompt::ePromptResponse>( param2 ) );
+
+ break;
+ }
+ }
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ if( message == GUI_MSG_CONTROLLER_SELECT &&
+ !GetMemoryCardManager()->IsMemcardInfoLoaded() )
+ {
+ // ignore user select inputs until memcard info is loaded
+ //
+ return;
+ }
+
+ switch( message )
+ {
+#ifdef RAD_PS2
+ case GUI_MSG_CONTROLLER_START:
+ {
+ if ( GetGameFlow()->GetCurrentContext() == CONTEXT_PAUSE )
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+
+ break;
+ }
+#endif
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ m_currentSlot = param1; // // param1 = slot
+
+ SaveGameInfo saveGameInfo;
+ bool corrupt = false;
+ IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
+ bool saveGameExists = GetGameDataManager()->GetSaveGameInfo( currentDrive, m_currentSlot, &saveGameInfo, &corrupt );
+
+ if (corrupt)
+ {
+ int plat_index = PLATFORM_TEXT_INDEX;
+#ifdef RAD_XBOX
+ if (GetMemoryCardManager()->GetCurrentDriveIndex()==0)
+ plat_index++;
+#endif
+#ifdef RAD_GAMECUBE
+ int errorMessage = GetErrorMessageIndex( DataCorrupt, ERROR_DURING_SAVING );
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE_WITHOUT_SAVE | ERROR_RESPONSE_RETRY | ERROR_RESPONSE_DELETE );
+ m_operation = SAVE;
+#else
+ m_guiManager->DisplayPrompt( PROMPT_LOAD_DELETE_CORRUPT_GC + plat_index, this );
+#endif
+ }
+ else if( (m_nonEmptySlots & (1 << m_currentSlot)) > 0 )
+ {
+ // saved game exists in current slot; prompt w/ overwrite
+ // confirmation message
+ //
+#ifdef RAD_GAMECUBE
+ m_guiManager->DisplayPrompt( PROMPT_SAVE_CONFIRM_OVERWRITE_GC, this );
+#endif
+
+#ifdef RAD_PS2
+ m_guiManager->DisplayPrompt( PROMPT_SAVE_CONFIRM_OVERWRITE_PS2, this );
+#endif
+
+#ifdef RAD_XBOX
+ m_guiManager->DisplayPrompt( PROMPT_SAVE_CONFIRM_OVERWRITE_XBOX, this );
+#endif
+
+#ifdef RAD_WIN32
+ m_guiManager->DisplayPrompt( PROMPT_SAVE_CONFIRM_OVERWRITE_XBOX, this );
+#endif
+ }
+ else
+ {
+#ifdef RAD_GAMECUBE
+ m_guiManager->DisplayPrompt( PROMPT_SAVE_CONFIRM_GC, this, PROMPT_TYPE_SAVE );
+#endif
+
+#ifdef RAD_PS2
+ m_guiManager->DisplayPrompt( PROMPT_SAVE_CONFIRM_PS2, this );
+#endif
+
+#ifdef RAD_XBOX
+ m_guiManager->DisplayPrompt( PROMPT_SAVE_CONFIRM_XBOX, this );
+#endif
+
+#ifdef RAD_WIN32
+ m_guiManager->DisplayPrompt( PROMPT_SAVE_CONFIRM_XBOX, this );
+#endif
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+#ifdef RAD_XBOX
+ s_forceGotoMemoryCardScreen = true;
+ this->GotoMemoryCardScreen();
+#endif
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ if( m_pMenu != NULL )
+ {
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+
+ CGuiScreenLoadSave::HandleMessage( message, param1, param2 );
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+void
+CGuiScreenSaveGame::OnSaveGameComplete( radFileError errorCode )
+{
+ m_lastError = errorCode;
+ m_StatusPromptShown = true;
+
+ if( errorCode == Success )
+ {
+ m_guiManager->DisplayPrompt( PROMPT_SAVE_SUCCESSFUL, this, PROMPT_TYPE_CONTINUE );
+ }
+ else
+ {
+ int errorMessage = GetErrorMessageIndex( errorCode, ERROR_DURING_SAVING );
+
+#ifdef RAD_GAMECUBE
+ switch( errorCode )
+ {
+ case Success:
+ {
+ rAssert( false );
+ break;
+ }
+ case MediaCorrupt:
+ {
+ m_guiManager->DisplayErrorPrompt( errorMessage, this, ERROR_RESPONSE_CONTINUE );
+
+ break;
+ }
+ case MediaEncodingErr:
+ case MediaNotFormatted:
+ {
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE | ERROR_RESPONSE_RETRY | ERROR_RESPONSE_FORMAT );
+
+ }
+ default:
+ {
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE | ERROR_RESPONSE_RETRY );
+
+ break;
+ }
+ }
+#endif // RAD_GAMECUBE
+
+#ifdef RAD_PS2
+ switch( errorCode )
+ {
+ case Success:
+ {
+ rAssert( false );
+ break;
+ }
+ default:
+ {
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE );
+
+ break;
+ }
+ }
+#endif // RAD_PS2
+
+#ifdef RAD_XBOX
+ switch( errorCode )
+ {
+ case Success:
+ {
+ rAssert( false );
+ break;
+ }
+ default:
+ {
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE );
+
+ break;
+ }
+ }
+#endif // RAD_XBOX
+
+#ifdef RAD_WIN32
+ switch( errorCode )
+ {
+ case Success:
+ {
+ rAssert( false );
+ break;
+ }
+ default:
+ {
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE );
+ break;
+ }
+ }
+#endif // RAD_WIN32
+ }
+}
+
+void
+CGuiScreenSaveGame::OnDeleteGameComplete( radFileError errorCode )
+{
+ m_operation = SCREEN_OP_IDLE;
+ m_StatusPromptShown = true;
+
+ if( errorCode == Success )
+ {
+ m_guiManager->DisplayPrompt( PROMPT_DELETE_CORRUPT_SUCCESS_GC + PLATFORM_TEXT_INDEX,
+ this,
+ PROMPT_TYPE_CONTINUE );
+ }
+ else
+ {
+ m_guiManager->DisplayPrompt( PROMPT_DELETE_CORRUPT_FAIL_GC + PLATFORM_TEXT_INDEX,
+ this,
+ PROMPT_TYPE_CONTINUE );
+ }
+}
+
+void
+CGuiScreenSaveGame::HandleErrorResponse( CGuiMenuPrompt::ePromptResponse response )
+{
+ switch( response )
+ {
+ case (CGuiMenuPrompt::RESPONSE_CONTINUE):
+ case (CGuiMenuPrompt::RESPONSE_CONTINUE_WITHOUT_SAVE):
+ {
+ if( m_operation == SAVE )
+ {
+ this->ReloadScreen();
+ }
+ else if( m_operation == FORMAT )
+ {
+ this->ReloadScreen();
+ }
+ else
+ {
+ this->GotoMemoryCardScreen( true );
+ }
+
+ break;
+ }
+ case (CGuiMenuPrompt::RESPONSE_RETRY):
+ {
+ if( m_operation == SAVE )
+ {
+#ifdef RAD_GAMECUBE
+ SaveGameInfo saveGameInfo;
+ bool corrupt = false;
+ IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
+ GetGameDataManager()->GetSaveGameInfo( currentDrive, m_currentSlot, &saveGameInfo, &corrupt );
+ if( corrupt )
+ {
+ int errorMessage = GetErrorMessageIndex( DataCorrupt, ERROR_DURING_SAVING );
+ m_guiManager->DisplayErrorPrompt( errorMessage, this,
+ ERROR_RESPONSE_CONTINUE_WITHOUT_SAVE | ERROR_RESPONSE_RETRY | ERROR_RESPONSE_DELETE );
+ }
+ else
+#endif
+ {
+ this->SaveGame();
+ }
+ }
+ else if( m_operation == FORMAT )
+ {
+ m_guiManager->DisplayMessage(CGuiScreenMessage::MSG_ID_FORMATTING_GC + PLATFORM_TEXT_INDEX, this);
+ }
+ else
+ {
+ this->ReloadScreen();
+ }
+
+ break;
+ }
+
+#ifdef RAD_GAMECUBE
+ case (CGuiMenuPrompt::RESPONSE_DELETE):
+ {
+ m_guiManager->DisplayPrompt( PROMPT_LOAD_DELETE_CORRUPT_GC, this );
+
+ break;
+ }
+#endif // RAD_GAMECUBE
+
+ case (CGuiMenuPrompt::RESPONSE_FORMAT_GC):
+ case (CGuiMenuPrompt::RESPONSE_FORMAT_XBOX):
+ case (CGuiMenuPrompt::RESPONSE_FORMAT_PS2):
+ {
+ m_guiManager->DisplayPrompt(PROMPT_FORMAT_CONFIRM2_GC + PLATFORM_TEXT_INDEX,this);
+ break;
+ }
+ default:
+ {
+ rTunePrintf( "*** WARNING: Unhandled response for error [%d]!\n", m_lastError );
+ rAssert( false );
+
+ this->ReloadScreen();
+
+ break;
+ }
+ }
+
+ CGuiScreenLoadSave::HandleErrorResponse( response );
+}
+
+
+//===========================================================================
+// CGuiScreenSaveGame::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSaveGame::InitIntro()
+{
+ unsigned int num_empty_slots = 0;
+ bool unformatted = false;
+ IRadDrive::MediaInfo::MediaState mediaState;
+
+ m_StatusPromptShown = false;
+ m_operation = SCREEN_OP_IDLE;
+
+ if( s_forceGotoMemoryCardScreen || !GetMemoryCardManager()->IsCurrentDriveReady( true, &unformatted, &mediaState ))
+ {
+ if (unformatted && !s_forceGotoMemoryCardScreen)
+ {
+#ifdef RAD_GAMECUBE
+ int errorMessage;
+
+ errorMessage = GetErrorMessageIndex( mediaState );
+ m_guiManager->DisplayErrorPrompt( errorMessage,
+ this,
+ ERROR_RESPONSE_CONTINUE_WITHOUT_SAVE | ERROR_RESPONSE_RETRY | ERROR_RESPONSE_FORMAT );
+#else
+ m_guiManager->DisplayPrompt(PROMPT_FORMAT_CONFIRM_GC+PLATFORM_TEXT_INDEX,this);
+#endif
+ m_numTransitionsPending = -1; // disable all transitions
+ }
+ else
+ {
+ this->GotoMemoryCardScreen();
+ m_numTransitionsPending = -1; // disable all transitions
+ }
+ return;
+ }
+
+ m_nonEmptySlots = 0; // reset non-empty slots bitmask
+
+ this->UpdateCurrentMemoryDevice();
+
+ rAssert( m_pMenu );
+ m_pMenu->Reset();
+
+ IRadDrive* currentDrive = GetMemoryCardManager()->GetCurrentDrive();
+
+ int currentDriveIndex = GetMemoryCardManager()->GetCurrentDriveIndex();
+ bool enoughFreeSpace = GetMemoryCardManager()->EnoughFreeSpace( currentDriveIndex );
+
+ radDate mostRecentTimestamp;
+ mostRecentTimestamp.m_Year = 0;
+
+ // update all save game slots display info
+ //
+ for( unsigned int i = 0; i < NUM_GAME_SLOTS; i++ )
+ {
+ SaveGameInfo saveGameInfo;
+ bool corrupt;
+ bool saveGameExists = GetGameDataManager()->GetSaveGameInfo( currentDrive, i, &saveGameInfo, &corrupt );
+// saveGameExists = saveGameExists && saveGameInfo.CheckData();
+
+ Scrooby::Text* slotText = dynamic_cast<Scrooby::Text*>( m_pMenu->GetMenuItem( i )->GetItem() );
+ rAssert( slotText != NULL );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+#ifdef RAD_XBOX
+ gGameFileName[i][0] = 0;
+#endif
+ if( saveGameExists )
+ {
+ if (corrupt)
+ {
+ UnicodeString corruptSlot;
+#ifdef RAD_XBOX
+ corruptSlot.ReadUnicode( GetTextBibleString( "CORRUPT_SLOT_(XBOX)" ) );
+#endif
+#ifdef RAD_WIN32
+ corruptSlot.ReadUnicode( GetTextBibleString( "CORRUPT_SLOT_(XBOX)" ) );
+#endif
+#ifdef RAD_PS2
+ corruptSlot.ReadUnicode( GetTextBibleString( "CORRUPT_SLOT_(PS2)" ) );
+#endif
+#ifdef RAD_GAMECUBE
+ corruptSlot.ReadUnicode( GetTextBibleString( "CORRUPT_SLOT_(GC)" ) );
+#endif
+ slotText->SetString(0,corruptSlot);
+#ifdef RAD_XBOX
+ strcpy(gGameFileName[i], saveGameInfo.m_displayFilename); // cache filename in the slot
+#endif
+ }
+ else
+ {
+ slotText->SetString( 0, saveGameInfo.m_displayFilename );
+ #ifdef RAD_XBOX
+ strcpy(gGameFileName[i], saveGameInfo.m_displayFilename); // cache filename in the slot
+ #endif
+ }
+
+ // default to slot with most recent saved game file
+ //
+ const SaveGameInfoData* pData = saveGameInfo.GetData();
+ rAssert( pData != NULL );
+ if( SaveGameInfo::CompareTimeStamps( pData->m_timeStamp, mostRecentTimestamp ) > 0 )
+ {
+ memcpy( &mostRecentTimestamp, &pData->m_timeStamp, sizeof( radDate ) );
+
+ m_pMenu->Reset( i );
+ }
+
+ // update non-empty slots bitmask
+ //
+ m_nonEmptySlots |= (1 << i);
+ }
+ else
+ {
+ UnicodeString emptySlot;
+ if (enoughFreeSpace)
+ emptySlot.ReadUnicode( GetTextBibleString( "EMPTY_SLOT" ) );
+ else
+ emptySlot.ReadUnicode( GetTextBibleString( "FULL_SLOT" ) );
+
+#ifdef RAD_XBOX
+ if (num_empty_slots) // blank out extra empty slot item
+ emptySlot.ReadUnicode( GetTextBibleString ("SPACE") );
+#endif
+ slotText->SetString( 0, emptySlot );
+ num_empty_slots++;
+ }
+ HeapMgr()->PopHeap(GMA_LEVEL_HUD);
+
+ // enable slot selection only if save game exists or there's enough
+ // free space to save a new game
+ //
+ m_pMenu->SetMenuItemEnabled( i, saveGameExists || enoughFreeSpace );
+#ifdef RAD_XBOX
+ if (num_empty_slots > 1) // disable extra empty slot for xbox
+ m_pMenu->SetMenuItemEnabled( i, false );
+#endif
+ }
+
+ /* display/hide full message */
+ if (!enoughFreeSpace)
+ {
+ int message_index = 0; // no existing slot
+ if (num_empty_slots < NUM_GAME_SLOTS )
+ message_index = 1; // has existing slot
+ // we have 2 group of per platform messages, gc,ps2,xbox_mu, xbox_hd
+ message_index = message_index * 4 + PLATFORM_TEXT_INDEX;
+#ifdef RAD_XBOX
+ if (currentDriveIndex==0)
+ {
+ message_index++; // xbox hard disk
+ }
+#endif
+#ifdef RAD_WIN32
+ message_index = 9;
+#endif
+ m_pFullText->SetIndex(message_index);
+ m_pFullText->SetVisible(true);
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ // append "Use Memory Card Screen" text to message; this is done in
+ // code because the text bible compiler can't handle strings with
+ // more than 255 characters
+ //
+ UnicodeString useMemCardScreen;
+ useMemCardScreen.ReadUnicode( GetTextBibleString( "USE_MEMORY_CARD_SCREEN" ) );
+
+ UnicodeString newString;
+ newString.ReadUnicode( GetTextBibleString( "MEMCARD_FULL_HAS_EXISTING_(GC)" ) );
+ newString.Append( ' ' );
+ newString += useMemCardScreen;
+
+ m_pFullText->SetString( message_index, newString );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+#endif // RAD_GAMECUBE
+ }
+ else
+ {
+ m_pFullText->SetVisible(false);
+ }
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, m_pMenu->GetSelection() != -1 );
+}
+
+
+//===========================================================================
+// CGuiScreenSaveGame::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSaveGame::InitRunning()
+{
+// rAssertMsg( GetMemoryCardManager()->IsMemcardInfoLoaded(),
+// "WARNING: *** Memory card info not loaded yet!" );
+}
+
+
+//===========================================================================
+// CGuiScreenSaveGame::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenSaveGame::InitOutro()
+{
+}
+
+
+void
+CGuiScreenSaveGame::GotoMemoryCardScreen( bool isFromPrompt )
+{
+#ifdef RAD_WIN32
+ m_pParent->HandleMessage( GUI_MSG_BACK_SCREEN );
+#else
+ if( isFromPrompt )
+ {
+ s_forceGotoMemoryCardScreen = true;
+ this->ReloadScreen();
+ }
+ else
+ {
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_MEMORY_CARD );
+ }
+#endif // RAD_WIN32
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenSaveGame::SaveGame()
+{
+ m_operation = SAVE;
+
+#ifdef RAD_GAMECUBE
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_SAVING_GAME_GC, this );
+#endif
+
+#ifdef RAD_PS2
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_SAVING_GAME_PS2, this );
+#endif
+
+#ifdef RAD_XBOX
+ if( m_currentDriveIndex == 0 ) // xbox hard disk
+ {
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_SAVING_GAME_XBOX_HD, this );
+ }
+ else // xbox memory unit
+ {
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_SAVING_GAME_XBOX, this );
+ }
+#endif
+
+#ifdef RAD_WIN32
+ m_guiManager->DisplayMessage( CGuiScreenMessage::MSG_ID_SAVING_GAME_PC, this );
+#endif
+}
+
diff --git a/game/code/presentation/gui/ingame/guiscreensavegame.h b/game/code/presentation/gui/ingame/guiscreensavegame.h
new file mode 100644
index 0000000..361b21f
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreensavegame.h
@@ -0,0 +1,72 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenSaveGame
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/30 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENSAVEGAME_H
+#define GUISCREENSAVEGAME_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/guiscreenmemorycard.h>
+#include <data/gamedatamanager.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenSaveGame : public CGuiScreen,
+ public CGuiScreenLoadSave,
+ public GameDataSaveCallback,
+ public GameDataDeleteCallback
+{
+public:
+ CGuiScreenSaveGame( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenSaveGame();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ // Implements GameDataSaveCallback and GameDataDeleteCallback
+ //
+ virtual void OnSaveGameComplete( radFileError errorCode );
+ virtual void OnDeleteGameComplete( radFileError errorCode );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ void GotoMemoryCardScreen( bool isFromPrompt = false );
+ void HandleErrorResponse( CGuiMenuPrompt::ePromptResponse response );
+
+private:
+ void SaveGame();
+
+ CGuiMenu* m_pMenu;
+ Scrooby::Text* m_pFullText;
+ bool m_StatusPromptShown;
+ unsigned short m_nonEmptySlots;
+
+};
+
+#endif // GUISCREENSAVEGAME_H
diff --git a/game/code/presentation/gui/ingame/guiscreentutorial.cpp b/game/code/presentation/gui/ingame/guiscreentutorial.cpp
new file mode 100644
index 0000000..7ff9536
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreentutorial.cpp
@@ -0,0 +1,373 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenTutorial
+//
+// Description: Implementation of the CGuiScreenTutorial class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/04/15 TChu Created
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreentutorial.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/utility/specialfx.h>
+
+#include <presentation/tutorialmanager.h>
+
+#include <events/eventmanager.h>
+#include <main/commandlineoptions.h>
+#include <sound/soundmanager.h>
+
+// Scrooby
+#include <screen.h>
+#include <page.h>
+#include <layer.h>
+#include <group.h>
+#include <sprite.h>
+
+// ATG
+#include <raddebug.hpp>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+#define WAIT_UNTIL_DIALOG_DONE
+
+const float BART_ICON_CORRECTION_SCALE = 1.0f;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenTutorial::CGuiScreenTutorial
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenTutorial::CGuiScreenTutorial
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_TUTORIAL, SCREEN_FX_ZOOM ),
+ m_tutorialMessage( NULL ),
+ m_bartsHead( NULL ),
+ m_disableTutorial( NULL ),
+ m_elapsedDialogTime( 0 )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "Tutorial" );
+ rAssert( pPage != NULL );
+
+ Scrooby::Group* iconAndMessage = pPage->GetGroup( "IconAndMessage" );
+ rAssert( iconAndMessage != NULL );
+
+ m_bartsHead = iconAndMessage->GetSprite( "BartIcon" );
+
+ m_tutorialMessage = iconAndMessage->GetSprite( "TutorialMessage" );
+ rAssert( m_tutorialMessage != NULL );
+ m_tutorialMessage->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ m_tutorialMessage->CreateBitmapTextBuffer( MAX_TUTORIAL_MESSAGE_LENGTH );
+#ifdef RAD_WIN32
+ m_tutorialMessage->ScaleAboutCenter( 0.25f );
+#else
+ m_tutorialMessage->ScaleAboutCenter( 0.5f );
+#endif
+
+#ifdef PAL
+ iconAndMessage->ResetTransformation();
+ iconAndMessage->Translate( 0, 15 );
+#endif // PAL
+
+ m_disableTutorial = pPage->GetGroup( "DisableTutorial" );
+ rAssert( m_disableTutorial != NULL );
+
+ Scrooby::Text* disableTutorial = m_disableTutorial->GetText( "DisableTutorial" );
+ if( disableTutorial != NULL )
+ {
+ disableTutorial->SetDisplayOutline( true );
+ disableTutorial->SetTextMode( Scrooby::TEXT_WRAP );
+ }
+
+ Scrooby::Page* pPageSmallBoard = m_pScroobyScreen->GetPage( "SmallBoard" );
+ if( pPageSmallBoard != NULL )
+ {
+ this->AutoScaleFrame( pPageSmallBoard );
+
+#ifdef PAL
+ Scrooby::Group* frame = pPageSmallBoard->GetGroup( "Frame" );
+ if( frame != NULL )
+ {
+ frame->ResetTransformation();
+ frame->ScaleAboutCenter( 1.0f, 1.2f, 1.0f );
+ }
+#endif // PAL
+ }
+
+/*
+ // get continue icon button
+ //
+ pPage = m_pScroobyScreen->GetPage( "Continue" );
+ if( pPage != NULL )
+ {
+ m_buttonIcons[ BUTTON_ICON_ACCEPT ] = pPage->GetGroup( "Continue" );
+ rAssert( m_buttonIcons[ BUTTON_ICON_ACCEPT ] != NULL );
+ }
+*/
+}
+
+//===========================================================================
+// CGuiScreenTutorial::~CGuiScreenTutorial
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenTutorial::~CGuiScreenTutorial()
+{
+}
+
+//===========================================================================
+// CGuiScreenTutorial::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenTutorial::HandleMessage( eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2 )
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ // pulse bart's head
+ //
+ m_elapsedDialogTime += param1;
+
+ float currentScale = GuiSFX::Pulse( (float)m_elapsedDialogTime,
+ 600.0f,
+ BART_ICON_CORRECTION_SCALE,
+ 0.1f );
+
+ rAssert( m_bartsHead != NULL );
+ m_bartsHead->ResetTransformation();
+ m_bartsHead->ScaleAboutCenter( currentScale );
+
+#ifdef WAIT_UNTIL_DIALOG_DONE
+/*
+ #ifndef FINAL
+ if( m_elapsedDialogTime > MAX_TUTORIAL_DIALOG_TIME )
+ {
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, true );
+
+ rAssert( m_disableTutorial != NULL );
+ m_disableTutorial->SetVisible( true );
+ }
+ else
+ #endif // !FINAL
+*/
+ {
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, !GetTutorialManager()->IsDialogPlaying() );
+
+// rAssert( m_disableTutorial != NULL );
+// m_disableTutorial->SetVisible( !GetTutorialManager()->IsDialogPlaying() );
+ }
+#endif // WAIT_UNTIL_DIALOG_DONE
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_AUX_X:
+ {
+ rAssert( m_disableTutorial != NULL );
+ if( m_disableTutorial->IsVisible() )
+ {
+ // disable tutorial events
+ //
+ GetTutorialManager()->EnableTutorialEvents( false );
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_BACK ); // sound effect
+
+ if( GetTutorialManager()->IsDialogPlaying() &&
+ !CommandLineOptions::Get( CLO_MUTE ) )
+ {
+ // if dialog is still playing, stop it!
+ // (unless all sounds are muted)
+ //
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_SKIP );
+ }
+
+ m_pParent->HandleMessage( GUI_MSG_RESUME_INGAME );
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+#ifdef WAIT_UNTIL_DIALOG_DONE
+ if( !this->IsButtonVisible( BUTTON_ICON_ACCEPT ) )
+ {
+ // ignore input
+ //
+ return;
+ }
+#endif
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_SELECT ); // sound effect
+
+ m_pParent->HandleMessage( GUI_MSG_RESUME_INGAME );
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_START:
+ {
+ // only pause if dialog is still playing
+ //
+ if( GetTutorialManager()->IsDialogPlaying() )
+ {
+ m_pParent->HandleMessage( GUI_MSG_PAUSE_INGAME );
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+//===========================================================================
+// CGuiScreenTutorial::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenTutorial::InitIntro()
+{
+ int index = GetTutorialManager()->GetCurrentEventID();
+
+ char textBibleID[ 32 ];
+ sprintf( textBibleID, "TUTORIAL_%03d", index );
+ P3D_UNICODE* text = GetTextBibleString( textBibleID );
+
+ // check for platform-specific text; if found, override default text
+ //
+#ifdef RAD_GAMECUBE
+ strcat( textBibleID, "_(GC)" );
+#endif
+#ifdef RAD_PS2
+ strcat( textBibleID, "_(PS2)" );
+#endif
+#ifdef RAD_XBOX
+ strcat( textBibleID, "_(XBOX)" );
+#endif
+ P3D_UNICODE* platformText = GetTextBibleString( textBibleID );
+ if( platformText != NULL )
+ {
+ text = platformText;
+
+ rTunePrintf( "Replacing default tutorial text with entry: %s\n", textBibleID );
+ }
+
+ rAssert( m_tutorialMessage != NULL );
+ m_tutorialMessage->SetBitmapText( text );
+
+#ifdef WAIT_UNTIL_DIALOG_DONE
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, false ); // hide by default
+
+// rAssert( m_disableTutorial != NULL );
+// m_disableTutorial->SetVisible( false ); // hide by default
+
+ rAssert( m_bartsHead != NULL );
+ m_bartsHead->ResetTransformation();
+ m_bartsHead->ScaleAboutCenter( BART_ICON_CORRECTION_SCALE );
+
+ m_elapsedDialogTime = 0;
+#endif
+
+ //
+ // Inform the sound manager that it's time to turn the sound down a bit
+ //
+ GetSoundManager()->OnMissionBriefingStart();
+}
+
+//===========================================================================
+// CGuiScreenTutorial::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenTutorial::InitRunning()
+{
+}
+
+//===========================================================================
+// CGuiScreenTutorial::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenTutorial::InitOutro()
+{
+ //
+ // Turn the sound back up
+ //
+ GetSoundManager()->OnMissionBriefingEnd();
+}
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/ingame/guiscreentutorial.h b/game/code/presentation/gui/ingame/guiscreentutorial.h
new file mode 100644
index 0000000..ad13bc4
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreentutorial.h
@@ -0,0 +1,58 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenTutorial
+//
+// Description:
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/04/15 TChu Created
+//
+//===========================================================================
+
+#ifndef GUISCREENTUTORIAL_H
+#define GUISCREENTUTORIAL_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenTutorial : public CGuiScreen
+{
+public:
+ CGuiScreenTutorial( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenTutorial();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ void SetTutorialMessage( int index );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+ static const int MAX_TUTORIAL_MESSAGE_LENGTH = 256;
+ Scrooby::Sprite* m_tutorialMessage;
+ Scrooby::Sprite* m_bartsHead;
+ Scrooby::Group* m_disableTutorial;
+
+ static const unsigned int MAX_TUTORIAL_DIALOG_TIME = 10000; // in msec
+ unsigned int m_elapsedDialogTime;
+
+};
+
+
+#endif // GUISCREENTUTORIAL_H
diff --git a/game/code/presentation/gui/ingame/guiscreenviewcards.cpp b/game/code/presentation/gui/ingame/guiscreenviewcards.cpp
new file mode 100644
index 0000000..be6bef2
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenviewcards.cpp
@@ -0,0 +1,213 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenViewCards
+//
+// Description: Implementation of the CGuiScreenViewCards class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/guiscreenviewcards.h>
+
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+
+// Scrooby
+//
+#include <screen.h>
+#include <page.h>
+#include <layer.h>
+
+// ATG
+//
+#include <raddebug.hpp>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenViewCards::CGuiScreenViewCards
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenViewCards::CGuiScreenViewCards
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreenCardGallery( pScreen, pParent, GUI_SCREEN_ID_VIEW_CARDS ),
+ m_pauseFgdLayer( NULL ),
+ m_bigBoardFgdLayer( NULL )
+{
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "PauseFgd" );
+ if( pPage != NULL )
+ {
+ m_pauseFgdLayer = pPage->GetLayerByIndex( 0 );
+ }
+
+ pPage = m_pScroobyScreen->GetPage( "BigBoard" );
+ if( pPage != NULL )
+ {
+ m_bigBoardFgdLayer = pPage->GetLayerByIndex( 0 );
+ }
+
+#ifndef RAD_WIN32
+ m_cardScaleLarge = (1.0f / m_cardScaleSmall) * 0.8f;
+ m_cardScaleSmall = 1.0f;
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenViewCards::~CGuiScreenViewCards
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenViewCards::~CGuiScreenViewCards()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenViewCards::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenViewCards::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ if( m_pauseFgdLayer != NULL )
+ {
+ m_pauseFgdLayer->SetVisible( m_cardGalleryState == STATE_BROWSING_CARDS );
+ }
+
+ if( m_bigBoardFgdLayer != NULL )
+ {
+ m_bigBoardFgdLayer->SetVisible( m_cardGalleryState == STATE_BROWSING_CARDS );
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_START:
+ {
+ if( !m_pMenu->HasSelectionBeenMade() && m_cardGalleryState == STATE_BROWSING_CARDS )
+ {
+ // resume game
+ m_pParent->HandleMessage( GUI_MSG_UNPAUSE_INGAME );
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreenCardGallery::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenViewCards::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenViewCards::InitIntro()
+{
+ this->UpdateCards( GetGameplayManager()->GetCurrentLevelIndex() );
+}
+
+
+//===========================================================================
+// CGuiScreenViewCards::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenViewCards::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenViewCards::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenViewCards::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/ingame/guiscreenviewcards.h b/game/code/presentation/gui/ingame/guiscreenviewcards.h
new file mode 100644
index 0000000..b70e8ca
--- /dev/null
+++ b/game/code/presentation/gui/ingame/guiscreenviewcards.h
@@ -0,0 +1,52 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenViewCards
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENVIEWCARDS_H
+#define GUISCREENVIEWCARDS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/frontend/guiscreencardgallery.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenViewCards : public CGuiScreenCardGallery
+{
+public:
+ CGuiScreenViewCards( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenViewCards();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ Scrooby::Layer* m_pauseFgdLayer;
+ Scrooby::Layer* m_bigBoardFgdLayer;
+
+};
+
+#endif // GUISCREENVIEWCARDS_H
diff --git a/game/code/presentation/gui/ingame/hudevents/allhudevents.cpp b/game/code/presentation/gui/ingame/hudevents/allhudevents.cpp
new file mode 100644
index 0000000..7a3724f
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/allhudevents.cpp
@@ -0,0 +1,9 @@
+#include <presentation/gui/ingame/hudevents/hudeventhandler.cpp>
+#include <presentation/gui/ingame/hudevents/hudcardcollected.cpp>
+#include <presentation/gui/ingame/hudevents/hudcoincollected.cpp>
+#include <presentation/gui/ingame/hudevents/hudmissionprogress.cpp>
+#include <presentation/gui/ingame/hudevents/hudmissionobjective.cpp>
+#include <presentation/gui/ingame/hudevents/hudcountdown.cpp>
+#include <presentation/gui/ingame/hudevents/hudhitnrun.cpp>
+#include <presentation/gui/ingame/hudevents/hudwaspdestroyed.cpp>
+#include <presentation/gui/ingame/hudevents/huditemdropped.cpp>
diff --git a/game/code/presentation/gui/ingame/hudevents/hudcardcollected.cpp b/game/code/presentation/gui/ingame/hudevents/hudcardcollected.cpp
new file mode 100644
index 0000000..00d801d
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudcardcollected.cpp
@@ -0,0 +1,429 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudCardCollected
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudcardcollected.h>
+#include <presentation/gui/utility/specialfx.h>
+
+#include <cards/cardgallery.h>
+#include <mission/gameplaymanager.h>
+
+// Scrooby
+#include <app.h>
+#include <page.h>
+#include <group.h>
+#include <sprite.h>
+#include <text.h>
+
+#ifdef RAD_WIN32
+const float HUDCARD_THUMBNAIL_SCALE = 0.44f;
+#endif
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+HudCardCollected::HudCardCollected( Scrooby::Page* pPage )
+: HudEventHandler( pPage->GetGroup( "ItemCollected" ) ),
+ m_currentSubState( 0 ),
+ m_cardImage( NULL ),
+ m_cardText( NULL ),
+ m_cardGet( NULL ),
+ m_cardTitle( NULL ),
+ m_itemsCount( NULL ),
+ m_numCards( NULL ),
+ m_itemsComplete( NULL ),
+ m_cardDeckComplete( NULL ),
+ m_isCardDeckComplete( false ),
+ m_itemsUnlocked( NULL )
+{
+ rAssert( pPage != NULL );
+ Scrooby::Group* itemCollected = pPage->GetGroup( "ItemCollected" );
+ rAssert( itemCollected != NULL );
+ m_cardImage = itemCollected->GetSprite( "CardCollected" );
+ rAssert( m_cardImage != NULL );
+
+ m_cardText = itemCollected->GetGroup( "CardText" );
+ rAssert( m_cardText != NULL );
+
+ m_cardGet = m_cardText->GetText( "CardGet" );
+ rAssert( m_cardGet != NULL );
+
+ m_cardTitle = m_cardText->GetText( "CardTitle" );
+ rAssert( m_cardTitle != NULL );
+ m_cardTitle->SetTextMode( Scrooby::TEXT_WRAP );
+
+ m_itemsCount = itemCollected->GetGroup( "ItemsCount" );
+ rAssert( m_itemsCount != NULL );
+
+ m_numCards = m_itemsCount->GetSprite( "ItemsCount" );
+ rAssert( m_numCards != NULL );
+ m_numCards->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ m_numCards->CreateBitmapTextBuffer( BITMAP_TEXT_BUFFER_SIZE );
+ m_numCards->SetBitmapTextSpacing( -8 );
+
+ m_itemsComplete = itemCollected->GetGroup( "ItemsComplete" );
+ rAssert( m_itemsComplete != NULL );
+
+ m_cardDeckComplete = m_itemsComplete->GetText( "ItemsComplete" );
+ rAssert( m_cardDeckComplete != NULL );
+ m_cardDeckComplete->SetTextMode( Scrooby::TEXT_WRAP );
+
+ m_itemsUnlocked = m_itemsComplete->GetText( "ItemsUnlocked" );
+ rAssert(m_itemsUnlocked != NULL );
+ m_itemsUnlocked->SetTextMode( Scrooby::TEXT_WRAP );
+}
+
+HudCardCollected::~HudCardCollected()
+{
+}
+
+void
+HudCardCollected::Start()
+{
+ this->OnStart();
+
+ m_currentSubState = STATE_CARD_TRANSITION_IN;
+
+ // restore drawable properties
+ //
+ rAssert( m_cardImage != NULL );
+ m_cardImage->ResetTransformation();
+ m_cardImage->SetAlpha( 1.0f );
+
+ rAssert( m_cardText != NULL );
+ m_cardText->ResetTransformation();
+ m_cardText->SetAlpha( 1.0f );
+
+ rAssert( m_itemsCount != NULL );
+ m_itemsCount->ResetTransformation();
+ m_itemsCount->SetAlpha( 1.0f );
+
+ rAssert( m_itemsComplete != NULL );
+ m_itemsComplete->ResetTransformation();
+ m_itemsComplete->SetAlpha( 1.0f );
+ m_itemsComplete->SetVisible( false );
+
+ // show all the cards sub-groups
+ //
+ m_cardImage->SetVisible( true );
+ m_cardText->SetVisible( true );
+
+ // update card count
+ //
+ int currentLevel = GetGameplayManager()->GetCurrentLevelIndex();
+ const CardList* cardList = GetCardGallery()->GetCollectedCards( currentLevel );
+ rAssert( cardList != NULL );
+ this->SetCardCount( cardList->m_numCards, NUM_CARDS_PER_LEVEL );
+}
+
+void
+HudCardCollected::Stop()
+{
+ this->OnStop();
+}
+
+void
+HudCardCollected::Update( float elapsedTime )
+{
+ if( m_currentState == STATE_RUNNING )
+ {
+ m_elapsedTime += elapsedTime;
+
+ switch( m_currentSubState )
+ {
+ case STATE_CARD_TRANSITION_IN:
+ {
+ const float CARD_TRANSITION_IN_TIME = 500.0f;
+
+ const float centerX = Scrooby::App::GetInstance()->GetScreenWidth() / 2.0f;
+ const float centerY = Scrooby::App::GetInstance()->GetScreenHeight() / 2.0f;
+
+ rAssert( m_cardImage != NULL );
+ m_cardImage->ResetTransformation();
+
+ int cardCenterX = 0;
+ int cardCenterY = 0;
+ m_cardImage->GetBoundingBoxCenter( cardCenterX, cardCenterY );
+
+ rAssert( m_cardText != NULL );
+ m_cardText->ResetTransformation();
+
+ rAssert( m_itemsCount != NULL );
+
+ if( m_elapsedTime < CARD_TRANSITION_IN_TIME )
+ {
+ float percentageDone = m_elapsedTime / CARD_TRANSITION_IN_TIME;
+
+ // fade in card image (and scale up)
+ //
+ m_cardImage->SetAlpha( percentageDone );
+#ifdef RAD_WIN32
+ m_cardImage->ScaleAboutCenter( percentageDone * HUDCARD_THUMBNAIL_SCALE );
+#else
+ m_cardImage->ScaleAboutCenter( percentageDone );
+#endif
+
+ // apply projectile motion effect to card image
+ //
+ const float CARD_GRAVITY = 0.005f;
+ GuiSFX::Projectile( m_cardImage,
+ m_elapsedTime,
+ CARD_TRANSITION_IN_TIME,
+ rmt::Vector( (float)cardCenterX, (float)cardCenterY, 0.0f ),
+ rmt::Vector( centerX, centerY, 0.0f ),
+ true,
+ CARD_GRAVITY );
+
+ // scale up card text
+ //
+ m_cardText->ScaleAboutCenter( percentageDone );
+
+ // apply projectile motion effect to card text
+ //
+ const float TEXT_GRAVITY = 0.0005f;
+ GuiSFX::Projectile( m_cardText,
+ m_elapsedTime,
+ CARD_TRANSITION_IN_TIME,
+ rmt::Vector( 0.0f, 0.0f, 0.0f ),
+ rmt::Vector( 0.0f, 0.0f, 0.0f ),
+ false,
+ TEXT_GRAVITY );
+
+ // fade in card count
+ //
+ m_itemsCount->SetAlpha( percentageDone );
+/*
+ // flash card text
+ //
+ GuiSFX::Flash( m_cardGet, m_elapsedTime, CARD_TRANSITION_IN_TIME, 1, 1.1f, 1.0f );
+ GuiSFX::Flash( m_cardTitle, m_elapsedTime, CARD_TRANSITION_IN_TIME, 1, 1.1f, 1.0f );
+*/
+ }
+ else
+ {
+ m_cardImage->SetAlpha( 1.0f );
+ m_itemsCount->SetAlpha( 1.0f );
+#ifdef RAD_WIN32
+ m_cardImage->ScaleAboutCenter( HUDCARD_THUMBNAIL_SCALE );
+#endif
+
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case STATE_CARD_DISPLAY_HOLD:
+ {
+ const float CARD_DISPLAY_HOLD_TIME = 2000.0f;
+
+ if( m_elapsedTime < CARD_DISPLAY_HOLD_TIME )
+ {
+ // do nothing
+ }
+ else
+ {
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case STATE_CARD_TRANSITION_OUT:
+ {
+ const float CARD_TRANSITION_OUT_TIME = 300.0f;
+
+ m_itemsComplete->ResetTransformation();
+
+ if( m_elapsedTime < CARD_TRANSITION_OUT_TIME )
+ {
+ float percentageDone = m_elapsedTime / CARD_TRANSITION_OUT_TIME;
+
+ // slide out card image
+ //
+ GuiSFX::SlideY( m_cardImage,
+ m_elapsedTime,
+ CARD_TRANSITION_OUT_TIME,
+ false,
+ GuiSFX::SLIDE_BORDER_TOP );
+
+ // slide out card count
+ //
+ GuiSFX::SlideY( m_itemsCount,
+ m_elapsedTime,
+ CARD_TRANSITION_OUT_TIME,
+ false,
+ GuiSFX::SLIDE_BORDER_TOP,
+ 100 );
+
+ // fade out card text (and scale up)
+ //
+ rAssert( m_cardText != NULL );
+ m_cardText->SetAlpha( 1.0f - percentageDone );
+
+ m_cardText->ResetTransformation();
+ m_cardText->ScaleAboutCenter( 1.0f + percentageDone * 0.5f );
+
+#ifdef RAD_WIN32
+ m_cardImage->ScaleAboutCenter( HUDCARD_THUMBNAIL_SCALE );
+#endif
+
+ if( m_isCardDeckComplete )
+ {
+ m_itemsComplete->SetVisible( true );
+
+ // scale up card deck complete text
+ //
+ m_itemsComplete->ScaleAboutCenter( percentageDone );
+
+ // apply projectile motion effect to card deck complete text
+ //
+ const float TEXT_GRAVITY = 0.005f;
+ GuiSFX::Projectile( m_itemsComplete,
+ m_elapsedTime,
+ CARD_TRANSITION_OUT_TIME,
+ rmt::Vector( 0.0f, 0.0f, 0.0f ),
+ rmt::Vector( 0.0f, 0.0f, 0.0f ),
+ false,
+ TEXT_GRAVITY );
+ }
+ }
+ else
+ {
+ m_cardText->SetAlpha( 0.0f );
+
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+
+ if( !m_isCardDeckComplete )
+ {
+ // skip to final sub state
+ //
+ m_currentSubState = NUM_SUB_STATES;
+ }
+ }
+
+ break;
+ }
+ case STATE_CARD_DECK_COMPLETE_DISPLAY_HOLD:
+ {
+ const float CARD_DISPLAY_HOLD_TIME = 2000.0f;
+
+ if( m_elapsedTime < CARD_DISPLAY_HOLD_TIME )
+ {
+ // do nothing
+ }
+ else
+ {
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case STATE_CARD_DECK_COMPLETE_TRANSITION_OUT:
+ {
+ const float CARD_TRANSITION_OUT_TIME = 300.0f;
+
+ if( m_elapsedTime < CARD_TRANSITION_OUT_TIME )
+ {
+ float percentageDone = m_elapsedTime / CARD_TRANSITION_OUT_TIME;
+
+ // fade out card text (and scale up)
+ //
+ rAssert( m_itemsComplete != NULL );
+ m_itemsComplete->SetAlpha( 1.0f - percentageDone );
+
+ m_itemsComplete->ResetTransformation();
+ m_itemsComplete->ScaleAboutCenter( 1.0f + percentageDone * 0.5f );
+ }
+ else
+ {
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case NUM_SUB_STATES:
+ {
+ // ok, we're done
+ //
+ this->Stop();
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Unhandled sub-state!" );
+ break;
+ }
+ }
+ }
+}
+
+void
+HudCardCollected::SetCurrentCard( unsigned int cardID )
+{
+ rAssert( m_cardImage != NULL );
+ m_cardImage->SetIndex( cardID );
+
+ rAssert( m_cardTitle != NULL );
+ m_cardTitle->SetIndex( cardID );
+}
+
+void
+HudCardCollected::SetCardCount( unsigned int numCollected,
+ unsigned int numCollectibles )
+{
+ char buffer[ BITMAP_TEXT_BUFFER_SIZE ];
+ sprintf( buffer, "%d/%d", numCollected, numCollectibles );
+
+ rAssert( m_numCards != NULL );
+ m_numCards->SetBitmapText( buffer );
+
+#ifndef RAD_DEMO
+ // set card deck complete flag
+ //
+ m_isCardDeckComplete = (numCollected == numCollectibles);
+
+ if( m_isCardDeckComplete &&
+ GetCardGallery()->GetNumCardDecksCompleted() == RenderEnums::numLevels )
+ {
+ // all card decks completed
+ //
+ rAssert( m_cardDeckComplete != NULL );
+ m_cardDeckComplete->SetIndex( CARD_DECK_COMPLETE );
+
+ rAssert( m_itemsUnlocked != NULL );
+ m_itemsUnlocked->SetIndex( UNLOCKED_IS_MOVIE );
+ }
+#endif // !RAD_DEMO
+}
+
diff --git a/game/code/presentation/gui/ingame/hudevents/hudcardcollected.h b/game/code/presentation/gui/ingame/hudevents/hudcardcollected.h
new file mode 100644
index 0000000..619af5c
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudcardcollected.h
@@ -0,0 +1,100 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudCardCollected
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+#ifndef HUDCARDCOLLECTED_H
+#define HUDCARDCOLLECTED_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudeventhandler.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Page;
+ class Group;
+ class Sprite;
+ class Text;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class HudCardCollected : public HudEventHandler
+{
+public:
+ HudCardCollected( Scrooby::Page* pPage );
+ virtual ~HudCardCollected();
+
+ virtual void Start();
+ virtual void Stop();
+ virtual void Update( float elapsedTime );
+
+ void SetCurrentCard( unsigned int cardID );
+
+private:
+ void SetCardCount( unsigned int numCollected, unsigned int numCollectibles );
+
+ static const unsigned int BITMAP_TEXT_BUFFER_SIZE = 8;
+
+ enum eSubState
+ {
+ STATE_CARD_TRANSITION_IN,
+ STATE_CARD_DISPLAY_HOLD,
+ STATE_CARD_TRANSITION_OUT,
+
+ // extra state when last card per level is collected
+ //
+ STATE_CARD_DECK_COMPLETE_DISPLAY_HOLD,
+ STATE_CARD_DECK_COMPLETE_TRANSITION_OUT,
+
+ NUM_SUB_STATES
+ };
+
+ unsigned int m_currentSubState;
+
+ Scrooby::Sprite* m_cardImage;
+
+ Scrooby::Group* m_cardText;
+ Scrooby::Text* m_cardGet;
+ Scrooby::Text* m_cardTitle;
+
+ Scrooby::Group* m_itemsCount;
+ Scrooby::Sprite* m_numCards;
+
+ Scrooby::Group* m_itemsComplete;
+
+ enum eCardDeckCompleteMessage
+ {
+ LEVEL_CARDS_COMPLETE,
+ CARD_DECK_COMPLETE
+ };
+
+ Scrooby::Text* m_cardDeckComplete;
+ bool m_isCardDeckComplete : 1;
+
+ enum eItemUnlockedMessage
+ {
+ UNLOCKED_MINI_GAME_TRACK,
+ UNLOCKED_IS_MOVIE
+ };
+
+ Scrooby::Text* m_itemsUnlocked;
+
+};
+
+#endif // HUDCARDCOLLECTED_H
diff --git a/game/code/presentation/gui/ingame/hudevents/hudcoincollected.cpp b/game/code/presentation/gui/ingame/hudevents/hudcoincollected.cpp
new file mode 100644
index 0000000..0728a36
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudcoincollected.cpp
@@ -0,0 +1,324 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudCoinCollected
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudcoincollected.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/guiscreen.h>
+
+#include <worldsim/coins/coinmanager.h>
+
+// Scrooby
+#include <app.h>
+#include <page.h>
+#include <group.h>
+#include <sprite.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const short NUMERIC_TEXT_SPACING = -12;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+HudCoinCollected::HudCoinCollected( Scrooby::Page* pPage )
+: HudEventHandler( pPage->GetGroup( "ItemCollected" ) ),
+ m_currentSubState( 0 ),
+ m_cardImage( NULL ),
+ m_cardText( NULL ),
+ m_itemsCount( NULL ),
+ m_numCoins( NULL ),
+ m_currentItemCount( 0 ),
+ m_itemsComplete( NULL ),
+ m_isFullCount( false ),
+ m_elapsedCoinDecrementTime( 0.0f )
+{
+ rAssert( pPage != NULL );
+
+ m_itemsCount = pPage->GetGroup( "ItemsCount" );
+ rAssert( m_itemsCount != NULL );
+
+ m_numCoins = m_itemsCount->GetSprite( "ItemsCount" );
+ rAssert( m_numCoins != NULL );
+ m_numCoins->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ m_numCoins->CreateBitmapTextBuffer( BITMAP_TEXT_BUFFER_SIZE );
+ m_numCoins->SetBitmapTextSpacing( NUMERIC_TEXT_SPACING );
+#ifdef RAD_WIN32
+ m_numCoins->Translate( 70, 0 );
+ m_numCoins->ScaleAboutCenter( 0.5f );
+#endif
+
+ m_itemsComplete = pPage->GetGroup( "ItemsComplete" );
+
+ Scrooby::Group* itemCollected = pPage->GetGroup( "ItemCollected" );
+ rAssert( itemCollected != NULL );
+
+ m_cardImage = itemCollected->GetSprite( "CardCollected" );
+ rAssert( m_cardImage != NULL );
+
+ m_cardText = itemCollected->GetGroup( "CardText" );
+ rAssert( m_cardText != NULL );
+
+ m_currentItemCount = GetCoinManager()->GetBankValue();
+}
+
+HudCoinCollected::~HudCoinCollected()
+{
+}
+
+void
+HudCoinCollected::Start()
+{
+ if( m_currentState == STATE_RUNNING )
+ {
+ if( m_currentSubState > STATE_COIN_TRANSITION_IN )
+ {
+ // return to beginning of display hold state
+ //
+ m_currentSubState = STATE_COIN_DISPLAY_HOLD;
+ m_elapsedTime = 0.0f;
+ m_elapsedCoinDecrementTime = 0.0f;
+
+ // restore drawable properties
+ //
+ rAssert( m_itemsCount != NULL );
+ m_itemsCount->ResetTransformation();
+ m_itemsCount->SetAlpha( 1.0f );
+ }
+ }
+ else
+ {
+ m_currentSubState = STATE_COIN_TRANSITION_IN;
+ m_elapsedCoinDecrementTime = 0.0f;
+
+ rAssert( m_numCoins != NULL );
+ m_numCoins->SetColour( tColour( 255, 255, 255 ) );
+
+ // restore drawable properties
+ //
+ rAssert( m_itemsCount != NULL );
+ m_itemsCount->ResetTransformation();
+ m_itemsCount->SetAlpha( 0.0f );
+
+ rAssert( m_itemsComplete != NULL );
+ m_itemsComplete->SetVisible( false );
+
+ // hide all the cards sub-groups
+ //
+ m_cardImage->SetVisible( false );
+ m_cardText->SetVisible( false );
+ }
+
+ this->OnStart();
+
+ // update coin count
+ //
+ this->SetItemCount( static_cast<unsigned int>( m_currentItemCount ), 0 );
+}
+
+void
+HudCoinCollected::Stop()
+{
+ GetCoinManager()->SetHUDCoin( 0, 0, false );
+
+ this->OnStop();
+}
+
+void
+HudCoinCollected::Update( float elapsedTime )
+{
+ if( m_currentState == STATE_RUNNING )
+ {
+ m_elapsedTime += elapsedTime;
+
+ switch( m_currentSubState )
+ {
+ case STATE_COIN_TRANSITION_IN:
+ {
+ const float COIN_TRANSITION_IN_TIME = 200.0f;
+
+ rAssert( m_itemsCount != NULL );
+
+ if( m_elapsedTime < COIN_TRANSITION_IN_TIME )
+ {
+ float percentageDone = m_elapsedTime / COIN_TRANSITION_IN_TIME;
+
+ // slide in coin count
+ //
+ GuiSFX::SlideY( m_itemsCount,
+ m_elapsedTime,
+ COIN_TRANSITION_IN_TIME,
+ true,
+ GuiSFX::SLIDE_BORDER_TOP,
+ 100 );
+
+ // fade in coin coint
+ //
+ m_itemsCount->SetAlpha( percentageDone );
+ }
+ else
+ {
+ m_itemsCount->ResetTransformation();
+ m_itemsCount->SetAlpha( 1.0f );
+
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case STATE_COIN_DISPLAY_HOLD:
+ {
+ const float COIN_DISPLAY_HOLD_TIME = 2000.0f;
+ const float COIN_UPDATE_PERIOD = 40.0f;
+
+ if( m_elapsedTime < COIN_DISPLAY_HOLD_TIME )
+ {
+ static int coinPosX = CGuiScreen::IsWideScreenDisplay() ? 500 : 555;
+ static int coinPosY = 422;
+ GetCoinManager()->SetHUDCoin( coinPosX, coinPosY, true );
+
+ int currentBankValue = GetCoinManager()->GetBankValue();
+
+ rAssert( m_numCoins != NULL );
+ if( m_currentItemCount > currentBankValue )
+ {
+ // coin loss
+ //
+ m_elapsedCoinDecrementTime += elapsedTime;
+
+ tColour coinColour;
+ GuiSFX::ModulateColour( &coinColour,
+ m_elapsedCoinDecrementTime,
+ COIN_UPDATE_PERIOD * 2,
+ tColour( 255, 255, 255 ),
+ tColour( 255, 0, 0 ) );
+
+ m_numCoins->SetColour( coinColour );
+ }
+ else
+ {
+ m_numCoins->SetColour( tColour( 255, 255, 255 ) );
+ }
+
+ // update coin change progressively
+ //
+ if( m_elapsedTime > COIN_UPDATE_PERIOD )
+ {
+ if( m_currentItemCount < currentBankValue )
+ {
+ m_currentItemCount++;
+ this->SetItemCount( static_cast<unsigned int>( m_currentItemCount ), 0 );
+
+ m_elapsedTime -= COIN_UPDATE_PERIOD;
+ }
+ else if( m_currentItemCount > currentBankValue )
+ {
+ m_currentItemCount--;
+ this->SetItemCount( static_cast<unsigned int>( m_currentItemCount ), 0 );
+
+ GetCoinManager()->AddFlyDownCoin();
+
+ m_elapsedTime -= COIN_UPDATE_PERIOD;
+ }
+ }
+ }
+ else
+ {
+ GetCoinManager()->SetHUDCoin( 0, 0, false );
+
+ rAssert( m_numCoins != NULL );
+ m_numCoins->SetColour( tColour( 255, 255, 255 ) );
+
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case STATE_COIN_TRANSITION_OUT:
+ {
+ const float COIN_TRANSITION_OUT_TIME = 200.0f;
+
+ if( m_elapsedTime < COIN_TRANSITION_OUT_TIME )
+ {
+ float percentageDone = m_elapsedTime / COIN_TRANSITION_OUT_TIME;
+
+ // slide out coin count
+ //
+ GuiSFX::SlideY( m_itemsCount,
+ m_elapsedTime,
+ COIN_TRANSITION_OUT_TIME,
+ false,
+ GuiSFX::SLIDE_BORDER_TOP,
+ 100 );
+ }
+ else
+ {
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+
+ if( !m_isFullCount )
+ {
+ // skip to final sub state
+ //
+ m_currentSubState = NUM_SUB_STATES;
+
+ }
+ }
+
+ break;
+ }
+ case NUM_SUB_STATES:
+ {
+ // ok, we're done
+ //
+ this->Stop();
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Unhandled sub-state!" );
+ break;
+ }
+ }
+ }
+}
+
+void
+HudCoinCollected::SetItemCount( unsigned int numCollected,
+ unsigned int numCollectibles )
+{
+ char buffer[ BITMAP_TEXT_BUFFER_SIZE ];
+ sprintf( buffer, "%d", numCollected );
+
+ rAssert( m_numCoins != NULL );
+ m_numCoins->SetBitmapText( buffer );
+
+ // set card deck complete flag
+ //
+ m_isFullCount = (numCollected == numCollectibles);
+}
+
diff --git a/game/code/presentation/gui/ingame/hudevents/hudcoincollected.h b/game/code/presentation/gui/ingame/hudevents/hudcoincollected.h
new file mode 100644
index 0000000..08f93bb
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudcoincollected.h
@@ -0,0 +1,84 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudCoinCollected
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+#ifndef HUDCOINCOLLECTED_H
+#define HUDCOINCOLLECTED_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudeventhandler.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Page;
+ class Group;
+ class Sprite;
+ class Text;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class HudCoinCollected : public HudEventHandler
+{
+public:
+ HudCoinCollected( Scrooby::Page* pPage );
+ virtual ~HudCoinCollected();
+
+ virtual void Start();
+ virtual void Stop();
+ virtual void Update( float elapsedTime );
+
+ void SetCurrentItemCount( int itemCount );
+
+private:
+ void SetItemCount( unsigned int numCollected, unsigned int numCollectibles );
+
+ static const unsigned int BITMAP_TEXT_BUFFER_SIZE = 8;
+
+ enum eSubState
+ {
+ STATE_COIN_TRANSITION_IN,
+ STATE_COIN_DISPLAY_HOLD,
+ STATE_COIN_TRANSITION_OUT,
+
+ NUM_SUB_STATES
+ };
+
+ unsigned int m_currentSubState;
+
+ Scrooby::Sprite* m_cardImage;
+ Scrooby::Group* m_cardText;
+
+ Scrooby::Group* m_itemsCount;
+ Scrooby::Sprite* m_numCoins;
+ int m_currentItemCount;
+
+ Scrooby::Group* m_itemsComplete;
+ bool m_isFullCount : 1;
+
+ float m_elapsedCoinDecrementTime;
+
+};
+
+inline void HudCoinCollected::SetCurrentItemCount( int itemCount )
+{
+ m_currentItemCount = itemCount;
+}
+
+#endif // HUDCOINCOLLECTED_H
diff --git a/game/code/presentation/gui/ingame/hudevents/hudcountdown.cpp b/game/code/presentation/gui/ingame/hudevents/hudcountdown.cpp
new file mode 100644
index 0000000..b8fc05f
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudcountdown.cpp
@@ -0,0 +1,233 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudCountDown
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudcountdown.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/guitextbible.h>
+
+#include <events/eventmanager.h>
+#include <input/controller.h>
+#include <input/inputmanager.h>
+#include <mission/gameplaymanager.h>
+#include <gameflow/gameflow.h>
+#include <worldsim/character/charactermanager.h>
+
+// Scrooby
+#include <group.h>
+#include <page.h>
+#include <sprite.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+HudCountDown::HudCountDown( Scrooby::Page* pPage )
+: HudEventHandler( pPage->GetGroup( "CountDown" ) ),
+ m_countDownMessage( NULL ),
+ m_missionStage( NULL ),
+ m_currentSequenceUnit( NULL ),
+ m_nextSequenceIndex( 0 ),
+ m_InputDisablePending( false ),
+ m_isDialogTriggerPending( false )
+{
+// const float BITMAP_TEXT_SPACING = 0.8f;
+
+ rAssert( pPage != NULL );
+ m_countDownMessage = pPage->GetSprite( "CountDown" );
+ rAssert( m_countDownMessage != NULL );
+ m_countDownMessage->SetSpriteMode( Scrooby::SPRITE_BITMAP_TEXT );
+ m_countDownMessage->CreateBitmapTextBuffer( 32 );
+// m_countDownMessage->SetBitmapTextSpacing( BITMAP_TEXT_SPACING );
+}
+
+HudCountDown::~HudCountDown()
+{
+}
+
+//=============================================================================
+// HudCountDown::OnStart
+//=============================================================================
+// Description: Called when the HudCountDown starts
+//
+// Parameters: NONE
+//
+// Return: void
+//
+//=============================================================================
+void HudCountDown::OnStart()
+{
+ Parent::OnStart();
+}
+
+//=============================================================================
+// HudCountDown::QueueDisableInput
+//=============================================================================
+// Description: tells the object that the next time update is called, we need
+// to disable the input system
+//
+// Parameters: NONE
+//
+// Return: void
+//
+//=============================================================================
+void HudCountDown::QueueDisableInput()
+{
+ m_InputDisablePending = true;
+}
+
+void
+HudCountDown::Start()
+{
+ Character* npcPtr;
+ const char* modelName;
+
+ this->OnStart();
+
+ m_missionStage = GetGameplayManager()->GetCurrentMission()->GetCurrentStage();
+
+ m_nextSequenceIndex = 0;
+ this->GetNextSequenceUnit();
+
+ // get dialog event data and queue it up for triggering on the next update
+ //
+ BonusMissionInfo* bonusMissionInfo = const_cast<BonusMissionInfo*>( GetGameplayManager()->GetCurrentBonusMissionInfo() );
+ if( bonusMissionInfo != NULL )
+ {
+ npcPtr = bonusMissionInfo->GetNPC();
+ rAssert( npcPtr != NULL );
+ modelName = GetCharacterManager()->GetModelName( npcPtr );
+
+ m_dialogData.charUID1 = tEntity::MakeUID( modelName );
+ m_dialogData.charUID2 = m_missionStage->GetCountdownSecondSpeakerUID();
+ m_dialogData.dialogName = m_missionStage->GetCountdownDialogID();
+
+ m_isDialogTriggerPending = true;
+ }
+
+ QueueDisableInput();
+}
+
+void
+HudCountDown::Stop()
+{
+ m_missionStage = NULL;
+ m_currentSequenceUnit = NULL;
+
+ // enable the controller - yes i know this is not an animated cam
+ //
+ if( GetInputManager()->GetGameState() == Input::ACTIVE_ANIM_CAM )
+ {
+ InputManager::GetInstance()->SetGameState( Input::DEACTIVE_ANIM_CAM );
+ }
+
+ this->OnStop();
+}
+
+void
+HudCountDown::Update( float elapsedTime )
+{
+ if( m_InputDisablePending )
+ {
+ // disable the controller - yes i know this is not an animated cam
+ //
+ InputManager::GetInstance()->SetGameState( Input::ACTIVE_ANIM_CAM );
+ m_InputDisablePending = false;
+ }
+
+ if( m_currentState == STATE_RUNNING )
+ {
+ if( m_isDialogTriggerPending )
+ {
+ m_isDialogTriggerPending = false;
+
+ GetEventManager()->TriggerEvent( EVENT_IN_GAMEPLAY_CONVERSATION, static_cast<void*>( &m_dialogData ) );
+ }
+
+#ifdef RAD_WIN32
+ static float COUNT_DOWN_MAX_SCALE = 1.5f;
+#else
+ static float COUNT_DOWN_MAX_SCALE = 3.0f;
+#endif
+ static float COUNT_DOWN_THRESHOLD_SCALE = 2.5f;
+ static int COUNT_DOWN_ZOOM_RATE = 2;
+
+ if( m_currentSequenceUnit != NULL )
+ {
+ bool done = GuiSFX::Flash( m_countDownMessage,
+ m_elapsedTime,
+ (float)m_currentSequenceUnit->durationTime,
+ COUNT_DOWN_ZOOM_RATE,
+ COUNT_DOWN_MAX_SCALE,
+ COUNT_DOWN_THRESHOLD_SCALE );
+ if( done )
+ {
+ m_elapsedTime = 0;
+
+ this->GetNextSequenceUnit();
+ }
+ }
+ else
+ {
+ // countdown finished
+ //
+ this->Stop();
+
+ // start the mission!
+ //
+ GetEventManager()->TriggerEvent( EVENT_GUI_MISSION_START );
+
+ //
+ // Take all the AI cars out of the limbo state
+ //
+ MissionStage* stage = GetGameplayManager()->GetCurrentMission()->GetCurrentStage();
+ rAssert( stage != NULL );
+ stage->PutAllAisInLimbo( false );
+ }
+
+ m_elapsedTime += elapsedTime;
+ }
+}
+
+void
+HudCountDown::GetNextSequenceUnit()
+{
+ rAssert( m_missionStage != NULL );
+ m_currentSequenceUnit = m_missionStage->GetCountdownSequenceUnit( m_nextSequenceIndex );
+
+ if( m_currentSequenceUnit != NULL )
+ {
+ // set text message
+ //
+ rAssert( m_countDownMessage != NULL );
+ m_countDownMessage->SetAlpha( 0.0f );
+
+ P3D_UNICODE* text = GetTextBibleString( m_currentSequenceUnit->textID );
+ if( text != NULL )
+ {
+ m_countDownMessage->SetBitmapText( text );
+ }
+ else
+ {
+ m_countDownMessage->SetBitmapText( "" );
+ }
+ }
+
+ m_nextSequenceIndex++;
+}
+
diff --git a/game/code/presentation/gui/ingame/hudevents/hudcountdown.h b/game/code/presentation/gui/ingame/hudevents/hudcountdown.h
new file mode 100644
index 0000000..4ea50b0
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudcountdown.h
@@ -0,0 +1,69 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudCountDown
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+#ifndef HUDCOUNTDOWN_H
+#define HUDCOUNTDOWN_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudeventhandler.h>
+#include <mission/missionstage.h>
+#include <events/eventdata.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Page;
+ class Sprite;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class HudCountDown : public HudEventHandler
+{
+private:
+ typedef HudEventHandler Parent;
+
+public:
+ HudCountDown( Scrooby::Page* pPage );
+ virtual ~HudCountDown();
+
+ virtual void Start();
+ virtual void Stop();
+ virtual void Update( float elapsedTime );
+
+protected:
+ void OnStart();
+ void QueueDisableInput();
+
+private:
+ void GetNextSequenceUnit();
+
+ Scrooby::Sprite* m_countDownMessage;
+
+ MissionStage* m_missionStage;
+ MissionStage::CountdownSequenceUnit* m_currentSequenceUnit;
+ int m_nextSequenceIndex;
+ bool m_InputDisablePending;
+
+ bool m_isDialogTriggerPending;
+ DialogEventData m_dialogData;
+
+};
+
+#endif // HUDCOUNTDOWN_H
diff --git a/game/code/presentation/gui/ingame/hudevents/hudeventhandler.cpp b/game/code/presentation/gui/ingame/hudevents/hudeventhandler.cpp
new file mode 100644
index 0000000..378401e
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudeventhandler.cpp
@@ -0,0 +1,77 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudEventHandler
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudeventhandler.h>
+
+// Scrooby
+#include <group.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+HudEventHandler::HudEventHandler( Scrooby::Group* drawableGroup )
+: m_drawableGroup( drawableGroup ),
+ m_elapsedTime( 0.0f ),
+ m_currentState( STATE_IDLE )
+{
+ rAssert( drawableGroup != NULL );
+ drawableGroup->SetVisible( false );
+}
+
+HudEventHandler::~HudEventHandler()
+{
+}
+
+void
+HudEventHandler::OnStart()
+{
+ if( m_currentState != STATE_RUNNING )
+ {
+ // show all drawable elements in group
+ //
+ rAssert( m_drawableGroup != NULL );
+ m_drawableGroup->SetVisible( true );
+
+ // reset elapsed time
+ //
+ m_elapsedTime = 0.0;
+
+ // update current state
+ //
+ m_currentState = STATE_RUNNING;
+ }
+}
+
+void
+HudEventHandler::OnStop()
+{
+ if( m_currentState != STATE_IDLE )
+ {
+ // hide all drawable elements in group
+ //
+ rAssert( m_drawableGroup != NULL );
+ m_drawableGroup->SetVisible( false );
+
+ // update current state
+ //
+ m_currentState = STATE_IDLE;
+ }
+}
+
diff --git a/game/code/presentation/gui/ingame/hudevents/hudeventhandler.h b/game/code/presentation/gui/ingame/hudevents/hudeventhandler.h
new file mode 100644
index 0000000..fcb931d
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudeventhandler.h
@@ -0,0 +1,69 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudEventHandler
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+#ifndef HUDEVENTHANDLER_H
+#define HUDEVENTHANDLER_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Group;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class HudEventHandler
+{
+public:
+ HudEventHandler( Scrooby::Group* drawableGroup );
+ virtual ~HudEventHandler();
+
+ virtual void Start() = 0;
+ virtual void Stop() = 0;
+ virtual void Update( float elapsedTime ) = 0;
+
+ bool IsActive() const;
+
+protected:
+ void OnStart();
+ void OnStop();
+
+ Scrooby::Group* m_drawableGroup;
+ float m_elapsedTime;
+
+ enum eState
+ {
+ STATE_IDLE,
+ STATE_RUNNING,
+
+ NUM_STATES
+ };
+
+ eState m_currentState;
+
+};
+
+inline bool HudEventHandler::IsActive() const
+{
+ return( m_currentState != STATE_IDLE );
+}
+
+#endif // HUDEVENTHANDLER_H
diff --git a/game/code/presentation/gui/ingame/hudevents/hudhitnrun.cpp b/game/code/presentation/gui/ingame/hudevents/hudhitnrun.cpp
new file mode 100644
index 0000000..17c1da3
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudhitnrun.cpp
@@ -0,0 +1,297 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudHitNRun
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudhitnrun.h>
+#include <presentation/gui/utility/specialfx.h>
+
+// Scrooby
+#include <app.h>
+#include <group.h>
+#include <page.h>
+#include <text.h>
+#include <sprite.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+#ifdef RAD_WIN32
+const float HNR_MESSAGE_SCALE = 1.75f;
+const float HNR_MESSAGE_BUSTED_SCALE = 2.0f;
+const float HNR_TICKET_SCALE = 1.0f;
+const float HNR_MESSAGE_BUSTED_TILT_ANGLE = -10.0f; // in degrees
+const tColour HNR_MESSAGE_BUSTED_COLOUR( 43, 89, 249 );
+#else
+const float HNR_MESSAGE_SCALE = 1.75f;
+const float HNR_MESSAGE_BUSTED_SCALE = 2.0f;
+const float HNR_TICKET_SCALE = 2.0f;
+const float HNR_MESSAGE_BUSTED_TILT_ANGLE = -10.0f; // in degrees
+const tColour HNR_MESSAGE_BUSTED_COLOUR( 43, 89, 249 );
+#endif
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+HudHitNRun::HudHitNRun( Scrooby::Page* pPage )
+: HudEventHandler( pPage->GetGroup( "HitNRun" ) ),
+ m_currentSubState( 0 ),
+ m_currentMessage( MSG_HIT_N_RUN ),
+ m_hnrMessage( NULL ),
+ m_hnrTicket( NULL ),
+ m_messageTranslateX( 0 ),
+ m_messageTranslateY( 0 ),
+ m_defaultMessageColour( 100, 0, 0 )
+{
+ rAssert( pPage != NULL );
+
+ Scrooby::Group* pGroup = pPage->GetGroup( "HitNRun" );
+ rAssert( pGroup != NULL );
+
+ m_hnrMessage = pGroup->GetText( "HitNRun" );
+ rAssert( m_hnrMessage != NULL );
+ m_hnrMessage->SetTextMode( Scrooby::TEXT_WRAP );
+ m_hnrMessage->SetDisplayOutline( true );
+
+// m_defaultMessageColour = m_hnrMessage->GetColour();
+
+ m_hnrTicket = pGroup->GetSprite( "HitNRunTicket" );
+
+ int messageCenterX = 0;
+ int messageCenterY = 0;
+ m_hnrMessage->GetBoundingBoxCenter( messageCenterX, messageCenterY );
+
+ Scrooby::Sprite* radar = pPage->GetSprite( "Radar0" );
+ rAssert( radar != NULL );
+ radar->GetBoundingBoxCenter( m_messageTranslateX, m_messageTranslateY );
+
+ m_messageTranslateX -= messageCenterX;
+ m_messageTranslateY -= messageCenterY;
+}
+
+HudHitNRun::~HudHitNRun()
+{
+}
+
+void
+HudHitNRun::Start()
+{
+ if( m_currentState != STATE_RUNNING )
+ {
+ m_currentSubState = STATE_MESSAGE_TRANSITION_IN;
+
+ rAssert( m_hnrMessage != NULL );
+ m_hnrMessage->SetIndex( m_currentMessage );
+ m_hnrMessage->SetAlpha( 0.0f );
+ m_hnrMessage->SetColour( m_currentMessage == MSG_BUSTED ?
+ HNR_MESSAGE_BUSTED_COLOUR :
+ m_defaultMessageColour ); // restore default colour
+
+ rAssert( m_hnrTicket != NULL );
+ m_hnrTicket->SetAlpha( 0.0f );
+ m_hnrTicket->SetVisible( m_currentMessage == MSG_BUSTED );
+ }
+
+ this->OnStart();
+}
+
+void
+HudHitNRun::Stop()
+{
+ this->OnStop();
+}
+
+void
+HudHitNRun::Update( float elapsedTime )
+{
+ if( m_currentState == STATE_RUNNING )
+ {
+ m_elapsedTime += elapsedTime;
+
+ switch( m_currentSubState )
+ {
+ case STATE_MESSAGE_TRANSITION_IN:
+ {
+ const float MESSAGE_TRANSITION_TIME = 200.0f;
+
+ rAssert( m_hnrMessage != NULL );
+ m_hnrMessage->ResetTransformation();
+
+ rAssert( m_hnrTicket != NULL );
+ m_hnrTicket->ResetTransformation();
+
+ if( m_elapsedTime < MESSAGE_TRANSITION_TIME )
+ {
+ float percentageDone = m_elapsedTime / MESSAGE_TRANSITION_TIME;
+
+ m_hnrMessage->SetAlpha( percentageDone );
+
+ if( m_hnrMessage->GetIndex() == MSG_HIT_N_RUN )
+ {
+ m_hnrMessage->ScaleAboutCenter( percentageDone * HNR_MESSAGE_SCALE );
+ }
+ else
+ {
+ m_hnrMessage->ScaleAboutCenter( percentageDone * HNR_MESSAGE_BUSTED_SCALE );
+ m_hnrMessage->RotateAboutCenter( percentageDone * HNR_MESSAGE_BUSTED_TILT_ANGLE );
+
+ m_hnrTicket->SetAlpha( percentageDone );
+ m_hnrTicket->ScaleAboutCenter( percentageDone * HNR_TICKET_SCALE );
+
+ GuiSFX::Projectile( m_hnrTicket,
+ m_elapsedTime,
+ MESSAGE_TRANSITION_TIME,
+ rmt::Vector( 0, 0, 0 ),
+ rmt::Vector( (float)m_messageTranslateX, (float)m_messageTranslateY, 0 ),
+ true,
+ 0.0f );
+ }
+
+ GuiSFX::Projectile( m_hnrMessage,
+ m_elapsedTime,
+ MESSAGE_TRANSITION_TIME,
+ rmt::Vector( 0, 0, 0 ),
+ rmt::Vector( (float)m_messageTranslateX, (float)m_messageTranslateY, 0 ),
+ true,
+ 0.0f );
+ }
+ else
+ {
+ m_hnrMessage->SetAlpha( 1.0f );
+
+ if( m_hnrMessage->GetIndex() == MSG_HIT_N_RUN )
+ {
+ m_hnrMessage->ScaleAboutCenter( HNR_MESSAGE_SCALE );
+ }
+ else
+ {
+ m_hnrMessage->ScaleAboutCenter( HNR_MESSAGE_BUSTED_SCALE );
+ m_hnrMessage->RotateAboutCenter( HNR_MESSAGE_BUSTED_TILT_ANGLE );
+
+ m_hnrTicket->SetAlpha( 1.0f );
+ m_hnrTicket->ScaleAboutCenter( HNR_TICKET_SCALE );
+ }
+
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case STATE_MESSAGE_DISPLAY_HOLD:
+ {
+ const float MESSAGE_DISPLAY_HOLD_TIME = 1200.0f;
+
+ rAssert( m_hnrMessage != NULL );
+ if( m_elapsedTime < MESSAGE_DISPLAY_HOLD_TIME )
+ {
+ if( m_hnrMessage->GetIndex() == MSG_HIT_N_RUN )
+ {
+ static tColour MESSAGE_OTHER_COLOUR( 240, 8, 8 );
+
+ tColour currentColour;
+ GuiSFX::ModulateColour( &currentColour,
+ m_elapsedTime,
+ 333.3f,
+ m_defaultMessageColour,
+ MESSAGE_OTHER_COLOUR,
+ rmt::PI_BY2 );
+
+ m_hnrMessage->SetColour( currentColour );
+ }
+ }
+ else
+ {
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case STATE_MESSAGE_TRANSITION_OUT:
+ {
+ const float MESSAGE_TRANSITION_TIME = 200.0f;
+
+ rAssert( m_hnrMessage != NULL );
+ m_hnrMessage->ResetTransformation();
+
+ rAssert( m_hnrTicket != NULL );
+ m_hnrTicket->ResetTransformation();
+
+ if( m_elapsedTime < MESSAGE_TRANSITION_TIME )
+ {
+ float percentageDone = m_elapsedTime / MESSAGE_TRANSITION_TIME;
+
+ if( m_hnrMessage->GetIndex() == MSG_HIT_N_RUN )
+ {
+ m_hnrMessage->SetAlpha( 1.0f - percentageDone );
+ m_hnrMessage->ScaleAboutCenter( (1.0f - percentageDone) * HNR_MESSAGE_SCALE );
+
+ GuiSFX::Projectile( m_hnrMessage,
+ m_elapsedTime,
+ MESSAGE_TRANSITION_TIME,
+ rmt::Vector( 0, 0, 0 ),
+ rmt::Vector( (float)m_messageTranslateX, (float)m_messageTranslateY, 0 ),
+ false,
+ 0.0f );
+ }
+ else
+ {
+ m_hnrMessage->SetAlpha( 0.0f );
+
+ m_hnrTicket->ScaleAboutCenter( (percentageDone * 3 + 1.0f) * HNR_TICKET_SCALE );
+ m_hnrTicket->SetAlpha( 1.0f - percentageDone );
+ }
+ }
+ else
+ {
+ m_hnrMessage->SetAlpha( 0.0f );
+ m_hnrTicket->SetAlpha( 0.0f );
+
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case NUM_SUB_STATES:
+ {
+ // ok, we're done
+ //
+ this->Stop();
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Unhandled sub-state!" );
+ break;
+ }
+ }
+ }
+}
+
+void
+HudHitNRun::SetMessage( eMessage message )
+{
+ m_currentMessage = message;
+}
+
diff --git a/game/code/presentation/gui/ingame/hudevents/hudhitnrun.h b/game/code/presentation/gui/ingame/hudevents/hudhitnrun.h
new file mode 100644
index 0000000..778a867
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudhitnrun.h
@@ -0,0 +1,81 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudHitNRun
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+#ifndef HUDHITNRUN_H
+#define HUDHITNRUN_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudeventhandler.h>
+
+#include <p3d/p3dtypes.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Page;
+ class Text;
+ class Sprite;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class HudHitNRun : public HudEventHandler
+{
+public:
+ HudHitNRun( Scrooby::Page* pPage );
+ virtual ~HudHitNRun();
+
+ virtual void Start();
+ virtual void Stop();
+ virtual void Update( float elapsedTime );
+
+ enum eMessage
+ {
+ MSG_HIT_N_RUN,
+ MSG_BUSTED,
+
+ NUM_MESSAGES
+ };
+
+ void SetMessage( eMessage message );
+
+private:
+ enum eSubState
+ {
+ STATE_MESSAGE_TRANSITION_IN,
+ STATE_MESSAGE_DISPLAY_HOLD,
+ STATE_MESSAGE_TRANSITION_OUT,
+
+ NUM_SUB_STATES
+ };
+
+ unsigned int m_currentSubState;
+
+ eMessage m_currentMessage;
+ Scrooby::Text* m_hnrMessage;
+ Scrooby::Sprite* m_hnrTicket;
+
+ int m_messageTranslateX;
+ int m_messageTranslateY;
+
+ tColour m_defaultMessageColour;
+
+};
+
+#endif // HUDHITNRUN_H
diff --git a/game/code/presentation/gui/ingame/hudevents/huditemdropped.cpp b/game/code/presentation/gui/ingame/hudevents/huditemdropped.cpp
new file mode 100644
index 0000000..38eb6f7
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/huditemdropped.cpp
@@ -0,0 +1,217 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudItemDropped
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/huditemdropped.h>
+#include <presentation/gui/utility/specialfx.h>
+
+// Scrooby
+#include <page.h>
+#include <group.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+HudItemDropped::HudItemDropped( Scrooby::Page* pPage )
+: HudEventHandler( pPage->GetGroup( "ItemDropped" ) ),
+ m_currentSubState( 0 ),
+ m_itemDropped( NULL )
+{
+ m_itemDropped = m_drawableGroup->GetText( "ItemDropped" );
+ rAssert( m_itemDropped != NULL );
+ m_itemDropped->SetTextMode( Scrooby::TEXT_WRAP );
+}
+
+HudItemDropped::~HudItemDropped()
+{
+}
+
+void
+HudItemDropped::Start()
+{
+ this->OnStart();
+
+ m_currentSubState = STATE_TRANSITION_IN;
+
+ rAssert( m_itemDropped != NULL );
+ m_itemDropped->ResetTransformation();
+ m_itemDropped->SetAlpha( 0.0f );
+ m_itemDropped->SetVisible( true );
+}
+
+void
+HudItemDropped::Stop()
+{
+ this->OnStop();
+}
+
+void
+HudItemDropped::Update( float elapsedTime )
+{
+ if( m_currentState == STATE_RUNNING )
+ {
+ m_elapsedTime += elapsedTime;
+
+ switch( m_currentSubState )
+ {
+ case STATE_TRANSITION_IN:
+ {
+ const float TRANSITION_IN_TIME = 200.0f;
+
+ rAssert( m_itemDropped != NULL );
+ m_itemDropped->ResetTransformation();
+
+ if( m_elapsedTime < TRANSITION_IN_TIME )
+ {
+ float percentageDone = m_elapsedTime / TRANSITION_IN_TIME;
+
+ // slide in from HUD icon
+ //
+ m_itemDropped->Translate( 0, static_cast<int>( (1.0f - percentageDone) * 100 ) );
+
+ // fade in coin coint
+ //
+ m_itemDropped->SetAlpha( percentageDone );
+ }
+ else
+ {
+ m_itemDropped->ResetTransformation();
+ m_itemDropped->SetAlpha( 1.0f );
+
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case STATE_DISPLAY_HOLD:
+ {
+ const float BLINKING_PERIOD = 250.0f;
+
+ bool isBlinked = GuiSFX::Blink( m_itemDropped,
+ m_elapsedTime,
+ BLINKING_PERIOD );
+
+ if( isBlinked )
+ {
+ m_elapsedTime = static_cast<float>( static_cast<int>( m_elapsedTime ) %
+ static_cast<int>( BLINKING_PERIOD ) );
+ }
+/*
+ const float DISPLAY_HOLD_TIME = 2000.0f;
+
+ if( m_elapsedTime < DISPLAY_HOLD_TIME )
+ {
+ static int coinPosX = CGuiScreen::IsWideScreenDisplay() ? 500 : 555;
+ static int coinPosY = 422;
+ GetCoinManager()->SetHUDCoin( coinPosX, coinPosY, true );
+
+ int currentBankValue = GetCoinManager()->GetBankValue();
+
+ rAssert( m_numCoins != NULL );
+ if( m_currentItemCount > currentBankValue )
+ {
+ // coin loss
+ //
+ m_elapsedCoinDecrementTime += elapsedTime;
+
+ tColour coinColour;
+ GuiSFX::ModulateColour( &coinColour,
+ m_elapsedCoinDecrementTime,
+ UPDATE_PERIOD * 2,
+ tColour( 255, 255, 255 ),
+ tColour( 255, 0, 0 ) );
+
+ m_numCoins->SetColour( coinColour );
+ }
+ else
+ {
+ m_numCoins->SetColour( tColour( 255, 255, 255 ) );
+ }
+ }
+ else
+ {
+ GetCoinManager()->SetHUDCoin( 0, 0, false );
+
+ rAssert( m_numCoins != NULL );
+ m_numCoins->SetColour( tColour( 255, 255, 255 ) );
+
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+*/
+ break;
+ }
+ case STATE_TRANSITION_OUT:
+ {
+ const float TRANSITION_OUT_TIME = 200.0f;
+/*
+ if( m_elapsedTime < TRANSITION_OUT_TIME )
+ {
+ float percentageDone = m_elapsedTime / TRANSITION_OUT_TIME;
+
+ // slide out coin count
+ //
+ GuiSFX::SlideY( m_itemsCount,
+ m_elapsedTime,
+ TRANSITION_OUT_TIME,
+ false,
+ GuiSFX::SLIDE_BORDER_TOP,
+ 100 );
+ }
+ else
+ {
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+
+ if( !m_isFullCount )
+ {
+ // skip to final sub state
+ //
+ m_currentSubState = NUM_SUB_STATES;
+
+ }
+ }
+*/
+ break;
+ }
+ case NUM_SUB_STATES:
+ {
+ // ok, we're done
+ //
+ this->Stop();
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Unhandled sub-state!" );
+ break;
+ }
+ }
+ }
+}
+
diff --git a/game/code/presentation/gui/ingame/hudevents/huditemdropped.h b/game/code/presentation/gui/ingame/hudevents/huditemdropped.h
new file mode 100644
index 0000000..824c001
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/huditemdropped.h
@@ -0,0 +1,61 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudItemDropped
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+#ifndef HUDITEMDROPPED_H
+#define HUDITEMDROPPED_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudeventhandler.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Page;
+ class Text;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class HudItemDropped : public HudEventHandler
+{
+public:
+ HudItemDropped( Scrooby::Page* pPage );
+ virtual ~HudItemDropped();
+
+ virtual void Start();
+ virtual void Stop();
+ virtual void Update( float elapsedTime );
+
+private:
+ enum eSubState
+ {
+ STATE_TRANSITION_IN,
+ STATE_DISPLAY_HOLD,
+ STATE_TRANSITION_OUT,
+
+ NUM_SUB_STATES
+ };
+
+ unsigned int m_currentSubState;
+
+ Scrooby::Text* m_itemDropped;
+
+};
+
+#endif // HUDITEMDROPPED_H
diff --git a/game/code/presentation/gui/ingame/hudevents/hudmissionobjective.cpp b/game/code/presentation/gui/ingame/hudevents/hudmissionobjective.cpp
new file mode 100644
index 0000000..a6b2e05
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudmissionobjective.cpp
@@ -0,0 +1,255 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudMissionObjective
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudmissionobjective.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/utility/specialfx.h>
+
+#include <mission/gameplaymanager.h>
+#include <mission/mission.h>
+#include <mission/missionstage.h>
+
+// Scrooby
+#include <group.h>
+#include <page.h>
+#include <sprite.h>
+
+// Pure3D
+#include <p3d/utility.hpp>
+#include <p3d/sprite.hpp>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const int HUD_ICON_SLIDE_DISTANCE = 310; // vertical distance in pixels
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+HudMissionObjective::HudMissionObjective( Scrooby::Page* pPage )
+: HudEventHandler( pPage->GetGroup( "MissionObjective" ) ),
+ m_currentSubState( STATE_ICON_POP_UP ),
+ m_missionIcon( NULL ),
+ m_missionIconImage( NULL ),
+ m_messageID( 0 )
+{
+ rAssert( pPage != NULL );
+
+ m_missionIcon = pPage->GetSprite( "ObjectiveIcon" );
+ rAssert( m_missionIcon != NULL );
+
+ m_iconTranslator.SetDrawable( m_missionIcon );
+ m_iconTranslator.SetStartOffscreenBottom( m_missionIcon );
+ m_iconTranslator.SetFrequency( 5.0f );
+ m_iconTranslator.SetTimeInterval( 500.0f );
+
+ bool isIconFound = this->UpdateIcon();
+ if( isIconFound )
+ {
+ this->OnStart();
+
+ m_missionIcon->ResetTransformation();
+ m_missionIcon->ScaleAboutCenter( MISSION_ICON_SCALE );
+ m_missionIcon->Translate( 0, HUD_ICON_SLIDE_DISTANCE );
+
+ m_currentSubState = STATE_IDLE;
+ }
+}
+
+HudMissionObjective::~HudMissionObjective()
+{
+ if( m_missionIconImage != NULL )
+ {
+ m_missionIconImage->Release();
+ m_missionIconImage = NULL;
+ }
+}
+
+void
+HudMissionObjective::Start()
+{
+ this->OnStart();
+
+ m_currentSubState = STATE_ICON_POP_UP;
+
+ bool isIconFound = this->UpdateIcon();
+ if( isIconFound )
+ {
+ m_iconTranslator.Reset();
+ m_iconTranslator.Activate();
+ }
+ else
+ {
+ m_iconTranslator.Deactivate();
+ }
+}
+
+void
+HudMissionObjective::Stop()
+{
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->DisplayMessage( false );
+ }
+
+ rAssert( m_missionIcon != NULL );
+ m_missionIcon->SetRawSprite( NULL );
+ m_missionIcon->SetVisible( false );
+
+ if( m_missionIconImage != NULL )
+ {
+ m_missionIconImage->Release();
+ m_missionIconImage = NULL;
+ }
+
+ this->OnStop();
+}
+
+void
+HudMissionObjective::Update( float elapsedTime )
+{
+ if( m_currentState == STATE_RUNNING )
+ {
+ m_elapsedTime += elapsedTime;
+
+ switch( m_currentSubState )
+ {
+ case STATE_ICON_POP_UP:
+ {
+ rAssert( m_missionIcon != NULL );
+ m_missionIcon->ResetTransformation();
+ m_missionIcon->ScaleAboutCenter( MISSION_ICON_SCALE );
+
+ if( !m_iconTranslator.IsDone() )
+ {
+ m_iconTranslator.Update( elapsedTime );
+ }
+ else
+ {
+ m_iconTranslator.Deactivate();
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ rAssert( currentHud != NULL );
+ currentHud->DisplayMessage( true, m_messageID );
+
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case STATE_ICON_DISPLAY_HOLD:
+ {
+ const float DISPLAY_HOLD_TIME = 3000.0f;
+
+ if( m_elapsedTime < DISPLAY_HOLD_TIME )
+ {
+ // do nothing
+ }
+ else
+ {
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case STATE_ICON_SLIDE_UP:
+ {
+ const float SLIDE_UP_TIME = 200.0f;
+
+ rAssert( m_missionIcon != NULL );
+ m_missionIcon->ResetTransformation();
+ m_missionIcon->ScaleAboutCenter( MISSION_ICON_SCALE );
+
+ if( m_elapsedTime < SLIDE_UP_TIME )
+ {
+ int translateY = (int)( (m_elapsedTime / SLIDE_UP_TIME) * HUD_ICON_SLIDE_DISTANCE );
+ m_missionIcon->Translate( 0, translateY );
+ }
+ else
+ {
+ m_missionIcon->Translate( 0, HUD_ICON_SLIDE_DISTANCE );
+
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case STATE_IDLE:
+ {
+ // do nothing
+ //
+ break;
+ }
+ case NUM_SUB_STATES:
+ {
+ rAssertMsg( false, "We shouldn't be here; last sub-state should be IDLE." );
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Unhandled sub-state!" );
+
+ break;
+ }
+ }
+ }
+}
+
+bool
+HudMissionObjective::UpdateIcon()
+{
+ tSprite* pSprite = NULL;
+
+ Mission* currentMission = GetGameplayManager()->GetCurrentMission();
+ if( currentMission != NULL )
+ {
+ MissionStage* currentStage = currentMission->GetCurrentStage();
+ if( currentStage != NULL )
+ {
+ const char* iconName = currentStage->GetHUDIcon();
+ if( iconName[ 0 ] != '\0' )
+ {
+ pSprite = p3d::find<tSprite>( iconName );
+ rTuneWarningMsg( pSprite != NULL, "Can't find HUD icon for mission presentation!" );
+ }
+ }
+ }
+
+ rAssert( m_missionIcon != NULL );
+ m_missionIcon->SetVisible( pSprite != NULL );
+ m_missionIcon->SetRawSprite( pSprite, true );
+
+ if( pSprite != NULL )
+ {
+ tRefCounted::Assign( m_missionIconImage, pSprite );
+ }
+
+ return (pSprite != NULL);
+}
+
diff --git a/game/code/presentation/gui/ingame/hudevents/hudmissionobjective.h b/game/code/presentation/gui/ingame/hudevents/hudmissionobjective.h
new file mode 100644
index 0000000..7e9c217
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudmissionobjective.h
@@ -0,0 +1,84 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudMissionObjective
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+#ifndef HUDMISSIONOBJECTIVE_H
+#define HUDMISSIONOBJECTIVE_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudeventhandler.h>
+#include <presentation/gui/utility/transitions.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+#ifdef RAD_WIN32 // temporary.. for art testing.
+const float MISSION_ICON_SCALE = 0.78f;
+#else
+const float MISSION_ICON_SCALE = 1.5f;
+#endif
+
+namespace Scrooby
+{
+ class Page;
+ class Sprite;
+}
+
+class tSprite;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class HudMissionObjective : public HudEventHandler
+{
+public:
+ HudMissionObjective( Scrooby::Page* pPage );
+ virtual ~HudMissionObjective();
+
+ virtual void Start();
+ virtual void Stop();
+ virtual void Update( float elapsedTime );
+
+ void SetMessageID( unsigned int messageID );
+ bool UpdateIcon();
+
+private:
+ enum eSubState
+ {
+ STATE_ICON_POP_UP,
+ STATE_ICON_DISPLAY_HOLD,
+ STATE_ICON_SLIDE_UP,
+ STATE_IDLE,
+
+ NUM_SUB_STATES
+ };
+
+ unsigned int m_currentSubState;
+
+ Scrooby::Sprite* m_missionIcon;
+ tSprite* m_missionIconImage;
+ unsigned int m_messageID;
+
+ GuiSFX::UnderdampedTranslator m_iconTranslator;
+
+};
+
+inline void
+HudMissionObjective::SetMessageID( unsigned int messageID )
+{
+ m_messageID = messageID;
+}
+
+#endif // HUDMISSIONOBJECTIVE_H
diff --git a/game/code/presentation/gui/ingame/hudevents/hudmissionprogress.cpp b/game/code/presentation/gui/ingame/hudevents/hudmissionprogress.cpp
new file mode 100644
index 0000000..be85e76
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudmissionprogress.cpp
@@ -0,0 +1,83 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudMissionProgress
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudmissionprogress.h>
+#include <presentation/gui/utility/specialfx.h>
+
+// Scrooby
+#include <group.h>
+#include <page.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+HudMissionProgress::HudMissionProgress( Scrooby::Page* pPage )
+: HudEventHandler( pPage->GetGroup( "MissionProgress" ) ),
+ m_stageComplete( NULL )
+{
+ rAssert( pPage != NULL );
+
+ Scrooby::Group* missionProgress = pPage->GetGroup( "MissionProgress" );
+ rAssert( missionProgress != NULL );
+
+ m_stageComplete = missionProgress->GetText( "StageComplete" );
+ rAssert( m_stageComplete != NULL );
+ m_stageComplete->SetTextMode( Scrooby::TEXT_WRAP );
+}
+
+HudMissionProgress::~HudMissionProgress()
+{
+}
+
+void
+HudMissionProgress::Start()
+{
+ this->OnStart();
+}
+
+void
+HudMissionProgress::Stop()
+{
+ this->OnStop();
+}
+
+void
+HudMissionProgress::Update( float elapsedTime )
+{
+ if( m_currentState == STATE_RUNNING )
+ {
+ m_elapsedTime += elapsedTime;
+
+ static float DURATION_TIME = 1000.0f;
+
+ bool isDone = GuiSFX::Flash( m_stageComplete,
+ m_elapsedTime,
+ DURATION_TIME,
+ 1,
+ 1.25f,
+ 1.0f );
+ if( isDone )
+ {
+ this->Stop();
+ }
+ }
+}
+
diff --git a/game/code/presentation/gui/ingame/hudevents/hudmissionprogress.h b/game/code/presentation/gui/ingame/hudevents/hudmissionprogress.h
new file mode 100644
index 0000000..11623f2
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudmissionprogress.h
@@ -0,0 +1,50 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudMissionProgress
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+#ifndef HUDMISSIONPROGRESS_H
+#define HUDMISSIONPROGRESS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudeventhandler.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Page;
+ class Text;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class HudMissionProgress : public HudEventHandler
+{
+public:
+ HudMissionProgress( Scrooby::Page* pPage );
+ virtual ~HudMissionProgress();
+
+ virtual void Start();
+ virtual void Stop();
+ virtual void Update( float elapsedTime );
+
+private:
+ Scrooby::Text* m_stageComplete;
+
+};
+
+#endif // HUDMISSIONPROGRESS_H
diff --git a/game/code/presentation/gui/ingame/hudevents/hudwaspdestroyed.cpp b/game/code/presentation/gui/ingame/hudevents/hudwaspdestroyed.cpp
new file mode 100644
index 0000000..3c3a879
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudwaspdestroyed.cpp
@@ -0,0 +1,184 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudWaspDestroyed
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudwaspdestroyed.h>
+#include <presentation/gui/utility/specialfx.h>
+
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <mission/rewards/rewardsmanager.h>
+
+// Scrooby
+#include <page.h>
+#include <group.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+HudWaspDestroyed::HudWaspDestroyed( Scrooby::Page* pPage )
+: HudEventHandler( pPage->GetGroup( "WaspsDestroyed" ) ),
+ m_currentSubState( 0 ),
+ m_itemsComplete( NULL ),
+ m_isGagInsteadOfWasp( false )
+{
+ m_itemsComplete = m_drawableGroup->GetText( "WaspsComplete" );
+ rAssert( m_itemsComplete != NULL );
+}
+
+HudWaspDestroyed::~HudWaspDestroyed()
+{
+}
+
+void
+HudWaspDestroyed::Start()
+{
+ RenderEnums::LevelEnum currentLevel = GetGameplayManager()->GetCurrentLevelIndex();
+
+ bool isComplete = m_isGagInsteadOfWasp ?
+ GetCharacterSheetManager()->QueryNumGagsViewed( currentLevel ) == GetRewardsManager()->GetTotalGags( currentLevel ) :
+ GetCharacterSheetManager()->QueryNumWaspsDestroyed( currentLevel ) == GetRewardsManager()->GetTotalWasps( currentLevel );
+
+ if( isComplete )
+ {
+ this->OnStart();
+
+ m_currentSubState = STATE_WASP_TRANSITION_IN;
+
+ // restore drawable properties
+ //
+ rAssert( m_itemsComplete != NULL );
+ m_itemsComplete->ResetTransformation();
+ m_itemsComplete->SetAlpha( 1.0f );
+ m_itemsComplete->SetIndex( m_isGagInsteadOfWasp ? 1 : 0 );
+ }
+}
+
+void
+HudWaspDestroyed::Stop()
+{
+ this->OnStop();
+}
+
+void
+HudWaspDestroyed::Update( float elapsedTime )
+{
+ if( m_currentState == STATE_RUNNING )
+ {
+ m_elapsedTime += elapsedTime;
+
+ switch( m_currentSubState )
+ {
+ case STATE_WASP_TRANSITION_IN:
+ {
+ const float CARD_TRANSITION_IN_TIME = 300.0f;
+
+ m_itemsComplete->ResetTransformation();
+
+ if( m_elapsedTime < CARD_TRANSITION_IN_TIME )
+ {
+ float percentageDone = m_elapsedTime / CARD_TRANSITION_IN_TIME;
+
+ // scale up card deck complete text
+ //
+ m_itemsComplete->ScaleAboutCenter( percentageDone );
+
+ // apply projectile motion effect to card deck complete text
+ //
+ const float TEXT_GRAVITY = 0.005f;
+ GuiSFX::Projectile( m_itemsComplete,
+ m_elapsedTime,
+ CARD_TRANSITION_IN_TIME,
+ rmt::Vector( 0.0f, 0.0f, 0.0f ),
+ rmt::Vector( 0.0f, 0.0f, 0.0f ),
+ false,
+ TEXT_GRAVITY );
+ }
+ else
+ {
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case STATE_WASP_DISPLAY_HOLD:
+ {
+ const float CARD_DISPLAY_HOLD_TIME = 2000.0f;
+
+ if( m_elapsedTime < CARD_DISPLAY_HOLD_TIME )
+ {
+ // do nothing
+ }
+ else
+ {
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case STATE_WASP_TRANSITION_OUT:
+ {
+ const float CARD_TRANSITION_OUT_TIME = 300.0f;
+
+ if( m_elapsedTime < CARD_TRANSITION_OUT_TIME )
+ {
+ float percentageDone = m_elapsedTime / CARD_TRANSITION_OUT_TIME;
+
+ // fade out card text (and scale up)
+ //
+ rAssert( m_itemsComplete != NULL );
+ m_itemsComplete->SetAlpha( 1.0f - percentageDone );
+
+ m_itemsComplete->ResetTransformation();
+ m_itemsComplete->ScaleAboutCenter( 1.0f + percentageDone * 0.5f );
+ }
+ else
+ {
+ // advance to next sub state
+ //
+ m_elapsedTime = 0.0f;
+ m_currentSubState++;
+ }
+
+ break;
+ }
+ case NUM_SUB_STATES:
+ {
+ // ok, we're done
+ //
+ this->Stop();
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Unhandled sub-state!" );
+ break;
+ }
+ }
+ }
+}
+
diff --git a/game/code/presentation/gui/ingame/hudevents/hudwaspdestroyed.h b/game/code/presentation/gui/ingame/hudevents/hudwaspdestroyed.h
new file mode 100644
index 0000000..9d50b96
--- /dev/null
+++ b/game/code/presentation/gui/ingame/hudevents/hudwaspdestroyed.h
@@ -0,0 +1,72 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudWaspDestroyed
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/02/05 TChu Created
+//
+//===========================================================================
+
+#ifndef HUDWASPDESTROYED_H
+#define HUDWASPDESTROYED_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/ingame/hudevents/hudeventhandler.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Page;
+ class Text;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+class HudWaspDestroyed : public HudEventHandler
+{
+public:
+ HudWaspDestroyed( Scrooby::Page* pPage );
+ virtual ~HudWaspDestroyed();
+
+ virtual void Start();
+ virtual void Stop();
+ virtual void Update( float elapsedTime );
+
+ void SetGagInsteadOfWasp( bool isGagInsteadOfWasp );
+
+private:
+ enum eSubState
+ {
+ STATE_WASP_TRANSITION_IN,
+ STATE_WASP_DISPLAY_HOLD,
+ STATE_WASP_TRANSITION_OUT,
+
+ NUM_SUB_STATES
+ };
+
+ unsigned int m_currentSubState;
+
+ Scrooby::Text* m_itemsComplete;
+
+ // re-use this event handler for gags
+ //
+ bool m_isGagInsteadOfWasp : 1;
+
+};
+
+inline void HudWaspDestroyed::SetGagInsteadOfWasp( bool isGagInsteadOfWasp )
+{
+ m_isGagInsteadOfWasp = isGagInsteadOfWasp;
+}
+
+#endif // HUDWASPDESTROYED_H
diff --git a/game/code/presentation/gui/minigame/allminigame.cpp b/game/code/presentation/gui/minigame/allminigame.cpp
new file mode 100644
index 0000000..b727b65
--- /dev/null
+++ b/game/code/presentation/gui/minigame/allminigame.cpp
@@ -0,0 +1,5 @@
+#include <presentation/gui/minigame/guimanagerminigame.cpp>
+#include <presentation/gui/minigame/guiscreenminimenu.cpp>
+#include <presentation/gui/minigame/guiscreenminihud.cpp>
+#include <presentation/gui/minigame/guiscreenminipause.cpp>
+#include <presentation/gui/minigame/guiscreenminisummary.cpp>
diff --git a/game/code/presentation/gui/minigame/guimanagerminigame.cpp b/game/code/presentation/gui/minigame/guimanagerminigame.cpp
new file mode 100644
index 0000000..0392273
--- /dev/null
+++ b/game/code/presentation/gui/minigame/guimanagerminigame.cpp
@@ -0,0 +1,491 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManagerMiniGame
+//
+// Description: Implementation of the CGuiManagerMiniGame class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/24 TChu Created
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/minigame/guimanagerminigame.h>
+#include <presentation/gui/guiwindow.h> // for window IDs
+
+#include <presentation/gui/guiscreenprompt.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/minigame/guiscreenminimenu.h>
+#include <presentation/gui/minigame/guiscreenminihud.h>
+#include <presentation/gui/minigame/guiscreenminipause.h>
+#include <presentation/gui/minigame/guiscreenminisummary.h>
+#include <presentation/gui/guiscreenmessage.h>
+
+#include <gameflow/gameflow.h>
+#include <memory/srrmemory.h>
+#include <loading/loadingmanager.h>
+
+#include <p3d/utility.hpp>
+#include <p3d/inventory.hpp>
+#include <raddebug.hpp> // Foundation
+
+#include <input/inputmanager.h>
+
+#include <main/platform.h>
+#include <main/game.h>
+#include <contexts/supersprint/supersprintcontext.h>
+#include <supersprint/supersprintmanager.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const char* MINIGAME_CHARACTERS_INVENTORY = "FE_MiniGameCharacters";
+
+const char* CHARACTER_FILES[] =
+{
+ "art\\chars\\homer_m.p3d",
+ "art\\chars\\bart_m.p3d",
+ "art\\chars\\lisa_m.p3d",
+ "art\\chars\\marge_m.p3d",
+ "art\\chars\\apu_m.p3d",
+
+ ""
+};
+
+const int NUM_CHARACTER_FILES = sizeof( CHARACTER_FILES ) / sizeof( CHARACTER_FILES[ 0 ] ) - 1;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiManagerMiniGame::CGuiManagerMiniGame
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManagerMiniGame::CGuiManagerMiniGame( Scrooby::Project* pProject,
+ CGuiEntity* pParent )
+: CGuiManager( pProject, pParent ),
+ m_isQuittingToSupersprint( false ),
+ m_controllerPromptShown( false ),
+ m_isControllerReconnected( false ),
+ m_lastControllerDisconnectedId( -1 )
+{
+ p3d::inventory->AddSection( MINIGAME_CHARACTERS_INVENTORY );
+}
+
+//===========================================================================
+// CGuiManagerMiniGame::~CGuiManagerMiniGame
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiManagerMiniGame::~CGuiManagerMiniGame()
+{
+ p3d::inventory->DeleteSection( MINIGAME_CHARACTERS_INVENTORY );
+}
+
+//===========================================================================
+// CGuiManagerMiniGame::Populate
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManagerMiniGame::Populate()
+{
+MEMTRACK_PUSH_GROUP( "CGuiManagerMiniGame" );
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ Scrooby::Screen* pScroobyScreen = NULL;
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "Prompt" );
+ if( pScroobyScreen != NULL )
+ {
+ CGuiScreen* pScreen = new CGuiScreenPrompt( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_GENERIC_PROMPT, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MiniMenu" );
+ if( pScroobyScreen != NULL )
+ {
+ CGuiScreen* pScreen = new CGuiScreenMiniMenu( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MINI_MENU, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MiniHud" );
+ if( pScroobyScreen != NULL )
+ {
+ CGuiScreen* pScreen = new CGuiScreenMiniHud( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MINI_HUD, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MiniPause" );
+ if( pScroobyScreen != NULL )
+ {
+ CGuiScreen* pScreen = new CGuiScreenMiniPause( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MINI_PAUSE, pScreen );
+ }
+
+ pScroobyScreen = m_pScroobyProject->GetScreen( "MiniSummary" );
+ if( pScroobyScreen != NULL )
+ {
+ CGuiScreen* pScreen = new CGuiScreenMiniSummary( pScroobyScreen, this );
+ this->AddWindow( CGuiWindow::GUI_SCREEN_ID_MINI_SUMMARY, pScreen );
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+MEMTRACK_POP_GROUP("CGuiManagerMiniGame");
+}
+
+void
+CGuiManagerMiniGame::Start( CGuiWindow::eGuiWindowID initialWindow )
+{
+ rAssert( GUI_FE_UNINITIALIZED == m_state );
+
+ m_nextScreen = initialWindow != CGuiWindow::GUI_WINDOW_ID_UNDEFINED ?
+ initialWindow :
+ CGuiWindow::GUI_SCREEN_ID_MINI_MENU;
+
+ m_state = GUI_FE_CHANGING_SCREENS; // must be set before calling GotoScreen()
+
+ CGuiScreen* nextScreen = static_cast<CGuiScreen*>( this->FindWindowByID( m_nextScreen ) );
+ rAssert( nextScreen != NULL );
+ m_pScroobyProject->GotoScreen( nextScreen->GetScroobyScreen(), this );
+}
+
+//===========================================================================
+// CGuiManagerMiniGame::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CGuiManagerMiniGame::HandleMessage( eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2 )
+{
+ switch( message )
+ {
+ case GUI_MSG_WINDOW_FINISHED:
+ {
+ if( GUI_FE_CHANGING_SCREENS == m_state )
+ {
+ m_currentScreen = m_nextScreen;
+
+ CGuiScreen* nextScreen = static_cast<CGuiScreen*>( this->FindWindowByID( m_nextScreen ) );
+ rAssert( nextScreen != NULL );
+ m_pScroobyProject->GotoScreen( nextScreen->GetScroobyScreen(), this );
+ }
+ else if( GUI_FE_SHUTTING_DOWN == m_state )
+ {
+ if( m_isQuittingToSupersprint )
+ {
+ // quit and start loading supersprint mini-game
+ //
+ GetGameFlow()->SetContext( CONTEXT_LOADING_SUPERSPRINT );
+
+ m_state = GUI_FE_UNINITIALIZED;
+ }
+ else
+ {
+ // quit mini-game mode and return to front-end
+ //
+ GetGameFlow()->SetContext( CONTEXT_FRONTEND );
+
+ m_state = GUI_FE_TERMINATED;
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_QUIT_MINIGAME:
+ {
+ rAssert( GUI_FE_SCREEN_RUNNING == m_state );
+
+ m_state = GUI_FE_SHUTTING_DOWN;
+
+ m_isQuittingToSupersprint = (param1 > 0 );
+
+ // Tell the current screen to shut down.
+ //
+ this->FindWindowByID( m_currentScreen )->HandleMessage( GUI_MSG_WINDOW_EXIT );
+
+ break;
+ }
+
+ case GUI_MSG_CONTROLLER_DISCONNECT:
+#ifndef RAD_GAMECUBE
+ this->OnControllerDisconnected( static_cast<int>( param1 ) );
+#endif // !RAD_GAMECUBE
+ break;
+
+ case GUI_MSG_CONTROLLER_CONNECT:
+#ifndef RAD_GAMECUBE
+ m_oldControllerState = InputManager::GetInstance()->GetGameState();
+ if( m_oldControllerState == Input::ACTIVE_ANIM_CAM )
+ {
+ // deactivate anim cam state first, since the input manager
+ // won't let us set the game state to anything else prior
+ // to that
+ //
+ GetInputManager()->SetGameState( Input::DEACTIVE_ANIM_CAM );
+ }
+ GetInputManager()->SetGameState( Input::ACTIVE_FRONTEND );
+#endif // !RAD_GAMECUBE
+ break;
+ default:
+ {
+ if( message == GUI_MSG_UPDATE && m_isControllerReconnected )
+ {
+ m_isControllerReconnected = false;
+ m_controllerPromptShown = false;
+ }
+
+ if (m_controllerPromptShown) // don't pass event if controller error
+ {
+ if (message==GUI_MSG_CONTROLLER_START) // start trigger reconnection
+ {
+ this->OnControllerConnected( static_cast<int>( param1 ) );
+ }
+
+ break;
+ }
+
+ if( m_state != GUI_FE_UNINITIALIZED &&
+ m_currentScreen != CGuiWindow::GUI_WINDOW_ID_UNDEFINED )
+ {
+ // Send the messages down to the current screen.
+ //
+ CGuiWindow* pScreen = this->FindWindowByID( m_currentScreen );
+ rAssert( pScreen != NULL );
+ pScreen->HandleMessage( message, param1, param2 );
+ }
+#ifndef RAD_GAMECUBE
+ if ( message == GUI_MSG_UPDATE)
+ PollControllers();
+#endif
+ break;
+ }
+ }
+
+ // propogate message up the hierarchy
+ //
+ CGuiManager::HandleMessage( message, param1, param2 );
+}
+
+void
+CGuiManagerMiniGame::OnControllerDisconnected( int controllerID, bool force_display )
+{
+ if (force_display) // update message
+ {
+ m_lastControllerDisconnectedId = controllerID;
+ char str_buffer[256];
+ CGuiScreenMessage::GetControllerDisconnectedMessage(controllerID, str_buffer, 255);
+ GetGame()->GetPlatform()->OnControllerError(str_buffer);
+ m_controllerPromptShown = true;
+ }
+ else if (m_controllerPromptShown) // check if this disconnection id is for the player already registered
+ {
+ for (int i = 0; i < SuperSprintData::NUM_PLAYERS; i++)
+ {
+ int player_controllerID = GetInputManager()->GetControllerIDforPlayer( i );
+ if (controllerID == player_controllerID)
+ {
+ m_lastControllerDisconnectedId = controllerID;
+ char str_buffer[256];
+ CGuiScreenMessage::GetControllerDisconnectedMessage(controllerID, str_buffer, 255);
+ GetGame()->GetPlatform()->OnControllerError(str_buffer);
+ m_controllerPromptShown = true;
+ }
+ }
+ }
+
+}
+bool
+CGuiManagerMiniGame::PollControllers()
+{
+ bool all_ok = true;
+ bool has_registered = false;
+
+ if ( GetGameFlow()->GetCurrentContext()==CONTEXT_SUPERSPRINT
+ && GetGameFlow()->GetContext( CONTEXT_SUPERSPRINT )->IsSuspended() ) // minigame pause menu
+ {
+ int controllerID = CGuiScreenMiniHud::s_pausedControllerID;
+ if (controllerID >= 0)
+ {
+ has_registered = true;
+ if( !GetInputManager()->GetController( controllerID )->IsConnected() )
+ {
+ OnControllerDisconnected(controllerID, true);
+ all_ok = false;
+ }
+ }
+ }
+ else
+ { // minigame game, or minigame menu
+ for (int i = 0; i < SuperSprintData::NUM_PLAYERS; i++)
+ {
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( i );
+ if (controllerID >= 0)
+ {
+ has_registered = true;
+ if( !GetInputManager()->GetController( controllerID )->IsConnected() )
+ {
+ OnControllerDisconnected(controllerID, true);
+ all_ok = false;
+ break;
+ }
+ }
+ }
+ }
+ if (all_ok) {
+ if (has_registered == false) // minigame menu but no one registered yet
+ {
+ int controllerID = GetGuiSystem()->GetPrimaryController( );
+ if (controllerID >= 0)
+ {
+ if( !GetInputManager()->GetController( controllerID )->IsConnected() )
+ {
+ OnControllerDisconnected(controllerID, true);
+ all_ok = false;
+ }
+ }
+ }
+ }
+
+ if (all_ok)
+ {
+ m_isControllerReconnected = true;
+// m_controllerPromptShown = false;
+ }
+ return all_ok;
+
+}
+
+void
+CGuiManagerMiniGame::OnControllerConnected( int controllerID )
+{
+ if (controllerID==m_lastControllerDisconnectedId)
+ { // poll
+ bool all_ok = PollControllers();
+ if (all_ok)
+ {
+ GetGame()->GetPlatform()->ClearControllerError();
+ GetSSM()->RestoreControllerState();
+
+ if ( GetGameFlow()->GetCurrentContext()==CONTEXT_SUPERSPRINT )
+ {
+#ifdef RAD_XBOX
+ CGuiScreenMiniHud::s_pausedControllerID = controllerID;
+
+ if( GetSSM()->GetState() != SuperSprintManager::IDLE )
+ {
+ this->HandleMessage( GUI_MSG_GOTO_SCREEN, CGuiWindow::GUI_SCREEN_ID_MINI_PAUSE );
+ }
+
+ GetGameFlow()->GetContext( CONTEXT_SUPERSPRINT )->Suspend();
+
+ if (GetSSM()->GetState()==SuperSprintManager::COUNT_DOWN)
+ {
+ InputManager::GetInstance()->SetGameState( Input::DEACTIVE_ANIM_CAM );
+ InputManager::GetInstance()->SetGameState( Input::ACTIVE_FRONTEND );
+ }
+ else
+ {
+ InputManager::GetInstance()->SetGameState( Input::DEACTIVE_ANIM_CAM );
+ InputManager::GetInstance()->SetGameState( Input::ACTIVE_ALL );
+ }
+#endif
+ }
+
+ m_isControllerReconnected = true;
+// m_controllerPromptShown = false;
+ m_lastControllerDisconnectedId = -1;
+ }
+ }
+}
+
+//===========================================================================
+// CGuiManagerMiniGame::LoadCharacters
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CGuiManagerMiniGame::LoadCharacters()
+{
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ "art\\chars\\global.p3d",
+ GMA_LEVEL_HUD,
+ MINIGAME_CHARACTERS_INVENTORY,
+ MINIGAME_CHARACTERS_INVENTORY );
+
+ for( int i = 0; i < NUM_CHARACTER_FILES; i++ )
+ {
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D,
+ CHARACTER_FILES[ i ],
+ GMA_LEVEL_HUD,
+ MINIGAME_CHARACTERS_INVENTORY,
+ MINIGAME_CHARACTERS_INVENTORY );
+ }
+}
+
+//===========================================================================
+// CGuiManagerMiniGame::UnloadCharacters
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CGuiManagerMiniGame::UnloadCharacters()
+{
+ p3d::pddi->DrawSync();
+ p3d::inventory->RemoveSectionElements( MINIGAME_CHARACTERS_INVENTORY );
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
diff --git a/game/code/presentation/gui/minigame/guimanagerminigame.h b/game/code/presentation/gui/minigame/guimanagerminigame.h
new file mode 100644
index 0000000..b4534e2
--- /dev/null
+++ b/game/code/presentation/gui/minigame/guimanagerminigame.h
@@ -0,0 +1,71 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiManagerMiniGame
+//
+// Description: Interface for the CGuiManagerMiniGame class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/24 TChu Created
+//
+//===========================================================================
+
+#ifndef GUIMANAGERMINIGAME_H
+#define GUIMANAGERMINIGAME_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guimanager.h>
+#include <input/inputmanager.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiManagerMiniGame : public CGuiManager
+{
+public:
+ CGuiManagerMiniGame( Scrooby::Project* pProject, CGuiEntity* pParent );
+ virtual ~CGuiManagerMiniGame();
+
+ virtual void Populate();
+ virtual void Start( CGuiWindow::eGuiWindowID initialWindow = CGuiWindow::GUI_WINDOW_ID_UNDEFINED );
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ static void LoadCharacters();
+ static void UnloadCharacters();
+
+private:
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or assignment. Declare but don't define.
+ //
+ CGuiManagerMiniGame( const CGuiManagerMiniGame& );
+ CGuiManagerMiniGame& operator= ( const CGuiManagerMiniGame& );
+
+ void OnControllerDisconnected( int controllerID, bool force_display = false );
+ void OnControllerConnected( int controllerID );
+ bool PollControllers();
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ bool m_isQuittingToSupersprint : 1;
+ bool m_controllerPromptShown : 1;
+ bool m_isControllerReconnected : 1;
+ int m_lastControllerDisconnectedId;
+ Input::ActiveState m_oldControllerState;
+
+};
+
+#endif // GUIMANAGERMINIGAME_H
diff --git a/game/code/presentation/gui/minigame/guiscreenminihud.cpp b/game/code/presentation/gui/minigame/guiscreenminihud.cpp
new file mode 100644
index 0000000..88a9904
--- /dev/null
+++ b/game/code/presentation/gui/minigame/guiscreenminihud.cpp
@@ -0,0 +1,217 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMiniHud
+//
+// Description: Implementation of the CGuiScreenMiniHud class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/24 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/minigame/guiscreenminihud.h>
+#include <presentation/gui/guimanager.h>
+
+#include <contexts/supersprint/supersprintcontext.h>
+#include <gameflow/gameflow.h>
+
+#ifdef RAD_WIN32
+#include <input/inputmanager.h>
+#endif
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+int CGuiScreenMiniHud::s_pausedControllerID = -1;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMiniHud::CGuiScreenMiniHud
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMiniHud::CGuiScreenMiniHud
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_MINI_HUD )
+{
+}
+
+
+//===========================================================================
+// CGuiScreenMiniHud::~CGuiScreenMiniHud
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMiniHud::~CGuiScreenMiniHud()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenMiniHud::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniHud::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+#ifdef RAD_WIN32
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ if( GetInputManager()->GetValue( 0, InputManager::KeyboardEsc ) > 0.0f )
+ {
+ //GOOD! Fall through.
+ }
+ else
+ {
+ break;
+ }
+ }
+#endif
+ case GUI_MSG_CONTROLLER_START:
+ {
+ // pause mini-game
+ //
+ GetGameFlow()->GetContext( CONTEXT_SUPERSPRINT )->Suspend();
+
+ // go to pause menu
+ //
+ s_pausedControllerID = static_cast<int>( param1 );
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_MINI_PAUSE );
+
+ break;
+ }
+/*
+ // TC: *** for testing only ***
+ //
+ case GUI_MSG_CONTROLLER_AUX_X:
+ case GUI_MSG_CONTROLLER_AUX_Y:
+ {
+ // go to Race Summary screen
+ //
+ GetGameFlow()->GetContext( CONTEXT_SUPERSPRINT )->Suspend();
+
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_MINI_SUMMARY );
+
+ break;
+ }
+*/
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenMiniHud::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniHud::InitIntro()
+{
+#ifdef RAD_WIN32
+ GetInputManager()->GetFEMouse()->SetInGameMode( true );
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenMiniHud::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniHud::InitRunning()
+{
+ if( GetGameFlow()->GetContext( CONTEXT_SUPERSPRINT )->IsSuspended() )
+ {
+ // resume mini-game
+ //
+ GetGameFlow()->GetContext( CONTEXT_SUPERSPRINT )->Resume();
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMiniHud::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniHud::InitOutro()
+{
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
diff --git a/game/code/presentation/gui/minigame/guiscreenminihud.h b/game/code/presentation/gui/minigame/guiscreenminihud.h
new file mode 100644
index 0000000..b36c436
--- /dev/null
+++ b/game/code/presentation/gui/minigame/guiscreenminihud.h
@@ -0,0 +1,52 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMiniHud
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/24 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMINIHUD_H
+#define GUISCREENMINIHUD_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMiniHud : public CGuiScreen
+{
+public:
+ CGuiScreenMiniHud( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMiniHud();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ // controller ID of user that paused mini-game
+ //
+ static int s_pausedControllerID;
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+};
+
+#endif // GUISCREENMINIHUD_H
diff --git a/game/code/presentation/gui/minigame/guiscreenminimenu.cpp b/game/code/presentation/gui/minigame/guiscreenminimenu.cpp
new file mode 100644
index 0000000..69e5db0
--- /dev/null
+++ b/game/code/presentation/gui/minigame/guiscreenminimenu.cpp
@@ -0,0 +1,1633 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMiniMenu
+//
+// Description: Implementation of the CGuiScreenMiniMenu class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/24 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/minigame/guiscreenminimenu.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/guimenu.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiuserinputhandler.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/guiscreenprompt.h>
+
+#include <cards/cardgallery.h>
+#include <events/eventmanager.h>
+#include <input/inputmanager.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <mission/rewards/rewardsmanager.h>
+#include <mission/rewards/reward.h>
+#include <render/enums/renderenums.h>
+#include <supersprint/supersprintmanager.h>
+#include <supersprint/supersprintdata.h>
+
+// Scrooby
+//
+#include <screen.h>
+#include <page.h>
+#include <layer.h>
+#include <group.h>
+#include <sprite.h>
+#include <text.h>
+#include <pure3dobject.h>
+#include <polygon.h>
+
+// ATG
+//
+#include <p3d/camera.hpp>
+#include <p3d/utility.hpp>
+#include <raddebug.hpp>
+
+#include <string.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const float NUM_LAPS_ARROW_ROTATION = 90.0f; // in degrees
+const float NUM_LAPS_ARROW_GREY_OUT_ALPHA = 0.5f;
+
+#ifdef RAD_WIN32
+const float CHARACTER_SCALE = 0.67f;
+const float VEHICLE_SCALE = 0.5f;
+const float CHARACTER_ARROW_SCALE = 0.45f;
+const float VEHICLE_ARROW_SCALE = 0.85f;
+const float NUM_LAPS_ARROW_SCALE = 0.375f; // scale arrows down a bit
+#else
+const float NUM_LAPS_ARROW_SCALE = 0.75f; // scale arrows down a bit
+#endif
+
+PlayerMenu::PlayerMenu()
+: m_currentSubMenu( 0 ),
+ m_controllerID( -1 ),
+ m_pressStart( NULL ),
+ m_vehicleRating( NULL ),
+ m_characterSelectedIcon( NULL ),
+ m_vehicleSelectedIcon( NULL )
+{
+ memset( m_pMenu, 0, sizeof( m_pMenu ) );
+}
+
+void
+PlayerMenu::HandleMessage( eGuiMessage message, unsigned int param1,
+ unsigned int param2 )
+{
+ if( m_currentSubMenu < NUM_SUB_MENUS )
+ {
+ rAssert( m_currentSubMenu >= 0 );
+ m_pMenu[ m_currentSubMenu ]->HandleMessage( message, param1, param2 );
+ }
+}
+
+CGuiMenu*
+PlayerMenu::GetCurrentSubMenu() const
+{
+ if( m_currentSubMenu < NUM_SUB_MENUS )
+ {
+ rAssert( m_currentSubMenu >= 0 );
+ return m_pMenu[ m_currentSubMenu ];
+ }
+
+ return NULL;
+}
+
+void
+PlayerMenu::SetActive( bool isActive, int controllerID )
+{
+ m_controllerID = isActive ? controllerID : -1;
+
+ // hide/show press start text
+ //
+ rAssert( m_pressStart != NULL );
+ m_pressStart->SetVisible( !isActive );
+
+ // show/hide player cursor
+ //
+ rAssert( m_pMenu[ MENU_CHARACTERS ] != NULL );
+ m_pMenu[ MENU_CHARACTERS ]->GetCursor()->SetVisible( isActive );
+
+ // show/hide vehicle rating
+ //
+ if( m_vehicleRating != NULL )
+ {
+ m_vehicleRating->SetVisible( isActive );
+ }
+
+ // reset vehicle selection
+ //
+ rAssert( m_pMenu[ MENU_VEHICLES ] != NULL );
+ m_pMenu[ MENU_VEHICLES ]->SetSelectionValue( 0, 0 );
+ m_pMenu[ MENU_VEHICLES ]->SetMenuItemEnabled( 0, isActive, true );
+
+ // turn off selected icons
+ //
+ rAssert( m_characterSelectedIcon != NULL );
+ m_characterSelectedIcon->SetAlpha( 0.0f );
+
+ rAssert( m_vehicleSelectedIcon != NULL );
+ m_vehicleSelectedIcon->SetAlpha( 0.0f );
+
+ // set controller ID for vehicle selection menu
+ //
+ m_pMenu[ MENU_VEHICLES ]->SetControllerID( controllerID );
+
+ // reset current sub-menu
+ //
+ m_currentSubMenu = 0;
+
+
+}
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMiniMenu::CGuiScreenMiniMenu
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMiniMenu::CGuiScreenMiniMenu
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_MINI_MENU ),
+// m_screenState( SCREEN_STATE_NORMAL ),
+ m_pTrackMenu( NULL ),
+ m_trackDirection( NULL ),
+ m_trackNumLaps( NULL ),
+ m_trackNumLapsArrowU( NULL ),
+ m_trackNumLapsArrowD( NULL ),
+ m_trackCursorBgd( NULL ),
+ m_elapsedTime( 0 ),
+ m_backLabel( NULL ),
+ m_numActivePlayers( 0 ),
+ m_numActivePlayersDone( 0 ),
+ m_characterSlots( 0 ),
+ m_numUnlockedVehicles( 0 ),
+ m_optionsButton( NULL ),
+#ifdef RAD_WIN32
+ m_currentTrack(0),
+ m_bTrackSelected(false),
+#endif
+// m_optionsOverlay( NULL ),
+// m_optionsMenu( NULL ),
+ m_characterSelectInfo( NULL ),
+ m_timerOverlay( NULL ),
+ m_remainingTime( TIMER_WAIT_TIME )
+{
+ memset( m_unlockedVehicles, 0, sizeof( m_unlockedVehicles ) );
+
+#ifdef MINI_MENU_SHOW_3D_CHARACTERS
+ memset( m_3dCharacters, 0, sizeof( m_3dCharacters ) );
+#endif
+
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "MiniMenu" );
+ rAssert( pPage != NULL );
+
+ int i = 0;
+ char name[ 32 ];
+ Scrooby::Group* pGroup = NULL;
+
+ // create sub menu for tracks (only one needed for all players)
+ //
+ m_pTrackMenu = new CGuiMenu2D( this, NUM_TRACKS, NUM_TRACKS, GUI_SPRITE_MENU, MENU_SFX_NONE );
+ rAssert( m_pTrackMenu != NULL );
+
+ // add tracks to menu
+ //
+ pGroup = pPage->GetGroup( "Tracks" );
+ rAssert( pGroup != NULL );
+ for( i = 0; i < NUM_TRACKS; i++ )
+ {
+ sprintf( name, "Track%d", i );
+ m_pTrackMenu->AddMenuItem( pGroup->GetSprite( name ) );
+ }
+
+ if( this->IsWideScreenDisplay() )
+ {
+ ApplyWideScreenCorrectionScale( pGroup );
+ }
+
+ // add track cursor
+ //
+ Scrooby::Group* pGroupCursor = pGroup->GetGroup( "TrackCursor" );
+ rAssert( pGroupCursor != NULL );
+ m_pTrackMenu->SetCursor( pGroupCursor );
+
+ // get track cursor background
+ //
+ m_trackCursorBgd = pGroup->GetPolygon( "TrackBgd" );
+
+ // get track num laps and direction
+ //
+ m_trackNumLaps = pGroupCursor->GetText( "NumLaps" );
+ m_trackDirection = pGroupCursor->GetSprite( "Direction" );
+
+ rAssert( m_trackNumLaps != NULL );
+ m_trackNumLaps->SetIndex( SuperSprintData::DEFAULT_NUM_LAPS );
+
+ // rotate num laps arrows (90 deg CW) so they point up and down, respectively
+ //
+ m_trackNumLapsArrowU = pGroupCursor->GetSprite( "NumLaps_LArrow" );
+ if( m_trackNumLapsArrowU != NULL )
+ {
+ m_trackNumLapsArrowU->ResetTransformation();
+ m_trackNumLapsArrowU->ScaleAboutCenter( NUM_LAPS_ARROW_SCALE );
+ m_trackNumLapsArrowU->RotateAboutCenter( NUM_LAPS_ARROW_ROTATION );
+ }
+
+ m_trackNumLapsArrowD = pGroupCursor->GetSprite( "NumLaps_RArrow" );
+ if( m_trackNumLapsArrowD != NULL )
+ {
+ m_trackNumLapsArrowD->ResetTransformation();
+ m_trackNumLapsArrowD->ScaleAboutCenter( NUM_LAPS_ARROW_SCALE );
+ m_trackNumLapsArrowD->RotateAboutCenter( NUM_LAPS_ARROW_ROTATION );
+ }
+
+ // prepare vehicle selections (only unlocked vehicles are selectable by players)
+ //
+ this->PrepareVehicleSelections();
+
+ // if no vehicles are unlocked, just add the L1 default vehicle so there's at least one vehicle
+ // to choose from
+ //
+ if( m_numUnlockedVehicles == 0 )
+ {
+ m_unlockedVehicles[ 0 ] = GetRewardsManager()->GetReward( RenderEnums::L1, Reward::eDefaultCar );
+ m_numUnlockedVehicles++;
+ }
+
+ rAssertMsg( m_numUnlockedVehicles > 0, "There aren't any unlocked vehicles to choose from!" );
+
+ // get back label
+ //
+ rAssert( m_buttonIcons[ BUTTON_ICON_BACK ] != NULL );
+ m_backLabel = m_buttonIcons[ BUTTON_ICON_BACK ]->GetText( "Back" );
+ rAssert( m_backLabel != NULL );
+
+ // create player-specific menus
+ //
+ for( int p = 0; p < SuperSprintData::NUM_PLAYERS; p++ )
+ {
+ // create sub menu for characters
+ //
+ m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ] = new CGuiMenu2D( this,
+ NUM_CHARACTERS,
+ NUM_CHARACTERS,
+ GUI_SPRITE_MENU,
+ MENU_SFX_NONE );
+
+ rAssert( m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ] != NULL );
+ m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ]->SetGreyOutEnabled( false );
+
+ // add character slots to menu
+ //
+ pGroup = pPage->GetGroup( "Characters" );
+ rAssert( pGroup != NULL );
+ for( i = 0; i < NUM_CHARACTERS; i++ )
+ {
+ sprintf( name, "Character%d", i );
+ Scrooby::Sprite* characterImage = pGroup->GetSprite( name );
+#ifdef RAD_WIN32
+ characterImage->ResetTransformation();
+ characterImage->ScaleAboutCenter( CHARACTER_SCALE );
+#endif
+ m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ]->AddMenuItem( characterImage,
+ characterImage );
+ }
+
+ // get player cursor
+ //
+ sprintf( name, "Player%d", p );
+ pGroupCursor = pGroup->GetGroup( name );
+ rAssert( pGroupCursor != NULL );
+ m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ]->SetCursor( pGroupCursor );
+
+ // set player-specific colour to cursor
+ //
+ pGroupCursor->GetText( name )->SetColour( SuperSprintData::PLAYER_COLOURS[ p ] );
+
+ // get character selected icon
+ //
+ sprintf( name, "Player%d_Selected", p );
+ m_playerMenus[ p ].m_characterSelectedIcon = pGroupCursor->GetSprite( name );
+
+ // create sub menu for vehicles
+ //
+ m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_VEHICLES ] = new CGuiMenu( this,
+ 1,
+ GUI_SPRITE_MENU,
+ MENU_SFX_NONE );
+
+ rAssert( m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_VEHICLES ] != NULL );
+ m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_VEHICLES ]->SetHighlightColour( false, tColour( 0, 0, 0 ) );
+// m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_VEHICLES ]->SetSelectionMadeOutlineColour( tColour( 0, 0, 0, 192 ) );
+
+ // add vehicle selections to menu
+ //
+ pGroup = pPage->GetGroup( "Vehicles" );
+ rAssert( pGroup != NULL );
+
+ // get press start text
+ //
+ sprintf( name, "PressStart%d", p );
+ m_playerMenus[ p ].m_pressStart = pGroup->GetText( name );
+ rAssert( m_playerMenus[ p ].m_pressStart != NULL );
+ m_playerMenus[ p ].m_pressStart->SetTextMode( Scrooby::TEXT_WRAP );
+ m_playerMenus[ p ].m_pressStart->ScaleAboutCenter( 0.9f );
+ m_playerMenus[ p ].m_pressStart->SetVisible( false ); // hide by default
+
+ sprintf( name, "Vehicle%d_Value", p );
+ Scrooby::Sprite* vehicleImage = pGroup->GetSprite( name );
+ sprintf( name, "Vehicle%d_LArrow", p );
+ Scrooby::Sprite* vehicleImageLArrow = pGroup->GetSprite( name );
+ sprintf( name, "Vehicle%d_RArrow", p );
+ Scrooby::Sprite* vehicleImageRArrow = pGroup->GetSprite( name );
+
+#ifdef RAD_WIN32
+ vehicleImage->ScaleAboutCenter( VEHICLE_SCALE );
+ vehicleImageLArrow->ResetTransformation();
+ vehicleImageRArrow->ResetTransformation();
+ vehicleImageLArrow->ScaleAboutCenter( VEHICLE_ARROW_SCALE );
+ vehicleImageRArrow->ScaleAboutCenter( VEHICLE_ARROW_SCALE );
+#endif
+
+ m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_VEHICLES ]->AddMenuItem( vehicleImage,
+ vehicleImage,
+ NULL,
+ NULL,
+ vehicleImageLArrow,
+ vehicleImageRArrow );
+
+ m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_VEHICLES ]->SetSelectionValueCount( 0, m_numUnlockedVehicles );
+
+ // get vehicle cursor
+ //
+ sprintf( name, "Vehicle%d", p );
+ pGroupCursor = pGroup->GetGroup( name );
+ rAssert( pGroupCursor != NULL );
+ m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_VEHICLES ]->SetCursor( pGroupCursor );
+
+ // set player-specific colour to cursor
+ //
+ pGroupCursor->GetText( name )->SetColour( SuperSprintData::PLAYER_COLOURS[ p ] );
+
+ // get vehicle selected icon
+ //
+ sprintf( name, "Vehicle%d_Selected", p );
+ m_playerMenus[ p ].m_vehicleSelectedIcon = pGroupCursor->GetSprite( name );
+
+ // get vehicle rating
+ //
+ sprintf( name, "Vehicle%d_Rating", p );
+ m_playerMenus[ p ].m_vehicleRating = pGroup->GetSprite( name );
+
+#ifdef RAD_WIN32
+ m_playerMenus[ p ].m_vehicleRating->ScaleAboutCenter( 0.5f );
+#endif
+
+ this->UpdateVehicleDisplayImages( p, vehicleImage );
+ }
+
+ // get 3D character models
+ //
+#ifdef MINI_MENU_SHOW_3D_CHARACTERS
+ pGroup = pPage->GetGroup( "Characters" );
+ rAssert( pGroup != NULL );
+ for( i = 0; i < NUM_CHARACTERS; i++ )
+ {
+ sprintf( name, "Character%d", i );
+ m_3dCharacters[ i ] = pGroup->GetPure3dObject( name );
+ rAssert( m_3dCharacters[ i ] != NULL );
+ m_3dCharacters[ i ]->SetClearDepthBuffer( true );
+ }
+#endif // MINI_MENU_SHOW_3D_CHARACTERS
+
+ // get options overlay stuff
+ //
+ m_optionsButton = pPage->GetGroup( "Options" );
+ rAssert( m_optionsButton != NULL );
+
+ Scrooby::Text* optionsText = m_optionsButton->GetText( "Options" );
+ if( optionsText != NULL )
+ {
+ optionsText->SetTextMode( Scrooby::TEXT_WRAP );
+ }
+
+ if( this->IsWideScreenDisplay() )
+ {
+ ApplyWideScreenCorrectionScale( m_optionsButton );
+ }
+/*
+ m_optionsOverlay = pPage->GetLayer( "Options" );
+ rAssert( m_optionsOverlay != NULL );
+
+ // create options menu
+ //
+ m_optionsMenu = new CGuiMenu( this, NUM_OPTIONS_MENU_ITEMS );
+ rAssert( m_optionsMenu != NULL );
+
+ // add menu items to options menu
+ //
+ pGroup = pPage->GetGroup( "OptionsMenu" );
+ rAssert( pGroup != NULL );
+
+ m_optionsMenu->AddMenuItem( pGroup->GetText( "NumLaps" ),
+ pGroup->GetText( "NumLaps_Value" ),
+ NULL,
+ NULL,
+ pGroup->GetSprite( "NumLaps_ArrowL" ),
+ pGroup->GetSprite( "NumLaps_ArrowR" ),
+ SELECTION_ENABLED | TEXT_OUTLINE_ENABLED );
+
+ m_optionsMenu->AddMenuItem( pGroup->GetText( "ReverseDirection" ),
+ pGroup->GetText( "ReverseDirection_Value" ),
+ NULL,
+ NULL,
+ pGroup->GetSprite( "ReverseDirection_ArrowL" ),
+ pGroup->GetSprite( "ReverseDirection_ArrowR" ),
+ SELECTION_ENABLED | VALUES_WRAPPED | TEXT_OUTLINE_ENABLED );
+*/
+
+ // get character select info
+ //
+ m_characterSelectInfo = pPage->GetGroup( "CharacterSelectInfo" );
+ if( m_characterSelectInfo != NULL )
+ {
+ m_characterSelectInfo->SetVisible( false ); // hide by default
+ }
+
+#ifdef RAD_WIN32
+ Scrooby::Sprite* arrow = m_characterSelectInfo->GetSprite( "CharacterLArrow" );
+ arrow->ResetTransformation();
+ arrow->ScaleAboutCenter( CHARACTER_ARROW_SCALE );
+ arrow = m_characterSelectInfo->GetSprite( "CharacterRArrow" );
+ arrow->ResetTransformation();
+ arrow->ScaleAboutCenter( CHARACTER_ARROW_SCALE );
+#endif
+
+ // get timer overlay stuff
+ //
+ m_timerOverlay = pPage->GetGroup( "Timer" );
+ rAssert( m_timerOverlay != NULL );
+ m_timerOverlay->SetVisible( false ); // hide by default
+
+ Scrooby::Text* waitingForOtherPlayers = m_timerOverlay->GetText( "WaitingForOtherPlayers" );
+ if( waitingForOtherPlayers != NULL )
+ {
+ waitingForOtherPlayers->SetTextMode( Scrooby::TEXT_WRAP );
+ }
+
+ m_timer.SetScroobyText( m_timerOverlay, "Timer" );
+ m_timer.m_digits[ 0 ]->ResetTransformation();
+ m_timer.m_digits[ 0 ]->ScaleAboutPoint( 1.5f, 0, 0 );
+}
+
+//===========================================================================
+// CGuiScreenMiniMenu::~CGuiScreenMiniMenu
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMiniMenu::~CGuiScreenMiniMenu()
+{
+#ifdef MINI_MENU_SHOW_3D_CHARACTERS
+ // release drawables for 3D characters
+ //
+ for( int c = 0; c < NUM_CHARACTERS; c++ )
+ {
+ if( m_3dCharacters[ c ] != NULL )
+ {
+ m_3dCharacters[ c ]->SetDrawable( NULL );
+ }
+ }
+#endif // MINI_MENU_SHOW_3D_CHARACTERS
+
+ // destroy track menu
+ //
+ if( m_pTrackMenu != NULL )
+ {
+ delete m_pTrackMenu;
+ m_pTrackMenu = NULL;
+ }
+
+ // destroy all player sub-menus
+ //
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+ for( int j = 0; j < PlayerMenu::NUM_SUB_MENUS; j++ )
+ {
+ if( m_playerMenus[ i ].m_pMenu[ j ] != NULL )
+ {
+ delete m_playerMenus[ i ].m_pMenu[ j ];
+ m_playerMenus[ i ].m_pMenu[ j ] = NULL;
+ }
+ }
+ }
+
+/*
+ // destroy options menu
+ //
+ if( m_optionsMenu != NULL )
+ {
+ delete m_optionsMenu;
+ m_optionsMenu = NULL;
+ }
+*/
+}
+
+//===========================================================================
+// CGuiScreenMiniMenu::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniMenu::HandleMessage( eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2 )
+{
+ if( message == GUI_MSG_MENU_PROMPT_RESPONSE )
+ {
+ rAssert( param1 == PROMPT_CONFIRM_QUIT );
+
+ switch( param2 )
+ {
+ case (CGuiMenuPrompt::RESPONSE_YES):
+ {
+ m_pParent->HandleMessage( GUI_MSG_QUIT_MINIGAME );
+
+ break;
+ }
+ case (CGuiMenuPrompt::RESPONSE_NO):
+ {
+ this->ReloadScreen();
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( 0, "WARNING: *** Invalid prompt response!\n" );
+
+ break;
+ }
+ }
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ int controllerID = -1;
+ int playerMenuID = -1;
+
+ if( this->IsControllerMessage( message ) )
+ {
+ controllerID = static_cast<int>( param1 );
+ playerMenuID = this->GetPlayerMenuID( controllerID );
+
+ this->UpdateCharacterSlots();
+ }
+
+ if( m_numActivePlayers == 0 )
+ {
+ // relay message to track menu
+ //
+ rAssert( m_pTrackMenu != NULL );
+ m_pTrackMenu->HandleMessage( message, param1, param2 );
+ }
+ else
+ {
+ if( this->IsControllerMessage( message ) )
+ {
+ // relay message ONLY to associated player's current sub-menu
+ //
+ if( playerMenuID != -1 )
+ {
+ m_playerMenus[ playerMenuID ].HandleMessage( message, param1, param2 );
+
+ if( m_playerMenus[ playerMenuID ].m_currentSubMenu == PlayerMenu::MENU_VEHICLES )
+ {
+ this->UpdateVehicleRating( playerMenuID );
+ }
+ }
+ }
+ else
+ {
+ // relay message to all players' current sub-menu
+ //
+ for( int p = 0; p < SuperSprintData::NUM_PLAYERS; p++ )
+ {
+ m_playerMenus[ p ].HandleMessage( message, param1, param2 );
+ }
+ }
+ }
+
+ switch( message )
+ {
+ case GUI_MSG_UPDATE:
+ {
+ this->UpdateCharacterSlots();
+
+ const unsigned int PULSE_PERIOD = 500;
+
+ float alpha = GuiSFX::Pulse( (float)m_elapsedTime,
+ (float)PULSE_PERIOD,
+ 0.6f,
+ 0.4f,
+ -rmt::PI_BY2 );
+
+ if( m_numActivePlayers == 0 ) // track selection state
+ {
+ // pulse cursor bgd alpha
+ //
+ if( m_trackCursorBgd != NULL )
+ {
+ m_trackCursorBgd->SetAlpha( alpha );
+ }
+
+ // update U/D arrows on num laps
+ //
+ if( m_trackNumLapsArrowU != NULL && m_trackNumLapsArrowD != NULL )
+ {
+ m_trackNumLapsArrowU->SetIndex( 0 );
+ m_trackNumLapsArrowD->SetIndex( 0 );
+
+ int numUserInputHandlers = GetGuiSystem()->GetNumUserInputHandlers();
+ for( int i = 0; i < numUserInputHandlers; i++ )
+ {
+ CGuiUserInputHandler* userInputHandler = GetGuiSystem()->GetUserInputHandler( i );
+ if( userInputHandler != NULL )
+ {
+#ifdef RAD_WIN32
+ if( userInputHandler->IsYAxisOnUp() ||
+ GetInputManager()->GetFEMouse()->LeftButtonDownOn() == HOTSPOT_ARROWUP )
+#else
+ if( userInputHandler->IsButtonDown( GuiInput::Up ) ||
+ userInputHandler->IsYAxisOnUp() )
+#endif
+ {
+ rAssert( m_trackNumLapsArrowU->GetNumOfImages() > 1 );
+ m_trackNumLapsArrowU->SetIndex( 1 );
+ }
+
+#ifdef RAD_WIN32
+ if( userInputHandler->IsYAxisOnDown() ||
+ GetInputManager()->GetFEMouse()->LeftButtonDownOn() == HOTSPOT_ARROWDOWN )
+#else
+ if( userInputHandler->IsButtonDown( GuiInput::Down ) ||
+ userInputHandler->IsYAxisOnDown() )
+#endif
+ {
+ rAssert( m_trackNumLapsArrowD->GetNumOfImages() > 1 );
+ m_trackNumLapsArrowD->SetIndex( 1 );
+ }
+ }
+ }
+ }
+ }
+ else // not in track selection state
+ {
+#ifdef RAD_XBOX
+ // re-enable Start-to-Select button mapping
+ //
+ for( int i = 0; i < GetGuiSystem()->GetNumUserInputHandlers(); i++ )
+ {
+ GetGuiSystem()->GetUserInputHandler( i )->EnableStartToSelectMapping( true );
+ }
+#endif // RAD_XBOX
+
+ for( int p = 0; p < SuperSprintData::NUM_PLAYERS; p++ )
+ {
+ // pulse player's current sub-menu cursor
+ //
+ CGuiMenu* currentSubMenu = m_playerMenus[ p ].GetCurrentSubMenu();
+ if( currentSubMenu != NULL )
+ {
+ Scrooby::Drawable* pCursor = currentSubMenu->GetCursor();
+ rAssert( pCursor != NULL );
+ pCursor->SetAlpha( alpha );
+ }
+ }
+ }
+
+ m_elapsedTime += param1;
+ m_elapsedTime %= PULSE_PERIOD;
+
+ rAssert( m_timerOverlay != NULL );
+ if( m_timerOverlay->IsVisible() )
+ {
+ // update timer
+ //
+ m_remainingTime -= static_cast<int>( param1 );
+ if( m_remainingTime > 0 )
+
+ {
+ m_timer.SetValue( static_cast<unsigned int>( m_remainingTime ) / 1000 );
+ }
+ else
+ {
+ this->SetTimerEnabled( false );
+
+ // timer expired, letz start the mini-game!
+ //
+ m_pParent->HandleMessage( GUI_MSG_QUIT_MINIGAME, m_numActivePlayersDone );
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_MENU_SELECTION_CHANGED: // param1 = old, param2 = new
+ {
+ if( m_numActivePlayers == 0 ) // track selection state
+ {
+ this->MoveTrackCursor( static_cast<int>( param2 ), static_cast<int>( param1 ) );
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_UP:
+ {
+ if( m_numActivePlayers == 0 ) // track selection state
+ {
+ // increment number of laps
+ //
+ rAssert( m_trackNumLaps != NULL );
+ int currentNumLaps = m_trackNumLaps->GetIndex();
+ if( currentNumLaps < SuperSprintData::MAX_NUM_LAPS )
+ {
+ currentNumLaps++;
+ m_trackNumLaps->SetIndex( currentNumLaps );
+
+ if( currentNumLaps == SuperSprintData::MAX_NUM_LAPS &&
+ m_trackNumLapsArrowU != NULL )
+ {
+ m_trackNumLapsArrowU->SetAlpha( NUM_LAPS_ARROW_GREY_OUT_ALPHA ); // grey out arrow
+ }
+
+ if( m_trackNumLapsArrowD != NULL )
+ {
+ m_trackNumLapsArrowD->SetAlpha( 1.0f ); // restore normal arrow
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_DOWN:
+ {
+ if( m_numActivePlayers == 0 ) // track selection state
+ {
+ // decrement number of laps
+ //
+ rAssert( m_trackNumLaps != NULL );
+ int currentNumLaps = m_trackNumLaps->GetIndex();
+ if( currentNumLaps > SuperSprintData::MIN_NUM_LAPS )
+ {
+ currentNumLaps--;
+ m_trackNumLaps->SetIndex( currentNumLaps );
+
+ if( currentNumLaps == SuperSprintData::MIN_NUM_LAPS &&
+ m_trackNumLapsArrowD != NULL )
+ {
+ m_trackNumLapsArrowD->SetAlpha( NUM_LAPS_ARROW_GREY_OUT_ALPHA ); // grey out arrow
+ }
+
+ if( m_trackNumLapsArrowU != NULL )
+ {
+ m_trackNumLapsArrowU->SetAlpha( 1.0f ); // restore normal arrow
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_START:
+ {
+ if( m_numActivePlayers == 0 )
+ {
+ this->OnTrackSelected();
+ }
+
+ // active new player menu
+ //
+ if( playerMenuID == -1 ) // means this player's not active yet
+ {
+#ifdef RAD_XBOX
+ // disable Start-to-Select button mapping to prevent duplicate SELECT inputs;
+ // it will be re-enabled in the next update
+ //
+ GetGuiSystem()->GetUserInputHandler( controllerID )->EnableStartToSelectMapping( false );
+#endif // RAD_XBOX
+
+ this->ActivateNewPlayer( controllerID );
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_SELECT );
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ if( m_numActivePlayers == 0 )
+ {
+ this->OnTrackSelected();
+
+ // track selected, activate the first player
+ //
+ this->ActivateNewPlayer( controllerID );
+ }
+ else
+ {
+ if( playerMenuID != -1 && !m_playerMenus[ playerMenuID ].IsDone() )
+ {
+ Scrooby::Drawable* pCursor = m_playerMenus[ playerMenuID ].GetCurrentSubMenu()->GetCursor();
+ rAssert( pCursor != NULL );
+ pCursor->SetAlpha( 1.0f ); // restore cursor alpha
+
+ m_playerMenus[ playerMenuID ].m_currentSubMenu++;
+
+ if( m_playerMenus[ playerMenuID ].IsDone() )
+ {
+ // player is done selecting from all sub-menus
+ //
+ m_numActivePlayersDone++;
+
+ this->SetTimerEnabled( true );
+
+ this->OnVehicleSelected( playerMenuID, true );
+ }
+ else
+ {
+ this->OnCharacterSelected( playerMenuID, true );
+ }
+
+ // if everyone's done selecting, start the game!
+ //
+ if( m_numActivePlayersDone == SuperSprintData::NUM_PLAYERS )
+ {
+ this->SetTimerEnabled( false );
+
+ m_pParent->HandleMessage( GUI_MSG_QUIT_MINIGAME, m_numActivePlayersDone );
+ }
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+ if( m_numActivePlayers == 0 )
+ {
+ // display quit confirmation screen
+ //
+ m_guiManager->DisplayPrompt( PROMPT_CONFIRM_QUIT, this );
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_BACK );
+ }
+ else
+ {
+ if( playerMenuID != -1 )
+ {
+ if( m_playerMenus[ playerMenuID ].m_currentSubMenu > 0 )
+ {
+ if( !m_playerMenus[ playerMenuID ].IsDone() )
+ {
+ Scrooby::Drawable* pCursor = m_playerMenus[ playerMenuID ].GetCurrentSubMenu()->GetCursor();
+ rAssert( pCursor != NULL );
+ pCursor->SetAlpha( 1.0f ); // restore cursor alpha
+
+ this->OnCharacterSelected( playerMenuID, false );
+ }
+ else
+ {
+ this->OnVehicleSelected( playerMenuID, false );
+
+ m_numActivePlayersDone--;
+
+ // if no one's done selecting all their sub-menus, disable timer
+ //
+ if( m_numActivePlayersDone == 0 )
+ {
+ this->SetTimerEnabled( false );
+ }
+ }
+
+ m_playerMenus[ playerMenuID ].m_currentSubMenu--;
+ }
+ else
+ {
+ // de-activate player menu
+ //
+ m_playerMenus[ playerMenuID ].SetActive( false );
+ GetInputManager()->UnregisterControllerID( playerMenuID );
+
+ m_numActivePlayers--;
+
+ if( m_numActivePlayers == 0 )
+ {
+ // re-enable track selection
+ //
+ this->SetTrackSelectionEnabled( true );
+ }
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_BACK );
+ }
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_AUX_X:
+ {
+ if( m_numActivePlayers == 0 ) // track selection state
+ {
+ // toggle track direction
+ //
+ rAssert( m_trackDirection != NULL );
+ m_trackDirection->SetIndex( 1 - m_trackDirection->GetIndex() );
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_UPORDOWN );
+/*
+ // show options overlay
+ //
+ rAssert( m_optionsOverlay != NULL );
+ m_optionsOverlay->SetVisible( true );
+
+ // hide buttons
+ //
+ rAssert( m_optionsButton != NULL );
+ m_optionsButton->SetVisible( false );
+ this->SetButtonVisible( BUTTON_ICON_ACCEPT, false );
+
+ // reset options menu
+ //
+ rAssert( m_optionsMenu != NULL );
+ m_optionsMenu->Reset();
+
+ m_screenState = SCREEN_STATE_OPTIONS;
+*/
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+#ifdef RAD_WIN32
+//===========================================================================
+// CGuiScreenMiniMenu::CheckCursorAgainstHotspots
+//===========================================================================
+// Description: Checks cursor position against its list of hotspots.
+//
+// Constraints: None.
+//
+// Parameters: float x - The x position of cursor in P3D coordinates.
+// float y - The y position of cursor in P3D coordinates.
+//
+// Return: N/A.
+//
+//===========================================================================
+eFEHotspotType CGuiScreenMiniMenu::CheckCursorAgainstHotspots( float x, float y )
+{
+ if( m_bTrackSelected ) return HOTSPOT_NONE;
+ eFEHotspotType hotSpotType = HOTSPOT_NONE;
+ CGuiMenu* pCurrentMenu = HasMenu();
+ int numMenuItems = 0;
+ GuiMenuItem* pMenuItem = NULL;
+
+
+ if( pCurrentMenu )
+ {
+ numMenuItems = pCurrentMenu->GetNumItems();
+ for( int i = 0; i < numMenuItems; i++ )
+ {
+ pMenuItem = pCurrentMenu->GetMenuItem( i );
+
+ if( pMenuItem )
+ {
+ if( pMenuItem->GetItem()->IsVisible() )
+ {
+ // Just tests if the point is in the bounding rect of the sprite.
+ if( pMenuItem->GetItem()->IsPointInBoundingRect( x, y ) )
+ {
+ //rDebugPrintf( "Cursor is inside Sprite %d rectangle!\n", i );
+ pCurrentMenu->HandleMessage( GUI_MSG_MOUSE_OVER, i );
+ m_currentTrack = i;
+ hotSpotType = HOTSPOT_BUTTON;
+
+ //After taking care of all the events for this menu item, just bail out.
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if( hotSpotType == HOTSPOT_NONE )
+ {
+ // Since there's only one set of up and down arrows, when they get translated,
+ // their bounding box info doesn't change.. so I just offset it depending on
+ // their current selection.
+ float xOffset = 0.25f * m_currentTrack;
+ x -= xOffset;
+
+ if( m_trackNumLapsArrowU )
+ {
+ if( m_trackNumLapsArrowU->IsPointInBoundingRect( x, y ) )
+ {
+ hotSpotType = HOTSPOT_ARROWUP;
+ }
+ }
+ if( m_trackNumLapsArrowD )
+ {
+ if( m_trackNumLapsArrowD->IsPointInBoundingRect( x, y ) )
+ {
+ hotSpotType = HOTSPOT_ARROWDOWN;
+ }
+ }
+ }
+
+ return hotSpotType;
+}
+#endif
+
+//===========================================================================
+// CGuiScreenMiniMenu::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniMenu::InitIntro()
+{
+ // disable track(s) that are not unlocked
+ //
+ rAssert( m_pTrackMenu != NULL );
+ if( !CommandLineOptions::Get( CLO_SKIP_FE ) )
+ {
+ for( int i = 0; i < NUM_TRACKS; i++ )
+ {
+ bool isTrackUnlocked = GetCardGallery()->IsCardDeckComplete( static_cast<unsigned int>( i ) );
+ m_pTrackMenu->SetMenuItemEnabled( i, isTrackUnlocked );
+ }
+ }
+
+ rAssertMsg( m_pTrackMenu->GetSelection() != -1, "Why isn't there at least one track enabled??" );
+
+ if( m_firstTimeEntered )
+ {
+ this->MoveTrackCursor( 0, m_pTrackMenu->GetSelection() );
+ }
+
+ // set default track direction
+ //
+ rAssert( m_trackDirection != NULL );
+ m_trackDirection->SetIndex( GetSSM()->IsTrackReversed() ? 0 : 1 );
+
+ // de-activate all player menus
+ //
+ for( int p = 0; p < SuperSprintData::NUM_PLAYERS; p++ )
+ {
+ m_playerMenus[ p ].SetActive( false );
+ GetInputManager()->UnregisterControllerID( p );
+ }
+
+ // restore all normal character bitmaps
+ //
+ for( int j = 0; j < NUM_CHARACTERS; j++ )
+ {
+ rAssert( m_playerMenus[ 0 ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ] != NULL );
+ m_playerMenus[ 0 ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ]->SetSelectionValue( j, 0 );
+ }
+
+ m_numActivePlayers = 0;
+ m_numActivePlayersDone = 0;
+
+ this->SetTrackSelectionEnabled( true );
+}
+
+//===========================================================================
+// CGuiScreenMiniMenu::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniMenu::InitRunning()
+{
+#ifdef MINI_MENU_SHOW_3D_CHARACTERS
+ // set drawables for 3D characters
+ //
+ tDrawable* pDrawable = NULL;
+
+ pDrawable = p3d::find<tDrawable>( "homer_h" );
+ if( pDrawable != NULL )
+ {
+ m_3dCharacters[ CHARACTER_HOMER ]->SetDrawable( pDrawable );
+ }
+
+ pDrawable = p3d::find<tDrawable>( "bart_h" );
+ if( pDrawable != NULL )
+ {
+ m_3dCharacters[ CHARACTER_BART ]->SetDrawable( pDrawable );
+ }
+
+ pDrawable = p3d::find<tDrawable>( "lisa_h" );
+ if( pDrawable != NULL )
+ {
+ m_3dCharacters[ CHARACTER_LISA ]->SetDrawable( pDrawable );
+ }
+
+ pDrawable = p3d::find<tDrawable>( "marge_h" );
+ if( pDrawable != NULL )
+ {
+ m_3dCharacters[ CHARACTER_MARGE ]->SetDrawable( pDrawable );
+ }
+
+ pDrawable = p3d::find<tDrawable>( "apu_h" );
+ if( pDrawable != NULL )
+ {
+ m_3dCharacters[ CHARACTER_APU ]->SetDrawable( pDrawable );
+ }
+
+ // adjust camera aspect ratio to match viewport dimensions
+ //
+ for( int i = 0; i < NUM_CHARACTERS; i++ )
+ {
+ rAssert( m_3dCharacters[ i ] != NULL );
+
+ int width = 0;
+ int height = 0;
+ m_3dCharacters[ i ]->GetBoundingBoxSize( width, height );
+
+ tCamera* pCamera = m_3dCharacters[ i ]->GetCamera();
+ rAssert( pCamera != NULL );
+ pCamera->SetFOV( pCamera->GetFieldOfView(), (float)width / (float)height );
+
+ // show 3D characters
+ //
+ m_3dCharacters[ i ]->SetVisible( false ); // true );
+ }
+#endif // MINI_MENU_SHOW_3D_CHARACTERS
+}
+
+//===========================================================================
+// CGuiScreenMiniMenu::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniMenu::InitOutro()
+{
+#ifdef MINI_MENU_SHOW_3D_CHARACTERS
+ // hide 3D characters
+ //
+ for( int i = 0; i < NUM_CHARACTERS; i++ )
+ {
+ rAssert( m_3dCharacters[ i ] != NULL );
+ m_3dCharacters[ i ]->SetVisible( false );
+ }
+#endif // MINI_MENU_SHOW_3D_CHARACTERS
+
+ // setup supersprint stuff
+ //
+ for( int p = 0; p < SuperSprintData::NUM_PLAYERS; p++ )
+ {
+ if( m_playerMenus[ p ].IsActive() )
+ {
+
+ int character = m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ]->GetSelection();
+ GetSSM()->SetCharacter( p, character );
+
+ int vehicle = m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_VEHICLES ]->GetSelectionValue( 0 );
+ rAssert( m_unlockedVehicles[ vehicle ] != NULL );
+ GetSSM()->SetVehicle( p, m_unlockedVehicles[ vehicle ]->GetName() );
+ }
+ }
+}
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenMiniMenu::UpdateCharacterSlots()
+{
+ // update character slots availability
+ //
+ int p = 0;
+
+ m_characterSlots = 0;
+ for( p = 0; p < SuperSprintData::NUM_PLAYERS; p++ )
+ {
+ if( m_playerMenus[ p ].IsActive() )
+ {
+ int currentSelection = m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ]->GetSelection();
+ m_characterSlots |= (1 << currentSelection );
+ }
+ }
+
+ // enable/disable character selections for all player menus
+ //
+ for( p = 0; p < SuperSprintData::NUM_PLAYERS; p++ )
+ {
+ if( m_playerMenus[ p ].IsActive() )
+ {
+ for( int i = 0; i < NUM_CHARACTERS; i++ )
+ {
+ int currentSelection = m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ]->GetSelection();
+ if( i != currentSelection )
+ {
+ bool isEnabled = ( ((1 << i) & m_characterSlots) == 0 );
+ m_playerMenus[ p ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ]->SetMenuItemEnabled( i, isEnabled );
+ }
+ }
+ }
+ }
+}
+
+void
+CGuiScreenMiniMenu::SetTrackSelectionEnabled( bool enable )
+{
+ // show/hide options button
+ //
+ rAssert( m_optionsButton != NULL );
+ m_optionsButton->SetVisible( enable );
+
+ // set back label to "quit" if enabled
+ //
+ rAssert( m_backLabel != NULL );
+ m_backLabel->SetIndex( enable ? 1 : 0 );
+
+ // hide/show all players' "press start"
+ //
+ for( int p = 0; p < SuperSprintData::NUM_PLAYERS; p++ )
+ {
+ rAssert( m_playerMenus[ p ].m_pressStart != NULL );
+ m_playerMenus[ p ].m_pressStart->SetVisible( !enable );
+ }
+
+ // hide/show character select info
+ //
+ if( m_characterSelectInfo != NULL )
+ {
+ m_characterSelectInfo->SetVisible( !enable );
+ }
+#ifdef RAD_WIN32
+ m_bTrackSelected = !enable;
+#endif
+}
+
+void
+CGuiScreenMiniMenu::MoveTrackCursor( int previousIndex, int nextIndex )
+{
+ // if cursor exists
+ //
+ if( m_trackCursorBgd != NULL )
+ {
+ Scrooby::BoundedDrawable* item = NULL;
+ int x1, y1, x2, y2;
+
+ rAssert( m_pTrackMenu != NULL );
+
+ // get location of previous item
+ item = m_pTrackMenu->GetMenuItem( previousIndex )->GetItem();
+ rAssert( item != NULL );
+ item->GetOriginPosition( x1, y1 );
+
+ // get location of next item
+ item = m_pTrackMenu->GetMenuItem( nextIndex )->GetItem();
+ rAssert( item != NULL );
+ item->GetOriginPosition( x2, y2 );
+
+ // translate cursor
+ m_trackCursorBgd->Translate( x2 - x1, y2 - y1 );
+ }
+}
+
+void
+CGuiScreenMiniMenu::OnTrackSelected()
+{
+ this->SetTrackSelectionEnabled( false );
+
+ // highlight track cursor bgd
+ //
+ if( m_trackCursorBgd != NULL )
+ {
+ m_trackCursorBgd->SetAlpha( 1.0f );
+ }
+
+ // restore U/D num laps arrows
+ //
+ if( m_trackNumLapsArrowU != NULL && m_trackNumLapsArrowD != NULL )
+ {
+ m_trackNumLapsArrowU->SetIndex( 0 );
+ m_trackNumLapsArrowD->SetIndex( 0 );
+ }
+
+ // get current track selection
+ //
+ rAssert( m_pTrackMenu != NULL );
+ int currentTrack = m_pTrackMenu->GetSelection();
+
+ // disable selection made delay
+ //
+ m_pTrackMenu->MakeSelection( false );
+
+ // tell gameplay manager which track to load
+ //
+ RenderEnums::LevelEnum levelIndex = static_cast<RenderEnums::LevelEnum>( RenderEnums::B01 + currentTrack );
+ GetGameplayManager()->SetLevelIndex( levelIndex );
+
+ // set track options
+ //
+ rAssert( m_trackNumLaps != NULL );
+ GetSSM()->SetNumLaps( m_trackNumLaps->GetIndex() );
+
+ rAssert( m_trackDirection != NULL );
+ GetSSM()->SetTrackDirection( m_trackDirection->GetIndex() == 0 );
+}
+
+void
+CGuiScreenMiniMenu::ActivateNewPlayer( int controllerID )
+{
+ if( m_numActivePlayers >= SuperSprintData::NUM_PLAYERS )
+ {
+ // can't activate anymore new players
+ //
+ return;
+ }
+
+ int player_slot = 0;
+#ifdef RAD_XBOX
+ player_slot = controllerID;
+#elif defined ( RAD_WIN32 )
+ player_slot = controllerID;
+#else
+ // look for non empty slot
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+ // find first non-active menu to activate
+ //
+ if( !m_playerMenus[ i ].IsActive() )
+ {
+ player_slot = i;
+ break;
+ }
+ }
+#endif
+
+ // ok, found it!
+ //
+ m_playerMenus[ player_slot ].SetActive( true, controllerID );
+ GetInputManager()->RegisterControllerID( player_slot , controllerID);
+
+ m_numActivePlayers++;
+
+ // default character selection to first empty slot
+ //
+ for( int s = 0; s < NUM_CHARACTERS; s++ )
+ {
+ if( ((1 << s) & m_characterSlots) == 0 )
+ {
+ m_playerMenus[ player_slot ].GetCurrentSubMenu()->Reset( s );
+
+ break;
+ }
+ }
+
+ // update vehicle rating
+ //
+ this->UpdateVehicleRating( player_slot );
+}
+
+void
+CGuiScreenMiniMenu::OnCharacterSelected( int playerID, bool isSelected )
+{
+ rAssert( playerID != -1 );
+ rAssert( m_playerMenus[ playerID ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ] != NULL );
+
+ // show/hide character selection done indicator
+ //
+ rAssert( m_playerMenus[ playerID ].m_characterSelectedIcon != NULL );
+ m_playerMenus[ playerID ].m_characterSelectedIcon->SetAlpha( isSelected ? 1.0f : 0.0f );
+
+ // swap selected character bitmap
+ //
+ int characterIndex = m_playerMenus[ playerID ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ]->GetSelection();
+ m_playerMenus[ playerID ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ]->SetSelectionValue( characterIndex, isSelected ? CHARACTER_SELECTED : CHARACTER_NOT_SELECTED );
+
+ // disable selection made delay
+ //
+ if( isSelected )
+ {
+ m_playerMenus[ playerID ].m_pMenu[ PlayerMenu::MENU_CHARACTERS ]->MakeSelection( false );
+ }
+}
+
+void
+CGuiScreenMiniMenu::OnVehicleSelected( int playerID, bool isSelected )
+{
+ rAssert( playerID != -1 );
+ rAssert( m_playerMenus[ playerID ].m_pMenu[ PlayerMenu::MENU_VEHICLES ] != NULL );
+
+ // show/hide vehicle selection done indicator
+ //
+ rAssert( m_playerMenus[ playerID ].m_vehicleSelectedIcon != NULL );
+ m_playerMenus[ playerID ].m_vehicleSelectedIcon->SetAlpha( isSelected ? 1.0f : 0.0f );
+
+ // disable selection made delay
+ //
+ if( isSelected )
+ {
+ m_playerMenus[ playerID ].m_pMenu[ PlayerMenu::MENU_VEHICLES ]->MakeSelection( false );
+ }
+}
+
+void
+CGuiScreenMiniMenu::AddRewardVehicle( Reward* pReward )
+{
+ rAssert( pReward != NULL );
+ if( pReward->RewardStatus() || // if reward is unlocked
+ CommandLineOptions::Get( CLO_SKIP_FE ) ||
+// GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_CARDS ) ||
+ GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_UNLOCK_VEHICLES ) )
+ {
+ rAssert( m_numUnlockedVehicles < MAX_NUM_VEHICLES );
+
+ m_unlockedVehicles[ m_numUnlockedVehicles ] = pReward;
+ m_numUnlockedVehicles++;
+ }
+}
+
+void
+CGuiScreenMiniMenu::PrepareVehicleSelections()
+{
+ for( int level = 0; level < RenderEnums::numLevels; level++ )
+ {
+ Reward* pReward = NULL;
+
+ for( int i = Reward::eBlank + 1; i < Reward::NUM_QUESTS; i++ )
+ {
+ pReward = GetRewardsManager()->GetReward( level, static_cast<Reward::eQuestType>( i ) );
+ if( pReward != NULL )
+ {
+ if( pReward->GetRewardType() == Reward::ALT_PLAYERCAR )
+ {
+ this->AddRewardVehicle( pReward );
+ }
+ }
+ }
+
+ for( pReward = GetRewardsManager()->FindFirstMerchandise( level, Merchandise::SELLER_GIL );
+ pReward != NULL;
+ pReward = GetRewardsManager()->FindNextMerchandise( level, Merchandise::SELLER_GIL ) )
+ {
+ this->AddRewardVehicle( pReward );
+ }
+
+ for( pReward = GetRewardsManager()->FindFirstMerchandise( level, Merchandise::SELLER_SIMPSON );
+ pReward != NULL;
+ pReward = GetRewardsManager()->FindNextMerchandise( level, Merchandise::SELLER_SIMPSON ) )
+ {
+ this->AddRewardVehicle( pReward );
+ }
+ }
+}
+
+void
+CGuiScreenMiniMenu::UpdateVehicleDisplayImages( int playerID, Scrooby::Sprite* vehicleImage )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ rAssert( vehicleImage != NULL );
+ for( int i = 0; i < m_numUnlockedVehicles; i++ )
+ {
+ rAssert( m_unlockedVehicles[ i ] != NULL );
+ vehicleImage->SetImage( i, m_unlockedVehicles[ i ]->GetName() );
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+}
+
+void
+CGuiScreenMiniMenu::UpdateVehicleRating( int playerID )
+{
+ if( m_playerMenus[ playerID ].m_vehicleRating != NULL )
+ {
+ int vehicleIndex = m_playerMenus[ playerID ].m_pMenu[ PlayerMenu::MENU_VEHICLES ]->GetSelectionValue( 0 );
+
+ rAssert( m_unlockedVehicles[ vehicleIndex ] != NULL );
+ CarAttributeRecord* carStats = GetRewardsManager()->GetCarAttributeRecord( m_unlockedVehicles[ vehicleIndex ]->GetName() );
+ if( carStats != NULL )
+ {
+ int rating = (int)( GetRewardsManager()->ComputeOverallCarRating( carStats ) );
+
+ rAssertMsg( rating >= 0 && rating < m_playerMenus[ playerID ].m_vehicleRating->GetNumOfImages(),
+ "Invalid vehicle rating (either negative or too high)!" );
+
+ m_playerMenus[ playerID ].m_vehicleRating->SetIndex( rating );
+ }
+ }
+}
+
+void
+CGuiScreenMiniMenu::SetTimerEnabled( bool enable )
+{
+ // show/hide timer overlay
+ //
+ rAssert( m_timerOverlay != NULL );
+ m_timerOverlay->SetVisible( enable );
+
+ if( !enable )
+ {
+ // reset timer
+ //
+ m_remainingTime = TIMER_WAIT_TIME;
+ }
+}
+
+int
+CGuiScreenMiniMenu::GetPlayerMenuID( int controllerID ) const
+{
+ // search for player menu w/ associated controller ID
+ //
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+ if( m_playerMenus[ i ].m_controllerID == controllerID )
+ {
+ // found it! return player menu ID
+ //
+ return i;
+ }
+ }
+
+ // player menu not found, return -1
+ //
+ return -1;
+}
+
diff --git a/game/code/presentation/gui/minigame/guiscreenminimenu.h b/game/code/presentation/gui/minigame/guiscreenminimenu.h
new file mode 100644
index 0000000..356233a
--- /dev/null
+++ b/game/code/presentation/gui/minigame/guiscreenminimenu.h
@@ -0,0 +1,210 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMiniMenu
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/24 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMINIMENU_H
+#define GUISCREENMINIMENU_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+#include <presentation/gui/utility/numerictext.h>
+
+#include <supersprint/supersprintdata.h>
+
+//#define MINI_MENU_SHOW_3D_CHARACTERS
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CGuiMenu;
+class Reward;
+
+struct PlayerMenu
+{
+ enum eSubMenu
+ {
+ MENU_CHARACTERS,
+ MENU_VEHICLES,
+
+ NUM_SUB_MENUS
+ };
+
+ CGuiMenu* m_pMenu[ NUM_SUB_MENUS ];
+ int m_currentSubMenu;
+ int m_controllerID;
+
+ Scrooby::Text* m_pressStart;
+ Scrooby::Sprite* m_vehicleRating;
+ Scrooby::Drawable* m_characterSelectedIcon;
+ Scrooby::Drawable* m_vehicleSelectedIcon;
+
+ PlayerMenu();
+
+ void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ CGuiMenu* GetCurrentSubMenu() const;
+ void SetActive( bool isActive, int controllerID = -1 );
+ bool IsActive() const;
+ bool IsDone() const;
+
+};
+
+inline bool PlayerMenu::IsActive() const
+{
+ return( m_controllerID != -1 );
+}
+
+inline bool PlayerMenu::IsDone() const
+{
+ return( m_currentSubMenu >= NUM_SUB_MENUS );
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMiniMenu : public CGuiScreen
+{
+public:
+ CGuiScreenMiniMenu( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMiniMenu();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+ virtual CGuiMenu* HasMenu() { return m_pTrackMenu; }
+#ifdef RAD_WIN32
+ virtual eFEHotspotType CheckCursorAgainstHotspots( float x, float y );
+#endif
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void UpdateCharacterSlots();
+
+ void SetTrackSelectionEnabled( bool enable );
+ void MoveTrackCursor( int previousIndex, int nextIndex );
+ void OnTrackSelected();
+
+ void ActivateNewPlayer( int controllerID );
+
+ void OnCharacterSelected( int playerID, bool isSelected );
+ void OnVehicleSelected( int playerID, bool isSelected );
+
+ void AddRewardVehicle( Reward* pReward );
+ void PrepareVehicleSelections();
+ void UpdateVehicleDisplayImages( int playerID, Scrooby::Sprite* vehicleImage );
+ void UpdateVehicleRating( int playerID );
+
+ void SetTimerEnabled( bool enable );
+
+ int GetPlayerMenuID( int controllerID ) const;
+
+ static const int NUM_TRACKS = 7;
+ static const int MAX_NUM_VEHICLES = 64;
+
+#ifdef RAD_DEBUG
+ static const int TIMER_WAIT_TIME = 5000; // in msec
+#else
+ static const int TIMER_WAIT_TIME = 10000; // in msec
+#endif
+
+ enum eScreenState
+ {
+ SCREEN_STATE_NORMAL,
+ SCREEN_STATE_OPTIONS,
+
+ NUM_SCREEN_STATES
+ };
+
+ enum eCharacter
+ {
+ CHARACTER_LISA,
+ CHARACTER_BART,
+ CHARACTER_HOMER,
+ CHARACTER_MARGE,
+ CHARACTER_APU,
+
+ NUM_CHARACTERS
+ };
+
+ enum eCharacterSelectionState
+ {
+ CHARACTER_NOT_SELECTED = 0,
+ CHARACTER_SELECTED = 1,
+
+ NUM_CHARACTER_SELECTION_STATES
+ };
+
+/*
+ enum eOptionsMenuItem
+ {
+ MENU_ITEM_NUM_LAPS,
+ MENU_ITEM_REVERSE_DIRECTION,
+
+ NUM_OPTIONS_MENU_ITEMS
+ };
+
+ eScreenState m_screenState;
+*/
+
+ CGuiMenu* m_pTrackMenu;
+ Scrooby::Sprite* m_trackDirection;
+ Scrooby::Text* m_trackNumLaps;
+ Scrooby::Sprite* m_trackNumLapsArrowU;
+ Scrooby::Sprite* m_trackNumLapsArrowD;
+ Scrooby::Polygon* m_trackCursorBgd;
+ unsigned int m_elapsedTime;
+
+ Scrooby::Text* m_backLabel;
+
+ PlayerMenu m_playerMenus[ SuperSprintData::NUM_PLAYERS ];
+ int m_numActivePlayers;
+ int m_numActivePlayersDone;
+ short m_characterSlots; // bit-mask
+
+#ifdef MINI_MENU_SHOW_3D_CHARACTERS
+ Scrooby::Pure3dObject* m_3dCharacters[ NUM_CHARACTERS ];
+#endif
+
+#ifdef RAD_WIN32
+ int m_currentTrack;
+ bool m_bTrackSelected;
+#endif
+
+ Reward* m_unlockedVehicles[ MAX_NUM_VEHICLES ];
+ int m_numUnlockedVehicles;
+
+ Scrooby::Group* m_optionsButton;
+/*
+ Scrooby::Layer* m_optionsOverlay;
+ CGuiMenu* m_optionsMenu;
+*/
+
+ Scrooby::Group* m_characterSelectInfo;
+
+ Scrooby::Group* m_timerOverlay;
+ NumericText<Scrooby::Text> m_timer;
+ int m_remainingTime;
+
+};
+
+#endif // GUISCREENMINIMENU_H
diff --git a/game/code/presentation/gui/minigame/guiscreenminipause.cpp b/game/code/presentation/gui/minigame/guiscreenminipause.cpp
new file mode 100644
index 0000000..db37e0c
--- /dev/null
+++ b/game/code/presentation/gui/minigame/guiscreenminipause.cpp
@@ -0,0 +1,297 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMiniPause
+//
+// Description: Implementation of the CGuiScreenMiniPause class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/24 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/minigame/guiscreenminipause.h>
+#include <presentation/gui/minigame/guiscreenminihud.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guimenu.h>
+
+#include <contexts/supersprint/supersprintcontext.h>
+#include <gameflow/gameflow.h>
+#include <input/inputmanager.h>
+#include <sound/soundmanager.h>
+
+#include <screen.h>
+#include <page.h>
+#include <group.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMiniPause::CGuiScreenMiniPause
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMiniPause::CGuiScreenMiniPause
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_MINI_PAUSE ),
+ m_pMenu( NULL )
+{
+ // Retrieve the Scrooby drawing elements (from MiniPause page).
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "MiniPause" );
+ rAssert( pPage != NULL );
+
+ // create menu
+ //
+ m_pMenu = new CGuiMenu( this, NUM_MENU_ITEMS );
+ rAssert( m_pMenu != NULL );
+
+ Scrooby::Group* pGroup = pPage->GetGroup( "Menu" );
+ rAssert( pGroup != NULL );
+ m_pMenu->AddMenuItem( pGroup->GetText( "Continue" ) );
+ m_pMenu->AddMenuItem( pGroup->GetText( "Quit" ) );
+
+ // Retrieve the Scrooby drawing elements (from PauseFgd page).
+ //
+ pPage = m_pScroobyScreen->GetPage( "PauseFgd" );
+ if( pPage != NULL )
+ {
+ // Wrap "Press Start" help text
+ //
+ Scrooby::Text* pressStart = pPage->GetText( "PressStartResumePlay" );
+ if( pressStart != NULL )
+ {
+ pressStart->SetTextMode( Scrooby::TEXT_WRAP );
+
+ // add text outline
+ //
+ pressStart->SetDisplayOutline( true );
+
+ // set platform-specific text
+ //
+ pressStart->SetIndex( PLATFORM_TEXT_INDEX );
+ }
+ }
+
+ this->AutoScaleFrame( m_pScroobyScreen->GetPage( "XSmallBoard" ) );
+
+ this->SetZoomingEnabled( true );
+}
+
+
+//===========================================================================
+// CGuiScreenMiniPause::~CGuiScreenMiniPause
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMiniPause::~CGuiScreenMiniPause()
+{
+ if( m_pMenu != NULL )
+ {
+ delete m_pMenu;
+ m_pMenu = NULL;
+ }
+}
+
+
+//===========================================================================
+// CGuiScreenMiniPause::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniPause::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ if( this->IsControllerMessage( message ) &&
+ static_cast<int>( param1 ) != CGuiScreenMiniHud::s_pausedControllerID )
+ {
+ // ignore controller messages if not from user who paused mini-game
+ //
+ return;
+ }
+
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_START:
+ {
+ if( !m_pMenu->HasSelectionBeenMade() )
+ {
+ this->ResumeGame();
+ }
+
+ break;
+ }
+ case GUI_MSG_CONTROLLER_BACK:
+ {
+#ifdef RAD_WIN32
+ this->ResumeGame();
+ break;
+#else
+ // don't allow user to back out of pause menu
+ //
+ return;
+#endif
+ }
+ case GUI_MSG_MENU_SELECTION_MADE:
+ {
+ if( param1 == MENU_ITEM_CONTINUE )
+ {
+ this->ResumeGame();
+ }
+ else if( param1 == MENU_ITEM_QUIT )
+ {
+ this->QuitGame();
+ }
+ else
+ {
+ rAssertMsg( false, "Invalid menu selection!" );
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ // relay message to menu
+ //
+ rAssert( m_pMenu != NULL );
+ m_pMenu->HandleMessage( message, param1, param2 );
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenMiniPause::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniPause::InitIntro()
+{
+ rAssert( m_pMenu != NULL );
+ m_pMenu->Reset(); // default to 'Continue'
+
+ GetSoundManager()->OnPauseStart();
+
+#ifdef RAD_WIN32
+ GetInputManager()->GetFEMouse()->SetInGameMode( false );
+#endif
+}
+
+
+//===========================================================================
+// CGuiScreenMiniPause::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniPause::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenMiniPause::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniPause::InitOutro()
+{
+ GetSoundManager()->OnPauseEnd();
+
+#ifdef RAD_WIN32
+ GetInputManager()->GetFEMouse()->SetInGameMode( true );
+#endif
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenMiniPause::ResumeGame()
+{
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN, GUI_SCREEN_ID_MINI_HUD, CLEAR_WINDOW_HISTORY );
+}
+
+void
+CGuiScreenMiniPause::QuitGame()
+{
+ // quit mini-game and return to mini-game FE
+ //
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ GUI_SCREEN_ID_MINI_MENU,
+ CLEAR_WINDOW_HISTORY );
+
+ GetGameFlow()->SetContext( CONTEXT_SUPERSPRINT_FE );
+}
+
diff --git a/game/code/presentation/gui/minigame/guiscreenminipause.h b/game/code/presentation/gui/minigame/guiscreenminipause.h
new file mode 100644
index 0000000..543ab36
--- /dev/null
+++ b/game/code/presentation/gui/minigame/guiscreenminipause.h
@@ -0,0 +1,66 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMiniPause
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/24 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMINIPAUSE_H
+#define GUISCREENMINIPAUSE_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class CGuiMenu;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMiniPause : public CGuiScreen
+{
+public:
+ CGuiScreenMiniPause( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMiniPause();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+ virtual CGuiMenu* HasMenu() { return m_pMenu; }
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void ResumeGame();
+ void QuitGame();
+
+ enum eMenuItem
+ {
+ MENU_ITEM_CONTINUE,
+ MENU_ITEM_QUIT,
+
+ NUM_MENU_ITEMS
+ };
+
+ CGuiMenu* m_pMenu;
+
+};
+
+#endif // GUISCREENMINIPAUSE_H
diff --git a/game/code/presentation/gui/minigame/guiscreenminisummary.cpp b/game/code/presentation/gui/minigame/guiscreenminisummary.cpp
new file mode 100644
index 0000000..4841d93
--- /dev/null
+++ b/game/code/presentation/gui/minigame/guiscreenminisummary.cpp
@@ -0,0 +1,539 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMiniSummary
+//
+// Description: Implementation of the CGuiScreenMiniSummary class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/24 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/minigame/guiscreenminisummary.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guiscreenprompt.h>
+#include <presentation/gui/guitextbible.h>
+
+#include <contexts/supersprint/supersprintcontext.h>
+#include <events/eventmanager.h>
+#include <gameflow/gameflow.h>
+#include <input/inputmanager.h>
+#include <sound/soundmanager.h>
+
+#include <screen.h>
+#include <page.h>
+#include <group.h>
+#include <sprite.h>
+#include <text.h>
+#include <polygon.h>
+
+#include <supersprint/supersprintmanager.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+const float MINI_SUMMARY_CHARACTER_SCALE_BIG = 0.9f;
+const float MINI_SUMMARY_CHARACTER_SCALE_SMALL = 0.7f;
+const float MINI_SUMMARY_KING_ICON_SCALE = 0.75f;
+
+void
+CGuiScreenMiniSummary::PlayerDisplayInfo::SetVisible( bool isVisible )
+{
+ rAssert( m_face != NULL );
+ m_face->SetVisible( isVisible );
+ rAssert( m_ranking != NULL );
+ m_ranking->SetVisible( isVisible );
+ rAssert( m_numWins != NULL );
+ m_numWins->SetVisible( isVisible );
+ rAssert( m_numPoints != NULL );
+ m_numPoints->SetVisible( isVisible );
+ rAssert( m_totalTime != NULL );
+ m_totalTime->SetVisible( isVisible );
+ rAssert( m_bestLap != NULL );
+ m_bestLap->SetVisible( isVisible );
+ rAssert( m_rowBgd != NULL );
+ m_rowBgd->SetVisible( isVisible );
+}
+
+int
+CGuiScreenMiniSummary::PlayerRanking::CompareScores( int points1, int wins1,
+ int points2, int wins2 )
+{
+ if( points1 > points2 )
+ {
+ return 1;
+ }
+ else if( points1 == points2 )
+ {
+ if( wins1 > wins2 )
+ {
+ return 1;
+ }
+ else if( wins1 == wins2 )
+ {
+ return 0;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+ else
+ {
+ return -1;
+ }
+}
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CGuiScreenMiniSummary::CGuiScreenMiniSummary
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMiniSummary::CGuiScreenMiniSummary
+(
+ Scrooby::Screen* pScreen,
+ CGuiEntity* pParent
+)
+: CGuiScreen( pScreen, pParent, GUI_SCREEN_ID_MINI_SUMMARY ),
+ m_totalTimeKingIcon( NULL ),
+ m_bestLapKingIcon( NULL )
+{
+ // Retrieve the Scrooby drawing elements.
+ //
+ Scrooby::Page* pPage = m_pScroobyScreen->GetPage( "MiniSummary" );
+ rAssert( pPage != NULL );
+
+ Scrooby::Text* raceSummary = pPage->GetText( "RaceSummary" );
+ if( raceSummary != NULL )
+ {
+ raceSummary->SetTextMode( Scrooby::TEXT_WRAP );
+ }
+
+ Scrooby::Group* pGroupFaces = pPage->GetGroup( "Faces" );
+ rAssert( pGroupFaces != NULL );
+ Scrooby::Group* pGroupRanks = pPage->GetGroup( "Ranks" );
+ rAssert( pGroupRanks != NULL );
+ Scrooby::Group* pGroupPlayers = pPage->GetGroup( "Players" );
+ rAssert( pGroupPlayers != NULL );
+ Scrooby::Group* pGroupWins = pPage->GetGroup( "Wins" );
+ rAssert( pGroupWins != NULL );
+ Scrooby::Group* pGroupPoints = pPage->GetGroup( "Points" );
+ rAssert( pGroupPoints != NULL );
+ Scrooby::Group* pGroupTotalTime = pPage->GetGroup( "TotalTime" );
+ rAssert( pGroupTotalTime != NULL );
+ Scrooby::Group* pGroupBestLap = pPage->GetGroup( "BestLap" );
+ rAssert( pGroupBestLap != NULL );
+ Scrooby::Group* pGroupRows = pPage->GetGroup( "Rows" );
+ rAssert( pGroupRows != NULL );
+
+ char name[ 32 ];
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+ sprintf( name, "Face%d", i );
+ m_displayInfo[ i ].m_face = pGroupFaces->GetSprite( name );
+
+ sprintf( name, "Rank%d", i );
+ m_displayInfo[ i ].m_ranking = pGroupRanks->GetText( name );
+
+ sprintf( name, "Player%d", i );
+ m_displayInfo[ i ].m_playerIndicator = pGroupPlayers->GetText( name );
+
+ sprintf( name, "NumWins%d", i );
+ m_displayInfo[ i ].m_numWins = pGroupWins->GetText( name );
+
+ sprintf( name, "NumPoints%d", i );
+ m_displayInfo[ i ].m_numPoints = pGroupPoints->GetText( name );
+
+ sprintf( name, "TotalTime%d", i );
+ m_displayInfo[ i ].m_totalTime = pGroupTotalTime->GetText( name );
+
+ sprintf( name, "BestLap%d", i );
+ m_displayInfo[ i ].m_bestLap = pGroupBestLap->GetText( name );
+
+ sprintf( name, "Row%d", i );
+ m_displayInfo[ i ].m_rowBgd = pGroupRows->GetPolygon( name );
+ }
+
+ // text wrap "Total Time" and "Best Lap"
+ //
+ Scrooby::Text* pText = pGroupTotalTime->GetText( "TotalTime" );
+ rAssert( pText != NULL );
+ pText->SetTextMode( Scrooby::TEXT_WRAP );
+
+ pText = pGroupBestLap->GetText( "BestLap" );
+ rAssert( pText != NULL );
+ pText->SetTextMode( Scrooby::TEXT_WRAP );
+
+ // get king icons
+ //
+ m_totalTimeKingIcon = pGroupTotalTime->GetSprite( "TotalTimeKingIcon" );
+ m_bestLapKingIcon = pGroupBestLap->GetSprite( "BestLapKingIcon" );
+
+#ifdef RAD_WIN32
+ m_totalTimeKingIcon->ScaleAboutCenter( 0.5f );
+ m_bestLapKingIcon->ScaleAboutCenter( 0.5f );
+ pGroupRanks->GetSprite( "RankKingIcon" )->ScaleAboutCenter( 0.5f );
+#endif
+
+ // TC: [TEMP] hide these for now until I have time to implement the
+ // functionality
+ //
+ rAssert( m_totalTimeKingIcon != NULL );
+ m_totalTimeKingIcon->SetVisible( false );
+ rAssert( m_bestLapKingIcon != NULL );
+ m_bestLapKingIcon->SetVisible( false );
+
+ this->AutoScaleFrame( m_pScroobyScreen->GetPage( "BigBoard" ) );
+}
+
+
+//===========================================================================
+// CGuiScreenMiniSummary::~CGuiScreenMiniSummary
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CGuiScreenMiniSummary::~CGuiScreenMiniSummary()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenMiniSummary::HandleMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniSummary::HandleMessage
+(
+ eGuiMessage message,
+ unsigned int param1,
+ unsigned int param2
+)
+{
+ if( message == GUI_MSG_MENU_PROMPT_RESPONSE )
+ {
+ rAssert( param1 == PROMPT_CONFIRM_RACE_AGAIN );
+
+ switch( param2 )
+ {
+ case (CGuiMenuPrompt::RESPONSE_YES):
+ {
+ // ok, letz start another race
+ //
+ GetGameFlow()->GetContext( CONTEXT_SUPERSPRINT )->Resume();
+
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ GUI_SCREEN_ID_MINI_HUD,
+ CLEAR_WINDOW_HISTORY );
+
+ GetSSM()->Reset();
+
+ GetSoundManager()->RestartSupersprintMusic();
+
+ break;
+ }
+ case (CGuiMenuPrompt::RESPONSE_NO):
+ {
+ // quit mini-game and return to mini-game FE
+ //
+ m_pParent->HandleMessage( GUI_MSG_GOTO_SCREEN,
+ GUI_SCREEN_ID_MINI_MENU,
+ CLEAR_WINDOW_HISTORY );
+
+ GetGameFlow()->SetContext( CONTEXT_SUPERSPRINT_FE );
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( 0, "WARNING: *** Invalid prompt response!\n" );
+
+ break;
+ }
+ }
+
+ GetSoundManager()->OnStoreScreenEnd();
+ }
+
+ if( m_state == GUI_WINDOW_STATE_RUNNING )
+ {
+ switch( message )
+ {
+ case GUI_MSG_CONTROLLER_SELECT:
+ {
+ // display 'race again' prompt
+ //
+ m_guiManager->DisplayPrompt( PROMPT_CONFIRM_RACE_AGAIN,
+ this,
+ PROMPT_TYPE_YES_NO,
+ false );
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_SELECT );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // Propogate the message up the hierarchy.
+ //
+ CGuiScreen::HandleMessage( message, param1, param2 );
+}
+
+
+//===========================================================================
+// CGuiScreenMiniSummary::InitIntro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniSummary::InitIntro()
+{
+ this->ResetCurrentRankings();
+
+ // rank players by points and wins
+ //
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+// if( GetSSM()->GetVehicleData( i )->mIsHuman )
+ {
+ this->InsertPlayerRanking( i );
+ }
+ }
+
+ // update display info
+ //
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+ this->UpdateDisplayInfo( i, m_currentRankings[ i ].m_playerID );
+ }
+
+ //
+ // This isn't a store, but it's a handy set of ducking values
+ //
+ GetSoundManager()->OnStoreScreenStart( false );
+}
+
+
+//===========================================================================
+// CGuiScreenMiniSummary::InitRunning
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniSummary::InitRunning()
+{
+}
+
+
+//===========================================================================
+// CGuiScreenMiniSummary::InitOutro
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+void CGuiScreenMiniSummary::InitOutro()
+{
+#ifdef RAD_WIN32
+ GetInputManager()->GetFEMouse()->SetInGameMode( false );
+#endif
+}
+
+
+//---------------------------------------------------------------------
+// Private Functions
+//---------------------------------------------------------------------
+
+void
+CGuiScreenMiniSummary::UpdateDisplayInfo( int rankIndex, int playerID )
+{
+ rAssert( rankIndex >= 0 && rankIndex < SuperSprintData::NUM_PLAYERS );
+
+ if( playerID != -1 )
+ {
+ m_displayInfo[ rankIndex ].SetVisible( true );
+
+ const SuperSprintData::PlayerData* playerData = GetSSM()->GetPlayerData( playerID );
+ rAssert( playerData != NULL );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_HUD );
+
+ // set character face
+ //
+ rAssert( m_displayInfo[ rankIndex ].m_face != NULL );
+ m_displayInfo[ rankIndex ].m_face->SetIndex( playerData->mCharacterIndex );
+ m_displayInfo[ rankIndex ].m_face->ResetTransformation();
+ if( rankIndex == 0 )
+ {
+ m_displayInfo[ rankIndex ].m_face->ScaleAboutCenter( MINI_SUMMARY_CHARACTER_SCALE_BIG );
+ }
+ else
+ {
+ m_displayInfo[ rankIndex ].m_face->ScaleAboutCenter( MINI_SUMMARY_CHARACTER_SCALE_SMALL );
+ }
+
+ // set ranking colour
+ //
+ rAssert( m_displayInfo[ rankIndex ].m_ranking != NULL );
+ m_displayInfo[ rankIndex ].m_ranking->SetColour( SuperSprintData::PLAYER_COLOURS[ playerID ] );
+
+ // set player ID indicator and colour
+ //
+ rAssert( m_displayInfo[ rankIndex ].m_playerIndicator != NULL );
+ if( GetSSM()->GetVehicleData( playerID )->mIsHuman )
+ {
+ m_displayInfo[ rankIndex ].m_playerIndicator->SetVisible( true );
+ m_displayInfo[ rankIndex ].m_playerIndicator->SetIndex( playerID );
+ m_displayInfo[ rankIndex ].m_playerIndicator->SetColour( SuperSprintData::PLAYER_COLOURS[ playerID ] );
+ }
+ else
+ {
+ m_displayInfo[ rankIndex ].m_playerIndicator->SetVisible( false );
+ }
+
+ char buffer[ 16 ];
+ UnicodeString unicodeString;
+
+ // set num wins
+ //
+ sprintf( buffer, "%d", playerData->mWins );
+ rAssert( m_displayInfo[ rankIndex ].m_numWins != NULL );
+ m_displayInfo[ rankIndex ].m_numWins->SetString( 0, buffer );
+
+ // set num points
+ //
+ sprintf( buffer, "%d", playerData->mPoints );
+ rAssert( m_displayInfo[ rankIndex ].m_numPoints != NULL );
+ m_displayInfo[ rankIndex ].m_numPoints->SetString( 0, buffer );
+
+ // set total time
+ //
+ if( playerData->mPosition > 0 )
+ {
+ sprintf( buffer, "%.2f", playerData->mRaceTime / 1000000.0f );
+ unicodeString.ReadAscii( buffer );
+ }
+ else
+ {
+ unicodeString.ReadUnicode( GetTextBibleString( "DNF" ) );
+ }
+ rAssert( m_displayInfo[ rankIndex ].m_totalTime != NULL );
+ m_displayInfo[ rankIndex ].m_totalTime->SetString( 0, unicodeString );
+
+ // set best lap
+ //
+ if( playerData->mRaceTime > 0 )
+ {
+ sprintf( buffer, "%.2f", playerData->mBestLap / 1000000.0f );
+ unicodeString.ReadAscii( buffer );
+ }
+ else
+ {
+ unicodeString.ReadUnicode( GetTextBibleString( "DNF" ) );
+ }
+ rAssert( m_displayInfo[ rankIndex ].m_bestLap != NULL );
+ m_displayInfo[ rankIndex ].m_bestLap->SetString( 0, unicodeString );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_HUD );
+ }
+ else
+ {
+ m_displayInfo[ rankIndex ].SetVisible( false );
+ }
+}
+
+void
+CGuiScreenMiniSummary::ResetCurrentRankings()
+{
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+ m_currentRankings[ i ].m_playerID = -1;
+ m_currentRankings[ i ].m_playerPoints = -1;
+ m_currentRankings[ i ].m_playerWins = -1;
+ }
+}
+
+void
+CGuiScreenMiniSummary::InsertPlayerRanking( int playerID )
+{
+ const SuperSprintData::PlayerData* playerData = GetSSM()->GetPlayerData( playerID );
+ rAssert( playerData != NULL );
+
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+ if( PlayerRanking::CompareScores( playerData->mPoints, playerData->mWins,
+ m_currentRankings[ i ].m_playerPoints, m_currentRankings[ i ].m_playerWins ) > 0 )
+ {
+ // move everyone else down a spot
+ //
+ for( int j = SuperSprintData::NUM_PLAYERS - 1; j > i; j-- )
+ {
+ m_currentRankings[ j ].m_playerID = m_currentRankings[ j - 1 ].m_playerID;
+ m_currentRankings[ j ].m_playerPoints = m_currentRankings[ j - 1 ].m_playerPoints;
+ m_currentRankings[ j ].m_playerWins = m_currentRankings[ j - 1 ].m_playerWins;
+ }
+
+ // insert here
+ //
+ m_currentRankings[ i ].m_playerID = playerID;
+ m_currentRankings[ i ].m_playerPoints = playerData->mPoints;
+ m_currentRankings[ i ].m_playerWins = playerData->mWins;
+
+ break;
+ }
+ }
+}
+
diff --git a/game/code/presentation/gui/minigame/guiscreenminisummary.h b/game/code/presentation/gui/minigame/guiscreenminisummary.h
new file mode 100644
index 0000000..231fcf9
--- /dev/null
+++ b/game/code/presentation/gui/minigame/guiscreenminisummary.h
@@ -0,0 +1,87 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CGuiScreenMiniSummary
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/24 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef GUISCREENMINISUMMARY_H
+#define GUISCREENMINISUMMARY_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <presentation/gui/guiscreen.h>
+
+#include <supersprint/supersprintdata.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CGuiScreenMiniSummary : public CGuiScreen
+{
+public:
+ CGuiScreenMiniSummary( Scrooby::Screen* pScreen, CGuiEntity* pParent );
+ virtual ~CGuiScreenMiniSummary();
+
+ virtual void HandleMessage( eGuiMessage message,
+ unsigned int param1 = 0,
+ unsigned int param2 = 0 );
+
+protected:
+ void InitIntro();
+ void InitRunning();
+ void InitOutro();
+
+private:
+ void UpdateDisplayInfo( int rankIndex, int playerID );
+
+ void ResetCurrentRankings();
+ void InsertPlayerRanking( int playerID );
+
+ struct PlayerDisplayInfo
+ {
+ Scrooby::Sprite* m_face;
+ Scrooby::Text* m_ranking;
+ Scrooby::Text* m_playerIndicator;
+ Scrooby::Text* m_numWins;
+ Scrooby::Text* m_numPoints;
+ Scrooby::Text* m_totalTime;
+ Scrooby::Text* m_bestLap;
+ Scrooby::Polygon* m_rowBgd;
+
+ void SetVisible( bool isVisible );
+ };
+
+ PlayerDisplayInfo m_displayInfo[ SuperSprintData::NUM_PLAYERS ];
+
+ struct PlayerRanking
+ {
+ int m_playerID;
+ int m_playerPoints;
+ int m_playerWins;
+
+ static int CompareScores( int points1, int wins1,
+ int points2, int wins2 );
+ };
+
+ PlayerRanking m_currentRankings[ SuperSprintData::NUM_PLAYERS ];
+
+ Scrooby::Sprite* m_totalTimeKingIcon;
+ Scrooby::Sprite* m_bestLapKingIcon;
+
+};
+
+#endif // GUISCREENMINISUMMARY_H
diff --git a/game/code/presentation/gui/utility/allutility.cpp b/game/code/presentation/gui/utility/allutility.cpp
new file mode 100644
index 0000000..f9cbf09
--- /dev/null
+++ b/game/code/presentation/gui/utility/allutility.cpp
@@ -0,0 +1,8 @@
+#include <presentation/gui/utility/hudmapcam.cpp>
+#include <presentation/gui/utility/hudmap.cpp>
+#include <presentation/gui/utility/numerictext.cpp>
+#include <presentation/gui/utility/slider.cpp>
+#include <presentation/gui/utility/specialfx.cpp>
+#include <presentation/gui/utility/teletypetext.cpp>
+#include <presentation/gui/utility/transitions.cpp>
+#include <presentation/gui/utility/scrollingtext.cpp>
diff --git a/game/code/presentation/gui/utility/colourutility.h b/game/code/presentation/gui/utility/colourutility.h
new file mode 100644
index 0000000..af8a24f
--- /dev/null
+++ b/game/code/presentation/gui/utility/colourutility.h
@@ -0,0 +1,63 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: this file contains some operators for colors *, +, -, etc
+//
+// History: 29/01/2003 + Created -- Ian Gipson
+//
+//=============================================================================
+
+#ifndef _COLOURUTILITY_H_
+#define _COLOURUTILITY_H_
+
+#include <p3d/p3dtypes.hpp>
+
+//==============================================================================
+// operator+
+//==============================================================================
+// Description: addition of two colors
+//
+// Parameters: a, b, the two colors to add
+//
+// Return: the added version of the two colors
+//
+//==============================================================================
+inline tColour operator+( const tColour a, const tColour b )
+{
+ int rr = a.Red() + b.Red();
+ int gg = a.Green() + b.Green();
+ int bb = a.Blue() + b.Blue();
+ int aa = a.Alpha() + b.Alpha();
+ return( tColour( rr, gg, bb, aa ) );
+}
+
+//==============================================================================
+// operator*
+//==============================================================================
+// Description: multiplication of a color by a constant
+//
+// Parameters: a, the color
+// f, the float
+//
+// Return: the scaled version of the colour
+//
+//==============================================================================
+inline tColour operator*( const tColour a, const float f )
+{
+ int rr = static_cast< int >( a.Red() * f );
+ int gg = static_cast< int >( a.Green() * f );
+ int bb = static_cast< int >( a.Blue() * f );
+ int aa = static_cast< int >( a.Alpha() * f );
+ return tColour( rr, gg, bb, aa );
+}
+
+inline tColour operator*( const float f, const tColour a )
+{
+ return operator*( a, f);
+}
+
+#endif
+
+
diff --git a/game/code/presentation/gui/utility/hudmap.cpp b/game/code/presentation/gui/utility/hudmap.cpp
new file mode 100644
index 0000000..dcd4a07
--- /dev/null
+++ b/game/code/presentation/gui/utility/hudmap.cpp
@@ -0,0 +1,1914 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CHudMap
+//
+// Description: Implementation of the CHudMap class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/01 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/utility/hudmap.h>
+#include <presentation/gui/utility/hudmapcam.h>
+#include <presentation/gui/utility/specialfx.h>
+#include <presentation/gui/guiscreen.h>
+
+#include <ai/vehicle/vehicleai.h>
+#include <interiors/interiormanager.h>
+#include <memory/srrmemory.h>
+#include <meta/zoneeventlocator.h>
+#include <mission/gameplaymanager.h>
+#include <render/intersectmanager/intersectmanager.h>
+#include <roads/geometry.h>
+#include <roads/road.h>
+#include <roads/intersection.h>
+#include <roads/roadmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+#include <worldsim/hitnrunmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+
+#include <p3d/utility.hpp>
+
+#include <raddebug.hpp> // Foundation
+
+#include <page.h>
+#include <group.h>
+#include <pure3dobject.h>
+#include <sprite.h>
+#include <polygon.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+#define SHOW_HUD_MAP
+#define CIRCULAR_HUD_MAP
+
+#ifdef CIRCULAR_HUD_MAP
+ #define SMARTER_ICON_LOCATIONS // only works for circular hud map
+#endif
+
+// This is for smooth camera height transitions, to prevent any sudden
+// camera cuts.
+//
+#define SMOOTH_CAMERA_HEIGHT_TRANSITIONS
+
+const int NUM_AMORTIZED_UPDATE_FRAMES = 5; // for DetermineOnRoadLocation()
+
+#ifdef DEBUGWATCH
+ #include <raddebugwatch.hpp>
+
+ static const char* WATCHER_NAMESPACE = "GUI System - HUD Map";
+
+ float MIN_CAMERA_HEIGHT = 50.0f;
+ float MAX_CAMERA_HEIGHT = 150.0f;
+
+ float MAX_CAMERA_HEIGHT_TRANSITION = 10.0f;
+
+ float CHASE_FLASH_DISTANCE_THRESHOLD = 0.5f;
+ float CHASE_FADE_DISTANCE_THRESHOLD = 0.75f;
+ float RADAR_VISIBLE_RADIUS = 150.0f; // in metres
+#else
+ const float MIN_CAMERA_HEIGHT = 50.0f;
+ const float MAX_CAMERA_HEIGHT = 150.0f;
+
+ const float MAX_CAMERA_HEIGHT_TRANSITION = 10.0f;
+
+ const float CHASE_FLASH_DISTANCE_THRESHOLD = 0.5f;
+ const float CHASE_FADE_DISTANCE_THRESHOLD = 0.75f;
+ const float RADAR_VISIBLE_RADIUS = 150.0f; // in metres
+#endif
+
+const float DEFAULT_CAMERA_HEIGHT = 150.0f; // in metres
+
+const float MAX_RADAR_CONE_ALPHA = 0.35f;
+
+enum eVisibilityMode
+{
+ VISIBLE_IN_SUNDAY_DRIVE = 1,
+ VISIBLE_IN_MISSION = 2,
+
+ NUM_VISIBILITY_MODES
+};
+
+const tColour HIT_N_RUN_COLOUR_RED( 255, 0, 0 );
+const tColour HIT_N_RUN_COLOUR_BLUE( 0, 0, 255 );
+
+HudMapIcon CHudMap::s_registeredIcons[ MAX_NUM_REGISTERED_ICONS ];
+int CHudMap::s_numRegisteredIcons = 0;
+int CHudMap::s_fpIconID = -1;
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+void
+HudMapIcon::ApplyAICarIconColour()
+{
+ rAssert( m_iconImage != NULL );
+
+ switch( m_type )
+ {
+ case ICON_AI_HIT_N_RUN:
+ {
+ m_iconImage->SetColour( HIT_N_RUN_COLOUR_RED );
+
+ break;
+ }
+ case ICON_AI_CHASE:
+ {
+ m_iconImage->SetColour( tColour( 255, 128, 0 ) );
+
+ break;
+ }
+ case ICON_AI_RACE:
+ {
+ m_iconImage->SetColour( tColour( 255, 255, 0 ) );
+
+ break;
+ }
+ case ICON_AI_EVADE:
+ {
+ m_iconImage->SetColour( tColour( 255, 255, 0 ) );
+
+ break;
+ }
+ case ICON_AI_TARGET:
+ {
+ m_iconImage->SetColour( tColour( 255, 0, 0 ) );
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Invalid AI car icon type!" );
+
+ break;
+ }
+ }
+}
+
+//===========================================================================
+// CHudMap::CHudMap
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CHudMap::CHudMap( Scrooby::Page* pPage, int playerID, const char* p3dFile )
+: m_playerID( playerID ),
+ m_p3dMap( NULL ),
+ m_p3dHole( NULL ),
+ m_originalPosX( 0 ),
+ m_originalPosY( 0 ),
+ m_originalWidth( 0 ),
+ m_originalHeight( 0 ),
+ m_isVisible( false ),
+ m_radar( NULL ),
+ m_radarCone( NULL ),
+ m_iconsGroup( NULL ),
+ m_hudMapCam( NULL ),
+ m_currentCameraHeight( DEFAULT_CAMERA_HEIGHT ),
+ m_fixedCameraHeight( 0.0f ),
+ m_currentAICarDistance( 0.0f ),
+ m_maxAICarDistance( 0.0f ),
+ m_elapsedTime( 0 ),
+ m_lastRoadSeg( NULL ),
+ m_lastOnRoadLocation( 0.0f, 0.0f, 0.0f ),
+ m_frameCount( -1 )
+{
+MEMTRACK_PUSH_GROUP( "CHudMap" );
+ memset( m_icons, 0, sizeof( m_icons ) );
+
+ m_hudMapCam = new HudMapCam( m_playerID );
+ rAssert( m_hudMapCam );
+ m_hudMapCam->AddRef();
+
+ m_currentCameraHeight = this->CalculateCameraHeight( RADAR_VISIBLE_RADIUS );
+
+ char name[ 16 ];
+ rAssert( pPage != NULL );
+
+ // get 3D HUD map
+ //
+ sprintf( name, "Map%d", playerID );
+ m_p3dMap = pPage->GetPure3dObject( name );
+ rAssert( m_p3dMap != NULL );
+ m_p3dMap->SetCamera( m_hudMapCam->GetCamera() );
+
+ // store original hud map position and size
+ //
+ m_p3dMap->GetOriginPosition( m_originalPosX, m_originalPosY );
+ m_p3dMap->GetBoundingBoxSize( m_originalWidth, m_originalHeight );
+
+#ifdef SHOW_HUD_MAP
+ if( p3dFile != NULL )
+ {
+ // set pure3d file for hud map
+ //
+ m_p3dMap->Add3dModel( p3dFile );
+ }
+
+ // get 3D HUD hole
+ //
+ sprintf( name, "Hole%d", playerID );
+ m_p3dHole = pPage->GetPure3dObject( name );
+ rAssert( m_p3dHole != NULL );
+ m_p3dHole->SetCamera( m_hudMapCam->GetCamera() );
+ m_p3dHole->SetColourWrite( false );
+
+ if( CGuiScreen::IsWideScreenDisplay() )
+ {
+ m_p3dMap->SetWideScreenCorrectionEnabled( true );
+ m_p3dHole->SetWideScreenCorrectionEnabled( true );
+ }
+#else
+ m_p3dMap->SetVisible( false );
+#endif
+
+/*
+ sprintf( name, "RadarLight%d", playerID );
+ m_radarLight = pPage->GetSprite( name );
+ rAssert( m_radarLight != NULL );
+
+ // hide radar light temporarily
+ //
+ m_radarLight->SetVisible( false );
+*/
+
+ // get radar & radar cone
+ //
+ sprintf( name, "Radar%d", playerID );
+ m_radar = pPage->GetGroup( name );
+ rAssert( m_radar != NULL );
+
+ sprintf( name, "RadarCone%d", playerID );
+ m_radarCone = pPage->GetGroup( name );
+ rAssert( m_radarCone != NULL );
+
+ Scrooby::Polygon* radarConePolygon = m_radarCone->GetPolygon( name );
+ rAssert( radarConePolygon != NULL );
+ radarConePolygon->SetAlpha( MAX_RADAR_CONE_ALPHA );
+
+ // get icons group
+ //
+ sprintf( name, "Icons%d", playerID );
+ m_iconsGroup = pPage->GetGroup( name );
+ rAssert( m_iconsGroup != NULL );
+
+#ifdef DEBUGWATCH
+ ::radDbgWatchAddFloat( &MIN_CAMERA_HEIGHT,
+ "Mininum Camera Height",
+ WATCHER_NAMESPACE,
+ NULL, NULL, 30.0f, 90.0f );
+
+ ::radDbgWatchAddFloat( &MAX_CAMERA_HEIGHT,
+ "Maximum Camera Height",
+ WATCHER_NAMESPACE,
+ NULL, NULL, 100.0f, 1000.0f );
+
+ ::radDbgWatchAddFloat( &MAX_CAMERA_HEIGHT_TRANSITION,
+ "Maximum Camera Height Change",
+ WATCHER_NAMESPACE,
+ NULL, NULL, 1.0f, 50.0f );
+
+ ::radDbgWatchAddFloat( &RADAR_VISIBLE_RADIUS,
+ "Radar Visible Radius",
+ WATCHER_NAMESPACE,
+ NULL, NULL, 0.0f, 1000.0f );
+
+ ::radDbgWatchAddFloat( &CHASE_FLASH_DISTANCE_THRESHOLD,
+ "Chase Flash Distance (%)",
+ WATCHER_NAMESPACE );
+
+ ::radDbgWatchAddFloat( &CHASE_FADE_DISTANCE_THRESHOLD,
+ "Chase Fade Distance (%)",
+ WATCHER_NAMESPACE );
+#endif
+MEMTRACK_POP_GROUP("CHudMap");
+}
+
+//===========================================================================
+// CHudMap::~CHudMap
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CHudMap::~CHudMap()
+{
+#ifdef DEBUGWATCH
+ ::radDbgWatchDelete( &MIN_CAMERA_HEIGHT );
+ ::radDbgWatchDelete( &MAX_CAMERA_HEIGHT );
+ ::radDbgWatchDelete( &MAX_CAMERA_HEIGHT_TRANSITION );
+ ::radDbgWatchDelete( &RADAR_VISIBLE_RADIUS );
+ ::radDbgWatchDelete( &CHASE_FLASH_DISTANCE_THRESHOLD );
+ ::radDbgWatchDelete( &CHASE_FADE_DISTANCE_THRESHOLD );
+#endif
+
+ if( m_hudMapCam != NULL )
+ {
+ m_hudMapCam->Release();
+ m_hudMapCam = NULL;
+ }
+}
+
+//===========================================================================
+// CHudMap::SetCameraTarget
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CHudMap::SetCameraTarget( ISuperCamTarget* target )
+{
+ rAssert( m_hudMapCam );
+ m_hudMapCam->SetTarget( target );
+}
+
+void
+CHudMap::EnableFixedCameraHeight( bool enable, float visibleRadius )
+{
+/*
+ if( !enable )
+ {
+ rWarningMsg( false, "Can't disable fixed camera height in new radar implementation!" );
+
+ // ignore request to disable fixed camera height
+ //
+ return;
+ }
+*/
+ m_fixedCameraHeight = enable ?
+ visibleRadius / rmt::Tan( m_hudMapCam->GetFOV() / 2.0f ) :
+ 0.0f;
+}
+
+//===========================================================================
+// CHudMap::Update
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CHudMap::Update( unsigned int elapsedTime )
+{
+ if( GetInteriorManager()->IsInside() )
+ {
+ this->SetVisible( false );
+ }
+
+ if( !m_isVisible )
+ {
+ // no need to update if not visible
+ //
+ return;
+ }
+
+/*
+ // update radar light (just rotate about radar center point)
+ //
+ static float RADAR_ROTATION_PERIOD = 2000.0f; // in msec
+ float rotation = (elapsedTime / RADAR_ROTATION_PERIOD) * 360.0f;
+ rAssert( m_radarLight != NULL );
+ m_radarLight->RotateAboutCenter( rotation );
+*/
+
+ // get current hud map position and size
+ //
+ int mapX = 0;
+ int mapY = 0;
+ int mapWidth = 0;
+ int mapHeight = 0;
+
+ rAssert( m_p3dMap != NULL );
+ m_p3dMap->GetOriginPosition( mapX, mapY );
+ m_p3dMap->GetBoundingBoxSize( mapWidth, mapHeight );
+
+ float newCameraHeight = DEFAULT_CAMERA_HEIGHT;
+ if( m_fixedCameraHeight != 0.0f ) // fixed camera height enabled
+ {
+ newCameraHeight = m_fixedCameraHeight;
+ }
+ else
+ {
+ if( s_fpIconID != -1 )
+ {
+ HudMapIcon* focalPointIcon = &(s_registeredIcons[ s_fpIconID ]);
+ rAssert( focalPointIcon->m_iconImage );
+
+ if( focalPointIcon->m_dynamicLocator != NULL )
+ {
+ focalPointIcon->m_dynamicLocator->GetPosition( &(focalPointIcon->m_location) );
+ }
+
+ newCameraHeight = this->CalculateCameraHeight( s_registeredIcons[ 0 ].m_location,
+ focalPointIcon->m_location );
+ }
+ }
+
+#ifdef SMOOTH_CAMERA_HEIGHT_TRANSITIONS
+ // for smoother camera height transitions
+ //
+ float cameraHeightChange = newCameraHeight - m_currentCameraHeight;
+ if( rmt::Abs( cameraHeightChange ) > MAX_CAMERA_HEIGHT_TRANSITION )
+ {
+ m_currentCameraHeight += rmt::Sign( cameraHeightChange ) * MAX_CAMERA_HEIGHT_TRANSITION;
+// rTunePrintf( "Current camera height = %f m\n", m_currentCameraHeight );
+ }
+ else
+#endif
+ {
+ m_currentCameraHeight = newCameraHeight;
+ }
+
+ // update hud map camera
+ //
+ rAssert( m_hudMapCam != NULL );
+ m_hudMapCam->SetHeight( m_currentCameraHeight );
+ m_hudMapCam->SetAspect( (float)mapWidth / (float)mapHeight );
+ m_hudMapCam->Update( elapsedTime );
+
+ rmt::Vector hudMapCenter;
+ m_hudMapCam->GetPosition( &hudMapCenter );
+
+#ifdef CIRCULAR_HUD_MAP
+ // update hud hole
+ //
+ if( m_p3dHole != NULL )
+ {
+ static float awayFromCam = 4.5f;
+ m_p3dHole->SetDrawableTranslation( hudMapCenter.x,
+ hudMapCenter.y - awayFromCam,
+ hudMapCenter.z );
+ }
+#endif
+
+ // update registered icons
+ //
+ int iconPosX = 0;
+ int iconPosY = 0;
+ int iconWidth = 0;
+ int iconHeight = 0;
+
+ const unsigned int PULSE_PERIOD = 500;
+// bool iconBlinked = false;
+
+// rWarningMsg( s_fpIconID != -1, "No active focal point icon on hud map!" );
+
+ for( unsigned int i = 0; i < MAX_NUM_REGISTERED_ICONS; i++ )
+ {
+ if( s_registeredIcons[ i ].m_iconImage != NULL )
+ {
+ // check if visible in current gameplay mode
+ //
+ if( GetGameplayManager()->IsSundayDrive() )
+ {
+ if( (s_registeredIcons[ i ].m_visibilityMask & VISIBLE_IN_SUNDAY_DRIVE) == 0 )
+ {
+ s_registeredIcons[ i ].m_iconImage->SetVisible( false );
+ continue;
+ }
+ }
+ else
+ {
+ if( (s_registeredIcons[ i ].m_visibilityMask & VISIBLE_IN_MISSION) == 0 )
+ {
+ s_registeredIcons[ i ].m_iconImage->SetVisible( false );
+ continue;
+ }
+ }
+
+ // hide icons that are not visible when player is in car
+ //
+ if( s_registeredIcons[ i ].m_type == HudMapIcon::ICON_PHONE_BOOTH ||
+ s_registeredIcons[ i ].m_type == HudMapIcon::ICON_PLAYER_CAR )
+ {
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer( m_playerID );
+ rAssert( player != NULL );
+ if( player->IsInCar() )
+ {
+ s_registeredIcons[ i ].m_iconImage->SetVisible( false );
+ continue;
+ }
+ }
+
+// s_registeredIcons[ i ].m_iconImage->SetAlpha( 1.0f );
+
+ // center icon in hud map first
+ //
+ s_registeredIcons[ i ].m_iconImage->GetBoundingBoxSize( iconWidth, iconHeight );
+ iconPosX = (mapX + mapWidth / 2) - (iconWidth / 2);
+ iconPosY = (mapY + mapHeight / 2) - (iconHeight / 2);
+ s_registeredIcons[ i ].m_iconImage->SetPosition( iconPosX, iconPosY );
+
+ s_registeredIcons[ i ].m_iconImage->ResetTransformation();
+
+ // update current location for dynamic icons
+ if( s_registeredIcons[ i ].m_dynamicLocator != NULL )
+ {
+ s_registeredIcons[ i ].m_dynamicLocator->GetPosition( &s_registeredIcons[ i ].m_location );
+
+ // place icon object at sea level
+ s_registeredIcons[ i ].m_location.y = 0;
+
+ if( s_registeredIcons[ i ].IsAICarIcon() ||
+ s_registeredIcons[ i ].m_type == HudMapIcon::ICON_PLAYER ||
+ s_registeredIcons[ i ].m_type == HudMapIcon::ICON_PLAYER_CAR )
+ {
+ rmt::Vector heading( 0, 0, 0 );
+ s_registeredIcons[ i ].m_dynamicLocator->GetHeading( &heading );
+
+ this->UpdateIconHeading( s_registeredIcons[ i ].m_iconImage, &heading );
+ }
+ }
+
+ rmt::Vector iconLocInWorld = s_registeredIcons[ i ].m_location;
+
+#ifdef SMARTER_ICON_LOCATIONS
+ if( static_cast< int >( i ) == s_fpIconID )
+ {
+ // TC: [TODO] need to determine this from the maximum camera height and the
+ // clipping boundary value
+ //
+ static float visibleRadius = 120.0f;
+
+ this->DetermineOnRoadLocation( hudMapCenter,
+ iconLocInWorld,
+ visibleRadius );
+ }
+#endif
+
+ rmt::Vector iconLoc( 0, 0, 0 );
+ rAssert( m_p3dMap->GetCamera() );
+ m_p3dMap->GetCamera()->WorldToView( iconLocInWorld, &iconLoc );
+ iconLoc.z = 0.0f;
+
+ static float CLIPPING_BOUNDARY = 0.40f;
+ float iconScale = 1.0f;
+
+#ifdef CIRCULAR_HUD_MAP
+ // apply view clipping (for circular view)
+ //
+ float iconLength = iconLoc.Magnitude();
+ if( iconLength > CLIPPING_BOUNDARY )
+ {
+ iconScale = CLIPPING_BOUNDARY / iconLength;
+ iconLoc.Scale( iconScale );
+ }
+#else
+ // apply view clipping (for rectangular view)
+ //
+
+ // x-clipping
+ float xScale = 1.0f;
+ if( iconLoc.x > CLIPPING_BOUNDARY )
+ {
+ xScale = CLIPPING_BOUNDARY / iconLoc.x;
+ iconLoc.Scale( xScale, xScale, 1 );
+ }
+ else if( iconLoc.x < -CLIPPING_BOUNDARY )
+ {
+ xScale = -CLIPPING_BOUNDARY / iconLoc.x;
+ iconLoc.Scale( xScale, xScale, 1 );
+ }
+
+ // y-clipping
+ float yScale = 1.0f;
+ if( iconLoc.y > CLIPPING_BOUNDARY )
+ {
+ yScale = CLIPPING_BOUNDARY / iconLoc.y;
+ iconLoc.Scale( yScale, yScale, 1 );
+ }
+ else if( iconLoc.y < -CLIPPING_BOUNDARY )
+ {
+ yScale = -CLIPPING_BOUNDARY / iconLoc.y;
+ iconLoc.Scale( yScale, yScale, 1 );
+ }
+
+ iconScale = xScale * yScale;
+#endif
+
+ bool isIconClipped = (iconScale < 1.0f);
+
+ rAssert( m_radarCone != NULL );
+ m_radarCone->SetVisible( s_fpIconID != -1 );
+
+ if( static_cast< int >( i ) == s_fpIconID ) // for focal point icon ...
+ {
+ // update radar cone direction
+ //
+ m_radarCone->ResetTransformation();
+ m_radarCone->RotateAboutCenter( rmt::RadianToDeg( this->CalculatRadarConeAngle( iconLoc ) ) );
+
+ if( !isIconClipped || s_registeredIcons[ i ].IsAICarIcon() )
+ {
+ m_radarCone->SetAlpha( iconScale );
+ }
+ else
+ {
+ const float MINIMUM_ICON_SCALE = 0.75f;
+ float alpha = (iconScale - MINIMUM_ICON_SCALE) / (1.0f - MINIMUM_ICON_SCALE);
+ m_radarCone->SetAlpha( alpha > 0.0f ? alpha : 0.0f );
+ }
+/*
+ // apply alpha if focal point icon is clipped on boundary
+ //
+ if( iconScale < 1.0f )
+ {
+ if( iconScale < 0.3f )
+ {
+ s_registeredIcons[ i ].m_iconImage->SetAlpha( 0.3f );
+ }
+ else
+ {
+ s_registeredIcons[ i ].m_iconImage->SetAlpha( iconScale );
+ }
+ }
+ else
+ {
+ s_registeredIcons[ i ].m_iconImage->SetAlpha( 1.0f );
+ }
+*/
+
+#ifdef SMARTER_ICON_LOCATIONS
+ s_registeredIcons[ i ].m_iconImage->SetVisible( true );
+#else
+ if( !s_registeredIcons[ i ].IsAICarIcon() )
+ {
+ // hide non-AI car icons if clipped on boundary
+ //
+ s_registeredIcons[ i ].m_iconImage->SetVisible( !isIconClipped );
+ }
+#endif
+
+ // special cases
+ //
+ if( s_registeredIcons[ i ].m_type == HudMapIcon::ICON_MISSION )
+ {
+ static float SCALE_AMPLITUDE = 0.25f;
+
+ float scale = GuiSFX::Pulse( (float)m_elapsedTime,
+ (float)PULSE_PERIOD,
+ 1.0f + SCALE_AMPLITUDE,
+ SCALE_AMPLITUDE );
+
+/*
+ float scale = 1.0f + (float)m_elapsedTime / PULSE_PERIOD * SCALE_AMPLITUDE;
+ float alpha = 1.0f - (float)m_elapsedTime / PULSE_PERIOD * ALPHA_AMPLITUDE;
+
+ s_registeredIcons[ i ].m_iconImage->SetAlpha( alpha );
+*/
+ s_registeredIcons[ i ].m_iconImage->ScaleAboutCenter( scale );
+ }
+/*
+ else if( s_registeredIcons[ i ].m_type == HudMapIcon::ICON_AI_CAR )
+ {
+ // if focal point is AI car icon, fade out when near
+ // distance limit
+ //
+ if( m_maxAICarDistance > 0.0f )
+ {
+ float distanceRatio = m_currentAICarDistance / m_maxAICarDistance;
+ rAssert( distanceRatio <= 1.0f );
+
+ if( distanceRatio > CHASE_FLASH_DISTANCE_THRESHOLD )
+ {
+ // blink icon
+ //
+ iconBlinked = GuiSFX::Blink( s_registeredIcons[ i ].m_iconImage,
+ (float)m_elapsedTime,
+ (float)BLINKING_PERIOD );
+
+ if( distanceRatio > CHASE_FADE_DISTANCE_THRESHOLD )
+ {
+ float iconAlpha = (1.0f - distanceRatio) /
+ (1.0f - CHASE_FADE_DISTANCE_THRESHOLD);
+
+ s_registeredIcons[ i ].m_iconImage->SetAlpha( iconAlpha );
+ }
+ else
+ {
+ s_registeredIcons[ i ].m_iconImage->SetAlpha( 1.0f );
+ }
+ }
+ else
+ {
+ s_registeredIcons[ i ].m_iconImage->SetVisible( true );
+ }
+ }
+ }
+*/
+ }
+ else // for non-focal point icons ...
+ {
+ if( !s_registeredIcons[ i ].IsAICarIcon() )
+ {
+ // hide non-AI car icons if clipped on boundary
+ //
+ s_registeredIcons[ i ].m_iconImage->SetVisible( !isIconClipped );
+ }
+/*
+ // for phone booth icons
+ //
+ if( s_registeredIcons[ i ].m_type == HudMapIcon::ICON_PHONE_BOOTH )
+ {
+ // only show if within visible radius
+ //
+ rmt::Vector playerToPhone;
+ playerToPhone.Sub( s_registeredIcons[ i ].m_location,
+ s_registeredIcons[ 0 ].m_location );
+
+ float distanceRatio = playerToPhone.Magnitude() / PHONE_BOOTH_VISIBLE_RADIUS;
+ bool isWithinRadius = (distanceRatio < 1.0f);
+
+ // fade out nicely to prevent popping
+ //
+ const float FADE_OUT_THRESHOLD = 0.9f;
+ if( !isIconClipped && isWithinRadius )
+ {
+ if( distanceRatio > FADE_OUT_THRESHOLD )
+ {
+ float iconAlpha = (1.0f - distanceRatio) / (1.0f - FADE_OUT_THRESHOLD);
+ s_registeredIcons[ i ].m_iconImage->SetAlpha( iconAlpha );
+ }
+ else
+ {
+ s_registeredIcons[ i ].m_iconImage->SetAlpha( 1.0f );
+ }
+ }
+ else
+ {
+ s_registeredIcons[ i ].m_iconImage->SetAlpha( 0.0f );
+ }
+ }
+*/
+ }
+
+ if( s_registeredIcons[ i ].m_type == HudMapIcon::ICON_AI_HIT_N_RUN )
+ {
+ if( GetHitnRunManager()->IsHitnRunActive() )
+ {
+ s_registeredIcons[ i ].m_iconImage->SetVisible( true );
+
+ // modulate icon colour
+ //
+ tColour iconColour;
+ GuiSFX::ModulateColour( &iconColour,
+ (float)m_elapsedTime,
+ (float)PULSE_PERIOD,
+ HIT_N_RUN_COLOUR_RED,
+ HIT_N_RUN_COLOUR_BLUE );
+
+ s_registeredIcons[ i ].m_iconImage->SetColour( iconColour );
+ }
+ else
+ {
+ // hide icon if H&R is not active
+ //
+ s_registeredIcons[ i ].m_iconImage->SetVisible( false );
+ }
+ }
+
+ // translate the icon (in screen space wrt. hud map center)
+ //
+ int iconX = static_cast<int>( iconLoc.x * mapWidth );
+ int iconY = static_cast<int>( iconLoc.y * mapHeight );
+
+ s_registeredIcons[ i ].m_iconImage->Translate( iconX, iconY );
+ }
+ }
+/*
+ if( iconBlinked )
+ {
+ m_elapsedTime %= PULSE_PERIOD;
+ }
+*/
+ m_elapsedTime = (m_elapsedTime + elapsedTime) % PULSE_PERIOD;
+}
+
+//===========================================================================
+// CHudMap::AddIconToInventory
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CHudMap::AddIconToInventory( HudMapIcon::eIconType type, Scrooby::Sprite* image )
+{
+ if( image == NULL )
+ {
+ return;
+ }
+
+ unsigned int i = 0;
+
+ for( i = 0; i < MAX_NUM_ICONS_PER_TYPE; i++ )
+ {
+ // find first empty slot in inventory to add icon
+ //
+ if( m_icons[ type ][ i ] == NULL )
+ {
+ m_icons[ type ][ i ] = image;
+
+ // hide icon image
+ //
+ image->SetVisible( false );
+
+ break;
+ }
+ }
+
+ rAssertMsg( i < MAX_NUM_ICONS_PER_TYPE,
+ "ERROR: *** Exceeded maximum number of icons per type!\n" );
+}
+
+//===========================================================================
+// CHudMap::RemoveIconFromInventory
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+Scrooby::Sprite*
+CHudMap::RemoveIconFromInventory( HudMapIcon::eIconType type )
+{
+ Scrooby::Sprite* iconImage = NULL;
+
+ // find an available icon of specified type in inventory
+ //
+ for( unsigned int i = 0; i < MAX_NUM_ICONS_PER_TYPE; i++ )
+ {
+ if( m_icons[ type ][ i ] != NULL )
+ {
+ iconImage = m_icons[ type ][ i ];
+ iconImage->SetVisible( true );
+
+ // free up slot in inventory
+ //
+ m_icons[ type ][ i ] = NULL;
+
+ break;
+ }
+ }
+
+ return iconImage;
+}
+
+//===========================================================================
+// CHudMap::RegisterIcon
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+int
+CHudMap::RegisterIcon( HudMapIcon::eIconType type,
+ rmt::Vector location,
+ IHudMapIconLocator* hudMapIconLocator,
+ bool newFocalPoint )
+{
+ rAssert( s_numRegisteredIcons < static_cast<int>( MAX_NUM_REGISTERED_ICONS ) );
+
+ int iconID = -1;
+
+ Scrooby::Sprite* iconImage = this->RemoveIconFromInventory( type );
+ if( iconImage == NULL )
+ {
+ rAssertMsg( false, "WARNING: *** No more available icons of this type! Please tell Tony." );
+
+ return -1;
+ }
+
+ // convert locations in interiors to world-space location of the interior
+ //
+ tName interiorName = GetInteriorManager()->ClassifyPoint( location );
+ if( interiorName.GetUID() != static_cast< tUID >( 0 ) )
+ {
+ ZoneEventLocator* locator = p3d::find<ZoneEventLocator>( interiorName.GetUID() );
+ locator->GetPosition( &location );
+
+ if( hudMapIconLocator != NULL )
+ {
+ hudMapIconLocator = locator;
+ }
+ }
+
+#ifdef RAD_DEBUG
+ // check if there's already a static icon of the same type
+ // registered at exactly the same location
+ //
+ for( unsigned int j = 0; j < MAX_NUM_REGISTERED_ICONS; j++ )
+ {
+ if( s_registeredIcons[ j ].m_iconImage != NULL &&
+ s_registeredIcons[ j ].m_type == type )
+ {
+ rAssertMsg( s_registeredIcons[ j ].m_dynamicLocator != NULL ||
+ s_registeredIcons[ j ].m_location != location,
+ "WARNING: *** Another icon of this type has already been registered at this location. Is this by design??" );
+ }
+ }
+#endif // RAD_DEBUG
+
+ for( unsigned int i = 0; i < MAX_NUM_REGISTERED_ICONS; i++ )
+ {
+ if( i == 0 && type != HudMapIcon::ICON_PLAYER )
+ {
+ // icon ID = 0 is reserved for player icon
+ continue;
+ }
+
+ // find first empty slot to register icon
+ if( s_registeredIcons[ i ].m_iconImage == NULL )
+ {
+ iconID = i;
+ s_numRegisteredIcons++;
+
+ // place icon object at sea level
+ location.y = 0;
+
+ s_registeredIcons[ iconID ].m_iconImage = iconImage;
+ s_registeredIcons[ iconID ].m_type = type;
+ s_registeredIcons[ iconID ].m_location = location;
+ s_registeredIcons[ iconID ].m_dynamicLocator = hudMapIconLocator;
+
+ // set visible in sunday drive mode for these icon types
+ //
+ if( type == HudMapIcon::ICON_PLAYER ||
+ type == HudMapIcon::ICON_PLAYER_CAR ||
+ type == HudMapIcon::ICON_MISSION ||
+ type == HudMapIcon::ICON_BONUS_MISSION ||
+ type == HudMapIcon::ICON_STREET_RACE ||
+ type == HudMapIcon::ICON_WAGER_RACE ||
+ type == HudMapIcon::ICON_PURCHASE_CENTRE ||
+ type == HudMapIcon::ICON_PHONE_BOOTH ||
+ type == HudMapIcon::ICON_AI_HIT_N_RUN )
+ {
+ s_registeredIcons[ i ].m_visibilityMask |= VISIBLE_IN_SUNDAY_DRIVE;
+ }
+
+ // set visible in mission mode for these icon types
+ //
+ if( type == HudMapIcon::ICON_PLAYER ||
+ type == HudMapIcon::ICON_PLAYER_CAR ||
+ type == HudMapIcon::ICON_FLAG_CHECKERED ||
+ type == HudMapIcon::ICON_FLAG_WAYPOINT ||
+ type == HudMapIcon::ICON_COLLECTIBLE ||
+ type == HudMapIcon::ICON_MISSION ||
+ s_registeredIcons[ i ].IsAICarIcon() )
+ {
+ s_registeredIcons[ i ].m_visibilityMask |= VISIBLE_IN_MISSION;
+ }
+
+ // colour code AI car icons
+ //
+ if( s_registeredIcons[ i ].IsAICarIcon() )
+ {
+ s_registeredIcons[ i ].ApplyAICarIconColour();
+ }
+
+ break;
+ }
+ }
+
+ rAssertMsg( iconID != -1, "ERROR: *** There should have been an empty slot!\n" );
+
+ if( newFocalPoint )
+ {
+ // set as new focal point icon
+ //
+ s_fpIconID = iconID;
+
+ // whenever the target changes, we want to recompute its closest road segment
+ m_lastRoadSeg = NULL;
+
+ m_frameCount = -1; // reset frame count
+ }
+
+
+ return iconID;
+}
+
+//===========================================================================
+// CHudMap::UnregisterIcon
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CHudMap::UnregisterIcon( int iconID )
+{
+ if( iconID != -1 )
+ {
+ rAssert( iconID < static_cast<int>( MAX_NUM_REGISTERED_ICONS ) );
+
+ if( s_registeredIcons[ iconID ].m_iconImage != NULL )
+ {
+ // return icon to inventory
+ //
+ this->AddIconToInventory( s_registeredIcons[ iconID ].m_type,
+ s_registeredIcons[ iconID ].m_iconImage );
+
+ s_registeredIcons[ iconID ].m_iconImage = NULL;
+ s_registeredIcons[ iconID ].m_visibilityMask = 0;
+ s_numRegisteredIcons--;
+
+ if( s_fpIconID == iconID )
+ {
+ s_fpIconID = -1;
+ }
+ }
+ }
+ else
+ {
+ rAssertMsg( false, "This should only happen if you didn't get a valid icon ID upon registration!" );
+ }
+}
+
+//=============================================================================
+// CHudMap::ChangeIconType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( HudMapIcon::eIconType type, int iconID )
+//
+// Return: void
+//
+//=============================================================================
+int
+CHudMap::ChangeIconType( int iconID, HudMapIcon::eIconType type )
+{
+ int newIconID = -1;
+
+ if( iconID != -1 )
+ {
+ bool isFocalPointIcon = (iconID == s_fpIconID);
+
+ // save a copy of old icon
+ //
+ HudMapIcon* oldIcon = &s_registeredIcons[ iconID ];
+
+ // unregister it first
+ //
+ this->UnregisterIcon( iconID );
+
+ // re-registered icon of new type
+ //
+ newIconID = this->RegisterIcon( type, oldIcon->m_location, oldIcon->m_dynamicLocator, isFocalPointIcon );
+ }
+
+ return newIconID;
+}
+
+//===========================================================================
+// CHudMap::SetFocalPointIcon
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CHudMap::SetFocalPointIcon( int iconID )
+{
+ if( iconID != -1 )
+ {
+ rAssert( iconID < static_cast< int >( MAX_NUM_REGISTERED_ICONS ) );
+ s_fpIconID = iconID;
+
+ // whenever the target changes, we want to recompute its closest road segment
+ m_lastRoadSeg = NULL;
+
+ m_frameCount = -1; // reset frame count
+ }
+}
+
+//===========================================================================
+// CHudMap::FindIcon
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+HudMapIcon*
+CHudMap::FindIcon( HudMapIcon::eIconType type ) const
+{
+ const HudMapIcon* icon = NULL;
+
+ for( unsigned int i = 0; i < MAX_NUM_REGISTERED_ICONS; i++ )
+ {
+ if( s_registeredIcons[ i ].m_iconImage != NULL &&
+ s_registeredIcons[ i ].m_type == type )
+ {
+ // found it!
+ //
+ icon = &(s_registeredIcons[ i ]);
+
+ break;
+ }
+ }
+
+ return const_cast<HudMapIcon*>( icon );
+}
+
+//===========================================================================
+// CHudMap::Translate
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CHudMap::Translate( int x, int y )
+{
+ rAssert( m_p3dMap );
+
+ m_originalPosX += x;
+ m_originalPosY += y;
+ m_p3dMap->SetPosition( m_originalPosX, m_originalPosY );
+ if( m_p3dHole != NULL )
+ {
+ m_p3dHole->SetPosition( m_originalPosX, m_originalPosY );
+ }
+
+ this->Update( 0 );
+}
+
+//===========================================================================
+// CHudMap::Reset
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CHudMap::Reset()
+{
+ rAssert( m_p3dMap != NULL );
+
+ // restore hud map position and size
+ //
+ m_p3dMap->SetPosition( m_originalPosX, m_originalPosY );
+ m_p3dMap->SetBoundingBoxSize( m_originalWidth, m_originalHeight );
+
+ if( m_p3dHole != NULL )
+ {
+ m_p3dHole->SetPosition( m_originalPosX, m_originalPosY );
+ m_p3dHole->SetBoundingBoxSize( m_originalWidth, m_originalHeight );
+ }
+
+ // restore hud map camera
+ //
+ m_p3dMap->SetCamera( m_hudMapCam->GetCamera() );
+
+ m_elapsedTime = 0;
+
+ this->Update( 0 );
+}
+
+//===========================================================================
+// CHudMap::SetVisible
+//===========================================================================
+// Description: Toggle visibility of 3D map.
+//
+// Constraints: None.
+//
+// Parameters: visibility
+//
+// Return:
+//
+//===========================================================================
+void CHudMap::SetVisible( bool isVisible )
+{
+ m_isVisible = isVisible;
+
+ rAssert( m_p3dMap != NULL );
+ m_p3dMap->SetVisible( isVisible );
+
+ rAssert( m_radar != NULL );
+ m_radar->SetVisible( isVisible );
+
+ rAssert( m_iconsGroup != NULL );
+ m_iconsGroup->SetVisible( isVisible );
+}
+
+void
+CHudMap::RestoreAllRegisteredIcons()
+{
+ if( s_numRegisteredIcons > 0 )
+ {
+ int numIconsRestored = 0;
+
+ for( unsigned int i = 0; i < MAX_NUM_REGISTERED_ICONS; i++ )
+ {
+ if( s_registeredIcons[ i ].m_iconImage != NULL )
+ {
+ s_registeredIcons[ i ].m_iconImage = this->RemoveIconFromInventory( s_registeredIcons[ i ].m_type );
+ rAssert( s_registeredIcons[ i ].m_iconImage != NULL );
+
+ if( s_registeredIcons[ i ].IsAICarIcon() )
+ {
+ s_registeredIcons[ i ].ApplyAICarIconColour();
+ }
+
+ numIconsRestored++;
+ }
+ }
+
+ rAssertMsg( numIconsRestored == s_numRegisteredIcons, "Not all registered icons were restored!" );
+ }
+}
+
+void
+CHudMap::ClearAllRegisteredIcons()
+{
+ memset( s_registeredIcons, 0, sizeof( s_registeredIcons ) );
+ s_numRegisteredIcons = 0;
+ s_fpIconID = -1;
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+//===========================================================================
+// CHudMap::CalculateDistanceBetweenPoints
+//===========================================================================
+// Description: Calculates the scalar distance between 2 points in 3D-space.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+float
+CHudMap::CalculateDistanceBetweenPoints( rmt::Vector& a, rmt::Vector& b )
+{
+ rmt::Vector displacement( 0.0f, 0.0f, 0.0f );
+ displacement.Sub( a, b );
+
+ return displacement.Magnitude();
+}
+
+//===========================================================================
+// CHudMap::UpdateIconHeading
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void
+CHudMap::UpdateIconHeading( Scrooby::Sprite* iconImage, rmt::Vector* iconHeading )
+{
+ rmt::Vector objectHeading( iconHeading->x, 0, iconHeading->z );
+
+ rmt::Vector camHeading;
+ tPointCamera* camera = (tPointCamera*)m_p3dMap->GetCamera();
+ rAssert( camera );
+ camera->GetVUp( &camHeading );
+ camHeading.y = 0.0f;
+
+ if( camHeading.MagnitudeSqr() > 0.0f && objectHeading.MagnitudeSqr() > 0.0f )
+ {
+ float ratio = camHeading.Dot( objectHeading ) /
+ (camHeading.Magnitude() * objectHeading.Magnitude());
+
+ float rotation = 0.0f;
+
+ if( ratio > 1.0f )
+ {
+ rotation = 0.0f;
+ }
+ else if( ratio < -1.0f )
+ {
+ rotation = rmt::PI;
+ }
+ else
+ {
+ rotation = rmt::ACos( ratio );
+ }
+
+ rAssert( !rmt::IsNan( rotation ) );
+
+ rmt::Vector normal = camHeading;
+ normal.CrossProduct( objectHeading );
+ if( normal.y < 0 )
+ {
+ rotation = -rotation;
+ }
+
+ rAssert( iconImage );
+ iconImage->RotateAboutCenter( rmt::RadianToDeg( rotation ) );
+ }
+}
+
+//===========================================================================
+// CHudMap::CalculateCameraHeight
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+float
+CHudMap::CalculateCameraHeight( rmt::Vector& player, rmt::Vector& mission ) const
+{
+ // fix locations at sea level
+ //
+ player.y = 0;
+ mission.y = 0;
+
+ float distance = CalculateDistanceBetweenPoints( player, mission );
+
+ // calculate camera height based on distance
+ //
+ float cameraHeight = this->CalculateCameraHeight( distance * 1.5f );
+
+ if( cameraHeight < MIN_CAMERA_HEIGHT )
+ {
+ cameraHeight = MIN_CAMERA_HEIGHT;
+ }
+ else if( cameraHeight > MAX_CAMERA_HEIGHT )
+ {
+ cameraHeight = MAX_CAMERA_HEIGHT;
+ }
+
+ return cameraHeight;
+}
+
+//===========================================================================
+// CHudMap::CalculateCameraHeight
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+float
+CHudMap::CalculateCameraHeight( float visibleRadius ) const
+{
+ float cameraHeight = visibleRadius;
+ if( rmt::Abs( m_hudMapCam->GetFOV() - rmt::PI_BY2 ) > 0.001f )
+ {
+ cameraHeight = visibleRadius / rmt::Tan( m_hudMapCam->GetFOV() / 2.0f );
+ }
+
+ return cameraHeight;
+}
+
+//===========================================================================
+// CHudMap::CalculatRadarConeAngle
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+float
+CHudMap::CalculatRadarConeAngle( rmt::Vector& iconLoc ) const
+{
+ float radarConeAngle = 0.0f; // zero angle by default
+
+ if( iconLoc.y > 0.0f ) // icon is located in either Quadrant I or II
+ {
+ radarConeAngle = rmt::ATan( iconLoc.x / iconLoc.y );
+ }
+ else if( iconLoc.y < 0.0f ) // icon is located in either Quadrant III or IV
+ {
+ radarConeAngle = rmt::ATan( iconLoc.x / iconLoc.y ) + rmt::PI;
+ }
+ else // icon is located somewhere along the X-axis
+ {
+ if( iconLoc.x > 0.0f )
+ {
+ radarConeAngle = rmt::PI_BY2;
+ }
+ else if( iconLoc.x < 0.0f )
+ {
+ radarConeAngle = -rmt::PI_BY2;
+ }
+ }
+
+ return radarConeAngle;
+}
+
+//===========================================================================
+// CHudMap::DetermineOnRoadLocation
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+
+// We return the target vector (where we want to direct the navigation cone)
+void
+CHudMap::DetermineOnRoadLocation( rmt::Vector& source, rmt::Vector& target, float visibleRadius )
+{
+ // place source and target at sea level
+ //
+ source.y = 0;
+ target.y = 0;
+
+ float distance = CalculateDistanceBetweenPoints( source, target );
+ if( distance <= visibleRadius )
+ {
+ // no need to do any calculations if target is within 'visibleRadius' from source
+ //
+ return;
+ }
+
+ // check to see if we should skip this update and return cached information instead
+ //
+ m_frameCount = (m_frameCount + 1) % NUM_AMORTIZED_UPDATE_FRAMES;
+ if( m_frameCount != 0 )
+ {
+ target = m_lastOnRoadLocation;
+
+ return;
+ }
+
+ // get reference to road manager
+ //
+ RoadManager* roadManager = RoadManager::GetInstance();
+ rAssert( roadManager != NULL );
+
+ //////////////
+ // find path between source and target
+ //
+ bool isPathElementFound = false;
+ float searchRadius = 200.0f;
+ float dummy;
+
+ // Find source's closest path element
+ RoadSegment* sourceSeg = NULL;
+ float sourceSegT = 0.0f;
+ RoadManager::PathElement sourceElem;
+ float sourceRoadT = 0.0f;
+
+ isPathElementFound = VehicleAI::FindClosestPathElement( source,
+ sourceElem, sourceSeg, sourceSegT, sourceRoadT, false ); // don't want shortcuts on hudmap
+
+ if( !isPathElementFound )
+ {
+ GetIntersectManager()->FindClosestRoad( source, searchRadius, sourceSeg, dummy ); // ignore shortcuts
+ rAssertMsg( sourceSeg, "Should always find a nearby road seg! Increase search radius!" );
+ if( !sourceSeg )
+ {
+ return;
+ }
+
+ sourceElem.type = RoadManager::ET_NORMALROAD;
+ sourceElem.elem = sourceSeg->GetRoad();
+ sourceSegT = roadManager->DetermineSegmentT( source, sourceSeg );
+ sourceRoadT = roadManager->DetermineRoadT( sourceSeg, sourceSegT );
+ }
+ rAssert( sourceElem.elem );
+
+
+ // Find target's closest path element
+ RoadSegment* targetSeg = NULL;
+ float targetSegT = 0.0f;
+ RoadManager::PathElement targetElem;
+ float targetRoadT = 0.0f;
+
+ isPathElementFound = VehicleAI::FindClosestPathElement( target,
+ targetElem, targetSeg, targetSegT, targetRoadT, false ); // don't want shortcuts on hudmap
+
+ if( !isPathElementFound )
+ {
+ RoadSegment* closestRoadSeg = NULL;
+ if( m_lastRoadSeg )
+ {
+ closestRoadSeg = m_lastRoadSeg;
+ }
+ else
+ {
+ GetIntersectManager()->FindClosestRoad( target, searchRadius, closestRoadSeg, dummy );
+ rAssertMsg( closestRoadSeg, "Should always find a nearby road seg! Increase search radius!" );
+ if( !closestRoadSeg )
+ {
+ return;
+ }
+ m_lastRoadSeg = closestRoadSeg;
+ }
+ targetElem.type = RoadManager::ET_NORMALROAD;
+ targetElem.elem = closestRoadSeg->GetRoad();
+ targetSeg = closestRoadSeg;
+ targetSegT = roadManager->DetermineSegmentT( target, targetSeg );
+ targetRoadT = roadManager->DetermineRoadT( targetSeg, targetSegT );
+ }
+ else
+ {
+ // need to make this check cuz FindClosestPathElement could return an intersection
+ if( targetSeg )
+ {
+ m_lastRoadSeg = targetSeg;
+ }
+ }
+ rAssert( targetElem.elem );
+
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+
+ SwapArray<RoadManager::PathElement> pathElements;
+ pathElements.Allocate( roadManager->GetMaxPathElements() );
+
+ RoadManager::PathElement tmpSrcElem = sourceElem;
+ float roadDistance = roadManager->FindPathElementsBetween( false,
+ tmpSrcElem, sourceRoadT, source,
+ targetElem, targetRoadT, target,
+ pathElements );
+
+ rAssert( pathElements.mUseSize > 0 );
+
+ // Temp stuff we use over and over again
+ RoadManager::PathElement* prevElem = &(pathElements[0]);
+ bool isRoadBackwards = false;
+ int numIntersects = 0;
+ rmt::Vector intPts[2];
+
+ rmt::Sphere visibleSphere( source, visibleRadius ); // this is the hud map circle
+
+ // find closest path element that intersects visible sphere (or circle, really)
+ //
+ bool isClosestPathElementFound = false;
+
+ if( pathElements.mUseSize == 1 )
+ {
+ // if only one element returned
+ if( pathElements[0].type == RoadManager::ET_INTERSECTION )
+ {
+ // if the only elem is an intersection, just point to target
+ numIntersects = IntersectLineSphere( source, target, visibleSphere, intPts );
+ rAssert( numIntersects == 1 );
+ target = intPts[0];
+ }
+ else // the only element is a road
+ {
+ Road* theRoad = (Road*) targetElem.elem;
+
+ // we know here that one element returned is either cuz:
+ // A) source and target lie on same road
+ // or
+ // B) source and target lie on opposite roads that
+ // describe the same physical road (target road is returned)
+
+ // case B
+ if( sourceElem != targetElem )
+ {
+ // iterate through target road's segments to find the closest seg
+ // to source, the t value of this segment will tell us whether to
+ // traverse the segments backwards or forwards...
+ //
+ rmt::Vector closestPos;
+ float closestDist;
+ int closestSegIndex;
+
+ RoadManager::FindClosestPointOnRoad( theRoad, source, closestPos, closestDist, closestSegIndex );
+
+ rAssert( 0 <= closestSegIndex && closestSegIndex < (int) theRoad->GetNumRoadSegments() );
+ RoadSegment* closestSeg = theRoad->GetRoadSegment( (unsigned int) closestSegIndex );
+ rAssert( closestSeg );
+
+ sourceElem.elem = theRoad;
+ sourceSeg = closestSeg;
+ sourceSegT = RoadManager::DetermineSegmentT( closestPos, closestSeg );
+ sourceRoadT = RoadManager::DetermineRoadT( sourceSeg, sourceSegT );
+ }
+
+ rAssert( sourceElem == targetElem );
+
+ // this should now work for either A or B
+ if( sourceRoadT > targetRoadT )
+ {
+ isRoadBackwards = true;
+ }
+
+ unsigned int startIndex = sourceSeg->GetSegmentIndex();
+ unsigned int endIndex = targetSeg->GetSegmentIndex();
+
+ bool foundIntersect = false;
+ if( !isRoadBackwards )
+ {
+ for( unsigned int i=startIndex; i<=endIndex; i++ )
+ {
+ RoadSegment* seg = theRoad->GetRoadSegment( i );
+
+ rmt::Vector vec0, vec1, vec2, vec3, start, end;
+ seg->GetCorner( 0, vec0 );
+ seg->GetCorner( 1, vec1 );
+ seg->GetCorner( 2, vec2 );
+ seg->GetCorner( 3, vec3 );
+
+ start = (vec0 + vec3) * 0.5f;
+ start.y = 0.0f;
+ end = (vec1 + vec2) * 0.5f;
+ end.y = 0.0f;
+
+ int numIntersects = IntersectLineSphere( start, end, visibleSphere, intPts );
+ rAssert( 0 <= numIntersects && numIntersects <= 1 );
+
+ if( numIntersects == 1 )
+ {
+ target = intPts[0];
+ isClosestPathElementFound = true;
+
+ break;
+ }
+ }
+ }
+ else
+ {
+ for( int i=(int)startIndex; i>=(int)endIndex; i-- )
+ {
+ RoadSegment* seg = theRoad->GetRoadSegment( (unsigned int)i );
+
+ rmt::Vector vec0, vec1, vec2, vec3, start, end;
+ seg->GetCorner( 0, vec0 );
+ seg->GetCorner( 1, vec1 );
+ seg->GetCorner( 2, vec2 );
+ seg->GetCorner( 3, vec3 );
+
+ start = (vec0 + vec3) * 0.5f;
+ start.y = 0.0f;
+ end = (vec1 + vec2) * 0.5f;
+ end.y = 0.0f;
+
+ int numIntersects = IntersectLineSphere( start, end, visibleSphere, intPts );
+ rAssert( 0 <= numIntersects && numIntersects <= 1 );
+
+ if( numIntersects == 1 )
+ {
+ target = intPts[0];
+ isClosestPathElementFound = true;
+
+ break;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ // At this point, we're dealing with multiple path elements
+ //
+ Intersection* previousIntersection = NULL;
+ for( int i = 0; i < pathElements.mUseSize; i++ )
+ {
+ if( isClosestPathElementFound )
+ {
+ // we're done! we found the closest path element
+ //
+ break;
+ }
+
+ RoadManager::PathElement* currentPathElement = &( pathElements[ i ] );
+ rAssert( currentPathElement != NULL );
+
+ switch( currentPathElement->type )
+ {
+ case RoadManager::ET_NORMALROAD:
+ {
+ Road* road = static_cast<Road*>( currentPathElement->elem );
+ rAssert( road != NULL );
+
+ int numRoadSegments = static_cast<int>( road->GetNumRoadSegments() );
+
+ // determine which direction to iterate over all road segments
+ //
+ bool isRoadBackwards = false;
+
+ if( i == 0 ) // special case if first path element is a road
+ {
+ for( int j = 1; j < pathElements.mUseSize; j++ )
+ {
+ RoadManager::PathElement* pathElement = &( pathElements[ j ] );
+ rAssert( pathElement != NULL );
+
+ if( pathElement->type == RoadManager::ET_INTERSECTION )
+ {
+ isRoadBackwards = ( road->GetSourceIntersection() == static_cast<Intersection*>( pathElement->elem ) );
+
+ break;
+ }
+ }
+ }
+ else
+ {
+ isRoadBackwards = ( road->GetSourceIntersection() != previousIntersection );
+ }
+
+ int currentRoadSegmentIndex = isRoadBackwards ? numRoadSegments - 1 : 0;
+
+ while( currentRoadSegmentIndex >= 0 && currentRoadSegmentIndex < numRoadSegments )
+ {
+ RoadSegment* currentRoadSegment = road->GetRoadSegment( currentRoadSegmentIndex );
+ rmt::Vector vec0, vec1, vec2, vec3, start, end;
+
+ currentRoadSegment->GetCorner( 0, vec0 );
+ currentRoadSegment->GetCorner( 1, vec1 );
+ currentRoadSegment->GetCorner( 2, vec2 );
+ currentRoadSegment->GetCorner( 3, vec3 );
+
+ start = (vec0 + vec3) * 0.5f;
+ start.y = 0.0f;
+ end = (vec1 + vec2) * 0.5f;
+ end.y = 0.0f;
+
+ rmt::Vector intersectPoints[ 2 ];
+ int numIntersectPoints = IntersectLineSphere( start, end, visibleSphere, intersectPoints );
+ if( numIntersectPoints > 0 )
+ {
+ rmt::Vector closestIntersectPoint = intersectPoints[ 0 ];
+
+ // TC [TODO]: if more than one intersection points, need to determine which
+ // one is closest to source
+ //
+ if( numIntersectPoints > 1 )
+ {
+ rTuneWarningMsg( false, "Multiple intersection points not yet handled properly, but this should not happen here!" );
+ }
+
+ // check to make sure that this point is, in fact, closer to the target than the source
+ //
+ if( i == 0 ) // only need to check this for the first path element
+ {
+ float roadSegT = roadManager->DetermineSegmentT( closestIntersectPoint, const_cast<RoadSegment*>( currentRoadSegment ) );
+ float intersectRoadT = roadManager->DetermineRoadT( const_cast<RoadSegment*>( currentRoadSegment ), roadSegT );
+
+ if( ( isRoadBackwards && intersectRoadT < sourceRoadT) ||
+ (!isRoadBackwards && intersectRoadT > sourceRoadT) )
+ {
+ target = closestIntersectPoint;
+ isClosestPathElementFound = true;
+
+ break; // stop iterating over road segments
+ }
+ else
+ {
+ int dummy = 0;
+ }
+ }
+ else
+ {
+ target = closestIntersectPoint;
+ isClosestPathElementFound = true;
+
+ break; // stop iterating over road segments
+ }
+ }
+
+ currentRoadSegmentIndex += isRoadBackwards ? -1 : +1;
+ }
+
+ break;
+ }
+ case RoadManager::ET_INTERSECTION:
+ {
+ Intersection* intersection = static_cast<Intersection*>( currentPathElement->elem );
+ rAssert( intersection != NULL );
+
+ // keep track of previous intersection
+ //
+ previousIntersection = intersection;
+
+ rmt::Vector intersectionLocation;
+ intersection->GetLocation( intersectionLocation );
+ intersectionLocation.y = 0.0f;
+ float distance = CalculateDistanceBetweenPoints( source, intersectionLocation );
+
+ if( rmt::Abs( distance - visibleRadius ) <= intersection->GetRadius() )
+ {
+ // project target onto radar edge, on a point that is closest to the
+ // intersection center point
+ //
+ rmt::Vector sourceToIntersection;
+ sourceToIntersection.Sub( intersectionLocation, source );
+ sourceToIntersection.Scale( visibleRadius / distance );
+
+ target.Add( source, sourceToIntersection );
+
+ isClosestPathElementFound = true;
+ }
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Invalid path element type!" );
+
+ break;
+ }
+ }
+ }
+ }
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ target.y = 0.0f;
+ m_lastOnRoadLocation = target;
+
+#ifndef RAD_RELEASE
+ if( !isClosestPathElementFound )
+ {
+ // TC: [INVESTIGATE] if closest path element is not found, there must be some discontinuity
+ // in the closest path found
+ //
+ rTuneWarningMsg( false, "Closest path element not found! Please go tell Tony." );
+ }
+#endif
+}
+
diff --git a/game/code/presentation/gui/utility/hudmap.h b/game/code/presentation/gui/utility/hudmap.h
new file mode 100644
index 0000000..cb8b9ec
--- /dev/null
+++ b/game/code/presentation/gui/utility/hudmap.h
@@ -0,0 +1,240 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CHudMap
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/01 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef HUDMAP_H
+#define HUDMAP_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <p3d/p3dtypes.hpp>
+#include <radmath/radmath.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+const unsigned int MAX_NUM_REGISTERED_ICONS = 64;
+const unsigned int MAX_NUM_ICONS_PER_TYPE = 32;
+
+struct HudMapCam;
+class ISuperCamTarget;
+class tPointCamera;
+class RoadSegment;
+
+namespace Scrooby
+{
+ class Page;
+ class Group;
+ class Pure3dObject;
+ class Sprite;
+}
+
+// pure virtual interface for dynamic hud map icons
+//
+struct IHudMapIconLocator
+{
+ virtual void GetPosition( rmt::Vector* currentLoc ) = 0;
+ virtual void GetHeading( rmt::Vector* heading ) = 0;
+};
+
+struct HudMapIcon
+{
+ enum eIconType
+ {
+ UNKNOWN_ICON_TYPE = -1,
+
+ ICON_PLAYER,
+ ICON_PLAYER_CAR,
+
+ ICON_AI_TYPES_BEGIN = ICON_PLAYER_CAR, // not an icon type
+ ICON_AI_HIT_N_RUN,
+ ICON_AI_CHASE,
+ ICON_AI_RACE,
+ ICON_AI_EVADE,
+ ICON_AI_TARGET,
+ ICON_AI_TYPES_END, // not an icon type
+
+ ICON_FLAG_CHECKERED = ICON_AI_TYPES_END,
+ ICON_FLAG_WAYPOINT,
+ ICON_COLLECTIBLE,
+ ICON_MISSION,
+ ICON_BONUS_MISSION,
+ ICON_PHONE_BOOTH,
+ ICON_PURCHASE_CENTRE,
+ ICON_STREET_RACE,
+ ICON_WAGER_RACE,
+
+ NUM_ICON_TYPES
+ };
+
+ Scrooby::Sprite* m_iconImage;
+ eIconType m_type;
+ rmt::Vector m_location;
+ IHudMapIconLocator* m_dynamicLocator;
+ unsigned int m_visibilityMask;
+
+ HudMapIcon()
+ : m_iconImage( NULL ),
+ m_type( UNKNOWN_ICON_TYPE ),
+ m_location( 0, 0, 0 ),
+ m_dynamicLocator( NULL ),
+ m_visibilityMask( 0 )
+ {
+ }
+
+ bool IsAICarIcon() const
+ {
+ return( static_cast<int>( m_type ) > ICON_AI_TYPES_BEGIN &&
+ static_cast<int>( m_type ) < ICON_AI_TYPES_END );
+ }
+
+ void ApplyAICarIconColour();
+
+};
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CHudMap
+{
+public:
+ CHudMap( Scrooby::Page* pPage, int playerID, const char* p3dFile = NULL );
+ virtual ~CHudMap();
+
+ // Camera Target must be set before doing anything else!
+ //
+ void SetCameraTarget( ISuperCamTarget* target );
+
+ // enable/disable fixed camera height (if enabling, the fixed camera
+ // height will be calculated from the visible radius)
+ //
+ void EnableFixedCameraHeight( bool enable,
+ float visibleRadius = 50.0f );
+
+ // Update
+ //
+ void Update( unsigned int elapsedTime );
+
+ // for AI Car icons only
+ //
+ void UpdateAICarDistance( float currentDistance, float maxDistance );
+
+ // Map Icons
+ //
+ void AddIconToInventory( HudMapIcon::eIconType type, Scrooby::Sprite* image );
+ Scrooby::Sprite* RemoveIconFromInventory( HudMapIcon::eIconType type );
+
+ int RegisterIcon( HudMapIcon::eIconType type, rmt::Vector location,
+ IHudMapIconLocator* hudMapIconLocator = NULL,
+ bool newFocalPoint = false );
+
+ void UnregisterIcon( int iconID );
+
+ int ChangeIconType( int iconID, HudMapIcon::eIconType type );
+ void SetFocalPointIcon( int iconID );
+ HudMapIcon* FindIcon( HudMapIcon::eIconType type ) const;
+
+ // Reset position and size to original settings
+ //
+ void Translate( int x, int y );
+ void Reset();
+
+ void SetVisible( bool isVisible );
+
+ // Accessors
+ //
+ Scrooby::Pure3dObject* GetMap() const { return m_p3dMap; }
+ Scrooby::Pure3dObject* GetHole() const { return m_p3dHole; }
+ HudMapCam* GetHudMapCam() const { return m_hudMapCam; }
+
+ void RestoreAllRegisteredIcons();
+ static void ClearAllRegisteredIcons();
+
+private:
+
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or asignment. Declare but don't define.
+ //
+ CHudMap( const CHudMap& );
+ CHudMap& operator= ( const CHudMap& );
+
+ static float CalculateDistanceBetweenPoints( rmt::Vector& a, rmt::Vector& b );
+
+ void UpdateIconHeading( Scrooby::Sprite* iconImage, rmt::Vector* iconHeading );
+
+ float CalculateCameraHeight( rmt::Vector& player, rmt::Vector& mission ) const;
+ float CalculateCameraHeight( float visibleRadius ) const;
+ float CalculatRadarConeAngle( rmt::Vector& iconLoc ) const;
+
+ void DetermineOnRoadLocation( rmt::Vector& source, rmt::Vector& target, float visibleRadius );
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ int m_playerID;
+
+ Scrooby::Pure3dObject* m_p3dMap;
+ Scrooby::Pure3dObject* m_p3dHole;
+ int m_originalPosX;
+ int m_originalPosY;
+ int m_originalWidth;
+ int m_originalHeight;
+
+ bool m_isVisible : 1;
+ Scrooby::Group* m_radar;
+ Scrooby::Group* m_radarCone;
+ Scrooby::Group* m_iconsGroup;
+
+ HudMapCam* m_hudMapCam;
+ float m_currentCameraHeight;
+ float m_fixedCameraHeight;
+
+ Scrooby::Sprite* m_icons[ HudMapIcon::NUM_ICON_TYPES ][ MAX_NUM_ICONS_PER_TYPE ];
+
+ // for AI Car icons only
+ //
+ float m_currentAICarDistance;
+ float m_maxAICarDistance;
+
+ unsigned int m_elapsedTime;
+
+ RoadSegment* m_lastRoadSeg;
+ rmt::Vector m_lastOnRoadLocation;
+ int m_frameCount;
+
+ // gameplay-persistent data
+ //
+ static HudMapIcon s_registeredIcons[ MAX_NUM_REGISTERED_ICONS ];
+ static int s_numRegisteredIcons;
+ static int s_fpIconID;
+
+};
+
+inline void CHudMap::UpdateAICarDistance( float currentDistance, float maxDistance )
+{
+ m_currentAICarDistance = currentDistance;
+ m_maxAICarDistance = maxDistance;
+}
+
+#endif // HUDMAP_H
diff --git a/game/code/presentation/gui/utility/hudmapcam.cpp b/game/code/presentation/gui/utility/hudmapcam.cpp
new file mode 100644
index 0000000..b83396b
--- /dev/null
+++ b/game/code/presentation/gui/utility/hudmapcam.cpp
@@ -0,0 +1,113 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudMapCam
+//
+// Description: Implementation of the CHudMap class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/utility/hudmapcam.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+
+#include <raddebug.hpp>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+HudMapCam::HudMapCam( int playerID )
+: KullCam(),
+ m_camera( NULL ),
+ m_originalHeading( -1, 0, 0 )
+{
+ SetPlayerID( playerID );
+ mIgnoreDebugController = true;
+
+ m_camera = new tPointCamera;
+ m_camera->AddRef();
+ SetCamera( m_camera );
+
+ // override default KullCam parameters
+ mElevation = 0.0f;
+ mMagnitude = 150.0f;
+
+ // fix hud map cam FOV
+ //
+ this->SetFOV( rmt::PI_BY2 );
+}
+
+HudMapCam::~HudMapCam()
+{
+ if( m_camera != NULL )
+ {
+ m_camera->Release();
+ m_camera = NULL;
+ }
+}
+
+void
+HudMapCam::Update( unsigned int milliseconds )
+{
+ // adjust kull cam rotation to match main camera heading
+ //
+ rmt::Vector camHeading;
+ GetSuperCamManager()->GetSCC( this->GetPlayerID() )->GetActiveSuperCam()->GetHeading( &camHeading );
+ camHeading.y = 0;
+
+ if( camHeading.MagnitudeSqr() > 0 )
+ {
+ float ratio = camHeading.DotProduct( m_originalHeading ) / camHeading.Magnitude();
+ if( ratio > 1.0f )
+ {
+ mRotation = 0.0f;
+ }
+ else if( ratio < -1.0f )
+ {
+ mRotation = rmt::PI;
+ }
+ else
+ {
+ mRotation = rmt::ACos( ratio );
+ }
+
+ rAssert( !rmt::IsNan( mRotation ) );
+
+ rmt::Vector normal = camHeading;
+ normal.CrossProduct( m_originalHeading );
+ if( normal.y < 0 )
+ {
+ mRotation = -mRotation;
+ }
+ }
+
+ KullCam::Update( milliseconds );
+
+ // fix hud map cam at 'mMagnitude' above sea level
+ //
+ rmt::Vector camPosition = m_camera->GetPosition();
+ camPosition.y = mMagnitude;
+ m_camera->SetPosition( camPosition );
+}
+
+void
+HudMapCam::SetHeight( float height )
+{
+ rAssert( height > 0.0f );
+ mMagnitude = height;
+}
+
diff --git a/game/code/presentation/gui/utility/hudmapcam.h b/game/code/presentation/gui/utility/hudmapcam.h
new file mode 100644
index 0000000..21db768
--- /dev/null
+++ b/game/code/presentation/gui/utility/hudmapcam.h
@@ -0,0 +1,40 @@
+//===========================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: HudMapCam
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2003/03/10 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef HUDMAPCAM_H
+#define HUDMAPCAM_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <camera/kullcam.h>
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+struct HudMapCam : public KullCam
+{
+ HudMapCam( int playerID );
+ virtual ~HudMapCam();
+
+ void Update( unsigned int milliseconds );
+ void SetHeight( float height );
+
+ tPointCamera* m_camera;
+ rmt::Vector m_originalHeading;
+
+};
+
+#endif // HUDMAPCAM_H
diff --git a/game/code/presentation/gui/utility/numerictext.cpp b/game/code/presentation/gui/utility/numerictext.cpp
new file mode 100644
index 0000000..6dafdcc
--- /dev/null
+++ b/game/code/presentation/gui/utility/numerictext.cpp
@@ -0,0 +1,27 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: NumericText
+//
+// Description: Implementation of the NumericText class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/19 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/utility/numerictext.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
diff --git a/game/code/presentation/gui/utility/numerictext.h b/game/code/presentation/gui/utility/numerictext.h
new file mode 100644
index 0000000..04bf24c
--- /dev/null
+++ b/game/code/presentation/gui/utility/numerictext.h
@@ -0,0 +1,197 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: NumericText
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/07 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef NUMERICTEXT_H
+#define NUMERICTEXT_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <raddebug.hpp> // Foundation
+#include <group.h>
+#include <text.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+namespace Scrooby
+{
+ class Group;
+ class Page;
+ class Text;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+template <class Digit>
+struct NumericText
+{
+ enum eDigits
+ {
+ DIGIT_ONES,
+ DIGIT_TENS,
+ DIGIT_HUNDREDS,
+
+ MAX_NUM_DIGITS
+ };
+
+ enum eAlignment
+ {
+ ALIGN_RIGHT,
+ ALIGN_LEFT,
+
+ NUM_ALIGNMENTS
+ };
+
+ Digit* m_digits[ MAX_NUM_DIGITS ];
+ Scrooby::Group* m_group;
+ int m_numDigits;
+ bool m_showLeadingZeros;
+ eAlignment m_alignment;
+
+ NumericText()
+ : m_group( NULL ),
+ m_numDigits( 0 ),
+ m_showLeadingZeros( false ),
+ m_alignment( ALIGN_RIGHT )
+ {
+ memset( m_digits, 0, sizeof( m_digits ) );
+ }
+
+ void SetScroobyText( Scrooby::Group* pGroup, const char* name )
+ {
+ m_group = pGroup;
+ m_numDigits = 0;
+
+ char objectName[ 32 ];
+ for( int i = 0; i < MAX_NUM_DIGITS; i++ )
+ {
+ sprintf( objectName, "%s_%d", name, i );
+
+ Digit* pDrawable = dynamic_cast<Digit*>( pGroup->GetText( objectName ) );
+ if( pDrawable == NULL )
+ {
+ pDrawable = dynamic_cast<Digit*>( pGroup->GetSprite( objectName ) );
+ }
+
+ if( pDrawable != NULL )
+ {
+ m_digits[ i ] = pDrawable;
+
+ // increment number of digits
+ //
+ m_numDigits++;
+ }
+ else
+ {
+ // break, assuming no more digits to follow
+ //
+ break;
+ }
+ }
+
+ // set value to zero by default
+ //
+ this->SetValue( 0 );
+ }
+
+ void SetValue( unsigned int value, unsigned int offset = 0 )
+ {
+ for( int i = 0; i < m_numDigits; i++ )
+ {
+ // set current digit value
+ rAssert( m_digits[ i ] );
+
+ m_digits[ i ]->SetIndex( value % 10 + offset );
+
+ // show leading zeros, if enabled
+ if( m_showLeadingZeros )
+ {
+ m_digits[ i ]->SetVisible( true );
+ }
+ else
+ {
+ m_digits[ i ]->SetVisible( value > 0 || i == 0 );
+ }
+
+ // shift value one digit to the right
+ value = value / 10;
+ }
+
+ if( m_alignment == ALIGN_LEFT )
+ {
+ // align numeric text to the left (TC: Can this be improved??)
+ for( int j = m_numDigits - 1; j >= 0; j-- )
+ {
+ if( m_digits[ j ]->IsVisible() )
+ {
+ // found leading digit = j, now shift digits to the left
+ //
+ for( int k = m_numDigits - 1; k >= 0; k-- )
+ {
+ if( j >= 0 )
+ {
+ m_digits[ k ]->SetIndex( m_digits[ j ]->GetIndex() );
+ m_digits[ k ]->SetVisible( true );
+
+ j--;
+ }
+ else
+ {
+ m_digits[ k ]->SetVisible( false );
+ }
+ }
+
+ // done, break out of loop
+ break;
+ }
+ }
+ }
+ }
+
+ void SetVisible( bool isVisible )
+ {
+ if( m_group != NULL )
+ {
+ m_group->SetVisible( isVisible );
+ }
+ else
+ {
+ for( int i = 0; i < MAX_NUM_DIGITS; i++ )
+ {
+ if( m_digits[ i ] != NULL )
+ {
+ m_digits[ i ]->SetVisible( isVisible );
+ }
+ }
+ }
+ }
+
+ void SetColour( tColour colour )
+ {
+ for( int i = 0; i < MAX_NUM_DIGITS; i++ )
+ {
+ if( m_digits[ i ] != NULL )
+ {
+ m_digits[ i ]->SetColour( colour );
+ }
+ }
+ }
+
+};
+
+#endif // NUMERICTEXT_H
diff --git a/game/code/presentation/gui/utility/scrollingtext.cpp b/game/code/presentation/gui/utility/scrollingtext.cpp
new file mode 100644
index 0000000..3c44e8c
--- /dev/null
+++ b/game/code/presentation/gui/utility/scrollingtext.cpp
@@ -0,0 +1,280 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ScrollingText
+//
+// Description: Implementation of the ScrollingText class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/11/21 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <p3d/font.hpp>
+#include <presentation/gui/utility/scrollingtext.h>
+
+#include <raddebug.hpp> // Foundation
+#include <app.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// ScrollingText::ScrollingText
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+ScrollingText::ScrollingText( Scrooby::Text* pText )
+: m_pText( pText ),
+ m_originalString( NULL ),
+ m_x0( 0 ),
+ m_y0( 0 ),
+ m_x1( 0 ),
+ m_y1( 0 ),
+ m_currentPos( 0.0f ),
+ m_state( STATE_IDLE ),
+ m_numPixelsPerSecond( 90 ),
+ m_isCyclic( false )
+{
+ rAssert( m_pText != NULL );
+
+ // set text mode to clip on right boundary
+ //
+ m_pText->SetTextMode( Scrooby::TEXT_CLIP );
+
+ // get scroll box boundary
+ //
+ m_pText->GetBoundingBox( m_x0, m_y0, m_x1, m_y1 );
+
+ // hide text until scrolling is started
+ //
+ m_pText->SetVisible( false );
+
+ // save reference to original string buffer
+ //
+ m_originalString = m_pText->GetStringBuffer();
+
+/*
+ // save copy of original string
+ //
+ P3D_UNICODE* originalString = static_cast<P3D_UNICODE*>( m_pText->GetStringBuffer() );
+ rAssert( originalString != NULL );
+ p3d::UnicodeStrCpy( originalString,
+ m_originalString,
+ p3d::UnicodeStrLen( originalString ) + 1 );
+*/
+}
+
+//===========================================================================
+// ScrollingText::~ScrollingText
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+ScrollingText::~ScrollingText()
+{
+}
+
+void
+ScrollingText::RestoreText()
+{
+ // restore position and size
+ //
+ m_pText->SetPosition( m_x0, m_y0 );
+ m_pText->SetBoundingBoxSize( m_x1 - m_x0, m_y1 - m_y0 );
+
+ // restore original string buffer
+ //
+ m_pText->SetStringBuffer( m_originalString );
+
+ // restore visibility
+ //
+ m_pText->SetVisible( true );
+}
+
+void
+ScrollingText::SetTextIndex( int index )
+{
+ rAssert( m_state == STATE_IDLE );
+
+ m_pText->SetIndex( index );
+
+ // update reference to original string buffer
+ //
+ m_originalString = m_pText->GetStringBuffer();
+}
+
+void
+ScrollingText::Start()
+{
+ m_state = STATE_SCROLLING;
+
+ m_currentPos = (float)m_x1;
+ this->ClipText( (int)m_currentPos, m_y0 );
+
+ // show text
+ //
+ m_pText->SetVisible( true );
+}
+
+void
+ScrollingText::Stop()
+{
+ m_state = STATE_IDLE;
+
+ m_pText->SetPosition( m_x0, m_y0 );
+
+ // restore original string buffer
+ //
+ m_pText->SetStringBuffer( m_originalString );
+
+ // hide text
+ //
+ m_pText->SetVisible( false );
+
+/*
+ P3D_UNICODE* originalString = static_cast<P3D_UNICODE*>( m_pText->GetStringBuffer() );
+ rAssert( originalString != NULL );
+ p3d::UnicodeStrCpy( m_originalString,
+ originalString,
+ p3d::UnicodeStrLen( m_originalString ) + 1 );
+*/
+}
+
+void
+ScrollingText::Update( unsigned int elapsedTime )
+{
+ if( m_state == STATE_SCROLLING )
+ {
+ // advance text towards the left
+ //
+ m_currentPos -= (m_numPixelsPerSecond * elapsedTime) / 1000.0f;
+
+ // apply text clipping for current position
+ //
+ this->ClipText( (int)m_currentPos, m_y0 );
+ }
+}
+
+//===========================================================================
+// Private Member Functions
+//===========================================================================
+
+void
+ScrollingText::ClipText( int x, int y )
+{
+ if( x >= m_x1 ) // *** origin beyond right boundary
+ {
+ m_pText->SetPosition( x, y );
+ m_pText->SetBoundingBoxSize( 0, m_y1 - y );
+ }
+ else if( x >= m_x0 ) // *** origin within both boundaries
+ {
+ m_pText->SetPosition( x, y );
+ m_pText->SetBoundingBoxSize( m_x1 - x, m_y1 - y );
+
+#ifdef RAD_WIN32
+ // TC: for PC sku, the source fonts are actually twice as big (for higher resolution display),
+ // so we need to stretch the bounding box accordingly for proper text clipping
+ //
+ m_pText->StretchBoundingBox( 2.0f, 1.0f );
+#endif
+ }
+ else // *** origin beyond left boundary
+ {
+ m_pText->SetBoundingBoxSize( m_x1 - m_x0, m_y1 - y );
+
+#ifdef RAD_WIN32
+ // TC: for PC sku, the source fonts are actually twice as big (for higher resolution display),
+ // so we need to stretch the bounding box accordingly for proper text clipping
+ //
+ m_pText->StretchBoundingBox( 2.0f, 1.0f );
+#endif
+
+ tFont* textFont = m_pText->GetFont();
+ if( textFont != NULL )
+ {
+ bool doneClipping = false;
+
+ for( UnicodeChar* currentString = m_pText->GetStringBuffer();
+ !doneClipping;
+ currentString++ )
+ {
+ UnicodeChar tempChar = currentString[ 0 ];
+ currentString[ 0 ] = '\0';
+
+ float clippedTextWidth = textFont->GetTextWidth( static_cast<P3D_UNICODE*>( m_originalString ) ) *
+ Scrooby::App::GetInstance()->GetScreenWidth() /
+ Scrooby::App::GetInstance()->GetScreenHeight();
+
+#ifdef RAD_WIN32
+ // TC: for PC sku, the source fonts are actually twice as big (for higher resolution display),
+ // so the width of the current text string is only half as wide
+ //
+ clippedTextWidth /= 2.0f;
+#endif
+
+ currentString[ 0 ] = tempChar;
+
+ int amountClipped = static_cast<int>( clippedTextWidth ) - (m_x0 - x);
+ if( amountClipped >= 0 )
+ {
+ m_pText->SetPosition( m_x0 + amountClipped, y );
+ m_pText->SetStringBuffer( currentString );
+ doneClipping = true;
+ }
+
+ if( currentString[ 0 ] == '\0' )
+ {
+ // done scrolling
+ //
+ this->Stop();
+
+ if( m_isCyclic )
+ {
+ // start over again
+ //
+ this->Start();
+ }
+
+ // make sure we break out of the loop; otherwise, there's
+ // a small chance that 'amountClipped' may be slightly less
+ // than 0 and so we might not be done clipping
+ //
+ break;
+ }
+ }
+ }
+ else
+ {
+ rWarningMsg( false, "No font for clipping text!" );
+
+ this->Stop();
+ }
+ }
+}
+
diff --git a/game/code/presentation/gui/utility/scrollingtext.h b/game/code/presentation/gui/utility/scrollingtext.h
new file mode 100644
index 0000000..c1f75c3
--- /dev/null
+++ b/game/code/presentation/gui/utility/scrollingtext.h
@@ -0,0 +1,110 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ScrollingText
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/11/21 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef SCROLLINGTEXT_H
+#define SCROLLINGTEXT_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <strings/unicodestring.h>
+
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Text;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class ScrollingText
+{
+public:
+ ScrollingText( Scrooby::Text* pText );
+ virtual ~ScrollingText();
+
+ void RestoreText();
+ void SetTextIndex( int index );
+
+ void Start();
+ void Stop();
+ void Pause();
+ void Resume();
+
+ void Update( unsigned int elapsedTime );
+
+ enum eScrollState
+ {
+ STATE_IDLE,
+ STATE_SCROLLING,
+ STATE_PAUSED,
+
+ NUM_SCROLL_STATES
+ };
+
+ eScrollState GetCurrentState() const { return m_state; }
+
+ void SetSpeed( int numPixelsPerSecond ) { m_numPixelsPerSecond = numPixelsPerSecond; }
+
+ void SetCyclic( bool isCyclic ) { m_isCyclic = isCyclic; }
+ bool IsCyclic() const { return m_isCyclic; }
+
+protected:
+ //---------------------------------------------------------------------
+ // Protected Functions
+ //---------------------------------------------------------------------
+
+ //---------------------------------------------------------------------
+ // Protected Data
+ //---------------------------------------------------------------------
+
+private:
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or asignment. Declare but don't define.
+ //
+ ScrollingText( const ScrollingText& );
+ ScrollingText& operator= ( const ScrollingText& );
+
+ void ClipText( int x, int y );
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ Scrooby::Text* m_pText;
+ UnicodeChar* m_originalString;
+
+ int m_x0;
+ int m_y0;
+ int m_x1;
+ int m_y1;
+ float m_currentPos;
+
+ eScrollState m_state;
+
+ int m_numPixelsPerSecond;
+ bool m_isCyclic;
+
+};
+
+#endif // SCROLLINGTEXT_H
diff --git a/game/code/presentation/gui/utility/slider.cpp b/game/code/presentation/gui/utility/slider.cpp
new file mode 100644
index 0000000..ac86521
--- /dev/null
+++ b/game/code/presentation/gui/utility/slider.cpp
@@ -0,0 +1,246 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Slider
+//
+// Description: Implementation of the Slider class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/19 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/utility/slider.h>
+
+#include <raddebug.hpp> // Foundation
+#include <polygon.h>
+#include <sprite.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+Slider::Slider( eSliderType type )
+: m_type( type ),
+ m_pPolygon( NULL ),
+ m_value( 1 ),
+ m_pImage( NULL ),
+ x0( 19820602 ), // some arbitrarily big integer
+ y0( 19780627 ), // some arbitrarily big integer
+ x1( 0 ),
+ y1( 0 )
+{
+}
+
+void
+Slider::SetScroobyPolygon( Scrooby::Polygon* pPolygon, Scrooby::Sprite* pImage )
+{
+ rAssert( pPolygon );
+ m_pPolygon = pPolygon;
+ m_pImage = pImage;
+
+ for( int i = 0; i < m_pPolygon->GetNumOfVertexes(); i++ )
+ {
+ int x = 0;
+ int y = 0;
+ m_pPolygon->GetVertexLocation( i, x, y );
+
+ if( x < x0 ) x0 = x;
+ if( y < y0 ) y0 = y;
+ if( x > x1 ) x1 = x;
+ if( y > y1 ) y1 = y;
+ }
+
+ m_pPolygon->SetVertexLocation( VERTEX_BOTTOM_LEFT, x0, y0 );
+ m_pPolygon->SetVertexLocation( VERTEX_TOP_LEFT, x0, y1 );
+ m_pPolygon->SetVertexLocation( VERTEX_TOP_RIGHT, x1, y1 );
+ m_pPolygon->SetVertexLocation( VERTEX_BOTTOM_RIGHT, x1, y0 );
+}
+
+void
+Slider::SetValue( float value )
+{
+ rAssert( value >= 0.0f && value <= 1.0f );
+ m_value = value;
+
+ rAssert( m_pPolygon );
+
+ int x = x0;
+ int y = y0;
+
+ switch( m_type )
+ {
+ case HORIZONTAL_SLIDER_LEFT:
+ {
+ x = x0 + static_cast<int>( (x1 - x0) * value );
+ m_pPolygon->SetVertexLocation( VERTEX_BOTTOM_RIGHT, x, y0 );
+ m_pPolygon->SetVertexLocation( VERTEX_TOP_RIGHT, x, y1 );
+
+ if( m_pImage != NULL )
+ {
+ m_pImage->ResetTransformation();
+ m_pImage->Translate( x - x1, 0 );
+ }
+
+ break;
+ }
+ case HORIZONTAL_SLIDER_RIGHT:
+ {
+ x = x1 + static_cast<int>( (x0 - x1) * value );
+ m_pPolygon->SetVertexLocation( VERTEX_BOTTOM_LEFT, x, y0 );
+ m_pPolygon->SetVertexLocation( VERTEX_TOP_LEFT, x, y1 );
+
+ if( m_pImage != NULL )
+ {
+ m_pImage->ResetTransformation();
+ m_pImage->Translate( x - x0, 0 );
+ }
+
+ break;
+ }
+ case VERTICAL_SLIDER_BOTTOM:
+ {
+ y = y0 + static_cast<int>( (y1 - y0) * value );
+ m_pPolygon->SetVertexLocation( VERTEX_TOP_LEFT, x0, y );
+ m_pPolygon->SetVertexLocation( VERTEX_TOP_RIGHT, x1, y );
+
+ if( m_pImage != NULL )
+ {
+ m_pImage->ResetTransformation();
+ m_pImage->Translate( 0, y - y1 );
+ }
+
+ break;
+ }
+ case VERTICAL_SLIDER_TOP:
+ {
+ y = y1 + static_cast<int>( (y0 - y1) * value );
+ m_pPolygon->SetVertexLocation( VERTEX_BOTTOM_LEFT, x0, y );
+ m_pPolygon->SetVertexLocation( VERTEX_BOTTOM_RIGHT, x1, y );
+
+ if( m_pImage != NULL )
+ {
+ m_pImage->ResetTransformation();
+ m_pImage->Translate( 0, y - y0 );
+ }
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( 0, "ERROR: *** Invalid slider type!" );
+
+ break;
+ }
+ }
+}
+
+//===========================================================================
+// Public Member Functions (for ImageSlider)
+//===========================================================================
+
+ImageSlider::ImageSlider( eSliderType type )
+: Slider( type )
+{
+}
+
+void
+ImageSlider::SetScroobyImage( Scrooby::Sprite* pImage )
+{
+ rAssert( pImage );
+ m_pImage = pImage;
+}
+
+void
+ImageSlider::SetValue( float value, bool resetTransformation )
+{
+ rAssert( value >= 0.0f && value <= 1.0f );
+ m_value = value;
+
+ rAssert( m_pImage );
+
+ if( resetTransformation )
+ {
+ m_pImage->ResetTransformation();
+ }
+
+ int posX = 0;
+ int posY = 0;
+ m_pImage->GetOriginPosition( posX, posY );
+
+ int width = 0;
+ int height = 0;
+ m_pImage->GetBoundingBoxSize( width, height );
+
+ switch( m_type )
+ {
+ case HORIZONTAL_SLIDER_LEFT:
+ {
+ m_pImage->Translate( -posX, 0 );
+ m_pImage->Scale( value, 1.0f, 1.0f );
+ m_pImage->Translate( posX, 0 );
+
+ break;
+ }
+ case HORIZONTAL_SLIDER_RIGHT:
+ {
+ m_pImage->Translate( -posX, 0 );
+ m_pImage->Scale( value, 1.0f, 1.0f );
+ m_pImage->Translate( posX, 0 );
+
+ // right align image
+ //
+ m_pImage->Translate( (int)( width * (1.0f - value) ), 0 );
+
+ break;
+ }
+ case HORIZONTAL_SLIDER_ABOUT_CENTER:
+ {
+ m_pImage->ScaleAboutCenter( value, 1.0f, 1.0f );
+
+ break;
+ }
+ case VERTICAL_SLIDER_BOTTOM:
+ {
+ m_pImage->Translate( 0, -posY );
+ m_pImage->Scale( 1.0f, value, 1.0f );
+ m_pImage->Translate( 0, posY );
+
+ break;
+ }
+ case VERTICAL_SLIDER_TOP:
+ {
+ m_pImage->Translate( 0, -posY );
+ m_pImage->Scale( 1.0f, value, 1.0f );
+ m_pImage->Translate( 0, posY );
+
+ // top align image
+ //
+ m_pImage->Translate( 0, (int)( height * (1.0f - value) ) );
+
+ break;
+ }
+ case VERTICAL_SLIDER_ABOUT_CENTER:
+ {
+ m_pImage->ScaleAboutCenter( 1.0f, value, 1.0f );
+
+ break;
+ }
+ default:
+ {
+ rAssertMsg( 0, "ERROR: *** Invalid slider type!" );
+
+ break;
+ }
+ }
+}
+
diff --git a/game/code/presentation/gui/utility/slider.h b/game/code/presentation/gui/utility/slider.h
new file mode 100644
index 0000000..31df284
--- /dev/null
+++ b/game/code/presentation/gui/utility/slider.h
@@ -0,0 +1,94 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Slider
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/07 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef SLIDER_H
+#define SLIDER_H
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Polygon;
+ class Sprite;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+struct Slider
+{
+ enum eSliderType
+ {
+ HORIZONTAL_SLIDER_LEFT,
+ HORIZONTAL_SLIDER_ABOUT_CENTER,
+ HORIZONTAL_SLIDER_RIGHT,
+
+ VERTICAL_SLIDER_BOTTOM,
+ VERTICAL_SLIDER_ABOUT_CENTER,
+ VERTICAL_SLIDER_TOP,
+
+ NUM_SLIDER_TYPES
+ };
+
+ eSliderType m_type;
+
+ enum eVertex
+ {
+ VERTEX_BOTTOM_LEFT,
+ VERTEX_TOP_LEFT,
+ VERTEX_TOP_RIGHT,
+ VERTEX_BOTTOM_RIGHT,
+
+ NUM_VERTICES
+ };
+
+ Scrooby::Polygon* m_pPolygon;
+ float m_value;
+
+ // optional image to tag onto the end of the slider
+ Scrooby::Sprite* m_pImage;
+
+ // bottom-left and top-right vertex co-ordinates
+ int x0;
+ int y0;
+ int x1;
+ int y1;
+
+ Slider( eSliderType type = HORIZONTAL_SLIDER_LEFT );
+ void SetScroobyPolygon( Scrooby::Polygon* pPolygon, Scrooby::Sprite* pImage = NULL );
+ virtual void SetValue( float value );
+
+};
+
+struct ImageSlider : public Slider
+{
+ ImageSlider( eSliderType type = HORIZONTAL_SLIDER_LEFT );
+
+ void SetScroobyImage( Scrooby::Sprite* pImage );
+ virtual void SetValue( float value, bool resetTransformation = true );
+
+};
+
+#endif // SLIDER_H
diff --git a/game/code/presentation/gui/utility/specialfx.cpp b/game/code/presentation/gui/utility/specialfx.cpp
new file mode 100644
index 0000000..b306653
--- /dev/null
+++ b/game/code/presentation/gui/utility/specialfx.cpp
@@ -0,0 +1,401 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SpecialFX
+//
+// Description: Implementation of the SpecialFX class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/30 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/utility/specialfx.h>
+#include <boundeddrawable.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+bool GuiSFX::Flash
+(
+ Scrooby::BoundedDrawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ int zoomRate,
+ float maxScale,
+ float thresholdScale
+)
+{
+ if( elapsedTime > durationTime )
+ {
+ return true;
+ }
+
+ rAssert( drawable != NULL );
+ rAssert( elapsedTime >= 0 && durationTime > 0 );
+
+ float scale = (float)elapsedTime / (float)durationTime;
+ for( int i = 0; i < zoomRate; i++ )
+ {
+ scale = rmt::Sqrt( scale );
+ }
+
+ scale *= maxScale;
+
+ drawable->ResetTransformation();
+ drawable->ScaleAboutCenter( scale, scale, 1.0f );
+
+ if( scale > thresholdScale )
+ {
+ drawable->SetAlpha( 1.0f - ((scale - thresholdScale) /
+ (maxScale - thresholdScale)) );
+ }
+ else
+ {
+ drawable->SetAlpha( 1.0f );
+ }
+
+ return false;
+}
+
+bool GuiSFX::Blink
+(
+ Scrooby::Drawable* drawable,
+ float elapsedTime,
+ float blinkingPeriod,
+ bool usingAlpha
+)
+{
+ rAssert( drawable );
+
+ if( elapsedTime > blinkingPeriod )
+ {
+ if( usingAlpha )
+ {
+ // toggle drawable visiblity (using alpha)
+ //
+ float newAlpha = (drawable->GetAlpha() > 0.5f) ? 0.0f : 1.0f;
+ drawable->SetAlpha( newAlpha );
+ }
+ else
+ {
+ // toggle drawable visibility
+ //
+ drawable->SetVisible( !drawable->IsVisible() );
+ }
+
+ return true;
+ }
+
+ return false;
+}
+
+float GuiSFX::Pulse
+(
+ float elapsedTime,
+ float period,
+ float center,
+ float amplitude,
+ float thetaOffset
+)
+{
+ float theta = rmt::PI_2 * elapsedTime / period - thetaOffset;
+
+ return( amplitude * rmt::Sin( theta ) + center );
+}
+
+void GuiSFX::ModulateColour
+(
+ tColour* currentColour,
+ float elapsedTime,
+ float period,
+ tColour startColour,
+ tColour endColour,
+ float thetaOffset
+)
+{
+ float modValue = GuiSFX::Pulse( elapsedTime, period, 0.5f, 0.5f, thetaOffset );
+
+ rAssert( currentColour != NULL );
+
+ currentColour->SetRed( startColour.Red() +
+ (int)( modValue * (endColour.Red() - startColour.Red()) ) );
+
+ currentColour->SetGreen( startColour.Green() +
+ (int)( modValue * (endColour.Green() - startColour.Green()) ) );
+
+ currentColour->SetBlue( startColour.Blue() +
+ (int)( modValue * (endColour.Blue() - startColour.Blue()) ) );
+
+ currentColour->SetAlpha( startColour.Alpha() +
+ (int)( modValue * (endColour.Alpha() - startColour.Alpha()) ) );
+}
+
+float GuiSFX::Pendulum
+(
+ Scrooby::BoundedDrawable* drawable,
+ float deltaTime,
+ float length,
+ float currentAngle,
+ float initialAngle,
+ float gravity
+)
+{
+ rAssertMsg( 0, "WARNING: *** Not yet implemented!" );
+
+ return 0.0f;
+/*
+ rAssert( drawable );
+
+ float root = (2 * gravity / length) *
+ (rmt::Cos( initialAngle ) - rmt::Cos( currentAngle ));
+
+ int rootSign = root > 0 ? 1 : -1;
+
+ // update current angle
+ if( currentAngle > 0.0f )
+ {
+ currentAngle -= rmt::Sqrt( root * rootSign )
+ * (deltaTime / 1000.0f);
+ }
+ else
+ {
+ currentAngle += rmt::Sqrt( root * rootSign )
+ * (deltaTime / 1000.0f);
+ }
+
+ // rotate pendulum arm
+ drawable->ResetTransformation();
+ drawable->RotateAboutCenter( rmt::RadianToDeg( currentAngle ) );
+
+ return currentAngle;
+*/
+}
+
+bool GuiSFX::Spiral
+(
+ Scrooby::BoundedDrawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ float rotationPeriod,
+ float startScale,
+ float endScale,
+ bool withFading
+)
+{
+ if( elapsedTime > durationTime )
+ {
+ drawable->ResetTransformation();
+ drawable->ScaleAboutCenter( endScale );
+
+ if( withFading )
+ {
+ drawable->SetAlpha( 1.0f );
+ }
+
+ return true;
+ }
+
+ float spiralValue = elapsedTime / durationTime;
+
+ // calculate current scale factor
+ //
+ float scale = startScale +
+ spiralValue * (endScale - startScale);
+
+ // slow down rotation when nearing end of spiral effect
+ //
+ rotationPeriod += spiralValue * rotationPeriod;
+
+ // calculate current rotation angle
+ //
+ float rotation = elapsedTime / rotationPeriod * rmt::PI_2;
+
+ // scale and rotate drawable
+ //
+ rAssert( drawable );
+ drawable->ResetTransformation();
+ drawable->ScaleAboutCenter( scale );
+ drawable->RotateAboutCenter( rmt::RadianToDeg( rotation ) );
+
+ // if enabled, fade in the drawable as well
+ if( withFading )
+ {
+ drawable->SetAlpha( spiralValue );
+ }
+
+ return false;
+}
+
+bool GuiSFX::SlideX
+(
+ Scrooby::Drawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ bool slideInwards,
+ int fromBorder,
+ int screenWidth
+)
+{
+ rAssert( drawable );
+
+ if( elapsedTime > durationTime )
+ {
+ drawable->ResetTransformation();
+
+ return true;
+ }
+
+ int posX = 0;
+ int posY = 0;
+ int width = 0;
+ int height = 0;
+ drawable->GetOriginPosition( posX, posY );
+ drawable->GetBoundingBoxSize( width, height );
+
+ int distanceX = (fromBorder > 0) ? (screenWidth - posX) : (posX + width);
+
+ float translationX = slideInwards ?
+ (1.0f - elapsedTime / durationTime) * distanceX :
+ (elapsedTime / durationTime) * distanceX;
+
+ drawable->ResetTransformation();
+ drawable->Translate( static_cast<int>( translationX ), 0 );
+
+ return false;
+}
+
+bool GuiSFX::SlideY
+(
+ Scrooby::Drawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ bool slideInwards,
+ int fromBorder,
+ int screenHeight
+)
+{
+ rAssert( drawable );
+
+ if( elapsedTime > durationTime )
+ {
+ drawable->ResetTransformation();
+
+ return true;
+ }
+
+ int posX = 0;
+ int posY = 0;
+ int width = 0;
+ int height = 0;
+ drawable->GetOriginPosition( posX, posY );
+ drawable->GetBoundingBoxSize( width, height );
+
+ int distanceY = (fromBorder > 0) ? (screenHeight - posY) : (posY + height);
+
+ float translationY = slideInwards ?
+ (1.0f - elapsedTime / durationTime) * distanceY :
+ (elapsedTime / durationTime) * distanceY;
+
+ drawable->ResetTransformation();
+ drawable->Translate( 0, static_cast<int>( translationY ) );
+
+ return false;
+}
+
+bool GuiSFX::Flip
+(
+ Scrooby::BoundedDrawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ float startAngle,
+ float endAngle,
+ rmt::Vector axis
+)
+{
+ rAssert( drawable );
+ drawable->ResetTransformation();
+
+ if( elapsedTime > durationTime )
+ {
+ return true;
+ }
+
+ // calculate current rotation angle
+ float rotation = startAngle +
+ elapsedTime / durationTime * (endAngle - startAngle);
+
+ // rotate the drawable about specified axis
+ drawable->RotateAboutCenter( rmt::RadianToDeg( rotation ), axis );
+
+ return false;
+}
+
+void GuiSFX::Projectile
+(
+ Scrooby::Drawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ rmt::Vector initVelocity,
+ bool reverse,
+ float gravity
+)
+{
+ if( reverse )
+ {
+ // reverse the elapsed time
+ //
+ elapsedTime = durationTime - elapsedTime;
+ }
+
+ // calculate the current trajectory position
+ //
+ rmt::Vector currentPos;
+ currentPos.x = initVelocity.x * elapsedTime;
+ currentPos.y = initVelocity.y * elapsedTime +
+ 0.5f * gravity * elapsedTime * elapsedTime;
+ currentPos.z = 0.0f;
+
+ // translate the drawable
+ //
+ rAssert( drawable );
+ drawable->Translate( static_cast<int>( currentPos.x ),
+ static_cast<int>( currentPos.y ) );
+}
+
+void GuiSFX::Projectile
+(
+ Scrooby::Drawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ rmt::Vector start,
+ rmt::Vector end,
+ bool reverse,
+ float gravity
+)
+{
+ // calculate the initial velocity
+ //
+ rmt::Vector initVelocity;
+ initVelocity.x = (end.x - start.x) / durationTime;
+ initVelocity.y = (end.y - start.y - 0.5f * gravity * durationTime * durationTime) / durationTime;
+ initVelocity.z = 0.0f;
+
+ GuiSFX::Projectile( drawable,
+ elapsedTime,
+ durationTime,
+ initVelocity,
+ reverse,
+ gravity );
+}
+
diff --git a/game/code/presentation/gui/utility/specialfx.h b/game/code/presentation/gui/utility/specialfx.h
new file mode 100644
index 0000000..12fc6fe
--- /dev/null
+++ b/game/code/presentation/gui/utility/specialfx.h
@@ -0,0 +1,166 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SpecialFX
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/30 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef SPECIALFX_H
+#define SPECIALFX_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <p3d/p3dtypes.hpp>
+#include <radmath/radmath.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Drawable;
+ class BoundedDrawable;
+}
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+namespace GuiSFX
+{
+
+bool Flash
+(
+ Scrooby::BoundedDrawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ int zoomRate = 0, // the higher the number, the faster the zoom
+ float maxScale = 1.5f,
+ float thresholdScale = 1.0f
+);
+
+bool Blink
+(
+ Scrooby::Drawable* drawable,
+ float elapsedTime,
+ float blinkingPeriod = 1000.0f,
+ bool usingAlpha = false
+);
+
+float Pulse
+(
+ float elapsedTime,
+ float period,
+ float center = 0.0f,
+ float amplitude = 1.0f,
+ float thetaOffset = 0.0f
+);
+
+void ModulateColour
+(
+ tColour* currentColour,
+ float elapsedTime,
+ float period,
+ tColour startColour,
+ tColour endColour,
+ float thetaOffset = 0.0f
+);
+
+float Pendulum
+(
+ Scrooby::BoundedDrawable* drawable,
+ float deltaTime,
+ float length,
+ float currentAngle,
+ float initialAngle,
+ float gravity = 9.81f
+);
+
+bool Spiral
+(
+ Scrooby::BoundedDrawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ float rotationPeriod,
+ float startScale = 0.0f,
+ float endScale = 1.0f,
+ bool withFading = true
+);
+
+enum eSlideBorderX
+{
+ SLIDE_BORDER_LEFT = -1,
+ SLIDE_BORDER_RIGHT = 1
+};
+
+bool SlideX
+(
+ Scrooby::Drawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ bool slideInwards,
+ int fromBorder,
+ int screenWidth = 640
+);
+
+enum eSlideBorderY
+{
+ SLIDE_BORDER_BOTTOM = -1,
+ SLIDE_BORDER_TOP = 1
+};
+
+bool SlideY
+(
+ Scrooby::Drawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ bool slideInwards,
+ int fromBorder,
+ int screenHeight = 480
+);
+
+bool Flip
+(
+ Scrooby::BoundedDrawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ float startAngle = 0.0f,
+ float endAngle = rmt::PI_2,
+ rmt::Vector axis = rmt::Vector( 1, 0, 0 )
+);
+
+void Projectile
+(
+ Scrooby::Drawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ rmt::Vector initVelocity,
+ bool reverse = false,
+ float gravity = 0.005f
+);
+
+void Projectile
+(
+ Scrooby::Drawable* drawable,
+ float elapsedTime,
+ float durationTime,
+ rmt::Vector start,
+ rmt::Vector end,
+ bool reverse = false,
+ float gravity = 0.005f
+);
+
+} // GuiSFX namespace
+
+#endif // SPECIALFX_H
diff --git a/game/code/presentation/gui/utility/teletypetext.cpp b/game/code/presentation/gui/utility/teletypetext.cpp
new file mode 100644
index 0000000..fea1dc3
--- /dev/null
+++ b/game/code/presentation/gui/utility/teletypetext.cpp
@@ -0,0 +1,318 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CTeleTypeText
+//
+// Description: Implementation of the CTeleTypeText class.
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/19 TChu Created for SRR2
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <presentation/gui/utility/teletypetext.h>
+
+#include <raddebug.hpp> // Foundation
+#include <group.h>
+#include <text.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+//===========================================================================
+// CTeleTypeText::CTeleTypeText
+//===========================================================================
+// Description: Constructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CTeleTypeText::CTeleTypeText( Scrooby::Text* pText,
+ Scrooby::Group* pTextBox,
+ unsigned int numCharsPerSecond,
+ unsigned int numSecondsToDisplay )
+: m_state( STATE_INACTIVE ),
+ m_elapsedTime( 0 ),
+ m_teletypePeriod( 1000 / numCharsPerSecond ),
+ m_displayDuration( numSecondsToDisplay * 1000 ),
+ m_pText( pText ),
+ m_pTextBox( pTextBox ),
+ m_charBuffer( NULL ),
+ m_nextChar( 0 ),
+ m_currentCharIndex( 0 )
+{
+ rAssert( m_pText != NULL );
+
+ // hide by default
+ if( m_pTextBox != NULL )
+ {
+ m_pTextBox->SetVisible( false );
+ }
+ else
+ {
+ m_pText->SetVisible( false );
+ }
+
+ // wrap text by default
+ m_pText->SetTextMode( Scrooby::TEXT_WRAP );
+}
+
+//===========================================================================
+// CTeleTypeText::~CTeleTypeText
+//===========================================================================
+// Description: Destructor.
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//===========================================================================
+CTeleTypeText::~CTeleTypeText()
+{
+}
+
+//===========================================================================
+// CTeleTypeText::Update
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CTeleTypeText::Update( unsigned int elapsedTime )
+{
+ switch( m_state )
+ {
+ case STATE_TELETYPING:
+ {
+ if( m_elapsedTime >= m_teletypePeriod )
+ {
+ this->TypeNextCharacter();
+
+ m_elapsedTime = m_elapsedTime % m_teletypePeriod;
+ }
+
+ // update elapsed time
+ m_elapsedTime += elapsedTime;
+
+ break;
+ }
+ case STATE_DISPLAYING:
+ {
+ // 0 means display forever, until client hides it
+ //
+ if( m_displayDuration > 0 )
+ {
+ if( m_elapsedTime >= m_displayDuration )
+ {
+ this->HideMessage();
+ }
+
+ // update elapsed time
+ m_elapsedTime += elapsedTime;
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//===========================================================================
+// CTeleTypeText::DisplayMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CTeleTypeText::DisplayMessage()
+{
+// rAssertMsg( m_state == STATE_INACTIVE, "ERROR: Teletype message in progress!\n" );
+ if( m_state != STATE_INACTIVE )
+ {
+ // ignore request if there's already a message in progress
+ return;
+ }
+
+ rAssert( m_pText );
+
+ // show scrooby text
+ if( m_pTextBox != NULL )
+ {
+ m_pTextBox->SetVisible( true );
+ }
+ else
+ {
+ m_pText->SetVisible( true );
+ }
+
+ // get string buffer from text
+ m_charBuffer = m_pText->GetStringBuffer();
+
+ rAssert( m_charBuffer );
+
+ // reset current character index
+ m_currentCharIndex = 0;
+
+ // save next character and terminate buffer
+ m_nextChar = m_charBuffer[ m_currentCharIndex + 1 ];
+ m_charBuffer[ m_currentCharIndex + 1 ] = '\0';
+
+ m_state = STATE_TELETYPING;
+}
+
+//===========================================================================
+// CTeleTypeText::DisplayMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CTeleTypeText::DisplayMessage( int index )
+{
+ rAssert( m_pText );
+
+ if( index >= 0 && index < m_pText->GetNumOfStrings() )
+ {
+ m_pText->SetIndex( index );
+
+ this->DisplayMessage();
+ }
+ else
+ {
+ rAssertMsg( 0, "WARNING: Invalid index for teletype message!\n" );
+ }
+}
+
+//===========================================================================
+// CTeleTypeText::HideMessage
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CTeleTypeText::HideMessage()
+{
+ // if stopped during middle of teletyping
+ if( m_state == STATE_TELETYPING )
+ {
+ // restore character in buffer
+ m_charBuffer[ m_currentCharIndex + 1 ] = m_nextChar;
+ }
+
+ // hide scrooby text
+ if( m_pTextBox != NULL )
+ {
+ m_pTextBox->SetVisible( false );
+ }
+ else
+ {
+ m_pText->SetVisible( false );
+ }
+
+ // reset elapsed time
+ m_elapsedTime = 0;
+
+ m_state = STATE_INACTIVE;
+}
+
+void CTeleTypeText::Pause()
+{
+ if( m_state == STATE_TELETYPING )
+ {
+ // restore character in buffer
+ m_charBuffer[ m_currentCharIndex + 1 ] = m_nextChar;
+
+ // hide scrooby text
+ m_pText->SetVisible( false );
+
+ m_state = STATE_PAUSED;
+ }
+}
+
+void CTeleTypeText::Resume()
+{
+ if( m_state == STATE_PAUSED )
+ {
+ // restore character in buffer
+ m_charBuffer[ m_currentCharIndex + 1 ] = '\0';
+
+ // show scrooby text
+ m_pText->SetVisible( true );
+
+ m_state = STATE_TELETYPING;
+ }
+}
+
+//===========================================================================
+// CTeleTypeText::TypeNextCharacter
+//===========================================================================
+// Description:
+//
+// Constraints: None.
+//
+// Parameters: None.
+//
+// Return:
+//
+//===========================================================================
+void CTeleTypeText::TypeNextCharacter()
+{
+ // increment current character index
+ m_currentCharIndex++;
+
+ // restore character in buffer
+ m_charBuffer[ m_currentCharIndex ] = m_nextChar;
+
+ // save next character and terminate buffer
+ m_nextChar = m_charBuffer[ m_currentCharIndex + 1 ];
+ m_charBuffer[ m_currentCharIndex + 1 ] = '\0';
+
+ // if end of buffer reached
+ if( m_nextChar == '\0' )
+ {
+ m_state = STATE_DISPLAYING;
+ }
+}
+
+//===========================================================================
+// Protected Member Functions
+//===========================================================================
diff --git a/game/code/presentation/gui/utility/teletypetext.h b/game/code/presentation/gui/utility/teletypetext.h
new file mode 100644
index 0000000..ebf9eae
--- /dev/null
+++ b/game/code/presentation/gui/utility/teletypetext.h
@@ -0,0 +1,120 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: CTeleTypeText
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/07/19 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef TELETYPETEXT_H
+#define TELETYPETEXT_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <p3d/p3dtypes.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Text;
+ class Group;
+}
+
+const unsigned int DEFAULT_TELETYPING_RATE = 8; // in characters/sec
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+class CTeleTypeText
+{
+ public:
+
+ CTeleTypeText( Scrooby::Text* pText,
+ Scrooby::Group* pTextBox = NULL,
+ unsigned int numCharsPerSecond = DEFAULT_TELETYPING_RATE,
+ unsigned int numSecondsToDisplay = 0 );
+ virtual ~CTeleTypeText();
+
+ // update teletype text
+ void Update( unsigned int elapsedTime );
+
+ // display message and start teletyping
+ void DisplayMessage();
+ void DisplayMessage( int index );
+
+ // hide message and stop teletyping if still in progress
+ void HideMessage();
+
+ // pause/resume teletyping
+ void Pause();
+ void Resume();
+
+ // accessor to scrooby text object
+ Scrooby::Text* GetText() const
+ {
+ return m_pText;
+ }
+
+ protected:
+
+ //---------------------------------------------------------------------
+ // Protected Functions
+ //---------------------------------------------------------------------
+
+ //---------------------------------------------------------------------
+ // Protected Data
+ //---------------------------------------------------------------------
+
+ private:
+
+ //---------------------------------------------------------------------
+ // Private Functions
+ //---------------------------------------------------------------------
+
+ // No copying or asignment. Declare but don't define.
+ //
+ CTeleTypeText( const CTeleTypeText& );
+ CTeleTypeText& operator= ( const CTeleTypeText& );
+
+ void TypeNextCharacter();
+
+ //---------------------------------------------------------------------
+ // Private Data
+ //---------------------------------------------------------------------
+
+ enum eTeletypeState
+ {
+ STATE_TELETYPING,
+ STATE_DISPLAYING,
+ STATE_PAUSED,
+
+ STATE_INACTIVE
+ };
+
+ eTeletypeState m_state;
+
+ unsigned int m_elapsedTime;
+ unsigned int m_teletypePeriod;
+ unsigned int m_displayDuration;
+
+ Scrooby::Text* m_pText;
+ Scrooby::Group* m_pTextBox;
+ P3D_UNICODE* m_charBuffer;
+ P3D_UNICODE m_nextChar;
+ int m_currentCharIndex;
+
+};
+
+#endif // TELETYPETEXT_H
diff --git a/game/code/presentation/gui/utility/transitions.cpp b/game/code/presentation/gui/utility/transitions.cpp
new file mode 100644
index 0000000..aba8d4b
--- /dev/null
+++ b/game/code/presentation/gui/utility/transitions.cpp
@@ -0,0 +1,2911 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Transitions.cpp
+//
+// Description: Some transitions for the hud/frontned
+//
+// Authors: Ian Gipson
+//
+//
+//===========================================================================
+
+//===========================================================================
+// Includes
+//===========================================================================
+#include <boundeddrawable.h>
+#include <contexts/gameplay/gameplaycontext.h>
+#include <events/eventmanager.h>
+#include <input/inputmanager.h>
+#include <memory/classsizetracker.h>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/utility.hpp>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiwindow.h>
+#include <presentation/gui/utility/colourutility.h>
+#include <presentation/gui/utility/transitions.h>
+#include <radmath/trig.hpp>
+#include <sprite.h>
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+#define WIDESCREEN_EXTRA_PIXELS 107
+//===========================================================================
+// Public Member Functions
+//===========================================================================
+
+namespace GuiSFX
+{
+
+#ifdef DEBUGWATCH
+ void ActivateCallback( void* userData )
+ {
+ Transition* transition = reinterpret_cast< Transition* >( userData );
+ Chainable* chainable = dynamic_cast< Chainable* >( transition );
+ if( chainable != NULL )
+ {
+ chainable->ResetChain();
+ chainable->Activate();
+ }
+ else
+ {
+ transition->Reset();
+ transition->Activate();
+ }
+ }
+
+ void DeativateCallback( void* userData )
+ {
+ Transition* transition = reinterpret_cast< Transition* >( userData );
+ transition->Deactivate();
+ }
+
+#endif
+
+//==============================================================================
+// Chainable::Chainable
+//==============================================================================
+// Description: constructor
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Chainable::Chainable()
+{
+}
+
+//==============================================================================
+// Chainable::Chainable
+//==============================================================================
+// Description: constructor
+//
+// Parameters: name - the name of the transition.
+//
+// Return: N/A.
+//
+//==============================================================================
+Chainable::Chainable( const tName& name ):
+ Transition( name )
+{
+ //CLASSTRACKER_CREATE( Chainable );
+}
+
+
+//==============================================================================
+// Chainable1::Chainable1
+//==============================================================================
+// Description: constructor
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Chainable1::Chainable1():
+ m_NextTransition( NULL )
+{
+ //nothing
+}
+
+//==============================================================================
+// Chainable1::Chainable1
+//==============================================================================
+// Description: constructor
+//
+// Parameters: name - the name of the object
+//
+// Return: N/A.
+//
+//==============================================================================
+Chainable1::Chainable1( const tName& name )
+ : Chainable( name )
+{
+ //nothing
+}
+
+//==============================================================================
+// Chainable1::ContinueChain
+//==============================================================================
+// Description: moves on to the next transition in the chain
+//
+// Parameters: none
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable1::ContinueChain()
+{
+ Deactivate();
+ if( m_NextTransition != NULL )
+ {
+ m_NextTransition->Activate();
+ }
+}
+
+//==============================================================================
+// Chainable1::DeactivateChain
+//==============================================================================
+// Description: shuts down the entire chain
+//
+// Parameters: none
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable1::DeactivateChain()
+{
+ Deactivate();
+ if( m_NextTransition != NULL )
+ {
+ m_NextTransition->DeactivateChain();
+ }
+}
+
+//==============================================================================
+// Chainable1::IsChainDone
+//==============================================================================
+// Description: determines if the entire chain of transitions is done
+//
+// Parameters: none
+//
+// Return: N/A.
+//
+//==============================================================================
+bool Chainable1::IsChainDone() const
+{
+ if( !IsDone() )
+ {
+ return false;
+ }
+
+ //
+ // Yay recursion!
+ //
+ if( m_NextTransition != NULL )
+ {
+ return m_NextTransition->IsChainDone();
+ }
+ return true;
+}
+
+//==============================================================================
+// Chainable1::operator=
+//==============================================================================
+// Description: assignment operator
+//
+// Parameters: right - the one we're assigning from
+//
+// Return: reference to self
+//
+//==============================================================================
+Chainable1& Chainable1::operator=( const Chainable1& right )
+{
+ if( this == &right )
+ {
+ return *this;
+ }
+ Transition::operator=( right );
+ m_NextTransition = right.m_NextTransition;
+ return *this;
+}
+
+//==============================================================================
+// Chainable1::ResetChain
+//==============================================================================
+// Description: resets the transition, and all subsequent transitions
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable1::ResetChain()
+{
+ Reset();
+ if( m_NextTransition != NULL )
+ {
+ return m_NextTransition->ResetChain();
+ }
+
+}
+
+//==============================================================================
+// Chainable1::SetNextTransition
+//==============================================================================
+// Description: sets the next transition in the chain
+//
+// Parameters: transition - the next transition to use
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable1::SetNextTransition( Chainable* transition )
+{
+ m_NextTransition = transition;
+}
+
+//==============================================================================
+// Chainable1::SetNextTransition
+//==============================================================================
+// Description: sets the next transition in the chain
+//
+// Parameters: transition - the next transition to use
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable1::SetNextTransition( Chainable& transition )
+{
+ m_NextTransition = &transition;
+}
+
+//==============================================================================
+// Chainable2::Chainable2()
+//==============================================================================
+// Description: consturctor
+//
+// Parameters: none
+//
+// Return: N/A.
+//
+//==============================================================================
+Chainable2::Chainable2()
+{
+ m_NextTransition[ 0 ] = NULL;
+ m_NextTransition[ 1 ] = NULL;
+}
+
+//==============================================================================
+// Chainable2::Chainable2()
+//==============================================================================
+// Description: consturctor
+//
+// Parameters: name - the name of the transition
+//
+// Return: N/A.
+//
+//==============================================================================
+Chainable2::Chainable2( const tName& name ):
+ Chainable( name )
+{
+ m_NextTransition[ 0 ] = NULL;
+ m_NextTransition[ 1 ] = NULL;
+}
+
+//==============================================================================
+// Chainable2::ContinueChain()
+//==============================================================================
+// Description: continues the chain of transitions
+//
+// Parameters: name - the name of the transition
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable2::ContinueChain()
+{
+ Deactivate();
+ int i;
+ for( i = 0; i < 2; ++i )
+ {
+ if( m_NextTransition[ i ] != NULL )
+ {
+ m_NextTransition[ i ]->Activate();
+ }
+ }
+}
+
+//==============================================================================
+// Chainable2::DeactivateChain()
+//==============================================================================
+// Description: shuts down the entire chain
+//
+// Parameters: none
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable2::DeactivateChain()
+{
+ Deactivate();
+ int i;
+ for( i = 0; i < 2; ++i )
+ {
+ if( m_NextTransition[ i ] != NULL )
+ {
+ m_NextTransition[ i ]->DeactivateChain();
+ }
+ }
+}
+
+//==============================================================================
+// Chainable2::IsChainDone
+//==============================================================================
+// Description: determines if the entire chain of transitions is done
+//
+// Parameters: none
+//
+// Return: N/A.
+//
+//==============================================================================
+bool Chainable2::IsChainDone() const
+{
+ if( !IsDone() )
+ {
+ return false;
+ }
+
+ //
+ // Yay recursion!
+ //
+ int i;
+ for( i = 0; i < 2; ++i )
+ {
+ if( m_NextTransition[ i ] != NULL )
+ {
+ bool nextDone = m_NextTransition[ i ]->IsChainDone();
+ if( !nextDone )
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+//==============================================================================
+// Chainable2::ResetChain
+//==============================================================================
+// Description: resets the transition, and all subsequent transitions
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable2::ResetChain()
+{
+ Reset();
+ if( m_NextTransition[ 0 ] != NULL )
+ {
+ m_NextTransition[ 0 ]->ResetChain();
+ }
+
+ if( m_NextTransition[ 1 ] != NULL )
+ {
+ m_NextTransition[ 1 ]->ResetChain();
+ }
+}
+
+//==============================================================================
+// Chainable2::SetNextTransition
+//==============================================================================
+// Description: sets one of the next transitions triggered by this one
+//
+// Parameters: index - which one of the next transitions to set
+// transition - pointer to the transition
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable2::SetNextTransition( const unsigned int index, Chainable* transition )
+{
+ rAssert( index < 2 );
+ m_NextTransition[ index ] = transition;
+}
+
+//==============================================================================
+// Chainable2::SetNextTransition
+//==============================================================================
+// Description: sets one of the next transitions triggered by this one
+//
+// Parameters: index - which one of the next transitions to set
+// transition - pointer to the transition
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable2::SetNextTransition( const unsigned int index, Chainable& transition )
+{
+ SetNextTransition( index, &transition );
+}
+
+//==============================================================================
+// Chainable3::Chainable3()
+//==============================================================================
+// Description: consturctor
+//
+// Parameters: none
+//
+// Return: N/A.
+//
+//==============================================================================
+Chainable3::Chainable3()
+{
+ m_NextTransition[ 0 ] = NULL;
+ m_NextTransition[ 1 ] = NULL;
+ m_NextTransition[ 2 ] = NULL;
+}
+
+//==============================================================================
+// Chainable3::Chainable3()
+//==============================================================================
+// Description: consturctor
+//
+// Parameters: name - the name of the transition
+//
+// Return: N/A.
+//
+//==============================================================================
+Chainable3::Chainable3( const tName& name ):
+ Chainable( name )
+{
+ m_NextTransition[ 0 ] = NULL;
+ m_NextTransition[ 1 ] = NULL;
+ m_NextTransition[ 2 ] = NULL;
+}
+
+//==============================================================================
+// Chainable3::ContinueChain()
+//==============================================================================
+// Description: continues the chain of transitions
+//
+// Parameters: name - the name of the transition
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable3::ContinueChain()
+{
+ Deactivate();
+ int i;
+ for( i = 0; i < 3; ++i )
+ {
+ if( m_NextTransition[ i ] != NULL )
+ {
+ m_NextTransition[ i ]->Activate();
+ }
+ }
+}
+
+//==============================================================================
+// Chainable3::DeactivateChain()
+//==============================================================================
+// Description: shuts down the entire chain
+//
+// Parameters: none
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable3::DeactivateChain()
+{
+ Deactivate();
+ int i;
+ for( i = 0; i < 3; ++i )
+ {
+ if( m_NextTransition[ i ] != NULL )
+ {
+ m_NextTransition[ i ]->DeactivateChain();
+ }
+ }
+}
+
+
+//==============================================================================
+// Chainable3::IsChainDone
+//==============================================================================
+// Description: determines if the entire chain of transitions is done
+//
+// Parameters: none
+//
+// Return: N/A.
+//
+//==============================================================================
+bool Chainable3::IsChainDone() const
+{
+ if( !IsDone() )
+ {
+ return false;
+ }
+
+ //
+ // Yay recursion!
+ //
+ int i;
+ for( i = 0; i < 3; ++i )
+ {
+ if( m_NextTransition[ i ] != NULL )
+ {
+ bool nextDone = m_NextTransition[ i ]->IsChainDone();
+ if( !nextDone )
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+//==============================================================================
+// Chainable3::ResetChain
+//==============================================================================
+// Description: resets the transition, and all subsequent transitions
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable3::ResetChain()
+{
+ Reset();
+ if( m_NextTransition[ 0 ] != NULL )
+ {
+ m_NextTransition[ 0 ]->ResetChain();
+ }
+
+ if( m_NextTransition[ 1 ] != NULL )
+ {
+ m_NextTransition[ 1 ]->ResetChain();
+ }
+
+ if( m_NextTransition[ 2 ] != NULL )
+ {
+ m_NextTransition[ 2 ]->ResetChain();
+ }
+}
+
+//==============================================================================
+// Chainable3::SetNextTransition
+//==============================================================================
+// Description: sets one of the next transitions triggered by this one
+//
+// Parameters: index - which one of the next transitions to set
+// transition - pointer to the transition
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable3::SetNextTransition( const unsigned int index, Chainable* transition )
+{
+ rAssert( index < 3 );
+ m_NextTransition[ index ] = transition;
+}
+
+//==============================================================================
+// Chainable3::SetNextTransition
+//==============================================================================
+// Description: sets one of the next transitions triggered by this one
+//
+// Parameters: index - which one of the next transitions to set
+// transition - pointer to the transition
+//
+// Return: N/A.
+//
+//==============================================================================
+void Chainable3::SetNextTransition( const unsigned int index, Chainable& transition )
+{
+ SetNextTransition( index, &transition );
+}
+
+//==============================================================================
+// ColorChange::ColorChange
+//==============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+ColorChange::ColorChange()
+{
+}
+
+//==============================================================================
+// ColorChange::ColorChange
+//==============================================================================
+// Description: constructor
+//
+// Parameters: name - the name of the object
+//
+// Return: N/A.
+//
+//==============================================================================
+ColorChange::ColorChange( const tName& name ):
+ Chainable1( name )
+{
+}
+
+//==============================================================================
+// ColorChange::SetStartColour
+//==============================================================================
+// Description: sets the starting color for the color transition
+//
+// Parameters: transition - the next transition to use
+//
+// Return: N/A.
+//
+//==============================================================================
+void ColorChange::SetStartColour( const tColour c )
+{
+ m_StartColor = c;
+}
+
+//==============================================================================
+// ColorChange::SetEndColour
+//==============================================================================
+// Description: sets the end color for the transition
+//
+// Parameters: transition - the next transition to use
+//
+// Return: N/A.
+//
+//==============================================================================
+void ColorChange::SetEndColour( const tColour c )
+{
+ m_EndColor = c;
+}
+
+//==============================================================================
+// ColorChange::Update
+//==============================================================================
+// Description: updates the color change transition
+//
+// Parameters: transition - the next transition to use
+//
+// Return: N/A.
+//
+//==============================================================================
+void ColorChange::Update( const float deltaT )
+{
+ if( this->IsActive() )
+ {
+ Transition::Update( deltaT );
+ float spillover = rmt::Max( m_ElapsedTime - m_TimeInterval, 0.0f );
+ m_ElapsedTime = rmt::Min( m_ElapsedTime, m_TimeInterval );
+
+ float percent = m_ElapsedTime / m_TimeInterval;
+ tColour color = m_StartColor * ( 1.0f - percent ) + m_EndColor * percent;
+ if( m_Drawable != NULL )
+ {
+ m_Drawable->SetColour( color );
+ }
+
+ if( spillover > 0 )
+ {
+ ContinueChain();
+ }
+ }
+}
+
+//==============================================================================
+// ColorChange::Watch
+//==============================================================================
+// Description: Adds this transition to the watcher
+//
+// Parameters: nameSpace - the name of the group in the watcher
+//
+// Return: N/A.
+//
+//==============================================================================
+#ifdef DEBUGWATCH
+void ColorChange::Watch( const char* nameSpace )
+{
+ char output[ 1024 ];
+ sprintf( output, "%s\\%s", nameSpace, m_Name.GetText() );
+ Parent1::Watch( output );
+ Parent2::Watch( output );
+}
+#endif
+
+//==============================================================================
+// Dummy::Dummy
+//==============================================================================
+// Description: Constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+Dummy::Dummy()
+{
+}
+
+//==============================================================================
+// Dummy::Dummy
+//==============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+Dummy::Dummy( const tName& name ):
+ Parent( name )
+{
+}
+
+//==============================================================================
+// Dummy::Activate
+//==============================================================================
+// Description: does nothing, and carrys on
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+void Dummy::Activate()
+{
+ Parent::Activate();
+ ContinueChain();
+}
+
+//=============================================================================
+// GotoScreen::GotoScreen()
+//=============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+GotoScreen::GotoScreen():
+ mScreen( CGuiWindow::GUI_WINDOW_ID_UNDEFINED ),
+ mParam1( 0 ),
+ mParam2( 0 ),
+ mWindowOptions( 0 )
+{
+ //nothing
+}
+
+//=============================================================================
+// GotoScreen::GotoScreen()
+//=============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+GotoScreen::GotoScreen( const tName& name ):
+ Parent( name ),
+ mScreen( CGuiWindow::GUI_WINDOW_ID_UNDEFINED ),
+ mParam1( 0 ),
+ mParam2( 0 ),
+ mWindowOptions( 0 )
+{
+ //nothing
+}
+
+//=============================================================================
+// SendEvent::Activate()
+//=============================================================================
+// Description: fires off the event that we're supposed to send at this point
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void GotoScreen::Activate()
+{
+ Transition::Activate();
+ rAssert( mScreen != CGuiWindow::GUI_WINDOW_ID_UNDEFINED );
+ GetGuiSystem()->GotoScreen( mScreen, mParam1, mParam2, mWindowOptions );
+ ContinueChain();
+}
+
+//=============================================================================
+// SendEvent::SetEventData
+//=============================================================================
+// Description: sets the event data that we're going to fire off later on
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void GotoScreen::SetEventData( void* eventData )
+{
+ mEventData = eventData;
+}
+
+//=============================================================================
+// GotoScreen::SetParam1
+//=============================================================================
+// Description: sets the event data that we're going to fire off later on
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void GotoScreen::SetParam1( const unsigned int param1 )
+{
+ mParam1 = param1;
+}
+
+//=============================================================================
+// GotoScreen::SetParam2
+//=============================================================================
+// Description: sets the event data that we're going to fire off later on
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void GotoScreen::SetParam2( const unsigned int param2 )
+{
+ mParam2 = param2;
+}
+
+//=============================================================================
+// SendEvent::SetEvent
+//=============================================================================
+// Description: sets the event that we're going to fire off later on
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void GotoScreen::SetScreen( CGuiWindow::eGuiWindowID screen )
+{
+ mScreen = screen;
+}
+
+//=============================================================================
+// GotoScreen::SetWindowOptions
+//=============================================================================
+// Description: sets the event data that we're going to fire off later on
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void GotoScreen::SetWindowOptions( const unsigned int windowOptions )
+{
+ mWindowOptions = windowOptions;
+}
+
+//==============================================================================
+// HasMulticontroller::ResetMultiControllerFrames
+//==============================================================================
+// Description: sometimes you screw up the nubmer of frames in a multicontoller
+// calling this function will fix things
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+void HasMulticontroller::ResetMultiControllerFrames()
+{
+ m_MultiController->SetFrameRange( 0.0f, m_NumFrames );
+}
+
+//==============================================================================
+// HasMulticontroller::SetMultiController
+//==============================================================================
+// Description: sets up the multicontroller that this transition requires
+//
+// Parameters: multicontroller - the multicontroller to use
+//
+// Return: N/A.
+//
+//==============================================================================
+void HasMulticontroller::SetMultiController( tMultiController* multicontroller )
+{
+ m_MultiController = multicontroller;
+ m_NumFrames = m_MultiController->GetNumFrames();
+}
+
+//==============================================================================
+// HasTimeInterval::HasTimeInterval
+//==============================================================================
+// Description: constructor
+//
+// Parameters: none
+//
+// Return: N/A.
+//
+//==============================================================================
+HasTimeInterval::HasTimeInterval():
+ m_TimeInterval( 1000.0f )
+{
+ //nothing
+}
+
+//==============================================================================
+// HasTimeInterval::SetTimeInterval
+//==============================================================================
+// Description: sets the time interval that this transition should wait
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void HasTimeInterval::SetTimeInterval( const float interval )
+{
+ m_TimeInterval = interval;
+}
+
+//==============================================================================
+// HasTimeInterval::Watch
+//==============================================================================
+// Description: Adds this transition to the watcher
+//
+// Parameters: nameSpace - the name of the group in the watcher
+//
+// Return: N/A.
+//
+//==============================================================================
+#ifdef DEBUGWATCH
+void HasTimeInterval::Watch( const char* nameSpace )
+{
+ radDbgWatchDelete( &m_TimeInterval );
+ radDbgWatchAddFloat( &m_TimeInterval, "timeInterval", nameSpace, NULL, NULL, 0.0f, 1000.0f );
+}
+#endif
+
+//==============================================================================
+// Hide::Hide
+//==============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+Hide::Hide()
+{
+ //nothing
+}
+
+//==============================================================================
+// Hide::Hide
+//==============================================================================
+// Description: constructor
+//
+// Parameters: name - the name of the object
+//
+// Return: N/A.
+//
+//==============================================================================
+Hide::Hide( const tName& name ):
+ Chainable1( name )
+{
+}
+
+//==============================================================================
+// Hide::Activate
+//==============================================================================
+// Description: this gets called when a hide transition is triggered
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void Hide::Activate()
+{
+ Transition::Activate();
+ rAssert( m_Drawable != NULL );
+ if( m_Drawable != NULL )
+ {
+ m_Drawable->SetVisible( false );
+ }
+ ContinueChain();
+}
+
+//==============================================================================
+// ImageCycler::SetDrawable
+//==============================================================================
+// Description: set the period for cycling images
+//
+// Parameters: period - float period in ms
+//
+// Return: N/A.
+//
+//==============================================================================
+void ImageCycler::SetDrawable( Scrooby::Drawable* drawable )
+{
+ Transition::SetDrawable( drawable );
+ m_Sprite = dynamic_cast< Scrooby::Sprite* >( m_Drawable );
+ rAssert( m_Sprite != NULL );
+}
+
+//==============================================================================
+// ImageCycler::SetDrawable
+//==============================================================================
+// Description: set the period for cycling images
+//
+// Parameters: period - float period in ms
+//
+// Return: N/A.
+//
+//==============================================================================
+void ImageCycler::SetDrawable( Scrooby::Drawable& drawable )
+{
+ Transition::SetDrawable( drawable );
+ m_Sprite = dynamic_cast< Scrooby::Sprite* >( m_Drawable );
+ rAssert( m_Sprite != NULL );
+}
+
+//==============================================================================
+// InputStateChange::InputStateChange
+//==============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+InputStateChange::InputStateChange():
+ Parent(),
+ mState( Input::ACTIVE_NONE )
+{
+ //nothing
+}
+
+//==============================================================================
+// InputStateChange::InputStateChange
+//==============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+InputStateChange::InputStateChange( const tName& name ):
+ Parent( name ),
+ mState( Input::ACTIVE_NONE )
+{
+ //nothing
+}
+
+//==============================================================================
+// InputStateChange::Activate
+//==============================================================================
+// Description: called when this transition is activated
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+void InputStateChange::Activate()
+{
+ Parent::Activate();
+ InputManager::GetInstance()->SetGameState( mState );
+ ContinueChain();
+}
+
+//==============================================================================
+// InputStateChange::SetState
+//==============================================================================
+// Description: set the state that this transition will take the input to
+//
+// Parameters: state - what input state should we go to?
+//
+// Return: N/A.
+//
+//==============================================================================
+void InputStateChange::SetState( const Input::ActiveState state )
+{
+ mState = state;
+}
+
+//==============================================================================
+// IrisWipeClose::IrisWipeClose
+//==============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+IrisWipeClose::IrisWipeClose()
+{
+ //none
+}
+
+//==============================================================================
+// IrisWipeClose::IrisWipeClose
+//==============================================================================
+// Description: constructor
+//
+// Parameters: name - the name of the object for debugging
+//
+// Return: N/A.
+//
+//==============================================================================
+IrisWipeClose::IrisWipeClose( const tName& name ):
+ Chainable1( name )
+{
+ //none
+}
+
+//==============================================================================
+// IrisWipeClose::Activate
+//==============================================================================
+// Description: starts the iris wipe transition
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+void IrisWipeClose::Activate()
+{
+ m_MultiController->Reset();
+ m_MultiController->SetFrameRange( 0, m_NumFrames * 0.5f );
+ m_MultiController->SetFrame( 0 );
+ Chainable1::Activate();
+}
+
+void IrisWipeClose::Deactivate()
+{
+ Chainable1::Deactivate();
+}
+
+//==============================================================================
+// IrisWipeOpen::Update
+//==============================================================================
+// Description: needed to check if the irisWipeTransition can move on
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+void IrisWipeClose::Update( const float deltaT )
+{
+ if( IsActive() )
+ {
+ Chainable1::Update( deltaT );
+ if( m_MultiController != NULL )
+ {
+ if( m_MultiController->LastFrameReached() )
+ {
+ m_MultiController->SetFrameRange( 0.0f, m_NumFrames );
+ ResetMultiControllerFrames();
+ ContinueChain();
+ }
+ }
+ }
+}
+
+//==============================================================================
+// IrisWipeOpen::IrisWipeOpen
+//==============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+IrisWipeOpen::IrisWipeOpen()
+{
+ //none
+}
+
+//==============================================================================
+// IrisWipeOpen::IrisWipeOpen
+//==============================================================================
+// Description: constructor
+//
+// Parameters: name - the name of the object for debugging
+//
+// Return: N/A.
+//
+//==============================================================================
+IrisWipeOpen::IrisWipeOpen( const tName& name ):
+ Chainable1( name )
+{
+ //none
+}
+
+//==============================================================================
+// IrisWipeOpen::Activate
+//==============================================================================
+// Description: starts the iris wipe transition
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+void IrisWipeOpen::Activate()
+{
+ tMultiController* multiController = p3d::find< tMultiController >( "IrisController" );
+ rAssert( m_MultiController != NULL );
+ SetMultiController( multiController );
+
+ m_MultiController->Reset();
+ m_MultiController->SetFrameRange( m_NumFrames * 0.5f, m_NumFrames );
+ m_MultiController->SetFrame( m_NumFrames * 0.5f );
+ Chainable1::Activate();
+}
+
+//==============================================================================
+// IrisWipeOpen::Update
+//==============================================================================
+// Description: needed to check if the irisWipeTransition can move on
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+void IrisWipeOpen::Update( const float deltaT )
+{
+ if( IsActive() )
+ {
+ if( m_MultiController->LastFrameReached() )
+ {
+ m_MultiController->SetFrameRange( 0.0f, m_NumFrames );
+ ResetMultiControllerFrames();
+ ContinueChain();
+ }
+ }
+}
+
+//==============================================================================
+// ImageCycler::SetPeriod
+//==============================================================================
+// Description: set the period for cycling images
+//
+// Parameters: period - float period in ms
+//
+// Return: N/A.
+//
+//==============================================================================
+void ImageCycler::SetPeriod( const float period )
+{
+ m_Period = period;
+}
+
+//==============================================================================
+// ImageCycler::Update
+//==============================================================================
+// Description: updates the transition animation
+//
+// Parameters: deltaT - the time elapsed
+//
+// Return: N/A.
+//
+//==============================================================================
+void ImageCycler::Update( const float deltaT )
+{
+ if( IsActive() )
+ {
+ Transition::Update( deltaT );
+ if( m_Sprite != NULL )
+ {
+ int nubmerOfStates = m_Sprite->GetNumOfImages();
+ int periodsElapsed = static_cast< int >( m_ElapsedTime / m_Period );
+ int imageNumber = periodsElapsed % nubmerOfStates;
+ m_Sprite->SetIndex( imageNumber );
+ }
+ }
+}
+
+//==============================================================================
+// Junction2::Activate()
+//==============================================================================
+// Description: activates all the transitions leaving this junction
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void Junction2::Activate()
+{
+ ContinueChain();
+}
+
+//==============================================================================
+// Junction3::Activate()
+//==============================================================================
+// Description: activates all the transitions leaving this junction
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void Junction3::Activate()
+{
+ ContinueChain();
+}
+
+//==============================================================================
+// Pause::Pause
+//==============================================================================
+// Description: constructor
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Pause::Pause()
+{
+ //nothing
+}
+
+//==============================================================================
+// Pause::Pause
+//==============================================================================
+// Description: constructor
+//
+// Parameters: name - the name of the object
+//
+// Return: N/A.
+//
+//==============================================================================
+Pause::Pause( const tName& name ):
+ Chainable1( name )
+{
+}
+
+//==============================================================================
+// Pause::Update
+//==============================================================================
+// Description: Updates the transition
+//
+// Parameters: elapsed time - how much time has passed
+//
+// Return: N/A.
+//
+//==============================================================================
+void Pause::Update( const float deltaT )
+{
+ if( IsActive() )
+ {
+ Transition::Update( deltaT );
+ float spillover = rmt::Max( m_ElapsedTime - m_TimeInterval, 0.0f );
+ m_ElapsedTime = rmt::Min( m_ElapsedTime, m_TimeInterval );
+ if( spillover > 0 )
+ {
+ ContinueChain();
+ }
+ }
+}
+
+//=============================================================================
+// Pause::Watch
+//=============================================================================
+// Description: adds the pause transition object to the watcher
+//
+// Parameters: deleaT - how much time has passed
+//
+// Return: N/A.
+//
+//=============================================================================
+#ifdef DEBUGWATCH
+void Pause::Watch( const char* nameSpace )
+{
+ char output[ 1024 ];
+ sprintf( output, "%s\\%s", nameSpace, m_Name.GetText() );
+ Parent1::Watch( output );
+ Parent2::Watch( output );
+}
+#endif
+
+//==============================================================================
+// PauseInFrames::PauseInFrames
+//==============================================================================
+// Description: constructor
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PauseInFrames::PauseInFrames():
+ m_FramesToPause( 1 ),
+ m_FramesElapsed( 0 )
+{
+ //nothing
+}
+
+//==============================================================================
+// Pause::Pause
+//==============================================================================
+// Description: constructor
+//
+// Parameters: name - the name of the object
+//
+// Return: N/A.
+//
+//==============================================================================
+PauseInFrames::PauseInFrames( const tName& name ):
+ Parent( name ),
+ m_FramesToPause( 1 ),
+ m_FramesElapsed( 0 )
+{
+ //nothing
+}
+
+//==============================================================================
+// PauseInFrames::Reset
+//==============================================================================
+// Description: Resets the transition
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+void PauseInFrames::Reset()
+{
+ Parent::Reset();
+ m_FramesElapsed = 0;
+}
+
+//==============================================================================
+// PauseInFrames::SetNumberOfFrames
+//==============================================================================
+// Description: How many frames should this transition pause for?
+//
+// Parameters: i - thenubmer of fraems to pause
+//
+// Return: N/A.
+//
+//==============================================================================
+void PauseInFrames::SetNumberOfFrames( const unsigned int i )
+{
+ m_FramesToPause = i;
+}
+
+//==============================================================================
+// PauseInFrames::Update
+//==============================================================================
+// Description: Updates the transition
+//
+// Parameters: elapsed time - how much time has passed
+//
+// Return: N/A.
+//
+//==============================================================================
+void PauseInFrames::Update( const float deltaT )
+{
+ if( IsActive() )
+ {
+ Parent::Update( deltaT );
+
+ if( deltaT > 0.0f )
+ {
+ ++m_FramesElapsed;
+ }
+
+ if( m_FramesElapsed == m_FramesToPause )
+ {
+ ContinueChain();
+ }
+ }
+}
+
+//==============================================================================
+// PauseGame::Activate
+//==============================================================================
+// Description: transition that pauses the game
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+void PauseGame::Activate()
+{
+ GameplayContext::GetInstance()->PauseAllButPresentation( true );
+ ContinueChain();
+}
+
+//==============================================================================
+// PulseScale::PulseScale
+//==============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+PulseScale::PulseScale():
+ Transition(),
+ m_BoundedDrawable( NULL ),
+ m_Amplitude( 0.25f ),
+ m_Frequency( 5.0f )
+{
+}
+
+//==============================================================================
+// PulseScale::PulseScale
+//==============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+PulseScale::PulseScale( const tName& name ):
+ Transition( name ),
+ m_BoundedDrawable( NULL ),
+ m_Amplitude( 0.25f ),
+ m_Frequency( 5.0f )
+{
+}
+
+//==============================================================================
+// PulseScale::MovesDrawable
+//==============================================================================
+// Description: does this transition move a drawable object?
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+bool PulseScale::MovesDrawable() const
+{
+ return true;
+}
+
+//==============================================================================
+// PulseScale::SetAmplitude
+//==============================================================================
+// Description: sets the amplitude of the pulsing
+//
+// Parameters: amplitude - the size of the pulsing
+//
+// Return: N/A.
+//
+//==============================================================================
+void PulseScale::SetAmplitude( const float amplitude )
+{
+ m_Amplitude = amplitude;
+}
+
+//==============================================================================
+// PulseScale::SetDrawable
+//==============================================================================
+// Description: sets the drawable that we'll be pulsing
+//
+// Parameters: drawable
+//
+// Return: N/A.
+//
+//==============================================================================
+void PulseScale::SetDrawable( Scrooby::Drawable& drawable )
+{
+ Transition::SetDrawable( drawable );
+ m_BoundedDrawable = dynamic_cast< Scrooby::BoundedDrawable* >( m_Drawable );
+ rAssert( m_BoundedDrawable != NULL );
+}
+
+//==============================================================================
+// PulseScale::SetDrawable
+//==============================================================================
+// Description: sets the drawable that we'll be pulsing
+//
+// Parameters: drawable
+//
+// Return: N/A.
+//
+//==============================================================================
+void PulseScale::SetDrawable( Scrooby::Drawable* drawable )
+{
+ Transition::SetDrawable( drawable );
+ m_BoundedDrawable = dynamic_cast< Scrooby::HasBoundingBox* >( m_Drawable );
+ rAssert( m_BoundedDrawable != NULL );
+}
+
+//==============================================================================
+// Pulse::SetFrequency
+//==============================================================================
+// Description: sets the frequency of the pulse effect (in Hz)
+//
+// Parameters: frequency - the frequency
+//
+// Return: N/A.
+//
+//==============================================================================
+void PulseScale::SetFrequency( const float frequency )
+{
+ m_Frequency = frequency;
+}
+
+//=============================================================================
+// Pulse::Update
+//=============================================================================
+// Description: causes the bitmap to pulse
+//
+// Parameters: deleaT - how much time has passed
+//
+// Return: N/A.
+//
+//=============================================================================
+void PulseScale::Update( const float deltaT )
+{
+ if( IsActive() )
+ {
+ Transition::Update( deltaT );
+ float period = 1000.0f / m_Frequency;
+ m_ElapsedTime = fmodf( m_ElapsedTime, period );
+ float sin = rmt::Sin( m_ElapsedTime * m_Frequency / 1000.0f * rmt::PI * 2 );
+ float scale = sin * m_Amplitude + 1.0f;
+ m_BoundedDrawable->ScaleAboutCenter( scale );
+ }
+}
+
+//=============================================================================
+// PulseScale::Watch
+//=============================================================================
+// Description: adds the pulse transition object to the watcher
+//
+// Parameters: deleaT - how much time has passed
+//
+// Return: N/A.
+//
+//=============================================================================
+#ifdef DEBUGWATCH
+void PulseScale::Watch( const char* nameSpace )
+{
+ Parent::Watch( nameSpace );
+ char output[ 1024 ];
+ sprintf( output, "%s\\%s", nameSpace, m_Name.GetText() );
+ radDbgWatchDelete( &m_Amplitude );
+ radDbgWatchDelete( &m_Frequency );
+ radDbgWatchAddFloat( &m_Amplitude, "Amplitude", output, NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &m_Frequency, "Frequency", output, NULL, NULL, 0.0f, rmt::PI * 4 );
+}
+#endif
+
+
+//==============================================================================
+// RecieveEvent::RecieveEvent
+//==============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+RecieveEvent::RecieveEvent():
+ Chainable1(),
+ mEvent( NUM_EVENTS )
+{
+ //nothing
+}
+
+//=============================================================================
+// RecieveEvent::RecieveEvent
+//=============================================================================
+// Description: constructor
+//
+// Parameters: name - the name of the object
+//
+// Return: N/A.
+//
+//=============================================================================
+RecieveEvent::RecieveEvent( const tName& name ):
+ Chainable1( name ),
+ mEvent( NUM_EVENTS )
+{
+ //nothing
+}
+
+//=============================================================================
+// RecieveEvent::Activate
+//=============================================================================
+// Description: activate makes the object start listening for it's event
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void RecieveEvent::Activate()
+{
+ rAssertMsg( mEvent != NUM_EVENTS, "you need to set the event to something meaningful before this can be activated" );
+ GetEventManager()->AddListener( this, mEvent );
+}
+
+//=============================================================================
+// RecieveEvent::HandleEvent
+//=============================================================================
+// Description: we've got the message we were listenting for if we get this
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void RecieveEvent::HandleEvent( EventEnum id, void* pEventData )
+{
+ GetEventManager()->RemoveListener( this, mEvent );
+ ContinueChain();
+}
+
+//=============================================================================
+// RecieveEvent::SetEvent
+//=============================================================================
+// Description: specify which event we should be listenting for
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void RecieveEvent::SetEvent( const EventEnum event )
+{
+ mEvent = event;
+ //IAN: better ensure that this listener isn't active at the moment
+}
+
+//==============================================================================
+// ResumeGame::Activate
+//==============================================================================
+// Description: transition that resumes a paused game the game
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+ResumeGame::ResumeGame():
+ Chainable1()
+{
+ //nothing
+}
+
+//==============================================================================
+// ResumeGame::Activate
+//==============================================================================
+// Description: transition that resumes a paused game the game
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+ResumeGame::ResumeGame( const tName& name ):
+ Chainable1( name )
+{
+ //nothing
+}
+
+//==============================================================================
+// ResumeGame::Activate
+//==============================================================================
+// Description: transition that resumes a paused game the game
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+void ResumeGame::Activate()
+{
+ GameplayContext::GetInstance()->PauseAllButPresentation( false );
+ ContinueChain();
+}
+
+//=============================================================================
+// SendEvent::SendEvent()
+//=============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+SendEvent::SendEvent():
+ mEvent( NUM_EVENTS )
+{
+ //nothing
+}
+
+//=============================================================================
+// SendEvent::SendEvent()
+//=============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+SendEvent::SendEvent( const tName& name ):
+ Chainable1( name ),
+ mEvent( NUM_EVENTS )
+{
+ //nothing
+}
+
+//=============================================================================
+// SendEvent::Activate()
+//=============================================================================
+// Description: fires off the event that we're supposed to send at this point
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void SendEvent::Activate()
+{
+ Transition::Activate();
+ rAssert( mEvent != NUM_EVENTS );
+ GetEventManager()->TriggerEvent( mEvent, mEventData );
+ ContinueChain();
+}
+
+//=============================================================================
+// SendEvent::SetEvent
+//=============================================================================
+// Description: sets the event that we're going to fire off later on
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void SendEvent::SetEvent( EventEnum event )
+{
+ mEvent = event;
+}
+
+//=============================================================================
+// SendEvent::SetEventData
+//=============================================================================
+// Description: sets the event data that we're going to fire off later on
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void SendEvent::SetEventData( void* eventData )
+{
+ mEventData = eventData;
+}
+
+//=============================================================================
+// SwitchContext::SwitchContext()
+//=============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+SwitchContext::SwitchContext():
+ mContext( NUM_CONTEXTS )
+{
+ //nothing
+}
+
+//=============================================================================
+// SwitchContext::SwitchContext()
+//=============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+SwitchContext::SwitchContext( const tName& name ):
+ Parent( name ),
+ mContext( NUM_CONTEXTS )
+{
+ //nothing
+}
+
+//=============================================================================
+// SwitchContext::Activate()
+//=============================================================================
+// Description: changes to the context that we want
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void SwitchContext::Activate()
+{
+ Transition::Activate();
+ rAssert( mContext != NUM_CONTEXTS );
+ GetGameFlow()->SetContext( mContext );
+ ContinueChain();
+}
+
+//=============================================================================
+// SwitchContext::SetContext
+//=============================================================================
+// Description: sets the context to which we will be going
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//=============================================================================
+void SwitchContext::SetContext( ContextEnum context )
+{
+ mContext = context;
+}
+
+//==============================================================================
+// Show::Show
+//==============================================================================
+// Description: constructor
+//
+// Parameters: NONE
+//
+// Return: N/A.
+//
+//==============================================================================
+Show::Show()
+{
+}
+//==============================================================================
+// Show::Show
+//==============================================================================
+// Description: constructor
+//
+// Parameters: name - the name of the object
+//
+// Return: N/A.
+//
+//==============================================================================
+Show::Show( const tName& name ):
+ Chainable1( name )
+{
+ //nothing
+}
+
+//==============================================================================
+// Show::Activate
+//==============================================================================
+// Description: this gets called when a show transition is triggered
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void Show::Activate()
+{
+ Transition::Activate();
+ rAssert( m_Drawable != NULL );
+ if( m_Drawable != NULL )
+ {
+ m_Drawable->SetVisible( true );
+ }
+ ContinueChain();
+}
+
+
+//==============================================================================
+// Spin::Spin
+//==============================================================================
+// Description: Constructor
+//
+// Parameters: none
+//
+// Return: N/A.
+//
+//==============================================================================
+Spin::Spin():
+ Transition( "Spin" ),
+ m_Period( 1.0f )
+{
+ //nothing
+}
+
+//==============================================================================
+// Spin::MovesDrawable
+//==============================================================================
+// Description: does this transition move a drawable object?
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+bool Spin::MovesDrawable() const
+{
+ return true;
+}
+
+//==============================================================================
+// Spin::SetPeriod
+//==============================================================================
+// Description: Sets the period of the spinning object
+//
+// Parameters: period - float time in seconds for the period
+//
+// Return: N/A.
+//
+//==============================================================================
+void Spin::SetDrawable( Scrooby::Drawable* drawable )
+{
+ Transition::SetDrawable( drawable );
+ m_BoundedDrawable = dynamic_cast< Scrooby::BoundedDrawable* >( drawable );
+ rAssert( m_BoundedDrawable != NULL );
+}
+
+//==============================================================================
+// Spin::SetPeriod
+//==============================================================================
+// Description: Sets the period of the spinning object
+//
+// Parameters: period - float time in seconds for the period
+//
+// Return: N/A.
+//
+//==============================================================================
+void Spin::SetPeriod( const float period )
+{
+ m_Period = period;
+}
+
+//==============================================================================
+// Spin::Update
+//==============================================================================
+// Description: Does the animation
+//
+// Parameters: deltaT - elapsed time
+//
+// Return: N/A.
+//
+//==============================================================================
+void Spin::Update( const float deltaT )
+{
+ if( IsActive() )
+ {
+ Transition::Update( deltaT );
+ m_ElapsedTime = fmodf( m_ElapsedTime, m_Period * 1000.0f );
+ float angle = m_ElapsedTime / ( m_Period * 1000 ) * 360.0f;
+ if( m_BoundedDrawable != NULL )
+ {
+ m_BoundedDrawable->RotateAboutCenter( angle );
+ }
+ }
+}
+
+//==============================================================================
+// Transition::Transition
+//==============================================================================
+// Description: constructor
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Transition::Transition():
+#ifdef RAD_DEBUG
+ m_Name( "NONE" ),
+#endif
+ m_Active( false ),
+ m_Done( false ),
+ m_Drawable( NULL ),
+ m_ElapsedTime( 0.0f )
+{
+#ifdef RAD_DEBUG
+ static int count = 0;
+ m_Id = count;
+ ++count;
+#endif
+}
+
+//==============================================================================
+// Transition::Transition
+//==============================================================================
+// Description: constructor
+//
+// Parameters: name - the name to assign - only in debug builds.
+//
+// Return: N/A.
+//
+//==============================================================================
+Transition::Transition( const tName& name ):
+ m_Active( false ),
+ m_Done( false ),
+ m_Drawable( NULL ),
+ m_ElapsedTime( 0.0f )
+{
+#ifdef RAD_DEBUG
+ m_Name = name;
+#endif
+}
+
+//==============================================================================
+// Transition::Activate
+//==============================================================================
+// Description: activates the transition
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void Transition::Activate()
+{
+ #ifdef RAD_DEBUG
+ //rAssert( m_Name != "NONE" );
+ if( m_Name.GetUID() != static_cast< tUID >( 0 ) )
+ {
+ const char* name = m_Name.GetText();
+ rDebugPrintf( "Transition Starting - %s\n", name );
+ }
+ #endif
+
+ m_Active = true;
+ m_Done = false;
+ Update( 0.0f );
+}
+
+//==============================================================================
+// Transition::Deactivate
+//==============================================================================
+// Description: deactivates the transition
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void Transition::Deactivate()
+{
+ m_Active = false;
+ m_Done = true;
+}
+
+//==============================================================================
+// Transition::GetDrawable
+//==============================================================================
+// Description: returns the drawable associated with this transition
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Scrooby::Drawable* Transition::GetDrawable()
+{
+ return m_Drawable;
+}
+
+//==============================================================================
+// Transition::IsActive
+//==============================================================================
+// Description: queries whether or not the transition is active
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+bool Transition::IsActive() const
+{
+ return m_Active;
+}
+
+//==============================================================================
+// Transition::IsDone
+//==============================================================================
+// Description: queries whether or not the transition is done
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+bool Transition::IsDone() const
+{
+ return m_Done;
+}
+
+//==============================================================================
+// Transition::MovesDrawable
+//==============================================================================
+// Description: does this transition move a drawable object?
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+bool Transition::MovesDrawable() const
+{
+ return false;
+}
+
+//==============================================================================
+// Transition::operator=
+//==============================================================================
+// Description: assignment operator
+//
+// Parameters: right - what we're assigning
+//
+// Return: N/A.
+//
+//==============================================================================
+Transition& Transition::operator=( const Transition& right )
+{
+ if( this == &right)
+ {
+ return *this;
+ }
+ m_Active = right.m_Active;
+ m_Drawable = right.m_Drawable;
+ m_ElapsedTime = right.m_ElapsedTime;
+ return *this;
+}
+
+//==============================================================================
+// Transition::Reset
+//==============================================================================
+// Description: resets the transition to it's 0 time state
+//
+// Parameters: None
+//
+// Return: N/A.
+//
+//==============================================================================
+void Transition::Reset()
+{
+ m_ElapsedTime = 0;
+ m_Done = false;
+}
+
+//==============================================================================
+// Transition::SetDrawable
+//==============================================================================
+// Description: sets the drawable that this transition will apply to
+//
+// Parameters: drawable - the drawable
+//
+// Return: N/A.
+//
+//==============================================================================
+void Transition::SetDrawable( Scrooby::Drawable* drawable )
+{
+ m_Drawable = drawable;
+}
+
+//==============================================================================
+// Transition::SetDrawable
+//==============================================================================
+// Description: sets the drawable that this transition will apply to
+//
+// Parameters: drawable - the drawable
+//
+// Return: N/A.
+//
+//==============================================================================
+void Transition::SetDrawable( Scrooby::Drawable& drawable )
+{
+ m_Drawable = &drawable;
+}
+
+//=============================================================================
+// Transition::Update
+//=============================================================================
+// Description: applies the transition to the front end element being modified
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+void Transition::Update( const float elapsedTime )
+{
+ if( !IsActive() )
+ {
+ return;
+ }
+
+ //rDebugPrintf( "%s\n", m_Name.GetText() );
+
+ m_ElapsedTime += elapsedTime;
+}
+
+//=============================================================================
+// Transition::Watch
+//=============================================================================
+// Description: adds a function to thw watcher to enable/disable this
+// transition
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+#ifdef DEBUGWATCH
+void Transition::Watch( const char* nameSpace )
+{
+ char output[ 1024 ];
+ sprintf( output, "%s\\%s", nameSpace, m_Name.GetText() );
+ radDbgWatchDelete( &m_DummyVariableForActivate );
+ radDbgWatchDelete( &m_DummyVariableForDeactivate );
+ radDbgWatchAddBoolean( &m_DummyVariableForActivate, "Activate", output, ActivateCallback, this );
+ radDbgWatchAddBoolean( &m_DummyVariableForDeactivate, "Deativate", output, DeativateCallback, this );
+}
+#endif
+
+
+//==============================================================================
+// Translator::Translator
+//==============================================================================
+// Description: constructor
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Translator::Translator():
+ startX( 0 ),
+ startY( 0 ),
+ endX( 0 ),
+ endY( 0 )
+{
+ //nothing
+}
+
+//==============================================================================
+// Translator::Translator
+//==============================================================================
+// Description: constructor
+//
+// Parameters: name - name of the object
+//
+// Return: N/A.
+//
+//==============================================================================
+Translator::Translator( const tName& name ):
+ Chainable1( name ),
+ startX( 0 ),
+ startY( 0 ),
+ endX( 0 ),
+ endY( 0 )
+{
+}
+
+//==============================================================================
+// Translator::~Translator
+//==============================================================================
+// Description: destructor
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Translator::~Translator()
+{
+ //nothing
+}
+
+//=============================================================================
+// Translator::MateCoordsEnd
+//=============================================================================
+// Description: match up the end coordinates of the transition with where the
+// scrooby object is supposed to be
+//
+// Parameters: drawable - the object to match up with
+//
+// Return: N/A.
+//
+//=============================================================================
+void Translator::MateCoordsEnd( const Scrooby::HasBoundingBox* drawable )
+{
+ SetCoordsEnd( 0, 0 );
+}
+
+//=============================================================================
+// Translator::MateCoordsStart
+//=============================================================================
+// Description: match up the start coordinates of the transition with where the
+// scrooby object is supposed to be
+//
+// Parameters: drawable - the object to match up with
+//
+// Return: N/A.
+//
+//=============================================================================
+void Translator::MateCoordsStart( const Scrooby::HasBoundingBox* drawable )
+{
+ SetCoordsStart( 0, 0 );
+}
+
+//==============================================================================
+// Translator::MovesDrawable
+//==============================================================================
+// Description: does this transition move a drawable object?
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+bool Translator::MovesDrawable() const
+{
+ return true;
+}
+
+//==============================================================================
+// Translator::operator=
+//==============================================================================
+// Description: assignment operator
+//
+// Parameters: right - what we're being assigned
+//
+// Return: reference to self
+//
+//==============================================================================
+Translator& Translator::operator=( const Translator& right )
+{
+ if( this == &right )
+ {
+ return *this;
+ }
+
+ Chainable1::operator=( right );
+
+ startX = right.startX;
+ startY = right.startY;
+ endX = right.endX;
+ endY = right.endY;
+ m_TimeInterval = right.m_TimeInterval;
+ return *this;
+}
+
+//==============================================================================
+// Translator::SetEndCoords
+//==============================================================================
+// Description: sets the ending position of the object
+//
+// Parameters: x, y, the coords
+//
+// Return: N/A.
+//
+//==============================================================================
+void Translator::SetCoordsEnd( const int x, const int y )
+{
+ endX = x;
+ endY = y;
+}
+
+//==============================================================================
+// Translator::SetStartCoords
+//==============================================================================
+// Description: sets the starting position of the object
+//
+// Parameters: x, y, the coords
+//
+// Return: N/A.
+//
+//==============================================================================
+void Translator::SetCoordsStart( const int x, const int y )
+{
+ startX = x;
+ startY = y;
+}
+
+//==============================================================================
+// Translator::SetEndOffscreenBottom
+//==============================================================================
+// Description: positions the transition to end the object offscreen to the bottom
+//
+// Parameters: drawable - the drawable to line up offscreen with
+//
+// Return: N/A.
+//
+//==============================================================================
+void Translator::SetEndOffscreenBottom( const Scrooby::Drawable* drawable )
+{
+ int xMin;
+ int xMax;
+ int yMin;
+ int yMax;
+ drawable->GetBoundingBox( xMin, yMin, xMax, yMax );
+ #ifdef RAD_PS2
+ SetCoordsEnd( 0, -20 -yMax );
+ #else
+ SetCoordsEnd( 0, -yMax );
+ #endif
+}
+
+//==============================================================================
+// Translator::SetEndOffscreenLeft
+//==============================================================================
+// Description: positions the transition to end the object offscreen to the left
+//
+// Parameters: drawable - the drawable to line up offscreen with
+//
+// Return: N/A.
+//
+//==============================================================================
+void Translator::SetEndOffscreenLeft( const Scrooby::Drawable* drawable )
+{
+ int xMin;
+ int xMax;
+ int yMin;
+ int yMax;
+ drawable->GetBoundingBox( xMin, yMin, xMax, yMax );
+ int width = xMax - xMin;
+
+ if( p3d::display->IsWidescreen() )
+ {
+ SetCoordsEnd( -xMin - width - WIDESCREEN_EXTRA_PIXELS, 0 );
+ }
+ else
+ {
+ SetCoordsEnd( -xMin - width, 0 );
+ }
+}
+//==============================================================================
+// Translator::SetEndOffscreenRight
+//==============================================================================
+// Description: positions the transition to end the object offscreen to the right
+//
+// Parameters: drawable - the drawable to line up offscreen with
+//
+// Return: N/A.
+//
+//==============================================================================
+void Translator::SetEndOffscreenRight( const Scrooby::Drawable* drawable )
+{
+ int xMin;
+ int xMax;
+ int yMin;
+ int yMax;
+ drawable->GetBoundingBox( xMin, yMin, xMax, yMax );
+ int width = xMax - xMin;
+ if( p3d::display->IsWidescreen() )
+ {
+ SetCoordsEnd( 640 - xMin + WIDESCREEN_EXTRA_PIXELS, 0 );
+ }
+ else
+ {
+ SetCoordsEnd( 640 - xMin, 0 );
+ }
+}
+
+//==============================================================================
+// Translator::SetEndOffscreenTop
+//==============================================================================
+// Description: positions the transition to end the object offscreen to the left
+//
+// Parameters: drawable - the drawable to line up offscreen with
+//
+// Return: N/A.
+//
+//==============================================================================
+void Translator::SetEndOffscreenTop( const Scrooby::Drawable* drawable )
+{
+ int xMin;
+ int xMax;
+ int yMin;
+ int yMax;
+ drawable->GetBoundingBox( xMin, yMin, xMax, yMax );
+ #ifdef RAD_PS2
+ SetCoordsEnd( 0, 480 + 20 - yMin );
+ #else
+ SetCoordsEnd( 0, 480 - yMin );
+ #endif
+}
+
+//==============================================================================
+// Translator::SetStartOffscreenBottom
+//==============================================================================
+// Description: positions the transition to start the object offscreen to the bottom
+//
+// Parameters: drawable - the drawable to line up offscreen with
+//
+// Return: N/A.
+//
+//==============================================================================
+void Translator::SetStartOffscreenBottom ( const Scrooby::Drawable* drawable )
+{
+ int xMin;
+ int xMax;
+ int yMin;
+ int yMax;
+ drawable->GetBoundingBox( xMin, yMin, xMax, yMax );
+
+ #ifdef RAD_PS2
+ SetCoordsStart( 0, -20 -yMax );
+ #else
+ SetCoordsStart( 0, -yMax );
+ #endif
+
+}
+
+//==============================================================================
+// Translator::SetTimeInterval
+//==============================================================================
+// Description: positions the transition to start the object offscreen to the left
+//
+// Parameters: drawable - the drawable to line up offscreen with
+//
+// Return: N/A.
+//
+//==============================================================================
+void Translator::SetStartOffscreenLeft( const Scrooby::Drawable* drawable )
+{
+ int xMin;
+ int xMax;
+ int yMin;
+ int yMax;
+ drawable->GetBoundingBox( xMin, yMin, xMax, yMax );
+ int width = xMax - xMin;
+
+ if( p3d::display->IsWidescreen() )
+ {
+ SetCoordsStart( -xMin - width - WIDESCREEN_EXTRA_PIXELS, 0 );
+ }
+ else
+ {
+ SetCoordsStart( -xMin - width, 0 );
+ }
+}
+//==============================================================================
+// Translator::SetStartOffscreenRight
+//==============================================================================
+// Description: positions the transition to start the object offscreen to the right
+//
+// Parameters: drawable - the drawable to line up offscreen with
+//
+// Return: N/A.
+//
+//==============================================================================
+void Translator::SetStartOffscreenRight ( const Scrooby::Drawable* drawable )
+{
+ int xMin;
+ int xMax;
+ int yMin;
+ int yMax;
+ drawable->GetBoundingBox( xMin, yMin, xMax, yMax );
+ int width = xMax - xMin;
+ if( p3d::display->IsWidescreen() )
+ {
+ SetCoordsStart( 640 - xMin + WIDESCREEN_EXTRA_PIXELS, 0 );
+ }
+ else
+ {
+ SetCoordsStart( 640 - xMin, 0 );
+ }
+}
+
+//==============================================================================
+// Translator::SetStartOffscreenTop
+//==============================================================================
+// Description: positions the transition to start the object offscreen to the top
+//
+// Parameters: drawable - the drawable to line up offscreen with
+//
+// Return: N/A.
+//
+//==============================================================================
+void Translator::SetStartOffscreenTop( const Scrooby::Drawable* drawable )
+{
+ int xMin;
+ int xMax;
+ int yMin;
+ int yMax;
+ drawable->GetBoundingBox( xMin, yMin, xMax, yMax );
+ SetCoordsStart( 0, 480 - yMin );
+ #ifdef RAD_PS2
+ SetCoordsStart( 0, 480 + 20 - yMin );
+ #else
+ SetCoordsStart( 0, 480 - yMin );
+ #endif
+
+}
+
+//==============================================================================
+// Translator::Apply
+//==============================================================================
+// Description: applies the transition to the front end element being modified
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void Translator::Update( const float deltaT )
+{
+ if( IsActive() )
+ {
+ Transition::Update( deltaT );
+ float spillover = rmt::Max( m_ElapsedTime - m_TimeInterval, 0.0f );
+ m_ElapsedTime = rmt::Min( m_ElapsedTime, m_TimeInterval );
+
+ float percentDone = rmt::Min( m_ElapsedTime / m_TimeInterval, 1.0f );
+ int x = static_cast< int >( ( endX - startX ) * percentDone + startX );
+ int y = static_cast< int >( ( endY - startY ) * percentDone + startY );
+ if( m_Drawable != NULL )
+ {
+ m_Drawable->Translate( x, y );
+ }
+
+ if( spillover > 0 )
+ {
+ ContinueChain();
+ }
+ }
+}
+
+//==============================================================================
+// Translator::Watch
+//==============================================================================
+// Description: Adds this transition to the watcher
+//
+// Parameters: nameSpace - the name of the group in the watcher
+//
+// Return: N/A.
+//
+//==============================================================================
+#ifdef DEBUGWATCH
+void Translator::Watch( const char* nameSpace )
+{
+ char output[ 1024 ];
+ sprintf( output, "%s\\%s", nameSpace, m_Name.GetText() );
+ Parent1::Watch( nameSpace );
+ Parent2::Watch( output ); //HasTimeInterval doesn't know the name.
+ radDbgWatchDelete( &startX );
+ radDbgWatchDelete( &startY );
+ radDbgWatchDelete( &endX );
+ radDbgWatchDelete( &endY );
+ radDbgWatchAddShort( &startX, "startX", output, NULL, NULL, -640, 640 * 2 );
+ radDbgWatchAddShort( &startY, "startY", output, NULL, NULL, -640, 640 * 2 );
+ radDbgWatchAddShort( &endX, "endX", output, NULL, NULL, -640, 640 * 2 );
+ radDbgWatchAddShort( &endY, "endY", output, NULL, NULL, -640, 640 * 2 );
+}
+#endif
+
+//==============================================================================
+// UnderdampedTranslator::UnderdampedTranslator
+//==============================================================================
+// Description: constructor
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+UnderdampedTranslator::UnderdampedTranslator():
+ Translator()
+{
+}
+
+//==============================================================================
+// UnderdampedTranslator::UnderdampedTranslator
+//==============================================================================
+// Description: constructor
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+UnderdampedTranslator::UnderdampedTranslator( const tName& name ):
+ Translator( name )
+{
+}
+
+//==============================================================================
+// UnderdampedTranslator::MovesDrawable
+//==============================================================================
+// Description: does this transition move a drawable object?
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+bool UnderdampedTranslator::MovesDrawable() const
+{
+ return true;
+}
+
+//==============================================================================
+// UnderdampedTranslator::SetFrequency
+//==============================================================================
+// Description: sets the frequency of oscillation of the underdamped translator
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void UnderdampedTranslator::SetFrequency( const float frequency )
+{
+ m_Frequency = frequency;
+}
+
+//==============================================================================
+// UnderdampedTranslator::Update
+//==============================================================================
+// Description: updates the object that we're translating
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void UnderdampedTranslator::Update( const float deltaT )
+{
+ if( IsActive() )
+ {
+ Transition::Update( deltaT );
+ float spillover = rmt::Max( m_ElapsedTime - m_TimeInterval, 0.0f );
+ m_ElapsedTime = rmt::Min( m_ElapsedTime, m_TimeInterval );
+ float decayRate = logf( 0.01f ) / m_TimeInterval;
+ float exponentialPart = expf( decayRate * m_ElapsedTime );
+ float cosinePart = cosf( 2.0f * rmt::PI * m_Frequency * m_ElapsedTime / 1000.0f );
+ float percentage = 1.0f - exponentialPart * cosinePart;
+ int x = static_cast< int >( ( endX - startX ) * percentage + startX );
+ int y = static_cast< int >( ( endY - startY ) * percentage + startY );
+ m_Drawable->Translate( x, y );
+
+ if( spillover > 0 )
+ {
+ ContinueChain();
+ }
+ }
+}
+
+} \ No newline at end of file
diff --git a/game/code/presentation/gui/utility/transitions.h b/game/code/presentation/gui/utility/transitions.h
new file mode 100644
index 0000000..f00b1b9
--- /dev/null
+++ b/game/code/presentation/gui/utility/transitions.h
@@ -0,0 +1,592 @@
+//===========================================================================
+// Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SpecialFX
+//
+// Description:
+//
+//
+// Authors: Tony Chu
+//
+// Revisions Date Author Revision
+// 2002/08/30 TChu Created for SRR2
+//
+//===========================================================================
+
+#ifndef TRANSITIONS_H
+#define TRANSITIONS_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <p3d/entity.hpp>
+#include "events/eventenum.h"
+#include "events/eventlistener.h"
+#include <input/controller.h>
+#include <gameflow/gameflow.h>
+#include <presentation/gui/guiwindow.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace Scrooby
+{
+ class Drawable;
+ class BoundedDrawable;
+ class HasBoundingBox;
+ class Sprite;
+}
+
+class tMultiController;
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+namespace GuiSFX
+{
+#ifdef DEBUGWATCH
+ #define WATCH( object, nameSpace ) object.Watch( nameSpace )
+#else
+ #define WATCH( object, nameSpace )
+#endif
+
+#ifdef DEBUGWATCH
+ void ActivateCallback ( void* userData );
+ void DeactivateCallback( void* userData );
+#endif
+//==============================================================================
+// Transition
+//==============================================================================
+class Transition
+{
+public:
+ Transition();
+ Transition( const tName& name );
+ virtual void Activate();
+ virtual void Deactivate();
+ Scrooby::Drawable* GetDrawable();
+ bool IsActive() const;
+ bool IsDone() const;
+ virtual bool MovesDrawable() const;
+ Transition& operator=( const Transition& right );
+ virtual void Reset();
+ virtual void SetDrawable( Scrooby::Drawable* drawable );
+ virtual void SetDrawable( Scrooby::Drawable& drawable );
+ virtual void Update( const float deltaT );
+
+ #ifdef DEBUGWATCH
+ virtual void Watch( const char* nameSpace );
+ #endif
+
+protected:
+#ifdef DEBUGWATCH
+ tName m_Name;
+ int m_Id;
+ bool m_DummyVariableForActivate;
+ bool m_DummyVariableForDeactivate;
+#endif
+ bool m_Active : 1;
+ bool m_Done : 1;
+ Scrooby::Drawable* m_Drawable;
+ float m_ElapsedTime;
+};
+
+//==============================================================================
+// Chainable
+//==============================================================================
+class Chainable : public Transition
+{
+public:
+ Chainable();
+ Chainable( const tName& name );
+ virtual void ContinueChain() = 0;
+ virtual void DeactivateChain() = 0;
+ virtual bool IsChainDone() const = 0;
+ virtual void ResetChain() = 0;
+protected:
+};
+
+//==============================================================================
+// Chainable1 - these transitions can be chained together temporally
+//==============================================================================
+class Chainable1 : public Chainable
+{
+private:
+ typedef Chainable Parent;
+public:
+ Chainable1();
+ Chainable1( const tName& name );
+ Chainable1& operator=( const Chainable1& right );
+ virtual void ContinueChain();
+ virtual void DeactivateChain();
+ virtual bool IsChainDone() const;
+ virtual void ResetChain();
+ void SetNextTransition( Chainable* transition );
+ void SetNextTransition( Chainable& transition );
+protected:
+ Chainable* m_NextTransition;
+};
+
+//==============================================================================
+// Chainable2 - one transition that spawns 2 others at the same time
+//==============================================================================
+class Chainable2 : public Chainable
+{
+public:
+ Chainable2();
+ Chainable2( const tName& name );
+ virtual void ContinueChain();
+ virtual void DeactivateChain();
+ virtual bool IsChainDone() const;
+ virtual void ResetChain();
+ void SetNextTransition( const unsigned int index, Chainable* transition );
+ void SetNextTransition( const unsigned int index, Chainable& transition );
+
+protected:
+ Chainable* m_NextTransition[ 2 ];
+};
+
+//==============================================================================
+// Chainable3 - one transition that spawns 3 others at the same time
+//==============================================================================
+//Ian - this should be a templatized class...
+class Chainable3 : public Chainable
+{
+public:
+ Chainable3();
+ Chainable3( const tName& name );
+ virtual void ContinueChain();
+ virtual void DeactivateChain();
+ virtual bool IsChainDone() const;
+ virtual void ResetChain();
+ void SetNextTransition( const unsigned int index, Chainable* transition );
+ void SetNextTransition( const unsigned int index, Chainable& transition );
+
+protected:
+ Chainable* m_NextTransition[ 3 ];
+};
+
+//==============================================================================
+// Junction2
+//==============================================================================
+class Junction2 : public Chainable2
+{
+public:
+ void Activate();
+protected:
+};
+
+//==============================================================================
+// Junction3
+//==============================================================================
+//IAN - this should be a templatized class too
+class Junction3 : public Chainable3
+{
+public:
+ void Activate();
+protected:
+};
+
+//==============================================================================
+// Dummy - this "transition" does nothing. it's a placeholder
+//==============================================================================
+class Dummy : public Chainable1
+{
+private:
+ typedef Chainable1 Parent;
+public:
+ Dummy();
+ Dummy( const tName& name );
+ void Activate();
+};
+
+//==============================================================================
+// GotoScreen
+//==============================================================================
+class GotoScreen : public Chainable1
+{
+private:
+ typedef Chainable1 Parent;
+public:
+ GotoScreen();
+ GotoScreen( const tName& name );
+ void Activate();
+ void SetEventData( void* eventData );
+ void SetParam1( const unsigned int param1 );
+ void SetParam2( const unsigned int param2 );
+ void SetScreen( CGuiWindow::eGuiWindowID screen );
+ void SetWindowOptions( const unsigned int windowOptions );
+
+protected:
+ CGuiWindow::eGuiWindowID mScreen;
+ unsigned int mParam1;
+ unsigned int mParam2;
+ void* mEventData;
+ unsigned int mWindowOptions;
+};
+
+
+//==============================================================================
+// Hide
+//==============================================================================
+class Hide : public Chainable1
+{
+public:
+ Hide();
+ Hide( const tName& name );
+ void Activate();
+
+protected:
+};
+
+//==============================================================================
+// InputStateChange
+//==============================================================================
+class InputStateChange : public Chainable1
+{
+private:
+ typedef Chainable1 Parent;
+public:
+ InputStateChange();
+ InputStateChange( const tName& name );
+ void Activate();
+ void SetState( const Input::ActiveState state );
+protected:
+private:
+ Input::ActiveState mState;
+};
+
+//==============================================================================
+// RecieveEvent
+//==============================================================================
+class RecieveEvent :
+ public Chainable1,
+ public EventListener
+{
+public:
+ RecieveEvent();
+ RecieveEvent( const tName& name );
+ void Activate();
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ void SetEvent( const EventEnum event );
+protected:
+private:
+ EventEnum mEvent;
+};
+
+//==============================================================================
+// SendEvent
+//==============================================================================
+class SendEvent : public Chainable1
+{
+public:
+ SendEvent();
+ SendEvent( const tName& name );
+ void Activate();
+ void SetEvent( EventEnum event );
+ void SetEventData( void* eventData );
+
+protected:
+ EventEnum mEvent;
+ void* mEventData;
+};
+
+//==============================================================================
+// SwitchContext
+//==============================================================================
+class SwitchContext : public Chainable1
+{
+private:
+ typedef Chainable1 Parent;
+public:
+ SwitchContext();
+ SwitchContext( const tName& name );
+ void Activate();
+ void SetContext( ContextEnum event );
+
+protected:
+ ContextEnum mContext;
+};
+
+//==============================================================================
+// HasMulticontroller
+//==============================================================================
+class HasMulticontroller
+{
+public:
+ void SetMultiController( tMultiController* multicontroller );
+protected:
+ void ResetMultiControllerFrames();
+
+ tMultiController* m_MultiController;
+ float m_NumFrames;
+};
+
+//==============================================================================
+// HasMulticontroller
+//==============================================================================
+class HasTimeInterval
+{
+public:
+ HasTimeInterval();
+ void SetTimeInterval( const float interval );
+ #ifdef DEBUGWATCH
+ virtual void Watch( const char* nameSpace );
+ #endif
+
+protected:
+ float m_TimeInterval;
+};
+
+//==============================================================================
+// ColorChange
+//==============================================================================
+class ColorChange :
+ public Chainable1,
+ public HasTimeInterval
+{
+private:
+ typedef Chainable1 Parent1;
+ typedef HasTimeInterval Parent2;
+public:
+ ColorChange();
+ ColorChange( const tName& name );
+ void SetStartColour( const tColour c );
+ void SetEndColour( const tColour c );
+ void Update( const float deltaT );
+ #ifdef DEBUGWATCH
+ virtual void Watch( const char* nameSpace );
+ #endif
+
+protected:
+ tColour m_StartColor;
+ tColour m_EndColor;
+};
+
+//==============================================================================
+// IrisWipeClose
+//==============================================================================
+class IrisWipeOpen
+ : public Chainable1,
+ public HasMulticontroller
+
+{
+ //this class doesn't even need a drawable really
+public:
+ IrisWipeOpen();
+ IrisWipeOpen( const tName& name );
+ void Activate();
+ void Update( const float deltaT );
+protected:
+};
+
+//==============================================================================
+// IrisWipeOpen
+//==============================================================================
+class IrisWipeClose
+ : public Chainable1,
+ public HasMulticontroller
+{
+ //this class doesn't even need a drawable really
+public:
+ IrisWipeClose();
+ IrisWipeClose( const tName& name );
+ void Activate();
+ void Deactivate();
+ void Update( const float deltaT );
+protected:
+};
+
+//==============================================================================
+// Show
+//==============================================================================
+class Show : public Chainable1
+{
+public:
+ Show();
+ Show( const tName& name );
+ void Activate();
+
+protected:
+};
+
+//=============================================================================
+// Pause
+//=============================================================================
+class Pause :
+ public Chainable1,
+ public HasTimeInterval
+{
+private:
+ typedef Chainable1 Parent1;
+ typedef HasTimeInterval Parent2;
+
+public:
+ Pause();
+ Pause( const tName& name );
+ void Update( const float deltaT );
+ #ifdef DEBUGWATCH
+ virtual void Watch( const char* nameSpace );
+ #endif
+protected:
+};
+
+//=============================================================================
+// PauseInFrames
+//=============================================================================
+class PauseInFrames :
+ public Chainable1
+{
+private:
+ typedef Chainable1 Parent;
+public:
+ PauseInFrames();
+ PauseInFrames( const tName& name );
+ void Reset();
+ void SetNumberOfFrames( const unsigned int i );
+ void Update( const float deltaT );
+protected:
+ unsigned int m_FramesToPause;
+ unsigned int m_FramesElapsed;
+};
+
+//==============================================================================
+// PauseGame
+//==============================================================================
+class PauseGame :
+ public Chainable1
+{
+ void Activate();
+};
+
+//==============================================================================
+// ResumeGame
+//==============================================================================
+class ResumeGame :
+ public Chainable1
+{
+public:
+ ResumeGame();
+ ResumeGame( const tName& name );
+ void Activate();
+};
+
+//==============================================================================
+// ImageCycler - causes images to cycle forever
+//==============================================================================
+class ImageCycler : public Transition
+{
+public:
+ void SetDrawable( Scrooby::Drawable* drawable );
+ void SetDrawable( Scrooby::Drawable& drawable );
+ void SetPeriod( const float period );
+ void Update( const float deltaT );
+protected:
+ float m_Period;
+ Scrooby::Sprite* m_Sprite; //this is a waste of memory
+};
+
+//==============================================================================
+// Pulse - pulses the scale of the image about its center
+//==============================================================================
+class PulseScale : public Transition
+{
+private:
+ typedef Transition Parent;
+public:
+ PulseScale();
+ PulseScale( const tName& name );
+ virtual bool MovesDrawable() const;
+ void SetAmplitude( const float amplitude );
+ void SetDrawable( Scrooby::Drawable* drawable );
+ void SetDrawable( Scrooby::Drawable& drawable );
+ void SetFrequency( const float frequency );
+ void Update( const float deltaT );
+
+ #ifdef DEBUGWATCH
+ virtual void Watch( const char* nameSpace );
+ #endif
+
+protected:
+ Scrooby::HasBoundingBox* m_BoundedDrawable;
+ float m_Amplitude;
+ float m_Frequency;
+};
+
+//==============================================================================
+// Spin - causes the object to spin about its center forever
+//==============================================================================
+class Spin : public Transition
+{
+public:
+ Spin();
+ virtual bool MovesDrawable() const;
+ void SetDrawable( Scrooby::Drawable* drawable );
+ void SetPeriod( const float period );
+ void Update( const float deltaT );
+protected:
+ float m_Period;
+ Scrooby::BoundedDrawable* m_BoundedDrawable;
+};
+
+//==============================================================================
+// Translator
+//==============================================================================
+class Translator :
+ public Chainable1,
+ public HasTimeInterval
+{
+private:
+ typedef Chainable Parent1;
+ typedef HasTimeInterval Parent2;
+
+public:
+ Translator();
+ Translator( const tName& name );
+ Translator( const Translator& right );
+ ~Translator();
+ void MateCoordsEnd( const Scrooby::HasBoundingBox* drawable );
+ void MateCoordsStart( const Scrooby::HasBoundingBox* drawable );
+ virtual bool MovesDrawable() const;
+ Translator& operator=( const Translator& right );
+ void SetCoordsEnd ( const int x, const int y );
+ void SetCoordsStart( const int x, const int y );
+ void SetEndOffscreenBottom ( const Scrooby::Drawable* drawable );
+ void SetEndOffscreenLeft ( const Scrooby::Drawable* drawable );
+ void SetEndOffscreenRight ( const Scrooby::Drawable* drawable );
+ void SetEndOffscreenTop ( const Scrooby::Drawable* drawable );
+ void SetStartOffscreenBottom ( const Scrooby::Drawable* drawable );
+ void SetStartOffscreenLeft ( const Scrooby::Drawable* drawable );
+ void SetStartOffscreenRight ( const Scrooby::Drawable* drawable );
+ void SetStartOffscreenTop ( const Scrooby::Drawable* drawable );
+ void Update( const float deltaT );
+ #ifdef DEBUGWATCH
+ virtual void Watch( const char* nameSpace );
+ #endif
+
+protected:
+ short int startX;
+ short int startY;
+ short int endX;
+ short int endY;
+};
+
+//==============================================================================
+// Translator - with a springy type of effect.
+//==============================================================================
+class UnderdampedTranslator : public Translator
+{
+public:
+ UnderdampedTranslator();
+ UnderdampedTranslator( const tName& name );
+ virtual bool MovesDrawable() const;
+ void SetFrequency( const float frequency );
+ void Update( const float deltaT );
+protected:
+ float m_Frequency;
+};
+
+} // GuiSFX namespace
+
+#endif // TRANSITIONS_H
diff --git a/game/code/presentation/language.cpp b/game/code/presentation/language.cpp
new file mode 100644
index 0000000..d8eaf00
--- /dev/null
+++ b/game/code/presentation/language.cpp
@@ -0,0 +1,181 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: language.cpp
+//
+// Description: contains functions for dealing with language
+//
+// History: 21/1/2002 + Created -- Ian Gipson
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#ifdef RAD_PS2
+ #include <libscf.h>
+#endif
+
+#ifdef RAD_GAMECUBE
+ #include <dolphin/os.h>
+#endif
+
+#ifdef RAD_XBOX
+ #include <xtl.h>
+#endif
+//========================================
+// Project Includes
+//========================================
+
+#include <presentation/language.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+namespace Language{
+
+//=============================================================================
+// Language::GetHardwareLanguage()
+//=============================================================================
+// Description: returns the currently set language for the console.
+//
+// Parameters: None.
+//
+// Return: Language enum specifying the current language
+//
+//=============================================================================
+Language GetHardwareLanguage()
+{
+ #ifdef RAD_PS2
+ ////////////////////////////////////////////////////////////
+ // PS2
+ ////////////////////////////////////////////////////////////
+ switch ( sceScfGetLanguage() )
+ {
+ case SCE_DUTCH_LANGUAGE :
+ {
+ return DUTCH;
+ }
+ case SCE_ENGLISH_LANGUAGE :
+ {
+ return ENGLISH;
+ }
+ case SCE_FRENCH_LANGUAGE :
+ {
+ return FRENCH;
+ }
+ case SCE_GERMAN_LANGUAGE :
+ {
+ return GERMAN;
+ }
+ case SCE_ITALIAN_LANGUAGE :
+ {
+ return ITALIAN;
+ }
+ case SCE_JAPANESE_LANGUAGE :
+ {
+ return JAPANESE;
+ }
+ case SCE_PORTUGUESE_LANGUAGE :
+ {
+ return PORTUGUESE;
+ }
+ case SCE_SPANISH_LANGUAGE :
+ {
+ return SPANISH;
+ }
+ default :
+ {
+ return UNKNOWN;
+ }
+ }
+ #endif
+
+ #ifdef RAD_XBOX
+ ////////////////////////////////////////////////////////////
+ // XBOX
+ ////////////////////////////////////////////////////////////
+ switch ( XGetLanguage() )
+ {
+ case XC_LANGUAGE_ENGLISH :
+ {
+ return ENGLISH;
+ }
+ case XC_LANGUAGE_FRENCH :
+ {
+ return FRENCH;
+ }
+ case XC_LANGUAGE_GERMAN :
+ {
+ return GERMAN;
+ }
+ case XC_LANGUAGE_ITALIAN :
+ {
+ return ITALIAN;
+ }
+ case XC_LANGUAGE_JAPANESE :
+ {
+ return JAPANESE;
+ }
+ case XC_LANGUAGE_SPANISH :
+ {
+ return SPANISH;
+ }
+ default :
+ {
+ return UNKNOWN;
+ }
+ }
+ #endif
+
+ #ifdef RAD_GAMECUBE
+ switch ( OSGetLanguage() )
+ {
+ case OS_LANG_ENGLISH:
+ {
+ return ENGLISH;
+ }
+ case OS_LANG_GERMAN:
+ {
+ return GERMAN;
+ }
+ case OS_LANG_FRENCH:
+ {
+ return FRENCH;
+ }
+ case OS_LANG_SPANISH:
+ {
+ return SPANISH;
+ }
+ case OS_LANG_ITALIAN:
+ {
+ return ITALIAN;
+ }
+ case OS_LANG_DUTCH:
+ {
+ return DUTCH;
+ }
+ default:
+ {
+ return UNKNOWN;
+ }
+ }
+ #endif
+
+ #ifdef RAD_WIN32
+ ////////////////////////////////////////////////////////////
+ // WIN32
+ ////////////////////////////////////////////////////////////
+ return ENGLISH; // to be implemented.
+ #endif
+}
+
+} //namespace Language \ No newline at end of file
diff --git a/game/code/presentation/language.h b/game/code/presentation/language.h
new file mode 100644
index 0000000..e2e1a24
--- /dev/null
+++ b/game/code/presentation/language.h
@@ -0,0 +1,43 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: language.h
+//
+// Description: this file contains the prototypes for functions for getting
+// access to languages that the console is set to
+//
+// History: 21/11/2002 + Created -- Ian Gipson
+//
+//=============================================================================
+
+#ifndef LANGUAGE_H
+#define LANGUAGE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+
+//========================================
+// Forward References
+//========================================
+namespace Language
+{
+ enum Language
+ {
+ ENGLISH,
+ FRENCH,
+ GERMAN,
+ ITALIAN,
+ SPANISH,
+ DUTCH,
+ JAPANESE,
+ PORTUGUESE,
+
+ UNKNOWN
+ };
+
+ Language GetHardwareLanguage();
+}
+
+#endif // LANGUAGE_H
diff --git a/game/code/presentation/mouthflapper.cpp b/game/code/presentation/mouthflapper.cpp
new file mode 100644
index 0000000..ea094d2
--- /dev/null
+++ b/game/code/presentation/mouthflapper.cpp
@@ -0,0 +1,565 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: MouthFlapper.cpp
+//
+// Description: Implement MouthFlapper
+//
+// History: 09/09/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radmath/trig.hpp>
+#include <radtime.hpp>
+#include <stdlib.h>
+#include <p3d/anim/skeleton.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <presentation/mouthflapper.h>
+
+#include <worldsim/character/character.h>
+
+#include <choreo/puppet.hpp>
+
+#include <poser/pose.hpp>
+#include <poser/joint.hpp>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+static float MIN_OPEN = -0.10f * rmt::PI;
+static float MAX_OPEN = 0.15f * rmt::PI;
+static float MAX_DEVIATION = 0.04f * rmt::PI;
+static float MIN_SPEED = 1.00f;
+static float MAX_SPEED = 4.00f;
+
+//default settings
+MouthFlapperDefaultSetting gDefaultSetting( "default", -0.314159f, 0.452389f, 0.13f, 1.66f, 3.44f );
+MouthFlapperDefaultSetting gDefaultSettings[] =
+{
+ MouthFlapperDefaultSetting( "apu", -0.314159f, 0.490973f, 0.13f, 2.20f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "b_cbg", -0.314159f, 0.559204f, 0.13f, 2.44f, 3.88f ), //done
+ MouthFlapperDefaultSetting( "b_cletus", -0.314159f, 0.420973f, 0.13f, 1.88f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "b_frink", -0.314159f, 0.420973f, 0.13f, 1.88f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "b_grandpa",-0.314159f, 0.420973f, 0.136640f, 2.20f, 3.56f ), //done
+ MouthFlapperDefaultSetting("b_milhouse",-0.314159f, 0.521504f, 0.125664f, 2.22f, 4.00f ), //done
+ MouthFlapperDefaultSetting( "b_nelson", -0.314159f, 0.452389f, 0.13f, 1.66f, 3.44f ),
+ MouthFlapperDefaultSetting( "b_ralph", -0.314159f, 0.471504f, 0.125664f, 2.22f, 4.00f ), //done
+ MouthFlapperDefaultSetting( "b_skinner",-0.314159f, 0.490088f, 0.13f, 2.00f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "b_smithers", -0.314159f, 0.490088f, 0.13f, 2.00f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "b_snake", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ), //done
+ MouthFlapperDefaultSetting( "b_zfem1", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ),
+ MouthFlapperDefaultSetting( "b_zmale1", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ),
+ MouthFlapperDefaultSetting( "b_zmale2", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ),
+ MouthFlapperDefaultSetting( "b_zmale3", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ),
+ MouthFlapperDefaultSetting( "brn_unf", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ), //done
+ MouthFlapperDefaultSetting( "barney", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ), //done
+ MouthFlapperDefaultSetting( "bart", -0.314159f, 0.521504f, 0.125664f, 2.50f, 4.00f ), //done
+ MouthFlapperDefaultSetting( "burns", -0.314159f, 0.490088f, 0.13f, 2.00f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "captain", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ), //done
+ MouthFlapperDefaultSetting( "carl", -0.314159f, 0.490088f, 0.13f, 2.00f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "cbg", -0.314159f, 0.559204f, 0.13f, 2.44f, 3.88f ), //done
+ MouthFlapperDefaultSetting( "cletus", -0.314159f, 0.420973f, 0.13f, 1.88f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "frink", -0.314159f, 0.420973f, 0.13f, 1.88f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "gil", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ), //done
+ MouthFlapperDefaultSetting( "grandpa", -0.314159f, 0.420973f, 0.136640f, 2.20f, 3.56f ), //done
+ MouthFlapperDefaultSetting( "hibbert", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ), //done
+ MouthFlapperDefaultSetting( "homer", -0.314159f, 0.420973f, 0.136640f, 2.00f, 3.44f ), //done
+ MouthFlapperDefaultSetting( "jimbo", -0.314159f, 0.470973f, 0.13f, 1.88f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "joger1", -0.314159f, 0.470973f, 0.13f, 1.88f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "kearney", -0.314159f, 0.521504f, 0.125664f, 2.34f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "krusty", -0.314159f, 0.420973f, 0.136640f, 2.20f, 3.56f ), //done
+ MouthFlapperDefaultSetting( "lenny", -0.314159f, 0.490088f, 0.13f, 2.00f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "lisa", -0.314159f, 0.471239f, 0.125664f, 2.5f, 4.00f ), //done
+ MouthFlapperDefaultSetting( "louie", -0.314159f, 0.490088f, 0.13f, 2.00f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "marge", -0.314159f, 0.420973f, 0.13f, 1.66f, 3.44f ), //done
+ MouthFlapperDefaultSetting( "milhouse", -0.314159f, 0.521504f, 0.125664f, 2.22f, 4.00f ), //done
+ MouthFlapperDefaultSetting( "moe", -0.314159f, 0.490088f, 0.13f, 2.00f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "moleman", -0.314159f, 0.452389f, 0.125664f, 1.56f, 3.54f ), //done
+ MouthFlapperDefaultSetting( "ned", -0.314159f, 0.490088f, 0.13f, 1.78f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "nelson", -0.314159f, 0.452389f, 0.13f, 1.66f, 3.44f ),
+ MouthFlapperDefaultSetting( "nriviera", -0.314159f, 0.470973f, 0.13f, 1.88f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "otto", -0.314159f, 0.490088f, 0.13f, 2.00f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "patty", -0.314159f, 0.420973f, 0.136640f, 2.20f, 3.56f ), //done
+ MouthFlapperDefaultSetting( "ped0", -0.314159f, 0.452389f, 0.13f, 1.66f, 3.44f ),
+ MouthFlapperDefaultSetting( "ped1", -0.314159f, 0.452389f, 0.13f, 1.66f, 3.44f ),
+ MouthFlapperDefaultSetting( "ped2", -0.314159f, 0.452389f, 0.13f, 1.66f, 3.44f ),
+ MouthFlapperDefaultSetting( "ped3", -0.314159f, 0.452389f, 0.13f, 1.66f, 3.44f ),
+ MouthFlapperDefaultSetting( "ped4", -0.314159f, 0.452389f, 0.13f, 1.66f, 3.44f ),
+ MouthFlapperDefaultSetting( "ped5", -0.314159f, 0.452389f, 0.13f, 1.66f, 3.44f ),
+ MouthFlapperDefaultSetting( "ped6", -0.314159f, 0.452389f, 0.13f, 1.66f, 3.44f ),
+ MouthFlapperDefaultSetting( "ralph", -0.314159f, 0.471504f, 0.125664f, 2.22f, 4.00f ), //done
+ MouthFlapperDefaultSetting( "reward_barney", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ), //done
+ MouthFlapperDefaultSetting( "reward_homer", -0.314159f, 0.420973f, 0.136640f, 2.00f, 3.44f ), //done
+ MouthFlapperDefaultSetting( "reward_kearney",-0.314159f, 0.521504f, 0.125664f, 2.34f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "reward_otto", -0.314159f, 0.490088f, 0.13f, 2.00f, 3.78f ),
+ MouthFlapperDefaultSetting( "reward_willie", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ),
+ MouthFlapperDefaultSetting( "selma", -0.314159f, 0.420973f, 0.136640f, 2.20f, 3.56f ), //done
+ MouthFlapperDefaultSetting( "skinner", -0.314159f, 0.490088f, 0.13f, 2.00f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "smithers", -0.314159f, 0.490088f, 0.13f, 2.00f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "snake", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ), //done
+ MouthFlapperDefaultSetting( "teen", -0.314159f, 0.470973f, 0.13f, 1.88f, 3.78f ), //done
+ MouthFlapperDefaultSetting( "wiggum", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ), //done
+ MouthFlapperDefaultSetting( "willie", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ),
+ MouthFlapperDefaultSetting( "zfem1", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ),
+ MouthFlapperDefaultSetting( "zmale1", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ),
+ MouthFlapperDefaultSetting( "zmale2", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ),
+ MouthFlapperDefaultSetting( "zmale3", -0.314159f, 0.559204f, 0.13f, 2.56f, 4.12f ),
+};
+
+char gWatcherNMnameSpace[ 256 ] = "Presentation\\MouthFlapping\\";
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// MouthFlapper::MouthFlapper
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MouthFlapper::MouthFlapper() :
+ mJointIndex( -1 ),
+ mJoint( NULL ),
+ mCharacter( NULL ),
+ mCurrentdt( 0.0f ),
+ mDirection( -1.0f ),
+ mAngle( 0.0f ),
+ mSpeed( 0.0f ),
+ mMaxOpen( MAX_OPEN ),
+ mMinOpen( MIN_OPEN ),
+ mSetting( gDefaultSetting ),
+ mGotDefaultSettings( false )
+{
+}
+
+//==============================================================================
+// MouthFlapper::~MouthFlapper
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MouthFlapper::~MouthFlapper()
+{
+ mCharacter = NULL;
+}
+
+//=============================================================================
+// MouthFlapper::AddVariablesToWatcher
+//=============================================================================
+// Description: Adds all the global variables controlling mouth flapping to the
+// watcher so that they can be tuned
+//
+// Parameters: none
+//
+// Return: void
+//
+//=============================================================================
+#ifdef DEBUGWATCH
+void MouthFlapper::AddVariablesToWatcher()
+{
+ int size = GetNumberOfDefaultSettings();
+ int i;
+ for( i = 0; i < size; ++i )
+ {
+ gDefaultSettings[ i ].AddToWatcher();
+ }
+}
+#endif //DEBUGWATCH
+
+//=============================================================================
+// MouthFlapper::GetDefaultSettings
+//=============================================================================
+// Description: Comment
+//
+// Parameters: name - the name of the character who's mouth is supposed to
+// flap
+//
+// Return: void
+//
+//=============================================================================
+void MouthFlapper::GetDefaultSettings( const tName& name )
+{
+ int size = GetNumberOfDefaultSettings();
+ int i;
+ for( i = 0; i < size; ++i )
+ {
+ MouthFlapperDefaultSetting& setting = gDefaultSettings[ i ];
+ tName settingName = setting.GetName();
+ if( settingName == name )
+ {
+ //
+ // Assign the settings
+ //
+ mSetting = setting;
+ return;
+ }
+ }
+ rAssertMsg( false, "A character for whom we have no mouth flapping parameters is talking - please tell Ian which level and mission this was" );
+}
+
+//=============================================================================
+// MouthFlapper::GetNumberOfDefaultSettings
+//=============================================================================
+// Description: returns the nubmer of characters for which default parameters
+// have been hardcoded
+//
+// Parameters: none
+//
+// Return: unsigned int - how many parameters are there
+//
+//=============================================================================
+unsigned int MouthFlapper::GetNumberOfDefaultSettings()
+{
+ size_t totalSize = sizeof( gDefaultSettings );
+ size_t sizeOfEach = sizeof( MouthFlapperDefaultSetting );
+ return totalSize / sizeOfEach;
+}
+
+//=============================================================================
+// MouthFlapper::SetCharacter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* guy )
+//
+// Return: void
+//
+//=============================================================================
+void MouthFlapper::SetCharacter( Character* pCharacter )
+{
+ mCharacter = pCharacter;
+ if( pCharacter != NULL )
+ {
+ choreo::Puppet* pPuppet = pCharacter->GetPuppet( );
+
+ poser::Pose* pPose = pPuppet->GetPose();
+ tSkeleton* skeleton = pPose->GetSkeleton();
+ mJointIndex = skeleton->FindJointIndex( "Jaw" );
+
+ mJoint = pPose->GetJoint( mJointIndex );
+ }
+ else
+ {
+ mJointIndex = -1;
+ mJoint = NULL;
+ }
+
+ NeuSpeed();
+}
+
+//=============================================================================
+// MouthFlapper::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int elapsedtime )
+//
+// Return: void
+//
+//=============================================================================
+void MouthFlapper::Advance( float deltaTime )
+{
+ mCurrentdt += deltaTime;
+}
+
+//=============================================================================
+// MouthFlapper::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Pose* pose )
+//
+// Return: void
+//
+//=============================================================================
+void MouthFlapper::Update( poser::Pose* pose )
+{
+ //
+ // make sure that the default settings are properly applied
+ //
+ rAssert( mCharacter != NULL );
+ const tName& name = mCharacter->GetNameObject();
+ GetDefaultSettings( name );
+
+ if( mCurrentdt <= 0.0f )
+ {
+ return;
+ }
+
+ rAssert( mJointIndex >= 0 );
+ poser::Joint* joint = pose->GetJoint(mJointIndex);
+ poser::Transform pt = joint->GetObjectTransform();
+ rmt::Matrix m = pt.GetMatrix();
+ rmt::Matrix r;
+ r.Identity();
+ float dAngle = mSpeed * mCurrentdt * mDirection;
+ mAngle += dAngle;
+ if( mAngle > mMaxOpen )
+ {
+ mDirection = -mDirection;
+ mAngle = mMaxOpen;
+ NeuSpeed();
+ }
+
+ if( mAngle < mMinOpen )
+ {
+ mDirection = -mDirection;
+ mAngle = mMinOpen;
+ NeuSpeed();
+ }
+
+ r.FillRotateZ( mAngle );
+ m.Mult( r );
+ pt.SetMatrix(m);
+ joint->SetObjectTransform(pt);
+ mCurrentdt = 0.0f;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// MouthFlapper::NeuSpeed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MouthFlapper::NeuSpeed()
+{
+ float defaultMaxDeviation = mSetting.GetMaxDeviation();
+ float defaultMaxSpeed = mSetting.GetMaxSpeed();
+ float defaultMinSpeed = mSetting.GetMinSpeed();
+ float defaultMaxOpen = mSetting.GetMaxOpen();
+ float defaultMinOpen = mSetting.GetMinOpen();
+ mSpeed = defaultMinSpeed + ( defaultMaxSpeed - defaultMinSpeed ) * rand() / RAND_MAX;
+ mMaxOpen = defaultMaxOpen - defaultMaxDeviation * rand() / RAND_MAX;
+ mMinOpen = defaultMinOpen + defaultMaxDeviation * rand() / RAND_MAX;
+}
+
+//=============================================================================
+// MouthFlapperDefaultSetting::MouthFlapperDefaultSetting
+//=============================================================================
+// Description: constructor
+//
+// Parameters: N/A
+//
+// Return: N/A
+//
+//=============================================================================
+MouthFlapperDefaultSetting::MouthFlapperDefaultSetting(
+ const tName& name,
+ const float minOpen,
+ const float maxOpen,
+ const float maxDeviation,
+ const float minSpeed,
+ const float maxSpeed ) :
+ name( name ),
+ minOpen( minOpen ),
+ maxOpen( maxOpen ),
+ maxDeviation( maxDeviation ),
+ minSpeed( minSpeed ),
+ maxSpeed( maxSpeed )
+{
+}
+
+//=============================================================================
+// MouthFlapperDefaultSetting::MouthFlapperDefaultSetting
+//=============================================================================
+// Description: copy constructor
+//
+// Parameters: N/A
+//
+// Return: N/A
+//
+//=============================================================================
+MouthFlapperDefaultSetting::MouthFlapperDefaultSetting( const MouthFlapperDefaultSetting& right )
+{
+ *this = right;
+}
+
+//=============================================================================
+// MouthFlapperDefaultSetting::AddToWatcher
+//=============================================================================
+// Description: adds this set of mouth flapper parameters to the watcher
+//
+// Parameters: NONE
+//
+// Return: NONE
+//
+//=============================================================================
+void MouthFlapperDefaultSetting::AddToWatcher()
+{
+ char nameSpace[ 256 ] = "";
+ const char* nameString = name.GetText();
+ sprintf( nameSpace , "%s%s", gWatcherNMnameSpace, nameString );
+ radDbgWatchAddFloat( &minOpen, "Min Open", nameSpace, NULL, NULL, -rmt::PI, rmt::PI );
+ radDbgWatchAddFloat( &maxOpen, "Max Open", nameSpace, NULL, NULL, -rmt::PI, rmt::PI );
+ radDbgWatchAddFloat( &maxDeviation, "Max Deviation", nameSpace, NULL, NULL, 0.0f, 20.0f );
+ radDbgWatchAddFloat( &minSpeed, "Min Speed", nameSpace, NULL, NULL, 0.0f, 20.0f );
+ radDbgWatchAddFloat( &maxSpeed, "Max Speed", nameSpace, NULL, NULL, 0.0f, 20.0f );
+}
+
+//=============================================================================
+// MouthFlapperDefaultSetting::GetMaxDeviation
+//=============================================================================
+// Description: allows access to the maximum deviation
+//
+// Parameters: NONE
+//
+// Return: the maximum deviation parameter
+//
+//=============================================================================
+const float MouthFlapperDefaultSetting::GetMaxDeviation() const
+{
+ return maxDeviation;
+}
+
+//=============================================================================
+// MouthFlapperDefaultSetting::GetMaxOpen
+//=============================================================================
+// Description: allows access to the maxopen parameters
+//
+// Parameters: NONE
+//
+// Return: the max open parameter
+//
+//=============================================================================
+const float MouthFlapperDefaultSetting::GetMaxOpen() const
+{
+ return maxOpen;
+}
+
+//=============================================================================
+// MouthFlapperDefaultSetting::GetMaxSpeed
+//=============================================================================
+// Description: GetMaxSpeed
+//
+// Parameters: NONE
+//
+// Return: the max speed parameter
+//
+//=============================================================================
+const float MouthFlapperDefaultSetting::GetMaxSpeed() const
+{
+ return maxSpeed;
+}
+
+//=============================================================================
+// MouthFlapperDefaultSetting::GetMinOpen
+//=============================================================================
+// Description: GetMinOpen
+//
+// Parameters: NONE
+//
+// Return: the min open parameter
+//
+//=============================================================================
+const float MouthFlapperDefaultSetting::GetMinOpen() const
+{
+ return minOpen;
+}
+
+//=============================================================================
+// MouthFlapperDefaultSetting::GetMinSpeed
+//=============================================================================
+// Description: GetMinSpeed
+//
+// Parameters: NONE
+//
+// Return: the minSpeed parameter
+//
+//=============================================================================
+const float MouthFlapperDefaultSetting::GetMinSpeed() const
+{
+ return minSpeed;
+}
+
+//=============================================================================
+// MouthFlapperDefaultSetting::GetName
+//=============================================================================
+// Description: GetName
+//
+// Parameters: NONE
+//
+// Return: the name of the object
+//
+//=============================================================================
+const tName& MouthFlapperDefaultSetting::GetName() const
+{
+ return name;
+}
+
+//=============================================================================
+// MouthFlapperDefaultSetting::operator=
+//=============================================================================
+// Description: assignment operator
+//
+// Parameters: N/A
+//
+// Return: N/A
+//
+//=============================================================================
+MouthFlapperDefaultSetting& MouthFlapperDefaultSetting::operator=( const MouthFlapperDefaultSetting& right )
+{
+ if( this == &right )
+ {
+ return *this;
+ }
+
+ name = right.name;
+ minOpen = right.minOpen;
+ maxOpen = right.maxOpen;
+ maxDeviation = right.maxDeviation;
+ minSpeed = right.minSpeed;
+ maxSpeed = right.maxSpeed;
+ return *this;
+}
+
+//=============================================================================
+// MouthFlapperDefaultSetting::RemoveFromWatcher
+//=============================================================================
+// Description: removes this set of mouth flapper parameters to the watcher
+//
+// Parameters: NONE
+//
+// Return: NONE
+//
+//=============================================================================
+void MouthFlapperDefaultSetting::RemoveFromWatcher()
+{
+ radDbgWatchDelete( &minOpen );
+ radDbgWatchDelete( &maxOpen );
+ radDbgWatchDelete( &maxDeviation );
+ radDbgWatchDelete( &minSpeed );
+ radDbgWatchDelete( &maxSpeed );
+}
diff --git a/game/code/presentation/mouthflapper.h b/game/code/presentation/mouthflapper.h
new file mode 100644
index 0000000..0a4e600
--- /dev/null
+++ b/game/code/presentation/mouthflapper.h
@@ -0,0 +1,107 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: mouthflapper.h
+//
+// Description: Blahblahblah
+//
+// History: 09/09/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef MOUTHFLAPPER_H
+#define MOUTHFLAPPER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <poser/joint.hpp>
+#include <poser/posedriver.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+class poser::Joint;
+class poser::Pose;
+class Character;
+
+
+//=============================================================================
+//
+// Synopsis: This class contains all the internal settings for mouth
+// flapping
+//
+//=============================================================================
+class MouthFlapperDefaultSetting
+{
+public:
+ MouthFlapperDefaultSetting( const tName& name, const float minOpen, const float maxOpen, const float maxDeviation, const float minSpeed, const float maxSpeed );
+ MouthFlapperDefaultSetting( const MouthFlapperDefaultSetting& right );
+ MouthFlapperDefaultSetting& operator=( const MouthFlapperDefaultSetting& right );
+ void AddToWatcher();
+ const float GetMaxDeviation() const;
+ const float GetMaxOpen() const;
+ const float GetMaxSpeed() const;
+ const float GetMinOpen() const;
+ const float GetMinSpeed() const;
+ const tName& GetName() const;
+ void RemoveFromWatcher();
+protected:
+private:
+ tName name;
+ float minOpen;
+ float maxOpen;
+ float maxDeviation;
+ float minSpeed;
+ float maxSpeed;
+};
+
+//=============================================================================
+//
+// Synopsis: class for mouth flapping
+//
+//=============================================================================
+class MouthFlapper : public poser::PoseDriver
+{
+public:
+ MouthFlapper();
+ virtual ~MouthFlapper();
+
+#ifdef DEBUGWATCH
+ static void AddVariablesToWatcher();
+#endif DEBUGWATCH
+ virtual void Advance( float deltaTime );
+ void GetDefaultSettings( const tName& name );
+ void SetCharacter( Character* pCharacter );
+ virtual void Update( poser::Pose* pose );
+
+protected:
+ static unsigned int GetNumberOfDefaultSettings();
+
+private:
+
+ //Prevent wasteful constructor creation.
+ MouthFlapper( const MouthFlapper& mouthflapper );
+ MouthFlapper& operator=( const MouthFlapper& mouthflapper );
+
+ void NeuSpeed();
+
+ int mJointIndex;
+ poser::Joint* mJoint;
+ Character* mCharacter;
+
+ float mCurrentdt;
+ float mDirection;
+ float mAngle;
+ float mSpeed;
+ float mMaxOpen;
+ float mMinOpen;
+
+ MouthFlapperDefaultSetting mSetting;
+ bool mGotDefaultSettings;
+};
+
+
+#endif //MOUTHFLAPPER_H
diff --git a/game/code/presentation/nisplayer.cpp b/game/code/presentation/nisplayer.cpp
new file mode 100644
index 0000000..b13a9fb
--- /dev/null
+++ b/game/code/presentation/nisplayer.cpp
@@ -0,0 +1,163 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: nisplayer.cpp
+//
+// Description: Implement NISPlayer
+//
+// History: 16/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <presentation/nisplayer.h>
+#include <loading/filehandlerenum.h>
+#include <loading/loadingmanager.h>
+
+#include <render/rendermanager/rendermanager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// NISPlayer::NISPlayer
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+NISPlayer::NISPlayer() :
+ mpSceneGraph( NULL )
+{
+ SetExclusive( false );
+}
+
+//==============================================================================
+// NISPlayer::~NISPlayer
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+NISPlayer::~NISPlayer()
+{
+
+}
+
+
+//=============================================================================
+// NISPlayer::ClearData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NISPlayer::ClearData()
+{
+ SimpleAnimationPlayer::ClearData();
+
+ if( mpSceneGraph != NULL )
+ {
+ GetRenderManager()->mEntityDeletionList.Add((tRefCounted*&)mpSceneGraph);
+ mpSceneGraph = NULL;
+ }
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// NISPlayer::DoLoaded
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NISPlayer::DoLoaded()
+{
+ SimpleAnimationPlayer::DoLoaded();
+
+ tRefCounted::Release(mpSceneGraph);
+ tRefCounted::Assign(mpSceneGraph, (tDrawable*)p3d::find<Scenegraph::Scenegraph>( GetAnimationName() ));
+ if(!mpSceneGraph)
+ {
+ tRefCounted::Assign(mpSceneGraph, (tDrawable*)p3d::find<tCompositeDrawable>( GetAnimationName() ));
+ }
+}
+
+//=============================================================================
+// NISPlayer::DoRender
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NISPlayer::DoRender()
+{
+ SimpleAnimationPlayer::DoRender();
+
+ if(mpSceneGraph)
+ mpSceneGraph->Display();
+}
+
+//=============================================================================
+// NISPlayer::GetBoundingBox
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Box3D* box )
+//
+// Return: true if box filled out, false otherwise
+//
+//=============================================================================
+bool NISPlayer::GetBoundingBox( rmt::Box3D* box )
+{
+ bool retval = false;
+
+ if( mpSceneGraph )
+ {
+ mpSceneGraph->GetBoundingBox( box );
+ retval = true;
+ }
+
+ return( retval );
+}
diff --git a/game/code/presentation/nisplayer.h b/game/code/presentation/nisplayer.h
new file mode 100644
index 0000000..430eeb0
--- /dev/null
+++ b/game/code/presentation/nisplayer.h
@@ -0,0 +1,59 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 16/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef NISPLAYER_H
+#define NISPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <presentation/simpleanimationplayer.h>
+
+#include <p3d/scenegraph/scenegraph.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class NISPlayer : public SimpleAnimationPlayer
+{
+ public:
+ NISPlayer();
+ virtual ~NISPlayer();
+
+ virtual void ClearData();
+
+ bool GetBoundingBox( rmt::Box3D* box );
+ tDrawable* GetDrawable() { return mpSceneGraph; }
+
+ protected:
+ virtual void DoLoaded();
+ virtual void DoRender();
+
+ private:
+
+ //Prevent wasteful constructor creation.
+ NISPlayer( const NISPlayer& nisPlayer );
+ NISPlayer& operator=( const NISPlayer& nisPlayer );
+
+ tDrawable* mpSceneGraph;
+};
+
+
+#endif //NISPLAYER_H
+
diff --git a/game/code/presentation/playerdrawable.cpp b/game/code/presentation/playerdrawable.cpp
new file mode 100644
index 0000000..1f36147
--- /dev/null
+++ b/game/code/presentation/playerdrawable.cpp
@@ -0,0 +1,90 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: Playerdrawable.cpp
+//
+// Description: Implement PlayerDrawable
+//
+// History: 23/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <presentation/playerdrawable.h>
+#include <presentation/animplayer.h>
+#include <render/rendermanager/rendermanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// PlayerDrawable::PlayerDrawable
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PlayerDrawable::PlayerDrawable() :
+ mpPlayer( NULL )
+{
+}
+
+//==============================================================================
+// PlayerDrawable::~PlayerDrawable
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PlayerDrawable::~PlayerDrawable()
+{
+
+}
+
+//=============================================================================
+// PlayerDrawable::Display
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PlayerDrawable::Display()
+{
+ if(mpPlayer != NULL)
+ {
+ mpPlayer->Render();
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/presentation/playerdrawable.h b/game/code/presentation/playerdrawable.h
new file mode 100644
index 0000000..059ee1b
--- /dev/null
+++ b/game/code/presentation/playerdrawable.h
@@ -0,0 +1,58 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 23/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef PlayerDRAWABLE_H
+#define PlayerDRAWABLE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/drawable.hpp>
+#include <render/enums/renderenums.h>
+
+//========================================
+// Forward References
+//========================================
+
+class AnimationPlayer;
+
+//=============================================================================
+//
+// Synopsis: The PlayerDrawable is just a temporary thing for the DSG to
+// call and get the current presentation player to draw.
+//
+//=============================================================================
+
+class PlayerDrawable : public tDrawable
+{
+ public:
+ PlayerDrawable();
+ virtual ~PlayerDrawable();
+
+ void SetPlayer( AnimationPlayer* pPlayer ) { mpPlayer = pPlayer; }
+ virtual void Display();
+
+ void SetRenderLayer( RenderEnums::LayerEnum layer ) { mRenderLayer = layer; }
+ RenderEnums::LayerEnum GetRenderLayer() { return mRenderLayer; }
+
+ private:
+
+ //Prevent wasteful constructor creation.
+ PlayerDrawable( const PlayerDrawable& pPlayerDrawable );
+ PlayerDrawable& operator=( const PlayerDrawable& pPlayerDrawable );
+
+ AnimationPlayer* mpPlayer;
+ RenderEnums::LayerEnum mRenderLayer;
+};
+
+
+#endif // PlayerDRAWABLE_H
+
diff --git a/game/code/presentation/presentation.cpp b/game/code/presentation/presentation.cpp
new file mode 100644
index 0000000..c67fc91
--- /dev/null
+++ b/game/code/presentation/presentation.cpp
@@ -0,0 +1,1466 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: presentation.cpp
+//
+// Description: Implement PresentationManager
+//
+// History: 16/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <p3d/view.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <events/eventmanager.h>
+#include <events/eventenum.h>
+#include <events/eventdata.h>
+#include <memory/srrmemory.h>
+#include <contexts/context.h>
+#include <contexts/bootupcontext.h>
+#include <contexts/gameplay/gameplaycontext.h>
+#include <gameflow/gameflow.h>
+#include <meta/eventlocator.h>
+#include <meta/triggervolume.h>
+#include <presentation/playerdrawable.h>
+#include <presentation/presentation.h>
+#include <presentation/presentationanimator.h>
+#include <presentation/nisplayer.h>
+#include <presentation/tutorialmanager.h>
+#include <presentation/cameraplayer.h>
+#include <presentation/transitionplayer.h>
+#include <presentation/gui/guimanager.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guitextbible.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guiscreenmissionload.h>
+#include <presentation/mouthflapper.h>
+#include <presentation/fmvplayer/fmvplayer.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/conversationcam.h>
+#include <camera/isupercamtarget.h>
+
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/charactertarget.h>
+#include <worldsim/avatarmanager.h>
+#include <p3d/matrixstack.hpp>
+
+#include <render/rendermanager/rendermanager.h>
+#include <render/RenderManager/RenderLayer.h>
+#include <render/enums/renderenums.h>
+#include <mission/gameplaymanager.h>
+#include <mission/objectives/missionobjective.h>
+#include <screen.h>
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+PresentationManager* PresentationManager::spInstance = NULL;
+const int PLAYER_ONE = 0;
+const int MAX_DIALOG_LINES = 64;
+#define MAX_CHARACTERS 64
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// PresentationManager::CreateInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: PresentationManager
+//
+//=============================================================================
+PresentationManager* PresentationManager::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "PresentationManager" );
+ if( spInstance == NULL )
+ {
+ spInstance = new(GMA_PERSISTENT) PresentationManager;
+ rAssert( spInstance );
+ }
+MEMTRACK_POP_GROUP( "PresentationManager" );
+
+ return spInstance;
+}
+
+//=============================================================================
+// PresentationManager::GetInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: PresentationManager
+//
+//=============================================================================
+PresentationManager* PresentationManager::GetInstance()
+{
+ return spInstance;
+}
+
+//=============================================================================
+// PresentationManager::DestroyInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::DestroyInstance()
+{
+ if( spInstance != NULL )
+ {
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+ }
+}
+
+//==============================================================================
+// PresentationManager::PresentationManager
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PresentationManager::PresentationManager() :
+ mTransitionPool( NULL ),
+ mNISPool( NULL ),
+ mFMVPool( NULL ),
+ mFIFOBegin( 0 ),
+ mFIFOEnd( 0 ),
+ mpCurrent( NULL ),
+ mpFMVPlayer( NULL ),
+ mpNISPlayer( NULL ),
+ mpCameraPlayer( NULL ),
+ mpTransitionPlayer( NULL ),
+// mLanguage( Language::MAX_LANGUAGES ),
+ mp_PCAnimator( NULL ),
+ mp_NPCAnimator( NULL ),
+ mDialogLineNumber( -1 ),
+ mCameraForLineOfDialog(MAX_DIALOG_LINES),
+ mpPlayCallback( 0 ),
+ mWaitingOnFade( false ),
+ mOverlay( 0 )
+{
+ mInConversation = false;
+ // mOldFOV= 0.0F;
+
+ unsigned int i;
+
+ for( i = 0; i < MAX_EVENT_SIZE; i++ )
+ {
+ mEventFIFO[ i ] = NULL;
+ }
+
+ // what is the current allocator
+ radMemoryAllocator current = HeapManager::GetInstance()->GetCurrentAllocator();
+ GameMemoryAllocator gmaCurrent = HeapManager::GetInstance()->GetCurrentHeap();
+
+ mFMVPool = new FMVEventPool( gmaCurrent, 10 );
+ mNISPool = new NISEventPool( gmaCurrent, 10 );
+ mTransitionPool = new TransitionEventPool( gmaCurrent, 10 );
+ mpNISPlayer = new NISPlayer();
+ mpCameraPlayer = new CameraPlayer();
+ mpTransitionPlayer = new TransitionPlayer();
+ mpFMVPlayer = new( current ) FMVPlayer();
+ mpPlayerDrawable = new PlayerDrawable();
+ mpPlayerDrawable->AddRef();
+ mOverlay = new PresentationOverlay();
+ mOverlay->AddRef();
+
+ mp_oldcam = NULL;
+ mOldCamIndexNum =0;
+}
+
+//==============================================================================
+// PresentationManager::~PresentationManager
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PresentationManager::~PresentationManager()
+{
+ delete mFMVPool; mFMVPool = NULL;
+ delete mNISPool; mNISPool = NULL;
+ delete mTransitionPool; mTransitionPool = NULL;
+ delete mpNISPlayer; mpNISPlayer = NULL;
+ delete mpCameraPlayer; mpCameraPlayer = NULL;
+ delete mpTransitionPlayer; mpTransitionPlayer = NULL;
+ delete mpFMVPlayer; mpFMVPlayer = NULL;
+
+ this->FinalizePlayerDrawable();
+
+ mpPlayerDrawable->ReleaseVerified();
+ mOverlay->Release();
+}
+
+//=============================================================================
+// PresentationManager::Initialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::Initialize()
+{
+MEMTRACK_PUSH_GROUP( "PresentationManager" );
+ //Cary: I took this out since it was allocating to the levelslot when
+ //the level isn't even loaded. No one seems to know what it's for
+ //so Darryl probably did it and it's not used anymore. Feb 13th 2003
+ //pLayer = GetRenderManager()->mpLayer( RenderEnums::LevelSlot );
+ //pLayer->AddGuts( mpPlayerDrawable );
+
+ GetEventManager()->AddListener( this, EVENT_LOCATOR );
+
+ //Listen for Conversation
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_INIT );
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_SKIP );
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_DONE );
+
+ //Listen for MouthFlapping Cues
+ GetEventManager()->AddListener( this, EVENT_PC_TALK );
+ GetEventManager()->AddListener( this, EVENT_PC_SHUTUP );
+ GetEventManager()->AddListener( this, EVENT_NPC_TALK );
+ GetEventManager()->AddListener( this, EVENT_NPC_SHUTUP );
+ GetEventManager()->AddListener( this, EVENT_GUI_FADE_OUT_DONE );
+
+ rAssert( mp_PCAnimator == NULL );
+ rAssert( mp_NPCAnimator == NULL );
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ mp_PCAnimator = new PresentationAnimator();
+ mp_NPCAnimator = new PresentationAnimator();
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+MEMTRACK_POP_GROUP( "PresentationManager" );
+}
+
+//=============================================================================
+// PresentationManager::Finalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::Finalize()
+{
+ mCameraForLineOfDialog.erase( mCameraForLineOfDialog.begin(), mCameraForLineOfDialog.end() );
+
+ mp_PCAnimator->SetCharacter( NULL );
+ mp_NPCAnimator->SetCharacter( NULL );
+ mp_PCAnimator->ClearAmbientAnimations();
+ mp_NPCAnimator->ClearAmbientAnimations();
+
+ //remove for Conversation
+ GetEventManager()->RemoveListener( this, EVENT_CONVERSATION_INIT );
+ GetEventManager()->RemoveListener( this, EVENT_CONVERSATION_SKIP );
+ GetEventManager()->RemoveListener( this, EVENT_CONVERSATION_DONE );
+
+ //remove for MouthFlapping Cues
+ GetEventManager()->RemoveListener( this, EVENT_PC_TALK );
+ GetEventManager()->RemoveListener( this, EVENT_PC_SHUTUP );
+ GetEventManager()->RemoveListener( this, EVENT_NPC_TALK );
+ GetEventManager()->RemoveListener( this, EVENT_NPC_SHUTUP );
+
+ delete mp_PCAnimator; mp_PCAnimator = NULL;
+ delete mp_NPCAnimator; mp_NPCAnimator = NULL;
+}
+
+//=============================================================================
+// PresentationManager::InitializePlayerDrawable
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void
+PresentationManager::InitializePlayerDrawable()
+{
+ RenderLayer* pLayer = GetRenderManager()->mpLayer( RenderEnums::PresentationSlot );
+ rAssert( pLayer != NULL );
+ pLayer->pView( 0 )->SetClearColour( tColour( 0, 0, 0 ) );
+ pLayer->AddGuts( mpPlayerDrawable );
+}
+
+//=============================================================================
+// PresentationManager::FinalizePlayerDrawable
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void
+PresentationManager::FinalizePlayerDrawable()
+{
+ RenderLayer* pLayer = GetRenderManager()->mpLayer( RenderEnums::PresentationSlot );
+ rAssert( pLayer != NULL );
+ pLayer->RemoveGuts( mpPlayerDrawable );
+}
+
+//=============================================================================
+// PresentationManager::GetAnimatorNpc
+//=============================================================================
+// Description: returns the presentation animator for the NPC
+//
+// Parameters: NONE
+//
+// Return: pointer to the presentationanimator
+//
+//=============================================================================
+PresentationAnimator* PresentationManager::GetAnimatorNpc()
+{
+ return this->mp_NPCAnimator;
+}
+
+//=============================================================================
+// PresentationManager::GetAnimatorPc
+//=============================================================================
+// Description: returns the presentation animator for the PC
+//
+// Parameters: NONE
+//
+// Return: pointer to the presentationanimator
+//
+//=============================================================================
+PresentationAnimator* PresentationManager::GetAnimatorPc()
+{
+ return this->mp_PCAnimator;
+}
+
+/*=============================================================================
+Description: You're one stop method for playing an FMV during gameplay. It
+ unloads the HUD (for memory), plays the FMV, and then
+ reloads the HUD. Don't use this in the frontend as the
+ un/reloading of the frontend is a waste.
+ NOTE that the platform specific path for the movies is prepended
+ to the filename. So if you pass "fmv2.rmv" you'll get
+ "d:\movies\fmv2.rmv" on XBox, "movies\fmv2.rmv" on PS2,
+ "movies/fmv2.rmv" on GC.
+=============================================================================*/
+void PresentationManager::PlayFMV( const char* FileName,
+ PresentationEvent::PresentationEventCallBack* pCallback,
+ bool IsSkippable,
+ bool StopMusic,
+ bool IsLocalized )
+{
+ // this puts the FMV player in the "loading" state
+ // this is needed so that the gag system can tell when a gag has ended
+ // (without this the state right before and right after the fmc playes is the same
+ mpFMVPlayer->PreLoad();
+
+#ifdef RAD_E3
+ // no in-game FMV's for E3 build
+ //
+ if( pCallback != NULL )
+ {
+ pCallback->OnPresentationEventEnd( NULL );
+ }
+
+ return;
+#endif
+
+ if( mOverlay )
+ {
+ //
+ // Hide the HUD
+ //
+ CGuiScreenHud* hud = dynamic_cast< CGuiScreenHud* >( GetGuiSystem()->GetInGameManager()->FindWindowByID( CGuiWindow::GUI_SCREEN_ID_HUD ) );
+ rAssert( hud != NULL );
+ Scrooby::Screen* screen = hud->GetScroobyScreen();
+ screen->SetAlpha( 0.0f );
+
+
+ mOverlay->SetStart( BLACK_TRANSPARENT );
+ mOverlay->SetEnd( BLACK );
+ mOverlay->SetDuration( 0.5f );
+ RenderLayer* pLayer = GetRenderManager()->mpLayer( RenderEnums::PresentationSlot );
+ rAssert( pLayer );
+ if( pLayer->IsDead() )
+ {
+ pLayer->Resurrect();
+ }
+ pLayer->Thaw();
+ pLayer->AddGuts( mOverlay );
+ }
+ mWaitingOnFade = true;
+ FMVEvent* pEvent = 0;
+ GetPresentationManager()->QueueFMV( &pEvent, this );
+#ifdef RAD_XBOX
+ strcpy( pEvent->fileName, "D:\\movies\\" );
+#elif RAD_PS2
+ strcpy( pEvent->fileName, "movies\\" );
+#elif RAD_WIN32
+ strcpy( pEvent->fileName, "movies\\" );
+#else
+ strcpy( pEvent->fileName, "movies/" );
+#endif
+ strcat( pEvent->fileName, FileName );
+ pEvent->SetRenderLayer( RenderEnums::PresentationSlot );
+ pEvent->SetAutoPlay( true );
+
+#ifdef PAL
+ if( IsLocalized )
+ {
+ switch( CGuiTextBible::GetCurrentLanguage() )
+ {
+ case Scrooby::XL_FRENCH:
+ {
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_FRENCH );
+
+ break;
+ }
+ case Scrooby::XL_GERMAN:
+ {
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_GERMAN );
+
+ break;
+ }
+ case Scrooby::XL_SPANISH:
+ {
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_SPANISH );
+
+ break;
+ }
+ default:
+ {
+ rAssert( CGuiTextBible::GetCurrentLanguage() == Scrooby::XL_ENGLISH );
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_ENGLISH );
+
+ break;
+ }
+ }
+ }
+ else
+#endif // PAL
+ {
+ pEvent->SetAudioIndex( FMVEvent::AUDIO_INDEX_ENGLISH );
+ }
+
+#ifdef RAD_GAMECUBE
+ pEvent->SetAllocator( GMA_ANYWHERE_IN_LEVEL );
+#else
+ pEvent->SetAllocator( GMA_LEVEL_HUD );
+#endif
+ pEvent->SetClearWhenDone( true );
+ pEvent->SetKeepLayersFrozen( true );
+ pEvent->SetSkippable(IsSkippable);
+ if( StopMusic )
+ {
+ pEvent->KillMusic();
+ }
+ mpPlayCallback = pCallback;
+}
+/*=============================================================================
+Description: This is a callback when the event queued by PlayFMV.
+=============================================================================*/
+void PresentationManager::OnPresentationEventBegin( PresentationEvent* pEvent )
+{
+ Context* context = GetGameFlow()->GetContext( GetGameFlow()->GetCurrentContext() );
+ if( dynamic_cast<GameplayContext*>( context ) )
+ {
+ static_cast<GameplayContext*>( context )->PauseAllButPresentation( true );
+ }
+ GetGuiSystem()->HandleMessage( GUI_MSG_RELEASE_INGAME );
+ if( mpPlayCallback )
+ {
+ mpPlayCallback->OnPresentationEventBegin( pEvent );
+ }
+ RenderLayer* rl = GetRenderManager()->mpLayer( RenderEnums::PresentationSlot );
+ rAssert( rl );
+ rl->RemoveGuts( mOverlay );
+}
+/*=============================================================================
+Description: This is a callback when the event queued by PlayFMV.
+=============================================================================*/
+void PresentationManager::OnPresentationEventLoadComplete( PresentationEvent* pEvent )
+{
+ if( mpPlayCallback )
+ {
+ mpPlayCallback->OnPresentationEventLoadComplete( pEvent );
+ }
+}
+/*=============================================================================
+Description: This is a callback when the event queued by PlayFMV.
+=============================================================================*/
+void PresentationManager::OnPresentationEventEnd( PresentationEvent* pEvent )
+{
+ HeapMgr()->PushHeap (GMA_LEVEL_HUD);
+ GetGuiSystem()->HandleMessage( GUI_MSG_INIT_INGAME );
+ HeapMgr()->PopHeap (GMA_LEVEL_HUD);
+ GetLoadingManager()->AddCallback( this );
+ if( mOverlay )
+ {
+ mOverlay->SetStart( BLACK_TRANSPARENT );
+ mOverlay->SetEnd( BLACK );
+ mOverlay->SetDuration( 0.5f );
+ //mOverlay->SetRemoveOnComplete( true );
+ RenderLayer* pLayer = GetRenderManager()->mpLayer( RenderEnums::PresentationSlot );
+ rAssert( pLayer );
+ pLayer->AddGuts( mOverlay );
+ }
+}
+/*=============================================================================
+Description:
+=============================================================================*/
+void PresentationManager::OnProcessRequestsComplete( void* pUserData )
+{
+ Context* context = GetGameFlow()->GetContext( GetGameFlow()->GetCurrentContext() );
+ if( dynamic_cast<GameplayContext*>( context ) )
+ {
+ static_cast<GameplayContext*>( context )->PauseAllButPresentation( false );
+ }
+ GetRenderManager()->ThawFromPresentation();
+ GetGuiSystem()->HandleMessage( GUI_MSG_RUN_INGAME );
+ if( mpPlayCallback )
+ {
+ mpPlayCallback->OnPresentationEventEnd( 0 );
+ }
+ mpPlayCallback = 0;
+ if( mOverlay )
+ {
+ mOverlay->SetEnd( BLACK );
+ mOverlay->SetFrames( 10 );
+ mOverlay->SetRemoveOnComplete( true );
+ RenderLayer* pLayer = GetRenderManager()->mpLayer( RenderEnums::PresentationSlot );
+ rAssert( pLayer );
+ pLayer->AddGuts( mOverlay );
+ }
+}
+//=============================================================================
+// PresentationManager::QueueFMV
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( FMVEvent** pFMVEvent, PresentationEvent::PresentationEventCallBack* pCallback )
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::QueueFMV( FMVEvent** pFMVEvent, PresentationEvent::PresentationEventCallBack* pCallback )
+{
+ (*pFMVEvent) = mFMVPool->AllocateFromPool();
+ (*pFMVEvent)->Init();
+ (*pFMVEvent)->SetAllocator(GMA_LEVEL_MOVIE);
+ (*pFMVEvent)->pCallback = pCallback;
+
+ AddToQueue( *pFMVEvent );
+}
+
+//=============================================================================
+// PresentationManager::QueueNIS
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( NISEvent** pNISEvent, PresentationEvent::PresentationEventCallBack* pCallback )
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::QueueNIS( NISEvent** pNISEvent, PresentationEvent::PresentationEventCallBack* pCallback )
+{
+ (*pNISEvent) = mNISPool->AllocateFromPool();
+ (*pNISEvent)->Init();
+
+ (*pNISEvent)->pCallback = pCallback;
+
+ AddToQueue( *pNISEvent );
+}
+
+//=============================================================================
+// PresentationManager::QueueTransition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( TransitionEvent** pTransitionEvent,
+// PresentationEvent::PresentationEventCallBack* pCallback )
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::QueueTransition( TransitionEvent** pTransitionEvent,
+ PresentationEvent::PresentationEventCallBack* pCallback )
+{
+ (*pTransitionEvent) = mTransitionPool->AllocateFromPool();
+ (*pTransitionEvent)->Init();
+
+ (*pTransitionEvent)->pCallback = pCallback;
+
+ AddToQueue( *pTransitionEvent );
+}
+
+bool PresentationManager::IsBusy(void) const
+{
+ return (mpCurrent != 0) || mWaitingOnFade;
+}
+
+//=============================================================================
+// PresentationManager::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::Update( unsigned int elapsedTime )
+{
+ GetTutorialManager()->Update( static_cast< float >( elapsedTime ) );
+
+ if( mp_PCAnimator != NULL )
+ {
+ mp_PCAnimator->Update( static_cast<int>( elapsedTime ));
+ }
+
+ if( mp_NPCAnimator != NULL )
+ {
+ mp_NPCAnimator->Update( static_cast<int>( elapsedTime ));
+ }
+
+ rAssert( mOverlay != NULL );
+ mOverlay->Update( elapsedTime );
+
+ if( mWaitingOnFade )
+ {
+ if( mOverlay->GetAlpha() == 0.0f )
+ {
+ // get one extra frame of full black.
+ mWaitingOnFade = false;
+ }
+ return;
+ }
+ if( mpCurrent == NULL )
+ {
+ mpCurrent = GetFirst();
+
+ if (mpCurrent != NULL )
+ {
+ if( mpCurrent->pCallback != NULL )
+ {
+ mpCurrent->pCallback->OnPresentationEventBegin( mpCurrent );
+ }
+ mpPlayerDrawable->SetPlayer( mpCurrent->GetPlayer() );
+ mpPlayerDrawable->SetRenderLayer( mpCurrent->GetRenderLayer() );
+
+ mpCurrent->Start();
+ mWaitingOnFade = false;
+ }
+ }
+
+ if( mpCurrent != NULL )
+ {
+ bool finished = !mpCurrent->Update( elapsedTime );
+
+ if( finished )
+ {
+ if( mpCurrent->pCallback != NULL )
+ {
+ mpCurrent->pCallback->OnPresentationEventEnd( mpCurrent );
+ }
+ mpCurrent->Stop();
+ mpPlayerDrawable->SetPlayer( NULL );
+
+ ReturnToPool( mpCurrent );
+
+ mpCurrent = NULL;
+ }
+ }
+}
+
+//=============================================================================
+// PresentationManager::ClearQueue
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::ClearQueue()
+{
+ if( mpCurrent == NULL )
+ {
+ mpCurrent = GetFirst();
+ }
+
+ while ( mpCurrent != NULL )
+ {
+ if( mpCurrent->pCallback != NULL )
+ {
+ mpCurrent->pCallback->OnPresentationEventEnd( mpCurrent );
+ }
+
+ mpCurrent->Stop();
+
+ ReturnToPool( mpCurrent );
+
+ mpCurrent = GetFirst();
+ };
+
+ mpPlayerDrawable->SetPlayer( NULL );
+}
+
+//=============================================================================
+// PresentationManager::GetCameraTargetForLineOfDialog
+//=============================================================================
+// Description: what should the camera look at for this line of dialog
+//
+// Parameters: lineOfDialog - which line are we on?
+//
+// Return: CameraTarget - PC, NPC, or Don't Care
+//
+//=============================================================================
+const tName PresentationManager::GetCameraTargetForLineOfDialog( const unsigned int lineOfDialog ) const
+{
+ size_t size = mCameraForLineOfDialog.size();
+ if( lineOfDialog >= size )
+ {
+ return tName( "NONE" );
+ }
+ else
+ {
+ const tName& returnMe = mCameraForLineOfDialog[ lineOfDialog ];
+ return returnMe;
+ }
+}
+//=============================================================================
+// PresentationManager::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_GUI_FADE_OUT_DONE:
+ {
+ if( mWaitingOnFade )
+ {
+ mWaitingOnFade = false;
+ }
+ }
+ break;
+ case EVENT_LOCATOR:
+ {
+/*
+ EventLocator* locator = (EventLocator*)pEventData;
+ if( locator->GetFlag(Locator::ACTIVE) )
+ {
+ if( strcmp( locator->GetTriggerVolume()->GetName(), "TestTrigger" ) == 0 )
+ {
+ NISEvent* pEvent;
+ QueueNIS( &pEvent, NULL );
+ pEvent->fileName = "art\\camtest.p3d";
+ pEvent->type = NISEvent::NIS_CAMERA;
+ strcpy( pEvent->camera, "cameraShape1" );
+ strcpy( pEvent->animation, "CAM_cameraShape1" );
+ strcpy( pEvent->controller, "MasterController" );
+ pEvent->LoadFromInventory();
+ }
+ }
+*/
+ break;
+ }
+
+ case EVENT_CONVERSATION_INIT:
+ {
+ DialogEventData* p_dialogstruct = static_cast <DialogEventData*> (pEventData);
+
+ //
+ // Register the character and the NPC with the conversation camera
+ //
+ SuperCam* camera = GetSuperCamManager()->GetSCC( 0 )->GetSuperCam( SuperCam::CONVERSATION_CAM );
+ ConversationCam* convCam = dynamic_cast< ConversationCam* >( camera );
+ convCam->SetCharacter( 0, p_dialogstruct->char1 );
+ convCam->SetCharacter( 1, p_dialogstruct->char2 );
+
+ rAssert(p_dialogstruct);
+ rAssert( p_dialogstruct->char1 != NULL );
+ rAssert( p_dialogstruct->char2 != NULL );
+
+ //
+ // some conversations are special - race missions for example
+ // require different bitmaps
+ //
+ Character* char1 = p_dialogstruct->char1;
+ Character* char2 = p_dialogstruct->char2;
+ Character* npc = NULL;
+ Character* pc = NULL;
+
+ if( char1->IsNPC() )
+ {
+ npc = char1;
+ pc = char2;
+ }
+ else
+ {
+ rAssert( char2->IsNPC() );
+ npc = char2;
+ pc = char1;
+ }
+
+ mp_PCAnimator->SetCharacter(pc);
+ mp_NPCAnimator->SetCharacter(npc);
+
+ if(pc->GetStateManager()->GetState() == CharacterAi::INCAR)
+ {
+ GetAvatarManager()->PutCharacterOnGround(pc, pc->GetTargetVehicle());
+ }
+
+ //
+ // Make the characters face one another
+ //
+
+ // If we're talking to patty and selma, we need to make selma face the user
+ GameplayManager* gpm = GetGameplayManager();
+ Mission* mission = gpm->GetCurrentMission();
+ bool pattyAndSelma = mission->GetCurrentStage()->GetObjective()->IsPattyAndSelmaDialog();
+ if( pattyAndSelma )
+ {
+ CharacterManager* cm = GetCharacterManager();
+ Character* selma = cm->GetCharacterByName( "selma" );
+ if(selma == NULL && GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L7)
+ {
+ selma = cm->GetMissionCharacter("zmale1");
+ rTuneAssert(selma != NULL);
+ }
+ MakeCharactersFaceEachOther( selma, pc );
+
+ }
+ MakeCharactersFaceEachOther( char1, char2 );
+
+ mDialogLineNumber = -1;
+ mOldCamIndexNum= GetSuperCamManager()->GetSCC(PLAYER_ONE)->GetActiveSuperCamIndex();
+
+ // don't swap to close up camera if we are using a static cam (generally in an interior,
+ // where the camera movement could screw us up)
+ if(GetSuperCamManager()->GetSCC(PLAYER_ONE)->AllowCameraToggle())
+ {
+ if ( GetGameplayManager()->GetCurrentMission()->DialogueCharactersTeleported() )
+ {
+ GetSuperCamManager()->GetSCC(PLAYER_ONE)->SelectSuperCam(SuperCam::CONVERSATION_CAM, SuperCamCentral::CUT, 0 );
+ }
+ else
+ {
+ GetSuperCamManager()->GetSCC(PLAYER_ONE)->SelectSuperCam(SuperCam::CONVERSATION_CAM, 0 );
+ }
+ GetSuperCamManager()->GetSCC(PLAYER_ONE)->AddTarget( mp_NPCAnimator->GetCharacter()->GetTarget() );
+ }
+
+ //TO DO call Character eye blinking
+ mInConversation =true;
+
+
+ // make the talk-to NPC stand still
+ rAssert( npc );
+ static_cast<NPCController*>(npc->GetController())->TransitToState( NPCController::TALKING_WITH_PLAYER );
+
+ tName name = npc->GetNameObject();
+ ReplaceMissionBriefingBitmap( name );
+ break;
+ }
+
+ case EVENT_PC_TALK:
+ {
+ if( InConversation() )
+ {
+ ++mDialogLineNumber;
+ tName target = GetCameraTargetForLineOfDialog( mDialogLineNumber );
+ if( target == tName( "NONE" ) )
+ {
+ ConversationCam::UsePcCam();
+ }
+ else
+ {
+ ConversationCam::SetCameraByName( target );
+ }
+ mp_PCAnimator->PlaySpecialAmbientAnimation();
+ mp_NPCAnimator->PlaySpecialAmbientAnimation();
+ }
+ else
+ {
+ Avatar* av = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ Character* character = av->GetCharacter();
+ mp_PCAnimator->SetCharacter( character );
+
+ }
+ mp_PCAnimator->StartTalking();
+ break;
+ }
+ case EVENT_PC_SHUTUP:
+ {
+ mp_PCAnimator->StopTalking();
+ break;
+ }
+ case EVENT_NPC_TALK:
+ {
+ if( InConversation() )
+ {
+ ++mDialogLineNumber;
+ const tName& target = GetCameraTargetForLineOfDialog( mDialogLineNumber );
+ if( target == tName( "NONE" ) )
+ {
+ ConversationCam::UseNpcCam();
+ }
+ else
+ {
+ ConversationCam::SetCameraByName( target );
+ }
+ mp_NPCAnimator->PlaySpecialAmbientAnimation();
+ mp_PCAnimator->PlaySpecialAmbientAnimation();
+ }
+ else
+ {
+ Character* character = reinterpret_cast< Character* >( pEventData );
+ mp_NPCAnimator->SetCharacter( character );
+ }
+ mp_NPCAnimator->StartTalking();
+ break;
+ }
+ case EVENT_NPC_SHUTUP:
+ {
+ mp_NPCAnimator->StopTalking();
+ if( !InConversation() )
+ {
+ mp_NPCAnimator->SetCharacter( NULL );
+ }
+ break;
+ }
+
+ case EVENT_CONVERSATION_SKIP:
+ {
+ //
+ // Eventually this will take us to CONVERSATION_DONE
+ //
+ mInConversation = false;
+ break;
+ }
+ case EVENT_CONVERSATION_DONE:
+ {
+ //change camera to previous
+
+ if ( GetGameplayManager()->GetCurrentMission()->DialogueCharactersTeleported() )
+ {
+ GetSuperCamManager()->GetSCC(PLAYER_ONE)->SelectSuperCam(mOldCamIndexNum, SuperCamCentral::CUT, 0);
+ }
+ else
+ {
+ GetSuperCamManager()->GetSCC(PLAYER_ONE)->SelectSuperCam(mOldCamIndexNum, 0);
+ }
+
+ // resume walking...
+ Character* npc = mp_NPCAnimator->GetCharacter();
+ if( npc )
+ {
+ static_cast<NPCController*>(npc->GetController())->TransitToState( NPCController::STOPPED );
+ }
+
+ //TO DO stop eyeblinking.
+
+ mInConversation = false;
+ if ( mp_PCAnimator->GetCharacter() != NULL)
+ {
+ mp_PCAnimator->StopTalking();
+ mp_PCAnimator->SetCharacter(NULL);
+ }
+
+ if ( mp_NPCAnimator->GetCharacter() != NULL)
+ {
+ mp_NPCAnimator->StopTalking();
+ mp_NPCAnimator->SetCharacter(NULL);
+ }
+
+
+
+ break;
+ }
+ default:
+ {
+ // don't be lazy! handle the event!
+ rAssert( false );
+ }
+ }
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// PresentationManager::AddToQueue
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( PresentationEvent* pEvent )
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::AddToQueue( PresentationEvent* pEvent )
+{
+ // if you hit this assert then chances are the presentation queue is full
+ rAssert( mFIFOEnd < MAX_EVENT_SIZE );
+ rAssert( mEventFIFO[ mFIFOEnd ] == NULL );
+
+ mEventFIFO[ mFIFOEnd ] = pEvent;
+
+ mFIFOEnd++;
+ if( mFIFOEnd >= MAX_EVENT_SIZE )
+ {
+ mFIFOEnd = 0;
+ }
+}
+
+//=============================================================================
+// PresentationManager::ReturnToPool
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( PresentationEvent* presevent )
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::ReturnToPool( PresentationEvent* presevent )
+{
+ // change to use appropriate pool without doing it the dumb way
+ mFMVPool->ReturnToPool( (unsigned int)presevent );
+ mNISPool->ReturnToPool( (unsigned int)presevent );
+ mTransitionPool->ReturnToPool( (unsigned int)presevent );
+}
+
+//=============================================================================
+// PresentationManager::GetFirst
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: PresentationEvent
+//
+//=============================================================================
+PresentationEvent* PresentationManager::GetFirst()
+{
+ rAssert( mFIFOBegin < MAX_EVENT_SIZE );
+
+ if( mEventFIFO[ mFIFOBegin ] != NULL )
+ {
+ PresentationEvent* pEvent = mEventFIFO[ mFIFOBegin ];
+
+ mEventFIFO[ mFIFOBegin ] = NULL;
+
+ mFIFOBegin++;
+ if( mFIFOBegin >= MAX_EVENT_SIZE )
+ {
+ mFIFOBegin = 0;
+ }
+
+ return( pEvent );
+ }
+ else
+ {
+ return( NULL );
+ }
+}
+
+
+//Do stuff once we are in the gameplay context
+void PresentationManager::OnGameplayStart()
+{
+ //get ptr to the Players Avatar
+ mp_PCAnimator->SetCharacter(GetCharacterManager()->GetCharacter(0) );
+}
+
+void PresentationManager::OnGameplayStop()
+{
+}
+
+//=============================================================================
+// PresentationManager::SetCamerasForLineOfDialog
+//=============================================================================
+// Description: sets the cameras for specific lines of dialog
+//
+// Parameters: names - a vector of names that represnet cameras for lines of
+// dialog
+//
+// Return: NONE
+//
+//=============================================================================
+void PresentationManager::SetCamerasForLineOfDialog( const TNAMEVECTOR& names )
+{
+ #ifdef RAD_DEBUG
+ size_t size = names.size();
+ size_t i;
+ for( i = 0; i < size; ++i )
+ {
+ tName name = names[ i ];
+ }
+ #endif
+ mCameraForLineOfDialog.erase( mCameraForLineOfDialog.begin(), mCameraForLineOfDialog.end() );
+ mCameraForLineOfDialog.insert( mCameraForLineOfDialog.begin(), names.begin(), names.end() );
+}
+
+//=============================================================================
+// PresentationManager::InConversation()
+//=============================================================================
+// Description: determines if the game is in a conversation or not
+//
+// Parameters: NONE
+//
+// Return: bool - are we in a conversation or not?
+//
+//=============================================================================
+bool PresentationManager::InConversation() const
+{
+ return mInConversation;
+}
+
+//=============================================================================
+// PresentationManager::StopAll
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::StopAll()
+{
+#ifdef RAD_WIN32
+ mpFMVPlayer->ForceStop();
+#else
+ mpFMVPlayer->Stop();
+#endif
+ mpNISPlayer->Stop();
+ mpTransitionPlayer->Stop();
+ mpCameraPlayer->Stop();
+}
+
+PresentationOverlay::PresentationOverlay() :
+mAlpha( 0.0f ),
+mInvDuration( 0.0f ),
+mStart( BLACK_TRANSPARENT ),
+mEnd( BLACK_TRANSPARENT ),
+mFrameCount( -1 ),
+mIsAutoRemove( false )
+{}
+
+void PresentationOverlay::Update( unsigned int ElapsedTime )
+{
+ if( mFrameCount == 0 )
+ {
+ mAlpha = 0.0f;
+ mInvDuration = 0.0f;
+ mFrameCount = -1;
+ if( mIsAutoRemove )
+ {
+ RenderLayer* rl = GetRenderManager()->mpLayer( RenderEnums::PresentationSlot );
+ rAssert( rl );
+ rl->RemoveGuts( this );
+ rl->Freeze();
+ mIsAutoRemove = false;
+ }
+ }
+ else if( mAlpha > 0.0f )
+ {
+ mAlpha -= ( (float)ElapsedTime * mInvDuration );
+ if( mAlpha < 0.0f )
+ {
+ mAlpha = 0.0f;
+ mInvDuration = 0.0f;
+ if( mIsAutoRemove )
+ {
+ RenderLayer* rl = GetRenderManager()->mpLayer( RenderEnums::PresentationSlot );
+ rAssert( rl );
+ rl->RemoveGuts( this );
+ rl->Freeze();
+ mIsAutoRemove = false;
+ }
+ }
+ }
+}
+
+void PresentationOverlay::Display( void )
+{
+ // First check if anything is visible...
+ int alpha = 0;
+ if( mFrameCount == -1 )
+ {
+ alpha = int( ( mStart.Alpha() * mAlpha ) + ( mEnd.Alpha() * ( 1.0f - mAlpha ) ) );
+ }
+ else
+ {
+ --mFrameCount;
+ if( mFrameCount < 0 )
+ {
+ mFrameCount = 0;
+ }
+ alpha = mEnd.Alpha();
+ }
+ if( alpha <= 0 )
+ {
+ return;
+ }
+ // Now figure out the colour.
+ tColour c;
+ if( mFrameCount == -1 )
+ {
+ int red = int( ( mStart.Red() * mAlpha ) + ( mEnd.Red() * ( 1.0f - mAlpha ) ) );
+ int green = int( ( mStart.Green() * mAlpha ) + ( mEnd.Green() * ( 1.0f - mAlpha ) ) );
+ int blue = int( ( mStart.Blue() * mAlpha ) + ( mEnd.Blue() * ( 1.0f - mAlpha ) ) );
+ c.Set( red, green, blue, alpha );
+ }
+ else
+ {
+ c.c = mEnd.c;
+ }
+
+ // Let's draw poly!
+ p3d::stack->Push();
+ bool oldZWrite = p3d::pddi->GetZWrite();
+ pddiCompareMode oldZComp = p3d::pddi->GetZCompare();
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( false );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( PDDI_COMPARE_ALWAYS );
+ }
+ p3d::stack->LoadIdentity();
+ p3d::pddi->SetProjectionMode( PDDI_PROJECTION_ORTHOGRAPHIC );
+ pddiColour oldAmbient = p3d::pddi->GetAmbientLight();
+ p3d::pddi->SetAmbientLight( pddiColour( 255, 255, 255 ) );
+
+ pddiPrimStream* overlay = 0;
+
+ pddiShader* overlayShader = BootupContext::GetInstance()->GetSharedShader();
+ rAssert( overlayShader );
+
+ overlayShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ overlayShader->SetInt( PDDI_SP_ISLIT, 0 );
+ overlayShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_FLAT );
+ overlayShader->SetInt( PDDI_SP_TWOSIDED, 1 );
+
+ overlay = p3d::pddi->BeginPrims( overlayShader, PDDI_PRIM_TRISTRIP, PDDI_V_C, 4 );
+
+ overlay->Colour( c );
+ overlay->Coord( 0.5f, -0.5f, 1.0f );
+ overlay->Colour( c );
+ overlay->Coord( -0.5f, -0.5f, 1.0f );
+ overlay->Colour( c );
+ overlay->Coord( 0.5f, 0.5f, 1.0f );
+ overlay->Colour( c );
+ overlay->Coord( -0.5f, 0.5f, 1.0f );
+
+ p3d::pddi->EndPrims( overlay );
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_PERSPECTIVE);
+ p3d::pddi->SetAmbientLight( oldAmbient );
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( true );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( oldZComp );
+ }
+ p3d::stack->Pop();
+}
+
+//=============================================================================
+// PresentationManager::CheckRaceMissionBitmaps
+//=============================================================================
+// Description: updates bitmaps on the mission start screen if we're entering
+// a race mission
+//
+// Parameters: none7
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::CheckRaceMissionBitmaps()
+{
+}
+
+//=============================================================================
+// PresentationManager::ReplaceMissionBriefingBitmap
+//=============================================================================
+// Description: depending on who we're talking to, we need to replace the
+// picture on the mission briefing screen
+//
+// Parameters: conversationCharacterName - name of the character that led to
+// this screen
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::ReplaceMissionBriefingBitmap( const tName& conversationCharacterName )
+{
+ const tName& name = conversationCharacterName;
+
+ //
+ // These are for the race missions
+ //
+ if( name == "b_nelson" )
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/misXX_CT.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else if( name == "b_milhouse" )
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/misXX_TT.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else if( name == "b_ralph" )
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/misXX_CP.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else if( name == "b_louie" )
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/misXX_GB.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else if( name == "b_witch" )
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/misXX_HW.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else if( name == "b_zfem1" )
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/misXX_HW.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else if( name == "b_zmale1" )
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/misXX_HW.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else if( name == "b_zmale3" )
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/misXX_HW.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+
+ //
+ // these are for the bonus missions
+ //
+ else if( name == "b_cletus")
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/mis01_08.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else if( name == "b_grandpa")
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/mis02_08.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else if( name == "b_skinner")
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/mis03_08.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else if( name == "b_cbg")
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/mis04_08.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else if( name == "b_frink")
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/mis05_08.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else if( name == "b_snake")
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/mis06_08.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else if( name == "b_smithers")
+ {
+ CGuiScreenMissionLoad::SetBitmapName( "art/frontend/dynaload/images/mis07_08.p3d" );
+ CGuiScreenMissionLoad::ClearBitmap();
+ }
+ else
+ {
+ return;
+ }
+
+ //
+ // Trigger an update of the picture via scrooby
+ //
+ CGuiScreenMissionLoad::ReplaceBitmap();
+}
+
+//=============================================================================
+// PresentationManager::MakeCharactersFaceEachOther
+//=============================================================================
+// Description: depending on who we're talking to, we need to replace the
+// picture on the mission briefing screen
+//
+// Parameters: c1, c2 - the two characters that we want to face each other
+//
+// Return: void
+//
+//=============================================================================
+void PresentationManager::MakeCharactersFaceEachOther( Character* c0, Character* c1 )
+{
+ rmt::Vector position0;
+ rmt::Vector position1;
+
+ c0->GetPosition( &position0 );
+ c1->GetPosition( &position1 );
+ rmt::Vector offset = position1 - position0;
+ offset.Normalize();
+ float rotation = choreo::GetWorldAngle( offset.x, offset.z );
+ c0->RelocateAndReset( position0, rotation, false );
+ c1->RelocateAndReset( position1, rotation + rmt::PI, false );
+}
diff --git a/game/code/presentation/presentation.h b/game/code/presentation/presentation.h
new file mode 100644
index 0000000..659c181
--- /dev/null
+++ b/game/code/presentation/presentation.h
@@ -0,0 +1,217 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 16/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef PRESENTATIONMANAGER_H
+#define PRESENTATIONMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <radmath/radmath.hpp>
+#include <p3d/entity.hpp>
+#include <p3d/drawable.hpp>
+#include <events/eventlistener.h>
+
+#include <memory/allocpool.h>
+#include <memory/stlallocators.h>
+//#include <presentation/language.h>
+#include <presentation/presevents/fmvevent.h>
+#include <presentation/presevents/nisevent.h>
+#include <presentation/presevents/presentationevent.h>
+#include <presentation/presevents/transevent.h>
+
+#include <render/enums/renderenums.h>
+#include <vector>
+
+//========================================
+// Forward References
+//========================================
+class Character;
+class NISPlayer;
+class FMVPlayer;
+class CameraPlayer;
+class TransitionPlayer;
+class PlayerDrawable;
+class SuperCam;
+class PresentationAnimator;
+
+//========================================
+// typedefs
+//========================================
+
+typedef AllocPool< FMVEvent > FMVEventPool;
+typedef AllocPool< NISEvent > NISEventPool;
+typedef AllocPool< TransitionEvent > TransitionEventPool;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+/*=============================================================================
+ This drawable is used to a poly over the camera to fade out the scene, etc.
+This was being done by the frontend, but it relies on a scrooby project being
+loaded and while playing a movie during game play we don't have that.
+ This could also be used for putting a texture over the screen, but that's not
+implemented at this time.
+=============================================================================*/
+const tColour BLACK( 0x00, 0x00, 0x00, 0xFF );
+const tColour WHITE( 0xFF, 0xFF, 0xFF, 0xFF );
+const tColour BLACK_TRANSPARENT( 0x00, 0x00, 0x00, 0x00 );
+const tColour WHITE_TRANSPARENT( 0xFF, 0xFF, 0xFF, 0x00 );
+class PresentationOverlay : public tDrawable
+{
+public:
+ PresentationOverlay();
+
+ virtual void Display();
+ void Update( unsigned int ElapsedTime );
+
+ void SetStart( tColour Colour ) { mStart = Colour; }
+ void SetEnd( tColour Colour ) { mEnd = Colour; }
+ // Note setting the duration begins the fading from one colour to another.
+ void SetDuration( float Duration ) { mInvDuration = 0.001f / Duration; mAlpha = 1.0f; mFrameCount = -1; }
+ void SetFrames( int FrameCount ) { mFrameCount = FrameCount; } // Hold start colour for this many frames.
+ float GetAlpha( void ) const { return mAlpha; }
+ void SetRemoveOnComplete( bool AutoRemove ) { mIsAutoRemove = AutoRemove; }
+
+protected:
+ float mAlpha;
+ float mInvDuration;
+ tColour mStart;
+ tColour mEnd;
+ int mFrameCount;
+ bool mIsAutoRemove : 1;
+};
+
+class PresentationManager : public EventListener,
+ public PresentationEvent::PresentationEventCallBack,
+ public LoadingManager::ProcessRequestsCallback
+{
+ public:
+ // Static Methods for accessing this singleton.
+ static PresentationManager* CreateInstance();
+ static PresentationManager* GetInstance();
+ static void DestroyInstance();
+
+ void Initialize();
+ void Finalize();
+ void OnGameplayStart();
+ void OnGameplayStop();
+
+ void InitializePlayerDrawable();
+ void FinalizePlayerDrawable();
+
+ // Free frontend, play movie, reload frontend.
+ void PlayFMV( const char* FileName, PresentationEvent::PresentationEventCallBack* pCallback = 0,
+ bool IsSkippable = true,
+ bool StopMusic = false,
+ bool IsLocalized = true );
+
+ // Creates an event and passes it back. Adds this event to the queue.
+ void QueueFMV( FMVEvent** pFMVEvent,
+ PresentationEvent::PresentationEventCallBack* pCallback );
+
+ // Creates an event and passes it back. Adds this event to the queue.
+ void QueueNIS( NISEvent** pNISEvent,
+ PresentationEvent::PresentationEventCallBack* pCallback );
+
+ // Creates an event and passes it back. Adds this event to the queue.
+ void QueueTransition( TransitionEvent** pTransitionEvent,
+ PresentationEvent::PresentationEventCallBack* pCallback );
+
+ // Scales the p3d stack by 11.9
+ void ClearQueue();
+ bool IsQueueEmpty() { return( mEventFIFO[ mFIFOBegin ] == NULL ); }
+ bool IsBusy(void) const;
+
+ void Update( unsigned int elapsedTime );
+
+ FMVPlayer* GetFMVPlayer() { return( mpFMVPlayer ); }
+ NISPlayer* GetNISPlayer() { return( mpNISPlayer ); }
+ TransitionPlayer* GetTransPlayer() { return( mpTransitionPlayer ); }
+ CameraPlayer* GetCameraPlayer() {return( mpCameraPlayer ); }
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ PresentationAnimator* GetAnimatorNpc();
+ PresentationAnimator* GetAnimatorPc();
+ typedef std::vector< tName, s2alloc<tName> > TNAMEVECTOR;
+ void SetCamerasForLineOfDialog( const TNAMEVECTOR& names );
+ bool InConversation() const;
+ void StopAll();
+ void CheckRaceMissionBitmaps();
+ void ReplaceMissionBriefingBitmap( const tName& conversationCharacterName );
+ void MakeCharactersFaceEachOther( Character* c0, Character* c1 );
+
+ protected:
+ void AddToQueue( PresentationEvent* pEvent );
+ const tName GetCameraTargetForLineOfDialog( const unsigned int lineOfDialog ) const;
+ void ReturnToPool( PresentationEvent* presevent );
+
+ virtual void OnPresentationEventBegin( PresentationEvent* pEvent );
+ virtual void OnPresentationEventLoadComplete( PresentationEvent* pEvent );
+ virtual void OnPresentationEventEnd( PresentationEvent* pEvent );
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+ // Gets the first event in the queue
+ PresentationEvent* GetFirst();
+ private:
+ PresentationManager();
+ virtual ~PresentationManager();
+
+ //Prevent wasteful constructor creation.
+ PresentationManager( const PresentationManager& presentationManager );
+ PresentationManager& operator=( const PresentationManager& presentationManager );
+
+ // Pointer to the one and only instance of this singleton.
+ static PresentationManager* spInstance;
+
+ TransitionEventPool* mTransitionPool;
+ NISEventPool* mNISPool;
+ FMVEventPool* mFMVPool;
+
+ SuperCam* mp_oldcam;
+ unsigned int mOldCamIndexNum;
+
+
+ // Very simple implementation of a queue. I don't actually
+ // even know if it works.
+ static const unsigned int MAX_EVENT_SIZE = 10;
+ PresentationEvent* mEventFIFO[ MAX_EVENT_SIZE ];
+ unsigned int mFIFOBegin;
+ unsigned int mFIFOEnd;
+
+ PresentationEvent* mpCurrent;
+
+ FMVPlayer* mpFMVPlayer;
+ NISPlayer* mpNISPlayer;
+ CameraPlayer* mpCameraPlayer;
+ TransitionPlayer* mpTransitionPlayer;
+
+ PlayerDrawable* mpPlayerDrawable;
+ PresentationAnimator* mp_PCAnimator;
+ PresentationAnimator* mp_NPCAnimator;
+// Language::Language mLanguage;
+ int mDialogLineNumber;
+ TNAMEVECTOR mCameraForLineOfDialog;
+ PresentationEvent::PresentationEventCallBack* mpPlayCallback;
+ bool mInConversation : 1;
+ bool mWaitingOnFade : 1;
+ PresentationOverlay* mOverlay;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline PresentationManager* GetPresentationManager() { return( PresentationManager::GetInstance() ); }
+
+#endif //PRESENTATIONMANAGER_H
+
diff --git a/game/code/presentation/presentationanimator.cpp b/game/code/presentation/presentationanimator.cpp
new file mode 100644
index 0000000..a719030
--- /dev/null
+++ b/game/code/presentation/presentationanimator.cpp
@@ -0,0 +1,367 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: PresentationAnimator.cpp
+//
+// Description: Implement PresentationAnimator
+//
+// History: 9/24/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <ai/sequencer/actioncontroller.h>
+#include <ai/sequencer/action.h>
+#include <memory/srrmemory.h>
+
+#include <presentation/blinker.h>
+#include <presentation/mouthflapper.h>
+#include <presentation/presentationanimator.h>
+
+#include <worldsim/character/character.h>
+#include <vector>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//=============================================================================
+// Forward Declarations
+//=============================================================================
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// PresentationAnimator::PresentationAnimator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PresentationAnimator::PresentationAnimator():
+ mCharacter( NULL ),
+ mMouthFlapper( NULL ),
+ mRandomSelection( true ),
+ mTalkTime(0)
+{
+ MEMTRACK_PUSH_GROUP( "PresentationAnimator" );
+ mMouthFlapper = new(GMA_LEVEL_OTHER) MouthFlapper();
+ mMouthFlapper->AddRef();
+ MEMTRACK_POP_GROUP( "PresentationAnimator" );
+}
+
+//==============================================================================
+// PresentationAnimator::~PresentationAnimator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PresentationAnimator::~PresentationAnimator()
+{
+ mMouthFlapper->Release();
+}
+//=============================================================================
+// PresentationAnimator::AddAmbientAnimations
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter )
+//
+// Return: void
+//
+//=============================================================================
+void PresentationAnimator::AddAmbientAnimations( const TNAMEVECTOR& animations )
+{
+ if( !animations.empty() )
+ {
+ mAnimationNames.erase( mAnimationNames.begin(), mAnimationNames.end() );
+ mAnimationNames.insert( mAnimationNames.begin(), animations.begin(), animations.end() );
+ }
+}
+
+//=============================================================================
+// PresentationAnimator::ClearAmbientAnimations
+//=============================================================================
+// Description: Comment
+//
+// Parameters:
+//
+// Return: void
+//
+//=============================================================================
+void PresentationAnimator::ClearAmbientAnimations( void )
+{
+ mAnimationNames.erase( mAnimationNames.begin(), mAnimationNames.end() );
+}
+
+
+//=============================================================================
+// PresentationAnimator::ChooseNextAnimation
+//=============================================================================
+// Description: Chooses the next animation from a list of animation names
+//
+// Parameters: NONE
+//
+// Return: tName - the name of the animation chosen
+//
+//=============================================================================
+const tName PresentationAnimator::ChooseNextAnimation()
+{
+ size_t size = mAnimationNames.size();
+ if( size == 0 )
+ {
+ return tName( "NONE" );
+ }
+ else
+ {
+ tName returnMe = mAnimationNames[ 0 ];
+ mAnimationNames.erase( mAnimationNames.begin() );
+ return returnMe;
+ }
+}
+
+//=============================================================================
+// PresentationAnimator::ChooseRandomAnimation
+//=============================================================================
+// Description: Chooses a random animation from the vector of potential
+// animation names that have been set in the script
+//
+// Parameters: NONE
+//
+// Return: tName - the name of the animation chosen
+//
+//=============================================================================
+const tName PresentationAnimator::ChooseRandomAnimation() const
+{
+ int size = mAnimationNames.size();
+ if( size == 0 )
+ {
+ return tName( "NONE" );
+ }
+ else
+ {
+ int randomNumber = rand();
+ int selection = randomNumber % size;
+ return mAnimationNames[ selection ];
+ }
+}
+
+//=============================================================================
+// PresentationAnimator::SetCharacter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter )
+//
+// Return: void
+//
+//=============================================================================
+void PresentationAnimator::SetCharacter( Character* pCharacter )
+{
+ mCharacter = pCharacter;
+ mMouthFlapper->SetCharacter( pCharacter );
+}
+
+//=============================================================================
+// PresentationAnimator::GetCharacter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Character
+//
+//=============================================================================
+Character* PresentationAnimator::GetCharacter()
+{
+ return mCharacter;
+}
+
+//=============================================================================
+// PresentationAnimator::PlaySpecialAmbientAnimation
+//=============================================================================
+// Description: triggers playing of a special ambient animation - ie homer
+// scratching himself
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PresentationAnimator::PlaySpecialAmbientAnimation()
+{
+ tName chosenAnimationName;
+ if( mRandomSelection )
+ {
+ chosenAnimationName = ChooseRandomAnimation();
+ }
+ else
+ {
+ chosenAnimationName = ChooseNextAnimation();
+ }
+
+ if( ( chosenAnimationName == "NONE" ) || ( chosenAnimationName == "none" ) )
+ {
+ return;
+ }
+
+ if( mCharacter == NULL )
+ {
+ return;
+ }
+ bool canPlay = mCharacter->CanPlayAnimation( chosenAnimationName );
+ if( canPlay )
+ {
+ ActionController* actionController = mCharacter->GetActionController();
+ rAssert( actionController != NULL );
+ Sequencer* pSeq = actionController->GetNextSequencer();
+ rAssert( pSeq != NULL );
+ if( !pSeq->IsBusy() )
+ {
+ pSeq->BeginSequence();
+ PlayAnimationAction* pAction = 0;
+ pAction = new PlayAnimationAction( mCharacter, chosenAnimationName );
+ pAction->AbortWhenMovementOccurs( true );
+ pSeq->AddAction( pAction );
+ pSeq->EndSequence();
+ }
+ }
+ else
+ {
+ #ifdef RAD_DEBUG
+ const char* characterName = mCharacter->GetNameDangerous();
+ const char* animationName = chosenAnimationName.GetText();
+ rDebugPrintf
+ (
+ "PresentationAnimator::PlaySpecialAmbientAnimation, character'%s' cannont play'%s'\n",
+ characterName,
+ animationName
+ );
+ #endif
+ }
+}
+
+//=============================================================================
+// PresentationAnimator::SetRandomSelection
+//=============================================================================
+// Description: should the animations be chosen in order or randomly from the
+// set of animations
+//
+// Parameters: random - random or not
+//
+// Return: NONE
+//
+//=============================================================================
+void PresentationAnimator::SetRandomSelection( const bool random )
+{
+ mRandomSelection = random;
+}
+
+const bool PresentationAnimator::GetRandomSelection() const
+{
+ return mRandomSelection;
+}
+
+
+//=============================================================================
+// PresentationAnimator::StartTalking
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PresentationAnimator::StartTalking()
+{
+ if( mCharacter != NULL )
+ {
+ mCharacter->GetPuppet()->GetEngine()->GetPoseEngine()->AddPoseDriver( 2, mMouthFlapper );
+ }
+ mMouthFlapper->SetIsEnabled( true );
+}
+
+//=============================================================================
+// PresentationAnimator::StopTalking
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PresentationAnimator::StopTalking()
+{
+ mMouthFlapper->SetIsEnabled( false );
+ if( mCharacter != NULL )
+ {
+ mCharacter->GetPuppet()->GetEngine()->GetPoseEngine()->RemovePoseDriver( 2, mMouthFlapper );
+ }
+}
+
+//=============================================================================
+// PresentationAnimator::Talkfor
+//=============================================================================
+void PresentationAnimator::TalkFor(int time)
+{
+ mTalkTime = time;
+ StartTalking();
+}
+
+//=============================================================================
+// PresentationAnimator::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void PresentationAnimator::Update( int elapsedTime )
+{
+ if(mTalkTime > 0)
+ {
+ if((mTalkTime - elapsedTime) < 0)
+ {
+ mTalkTime = 0;
+ StopTalking();
+ }
+ else
+ {
+ mTalkTime -= elapsedTime;
+ }
+ }
+
+
+ //mBlinker->Update( elapsedTime );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/presentation/presentationanimator.h b/game/code/presentation/presentationanimator.h
new file mode 100644
index 0000000..b2d455a
--- /dev/null
+++ b/game/code/presentation/presentationanimator.h
@@ -0,0 +1,70 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: presentationanimator.h
+//
+// Description: Blahblahblah
+//
+// History: 9/24/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef PRESENTATIONANIMATOR_H
+#define PRESENTATIONANIMATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <vector>
+
+#include <memory/stlallocators.h>
+
+
+//========================================
+// Forward References
+//========================================
+
+class Character;
+class MouthFlapper;
+
+//=============================================================================
+//
+// Synopsis: PresentationAnimator
+//
+//=============================================================================
+
+class PresentationAnimator
+{
+public:
+ PresentationAnimator();
+ virtual ~PresentationAnimator();
+
+ void SetCharacter( Character* pCharacter );
+ Character* GetCharacter();
+
+ typedef std::vector< tName, s2alloc<tName> > TNAMEVECTOR;
+ void AddAmbientAnimations( const TNAMEVECTOR& animations );
+ void ClearAmbientAnimations( void );
+ void PlaySpecialAmbientAnimation();
+ void SetRandomSelection( const bool random );
+ const bool GetRandomSelection() const;
+ void StartTalking();
+ void StopTalking();
+ void TalkFor(int time);
+ void Update( int elapsedTime );
+
+private:
+ const tName ChooseNextAnimation();
+ const tName ChooseRandomAnimation() const;
+ PresentationAnimator( const PresentationAnimator& presentationanimator );
+ PresentationAnimator& operator=( const PresentationAnimator& presentationanimator );
+
+ Character* mCharacter;
+ MouthFlapper* mMouthFlapper;
+ TNAMEVECTOR mAnimationNames;
+ bool mRandomSelection;
+ int mTalkTime;
+};
+
+
+#endif //PRESENTATIONANIMATOR_H
diff --git a/game/code/presentation/presevents/allpresevents.cpp b/game/code/presentation/presevents/allpresevents.cpp
new file mode 100644
index 0000000..9182536
--- /dev/null
+++ b/game/code/presentation/presevents/allpresevents.cpp
@@ -0,0 +1,4 @@
+#include <presentation/presevents/fmvevent.cpp>
+#include <presentation/presevents/nisevent.cpp>
+#include <presentation/presevents/presentationevent.cpp>
+#include <presentation/presevents/transevent.cpp>
diff --git a/game/code/presentation/presevents/fmvevent.cpp b/game/code/presentation/presevents/fmvevent.cpp
new file mode 100644
index 0000000..70235f0
--- /dev/null
+++ b/game/code/presentation/presevents/fmvevent.cpp
@@ -0,0 +1,74 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: fmvevent.cpp
+//
+// Description: Implement FMVEvent
+//
+// History: 23/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <presentation/fmvplayer/fmvplayer.h>
+#include <presentation/presentation.h>
+#include <presentation/presevents/fmvevent.h>
+#include <memory/srrmemory.h>
+#include <render/rendermanager/rendermanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+FMVEvent::FMVEvent () : PresentationEvent ()
+{
+}
+
+
+FMVEvent::~FMVEvent ()
+{
+}
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// FMVEvent::GetPlayer
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: AnimationPlayer
+//
+//=============================================================================
+AnimationPlayer* FMVEvent::GetPlayer()
+{
+ return( GetPresentationManager()->GetFMVPlayer() );
+}
+
+FMVEvent::FMVEventData::FMVEventData() : AudioIndex( 0 ), Allocator( GMA_LEVEL_MOVIE ), KillMusic( false )
+{
+} \ No newline at end of file
diff --git a/game/code/presentation/presevents/fmvevent.h b/game/code/presentation/presevents/fmvevent.h
new file mode 100644
index 0000000..498d0a1
--- /dev/null
+++ b/game/code/presentation/presevents/fmvevent.h
@@ -0,0 +1,62 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 23/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef FMVEVENT_H
+#define FMVEVENT_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <presentation/presevents/presentationevent.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class FMVEvent : public PresentationEvent
+{
+ public:
+ FMVEvent ();
+ virtual ~FMVEvent ();
+
+ AnimationPlayer* GetPlayer();
+ void SetAudioIndex(unsigned int idx) { mData.AudioIndex = idx; }
+ void SetAllocator( GameMemoryAllocator Alloc ) { mData.Allocator = Alloc; }
+ void KillMusic() { mData.KillMusic = true; }
+
+ struct FMVEventData
+ {
+ FMVEventData();
+ unsigned int AudioIndex;
+ GameMemoryAllocator Allocator;
+ bool KillMusic;
+ };
+
+ static const unsigned int AUDIO_INDEX_ENGLISH = 0;
+ static const unsigned int AUDIO_INDEX_FRENCH = 1;
+ static const unsigned int AUDIO_INDEX_GERMAN = 2;
+ static const unsigned int AUDIO_INDEX_SPANISH = 3;
+
+ protected:
+ void* GetUserData () { return reinterpret_cast<void*>(&mData); }
+
+ FMVEventData mData;
+};
+
+#endif //FMVEVENT_H
+
diff --git a/game/code/presentation/presevents/nisevent.cpp b/game/code/presentation/presevents/nisevent.cpp
new file mode 100644
index 0000000..f549d7e
--- /dev/null
+++ b/game/code/presentation/presevents/nisevent.cpp
@@ -0,0 +1,229 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: NISevent.cpp
+//
+// Description: Implement NISEvent
+//
+// History: 23/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <presentation/animplayer.h>
+#include <presentation/cameraplayer.h>
+#include <presentation/nisplayer.h>
+#include <presentation/presentation.h>
+#include <presentation/presevents/nisevent.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// NISEvent::NISEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: NISEvent
+//
+//=============================================================================
+NISEvent::NISEvent()
+{
+ type = NIS_SCENEGRAPH;
+}
+
+//=============================================================================
+// NISEvent::~NISEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: NISEvent
+//
+//=============================================================================
+NISEvent::~NISEvent()
+{
+}
+
+//=============================================================================
+// NISEvent::LoadNow
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NISEvent::LoadNow()
+{
+ AnimationPlayer* player = GetPlayer();
+
+ SetNames();
+
+ player->LoadData( fileName, false, GetUserData() );
+
+ player->SetPlayAfterLoad( false );
+
+ SetLoaded( true );
+}
+
+//=============================================================================
+// NISEvent::LoadFromInventory
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NISEvent::LoadFromInventory()
+{
+ AnimationPlayer* player = GetPlayer();
+
+ SetNames();
+
+ player->LoadData( fileName, true, GetUserData() );
+
+ SetClearWhenDone( false );
+ SetLoaded( true );
+}
+
+//=============================================================================
+// NISEvent::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NISEvent::Init()
+{
+ PresentationEvent::Init();
+
+ mbHasSetNames = false;
+}
+
+//=============================================================================
+// NISEvent::Start
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NISEvent::Start()
+{
+ SetNames();
+
+ PresentationEvent::Start();
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// NISEvent::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+AnimationPlayer* NISEvent::GetPlayer()
+{
+ AnimationPlayer* player = NULL;
+
+ switch( type )
+ {
+ case NIS_CAMERA:
+ {
+ player = GetPresentationManager()->GetCameraPlayer();
+ break;
+ }
+ case NIS_SCENEGRAPH:
+ {
+ player = GetPresentationManager()->GetNISPlayer();
+ break;
+ }
+ default:
+ {
+ // trouble!
+ rAssert( false );
+ break;
+ }
+ }
+
+ return( player );
+}
+
+//=============================================================================
+// NISEvent::SetNames
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NISEvent::SetNames()
+{
+ if( !mbHasSetNames )
+ {
+ mbHasSetNames = true;
+
+ switch( type )
+ {
+ case NIS_CAMERA:
+ {
+ CameraPlayer* player = GetPresentationManager()->GetCameraPlayer();
+ player->SetNameData( controller, camera, animation );
+ break;
+ }
+ case NIS_SCENEGRAPH:
+ {
+ NISPlayer* player = GetPresentationManager()->GetNISPlayer();
+ player->SetNameData( controller, camera, animation );
+ break;
+ }
+ default:
+ {
+ // trouble!
+ rAssert( false );
+ break;
+ }
+ }
+ }
+}
diff --git a/game/code/presentation/presevents/nisevent.h b/game/code/presentation/presevents/nisevent.h
new file mode 100644
index 0000000..6b1e684
--- /dev/null
+++ b/game/code/presentation/presevents/nisevent.h
@@ -0,0 +1,66 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 23/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef NISEVENT_H
+#define NISEVENT_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <presentation/presevents/presentationevent.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class NISEvent : public PresentationEvent
+{
+ public:
+ NISEvent();
+ ~NISEvent();
+
+ enum NISType
+ {
+ NIS_CAMERA,
+ NIS_SCENEGRAPH,
+ NUM_NIS_TYPES
+ };
+
+ NISType type;
+
+ char controller[32];
+ char camera[32];
+ char animation[32];
+
+ void LoadNow();
+ void LoadFromInventory();
+
+ AnimationPlayer* GetPlayer();
+
+ virtual void Init();
+
+ virtual void Start();
+ protected:
+ private:
+ void SetNames();
+
+ bool mbHasSetNames;
+};
+
+#endif //NISEVENT_H
+
diff --git a/game/code/presentation/presevents/presentationevent.cpp b/game/code/presentation/presevents/presentationevent.cpp
new file mode 100644
index 0000000..dd26a66
--- /dev/null
+++ b/game/code/presentation/presevents/presentationevent.cpp
@@ -0,0 +1,180 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: presentationevent.cpp
+//
+// Description: Implement PresentationEvent
+//
+// History: 22/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <presentation/animplayer.h>
+#include <presentation/presevents/presentationevent.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// PresentationEvent::PresentationEvent
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PresentationEvent::PresentationEvent()
+{
+ Init();
+}
+
+//==============================================================================
+// PresentationEvent::~PresentationEvent
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PresentationEvent::~PresentationEvent()
+{
+
+}
+
+
+//==============================================================================
+// PresentationEvent::OnLoadDataComplete
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void PresentationEvent::OnLoadDataComplete()
+{
+ if( pCallback != NULL )
+ {
+ pCallback->OnPresentationEventLoadComplete( this );
+ }
+}
+
+//=============================================================================
+// PresentationEvent::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: bool
+//
+//=============================================================================
+bool PresentationEvent::Update( unsigned int elapsedTime )
+{
+ AnimationPlayer* player = GetPlayer();
+
+ player->Update( elapsedTime );
+
+ return( player->IsPlaying() );
+}
+
+//=============================================================================
+// PresentationEvent::Start
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PresentationEvent::Start()
+{
+ AnimationPlayer* player = GetPlayer();
+
+ if( !mbLoaded )
+ {
+ player->LoadData( fileName, this, false, GetUserData() );
+
+ mbLoaded = true;
+ }
+ player->SetKeepLayersFrozen( mbKeepLayersFrozen );
+ player->SetSkippable(mbIsSkippable);
+ player->Play();
+}
+
+//=============================================================================
+// PresentationEvent::Stop
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PresentationEvent::Stop()
+{
+ AnimationPlayer* player = GetPlayer();
+
+ if( mbClearWhenDone )
+ {
+ player->ClearData();
+ }
+ else
+ {
+ player->Reset();
+ }
+}
+
+//=============================================================================
+// PresentationEvent::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PresentationEvent::Init()
+{
+ mbAutoPlay = true;
+ mbClearWhenDone = true;
+ mbLoaded = false;
+ mbKeepLayersFrozen = false;
+ mbIsSkippable = true;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
diff --git a/game/code/presentation/presevents/presentationevent.h b/game/code/presentation/presevents/presentationevent.h
new file mode 100644
index 0000000..ae007c0
--- /dev/null
+++ b/game/code/presentation/presevents/presentationevent.h
@@ -0,0 +1,107 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 22/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef PRESENTATIONEVENT_H
+#define PRESENTATIONEVENT_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <events/eventenum.h>
+
+#include <presentation/animplayer.h>
+
+#include <render/enums/renderenums.h>
+
+//========================================
+// Forward References
+//========================================
+
+class AnimationPlayer;
+
+//=============================================================================
+//
+// Synopsis: These classes make it easier for the PresentationManager to
+// queue lots of animations for use on the different players.
+// Keep in mind that the PresentationManager is keeping a bunch
+// of these in a pool so you MUST call Init to reset all the
+// members to their default values.
+//
+//=============================================================================
+
+class PresentationEvent : public AnimationPlayer::LoadDataCallBack
+{
+ public:
+ PresentationEvent();
+ virtual ~PresentationEvent();
+
+ // Implement AnimationPlayer::LoadDataCallBack
+ //
+ virtual void OnLoadDataComplete();
+
+ struct PresentationEventCallBack
+ {
+ virtual void OnPresentationEventBegin( PresentationEvent* pEvent ) = 0;
+ virtual void OnPresentationEventLoadComplete( PresentationEvent* pEvent ) = 0;
+ virtual void OnPresentationEventEnd( PresentationEvent* pEvent ) = 0;
+ };
+
+ PresentationEventCallBack* pCallback;
+ char fileName[64];
+ bool bInInventory;
+
+ // set ClearWhenDone to true to clear all data when this event finishes
+ // or false to leave stuff (like the MoviePlayer) allocated. Default = true
+ void SetClearWhenDone( bool bClear ) { mbClearWhenDone = bClear; }
+
+ // set AutoPlay to true to play animation immediately after loading finsihes
+ // Default = true
+ void SetAutoPlay( bool bAutoPlay ) { mbAutoPlay = bAutoPlay; }
+ bool GetAutoPlay() { return mbAutoPlay; }
+ void SetKeepLayersFrozen( bool IsKeep ) { mbKeepLayersFrozen = IsKeep; }
+ bool GetKeepLayersFrozen( void ) const { return mbKeepLayersFrozen; }
+ void SetSkippable(bool IsSkippable) {mbIsSkippable = IsSkippable;}
+ bool IsSkippable(void) const {return mbIsSkippable;}
+
+ void SetRenderLayer( RenderEnums::LayerEnum layer ) { mRenderLayer = layer; }
+ RenderEnums::LayerEnum GetRenderLayer() { return mRenderLayer; }
+
+ virtual AnimationPlayer* GetPlayer() = 0;
+
+ bool Update( unsigned int elapsedTime );
+
+ virtual void Start();
+ virtual void Stop();
+
+ virtual void Init();
+
+ protected:
+ void SetLoaded( bool bLoaded ) { mbLoaded = bLoaded; }
+
+ virtual void* GetUserData () { return 0; }
+
+ private:
+
+ //Prevent wasteful constructor creation.
+ PresentationEvent( const PresentationEvent& presentationEvent );
+ PresentationEvent& operator=( const PresentationEvent& presentationEvent );
+
+ bool mbAutoPlay : 1;
+ bool mbClearWhenDone : 1;
+ bool mbLoaded : 1;
+ bool mbKeepLayersFrozen : 1;
+ bool mbIsSkippable : 1;
+ RenderEnums::LayerEnum mRenderLayer;
+};
+
+#endif // PRESENTATIONEVENT_H
+
diff --git a/game/code/presentation/presevents/transevent.cpp b/game/code/presentation/presevents/transevent.cpp
new file mode 100644
index 0000000..daea4a1
--- /dev/null
+++ b/game/code/presentation/presevents/transevent.cpp
@@ -0,0 +1,77 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: Transevent.cpp
+//
+// Description: Implement TransEvent
+//
+// History: 23/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <presentation/presentation.h>
+#include <presentation/transitionplayer.h>
+#include <presentation/presevents/transevent.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// TransitionEvent::GetPlayer
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: AnimationPlayer
+//
+//=============================================================================
+AnimationPlayer* TransitionEvent::GetPlayer()
+{
+ return( GetPresentationManager()->GetTransPlayer() );
+}
+
+//=============================================================================
+// TransitionEvent::Start
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TransitionEvent::Start()
+{
+ TransitionPlayer* player = GetPresentationManager()->GetTransPlayer();
+
+ player->SetTransition( &transInfo );
+
+ player->Play();
+}
+
diff --git a/game/code/presentation/presevents/transevent.h b/game/code/presentation/presevents/transevent.h
new file mode 100644
index 0000000..6c921b1
--- /dev/null
+++ b/game/code/presentation/presevents/transevent.h
@@ -0,0 +1,44 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 23/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef TRANSEVENT_H
+#define TRANSEVENT_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <presentation/presevents/presentationevent.h>
+#include <presentation/transitionplayer.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class TransitionEvent : public PresentationEvent
+{
+ public:
+ TransitionPlayer::TransitionInfo transInfo;
+
+ AnimationPlayer* GetPlayer();
+
+ virtual void Start();
+ protected:
+};
+
+#endif //TRANSEVENT_H
+
diff --git a/game/code/presentation/simpleanimationplayer.cpp b/game/code/presentation/simpleanimationplayer.cpp
new file mode 100644
index 0000000..dc250c8
--- /dev/null
+++ b/game/code/presentation/simpleanimationplayer.cpp
@@ -0,0 +1,300 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: simpleanimationplayer.cpp
+//
+// Description: Implement SimpleAnimationPlayer
+//
+// History: 29/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/view.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <presentation/simpleanimationplayer.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SimpleAnimationPlayer::SimpleAnimationPlayer
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SimpleAnimationPlayer::SimpleAnimationPlayer() :
+ mpMasterController( NULL ),
+ mCycleMode( DEFAULT_CYCLE_MODE ),
+ mpCamera( NULL ),
+ mpViewCamera( NULL ),
+ mbSetCamera( false ),
+ mIntroFrames(0),
+ mOutroFrames(0),
+ mInIntro(false)
+{
+ strcpy( msCamera, "" );
+ strcpy( msController, "" );
+ strcpy( msAnimation, "" );
+}
+
+//==============================================================================
+// SimpleAnimationPlayer::~SimpleAnimationPlayer
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SimpleAnimationPlayer::~SimpleAnimationPlayer()
+{
+ tRefCounted::Release( mpViewCamera );
+}
+
+//=============================================================================
+// SimpleAnimationPlayer::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int elapsedTime )
+//
+// Return: void
+//
+//=============================================================================
+void SimpleAnimationPlayer::Update( unsigned int elapsedTime )
+{
+ if ( (GetState() == ANIM_PLAYING) && mpMasterController)
+ {
+ if(mOutroFrames && (mpMasterController->GetFrame() > static_cast<float>(mNumFrames - mOutroFrames)))
+ {
+ mpMasterController->SetFrameRange(static_cast<float>(mNumFrames - mOutroFrames), static_cast<float>(mNumFrames));
+ mpMasterController->SetCycleMode(FORCE_CYCLIC);
+ }
+
+ mpMasterController->Advance( static_cast<float>(elapsedTime) );
+ }
+}
+
+
+//==============================================================================
+// SimpleAnimationPlayer::Rewind
+//==============================================================================
+//
+// Description:
+//
+// Parameters:
+//
+// Return:
+//
+//==============================================================================
+void SimpleAnimationPlayer::Rewind()
+{
+ if(mpMasterController)
+ {
+ mpMasterController->Reset();
+ mpMasterController->Advance(0.0f);
+
+ SetState( ANIM_LOADED );
+ }
+}
+
+//=============================================================================
+// SimpleAnimationPlayer::ClearData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SimpleAnimationPlayer::ClearData()
+{
+ AnimationPlayer::ClearData();
+
+ if( mpMasterController != NULL )
+ {
+ mpMasterController->Release();
+ mpMasterController = NULL;
+ }
+
+ mCycleMode = DEFAULT_CYCLE_MODE;
+
+ if( mpCamera != NULL )
+ {
+ mpCamera->Release();
+ mpCamera = NULL;
+ mbSetCamera = false;
+ }
+}
+
+//=============================================================================
+// SimpleAnimationPlayer::SetNameData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char* controller, char* camera, char* animation )
+//
+// Return: void
+//
+//=============================================================================
+void SimpleAnimationPlayer::SetNameData( char* controller, char* camera, char* animation )
+{
+ strcpy( msController, controller );
+ if( camera != NULL )
+ {
+ strcpy( msCamera, camera );
+ }
+ strcpy( msAnimation, animation );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// SimpleAnimationPlayer::DoLoaded
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SimpleAnimationPlayer::DoLoaded()
+{
+ tRefCounted::Assign(mpMasterController, p3d::find<tMultiController>( msController ));
+
+ if(mpMasterController)
+ {
+ // reset to the first frame, and update (so evrything is in the correct
+ // place if we display before calling update again)
+ mpMasterController->Reset();
+ mpMasterController->SetFrame(0);
+ mpMasterController->Advance(0.0f);
+
+ mpMasterController->SetCycleMode( mCycleMode );
+
+ mNumFrames = rmt::FtoL(mpMasterController->GetNumFrames());
+
+ if(mIntroFrames != 0)
+ {
+ mpMasterController->SetFrameRange(0.0f, (float)mIntroFrames);
+ mpMasterController->SetCycleMode( FORCE_CYCLIC );
+ }
+ }
+
+ if( strlen( msCamera ) > 0 )
+ {
+ tRefCounted::Assign(mpCamera,p3d::find<tCamera>( msCamera ));
+ }
+
+ mbSetCamera = false;
+}
+
+//=============================================================================
+// SimpleAnimationPlayer::DoRender
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SimpleAnimationPlayer::DoRender()
+{
+ if(!mpMasterController)
+ {
+ Stop();
+ return;
+ }
+
+ if ( !mbSetCamera && mpCamera != NULL )
+ {
+ tView* view = p3d::context->GetView();
+ rAssert( view );
+
+ mpViewCamera = view->GetCamera();
+ mpViewCamera->AddRef();
+
+ view->SetCamera( mpCamera );
+
+ mbSetCamera = true;
+ }
+
+ if ( (mpMasterController->GetFrame() >= mpMasterController->GetNumFrames()) && (mOutroFrames == 0))
+ {
+ if ( mbSetCamera )
+ {
+ tView* view = p3d::context->GetView();
+ rAssert( view );
+
+ view->SetCamera( mpViewCamera );
+ tRefCounted::Release( mpViewCamera );
+ }
+
+ Stop();
+ }
+}
+
+
+void SimpleAnimationPlayer::SetIntroLoop(unsigned nFrames)
+{
+ mIntroFrames = nFrames;
+}
+
+void SimpleAnimationPlayer::SetOutroLoop(unsigned nFrames)
+{
+ mOutroFrames = nFrames;
+}
+
+void SimpleAnimationPlayer::Play(void)
+{
+ AnimationPlayer::Play();
+
+ if(mIntroFrames)
+ {
+ mInIntro = true;
+ }
+}
+
+void SimpleAnimationPlayer::DoneIntro(void)
+{
+ if(mpMasterController)
+ {
+ mInIntro = false;
+ mpMasterController->SetFrameRange(0.0f, static_cast<float>(mNumFrames));
+ mpMasterController->SetCycleMode(mCycleMode);
+ }
+}
diff --git a/game/code/presentation/simpleanimationplayer.h b/game/code/presentation/simpleanimationplayer.h
new file mode 100644
index 0000000..eb39bd8
--- /dev/null
+++ b/game/code/presentation/simpleanimationplayer.h
@@ -0,0 +1,89 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 29/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef SIMPLEANIMATIONPLAYER_H
+#define SIMPLEANIMATIONPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/anim/animate.hpp> // p3dCycleMode
+#include <presentation/animplayer.h>
+
+//========================================
+// Forward References
+//========================================
+
+class tMultiController;
+class tCamera;
+
+//=============================================================================
+//
+// Synopsis: Wraps up the P3D related crap involved with playing an
+// animation.
+//
+//=============================================================================
+
+class SimpleAnimationPlayer : public AnimationPlayer
+{
+ public:
+ SimpleAnimationPlayer();
+ virtual ~SimpleAnimationPlayer();
+
+ virtual void Update( unsigned int elapsedTime );
+
+ virtual void ClearData();
+
+ void SetNameData( char* controller, char* camera, char* animation );
+
+ void SetCycleMode( p3dCycleMode cycleMode ) { mCycleMode = cycleMode; }
+ void SetIntroLoop(unsigned nFrames);
+ void SetOutroLoop(unsigned nFrames);
+
+ void Play(void);
+ void DoneIntro(void);
+
+ void Rewind();
+
+ protected:
+
+ // These set all the objects needs to play an animation
+ void SetController(tMultiController* pController ) { mpMasterController = pController; }
+ void SetCamera( tCamera* pCamera ) { mpCamera = pCamera; }
+
+ virtual void DoLoaded();
+ virtual void DoRender();
+
+ const char* GetAnimationName() { return( &msAnimation[0] ); }
+
+ private:
+
+ //Prevent wasteful constructor creation.
+ SimpleAnimationPlayer( const SimpleAnimationPlayer& );
+ SimpleAnimationPlayer& operator=( const SimpleAnimationPlayer& );
+
+ char msController[32];
+ char msCamera[32];
+ char msAnimation[32];
+
+ tMultiController* mpMasterController;
+ p3dCycleMode mCycleMode;
+ tCamera* mpCamera;
+ tCamera* mpViewCamera;
+ bool mbSetCamera;
+ unsigned mIntroFrames;
+ unsigned mOutroFrames;
+ bool mInIntro;
+ unsigned mNumFrames;
+};
+
+
+#endif // SIMPLEANIMATIONPLAYER_H
diff --git a/game/code/presentation/transitionplayer.cpp b/game/code/presentation/transitionplayer.cpp
new file mode 100644
index 0000000..5d7f3ce
--- /dev/null
+++ b/game/code/presentation/transitionplayer.cpp
@@ -0,0 +1,161 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: transitionplayer.cpp
+//
+// Description: Implement TransitionPlayer
+//
+// History: 02/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/view.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <presentation/transitionplayer.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/renderlayer.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// TransitionPlayer::TransitionPlayer
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TransitionPlayer::TransitionPlayer() :
+ mpLayer1( NULL ),
+ mpLayer2( NULL ),
+ miIndex( static_cast< unsigned int >( -1 ) )
+{
+ //init msInfo?
+}
+
+//==============================================================================
+// TransitionPlayer::~TransitionPlayer
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TransitionPlayer::~TransitionPlayer()
+{
+}
+
+void TransitionPlayer::SetTransition( TransitionInfo* info )
+{
+ rAssert( GetState() != ANIM_IDLE );
+
+ msInfo = *info;
+
+ RenderManager* rm = GetRenderManager();
+
+ mpLayer1 = rm->mpLayer( msInfo.layer1 );
+ mpLayer2 = rm->mpLayer( msInfo.layer2 );
+
+ miIndex = 0;
+
+ rAssert( msInfo.length != 0 );
+}
+
+void TransitionPlayer::Update( unsigned int elapsedTime )
+{
+ if ( GetState() == ANIM_PLAYING )
+ {
+ if (miIndex == 0)
+ {
+ if ( mpLayer2->IsFrozen() )
+ {
+ mpLayer2->Thaw();
+ }
+ else if ( mpLayer2->IsDead() )
+ {
+ mpLayer2->Resurrect();
+ }
+ }
+
+ miIndex += elapsedTime;
+
+ if ( miIndex < msInfo.length )
+ {
+ DoUpdate( elapsedTime );
+ }
+ else
+ {
+ mpLayer1->Freeze();
+
+ Stop();
+ }
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+void TransitionPlayer::DoUpdate( unsigned int elapsedTime )
+{
+ switch ( msInfo.type )
+ {
+ case TRANS_WIPE_RIGHT:
+ {
+ float f1 = static_cast<float>( miIndex );
+ float f2 = static_cast<float>( msInfo.length );
+
+ float r = f1 / f2;
+
+ tView* view = mpLayer2->pView( 0 );
+ view->SetWindow( 0.0f, 0.0f, r, 1.0f );
+
+ view = mpLayer1->pView( 0 );
+ view->SetWindow( r, 0.0f, 1.0f, 1.0f );
+ break;
+ }
+ default :
+ {
+ //nothing
+ }
+ }
+}
+
+//=============================================================================
+// TransitionPlayer::DoRender
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TransitionPlayer::DoRender()
+{
+}
diff --git a/game/code/presentation/transitionplayer.h b/game/code/presentation/transitionplayer.h
new file mode 100644
index 0000000..3e48fb3
--- /dev/null
+++ b/game/code/presentation/transitionplayer.h
@@ -0,0 +1,82 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 02/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef TRANSITIONPLAYER_H
+#define TRANSITIONPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <presentation/animplayer.h>
+
+#include <render/enums/renderenums.h>
+
+//========================================
+// Forward References
+//========================================
+
+class RenderLayer;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class TransitionPlayer : public AnimationPlayer
+{
+ public:
+ TransitionPlayer();
+ virtual ~TransitionPlayer();
+
+ enum TransitionType
+ {
+ TRANS_CROSSFADE,
+ TRANS_WIPE_RIGHT,
+ TRANS_TO_BLACK,
+ TRANS_FROM_BLACK,
+ NUM_TRANS
+ };
+
+ struct TransitionInfo
+ {
+ RenderEnums::LayerEnum layer1;
+ RenderEnums::LayerEnum layer2;
+ unsigned int length;
+ TransitionType type;
+ };
+
+ void SetTransition( TransitionInfo* info );
+
+ virtual void Update( unsigned int elapsedTime );
+ protected:
+ virtual void DoUpdate( unsigned int elapsedTime );
+
+ virtual void DoLoaded() {};
+ virtual void DoRender();
+
+ private:
+
+ //Prevent wasteful constructor creation.
+ TransitionPlayer( const TransitionPlayer& transitionPlayer );
+ TransitionPlayer& operator=( const TransitionPlayer& transitionPlayer );
+
+ RenderLayer* mpLayer1;
+ RenderLayer* mpLayer2;
+
+ TransitionInfo msInfo;
+ unsigned int miIndex;
+};
+
+
+#endif //TRANSITIONPLAYER_H
+
diff --git a/game/code/presentation/tutorialmanager.cpp b/game/code/presentation/tutorialmanager.cpp
new file mode 100644
index 0000000..f78e952
--- /dev/null
+++ b/game/code/presentation/tutorialmanager.cpp
@@ -0,0 +1,745 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: presentation.cpp
+//
+// Description: Implement PresentationManager
+//
+// History: 16/04/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <data/gamedatamanager.h>
+#include <events/eventmanager.h>
+#include <gameflow/gameflow.h>
+#include <main/commandlineoptions.h>
+#include <memory/srrmemory.h>
+#include <meta/locatorevents.h>
+#include <mission/mission.h>
+#include <mission/missionmanager.h>
+#include <mission/gameplaymanager.h>
+#include <mission/objectives/missionobjective.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/tutorialmanager.h>
+#include <presentation/tutorialmode.h>
+#include <worldsim/redbrick/vehicle.h>
+
+#include <string.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+TutorialManager* TutorialManager::spInstance = NULL;
+//#define NUMBER_OF_MISSION_OBJECTIVE_MESSAGES 300
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// TutorialManager::CreateInstance
+//=============================================================================
+// Description: creats an instance of the singleton
+//
+// Parameters: ()
+//
+// Return: TutorialManager
+//
+//=============================================================================
+TutorialManager* TutorialManager::CreateInstance()
+{
+ MEMTRACK_PUSH_GROUP( "TutorialManager" );
+ if( spInstance == NULL )
+ {
+ spInstance = new TutorialManager;
+ rAssert( spInstance );
+ }
+ size_t size = sizeof( TutorialManager );
+ MEMTRACK_POP_GROUP( "TutorialManager" );
+ return spInstance;
+}
+
+//=============================================================================
+// TutorialManager::GetInstance
+//=============================================================================
+// Description: gets the instance of the singleton
+//
+// Parameters: ()
+//
+// Return: TutorialManager
+//
+//=============================================================================
+TutorialManager* TutorialManager::GetInstance()
+{
+ return spInstance;
+}
+
+//=============================================================================
+// TutorialManager::AddToQueue
+//=============================================================================
+// Description: adds an item to the queue of events to process
+//
+// Parameters: event - the tutorial event that we want to show as soon as
+// possible
+//
+// Return: void
+//
+//=============================================================================
+void TutorialManager::AddToQueue( TutorialMode event )
+{
+ m_Queue.push_back( event );
+}
+
+//=============================================================================
+// TutorialManager::DestroyInstance
+//=============================================================================
+// Description: destroys the only instance of the singleton
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TutorialManager::DestroyInstance()
+{
+ if( spInstance != NULL )
+ {
+ delete spInstance;
+ spInstance = NULL;
+ }
+}
+
+//==============================================================================
+// TutorialManager::TutorialManager
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TutorialManager::TutorialManager() :
+ m_EnableTutorialMode( false ),
+ m_EnableTutorialEvents( true ),
+ m_DialogCurrentlyPlaying( false ),
+ m_TimeSinceDialogStart( 0.0f ),
+ m_tutorialsSeen( 0 )
+{
+#ifndef FINAL
+ // We're going to be passing enums in void* messages - they'd better be the
+ // same size or else we're in big trouble
+ size_t sizeOfEnum = sizeof( TutorialMode );
+ size_t sizeOfVoid = sizeof( void* );
+ rReleaseAssert( sizeOfEnum == sizeOfVoid );
+#endif
+ m_Queue.reserve( 16 );
+
+ GetGameDataManager()->RegisterGameData( this, 1 + sizeof( m_tutorialsSeen ), "Tutorial Manager" );
+
+ if( CommandLineOptions::Get( CLO_NO_TUTORIAL ) )
+ {
+ m_EnableTutorialEvents = false;
+ }
+}
+
+//==============================================================================
+// TutorialManager::~TutorialManager
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+TutorialManager::~TutorialManager()
+{
+ GetEventManager()->RemoveAll( this );
+}
+
+//==============================================================================
+// TutorialManager::HandleEvent
+//==============================================================================
+// Description: Handles events that are passed to this listener
+//
+// Parameters: id - the event id
+// pEventData - any data that tags along for the ride
+//
+// Return: N/A.
+//
+//==============================================================================
+void TutorialManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ if( id == EVENT_TUTORIAL_DIALOG_DONE )
+ {
+ MarkDialogFinished();
+
+ return;
+ }
+
+ if( !m_EnableTutorialMode || !m_EnableTutorialEvents )
+ {
+ return;
+ }
+
+ switch( id )
+ {
+ case EVENT_CARD_COLLECTED:
+ {
+ AddToQueue( TUTORIAL_COLLECTOR_CARD );
+ GetEventManager()->RemoveListener( this, EVENT_CARD_COLLECTED );
+ break;
+ }
+ case EVENT_COLLECTED_COINS:
+ {
+ AddToQueue( TUTORIAL_COLLECTOR_COIN );
+ GetEventManager()->RemoveListener( this, EVENT_COLLECTED_COINS );
+ break;
+ }
+ case static_cast< EventEnum >( EVENT_LOCATOR + LocatorEvent::INTERIOR_ENTRANCE ):
+ {
+ AddToQueue( TUTORIAL_INTERIOR_ENTERED );
+ GetEventManager()->RemoveListener( this, static_cast< EventEnum >( EVENT_LOCATOR + LocatorEvent::INTERIOR_ENTRANCE ) );
+ break;
+ }
+ case static_cast< EventEnum >( EVENT_LOCATOR + LocatorEvent::CAR_DOOR ):
+ {
+ AddToQueue( TUTORIAL_AT_CAR_DOOR );
+ GetEventManager()->RemoveListener( this, static_cast< EventEnum >( EVENT_LOCATOR + LocatorEvent::CAR_DOOR ) );
+ break;
+ }
+ case static_cast< EventEnum >( EVENT_INTERACTIVE_GAG ):
+ {
+ AddToQueue( TUTORIAL_INTERACTIVE_GAG );
+ GetEventManager()->RemoveListener( this, static_cast< EventEnum >( EVENT_INTERACTIVE_GAG ) );
+ break;
+ }
+ case EVENT_ENTERING_PLAYER_CAR:
+ {
+ AddToQueue( TUTORIAL_GETTING_INTO_PLAYER_CAR );
+ GetEventManager()->RemoveListener( this, EVENT_ENTERING_PLAYER_CAR );
+ break;
+ }
+ case EVENT_ENTERING_TRAFFIC_CAR:
+ {
+ //
+ // Have we played the player car tutorial message yet?
+ //
+ bool seenDrivingTutorial = QueryTutorialSeen( TUTORIAL_GETTING_INTO_PLAYER_CAR );
+ if( seenDrivingTutorial )
+ {
+ AddToQueue( TUTORIAL_GETTING_INTO_TRAFFIC_CAR );
+ GetEventManager()->RemoveListener( this, EVENT_ENTERING_TRAFFIC_CAR );
+ }
+ else
+ {
+ AddToQueue( TUTORIAL_GETTING_INTO_PLAYER_CAR );
+ SetTutorialSeen( TUTORIAL_GETTING_INTO_TRAFFIC_CAR, true );
+ GetEventManager()->RemoveListener( this, EVENT_ENTERING_TRAFFIC_CAR );
+ GetEventManager()->RemoveListener( this, EVENT_ENTERING_PLAYER_CAR );
+ }
+ break;
+ }
+ case EVENT_ANIMATED_CAM_SHUTDOWN:
+ {
+ AddToQueue( TUTORIAL_START_GAME );
+ GetEventManager()->RemoveListener( this, EVENT_ANIMATED_CAM_SHUTDOWN );
+ break;
+ }
+ case EVENT_BONUS_MISSION_CHARACTER_APPROACHED:
+ {
+ int missionNum = reinterpret_cast< int >( pEventData );
+ Mission* mission = GetMissionManager()->GetMission( missionNum );
+ bool raceMission = mission->IsRaceMission();
+ bool wagerMission = mission->IsWagerMission();
+
+ if( wagerMission )
+ {
+ AddToQueue( TUTORIAL_WAGER_MISSION );
+ }
+ else if( raceMission )
+ {
+ AddToQueue( TUTORIAL_RACE );
+ }
+ else
+ {
+ AddToQueue( TUTORIAL_BONUS_MISSION );
+ }
+
+ bool seenRace = QueryTutorialSeen( TUTORIAL_RACE );
+ bool seenBonus = QueryTutorialSeen( TUTORIAL_BONUS_MISSION );
+ bool seenWager = QueryTutorialSeen( TUTORIAL_WAGER_MISSION );
+ if( seenRace && seenBonus && seenWager )
+ {
+ GetEventManager()->RemoveListener( this, EVENT_MISSION_START );
+ }
+ break;
+ }
+ case EVENT_HIT_BREAKABLE:
+ {
+ AddToQueue( TUTORIAL_BREAKABLE_DESTROYED );
+ GetEventManager()->RemoveListener( this, EVENT_HIT_BREAKABLE );
+ break;
+ }
+ case EVENT_UNLOCKED_CAR:
+ {
+ AddToQueue( TUTORIAL_UNLOCKED_CAR );
+ GetEventManager()->RemoveListener( this, EVENT_UNLOCKED_CAR );
+ break;
+ }
+ case EVENT_VEHICLE_DESTROYED:
+ {
+ //
+ // Check if this is a user vehicle
+ //
+ Vehicle* vehicle = reinterpret_cast< Vehicle* >( pEventData );
+ rAssert( vehicle != NULL );
+ if( vehicle->mVehicleType == VT_USER )
+ {
+ AddToQueue( TUTORIAL_VEHICLE_DESTROYED );
+ GetEventManager()->RemoveListener( this, EVENT_VEHICLE_DESTROYED );
+ }
+ break;
+ }
+ case EVENT_COLLECTED_WRENCH:
+ {
+ AddToQueue( TUTORIAL_WRENCH );
+ GetEventManager()->RemoveListener( this, EVENT_COLLECTED_WRENCH );
+ break;
+ }
+ case EVENT_WASP_APPROACHED:
+ {
+ AddToQueue( TUTORIAL_BREAK_CAMERA );
+ GetEventManager()->RemoveListener( this, EVENT_WASP_APPROACHED );
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "TutorialManager: why are we registered for messages we don't care about?" );
+ break;
+ }
+ }
+
+// ProcessQueue();
+}
+
+//==============================================================================
+// TutorialManager::Initialize
+//==============================================================================
+// Description: Initializes this manager as a listener to various event types
+//
+// Parameters: None
+//
+// Return: N/A.
+//
+//==============================================================================
+void TutorialManager::Initialize()
+{
+ while( !m_Queue.empty() )
+ {
+ m_Queue.pop_back();
+ }
+
+ GetEventManager()->AddListener( this, EVENT_CARD_COLLECTED );
+ GetEventManager()->AddListener( this, EVENT_COLLECTED_COINS );
+ GetEventManager()->AddListener( this, EVENT_COLLECTED_WRENCH );
+ GetEventManager()->AddListener( this, static_cast< EventEnum >( EVENT_LOCATOR + LocatorEvent::INTERIOR_ENTRANCE ) );
+ GetEventManager()->AddListener( this, static_cast< EventEnum >( EVENT_LOCATOR + LocatorEvent::CAR_DOOR ) );
+ GetEventManager()->AddListener( this, EVENT_INTERACTIVE_GAG );
+ GetEventManager()->AddListener( this, EVENT_ENTERING_PLAYER_CAR );
+ GetEventManager()->AddListener( this, EVENT_ENTERING_TRAFFIC_CAR );
+ GetEventManager()->AddListener( this, EVENT_ANIMATED_CAM_SHUTDOWN );
+ GetEventManager()->AddListener( this, EVENT_BONUS_MISSION_CHARACTER_APPROACHED );
+ GetEventManager()->AddListener( this, EVENT_HIT_BREAKABLE );
+ GetEventManager()->AddListener( this, EVENT_TUTORIAL_DIALOG_DONE );
+ GetEventManager()->AddListener( this, EVENT_UNLOCKED_CAR );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_WASP_APPROACHED );
+}
+
+//==============================================================================
+// TutorialManager::MarkDialogFinished
+//==============================================================================
+// Description: when a piece of dialog is finished playing, you may need to play
+// another
+//
+// Parameters: None
+//
+// Return: N/A.
+//
+//==============================================================================
+void TutorialManager::MarkDialogFinished()
+{
+/*
+ CGuiScreenHud* hud = GetCurrentHud();
+ if( hud != NULL )
+ {
+ hud->TutorialBitmapInitOutro();
+ }
+*/
+ m_DialogCurrentlyPlaying = false;
+// ProcessQueue();
+}
+
+//==============================================================================
+// TutorialManager::ProcessQueue
+//==============================================================================
+// Description: takes an item out of the queue, and processes it
+//
+// Parameters: None
+//
+// Return: N/A.
+//
+//==============================================================================
+void TutorialManager::ProcessQueue()
+{
+/*
+ CGuiScreenHud* hud = GetCurrentHud();
+ if( hud == NULL )
+ {
+ return;
+ }
+*/
+ if( m_DialogCurrentlyPlaying )
+ {
+ return;
+ }
+
+ //
+ // return if there's nothing in the queue
+ //
+ size_t size = m_Queue.size();
+ if( size <= 0 )
+ {
+ return;
+ }
+
+ TutorialMode event = m_Queue.front();
+
+ //
+ // Check if the event is marked in the character sheet as "already played"
+ //
+ bool alreadySeen = this->QueryTutorialSeen( event );
+ if( !alreadySeen )
+ {
+ //
+ // Make sure we never see this message again
+ //
+ this->SetTutorialSeen( event, true );
+
+#ifndef RAD_WIN32
+ switch( event )
+ {
+ case TUTORIAL_BREAK_CAMERA:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_BREAK_CAMERA;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_BONUS_MISSION:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_BONUS_MISSION;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_BREAKABLE_DESTROYED:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_BREAKABLE_DESTROYED;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_COLLECTOR_CARD:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_COLLECTOR_CARD;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_COLLECTOR_COIN:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_COLLECTOR_COIN;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_INTERACTIVE_GAG:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_INTERACTIVE_GAG;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_INTERIOR_ENTERED:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_INTERIOR_ENTERED;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode);
+ break;
+ }
+ case TUTORIAL_GETTING_INTO_PLAYER_CAR:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_GETTING_INTO_PLAYER_CAR;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_GETTING_INTO_TRAFFIC_CAR:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_GETTING_INTO_TRAFFIC_CAR;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_GETTING_OUT_OF_CAR:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_GETTING_OUT_OF_CAR;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_RACE:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_RACE;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_START_GAME:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_START_GAME;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_VEHICLE_DESTROYED:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_VEHICLE_DESTROYED;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_REWARD:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_REWARD;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_UNLOCKED_CAR:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_UNLOCKED_CAR;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_WAGER_MISSION:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_WAGER_MISSION;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_WRENCH:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_WRENCH;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_AT_CAR_DOOR:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_AT_CAR_DOOR;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ case TUTORIAL_INTERACTIVE_GAG_APPROACHED:
+ {
+ m_DialogCurrentlyPlaying = true;
+ m_TimeSinceDialogStart = 0;
+ TutorialMode mode = TUTORIAL_INTERACTIVE_GAG_APPROACHED;
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_PLAY, &mode );
+ break;
+ }
+ default:
+ {
+ rAssertMsg( false, "Why don't we process this message" );
+ return;
+
+ break;
+ }
+ }
+#endif
+
+// hud->SetTutorialMessage( event );
+// hud->TutorialBitmapShow();
+
+ GetGuiSystem()->GotoScreen( CGuiWindow::GUI_SCREEN_ID_TUTORIAL, 0, 0,
+ CLEAR_WINDOW_HISTORY | FORCE_WINDOW_CHANGE_IMMEDIATE );
+
+#ifdef RAD_WIN32
+ m_DialogCurrentlyPlaying = false;
+#endif
+ GetGameFlow()->SetContext( CONTEXT_PAUSE );
+
+ }
+
+ //
+ // Pop an item off the queue
+ //
+ m_Queue.erase( m_Queue.begin() );
+}
+
+//==============================================================================
+// TutorialManager::Update
+//==============================================================================
+// Description: takes an item out of the queue, and processes it
+//
+// Parameters: None
+//
+// Return: N/A.
+//
+//==============================================================================
+void TutorialManager::Update( const float deltaT )
+{
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL && currentHud->IsActive() )
+ {
+ // only update tutorial manager if the HUD is currently active
+ //
+// m_TimeSinceDialogStart += deltaT;
+// if( m_TimeSinceDialogStart > 10000.0f )
+ {
+ m_TimeSinceDialogStart = 0;
+ m_DialogCurrentlyPlaying = false;
+/*
+ CGuiScreenHud* hud = GetCurrentHud();
+ if( hud != NULL )
+ {
+ hud->TutorialBitmapInitOutro();
+ }
+*/
+ }
+
+ ProcessQueue();
+ }
+}
+
+void
+TutorialManager::LoadData( const GameDataByte* dataBuffer, unsigned int numBytes )
+{
+ m_EnableTutorialEvents = ( dataBuffer[ 0 ] != 0 );
+
+ memcpy( &m_tutorialsSeen, dataBuffer + 1, sizeof( m_tutorialsSeen ) );
+}
+
+void
+TutorialManager::SaveData( GameDataByte* dataBuffer, unsigned int numBytes )
+{
+ dataBuffer[ 0 ] = m_EnableTutorialEvents ? ~0 : 0;
+
+ memcpy( dataBuffer + 1, &m_tutorialsSeen, sizeof( m_tutorialsSeen ) );
+}
+
+void
+TutorialManager::ResetData()
+{
+#ifdef RAD_WIN32
+
+ if( !GetInputManager()->GetController(0)->IsTutorialDisabled() )
+ {
+#endif
+ m_EnableTutorialEvents = true;
+ m_tutorialsSeen = 0;
+#ifdef RAD_WIN32
+ }
+#endif
+
+#ifndef FINAL
+ if( CommandLineOptions::Get( CLO_NO_TUTORIAL ) )
+ {
+ m_EnableTutorialEvents = false;
+ }
+#endif
+
+ // re-add event listeners
+ //
+ GetEventManager()->RemoveAll( this );
+ this->Initialize();
+}
+
+//=============================================================================
+// TutorialManager::QueryTutorialSeen
+//=============================================================================
+// Description: checks to see if a given tutorial has already been played
+//
+// Parameters: tutorial - enum of the tutorial in question
+//
+// Return: bool - has the tutorial been shown
+//
+//=============================================================================
+bool TutorialManager::QueryTutorialSeen( const TutorialMode tutorial )
+{
+ return (m_tutorialsSeen & (1 << tutorial)) > 0;
+
+}
+
+//=============================================================================
+// TutorialManager::SetTutorialSeen
+//=============================================================================
+// Description: sets the status of a specific tutorial
+//
+// Parameters: tutorial - enum of the tutorial in question
+//
+// Return: bool - has the tutorial been shown
+//
+//=============================================================================
+void TutorialManager::SetTutorialSeen( const TutorialMode tutorial, const bool seen )
+{
+ if ( seen )
+ {
+ m_tutorialsSeen |= (1 << tutorial);
+ }
+ else
+ {
+ m_tutorialsSeen &= ~(1 << tutorial);
+ }
+}
+
diff --git a/game/code/presentation/tutorialmanager.h b/game/code/presentation/tutorialmanager.h
new file mode 100644
index 0000000..c2a6f81
--- /dev/null
+++ b/game/code/presentation/tutorialmanager.h
@@ -0,0 +1,127 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: tutorialmanager.h
+//
+// Description: this system controls the operation of tutorial mode
+//
+// History: 19/12/2002 + Created -- Ian Gipson
+//
+//=============================================================================
+
+#ifndef TUTORIALMANGER_H
+#define TUTORIALMANGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include "events/eventlistener.h"
+#include "presentation/tutorialmode.h"
+#include <vector>
+
+#include <data/gamedata.h>
+#include <memory/stlallocators.h>
+
+//========================================
+// Forward References
+//========================================
+
+//========================================
+// typedefs
+//========================================
+
+
+//=============================================================================
+//
+// Synopsis: TutorialManager
+//
+//=============================================================================
+
+class TutorialManager : public EventListener,
+ public GameDataHandler
+{
+ public:
+ // Static Methods for accessing this singleton.
+ static TutorialManager* CreateInstance();
+ static TutorialManager* GetInstance();
+ static void DestroyInstance();
+
+ void EnableTutorialMode( const bool enabled );
+ bool IsTutorialModeEnabled() const;
+
+ void EnableTutorialEvents( const bool enabled );
+ bool IsTutorialEventsEnabled() const;
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ void Initialize();
+ void MarkDialogFinished();
+ void Update( const float deltaT );
+
+ TutorialMode GetCurrentEventID() const;
+ bool IsDialogPlaying() const;
+
+ // Implements GameDataHandler
+ //
+ virtual void LoadData( const GameDataByte* dataBuffer, unsigned int numBytes );
+ virtual void SaveData( GameDataByte* dataBuffer, unsigned int numBytes );
+ virtual void ResetData();
+
+ protected:
+ void AddToQueue( TutorialMode event );
+ void ProcessQueue();
+
+ bool QueryTutorialSeen( const TutorialMode tutorial );
+ void SetTutorialSeen( const TutorialMode tutorial, const bool seen );
+
+ static TutorialManager* spInstance;
+ private:
+ TutorialManager();
+ virtual ~TutorialManager();
+
+ std::vector< TutorialMode, s2alloc<TutorialMode> > m_Queue;
+
+ bool m_EnableTutorialMode : 1; // controlled by scripts
+ bool m_EnableTutorialEvents : 1; // controlled by user (in pause menu settings)
+ bool m_DialogCurrentlyPlaying : 1;
+ bool m_RaceMissionPlayed : 1;
+ bool m_BonusMissionPlayed : 1;
+ float m_TimeSinceDialogStart;
+
+ int m_tutorialsSeen; // bit field
+
+};
+
+inline TutorialManager* GetTutorialManager() { return( TutorialManager::GetInstance() ); }
+
+inline void TutorialManager::EnableTutorialMode( const bool enabled )
+{
+ m_EnableTutorialMode = enabled;
+}
+
+inline bool TutorialManager::IsTutorialModeEnabled() const
+{
+ return m_EnableTutorialMode;
+}
+
+inline void TutorialManager::EnableTutorialEvents( const bool enabled )
+{
+ m_EnableTutorialEvents = enabled;
+}
+
+inline bool TutorialManager::IsTutorialEventsEnabled() const
+{
+ return m_EnableTutorialEvents;
+}
+
+inline TutorialMode TutorialManager::GetCurrentEventID() const
+{
+ return m_Queue.front();
+}
+
+inline bool TutorialManager::IsDialogPlaying() const
+{
+ return m_DialogCurrentlyPlaying;
+}
+
+#endif //PRESENTATIONMANAGER_H
+
diff --git a/game/code/presentation/tutorialmode.h b/game/code/presentation/tutorialmode.h
new file mode 100644
index 0000000..80ed292
--- /dev/null
+++ b/game/code/presentation/tutorialmode.h
@@ -0,0 +1,61 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: tutorialmode.h
+//
+// Description: this system controls the operation of tutorial mode
+//
+// History: 19/12/2002 + Created -- Ian Gipson
+//
+//=============================================================================
+
+#ifndef TUTORIALMODE_H
+#define TUTORIALMODE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+//========================================
+// typedefs
+//========================================
+enum TutorialMode
+{
+ TUTORIAL_BREAK_CAMERA,
+ TUTORIAL_BONUS_MISSION,
+ TUTORIAL_START_GAME,
+ TUTORIAL_GETTING_INTO_PLAYER_CAR,
+ TUTORIAL_GETTING_INTO_TRAFFIC_CAR,
+ TUTORIAL_INTERACTIVE_GAG,
+ TUTORIAL_RACE,
+ TUTORIAL_COLLECTOR_CARD,
+ TUTORIAL_COLLECTOR_COIN,
+ TUTORIAL_REWARD,
+ TUTORIAL_GETTING_OUT_OF_CAR,
+ TUTORIAL_BREAKABLE_APPROACHED,
+ TUTORIAL_BREAKABLE_DESTROYED,
+ TUTORIAL_INTERIOR_ENTERED,
+ TUTORIAL_COIN_COLLECTED, //never triggered?
+ TUTORIAL_VEHICLE_DESTROYED,
+ TUTORIAL_UNLOCKED_CAR,
+ TUTORIAL_WRENCH,
+ TUTORIAL_AT_CAR_DOOR,
+ TUTORIAL_INTERACTIVE_GAG_APPROACHED, //never triggered
+ TUTORIAL_WAGER_MISSION,
+ TUTORIAL_INVALID,
+ TUTORIAL_MAX = TUTORIAL_INVALID
+};
+
+//=============================================================================
+//
+// Synopsis:
+//
+//=============================================================================
+
+
+#endif //TUTORIALMODE_H
+
diff --git a/game/code/render/Culling/BlockCoord.h b/game/code/render/Culling/BlockCoord.h
new file mode 100644
index 0000000..8f2688a
--- /dev/null
+++ b/game/code/render/Culling/BlockCoord.h
@@ -0,0 +1,34 @@
+#ifndef __BLOCK_COORD_H__
+#define __BLOCK_COORD_H__
+
+#include <render/culling/Vector3f.h>
+#include <render/culling/Bounds.h>
+#include <render/culling/FloatFuncs.h>
+
+class BlockCoord
+{
+public:
+ BlockCoord(){}
+ ~BlockCoord(){}
+
+ void Init( Bounds3f& irBounds, Vector3f& irGranularities )
+ {
+ mOffset = irBounds.mMin;
+
+ for( int i=0; i<3; i++ )
+ {
+ mExtents[i] = sUpperInt( (irBounds.mMax[i] - irBounds.mMin[i])/irGranularities[i] );
+
+ rAssert( mExtents[i] > -1 );
+
+ if( mExtents[i] == 0 )
+ mExtents[i] = 1;
+ }
+ }
+
+ Vector3f mOffset;
+ int mExtents[3];
+protected:
+
+};
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/Bounds.h b/game/code/render/Culling/Bounds.h
new file mode 100644
index 0000000..1df07c2
--- /dev/null
+++ b/game/code/render/Culling/Bounds.h
@@ -0,0 +1,171 @@
+#ifndef __BOUNDS_H__
+#define __BOUNDS_H__
+
+#include <render/culling/Vector3f.h>
+#include <render/culling/Vector3i.h>
+
+class Bounds3f
+{
+public:
+ Bounds3f(){}
+ ~Bounds3f(){}
+
+ void Accumulate( float iX, float iY, float iZ )
+ {
+ if( iX < mMin[0])
+ {
+ mMin[0] = iX;
+ }
+ else
+ {
+ if( iX > mMax[0])
+ {
+ mMax[0] = iX;
+ }
+ }
+
+ if( iY < mMin[1])
+ {
+ mMin[1] = iY;
+ }
+ else
+ {
+ if( iY > mMax[1])
+ {
+ mMax[1] = iY;
+ }
+ }
+
+ if( iZ < mMin[2])
+ {
+ mMin[2] = iZ;
+ }
+ else
+ {
+ if( iZ > mMax[2])
+ {
+ mMax[2] = iZ;
+ }
+ }
+ }
+
+
+ void Accumulate( float* ipPoint )
+ {
+ for( int i=0; i<3; i++ )
+ {
+ if( ipPoint[i] < mMin[i])
+ {
+ mMin[i] = ipPoint[i];
+ }
+ else
+ {
+ if( ipPoint[i] > mMax[i])
+ {
+ mMax[i] = ipPoint[i];
+ }
+ }
+ }
+ }
+
+ void Accumulate( Vector3f& irPoint )
+ {
+ for( int i=0; i<3; i++ )
+ {
+ if( irPoint[i] < mMin[i])
+ {
+ mMin[i] = irPoint[i];
+ }
+ else
+ {
+ if( irPoint[i] > mMax[i])
+ {
+ mMax[i] = irPoint[i];
+ }
+ }
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Taken & Tailored
+ // from http://www.gamasutra.com/features/19991018/Gomez_4.htm
+ //////////////////////////////////////////////////////////////////////////
+ bool IntersectsSphere( Vector3f& irCenter, float iRadius )
+ {
+ float s, d = 0;
+
+ //find the square of the distance
+ //from the sphere to the box
+ for( int i=0 ; i<3 ; i++ )
+ {
+ if( irCenter[i] < mMin[i] )
+ {
+ s = irCenter[i] - mMin[i];
+ d += s*s;
+ }
+ else if( irCenter[i] > mMax[i] )
+ {
+ s = irCenter[i] - mMax[i];
+ d += s*s;
+ }
+ }
+ return d <= iRadius*iRadius;
+ }
+
+ Vector3f mMin, mMax;
+};
+
+class MinBound : public Vector3f
+{
+public:
+ MinBound(){}
+ ~MinBound(){}
+
+ void Accumulate( Vector3f& irPoint )
+ {
+ if( irPoint.x < x)
+ {
+ x = irPoint.x;
+ }
+ if( irPoint.y < y)
+ {
+ y = irPoint.y;
+ }
+ if( irPoint.z < z)
+ {
+ z = irPoint.z;
+ }
+ }
+
+};
+
+
+class Bounds3i
+{
+public:
+ Bounds3i(){}
+ ~Bounds3i(){}
+
+ void Accumulate( Vector3i& irPoint )
+ {
+ for( int i=0; i<3; i++ )
+ {
+ if( irPoint[i] < mMin[i])
+ {
+ mMin[i] = irPoint[i];
+ }
+ else
+ {
+ if( irPoint[i] > mMax[i])
+ {
+ mMax[i] = irPoint[i];
+ }
+ }
+ }
+ }
+
+ Vector3i mMin, mMax;
+};
+
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/BoxPts.cpp b/game/code/render/Culling/BoxPts.cpp
new file mode 100644
index 0000000..f722da8
--- /dev/null
+++ b/game/code/render/Culling/BoxPts.cpp
@@ -0,0 +1,370 @@
+#include <render/culling/BoxPts.h>
+#include <debug/profiler.h>
+ // ISpatialProxyAA
+
+// <0.0 - Less than comparison object in posn
+// =0.0 - Intersection with comparison object
+// >0.0 - Greater than comparison object in posn
+float BoxPts::CompareTo( AAPlane3f& irPlane )
+{
+ if( irPlane.mPosn - msIntersectionEpsilon <= mBounds.mMin[(int)irPlane.mAxis] )
+ {
+ return -1.0f;
+ }
+ if( irPlane.mPosn + msIntersectionEpsilon >= mBounds.mMax[(int)irPlane.mAxis] )
+ {
+ return 1.0f;
+ }
+
+ return 0.0f;
+}
+
+bool BoxPts::DoesIntersect( AAPlane3f& irClipPlane )
+{
+ rAssert(false);
+ return true;
+}
+
+bool BoxPts::DoesntIntersect( AAPlane3f& irClipPlane )
+{
+ rAssert(false);
+ return true;
+}
+
+
+float BoxPts::TestNotOutsideMinX( ISpatialProxyAA& irSpatialProxy )
+{
+ for( int i=irSpatialProxy.nPts()-2; i>-1; i-- )
+ {
+ if( irSpatialProxy.mPt(i).x >= mBounds.mMin.x )
+ {
+ //Inside
+ return -1.0f;
+ }
+ }
+ //Outside
+ return 1.0f;
+}
+
+float BoxPts::TestNotOutsideMinY( ISpatialProxyAA& irSpatialProxy )
+{
+ for( int i=irSpatialProxy.nPts()-2; i>-1; i-- )
+ {
+ if( irSpatialProxy.mPt(i).y >= mBounds.mMin.y )
+ {
+ //Inside
+ return -1.0f;
+ }
+ }
+ //Outside
+ return 1.0f;
+}
+
+float BoxPts::TestNotOutsideMinZ( ISpatialProxyAA& irSpatialProxy )
+{
+ for( int i=irSpatialProxy.nPts()-2; i>-1; i-- )
+ {
+ if( irSpatialProxy.mPt(i).z >= mBounds.mMin.z )
+ {
+ //Inside
+ return -1.0f;
+ }
+ }
+ //Outside
+ return 1.0f;
+}
+
+float BoxPts::TestNotOutsideMaxX( ISpatialProxyAA& irSpatialProxy )
+{
+ for( int i=irSpatialProxy.nPts()-2; i>-1; i-- )
+ {
+ if( irSpatialProxy.mPt(i).x <= mBounds.mMax.x )
+ {
+ //Inside
+ return -1.0f;
+ }
+ }
+ //Outside
+ return 1.0f;
+}
+
+float BoxPts::TestNotOutsideMaxY( ISpatialProxyAA& irSpatialProxy )
+{
+ for( int i=irSpatialProxy.nPts()-2; i>-1; i-- )
+ {
+ if( irSpatialProxy.mPt(i).y <= mBounds.mMax.y )
+ {
+ //Inside
+ return -1.0f;
+ }
+ }
+ //Outside
+ return 1.0f;
+}
+
+float BoxPts::TestNotOutsideMaxZ( ISpatialProxyAA& irSpatialProxy )
+{
+ for( int i=irSpatialProxy.nPts()-2; i>-1; i-- )
+ {
+ if( irSpatialProxy.mPt(i).z <= mBounds.mMax.z )
+ {
+ //Inside
+ return -1.0f;
+ }
+ }
+ //Outside
+ return 1.0f;
+}
+
+int BoxPts::nPts()
+{
+ return msPtCount;
+}
+
+Vector3f BoxPts::mPt( int iIndex )
+{
+ switch(iIndex)
+ {
+ case 0:
+ return mBounds.mMin;
+ break;
+
+ case 1:
+ return Vector3f( mBounds.mMin.x, mBounds.mMin.y, mBounds.mMax.z );
+ break;
+
+ case 2:
+ return Vector3f( mBounds.mMin.x, mBounds.mMax.y, mBounds.mMin.z );
+ break;
+
+ case 3:
+ return Vector3f( mBounds.mMax.x, mBounds.mMin.y, mBounds.mMin.z );
+ break;
+
+ case 4:
+ return Vector3f( mBounds.mMin.x, mBounds.mMax.y, mBounds.mMax.z );
+ break;
+
+ case 5:
+ return Vector3f( mBounds.mMax.x, mBounds.mMax.y, mBounds.mMin.z );
+ break;
+
+ case 6:
+ return Vector3f( mBounds.mMax.x, mBounds.mMin.y, mBounds.mMax.z );
+ break;
+
+ case 7:
+ return mBounds.mMax;
+ break;
+
+ default:
+ rAssert(false);
+ return mBounds.mMin;
+ break;
+
+ /*
+ m[0] = irBounds.mMin;
+
+ m[1].x = irBounds.mMin.x;
+ m[1].y = irBounds.mMin.y;
+ m[1].z = irBounds.mMax.z;
+
+ m[2].x = irBounds.mMin.x;
+ m[2].y = irBounds.mMax.y;
+ m[2].z = irBounds.mMin.z;
+
+ m[3].x = irBounds.mMax.x;
+ m[3].y = irBounds.mMin.y;
+ m[3].z = irBounds.mMin.z;
+
+ m[4].x = irBounds.mMin.x;
+ m[4].y = irBounds.mMax.y;
+ m[4].z = irBounds.mMax.z;
+
+ m[5].x = irBounds.mMax.x;
+ m[5].y = irBounds.mMax.y;
+ m[5].z = irBounds.mMin.z;
+
+ m[6].x = irBounds.mMax.x;
+ m[6].y = irBounds.mMin.y;
+ m[6].z = irBounds.mMax.z;
+
+ m[7] = irBounds.mMax;
+ */
+ }
+}
+
+Vector3f BoxPts::GetPoint()
+{
+ return mBounds.mMin;
+}
+
+void BoxPts::CutOffGT( AAPlane3f& irPlane3f )
+{
+ if( mBounds.mMax[(int)irPlane3f.mAxis] > irPlane3f.mPosn )
+ {
+ mBounds.mMax[(int)irPlane3f.mAxis] = irPlane3f.mPosn;
+
+ if( mBounds.mMin[(int)irPlane3f.mAxis] > irPlane3f.mPosn )
+ {
+ mBounds.mMin[(int)irPlane3f.mAxis] = irPlane3f.mPosn;
+ }
+ }
+ //Temporary debug;
+ //Though it's conceivable that you'd want to cut a box down into a plane,
+ //SpatialIterators don't want to.
+ rAssert( mBounds.mMin[(int)irPlane3f.mAxis] != mBounds.mMax[(int)irPlane3f.mAxis] );
+}
+
+void BoxPts::CutOffLT( AAPlane3f& irPlane3f )
+{
+ if( mBounds.mMin[(int)irPlane3f.mAxis] < irPlane3f.mPosn )
+ {
+ mBounds.mMin[(int)irPlane3f.mAxis] = irPlane3f.mPosn;
+
+ if( mBounds.mMax[(int)irPlane3f.mAxis] < irPlane3f.mPosn )
+ {
+ mBounds.mMax[(int)irPlane3f.mAxis] = irPlane3f.mPosn;
+ }
+ }
+ //Temporary debug;
+ //Though it's conceivable that you'd want to cut a box down into a plane,
+ //SpatialIterators don't want to.
+ rAssert( mBounds.mMin[(int)irPlane3f.mAxis] != mBounds.mMax[(int)irPlane3f.mAxis] );
+}
+
+
+float BoxPts::CompareTo( const Vector3f& irPoint )
+{
+ if( irPoint.x < mBounds.mMin.x )
+ return 1.0f;
+
+ if( irPoint.y < mBounds.mMin.y )
+ return 1.0f;
+
+ if( irPoint.z < mBounds.mMin.z )
+ return 1.0f;
+
+ if( irPoint.x > mBounds.mMax.x )
+ return 1.0f;
+
+ if( irPoint.y > mBounds.mMax.y )
+ return 1.0f;
+
+ if( irPoint.z > mBounds.mMax.z )
+ return 1.0f;
+
+ return -1.0f;
+}
+float BoxPts::CompareToXZ( const Vector3f& irPoint )
+{
+ if( irPoint.x < mBounds.mMin.x )
+ return 1.0f;
+
+ if( irPoint.z < mBounds.mMin.z )
+ return 1.0f;
+
+ if( irPoint.x > mBounds.mMax.x )
+ return 1.0f;
+
+ if( irPoint.z > mBounds.mMax.z )
+ return 1.0f;
+
+ return -1.0f;
+}
+
+void BoxPts::SetTo( Bounds3f& irBounds )
+{
+ mBounds = irBounds;
+}
+
+Vector3f BoxPts::operator[]( int i )
+{
+ return mPt(i);
+
+}
+
+// <0.0 - Inside Spatial Proxy
+// =0.0 - On Spatial Proxy Surface
+// >0.0 - Outside Spatial Proxy
+float BoxPts::TestNotOutside( ISpatialProxyAA& irSpatialProxy )
+{
+//BEGIN_PROFILE("BoxPts::TestNotOutside")
+ Vector3f testPt( irSpatialProxy.mPt(irSpatialProxy.nPts()-1) );
+
+ //Test the first Pt, if it's outside a plane,
+ //ensure all other pts are outside the same plane
+ //Gives false positives for crossing the corner on the outside
+ /*
+ if( testPt.x < mBounds.mMin.x )
+ return TestNotOutsideMinX( irSpatialProxy );
+
+ if( testPt.y < mBounds.mMin.y )
+ return TestNotOutsideMinY( irSpatialProxy );
+
+ if( testPt.z < mBounds.mMin.z )
+ return TestNotOutsideMinZ( irSpatialProxy );
+
+ if( testPt.x > mBounds.mMax.x )
+ return TestNotOutsideMaxX( irSpatialProxy );
+
+ if( testPt.y > mBounds.mMax.y )
+ return TestNotOutsideMaxY( irSpatialProxy );
+
+ if( testPt.z > mBounds.mMax.z )
+ return TestNotOutsideMinZ( irSpatialProxy );
+ */
+
+ if( testPt.x < mBounds.mMin.x )
+ {
+ if( TestNotOutsideMinX( irSpatialProxy ) > 0.0f )
+ {
+//END_PROFILE("BoxPts::TestNotOutside")
+ return 1.0f;
+ }
+ }
+ else if( testPt.x > mBounds.mMax.x )
+ {
+ if( TestNotOutsideMaxX( irSpatialProxy ) > 0.0f )
+ {
+//END_PROFILE("BoxPts::TestNotOutside")
+ return 1.0f;
+ }
+ }
+
+ if( testPt.y < mBounds.mMin.y )
+ {
+ if( TestNotOutsideMinY( irSpatialProxy ) > 0.0f )
+ {
+//END_PROFILE("BoxPts::TestNotOutside")
+ return 1.0f;
+ }
+ }
+ else if( testPt.y > mBounds.mMax.y )
+ {
+ if( TestNotOutsideMaxY( irSpatialProxy ) > 0.0f )
+ {
+//END_PROFILE("BoxPts::TestNotOutside")
+ return 1.0f;
+ }
+ }
+
+ if( testPt.z < mBounds.mMin.z )
+ {
+ if( TestNotOutsideMinZ( irSpatialProxy ) > 0.0f )
+ {
+//END_PROFILE("BoxPts::TestNotOutside")
+ return 1.0f;
+ }
+ }
+ else if( testPt.z > mBounds.mMax.z )
+ {
+ if( TestNotOutsideMaxZ( irSpatialProxy ) > 0.0f )
+ {
+//END_PROFILE("BoxPts::TestNotOutside")
+ return 1.0f;
+ }
+ }
+//END_PROFILE("BoxPts::TestNotOutside")
+ return -1.0f;
+}
diff --git a/game/code/render/Culling/BoxPts.h b/game/code/render/Culling/BoxPts.h
new file mode 100644
index 0000000..aa18ac1
--- /dev/null
+++ b/game/code/render/Culling/BoxPts.h
@@ -0,0 +1,58 @@
+#ifndef __BOX_PTS_H__
+#define __BOX_PTS_H__
+
+#include <render/culling/Vector3f.h>
+#include <render/culling/Plane3f.h>
+#include <render/culling/Bounds.h>
+
+#include <render/culling/ISpatialProxy.h>
+
+class BoxPts : public ISpatialProxyAA
+{
+public:
+ BoxPts(){}
+ ~BoxPts(){}
+
+ // ISpatialProxyAA
+ // <0.0 - Inside Spatial Proxy
+ // =0.0 - On Spatial Proxy Surface
+ // >0.0 - Outside Spatial Proxy
+ virtual float CompareTo( AAPlane3f& irPlane );
+ virtual float CompareTo( const Vector3f& irPoint );
+ virtual float CompareToXZ( const Vector3f& irPoint );
+
+ virtual float TestNotOutside( ISpatialProxyAA& irSpatialProxy );
+
+ virtual int nPts();
+ virtual Vector3f mPt( int iIndex );
+
+ virtual bool DoesIntersect( AAPlane3f& irClipPlane );
+ virtual bool DoesntIntersect( AAPlane3f& irClipPlane );
+
+ //Get a point representing some point within the SpatialProxy
+ virtual Vector3f GetPoint();
+
+ void CutOffGT( AAPlane3f& irPlane3f );
+ void CutOffLT( AAPlane3f& irPlane3f );
+
+ virtual void SetTo( Bounds3f& irBounds );
+
+ Vector3f operator[]( int i );
+
+
+ float TestNotOutsideMinX( ISpatialProxyAA& irSpatialProxy );
+ float TestNotOutsideMinY( ISpatialProxyAA& irSpatialProxy );
+ float TestNotOutsideMinZ( ISpatialProxyAA& irSpatialProxy );
+ float TestNotOutsideMaxX( ISpatialProxyAA& irSpatialProxy );
+ float TestNotOutsideMaxY( ISpatialProxyAA& irSpatialProxy );
+ float TestNotOutsideMaxZ( ISpatialProxyAA& irSpatialProxy );
+
+ enum
+ {
+ msPtCount = 8
+ };
+
+ Bounds3f mBounds;
+protected:
+};
+#endif
diff --git a/game/code/render/Culling/Cell.cpp b/game/code/render/Culling/Cell.cpp
new file mode 100644
index 0000000..8c74b44
--- /dev/null
+++ b/game/code/render/Culling/Cell.cpp
@@ -0,0 +1,209 @@
+#include <render/culling/Cell.h>
+#include <raddebug.hpp>
+
+Cell::Cell()
+{
+ mRenderWeight = 0;
+
+ mpCentroid[0] = 0.0f;
+ mpCentroid[1] = 0.0f;
+ mpCentroid[2] = 0.0f;
+
+ mBounds.mMax.x = 0.0f;
+ mBounds.mMin.x = 0.0f;
+
+ mBlockIndex[0] = -1;
+}
+
+Cell::~Cell()
+{
+}
+
+////////////////////////////////////////////////////////////////////////
+// What:
+// Accumulator functions use negative numbers to accumulate
+// Value is inverted on call to Generate Cell
+// Why:
+// This is used to ensure access doesn't happen previous to generation
+// Note:
+// The only holes in this type of assertion is where all the vertices
+// are at X = 0, or there are no Vertices.
+//
+////////////////////////////////////////////////////////////////////////
+
+////////////////////////////////////////////////////////////////////////
+// TODO:
+// Eliminate a linear factor out of these for loops:
+// Incriment RenderWeight by size at the end; reorganize.
+////////////////////////////////////////////////////////////////////////
+
+
+void Cell::AccVertexList( FixedArray<Vector3f>& irVertexList )
+{
+ int i;
+ for( i=irVertexList.mSize; i>-1; i-- )
+ {
+ AccVertex( irVertexList[i] );
+ }
+}
+
+void Cell::AccVertexList( rmt::Vector* ipVector, int iCount )
+{
+ int i;
+ for( i=0; i<iCount; i++ )
+ {
+ AccVertex( ipVector[i] );
+ }
+}
+
+void Cell::AccVertexList( float* ipVertex, int iCount )
+{
+ int i;
+ iCount = iCount*3;
+
+ for( i=0; i<iCount; i+=3 )
+ {
+ AccVertex( &(ipVertex[i]) );
+ }
+}
+
+void Cell::AccVertex( Vector3f& irVector )
+{
+ mpCentroid[0] -= irVector.x;
+ mpCentroid[1] -= irVector.y;
+ mpCentroid[2] -= irVector.z;
+ mRenderWeight++;
+}
+
+void Cell::AccVertex( Vector3f& irVertex, int& irWeight )
+{
+ float tmpWeight = (float)irWeight;
+ mpCentroid[0] -= irVertex.x * tmpWeight;
+ mpCentroid[1] -= irVertex.y * tmpWeight;
+ mpCentroid[2] -= irVertex.z * tmpWeight;
+ mRenderWeight += irWeight;
+}
+
+
+void Cell::AccVertex( rmt::Vector& irVector )
+{
+ mpCentroid[0] -= irVector.x;
+ mpCentroid[1] -= irVector.y;
+ mpCentroid[2] -= irVector.z;
+ mRenderWeight++;
+}
+
+void Cell::AccVertex( float iX, float iY, float iZ )
+{
+ mpCentroid[0] -= iX;
+ mpCentroid[1] -= iY;
+ mpCentroid[2] -= iZ;
+ mRenderWeight++;
+}
+
+void Cell::AccVertex( float* ipVertex )
+{
+ mpCentroid[0] -= ipVertex[0];
+ mpCentroid[1] -= ipVertex[1];
+ mpCentroid[2] -= ipVertex[2];
+ mRenderWeight++;
+}
+
+void Cell::GenerateCell()
+{
+ if( mRenderWeight != 0 )
+ {
+ mpCentroid[0] = mpCentroid[0]/(-mRenderWeight);
+ mpCentroid[1] = mpCentroid[1]/(-mRenderWeight);
+ mpCentroid[2] = mpCentroid[2]/(-mRenderWeight);
+ }
+
+}
+
+float* Cell::Centroid()
+{
+ rAssert( IsGenerated() );
+ return mpCentroid;
+}
+
+float Cell::Centroid( int iAxis )
+{
+ rAssert( IsGenerated() );
+ rAssert( iAxis < 3 );
+ return mpCentroid[iAxis];
+}
+
+void Cell::Centroid( float* ipCentroid )
+{
+ rAssert( IsGenerated() );
+
+ ipCentroid[0] = mpCentroid[0];
+ ipCentroid[1] = mpCentroid[1];
+ ipCentroid[2] = mpCentroid[2];
+}
+
+int Cell::RenderWeight()
+{
+ rAssert( IsGenerated() );
+ return mRenderWeight;
+}
+
+
+void Cell::SetBounds( Bounds3f& irBounds )
+{
+ mBounds = irBounds;
+
+ rAssert( mBounds.mMin.x != mBounds.mMax.x );
+ rAssert( mBounds.mMin.y != mBounds.mMax.y );
+ rAssert( mBounds.mMin.z != mBounds.mMax.z );
+}
+
+float Cell::MaxPlane( int iAxis )
+{
+ rAssert( IsGenerated() );
+ rAssert( IsBoundsSet() );
+ return mBounds.mMax[iAxis];
+}
+
+float Cell::MinPlane( int iAxis )
+{
+ rAssert( IsGenerated() );
+ rAssert( IsBoundsSet() );
+ return mBounds.mMin[iAxis];
+}
+
+
+void Cell::SetBlockIndex( Vector3i& irBlockIndex )
+{
+ mBlockIndex = irBlockIndex;
+}
+
+int& Cell::BlockIndex( int iAxis )
+{
+ rAssert( IsGenerated() );
+ rAssert( IsIndexSet() );
+ return mBlockIndex[iAxis];
+}
+
+Vector3i& Cell::BlockIndex()
+{
+ rAssert( IsGenerated() );
+ rAssert( IsIndexSet() );
+ return mBlockIndex;
+}
+
+bool Cell::IsGenerated()
+{
+ // return (mpCentroid[0] >= 0.0f);
+ return true;
+}
+
+bool Cell::IsBoundsSet()
+{
+ return ( !((mBounds.mMin.x == 0.0f)&&(mBounds.mMax.x == 0.0f)) );
+}
+
+bool Cell::IsIndexSet()
+{
+ return (mBlockIndex[0] >= 0);
+}
diff --git a/game/code/render/Culling/Cell.h b/game/code/render/Culling/Cell.h
new file mode 100644
index 0000000..84c02f7
--- /dev/null
+++ b/game/code/render/Culling/Cell.h
@@ -0,0 +1,66 @@
+#ifndef __CELL_H__
+#define __CELL_H__
+
+#include <radmath/radmath.hpp>
+#include <render/culling/FixedArray.h>
+#include <render/culling/Vector3f.h>
+#include <render/culling/Vector3i.h>
+#include <render/culling/Bounds.h>
+//
+// Cell
+//
+// A Cell is a collection of geometry or other render-salient data
+// which is considered as a single chunk in space. It has static bounds
+// as it is used to drive the OctTree culling algorithms. These bounds
+// are unknown to the Cell, as it can be more effecient to combine the
+// indexing scheme with the bounds.
+//
+// mRenderWeight - Currently the number of vertices in the cell
+// mCentroid - A Vector representing the centroid of the cell
+//
+class Cell
+{
+public:
+ Cell();
+ ~Cell();
+
+ void AccVertexList( FixedArray<Vector3f>& irVertexList );
+ void AccVertexList( rmt::Vector* ipVector, int iCount );
+ void AccVertexList( float* ipVertex, int iCount );
+
+ void AccVertex( Vector3f& irVertex );
+ void AccVertex( Vector3f& irVertex, int& irWeight );
+ void AccVertex( rmt::Vector& iVector );
+ void AccVertex( float* ipVertex);
+ void AccVertex( float iX, float iY, float iZ );
+
+ void GenerateCell();
+
+ float* Centroid();
+ float Centroid( int iAxis );
+ void Centroid( float* ipCentroid );
+ int RenderWeight();
+
+ void SetBounds( Bounds3f& irBounds );
+ float MaxPlane( int iAxis );
+ float MinPlane( int iAxis );
+
+ void SetBlockIndex( Vector3i& irBlockIndex );
+ int& BlockIndex( int iAxis );
+ Vector3i& BlockIndex();
+
+ //
+ // Debug checking
+ //
+ bool IsGenerated();
+ bool IsBoundsSet();
+ bool IsIndexSet();
+protected:
+ float mpCentroid[3];
+ int mRenderWeight;
+
+ Bounds3f mBounds;
+ Vector3i mBlockIndex;
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/CellBlock.cpp b/game/code/render/Culling/CellBlock.cpp
new file mode 100644
index 0000000..4bf125c
--- /dev/null
+++ b/game/code/render/Culling/CellBlock.cpp
@@ -0,0 +1,317 @@
+#include <render/culling/CellBlock.h>
+#include <render/culling/VectorLib.h>
+#include <render/culling/FloatFuncs.h>
+
+
+CellBlock::CellBlock(){}
+CellBlock::~CellBlock(){}
+
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+/*
+void CellBlock::Init
+(
+ int iSize,
+ BlockCoord& irBlockCoord,
+ Vector3f& irGranularities
+)
+{
+ mBlockCoord = irBlockCoord;
+ mGranularities = irGranularities;
+
+ Allocate( iSize );
+}
+*/
+
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+void CellBlock::Init
+(
+ FixedArray<Vector3f>& irPoints,
+ Vector3f& irGranularities
+)
+{
+ /////////////////////////////////////////////////////////////
+ // TODO -
+ //
+ // 1) Resolve: Find out whether any platforms round negative
+ // numbers toward Zero (ie, the sensible, magnitude driven way)
+ // as ARM says it's platform dependant
+ /////////////////////////////////////////////////////////////
+
+ theVectorLib().FindBounds( mBounds, irPoints );
+
+ mGranularities = irGranularities;
+ mBlockCoord.Init( mBounds, mGranularities );
+
+ Allocate( mBlockCoord.mExtents[0] * mBlockCoord.mExtents[1] * mBlockCoord.mExtents[2] );
+
+ //BoundCellsByGranularities();
+
+ Add( irPoints );
+}
+
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+void CellBlock::Init
+(
+ FixedArray<Vector3f>& irPoints,
+ FixedArray<int>& irPointWeights,
+ Vector3f& irGranularities
+)
+{
+ theVectorLib().FindBounds( mBounds, irPoints );
+
+ mGranularities = irGranularities;
+ mBlockCoord.Init( mBounds, mGranularities );
+
+ Allocate( mBlockCoord.mExtents[0] * mBlockCoord.mExtents[1] * mBlockCoord.mExtents[2] );
+
+ //BoundCellsByGranularities();
+
+ Add( irPoints, irPointWeights );
+}
+
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+/*void CellBlock::BoundCellsByGranularities()
+{
+ Bounds3f CurBounds;
+ for( int i=mBlockCoord.mExtents[0]; i>=0; i-- )
+ {
+ for( int j=mBlockCoord.mExtents[1]; j>=0; j-- )
+ {
+ for( int k=mBlockCoord.mExtents[2]; k>=0; k-- )
+ {
+ CurBounds.mMin.Set( mBlockCoord.mOffset.x + i*mGranularities.x,
+ mBlockCoord.mOffset.y + j*mGranularities.y,
+ mBlockCoord.mOffset.z + k*mGranularities.z );
+
+ CurBounds.mMax.Set( mBlockCoord.mOffset.x + (i+1)*mGranularities.x,
+ mBlockCoord.mOffset.y + (j+1)*mGranularities.y,
+ mBlockCoord.mOffset.z + (k+1)*mGranularities.z );
+
+
+ GetCell(i,j,k).SetBounds( CurBounds );
+ }
+ }
+ }
+}
+*/
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+void CellBlock::Add( FixedArray<Vector3f>& irPoints )
+{
+ rAssert( IsSetUp() );
+
+ int i;
+ for( i=(irPoints.mSize-1); i>-1; i-- )
+ {
+ mpData[ CellIndex(irPoints[i]) ].AccVertex(irPoints[i]) ;
+ }
+}
+
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+void CellBlock::Add
+(
+ FixedArray<Vector3f>& irPoints,
+ FixedArray<int>& irPointWeights
+)
+{
+ rAssert( IsSetUp() );
+
+ int i;
+ for( i=(irPoints.mSize-1); i>-1; i-- )
+ {
+ operator[](CellIndex(irPoints[i])).AccVertex( irPoints[i], irPointWeights[i] );
+ }
+}
+
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+void CellBlock::Add( Vector3f& irPoint )
+{
+ rAssert( IsSetUp() );
+
+ mpData[ CellIndex(irPoint) ].AccVertex(irPoint);
+}
+
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+void CellBlock::GenerateCells()
+{
+ for( int i=mSize-1; i>-1; i-- )
+ {
+ mpData[i].GenerateCell();
+ }
+
+ SetCellsBlockData();
+}
+
+/////////////////////////////////////////////////////////////
+// Translate Extracts the Data from this Object destructively
+//
+// The Object is emptied of its translation-salient data, as
+// said data takes on a new representation.
+/////////////////////////////////////////////////////////////
+void CellBlock::Translate( FixedArray<Cell>& orCellArray, Vector3i& orSpans )
+{
+ //
+ // Extract the array without allocations
+ //
+ orCellArray.Clear();
+ orCellArray.mSize = mSize;
+ orCellArray.mpData = mpData;
+
+ mpData = NULL;
+
+ orSpans.Set( mBlockCoord.mExtents );
+}
+/////////////////////////////////////////////////////////////
+// Only extracts the non-empty cells from the cell matrix
+// More expensive than the simple Translate, as it requires
+// two iterative passes and an extra alloc and free.
+/////////////////////////////////////////////////////////////
+void CellBlock::TranslateNonEmpty( FixedArray<Cell>& orCellArray, Vector3i& orSpans )
+{
+ int emptyCells = CountEmptyCells();
+
+ orCellArray.Clear();
+ orCellArray.Allocate( mSize - emptyCells );
+
+ for( int j = 0, i=mSize-1; i>-1; i-- )
+ {
+ if( mpData[i].RenderWeight() > 0 )
+ {
+ orCellArray.mpData[j] = mpData[i];
+ j++;
+ }
+ }
+
+ Clear();
+
+ orSpans.Set( mBlockCoord.mExtents );
+}
+/////////////////////////////////////////////////////////////
+// Only extracts the non-empty cells from the cell matrix
+// More expensive than the simple Translate, as it requires
+// two iterative passes and an extra alloc and free.
+/////////////////////////////////////////////////////////////
+void CellBlock::ExtractNonEmptyCells( FixedArray<Cell>& orCellArray )
+{
+ int emptyCells = CountEmptyCells();
+
+ orCellArray.Allocate( mSize - emptyCells );
+
+ for( int j = 0, i=mSize-1; i>-1; i-- )
+ {
+ if( mpData[i].RenderWeight() > 0 )
+ {
+ orCellArray.mpData[j] = mpData[i];
+ j++;
+ }
+ }
+
+ Clear();
+}
+/////////////////////////////////////////////////////////////
+// Counts all the Cells which have not been seeded with
+// non-zero render weights
+/////////////////////////////////////////////////////////////
+int CellBlock::CountEmptyCells()
+{
+ int Count=0;
+
+ for( int i=mSize-1; i>-1; i-- )
+ {
+ if( mpData[i].RenderWeight() < 1 )
+ {
+ Count++;
+ }
+ }
+ return Count;
+}
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+void CellBlock::ExtractCells( FixedArray<Cell>& orCellArray )
+{
+ //
+ // Extract the array without allocations
+ //
+ orCellArray.Clear();
+ orCellArray.mSize = mSize;
+ orCellArray.mpData = mpData;
+
+ mpData = NULL;
+}
+
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+void CellBlock::ExtractDims( Vector3i& orSpans )
+{
+ orSpans.Set( mBlockCoord.mExtents );
+}
+
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+void CellBlock::ExtractBounds( Bounds3f& orBounds )
+{
+ orBounds = mBounds;
+}
+
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+int CellBlock::CellIndex( Vector3f& irPoint )
+{
+ Vector3f index3f;
+ Vector3f AdjustedGranularities;
+
+ AdjustedGranularities.Mult( mGranularities, 1.0f+FloatFuncs::MinRemainder );
+
+ index3f.Sub( irPoint, mBlockCoord.mOffset);
+ index3f.Div( index3f, AdjustedGranularities );//mGranularities );
+
+ return CellIndex( (int)index3f[0], (int)index3f[1], (int)index3f[2] );
+// return( ((int)(index3f[0]) * mBlockCoord.mExtents[1] * mBlockCoord.mExtents[2]) +
+// ((int)(index3f[1]) * mBlockCoord.mExtents[2]) +
+// (int)(index3f[2]) );
+}
+
+Cell& CellBlock::GetCell( int iX, int iY, int iZ )
+{
+ return mpData[CellIndex(iX, iY, iZ)];
+}
+
+int CellBlock::CellIndex( int iX, int iY, int iZ )
+{
+ return( ((iX) * mBlockCoord.mExtents[1] * mBlockCoord.mExtents[2]) +
+ ((iY) * mBlockCoord.mExtents[2]) +
+ (iZ) );
+}
+/////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////
+void CellBlock::SetCellsBlockData()
+{
+ Bounds3f Bounds;
+ Vector3i Index;
+
+ for( Index.mX=0; Index.mX<mBlockCoord.mExtents[0]; Index.mX++ )
+ {
+ for( Index.mY=0; Index.mY<mBlockCoord.mExtents[1]; Index.mY++ )
+ {
+ for( Index.mZ=0; Index.mZ<mBlockCoord.mExtents[2]; Index.mZ++ )
+ {
+ Bounds.mMin.Set( mBlockCoord.mOffset.x + ((float)Index.mX)*mGranularities.x,
+ mBlockCoord.mOffset.y + ((float)Index.mY)*mGranularities.y,
+ mBlockCoord.mOffset.z + ((float)Index.mZ)*mGranularities.z );
+ Bounds.mMax.Set( Bounds.mMin.x+mGranularities.x,
+ Bounds.mMin.y+mGranularities.y,
+ Bounds.mMin.z+mGranularities.z );
+
+ GetCell(Index.mX,Index.mY,Index.mZ).SetBounds( Bounds );
+ GetCell(Index.mX,Index.mY,Index.mZ).SetBlockIndex( Index );
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/game/code/render/Culling/CellBlock.h b/game/code/render/Culling/CellBlock.h
new file mode 100644
index 0000000..9874f49
--- /dev/null
+++ b/game/code/render/Culling/CellBlock.h
@@ -0,0 +1,56 @@
+#ifndef __CELL_BLOCK_H__
+#define __CELL_BLOCK_H__
+
+#include <render/culling/Cell.h>
+#include <render/culling/FixedArray.h>
+#include <render/culling/BlockCoord.h>
+#include <render/culling/Vector3i.h>
+
+class CellBlock : public FixedArray<Cell>
+{
+public:
+ CellBlock();
+ ~CellBlock();
+
+ void Add( FixedArray<Vector3f>& irPoints );
+ void Add( Vector3f& irPoint );
+ void Add( FixedArray<Vector3f>& irPoints,
+ FixedArray<int>& irPointWeights);
+
+
+ /*
+ void Init( int iSize,
+ BlockCoord& irBlockCoord,
+ Vector3f& irGranularities );
+ */
+
+ void Init( FixedArray<Vector3f>& irPoints,
+ Vector3f& irGranularities );
+
+ void Init( FixedArray<Vector3f>& irPoints,
+ FixedArray<int>& irPointWeights,
+ Vector3f& irGranularities );
+
+ void GenerateCells();
+
+ int CellIndex( Vector3f& irPoint );
+ int CellIndex( int iX, int iY, int iZ );
+ Cell& GetCell( int iX, int iY, int iZ );
+
+ void Translate( FixedArray<Cell>& orCellArray, Vector3i& orSpans );
+ void TranslateNonEmpty( FixedArray<Cell>& orCellArray, Vector3i& orSpans );
+
+ void ExtractNonEmptyCells( FixedArray<Cell>& orCellArray );
+ void ExtractCells( FixedArray<Cell>& orCellArray );
+ void ExtractDims( Vector3i& orSpans );
+ void ExtractBounds( Bounds3f& orBounds );
+
+ Bounds3f mBounds;
+ BlockCoord mBlockCoord;
+ Vector3f mGranularities;
+protected:
+ void SetCellsBlockData();
+ int CountEmptyCells();
+ void BoundCellsByGranularities();
+};
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/ContiguousBinNode.h b/game/code/render/Culling/ContiguousBinNode.h
new file mode 100644
index 0000000..f25f1bc
--- /dev/null
+++ b/game/code/render/Culling/ContiguousBinNode.h
@@ -0,0 +1,313 @@
+#ifndef __CONTIGUOUS_BIN_NODE_H___
+#define __CONTIGUOUS_BIN_NODE_H___
+
+#include <raddebug.hpp>
+
+
+//////////////////////////////////////////////
+// These classes are defined in one file pair
+// because they're very tightly bound
+//////////////////////////////////////////////
+
+template <class T> class CBinNodeL;
+template <class T> class CBinNodeR;
+
+template <class T> class ContiguousBinNode
+{
+public:
+ ContiguousBinNode();
+ ContiguousBinNode( T& irData );
+ ~ContiguousBinNode();
+
+ ContiguousBinNode<T>* LChild();
+ ContiguousBinNode<T>* RChild();
+
+ int LChildOffset();
+ int RChildOffset();
+
+ //void LinkRChild( int iRChild );
+
+ int GetSubTreeSize(); //SubTreeSize is the number of nodes in the subtree not including the node called
+ void SetSubTreeSize( int iSubTreeSize );
+
+ bool IsRoot();
+
+ ContiguousBinNode<T>* Parent();
+ void LinkParent( int iParentOffset );
+
+ //Previously Asymetric
+ ContiguousBinNode<T>* LSibling();
+ ContiguousBinNode<T>* RSibling();
+ int RSiblingOffset();
+
+
+ enum
+ {
+ //msNoChildren = -1,
+ msNoChildren = 0,
+
+ msNoParent = 0
+ };
+
+ T mData;
+protected:
+ int mSubTreeSize;
+ int mParentOffset;
+
+};
+
+/*
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T> class CBinNodeL : public ContiguousBinNode<T>
+{
+public:
+ //Constuction
+ //Navigation Accessors
+ virtual CBinNodeR<T>* RSibling();
+ virtual int RSiblingOffset();
+};
+
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T> class CBinNodeR : public ContiguousBinNode<T>
+{
+public:
+ //Constuction
+ //Navigation Accessors
+ virtual CBinNodeL<T>* LSibling();
+};
+*/
+
+
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+#ifndef NULL
+#define NULL 0
+#endif
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+ContiguousBinNode<T>::ContiguousBinNode()
+{
+}
+
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+ContiguousBinNode<T>::ContiguousBinNode( T& irData )
+:
+ mData(irData)
+{
+}
+
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+ContiguousBinNode<T>::~ContiguousBinNode()
+{
+}
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+ContiguousBinNode<T>* ContiguousBinNode<T>::LChild()
+{
+ rAssert( mSubTreeSize != msNoChildren );
+ return (this+1);
+
+ /*
+ if( mSubTreeSize != msNoChildren )
+ return (this+1);
+ else
+ return NULL;
+ */
+}
+
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+ContiguousBinNode<T>* ContiguousBinNode<T>::RChild()
+{
+ rAssert( mSubTreeSize != msNoChildren );
+ return (this+1)->RSibling();
+/*
+ if( mSubTreeSize != msNoChildren )
+ return (this+1)->RSibling();
+ else
+ return NULL;
+*/
+}
+
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+int ContiguousBinNode<T>::LChildOffset()
+{
+ rAssert( mSubTreeSize != msNoChildren );
+ return 1;
+/*
+ if( mSubTreeSize != msNoChildren )
+ return (1);
+ else
+ return 0;
+*/
+}
+
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+int ContiguousBinNode<T>::RChildOffset()
+{
+ rAssert( mSubTreeSize != msNoChildren );
+ return LChild()->RSiblingOffset() + 1;
+/*
+ if( mSubTreeSize != msNoChildren )
+ // return ((CBinNodeL<T>*)(this+1))->RSiblingOffset() + 1;
+ return LChild()->RSiblingOffset() + 1;
+ else
+ return 0;
+*/
+}
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+bool ContiguousBinNode<T>::IsRoot()
+{
+ if( mParentOffset == msNoParent )
+ return true;
+ else
+ return false;
+}
+
+
+///////////////////////////////////////////////////////////////
+//SubTreeSize is the number of nodes in the subtree not including the node called
+///////////////////////////////////////////////////////////////
+template <class T>
+int ContiguousBinNode<T>::GetSubTreeSize()
+{
+ return mSubTreeSize;
+}
+
+///////////////////////////////////////////////////////////////
+//SubTreeSize is the number of nodes in the subtree not including the node called
+///////////////////////////////////////////////////////////////
+template <class T>
+void ContiguousBinNode<T>::SetSubTreeSize( int iSubTreeSize )
+{
+ mSubTreeSize = iSubTreeSize;
+}
+
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+void ContiguousBinNode<T>::LinkParent( int iParentOffset )
+{
+ mParentOffset = iParentOffset;
+}
+
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+ContiguousBinNode<T>* ContiguousBinNode<T>::Parent()
+{
+ return (this+mParentOffset);
+}
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+ContiguousBinNode<T>* ContiguousBinNode<T>::LSibling()
+{
+ return (this+mParentOffset)->LChild();
+}
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+ContiguousBinNode<T>* ContiguousBinNode<T>::RSibling()
+{
+ //
+ // Even unbalanced binary trees should have siblings
+ // Though there are exceptions, I'm not dealing with them right now
+ //
+ return (this+mSubTreeSize+1);
+}
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+int ContiguousBinNode<T>::RSiblingOffset()
+{
+ //
+ // Even unbalanced binary trees should have siblings
+ // Though there are exceptions, I'm not dealing with them right now
+ //
+ return mSubTreeSize+1;
+}
+
+/*
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+CBinNodeR<T>* ContiguousBinNode<T>::RSibling()
+{
+ rAssert(false);
+ return NULL;
+}
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+int ContiguousBinNode<T>::RSiblingOffset()
+{
+ rAssert(false);
+ return 0;
+}
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+CBinNodeL<T>* ContiguousBinNode<T>::LSibling()
+{
+ rAssert(false);
+ return NULL;
+}
+
+////////-------------CBinNodeR-------------////////////////////
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+CBinNodeL<T>* CBinNodeR<T>::LSibling()
+{
+ return (this+mParentOffset)->LChild();
+}
+
+
+////////-------------CBinNodeL-------------////////////////////
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+CBinNodeR<T>* CBinNodeL<T>::RSibling()
+{
+ //
+ // Even unbalanced binary trees should have siblings
+ // Though there are exceptions, I'm not dealing with them right now
+ //
+ return (CBinNodeR<T>*)(this+mSubTreeSize);
+}
+///////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////
+template <class T>
+int CBinNodeL<T>::RSiblingOffset()
+{
+ //
+ // Even unbalanced binary trees should have siblings
+ // Though there are exceptions, I'm not dealing with them right now
+ //
+ return mSubTreeSize;
+}
+
+*/
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/CoordSubList.cpp b/game/code/render/Culling/CoordSubList.cpp
new file mode 100644
index 0000000..64339e2
--- /dev/null
+++ b/game/code/render/Culling/CoordSubList.cpp
@@ -0,0 +1,640 @@
+#include <render/culling/CoordSubList.h>
+#include <render/culling/CullData.h>
+#include <raddebug.hpp>
+#include <render/culling/Vector3i.h>
+
+#include <memory/srrmemory.h>
+
+//
+// TODO - Replace this with a real variable setting
+//
+#define DEV_NUM_BINS 1024
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// External Functions
+//
+// Used in sorting process for centroids
+// Subtracting 1 is compensation for integral truncation in conversion
+///////////////////////////////////////////////////////////////////////////////////////////
+
+//
+// TODO - inline it!
+//
+inline int CompareCellN( Cell* ipCell1, Cell* ipCell2, int iIndex )
+{
+ //
+ // Is there anyway to preserve non-zero sign faster for wholly fractional differences?
+ // If not, you can always accept truncation loss
+ //
+ float Diff = ((Cell*)ipCell1)->Centroid(iIndex) - ((Cell*)ipCell2)->Centroid(iIndex);
+
+ if( Diff > 0.0f )
+ return 1;
+
+ if( Diff < 0.0f )
+ return -1;
+
+ return 0;
+
+//Lossy version
+// return (int)( ((Cell*)ipCell1)->Centroid(iIndex) - ((Cell*)ipCell2)->Centroid(iIndex) -1);
+//
+}
+
+int CompareCellXs( const void* ipCell1, const void* ipCell2)
+{
+ return CompareCellN( ((Cell*)ipCell1), ((Cell*)ipCell2), 0);
+}
+
+int CompareCellYs( const void* ipCell1, const void* ipCell2)
+{
+ return CompareCellN( ((Cell*)ipCell1), ((Cell*)ipCell2), 1);
+}
+
+int CompareCellZs( const void* ipCell1, const void* ipCell2)
+{
+ return CompareCellN( ((Cell*)ipCell1), ((Cell*)ipCell2), 2);
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////
+// Data List
+//
+// int mSize;
+// int mWeightedSize;
+// Cell* mpCellList;
+// Bounds3f mBounds;
+///////////////////////////////////////////////////////////////////////////////////////////
+
+CoordSubList::CoordSubList( Cell* ipCellList, int iSize, int iWeightedSize, Bounds3f& irBounds )
+{
+ mpCellList = ipCellList;
+ mSize = iSize;
+ mWeightedSize = iWeightedSize;
+
+ mSpans.SetToSpan( irBounds.mMin, irBounds.mMax );
+}
+
+CoordSubList::CoordSubList( Cell* ipCellList, int iSize, Vector3i iSpans )
+{
+ mpCellList = ipCellList;
+ mSize = iSize;
+ mSpans = iSpans;
+
+ GenWeightedSize();
+
+}
+
+CoordSubList::CoordSubList( Cell* ipCellList, int iSize )
+{
+ mpCellList = ipCellList;
+ mSize = iSize;
+
+ GenerateMembers();
+}
+
+
+CoordSubList::CoordSubList()
+{
+ mpCellList = NULL;
+ mSize = 0;
+}
+
+CoordSubList::~CoordSubList()
+{
+}
+
+
+
+//
+// Generate deterministic members
+//
+// WeightedSize
+// Spans
+//
+void CoordSubList::GenerateMembers()
+{
+ GenWeightedSize();
+ GenSpans();
+}
+
+void CoordSubList::GenWeightedSize()
+{
+ int i;
+ mWeightedSize = 0;
+
+ for( i=0; i<mSize; i++ )
+ {
+ mWeightedSize += mpCellList[i].RenderWeight();
+ }
+}
+
+void CoordSubList::GenSpans()
+{
+ int i;
+/*
+ Bounds3f tempBounds;
+
+ tempBounds.mMin.SetFP( mpCellList[0].Centroid() );
+ tempBounds.mMax.SetFP( mpCellList[0].Centroid() );
+
+ for( i=1; i<mSize; i++ )
+ {
+ tempBounds.Accumulate( mpCellList[i].Centroid() );
+ }
+ */
+
+ Bounds3i tempBounds;
+
+ tempBounds.mMin.Set( mpCellList[0].BlockIndex() );
+ tempBounds.mMax.Set( mpCellList[0].BlockIndex() );
+
+ for( i=1; i<mSize; i++ )
+ {
+ tempBounds.Accumulate( mpCellList[i].BlockIndex() );
+ }
+
+ mSpans.SetToSpan( tempBounds.mMin, tempBounds.mMax );
+}
+
+int CoordSubList::WeightedSize()
+{
+ return mWeightedSize;
+}
+
+int CoordSubList::Size()
+{
+ return mSize;
+}
+
+Cell& CoordSubList::Get( int i )
+{
+ rAssert( i>-1 && i<mSize );
+ return mpCellList[i];
+}
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+// This data structure should be able to split itself if any of the three axes contain
+// elements occupying separate bins (marked by their block index)
+///////////////////////////////////////////////////////////////////////////////////////////////
+bool CoordSubList::CanBeSplit()
+{
+ if( mSize > 1 )
+ {
+ for( int iAxis=0; iAxis<3; iAxis++ )
+ {
+ for( int i=1; i<mSize; i++ )
+ {
+ if( mpCellList[i].BlockIndex(iAxis) != mpCellList[0].BlockIndex(iAxis) )
+ return true;
+ }
+ }
+ }
+ return false;
+}
+//
+// SubDivide the List
+//
+// Divide List by a weighting value
+//
+void CoordSubList::SubDivideHeuristic
+(
+ Vector3f& irGranularity,
+ CoordSubList** opList1,
+ CoordSubList** opList2,
+ int& iorAxis,
+ float& orPlanePosn
+)
+{
+MEMTRACK_PUSH_GROUP( "CoortSubList" );
+
+ rAssert( mpCellList );
+ rAssert( *opList1 == NULL );
+ rAssert( *opList1 == NULL );
+
+ /* if( iorAxis == -1 )
+ {
+ iorAxis = ChooseAxis();
+ }
+*/
+ //
+ // Accumulate the WeightCount as the list is sorted by median,
+ // where each point of Cell weight accounts for an item
+ //
+ //
+ // Currently this algorithm is inefficient, as it re-sorts the data
+ // Each iteration. But this is just a test for an offline tool.
+ //
+ /* switch( iorAxis )
+ {
+ case 0:
+ qsort( mpCellList, mSize, sizeof(Cell), CompareCellXs );
+ break;
+ case 1:
+ qsort( mpCellList, mSize, sizeof(Cell), CompareCellYs );
+ break;
+ case 2:
+ qsort( mpCellList, mSize, sizeof(Cell), CompareCellZs );
+ break;
+ default:
+ rAssert(false);
+ break;
+ }
+*/
+ //
+ // Set Plane Posn
+ //
+// int List1Size = GetWeightedMedian( orPlanePosn, iorAxis );
+ int List1Size = FindSplitIndex( iorAxis, 0 );
+
+ //Debug
+ if( List1Size >= mSize || List1Size <= 0 )
+ {
+ bool fuckedUp = true;
+ iorAxis = -1;
+ List1Size = FindSplitIndex( iorAxis, 0 );
+ }
+
+
+ SeekMinPlane( orPlanePosn, List1Size, iorAxis );
+
+ *opList1 = new(GMA_TEMP) CoordSubList( mpCellList, List1Size);
+ *opList2 = new(GMA_TEMP) CoordSubList( &(mpCellList[List1Size]), mSize - List1Size);
+MEMTRACK_POP_GROUP("CoortSubList");
+
+}
+
+//
+// SubDivide the List
+//
+void CoordSubList::SubDivideUniform( int iAxis, CoordSubList** opList1, CoordSubList** opList2 )
+{
+ rAssert( mpCellList );
+ rAssert( false ); // not implemented
+ if( iAxis == -1 )
+ {
+ iAxis = ChooseAxis();
+ }
+}
+
+
+//
+// TODO -rename these GetWeightedMedian's to reflect what the second one does; distinguish
+//
+int CoordSubList::GetWeightedMedian( int iAxis )
+{
+ int MedianWeightCount = mWeightedSize / 2;
+ int WeightCount = 0;
+ int i=-1;
+
+// for( i = 0; (i < mSize) && (WeightCount < MedianWeightCount); i++ )
+// {
+// WeightCount += mpCellList[i].RenderWeight();
+// }
+ do
+ {
+ i++;
+ WeightCount += mpCellList[i].RenderWeight();
+ } while( (i<mSize-1) && (WeightCount<MedianWeightCount) );
+
+ //
+ // If we've surpassed the median, step back,
+ // undercut the median to handle the 2 element cases.
+ //
+ if( WeightCount > MedianWeightCount )
+ {
+ if(i-1>=0)
+ {
+ i--;
+ }
+ }
+
+ return i;
+}
+
+int CoordSubList::GetWeightedMedian( float& orPlanePosn, int iAxis )
+{
+ int i = GetWeightedMedian( iAxis );
+ return GetPlanarDivision( orPlanePosn, iAxis, i );
+}
+
+int CoordSubList::GetPlanarDivision( float& orPlanePosn, int iAxis, int iIndex )
+{
+ int DivisionIndex = mpCellList[iIndex].BlockIndex(iAxis);
+ orPlanePosn = mpCellList[iIndex].MaxPlane(iAxis);
+
+ for( iIndex++; (iIndex<mSize) && (mpCellList[iIndex].BlockIndex(iAxis) <= DivisionIndex); iIndex++ ){}
+ return iIndex;
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+// Returns: The index in the SubList which marks the beginning of the second list
+//
+// Method:
+// Find the best axis/index to split the data along.
+// Best is defn'd as the one which can be split most closely to half (dichotomy heuristic)
+// Then do an in-place GT/LT median sort, returning the index of the start of the GT list
+///////////////////////////////////////////////////////////////////////////////////////////////
+int CoordSubList::FindSplitIndex( int& ioAxis, int iDeviationThreshold )
+{
+ Vector3i MedianDeviation, MedianBin;
+
+ if( ioAxis == SRR_ERR_INDEX )
+ {
+ for( ioAxis=0; ioAxis<3; ioAxis++ )
+ {
+ if( mSpans[ioAxis] > 0 )
+ {
+ BinCount( MedianDeviation[ioAxis], MedianBin[ioAxis], ioAxis );
+
+ if( MedianDeviation[ioAxis] <= iDeviationThreshold )
+ {
+ return BinSort( MedianBin[ioAxis], ioAxis );
+ }
+ }
+ else
+ {
+ //Don't consider this axis
+ MedianDeviation[ioAxis] = mWeightedSize;
+ MedianBin[ioAxis] = mSize+1;
+ }
+ }
+ /*
+ BinCount( MedianDeviation.mY, MedianBin.mY, 1 );
+ if( MedianDeviation.mY < iDeviationThreshold )
+ {
+ return BinSort( MedianBin.mY, 1 );
+ }
+
+ BinCount( MedianDeviation.mZ, MedianBin.mZ, 2 );
+ if( MedianDeviation.mZ < iDeviationThreshold )
+ {
+ return BinSort( MedianBin.mZ, 2 );
+ }
+ */
+ ioAxis = MedianDeviation.MinIndex();
+ return BinSort( MedianBin[ioAxis], ioAxis );
+ }
+ else
+ {
+ BinCount( MedianDeviation[ioAxis], MedianBin[ioAxis], ioAxis );
+ return BinSort( MedianBin[ioAxis], ioAxis );
+ }
+}
+///////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////
+void CoordSubList::BinCount( int& oMedianDeviation, int& oMedianBin, int iAxis )
+{
+ int i, Count=0, nNonEmptyBins=0;
+ SubArray<int> Bins;
+ theCullCache().Acquire( Bins, DEV_NUM_BINS );
+
+ Bins.SetAll(Count);
+
+ //Get Total, Bin Count List
+ for( i=0; i<mSize; i++ )
+ {
+ Bins[ mpCellList[i].BlockIndex(iAxis) ] += mpCellList[i].RenderWeight();
+ Count += mpCellList[i].RenderWeight();
+ }
+
+ //Find Median & Deviation
+ Count = Count/2;
+
+ for( i=0; (i<Bins.mSize)&&(Count>0); i++ )
+ {
+ Count -= Bins[i];
+ }
+
+ i--;
+
+ //Find the best approximate median
+ if( Count < Bins[i]/-2 )
+ {
+ Count += Bins[i];
+ oMedianBin = i;
+ }
+ else
+ {
+ Count = rmt::Abs( Count );
+ oMedianBin = i+1;
+ }
+
+ oMedianDeviation = Count;
+
+ theCullCache().Release( Bins );
+}
+
+///////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////
+int CoordSubList::BinSort( int iMedianBin, int iAxis )
+{
+ int RightIndex = mSize-1;
+ Cell temp;
+ Cell* left = &(mpCellList[0]);
+ Cell* right = &(mpCellList[RightIndex]);
+
+ while( left!=right )
+ {
+ if( left->BlockIndex(iAxis) < iMedianBin )
+ {
+ left++;
+ }
+ else
+ {
+ //left > MedianBin;
+ //left needs to be swapped with a right < Median
+ if( right->BlockIndex(iAxis) < iMedianBin )
+ {
+ temp = *right;
+ *right = *left;
+ *left = temp;
+
+ left++;
+ if( left!=right )
+ {
+ right--;
+ RightIndex--;
+ }
+ }
+ else
+ {
+ right--;
+ RightIndex--;
+/* //In the case where they overlap, ensure right ends on GT
+ if( left == right )
+ {
+ if( right->BlockIndex(iAxis) < iMedianBin )
+ {
+ RightIndex++;
+ }
+ }
+ */
+ }
+ }
+ }
+ //Debug
+ if( right->BlockIndex(iAxis) < iMedianBin )
+ {
+ rAssert( (right+1)->BlockIndex(iAxis) >= iMedianBin );
+ RightIndex++;
+ }
+
+ float minPlane;
+ SeekMinPlane( minPlane, RightIndex, iAxis );
+
+ return RightIndex;
+}
+///////////////////////////////////////////////////////////////////////////////////////////////
+///////////////////////////////////////////////////////////////////////////////////////////////
+
+void CoordSubList::SeekMinPlane( float& oPlanePosn, int i, int iAxis )
+{
+ //Debug
+ float LTMaxPlane;
+ SeekMaxPlane( LTMaxPlane, 0, i, iAxis);
+ //Debug
+
+ oPlanePosn = mpCellList[i].MinPlane(iAxis);
+ for( i++; i<mSize; i++ )
+ {
+ rAssert( mpCellList[i].MinPlane(iAxis) < mpCellList[i].MaxPlane(iAxis) );
+ if( mpCellList[i].MinPlane(iAxis) < oPlanePosn )
+ {
+ oPlanePosn = mpCellList[i].MinPlane(iAxis);
+ }
+ }
+
+ //Debug
+ rAssert( LTMaxPlane <= oPlanePosn );
+}
+
+void CoordSubList::SeekMaxPlane( float& oPlanePosn, int iCur, int iEnd, int iAxis )
+{
+ oPlanePosn = mpCellList[iCur].MaxPlane(iAxis);
+ for( iCur++; iCur<iEnd; iCur++ )
+ {
+ rAssert( mpCellList[iCur].MinPlane(iAxis) < mpCellList[iCur].MaxPlane(iAxis) );
+
+ if( mpCellList[iCur].MaxPlane(iAxis) > oPlanePosn )
+ {
+ oPlanePosn = mpCellList[iCur].MaxPlane(iAxis);
+ }
+ }
+}
+/*
+int CoordSubList::GetPlanarDivision( float& orPlanePosn, int iAxis, int iIndex )
+{
+ orPlanePosn = mpCellList[iIndex].MaxPlane(iAxis);
+
+ for( iIndex++; (iIndex<mSize) && (mpCellList[iIndex].MaxPlane(iAxis) <= orPlanePosn); iIndex++ ){}
+ return iIndex;
+}
+*/
+/*
+int CoordSubList::GetPlanarDivision( int iIndex, int iAxis, Vector3f irGranularity )
+{
+ float UpperBound = GetCellUpperBound( mpCellList[iIndex].Centroid(iAxis), irGranularity[iAxis] );
+
+ for( ; (iIndex < mSize) && (mpCellList[iIndex].Centroid(iAxis) < UpperBound); iIndex++ ){}
+
+ return iIndex;
+}
+
+float CoordSubList::GetCellUpperBound( float iCoord, float iGranularity )
+{
+ return (sTruncate(iCoord / iGranularity) + 1.0f) * iGranularity;
+}
+*/
+
+/*int CoordSubList::GetWeightedMedian( int iAxis, int* opLowerWeightCount, tBox3D* opBounds )
+{
+ int MedianWeightCount = mWeightedSize / 2;
+ int WeightCount = 0;
+ int i;
+
+ for( i = 0; (i < mSize) && (WeightCount < MedianWeightCount); i++ )
+ {
+ WeightCount += mpCellList[i].RenderWeight();
+ }
+ return i;
+}*/
+
+
+
+
+int CoordSubList::ChooseAxis()
+{
+ // int ChosenAxis;
+
+ //
+ // The idea is to choose the axis that needs
+ // subdivision the most.
+ //
+ // Key Properties:
+ // Count: same in each dimension
+ // Span: calc'd on mBounds
+ // Spread: may want to ensure that the evenly spread data gets divided
+ //
+ /*float Spans[3];
+ Spans[0] = mBounds.mMax[0] - mBounds.mMin[0];
+ Spans[1] = mBounds.mMax[1] - mBounds.mMin[1];
+ Spans[2] = mBounds.mMax[2] - mBounds.mMin[2];
+*/
+ return mSpans.MaxIndex();
+/*
+ if( mSpans[0] >= mSpans[1] )
+ {
+ if( mSpans[0] >= mSpans[2] )
+ {
+ return 0;
+ }
+ else
+ {
+ return 2;
+ }
+ }
+ else
+ {
+ if( mSpans[1] >= mSpans[2] )
+ {
+ return 1;
+ }
+ else
+ {
+ return 2;
+ }
+ }
+ */
+}
+
+/*
+char CoordSubList::BestSpreadAxis()
+{
+ int i, i_plane, weightCount, MedianWeight = mWeightedSize/2;
+ Vector3i LTCounts, GTCounts;
+
+ qsort( mpCellList, mSize, sizeof(Cell), CompareCellXs );
+ for( i=0, weightCount=0; (i<mSize)&&(weightCount<MedianWeight); i++ )
+ {
+ weightCount += mpCellList.mpData[i].mRenderWeight();
+ }
+ //
+ // If we've surpassed the median, step back,
+ // undercut the median to handle the 2 element cases.
+ //
+ if( weightCount > MedianWeight )
+ {
+ if(i-1>=0)
+ {
+ i--;
+ }
+ }
+ i_plane = GetPlanarDivision( junkedPlane, 0, i );
+
+ qsort( mpCellList, mSize, sizeof(Cell), CompareCellYs );
+ qsort( mpCellList, mSize, sizeof(Cell), CompareCellZs );
+ mpCellList.mpData[i].
+}
+*/ \ No newline at end of file
diff --git a/game/code/render/Culling/CoordSubList.h b/game/code/render/Culling/CoordSubList.h
new file mode 100644
index 0000000..fb4fa9b
--- /dev/null
+++ b/game/code/render/Culling/CoordSubList.h
@@ -0,0 +1,96 @@
+#ifndef __COORD_SUB_LIST_H__
+#define __COORD_SUB_LIST_H__
+
+#include <render/culling/OctTreeConst.h>
+#include <render/culling/Bounds.h>
+#include <render/culling/Cell.h>
+#include <render/culling/Vector3i.h>
+
+//
+// CoordSubList
+//
+// Contains:
+// -An mSize list of points in mpVectorList
+// which reaches extents of mBounds
+//
+// Responsible for:
+// -Subdividing a list of points
+// -NOT responsisble for the data mpVectorList as it is expected that
+// other SubLists will reference the same data
+//
+class CoordSubList
+{
+public:
+ ~CoordSubList();
+ CoordSubList( Cell* ipCellList, int iSize, int iWeightedSize, Bounds3f& irBounds );
+ CoordSubList( Cell* ipCellList, int iSize, Vector3i iSpans );
+ CoordSubList( Cell* ipCellList, int iSize );
+
+ //
+ // SubDivide the List in-place
+ //
+ void SubDivideHeuristic( Vector3f& irGranularity,
+ CoordSubList** opList1,
+ CoordSubList** opList2,
+ int& iorAxis,
+ float& orPlanePosn );
+ bool CanBeSplit();
+
+ //
+ // SubDivide the List in-place
+ //
+ void SubDivideUniform( int iAxis, CoordSubList** opList1, CoordSubList** opList2 );
+
+ int WeightedSize();
+ int Size();
+
+ Cell& Get( int i );
+protected:
+ //////////////////////////////////////////////////////////////////
+ // Class Data
+ //////////////////////////////////////////////////////////////////
+ static const char smAxis0 = 1;
+ static const char smAxis1 = 1 << 1;
+ static const char smAxis2 = 1 << 2;
+
+ //////////////////////////////////////////////////////////////////
+ // Data
+ //////////////////////////////////////////////////////////////////
+ int mSize;
+ int mWeightedSize;
+ Cell* mpCellList;
+ Vector3i mSpans;
+
+
+ //////////////////////////////////////////////////////////////////
+ // Methods
+ //////////////////////////////////////////////////////////////////
+// CoordSubList( Cell* ipCellList, int iSize );
+ CoordSubList();
+
+ int ChooseAxis();
+ char BestSpreadAxis();
+
+
+ int GetWeightedMedian( int iAxis );
+ int GetWeightedMedian( float& orPlanePosn, int iAxis );
+ int GetPlanarDivision( float& orPlanePosn, int iAxis, int iIndex );
+
+ float GetCellUpperBound( float iCoord, float iGranularity );
+ // int GetWeightedMedian( int iAxis, int* opLowerWeightCount, tBox3D* opBounds );
+
+ int FindSplitIndex( int& ioAxis, int iDeviationThreshold );
+ void BinCount( int& oMedianDeviation, int& oMedian, int iAxis );
+ int BinSort( int oMedian, int iAxis );
+ void SeekMinPlane( float& oPlanePosn, int i, int iAxis );
+ void SeekMaxPlane( float& oPlanePosn, int iCur, int iEnd, int iAxis );
+
+ //
+ // Generate deterministic members
+ //
+ void GenerateMembers();
+ void GenWeightedSize();
+ void GenSpans();
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/CullData.cpp b/game/code/render/Culling/CullData.cpp
new file mode 100644
index 0000000..7614132
--- /dev/null
+++ b/game/code/render/Culling/CullData.cpp
@@ -0,0 +1,12 @@
+#include <render/culling/CullData.h>
+
+CullCache& theCullCache()
+{
+ //
+ // This will eventually house the ScratchArray implementation.
+ // In the meantime, we'll use a placeholder
+ //
+ static CullCache tcc;
+
+ return tcc;
+}
diff --git a/game/code/render/Culling/CullData.h b/game/code/render/Culling/CullData.h
new file mode 100644
index 0000000..5437213
--- /dev/null
+++ b/game/code/render/Culling/CullData.h
@@ -0,0 +1,46 @@
+#ifndef __CULL_DATA_H__
+#define __CULL_DATA_H__
+
+#include <render/culling/ScratchArray.h>
+
+class CullCache
+{
+public:
+ CullCache()
+ {
+ mIntArray.Allocate( 1024 );
+ InUse = false;
+ }
+ ~CullCache(){}
+
+ bool Acquire( SubArray<int>& orSubArray, int iSize )
+ {
+ if( !InUse )
+ {
+ rAssert( iSize <= mIntArray.mSize );
+ orSubArray.Init( iSize, mIntArray.mpData );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ void Release( SubArray<int>& orSubArray )
+ {
+ // This will be replaced by a scratch array soon
+ orSubArray.Invalidate();
+ }
+
+protected:
+
+ // This will be replaced by a scratch array soon
+ FixedArray<int> mIntArray;
+ bool InUse;
+
+};
+
+CullCache& theCullCache();
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/FixedArray.h b/game/code/render/Culling/FixedArray.h
new file mode 100644
index 0000000..066deb1
--- /dev/null
+++ b/game/code/render/Culling/FixedArray.h
@@ -0,0 +1,96 @@
+#ifndef __FIXED_ARRAY_H__
+#define __FIXED_ARRAY_H__
+
+#include <p3d/p3dtypes.hpp>
+#include <raddebug.hpp>
+#include <memory/srrmemory.h>
+
+template <class T> class FixedArray
+{
+public:
+
+ /////////////////////////////////////////
+ // Constructors/Destructors
+ /////////////////////////////////////////
+ ~FixedArray()
+ {
+ Clear();
+ }
+
+ FixedArray() : mpData(NULL)
+ {
+ }
+
+ FixedArray( int iSize ) : mpData(NULL)
+ {
+ Allocate( iSize );
+ }
+
+
+ /////////////////////////////////////////
+ // Main Methods
+ /////////////////////////////////////////
+ T& operator[]( int iIndex )
+ {
+ rAssert( (iIndex < mSize) && (iIndex > -1) );
+ return mpData[iIndex];
+ }
+
+ void Allocate( int iSize )
+ {
+ Clear();
+ mSize = iSize;
+
+#ifdef RAD_GAMECUBE
+ //HeapMgr()->PushHeap( GMA_GC_VMM );
+#endif
+
+ mpData = new T[mSize];
+
+#ifdef RAD_GAMECUBE
+ //HeapMgr()->PopHeap( GMA_GC_VMM );
+#endif
+
+ rAssert(mSize>0);
+ rAssert(mpData!=NULL);
+ }
+
+ void Clear()
+ {
+ if( mpData != NULL )
+ {
+ delete[] mpData;
+ mpData = NULL;
+ }
+ }
+
+ bool IsSetUp()
+ {
+ if( mpData == NULL )
+ return false;
+ else
+ return true;
+ }
+
+ /////////////////////////////////////////
+ // Data
+ /////////////////////////////////////////
+ int mSize;
+ T* mpData;
+
+protected:
+
+private:
+ /////////////////////////////////////////
+ // Currently Disallowed Functions
+ // -Not implemented
+ // -Not accessible
+ // -Use will error at Compile
+ /////////////////////////////////////////
+ FixedArray( const FixedArray& iSource );
+ FixedArray& operator=( const FixedArray& iSource );
+
+};
+
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/FloatFuncs.h b/game/code/render/Culling/FloatFuncs.h
new file mode 100644
index 0000000..57c64c1
--- /dev/null
+++ b/game/code/render/Culling/FloatFuncs.h
@@ -0,0 +1,40 @@
+#ifndef __FLOAT_FUNCS_H__
+#define __FLOAT_FUNCS_H__
+
+namespace FloatFuncs
+{
+ const float MaxRemainder = 0.999999f;
+ const float MinRemainder = 0.000001f;
+}
+
+//
+// TODO - Some Functions won't work in the negative domain for now
+//
+extern "C"
+{
+ inline float sRoundUp( float iFloat )
+ {
+ return iFloat + FloatFuncs::MaxRemainder;
+ }
+
+ inline int sUpperInt( float iFloat )
+ {
+ return (int)(sRoundUp(iFloat));
+ }
+
+ inline float sRoundDown( float iFloat )
+ {
+ return iFloat - FloatFuncs::MaxRemainder;
+ }
+
+ inline int sLowerInt( float iFloat )
+ {
+ return (int)(iFloat);
+ }
+
+ inline float sTruncate( float iFloat )
+ {
+ return (float) ((int)iFloat);
+ }
+}
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/HexahedronP.cpp b/game/code/render/Culling/HexahedronP.cpp
new file mode 100644
index 0000000..5a8fd86
--- /dev/null
+++ b/game/code/render/Culling/HexahedronP.cpp
@@ -0,0 +1,142 @@
+#include <render/culling/HexahedronP.h>
+
+HexahedronP::HexahedronP()
+{
+}
+
+HexahedronP::~HexahedronP()
+{
+}
+
+// ISpatialProxyAA
+// <0.0 - Less than comparison object in posn
+// =0.0 - Intersection with comparison object
+// >0.0 - Greater than comparison object in posn
+
+float HexahedronP::CompareTo( AAPlane3f& irPlane )
+{
+ rAssert(false);
+ return 0.0f;
+}
+
+bool HexahedronP::DoesIntersect( AAPlane3f& irClipPlane )
+{
+ rAssert(false);
+ return false;
+}
+
+bool HexahedronP::DoesntIntersect( AAPlane3f& irClipPlane )
+{
+ rAssert(false);
+ return false;
+}
+
+// ISpatialProxyAA
+// <0.0 - Inside Spatial Proxy
+// =0.0 - On Spatial Proxy Surface
+// >0.0 - Outside Spatial Proxy
+
+float HexahedronP::CompareTo( const Vector3f& irPoint )
+{
+ for( int i = 0; i < 6; i++ )
+ {
+ if( mPlanes[i].mNorm.x * irPoint.x +
+ mPlanes[i].mNorm.y * irPoint.y +
+ mPlanes[i].mNorm.z * irPoint.z -
+ mPlanes[i].mOffset < 0.0f )
+ {
+ return 1.0f; //Outside
+ }
+ }
+ return -1.0f; //Inside
+}
+
+
+float HexahedronP::TestNotOutside( ISpatialProxyAA& irSpatialProxy )
+{
+ for( int i=irSpatialProxy.nPts()-1; i>-1; i-- )
+ {
+ if( CompareTo(irSpatialProxy.mPt(i)) < 0.0f )
+ {
+ //Inside
+ return -1.0f;
+ }
+ }
+ //Outside
+ return 1.0f;
+}
+
+int HexahedronP::nPts()
+{
+ return msPtCount;
+}
+
+Vector3f HexahedronP::mPt( int iIndex )
+{
+ return mPoints[iIndex];
+}
+
+NormPlane3f& HexahedronP::mPlane( int inPlane )
+{
+ return mPlanes[inPlane];
+}
+
+void HexahedronP::SetPlane( int inPlane, float iX, float iY, float iZ, float iD )
+{
+ mPlanes[inPlane].mNorm.x = iX;
+ mPlanes[inPlane].mNorm.y = iY;
+ mPlanes[inPlane].mNorm.z = iZ;
+ mPlanes[inPlane].mOffset = iD;
+//TODO: I'm pretty sure just the sign matters for our tests, check the validity of this,
+ // mPlanes[inPlane].Normalise();
+}
+
+//TODO: Currently I only care whether *any* point is within the bounding box of a tree
+// So, later, I can optimise this to just generate one point, and test vs that one
+void HexahedronP::GeneratePoints()
+{
+ GeneratePoint( 0, msFront, msRight, msTop );
+ GeneratePoint( 1, msFront, msLeft, msTop );
+ GeneratePoint( 2, msFront, msRight, msBottom );
+ GeneratePoint( 3, msFront, msLeft, msBottom );
+ GeneratePoint( 4, msBack, msRight, msTop );
+ GeneratePoint( 5, msBack, msLeft, msTop );
+ GeneratePoint( 6, msBack, msRight, msBottom );
+ GeneratePoint( 7, msBack, msLeft, msBottom );
+}
+
+// - d1 ( N2 x N3 ) - d2 ( N3 x N1 ) - d3 ( N1 x N2 )
+//P = -------------------------------------------------------------------------
+// N1 . ( N2 x N3 )
+void HexahedronP::GeneratePoint( int inPoint, int inPlane1, int inPlane2, int inPlane3 )
+{
+ Vector3f tmp, tmp2;
+ float div;
+
+ tmp.CrossProduct( mPlanes[inPlane2].mNorm, mPlanes[inPlane3].mNorm );
+ tmp.Mult( -1.0f * mPlanes[inPlane1].mOffset );
+
+ tmp2.CrossProduct( mPlanes[inPlane3].mNorm, mPlanes[inPlane1].mNorm );
+ tmp2.Mult( -1.0f * mPlanes[inPlane2].mOffset );
+
+ tmp.Add( tmp2 );
+
+ tmp2.CrossProduct( mPlanes[inPlane1].mNorm, mPlanes[inPlane2].mNorm );
+ tmp2.Mult( -1.0f * mPlanes[inPlane3].mOffset );
+
+ tmp.Add( tmp2 );
+
+ tmp2.CrossProduct( mPlanes[inPlane2].mNorm, mPlanes[inPlane3].mNorm );
+ div = mPlanes[inPlane1].mNorm.DotProduct( tmp2 );
+
+ rAssert( div != 0.0f );
+
+ tmp.Div( tmp, div );
+ mPoints[inPoint] = tmp;
+}
+
+
+Vector3f HexahedronP::GetPoint()
+{
+ return mPoints[0];
+}
diff --git a/game/code/render/Culling/HexahedronP.h b/game/code/render/Culling/HexahedronP.h
new file mode 100644
index 0000000..f70059f
--- /dev/null
+++ b/game/code/render/Culling/HexahedronP.h
@@ -0,0 +1,65 @@
+#ifndef __HEXAHEDRON_P_H___
+#define __HEXAHEDRON_P_H___
+
+#include <render/culling/ISpatialProxy.h>
+
+class HexahedronP : public ISpatialProxyAA
+{
+public:
+ HexahedronP();
+ ~HexahedronP();
+
+ /////////////////////////////////////////////////////////////////////
+ //------------------ISpatialProxyAA methods------------------------//
+ // <0.0 - Less than comparison object in posn
+ // =0.0 - Intersection with comparison object
+ // >0.0 - Greater than comparison object in posn
+ virtual float CompareTo( AAPlane3f& irPlane );
+ virtual bool DoesIntersect( AAPlane3f& irClipPlane );
+ virtual bool DoesntIntersect( AAPlane3f& irClipPlane );
+
+ virtual float CompareTo( const Vector3f& irPoint );
+
+ virtual float TestNotOutside( ISpatialProxyAA& irSpatialProxy );
+ virtual int nPts();
+ virtual Vector3f mPt( int iIndex );
+
+
+ /////////////////////////////////////////////////////////////////////
+ //----------------HexahedronP methods------------------------------//
+ void SetPlane( int inPlane, float iX, float iY, float iZ, float iD );
+
+ NormPlane3f& mPlane( int inPlane );
+
+ //
+ // Generate all the points,
+ // where the planes intersect
+ //
+ void GeneratePoints();
+
+ virtual Vector3f GetPoint();
+
+ enum
+ {
+ msFront,
+ msBack,
+ msTop,
+ msBottom,
+ msRight,
+ msLeft
+ };
+
+ enum
+ {
+ msPtCount = 8,
+ msEdgeCount = 12,
+ msSideCount = 6
+ };
+protected:
+ NormPlane3f mPlanes[msSideCount];
+ Vector3f mPoints[msPtCount];
+
+ void GeneratePoint( int inPoint, int inPlane1, int inPlane2, int inPlane3 );
+
+};
+#endif
diff --git a/game/code/render/Culling/ISpatialProxy.cpp b/game/code/render/Culling/ISpatialProxy.cpp
new file mode 100644
index 0000000..9704eac
--- /dev/null
+++ b/game/code/render/Culling/ISpatialProxy.cpp
@@ -0,0 +1,4 @@
+#include <render/culling/ISpatialProxy.h>
+
+float ISpatialProxyAA::msIntersectionEpsilon = 0.01f;
+
diff --git a/game/code/render/Culling/ISpatialProxy.h b/game/code/render/Culling/ISpatialProxy.h
new file mode 100644
index 0000000..4938367
--- /dev/null
+++ b/game/code/render/Culling/ISpatialProxy.h
@@ -0,0 +1,191 @@
+#ifndef __I_SPATIAL_PROXY_H__
+#define __I_SPATIAL_PROXY_H__
+
+#include <render/culling/Plane3f.h>
+
+
+class ISpatialProxyAA
+{
+public:
+ ISpatialProxyAA(){}
+ virtual ~ISpatialProxyAA() {}
+
+ //
+ // Implment this!
+ //
+ // Return values for all Comparison functions:
+ //
+ // This SpatialProxy is...
+ // <0.0 - Less than comparison object in posn
+ // =0.0 - Intersection with comparison object
+ // >0.0 - Greater than comparison object in posn
+ //
+ // For normalized references, normal side is Greater, inverse is lesser
+ // For axis aligned refs, the Origin is the smallest value
+ // For axis aligned normalized refs, normal takes precedence
+ //
+ virtual float CompareTo( AAPlane3f& irPlane ) = 0;
+
+ // ISpatialProxyAA
+ // <0.0 - Inside Spatial Proxy
+ // =0.0 - On Spatial Proxy Surface
+ // >0.0 - Outside Spatial Proxy
+ virtual float CompareTo( const Vector3f& irPoint ) =0;
+ virtual float CompareToXZ( const Vector3f& irPoint ) =0;
+
+ virtual float TestNotOutside( ISpatialProxyAA& irSpatialProxy ) =0;
+
+ virtual int nPts() =0;
+ virtual Vector3f mPt( int iIndex ) =0;
+
+
+ virtual bool DoesIntersect( AAPlane3f& irClipPlane ) =0;
+ virtual bool DoesntIntersect( AAPlane3f& irClipPlane ) =0;
+
+ //Get a point representing some point within the SpatialProxy
+ virtual Vector3f GetPoint() =0;
+
+protected:
+ static float msIntersectionEpsilon;
+};
+
+
+class IValidity
+{
+public:
+ IValidity(){}
+ virtual ~IValidity(){}
+
+ virtual bool IsValid() =0;
+ virtual bool IsInvalid() =0;
+ virtual void Invalidate() =0;
+
+protected:
+};
+
+template <class T>
+class IMutableSP : public ISpatialProxyAA,
+ public IValidity
+{
+public:
+ IMutableSP(){}
+ virtual ~IMutableSP() {}
+
+ //
+ // Implment this!
+ //
+ // Return values for all Comparison functions:
+ //
+ // <0.0 - Less than comparison object in posn
+ // =0.0 - Intersection with comparison object
+ // >0.0 - Greater than comparison object in posn
+ //
+ // For normalized references, normal side is Greater, inverse is lesser
+ // For axis aligned refs, the Origin is the smallest value
+ // For axis aligned normalized refs, normal takes precedence
+ //
+ //virtual float CompareTo( AAPlane3f& irPlane ) = 0;
+ //virtual float CompareTo( AANormPlane3f& irPlane ) = 0;
+
+ //virtual void KeepLT( AAPlane3f& irClipPlane ) =0;
+ //virtual void KeepGT( AAPlane3f& irClipPlane ) =0;
+
+ //virtual void ClipOffLT( AAPlane3f& irClipPlane ) =0;
+ //virtual void ClipOffGT( AAPlane3f& irClipPlane ) =0;
+
+ virtual void ClipOffLT( IMutableSP<T>& irSpatialProxyLT, AAPlane3f& irClipPlane ) =0;
+ virtual void ClipOffGT( IMutableSP<T>& irSpatialProxyGT, AAPlane3f& irClipPlane ) =0;
+
+ virtual T* pT() =0;
+
+protected:
+};
+
+/*
+template <class T>
+class ISpatialProxy : public ISpatialProxyAA<T>
+{
+public:
+ ISpatialProxy(){}
+ virtual ~ISpatialProxy() {}
+
+ //
+ // Implment this!
+ //
+ // Return values for all Comparison functions:
+ //
+ // <0.0 - Less than comparison object in posn
+ // =0.0 - On/Inside comparison object
+ // >0.0 - Greater than comparison object in posn
+ //
+ // For normalized references, normal side is Greater, inverse is lesser
+ // For axis aligned refs, the Origin is the smallest value
+ // For axis aligned normalized refs, normal takes precedence
+ //
+ virtual float CompareTo( Plane3f& irPlane ) = 0;
+protected:
+};
+*/
+/*
+class PointSP : public Vector3f, public ISpatialProxy
+{
+ PointSP(){}
+ ~PointSP(){}
+
+ virtual float CompareTo( AAPlane3f& irPlane )
+ {
+ switch(irPlane.mAxis )
+ {
+ case 0:
+ return irPlane.mPosn - x;
+ break;
+ case 1:
+ return irPlane.mPosn - y;
+ break;
+ case 2:
+ return irPlane.mPosn - z;
+ break;
+ }
+ }
+
+ virtual float CompareTo( AANormPlane3f& irPlane )
+ {
+ if( irPlane.mNorm == 0 )
+ {
+ switch(irPlane.mAxis )
+ {
+ case 0:
+ return x - irPlane.mPosn;
+ break;
+ case 1:
+ return y - irPlane.mPosn;
+ break;
+ case 2:
+ return z - irPlane.mPosn;
+ break;
+ }
+ }
+ else
+ {
+ switch(irPlane.mAxis )
+ {
+ case 0:
+ return irPlane.mPosn - x;
+ break;
+ case 1:
+ return irPlane.mPosn - y;
+ break;
+ case 2:
+ return irPlane.mPosn - z;
+ break;
+ }
+ }
+ }
+
+ virtual float CompareTo( Plane3f& irPlane )
+ {
+ to do ........
+ }
+};
+*/
+#endif
diff --git a/game/code/render/Culling/Matrix3f.h b/game/code/render/Culling/Matrix3f.h
new file mode 100644
index 0000000..20b398d
--- /dev/null
+++ b/game/code/render/Culling/Matrix3f.h
@@ -0,0 +1,20 @@
+#ifndef __MATRIX_3_F__
+#define __MATRIX_3_F__
+
+#include <radmath/radmath.hpp>
+#include <raddebug.hpp>
+
+class Matrix3f : public rmt::Matrix
+{
+public:
+ Matrix3f(){}
+ ~Matrix3f(){}
+
+ float& operator[](int i)
+ {
+ return m[i/4][i%4];
+ }
+protected:
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/NodeFLL.h b/game/code/render/Culling/NodeFLL.h
new file mode 100644
index 0000000..1f3f677
--- /dev/null
+++ b/game/code/render/Culling/NodeFLL.h
@@ -0,0 +1,98 @@
+#ifndef __NodeFLL_H__
+#define __NodeFLL_H__
+/*
+#include <radmath/vector.hpp>
+
+#include <render/DSG/IEntityDSG.h>
+
+
+
+class ISortPriority
+{
+public:
+ ISortPriority(){}
+ ~ISortPriority(){}
+
+ virtual void SetRank(rmt::Vector& irRefPosn)=0;
+ virtual float Rank()=0;
+ virtual void Display()=0;
+};
+
+
+class NodeFLL
+{
+public:
+ NodeFLL(){}
+ ~NodeFLL(){}
+
+ void Clear()
+ {
+ mpNext = NULL;
+ mpData = NULL;
+ }
+
+ void ClearSetData(IEntityDSG* ipData)
+ {
+ mpNext = NULL;
+ mpData = ipData;
+ }
+ //
+ // Returns Head Node
+ //
+ NodeFLL* PlaceLowestFirst( NodeFLL* ipNode )
+ {
+ if(ipNode->mpData->Rank() < mpData->Rank())
+ {
+ //add prev to this node
+ ipNode->mpNext = this;
+ return ipNode;
+ }
+
+ if(mpNext)
+ {
+ //add after this node
+ mpNext = mpNext->PlaceLowestFirst(ipNode);
+ return this;
+ }
+ else
+ {
+ //add to end
+ mpNext = ipNode;
+ ipNode->mpNext = NULL;
+ return this;
+ }
+ }
+
+ //
+ // Returns Head Node
+ //
+ NodeFLL* PlaceHighestFirst( NodeFLL* ipNode )
+ {
+ if(ipNode->mpData->Rank() > mpData->Rank())
+ {
+ //add prev to this node
+ ipNode->mpNext = this;
+ return ipNode;
+ }
+
+ if(mpNext)
+ {
+ //add after this node
+ mpNext = mpNext->PlaceHighestFirst(ipNode);
+ return this;
+ }
+ else
+ {
+ //add to end
+ mpNext = ipNode;
+ ipNode->mpNext = NULL;
+ return this;
+ }
+ }
+
+ IEntityDSG* mpData;
+ NodeFLL* mpNext;
+protected:
+
+};*/
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/OctTreeConst.h b/game/code/render/Culling/OctTreeConst.h
new file mode 100644
index 0000000..b8c39ce
--- /dev/null
+++ b/game/code/render/Culling/OctTreeConst.h
@@ -0,0 +1,15 @@
+#ifndef __OCT_TREE_CONST_H__
+#define __OCT_TREE_CONST_H__
+
+//namespace OctTreeConst
+//{
+// enum Axis
+// {
+// UNASSIGNED = -1,
+// X = 0,
+// Y = 1,
+// Z = 2
+// };
+//};
+
+#endif
diff --git a/game/code/render/Culling/OctTreeData.cpp b/game/code/render/Culling/OctTreeData.cpp
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/game/code/render/Culling/OctTreeData.cpp
diff --git a/game/code/render/Culling/OctTreeData.h b/game/code/render/Culling/OctTreeData.h
new file mode 100644
index 0000000..425ef1d
--- /dev/null
+++ b/game/code/render/Culling/OctTreeData.h
@@ -0,0 +1,29 @@
+#ifndef __OCT_TREE_DATA_H__
+#define __OCT_TREE_DATA_H__
+
+//
+// This is a Data Declaration file.
+//
+// Containing:
+// -Includes for the Data relevant to the persistence of the
+// OctTree as a whole
+// -NOT the data relevant to the persistence of the components
+// which make up an OctTree.
+// -NOR the data solely salient to the individual processes that
+// might be run on the OctTree.
+// (ie, it's expected that only one OctTreeData instance exist )
+// (for every OctTree, which is managed by an OctTreeManager )
+// (and consists of a series of linked OctTreeNode's )
+//
+// Useful to:
+// -OctTreeNode
+// -OctTreeManager
+//
+
+#include <render/culling/OctTreeConst.h>
+#include <render/culling/OctTreeParams.h>
+#include <render/culling/CoordSubList.h>
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/OctTreeNode.cpp b/game/code/render/Culling/OctTreeNode.cpp
new file mode 100644
index 0000000..2660102
--- /dev/null
+++ b/game/code/render/Culling/OctTreeNode.cpp
@@ -0,0 +1,246 @@
+#include <render/culling/OctTreeNode.h>
+#include <render/culling/srrRenderTypes.h>
+#include <raddebug.hpp>
+//
+// int mAxis;
+// float mPlanePosn;
+//
+// CoordSubList* mpCoordList
+//
+// OctTreeNode* mpChildLT;
+// OctTreeNode* mpChildGT;
+//
+
+OctTreeNode::OctTreeNode()
+{
+ mpChildLT = NULL;
+ mpChildGT = NULL;
+ mpCoordList = NULL;
+}
+
+OctTreeNode::~OctTreeNode()
+{
+ CascadeDelete();
+}
+
+//
+// Grow Functions recursively build the Tree,
+// Calling appropriate SubDivide functions on their way down,
+// Managing stopping conditions.
+//
+// Input Constraints:
+// World must start at (0,0) (X,Z) and run contiguously to
+// positive max's in X and Z
+//
+void OctTreeNode::GrowTreeHeuristic( int iAxis,
+ CoordSubList* ipCoordList,
+ Vector3f& irGranularity,
+ int iRecursionsRemaining,
+ int iMinVertexCount )
+{
+ rAssert( mpChildLT == NULL );
+ rAssert( mpChildGT == NULL );
+ rAssert( mpCoordList == NULL );
+
+ mpCoordList = ipCoordList;
+
+ if( RoomToGrow( iRecursionsRemaining, iMinVertexCount ) )
+ {
+ mpChildLT = new(GMA_TEMP) OctTreeNode();
+ mpChildGT = new(GMA_TEMP) OctTreeNode();
+
+ CoordSubList *pListLT = NULL;
+ CoordSubList *pListGT = NULL;
+
+ mAxis = -1;
+
+ mpCoordList->SubDivideHeuristic( irGranularity, &pListLT, &pListGT, mAxis, mPlanePosn );
+
+ rAssert( pListLT->Size() > 0 );
+ rAssert( pListGT->Size() > 0 );
+
+ mpChildLT->GrowTreeHeuristic( -1, pListLT, irGranularity, iRecursionsRemaining--, iMinVertexCount );
+ mpChildGT->GrowTreeHeuristic( -1, pListGT, irGranularity, iRecursionsRemaining--, iMinVertexCount );
+ }
+ else
+ {
+ mpChildLT = NULL;
+ mpChildGT = NULL;
+ mPlanePosn = SRR_ERR_WS_COORD;
+ mAxis = SRR_ERR_INDEX;
+ }
+}
+
+bool OctTreeNode::AllElemsLT( CoordSubList *pListLT, AAPlane3f& irDivPlane )
+{
+ BoxPts CurBBox;
+
+ for( int i=pListLT->Size()-1; i>=0; i--)
+ {
+ if( pListLT->Get(i).MaxPlane( irDivPlane.mAxis ) > irDivPlane.mPosn )
+ return false;
+ }
+ return true;
+}
+
+bool OctTreeNode::AllElemsGT( CoordSubList *pListGT, AAPlane3f& irDivPlane )
+{
+ BoxPts CurBBox;
+
+ for( int i=pListGT->Size()-1; i>=0; i--)
+ {
+ if( pListGT->Get(i).MinPlane( irDivPlane.mAxis ) < irDivPlane.mPosn )
+ return false;
+ }
+ return true;
+}
+
+void OctTreeNode::GrowTreeHeuristicDebug
+(
+ int iAxis,
+ CoordSubList* ipCoordList,
+ Vector3f& irGranularity,
+ int iRecursionsRemaining,
+ int iMinVertexCount,
+ BoxPts& irBoxPts
+)
+{
+ rAssert( mpChildLT == NULL );
+ rAssert( mpChildGT == NULL );
+ rAssert( mpCoordList == NULL );
+
+ mpCoordList = ipCoordList;
+
+ if( RoomToGrow( iRecursionsRemaining, iMinVertexCount ) )
+ {
+ mpChildLT = new(GMA_TEMP) OctTreeNode();
+ mpChildGT = new(GMA_TEMP) OctTreeNode();
+
+ CoordSubList *pListLT = NULL;
+ CoordSubList *pListGT = NULL;
+
+ mAxis = -1;
+
+ mpCoordList->SubDivideHeuristic( irGranularity, &pListLT, &pListGT, mAxis, mPlanePosn );
+
+ //Debug begin
+ rAssert( pListLT->Size() > 0 );
+ rAssert( pListGT->Size() > 0 );
+
+ AAPlane3f DivPlane;
+
+ DivPlane.mAxis = mAxis;
+ DivPlane.mPosn = mPlanePosn;
+
+ BoxPts BoxPtsLT( irBoxPts );
+ BoxPtsLT.CutOffGT( DivPlane );
+
+ irBoxPts.CutOffLT( DivPlane );
+
+ if( !AllElemsLT( pListLT, DivPlane ) )
+ {
+ bool fucked = true;
+ }
+ if( !AllElemsGT( pListGT, DivPlane ) )
+ {
+ bool fucked = true;
+ }
+
+ mpChildLT->GrowTreeHeuristicDebug( -1, pListLT, irGranularity, iRecursionsRemaining--, iMinVertexCount, BoxPtsLT );
+ mpChildGT->GrowTreeHeuristicDebug( -1, pListGT, irGranularity, iRecursionsRemaining--, iMinVertexCount, irBoxPts );
+
+ rAssert( pListLT->Size() > 0 );
+ rAssert( pListGT->Size() > 0 );
+ //Debug end
+ }
+ else
+ {
+ mpChildLT = NULL;
+ mpChildGT = NULL;
+ mPlanePosn = SRR_ERR_WS_COORD;
+ mAxis = SRR_ERR_INDEX;
+ }
+}
+
+void OctTreeNode::GrowTreeUniform( int iAxis,
+ CoordSubList* ipCoordList,
+ int iRecursionsRemaining,
+ int iMinVertexCount )
+{
+}
+
+
+//////////////////////////////////////////////////////////////////////////////
+// Get All elements in the subdivision
+// that occupies irPosn
+//////////////////////////////////////////////////////////////////////////////
+void OctTreeNode::AccessSubDiv( Vector3f& irPosn, CoordSubList& orSubList )
+{
+
+}
+
+//
+// Generate X,Z tuples which enumerate the Cells in the World
+// Ranges:
+// 0 <-> max(X)/iSpaceGranularity
+// 0 <-> max(Z)/iSpaceGranularity
+//
+//void OctTreeNode::UpdateVisibleCellList( rmt::Vector* opVisibleCellList,
+// float iSpaceGranularity )
+//{
+//}
+
+////////////////////////////////// protected: ////////////////////////////////////////////
+
+//
+// Divides the space by a plane which cuts iAxis, such that
+// as close as possible to half the geometry is on each side
+// of the plane.
+//
+//void OctTreeNode::SubDivideHeuristic( int iAxis, rmt::Vector* ipVertexList, float iSpaceGranularity )
+//{
+//}
+
+//
+// Divides the space by a plane which cuts iAxis, such that
+// as close as possible to half the euclidean space is on
+// each side of the plane.
+//
+//void OctTreeNode::SubDivideUniform( int iAxis, rmt::Vector* ipVertexList, float iSpaceGranularity )
+//{
+//}
+
+
+bool OctTreeNode::RoomToGrow( int iRecursionsRemaining, int iMinVertexCount )
+{
+ if( iRecursionsRemaining > 0 )
+ {
+ if( mpCoordList->WeightedSize() > iMinVertexCount )
+ {
+ return mpCoordList->CanBeSplit();
+ //return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ return false;
+ }
+}
+
+void OctTreeNode::CascadeDelete()
+{
+ //
+ // They should either all be NULL or all be valid.
+ //
+ if( mpChildLT != NULL && mpChildGT != NULL )
+ {
+ delete mpChildLT;
+ delete mpChildGT;
+ }
+
+}
+
diff --git a/game/code/render/Culling/OctTreeNode.h b/game/code/render/Culling/OctTreeNode.h
new file mode 100644
index 0000000..ed10009
--- /dev/null
+++ b/game/code/render/Culling/OctTreeNode.h
@@ -0,0 +1,92 @@
+#ifndef __OCT_TREE_NODE_H__
+#define __OCT_TREE_NODE_H__
+
+#include <render/culling/OctTreeData.h>
+#include <render/culling/BoxPts.h>
+
+class OctTreeNode
+{
+public:
+ OctTreeNode();
+ ~OctTreeNode();
+
+ //
+ // Grow Functions recursively build the Tree,
+ // Calling appropriate SubDivide functions on their way down,
+ // Managing stopping conditions.
+ //
+ // Input Constraints:
+ // World must start at (0,0) (X,Z) and run contiguously to
+ // positive max's in X and Z
+ //
+ void GrowTreeHeuristic( int iAxis,
+ CoordSubList* ipCoordList,
+ Vector3f& irGranularity,
+ int iRecursionsRemaining,
+ int iMinVertexCount );
+
+ void GrowTreeUniform( int iAxis,
+ CoordSubList* ipCoordList,
+ int iRecursionsRemaining,
+ int iMinVertexCount );
+
+ //Debug
+ bool AllElemsLT( CoordSubList *pListLT, AAPlane3f& irDivPlane );
+ bool AllElemsGT( CoordSubList *pListGT, AAPlane3f& irDivPlane );
+ void GrowTreeHeuristicDebug(
+ int iAxis,
+ CoordSubList* ipCoordList,
+ Vector3f& irGranularity,
+ int iRecursionsRemaining,
+ int iMinVertexCount,
+ BoxPts& irBoxPts);
+
+ //
+ // Get All elements in the subdivision
+ // that occupies irPosn
+ //
+ void AccessSubDiv( Vector3f& irPosn, CoordSubList& orSubList );
+
+ //
+ // Generate X,Z tuples which enumerate the Cells in the World
+ // Ranges:
+ // 0 <-> max(X)/iSpaceGranularity
+ // 0 <-> max(Z)/iSpaceGranularity
+ //
+ //void UpdateVisibleCellList( rmt::Vector* opVisibleCellList,
+ // float iSpaceGranularity );
+
+ int mAxis;
+ float mPlanePosn;
+
+ OctTreeNode* mpChildLT;
+ OctTreeNode* mpChildGT;
+protected:
+ //
+ // Divides the space by a plane which cuts iAxis, such that
+ // as close as possible to half the geometry is on each side
+ // of the plane.
+ //
+ //void SubDivideHeuristic( int iAxis, rmt::Vector* ipVertexList, float iSpaceGranularity );
+
+ //
+ // Divides the space by a plane which cuts iAxis, such that
+ // as close as possible to half the euclidean space is on
+ // each side of the plane.
+ //
+ //void SubDivideUniform( int iAxis, rmt::Vector* ipVertexList, float iSpaceGranularity );
+
+
+ bool RoomToGrow( int iRecursionsRemaining, int iMinVertexCount );
+ void CascadeDelete();
+
+
+
+ // TODO - Put some sort of UID list here for final tree output
+ // This will link up with the actual geometry in-game
+ // This will be used by UpdateVisibleCellList
+ CoordSubList* mpCoordList;
+
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/OctTreeParams.h b/game/code/render/Culling/OctTreeParams.h
new file mode 100644
index 0000000..9c2d9e7
--- /dev/null
+++ b/game/code/render/Culling/OctTreeParams.h
@@ -0,0 +1,31 @@
+#ifndef __OCT_TREE_PARAMS_H__
+#define __OCT_TREE_PARAMS_H__
+
+#include <p3d/p3dtypes.hpp>
+#include <render/Culling/Bounds.h>
+
+//
+// OctTreeParams:
+//
+// Contains:
+// -All data necessary for which only a single instance is needed
+// for the persistence of an OctTree
+//
+class OctTreeParams
+{
+public:
+ OctTreeParams();
+ ~OctTreeParams();
+
+ //
+ // The lowest possible value boundary at which space can be divided
+ //
+ float mSpaceGranularity;
+ //
+ // The extents of the world being bound by the OctTree (X,Y,Z)
+ //
+ rmt::Box3D mBounds;
+protected:
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/Plane3f.h b/game/code/render/Culling/Plane3f.h
new file mode 100644
index 0000000..848dbe7
--- /dev/null
+++ b/game/code/render/Culling/Plane3f.h
@@ -0,0 +1,52 @@
+#ifndef __PLANE_3F_H__
+#define __PLANE_3F_H__
+
+#include <render/culling/Vector3f.h>
+
+class AAPlane3f
+{
+public:
+ AAPlane3f(){}
+ ~AAPlane3f(){}
+
+ void Set( char iAxis, float iPosn )
+ {
+ mAxis = iAxis;
+ mPosn = iPosn;
+ }
+
+ char mAxis;
+ float mPosn;
+};
+
+class AANormPlane3f
+{
+public:
+ AANormPlane3f(){}
+ ~AANormPlane3f(){}
+
+ char mAxis;
+ float mPosn;
+ char mNorm; //Norm == 0, Normal Points toward the Origin; != 0, away
+};
+
+class NormPlane3f
+{
+public:
+ NormPlane3f(){}
+ ~NormPlane3f(){}
+
+ void Normalise()
+ {
+ float Mag = (float)rmt::Sqrt( mNorm.x*mNorm.x + mNorm.y*mNorm.y + mNorm.z*mNorm.z + mOffset*mOffset );
+ mNorm.x /= Mag;
+ mNorm.y /= Mag;
+ mNorm.z /= Mag;
+ mOffset /= Mag;
+ }
+
+ Vector3f mNorm;
+ float mOffset;
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/Point3f.h b/game/code/render/Culling/Point3f.h
new file mode 100644
index 0000000..140a1c5
--- /dev/null
+++ b/game/code/render/Culling/Point3f.h
@@ -0,0 +1,22 @@
+#ifndef __VECTOR_3F_H__
+#define __VECTOR_3F_H__
+
+#include <radmath.hpp>
+
+class Vector3f : public Vector
+{
+public:
+ Vector3f() : Vector() {}
+ Vector3f( float iX, float iY, float iZ ) : Vector( iX, iY, iZ ) {}
+ Vector3f( float* ipVector ) : Vector( ipVector[0], ipVector[1], ipVector[2] ) {}
+
+ float& operator[]( int iIndex )
+ {
+ rAssert( (iIndex>-1) && (iIndex<3) );
+ return ((float*)(this))[iIndex];
+ }
+
+protected:
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/ReserveArray.h b/game/code/render/Culling/ReserveArray.h
new file mode 100644
index 0000000..51849c6
--- /dev/null
+++ b/game/code/render/Culling/ReserveArray.h
@@ -0,0 +1,169 @@
+#ifndef __RESERVE_ARRAY_H__
+#define __RESERVE_ARRAY_H__
+
+#ifndef TOOLS
+#include <memory/srrmemory.h>
+#endif
+
+template <class T> class ReserveArray
+{
+public:
+
+ /////////////////////////////////////////
+ // Constructors/Destructors
+ /////////////////////////////////////////
+ ~ReserveArray()
+ {
+ Clear();
+ }
+
+ ReserveArray() : mUseSize(0), mpData(NULL)
+ {
+ }
+
+ ReserveArray( int iSize ) : mUseSize(0), mpData(NULL)
+ {
+ Allocate( iSize );
+ }
+
+
+ /////////////////////////////////////////
+ // Main Methods
+ /////////////////////////////////////////
+ void Init( int iIndex, T& irVal )
+ {
+ rAssert( (iIndex>=mUseSize)&&(iIndex<mSize) );
+ mUseSize = iIndex+1;
+ mpData[iIndex] = irVal;
+ }
+
+ void ClearUse()
+ {
+ rAssert(IsSetUp());
+ mUseSize = 0;
+ }
+
+ void Use( int iIndex )
+ {
+ rAssert( (iIndex>=mUseSize)&&(iIndex<mSize) );
+ mUseSize = iIndex+1;
+ }
+
+ void AddUse( int iCountSize )
+ {
+ rAssert( (iCountSize+mUseSize)<=mSize );
+ mUseSize += iCountSize;
+ }
+
+ void Add( T& irVal )
+ {
+ rAssert(mUseSize<mSize);
+ mpData[mUseSize] = irVal;
+ mUseSize++;
+ }
+
+ T& operator[]( int iIndex )
+ {
+ rAssert( (iIndex < mUseSize) && (iIndex > -1));
+ return mpData[iIndex];
+ }
+
+ void Reserve( int iCount )
+ {
+ // UseSize is used during the
+ // unallocated state to count the reservations
+ rAssert( !IsSetUp() );
+ mUseSize += iCount;
+ }
+
+ void Allocate()
+ {
+ rAssert( !IsSetUp() );
+ if( mUseSize == 0 )
+ {
+ mSize = mUseSize;
+ mpData = NULL;
+ }
+ else
+ {
+ mSize = mUseSize;
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#endif
+
+ mpData = new T[mSize];
+ rAssert(mSize>0);
+ rAssert(mpData!=NULL);
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+#endif
+
+ mUseSize = 0;
+ }
+ }
+
+ void Allocate( int iSize )
+ {
+ rAssert( mUseSize == 0 );
+ //TODO: wha?
+ if( !IsSetUp() )
+ iSize += mUseSize;
+ Clear();
+ mSize = iSize;
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#endif
+
+ mpData = new T[mSize];
+ rAssert(mSize>0);
+ rAssert(mpData!=NULL);
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+#endif
+
+ }
+
+ void Clear()
+ {
+ if( mpData != NULL )
+ {
+ delete[] mpData;
+ }
+ mpData = NULL;
+ mUseSize = 0;
+ }
+
+ bool IsSetUp()
+ {
+ if( mpData == NULL )
+ return false;
+ else
+ return true;
+ }
+
+ /////////////////////////////////////////
+ // Data
+ /////////////////////////////////////////
+ int mSize;
+ int mUseSize;
+ T* mpData;
+
+protected:
+
+private:
+ /////////////////////////////////////////
+ // Currently Disallowed Functions
+ // -Not implemented
+ // -Not accessible
+ // -Use will error at Compile
+ /////////////////////////////////////////
+ ReserveArray( const ReserveArray& iSource );
+ ReserveArray& operator=( const ReserveArray& iSource );
+
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/ScratchArray.h b/game/code/render/Culling/ScratchArray.h
new file mode 100644
index 0000000..448d261
--- /dev/null
+++ b/game/code/render/Culling/ScratchArray.h
@@ -0,0 +1,259 @@
+#ifndef __SCRATCH_ARRAY_H__
+#define __SCRATCH_ARRAY_H__
+
+#include <render/culling/FixedArray.h>
+#include <render/culling/srrRenderTypes.h>
+//
+// Designed for quick alloc/dealloc
+//
+
+template <class T> class SubArray
+{
+public:
+ SubArray()
+ {
+ Invalidate();
+ }
+
+ SubArray( T* ipData, int iSize )
+ {
+ mpData = ipData;
+ mSize = iSize;
+ }
+
+ ~SubArray()
+ {
+ if( IsValid() )
+ {
+ rAssert(false);
+ Invalidate();
+ }
+ }
+
+ T& operator[]( int iIndex )
+ {
+ rAssert( IsValid() );
+ rAssert( (iIndex < mSize) && (iIndex > -1) );
+ return mpData[iIndex];
+ }
+
+ void SetAll( T& irDefaultValue )
+ {
+ rAssert( IsValid() );
+ for(int i=0; i<mSize; i++ )
+ {
+ mpData[i] = irDefaultValue;
+ }
+ }
+
+ void SplitOffEnd( SubArray<T>& orSplitArray, int iSplitSize )
+ {
+ rAssert( IsValid() );
+ rAssert( iSplitSize <= mSize );
+
+ orSplitArray.mSize = iSplitSize;
+ orSplitArray.mpData = mpData + (mSize-iSplitSize);
+
+ if( mSize == iSplitSize )
+ Invalidate();
+ }
+
+ void AbsorbR( SubArray<T>& orAdjArray )
+ {
+ rAssert(IsValid());
+ rAssert(CanAbsorbR( orAdjArray ));
+
+ mSize += orAdjArray.mSize;
+ orAdjArray.Invalidate();
+ }
+
+ void Init( int iSize, T* ipData )
+ {
+ rAssert(!IsValid());
+
+ mSize = iSize;
+ mpData = ipData;
+ }
+
+
+ bool CanAbsorbR( SubArray<T>& irAdjArray )
+ {
+ if( mpData+mSize == orSplitArray.mpData )
+ return true;
+ else
+ return false;
+ }
+
+ bool IsValid()
+ {
+ return (mpData!=NULL)&&(mSize!=SRR_ERR_INDEX);
+ }
+
+ void Invalidate()
+ {
+ mSize = SRR_ERR_INDEX;
+ mpData = NULL;
+ }
+
+ T* mpData;
+ int mSize;
+protected:
+
+};
+
+/*
+template <class T> class ScratchArray : protected FixedArray<T>
+{
+public:
+ ScratchArray(){}
+ ~ScratchArray(){}
+
+ //////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////
+ void Init( int iSize )
+ {
+ Allocate( iSize );
+ CleanFreeSlots();
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////
+ void SetAll( T& irDefaultValue )
+ {
+ for(int i=0; i<mSize; i++ )
+ {
+ mpData[i] = irDefaultValue;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////
+ void Acquire( SubArray<T>& orSubArray, int iSize )
+ {
+ int FSIndex = IsThereSpace(iSize); //get free frag slot index
+ if( FSIndex != SRR_ERR_INDEX )
+ {
+ Alloc( orSubArray, iSize, FSIndex );
+ }
+ else
+ {
+ opSubArray = NULL;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////
+ void Release( SubArray<T>& opSubArray )
+ {
+ all of this work is to set up a set of scratchpad allocators to do bin counts
+ to better divide the data based on good spreads
+ it will run faster... yay.
+
+ }
+
+protected:
+ //////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////
+ void Alloc( SubArray<T>& orSubArray, int iSize, int iHoleIndex )
+ {
+ mAllocCount++;
+
+ mFreeHoles[iHoleIndex].SplitOffEnd( orSubArray, iSize );
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////
+ void Free( SubArray<T>& orSubArray )
+ {
+ int AdjLIndex;
+ FindAdjLHole( AdjLIndex, orSubArray );
+
+ if( AdjLIndex != SRR_ERR_INDEX )
+ {
+ mFreeHoles[AdjLIndex].AbsorbR(orSubArray);
+ }
+ else
+ {
+ AddFreeHole( orSubArray, InvalidFreeHole() );
+ }
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////
+ void FindAdjLHole( int& i, SubArray<T>& orSubArray )
+ {
+ rAssert((orSubArray.mpData >= mpData) && (orSubArray.mpData < (mpData+mSize)));
+
+ for( i=0; i<msMaxFreeHoles; i++ )
+ {
+ if( (mFreeHoles[i].IsValid()) && (mFreeHoles[i].CanAbsorbR(orSubArray)) )
+ return;
+ }
+ i = SRR_ERR_INDEX;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////
+ void AddFreeHole( SubArray<T>& orSubArray, int& i )
+ {
+ rAssert( i!=SRR_ERR_INDEX );
+
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////
+ void CleanFreeHoles()
+ {
+ for( int i=0; i<msMaxFreeHoles; i++ )
+ {
+ mFreeFrags[i].Invalidate();
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////
+ int InvalidFreeHole()
+ {
+ for( int i=0; i<msMaxFreeHoles; i++ )
+ {
+ if( mFreeHoles[i].IsInvalid() )
+ return i;
+ }
+ return SRR_ERR_INDEX;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////////
+ int IsThereSpace( int iSize )
+ {
+ int svfi = SRR_ERR_INDEX;//SmallestValidFragIndex;
+ int numFreeHoles = 0;
+ for( int i=0; i<msMaxFreeHoles; i++ )
+ {
+ if( mFreeHoles[i].IsValid() )
+ {
+ numFreeHoles++;
+ if ( (mFreeHoles[i].mSize >= iSize) && (mFreeHoles[i].mSize <= mFreeHoles[svfi].mSize) )
+ svfi = i;
+ }
+ }
+
+ if( mAllocCount < msMaxFreeHoles-1 )
+ {
+ return svfi;
+ }
+ else
+ {
+ return SRR_ERR_INDEX;
+ }
+ }
+
+ static const int msMaxFreeHoles = 10;
+
+ int mAllocCount;
+ SubArray<T> mFreeHoles[msMaxFreeHoles];
+
+};
+*/
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/SpatialNode.h b/game/code/render/Culling/SpatialNode.h
new file mode 100644
index 0000000..f4e5f39
--- /dev/null
+++ b/game/code/render/Culling/SpatialNode.h
@@ -0,0 +1,67 @@
+#ifndef __SPATIAL_NODE_H__
+#define __SPATIAL_NODE_H__
+
+#include <render/culling/OctTreeNode.h>
+#include <render/culling/Plane3f.h>
+#include <render/culling/ContiguousBinNode.h>
+#include <render/culling/SwapArray.h>
+#include <render/culling/BoxPts.h>
+
+//#include <render/DSG/StaticEntityDSG.h>
+//#include <render/DSG/StaticPhysDSG.h>
+//#include <render/DSG/IntersectDSG.h>
+
+class StaticEntityDSG;
+class StaticPhysDSG;
+class IntersectDSG;
+class DynaPhysDSG;
+class FenceEntityDSG;
+class AnimCollisionEntityDSG;
+class TriggerVolume;
+class RoadSegment;
+class PathSegment;
+class AnimEntityDSG;
+
+//////////////////////////////////////////////////////////////////////////
+//
+// A SpatialNode is data
+// usually attached to a ContiguousBinNode
+// with Spatial Capabilities encompassing the following:
+//
+// -A Subdivision Plane representing the plane which subdivides
+// the Space contained at the current node into two discrete
+// volumes contained in the child nodes
+//
+
+class SpatialNode
+{
+public:
+ SpatialNode();
+ ~SpatialNode();
+
+ //This plane should be move the the contig bin node, to improve cache hits
+ AAPlane3f mSubDivPlane;
+
+ NodeSwapArray<StaticEntityDSG*> mSEntityElems; //mSpatialElems;
+ SwapArray<StaticPhysDSG*> mSPhysElems; //mDynamicElems;
+ SwapArray<IntersectDSG*> mIntersectElems;
+ NodeSwapArray<DynaPhysDSG*> mDPhysElems;
+ SwapArray<FenceEntityDSG*> mFenceElems;
+ NodeSwapArray<AnimCollisionEntityDSG*> mAnimCollElems;
+ NodeSwapArray<AnimEntityDSG*> mAnimElems;
+ SwapArray<TriggerVolume*> mTrigVolElems;
+ SwapArray<RoadSegment*> mRoadSegmentElems;
+ SwapArray<PathSegment*> mPathSegmentElems;
+ //Debug
+ BoxPts mBBox;
+
+protected:
+};
+
+
+inline SpatialNode::SpatialNode(){}
+
+inline SpatialNode::~SpatialNode(){}
+
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/SpatialTree.cpp b/game/code/render/Culling/SpatialTree.cpp
new file mode 100644
index 0000000..866dfb9
--- /dev/null
+++ b/game/code/render/Culling/SpatialTree.cpp
@@ -0,0 +1,94 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: SpatialTree.cpp
+//
+// Description: Implement SpatialTree
+//
+// History: 16/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <render/culling/SpatialTree.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//////////////////////////////////////////////////////////////////////////
+//
+// TODO: remove rAssert associated code
+//
+//////////////////////////////////////////////////////////////////////////
+void SpatialTree::SetNodes( int& orSubTreeSize, int iNodeIndex, int iParentIndex, OctTreeNode* ipTreeRoot, BoxPts& irBoxPts )
+{
+ int rightSubTreeSize = 0;
+
+ mTreeNodes[iNodeIndex].mData.mSubDivPlane.Set( ipTreeRoot->mAxis, ipTreeRoot->mPlanePosn );
+ mTreeNodes[iNodeIndex].mData.mBBox = irBoxPts;
+ mTreeNodes[iNodeIndex].LinkParent(iParentIndex - iNodeIndex);
+
+ if( ipTreeRoot->mpChildLT != NULL )
+ {
+ rAssert( ipTreeRoot->mpChildGT != NULL );
+
+ BoxPts BBoxLT( irBoxPts );
+ BBoxLT.CutOffGT( mTreeNodes[iNodeIndex].mData.mSubDivPlane );
+ irBoxPts.CutOffLT( mTreeNodes[iNodeIndex].mData.mSubDivPlane );
+
+ SetNodes( orSubTreeSize, iNodeIndex+1, iNodeIndex, ipTreeRoot->mpChildLT, BBoxLT );
+ SetNodes( rightSubTreeSize, iNodeIndex+orSubTreeSize+1, iNodeIndex, ipTreeRoot->mpChildGT, irBoxPts );
+
+ orSubTreeSize = orSubTreeSize + rightSubTreeSize;
+ mTreeNodes[iNodeIndex].SetSubTreeSize( orSubTreeSize );
+ }
+ else
+ {
+ rAssert( ipTreeRoot->mpChildGT == NULL );
+
+ mTreeNodes[iNodeIndex].SetSubTreeSize( ContiguousBinNode< SpatialNode >::msNoChildren );
+ }
+
+ orSubTreeSize++;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void SpatialTree::CountNodes( int& orCount, OctTreeNode* ipTreeRoot )
+{
+ orCount++;
+
+ if( ipTreeRoot->mpChildLT != NULL )
+ {
+ rAssert( ipTreeRoot->mpChildGT != NULL );
+ CountNodes( orCount, ipTreeRoot->mpChildLT );
+ CountNodes( orCount, ipTreeRoot->mpChildGT );
+ }
+ else
+ {
+ rAssert( ipTreeRoot->mpChildGT == NULL );
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/render/Culling/SpatialTree.h b/game/code/render/Culling/SpatialTree.h
new file mode 100644
index 0000000..52f8deb
--- /dev/null
+++ b/game/code/render/Culling/SpatialTree.h
@@ -0,0 +1,101 @@
+#ifndef __SPATIAL_TREE_H__
+#define __SPATIAL_TREE_H__
+
+#include <p3d/entity.hpp>
+
+#include <render/culling/OctTreeNode.h>
+#include <render/culling/FixedArray.h>
+#include <render/culling/SpatialNode.h>
+#include <render/culling/BoxPts.h>
+
+class SpatialTree
+: public tEntity
+{
+public:
+ SpatialTree();
+ ~SpatialTree();
+
+ void Generate( OctTreeNode* ipTreeRoot, Bounds3f& irTreeBounds );
+
+ ContiguousBinNode< SpatialNode >* GetRoot();
+
+ void SetTo( int iNumNodes, Bounds3f iTreeBounds );
+
+ Bounds3f& GetBounds();
+
+protected:
+ FixedArray< ContiguousBinNode< SpatialNode > > mTreeNodes;
+ Bounds3f mTreeBounds;
+
+ void CountNodes( int& orCount, OctTreeNode* ipTreeRoot );
+ void SetNodes( int& orSubTreeSize, int iNodeIndex, int iParentIndex, OctTreeNode* ipTreeRoot, BoxPts& irBoxPts );
+};
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+inline
+SpatialTree::SpatialTree()
+{
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+inline
+SpatialTree::~SpatialTree()
+{
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTree::Generate( OctTreeNode* ipTreeRoot, Bounds3f& irTreeBounds )
+{
+ int nNodes=0, SubTreeSize=0, NodeIndex=0;
+
+ mTreeBounds = irTreeBounds;
+
+ CountNodes( nNodes, ipTreeRoot );
+
+ mTreeNodes.Allocate(nNodes);
+
+ BoxPts rootBBox;
+ rootBBox.SetTo( mTreeBounds );
+
+ SetNodes( SubTreeSize, NodeIndex, ContiguousBinNode< SpatialNode >::msNoParent, ipTreeRoot, rootBBox );
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+inline
+ContiguousBinNode< SpatialNode >* SpatialTree::GetRoot()
+{
+ return mTreeNodes.mpData;
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+inline
+Bounds3f& SpatialTree::GetBounds()
+{
+ return mTreeBounds;
+}
+
+//========================================================================
+// SpatialTree::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+inline
+void SpatialTree::SetTo( int iNumNodes, Bounds3f iTreeBounds )
+{
+ mTreeNodes.Allocate(iNumNodes);
+ mTreeBounds = iTreeBounds;
+}
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/SpatialTreeFactory.h b/game/code/render/Culling/SpatialTreeFactory.h
new file mode 100644
index 0000000..7498124
--- /dev/null
+++ b/game/code/render/Culling/SpatialTreeFactory.h
@@ -0,0 +1,208 @@
+#ifndef __SPATIAL_TREE_FACTORY_H__
+#define __SPATIAL_TREE_FACTORY_H__
+
+#include <render/culling/UseArray.h>
+#include <render/culling/FixedArray.h>
+#include <render/culling/srrRenderTypes.h>
+#include <render/culling/Bounds.h>
+#include <render/culling/SpatialTree.h>
+#include <render/culling/SpatialTreeIter.h>
+#include <render/culling/HexahedronP.h>
+//#include <render/culling/SpatialFruit.h>
+#include <render/culling/CellBlock.h>
+
+//////////////////////////////////////////////////////////////////////
+//
+// The Purpose of the class is to provide a black box abstraction
+// of the various processes involved in the creation of a runtime
+// spatial tree from a set of inputs ( posn & weighting )
+//
+// Support for Fruit (automatic queuing of fruit/payload and adding
+// to generated tree) will be added in sub classes
+//
+//////////////////////////////////////////////////////////////////////
+class SpatialTreeFactory
+{
+public:
+ SpatialTreeFactory();
+ ~SpatialTreeFactory();
+
+ void Clear();
+
+ void Reset( int inSeedMax );
+
+ void Seed( Vector3f& irPosn, int iWeight );
+
+ void Generate( Vector3f& irGranularity );
+
+ void ExtractTree( SpatialTree** oppRuntimeTree );
+
+protected:
+
+ bool TreeGenerated();
+
+ ////////////////////////////////////////////////////////////
+ // Data neccessary for the entire tree creation lifecycle
+ ////////////////////////////////////////////////////////////
+ SpatialTree* mpRuntimeTree;
+ SpatialTreeIter mTreeWalker;
+
+ UseArray<Vector3f> mSeedPosns;
+ UseArray<int> mSeedWeights;
+};
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+SpatialTreeFactory::SpatialTreeFactory()
+{
+ mpRuntimeTree = NULL;
+}
+//////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////
+SpatialTreeFactory::~SpatialTreeFactory()
+{
+}
+//////////////////////////////////////////////////////////////////////
+// Removes all state-based allocations (ie the allocations neccessary
+// for the creation of a Tree)
+//////////////////////////////////////////////////////////////////////
+void SpatialTreeFactory::Clear()
+{
+ if( mpRuntimeTree != NULL )
+ {
+ delete mpRuntimeTree;
+ mpRuntimeTree = NULL;
+ }
+
+ mTreeWalker.Clear();
+ mSeedPosns.Clear();
+ mSeedWeights.Clear();
+}
+//////////////////////////////////////////////////////////////////////
+// Initialise the factory for the creation of a new runtime tree of
+// the same type (T). Where each spatial node within the final tree
+// contains a ReserveArray of T's.
+//
+// The SeedMax determines the maximum size of the queue of Seeds
+// (see Seed)
+//
+//////////////////////////////////////////////////////////////////////
+void SpatialTreeFactory::Reset( int inSeedMax )
+{
+ Clear();
+
+ mSeedPosns.Allocate( inSeedMax );
+ mSeedWeights.Allocate( inSeedMax );
+}
+//////////////////////////////////////////////////////////////////////
+// Declare a position and a weight which will influence the placement
+// of Axis Aligned Planes and the general Topology of the RuntimeTree
+//////////////////////////////////////////////////////////////////////
+void SpatialTreeFactory::Seed( Vector3f& irPosn, int iWeight )
+{
+ rAssert( !TreeGenerated() );
+
+ mSeedPosns.Add( irPosn );
+ mSeedWeights.Add( iWeight );
+}
+//////////////////////////////////////////////////////////////////////
+// This call:
+// -finalises the Seed'ing,
+// -generates the Runtime Tree Topology
+//
+// Calls to AttachFruit can still be made up until the Tree is
+// extracted.
+//////////////////////////////////////////////////////////////////////
+void SpatialTreeFactory::Generate( Vector3f& irGranularity )
+{
+ rAssert( !TreeGenerated() );
+
+ Bounds3f WorldBounds;
+ Vector3i ArrayDims;
+ CellBlock CellMatrix;
+ OctTreeNode RootNode;
+
+ FixedArray<Cell> DataCellsArray;
+
+ //
+ // Although it'd be 'nice' to have UseArray Inherit
+ // from FixedArray, it'd be vtable badness in inner loop atomics
+ // and extra overhead.
+ //
+ // This alternative is still safe, cheap, and only a little sticky
+ //
+ FixedArray<Vector3f> seedPosnsFacade;
+ FixedArray<int> seedWeightsFacade;
+
+ seedPosnsFacade.mpData = mSeedPosns.mpData;
+ seedPosnsFacade.mSize = mSeedPosns.mUseSize;
+
+ seedWeightsFacade.mpData = mSeedWeights.mpData;
+ seedWeightsFacade.mSize = mSeedWeights.mUseSize;
+
+ ///////////////////////////////////////////////////////////////////////
+ CellMatrix.Init( seedPosnsFacade, seedWeightsFacade, irGranularity );
+
+ CellMatrix.GenerateCells();
+
+ ////////////////////////////////////////////////////////////////////
+ CellMatrix.ExtractNonEmptyCells( DataCellsArray );
+ CellMatrix.ExtractDims( ArrayDims );
+ CellMatrix.ExtractBounds( WorldBounds );
+
+ ////////////////////////////////////////////////////////////////////
+ CoordSubList RootList( DataCellsArray.mpData, DataCellsArray.mSize, ArrayDims );
+
+ BoxPts DebugBBox;
+ DebugBBox.SetTo( WorldBounds );
+
+ // RootNode.GrowTreeHeuristic( -1, &RootList, irCellGranularity, 12, 1 );
+ //Debug
+// RootNode.GrowTreeHeuristicDebug( -1, &RootList, irGranularity, 12, 1, DebugBBox );
+ RootNode.GrowTreeHeuristicDebug( -1, &RootList, irGranularity, 32, 1, DebugBBox );
+
+ mpRuntimeTree = new SpatialTree;
+ mpRuntimeTree->Generate( &RootNode, WorldBounds );
+
+ mTreeWalker.SetToRoot( *mpRuntimeTree );
+
+
+ ////////////////////////////////////////////////////////////////////
+ mSeedPosns.mpData = seedPosnsFacade.mpData;
+ mSeedPosns.mUseSize = seedPosnsFacade.mSize;
+
+ mSeedWeights.mpData = seedWeightsFacade.mpData;
+ mSeedWeights.mUseSize = seedWeightsFacade.mSize;
+
+ seedPosnsFacade.mpData = NULL;
+ seedWeightsFacade.mpData = NULL;
+}
+//////////////////////////////////////////////////////////////////////
+// Once the Tree is extracted, SpatialTreeFactory divorces
+// all responsability for said data; and removes internal
+// reference for it.
+//
+// When the Factory is deleted, it deletes any data it has
+// maintained reference to.
+//////////////////////////////////////////////////////////////////////
+void SpatialTreeFactory::ExtractTree( SpatialTree** oppRuntimeTree )
+{
+ rAssert( TreeGenerated() );
+
+ *oppRuntimeTree = mpRuntimeTree;
+ mpRuntimeTree = NULL;
+
+ Clear();
+}
+//////////////////////////////////////////////////////////////////////
+// mpRuntimeTree should only be non-null between the calls to
+// Generate and ExtractTree (or, more specifically, Clear)
+//////////////////////////////////////////////////////////////////////
+bool SpatialTreeFactory::TreeGenerated()
+{
+ if( mpRuntimeTree == NULL )
+ return false;
+ return true;
+}
+
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/SpatialTreeIter.cpp b/game/code/render/Culling/SpatialTreeIter.cpp
new file mode 100644
index 0000000..b9b84ef
--- /dev/null
+++ b/game/code/render/Culling/SpatialTreeIter.cpp
@@ -0,0 +1,645 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: SpatialTreeIter.cpp
+//
+// Description: Implement SpatialTreeIter
+//
+// History: 16/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <render/culling/SpatialTreeIter.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//========================================================================
+// SpatialTreeIter::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+
+SpatialNode& SpatialTreeIter::rSeekNode
+(
+ ISpatialProxyAA& irVolume,
+ int iCurNodeOffset
+)
+{
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ // if(pCurNode->mData.mSubDivPlane.mAxis ==2 && pCurNode->mData.mSubDivPlane.mPosn ==0.0f)
+ // rReleasePrintf("ackaMaAck\n");
+ float PlaneVolResult = irVolume.CompareTo( pCurNode->mData.mSubDivPlane );
+
+ if( PlaneVolResult > 0.0f ) //The Plane is greater than TVolume in Posn, so TVolume is in the LT Partitiion..
+ {
+ return rSeekNode( irVolume, iCurNodeOffset + pCurNode->LChildOffset() );
+ }
+ else
+ {
+ if( PlaneVolResult < 0.0f ) //The Plane is less han TVolume in Posn, so TVolume is in the GT Partitiion..
+ {
+ return rSeekNode( irVolume, iCurNodeOffset + pCurNode->RChildOffset() );
+ }
+ else // PlaneVolResult == 0
+ {
+ return (pCurNode->mData);
+ }
+ }
+ }
+ else
+ {
+ return (pCurNode->mData);
+ }
+}
+
+//========================================================================
+// SpatialTreeIter::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+
+void SpatialTreeIter::SeekSubNodes
+(
+ ISpatialProxyAA& irVolume,
+ UseArray<int>& orNodeOffsets,
+ int iCurNodeOffset
+)
+{
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ float PlaneVolResult = irVolume.CompareTo( pCurNode->mData.mSubDivPlane );
+
+ if( PlaneVolResult > 0.0f ) //The Plane is greater than TVolume in Posn, so TVolume is in the LT Partitiion..
+ {
+ SeekSubNodes( irVolume, orNodeOffsets, iCurNodeOffset + pCurNode->LChildOffset() );
+ }
+ else
+ {
+ if( PlaneVolResult < 0.0f ) //The Plane is less han TVolume in Posn, so TVolume is in the GT Partitiion..
+ {
+ SeekSubNodes( irVolume, orNodeOffsets, iCurNodeOffset + pCurNode->RChildOffset() );
+ }
+ else // PlaneVolResult == 0
+ {
+ //return *pCurNode;
+ int max = pCurNode->GetSubTreeSize()+1;
+ for(int i=0; i<max; i++, iCurNodeOffset++)
+ {
+ orNodeOffsets.Add(iCurNodeOffset);
+ }
+
+ }
+ }
+ }
+ else
+ {
+ //return *pCurNode;
+ orNodeOffsets.Add(iCurNodeOffset);
+ }
+}
+
+//========================================================================
+// SpatialTreeIter::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+
+void SpatialTreeIter::SeekAllNodes
+(
+ ISpatialProxyAA& irVolume,
+ UseArray<int>& orNodeOffsets,
+ int iCurNodeOffset
+)
+{
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ float PlaneVolResult = irVolume.CompareTo( pCurNode->mData.mSubDivPlane );
+
+ if( PlaneVolResult > 0.0f ) //The Plane is greater than TVolume in Posn, so TVolume is in the LT Partitiion..
+ {
+ orNodeOffsets.Add(iCurNodeOffset);
+ SeekAllNodes( irVolume, orNodeOffsets, iCurNodeOffset + pCurNode->LChildOffset() );
+ }
+ else
+ {
+ if( PlaneVolResult < 0.0f ) //The Plane is less han TVolume in Posn, so TVolume is in the GT Partitiion..
+ {
+ orNodeOffsets.Add(iCurNodeOffset);
+ SeekAllNodes( irVolume, orNodeOffsets, iCurNodeOffset + pCurNode->RChildOffset() );
+ }
+ else // PlaneVolResult == 0
+ {
+ //return *pCurNode;
+ int max = pCurNode->GetSubTreeSize()+1;
+ for(int i=0; i<max; i++, iCurNodeOffset++)
+ {
+ orNodeOffsets.Add(iCurNodeOffset);
+ }
+
+ }
+ }
+ }
+ else
+ {
+ //return *pCurNode;
+ orNodeOffsets.Add(iCurNodeOffset);
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+SpatialNode& SpatialTreeIter::rSeekLeaf
+(
+ int iCurNodeOffset,
+ Vector3f& irPt
+)
+{
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ if( irPt[(int)pCurNode->mData.mSubDivPlane.mAxis] < pCurNode->mData.mSubDivPlane.mPosn )
+ {
+ return rSeekLeaf( iCurNodeOffset + pCurNode->LChildOffset(), irPt );
+ }
+ else
+ {
+ return rSeekLeaf( iCurNodeOffset + pCurNode->RChildOffset(), irPt );
+ }
+ }
+ else
+ {
+ return pCurNode->mData;
+ }
+}
+
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+void SpatialTreeIter::MarkAll( int iCurNodeOffset,
+ BoxPts& irBoxPts,
+ SphereSP& irDesiredVolume,
+ tMark iMark )
+{
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ SpatialNode* pFuckinCast;
+ pFuckinCast = &(pCurNode->mData);
+ mCurNodes.Add( pFuckinCast );
+ mNodeMarks[iCurNodeOffset] |= iMark;
+
+ if( pCurNode->GetSubTreeSize() == 0 ) //if leaf, marked, so leave
+ {
+ return;
+ }
+
+ float intersectResult = irDesiredVolume.CompareTo(pCurNode->mData.mSubDivPlane);
+ if( intersectResult == 0.0f ) //intersection
+ {
+ //split subspace representation, and recurse
+ BoxPts BoxPtsLT;
+ BoxPtsLT = irBoxPts;
+
+ BoxPtsLT.CutOffGT( pCurNode->mData.mSubDivPlane );
+
+ MarkAll( iCurNodeOffset + pCurNode->LChildOffset(), BoxPtsLT, irDesiredVolume, iMark );
+
+ irBoxPts.CutOffLT( pCurNode->mData.mSubDivPlane );
+
+ MarkAll( iCurNodeOffset + pCurNode->RChildOffset(), irBoxPts, irDesiredVolume, iMark );
+ }
+ else
+ {
+ if(intersectResult>0.0f) //Plane falls on the right, so recurse lt side
+ {
+ irBoxPts.CutOffGT( pCurNode->mData.mSubDivPlane );
+
+ MarkAll( iCurNodeOffset + pCurNode->LChildOffset(), irBoxPts, irDesiredVolume, iMark );
+ }
+ else //Plane falls on the left(lt), so recurse gt side
+ {
+ irBoxPts.CutOffLT( pCurNode->mData.mSubDivPlane );
+
+ MarkAll( iCurNodeOffset + pCurNode->RChildOffset(), irBoxPts, irDesiredVolume, iMark );
+ }
+ }
+#if 0
+ if( irBoxPts.TestNotOutside( irDesiredVolume ) < 0.0f )
+ {
+ //if any pt, or the volume defined by said pts of desired volume is in the AABB...
+ mNodeMarks[iCurNodeOffset] |= iMark;
+
+ //If all of the pts of the AABB are inside the Desired Volume,
+ //just mark the whole subtree, otherwise recurse
+
+ //BEGIN_PROFILE("Tree Compare")
+
+ int nPtsInside = 0;
+ // Ignore whole subtree marks until it pays off query volume vs subtree
+ // size... put test here to this effect
+ for( int i=0; i<8 && !nPtsInside; i++ )//
+ {
+ if( irDesiredVolume.CompareTo( irBoxPts[i] ) > 0.0f )
+ {
+ break;
+ }
+ //Pt is inside SpatialProxy
+ nPtsInside++;
+ }
+
+ //END_PROFILE("Tree Compare")
+
+ //debug: make this inclusion threshold a parameter
+ //if( nPtsInside > 5 && nPtsInside !=8 )
+ // nPtsInside = 8;
+
+ SpatialNode* pFuckinCast;
+ pFuckinCast = &(pCurNode->mData);
+ mCurNodes.Add( pFuckinCast );
+ // if the entire subspace is within the desired volume
+ switch( nPtsInside )
+ {
+ case 8:
+ //BEGIN_PROFILE("SubTree Marked")
+ mNodeMarks[iCurNodeOffset] |= iMark;
+ MarkSubTree( iCurNodeOffset, iMark );
+ //END_PROFILE("SubTree Marked")
+ break;
+
+ default:
+ mNodeMarks[iCurNodeOffset] |= iMark;
+ //partial intersection; recurse
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ //split subspace representation, and recurse
+ BoxPts BoxPtsLT;
+ BoxPtsLT = irBoxPts;
+
+ BoxPtsLT.CutOffGT( pCurNode->mData.mSubDivPlane );
+
+ MarkAll( iCurNodeOffset + pCurNode->LChildOffset(), BoxPtsLT, irDesiredVolume, iMark );
+
+ irBoxPts.CutOffLT( pCurNode->mData.mSubDivPlane );
+
+ MarkAll( iCurNodeOffset + pCurNode->RChildOffset(), irBoxPts, irDesiredVolume, iMark );
+ }
+ break;
+
+ }
+ } //else, no desired volume and AABB don't intersect, therefore do nothing
+#endif
+ /*
+ int nPtsInside = 0;
+ for( int i=0; i<8; i++ )
+ {
+ if( irDesiredVolume.CompareTo( irBoxPts[i] ) < 0.0f )
+ {
+ //Pt is inside SpatialProxy
+ nPtsInside++;
+ }
+ }
+
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ // if the entire subspace is within the desired volume
+ switch( nPtsInside )
+ {
+ case 8:
+ mNodeMarks[iCurNodeOffset] = iMark;
+ MarkSubTree( iCurNodeOffset, iMark );
+ break;
+
+ case 0:
+ //Find out whether the subtree is outside the volume
+ //or the subtree envelopes the volume
+// if( irBoxPts.CompareTo( irDesiredVolume.GetPoint() ) < 0.0f )
+// {
+// mNodeMarks[iCurNodeOffset] = iMark;
+// MarkSubTree( iCurNodeOffset, iMark );
+// }
+ //TODO: can reuse the code in default by saying if compare fails break (else continue, no break;).
+
+ if( irBoxPts.CompareTo( irDesiredVolume.GetPoint() ) < 0.0f )
+ //if( irBoxPts.TestNotOutside( irDesiredVolume ) < 0.0f ) //if any pt, or the volume defined by said pts of desired volume is in the AABB...
+ {
+ mNodeMarks[iCurNodeOffset] = iMark;
+
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ //split subspace representation, and recurse
+ BoxPts BoxPtsLT;
+ BoxPtsLT = irBoxPts;
+
+ BoxPtsLT.CutOffGT( pCurNode->mData.mSubDivPlane );
+
+ MarkAll( iCurNodeOffset + pCurNode->LChildOffset(), BoxPtsLT, irDesiredVolume, iMark );
+
+ irBoxPts.CutOffLT( pCurNode->mData.mSubDivPlane );
+
+ MarkAll( iCurNodeOffset + pCurNode->RChildOffset(), irBoxPts, irDesiredVolume, iMark );
+ }
+ }
+ break;
+
+
+ default:
+ mNodeMarks[iCurNodeOffset] = iMark;
+ //partial intersection; recurse
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ //split subspace representation, and recurse
+ BoxPts BoxPtsLT;
+ BoxPtsLT = irBoxPts;
+
+ BoxPtsLT.CutOffGT( pCurNode->mData.mSubDivPlane );
+
+ MarkAll( iCurNodeOffset + pCurNode->LChildOffset(), BoxPtsLT, irDesiredVolume, iMark );
+
+ irBoxPts.CutOffLT( pCurNode->mData.mSubDivPlane );
+
+ MarkAll( iCurNodeOffset + pCurNode->RChildOffset(), irBoxPts, irDesiredVolume, iMark );
+ }
+ break;
+ }
+ */
+}
+void SpatialTreeIter::MarkAllSphere
+(
+ int iCurNodeOffset,
+ BoxPts& irBoxPts,
+ SphereSP& irSphere,
+ tMark iMark
+)
+{
+ if( (iMark != 0x0f)
+ && (mNodeMarks[iCurNodeOffset] & msFilterInvisible) )
+ {
+ return;
+ }
+ if( (iMark != 0x0f)
+ && (mNodeMarks[iCurNodeOffset] & msFilterVisible) )
+ {
+ return;
+ //because this chunk will always be visible, and tracked during the marksubtrees
+ }
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ SpatialNode* pFuckinCast;
+ pFuckinCast = &(pCurNode->mData);
+ mCurNodes.Add( pFuckinCast );
+ mNodeMarks[iCurNodeOffset] |= iMark;
+
+ if( pCurNode->GetSubTreeSize() == 0 ) //if leaf, marked, so leave
+ {
+ return;
+ }
+
+
+ float intersectResult = irSphere.CompareTo(pCurNode->mData.mSubDivPlane);
+ if( intersectResult == 0.0f ) //intersection
+ {
+ //split subspace representation, and recurse
+ BoxPts BoxPtsLT;
+ BoxPtsLT = irBoxPts;
+
+ BoxPtsLT.CutOffGT( pCurNode->mData.mSubDivPlane );
+
+ MarkAllSphereIntersect( iCurNodeOffset + pCurNode->LChildOffset(), BoxPtsLT, irSphere, iMark );
+
+ irBoxPts.CutOffLT( pCurNode->mData.mSubDivPlane );
+
+ MarkAllSphereIntersect( iCurNodeOffset + pCurNode->RChildOffset(), irBoxPts, irSphere, iMark );
+ }
+ else
+ {
+ if(intersectResult>0.0f) //Plane falls on the right, so recurse lt side
+ {
+ irBoxPts.CutOffGT( pCurNode->mData.mSubDivPlane );
+
+ MarkAllSphere( iCurNodeOffset + pCurNode->LChildOffset(), irBoxPts, irSphere, iMark );
+ }
+ else //Plane falls on the left(lt), so recurse gt side
+ {
+ irBoxPts.CutOffLT( pCurNode->mData.mSubDivPlane );
+
+ MarkAllSphere( iCurNodeOffset + pCurNode->RChildOffset(), irBoxPts, irSphere, iMark );
+ }
+ }
+}
+
+void SpatialTreeIter::MarkAllSphereIntersect
+(
+ int iCurNodeOffset,
+ BoxPts& irBoxPts,
+ SphereSP& irSphere,
+ tMark iMark
+)
+{
+ if( (iMark != 0x0f)
+ && (mNodeMarks[iCurNodeOffset] & msFilterInvisible) )
+ {
+ return;
+ }
+ if( (iMark != 0x0f)
+ && (mNodeMarks[iCurNodeOffset] & msFilterVisible) )
+ {
+ return;
+ //because this chunk will always be visible, and tracked during the marksubtrees
+ }
+
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ if( !irBoxPts.mBounds.IntersectsSphere((Vector3f&)irSphere.mCenter, irSphere.mRadius) )
+ {
+ return;
+ }
+
+ SpatialNode* pFuckinCast;
+ pFuckinCast = &(pCurNode->mData);
+ mCurNodes.Add( pFuckinCast );
+ mNodeMarks[iCurNodeOffset] |= iMark;
+
+ if( pCurNode->GetSubTreeSize() == 0 ) //if leaf, marked, so leave
+ {
+ return;
+ }
+
+ float intersectResult = irSphere.CompareTo(pCurNode->mData.mSubDivPlane);
+ if( intersectResult == 0.0f ) //intersection
+ {
+ //split subspace representation, and recurse
+ BoxPts BoxPtsLT;
+ BoxPtsLT = irBoxPts;
+
+ BoxPtsLT.CutOffGT( pCurNode->mData.mSubDivPlane );
+
+ MarkAllSphereIntersect( iCurNodeOffset + pCurNode->LChildOffset(), BoxPtsLT, irSphere, iMark );
+
+ irBoxPts.CutOffLT( pCurNode->mData.mSubDivPlane );
+
+ MarkAllSphereIntersect( iCurNodeOffset + pCurNode->RChildOffset(), irBoxPts, irSphere, iMark );
+ }
+ else
+ {
+ if(intersectResult>0.0f) //Plane falls on the right, so recurse lt side
+ {
+ irBoxPts.CutOffGT( pCurNode->mData.mSubDivPlane );
+
+ MarkAllSphereIntersect( iCurNodeOffset + pCurNode->LChildOffset(), irBoxPts, irSphere, iMark );
+ }
+ else //Plane falls on the left(lt), so recurse gt side
+ {
+ irBoxPts.CutOffLT( pCurNode->mData.mSubDivPlane );
+
+ MarkAllSphereIntersect( iCurNodeOffset + pCurNode->RChildOffset(), irBoxPts, irSphere, iMark );
+ }
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void SpatialTreeIter::MarkSubTrees
+(
+ int iCurNodeOffset,
+ BoxPts& irBoxPts,
+ ISpatialProxyAA& irDesiredVolume,
+ tMark iMark
+)
+{
+ if(mNodeMarks[iCurNodeOffset] & iMark)
+ {
+ return;
+ }
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ SpatialNode* pFuckinCast;
+ pFuckinCast = &(pCurNode->mData);
+ //mCurNodes.Add( pFuckinCast );
+ //mNodeMarks[iCurNodeOffset] |= iMark;
+
+ //Test to see if the desired volume fully encompasses said subtree
+ if( irDesiredVolume.CompareToXZ(irBoxPts.mBounds.mMin) <= 0.0f )
+ {
+ if( irDesiredVolume.CompareToXZ(irBoxPts.mBounds.mMax) <= 0.0f )
+ {
+ mNodeMarks[iCurNodeOffset] |= iMark;
+ //It's sufficient to mark the roots of the subtrees, since queries
+ //are always performed top-down
+
+ if(iMark & msFilterVisible)
+ {
+ mpCurNodeList = &mCurAlwaysVisNodes;
+ MarkSubTree(iCurNodeOffset,iMark);
+ mpCurNodeList = &mCurNodes;
+ }
+ return;
+ }
+ }
+
+ if( pCurNode->GetSubTreeSize() == 0 ) //if leaf, leave
+ {
+ return;
+ }
+
+ float intersectResult = irDesiredVolume.CompareTo(pCurNode->mData.mSubDivPlane);
+ if( intersectResult == 0.0f ) //intersection
+ {
+ //split subspace representation, and recurse
+ BoxPts BoxPtsLT;
+ BoxPtsLT = irBoxPts;
+
+ BoxPtsLT.CutOffGT( pCurNode->mData.mSubDivPlane );
+
+ MarkSubTrees( iCurNodeOffset + pCurNode->LChildOffset(), BoxPtsLT, irDesiredVolume, iMark );
+
+ irBoxPts.CutOffLT( pCurNode->mData.mSubDivPlane );
+
+ MarkSubTrees( iCurNodeOffset + pCurNode->RChildOffset(), irBoxPts, irDesiredVolume, iMark );
+ }
+ else
+ {
+ if(intersectResult>0.0f) //Plane falls on the right, so recurse lt side
+ {
+ irBoxPts.CutOffGT( pCurNode->mData.mSubDivPlane );
+
+ MarkSubTrees( iCurNodeOffset + pCurNode->LChildOffset(), irBoxPts, irDesiredVolume, iMark );
+ }
+ else //Plane falls on the left(lt), so recurse gt side
+ {
+ irBoxPts.CutOffLT( pCurNode->mData.mSubDivPlane );
+
+ MarkSubTrees( iCurNodeOffset + pCurNode->RChildOffset(), irBoxPts, irDesiredVolume, iMark );
+ }
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void SpatialTreeIter::BuildBBoxes( BoxPts iBoxPts, int iCurNodeOffset )
+{
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ pCurNode->mData.mBBox = iBoxPts;
+
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ iBoxPts.CutOffGT(pCurNode->mData.mSubDivPlane);
+ BuildBBoxes(iBoxPts, iCurNodeOffset+pCurNode->LChildOffset());
+
+ iBoxPts = pCurNode->mData.mBBox;
+ iBoxPts.CutOffLT(pCurNode->mData.mSubDivPlane);
+ BuildBBoxes(iBoxPts, iCurNodeOffset+pCurNode->RChildOffset());
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/render/Culling/SpatialTreeIter.h b/game/code/render/Culling/SpatialTreeIter.h
new file mode 100644
index 0000000..59a101e
--- /dev/null
+++ b/game/code/render/Culling/SpatialTreeIter.h
@@ -0,0 +1,897 @@
+#ifndef __SPATIAL_TREE_ITER_H__
+#define __SPATIAL_TREE_ITER_H__
+
+#include <p3d/utility.hpp>
+#include <render/culling/SpatialTree.h>
+#include <render/culling/ISpatialProxy.h>
+#include <render/culling/SphereSP.h>
+#include <render/culling/Bounds.h>
+#include <render/culling/BoxPts.h>
+#include <render/culling/FixedArray.h>
+#include <render/culling/UseArray.h>
+
+//#include <render/culling/../../profiler/profiler.hpp>
+
+typedef int tMark;
+
+
+class SpatialTreeIter
+{
+public:
+ SpatialTreeIter();
+ ~SpatialTreeIter();
+
+ void SetToRoot( SpatialTree& irTree );
+ void Clear();
+
+ //Allocation&Placement Navigation
+/*
+ void Place( ISpatialProxyAA& irTVolume, T* ipT );
+ void ReservePlaceT( ISpatialProxyAA& irTVolume, int iCount );
+ void Place( ISpatialProxyAA& irTVolume, D* ipD );
+ void ReservePlaceD( ISpatialProxyAA& irTVolume, int iCount );
+ void Place( ISpatialProxyAA& irIVolume, I* ipI );
+ void ReservePlaceI( ISpatialProxyAA& irIVolume, int iCount );
+ void AllocateAllReservations();
+*/
+
+ //Node Retrieval Methods
+ SpatialNode& rSeekNode( ISpatialProxyAA& irTVolume,
+ int iCurNodeOffset=0 );
+ SpatialNode& rIthNode( int iIth );
+ int NumNodes();
+
+ void SeekSubNodes( ISpatialProxyAA& irTVolume,
+ UseArray<int>& orNodeOffsets,
+ int iCurNodeOffset=0 );
+ void SeekAllNodes( ISpatialProxyAA& irTVolume,
+ UseArray<int>& orNodeOffsets,
+ int iCurNodeOffset=0 );
+ void SetUpNodeList( UseArray<int>& orNodeOffsets );
+
+ //Iteration Initialization
+ void MarkAll( SphereSP& irDesiredVolume, tMark iMark );
+ void MarkAllSphere( SphereSP& irSphere, tMark iMark );
+ void MarkSubTrees( ISpatialProxyAA& irDesiredVolume, tMark iMark );
+ void MarkTree( tMark iMark );
+ void AndTree( tMark iMark );
+ void OrTree( tMark iMark );
+ void OrTreeVis( tMark iMark );
+
+
+ //Iteration Navigation
+ SpatialNode& rSeekLeaf( Vector3f& irPt );
+
+ void SetIterFilter( tMark iMark );
+ void MoveToFirst();
+
+ SpatialNode& rCurrent();
+ inline SpatialNode* pCurrent();
+
+ void MoveToNext(bool ibIncludeVis=false);
+ SpatialNode& rMoveToNext(bool ibIncludeVis=false);
+ SpatialNode* pMoveToNext(bool ibIncludeVis=false);
+
+ bool NotDone();
+ bool IsCurrentLeaf();
+
+ //Debug Functionality
+// void DisplayBoundingBox();
+ void DisplayCurrentBoundingBox( const tColour& irColour );
+ bool IsSetUp();
+ BoxPts* CurrentBBox();
+
+ BoxPts& rBBox();
+ void BuildBBoxes( BoxPts iBoxPts, int iCurNodeOffset = 0);
+
+ enum
+ {
+ msFilterInvisible = 0xF0000000,
+ msFilterVisible = 0x0F000000,
+ msFilterAll = 0x00FFFFFF
+ };
+
+ SwapArray<SpatialNode*> mCurNodes;
+ SwapArray<SpatialNode*> mCurAlwaysVisNodes;
+
+protected:
+ SwapArray<SpatialNode*>* mpCurNodeList;
+ ContiguousBinNode< SpatialNode >* mpRootNode;
+ ContiguousBinNode< SpatialNode >* mpCurNode;
+
+ int mCurNodeOffset;
+
+ FixedArray<tMark> mNodeMarks;
+ tMark mCurMarkFilter;
+
+ int mCurNodeI;
+
+ //Bounding Box is left as series of defining points to make iteration simpler
+ BoxPts mBBox;
+
+
+ SpatialNode& rSeekLeaf( int iCurNodeOffset, Vector3f& irPt );
+
+ void MarkAll( int ipCurNode,
+ BoxPts& irBoxPts,
+ SphereSP& irDesiredVolume,
+ tMark iMark );
+
+ void MarkSubTrees( int ipCurNode,
+ BoxPts& irBoxPts,
+ ISpatialProxyAA& irDesiredVolume,
+ tMark iMark );
+
+ void MarkAllSphere( int ipCurNode,
+ BoxPts& irBoxPts,
+ SphereSP& irSphere,
+ tMark iMark );
+
+ void MarkAllSphereIntersect( int ipCurNode,
+ BoxPts& irBoxPts,
+ SphereSP& irSphere,
+ tMark iMark );
+
+ void MarkSubTree( int iCurNodeOffset,
+ tMark iMark );
+
+/*
+ void Place( int iCurNodeOffset, ISpatialProxyAA& irTVolume, T* ipT );
+ void ReservePlaceT( int iCurNodeOffset, ISpatialProxyAA& irTVolume, int iCount );
+ void Place( int iCurNodeOffset, ISpatialProxyAA& irTVolume, D* ipD );
+ void ReservePlaceD( int iCurNodeOffset, ISpatialProxyAA& irTVolume, int iCount );
+ void Place( int iCurNodeOffset, ISpatialProxyAA& irIVolume, I* ipI );
+ void ReservePlaceI( int iCurNodeOffset, ISpatialProxyAA& irIVolume, int iCount );
+*/
+ //Debug functionality
+// void DisplayBoundingBox( int iCurNodeOffset, BoxPts& orBBox, tColour colour );
+ void DisplayBoundingBox( BoxPts& irBBox, tColour colour );
+
+};
+
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+SpatialTreeIter::SpatialTreeIter()
+: mpRootNode( NULL )
+{
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+bool SpatialTreeIter::IsSetUp()
+{
+ return ( mpRootNode != NULL );
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+SpatialTreeIter::~SpatialTreeIter()
+{
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::SetToRoot( SpatialTree& irTree )
+{
+ mpRootNode = irTree.GetRoot();
+
+ mBBox.SetTo( irTree.GetBounds() );
+
+ mNodeMarks.Allocate( mpRootNode->GetSubTreeSize()+1 );
+ mCurNodes.Allocate( mpRootNode->GetSubTreeSize()+1 );
+ mCurAlwaysVisNodes.Allocate( mpRootNode->GetSubTreeSize()+1 );
+ mpCurNodeList = &mCurNodes;
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::Clear()
+{
+ mpRootNode = NULL;
+}
+
+//========================================================================
+// SpatialTreeIter::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+inline
+SpatialNode& SpatialTreeIter::rIthNode
+(
+ int iIth
+)
+{
+ return (mpRootNode+iIth)->mData;
+}
+//========================================================================
+// SpatialTreeIter::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+inline
+int SpatialTreeIter::NumNodes
+(
+)
+{
+ return (mpRootNode->GetSubTreeSize()+1);
+}
+
+//========================================================================
+// SpatialTreeIter::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+inline
+void SpatialTreeIter::SetUpNodeList
+(
+ UseArray<int>& orNodeOffsets
+)
+{
+ orNodeOffsets.Allocate( mpRootNode->GetSubTreeSize()+1 );
+}
+/*
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::ReservePlaceD( ISpatialProxyAA& irDVolume, int iCount)
+{
+ ReservePlaceD( 0, irDVolume, iCount );
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::ReservePlaceD
+(
+ int iCurNodeOffset,
+ ISpatialProxyAA& irDVolume,
+ int iCount
+)
+{
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ float PlaneVolResult = irDVolume.CompareTo( pCurNode->mData.mSubDivPlane );
+
+ if( PlaneVolResult > 0.0f ) //The Plane is greater than TVolume in Posn, so TVolume is in the LT Partitiion..
+ {
+ ReservePlaceD( iCurNodeOffset + pCurNode->LChildOffset(), irDVolume, iCount );
+ }
+ else
+ {
+ if( PlaneVolResult < 0.0f ) //The Plane is less han TVolume in Posn, so TVolume is in the GT Partitiion..
+ {
+ ReservePlaceD( iCurNodeOffset + pCurNode->RChildOffset(), irDVolume, iCount );
+ }
+ else // PlaneVolResult == 0
+ {
+ //pCurNode = pCurNode->Parent();
+ pCurNode->mData.mDynamicElems.Reserve(iCount);
+ }
+ }
+ }
+ else
+ {
+ pCurNode->mData.mDynamicElems.Reserve(iCount);
+ }
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::ReservePlaceT( ISpatialProxyAA& irTVolume, int iCount)
+{
+ ReservePlaceT( 0, irTVolume, iCount );
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::ReservePlaceT
+(
+ int iCurNodeOffset,
+ ISpatialProxyAA& irTVolume,
+ int iCount
+)
+{
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ float PlaneVolResult = irTVolume.CompareTo( pCurNode->mData.mSubDivPlane );
+
+ if( PlaneVolResult > 0.0f ) //The Plane is greater than TVolume in Posn, so TVolume is in the LT Partitiion..
+ {
+ ReservePlaceT( iCurNodeOffset + pCurNode->LChildOffset(), irTVolume, iCount );
+ }
+ else
+ {
+ if( PlaneVolResult < 0.0f ) //The Plane is less han TVolume in Posn, so TVolume is in the GT Partitiion..
+ {
+ ReservePlaceT( iCurNodeOffset + pCurNode->RChildOffset(), irTVolume, iCount );
+ }
+ else // PlaneVolResult == 0
+ {
+ //pCurNode = pCurNode->Parent();
+ pCurNode->mData.mSpatialElems.Reserve(iCount);
+ }
+ }
+ }
+ else
+ {
+ pCurNode->mData.mSpatialElems.Reserve(iCount);
+ }
+}
+*/
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+SpatialNode& SpatialTreeIter::rSeekLeaf( Vector3f& irPt )
+{
+ return rSeekLeaf( 0, irPt );
+}
+/*
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::ReservePlaceI( ISpatialProxyAA& irIVolume, int iCount)
+{
+ ReservePlaceI( 0, irIVolume, iCount );
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::ReservePlaceI
+(
+ int iCurNodeOffset,
+ ISpatialProxyAA& irIVolume,
+ int iCount
+)
+{
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ float PlaneVolResult = irIVolume.CompareTo( pCurNode->mData.mSubDivPlane );
+
+ if( PlaneVolResult > 0.0f ) //The Plane is greater than TVolume in Posn, so TVolume is in the LT Partitiion..
+ {
+ ReservePlaceI( iCurNodeOffset + pCurNode->LChildOffset(), irIVolume, iCount );
+ }
+ else
+ {
+ if( PlaneVolResult < 0.0f ) //The Plane is less han TVolume in Posn, so TVolume is in the GT Partitiion..
+ {
+ ReservePlaceI( iCurNodeOffset + pCurNode->RChildOffset(), irIVolume, iCount );
+ }
+ else // PlaneVolResult == 0
+ {
+ //pCurNode = pCurNode->Parent();
+ pCurNode->mData.mIntersectElems.Reserve(iCount);
+ }
+ }
+ }
+ else
+ {
+ pCurNode->mData.mIntersectElems.Reserve(iCount);
+ }
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::Place( ISpatialProxyAA& irDVolume, D* ipD )
+{
+ Place( 0, irDVolume, ipD );
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::Place
+(
+ int iCurNodeOffset,
+ ISpatialProxyAA& irDVolume,
+ D* ipD
+)
+{
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ float PlaneVolResult = irDVolume.CompareTo( pCurNode->mData.mSubDivPlane );
+
+ if( PlaneVolResult > 0.0f ) //The Plane is greater than TVolume in Posn, so TVolume is in the LT Partitiion..
+ {
+ Place( iCurNodeOffset + pCurNode->LChildOffset(), irDVolume, ipD );
+ }
+ else
+ {
+ if( PlaneVolResult < 0.0f ) //The Plane is less han TVolume in Posn, so TVolume is in the GT Partitiion..
+ {
+ Place( iCurNodeOffset + pCurNode->RChildOffset(), irDVolume, ipD );
+ }
+ else // PlaneVolResult == 0
+ {
+ //pCurNode = pCurNode->Parent();
+ pCurNode->mData.mDynamicElems.Add(ipD);
+ }
+ }
+ }
+ else
+ {
+ pCurNode->mData.mDynamicElems.Add(ipD);
+ }
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::Place( ISpatialProxyAA& irTVolume, T* ipT )
+{
+ Place( 0, irTVolume, ipT );
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::Place
+(
+ int iCurNodeOffset,
+ ISpatialProxyAA& irTVolume,
+ T* ipT
+)
+{
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ float PlaneVolResult = irTVolume.CompareTo( pCurNode->mData.mSubDivPlane );
+
+ if( PlaneVolResult > 0.0f ) //The Plane is greater than TVolume in Posn, so TVolume is in the LT Partitiion..
+ {
+ Place( iCurNodeOffset + pCurNode->LChildOffset(), irTVolume, ipT );
+ }
+ else
+ {
+ if( PlaneVolResult < 0.0f ) //The Plane is less han TVolume in Posn, so TVolume is in the GT Partitiion..
+ {
+ Place( iCurNodeOffset + pCurNode->RChildOffset(), irTVolume, ipT );
+ }
+ else // PlaneVolResult == 0
+ {
+ //pCurNode = pCurNode->Parent();
+ pCurNode->mData.mSpatialElems.Add(ipT);
+ }
+ }
+ }
+ else
+ {
+ pCurNode->mData.mSpatialElems.Add(ipT);
+ }
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::Place( ISpatialProxyAA& irIVolume, I* ipI )
+{
+ Place( 0, irIVolume, ipI );
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::Place
+(
+ int iCurNodeOffset,
+ ISpatialProxyAA& irIVolume,
+ I* ipI
+)
+{
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ float PlaneVolResult = irIVolume.CompareTo( pCurNode->mData.mSubDivPlane );
+
+ if( PlaneVolResult > 0.0f ) //The Plane is greater than TVolume in Posn, so TVolume is in the LT Partitiion..
+ {
+ Place( iCurNodeOffset + pCurNode->LChildOffset(), irIVolume, ipI );
+ }
+ else
+ {
+ if( PlaneVolResult < 0.0f ) //The Plane is less han TVolume in Posn, so TVolume is in the GT Partitiion..
+ {
+ Place( iCurNodeOffset + pCurNode->RChildOffset(), irIVolume, ipI );
+ }
+ else // PlaneVolResult == 0
+ {
+ //pCurNode = pCurNode->Parent();
+ pCurNode->mData.mIntersectElems.Add(ipI);
+ }
+ }
+ }
+ else
+ {
+ pCurNode->mData.mIntersectElems.Add(ipI);
+ }
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::AllocateAllReservations()
+{
+ int TreeSize = mpRootNode->GetSubTreeSize();
+
+ for( int i=0; i<=TreeSize; i++ )
+ {
+ (mpRootNode+i)->mData.mSpatialElems.Allocate();
+ (mpRootNode+i)->mData.mDynamicElems.Allocate();
+ (mpRootNode+i)->mData.mIntersectElems.Allocate();
+ }
+}
+*/
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::MarkAll( SphereSP& irDesiredVolume, tMark iMark )
+{
+ mCurNodes.ClearUse();
+ BoxPts tmpBBox(mBBox);
+ MarkAll( 0, tmpBBox, irDesiredVolume, iMark );
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::MarkAllSphere( SphereSP& irSphere, tMark iMark )
+{
+ mCurNodes.ClearUse();
+ BoxPts tmpBBox(mBBox);
+ MarkAllSphere( 0, tmpBBox, irSphere, iMark );
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::MarkSubTrees( ISpatialProxyAA& irDesiredVolume, tMark iMark )
+{
+ BoxPts tmpBBox(mBBox);
+ MarkSubTrees( 0, tmpBBox, irDesiredVolume, iMark );
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+/*inline
+void SpatialTreeIter::CallBackAll( ISpatialProxyAA& irDesiredVolume, void (*pCB)(SpatialNode*) )
+{
+ BoxPts tmpBBox(mBBox);
+ CallBackAll( 0, tmpBBox, irDesiredVolume, pCB );
+}
+*/
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+BoxPts* SpatialTreeIter::CurrentBBox()
+{
+ return &(mpCurNode->mData.mBBox);
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+/*
+
+void SpatialTreeIter::DisplayBoundingBox( )
+{
+ BoxPts tmpBBox( mBBox );
+
+ DisplayBoundingBox( 0, tmpBBox, tColour(0,255,0) );
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+
+void SpatialTreeIter::DisplayBoundingBox( int iCurNodeOffset, BoxPts& orBBox, tColour colour )
+{
+ DisplayBoundingBox( orBBox, colour );
+
+
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+
+ if( pCurNode->GetSubTreeSize() > 0 )
+ {
+ //split subspace representation, and recurse
+ BoxPts BBoxLT;
+ BBoxLT = orBBox;
+
+ BBoxLT.CutOffGT( pCurNode->mData.mSubDivPlane );
+
+ DisplayBoundingBox( iCurNodeOffset + pCurNode->LChildOffset(), BBoxLT, colour );
+
+ orBBox.CutOffLT( pCurNode->mData.mSubDivPlane );
+
+ DisplayBoundingBox( iCurNodeOffset + pCurNode->RChildOffset(), orBBox, colour );
+ }
+}
+*/
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+inline
+BoxPts& SpatialTreeIter::rBBox()
+{
+ return mBBox;
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::DisplayBoundingBox( BoxPts& irBBox, tColour colour )
+{
+//#if PURE3D_VERSION_MAJOR < 15
+// tShader* testMat = p3d::find<tShader>("p3d_default");
+//#endif
+
+//#if PURE3D_VERSION_MAJOR < 15
+// pddiPrimStream* stream = p3d::pddi->BeginPrims(testMat->GetShader(), PDDI_PRIM_LINESTRIP, PDDI_V_C );
+//#else
+ pddiPrimStream* stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+//#endif
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMin.x, irBBox.mBounds.mMin.y, irBBox.mBounds.mMin.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMax.x, irBBox.mBounds.mMin.y, irBBox.mBounds.mMin.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMax.x, irBBox.mBounds.mMax.y, irBBox.mBounds.mMin.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMin.x, irBBox.mBounds.mMax.y, irBBox.mBounds.mMin.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMin.x, irBBox.mBounds.mMin.y, irBBox.mBounds.mMin.z);
+ p3d::pddi->EndPrims(stream);
+
+//#if PURE3D_VERSION_MAJOR < 15
+// stream = p3d::pddi->BeginPrims(testMat->GetShader(), PDDI_PRIM_LINESTRIP, PDDI_V_C );
+//#else
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+//#endif
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMax.x, irBBox.mBounds.mMax.y, irBBox.mBounds.mMax.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMin.x, irBBox.mBounds.mMax.y, irBBox.mBounds.mMax.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMin.x, irBBox.mBounds.mMin.y, irBBox.mBounds.mMax.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMax.x, irBBox.mBounds.mMin.y, irBBox.mBounds.mMax.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMax.x, irBBox.mBounds.mMax.y, irBBox.mBounds.mMax.z);
+ p3d::pddi->EndPrims(stream);
+
+//#if PURE3D_VERSION_MAJOR < 15
+// stream = p3d::pddi->BeginPrims(testMat->GetShader(), PDDI_PRIM_LINESTRIP, PDDI_V_C );
+//#else
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+//#endif
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMax.x, irBBox.mBounds.mMax.y, irBBox.mBounds.mMax.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMax.x, irBBox.mBounds.mMin.y, irBBox.mBounds.mMax.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMax.x, irBBox.mBounds.mMin.y, irBBox.mBounds.mMin.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMax.x, irBBox.mBounds.mMax.y, irBBox.mBounds.mMin.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMax.x, irBBox.mBounds.mMax.y, irBBox.mBounds.mMax.z);
+ p3d::pddi->EndPrims(stream);
+
+//#if PURE3D_VERSION_MAJOR < 15
+// stream = p3d::pddi->BeginPrims(testMat->GetShader(), PDDI_PRIM_LINESTRIP, PDDI_V_C );
+//#else
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+//#endif
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMin.x, irBBox.mBounds.mMax.y, irBBox.mBounds.mMax.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMin.x, irBBox.mBounds.mMin.y, irBBox.mBounds.mMax.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMin.x, irBBox.mBounds.mMin.y, irBBox.mBounds.mMin.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMin.x, irBBox.mBounds.mMax.y, irBBox.mBounds.mMin.z);
+ stream->Colour(colour);
+ stream->Coord(irBBox.mBounds.mMin.x, irBBox.mBounds.mMax.y, irBBox.mBounds.mMax.z);
+ p3d::pddi->EndPrims(stream);
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+SpatialNode* SpatialTreeIter::pCurrent()
+{
+ return (*mpCurNodeList)[mCurNodeI];
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::DisplayCurrentBoundingBox( const tColour& irColour )
+{
+ DisplayBoundingBox( pCurrent()->mBBox, irColour );
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::MarkTree( tMark iMark )
+{
+ for( int i=mNodeMarks.mSize-1; i>= 0; i-- )
+ {
+ mNodeMarks[i] = iMark;
+ }
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::AndTree( tMark iMark )
+{
+ //If we're clearing all flags including the visibility flags, then we want
+ //to clear away our extra visible set of nodes
+ if(iMark==0x00000000)
+ {
+ mCurAlwaysVisNodes.ClearUse();
+ }
+
+ for( int i=mNodeMarks.mSize-1; i>= 0; i-- )
+ {
+ mNodeMarks[i] &= iMark;
+ }
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::OrTree( tMark iMark )
+{
+ for( int i=mNodeMarks.mSize-1; i>= 0; i-- )
+ {
+ mNodeMarks[i] |= iMark;
+ }
+}
+inline
+void SpatialTreeIter::OrTreeVis( tMark iMark )
+{
+ mCurNodes.ClearUse();
+ SpatialNode* pFuckinCast;
+ for( int i=mNodeMarks.mSize-1; i>= 0; i-- )
+ {
+ mNodeMarks[i] |= iMark;
+ pFuckinCast = &(mpRootNode[i].mData);
+ mCurNodes.Add( pFuckinCast );
+ }
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::MarkSubTree( int iCurNodeOffset,
+ tMark iMark )
+{
+ SpatialNode* pSNCastPtr;
+ ContiguousBinNode< SpatialNode >* pCurNode = (mpRootNode+iCurNodeOffset);
+ int StopCondition = iCurNodeOffset+pCurNode->GetSubTreeSize();
+ for( int i=iCurNodeOffset; i<=StopCondition; i++, pCurNode++ )
+ {
+ if((iMark & msFilterVisible)&&(mNodeMarks[i] & msFilterInvisible))
+ {
+ //Skip over invisible branches, if we're doing an visible pass
+ i+=pCurNode->GetSubTreeSize();
+ pCurNode+=pCurNode->GetSubTreeSize();
+ }
+ else
+ {
+ mNodeMarks[i] |= iMark;
+ pSNCastPtr = &(pCurNode->mData);
+ (*mpCurNodeList).Add( pSNCastPtr );
+ }
+ }
+
+
+/* SpatialNode* pSNCastPtr;
+
+ for( int i=mpRootNode->GetSubTreeSize(); i>=iCurNodeOffset; i-- )
+ {
+ mNodeMarks[i] |= iMark;
+ pSNCastPtr = &(this->rIthNode(i));
+ mCurNodes.Add(pSNCastPtr);
+ }
+ */
+/*
+ mNodeMarks[iCurNodeOffset] = iMark;
+
+ if( (mpRootNode+iCurNodeOffset)->GetSubTreeSize() > 0 )
+ {
+ int newOffset = (mpRootNode+iCurNodeOffset)->LChildOffset();
+ MarkSubTree( newOffset + iCurNodeOffset, iMark );
+
+ newOffset = (mpRootNode+iCurNodeOffset)->RChildOffset();
+ MarkSubTree( newOffset + iCurNodeOffset, iMark );
+ }
+*/
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::SetIterFilter( tMark iMark )
+{
+ mCurMarkFilter = iMark;
+}
+/////////////////////////////////////////////////////////////////////////////////////
+//-------------------------Iteration Navigation------------------------------------//
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::MoveToFirst()
+{
+ mpCurNode = mpRootNode;
+ mCurNodeOffset = 0;
+
+ mCurNodeI = 0;
+ mpCurNodeList = &mCurNodes;
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+SpatialNode& SpatialTreeIter::rCurrent()
+{
+ return *(*mpCurNodeList)[mCurNodeI];
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+void SpatialTreeIter::MoveToNext(bool ibIncludeVis)
+{
+ mCurNodeI++;
+ if(ibIncludeVis
+ && (mCurNodeI >= (*mpCurNodeList).mUseSize)
+ && (mpCurNodeList != &mCurAlwaysVisNodes))
+ {
+ mpCurNodeList= &mCurAlwaysVisNodes;
+ mCurNodeI=0;
+ }
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+SpatialNode& SpatialTreeIter::rMoveToNext(bool ibIncludeVis)
+{
+ MoveToNext(ibIncludeVis);
+ return *(*mpCurNodeList)[mCurNodeI];
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+SpatialNode* SpatialTreeIter::pMoveToNext(bool ibIncludeVis)
+{
+ MoveToNext(ibIncludeVis);
+ return (*mpCurNodeList)[mCurNodeI];
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+bool SpatialTreeIter::NotDone()
+{
+ return (mCurNodeI < (*mpCurNodeList).mUseSize);
+}
+/////////////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////////////
+inline
+bool SpatialTreeIter::IsCurrentLeaf()
+{
+ return ((*mpCurNodeList)[mCurNodeI]->mSubDivPlane.mAxis == -1);
+}
+
+
+#endif
diff --git a/game/code/render/Culling/SphereSP.cpp b/game/code/render/Culling/SphereSP.cpp
new file mode 100644
index 0000000..7c9ef8a
--- /dev/null
+++ b/game/code/render/Culling/SphereSP.cpp
@@ -0,0 +1,252 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: SphereSP.cpp
+//
+// Description: Implementation for SphereSP class.
+//
+// History: Implemented --Devin [5/29/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <render/Culling/SphereSP.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : SphereSP Interface
+//
+//************************************************************************
+//========================================================================
+// SphereSP::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+SphereSP::SphereSP()
+{
+}
+//========================================================================
+// SphereSP::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+SphereSP::~SphereSP()
+{
+}
+//////////////////////////////////////////////////////////////////////////
+// ISpatialProxyAA
+// <0.0 - Inside Spatial Proxy
+// =0.0 - On Spatial Proxy Surface
+// >0.0 - Outside Spatial Proxy
+//////////////////////////////////////////////////////////////////////////
+//========================================================================
+// SphereSP::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+/*
+float SphereSP::CompareTo( AAPlane3f& irPlane )
+{
+ if( irPlane.mPosn - msIntersectionEpsilon <= mBounds.mMin[(int)irPlane.mAxis] )
+ {
+ return -1.0f;
+ }
+ if( irPlane.mPosn + msIntersectionEpsilon >= mBounds.mMax[(int)irPlane.mAxis] )
+ {
+ return 1.0f;
+ }
+
+ return 0.0f;
+}
+*/
+//========================================================================
+// SphereSP::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+float SphereSP::CompareTo( const Vector3f& irPoint )
+{
+ float distance = (mRadius+msIntersectionEpsilon)*(mRadius+msIntersectionEpsilon);
+ Vector3f temp;
+ temp.Sub(irPoint,mCenter);
+ float result = temp.MagnitudeSqr();
+ if( result < distance )
+ {
+ return -1.0f;
+ }
+ if( result > distance )
+ {
+ return 1.0f;
+ }
+ return 0.0f;
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+float SphereSP::CompareToXZ( const Vector3f& irPoint )
+{
+ float distance = (mRadius+msIntersectionEpsilon)*(mRadius+msIntersectionEpsilon);
+ Vector3f temp;
+ temp.Sub(irPoint,mCenter);
+ temp.y=0.0f;
+ float result = temp.MagnitudeSqr();
+ if( result < distance )
+ {
+ return -1.0f;
+ }
+ if( result > distance )
+ {
+ return 1.0f;
+ }
+ return 0.0f;
+}
+//========================================================================
+// SphereSP::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+float SphereSP::CompareTo( AAPlane3f& irPlane )
+{
+ if( irPlane.mPosn > mCenter[(int)irPlane.mAxis]+mRadius+msIntersectionEpsilon )
+ {
+ return 1.0f;
+ }
+ if( irPlane.mPosn < mCenter[(int)irPlane.mAxis]-mRadius-msIntersectionEpsilon )
+ {
+ return -1.0f;
+ }
+ return 0.0f;
+}
+//========================================================================
+// SphereSP::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+float SphereSP::TestNotOutside( ISpatialProxyAA& irSpatialProxy )
+{
+ for( int i=irSpatialProxy.nPts()-1; i>-1; i-- )
+ {
+ if( CompareTo(irSpatialProxy.mPt(i)) < 0.0f )
+ {
+ //Inside
+ return -1.0f;
+ }
+ }
+ //Outside
+ return 1.0f;
+
+}
+//========================================================================
+// SphereSP::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void SphereSP::SetTo( Bounds3f& irBounds )
+{
+ rAssert(false);
+}
+//========================================================================
+// SphereSP::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void SphereSP::SetTo( rmt::Vector irCenter, float iRadius )
+{
+ mCenter = irCenter;
+ mRadius = iRadius;
+
+ mBounds.mMin.SetTo( irCenter );
+ mBounds.mMin -= iRadius;
+ mBounds.mMax.SetTo( irCenter );
+ mBounds.mMax += iRadius;
+}
+
+//************************************************************************
+//
+// Protected Member Functions : SphereSP
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : SphereSP
+//
+//************************************************************************
+
+
+
+
diff --git a/game/code/render/Culling/SphereSP.h b/game/code/render/Culling/SphereSP.h
new file mode 100644
index 0000000..4a796e8
--- /dev/null
+++ b/game/code/render/Culling/SphereSP.h
@@ -0,0 +1,120 @@
+#ifndef __SphereSP_H__
+#define __SphereSP_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SphereSP
+//
+// Description: The SphereSP does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/29]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/culling/Vector3f.h>
+#include <render/culling/Plane3f.h>
+#include <render/culling/Bounds.h>
+
+#include <render/culling/ISpatialProxy.h>
+#include <render/culling/BoxPts.h>
+
+//========================================================================
+//
+// Synopsis: The SphereSP; Synopsis by Inspection.
+//
+//========================================================================
+class SphereSP : public BoxPts
+{
+public:
+ SphereSP();
+ ~SphereSP();
+
+ // This SpatialProxy is...
+ // <0.0 - Less than comparison object in posn
+ // =0.0 - Intersection with comparison object
+ // >0.0 - Greater than comparison object in posn
+ float CompareTo( AAPlane3f& irPlane );
+ // ISpatialProxyAA
+ // <0.0 - Inside Spatial Proxy
+ // =0.0 - On Spatial Proxy Surface
+ // >0.0 - Outside Spatial Proxy
+ virtual float CompareTo( const Vector3f& irPoint );
+ virtual float CompareToXZ( const Vector3f& irPoint );
+ virtual float TestNotOutside( ISpatialProxyAA& irSpatialProxy );
+
+ virtual void SetTo( Bounds3f& irBounds );
+ void SetTo( rmt::Vector irCenter, float iRadius );
+
+ rmt::Vector mCenter;
+ float mRadius;
+private:
+};
+
+#endif
+/*
+#ifndef __BOX_PTS_H__
+#define __BOX_PTS_H__
+
+#include <render/culling/Vector3f.h>
+#include <render/culling/Plane3f.h>
+#include <render/culling/Bounds.h>
+
+#include <render/culling/ISpatialProxy.h>
+
+class BoxPts : public ISpatialProxyAA
+{
+public:
+ BoxPts(){}
+ ~BoxPts(){}
+
+ // ISpatialProxyAA
+ // <0.0 - Inside Spatial Proxy
+ // =0.0 - On Spatial Proxy Surface
+ // >0.0 - Outside Spatial Proxy
+ virtual float CompareTo( AAPlane3f& irPlane );
+ virtual float CompareTo( const Vector3f& irPoint );
+
+ virtual float TestNotOutside( ISpatialProxyAA& irSpatialProxy );
+
+ virtual int nPts();
+ virtual Vector3f mPt( int iIndex );
+
+ virtual bool DoesIntersect( AAPlane3f& irClipPlane );
+ virtual bool DoesntIntersect( AAPlane3f& irClipPlane );
+
+ //Get a point representing some point within the SpatialProxy
+ virtual Vector3f GetPoint();
+
+ void CutOffGT( AAPlane3f& irPlane3f );
+ void CutOffLT( AAPlane3f& irPlane3f );
+
+ void SetTo( Bounds3f& irBounds );
+
+ Vector3f operator[]( int i );
+
+
+ float TestNotOutsideMinX( ISpatialProxyAA& irSpatialProxy );
+ float TestNotOutsideMinY( ISpatialProxyAA& irSpatialProxy );
+ float TestNotOutsideMinZ( ISpatialProxyAA& irSpatialProxy );
+ float TestNotOutsideMaxX( ISpatialProxyAA& irSpatialProxy );
+ float TestNotOutsideMaxY( ISpatialProxyAA& irSpatialProxy );
+ float TestNotOutsideMaxZ( ISpatialProxyAA& irSpatialProxy );
+
+ enum
+ {
+ msPtCount = 8
+ };
+
+ Bounds3f mBounds;
+protected:
+};
+#endif
+*/ \ No newline at end of file
diff --git a/game/code/render/Culling/SwapArray.h b/game/code/render/Culling/SwapArray.h
new file mode 100644
index 0000000..9338a66
--- /dev/null
+++ b/game/code/render/Culling/SwapArray.h
@@ -0,0 +1,386 @@
+#ifndef __SWAP_ARRAY_H__
+#define __SWAP_ARRAY_H__
+
+#include <p3d/p3dtypes.hpp>
+
+#ifndef TOOLS
+//#include "../../memory/srrmemory.h"
+#endif
+
+#define ASSSERT(s) rTuneAssert(s)
+
+template <class T> class SwapArray
+{
+public:
+
+ /////////////////////////////////////////
+ // Constructors/Destructors
+ /////////////////////////////////////////
+ ~SwapArray()
+ {
+ Clear();
+ }
+
+ SwapArray() : mUseSize(0), mpData(NULL)
+ {
+ }
+
+ SwapArray( int iSize ) :
+ mUseSize(0),
+ mpData(NULL)
+ {
+ Allocate( iSize );
+ }
+
+
+ /////////////////////////////////////////
+ // Main Methods
+ /////////////////////////////////////////
+ void Remove( int iIndex )
+ {
+ //A pointer safe swap-out
+ ASSSERT( (iIndex<mUseSize) );
+ mSwapT = mpData[iIndex];
+ mpData[iIndex] = mpData[mUseSize-1];
+ mpData[mUseSize-1] = mSwapT;
+ mUseSize--;
+ }
+
+ void RemoveKeepOrder( int iIndex )
+ {
+ //A pointer safe swap-out
+ ASSSERT( (iIndex<mUseSize) );
+ mSwapT = mpData[iIndex];
+ while(iIndex<mUseSize-1)
+ {
+ mpData[iIndex] = mpData[iIndex+1];
+ iIndex++;
+ }
+ mpData[mUseSize-1] = mSwapT;
+ mUseSize--;
+ }
+
+ void Swap( int iIndex1, int iIndex2 )
+ {
+ //A pointer safe swap-out
+ ASSSERT( (iIndex1<mUseSize) && (iIndex2<mUseSize) );
+ mSwapT = mpData[iIndex1];
+ mpData[iIndex1] = mpData[iIndex2];
+ mpData[iIndex2] = mSwapT;
+ }
+
+ void Init( int iIndex, T& irVal )
+ {
+ ASSSERT( (iIndex>=mUseSize)&&(iIndex<mSize) );
+ mUseSize = iIndex+1;
+ mpData[iIndex] = irVal;
+ }
+
+ void ClearUse()
+ {
+ ASSSERT(IsSetUp());
+ mUseSize = 0;
+ }
+
+ void Use( int iIndex )
+ {
+ ASSSERT( (iIndex>=mUseSize)&&(iIndex<mSize) );
+ mUseSize = iIndex+1;
+ }
+
+ void AddUse( int iCountSize )
+ {
+ ASSSERT( (iCountSize+mUseSize)<=mSize );
+ mUseSize += iCountSize;
+ }
+
+ void Add( T& irVal )
+ {
+ ASSSERT(mUseSize<mSize);
+ mpData[mUseSize] = irVal;
+ mUseSize++;
+ }
+
+ void Add( const T& irVal )
+ {
+ ASSSERT(mUseSize<mSize);
+ mpData[mUseSize] = irVal;
+ mUseSize++;
+ }
+
+ T& operator[]( int iIndex )
+ {
+ ASSSERT( (iIndex < mUseSize) && (iIndex > -1));
+ return mpData[iIndex];
+ }
+
+ const T& operator[]( int iIndex )const
+ {
+ ASSSERT( (iIndex < mUseSize) && (iIndex > -1));
+ return mpData[iIndex];
+ }
+
+ void Reserve( int iCount )
+ {
+ // UseSize is used during the
+ // unallocated state to count the reservations
+ ASSSERT( !IsSetUp() );
+ mUseSize += iCount;
+ }
+
+ void Allocate()
+ {
+ ASSSERT( !IsSetUp() );
+ if( mUseSize == 0 )
+ {
+ mSize = mUseSize;
+ mpData = NULL;
+ }
+ else
+ {
+ mSize = mUseSize;
+ mpData = new T[mSize];
+ ASSSERT(mSize>0);
+ ASSSERT(mpData!=NULL);
+ mUseSize = 0;
+ }
+ }
+
+ void Allocate( int iSize )
+ {
+ ASSSERT( mUseSize == 0 );
+ //TODO: wha?
+ if( !IsSetUp() )
+ iSize += mUseSize;
+ Clear();
+ mSize = iSize;
+ mpData = new T[mSize];
+ ASSSERT(mSize>0);
+ ASSSERT(mpData!=NULL);
+ }
+
+ void Clear()
+ {
+ if( mpData != NULL )
+ {
+ delete[] mpData;
+ }
+ mpData = NULL;
+ mUseSize = 0;
+ }
+
+ bool IsSetUp()
+ {
+ if( mpData == NULL )
+ return false;
+ else
+ return true;
+ }
+
+ /////////////////////////////////////////
+ // Data
+ /////////////////////////////////////////
+ int mSize;
+ int mUseSize;
+ T* mpData;
+ T mSwapT;
+
+protected:
+
+private:
+ /////////////////////////////////////////
+ // Currently Disallowed Functions
+ // -Not implemented
+ // -Not accessible
+ // -Use will error at Compile
+ /////////////////////////////////////////
+ SwapArray( const SwapArray& iSource );
+ SwapArray& operator=( const SwapArray& iSource );
+
+};
+
+template <class T> class NodeSwapArray
+{
+public:
+
+ /////////////////////////////////////////
+ // Constructors/Destructors
+ /////////////////////////////////////////
+ ~NodeSwapArray()
+ {
+ Clear();
+ }
+
+ NodeSwapArray() : mUseSize(0), mpData(NULL)
+ {
+ }
+
+ NodeSwapArray( int iSize ) :
+ mUseSize(0),
+ mpData(NULL)
+ {
+ Allocate( iSize );
+ }
+
+
+ /////////////////////////////////////////
+ // Main Methods
+ /////////////////////////////////////////
+ void Remove( int iIndex )
+ {
+ //A pointer safe swap-out
+ ASSSERT( (iIndex<mUseSize) );
+ mSwapT = mpData[iIndex];
+ mpData[iIndex] = mpData[mUseSize-1];
+ mpData[mUseSize-1] = mSwapT;
+ mUseSize--;
+ }
+
+ void RemoveKeepOrder( int iIndex )
+ {
+ //A pointer safe swap-out
+ ASSSERT( (iIndex<mUseSize) );
+ mSwapT = mpData[iIndex];
+ while(iIndex<mUseSize-1)
+ {
+ mpData[iIndex] = mpData[iIndex+1];
+ iIndex++;
+ }
+ mpData[mUseSize-1] = mSwapT;
+ mUseSize--;
+ }
+
+ void Swap( int iIndex1, int iIndex2 )
+ {
+ //A pointer safe swap-out
+ ASSSERT( (iIndex1<mUseSize) && (iIndex2<mUseSize) );
+ mSwapT = mpData[iIndex1];
+ mpData[iIndex1] = mpData[iIndex2];
+ mpData[iIndex2] = mSwapT;
+ }
+
+ void Init( int iIndex, T& irVal )
+ {
+ ASSSERT( (iIndex>=mUseSize)&&(iIndex<mSize) );
+ mUseSize = iIndex+1;
+ mpData[iIndex] = irVal;
+ }
+
+ void ClearUse()
+ {
+ ASSSERT(IsSetUp());
+ mUseSize = 0;
+ }
+
+ void Use( int iIndex )
+ {
+ ASSSERT( (iIndex>=mUseSize)&&(iIndex<mSize) );
+ mUseSize = iIndex+1;
+ }
+
+ void AddUse( int iCountSize )
+ {
+ ASSSERT( (iCountSize+mUseSize)<=mSize );
+ mUseSize += iCountSize;
+ }
+
+ bool Add( T& irVal )
+ {
+ if(mUseSize>=mSize)
+ return false;
+ mpData[mUseSize] = irVal;
+ mUseSize++;
+ return true;
+ }
+
+ T& operator[]( int iIndex )
+ {
+ ASSSERT( (iIndex < mUseSize) && (iIndex > -1));
+ return mpData[iIndex];
+ }
+
+ const T& operator[]( int iIndex )const
+ {
+ ASSSERT( (iIndex < mUseSize) && (iIndex > -1));
+ return mpData[iIndex];
+ }
+
+ void Reserve( int iCount )
+ {
+ // UseSize is used during the
+ // unallocated state to count the reservations
+ ASSSERT( !IsSetUp() );
+ mUseSize += iCount;
+ }
+
+ void Allocate()
+ {
+ ASSSERT( !IsSetUp() );
+ if( mUseSize == 0 )
+ {
+ mSize = mUseSize;
+ mpData = NULL;
+ }
+ else
+ {
+ mSize = mUseSize;
+ mpData = new T[mSize];
+ ASSSERT(mSize>0);
+ ASSSERT(mpData!=NULL);
+ mUseSize = 0;
+ }
+ }
+
+ void Allocate( int iSize )
+ {
+ ASSSERT( mUseSize == 0 );
+ //TODO: wha?
+ if( !IsSetUp() )
+ iSize += mUseSize;
+ Clear();
+ mSize = iSize;
+ mpData = new T[mSize];
+ ASSSERT(mSize>0);
+ ASSSERT(mpData!=NULL);
+ }
+
+ void Clear()
+ {
+ if( mpData != NULL )
+ {
+ delete[] mpData;
+ }
+ mpData = NULL;
+ mUseSize = 0;
+ }
+
+ bool IsSetUp()
+ {
+ if( mpData == NULL )
+ return false;
+ else
+ return true;
+ }
+
+ /////////////////////////////////////////
+ // Data
+ /////////////////////////////////////////
+ int mSize;
+ int mUseSize;
+ T* mpData;
+ T mSwapT;
+
+protected:
+
+private:
+ /////////////////////////////////////////
+ // Currently Disallowed Functions
+ // -Not implemented
+ // -Not accessible
+ // -Use will error at Compile
+ /////////////////////////////////////////
+ NodeSwapArray( const NodeSwapArray& iSource );
+ NodeSwapArray& operator=( const NodeSwapArray& iSource );
+
+};
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/UseArray.h b/game/code/render/Culling/UseArray.h
new file mode 100644
index 0000000..e3b5345
--- /dev/null
+++ b/game/code/render/Culling/UseArray.h
@@ -0,0 +1,119 @@
+#ifndef __USE_ARRAY_H__
+#define __USE_ARRAY_H__
+
+#include <p3d/p3dtypes.hpp>
+#include <raddebug.hpp>
+
+#ifndef TOOLS
+#include <memory/srrmemory.h>
+#endif
+
+template <class T> class UseArray
+{
+public:
+
+ /////////////////////////////////////////
+ // Constructors/Destructors
+ /////////////////////////////////////////
+ ~UseArray()
+ {
+ Clear();
+ }
+
+ UseArray() : mpData(NULL), mUseSize(0)
+ {
+ }
+
+ UseArray( int iSize ) : mpData(NULL)
+ {
+ Allocate( iSize );
+ }
+
+
+ /////////////////////////////////////////
+ // Main Methods
+ /////////////////////////////////////////
+ void Init( int iIndex, T& irVal )
+ {
+ rAssert( (iIndex>=mUseSize)&&(iIndex<mSize) );
+ mUseSize = iIndex+1;
+ mpData[iIndex] = irVal;
+ }
+
+ void ClearUse()
+ {
+ rAssert(IsSetUp());
+ mUseSize = 0;
+ }
+
+ void Add( T& irVal )
+ {
+ rAssert(mUseSize<mSize);
+ mpData[mUseSize] = irVal;
+ mUseSize++;
+ }
+
+ T& operator[]( int iIndex )
+ {
+ rAssert( (iIndex < mUseSize) && (iIndex > -1));
+ return mpData[iIndex];
+ }
+
+ void Allocate( int iSize )
+ {
+ Clear();
+ mSize = iSize;
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#endif
+
+ mpData = new T[mSize];
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+#endif
+ rAssert(iSize>0);
+ rAssert(mpData!=NULL);
+ mUseSize = 0;
+ }
+
+ void Clear()
+ {
+ if( mpData != NULL )
+ {
+ delete[] mpData;
+ }
+ mpData = NULL;
+ mUseSize = 0;
+ }
+
+ bool IsSetUp()
+ {
+ if( mpData == NULL )
+ return false;
+ else
+ return true;
+ }
+
+ /////////////////////////////////////////
+ // Data
+ /////////////////////////////////////////
+ int mSize;
+ T* mpData;
+ int mUseSize;
+
+protected:
+
+private:
+ /////////////////////////////////////////
+ // Currently Disallowed Functions
+ // -Not implemented
+ // -Not accessible
+ // -Use will error at Compile
+ /////////////////////////////////////////
+ UseArray( const UseArray& iSource );
+ UseArray& operator=( const UseArray& iSource );
+
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/Vector3f.h b/game/code/render/Culling/Vector3f.h
new file mode 100644
index 0000000..1555891
--- /dev/null
+++ b/game/code/render/Culling/Vector3f.h
@@ -0,0 +1,80 @@
+#ifndef __VECTOR_3F_H__
+#define __VECTOR_3F_H__
+
+#include <radmath/radmath.hpp>
+#include <raddebug.hpp>
+
+class Vector3f : public rmt::Vector
+{
+public:
+ Vector3f() : Vector() {}
+ Vector3f( float iX, float iY, float iZ ) : Vector( iX, iY, iZ ) {}
+ Vector3f( float* ipVector ) : Vector( ipVector[0], ipVector[1], ipVector[2] ) {}
+
+ Vector& SetTo( const Vector& vect )
+ {
+ x = vect.x;
+ y = vect.y;
+ z = vect.z;
+ return *this;
+ }
+
+ Vector& Div(const Vector& vect1, const Vector& vect2)
+ {
+ x = vect1.x / vect2.x;
+ y = vect1.y / vect2.y;
+ z = vect1.z / vect2.z;
+ return *this;
+ }
+
+ Vector& Div(const Vector& vect1, const float iMag )
+ {
+ x = vect1.x / iMag;
+ y = vect1.y / iMag;
+ z = vect1.z / iMag;
+ return *this;
+ }
+
+ Vector& Mult(const Vector& vect1, const float iMag)
+ {
+ x = vect1.x * iMag;
+ y = vect1.y * iMag;
+ z = vect1.z * iMag;
+ return *this;
+ }
+
+ Vector& Mult( const float iMag)
+ {
+ x *= iMag;
+ y *= iMag;
+ z *= iMag;
+ return *this;
+ }
+
+ void SetFP( float* ipVector )
+ {
+ x = ipVector[0];
+ y = ipVector[1];
+ z = ipVector[2];
+ }
+
+ Vector& operator-=( const float& right )
+ {
+ x -= right;
+ y -= right;
+ z -= right;
+ return *this;
+ }
+
+ Vector& operator+=( const float& right )
+ {
+ x += right;
+ y += right;
+ z += right;
+ return *this;
+ }
+
+protected:
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/Vector3i.h b/game/code/render/Culling/Vector3i.h
new file mode 100644
index 0000000..592126d
--- /dev/null
+++ b/game/code/render/Culling/Vector3i.h
@@ -0,0 +1,160 @@
+#ifndef __VECTOR_3I_H__
+#define __VECTOR_3I_H__
+
+#include <render/culling/FloatFuncs.h>
+
+#include <render/culling/Vector3f.h>
+
+class Vector3i
+{
+public:
+ Vector3i(){}
+ ~Vector3i(){}
+
+ void Set( int iX, int iY, int iZ )
+ {
+ mX = iX;
+ mY = iY;
+ mZ = iZ;
+ }
+
+ void Set( int* iCoords )
+ {
+ mX = iCoords[0];
+ mY = iCoords[1];
+ mZ = iCoords[2];
+ }
+
+ void Set( Vector3i& irVector )
+ {
+ mX = irVector.mX;
+ mY = irVector.mY;
+ mZ = irVector.mZ;
+ }
+
+ void SetToSpan( Vector3f& irMin, Vector3f& irMax )
+ {
+ mX = sUpperInt(irMax.x) - sLowerInt(irMin.x);
+ mY = sUpperInt(irMax.y) - sLowerInt(irMin.y);
+ mZ = sUpperInt(irMax.z) - sLowerInt(irMin.z);
+ }
+
+ void SetToSpan( Vector3i& irMin, Vector3i& irMax )
+ {
+ mX = irMax.mX - irMin.mX;
+ mY = irMax.mY - irMin.mY;
+ mZ = irMax.mZ - irMin.mZ;
+ }
+
+ int& operator[]( int iIndex )
+ {
+ return ((int*)this)[iIndex];
+ }
+
+ int MaxElem()
+ {
+ if( mX >= mY )
+ {
+ if( mX >= mZ )
+ {
+ return mX;
+ }
+ else
+ {
+ return mZ;
+ }
+ }
+ else
+ {
+ if( mY >= mZ )
+ {
+ return mY;
+ }
+ else
+ {
+ return mZ;
+ }
+ }
+ }
+
+ int MaxIndex()
+ {
+ if( mX >= mY )
+ {
+ if( mX >= mZ )
+ {
+ return 0;
+ }
+ else
+ {
+ return 2;
+ }
+ }
+ else
+ {
+ if( mY >= mZ )
+ {
+ return 1;
+ }
+ else
+ {
+ return 2;
+ }
+ }
+ }
+
+ int MinElem()
+ {
+ if( mX <= mY )
+ {
+ if( mX <= mZ )
+ {
+ return mX;
+ }
+ else
+ {
+ return mZ;
+ }
+ }
+ else
+ {
+ if( mY <= mZ )
+ {
+ return mY;
+ }
+ else
+ {
+ return mZ;
+ }
+ }
+ }
+
+ int MinIndex()
+ {
+ if( mX <= mY )
+ {
+ if( mX <= mZ )
+ {
+ return 0;
+ }
+ else
+ {
+ return 2;
+ }
+ }
+ else
+ {
+ if( mY <= mZ )
+ {
+ return 1;
+ }
+ else
+ {
+ return 2;
+ }
+ }
+ }
+ int mX,mY,mZ;
+protected:
+};
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/VectorLib.cpp b/game/code/render/Culling/VectorLib.cpp
new file mode 100644
index 0000000..a34c0f9
--- /dev/null
+++ b/game/code/render/Culling/VectorLib.cpp
@@ -0,0 +1,31 @@
+#include <render/culling/VectorLib.h>
+
+VectorLib& theVectorLib()
+{
+ static VectorLib tVL;
+
+ return tVL;
+}
+
+
+VectorLib::VectorLib()
+{
+}
+
+VectorLib::~VectorLib()
+{
+}
+
+void VectorLib::FindBounds( Bounds3f& orBounds, FixedArray<Vector3f>& irPoints )
+{
+ int i;
+ orBounds.mMin = irPoints[0];
+ orBounds.mMax = irPoints[0];
+
+ for( i=1; i<irPoints.mSize; i++ )
+ {
+ orBounds.Accumulate( irPoints[i] );
+ }
+}
+
+
diff --git a/game/code/render/Culling/VectorLib.h b/game/code/render/Culling/VectorLib.h
new file mode 100644
index 0000000..17b89b7
--- /dev/null
+++ b/game/code/render/Culling/VectorLib.h
@@ -0,0 +1,29 @@
+#ifndef __VECTOR_LIB_H__
+#define __VECTOR_LIB_H__
+
+#include <render/culling/Vector3f.h>
+#include <render/culling/FixedArray.h>
+#include <render/culling/Bounds.h>
+
+///////////////////////////////////////////////////
+// Originally, I was going to make this a bunch
+// of functions, that operate on lists of Vectors.
+//
+// However, I decided to put them into a class in
+// case I need some persistent state further down
+// the road.
+///////////////////////////////////////////////////
+class VectorLib
+{
+public:
+ VectorLib();
+ ~VectorLib();
+
+ void FindBounds( Bounds3f& orBounds, FixedArray<Vector3f>& irPoints );
+
+protected:
+};
+
+VectorLib& theVectorLib();
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/WorldScene.cpp b/game/code/render/Culling/WorldScene.cpp
new file mode 100644
index 0000000..e6f514d
--- /dev/null
+++ b/game/code/render/Culling/WorldScene.cpp
@@ -0,0 +1,2380 @@
+#include <render/culling/WorldScene.h>
+#include <render/culling/SpatialTreeFactory.h>
+#include <render/Culling/SpatialTree.h>
+#include <render/Culling/SpatialTreeIter.h>
+#include <render/DSG/IntersectDSG.h>
+#include <render/DSG/StaticPhysDSG.h>
+#include <render/DSG/StaticEntityDSG.h>
+#include <render/DSG/DynaPhysDSG.h>
+#include <render/DSG/FenceEntityDSG.h>
+#include <render/DSG/AnimCollisionEntityDSG.h>
+#include <render/DSG/AnimEntityDSG.h>
+#include <roads/roadsegment.h>
+#include <pedpaths/pathsegment.h>
+#include <meta/triggervolume.h>
+#include <algorithm>
+#include <functional>
+
+#include <render/culling/NodeFLL.h>
+
+#include <render/culling/Matrix3f.h>
+#include <camera/supercammanager.h>
+
+#include <raddebugwatch.hpp>
+#include <radtime.hpp>
+
+#include <worldsim/worldphysicsmanager.h>
+
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/character/character.h>
+#include <pddi/pdditype.hpp>
+
+#ifdef DEBUGWATCH
+#include <simcollision/collisiondisplay.hpp>
+#include <simcollision/collisionmanager.hpp>
+#include <simcommon/simutility.hpp>
+#endif
+
+#ifdef RAD_DEBUG
+#include <mission/missionmanager.h>
+#include <mission/objectives/missionobjective.h>
+#include <meta/triggervolumetracker.h>
+#include <gameflow/gameflow.h>
+#include <contexts/contextenum.h>
+#endif
+
+#include <debug/profiler.h>
+
+#include <stdlib.h>
+
+#ifdef RAD_GAMECUBE
+#include <pddi/gamecube/gcrefractionshader.hpp>
+#endif
+
+#define TEST_DISTRIBUTED_SORT
+#define ZSORT_RENDER
+//#define TRACK_SHADERS
+//#define TEST_WHOLE_TREE
+
+//For debug section
+//#include <render/culling/../debuginfo.hpp"
+//#include <render/culling/../../profiler/profiler.hpp"
+//For test debugging (camera)
+//#include <render/culling/../../main/globals.hpp"
+//#include <render/culling/../../main/gamesettings.hpp"
+//#include <render/culling/../../worldsim/supercam.hpp"
+//#include <render/culling/../../worldsim/player.hpp"
+
+//static Vector3f TODO_GRANULARITY(160.0f, 2000.0f, 160.0f);
+//static Vector3f TODO_GRANULARITY(240.0f, 2000.0f, 240.0f);
+//static Vector3f TODO_GRANULARITY(10.0f, 2000.0f, 10.0f);
+//static Vector3f TODO_GRANULARITY(200.0f, 2000.0f, 200.0f);
+#ifdef RAD_GAMECUBE
+static Vector3f TODO_GRANULARITY(40.0f, 2000.0f, 40.0f);
+#else
+static Vector3f TODO_GRANULARITY(20.0f, 2000.0f, 20.0f);
+#endif
+//static Vector3f TODO_GRANULARITY(120.0f, 2000.0f, 120.0f);
+
+//defines
+//#define RENDER_W_DLIST
+#ifdef ZSORT_RENDER
+// Replace all the following qsort and stl compare functions with stl function objects
+
+struct gZSortCompare: public std::binary_function< IEntityDSG*, IEntityDSG*, bool >
+{
+ inline bool operator() ( IEntityDSG* pArg1, IEntityDSG* pArg2 )
+ {
+ return pArg1->mRank < pArg2->mRank;
+ }
+};
+
+struct gShaderCompare : public std::binary_function< const WorldScene::zSortBlah&, const WorldScene::zSortBlah&, bool >
+{
+ inline bool operator() ( const WorldScene::zSortBlah& pArg1, const WorldScene::zSortBlah& pArg2 )
+ {
+ return pArg1.shaderUID < pArg2.shaderUID;
+ }
+};
+
+struct gTestZ : public std::binary_function< IEntityDSG*, IEntityDSG*, bool >
+{
+ inline bool operator() ( IEntityDSG* pArg1, IEntityDSG* pArg2 )
+ {
+ return pArg1->mRank < pArg2->mRank;
+ }
+};
+
+struct gTestShader : public std::binary_function< IEntityDSG*, IEntityDSG*, bool >
+{
+ inline bool operator() ( IEntityDSG* pArg1, IEntityDSG* pArg2 )
+ {
+ return pArg1->GetShaderUID() < pArg2->GetShaderUID();
+ }
+};
+
+/*int gZSortCompare( const void *arg1, const void *arg2 )
+{
+ return (int)(0.1f*((*(IEntityDSG**)arg1)->Rank() - (*(IEntityDSG**)arg2)->Rank()));
+}
+
+int gShaderCompare( const void *arg1, const void *arg2 )
+{
+ return (int)(((WorldScene::zSortBlah*)arg1)->shaderUID - ((WorldScene::zSortBlah*)arg2)->shaderUID);
+}
+
+bool gTestZ( IEntityDSG* arg1, IEntityDSG* arg2 )
+{
+ if( arg1->mRank < arg2->mRank )
+ return true;
+ else
+ return false;
+// return (int)(0.1f*(arg1->Rank() - arg2->Rank()));
+}
+
+bool gTestShader( IEntityDSG* arg1, IEntityDSG* arg2 )
+{
+ if( arg1->mShaderUID < arg2->mShaderUID )
+ return true;
+ else
+ return false;
+ //return (int)(arg1->GetShaderUID() - arg2->GetShaderUID());
+}*/
+
+#endif
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+WorldScene::WorldScene()
+:
+ mEpsilonOffset(0.01f,0.01f,0.01f),
+ mRenderAll(false),
+ mpStaticTree(NULL)
+{
+ GetEventManager()->AddListener(this,(EventEnum)(EVENT_LOCATOR+LocatorEvent::FAR_PLANE));
+ mDrawDist = 200.0f;
+
+ mpZSorts.reserve(5000);
+ rTuneAssert( mpZSorts.capacity() == 5000 );
+
+ mpZSortsPass2.reserve(5000);
+ rTuneAssert( mpZSortsPass2.capacity() == 5000 );
+
+ mShadowCastersPass1.Allocate(300);
+ //mShadowCastersPass2.Allocate(300);
+ mCamPlanes.Allocate(6);
+
+ mpZSortsPassShadowCasters.reserve(300);
+
+#ifdef DEBUGWATCH
+ radDbgWatchAddUnsignedInt( &mDebugZSWalkTiming, "ZSort Walk micros", "WorldScene", NULL, NULL );
+ radDbgWatchAddUnsignedInt( &mDebugZSAddTiming, "ZSort Add micros", "WorldScene", NULL, NULL );
+ radDbgWatchAddUnsignedInt( &mDebugZSSortTiming, "ZSort Sort micros", "WorldScene", NULL, NULL );
+ radDbgWatchAddUnsignedInt( &mDebugMarkTiming, "Debug Mark micros", "WorldScene", NULL, NULL );
+ radDbgWatchAddUnsignedInt( &mDebugWalkTiming, "Debug Walk micros", "WorldScene", NULL, NULL );
+ radDbgWatchAddUnsignedInt( &mDebugRenderTiming, "Debug Render micros", "WorldScene", NULL, NULL );
+
+ mDebugShowTree=false;
+ radDbgWatchAddBoolean(&mDebugShowTree, "ShowTree", "WorldScene", NULL, NULL);
+
+ // toggle collision volume drawing on and off
+ mDebugSimCollisionVolumeDrawing = false;
+ radDbgWatchAddBoolean(&mDebugSimCollisionVolumeDrawing, "Sim Collision Volume Drawing", "Physics Debug", NULL, NULL);
+
+ mDebugVehicleCollisionDrawing = false;
+ radDbgWatchAddBoolean(&mDebugVehicleCollisionDrawing, "Vehicle Collision Volume", "Physics Debug", NULL, NULL);
+
+ mDebugFenceCollisionVolumeDrawing = false;
+ radDbgWatchAddBoolean(&mDebugFenceCollisionVolumeDrawing, "Fence Pieces in Active Area", "Physics Debug", NULL, NULL);
+
+ mDebugSimStatsDisplay = false;
+ radDbgWatchAddBoolean(&mDebugSimStatsDisplay, "Sim Stats Display", "Physics Debug", NULL, NULL);
+
+ mDebugWatchNumCollisionPairs = 0;
+ radDbgWatchAddInt(&mDebugWatchNumCollisionPairs, "Num Collision Pairs", "Physics Debug", NULL, NULL );
+#endif
+}
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+WorldScene::~WorldScene()
+{
+ GetEventManager()->RemoveListener(this,(EventEnum)(EVENT_LOCATOR+LocatorEvent::FAR_PLANE));
+
+ int i;
+/*
+ for( i=mStaticIntersects.mUseSize-1; i>-1; i-- )
+ {
+ mStaticIntersects[i]->Release();
+ }*/
+
+#ifdef DEBUGWATCH
+ radDbgWatchDelete(&mDebugMarkTiming);
+ radDbgWatchDelete(&mDebugWalkTiming);
+ radDbgWatchDelete(&mDebugRenderTiming);
+ radDbgWatchDelete(&mDebugZSWalkTiming);
+ radDbgWatchDelete(&mDebugZSAddTiming);
+ radDbgWatchDelete(&mDebugZSSortTiming);
+ radDbgWatchDelete(&mDebugShowTree);
+
+ radDbgWatchDelete(&mDebugSimCollisionVolumeDrawing);
+ radDbgWatchDelete(&mDebugVehicleCollisionDrawing);
+ radDbgWatchDelete(&mDebugFenceCollisionVolumeDrawing);
+ radDbgWatchDelete(&mDebugSimStatsDisplay);
+ radDbgWatchDelete(&mDebugWatchNumCollisionPairs);
+#endif
+
+ if(mpStaticTree == NULL)
+ return;
+
+ for(i=mStaticTreeWalker.NumNodes()-1; i>-1; i--)
+ {
+ rTuneAssert( mStaticTreeWalker.rIthNode(i).mDPhysElems.mUseSize == 0 );
+ rTuneAssert( mStaticTreeWalker.rIthNode(i).mFenceElems.mUseSize == 0 );
+ rTuneAssert( mStaticTreeWalker.rIthNode(i).mSEntityElems.mUseSize == 0 );
+ rTuneAssert( mStaticTreeWalker.rIthNode(i).mSPhysElems.mUseSize == 0 );
+ rTuneAssert( mStaticTreeWalker.rIthNode(i).mAnimCollElems.mUseSize == 0 );
+ rTuneAssert( mStaticTreeWalker.rIthNode(i).mAnimElems.mUseSize == 0 );
+ rTuneAssert( mStaticTreeWalker.rIthNode(i).mIntersectElems.mUseSize == 0 );
+ rTuneAssert( mStaticTreeWalker.rIthNode(i).mPathSegmentElems.mUseSize == 0 );
+ rTuneAssert( mStaticTreeWalker.rIthNode(i).mRoadSegmentElems.mUseSize == 0 );
+ rTuneAssert( mStaticTreeWalker.rIthNode(i).mTrigVolElems.mUseSize == 0 );
+ }
+
+ mpStaticTree->ReleaseVerified();
+
+ // mpDefaultShader->Release();
+}
+//========================================================================
+// worldscene::
+//========================================================================
+//
+// Description: adapted from http://www.magic-software.com , David Eberly
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldScene::SetVisCone( rmt::Vector& irView, rmt::Vector& irPosn, float iAngleRadians )
+{
+ mViewVector = irView;
+ mViewPosn = irPosn;
+
+ mViewSinInv = rmt::Sin(iAngleRadians);
+ mViewSinSqr = mViewSinInv*mViewSinInv;
+ mViewSinInv = 1.0f/mViewSinInv;
+
+ mViewCosSqr = rmt::Cos(iAngleRadians);
+ mViewCosSqr = mViewCosSqr * mViewCosSqr;
+
+ //mVisUBase.Div( irView, mViewSin );
+ //mVisUBase
+}
+//========================================================================
+// worldscene::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+
+bool WorldScene::IsSphereInCone( rmt::Vector& irCenter, float iRadius )
+{
+ if ( mRenderAll )
+ {
+ return true;
+ }
+
+ // nv: cull small things that are far away
+ const float SMALL_THING = 0.85f;
+ const float SMALL_THING_CULL_DIST_SQR = 4000.0f; // 40m
+ rmt::Vector v;
+ v.Sub(mViewPosn, irCenter);
+ if( (iRadius < SMALL_THING) && (v.MagnitudeSqr() > SMALL_THING_CULL_DIST_SQR) )
+ {
+ return false;
+ }
+
+ float Dsqr, e;
+ rmt::Vector bigD, temp1, temp2;
+ temp2 = mViewPosn;
+
+ temp1 = mViewVector;
+ temp1.Scale(iRadius*mViewSinInv);
+
+ temp2.Sub(temp1);
+
+ bigD.Sub(irCenter,temp2);
+ Dsqr = bigD.Dot(bigD);
+
+ e = mViewVector.Dot(bigD);
+
+ if( e>0.0f && e*e >= Dsqr*mViewCosSqr )
+ {
+ bigD.Sub(irCenter,mViewPosn);
+ Dsqr = bigD.Dot(bigD);
+ e = -1.0f*mViewVector.Dot(bigD);
+
+ if(e>0.0f && e*e > Dsqr*mViewSinSqr)
+ return Dsqr <= iRadius*iRadius;
+ else
+ return true;
+ }
+ return false;
+
+}
+//========================================================================
+// worldscene::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldScene::HandleEvent( EventEnum id, void* pEventData )
+{
+ if(id==EVENT_LOCATOR+LocatorEvent::FAR_PLANE)
+ {
+ unsigned int newDrawDist = ((EventLocator*)pEventData)->GetData();
+
+ if(((EventLocator*)pEventData)->GetPlayerID()<1)
+ {
+ if(((EventLocator*)pEventData)->GetPlayerEntered())
+ {
+ mDrawDist = rmt::LtoF(newDrawDist);
+ }
+ else //exit
+ {
+ mDrawDist = 200.0f;
+ }
+ }
+ return;
+ }
+ rAssert(false);
+}
+//========================================================================
+// WorldScene::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldScene::SetTree( SpatialTree* ipSpatialTree )
+{
+ rAssert( IsPreTreeGen() );
+ mpStaticTree = ipSpatialTree;
+ mpStaticTree->AddRef();
+ mStaticTreeWalker.SetToRoot( *mpStaticTree );
+ mStaticTreeWalker.AndTree(0x00000000);
+ GenerateSpatialReps();
+}
+////////////////////////////////////////////////////////////////////
+// Init will intialise the WorldScene Object:
+// -Tells WorldScene at maximum how many nRenderables will be
+// Add'ed
+// -Is a neccessary precursor to any and all other calls to this
+// object
+////////////////////////////////////////////////////////////////////
+void WorldScene::Init( int nRenderables )
+{
+ rAssert( IsPreTreeGen() );
+
+/*
+ mStaticGeos.Allocate( nRenderables );
+ mStaticIntersects.Allocate( nRenderables );
+ mStaticEntities.Allocate( nRenderables );
+ mStaticPhys.Allocate( nRenderables );
+ */
+}
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+void WorldScene::Add( tGeometry* pGeometry )
+{
+// pGeometry->AddRef();
+// if( IsNotInScene( pGeometry ))
+// {
+// mStaticGeos.Add(pGeometry);
+// }
+
+ if( IsPostTreeGen() )
+ {
+ //todo: uncomment and fix control flow
+ // PlaceStaticGeo( pGeometry );
+ }
+}
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+void WorldScene::Add( IntersectDSG* ipIntersectDSG )
+{
+// ipIntersectDSG->AddRef();
+/// if( IsNotInScene( ipIntersectDSG ))
+ {
+// mStaticIntersects.Add(ipIntersectDSG);
+
+// if( IsPostTreeGen() )
+ rAssert( IsPostTreeGen() );
+ {
+ Place( ipIntersectDSG );
+ }
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Add( StaticPhysDSG* ipStaticPhysDSG )
+{
+ ipStaticPhysDSG->AddRef();
+// if( IsNotInScene( ipStaticPhysDSG ))
+// {
+// mStaticPhys.Add(ipStaticPhysDSG);
+
+ rAssert( IsPostTreeGen() );
+// if( IsPostTreeGen() )
+ {
+ Place( ipStaticPhysDSG );
+ }
+// }
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Add( StaticEntityDSG* ipStaticEntityDSG )
+{
+// if( IsNotInScene( ipStaticEntityDSG ))
+// {
+// mStaticEntities.Add(ipStaticEntityDSG);
+
+ bool isPostTreeGen = IsPostTreeGen();
+ rAssert( isPostTreeGen );
+ if( IsPostTreeGen() )
+ {
+ ipStaticEntityDSG->AddRef();
+ Place( ipStaticEntityDSG );
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Add( FenceEntityDSG* ipFenceEntityDSG )
+{
+ ipFenceEntityDSG->AddRef();
+// if( IsNotInScene( ipStaticEntityDSG ))
+// {
+// mFenceEntities.Add(ipFenceEntityDSG );
+
+ rAssert( IsPostTreeGen() );
+// if( IsPostTreeGen() )
+ {
+ Place( ipFenceEntityDSG );
+ }
+// }
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Add( AnimCollisionEntityDSG* ipAnimCollDSG )
+{
+ ipAnimCollDSG->AddRef();
+
+ rAssert( IsPostTreeGen() );
+
+ Place( ipAnimCollDSG );
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Add( AnimEntityDSG* ipAnimDSG )
+{
+ ipAnimDSG->AddRef();
+
+ rAssert( IsPostTreeGen() );
+
+ Place( ipAnimDSG );
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Add( DynaPhysDSG* ipDynaPhysDSG )
+{
+ ipDynaPhysDSG->AddRef();
+
+ rAssert( IsPostTreeGen() );
+
+ Place( ipDynaPhysDSG );
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Add( TriggerVolume* ipTriggerVolume )
+{
+ ipTriggerVolume->AddRef();
+
+ rAssert(IsPostTreeGen());
+
+ Place( ipTriggerVolume );
+}
+
+////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Add( RoadSegment* ipRoadSegment )
+{
+ ipRoadSegment->AddRef();
+
+ rAssert(IsPostTreeGen());
+
+ Place( ipRoadSegment );
+}
+////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Add( PathSegment* ipPathSegment )
+{
+ ipPathSegment->AddRef();
+
+ rAssert(IsPostTreeGen());
+
+ Place( ipPathSegment );
+}
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+void WorldScene::GenerateSpatialReps()
+{
+/*
+ mpDefaultShader = p3d::find<tShader>("OakL_m");
+ tTexture* pTexture = p3d::find<tTexture>("OakL.bmp");
+ for(int j=mStaticGeos.mUseSize-1; j>-1; j--)
+ {
+ for( int i=mStaticGeos[j]->GetNumPrimGroup()-1; i>-1; i-- )
+ {
+// mStaticGeos[j]->GetShader(i)->SetTexture(PDDI_SP_BASETEX, pTexture);
+ if( mStaticGeos[j]->GetShader(i)->GetType() == mpDefaultShader->GetType() )
+ {
+ // mStaticGeos[j]->SetShader(i, mpDefaultShader);
+ }
+ }
+ }
+*/
+ if( IsPreTreeGen() )
+ {
+ GenerateStaticTree();
+ }
+
+ //temporary to ignore weird bounding values
+ mStaticTreeWalker.rBBox().mBounds.mMin.y = -200.0f;
+ mStaticTreeWalker.rBBox().mBounds.mMax.y = 100.0f;
+ mStaticTreeWalker.BuildBBoxes(mStaticTreeWalker.rBBox());
+
+ PopulateStaticTree();
+}
+//========================================================================
+// WorldScene::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldScene::Move( rmt::Box3D& irOldBBox, IEntityDSG* ipEDSG )
+{
+ /*
+ if(RemovePlace(ipEDSG, mStaticTreeWalker.rIthNode(0)))
+ return;
+
+ BoxPts aBBox;
+
+ aBBox.mBounds.mMin.SetTo(irOldBBox.low);
+ aBBox.mBounds.mMax.SetTo(irOldBBox.high);
+ aBBox.mBounds.mMin.Add(mEpsilonOffset);
+ aBBox.mBounds.mMax.Sub(mEpsilonOffset);
+
+ if(RemovePlace(ipEDSG, mStaticTreeWalker.rSeekNode( aBBox )))
+ return;
+*/
+ if(ipEDSG->mpSpatialNode)
+ {
+ if(RemovePlace(ipEDSG, *(ipEDSG->mpSpatialNode) ))
+ return;
+ }
+
+ rTunePrintf("Move: Can't find ipEDSG: %s, which needs to be moved in the DSG\n", ipEDSG->GetName());
+// rAssert(false);
+}
+
+bool WorldScene::RemovePlace(IEntityDSG* ipEDSG, SpatialNode& irNode)
+{
+ int i;
+ for(i=irNode.mDPhysElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == irNode.mDPhysElems[i] )
+ {
+ irNode.mDPhysElems.Remove(i);
+ Place((DynaPhysDSG*)(ipEDSG));
+ return true;
+ }
+ }
+ for(i=irNode.mAnimCollElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == irNode.mAnimCollElems[i] )
+ {
+ irNode.mAnimCollElems.Remove(i);
+ Place((AnimCollisionEntityDSG*)(ipEDSG));
+ return true;
+ }
+ }
+ for(i=irNode.mAnimElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == irNode.mAnimElems[i] )
+ {
+ irNode.mAnimElems.Remove(i);
+ Place((AnimEntityDSG*)(ipEDSG));
+ return true;
+ }
+ }
+
+
+ for(i=irNode.mTrigVolElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == irNode.mTrigVolElems[i] )
+ {
+ irNode.mTrigVolElems.Remove(i);
+ Place((TriggerVolume*)(ipEDSG));
+ return true;
+ }
+ }
+ for(i=irNode.mSEntityElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == irNode.mSEntityElems[i] )
+ {
+ irNode.mSEntityElems.Remove(i);
+ Place((StaticEntityDSG*)(ipEDSG));
+ return true;
+ }
+ }
+ for(i=irNode.mSPhysElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == irNode.mSPhysElems[i] )
+ {
+ irNode.mSPhysElems.Remove(i);
+ Place((StaticPhysDSG*)(ipEDSG));
+ return true;
+ }
+ }
+ return false;
+}
+//========================================================================
+// WorldScene::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldScene::Remove( IEntityDSG* ipEDSG )
+{
+ //if( RemoveFromLeaf(ipEDSG) )
+ // return;
+
+ int i;
+/* BoxPts aBBox;
+ rmt::Box3D Box3DBBox;
+ ipEDSG->GetBoundingBox(&Box3DBBox);
+
+ aBBox.mBounds.mMin.SetTo(Box3DBBox.low);
+ aBBox.mBounds.mMax.SetTo(Box3DBBox.high);
+ aBBox.mBounds.mMin.Add(mEpsilonOffset);
+ aBBox.mBounds.mMax.Sub(mEpsilonOffset);
+
+ SpatialNode& rNode = mStaticTreeWalker.rSeekNode( aBBox );
+*/
+ rAssert(ipEDSG->mpSpatialNode);
+ if(!ipEDSG->mpSpatialNode)
+ {
+ return;
+ }
+ SpatialNode& rNode = *(ipEDSG->mpSpatialNode);
+ ipEDSG->mpSpatialNode = NULL;
+
+ for(i=rNode.mDPhysElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mDPhysElems[i] )
+ {
+ rNode.mDPhysElems.Remove(i);
+ ipEDSG->Release();
+ return;
+ }
+ }
+ for(i=rNode.mSEntityElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mSEntityElems[i] )
+ {
+ rNode.mSEntityElems.Remove(i);
+ ipEDSG->Release();
+ return;
+ }
+ }
+ for(i=rNode.mSPhysElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mSPhysElems[i] )
+ {
+ rNode.mSPhysElems.Remove(i);
+ ipEDSG->Release();
+ return;
+ }
+ }
+ for(i=rNode.mIntersectElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mIntersectElems[i] )
+ {
+ rNode.mIntersectElems.Remove(i);
+ ipEDSG->Release();
+ return;
+ }
+ }
+ for(i=rNode.mTrigVolElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mTrigVolElems[i] )
+ {
+ rNode.mTrigVolElems.Remove(i);
+ ipEDSG->Release();
+ return;
+ }
+ }
+ rTunePrintf("Remove: Can't find ipEDSG: %s, which needs to be removed in the DSG\n", ipEDSG->GetName());
+ rAssert(false);
+}
+
+bool WorldScene::RemoveFromLeaf( IEntityDSG* ipEDSG )
+{
+ SpatialNode& rNode = mStaticTreeWalker.rIthNode(0);
+
+ int i;
+ for(i=rNode.mDPhysElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mDPhysElems[i] )
+ {
+ rNode.mDPhysElems.Remove(i);
+ ipEDSG->Release();
+ return true;
+ }
+ }
+ for(i=rNode.mSEntityElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mSEntityElems[i] )
+ {
+ rNode.mSEntityElems.Remove(i);
+ ipEDSG->Release();
+ return true;
+ }
+ }
+ for(i=rNode.mAnimCollElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mAnimCollElems[i] )
+ {
+ rNode.mAnimCollElems.Remove(i);
+ ipEDSG->Release();
+ return true;
+ }
+ }
+ for(i=rNode.mAnimElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mAnimElems[i] )
+ {
+ rNode.mAnimCollElems.Remove(i);
+ ipEDSG->Release();
+ return true;
+ }
+ }
+ return false;
+}
+//========================================================================
+// WorldScene::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldScene::RemoveQuietFail( IEntityDSG* ipEDSG )
+{
+ //if( RemoveFromLeaf(ipEDSG) )
+ // return;
+
+ int i;
+ /*
+ BoxPts aBBox;
+ rmt::Box3D Box3DBBox;
+ ipEDSG->GetBoundingBox(&Box3DBBox);
+
+ aBBox.mBounds.mMin.SetTo(Box3DBBox.low);
+ aBBox.mBounds.mMax.SetTo(Box3DBBox.high);
+ aBBox.mBounds.mMin.Add(mEpsilonOffset);
+ aBBox.mBounds.mMax.Sub(mEpsilonOffset);
+
+ SpatialNode& rNode = mStaticTreeWalker.rSeekNode( aBBox );
+ */
+ if(!ipEDSG->mpSpatialNode)
+ {
+ rDebugPrintf("RemoveQuietFail: Can't find ipEDSG: %s, which needs to be moved in the DSG\n", ipEDSG->GetName());
+ return;
+ }
+
+ SpatialNode& rNode = *(ipEDSG->mpSpatialNode);
+ ipEDSG->mpSpatialNode = NULL;
+
+ for(i=rNode.mDPhysElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mDPhysElems[i] )
+ {
+ rNode.mDPhysElems.Remove(i);
+ ipEDSG->Release();
+ return;
+ }
+ }
+ for(i=rNode.mSEntityElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mSEntityElems[i] )
+ {
+ rNode.mSEntityElems.Remove(i);
+ ipEDSG->Release();
+ return;
+ }
+ }
+ for(i=rNode.mSPhysElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mSPhysElems[i] )
+ {
+ rNode.mSPhysElems.Remove(i);
+ ipEDSG->Release();
+ return;
+ }
+ }
+ for(i=rNode.mIntersectElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mIntersectElems[i] )
+ {
+ rNode.mIntersectElems.Remove(i);
+ ipEDSG->Release();
+ return;
+ }
+ }
+ for(i=rNode.mTrigVolElems.mUseSize-1;i>-1;i--)
+ {
+ if( ipEDSG == rNode.mTrigVolElems[i] )
+ {
+ rNode.mTrigVolElems.Remove(i);
+ ipEDSG->Release();
+ return;
+ }
+ }
+ rDebugPrintf("RemoveQuietFail: Can't find ipEDSG: %s, which needs to be moved in the DSG\n", ipEDSG->GetName());
+}
+
+
+//========================================================================
+// WorldScene::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldScene::RenderScene( unsigned int iFilter, tPointCamera* ipCam )
+{
+#ifdef TRACK_SHADERS
+ static int sRenderCall=0;
+ sRenderCall++;
+#endif
+/*
+ if(!mpTempShader)
+ {
+ mpTempShader = p3d::find<tShader>("fire_hydrant_m");
+ rReleaseAssert(mpTempShader);
+ }
+*/
+ static int sMaxDebugGeoCount = 0;
+ int DebugRenderGeoCount = 0;
+ int DebugRenderNodeCount = 0;
+ int DebugRenderLeafCount = 0;
+
+// mpZSorts.ClearUse();
+// mpZSortsPass2.ClearUse();
+
+ mpZSorts.resize( 0 );
+ rTuneAssert( mpZSorts.capacity() == 5000 );
+ mpZSortsPass2.resize( 0 );
+ rTuneAssert( mpZSortsPass2.capacity() == 5000 );
+ mpZSortsPassShadowCasters.resize(0);
+ rTuneAssert( mpZSortsPassShadowCasters.capacity() == 300 );
+
+ mShadowCastersPass1.ClearUse();
+ //mShadowCastersPass2.ClearUse();
+
+ std::vector<IEntityDSG*, s2alloc<IEntityDSG*> > mSort1;
+ std::vector<IEntityDSG*, s2alloc<IEntityDSG*> > mSort2;
+
+#ifdef DEBUGWATCH
+
+
+ // turn off Z compare if we're drawing fence pieces also
+/*
+ pddiCompareMode restoreZMode;
+ if(mDebugFenceCollisionVolumeDrawing)
+ {
+ restoreZMode = p3d::pddi->GetZCompare();
+ p3d::pddi->SetZCompare(PDDI_COMPARE_NONE);
+ }
+*/
+#endif
+
+#ifdef TEST_WHOLE_TREE
+ static int maxDPhys=0,maxSEntity=0,maxAnimColl=0,maxAnimElem=0;
+
+ int DPhysCount=0, SEntityCount=0, AnimCollCount=0, AnimElemCount=0, temp=0;
+
+ for( int ith=mStaticTreeWalker.NumNodes()-1; ith>-1; ith--)
+ {
+ SpatialNode& rCurNode = mStaticTreeWalker.rIthNode(ith);
+
+ temp = rCurNode.mDPhysElems.mUseSize-(rCurNode.mDPhysElems.mSize-50);
+ if(temp>0)
+ DPhysCount+=temp;
+
+ temp = rCurNode.mSEntityElems.mUseSize-(rCurNode.mSEntityElems.mSize-32);
+ if(temp>0)
+ SEntityCount+=temp;
+
+ AnimCollCount+=rCurNode.mAnimCollElems.mUseSize;
+ AnimElemCount+=rCurNode.mAnimElems.mUseSize;
+ }
+
+ if(DPhysCount > maxDPhys)
+ maxDPhys = DPhysCount;
+ if(SEntityCount > maxSEntity)
+ maxSEntity = SEntityCount;
+ if(AnimCollCount > maxAnimColl)
+ maxAnimColl = AnimCollCount;
+ if(AnimElemCount > maxAnimElem)
+ maxAnimElem = AnimElemCount;
+#endif
+
+
+
+ mStaticTreeWalker.SetIterFilter( iFilter );
+//////////////////////////////////////////////////////////////////////////
+#ifdef DEBUGWATCH
+ mDebugZSAddTiming = 0;
+ unsigned int t0,t1,tRenderAcc=0;
+ t0 = radTimeGetMicroseconds();
+ mDebugZSWalkTiming = radTimeGetMicroseconds();
+#endif
+BEGIN_PROFILE("list construction")
+
+#ifdef TRACK_SHADERS
+ typedef std::map< tUID, int, std::less<tUID>, s2alloc< std::pair< const tUID, int > > > UIDMap;
+ UIDMap UniqueShaders;
+#endif
+ //Hack
+#ifdef RAD_DEBUG
+ if(0)//GetMissionManager()->GetCurrentMission()->GetState()==Mission::STATE_INPROGRESS &&
+ //!GetMissionManager()->InResetState() &&
+ //GetGameFlow()->GetCurrentContext() == CONTEXT_GAMEPLAY)
+ {
+ rmt::Matrix Trans;
+ rmt::Vector testPosn;
+ pddiPrimStream* stream;
+
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetPosition(testPosn);
+ Trans.Identity();
+ Trans.FillTranslate(GetMissionManager()->GetCurrentMission()->GetCurrentStage()->GetObjective()->mPathStart);
+ p3d::pddi->PushMatrix(PDDI_MATRIX_MODELVIEW);
+ p3d::pddi->MultMatrix(PDDI_MATRIX_MODELVIEW,&Trans);
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 2);
+ stream->Colour(tColour(255,0,0));
+ stream->Coord(0.0f, 0.0f, 0.0f);
+ stream->Colour(tColour(255,0,0));
+ stream->Coord(0.0f, 2.0f, 0.0f);
+ p3d::pddi->EndPrims(stream);
+ p3d::pddi->PopMatrix(PDDI_MATRIX_MODELVIEW);
+
+ Trans.Identity();
+ Trans.FillTranslate(GetMissionManager()->GetCurrentMission()->GetCurrentStage()->GetObjective()->mPathEnd);
+ p3d::pddi->PushMatrix(PDDI_MATRIX_MODELVIEW);
+ p3d::pddi->MultMatrix(PDDI_MATRIX_MODELVIEW,&Trans);
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 2);
+ stream->Colour(tColour(0,255,0));
+ stream->Coord(0.0f, 0.0f, 0.0f);
+ stream->Colour(tColour(0,255,0));
+ stream->Coord(0.0f, 2.0f, 0.0f);
+ p3d::pddi->EndPrims(stream);
+ p3d::pddi->PopMatrix(PDDI_MATRIX_MODELVIEW);
+
+ }
+#endif
+ rmt::Sphere ObjectSphere;
+ rmt::Vector CamPosn = ipCam->GetPosition();
+//////////////////////////////////////////////////////////////////////////
+ //For all the visible nodes in the tree
+//#ifdef DEBUGWATCH
+ pddiCompareMode origZCompare = p3d::pddi->GetZCompare();
+//#endif
+ DebugRenderNodeCount = 0;
+ for( mStaticTreeWalker.MoveToFirst(); mStaticTreeWalker.NotDone(); mStaticTreeWalker.MoveToNext(true) )
+ {
+
+ DebugRenderNodeCount++;
+
+ if(GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_SHOW_TREE))
+ {
+ if( mStaticTreeWalker.IsCurrentLeaf() )
+ {
+ DebugRenderLeafCount++;
+ //#ifdef DEBUGWATCH
+ // if(mDebugShowTree)
+ {
+ p3d::pddi->SetZCompare(PDDI_COMPARE_ALWAYS);
+ mStaticTreeWalker.DisplayCurrentBoundingBox( tColour(255,0,0) );
+ p3d::pddi->SetZCompare(origZCompare);
+ }
+ //#endif
+ }
+ else
+ {
+ //#ifdef DEBUGWATCH
+ // if(mDebugShowTree)
+ {
+ p3d::pddi->SetZCompare(PDDI_COMPARE_ALWAYS);
+ mStaticTreeWalker.DisplayCurrentBoundingBox( tColour(0,0,0) );
+ p3d::pddi->SetZCompare(origZCompare);
+ }
+ //#endif
+ //rAssert( mStaticTreeWalker.pCurrent()->mSpatialElems.mSize == 0 );
+ //rAssert( mStaticTreeWalker.pCurrent()->mSpatialElems.mUseSize == 0 );
+ // rAssert( mStaticTreeWalker.pCurrent()->mIntersectElems.mSize == 0 );
+ // rAssert( mStaticTreeWalker.pCurrent()->mIntersectElems.mUseSize == 0 );
+ }
+ }
+
+ // if( BEGIN_DEBUGINFO_SECTION( "CullDebug" ) )
+ // {
+ //mStaticTreeWalker.DisplayCurrentBoundingBox( tColour(255,255,0) );
+ // }
+ // END_DEBUGINFO_SECTION;
+
+#if 1
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+#endif
+ //For all the Used slots in the node's T array
+ int i;
+ //////////////////////////////////////////////////////////////////////////
+ for( i= mStaticTreeWalker.rCurrent().mSEntityElems.mUseSize-1; i>-1; i-- )
+ {
+#ifdef ZSORT_RENDER
+ mStaticTreeWalker.rCurrent().mSEntityElems[i]->GetBoundingSphere(&ObjectSphere);
+ if(!IsSphereInCone(ObjectSphere.centre, ObjectSphere.radius))
+ continue;
+
+ //mStaticTreeWalker.rCurrent().mSEntityElems[i]->SetShader(mpTempShader,0);
+ switch(3)//mStaticTreeWalker.rCurrent().mSEntityElems[i]->CastsShadow())
+ {
+ case 1:
+ mShadowCastersPass1.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mSEntityElems[i]);
+ break;
+ case 2:
+ {
+ //mShadowCastersPass2.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mSEntityElems[i]);
+ IEntityDSG* newDSGPtr = mStaticTreeWalker.rCurrent().mSEntityElems[i];
+ newDSGPtr->SetRank( CamPosn, mViewVector );
+ mpZSortsPassShadowCasters.push_back( newDSGPtr );
+ }
+ break;
+ default:
+ if(!mStaticTreeWalker.rCurrent().mSEntityElems[i]->mTranslucent && mStaticTreeWalker.rCurrent().mSEntityElems[i]->CastsShadow()==0)
+ {
+ //mpZSorts.AddUse(1);
+ //mpZSorts[mpZSorts.mUseSize-1].entityPtr = mStaticTreeWalker.rCurrent().mSEntityElems[i];
+ //mpZSorts[mpZSorts.mUseSize-1].shaderUID = mpZSorts[mpZSorts.mUseSize-1].entityPtr->GetShaderUID();
+ zSortBlah newBlah;
+ newBlah.entityPtr = mStaticTreeWalker.rCurrent().mSEntityElems[i];
+ newBlah.shaderUID = newBlah.entityPtr->GetShaderUID();
+ mpZSorts.push_back( newBlah );
+ //mpZSorts[mpZSorts.mUseSize-1]->SetRank((rmt::Vector&)ipCam->GetPosition());
+ //mSort1.push_back(mStaticTreeWalker.rCurrent().mSEntityElems[i]);
+ }
+ else
+ {
+
+ //mpZSortsPass2.AddUse(1);
+ //mpZSortsPass2[mpZSortsPass2.mUseSize-1] = mStaticTreeWalker.rCurrent().mSEntityElems[i];
+ //mpZSortsPass2[mpZSortsPass2.mUseSize-1]->SetRank(CamPosn);
+
+ IEntityDSG* newDSGPtr = mStaticTreeWalker.rCurrent().mSEntityElems[i];
+ newDSGPtr->SetRank( CamPosn, mViewVector );
+ mpZSortsPass2.push_back( newDSGPtr );
+ //mSort2.push_back(mStaticTreeWalker.rCurrent().mSEntityElems[i]);
+ }
+ break;
+ }
+#else
+ switch(mStaticTreeWalker.rCurrent().mSEntityElems[i]->CastsShadow())
+ {
+ mShadowCasters.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mSEntityElems[i]);
+ }
+ else
+ {
+ mStaticTreeWalker.rCurrent().mSEntityElems[i]->Display();
+ }
+#endif
+ DebugRenderGeoCount++;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ for( i= mStaticTreeWalker.rCurrent().mAnimCollElems.mUseSize-1; i>-1; i-- )
+ {
+#ifdef ZSORT_RENDER
+ mStaticTreeWalker.rCurrent().mAnimCollElems[i]->GetBoundingSphere(&ObjectSphere);
+ if(!IsSphereInCone(ObjectSphere.centre, ObjectSphere.radius))
+ continue;
+
+ switch(3)//mStaticTreeWalker.rCurrent().mAnimCollElems[i]->CastsShadow())
+ {
+ case 1:
+ mShadowCastersPass1.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mAnimCollElems[i]);
+ break;
+ case 2:
+ {
+ //mShadowCastersPass2.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mAnimCollElems[i]);
+ IEntityDSG* newDSGPtr = mStaticTreeWalker.rCurrent().mAnimCollElems[i];
+ newDSGPtr->SetRank( CamPosn, mViewVector );
+ mpZSortsPassShadowCasters.push_back( newDSGPtr );
+ }
+ break;
+ default:
+ if(!mStaticTreeWalker.rCurrent().mAnimCollElems[i]->mTranslucent && mStaticTreeWalker.rCurrent().mAnimCollElems[i]->CastsShadow()==0)
+ {
+ //mpZSorts.AddUse(1);
+ //mpZSorts[mpZSorts.mUseSize-1].entityPtr = mStaticTreeWalker.rCurrent().mAnimCollElems[i];
+ //mpZSorts[mpZSorts.mUseSize-1].shaderUID = mpZSorts[mpZSorts.mUseSize-1].entityPtr->GetShaderUID();
+
+ zSortBlah newBlah;
+ newBlah.entityPtr = mStaticTreeWalker.rCurrent().mAnimCollElems[i];
+ newBlah.shaderUID = newBlah.entityPtr->GetShaderUID();
+ mpZSorts.push_back( newBlah );
+ //mpZSorts[mpZSorts.mUseSize-1]->SetRank(CamPosn);
+ //mSort1.push_back(mStaticTreeWalker.rCurrent().mAnimCollElems[i]);
+ }
+ else
+ {
+ //mpZSortsPass2.AddUse(1);
+ //mpZSortsPass2[mpZSortsPass2.mUseSize-1] = mStaticTreeWalker.rCurrent().mAnimCollElems[i];
+ //mpZSortsPass2[mpZSortsPass2.mUseSize-1]->SetRank(CamPosn);
+
+ IEntityDSG* pDSGPtr = mStaticTreeWalker.rCurrent().mAnimCollElems[i];;
+ pDSGPtr->SetRank( CamPosn, mViewVector );
+ mpZSortsPass2.push_back( pDSGPtr );
+ //mSort2.push_back(mStaticTreeWalker.rCurrent().mAnimCollElems[i]);
+ }
+ }
+#else
+ if(mStaticTreeWalker.rCurrent().mAnimCollElems[i]->CastsShadow())
+ {
+ mShadowCasters.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mAnimCollElems[i]);
+ }
+ else
+ {
+ mStaticTreeWalker.rCurrent().mAnimCollElems[i]->Display();
+ }
+#endif
+ DebugRenderGeoCount++;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ for( i= mStaticTreeWalker.rCurrent().mAnimElems.mUseSize-1; i>-1; i-- )
+ {
+#ifdef ZSORT_RENDER
+ // if( mStaticTreeWalker.rCurrent().mAnimElems[i]->GetUID() == tName::MakeUID("smokecolumn") )
+ // rReleasePrintf("n");
+
+ mStaticTreeWalker.rCurrent().mAnimElems[i]->GetBoundingSphere(&ObjectSphere);
+ if(!IsSphereInCone(ObjectSphere.centre, ObjectSphere.radius))
+ continue;
+
+ switch(3)//mStaticTreeWalker.rCurrent().mAnimElems[i]->CastsShadow())
+ {
+ case 1:
+ mShadowCastersPass1.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mAnimElems[i]);
+ break;
+ case 2:
+ {
+ //mShadowCastersPass2.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mAnimElems[i]);
+ IEntityDSG* newDSGPtr = mStaticTreeWalker.rCurrent().mAnimElems[i];
+ newDSGPtr->SetRank( CamPosn, mViewVector );
+ mpZSortsPassShadowCasters.push_back( newDSGPtr );
+ }
+ break;
+ default:
+ if(!mStaticTreeWalker.rCurrent().mAnimElems[i]->mTranslucent && mStaticTreeWalker.rCurrent().mAnimElems[i]->CastsShadow()==0)
+ {
+ //mpZSorts.AddUse(1);
+ //mpZSorts[mpZSorts.mUseSize-1].entityPtr = mStaticTreeWalker.rCurrent().mAnimElems[i];
+ //mpZSorts[mpZSorts.mUseSize-1].shaderUID = mpZSorts[mpZSorts.mUseSize-1].entityPtr->GetShaderUID();
+
+ zSortBlah newBlah;
+ newBlah.entityPtr = mStaticTreeWalker.rCurrent().mAnimElems[i];
+ newBlah.shaderUID = newBlah.entityPtr->GetShaderUID();
+ mpZSorts.push_back( newBlah );
+ //mpZSorts[mpZSorts.mUseSize-1]->SetRank(CamPosn);
+ //mSort1.push_back(mStaticTreeWalker.rCurrent().mAnimElems[i]);
+ }
+ else
+ {
+ // mpZSortsPass2.AddUse(1);
+ IEntityDSG* newDSGPtr = mStaticTreeWalker.rCurrent().mAnimElems[i];
+ newDSGPtr->SetRank(CamPosn,mViewVector);
+ mpZSortsPass2.push_back( newDSGPtr );
+ //mpZSortsPass2[mpZSortsPass2.mUseSize-1] = mStaticTreeWalker.rCurrent().mAnimElems[i];
+ //mpZSortsPass2[mpZSortsPass2.mUseSize-1]->SetRank(CamPosn);
+
+ //mSort2.push_back(mStaticTreeWalker.rCurrent().mAnimElems[i]);
+ }
+ }
+#else
+ if(mStaticTreeWalker.rCurrent().mAnimElems[i]->CastsShadow())
+ {
+ mShadowCasters.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mAnimElems[i]);
+ }
+ else
+ {
+ mStaticTreeWalker.rCurrent().mAnimElems[i]->Display();
+ }
+#endif
+ DebugRenderGeoCount++;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ for( i= mStaticTreeWalker.rCurrent().mDPhysElems.mUseSize-1; i>-1; i-- )
+ {
+#ifdef ZSORT_RENDER
+#ifdef DEBUGWATCH
+ // unsigned int dbwt=radTimeGetMicroseconds();
+#endif
+ mStaticTreeWalker.rCurrent().mDPhysElems[i]->GetBoundingSphere(&ObjectSphere);
+ if(!IsSphereInCone(ObjectSphere.centre, ObjectSphere.radius))
+ continue;
+
+ switch(mStaticTreeWalker.rCurrent().mDPhysElems[i]->CastsShadow())
+ {
+ /*
+ case 1:
+ mShadowCastersPass1.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mDPhysElems[i]);
+ break;
+ case 2:
+ {
+ //mShadowCastersPass2.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mDPhysElems[i]);
+ IEntityDSG* newDSGPtr = mStaticTreeWalker.rCurrent().mDPhysElems[i];
+ newDSGPtr->SetRank( CamPosn, mViewVector );
+ mpZSortsPassShadowCasters.push_back( newDSGPtr );
+ }
+ break;
+ */
+ case 989: // <--Vehicle code
+ // Dump the vehicle into the mpZSortsPassShadowCasters array so that
+ // displaysimpleshadow will be called on it, also let it slide into the
+ // default case, so that it gets tossed into the normal mpZSorts (normal
+ // translucent object
+ {
+ IEntityDSG* object = mStaticTreeWalker.rCurrent().mDPhysElems[i];
+ rTuneAssert( dynamic_cast< Vehicle* >( object ) != NULL );
+ mShadowCastersPass1.Add( object );
+ }
+ default:
+ if(!mStaticTreeWalker.rCurrent().mDPhysElems[i]->mTranslucent && mStaticTreeWalker.rCurrent().mDPhysElems[i]->CastsShadow()==0)
+ {
+ // mpZSorts.AddUse(1);
+ //mpZSorts[mpZSorts.mUseSize-1].entityPtr = mStaticTreeWalker.rCurrent().mDPhysElems[i];
+ //mpZSorts[mpZSorts.mUseSize-1].shaderUID = mpZSorts[mpZSorts.mUseSize-1].entityPtr->GetShaderUID();
+
+ zSortBlah newBlah;
+ newBlah.entityPtr = mStaticTreeWalker.rCurrent().mDPhysElems[i];
+ newBlah.shaderUID = newBlah.entityPtr->GetShaderUID();
+ mpZSorts.push_back( newBlah );
+ //mpZSorts[mpZSorts.mUseSize-1]->SetRank(CamPosn);
+ //mSort1.push_back(mStaticTreeWalker.rCurrent().mDPhysElems[i]);
+ }
+ else
+ {
+ IEntityDSG* newDSGPtr = mStaticTreeWalker.rCurrent().mDPhysElems[i];
+ newDSGPtr->SetRank(CamPosn,mViewVector);
+ mpZSortsPass2.push_back( newDSGPtr );
+ //mpZSortsPass2.AddUse(1);
+ //mpZSortsPass2[mpZSortsPass2.mUseSize-1] = mStaticTreeWalker.rCurrent().mDPhysElems[i];
+ //mpZSortsPass2[mpZSortsPass2.mUseSize-1]->SetRank(CamPosn);
+ //mSort2.push_back(mStaticTreeWalker.rCurrent().mDPhysElems[i]);
+ }
+ }
+#ifdef DEBUGWATCH
+ // mDebugZSAddTiming += radTimeGetMicroseconds()-dbwt;
+#endif
+#else
+ if(mStaticTreeWalker.rCurrent().mDPhysElems[i]->CastsShadow())
+ {
+ mShadowCasters.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mDPhysElems[i]);
+ }
+ else
+ {
+ mStaticTreeWalker.rCurrent().mDPhysElems[i]->Display();
+ }
+#endif
+ DebugRenderGeoCount++;
+ }
+ //////////////////////////////////////////////////////////////////////////
+ for( i= mStaticTreeWalker.rCurrent().mSPhysElems.mUseSize-1; i>-1; i-- )
+ {
+#ifdef ZSORT_RENDER
+#ifdef DEBUGWATCH
+ // unsigned int dbwt=radTimeGetMicroseconds();
+#endif
+ mStaticTreeWalker.rCurrent().mSPhysElems[i]->GetBoundingSphere(&ObjectSphere);
+ if(!IsSphereInCone(ObjectSphere.centre, ObjectSphere.radius))
+ continue;
+
+ switch(3)//mStaticTreeWalker.rCurrent().mSPhysElems[i]->CastsShadow())
+ {
+ case 1:
+ mShadowCastersPass1.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mSPhysElems[i]);
+ break;
+ case 2:
+ {
+ //mShadowCastersPass2.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mSPhysElems[i]);
+ IEntityDSG* newDSGPtr = mStaticTreeWalker.rCurrent().mSPhysElems[i];
+ newDSGPtr->SetRank( CamPosn, mViewVector );
+ mpZSortsPassShadowCasters.push_back( newDSGPtr );
+ }
+ break;
+ default:
+ if(!mStaticTreeWalker.rCurrent().mSPhysElems[i]->mTranslucent && mStaticTreeWalker.rCurrent().mSPhysElems[i]->CastsShadow()==0)
+ {
+ //mpZSorts.AddUse(1);
+ //mpZSorts[mpZSorts.mUseSize-1].entityPtr = mStaticTreeWalker.rCurrent().mSPhysElems[i];
+ //mpZSorts[mpZSorts.mUseSize-1].shaderUID = mpZSorts[mpZSorts.mUseSize-1].entityPtr->GetShaderUID();
+
+ zSortBlah newBlah;
+ newBlah.entityPtr = mStaticTreeWalker.rCurrent().mSPhysElems[i];
+ newBlah.shaderUID = newBlah.entityPtr->GetShaderUID();
+ mpZSorts.push_back( newBlah );
+ //mpZSorts[mpZSorts.mUseSize-1]->SetRank(CamPosn);
+ //mSort1.push_back(mStaticTreeWalker.rCurrent().mSPhysElems[i]);
+ }
+ else
+ {
+ IEntityDSG* newDSGPtr = mStaticTreeWalker.rCurrent().mSPhysElems[i];
+ newDSGPtr->SetRank(CamPosn, mViewVector);
+ mpZSortsPass2.push_back( newDSGPtr );
+ //mpZSortsPass2.AddUse(1);
+ //mpZSortsPass2[mpZSortsPass2.mUseSize-1] = mStaticTreeWalker.rCurrent().mSPhysElems[i];
+ //mpZSortsPass2[mpZSortsPass2.mUseSize-1]->SetRank(CamPosn);
+ //mSort2.push_back(mStaticTreeWalker.rCurrent().mSPhysElems[i]);
+ }
+ }
+#ifdef DEBUGWATCH
+ // mDebugZSAddTiming += radTimeGetMicroseconds()-dbwt;
+#endif
+#else
+ if(mStaticTreeWalker.rCurrent().mSPhysElems[i]->CastsShadow())
+ {
+ mShadowCasters.Add((IEntityDSG*&)mStaticTreeWalker.rCurrent().mSPhysElems[i]);
+ }
+ else
+ {
+ mStaticTreeWalker.rCurrent().mSPhysElems[i]->Display();
+ }
+#endif
+ DebugRenderGeoCount++;
+ }
+/* for( i= mStaticTreeWalker.rCurrent().mTrigVolElems.mUseSize-1; i>-1; i-- )
+ {
+#ifdef ZSORT_RENDER
+#ifdef DEBUGWATCH
+ // unsigned int dbwt=radTimeGetMicroseconds();
+#endif
+ if(!mStaticTreeWalker.rCurrent().mTrigVolElems[i]->mTranslucent)
+ {
+ mpZSorts.AddUse(1);
+ mpZSorts[mpZSorts.mUseSize-1] = mStaticTreeWalker.rCurrent().mTrigVolElems[i];
+ //mpZSorts[mpZSorts.mUseSize-1]->SetRank(CamPosn);
+ //mSort1.push_back(mStaticTreeWalker.rCurrent().mTrigVolElems[i]);
+ }
+ else
+ {
+ mpZSortsPass2.AddUse(1);
+ mpZSortsPass2[mpZSortsPass2.mUseSize-1] = mStaticTreeWalker.rCurrent().mTrigVolElems[i];
+ mpZSortsPass2[mpZSortsPass2.mUseSize-1]->SetRank(CamPosn);
+ //mSort2.push_back(mStaticTreeWalker.rCurrent().mTrigVolElems[i]);
+ }
+#ifdef DEBUGWATCH
+ // mDebugZSAddTiming += radTimeGetMicroseconds()-dbwt;
+#endif
+#else
+ mStaticTreeWalker.rCurrent().mTrigVolElems[i]->Display();
+#endif
+ DebugRenderGeoCount++;
+ }*/
+ }
+
+// rAssert(DebugRenderNodeCount<1000);
+END_PROFILE("list construction")
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+#ifdef ZSORT_RENDER
+ #ifdef DEBUGWATCH
+ mDebugZSWalkTiming = radTimeGetMicroseconds()-mDebugZSWalkTiming;
+ mDebugZSSortTiming = radTimeGetMicroseconds();
+ #endif
+ /*
+ NodeFLL* pHead = &(mpZSorts[mpZSorts.mUseSize-1]);
+ for(int i=mpZSorts.mUseSize-2; i>-1; i--)
+ {
+ pHead = pHead->PlaceHighestFirst(&(mpZSorts[i]));
+ }
+ */
+ #ifndef RAD_RELEASE
+ BEGIN_PROFILE("STD::sort1")
+ gTestShader shaderTestObj;
+ std::sort(mSort1.begin(),mSort1.end(), shaderTestObj);
+ END_PROFILE("STD::sort1")
+
+ BEGIN_PROFILE("STD::sort2")
+ gTestZ zTestObj;
+ std::sort(mSort2.begin(),mSort2.end(), zTestObj);
+ END_PROFILE("STD::sort2")
+ #endif
+
+#ifndef TEST_DISTRIBUTED_SORT
+ BEGIN_PROFILE("qsort1")
+ //qsort(mpZSorts.mpData, (size_t)mpZSorts.mUseSize, sizeof(zSortBlah), gShaderCompare);
+ gShaderCompare blahShaderCompareObj;
+ std::sort( mpZSorts.begin(), mpZSorts.end(), blahShaderCompareObj );
+ END_PROFILE("qsort1")
+
+ BEGIN_PROFILE("qsort2")
+ //qsort(mpZSortsPass2.mpData, (size_t)mpZSortsPass2.mUseSize, sizeof(IEntityDSG*), gZSortCompare);
+ gZSortCompare dsgZSortObj;
+ std::sort( mpZSortsPass2.begin(), mpZSortsPass2.end(), dsgZSortObj );
+ END_PROFILE("qsort2")
+#endif
+ //BEGIN_PROFILE("shadow caster zsort")
+ // std::sort( mpZSortsPassShadowCasters.begin(), mpZSortsPassShadowCasters.end(), dsgZSortObj );
+ //END_PROFILE("shadow caster zsort")
+
+ #ifdef DEBUGWATCH
+ mDebugZSSortTiming = radTimeGetMicroseconds()-mDebugZSSortTiming;
+ #endif
+
+ #ifdef TRACK_SHADERS
+ int avgUse=0, totalCount=0, unsortableCount;
+ UIDMap::iterator it;
+ if(sRenderCall==200)
+ {
+ for (it = UniqueShaders.begin(); it != UniqueShaders.end(); it++)
+ {
+ rTunePrintf("UniqueShader %d use count %d\n", (*it).first, (*it).second );
+ if((*it).first != tName::MakeUID("__none__"))
+ {
+ avgUse += (*it).second;
+ totalCount++;
+ }
+ else
+ {
+ unsortableCount = (*it).second;
+ }
+ }
+ rTunePrintf("Avg Use: %d Total Sortable Count: %d Unsortable Shader Count: %d Alpha Shaders %d\n", avgUse/totalCount, totalCount, unsortableCount, mpZSortsPass2.mUseSize);
+ }
+ #endif
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+#endif
+ //////////////////////////////////////////////////////////////////////////
+ //Debug Collision volumes
+ //////////////////////////////////////////////////////////////////////////
+ /*
+ ReserveArray<StaticPhysDSG*>& rCollisionElems = mStaticTreeWalker.rCurrent().mDynamicElems;
+ for( int i=rCollisionElems.mUseSize-1; i>-1; i-- )
+ {
+ rCollisionElems[i]->DisplayBoundingBox();
+
+ }
+ */
+
+#ifdef DEBUGWATCH
+
+ int activeArea = -1;
+
+ Avatar* av = GetAvatarManager()->GetAvatarForPlayer(0);
+ Vehicle* v = av->GetVehicle();
+ Character* c = av->GetCharacter();
+ if(v)
+ {
+ activeArea = v->mCollisionAreaIndex;
+ }
+ else if(c)
+ {
+ activeArea = c->GetCollisionAreaIndex();
+ }
+
+
+ // call to martin's sim display
+ if(mDebugSimCollisionVolumeDrawing && /*v &&*/ activeArea > -1)
+ {
+ sim::DisplayCollisionObjects(GetWorldPhysicsManager()->mCollisionManager, activeArea);
+ }
+
+ if(mDebugFenceCollisionVolumeDrawing && activeArea > -1)
+ {
+ GetWorldPhysicsManager()->DisplayFencesInArea(activeArea);
+
+
+ }
+
+ if(mDebugVehicleCollisionDrawing && v)
+ {
+ v->DebugDisplay();
+ v->CarDisplay(false);
+ }
+ else if(v)
+ {
+ v->CarDisplay(true);
+ }
+
+ if(mDebugSimStatsDisplay)
+ {
+ // this makes things grind to a fucking halt
+ sim::SimStats::DisplayStats();
+ }
+
+ if(activeArea > -1)
+ {
+ mDebugWatchNumCollisionPairs = GetWorldPhysicsManager()->mCollisionManager->GetCollisionObjectPairList(activeArea)->GetSize();
+ }
+ else
+ {
+ mDebugWatchNumCollisionPairs = -1;
+ }
+
+
+ // set Z Compare back
+ if(mDebugFenceCollisionVolumeDrawing)
+ {
+ // p3d::pddi->SetZCompare(restoreZMode);
+ }
+
+#endif
+
+
+
+//////////////////////////////////////////////////////////////////////////
+#ifdef DEBUGWATCH
+ t1 = radTimeGetMicroseconds();
+#endif
+#ifdef DEBUGWATCH
+ tRenderAcc += radTimeGetMicroseconds()-t1;
+#endif
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+#ifdef DEBUGWATCH
+ t1 = radTimeGetMicroseconds();
+ mDebugWalkTiming = t1-t0;
+ mDebugRenderTiming = tRenderAcc;
+#endif
+//////////////////////////////////////////////////////////////////////////
+
+ if(DebugRenderGeoCount > sMaxDebugGeoCount )
+ sMaxDebugGeoCount = DebugRenderGeoCount;
+ //Assert: we are not drawing more geometries that we have
+ //(ie, drawing everything twice or something)
+ //rAssert( DebugRenderGeoCount <= mStaticGeos.mUseSize );
+}
+//========================================================================
+// WorldScene::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldScene::RenderShadows()
+{
+ for( int i=mShadowCastersPass1.mUseSize-1; i>-1; i-- )
+ {
+ mShadowCastersPass1[i]->DisplayShadow();
+ }
+// for( int i=mShadowCastersPass2.mUseSize-1; i>-1; i-- )
+// {
+// mShadowCastersPass2[i]->DisplayShadow();
+// }
+ for(int i=mpZSortsPassShadowCasters.size()-1; i>-1; i--)
+ {
+ mpZSortsPassShadowCasters[i]->DisplayShadow();
+ }
+}
+
+/*========================================================================
+Draw simple blob shadows under a character. Don't call within the normal
+shadow generator setup since the tris won't shade properly.
+========================================================================*/
+void WorldScene::RenderSimpleShadows( void )
+{
+ p3d::pddi->SetZWrite(false);
+ for( int i = 0; i < mShadowCastersPass1.mUseSize; ++i )
+ {
+ mShadowCastersPass1[ i ]->DisplaySimpleShadow();
+ }
+// for( int i = 0; i < mShadowCastersPass2.mUseSize; ++i )
+// {
+// mShadowCastersPass2[ i ]->DisplaySimpleShadow();
+// }
+ for(int i=mpZSortsPassShadowCasters.size()-1; i>-1; i--)
+ {
+ mpZSortsPassShadowCasters[i]->DisplaySimpleShadow();
+ }
+ p3d::pddi->SetZWrite( true );
+}
+//========================================================================
+// WorldScene::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldScene::RenderShadowCasters()
+{
+DSG_SET_PROFILE('C')
+ for( int i=mShadowCastersPass1.mUseSize-1; i>-1; i-- )
+ {
+ // BEGIN_PROFILE("ShadowCastPass1")
+ mShadowCastersPass1[i]->Display();
+ // END_PROFILE("ShadowCastPass1")
+ }
+
+ //for( int i=mShadowCastersPass2.mUseSize-1; i>-1; i-- )
+ //{
+ // mShadowCastersPass2[i]->Display();
+ //}
+ for(int i=mpZSortsPassShadowCasters.size()-1; i>-1; i--)
+ {
+ mpZSortsPassShadowCasters[i]->Display();
+ }
+
+}
+
+
+void WorldScene::RenderOpaque( void )
+{
+#ifdef TEST_DISTRIBUTED_SORT
+ BEGIN_PROFILE("qsort1")
+ //qsort(mpZSorts.mpData, (size_t)mpZSorts.mUseSize, sizeof(zSortBlah), gShaderCompare);
+ gShaderCompare blahShaderCompareObj;
+ std::sort( mpZSorts.begin(), mpZSorts.end(), blahShaderCompareObj );
+ END_PROFILE("qsort1")
+#endif
+DSG_SET_PROFILE('O')
+BEGIN_PROFILE("qsort display")
+ for(int i=mpZSorts.size() - 1; i>-1; i--)
+ {
+//BEGIN_PROFILE("opaque inner")
+ mpZSorts[i].entityPtr->Display();
+#ifdef TRACK_SHADERS
+ UniqueShaders[mpZSorts[i].entityPtr->GetShaderUID()]++;
+#endif
+//END_PROFILE("opaque inner")
+ }
+END_PROFILE("qsort display")
+}
+
+void WorldScene::RenderTranslucent( void )
+{
+ #ifdef RAD_GAMECUBE
+ RefractionShader::AllowOneBufferCapture();
+ #endif
+
+#ifdef TEST_DISTRIBUTED_SORT
+ BEGIN_PROFILE("qsort2")
+ //qsort(mpZSortsPass2.mpData, (size_t)mpZSortsPass2.mUseSize, sizeof(IEntityDSG*), gZSortCompare);
+ gZSortCompare dsgZSortObj;
+ std::sort( mpZSortsPass2.begin(), mpZSortsPass2.end(), dsgZSortObj );
+ END_PROFILE("qsort2")
+#endif
+DSG_SET_PROFILE('T')
+BEGIN_PROFILE("qsort2 display")
+ for(int i=mpZSortsPass2.size()-1; i>-1; i--)
+ {
+//BEGIN_PROFILE("translucent inner")
+ mpZSortsPass2[i]->Display();
+//END_PROFILE("translucent inner")
+ }
+END_PROFILE("qsort2 display")
+}
+
+//========================================================================
+// WorldScene::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldScene::Render( unsigned int viewIndex )
+{
+ //mStaticTreeWalker.MarkTree( msVisible );
+ mStaticTreeWalker.AndTree( msClear );
+ //mStaticTreeWalker.OrTree( msVisible );
+ //BEGIN_PROFILE("Mark Camera")
+
+ tPointCamera* pCam;
+
+//////////////////////////////////////////////////////////////////////////
+#ifdef DEBUGWATCH
+ unsigned int t0, t1;
+#endif
+//////////////////////////////////////////////////////////////////////////
+ switch( viewIndex )
+ {
+ case 0:
+ {
+//////////////////////////////////////////////////////////////////////////
+#ifdef DEBUGWATCH
+ t0 = radTimeGetMicroseconds();
+#endif
+//////////////////////////////////////////////////////////////////////////
+ pCam = (tPointCamera*)GetSuperCamManager()->GetSCC(0)->GetCamera();
+BEGIN_PROFILE("cam viz")
+ if( mRenderAll ) mStaticTreeWalker.OrTreeVis( msVisible0 );
+ else MarkCameraVisible( pCam, msVisible0 );
+
+END_PROFILE("cam viz")
+//////////////////////////////////////////////////////////////////////////
+#ifdef DEBUGWATCH
+ t1 = radTimeGetMicroseconds();
+ mDebugMarkTiming = t1-t0;
+#endif
+//////////////////////////////////////////////////////////////////////////
+ RenderScene( msVisible0, pCam );
+ break;
+ }
+ case 1:
+ {
+ pCam = (tPointCamera*)GetSuperCamManager()->GetSCC(1)->GetCamera();
+ MarkCameraVisible( pCam, msVisible1 );
+ RenderScene( msVisible1, pCam );
+ break;
+ }
+ default:
+ {
+ rAssertMsg(false, "Only supporting 1 or 2 players right now.");
+ break;
+ }
+ }
+
+ //END_PROFILE("Mark Camera")
+
+
+
+}
+
+////////////////////////////////////////////////////////////////////
+//-------------------Private--------------------------------------//
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+void WorldScene::GenerateStaticTree()
+{/*
+ Bounds3f WorldBounds;
+ rmt::Box3D BBox;
+ int i;
+ SpatialTreeFactory StaticTF;
+
+ StaticTF.Reset( (mStaticIntersects.mUseSize+mStaticGeos.mUseSize) * 2 +2);
+
+ mStaticGeos[0]->GetBoundingBox( &BBox ); WorldBounds.mMin.SetTo( BBox.low ); WorldBounds.mMax.SetTo( BBox.high );
+
+ ///////////////////////////////////////////////////////////////////////
+ // Seed the TreeFactory with data from all the drawable elements
+ ///////////////////////////////////////////////////////////////////////
+ for( i=mStaticGeos.mUseSize-1; i>-1; i-- )
+ {
+ mStaticGeos[i]->GetBoundingBox( &BBox );
+ StaticTF.Seed( ((Vector3f&)BBox.low), 0 );
+ StaticTF.Seed( ((Vector3f&)BBox.high), mStaticGeos[i]->GetNumPrimGroup() );
+ WorldBounds.Accumulate( (Vector3f&)BBox.low );
+ WorldBounds.Accumulate( (Vector3f&)BBox.high );
+ //used to use low, but that'll lead to more bad volume intersects
+ }
+ ///////////////////////////////////////////////////////////////////////
+ // Seed the TreeFactory with data from all the intersect elements
+ ///////////////////////////////////////////////////////////////////////
+ for( i=mStaticIntersects.mUseSize-1; i>-1; i-- )
+ {
+ mStaticIntersects[i]->GetBoundingBox( &BBox );
+ StaticTF.Seed( ((Vector3f&)BBox.low), 0 );
+ StaticTF.Seed( ((Vector3f&)BBox.high), mStaticIntersects[i]->GetNumPrimGroup() );
+ WorldBounds.Accumulate( (Vector3f&)BBox.low );
+ WorldBounds.Accumulate( (Vector3f&)BBox.high );
+ //used to use low, but that'll lead to more bad volume intersects
+ }
+
+ //Add a coupla bounding seeds; bound correction to handle
+ //p3dsplit/modified, which subdivides from the origin
+ WorldBounds.mMin.x = rmt::Floor(WorldBounds.mMin.x/TODO_GRANULARITY.x)*TODO_GRANULARITY.x;
+ WorldBounds.mMin.y = rmt::Floor(WorldBounds.mMin.y/TODO_GRANULARITY.y)*TODO_GRANULARITY.y;
+ WorldBounds.mMin.z = rmt::Floor(WorldBounds.mMin.z/TODO_GRANULARITY.z)*TODO_GRANULARITY.z;
+ StaticTF.Seed( WorldBounds.mMin, 0 );
+ StaticTF.Seed( WorldBounds.mMax, 0 );
+
+ ///////////////////////////////////////////////////////////////////////
+ // TreeFactory: Grow a Tree, damn you!
+ ///////////////////////////////////////////////////////////////////////
+ TODO_GRANULARITY.y = WorldBounds.mMax.y - WorldBounds.mMin.y;
+ StaticTF.Generate( TODO_GRANULARITY );
+
+ StaticTF.ExtractTree( &mpStaticTree );
+
+ mStaticTreeWalker.SetToRoot( *mpStaticTree );*/
+}
+////////////////////////////////////////////////////////////////////
+// TODO: this can be per-function templatized for all member types
+////////////////////////////////////////////////////////////////////
+void WorldScene::PopulateStaticTree()
+{
+ BoxPts DrawableSP;
+ rmt::Box3D BBox;
+ int i;
+
+ int max=mStaticTreeWalker.NumNodes();
+ for(i=0; i<max; i++)
+ {
+ mStaticTreeWalker.rIthNode(i).mSEntityElems.Allocate();
+ mStaticTreeWalker.rIthNode(i).mSPhysElems.Allocate();
+ mStaticTreeWalker.rIthNode(i).mIntersectElems.Allocate();
+ mStaticTreeWalker.rIthNode(i).mDPhysElems.Allocate();
+ mStaticTreeWalker.rIthNode(i).mFenceElems.Allocate();
+ mStaticTreeWalker.rIthNode(i).mAnimCollElems.Allocate();
+ mStaticTreeWalker.rIthNode(i).mAnimElems.Allocate();
+ mStaticTreeWalker.rIthNode(i).mTrigVolElems.Allocate();
+ mStaticTreeWalker.rIthNode(i).mRoadSegmentElems.Allocate();
+ mStaticTreeWalker.rIthNode(i).mPathSegmentElems.Allocate();
+ }
+
+}
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+//void WorldScene::PlaceStaticGeo( tGeometry* pGeometry )
+//{
+// rAssert(false);
+ //This currently fails because PopulateStaticTree doesn't
+ //reserve any extra places for geo to be added
+//}
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+void WorldScene::Place( IntersectDSG* ipIntersectDSG )
+{
+ //rAssert(false);
+ //This currently fails because PopulateStaticTree doesn't
+ //reserve any extra places for geo to be added
+ BoxPts DrawableSP;
+ rmt::Box3D BBox;
+
+ ipIntersectDSG->GetBoundingBox( &BBox );
+
+ DrawableSP.mBounds.mMin.SetTo( BBox.low );
+ DrawableSP.mBounds.mMax.SetTo( BBox.high );
+ DrawableSP.mBounds.mMin.Add(mEpsilonOffset);
+ DrawableSP.mBounds.mMax.Sub(mEpsilonOffset);
+
+ //mStaticTreeWalker.Place( (ISpatialProxyAA&)DrawableSP, mStaticEntities[i] );
+ //mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP).mIntersectElems.Add(ipIntersectDSG);
+ SpatialNode* pSpatialNode = &(mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP));
+ pSpatialNode->mIntersectElems.Add(ipIntersectDSG);
+ ipIntersectDSG->mpSpatialNode = pSpatialNode;
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Place( StaticEntityDSG* ipStaticEntity )
+{
+ //rAssert(false);
+ //This currently fails because PopulateStaticTree doesn't
+ //reserve any extra places for stuff to be added
+ BoxPts DrawableSP;
+ rmt::Box3D BBox;
+
+ ipStaticEntity->GetBoundingBox( &BBox );
+
+ DrawableSP.mBounds.mMin.SetTo( BBox.low );
+ DrawableSP.mBounds.mMax.SetTo( BBox.high );
+ DrawableSP.mBounds.mMin.Add(mEpsilonOffset);
+ DrawableSP.mBounds.mMax.Sub(mEpsilonOffset);
+
+ //mStaticTreeWalker.Place( (ISpatialProxyAA&)DrawableSP, mStaticEntities[i] );
+
+ SpatialNode* pSpatialNode = &(mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP));
+
+ if(! pSpatialNode->mSEntityElems.Add(ipStaticEntity))
+ {
+ pSpatialNode = &(mStaticTreeWalker.rIthNode(0));
+ if( ! pSpatialNode->mSEntityElems.Add(ipStaticEntity) )
+ {
+ rTuneAssert(false);
+ }
+ else
+ {
+ ipStaticEntity->mpSpatialNode = pSpatialNode;
+ }
+ }
+ else
+ {
+ ipStaticEntity->mpSpatialNode = pSpatialNode;
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Place( StaticPhysDSG* ipStaticPhys )
+{
+ //rAssert(false);
+ //This currently fails because PopulateStaticTree doesn't
+ //reserve any extra places for stuff to be added
+ BoxPts DrawableSP;
+ rmt::Box3D BBox;
+
+ ipStaticPhys->GetBoundingBox( &BBox );
+
+ DrawableSP.mBounds.mMin.SetTo( BBox.low );
+ DrawableSP.mBounds.mMax.SetTo( BBox.high );
+ DrawableSP.mBounds.mMin.Add(mEpsilonOffset);
+ DrawableSP.mBounds.mMax.Sub(mEpsilonOffset);
+
+ //mStaticTreeWalker.Place( (ISpatialProxyAA&)DrawableSP, mStaticEntities[i] );
+ //mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP).mSPhysElems.Add(ipStaticPhys);
+ SpatialNode* pSpatialNode = &(mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP));
+ pSpatialNode->mSPhysElems.Add(ipStaticPhys);
+ ipStaticPhys->mpSpatialNode = pSpatialNode;
+
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Place( DynaPhysDSG* ipDynaPhys )
+{
+ //rAssert(false);
+ //This currently fails because PopulateStaticTree doesn't
+ //reserve any extra places for stuff to be added
+ BoxPts DrawableSP;
+ rmt::Box3D BBox;
+
+ ipDynaPhys->GetBoundingBox( &BBox );
+
+ DrawableSP.mBounds.mMin.SetTo( BBox.low );
+ DrawableSP.mBounds.mMax.SetTo( BBox.high );
+ DrawableSP.mBounds.mMin.Add(mEpsilonOffset);
+ DrawableSP.mBounds.mMax.Sub(mEpsilonOffset);
+
+
+ SpatialNode* pSpatialNode = &(mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP));
+
+ if(! pSpatialNode->mDPhysElems.Add(ipDynaPhys))
+ {
+ pSpatialNode = &(mStaticTreeWalker.rIthNode(0));
+ if( ! pSpatialNode->mDPhysElems.Add(ipDynaPhys) )
+ {
+ rTuneAssert(false);
+ }
+ else
+ {
+ ipDynaPhys->mpSpatialNode = pSpatialNode;
+ }
+ }
+ else
+ {
+ ipDynaPhys->mpSpatialNode = pSpatialNode;
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Place( FenceEntityDSG* ipFence )
+{
+ //rAssert(false);
+ //This currently fails because PopulateStaticTree doesn't
+ //reserve any extra places for stuff to be added
+ BoxPts DrawableSP;
+ rmt::Box3D BBox;
+
+ ipFence->GetBoundingBox( &BBox );
+
+ DrawableSP.mBounds.mMin.SetTo( BBox.low );
+ DrawableSP.mBounds.mMax.SetTo( BBox.high );
+ DrawableSP.mBounds.mMin.Add(mEpsilonOffset);
+ DrawableSP.mBounds.mMax.Sub(mEpsilonOffset);
+
+ //mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP).mFenceElems.Add(ipFence);
+ SpatialNode* pSpatialNode = &(mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP));
+ pSpatialNode->mFenceElems.Add(ipFence);
+ ipFence->mpSpatialNode = pSpatialNode;
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Place( AnimCollisionEntityDSG* ipAnimColl )
+{
+ //rAssert(false);
+ //This currently fails because PopulateStaticTree doesn't
+ //reserve any extra places for stuff to be added
+ BoxPts DrawableSP;
+ rmt::Box3D BBox;
+
+ ipAnimColl->GetBoundingBox( &BBox );
+
+ DrawableSP.mBounds.mMin.SetTo( BBox.low );
+ DrawableSP.mBounds.mMax.SetTo( BBox.high );
+ DrawableSP.mBounds.mMin.Add(mEpsilonOffset);
+ DrawableSP.mBounds.mMax.Sub(mEpsilonOffset);
+
+
+ SpatialNode* pSpatialNode = &(mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP));
+
+ if(! pSpatialNode->mAnimCollElems.Add(ipAnimColl))
+ {
+ pSpatialNode = &(mStaticTreeWalker.rIthNode(0));
+ if( ! pSpatialNode->mAnimCollElems.Add(ipAnimColl) )
+ {
+ rTuneAssert(false);
+ }
+ else
+ {
+ ipAnimColl->mpSpatialNode = pSpatialNode;
+ }
+ }
+ else
+ {
+ ipAnimColl->mpSpatialNode = pSpatialNode;
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Place( AnimEntityDSG* ipAnim )
+{
+// if( ipAnim->GetUID() == tName::MakeUID("smokecolumn") )
+// rReleasePrintf("hjeifjijf");
+
+
+ //rAssert(false);
+ //This currently fails because PopulateStaticTree doesn't
+ //reserve any extra places for stuff to be added
+ BoxPts DrawableSP;
+ rmt::Box3D BBox;
+
+ ipAnim->GetBoundingBox( &BBox );
+
+ DrawableSP.mBounds.mMin.SetTo( BBox.low );
+ DrawableSP.mBounds.mMax.SetTo( BBox.high );
+ DrawableSP.mBounds.mMin.Add(mEpsilonOffset);
+ DrawableSP.mBounds.mMax.Sub(mEpsilonOffset);
+
+ SpatialNode* pSpatialNode = &(mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP));
+
+ if(! pSpatialNode->mAnimElems.Add(ipAnim))
+ {
+ pSpatialNode = &(mStaticTreeWalker.rIthNode(0));
+ if( ! pSpatialNode->mAnimElems.Add(ipAnim) )
+ {
+ rTuneAssert(false);
+ }
+ else
+ {
+ ipAnim->mpSpatialNode = pSpatialNode;
+ }
+ }
+ else
+ {
+ ipAnim->mpSpatialNode = pSpatialNode;
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Place( TriggerVolume* ipTriggerVolume )
+{
+ BoxPts DrawableSP;
+ rmt::Box3D BBox;
+
+ ipTriggerVolume->GetBoundingBox( &BBox );
+
+ DrawableSP.mBounds.mMin.SetTo( BBox.low );
+ DrawableSP.mBounds.mMax.SetTo( BBox.high );
+ DrawableSP.mBounds.mMin.Add(mEpsilonOffset);
+ DrawableSP.mBounds.mMax.Sub(mEpsilonOffset);
+
+ //mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP).mTrigVolElems.Add(ipTriggerVolume);
+ SpatialNode* pSpatialNode = &(mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP));
+ pSpatialNode->mTrigVolElems.Add(ipTriggerVolume);
+ ipTriggerVolume->mpSpatialNode = pSpatialNode;
+}
+
+////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Place( RoadSegment* ipRoadSegment )
+{
+ BoxPts DrawableSP;
+ rmt::Box3D BBox;
+
+ ipRoadSegment->GetBoundingBox( &BBox );
+
+ DrawableSP.mBounds.mMin.SetTo( BBox.low );
+ DrawableSP.mBounds.mMax.SetTo( BBox.high );
+ DrawableSP.mBounds.mMin.Add(mEpsilonOffset);
+ DrawableSP.mBounds.mMax.Sub(mEpsilonOffset);
+
+ //mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP).mRoadSegmentElems.Add(ipRoadSegment);
+ SpatialNode* pSpatialNode = &(mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP));
+ pSpatialNode->mRoadSegmentElems.Add(ipRoadSegment);
+ ipRoadSegment->mpSpatialNode = pSpatialNode;
+}
+
+////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldScene::Place( PathSegment* ipPathSegment )
+{
+ BoxPts DrawableSP;
+ rmt::Box3D BBox;
+
+ ipPathSegment->GetBoundingBox( &BBox );
+
+ DrawableSP.mBounds.mMin.SetTo( BBox.low );
+ DrawableSP.mBounds.mMax.SetTo( BBox.high );
+ DrawableSP.mBounds.mMin.Add(mEpsilonOffset);
+ DrawableSP.mBounds.mMax.Sub(mEpsilonOffset);
+
+ //mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP).mPathSegmentElems.Add(ipPathSegment);
+ SpatialNode* pSpatialNode = &(mStaticTreeWalker.rSeekNode((ISpatialProxyAA&)DrawableSP));
+ pSpatialNode->mPathSegmentElems.Add(ipPathSegment);
+ ipPathSegment->mpSpatialNode = pSpatialNode;
+}
+
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+bool WorldScene::IsPreTreeGen()
+{
+ if( mpStaticTree == NULL )
+ return true;
+ else
+ return false;
+}
+////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////
+bool WorldScene::IsPostTreeGen()
+{
+ if( mpStaticTree == NULL )
+ return false;
+ else
+ return true;
+}
+#include <render/culling/SphereSP.h>
+#include <p3d/debugdraw.hpp>
+////////////////////////////////////////////////////////////////////
+// TODO: Move this type of functionality elsewhere
+// I think WorldScene Should just maintain the data interfaces
+// and management, not state of said data nor manipulation
+// thereof
+///////////////////////////////////////////////////////////////////
+//static tShader* spDefaultShader = NULL;
+void WorldScene::MarkCameraVisible( tPointCamera* pCam, unsigned int iFilter )
+{
+
+
+ //HexahedronP ViewVolSP;
+#if 1
+ SphereSP ViewVolSP;
+ Vector3f CamPosn, ViewVector, FarPlaneExtentVect;
+ pCam->GetPosition(&CamPosn);
+ pCam->GetTarget(&ViewVector); ViewVector.Sub( CamPosn ); ViewVector.Normalize();
+
+ SetVisCone( ViewVector, CamPosn, pCam->GetFieldOfView()/1.87f );//this tweak was done to account for the pop-in along the diagonal, especially evident in the interiors//0.8726645f ); // 50 degrees 1.745329f ); //100 degrees
+
+ ViewVector.Mult( mDrawDist / 2.0f );
+ ViewVector.Add( CamPosn );
+ ViewVolSP.SetTo( ViewVector, mDrawDist / 2.0f );
+
+
+ //comment this out
+ //if(spDefaultShader==NULL)
+ // spDefaultShader = new tShader("simple");
+ //P3DDrawSphere(ViewVolSP.mRadius,ViewVolSP.mCenter,*spDefaultShader,tColour(255,255,0));
+#endif
+// tPointCamera* pCam = (tPointCamera*)p3d::context->GetView()->GetCamera();
+// tPointCamera* pCam = (tPointCamera*)GetSuperCamManager()->GetSCC(0)->GetCamera();
+#if 0
+
+ rmt::Vector4 WorldPlane;
+// float WorldPlaneNorm;
+
+
+ //Do a hackey box approximation to the frustum,
+ //cuz tCamera doesn't properly provide a decent projection matrix
+
+
+ Bounds3f FrustumBBox;
+ Vector3f CamPosn, ViewVector, FarPlaneExtentVect;
+ pCam->GetPosition(&CamPosn);
+ pCam->GetTarget(&ViewVector); ViewVector.Sub( CamPosn ); ViewVector.Normalize(); //ViewVector.Mult( 200.0f );
+
+ FarPlaneExtentVect.CrossProduct(ViewVector,rmt::Vector(0.0f,1.0f,0.0f));
+ FarPlaneExtentVect.Mult( mDrawDist ); //200
+ //ViewVector.Mult(7.0f);
+ // ViewVector.Mult(16.0f); cut back for new art
+ //ViewVector.Mult(4.0f);
+ ViewVector.Mult(mDrawDist);
+
+ FrustumBBox.mMin = CamPosn; FrustumBBox.mMin.y -= 10.0f;
+ FrustumBBox.mMax = CamPosn;
+
+ FrustumBBox.Accumulate( CamPosn.x + ViewVector.x + FarPlaneExtentVect.x,
+ CamPosn.y + ViewVector.y + FarPlaneExtentVect.y,
+ CamPosn.z + ViewVector.z + FarPlaneExtentVect.z );
+
+ FrustumBBox.Accumulate( CamPosn.x + ViewVector.x - FarPlaneExtentVect.x,
+ CamPosn.y + ViewVector.y - FarPlaneExtentVect.y,
+ CamPosn.z + ViewVector.z - FarPlaneExtentVect.z );
+
+ ViewVolSP.SetPlane( HexahedronP::msBack, 0.0f, -1.0f, 0.0f, FrustumBBox.mMin.y);
+ ViewVolSP.SetPlane( HexahedronP::msFront, 0.0f, 1.0f, 0.0f, (-1.0f)*FrustumBBox.mMax.y);
+ ViewVolSP.SetPlane( HexahedronP::msLeft, -1.0f, 0.0f, 0.0f, FrustumBBox.mMin.x);
+ ViewVolSP.SetPlane( HexahedronP::msRight, 1.0f, 0.0f, 0.0f, (-1.0f)*FrustumBBox.mMax.x);
+ ViewVolSP.SetPlane( HexahedronP::msTop, 0.0f, 0.0f, -1.0f, FrustumBBox.mMin.z);
+ ViewVolSP.SetPlane( HexahedronP::msBottom, 0.0f, 0.0f, 1.0f, (-1.0f)*FrustumBBox.mMax.z);
+
+//#else
+ // ViewVector.Mult(16.0f); cut back for new art
+ float tmpFar = pCam->GetFarPlane();
+ pCam->SetFarPlane(mDrawDist);
+ //pCam->SetFarPlane(175.0f);
+ BuildFrustumPlanes( pCam, mCamPlanes );
+ pCam->SetFarPlane(tmpFar);
+
+ ViewVolSP.SetPlane( HexahedronP::msTop, mCamPlanes[0].x, mCamPlanes[0].y, mCamPlanes[0].z, mCamPlanes[0].w );
+ ViewVolSP.SetPlane( HexahedronP::msLeft, mCamPlanes[1].x, mCamPlanes[1].y, mCamPlanes[1].z, mCamPlanes[1].w );
+ ViewVolSP.SetPlane( HexahedronP::msBottom, mCamPlanes[2].x, mCamPlanes[2].y, mCamPlanes[2].z, mCamPlanes[2].w );
+ ViewVolSP.SetPlane( HexahedronP::msRight, mCamPlanes[3].x, mCamPlanes[3].y, mCamPlanes[3].z, mCamPlanes[3].w );
+ ViewVolSP.SetPlane( HexahedronP::msBack, mCamPlanes[4].x, mCamPlanes[4].y, mCamPlanes[4].z, mCamPlanes[4].w );
+ ViewVolSP.SetPlane( HexahedronP::msFront, mCamPlanes[5].x, mCamPlanes[5].y, mCamPlanes[5].z, mCamPlanes[5].w );
+#endif
+
+
+ //ViewVolSP.GeneratePoints();
+ //mStaticTreeWalker.MarkAll( ViewVolSP, iFilter );
+ mStaticTreeWalker.MarkAllSphere( ViewVolSP, iFilter );
+}
+/////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////
+void WorldScene::BuildFrustumPlanes( tPointCamera* pCam, FixedArray<rmt::Vector4>& orCamPlanes )
+{
+ rAssert( orCamPlanes.mSize == 6 );
+
+// tPointCamera* pCam = (tPointCamera*)p3d::context->GetView()->GetCamera();
+// tPointCamera* pCam = (tPointCamera*)GetSuperCamManager()->GetSCC(0)->GetCamera();
+ const rmt::Matrix* pCam2WorldMat = &pCam->GetCameraToWorldMatrix();
+
+ //
+ // Build the camera volume (written by Nigel Brooke)
+ //
+
+// Plane cameraPlanes[6];
+
+ // camera parameters
+ float fov, aspect; pCam->GetFOV( &fov, &aspect ); //fov = fov/1.5f;
+ //float aspect = 1.333f; // TODO : shouldn't be hardcoded
+
+ float nearPlane = pCam->GetNearPlane();
+ float farPlane = pCam->GetFarPlane();
+
+ // build some useful points
+ rmt::Vector eye = pCam2WorldMat->Row(3); // eye point
+ rmt::Vector look = pCam2WorldMat->Row(2); // look direciton, normal for far plane
+ rmt::Vector toFarPlane = look; toFarPlane.Scale(farPlane);
+ rmt::Vector onFarPlane = eye; onFarPlane.Add(toFarPlane); // a point on the far plane
+
+ rmt::Vector lookInv = pCam2WorldMat->Row(2); lookInv.Scale(-1);
+ // toNearPlane offset by a small value to avoid some artifacts
+ rmt::Vector toNearPlane = look; toNearPlane.Scale(nearPlane - 0.00001f);
+ rmt::Vector onNearPlane = eye; onNearPlane.Add(toNearPlane);
+
+ // find surface normals for left and right clipping pplanes
+ // first get camera space vectors
+ rmt::Vector tmpr(rmt::Cos(fov/2), 0, -rmt::Sin(fov/2)); // right vector is 'rotated' by fov/2
+ rmt::Vector tmpl = tmpr; tmpl.Scale(-1,1,1); // left vector is inverse of right
+
+ // then pass points through camera matrix to get world space vectors
+ rmt::Vector right, left;
+ pCam2WorldMat->RotateVector(tmpr, &right);
+ pCam2WorldMat->RotateVector(tmpl, &left);
+
+ // find surface normals for top and bottom clipping planes, just like left and right
+ // get camera space vectors
+ rmt::Vector tmpu(0, rmt::Cos(fov/2), -rmt::Sin(fov/2));
+ tmpu.Scale(1, aspect, 1);
+ tmpu.Normalize();
+ rmt::Vector tmpd = tmpu; tmpd.Scale(1,-1,1);
+
+ // tranform to worldspace
+ rmt::Vector up, down;
+ pCam2WorldMat->RotateVector(tmpu, &up);
+ pCam2WorldMat->RotateVector(tmpd, &down);
+
+ // set the planes using point in plane and normal format
+ // 'eye' is on all clipping planes except far and near
+ orCamPlanes[0].Set(up.x, up.y, up.z, -up.DotProduct(eye));
+ orCamPlanes[1].Set(left.x, left.y, left.z, -left.DotProduct(eye));
+ orCamPlanes[2].Set(down.x, down.y, down.z, -down.DotProduct(eye));
+ orCamPlanes[3].Set(right.x, right.y, right.z, -right.DotProduct(eye));
+ orCamPlanes[4].Set(look.x, look.y, look.z, -look.DotProduct(onFarPlane));
+ orCamPlanes[5].Set(lookInv.x, lookInv.y, lookInv.z, -lookInv.DotProduct(onNearPlane));
+}
diff --git a/game/code/render/Culling/WorldScene.h b/game/code/render/Culling/WorldScene.h
new file mode 100644
index 0000000..84fa023
--- /dev/null
+++ b/game/code/render/Culling/WorldScene.h
@@ -0,0 +1,187 @@
+#ifndef __WORLD_SCENE_H__
+#define __WORLD_SCENE_H__
+
+#include <render/Culling/UseArray.h>
+#include <render/Culling/ReserveArray.h>
+#include <render/Culling/SpatialTreeIter.h>
+#include <vector>
+#include <memory/stlallocators.h>
+#include <events/eventlistener.h>
+#include <events/eventmanager.h>
+#include <meta/eventlocator.h>
+
+//#include <render/DSG/IntersectDSG.h>
+//#include <render/DSG/StaticPhysDSG.h>
+//#include <render/DSG/StaticEntityDSG.h>
+
+#include <raddebugwatch.hpp>
+
+
+class IntersectDSG;
+class StaticEntityDSG;
+class StaticPhysDSG;
+class SpatialTree;
+class DynaPhysDSG;
+class IEntityDSG;
+class FenceEntityDSG;
+class DynaPhysDSG;
+class RoadSegmentData;
+class PathSegment;
+class NodeFLL;
+class AnimEntityDSG;
+class tGeometry;
+class tPointCamera;
+class tShader;
+//class SpatialTreeIter;
+
+/////////////////////////////////////////////////////////////////////////
+// This class couples renderables (currently tGeometries) and such to
+// the Spatial Graphs used to maintain them.
+//
+// Currently it handles:
+// -the collection of renderables (tGeometry)
+// -the generation of a Spatial Tree
+// -whose topology is influenced by the spatial distribution of
+// primgroups
+// -the rendering of all elements within the tree marked for render
+//
+/////////////////////////////////////////////////////////////////////////
+class WorldScene : EventListener
+{
+public:
+ WorldScene();
+ ~WorldScene();
+
+ ////////////////////////////////////////////////////////////
+ // Pure3D Data Reference Interface
+ ////////////////////////////////////////////////////////////
+ //const char* GetStaticInventorySection();
+
+ ////////////////////////////////////////////////////////////
+ // Manipulation Interface
+ ////////////////////////////////////////////////////////////
+ void SetTree( SpatialTree* ipSpatialTree );
+ void Init( int nRenderables );
+ void Add( tGeometry* pGeometry );
+ void Add( IntersectDSG* ipIntersectDSG );
+ void Add( StaticPhysDSG* ipStaticPhysDSG );
+ void Add( StaticEntityDSG* ipStaticEntityDSG );
+ void Add( FenceEntityDSG* ipFenceEntityDSG );
+ void Add( AnimCollisionEntityDSG* ipAnimCollDSG );
+ void Add( AnimEntityDSG* ipAnimDSG );
+ void Add( DynaPhysDSG* ipDynaPhysDSG );
+ void Add( TriggerVolume* ipTriggerVolume );
+ void Add( RoadSegment* ipRoadSegment );
+ void Add( PathSegment* ipPathSegment );
+
+ void GenerateSpatialReps();
+
+ void Move( rmt::Box3D& irOldBBox, IEntityDSG* ipEDSG );
+ void Remove( IEntityDSG* ipEDSG );
+ void RemoveQuietFail( IEntityDSG* ipEDSG );
+
+ void Render( unsigned int viewIndex );
+ void RenderOpaque( void );
+ void RenderTranslucent( void );
+ void RenderShadows();
+ void RenderSimpleShadows( void );
+ void RenderShadowCasters();
+
+ ////////////////////////////////////////////////////////////
+ // Public Tree Masks
+ ////////////////////////////////////////////////////////////
+ enum
+ {
+ msClear = ~SpatialTreeIter::msFilterAll,
+ msVisible0 = 0x01,
+ msVisible1 = 0x02,
+ msVisible2 = 0x04,
+ msVisible3 = 0x08,
+ msStaticPhys = 0x0f,
+ msDynaPhys = 0x10
+ };
+ ////////////////////////////////////////////////////////////
+ // Public Members
+ ////////////////////////////////////////////////////////////
+ SpatialTreeIter mStaticTreeWalker;
+ rmt::Vector mEpsilonOffset;
+
+ struct zSortBlah
+ {
+ IEntityDSG* entityPtr;
+ tUID shaderUID;
+ };
+
+ void SetRenderAll(bool iRenderAll){ mRenderAll = iRenderAll; }
+
+ void HandleEvent( EventEnum id, void* pEventData );
+
+protected:
+ float mDrawDist;
+ bool mRenderAll;
+ ////////////////////////////////////////////////////////////
+ // State Methods
+ ////////////////////////////////////////////////////////////
+ bool IsPreTreeGen();
+ bool IsPostTreeGen();
+
+ ////////////////////////////////////////////////////////////
+ // Helper Methods
+ ////////////////////////////////////////////////////////////
+ void GenerateStaticTree();
+ void PopulateStaticTree();
+
+ bool RemoveFromLeaf( IEntityDSG* ipEDSG );
+ bool RemovePlace(IEntityDSG* ipEDSG, SpatialNode& irNode);
+ //void PlaceStaticGeo( tGeometry* pGeometry );
+ void Place( IntersectDSG* ipIntersectDSG );
+ void Place( StaticEntityDSG* ipStaticEntity );
+ void Place( StaticPhysDSG* ipStaticPhys );
+ void Place( DynaPhysDSG* ipDynaPhys );
+ void Place( FenceEntityDSG* ipFence );
+ void Place( AnimCollisionEntityDSG* ipAnimColl );
+ void Place( AnimEntityDSG* ipAnim );
+ void Place( TriggerVolume* ipTriggerVolume );
+ void Place( RoadSegment* ipRoadSegment );
+ void Place( PathSegment* ipPathSegment );
+
+ void MarkCameraVisible( tPointCamera* pCam, unsigned int iFilter );
+ void BuildFrustumPlanes( tPointCamera* pCam, FixedArray<rmt::Vector4>& orCamPlanes );
+
+ void RenderScene( unsigned int iFilter, tPointCamera* ipCam );
+
+ ////////////////////////////////////////////////////////////
+ // Private Members
+ ////////////////////////////////////////////////////////////
+ SpatialTree* mpStaticTree;
+
+ tShader* mpTempShader;
+ std::vector< IEntityDSG*, s2alloc<IEntityDSG*> > mpZSortsPassShadowCasters;
+ std::vector< zSortBlah, s2alloc<zSortBlah> > mpZSorts;
+ std::vector< IEntityDSG*, s2alloc<IEntityDSG*> > mpZSortsPass2;
+ ReserveArray<IEntityDSG*> mShadowCastersPass1;
+ //ReserveArray<IEntityDSG*> mShadowCastersPass2;
+ FixedArray< rmt::Vector4 > mCamPlanes;
+
+#ifdef DEBUGWATCH
+ unsigned int mDebugMarkTiming, mDebugWalkTiming, mDebugRenderTiming,
+ mDebugZSWalkTiming,mDebugZSAddTiming,mDebugZSSortTiming;
+
+ bool mDebugSimCollisionVolumeDrawing;
+ bool mDebugFenceCollisionVolumeDrawing;
+ bool mDebugVehicleCollisionDrawing;
+ bool mDebugSimStatsDisplay;
+ bool mDebugShowTree;
+ int mDebugWatchNumCollisionPairs;
+#endif
+
+ void SetVisCone( rmt::Vector& irView, rmt::Vector& irPosn, float iAngleRadians );
+ bool IsSphereInCone( rmt::Vector& irCenter, float iRadius );
+ rmt::Vector mViewVector, mViewPosn;
+ float mViewSinInv, mViewSinSqr, mViewCosSqr;
+ ////////////////////////////////////////////////////////////
+ // Private Statics
+ ////////////////////////////////////////////////////////////
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Culling/allculling.cpp b/game/code/render/Culling/allculling.cpp
new file mode 100644
index 0000000..d79b421
--- /dev/null
+++ b/game/code/render/Culling/allculling.cpp
@@ -0,0 +1,14 @@
+#include <render/Culling/BoxPts.cpp>
+#include <render/Culling/Cell.cpp>
+#include <render/Culling/CellBlock.cpp>
+#include <render/Culling/CoordSubList.cpp>
+#include <render/Culling/CullData.cpp>
+#include <render/Culling/HexahedronP.cpp>
+#include <render/Culling/ISpatialProxy.cpp>
+#include <render/Culling/OctTreeData.cpp>
+#include <render/Culling/OctTreeNode.cpp>
+#include <render/Culling/SpatialTree.cpp>
+#include <render/Culling/SpatialTreeIter.cpp>
+#include <render/Culling/SphereSP.cpp>
+#include <render/Culling/VectorLib.cpp>
+#include <render/Culling/WorldScene.cpp>
diff --git a/game/code/render/Culling/srrRenderTypes.h b/game/code/render/Culling/srrRenderTypes.h
new file mode 100644
index 0000000..d52bde0
--- /dev/null
+++ b/game/code/render/Culling/srrRenderTypes.h
@@ -0,0 +1,24 @@
+#ifndef __SRR_RENDER_TYPES__
+#define __SRR_RENDER_TYPES__
+
+#include <render/culling/Vector3i.h>
+#include <render/culling/Vector3f.h>
+#include <render/culling/Bounds.h>
+#include <render/culling/FloatFuncs.h>
+//
+// Values for unset/unused data fields
+//
+// Asserts on these values can be used
+// to detect erroneous game(object) states
+//
+// Any object shoulde be able to one of these
+// values to initialize empty fields on finalizing
+// the creation process.
+//
+
+// Invalid Array Index
+#define SRR_ERR_INDEX -1
+// Invalid World Space Coordinate
+#define SRR_ERR_WS_COORD -1.0f
+
+#endif \ No newline at end of file
diff --git a/game/code/render/DSG/DSGFactory.cpp b/game/code/render/DSG/DSGFactory.cpp
new file mode 100644
index 0000000..b37a571
--- /dev/null
+++ b/game/code/render/DSG/DSGFactory.cpp
@@ -0,0 +1,194 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: DSGFactory.cpp
+//
+// Description: Implementation for DSGFactory class.
+//
+// History: Implemented --Devin [5/6/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/DSGFactory.h>
+#include <render/DSG/IntersectDSG.h>
+#include <memory/srrmemory.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+//
+// Static pointer to instance of this singleton.
+//
+DSGFactory* DSGFactory::mspInstance = NULL;
+
+//************************************************************************
+//
+// Public Member Functions : DSGFactory Interface
+//
+//************************************************************************
+//******************************************************************************
+// Public Member Functions : Instance Interface
+//******************************************************************************
+//==============================================================================
+// DSGFactory::CreateInstance
+//==============================================================================
+//
+// Description: Create the DSGFactory controller if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the created DSGFactory controller.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+DSGFactory* DSGFactory::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "DSGFactory" );
+
+ rAssert( mspInstance == NULL );
+ mspInstance = new(GMA_PERSISTENT) DSGFactory();
+MEMTRACK_POP_GROUP("DSGFactory");
+
+ return mspInstance;
+}
+
+//==============================================================================
+// DSGFactory::GetInstance
+//==============================================================================
+//
+// Description: Get the DSGFactory controller if exists.
+//
+// Parameters: None.
+//
+// Return: Pointer to the created DSGFactory controller.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+DSGFactory* DSGFactory::GetInstance()
+{
+ rAssert( mspInstance != NULL );
+
+ return mspInstance;
+}
+
+
+//==============================================================================
+// DSGFactory::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the DSGFactory controller.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void DSGFactory::DestroyInstance()
+{
+ //
+ // Make sure this doesn't get called twice.
+ //
+ rAssert( mspInstance != NULL );
+ delete mspInstance;
+ mspInstance = NULL;
+}
+///////////////////////////////////////////////////////////////////////
+//Meat Interface
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// DSGFactory::CreateEntityDSG
+//========================================================================
+//
+// Description:
+//
+// Parameters: tDrawable*
+//
+// Return: IEntityDSG*
+//
+// Constraints: None.
+//
+//========================================================================
+IEntityDSG* DSGFactory::CreateEntityDSG( tDrawable* ipDrawable )
+{
+ return (IEntityDSG*)(ipDrawable);
+}
+//========================================================================
+// DSGFactory::CreateIntersectDSG
+//========================================================================
+//
+// Description:
+//
+// Parameters: tGeometry*
+//
+// Return: IntersectDSG*
+//
+// Constraints: None.
+//
+//========================================================================
+IntersectDSG* DSGFactory::CreateIntersectDSG( tGeometry* ipGeometry )
+{
+MEMTRACK_PUSH_GROUP( "DSGFactory" );
+ IntersectDSG* pIDSG = new(GMA_LEVEL_ZONE) IntersectDSG( ipGeometry );
+MEMTRACK_POP_GROUP("DSGFactory");
+ return pIDSG;
+}
+
+//************************************************************************
+//
+// Protected Member Functions : DSGFactory
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : DSGFactory
+//
+//************************************************************************
+//========================================================================
+// DSGFactory::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+DSGFactory::DSGFactory()
+{
+}
+//========================================================================
+// DSGFactory::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+DSGFactory::~DSGFactory()
+{
+}
diff --git a/game/code/render/DSG/DSGFactory.h b/game/code/render/DSG/DSGFactory.h
new file mode 100644
index 0000000..a6987bb
--- /dev/null
+++ b/game/code/render/DSG/DSGFactory.h
@@ -0,0 +1,64 @@
+#ifndef __DSGFactory_H__
+#define __DSGFactory_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: DSGFactory
+//
+// Description: The DSGFactory does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/06]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+class IEntityDSG;
+class IntersectDSG;
+class tDrawable;
+class tGeometry;
+
+//========================================================================
+//
+// Synopsis: The DSGFactory; Synopsis by Inspection.
+//
+//========================================================================
+class DSGFactory
+{
+public:
+ // Static Methods (for creating, destroying and acquiring an instance
+ // of the RenderManager)
+ static DSGFactory* CreateInstance();
+ static DSGFactory* GetInstance();
+ static void DestroyInstance();
+
+ ///////////////////////////////////////////////////////////////////////
+ //Meat Interface
+ ///////////////////////////////////////////////////////////////////////
+ IEntityDSG* CreateEntityDSG( tDrawable* ipDrawable );
+ IntersectDSG* CreateIntersectDSG( tGeometry* ipGeometry );
+
+private:
+ DSGFactory();
+ ~DSGFactory();
+
+ static DSGFactory* mspInstance;
+
+ //MS7 Allocations can be pooled here
+};
+
+//
+// A little syntactic sugar for getting at this singleton.
+//
+inline DSGFactory* GetDSGFactory()
+{
+ return( DSGFactory::GetInstance() );
+}
+
+#endif
diff --git a/game/code/render/DSG/DynaLoadListDSG.h b/game/code/render/DSG/DynaLoadListDSG.h
new file mode 100644
index 0000000..2b53701
--- /dev/null
+++ b/game/code/render/DSG/DynaLoadListDSG.h
@@ -0,0 +1,102 @@
+#ifndef __DynaLoadListDSG_H__
+#define __DynaLoadListDSG_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: DynaLoadListDSG
+//
+// Description: The DynaLoadListDSG does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/06/26]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <memory/srrmemory.h>
+
+
+//=================================================
+// Project Includes
+//=================================================
+
+//========================================================================
+//
+// Synopsis: The DynaLoadListDSG; Synopsis by Inspection.
+//
+//========================================================================
+class DynaLoadListDSG
+{
+public:
+ DynaLoadListDSG(){}
+ ~DynaLoadListDSG(){}
+
+ void ClearAll()
+ {
+ mWorldSphereElems.Clear();
+ mSEntityElems.Clear();
+ mSPhysElems.Clear();
+ mIntersectElems.Clear();
+ mDPhysElems.Clear();
+ mFenceElems.Clear();
+ mAnimCollElems.Clear();
+ mAnimElems.Clear();
+ mTrigVolElems.Clear();
+ mRoadSegmentElems.Clear();
+ mPathSegmentElems.Clear();
+
+ }
+
+ void ClearAllUse()
+ {
+ mWorldSphereElems.ClearUse();
+ mSEntityElems.ClearUse();
+ mSPhysElems.ClearUse();
+ mIntersectElems.ClearUse();
+ mDPhysElems.ClearUse();
+ mFenceElems.ClearUse();
+ mAnimCollElems.ClearUse();
+ mAnimElems.ClearUse();
+ mTrigVolElems.ClearUse();
+ mRoadSegmentElems.ClearUse();
+ mPathSegmentElems.ClearUse();
+
+ }
+
+ void AllocateAll(int inSize)
+ {
+ MEMTRACK_PUSH_GROUP( "Devin's world" );
+ mWorldSphereElems.Allocate(2);
+ mSEntityElems.Allocate(inSize);
+ mSPhysElems.Allocate(inSize/2);
+ mIntersectElems.Allocate(inSize/4);
+ mDPhysElems.Allocate(inSize);
+ mFenceElems.Allocate(inSize);
+ mAnimCollElems.Allocate(inSize/4);
+ mAnimElems.Allocate(inSize/4);
+ mTrigVolElems.Allocate(inSize/4);
+ mRoadSegmentElems.Allocate(1250);
+ mPathSegmentElems.Allocate(inSize);
+ MEMTRACK_POP_GROUP( "Devin's world" );
+ }
+
+ tName mGiveItAFuckinName;
+
+ SwapArray<WorldSphereDSG*> mWorldSphereElems;
+ SwapArray<StaticEntityDSG*> mSEntityElems;
+ SwapArray<StaticPhysDSG*> mSPhysElems;
+ SwapArray<IntersectDSG*> mIntersectElems;
+ SwapArray<DynaPhysDSG*> mDPhysElems;
+ SwapArray<FenceEntityDSG*> mFenceElems;
+ SwapArray<AnimCollisionEntityDSG*> mAnimCollElems;
+ SwapArray<AnimEntityDSG*> mAnimElems;
+ SwapArray<TriggerVolume*> mTrigVolElems;
+ SwapArray<RoadSegment*> mRoadSegmentElems;
+ SwapArray<PathSegment*> mPathSegmentElems;
+
+private:
+};
+
+#endif
diff --git a/game/code/render/DSG/DynaPhysDSG.cpp b/game/code/render/DSG/DynaPhysDSG.cpp
new file mode 100644
index 0000000..e829941
--- /dev/null
+++ b/game/code/render/DSG/DynaPhysDSG.cpp
@@ -0,0 +1,424 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: DynaPhysDSG.cpp
+//
+// Description: Implementation for DynaPhysDSG class.
+//
+// History: Implemented --Devin [6/17/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/DynaPhysDSG.h>
+
+#include <worldsim/character/character.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <mission/gameplaymanager.h>
+
+#include <simcommon/simulatedobject.hpp>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+// tuning values for rest testing
+static const float REST_LINEAR_TOL = 0.03f; // linear velocity tolerance
+static const float REST_ANGULAR_TOL = 0.2f; // angular velocity tolerance
+static const int REST_HISTORY = 15; // number of sim frames to average velocities
+
+//************************************************************************
+//
+// Public Member Functions : DynaPhysDSG Interface
+//
+//************************************************************************
+//========================================================================
+// DynaPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+DynaPhysDSG::DynaPhysDSG() :
+ mPastLinear(REST_LINEAR_TOL * 2.0f, 15),
+ mPastAngular(REST_ANGULAR_TOL * 2.0f, 15)
+{
+ mGroundPlaneIndex = -1;
+ mGroundPlaneRefs = 0;
+
+ mIsHit = false;
+}
+//========================================================================
+// DynaPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+DynaPhysDSG::~DynaPhysDSG()
+{
+}
+
+
+
+//=============================================================================
+// DynaPhysDSG::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void DynaPhysDSG::Update(float dt)
+{
+
+}
+
+
+//=============================================================================
+// DynaPhysDSG::FetchGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+int DynaPhysDSG::FetchGroundPlane()
+{
+ //mGroundPlaneRefs++; moved down to a point where we know we got one
+
+ if(mGroundPlaneIndex != -1)
+ {
+ // we already got one
+ mGroundPlaneRefs++;
+ return mGroundPlaneIndex;
+ }
+
+ rAssert(this->GetSimState());
+ mGroundPlaneIndex = GetWorldPhysicsManager()->GetNewGroundPlane(this->GetSimState());
+ rAssert(mGroundPlaneIndex > -1);
+ if(mGroundPlaneIndex != -1)
+ {
+ mGroundPlaneRefs++;
+ }
+
+ return mGroundPlaneIndex;
+
+}
+
+//=============================================================================
+// DynaPhysDSG::FreeGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DynaPhysDSG::FreeGroundPlane()
+{
+ mGroundPlaneRefs--;
+ if(mGroundPlaneRefs == 0)
+ {
+ GetWorldPhysicsManager()->DisableGroundPlaneCollision(mGroundPlaneIndex); // also disable when thing is at rest?
+ GetWorldPhysicsManager()->FreeGroundPlane(mGroundPlaneIndex);
+ mGroundPlaneIndex = -1;
+ }
+
+
+ // this shoudln't matter, because the ground plane shouldn't be
+ // removed unless it is already under AI control, but better safe than sorry
+ mpSimStateObj->SetControl(sim::simAICtrl);
+ mpSimStateObj->ResetVelocities();
+
+ // remove from collision
+}
+
+//=============================================================================
+// DynaPhysDSG::IsAtRest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool DynaPhysDSG::IsAtRest()
+{
+ // object under AI control are always "at rest"
+ if(!GetSimState() || GetSimState()->GetControl() == sim::simAICtrl)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+void DynaPhysDSG::RestTest()
+{
+ // already at rest
+ sim::SimState* simState = GetSimState();
+
+ if(!simState || simState->GetControl() == sim::simAICtrl)
+ {
+ return;
+ }
+
+ // don't update characters (which should always be in sim), vehicles (they have their own rest test)
+ // or breakables (they are always at rest, but removing them from physics screws them up)
+ if((simState->mAIRefIndex != PhysicsAIRef::redBrickVehicle) &&
+ (simState->mAIRefIndex != PhysicsAIRef::PlayerCharacter ) &&
+ (simState->mAIRefIndex != PhysicsAIRef::NPCharacter ) &&
+ (!mpCollisionAttributes || (mpCollisionAttributes->GetClasstypeid() != PROP_BREAKABLE)))
+ {
+ // check if smoothed velocities are below tolerance
+ if((mPastLinear.Smooth(simState->GetLinearVelocity().Magnitude()) < REST_LINEAR_TOL) &&
+ (mPastAngular.Smooth(simState->GetAngularVelocity().Magnitude()) < REST_ANGULAR_TOL))
+ {
+ // put it at rest
+ simState->SetControl(sim::simAICtrl);
+ simState->ResetVelocities();
+ OnTransitToAICtrl();
+ }
+ }
+ else if( simState->mAIRefIndex == PhysicsAIRef::NPCharacter ||
+ simState->mAIRefIndex == PhysicsAIRef::PlayerCharacter )
+ {
+ static const float PC_REST_LINEAR_TOL = 1.0f;
+ static const float PC_REST_ANGULAR_TOL = 1.5f;
+ static const float NPC_REST_LINEAR_TOL = 0.5f;
+ static const float NPC_REST_ANGULAR_TOL = 0.7f;
+
+ float linearTol = 0.0f;
+ float angularTol = 0.0f;
+
+ if( simState->mAIRefIndex == PhysicsAIRef::PlayerCharacter )
+ {
+ linearTol = PC_REST_LINEAR_TOL;
+ angularTol = PC_REST_ANGULAR_TOL;
+ }
+ else
+ {
+ linearTol = NPC_REST_LINEAR_TOL;
+ angularTol = NPC_REST_ANGULAR_TOL;
+ }
+
+ // check if smoothed velocities are below tolerance
+ float linVel = simState->GetLinearVelocity().Magnitude();
+ float angVel = simState->GetAngularVelocity().Magnitude();
+ float testLin = mPastLinear.Smooth( linVel );
+ float testAng = mPastAngular.Smooth( angVel );
+
+ if( testLin < linearTol && testAng < angularTol )
+ {
+ bool okToRest = true;
+
+ Character* character = (Character*) simState->mAIRefPointer;
+
+ //
+ // Only test for aborting rest test for NPCs... We want to
+ // restore player control over his character ASAP, so no delays there.
+ //
+ if( character->IsNPC() )
+ {
+ rmt::Vector myPos;
+ character->GetPosition( &myPos );
+
+
+ ////////////////////////////////////////////////////////////////
+ // NOTE: Do this if we encounter probs with characters coming to rest
+ // while in the air. It can potentially cause problems if you got
+ // teleported and your ground pos is out of synch with where you've
+ // been teleported to.
+ //
+ rmt::Vector myGroundPos, myGroundNormal;
+ character->GetTerrainIntersect( myGroundPos, myGroundNormal );
+
+ const float DELTA_ABOVE_GROUND_OR_STATIC_KEEP_IN_SIM = 1.0f;
+ if( myPos.y > (myGroundPos.y + DELTA_ABOVE_GROUND_OR_STATIC_KEEP_IN_SIM) )
+ {
+ okToRest = false;
+ }
+ ////////////////////////////////////////////////////////////////
+
+
+ if( okToRest )
+ {
+ // don't transit back out of sim if still colliding with a vehicle
+ if( character->mbCollidedWithVehicle )
+ {
+ okToRest = false;
+ }
+ else
+ {
+ Vehicle* playerVehicle = GetGameplayManager()->GetCurrentVehicle();
+ if( playerVehicle )
+ {
+ // do a quick test if we're inside the vehicle's volume
+ // if so, don't put at rest.
+ rmt::Vector box = playerVehicle->GetExtents();
+ rmt::Matrix worldToCar = playerVehicle->GetTransform();
+ worldToCar.Invert();
+
+ // put the character position in car space
+ // so now the box is "axis"-aligned and we
+ // can do simple containment test
+ myPos.Transform( worldToCar );
+
+ // ignore y... if we're anywhere in car's x or z space
+ // then we don't allow putting at rest.
+ const float FUDGE_EXTENT = 0.2f;
+ float xExtent = box.x + FUDGE_EXTENT;
+ float zExtent = box.z + FUDGE_EXTENT;
+
+ if( -xExtent < myPos.x && myPos.x < xExtent &&
+ -zExtent < myPos.z && myPos.z < zExtent )
+ {
+ okToRest = false;
+ }
+ }
+ }
+ }
+ }
+
+ if( okToRest )
+ {
+ // put it at rest
+ simState->SetControl(sim::simAICtrl);
+ simState->ResetVelocities();
+ OnTransitToAICtrl();
+ }
+ else
+ {
+ // Keep turning
+ rmt::Vector linearAdd( linearTol, 0.0f, linearTol );
+ rmt::Vector angularAdd( angularTol, angularTol, angularTol );
+
+ rmt::Vector& linearVel = simState->GetLinearVelocity();
+ linearVel.Add( linearAdd );
+
+ rmt::Vector& angularVel = simState->GetAngularVelocity();
+ angularVel.Add( angularAdd );
+
+ }
+ }
+ }
+}
+
+void
+DynaPhysDSG::AddToSimulation()
+{
+ sim::SimState* pSimState = GetSimState();
+ if ( pSimState )
+ {
+ if ( pSimState->GetSimulatedObject() != NULL )
+ {
+ pSimState->SetControl( sim::simSimulationCtrl );
+ //int groundPlaneIndex = FetchGroundPlane();
+ if ( this->mGroundPlaneIndex >= 0 )
+ {
+ GetWorldPhysicsManager()->EnableGroundPlaneCollision( mGroundPlaneIndex );
+ }
+ }
+ }
+}
+
+
+void
+DynaPhysDSG::ApplyForce( const rmt::Vector& direction, float force )
+{
+ if ( mpSimStateObj == NULL )
+ {
+ return;
+ }
+
+ const float DEFAULT_MASS = 100.0f;
+
+ float mass;
+ if ( mpCollisionAttributes != NULL )
+ {
+ mass = mpCollisionAttributes->GetMass();
+ }
+ else
+ {
+ mass = DEFAULT_MASS;
+ }
+
+ if ( rmt::Epsilon( mass, 0.0f ) )
+ {
+#ifdef RAD_DEBUG
+ char error[128];
+ sprintf( error, "DynaPhysDsg object: %s has 0 mass\n", GetName() );
+ rAssertMsg( false, error );
+#endif
+ mass = DEFAULT_MASS;
+ }
+
+ if(mpSimStateObj->GetControl() == sim::simAICtrl)
+ {
+ mpSimStateObj->ResetVelocities();
+ }
+
+ if(mpSimStateObj->GetSimulatedObject())
+ {
+ mpSimStateObj->GetSimulatedObject()->ResetRestingDetector();
+
+ rmt::Vector& rVelocity = mpSimStateObj->GetLinearVelocity();
+ // Apply delta velocity
+ float deltaV = force / mass;
+ rVelocity += (direction * deltaV);
+ // Make it interact with the world
+ AddToSimulation();
+ }
+}
+
+bool
+DynaPhysDSG::IsCollisionEnabled()const
+{
+ sim::SimState* pSimState = GetSimState();
+ if ( pSimState == NULL )
+ return false; // no simstate, no collision possible
+
+ sim::CollisionObject* pCollisionObject = pSimState->GetCollisionObject();
+ if ( pCollisionObject == NULL )
+ return false; // ditto for the held collision object
+
+ return pCollisionObject->GetCollisionEnabled();
+}
+
+//************************************************************************
+//
+// Protected Member Functions : DynaPhysDSG
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : DynaPhysDSG
+//
+//************************************************************************
diff --git a/game/code/render/DSG/DynaPhysDSG.h b/game/code/render/DSG/DynaPhysDSG.h
new file mode 100644
index 0000000..44cf451
--- /dev/null
+++ b/game/code/render/DSG/DynaPhysDSG.h
@@ -0,0 +1,105 @@
+#ifndef __DynaPhysDSG_H__
+#define __DynaPhysDSG_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: DynaPhysDSG
+//
+// Description: The DynaPhysDSG does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/06/17]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/DSG/StaticPhysDSG.h>
+
+// a little class to perform smoothing on the velocity values
+class Smoother
+{
+public:
+ Smoother(float initial, unsigned c) :
+ rollingAverage(initial),
+ factor(1.0f / float(c))
+ {
+ }
+
+ ~Smoother()
+ {
+ }
+
+ float Smooth(float sample)
+ {
+ rollingAverage = (rollingAverage * (1.0f - factor)) + (sample * factor);
+ return rollingAverage ;
+ }
+
+ void SetAverage( float sample )
+ {
+ rollingAverage = sample;
+ }
+
+protected:
+ float rollingAverage;
+ float factor;
+};
+
+//========================================================================
+//
+// Synopsis: The DynaPhysDSG; Synopsis by Inspection.
+//
+//========================================================================
+class DynaPhysDSG
+: public StaticPhysDSG
+{
+public:
+ DynaPhysDSG();
+ ~DynaPhysDSG();
+
+ virtual void Update(float dt);
+
+
+ // vehicle's need these
+ virtual int FetchGroundPlane(); // tell object to get itself a ground plane, through worldphysicsmanager, through groundplanepool
+ // update the refs
+
+ virtual void FreeGroundPlane(); // make this safe to call even when we don't have one?
+
+ virtual bool IsAtRest(); // Is this obejct currently at rest
+ virtual void RestTest(); // Perform the object rest test
+ virtual void OnTransitToAICtrl() {}
+ // need these?
+ virtual int GetGroundPlaneIndex() {return mGroundPlaneIndex;}
+ //virtual void SetGroundPlaneIndex(int index) {mGroundPlaneIndex = index;}
+ virtual void AddToSimulation();
+ virtual void ApplyForce( const rmt::Vector& direction, float force );
+
+ bool IsCollisionEnabled()const;
+
+ bool mIsHit; // had to move this up from InstDynaPhys
+
+protected:
+
+ Smoother mPastLinear;
+ Smoother mPastAngular;
+
+ //sim::PhysicsObject* mpPhysObj;
+
+ //rmt::Matrix* mpMatrix;
+ //tGeometry* mpGeo;
+
+ int mGroundPlaneIndex;
+ int mGroundPlaneRefs;
+private:
+
+
+};
+
+#endif
diff --git a/game/code/render/DSG/FenceEntityDSG.cpp b/game/code/render/DSG/FenceEntityDSG.cpp
new file mode 100644
index 0000000..2520473
--- /dev/null
+++ b/game/code/render/DSG/FenceEntityDSG.cpp
@@ -0,0 +1,236 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: FenceEntityDSG.cpp
+//
+// Description: Implementation for FenceEntityDSG class.
+//
+// History: Implemented --Devin [7/6/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/FenceEntityDSG.h>
+#include <render/Culling/Bounds.h>
+
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : FenceEntityDSG Interface
+//
+//************************************************************************
+FenceEntityDSG::FenceEntityDSG( void ){}
+FenceEntityDSG::~FenceEntityDSG( void ){}
+
+///////////////////////////////////////////////////////////////////////
+// Drawable
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// FenceEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FenceEntityDSG::Display()
+{
+#ifndef RAD_RELEASE
+ if(IS_DRAW_LONG) return;
+ //rAssert(false);
+ pddiPrimStream* stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 2);
+
+ tColour colour(0, 0, 255);
+
+ stream->Colour(colour);
+ stream->Coord(mStartPoint.x, mStartPoint.y, mStartPoint.z);
+ stream->Colour(colour);
+ stream->Coord(mEndPoint.x, mEndPoint.y, mEndPoint.z);
+ p3d::pddi->EndPrims(stream);
+#endif
+}
+//========================================================================
+// FenceEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FenceEntityDSG::DisplayBoundingBox(tColour colour)
+{
+ rAssert(false);
+}
+//========================================================================
+// FenceEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FenceEntityDSG::DisplayBoundingSphere(tColour colour)
+{
+ rAssert(false);
+}
+
+//========================================================================
+// FenceEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FenceEntityDSG::GetBoundingBox(rmt::Box3D* box)
+{
+ Bounds3f bounds;
+
+ bounds.mMin.SetTo(mStartPoint);
+ bounds.mMax.SetTo(mStartPoint);
+ bounds.Accumulate(mEndPoint);
+
+ box->low = (bounds.mMin);
+ box->high = (bounds.mMax);
+}
+//========================================================================
+// FenceEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FenceEntityDSG::GetBoundingSphere(rmt::Sphere* sphere)
+{
+ rmt::Vector tmp(mStartPoint);
+ tmp += mEndPoint;
+ tmp /= 2.0f;
+
+ sphere->centre = tmp;
+
+ tmp.Sub(mStartPoint,tmp);
+ sphere->radius = tmp.Magnitude();
+}
+
+///////////////////////////////////////////////////////////////////////
+// IEntityDSG
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// FenceEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+rmt::Vector* FenceEntityDSG::pPosition()
+{
+ return &mStartPoint;
+}
+//========================================================================
+// FenceEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+const rmt::Vector& FenceEntityDSG::rPosition()
+{
+ return mStartPoint;
+}
+//========================================================================
+// FenceEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FenceEntityDSG::GetPosition( rmt::Vector* ipPosn )
+{
+ *ipPosn = mStartPoint;
+}
+
+//////////////////////////////////////////////////////////////////////////
+// override these methods so we can stub them out
+sim::Solving_Answer FenceEntityDSG::PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+{
+ //Greg, do the stuff you need here
+ return sim::Solving_Continue;
+}
+
+
+sim::Solving_Answer FenceEntityDSG::PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision)
+{
+
+ // subclass-specific shit here
+
+ return CollisionEntityDSG::PostReactToCollision(impulse, inCollision);
+}
+
+
+//************************************************************************
+//
+// Protected Member Functions : FenceEntityDSG
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : FenceEntityDSG
+//
+//************************************************************************
diff --git a/game/code/render/DSG/FenceEntityDSG.h b/game/code/render/DSG/FenceEntityDSG.h
new file mode 100644
index 0000000..b157a00
--- /dev/null
+++ b/game/code/render/DSG/FenceEntityDSG.h
@@ -0,0 +1,94 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: fencedsg.h
+//
+// Description: Blahblahblah
+//
+// History: created june 27th, 2002 - gmayer
+//
+//=============================================================================
+
+#ifndef FENCEENTITYDSG_H
+#define FENCEENTITYDSG_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//=================================================
+// System Includes
+//=================================================
+
+
+#include <p3d/refcounted.hpp>
+#include <radmath/radmath.hpp>
+
+//=================================================
+// Project Includes
+//=================================================
+//#include <render/DSG/IEntityDSG.h>
+#include <render/DSG/collisionentitydsg.h>
+#include <worldsim/physicsairef.h>
+
+
+
+class FenceEntityDSG
+:
+public CollisionEntityDSG
+{
+public:
+ FenceEntityDSG( void );
+ virtual ~FenceEntityDSG( void );
+
+ ///////////////////////////////////////////////////////////////////////
+ // Drawable
+ ///////////////////////////////////////////////////////////////////////
+ void Display();
+
+ void DisplayBoundingBox(tColour colour = tColour(0,255,0));
+ void DisplayBoundingSphere(tColour colour = tColour(0,255,0));
+
+ void GetBoundingBox(rmt::Box3D* box);
+ void GetBoundingSphere(rmt::Sphere* sphere);
+
+ ///////////////////////////////////////////////////////////////////////
+ // IEntityDSG
+ ///////////////////////////////////////////////////////////////////////
+ rmt::Vector* pPosition();
+ const rmt::Vector& rPosition();
+ void GetPosition( rmt::Vector* ipPosn );
+
+//////////////////////////////////////////////////////////////////////////
+ // override these methods so we can stub them out
+ virtual sim::Solving_Answer PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision );
+ virtual sim::Solving_Answer PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision);
+
+ // the only reason to inherit from CollisionEntityDSG is so desingers can
+ // tweak and tune the friction values when you hit a wall.
+ CollisionAttributes* GetCollisionAttributes( void ) const;
+ void SetCollisionAttributes( CollisionAttributes* pCollisionAttributes );
+
+ //
+ // Implement pure virtual function from CollisionEntityDSG
+ //
+ int GetAIRef() { return( PhysicsAIRef::redBrickPhizFence ); }
+
+
+
+ //-----------------------
+ // the guts of this class
+ //-----------------------
+ rmt::Vector mStartPoint;
+ rmt::Vector mEndPoint;
+ rmt::Vector mNormal;
+
+
+protected:
+
+private:
+ // Contained by CollisionEntityDSG
+ //CollisionAttributes* mpCollisionAttributes;
+};
+
+#endif //FENCEENTITYDSG_H \ No newline at end of file
diff --git a/game/code/render/DSG/IEntityDSG.cpp b/game/code/render/DSG/IEntityDSG.cpp
new file mode 100644
index 0000000..0361728
--- /dev/null
+++ b/game/code/render/DSG/IEntityDSG.cpp
@@ -0,0 +1,226 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: EntityDSG.cpp
+//
+// Description: Implementation for IEntityDSG class.
+//
+// History: Implemented --Devin [5/3/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#ifdef WORLD_BUILDER
+#include "IEntityDSG.h"
+#else
+#include <memory/classsizetracker.h>
+#include <meta/triggervolumetracker.h>
+#include <render/DSG/IEntityDSG.h>
+#include <render/Culling/SwapArray.h>
+#include <render/RenderManager/RenderManager.h>
+#include <p3d/shader.hpp>
+#endif //WORLD_BUILDER
+
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+bool IEntityDSG::msDeletionsSafe = false;
+#ifdef BREAK_DOWN_PROFILE
+ char IEntityDSG::msMarker = '_';
+#endif
+
+//************************************************************************
+//
+// Public Member Functions : IEntityDSG Interface
+//
+//************************************************************************
+//========================================================================
+// IEntityDSG::IEntityDSG()
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+IEntityDSG::IEntityDSG() :
+ mTranslucent(0),
+ mpSpatialNode(NULL)
+{
+ CLASSTRACKER_CREATE( IEntityDSG );
+#ifndef WORLD_BUILDER
+ //if(msDeletionsSafe)
+ //{
+ // GetRenderManager()->MunchDelList(5);
+ //}
+ mShaderName = "__none__";
+ /*
+ SwapArray<IEntityDSG*>& rEDL = GetRenderManager()->mEntityDeletionList;
+ int i=3;
+ while(rEDL.mUseSize && i)
+ {
+ rEDL[0]->Release();
+ rEDL.Remove(0);
+ }
+ */
+#endif
+#ifdef BREAK_DOWN_PROFILE
+ mMicros = 0;
+#endif
+}
+//========================================================================
+// IEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tShader* IEntityDSG::Process(tShader* pShader)
+{
+#ifndef WORLD_BUILDER
+ if( mShaderName != "__none__" &&
+ mShaderName != pShader->GetNameObject() )
+ {
+ //more than one shader, can't sort by shader
+ // rDebugPrintf("More than one Shader in IEntityDSG %s\n", this->GetName() );
+ mShaderName = "__none__";
+ }
+ else
+ {
+ mShaderName = pShader->GetName();
+ }
+#endif
+ return pShader;
+}
+//========================================================================
+// EntityDSG::~IEntityDSG()
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+IEntityDSG::~IEntityDSG()
+{
+ CLASSTRACKER_DESTROY( IEntityDSG );
+}
+//========================================================================
+// IEntityDSG::RenderUpdate()
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IEntityDSG::RenderUpdate()
+{
+ //Do Nothing for the base case
+ //Animated objects/textures will update here in subclasses
+}
+//========================================================================
+// IEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+const tUID BAD_LIGHT = tEntity::MakeUID("l7_searchlights");
+void IEntityDSG::SetRank(rmt::Vector& irRefPosn, rmt::Vector& irRefVect)
+{
+// rmt::Vector posn;
+// GetPosition(&posn);
+ rmt::Vector temp;
+ rmt::Sphere sphere;
+ GetBoundingSphere(&sphere);
+
+ //sphere.centre.Sub(sphere.radius);
+ sphere.centre.Sub(irRefPosn);
+ temp = irRefVect;
+ temp.Scale(sphere.radius);
+ sphere.centre.Sub(temp);
+
+ if(sphere.centre.Dot(irRefVect)<0.0f)
+ mRank = -sphere.centre.MagnitudeSqr();//-(1.75f*sphere.radius*sphere.radius);//posn.MagnitudeSqr();
+ else
+ mRank = sphere.centre.MagnitudeSqr();//-(1.75f*sphere.radius*sphere.radius);//posn.MagnitudeSqr();
+
+ // Evil hack for L7 military spotlights
+ if(GetUID() == BAD_LIGHT)
+ {
+ mRank = FLT_MAX;
+ }
+ //posn.Sub(irRefPosn);
+}
+
+float IEntityDSG::Rank()
+{
+ return mRank;
+}
+
+#ifdef DONT_DRAW_EXPENSIVE
+bool IEntityDSG::IsDrawLong()
+{
+ if(mMicros>90)
+ {
+ /*rmt::Matrix Trans;
+ rmt::Vector posn;
+ GetPosition(&posn);
+ Trans.Identity();
+ Trans.FillTranslate(posn);
+ p3d::pddi->PushMatrix(PDDI_MATRIX_MODELVIEW);
+ p3d::pddi->MultMatrix(PDDI_MATRIX_MODELVIEW,&Trans);
+ GetTriggerVolumeTracker()->mpTriggerSphere->Display();
+ p3d::pddi->PopMatrix(PDDI_MATRIX_MODELVIEW);*/
+ rReleasePrintf("%s is taking %u us to draw.\n", GetName(), mMicros );
+ return false;
+ }
+ return false;
+}
+#endif
+
+//************************************************************************
+//
+// Protected Member Functions : IEntityDSG
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : IEntityDSG
+//
+//************************************************************************
+
diff --git a/game/code/render/DSG/IEntityDSG.h b/game/code/render/DSG/IEntityDSG.h
new file mode 100644
index 0000000..bdf7daa
--- /dev/null
+++ b/game/code/render/DSG/IEntityDSG.h
@@ -0,0 +1,139 @@
+#ifndef __EntityDSG_H__
+#define __EntityDSG_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: IEntityDSG
+//
+// Description: The IEntityDSG does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/03]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <p3d/drawable.hpp>
+
+#ifndef WORLD_BUILDER
+#include <render/Culling/NodeFLL.h>
+#else
+#include "../Culling/NodeFLL.h"
+#endif
+
+#include <radtime.hpp>
+#include <debug/profiler.h>
+
+class tShadowSkin;
+class tShadowMesh;
+class SpatialNode;
+//=================================================
+// Project Includes
+//=================================================
+
+//========================================================================
+//
+// Synopsis: The IEntityDSG; Synopsis by Inspection.
+//
+//========================================================================
+//#define DONT_DRAW_EXPENSIVE
+//#define BREAK_DOWN_PROFILE
+
+//////////////////////////////////////////////////////////////////////////
+//
+//////////////////////////////////////////////////////////////////////////
+class IEntityDSG :
+ public tDrawable,
+ public tDrawable::ShaderCallback
+ //,public ISortPriority
+{
+public:
+ IEntityDSG();
+
+ virtual rmt::Vector* pPosition() = 0;
+ virtual const rmt::Vector& rPosition() = 0;
+
+ virtual void SetShadow( tShadowSkin* pShadowSkin ){ rAssert ("SetShadow not implemented"); }
+ virtual void SetShadow( tShadowMesh* pShadowMesh ){ rAssert ("SetShadow not implemented"); }
+ virtual int CastsShadow(){return 0;}
+ virtual void DisplayShadow() { rAssert( "DisplayShadow not implemented" ); }
+ virtual void DisplaySimpleShadow( void ) { rAssert( false && "DisplaySimpleShadow not implemented" ); }
+
+ virtual void RenderUpdate();
+
+ virtual void SetShader(tShader* pShader, int i){}
+ const tName& GetShaderName(){ return mShaderName; }
+ const tUID GetShaderUID(){ return mShaderName.GetUID(); }
+
+ tShader* Process(tShader* pShaders);
+
+ virtual void GetPosition( rmt::Vector* ipPosn ){}
+ virtual void SetRank(rmt::Vector& irRefPosn, rmt::Vector& mViewVector);
+ float Rank();
+ float mRank;
+
+
+ bool mTranslucent;
+ tName mShaderName;
+ static bool msDeletionsSafe;
+
+ SpatialNode* mpSpatialNode;
+
+#ifdef BREAK_DOWN_PROFILE
+ static char msMarker;
+ unsigned int mMicros;
+
+ void BeginProfile(char* iString)
+ {
+ iString[0] = IEntityDSG::msMarker;
+ BEGIN_PROFILE(iString)
+ mMicros = radTimeGetMicroseconds();
+ }
+
+ void EndProfile(char* iString)
+ {
+ mMicros = radTimeGetMicroseconds()-mMicros;
+ iString[0] = IEntityDSG::msMarker;
+ END_PROFILE(iString)
+ }
+#endif
+
+#ifdef DONT_DRAW_EXPENSIVE
+ bool IsDrawLong();
+#endif
+
+protected:
+ virtual ~IEntityDSG();
+};
+
+#ifdef DONT_DRAW_EXPENSIVE
+ #define IS_DRAW_LONG this->IsDrawLong()
+#else
+ #define IS_DRAW_LONG false
+#endif
+
+#ifdef BREAK_DOWN_PROFILE
+ #define DSG_BEGIN_PROFILE(string) {this->BeginProfile(string);}
+ #define DSG_END_PROFILE(string) {this->EndProfile(string);}
+ #define DSG_SET_PROFILE(marker) {IEntityDSG::msMarker=marker;}
+#else
+ #define DSG_BEGIN_PROFILE(string)
+ #define DSG_END_PROFILE(string)
+ #define DSG_SET_PROFILE(marker)
+#endif
+/*
+ bool operator<(IEntityDSG* x, IEntityDSG* y)
+ {
+ if(x->mRank==0.0f)
+ {
+ return x->mShaderUID-y->mShaderUID;
+ }
+ else
+ {
+ return 0.1f*(x->mRank-y->mRank);
+ }
+ }
+*/
+#endif
diff --git a/game/code/render/DSG/InstAnimDynaPhysDSG.cpp b/game/code/render/DSG/InstAnimDynaPhysDSG.cpp
new file mode 100644
index 0000000..96dc6f6
--- /dev/null
+++ b/game/code/render/DSG/InstAnimDynaPhysDSG.cpp
@@ -0,0 +1,557 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: InstAnimDynaPhysDSG
+//
+// Description: An InstDynaPhysDSG object that has a simple cyclic animation
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+#include <memory/classsizetracker.h>
+#include <render/DSG/InstAnimDynaPhysDSG.h>
+#include <p3d/anim/compositedrawable.hpp>
+#include <render/AnimEntityDSGManager/AnimEntityDSGManager.h>
+#include <p3d/anim/pose.hpp>
+#include <p3d/anim/poseanimation.hpp>
+#include <p3d/anim/animate.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <poser/poseengine.hpp>
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/Culling/WorldScene.h>
+#include <ai/actionbuttonhandler.h>
+#include <stateprop/statepropdata.hpp>
+#include <p3d/billboardobject.hpp>
+
+//#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/character/character.h>
+
+#include <render/particles/particlemanager.h>
+#include <render/breakables/breakablesmanager.h>
+
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// InstAnimDynaPhysDSG::InstAnimDynaPhysDSG
+//===========================================================================
+// Description:
+// InstAnimDynaPhysDSG ctor
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+InstAnimDynaPhysDSG::InstAnimDynaPhysDSG()
+: mpCompDraw( NULL ),
+mpMultiController( NULL ),
+mpActionButton( NULL )
+{
+ CLASSTRACKER_CREATE( InstAnimDynaPhysDSG );
+}
+//===========================================================================
+// InstAnimDynaPhysDSG::~InstAnimDynaPhysDSG
+//===========================================================================
+// Description:
+// InstAnimDynaPhysDSG dtor
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+InstAnimDynaPhysDSG::~InstAnimDynaPhysDSG()
+{
+ CLASSTRACKER_DESTROY( InstAnimDynaPhysDSG );
+ if ( mpCompDraw != NULL )
+ {
+ mpCompDraw->Release();
+ mpCompDraw = NULL;
+ }
+ if ( mpMultiController != NULL )
+ {
+ GetAnimEntityDSGManager()->Remove( mpMultiController );
+
+ mpMultiController->Release();
+ mpMultiController = NULL;
+ }
+ if ( mpActionButton != NULL )
+ {
+ // We don't addref this mpactionbutton (though perhaps we should)
+ // The actionbuttonhandler owns this DSG object, if we addrefed the button
+ // we would have problems trying to destroy both if the kept references to
+ // each other and only released in the dtors.
+ // Wierd Travis-inherited code and an annoying cyclic dependency.
+ mpActionButton = NULL;
+ }
+ if ( mpPose != NULL )
+ {
+ mpPose->Release();
+ mpPose = NULL ;
+ }
+}
+
+//===========================================================================
+// InstAnimDynaPhysDSG::Display
+//===========================================================================
+// Description:
+// Draws the object
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+void InstAnimDynaPhysDSG::Display()
+{
+ if(IS_DRAW_LONG) return;
+#ifdef PROFILER_ENABLED
+ char profileName[] = " InstAnimDynaPhysDSG Display";
+#endif
+ DSG_BEGIN_PROFILE(profileName)
+ if(CastsShadow())
+ {
+ BillboardQuadManager::Enable();
+ }
+
+ p3d::pddi->PushMultMatrix(PDDI_MATRIX_MODELVIEW, &mMatrix);
+ mpCompDraw->Display();
+ p3d::pddi->PopMatrix(PDDI_MATRIX_MODELVIEW);
+
+ if(CastsShadow())
+ {
+ BillboardQuadManager::Disable();
+ DisplaySimpleShadow();
+ }
+ DSG_END_PROFILE(profileName)
+}
+//===========================================================================
+// InstAnimDynaPhysDSG::DisplayBoundingBox
+//===========================================================================
+// Description:
+// Draws the object's bounding box
+//
+// Constraints:
+// Slow as hell
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+void InstAnimDynaPhysDSG::DisplayBoundingBox( tColour colour )
+{
+#ifndef RAD_RELEASE
+ p3d::pddi->PushMatrix(PDDI_MATRIX_MODELVIEW);
+ p3d::pddi->MultMatrix(PDDI_MATRIX_MODELVIEW,&mMatrix);
+
+
+ pddiPrimStream* stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.low.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.low.z);
+ p3d::pddi->EndPrims(stream);
+
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.low.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.high.z);
+ p3d::pddi->EndPrims(stream);
+
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.low.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.low.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.high.z);
+ p3d::pddi->EndPrims(stream);
+
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.high.z);
+ p3d::pddi->EndPrims(stream);
+
+ p3d::pddi->PopMatrix(PDDI_MATRIX_MODELVIEW);
+#endif
+}
+
+//===========================================================================
+// InstAnimDynaPhysDSG::GetBoundingBox
+//===========================================================================
+// Description:
+// Fills out the given bounding box pointer
+//
+// Constraints:
+//
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+/*
+void InstAnimDynaPhysDSG::GetBoundingBox(rmt::Box3D* box)
+{
+ rAssert( box != NULL );
+ (box->low).Add(mBBox.low, mPosn);
+ (box->high).Add(mBBox.high, mPosn);
+
+}*/
+
+//===========================================================================
+// InstAnimDynaPhysDSG::GetBoundingSphere
+//===========================================================================
+// Description:
+// Fills out the given bounding sphere pointer
+//
+// Constraints:
+//
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+/*
+void InstAnimDynaPhysDSG::GetBoundingSphere( rmt::Sphere* pSphere )
+{
+ (pSphere->centre).Add(mSphere.centre, mPosn);
+ pSphere->radius = mSphere.radius;
+}*/
+
+//===========================================================================
+// InstAnimDynaPhysDSG::LoadSetUp
+//===========================================================================
+// Description:
+// Sets up internal state. Required before any use
+//
+// Constraints:
+//
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+
+void InstAnimDynaPhysDSG::LoadSetUp( CollisionAttributes* ipCollAttr,
+ const rmt::Matrix& iMatrix,
+ tCompositeDrawable* ipCompDrawable,
+ tMultiController* ipMultiController,
+ tEntityStore* ipSearchStore )
+{
+
+ rAssert( ipCollAttr != NULL );
+ rAssert( ipCompDrawable != NULL );
+ rAssert( ipMultiController != NULL );
+
+ // Clone the composite drawable, and most importantly, clone its pose. We want a
+ // seperate pose for each composite drawable.
+ mpCompDraw = ipCompDrawable->Clone();
+ mpCompDraw->SetPose( ipCompDrawable->GetPose () );
+
+ // mpCompDraw = ipCompDrawable;
+
+ mpCompDraw->AddRef();
+
+
+ mpMultiController = ipMultiController;
+ mpMultiController->AddRef();
+ GetAnimEntityDSGManager()->Add( mpMultiController );
+
+ mMatrix = iMatrix;
+ // Setup the physics
+ tPose* p3dPose = mpCompDraw->GetPose();
+ p3dPose->Evaluate();
+
+ // the sim library doesn't have RTTI enabled. When I upcast this, there will be no RTTI check, if
+ // someone changes this to something other than SimStateArticulated, make absolutely sure that everything changes in this file
+ // I can only imagine what horrific errors will result from a bad static_cast downcast.
+
+ // The current sim library doesn't appear to dump anything in the store, it only uses
+ // it to find the collision and physics objects out of it using the name as the parameter to p3d::find<>
+ mpPose = new poser::Pose( p3dPose );
+ mpPose->AddRef();
+
+ // The sim library does in fact add something to the store, the above comment notwithstanding.
+ // So we wrap this call in a PushSection/PopSection.
+ //
+ p3d::inventory->PushSection( );
+ p3d::inventory->SelectSection( "Default" );
+
+ sim::SimState* pSimState = sim::SimStateArticulated::CreateSimStateArticulated( mpCompDraw->GetUID(),mpPose, sim::SimStateAttribute_Default, ipSearchStore );
+ if ( pSimState != NULL )
+ {
+ // Convert the articulated object into a rigid body, requiring fewer CPU computations
+ // and avoiding a lot of the nonsense involved with bounding box sub-volumes
+ sim::SimStateArticulated* pArty = static_cast< sim::SimStateArticulated* > ( pSimState );
+ // Sanity check on type
+ rAssert( dynamic_cast< sim::SimStateArticulated* > (pSimState) != NULL );
+ pArty->ConvertToRigidBody();
+ rAssertMsg( pArty->ConvertedToRigidBody(), "This articulated object cannot be converted to a rigid body, aborting." );
+ }
+ else
+ {
+ pSimState = sim::SimState::CreateSimState( mpCompDraw->GetUID(), sim::SimStateAttribute_Default, ipSearchStore );
+ rAssertMsg( pSimState != NULL, "Cannot load object as either a simstate or simstatearticulated!" );
+ }
+
+ p3d::inventory->PopSection( );
+
+ pSimState->SetControl(sim::simAICtrl);
+ pSimState->SetTransform(mMatrix);
+
+
+
+ pSimState->GetCollisionObject()->GetCollisionVolume()->UpdateAll();
+ // Needed for SetSimState to work because of tRefCounted::Assign
+ pSimState->AddRef();
+
+ mpPhysObj = (sim::PhysicsObject*)(pSimState->GetSimulatedObject());
+ pSimState->mAIRefIndex = PhysicsAIRef::redBrickPhizMoveableAnim;
+
+ SetCollisionAttributes(ipCollAttr);
+
+ pSimState->mAIRefPointer = this;
+ SetSimState( pSimState );
+ mpCompDraw->ProcessShaders(*this);
+
+ pSimState->Release();
+
+
+}
+bool InstAnimDynaPhysDSG::Break()
+{
+ bool success;
+ success = InstDynaPhysDSG::Break();
+ if ( mpActionButton != NULL )
+ {
+ mpActionButton->GameObjectDestroyed();
+ }
+ return success;
+}
+
+void InstAnimDynaPhysDSG::Update( float deltaTime )
+{
+
+ // If an action button is tied to this thing, update it
+ if ( mpActionButton )
+ {
+ //mpActionButton->Update( deltaTime );
+ }
+ mpPhysObj->Update( deltaTime );
+
+ sim::CollisionObject* collObj = mpSimStateObj->GetCollisionObject();
+ if ( collObj->HasMoved( ) || collObj->HasRelocated( ) )
+ {
+ collObj->Update();
+ // Save the old matrix
+ rmt::Box3D oldBox;
+ //GetBoundingBox( &oldBox );
+ (oldBox.low).Add(mBBox.low, mPosn);
+ (oldBox.high).Add(mBBox.high, mPosn);
+
+
+ // Bounding box automatically changes when mPosn changes
+ mMatrix = mpSimStateObj->GetTransform();
+ mPosn = mpSimStateObj->GetPosition();
+ // Move the object in the DSG
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mRenderLayer ));
+ // Sanity check
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Move( oldBox, this );
+ }
+}
+
+
+
+
+void InstAnimDynaPhysDSG::SetTransform( const rmt::Matrix& transform )
+{
+ mMatrix = transform;
+ // Matrix is reset, recompute the bounding box and move it
+ // in the DSG( Warning, this assumes that the user actually has it IN
+ // the DSG. This was always the case for DSG objects so lets not break
+ // tradition
+ rmt::Box3D oldBox;
+ GetBoundingBox( &oldBox );
+
+ // Bounding box relies on mPosn, lets update it
+ mPosn = transform.Row(3);
+
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mRenderLayer ));
+ // Sanity check
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Move( oldBox, this );
+
+ // Finally, tell physics what our new transform matrix
+ mpSimStateObj->SetTransform( transform );
+ mpSimStateObj->GetCollisionObject()->SetHasRelocated( true );
+ mpSimStateObj->GetCollisionObject()->SetHasMoved( true );
+ mpSimStateObj->GetCollisionObject()->Update();
+
+ sim::CollisionObject* collObj = mpSimStateObj->GetCollisionObject();
+
+}
+
+void InstAnimDynaPhysDSG::SetAction( ActionButton::AnimSwitch* pActionButton )
+{
+ // Don't increment refcount. TBJ's animcolldsg does increment the refcount, however
+ // the bee cameras are in essence owned by the actionbuttonhandler thats calling
+ // SetAction. Refcounting in the reverse direction will cause major problems when the
+ // ActionButtonManager tries to kill the Handler, which tries to kill the DSG object
+ mpActionButton = pActionButton;
+}
+
+AnimDynaPhysWrapper::AnimDynaPhysWrapper():
+mCompDraw( NULL ),
+mMultiController( NULL ),
+mPhysicsObject( NULL ),
+mCollisionObject( NULL ),
+mHasAlpha( false ),
+mStatePropData( NULL ),
+mHasAnimations( false )
+{
+
+}
+
+AnimDynaPhysWrapper::~AnimDynaPhysWrapper()
+{
+ if ( mCompDraw != NULL )
+ {
+ mCompDraw->Release();
+ mCompDraw = NULL;
+ }
+ if ( mMultiController != NULL )
+ {
+ mMultiController->Release();
+ mMultiController = NULL;
+ }
+ if ( mPhysicsObject != NULL )
+ {
+ mPhysicsObject->Release();
+ mPhysicsObject = NULL;
+ }
+ if ( mCollisionObject != NULL )
+ {
+ mCollisionObject->Release();
+ mCollisionObject = NULL;
+ }
+ if ( mStatePropData != NULL )
+ {
+ mStatePropData->Release();
+ mStatePropData = NULL;
+ }
+}
+
+float AnimDynaPhysWrapper::GetVolume()const
+{
+ rAssert( mPhysicsObject != NULL );
+
+#ifndef FINAL
+ if (mPhysicsObject == NULL)
+ {
+ char outbuffer [255];
+ sprintf(outbuffer,"Error: %s is missing a Dynamic Bounding Volume \n",this->GetName());
+ rTuneAssertMsg( 0,outbuffer );
+ }
+#endif
+
+ return mPhysicsObject->GetVolume();
+}
+
+tCompositeDrawable* AnimDynaPhysWrapper::GetDrawable()const
+{
+ return mCompDraw;
+}
+
+tMultiController* AnimDynaPhysWrapper::GetController()const
+{
+ return mMultiController;
+}
+
+CStatePropData* AnimDynaPhysWrapper::GetStatePropData()const
+{
+ return mStatePropData;
+}
+
+
+bool AnimDynaPhysWrapper::HasAlpha()const
+{
+ return mHasAlpha;
+}
+
+InstAnimDynaPhysDSG* AnimDynaPhysWrapper::CreateDSGObject( CollisionAttributes *pAttr, const rmt::Matrix& transform, GameMemoryAllocator heap )
+{
+ // allocate a new dynaphys object
+ InstAnimDynaPhysDSG* pDSG = new (heap) InstAnimDynaPhysDSG;
+
+ // set it up via loadsetup
+ pDSG->LoadSetUp( pAttr, transform,
+ mCompDraw, mMultiController, NULL );
+
+ pDSG->mTranslucent = true;
+
+ return pDSG;
+}
+
+
+
+
diff --git a/game/code/render/DSG/InstAnimDynaPhysDSG.h b/game/code/render/DSG/InstAnimDynaPhysDSG.h
new file mode 100644
index 0000000..8ae410e
--- /dev/null
+++ b/game/code/render/DSG/InstAnimDynaPhysDSG.h
@@ -0,0 +1,163 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: InstAnimDynaPhysDSG
+//
+// Description: An InstDynaPhysDSG object that has a simple cyclic animation
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef INSTANIMDYNAPHYSDSG_H
+#define INSTANIMDYNAPHYSDSG_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <render/DSG/InstDynaPhysDSG.h>
+#include <memory/srrmemory.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+class tMultiController;
+class CStatePropData;
+
+namespace sim
+{
+ class SimState;
+ class SimStateArticulated;
+ class CollisionVolume;
+ class CollisionObject;
+};
+namespace ActionButton
+{
+ class AnimSwitch;
+};
+namespace poser
+{
+ class Pose;
+}
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// An instanced DSG object with a simple animation (texture, billboards, uv, etc)
+// that has physics, could possibly be breakable.
+//
+// Constraints:
+// Animation playback is set to cycle on all objects, animation is not instanced
+//
+//===========================================================================
+class InstAnimDynaPhysDSG : public InstDynaPhysDSG
+{
+ public:
+ InstAnimDynaPhysDSG();
+ virtual ~InstAnimDynaPhysDSG();
+
+ virtual void Display();
+ virtual void DisplayBoundingBox( tColour colour = tColour( 255,0 ,0 ) );
+ virtual void Update(float dt);
+
+ // Set position and orientation
+ virtual void SetTransform( const rmt::Matrix& transform );
+
+ void LoadSetUp( CollisionAttributes* ipCollAttr,
+ const rmt::Matrix& iMatrix,
+ tCompositeDrawable* ipCompDrawable,
+ tMultiController* ipMultiController,
+ tEntityStore* ipSearchStore );
+
+ void SetAction( ActionButton::AnimSwitch* pActionButton );
+ // If the object is a breakable, play the breakable animation
+ // and flag this object for removal
+ // otherwise, do nothing
+ virtual bool Break();
+
+ protected:
+
+ tCompositeDrawable* mpCompDraw;
+ tMultiController* mpMultiController;
+ ActionButton::AnimSwitch* mpActionButton;
+ poser::Pose* mpPose;
+
+ protected:
+
+ void UpdatePose( float deltaTime );
+ void UpdateBBox( sim::CollisionVolume* pVolume );
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow InstAnimDynaPhysDSG from being copied and assigned.
+ InstAnimDynaPhysDSG( const InstAnimDynaPhysDSG& );
+ InstAnimDynaPhysDSG& operator=( const InstAnimDynaPhysDSG& );
+
+ friend class AnimDynaPhysLoader;
+
+};
+//===========================================================================
+//
+// Description:
+// Wrapper for Instanced Animated Objects. The wrapper contains the compdrawables
+// all multicontrollers, meshes, billboards, etc.
+//
+// Constraints:
+//
+//
+//===========================================================================
+
+class AnimDynaPhysWrapper : public tEntity
+{
+public:
+
+ AnimDynaPhysWrapper();
+ virtual ~AnimDynaPhysWrapper();
+
+ float GetVolume()const;
+ bool HasAlpha()const;
+
+ tCompositeDrawable* GetDrawable()const;
+ tMultiController* GetController()const;
+ CStatePropData* GetStatePropData()const;
+ sim::PhysicsObject* GetPhysicsObject()const { return mPhysicsObject; }
+ sim::CollisionObject* GetCollisionObject()const { return mCollisionObject; }
+
+ void SetHasAnimation( bool hasAnim ) { mHasAnimations = hasAnim; }
+ bool HasAnimation()const { return mHasAnimations; }
+
+ // Creates a new InstAnimDynaPhys object,
+ // optionally places it on the given heap
+ InstAnimDynaPhysDSG* CreateDSGObject( CollisionAttributes* pAttr, const rmt::Matrix& transform, GameMemoryAllocator );
+
+private:
+
+ AnimDynaPhysWrapper( const AnimDynaPhysWrapper& );
+ AnimDynaPhysWrapper& operator=( const AnimDynaPhysWrapper& );
+
+private:
+
+ tCompositeDrawable* mCompDraw;
+ tMultiController* mMultiController;
+ sim::PhysicsObject* mPhysicsObject;
+ sim::CollisionObject* mCollisionObject;
+ bool mHasAlpha;
+ CStatePropData* mStatePropData;
+ bool mHasAnimations;
+
+ friend class AnimDynaPhysWrapperLoader;
+ friend class AnimObjDSGWrapperLoader;
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/DSG/InstDynaPhysDSG.cpp b/game/code/render/DSG/InstDynaPhysDSG.cpp
new file mode 100644
index 0000000..f7a30ff
--- /dev/null
+++ b/game/code/render/DSG/InstDynaPhysDSG.cpp
@@ -0,0 +1,663 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: InstDynaPhysDSG.cpp
+//
+// Description: Implementation for InstDynaPhysDSG class.
+//
+// History: Implemented --Devin [6/17/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+
+
+#include <simcollision/collisionobject.hpp>
+#include <simcollision/collisionvolume.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/InstDynaPhysDSG.h>
+
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/Culling/WorldScene.h>
+
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/character/character.h>
+
+#include <render/particles/particlemanager.h>
+#include <render/breakables/breakablesmanager.h>
+#include <render/IntersectManager/IntersectManager.h>
+
+#include <mission/gameplaymanager.h>
+#include <worldsim/worldphysicsmanager.h>
+
+#include <p3d/billboardobject.hpp>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+
+// A bias for the mass of a breakable object that determines how much force is required to smash it
+// if ( impactForce > mass * MASS_IMPULSE_BREAK_BIAS )
+// object breaks
+const float MASS_IMPULSE_BREAK_BIAS = 0.0f; //200.0f;
+const float INST_DYNA_MASS_IMPULSE_PARTICLE_BIAS = 5.0f;
+
+//************************************************************************
+//
+// Public Member Functions : InstDynaPhysDSG Interface
+//
+//************************************************************************
+//========================================================================
+// InstDynaPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+InstDynaPhysDSG::InstDynaPhysDSG()
+: /*mIsHit( false ), moved up to DynaPhysDSG*/ mpGeo( NULL ), mHideOnHitIndex( -1 )
+{
+
+ // move to DynaPhysDSG
+ //mGroundPlaneIndex = -1;
+}
+//========================================================================
+// InstDynaPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+InstDynaPhysDSG::~InstDynaPhysDSG()
+{
+BEGIN_PROFILE( "InstDynaPhysDSG Destroy" );
+ //mpPhysObj->Release(); // the owner simstate does this.
+ if ( mpGeo != NULL )
+ {
+ mpGeo->Release();
+ }
+
+END_PROFILE( "InstDynaPhysDSG Destroy" );
+}
+//========================================================================
+// InstDynaPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+
+void InstDynaPhysDSG::Display()
+{
+ if(IS_DRAW_LONG) return;
+#ifdef PROFILER_ENABLED
+ char profileName[] = " InstDynaPhysDSG Display";
+#endif
+ DSG_BEGIN_PROFILE(profileName)
+ if(CastsShadow())
+ {
+ BillboardQuadManager::Enable();
+ }
+ p3d::pddi->PushMultMatrix(PDDI_MATRIX_MODELVIEW, &mMatrix);
+ if( mHideOnHitIndex > -1 )
+ {
+ tCompositeDrawable* compDraw = static_cast<tCompositeDrawable*>( mpGeo );
+ compDraw->GetDrawableElement( mHideOnHitIndex )->SetVisibility( !mIsHit );
+ }
+ mpGeo->Display();
+ p3d::pddi->PopMatrix(PDDI_MATRIX_MODELVIEW);
+ if(CastsShadow())
+ {
+ BillboardQuadManager::Disable();
+ DisplaySimpleShadow();
+ }
+ DSG_END_PROFILE(profileName)
+}
+//========================================================================
+// InstDynaPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstDynaPhysDSG::GetBoundingBox(rmt::Box3D* box)
+{
+ //box->low = mBBox.low;
+ //box->high = mBBox.high;
+
+ // note: - because the box is assumed to be axis aligned, we just want to translate it,
+ // not rotate!
+
+ (box->low).Add(mBBox.low, mPosn);
+ (box->high).Add(mBBox.high, mPosn);
+}
+//========================================================================
+// InstDynaPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstDynaPhysDSG::GetBoundingSphere(rmt::Sphere* pSphere)
+{
+ // transform here is overkill
+ (pSphere->centre).Add(mSphere.centre, mPosn);
+
+ pSphere->radius = mSphere.radius;
+}
+
+
+//========================================================================
+// InstDynaPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+sim::PhysicsObject* InstDynaPhysDSG::pPhysObj()
+{
+ // not sure we need this?
+ return mpPhysObj;
+}
+//========================================================================
+// InstDynaPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+rmt::Matrix* InstDynaPhysDSG::pMatrix()
+{
+ return &mMatrix;
+}
+//========================================================================
+// InstDynaPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tDrawable* InstDynaPhysDSG::pGeo()
+{
+ return mpGeo;
+}
+
+
+
+
+
+
+
+//=============================================================================
+// InstDynaPhysDSG::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void InstDynaPhysDSG::Update(float dt)
+{
+ mpPhysObj->Update(dt);
+
+ sim::CollisionObject* collObj = mpSimStateObj->GetCollisionObject();
+ if ( collObj->HasMoved( ) || collObj->HasRelocated( ) )
+ {
+ collObj->Update();
+
+
+ // do this _before_ updating matrix!
+ rmt::Box3D oldBox;
+
+ (oldBox.low).Add(mBBox.low, mPosn);
+ (oldBox.high).Add(mBBox.high, mPosn);
+
+ mMatrix = mpSimStateObj->GetTransform();
+ mPosn = mpSimStateObj->GetPosition();
+
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mRenderLayer ));
+ // Sanity check
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Move( oldBox, this );
+ }
+
+ if(mIsHit && (GetCollisionAttributes()->GetClasstypeid() == PROP_ONETIME_MOVEABLE))
+ {
+ if(!GetGameplayManager()->TestPosInFrustrumOfPlayer( mPosn, 0, mSphere.radius))
+ {
+ this->AddRef();
+ ((WorldRenderLayer*)GetRenderManager()->mpLayer(RenderEnums::LevelSlot))->RemoveGuts(this);
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(this);
+ GetRenderManager()->mEntityDeletionList.Add(this);
+ }
+ }
+}
+
+
+//=============================================================================
+// InstDynaPhysDSG::PreReactToCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (sim::SimState* pCollidedObj, sim::Collision& inCollision)
+//
+// Return: bool
+//
+//=============================================================================
+sim::Solving_Answer InstDynaPhysDSG::PreReactToCollision(sim::SimState* pCollidedObj, sim::Collision& inCollision)
+{
+ if(this->GetSimState()->GetControl() == sim::simSimulationCtrl)
+ {
+ return sim::Solving_Continue;
+ }
+
+ //Character* pCharacter = dynamic_cast<Character*>( static_cast<IEntityDSG*>( pCollidedObj->mAIRefPointer ) );
+ if ( pCollidedObj->mAIRefIndex == PhysicsAIRef::PlayerCharacter ||
+ pCollidedObj->mAIRefIndex == PhysicsAIRef::NPCharacter )
+ {
+ rAssert( dynamic_cast< Character* >( static_cast< tRefCounted* >( pCollidedObj->mAIRefPointer ) ) );
+ Character* pCharacter = static_cast< Character* >( pCollidedObj->mAIRefPointer );
+ pCharacter->TouchProp( this );
+ return sim::Solving_Aborted;
+ }
+ else
+ {
+
+ // if this object is under AI ctrl and it collided with a static it is just a poory placed prop
+ //
+ // return
+
+ if(this->GetSimState()->GetControl() == sim::simAICtrl && ((pCollidedObj->GetCollisionObject()->IsStatic() == true) || (pCollidedObj->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveableGroundPlane)))
+ {
+ return sim::Solving_Aborted;
+ }
+
+
+ // try moving this to post....
+
+
+ // Moving to sim. Breakables use a bit of a trick, they don't
+ // set the object to go under sim control immediately, but get set in
+ // PostReactToCollision. Everything else can be turned on automatically
+
+ mIsHit = true;
+ //if ( mpCollisionAttributes == NULL ||
+ // mpCollisionAttributes->GetClasstypeid() != PROP_BREAKABLE )
+
+ {
+ AddToSimulation( );
+ }
+
+ return sim::Solving_Continue;
+ //return sim::Solving_Aborted;
+ }
+
+}
+/*
+==============================================================================
+InstDynaPhysDSG::AddToSimulation
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void InstDynaPhysDSG::AddToSimulation( void )
+{
+ mpSimStateObj->SetControl(sim::simSimulationCtrl);
+
+// rAssert(mGroundPlaneIndex != -1);
+ if ( mGroundPlaneIndex != -1 )
+ {
+ // mGroundPlaneIndex = FetchGroundPlane();
+
+ GetWorldPhysicsManager()->EnableGroundPlaneCollision(mGroundPlaneIndex);
+
+ }
+}
+
+//=============================================================================
+// InstDynaPhysDSG::PostReactToCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (sim::SimState* pCollidedObj, sim::Collision& inCollision)
+//
+// Return: sim
+//
+//=============================================================================
+sim::Solving_Answer InstDynaPhysDSG::PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision)
+{
+
+
+ // subclass-specific shit here
+
+
+ // If it is a breakable object and has an assicated particle animation with it (it should)
+ // play the associated particle effect
+ if (mpCollisionAttributes != NULL)
+ {
+ if( mpCollisionAttributes->GetClasstypeid() == PROP_BREAKABLE ||
+ mpCollisionAttributes->GetClasstypeid() == PROP_MOVEABLE ||
+ mpCollisionAttributes->GetClasstypeid() == PROP_ONETIME_MOVEABLE)
+
+ {
+
+ //float impulsemag = impulse.Magnitude();
+
+ // Ok, we are going to be doing a bit of a trick to determine whether or not a breakable
+ // object is hit with enough power to blow it right out of the DSG.
+ // How hard an object is to break is determined directly by its mass * a scalar bias
+
+
+ // if the impact force exceeds the threshold, the object is broken
+ // What happens is that Greg switches it from AI control to sim control.
+ // If the object remained under AI control, the vehicle, despite hitting it hard enough
+ // to annihilate it, would still bounce off if it as if it hit a brick wall
+ // So we switch it to SimControl. The vehicle is slowed down somewhat by the impact
+ // but it won't just bounce off it.
+
+
+
+ /*
+
+ greg
+ jan 29, 2003
+
+ move this to pre react to collision since the threshold is 0
+
+
+ float threshold = mpCollisionAttributes->GetMass() * MASS_IMPULSE_BREAK_BIAS;
+
+ // temp - until we get the mass/threshold thing worked out.
+ if(mpSimStateObj->GetControl() == sim::simAICtrl && impulsemag > threshold)
+ {
+ this->AddToSimulation();
+ return sim::Solving_Aborted; // was this so that we don't get the static reaction? yes.
+ //return sim::Solving_Continue;
+ }
+ */
+
+ }
+
+ bool wasBroken = false;
+ if ( mpSimStateObj->GetControl() == sim::simSimulationCtrl )
+ {
+ wasBroken = Break();
+ }
+
+ // Calc the minimum amount of force required to emit particles. The function is trivial
+ // if impulsemag > mass * scale
+ // emit particles
+ float particleThreshold = mpCollisionAttributes->GetMass() * INST_DYNA_MASS_IMPULSE_PARTICLE_BIAS;
+
+ if ( mpCollisionAttributes->GetParticle() != ParticleEnum::eNull &&
+ mWasParticleEffectTriggered == false &&
+ wasBroken == false &&
+ impulse.Magnitude() > particleThreshold )
+ {
+ ParticleAttributes attr;
+ attr.mType = mpCollisionAttributes->GetParticle();
+ GetParticleManager()->Add( attr, mMatrix );
+ mWasParticleEffectTriggered = true;
+ }
+
+
+ /*
+
+ I don't think this test had any real effect
+
+ if( mpCollisionAttributes->GetClasstypeid() == PROP_BREAKABLE &&
+ mpCollisionAttributes->GetBreakable() == BreakablesEnum::eKrustyGlassBreaking )
+ {
+ // we don't want the car to receive any impulse from this
+ return sim::Solving_Aborted;
+ }
+ */
+
+
+ }
+
+
+
+ return CollisionEntityDSG::PostReactToCollision(impulse, inCollision);
+
+}
+
+bool InstDynaPhysDSG::Break()
+{
+ bool success;
+
+ // Any shadows or light pools should be deactivated
+ SetShadow( NULL );
+
+ if ( mpCollisionAttributes->GetClasstypeid() == PROP_BREAKABLE &&
+ mpCollisionAttributes->GetBreakable() != BreakablesEnum::eNull )
+ {
+ GetBreakablesManager()->Play( mpCollisionAttributes->GetBreakable(), mMatrix );
+ GetBreakablesManager()->RemoveBrokenObjectFromWorld( this, GetRenderLayer(), true );
+ success = true;
+ }
+ else
+ {
+ success = false;
+ }
+ return success;
+}
+
+
+//=============================================================================
+// InstDynaPhysDSG::OnSetSimState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( sim::SimState* ipSimState )
+//
+// Return: void
+//
+//=============================================================================
+void InstDynaPhysDSG::OnSetSimState( sim::SimState* ipSimState )
+{
+ tRefCounted::Assign( mpSimStateObj, ipSimState );
+
+ //mpSimStateObj->mAIRefIndex = InstDynaPhysDSG::GetAIRef();
+ mpSimStateObj->mAIRefIndex = this->GetAIRef();
+
+ mpSimStateObj->ResetVelocities();
+
+ mPosn = mpSimStateObj->GetPosition();
+
+
+ // set up box and sphere for DSG
+ //
+ // note, unlike the StaticPhysDSG, this box and sphere are in local space
+
+ //mPosn = mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mPosition;
+
+ /*
+ mBBox.low = mPosn;
+ mBBox.high = mPosn;
+ mBBox.high += mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+ mBBox.low -= mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+
+ mSphere.centre.Sub(mBBox.high,mBBox.low);
+ mSphere.centre *= 0.5f;
+ mSphere.centre.Add(mBBox.low);
+ mSphere.radius = mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mSphereRadius;
+ */
+
+
+ rmt::Vector center = mpPhysObj->GetExternalCMOffset();
+ float radius = mpSimStateObj->GetSphereRadius();
+
+ mBBox.low = center;
+ mBBox.high = center;
+ mBBox.high += mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+ mBBox.low -= mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+
+ mSphere.centre = center;
+ mSphere.radius = radius;
+
+
+ // assing physics attributes
+ rAssert(mpCollisionAttributes);
+ mpSimStateObj->SetPhysicsProperties(mpCollisionAttributes->GetPhysicsProperties());
+
+
+}
+
+
+
+///////////////////////////////////////////////////////////////////////
+// Load interface
+///////////////////////////////////////////////////////////////////////
+
+//========================================================================
+// InstDynaPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstDynaPhysDSG::LoadSetUp
+(
+ sim::SimState* ipSimState,
+ CollisionAttributes* ipCollAttr,
+ const rmt::Matrix& iMatrix,
+ tDrawable* ipGeo,
+ tDrawable* ipShadow )
+{
+
+ //mpPhysObj = ipPhysObj;
+ mpPhysObj = (sim::PhysicsObject*)(ipSimState->GetSimulatedObject());
+
+ mMatrix = iMatrix;
+ mpGeo = ipGeo;
+ mpGeo->AddRef();
+
+ SetCollisionAttributes(ipCollAttr);
+ SetSimState(ipSimState);
+
+ //mShaderUID = ipGeo->GetShader(0)->GetUID();
+ ipGeo->ProcessShaders(*this);
+
+ // update some shit with collision attributes:
+
+ //StaticEntityDSG* pStatEntityDSG = static_cast< StaticEntityDSG* >( this );
+ // rAssert( dynamic_cast<StaticEntityDSG* >(this) != NULL )
+ SetShadow( ipShadow );
+}
+
+InstDynaPhysDSG* InstDynaPhysDSG::Clone(const char* Name, const rmt::Matrix& iMatrix)
+{
+ InstDynaPhysDSG* c = new InstDynaPhysDSG();
+ rAssert(c);
+ c->SetName(Name);
+ c->mMatrix = iMatrix;
+ c->mpGeo = this->mpGeo;
+ c->mpGeo->AddRef();
+ c->SetCollisionAttributes(this->GetCollisionAttributes());
+ sim::SimState* simState = sim::SimState::CreateSimState(this->GetSimState());
+ simState->SetControl(sim::simAICtrl);
+ simState->SetTransform(iMatrix);
+ c->mpPhysObj = (sim::PhysicsObject*)(simState->GetSimulatedObject());
+ c->SetSimState(simState);
+ mpGeo->ProcessShaders(*c);
+ c->SetShadow(this->mpShadow);
+ return c;
+}
+
+// Override setrank, shadows get drawn first always!
+void
+InstDynaPhysDSG::SetRank(rmt::Vector& irRefPosn, rmt::Vector& irRefVect)
+{
+ if ( CastsShadow() )
+ {
+ mRank = FLT_MAX;
+ }
+ else
+ {
+ IEntityDSG::SetRank( irRefPosn, irRefVect );
+ }
+}
+
+//************************************************************************
+//
+// Protected Member Functions : InstDynaPhysDSG
+//
+//************************************************************************
+
+
+
+//************************************************************************
+//
+// Private Member Functions : InstDynaPhysDSG
+//
+//************************************************************************
diff --git a/game/code/render/DSG/InstDynaPhysDSG.h b/game/code/render/DSG/InstDynaPhysDSG.h
new file mode 100644
index 0000000..215a394
--- /dev/null
+++ b/game/code/render/DSG/InstDynaPhysDSG.h
@@ -0,0 +1,124 @@
+#ifndef __InstDynaPhysDSG_H__
+#define __InstDynaPhysDSG_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: InstDynaPhysDSG
+//
+// Description: The InstDynaPhysDSG does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/06/17]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/DSG/DynaPhysDSG.h>
+#include <worldsim/physicsairef.h>
+
+class tDrawable;
+
+namespace sim
+{
+ class SimState;
+ class Collision;
+ class PhysicsObject;
+}
+
+//========================================================================
+//
+// Synopsis: The InstDynaPhysDSG; Synopsis by Inspection.
+//
+//========================================================================
+class InstDynaPhysDSG
+: public DynaPhysDSG
+{
+public:
+ InstDynaPhysDSG();
+ virtual ~InstDynaPhysDSG();
+
+ //////////////////////////////////////////////////////////////////////////
+ // tDrawable
+ //////////////////////////////////////////////////////////////////////////
+ virtual void Display();
+ virtual void GetBoundingBox(rmt::Box3D* box);
+ virtual void GetBoundingSphere(rmt::Sphere* sphere);
+
+ virtual void Update(float dt);
+
+ virtual sim::Solving_Answer PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision );
+ virtual sim::Solving_Answer PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision);
+ // Plays the breakable animation if one exists
+ // and flags the object for DSG removal
+ // returns true if the object was broken, false if it was not(no animation available)
+ virtual bool Break();
+
+ void AddToSimulation( void );
+ virtual int GetAIRef() {return PhysicsAIRef::redBrickPhizMoveable;}
+ /*
+ static int GetAIRef()
+ {
+ return PhysicsAIRef::redBrickPhizMoveable;
+ }
+ */
+ virtual void OnSetSimState( sim::SimState* ipSimState );
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // Accessors
+ ///////////////////////////////////////////////////////////////////////
+
+ sim::PhysicsObject* pPhysObj();
+ rmt::Matrix* pMatrix();
+ tDrawable* pGeo();
+
+ //virtual void FetchGroundPlane(); // tell object to get itself a ground plane, through worldphysicsmanager, through groundplanepool
+ //virtual void FreeGroundPlane(); // make this safe to call even when we don't have one?
+
+ //virtual bool IsAtRest();
+
+ // need these?
+ //virtual int GetGroundPlaneIndex() {return mGroundPlaneIndex;}
+ //virtual void SetGroundPlaneIndex(int index) {mGroundPlaneIndex = index;}
+
+ ///////////////////////////////////////////////////////////////////////
+ // Load interface
+ ///////////////////////////////////////////////////////////////////////
+ // Shadow is optional, set NULL to disable
+ void LoadSetUp( sim::SimState* ipSimState,
+ CollisionAttributes* ipCollAttr,
+ const rmt::Matrix& iMatrix,
+ tDrawable* ipGeo,
+ tDrawable* ipShadow = NULL);
+ InstDynaPhysDSG* Clone(const char* Name, const rmt::Matrix& iMatrix);
+ virtual void SetRank(rmt::Vector& irRefPosn, rmt::Vector& irRefVect);
+
+ // you unbelievably stupid motherfucker! (greg talking to himself)
+ //bool mIsHit;
+
+protected:
+
+ // just for convenience I guess
+ sim::PhysicsObject* mpPhysObj;
+
+ rmt::Matrix mMatrix;
+ tDrawable* mpGeo;
+ int mHideOnHitIndex;
+
+
+ // need to hold this here, since the same object might be in more than one list..
+ // moved to DynaPhysDSG...
+ //int mGroundPlaneIndex;
+
+private:
+
+
+};
+
+#endif
diff --git a/game/code/render/DSG/InstStatEntityDSG.cpp b/game/code/render/DSG/InstStatEntityDSG.cpp
new file mode 100644
index 0000000..e44bdef
--- /dev/null
+++ b/game/code/render/DSG/InstStatEntityDSG.cpp
@@ -0,0 +1,487 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: InstStatEntityDSG.cpp
+//
+// Description: Implementation for InstStatEntityDSG class.
+//
+// History: Implemented --Devin [6/17/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <p3d/utility.hpp>
+#include <render/DSG/InstStatEntityDSG.h>
+#include <memory/srrmemory.h>
+#include <render/IntersectManager/IntersectManager.h>
+#include <p3d/matrixstack.hpp>
+#include <p3d/view.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/billboardobject.hpp>
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : InstStatEntityDSG Interface
+//
+//************************************************************************
+//========================================================================
+// InstStatEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+InstStatEntityDSG::InstStatEntityDSG()
+: mpMatrix( NULL ),
+mpShadowDrawable( NULL ),
+mpShadowMatrix( NULL )
+{
+}
+//========================================================================
+// InstStatEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+InstStatEntityDSG::~InstStatEntityDSG()
+{
+BEGIN_PROFILE( "InstStatEntityDSG Destroy" );
+ if(mpMatrix)
+ {
+ delete mpMatrix;
+ }
+ if ( mpShadowDrawable )
+ {
+ mpShadowDrawable->Release();
+ mpShadowDrawable = NULL;
+ }
+ if ( mpShadowMatrix )
+ {
+ delete mpShadowMatrix;
+ }
+
+END_PROFILE( "InstStatEntityDSG Destroy" );
+}
+//========================================================================
+// InstStatEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+
+void InstStatEntityDSG::Display()
+{
+ if(IS_DRAW_LONG) return;
+#ifdef PROFILER_ENABLED
+ char profileName[] = " InstStatEntityDSG Display";
+#endif
+ DSG_BEGIN_PROFILE(profileName)
+ if(CastsShadow())
+ {
+ BillboardQuadManager::Enable();
+ }
+ p3d::pddi->PushMultMatrix(PDDI_MATRIX_MODELVIEW, mpMatrix);
+ mpDrawstuff->Display();
+ p3d::pddi->PopMatrix(PDDI_MATRIX_MODELVIEW);
+
+ if(CastsShadow())
+ {
+ BillboardQuadManager::Disable();
+ DisplaySimpleShadow();
+ }
+
+ DSG_END_PROFILE(profileName)
+}
+
+//========================================================================
+// InstStatEntityDSG::DisplaySimpleShadow
+//========================================================================
+//
+// Description: Draws the given shadow drawable after translation by the
+// shadow matrix
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+
+void InstStatEntityDSG::DisplaySimpleShadow()
+{
+ BEGIN_PROFILE("DisplaySimpleShadow")
+ p3d::pddi->SetZWrite(false);
+ if ( mpShadowDrawable != NULL )
+ {
+ if ( mpShadowMatrix == NULL )
+ {
+ mpShadowMatrix = CreateShadowMatrix( mpMatrix->Row( 3 ) );
+ }
+ if ( mpShadowMatrix )
+ {
+
+ // Create a camera that pushes the shadow a meter towards
+ // the camera
+ rmt::Vector camPos;
+ p3d::context->GetView()->GetCamera()->GetWorldPosition( &camPos );
+ camPos.Sub( mpShadowMatrix->Row(3) );
+ camPos.Normalize();
+
+ // Move the shadow towards the camera by the following distance.
+ const float Z_FIGHTING_OFFSET = 0.2f;
+ camPos.Scale( Z_FIGHTING_OFFSET );
+
+ // Final shadow transform = position/orientation * tocamera translation
+ rmt::Matrix shadowTransform( *mpShadowMatrix );
+ shadowTransform.Row( 3 ).Add( camPos );
+
+ // Display
+ p3d::stack->PushMultiply( shadowTransform );
+ mpShadowDrawable->Display();
+ p3d::stack->Pop();
+ }
+ }
+ p3d::pddi->SetZWrite(true);
+ END_PROFILE("DisplaySimpleShadow")
+}
+//========================================================================
+// InstStatEntityDSG::RecomputeShadowPosition
+//========================================================================
+//
+// Description: Finds the intersection point and computes mpShadowMatrix
+//
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstStatEntityDSG::RecomputeShadowPosition()
+{
+ if ( mpShadowMatrix )
+ {
+ rmt::Vector position;
+ GetPosition( &position );
+ ComputeShadowMatrix( position, mpShadowMatrix );
+ }
+}
+
+//========================================================================
+// InstStatEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstStatEntityDSG::GetBoundingBox(rmt::Box3D* box)
+{
+ rmt::Box3D fuckinTempBox;
+ mpDrawstuff->GetBoundingBox(&fuckinTempBox);
+
+ if ( rmt::Fabs(fuckinTempBox.Height()) > 10000.0f &&
+ rmt::Fabs(fuckinTempBox.Width()) > 10000.0f &&
+ rmt::Fabs(fuckinTempBox.Length()) > 10000.0f )
+ {
+ fuckinTempBox.low = rmt::Vector( -1.0f, -1.0f, -1.0f );
+ fuckinTempBox.high = rmt::Vector( 1.0f, 1.0f, 1.0f );
+ }
+
+ mpMatrix->Transform(fuckinTempBox.low, &box->low);
+ mpMatrix->Transform(fuckinTempBox.high, &box->high);
+}
+//========================================================================
+// InstStatEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstStatEntityDSG::GetBoundingSphere(rmt::Sphere* pSphere)
+{
+ rmt::Sphere fuckinTempSphere;
+ mpDrawstuff->GetBoundingSphere(&fuckinTempSphere);
+
+ mpMatrix->Transform(fuckinTempSphere.centre, &pSphere->centre);
+ pSphere->radius = fuckinTempSphere.radius;
+}
+
+ ///////////////////////////////////////////////////////////////////////
+ // Accessors
+ ///////////////////////////////////////////////////////////////////////
+//========================================================================
+// InstStatEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+rmt::Matrix* InstStatEntityDSG::pMatrix()
+{
+ return mpMatrix;
+}
+//========================================================================
+// InstStatEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tGeometry* InstStatEntityDSG::pGeo()
+{
+ rAssert(false);
+ return (tGeometry*)mpDrawstuff;
+}
+
+ ///////////////////////////////////////////////////////////////////////
+ // Load interface
+ ///////////////////////////////////////////////////////////////////////
+//========================================================================
+// InstStatEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstStatEntityDSG::LoadSetUp
+(
+ rmt::Matrix* ipMatrix,
+ tGeometry* ipGeo,
+ tDrawable* ipShadow
+)
+{
+ mpMatrix = ipMatrix;
+ mpDrawstuff = ipGeo;
+
+ mpDrawstuff->AddRef();
+
+ //mShaderUID = ipGeo->GetShader(0)->GetUID();
+ ipGeo->ProcessShaders(*this);
+
+ if ( ipShadow )
+ {
+ tRefCounted::Assign( mpShadowDrawable, ipShadow );
+
+ mpShadowMatrix = CreateShadowMatrix( ipMatrix->Row( 3 ) );
+ }
+}
+//========================================================================
+// InstStatEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstStatEntityDSG::LoadSetUp
+(
+ rmt::Matrix* ipMatrix,
+ tDrawable* ipGeo,
+ tDrawable* ipShadow
+)
+{
+ mpMatrix = ipMatrix;
+ mpDrawstuff = ipGeo;
+
+ mpDrawstuff->AddRef();
+ ipGeo->ProcessShaders(*this);
+
+ if ( ipShadow )
+ {
+ tRefCounted::Assign( mpShadowDrawable, ipShadow );
+
+ mpShadowMatrix = CreateShadowMatrix( ipMatrix->Row( 3 ) );
+ }
+
+}
+
+//=============================================================================
+// InstStatEntityDSG::pPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: rmt
+//
+//=============================================================================
+rmt::Vector* InstStatEntityDSG::pPosition()
+{
+ rAssert( false );
+
+ return NULL;
+}
+
+//=============================================================================
+// InstStatEntityDSG::rPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+const rmt::Vector& InstStatEntityDSG::rPosition()
+{
+ rAssert( false );
+
+ return mPosn;
+}
+
+//=============================================================================
+// InstStatEntityDSG::GetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* ipPosn )
+//
+// Return: void
+//
+//=============================================================================
+void InstStatEntityDSG::GetPosition( rmt::Vector* ipPosn )
+{
+ rmt::Sphere sphere;
+ mpDrawstuff->GetBoundingSphere(&sphere);
+ *ipPosn = sphere.centre;
+
+ ipPosn->Transform( *mpMatrix );
+}
+
+//************************************************************************
+//
+// Protected Member Functions : InstStatEntityDSG
+//
+//************************************************************************
+
+
+rmt::Matrix*
+InstStatEntityDSG::CreateShadowMatrix( const rmt::Vector& position )
+{
+
+ rmt::Matrix* pResult;
+ rmt::Matrix shadowMat;
+ if ( ComputeShadowMatrix( position, &shadowMat ) )
+ {
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ pResult = new rmt::Matrix();
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ *pResult = shadowMat;
+
+ }
+ else
+ {
+ pResult = NULL ;
+ }
+ return pResult;
+
+}
+
+bool
+InstStatEntityDSG::ComputeShadowMatrix( const rmt::Vector& in_position, rmt::Matrix* out_pMatrix )
+{
+ // Determine where our shadow casting object intersects the ground plane
+ rmt::Vector groundNormal(0,1,0);
+ rmt::Vector groundPlaneIntersectionPoint;
+
+ const float INTERSECT_TEST_RADIUS = 10.0f;
+ bool foundPlane;
+ rmt::Vector deepestIntersectPos, deepestIntersectNormal;
+
+ // Get rid of the fact that FindIntersection doesn't want a const value
+ // and I'm above casting away constness
+
+ rmt::Vector searchPosition = in_position;
+ searchPosition.y += 10.0f;
+
+ GetIntersectManager()->FindIntersection( searchPosition,
+ foundPlane,
+ groundNormal,
+ groundPlaneIntersectionPoint );
+
+ if ( foundPlane )
+ {
+ out_pMatrix->Identity();
+ out_pMatrix->FillTranslate( groundPlaneIntersectionPoint );
+ rmt::Vector worldRight( 1,0,0 );
+ rmt::Vector forward;
+ forward.CrossProduct( worldRight, groundNormal );
+ out_pMatrix->FillHeading( forward, groundNormal );
+ }
+ return foundPlane;
+
+}
+
+
+//************************************************************************
+//
+// Private Member Functions : InstStatEntityDSG
+//
+//************************************************************************
diff --git a/game/code/render/DSG/InstStatEntityDSG.h b/game/code/render/DSG/InstStatEntityDSG.h
new file mode 100644
index 0000000..e327c88
--- /dev/null
+++ b/game/code/render/DSG/InstStatEntityDSG.h
@@ -0,0 +1,82 @@
+#ifndef __InstStatEntityDSG_H__
+#define __InstStatEntityDSG_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: InstStatEntityDSG
+//
+// Description: The InstStatEntityDSG does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/06/17]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <simphysics/physicsobject.hpp>
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/DSG/StaticEntityDSG.h>
+
+//========================================================================
+//
+// Synopsis: The InstStatEntityDSG; Synopsis by Inspection.
+//
+//========================================================================
+class InstStatEntityDSG
+: public StaticEntityDSG
+{
+public:
+ InstStatEntityDSG();
+ virtual ~InstStatEntityDSG();
+
+ //////////////////////////////////////////////////////////////////////////
+ // tDrawable
+ //////////////////////////////////////////////////////////////////////////
+ void Display();
+ void GetBoundingBox(rmt::Box3D* box);
+ void GetBoundingSphere(rmt::Sphere* pSphere);
+
+ virtual rmt::Vector* pPosition();
+ virtual const rmt::Vector& rPosition();
+ virtual void GetPosition( rmt::Vector* ipPosn );
+
+ virtual void SetShader(tShader* pShader, int i)
+ {
+ }
+ ///////////////////////////////////////////////////////////////////////
+ // Accessors
+ ///////////////////////////////////////////////////////////////////////
+ rmt::Matrix* pMatrix();
+ tGeometry* pGeo();
+
+ ///////////////////////////////////////////////////////////////////////
+ // Load interface
+ ///////////////////////////////////////////////////////////////////////
+ virtual void LoadSetUp( rmt::Matrix* ipMatrix,
+ tGeometry* ipGeo,
+ tDrawable* shadow = NULL );
+ virtual void LoadSetUp( rmt::Matrix* ipMatrix,
+ tDrawable* ipGeo,
+ tDrawable* shadow = NULL );
+
+ virtual void DisplaySimpleShadow();
+ virtual int CastsShadow() { if(mpShadowDrawable != NULL ) return 2; else return 0; }
+ // Used to recompute the shadow position on the ground when the object moves
+ virtual void RecomputeShadowPosition();
+protected:
+ rmt::Matrix* mpMatrix;
+ tDrawable* mpShadowDrawable;
+ rmt::Matrix* mpShadowMatrix;
+
+ rmt::Matrix* CreateShadowMatrix( const rmt::Vector& position );
+ bool ComputeShadowMatrix( const rmt::Vector& in_position, rmt::Matrix* out_pMatrix );
+
+private:
+};
+
+#endif
diff --git a/game/code/render/DSG/InstStatPhysDSG.cpp b/game/code/render/DSG/InstStatPhysDSG.cpp
new file mode 100644
index 0000000..393b730
--- /dev/null
+++ b/game/code/render/DSG/InstStatPhysDSG.cpp
@@ -0,0 +1,391 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: InstStatPhysDSG.cpp
+//
+// Description: Implementation for InstStatPhysDSG class.
+//
+// History: Implemented --Devin [6/17/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/geometry.hpp>
+#include <p3d/utility.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <simcollision/collisionvolume.hpp>
+#include <p3d/billboardobject.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/InstStatPhysDSG.h>
+#include <memory/srrmemory.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : InstStatPhysDSG Interface
+//
+//************************************************************************
+//========================================================================
+// InstStatPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+InstStatPhysDSG::InstStatPhysDSG()
+{
+}
+//========================================================================
+// InstStatPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+InstStatPhysDSG::~InstStatPhysDSG()
+{
+BEGIN_PROFILE( "InstStatPhysDSG Destroy" );
+ if(mpGeo)
+ {
+ mpGeo->Release();
+ }
+END_PROFILE( "InstStatPhysDSG Destroy" );
+}
+//========================================================================
+// InstStatPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstStatPhysDSG::Display()
+{
+#ifdef PROFILER_ENABLED
+ char profileName[] = " InstStatPhysDSG Display";
+#endif
+ DSG_BEGIN_PROFILE(profileName)
+ if(CastsShadow())
+ {
+ BillboardQuadManager::Enable();
+ }
+ p3d::pddi->PushMultMatrix(PDDI_MATRIX_MODELVIEW, &mMatrix);
+ mpGeo->Display();
+ p3d::pddi->PopMatrix(PDDI_MATRIX_MODELVIEW);
+ if(CastsShadow())
+ {
+ BillboardQuadManager::Disable();
+ DisplaySimpleShadow();
+ }
+ DSG_END_PROFILE(profileName)
+}
+//========================================================================
+// InstStatPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstStatPhysDSG::GetBoundingBox(rmt::Box3D* box)
+{
+ // the box is in world space already:
+
+ box->low = mBBox.low;
+ box->high = mBBox.high;
+
+
+}
+//========================================================================
+// InstStatPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstStatPhysDSG::GetBoundingSphere(rmt::Sphere* pSphere)
+{
+ // the sphere is in world space already:
+
+ pSphere->centre = mSphere.centre;
+ pSphere->radius = mSphere.radius;
+}
+
+
+//=============================================================================
+// InstStatPhysDSG::OnSetSimState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( sim::SimState* ipSimState )
+//
+// Return: void
+//
+//=============================================================================
+void InstStatPhysDSG::OnSetSimState( sim::SimState* ipSimState )
+{
+ tRefCounted::Assign( mpSimStateObj, ipSimState );
+
+ // ok for this to have the same ai ref I guess
+ //mpSimStateObj->mAIRefIndex = StaticPhysDSG::GetAIRef();
+ mpSimStateObj->mAIRefIndex = this->GetAIRef();
+
+ // maybe a bit useless to have this in another method, but oh well...
+ SetInternalState();
+}
+
+
+//=============================================================================
+// InstStatPhysDSG::SetInternalState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: virtual
+//
+//=============================================================================
+void InstStatPhysDSG::SetInternalState()
+{
+
+
+
+ mPosn = mpSimStateObj->GetPosition();
+
+ // this box will be in world space:
+ mpGeo->GetBoundingBox(&mBBox);
+ mBBox.low.Transform(mMatrix);
+ mBBox.high.Transform(mMatrix);
+
+ mpGeo->GetBoundingSphere(&mSphere);
+ mSphere.centre.Transform(mMatrix);
+
+/*
+ mBBox.low = mPosn;
+ mBBox.high = mPosn;
+ mBBox.high += mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+ mBBox.low -= mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+
+
+ mSphere.centre.Sub(mBBox.high,mBBox.low);
+ mSphere.centre *= 0.5f;
+ mSphere.centre.Add(mBBox.low);
+ mSphere.radius = mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mSphereRadius;
+ */
+
+
+
+ /*
+ this is what InstDynaPhys does
+
+
+
+ mPosn = mpSimStateObj->GetPosition();
+ // set up box and sphere
+ //
+ // note, unlike the StaticPhysDSG, this box and sphere are in local space
+
+ rmt::Vector center = mpPhysObj->GetExternalCMOffset();
+ float radius = mpSimStateObj->GetSphereRadius();
+
+ mBBox.low = center;
+ mBBox.high = center;
+ mBBox.high += mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+ mBBox.low -= mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+
+ mSphere.centre = center;
+ mSphere.radius = radius;
+
+ */
+
+
+
+
+
+
+ /*
+ this is what StaticPhysDSG does:
+
+
+ mPosn = mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mPosition;
+
+ mBBox.low = mPosn;
+ mBBox.high = mPosn;
+ mBBox.high += mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+ mBBox.low -= mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+
+ mSphere.centre.Sub(mBBox.high,mBBox.low);
+ mSphere.centre *= 0.5f;
+ mSphere.centre.Add(mBBox.low);
+ mSphere.radius = mpS
+
+ */
+
+
+
+
+}
+
+ ///////////////////////////////////////////////////////////////////////
+ // Accessors
+ ///////////////////////////////////////////////////////////////////////
+//========================================================================
+// InstStatPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+sim::SimState* InstStatPhysDSG::pSimStateObj()
+{
+ return mpSimStateObj;
+}
+//========================================================================
+// InstStatPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+rmt::Matrix* InstStatPhysDSG::pMatrix()
+{
+ return &mMatrix;
+}
+//========================================================================
+// InstStatPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tGeometry* InstStatPhysDSG::pGeo()
+{
+ return mpGeo;
+}
+
+ ///////////////////////////////////////////////////////////////////////
+ // Load interface
+ ///////////////////////////////////////////////////////////////////////
+//========================================================================
+// InstStatPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstStatPhysDSG::LoadSetUp
+(
+ sim::SimState* ipSimStateObj,
+ CollisionAttributes* ipCollAttr,
+ const rmt::Matrix& iMatrix,
+ tGeometry* ipGeo
+)
+{
+
+ mMatrix = iMatrix;
+ mpGeo = ipGeo;
+ mpGeo->AddRef();
+
+ SetCollisionAttributes(ipCollAttr);
+
+ // recall: this is the non-virtual method
+ SetSimState(ipSimStateObj);
+
+// mShaderUID = ipGeo->GetShader(0)->GetUID();
+ ipGeo->ProcessShaders(*this);
+}
+
+InstStatPhysDSG* InstStatPhysDSG::Clone(const char* Name, const rmt::Matrix& iMatrix) const
+{
+ InstStatPhysDSG* c = new InstStatPhysDSG();
+ rAssert(c);
+ c->SetName(Name);
+ c->mMatrix = iMatrix;
+ c->mpGeo = this->mpGeo;
+ c->mpGeo->AddRef();
+ c->SetCollisionAttributes(this->GetCollisionAttributes());
+ sim::SimState* simState = sim::SimState::CreateSimState(this->GetSimState());
+ simState->SetControl(sim::simAICtrl);
+ simState->GetCollisionObject()->SetIsStatic(false);
+ simState->GetCollisionObject()->SetManualUpdate(false);
+ simState->SetTransform(iMatrix);
+ simState->GetCollisionObject()->Update();
+ simState->GetCollisionObject()->SetIsStatic(true);
+ simState->GetCollisionObject()->SetManualUpdate(true);
+ c->SetSimState(simState);
+ mpGeo->ProcessShaders(*c);
+ c->SetShadow(this->mpShadow);
+ return c;
+}
+//************************************************************************
+//
+// Protected Member Functions : InstStatPhysDSG
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : InstStatPhysDSG
+//
+//************************************************************************
diff --git a/game/code/render/DSG/InstStatPhysDSG.h b/game/code/render/DSG/InstStatPhysDSG.h
new file mode 100644
index 0000000..27c50b0
--- /dev/null
+++ b/game/code/render/DSG/InstStatPhysDSG.h
@@ -0,0 +1,84 @@
+#ifndef __InstStatPhysDSG_H__
+#define __InstStatPhysDSG_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: InstStatPhysDSG
+//
+// Description: The InstStatPhysDSG does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/06/17]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <simphysics/physicsobject.hpp>
+
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/DSG/StaticPhysDSG.h>
+
+class tGeometry;
+
+//========================================================================
+//
+// Synopsis: The InstStatPhysDSG; Synopsis by Inspection.
+//
+//========================================================================
+class InstStatPhysDSG
+: public StaticPhysDSG
+{
+public:
+ InstStatPhysDSG();
+ ~InstStatPhysDSG();
+
+ //////////////////////////////////////////////////////////////////////////
+ // tDrawable
+ //////////////////////////////////////////////////////////////////////////
+ void Display();
+ void GetBoundingBox(rmt::Box3D* box);
+ void GetBoundingSphere(rmt::Sphere* pSphere);
+
+ virtual void OnSetSimState(sim::SimState* ipCollObj);
+
+ ///////////////////////////////////////////////////////////////////////
+ // Accessors
+ ///////////////////////////////////////////////////////////////////////
+ sim::SimState* pSimStateObj();
+ rmt::Matrix* pMatrix();
+ tGeometry* pGeo();
+
+ ///////////////////////////////////////////////////////////////////////
+ // Load interface
+ ///////////////////////////////////////////////////////////////////////
+ void LoadSetUp( sim::SimState* ipSimStateObj,
+ CollisionAttributes* ipCollAttr,
+ const rmt::Matrix& iMatrix,
+ tGeometry* ipGeo );
+ InstStatPhysDSG* Clone(const char* Name, const rmt::Matrix& iMatrix) const;
+
+protected:
+
+ virtual void SetInternalState();
+
+ rmt::Matrix mMatrix;
+ tGeometry* mpGeo;
+
+ /*
+ inherited data:
+
+ rmt::Box3D mBBox;
+ rmt::Sphere mSphere;
+ rmt::Vector mPosn;
+ sim::SimState* mpSimStateObj;
+ */
+
+private:
+};
+
+#endif
diff --git a/game/code/render/DSG/IntersectDSG.cpp b/game/code/render/DSG/IntersectDSG.cpp
new file mode 100644
index 0000000..1a118f8
--- /dev/null
+++ b/game/code/render/DSG/IntersectDSG.cpp
@@ -0,0 +1,1000 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: IntersectDSG.cpp
+//
+// Description: Implementation for IntersectDSG class.
+//
+// History: Implemented --Devin [5/6/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/geometry.hpp>
+#include <p3d/primgroup.hpp>
+#include <p3d/vertexlist.hpp>
+#include <raddebug.hpp>
+#include <pddi/pddiext.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/IntersectDSG.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+int* IntersectDSG::mspIndexData = NULL;
+unsigned char* IntersectDSG::mspTerrainData = NULL;
+rmt::Vector* IntersectDSG::mspVertexData = NULL;
+rmt::Vector* IntersectDSG::mspNormalData = NULL;
+bool IntersectDSG::msbInScratchPad= false;
+
+//************************************************************************
+//
+// Public Member Functions : IntersectDSG Interface
+//
+//************************************************************************
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+IntersectDSG::IntersectDSG( tGeometry* ipGeometry )
+{
+ GenIDSG(ipGeometry);
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+IntersectDSG::IntersectDSG()
+{
+}
+
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+IntersectDSG::~IntersectDSG()
+{
+}
+//========================================================================
+// intersectdsg::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+//#ifndef RAD_RELEASE
+inline void IntersectDSG::DrawTri(rmt::Vector* ipTriPts, tColour iColour)
+{
+ pddiPrimStream* stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 4);
+ stream->Colour(iColour);
+ stream->Coord(ipTriPts[0].x, ipTriPts[0].y, ipTriPts[0].z);
+ stream->Colour(iColour);
+ stream->Coord(ipTriPts[1].x, ipTriPts[1].y, ipTriPts[1].z);
+ stream->Colour(iColour);
+ stream->Coord(ipTriPts[2].x, ipTriPts[2].y, ipTriPts[2].z);
+ stream->Colour(iColour);
+ stream->Coord(ipTriPts[0].x, ipTriPts[0].y, ipTriPts[0].z);
+ p3d::pddi->EndPrims(stream);
+}
+//#endif
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::Display()
+{
+ //Currently unsupported function. Contact Devin.
+ // rAssert(false);
+//#ifndef RAD_RELEASE
+ rmt::Vector triPts[3], triNorm;
+ for(int i = nTris()-1; i>-1; i--)
+ {
+ mTri(i,triPts,triNorm);
+ DrawTri(triPts,tColour(255,255,255));
+ }
+//#endif
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+const rmt::Vector& IntersectDSG::rPosition()
+{
+ return mPosn;
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+rmt::Vector* IntersectDSG::pPosition()
+{
+ return &mPosn;
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::GetPosition( rmt::Vector* ipPosn )
+{
+ *ipPosn = mPosn;
+}
+//========================================================================
+// intersectdsg::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::IntoTheVoid_WithGoFastaStripes()
+{
+ mspVertexData = mTriPts.mpData;
+ mspNormalData = mTriNorms.mpData;
+ mspIndexData = mTriIndices.mpData;
+ mspTerrainData = mTerrainType.mpData;
+ msbInScratchPad = false;
+#ifdef RAD_PS2
+ static pddiExtPS2Control* ps2Control = (pddiExtPS2Control*)p3d::pddi->GetExtension(PDDI_EXT_PS2_CONTROL);
+
+ //////////////////////////////////////////////////////////////////////////
+ // If we can fit on the scratchpad used by the mFIFO
+ //////////////////////////////////////////////////////////////////////////
+ if( ( sizeof(*mTriPts.mpData)*mTriPts.mUseSize
+ + sizeof(*mTriNorms.mpData)*mTriNorms.mUseSize
+ + sizeof(*mTriIndices.mpData)*mTriIndices.mUseSize
+ + sizeof(*mTerrainType.mpData)*mTerrainType.mUseSize ) < 8192 )
+ {
+ msbInScratchPad = true;
+ ps2Control->SyncScratchPad();
+
+
+ char* pScratchPad = (char*)0x70000000;
+
+ if(mTriPts.mUseSize>0)
+ {
+ memcpy(pScratchPad, mTriPts.mpData, mTriPts.mUseSize*sizeof(*mTriPts.mpData) );
+ mTriPts.mpData = (rmt::Vector*)pScratchPad;
+ pScratchPad+=mTriPts.mUseSize*sizeof(*mTriPts.mpData);
+ for(int i=mTriPts.mUseSize-1; i>-1; i--)
+ {
+ ((int&)(mTriPts[i].y)) = IntersectDSG::UNINIT_PT;
+ }
+ }
+
+ if(mTriNorms.mUseSize>0)
+ {
+ memcpy(pScratchPad, mTriNorms.mpData, mTriNorms.mUseSize*sizeof(*mTriNorms.mpData) );
+ mTriNorms.mpData = (rmt::Vector*)pScratchPad;
+ pScratchPad+=mTriNorms.mUseSize*sizeof(*mTriNorms.mpData);
+ }
+
+ if(mTriIndices.mUseSize>0)
+ {
+ memcpy(pScratchPad, mTriIndices.mpData, mTriIndices.mUseSize*sizeof(*mTriIndices.mpData) );
+ mTriIndices.mpData = (int*)pScratchPad;
+ pScratchPad+=mTriIndices.mUseSize*sizeof(*mTriIndices.mpData);
+ }
+
+ if(mTerrainType.mUseSize>0)
+ {
+ memcpy(pScratchPad, mTerrainType.mpData, mTerrainType.mUseSize*sizeof(*mTerrainType.mpData) );
+ mTerrainType.mpData = (unsigned char*)pScratchPad;
+ pScratchPad+=mTerrainType.mUseSize*sizeof(*mTerrainType.mpData);
+ }
+
+ //if(((int)pScratchPad)-0x70000000 > 8192)
+ // rReleasePrintf("***ack! Overwriting ScratchPad memory for something non mFIFO\n" );
+ }
+#endif
+}
+//========================================================================
+// intersectdsg::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::OutOfTheVoid_WithGoFastaStripes()
+{
+ mTriPts.mpData = mspVertexData;
+ mTriNorms.mpData = mspNormalData;
+ mTriIndices.mpData = mspIndexData;
+ mTerrainType.mpData = mspTerrainData;
+ msbInScratchPad = false;
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+int IntersectDSG::mFlatTriFast
+(
+ int& orTriNum,
+ rmt::Vector* iopTriPts,
+ rmt::Vector& orTriNorm
+)
+{
+ const float PT_EPSILON = 0.01f;
+ rmt::Vector posnRef = iopTriPts[0];
+ int Mask = IntersectDSG::UNINIT_PT;
+ rReleaseAssert(mTriIndices.mUseSize > 0);
+
+ rAssert(mTriIndices.IsSetUp());
+
+ int i, j;
+
+ if(msbInScratchPad)
+ {
+ for(i=orTriNum; Mask!=IntersectDSG::FOUND_PT && i>-1; i--)
+ {
+ Mask = IntersectDSG::UNINIT_PT;
+ //////////////////////////////////////////////////////////////////////////
+ // Intentional Copy n Paste is done to ensure inlining
+ //////////////////////////////////////////////////////////////////////////
+ j = mTriIndices[i*3];
+
+ int* pPtFlags = &((int&)(mTriPts[j].y));//rPtFlags = Mask;
+ if( *pPtFlags == IntersectDSG::UNINIT_PT )//!(rPtFlags & IntersectDSG::INIT_PT) )
+ {
+ //Initialise Point; intentional overlap. See Mask.
+ if( mTriPts[j].x <= (posnRef.x + PT_EPSILON) ) (*pPtFlags) |= IntersectDSG::X_LT_PT;
+ if( mTriPts[j].x >= (posnRef.x - PT_EPSILON) ) (*pPtFlags) |= IntersectDSG::X_GT_PT;
+ if( mTriPts[j].z <= (posnRef.z + PT_EPSILON) ) (*pPtFlags) |= IntersectDSG::Z_LT_PT;
+ if( mTriPts[j].z >= (posnRef.z - PT_EPSILON) ) (*pPtFlags) |= IntersectDSG::Z_GT_PT;
+ (*pPtFlags) |= IntersectDSG::INIT_PT;
+ }
+ Mask |= (*pPtFlags);
+
+ j = mTriIndices[i*3+1];
+ pPtFlags = &((int&)(mTriPts[j].y));//rPtFlags = Mask;
+ if( (*pPtFlags) == IntersectDSG::UNINIT_PT )//!(rPtFlags & IntersectDSG::INIT_PT) )
+ {
+ //Initialise Point; intentional overlap. See Mask.
+ if( mTriPts[j].x <= (posnRef.x + PT_EPSILON) ) (*pPtFlags) |= IntersectDSG::X_LT_PT;
+ if( mTriPts[j].x >= (posnRef.x - PT_EPSILON) ) (*pPtFlags) |= IntersectDSG::X_GT_PT;
+ if( mTriPts[j].z <= (posnRef.z + PT_EPSILON) ) (*pPtFlags) |= IntersectDSG::Z_LT_PT;
+ if( mTriPts[j].z >= (posnRef.z - PT_EPSILON) ) (*pPtFlags) |= IntersectDSG::Z_GT_PT;
+ (*pPtFlags) |= IntersectDSG::INIT_PT;
+ }
+ Mask |= (*pPtFlags);
+
+ j = mTriIndices[i*3+2];
+ pPtFlags = &((int&)(mTriPts[j].y));//rPtFlags = Mask;
+ if( (*pPtFlags) == IntersectDSG::UNINIT_PT )//!(rPtFlags & IntersectDSG::INIT_PT) )
+ {
+ //Initialise Point; intentional overlap. See Mask.
+ if( mTriPts[j].x <= (posnRef.x + PT_EPSILON) ) (*pPtFlags) |= IntersectDSG::X_LT_PT;
+ if( mTriPts[j].x >= (posnRef.x - PT_EPSILON) ) (*pPtFlags) |= IntersectDSG::X_GT_PT;
+ if( mTriPts[j].z <= (posnRef.z + PT_EPSILON) ) (*pPtFlags) |= IntersectDSG::Z_LT_PT;
+ if( mTriPts[j].z >= (posnRef.z - PT_EPSILON) ) (*pPtFlags) |= IntersectDSG::Z_GT_PT;
+ (*pPtFlags) |= IntersectDSG::INIT_PT;
+ }
+ Mask |= (*pPtFlags);
+
+ if(Mask==IntersectDSG::FOUND_PT)
+ {
+ j=mTriIndices[i*3]; *iopTriPts = mTriPts[j]; iopTriPts->y = mspVertexData[j].y;
+ j=mTriIndices[i*3+1]; *(iopTriPts+1) = mTriPts[j]; (iopTriPts+1)->y = mspVertexData[j].y;
+ j=mTriIndices[i*3+2]; *(iopTriPts+2) = mTriPts[j]; (iopTriPts+2)->y = mspVertexData[j].y;
+ orTriNum=i;
+
+ orTriNorm = mTriNorms[orTriNum];
+
+ if( mTerrainType.IsSetUp() )
+ {
+ return mTerrainType[orTriNum];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+ }
+ else //NotInScratchPad
+ {
+ for(i=orTriNum; Mask!=IntersectDSG::FOUND_PT && i>-1; i--)
+ {
+ Mask |= IntersectDSG::INIT_PT;
+ //////////////////////////////////////////////////////////////////////////
+ // Intentional Copy n Paste is done to ensure inlining
+ //////////////////////////////////////////////////////////////////////////
+ j = mTriIndices[i*3];
+ {
+ if( mTriPts[j].x <= (posnRef.x + PT_EPSILON) ) Mask |= IntersectDSG::X_LT_PT;
+ if( mTriPts[j].x >= (posnRef.x - PT_EPSILON) ) Mask |= IntersectDSG::X_GT_PT;
+ if( mTriPts[j].z <= (posnRef.z + PT_EPSILON) ) Mask |= IntersectDSG::Z_LT_PT;
+ if( mTriPts[j].z >= (posnRef.z - PT_EPSILON) ) Mask |= IntersectDSG::Z_GT_PT;
+ }
+
+ j = mTriIndices[i*3+1];
+ {
+ //Initialise Point; intentional overlap. See Mask.
+ if( mTriPts[j].x <= (posnRef.x + PT_EPSILON) ) Mask |= IntersectDSG::X_LT_PT;
+ if( mTriPts[j].x >= (posnRef.x - PT_EPSILON) ) Mask |= IntersectDSG::X_GT_PT;
+ if( mTriPts[j].z <= (posnRef.z + PT_EPSILON) ) Mask |= IntersectDSG::Z_LT_PT;
+ if( mTriPts[j].z >= (posnRef.z - PT_EPSILON) ) Mask |= IntersectDSG::Z_GT_PT;
+ }
+
+ j = mTriIndices[i*3+2];
+ {
+ //Initialise Point; intentional overlap. See Mask.
+ if( mTriPts[j].x <= (posnRef.x + PT_EPSILON) ) Mask |= IntersectDSG::X_LT_PT;
+ if( mTriPts[j].x >= (posnRef.x - PT_EPSILON) ) Mask |= IntersectDSG::X_GT_PT;
+ if( mTriPts[j].z <= (posnRef.z + PT_EPSILON) ) Mask |= IntersectDSG::Z_LT_PT;
+ if( mTriPts[j].z >= (posnRef.z - PT_EPSILON) ) Mask |= IntersectDSG::Z_GT_PT;
+ }
+
+ if(Mask==IntersectDSG::FOUND_PT)
+ {
+ j=mTriIndices[i*3]; *iopTriPts = mTriPts[j];
+ j=mTriIndices[i*3+1]; *(iopTriPts+1) = mTriPts[j];
+ j=mTriIndices[i*3+2]; *(iopTriPts+2) = mTriPts[j];
+ orTriNum=i;
+
+ orTriNorm = mTriNorms[orTriNum];
+
+ if( mTerrainType.IsSetUp() )
+ {
+ return mTerrainType[orTriNum];
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+ }
+ return -1;
+ //rTuneAssert(orTriNum==i+1);
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+int IntersectDSG::mTri
+(
+ int inTri,
+ rmt::Vector* opTriPts,
+ rmt::Vector& orTriNorm//,
+ //rmt::Vector& orTriCtr,
+ //int* opTerrainType
+)
+{
+ //rReleaseAssert( ((((int)mspVertexData)&0x70000000)==0)
+ // && ((((int)mspNormalData)&0x70000000)==0)
+ // && ((((int)mspIndexData)&0x70000000)==0)
+ // && ((((int)mspTerrainData)&0x70000000)==0));
+
+ if( mTriIndices.mUseSize > 0 )
+ {
+ rAssert(mTriIndices.IsSetUp());
+
+ *opTriPts = mTriPts[mTriIndices[inTri*3]];
+ *(opTriPts+1) = mTriPts[mTriIndices[inTri*3+1]];
+ *(opTriPts+2) = mTriPts[mTriIndices[inTri*3+2]];
+
+ orTriNorm = mTriNorms[inTri];
+ //orTriCtr = mTriCentroids[inTri];
+ //if( opTerrainType )
+ {
+ if( mTerrainType.IsSetUp() )
+ {
+ //*opTerrainType = mTerrainType[ inTri ];
+ return mTerrainType[ inTri ];
+ }
+ else
+ {
+ //*opTerrainType = 0;
+ return 0;
+ }
+ }
+ //return 0.0f;// mTriRadius[inTri];
+ }
+ else
+ {
+ rAssert(mTriPts.IsSetUp());
+
+ *opTriPts = mTriPts[inTri*3];
+ *(opTriPts+1) = mTriPts[inTri*3+1];
+ *(opTriPts+2) = mTriPts[inTri*3+2];
+
+ orTriNorm = mTriNorms[inTri];
+ //orTriCtr = mTriCentroids[inTri];
+ //if( opTerrainType )
+ {
+ if( mTerrainType.IsSetUp() )
+ {
+ //*opTerrainType = mTerrainType[ inTri ];
+ return mTerrainType[ inTri ];
+ }
+ else
+ {
+ //*opTerrainType = 0;
+ return 0;
+ }
+ }
+ //return mTriRadius[inTri];
+ }
+
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+int IntersectDSG::nTris()
+{
+ if( mTriIndices.mUseSize > 0 )
+ {
+ rAssert(mTriIndices.IsSetUp());
+
+ return mTriIndices.mUseSize/3;
+ }
+ else
+ {
+ rAssert(mTriPts.IsSetUp());
+
+ return mTriPts.mUseSize / 3;
+ }
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::GetBoundingBox(rmt::Box3D* box)
+{
+ *box = mBox3D;
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::GetBoundingSphere(rmt::Sphere* sphere)
+{
+ *sphere = mSphere;
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::SetBoundingBox
+(
+ float x1, float y1, float z1,
+ float x2, float y2, float z2
+)
+{
+ mBox3D.low.Set(x1,y1,z1);
+ mBox3D.high.Set(x2,y2,z2);
+
+ mPosn.Sub(mBox3D.high, mBox3D.low);
+ mPosn *= 0.5f;
+ mPosn.Add(mBox3D.low);
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::SetBoundingSphere(float x, float y, float z, float radius)
+{
+ mSphere.centre.Set(x,y,z);
+ mSphere.radius = radius;
+
+ mPosn.Set(x,y,z);
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+int IntersectDSG::GetNumPrimGroup()
+{
+ return mnPrimGroups;
+}
+
+//************************************************************************
+//
+// Protected Member Functions : IntersectDSG
+//
+//************************************************************************
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::GenIDSG( tGeometry* ipGeometry )
+{
+ int i;
+
+ ipGeometry->GetBoundingBox( &mBox3D );
+ ipGeometry->GetBoundingSphere( &mSphere );
+
+ SetUID(ipGeometry->GetUID());
+ CopyName(ipGeometry);
+ mnPrimGroups = ipGeometry->GetNumPrimGroup();
+
+ for( i=0; i<mnPrimGroups; i++ )
+ {
+ switch( ipGeometry->GetPrimGroup(i)->GetPrimType() )
+ {
+ case PDDI_PRIM_TRIANGLES:
+ PreParseTris((tPrimGroupStreamed*)ipGeometry->GetPrimGroup(i));
+ break;
+ case PDDI_PRIM_TRISTRIP:
+ PreParseTriStrips((tPrimGroupStreamed*)ipGeometry->GetPrimGroup(i));
+ break;
+ default:
+ rAssert(false);
+ break;
+ }
+ }
+
+ DoAllAllocs();
+
+// for( int i=ipGeometry->GetNumPrimGroup()-1; i>-1; i-- )
+ for( i=0; i<mnPrimGroups; i++ )
+ {
+ switch( ipGeometry->GetPrimGroup(i)->GetPrimType() )
+ {
+ case PDDI_PRIM_TRIANGLES:
+ ParseTris((tPrimGroupStreamed*)ipGeometry->GetPrimGroup(i));
+ break;
+ case PDDI_PRIM_TRISTRIP:
+ ParseTriStrips((tPrimGroupStreamed*)ipGeometry->GetPrimGroup(i));
+ break;
+ default:
+ rAssert(false);
+ break;
+ }
+ }
+ CalcAllFields();
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::PreParseTris( tPrimGroupStreamed* ipPrimGroup )
+{
+#if 0
+ int n = ipPrimGroup->GetVertexCount();
+ mTriPts.Reserve(n);
+ int m = ipPrimGroup->GetNumIndices();
+
+ // don't reserve indices if this is de-indexed geometry
+ if (m > 0)
+ {
+ mTriIndices.Reserve(m);
+ m=m/3;
+ mTriNorms.Reserve(m);
+ mTriCentroids.Reserve(m);
+ mTriRadius.Reserve(m);
+ }
+ else
+ {
+ n=n/3;
+ mTriNorms.Reserve(n);
+ mTriCentroids.Reserve(n);
+ mTriRadius.Reserve(n);
+ }
+#endif
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::PreParseTriStrips( tPrimGroupStreamed* ipPrimGroup )
+{
+ //Currently unsupported feature; contact Devin
+ rAssert(false);
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::DoAllAllocs()
+{
+#if 0
+ mTriIndices.Allocate();
+ mTriPts.Allocate();
+ mTriNorms.Allocate();
+ mTriCentroids.Allocate();
+ mTriRadius.Allocate();
+#endif
+}
+
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::ParseTris( tPrimGroupStreamed* ipPrimGroup )
+{
+#if 0
+ int nVerts = ipPrimGroup->GetVertexCount();
+ int nStart = mTriPts.mUseSize;
+
+ //mTriPts.AddUse(nVerts);
+
+ rmt::Vector* pVerts = ipPrimGroup->GetVertexList()->GetPositions();
+
+
+ //memcpy(mTriPts.mpData+nStart, pVerts, sizeof(rmt::Vector)*nVerts);
+ for( int i=0; i<nVerts; i++ )
+ {
+ mTriPts.Add(*(pVerts+i));
+ }
+ //////////////////////////////////////////////////////////////////////////
+ int nIndices = ipPrimGroup->GetNumIndices();
+ if( nIndices > 0 )
+ {
+ rAssert(nStart+nVerts<=mTriIndices.mSize);
+
+ // nStart = mTriIndices.mUseSize;
+ // mTriIndices.AddUse(nVerts);
+
+ unsigned short* pIndices = ipPrimGroup->GetIndices();
+
+ rAssert(nStart+nIndices<=mTriIndices.mSize);
+
+ // memcpy(mTriIndices.mpData+nStart, pIndices, sizeof(unsigned short)*nVerts);
+ // for( int i=nVerts-1; i>-1; i-- )
+ for( int i=0; i<nIndices; i++ )
+ {
+ unsigned long FuckU_MS = (*(pIndices+i))+(nStart);
+ mTriIndices.Add( FuckU_MS );
+ }
+
+ }
+#endif
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::ParseTriStrips( tPrimGroupStreamed* ipPrimGroup )
+{
+ //Currently unsupported feature; contact Devin
+ rAssert(false);
+}
+//========================================================================
+// IntersectDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectDSG::CalcAllFields()
+{
+#if 0
+ rmt::Vector tmpA, tmpB;
+ rmt::Vector *pPt0, *pPt1, *pPt2;
+
+ if(mTriIndices.mUseSize > 0) //Indexed Geometry
+ {
+ mTriNorms.AddUse( mTriIndices.mUseSize/3);
+ mTriCentroids.AddUse(mTriIndices.mUseSize/3);
+ mTriRadius.AddUse( mTriIndices.mUseSize/3);
+
+// for( int i=mTriIndices.mUseSize-1; i>-1; i-=3 )
+ for( int i=0; i<mTriIndices.mUseSize; i+=3 )
+ {
+ pPt0 = &mTriPts[mTriIndices[i]];
+ pPt1 = &mTriPts[mTriIndices[i+1]];
+ pPt2 = &mTriPts[mTriIndices[i+2]];
+
+ tmpA.Sub( *pPt1, *pPt0 );
+ tmpB.Sub( *pPt2, *pPt0 );
+
+ mTriNorms[i/3].CrossProduct( tmpA, tmpB );
+ mTriNorms[i/3].Normalize();
+/*
+ if( mTriNorms[i/3].y < 0.1f )
+ {
+ rDebugPrintf("Warning: -y in Terrain Intersect Normal\n");
+ mTriNorms[i/3].Scale(-1.0f);
+// mTriNorms[i/3].Set(0.0f,1.0f,0.0f);
+ }
+ */
+ tmpA.Add( *pPt0, *pPt1);
+ tmpA.Add( *pPt2 );
+ tmpA.Scale( 0.33333333f );
+
+ mTriCentroids[i/3] = tmpA;
+
+ tmpA.Sub( *pPt2, tmpA );
+ tmpB.Sub( *pPt1, mTriCentroids[i/3] );
+
+ if( tmpB.MagnitudeSqr() > tmpA.MagnitudeSqr() )
+ tmpA = tmpB;
+
+ tmpB.Sub( *pPt0, mTriCentroids[i/3] );
+
+ if( tmpB.MagnitudeSqr() > tmpA.MagnitudeSqr() )
+ tmpA = tmpB;
+
+ mTriRadius[i/3] = tmpA.Magnitude();
+ }
+ }
+ else //De-indexed Geometry
+ {
+ mTriNorms.AddUse( mTriPts.mUseSize/3);
+ mTriCentroids.AddUse(mTriPts.mUseSize/3);
+ mTriRadius.AddUse( mTriPts.mUseSize/3);
+
+ for( int i=0; i < mTriPts.mUseSize; i += 3 )
+ {
+ pPt0 = &mTriPts[i];
+ pPt1 = &mTriPts[i+1];
+ pPt2 = &mTriPts[i+2];
+
+ tmpA.Sub( *pPt1, *pPt0 );
+ tmpB.Sub( *pPt2, *pPt0 );
+
+ mTriNorms[i/3].CrossProduct( tmpA, tmpB );
+ mTriNorms[i/3].Normalize();
+
+ tmpA.Add( *pPt0, *pPt1);
+ tmpA.Add( *pPt2 );
+ tmpA.Scale( 0.33333333f );
+
+ mTriCentroids[i/3] = tmpA;
+
+ tmpA.Sub( *pPt2, tmpA );
+ tmpB.Sub( *pPt1, mTriCentroids[i/3] );
+
+ if( tmpB.MagnitudeSqr() > tmpA.MagnitudeSqr() )
+ tmpA = tmpB;
+
+ tmpB.Sub( *pPt0, mTriCentroids[i/3] );
+
+ if( tmpB.MagnitudeSqr() > tmpA.MagnitudeSqr() )
+ tmpA = tmpB;
+
+ mTriRadius[i/3] = tmpA.Magnitude();
+
+/* tmpA.Sub( mTriPts[i+1], mTriPts[i] );
+ tmpB.Sub( mTriPts[i+2], mTriPts[i] );
+
+ mTriNorms[i/3].CrossProduct( tmpA, tmpB );
+
+ tmpA.Add( mTriPts[i], mTriPts[i+1] );
+ tmpA.Add( mTriPts[i+2] );
+ tmpA.Scale( 0.33333333f );
+
+ mTriCentroids[i/3] = tmpA;
+
+ tmpA.Sub( mTriPts[i+2], tmpA );
+ tmpB.Sub( mTriPts[i+1], mTriCentroids[i/3] );
+
+ if( tmpB.MagnitudeSqr() < tmpA.MagnitudeSqr() )
+ tmpA = tmpB;
+
+ tmpB.Sub( mTriPts[i], mTriCentroids[i/3] );
+
+ if( tmpB.MagnitudeSqr() < tmpA.MagnitudeSqr() )
+ tmpA = tmpB;
+
+ mTriRadius[i/3] = tmpA.MagnitudeSqr();*/
+ }
+ }
+#endif
+}
+
+//************************************************************************
+//
+// Private Member Functions : IntersectDSG
+//
+//************************************************************************
diff --git a/game/code/render/DSG/IntersectDSG.h b/game/code/render/DSG/IntersectDSG.h
new file mode 100644
index 0000000..af6fe8f
--- /dev/null
+++ b/game/code/render/DSG/IntersectDSG.h
@@ -0,0 +1,116 @@
+#ifndef __IntersectDSG_H__
+#define __IntersectDSG_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: IntersectDSG
+//
+// Description: The IntersectDSG does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/06]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <p3d/p3dtypes.hpp>
+#include <p3d/geometry.hpp>
+#include <p3d/primgroup.hpp>
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/DSG/IEntityDSG.h>
+#include <render/Culling/ReserveArray.h>
+
+//========================================================================
+//
+// Synopsis: The IntersectDSG; Synopsis by Inspection.
+//
+//========================================================================
+class IntersectDSG : public IEntityDSG
+{
+public:
+ IntersectDSG();
+ IntersectDSG( tGeometry* ipGeometry );
+ ~IntersectDSG();
+
+ void Display();
+//#ifndef RAD_RELEASE
+ inline void DrawTri(rmt::Vector* ipTriPts, tColour iColour);
+//#endif
+ int mFlatTriFast( int& orTriNum, rmt::Vector* iopTriPts, rmt::Vector& orTriNorm );
+ int mTri( int inTri, rmt::Vector* opTriPts, rmt::Vector& orTriNorm );
+ int nTris();
+
+ virtual void GetBoundingBox(rmt::Box3D* box);
+ virtual void GetBoundingSphere(rmt::Sphere* sphere);
+ virtual void SetBoundingBox( float x1, float y1, float z1,
+ float x2, float y2, float z2);
+ virtual void SetBoundingSphere(float x, float y, float z, float radius);
+
+ virtual rmt::Vector* pPosition();
+ virtual const rmt::Vector& rPosition();
+ virtual void GetPosition( rmt::Vector* ipPosn );
+
+ virtual int GetNumPrimGroup();
+
+ //////////////////////////////////////////////////////////////////////////
+ // DONT TOUCH, unles you know what you're doing
+ //////////////////////////////////////////////////////////////////////////
+ ReserveArray<int> mTriIndices;
+ ReserveArray<rmt::Vector> mTriPts; //Separated Triangles
+ ReserveArray<rmt::Vector> mTriNorms; //Triangle Normals
+ ReserveArray<unsigned char> mTerrainType; // The terrain type for the triangle.
+
+ //////////////////////////////////////////////////////////////////////////
+ // Pre and Post calls for ScratchPad
+ //////////////////////////////////////////////////////////////////////////
+ void IntoTheVoid_WithGoFastaStripes();
+ void OutOfTheVoid_WithGoFastaStripes();
+ //////////////////////////////////////////////////////////////////////////
+ // Used to cache real data ptrs while data is uploaded to ScratchPad
+ //////////////////////////////////////////////////////////////////////////
+ static int* mspIndexData;
+ static unsigned char* mspTerrainData;
+ static rmt::Vector* mspVertexData;
+ static rmt::Vector* mspNormalData;
+ static bool msbInScratchPad;
+
+// ReserveArray<rmt::Vector> mTriCentroids; //Triangle Centroids
+// ReserveArray<float> mTriRadius; //Triangle Radius; the approximate
+ // length of the furthest pt from the centroid
+
+ int mnPrimGroups;
+
+ rmt::Box3D mBox3D;
+ rmt::Sphere mSphere;
+ rmt::Vector mPosn;
+
+ enum
+ {
+ UNINIT_PT= 0x0,
+ X_GT_PT = 0x0001,
+ X_LT_PT = 0x0002,
+ Z_GT_PT = 0x0004,
+ Z_LT_PT = 0x0008,
+ INIT_PT = 0x0010,
+ FOUND_PT = 0X001F
+ };
+
+protected:
+ void GenIDSG( tGeometry* ipGeometry );
+
+ void PreParseTris( tPrimGroupStreamed* ipPrimGroup );
+ void PreParseTriStrips( tPrimGroupStreamed* ipPrimGroup );
+ void DoAllAllocs();
+ void ParseTris( tPrimGroupStreamed* ipPrimGroup );
+ void ParseTriStrips( tPrimGroupStreamed* ipPrimGroup );
+ void CalcAllFields();
+
+private:
+};
+
+#endif
diff --git a/game/code/render/DSG/LensFlareDSG.cpp b/game/code/render/DSG/LensFlareDSG.cpp
new file mode 100644
index 0000000..30a606f
--- /dev/null
+++ b/game/code/render/DSG/LensFlareDSG.cpp
@@ -0,0 +1,588 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: LensFlareDSG.cpp
+//
+// Description: Implementation for LensFlareDSG class.
+//
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/LensFlareDSG.h>
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/billboardobject.hpp>
+#include <p3d/utility.hpp>
+#include <pddi/pddiext.hpp>
+
+// DEBUG stuff
+#include <worldsim/avatarmanager.h>
+#include <worldsim/character/character.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+const float INTENSITY_CHANGE_PER_FRAME = 0.1f;
+const int MAX_NUM_FLARES = 20;
+
+SwapArray< LensFlareDSG* > LensFlareDSG::spDrawQueue(MAX_NUM_FLARES);
+SwapArray< LensFlareDSG* > LensFlareDSG::spVisTestQueue(MAX_NUM_FLARES);
+
+unsigned LensFlareDSG::sP3DVisibilityCounter = 0;
+
+static rmt::Vector s_PixelPos(320,240,0);
+
+#ifdef RAD_PS2
+const tColour PS2_VISIBILITY_MESH_COLOUR = tColour( 0, 0,0,0 );
+#endif
+
+static rmt::Vector dstart,dend;
+
+
+// Clears the flare draw queue. Should be done after they are drawn
+void LensFlareDSG::ClearAllFlares()
+{
+ for (int i = 0 ; i < spDrawQueue.mUseSize ; i++)
+ {
+ spDrawQueue[ i ]->Release();
+ }
+ spDrawQueue.ClearUse();
+}
+// Draws every flare in the draw queue
+void LensFlareDSG::DisplayAllFlares()
+{
+// rTuneAssert( spDrawQueue.mUseSize < 2 );
+
+ for (int i = 0 ; i < spDrawQueue.mUseSize ; i++)
+ {
+ spDrawQueue[ i ]->DisplayImmediate();
+ }
+ ClearAllFlares();
+}
+// Adds a flare to the draw queue
+void LensFlareDSG::PostDisplayFlare( LensFlareDSG* pFlare )
+{
+
+
+ spDrawQueue.Add( pFlare );
+ pFlare->AddRef();
+}
+
+// Checks all queued flares to see if they are visible
+void LensFlareDSG::ReadFrameBufferIntensities()
+{
+#ifndef RAD_PS2
+ pddiExtVisibilityTest* mVisibilityTestExtension = static_cast<pddiExtVisibilityTest*> (p3d::pddi->GetExtension( PDDI_EXT_VISIBILITY_TEST ) );
+ if ( mVisibilityTestExtension != NULL )
+ {
+ for (int i = 0 ; i < spVisTestQueue.mUseSize ; i++)
+ {
+ spVisTestQueue[ i ]->ReadFrameBufferIntensity();
+ spVisTestQueue[ i ]->Release();
+ }
+ // Clear the array
+ spVisTestQueue.ClearUse();
+ }
+#endif
+
+}
+// LensFlareDSG constructor
+LensFlareDSG::LensFlareDSG()
+: mPosn( 0,0,0 ),
+mpCompDraw( NULL ),
+mVisTestGeoIndex( -1 ),
+mP3DVisibilityId( sP3DVisibilityCounter++ ),
+mNumPixelsVisible( 1 ),
+mCurrentIntensity( 0 )
+{
+
+}
+// LensFlareDSG destructor
+LensFlareDSG::~LensFlareDSG()
+{
+ int i;
+ for (i=mpBillBoards.mUseSize - 1 ; i > -1 ; i--)
+ {
+ mpBillBoards[i]->Release();
+ }
+ if ( mpCompDraw != NULL )
+ {
+ mpCompDraw->Release();
+ }
+
+}
+// Checks to see if this flare is visible to the camera
+void LensFlareDSG::ReadFrameBufferIntensity()
+{
+#ifndef RAD_GAMECUBE
+
+ pddiExtVisibilityTest* mVisibilityTestExtension = static_cast<pddiExtVisibilityTest*> (p3d::pddi->GetExtension( PDDI_EXT_VISIBILITY_TEST ) );
+ if ( mVisibilityTestExtension != NULL )
+ {
+ int count = mVisibilityTestExtension->Result( mP3DVisibilityId );
+ mNumPixelsVisible = count;
+ }
+#endif
+
+}
+// Called by the loader - allocates space for iNumGroups of billboards
+void LensFlareDSG::SetNumBillBoardQuadGroups( int iNumGroups )
+{
+ if (iNumGroups > 0)
+ {
+ mpBillBoards.Allocate( iNumGroups );
+ }
+}
+
+
+
+//========================================================================
+// LensFlare::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void LensFlareDSG::AddBillBoardQuadGroup( tBillboardQuadGroup* pGroup )
+{
+ mpBillBoards.Add( pGroup );
+ pGroup->AddRef();
+}
+
+//========================================================================
+// WorldSphereDSG::SetCompositeDrawable
+//========================================================================
+//
+// Description: Sets the single composite drawable
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void LensFlareDSG::SetCompositeDrawable( tCompositeDrawable* ipCompDraw )
+{
+
+
+ mpCompDraw = ipCompDraw;
+ mpCompDraw->AddRef();
+ rmt::Sphere sphere;
+ mpCompDraw->GetBoundingSphere( &sphere );
+ mPosn = sphere.centre;
+
+ // Find the visibility mesh. It will be the only piece of geo in this thing
+ for ( int i = 0 ; i <mpCompDraw->GetNumDrawableElement() ; i++ )
+ {
+ tCompositeDrawable::DrawableElement* elem = mpCompDraw->GetDrawableElement(i);
+ if ( elem->GetType() == tCompositeDrawable::DrawableElement::PROP_ELEMENT )
+ {
+ tCompositeDrawable::DrawablePropElement* propElem = static_cast< tCompositeDrawable::DrawablePropElement* >(elem);
+ rAssert( dynamic_cast < tCompositeDrawable::DrawablePropElement* >( elem ) != NULL );
+ if ( dynamic_cast< tGeometry* >( propElem->GetDrawable() ) != NULL )
+ {
+ // Found it, record the index of the composite drawable
+ mVisTestGeoIndex = i;
+ // Turn off visibility for this thing
+ propElem->SetVisibility( false );
+ propElem->SetLockVisibility( true );
+ break;
+ }
+ }
+ }
+}
+///////////////////////////////////////////////////////////////////////
+// Drawable
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void LensFlareDSG::Display()
+{
+#ifdef PROFILER_ENABLED
+ char profileName[] = " LensFlareDSG Display";
+#endif
+ DSG_BEGIN_PROFILE(profileName)
+ LensFlareDSG::PostDisplayFlare( this );
+ DSG_END_PROFILE(profileName)
+}
+void LensFlareDSG::DisplayImmediate()
+{
+#ifdef RAD_WIN32
+ return;
+#endif
+
+#ifdef PROFILER_ENABLED
+ char profileName[] = " LensFlareDSG DisplayImmediate";
+#endif
+
+ tView* view = p3d::context->GetView();
+ if ( view == NULL )
+ return;
+ tCamera* camera = view->GetCamera();
+ rmt::Vector cameraPosition;
+ camera->GetWorldPosition( &cameraPosition );
+ rmt::Matrix toCameraPosition;
+ toCameraPosition.Identity();
+ toCameraPosition.FillTranslate( cameraPosition );
+ p3d::stack->PushMultiply( toCameraPosition );
+
+
+ DSG_BEGIN_PROFILE(profileName)
+ DrawVisibilityChecker();
+#ifdef RAD_PS2
+ // turn z compare to always
+ SetBillBoardIntensity( 1.0f );
+ pddiCompareMode origZCompare = p3d::pddi->GetZCompare();
+ p3d::pddi->SetZCompare(PDDI_COMPARE_ALWAYS);
+ mpCompDraw->Display();
+ p3d::pddi->SetZCompare(origZCompare);
+#else
+ // Visible but not at full intensity, ramp up
+ if (mNumPixelsVisible > 0 && mCurrentIntensity < 1.0f)
+ {
+ mCurrentIntensity += INTENSITY_CHANGE_PER_FRAME;
+ if ( mCurrentIntensity > 1.0f )
+ mCurrentIntensity = 1.0f;
+ }
+ else if (mNumPixelsVisible == 0 && mCurrentIntensity > 0)
+ {
+ mCurrentIntensity -= INTENSITY_CHANGE_PER_FRAME;
+ if ( mCurrentIntensity < 0 )
+ mCurrentIntensity = 0;
+ }
+
+ SetBillBoardIntensity( mCurrentIntensity );
+
+ pddiCompareMode origZCompare = p3d::pddi->GetZCompare();
+ p3d::pddi->SetZCompare(PDDI_COMPARE_ALWAYS);
+ mpCompDraw->Display();
+ p3d::pddi->SetZCompare(origZCompare);
+
+#endif
+
+
+ p3d::stack->Pop();
+
+ DSG_END_PROFILE(profileName)
+}
+
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void LensFlareDSG::DisplayBoundingBox(tColour colour)
+{
+ rTuneAssert(false);
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void LensFlareDSG::DisplayBoundingSphere(tColour colour)
+{
+ rTuneAssert(false);
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void LensFlareDSG::GetBoundingBox(rmt::Box3D* box)
+{
+ mpCompDraw->GetBoundingBox( box );
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void LensFlareDSG::GetBoundingSphere(rmt::Sphere* pSphere)
+{
+ mpCompDraw->GetBoundingSphere( pSphere );
+}
+///////////////////////////////////////////////////////////////////////
+// IEntityDSG
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+rmt::Vector* LensFlareDSG::pPosition()
+{
+ rTuneAssert(false);
+ return NULL;
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+const rmt::Vector& LensFlareDSG::rPosition()
+{
+ return mPosn;
+}
+//==============================6==========================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void LensFlareDSG::GetPosition( rmt::Vector* ipPosn )
+{
+ *ipPosn = mPosn;
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void LensFlareDSG::RenderUpdate()
+{
+ //Do Nothing
+}
+//************************************************************************
+//
+// Protected Member Functions : WorldSphereDSG
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : WorldSphereDSG
+//
+//************************************************************************
+
+#ifdef RAD_PS2
+void LensFlareDSG::DrawVisibilityChecker()
+{
+
+}
+#endif
+
+#if defined( RAD_XBOX ) || defined( RAD_WIN32 )
+
+void LensFlareDSG::DrawVisibilityChecker()
+{
+ // We need to use a PDDI extension to draw the billboards
+ // with colour write off at a specific location
+ // This will fill the Z buffer (make sure to draw enable Z buffering!)
+ // with the billboard distances
+ // Then draw the scene as usual
+ // Fetch the extension from PDDI
+ pddiExtVisibilityTest* mVisibilityTestExtension = static_cast<pddiExtVisibilityTest*> (p3d::pddi->GetExtension( PDDI_EXT_VISIBILITY_TEST ) );
+ if ( mVisibilityTestExtension != NULL &&
+ mVisTestGeoIndex != -1 )
+ {
+ bool origZWrite = p3d::pddi->GetZWrite();
+ p3d::pddi->SetZWrite(false);
+
+ // Tell PDDI to begin logging my Z writes
+
+ // Force Z writes to true
+ // We know that lens flares never write into the Z buffer, so we don't have to save
+ // their Z write values
+
+ mpCompDraw->GetPose()->Evaluate();
+ p3d::pddi->SetColourWrite( false, false, false, false );
+ rTuneAssert( p3d::pddi->GetZCompare() != PDDI_COMPARE_ALWAYS );
+ tCompositeDrawable::DrawableElement* elem = mpCompDraw->GetDrawableElement( mVisTestGeoIndex );
+ elem->SetPose( mpCompDraw->GetPose() );
+ elem->SetLockVisibility( false );
+ elem->SetVisibility( true );
+ mVisibilityTestExtension->Begin();
+ elem->Display();
+ // Tell PDDI to stop logging z writes
+ mVisibilityTestExtension->End( mP3DVisibilityId );
+
+ elem->SetVisibility( false );
+ elem->SetLockVisibility( true );
+
+ // Restore Z write values
+ p3d::pddi->SetColourWrite( true, true, true, true );
+ p3d::pddi->SetZWrite(origZWrite);
+
+ // Insert a pointer to this DSG object into the vis test array
+ // so we can set it up to be retreived upon DSG removal
+ this->AddRef();
+ LensFlareDSG* pLensFlare = this;
+ spVisTestQueue.Add( pLensFlare );
+ }
+}
+#endif
+
+#ifdef RAD_GAMECUBE
+void LensFlareDSG::DrawVisibilityChecker()
+{
+ rTuneAssert( p3d::pddi->GetZCompare() != PDDI_COMPARE_ALWAYS );
+
+ // Gamecube
+ // Have to read the Z value of the center of the visibility mesh before and after it
+ // was drawn
+
+ // Read Z value before it was drawn
+
+ // Determine position to read from
+ // Evaluate the composite drawable
+ mpCompDraw->GetPose()->Evaluate();
+ // and fetch the element's matrix
+ tCompositeDrawable::DrawableElement* elem = mpCompDraw->GetDrawableElement( mVisTestGeoIndex );
+ elem->SetPose( mpCompDraw->GetPose() );
+ const rmt::Matrix* pWorldMat = elem->GetWorldMatrix();
+ // Position is determined by using tContext::ObjectToDevice
+ p3d::stack->PushMultiply( *pWorldMat );
+ rmt::Vector deviceCoords;
+ p3d::context->ObjectToDevice( rmt::Vector(0,0,0), &deviceCoords );
+ p3d::stack->Pop();
+ // Check that the deviceCoords fall within valid Gamecube API range
+ // x from 0 to 639 inclusive
+ // y from 0 to 527 inclusive
+ // If either coordinate is not in this range, bail, its not visible
+ if ( deviceCoords.x < 0 || deviceCoords.x > 639 || deviceCoords.y < 0 || deviceCoords.y > 527 )
+ {
+ mNumPixelsVisible = 0;
+ }
+ else
+ {
+
+ // Make sure drawing is completed so far and read the z buffer depth at the point where
+ // the mesh will be drawn
+ GXDrawDone();
+ u32 depthBeforeVisMesh;
+ GXPeekZ( deviceCoords.x, deviceCoords.y, &depthBeforeVisMesh );
+
+
+ // Now draw the visibility mesh and see if it was actually written
+ p3d::pddi->SetColourWrite( false, false, false, true );
+ elem->SetLockVisibility( false );
+ elem->SetVisibility( true );
+ elem->Display();
+ elem->SetVisibility( false );
+ elem->SetLockVisibility( true );
+ GXDrawDone();
+ // Vis mesh has been drawn, read the pixel
+ u32 depthAfterVisMesh;
+ GXPeekZ( deviceCoords.x, deviceCoords.y, &depthAfterVisMesh );
+
+ if ( depthAfterVisMesh < depthBeforeVisMesh )
+ mNumPixelsVisible = 1;
+ else
+ mNumPixelsVisible = 0;
+
+ p3d::pddi->SetColourWrite( true, true, true, true );
+
+
+ }
+
+}
+#endif
+
+
+void LensFlareDSG::SetBillBoardIntensity( float intensity )
+{
+ for (int i = 0 ; i < mpBillBoards.mUseSize ; i++)
+ {
+ mpBillBoards[ i ]->SetIntensityBias( intensity );
+ }
+}
+
+
diff --git a/game/code/render/DSG/LensFlareDSG.h b/game/code/render/DSG/LensFlareDSG.h
new file mode 100644
index 0000000..107f3ca
--- /dev/null
+++ b/game/code/render/DSG/LensFlareDSG.h
@@ -0,0 +1,137 @@
+#ifndef LENSFLAREDSG_H
+#define LENSFLAREDSG_H
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: LensFlareDSG
+//
+// Description: A compdrawable with a number of billboards. Also drawn AFTER everything else
+//
+// History: + Initial Implementation -- Michael Riegger
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <p3d/p3dtypes.hpp>
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/DSG/InstStatEntityDSG.h>
+#include <render/Culling/SwapArray.h>
+
+
+class tCompositeDrawable;
+class tBillboardQuadGroup;
+class tGeometry;
+
+//========================================================================
+//
+// The LensFlareDSG - Draws lens flares
+// There could be a problem with the lens flares in split screen mode
+// Each flare is tested once after swapbuffer, they should be tested
+// Once per viewport
+//
+//========================================================================
+class LensFlareDSG : public StaticEntityDSG
+{
+public:
+ LensFlareDSG();
+ ~LensFlareDSG();
+
+ // Lens flares are composed of a varying number of billboarded quads
+ // This functions adds a new quad to the flare (typically from the lensflareloader)
+ void AddBillBoardQuadGroup( tBillboardQuadGroup* );
+ // Allocates the number of billboards, must be called before addbillboardquadgroup
+ void SetNumBillBoardQuadGroups( int iNumGroups );
+ // Sets the compdrawable. The compdraw contains the billboards
+ void SetCompositeDrawable( tCompositeDrawable* ipCompDraw );
+ // Sets the visibility geometry. NOTE - THIS IS NEVER ACTUALLY DRAWN!!!
+ // It is only used to determine if the lens flare is visible or not
+ // It represents the actual object that is generating the flare, which
+ // could be occulded by something, hence we test to see if this is actually
+ // visible before drawing the flares
+ // Not used, I iterate through the comp drawable and find the desired mesh
+// void SetVisTestMesh( tGeometry* ipGeo );
+
+ ///////////////////////////////////////////////////////////////////////
+ // Drawable
+ ///////////////////////////////////////////////////////////////////////
+
+ // Does not draw immediately!!!! Calls PostDisplayFlare() internally to set
+ // up the flare to be drawn at a later date.
+ void Display();
+ // Immediately displays the flares.
+ void DisplayImmediate();
+
+ // Checks the visibility test to see if the vistest mesh is visible or not
+ // used to determine flare intensity (not visible - flare fades to zero)
+ void ReadFrameBufferIntensity();
+
+ void DisplayBoundingBox(tColour colour = tColour(0,255,0));
+ void DisplayBoundingSphere(tColour colour = tColour(0,255,0));
+
+ void GetBoundingBox(rmt::Box3D* box);
+ void GetBoundingSphere(rmt::Sphere* sphere);
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // IEntityDSG
+ ///////////////////////////////////////////////////////////////////////
+ rmt::Vector* pPosition();
+ const rmt::Vector& rPosition();
+ void GetPosition( rmt::Vector* ipPosn );
+
+ void RenderUpdate();
+
+ // Draw all flares, immediately (usually called after all other objects in the world have been drawn )
+ static void DisplayAllFlares();
+
+ // Clears the list of PostDisplayed flares
+ static void ClearAllFlares();
+
+ // Flags a flare to be drawn during DisplayAllFlares(). Display() defaults to calling
+ // PostDisplayFlare()
+ static void PostDisplayFlare( LensFlareDSG* pFlare );
+
+ // Reads all flare intensities
+ static void ReadFrameBufferIntensities();
+
+ virtual void SetShader(tShader* pShader, int i)
+ {
+ }
+
+protected:
+
+ // listing of all available lens flares
+
+ rmt::Vector mPosn;
+ tCompositeDrawable* mpCompDraw;
+// tGeometry* mpTestVisGeo;
+ int mVisTestGeoIndex;
+
+ SwapArray< tBillboardQuadGroup* > mpBillBoards;
+ unsigned mP3DVisibilityId;
+ int mNumPixelsVisible;
+ float mCurrentIntensity;
+
+ void DrawVisibilityChecker();
+ void EnableZWrites( bool enable );
+
+ void SetBillBoardIntensity( float intensity );
+
+
+private:
+
+ // Listing of all flares in the world that want to be drawn
+ static SwapArray< LensFlareDSG* > spDrawQueue;
+ static SwapArray< LensFlareDSG* > spVisTestQueue;
+ static unsigned sP3DVisibilityCounter;
+
+
+};
+
+#endif
diff --git a/game/code/render/DSG/StatePropDSG.cpp b/game/code/render/DSG/StatePropDSG.cpp
new file mode 100644
index 0000000..f0b2274
--- /dev/null
+++ b/game/code/render/DSG/StatePropDSG.cpp
@@ -0,0 +1,1118 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: StatePropDSG
+//
+// Description: DSG object that contains a state prop
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+#include <memory/classsizetracker.h>
+#include <radtime.hpp>
+#include <render/DSG/StatePropDSG.h>
+#include <console/console.h>
+#include <stateprop/statepropdata.hpp>
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <poser/pose.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <simphysics/articulatedphysicsobject.hpp>
+#include <simphysics/physicsobject.hpp>
+#include <worldsim/character/character.h>
+#include <render/breakables/breakablesmanager.h>
+#include <render/animentitydsgmanager/animentitydsgmanager.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <ai/actor/actormanager.h>
+#include <ai/actor/actoranimation.h>
+#include <events/eventenum.h>
+#include <data/persistentworldmanager.h>
+#include <constants/StatePropEnum.h>
+#include <worldsim/hitnrunmanager.h>
+#include <sound/soundcollisiondata.h>
+#include <worldsim/avatarmanager.h>
+#include <events/eventdata.h>
+#include <worldsim/avatar.h>
+#include <mission/gameplaymanager.h>
+#include <worldsim/redbrick/rootmatrixdriver.h>
+
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+// Amount to move the shadow/light pools off the ground
+const float STATEPROP_SHADOW_Z_OFFSET = 1.0f;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+StatePropDSG::PoseMap* StatePropDSG::sp_SharedtPoses;
+
+// How large the pool of tPoses is
+const int MAX_NUM_SHARED_TPOSES = 20;
+
+// Determine if a given physics object is a heavy duty ArticulatedPhysicsObject
+// or not.
+static bool IsFullArticulatedPhysicsObject( sim::PhysicsObject* object )
+{
+ bool isArtPhys;
+ sim::ArticulatedPhysicsObject* artPhys = dynamic_cast< sim::ArticulatedPhysicsObject* >( object );
+ if ( artPhys != NULL )
+ {
+ isArtPhys = true;
+ }
+ else
+ {
+ isArtPhys = false;
+ }
+ return isArtPhys;
+}
+
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+StatePropDSG::StatePropDSG() :
+mpStateProp( NULL ),
+mpPhysObj( NULL ),
+mpProcAnimator( 0 ),
+m_ShadowElement( -1 ),
+m_SimAnimJoint( -1 )
+{
+ CLASSTRACKER_CREATE( StatePropDSG );
+ mTransform.Identity();
+ mPosn = mTransform.Row(3);
+ // Allocate some space for our array of shared tPoses
+ if ( sp_SharedtPoses == NULL )
+ {
+ sp_SharedtPoses = new PoseMap;
+ sp_SharedtPoses->reserve( MAX_NUM_SHARED_TPOSES );
+ }
+}
+
+StatePropDSG::~StatePropDSG()
+{
+ CLASSTRACKER_DESTROY( StatePropDSG );
+ GetAnimEntityDSGManager()->Remove( this );
+ delete mpProcAnimator;
+ mpProcAnimator = 0;
+
+ // Ok, we are destroying a stateprop
+
+
+ if ( mpStateProp != NULL )
+ {
+ mpStateProp->RemoveStatePropListener( this );
+ mpStateProp->Release();
+ mpStateProp = NULL;
+ }
+
+
+ if ( mpPhysObj != NULL )
+ {
+ mpPhysObj->Release();
+ mpPhysObj = NULL;
+ }
+}
+
+
+void
+StatePropDSG::LoadSetup( CStatePropData* statePropData,
+ int startState,
+ const rmt::Matrix& transform,
+ CollisionAttributes* ipCollAttr,
+ bool isDynaLoaded,
+ tEntityStore* ipSearchStore,
+ bool useSharedtPose,
+ sim::CollisionObject* collisionObject,
+ sim::PhysicsObject* physicsObject )
+
+{
+ int type = ipCollAttr->GetClasstypeid();
+ mTransform = transform;
+ mPosn = transform.Row(3);
+ // Very likely that mTranslucent will always be true considering all the particles
+ // animations, fades, etc that are possible.
+ mTranslucent = true;
+ m_IsDynaLoaded = isDynaLoaded;
+ rAssert( mpStateProp == NULL );
+ // Memory optimization. If we are told to use a shared tPose, then
+ // try and grab an existing one (for this type) off out map
+ bool newSharedtPoseCreated;
+ tPose* tpose;
+ if ( useSharedtPose )
+ {
+ tpose = GetSharedtPose( statePropData->GetUID() );
+ if ( tpose == NULL )
+ // we want to share the pose, but not premade pose exists, so
+ // grab the pose that will be created in CreateStateProp
+ newSharedtPoseCreated = true;
+ else
+ newSharedtPoseCreated = false;
+ }
+ else
+ {
+ newSharedtPoseCreated = false;
+ tpose = NULL;
+ }
+ mpStateProp = CStateProp::CreateStateProp( statePropData, startState, tpose );
+ mpStateProp->SetUID( statePropData->GetUID() );
+ mpStateProp->AddStatePropListener( this );
+
+ // A new shared tPose was created, grab it off the composite drawable and insert it
+ // into our list
+ if ( newSharedtPoseCreated )
+ {
+ tCompositeDrawable* compDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable());
+ AddNewSharedtPose( statePropData->GetUID(), compDraw->GetPose() );
+ }
+
+
+
+ // Give the DSG object name the same as the stateprop object by default
+ SetName( mpStateProp->GetName() );
+
+ // Setup the physics, retrieve the pose
+ rAssert( dynamic_cast< tCompositeDrawable* > ( mpStateProp->GetDrawable()) );
+ tCompositeDrawable* pCompDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable() );
+ tPose* p3dPose = pCompDraw->GetPose();
+ p3dPose->Evaluate();
+
+
+ // The sim library does in fact add something to the store, the above comment notwithstanding.
+ // So we wrap this call in a PushSection/PopSection.
+ //
+ if ( physicsObject == NULL )
+ physicsObject = p3d::find< sim::PhysicsObject >( statePropData->GetUID() );
+
+ if ( collisionObject == NULL )
+ collisionObject = p3d::find< sim::CollisionObject >( statePropData->GetUID() );
+
+ // Abort if there are no physics or collision objects found
+ if ( collisionObject == NULL )
+ return;
+
+ p3d::inventory->PushSection( );
+ p3d::inventory->SelectSection( "Default" );
+
+ sim::SimState* pSimState = NULL;
+ if ( type == PROP_MOVEABLE || type == PROP_ONETIME_MOVEABLE || type == PROP_BREAKABLE )
+ {
+
+ if ( IsFullArticulatedPhysicsObject( physicsObject ) )
+ {
+ poser::PoseEngine* poseEngine = new poser::PoseEngine(p3dPose , 1, p3dPose->GetNumJoint() );
+ poseEngine->AddRef();
+ RootMatrixDriver* rmd = new RootMatrixDriver( &mTransform );
+ rmd->AddRef();
+ poseEngine->AddPoseDriver( 0, rmd );
+ poseEngine->Begin();
+
+ poser::Pose* poserPose = poseEngine->GetPose();
+
+ // This is they expensive route, heavy duty articulated physics.
+ sim::SimStateArticulated* pSimStateArt = sim::SimStateArticulated::CreateSimStateArticulated( statePropData->GetUID(), poserPose, sim::SimStateAttribute_Default , ipSearchStore );
+ if ( pSimStateArt != NULL )
+ {
+ // Simplify calculations, we don't need or want expensive articulated computations
+ pSimStateArt->ConvertToRigidBody();
+ // Kill the poser::Pose object. Its *TONS* of useless matrices and garbage
+ pSimStateArt->ReleasePose();
+ pSimState = pSimStateArt;
+ }
+ else
+ {
+ // Fallback path
+ pSimState = sim::SimState::CreateSimState( statePropData->GetUID(), sim::SimStateAttribute_Default , ipSearchStore );
+ }
+
+ // Cleanup unused Articulated poseengine stuff
+ poseEngine->End();
+ // With no pose, the poseengine should be killed
+ poseEngine->ReleaseVerified();
+ poseEngine = NULL;
+ // No use for the root matrix driver either
+ rmd->ReleaseVerified();
+ rmd = NULL;
+
+ }
+ else
+ {
+ rAssert( physicsObject != NULL );
+ // Try creating a simplified sim state (Ideal by far!)
+ pSimState = sim::SimState::CreateSimState( statePropData->GetUID(), sim::SimStateAttribute_Default , ipSearchStore );
+ }
+
+ // Place the collision object in the correct place
+ pSimState->SetTransform( transform );
+ }
+ else
+ {
+ pSimState = sim::SimStateArticulated::CreateStaticSimState( collisionObject );
+ // Tell the simstate where to place it in the world
+ pSimState->GetCollisionObject()->SetIsStatic(false);
+ pSimState->GetCollisionObject()->SetManualUpdate(false);
+ pSimState->SetTransform( transform );
+ pSimState->GetCollisionObject()->Update();
+ pSimState->GetCollisionObject()->GetCollisionVolume()->UpdateAll();
+ pSimState->GetCollisionObject()->SetIsStatic(true);
+ pSimState->GetCollisionObject()->SetManualUpdate(true);
+ }
+ rAssert( pSimState != NULL );
+ pSimState->SetControl( sim::simAICtrl );
+
+ // Lets hold onto the physics object, we'll need it for physics updates
+ // when in sim mode
+ mpPhysObj = static_cast< sim::PhysicsObject* >( pSimState->GetSimulatedObject() );
+ if ( mpPhysObj )
+ mpPhysObj->AddRef();
+
+ p3d::inventory->PopSection();
+
+ // Needed for SetSimState to work because of tRefCounted::Assign
+ pSimState->AddRef();
+ // The simstate holds the ai ref pointer of its parent object
+ // tell it what type of object this is
+ pSimState->mAIRefIndex = GetAIRef();
+ SetCollisionAttributes(ipCollAttr);
+ pSimState->mAIRefPointer = this;
+ // This sets the mpSimStateObj pointer and calls OnSetsimstate
+ SetSimState( pSimState );
+ pCompDraw->ProcessShaders(*this);
+ pSimState->Release();
+
+ // Add this object to the animation update list that gets called every frame
+ GetAnimEntityDSGManager()->Add( this );
+
+}
+
+StatePropDSG*
+StatePropDSG::Clone(const char* Name, const rmt::Matrix& iMatrix) const
+{
+ StatePropDSG* pClone = new StatePropDSG();
+
+ CStatePropData* spData = mpStateProp->GetStatePropData();
+ const int START_STATE = 0;
+ CollisionAttributes* pCollAttr = GetCollisionAttributes();
+ bool usingSharedPose;
+
+ // Should we share the pose?
+ // That depends on whether the original is sharing a pose
+ if ( GetSharedtPose( spData->GetUID() ) != NULL )
+ usingSharedPose = true;
+ else
+ usingSharedPose = false;
+
+ pClone->LoadSetup( spData, START_STATE, iMatrix, pCollAttr, true, NULL, usingSharedPose, NULL, NULL );
+ pClone->SetName( Name );
+
+ return pClone;
+}
+
+void StatePropDSG::OnSetSimState( sim::SimState* ipSimState )
+{
+ tRefCounted::Assign( mpSimStateObj, ipSimState );
+ mpSimStateObj->mAIRefIndex = this->GetAIRef();
+ mpSimStateObj->ResetVelocities();
+
+ // set up box and sphere for DSG
+ //
+ // note, unlike the StaticPhysDSG, this box and sphere are in object space
+ rmt::Vector center;
+ if ( mpPhysObj != NULL )
+ {
+ center = mpPhysObj->GetExternalCMOffset();
+ }
+ else
+ {
+ center = mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mPosition;
+ center -= mPosn;
+ }
+ float radius = mpSimStateObj->GetSphereRadius();
+
+ mBBox.low = center;
+ mBBox.high = center;
+ mBBox.high += mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+ mBBox.low -= mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+
+ mSphere.centre = center;
+ mSphere.radius = radius;
+
+ rAssert(mpCollisionAttributes);
+ mpSimStateObj->SetPhysicsProperties(mpCollisionAttributes->GetPhysicsProperties());
+}
+
+void
+StatePropDSG::Display()
+{
+
+#ifdef PROFILER_ENABLED
+ char profileName[] = " StatePropDSG Display";
+#endif
+ if(IS_DRAW_LONG) return;
+ DSG_BEGIN_PROFILE(profileName)
+ if(CastsShadow())
+ {
+ BillboardQuadManager::Enable();
+ }
+ p3d::stack->PushMultiply( mTransform );
+ mpStateProp->Display( mpProcAnimator );
+ p3d::stack->Pop();
+ if(CastsShadow())
+ {
+ BillboardQuadManager::Disable();
+ DisplaySimpleShadow();
+ }
+ DSG_END_PROFILE(profileName)
+}
+
+void
+StatePropDSG::AdvanceAnimation( float timeInMS )
+{
+ // Simple animation frame controller advancement
+ mpStateProp->Update( timeInMS );
+ if( mpProcAnimator )
+ {
+ mpProcAnimator->Advance( timeInMS );
+ }
+ if ( m_SimAnimJoint != -1 )
+ {
+ AnimateCollisionVolume();
+ }
+}
+
+
+void
+StatePropDSG::Update( float timeInSec )
+{
+ // Update physics and collision, if necessary
+ if ( mpPhysObj != NULL )
+ {
+ mpPhysObj->Update( timeInSec );
+ }
+
+ if ( mpSimStateObj )
+ {
+ sim::CollisionObject* collObj = mpSimStateObj->GetCollisionObject();
+ if ( collObj->HasMoved( ) || collObj->HasRelocated( ) )
+ {
+ collObj->Update();
+
+ // do this _before_ updating matrix!
+ rmt::Box3D oldBox;
+
+ GetBoundingBox( &oldBox );
+
+ mTransform = mpSimStateObj->GetTransform();
+ mPosn = mpSimStateObj->GetPosition();
+
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mRenderLayer ));
+ // Sanity check
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Move( oldBox, this );
+ }
+
+ }
+
+ if(mIsHit && (GetCollisionAttributes()->GetClasstypeid() == PROP_ONETIME_MOVEABLE))
+ {
+ if(!GetGameplayManager()->TestPosInFrustrumOfPlayer( mPosn, 0, mSphere.radius))
+ {
+ this->AddRef();
+ ((WorldRenderLayer*)GetRenderManager()->mpLayer(RenderEnums::LevelSlot))->RemoveGuts(this);
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(this);
+ GetRenderManager()->mEntityDeletionList.Add(this);
+ }
+ }
+}
+
+
+
+void
+StatePropDSG::GetBoundingBox( rmt::Box3D* box )
+{
+
+ (box->low).Add(mBBox.low, mPosn);
+ (box->high).Add(mBBox.high, mPosn);
+}
+void
+StatePropDSG::GetBoundingSphere( rmt::Sphere* out_sphere )
+{
+ (out_sphere->centre).Add(mSphere.centre, mPosn);
+
+ out_sphere->radius = mSphere.radius;
+}
+
+rmt::Vector*
+StatePropDSG::pPosition()
+{
+ rAssert( false );
+ return &mTransform.Row(3);
+}
+
+
+const rmt::Vector&
+StatePropDSG::rPosition()
+{
+ return mTransform.Row(3);
+}
+
+void
+StatePropDSG::GetPosition( rmt::Vector* ipPosn )
+{
+ rAssert( ipPosn != NULL );
+ *ipPosn = mTransform.Row(3);
+}
+
+void
+StatePropDSG::SetPosition( const rmt::Vector& position )
+{
+ rmt::Matrix newTransform = mTransform;
+ newTransform.FillTranslate( position );
+ SetTransform( newTransform );
+}
+
+void
+StatePropDSG::GetTransform( rmt::Matrix* pTransform )
+{
+ rAssert( pTransform != NULL );
+ *pTransform = mTransform;
+}
+
+void
+StatePropDSG::SetTransform( const rmt::Matrix& transform )
+{
+
+
+ rmt::Box3D oldBox;
+ GetBoundingBox( &oldBox );
+
+ // Remember old manual update settings
+ bool manualUpdate = mpSimStateObj->GetCollisionObject()->IsManualUpdate();
+ mpSimStateObj->GetCollisionObject()->SetManualUpdate( false );
+
+ if ( mpSimStateObj->GetControl() == sim::simAICtrl )
+ {
+ mpSimStateObj->SetTransform( transform );
+ }
+ else
+ {
+ // Can't just tell an object to move to a new position
+ // if we are in sim control
+ // force it to ai, set transform, then set it back
+ mpSimStateObj->SetControl( sim::simAICtrl );
+ mpSimStateObj->SetTransform( transform );
+ mpSimStateObj->SetControl( sim::simSimulationCtrl );
+ }
+ mpSimStateObj->ResetVelocities();
+
+ // Matrix is reset, recompute the bounding box and move it
+ // in the DSG( Warning, this assumes that the user actually has it IN
+ // the DSG. This was always the case for DSG objects so lets not break
+ // tradition
+ // Bounding box relies on mPosn, lets update it
+
+ mTransform = mpSimStateObj->GetTransform(-1);
+ mPosn = mTransform.Row( 3 );
+
+
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mRenderLayer ));
+ // Sanity check
+
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Move( oldBox, this );
+
+ // Restore manual update settings
+ mpSimStateObj->GetCollisionObject()->SetManualUpdate( manualUpdate );
+}
+
+
+sim::Solving_Answer
+StatePropDSG::PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+{
+ // Make sure the object doesn't collide with other objects that it may be touching that are
+ // part of the static world
+ if(this->GetSimState()->GetControl() == sim::simAICtrl && pCollidedObj->GetCollisionObject()->IsStatic() == true)
+ {
+ return sim::Solving_Aborted;
+ }
+
+ switch ( pCollidedObj->mAIRefIndex )
+ {
+ case PhysicsAIRef::PlayerCharacter:
+ case PhysicsAIRef::NPCharacter:
+ {
+ rAssert( dynamic_cast< Character* >( static_cast< tRefCounted* >( pCollidedObj->mAIRefPointer) ) );
+ Character* pCharacter = static_cast< Character* >( pCollidedObj->mAIRefPointer );
+ return sim::Solving_Aborted;
+ }
+ case PhysicsAIRef::redBrickVehicle:
+ {
+ // Collision with a vehicle
+ // is this prop a breakable?
+ if ( GetType() == PROP_BREAKABLE )
+ {
+ Vehicle* vehicle = static_cast< Vehicle* >( pCollidedObj->mAIRefPointer );
+
+ if ( HandleEvent( VEHICLE_HIT ) == false )
+ {
+ // goto the destroyed state
+ HandleEvent( DESTROYED );
+ HandleEvent( STOMP );
+ HandleEvent( HIT );
+ }
+
+ //
+ // Cue the breaking sounds
+ //
+ SoundCollisionData soundData( 1.0f, vehicle, this );
+
+ GetEventManager()->TriggerEvent( EVENT_COLLISION, &soundData );
+
+ // return solving aborted, the breakable isn't stopping this vehicle
+ return sim::Solving_Aborted;
+ }
+ }
+ break;
+ }
+
+ HandleEvent( StatePropDSG::FEATHER_TOUCH );
+
+ // need this here?
+ this->mIsHit = true;
+
+ // Breakables always stay put and animate in place
+ if ( GetType() != PROP_BREAKABLE )
+ AddToSimulation( );
+ return sim::Solving_Continue;
+}
+
+
+sim::Solving_Answer
+StatePropDSG::PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision)
+{
+ // Calculate impulse
+ float impulseMagSqr = impulse.MagnitudeSqr();
+
+ if ( impulseMagSqr > 10000 )
+ {
+ sim::CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ sim::CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ sim::SimState* simStateA = collObjA->GetSimState();
+ sim::SimState* simStateB = collObjB->GetSimState();
+
+ // Has the stateprop been hit by a vehicle?
+ // If so, notify the stateprop
+ if ( simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle ||
+ simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle )
+ {
+ bool msgHandled = HandleEvent( VEHICLE_HIT );
+ if ( msgHandled == false )
+ HandleEvent( HIT );
+ }
+ else
+ {
+ // Tell the stateprop that a normal event was triggered
+ HandleEvent( HIT );
+ }
+ }
+
+
+ return CollisionEntityDSG::PostReactToCollision(impulse, inCollision);
+}
+
+void
+StatePropDSG::GenerateCoins( int numCoins )
+{
+ HitnRunManager* hrm = GetHitnRunManager();
+
+ // This is just so bad.
+ //Since an object has no idea what it really is, we can't tell if the
+ //breakable object is a crate, tree, or krusty glass, etc. So to try
+ //and pick out the crates we'll look at the sound file played when the
+ //object breaks and if it's car_hit_crate, we'll ASSUME that it's a
+ //crate. *sigh*
+ bool isCrate = false;
+ bool coinsDisabled = hrm->IsHitnRunDisabled();
+ CollisionAttributes* collAttribs = this->GetCollisionAttributes();
+ if(collAttribs)
+ {
+ if((strcmp(collAttribs->GetSound(), "car_hit_crate")) == 0 && (mPersistentObjectID >= 0))
+ {
+ isCrate = true;
+ }
+ }
+
+ if(!coinsDisabled || isCrate)
+ {
+ GetCoinManager()->SpawnCoins( numCoins + (coinsDisabled && isCrate ? 1 : 0), rPosition() );
+ }
+}
+
+
+
+void
+StatePropDSG::RecieveEvent( int callback , CStateProp* stateProp )
+{
+ // The artists are trying to tell us something!
+ // These are callbacks set from the stateprop builder tool
+ // switch on the callback type and perform the appropriate action
+
+ switch ( callback )
+ {
+ case StatePropEnum::eStateChange:
+ break;
+
+ case StatePropEnum::eRemoveFromWorld:
+ GetBreakablesManager()->RemoveBrokenObjectFromWorld( this, RenderEnums::LevelSlot, m_IsDynaLoaded );
+ GetPersistentWorldManager()->OnObjectBreak( mPersistentObjectID );
+ break;
+ case StatePropEnum::eSpawn1Coin:
+ GenerateCoins( 1 );
+ break;
+ case StatePropEnum::eSpawn5Coins:
+ GenerateCoins( 5 );
+ break;
+ case StatePropEnum::eSpawn10Coins:
+ GenerateCoins( 10 );
+ break;
+ case StatePropEnum::eSpawn15Coins:
+ GenerateCoins( 15 );
+ break;
+ case StatePropEnum::eSpawn20Coins:
+ GenerateCoins( 20 );
+ break;
+ case StatePropEnum::eRemoveCollisionVolume:
+ {
+ EnableCollisionVolume( false );
+ GetPersistentWorldManager()->OnObjectBreak( mPersistentObjectID );
+ }
+ break;
+ case StatePropEnum::eRemoveFirstCollisionVolume:
+ {
+ RemoveSubCollisionVolume( 0 );
+ }
+ break;
+ case StatePropEnum::eRemoveSecondCollisionVolume:
+ {
+ RemoveSubCollisionVolume( 1 );
+ }
+ break;
+ case StatePropEnum::eRemoveThirdCollisionVolume:
+ {
+ RemoveSubCollisionVolume( 2 );
+ }
+ break;
+ case StatePropEnum::eKillSpeed:
+ if ( mpSimStateObj != NULL )
+ {
+ mpSimStateObj->ResetVelocities();
+ mpSimStateObj->SetControl( sim::simAICtrl );
+ }
+ break;
+ case StatePropEnum::eRadiateForce:
+ {
+ const float FORCE_RADIUS = 1.5f;
+ ReserveArray< DynaPhysDSG* > dynamicsList( 200 );
+ // Alpha hack to make the head (jeb's statute) go into SIM but nothing else.
+ // Benches have bad volumes so they look stupid when they go into sim control
+ rmt::Vector forcePosition( 135.16f, 13.1979f, 37.911f );
+ GetIntersectManager()->FindDynaPhysElems( forcePosition, FORCE_RADIUS, dynamicsList );
+ for ( int i = 0 ; i < dynamicsList.mUseSize ; i++ )
+ {
+ if ( dynamicsList[i]->GetAIRef() != PhysicsAIRef::PlayerCharacter )
+ {
+ dynamicsList[i]->ApplyForce( rmt::Vector(0,1,0), 200.0f );
+ }
+ }
+ }
+ break;
+ case StatePropEnum::eEmitLeaves:
+ {
+
+ }
+ break;
+ case StatePropEnum::eSpawn5CoinsZ:
+ {
+ int numCoins = 5;
+ rmt::Vector coinDirection = -mTransform.Row(2);
+ GetCoinManager()->SpawnCoins( numCoins, rPosition(), rPosition().y, &coinDirection );
+ }
+ break;
+ case StatePropEnum::eColaDestroyed:
+ // A cola object (crate/vending machine) was destroyed. For some reason, people actually
+ // care about this. (Hit and Run meter I think) fire off an event
+ // signalling this
+ GetEventManager()->TriggerEvent( EVENT_COLAPROP_DESTROYED, this );
+ break;
+
+
+
+ default:
+ break;
+ };
+}
+
+void
+StatePropDSG::AddToSimulation()
+{
+ // Only valid if this object is flagged as moveable
+ if ( GetType() == PROP_MOVEABLE || GetType() == PROP_ONETIME_MOVEABLE)
+ {
+ if ( mpSimStateObj->GetControl() == sim::simAICtrl )
+ {
+ mpSimStateObj->SetControl(sim::simSimulationCtrl);
+ if ( mGroundPlaneIndex != -1 )
+ {
+ //mGroundPlaneIndex = FetchGroundPlane();
+ GetWorldPhysicsManager()->EnableGroundPlaneCollision(mGroundPlaneIndex);
+ }
+ //GetWorldPhysicsManager()->EnableGroundPlaneCollision(mGroundPlaneIndex);
+ GetEventManager()->TriggerEvent( EVENT_STATEPROP_ADDED_TO_SIM, this );
+ }
+ }
+}
+
+void
+StatePropDSG::ApplyForce( const rmt::Vector& direction, float force )
+{
+ if ( force >= 800.0f )
+ {
+ HandleEvent( StatePropDSG::STOMP );
+ }
+
+ if ( force > 400.0f )
+ {
+ HandleEvent( StatePropDSG::DESTROYED );
+ HandleEvent( StatePropDSG::HIT );
+ }
+ else
+ {
+ HandleEvent( StatePropDSG::HIT );
+ }
+ if ( GetType() != PROP_BREAKABLE )
+ {
+ DynaPhysDSG::ApplyForce( direction, force );
+ }
+}
+
+int
+StatePropDSG::CastsShadow()
+{
+ // StatepropDSGs can use a joint as a shadow
+ // if the m_ShadowElement variable is not -1, then
+ // we want to use the joint as a shadow
+ int retVal;
+ if ( m_ShadowElement == -1 )
+ retVal = 0;
+ else
+ retVal = 1;
+
+ return retVal;
+}
+
+void
+StatePropDSG::DisplaySimpleShadow()
+{
+ BEGIN_PROFILE("DisplaySimpleShadow")
+ p3d::pddi->SetZWrite(false);
+ if ( mpShadowMatrix != NULL )
+ {
+ rAssert( m_ShadowElement != -1 );
+ // Activate the joint's visibility
+ // DONT TOUCH THE LOCK SETTINGS, the stateprop will lock visibility
+ // off. Don't override the artists
+ tCompositeDrawable* compDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable() );
+ tCompositeDrawable::DrawableElement* element = compDraw->GetDrawableElement( m_ShadowElement );
+
+ element->SetVisibility( true );
+
+ // the camera
+ rmt::Vector camPos;
+ p3d::context->GetView()->GetCamera()->GetWorldPosition( &camPos );
+ camPos.Sub( mpShadowMatrix->Row(3) );
+ camPos.Normalize();
+ rmt::Matrix toCamera;
+ toCamera.Identity();
+ toCamera.FillTranslate( camPos * STATEPROP_SHADOW_Z_OFFSET );
+
+ // Final shadow transform = position/orientation * tocamera translation
+ rmt::Matrix shadowTransform( *mpShadowMatrix );
+ shadowTransform.Mult( toCamera );
+
+ // Display
+ p3d::stack->PushMultiply( shadowTransform );
+ element->Display();
+ p3d::stack->Pop();
+ element->SetVisibility( false );
+ }
+ else
+ {
+ tCompositeDrawable* compDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable() );
+ tCompositeDrawable::DrawableElement* element = compDraw->GetDrawableElement( m_ShadowElement );
+ compDraw->GetPose()->SetPoseReady( false );
+ compDraw->GetPose()->Evaluate( &mTransform );
+ const rmt::Matrix* worldMatrix = element->GetWorldMatrix();
+ mpShadowMatrix = CreateShadowMatrix( worldMatrix->Row(3) );
+ compDraw->GetPose()->SetPoseReady( false );
+ if ( mpShadowMatrix == NULL )
+ {
+
+ }
+
+ }
+ p3d::pddi->SetZWrite(true);
+ END_PROFILE("DisplaySimpleShadow")
+}
+
+void
+StatePropDSG::SetRank(rmt::Vector& irRefPosn, rmt::Vector& irRefVect)
+{
+ if ( CastsShadow() )
+ {
+ mRank = FLT_MAX;
+ }
+ else
+ {
+ IEntityDSG::SetRank( irRefPosn, irRefVect );
+ }
+}
+
+void
+StatePropDSG::SetShadowElement( tUID elementName )
+{
+ // Lets use the given joint as a shadow / light pool!
+ tCompositeDrawable* compDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable() );
+
+ m_ShadowElement = compDraw->FindNodeIndex( elementName );
+ rAssert( m_ShadowElement != -1 );
+ tCompositeDrawable::DrawableElement* element = compDraw->GetDrawableElement( m_ShadowElement );
+
+ element->SetPose( compDraw->GetPose() );
+ compDraw->GetPose()->SetPoseReady( false );
+ compDraw->GetPose()->Evaluate( &mTransform );
+ // Each drawable elements pose is set upon call to tCompositeDrawable::
+ const rmt::Matrix* worldMatrix = element->GetWorldMatrix();
+ rAssert( mpShadowMatrix == NULL );
+ mpShadowMatrix = CreateShadowMatrix( worldMatrix->Row(3) );
+
+ // set visibility to false so it won't be drawn as part of the regular pass
+ // Get the drawable element
+ element->SetVisibility( false );
+ compDraw->GetPose()->SetPoseReady( false );
+}
+
+
+// Turn collisions on and off. Pretty useful if you want to
+// attach stateprops to vehicles as collectibles
+// or when they get destroyed
+void
+StatePropDSG::EnableCollisionVolume( bool enable )
+{
+ sim::SimState* pSimState = GetSimState();
+ if ( pSimState == NULL )
+ return;
+
+ sim::CollisionObject* pCollisionObject = pSimState->GetCollisionObject();
+ if ( pCollisionObject == NULL )
+ return;
+
+ pCollisionObject->SetCollisionEnabled( enable );
+}
+
+void
+StatePropDSG::RemoveSubCollisionVolume( int volumeIndex )
+{
+ sim::SimState* pSimState = GetSimState();
+ rTuneAssert( pSimState != NULL );
+
+ sim::CollisionObject* pCollisionObject = pSimState->GetCollisionObject();
+ rTuneAssert( pCollisionObject != NULL );
+
+ // Find the collision volume, then grab its subcollisionvolume
+ sim::CollisionVolume* primaryVolume = pCollisionObject->GetCollisionVolume();
+
+ sim::TList<sim::CollisionVolume*>* pSubVolumeList = primaryVolume->SubVolumeList();
+ rTuneAssert( volumeIndex < pSubVolumeList->GetSize() && volumeIndex >= 0 );
+ if ( volumeIndex >= 0 && volumeIndex < pSubVolumeList->GetSize() )
+ {
+ sim::CollisionVolume* subVolume = pSubVolumeList->GetAt( volumeIndex );
+ rTuneAssert( subVolume != NULL );
+ if ( subVolume != NULL )
+ {
+ // Kill the subvolume
+ primaryVolume->RemoveSubVolume( subVolume );
+ }
+ }
+}
+
+int
+StatePropDSG::GetAIRef()
+{
+ return PhysicsAIRef::StateProp;
+}
+
+
+// Force an animation state change
+void
+StatePropDSG::SetState( int state )
+{
+ mpStateProp->SetState( state );
+}
+
+// Lets send this thing an event and hope that the artists
+// have everything set up to handle it
+bool
+StatePropDSG::HandleEvent( Event eventID )
+{
+ return mpStateProp->OnEvent( eventID );
+}
+
+unsigned int
+StatePropDSG::GetState()const
+{
+ return mpStateProp->GetState();
+}
+
+// Used if other people are interested in the messages generated from the
+// stateprop subsystem
+void
+StatePropDSG::AddStatePropListener( CStatePropListener* statePropListener )
+{
+ if ( statePropListener != NULL && mpStateProp != NULL )
+ {
+ mpStateProp->AddStatePropListener( statePropListener );
+ }
+}
+
+void
+StatePropDSG::RemoveStatePropListener( CStatePropListener* statePropListener )
+{
+ if ( statePropListener != NULL && mpStateProp != NULL )
+ {
+ mpStateProp->RemoveStatePropListener( statePropListener );
+ }
+}
+
+// Sets which joint the sim state should animate upon
+void
+StatePropDSG::SetSimJoint( int jointId )
+{
+ if ( GetType() != PROP_STATIC )
+ m_SimAnimJoint = jointId;
+
+}
+
+int
+StatePropDSG::GetType()const
+{
+ return GetCollisionAttributes()->GetClasstypeid();
+}
+
+// Turn on/off visibility
+void
+StatePropDSG::EnableVisibility( bool enable )
+{
+ // Iterate through the composite drawable and set visiblity on each one to
+ // the input
+ tCompositeDrawable* compDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable() );
+ rAssert( compDraw != NULL );
+ for ( int i = 0 ; i < compDraw->GetNumDrawableElement() ; i++ )
+ {
+ tCompositeDrawable::DrawableElement* element = compDraw->GetDrawableElement(i);
+ if ( element )
+ element->SetVisibility( enable );
+ }
+}
+
+
+void
+StatePropDSG::RemoveAllSharedtPoses()
+{
+
+ if ( sp_SharedtPoses )
+ {
+ PoseMapIt it;
+ // We addrefed each tPose when we put them onto the array, now release them
+ for ( it = sp_SharedtPoses->begin() ; it != sp_SharedtPoses->end() ; it++ )
+ {
+ it->second->Release();
+ }
+ delete sp_SharedtPoses;
+ sp_SharedtPoses = NULL;
+ }
+}
+
+// Uses m_SimJoint to animate the collision volume
+void
+StatePropDSG::AnimateCollisionVolume()
+{
+ rAssert( m_SimAnimJoint != -1 );
+ // We want to call SimState::SetTransform with the world space transform
+ // of the evaluated joint
+ // So first evaluate the composite drawables pose
+ tCompositeDrawable* compDraw = static_cast< tCompositeDrawable* >( mpStateProp->GetDrawable() );
+ rAssert( compDraw != NULL );
+ tPose* pose = compDraw->GetPose();
+ pose->Evaluate();
+ tPose::Joint* joint = pose->GetJoint( m_SimAnimJoint );
+ // Now grab the world matrix and apply it to the simstate
+ rmt::Matrix simTransform = mTransform;
+ simTransform.Row(3) += joint->worldMatrix.Row(3);
+
+ sim::SimState* simState = GetSimState();
+
+ // Before setting the transform, make sure to set manual updates to true
+ bool origManualUpdate = simState->GetCollisionObject()->IsManualUpdate();
+ simState->GetCollisionObject()->SetManualUpdate( false );
+
+ simState->SetTransform( simTransform );
+ simState->GetCollisionObject()->SetManualUpdate( origManualUpdate );
+
+ // Call reset velocities.
+ // Other the sim state is going to get a velocity based upon the distance I just moved it
+ simState->ResetVelocities();
+}
+
+
+tPose*
+StatePropDSG::GetSharedtPose( tUID type )
+{
+ tPose* pose;
+ PoseMapIt it = sp_SharedtPoses->find( type );
+ if ( it != sp_SharedtPoses->end() )
+ {
+ pose = it->second;
+ }
+ else
+ {
+ pose = NULL;
+ }
+ return pose;
+}
+
+// Add the given shared pose to the shared pose list
+void
+StatePropDSG::AddNewSharedtPose( tUID type, tPose* pose )
+{
+ if(sp_SharedtPoses->find(type) == sp_SharedtPoses->end())
+ {
+ pose->AddRef();
+ sp_SharedtPoses->insert( type, pose );
+ }
+ else
+ {
+ rAssert(0);
+ }
+}
diff --git a/game/code/render/DSG/StatePropDSG.h b/game/code/render/DSG/StatePropDSG.h
new file mode 100644
index 0000000..bb8287c
--- /dev/null
+++ b/game/code/render/DSG/StatePropDSG.h
@@ -0,0 +1,211 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: StatePropDSG
+//
+// Description: DSG object that contains a state prop
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef STATEPROPDSG_H
+#define STATEPROPDSG_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <render/dsg/DynaPhysDsg.h>
+#include <stateprop/stateprop.hpp>
+#include <loading/loadingmanager.h>
+#include <worldsim/physicsairef.h>
+#include <memory/map.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+namespace poser
+{
+ class Pose;
+};
+namespace sim
+{
+ class PhysicsObject;
+};
+
+class tEntityStore;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// A State Prop Object suited for entry into a DSG
+//
+// Constraints:
+//
+//
+//===========================================================================
+class StatePropDSGProcAnimator;
+
+class StatePropDSG : public DynaPhysDSG, public CStatePropListener
+{
+ public:
+
+ enum Event
+ {
+ IDLE,
+ FADE_IN,
+ FADE_OUT,
+ MOVING,
+ ATTACK_CHARGING,
+ ATTACK_CHARGED,
+ ATTACKING,
+ DESTROYED,
+ HIT,
+ FEATHER_TOUCH,
+ STOMP,
+ VEHICLE_HIT
+ };
+
+ StatePropDSG();
+ virtual ~StatePropDSG();
+
+ virtual void LoadSetup( CStatePropData* statePropData,
+ int startState,
+ const rmt::Matrix& transform,
+ CollisionAttributes* ipCollAttr,
+ bool isDynaLoaded = true,
+ tEntityStore* ipSearchStore = NULL,
+ bool useSharedtPose = false,
+ sim::CollisionObject* collisionObject = NULL,
+ sim::PhysicsObject* physicsObject = NULL );
+
+
+ StatePropDSG* Clone(const char* Name, const rmt::Matrix& iMatrix) const;
+
+
+ virtual void Display();
+ // Updates animation
+ virtual void AdvanceAnimation( float timeInMS );
+ // Updates physics
+ virtual void Update( float dt );
+
+ virtual void GetBoundingBox( rmt::Box3D* box );
+ virtual void GetBoundingSphere( rmt::Sphere* sphere );
+
+ // Dangerous function, assert on use
+ virtual rmt::Vector* pPosition();
+ virtual const rmt::Vector& rPosition();
+ virtual void GetPosition( rmt::Vector* ipPosn );
+ virtual void SetPosition( const rmt::Vector& position );
+ virtual void SetTransform( const rmt::Matrix& matrix );
+ virtual void GetTransform( rmt::Matrix* pMatrix );
+ // This will have to be changed, default to true for now
+ virtual bool HasAlpha()const { return true; }
+
+ virtual sim::Solving_Answer PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision );
+ virtual sim::Solving_Answer PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision);
+ virtual int GetAIRef();
+ virtual void RecieveEvent( int callback , CStateProp* stateProp );
+
+ virtual void AddToSimulation( void );
+ virtual void ApplyForce( const rmt::Vector& direction, float force );
+
+ virtual int CastsShadow();
+ virtual void DisplaySimpleShadow();
+ virtual void SetRank(rmt::Vector& irRefPosn, rmt::Vector& mViewVector);
+
+ // Sets which drawable element is the shadow/lightpool and should
+ // be projected on the ground and drawn only in the shadow pass
+ void SetShadowElement( tUID elementName );
+
+ // Turn collisions on and off. Used by the artist callback : REMOVE_COLLISION_VOLUME
+ void EnableCollisionVolume( bool enable );
+ // Kills a subvolume, called by callbacks of the same name
+ void RemoveSubCollisionVolume( int volumeIndex );
+
+ virtual void GenerateCoins( int numCoins );
+
+
+ void SetState( int state );
+ bool HandleEvent( Event );
+ unsigned int GetState()const;
+ tUID GetStatePropUID()const { return mpStateProp->GetUID(); }
+ tUID GetDrawableUID()const { return mpStateProp->GetDrawable()->GetUID(); }
+
+ void SetProcAnimator( StatePropDSGProcAnimator* Animator ) { mpProcAnimator = Animator; }
+ StatePropDSGProcAnimator* GetProcAnimator( void ) const { return mpProcAnimator; }
+
+ // Sets which joint the sim state should animate upon
+ void SetSimJoint( int jointId );
+
+ // Returns type of stateprop based upon CollisionAttributes
+ // either PROP_MOVEABLE / BREAKABLE / STATIC
+ int GetType()const;
+
+ // Add and remove a CStatePropListener
+ void AddStatePropListener( CStatePropListener* statePropListener );
+ void RemoveStatePropListener( CStatePropListener* statePropListener );
+
+ // Turn on/off visibility
+ void EnableVisibility( bool enable );
+
+ // Remove the shared tPose pool
+ static void RemoveAllSharedtPoses();
+
+ // Typedefs
+ typedef Map< tUID, tPose* > PoseMap;
+ typedef PoseMap::iterator PoseMapIt;
+
+ protected:
+
+ virtual void OnSetSimState( sim::SimState* ipCollObj );
+
+ rmt::Matrix mTransform;
+ CStateProp* mpStateProp;
+
+ sim::PhysicsObject* mpPhysObj;
+ bool m_IsDynaLoaded;
+ // The drawable element index
+ StatePropDSGProcAnimator* mpProcAnimator;
+ int m_ShadowElement;
+
+ // Which joint any sim animation is tied to
+ int m_SimAnimJoint;
+
+ // Uses m_SimJoint to animate the collision volume
+ void AnimateCollisionVolume( );
+
+ // Memory optimizations: optionally make the tPose shared amongst
+ // all elements of that stateprop type
+ // Find a tPose from the given stateprop type
+ // If none exist, then NULL is returned
+ static tPose* GetSharedtPose( tUID type );
+ // Add the given shared pose to the shared pose list
+ static void AddNewSharedtPose( tUID uid, tPose* );
+
+ // List of all tPoses, indexed by stateprop type UID
+ // So that the tCompositeDrawables don't have to allocate a new tPose
+ // every time.
+ static PoseMap* sp_SharedtPoses;
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow StatePropDSG from being copied and assigned.
+ StatePropDSG( const StatePropDSG& );
+ StatePropDSG& operator=( const StatePropDSG& );
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/DSG/StaticEntityDSG.cpp b/game/code/render/DSG/StaticEntityDSG.cpp
new file mode 100644
index 0000000..298a4e6
--- /dev/null
+++ b/game/code/render/DSG/StaticEntityDSG.cpp
@@ -0,0 +1,414 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: StaticEntityDSG.cpp
+//
+// Description: Implementation for StaticEntityDSG class.
+//
+// History: Implemented --Devin [5/27/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/StaticEntityDSG.h>
+#include <memory/srrmemory.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : StaticEntityDSG Interface
+//
+//************************************************************************
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+StaticEntityDSG::StaticEntityDSG()
+{
+ mpDrawstuff = NULL;
+}
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+StaticEntityDSG::~StaticEntityDSG()
+{
+BEGIN_PROFILE( "StaticEntityDSG Destroy" );
+ if(mpDrawstuff != NULL)
+ {
+ mpDrawstuff->Release();
+ }
+END_PROFILE( "StaticEntityDSG Destroy" );
+}
+//========================================================================
+// StaticEntityDSG::SetRank
+//========================================================================
+//
+// Description: Sets rank, defaults to default SetRank for normal geo
+// however, shadows always get drawn first in the translucent pass
+//
+// Parameters: rmt::Vector& irRefPosn, rmt::Vector& mViewVector.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticEntityDSG::SetRank(rmt::Vector& irRefPosn, rmt::Vector& mViewVector)
+{
+ if ( ( mIsGeo & IS_SHADOW ) == false )
+ {
+ IEntityDSG::SetRank( irRefPosn, mViewVector );
+ }
+ else
+ {
+ mRank = FLT_MAX;
+ }
+}
+
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticEntityDSG::SetGeometry(tGeometry* ipGeo)
+{
+ if(mpDrawstuff != NULL)
+ {
+ mpDrawstuff->Release();
+ }
+
+ mpDrawstuff = ipGeo;
+
+ if(mpDrawstuff != NULL)
+ {
+ mpDrawstuff->AddRef();
+ }
+ mIsGeo = GEO;
+
+ if(ipGeo->CastsShadow())
+ {
+ mIsGeo = mIsGeo | IS_SHADOW;
+ }
+
+// mShaderUID = ipGeo->GetShader(0)->GetUID();
+ ipGeo->ProcessShaders(*this);
+
+ SetInternalState();
+}
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tGeometry* StaticEntityDSG::mpGeo()
+{
+ return (tGeometry*)mpDrawstuff;
+}
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticEntityDSG::SetDrawable(tDrawable* ipDraw)
+{
+ if(mpDrawstuff != NULL)
+ {
+ mpDrawstuff->Release();
+ }
+
+ mpDrawstuff = ipDraw;
+
+ if(mpDrawstuff != NULL)
+ {
+ mpDrawstuff->AddRef();
+ }
+
+ mIsGeo = NOT_GEO;
+
+ SetInternalState();
+}
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tDrawable* StaticEntityDSG::mpDraw()
+{
+ return mpDrawstuff;
+
+}
+
+///////////////////////////////////////////////////////////////////////
+// Drawable
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticEntityDSG::Display()
+{
+#ifdef PROFILER_ENABLED
+ char profileName[] = " StaticEntityDSG Display";
+#endif
+ if(IS_DRAW_LONG) return;
+ DSG_BEGIN_PROFILE(profileName)
+
+ if(mIsGeo & IS_SHADOW)
+ {
+ p3d::pddi->SetZWrite(false);
+ mpDrawstuff->Display();
+ p3d::pddi->SetZWrite(true);
+ }
+ else
+ {
+ mpDrawstuff->Display();
+ }
+ DSG_END_PROFILE(profileName)
+}
+
+#ifndef RAD_RELEASE
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticEntityDSG::DisplayBoundingBox(tColour colour)
+{
+#ifndef RAD_RELEASE
+ mpDrawstuff->DisplayBoundingBox(colour);
+#endif
+}
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticEntityDSG::DisplayBoundingSphere(tColour colour)
+{
+ mpDrawstuff->DisplayBoundingSphere(colour);
+}
+#endif
+
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticEntityDSG::GetBoundingBox(rmt::Box3D* box)
+{
+ mpDrawstuff->GetBoundingBox(box);
+}
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticEntityDSG::GetBoundingSphere(rmt::Sphere* sphere)
+{
+ mpDrawstuff->GetBoundingSphere(sphere);
+}
+///////////////////////////////////////////////////////////////////////
+// IEntityDSG
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+rmt::Vector* StaticEntityDSG::pPosition()
+{
+ return &mPosn;
+}
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+const rmt::Vector& StaticEntityDSG::rPosition()
+{
+ return mPosn;
+}
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticEntityDSG::GetPosition( rmt::Vector* ipPosn )
+{
+ *ipPosn = mPosn;
+}
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticEntityDSG::RenderUpdate()
+{
+ //Do Nothing
+}
+//************************************************************************
+//
+// Protected Member Functions : StaticEntityDSG
+//
+//************************************************************************
+//========================================================================
+// StaticEntityDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticEntityDSG::SetInternalState()
+{
+ rmt::Sphere sphere;
+
+ mpDrawstuff->GetBoundingSphere(&sphere);
+ mPosn = sphere.centre;
+}
+//************************************************************************
+//
+// Private Member Functions : StaticEntityDSG
+//
+//************************************************************************
+
+
diff --git a/game/code/render/DSG/StaticEntityDSG.h b/game/code/render/DSG/StaticEntityDSG.h
new file mode 100644
index 0000000..4d845dc
--- /dev/null
+++ b/game/code/render/DSG/StaticEntityDSG.h
@@ -0,0 +1,94 @@
+#ifndef __StaticEntityDSG_H__
+#define __StaticEntityDSG_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: StaticEntityDSG
+//
+// Description: The StaticEntityDSG does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/27]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <p3d/p3dtypes.hpp>
+#include <p3d/Geometry.hpp>
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/DSG/IEntityDSG.h>
+#include <p3d/shader.hpp>
+//========================================================================
+//
+// Synopsis: The StaticEntityDSG; Synopsis by Inspection.
+//
+//========================================================================
+class StaticEntityDSG : public IEntityDSG
+{
+public:
+ StaticEntityDSG();
+ ~StaticEntityDSG();
+
+ void SetGeometry(tGeometry* ipGeo);
+ tGeometry* mpGeo();
+ void SetDrawable(tDrawable* ipDraw);
+ tDrawable* mpDraw();
+ virtual void SetRank(rmt::Vector& irRefPosn, rmt::Vector& mViewVector);
+ ///////////////////////////////////////////////////////////////////////
+ // Drawable
+ ///////////////////////////////////////////////////////////////////////
+ void Display();
+
+ #ifndef RAD_RELEASE
+ void DisplayBoundingBox(tColour colour = tColour(0,255,0));
+ void DisplayBoundingSphere(tColour colour = tColour(0,255,0));
+ #endif
+
+ void GetBoundingBox(rmt::Box3D* box);
+ void GetBoundingSphere(rmt::Sphere* sphere);
+
+ ///////////////////////////////////////////////////////////////////////
+ // IEntityDSG
+ ///////////////////////////////////////////////////////////////////////
+ rmt::Vector* pPosition();
+ const rmt::Vector& rPosition();
+ void GetPosition( rmt::Vector* ipPosn );
+
+ void RenderUpdate();
+
+ virtual void SetShader(tShader* pShader, int i)
+ {
+ if(mIsGeo)
+ {
+ ((tGeometry*)mpDrawstuff)->SetShader(i,pShader);
+ }
+ }
+
+protected:
+ void SetInternalState();
+
+ rmt::Vector mPosn;
+
+ //tGeometry* mpGeometry;
+ // Downcasting with extreme prejudice; these objects will be used
+ // responsibly, and with foreknowledge of type. It's ugly.
+ // But it's also almost midnight
+ // MS 11
+ enum
+ {
+ NOT_GEO = 0x0,
+ GEO = 0x1,
+ IS_SHADOW = 0x2
+ };
+
+ int mIsGeo;
+ tDrawable* mpDrawstuff;
+private:
+};
+
+#endif
diff --git a/game/code/render/DSG/StaticPhysDSG.cpp b/game/code/render/DSG/StaticPhysDSG.cpp
new file mode 100644
index 0000000..c12a340
--- /dev/null
+++ b/game/code/render/DSG/StaticPhysDSG.cpp
@@ -0,0 +1,594 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: StaticPhysDSG.cpp
+//
+// Description: Implementation for StaticPhysDSG class.
+//
+// History: Implemented --Devin [5/27/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/camera.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/view.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <simcollision/collisionvolume.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/StaticPhysDSG.h>
+#include <render/particles/particlemanager.h>
+#include <render/breakables/breakablesmanager.h>
+#include <render/IntersectManager/IntersectManager.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+// Bias that determines how much force is required to emit particles during a
+// collision
+const float STAT_PHYS_MASS_IMPULSE_PARTICLE_BIAS = 10.0f;
+
+//************************************************************************
+//
+// Public Member Functions : StaticPhysDSG Interface
+//
+//************************************************************************
+
+//========================================================================
+// StaticPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+StaticPhysDSG::StaticPhysDSG() :
+mpShadow( NULL ),
+mpShadowMatrix( NULL )
+{
+ mpSimStateObj = NULL;
+}
+
+//========================================================================
+// StaticPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+StaticPhysDSG::~StaticPhysDSG()
+{
+BEGIN_PROFILE( "StaticPhysDSG Destroy" );
+ if(mpSimStateObj != NULL)
+ {
+ mpSimStateObj->Release();
+ }
+ if (mpShadow)
+ {
+ mpShadow->Release();
+ mpShadow = 0;
+ }
+ if (mpShadowMatrix != NULL )
+ {
+ delete mpShadowMatrix;
+ mpShadowMatrix = NULL;
+ }
+END_PROFILE( "StaticPhysDSG Destroy" );
+}
+//========================================================================
+// StaticPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticPhysDSG::OnSetSimState( sim::SimState* ipSimState )
+{
+ tRefCounted::Assign( mpSimStateObj, ipSimState );
+
+ //mpSimStateObj->mAIRefIndex = StaticPhysDSG::GetAIRef();
+ mpSimStateObj->mAIRefIndex = this->GetAIRef();
+
+ SetInternalState();
+}
+//========================================================================
+// StaticPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+sim::SimState* StaticPhysDSG::GetSimState() const
+{
+ return mpSimStateObj;
+}
+///////////////////////////////////////////////////////////////////////
+// Drawable
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// StaticPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticPhysDSG::Display()
+{
+ if(IS_DRAW_LONG) return;
+#ifdef PROFILER_ENABLED
+ char profileName[] = " StaticPhysDSG Display";
+#endif
+ DSG_BEGIN_PROFILE(profileName)
+ //Currently unsupported. Contact Devin.
+ //rAssert(false);
+ //Do nothing, but allow inst stat phys to render their pGeo's
+ DSG_END_PROFILE(profileName)
+}
+//========================================================================
+// StaticPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticPhysDSG::DisplayBoundingBox(tColour colour)
+{
+#ifndef RAD_RELEASE
+ //Currently unsupported. Contact Devin.
+ //rAssert(false);
+ pddiPrimStream* stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.low.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.low.z);
+ p3d::pddi->EndPrims(stream);
+
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.low.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.high.z);
+ p3d::pddi->EndPrims(stream);
+
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.low.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.low.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.high.z);
+ p3d::pddi->EndPrims(stream);
+
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.high.z);
+ p3d::pddi->EndPrims(stream);
+#endif
+}
+//========================================================================
+// StaticPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticPhysDSG::DisplayBoundingSphere(tColour colour)
+{
+ //Currently unsupported. Contact Devin.
+ rAssert(false);
+}
+//========================================================================
+// StaticPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticPhysDSG::GetBoundingBox(rmt::Box3D* box)
+{
+ (*box) = mBBox;
+}
+//========================================================================
+// StaticPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticPhysDSG::GetBoundingSphere(rmt::Sphere* sphere)
+{
+ (*sphere) = mSphere;
+}
+
+///////////////////////////////////////////////////////////////////////
+// IEntityDSG
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// StaticPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+rmt::Vector* StaticPhysDSG::pPosition()
+{
+ return &mPosn;
+}
+//========================================================================
+// StaticPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+const rmt::Vector& StaticPhysDSG::rPosition()
+{
+ return mPosn;
+}
+//========================================================================
+// StaticPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticPhysDSG::GetPosition( rmt::Vector* ipPosn )
+{
+ *ipPosn = mPosn;
+}
+//========================================================================
+// StaticPhysDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+
+
+
+void StaticPhysDSG::RenderUpdate()
+{
+ //do Nothing
+}
+
+
+//************************************************************************
+//
+// Protected Member Functions : StaticPhysDSG
+//
+//************************************************************************
+void StaticPhysDSG::SetInternalState()
+{
+ mPosn = mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mPosition;
+
+ mBBox.low = mPosn;
+ mBBox.high = mPosn;
+ mBBox.high += mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+ mBBox.low -= mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+
+ mSphere.centre.Sub(mBBox.high,mBBox.low);
+ mSphere.centre *= 0.5f;
+ mSphere.centre.Add(mBBox.low);
+ mSphere.radius = mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mSphereRadius;
+}
+
+
+
+
+
+//=============================================================================
+// StaticPhysDSG::PreReactToCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+//
+// Return: sim
+//
+//=============================================================================
+sim::Solving_Answer StaticPhysDSG::PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+{
+ return sim::Solving_Continue;
+}
+
+
+//=============================================================================
+// StaticPhysDSG::PostReactToCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+//
+// Return: sim
+//
+//=============================================================================
+sim::Solving_Answer StaticPhysDSG::PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision)
+{
+
+ // subclass-specific shit here
+
+ // If it is a breakable object and has an assicated particle animation with it (it should)
+ // play the associated particle effect, if the impulse magnitude is greater than a certain threshold
+
+ if ( mpCollisionAttributes != NULL )
+ {
+ float threshold = STAT_PHYS_MASS_IMPULSE_PARTICLE_BIAS * mpCollisionAttributes->GetMass();
+ if( impulse.MagnitudeSqr() > (threshold*threshold) )
+ {
+ if (mpCollisionAttributes->GetParticle() != ParticleEnum::eNull )
+ {
+ ParticleAttributes attr;
+ attr.mType = mpCollisionAttributes->GetParticle();
+ GetParticleManager()->Add( attr, inCollision.GetPositionA() );
+ }
+ }
+ }
+ return CollisionEntityDSG::PostReactToCollision(impulse, inCollision);
+}
+
+
+
+void StaticPhysDSG::SetShadow( tDrawable* ipShadow )
+{
+ tRefCounted::Assign( mpShadow, ipShadow );
+
+ if ( ipShadow != NULL )
+ {
+ // Hang onto the shadow drawable
+ rAssert( mpShadowMatrix == NULL );
+ mpShadowMatrix = CreateShadowMatrix( rPosition() );
+ }
+
+}
+
+rmt::Matrix* StaticPhysDSG::CreateShadowMatrix( const rmt::Vector& objectPosition )
+{
+ rmt::Matrix* pResult;
+ rmt::Matrix shadowMat;
+ if ( ComputeShadowMatrix( objectPosition, &shadowMat ) )
+ {
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ pResult = new rmt::Matrix();
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER);
+ *pResult = shadowMat;
+
+ }
+ else
+ {
+ pResult = NULL ;
+ }
+ return pResult;
+}
+
+void StaticPhysDSG::RecomputeShadowPosition( float height_radius_bias )
+{
+ if ( mpShadowMatrix )
+ {
+ rmt::Vector position;
+ GetPosition( &position );
+ ComputeShadowMatrix( position, mpShadowMatrix );
+ }
+}
+
+void StaticPhysDSG::RecomputeShadowPositionNoIntersect( float height, const rmt::Vector& normal, float height_radius_bias, float scale )
+{
+ if ( mpShadowMatrix )
+ {
+ rmt::Vector position;
+ GetPosition( &position );
+ rmt::Vector shadowPosition( position.x, height, position.z );
+
+
+ mpShadowMatrix->Identity();
+ mpShadowMatrix->FillTranslate( shadowPosition );
+ rmt::Vector worldRight( 1,0,0 );
+ rmt::Vector forward;
+ forward.CrossProduct( worldRight, normal );
+ mpShadowMatrix->FillHeading( forward, normal );
+/*
+ if ( height_radius_bias != 1.0f )
+ {
+ // scale = (objheight - groundY) * bias
+ float matrixScale = 1.0f - ( position.y - shadowPosition.y ) * height_radius_bias;
+ matrixScale *= scale;
+ if ( matrixScale < 0.0f )
+ {
+ matrixScale = 0.0f;
+ }
+ mpShadowMatrix->FillScale( matrixScale );
+ }*/
+ }
+}
+
+void StaticPhysDSG::DisplaySimpleShadow()
+{
+ p3d::pddi->SetZWrite(false);
+ BEGIN_PROFILE("DisplaySimpleShadow")
+ rAssert( mpShadow != NULL );
+
+ // Translate the shadow towards the camera slightly, instead of moving it off the
+ //ground in the direction of the the ground normal. Hopefully this will cause less distortion of the shadow.
+
+ if ( mpShadow != NULL && mpShadowMatrix != NULL )
+ {
+
+ // Create a camera that pushes the shadow a meter towards
+ // the camera
+ rmt::Vector camPos;
+ p3d::context->GetView()->GetCamera()->GetWorldPosition( &camPos );
+ camPos.Sub( mpShadowMatrix->Row(3) );
+ camPos.Normalize();
+ // Distance to raise the object
+ const float Z_FIGHTING_OFFSET = 1.0f;
+ camPos.Scale( Z_FIGHTING_OFFSET );
+
+ // Final shadow transform = position/orientation * tocamera translation
+ rmt::Matrix shadowTransform( *mpShadowMatrix );
+ shadowTransform.Row( 3 ).Add( camPos );
+
+ // Display
+ p3d::stack->PushMultiply( shadowTransform );
+ mpShadow->Display();
+ p3d::stack->Pop();
+ }
+ else
+ {
+ mpShadowMatrix = CreateShadowMatrix( rPosition() );
+ }
+ END_PROFILE("DisplaySimpleShadow")
+ p3d::pddi->SetZWrite(true);
+}
+
+
+//************************************************************************
+//
+// Private Member Functions : StaticPhysDSG
+//
+//************************************************************************
+
+
+bool
+StaticPhysDSG::ComputeShadowMatrix( const rmt::Vector& in_position, rmt::Matrix* out_pMatrix )
+{
+ // Determine where our shadow casting object intersects the ground plane
+ rmt::Vector groundNormal(0,1,0);
+ rmt::Vector groundPlaneIntersectionPoint;
+
+ const float INTERSECT_TEST_RADIUS = 10.0f;
+ bool foundPlane;
+ rmt::Vector deepestIntersectPos, deepestIntersectNormal;
+
+ // Get rid of the fact that FindIntersection doesn't want a const value
+ // and I'm above casting away constness
+
+ rmt::Vector searchPosition = in_position;
+ searchPosition.y += 10.0f;
+
+ GetIntersectManager()->FindIntersection( searchPosition,
+ foundPlane,
+ groundNormal,
+ groundPlaneIntersectionPoint );
+
+ if ( foundPlane )
+ {
+ out_pMatrix->Identity();
+ out_pMatrix->FillTranslate( groundPlaneIntersectionPoint );
+ rmt::Vector worldRight( 1,0,0 );
+ rmt::Vector forward;
+ forward.CrossProduct( worldRight, groundNormal );
+ out_pMatrix->FillHeading( forward, groundNormal );
+
+ }
+ return foundPlane;
+
+}
+
diff --git a/game/code/render/DSG/StaticPhysDSG.h b/game/code/render/DSG/StaticPhysDSG.h
new file mode 100644
index 0000000..43bf999
--- /dev/null
+++ b/game/code/render/DSG/StaticPhysDSG.h
@@ -0,0 +1,96 @@
+#ifndef __StaticPhysDSG_H__
+#define __StaticPhysDSG_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: StaticPhysDSG
+//
+// Description: The StaticPhysDSG does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/27]
+//
+//========================================================================
+
+#include <render/DSG/collisionentitydsg.h>
+#include <worldsim/physicsairef.h>
+
+//========================================================================
+//
+// Synopsis: The StaticPhysDSG; Synopsis by Inspection.
+//
+//========================================================================
+class StaticPhysDSG : public CollisionEntityDSG
+{
+public:
+ StaticPhysDSG();
+ ~StaticPhysDSG();
+
+ // Implements ICollisionEntityDSG.
+ //
+ virtual sim::Solving_Answer PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision );
+ virtual sim::Solving_Answer PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision);
+
+ virtual sim::SimState* GetSimState() const;
+ virtual sim::SimState* mpSimState() const { return GetSimState(); }
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // Drawable
+ ///////////////////////////////////////////////////////////////////////
+ void Display();
+
+ void DisplayBoundingBox(tColour colour = tColour(0,255,0));
+ void DisplayBoundingSphere(tColour colour = tColour(0,255,0));
+
+ virtual void GetBoundingBox(rmt::Box3D* box);
+ virtual void GetBoundingSphere(rmt::Sphere* sphere);
+
+ // Inherited from IEntityDSG. Whether or not the object casts a shadow
+ virtual int CastsShadow(){ if(mpShadow != NULL ) return 1; else return 0;}
+ virtual void SetShadow( tDrawable* ipShadow );
+ virtual void RecomputeShadowPosition( float height_radius_bias = 1.0f );
+ // like recompute shadow position, but avoids expensive ground intersect test
+ // because the ground height is passed in as a parameter
+ virtual void RecomputeShadowPositionNoIntersect( float height, const rmt::Vector& normal, float height_radius_bias = 1.0f, float scale = 1.0f );
+ virtual void DisplaySimpleShadow();
+
+ ///////////////////////////////////////////////////////////////////////
+ // IEntityDSG
+ ///////////////////////////////////////////////////////////////////////
+ rmt::Vector* pPosition();
+ const rmt::Vector& rPosition();
+ void GetPosition( rmt::Vector* ipPosn );
+
+ void RenderUpdate();
+
+ virtual int GetAIRef() {return PhysicsAIRef::redBrickPhizStatic;}
+
+ /*
+ static int GetAIRef( void )
+ {
+ return PhysicsAIRef::redBrickPhizStatic;
+ }
+ */
+
+protected:
+ virtual void OnSetSimState( sim::SimState* ipSimState );
+
+ virtual void SetInternalState( );
+
+ rmt::Box3D mBBox;
+ rmt::Sphere mSphere;
+ rmt::Vector mPosn;
+ sim::SimState* mpSimStateObj;
+
+ tDrawable* mpShadow;
+ // Transform the shadow by this matrix to
+ // place it in the world
+ rmt::Matrix* mpShadowMatrix;
+
+ rmt::Matrix* CreateShadowMatrix( const rmt::Vector& objectPosition );
+ bool ComputeShadowMatrix( const rmt::Vector& in_position, rmt::Matrix* out_pMatrix );
+
+};
+
+#endif
diff --git a/game/code/render/DSG/TriStripDSG.cpp b/game/code/render/DSG/TriStripDSG.cpp
new file mode 100644
index 0000000..011edc0
--- /dev/null
+++ b/game/code/render/DSG/TriStripDSG.cpp
@@ -0,0 +1,352 @@
+#include <p3d/utility.hpp>
+#include <render/DSG/tristripdsg.h>
+//===========================================================================
+//
+// Description:
+// Encapsulates a vertex buffer (non-indexed, size allocated once on creation)
+// Derived from StaticEntityDSG and intended for insertion into the DSG
+//
+// Constraints:
+// tShader is a hack. Need to be able to set the shader in a function somewhere
+// pddi definitions may be inappropriate, use tColour and equivalents instead?
+//
+//===========================================================================
+
+
+//===========================================================================
+// TriStripDSG::TriStripDSG
+//===========================================================================
+// Description:
+// TriStripDSG constructor
+//
+// Constraints:
+// Cannot change the maximum size of the vertex buffer, ever (prevents fragmentation)
+// Tristrippool has functionality to end a TriStripDSG and start a new one automatically
+// when it runs out of space
+//
+// Parameters:
+// Number of vertices to preallocate. Vertices are 24 bytes each + padding
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+
+TriStripDSG::TriStripDSG( int maxVertices )
+:mpShader( NULL )
+{
+ IEntityDSG::mTranslucent = true;
+ mMaxVertices = maxVertices;
+ mpVertices = new TriStripDSG::Vertex[ mMaxVertices ];
+ mNumVertices = 0;
+}
+
+//===========================================================================
+// TriStripDSG::~TriStripDSG
+//===========================================================================
+// Description:
+// TriStripDSG dtor. Frees all memory allocated in the ctor.
+//
+// Constraints:
+//
+// Parameters:
+// None.
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+
+TriStripDSG::~TriStripDSG()
+{
+BEGIN_PROFILE( "TriStripDSG Destroy" );
+ delete [] mpVertices;
+ mpVertices = NULL;
+ if( mpShader != NULL )
+ {
+ mpShader->Release();
+ }
+END_PROFILE( "TriStripDSG Destroy" );
+}
+//===========================================================================
+// TriStripDSG::GetVertex
+//===========================================================================
+// Description:
+// Returns the information for a vertex in the array.
+//
+// Constraints:
+// Assertion failure if index isn't valid or not filled out.
+//
+// Parameters:
+// const reference (careful) to the internal vertex structure
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+const TriStripDSG::Vertex& TriStripDSG::GetVertex( int index ) const
+{
+ rAssert( index >=0 && index < mNumVertices );
+ return mpVertices[ index ];
+}
+
+//===========================================================================
+// TriStripDSG::Display
+//===========================================================================
+// Description:
+// Overloaded Display() function from DSG parent
+//
+// Constraints:
+//
+//
+// Parameters:
+// None
+//
+// Return:
+// None.
+//
+//===========================================================================
+void TriStripDSG::Display()
+{
+ if(IS_DRAW_LONG) return;
+#ifdef PROFILER_ENABLED
+ char profileName[] = " TriStripDSG Display";
+#endif
+ DSG_BEGIN_PROFILE(profileName)
+
+ if ( mNumVertices > 0 )
+ {
+
+ // We will hack this stuff in for use with the skid marks
+ // subtractive blending
+ // rAssert( mpShader != NULL );
+
+ if( mpShader != NULL )
+ {
+ pddiPrimStream* pStream = p3d::pddi->BeginPrims( mpShader->GetShader(), PDDI_PRIM_TRISTRIP, PDDI_V_CT, mNumVertices );
+
+ for (int i = 0 ; i < mNumVertices ; ++i)
+ {
+ pStream->Vertex( &mpVertices[ i ].vertex, mpVertices[ i ].colour, &mpVertices[ i ].uv );
+ }
+ p3d::pddi->EndPrims( pStream );
+ }
+ }
+ DSG_END_PROFILE(profileName)
+}
+//===========================================================================
+// TriStripDSG::Clear
+//===========================================================================
+// Description:
+// Resets number of vertices to zero.
+//
+// Constraints:
+// Doesnt free vertex buffer
+//
+// Parameters:
+// None
+//
+// Return:
+// None.
+//
+//===========================================================================
+void TriStripDSG::Clear()
+{
+ mNumVertices = 0;
+ mBoundingBox = rmt::Box3D();
+}
+//===========================================================================
+// TriStripDSG::SetShader
+//===========================================================================
+// Description:
+// Sets the shader to use with the strip
+//
+// Constraints:
+//
+//
+// Parameters:
+// None
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+void TriStripDSG::SetShader( tShader* pShader)
+{
+ if ( pShader != mpShader )
+ {
+ if( pShader != NULL)
+ {
+ pShader->AddRef();
+ }
+ if( mpShader != NULL)
+ {
+ mpShader->Release();
+ }
+ mpShader = pShader;
+ }
+}
+//===========================================================================
+// TriStripDSG::AddVertex
+//===========================================================================
+// Description:
+// Adds a vertex the strip.
+//
+// Constraints:
+// Assertion if not enough space. Use IsSpaceLeft() to check whether or not to call
+// the function
+//
+// Parameters:
+// Position, colour, uv coordinates
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+void TriStripDSG::AddVertex( const pddiVector& vertex, pddiColour colour, const pddiVector2& uv )
+{
+ rAssert( IsSpaceLeft() == true );
+ mpVertices[ mNumVertices ].vertex = vertex;
+ mpVertices[ mNumVertices ].colour = colour;
+ mpVertices[ mNumVertices ].uv = uv;
+
+ ++mNumVertices;
+
+ // Adjust bounding box based upon the new vertex position.
+ mBoundingBox.Expand( vertex );
+ mPosition = mBoundingBox.Mid();
+}
+//===========================================================================
+// TriStripDSG::AddVertex
+//===========================================================================
+// Description:
+// Adds a new vertex
+//
+// Constraints:
+// Assertion if the strip has run out of room
+//
+// Parameters:
+// TriStripDSG::Vertex structure.
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+void TriStripDSG::AddVertex( const Vertex& vertex )
+{
+ rAssert( IsSpaceLeft() == true );
+ mpVertices[ mNumVertices ] = vertex;
+ ++mNumVertices;
+
+ // Adjust bounding box based upon the new vertex position.
+ mBoundingBox.Expand( vertex.vertex );
+ mPosition = mBoundingBox.Mid();
+}
+//===========================================================================
+// TriStripDSG::SetVertex
+//===========================================================================
+// Description:
+// Modifies an existing vertex
+//
+// Constraints:
+// Assertion if vertex does not exist
+//
+// Parameters:
+// position, colour, and texture coordinates
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+void TriStripDSG::SetVertex( int index, const pddiVector& vertex, pddiColour colour, const pddiVector2& uv )
+{
+ rAssert( index >=0 && index < mNumVertices );
+ mpVertices[ index ].vertex = vertex;
+ mpVertices[ index ].colour = colour;
+ mpVertices[ index ].uv = uv;
+
+ // Adjust bounding box based upon the new vertex position.
+ mBoundingBox.Expand( vertex );
+ mPosition = mBoundingBox.Mid();
+}
+
+//===========================================================================
+// TriStripDSG::DisplayBoundingBox
+//===========================================================================
+// Description:
+// Draws the bounding box associated with the object
+//
+// Constraints:
+// Debugging only
+//
+// Parameters:
+// Bounding box colour
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+void TriStripDSG::DisplayBoundingBox( tColour colour )
+{
+#ifndef RAD_RELEASE
+ pddiPrimStream* stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.low.x, mBoundingBox.low.y, mBoundingBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.high.x, mBoundingBox.low.y, mBoundingBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.high.x, mBoundingBox.high.y, mBoundingBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.low.x, mBoundingBox.high.y, mBoundingBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.low.x, mBoundingBox.low.y, mBoundingBox.low.z);
+ p3d::pddi->EndPrims(stream);
+
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.high.x, mBoundingBox.high.y, mBoundingBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.low.x, mBoundingBox.high.y, mBoundingBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.low.x, mBoundingBox.low.y, mBoundingBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.high.x, mBoundingBox.low.y, mBoundingBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.high.x, mBoundingBox.high.y, mBoundingBox.high.z);
+ p3d::pddi->EndPrims(stream);
+
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.high.x, mBoundingBox.high.y, mBoundingBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.high.x, mBoundingBox.low.y, mBoundingBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.high.x, mBoundingBox.low.y, mBoundingBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.high.x, mBoundingBox.high.y, mBoundingBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.high.x, mBoundingBox.high.y, mBoundingBox.high.z);
+ p3d::pddi->EndPrims(stream);
+
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.low.x, mBoundingBox.high.y, mBoundingBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.low.x, mBoundingBox.low.y, mBoundingBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.low.x, mBoundingBox.low.y, mBoundingBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.low.x, mBoundingBox.high.y, mBoundingBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBoundingBox.low.x, mBoundingBox.high.y, mBoundingBox.high.z);
+ p3d::pddi->EndPrims(stream);
+#endif
+} \ No newline at end of file
diff --git a/game/code/render/DSG/TriStripDSG.h b/game/code/render/DSG/TriStripDSG.h
new file mode 100644
index 0000000..7d1699c
--- /dev/null
+++ b/game/code/render/DSG/TriStripDSG.h
@@ -0,0 +1,146 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: tristripdsg
+//
+// Description: A triangle strip that can be inserted into the DSG,
+// rendered using the (slow) pddiPrimStream, but
+// able to get modified after it is created and inserted
+// Intended for skid marks
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+
+#ifndef TRISTRIPDSG_H
+#define TRISTRIPDSG_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <render/dsg/staticentitydsg.h>
+#include <p3d/shader.hpp>
+#include <memory/srrmemory.h>
+//===========================================================================
+//
+// Description:
+// Encapsulates a vertex buffer (non-indexed, size allocated once on creation)
+// Derived from StaticEntityDSG and intended for insertion into the DSG
+//
+// Constraints:
+// tShader is a hack. Need to be able to set the shader in a function somewhere
+// pddi definitions may be inappropriate, use tColour and equivalents instead?
+//
+//===========================================================================
+
+class TriStripDSG : public StaticEntityDSG
+{
+public:
+
+ // Ctor, input the number of vertices to preallocate
+ TriStripDSG( int maxVertices );
+ virtual ~TriStripDSG();
+
+ virtual void SetShader(tShader* pShader, int i)
+ {
+ }
+
+ // Format of the vertices stored in the buffer, 24 bytes per vertex (assuming no padding)
+ struct Vertex
+ {
+ pddiVector vertex;
+ pddiColour colour;
+ pddiVector2 uv;
+ };
+
+ virtual void GetBoundingBox( rmt::Box3D* box )
+ {
+ *box = mBoundingBox;
+ }
+ virtual void GetBoundingSphere( rmt::Sphere* sphere )
+ {
+ *sphere = mBoundingBox.GetBoundingSphere();
+ }
+ virtual void GetPosition(rmt::Vector *ipPosn)
+ {
+ *ipPosn = mPosition;
+ }
+ virtual rmt::Vector* pPosition()
+ {
+ return &mPosition;
+ }
+ virtual const rmt::Vector& rPosition()
+ {
+ return mPosition;
+ }
+ // Obsolete function?
+ virtual void RenderUpdate()
+ {
+ rAssert( false );
+ }
+ // Draw the strip using a pddiPrimStream
+ virtual void Display();
+
+ // Draw the strips bounding box
+ virtual void DisplayBoundingBox( tColour colour = tColour( 255, 0, 0 ) );
+
+ // Eliminate all vertices (well, really just sets the current vertex back to zero
+ // no deallocations take place
+ void Clear();
+
+ // Add a new vertex, seperate component format
+ void AddVertex( const pddiVector& vertex, pddiColour colour, const pddiVector2& uv );
+
+ // Add a new vertex, TriStripDSG::Vertex format
+ void AddVertex( const Vertex& vertex );
+
+ void SetVertex( int index, const pddiVector& vertex, pddiColour colour, const pddiVector2& uv );
+
+ void SetShader( tShader* pShader );
+ tShader* GetShader() const { return mpShader; }
+ void SetColour( int index, tColour colour )
+ {
+ rAssert( index >= 0 && index < mNumVertices );
+ mpVertices[ index ].colour = colour;
+ }
+
+ // Retrieve the vertex at the specified index
+ const Vertex& GetVertex( int index ) const;
+
+
+ // Number of vertices in the vertex buffer
+ inline int Size()const { return mNumVertices; }
+
+ // Can we insert more vertices in the list
+ inline bool IsSpaceLeft() const
+ {
+ return mNumVertices < mMaxVertices;
+ }
+
+private:
+
+ // Disable default constructor
+ TriStripDSG();
+
+ tShader* mpShader;
+
+ // Vertex buffer
+ Vertex* mpVertices;
+
+ // Encloses all vertices in the strip, extended when new vertices added
+ rmt::Box3D mBoundingBox;
+ // Center of the bounding box
+ rmt::Vector mPosition;
+
+ // Size of vertex buffer, all or none of these may actually hold valid vertices
+ int mMaxVertices;
+ // Number of vertices that are valid, number to render on Display()
+ int mNumVertices;
+
+};
+
+#endif
+
diff --git a/game/code/render/DSG/WorldSphereDSG.cpp b/game/code/render/DSG/WorldSphereDSG.cpp
new file mode 100644
index 0000000..ec71670
--- /dev/null
+++ b/game/code/render/DSG/WorldSphereDSG.cpp
@@ -0,0 +1,487 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: WorldSphereDSG.cpp
+//
+// Description: Implementation for WorldSphereDSG class.
+//
+// History: Implemented --Devin [5/27/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/WorldSphereDSG.h>
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/view.hpp>
+#include <render/animentitydsgmanager/animentitydsgmanager.h>
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : WorldSphereDSG Interface
+//
+//************************************************************************
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+WorldSphereDSG::WorldSphereDSG()
+: mbActive(FALSE),
+mpCompDraw( NULL ),
+mpMultiCon( NULL ),
+mpFlare( NULL )
+{
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+WorldSphereDSG::~WorldSphereDSG()
+{
+BEGIN_PROFILE( "WorldSphereDSG Destroy" );
+ int i;
+ for(i=mpGeos.mUseSize-1; i>-1; i--)
+ {
+ mpGeos[i]->Release();
+ }
+ for (i=mpBillBoards.mUseSize - 1 ; i > -1 ; i--)
+ {
+ mpBillBoards[i]->Release();
+ }
+ if ( mpCompDraw != NULL )
+ {
+ mpCompDraw->Release();
+ }
+
+ if(mpMultiCon)
+ {
+ mpMultiCon->Release();
+ GetAnimEntityDSGManager()->Remove( mpMultiCon );
+ }
+ if( mpFlare )
+ {
+ mpFlare->Release();
+ }
+END_PROFILE( "WorldSphereDSG Destroy" );
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::Activate()
+{
+ mbActive = TRUE;
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::Deactivate()
+{
+ mbActive = FALSE;
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::SetNumMeshes(int iNumMeshes)
+{
+ if ( iNumMeshes > 0 )
+ {
+ mpGeos.Allocate(iNumMeshes);
+ }
+}
+
+void WorldSphereDSG::SetNumBillBoardQuadGroups( int iNumGroups )
+{
+ if (iNumGroups > 0)
+ {
+ mpBillBoards.Allocate( iNumGroups );
+ }
+}
+
+void WorldSphereDSG::SetFlare( LensFlareDSG* pFlare )
+{
+ rAssert( mpFlare == NULL );
+ rAssert( pFlare != NULL );
+ mpFlare = pFlare;
+ mpFlare->AddRef();
+}
+
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::AddMesh(tGeometry* ipGeo)
+{
+ mpGeos.Add(ipGeo);
+ ipGeo->AddRef();
+
+ SetInternalState();
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::AddBillBoardQuadGroup( tBillboardQuadGroup* pGroup )
+{
+ mpBillBoards.Add( pGroup );
+ pGroup->AddRef();
+}
+
+//========================================================================
+// WorldSphereDSG::SetCompositeDrawable
+//========================================================================
+//
+// Description: Sets the single composite drawable
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::SetCompositeDrawable( tCompositeDrawable* ipCompDraw )
+{
+ mpCompDraw = ipCompDraw;
+ mpCompDraw->AddRef();
+ rmt::Sphere sphere;
+ mpCompDraw->GetBoundingSphere( &sphere );
+ mPosn = sphere.centre;
+}
+
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::SetMultiController(tMultiController* ipMultiController)
+{
+ mpMultiCon = ipMultiController;
+ mpMultiCon->AddRef();
+ mpMultiCon->SetCycleMode( FORCE_CYCLIC );
+ GetAnimEntityDSGManager()->Add( mpMultiCon );
+}
+///////////////////////////////////////////////////////////////////////
+// Drawable
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::Display()
+{
+
+#ifdef PROFILER_ENABLED
+ char profileName[] = " WorldSphereDSG Display";
+#endif
+ DSG_BEGIN_PROFILE(profileName)
+ if( mbActive )
+ {
+ int i;
+ // Translate the worldsphere to the camera position
+ tCamera* pCurrentCamera = p3d::context->GetView()->GetCamera();
+ rAssert( pCurrentCamera != NULL );
+ rmt::Vector cameraPosition;
+ pCurrentCamera->GetWorldPosition( &cameraPosition );
+
+ rmt::Matrix toCameraPosition;
+ toCameraPosition.Identity();
+ toCameraPosition.FillTranslate( cameraPosition );
+
+ p3d::stack->PushMultiply( toCameraPosition );
+
+ if ( mpCompDraw != NULL )
+ {
+ mpCompDraw->Display();
+ }
+ else
+ {
+ for(i=0; i<mpGeos.mUseSize; i++)
+ {
+ mpGeos[i]->Display();
+ }
+ }
+ // Pop the toCamera matrix transformation
+ p3d::stack->Pop();
+ }
+ if (mpFlare)
+ {
+ mpFlare->Display();
+ }
+ DSG_END_PROFILE(profileName)
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::DisplayBoundingBox(tColour colour)
+{
+ rTuneAssert(false);
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::DisplayBoundingSphere(tColour colour)
+{
+ rTuneAssert(false);
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::GetBoundingBox(rmt::Box3D* box)
+{
+ rTuneAssert(false);
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::GetBoundingSphere(rmt::Sphere* sphere)
+{
+ rTuneAssert(false);
+}
+///////////////////////////////////////////////////////////////////////
+// IEntityDSG
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+rmt::Vector* WorldSphereDSG::pPosition()
+{
+ rTuneAssert(false);
+ return NULL;
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+const rmt::Vector& WorldSphereDSG::rPosition()
+{
+ rTuneAssert(false);
+ return mPosn;
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::GetPosition( rmt::Vector* ipPosn )
+{
+ rTuneAssert(false);
+}
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::RenderUpdate()
+{
+ //Do Nothing
+}
+//************************************************************************
+//
+// Protected Member Functions : WorldSphereDSG
+//
+//************************************************************************
+//========================================================================
+// WorldSphereDSG::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereDSG::SetInternalState()
+{
+ rmt::Sphere sphere;
+ mpGeos[0]->GetBoundingSphere(&sphere);
+ mPosn = sphere.centre;
+}
+//************************************************************************
+//
+// Private Member Functions : WorldSphereDSG
+//
+//************************************************************************
+
+
diff --git a/game/code/render/DSG/WorldSphereDSG.h b/game/code/render/DSG/WorldSphereDSG.h
new file mode 100644
index 0000000..fa25cb6
--- /dev/null
+++ b/game/code/render/DSG/WorldSphereDSG.h
@@ -0,0 +1,89 @@
+#ifndef __WorldSphereDSG_H__
+#define __WorldSphereDSG_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: WorldSphereDSG
+//
+// Description: The WorldSphereDSG does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/27]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <p3d/p3dtypes.hpp>
+#include <p3d/billboardobject.hpp>
+#include <p3d/Geometry.hpp>
+#include <p3d/anim/multicontroller.hpp>
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/DSG/IEntityDSG.h>
+#include <render/Culling/SwapArray.h>
+#include <render/DSG/LensFlareDSG.h>
+
+
+class tCompositeDrawable;
+
+//========================================================================
+//
+// Synopsis: The WorldSphereDSG; Synopsis by Inspection.
+//
+//========================================================================
+class WorldSphereDSG : public IEntityDSG
+{
+public:
+ WorldSphereDSG();
+ ~WorldSphereDSG();
+
+ void AddMesh(tGeometry* ipGeo);
+ void AddBillBoardQuadGroup( tBillboardQuadGroup* );
+ void SetNumMeshes(int iNumMeshes);
+ void SetNumBillBoardQuadGroups( int iNumGroups );
+ void SetMultiController(tMultiController* ipMultiController);
+ void SetCompositeDrawable( tCompositeDrawable* ipCompDraw );
+ void SetFlare( LensFlareDSG* pFlare );
+ void Activate();
+ void Deactivate();
+
+ ///////////////////////////////////////////////////////////////////////
+ // Drawable
+ ///////////////////////////////////////////////////////////////////////
+ void Display();
+
+ void DisplayBoundingBox(tColour colour = tColour(0,255,0));
+ void DisplayBoundingSphere(tColour colour = tColour(0,255,0));
+
+ void GetBoundingBox(rmt::Box3D* box);
+ void GetBoundingSphere(rmt::Sphere* sphere);
+
+ ///////////////////////////////////////////////////////////////////////
+ // IEntityDSG
+ ///////////////////////////////////////////////////////////////////////
+ rmt::Vector* pPosition();
+ const rmt::Vector& rPosition();
+ void GetPosition( rmt::Vector* ipPosn );
+
+ void RenderUpdate();
+
+
+protected:
+ void SetInternalState();
+
+ bool mbActive;
+ rmt::Vector mPosn;
+ tCompositeDrawable* mpCompDraw;
+ SwapArray<tGeometry*> mpGeos;
+ SwapArray<tBillboardQuadGroup*> mpBillBoards;
+ tMultiController* mpMultiCon;
+ LensFlareDSG* mpFlare;
+
+private:
+};
+
+#endif
diff --git a/game/code/render/DSG/alldsg.cpp b/game/code/render/DSG/alldsg.cpp
new file mode 100644
index 0000000..939dd3f
--- /dev/null
+++ b/game/code/render/DSG/alldsg.cpp
@@ -0,0 +1,18 @@
+#include <render/DSG/animcollisionentitydsg.cpp>
+#include <render/DSG/animentitydsg.cpp>
+#include <render/DSG/collisionentitydsg.cpp>
+#include <render/DSG/DSGFactory.cpp>
+#include <render/DSG/DynaPhysDSG.cpp>
+#include <render/DSG/FenceEntityDSG.cpp>
+#include <render/DSG/IEntityDSG.cpp>
+#include <render/DSG/InstDynaPhysDSG.cpp>
+#include <render/DSG/InstStatEntityDSG.cpp>
+#include <render/DSG/InstStatPhysDSG.cpp>
+#include <render/DSG/IntersectDSG.cpp>
+#include <render/DSG/StaticEntityDSG.cpp>
+#include <render/DSG/StaticPhysDSG.cpp>
+#include <render/DSG/WorldSphereDSG.cpp>
+#include <render/DSG/breakableobjectdsg.cpp>
+#include <render/DSG/LensFlareDSG.cpp>
+#include <render/DSG/InstAnimDynaPhysDSG.cpp>
+#include <render/DSG/StatePropDSG.cpp> \ No newline at end of file
diff --git a/game/code/render/DSG/animcollisionentitydsg.cpp b/game/code/render/DSG/animcollisionentitydsg.cpp
new file mode 100644
index 0000000..05425da
--- /dev/null
+++ b/game/code/render/DSG/animcollisionentitydsg.cpp
@@ -0,0 +1,595 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: animcollisionentitydsg.cpp
+//
+// Description: Implementation of class AnimCollisionEntityDSG
+//
+// History: 05/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/anim/poseanimation.hpp>
+#include <p3d/anim/animate.hpp>
+#include <p3d/anim/multicontroller.hpp>
+
+#include <simcommon/simstatearticulated.hpp>
+#include <simcollision/collisionobject.hpp>
+
+#include <poser/poseengine.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/animcollisionentitydsg.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/Culling/WorldScene.h>
+
+#include <worldsim/redbrick/rootmatrixdriver.h>
+#include <worldsim/physicsairef.h>
+#include <worldsim/worldobject.h>
+
+#include <ai/actionbuttonhandler.h>
+
+#include <simcollision/collisiondisplay.hpp>
+#include <simcommon/simutility.hpp>
+#include <simcollision/collisionvolume.hpp>
+#include <simcollision/collisionobject.hpp>
+
+#include <events/eventmanager.h>
+#include <events/eventdata.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Table of objects with sounds (but no sound in CollisionAttributes)
+//
+
+struct AnimEntitySoundData
+{
+ radInt64 animEntityUID;
+ const char* soundName;
+ unsigned int jointNumber;
+ const char* settingName;
+};
+
+
+static const AnimEntitySoundData s_animSoundTable[] =
+{
+ { tEntity::MakeUID( "Splatform2Trans" ), "platform_02", 1, "platform_settings" },
+ { tEntity::MakeUID( "Aplatform1Trans" ), "platform_01", 1, "platform_settings" },
+ { tEntity::MakeUID( "Aplatform2Trans" ), "platform_01", 1, "platform_settings" },
+ { tEntity::MakeUID( "trapdoor1Trans" ), "platform_01", 1, "platform_settings" },
+ { tEntity::MakeUID( "crane" ), "platform_02", 3, "crane_settings" },
+ { tEntity::MakeUID( "roboarm" ), "robo_arm", 1, "platform_settings" }
+};
+
+static unsigned int s_animSoundTableSize = sizeof( s_animSoundTable ) / sizeof( AnimEntitySoundData );
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// AnimCollisionEntityDSG::AnimCollisionEntityDSG
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AnimCollisionEntityDSG::AnimCollisionEntityDSG()
+:
+mfDirection( 1.0f ),
+mpActionButton( 0 )
+{
+}
+
+//==============================================================================
+// AnimCollisionEntityDSG::~AnimCollisionEntityDSG
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AnimCollisionEntityDSG::~AnimCollisionEntityDSG()
+{
+BEGIN_PROFILE( "AnimColl Destroy" );
+
+ tRefCounted::Release( mpDrawable );
+ tRefCounted::Release( mpPoseEngine );
+ tRefCounted::Release( mpRootMatrixDriver );
+ tRefCounted::Release( mpSimStateArticulated );
+ tRefCounted::Release( mpAnimController );
+ if ( mpActionButton )
+ {
+ mpActionButton->Destroy( );
+ tRefCounted::Release( mpActionButton );
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_STOP_ANIM_ENTITY_DSG_SOUND, this );
+
+END_PROFILE( "AnimColl Destroy" );
+}
+/*
+==============================================================================
+AnimCollisionEntityDSG::Create
+==============================================================================
+Description: Comment
+
+Parameters: ( const char* objectName, const char* animName )
+
+Return: bool
+
+=============================================================================
+*/
+bool AnimCollisionEntityDSG::Create( const char* objectName, const char* animName )
+{
+ rAssert( false );
+ return false;
+}
+//========================================================================
+// animcollisionentitydsg::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void AnimCollisionEntityDSG::LoadSetUp
+(
+ tCompositeDrawable* ipCompD,
+ tMultiController* ipAnimFC,
+ sim::CollisionObject* ipCollObject,
+ tEntityStore* ipStore
+)
+{
+ mpDrawable = ipCompD;
+ mpAnimController = ipAnimFC;
+
+ rAssert( mpDrawable );
+ rAssert(mpAnimController);
+ rAssert( ipCollObject );
+ if(mpDrawable && mpAnimController && ipCollObject )
+ {
+ mpDrawable->AddRef();
+ mpAnimController->AddRef();
+ mTransform.Identity( );
+ tPose* p3dPose = mpDrawable->GetPose();
+ p3dPose->Evaluate();
+ mTransform = p3dPose->GetJoint( 0 )->worldMatrix;
+
+ mpPoseEngine = new poser::PoseEngine( p3dPose, 1, p3dPose->GetNumJoint() );
+ mpPoseEngine->AddRef();
+
+ mpRootMatrixDriver = new RootMatrixDriver(&(mTransform));
+ mpRootMatrixDriver->AddRef(); // TODO - not really doing proper cleanup on this shit
+ mpPoseEngine->AddPoseDriver(0, mpRootMatrixDriver );
+
+
+ // The section stuff here is a hack.
+ // Tracked to ATG as bug 1259.
+ //
+ p3d::inventory->PushSection ();
+ p3d::inventory->AddSection (SKELCACHE);
+ p3d::inventory->SelectSection (SKELCACHE);
+ mpSimStateArticulated = sim::SimStateArticulated::CreateSimStateArticulated( mpPoseEngine->GetPose(), ipCollObject, (sim::SimulatedObject*)0 );
+ p3d::inventory->PopSection ();
+
+ rAssert( mpSimStateArticulated );
+
+ mpSimStateArticulated->AddRef();
+ mpSimStateArticulated->mAIRefIndex = PhysicsAIRef::redBrickPhizMoveableAnim;
+ mpSimStateArticulated->mAIRefPointer = (void*)this;
+ // Manually set this because physics seems to be pretty confused.
+ //
+ // TODO: fix this, because now physics system will generate collision pairs
+ // for this object with other animcollentity.
+ //
+ // TBJ [8/28/2002]
+ //
+ mpSimStateArticulated->GetCollisionObject()->SetIsStatic( false );
+
+ // This will init the pose, and set the bounding boxes.
+ //
+ UpdatePose( 0.0f );
+ }
+
+ mpDrawable->ProcessShaders(*this);
+
+ //
+ // Now that we're set up, see if we're supposed to play a sound -- Esan
+ //
+ findSoundName();
+}
+/*
+==============================================================================
+AnimCollisionEntityDSG::SetAction
+==============================================================================
+Description: Comment
+
+Parameters: ( ActionButton::AnimSwitch* pActionButton )
+
+Return: void
+
+=============================================================================
+*/
+void AnimCollisionEntityDSG::SetAction( ActionButton::AnimSwitch* pActionButton )
+{
+ tRefCounted::Assign( mpActionButton, pActionButton );
+}
+//========================================================================
+// animcollisionentitydsg::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void AnimCollisionEntityDSG::GetBoundingBox(rmt::Box3D* box)
+{
+ *box = mBoundingBox;
+}
+//========================================================================
+// animcollisionentitydsg::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void AnimCollisionEntityDSG::GetBoundingSphere(rmt::Sphere* sphere)
+{
+ *sphere = mBoundingSphere;
+}
+
+/*
+==============================================================================
+AnimCollisionEntityDSG::Display
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: void
+
+=============================================================================
+*/
+void AnimCollisionEntityDSG::Display()
+{
+ if(IS_DRAW_LONG) return;
+#ifdef PROFILER_ENABLED
+ char profileName[] = " AnimCollisionEntityDSG Display";
+#endif
+ DSG_BEGIN_PROFILE(profileName)
+ mpDrawable->Display();
+ DSG_END_PROFILE(profileName)
+}
+/*
+==============================================================================
+AnimCollisionEntityDSG::pPosition
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: rmt
+
+=============================================================================
+*/
+rmt::Vector* AnimCollisionEntityDSG::pPosition()
+{
+ return (rmt::Vector*)(mTransform.m[3]);
+}
+
+/*
+==============================================================================
+AnimCollisionEntityDSG::rPosition
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: const
+
+=============================================================================
+*/
+const rmt::Vector& AnimCollisionEntityDSG::rPosition()
+{
+ return mTransform.Row( 3 );
+}
+
+/*
+==============================================================================
+AnimCollisionEntityDSG::GetPosition
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector* ipPosn )
+
+Return: void
+
+=============================================================================
+*/
+void AnimCollisionEntityDSG::GetPosition( rmt::Vector* ipPosn )
+{
+ *ipPosn = mTransform.Row(3);
+}
+
+void AnimCollisionEntityDSG::AdvanceAnimation( float timeins )
+{
+ mpAnimController->Advance( timeins * 1000.0f * mfDirection );
+}
+
+
+/*
+==============================================================================
+AnimCollisionEntityDSG::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void AnimCollisionEntityDSG::Update( float timeins )
+{
+ if ( mpActionButton )
+ {
+ mpActionButton->Update( timeins );
+ }
+ // Store this before we update the transform so the scenegraph can
+ // find 'this'.
+ //
+ rmt::Box3D oldBox;
+ GetBoundingBox( &oldBox );
+
+ // We only need to update when the animation is actually playing.
+ //
+ if ( mfDirection != 0 )
+ {
+ UpdatePose( timeins );
+ }
+ // Move the object in the scenegraph.
+ //
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mRenderLayer ));
+ // Sanity check
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Move( oldBox, this );
+
+}
+/*
+==============================================================================
+AnimCollisionEntityDSG::UpdatePose
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void AnimCollisionEntityDSG::UpdatePose( float timeins )
+{
+ // advance the animation and update transform!
+ //
+ //mpAnimController->Advance( timeins * 1000.0f * mfDirection );
+ this->mTransform = mpDrawable->GetPose()->GetJoint( 0 )->worldMatrix;//objectMatrix;
+
+ mpSimStateArticulated->StoreJointState( timeins );
+ mpPoseEngine->Begin(false);
+ int i;
+
+ for (i = 0; i < mpPoseEngine->GetPassCount(); i++)
+ {
+ mpPoseEngine->Advance(i, timeins );
+ mpPoseEngine->Update(i);
+ }
+ mpPoseEngine->End(); // copy over what we're gonna render
+
+ mpSimStateArticulated->UpdateJointState( timeins );
+
+ sim::CollisionObject* pObject = mpSimStateArticulated->GetCollisionObject();
+ pObject->Update();
+
+ UpdateBBox( pObject->GetCollisionVolume( ) );
+}
+/*
+==============================================================================
+AnimCollisionEntityDSG::UpdateBBox
+==============================================================================
+Description: Comment
+
+Parameters: ( sim::CollisionVolume* pVolume )
+
+Return: void
+
+=============================================================================
+*/
+void AnimCollisionEntityDSG::UpdateBBox( sim::CollisionVolume* pVolume )
+{
+ // Use the BBox and BSphere from the CollisionVolume to
+ // update the drawable BBOX and BSphere.
+ //
+ mBoundingSphere.centre = pVolume->mPosition;
+ mBoundingSphere.radius = pVolume->mSphereRadius;
+
+ rmt::Vector position = pVolume->mPosition;
+ rmt::Vector size = pVolume->mBoxSize;
+ mBoundingBox.high.Add( position, size );
+ size.Scale( -1.0f );
+ mBoundingBox.low.Add( position, size );
+}
+/*
+==============================================================================
+AnimCollisionEntityDSG::UpdateVisibility
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void AnimCollisionEntityDSG::UpdateVisibility( void )
+{
+ mpSimStateArticulated->GetCollisionObject( )->SetVisibility( mpDrawable );
+}
+/*
+==============================================================================
+AnimCollisionEntityDSG::PreReactToCollision
+==============================================================================
+Description: Comment
+
+Parameters: ( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+
+Return: bool
+
+=============================================================================
+*/
+sim::Solving_Answer AnimCollisionEntityDSG::PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+{
+ return sim::Solving_Continue;
+}
+
+
+
+//=============================================================================
+// AnimCollisionEntityDSG::PostReactToCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+//
+// Return: sim
+//
+//=============================================================================
+sim::Solving_Answer AnimCollisionEntityDSG::PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision)
+{
+
+ // subclass-specific shit here
+
+ return CollisionEntityDSG::PostReactToCollision(impulse, inCollision);
+}
+
+
+
+
+/*
+==============================================================================
+AnimCollisionEntityDSG::GetPoseJoint
+==============================================================================
+Description: Comment
+
+Parameters: ( int jointIndex, bool bAttachToJoint )
+
+Return: tPose
+
+=============================================================================
+*/
+tPose::Joint* AnimCollisionEntityDSG::GetPoseJoint( int jointIndex, bool bAttachToJoint )
+{
+ tPose::Joint* pJoint = 0;
+ if ( jointIndex >= 0 )
+ {
+ pJoint = mpPoseEngine->GetP3DPose( )->GetJoint( jointIndex );// GetPose( )->GetJoint( jointIndex );
+ }
+ // If not found, try the root.
+ //
+ if ( pJoint == (tPose::Joint*)0 && !bAttachToJoint )
+ {
+ pJoint = mpPoseEngine->GetP3DPose( )->GetJoint( 0 );// mpPoseEngine->GetPose( )->GetJoint( 0 );
+ }
+ rAssert( pJoint );
+
+ return pJoint;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+void AnimCollisionEntityDSG::OnSetSimState( sim::SimState* ipCollObj )
+{
+}
+
+//=============================================================================
+// AnimCollisionEntityDSG::findSoundName
+//=============================================================================
+// Description: Platforms messed up the usual sound drill. Because they don't
+// have entries in the art DB, we need to search a table to find
+// out whether this anim entity has a sound that the sound system
+// needs to know about.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void AnimCollisionEntityDSG::findSoundName()
+{
+ unsigned int i;
+ const char* soundName;
+ tUID myName = GetUID();
+
+ soundName = NULL;
+
+ for( i = 0; i < s_animSoundTableSize; i++ )
+ {
+ if( myName == static_cast< tUID >( s_animSoundTable[i].animEntityUID ) )
+ {
+ soundName = s_animSoundTable[i].soundName;
+ break;
+ }
+ }
+
+ if( soundName != NULL )
+ {
+ //
+ // We've got sound, tell the sound system
+ //
+ AnimSoundDSGData data( soundName,
+ this,
+ GetPoseJoint( s_animSoundTable[i].jointNumber, false ),
+ s_animSoundTable[i].settingName );
+ GetEventManager()->TriggerEvent( EVENT_START_ANIM_ENTITY_DSG_SOUND, &data );
+ }
+} \ No newline at end of file
diff --git a/game/code/render/DSG/animcollisionentitydsg.h b/game/code/render/DSG/animcollisionentitydsg.h
new file mode 100644
index 0000000..aceaf89
--- /dev/null
+++ b/game/code/render/DSG/animcollisionentitydsg.h
@@ -0,0 +1,183 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: animcollisionentitydsg.h
+//
+// Description: Blahblahblah
+//
+// History: 05/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef ANIMCOLLLISIONENTITYDSG_H
+#define ANIMCOLLLISIONENTITYDSG_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/refcounted.hpp>
+#include <p3d/anim/pose.hpp>
+#include <radmath/radmath.hpp>
+
+#include <render/DSG/collisionentitydsg.h>
+#include <worldsim/physicsairef.h>
+
+
+//========================================
+// Forward References
+//========================================
+class tCompositeDrawable;
+class tMultiController;
+class tAnimation;
+class RootMatrixDriver;
+//class tPose::Joint; this doesn't compile on ps2
+
+
+namespace sim
+{
+ class SimStateArticulated;
+ class CollisionVolume;
+ class CollisionObject;
+};
+
+namespace poser
+{
+ class PoseEngine;
+ class Joint;
+};
+
+namespace ActionButton
+{
+ class AnimSwitch;
+};
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class AnimCollisionEntityDSG
+:
+public CollisionEntityDSG
+{
+public:
+ AnimCollisionEntityDSG( void );
+ ~AnimCollisionEntityDSG( void );
+
+ bool Create( const char* objectName, const char* animName );
+
+ void AdvanceAnimation( float timeins );
+ void Update( float timeins );
+
+ void UpdateVisibility( void );
+
+ virtual sim::Solving_Answer PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision );
+ virtual sim::Solving_Answer PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision);
+
+ virtual void Display();
+ virtual rmt::Vector* pPosition() ;
+ virtual const rmt::Vector& rPosition() ;
+ virtual void GetPosition( rmt::Vector* ipPosn );
+
+ tCompositeDrawable* GetDrawable( void ) const
+ {
+ return mpDrawable;
+ }
+
+ sim::SimStateArticulated* GetSimState( void ) const
+ {
+ return mpSimStateArticulated;
+ }
+
+ tMultiController* GetAnimController( void ) const
+ {
+ return mpAnimController;
+ }
+
+ poser::PoseEngine* GetPoseEngine( void ) const
+ {
+ return mpPoseEngine;
+ }
+
+ RootMatrixDriver* GetRootMatrixDriver( void ) const
+ {
+ return mpRootMatrixDriver;
+ }
+
+ void SetTransform( const rmt::Matrix& transform )
+ {
+ mTransform = transform;
+ }
+ rmt::Matrix* GetTransformPointer( void )
+ {
+ return &mTransform;
+ }
+
+ rmt::Matrix& GetTransformRef( void )
+ {
+ return mTransform;
+ }
+
+ tPose::Joint* GetPoseJoint( int jointIndex, bool bAttachToJoint );
+
+ float& GetAnimationDirection( void )
+ {
+ return mfDirection;
+ }
+ void SetAnimationDirection( float fDirection )
+ {
+ mfDirection = fDirection;
+ }
+
+ void SetAction( ActionButton::AnimSwitch* pActionButton );
+ //////////////////////////////////////////////////////////////////////////
+ // tDrawable
+ //////////////////////////////////////////////////////////////////////////
+ void GetBoundingBox(rmt::Box3D* box);
+ void GetBoundingSphere(rmt::Sphere* sphere);
+
+ //////////////////////////////////////////////////////////////////////////
+ // Load Interface
+ //////////////////////////////////////////////////////////////////////////
+ void LoadSetUp(tCompositeDrawable* pCompD, tMultiController* pAnimFC, sim::CollisionObject* pCollObject, tEntityStore* ipStore );
+
+ //
+ // Implement pure virtual function from CollisionEntityDSG
+ //
+ int GetAIRef() { return( PhysicsAIRef::redBrickPhizMoveableAnim ); }
+
+protected:
+ virtual void OnSetSimState( sim::SimState* ipCollObj );
+ void UpdatePose( float timeins );
+ void UpdateBBox( sim::CollisionVolume* pVolume );
+private:
+ void findSoundName();
+
+ tCompositeDrawable* mpDrawable;
+ sim::SimStateArticulated* mpSimStateArticulated;
+
+ tMultiController* mpAnimController;
+
+ poser::PoseEngine* mpPoseEngine;
+ RootMatrixDriver* mpRootMatrixDriver;
+
+ rmt::Matrix mTransform;
+
+ //Prevent wasteful constructor creation.
+ AnimCollisionEntityDSG( const AnimCollisionEntityDSG& AnimCollisionEntityDSG );
+ AnimCollisionEntityDSG& operator=( const AnimCollisionEntityDSG& AnimCollisionEntityDSG );
+ float mfDirection;
+
+ ActionButton::AnimSwitch* mpActionButton;
+ // We need to store our own bounding boxes and spheres.
+ // the tCompositeDrawable's are stored in world space,
+ // but they are not updated when an object animates.
+ // we will update these boxes once per update.
+ //
+ rmt::Box3D mBoundingBox;
+ rmt::Sphere mBoundingSphere;
+};
+
+
+#endif //ANIMCOLLLISIONENTITYDSG_H
diff --git a/game/code/render/DSG/animentitydsg.cpp b/game/code/render/DSG/animentitydsg.cpp
new file mode 100644
index 0000000..2ec94dc
--- /dev/null
+++ b/game/code/render/DSG/animentitydsg.cpp
@@ -0,0 +1,570 @@
+//===========================================================================
+// Copyright (C) 1999 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: animentitydsg
+//
+// Description: Animated object without collision detection for use in Devin's
+// scene graph
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <render/dsg/animentitydsg.h>
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/anim/animate.hpp>
+#include <p3d/anim/animatedobject.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <render/RenderManager/RenderManager.h>
+#include <render/Culling/WorldScene.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/IntersectManager/IntersectManager.h>
+#include <p3d/effects/particlesystem.hpp>
+#include <main/game.h>
+#include <mission/gameplaymanager.h>
+
+#include <radtime.hpp>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+const int MAX_NUM_PARTICLE_SYSTEMS = 10;
+
+static unsigned sUpdateIndex = 0;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// AnimEntityDSG::AnimEntityDSG
+//===========================================================================
+// Description:
+// AnimEntityDSG constructor
+//
+// Constraints:
+// None.
+//
+// Parameters:
+// None.
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+AnimEntityDSG::AnimEntityDSG()
+:
+mbAddToUpdateList( true ),
+mTrackSeparately( 0 ),
+mpDrawable( NULL ),
+mpMultiController( NULL ),
+mIsVisible( true ),
+mIsAnimationPlaying( true ),
+mRenderLayer( RenderEnums::LevelSlot ),
+mParticleSystemBoundingBox( NULL ),
+m_TimeLastParticleUpdate(0),
+mTimeSinceLastUpdate(0.0f),
+updateIndex(sUpdateIndex++)
+{
+ mEffectElements.Allocate( MAX_NUM_PARTICLE_SYSTEMS );
+
+}
+
+//===========================================================================
+// AnimEntityDSG::~AnimEntityDSG
+//===========================================================================
+// Description:
+// AnimEntityDSG destructor
+//
+// Constraints:
+// None.
+//
+// Parameters:
+// None.
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+AnimEntityDSG::~AnimEntityDSG()
+{
+BEGIN_PROFILE( "Anim Destroy" );
+ if (mpDrawable != NULL)
+ {
+ mpDrawable->Release();
+ mpDrawable = NULL;
+ }
+ if (mpMultiController != NULL)
+ {
+ mpMultiController->Release();
+ mpMultiController = NULL;
+ }
+ if ( mParticleSystemBoundingBox != NULL )
+ {
+ delete mParticleSystemBoundingBox;
+ mParticleSystemBoundingBox = NULL;
+ }
+
+END_PROFILE( "Anim Destroy" );
+
+}
+//===========================================================================
+// AnimEntityDSG::Display
+//===========================================================================
+// Description:
+// Draws the composite drawable object
+//
+// Constraints:
+// None.
+//
+// Parameters:
+// None.
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+void AnimEntityDSG::Display()
+{
+ if(IS_DRAW_LONG) return;
+#ifdef PROFILER_ENABLED
+ char profileName[] = " AnimEntityDSG Display";
+#endif
+ DSG_BEGIN_PROFILE(profileName)
+ rAssert( mpDrawable != NULL);
+
+ if ( mIsVisible )
+ {
+ p3d::pddi->PushMultMatrix(PDDI_MATRIX_MODELVIEW, &mTransformOffset);
+ mpDrawable->Display();
+ p3d::pddi->PopMatrix(PDDI_MATRIX_MODELVIEW);
+ }
+
+ DSG_END_PROFILE(profileName)
+}
+
+void AnimEntityDSG::Reset()
+{
+ mpMultiController->Reset();
+}
+
+//===========================================================================
+// AnimEntityDSG::pPosition
+//===========================================================================
+// Description:
+// Returns a mutable pointer to the position of the entity
+//
+// Constraints:
+// Breaks encapsulation?
+//
+// Parameters:
+// None.
+//
+// Return:
+// non-const (!) pointer to the position vector in mTransform
+//
+//===========================================================================
+
+rmt::Vector* AnimEntityDSG::pPosition()
+{
+ return (rmt::Vector*)(mTransformOffset.m[3]);
+}
+//===========================================================================
+// AnimEntityDSG::rPosition
+//===========================================================================
+// Description:
+// Returns the position of the entity
+//
+// Constraints:
+//
+// Parameters:
+// None.
+//
+// Return:
+// Const reference to the entity position
+//
+//===========================================================================
+
+const rmt::Vector& AnimEntityDSG::rPosition()
+{
+ return mTransformOffset.Row( 3 );
+}
+//===========================================================================
+// AnimEntityDSG::GetPosition
+//===========================================================================
+// Description:
+//
+//
+// Constraints:
+//
+//
+// Parameters:
+// None.
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+void AnimEntityDSG::GetPosition( rmt::Vector* ipPosn )
+{
+ *ipPosn = *(rmt::Vector*)(mTransformOffset.m[3]);
+ ipPosn->Add(*(rmt::Vector*)(mTransform.m[3]));
+}
+//========================================================================
+// animentitydsg::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void AnimEntityDSG::SetAnimRootHeadingYUp( rmt::Vector& irHeading )
+{
+ mTransformOffset.FillHeading( irHeading, rmt::Vector(0.0f,1.0f,0.0f));
+}
+
+void AnimEntityDSG::GetAnimRootHeading( rmt::Vector& orHeading )
+{
+ mTransformOffset.GetHeading(orHeading);
+}
+
+void AnimEntityDSG::SetPosition( rmt::Vector& irNewPosition )
+{
+ //mTransformOffset.Identity();
+ mTransformOffset.FillTranslate( irNewPosition );
+}
+
+//===========================================================================
+// AnimEntityDSG::Update
+//===========================================================================
+// Description:
+// Advances animation and retrieves the new transform matrix
+//
+// Constraints:
+//
+//
+// Parameters:
+// Time elapsed in seconds (animcollisionentitydsg::update uses seconds)
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+
+void AnimEntityDSG::Update( float timeElapsedSec )
+{
+ bool doUpdate = true;
+ rmt::Sphere sphere;
+
+ GetBoundingSphere(&sphere);
+
+ bool vis = GetGameplayManager()->TestPosInFrustrumOfPlayer(sphere.centre, 0, sphere.radius);
+
+ doUpdate = vis || ((GetGame()->GetFrameCount() % 7) == (updateIndex % 7));
+
+ if(mIsAnimationPlaying)
+ {
+ mTimeSinceLastUpdate += timeElapsedSec;
+ }
+
+ if ( mIsAnimationPlaying && doUpdate)
+ {
+ // Save the bounding box information before the animation
+ //
+ rmt::Box3D oldBox;
+ GetBoundingBox( &oldBox );
+
+ // advance the animation and update transform!
+ //
+
+ float timeElapsedMS = mTimeSinceLastUpdate * 1000.0f;
+
+ mpMultiController->Advance( timeElapsedMS );
+ mTransform = mpDrawable->GetPose()->GetJoint( 0 )->objectMatrix;
+
+ // Move the object in the scenegraph.
+ //
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mRenderLayer ));
+ // Sanity check
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ rAssert( mRenderLayer != RenderEnums::numLayers );
+ pWorldRenderLayer->pWorldScene()->Move( oldBox, this );
+ mTimeSinceLastUpdate = 0.0f;
+ }
+}
+
+
+
+
+//===========================================================================
+// AnimEntityDSG::LoadSetUp
+//===========================================================================
+// Description:
+// Initializes animation, frame controller and drawable object and
+// the transform matrix
+//
+// Constraints:
+//
+//
+// Parameters:
+// Valid tCompositeDrawable and tAnimationFrameController objects
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+void AnimEntityDSG::LoadSetUp( tCompositeDrawable* pDrawable, tMultiController* pMultiController, tEntityStore* store, rmt::Vector& irPosition )
+{
+ mpDrawable = pDrawable;
+ mpMultiController = pMultiController;
+
+ char outputbuffer [255];
+
+ if (mpDrawable == NULL)
+ {
+ sprintf(outputbuffer,"%s is missing a drawable \n",this->GetName());
+ rTuneAssertMsg( mpDrawable != NULL,outputbuffer );
+ }
+
+ if( mpMultiController == NULL)
+ {
+ sprintf(outputbuffer,"%s is missing a Multicontroller \n",this->GetName());
+ rTuneAssertMsg( pMultiController !=NULL, outputbuffer);
+ }
+
+ mpDrawable->AddRef();
+ pMultiController->AddRef();
+ pMultiController->Reset();
+ pMultiController->SetFrame(0.0f);
+
+
+ mTransform.Identity();
+
+ mTransformOffset.Identity();
+ mTransformOffset.FillTranslate( irPosition );
+
+
+ tPose* pPose = mpDrawable->GetPose();
+ pPose->Evaluate();
+ mTransform = pPose->GetJoint( 0 )->worldMatrix;
+
+ mpDrawable->ProcessShaders(*this);
+
+ // Whether or not the animentity is cyclic or not.
+ // If any of the animations referenced are not cyclic, then assume the animation
+ // is non-cyclic
+
+
+ FindEffectElements( mpDrawable );
+ rmt::Box3D particlebox;
+ bool particleBBFound = GetParticleSystemBoundingBox( mpDrawable, &particlebox );
+ if ( particleBBFound )
+ {
+ delete mParticleSystemBoundingBox;
+ mParticleSystemBoundingBox = new rmt::Box3D;
+ *mParticleSystemBoundingBox = particlebox;
+ }
+}
+
+
+//===========================================================================
+// AnimEntityDSG::GetBoundingBox
+//===========================================================================
+// Description:
+//
+//
+// Constraints:
+//
+//
+// Parameters:
+// pointer to valid rmt::Box3D that will be filled with the box info
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+void AnimEntityDSG::GetBoundingBox( rmt::Box3D* box )
+{
+
+ mpDrawable->GetBoundingBox( &mBoundingBox );
+
+
+ //mTransformOffset.Transform( mBoundingBox.low, &mBoundingBox.low );
+ //mTransformOffset.Transform( mBoundingBox.high, &mBoundingBox.high );
+ //Only mod the box with translation; don't want to have to move the box in
+ //the tree for every rotation
+ mBoundingBox.low.Add( *(rmt::Vector*)(mTransformOffset.m[3]) );
+ mBoundingBox.high.Add( *(rmt::Vector*)(mTransformOffset.m[3]) );
+
+
+ if ( mParticleSystemBoundingBox )
+ {
+ mParticleSystemBoundingBox->low.Add( *(rmt::Vector*)(mTransformOffset.m[3]) );
+ mParticleSystemBoundingBox->high.Add( *(rmt::Vector*)(mTransformOffset.m[3]) );
+ mBoundingBox.Expand( mParticleSystemBoundingBox->low );
+ mBoundingBox.Expand( mParticleSystemBoundingBox->high );
+ }
+
+ *box = mBoundingBox;
+}
+//===========================================================================
+// AnimEntityDSG::GetBoundingSphere
+//===========================================================================
+// Description:
+// Gets the bounding sphere
+//
+// Constraints:
+//
+//
+// Parameters:
+// pointer to valid rmt::Sphere that will be filled with the sphere info
+//
+// Return:
+// None.
+//
+//===========================================================================
+
+void AnimEntityDSG::GetBoundingSphere( rmt::Sphere* sphere )
+{/*
+ mpDrawable->GetBoundingSphere( &mBoundingSphere );
+
+ //mTransformOffset.Transform( mBoundingSphere.centre, &mBoundingSphere.centre );
+ //Only mod the box with translation; don't want to have to move the box in
+ //the tree for every rotation
+ mBoundingSphere.centre.Add( *(rmt::Vector*)(mTransformOffset.m[3]) );
+
+ // rmt::Sphere particleSphere;
+ // mParticleSystemBoundingBox.GetBoundingSphere( &particleSphere );
+ *sphere = mBoundingSphere;*/
+
+ rmt::Box3D box;
+ GetBoundingBox( &box );
+ *sphere = box.GetBoundingSphere();
+}
+//===========================================================================
+// AnimEntityDSG::PlaceOnGround
+//===========================================================================
+// Description:
+// Moves object on the ground
+//
+// Constraints:
+//
+//
+// Parameters:
+// float heightAboveGround ( typically z fighting offset )
+//
+// Return:
+// None.
+//
+//===========================================================================
+bool AnimEntityDSG::PlaceOnGround( float heightAboveGround, bool ibMoveOnFail )
+{
+
+ bool foundPlane;
+ rmt::Vector searchPosn = rPosition();
+ searchPosn.y += 100.0f;
+ rmt::Vector groundPlaneNormal, groundPlanePosn;
+ GetIntersectManager()->FindIntersection( searchPosn, foundPlane, groundPlaneNormal, groundPlanePosn );
+ if ( foundPlane )
+ {
+ rmt::Vector newPosition = groundPlanePosn;
+ newPosition.y += heightAboveGround;
+ SetPosition( newPosition );
+ }
+ else if( ibMoveOnFail )
+ {
+ rmt::Vector newPosition = rPosition();
+ newPosition.y += heightAboveGround;
+ SetPosition( newPosition );
+ }
+
+ return foundPlane;
+}
+
+
+// Searches the given drawable and inits the mEffectElements array
+void AnimEntityDSG::FindEffectElements( tCompositeDrawable* drawable )
+{
+ mEffectElements.ClearUse();
+ rAssert( drawable != NULL );
+ // Iterate through the elements, search for effect elements
+ // which get stuffed into the array
+ for ( int i = 0 ; i < drawable->GetNumDrawableElement() ; i++ )
+ {
+ tCompositeDrawable::DrawableElement* element = drawable->GetDrawableElement( i );
+ // Only insert effect elements, the others dont contain particle systems
+ if ( element->GetType() == tCompositeDrawable::DrawableElement::EFFECT_ELEMENT )
+ {
+ // Better do a cast check
+ tCompositeDrawable::DrawableEffectElement* effectElement = static_cast< tCompositeDrawable::DrawableEffectElement* >( element );
+ rAssert( dynamic_cast< tCompositeDrawable::DrawableEffectElement* >(element) != NULL );
+
+ mEffectElements.Add( effectElement );
+
+ effectElement->SetPose( drawable->GetPose() );
+ }
+ }
+}
+
+bool AnimEntityDSG::GetParticleSystemBoundingBox( tCompositeDrawable* drawable, rmt::Box3D* out_box )
+{
+ rmt::Box3D rootBox;
+ rAssert( drawable != NULL );
+
+ drawable->GetPose()->Evaluate();
+
+ bool boxFound = false;
+ // Lets find the particle systems
+ for ( int i = 0 ; i < drawable->GetNumDrawableElement() ; i++ )
+ {
+ tCompositeDrawable::DrawableElement* element = drawable->GetDrawableElement( i );
+ if ( element )
+ {
+ if ( element->GetType() == tCompositeDrawable::DrawableElement::EFFECT_ELEMENT )
+ {
+ // Its a tEffect
+ tParticleSystem* particleSystem = static_cast< tParticleSystem* >( element->GetDrawable() );
+ rAssert( dynamic_cast< tParticleSystem* >( element->GetDrawable() ) );
+ if ( particleSystem )
+ {
+ rmt::Box3D subbox;
+ particleSystem->ComputePreciseBoundingBox( &subbox );
+ const rmt::Matrix* worldMatrix = element->GetWorldMatrix();
+ subbox.low.Add( worldMatrix->Row(3) );
+ subbox.high.Add( worldMatrix->Row(3) );
+
+ rootBox.Expand( subbox.low );
+ rootBox.Expand( subbox.high );
+
+ boxFound = true;
+ }
+ }
+ }
+ }
+ *out_box = rootBox;
+ return boxFound;
+}
+
+
diff --git a/game/code/render/DSG/animentitydsg.h b/game/code/render/DSG/animentitydsg.h
new file mode 100644
index 0000000..2e6ae1a
--- /dev/null
+++ b/game/code/render/DSG/animentitydsg.h
@@ -0,0 +1,186 @@
+//===========================================================================
+// Copyright (C) 1999 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: animentitydsg
+//
+// Description: Animated object without collision detection for use in Devin's
+// scene graph
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef ANIMENTITYDSG_H
+#define ANIMENTITYDSG_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <render/dsg/IEntityDSG.h>
+#include <radmath/radmath.hpp>
+#include <render/Enums/RenderEnums.h>
+#include <p3d/refcounted.hpp>
+#include <p3d/anim/compositedrawable.hpp>
+
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class tAnimationFrameController;
+class tAnimation;
+class tCompositeDrawable;
+class tEntityStore;
+class tMultiController;
+
+namespace poser
+{
+ class PoseEngine;
+}
+namespace sim
+{
+ class SimStateArticulated;
+};
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// An Animated Object, nearly identical to AnimCollisionEntityDSG but
+// without the collision information and processing. These are stored
+// in a separate array in the Spatial Tree Nodes
+//
+// Constraints:
+//
+// RenderUpdate() function - ask what this is supposed to do. The other
+// implementations are all empty.
+//
+//===========================================================================
+class AnimEntityDSG
+: public IEntityDSG
+{
+ public:
+ AnimEntityDSG();
+ ~AnimEntityDSG();
+
+ virtual void Display();
+ virtual void SetVisibility( bool vis ) { mIsVisible = vis; }
+ virtual bool GetVisibility() const { return mIsVisible; }
+ virtual void PlayAnimation( bool anim ) { mIsAnimationPlaying = anim; }
+ void Reset();
+
+ virtual rmt::Vector* pPosition();
+ virtual const rmt::Vector& rPosition();
+ virtual void GetPosition( rmt::Vector* ipPosn );
+
+ // Purpose of this function?
+ virtual void RenderUpdate(){ ; }
+
+ virtual void Update( float timeins );
+ virtual void GetBoundingBox( rmt::Box3D* box );
+ virtual void GetBoundingSphere( rmt::Sphere* sphere );
+
+ // Calculate the ground height underneath the object
+ // and move the object to there plus the given height
+ // Returns true if intersection found, false if not
+ bool PlaceOnGround( float heightAboveGround, bool ibMoveOnFail = true );
+
+ //////////////////////////////////////////////////////////////////////////
+ // Load Interface
+ //////////////////////////////////////////////////////////////////////////
+ void LoadSetUp(tCompositeDrawable* pCompD, tMultiController* pMC, tEntityStore* ipStore, rmt::Vector& irPosition );
+
+ // Tells the object what render layer it is currently occupying
+ // Note - the default layer if this function is not used is the Level layer
+ void SetRenderLayer( RenderEnums::LayerEnum renderLayer ) { mRenderLayer = renderLayer; }
+ // Returns the render layer enumeration that the object resides in
+ RenderEnums::LayerEnum GetRenderLayer() const { return mRenderLayer; }
+
+ bool AddToUpdateList() {return mbAddToUpdateList;}
+ int TrackSeparately() {return mTrackSeparately;}
+
+ void SetAddToUpdateList(bool b){ mbAddToUpdateList=b; }
+ void SetTrackSeparately(int iFlag){ mTrackSeparately=iFlag; }
+
+ void SetAnimRootHeadingYUp( rmt::Vector& irHeading );
+ void GetAnimRootHeading( rmt::Vector& orHeading );
+
+ void SetPosition( rmt::Vector& irNewPosition );
+
+ protected:
+ bool mbAddToUpdateList;
+ int mTrackSeparately;
+ //this is a hack specifically reserved for arrow use.
+ //it also doubles as returning the intended index,
+ //where 1 is index 0, for direcitonal arrow, and
+ // 2 is indeex 1, for wrong way arrow
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow AnimEntityDSG from being copied and assigned.
+ AnimEntityDSG( const AnimEntityDSG& );
+ AnimEntityDSG& operator=( const AnimEntityDSG& );
+
+ tCompositeDrawable* mpDrawable;
+ // Holds position and orientation of entity
+ rmt::Matrix mTransform;
+ rmt::Matrix mTransformOffset;
+
+ // Animation data
+ tMultiController * mpMultiController;
+
+ // We need to store our own bounding boxes and spheres.
+ // the tCompositeDrawable's are stored in world space,
+ // but they are not updated when an object animates.
+ // we will update these boxes once per update.
+ //
+ rmt::Box3D mBoundingBox;
+ rmt::Sphere mBoundingSphere;
+
+ // Whether not we can see the object
+ bool mIsVisible : 1;
+
+ // Whether or not the animation is playing
+ bool mIsAnimationPlaying : 1;
+ bool debugflag : 1;
+
+ // Lets keep a list of pointers to ParticleSystems inside the composite
+ // Drawable so that when we advance the framecontroller, we also cause it
+ // to release new particles. If we dont do this, they will only emit
+ // upon Display(). If this happens, the particles will appear to
+ // only start sprouting when you look at them.
+ SwapArray< tCompositeDrawable::DrawableEffectElement* > mEffectElements;
+
+ // Searches the given drawable and inits the mEffectElements array
+ void FindEffectElements( tCompositeDrawable* drawable );
+
+ // The Current render layer, needed for when the objects want to move themselves in the DSG
+ RenderEnums::LayerEnum mRenderLayer;
+
+ // A bounding box, centered at the origin, computed using
+ // tParticleSystem::ComputePreciseBoundingBox
+
+ rmt::Box3D* mParticleSystemBoundingBox;
+
+ // Initializes the above bounding box using the given composite drawable
+ bool GetParticleSystemBoundingBox( tCompositeDrawable* drawable, rmt::Box3D* out_box );
+
+
+ unsigned int m_TimeLastParticleUpdate;
+ float mTimeSinceLastUpdate;
+ unsigned updateIndex;
+
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/DSG/breakableobjectdsg.cpp b/game/code/render/DSG/breakableobjectdsg.cpp
new file mode 100644
index 0000000..0f1a95f
--- /dev/null
+++ b/game/code/render/DSG/breakableobjectdsg.cpp
@@ -0,0 +1,243 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: BreakableObjectDSG
+//
+// Description: Breakleobjects
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <render/DSG/breakableobjectdsg.h>
+#include <p3d/anim/animatedobject.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/utility.hpp>
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// BreakableObjectDSG::BreakableObjectDSG
+//===========================================================================
+// Description:
+// A breakable DSG object's ctor
+//
+// Constraints:
+// Most other DSG objects expect a preallocated matrix to be placed handed
+// to the object on loadsetup. This class allocates its own matrix here
+// mTranslucent flag set to true. Probably a pretty safe assumption, since
+// breakables would have to fade out to look decent
+//
+// Parameters:
+//
+//
+// Return:
+//
+//
+//===========================================================================
+
+BreakableObjectDSG::BreakableObjectDSG()
+: mpObject( NULL ),
+mpController( NULL )
+{
+ IEntityDSG::mTranslucent = true;
+ mpMatrix = new rmt::Matrix;
+ mpMatrix->Identity();
+}
+
+//===========================================================================
+// BreakableObjectDSG::~BreakableObjectDSG
+//===========================================================================
+// Description:
+// A breakable DSG object's dtor
+//
+// Constraints:
+//
+//
+// Parameters:
+//
+//
+// Return:
+//
+//
+//===========================================================================
+
+
+BreakableObjectDSG::~BreakableObjectDSG()
+{
+BEGIN_PROFILE( "BreakableObjectDSG Destroy" );
+ // The controller should also release the attached effect, use the debugger to
+ // make sure this happens!
+ if (mpController != NULL)
+ {
+ mpController->Release();
+ mpController = NULL;
+ }
+
+ delete mpMatrix;
+ mpMatrix = NULL;
+END_PROFILE( "BreakableObjectDSG Destroy" );
+}
+//===========================================================================
+// BreakableObjectDSG::Init
+//===========================================================================
+// Description:
+// Create a breakable from a tAnimatedObject via cloning
+//
+// Constraints:
+// Only call it once
+//
+// Parameters:
+//
+//
+// Return:
+//
+//
+//===========================================================================
+
+void BreakableObjectDSG::Init( tAnimatedObjectFactory* pFactory, tAnimatedObjectFrameController* pController)
+{
+ rAssert( pFactory != NULL );
+ rAssert( pController != NULL );
+
+ rAssert( mpController == NULL );
+ rAssert( mpObject == NULL );
+
+ // Clone a new frame controller off the given controller
+ mpController = static_cast<tAnimatedObjectFrameController*> (pController->Clone() );
+ // Check that the clone and downcast both worked correctly
+ rAssert( mpController != NULL );
+
+ // Clone a new animated object from the factory
+ mpObject = pFactory->CreateObject( mpController );
+
+ mpObject->ProcessShaders(*this);
+}
+//===========================================================================
+// BreakableObjectDSG::Display
+//===========================================================================
+// Description:
+// Draw the breakable
+//
+// Constraints:
+//
+//
+// Parameters:
+//
+//
+// Return:
+//
+//
+//===========================================================================
+
+void BreakableObjectDSG::Display()
+{
+ if(IS_DRAW_LONG) return;
+#ifdef PROFILER_ENABLED
+ char profileName[] = " BreakableObjectDSG Display";
+#endif
+ DSG_BEGIN_PROFILE(profileName)
+ p3d::stack->PushMultiply( *mpMatrix );
+ mpObject->Display();
+ p3d::stack->Pop();
+ DSG_END_PROFILE(profileName)
+}
+
+//===========================================================================
+// BreakableObjectDSG::GetBoundingBox
+//===========================================================================
+// Description:
+// Fetches the bounding box
+//
+// Constraints:
+// Its not accurate!
+//
+// Parameters:
+//
+//
+// Return:
+//
+//
+//===========================================================================
+
+void BreakableObjectDSG::GetBoundingBox( rmt::Box3D* box )
+{
+
+ mpObject->GetBaseObject()->GetBoundingBox( box );
+ // Lets inflate the bounding box, as particle systems do not have accurate ones
+ rmt::Vector inflate( 5.0f,5.0f,5.0f );
+ box->low += ( rPosition() - inflate );
+ box->high += ( rPosition() + inflate );
+
+}
+void BreakableObjectDSG::GetBoundingSphere( rmt::Sphere* pSphere )
+{
+ mpObject->GetBaseObject()->GetBoundingSphere( pSphere );
+ pSphere->centre += rPosition();
+ // Lets inflate the bounding sphere, as particle systems do not have accurate ones
+ pSphere->radius += 5.0f;
+}
+
+
+//===========================================================================
+// BreakableObjectDSG::Update
+//===========================================================================
+// Description:
+// Advances effect animation
+//
+// Constraints:
+// None.
+//
+// Parameters:
+// Time to advance effect in milliseconds
+//
+// Return:
+// None
+//
+//===========================================================================
+void BreakableObjectDSG::Update( float deltaTime )
+{
+ mpController->Advance( deltaTime, true );
+}
+rmt::Vector* BreakableObjectDSG::pPosition()
+{
+ rAssert( 0 ); // no breaking encapsulation!
+ return &mPosn;
+}
+const rmt::Vector& BreakableObjectDSG::rPosition()
+{
+ return mpMatrix->Row( 3 );
+}
+void BreakableObjectDSG::GetPosition( rmt::Vector* ipPosn )
+{
+ *ipPosn = mpMatrix->Row( 3 );
+}
+void BreakableObjectDSG::SetTransform( const rmt::Matrix& transform )
+{
+ *mpMatrix = transform;
+}
+void BreakableObjectDSG::Reset()
+{
+ mpController->Reset();
+}
+int BreakableObjectDSG::LastFrameReached()
+{
+ return mpController->LastFrameReached();
+} \ No newline at end of file
diff --git a/game/code/render/DSG/breakableobjectdsg.h b/game/code/render/DSG/breakableobjectdsg.h
new file mode 100644
index 0000000..0756a69
--- /dev/null
+++ b/game/code/render/DSG/breakableobjectdsg.h
@@ -0,0 +1,94 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: BreakableObjectDSG
+//
+// Description: Breakable object that can be placed in the DSG
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef BREAKABLEOBJECTDSG_H
+#define BREAKABLEOBJECTDSG_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <render/dsg/inststatentitydsg.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class tAnimatedObject;
+class tAnimatedObjectFactory;
+class tAnimatedObjectFrameController;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+// Describe here what you require of the client which uses or
+// has this class - essentially, the converse of the description
+// above. A constraint is an expression of some semantic
+// condition that must be preserved, an invariant of a class or
+// relationship that must be preserved while the system is in a
+// steady state.
+//
+//===========================================================================
+class BreakableObjectDSG : public InstStatEntityDSG
+{
+ public:
+ BreakableObjectDSG();
+ ~BreakableObjectDSG();
+
+ void Init( tAnimatedObjectFactory* pFactory, tAnimatedObjectFrameController* pController);
+
+ // Update time in milliseconds
+ void Update( float deltaTime );
+ void SetTransform( const rmt::Matrix& transform );
+
+ void Reset();
+ int LastFrameReached();
+
+
+ virtual void Display();
+ virtual void GetBoundingBox(rmt::Box3D* box);
+ virtual void GetBoundingSphere(rmt::Sphere* pSphere);
+
+ virtual rmt::Vector* pPosition();
+ virtual const rmt::Vector& rPosition();
+ virtual void GetPosition( rmt::Vector* ipPosn );
+
+ protected:
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow BreakableObjectDSG from being copied and assigned.
+ BreakableObjectDSG( const BreakableObjectDSG& );
+ BreakableObjectDSG& operator=( const BreakableObjectDSG& );
+
+ tAnimatedObject* mpObject;
+ tAnimatedObjectFrameController* mpController;
+
+
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/DSG/collisionentitydsg.cpp b/game/code/render/DSG/collisionentitydsg.cpp
new file mode 100644
index 0000000..332c592
--- /dev/null
+++ b/game/code/render/DSG/collisionentitydsg.cpp
@@ -0,0 +1,609 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implementation of class CollisionEntityDSG
+//
+// History: 6/17/2002 + Created -- TBJ
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/effects/particlesystem.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <simcollision/collisionvolume.hpp>
+
+#include <simcollision/impulsebasedcollisionsolver.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/collisionentitydsg.h>
+
+#include <sound/soundcollisiondata.h>
+
+#include <events/eventmanager.h>
+
+#include <constants/particleenum.h>
+
+#include <memory/srrmemory.h>
+
+// HACK STUFF FOR REMOVING OBJECTS AFTER COLLISION
+#include <render/DSG/DynaPhysDSG.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/Culling/WorldScene.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+struct BreakablePair
+{
+ const char* name;
+ BreakablesEnum::BreakableID id;
+};
+
+const BreakablePair s_BreakableList[] =
+{
+ { "eHydrantBreaking", BreakablesEnum::eHydrantBreaking },
+ { "eOakTreeBreaking", BreakablesEnum::eOakTreeBreaking },
+ { "ePineTreeBreaking", BreakablesEnum::ePineTreeBreaking },
+ { "eBigBarrierBreaking", BreakablesEnum::eBigBarrierBreaking },
+ { "eRailCrossBreaking", BreakablesEnum::eRailCrossBreaking },
+ { "eSpaceNeedleBreaking", BreakablesEnum::eSpaceNeedleBreaking },
+ { "eTomaccoBreaking", BreakablesEnum::eTommacoPlantsBreaking },
+ { "eCypressTreeBreaking", BreakablesEnum::eCypressTreeBreaking },
+ { "eDeadTreeBreaking", BreakablesEnum::eDeadTreeBreaking },
+ { "eKrustyGlassBreaking", BreakablesEnum::eKrustyGlassBreaking },
+ { "eSkeletonBreaking", BreakablesEnum::eSkeletonBreaking },
+ { "eWillow", BreakablesEnum::eWillow },
+ { "eGlobeLight", BreakablesEnum::eGlobeLight },
+ { "eTreeMorn", BreakablesEnum::eTreeMorn },
+ { "ePalmTreeSmall", BreakablesEnum::ePalmTreeSmall },
+ { "ePalmTreeLarge", BreakablesEnum::ePalmTreeLarge },
+ { "eStopsign", BreakablesEnum::eStopsign },
+ { "ePumpkin", BreakablesEnum::ePumpkin },
+ { "ePumpkinMed", BreakablesEnum::ePumpkinMed },
+ { "ePumpkinSmall", BreakablesEnum::ePumpkinSmall },
+ { "eCasinoJump", BreakablesEnum::eCasinoJump }
+};
+
+const int NUM_BREAKABLES = sizeof(s_BreakableList) / sizeof(s_BreakableList[0]);
+
+//==============================================================================
+// CollisionEntityDSG::CollisionEntityDSG
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CollisionEntityDSG::CollisionEntityDSG()
+:
+mPersistentObjectID( -1 ),
+mpCollisionAttributes( 0 ),
+mWasParticleEffectTriggered( false ),
+mRenderLayer( RenderEnums::LevelSlot )
+{
+ lastUpdate = 0xffffffff;
+}
+
+//==============================================================================
+// CollisionEntityDSG::~CollisionEntityDSG
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CollisionEntityDSG::~CollisionEntityDSG()
+{
+BEGIN_PROFILE( "CollisionEntityDSG Destroy" );
+ if ( mpCollisionAttributes )
+ {
+ mpCollisionAttributes->Release( );
+ mpCollisionAttributes = 0;
+ }
+END_PROFILE( "CollisionEntityDSG Destroy" );
+}
+
+/*
+==============================================================================
+CollisionEntityDSG::SetSimState
+==============================================================================
+Description: Comment
+
+Parameters: ( sim::SimState* ipCollObj )
+
+Return: void
+
+=============================================================================
+*/
+void CollisionEntityDSG::SetSimState( sim::SimState* ipCollObj )
+{
+ if ( ipCollObj )
+ {
+ ipCollObj->mAIRefPointer = this;
+ }
+ OnSetSimState( ipCollObj );
+}
+
+/*
+==============================================================================
+CollisionEntityDSG::SetCollisionAttributes
+==============================================================================
+Description: Comment
+
+Parameters: ( CollisionAttributes* pCollisionAttributes )
+
+Return: void
+
+=============================================================================
+*/
+void CollisionEntityDSG::SetCollisionAttributes( CollisionAttributes* pCollisionAttributes )
+{
+ tRefCounted::Assign( mpCollisionAttributes, pCollisionAttributes );
+}
+/*
+==============================================================================
+CollisionEntityDSG::SetRenderLayer
+==============================================================================
+Description: Sets the renderlayer. It must match up with the actually layer
+ that it inhabits otherwise, when the object tries to move() itself
+ in the DSG, the move will fail and the RenderManager will spew warnings
+
+
+Parameters: RenderEnums::LayerEnum indicating this object's layer)
+
+Return: void
+
+=============================================================================
+*/
+void CollisionEntityDSG::SetRenderLayer( RenderEnums::LayerEnum renderLayer )
+{
+ mRenderLayer = renderLayer;
+}
+
+//=============================================================================
+// CollisionEntityDSG::PostReactToCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& impulse, sim::Collision& inCollision)
+//
+// Return: sim
+//
+//=============================================================================
+sim::Solving_Answer CollisionEntityDSG::PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision)
+{
+ // generic handling of collision
+ //
+ // fire off a sound event at appropriate intensity
+
+ sim::CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ sim::CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ sim::SimState* simStateA = collObjA->GetSimState();
+ sim::SimState* simStateB = collObjB->GetSimState();
+
+ //
+ // SOUND EVENT HERE?
+ //
+
+ // need to convert the impulse to a nice friendly float between 0.0 and 1.0
+ //
+ // just looking at some empirical data, the range of impulse magnitude seems to be from around
+ // 0.0 to 100000.0 - at the high end would be a heavy vehicle (3000 kg) hitting a static wall at 122kmh
+ // a 2000kg car hitting a wall at 150kmh gave 78706.7
+
+ float impulseMagnitude = impulse.Magnitude();
+
+ const float maxIntensity = 100000.0f;
+
+ float soundScale = impulseMagnitude / maxIntensity;
+ if(soundScale > 1.0f)
+ {
+ soundScale = 1.0f;
+ }
+ if(soundScale < 0.0f)
+ {
+ rAssert(0);
+ }
+
+ SoundCollisionData soundData( soundScale,
+ static_cast<CollisionEntityDSG*>(simStateA->mAIRefPointer),
+ static_cast<CollisionEntityDSG*>(simStateB->mAIRefPointer) );
+ GetEventManager()->TriggerEvent( EVENT_COLLISION, &soundData );
+
+ return sim::Solving_Continue;
+}
+
+
+
+
+/*
+==============================================================================
+CollisionEntityDSG::GetCollisionAttributes
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CollisionAttributes
+
+=============================================================================
+*/
+CollisionAttributes* CollisionEntityDSG::GetCollisionAttributes( void ) const
+{
+ return mpCollisionAttributes;
+}
+
+
+
+CollisionAttributes::CollisionAttributes ()
+: mBreakableID( BreakablesEnum::eNull )
+{
+ memset(mp_Sound, 0, sizeof(mp_Sound));
+ memset(mp_Animation, 0, sizeof(mp_Animation));
+ mp_Particle=ParticleEnum::eNull;
+ mMass=0.0;
+ mFriction=0.0;
+ mElasticity=0.0;
+ mClasstypeid =WTF;
+
+ mPhizProp = 0;
+}
+
+
+CollisionAttributes::CollisionAttributes(char* p_sound,char* p_particle,char* p_animation,float friction, float mass,float elasticity,unsigned int classtypeid, float volume)
+{
+ // if the item creates a specific particle effect when hit
+ // get it from p_particle string
+ if ( strcmp( p_particle,"eGarbage" )==0)
+ {
+ mp_Particle = ParticleEnum::eGarbage;
+ }
+ else if ( strcmp( p_particle, "eShrub" )==0 )
+ {
+ mp_Particle = ParticleEnum::eShrub;
+ }
+ else if ( strcmp( p_particle, "eOakTreeLeaves" )==0 )
+ {
+ mp_Particle = ParticleEnum::eOakTreeLeaves;
+ }
+ else if ( strcmp( p_particle, "eMail" )==0 )
+ {
+ mp_Particle = ParticleEnum::eMail;
+ }
+ else if ( strcmp( p_particle, "ePineTreeNeedles" )==0)
+ {
+ mp_Particle = ParticleEnum::ePineTreeNeedles;
+ }
+ else if ( strcmp( p_particle, "eStars" ) == 0)
+ {
+ mp_Particle = ParticleEnum::eStars;
+ }
+ else if(strcmp( p_particle, "eParkingMeter" ) == 0)
+ {
+ mp_Particle = ParticleEnum::eParkingMeter ;
+ }
+ else if(strcmp( p_particle, "eCarExplosion" ) == 0)
+ {
+ mp_Particle = ParticleEnum::eCarExplosion ;
+ }
+ else if(strcmp( p_particle, "ePopsicles" ) == 0)
+ {
+ mp_Particle = ParticleEnum::ePopsicles ;
+ }
+ else
+ {
+ mp_Particle = ParticleEnum::eNull;
+ }
+
+ // If the item is breakable, the enumeration string is stored in
+ // p_animation
+
+ mBreakableID = BreakablesEnum::eNull;
+ for ( int i = 0 ; i < NUM_BREAKABLES ; i++ )
+ {
+ if ( strcmp( p_animation, s_BreakableList[i].name ) == 0 )
+ {
+ mBreakableID = s_BreakableList[i].id;
+ break;
+ }
+ }
+ // mp_Particle=p_particle;
+ strcpy(mp_Animation,p_animation);
+ strcpy(mp_Sound, p_sound);
+ mMass=mass;
+ //mMass = 1000.0f;
+ switch (classtypeid)
+ {
+ case 0:
+ {
+ mClasstypeid = WTF;
+ break;
+ }
+ case 1:
+ {
+ mClasstypeid = GROUND;
+ break;
+ }
+ case 2:
+ {
+ mClasstypeid = PROP_STATIC;
+ break;
+ }
+ case 3:
+ {
+ mClasstypeid = PROP_MOVEABLE;
+ break;
+ }
+ case 4:
+ {
+ mClasstypeid = PROP_BREAKABLE;
+ break;
+ }
+ case 5:
+ {
+ mClasstypeid = ANIMATED_BV;
+ break;
+ }
+ case 6:
+ {
+ mClasstypeid = DRAWABLE;
+ break;
+ }
+ case 7:
+ {
+ mClasstypeid = STATIC;
+ break;
+ }
+ case 8:
+ {
+ mClasstypeid = PROP_DRAWABLE;
+ break;
+ }
+ case 10:
+ {
+
+ mClasstypeid = PROP_ONETIME_MOVEABLE;
+ break;
+ }
+ default:
+ {
+ printf("ERROR: Unknown ClasstypeID \n");
+ }
+ }
+
+
+ // need to make new physics properties for this thing:
+
+ // TODO - verify that incoming values make any sense
+
+ MEMTRACK_PUSH_GROUP("PhysicsProperties");
+
+ mPhizProp = new(GMA_LEVEL_OTHER)sim::PhysicsProperties;
+ mPhizProp->AddRef();
+
+ MEMTRACK_POP_GROUP("PhysicsProperties");
+
+
+ if(volume > 0.0f)
+ {
+ if(mClasstypeid == PROP_MOVEABLE || mClasstypeid == PROP_BREAKABLE || mClasstypeid == PROP_ONETIME_MOVEABLE)
+ {
+ float density = mMass / volume;
+ mPhizProp->SetDensityCGS(density);
+ }
+ else
+ {
+ rAssert(0); // shouldn't be setting volume (mass) on this thing
+ }
+ }
+
+
+
+
+ if(friction > 0.0f && friction < 3.5f) // changed from friction >= 0.0f - don't think we ever want somethign with 0.0 friction
+ {
+ mFriction = friction;
+ mPhizProp->SetFrictCoeffCGS(friction); // good
+ }
+ else
+ {
+ mFriction = 1.2f;
+ mPhizProp->SetFrictCoeffCGS(1.2f); // good
+
+ }
+
+ if(elasticity >= 1.0f && elasticity <= 2.0f)
+ {
+ mElasticity = elasticity;
+ mPhizProp->SetRestCoeffCGS(elasticity); // 1.0 to 2.0
+ }
+ else
+ {
+ mElasticity = 1.5f;
+ mPhizProp->SetRestCoeffCGS(1.5f); // 1.0 to 2.0
+ }
+
+ mPhizProp->SetTangRestCoeffCGS(0.0f); // leave this one at 0
+
+
+}
+
+
+CollisionAttributes::~CollisionAttributes()
+{
+ if ( mPhizProp != NULL )
+ {
+ mPhizProp->Release();
+ }
+}
+
+char* CollisionAttributes::GetAnimation()
+{
+ return mp_Animation;
+}
+BreakablesEnum::BreakableID CollisionAttributes::GetBreakable()
+{
+ return mBreakableID;
+}
+ParticleEnum::ParticleID CollisionAttributes::GetParticle()
+{
+ return mp_Particle;
+}
+
+char* CollisionAttributes::GetSound()
+{
+ return mp_Sound;
+}
+
+
+float CollisionAttributes::GetFriction()
+{
+ return mFriction;
+}
+
+float CollisionAttributes::GetMass()
+{
+ return mMass;
+}
+
+float CollisionAttributes::GetElasticity()
+{
+ return mElasticity;
+}
+
+unsigned int CollisionAttributes::GetClasstypeid ()
+{
+ return mClasstypeid;
+}
+
+void CollisionAttributes::SetAnimation(char* p_animation)
+{
+ //mp_Animation=p_animation;
+ strcpy(mp_Animation,p_animation);
+
+}
+void CollisionAttributes::SetBreakable( BreakablesEnum::BreakableID id )
+{
+ mBreakableID = id;
+}
+void CollisionAttributes::SetParticle(ParticleEnum::ParticleID p_particle)
+{
+ mp_Particle=p_particle;
+}
+
+void CollisionAttributes::SetSound(char* p_sound)
+{
+ //mp_Sound=p_sound;
+ strcpy(mp_Sound,p_sound);
+
+}
+
+void CollisionAttributes::SetMass(const float mass)
+{
+ mMass=mass;
+}
+
+void CollisionAttributes::SetFriction(const float friction)
+{
+ mFriction=friction;
+}
+
+void CollisionAttributes::SetElasticity(const float elasticity)
+{
+ mElasticity=elasticity;
+}
+
+void CollisionAttributes::SetClasstypeid(unsigned int classtypeid)
+{
+ switch (classtypeid)
+ {
+ case 0:
+ {
+ mClasstypeid = WTF;
+ break;
+ }
+ case 1:
+ {
+ mClasstypeid = GROUND;
+ break;
+ }
+ case 2:
+ {
+ mClasstypeid = PROP_STATIC;
+ break;
+ }
+ case 3:
+ {
+ mClasstypeid = PROP_MOVEABLE;
+ //mClasstypeid = PROP_ONETIME_MOVEABLE;
+ break;
+ }
+ case 4:
+ {
+ mClasstypeid = PROP_BREAKABLE;
+ break;
+ }
+ case 5:
+ {
+ mClasstypeid = ANIMATED_BV;
+ break;
+ }
+ case 6:
+ {
+ mClasstypeid = DRAWABLE;
+ break;
+ }
+ case 7:
+ {
+ mClasstypeid = STATIC;
+ break;
+ }
+ case 8:
+ {
+ mClasstypeid = PROP_DRAWABLE;
+ break;
+ }
+ case 9:
+ {
+ mClasstypeid = PROP_ANIM_BREAKABLE;
+ break;
+ }
+ case 10:
+ {
+ mClasstypeid = PROP_ONETIME_MOVEABLE;
+ //mClasstypeid = PROP_MOVEABLE;
+
+ break;
+ }
+
+ default:
+ {
+ printf("ERROR: Unknown ClasstypeID \n");
+ }
+ }
+}
+
+
+
diff --git a/game/code/render/DSG/collisionentitydsg.h b/game/code/render/DSG/collisionentitydsg.h
new file mode 100644
index 0000000..4d2c254
--- /dev/null
+++ b/game/code/render/DSG/collisionentitydsg.h
@@ -0,0 +1,166 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: collisionentitydsg.h
+//
+// Description: Blahblahblah
+//
+// History: 6/14/2002 + Created -- TBJ
+//
+//=============================================================================
+
+#ifndef COLLISIONENTITYDSG_H
+#define COLLISIONENTITYDSG_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <string.h>
+#include <p3d/refcounted.hpp>
+#include <radmath/radmath.hpp>
+
+#include <simcollision/impulsebasedcollisionsolver.hpp>
+#include <simcollision/collision.hpp>
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/DSG/IEntityDSG.h>
+#include <constants/physprop.h>
+#include <constants/particleenum.h>
+#include <constants/breakablesenum.h>
+#include <render/Enums/RenderEnums.h>
+
+namespace sim
+{
+ class Collision;
+ class SimState;
+};
+
+
+//=============================
+//Forward Reference
+//=============================
+class tParticleSystem;
+
+
+
+
+// Contains attributes for collision reactions.
+// Particle system, sound fx.
+//
+class CollisionAttributes
+:
+public tRefCounted
+{
+public:
+
+ CollisionAttributes( void );
+ CollisionAttributes(char* p_sound,char* p_particle,char* p_animation,float friction,float mass,float elasticity,unsigned int classtypeid, float volume);
+ ~CollisionAttributes( void );
+ void SetSound(char * p_sound);
+ void SetParticle(ParticleEnum::ParticleID p_particle);
+ void SetBreakable( BreakablesEnum::BreakableID type );
+ void SetAnimation(char* p_animation);
+ void SetFriction(const float friction);
+ void SetMass(const float mass);
+ void SetElasticity(const float elasticity);
+ void SetClasstypeid(unsigned int classtypeid);
+
+ char* GetSound( void);
+ char* GetAnimation(void);
+ ParticleEnum::ParticleID GetParticle(void);
+ BreakablesEnum::BreakableID GetBreakable(void);
+ unsigned int GetClasstypeid(void);
+ float GetMass(void);
+ float GetFriction(void);
+ float GetElasticity(void);
+
+ sim::PhysicsProperties* GetPhysicsProperties() {return mPhizProp;}
+
+private:
+
+ //char* mp_Sound;
+ //char* mp_Animation;
+ char mp_Sound[32];
+ char mp_Animation[32];
+
+ ParticleEnum::ParticleID mp_Particle;
+ BreakablesEnum::BreakableID mBreakableID;
+ float mMass;
+ float mFriction;
+ float mElasticity;
+ enClasstypeID mClasstypeid;
+
+ sim::PhysicsProperties* mPhizProp;
+};
+
+class CollisionEntityDSG
+:
+public IEntityDSG
+{
+public:
+ CollisionEntityDSG( void );
+ virtual ~CollisionEntityDSG( void );
+
+ // Implement this method to handle collisions between ICollisionEntityDSG objects.
+ //
+ // All sim::SimState::mAIRefPointer's would point to a ICollisionEntityDSG.
+ //
+ // recall:
+ // enum Solving_Answer { Solving_Continue = 0, Solving_Aborted };
+
+ virtual sim::Solving_Answer PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision ) = 0;
+
+ // the PostReactToCollision doesn't need to be pure virtual
+ //
+ // many of the different classes will want to do the exact same thing here, so they can just explicitly call the
+ // implementation of the base class...
+ virtual sim::Solving_Answer PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision);
+
+
+ void SetSimState( sim::SimState* ipCollObj );
+
+ // Returns a pointer to a table of collision attributes.
+ // Attributes such as particle system, sound effects would be found here.
+ //
+ CollisionAttributes* GetCollisionAttributes( void ) const;
+ void SetCollisionAttributes( CollisionAttributes* pCollisionAttributes );
+
+ virtual int GetAIRef() = 0;
+
+ // keep track of what simulation tick this object was last updated on
+ // (to aviod duplicate updating when it is in multiple collision lists)
+ unsigned GetLastUpdate() { return lastUpdate;}
+ void SetLastUpdate(unsigned l) { lastUpdate = l;}
+
+ // Tells the object what render layer it is currently occupying
+ // Note - the default layer if this function is not used is the Level layer
+ void SetRenderLayer( RenderEnums::LayerEnum renderLayer );
+ // Returns the render layer enumeration that the object resides in
+ RenderEnums::LayerEnum GetRenderLayer() const { return mRenderLayer; }
+
+ short mPersistentObjectID;
+
+protected:
+ unsigned lastUpdate;
+
+ virtual void OnSetSimState( sim::SimState* ipCollObj )
+ {
+ }
+//private:
+ CollisionAttributes* mpCollisionAttributes; // this should be protected I think, not private
+
+ // Michael Riegger - indicates whether a particle effect has bee triggered. This does not
+ // apply to breakables, but instead to one time things like mail flying out of a mailbox or
+ // garbage from a garbage can
+ bool mWasParticleEffectTriggered;
+ // The Current render layer, needed for when the objects want to move themselves in the DSG
+ RenderEnums::LayerEnum mRenderLayer;
+};
+
+#endif //COLLISIONENTITYDSG_H \ No newline at end of file
diff --git a/game/code/render/Enums/RenderEnums.h b/game/code/render/Enums/RenderEnums.h
new file mode 100644
index 0000000..f0529aa
--- /dev/null
+++ b/game/code/render/Enums/RenderEnums.h
@@ -0,0 +1,142 @@
+#ifndef __RenderEnums_H__
+#define __RenderEnums_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: RenderEnums
+//
+// Description: The RenderEnums does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/04/24]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+
+//========================================================================
+//
+// Synopsis: The RenderEnums; Synopsis by Inspection.
+//
+//========================================================================
+class RenderEnums
+{
+public:
+ RenderEnums(){}
+ ~RenderEnums(){}
+
+ enum LayerEnum
+ {
+ GUI = 0x00000000,
+ PresentationSlot,
+ LevelSlot,
+ MissionSlot1,
+ MissionSlot2,
+ numLayers,
+ LayerOnlyMask = 0x000000FF
+ };
+
+ enum UserDataEnum
+ {
+ BogusUserData = 0x00000000,
+ AllRenderLoadingComplete = 0x00100000,
+ AllIntersectLoadingComplete= 0x00200000,
+ DynamicLoadComplete = 0x00400000,
+ CompletionOnlyMask = 0x00F00000
+ };
+
+ enum LoadZoneEnum
+ {
+ Zone1 = 0x00001000,
+ ZoneMask = 0x000FF000,
+ ZoneShift= 12
+ };
+
+ //GutsCallEnum is meant to mask to the top byte of a CB whose
+ //bottom byte is LayerEnum
+ enum GutsCallEnum
+ {
+ DrawableGuts = 0x01000000,
+ GeometryGuts = 0x02000000,
+ IntersectGuts = 0x03000000,
+ StaticEntityGuts = 0x04000000,
+ StaticPhysGuts = 0x05000000,
+ TreeDSGGuts = 0x06000000,
+ FenceGuts = 0x07000000,
+ AnimCollGuts = 0x08000000,
+ DynaPhysGuts = 0x09000000,
+ LocatorGuts = 0x0A000000,
+ WorldSphereGuts = 0x0B000000,
+ RoadSegmentGuts = 0x0C000000,
+ PathSegmentGuts = 0x0D000000,
+ GlobalWSphereGuts = 0x0E000000,
+ AnimGuts = 0x0F000000,
+ IgnoreGuts = 0xFE000000,
+ GutsOnlyMask = 0xFF000000
+ };
+
+ enum LevelEnum
+ {
+ L1,
+ L2,
+ L3,
+ L4,
+ L5,
+ L6,
+ L7,
+ //L8,
+ //L9,
+ //MULTI,
+ numLevels,
+
+ //Mini games
+ B00 = numLevels,
+ B01,
+ B02,
+ B03,
+ B04,
+ B05,
+ B06,
+ B07,
+
+ MAX_LEVEL
+ };
+
+ enum LevelMissionCountEnum
+ {
+ L1MCount = 10,
+ L2MCount = 10,
+ L3MCount = 10,
+ L4MCount = 10,
+ L5MCount = 10,
+ L6MCount = 10,
+ L7MCount = 10,
+ L8MCount = 10,
+ L9MCount = 10
+ };
+
+ enum MissionEnum
+ {
+ M1,
+ M2,
+ M3,
+ M4,
+ M5,
+ M6,
+ M7,
+ M8,
+ M9,
+ M10,
+ numMissions
+ };
+
+private:
+};
+
+#endif
diff --git a/game/code/render/IntersectManager/IntersectManager.cpp b/game/code/render/IntersectManager/IntersectManager.cpp
new file mode 100644
index 0000000..451290d
--- /dev/null
+++ b/game/code/render/IntersectManager/IntersectManager.cpp
@@ -0,0 +1,1895 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: IntersectManager.cpp
+//
+// Description: Implementation for IntersectManager class.
+//
+// History: Implemented --Devin [5/5/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+//#include <radtime.hpp>
+//#include <raddebugwatch.hpp>
+#include <radmath/radmath.hpp>
+#include <simcollision/proximitydetection.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <debug/profiler.h>
+
+#include <render/IntersectManager/IntersectManager.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/Culling/SphereSP.h>
+#include <render/Culling/WorldScene.h>
+#include <render/DSG/IntersectDSG.h>
+#include <render/DSG/StaticPhysDSG.h>
+#include <render/DSG/DynaPhysDSG.h>
+#include <render/DSG/FenceEntityDSG.h>
+#include <render/DSG/animcollisionentitydsg.h>
+#include <meta/triggervolume.h>
+#include <roads/roadsegment.h>
+#include <roads/geometry.h>
+#include <roads/road.h>
+#include <pedpaths/pathsegment.h>
+#include <render/dsg/staticentitydsg.h>
+
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <camera/supercammanager.h>
+
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+//
+// Static pointer to instance of this singleton.
+//
+IntersectManager* IntersectManager::mspInstance = NULL;
+
+//******************************************************************************
+// Public Member Functions : Instance Interface
+//******************************************************************************
+//==============================================================================
+// IntersectManager::CreateInstance
+//==============================================================================
+//
+// Description: Create the IntersectManager controller if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the created IntersectManager controller.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+IntersectManager* IntersectManager::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "IntersectManager" );
+ rAssert( mspInstance == NULL );
+ mspInstance = new(GMA_PERSISTENT) IntersectManager();
+MEMTRACK_POP_GROUP( "IntersectManager" );
+
+ return mspInstance;
+}
+
+//==============================================================================
+// IntersectManager::GetInstance
+//==============================================================================
+//
+// Description: Get the IntersectManager controller if exists.
+//
+// Parameters: None.
+//
+// Return: Pointer to the created IntersectManager controller.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+IntersectManager* IntersectManager::GetInstance()
+{
+ rAssert( mspInstance != NULL );
+
+ return mspInstance;
+}
+
+
+//==============================================================================
+// IntersectManager::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the IntersectManager controller.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void IntersectManager::DestroyInstance()
+{
+ //
+ // Make sure this doesn't get called twice.
+ //
+ rAssert( mspInstance != NULL );
+ delete mspInstance;
+ mspInstance = NULL;
+}
+
+//************************************************************************
+// Public Member Functions : IntersectManager "Meat" Interface
+//************************************************************************
+
+bool IntersectManager::IntersectWithPlane( rmt::Vector planeOrigin,
+ rmt::Vector planeNormal,
+ rmt::Vector rayOrigin,
+ rmt::Vector rayVector,
+ float& time )
+{
+ float numer, denom;
+
+ denom = rayVector.Dot( planeNormal );
+
+ if( rmt::Fabs( denom ) <= 0.0001f)
+ {
+ return( false );
+ }
+
+ numer = planeNormal.Dot( rayOrigin ) - planeNormal.Dot( planeOrigin );
+
+ time = -numer / denom;
+
+ return( true );
+}
+
+//========================================================================
+// IntersectManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectManager::ResetCache( const rmt::Vector& irPosn, float iRadius )
+{
+ mCachedPosn = irPosn;
+ mCachedRadius = iRadius;
+ mbSameFrame = true;
+}
+#define SPHERE_TEST
+//#define VIEW_FRUSTUM_TEST
+
+//========================================================================
+// intersectmanager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectManager::FindClosestRoad
+(
+ const rmt::Vector& irPosn,
+ float iRadius,
+ RoadSegment*& orpRoad,
+ float& oDistSqr
+)
+{
+BEGIN_PROFILE("::FindClosestRoad")
+ SphereSP desiredVol;
+ desiredVol.SetTo(irPosn,iRadius);
+
+ rmt::Sphere segmentSphere;
+
+ orpRoad = NULL;
+
+ SpatialTreeIter* pTreeIter = &(GetRenderManager()->pWorldScene()->mStaticTreeWalker);
+
+ if( !mbSameFrame ||
+ (mCachedPosn != irPosn) ||
+ (mCachedRadius != iRadius) )
+ {
+ ResetCache( irPosn, iRadius );
+ pTreeIter->MarkAll( desiredVol, WorldScene::msStaticPhys );
+ }
+
+ unsigned int itCount = 0;
+
+ oDistSqr = 100000.0f;
+
+ for(pTreeIter->MoveToFirst(); pTreeIter->NotDone(); pTreeIter->MoveToNext())
+ {
+ for( int i=pTreeIter->rCurrent().mRoadSegmentElems.mUseSize-1; i>-1; i-- )
+ {
+ RoadSegment* segment = pTreeIter->rCurrent().mRoadSegmentElems[i];
+
+ if( segment->GetRoad()->GetShortCut() )
+ continue;
+
+ itCount++;
+
+ segment->GetBoundingSphere( &segmentSphere );
+
+ rmt::Vector vec0, vec1, vec2, vec3;
+ rmt::Vector start, end;
+ segment->GetCorner( 0, vec0 );
+ segment->GetCorner( 1, vec1 );
+ segment->GetCorner( 2, vec2 );
+ segment->GetCorner( 3, vec3 );
+
+ start = ( vec0 + vec3 ) * 0.5f;
+ end = ( vec1 + vec2 ) * 0.5f;
+
+ rmt::Vector closestPtOnSeg;
+ FindClosestPointOnLine( start, end, irPosn, closestPtOnSeg );
+
+ float distSqr = ( closestPtOnSeg - irPosn ).MagnitudeSqr();
+ if( distSqr < oDistSqr )
+ {
+ orpRoad = segment;
+ oDistSqr = distSqr;
+ }
+ }
+ }
+END_PROFILE("::FindClosestRoad")
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void IntersectManager::FindClosestAnyRoad
+(
+ const rmt::Vector& irPosn,
+ float iRadius,
+ RoadSegment*& orpRoad,
+ float& oDistSqr
+)
+{
+BEGIN_PROFILE("::FindClosestAnyRoad")
+ SphereSP desiredVol;
+ desiredVol.SetTo(irPosn,iRadius);
+
+ rmt::Sphere segmentSphere;
+
+ orpRoad = NULL;
+
+ SpatialTreeIter* pTreeIter = &(GetRenderManager()->pWorldScene()->mStaticTreeWalker);
+
+ if( !mbSameFrame ||
+ (mCachedPosn != irPosn) ||
+ (mCachedRadius != iRadius) )
+ {
+ ResetCache( irPosn, iRadius );
+ pTreeIter->MarkAll( desiredVol, WorldScene::msStaticPhys );
+ }
+
+ unsigned int itCount = 0;
+
+ oDistSqr = 100000.0f;
+
+ for(pTreeIter->MoveToFirst(); pTreeIter->NotDone(); pTreeIter->MoveToNext())
+ {
+ for( int i=pTreeIter->rCurrent().mRoadSegmentElems.mUseSize-1; i>-1; i-- )
+ {
+ RoadSegment* segment = pTreeIter->rCurrent().mRoadSegmentElems[i];
+
+ itCount++;
+
+ segment->GetBoundingSphere( &segmentSphere );
+
+ rmt::Vector vec0, vec1, vec2, vec3;
+ rmt::Vector start, end;
+ segment->GetCorner( 0, vec0 );
+ segment->GetCorner( 1, vec1 );
+ segment->GetCorner( 2, vec2 );
+ segment->GetCorner( 3, vec3 );
+
+ start = ( vec0 + vec3 ) * 0.5f;
+ end = ( vec1 + vec2 ) * 0.5f;
+
+ rmt::Vector closestPtOnSeg;
+ FindClosestPointOnLine( start, end, irPosn, closestPtOnSeg );
+
+ float distSqr = ( closestPtOnSeg - irPosn ).MagnitudeSqr();
+ if( distSqr < oDistSqr )
+ {
+ orpRoad = segment;
+ oDistSqr = distSqr;
+ }
+ }
+ }
+END_PROFILE("::FindClosestAnyRoad")
+}
+//========================================================================
+// IntersectManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectManager::FindFenceElems
+(
+ rmt::Vector& irPosn,
+ float iRadius,
+ ReserveArray<FenceEntityDSG*>& orList
+)
+{
+BEGIN_PROFILE("::FindFenceElems")
+ SphereSP desiredVol;
+ desiredVol.SetTo(irPosn,iRadius);
+
+// rmt::Sphere tempSphere;
+
+ SpatialTreeIter* pTreeIter = &(GetRenderManager()->pWorldScene()->mStaticTreeWalker);
+// pTreeIter->OrTree( WorldScene::msStaticPhys );
+// pTreeIter->AndTree( ~WorldScene::msStaticPhys );
+// pTreeIter->OrTree( WorldScene::msStaticPhys );
+// pTreeIter->AndTree( ~WorldScene::msDynaPhys );
+ if( !mbSameFrame ||
+ (mCachedPosn != irPosn) ||
+ (mCachedRadius != iRadius) )
+ {
+ ResetCache( irPosn, iRadius );
+ pTreeIter->MarkAll( desiredVol, WorldScene::msStaticPhys );
+ }
+// pTreeIter->SetIterFilter( WorldScene::msDynaPhys );
+// pTreeIter->SetIterFilter( WorldScene::msStaticPhys );
+
+ orList.Allocate(200);
+
+ for(pTreeIter->MoveToFirst(); pTreeIter->NotDone(); pTreeIter->MoveToNext())
+ {
+ for( int i=pTreeIter->rCurrent().mFenceElems.mUseSize-1; i>-1; i-- )
+ {
+#ifdef SPHERE_TEST
+ //pTreeIter->rCurrent().mFenceElems[i]->GetBoundingSphere(&tempSphere);
+ rmt::Vector pt1 = pTreeIter->rCurrent().mFenceElems[i]->mStartPoint;
+ rmt::Vector pt2 = pTreeIter->rCurrent().mFenceElems[i]->mEndPoint;
+
+ rmt::Vector center = pt1;
+ center.Add( pt2 );
+ center.Scale(0.5f);
+
+ pt1.y = 0.0f;
+ pt2.y = 0.0f;
+ center.y = 0.0f;
+
+ pt1.Sub(center,pt2);
+
+ float circleRadiusSqr = pt1.MagnitudeSqr();
+
+ float maxDistSqr = (iRadius*iRadius)+circleRadiusSqr;//* (iRadius+circleRadius);
+
+ //tempSphere.centre.Sub(tempSphere.centre,irPosn);
+ center.y = irPosn.y;
+ center.Sub(irPosn);
+
+ if( center.MagnitudeSqr() < maxDistSqr )
+#endif
+ {
+ orList.Add( pTreeIter->rCurrent().mFenceElems[i] );
+ }
+ }
+ }
+END_PROFILE("::FindFenceElems")
+}
+//========================================================================
+// IntersectManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectManager::FindStaticPhysElems
+(
+ rmt::Vector& irPosn,
+ float iRadius,
+ ReserveArray<StaticPhysDSG*>& orList
+)
+{
+BEGIN_PROFILE("::FindStaticPhysElems")
+ //SpatialNode<StaticEntityDSG,StaticPhysDSG,IntersectDSG>& rCurrentLeaf =
+ SphereSP desiredVol;
+ desiredVol.SetTo(irPosn,iRadius);
+
+ int i,j;
+ rmt::Sphere tempSphere;
+
+ SpatialTreeIter* pTreeIter = &(GetRenderManager()->pWorldScene()->mStaticTreeWalker);
+// pTreeIter->OrTree( WorldScene::msStaticPhys );
+ //BEGIN_PROFILE( "AndTree" );
+ //pTreeIter->AndTree( ~WorldScene::msStaticPhys );
+ //END_PROFILE( "AndTree" );
+// BEGIN_PROFILE( "MarkAll" );
+// pTreeIter->OrTree( WorldScene::msStaticPhys );
+// pTreeIter->AndTree( ~WorldScene::msDynaPhys );
+ if( !mbSameFrame ||
+ (mCachedPosn != irPosn) ||
+ (mCachedRadius != iRadius) )
+ {
+ ResetCache( irPosn, iRadius );
+ pTreeIter->MarkAll( desiredVol, WorldScene::msStaticPhys );
+ }
+// pTreeIter->SetIterFilter( WorldScene::msDynaPhys );
+// END_PROFILE( "MarkAll" );
+ //pTreeIter->SetIterFilter( WorldScene::msStaticPhys );
+
+ orList.Allocate(200);
+
+ //BEGIN_PROFILE( "Iter" );
+ //for(pTreeIter->MoveToFirst(); pTreeIter->NotDone(); pTreeIter->MoveToNext())
+ //{
+ // for( i=pTreeIter->rCurrent().mSPhysElems.mUseSize-1; i>-1; i-- )
+ // {
+ // pTreeIter->rCurrent().mSPhysElems[i]->GetBoundingSphere(&tempSphere);
+ //
+ // maxDistSqr = (iRadius+tempSphere.radius) * (iRadius+tempSphere.radius);
+ //
+ // tempSphere.centre.Sub(tempSphere.centre,irPosn);
+ //
+ // if( tempSphere.centre.MagnitudeSqr() < maxDistSqr )
+ // {
+ // orList.Add( pTreeIter->rCurrent().mSPhysElems[i] );
+ // }
+ // }
+ //}
+ //END_PROFILE( "Iter" );
+ j= pTreeIter->mCurNodes.mUseSize-1;
+ if(j>100)
+ {
+ rReleasePrintf("\n\nWTF? %d nodes \n\n", j);
+ }
+
+// BEGIN_PROFILE( "Quack" );
+ for(j=pTreeIter->mCurNodes.mUseSize-1; j>-1; j--)
+ {
+ for( i=pTreeIter->mCurNodes[j]->mSPhysElems.mUseSize-1; i>-1; i-- )
+ {
+#ifdef SPHERE_TEST
+ pTreeIter->mCurNodes[j]->mSPhysElems[i]->GetBoundingSphere(&tempSphere);
+
+ float maxDistSqr = (iRadius+tempSphere.radius) * (iRadius+tempSphere.radius);
+
+ tempSphere.centre.Sub(tempSphere.centre,irPosn);
+
+ if( tempSphere.centre.MagnitudeSqr() < maxDistSqr )
+#endif
+ {
+ orList.Add( pTreeIter->mCurNodes[j]->mSPhysElems[i] );
+ }
+ }
+ }
+// END_PROFILE( "Quack" );
+END_PROFILE("::FindStaticPhysElems")
+}
+
+
+//========================================================================
+// IntersectManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectManager::FindStaticElems
+(
+ rmt::Vector& irPosn,
+ float iRadius,
+ ReserveArray<StaticEntityDSG*>& orList
+)
+{
+BEGIN_PROFILE("::FindStaticElems")
+ SphereSP desiredVol;
+ desiredVol.SetTo(irPosn,iRadius);
+
+ int i,j;
+ rmt::Sphere tempSphere;
+ SpatialTreeIter* pTreeIter = &(GetRenderManager()->pWorldScene()->mStaticTreeWalker);
+ if( !mbSameFrame ||
+ (mCachedPosn != irPosn) ||
+ (mCachedRadius != iRadius) )
+ {
+ ResetCache( irPosn, iRadius );
+ pTreeIter->MarkAll( desiredVol, WorldScene::msStaticPhys );
+ }
+ orList.Allocate(200);
+
+ //BEGIN_PROFILE( "Iter" );
+ //for(pTreeIter->MoveToFirst(); pTreeIter->NotDone(); pTreeIter->MoveToNext())
+ //{
+ // for( i=pTreeIter->rCurrent().mSPhysElems.mUseSize-1; i>-1; i-- )
+ // {
+ // pTreeIter->rCurrent().mSPhysElems[i]->GetBoundingSphere(&tempSphere);
+ //
+ // maxDistSqr = (iRadius+tempSphere.radius) * (iRadius+tempSphere.radius);
+ //
+ // tempSphere.centre.Sub(tempSphere.centre,irPosn);
+ //
+ // if( tempSphere.centre.MagnitudeSqr() < maxDistSqr )
+ // {
+ // orList.Add( pTreeIter->rCurrent().mSPhysElems[i] );
+ // }
+ // }
+ //}
+ //END_PROFILE( "Iter" );
+ j= pTreeIter->mCurNodes.mUseSize-1;
+ if(j>100)
+ {
+ rReleasePrintf("\n\nWTF? %d nodes \n\n", j);
+ }
+
+// BEGIN_PROFILE( "Quack" );
+ for(j=pTreeIter->mCurNodes.mUseSize-1; j>-1; j--)
+ {
+ for( i=pTreeIter->mCurNodes[j]->mSEntityElems.mUseSize-1; i>-1; i-- )
+ {
+#ifdef SPHERE_TEST
+ pTreeIter->mCurNodes[j]->mSEntityElems[i]->GetBoundingSphere(&tempSphere);
+
+ float maxDistSqr = (iRadius+tempSphere.radius) * (iRadius+tempSphere.radius);
+
+ tempSphere.centre.Sub(tempSphere.centre,irPosn);
+
+ if( tempSphere.centre.MagnitudeSqr() < maxDistSqr )
+#endif
+ {
+ orList.Add( pTreeIter->mCurNodes[j]->mSEntityElems[i] );
+ }
+ }
+ }
+// END_PROFILE( "Quack" );
+END_PROFILE("::FindStaticElems")
+}
+
+//========================================================================
+// IntersectManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectManager::FindDynaPhysElems
+(
+ rmt::Vector& irPosn,
+ float iRadius,
+ ReserveArray<DynaPhysDSG*>& orList
+)
+{
+BEGIN_PROFILE("::FindDynaPhysElems")
+ //SpatialNode<StaticEntityDSG,StaticPhysDSG,IntersectDSG>& rCurrentLeaf =
+ SphereSP desiredVol;
+ desiredVol.SetTo(irPosn,iRadius);
+
+ rmt::Sphere tempSphere;
+
+ SpatialTreeIter* pTreeIter = &(GetRenderManager()->pWorldScene()->mStaticTreeWalker);
+// pTreeIter->OrTree( WorldScene::msStaticPhys );
+// pTreeIter->AndTree( ~WorldScene::msDynaPhys );
+ if( !mbSameFrame ||
+ (mCachedPosn != irPosn) ||
+ (mCachedRadius != iRadius) )
+ {
+ ResetCache( irPosn, iRadius );
+ pTreeIter->MarkAll( desiredVol, WorldScene::msStaticPhys );
+ }
+// pTreeIter->SetIterFilter( WorldScene::msDynaPhys );
+
+ orList.Allocate(200);
+
+ for(pTreeIter->MoveToFirst(); pTreeIter->NotDone(); pTreeIter->MoveToNext())
+ {
+ for( int i=pTreeIter->rCurrent().mDPhysElems.mUseSize-1; i>-1; i-- )
+ {
+#ifdef SPHERE_TEST
+ pTreeIter->rCurrent().mDPhysElems[i]->GetBoundingSphere(&tempSphere);
+
+ float maxDistSqr = (iRadius+tempSphere.radius) * (iRadius+tempSphere.radius);
+
+ tempSphere.centre.Sub(tempSphere.centre,irPosn);
+
+ if( tempSphere.centre.MagnitudeSqr() < maxDistSqr )
+#endif
+ {
+ orList.Add( pTreeIter->rCurrent().mDPhysElems[i] );
+ }
+ }
+ }
+END_PROFILE("::FindDynaPhysElems")
+}
+
+//========================================================================
+// IntersectManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectManager::FindAnimPhysElems
+(
+ rmt::Vector& irPosn,
+ float iRadius,
+ ReserveArray<AnimCollisionEntityDSG*>& orList
+)
+{
+#ifndef RAD_RELEASE
+ char temp[100];
+ sprintf(temp, "::FindAnimPhysElems %f", iRadius);
+#endif
+BEGIN_PROFILE("::FindAnimPhysElems")
+ //SpatialNode<StaticEntityDSG,StaticPhysDSG,IntersectDSG>& rCurrentLeaf =
+ SphereSP desiredVol;
+ desiredVol.SetTo(irPosn,iRadius);
+
+ rmt::Sphere tempSphere;
+
+ SpatialTreeIter* pTreeIter = &(GetRenderManager()->pWorldScene()->mStaticTreeWalker);
+// pTreeIter->OrTree( WorldScene::msStaticPhys );
+// pTreeIter->AndTree( ~WorldScene::msDynaPhys );
+ if( !mbSameFrame ||
+ (mCachedPosn != irPosn) ||
+ (mCachedRadius != iRadius) )
+ {
+ ResetCache( irPosn, iRadius );
+ pTreeIter->MarkAll( desiredVol, WorldScene::msStaticPhys );
+ }
+// pTreeIter->SetIterFilter( WorldScene::msDynaPhys );
+
+ orList.Allocate(200);
+
+ for(pTreeIter->MoveToFirst(); pTreeIter->NotDone(); pTreeIter->MoveToNext())
+ {
+ for( int i=pTreeIter->rCurrent().mAnimCollElems.mUseSize-1; i>-1; i-- )
+ {
+ bool addToList = false;
+
+#ifdef VIEW_FRUSTUM_TEST
+ // Test the object and see if it is within any of the view frustums
+ pTreeIter->rCurrent().mAnimCollElems[i]->GetBoundingSphere(&tempSphere);
+
+ int numPlayers = GetGameplayManager()->GetNumPlayers();
+ for ( int player = 0 ; player < numPlayers ; player++)
+ {
+ if (GetSuperCamManager()->GetSCC( player )->GetCamera()->SphereVisible( tempSphere.centre, tempSphere.radius ) )
+ {
+ addToList = true;
+ break;
+ }
+ }
+#else
+#ifdef SPHERE_TEST
+ // Use the sphere test if the view frustum test is not enabled
+ pTreeIter->rCurrent().mAnimCollElems[i]->GetBoundingSphere(&tempSphere);
+
+ float maxDistSqr = (iRadius+tempSphere.radius) * (iRadius+tempSphere.radius);
+
+ tempSphere.centre.Sub(tempSphere.centre,irPosn);
+
+ if( tempSphere.centre.MagnitudeSqr() < maxDistSqr )
+ {
+ addToList = true;
+ }
+#else
+ // Neither test is activated, always add it to the list
+ addToList = true;
+#endif
+
+#endif
+ if ( addToList )
+ {
+ orList.Add( pTreeIter->rCurrent().mAnimCollElems[i] );
+ }
+ }
+ }
+END_PROFILE("::FindAnimPhysElems")
+}
+//========================================================================
+// IntersectManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectManager::FindTrigVolElems
+(
+ rmt::Vector& irPosn,
+ float iRadius,
+ ReserveArray<TriggerVolume*>& orList
+)
+{
+#ifndef RAD_RELEASE
+ char temp[100];
+ sprintf(temp, "::FindTrigVolElems %f", iRadius);
+#endif
+BEGIN_PROFILE(temp)
+ //SpatialNode<StaticEntityDSG,StaticPhysDSG,IntersectDSG>& rCurrentLeaf =
+ SphereSP desiredVol;
+ desiredVol.SetTo(irPosn,iRadius);
+
+
+ rmt::Sphere tempSphere;
+
+ SpatialTreeIter* pTreeIter = &(GetRenderManager()->pWorldScene()->mStaticTreeWalker);
+// pTreeIter->OrTree( WorldScene::msStaticPhys );
+// pTreeIter->AndTree( ~WorldScene::msDynaPhys );
+ if( !mbSameFrame ||
+ (mCachedPosn != irPosn) ||
+ (mCachedRadius != iRadius) )
+ {
+ ResetCache( irPosn, iRadius );
+ pTreeIter->MarkAll( desiredVol, WorldScene::msStaticPhys );
+ }
+// pTreeIter->SetIterFilter( WorldScene::msDynaPhys );
+
+ orList.Allocate(200);
+
+ for(pTreeIter->MoveToFirst(); pTreeIter->NotDone(); pTreeIter->MoveToNext())
+ {
+ for( int i=pTreeIter->rCurrent().mTrigVolElems.mUseSize-1; i>-1; i-- )
+ {
+#ifdef SPHERE_TEST
+ pTreeIter->rCurrent().mTrigVolElems[i]->GetBoundingSphere(&tempSphere);
+
+ float maxDistSqr = (iRadius+tempSphere.radius) * (iRadius+tempSphere.radius);
+
+ tempSphere.centre.Sub(tempSphere.centre,irPosn);
+
+ if( tempSphere.centre.MagnitudeSqr() < maxDistSqr )
+#endif
+ {
+ orList.Add( pTreeIter->rCurrent().mTrigVolElems[i] );
+ }
+ }
+ }
+END_PROFILE(temp)
+}
+
+
+//========================================================================
+// IntersectManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectManager::FindRoadSegmentElems
+(
+ rmt::Vector& irPosn,
+ float iRadius,
+ ReserveArray<RoadSegment*>& orList
+)
+{
+BEGIN_PROFILE("::FindRoadSegmentElems")
+ //SpatialNode<StaticEntityDSG,StaticPhysDSG,IntersectDSG>& rCurrentLeaf =
+ SphereSP desiredVol;
+ desiredVol.SetTo(irPosn,iRadius);
+
+ rmt::Sphere segmentSphere;
+ //rmt::Vector segmentPos;
+
+ SpatialTreeIter* pTreeIter = &(GetRenderManager()->pWorldScene()->mStaticTreeWalker);
+// pTreeIter->OrTree( WorldScene::msStaticPhys );
+// pTreeIter->AndTree( ~WorldScene::msDynaPhys );
+ if( !mbSameFrame ||
+ (mCachedPosn != irPosn) ||
+ (mCachedRadius != iRadius) )
+ {
+ ResetCache( irPosn, iRadius );
+ pTreeIter->MarkAll( desiredVol, WorldScene::msStaticPhys );
+ }
+// pTreeIter->SetIterFilter( WorldScene::msDynaPhys );
+
+ orList.Allocate(200);
+
+ unsigned int itCount = 0;
+
+ for(pTreeIter->MoveToFirst(); pTreeIter->NotDone(); pTreeIter->MoveToNext())
+ {
+ for( int i=pTreeIter->rCurrent().mRoadSegmentElems.mUseSize-1; i>-1; i-- )
+ {
+ RoadSegment* segment = pTreeIter->rCurrent().mRoadSegmentElems[i];
+#ifdef SPHERE_TEST
+ itCount++;
+
+ segment->GetBoundingSphere( &segmentSphere );
+ //segment->GetPosition( &segmentPos );
+
+ float halfrad = segmentSphere.radius;// / 2.0f;
+ float maxDistSqr = (iRadius+halfrad) * (iRadius+halfrad);
+ float minDistSqr;
+ if( halfrad > iRadius )
+ {
+ minDistSqr = 0.0f;
+ }
+ else
+ {
+ minDistSqr = (iRadius-halfrad) * (iRadius-halfrad);
+ }
+
+ rmt::Vector temp;
+ //temp.Sub(segmentPos,irPosn);
+ temp.Sub( segmentSphere.centre, irPosn );
+
+ if( temp.MagnitudeSqr() < maxDistSqr &&
+ temp.MagnitudeSqr() > minDistSqr)
+#endif
+ {
+ orList.Add( segment );
+ }
+ }
+ }
+END_PROFILE("::FindRoadSegmentElems")
+}
+
+//========================================================================
+// IntersectManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectManager::FindPathSegmentElems
+(
+ rmt::Vector& irPosn,
+ float iRadius,
+ ReserveArray<PathSegment*>& orList
+)
+{
+BEGIN_PROFILE("::FindPathSegmentElems")
+ //SpatialNode<StaticEntityDSG,StaticPhysDSG,IntersectDSG>& rCurrentLeaf =
+ SphereSP desiredVol;
+ desiredVol.SetTo(irPosn,iRadius);
+
+
+ rmt::Sphere segmentSphere;
+ //rmt::Vector segmentPos;
+
+ SpatialTreeIter* pTreeIter = &(GetRenderManager()->pWorldScene()->mStaticTreeWalker);
+// pTreeIter->OrTree( WorldScene::msStaticPhys );
+// pTreeIter->AndTree( ~WorldScene::msDynaPhys );
+ if( !mbSameFrame ||
+ (mCachedPosn != irPosn) ||
+ (mCachedRadius != iRadius) )
+ {
+ ResetCache( irPosn, iRadius );
+ pTreeIter->MarkAll( desiredVol, WorldScene::msStaticPhys );
+ }
+// pTreeIter->SetIterFilter( WorldScene::msDynaPhys );
+
+ orList.Allocate(200);
+
+ unsigned int itCount = 0;
+
+ for(pTreeIter->MoveToFirst(); pTreeIter->NotDone(); pTreeIter->MoveToNext())
+ {
+ for( int i=pTreeIter->rCurrent().mPathSegmentElems.mUseSize-1; i>-1; i-- )
+ {
+ PathSegment* segment = pTreeIter->rCurrent().mPathSegmentElems[i];
+#ifdef SPHERE_TEST
+ itCount++;
+
+ segment->GetBoundingSphere( &segmentSphere );
+ //segment->GetPosition( &segmentPos );
+
+ float halfrad = segmentSphere.radius;// / 2.0f;
+ float maxDistSqr = (iRadius+halfrad) * (iRadius+halfrad);
+ float minDistSqr;
+ if( halfrad > iRadius )
+ {
+ minDistSqr = 0.0f;
+ }
+ else
+ {
+ minDistSqr = (iRadius-halfrad) * (iRadius-halfrad);
+ }
+
+ rmt::Vector temp;
+ //temp.Sub(segmentPos,irPosn);
+ temp.Sub( segmentSphere.centre, irPosn );
+
+ if( temp.MagnitudeSqr() < maxDistSqr &&
+ temp.MagnitudeSqr() > minDistSqr)
+#endif
+ {
+ orList.Add( segment );
+ }
+ }
+ }
+END_PROFILE("::FindPathSegmentElems")
+}
+
+
+
+
+
+
+//#ifndef RAD_RELEASE
+IntersectDSG* IntersectManager::FindIntersectionTri
+(
+ rmt::Vector& irPosn,
+ rmt::Vector* opTriPoints,
+ rmt::Vector& orIntersectPosn,
+ rmt::Vector& orIntersectNorm
+)
+{
+ SpatialNode& rCurrentLeaf = GetRenderManager()->pWorldScene()->mStaticTreeWalker.rSeekLeaf((Vector3f&)irPosn);
+
+ rmt::Vector tmpVect, tmpVect2, TriPts[3], TriNorm;//, TriCtr;
+ float DistToPlane, ClosestDistToPlane = 20000.0f;//TriRadius,
+ int foundTerrainType;
+
+ // iRadius *= 2.5f;
+ //MS7 Default, in case we don't find an intersection
+
+ for( int i=rCurrentLeaf.mIntersectElems.mUseSize-1; i>-1; i-- )
+ {
+ for( int j=rCurrentLeaf.mIntersectElems[i]->nTris()-1; j>-1; j-- )
+ {
+ foundTerrainType = rCurrentLeaf.mIntersectElems[i]->mTri(j,TriPts,TriNorm );//TriCtr,
+ // This first test finds the ground plane directly beneath the sphere (on the y axis)
+
+ // make tmpVect a vector from the sphere position towards the ground
+ tmpVect2.Set( 0.0f, -1.0f, 0.0f );
+
+ //if(( !obFoundPlane ) && ( tmpVect2.Dot( TriNorm ) < 0 ))
+ {
+ tmpVect.Set( irPosn.x, 10000.0f, irPosn.z );
+
+ if( IntersectWithPlane( TriPts[ 0 ], TriNorm, tmpVect, tmpVect2, DistToPlane ) )
+ {
+ if(( DistToPlane >= 0.0f ) /*&& ( DistToPlane <= iRadius )*/)
+ {
+ rmt::Vector pointOnPlane = tmpVect2;
+ pointOnPlane.Scale( DistToPlane );
+
+ pointOnPlane.Add( tmpVect );
+// pointOnPlane.Add( irPosn );
+
+ tmpVect.Sub(TriPts[0],TriPts[1]);
+ tmpVect.CrossProduct(TriNorm);
+
+ tmpVect2.Sub(pointOnPlane,TriPts[1]);
+ if( tmpVect.Dot(tmpVect2) >= -0.00f )
+ {
+ tmpVect.Sub(TriPts[1],TriPts[2]);
+ tmpVect.CrossProduct(TriNorm);
+
+ tmpVect2.Sub(pointOnPlane,TriPts[2]);
+ if( tmpVect.Dot(tmpVect2) >= -0.00f )
+ {
+ tmpVect.Sub(TriPts[2],TriPts[0]);
+ tmpVect.CrossProduct(TriNorm);
+
+ tmpVect2.Sub(pointOnPlane,TriPts[0]);
+ if( tmpVect.Dot(tmpVect2) >= -0.00f )
+ {
+ opTriPoints[0] = TriPts[0];
+ opTriPoints[1] = TriPts[1];
+ opTriPoints[2] = TriPts[2];
+ orIntersectPosn = pointOnPlane;
+ orIntersectNorm = TriNorm;
+
+ return rCurrentLeaf.mIntersectElems[i];
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+}
+IntersectDSG* IntersectManager::FindIntersectionTriNew
+(
+ rmt::Vector& irPosn,
+ rmt::Vector* opTriPoints,
+ rmt::Vector& orIntersectPosn,
+ rmt::Vector& orIntersectNorm
+)
+{
+ SpatialNode& rCurrentLeaf = GetRenderManager()->pWorldScene()->mStaticTreeWalker.rSeekLeaf((Vector3f&)irPosn);
+
+ rmt::Vector tmpVect, tmpVect2, TriPts[3], TriNorm;//, TriCtr;
+ float DistToPlane, ClosestDistToPlane = 20000.0f;//TriRadius,
+ int foundTerrainType;
+
+ // iRadius *= 2.5f;
+ //MS7 Default, in case we don't find an intersection
+
+#if defined(RAD_PS2) && !defined(RAD_MW)
+ radTime64 time = radTimeGetMicroseconds64();
+static rmt::Vector4 alignedVertices[3] __attribute__((aligned(16))); //vf1,vf2,vf3
+static rmt::Vector4 alignedNormal __attribute__((aligned(16))); //vf4
+
+static rmt::Vector4 alignedRayOrigin __attribute__((aligned(16))); //vf5
+
+static rmt::Vector4 alignedPointOnPlane __attribute__((aligned(16))); //vf7
+static rmt::Vector4 alignedDistFromPlane __attribute__((aligned(16)));//vf8
+
+ for( int i=rCurrentLeaf.mIntersectElems.mUseSize-1; i>-1; i-- )
+ {
+ rCurrentLeaf.mIntersectElems[i]->IntoTheVoid_WithGoFastaStripes();
+ for( int j=rCurrentLeaf.mIntersectElems[i]->nTris()-1; j>-1; j-- )
+ {
+/*
+ foundTerrainType = rCurrentLeaf.mIntersectElems[i]->mTri(j,alignedVertices,alignedNormal);//TriCtr,
+
+ alignedRayVector.Set( 0.0f, -1.0f, 0.0f );
+ alignedRayOrigin.Set( irPosn.x, 10000.0f, irPosn.z );
+
+ if( IntersectWithPlane( alignedVertices[0], alignedNormal, alignedRayOrigin, alignedRayVector, alignedDistFromPlane.x) )
+ {
+*/
+ TriPts[0] = irPosn;
+ foundTerrainType = rCurrentLeaf.mIntersectElems[i]->mFlatTriFast(j,TriPts,TriNorm );//TriCtr,
+ if(foundTerrainType==-1) continue;
+ alignedVertices[0] = TriPts[0];
+ alignedVertices[1] = TriPts[1];
+ alignedVertices[2] = TriPts[2];
+ alignedNormal = TriNorm;
+
+ TriPts[2].Set( 0.0f, -1.0f, 0.0f );
+ TriPts[1].Set( irPosn.x, 10000.0f, irPosn.z );
+ if( IntersectWithPlane( TriPts[ 0 ], TriNorm, TriPts[1], TriPts[2], DistToPlane ) )
+ {
+ if( DistToPlane >= 0.0f )//alignedDistFromPlane.x >= 0.0f )
+ {
+ alignedDistFromPlane.Set( DistToPlane, DistToPlane, DistToPlane, 1.0f );
+ //alignedDistFromPlane.y = alignedDistFromPlane.x;
+ //alignedDistFromPlane.z = alignedDistFromPlane.x;
+
+ alignedPointOnPlane = TriPts[2];
+ alignedRayOrigin = TriPts[1];
+
+ asm __volatile__("
+ lqc2 vf7, 0(%5) # load pointOnPlane
+ lqc2 vf8, 0(%6) # load distFromPlane
+ lqc2 vf5, 0(%4) # load rayOrigin
+ vmul.xyz vf7, vf7, vf8 # pointOnPlane.Scale( DistToPlane );
+ lqc2 vf1, 0(%0) # load vertex0
+ lqc2 vf2, 0(%1) # load vertex1
+ vadd.xyz vf7, vf7, vf5 # pointOnPlane.Add( tmpVect ); tempVect == alignedRayOrigin, tempVect2 == alignedRayVector
+ lqc2 vf4, 0(%3) # load normal
+ vsub.xyz vf9, vf1, vf2 # tmpVect.Sub(TriPts[0],TriPts[1]);
+ lqc2 vf3, 0(%2) # load vertex2
+ vopmula.xyz ACC, vf9, vf4 # outer product stage 1 tmpVect.CrossProduct(TriNorm);
+ vopmsub.xyz vf9, vf4, vf9 # outer product stage 2
+ vsub.xyz vf10,vf7, vf2 # tmpVect2.Sub(pointOnPlane,TriPts[1]);
+ vmul.xyz vf20,vf10, vf9 # ==if( tmpVect.Dot(tmpVect2) >= 0.00f)
+ sqc2 vf20, 0(%0) # store result in vertex 0
+ vsub.xyz vf9, vf2, vf3 # tmpVect.Sub(TriPts[1],TriPts[2]);
+ vopmula.xyz ACC, vf9, vf4 # outer product stage 1 tmpVect.CrossProduct(TriNorm);
+ vopmsub.xyz vf9, vf4, vf9 # outer product stage 2
+ vsub.xyz vf10,vf7, vf3 # tmpVect2.Sub(pointOnPlane,TriPts[2]);
+ vmul.xyz vf21,vf10, vf9 # ==if( tmpVect.Dot(tmpVect2) >= 0.00f)
+ sqc2 vf21, 0(%1) # store result in vertex 1
+ vsub.xyz vf9, vf3, vf1 # tmpVect.Sub(TriPts[2],TriPts[0]);
+ vopmula.xyz ACC, vf9, vf4 # outer product stage 1 tmpVect.CrossProduct(TriNorm);
+ vopmsub.xyz vf9, vf4, vf9 # outer product stage 2
+ vsub.xyz vf10,vf7, vf1 # tmpVect2.Sub(pointOnPlane,TriPts[0]);
+ vmul.xyz vf22,vf10, vf9 # ==if( tmpVect.Dot(tmpVect2) >= 0.00f)
+ sqc2 vf22, 0(%2) # store result in vertex 2
+ sqc2 vf7, 0(%5) # store result in vertex 2
+ ": // no outputs
+ : "r" (&(alignedVertices[0])),
+ "r" (&(alignedVertices[1])),
+ "r" (&(alignedVertices[2])),
+ "r" (&alignedNormal),
+ "r" (&alignedRayOrigin),
+ "r" (&alignedPointOnPlane),
+ "r" (&alignedDistFromPlane)
+ : "memory" );
+
+ if( ((alignedVertices[0].x+alignedVertices[0].y+alignedVertices[0].z) >= 0.00f)
+ && ((alignedVertices[1].x+alignedVertices[1].y+alignedVertices[1].z) >= 0.00f)
+ && ((alignedVertices[2].x+alignedVertices[2].y+alignedVertices[2].z) >= 0.00f)
+ )
+ {
+ orIntersectNorm = alignedNormal;
+ orIntersectPosn = alignedPointOnPlane;
+ rCurrentLeaf.mIntersectElems[i]->OutOfTheVoid_WithGoFastaStripes();
+
+ //time = radTimeGetMicroseconds64()-time;
+ //rReleasePrintf("vu0 found t=%d \t-=- ",(int)time);
+
+ return rCurrentLeaf.mIntersectElems[i];
+ }
+ }
+ }
+ }
+ rCurrentLeaf.mIntersectElems[i]->OutOfTheVoid_WithGoFastaStripes();
+ }
+
+ //time = radTimeGetMicroseconds64()-time;
+ //rReleasePrintf("vu0 miss t=%d \t-=- ",(int)time);
+ return NULL;
+
+#else
+// time = radTimeGetMicroseconds64();
+ for( int i=rCurrentLeaf.mIntersectElems.mUseSize-1; i>-1; i-- )
+ {
+ rCurrentLeaf.mIntersectElems[i]->IntoTheVoid_WithGoFastaStripes();
+ for( int j=rCurrentLeaf.mIntersectElems[i]->nTris()-1; j>-1; j-- )
+ {
+ foundTerrainType = rCurrentLeaf.mIntersectElems[i]->mTri(j,TriPts,TriNorm );//TriCtr,
+ // This first test finds the ground plane directly beneath the sphere (on the y axis)
+
+ // make tmpVect a vector from the sphere position towards the ground
+ tmpVect2.Set( 0.0f, -1.0f, 0.0f );
+
+ //if(( !obFoundPlane ) && ( tmpVect2.Dot( TriNorm ) < 0 ))
+ {
+ tmpVect.Set( irPosn.x, 10000.0f, irPosn.z );
+
+ if( IntersectWithPlane( TriPts[ 0 ], TriNorm, tmpVect, tmpVect2, DistToPlane ) )
+ {
+ if(( DistToPlane >= 0.0f ) )
+ {
+ rmt::Vector pointOnPlane = tmpVect2;
+ pointOnPlane.Scale( DistToPlane );
+
+ pointOnPlane.Add( tmpVect );
+
+ tmpVect.Sub(TriPts[0],TriPts[1]);
+ tmpVect.CrossProduct(TriNorm);
+
+ tmpVect2.Sub(pointOnPlane,TriPts[1]);
+ if( tmpVect.Dot(tmpVect2) >= 0.00f)
+ {
+ tmpVect.Sub(TriPts[1],TriPts[2]);
+ tmpVect.CrossProduct(TriNorm);
+
+ tmpVect2.Sub(pointOnPlane,TriPts[2]);
+ if( tmpVect.Dot(tmpVect2) >= 0.00f)
+ {
+ tmpVect.Sub(TriPts[2],TriPts[0]);
+ tmpVect.CrossProduct(TriNorm);
+
+ tmpVect2.Sub(pointOnPlane,TriPts[0]);
+ if( tmpVect.Dot(tmpVect2) >= 0.00f)
+ {
+ orIntersectNorm = TriNorm;
+ orIntersectPosn = pointOnPlane;
+ rCurrentLeaf.mIntersectElems[i]->OutOfTheVoid_WithGoFastaStripes();
+
+ //time = radTimeGetMicroseconds64()-time;
+ //rReleasePrintf("normal found t=%d\n",(int)time);
+
+ return rCurrentLeaf.mIntersectElems[i];
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ rCurrentLeaf.mIntersectElems[i]->OutOfTheVoid_WithGoFastaStripes();
+ }
+
+ return NULL;
+#endif
+}
+//#endif
+
+#if 1
+//========================================================================
+// IntersectManager::FindIntersection
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+int IntersectManager::FindIntersection
+(
+ rmt::Vector& irPosn,
+ bool& obFoundPlane,
+ rmt::Vector& orGroundPlaneNorm,
+ rmt::Vector& orGroundPlanePosn
+)
+{
+ SpatialNode& rCurrentLeaf = GetRenderManager()->pWorldScene()->mStaticTreeWalker.rSeekLeaf((Vector3f&)irPosn);
+
+ rmt::Vector tmpVect, tmpVect2, TriPts[3], TriNorm;//, TriCtr;
+ float DistToPlane, ClosestDistToPlane = 20000.0f;//TriRadius,
+ int foundTerrainType;
+
+ // iRadius *= 2.5f;
+ //MS7 Default, in case we don't find an intersection
+ obFoundPlane = false;
+
+#if defined(RAD_PS2) && !defined(RAD_MW)
+ //radTime64 time = radTimeGetMicroseconds64();
+static rmt::Vector4 alignedVertices[3] __attribute__((aligned(16))); //vf1,vf2,vf3
+static rmt::Vector4 alignedNormal __attribute__((aligned(16))); //vf4
+
+static rmt::Vector4 alignedRayOrigin __attribute__((aligned(16))); //vf5
+
+static rmt::Vector4 alignedPointOnPlane __attribute__((aligned(16))); //vf7
+static rmt::Vector4 alignedDistFromPlane __attribute__((aligned(16)));//vf8
+
+ for( int i=rCurrentLeaf.mIntersectElems.mUseSize-1; i>-1; i-- )
+ {
+ rCurrentLeaf.mIntersectElems[i]->IntoTheVoid_WithGoFastaStripes();
+ for( int j=rCurrentLeaf.mIntersectElems[i]->nTris()-1; j>-1; j-- )
+ {
+/*
+ foundTerrainType = rCurrentLeaf.mIntersectElems[i]->mTri(j,alignedVertices,alignedNormal);//TriCtr,
+
+ alignedRayVector.Set( 0.0f, -1.0f, 0.0f );
+ alignedRayOrigin.Set( irPosn.x, 10000.0f, irPosn.z );
+
+ if( IntersectWithPlane( alignedVertices[0], alignedNormal, alignedRayOrigin, alignedRayVector, alignedDistFromPlane.x) )
+ {
+*/
+ TriPts[0] = irPosn;
+ foundTerrainType = rCurrentLeaf.mIntersectElems[i]->mFlatTriFast(j,TriPts,TriNorm );//TriCtr,
+ if(foundTerrainType==-1) continue;
+ alignedVertices[0] = TriPts[0];
+ alignedVertices[1] = TriPts[1];
+ alignedVertices[2] = TriPts[2];
+ alignedNormal = TriNorm;
+
+ TriPts[2].Set( 0.0f, -1.0f, 0.0f );
+ TriPts[1].Set( irPosn.x, 10000.0f, irPosn.z );
+ if( IntersectWithPlane( TriPts[ 0 ], TriNorm, TriPts[1], TriPts[2], DistToPlane ) )
+ {
+ if( DistToPlane >= 0.0f )//alignedDistFromPlane.x >= 0.0f )
+ {
+ alignedDistFromPlane.Set( DistToPlane, DistToPlane, DistToPlane, 1.0f );
+ //alignedDistFromPlane.y = alignedDistFromPlane.x;
+ //alignedDistFromPlane.z = alignedDistFromPlane.x;
+
+ alignedPointOnPlane = TriPts[2];
+ alignedRayOrigin = TriPts[1];
+
+ asm __volatile__("
+ lqc2 vf7, 0(%5) # load pointOnPlane
+ lqc2 vf8, 0(%6) # load distFromPlane
+ lqc2 vf5, 0(%4) # load rayOrigin
+ vmul.xyz vf7, vf7, vf8 # pointOnPlane.Scale( DistToPlane );
+ lqc2 vf1, 0(%0) # load vertex0
+ lqc2 vf2, 0(%1) # load vertex1
+ vadd.xyz vf7, vf7, vf5 # pointOnPlane.Add( tmpVect ); tempVect == alignedRayOrigin, tempVect2 == alignedRayVector
+ lqc2 vf4, 0(%3) # load normal
+ vsub.xyz vf9, vf1, vf2 # tmpVect.Sub(TriPts[0],TriPts[1]);
+ lqc2 vf3, 0(%2) # load vertex2
+ vopmula.xyz ACC, vf9, vf4 # outer product stage 1 tmpVect.CrossProduct(TriNorm);
+ vopmsub.xyz vf9, vf4, vf9 # outer product stage 2
+ vsub.xyz vf10,vf7, vf2 # tmpVect2.Sub(pointOnPlane,TriPts[1]);
+ vmul.xyz vf20,vf10, vf9 # ==if( tmpVect.Dot(tmpVect2) >= 0.00f)
+ sqc2 vf20, 0(%0) # store result in vertex 0
+ vsub.xyz vf9, vf2, vf3 # tmpVect.Sub(TriPts[1],TriPts[2]);
+ vopmula.xyz ACC, vf9, vf4 # outer product stage 1 tmpVect.CrossProduct(TriNorm);
+ vopmsub.xyz vf9, vf4, vf9 # outer product stage 2
+ vsub.xyz vf10,vf7, vf3 # tmpVect2.Sub(pointOnPlane,TriPts[2]);
+ vmul.xyz vf21,vf10, vf9 # ==if( tmpVect.Dot(tmpVect2) >= 0.00f)
+ sqc2 vf21, 0(%1) # store result in vertex 1
+ vsub.xyz vf9, vf3, vf1 # tmpVect.Sub(TriPts[2],TriPts[0]);
+ vopmula.xyz ACC, vf9, vf4 # outer product stage 1 tmpVect.CrossProduct(TriNorm);
+ vopmsub.xyz vf9, vf4, vf9 # outer product stage 2
+ vsub.xyz vf10,vf7, vf1 # tmpVect2.Sub(pointOnPlane,TriPts[0]);
+ vmul.xyz vf22,vf10, vf9 # ==if( tmpVect.Dot(tmpVect2) >= 0.00f)
+ sqc2 vf22, 0(%2) # store result in vertex 2
+ sqc2 vf7, 0(%5) # store result in vertex 2
+ ": // no outputs
+ : "r" (&(alignedVertices[0])),
+ "r" (&(alignedVertices[1])),
+ "r" (&(alignedVertices[2])),
+ "r" (&alignedNormal),
+ "r" (&alignedRayOrigin),
+ "r" (&alignedPointOnPlane),
+ "r" (&alignedDistFromPlane)
+ : "memory" );
+
+ if( ((alignedVertices[0].x+alignedVertices[0].y+alignedVertices[0].z) >= 0.00f)
+ && ((alignedVertices[1].x+alignedVertices[1].y+alignedVertices[1].z) >= 0.00f)
+ && ((alignedVertices[2].x+alignedVertices[2].y+alignedVertices[2].z) >= 0.00f)
+ )
+ {
+ orGroundPlaneNorm = alignedNormal;
+ orGroundPlanePosn = alignedPointOnPlane;
+ obFoundPlane = true;
+ rCurrentLeaf.mIntersectElems[i]->OutOfTheVoid_WithGoFastaStripes();
+
+ //time = radTimeGetMicroseconds64()-time;
+ //rReleasePrintf("vu0 found t=%d \t-=- ",(int)time);
+
+ return foundTerrainType;
+ //j=-1; //i=-1;
+ }
+ }
+ }
+ }
+ rCurrentLeaf.mIntersectElems[i]->OutOfTheVoid_WithGoFastaStripes();
+ }
+
+ //time = radTimeGetMicroseconds64()-time;
+ //rReleasePrintf("vu0 miss t=%d \t-=- ",(int)time);
+ return 0;
+
+#else
+ obFoundPlane = false;
+// time = radTimeGetMicroseconds64();
+ for( int i=rCurrentLeaf.mIntersectElems.mUseSize-1; i>-1; i-- )
+ {
+ rCurrentLeaf.mIntersectElems[i]->IntoTheVoid_WithGoFastaStripes();
+ for( int j=rCurrentLeaf.mIntersectElems[i]->nTris()-1; j>-1; j-- )
+ {
+ //foundTerrainType = rCurrentLeaf.mIntersectElems[i]->mTri(j,TriPts,TriNorm );//TriCtr,
+ TriPts[0] = irPosn;
+ foundTerrainType = rCurrentLeaf.mIntersectElems[i]->mFlatTriFast(j,TriPts,TriNorm );
+ if(foundTerrainType==-1) continue;
+ // This first test finds the ground plane directly beneath the sphere (on the y axis)
+
+ // make tmpVect a vector from the sphere position towards the ground
+ tmpVect2.Set( 0.0f, -1.0f, 0.0f );
+
+ //if(( !obFoundPlane ) && ( tmpVect2.Dot( TriNorm ) < 0 ))
+ {
+ tmpVect.Set( irPosn.x, 10000.0f, irPosn.z );
+
+ if( IntersectWithPlane( TriPts[ 0 ], TriNorm, tmpVect, tmpVect2, DistToPlane ) )
+ {
+ if(( DistToPlane >= 0.0f ) )
+ {
+ rmt::Vector pointOnPlane = tmpVect2;
+ pointOnPlane.Scale( DistToPlane );
+
+ pointOnPlane.Add( tmpVect );
+
+ tmpVect.Sub(TriPts[0],TriPts[1]);
+ tmpVect.CrossProduct(TriNorm);
+
+ tmpVect2.Sub(pointOnPlane,TriPts[1]);
+ if( tmpVect.Dot(tmpVect2) >= 0.00f)
+ {
+ tmpVect.Sub(TriPts[1],TriPts[2]);
+ tmpVect.CrossProduct(TriNorm);
+
+ tmpVect2.Sub(pointOnPlane,TriPts[2]);
+ if( tmpVect.Dot(tmpVect2) >= 0.00f)
+ {
+ tmpVect.Sub(TriPts[2],TriPts[0]);
+ tmpVect.CrossProduct(TriNorm);
+
+ tmpVect2.Sub(pointOnPlane,TriPts[0]);
+ if( tmpVect.Dot(tmpVect2) >= 0.00f)
+ {
+ orGroundPlaneNorm = TriNorm;
+ orGroundPlanePosn = pointOnPlane;
+ obFoundPlane = true;
+ rCurrentLeaf.mIntersectElems[i]->OutOfTheVoid_WithGoFastaStripes();
+
+ //time = radTimeGetMicroseconds64()-time;
+ //rReleasePrintf("normal found t=%d\n",(int)time);
+
+ return foundTerrainType;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ rCurrentLeaf.mIntersectElems[i]->OutOfTheVoid_WithGoFastaStripes();
+ }
+
+ return 0;
+#endif
+}
+
+#else
+//========================================================================
+// IntersectManager::FindIntersection
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+int IntersectManager::FindIntersection
+(
+ rmt::Vector& irPosn,
+ float iRadius,
+ rmt::Vector& orDeepestIntersectPosn,
+ rmt::Vector& orDeepestIntersectNorm
+)
+{
+ SpatialNode<tGeometry,tGeometry,IntersectDSG>& rCurrentLeaf = GetRenderManager()->GetWorldScene()->mStaticTreeWalker.rSeekLeaf((Vector3f&)irPosn);
+
+ rmt::Vector tmpVect, tmpVect2, TriPts[3], TriNorm, TriCtr;
+ float TriRadius, DistToPlane, ClosestDistToPlane = 20000.0f;
+ int foundTerrainType = -1;
+ // iRadius *= 2.5f;
+ //MS7 Default, in case we don't find an intersection
+ orDeepestIntersectPosn = irPosn;
+ orDeepestIntersectPosn.y -= 2.0f*iRadius;
+
+ orDeepestIntersectNorm.Set(0.0f,1.0f,0.0f);
+
+ for( int i=rCurrentLeaf.mIntersectElems.mUseSize-1; i>-1; i-- )
+ {
+ for( int j=rCurrentLeaf.mIntersectElems[i]->nTris()-1; j>-1; j-- )
+ {
+ TriRadius = rCurrentLeaf.mIntersectElems[i]->mTri(j,TriPts,TriNorm,TriCtr, &foundTerrainType );
+
+ // make tmpVect a vector from the wheel position towards the ground
+ tmpVect2.Set( 0.0f, -1.0f, 0.0f );
+
+ if( TriNorm.y < 0 )
+ {
+ int i = 0;
+ }
+
+ if( tmpVect2.Dot( TriNorm ) < 0 )
+ {
+ if( IntersectWithPlane( TriPts[ 0 ], TriNorm, irPosn, tmpVect2, DistToPlane ) )
+ {
+// if( (DistToPlane >= 0) && (DistToPlane <= iRadius) && (DistToPlane < ClosestDistToPlane) )
+ {
+ rmt::Vector pointOnPlane = tmpVect2;
+ pointOnPlane.Scale( DistToPlane );
+ pointOnPlane.Add( irPosn );
+
+ (tmpVect.Sub(TriPts[0],TriPts[1])).CrossProduct(TriNorm);
+
+ if( tmpVect.Dot(tmpVect2.Sub(pointOnPlane,TriPts[1])) >= 0.0f )
+ {
+ (tmpVect.Sub(TriPts[1],TriPts[2])).CrossProduct(TriNorm);
+
+ if( tmpVect.Dot(tmpVect2.Sub(pointOnPlane,TriPts[1])) >= 0.0f )
+ {
+ (tmpVect.Sub(TriPts[2],TriPts[0])).CrossProduct(TriNorm);
+
+ if( tmpVect.Dot(tmpVect2.Sub(pointOnPlane,TriPts[0])) >= 0.0f )
+ {
+ ClosestDistToPlane = DistToPlane;
+
+ orDeepestIntersectNorm = TriNorm;
+ orDeepestIntersectPosn = pointOnPlane;
+ return foundTerrainType;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+/*
+ for( int i=rCurrentLeaf.mIntersectElems.mUseSize-1; i>-1; i-- )
+ {
+ for( int j=rCurrentLeaf.mIntersectElems[i]->nTris()-1; j>-1; j-- )
+ {
+ TriRadius = rCurrentLeaf.mIntersectElems[i]->mTri(j,TriPts,TriNorm,TriCtr);
+ //if( tmpVect.Sub(irPosn,TriCtr).MagnitudeSqr() < (TriRadius+iRadius)*(TriRadius+iRadius))
+ {
+ DistToPlane = TriNorm.Dot(tmpVect.Sub(irPosn,TriPts[0]));
+ //if( (DistToPlane >= 0.0f)&&(DistToPlane <= iRadius)&&(DistToPlane < ClosestDistToPlane) )
+ {
+ {
+ (tmpVect.Sub(TriPts[0],TriPts[1])).CrossProduct(TriNorm);
+
+ //rAssert( tmpVect.Dot(tmpVect2.Sub(TriCtr,TriPts[1])) >= 0.0f );
+
+ if( tmpVect.Dot(tmpVect2.Sub(irPosn,TriPts[1])) >= 0.0f )
+ {
+ (tmpVect.Sub(TriPts[1],TriPts[2])).CrossProduct(TriNorm);
+
+ //rAssert( tmpVect.Dot(tmpVect2.Sub(TriCtr,TriPts[2])) >= 0.0f );
+
+ if( tmpVect.Dot(tmpVect2.Sub(irPosn,TriPts[1])) >= 0.0f )
+ {
+ (tmpVect.Sub(TriPts[2],TriPts[0])).CrossProduct(TriNorm);
+
+ //rAssert( tmpVect.Dot(tmpVect2.Sub(TriCtr,TriPts[0])) >= 0.0f );
+
+ if( tmpVect.Dot(tmpVect2.Sub(irPosn,TriPts[0])) >= 0.0f )
+ {
+ tmpVect2 = TriNorm;
+ tmpVect.Sub(irPosn,TriNorm.Scale(DistToPlane));
+
+ ClosestDistToPlane = DistToPlane;
+
+ orDeepestIntersectNorm = tmpVect2;
+ orDeepestIntersectPosn = tmpVect;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+*/
+ if( ClosestDistToPlane == 20000.0f )
+ {
+ return -1;
+ }
+ else if( orDeepestIntersectNorm.y < 0.0001f )
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+#endif
+
+
+bool IntersectManager::LineOfSightXZ( const rmt::Vector& start, const rmt::Vector& end, const DynaPhysDSG* avoidObject )
+{
+
+ rmt::Vector segmentCenter = ( start + end ) / 2;
+ float radius = ( segmentCenter - start ).Magnitude();
+
+ rmt::Vector2 midPoint;
+ midPoint.x = ( start.x + end.x ) * 0.5f;
+ midPoint.y = ( start.z + end.z ) * 0.5f;
+
+ float length;
+ rmt::Vector2 direction;
+ {
+ float diffX = end.x - start.x;
+ float diffZ = end.z - start.z;
+ length = rmt::Sqrt( diffX * diffX + diffZ * diffZ );
+ float scale = 1.0f / length;
+ direction.x = diffX * scale;
+ direction.y = diffZ * scale;
+ }
+ float halfLen = length * 0.5f;
+
+ ReserveArray< StaticPhysDSG* > staticsList( 200 );
+
+ bool lineOfSight = true;
+ // Test static objects
+ FindStaticPhysElems( segmentCenter, radius, staticsList );
+ for ( int i = 0 ; i < staticsList.mUseSize ; i++ )
+ {
+ rmt::Box3D box;
+ staticsList[i]->GetBoundingBox( &box );
+ if ( IntersectsXZ( direction, midPoint, halfLen, box ) )
+ {
+ lineOfSight = false;
+ break;
+ }
+ }
+ // Test fence pieces
+ if ( lineOfSight )
+ {
+ ReserveArray< FenceEntityDSG* > fenceList(400);
+ FindFenceElems( segmentCenter, radius, fenceList );
+ for ( int i = 0 ; i < fenceList.mUseSize ; i++ )
+ {
+ rmt::Box3D box;
+ fenceList[i]->GetBoundingBox( &box );
+ if ( IntersectsXZ( direction, midPoint, halfLen, box ) )
+ {
+ lineOfSight = false;
+ break;
+ }
+ }
+ }
+ // Test dynamic objects
+ if ( lineOfSight )
+ {
+ ReserveArray< DynaPhysDSG* > dynaList(200);
+ FindDynaPhysElems( segmentCenter, radius, dynaList );
+ for ( int i = 0 ; i < dynaList.mUseSize ; i++ )
+ {
+ // Avoid testing line of sight with the actor's DSG object
+ if ( dynaList[i] == avoidObject )
+ {
+ continue;
+ }
+
+ rmt::Box3D box;
+ dynaList[i]->GetBoundingBox( &box );
+ if ( IntersectsXZ( direction, midPoint, halfLen, box ) )
+ {
+ lineOfSight = false;
+ break;
+ }
+ }
+ }
+ return lineOfSight;
+
+}
+bool IntersectManager::LineOfSight( const rmt::Vector& start, const rmt::Vector& end, const DynaPhysDSG* avoidObject )
+{
+
+ rmt::Vector segmentCenter = ( start + end ) / 2;
+ float radius = ( segmentCenter - start ).Magnitude();
+
+ rmt::Vector midPoint;
+ midPoint = ( end + start ) * 0.5f;
+
+ float length;
+ rmt::Vector direction = end - start;
+ length = direction.Length();
+ direction.Normalize();
+ float halfLen = length * 0.5f;
+
+ ReserveArray< StaticPhysDSG* > staticsList( 200 );
+
+ bool lineOfSight = true;
+ // Test static objects
+ FindStaticPhysElems( segmentCenter, radius, staticsList );
+ for ( int i = 0 ; i < staticsList.mUseSize ; i++ )
+ {
+ rmt::Box3D box;
+ staticsList[i]->GetBoundingBox( &box );
+ if ( Intersects( direction, midPoint, halfLen, box ) )
+ {
+ lineOfSight = false;
+ break;
+ }
+ }
+ // Test fence pieces
+ if ( lineOfSight )
+ {
+ ReserveArray< FenceEntityDSG* > fenceList(400);
+ FindFenceElems( segmentCenter, radius, fenceList );
+ for ( int i = 0 ; i < fenceList.mUseSize ; i++ )
+ {
+ rmt::Box3D box;
+ fenceList[i]->GetBoundingBox( &box );
+ if ( Intersects( direction, midPoint, halfLen, box ) )
+ {
+ lineOfSight = false;
+ break;
+ }
+ }
+ }
+ // Test dynamic objects
+ if ( lineOfSight )
+ {
+ ReserveArray< DynaPhysDSG* > dynaList(200);
+ FindDynaPhysElems( segmentCenter, radius, dynaList );
+ for ( int i = 0 ; i < dynaList.mUseSize ; i++ )
+ {
+ // Avoid testing line of sight with the actor's DSG object
+ if ( dynaList[i] == avoidObject )
+ {
+ continue;
+ }
+
+ rmt::Box3D box;
+ dynaList[i]->GetBoundingBox( &box );
+ if ( Intersects( direction, midPoint, halfLen, box ) )
+ {
+ lineOfSight = false;
+ break;
+ }
+ }
+ }
+ return lineOfSight;
+
+}
+
+//************************************************************************
+//
+// Protected Member Functions : IntersectManager
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : IntersectManager
+//
+//************************************************************************
+
+
+//========================================================================
+// IntersectManager::IntersectManager
+//========================================================================
+//
+// Description:
+// Determines if a line segment intersects a bounding box
+// line segment is broken into a direction vector, the midway point of the segment
+// and the length / 2. height values are ignored when calculating
+// for a minor performance boost
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+
+bool IntersectManager::IntersectsXZ( const rmt::Vector2& direction,
+ const rmt::Vector2& midPoint,
+ float halfLen,
+ const rmt::Box3D& box )
+{
+ rmt::Vector boxMid = box.Mid();
+
+ rmt::Vector2 boxExtents;
+ boxExtents.x = box.high.x - boxMid.x;
+ boxExtents.y = box.high.z - boxMid.z;
+
+ rmt::Vector2 t;
+ t.x = boxMid.x - midPoint.x;
+ t.y = boxMid.z - midPoint.y;
+
+ if ( fabsf( t.x ) > boxExtents.x + halfLen * fabsf( direction.x ) )
+ {
+ return false;
+ }
+ if ( fabsf( t.y ) > boxExtents.y + halfLen * fabsf( direction.y ) )
+ {
+ return false;
+ }
+ float r = boxExtents.x * fabsf( direction.y ) +
+ boxExtents.y * fabsf( direction.x );
+
+ if ( fabsf( t.x * direction.y - t.y * direction.x ) > r )
+ {
+ return false;
+ }
+ return true;
+}
+
+
+bool IntersectManager::Intersects( const rmt::Vector& direction,
+ const rmt::Vector& midPoint,
+ float halfLen,
+ const rmt::Box3D& box )
+{
+ rmt::Vector boxMid = box.Mid();
+
+ rmt::Vector boxExtents;
+ boxExtents.x = box.high.x - boxMid.x;
+ boxExtents.y = box.high.y - boxMid.y;
+ boxExtents.z = box.high.z - boxMid.z;
+
+ rmt::Vector t;
+ t.x = boxMid.x - midPoint.x;
+ t.y = boxMid.y - midPoint.y;
+ t.z = boxMid.z - midPoint.z;
+
+ if ( fabsf( t.x ) > boxExtents.x + halfLen * fabsf( direction.x ) )
+ {
+ return false;
+ }
+ if ( fabsf( t.y ) > boxExtents.y + halfLen * fabsf( direction.y ) )
+ {
+ return false;
+ }
+ if ( fabsf( t.z ) > boxExtents.z + halfLen * fabsf( direction.z ) )
+ {
+ return false;
+ }
+
+ /////////////////////////////////////////////
+ float r = boxExtents.y * fabsf( direction.z ) +
+ boxExtents.z * fabsf( direction.y );
+
+ if ( fabsf( t.y * direction.z - t.z * direction.y ) > r )
+ return false;
+
+ /////////////////////////////////////////////
+ r = boxExtents.x * fabsf( direction.z ) +
+ boxExtents.z * fabsf( direction.x );
+
+ if ( fabsf( t.z * direction.x - t.x * direction.z ) > r )
+ return false;
+ /////////////////////////////////////////////
+ r = boxExtents.x * fabsf( direction.y ) +
+ boxExtents.y * fabsf( direction.x );
+
+ if ( fabsf( t.x * direction.y - t.y * direction.x ) > r )
+ return false;
+
+ return true;
+}
+
+
+//========================================================================
+// IntersectManager::IntersectManager
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+
+IntersectManager::IntersectManager()
+: mbSameFrame(false)
+{
+}
+
+//========================================================================
+// IntersectManager::~IntersectManager
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+IntersectManager::~IntersectManager()
+{
+}
diff --git a/game/code/render/IntersectManager/IntersectManager.h b/game/code/render/IntersectManager/IntersectManager.h
new file mode 100644
index 0000000..f32c6f8
--- /dev/null
+++ b/game/code/render/IntersectManager/IntersectManager.h
@@ -0,0 +1,202 @@
+#ifndef __IntersectManager_H__
+#define __IntersectManager_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: IntersectManager
+//
+// Description: The IntersectManager does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/05]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <p3d/p3dtypes.hpp>
+
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Culling/ReserveArray.h>
+class StaticPhysDSG;
+class FenceEntityDSG;
+class DynaPhysDSG;
+class AnimCollisionEntityDSG;
+class TriggerVolume;
+class RoadSegment;
+class PathSegment;
+class StaticEntityDSG;
+class IntersectDSG;
+
+enum eTerrainType
+{
+ TT_Road, // Default road terrain. Also used for sidewalk. This is default. If not set, it's this.
+ TT_Grass, // Grass type terrain most everything else which isn't road or sidewalk.
+ TT_Sand, // Sand type terrain.
+ TT_Gravel, // Loose gravel type terrain.
+ TT_Water, // Water on surface type terrain.
+ TT_Wood, // Boardwalks, docks type terrain.
+ TT_Metal, // Powerplant and other structures.
+ TT_Dirt, // Dirt type terrain.
+ TT_NumTerrainTypes
+};
+
+//========================================================================
+//
+// Synopsis: The IntersectManager; Synopsis by Inspection.
+//
+//========================================================================
+class IntersectManager
+{
+public:
+ // Static Methods (for creating, destroying and acquiring an instance
+ // of the IntersectManager)
+ static IntersectManager* CreateInstance();
+ static IntersectManager* GetInstance();
+ static void DestroyInstance();
+
+ // "Meat" Interface; it's raison d'etre
+ void FindClosestAnyRoad( const rmt::Vector& irPosn,
+ float iRadius,
+ RoadSegment*& orpRoad,
+ float& oDistSqr );
+
+ void FindClosestRoad( const rmt::Vector& irPosn,
+ float iRadius,
+ RoadSegment*& orpRoad,
+ float& oDistSqr );
+
+ void FindFenceElems( rmt::Vector& irPosn,
+ float iRadius,
+ ReserveArray<FenceEntityDSG*>& orList );
+
+ void FindStaticPhysElems( rmt::Vector& irPosn,
+ float iRadius,
+ ReserveArray<StaticPhysDSG*>& orList );
+
+ void FindStaticElems(rmt::Vector& irPos,
+ float iRadius,
+ ReserveArray<StaticEntityDSG*>& orList );
+
+
+ void FindDynaPhysElems( rmt::Vector& irPosn,
+ float iRadius,
+ ReserveArray<DynaPhysDSG*>& orList );
+
+ void FindAnimPhysElems( rmt::Vector& irPosn,
+ float iRadius,
+ ReserveArray<AnimCollisionEntityDSG*>& orList );
+
+ void FindTrigVolElems( rmt::Vector& irPosn,
+ float iRadius,
+ ReserveArray<TriggerVolume*>& orList );
+
+ void FindRoadSegmentElems(rmt::Vector& irPos,
+ float iRadius,
+ ReserveArray<RoadSegment*>& orList );
+
+ void FindPathSegmentElems(rmt::Vector& irPos,
+ float iRadius,
+ ReserveArray<PathSegment*>& orList );
+
+
+//#ifndef RAD_RELEASE
+ IntersectDSG* FindIntersectionTri( rmt::Vector& irPosn,
+ rmt::Vector* opTriPoints,
+ rmt::Vector& orIntersectPosn,
+ rmt::Vector& orIntersectNorm
+ );
+ IntersectDSG* FindIntersectionTriNew( rmt::Vector& irPosn,
+ rmt::Vector* opTriPoints,
+ rmt::Vector& orIntersectPosn,
+ rmt::Vector& orIntersectNorm
+ );
+//#endif
+#if 1
+ // The interior flag is now bit packed in as the highest order bit in the TerrainType. The terrain type is returned.
+ int FindIntersection( rmt::Vector& irPosn,
+ bool& obFoundPlane,
+ rmt::Vector& orGroundPlaneNorm,
+ rmt::Vector& orGroundPlanePosn );
+#else
+ // The interior flag is now bit packed in as the highest order bit in the TerrainType.
+ // The terrain type is returned, or -1 for no intersection.
+ int FindIntersection( rmt::Vector& irPosn, float iRadius,
+ rmt::Vector& orDeepestIntersectPosn,
+ rmt::Vector& orDeepestIntersectNorm );
+#endif
+
+ // Determine if there is a clear line of sight between start and end
+ // if specified, avoid object will be ignored in the testing
+ // All height values will be ignored in this function
+ bool LineOfSightXZ( const rmt::Vector& start, const rmt::Vector& end, const DynaPhysDSG* avoidObject = NULL );
+ bool LineOfSight( const rmt::Vector& start, const rmt::Vector& end, const DynaPhysDSG* avoidObject = NULL );
+
+
+ static bool IntersectsXZ( const rmt::Vector2& direction,
+ const rmt::Vector2& midPoint,
+ float halfLen,
+ const rmt::Box3D& box );
+
+ static bool Intersects( const rmt::Vector& direction,
+ const rmt::Vector& midPoint,
+ float halfLen,
+ const rmt::Box3D& box );
+
+
+ rmt::Vector mCachedPosn;
+ float mCachedRadius;
+ bool mbSameFrame;
+
+protected:
+ inline bool CheckRayCache( rmt::Vector& orPoint,
+ rmt::Vector& orNorm,
+ float& orDistToPlane );
+ inline bool CheckSphereCache(rmt::Vector& orPoint,
+ rmt::Vector& orNorm,
+ float& orDistToPlane );
+ inline void RayCache( rmt::Vector* ipTriPts, rmt::Vector& irNorm );
+ inline void SphereCache( rmt::Vector* ipTriPts, rmt::Vector& irNorm );
+ inline bool IsInTri( rmt::Vector* ipTriPts,
+ rmt::Vector& irTriNorm,
+ rmt::Vector& irPoint );
+
+ enum
+ {
+ msCacheSize = 4
+ };
+ rmt::Vector mSphereCache[msCacheSize*4];
+ rmt::Vector mRayCache[msCacheSize*4];
+ int mNextSphereCache, mNextRayCache;
+
+
+private:
+ void ResetCache( const rmt::Vector& irPosn, float iRadius );
+
+ IntersectManager();
+ ~IntersectManager();
+
+ bool IntersectWithPlane( rmt::Vector planeOrigin,
+ rmt::Vector planeNormal,
+ rmt::Vector rayOrigin,
+ rmt::Vector rayVector,
+ float& time );
+
+
+ // Static Private Render Data
+ static IntersectManager* mspInstance;
+};
+
+//
+// A little syntactic sugar for getting at this singleton.
+//
+inline IntersectManager* GetIntersectManager()
+{
+ return( IntersectManager::GetInstance() );
+}
+
+#endif
diff --git a/game/code/render/IntersectManager/allintersect.cpp b/game/code/render/IntersectManager/allintersect.cpp
new file mode 100644
index 0000000..eaeafd0
--- /dev/null
+++ b/game/code/render/IntersectManager/allintersect.cpp
@@ -0,0 +1 @@
+#include <render/IntersectManager/IntersectManager.cpp>
diff --git a/game/code/render/Loaders/AllWrappers.cpp b/game/code/render/Loaders/AllWrappers.cpp
new file mode 100644
index 0000000..24822c1
--- /dev/null
+++ b/game/code/render/Loaders/AllWrappers.cpp
@@ -0,0 +1,447 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: AllWrappers.cpp
+//
+// Description: Implementation for AllWrappers class.
+//
+// History: Implemented --Devin [4/24/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <raddebug.hpp>
+#include <memory/srrmemory.h>
+
+#include <render/Loaders/AllWrappers.h>
+#include <render/Loaders/GeometryWrappedLoader.h>
+#include <render/Loaders/StaticEntityLoader.h>
+#include <render/Loaders/StaticPhysLoader.h>
+#include <render/Loaders/TreeDSGLoader.h>
+#include <render/Loaders/FenceLoader.h>
+#include <render/Loaders/IntersectLoader.h>
+#include <render/Loaders/AnimCollLoader.h>
+#include <render/Loaders/AnimDSGLoader.h>
+#include <render/Loaders/DynaPhysLoader.h>
+#include <render/Loaders/InstStatPhysLoader.h>
+#include <render/Loaders/InstStatEntityLoader.h>
+#include <render/Loaders/WorldSphereLoader.h>
+#include <render/Loaders/AnimDynaPhysLoader.h>
+#include <loading/roadloader.h>
+#include <loading/pathloader.h>
+#include <render/Loaders/BillboardWrappedLoader.h>
+#include <render/Loaders/instparticlesystemloader.h>
+#include <render/Loaders/breakableobjectloader.h>
+#include <render/Loaders/LensFlareLoader.h>
+#include <loading/locatorloader.h>
+#include <p3d/drawable.hpp>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+AllWrappers* AllWrappers::mspInstance = NULL;
+
+static const int NUM_GLOBAL_ENTITIES = 32;
+
+//************************************************************************
+//
+// Public Member Functions : AllWrappers Interface
+//
+//************************************************************************
+
+//========================================================================
+// AllWrappers::CreateInstance
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+AllWrappers* AllWrappers::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "AllWrappers" );
+ rAssert(mspInstance == NULL);
+
+ mspInstance = new(GMA_PERSISTENT) AllWrappers;
+MEMTRACK_POP_GROUP( "AllWrappers" );
+
+ return mspInstance;
+}
+
+//========================================================================
+// AllWrappers::GetInstance
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+AllWrappers* AllWrappers::GetInstance()
+{
+ rAssert(mspInstance != NULL);
+
+ return mspInstance;
+}
+
+//========================================================================
+// AllWrappers::DestroyInstance
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void AllWrappers::DestroyInstance()
+{
+ rAssert(mspInstance != NULL);
+
+ delete mspInstance;
+}
+
+//========================================================================
+// AllWrappers::mLoader( int iIndex )
+//========================================================================
+//
+// Description: Get iIndexth's element
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+IWrappedLoader& AllWrappers::mLoader( int iIndex )
+{
+ rAssert( iIndex < msNumWrappers);
+
+ return *(mpLoaders[iIndex]);
+}
+
+//========================================================================
+// AllWrappers::mpLoader( int iIndex )
+//========================================================================
+//
+// Description: Get iIndexth's element's ptr
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+IWrappedLoader* AllWrappers::mpLoader( int iIndex )
+{
+ rAssert( iIndex < msNumWrappers);
+
+ return (mpLoaders[iIndex]);
+}
+
+//************************************************************************
+//
+// Protected Member Functions : AllWrappers
+//
+//************************************************************************
+void AllWrappers::CoupleAllLoaders()
+{
+MEMTRACK_PUSH_GROUP( "AllWrappers" );
+ IWrappedLoader* pWL = NULL;
+
+ for( int i=msNumWrappers-1; i>-1; i-- )
+ {
+ rAssert( mpLoaders[i] == NULL );
+
+ switch( i )
+ {
+ case msGeometry:
+ {
+ pWL = new(GMA_PERSISTENT) GeometryWrappedLoader();
+ ((GeometryWrappedLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msStaticPhys:
+ {
+
+ pWL = new(GMA_PERSISTENT) StaticPhysLoader();
+ ((StaticPhysLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msStaticEntity:
+ {
+ pWL = new(GMA_PERSISTENT) StaticEntityLoader();
+ ((StaticEntityLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msTreeDSG:
+ {
+ pWL = new(GMA_PERSISTENT) TreeDSGLoader();
+ ((TreeDSGLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msFenceEntity:
+ {
+ pWL = new(GMA_PERSISTENT) FenceLoader();
+ ((FenceLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msIntersectDSG:
+ {
+ pWL = new(GMA_PERSISTENT) IntersectLoader();
+ ((IntersectLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msAnimCollEntity:
+ {
+ pWL = new(GMA_PERSISTENT) AnimCollLoader();
+ ((AnimCollLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msDynaPhys:
+ {
+ pWL = new(GMA_PERSISTENT) DynaPhysLoader();
+ ((DynaPhysLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msInstStatEntity:
+ {
+ pWL = new(GMA_PERSISTENT) InstStatEntityLoader();
+ ((InstStatEntityLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msInstStatPhys:
+ {
+ pWL = new(GMA_PERSISTENT) InstStatPhysLoader();
+ ((InstStatPhysLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msLocator:
+ {
+ pWL = new(GMA_PERSISTENT) LocatorLoader();
+ ((LocatorLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msWorldSphere:
+ {
+ pWL = new(GMA_PERSISTENT) WorldSphereLoader();
+ ((WorldSphereLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msRoadSegment:
+ {
+ pWL = new(GMA_PERSISTENT) RoadLoader();
+ ((RoadLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msPathSegment:
+ {
+ pWL = new(GMA_PERSISTENT) PathLoader();
+ ((PathLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+
+ case msBillboard:
+ {
+ pWL = new(GMA_PERSISTENT) BillboardWrappedLoader();
+ ((BillboardWrappedLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+ case msInstParticleSystem:
+ {
+ pWL = new (GMA_PERSISTENT) InstParticleSystemLoader();
+ ((InstParticleSystemLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+ case msBreakableObject:
+ {
+ pWL = new (GMA_PERSISTENT) BreakableObjectLoader();
+ ((BreakableObjectLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+ case msAnimEntity:
+ {
+ pWL = new (GMA_PERSISTENT) AnimDSGLoader();
+ ((AnimDSGLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+ case msLensFlare:
+ {
+ pWL = new(GMA_PERSISTENT) LensFlareLoader();
+ ((LensFlareLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+ case msAnimDynaPhys:
+ {
+ pWL = new(GMA_PERSISTENT) AnimDynaPhysLoader();
+ ((AnimDynaPhysLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+ case msAnimDynaPhysWrapper:
+ {
+ pWL = new (GMA_PERSISTENT) AnimDynaPhysWrapperLoader();
+ ((AnimDynaPhysWrapperLoader*)pWL)->AddRef();
+ mpLoaders[i] = pWL;
+ }
+ break;
+ default:
+ rAssert(false);
+ break;
+ }
+ }
+MEMTRACK_POP_GROUP( "AllWrappers" );
+}
+
+void AllWrappers::AddGlobalEntity(tDrawable* Entity)
+{
+ if( !Entity )
+ {
+ return;
+ }
+ rAssert(mNumGlobalEntities < NUM_GLOBAL_ENTITIES);
+ rAssert(GetGlobalEntity(Entity->GetUID()) == 0);
+ mpGlobalEntities[mNumGlobalEntities] = Entity;
+ mpGlobalEntities[mNumGlobalEntities]->AddRef();
+ ++mNumGlobalEntities;
+}
+
+tDrawable* AllWrappers::GetGlobalEntity(tUID EntityID) const
+{
+ for(int i = 0; i < mNumGlobalEntities; ++i)
+ {
+ if(mpGlobalEntities[i]->GetUID() == EntityID )
+ {
+ return mpGlobalEntities[i];
+ }
+ }
+ return 0;
+}
+
+void AllWrappers::ClearGlobalEntities(void)
+{
+ for(int i = 0; i < mNumGlobalEntities; ++i)
+ {
+ tRefCounted::Release(mpGlobalEntities[i]);
+ }
+ mNumGlobalEntities = 0;
+}
+
+
+//************************************************************************
+//
+// Private Member Functions : AllWrappers
+//
+//************************************************************************
+
+//========================================================================
+// AllWrappers::AllWrappers
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+AllWrappers::AllWrappers()
+{
+ for( int i=msNumWrappers-1; i>-1; i--)
+ {
+ mpLoaders[i] = NULL;
+ }
+ CoupleAllLoaders();
+
+ mpGlobalEntities = new tDrawable* [NUM_GLOBAL_ENTITIES];
+ rAssert(mpGlobalEntities);
+ mNumGlobalEntities = 0;
+}
+
+//========================================================================
+// AllWrappers::~AllWrappers()
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+AllWrappers::~AllWrappers()
+{
+ // this codeblock does not apply anymore because the inheritance tree
+ // was changed.. leaving it in just in case.
+
+ //for( int i=msNumWrappers-1; i>-1; i--)
+ //{
+ // if( mpLoaders[i] != NULL )
+ // {
+
+ // ( static_cast<tRefCounted*>(mpLoaders[i]) )->Release();
+ // mpLoaders[i] = NULL;
+ // }
+ //}
+ ClearGlobalEntities();
+ delete mpGlobalEntities;
+ mpGlobalEntities = 0;
+}
diff --git a/game/code/render/Loaders/AllWrappers.h b/game/code/render/Loaders/AllWrappers.h
new file mode 100644
index 0000000..9f662b3
--- /dev/null
+++ b/game/code/render/Loaders/AllWrappers.h
@@ -0,0 +1,102 @@
+#ifndef __AllWrappers_H__
+#define __AllWrappers_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: AllWrappers
+//
+// Description: The AllWrappers does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/04/24]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/IWrappedLoader.h>
+
+class tDrawable;
+
+//========================================================================
+//
+// Synopsis: The AllWrappers; Synopsis by Inspection.
+//
+//========================================================================
+class AllWrappers
+{
+public:
+ // Static Methods (for creating, destroying and acquiring an instance
+ // of the RenderManager)
+ static AllWrappers* CreateInstance();
+ static AllWrappers* GetInstance();
+ static void DestroyInstance();
+
+ enum
+ {
+ msGeometry,
+ msStaticEntity,
+ msStaticPhys,
+ msTreeDSG,
+ msFenceEntity,
+ msIntersectDSG,
+ msAnimCollEntity,
+ msAnimEntity,
+ msDynaPhys,
+ msInstStatEntity,
+ msInstStatPhys,
+ msLocator,
+ msWorldSphere,
+ msRoadSegment,
+ msPathSegment,
+ msBillboard,
+ msInstParticleSystem,
+ msBreakableObject,
+ msLensFlare,
+ msAnimDynaPhys,
+ msAnimDynaPhysWrapper,
+ msNumWrappers
+ };
+
+ IWrappedLoader& mLoader( int iIndex );
+ IWrappedLoader* mpLoader( int iIndex );
+
+ // Global entities are loaded in the global art file (i.e. L1_TERRA.p3d) and
+ //a single instance is created and held here. Whenever one is loaded in the
+ //zone/rail files we check here first to see if we already have it. If so we
+ //just use the instance chunk to place the object around the world and skip
+ //loading the rest of the data. The idea is that we can have a single copy
+ //of phonebooths, stop signs, lamp posts, etc instead of one in each on the
+ //zone/rail inventories.
+ void AddGlobalEntity(tDrawable* Entity);
+ tDrawable* GetGlobalEntity(tUID EntityID) const;
+ void ClearGlobalEntities(void);
+
+protected:
+ void CoupleAllLoaders();
+
+ IWrappedLoader* mpLoaders[msNumWrappers];
+ tDrawable** mpGlobalEntities;
+ int mNumGlobalEntities;
+
+private:
+ AllWrappers();
+ ~AllWrappers();
+
+ static AllWrappers* mspInstance;
+};
+
+//
+// A little syntactic sugar for getting at this singleton.
+//
+inline AllWrappers* GetAllWrappers()
+{
+ return( AllWrappers::GetInstance() );
+}
+
+#endif
diff --git a/game/code/render/Loaders/AnimCollLoader.cpp b/game/code/render/Loaders/AnimCollLoader.cpp
new file mode 100644
index 0000000..b21407d
--- /dev/null
+++ b/game/code/render/Loaders/AnimCollLoader.cpp
@@ -0,0 +1,278 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: AnimCollLoader.cpp
+//
+// Description: Implementation for AnimCollLoader class.
+//
+// History: Implemented --Devin [5/27/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/chunkfile.hpp>
+#include <p3d/anim/animate.hpp>
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/inventory.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <render/Loaders/BillboardWrappedLoader.h>
+//========================================
+// Project Includes
+//========================================
+#include <render/Loaders/AnimCollLoader.h>
+#include <render/DSG/AnimCollisionEntityDSG.h>
+
+#include <constants/srrchunks.h>
+#include <constants/chunkids.hpp>
+#include <constants/chunks.h>
+#include <memory/srrmemory.h>
+
+#include <render/Loaders/AllWrappers.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : AnimCollLoader Interface
+//
+//************************************************************************
+//========================================================================
+// AnimCollLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+AnimCollLoader::AnimCollLoader() :
+tSimpleChunkHandler(SRR2::ChunkID::ANIM_COLL_DSG)
+{
+MEMTRACK_PUSH_GROUP( "AnimCollLoader" );
+ mpCompDLoader = new(GMA_PERSISTENT) tCompositeDrawableLoader;
+ mpCompDLoader->AddRef();
+
+ mpFCLoader = new(GMA_PERSISTENT) tFrameControllerLoader;
+ mpFCLoader->AddRef();
+
+ mpCollObjLoader = new (GMA_PERSISTENT) sim::CollisionObjectLoader;
+ mpCollObjLoader->AddRef();
+
+ mpMultiControllerLoader = new (GMA_PERSISTENT) tMultiControllerLoader;
+ mpMultiControllerLoader->AddRef();
+ mpListenerCB = NULL;
+ mUserData = -1;
+MEMTRACK_POP_GROUP( "AnimCollLoader" );
+}
+//========================================================================
+// AnimCollLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+AnimCollLoader::~AnimCollLoader()
+{
+ mpCompDLoader->Release();
+ mpFCLoader->Release();
+ mpCollObjLoader->Release();
+ tRefCounted::Release( mpMultiControllerLoader );
+}
+///////////////////////////////////////////////////////////////////////
+// tSimpleChunkHandler
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// AnimCollLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* AnimCollLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ IEntityDSG::msDeletionsSafe=true;
+ char name[255];
+ f->GetPString(name);
+
+ int version = f->GetLong();
+ int HasAlpha = f->GetLong();
+
+ AnimCollisionEntityDSG *pAnimCollDSG = new AnimCollisionEntityDSG;
+ pAnimCollDSG->SetName(name);
+
+ if(HasAlpha)
+ {
+ pAnimCollDSG->mTranslucent = true;
+ }
+
+ tCompositeDrawable* pCompD = NULL;
+ tMultiController* pAnimFC = NULL;
+ sim::CollisionObject* pCollObject = NULL;
+ while(f->ChunksRemaining())
+ {
+ // Use this for tEntity's that have dependencies with other objects.
+ // ie, they need to be in the inventory.
+ //
+ tEntity* pStoreMe = 0;
+ f->BeginChunk();
+ switch(f->GetCurrentID())
+ {
+ case P3D_COMPOSITE_DRAWABLE:
+ {
+ pCompD = (tCompositeDrawable*)mpCompDLoader->LoadObject(f,store);
+ pStoreMe = pCompD;
+ break;
+ }
+ case Pure3D::BillboardObject::QUAD_GROUP:
+ {
+
+ BillboardWrappedLoader::OverrideLoader( true );
+ BillboardWrappedLoader* pBBQLoader = static_cast<BillboardWrappedLoader*>(AllWrappers::GetInstance()->mpLoader(AllWrappers::msBillboard));
+ tBillboardQuadGroup* pGroup = static_cast<tBillboardQuadGroup*>( pBBQLoader->LoadObject(f, store) );
+ rAssert( pGroup != NULL );
+ store->Store( pGroup );
+ BillboardWrappedLoader::OverrideLoader( false );
+ break;
+ }
+ case P3D_MULTI_CONTROLLER:
+ {
+ pAnimFC = (tMultiController*)mpMultiControllerLoader->LoadObject(f,store);
+ break;
+ }
+ case Pure3D::Animation::FrameControllerData::FRAME_CONTROLLER:
+ {
+ pStoreMe = mpFCLoader->LoadObject(f,store);
+ break;
+ }
+ case Simulation::Collision::OBJECT:
+ {
+ pCollObject = (sim::CollisionObject*)mpCollObjLoader->LoadObject(f, store );
+ break;
+ }
+ default:
+ break;
+ } // switch
+ // This tEntity needs to be found in the inventory for another object.
+ //
+ if ( pStoreMe != 0 )
+ {
+ if( store->TestCollision( pStoreMe->GetUID(), pStoreMe ) )
+ {
+ HandleCollision( pStoreMe );
+ }
+ else
+ {
+ store->Store( pStoreMe );
+ }
+ }
+ f->EndChunk();
+ } // while
+
+ pAnimCollDSG->LoadSetUp(pCompD,pAnimFC,pCollObject,store);
+
+ mpListenerCB->OnChunkLoaded( pAnimCollDSG, mUserData, _id );
+
+ IEntityDSG::msDeletionsSafe=false;
+ //
+ // Spin Pure3D async loading.
+ //
+ //p3d::loadManager->SwitchTask();
+ return pAnimCollDSG;
+}
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// AnimCollLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void AnimCollLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// AnimCollLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void AnimCollLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "AnimCollLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+//************************************************************************
+//
+// Protected Member Functions : AnimCollLoader
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : AnimCollLoader
+//
+//************************************************************************
diff --git a/game/code/render/Loaders/AnimCollLoader.h b/game/code/render/Loaders/AnimCollLoader.h
new file mode 100644
index 0000000..3224b7f
--- /dev/null
+++ b/game/code/render/Loaders/AnimCollLoader.h
@@ -0,0 +1,71 @@
+#ifndef __AnimCollLoader_H__
+#define __AnimCollLoader_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: AnimCollLoader
+//
+// Description: The AnimCollLoader does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/27]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/IWrappedLoader.h>
+namespace sim
+{
+ class CollisionObjectLoader;
+}
+class tFrameControllerLoader;
+class tCompositeDrawableLoader;
+class tMultiControllerLoader;
+//========================================================================
+//
+// Synopsis: The AnimCollLoader; Synopsis by Inspection.
+//
+//========================================================================
+class AnimCollLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ AnimCollLoader();
+ virtual ~AnimCollLoader();
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+protected:
+ tCompositeDrawableLoader* mpCompDLoader;
+ tFrameControllerLoader* mpFCLoader;
+ tMultiControllerLoader* mpMultiControllerLoader;
+ sim::CollisionObjectLoader* mpCollObjLoader;
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+private:
+};
+
+
+#endif
diff --git a/game/code/render/Loaders/AnimDSGLoader.cpp b/game/code/render/Loaders/AnimDSGLoader.cpp
new file mode 100644
index 0000000..17b02d4
--- /dev/null
+++ b/game/code/render/Loaders/AnimDSGLoader.cpp
@@ -0,0 +1,355 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Name of subsystem or component this class belongs to.
+//
+// Description: This file contains the implementation of...
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <render/Loaders/AnimDSGLoader.h>
+#include <render/DSG/animentitydsg.h>
+#include <p3d/chunkfile.hpp>
+#include <p3d/anim/animate.hpp>
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/inventory.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <render/Loaders/BillboardWrappedLoader.h>
+
+#include <constants/srrchunks.h>
+#include <constants/chunkids.hpp>
+#include <constants/chunks.h>
+#include <memory/srrmemory.h>
+
+#include <render/Loaders/AllWrappers.h>
+
+#include <roads/roadmanager.h>
+#include <roads/intersection.h>
+
+
+
+//========================================================================
+// AnimDSGLoader::AnimDSGLoader
+//========================================================================
+//
+// Description: AnimDSGLoader Constructor.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+AnimDSGLoader::AnimDSGLoader() :
+tSimpleChunkHandler(SRR2::ChunkID::ANIM_DSG)
+{
+ mpCompDLoader = new(GMA_PERSISTENT) tCompositeDrawableLoader;
+ mpCompDLoader->AddRef();
+
+ mpMCLoader = new(GMA_PERSISTENT) tMultiControllerLoader;
+ mpMCLoader->AddRef();
+
+ mpFCLoader = new(GMA_PERSISTENT) tFrameControllerLoader;
+ mpFCLoader->AddRef();
+
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+//========================================================================
+// AnimDSGLoader::~AnimDSGLoader
+//========================================================================
+//
+// Description: AnimDSGLoader dtor
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+AnimDSGLoader::~AnimDSGLoader()
+{
+ mpCompDLoader->Release();
+ mpMCLoader->Release();
+ mpFCLoader->Release();
+
+}
+///////////////////////////////////////////////////////////////////////
+// tSimpleChunkHandler
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// AnimCollLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* AnimDSGLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ IEntityDSG::msDeletionsSafe=true;
+ char name[255];
+ f->GetPString(name);
+
+ int version = f->GetLong();
+ int HasAlpha = f->GetLong();
+
+ tCompositeDrawable* pCompD = NULL;
+ tMultiController* pAnimMC = NULL;
+
+ while(f->ChunksRemaining())
+ {
+ f->BeginChunk();
+ switch(f->GetCurrentID())
+ {
+ case P3D_COMPOSITE_DRAWABLE:
+ {
+ pCompD = (tCompositeDrawable*)mpCompDLoader->LoadObject(f,store);
+ if( store->TestCollision( pCompD->GetUID(), pCompD ) )
+ {
+ HandleCollision( pCompD );
+ pCompD = NULL;
+ }
+ else
+ {
+ store->Store(pCompD);
+ }
+ rAssert( pCompD );
+ break;
+ }
+ case P3D_MULTI_CONTROLLER:
+ pAnimMC = static_cast<tMultiController*>(mpMCLoader->LoadObject( f, store ));
+ break;
+
+ case Pure3D::Animation::FrameControllerData::FRAME_CONTROLLER:
+ {
+ tFrameController* pFC = static_cast<tFrameController*>(mpFCLoader->LoadObject(f,store));
+ pFC->AddRef();
+ if(!store->TestCollision(pFC->GetUID(), pFC))
+ {
+ store->Store( pFC );
+ }
+ pFC->Release();
+ break;
+ }
+ case Pure3D::BillboardObject::QUAD_GROUP:
+ {
+
+ BillboardWrappedLoader::OverrideLoader( true );
+ BillboardWrappedLoader* pBBQLoader = static_cast<BillboardWrappedLoader*>(AllWrappers::GetInstance()->mpLoader(AllWrappers::msBillboard));
+ tBillboardQuadGroup* pGroup = static_cast<tBillboardQuadGroup*>( pBBQLoader->LoadObject(f, store) );
+ rAssert( pGroup != NULL );
+ store->Store( pGroup );
+ BillboardWrappedLoader::OverrideLoader( false );
+ break;
+ }
+
+ default:
+ rAssertMsg( 0, "Unknown chunk found in AnimDSG chunk" );
+ break;
+ } // switch
+ f->EndChunk();
+ } // while
+
+ //
+ // This is a Hack. I don't like it, but it is the simplest, and least likely
+ // to break (ie, "most robust") implementation I can come up with, since it
+ // needs to be in by tomorrow morning.
+ //
+ AnimEntityDSG *pAnimDSG;
+
+ tUID darrowUID = tName::MakeUID("darrow");
+ tUID warrowUID = tName::MakeUID("warrow");
+ tUID nameUID = tName::MakeUID(name);
+ int linkEnum = 0;
+ if( nameUID==darrowUID || nameUID==warrowUID )
+ {
+ if( nameUID==warrowUID )
+ linkEnum = 1;
+
+ bool bHasAlpha = (HasAlpha != 0);
+ pAnimDSG = LoadAnimAtIntersections(pCompD, pAnimMC, store, bHasAlpha, name, linkEnum);
+ }
+ else
+ {
+ pAnimDSG = new AnimEntityDSG;
+ pAnimDSG->SetName(name);
+
+ if(HasAlpha)
+ {
+ pAnimDSG->mTranslucent = true;
+ }
+
+ rmt::Vector tempPosn(0.0f,0.0f,0.0f);
+ pAnimDSG->LoadSetUp(pCompD, pAnimMC, store, tempPosn);
+
+ mpListenerCB->OnChunkLoaded( pAnimDSG, mUserData, _id );
+ }
+
+ IEntityDSG::msDeletionsSafe=false;
+ return pAnimDSG;
+}
+
+//========================================================================
+// animdsgloader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+AnimEntityDSG* AnimDSGLoader::LoadAnimAtIntersections
+(
+ tCompositeDrawable* ipCompD,
+ tMultiController* ipAnimMC,
+ tEntityStore* ipStore,
+ bool iHasAlpha,
+ char* ipName,
+ int iLinkEnum
+)
+{
+ rmt::Vector IntersectionPosn;
+ AnimEntityDSG *pAnimDSG = NULL;
+ RoadManager* pRoadManager = RoadManager::GetInstance();
+ for( int i = pRoadManager->GetNumIntersectionsUsed()-1; i>-1; i--)
+ {
+ pAnimDSG = new AnimEntityDSG;
+
+ pAnimDSG->SetName(ipName);
+
+ // Only add one instance to update list, because
+ // otherwise the animation get updated too many times
+ pAnimDSG->SetAddToUpdateList( i == 0 );
+
+ if(iHasAlpha)
+ {
+ pAnimDSG->mTranslucent = true;
+ }
+
+ pRoadManager->FindIntersection(i)->GetLocation(IntersectionPosn);
+
+ pAnimDSG->LoadSetUp(ipCompD, ipAnimMC, ipStore, IntersectionPosn );
+
+ pAnimDSG->SetVisibility(false);
+
+ pRoadManager->FindIntersection(i)->LinkAnimEntity(pAnimDSG, iLinkEnum);
+
+ mpListenerCB->OnChunkLoaded( pAnimDSG, mUserData, _id );
+ }
+
+ ///////////////////////////////////////////////////////////////////////////
+ // Special Case for the one arrow that is used to float arround, not bound
+ // to an intersection
+ //
+ // Now includes 5 floaters the longer road segments
+ //
+ ///////////////////////////////////////////////////////////////////////////
+ for(int i=11+iLinkEnum; i>0; i-=2)
+ {
+ pAnimDSG = new AnimEntityDSG;
+
+ pAnimDSG->SetName(ipName);
+
+ // Only add one instance to update list, because
+ // otherwise the animation get updated too many times
+ pAnimDSG->SetAddToUpdateList( false );
+
+ if(iHasAlpha)
+ {
+ pAnimDSG->mTranslucent = true;
+ }
+
+ pAnimDSG->LoadSetUp(ipCompD, ipAnimMC, ipStore, IntersectionPosn );
+ pAnimDSG->SetVisibility(false);
+ pAnimDSG->SetTrackSeparately(i); //index into the stored array as well as whether to track separately bool
+
+ mpListenerCB->OnChunkLoaded( pAnimDSG, mUserData, _id );
+ }
+ //////////////////////////////////////////////////////////////////////////
+ //////////////////////////////////////////////////////////////////////////
+ return pAnimDSG;
+}
+
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// AnimDSGLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void AnimDSGLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// AnimDSGLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void AnimDSGLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "AnimDSGLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
diff --git a/game/code/render/Loaders/AnimDSGLoader.h b/game/code/render/Loaders/AnimDSGLoader.h
new file mode 100644
index 0000000..075ed30
--- /dev/null
+++ b/game/code/render/Loaders/AnimDSGLoader.h
@@ -0,0 +1,89 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: AnimDSGLoader
+//
+// Description: Loads animated DSG objects that do NOT have collision data
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef ANIMDSGLOADER_H
+#define ANIMDSGLOADER_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <render/Loaders/IWrappedLoader.h>
+
+
+class tCompositeDrawableLoader;
+class tMultiControllerLoader;
+class tFrameControllerLoader;
+class AnimEntityDSG;
+class tEntityStore;
+class tMultiController;
+
+//===========================================================================
+//
+// Description:
+// Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+// Describe here what you require of the client which uses or
+// has this class - essentially, the converse of the description
+// above. A constraint is an expression of some semantic
+// condition that must be preserved, an invariant of a class or
+// relationship that must be preserved while the system is in a
+// steady state.
+//
+//===========================================================================
+class AnimDSGLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ AnimDSGLoader();
+ virtual ~AnimDSGLoader();
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+protected:
+ tCompositeDrawableLoader* mpCompDLoader;
+ tMultiControllerLoader* mpMCLoader;
+ tFrameControllerLoader* mpFCLoader;
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+
+ AnimEntityDSG* LoadAnimAtIntersections( tCompositeDrawable* ipCompD,
+ tMultiController* ipAnimMC,
+ tEntityStore* ipStore,
+ bool iHasAlpha,
+ char* ipName,
+ int iLinkEnum);
+
+private:
+};
+
+
+#endif
diff --git a/game/code/render/Loaders/AnimDynaPhysLoader.cpp b/game/code/render/Loaders/AnimDynaPhysLoader.cpp
new file mode 100644
index 0000000..7d81fb2
--- /dev/null
+++ b/game/code/render/Loaders/AnimDynaPhysLoader.cpp
@@ -0,0 +1,1074 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: AnimDynaPhysLoader
+//
+// Description: Loader for instanced, animated, dynaphys DSG objects
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+#include <constants/chunks.h>
+#include <constants/chunkids.hpp>
+#include <render/Loaders/AnimDynaPhysLoader.h>
+#include <render/DSG/InstAnimDynaPhysDSG.h>
+#include <atc/atcmanager.h>
+#include <constants/srrchunks.h>
+#include <memory/srrmemory.h>
+#include <simcollision/collisionobject.hpp>
+#include <simphysics/physicsobject.hpp>
+#include <render/Loaders/GeometryWrappedLoader.h>
+#include <p3d/anim/multicontroller.hpp>
+#include <render/Loaders/AllWrappers.h>
+#include <render/Loaders/BillboardWrappedLoader.h>
+#include <p3d/anim/skeleton.hpp>
+#include <p3d/anim/animate.hpp>
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/chunkfile.hpp>
+#include <p3d/inventory.hpp>
+#include <render/AnimEntityDSGManager/AnimEntityDSGManager.h>
+#include <render/DSG/StatePropDSG.h>
+#include <stateprop/statepropdata.hpp>
+#include <p3d/anim/animatedobject.hpp>
+#include <constants/physprop.h>
+#include <data/persistentworldmanager.h>
+#include <main/game.h>
+#include <radmath/random.hpp>
+#ifndef RAD_RELEASE
+#include <memory/propstats.h>
+#endif
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+Map< tUID, tUID > AnimDynaPhysLoader::s_ShadowList;
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// AnimDynaPhysLoader::AnimDynaPhysLoader
+//===========================================================================
+// Description:
+// AnimDynaPhysLoader ctor
+//
+// Constraints:
+//
+// Parameters:
+//
+//
+// Return:
+//
+//===========================================================================
+
+AnimDynaPhysLoader::AnimDynaPhysLoader() : tSimpleChunkHandler(SRR2::ChunkID::INSTA_ANIM_DYNA_PHYS_DSG)
+{
+
+ mpListenerCB = NULL;
+ mUserData = -1;
+
+ if ( s_ShadowList.capacity() == 0 )
+ {
+ s_ShadowList.reserve( 20 );
+ }
+}
+
+//===========================================================================
+// AnimDynaPhysLoader::~AnimDynaPhysLoader
+//===========================================================================
+// Description:
+// AnimDynaPhysLoader dtor
+//
+// Constraints:
+//
+// Parameters:
+//
+//
+// Return:
+//
+//===========================================================================
+
+AnimDynaPhysLoader::~AnimDynaPhysLoader()
+{
+
+}
+
+//===========================================================================
+// AnimDynaPhysLoader::LoadObject
+//===========================================================================
+// Description:
+// Loads an InstAnimDynaPhysDSG chunk and creates all the instances
+//
+// Constraints:
+// Expecting file format to be:
+// string name (max size 255)
+// long version
+// long hasalpha flag
+//
+//
+// Parameters:
+// Chunk file, and store
+//
+// Return:
+// NULL, no final chunk going specifically into the inventory via return
+//
+//===========================================================================
+
+tEntity* AnimDynaPhysLoader::LoadObject(tChunkFile* file, tEntityStore* store)
+{
+ IEntityDSG::msDeletionsSafe=true;
+ // Grab the name
+ char typeName[255];
+ file->GetPString(typeName);
+
+ // Quick test to see if we are dealing with an animated BV object
+ int simAnimJoint = -1;
+ if ( strstr ( typeName, "crate" ) != NULL )
+ simAnimJoint = 3;
+ else if ( strstr ( typeName, "vending" ) != NULL )
+ simAnimJoint = 1;
+ else if ( strstr ( typeName, "l2_monkiesgag" ) != NULL )
+ simAnimJoint = 1;
+ else if ( strstr ( typeName, "l7_spider" ) != NULL )
+ simAnimJoint = 1;
+
+
+#ifndef RAD_RELEASE
+ PropStats::StartTracking(typeName);
+#endif
+
+ int instanceCount = 0;
+
+ // Grab version info
+ int version = file->GetLong();
+ // Grab the flag that tell us whether or not the object has alpha or not
+ int hasAlpha = file->GetLong();
+
+ // Does this chunk contain a subchunk for instances
+ bool foundInstances = false;
+ bool persistance = true;
+
+ // Is this object a global entity (i.e. is it loaded once at level startup and
+ // all subsequent instances are cloned off this thing?)
+ StatePropDSG* globalStatepropEntity = static_cast<StatePropDSG*>(GetAllWrappers()->GetGlobalEntity(tName::MakeUID(typeName)));
+ if(strstr(typeName, "phonestop_Shape"))
+ {
+ persistance = false;
+ }
+ bool haveGlobal = globalStatepropEntity != 0;
+#ifndef RAD_RELEASE
+ if ( haveGlobal )
+ {
+ rAssert( dynamic_cast< StatePropDSG* >( globalStatepropEntity ) != NULL );
+ }
+#endif
+ CollisionAttributes* pCollAttr = NULL;
+ AnimDynaPhysWrapper* pWrappedObject = NULL;
+
+ // Is this a mission prop?
+
+ bool isMissionProp = IsMissionProp( typeName );
+
+
+ while( file->ChunksRemaining() )
+ {
+ file->BeginChunk();
+ switch(file->GetCurrentID())
+ {
+ case SRR2::ChunkID::ANIM_OBJ_DSG_WRAPPER:
+ // We are loading a stateprop
+ {
+ if(!haveGlobal)
+ {
+ HeapMgr()->PushHeap( GMA_TEMP );
+ AnimObjDSGWrapperLoader* pObjWrapperLoader = new AnimObjDSGWrapperLoader;
+ HeapMgr()->PopHeap( GMA_TEMP );
+ pWrappedObject = (AnimDynaPhysWrapper*) pObjWrapperLoader->LoadObject( file, store );
+ pWrappedObject->AddRef ();
+ pObjWrapperLoader->ReleaseVerified();
+ }
+ m_IsStateProp = true;
+ }
+ break;
+
+ case SRR2::ChunkID::ANIM_DSG_WRAPPER:
+ {
+ if(!haveGlobal)
+ {
+ // This is the wrapped object and contains a whole load of crap, meshes, comp drawables, billboards
+ // load them all
+ // Format of the wrapped object is
+ // string name
+ // ULONG version
+ // ULONG hasalpha
+ // pWrappedObject = new (GMA_TEMP ) WrappedObject;
+ // LoadAnimWrapper( file, store, pWrappedObject );
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+ AnimDynaPhysWrapperLoader* pWrapperLoader = new AnimDynaPhysWrapperLoader;
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ pWrappedObject = (AnimDynaPhysWrapper*) pWrapperLoader->LoadObject( file, store );
+ pWrappedObject->AddRef ();
+ pWrapperLoader->Release();
+ }
+ m_IsStateProp = false;
+ }
+ break;
+ // Relies on ObjectAttributes and the Wrapped DSG object being loaded first
+
+ case SRR2::ChunkID::OBJECT_ATTRIBUTES:
+ {
+ if(!haveGlobal)
+ {
+ rAssert( pWrappedObject != NULL );
+ // Relies on the Wrapped Object being loaded first
+ int classType = file->GetLong();
+ int physPropID = file->GetLong();
+ char tempsound [64];
+
+ file->GetString(tempsound); //Chuck: Reading the new sound properties since we dont use the sound returned from the ATC manager.
+
+ float volume;
+ if ( classType == PROP_MOVEABLE ||
+ classType == PROP_BREAKABLE ||
+ classType == PROP_ONETIME_MOVEABLE ||
+ classType == 8 )
+ {
+ volume = pWrappedObject->GetVolume();
+ }
+ else
+ {
+ volume = 0.0f;
+ }
+ pCollAttr = GetATCManager()->CreateCollisionAttributes(classType, physPropID, volume);
+ pCollAttr->AddRef ();
+ pCollAttr->SetSound(tempsound); //Chuck: Setting the CollAttr to use the correct sound from the OTC chunk.
+ }
+ }
+ break;
+
+ case SRR2::ChunkID::INSTANCES:
+ {
+ foundInstances = true;
+
+ //Instances >> Scenegraph
+ file->BeginChunk();
+ //Scenegraph >> ScenegraphRoot
+ file->BeginChunk();
+ //ScenegraphRoot >> ScenegraphBranch
+ file->BeginChunk();
+ //ScenegraphBranch >> ScenegraphTransform
+ file->BeginChunk();
+
+ //ScenegraphTransform >> real ScenegraphTransform
+ //f->BeginChunk();
+
+ while( file->ChunksRemaining() )
+ {
+ instanceCount++;
+
+
+ file->BeginChunk();
+ char name[256];
+ file->GetPString(name);
+ int numChild = file->GetLong();
+
+ rmt::Matrix matrix;
+ file->GetData( &matrix,16, tFile::DWORD );
+
+ if ( m_IsStateProp )
+ {
+ short persistentID = -1;
+ if(persistance && ((haveGlobal && globalStatepropEntity->GetCollisionAttributes()->GetClasstypeid()) || (!haveGlobal && ((pCollAttr->GetClasstypeid() == PROP_BREAKABLE) || (pCollAttr->GetClasstypeid() == PROP_MOVEABLE)))))
+ {
+ if ( isMissionProp == false )
+ persistentID = GetPersistentWorldManager()->GetPersistentObjectID( tEntity::MakeUID( file->GetFilename() ),tEntity::MakeUID(typeName) );
+ else
+ persistentID = -1;
+ }
+ if( persistentID >= -1 )
+ {
+ StatePropDSG* pStatePropDSG = NULL;
+
+ // Should we clone off the global entity (if it exists?)
+ if (haveGlobal)
+ {
+ pStatePropDSG = globalStatepropEntity->Clone( name, matrix );
+ }
+ else
+ {
+ rAssert( pCollAttr != NULL );
+ rAssert( pWrappedObject != NULL );
+ bool useSharedtPose = pWrappedObject->HasAnimation() ? false : true;
+ pStatePropDSG = new StatePropDSG();
+ pStatePropDSG->LoadSetup( pWrappedObject->GetStatePropData(), 0, matrix, pCollAttr, true, store, useSharedtPose, pWrappedObject->GetCollisionObject(), pWrappedObject->GetPhysicsObject() );
+ pStatePropDSG->SetName( name );
+ pStatePropDSG->SetSimJoint( simAnimJoint );
+ }
+
+
+
+ #ifndef FINAL
+ if( !haveGlobal && (pWrappedObject->GetStatePropData() == NULL))
+ {
+ char outbuffer [255];
+ sprintf(outbuffer,"Error: %s is missing a StateProp Chunk \n",pWrappedObject->GetName());
+ rTuneAssertMsg( 0,outbuffer );
+ }
+ #endif
+
+
+ mpListenerCB->OnChunkLoaded( pStatePropDSG, mUserData, _id );
+ pStatePropDSG->mPersistentObjectID = persistentID;
+
+ // Check to see if there is a shadow associated with this object
+ tUID compDrawUID = pStatePropDSG->GetDrawableUID();
+
+ tUID shadowElementName = GetShadowElement( compDrawUID );
+ if ( shadowElementName != static_cast< tUID >( 0 ) )
+ {
+ pStatePropDSG->SetShadowElement( shadowElementName );
+ }
+
+ // place it into the inventory so that locators can access it by name
+ bool collision = store->TestCollision( pStatePropDSG->GetUID(), pStatePropDSG );
+ if( !collision )
+ {
+ store->Store( pStatePropDSG );
+ }
+ else
+ {
+ HandleCollision( pStatePropDSG );
+ }
+ // Lets offset the animation so that every crate or vending machine
+ // instance isn't jumping together
+ // Make the stateprop instances each start at a different point in time
+ static rmt::Randomizer randomizer( Game::GetRandomSeed () );
+ const float RANDOM_TIME_MS = 10000.0f;
+ // Advance animations by 0 - 10 seconds
+ float randomUpdateTime = randomizer.Float() * RANDOM_TIME_MS;
+ pStatePropDSG->AdvanceAnimation( randomUpdateTime );
+
+ }
+ }
+ else
+ {
+ InstAnimDynaPhysDSG* pAnimDSG = new InstAnimDynaPhysDSG();
+ // Make sure that each name is unique
+ // things like the powerboxes are set via lookup
+ pAnimDSG->SetName( name );
+
+ if( hasAlpha || pWrappedObject->HasAlpha() )
+ {
+ pAnimDSG->mTranslucent = true;
+ }
+ else
+ {
+ pAnimDSG->mTranslucent = false;
+ }
+
+ rAssert( pCollAttr != NULL );
+ pAnimDSG->LoadSetUp( pCollAttr, matrix, pWrappedObject->GetDrawable(), pWrappedObject->GetController(), store );
+ mpListenerCB->OnChunkLoaded( static_cast< InstDynaPhysDSG*> (pAnimDSG), mUserData, _id );
+
+ // place it into the inventory so that locators can access it by name
+ store->Store( pAnimDSG );
+ }
+ file->EndChunk();
+
+#ifndef RAD_RELEASE
+ if ( instanceCount == 1 )
+ {
+ PropStats::StopTracking( typeName, 1 );
+ PropStats::StartTracking( typeName );
+ instanceCount = 0;
+ }
+#endif
+
+
+ }
+ //ScenegraphBranch >> ScenegraphTransform
+ file->EndChunk();
+ //ScenegraphRoot >> ScenegraphBranch
+ file->EndChunk();
+ //Scenegraph >> ScenegraphRoot
+ file->EndChunk();
+ //Instances >> Scenegraph
+ file->EndChunk();
+ }
+ break;
+
+
+ default:
+ rAssertMsg( false, "Unknown chunk in AnimatedDynaPhysDSG file");
+ break;
+ };
+ file->EndChunk();
+ }
+
+ if(0&&!foundInstances && !haveGlobal )
+ {
+ // No instance chunk!
+ // Therefore this MUST be a global entity
+ // Dump it into the global entity list
+ // so that subsequent instances of this type
+ // will get cloned off the global entity instead of loading all this crap again
+ //This must be a global entity.
+ StatePropDSG* globalEntityStateProp = new StatePropDSG();
+ rAssert(globalEntityStateProp);
+ // Give this thing the name of the type
+
+ bool useSharedtPose = pWrappedObject->HasAnimation() ? false : true;
+ rmt::Matrix identity;
+ identity.Identity();
+ globalEntityStateProp->LoadSetup( pWrappedObject->GetStatePropData(),
+ 0,
+ identity,
+ pCollAttr,
+ false,
+ NULL,
+ useSharedtPose,
+ pWrappedObject->GetCollisionObject(),
+ pWrappedObject->GetPhysicsObject() );
+ globalEntityStateProp->SetName( typeName );
+ GetAllWrappers()->AddGlobalEntity( globalEntityStateProp );
+ globalEntityStateProp->SetSimJoint( simAnimJoint );
+ instanceCount++;
+ }
+
+ if( pWrappedObject != NULL)
+ {
+ pWrappedObject->ReleaseVerified ();
+ }
+ if ( pCollAttr != NULL )
+ {
+ pCollAttr->Release ();
+ }
+
+#ifndef RAD_RELEASE
+ PropStats::StopTracking( typeName, instanceCount );
+#endif
+
+
+ IEntityDSG::msDeletionsSafe=false;
+ return NULL;
+}
+
+
+
+//===========================================================================
+// AnimDynaPhysLoader::SetRegdListener
+//===========================================================================
+// Description:
+// Informs the loader its listener is.
+//
+// Constraints:
+//
+// Parameters:
+// Pointer to the new listener. Integer for the data it sends to it OnChunkLoaded()
+//
+// Return:
+//
+//===========================================================================
+void AnimDynaPhysLoader::SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData )
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//===========================================================================
+// AnimDynaPhysLoader::ModRegdListener
+//===========================================================================
+// Description:
+// Changes the data that gets sent to the listener
+//
+// Constraints:
+// Listener must have been set already via SetRegdListener
+//
+// Parameters:
+// Pointer to the listener. Integer holding the new data
+//
+// Return:
+//
+//===========================================================================
+void AnimDynaPhysLoader::ModRegdListener( ChunkListenerCallback* pListenerCB, int iUserData )
+{
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+
+void AnimDynaPhysLoader::SetShadowElement( const char* compDrawName,
+ const char* drawableElementName )
+{
+ s_ShadowList.insert( tName::MakeUID( compDrawName ), tName::MakeUID( drawableElementName ) );
+}
+
+void AnimDynaPhysLoader::ClearShadowList()
+{
+ s_ShadowList.clear();
+}
+
+tUID AnimDynaPhysLoader::GetShadowElement( tUID compDrawName )
+{
+ tUID elementName;
+ Map< tUID, tUID >::const_iterator it;
+ it = s_ShadowList.find( compDrawName );
+ if ( it != s_ShadowList.end() )
+ elementName = it->second;
+ else
+ elementName = 0;
+
+ return elementName;
+}
+
+
+
+///////////////////////////////////////////////////////////
+// AnimDyaPhysWrapperLoader methods
+///////////////////////////////////////////////////////////
+
+AnimDynaPhysWrapperLoader::AnimDynaPhysWrapperLoader() : tSimpleChunkHandler(SRR2::ChunkID::ANIM_DSG_WRAPPER)
+{
+
+ mpListenerCB = NULL;
+ mUserData = -1;
+
+ mpCompDLoader = new tCompositeDrawableLoader;
+ mpCompDLoader->AddRef();
+
+ mpMCLoader = new tMultiControllerLoader;
+ mpMCLoader->AddRef();
+
+ mpSkelLoader = new tSkeletonLoader;
+ mpSkelLoader->AddRef();
+
+ mpAnimationLoader = new tAnimationLoader;
+ mpAnimationLoader->AddRef();
+
+ mpCollObjLoader = new sim::CollisionObjectLoader;
+ mpCollObjLoader->AddRef();
+
+ mpPhysObjLoader = new sim::PhysicsObjectLoader;
+ mpPhysObjLoader->AddRef();
+
+ mpFCLoader = new tFrameControllerLoader;
+ mpFCLoader->AddRef();
+}
+
+AnimDynaPhysWrapperLoader::~AnimDynaPhysWrapperLoader()
+{
+ mpCollObjLoader->ReleaseVerified();
+ mpPhysObjLoader->ReleaseVerified();
+ mpCompDLoader->ReleaseVerified();
+ mpMCLoader->ReleaseVerified();
+ mpSkelLoader->ReleaseVerified();
+ mpAnimationLoader->ReleaseVerified();
+ mpFCLoader->ReleaseVerified();
+}
+
+
+
+tEntity* AnimDynaPhysWrapperLoader::LoadObject( tChunkFile* file, tEntityStore* store )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ AnimDynaPhysWrapper* wrapper = new AnimDynaPhysWrapper;
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ rAssert( file != NULL );
+ rAssert( store != NULL );
+
+ // Grab the name
+ char buffer[256];
+ file->GetPString( buffer );
+
+ wrapper->SetName( buffer );
+
+ // Grab version info
+ int version = file->GetLong();
+ // Grab the flag that tell us whether or not the object has alpha or not
+ int hasAlpha = file->GetLong();
+
+ wrapper->mHasAlpha = ( hasAlpha != 0x00000000 );
+ bool collisionObjectFound = false;
+
+ while( file->ChunksRemaining() )
+ {
+ file->BeginChunk();
+ switch( file->GetCurrentID() )
+ {
+ case P3D_COMPOSITE_DRAWABLE:
+ {
+ // We will save the composite drawable since we need it
+ // for our DSG objects
+ rAssertMsg( wrapper->mCompDraw == NULL, "There must only be one composite drawable in the wrapper!" );
+ wrapper->mCompDraw = (tCompositeDrawable*)mpCompDLoader->LoadObject( file, store );
+ wrapper->mCompDraw->AddRef();
+ store->Store( wrapper->mCompDraw );
+ rAssert( wrapper->mCompDraw != NULL );
+ }
+ break;
+ case P3D_MULTI_CONTROLLER:
+ {
+ rAssert( wrapper->mMultiController == NULL );
+ wrapper->mMultiController = (tMultiController*)mpMCLoader->LoadObject( file, store );
+ rAssert( wrapper->mMultiController != NULL );
+ // This should be inserted into the list of world render multicontrollers that
+ // get advanced every frame
+ wrapper->mMultiController->SetCycleMode( FORCE_CYCLIC );
+ wrapper->mMultiController->AddRef();
+
+ bool collision = store->TestCollision( wrapper->mMultiController->GetUID(), wrapper->mMultiController );
+ if( !collision )
+ {
+ store->Store( wrapper->mMultiController );
+ }
+ else
+ {
+ HandleCollision( wrapper->mMultiController );
+ }
+ }
+ break;
+ case Pure3D::Animation::FrameControllerData::FRAME_CONTROLLER:
+ {
+ tFrameController* pFC = static_cast< tFrameController* >( mpFCLoader->LoadObject( file, store ) );
+ rAssert( pFC != NULL );
+ store->Store( pFC );
+ }
+ break;
+ case Simulation::Collision::OBJECT:
+ {
+ collisionObjectFound = true;
+ rAssert( wrapper->mCollisionObject == NULL );
+ wrapper->mCollisionObject = static_cast< sim::CollisionObject* > (mpCollObjLoader->LoadObject( file, store ));
+ rAssert( wrapper->mCollisionObject != NULL );
+ wrapper->mCollisionObject->AddRef();
+ store->Store( wrapper->mCollisionObject );
+ }
+ break;
+ case Pure3D::BillboardObject::QUAD_GROUP:
+ {
+ // Remember that we have our own billboard loader
+ // make sure that these don't get dumped into the DSG, override first
+
+ // Grab the loader
+ BillboardWrappedLoader::OverrideLoader( true );
+ BillboardWrappedLoader* pBBQLoader = static_cast<BillboardWrappedLoader*>(AllWrappers::GetInstance()->mpLoader(AllWrappers::msBillboard));
+
+ tBillboardQuadGroup* pGroup = static_cast<tBillboardQuadGroup*>( pBBQLoader->LoadObject( file, store ) );
+ rAssert( pGroup != NULL );
+ store->Store( pGroup );
+
+ // Set the loader back to its normal state
+ BillboardWrappedLoader::OverrideLoader( false );
+ }
+ break;
+ case Pure3D::Mesh::MESH:
+ {
+ GeometryWrappedLoader* pGeoLoader = (GeometryWrappedLoader*)AllWrappers::GetInstance()->mpLoader( AllWrappers::msGeometry );
+ tGeometry* pGeo = static_cast<tGeometry*>(pGeoLoader->LoadObject( file, store ) );
+ rAssert( pGeo != NULL );
+ store->Store( pGeo );
+ }
+ break;
+ case Pure3D::Animation::AnimationData::ANIMATION:
+ {
+ tAnimation* pAnimation = static_cast< tAnimation*> ( mpAnimationLoader->LoadObject( file, store ) );
+ rAssert( pAnimation != NULL );
+ store->Store( pAnimation );
+ wrapper->SetHasAnimation( true );
+ }
+ break;
+ case Simulation::Physics::OBJECT:
+
+ rAssert( wrapper->mPhysicsObject == NULL );
+ wrapper->mPhysicsObject = (sim::PhysicsObject*)mpPhysObjLoader->LoadObject( file,store );
+ rAssert( wrapper->mPhysicsObject != NULL );
+ store->Store( wrapper->mPhysicsObject );
+ wrapper->mPhysicsObject->AddRef ();
+
+ break;
+ case P3D_SKELETON:
+ {
+
+ tSkeleton* pSkeleton = static_cast< tSkeleton* > (mpSkelLoader->LoadObject( file, store ) );
+ rAssert( pSkeleton != NULL );
+ store->Store( pSkeleton );
+ }
+ break;
+ default:
+ rAssertMsg( false, "Unknown chunk in animated wrapper chunk file");
+ break;
+ };
+ file->EndChunk();
+ }
+ if ( collisionObjectFound == false )
+ {
+ rReleasePrintf("WARNING - Stateprop %s is missing a collision volume!\n", buffer );
+ }
+ return wrapper;
+}
+//===========================================================================
+// AnimDynaPhysWrapperLoader::SetRegdListener
+//===========================================================================
+// Description:
+// Informs the loader its listener is.
+//
+// Constraints:
+//
+// Parameters:
+// Pointer to the new listener. Integer for the data it sends to it OnChunkLoaded()
+//
+// Return:
+//
+//===========================================================================
+void AnimDynaPhysWrapperLoader::SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData )
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//===========================================================================
+// AnimDynaPhysWrapperLoader::ModRegdListener
+//===========================================================================
+// Description:
+// Changes the data that gets sent to the listener
+//
+// Constraints:
+// Listener must have been set already via SetRegdListener
+//
+// Parameters:
+// Pointer to the listener. Integer holding the new data
+//
+// Return:
+//
+//===========================================================================
+void AnimDynaPhysWrapperLoader::ModRegdListener( ChunkListenerCallback* pListenerCB, int iUserData )
+{
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+
+
+///////////////////////////////////////////////////////////
+// ANIM_OBJ_DSG_WRAPPER methods
+///////////////////////////////////////////////////////////
+
+AnimObjDSGWrapperLoader::AnimObjDSGWrapperLoader() : tSimpleChunkHandler(SRR2::ChunkID::ANIM_OBJ_DSG_WRAPPER)
+{
+
+ mpListenerCB = NULL;
+ mUserData = -1;
+
+ mpCompDLoader = new tCompositeDrawableLoader;
+ mpCompDLoader->AddRef();
+
+ mpMCLoader = new tMultiControllerLoader;
+ mpMCLoader->AddRef();
+
+ mpSkelLoader = new tSkeletonLoader;
+ mpSkelLoader->AddRef();
+
+ mpAnimationLoader = new tAnimationLoader;
+ mpAnimationLoader->AddRef();
+
+ mpCollObjLoader = new sim::CollisionObjectLoader;
+ mpCollObjLoader->AddRef();
+
+ mpPhysObjLoader = new sim::PhysicsObjectLoader;
+ mpPhysObjLoader->AddRef();
+
+ mpFCLoader = new tFrameControllerLoader;
+ mpFCLoader->AddRef();
+
+ mpStatePropLoader = new CStatePropDataLoader;
+ mpStatePropLoader->AddRef();
+
+ mpFactoryLoader = new tAnimatedObjectFactoryLoader;
+ mpAnimObjectLoader = new tAnimatedObjectLoader;
+
+}
+
+AnimObjDSGWrapperLoader::~AnimObjDSGWrapperLoader()
+{
+ mpCollObjLoader->ReleaseVerified();
+ mpPhysObjLoader->ReleaseVerified();
+ mpCompDLoader->ReleaseVerified();
+ mpMCLoader->ReleaseVerified();
+ mpSkelLoader->ReleaseVerified();
+ mpAnimationLoader->ReleaseVerified();
+ mpFCLoader->ReleaseVerified();
+ mpStatePropLoader->ReleaseVerified();
+ mpFactoryLoader->ReleaseVerified();
+ mpAnimObjectLoader->ReleaseVerified();
+}
+
+
+
+tEntity* AnimObjDSGWrapperLoader::LoadObject( tChunkFile* file, tEntityStore* store )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ AnimDynaPhysWrapper* wrapper = new AnimDynaPhysWrapper;
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ rAssert( file != NULL );
+ rAssert( store != NULL );
+
+ // Grab the name
+ char buffer[256];
+ file->GetPString( buffer );
+
+ wrapper->SetName( buffer );
+
+
+
+ // Grab version info
+ int version = file->GetUChar();
+ // Grab the flag that tell us whether or not the object has alpha or not
+ int hasAlpha = file->GetUChar();
+
+ wrapper->mHasAlpha = ( hasAlpha != 0x00000000 );
+
+ while( file->ChunksRemaining() )
+ {
+ file->BeginChunk();
+ unsigned int id = file->GetCurrentID();
+ switch( id )
+ {
+ case P3D_COMPOSITE_DRAWABLE:
+ {
+ // We will save the composite drawable since we need it
+ // for our DSG objects
+ rAssertMsg( wrapper->mCompDraw == NULL, "There must only be one composite drawable in the wrapper!" );
+ wrapper->mCompDraw = (tCompositeDrawable*)mpCompDLoader->LoadObject( file, store );
+ wrapper->mCompDraw->AddRef();
+ store->Store( wrapper->mCompDraw );
+ rAssert( wrapper->mCompDraw != NULL );
+ }
+ break;
+ case P3D_MULTI_CONTROLLER:
+ {
+ rAssert( wrapper->mMultiController == NULL );
+ wrapper->mMultiController = (tMultiController*)mpMCLoader->LoadObject( file, store );
+ rAssert( wrapper->mMultiController != NULL );
+ // This should be inserted into the list of world render multicontrollers that
+ // get advanced every frame
+ wrapper->mMultiController->SetCycleMode( FORCE_CYCLIC );
+ wrapper->mMultiController->AddRef();
+ bool collision = store->TestCollision( wrapper->mMultiController->GetUID(), wrapper->mMultiController );
+ if( !collision )
+ {
+ store->Store( wrapper->mMultiController );
+ }
+ else
+ {
+ HandleCollision( wrapper->mMultiController );
+ }
+ }
+ break;
+ case Pure3D::Animation::FrameControllerData::FRAME_CONTROLLER:
+ {
+ tFrameController* pFC = static_cast< tFrameController* >( mpFCLoader->LoadObject( file, store ) );
+ rAssert( pFC != NULL );
+ store->Store( pFC );
+ }
+ break;
+ case Simulation::Collision::OBJECT:
+ {
+
+ rAssert( wrapper->mCollisionObject == NULL );
+ wrapper->mCollisionObject = static_cast< sim::CollisionObject* > (mpCollObjLoader->LoadObject( file, store ));
+ rAssert( wrapper->mCollisionObject != NULL );
+ wrapper->mCollisionObject->AddRef();
+ store->Store( wrapper->mCollisionObject );
+ //wrapper->mCollisionObject->AddRef ();
+ }
+ break;
+ case Pure3D::BillboardObject::QUAD_GROUP:
+ {
+ // Remember that we have our own billboard loader
+ // make sure that these don't get dumped into the DSG, override first
+
+ // Grab the loader
+ BillboardWrappedLoader::OverrideLoader( true );
+ BillboardWrappedLoader* pBBQLoader = static_cast<BillboardWrappedLoader*>(AllWrappers::GetInstance()->mpLoader(AllWrappers::msBillboard));
+
+ tBillboardQuadGroup* pGroup = static_cast<tBillboardQuadGroup*>( pBBQLoader->LoadObject( file, store ) );
+ rAssert( pGroup != NULL );
+ store->Store( pGroup );
+
+ // Set the loader back to its normal state
+ BillboardWrappedLoader::OverrideLoader( false );
+ }
+ break;
+ case Pure3D::Mesh::MESH:
+ {
+ GeometryWrappedLoader* pGeoLoader = (GeometryWrappedLoader*)AllWrappers::GetInstance()->mpLoader( AllWrappers::msGeometry );
+ tGeometry* pGeo = static_cast<tGeometry*>(pGeoLoader->LoadObject( file, store ) );
+ rAssert( pGeo != NULL );
+ store->Store( pGeo );
+ }
+ break;
+ case Pure3D::Animation::AnimationData::ANIMATION:
+ {
+ tAnimation* pAnimation = static_cast< tAnimation*> ( mpAnimationLoader->LoadObject( file, store ) );
+ rAssert( pAnimation != NULL );
+ store->Store( pAnimation );
+ }
+ break;
+ case Simulation::Physics::OBJECT:
+
+ rAssert( wrapper->mPhysicsObject == NULL );
+ wrapper->mPhysicsObject = (sim::PhysicsObject*)mpPhysObjLoader->LoadObject( file,store );
+ rAssert( wrapper->mPhysicsObject != NULL );
+ store->Store( wrapper->mPhysicsObject );
+ wrapper->mPhysicsObject->AddRef ();
+
+ break;
+ case P3D_SKELETON:
+ {
+
+ tSkeleton* pSkeleton = static_cast< tSkeleton* > (mpSkelLoader->LoadObject( file, store ) );
+ rAssert( pSkeleton != NULL );
+ store->Store( pSkeleton );
+ }
+ break;
+ case StateProp::STATEPROP:
+ {
+ CStatePropData* pPropData = static_cast< CStatePropData* > (mpStatePropLoader->LoadObject( file, store ) );
+ rAssert( dynamic_cast< CStatePropData* >( pPropData ) != NULL );
+ rAssert( pPropData != NULL );
+ tRefCounted::Assign(wrapper->mStatePropData, pPropData);
+ store->Store( pPropData );
+ }
+ break;
+ case Pure3D::AnimatedObject::FACTORY:
+ {
+
+ tEntity* entity = mpFactoryLoader->LoadObject( file, store );
+ rAssert( dynamic_cast< tAnimatedObjectFactory* >(entity) != NULL );
+ store->Store( entity );
+ }
+ break;
+ case Pure3D::AnimatedObject::OBJECT:
+ {
+ tEntity* pEntity = mpAnimObjectLoader->LoadObject( file, store );
+ rAssert( pEntity != NULL );
+ store->Store( pEntity );
+ }
+ break;
+ default:
+ rAssertMsg( false, "Unknown chunk in animated wrapper chunk file");
+ break;
+ };
+ file->EndChunk();
+ }
+
+ return wrapper;
+}
+//===========================================================================
+// AnimObjDSGWrapperLoader::SetRegdListener
+//===========================================================================
+// Description:
+// Informs the loader its listener is.
+//
+// Constraints:
+//
+// Parameters:
+// Pointer to the new listener. Integer for the data it sends to it OnChunkLoaded()
+//
+// Return:
+//
+//===========================================================================
+void AnimObjDSGWrapperLoader::SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData )
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//===========================================================================
+// AnimObjDSGWrapperLoader::ModRegdListener
+//===========================================================================
+// Description:
+// Changes the data that gets sent to the listener
+//
+// Constraints:
+// Listener must have been set already via SetRegdListener
+//
+// Parameters:
+// Pointer to the listener. Integer holding the new data
+//
+// Return:
+//
+//===========================================================================
+void AnimObjDSGWrapperLoader::ModRegdListener( ChunkListenerCallback* pListenerCB, int iUserData )
+{
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+
+//===========================================================================
+// AnimObjDSGWrapperLoader::IsMissionProp
+//===========================================================================
+// Description:
+// Returns boolean indicating if the prop is used in a mission
+//
+// Constraints:
+// Only one stateprop mission prop - the powerbox
+//
+// Parameters:
+// none
+//
+// Return:
+// bool indicating if this prop is used in a mission
+//
+//===========================================================================
+bool AnimDynaPhysLoader::IsMissionProp( const char* name )const
+{
+ if ( strcmp( name, "l1z6_powerbox_Shape" ) == 0 )
+ return true;
+ else
+ return false;
+}
+
diff --git a/game/code/render/Loaders/AnimDynaPhysLoader.h b/game/code/render/Loaders/AnimDynaPhysLoader.h
new file mode 100644
index 0000000..898296c
--- /dev/null
+++ b/game/code/render/Loaders/AnimDynaPhysLoader.h
@@ -0,0 +1,200 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: AnimDynaPhysLoader
+//
+// Description: Loader for instanced, animated, dynaphys DSG objects
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef ANIMDYNAPHYSLOADER_H
+#define ANIMDYNAPHYSLOADER_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+#include <render/Loaders/IWrappedLoader.h>
+#include <memory/map.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class tCompositeDrawableLoader;
+class tMultiControllerLoader;
+class tSkeletonLoader;
+class tFrameControllerLoader;
+class tMultiController;
+class tAnimationLoader;
+class CStatePropDataLoader;
+class tAnimatedObjectFactoryLoader;
+class tAnimatedObjectLoader;
+namespace sim
+{
+ class CollisionObjectLoader;
+ class PhysicsObjectLoader;
+ class PhysicsObject;
+ class CollisionObject;
+};
+
+
+
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Loads an InstAnimDynaPhysDSG chunk, and builds N DSG objects from it.
+//
+// Constraints:
+//
+//
+//===========================================================================
+class AnimDynaPhysLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+ public:
+ AnimDynaPhysLoader();
+ virtual ~AnimDynaPhysLoader();
+
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ static void SetShadowElement( const char* compDrawName,
+ const char* compDrawElement );
+
+ static void ClearShadowList();
+ static tUID GetShadowElement( tUID );
+
+ protected:
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow AnimDynaPhysLoader from being copied and assigned.
+ AnimDynaPhysLoader( const AnimDynaPhysLoader& );
+ AnimDynaPhysLoader& operator=( const AnimDynaPhysLoader& );
+
+ bool m_IsStateProp;
+
+ bool IsMissionProp( const char* name )const;
+ static Map< tUID, tUID > s_ShadowList;
+
+
+ //void LoadAnimWrapper( tChunkFile* file, tEntityStore* store, AnimDynaPhysWrapper* opAnimDynaPhysWrapper );
+};
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Loads an InstAnimDynaPhysDSG WRAPPER chunk and returns it from LoadObject
+//
+// Constraints:
+// Puts all subchunks into the store, as well as itself. CreateSimStateArticulated
+// requires a store search to find physics and collision objects
+//
+//===========================================================================
+class AnimDynaPhysWrapperLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ AnimDynaPhysWrapperLoader();
+ virtual ~AnimDynaPhysWrapperLoader();
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ virtual tEntity* LoadObject( tChunkFile* file, tEntityStore* store );
+
+private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow AnimDynaPhysLoader from being copied and assigned.
+ AnimDynaPhysWrapperLoader( const AnimDynaPhysWrapperLoader& );
+ AnimDynaPhysWrapperLoader& operator=( const AnimDynaPhysWrapperLoader& );
+
+private:
+
+ sim::CollisionObjectLoader* mpCollObjLoader;
+ tCompositeDrawableLoader* mpCompDLoader;
+ tMultiControllerLoader* mpMCLoader;
+ tSkeletonLoader* mpSkelLoader;
+ sim::PhysicsObjectLoader* mpPhysObjLoader;
+ tFrameControllerLoader* mpFCLoader;
+ tAnimationLoader* mpAnimationLoader;
+
+
+};
+//===========================================================================
+//
+// Description:
+// Loads an ANIM_OBJ_DSG_WRAPPER chunk and returns it from LoadObject
+//
+// Constraints:
+// Puts all subchunks into the store, as well as itself. CreateSimStateArticulated
+// requires a store search to find physics and collision objects
+//
+//===========================================================================
+class AnimObjDSGWrapperLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ AnimObjDSGWrapperLoader();
+ virtual ~AnimObjDSGWrapperLoader();
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ virtual tEntity* LoadObject( tChunkFile* file, tEntityStore* store );
+
+private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow AnimDynaPhysLoader from being copied and assigned.
+ AnimObjDSGWrapperLoader( const AnimObjDSGWrapperLoader& );
+ AnimObjDSGWrapperLoader& operator=( const AnimObjDSGWrapperLoader& );
+
+private:
+
+ sim::CollisionObjectLoader* mpCollObjLoader;
+ tCompositeDrawableLoader* mpCompDLoader;
+ tMultiControllerLoader* mpMCLoader;
+ tSkeletonLoader* mpSkelLoader;
+ sim::PhysicsObjectLoader* mpPhysObjLoader;
+ tFrameControllerLoader* mpFCLoader;
+ tAnimationLoader* mpAnimationLoader;
+ CStatePropDataLoader* mpStatePropLoader;
+ tAnimatedObjectFactoryLoader* mpFactoryLoader;
+ tAnimatedObjectLoader* mpAnimObjectLoader;
+
+
+
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Loaders/BillboardWrappedLoader.cpp b/game/code/render/Loaders/BillboardWrappedLoader.cpp
new file mode 100644
index 0000000..e9686f7
--- /dev/null
+++ b/game/code/render/Loaders/BillboardWrappedLoader.cpp
@@ -0,0 +1,211 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: BillboardWrappedLoader.cpp
+//
+// Description: Implementation for RenderManager class.
+//
+// History: Implemented --Devin [4/22/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/inventory.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <raddebug.hpp>
+#include <render/Loaders/BillboardWrappedLoader.h>
+#include <render/DSG/StaticEntityDSG.h>
+#include <constants/srrchunks.h>
+#include <memory/srrmemory.h>
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+bool BillboardWrappedLoader::mOverrideWrapper = false;
+
+//************************************************************************
+//
+// Public Member Functions : BillboardWrappedLoader Interface
+//
+//************************************************************************
+
+//========================================================================
+// BillboardWrappedLoader::BillboardWrappedLoader
+//========================================================================
+//
+// Description: Init members to NULL
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+BillboardWrappedLoader::BillboardWrappedLoader()
+{
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+
+//========================================================================
+// BillboardWrappedLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void BillboardWrappedLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// BillboardWrappedLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void BillboardWrappedLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "BillboardWrappedLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+
+//************************************************************************
+//
+// Protected Member Functions : BillboardWrappedLoader
+//
+//************************************************************************
+
+//========================================================================
+// BillboardWrappedLoader::LoadObject
+//========================================================================
+//
+// Description: LoadObject, call the old LoadObject, wrapping the call
+// with a Listener Notification.
+//
+// Parameters: file: ChunkFile handle
+// store: where you put the damn thing
+//
+// Return: tEntity; the new thing you create on parse.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* BillboardWrappedLoader::LoadObject
+(
+ tChunkFile* file,
+ tEntityStore* store
+)
+{
+ IEntityDSG::msDeletionsSafe=true;
+ tBillboardQuadGroup* pBQGroup = (tBillboardQuadGroup*)tBillboardQuadGroupLoader::LoadObject( file, store );
+
+ rAssert(pBQGroup);
+
+ if ( mOverrideWrapper )
+ {
+ return pBQGroup;
+ }
+
+ // TBJ: Hack until Devin gets back.
+ // Just store the tBillboardQuadGroup in the inventory (ie, make it act like a regular
+ // loader). The problem is that we will also create a StaticEntityDSG (at the origin?)
+ // but that is not a huge cost, and it will work for now.
+ //
+ tEntity* t = pBQGroup;
+
+ if(!t)
+ return 0;
+
+ if( store->TestCollision( t->GetUID(), t ) )
+ {
+ HandleCollision(t);
+ }
+ else
+ {
+ store->Store(t);
+ }
+
+ // End hack.
+ //
+ StaticEntityDSG* pStaticEntity = new StaticEntityDSG;
+ pStaticEntity->SetName( pBQGroup->GetName() );
+ const char* name = pStaticEntity->GetName();
+
+ // Billboards anre't always translucent
+ // This can happen when they are using alpha test to carve out the opaque sections
+ // check the shader
+ tShader* shader = pBQGroup->GetShader();
+ if ( shader->mTranslucent )
+ {
+ pStaticEntity->mTranslucent = true;
+ }
+ else
+ {
+ pStaticEntity->mTranslucent = false;
+ }
+
+ pStaticEntity->SetDrawable(pBQGroup);
+
+ //
+ // _id is from SimpleChunkHandler; it is the chunk id;
+ // however, this should be treated like any other StaticEntity,
+ // so we change its return type _id
+ //
+ mpListenerCB->OnChunkLoaded( pStaticEntity, mUserData, SRR2::ChunkID::ENTITY_DSG );//_id );
+
+ IEntityDSG::msDeletionsSafe=false;
+ return pStaticEntity;
+}
+
+//************************************************************************
+//
+// Private Member Functions : BillboardWrappedLoader
+//
+//************************************************************************
diff --git a/game/code/render/Loaders/BillboardWrappedLoader.h b/game/code/render/Loaders/BillboardWrappedLoader.h
new file mode 100644
index 0000000..7039eec
--- /dev/null
+++ b/game/code/render/Loaders/BillboardWrappedLoader.h
@@ -0,0 +1,66 @@
+#ifndef __BillboardWrappedLoader_H__
+#define __BillboardWrappedLoader_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: BillboardWrappedLoader
+//
+// Description: The BillboardWrappedLoader does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/04/21]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/ChunkListenerCallback.h>
+#include <render/Loaders/IWrappedLoader.h>
+#include <p3d/billboardobject.hpp>
+
+//========================================================================
+//
+// Synopsis: The BillboardWrappedLoader; Synopsis by Inspection.
+//
+//========================================================================
+class BillboardWrappedLoader
+: public tBillboardQuadGroupLoader,
+ public IWrappedLoader
+{
+public:
+ BillboardWrappedLoader();
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // tBillboardLoader
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+ static void OverrideLoader( bool override ) { mOverrideWrapper = override; };
+
+protected:
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+private:
+
+ static bool mOverrideWrapper;
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Loaders/ChunkListenerCallback.h b/game/code/render/Loaders/ChunkListenerCallback.h
new file mode 100644
index 0000000..52da61e
--- /dev/null
+++ b/game/code/render/Loaders/ChunkListenerCallback.h
@@ -0,0 +1,44 @@
+#ifndef __ChunkListenerCallback_H__
+#define __ChunkListenerCallback_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ChunkListenerCallback
+//
+// Description: The ChunkListenerCallback does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/04/21]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <p3d/entity.hpp>
+
+//========================================================================
+//
+// Synopsis: The ChunkListenerCallback; Synopsis by Inspection.
+//
+//========================================================================
+
+class ChunkListenerCallback
+{
+public:
+ //
+ // Protocol: OnChunkLoaded is called as normal, EXCEPT when the
+ // Listener's Registration is being cancelled.
+ // In this case OnChunkLoaded( NULL, pNewUserData, ipCUID )
+ // is called.
+ //
+ virtual void OnChunkLoaded( tEntity* ipEntity,
+ int iUserData,
+ unsigned ipChunkID ) = 0;
+};
+
+#endif
diff --git a/game/code/render/Loaders/DynaPhysLoader.cpp b/game/code/render/Loaders/DynaPhysLoader.cpp
new file mode 100644
index 0000000..018d987
--- /dev/null
+++ b/game/code/render/Loaders/DynaPhysLoader.cpp
@@ -0,0 +1,438 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: DynaPhysLoader.cpp
+//
+// Description: Implementation for DynaPhysLoader class.
+//
+// History: Implemented --Devin [5/27/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <simcollision/collisionobject.hpp>
+#include <p3d/chunkfile.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/Loaders/DynaPhysLoader.h>
+#include <render/Loaders/GeometryWrappedLoader.h>
+#include <render/Loaders/AllWrappers.h>
+#include <render/DSG/InstDynaPhysDSG.h>
+#include <constants/srrchunks.h>
+#include <constants/chunkids.hpp>
+#include <constants/blobshadownames.h>
+#include <memory/srrmemory.h>
+
+#include <atc/atcmanager.h>
+
+#ifndef RAD_RELEASE
+#include <memory/propstats.h>
+#endif
+
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+
+//************************************************************************
+//
+// Public Member Functions : DynaPhysLoader Interface
+//
+//************************************************************************
+//========================================================================
+// DynaPhysLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+DynaPhysLoader::DynaPhysLoader() :
+tSimpleChunkHandler(SRR2::ChunkID::DYNA_PHYS_DSG)
+{
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ mpCollObjLoader = new sim::CollisionObjectLoader();
+ mpCollObjLoader->AddRef();
+
+ mpPhysObjLoader = new sim::PhysicsObjectLoader();
+ mpPhysObjLoader->AddRef();
+
+ mpCompDLoader = new tCompositeDrawableLoader;
+ mpCompDLoader->AddRef();
+
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+//========================================================================
+// DynaPhysLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+DynaPhysLoader::~DynaPhysLoader()
+{
+ mpCollObjLoader->Release();
+ mpPhysObjLoader->Release();
+ mpCompDLoader->Release();
+}
+
+///////////////////////////////////////////////////////////////////////
+// tSimpleChunkHandler
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// DynaPhysLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* DynaPhysLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ IEntityDSG::msDeletionsSafe=true;
+ char objName[128];
+ char name[128];
+ f->GetPString(objName);
+
+#ifndef RAD_RELEASE
+ PropStats::StartTracking( objName );
+#endif
+
+ bool test = tName::MakeUID(objName) == tName::MakeUID("l1_streetlamp_Shape");
+ static bool doTestOnce = true;
+
+ // Lets see if theis object has a shadow associated with it
+ const char* pShadowName = BlobbyShadowNames::FindShadowName( objName );
+ tDrawable* pShadow;
+ if ( pShadowName != NULL )
+ {
+ pShadow = p3d::find< tDrawable > ( pShadowName );
+ }
+ else
+ {
+ pShadow = NULL;
+ }
+
+ int instanceCount = 0;
+
+ int version = f->GetLong();
+ int HasAlpha = f->GetLong();
+ //pDynaPhysDSG->SetName(name);
+
+ sim::CollisionObject* pCollObj = NULL;
+ sim::PhysicsObject* pPhysObj = NULL;
+ tDrawable* pDrawable = NULL;
+ sim::SimState* pSimState = NULL;
+ CollisionAttributes* pCollAttr = NULL;
+ bool foundInstances = false;
+
+ InstDynaPhysDSG* pCurDynaPhysDSG = static_cast<InstDynaPhysDSG*>(GetAllWrappers()->GetGlobalEntity(tName::MakeUID(objName)));
+
+ //new InstDynaPhysDSG;
+
+ while(f->ChunksRemaining())
+ {
+ f->BeginChunk();
+ switch(f->GetCurrentID())
+ {
+ case SRR2::ChunkID::INSTANCES:
+ {
+ //Instances >> Scenegraph
+ f->BeginChunk();
+ //Scenegraph >> ScenegraphRoot
+ f->BeginChunk();
+ //ScenegraphRoot >> ScenegraphBranch
+ f->BeginChunk();
+ //ScenegraphBranch >> ScenegraphTransform
+ f->BeginChunk();
+
+ foundInstances = true;
+
+ //ScenegraphTransform >> real ScenegraphTransform
+ //f->BeginChunk();
+
+ for(;f->ChunksRemaining();)
+ {
+
+ instanceCount++;
+ f->BeginChunk();
+
+ f->GetPString(name);
+ int numChild = f->GetLong();
+
+ rmt::Matrix matrix;
+ f->GetData(&matrix,16,tFile::DWORD);
+
+ if(pCurDynaPhysDSG == 0)
+ {
+ pCurDynaPhysDSG = new InstDynaPhysDSG();
+ rAssert(pCurDynaPhysDSG);
+ pCurDynaPhysDSG->SetName(name);
+ pCurDynaPhysDSG->mTranslucent = ( HasAlpha != 0 ) || pShadow;
+ pSimState = sim::SimState::CreateSimState(pCollObj,pPhysObj);
+
+ // I suppose this could have just as easily gone into LoadSetUp
+ pSimState->SetControl(sim::simAICtrl);
+ pSimState->SetTransform(matrix);
+
+ pCurDynaPhysDSG->LoadSetUp(pSimState, pCollAttr, matrix, pDrawable, pShadow );
+
+ mpListenerCB->OnChunkLoaded( pCurDynaPhysDSG, mUserData, _id );
+ pCurDynaPhysDSG = 0;
+ }
+ else
+ {
+ InstDynaPhysDSG* clone = pCurDynaPhysDSG->Clone(name, matrix);
+ mpListenerCB->OnChunkLoaded(clone, mUserData, _id);
+ }
+ f->EndChunk();
+ }
+ //ScenegraphBranch >> ScenegraphTransform
+ f->EndChunk();
+ //ScenegraphRoot >> ScenegraphBranch
+ f->EndChunk();
+ //Scenegraph >> ScenegraphRoot
+ f->EndChunk();
+ //Instances >> Scenegraph
+ f->EndChunk();
+ }
+ break;
+
+ case Pure3D::Mesh::MESH:
+ {
+ if(pCurDynaPhysDSG == 0)
+ {
+ tGeometry* pGeo = (tGeometry*)((GeometryWrappedLoader*)GetAllWrappers()->mpLoader(AllWrappers::msGeometry))->LoadObject(f,store);
+ if( pGeo )
+ {
+ tRefCounted::Assign(pDrawable,(tDrawable*)pGeo);
+ }
+ }
+ }
+ break;
+
+ case P3D_COMPOSITE_DRAWABLE:
+ {
+ if(pCurDynaPhysDSG == 0)
+ {
+ tCompositeDrawable* pCompD = static_cast<tCompositeDrawable*>( mpCompDLoader->LoadObject( f, store ) );
+ if( store->TestCollision( pCompD->GetUID(), pCompD ) )
+ {
+ HandleCollision( pCompD );
+ pCompD = NULL;
+ }
+ else
+ {
+ store->Store( pCompD );
+ tRefCounted::Assign(pDrawable,(tDrawable*)pCompD);
+ }
+ }
+ }
+ break;
+
+ case Simulation::Physics::OBJECT:
+ {
+ if(pCurDynaPhysDSG == 0)
+ {
+ tRefCounted::Assign(pPhysObj,(sim::PhysicsObject*)mpPhysObjLoader->LoadObject(f,store));
+ }
+ }
+ break;
+
+ case Simulation::Collision::OBJECT:
+ {
+ if(pCurDynaPhysDSG == 0)
+ {
+ tRefCounted::Assign(pCollObj,(sim::CollisionObject*)mpCollObjLoader->LoadObject(f, store));
+ // TBJ [7/9/2002]
+ // Added this to store collision objects in the inventory.
+ // A normal loader stores the top level chunk, in this case the StaticPhysDSG.
+ // Since we want the Collision Object in the inventory, we have to store it here.
+ //
+ if ( pCollObj )
+ {
+ if( store->TestCollision( pCollObj->GetUID(), pCollObj ) )
+ {
+ HandleCollision(pCollObj);
+ // TBJ [7/9/2002]
+ // Don't know what to do with this code? Doesn't seem right to assign to NULL if
+ // the collObj does in fact exist.
+ // I'll take it out for now.
+ //
+ //pCollObj = NULL;
+ //return LOAD_ERROR;
+ }
+ else
+ {
+ store->Store(pCollObj);
+ }
+ }
+ }
+ }
+ break;
+
+ case SRR2::ChunkID::OBJECT_ATTRIBUTES:
+ {
+ if(pCurDynaPhysDSG == 0)
+ {
+ int classType = f->GetLong();
+ int physPropID = f->GetLong();
+ char tempsound [64];
+ f->GetString(tempsound);
+
+ // MS10 GREG TODO: This isn't even being used and calling it is leaking memory.
+ //
+
+ // we need to pass in volume for this thing to be able to set the mass properly in the PhysicsProperties
+ rAssert(pPhysObj);
+ float volume = pPhysObj->GetVolume();
+
+ tRefCounted::Assign(pCollAttr,GetATCManager()->CreateCollisionAttributes(classType, physPropID, volume));
+ pCollAttr->SetSound(tempsound);
+ }
+ }
+ break;
+
+ default:
+ break;
+ } // switch
+ f->EndChunk();
+ } // while
+
+ if( foundInstances == false || (test && doTestOnce) )
+ {
+ doTestOnce = false;
+ //This must be a global entity.
+ rmt::Matrix matrix;
+ matrix.Identity();
+ pCurDynaPhysDSG = new InstDynaPhysDSG();
+ rAssert(pCurDynaPhysDSG);
+ pCurDynaPhysDSG->SetName(objName);
+ pCurDynaPhysDSG->mTranslucent = HasAlpha != 0;
+ pSimState = sim::SimState::CreateSimState(pCollObj, pPhysObj);
+ pSimState->SetControl(sim::simAICtrl);
+ pSimState->SetTransform(matrix);
+ pCurDynaPhysDSG->LoadSetUp(pSimState, pCollAttr, matrix, pDrawable, pShadow );
+ GetAllWrappers()->AddGlobalEntity(pCurDynaPhysDSG);
+ instanceCount++;
+ }
+
+ //
+ // Spin Pure3D async loading.
+ //
+ ///p3d::loadManager->SwitchTask();
+ tRefCounted::Release(pCollObj);
+ tRefCounted::Release(pPhysObj);
+ tRefCounted::Release(pDrawable);
+ tRefCounted::Release(pCollAttr);
+
+#ifndef RAD_RELEASE
+ PropStats::StopTracking( objName, instanceCount );
+#endif
+
+
+ IEntityDSG::msDeletionsSafe=false;
+ return NULL;
+}
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// DynaPhysLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void DynaPhysLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// DynaPhysLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void DynaPhysLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "GeometryWrappedLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+//************************************************************************
+//
+// Protected Member Functions : DynaPhysLoader
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : DynaPhysLoader
+//
+//************************************************************************
diff --git a/game/code/render/Loaders/DynaPhysLoader.h b/game/code/render/Loaders/DynaPhysLoader.h
new file mode 100644
index 0000000..e33ba17
--- /dev/null
+++ b/game/code/render/Loaders/DynaPhysLoader.h
@@ -0,0 +1,64 @@
+#ifndef __DynaPhysLoader_H__
+#define __DynaPhysLoader_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: DynaPhysLoader
+//
+// Description: The DynaPhysLoader does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/28]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <simcollision/collisionobject.hpp>
+#include <simphysics/physicsobject.hpp>
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/IWrappedLoader.h>
+
+//========================================================================
+//
+// Synopsis: The DynaPhysLoader; Synopsis by Inspection.
+//
+//========================================================================
+class DynaPhysLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ DynaPhysLoader();
+ virtual ~DynaPhysLoader();
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+protected:
+ sim::CollisionObjectLoader* mpCollObjLoader;
+ sim::PhysicsObjectLoader* mpPhysObjLoader;
+ tCompositeDrawableLoader* mpCompDLoader;
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+private:
+};
+
+#endif
diff --git a/game/code/render/Loaders/FenceLoader.cpp b/game/code/render/Loaders/FenceLoader.cpp
new file mode 100644
index 0000000..5ce99a4
--- /dev/null
+++ b/game/code/render/Loaders/FenceLoader.cpp
@@ -0,0 +1,205 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: FenceLoader.cpp
+//
+// Description: Implementation for FenceLoader class.
+//
+// History: Implemented --Devin [5/27/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/chunkfile.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/Culling/BoxPts.h>
+#include <render/Culling/WorldScene.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/Loaders/FenceLoader.h>
+#include <render/DSG/FenceEntityDSG.h>
+#include <constants/srrchunks.h>
+#include <memory/srrmemory.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+unsigned int FenceLoader::mFenceCount = 0;
+
+//************************************************************************
+//
+// Public Member Functions : FenceLoader Interface
+//
+//************************************************************************
+//========================================================================
+// FenceLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+FenceLoader::FenceLoader() :
+tSimpleChunkHandler(SRR2::ChunkID::FENCE_DSG)
+{
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+///////////////////////////////////////////////////////////////////////
+// tSimpleChunkHandler
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// FenceLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* FenceLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+
+ IEntityDSG::msDeletionsSafe = true;
+ FenceEntityDSG* pFenceDSG = new FenceEntityDSG;
+
+ while(f->ChunksRemaining())
+ {
+ f->BeginChunk();
+ switch(f->GetCurrentID())
+ {
+ case SRR2::ChunkID::WALL:
+ {
+ f->GetData(&(pFenceDSG->mStartPoint), 3, tFile::DWORD);
+ f->GetData(&(pFenceDSG->mEndPoint), 3, tFile::DWORD);
+ f->GetData(&(pFenceDSG->mNormal), 3, tFile::DWORD);
+
+
+ BoxPts WorldBBox = GetRenderManager()->pWorldScene()->mStaticTreeWalker.rIthNode(0).mBBox;
+ pFenceDSG->mStartPoint.y = WorldBBox.mBounds.mMin.y;
+ pFenceDSG->mEndPoint.y = WorldBBox.mBounds.mMax.y;
+ // debuggin'
+ //if(pFenceDSG->mStartPoint
+
+ break;
+ }
+
+ default:
+ break;
+ } // switch
+ f->EndChunk();
+ } // while
+
+ mpListenerCB->OnChunkLoaded( pFenceDSG, mUserData, _id );
+
+ char name[64];
+ sprintf(name, "FenceDSG%d", mFenceCount );
+ ++mFenceCount;
+ pFenceDSG->SetName(name);
+
+ IEntityDSG::msDeletionsSafe=false;
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+
+
+ return pFenceDSG;
+}
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// FenceLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FenceLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// FenceLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FenceLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "GeometryWrappedLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+//************************************************************************
+//
+// Protected Member Functions : FenceLoader
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : FenceLoader
+//
+//************************************************************************
diff --git a/game/code/render/Loaders/FenceLoader.h b/game/code/render/Loaders/FenceLoader.h
new file mode 100644
index 0000000..8b249d8
--- /dev/null
+++ b/game/code/render/Loaders/FenceLoader.h
@@ -0,0 +1,61 @@
+#ifndef __FenceLoader_H__
+#define __FenceLoader_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: FenceLoader
+//
+// Description: The FenceLoader does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/28]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/IWrappedLoader.h>
+
+//========================================================================
+//
+// Synopsis: The FenceLoader; Synopsis by Inspection.
+//
+//========================================================================
+class FenceLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ FenceLoader();
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+protected:
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+private:
+
+ static unsigned int mFenceCount;
+};
+
+#endif
diff --git a/game/code/render/Loaders/GeometryWrappedLoader.cpp b/game/code/render/Loaders/GeometryWrappedLoader.cpp
new file mode 100644
index 0000000..3755f16
--- /dev/null
+++ b/game/code/render/Loaders/GeometryWrappedLoader.cpp
@@ -0,0 +1,158 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: GeometryWrappedLoader.cpp
+//
+// Description: Implementation for RenderManager class.
+//
+// History: Implemented --Devin [4/22/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <raddebug.hpp>
+#include <render/Loaders/GeometryWrappedLoader.h>
+#include <render/DSG/IEntityDSG.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : GeometryWrappedLoader Interface
+//
+//************************************************************************
+
+//========================================================================
+// GeometryWrappedLoader::GeometryWrappedLoader
+//========================================================================
+//
+// Description: Init members to NULL
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+GeometryWrappedLoader::GeometryWrappedLoader()
+{
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+
+//========================================================================
+// GeometryWrappedLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void GeometryWrappedLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// GeometryWrappedLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void GeometryWrappedLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "GeometryWrappedLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+
+//************************************************************************
+//
+// Protected Member Functions : GeometryWrappedLoader
+//
+//************************************************************************
+
+//========================================================================
+// GeometryWrappedLoader::LoadObject
+//========================================================================
+//
+// Description: LoadObject, call the old LoadObject, wrapping the call
+// with a Listener Notification.
+//
+// Parameters: file: ChunkFile handle
+// store: where you put the damn thing
+//
+// Return: tEntity; the new thing you create on parse.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* GeometryWrappedLoader::LoadObject
+(
+ tChunkFile* file,
+ tEntityStore* store
+)
+{
+ IEntityDSG::msDeletionsSafe=true;
+ tEntity* pEntity = tGeometryLoader::LoadObject( file, store );
+
+ //
+ // _id is from SimpleChunkHandler; it is the chunk id
+ //
+ mpListenerCB->OnChunkLoaded( pEntity, mUserData, _id );
+
+ IEntityDSG::msDeletionsSafe=false;
+ return pEntity;
+}
+
+//************************************************************************
+//
+// Private Member Functions : GeometryWrappedLoader
+//
+//************************************************************************
diff --git a/game/code/render/Loaders/GeometryWrappedLoader.h b/game/code/render/Loaders/GeometryWrappedLoader.h
new file mode 100644
index 0000000..745c1e7
--- /dev/null
+++ b/game/code/render/Loaders/GeometryWrappedLoader.h
@@ -0,0 +1,62 @@
+#ifndef __GeometryWrappedLoader_H__
+#define __GeometryWrappedLoader_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: GeometryWrappedLoader
+//
+// Description: The GeometryWrappedLoader does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/04/21]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/ChunkListenerCallback.h>
+#include <render/Loaders/IWrappedLoader.h>
+#include <p3d/geometry.hpp>
+
+//========================================================================
+//
+// Synopsis: The GeometryWrappedLoader; Synopsis by Inspection.
+//
+//========================================================================
+class GeometryWrappedLoader
+: public tGeometryLoader,
+ public IWrappedLoader
+{
+public:
+ GeometryWrappedLoader();
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+
+ ///////////////////////////////////////////////////////////////////////
+ // tGeometryLoader
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+protected:
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+private:
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Loaders/IWrappedLoader.h b/game/code/render/Loaders/IWrappedLoader.h
new file mode 100644
index 0000000..edf03c6
--- /dev/null
+++ b/game/code/render/Loaders/IWrappedLoader.h
@@ -0,0 +1,49 @@
+#ifndef __IWrappedLoader_H__
+#define __IWrappedLoader_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: IWrappedLoader
+//
+// Description: The IWrappedLoader does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/04/24]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/ChunkListenerCallback.h>
+#include <p3d/loadmanager.hpp>
+
+//========================================================================
+//
+// Synopsis: The IWrappedLoader; Synopsis by Inspection.
+//
+//========================================================================
+class IWrappedLoader //: public tSimpleChunkHandler
+{
+public:
+ IWrappedLoader(){}
+ virtual ~IWrappedLoader(){}
+
+ virtual void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData ) = 0;
+
+ virtual void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData ) = 0;
+
+
+protected:
+ ChunkListenerCallback* mpListenerCB;
+ int mUserData;
+
+};
+
+#endif
diff --git a/game/code/render/Loaders/InstStatEntityLoader.cpp b/game/code/render/Loaders/InstStatEntityLoader.cpp
new file mode 100644
index 0000000..6add08d
--- /dev/null
+++ b/game/code/render/Loaders/InstStatEntityLoader.cpp
@@ -0,0 +1,244 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: InstStatEntityLoader.cpp
+//
+// Description: Implementation for InstStatEntityLoader class.
+//
+// History: Implemented --Devin [5/27/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/chunkfile.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/Loaders/InstStatEntityLoader.h>
+#include <render/Loaders/GeometryWrappedLoader.h>
+#include <render/DSG/InstStatEntityDSG.h>
+
+#include <constants/srrchunks.h>
+#include <constants/chunkids.hpp>
+#include <constants/chunks.h>
+#include <memory/srrmemory.h>
+
+#include <render/Loaders/AllWrappers.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : InstStatEntityLoader Interface
+//
+//************************************************************************
+//========================================================================
+// InstStatEntityLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+InstStatEntityLoader::InstStatEntityLoader() :
+tSimpleChunkHandler(SRR2::ChunkID::INSTA_ENTITY_DSG)
+{
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+///////////////////////////////////////////////////////////////////////
+// tSimpleChunkHandler
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// InstStatEntityLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* InstStatEntityLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ IEntityDSG::msDeletionsSafe=true;
+ char name[255];
+ f->GetPString(name);
+
+ int version = f->GetLong();
+ int HasAlpha = f->GetLong();
+
+ InstStatEntityDSG* pCurStatEntity = NULL;
+ tGeometry* pGeo = NULL;
+ rmt::Matrix* pMatrix = NULL;
+
+ bool foundInstances = false;
+
+ while(f->ChunksRemaining())
+ {
+ f->BeginChunk();
+ switch(f->GetCurrentID())
+ {
+ case SRR2::ChunkID::INSTANCES:
+ {
+ //Instances >> Scenegraph
+ f->BeginChunk();
+ //Scenegraph >> ScenegraphRoot
+ f->BeginChunk();
+ //ScenegraphRoot >> ScenegraphBranch
+ f->BeginChunk();
+ //ScenegraphBranch >> ScenegraphTransform
+ f->BeginChunk();
+
+ //ScenegraphTransform >> real ScenegraphTransform
+ //f->BeginChunk();
+
+ for(;f->ChunksRemaining();)
+ {
+ foundInstances = true;
+
+ f->BeginChunk();
+
+ f->GetPString(name);
+ int numChild = f->GetLong();
+
+ pMatrix = new rmt::Matrix;
+ f->GetData(pMatrix,16,tFile::DWORD);
+
+ pCurStatEntity = new InstStatEntityDSG;
+ pCurStatEntity->SetName(name);
+
+ if(HasAlpha)
+ {
+ pCurStatEntity->mTranslucent = true;
+ }
+
+ f->EndChunk();
+
+ pCurStatEntity->LoadSetUp(pMatrix,pGeo);
+
+ mpListenerCB->OnChunkLoaded( pCurStatEntity, mUserData, _id );
+ }
+ //ScenegraphBranch >> ScenegraphTransform
+ f->EndChunk();
+ //ScenegraphRoot >> ScenegraphBranch
+ f->EndChunk();
+ //Scenegraph >> ScenegraphRoot
+ f->EndChunk();
+ //Instances >> Scenegraph
+ f->EndChunk();
+ }
+ break;
+
+ case Pure3D::Mesh::MESH:
+ {
+ GeometryWrappedLoader* pGeoLoader = (GeometryWrappedLoader*)AllWrappers::GetInstance()->mpLoader(AllWrappers::msGeometry) ;
+ tRefCounted::Assign(pGeo,(tGeometry*)pGeoLoader->LoadObject(f, store));
+ break;
+ }
+
+ default:
+ break;
+ } // switch
+ f->EndChunk();
+ } // while
+
+ tRefCounted::Release(pGeo);
+
+ if(!foundInstances)
+ {
+ rDebugPrintf("WARNING : no instances for inststatentity (%s)\n", name);
+ }
+
+ IEntityDSG::msDeletionsSafe=false;
+ return NULL;
+}
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// InstStatEntityLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstStatEntityLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// InstStatEntityLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstStatEntityLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "GeometryWrappedLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+//************************************************************************
+//
+// Protected Member Functions : InstStatEntityLoader
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : InstStatEntityLoader
+//
+//************************************************************************
diff --git a/game/code/render/Loaders/InstStatEntityLoader.h b/game/code/render/Loaders/InstStatEntityLoader.h
new file mode 100644
index 0000000..b418047
--- /dev/null
+++ b/game/code/render/Loaders/InstStatEntityLoader.h
@@ -0,0 +1,61 @@
+#ifndef __InstStatEntityLoader_H__
+#define __InstStatEntityLoader_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: InstStatEntityLoader
+//
+// Description: The InstStatEntityLoader does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/27]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/IWrappedLoader.h>
+
+//========================================================================
+//
+// Synopsis: The InstStatEntityLoader; Synopsis by Inspection.
+//
+//========================================================================
+class InstStatEntityLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ InstStatEntityLoader();
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+protected:
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+private:
+};
+
+
+#endif
diff --git a/game/code/render/Loaders/InstStatPhysLoader.cpp b/game/code/render/Loaders/InstStatPhysLoader.cpp
new file mode 100644
index 0000000..c5eb10e
--- /dev/null
+++ b/game/code/render/Loaders/InstStatPhysLoader.cpp
@@ -0,0 +1,391 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: InstStatPhysLoader.cpp
+//
+// Description: Implementation for InstStatPhysLoader class.
+//
+// History: Implemented --Devin [5/27/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <simcollision/collisionobject.hpp>
+#include <p3d/chunkfile.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/Loaders/InstStatPhysLoader.h>
+#include <render/Loaders/GeometryWrappedLoader.h>
+#include <render/Loaders/AllWrappers.h>
+#include <render/DSG/InstStatPhysDSG.h>
+#include <constants/srrchunks.h>
+#include <constants/chunkids.hpp>
+#include <memory/srrmemory.h>
+#include <atc/atcmanager.h>
+#include <constants/blobshadownames.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : InstStatPhysLoader Interface
+//
+//************************************************************************
+//========================================================================
+// InstStatPhysLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+InstStatPhysLoader::InstStatPhysLoader() :
+tSimpleChunkHandler(SRR2::ChunkID::INSTA_STATIC_PHYS_DSG)
+{
+ mpCollObjLoader = new(GMA_PERSISTENT) sim::CollisionObjectLoader();
+ mpCollObjLoader->AddRef();
+
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+//========================================================================
+// InstStatPhysLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+InstStatPhysLoader::~InstStatPhysLoader()
+{
+ mpCollObjLoader->Release();
+}
+
+///////////////////////////////////////////////////////////////////////
+// tSimpleChunkHandler
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// InstStatPhysLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* InstStatPhysLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ IEntityDSG::msDeletionsSafe=true;
+ char name[128];
+ char objName[128];
+ f->GetPString(objName);
+
+ int version = f->GetLong();
+ int HasAlpha = f->GetLong();
+
+ // Lets see if theis object has a shadow associated with it
+ const char* pShadowName = BlobbyShadowNames::FindShadowName( objName );
+ tDrawable* pShadow;
+ if ( pShadowName != NULL )
+ {
+ pShadow = p3d::find< tDrawable > ( pShadowName );
+ }
+ else
+ {
+ pShadow = NULL;
+ }
+
+ sim::CollisionObject* pCollObj = NULL;
+ tGeometry* pGeo = NULL;
+ sim::SimState* pSimState = NULL;
+
+ sim::PhysicsObject* pPhysObj = NULL;
+
+ InstStatPhysDSG* pCurStatPhysDSG = static_cast<InstStatPhysDSG*>(GetAllWrappers()->GetGlobalEntity(tName::MakeUID(objName)));
+
+ CollisionAttributes* pCollAttr = NULL;
+
+ //new InstDynaPhysDSG;
+
+ bool foundInstances = false;
+
+ while(f->ChunksRemaining())
+ {
+ f->BeginChunk();
+ switch(f->GetCurrentID())
+ {
+ case SRR2::ChunkID::INSTANCES:
+ {
+ foundInstances = true;
+ //Instances >> Scenegraph
+ f->BeginChunk();
+ //Scenegraph >> ScenegraphRoot
+ f->BeginChunk();
+ //ScenegraphRoot >> ScenegraphBranch
+ f->BeginChunk();
+ //ScenegraphBranch >> ScenegraphTransform
+ f->BeginChunk();
+
+ //ScenegraphTransform >> real ScenegraphTransform
+ //f->BeginChunk();
+
+ for(;f->ChunksRemaining();)
+ {
+ f->BeginChunk();
+
+ f->GetPString(name);
+ int numChild = f->GetLong();
+
+ rmt::Matrix matrix;
+ f->GetData(&matrix,16,tFile::DWORD);
+
+ if( pCurStatPhysDSG )
+ {
+ // We are dealing with a global entity.
+ InstStatPhysDSG* clone = pCurStatPhysDSG->Clone(name, matrix);
+ mpListenerCB->OnChunkLoaded( clone, mUserData, _id );
+ }
+ else
+ {
+ pCurStatPhysDSG = new InstStatPhysDSG();
+ rAssert(pCurStatPhysDSG);
+ pCurStatPhysDSG->SetName(name);
+
+ if(HasAlpha)
+ {
+ pCurStatPhysDSG->mTranslucent = true;
+ }
+ pSimState = sim::SimState::CreateStaticSimState(pCollObj);
+ pSimState->SetControl(sim::simAICtrl);
+ pSimState->GetCollisionObject()->SetIsStatic(false);
+ pSimState->GetCollisionObject()->SetManualUpdate(false);
+ pSimState->SetTransform(matrix);
+ pSimState->GetCollisionObject()->Update();
+ pSimState->GetCollisionObject()->SetIsStatic(true);
+ pSimState->GetCollisionObject()->SetManualUpdate(true);
+ pCurStatPhysDSG->LoadSetUp(pSimState,pCollAttr,matrix,pGeo);
+ if ( pShadow != NULL )
+ {
+ pCurStatPhysDSG->SetShadow( pShadow );
+ }
+ mpListenerCB->OnChunkLoaded( pCurStatPhysDSG, mUserData, _id );
+ pCurStatPhysDSG = 0;
+ }
+ f->EndChunk();
+ }
+ //ScenegraphBranch >> ScenegraphTransform
+ f->EndChunk();
+ //ScenegraphRoot >> ScenegraphBranch
+ f->EndChunk();
+ //Scenegraph >> ScenegraphRoot
+ f->EndChunk();
+ //Instances >> Scenegraph
+ f->EndChunk();
+ }
+ break;
+
+ case Pure3D::Mesh::MESH:
+ {
+ if(!pCurStatPhysDSG)
+ {
+ tRefCounted::Assign(pGeo,(tGeometry*)((GeometryWrappedLoader*)GetAllWrappers()->mpLoader(AllWrappers::msGeometry))->LoadObject(f,store));
+ }
+ }
+ break;
+
+
+ case SRR2::ChunkID::OBJECT_ATTRIBUTES:
+ {
+ if(!pCurStatPhysDSG)
+ {
+ int classType = f->GetLong();
+ int physPropID = f->GetLong();
+ char tempsound [64];
+
+ f->GetString(tempsound);
+
+ // Michael R. Volume set to zero. I only need the information
+ // from physPropID to break particles and breakables
+ // will Greg need this later?
+ tRefCounted::Assign(pCollAttr,GetATCManager()->CreateCollisionAttributes(classType, physPropID, 0.0f));
+ pCollAttr->SetSound(tempsound);
+ }
+ }
+ break;
+
+ case Simulation::Collision::OBJECT:
+ {
+ if(!pCurStatPhysDSG)
+ {
+ tRefCounted::Assign(pCollObj,(sim::CollisionObject*)mpCollObjLoader->LoadObject(f, store));
+ // TBJ [7/9/2002]
+ // Added this to store collision objects in the inventory.
+ // A normal loader stores the top level chunk, in this case the StaticPhysDSG.
+ // Since we want the Collision Object in the inventory, we have to store it here.
+ //
+ if ( pCollObj )
+ {
+ if( store->TestCollision( pCollObj->GetUID(), pCollObj ) )
+ {
+ HandleCollision( pCollObj );
+ // TBJ [7/9/2002]
+ // Don't know what to do with this code? Doesn't seem right to assign to NULL if
+ // the collObj does in fact exist.
+ // I'll take it out for now.
+ //
+ //pCollObj = NULL;
+ //return LOAD_ERROR;
+ }
+ else
+ {
+ store->Store(pCollObj);
+ }
+
+ // do this so that we can move the simstate around with ai
+ //pCollObj->SetManualUpdate(true);
+
+ }
+ //pSimState = sim::SimState::CreateSimState(pCollObj);
+ //pStaticPhysDSG->SetSimState(sim::SimState::CreateStaticSimState(pCollObj));
+ }
+ }
+ break;
+
+ default:
+ break;
+ } // switch
+ f->EndChunk();
+ } // while
+
+ if(!foundInstances)
+ {
+ rmt::Matrix matrix;
+ matrix.Identity();
+ // This is an instanced object without an instance chunks so we assume
+ //it's a global asset which we will hold only one copy of.
+ pCurStatPhysDSG = new InstStatPhysDSG();
+ rAssert(pCurStatPhysDSG);
+ pCurStatPhysDSG->SetName(objName);
+ pCurStatPhysDSG->mTranslucent = HasAlpha != 0;
+ pSimState = sim::SimState::CreateStaticSimState(pCollObj);
+ pCurStatPhysDSG->SetShadow(pShadow);
+ pSimState->SetControl(sim::simAICtrl);
+ pSimState->GetCollisionObject()->SetIsStatic(false);
+ pSimState->GetCollisionObject()->SetManualUpdate(false);
+ pSimState->SetTransform(matrix);
+ pSimState->GetCollisionObject()->Update();
+ pSimState->GetCollisionObject()->SetIsStatic(true);
+ pSimState->GetCollisionObject()->SetManualUpdate(true);
+ pCurStatPhysDSG->LoadSetUp(pSimState,pCollAttr,matrix,pGeo);
+ GetAllWrappers()->AddGlobalEntity(pCurStatPhysDSG);
+ }
+
+ //
+ // Spin Pure3D async loading.
+ //
+ //p3d::loadManager->SwitchTask();
+
+ tRefCounted::Release(pGeo);
+ tRefCounted::Release(pCollAttr);
+ tRefCounted::Release(pCollObj);
+
+ IEntityDSG::msDeletionsSafe=false;
+ return NULL;
+}
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// InstStatPhysLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstStatPhysLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// InstStatPhysLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void InstStatPhysLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "GeometryWrappedLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+//************************************************************************
+//
+// Protected Member Functions : InstStatPhysLoader
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : InstStatPhysLoader
+//
+//************************************************************************
diff --git a/game/code/render/Loaders/InstStatPhysLoader.h b/game/code/render/Loaders/InstStatPhysLoader.h
new file mode 100644
index 0000000..da2063b
--- /dev/null
+++ b/game/code/render/Loaders/InstStatPhysLoader.h
@@ -0,0 +1,63 @@
+#ifndef __InstStatPhysLoader_H__
+#define __InstStatPhysLoader_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: InstStatPhysLoader
+//
+// Description: The InstStatPhysLoader does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/28]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <simcollision/collisionobject.hpp>
+#include <simphysics/physicsobject.hpp>
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/IWrappedLoader.h>
+
+//========================================================================
+//
+// Synopsis: The InstStatPhysLoader; Synopsis by Inspection.
+//
+//========================================================================
+class InstStatPhysLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ InstStatPhysLoader();
+ virtual ~InstStatPhysLoader();
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+protected:
+ sim::CollisionObjectLoader* mpCollObjLoader;
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+private:
+};
+
+#endif
diff --git a/game/code/render/Loaders/IntersectLoader.cpp b/game/code/render/Loaders/IntersectLoader.cpp
new file mode 100644
index 0000000..979c453
--- /dev/null
+++ b/game/code/render/Loaders/IntersectLoader.cpp
@@ -0,0 +1,286 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: IntersectLoader.cpp
+//
+// Description: Implementation for IntersectLoader class.
+//
+// History: Implemented --Devin [7/19/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/chunkfile.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/Loaders/IntersectLoader.h>
+#include <render/DSG/IntersectDSG.h>
+#include <constants/srrchunks.h>
+#include <constants/chunkids.hpp>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : IntersectLoader Interface
+//
+//************************************************************************
+//========================================================================
+// IntersectLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+IntersectLoader::IntersectLoader() :
+tSimpleChunkHandler(SRR2::ChunkID::INTERSECT_DSG)
+{
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+
+///////////////////////////////////////////////////////////////////////
+// tSimpleChunkHandler
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// IntersectLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+// ULONG NumIndices;
+// ULONG Indices
+// array = NumIndices;
+// ULONG NumPositions;
+// tlPoint Positions
+// array = NumPositions;
+// ULONG NumNormals;
+// tlPoint Normals
+// array = NumNormals;
+// ULONG NumCentroids;
+// tlPoint Centroids
+// array = NumCentroids;
+// ULONG NumRadii;
+// float Radii
+// array = NumRadii;
+// Chunk tlBBoxChunk;
+// Chunk tlBSphereChunk;
+//
+//========================================================================
+tEntity* IntersectLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+/* static int profileNumPts = 0;
+ static int profileNumNorms = 0;
+ static int profileNumTerrain = 0;
+ static int profileNumIndicies = 0;
+ static int profileTotalSize = 0;
+*/
+
+ IEntityDSG::msDeletionsSafe=true;
+ int i;
+ IntersectDSG* pIDSG = new IntersectDSG;
+
+ pIDSG->mTriIndices.Allocate(f->GetLong());
+ pIDSG->mTriIndices.AddUse(pIDSG->mTriIndices.mSize);
+
+ for(i=0; i<pIDSG->mTriIndices.mSize; i++)
+ {
+ pIDSG->mTriIndices[i] = (int)(f->GetLong());
+ }
+
+ pIDSG->mTriPts.Allocate(f->GetLong());
+ pIDSG->mTriPts.AddUse(pIDSG->mTriPts.mSize);
+
+ //for(i=0; i<pIDSG->mTriPts.mSize; i++)
+ {
+ f->GetData(&pIDSG->mTriPts[0], 3*pIDSG->mTriPts.mSize, tFile::DWORD);
+ }
+
+ pIDSG->mTriNorms.Allocate(f->GetLong());
+ pIDSG->mTriNorms.AddUse(pIDSG->mTriNorms.mSize);
+
+ //for(i=0; i<pIDSG->mTriNorms.mSize; i++)
+ {
+ f->GetData(&pIDSG->mTriNorms[0], 3*pIDSG->mTriNorms.mSize, tFile::DWORD);
+ }
+/*
+ pIDSG->mTriCentroids.Allocate(f->GetLong());
+ pIDSG->mTriCentroids.AddUse(pIDSG->mTriCentroids.mSize);
+
+ //for(i=0; i<pIDSG->mTriCentroids.mSize; i++)
+ {
+ f->GetData(&pIDSG->mTriCentroids[0], 3*pIDSG->mTriCentroids.mSize, tFile::DWORD);
+ }
+
+ pIDSG->mTriRadius.Allocate(f->GetLong());
+ pIDSG->mTriRadius.AddUse(pIDSG->mTriRadius.mSize);
+
+ //for(i=0; i<pIDSG->mTriRadius.mSize; i++)
+ {
+ f->GetData(&pIDSG->mTriRadius[0], pIDSG->mTriRadius.mSize, tFile::DWORD);
+ }
+*/
+ for(i=0 ;
+ f->ChunksRemaining();
+ i++)
+ {
+ f->BeginChunk();
+ switch(f->GetCurrentID())
+ {
+ case Pure3D::Mesh::BOX:
+ {
+ float minx = f->GetFloat();
+ float miny = f->GetFloat();
+ float minz = f->GetFloat();
+ float maxx = f->GetFloat();
+ float maxy = f->GetFloat();
+ float maxz = f->GetFloat();
+
+ pIDSG->SetBoundingBox( minx, miny, minz, maxx, maxy, maxz);
+ break;
+ }
+
+ case Pure3D::Mesh::SPHERE:
+ {
+ float cx = f->GetFloat();
+ float cy = f->GetFloat();
+ float cz = f->GetFloat();
+ float r = f->GetFloat();
+
+ pIDSG->SetBoundingSphere(cx,cy,cz,r);
+ break;
+ }
+
+ case SRR2::ChunkID::TERRAIN_TYPE:
+ {
+ long version = f->GetLong();
+ rAssert( version == 0 );
+ long size = f->GetLong();
+ pIDSG->mTerrainType.Allocate( size );
+ pIDSG->mTerrainType.AddUse( size );
+ //for( i = 0; i < size; ++i )
+ //{
+ f->GetData(&pIDSG->mTerrainType[0], pIDSG->mTerrainType.mSize, tFile::BYTE);
+ //f->GetData( &pIDSG->mTerrainType[ i ], 1, tFile::BYTE );
+ //}
+ break;
+ }
+
+ default:
+ rAssert(false);
+ break;
+ } // switch
+ f->EndChunk();
+ }
+/*
+ if(pIDSG->mTriPts.mSize > profileNumPts) profileNumPts = pIDSG->mTriPts.mSize;
+ if(pIDSG->mTriNorms.mSize > profileNumNorms) profileNumNorms = pIDSG->mTriNorms.mSize;
+ if(pIDSG->mTerrainType.mSize > profileNumTerrain) profileNumTerrain = pIDSG->mTerrainType.mSize;
+ if(pIDSG->mTriIndices.mSize > profileNumIndicies) profileNumIndicies = pIDSG->mTriIndices.mSize;
+
+ if( 3*4*(pIDSG->mTriPts.mSize + pIDSG->mTriNorms.mSize) + pIDSG->mTriIndices.mSize*4 + pIDSG->mTerrainType.mSize > profileTotalSize )
+ {
+ profileTotalSize = 3*4*(pIDSG->mTriPts.mSize + pIDSG->mTriNorms.mSize) + pIDSG->mTriIndices.mSize*4 + pIDSG->mTerrainType.mSize;
+ if(profileTotalSize > 8192)
+ profileTotalSize = 8192;
+ }
+*/
+ mpListenerCB->OnChunkLoaded( pIDSG, mUserData, _id );
+ IEntityDSG::msDeletionsSafe=false;
+ return NULL;//pIDSG;
+}
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// IntersectLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// IntersectLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void IntersectLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "IntersectLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+//************************************************************************
+//
+// Protected Member Functions : IntersectLoader
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : IntersectLoader
+//
+//************************************************************************
diff --git a/game/code/render/Loaders/IntersectLoader.h b/game/code/render/Loaders/IntersectLoader.h
new file mode 100644
index 0000000..bf69ec0
--- /dev/null
+++ b/game/code/render/Loaders/IntersectLoader.h
@@ -0,0 +1,59 @@
+#ifndef __IntersectLoader_H__
+#define __IntersectLoader_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: IntersectLoader
+//
+// Description: The IntersectLoader does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/07/19]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/IWrappedLoader.h>
+
+//========================================================================
+//
+// Synopsis: The IntersectLoader; Synopsis by Inspection.
+//
+//========================================================================
+class IntersectLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ IntersectLoader();
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+private:
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+};
+
+#endif
+
diff --git a/game/code/render/Loaders/LensFlareLoader.cpp b/game/code/render/Loaders/LensFlareLoader.cpp
new file mode 100644
index 0000000..7b79935
--- /dev/null
+++ b/game/code/render/Loaders/LensFlareLoader.cpp
@@ -0,0 +1,291 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: LensFlareLoader.cpp
+//
+// Description: Implementation for LensFlareLoader class.
+//
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <constants/chunks.h>
+#include <constants/chunkids.hpp>
+#include <p3d/chunkfile.hpp>
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/billboardobject.hpp>
+#include <p3d/inventory.hpp>
+//========================================
+// Project Includes
+//========================================
+#include <render/Loaders/LensFlareLoader.h>
+#include <render/Loaders/GeometryWrappedLoader.h>
+#include <render/DSG/LensFlareDSG.h>
+
+
+#include <constants/srrchunks.h>
+#include <memory/srrmemory.h>
+
+#include <render/Loaders/AllWrappers.h>
+#include <render/Loaders/BillboardWrappedLoader.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+const char* VIS_TEST_NAME = "visibility_test";
+
+//************************************************************************
+//
+// Public Member Functions : LensFlareLoader Interface
+//
+//************************************************************************
+//========================================================================
+// LensFlareLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+LensFlareLoader::LensFlareLoader() :
+tSimpleChunkHandler(SRR2::ChunkID::LENS_FLARE_DSG)
+{
+
+ mpCompDLoader = new(GMA_PERSISTENT) tCompositeDrawableLoader;
+ mpCompDLoader->AddRef();
+
+ mpBillBoardQuadLoader = new (GMA_PERSISTENT) tBillboardQuadGroupLoader;
+ mpBillBoardQuadLoader->AddRef();
+
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+//========================================================================
+// LensFlareLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+LensFlareLoader::~LensFlareLoader()
+{
+ mpCompDLoader->Release();
+ mpBillBoardQuadLoader->Release();
+
+}
+///////////////////////////////////////////////////////////////////////
+// tSimpleChunkHandler
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// LensFlareLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* LensFlareLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ IEntityDSG::msDeletionsSafe=true;
+ char name[255];
+ f->GetPString(name);
+
+ int version = f->GetLong();
+
+ LensFlareDSG *pLensFlareDSG = new LensFlareDSG;
+ pLensFlareDSG->SetNumBillBoardQuadGroups( f->GetLong() );
+
+ // Composite drawable, hold onto a pointer so that we can
+ // search through it after loading and pick out the bbqs
+ tCompositeDrawable* pCompDraw = NULL;
+
+ while(f->ChunksRemaining())
+ {
+ f->BeginChunk();
+ int id = f->GetCurrentID();
+ switch(f->GetCurrentID())
+ {
+
+ case P3D_COMPOSITE_DRAWABLE:
+ {
+ pCompDraw = static_cast<tCompositeDrawable*>( mpCompDLoader->LoadObject( f, store ) );
+ pLensFlareDSG->SetCompositeDrawable( pCompDraw );
+ store->Store( pCompDraw );
+ }
+ break;
+ case Pure3D::Mesh::MESH:
+ {
+ GeometryWrappedLoader* pGeoLoader = (GeometryWrappedLoader*)AllWrappers::GetInstance()->mpLoader(AllWrappers::msGeometry);
+ tGeometry* pGeo = static_cast<tGeometry*>(pGeoLoader->LoadObject( f, store) );
+ store->Store( pGeo );
+ }
+ break;
+
+ case Pure3D::BillboardObject::QUAD_GROUP:
+ {
+
+ BillboardWrappedLoader::OverrideLoader( true );
+ BillboardWrappedLoader* pBBQLoader = static_cast<BillboardWrappedLoader*>(AllWrappers::GetInstance()->mpLoader(AllWrappers::msBillboard));
+
+ tBillboardQuadGroup* pGroup = static_cast<tBillboardQuadGroup*>( pBBQLoader->LoadObject(f, store) );
+ rAssert( pGroup != NULL );
+ pLensFlareDSG->AddBillBoardQuadGroup( pGroup );
+ store->Store( pGroup );
+ BillboardWrappedLoader::OverrideLoader( false );
+ break;
+ }
+
+ default:
+ break;
+ } // switch
+ f->EndChunk();
+ } // while
+
+
+ SetOcclusionFlags( pCompDraw );
+
+
+ if ( mpListenerCB != NULL )
+ {
+ // mpListenerCB->OnChunkLoaded( pLensFlareDSG, mUserData, _id );
+ }
+ IEntityDSG::msDeletionsSafe=false;
+ return pLensFlareDSG;
+}
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// LensFlareLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void LensFlareLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// LensFlareLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void LensFlareLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "LensFlareLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+//************************************************************************
+//
+// Protected Member Functions : LensFlareLoader
+//
+//************************************************************************
+
+
+void LensFlareLoader::SetOcclusionFlags( tCompositeDrawable* compDraw )
+{
+ rAssert( compDraw != NULL );
+ // Iterate through the drawable elements, looking for billboard quad groups
+ // to set the occlusion flags on
+ int currentBBQ = 0;
+ float highestPriority = -FLT_MAX;
+ int highestPriorityElement = -1;
+ for ( int i = 0 ; i < compDraw->GetNumDrawableElement() ; i++ )
+ {
+ tCompositeDrawable::DrawableElement* element = compDraw->GetDrawableElement(i);
+ if( element )
+ {
+ tBillboardQuadGroup* bbq = dynamic_cast< tBillboardQuadGroup* >( element->GetDrawable() );
+ if ( bbq )
+ {
+ // Set to 1, occlusion test enabled on this one
+ bbq->SetOcclusion( 1 );
+ if ( element->SortOrder() > highestPriority )
+ {
+ highestPriorityElement = i;
+ highestPriority = element->SortOrder();
+ currentBBQ++;
+ }
+ }
+ }
+ }
+ // Find the one that will be drawn first ( highest sort order ) and set its occlusion to 2
+ if ( highestPriorityElement != -1 )
+ {
+ tCompositeDrawable::DrawableElement* element = compDraw->GetDrawableElement(highestPriorityElement);
+ tBillboardQuadGroup* bbq = dynamic_cast< tBillboardQuadGroup* >( element->GetDrawable() );
+ rAssert( bbq != NULL );
+ if ( bbq )
+ {
+ bbq->SetOcclusion( 2 );
+ }
+ }
+}
+
+
+//************************************************************************
+//
+// Private Member Functions : LensFlareLoader
+//
+//************************************************************************
diff --git a/game/code/render/Loaders/LensFlareLoader.h b/game/code/render/Loaders/LensFlareLoader.h
new file mode 100644
index 0000000..2e08a03
--- /dev/null
+++ b/game/code/render/Loaders/LensFlareLoader.h
@@ -0,0 +1,68 @@
+#ifndef LENSFLARELOADER_H
+#define LENSFLARELOADER_H
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: LensFlareLoader
+//
+// Description: Loads a lensflaredsg chunk
+//
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/IWrappedLoader.h>
+
+
+class tCompositeDrawableLoader;
+class tBillboardQuadGroupLoader;
+
+
+class LensFlareLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ LensFlareLoader();
+ virtual ~LensFlareLoader();
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+protected:
+ tCompositeDrawableLoader* mpCompDLoader;
+ tBillboardQuadGroupLoader* mpBillBoardQuadLoader;
+
+ // Iterates through the compdrawable, setting the first billboardquadgroup's
+ // occlusion flag to 2
+ // all subsequent ones to 1
+ void SetOcclusionFlags( tCompositeDrawable* compDraw );
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+private:
+};
+
+
+#endif
diff --git a/game/code/render/Loaders/StaticEntityLoader.cpp b/game/code/render/Loaders/StaticEntityLoader.cpp
new file mode 100644
index 0000000..6254182
--- /dev/null
+++ b/game/code/render/Loaders/StaticEntityLoader.cpp
@@ -0,0 +1,191 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: StaticEntityLoader.cpp
+//
+// Description: Implementation for StaticEntityLoader class.
+//
+// History: Implemented --Devin [5/27/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/chunkfile.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/Loaders/StaticEntityLoader.h>
+#include <render/Loaders/GeometryWrappedLoader.h>
+#include <render/DSG/StaticEntityDSG.h>
+
+#include <constants/srrchunks.h>
+#include <constants/chunkids.hpp>
+#include <memory/srrmemory.h>
+
+#include <render/Loaders/AllWrappers.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : StaticEntityLoader Interface
+//
+//************************************************************************
+//========================================================================
+// StaticEntityLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+StaticEntityLoader::StaticEntityLoader() :
+tSimpleChunkHandler(SRR2::ChunkID::ENTITY_DSG)
+{
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+///////////////////////////////////////////////////////////////////////
+// tSimpleChunkHandler
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// StaticEntityLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* StaticEntityLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ IEntityDSG::msDeletionsSafe=true;
+ char name[255];
+ f->GetPString(name);
+
+ int version = f->GetLong();
+ int HasAlpha = f->GetLong();
+
+ StaticEntityDSG *pStaticEntityDSG = new StaticEntityDSG;
+ pStaticEntityDSG->SetName(name);
+
+ if(HasAlpha)
+ {
+ pStaticEntityDSG->mTranslucent = true;
+ }
+
+ while(f->ChunksRemaining())
+ {
+ f->BeginChunk();
+ switch(f->GetCurrentID())
+ {
+ case Pure3D::Mesh::MESH:
+ {
+ GeometryWrappedLoader* pGeoLoader = (GeometryWrappedLoader*)AllWrappers::GetInstance()->mpLoader(AllWrappers::msGeometry) ;
+ rAssert(pGeoLoader!=NULL);
+ tGeometry* pGeo = (tGeometry*)pGeoLoader->LoadObject(f, store);
+ rAssert(pGeo!=NULL);
+ pStaticEntityDSG->SetGeometry(pGeo);
+ }
+
+ default:
+ break;
+ } // switch
+ f->EndChunk();
+ } // while
+
+ mpListenerCB->OnChunkLoaded( pStaticEntityDSG, mUserData, _id );
+ IEntityDSG::msDeletionsSafe=false;
+ return pStaticEntityDSG;
+}
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// StaticEntityLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticEntityLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// StaticEntityLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticEntityLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "GeometryWrappedLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+//************************************************************************
+//
+// Protected Member Functions : StaticEntityLoader
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : StaticEntityLoader
+//
+//************************************************************************
diff --git a/game/code/render/Loaders/StaticEntityLoader.h b/game/code/render/Loaders/StaticEntityLoader.h
new file mode 100644
index 0000000..95a1d2b
--- /dev/null
+++ b/game/code/render/Loaders/StaticEntityLoader.h
@@ -0,0 +1,61 @@
+#ifndef __StaticEntityLoader_H__
+#define __StaticEntityLoader_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: StaticEntityLoader
+//
+// Description: The StaticEntityLoader does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/27]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/IWrappedLoader.h>
+
+//========================================================================
+//
+// Synopsis: The StaticEntityLoader; Synopsis by Inspection.
+//
+//========================================================================
+class StaticEntityLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ StaticEntityLoader();
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+protected:
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+private:
+};
+
+
+#endif
diff --git a/game/code/render/Loaders/StaticPhysLoader.cpp b/game/code/render/Loaders/StaticPhysLoader.cpp
new file mode 100644
index 0000000..a7dd770
--- /dev/null
+++ b/game/code/render/Loaders/StaticPhysLoader.cpp
@@ -0,0 +1,256 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: StaticPhysLoader.cpp
+//
+// Description: Implementation for StaticPhysLoader class.
+//
+// History: Implemented --Devin [5/27/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <simcollision/collisionobject.hpp>
+#include <p3d/chunkfile.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/Loaders/StaticPhysLoader.h>
+#include <render/DSG/StaticPhysDSG.h>
+#include <constants/srrchunks.h>
+#include <constants/chunkids.hpp>
+#include <memory/srrmemory.h>
+#include <atc/atcmanager.h>
+#include <constants/blobshadownames.h>
+
+#include <radtime.hpp>
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : StaticPhysLoader Interface
+//
+//************************************************************************
+//========================================================================
+// StaticPhysLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+StaticPhysLoader::StaticPhysLoader() :
+tSimpleChunkHandler(SRR2::ChunkID::STATIC_PHYS_DSG)
+{
+ mpCollObjLoader = new(GMA_PERSISTENT) sim::CollisionObjectLoader();
+ mpCollObjLoader->AddRef();
+
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+//========================================================================
+// StaticPhysLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+StaticPhysLoader::~StaticPhysLoader()
+{
+ mpCollObjLoader->Release();
+}
+
+///////////////////////////////////////////////////////////////////////
+// tSimpleChunkHandler
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// StaticPhysLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* StaticPhysLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ IEntityDSG::msDeletionsSafe=true;
+ char name[255];
+ f->GetPString(name);
+ // Lets see if theis object has a shadow associated with it
+ const char* pShadowName = BlobbyShadowNames::FindShadowName( name );
+ tDrawable* pShadow;
+ if ( pShadowName != NULL )
+ {
+ pShadow = p3d::find< tDrawable > ( pShadowName );
+ }
+ else
+ {
+ pShadow = NULL;
+ }
+
+ int version = f->GetLong();
+
+ StaticPhysDSG* pStaticPhysDSG = new StaticPhysDSG;
+ pStaticPhysDSG->SetName(name);
+ CollisionAttributes* pCollAttr = NULL;
+
+ while(f->ChunksRemaining())
+ {
+ f->BeginChunk();
+ switch(f->GetCurrentID())
+ {
+ case Simulation::Collision::OBJECT:
+ {
+ sim::CollisionObject* pCollObj = (sim::CollisionObject*)mpCollObjLoader->LoadObject(f, store);
+ // TBJ [7/9/2002]
+ // Added this to store collision objects in the inventory.
+ // A normal loader stores the top level chunk, in this case the StaticPhysDSG.
+ // Since we want the Collision Object in the inventory, we have to store it here.
+ //
+ if ( pCollObj )
+ {
+ if( store && store->TestCollision( pCollObj->GetUID(), pCollObj ) )
+ {
+ HandleCollision(pCollObj);
+ }
+ else
+ {
+ store->Store(pCollObj);
+ }
+ }
+ pStaticPhysDSG->SetSimState(sim::SimState::CreateStaticSimState(pCollObj));
+
+ pStaticPhysDSG->SetShadow( pShadow );
+
+ pStaticPhysDSG->GetSimState()->GetCollisionObject()->GetCollisionVolume()->GenerateHierarchy();
+
+ break;
+ }
+ case SRR2::ChunkID::OBJECT_ATTRIBUTES:
+ {
+
+ int classType = f->GetLong();
+ int physPropID = f->GetLong();
+ char tempsound [64];
+ f->GetString(tempsound);
+
+ pCollAttr = GetATCManager()->CreateCollisionAttributes(classType, physPropID, 0.0f);
+ pCollAttr->SetSound(tempsound);
+ pStaticPhysDSG->SetCollisionAttributes( pCollAttr );
+ }
+ break;
+
+ break;
+ default:
+ break;
+ } // switch
+ f->EndChunk();
+ } // while
+
+
+ mpListenerCB->OnChunkLoaded( pStaticPhysDSG, mUserData, _id );
+ //
+ // Spin Pure3D async loading.
+ //
+ //p3d::loadManager->SwitchTask();
+ IEntityDSG::msDeletionsSafe=false;
+ return pStaticPhysDSG;
+}
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// StaticPhysLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticPhysLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// StaticPhysLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void StaticPhysLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "GeometryWrappedLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+//************************************************************************
+//
+// Protected Member Functions : StaticPhysLoader
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : StaticPhysLoader
+//
+//************************************************************************
diff --git a/game/code/render/Loaders/StaticPhysLoader.h b/game/code/render/Loaders/StaticPhysLoader.h
new file mode 100644
index 0000000..b080a31
--- /dev/null
+++ b/game/code/render/Loaders/StaticPhysLoader.h
@@ -0,0 +1,62 @@
+#ifndef __StaticPhysLoader_H__
+#define __StaticPhysLoader_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: StaticPhysLoader
+//
+// Description: The StaticPhysLoader does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/28]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+#include <simcollision/collisionobject.hpp>
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/IWrappedLoader.h>
+
+//========================================================================
+//
+// Synopsis: The StaticPhysLoader; Synopsis by Inspection.
+//
+//========================================================================
+class StaticPhysLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ StaticPhysLoader();
+ virtual ~StaticPhysLoader();
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+protected:
+ sim::CollisionObjectLoader* mpCollObjLoader;
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+private:
+};
+
+#endif
diff --git a/game/code/render/Loaders/TreeDSGLoader.cpp b/game/code/render/Loaders/TreeDSGLoader.cpp
new file mode 100644
index 0000000..d1bbf65
--- /dev/null
+++ b/game/code/render/Loaders/TreeDSGLoader.cpp
@@ -0,0 +1,219 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: TreeDSGLoader.cpp
+//
+// Description: Implementation for TreeDSGLoader class.
+//
+// History: Implemented --Devin [6/10/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/chunkfile.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/Culling/Bounds.h>
+#include <render/Culling/SpatialTree.h>
+#include <render/Loaders/TreeDSGLoader.h>
+#include <render/DSG/StaticEntityDSG.h>
+#include <render/DSG/StaticPhysDSG.h>
+#include <render/DSG/IntersectDSG.h>
+#include <constants/srrchunks.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : TreeDSGLoader Interface
+//
+//************************************************************************
+//========================================================================
+// TreeDSGLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+TreeDSGLoader::TreeDSGLoader() :
+tSimpleChunkHandler(SRR2::ChunkID::TREE_DSG)
+{
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+///////////////////////////////////////////////////////////////////////
+// tSimpleChunkHandler
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// TreeDSGLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* TreeDSGLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ IEntityDSG::msDeletionsSafe=true;
+ int nNodes = f->GetLong();
+
+ Bounds3f bounds;
+
+ f->GetData(&bounds.mMin, 3, tFile::DWORD);
+ f->GetData(&bounds.mMax, 3, tFile::DWORD);
+
+ SpatialTree* pSpatialTree = new SpatialTree;
+
+ pSpatialTree->SetTo(nNodes,bounds);
+
+ ContiguousBinNode< SpatialNode >* pCurNode=pSpatialTree->GetRoot();
+ for(int i=0 ;
+ f->ChunksRemaining();
+ i++, pCurNode++)
+ {
+ rAssert(i<nNodes);
+
+ f->BeginChunk();
+ switch(f->GetCurrentID())
+ {
+ case SRR2::ChunkID::CONTIGUOUS_BIN_NODE:
+ {
+ pCurNode->SetSubTreeSize(f->GetLong());
+ pCurNode->LinkParent(f->GetLong());
+ f->BeginChunk();
+ //////////////////////////////////////////////////////////////////////////
+ // WET PAINT Do not Touch!! Talk to Devin first.
+ // Violation leads to the Tree of Woe
+ //////////////////////////////////////////////////////////////////////////
+ pCurNode->mData.mSubDivPlane.mAxis = f->GetChar();
+ pCurNode->mData.mSubDivPlane.mPosn = f->GetFloat();
+ pCurNode->mData.mSEntityElems.mUseSize = f->GetLong();
+ pCurNode->mData.mSPhysElems.mUseSize = f->GetLong();
+ pCurNode->mData.mIntersectElems.mUseSize = f->GetLong();
+ pCurNode->mData.mDPhysElems.mUseSize = f->GetLong();
+ pCurNode->mData.mFenceElems.mUseSize = f->GetLong();
+ pCurNode->mData.mRoadSegmentElems.mUseSize = f->GetLong();
+ pCurNode->mData.mPathSegmentElems.mUseSize = f->GetLong();
+ pCurNode->mData.mAnimElems.mUseSize = f->GetLong()+1;
+ pCurNode->mData.mAnimCollElems.mUseSize = 1;
+ f->EndChunk();
+
+ if(pCurNode->IsRoot())
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // WET PAINT Do not Touch!! Talk to Devin first.
+ // Violation leads to the Tree of Woe
+ //////////////////////////////////////////////////////////////////////////
+ pCurNode->mData.mSEntityElems.mUseSize += 100;
+ pCurNode->mData.mDPhysElems.mUseSize += 10;
+ pCurNode->mData.mAnimCollElems.mUseSize += 50;
+ pCurNode->mData.mAnimElems.mUseSize += 60;
+ }
+
+ break;
+ }
+
+ default:
+ rAssert(false);
+ break;
+ } // switch
+ f->EndChunk();
+ }
+
+ mpListenerCB->OnChunkLoaded( pSpatialTree, mUserData, _id );
+ IEntityDSG::msDeletionsSafe=false;
+ return NULL; //Screw you inventory! pSpatialTree;
+}
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// TreeDSGLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void TreeDSGLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// TreeDSGLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void TreeDSGLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "TreeDSGLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+//************************************************************************
+//
+// Protected Member Functions : TreeDSGLoader
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : TreeDSGLoader
+//
+//************************************************************************
diff --git a/game/code/render/Loaders/TreeDSGLoader.h b/game/code/render/Loaders/TreeDSGLoader.h
new file mode 100644
index 0000000..8046f5d
--- /dev/null
+++ b/game/code/render/Loaders/TreeDSGLoader.h
@@ -0,0 +1,58 @@
+#ifndef __TreeDSGLoader_H__
+#define __TreeDSGLoader_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: TreeDSGLoader
+//
+// Description: The TreeDSGLoader does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/06/10]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/IWrappedLoader.h>
+
+//========================================================================
+//
+// Synopsis: The TreeDSGLoader; Synopsis by Inspection.
+//
+//========================================================================
+class TreeDSGLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ TreeDSGLoader();
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+protected:
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+};
+
+#endif
diff --git a/game/code/render/Loaders/WorldSphereLoader.cpp b/game/code/render/Loaders/WorldSphereLoader.cpp
new file mode 100644
index 0000000..97582d2
--- /dev/null
+++ b/game/code/render/Loaders/WorldSphereLoader.cpp
@@ -0,0 +1,309 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: WorldSphereLoader.cpp
+//
+// Description: Implementation for WorldSphereLoader class.
+//
+// History: Implemented --Devin [5/27/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/chunkfile.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/billboardobject.hpp>
+#include <p3d/inventory.hpp>
+#include <p3d/anim/skeleton.hpp>
+#include <p3d/anim/animate.hpp>
+#include <constants/chunks.h>
+#include <constants/chunkids.hpp>
+//========================================
+// Project Includes
+//========================================
+#include <render/Loaders/WorldSphereLoader.h>
+#include <render/Loaders/GeometryWrappedLoader.h>
+#include <render/DSG/WorldSphereDSG.h>
+#include <render/Loaders/LensFlareLoader.h>
+
+#include <constants/srrchunks.h>
+#include <memory/srrmemory.h>
+
+#include <render/Loaders/AllWrappers.h>
+#include <render/Loaders/BillboardWrappedLoader.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : WorldSphereLoader Interface
+//
+//************************************************************************
+//========================================================================
+// WorldSphereLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+WorldSphereLoader::WorldSphereLoader() :
+tSimpleChunkHandler(SRR2::ChunkID::WORLD_SPHERE_DSG)
+{
+
+ mpCompDLoader = new(GMA_PERSISTENT) tCompositeDrawableLoader;
+ mpCompDLoader->AddRef();
+
+ mpMCLoader = new(GMA_PERSISTENT) tMultiControllerLoader;
+ mpMCLoader->AddRef();
+
+ mpBillBoardQuadLoader = new (GMA_PERSISTENT) tBillboardQuadGroupLoader;
+ mpBillBoardQuadLoader->AddRef();
+
+ mpAnimLoader = new (GMA_PERSISTENT) tAnimationLoader;
+ mpAnimLoader->AddRef();
+
+ mpSkelLoader = new (GMA_PERSISTENT) tSkeletonLoader;
+ mpSkelLoader->AddRef();
+
+ mpFCLoader = new (GMA_PERSISTENT) tFrameControllerLoader;
+ mpFCLoader->AddRef();
+
+ mpLensFlareLoader = new (GMA_PERSISTENT) LensFlareLoader;
+ mpLensFlareLoader->AddRef();
+
+
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+//========================================================================
+// WorldSphereLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+WorldSphereLoader::~WorldSphereLoader()
+{
+ mpCompDLoader->Release();
+ mpMCLoader->Release();
+ mpBillBoardQuadLoader->Release();
+
+ mpAnimLoader->Release();
+ mpSkelLoader->Release();
+ mpFCLoader->Release();
+ mpLensFlareLoader->Release();
+}
+///////////////////////////////////////////////////////////////////////
+// tSimpleChunkHandler
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// WorldSphereLoader::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+tEntity* WorldSphereLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+ IEntityDSG::msDeletionsSafe=true;
+ char name[255];
+ f->GetPString(name);
+
+ int version = f->GetLong();
+
+ WorldSphereDSG *pWorldSphereDSG = new WorldSphereDSG;
+ pWorldSphereDSG->SetName(name);
+
+ tMultiController* pMC = NULL;
+
+ pWorldSphereDSG->SetNumMeshes(f->GetLong());
+
+ pWorldSphereDSG->SetNumBillBoardQuadGroups( f->GetLong() );
+
+ while(f->ChunksRemaining())
+ {
+ f->BeginChunk();
+ int id = f->GetCurrentID();
+ switch(f->GetCurrentID())
+ {
+ case Pure3D::Mesh::MESH:
+ {
+ GeometryWrappedLoader* pGeoLoader = (GeometryWrappedLoader*)AllWrappers::GetInstance()->mpLoader(AllWrappers::msGeometry) ;
+ tGeometry* pGeo = (tGeometry*)pGeoLoader->LoadObject(f, store);
+ store->Store( pGeo );
+ pWorldSphereDSG->AddMesh(pGeo);
+ break;
+ }
+ case P3D_COMPOSITE_DRAWABLE:
+ {
+ tCompositeDrawable* pCompDraw = static_cast<tCompositeDrawable*>( mpCompDLoader->LoadObject( f, store ) );
+ pWorldSphereDSG->SetCompositeDrawable( pCompDraw );
+ store->Store( pCompDraw );
+ }
+ break;
+ case P3D_SKELETON:
+ {
+ tSkeleton* pSkeleton = static_cast<tSkeleton*>(mpSkelLoader->LoadObject( f, store ));
+ rAssert( pSkeleton != NULL );
+ store->Store( pSkeleton );
+
+ break;
+ }
+ case SRR2::ChunkID::LENS_FLARE_DSG:
+ {
+ LensFlareDSG* pLensFlare = static_cast<LensFlareDSG*>(mpLensFlareLoader->LoadObject( f, store ));
+ pWorldSphereDSG->SetFlare( pLensFlare );
+ store->Store( pLensFlare );
+ }
+ break;
+ case Pure3D::Animation::AnimationData::ANIMATION:
+ {
+ tAnimation* pAnimation = static_cast<tAnimation*>(mpAnimLoader->LoadObject( f, store ));
+ rAssert( pAnimation != NULL );
+ store->Store( pAnimation );
+ break;
+ }
+ case Pure3D::Animation::FrameControllerData::FRAME_CONTROLLER:
+ {
+ tFrameController* pFC = static_cast<tFrameController*>(mpFCLoader->LoadObject( f, store ) );
+ pFC->SetCycleMode( FORCE_CYCLIC );
+ rAssert( pFC != NULL );
+ store->Store( pFC );
+ }
+ break;
+ case P3D_MULTI_CONTROLLER:
+ {
+ pMC = static_cast<tMultiController*>(mpMCLoader->LoadObject(f,store));
+ rAssert( pMC != NULL );
+ pWorldSphereDSG->SetMultiController(pMC);
+
+ store->Store( pMC );
+ break;
+ }
+ case Pure3D::BillboardObject::QUAD_GROUP:
+ {
+
+ BillboardWrappedLoader::OverrideLoader( true );
+ BillboardWrappedLoader* pBBQLoader = static_cast<BillboardWrappedLoader*>(AllWrappers::GetInstance()->mpLoader(AllWrappers::msBillboard));
+
+ tBillboardQuadGroup* pGroup = static_cast<tBillboardQuadGroup*>( pBBQLoader->LoadObject(f, store) );
+ rAssert( pGroup != NULL );
+ pWorldSphereDSG->AddBillBoardQuadGroup( pGroup );
+ store->Store( pGroup );
+ BillboardWrappedLoader::OverrideLoader( false );
+
+ break;
+ }
+
+ default:
+ break;
+ } // switch
+ f->EndChunk();
+ } // while
+
+ LensFlareDSG* pFlare = NULL;
+
+
+ mpListenerCB->OnChunkLoaded( pWorldSphereDSG, mUserData, _id );
+ IEntityDSG::msDeletionsSafe=false;
+ return pWorldSphereDSG;
+}
+///////////////////////////////////////////////////////////////////////
+// IWrappedLoader
+///////////////////////////////////////////////////////////////////////
+//========================================================================
+// WorldSphereLoader::SetRegdListener
+//========================================================================
+//
+// Description: Register a new listener/caretaker, notify old listener of
+// severed connection.
+//
+// Parameters: pListenerCB: Callback to call OnChunkLoaded
+// pUserData: Data to pass along for filtering, etc
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereLoader::SetRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+
+//========================================================================
+// WorldSphereLoader::ModRegdListener
+//========================================================================
+//
+// Description: Just fuck with the current pUserData
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldSphereLoader::ModRegdListener
+(
+ ChunkListenerCallback* pListenerCB,
+ int iUserData
+)
+{
+#if 0
+ char DebugBuf[255];
+ sprintf( DebugBuf, "WorldSphereLoader::ModRegdListener: pListenerCB %X vs mpListenerCB %X\n", pListenerCB, mpListenerCB );
+ rDebugString( DebugBuf );
+#endif
+ rAssert( pListenerCB == mpListenerCB );
+
+ mUserData = iUserData;
+}
+//************************************************************************
+//
+// Protected Member Functions : WorldSphereLoader
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : WorldSphereLoader
+//
+//************************************************************************
diff --git a/game/code/render/Loaders/WorldSphereLoader.h b/game/code/render/Loaders/WorldSphereLoader.h
new file mode 100644
index 0000000..21b5bf6
--- /dev/null
+++ b/game/code/render/Loaders/WorldSphereLoader.h
@@ -0,0 +1,76 @@
+#ifndef __WorldSphereLoader_H__
+#define __WorldSphereLoader_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: WorldSphereLoader
+//
+// Description: The WorldSphereLoader does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/05/27]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Loaders/IWrappedLoader.h>
+
+class tMultiControllerLoader;
+class tCompositeDrawableLoader;
+class tBillboardQuadGroupLoader;
+class tSkeletonLoader;
+class tAnimationLoader;
+class tFrameControllerLoader;
+class LensFlareLoader;
+
+//========================================================================
+//
+// Synopsis: The WorldSphereLoader; Synopsis by Inspection.
+//
+//========================================================================
+class WorldSphereLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+public:
+ WorldSphereLoader();
+ virtual ~WorldSphereLoader();
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+protected:
+ tCompositeDrawableLoader* mpCompDLoader;
+ tMultiControllerLoader* mpMCLoader;
+ tFrameControllerLoader* mpFCLoader;
+ tBillboardQuadGroupLoader* mpBillBoardQuadLoader;
+ tAnimationLoader* mpAnimLoader;
+ tSkeletonLoader* mpSkelLoader;
+ LensFlareLoader* mpLensFlareLoader;
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ //ChunkListenerCallback* mpListenerCB;
+ //void* mpUserData;
+private:
+};
+
+
+#endif
diff --git a/game/code/render/Loaders/allloaders.cpp b/game/code/render/Loaders/allloaders.cpp
new file mode 100644
index 0000000..881765b
--- /dev/null
+++ b/game/code/render/Loaders/allloaders.cpp
@@ -0,0 +1,18 @@
+#include <render/Loaders/AllWrappers.cpp>
+#include <render/Loaders/AnimCollLoader.cpp>
+#include <render/Loaders/AnimDSGLoader.cpp>
+#include <render/Loaders/BillboardWrappedLoader.cpp>
+#include <render/Loaders/breakableobjectloader.cpp>
+#include <render/Loaders/DynaPhysLoader.cpp>
+#include <render/Loaders/FenceLoader.cpp>
+#include <render/Loaders/GeometryWrappedLoader.cpp>
+#include <render/Loaders/instparticlesystemloader.cpp>
+#include <render/Loaders/InstStatEntityLoader.cpp>
+#include <render/Loaders/InstStatPhysLoader.cpp>
+#include <render/Loaders/IntersectLoader.cpp>
+#include <render/Loaders/StaticEntityLoader.cpp>
+#include <render/Loaders/StaticPhysLoader.cpp>
+#include <render/Loaders/TreeDSGLoader.cpp>
+#include <render/Loaders/WorldSphereLoader.cpp>
+#include <render/Loaders/LensFlareLoader.cpp>
+#include <render/Loaders/AnimDynaPhysLoader.cpp> \ No newline at end of file
diff --git a/game/code/render/Loaders/breakableobjectloader.cpp b/game/code/render/Loaders/breakableobjectloader.cpp
new file mode 100644
index 0000000..1017304
--- /dev/null
+++ b/game/code/render/Loaders/breakableobjectloader.cpp
@@ -0,0 +1,323 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: BreakableObjectLoader
+//
+// Description: Loads breakable objects
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+#include <p3d/chunkfile.hpp>
+#include <render/loaders/breakableobjectloader.h>
+#include <constants/srrchunks.h>
+#include <constants/chunkids.hpp>
+#include <constants/chunks.h>
+#include <render/particles/particlemanager.h>
+#include <p3d/anim/animatedobject.hpp>
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/anim/skeleton.hpp>
+#include <p3d/inventory.hpp>
+#include <p3d/utility.hpp>
+#include <constants/srrchunks.h>
+#include <memory/srrmemory.h>
+#include <render/Loaders/AllWrappers.h>
+#include <p3d/effects/particleloader.hpp>
+#include <render/breakables/BreakablesManager.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// BreakableObjectLoader::BreakableObjectLoader
+//===========================================================================
+// Description:
+// BreakableObjectLoader constructor
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+BreakableObjectLoader::BreakableObjectLoader()
+: tSimpleChunkHandler(SRR2::ChunkID::BREAKABLE_OBJECT)
+{
+ mpListenerCB = NULL;
+ mUserData = -1;
+
+ mpFactoryLoader = new (GMA_PERSISTENT)tAnimatedObjectFactoryLoader;
+ mpAnimObjectLoader = new (GMA_PERSISTENT)tAnimatedObjectLoader;
+
+ mpControllerLoader = new (GMA_PERSISTENT)tFrameControllerLoader;
+
+ mpCompDrawLoader = new (GMA_PERSISTENT)tCompositeDrawableLoader;
+ mpP3DGeoLoader = new (GMA_PERSISTENT)tGeometryLoader;
+
+ mpSkelLoader = new (GMA_PERSISTENT)tSkeletonLoader;
+ mpAnimLoader = new (GMA_PERSISTENT)tAnimationLoader;
+
+ mpParticleSystemLoader = new (GMA_PERSISTENT)tParticleSystemLoader;
+ mpParticleSystemFactoryLoader = new (GMA_PERSISTENT)tParticleSystemFactoryLoader;
+
+
+
+}
+//===========================================================================
+// BreakableObjectLoader::~BreakableObjectLoader
+//===========================================================================
+// Description:
+// BreakableObjectLoader destructor
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+BreakableObjectLoader::~BreakableObjectLoader()
+{
+ mpFactoryLoader->ReleaseVerified();
+ mpAnimObjectLoader->ReleaseVerified();
+
+ mpControllerLoader->ReleaseVerified();
+
+ mpCompDrawLoader->ReleaseVerified();
+ mpP3DGeoLoader->ReleaseVerified();
+
+ mpSkelLoader->ReleaseVerified();
+ mpAnimLoader->ReleaseVerified();
+
+ mpParticleSystemLoader->ReleaseVerified();
+ mpParticleSystemFactoryLoader->ReleaseVerified();
+
+}
+
+//===========================================================================
+// BreakableObjectLoader::SetRegdListener
+//===========================================================================
+// Description:
+//
+//
+// Constraints:
+//
+//
+// Parameters:
+//
+//
+// Return:
+//
+//===========================================================================
+
+void BreakableObjectLoader::SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData )
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+//===========================================================================
+// BreakableObjectLoader::SetRegdListener
+//===========================================================================
+// Description:
+//
+//
+// Constraints:
+//
+//
+// Parameters:
+//
+//
+// Return:
+//
+//===========================================================================
+
+void BreakableObjectLoader::ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData )
+{
+ rAssert( pListenerCB == mpListenerCB );
+ mUserData = iUserData;
+}
+//===========================================================================
+// BreakableObjectLoader::LoadObject
+//===========================================================================
+// Description:
+//
+//
+// Constraints:
+//
+//
+// Parameters:
+// tChunkFile - contains the input particle system to parse
+// tEntityStore - unused???
+//
+// Return:
+// An object that will get put into the inventory. Here we will put in a
+// tBreakableObject
+//
+//===========================================================================
+
+tEntity* BreakableObjectLoader::LoadObject(tChunkFile* file, tEntityStore* store)
+{
+ IEntityDSG::msDeletionsSafe=true;
+MEMTRACK_PUSH_GROUP( "Breakables" );
+
+
+ tAnimatedObjectFactory* pFactory = NULL;
+ tAnimatedObjectFrameController* pController = NULL;
+
+
+ unsigned long id = file->GetLong();
+ BreakablesEnum::BreakableID type = BreakablesEnum::BreakableID( id );
+ if ( GetBreakablesManager()->IsLoaded( type ) )
+ {
+ GetBreakablesManager()->AddToZoneList( type );
+ MEMTRACK_POP_GROUP( "Breakables" );
+ return NULL;
+ }
+
+ int maxInstances = static_cast<int>( file->GetLong() );
+
+ while( file->ChunksRemaining() )
+ {
+ file->BeginChunk();
+ switch( file->GetCurrentID() )
+ {
+ case Pure3D::AnimatedObject::FACTORY:
+ {
+ rAssert( pFactory == NULL );
+ tEntity* entity = mpFactoryLoader->LoadObject( file, store );
+ pFactory = static_cast<tAnimatedObjectFactory*> (entity);
+ rAssert( dynamic_cast< tAnimatedObjectFactory* >(entity) != NULL );
+ }
+ break;
+ case Pure3D::ParticleSystem::SYSTEM_FACTORY:
+ {
+ tEntity* pParticleSystemFactory = mpParticleSystemFactoryLoader->LoadObject( file, store );
+ rAssert( pParticleSystemFactory != NULL );
+ bool collision = store->TestCollision( pParticleSystemFactory->GetUID(), pParticleSystemFactory );
+ if( !collision )
+ {
+ store->Store( pParticleSystemFactory );
+ }
+ else
+ {
+ HandleCollision( pParticleSystemFactory );
+ }
+
+ }
+ break;
+ case Pure3D::ParticleSystem::SYSTEM:
+ {
+ tEntity* pParticleSystem = mpParticleSystemLoader->LoadObject( file, store );
+ rAssert( pParticleSystem != NULL );
+ bool collision = store->TestCollision( pParticleSystem->GetUID(), pParticleSystem );
+ if( !collision )
+ {
+ store->Store( pParticleSystem );
+ }
+ else
+ {
+ HandleCollision( pParticleSystem );
+ }
+
+ }
+ break;
+ case Pure3D::Animation::FrameControllerData::FRAME_CONTROLLER:
+ if ( pController == NULL )
+ {
+ tEntity* pFC = mpControllerLoader->LoadObject( file, store ) ;
+ pController = static_cast< tAnimatedObjectFrameController* > ( pFC );
+ store->Store( pFC );
+ }
+ break;
+ case Pure3D::AnimatedObject::OBJECT:
+ {
+ tEntity* pEntity = mpAnimObjectLoader->LoadObject( file, store );
+ rAssert( pEntity != NULL );
+ store->Store( pEntity );
+ }
+ break;
+ case P3D_COMPOSITE_DRAWABLE:
+ {
+ tEntity* entity = mpCompDrawLoader->LoadObject( file, store );
+ tCompositeDrawable* pCompDraw = static_cast< tCompositeDrawable* >( entity );
+ rAssert( dynamic_cast< tCompositeDrawable* >(entity) != NULL );
+ rAssert( pCompDraw != NULL );
+ store->Store( pCompDraw );
+ }
+ break;
+ case Pure3D::Mesh::MESH:
+ {
+ tEntity* pGeo = mpP3DGeoLoader->LoadObject( file, store );
+ bool collision = store->TestCollision( pGeo->GetUID(), pGeo );
+ if( !collision )
+ {
+ store->Store( pGeo );
+ }
+ else
+ {
+ HandleCollision( pGeo );
+ }
+ }
+ break;
+ case P3D_SKELETON:
+ {
+ tEntity* pSkel = mpSkelLoader->LoadObject( file, store );
+ rAssert( pSkel != NULL );
+ store->Store( pSkel );
+ }
+ break;
+ case Pure3D::Animation::AnimationData::ANIMATION:
+ {
+ tEntity* pAnim = mpAnimLoader->LoadObject( file, store );
+ rAssert( pAnim != NULL );
+ store->Store( pAnim );
+ }
+ break;
+ case P3D_MULTI_CONTROLLER:
+ break;
+
+ default:
+ int currchunk = file->GetCurrentID();
+ rAssertMsg(false, "Error-Unknown chunk in breakable object");
+
+ break;
+ }
+ file->EndChunk();
+ }
+
+ BreakablesManager::GetInstance()->AllocateBreakables( type, pFactory, pController, maxInstances );
+
+ if ( pFactory != NULL )
+ {
+ pFactory->ReleaseVerified();
+ }
+
+MEMTRACK_POP_GROUP("Breakables");
+
+ IEntityDSG::msDeletionsSafe=false;
+ return NULL;
+}
diff --git a/game/code/render/Loaders/breakableobjectloader.h b/game/code/render/Loaders/breakableobjectloader.h
new file mode 100644
index 0000000..a53f184
--- /dev/null
+++ b/game/code/render/Loaders/breakableobjectloader.h
@@ -0,0 +1,104 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: breakableobjectloader
+//
+// Description: Loads breakable objects
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef BREAKABLEOBJECTLOADER_H
+#define BREAKABLEOBJECTLOADER_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <render/Loaders/IWrappedLoader.h>
+
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class tAnimatedObjectFactoryLoader;
+class tAnimatedObjectLoader;
+class tFrameControllerLoader;
+class tCompositeDrawableLoader;
+class tGeometryLoader;
+class tSkeletonLoader;
+class tAnimationLoader;
+class tParticleSystemLoader;
+class tParticleSystemFactoryLoader;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Loads breakableobject chunks. Essentially just a tAnimatedObjectFactory thats
+// cloned off N times.
+//
+// Constraints:
+//
+//===========================================================================
+class BreakableObjectLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+ public:
+ BreakableObjectLoader();
+ virtual ~BreakableObjectLoader();
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ virtual void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ virtual void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+ protected:
+
+ private:
+
+ tAnimatedObjectFactoryLoader* mpFactoryLoader;
+ tAnimatedObjectLoader* mpAnimObjectLoader;
+
+ tFrameControllerLoader* mpControllerLoader;
+
+ tCompositeDrawableLoader* mpCompDrawLoader;
+ tGeometryLoader* mpP3DGeoLoader;
+
+ tSkeletonLoader* mpSkelLoader;
+ tAnimationLoader* mpAnimLoader;
+
+ tParticleSystemLoader* mpParticleSystemLoader;
+ tParticleSystemFactoryLoader* mpParticleSystemFactoryLoader;
+
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow BreakableObjectLoader from being copied and assigned.
+ BreakableObjectLoader( const BreakableObjectLoader& );
+ BreakableObjectLoader& operator=( const BreakableObjectLoader& );
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Loaders/instparticlesystemloader.cpp b/game/code/render/Loaders/instparticlesystemloader.cpp
new file mode 100644
index 0000000..91c5f63
--- /dev/null
+++ b/game/code/render/Loaders/instparticlesystemloader.cpp
@@ -0,0 +1,229 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: InstParticleSystemLoader
+//
+// Description: Loads particle systems (and also breakable objects)
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <render/loaders/InstParticleSystemLoader.h>
+#include <constants/srrchunks.h>
+#include <constants/chunkids.hpp>
+#include <render/particles/particlemanager.h>
+#include <p3d/effects/particlesystem.hpp>
+#include <p3d/effects/particleloader.hpp>
+#include <p3d/chunkfile.hpp>
+#include <p3d/utility.hpp>
+#include <constants/srrchunks.h>
+#include <memory/srrmemory.h>
+#include <render/Loaders/AllWrappers.h>
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// InstParticleSystemLoader::InstParticleSystemLoader
+//===========================================================================
+// Description:
+// InstParticleSystemLoader constructor
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+InstParticleSystemLoader::InstParticleSystemLoader()
+: tSimpleChunkHandler(SRR2::ChunkID::INST_PARTICLE_SYSTEM)
+{
+ mpListenerCB = NULL;
+ mUserData = -1;
+}
+//===========================================================================
+// InstParticleSystemLoader::~InstParticleSystemLoader
+//===========================================================================
+// Description:
+// InstParticleSystemLoader destructor
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+InstParticleSystemLoader::~InstParticleSystemLoader()
+{
+
+}
+
+//===========================================================================
+// InstParticleSystemLoader::SetRegdListener
+//===========================================================================
+// Description:
+//
+//
+// Constraints:
+//
+//
+// Parameters:
+//
+//
+// Return:
+//
+//===========================================================================
+
+void InstParticleSystemLoader::SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData )
+{
+ //
+ // Follow protocol; notify old Listener, that it has been
+ // "disconnected".
+ //
+ if( mpListenerCB != NULL )
+ {
+ mpListenerCB->OnChunkLoaded( NULL, iUserData, 0 );
+ }
+ mpListenerCB = pListenerCB;
+ mUserData = iUserData;
+}
+//===========================================================================
+// InstParticleSystemLoader::SetRegdListener
+//===========================================================================
+// Description:
+//
+//
+// Constraints:
+//
+//
+// Parameters:
+//
+//
+// Return:
+//
+//===========================================================================
+
+void InstParticleSystemLoader::ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData )
+{
+ rAssert( pListenerCB == mpListenerCB );
+ mUserData = iUserData;
+}
+//===========================================================================
+// InstParticleSystemLoader::LoadObject
+//===========================================================================
+// Description:
+//
+//
+// Constraints:
+//
+//
+// Parameters:
+// tChunkFile - contains the input particle system to parse
+// tEntityStore - unused???
+//
+// Return:
+// An object that will get put into the inventory. Here we will put in a
+// tParticleSystem
+//
+//===========================================================================
+
+tEntity* InstParticleSystemLoader::LoadObject(tChunkFile* file, tEntityStore* store)
+{
+ IEntityDSG::msDeletionsSafe=true;
+MEMTRACK_PUSH_GROUP( "Particle Systems" );
+
+ tParticleSystemFactoryLoader* pFactoryLoader = new (GMA_TEMP) tParticleSystemFactoryLoader;
+ tParticleSystemFactory* pFactory = NULL;
+ tFrameControllerLoader* pControllerLoader = new (GMA_TEMP) tFrameControllerLoader;
+ tEffectController* pController = NULL;
+
+ unsigned long id = file->GetLong();
+ ParticleEnum::ParticleID type = ParticleEnum::ParticleID(id);
+
+ int maxInstances = static_cast<int>( file->GetLong() );
+
+ while( file->ChunksRemaining() )
+ {
+ file->BeginChunk();
+ switch( file->GetCurrentID() )
+ {
+ case Pure3D::ParticleSystem::SYSTEM_FACTORY:
+ {
+
+ pFactory = static_cast<tParticleSystemFactory*> (pFactoryLoader->LoadObject( file, store ) );
+ bool collision = store->TestCollision( pFactory->GetUID(), pFactory );
+ if( !collision )
+ {
+ store->Store( pFactory );
+ }
+ else
+ {
+ pFactory->Release();
+ pFactory = 0;
+ }
+ break;
+ }
+ case Pure3D::Animation::FrameControllerData::FRAME_CONTROLLER:
+ {
+ if ( pController == NULL )
+ {
+ tFrameController* pfc = (tFrameController*)pControllerLoader->LoadObject(file,store);
+ pController = static_cast<tEffectController*>(pfc);
+ if ( pController != NULL )
+ {
+ bool collision = store->TestCollision( pController->GetUID(), pController );
+ if( !collision )
+ {
+ store->Store( pController );
+ }
+ else
+ {
+ pController->Release();
+ pController = 0;
+ }
+ }
+ }
+ break;
+ }
+ // Textures, shaders
+ default:
+ unsigned int fileId = file->GetCurrentID();
+ tChunkHandler* pChunkHandler = p3d::loadManager->GetP3DHandler()->GetHandler( file->GetCurrentID() );
+ if ( pChunkHandler != NULL )
+ tLoadStatus status = pChunkHandler->Load( file, store );
+
+ break;
+ }
+ file->EndChunk();
+ }
+
+ if(pFactory)
+ {
+ ParticleManager::GetInstance()->InitializeSystem( type, pFactory, pController, maxInstances );
+ }
+
+ pFactoryLoader->ReleaseVerified();
+ pControllerLoader->ReleaseVerified();
+
+MEMTRACK_POP_GROUP( "Particle Systems" );
+
+ IEntityDSG::msDeletionsSafe=false;
+ return NULL;
+}
diff --git a/game/code/render/Loaders/instparticlesystemloader.h b/game/code/render/Loaders/instparticlesystemloader.h
new file mode 100644
index 0000000..f9d0831
--- /dev/null
+++ b/game/code/render/Loaders/instparticlesystemloader.h
@@ -0,0 +1,84 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: InstParticleSystemLoader
+//
+// Description: Loads particle systems (and also breakable objects)
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef INSTPARTICLESYSTEMLOADER_H
+#define INSTPARTICLESYSTEMLOADER_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <render/Loaders/IWrappedLoader.h>
+
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+// Describe here what you require of the client which uses or
+// has this class - essentially, the converse of the description
+// above. A constraint is an expression of some semantic
+// condition that must be preserved, an invariant of a class or
+// relationship that must be preserved while the system is in a
+// steady state.
+//
+//===========================================================================
+class InstParticleSystemLoader
+: public tSimpleChunkHandler,
+ public IWrappedLoader
+{
+ public:
+ InstParticleSystemLoader();
+ virtual ~InstParticleSystemLoader();
+
+ ///////////////////////////////////////////////////////////////////////
+ // IWrappedLoader
+ ///////////////////////////////////////////////////////////////////////
+ virtual void SetRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ virtual void ModRegdListener( ChunkListenerCallback* pListenerCB,
+ int iUserData );
+
+ ///////////////////////////////////////////////////////////////////////
+ // tSimpleChunkHandler
+ ///////////////////////////////////////////////////////////////////////
+ virtual tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+ protected:
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow InstParticleSystemLoader from being copied and assigned.
+ InstParticleSystemLoader( const InstParticleSystemLoader& );
+ InstParticleSystemLoader& operator=( const InstParticleSystemLoader& );
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Particles/allparticles.cpp b/game/code/render/Particles/allparticles.cpp
new file mode 100644
index 0000000..e6460df
--- /dev/null
+++ b/game/code/render/Particles/allparticles.cpp
@@ -0,0 +1,3 @@
+#include <render/Particles/particlemanager.cpp>
+#include <render/Particles/particlesystemdsg.cpp>
+#include <render/Particles/vehicleparticleemitter.cpp>
diff --git a/game/code/render/Particles/particlemanager.cpp b/game/code/render/Particles/particlemanager.cpp
new file mode 100644
index 0000000..a969c70
--- /dev/null
+++ b/game/code/render/Particles/particlemanager.cpp
@@ -0,0 +1,822 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ParticleManager
+//
+// Description: The ParticleManager class is a singleton that encapsulates
+// all particle effects in the game. Currently this means
+// Breakable Objects, and InstParticleSystems
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <render/particles/particlemanager.h>
+#include <memory/srrmemory.h> // We are putting this class on the GMA_PERSISTENT heap
+#include <algorithm> // For std algorithms that operate on our std::list of systems
+#include <render/RenderManager/RenderLayer.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/Culling/WorldScene.h>
+#include <memory/srrmemory.h>
+#include <p3d/anim/animatedobject.hpp>
+#include <p3d/effects/effect.hpp>
+#include <p3d/effects/particlesystem.hpp>
+#include <worldsim/coins/sparkle.h>
+#include <constants/particleenum.h>
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+// Static instance of our singleton pointer.
+ParticleManager* ParticleManager::spInstance = NULL;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+
+//==============================================================================
+// ParticleManager::ManagedParticleSystem::ManagedParticleSystem
+//==============================================================================
+//
+// Description: ManagedParticleSystem ctor (instanced particle systems)
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: Must be given valid pFactory and pController pointers
+//
+//==============================================================================
+ParticleManager::ManagedParticleSystem::ManagedParticleSystem( tParticleSystemFactory* pFactory, tEffectController* pController )
+: mUserID( -1 ),
+mIsActive( false ),
+mEmissionBias( 1.0f ),
+mThrowUpNewParticles( false ),
+mIsInDSG( false )
+{
+ mpSystem = new ParticleSystemDSG;
+ mpSystem->AddRef();
+
+ rAssert( mpSystem != NULL );
+ mpSystem->Init( pFactory, pController );
+}
+//==============================================================================
+// ParticleManager::ParticleSystemEntityDSG::~ParticleSystemEntityDSG
+//==============================================================================
+//
+// Description: ParticleSystemEntityDSG dtor
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//==============================================================================
+ParticleManager::ManagedParticleSystem::~ManagedParticleSystem()
+{
+
+ mpSystem->Release();
+ mpSystem = NULL;
+}
+//==============================================================================
+// ParticleManager::ManagedParticleSystem::Update
+//==============================================================================
+//
+// Description: Updates animation based upon given time delta
+//
+// Parameters: Time delta in milliseconds
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//==============================================================================
+void ParticleManager::ManagedParticleSystem::Update( float deltaTime)
+{
+
+ if ( IsLocked() )
+ {
+ if ( mThrowUpNewParticles )
+ {
+ mThrowUpNewParticles = false;
+ }
+ else
+ {
+ SetBias( p3dParticleSystemConstants::EmitterBias::EMISSION, 0.0f);
+
+ if ( mpSystem->GetNumLiveParticles() == 0 )
+ {
+ Unlock();
+ SetActive( false );
+ }
+ }
+ }
+ mpSystem->Update ( deltaTime );
+}
+//==============================================================================
+// ParticleManager::ManagedParticleSystem::Reset
+//==============================================================================
+//
+// Description: Resets animation to the start
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//==============================================================================
+void ParticleManager::ManagedParticleSystem::Reset()
+{
+ mpSystem->Reset ();
+}
+
+
+
+
+//==============================================================================
+// ParticleManager::ParticleSystemEntityDSG::SetTransform
+//==============================================================================
+//
+// Description: Sets the transform matrix (local->world)
+//
+// Parameters: Transformation matrix that contains the world position/orientation of the system
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//==============================================================================
+void ParticleManager::ManagedParticleSystem::SetTransform( const rmt::Matrix& transform )
+{
+ // Check to see if the object has moved, if it has, and its in the DSG
+ // call DSGtree->Move
+ rmt::Vector oldPos = mpSystem->rPosition();
+ rmt::Vector newPos = transform.Row(3);
+ if ( mIsInDSG && newPos != oldPos )
+ {
+ rmt::Box3D oldBB;
+ mpSystem->GetBoundingBox( &oldBB );
+ mpSystem->SetTransform( transform );
+
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mLayer ));
+ // Sanity check
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Move( oldBB, mpSystem );
+
+ }
+ else
+ {
+ mpSystem->SetTransform( transform );
+ }
+}
+//==============================================================================
+// ParticleManager::ParticleSystemEntityDSG::LastFrameReached
+//==============================================================================
+//
+// Description: Returns how many times the animation has been played through.
+// Useful for finding out if the animation has reached the end of its lifespan
+// and should be deactivated.
+//
+// Parameters: None.
+//
+// Return: int, 0 for still playing, 1 for played once through, N for played N times.
+//
+// Constraints: None.
+//
+//==============================================================================
+int ParticleManager::ManagedParticleSystem::LastFrameReached() const
+{
+ return mpSystem->LastFrameReached();
+}
+
+
+//==============================================================================
+// ParticleManager::CreateInstance
+//==============================================================================
+//
+// Description: Creates the ParticleManager.
+//
+// Parameters: None.
+//
+// Return: Pointer to the ParticleManager.
+//
+// Constraints: Multiple calls to CreateInstance without a DestroyInstance call in between
+// will result in an assertion (or lost memory if assertions not enabled)
+//
+//==============================================================================
+ParticleManager* ParticleManager::CreateInstance()
+{
+ rAssert( spInstance == NULL );
+
+ spInstance = new(GMA_PERSISTENT) ParticleManager;
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+//==============================================================================
+// ParticleManager::GetInstance
+//==============================================================================
+//
+// Description: Returns a ParticleManager singleton object.
+//
+// Parameters: None.
+//
+// Return: Pointer to a ParticleManager object.
+//
+// Constraints: Assertion failure if CreateInstance was not called first.
+//
+//
+//==============================================================================
+ParticleManager* ParticleManager::GetInstance()
+{
+ if ( spInstance == NULL )
+ {
+ CreateInstance();
+ }
+ rAssert ( spInstance != NULL);
+
+ return spInstance;
+}
+//==============================================================================
+// ParticleManager::DestroyInstance
+//==============================================================================
+//
+// Description: Frees the ParticleManager singleton.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: Assertion failure if CreateInstance was not called first.
+//
+//
+//==============================================================================
+void ParticleManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+}
+
+
+//==============================================================================
+// ParticleManager::DeactiveateAll
+//==============================================================================
+//
+// Description: Removes all active particles from the DSG
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints:
+//
+//
+//==============================================================================
+
+void ParticleManager::DeactiveateAll()
+{
+ // for each active particle system type
+ for (unsigned int i = 0 ; i < mActiveSystems.Size() ; ++i)
+ {
+ for (unsigned int j = 0 ; j < mActiveSystems[i].Size() ; ++j)
+ {
+ // Advance controller based upon time elapsed but only if it is active
+ if ( mActiveSystems[i][j]->IsActive() )
+ {
+ mActiveSystems[i][j]->SetActive( false );
+ }
+ }
+ }
+}
+
+
+//==============================================================================
+// ParticleManager::ParticleManager
+//==============================================================================
+//
+// Description: ParticleManager constructor.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints:
+//
+//
+//==============================================================================
+ParticleManager::ParticleManager()
+{
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+ // Allocate the (std:vector) arrays that will be filled out using the InitializeSystem function
+ mActiveSystems.Grow( ParticleEnum::eNumParticleTypes );
+ mIsParticleTypeDynamicallyLoaded.resize( ParticleEnum::eNumParticleTypes, false );
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+}
+//==============================================================================
+// ParticleManager::~ParticleManager
+//==============================================================================
+//
+// Description: ParticleManager destructor, frees all particle systems via ClearSystems()
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints:
+//
+//
+//==============================================================================
+ParticleManager::~ParticleManager()
+{
+
+ // Clear out active particle systems
+ ClearSystems();
+}
+
+
+//==============================================================================
+// ParticleManager::Clear
+//==============================================================================
+//
+// Description: Frees all particle systems that were added to the manager.
+// GetNumParticleSystems() will return zero after this call.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: Removal of the system from the scene graph not implemented yet!
+//
+//
+//==============================================================================
+void ParticleManager::ClearSystems()
+{
+ // for each active particle system
+ // remove it from the scene graph
+ DeactiveateAll();
+ // free it
+ for (unsigned int i = 0 ; i < mActiveSystems.Size() ; ++i)
+ {
+ for (unsigned int j = 0 ; j < mActiveSystems[i].Size () ; ++j)
+ {
+ delete mActiveSystems[i][j];
+ }
+ mActiveSystems[i].Shrink(0);
+ }
+}
+
+//==============================================================================
+// ParticleManager::InitializeSystem
+//==============================================================================
+//
+// Description: Creates maxInstances of InstParticleSystems objects via cloning the given factory
+// and frame controllers
+//
+// Parameters:
+// ParticleEnum::ParticleID, type of particle to create
+// tParticleSystemFactory*, typically from a instparticlesystem object chunk file
+// tEffectController*, typically from a instparticlesystem object chunk file
+// int maxInstances, number of instanes to instantiate
+//
+// Return: None.
+//
+// Constraints: InitializeSystem cannot be called twice for the same type
+//
+//
+//==============================================================================
+void ParticleManager::InitializeSystem( ParticleEnum::ParticleID type,
+ tParticleSystemFactory* pFactory,
+ tEffectController* pController,
+ int maxInstances,
+ bool isDynamic )
+{
+ rAssert( type >=0 && type < ParticleEnum::eNumParticleTypes );
+ rAssert( pFactory != NULL );
+ rAssert( pController != NULL );
+ rAssert( maxInstances > 0 );
+
+ // Remember we are holding pointers, if we call resize, any valid pointers that are killed
+ // will not be freed properly, assert to make sure this never happens
+ rAssert( mActiveSystems[ type ].Size() == 0 );
+
+
+ MEMTRACK_PUSH_GROUP( "InitParticleSystem" );
+
+ //This should be looked at. TODO
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ #endif
+ mActiveSystems[ type ].Resize( maxInstances );
+
+
+ for (int i = 0 ; i < maxInstances ; ++i)
+ {
+ mActiveSystems[ type ][ i ] = new ManagedParticleSystem( pFactory, pController);
+ }
+ mIsParticleTypeDynamicallyLoaded[ type ] = isDynamic;
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ #endif
+ MEMTRACK_POP_GROUP( "InitParticleSystem" );
+
+
+}
+
+//==============================================================================
+// ParticleManager::DeleteSystem
+//==============================================================================
+//
+// Description: Kills all particles of the given type
+//
+// Parameters: The ParticleEnum type.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//
+//==============================================================================
+void ParticleManager::DeleteSystem( ParticleEnum::ParticleID type )
+{
+ rAssert( type >=0 && type < ParticleEnum::eNumParticleTypes );
+ for (unsigned int i = 0 ; i < mActiveSystems[ type ].Size() ; ++i)
+ {
+
+ // Remove it from the DSG
+ mActiveSystems[ type ][ i ]->SetActive( false );
+ delete mActiveSystems[ type ][ i ];
+ }
+ mActiveSystems[ type ].Shrink(0);
+}
+//==============================================================================
+// ParticleManager::DumpDynaLoad
+//==============================================================================
+//
+// Description: Kills all the particles of any type that was
+// Initialized with isDynamic to true
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//
+//==============================================================================
+
+void ParticleManager::DumpDynaLoad()
+{
+ for (int i = 0 ; i < ParticleEnum::eNumParticleTypes ; ++i)
+ {
+ if ( mIsParticleTypeDynamicallyLoaded[ i ] )
+ {
+ DeleteSystem( ParticleEnum::ParticleID( i ) );
+ mIsParticleTypeDynamicallyLoaded[ i ] = false;
+ }
+ }
+}
+
+//==============================================================================
+// ParticleManager::GetUniqueID
+//==============================================================================
+//
+// Description: Returns an identifier that can be used to reference
+// a continuously playing particle effect
+//
+// Parameters: None.
+//
+// Return: ParticlePlayerID, different each time function is called
+//
+// Constraints: None.
+//
+//
+//==============================================================================
+
+ParticlePlayerID ParticleManager::GetUniqueID()const
+{
+ static int sUniqueID = 0;
+ return sUniqueID++;
+}
+
+//==============================================================================
+// ParticleManager::PlayCyclic
+//==============================================================================
+//
+// Description: Tells the PM to play a cycling particle system. Note that this function
+// must be called every frame to have the emitter keep adding particles to
+// it. Otherwise, EMISSION bias is set to zero. Alive particles will gradually
+// fall to earth and disappear.
+//
+// Parameters: ParticlePlayerID - identifying a particle system that is currently playing
+// or assigning a new one if never played before
+//
+// ParticleAttributes structure. Identifying particle type and attributes.
+//
+// rmt::Matrix, with local->world transform matrix
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//
+//==============================================================================
+
+
+void ParticleManager::PlayCyclic( ParticlePlayerID id, const ParticleAttributes& attr, const rmt::Matrix& localMatrix )
+{
+ rAssert( attr.mType >=0 && attr.mType < ParticleEnum::eNumParticleTypes );
+ // get animation associated with UniqueID, or assign an unused one.
+
+ if( attr.mType == ParticleEnum::eStars )
+ {
+ return;
+ }
+
+ rmt::Matrix orientedLocalMatrix;
+ ReorientUpAxis( localMatrix, &orientedLocalMatrix );
+
+ ManagedParticleSystem* pFreeSystem = NULL;
+ bool wasFreeSystemFound = false;
+
+ rWarningMsg( mActiveSystems[ attr.mType ].Size() > 0 , "ParticleManager::PlayCyclic, particles of that type have not been loaded!" );
+
+ for ( unsigned int i = 0 ; i < mActiveSystems[ attr.mType ].Size() ; i++ )
+ {
+ ManagedParticleSystem* currentSystem = mActiveSystems[ attr.mType ][ i ];
+ if ( id == currentSystem->GetUserID() )
+ {
+ // we have found the system we are looking for
+ // tell it to keep playing
+ currentSystem->ThrowUpNewParticles();
+ currentSystem->SetTransform( orientedLocalMatrix );
+ currentSystem->SetBias( p3dParticleSystemConstants::EmitterBias::EMISSION, attr.mEmissionBias );
+ currentSystem->SetVelocity( attr.mVelocity );
+ return;
+ }
+ else if ( wasFreeSystemFound == false &&
+ currentSystem->IsActive() == false &&
+ currentSystem->GetUserID() < 0 )
+ {
+ // this thing is unused. Lets flag it so that we can use it for
+ // a locked player if needed
+ wasFreeSystemFound = true;
+ pFreeSystem = currentSystem;
+ }
+ }
+ // The system was not assigned yet. Assign it now.
+ if ( pFreeSystem != NULL )
+ {
+ pFreeSystem->SetTransform( orientedLocalMatrix );
+ pFreeSystem->SetActive( true );
+ pFreeSystem->Lock( id );
+ pFreeSystem->ThrowUpNewParticles();
+ pFreeSystem->SetVelocity( attr.mVelocity );
+ pFreeSystem->SetBias( p3dParticleSystemConstants::EmitterBias::EMISSION, attr.mEmissionBias );
+ }
+}
+
+//==============================================================================
+// ParticleManager::PlayCyclic
+//==============================================================================
+//
+// Description: Tells the PM to play a cycling particle system. Note that this function
+// must be called every frame to have the emitter keep adding particles to
+// it. Otherwise, EMISSION bias is set to zero. Alive particles will gradually
+// fall to earth and disappear.
+//
+// Parameters: ParticlePlayerID - identifying a particle system that is currently playing
+// or assigning a new one if never played before
+//
+// ParticleAttributes structure. Identifying particle type and attributes.
+//
+// rmt::Vector, with world position
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//
+//==============================================================================
+
+
+void ParticleManager::PlayCyclic( ParticlePlayerID id, const ParticleAttributes& attr, const rmt::Vector& position )
+{
+ rmt::Matrix localMatrix;
+ localMatrix.Identity();
+ localMatrix.FillTranslate( position );
+
+ PlayCyclic( id, attr, localMatrix );
+}
+
+
+
+
+
+//==============================================================================
+// ParticleManager::Add
+//==============================================================================
+//
+// Description: Adds a new particle system into the manager, 2nd parameter is a matrix
+//
+// Parameters: ParticleAttributes structure, matrix describing orientation/
+// position of particles
+//
+// Return: None.
+//
+// Constraints: Currently all systems are set to non cyclic mode.
+// Attributes structure is extremely simplified at this point
+//
+//
+//
+//==============================================================================
+void ParticleManager::Add( const ParticleAttributes& attr, const rmt::Matrix& localMatrix )
+{
+ if( attr.mType == ParticleEnum::eStars )
+ {
+ GetSparkleManager()->AddStars( localMatrix.Row( 3 ), 1.0f );
+ return; // Early return. This particle system is now done procedurally.
+ }
+ rWarningMsg( mActiveSystems[ attr.mType ].Size() > 0, "ParticleManager::Add(), Particles of that type have not been loaded" );
+
+ for (unsigned int i = 0 ; i < mActiveSystems[ attr.mType ].Size() ; ++i)
+ {
+
+ if ( mActiveSystems[ attr.mType ][ i ]->IsActive() == false )
+ {
+ rmt::Matrix orientedLocalMatrix;
+ ReorientUpAxis( localMatrix, &orientedLocalMatrix );
+ mActiveSystems[ attr.mType ][ i ]->Reset();
+ mActiveSystems[ attr.mType ][ i ]->SetTransform( localMatrix );
+ mActiveSystems[ attr.mType ][ i ]->SetActive( true );
+ mActiveSystems[ attr.mType ][ i ]->SetBias( p3dParticleSystemConstants::EmitterBias::EMISSION, attr.mEmissionBias );
+ break;
+ }
+ }
+
+
+}
+//==============================================================================
+//
+// Description: Adds a new particle system into the manager, 2nd parameter is a vector
+//
+// Parameters: ParticleAttributes structure, vector containing position (no rotation associated)
+// with the particles
+//
+// Return: None.
+//
+// Constraints: Currently all systems are set to non cyclic mode.
+// Attributes structure is extremely simplified at this point
+//
+//
+//
+//==============================================================================
+void ParticleManager::Add( const ParticleAttributes& attr, const rmt::Vector& position )
+{
+ rmt::Matrix localMatrix;
+ localMatrix.Identity();
+ localMatrix.FillTranslate ( position );
+ Add ( attr, localMatrix );
+}
+//==============================================================================
+// ParticleManager::DebugRender
+//==============================================================================
+//
+// Description: DSG doesnt have sorting yet. Though to see whats going on
+// use this function to force a render of active systems
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: Don't call it!.
+//
+//
+//
+//==============================================================================
+
+
+//==============================================================================
+// ParticleManager::Update
+//==============================================================================
+//
+// Description: Updates the particle animation frame for each active system
+// thats being managed
+//
+// Parameters: Elapsed time in milliseconds.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//
+//
+//==============================================================================
+
+void ParticleManager::Update( unsigned int deltaTime )
+{
+
+ float fDeltaTime = static_cast< float >( deltaTime );
+
+ // for each active particle system type
+ for (unsigned int i = 0 ; i < mActiveSystems.Size() ; ++i)
+ {
+ for (unsigned int j = 0 ; j < mActiveSystems[i].Size() ; ++j)
+ {
+ // Advance controller based upon time elapsed but only if it is active
+ if ( mActiveSystems[i][j]->IsActive() )
+ {
+ // Advance animation and/or throw up new particles.
+ mActiveSystems[i][j]->Update( fDeltaTime );
+
+ if ( mActiveSystems[i][j]->IsLocked() )
+ {
+ // Locked systems can be in one of several states
+ // 1) they were told to throw up new particles
+ // in between calls to Update
+
+ // 2) they were not told to throw up new particles
+ // but still have active particles inside them
+ // (keep system assigned to a user)
+
+ // 3) no throw, no active particles, return it to the
+ // system for reassignment
+
+ }
+ else
+ {
+ // if the animation is complete and the system is not cyclic
+ if ( mActiveSystems[i][j]->LastFrameReached() )
+ {
+ // tell the DSG to remove the system
+ // disable playback
+ mActiveSystems[i][j]->SetActive( false );
+ }
+ }
+ }
+ }
+ }
+
+}
+void ParticleManager::ManagedParticleSystem::SetActive( bool isActive )
+{
+ if ( mIsActive && !isActive && mIsInDSG)
+ {
+ // We are deactivating the system
+ // remove it from the DSG
+
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mLayer ));
+ // Sanity check
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Remove( mpSystem );
+
+ mIsInDSG = false;
+ }
+ else if ( !mIsActive && isActive && !mIsInDSG )
+ {
+ mLayer = static_cast< RenderEnums::LayerEnum > (GetRenderManager()->rCurWorldRenderLayer() );
+
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mLayer ));
+ // Sanity check
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Add( mpSystem );
+
+ mIsInDSG = true;
+ }
+ mIsActive = isActive;
+
+}
+
+void
+ParticleManager::ReorientUpAxis( const rmt::Matrix& in, rmt::Matrix* out )
+{
+ *out = in;
+ out->Row(1) = rmt::Vector( 0.0f, 1.0f, 0.0f );
+ out->Row(0).CrossProduct( out->Row(1), out->Row( 0 ) );
+}
diff --git a/game/code/render/Particles/particlemanager.h b/game/code/render/Particles/particlemanager.h
new file mode 100644
index 0000000..5cd8c9c
--- /dev/null
+++ b/game/code/render/Particles/particlemanager.h
@@ -0,0 +1,204 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: ParticleManager
+//
+// Description: Singleton class that encapsulates handles free floating particle systems
+// e.g. particles generated from collisions and other one-off entries
+// Particle Systems are added into the DSG
+//
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef PARTICLEMANAGER_H
+#define PARTICLEMANAGER_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <vector>
+#include <memory\srrmemory.h> // Needed for my STL allocations to go on the right heap
+#include <memory/stlallocators.h>
+#include <p3d/p3dtypes.hpp>
+#include <radmath/radmath.hpp>
+#include <radmath/vector.hpp>
+#include <render/Enums/RenderEnums.h>
+#include <p3d/effects/effect.hpp>
+#include <constants/particleenum.h>
+#include <render/particles/particlesystemdsg.h>
+#include <p3d/array.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class tParticleSystemFactory;
+class tEffectController;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+typedef int ParticlePlayerID;
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+
+// Structure passed to ParticleManager::Add that describes what type of
+// particle system to create
+struct ParticleAttributes
+{
+
+ ParticleAttributes() :
+ mEmissionBias ( 1.0f ),
+ mVelocity( 0, 0, 0)
+ { }
+ ParticleEnum::ParticleID mType;
+
+ // only one bias so far, the emission bias
+ // other biases would be simple to put in
+ // NumParticles/Life/Speed/Weight/Gravity/Drag/Size/Spin
+ float mEmissionBias;
+
+ rmt::Vector mVelocity;
+
+};
+
+//===========================================================================
+//
+// Description:
+// ParticleManager provides a way to centalize adding/updating/rendering
+// of particle systems. Particles are inserted into the scene graph at the
+// specified layer and rendered when that specific layer is rendered.
+//
+// Constraints:
+//
+//===========================================================================
+
+class ParticleManager
+{
+
+ public:
+
+ // Static Methods (for creating, destroying and acquiring an instance
+ // of the ParticleManager)
+ static ParticleManager* CreateInstance();
+ static ParticleManager* GetInstance();
+ static void DestroyInstance();
+
+
+ // Removes all particles from the DSG. Doesn't free any of them
+ void DeactiveateAll();
+
+ // Systems are allocated into the ParticleManager via the InitializeSystem functions
+ // These should be called from inside the particle system and breakable object loaders
+ void InitializeSystem( ParticleEnum::ParticleID type, tParticleSystemFactory* factory, tEffectController* controller, int maxInstances, bool isDynamic = false);
+
+ // Deletes a system, and frees all particles associated with it
+ void DeleteSystem( ParticleEnum::ParticleID type );
+
+ // Kills all the particles of any type that was Initialized with isDynamic to true
+ void DumpDynaLoad();
+
+ // Returns an identifier that can be used to reference
+ // a continuously playing particle effect
+ ParticlePlayerID GetUniqueID()const;
+
+ // Tells the PM to play a cycling particle system. Note that this function
+ // must be called every frame to have the emitter keep adding particles to
+ // it. Otherwise, EMISSION bias is set to zero. Alive particles will gradually
+ // fall to earth and disappear.
+ void PlayCyclic( ParticlePlayerID id, const ParticleAttributes& attr, const rmt::Matrix& localMatrix );
+ void PlayCyclic( ParticlePlayerID id, const ParticleAttributes& attr, const rmt::Vector& position );
+
+ // Add a new particle system to the manager and insert it into the scene graph
+ void Add ( const ParticleAttributes& attr, const rmt::Matrix& localMatrix );
+ void Add ( const ParticleAttributes& attr, const rmt::Vector& position );
+
+ // Temporary force render function for debugging only
+ void DebugRender ();
+
+ // Update animations on all managed particle systems
+ void Update ( unsigned int deltaTime);
+
+ // Remove all particle systems from the manager
+ void ClearSystems ();
+
+ protected:
+
+ private:
+
+ // Disable all constructors and the copy assignment operator, the singleton functions
+ // CreateInstance and DestroyInstance handle this for the user
+ ParticleManager();
+ ~ParticleManager();
+ ParticleManager( const ParticleManager& );
+ ParticleManager& operator=( const ParticleManager& );
+
+ // Reorients the transform matrix so that Up is always <0,1,0>
+ // needed for particle system's gravity to always work the right way
+ void ReorientUpAxis( const rmt::Matrix& in, rmt::Matrix* out );
+
+ class ManagedParticleSystem
+ {
+ public:
+ ManagedParticleSystem( tParticleSystemFactory*, tEffectController* );
+ ~ManagedParticleSystem();
+ void Display()const { mpSystem->Display(); }
+ void Update( float deltaTime );
+
+ void Unlock() { mUserID = -1; }
+ bool IsLocked()const { return (mUserID >= 0); }
+ void SetBias( unsigned int bias, float value ){ mpSystem->SetBias( bias, value ); }
+ void SetActive( bool isActive );
+ void Reset();
+ void SetTransform( const rmt::Matrix& );
+ int LastFrameReached() const;
+ ParticlePlayerID GetUserID()const { return mUserID; }
+ void ThrowUpNewParticles() { mThrowUpNewParticles = true; }
+ bool IsActive()const { return mIsActive; }
+ void Lock( ParticlePlayerID id ) { mUserID = id; }
+ void SetVelocity( const rmt::Vector& velocity ) { mpSystem->SetVelocity( velocity ); }
+
+ private:
+ RenderEnums::LayerEnum mLayer;
+ ParticlePlayerID mUserID;
+ bool mIsActive;
+ float mEmissionBias;
+ bool mThrowUpNewParticles;
+ bool mIsInDSG;
+ ParticleSystemDSG* mpSystem;
+ };
+
+ // List of all active particle systems within the manager
+ // Use a list for constant time random access deletions
+ // typedef std::vector< ManagedParticleSystem*, s2alloc<ManagedParticleSystem*> > MPSVector;
+ // typedef std::vector< MPSVector, s2alloc<MPSVector> > MPSVectorVector;
+
+ typedef tPtrDynamicArray< ManagedParticleSystem* > MPSVector;
+ typedef tPtrDynamicArray< MPSVector > MPSVectorVector;
+
+
+ MPSVectorVector mActiveSystems;
+
+ // List of all particle types that are dynamically loaded
+ std::vector< bool, s2alloc<bool> > mIsParticleTypeDynamicallyLoaded;
+
+ // Static instance of the ParticleManager singleton
+ static ParticleManager* spInstance;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline ParticleManager* GetParticleManager()
+{
+ return( ParticleManager::GetInstance() );
+}
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Particles/particlesystemdsg.cpp b/game/code/render/Particles/particlesystemdsg.cpp
new file mode 100644
index 0000000..35c59c6
--- /dev/null
+++ b/game/code/render/Particles/particlesystemdsg.cpp
@@ -0,0 +1,356 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: particlesystemdsg
+//
+// Description: A DSG (InstStatEntityDSG) that contains a particle system
+// for insertion into the DSG tree
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <render/Particles/particlesystemdsg.h>
+#include <p3d/effects/effect.hpp>
+#include <p3d/effects/particlesystem.hpp>
+#include <p3d/utility.hpp>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+
+
+//===========================================================================
+// ParticleSystemDSG::ParticleSystemDSG
+//===========================================================================
+// Description:
+// ParticleSystemDSG constructor
+//
+// Constraints:
+// Constructor can only be called by the ParticleManager
+//
+// Parameters:
+// None
+//
+// Return:
+// None
+// //===========================================================================
+ParticleSystemDSG::ParticleSystemDSG()
+: mpSystem( NULL ),
+mpController( NULL )
+{
+ IEntityDSG::mTranslucent = true;
+}
+//===========================================================================
+// ParticleSystemDSG::~ParticleSystemDSG
+//===========================================================================
+// Description:
+// ParticleSystemDSG destructor
+//
+// Constraints:
+// Destructor can only be called by ParticleManager
+//
+// Parameters:
+// None
+//
+// Return:
+// None
+//
+//===========================================================================
+ParticleSystemDSG::~ParticleSystemDSG()
+{
+BEGIN_PROFILE( "ParticleSystemDSG Destroy" );
+ if( mpController != NULL )
+ {
+ // Releasing the controller will release its attached effect
+ mpController->Release();
+ mpController = NULL;
+ mpSystem = NULL;
+ }
+END_PROFILE( "ParticleSystemDSG Destroy" );
+}
+void ParticleSystemDSG::Init( tParticleSystemFactory* pFactory, tEffectController* pController )
+{
+ mpController = static_cast< tEffectController* >( pController->Clone() );
+ rAssert( mpController != NULL );
+ mpController->AddRef();
+
+ mpSystem = static_cast< tParticleSystem*> ( pFactory->CreateEffect( mpController ) );
+ mpSystem->SetCycleMode(FORCE_CYCLIC);
+ rAssert( mpSystem != NULL );
+
+};
+
+//===========================================================================
+// ParticleSystemDSG::SetBias
+//===========================================================================
+// Description:
+// Sets the contained tParticleSystem to a new bias
+//
+// Constraints:
+// None.
+//
+// Parameters:
+// p3dParticleSystemConstants:: identifier that indicates the bias
+// float b = bias value
+//
+// Return:
+// None.
+//
+//===========================================================================
+void ParticleSystemDSG::SetBias(unsigned bias, float b)
+{
+ mpSystem->SetBias( bias, b );
+}
+
+//===========================================================================
+// ParticleSystemDSG::GetNumLiveParticles
+//===========================================================================
+// Description:
+// Returns the number of active particles
+//
+// Constraints:
+// None.
+//
+// Parameters:
+// None
+//
+// Return:
+// integer with the number of active particles
+//
+//===========================================================================
+int ParticleSystemDSG::GetNumLiveParticles()const
+{
+ return mpSystem->GetNumLiveParticles();
+}
+
+//===========================================================================
+// ParticleSystemDSG::LastFrameReached
+//===========================================================================
+// Description:
+// Whether the effect controller has reached the end of the animation
+//
+// Constraints:
+// None.
+//
+// Parameters:
+// None
+//
+// Return:
+// int
+//
+//===========================================================================
+int ParticleSystemDSG::LastFrameReached()const
+{
+ return mpController->LastFrameReached();
+}
+//===========================================================================
+// ParticleSystemDSG::Display
+//===========================================================================
+// Description:
+// Draws the particle system
+//
+// Constraints:
+// None.
+//
+// Parameters:
+// None
+//
+// Return:
+// None
+//
+//===========================================================================
+void ParticleSystemDSG::Display()
+{
+#ifdef PROFILER_ENABLED
+ char profileName[] = " ParticleSystemDSG Display";
+#endif
+ DSG_BEGIN_PROFILE(profileName)
+ mpSystem->Display();
+ DSG_END_PROFILE(profileName)
+}
+//===========================================================================
+// ParticleSystemDSG::Update
+//===========================================================================
+// Description:
+// Advances effect animation
+//
+// Constraints:
+// None.
+//
+// Parameters:
+// Time to advance effect in milliseconds
+//
+// Return:
+// None
+//
+//===========================================================================
+
+void ParticleSystemDSG::DisplayBoundingBox( tColour colour )
+{
+#ifndef RAD_RELEASE
+ rmt::Box3D mBBox;
+ GetBoundingBox( &mBBox );
+ pddiPrimStream* stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.low.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.low.z);
+ p3d::pddi->EndPrims(stream);
+
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.low.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.high.z);
+ p3d::pddi->EndPrims(stream);
+
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.low.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.low.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.high.x, mBBox.high.y, mBBox.high.z);
+ p3d::pddi->EndPrims(stream);
+
+ stream = p3d::pddi->BeginPrims(NULL, PDDI_PRIM_LINESTRIP, PDDI_V_C, 5);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.high.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.low.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.low.z);
+ stream->Colour(colour);
+ stream->Coord(mBBox.low.x, mBBox.high.y, mBBox.high.z);
+ p3d::pddi->EndPrims(stream);
+#endif
+}
+//===========================================================================
+// ParticleSystemDSG::Update
+//===========================================================================
+// Description:
+// Advances animations and moves all particles in the world
+//
+// Constraints:
+// None.
+//
+// Parameters:
+// Time elapsed in milliseconds
+//
+// Return:
+// None
+//
+//===========================================================================
+void ParticleSystemDSG::Update( float deltaTime )
+{
+ mpController->Advance( deltaTime );
+ rmt::Matrix ident;
+ ident.Identity();
+ mpSystem->Update( &ident );
+}
+//===========================================================================
+// ParticleSystemDSG::SetTransform
+//===========================================================================
+// Description:
+// Sets the transform matrix
+//
+// Constraints:
+// None.
+//
+// Parameters:
+// Transformation matrix
+//
+// Return:
+// None
+//
+//===========================================================================
+void ParticleSystemDSG::SetTransform( const rmt::Matrix& transform )
+{
+ mpSystem->SetMatrix( transform );
+ mPosition = transform.Row(3);
+}
+//===========================================================================
+// ParticleSystemDSG::Reset
+//===========================================================================
+// Description:
+// Resets the frame controller
+//
+// Constraints:
+// None.
+//
+// Parameters:
+// None
+//
+// Return:
+// None
+//
+//===========================================================================
+void ParticleSystemDSG::Reset()
+{
+ mpController->Reset();
+}
+
+void ParticleSystemDSG::GetPosition( rmt::Vector* ipPosn )
+{
+ //mpSystem->GetPosition( ipPosn );
+ *ipPosn = mPosition;
+}
+const rmt::Vector& ParticleSystemDSG::rPosition()
+{
+ return mPosition;//mpSystem->GetMatrix().Row( 3 );
+}
+void ParticleSystemDSG::GetBoundingBox( rmt::Box3D* box )
+{
+ // Bounding Boxes not overloaded for tParticleSystems!!!!!!!!
+ // Use a BB that is 2 m^3
+ rmt::Vector position = rPosition();
+ rmt::Vector low = position - rmt::Vector( 1.0f, 1.0f, 1.0f );
+ rmt::Vector high = position + rmt::Vector( 1.0f, 1.0f, 1.0f );
+ box->Set( low, high );
+
+}
+void ParticleSystemDSG::GetBoundingSphere( rmt::Sphere* sphere )
+{
+ sphere->centre = rPosition();
+ sphere->radius = 1.0f;
+}
+
+void ParticleSystemDSG::SetVelocity( const rmt::Vector& velocity )
+{
+ rAssert( mpSystem != NULL );
+ mpSystem->SetVelocity( velocity );
+}
+
+
diff --git a/game/code/render/Particles/particlesystemdsg.h b/game/code/render/Particles/particlesystemdsg.h
new file mode 100644
index 0000000..dd4762f
--- /dev/null
+++ b/game/code/render/Particles/particlesystemdsg.h
@@ -0,0 +1,99 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: particlesystemdsg
+//
+// Description: A DSG (InstStatEntityDSG) that contains a particle system
+// for insertion into the DSG tree
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef PARTICLESYSTEMDSG_H
+#define PARTICLESYSTEMDSG_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <render/dsg/inststatentitydsg.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class tParticleSystem;
+class tParticleSystemFactory;
+class tEffectController;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+// Describe here what you require of the client which uses or
+// has this class - essentially, the converse of the description
+// above. A constraint is an expression of some semantic
+// condition that must be preserved, an invariant of a class or
+// relationship that must be preserved while the system is in a
+// steady state.
+//
+//===========================================================================
+class ParticleSystemDSG : public InstStatEntityDSG
+{
+ public:
+ ParticleSystemDSG();
+ ~ParticleSystemDSG();
+
+ virtual void Display();
+ virtual void DisplayBoundingBox( tColour colour );
+ virtual void GetBoundingBox( rmt::Box3D* box );
+ virtual void GetBoundingSphere( rmt::Sphere* sphere );
+
+ virtual rmt::Vector* pPosition(){ rAssert( false ); return NULL; }
+ virtual const rmt::Vector& rPosition();
+ virtual void GetPosition( rmt::Vector* ipPosn );
+
+ void Init( tParticleSystemFactory* pFactory, tEffectController* pController);
+ void SetVelocity( const rmt::Vector& velocity );
+
+ void SetBias(unsigned bias, float b);
+ int GetNumLiveParticles()const;
+ int LastFrameReached()const;
+ void SetTransform( const rmt::Matrix& transform );
+ // Reset animation to start
+ void Reset();
+ void Update( float deltaTime );
+
+
+ protected:
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow ParticleSystemDSG from being copied and assigned.
+ ParticleSystemDSG( const ParticleSystemDSG& );
+ ParticleSystemDSG& operator=( const ParticleSystemDSG& );
+
+ rmt::Vector mPosition;
+ tParticleSystem* mpSystem;
+ tEffectController* mpController;
+};
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/render/Particles/vehicleparticleemitter.cpp b/game/code/render/Particles/vehicleparticleemitter.cpp
new file mode 100644
index 0000000..a12b9e1
--- /dev/null
+++ b/game/code/render/Particles/vehicleparticleemitter.cpp
@@ -0,0 +1,306 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: Name of subsystem or component this class belongs to.
+//
+// Description: This file contains the implementation of...
+//
+// Authors: Name Here
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <render\Particles\vehicleparticleemitter.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+
+//===========================================================================
+// VehicleParticleEmitter::PartEmitter::PartEmitter
+//===========================================================================
+// Description:
+// PartEmitter ctor
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+VehicleParticleEmitter::PartEmitter::PartEmitter( const rmt::Vector& location)
+: mLocation( location )
+{
+ mParticlePlayers.reserve( 10 );
+
+}
+//===========================================================================
+// VehicleParticleEmitter::PartEmitter::~PartEmitter
+//===========================================================================
+// Description:
+// PartEmitter dtor
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+VehicleParticleEmitter::PartEmitter::~PartEmitter()
+{
+
+}
+//===========================================================================
+// VehicleParticleEmitter::PartEmitter::AddParticleType
+//===========================================================================
+// Description:
+// Adds a new particle effect that can be emittted from this Vehicle Part
+//
+// Constraints:
+//
+// Parameters:
+// ParticleEnum::ParticleID indicating the type of particle effects
+// that can be emitted by this engine type
+//
+// Return:
+// None.
+//===========================================================================
+void VehicleParticleEmitter::PartEmitter::AddParticleType( ParticleEnum::ParticleID type )
+{
+ mParticlePlayers.insert( type, ParticleManager::GetInstance()->GetUniqueID() );
+}
+//===========================================================================
+// VehicleParticleEmitter::SetPartLocation
+//===========================================================================
+// Description:
+// Sets the location of the vehicle part relative to the vehicle's origin
+//
+// Constraints:
+// No orientation with this function
+//
+// Parameters:
+// VehiclePartEnum - indicating which part we are setting the location offset of
+// partOffset - translation vector indicating offset from the vehicle origin
+//
+// Return:
+// None.
+//===========================================================================
+void VehicleParticleEmitter::SetPartLocation( VehiclePartEnum part,
+ const rmt::Vector& partOffset )
+{
+ switch( part )
+ {
+ case eEngine:
+ mEngineEmitter.SetLocation( partOffset );
+ break;
+ case eLeftBackTire:
+ mLeftBackTireEmitter.SetLocation( partOffset );
+ break;
+ case eRightBackTire:
+ mRightBackTireEmitter.SetLocation( partOffset );
+ break;
+ case eLeftTailPipe:
+ mLeftTailPipeEmitter.SetLocation( partOffset );
+ break;
+ case eRightTailPipe:
+ mRightTailPipeEmitter.SetLocation( partOffset );
+ break;
+ default:
+ // Unhandled case!
+ rAssert(0);
+ break;
+ }
+
+}
+//===========================================================================
+// VehicleParticleEmitter::SetPartLocation
+//===========================================================================
+// Description:
+// Sets the location of the vehicle part relative to the vehicle's origin
+//
+// Constraints:
+// No orientation with this function
+//
+// Parameters:
+// VehiclePartEnum - indicating which part we are setting the location offset of
+// partOffset - translation vector indicating offset from the vehicle origin
+//
+// Return:
+// None.
+//===========================================================================
+void VehicleParticleEmitter::PartEmitter::SetLocation( const rmt::Vector& location )
+{
+ mLocation = location;
+}
+
+//===========================================================================
+// VehicleParticleEmitter::PartEmitter::Generate
+//===========================================================================
+// Description:
+// Tells this part of the vehicle to emit particles
+//
+// Constraints:
+//
+// Parameters:
+// ParticleAttributes struct indicating the particle effect that we want this vehicle part to emit
+// Matrix indicating the vehicle's location in the world
+//
+// Return:
+//
+//===========================================================================
+void VehicleParticleEmitter::PartEmitter::Generate( const ParticleAttributes& attr, const rmt::Matrix& vehicleMatrix )
+{
+ Map< ParticleEnum::ParticleID, ParticlePlayerID >::iterator it = mParticlePlayers.find( attr.mType );
+
+ rAssert( it != mParticlePlayers.end() );
+
+ // Translate particle generation point form the origin to the parts location
+ rmt::Matrix offsetLocationMatrix;
+ offsetLocationMatrix.Identity();
+ offsetLocationMatrix.FillTranslate( mLocation );
+
+ rmt::Matrix partMatrix;
+ partMatrix.Mult( offsetLocationMatrix, vehicleMatrix);
+
+ ParticleManager::GetInstance()->PlayCyclic( it->second, attr, partMatrix );
+}
+
+
+//===========================================================================
+// VehicleParticleEmitter::VehicleParticleEmitter
+//===========================================================================
+// Description:
+// VehicleParticleEmitter ctor
+// Initializes all the emitters and instructs them what type of particles that they
+// can allocate.
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+VehicleParticleEmitter::VehicleParticleEmitter()
+:
+ mEngineEmitter( rmt::Vector( 0,1,0 ) ),
+ mLeftBackTireEmitter( rmt::Vector(-0.8f, 0.5f, -1.5f) ),
+ mRightBackTireEmitter( rmt::Vector(0.8f, 0.5f, -1.5f) ),
+ mLeftTailPipeEmitter( rmt::Vector( -0.8f, 0, -1.5f ) ),
+ mRightTailPipeEmitter( rmt::Vector( 0.8f, 0, -1.5f ) ),
+ mSpecialEmitter( rmt::Vector( 0, 0 , 0 ) )
+{
+ // Add all the particles that can be emitted by the engine
+ mEngineEmitter.AddParticleType( ParticleEnum::eEngineSmokeLight );
+ mEngineEmitter.AddParticleType( ParticleEnum::eEngineSmokeHeavy );
+ mEngineEmitter.AddParticleType( ParticleEnum::eEngineSmokeMedium );
+
+ // Add all the particles that can be emitted by the tires
+ mLeftBackTireEmitter.AddParticleType( ParticleEnum::eGrassSpray );
+ mLeftBackTireEmitter.AddParticleType( ParticleEnum::eDirtSpray );
+ mLeftBackTireEmitter.AddParticleType( ParticleEnum::eWaterSpray );
+ mLeftBackTireEmitter.AddParticleType( ParticleEnum::eSmokeSpray );
+ mLeftBackTireEmitter.AddParticleType( ParticleEnum::eFireSpray );
+
+ mRightBackTireEmitter.AddParticleType( ParticleEnum::eGrassSpray );
+ mRightBackTireEmitter.AddParticleType( ParticleEnum::eDirtSpray );
+ mRightBackTireEmitter.AddParticleType( ParticleEnum::eWaterSpray );
+ mRightBackTireEmitter.AddParticleType( ParticleEnum::eSmokeSpray );
+ mRightBackTireEmitter.AddParticleType( ParticleEnum::eFireSpray );
+
+ // Add all the particles that can be emitted by the special effects emitter
+ mSpecialEmitter.AddParticleType( ParticleEnum::eFrinksCarSpecialEffect );
+}
+//===========================================================================
+// VehicleParticleEmitter::~VehicleParticleEmitter
+//===========================================================================
+// Description:
+// VehicleParticleEmitter dtor
+//
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+VehicleParticleEmitter::~VehicleParticleEmitter()
+{
+
+}
+//===========================================================================
+// VehicleParticleEmitter::Generate
+//===========================================================================
+// Description:
+// Generates particles from the specified vehicle bodypart of the specified type
+//
+// Constraints:
+// Can't generate just any particle type, it has to be assigned to that bodypart
+//
+// Parameters:
+// VehicleParts enumeration indicating which part of the vehicle is making the particles
+// ParticleAttributes with particle type and emission bias
+// Matrix indicating the base vehicle position / orientation
+//
+// Return:
+//
+// None.
+//
+//===========================================================================
+
+void VehicleParticleEmitter::Generate( VehiclePartEnum part,
+ const ParticleAttributes& attr,
+ const rmt::Matrix& vehicleTransform )
+{
+ switch( part )
+ {
+
+ case eLeftBackTire:
+ mLeftBackTireEmitter.Generate( attr, vehicleTransform );
+ break;
+
+ case eRightBackTire:
+ mRightBackTireEmitter.Generate( attr, vehicleTransform );
+ break;
+
+ case eLeftTailPipe:
+ mLeftTailPipeEmitter.Generate( attr, vehicleTransform );
+ break;
+
+ case eRightTailPipe:
+ mRightTailPipeEmitter.Generate( attr, vehicleTransform );
+ break;
+
+ case eSpecialEmitter:
+ mSpecialEmitter.Generate( attr, vehicleTransform );
+ break;
+
+ case eEngine:
+ mEngineEmitter.Generate( attr, vehicleTransform );
+ break;
+ default:
+ // Unhandled case
+ rAssert( false );
+ break;
+ };
+}
diff --git a/game/code/render/Particles/vehicleparticleemitter.h b/game/code/render/Particles/vehicleparticleemitter.h
new file mode 100644
index 0000000..fedcc07
--- /dev/null
+++ b/game/code/render/Particles/vehicleparticleemitter.h
@@ -0,0 +1,130 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: vehicleparticleemitter
+//
+// Description: An encapsulation of all the particle effects that are generated
+// by a vehicle in Simpsons2
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef VEHICLEPARTICLEEMITTER_H
+#define VEHICLEPARTICLEEMITTER_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <render/particles/particlemanager.h>
+#include <radmath/radmath.hpp>
+#include <radmath/matrix.hpp>
+#include <memory/map.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Bundles a large number of particle emitters together in a logical
+// interface for a single Vehicle. Each Vehicle that has an VehicleParticleEmitter
+// object can easily generate all the
+//
+// Constraints:
+// Requires the particle manager to be instanciated before any particles
+// can be generated.
+//
+// The following types of particles can be emitted
+//
+// Engine - ParticleEnum::eEngineSmokeHeavy
+// ParticleEnum::eEngineSmokeLight
+// ParticleEnum::eEngineSmokeMedium
+//
+// Tires - ParticleEnum::eGrassSpray
+// ParticleEnum::eSmokeSpray
+// ParticleEnum::eWaterSpray
+// ParticleEnum::eDirtSpray
+//
+// Tailpipes - none
+// SpecialFX - none
+//
+// More will appear here as they are made
+//===========================================================================
+
+class VehicleParticleEmitter
+{
+ public:
+ VehicleParticleEmitter();
+ ~VehicleParticleEmitter();
+
+ enum VehiclePartEnum
+ {
+ eEngine,
+ eLeftBackTire,
+ eRightBackTire,
+ eLeftTailPipe,
+ eRightTailPipe,
+ eSpecialEmitter
+ };
+
+ // Plays the given particle system (EMISSION set to 1) for the given vehicle part
+ // and the given particle type
+ void Generate( VehiclePartEnum partEnum,
+ const ParticleAttributes& attr,
+ const rmt::Matrix& vehicleTransform );
+
+ // sets the location of this part relative to the vehicle's origin
+ void SetPartLocation( VehiclePartEnum part, const rmt::Vector& partOffset );
+
+ protected:
+
+ private:
+ // An emitter that encapsulates all the types of particles that
+ // can come from a certain part of the vehicle (tailpipe, engine, etc).
+ class PartEmitter
+ {
+ public:
+ PartEmitter( const rmt::Vector& location );
+ ~PartEmitter();
+
+ void AddParticleType( ParticleEnum::ParticleID type );
+ void SetLocation( const rmt::Vector& location );
+
+ void Generate( const ParticleAttributes& attr, const rmt::Matrix& localMatrix );
+
+ private:
+
+ Map < ParticleEnum::ParticleID, ParticlePlayerID > mParticlePlayers;
+ rmt::Vector mLocation;
+ };
+ PartEmitter mEngineEmitter;
+ PartEmitter mLeftBackTireEmitter;
+ PartEmitter mRightBackTireEmitter;
+ PartEmitter mLeftTailPipeEmitter;
+ PartEmitter mRightTailPipeEmitter;
+ PartEmitter mSpecialEmitter;
+
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow VehicleParticleEmitter from being copied and assigned.
+ VehicleParticleEmitter( const VehicleParticleEmitter& );
+ VehicleParticleEmitter& operator=( const VehicleParticleEmitter& );
+
+};
+
+
+#endif
+
+
+
diff --git a/game/code/render/RenderFlow/allrenderflow.cpp b/game/code/render/RenderFlow/allrenderflow.cpp
new file mode 100644
index 0000000..bbe7f86
--- /dev/null
+++ b/game/code/render/RenderFlow/allrenderflow.cpp
@@ -0,0 +1 @@
+#include <render/RenderFlow/renderflow.cpp>
diff --git a/game/code/render/RenderFlow/renderflow.cpp b/game/code/render/RenderFlow/renderflow.cpp
new file mode 100644
index 0000000..f3a19a7
--- /dev/null
+++ b/game/code/render/RenderFlow/renderflow.cpp
@@ -0,0 +1,384 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: renderflow.cpp
+//
+// Description: Implementation for RenderFlow class.
+//
+// History: + Stolen and cleaned up from Penthouse -- Darwin Chau
+// + Stolen from Darwin and Tailored to RenderFlow from GameFlow -- Devin [4/17/2002]
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/billboardobject.hpp>
+#include <p3d/utility.hpp>
+#include <pddi/pddiext.hpp>
+#include <raddebug.hpp>
+#include <radtime.hpp>
+#include <raddebugwatch.hpp>
+#include <p3d/effects/particleutility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/renderflow/renderflow.h>
+#include <main/game.h>
+#include <events/eventmanager.h>
+
+//////////////////////////////////////////////////////////////////////////
+#include <render/particles/particlemanager.h>
+#include <render/breakables/breakablesmanager.h>
+#include <render/animentitydsgmanager/animentitydsgmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Static pointer to instance of this singleton.
+//
+RenderFlow* RenderFlow::spInstance = NULL;
+
+bool RenderFlow::sDrawStatsOverlay = false;
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// RenderFlow::CreateInstance
+//==============================================================================
+//
+// Description: Create the RenderFlow controller if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the created RenderFlow controller.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+RenderFlow* RenderFlow::CreateInstance()
+{
+ rAssert( spInstance == NULL );
+ spInstance = new(GMA_PERSISTENT) RenderFlow();
+
+ HeapMgr()->PushHeap(GMA_PERSISTENT);
+ BillboardQuadManager::CreateInstance();
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+
+ return spInstance;
+}
+
+//==============================================================================
+// RenderFlow::GetInstance
+//==============================================================================
+//
+// Description: Get the RenderFlow controller if exists.
+//
+// Parameters: None.
+//
+// Return: Pointer to the created RenderFlow controller.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+RenderFlow* RenderFlow::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// RenderFlow::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the RenderFlow controller.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void RenderFlow::DestroyInstance()
+{
+ //
+ // Make sure this doesn't get called twice.
+ //
+ rAssert( spInstance != NULL );
+ delete spInstance;
+ spInstance = NULL;
+}
+
+//========================================================================
+// RenderFlow::DoAllRegistration
+//========================================================================
+//
+// Description: Register with whatever external dependancies/couplings
+// RenderFlow has
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderFlow::DoAllRegistration()
+{
+#ifdef RAD_DEBUG
+ sDrawStatsOverlay = false;
+#else
+ sDrawStatsOverlay = false;
+#endif
+
+ if ( CommandLineOptions::Get( CLO_FPS ) )
+ {
+ sDrawStatsOverlay = true;
+ }
+
+ p3d::pddi->EnableStatsOverlay(sDrawStatsOverlay);
+
+#ifndef RAD_PS2
+ p3d::pddi->SetCullMode(PDDI_CULL_NONE);
+#endif
+
+#ifdef RAD_PS2
+ pddiExtPS2Control* ps2Control = (pddiExtPS2Control*)p3d::pddi->GetExtension(PDDI_EXT_PS2_CONTROL);
+
+ ps2Control->DisableTexCache( false );
+
+// pddiExtPS2Control* ps2Control = (pddiExtPS2Control*) p3d::pddi->GetExtension(PDDI_EXT_PS2_CONTROL);
+// ps2Control->ForceMFIFOSync( true );
+#endif
+
+#if (defined(RAD_XBOX))
+ ((pddiExtGammaControl*)p3d::pddi->GetExtension(PDDI_EXT_GAMMACONTROL))->SetGamma(0.956f,0.914f,0.866f);
+#endif
+#if (defined(RAD_XBOX) && defined(DEBUGWATCH))
+ mpDebugXBoxGamma = (pddiExtGammaControl*)p3d::pddi->GetExtension(PDDI_EXT_GAMMACONTROL);
+ mpDebugXBoxGamma->GetGamma( &mDebugGammaR, &mDebugGammaG, &mDebugGammaB );
+#endif
+
+#ifdef RAD_WIN32
+ mpGammaControl = (pddiExtGammaControl*)p3d::pddi->GetExtension(PDDI_EXT_GAMMACONTROL);
+
+ float r,g,b;
+ mpGammaControl->GetGamma( &r, &g, &b );
+ mpGammaControl->SetGamma( r, r, r ); // We will only deal with one degree.
+
+ mGamma = r;
+#endif
+ ParticleSystemRandomData::SetUp();
+
+ GetEventManager()->AddListener(GetRenderManager(),(EventEnum)(EVENT_LOCATOR+LocatorEvent::DYNAMIC_ZONE));
+ GetEventManager()->AddListener(GetRenderManager(), EVENT_FIRST_DYNAMIC_ZONE_START );
+ GetEventManager()->AddListener(GetRenderManager(), EVENT_ALL_DYNAMIC_ZONES_DUMPED );
+ GetEventManager()->AddListener(GetRenderManager(),(EventEnum)(EVENT_LOCATOR+LocatorEvent::OCCLUSION_ZONE));
+ GetEventManager()->AddListener(GetRenderManager(), static_cast<EventEnum>( EVENT_LOCATOR + LocatorEvent::LIGHT_CHANGE ) );
+ GetEventManager()->AddListener(GetRenderManager(), EVENT_MISSION_RESET );
+}
+
+
+//==============================================================================
+// RenderFlow::OnTimerDone
+//==============================================================================
+//
+// Description: This routine is invoked to run the game. It gets called by the
+// dispatcher once per frame.
+//
+// Parameters: elapsedtime - time it actually took for timer to expire
+// pUserData - custom user data
+//
+// Return: None.
+//
+//==============================================================================
+void RenderFlow::OnTimerDone( unsigned int iElapsedTime, void* pUserData )
+{
+ //////////////////////////////////////////////////
+ // Debugging stuff.
+ //////////////////////////////////////////////////
+
+#if (defined(RAD_XBOX) && defined(DEBUGWATCH))
+ if(mpDebugXBoxGamma != NULL)
+ mpDebugXBoxGamma->SetGamma( mDebugGammaR, mDebugGammaG, mDebugGammaB );
+#endif
+ #ifndef RAD_RELEASE
+
+ // HACK to prevent iElapsedTime from being ridiculously huge.
+ // This is so that when we set breakpoints we don't have really huge
+ // elapsedtime values screwing us up.
+ if( iElapsedTime > 1000 )
+ {
+ iElapsedTime = 20;
+ }
+
+ #endif
+BEGIN_PROFILE("RenderFlow");
+
+#ifdef DEBUGWATCH
+ unsigned int t0 = radTimeGetMicroseconds();
+#endif
+ mpRenderManager->ContextUpdate( iElapsedTime );
+#ifdef DEBUGWATCH
+ mDebugRenderTime = radTimeGetMicroseconds() - t0;
+
+ if( p3d::pddi->IsStatsOverlayEnabled() != sDrawStatsOverlay )
+ {
+ p3d::pddi->EnableStatsOverlay( sDrawStatsOverlay );
+ }
+#endif
+END_PROFILE("RenderFlow");
+
+}
+
+#ifdef RAD_WIN32
+//==============================================================================
+// RenderFlow::SetGamma
+//==============================================================================
+//
+// Description: Sets the gamma ramp for the game.
+//
+// Parameters: gamma - one dimensional gamma value to set for r,g, and b.
+//
+// Return: None.
+//
+//==============================================================================
+
+void RenderFlow::SetGamma( float gamma )
+{
+ if( mpGammaControl == NULL )
+ {
+ return;
+ }
+
+ if( gamma < 0 )
+ {
+ gamma = 0;
+ }
+
+ mGamma = gamma;
+ mpGammaControl->SetGamma( mGamma, mGamma, mGamma );
+}
+
+//==============================================================================
+// RenderFlow::GetGamma
+//==============================================================================
+//
+// Description: Returns the current gamma for the game
+//
+// Parameters: n/a
+//
+// Return: gamma value - equivalent for r g and b
+//
+//==============================================================================
+
+float RenderFlow::GetGamma() const
+{
+ return mGamma;
+}
+
+#endif //rad_win32
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//==============================================================================
+// RenderFlow::RenderFlow
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+RenderFlow::RenderFlow() :
+ mpITimer( NULL )
+{
+#ifdef DEBUGWATCH
+ radDbgWatchAddUnsignedInt( &mDebugRenderTime, "Debug Render Flow micros", "RenderFlow", NULL, NULL );
+ radDbgWatchAddBoolean( &sDrawStatsOverlay, "Draw Stats Overlay", "RenderFlow" );
+ radDbgWatchAddFloat( &mDebugGammaR, "Xbox R Gamma", "RenderFlow", NULL, NULL, 0.0f, 2.0f );
+ radDbgWatchAddFloat( &mDebugGammaG, "Xbox G Gamma", "RenderFlow", NULL, NULL, 0.0f, 2.0f );
+ radDbgWatchAddFloat( &mDebugGammaB, "Xbox B Gamma", "RenderFlow", NULL, NULL, 0.0f, 2.0f );
+
+ mpDebugXBoxGamma = NULL;
+#endif
+#ifdef RAD_WIN32
+ mpGammaControl = NULL;
+ mGamma = 0.0f;
+#endif
+ //
+ // Only 1 unique RenderManager/etc should exist for the lifetime of
+ // RenderFlow [4/17/2002]
+ //
+ mpRenderManager = RenderManager::CreateInstance();
+ mpDSGFactory = DSGFactory::CreateInstance();
+ mpLoadWrappers = AllWrappers::CreateInstance();
+ mpIntersectManager= IntersectManager::CreateInstance();
+
+ ParticleManager* pParticleManager = ParticleManager::CreateInstance();
+ rAssert( pParticleManager != NULL );
+
+ BreakablesManager* pBreakablesManager = BreakablesManager::CreateInstance();
+ rAssert( pBreakablesManager != NULL );
+
+ AnimEntityDSGManager* pAnimEntityDSGManager = AnimEntityDSGManager::CreateInstance();
+ rAssert( pAnimEntityDSGManager != NULL );
+
+}
+
+//==============================================================================
+// RenderFlow::~RenderFlow
+//==============================================================================
+//
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+RenderFlow::~RenderFlow()
+{
+ ParticleManager::DestroyInstance();
+ BreakablesManager::DestroyInstance();
+ AnimEntityDSGManager::DestroyInstance();
+
+ //
+ //DebugWatch Stuff
+ //
+#ifdef DEBUGWATCH
+ radDbgWatchDelete(&mDebugRenderTime);
+ radDbgWatchDelete(&sDrawStatsOverlay);
+ radDbgWatchDelete( &mDebugGammaR );
+ radDbgWatchDelete( &mDebugGammaG );
+ radDbgWatchDelete( &mDebugGammaB );
+#endif
+
+ //
+ // Kill lifetime Singletons
+ //
+ RenderManager::DestroyInstance();
+ AllWrappers::DestroyInstance();
+ DSGFactory::DestroyInstance();
+ IntersectManager::DestroyInstance();
+}
+
diff --git a/game/code/render/RenderFlow/renderflow.h b/game/code/render/RenderFlow/renderflow.h
new file mode 100644
index 0000000..af4823a
--- /dev/null
+++ b/game/code/render/RenderFlow/renderflow.h
@@ -0,0 +1,106 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: RenderFlow
+//
+// Description: The RenderFlow Controller orchestrates the overall render
+// loop [4/17/2002].
+//
+// History: + Stolen and cleaned up from Penthouse -- Darwin Chau
+// + Stolen from Darwin and Tailored to RenderFlow from GameFlow -- Devin [4/17/2002]
+//
+//=============================================================================
+
+#ifndef RENDERFLOW_H
+#define RENDERFLOW_H
+
+//========================================
+// System Includes
+//========================================
+#include <radtime.hpp> // IRadTimerCallback
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/RenderManager/RenderManager.h>
+#include <render/Loaders/AllWrappers.h>
+#include <render/DSG/DSGFactory.h>
+#include <render/IntersectManager/IntersectManager.h>
+#include <pddi/pddiext.hpp>
+
+
+//=============================================================================
+//
+// Synopsis: The render "loop"
+//
+//=============================================================================
+class RenderFlow : public IRadTimerCallback
+{
+public:
+
+ // Static Methods (for creating and getting an instance of the game)
+ static RenderFlow* CreateInstance();
+ static RenderFlow* GetInstance();
+ static void DestroyInstance();
+
+ // Establish all persistent couplings; must be called before
+ // Instance can be operable
+ void DoAllRegistration();
+
+ // Implement IRadTimerCallback interface.
+ // This member is called whenever the timer expires.
+ void OnTimerDone( unsigned int iElapsedTime, void* pUserData );
+
+#ifdef RAD_WIN32
+ float GetGamma() const;
+ void SetGamma( float gamma );
+#endif
+
+private:
+
+ // Declared but not defined to prevent copying and assignment.
+ RenderFlow( const RenderFlow& );
+ RenderFlow& operator=( const RenderFlow& );
+
+ // Constructor - these are private to prevent anybody else from
+ // creating me.
+ RenderFlow();
+ virtual ~RenderFlow();
+
+ // This member is called when the gameflow is being initialized.
+ void Initialize();
+
+ // The one and only RenderFlow instance.
+ static RenderFlow* spInstance;
+
+#ifdef DEBUGWATCH
+ unsigned int mDebugRenderTime;
+ pddiExtGammaControl* mpDebugXBoxGamma;
+ float mDebugGammaR, mDebugGammaG, mDebugGammaB;
+#endif
+#ifdef RAD_WIN32
+ pddiExtGammaControl* mpGammaControl;
+ float mGamma;
+#endif
+ static bool sDrawStatsOverlay;
+
+ // Timer for gameflow updates.
+ IRadTimer* mpITimer;
+
+ // Maintain a pointer to the RenderManager; [4/17/2002]
+ // Maintain a ptr to all load wrappers
+ RenderManager* mpRenderManager;
+ AllWrappers* mpLoadWrappers;
+ DSGFactory* mpDSGFactory;
+ IntersectManager* mpIntersectManager;
+};
+
+
+//
+// A little syntactic sugar for getting at this singleton.
+//
+inline RenderFlow* GetRenderFlow() { return( RenderFlow::GetInstance() ); }
+
+
+#endif
diff --git a/game/code/render/RenderManager/FrontEndRenderLayer.cpp b/game/code/render/RenderManager/FrontEndRenderLayer.cpp
new file mode 100644
index 0000000..23ba1df
--- /dev/null
+++ b/game/code/render/RenderManager/FrontEndRenderLayer.cpp
@@ -0,0 +1,319 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: FrontEndRenderLayer.cpp
+//
+// Description: Implementation for FrontEndRenderLayer class.
+//
+// History: + Initial Implementation -- Tony [6/05/2002]
+//
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+#include <p3d/view.hpp>
+#include <p3d/billboardobject.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/RenderManager/FrontEndRenderLayer.h>
+#include <debug/profiler.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/presentation.h>
+#include <gameflow/gameflow.h>
+#include <contexts/contextenum.h>
+#include <contexts/gameplay/gameplaycontext.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/coins/sparkle.h>
+#ifdef RAD_WIN32
+#include <input/inputmanager.h>
+#endif
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : Context Interface
+//
+//************************************************************************
+
+//========================================================================
+// FrontEndFrontEndRenderLayer::FrontEndRenderLayer
+//========================================================================
+//
+// Description: Inits state and variables to represent Dead State
+//
+// Parameters: None
+//
+// Return: None.
+//
+// Constraints:
+//
+//========================================================================
+FrontEndRenderLayer::FrontEndRenderLayer()
+: RenderLayer(),
+ mpScroobyApp( NULL )
+{
+#ifdef DEBUGWATCH
+ radDbgWatchAddUnsignedInt(&mDebugRenderTime, "Render Time", "Front End Render Layer" );
+#endif
+}
+
+//========================================================================
+// FrontEndFrontEndRenderLayer::~FrontEndRenderLayer
+//========================================================================
+//
+// Description: Cleans state and variables to represent Dead State
+//
+// Parameters: None
+//
+// Return: None.
+//
+// Constraints:
+//
+//========================================================================
+FrontEndRenderLayer::~FrontEndRenderLayer()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete(&mDebugRenderTime);
+#endif
+}
+
+void FrontEndRenderLayer::DrawCoinObject()
+{
+ // Render HUD coin effects.
+ if((GetGameFlow()->GetCurrentContext() == CONTEXT_GAMEPLAY ||
+ GetGameFlow()->GetCurrentContext() == CONTEXT_PAUSE) &&
+ !GetPresentationManager()->IsBusy())
+ {
+ GetCoinManager()->HUDRender();
+ GetSparkleManager()->HUDRender();
+ //??? GetHitnRunManager()->HUDRender();
+ }
+ else
+ {
+ GetCoinManager()->ClearHUDCoins();
+ }
+
+}
+//************************************************************************
+// Render Interface
+//************************************************************************
+//========================================================================
+// FrontEndFrontEndRenderLayer::Render
+//========================================================================
+//
+// Description: Renders all (TODO:visible/DSG) drawables
+//
+// Parameters: None
+//
+// Return: None.
+//
+// Constraints:
+//
+//========================================================================
+void FrontEndRenderLayer::Render()
+{
+ BEGIN_PROFILE( "FE Render" );
+
+#ifdef DEBUGWATCH
+ mDebugRenderTime = radTimeGetMicroseconds();
+#endif
+
+ for( unsigned int view = 0; view < mNumViews; view++ )
+ {
+ mpView[ view ]->BeginRender();
+
+ rAssert(!IsDead());
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+
+ if (!GetCoinManager()->DrawAfterGui())
+ DrawCoinObject();
+ // display Scrooby screen (and updates all Pure3d objects)
+ //
+ mpScroobyApp->DrawFrame( static_cast<float>( g_scroobySimulationTime ) );
+#ifdef RAD_WIN32
+ // Update the frontend cursor.
+ GetInputManager()->GetFEMouse()->Update();
+#endif
+
+ if (GetCoinManager()->DrawAfterGui())
+ DrawCoinObject();
+
+ HeapMgr()->PopHeap ( GMA_TEMP );
+
+// GetBillboardQuadManager()->DisplayAll();
+
+ mpView[ view ]->EndRender();
+ }
+
+#ifdef DEBUGWATCH
+ mDebugRenderTime = radTimeGetMicroseconds()-mDebugRenderTime;
+#endif
+
+ END_PROFILE( "FE Render" );
+}
+
+//************************************************************************
+// Resource Interface
+//************************************************************************
+//////////////////////////////////////////////////////////////////////////
+// Guts; Renderable Type Things
+//////////////////////////////////////////////////////////////////////////
+//========================================================================
+// FrontEndRenderLayer::AddGuts
+//========================================================================
+//
+// Description: Add a tDrawable
+//
+// Parameters: tDrawable to add
+//
+// Return: None.
+//
+// Constraints:
+//
+//========================================================================
+void FrontEndRenderLayer::AddGuts( tDrawable* ipDrawable )
+{
+ //The Basic FrontEndRenderLayer does not support this type
+ rAssert(false);
+}
+
+//========================================================================
+// FrontEndRenderLayer::AddGuts
+//========================================================================
+//
+// Description: Add a tGeometry
+//
+// Parameters: tGeometry to add
+//
+// Return: None.
+//
+// Constraints:
+//
+//========================================================================
+void FrontEndRenderLayer::AddGuts( tGeometry* ipGeometry )
+{
+ //The Basic FrontEndRenderLayer does not support this type
+ rAssert(false);
+}
+
+//========================================================================
+// FrontEndRenderLayer::AddGuts
+//========================================================================
+//
+// Description: Add an IntersectDSG
+//
+// Parameters: IntersectDSG to add
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FrontEndRenderLayer::AddGuts( IntersectDSG* ipIntersectDSG )
+{
+ //The Basic FrontEndRenderLayer does not support this type
+ rAssert(false);
+}
+
+//========================================================================
+// FrontEndRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FrontEndRenderLayer::AddGuts( StaticEntityDSG* ipStaticEntityDSG )
+{
+ //The Basic FrontEndRenderLayer does not support this type
+ rAssert(false);
+}
+
+//========================================================================
+// FrontEndRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FrontEndRenderLayer::AddGuts( StaticPhysDSG* ipStaticPhysDSG )
+{
+ //The Basic FrontEndRenderLayer does not support this type
+ rAssert(false);
+}
+
+//========================================================================
+// FrontEndRenderLayer::AddGuts
+//========================================================================
+//
+// Description: Add the Scrooby App reference
+//
+// Parameters: Scrooby App to add
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FrontEndRenderLayer::AddGuts( Scrooby::App* ipScroobyApp )
+{
+ rAssert( mpScroobyApp == NULL );
+
+ mpScroobyApp = ipScroobyApp;
+}
+
+//========================================================================
+// FrontEndRenderLayer::SetUpGuts
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FrontEndRenderLayer::SetUpGuts()
+{
+ // do nothing
+}
+
+//========================================================================
+// FrontEndRenderLayer::NullifyGuts
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void FrontEndRenderLayer::NullifyGuts()
+{
+ mpScroobyApp = NULL;
+}
diff --git a/game/code/render/RenderManager/FrontEndRenderLayer.h b/game/code/render/RenderManager/FrontEndRenderLayer.h
new file mode 100644
index 0000000..233d185
--- /dev/null
+++ b/game/code/render/RenderManager/FrontEndRenderLayer.h
@@ -0,0 +1,71 @@
+#ifndef __FRONTEND_RENDER_LAYER_H__
+#define __FRONTEND_RENDER_LAYER_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: FrontEndRenderLayer
+//
+// Description:
+//
+// History: + Implemented Initial interfaces -- Tony [6/05/2002]
+//
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/RenderManager/RenderLayer.h>
+#include <raddebugwatch.hpp>
+
+//=================================================
+// TODOs: Re-encapsulate (into other file(s)) the
+// data-sets below:
+//=================================================
+
+//========================================================================
+//
+// Synopsis: The FrontEndRenderLayer
+//
+//========================================================================
+class FrontEndRenderLayer : public RenderLayer
+{
+public:
+ FrontEndRenderLayer();
+ ~FrontEndRenderLayer();
+
+ ///////////////////////////////////////////////////////////////////////
+ // Render Interface
+ ///////////////////////////////////////////////////////////////////////
+ virtual void Render();
+
+ ///////////////////////////////////////////////////////////////////////
+ // Resource Interfaces
+ ///////////////////////////////////////////////////////////////////////
+ // Guts; Renderable Type Things
+ virtual void AddGuts( tDrawable* ipDrawable );
+ virtual void AddGuts( tGeometry* ipGeometry );
+ virtual void AddGuts( IntersectDSG* ipIntersectDSG );
+ virtual void AddGuts( StaticEntityDSG* ipStaticEntityDSG );
+ virtual void AddGuts( StaticPhysDSG* ipStaticPhysDSG );
+ virtual void AddGuts( Scrooby::App* ipScroobyApp );
+ virtual void SetUpGuts();
+ virtual void NullifyGuts();
+
+protected:
+ Scrooby::App* mpScroobyApp;
+
+private:
+ void DrawCoinObject();
+#ifdef DEBUGWATCH
+ unsigned int mDebugRenderTime;
+#endif
+
+};
+
+#endif // __FRONTEND_RENDER_LAYER_H__
diff --git a/game/code/render/RenderManager/RenderLayer.cpp b/game/code/render/RenderManager/RenderLayer.cpp
new file mode 100644
index 0000000..d3cf5cc
--- /dev/null
+++ b/game/code/render/RenderManager/RenderLayer.cpp
@@ -0,0 +1,988 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: RenderLayer.cpp
+//
+// Description: Implementation for RenderManager class.
+//
+// History: + Stolen and cleaned up from Penthouse -- Darwin Chau
+// + Stolen from Darwin and Tailored
+// to RenderFlow from GameFlow -- Devin [4/17/2002]
+// + Stolen from Devin and Tailored
+// to RenderManager from RenderFlow -- Devin [4/17/2002]
+// + Stolen from Devin and Tailored
+// to RenderManager from RenderLayer -- Devin [4/18/2002]
+//
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+#include <p3d/pointcamera.hpp>
+#include <p3d/view.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/RenderManager/RenderLayer.h>
+#include <render/DSG/IntersectDSG.h>
+#include <render/DSG/StaticEntityDSG.h>
+#include <render/DSG/StaticPhysDSG.h>
+#include <render/DSG/FenceEntityDSG.h>
+#include <render/Culling/SpatialTree.h>
+
+#include <memory/srrmemory.h>
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : Context Interface
+//
+//************************************************************************
+
+//========================================================================
+// RenderLayer::RenderLayer
+//========================================================================
+//
+// Description: Inits state and variables to represent Dead State
+//
+// Parameters: None
+//
+// Return: None.
+//
+// Constraints:
+//
+//========================================================================
+RenderLayer::RenderLayer() :
+mIsBeginView( true )
+{
+ mNumViews = 1;
+ OnRenderLayerInit();
+ mExportedState = msDead;
+ mPreviousState = msDead;
+}
+
+//========================================================================
+// RenderLayer::~RenderLayer
+//========================================================================
+//
+// Description: Cleans state and variables to represent Dead State
+//
+// Parameters: None
+//
+// Return: None.
+//
+// Constraints:
+//
+//========================================================================
+RenderLayer::~RenderLayer()
+{
+ if(!IsDead())
+ Kill();
+}
+
+//************************************************************************
+// Render Interface
+//************************************************************************
+//========================================================================
+// RenderLayer::Render
+//========================================================================
+//
+// Description: Renders all (TODO:visible/DSG) drawables
+//
+// Parameters: None
+//
+// Return: None.
+//
+// Constraints:
+//
+//========================================================================
+void RenderLayer::Render()
+{
+ BEGIN_PROFILE( "UNKNOWN Render" );
+ for( unsigned int view = 0; view < mNumViews; view++ )
+ {
+ if( mIsBeginView )
+ {
+ mpView[ view ]->BeginRender();
+ }
+
+ rAssert(!IsDead());
+
+ // if( mExportedState == msRenderReady )
+ // {
+ for(int i = mpGuts.mUseSize-1; i>-1; i-- )
+ {
+ mpGuts[i]->Display();
+ }
+ // }
+
+ if( mIsBeginView )
+ {
+ mpView[ view ]->EndRender();
+ }
+ }
+ END_PROFILE( "UNKNOWN Render" );
+}
+
+//************************************************************************
+// Exported Class/State Manipulators
+//************************************************************************
+//========================================================================
+// RenderLayer::Kill
+//========================================================================
+//
+// Description: ExportedState: RenderReady||Frozen >> Dead;
+// Dump any data and set state to dead
+//
+// Parameters: None
+//
+// Return: None.
+//
+// Constraints:
+//
+//========================================================================
+void RenderLayer::Kill()
+{
+ if( IsDead() )
+ {
+ return;
+ }
+
+ NullifyGuts();
+ NullifyViewCam();
+
+ mExportedState = msDead;
+}
+
+//========================================================================
+// RenderLayer::Resurrect
+//========================================================================
+//
+// Description: ExportedState: Dead >> RenderReady
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::Resurrect()
+{
+ if( !IsDead() )
+ {
+ return;
+ }
+ rAssert(mpView != NULL);
+
+ mExportedState = msRenderReady;
+}
+
+//========================================================================
+// RenderLayer::FreezeCorpse
+//========================================================================
+//
+// Description: Dead >> Frozen
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::FreezeCorpse()
+{
+ if( IsFrozen() )
+ {
+ return;
+ }
+ rAssert(mpView != NULL);
+
+ mExportedState = msFrozen;
+}
+
+//========================================================================
+// RenderLayer::Freeze
+//========================================================================
+//
+// Description: ExportedState: RenderReady >> Frozen; don't render
+//
+// Parameters: None
+//
+// Return: None.
+//
+// Constraints:
+//
+//========================================================================
+void RenderLayer::Freeze()
+{
+ if( !IsRenderReady() )
+ {
+ return;
+ }
+ mExportedState = msFrozen;
+}
+
+/*=============================================================================
+Description: Call to freeze the layer and remember the previous state.
+ Match with a call to Warm().
+=============================================================================*/
+void RenderLayer::Chill( void )
+{
+ mPreviousState = mExportedState;
+ mExportedState = msFrozen;
+}
+/*=============================================================================
+Description: Call to restore the state frozen in Chill. Make sure it is a
+ match to a Chill() call.
+=============================================================================*/
+void RenderLayer::Warm( void )
+{
+ mExportedState = mPreviousState;
+ mPreviousState = msDead;
+}
+//========================================================================
+// RenderLayer::Thaw
+//========================================================================
+//
+// Description: ExportedState: Frozen >> RenderReady
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::Thaw()
+{
+ if( !IsFrozen() )
+ {
+ return;
+ }
+ mExportedState = msRenderReady;
+}
+
+//************************************************************************
+// Resource Interface
+//************************************************************************
+//////////////////////////////////////////////////////////////////////////
+// Guts; Renderable Type Things
+//////////////////////////////////////////////////////////////////////////
+//========================================================================
+// RenderLayer::AddGuts
+//========================================================================
+//
+// Description: Add a tDrawable
+//
+// Parameters: tDrawable to add
+//
+// Return: None.
+//
+// Constraints:
+//
+//========================================================================
+void RenderLayer::AddGuts( tDrawable* ipDrawable )
+{
+ rAssert( ipDrawable != NULL );
+ ipDrawable->AddRef();
+ mpGuts.Add( ipDrawable );
+}
+//========================================================================
+// RenderLayer::AddGuts
+//========================================================================
+//
+// Description: Add a tDrawable
+//
+// Parameters: tDrawable to add
+//
+// Return: None.
+//
+// Constraints:
+//
+//========================================================================
+void RenderLayer::RemoveGuts( tDrawable* ipDrawable )
+{
+ rAssert( ipDrawable != NULL );
+ for(int i=mpGuts.mUseSize-1; i>-1; i--)
+ {
+ if(mpGuts[i] == ipDrawable)
+ {
+ mpGuts.Remove(i);
+ ipDrawable->Release();
+ }
+ }
+}
+
+//========================================================================
+// RenderLayer::AddGuts
+//========================================================================
+//
+// Description: Add a tGeometry
+//
+// Parameters: tGeometry to add
+//
+// Return: None.
+//
+// Constraints:
+//
+//========================================================================
+void RenderLayer::AddGuts( tGeometry* ipGeometry )
+{
+ ipGeometry->AddRef();
+ mpGuts.Add( (tDrawable*&)ipGeometry );
+}
+
+//========================================================================
+// RenderLayer::AddGuts
+//========================================================================
+//
+// Description: Add an IntersectDSG
+//
+// Parameters: IntersectDSG to add
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::AddGuts( IntersectDSG* ipIntersectDSG )
+{
+ //The Basic RenderLayer does not support adding of IntersectDSG's
+ rAssert(false);
+}
+
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::AddGuts( StaticEntityDSG* ipStaticEntityDSG )
+{
+ //The Basic RenderLayer does not support adding of IntersectDSG's
+ rAssert(false);
+}
+
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::AddGuts( StaticPhysDSG* ipStaticPhysDSG )
+{
+ //The Basic RenderLayer does not support adding of IntersectDSG's
+ rAssert(false);
+}
+
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::AddGuts( Scrooby::App* ipScroobyApp )
+{
+ //The Basic RenderLayer does not support adding of Scrooby App
+ rAssert(false);
+}
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::AddGuts( SpatialTree* ipSpatialTree )
+{
+ //The Basic RenderLayer does not support adding of SpatialTree
+ rAssert(false);
+}
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::AddGuts( FenceEntityDSG* ipFenceEntityDSG )
+{
+ //The Basic RenderLayer does not support adding of fence entities
+ rAssert(false);
+}
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::AddGuts( AnimCollisionEntityDSG* ipAnimCollDSG )
+{
+ //The Basic RenderLayer does not support adding of Animated Collision entities
+ rAssert(false);
+}
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::AddGuts( AnimEntityDSG* ipAnimDSG )
+{
+ //The Basic RenderLayer does not support adding of Animated entities
+ rAssert(false);
+}
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::AddGuts( DynaPhysDSG* ipDynaPhysDSG )
+{
+ //The Basic RenderLayer does not support adding of Dyna Phys entities
+ rAssert(false);
+}
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::AddGuts( TriggerVolume* ipTriggerVolume )
+{
+ //The Basic RenderLayer does not support adding of Trigger Volume entities
+ rAssert(false);
+}
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::AddGuts( WorldSphereDSG* ipWorldSphereDSG )
+{
+ //The Basic RenderLayer does not support adding of WorldSphere entities
+ rAssert(false);
+}
+
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::AddGuts( RoadSegment* ipRoadSegment )
+{
+ //The Basic RenderLayer does not support adding of road segment entities
+ rAssert(false);
+}
+
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::AddGuts( PathSegment* ipPathSegment )
+{
+ //The Basic RenderLayer does not support adding of path segment entities
+ rAssert(false);
+}
+
+
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::RemoveGuts( IEntityDSG* ipEDSG )
+{
+ //Only supported in WorldRenderLayer
+ rAssert(false);
+}
+//========================================================================
+// RenderLayer::RemoveGuts
+//========================================================================
+//
+// Description: Removes multicontrollers
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: Assert failure, always. Only works on WorldRenderLayers
+//
+//========================================================================
+
+void RenderLayer::RemoveGuts( tMultiController* ipZoneController )
+{
+ //Only supported in WorldRenderLayer
+ rAssert(false);
+}
+//////////////////////////////////////////////////////////////////////////
+// Other; Other Layer Resources of interest
+//////////////////////////////////////////////////////////////////////////
+
+//========================================================================
+// RenderLayer::pCam
+//========================================================================
+//
+// Description: Get Cam Ptr
+//
+// Parameters: None.
+//
+// Return: *Cam (*tPointCamera)
+//
+// Constraints:
+//
+//========================================================================
+tCamera* RenderLayer::pCam( unsigned int viewIndex )
+{
+ return mpView[ viewIndex ]->GetCamera();
+}
+
+//========================================================================
+// RenderLayer::pView
+//========================================================================
+//
+// Description: Get View Ptr
+//
+// Parameters: None.
+//
+// Return: *View (*tView)
+//
+// Constraints:
+//
+//========================================================================
+tView* RenderLayer::pView( unsigned int viewIndex )
+{
+ return mpView[ viewIndex ];
+}
+
+//========================================================================
+// RenderLayer::rAlpha
+//========================================================================
+//
+// Description: Get Alpha Ref
+//
+// Parameters: None.
+//
+// Return: *Alpha
+//
+// Constraints:
+//
+//========================================================================
+float& RenderLayer::rAlpha()
+{
+ // This is a currently unsupported function
+ rAssert(false);
+
+ return mAlpha;
+}
+
+
+//========================================================================
+// RenderLayer::DoAllSetups()
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::DoAllSetups()
+{
+ SetUpViewCam();
+ SetUpGuts();
+}
+//========================================================================
+// RenderLayer::SetUpViewCam
+//========================================================================
+//
+// Description: Setup View, Cam and alpha to default/initial/non-null
+// values
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::SetUpViewCam()
+{
+MEMTRACK_PUSH_GROUP( "RenderLayer" );
+ for( unsigned int i = 0; i < mNumViews; i++ )
+ {
+ if( mpView[ i ] != NULL )
+ {
+ mpView[ i ]->Release();
+ }
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ mpView[ i ] = new tView();
+ mpView[ i ]->AddRef();
+
+ tPointCamera* pDefaultCam = new tPointCamera;
+ pDefaultCam->AddRef();
+ mpView[ i ]->SetCamera( pDefaultCam );
+ pDefaultCam->Release();
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+
+
+// Senta mpView[ i ]->SetClearColour( tColour(0x60,0x70,0xA0,0x00) );
+// mpView[ i ]->SetClearColour( tColour(0x60,0x70,0xA0,0xff) );
+ mpView[ i ]->SetClearColour( tColour(0x9A,0xC2,0xDE,0xff) );
+ mpView[ i ]->SetClearMask( PDDI_BUFFER_ALL );
+
+ mpView[ i ]->EnableFog(false);
+ }
+
+ switch ( mNumViews )
+ {
+ case 1:
+ {
+ mpView[ 0 ]->SetWindow( 0.0f, 0.0f, 1.0f, 1.0f );
+ break;
+ }
+ case 2:
+ {
+ mpView[ 0 ]->SetWindow( 0.0f, 0.0f, 1.0f, 0.5f );
+ mpView[ 1 ]->SetWindow( 0.0f, 0.5f, 1.0f, 1.0f );
+ break;
+ }
+ default:
+ {
+ rAssertMsg(false, "Only have support for 1 or 2 players right now!");
+ }
+ }
+MEMTRACK_POP_GROUP( "RenderLayer" );
+}
+
+//========================================================================
+// RenderLayer::NullifyViewCam
+//========================================================================
+//
+// Description: Nullify View, Cam and alpha to null values safely
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::NullifyViewCam()
+{
+ for( unsigned int view = 0; view < mNumViews; view++ )
+ {
+ mpView[ view ]->Release();
+ mpView[ view ] = NULL;
+ }
+}
+
+//========================================================================
+// RenderLayer::SetUpGuts()
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::SetUpGuts()
+{
+ rAssert( !mpGuts.IsSetUp() );
+ mpGuts.Allocate( msMaxGuts );
+}
+
+//========================================================================
+// RenderLayer::NullifyGuts()
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::NullifyGuts()
+{
+ if( mExportedState == msDead )
+ {
+ return;
+ }
+
+ for(int i = mpGuts.mUseSize-1; i>-1; i-- )
+ {
+ mpGuts[i]->Release();
+ }
+ //mpGuts.mUseSize = 0;
+ mpGuts.Clear();
+}
+
+//=============================================================================
+// RenderLayer::HasGuts
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tDrawable* guts )
+//
+// Return: bool
+//
+//=============================================================================
+bool RenderLayer::HasGuts( tDrawable* guts )
+{
+ for ( int i = mpGuts.mUseSize-1; i>-1; i-- )
+ {
+ if ( mpGuts[i] == guts )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Load Related interfaces
+//////////////////////////////////////////////////////////////////////////
+void RenderLayer::DoPreStaticLoad(){}
+void RenderLayer::DoPostStaticLoad(){}
+void RenderLayer::DumpAllDynaLoads(unsigned int count, SwapArray<tRefCounted*>& irEntityDeletionList ){}
+void RenderLayer::DumpDynaLoad(tName& irGiveItAFuckinName, SwapArray<tRefCounted*>& irEntityDeletionList){}
+bool RenderLayer::DoPreDynaLoad(tName& irGiveItAFuckinName){ return false; }
+void RenderLayer::DoPostDynaLoad(){}
+
+//************************************************************************
+// Exported Class/State
+//************************************************************************
+//========================================================================
+// RenderLayer::IsDead
+//========================================================================
+//
+// Description: Is this layer dead?
+//
+// Parameters: None.
+//
+// Return: bool Is this layer dead?
+//
+// Constraints:
+//
+//========================================================================
+bool RenderLayer::IsDead()
+{
+ return( mExportedState == msDead );
+}
+
+//========================================================================
+// RenderLayer::IsDead
+//========================================================================
+//
+// Description: Is this layer dead?
+//
+// Parameters: None.
+//
+// Return: bool Is this layer dead?
+//
+// Constraints:
+//
+//========================================================================
+bool RenderLayer::IsFrozen()
+{
+ return( mExportedState == msFrozen );
+}
+//========================================================================
+// RenderLayer::IsDead
+//========================================================================
+//
+// Description: Is this layer dead?
+//
+// Parameters: None.
+//
+// Return: bool Is this layer dead?
+//
+// Constraints:
+//
+//========================================================================
+bool RenderLayer::IsRenderReady()
+{
+ return( mExportedState == msRenderReady );
+}
+
+
+//************************************************************************
+// Protected
+//************************************************************************
+//========================================================================
+// RenderLayer::IsGutsSetup
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: IsGutsSetup
+//
+// Constraints: None.
+//
+//========================================================================
+bool RenderLayer::IsGutsSetup()
+{
+ return( mpGuts.IsSetUp() );
+}
+
+//========================================================================
+// RenderLayer::IsViewCamSetup
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: IsViewCamSetup
+//
+// Constraints: None.
+//
+//========================================================================
+bool RenderLayer::IsViewCamSetup( unsigned int viewIndex )
+{
+ return( mpView[ viewIndex ] != NULL );
+}
+
+//========================================================================
+// RenderLayer::OnRenderLayerInit()
+//========================================================================
+//
+// Description: Called on & only on construction
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderLayer::OnRenderLayerInit()
+{
+ for( int view = 0; view < MAX_PLAYERS; view++ )
+ {
+ mpView[view] = NULL;
+ }
+
+ mAlpha = 1.0f;
+
+ mExportedState = msDead;
+}
diff --git a/game/code/render/RenderManager/RenderLayer.h b/game/code/render/RenderManager/RenderLayer.h
new file mode 100644
index 0000000..421ff24
--- /dev/null
+++ b/game/code/render/RenderManager/RenderLayer.h
@@ -0,0 +1,182 @@
+#ifndef __RENDER_LAYER_H__
+#define __RENDER_LAYER_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: RenderLayer
+//
+// Description: The RenderLayer Model provides all interfaces
+// neccessary for interaction with the render resources
+// associated with a given Layer (to be rendered in a sorted
+// order and composited in said order)
+//
+// History: + Implemented Initial interfaces -- Devin [4/18/2002]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/Culling/UseArray.h>
+#include <render/Culling/SwapArray.h>
+#include <constants/maxplayers.h>
+//#include <render/DSG/IntersectDSG.h>
+//#include <render/DSG/StaticEntityDSG.h>
+//#include <render/DSG/StaticPhysDSG.h>
+//#include <render/Culling/SpatialTree.h>
+
+class IEntityDSG;
+class IntersectDSG;
+class StaticEntityDSG;
+class StaticPhysDSG;
+class SpatialTree;
+class FenceEntityDSG;
+class AnimCollisionEntityDSG;
+class AnimEntityDSG;
+class DynaPhysDSG;
+class TriggerVolume;
+class WorldSphereDSG;
+class RoadSegment;
+class PathSegment;
+class tCamera;
+class tDrawable;
+class tGeometry;
+class tMultiController;
+class tName;
+class tView;
+
+namespace Scrooby
+{
+ class App;
+}
+
+//=================================================
+// TODOs: Re-encapsulate (into other file(s)) the
+// data-sets below:
+//=================================================
+
+//========================================================================
+//
+// Synopsis: The RenderLayer ;
+// -A simple datastructure for collecting a set of Layer
+// resources in a single container for ease of
+// management --Devin [4/18/2002]
+//
+//========================================================================
+class RenderLayer
+{
+public:
+ RenderLayer();
+ ~RenderLayer();
+
+ ///////////////////////////////////////////////////////////////////////
+ // Render Interface
+ ///////////////////////////////////////////////////////////////////////
+ virtual void Render();
+
+ ///////////////////////////////////////////////////////////////////////
+ // Resource Interfaces
+ ///////////////////////////////////////////////////////////////////////
+ // Setup all components to their default initial states
+ void DoAllSetups();
+ // Guts; Renderable Type Things
+ virtual void AddGuts( tDrawable* ipDrawable );
+ virtual void AddGuts( tGeometry* ipGeometry );
+ virtual void AddGuts( IntersectDSG* ipIntersectDSG );
+ virtual void AddGuts( StaticEntityDSG* ipStaticEntityDSG );
+ virtual void AddGuts( StaticPhysDSG* ipStaticPhysDSG );
+ virtual void AddGuts( FenceEntityDSG* ipFenceEntityDSG );
+ virtual void AddGuts( Scrooby::App* ipScroobyApp );
+ virtual void AddGuts( SpatialTree* ipSpatialTree );
+ virtual void AddGuts( AnimCollisionEntityDSG* ipAnimCollDSG );
+ virtual void AddGuts( AnimEntityDSG* ipAnimDSG );
+ virtual void AddGuts( DynaPhysDSG* ipDynaPhysDSG );
+ virtual void AddGuts( TriggerVolume* ipTriggerVolume );
+ virtual void AddGuts( WorldSphereDSG* ipWorldSphere );
+ virtual void AddGuts( RoadSegment* ipRoadSegment );
+ virtual void AddGuts( PathSegment* ipPathSegment );
+ virtual void RemoveGuts( tDrawable* ipDrawable );
+ virtual void RemoveGuts( IEntityDSG* ipEDSG );
+ virtual void RemoveGuts( tMultiController* ipZoneController );
+
+ virtual void SetUpGuts();
+ virtual void NullifyGuts();
+
+ bool HasGuts( tDrawable* guts );
+
+ // Other; Other Layer Resources of interest
+ // NOTE: if you want the Cam, you can & should get it through the
+ // SuperCam system, unless you're Cary.
+ tCamera* pCam( unsigned int viewIndex );
+ tView* pView( unsigned int viewIndex );
+ float& rAlpha();
+ void SetUpViewCam();
+ void NullifyViewCam();
+ // Load Related interfaces
+ virtual void DoPreStaticLoad();
+ virtual void DoPostStaticLoad();
+ virtual void DumpAllDynaLoads(unsigned int start, SwapArray<tRefCounted*>& irEntityDeletionList);
+ virtual void DumpDynaLoad(tName& irGiveItAFuckinName, SwapArray<tRefCounted*>& irEntityDeletionList);
+ virtual bool DoPreDynaLoad(tName& irGiveItAFuckinName);
+ virtual void DoPostDynaLoad();
+
+ ///////////////////////////////////////////////////////////////////////
+ //Exported Class/State Manipulators
+ ///////////////////////////////////////////////////////////////////////
+ void Kill(); // RenderReady||Frozen >> Dead
+ void Resurrect(); // Dead >> RenderReady
+ void FreezeCorpse();// Dead >> Frozen
+ void Freeze(); // RenderReady >> Frozen
+ void Thaw(); // Frozen >> RenderReady
+ void Chill( void ); // To frozen, but remembers old state.
+ void Warm( void ); // Restores state remember by Chill.
+
+ ///////////////////////////////////////////////////////////////////////
+ //Exported Class/State
+ ///////////////////////////////////////////////////////////////////////
+ enum eExportedState
+ {
+ msDead,
+ msFrozen,
+ msRenderReady
+ };
+ eExportedState mExportedState;
+ eExportedState mPreviousState;
+ bool IsDead();
+ bool IsFrozen();
+ bool IsRenderReady();
+
+ void SetNumViews( unsigned int numViews ) { mNumViews = numViews; }
+ unsigned int GetNumViews() { return( mNumViews ); }
+ void SetBeginView( bool BeginView ) { mIsBeginView = BeginView; }
+ bool IsBeginView( void ) const { return mIsBeginView; }
+
+protected:
+ virtual bool IsGutsSetup();
+ virtual bool IsViewCamSetup( unsigned int viewIndex );
+ //Called by constructor
+ void OnRenderLayerInit();
+
+ //Static Data
+ enum
+ {
+ msMaxGuts=10
+ };
+
+ //Member data
+ tView* mpView[ MAX_PLAYERS ];
+ float mAlpha;
+
+ SwapArray<tDrawable*> mpGuts;
+
+ bool mIsBeginView : 1;
+ unsigned int mNumViews;
+};
+
+#endif
+
diff --git a/game/code/render/RenderManager/RenderManager.cpp b/game/code/render/RenderManager/RenderManager.cpp
new file mode 100644
index 0000000..75be7ee
--- /dev/null
+++ b/game/code/render/RenderManager/RenderManager.cpp
@@ -0,0 +1,2060 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: RenderManager.cpp
+//
+// Description: Implementation for RenderManager class.
+//
+// History: + Stolen and cleaned up from Penthouse -- Darwin Chau
+// + Stolen from Darwin and Tailored
+// to RenderFlow from GameFlow -- Devin [4/17/2002]
+// + Stolen from Devin and Tailored
+// to RenderManager from RenderFlow -- Devin [4/17/2002]
+//
+//=============================================================================
+
+//If you want only level 1, do this.
+//#define MS8_PANIC
+
+//I got yer synchronous loads right here!
+//#define LOAD_SYNC
+
+//========================================
+// System Includes
+//========================================
+#include <constants/chunkids.hpp>
+#include <raddebug.hpp>
+#include <radtime.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/RenderManager/RenderLayer.h>
+#include <render/Culling/WorldScene.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/RenderManager/FrontEndRenderLayer.h>
+#include <render/IntersectManager/IntersectManager.h>
+#include <render/Loaders/AllWrappers.h>
+#include <render/DSG/IntersectDSG.h>
+#include <render/DSG/WorldSphereDSG.h>
+#include <render/DSG/DSGFactory.h>
+#include <render/DSG/LensFlareDSG.h>
+#include <render/DSG/animcollisionentitydsg.h>
+#include <render/DSG/animentitydSG.h>
+#include <render/Enums/RenderEnums.h>
+#include <constants/srrchunks.h>
+#include <memory/srrmemory.h>
+
+#include <mission/gameplaymanager.h>
+
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+
+#include <worldsim/character/character.h>
+
+#include <debug/profiler.h>
+#include <debug/debuginfo.h>
+
+#include <meta/locatorevents.h>
+#include <events/eventdata.h>
+#include <events/eventenum.h>
+#include <events/eventmanager.h>
+
+#include <meta/zoneeventlocator.h>
+#include <meta/occlusionlocator.h>
+#include <meta/triggervolume.h>
+
+#include <worldsim/character/charactermanager.h>
+
+#include <main/game.h>
+#include <main/platform.h>
+
+#include <memory/srrmemory.h>
+
+#include <sound/soundmanager.h>
+
+#include <presentation/presentation.h>
+#include <presentation/fmvplayer/fmvplayer.h>
+
+#include <pddi/pddiext.hpp>
+#include <p3d/light.hpp>
+
+#ifdef DEBUGWATCH
+#include <radmemorymonitor.hpp>
+#include <worldsim/worldphysicsmanager.h>
+#endif
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Static pointer to instance of this singleton.
+//
+RenderManager* RenderManager::mspInstance = NULL;
+
+#ifndef RAD_RELEASE
+char gZoneLoadID[256];
+const char* ZONE_STRING = "%s : %d";
+void LoadTag( const char* zone, int& index )
+{
+ char name[ 9 ] = "";
+ strncpy( name, &zone[4], strlen( zone ) - 8 );
+ name[8] = '\0';
+ sprintf( gZoneLoadID, ZONE_STRING, name, index );
+ SetMemoryIdentification( gZoneLoadID );
+
+ index++;
+}
+
+void FinishedZone()
+{
+ strcat( gZoneLoadID, " - FINSIHED" );
+ SetMemoryIdentification( gZoneLoadID );
+}
+#endif
+
+//
+// Static list of items to load per level, per mission
+//
+static char* sLevelLoadList[] =
+{
+ "ART\\L1_TERRA.P3D", //L1
+ "ART\\L2_TERRA.P3D", //L2
+ "ART\\L3_TERRA.p3d", //L3
+ "ART\\L4_TERRA.P3D", //L4
+ "ART\\L5_TERRA.P3D", //L5
+ "ART\\L6_TERRA.P3D", //L6
+ "ART\\L7_TERRA.P3D", //L7
+ "ART\\B00.P3D", //SUPER_SPRINT
+ "ART\\B01.P3D", //SUPER_SPRINT
+ "ART\\B02.P3D", //SUPER_SPRINT
+ "ART\\B03.P3D", //SUPER_SPRINT
+ "ART\\B04.P3D", //SUPER_SPRINT
+ "ART\\B05.P3D", //SUPER_SPRINT
+ "ART\\B06.P3D", //SUPER_SPRINT
+ "ART\\B07.P3D" //SUPER_SPRINT
+};
+
+static char* sIntersectLoadList[] =
+{
+ "ART\\L1_INTER.P3D", //L1
+ "ART\\L2_INTER.P3D", //L2
+ "ART\\L3_INTER.p3d", //L3
+ "ART\\L4_INTER.P3D", //L4
+ "ART\\L5_INTER.P3D", //L5
+ "ART\\L6_INTER.P3D", //L6
+ "ART\\L7_INTER.P3D", //L7
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ ""
+};
+
+static char* sMissionLoadList[] =
+{
+ "everground.p3d" //L1M1
+};
+
+bool ENABLE_MOTION_BLUR = true;
+
+float BLUR_SCALE = 0.9f;
+float BLUR_START = 33.33f;
+float MAX_BLUR = 0.15f; // Blur alpha will never go over this level
+// Blur gradient is slope in the linear blur equation
+// So max blur will be reached at 15 fps, scaled linearly from 30 fps
+float BLUR_GRADIENT = MAX_BLUR / ( 66.66f - 33.33f );
+
+// Vlad wants the PS2 to use a minimum amount of fixed blurring all the time
+#ifdef RAD_PS2
+//float MIN_PS2_BLUR = 0.075f;
+float MIN_PS2_BLUR = 0.15f;
+float MIN_PS2_BLUR_CHEAT = 0.8f;
+#endif
+
+#if defined( RAD_PS2) || defined( RAD_XBOX )
+#define USE_BLUR
+#endif
+
+//******************************************************************************
+//
+// Public Member Functions : RenderManager Interface
+//
+//******************************************************************************
+
+//==============================================================================
+// RenderManager::DumpAllLoadedData
+//==============================================================================
+//
+// Description: .
+//
+// Parameters:
+//
+// Return: None.
+//
+// Constraints:
+//
+//==============================================================================
+void RenderManager::DumpAllLoadedData
+(
+)
+{
+ mpRenderLayers[RenderEnums::LevelSlot]->Freeze();
+// mpRenderLayers[RenderEnums::LevelSlot]->NullifyGuts();
+ mpRenderLayers[RenderEnums::LevelSlot]->Kill();
+
+ // this can release some objects, need to do it now or
+ // we'll die next time a frame is rendered since heaps have probably been
+ // blown away
+ LensFlareDSG::ReadFrameBufferIntensities();
+
+ //
+ // This will cause a lag when dumping level data GC Lot Check Violation
+ //
+ FlushDelList();
+ //
+ // Clean Reinit
+ //
+ // mpRenderLayers[RenderEnums::LevelSlot]->DoPreStaticLoad();
+ // mpRenderLayers[RenderEnums::InteriorSlot]->DoPreStaticLoad();
+ //
+ // Kill the Default Pure3D inventory;
+ // this is where the first level load went
+ //
+ p3d::inventory->RemoveSectionElements(tName::MakeUID("Default"));
+ p3d::inventory->DeleteSection(tName::MakeUID("Default"));
+
+ AllWrappers::GetInstance()->ClearGlobalEntities();
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msGeometry ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msStaticEntity ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msStaticPhys ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msTreeDSG ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msFenceEntity ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msIntersectDSG ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msAnimCollEntity).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msAnimEntity ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msDynaPhys ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msInstStatPhys ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msInstStatEntity).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msLocator ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msWorldSphere ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msRoadSegment ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msPathSegment ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msBillboard ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msLensFlare ).ModRegdListener( this, RenderEnums::BogusUserData );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msAnimDynaPhys ).ModRegdListener( this, RenderEnums::BogusUserData );
+
+}
+
+//==============================================================================
+// RenderManager::LoadAllNeededData
+//==============================================================================
+//
+// Description: Loads data unique to a Level,Mission tuple.
+//
+// Parameters: Level and Mission specifier
+//
+// Return: Bool: was the layer that got wiped out marked as dead?
+//
+// Constraints:
+//
+//==============================================================================
+void RenderManager::SetLoadData
+(
+ RenderEnums::LayerEnum isLayer,
+ RenderEnums::LevelEnum isLevel,
+ RenderEnums::MissionEnum isMission
+)
+{
+ //Valid Layer?
+ rAssert( isLayer < RenderEnums::numLayers );
+ rAssert( isLevel < RenderEnums::MAX_LEVEL );
+ rAssert( isMission < RenderEnums::numMissions );
+
+ msLayer = isLayer;
+ msLevel = isLevel;
+ msMission = isMission;
+
+ mCurWorldLayer = msLayer;
+}
+//========================================================================
+// RenderManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderManager::RedirectChunks( int ChunkDestinationMask )
+{
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msStaticEntity ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::StaticEntityGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msStaticPhys ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::StaticPhysGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msTreeDSG ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::TreeDSGGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msFenceEntity ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::FenceGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msGeometry ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::IgnoreGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msIntersectDSG ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::IntersectGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msAnimCollEntity).ModRegdListener( this, ChunkDestinationMask | RenderEnums::AnimCollGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msAnimEntity ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::AnimGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msDynaPhys ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::DynaPhysGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msInstStatPhys ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::StaticPhysGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msInstStatEntity).ModRegdListener( this, ChunkDestinationMask | RenderEnums::StaticEntityGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msLocator ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::LocatorGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msWorldSphere ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::WorldSphereGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msRoadSegment ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::RoadSegmentGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msPathSegment ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::PathSegmentGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msBillboard ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::StaticEntityGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msLensFlare ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::StaticEntityGuts );
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msAnimDynaPhys ).ModRegdListener( this, ChunkDestinationMask | RenderEnums::DynaPhysGuts );
+
+}
+//========================================================================
+// RenderManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderManager::FlushDelList()
+{
+ radTime64 start = radTimeGetMicroseconds64();
+
+ while(mEntityDeletionList.mUseSize)
+ {
+ mEntityDeletionList[0]->Release();
+ mEntityDeletionList.Remove(0);
+ }
+
+ radTime64 end = radTimeGetMicroseconds64();
+ unsigned deleteTime = (unsigned) (end - start);
+
+ rTunePrintf("RenderManager::FlushDelList Delete time: %.3fms\n", deleteTime / 1000.0F);
+}
+//========================================================================
+// RenderManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderManager::MunchDelList(unsigned us)
+{
+ radTime64 start = radTimeGetMicroseconds64();
+
+ while(mEntityDeletionList.mUseSize)
+ {
+ mEntityDeletionList[0]->Release();
+ mEntityDeletionList.Remove(0);
+ radTime64 elapsed = radTimeGetMicroseconds64() - start;
+
+ if(elapsed > us)
+ {
+ break; // too spikey, delete some more next frame
+ }
+ }
+}
+
+//========================================================================
+// RenderManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+bool RenderManager::LoadAllNeededData
+(
+)
+{
+ mbLoadZonesDumped = false;
+ mDoneInitialLoad = false;
+
+ //MS7
+ //static int sFuckinCompilerWontShutUp = isLayer;
+
+ switch( msLayer )
+ {
+ case RenderEnums::LevelSlot:
+ if( mpRenderLayers[msLayer]->IsDead() )
+ {
+#if 0
+ int TheEnum = RenderEnums::LevelSlot | RenderEnums::GeometryGuts;
+ rDebugPrintf( "Wrapper Init: Layer:%X GutsID: %X\n",
+ (TheEnum & RenderEnums::LayerOnlyMask),
+ (TheEnum & RenderEnums::GutsOnlyMask));
+#endif
+ mpRenderLayers[msLayer]->DoPreStaticLoad();
+ HeapMgr()->PushHeap (GMA_LEVEL_ZONE);
+
+ (dynamic_cast<tGeometryLoader*>(&AllWrappers::GetInstance()->mLoader( AllWrappers::msGeometry )))->SetOptimize(true);
+
+ RedirectChunks(RenderEnums::LevelSlot);
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msWorldSphere ).ModRegdListener( this, RenderEnums::LevelSlot | RenderEnums::GlobalWSphereGuts );
+
+
+//////////////////////////////////////////////////////////////////////////
+// SRR2_LOAD_ASYNC
+//////////////////////////////////////////////////////////////////////////
+ tName LevelName(sLevelLoadList[msLevel]);
+ mpRenderLayers[RenderEnums::LevelSlot]->DoPreDynaLoad(LevelName);
+
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msBillboard ).ModRegdListener( this, RenderEnums::IgnoreGuts );
+
+#ifndef RAD_RELEASE
+ static int loadTag1 = 0;
+ LoadTag( sLevelLoadList[msLevel], loadTag1 );
+#endif
+
+#ifdef LOAD_SYNC
+ HeapMgr()->DumpHeapStats( true );
+ GetLoadingManager()->LoadSync( FILEHANDLER_PURE3D, sLevelLoadList[msLevel], GMA_LEVEL_ZONE );
+ HeapMgr()->DumpHeapStats( true );
+#else
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, sLevelLoadList[msLevel], GMA_LEVEL_ZONE, this, &msLayer);
+#endif
+
+ //////////////////////////////////////////////////////////////////////////
+ //intesects rolled into zone files
+ //GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, sIntersectLoadList[msLevel], GMA_LEVEL_ZONE, sLevelLoadList[msLevel]);
+
+ msLayer &= ~RenderEnums::AllIntersectLoadingComplete;
+ msLayer |= RenderEnums::AllRenderLoadingComplete;
+// GetLoadingManager()->ProcessRequests( this, &msLayer );
+
+ //mbDynaLoading = true;
+ mbDynaLoading = false;
+ mbDrivingTooFastLoad = false;
+
+ HeapMgr()->PopHeap (GMA_LEVEL_ZONE);
+
+#ifdef LOAD_SYNC
+ GetLoadingManager()->AddCallback( this, &msLayer );
+#endif
+
+ return true;
+ }
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+//==============================================================================
+// RenderManager::ContextUpdate
+//==============================================================================
+//
+// Description: Called (responsibly) from managing Context
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints:
+//
+//==============================================================================
+void RenderManager::ContextUpdate( unsigned int iElapsedTime )
+{
+ // On the PS2, use a minimum blur all the time
+#ifdef RAD_PS2
+ ApplyPS2Blur();
+#endif
+
+ GetIntersectManager()->mbSameFrame = false;
+#ifdef DEBUGWATCH
+ unsigned int t0 = radTimeGetMicroseconds();
+#endif
+ BEGIN_PROFILE( "Rendering" );
+
+
+ BEGIN_PROFILE( "Swap Buffers" );
+ p3d::context->SwapBuffers();
+ END_PROFILE( "Swap Buffers" );
+
+#if defined( RAD_XBOX ) || defined ( RAD_GAMECUBE )
+ LoadingManager* lm = GetLoadingManager();
+ PresentationManager* pm = GetPresentationManager();
+ p3d::display->SetForceVSync( lm && !lm->IsLoading(), !(pm && pm->GetFMVPlayer()->IsPlaying()));
+#endif
+
+#ifdef LOAD_SYNC
+ FlushDelList();
+#else
+ MunchDelList(2000); // work on the DelList for up to 2000 microseconds, then return
+#endif
+
+ BEGIN_PROFILE( "Lens Flare Frame Buffer Read" );
+ LensFlareDSG::ReadFrameBufferIntensities();
+ END_PROFILE( "Lens Flare Frame Buffer Read" );
+
+#ifdef DEBUGWATCH
+ mDebugSwapTime = radTimeGetMicroseconds()-t0;
+ t0 = radTimeGetMicroseconds();
+#endif
+
+ if( mMood.mTransition >= 0.0f )
+ {
+ TransitionMoodLighting( iElapsedTime );
+ }
+
+ BEGIN_PROFILE( "Begin Frame" );
+ p3d::context->BeginFrame();
+ END_PROFILE( "Begin Frame" );
+
+ // Render Stuff; call your Render shots, Tex.
+ for( int i=RenderEnums::numLayers-1; i>-1; i-- )
+ {
+#ifdef DEBUGINFO_ENABLED
+ // We need to render the debug info just before we render the GUI layer
+ //since rendering that layer changes the world matrix.
+ if( i == RenderEnums::GUI )
+ {
+ DEBUGINFO_RENDER();
+ }
+#endif
+ if( mpRenderLayers[i]->IsRenderReady() )
+ {
+ BEGIN_PROFILE( "Layers" );
+ mpRenderLayers[i]->Render();
+ END_PROFILE( "Layers" );
+ }
+ }
+
+
+ END_PROFILE( "Rendering" );
+
+#ifdef DEBUGWATCH
+ t0 = radTimeGetMicroseconds();
+#endif
+#ifndef FINAL
+ BEGIN_PROFILE( "Dump Stats" );
+ HeapMgr()->DumpHeapStats();
+ HeapMgr()->DumpArtStats();
+ END_PROFILE( "Dump Stats" );
+#endif
+
+ RENDER_PROFILER();
+
+ //MEMTRACK_RENDER();
+
+ //HEAPSTACKS_RENDER();
+
+ SOUNDDEBUG_RENDER();
+
+#ifdef USE_BLUR
+ ((pddiExtFramebufferEffects*)p3d::pddi->GetExtension( PDDI_EXT_FRAMEBUFFER_EFFECTS ))->EnableMotionBlur( mEnableMotionBlur || ENABLE_MOTION_BLUR, mBlurAlpha, BLUR_SCALE, false );
+ ((pddiExtFramebufferEffects*)p3d::pddi->GetExtension( PDDI_EXT_FRAMEBUFFER_EFFECTS ))->SetQuality( pddiExtFramebufferEffects::Smallest );
+
+ if ( mEnableMotionBlur || ENABLE_MOTION_BLUR )
+ {
+ ((pddiExtFramebufferEffects*)p3d::pddi->GetExtension( PDDI_EXT_FRAMEBUFFER_EFFECTS ))->RenderMotionBlur();
+ }
+#endif
+
+
+ if ( CommandLineOptions::Get( CLO_DEMO_TEST ) ||
+ GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_DEMO_TEST ) )
+ {
+ char buffy[32];
+ sprintf( buffy, "Demo Count: %d", GetGame()->GetDemoCount() );
+
+ const int LEFT = 35;
+ const int TOP = 45;
+ const pddiColour WHITE(128,128,128);
+
+ p3d::pddi->DrawString( buffy, LEFT , 80 + TOP, WHITE );
+
+ static unsigned int time = 0;
+ time = GetGame()->GetTime();
+ time += iElapsedTime;
+
+ unsigned int hours = time / 3600000;
+ unsigned int deltaTime = time % 3600000;
+
+ unsigned int minutes = deltaTime / 60000;
+ deltaTime = deltaTime % 60000;
+
+ unsigned int seconds = deltaTime / 1000;
+ deltaTime = deltaTime % 1000;
+
+ sprintf( buffy, "Time: %d:%d:%d.%d", hours, minutes, seconds, deltaTime );
+ p3d::pddi->DrawString( buffy, LEFT , 100 + TOP, WHITE );
+
+ if ( GetGameplayManager() )
+ {
+ sprintf( buffy, "Level %d", GetGameplayManager()->GetCurrentLevelIndex() );
+ p3d::pddi->DrawString( buffy, LEFT , 120 + TOP, WHITE );
+ }
+
+ GetGame()->SetTime( time );
+ }
+
+ p3d::context->EndFrame( false );
+
+
+#ifdef DEBUGWATCH
+ mDebugRenderTime = radTimeGetMicroseconds()-t0;
+
+ if(mDebugDumpAllZones)
+ {
+ mDebugDumpAllZones = false;
+ mpLayer( RenderEnums::LevelSlot )->DumpAllDynaLoads(1, mEntityDeletionList );
+ GetWorldPhysicsManager()->FreeAllCollisionAreaIndicies();
+ FlushDelList();
+ ::radMemoryMonitorSuspend();
+ }
+#endif
+ //unsigned int time1 = radTimeGetMicroseconds();
+ //rReleasePrintf( "Render Loop: %d micro's\n", time1-time0 );
+}
+
+//******************************************************************************
+//
+// Public Member Functions : Instance Interface
+//
+//******************************************************************************
+
+//==============================================================================
+// RenderManager::CreateInstance
+//==============================================================================
+//
+// Description: Create the RenderManager controller if needed.
+//
+// Parameters: None.
+//
+// Return: Pointer to the created RenderManager controller.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+RenderManager* RenderManager::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "RenderManager" );
+ rAssert( mspInstance == NULL );
+ mspInstance = new(GMA_PERSISTENT) RenderManager();
+MEMTRACK_POP_GROUP( "RenderManager" );
+
+ return mspInstance;
+}
+
+//==============================================================================
+// RenderManager::GetInstance
+//==============================================================================
+//
+// Description: Get the RenderManager controller if exists.
+//
+// Parameters: None.
+//
+// Return: Pointer to the created RenderManager controller.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+RenderManager* RenderManager::GetInstance()
+{
+ rAssert( mspInstance != NULL );
+
+ return mspInstance;
+}
+
+
+//==============================================================================
+// RenderManager::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the RenderManager controller.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void RenderManager::DestroyInstance()
+{
+ //
+ // Make sure this doesn't get called twice.
+ //
+ rAssert( mspInstance != NULL );
+ delete mspInstance;
+ mspInstance = NULL;
+}
+//========================================================================
+// RenderManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+WorldScene* RenderManager::pWorldScene()
+{
+// return ((WorldRenderLayer*)mpRenderLayers[RenderEnums::LevelSlot])->pWorldScene();
+ return ((WorldRenderLayer*)mpRenderLayers[mCurWorldLayer])->pWorldScene();
+}
+//========================================================================
+// RenderManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+WorldRenderLayer* RenderManager::pWorldRenderLayer()
+{
+ return ((WorldRenderLayer*)mpRenderLayers[mCurWorldLayer]);
+}
+
+//========================================================================
+// RenderManager::OnChunkLoaded
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderManager::OnChunkLoaded
+(
+ tEntity* ipEntity,
+ int iUserData,
+ unsigned iChunkID
+)
+{
+
+ if( (iUserData == RenderEnums::BogusUserData) ||
+ (iUserData == (int)(RenderEnums::IgnoreGuts)) )// ignore bogus idata -go to hell, compiler! With your trite, meaningless warnings
+ return;
+
+ rAssert( ipEntity != NULL );
+ rAssert( (iUserData & RenderEnums::LayerOnlyMask) < RenderEnums::numLayers &&
+ (iUserData & RenderEnums::LayerOnlyMask) > -1 );
+
+ /*rDebugPrintf( "Chunk: Layer:%X GutsID: %X\n",
+ (iUserData & RenderEnums::LayerOnlyMask),
+ (iUserData & RenderEnums::GutsOnlyMask));*/
+
+ IntersectDSG* pIDSG = NULL;
+
+ // if( (iUserData & RenderEnums::CompletionOnlyMask) == RenderEnums::AllRenderLoadingComplete )
+ // {
+ // return;
+ // }
+
+ switch( iChunkID )
+ {
+ case SRR2::ChunkID::LENS_FLARE_DSG:
+ case SRR2::ChunkID::INSTA_ENTITY_DSG:
+ case SRR2::ChunkID::ENTITY_DSG:
+ switch( iUserData & RenderEnums::GutsOnlyMask )
+ {
+ case RenderEnums::StaticEntityGuts:
+ mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts((StaticEntityDSG*)ipEntity);
+ break;
+ default:
+ //Unexpected GutsEnum
+ rAssert(false);
+ break;
+ }
+ break;
+
+
+ case SRR2::ChunkID::INSTA_STATIC_PHYS_DSG:
+ case SRR2::ChunkID::STATIC_PHYS_DSG:
+ switch( iUserData & RenderEnums::GutsOnlyMask )
+ {
+ case RenderEnums::StaticPhysGuts:
+ mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts((StaticPhysDSG*)ipEntity);
+ break;
+ default:
+ //Unexpected GutsEnum
+ rAssert(false);
+ break;
+ }
+ break;
+
+ case SRR2::ChunkID::DYNA_PHYS_DSG:
+ case SRR2::ChunkID::INSTA_ANIM_DYNA_PHYS_DSG:
+
+ switch( iUserData & RenderEnums::GutsOnlyMask )
+ {
+ case RenderEnums::DynaPhysGuts:
+ {
+ int renderLayer = iUserData & RenderEnums::LayerOnlyMask;
+ DynaPhysDSG* pDynaPhys = static_cast< DynaPhysDSG* >( ipEntity );
+ rAssert( dynamic_cast< DynaPhysDSG* >( ipEntity ) != NULL );
+ mpRenderLayers[ renderLayer ]->AddGuts( pDynaPhys );
+ pDynaPhys->SetRenderLayer( static_cast< RenderEnums::LayerEnum >( renderLayer ) );
+ }
+ break;
+ default:
+ //Unexpected GutsEnum
+ rAssert(false);
+ break;
+ }
+ break;
+
+ case SRR2::ChunkID::TREE_DSG:
+ switch( iUserData & RenderEnums::GutsOnlyMask )
+ {
+ case RenderEnums::TreeDSGGuts:
+ mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts((SpatialTree*)ipEntity);
+ break;
+ default:
+ //Unexpected GutsEnum
+ rAssert(false);
+ break;
+ }
+ break;
+
+ case SRR2::ChunkID::FENCE_DSG:
+ switch( iUserData & RenderEnums::GutsOnlyMask )
+ {
+ case RenderEnums::FenceGuts:
+ mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts((FenceEntityDSG*)ipEntity);
+ break;
+ default:
+ //Unexpected GutsEnum
+ rAssert(false);
+ break;
+ }
+ break;
+
+ case SRR2::ChunkID::INTERSECT_DSG:
+ switch( iUserData & RenderEnums::GutsOnlyMask )
+ {
+ case RenderEnums::IntersectGuts:
+ mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts((IntersectDSG*)ipEntity);
+ break;
+ default:
+ //Unexpected GutsEnum
+ rAssert(false);
+ break;
+ }
+ break;
+
+ case SRR2::ChunkID::ANIM_DSG:
+ switch ( iUserData & RenderEnums::GutsOnlyMask )
+ {
+ case RenderEnums::AnimGuts:
+ {
+// mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts((AnimEntityDSG*)ipEntity);
+ int renderLayer = iUserData & RenderEnums::LayerOnlyMask;
+ AnimEntityDSG* pAnimDSG = static_cast< AnimEntityDSG* >( ipEntity );
+ rAssert( dynamic_cast< AnimEntityDSG* >( ipEntity ) != NULL );
+ mpRenderLayers[ renderLayer ]->AddGuts( pAnimDSG );
+ pAnimDSG->SetRenderLayer( static_cast< RenderEnums::LayerEnum > (renderLayer) );
+ }
+ break;
+ default:
+ rAssert( false );
+ break;
+ }
+ break;
+
+
+
+ case SRR2::ChunkID::ANIM_COLL_DSG:
+ switch( iUserData & RenderEnums::GutsOnlyMask )
+ {
+ case RenderEnums::AnimCollGuts:
+ {
+ // mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts((AnimCollisionEntityDSG*)ipEntity);
+ int renderLayer = iUserData & RenderEnums::LayerOnlyMask;
+ AnimCollisionEntityDSG* pAnimCollDSG = static_cast< AnimCollisionEntityDSG* >( ipEntity );
+ rAssert( dynamic_cast< AnimCollisionEntityDSG* >( ipEntity ) != NULL );
+ mpRenderLayers[ renderLayer ]->AddGuts( pAnimCollDSG );
+ pAnimCollDSG->SetRenderLayer( static_cast< RenderEnums::LayerEnum > (renderLayer) );
+ break;
+ }
+ default:
+ //Unexpected GutsEnum
+ rAssert(false);
+ break;
+ }
+ break;
+
+
+
+ case Pure3D::Mesh::MESH:
+ switch( iUserData & RenderEnums::GutsOnlyMask )
+ {
+ case RenderEnums::GeometryGuts:
+ mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts((tGeometry*)ipEntity);
+ break;
+ case RenderEnums::DrawableGuts:
+ mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts((tDrawable*)ipEntity);
+ break;
+ case RenderEnums::IntersectGuts:
+ pIDSG = GetDSGFactory()->CreateIntersectDSG( (tGeometry*)ipEntity );
+ mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts( pIDSG );
+ break;
+ case RenderEnums::IgnoreGuts:
+ break;
+ default:
+ //Unexpected GutsEnum
+ rAssert(false);
+ break;
+ }
+ break;
+
+ case SRR2::ChunkID::LOCATOR:
+ switch( iUserData & RenderEnums::GutsOnlyMask )
+ {
+ case RenderEnums::LocatorGuts:
+ mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts((TriggerVolume*)ipEntity);
+ break;
+ default:
+ //Unexpected GutsEnum
+ rAssert(false);
+ break;
+ }
+ break;
+
+ case SRR2::ChunkID::ROAD_SEGMENT:
+ switch( iUserData & RenderEnums::GutsOnlyMask )
+ {
+ case RenderEnums::RoadSegmentGuts:
+ mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts((RoadSegment*)ipEntity);
+ break;
+ default:
+ //Unexpected GutsEnum
+ rAssert(false);
+ break;
+ }
+ break;
+
+ case SRR2::ChunkID::PED_PATH_SEGMENT:
+ switch( iUserData & RenderEnums::GutsOnlyMask )
+ {
+ case RenderEnums::PathSegmentGuts:
+ mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts((PathSegment*)ipEntity);
+ break;
+ default:
+ //Unexpected GutsEnum
+ rAssert(false);
+ break;
+ }
+ break;
+
+ case SRR2::ChunkID::WORLD_SPHERE_DSG:
+ switch( iUserData & RenderEnums::GutsOnlyMask )
+ {
+ case RenderEnums::GlobalWSphereGuts:
+ ((WorldSphereDSG*)ipEntity)->Activate();
+ case RenderEnums::WorldSphereGuts:
+ mpRenderLayers[iUserData & RenderEnums::LayerOnlyMask]->AddGuts((WorldSphereDSG*)ipEntity);
+ break;
+ default:
+ //Unexpected GutsEnum
+ rAssert(false);
+ break;
+ }
+ break;
+
+ default:
+ //Unexpected ChunkID
+ rAssert(false);
+ break;
+ }
+}
+
+//========================================================================
+// RenderManager::OnProcessRequestsComplete( void* pUserData );
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderManager::OnProcessRequestsComplete( void* pUserData )
+{
+ rAssert( pUserData != NULL );
+
+ //
+ // When done queued loading, Ignore any new geo's loaded through the
+ // LoaderWrappers
+ //
+ //AllWrappers::GetInstance()->mLoader( AllWrappers::msGeometry ).ModRegdListener( this, RenderEnums::LevelSlot | RenderEnums::IgnoreGuts );
+ GetEventManager()->TriggerEvent( EVENT_DYNAMIC_ZONE_LOAD_ENDED );
+#ifndef RAD_RELEASE
+ FinishedZone();
+#endif
+
+//***
+ //////////////////////////////////////////////////////////////////////////
+ // Dynamic Loading
+ //////////////////////////////////////////////////////////////////////////
+ if( ((*(int*)pUserData) & RenderEnums::CompletionOnlyMask) == RenderEnums::DynamicLoadComplete )
+ {
+ tName GiveItAFuckinName;
+ static char spSomeDamnFile[128];
+ int i = ((*(int*)pUserData) & RenderEnums::ZoneMask) >> RenderEnums::ZoneShift;
+
+ mpRenderLayers[(*(int*)pUserData) & RenderEnums::LayerOnlyMask]->DoPostDynaLoad();
+
+ bool alreadyLoaded=true;
+
+ if( i<mpZEL->GetNumLoadZones() && (msLayer & RenderEnums::LevelSlot) )
+ {
+ rReleasePrintf("Zone Loading Ended: %s\n",mpZEL->GetLoadZone(i));
+ //HeapMgr()->DumpHeapStats(true);
+ tUID tempUID = tName::MakeUID(mpZEL->GetLoadZone(i));
+ GetEventManager()->TriggerEvent( EVENT_NAMED_DYNAMIC_ZONE_LOAD_ENDED, &tempUID );
+
+ if( mbLoadZonesDumped )
+ {
+ mbLoadZonesDumped = false;
+
+ //
+ // Do no further loading; it's been "cancelled".
+ // This is to cover the load resumption logged as bug 10575.
+ // Essentially, the dump would get queued before a zone finished loading
+ // (through a mission reset). Then, on completion, it would dump the load
+ // (with the doPost above) and start loading all the other zones listed in the ZEL.
+ //
+ mZELs.mUseSize = 0;
+ i = mpZEL->GetNumLoadZones();
+ rReleasePrintf("***All LoadZones Dumped; ZEL's cancelled.***\n");
+
+ }
+
+ for(i;i<mpZEL->GetNumLoadZones()&&alreadyLoaded; )
+ {
+ i++;
+ if(i<mpZEL->GetNumLoadZones())
+ {
+ HeapMgr()->PushHeap (GMA_TEMP);
+ GiveItAFuckinName.SetText(mpZEL->GetLoadZone(i));
+ HeapMgr()->PopHeap ( GMA_TEMP);
+
+ alreadyLoaded= ! mpRenderLayers[RenderEnums::LevelSlot]->DoPreDynaLoad(GiveItAFuckinName);
+ }
+ }
+
+ if(i<mpZEL->GetNumLoadZones())
+ {
+ sprintf(spSomeDamnFile,"ART\\%s",mpZEL->GetLoadZone(i));
+
+ (*(int*)pUserData) &= ~RenderEnums::CompletionOnlyMask;
+ (*(int*)pUserData) |= RenderEnums::DynamicLoadComplete;
+
+ (*(int*)pUserData) &= ~RenderEnums::ZoneMask;
+ (*(int*)pUserData) |= i<<RenderEnums::ZoneShift;
+
+ (*(int*)pUserData) &= ~RenderEnums::LayerOnlyMask;
+ (*(int*)pUserData) |= RenderEnums::LevelSlot;
+
+ RedirectChunks(RenderEnums::LevelSlot);
+
+#ifndef RAD_RELEASE
+ static int loadTag2 = 0;
+ LoadTag( spSomeDamnFile, loadTag2 );
+#endif
+
+#ifdef LOAD_SYNC
+ HeapMgr()->DumpHeapStats( true );
+ GetLoadingManager()->LoadSync( FILEHANDLER_PURE3D, spSomeDamnFile, GMA_LEVEL_ZONE, mpZEL->GetLoadZone(i) );
+ HeapMgr()->DumpHeapStats( true );
+ GetLoadingManager()->AddCallback( this, (int*)pUserData );
+#else
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, spSomeDamnFile, GMA_LEVEL_ZONE, mpZEL->GetLoadZone(i), NULL, this, (int*)pUserData);
+#endif
+ }
+ else
+ {
+ if(mpZEL->IsInteriorLoad())
+ {
+ InteriorLoadedEventData data;
+ data.interiorName = mpZEL->GetNameObject();
+ data.sectionName = mpZEL->GetInteriorSection();
+ data.first = mbFirstDynamicZone;
+ GetEventManager()->TriggerEvent( EVENT_INTERIOR_LOADED, reinterpret_cast<void*>( &data ) );
+ }
+
+ if( mbFirstDynamicZone )
+ {
+ mbFirstDynamicZone = false;
+ GetEventManager()->TriggerEvent( EVENT_FIRST_DYNAMIC_ZONE_END );
+ }
+/*
+ //////////////////////////////////////////////////////////////////////////
+ // Find a Zone to load
+ //////////////////////////////////////////////////////////////////////////
+BEGIN_PROFILE( "Find Load Zone" );
+ i=-1;
+ for(i;i<mpZEL->GetNumLoadZones()&&alreadyLoaded; )
+ {
+ i++;
+ if(i<mpZEL->GetNumLoadZones())
+ {
+ HeapMgr()->PushHeap (GMA_TEMP);
+ GiveItAFuckinName.SetText(mpZEL->GetLoadZone(i));
+ HeapMgr()->PopHeap ( GMA_TEMP );
+
+ alreadyLoaded= ! mpRenderLayers[RenderEnums::LevelSlot]->DoPreDynaLoad(GiveItAFuckinName);
+ }
+ }
+END_PROFILE( "Find Load Zone" );
+
+ //////////////////////////////////////////////////////////////////////////
+ // Zone Loading
+ //////////////////////////////////////////////////////////////////////////
+ if(i<mpZEL->GetNumLoadZones())
+ {
+ sprintf(spSomeDamnFile,"ART\\%s",mpZEL->GetLoadZone(i));
+
+ msLayer &= ~RenderEnums::CompletionOnlyMask;
+ msLayer |= RenderEnums::DynamicLoadComplete;
+
+ msLayer &= ~RenderEnums::ZoneMask;
+ msLayer |= i<<RenderEnums::ZoneShift;
+
+ msLayer &= ~RenderEnums::LayerOnlyMask;
+ msLayer |= RenderEnums::LevelSlot;
+
+ RedirectChunks(RenderEnums::LevelSlot);
+
+BEGIN_PROFILE( "Add Requests Int" );
+#ifndef RAD_RELEASE
+ static int loadTag3 = 0;
+ LoadTag( spSomeDamnFile, loadTag3 );
+#endif
+
+#ifdef LOAD_SYNC
+ HeapMgr()->DumpHeapStats( true );
+ GetLoadingManager()->LoadSync( FILEHANDLER_PURE3D, spSomeDamnFile, GMA_LEVEL_ZONE, mpZEL->GetLoadZone(i) );
+ HeapMgr()->DumpHeapStats( true );
+#else
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, spSomeDamnFile, GMA_LEVEL_ZONE, mpZEL->GetLoadZone(i), NULL, this, &msLayer);
+#endif
+
+END_PROFILE( "Add Requests Int" );
+
+ rReleasePrintf("ZoneLoadingStart--===--%s--===--\n", spSomeDamnFile);
+ mbDynaLoading = true;
+
+#ifdef LOAD_SYNC
+ GetLoadingManager()->AddCallback( this, &msLayer );
+#endif
+ return;
+ }
+*/
+ //
+ // If other loading sets were queued while loading
+ //
+ if( mZELs.mUseSize != 0 )
+ {
+ mpZEL = mZELs[0];
+ mZELs.RemoveKeepOrder(0);
+
+ (*(int*)pUserData) &= ~RenderEnums::ZoneMask;
+ (*(int*)pUserData) |= 0<<RenderEnums::ZoneShift;
+
+ rReleasePrintf("Attemptimg to Load a Driving Too Fast Zone.\n");
+
+ mbDynaLoading = false;
+ mbDrivingTooFastLoad = true;
+ HandleEvent((EventEnum)(EVENT_LOCATOR+LocatorEvent::DYNAMIC_ZONE), mpZEL);
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_ALL_DYNAMIC_ZONE_END );
+ rReleasePrintf("ZoneLoadingEnded Verified\n");
+ mbDynaLoading = false;
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msBillboard ).ModRegdListener( this, RenderEnums::IgnoreGuts );
+ }
+ }
+ }
+ }
+}
+//========================================================================
+// RenderManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderManager::DoPostLevelLoad()
+{
+ //////////////////////////////////////////////////////////////////////////
+ // PreDynamic Loading
+ //////////////////////////////////////////////////////////////////////////
+// if( ((*(int*)pUserData) & RenderEnums::CompletionOnlyMask) == RenderEnums::AllRenderLoadingComplete )
+ {
+// mpRenderLayers[(*(int*)pUserData) & RenderEnums::LayerOnlyMask]->DoPostStaticLoad();
+//// mpRenderLayers[(*(int*)pUserData) & RenderEnums::LayerOnlyMask]->Resurrect();
+
+ //mpRenderLayers[(*(int*)pUserData) & RenderEnums::LayerOnlyMask]->FreezeCorpse();
+
+ mpRenderLayers[RenderEnums::LevelSlot]->DoPostDynaLoad();
+ mpRenderLayers[RenderEnums::LevelSlot]->FreezeCorpse();
+ //
+ // When done queued loading, Ignore any new geo's loaded through the
+ // LoaderWrappers
+ //
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msGeometry ).ModRegdListener( this, RenderEnums::LevelSlot | RenderEnums::IgnoreGuts );
+ mDoneInitialLoad = true;
+ mbDynaLoading = false;
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msBillboard ).ModRegdListener( this, RenderEnums::IgnoreGuts );
+ }
+
+}
+bool RenderManager::DoneInitialLoad()
+{
+ return mDoneInitialLoad;
+}
+//=============================================================================
+// RenderManager::mpLayer
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( RenderEnums::LayerEnum isLayer )
+//
+// Return: RenderLayer
+//
+//=============================================================================
+RenderLayer* RenderManager::mpLayer( RenderEnums::LayerEnum isLayer )
+{
+ //Valid Layer?
+ rAssert( isLayer < RenderEnums::numLayers );
+
+ //
+ // Walk (talk?) like an egyptian
+ //
+ return mpRenderLayers[isLayer];
+}
+
+//=============================================================================
+// RenderManager::FreezeAllLayers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RenderManager::FreezeAllLayers()
+{
+ for( unsigned int i = 0; i < RenderEnums::numLayers; i++ )
+ {
+ mpRenderLayers[ i ]->Freeze();
+ }
+}
+/*=============================================================================
+Description: Freeze the layers, except for the presentation layer. The trick
+ here however is that the layer remembers it's previous state
+ because some layers could have already been frozen (such as
+ interior/exterior layers) and we don't want them to thaw after
+ the presentation. So match this call will a call to
+ ThawFromPresentation().
+=============================================================================*/
+void RenderManager::FreezeForPresentation( void )
+{
+ for( unsigned int i = 0; i < RenderEnums::numLayers; ++i )
+ {
+ if( i != RenderEnums::GUI ) // exclude GUI layer
+ {
+ mpRenderLayers[ i ]->Chill();
+ }
+ }
+}
+/*=============================================================================
+Description: Thaws all layers. The idea is that we'll freeze all the layers
+ for playing an FMV, then thaw the presentation layer. After
+ the movie we need to thaw all the layers and then freeze the
+ presentation layer. I'm not sure this will work so well for
+ cases where we had a layer frozen for another reason before the
+ movie started.
+=============================================================================*/
+void RenderManager::ThawFromPresentation( void )
+{
+ for( unsigned int i = 0; i < RenderEnums::numLayers; ++i )
+ {
+ if( i != RenderEnums::GUI ) // exclude GUI layer
+ {
+ mpRenderLayers[ i ]->Warm();
+ }
+ }
+}
+//========================================================================
+// RenderManager::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderManager::HandleEvent( EventEnum id, void* pEventData )
+{
+BEGIN_PROFILE( "RenderManager HandleEvent" );
+ switch(id)
+ {
+ case EVENT_MISSION_RESET:
+ {
+#if defined( RAD_XBOX ) || defined( RAD_WIN32 )
+ // XBox seems to like this syntax better.
+ bool jumpStage = reinterpret_cast<bool>( pEventData );
+#else
+ bool jumpStage = (bool)( pEventData );
+#endif
+ if( jumpStage )
+ {
+ ResetMoodLighting( true );
+ }
+ }
+ break;
+ case EVENT_LOCATOR + LocatorEvent::LIGHT_CHANGE:
+ {
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ rAssert( pLocator );
+ tColour lightMod( pLocator->GetData() );
+ if ( pLocator->GetPlayerEntered() )
+ {
+ SetLightMod( lightMod );
+ ++(mMood.mVolumeCount);
+ }
+ else
+ {
+ --(mMood.mVolumeCount);
+ if( mMood.mVolumeCount == 0 )
+ {
+ ResetMoodLighting();
+ }
+ }
+ if( mMood.mVolumeCount < 0 )
+ {
+ mMood.mVolumeCount = 0;
+ }
+ }
+ break;
+ case EVENT_LOCATOR+LocatorEvent::OCCLUSION_ZONE:
+ {
+ OcclusionLocator* pOccLocator = ((OcclusionLocator*)pEventData);
+ SpatialTreeIter& rTreeWalker = pWorldScene()->mStaticTreeWalker;
+
+ SphereSP occSphere;
+ rmt::Sphere sphere;
+
+ BoxPts occBox;
+ rmt::Box3D box;
+
+ if(pOccLocator->GetPlayerEntered())
+ {
+ if(mbInVsibilityVolume)
+ {
+ rTreeWalker.AndTree(0x00000000);
+ mbIgnoreVisibilityClear = true;
+ }
+ mbInVsibilityVolume = true;
+ tMark curMark = SpatialTreeIter::msFilterInvisible;
+ int numOccluders = pOccLocator->GetNumOccTriggers();
+ int stopCondition = pOccLocator->GetNumTriggers();
+ for(int i=1; i<stopCondition; i++)
+ {
+ if(i>numOccluders)
+ curMark = SpatialTreeIter::msFilterVisible;
+ else
+ curMark = SpatialTreeIter::msFilterInvisible;
+
+ if(pOccLocator->GetTriggerVolume(i)->GetType() == TriggerVolume::SPHERE )
+ {
+ pOccLocator->GetTriggerVolume(i)->GetBoundingSphere(&sphere);
+ occSphere.SetTo( sphere.centre, sphere.radius );
+ rTreeWalker.MarkSubTrees(occSphere, curMark );
+ }
+ else
+ {
+ pOccLocator->GetTriggerVolume(i)->GetBoundingBox(&box);
+ occBox.mBounds.mMin.SetTo(box.low);
+ occBox.mBounds.mMax.SetTo(box.high);
+ rTreeWalker.MarkSubTrees(occBox, curMark );
+ }
+ }
+ }
+ else
+ {
+ if(mbIgnoreVisibilityClear)
+ {
+ mbIgnoreVisibilityClear = false;
+ }
+ else
+ {
+ mbInVsibilityVolume = false;
+ rTreeWalker.AndTree(0x00000000);
+ }
+ }
+
+ }
+ break;
+
+ case EVENT_ALL_DYNAMIC_ZONES_DUMPED:
+ {
+ if(mbDynaLoading)
+ mbLoadZonesDumped = true;
+ break;
+ }
+ case EVENT_FIRST_DYNAMIC_ZONE_START:
+ mbFirstDynamicZone = true;
+ case EVENT_LOCATOR+LocatorEvent::DYNAMIC_ZONE:
+ {
+ ZoneEventLocator* pZEL = (ZoneEventLocator*)pEventData;
+ //HeapMgr()->DumpHeapStats(true);
+
+ /* if(!(pZEL->GetPlayerEntered()))
+ {
+ int j;
+ for( j=pZEL->GetNumLWSActivates()-1; j>-1; j-- )
+ {
+ pWorldRenderLayer()->DeactivateWS(tName::MakeUID(pZEL->GetLWSActivates(j)));
+ }
+ for( j=pZEL->GetNumLWSDeactivates()-1; j>-1; j-- )
+ {
+ pWorldRenderLayer()->ActivateWS(tName::MakeUID(pZEL->GetLWSDeactivates(j)));
+ }
+ }*/
+ if(pZEL->GetPlayerEntered()||mbDrivingTooFastLoad)
+ {
+ if(pZEL->IsInteriorLoad())
+ {
+ InteriorLoadedEventData data;
+ data.interiorName = pZEL->GetUID();
+ data.sectionName = tEntity::MakeUID(pZEL->GetInteriorSection());
+ data.first = mbFirstDynamicZone;
+ GetEventManager()->TriggerEvent( EVENT_INTERIOR_LOAD_START, reinterpret_cast<void*>( &data ) );
+ }
+
+ int j;
+ for( j=pZEL->GetNumLWSActivates()-1; j>-1; j-- )
+ {
+ pWorldRenderLayer()->ActivateWS(tName::MakeUID(pZEL->GetLWSActivates(j)));
+ }
+ for( j=pZEL->GetNumLWSDeactivates()-1; j>-1; j-- )
+ {
+ pWorldRenderLayer()->DeactivateWS(tName::MakeUID(pZEL->GetLWSDeactivates(j)));
+ }
+
+ if(pZEL->GetNumLoadZones()==0 && pZEL->GetNumDumpZones()==0)
+ {
+END_PROFILE( "RenderManager HandleEvent" );
+ rReleasePrintf("Nothin to Load, skipping zone\n");
+ return;
+ }
+
+ //If we're already Loading
+ if(mbDynaLoading == true)
+ {
+ rReleasePrintf("Driving TOO FAST: Adding to queue.\n");
+ mZELs.Add((ZoneEventLocator*&)pEventData);
+END_PROFILE( "RenderManager HandleEvent" );
+ return;
+ }
+
+ mbDrivingTooFastLoad = false;
+ mpZEL = (ZoneEventLocator*)pEventData;
+
+ //If we're not currently loading
+ static char spSomeDamnFile[128];
+ int i;
+ bool alreadyLoaded = true;
+ tName GiveItAFuckinName;
+
+ rReleasePrintf("Encountered Dynamic Zone:\n");
+ for(i=0; i<mpZEL->GetNumDumpZones(); i++)
+ {
+ rReleasePrintf("Dump: %s\n",mpZEL->GetDumpZone(i));
+ }
+ for(i=0; i<mpZEL->GetNumLoadZones(); i++)
+ {
+ rReleasePrintf("Load: %s\n",mpZEL->GetLoadZone(i));
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Zone Dumping
+ //////////////////////////////////////////////////////////////////////////
+BEGIN_PROFILE( "Zone/Int Dump" );
+
+BEGIN_PROFILE( "Dump Zones" );
+ for(i=0; i<mpZEL->GetNumDumpZones(); i++)
+ {
+ HeapMgr()->PushHeap (GMA_TEMP);
+ GiveItAFuckinName.SetText(mpZEL->GetDumpZone(i));
+ HeapMgr()->PopHeap ( GMA_TEMP);
+
+ //mpRenderLayers[msLayer & RenderEnums::LayerOnlyMask]->DumpDynaLoad(GiveItAFuckinName);
+ mpRenderLayers[RenderEnums::LevelSlot]->DumpDynaLoad(GiveItAFuckinName, mEntityDeletionList);
+ }
+
+BEGIN_PROFILE( "Trigger IntDump Event" );
+ if(mpZEL->IsInteriorDump())
+ {
+ GetEventManager()->TriggerEvent( EVENT_INTERIOR_DUMPED );
+ }
+END_PROFILE( "Trigger IntDump Event" );
+
+END_PROFILE( "Dump Zones" );
+
+ //MunchDelList(50);
+
+END_PROFILE( "Zone/Int Dump" );
+
+ //////////////////////////////////////////////////////////////////////////
+ // Find a Zone to load
+ //////////////////////////////////////////////////////////////////////////
+BEGIN_PROFILE( "Find Load Zone" );
+ i=-1;
+ for(i;i<mpZEL->GetNumLoadZones()&&alreadyLoaded; )
+ {
+ i++;
+ if(i<mpZEL->GetNumLoadZones())
+ {
+ HeapMgr()->PushHeap (GMA_TEMP);
+ GiveItAFuckinName.SetText(mpZEL->GetLoadZone(i));
+ HeapMgr()->PopHeap ( GMA_TEMP );
+
+ alreadyLoaded= ! mpRenderLayers[RenderEnums::LevelSlot]->DoPreDynaLoad(GiveItAFuckinName);
+ }
+ }
+END_PROFILE( "Find Load Zone" );
+
+ //////////////////////////////////////////////////////////////////////////
+ // Zone Loading
+ //////////////////////////////////////////////////////////////////////////
+ if(i<mpZEL->GetNumLoadZones())
+ {
+ sprintf(spSomeDamnFile,"ART\\%s",mpZEL->GetLoadZone(i));
+
+ msLayer &= ~RenderEnums::CompletionOnlyMask;
+ msLayer |= RenderEnums::DynamicLoadComplete;
+
+ msLayer &= ~RenderEnums::ZoneMask;
+ msLayer |= i<<RenderEnums::ZoneShift;
+
+ msLayer &= ~RenderEnums::LayerOnlyMask;
+ msLayer |= RenderEnums::LevelSlot;
+
+ RedirectChunks(RenderEnums::LevelSlot);
+
+BEGIN_PROFILE( "Add Requests Int" );
+#ifndef RAD_RELEASE
+ static int loadTag4 = 0;
+ LoadTag( spSomeDamnFile, loadTag4 );
+#endif
+
+#ifdef LOAD_SYNC
+ HeapMgr()->DumpHeapStats( true );
+ GetLoadingManager()->LoadSync( FILEHANDLER_PURE3D, spSomeDamnFile, GMA_LEVEL_ZONE, mpZEL->GetLoadZone(i) );
+ HeapMgr()->DumpHeapStats( true );
+#else
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, spSomeDamnFile, GMA_LEVEL_ZONE, mpZEL->GetLoadZone(i), NULL, this, &msLayer);
+#endif
+
+END_PROFILE( "Add Requests Int" );
+
+ rReleasePrintf("ZoneLoadingStart--===--%s--===--\n", spSomeDamnFile);
+ mbDynaLoading = true;
+#ifdef LOAD_SYNC
+ GetLoadingManager()->AddCallback( this, &msLayer );
+#endif
+ }
+ else
+ {
+ rReleasePrintf("Nothing to load in that Zone, checking for others...\n");
+ if( mZELs.mUseSize != 0 )
+ {
+ mpZEL = mZELs[0];
+ mZELs.RemoveKeepOrder(0);
+
+ msLayer &= ~RenderEnums::ZoneMask;
+ msLayer |= i<<RenderEnums::ZoneShift;
+
+ rReleasePrintf("Attemptimg to Load a Driving Too Fast Zone.\n");
+
+ mbDynaLoading = false;
+ mbDrivingTooFastLoad = true;
+ HandleEvent((EventEnum)(EVENT_LOCATOR+LocatorEvent::DYNAMIC_ZONE), mpZEL);
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_ALL_DYNAMIC_ZONE_END );
+ rReleasePrintf("ZoneLoadingEnded Verified\n");
+ mbDynaLoading = false;
+ AllWrappers::GetInstance()->mLoader( AllWrappers::msBillboard ).ModRegdListener( this, RenderEnums::IgnoreGuts );
+ }
+
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+END_PROFILE( "RenderManager HandleEvent" );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//========================================================================
+// RenderManager::InitLayers
+//========================================================================
+//
+// Description: Initialize all Layers
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void RenderManager::InitLayers()
+{
+MEMTRACK_PUSH_GROUP( "RenderManager" );
+ HeapMgr()->PushHeap (GMA_PERSISTENT);
+
+ for( int i=RenderEnums::numLayers-1; i>-1; i-- )
+ {
+ switch(i)
+ {
+ case RenderEnums::LevelSlot:
+ mpRenderLayers[i] = new WorldRenderLayer();
+ break;
+ case RenderEnums::GUI:
+ mpRenderLayers[i] = new FrontEndRenderLayer();
+ mpRenderLayers[i]->DoAllSetups();
+ break;
+ case RenderEnums::PresentationSlot:
+ mpRenderLayers[ i ] = new RenderLayer();
+ mpRenderLayers[ i ]->SetBeginView( false );
+ mpRenderLayers[ i ]->DoAllSetups();
+ break;
+ default:
+ mpRenderLayers[i] = new RenderLayer();
+ mpRenderLayers[i]->DoAllSetups();
+ break;
+ }
+ mpRenderLayers[i]->SetUpViewCam();
+ }
+
+ HeapMgr()->PopHeap (GMA_PERSISTENT);
+MEMTRACK_POP_GROUP( "RenderManager" );
+}
+
+void RenderManager::InitLevel()
+{
+ /*
+ for( unsigned int i = 0; i < RenderEnums::numLayers-1; i++ )
+ {
+ mpRenderLayers[ i ]->DoPostStaticLoad();
+ }
+
+ mpRenderLayers[ RenderEnums::LevelSlot ]->Resurrect();
+
+ for( unsigned int i = 0; i < GetGameplayManager()->GetNumPlayers(); i++ )
+ {
+ mpRenderLayers[RenderEnums::LevelSlot]->AddGuts((tDrawable*)(GetAvatarManager( )->GetAvatarForPlayer( i )->GetVehicle() ) );
+ mpRenderLayers[RenderEnums::LevelSlot]->AddGuts((tDrawable*)(GetAvatarManager( )->GetAvatarForPlayer( i )->GetCharacter()->GetDrawablePose() ) );
+ }
+
+ //Find and attach a light to the view
+ //MS7, this should go somewhere else.
+ tLight* sun = p3d::find<tLight>("sun");
+ rAssert( sun );
+
+ for( unsigned int i = 0; i < mpRenderLayers[RenderEnums::LevelSlot]->GetNumViews(); i++ )
+ {
+ mpRenderLayers[RenderEnums::LevelSlot]->pView( i )->AddLight( sun );
+ }
+
+ //Get the clouds too!
+ mClouds = p3d::find<tMultiController>("CloudController");
+ rAssert( mClouds );
+ */
+}
+
+void RenderManager::SetLevelLayerLights( tLightGroup* SunGroup )
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ if ( mMood.mOriginals != 0 )
+ {
+ delete [] mMood.mOriginals;
+ mMood.mOriginals = 0;
+ }
+ mMood.mSunGroup = SunGroup;
+ if ( SunGroup != NULL )
+ {
+ // Mood lighting is enabled, set the new parameters
+ rAssert( SunGroup );
+ mMood.mVolumeCount = 0;
+ mMood.mTransition = -1.0f;
+ mMood.mSrcModulus.Set( 0xff, 0xff, 0xff );
+ mMood.mDstModulus.Set( 0xff, 0xff, 0xff );
+ mMood.mOriginals = new tColour[ SunGroup->GetNumLights() ];
+ rAssert( mMood.mOriginals );
+
+ for( int j = 0; j < SunGroup->GetNumLights(); ++j )
+ {
+ mMood.mOriginals[ j ] = SunGroup->GetLight( j )->GetColour();
+ }
+ RenderLayer* rl = mpLayer( RenderEnums::LevelSlot );
+ rAssert( rl );
+ for( unsigned int i = 0; i < rl->GetNumViews(); ++i )
+ {
+ for(int j = 0; j < SunGroup->GetNumLights(); ++j )
+ {
+ rl->pView( i )->AddLight( SunGroup->GetLight(j) );
+ }
+ }
+ }
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+}
+
+void RenderManager::ClearLevelLayerLights()
+{
+ delete [] mMood.mOriginals;
+ mMood.mOriginals = 0;
+ mMood.mSunGroup = 0;
+}
+
+
+//==============================================================================
+// RenderManager::RenderManager
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+RenderManager::RenderManager() :
+//MS7: Cary
+ mClouds( NULL ),
+#ifdef DEBUGWATCH
+ mDebugDumpAllZones( false ),
+#endif
+ mEnableMotionBlur( false ),
+ mBlurAlpha( 0 )
+{
+ mEntityDeletionList.Allocate(5000);
+#ifdef DEBUGWATCH
+ radDbgWatchAddUnsignedInt( &mDebugRenderTime, "Debug Render All Layers micros", "RenderManager", NULL, NULL );
+ radDbgWatchAddUnsignedInt( &mDebugSwapTime, "Debug Render Swap micros", "RenderManager", NULL, NULL );
+ radDbgWatchAddBoolean( &mDebugDumpAllZones, "Dump All Zones", "RenderManager", NULL, NULL );
+#ifdef RAD_PS2
+ radDbgWatchAddFloat( &MIN_PS2_BLUR, "Minimum PS2 motion blur", "RenderManager", NULL, NULL );
+#endif
+
+// radDbgWatchAddFloat( &BLUR_ALPHA, "Blur Alpha", "RenderManager", NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &BLUR_SCALE, "Blur Scale", "RenderManager", NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &MAX_BLUR, "Blur max", "RenderManager", NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddBoolean( &ENABLE_MOTION_BLUR, "Enable Blur", "RenderManager" );
+
+// radDbgWatchAddFloat( &BLUR_ALPHA, "Blur Alpha", "RenderManager", NULL, NULL, 0.0f, 1.0f );
+ radDbgWatchAddFloat( &BLUR_SCALE, "Blur Scale", "RenderManager", NULL, NULL, 0.0f, 1.0f );
+
+#endif
+ InitLayers();
+ mDoneInitialLoad = false;
+ mbLoadZonesDumped = false;
+ mbIgnoreVisibilityClear = false;
+ mbInVsibilityVolume = false;
+
+ mZELs.Allocate(10);
+
+
+}
+
+//==============================================================================
+// RenderManager::~RenderManager
+//==============================================================================
+//
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================//
+RenderManager::~RenderManager()
+{
+#ifdef DEBUGWATCH
+ radDbgWatchDelete(&mDebugRenderTime);
+ radDbgWatchDelete(&mDebugSwapTime);
+// radDbgWatchDelete(&BLUR_ALPHA);
+ radDbgWatchDelete(&BLUR_SCALE);
+ radDbgWatchDelete(&MAX_BLUR);
+ radDbgWatchDelete(&ENABLE_MOTION_BLUR );
+#endif
+}
+
+RenderManager::MoodLighting::MoodLighting() :
+mSunGroup( 0 ),
+mSrcModulus( 0xffffffff ),
+mDstModulus( 0xffffffff ),
+mOriginals( 0 ),
+mTransition( -1.0f )
+{
+}
+
+RenderManager::MoodLighting::~MoodLighting()
+{
+ delete mOriginals;
+ mOriginals = 0;
+ mSunGroup = 0;
+}
+
+tColour RenderManager::MoodLighting::CalculateModulus( void )
+{
+ int red = rmt::Clamp( mSrcModulus.Red() - int( ( mSrcModulus.Red() - mDstModulus.Red() ) * mTransition ), 0, 0xFF );
+ int green = rmt::Clamp( mSrcModulus.Green() - int( ( mSrcModulus.Green() - mDstModulus.Green() ) * mTransition ), 0, 0xFF );
+ int blue = rmt::Clamp( mSrcModulus.Blue() - int( ( mSrcModulus.Blue() - mDstModulus.Blue() ) * mTransition ), 0, 0xFF );
+ int alpha = rmt::Clamp( mSrcModulus.Alpha() - int( ( mSrcModulus.Alpha() - mDstModulus.Alpha() ) * mTransition ), 0, 0xFF );
+ return tColour( red, green, blue, alpha );
+}
+
+void RenderManager::SetLightMod( const tColour& LightMod )
+{
+ if( LightMod.c != mMood.mDstModulus.c )
+ {
+ mMood.mSrcModulus = mMood.CalculateModulus();
+ mMood.mDstModulus = LightMod;
+ mMood.mTransition = 0.0f;
+ }
+}
+
+
+void RenderManager::TransitionMoodLighting( unsigned int ElapsedTime )
+{
+ // Check for no mood lighting (supersprint doesnt have any)
+ if ( mMood.mSunGroup == NULL )
+ return;
+
+ const float TIME_RATIO = ( 1.0f / 1000.0f ) / 1.0f; // Transition over 1 second.
+ float deltaTransition = (float)ElapsedTime * TIME_RATIO;
+ mMood.mTransition += deltaTransition;
+ if( mMood.mTransition > 1.0f )
+ {
+ mMood.mTransition = 1.0f;
+ }
+
+ tColour curMod = mMood.CalculateModulus();
+
+ for( int i = 0; i < mMood.mSunGroup->GetNumLights(); ++i )
+ {
+ tLight* l = mMood.mSunGroup->GetLight( i );
+ if( curMod.c == 0xffffffff )
+ {
+ l->SetColour( mMood.mOriginals[ i ] );
+ }
+ else
+ {
+ int red = ( ( mMood.mOriginals[ i ].Red() * curMod.Red() ) + 0x80 ) >> 8;
+ int green = ( ( mMood.mOriginals[ i ].Green() * curMod.Green() ) + 0x80 ) >> 8;
+ int blue = ( ( mMood.mOriginals[ i ].Blue() * curMod.Blue() ) + 0x80 ) >> 8;
+ int alpha = ( ( mMood.mOriginals[ i ].Alpha() * curMod.Alpha() ) + 0x80 ) >> 8;
+ l->SetColour( tColour( red, green, blue ) );
+ }
+ }
+
+ if( mMood.mTransition == 1.0f )
+ {
+ mMood.mSrcModulus = mMood.mDstModulus;
+ mMood.mTransition = -1.0f;
+ }
+}
+
+void RenderManager::ResetMoodLighting( bool Immediate )
+{
+ if ( mMood.mSunGroup == NULL )
+ return;
+
+ mMood.mVolumeCount = 0;
+ if( Immediate )
+ {
+ mMood.mTransition = -1.0f;
+ mMood.mSrcModulus.c = 0xFFFFFFFF;
+ mMood.mDstModulus.c = 0xFFFFFFFF;
+ for( int i = 0; i < mMood.mSunGroup->GetNumLights(); ++i )
+ {
+ tLight* l = mMood.mSunGroup->GetLight( i );
+ l->SetColour( mMood.mOriginals[ i ] );
+ }
+ }
+ else
+ {
+ mMood.mSrcModulus = mMood.CalculateModulus();
+ mMood.mDstModulus.c = 0xFFFFFFFF;
+ mMood.mTransition = 0.0f;
+ }
+}
+
+#ifdef RAD_PS2
+// Bump up the blur to a minimum level on the PS2
+void RenderManager::ApplyPS2Blur()
+{
+ // We only want blur in game, not in the frontend.
+ if ( mpRenderLayers[ RenderEnums::LevelSlot ]->IsRenderReady() )
+ {
+ float blurThreshold = GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_TRIPPY ) ? MIN_PS2_BLUR_CHEAT : MIN_PS2_BLUR;
+ if ( mBlurAlpha < blurThreshold )
+ {
+ mBlurAlpha = blurThreshold;
+ }
+ }
+ else
+ {
+ mBlurAlpha = 0;
+ }
+
+}
+#endif
+
+void RenderManager::AdjustBlurByFrameRate( unsigned int elapsedTime )
+{
+ // Lets adjust the blur effect if the framerate gets too bad
+ // Blurring effect is scaled linearly
+ // it kicks in at BLUR_START
+ // So y = m(x - x1) + y1
+ // blur_alpha = blur_gradient ( elapsedtime - blur_start )
+
+ float blur;
+ // We only want blur in game, not in the frontend.
+ if ( mpRenderLayers[ RenderEnums::LevelSlot ]->IsRenderReady() )
+ {
+ float alpha = BLUR_GRADIENT * ( elapsedTime - BLUR_START );
+ blur = rmt::Clamp( alpha, 0.0f, MAX_BLUR );
+ }
+ else
+ {
+ blur = 0;
+ }
+ if ( blur > mBlurAlpha )
+ mBlurAlpha = blur;
+}
+
+
+//==============================================================================
+// AvgTimeCounter::AvgTimeCounter
+//==============================================================================
+//
+// Description: Ctor.
+//
+// Intialize the AvgTimeCounter class with idealized initial values
+// i.e. first run it will report an average milliseconds elapsed of 17
+//
+// Return: N/A.
+//
+//==============================================================================//
+
+RenderManager::AvgTimeCounter::AvgTimeCounter():
+mCurrentArrayIndex( 0 )
+{
+ const unsigned int IDEAL_ELAPSED_TIME = 17;
+ // Division replaced by a bit shift if array is a power of two right?
+ mElapsedTimeCount.Allocate( ELAPSED_TIME_ARRAY_SIZE );
+ for ( int i = 0 ; i < ELAPSED_TIME_ARRAY_SIZE ; i++ )
+ {
+ // Fill with the idealized time, i.e. a rock solid 60fps.
+ mElapsedTimeCount.Add( IDEAL_ELAPSED_TIME );
+ }
+ mElapsedTimeSum = IDEAL_ELAPSED_TIME * ELAPSED_TIME_ARRAY_SIZE;
+}
+//==============================================================================
+// AvgTimeCounter::Tick
+//==============================================================================
+//
+// Description: Call this function every frame to update the average fps count
+//
+//
+//
+//
+// Return: N/A.
+//
+//==============================================================================//
+
+void
+RenderManager::AvgTimeCounter::Tick( unsigned int elapsedTime )
+{
+ // New frame
+ // Adjust the sum
+ mElapsedTimeSum -= mElapsedTimeCount[ mCurrentArrayIndex ];
+ mElapsedTimeSum += elapsedTime;
+ // update the mElapsedTimeCount array
+ mElapsedTimeCount[ mCurrentArrayIndex ] = elapsedTime;
+ mCurrentArrayIndex++;
+ // Wrap around to the start of the array
+ if ( mCurrentArrayIndex >= mElapsedTimeCount.mUseSize )
+ mCurrentArrayIndex = 0;
+}
+
+unsigned int
+RenderManager::AvgTimeCounter::GetAverageTimePerFrame()const
+{
+ return mElapsedTimeSum / ELAPSED_TIME_ARRAY_SIZE;
+}
+
diff --git a/game/code/render/RenderManager/RenderManager.h b/game/code/render/RenderManager/RenderManager.h
new file mode 100644
index 0000000..b5f9b63
--- /dev/null
+++ b/game/code/render/RenderManager/RenderManager.h
@@ -0,0 +1,228 @@
+#ifndef __RENDER_MANAGER_H__
+#define __RENDER_MANAGER_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: RenderManager
+//
+// Description: The RenderManager Controller provides all interfaces
+// neccessary for interaction with the render systems from
+// Systems external to the Render directory. [4/17/2002]
+//
+// History: + Implemented Initial interfaces -- Devin [4/17/2002]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+//#include <render/RenderManager/RenderLayer.h>
+#include <render/Culling/SwapArray.h>
+#include <render/Enums/RenderEnums.h>
+#include <render/Loaders/ChunkListenerCallback.h>
+#include <loading/loadingmanager.h>
+#include <events/eventlistener.h>
+#include <raddebugwatch.hpp>
+
+//fwd dec's
+class IEntityDSG;
+class RenderLayer;
+class WorldRenderLayer;
+class WorldScene;
+class tLightGroup;
+class ZoneEventLocator;
+
+//MS7: Cary added this to make the clouds go.
+class tMultiController;
+
+//=================================================
+// TODOs: Re-encapsulate (into other file(s)) the
+// data-sets below:
+//=================================================
+
+//========================================================================
+//
+// Synopsis: The RenderManager;
+// -The entry point for all non render systems
+// -Supports
+// -ContextSwitch Interface --Devin[4/17/2002]
+// -DSG Interface
+//========================================================================
+class RenderManager
+: public ChunkListenerCallback,
+ public LoadingManager::ProcessRequestsCallback,
+ public EventListener
+{
+public:
+
+ // Static Methods (for creating, destroying and acquiring an instance
+ // of the RenderManager)
+ static RenderManager* CreateInstance();
+ static RenderManager* GetInstance();
+ static void DestroyInstance();
+
+ ///////////////////////////////////////////////////////////////////////
+ // Accessors
+ ///////////////////////////////////////////////////////////////////////
+ WorldScene* pWorldScene();
+ WorldRenderLayer* pWorldRenderLayer();
+ inline int& rCurWorldRenderLayer();
+
+ // Context Interface
+ bool LoadAllNeededData();
+ void SetLoadData( RenderEnums::LayerEnum isLayer,
+ RenderEnums::LevelEnum isLevel,
+ RenderEnums::MissionEnum isMission );
+
+ void InitLevel();
+ // Copies light values from the given sun group into the RenderManager
+ // Dynamic allocations on GMA_LEVEL_OTHER
+ // Pass in NULL to disable
+ void SetLevelLayerLights( tLightGroup* SunGroup );
+ // Releases sun group information set in SetLevelLayerLights
+ void ClearLevelLayerLights();
+
+ void SetLightMod( const tColour& LightMod );
+ void ResetMoodLighting( bool Immediate = false );
+ void DumpAllLoadedData();
+ void ContextUpdate( unsigned int iElapsedTime );
+
+ // Layer Interface
+ RenderLayer* mpLayer( RenderEnums::LayerEnum isLayer );
+ void FreezeAllLayers();
+ void FreezeForPresentation( void );
+ void ThawFromPresentation( void );
+
+ void RedirectChunks( int ChunkDestinationMask );
+
+ ///////////////////////////////////////////////////////////////////////
+ // ChunkListenerCallback's
+ ///////////////////////////////////////////////////////////////////////
+ virtual void OnChunkLoaded( tEntity* ipEntity,
+ int iUserData,
+ unsigned iChunkID );
+
+ ///////////////////////////////////////////////////////////////////////
+ // LoadingManager::ProcessRequestsCallback's
+ ///////////////////////////////////////////////////////////////////////
+ void OnProcessRequestsComplete( void* pUserData );
+ bool DoneInitialLoad();
+ void DoPostLevelLoad();
+
+ ///////////////////////////////////////////////////////////////////////
+ // EventListener
+ ///////////////////////////////////////////////////////////////////////
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ void FlushDelList();
+ void MunchDelList(unsigned howManyMicroseconds);
+
+ SwapArray<tRefCounted*> mEntityDeletionList;
+ void SetBlurAlpha( float alpha ) { mBlurAlpha = alpha; }
+private:
+ //Initialize the mpLayers
+ void InitLayers();
+
+ // Private: it's a fuckin singleton.
+ RenderManager();
+ ~RenderManager();
+
+ // Private Member Data
+ RenderLayer* mpRenderLayers[RenderEnums::numLayers];
+ tMultiController* mClouds;
+
+ // Static Private Render Data
+ static RenderManager* mspInstance;
+
+ int msLayer; //RenderEnums::LayerEnum
+ int msLevel; //RenderEnums::LevelEnum
+ int msMission; //RenderEnums::MissionEnum
+
+ int mCurWorldLayer;
+
+ bool mDoneInitialLoad;
+
+ SwapArray<ZoneEventLocator*> mZELs;
+ ZoneEventLocator* mpZEL;
+ bool mbDynaLoading;
+ bool mbFirstDynamicZone;
+ bool mbDrivingTooFastLoad;
+ bool mbLoadZonesDumped;
+ bool mbIgnoreVisibilityClear;
+ bool mbInVsibilityVolume;
+ // Motion blur accessors / mutator functions
+ void SetMotionBlurEnable( bool enable ) { mEnableMotionBlur = enable; }
+ bool IsBlurEnabled() const { return mEnableMotionBlur; }
+
+#ifdef RAD_PS2
+ void ApplyPS2Blur();
+#endif
+ void AdjustBlurByFrameRate( unsigned int elapsedTime );
+
+#ifdef DEBUGWATCH
+ unsigned int mDebugSwapTime, mDebugRenderTime;
+ bool mDebugDumpAllZones;
+#endif
+
+ bool mEnableMotionBlur;
+ float mBlurAlpha;
+
+ // Simple class to log the time it took each frame and
+ // average it out
+ class AvgTimeCounter
+ {
+ public:
+ AvgTimeCounter();
+ void Tick( unsigned int elapsedTime );
+ unsigned int GetAverageTimePerFrame()const;
+
+ enum { ELAPSED_TIME_ARRAY_SIZE = 32 };
+
+ private:
+
+ // Index to the current spot in mElapsedTimeCount. Increment every tick
+ int mCurrentArrayIndex;
+ // Sum of all values in the mElapsedTimeCount array
+ unsigned int mElapsedTimeSum;
+ // Array holds the elapsed time counts for the past N updates
+ SwapArray< unsigned int > mElapsedTimeCount;
+ };
+
+
+ struct MoodLighting
+ {
+ MoodLighting();
+ ~MoodLighting();
+ tLightGroup* mSunGroup;
+ tColour mSrcModulus;
+ tColour mDstModulus;
+ tColour* mOriginals;
+ float mTransition;
+ int mVolumeCount; // Count how many volumes of the same modulus we've entered.
+ tColour CalculateModulus( void );
+ };
+ MoodLighting mMood;
+ void TransitionMoodLighting( unsigned int elapsedTime );
+};
+
+//
+// A little syntactic sugar for getting at this singleton.
+//
+inline RenderManager* GetRenderManager()
+{
+ return( RenderManager::GetInstance() );
+}
+
+
+inline int& RenderManager::rCurWorldRenderLayer()
+{
+ return mCurWorldLayer;
+}
+
+
+#endif
diff --git a/game/code/render/RenderManager/WorldRenderLayer.cpp b/game/code/render/RenderManager/WorldRenderLayer.cpp
new file mode 100644
index 0000000..cb2fdb3
--- /dev/null
+++ b/game/code/render/RenderManager/WorldRenderLayer.cpp
@@ -0,0 +1,2080 @@
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: WorldRenderLayer.cpp
+//
+// Description: Implementation for WorldRenderLayer class.
+//
+// History: Implemented --Devin [4/23/2002]
+//========================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebugwatch.hpp>
+#include <radtime.hpp>
+#include <p3d/billboardobject.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/DSG/DynaPhysDSG.h>
+#include <render/DSG/StaticPhysDSG.h>
+#include <render/DSG/IntersectDSG.h>
+#include <render/DSG/StaticEntityDSG.h>
+#include <render/DSG/FenceEntityDSG.h>
+#include <render/DSG/AnimCollisionEntityDSG.h>
+#include <render/DSG/AnimEntityDSG.h>
+#include <render/DSG/WorldSphereDSG.h>
+#include <roads/roadsegment.h>
+#include <pedpaths/pathsegment.h>
+#include <meta/triggervolume.h>
+
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/coins/sparkle.h>
+#include <worldsim/character/footprint/footprintmanager.h>
+
+#include <worldsim/WorldPhysicsManager.h>
+
+#include <meta/triggervolumetracker.h> // HACK for drawing the trigger volumes
+
+#include <render/breakables/breakablesmanager.h>
+#include <render/particles/particlemanager.h>
+#include <render/animentitydsgmanager/animentitydsgmanager.h>
+
+#include <p3d/shadow.hpp>
+#include <p3d/view.hpp>
+
+#include <events/eventmanager.h>
+#include <events/eventenum.h>
+
+#include <data/persistentworldmanager.h>
+
+#include <ai/vehicle/vehicleairender.h>
+
+#include <render/Loaders/BillboardWrappedLoader.h>
+#include <worldsim/avatarmanager.h>
+
+#ifdef RAD_PS2
+#define DEFAULT_R 67;
+#define DEFAULT_G 67;
+#define DEFAULT_B 67;
+#elif defined(RAD_XBOX)
+#define DEFAULT_R 67;
+#define DEFAULT_G 67;
+#define DEFAULT_B 67;
+#elif defined(RAD_WIN32)
+#define DEFAULT_R 67;
+#define DEFAULT_G 67;
+#define DEFAULT_B 67;
+#else
+#define DEFAULT_R 67;
+#define DEFAULT_G 67;
+#define DEFAULT_B 67;
+#endif
+
+static unsigned char gWashColourR = DEFAULT_R;
+static unsigned char gWashColourG = DEFAULT_G;
+static unsigned char gWashColourB = DEFAULT_B;
+
+//************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Public Member Functions : WorldRenderLayer Interface
+//
+//************************************************************************
+
+//========================================================================
+// WorldRenderLayer::WorldRenderLayer
+//========================================================================
+//
+// Description: Do some init stuff
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+WorldRenderLayer::WorldRenderLayer()
+{
+ mQdDump = false;
+ OnWorldRenderLayerInit();
+ //mpShadowGenerator = NULL; // VolShadows
+
+#ifdef DEBUGWATCH
+ static bool firstTimeAdding = true;
+
+ radDbgWatchAddUnsignedInt( &mDebugInnerRenderTime, "Inner Render Time", "World Render Layer" );
+ radDbgWatchAddUnsignedInt( &mDebugRenderTime, "Render Time", "World Render Layer" );
+ radDbgWatchAddUnsignedInt( &mDebugGutsTime, "Guts Time", "World Render Layer" );
+
+ if ( firstTimeAdding )
+ {
+ radDbgWatchAddUnsignedChar( &gWashColourR, "Colour R", "Shadows" );
+ radDbgWatchAddUnsignedChar( &gWashColourG, "Colour G", "Shadows" );
+ radDbgWatchAddUnsignedChar( &gWashColourB, "Colour B", "Shadows" );
+ firstTimeAdding = false;
+ }
+#endif
+
+ mMirror = false;
+}
+
+//========================================================================
+// WorldRenderLayer::~WorldRenderLayer()
+//========================================================================
+//
+// Description: Quick! to the Garbage heap!
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+WorldRenderLayer::~WorldRenderLayer()
+{
+ NullifyGuts();
+ //delete mpShadowGenerator; // VolShadows
+
+#ifdef DEBUGWATCH
+ static bool firstTimeRemoving = true;
+
+ radDbgWatchDelete( &mDebugInnerRenderTime );
+ radDbgWatchDelete( &mDebugRenderTime );
+ radDbgWatchDelete( &mDebugGutsTime );
+
+ if ( firstTimeRemoving )
+ {
+ radDbgWatchDelete( &gWashColourR );
+ radDbgWatchDelete( &gWashColourG );
+ radDbgWatchDelete( &gWashColourB );
+ firstTimeRemoving = false;
+ }
+#endif
+
+}
+
+static bool simpleShadows = true;
+
+//////////////////////////////////////////////////////////////////////////
+// Render Interface
+//////////////////////////////////////////////////////////////////////////
+//========================================================================
+// WorldRenderLayer::Render
+//========================================================================
+//
+// Description: Whacha got? Render it.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::Render()
+{
+ BEGIN_PROFILE( "WRL Render" );
+#ifdef DEBUGWATCH
+ mDebugRenderTime = radTimeGetMicroseconds();
+#endif
+ rAssert(!IsDead());
+
+ BEGIN_PROFILE( "pddi ZBuf" );
+ p3d::pddi->EnableZBuffer(true);
+ END_PROFILE( "pddi ZBuf" );
+
+ // VolShadows
+ /*
+ if(mpShadowGenerator == NULL)
+ {
+ mpShadowGenerator = new tShadowGenerator;
+ }
+
+ BEGIN_PROFILE( "PreRender Shadows" );
+ mpShadowGenerator->PreRender();
+ END_PROFILE( "PreRender Shadows" );
+ */
+
+ // Hack for the number of players
+ for ( unsigned int view = 0; view < GetNumViews(); view++ )
+ {
+#ifdef DEBUGWATCH
+ mDebugInnerRenderTime = radTimeGetMicroseconds();
+#endif
+
+ for(int mirrorPass = mMirror ? 1 : 0; mirrorPass >= 0; mirrorPass--)
+ {
+ rmt::Matrix originalCamera;
+ if(mirrorPass == 1)
+ {
+ tCamera * camera = mpView[ view ]->GetCamera();
+ originalCamera = camera->GetCameraToWorldMatrix();
+ rmt::Matrix mirroredCamera;
+ mirroredCamera.Mult(originalCamera, mMirrorMatrix);
+ camera->SetCameraMatrix(&mirroredCamera);
+ }
+
+ if(mMirror && (mirrorPass == 0))
+ {
+ mpView[ view ]->SetClearMask(PDDI_BUFFER_DEPTH);
+ }
+ else
+ {
+ mpView[ view ]->SetClearMask(PDDI_BUFFER_ALL);
+ }
+
+ BEGIN_PROFILE( "View Begin Render" );
+ mpView[ view ]->BeginRender();
+ END_PROFILE( "View Begin Render" );
+
+ int i;
+
+ if(!mMirror)
+ {
+ BEGIN_PROFILE( "Render World Spheres" );
+ p3d::pddi->EnableZBuffer(false);
+ for(i=0; i<mWorldSpheres.mUseSize; i++)
+ {
+ mWorldSpheres[i]->Display();
+ }
+ BEGIN_PROFILE( "pddi ZBuf" );
+ p3d::pddi->EnableZBuffer(true);
+ END_PROFILE( "pddi ZBuf" );
+ END_PROFILE( "Render World Spheres" );
+ }
+
+
+ BEGIN_PROFILE( "Render WorldScene" );
+ mpWorldScene->Render( view );
+ #ifdef DEBUGWATCH
+ mDebugInnerRenderTime = radTimeGetMicroseconds()-mDebugInnerRenderTime;
+ #endif
+ END_PROFILE( "Render WorldScene" );
+
+ //p3d::inventory->PushSection();
+ //p3d::inventory->SelectSection("Default");
+
+ mpWorldScene->RenderOpaque();
+
+ BEGIN_PROFILE( "Render coins" );
+ GetCoinManager()->Render();
+ END_PROFILE( "Render coins" );
+
+ // VolShadows
+ /*
+ BEGIN_PROFILE( "Render Shadows" );
+ // let's draw ourselves some shadows
+ mpShadowGenerator->Begin();
+ mpWorldScene->RenderShadows();
+ tColour washColour(gWashColourR, gWashColourG, gWashColourB);
+ mpShadowGenerator->SetWashColour(washColour);
+ mpShadowGenerator->End();
+ END_PROFILE( "Render Shadows" );
+ */
+ GetFootprintManager()->Render();
+ BEGIN_PROFILE( "Render Simple Shadows" );
+ mpWorldScene->RenderSimpleShadows();
+ END_PROFILE( "Render Simple Shadows" );
+
+ //Temp Disable BBQ optimisation for cars, as it may be outstripped by
+ // Kevin's fix to the art
+ BEGIN_PROFILE( "Render Shadow Casters" );
+ //mpWorldScene->RenderShadowCasters();
+ END_PROFILE( "Render Shadow Casters" );
+
+
+ BEGIN_PROFILE( "RenderTranslucent" );
+ mpWorldScene->RenderTranslucent();
+ END_PROFILE( "RenderTranslucent" );
+
+
+ BillboardQuadManager::Enable();
+ BEGIN_PROFILE( "BBQ Display All" );
+ GetBillboardQuadManager()->DisplayAll();
+ END_PROFILE( "BBQ Display All" );
+ BillboardQuadManager::Disable();
+
+ BEGIN_PROFILE( "RenderSparkles" );
+ // Render the procedural particle effects:
+ // coin glints, collection/spawn trail sparkles, hit sparks, etc.
+ GetSparkleManager()->Render( Sparkle::SRM_ExcludeSorted );
+
+ END_PROFILE( "RenderSparkles" );
+ //p3d::inventory->PopSection();
+
+ //Drawing the characters and stuff after the shadows to attempt to
+ //eliminate the bleeding shadows.
+ BEGIN_PROFILE( "Render Guts" );
+ for(i = mpGuts.mUseSize-1; i>-1; i-- )
+ {
+ mpGuts[i]->Display();
+ }
+ END_PROFILE( "Render Guts" );
+
+ BEGIN_PROFILE( "Render Trigger Volume Tracker" );
+ GetTriggerVolumeTracker()->Render();
+ END_PROFILE( "Render Trigger Volume Tracker" );
+
+ #ifdef DEBUGWATCH
+ BEGIN_PROFILE( "Render Vehicle AI Debug" );
+ VehicleAIRender::GetVehicleAIRender()->Display();
+ END_PROFILE( "Render Vehicle AI Debug" );
+ #endif
+
+ BEGIN_PROFILE( "Lens Flare Render" );
+ LensFlareDSG::DisplayAllFlares();
+ END_PROFILE( "Lens Flare Render" );
+
+ BEGIN_PROFILE( "View End Render" );
+ mpView[ view ]->EndRender();
+ END_PROFILE( "View End Render" );
+
+ if(mirrorPass == 1)
+ {
+ tCamera * camera = mpView[ view ]->GetCamera();
+ camera->SetCameraMatrix(&originalCamera);
+ }
+ if(GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_SHOW_TREE))
+ {
+ rmt::Vector posn, pTriPts[3], intPosn, intNorm;
+ IntersectDSG* pIntersectChunk;
+ AvatarManager::GetInstance()->GetAvatarForPlayer(0)->GetPosition(posn);
+ pIntersectChunk = GetIntersectManager()->FindIntersectionTri(posn,pTriPts,intPosn,intNorm);
+ if(pIntersectChunk!=NULL)
+ {
+ pddiCompareMode origZCompare = p3d::pddi->GetZCompare();
+ p3d::pddi->SetZCompare(PDDI_COMPARE_ALWAYS);
+ pIntersectChunk->Display();
+ pIntersectChunk->DrawTri(pTriPts, tColour(255,0,0));
+ p3d::pddi->SetZCompare(origZCompare);
+ }
+ }
+ }
+ }
+#ifdef DEBUGWATCH
+ mDebugRenderTime = radTimeGetMicroseconds()-mDebugRenderTime;
+#endif
+ END_PROFILE( "WRL Render" );
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+// Resource Interface
+//////////////////////////////////////////////////////////////////////////
+//========================================================================
+// WorldRenderLayer::AddGuts
+//========================================================================
+//
+// Description: Add a tDrawable
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+//void WorldRenderLayer::AddGuts( tDrawable* ipDrawable )
+//{
+// // This is a currently unsupported function
+// rAssert(false);
+//}
+
+//========================================================================
+// WorldRenderLayer::AddGuts
+//========================================================================
+//
+// Description: Add a tGeometry
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+//void WorldRenderLayer::AddGuts( tGeometry* ipGeometry )
+//{
+// mpWorldScene->Add( ipGeometry );
+//
+//}
+
+//========================================================================
+// WorldRenderLayer::AddGuts
+//========================================================================
+//
+// Description: add an IntersectDSG
+//
+// Parameters: IntersectDSG to add
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::AddGuts( IntersectDSG* ipIntersectDSG )
+{
+ mpWorldScene->Add( ipIntersectDSG );
+
+ if(mDynaLoadState==msLoad)
+ {
+ mLoadLists[mCurLoadIndex]->mIntersectElems.Add(ipIntersectDSG);
+ }
+ else
+ {
+ rAssert(mDynaLoadState==msPreLoads);
+ }
+}
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::AddGuts( StaticEntityDSG* ipStaticEntityDSG )
+{
+ mpWorldScene->Add( ipStaticEntityDSG );
+
+ if(mDynaLoadState==msLoad)
+ {
+ mLoadLists[mCurLoadIndex]->mSEntityElems.Add(ipStaticEntityDSG);
+ }
+ else
+ {
+ rAssert(mDynaLoadState==msPreLoads);
+ }
+}
+//========================================================================
+// RenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::AddGuts( StaticPhysDSG* ipStaticPhysDSG )
+{
+ mpWorldScene->Add( ipStaticPhysDSG );
+
+ if(mDynaLoadState==msLoad)
+ {
+ mLoadLists[mCurLoadIndex]->mSPhysElems.Add(ipStaticPhysDSG);
+ }
+ else
+ {
+ rAssert(mDynaLoadState==msPreLoads);
+ }
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::AddGuts( FenceEntityDSG* ipFenceEntityDSG )
+{
+ mpWorldScene->Add( ipFenceEntityDSG );
+
+ if(mDynaLoadState==msLoad)
+ {
+ mLoadLists[mCurLoadIndex]->mFenceElems.Add(ipFenceEntityDSG);
+ }
+ else
+ {
+ rAssert(mDynaLoadState==msPreLoads);
+ }
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::AddGuts( AnimCollisionEntityDSG* ipAnimCollDSG )
+{
+ mpWorldScene->Add( ipAnimCollDSG );
+
+ if(mDynaLoadState==msLoad)
+ {
+ mLoadLists[mCurLoadIndex]->mAnimCollElems.Add(ipAnimCollDSG);
+ // Add it to a list of managed animentitydsgs so that Update can be called on it every frame
+ GetAnimEntityDSGManager()->Add( ipAnimCollDSG );
+ }
+ else
+ {
+ rAssert(mDynaLoadState==msPreLoads);
+ }
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::AddGuts( AnimEntityDSG* ipAnimDSG )
+{
+ mpWorldScene->Add( ipAnimDSG );
+
+ if(mDynaLoadState==msLoad)
+ {
+ mLoadLists[mCurLoadIndex]->mAnimElems.Add(ipAnimDSG);
+ // Add it to a list of managed animentitydsgs so that Update can be called on it every frame
+ GetAnimEntityDSGManager()->Add( ipAnimDSG );
+ }
+ else
+ {
+ rAssert(mDynaLoadState==msPreLoads);
+ }
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::AddGuts( DynaPhysDSG* ipDynaPhysDSG )
+{
+ mpWorldScene->Add( ipDynaPhysDSG );
+
+ if(mDynaLoadState==msLoad)
+ {
+ mLoadLists[mCurLoadIndex]->mDPhysElems.Add(ipDynaPhysDSG);
+ }
+ else
+ {
+ rAssert(mDynaLoadState==msPreLoads);
+ }
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::AddGuts( TriggerVolume* ipTriggerVolume )
+{
+ mpWorldScene->Add( ipTriggerVolume );
+
+ if(mDynaLoadState==msLoad)
+ {
+ mLoadLists[mCurLoadIndex]->mTrigVolElems.Add(ipTriggerVolume);
+ }
+ else
+ {
+
+ rAssert(mDynaLoadState==msPreLoads);
+ }
+}
+
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::AddGuts( RoadSegment* ipRoadSegment )
+{
+ mpWorldScene->Add( ipRoadSegment );
+
+ if(mDynaLoadState==msLoad)
+ {
+ mLoadLists[mCurLoadIndex]->mRoadSegmentElems.Add(ipRoadSegment);
+ }
+ else
+ {
+ rAssert(mDynaLoadState==msPreLoads);
+ }
+}
+
+
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::AddGuts( PathSegment* ipPathSegment )
+{
+ mpWorldScene->Add( ipPathSegment );
+
+ if(mDynaLoadState==msLoad)
+ {
+ mLoadLists[mCurLoadIndex]->mPathSegmentElems.Add(ipPathSegment);
+ }
+ else
+ {
+ rAssert(mDynaLoadState==msPreLoads);
+ }
+}
+
+
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::AddGuts( WorldSphereDSG* ipWorldSphereDSG )
+{
+ ipWorldSphereDSG->AddRef();
+ mWorldSpheres.Add( ipWorldSphereDSG );
+
+ if(mDynaLoadState==msLoad)
+ {
+ mLoadLists[mCurLoadIndex]->mWorldSphereElems.Add(ipWorldSphereDSG);
+ }
+ else
+ {
+ rAssert(mDynaLoadState==msPreLoads);
+ }
+}
+
+//=============================================================================
+// WorldRenderLayer::GetCurSectionName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: tName
+//
+//=============================================================================
+tName& WorldRenderLayer::GetCurSectionName()
+{
+ return mLoadLists[mCurLoadIndex]->mGiveItAFuckinName;
+}
+
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::RemoveGuts( IEntityDSG* ipEDSG )
+{
+ rmt::Box3D BBox;
+ BoxPts BBoxSP;
+
+ int i,j;
+ for(i=0;i<mLoadLists.mUseSize;i++ )
+ {
+ //////////////////////////////////////////////////////////////////////////
+ // DPhys
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mDPhysElems.mUseSize;j++)
+ {
+ if(ipEDSG == mLoadLists[i]->mDPhysElems[j])
+ {
+ mLoadLists[i]->mDPhysElems.Remove(j);
+ mpWorldScene->Remove(ipEDSG);
+ return;
+ }
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Intersect
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mIntersectElems.mUseSize;j++)
+ {
+ if(ipEDSG == mLoadLists[i]->mIntersectElems[j])
+ {
+ mLoadLists[i]->mIntersectElems.Remove(j);
+ mpWorldScene->Remove(ipEDSG);
+ return;
+ }
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SEntity
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mSEntityElems.mUseSize;j++)
+ {
+ if(ipEDSG == mLoadLists[i]->mSEntityElems[j])
+ {
+ mLoadLists[i]->mSEntityElems.Remove(j);
+ mpWorldScene->Remove(ipEDSG);
+ return;
+ }
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SPhys
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mSPhysElems.mUseSize;j++)
+ {
+ if(ipEDSG == mLoadLists[i]->mSPhysElems[j])
+ {
+ mLoadLists[i]->mSPhysElems.Remove(j);
+ mpWorldScene->Remove(ipEDSG);
+ return;
+ }
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // AnimColl
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mAnimCollElems.mUseSize;j++)
+ {
+ if(ipEDSG == mLoadLists[i]->mAnimCollElems[j])
+ {
+ mLoadLists[i]->mAnimCollElems.Remove(j);
+ mpWorldScene->Remove(ipEDSG);
+ return;
+ }
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Anim
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mAnimElems.mUseSize;j++)
+ {
+ if(ipEDSG == mLoadLists[i]->mAnimElems[j])
+ {
+ mLoadLists[i]->mAnimElems.Remove(j);
+ mpWorldScene->Remove(ipEDSG);
+ return;
+ }
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Fences
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mFenceElems.mUseSize;j++)
+ {
+ if(ipEDSG == mLoadLists[i]->mFenceElems[j])
+ {
+ mLoadLists[i]->mFenceElems.Remove(j);
+ mpWorldScene->Remove(ipEDSG);
+ return;
+ }
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Trigger Volumes
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mTrigVolElems.mUseSize;j++)
+ {
+ if(ipEDSG == mLoadLists[i]->mTrigVolElems[j])
+ {
+ mLoadLists[i]->mTrigVolElems.Remove(j);
+ mpWorldScene->Remove(ipEDSG);
+ return;
+ }
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Road Segments
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mRoadSegmentElems.mUseSize;j++)
+ {
+ if(ipEDSG == mLoadLists[i]->mRoadSegmentElems[j])
+ {
+ mLoadLists[i]->mRoadSegmentElems.Remove(j);
+ mpWorldScene->Remove(ipEDSG);
+ return;
+ }
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Path Segments
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mPathSegmentElems.mUseSize;j++)
+ {
+ if(ipEDSG == mLoadLists[i]->mPathSegmentElems[j])
+ {
+ mLoadLists[i]->mPathSegmentElems.Remove(j);
+ mpWorldScene->Remove(ipEDSG);
+ return;
+ }
+ }
+ }
+
+ //Item ipEDSG was not found in any of the dynaloadlists
+ rTuneAssert(false);
+}
+
+
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::AddGuts( SpatialTree* ipSpatialTree )
+{
+ mpWorldScene->SetTree(ipSpatialTree);
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::ActivateWS(tUID iUID)
+{
+ for(int i=mWorldSpheres.mUseSize-1; i>-1; i--)
+ {
+ if(mWorldSpheres[i]->GetUID() == iUID)
+ {
+ mWorldSpheres[i]->Activate();
+ return;
+ }
+ }
+ //rAssert(false);
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::DeactivateWS(tUID iUID)
+{
+ for(int i=mWorldSpheres.mUseSize-1; i>-1; i--)
+ {
+ if(mWorldSpheres[i]->GetUID() == iUID)
+ {
+ mWorldSpheres[i]->Deactivate();
+ return;
+ }
+ }
+ // rAssert(false);
+}
+//========================================================================
+// WorldRenderLayer::NullifyGuts()
+//========================================================================
+//
+// Description: Get rid of it all! Burn. it. all. down.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::NullifyGuts()
+{
+ RenderLayer::NullifyGuts();
+// RenderLayer::DumpGuts();
+
+ DumpAllDynaLoads();
+
+ if( mpWorldScene != NULL )
+ {
+ delete mpWorldScene;
+ mpWorldScene = NULL;
+ }
+
+ mLoadLists.Clear();
+ mStaticLoadLists.Clear();
+ mWorldSpheres.Clear();
+
+ mDynaLoadState = msPreLoads;
+ mCurLoadIndex = -1;
+}
+
+//========================================================================
+// WorldRenderLayer::SetUpGuts()
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::SetUpGuts()
+{
+MEMTRACK_PUSH_GROUP( "WorldRenderLayer" );
+ HeapMgr()->PushHeap (GMA_LEVEL_ZONE);
+
+ RenderLayer::SetUpGuts();
+ rAssert( mpWorldScene == NULL );
+ mpWorldScene = new WorldScene;
+ mpWorldScene->Init(msMaxGuts);
+
+ mStaticLoadLists.Allocate(7);
+ mLoadLists.Allocate(7);
+
+ mLoadLists.AddUse(7);
+ mStaticLoadLists.AddUse(7);
+
+ mWorldSpheres.Allocate(10);
+
+ mDynaLoadState = msPreLoads;
+ mnLoadListRefs = 2000;
+ mCurLoadIndex = -1;
+
+ int i;
+ for(i=0;i<7;i++)
+ {
+ mStaticLoadLists[i].AllocateAll(mnLoadListRefs);
+ mLoadLists[i] = &(mStaticLoadLists[i]);
+ }
+
+ mLoadLists.ClearUse();
+
+
+ HeapMgr()->PopHeap (GMA_LEVEL_ZONE);
+MEMTRACK_POP_GROUP( "WorldRenderLayer" );
+}
+
+//************************************************************************
+// Load-Related Interface
+//************************************************************************
+void WorldRenderLayer::DoPreStaticLoad()
+{
+ if( !IsGutsSetup() )
+ SetUpGuts();
+
+ mExportedState = msFrozen;
+}
+
+void WorldRenderLayer::DoPostStaticLoad()
+{
+// mpWorldScene->GenerateSpatialReps();
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: unsigned int start.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::DumpAllDynaLoads( unsigned int start, SwapArray<tRefCounted*>& irEntityDeletionList )
+{
+ GetEventManager()->TriggerEvent( EVENT_INTERIOR_DUMPED );
+
+ rTuneAssert(mQdDump==false);
+
+ if(mDynaLoadState==msLoad)
+ {
+ rReleasePrintf("-=-=-=-=-=-=-=-Dump Queued-=-=-=-=-=-=-=-\n");
+ mQdDump = true;
+ mQdDeletionStart = start;
+ mpQdDeletionList = &irEntityDeletionList;
+ }
+ else
+ {
+ while( mLoadLists.mUseSize > static_cast<int>( start ) )
+ {
+ DumpDynaLoad(mLoadLists[start]->mGiveItAFuckinName, irEntityDeletionList );
+ }
+ }
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::DumpDynaLoad(tName& irGiveItAFuckinName, SwapArray<tRefCounted*>& irEntityDeletionList)
+{
+ //TODO: this is the ugliest, most embarrasing piece of code I've ever written.
+ // re-write.
+
+ rmt::Box3D BBox;
+ BoxPts BBoxSP;
+
+ int i,j,k;
+ for(i=0;i<mLoadLists.mUseSize;i++ )
+ {
+ if( mLoadLists[i]->mGiveItAFuckinName.GetUID() == irGiveItAFuckinName.GetUID() )
+ {
+BEGIN_PROFILE( "Remove Searches" );
+
+ GetEventManager()->TriggerEvent( EVENT_DUMP_DYNA_SECTION, (void*)(&(mLoadLists[i]->mGiveItAFuckinName)) );
+ //////////////////////////////////////////////////////////////////////////
+ // WorldSpheres
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mWorldSphereElems.mUseSize;j++)
+ {
+ for(k=0;k<mWorldSpheres.mUseSize;k++)
+ {
+ if(mLoadLists[i]->mWorldSphereElems[j] == mWorldSpheres[k])
+ {
+ irEntityDeletionList.Add((tRefCounted*&)mWorldSpheres[k]);
+ //mWorldSpheres[k]->Release();
+ mWorldSpheres.Remove(k);
+ k = mWorldSpheres.mUseSize +10;
+ }
+ }
+ rAssert( k= mWorldSpheres.mUseSize+10 );
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DPhys
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mDPhysElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mDPhysElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mDPhysElems[j]->mpSpatialNode);
+
+
+ for(k=0;k<rSNode.mDPhysElems.mUseSize;k++)
+ {
+ if( rSNode.mDPhysElems[k] == mLoadLists[i]->mDPhysElems[j] )
+ {
+ irEntityDeletionList.Add((tRefCounted*&)rSNode.mDPhysElems[k]);
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(rSNode.mDPhysElems[k]);
+ rSNode.mDPhysElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+
+ if(k!=-1)
+ {
+ rAssert(false);
+ //it might be in the root node, which now doubles as overflow...
+
+ SpatialNode& rRootNode = mpWorldScene->mStaticTreeWalker.rIthNode(0);
+
+ for(k=0;k<rRootNode.mDPhysElems.mUseSize;k++)
+ {
+ if( rRootNode.mDPhysElems[k] == mLoadLists[i]->mDPhysElems[j] )
+ {
+ irEntityDeletionList.Add((tRefCounted*&)rRootNode.mDPhysElems[k]);
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(rRootNode.mDPhysElems[k]);
+ rRootNode.mDPhysElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ }
+ //Cant find the dynaphys where the bbox says it is
+ rAssert(k==-1);
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SEntity
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mSEntityElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mSEntityElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mSEntityElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mSEntityElems.mUseSize;k++)
+ {
+ if( rSNode.mSEntityElems[k] == mLoadLists[i]->mSEntityElems[j] )
+ {
+ irEntityDeletionList.Add((tRefCounted*&)rSNode.mSEntityElems[k]);
+ rSNode.mSEntityElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ if(k!=-1)
+ {
+ rAssert(false);
+ //it might be in the root node, which now doubles as overflow...
+
+ SpatialNode& rRootNode = mpWorldScene->mStaticTreeWalker.rIthNode(0);
+
+ for(k=0;k<rRootNode.mSEntityElems.mUseSize;k++)
+ {
+ if( rRootNode.mSEntityElems[k] == mLoadLists[i]->mSEntityElems[j] )
+ {
+ irEntityDeletionList.Add((tRefCounted*&)rRootNode.mSEntityElems[k]);
+ rRootNode.mSEntityElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ }
+ rAssert(k==-1);
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // AnimColl
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mAnimCollElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mAnimCollElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mAnimCollElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mAnimCollElems.mUseSize;k++)
+ {
+ if( rSNode.mAnimCollElems[k] == mLoadLists[i]->mAnimCollElems[j] )
+ {
+ irEntityDeletionList.Add((tRefCounted*&)rSNode.mAnimCollElems[k]);
+ GetAnimEntityDSGManager()->Remove( rSNode.mAnimCollElems[k] );
+ rSNode.mAnimCollElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ if(k!=-1)
+ {
+ rAssert(false);
+ SpatialNode& rRootNode = mpWorldScene->mStaticTreeWalker.rIthNode(0);
+
+ for(k=0;k<rRootNode.mAnimCollElems.mUseSize;k++)
+ {
+ if( rRootNode.mAnimCollElems[k] == mLoadLists[i]->mAnimCollElems[j] )
+ {
+ irEntityDeletionList.Add((tRefCounted*&)rRootNode.mAnimCollElems[k]);
+ GetAnimEntityDSGManager()->Remove( rRootNode.mAnimCollElems[k] );
+ rRootNode.mAnimCollElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ }
+ rAssert(k==-1);
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Anim
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mAnimElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mAnimElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+
+ SpatialNode& rSNode = *(mLoadLists[i]->mAnimElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mAnimElems.mUseSize;k++)
+ {
+ if( rSNode.mAnimElems[k] == mLoadLists[i]->mAnimElems[j] )
+ {
+ // Remove it from the list of managed animentities
+ GetAnimEntityDSGManager()->Remove( rSNode.mAnimElems[k] );
+ irEntityDeletionList.Add((tRefCounted*&)rSNode.mAnimElems[k]);
+ rSNode.mAnimElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ if(k!=-1)
+ {
+ rAssert(false);
+ SpatialNode& rRootNode = mpWorldScene->mStaticTreeWalker.rIthNode(0);
+
+ for(k=0;k<rRootNode.mAnimElems.mUseSize;k++)
+ {
+ if( rRootNode.mAnimElems[k] == mLoadLists[i]->mAnimElems[j] )
+ {
+ // Remove it from the list of managed animentities
+ GetAnimEntityDSGManager()->Remove( rRootNode.mAnimElems[k] );
+ irEntityDeletionList.Add((tRefCounted*&)rRootNode.mAnimElems[k]);
+ rRootNode.mAnimElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ }
+ rAssert(k==-1);
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Intersect
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mIntersectElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mIntersectElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mIntersectElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mIntersectElems.mUseSize;k++)
+ {
+ if( rSNode.mIntersectElems[k] == mLoadLists[i]->mIntersectElems[j] )
+ {
+ irEntityDeletionList.Add((tRefCounted*&)rSNode.mIntersectElems[k]);
+ rSNode.mIntersectElems.Remove(k);
+ k = rSNode.mIntersectElems.mUseSize+10;
+ }
+ }
+ rAssert(k==rSNode.mIntersectElems.mUseSize+11);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // SPhys
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mSPhysElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mSPhysElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mSPhysElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mSPhysElems.mUseSize;k++)
+ {
+ if( rSNode.mSPhysElems[k] == mLoadLists[i]->mSPhysElems[j] )
+ {
+ irEntityDeletionList.Add((tRefCounted*&)rSNode.mSPhysElems[k]);
+ //rSNode.mSPhysElems[k]->Release();
+ // rSNode.mSPhysElems[k]->ReleaseVerified();
+ rSNode.mSPhysElems.Remove(k);
+ k = rSNode.mSPhysElems.mUseSize+10;
+ }
+ }
+ rAssert(k==rSNode.mSPhysElems.mUseSize+11);
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Fences
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mFenceElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mFenceElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mFenceElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mFenceElems.mUseSize;k++)
+ {
+ if( rSNode.mFenceElems[k] == mLoadLists[i]->mFenceElems[j] )
+ {
+ irEntityDeletionList.Add((tRefCounted*&)rSNode.mFenceElems[k]);
+ //rSNode.mFenceElems[k]->Release();
+ // rSNode.mSPhysElems[k]->ReleaseVerified();
+ rSNode.mFenceElems.Remove(k);
+ k = rSNode.mFenceElems.mUseSize+10;
+ }
+ }
+ rAssert(k==rSNode.mFenceElems.mUseSize+11);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Trigger Volumes
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mTrigVolElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mTrigVolElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mTrigVolElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mTrigVolElems.mUseSize;k++)
+ {
+ if( rSNode.mTrigVolElems[k] == mLoadLists[i]->mTrigVolElems[j] )
+ {
+ irEntityDeletionList.Add((tRefCounted*&)rSNode.mTrigVolElems[k]);
+ //rSNode.mTrigVolElems[k]->Release();
+ rSNode.mTrigVolElems.Remove(k);
+ k = rSNode.mTrigVolElems.mUseSize+10;
+ }
+ }
+ rAssert(k==rSNode.mTrigVolElems.mUseSize+11);
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Road Segments
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mRoadSegmentElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mRoadSegmentElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+
+ SpatialNode& rSNode = *(mLoadLists[i]->mRoadSegmentElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mRoadSegmentElems.mUseSize;k++)
+ {
+ if( rSNode.mRoadSegmentElems[k] == mLoadLists[i]->mRoadSegmentElems[j] )
+ {
+ irEntityDeletionList.Add((tRefCounted*&)rSNode.mRoadSegmentElems[k]);
+ //rSNode.mRoadSegmentElems[k]->Release();
+ rSNode.mRoadSegmentElems.Remove(k);
+ k = rSNode.mRoadSegmentElems.mUseSize+10;
+ }
+ }
+ rAssert(k==rSNode.mRoadSegmentElems.mUseSize+11);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Path Segments
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mPathSegmentElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mPathSegmentElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mPathSegmentElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mPathSegmentElems.mUseSize;k++)
+ {
+ if( rSNode.mPathSegmentElems[k] == mLoadLists[i]->mPathSegmentElems[j] )
+ {
+ irEntityDeletionList.Add((tRefCounted*&)rSNode.mPathSegmentElems[k]);
+ //rSNode.mPathSegmentElems[k]->Release();
+ rSNode.mPathSegmentElems.Remove(k);
+ k = rSNode.mPathSegmentElems.mUseSize+10;
+ }
+ }
+ rAssert(k==rSNode.mPathSegmentElems.mUseSize+11);
+ }
+
+END_PROFILE( "Remove Searches" );
+
+ radTime64 start = radTimeGetMicroseconds64();
+
+BEGIN_PROFILE( "Remove Section Elems" );
+ p3d::inventory->RemoveSectionElements(irGiveItAFuckinName.GetUID());
+END_PROFILE( "Remove Section Elems" );
+BEGIN_PROFILE( "Delete Section" );
+ p3d::inventory->DeleteSection(irGiveItAFuckinName.GetUID());
+END_PROFILE( "Delete Section" );
+
+ radTime64 end = radTimeGetMicroseconds64();
+ unsigned deleteTime = (unsigned) (end - start);
+
+ rTunePrintf("WorldRenderLayer::DumpDynaLoad Delete time: %.3fms\n", deleteTime / 1000.0F);
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+BEGIN_PROFILE( "Cleanup" );
+ rReleasePrintf("LoadList Dump: %s Num: %d\n", mLoadLists[i]->mGiveItAFuckinName.GetText(), i );
+ //mLoadLists[i]->ClearAll();
+ //mLoadLists[i]->AllocateAll(mnLoadListRefs);
+ mLoadLists[i]->ClearAllUse();
+ mLoadLists.Remove(i);
+END_PROFILE( "Cleanup" );
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+ }
+ }
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+bool WorldRenderLayer::DoPreDynaLoad(tName& irGiveItAFuckinName)//tUID iUID)
+{
+ int i;
+ for(i=0;i<mLoadLists.mUseSize;i++)
+ {
+ if(mLoadLists[i]->mGiveItAFuckinName.GetUID() == irGiveItAFuckinName.GetUID() )
+ return false;
+ }
+
+ HeapMgr()->PushHeap (GMA_LEVEL_ZONE);
+
+ if( mDynaLoadState == msLoad )
+ {
+ rReleasePrintf("ARGH! You're driving TOO FAST!*******************\n");
+ rAssert(mDynaLoadState != msLoad);
+ }
+ else
+ {
+ rReleasePrintf("*******************Loading*****%s****\n", irGiveItAFuckinName.GetText());
+ mLoadLists.AddUse(1);
+ mCurLoadUID = irGiveItAFuckinName.GetUID();
+ rReleasePrintf("CurLoadIndex was %d\n", mCurLoadIndex );
+ mCurLoadIndex = mLoadLists.mUseSize-1;
+ rReleasePrintf("CurLoadIndex is %d\n", mCurLoadIndex );
+ mDynaLoadState = msLoad;
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ mLoadLists[mCurLoadIndex]->mGiveItAFuckinName= irGiveItAFuckinName;
+ //mLoadLists[mCurLoadIndex]->ClearAll();
+ //mLoadLists[mCurLoadIndex]->AllocateAll(mnLoadListRefs);
+ mLoadLists[mCurLoadIndex]->ClearAllUse();
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+ BillboardWrappedLoader::OverrideLoader( false );
+ }
+
+ GetPersistentWorldManager()->OnSectorLoad( irGiveItAFuckinName.GetUID() );
+
+ HeapMgr()->PopHeap (GMA_LEVEL_ZONE);
+
+ return true;
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::DoPostDynaLoad()
+{
+ rAssert(mDynaLoadState != msNoLoad);
+ mDynaLoadState = msNoLoad;
+ mCurLoadUID = 0;
+
+ if(mQdDump)
+ {
+ rReleasePrintf("-=-=-=-=-=-=-=-Queued Dump, Dumped-=-=-=-=-=-=-=-\n");
+ mQdDump = false;
+ DumpAllDynaLoads(mQdDeletionStart,*mpQdDeletionList);
+ GetEventManager()->TriggerEvent( EVENT_ALL_DYNAMIC_ZONES_DUMPED, NULL );
+ }
+ BillboardWrappedLoader::OverrideLoader( true );
+}
+
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+WorldScene* WorldRenderLayer::pWorldScene()
+{
+ rAssert( mpWorldScene != NULL );
+
+ return mpWorldScene;
+}
+
+//************************************************************************
+//
+// Protected Member Functions : WorldRenderLayer
+//
+//************************************************************************
+
+//************************************************************************
+//
+// Private Member Functions : WorldRenderLayer
+//
+//************************************************************************
+//========================================================================
+// WorldRenderLayer::IsGutsSetup()
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+bool WorldRenderLayer::IsGutsSetup()
+{
+ return( mpGuts.IsSetUp() && (mpWorldScene != NULL) );
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::OnWorldRenderLayerInit()
+{
+ mpWorldScene = NULL;
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: unsigned int start.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::DumpAllDynaLoads()
+{
+ GetEventManager()->TriggerEvent( EVENT_INTERIOR_DUMPED );
+
+ int start = 0;
+ while( mLoadLists.mUseSize > start )
+ {
+ DumpDynaLoad(mLoadLists[start]->mGiveItAFuckinName);
+ }
+}
+//========================================================================
+// WorldRenderLayer::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+void WorldRenderLayer::DumpDynaLoad(tName& irGiveItAFuckinName)
+{
+ rmt::Box3D BBox;
+ BoxPts BBoxSP;
+
+ int i,j,k;
+ for(i=0;i<mLoadLists.mUseSize;i++ )
+ {
+ if( mLoadLists[i]->mGiveItAFuckinName.GetUID() == irGiveItAFuckinName.GetUID() )
+ {
+ GetEventManager()->TriggerEvent( EVENT_DUMP_DYNA_SECTION, (void*)(&(mLoadLists[i]->mGiveItAFuckinName)) );
+
+ //////////////////////////////////////////////////////////////////////////
+ // WorldSpheres
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mWorldSphereElems.mUseSize;j++)
+ {
+ for(k=0;k<mWorldSpheres.mUseSize;k++)
+ {
+ if(mLoadLists[i]->mWorldSphereElems[j] == mWorldSpheres[k])
+ {
+ //irEntityDeletionList.Add(mWorldSpheres[k]);
+ mWorldSpheres[k]->Release();
+ mWorldSpheres.Remove(k);
+ k = mWorldSpheres.mUseSize +10;
+ }
+ }
+ rAssert( k= mWorldSpheres.mUseSize+10 );
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // DPhys
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mDPhysElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mDPhysElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mDPhysElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mDPhysElems.mUseSize;k++)
+ {
+ if( rSNode.mDPhysElems[k] == mLoadLists[i]->mDPhysElems[j] )
+ {
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(rSNode.mDPhysElems[k]);
+ rSNode.mDPhysElems[k]->Release();
+ rSNode.mDPhysElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ if(k!=-1)
+ {
+ rAssert(false);
+ SpatialNode& rRootNode = mpWorldScene->mStaticTreeWalker.rIthNode(0);
+
+ for(k=0;k<rRootNode.mDPhysElems.mUseSize;k++)
+ {
+ if( rRootNode.mDPhysElems[k] == mLoadLists[i]->mDPhysElems[j] )
+ {
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(rRootNode.mDPhysElems[k]);
+ rRootNode.mDPhysElems[k]->Release();
+ rRootNode.mDPhysElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ }
+ rAssert(k==-1);
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SEntity
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mSEntityElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mSEntityElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mSEntityElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mSEntityElems.mUseSize;k++)
+ {
+ if( rSNode.mSEntityElems[k] == mLoadLists[i]->mSEntityElems[j] )
+ {
+ rSNode.mSEntityElems[k]->Release();
+ rSNode.mSEntityElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ if(k!=-1)
+ {
+ rAssert(false);
+ SpatialNode& rRootNode = mpWorldScene->mStaticTreeWalker.rIthNode(0);
+
+ for(k=0;k<rRootNode.mSEntityElems.mUseSize;k++)
+ {
+ if( rRootNode.mSEntityElems[k] == mLoadLists[i]->mSEntityElems[j] )
+ {
+ rRootNode.mSEntityElems[k]->Release();
+ rRootNode.mSEntityElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ }
+ rAssert(k==-1);
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // AnimColl
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mAnimCollElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mAnimCollElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mAnimCollElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mAnimCollElems.mUseSize;k++)
+ {
+ if( rSNode.mAnimCollElems[k] == mLoadLists[i]->mAnimCollElems[j] )
+ {
+ rSNode.mAnimCollElems[k]->Release();
+ GetAnimEntityDSGManager()->Remove( rSNode.mAnimCollElems[k] );
+ rSNode.mAnimCollElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ if(k!=-1)
+ {
+ rAssert(false);
+ SpatialNode& rRootNode = mpWorldScene->mStaticTreeWalker.rIthNode(0);
+
+ for(k=0;k<rRootNode.mAnimCollElems.mUseSize;k++)
+ {
+ if( rRootNode.mAnimCollElems[k] == mLoadLists[i]->mAnimCollElems[j] )
+ {
+ rRootNode.mAnimCollElems[k]->Release();
+ GetAnimEntityDSGManager()->Remove( rRootNode.mAnimCollElems[k] );
+ rRootNode.mAnimCollElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ }
+ rAssert(k==-1);
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Anim
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mAnimElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mAnimElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mAnimElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mAnimElems.mUseSize;k++)
+ {
+ if( rSNode.mAnimElems[k] == mLoadLists[i]->mAnimElems[j] )
+ {
+ // Remove it from the list of managed animentities
+ GetAnimEntityDSGManager()->Remove( rSNode.mAnimElems[k] );
+ rSNode.mAnimElems[k]->Release();
+ rSNode.mAnimElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ if(k!=-1)
+ {
+ rAssert(false);
+ SpatialNode& rRootNode = mpWorldScene->mStaticTreeWalker.rIthNode(0);
+
+ for(k=0;k<rRootNode.mAnimElems.mUseSize;k++)
+ {
+ if( rRootNode.mAnimElems[k] == mLoadLists[i]->mAnimElems[j] )
+ {
+ // Remove it from the list of managed animentities
+ GetAnimEntityDSGManager()->Remove( rRootNode.mAnimElems[k] );
+ rRootNode.mAnimElems[k]->Release();
+ rRootNode.mAnimElems.Remove(k);
+ k = -1;
+ break;
+ }
+ }
+ }
+ rAssert(k==-1);
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Intersect
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mIntersectElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mIntersectElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mIntersectElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mIntersectElems.mUseSize;k++)
+ {
+ if( rSNode.mIntersectElems[k] == mLoadLists[i]->mIntersectElems[j] )
+ {
+ //irEntityDeletionList.Add(rSNode.mIntersectElems[k]);
+ rSNode.mIntersectElems[k]->Release();
+ // rSNode.mIntersectElems[k]->ReleaseVerified();
+ rSNode.mIntersectElems.Remove(k);
+ k = rSNode.mIntersectElems.mUseSize+10;
+ }
+ }
+ rAssert(k==rSNode.mIntersectElems.mUseSize+11);
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // SPhys
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mSPhysElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mSPhysElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mSPhysElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mSPhysElems.mUseSize;k++)
+ {
+ if( rSNode.mSPhysElems[k] == mLoadLists[i]->mSPhysElems[j] )
+ {
+ //irEntityDeletionList.Add(rSNode.mSPhysElems[k]);
+ rSNode.mSPhysElems[k]->Release();
+ // rSNode.mSPhysElems[k]->ReleaseVerified();
+ rSNode.mSPhysElems.Remove(k);
+ k = rSNode.mSPhysElems.mUseSize+10;
+ }
+ }
+ rAssert(k==rSNode.mSPhysElems.mUseSize+11);
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Fences
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mFenceElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mFenceElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mFenceElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mFenceElems.mUseSize;k++)
+ {
+ if( rSNode.mFenceElems[k] == mLoadLists[i]->mFenceElems[j] )
+ {
+ //irEntityDeletionList.Add(rSNode.mFenceElems[k]);
+ rSNode.mFenceElems[k]->Release();
+ // rSNode.mSPhysElems[k]->ReleaseVerified();
+ rSNode.mFenceElems.Remove(k);
+ k = rSNode.mFenceElems.mUseSize+10;
+ }
+ }
+ rAssert(k==rSNode.mFenceElems.mUseSize+11);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Trigger Volumes
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mTrigVolElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mTrigVolElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mTrigVolElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mTrigVolElems.mUseSize;k++)
+ {
+ if( rSNode.mTrigVolElems[k] == mLoadLists[i]->mTrigVolElems[j] )
+ {
+ //irEntityDeletionList.Add(rSNode.mTrigVolElems[k]);
+ rSNode.mTrigVolElems[k]->Release();
+ rSNode.mTrigVolElems.Remove(k);
+ k = rSNode.mTrigVolElems.mUseSize+10;
+ }
+ }
+ rAssert(k==rSNode.mTrigVolElems.mUseSize+11);
+ }
+ //////////////////////////////////////////////////////////////////////////
+ // Road Segments
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mRoadSegmentElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mRoadSegmentElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mRoadSegmentElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mRoadSegmentElems.mUseSize;k++)
+ {
+ if( rSNode.mRoadSegmentElems[k] == mLoadLists[i]->mRoadSegmentElems[j] )
+ {
+ //irEntityDeletionList.Add(rSNode.mRoadSegmentElems[k]);
+ rSNode.mRoadSegmentElems[k]->Release();
+ rSNode.mRoadSegmentElems.Remove(k);
+ k = rSNode.mRoadSegmentElems.mUseSize+10;
+ }
+ }
+ rAssert(k==rSNode.mRoadSegmentElems.mUseSize+11);
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // Path Segments
+ //////////////////////////////////////////////////////////////////////////
+ for(j=0;j<mLoadLists[i]->mPathSegmentElems.mUseSize;j++)
+ {
+ /*
+ mLoadLists[i]->mPathSegmentElems[j]->GetBoundingBox(&BBox);
+ BBoxSP.mBounds.mMin.Add(BBox.low, mpWorldScene->mEpsilonOffset);
+ BBoxSP.mBounds.mMax.Sub(BBox.high,mpWorldScene->mEpsilonOffset);
+
+ SpatialNode& rSNode = mpWorldScene->mStaticTreeWalker.rSeekNode(BBoxSP);
+ */
+ SpatialNode& rSNode = *(mLoadLists[i]->mPathSegmentElems[j]->mpSpatialNode);
+
+ for(k=0;k<rSNode.mPathSegmentElems.mUseSize;k++)
+ {
+ if( rSNode.mPathSegmentElems[k] == mLoadLists[i]->mPathSegmentElems[j] )
+ {
+ //irEntityDeletionList.Add(rSNode.mPathSegmentElems[k]);
+ rSNode.mPathSegmentElems[k]->Release();
+ rSNode.mPathSegmentElems.Remove(k);
+ k = rSNode.mPathSegmentElems.mUseSize+10;
+ }
+ }
+ rAssert(k==rSNode.mPathSegmentElems.mUseSize+11);
+ }
+
+ radTime64 start = radTimeGetMicroseconds64();
+
+ p3d::inventory->RemoveSectionElements(irGiveItAFuckinName.GetUID());
+ p3d::inventory->DeleteSection(irGiveItAFuckinName.GetUID());
+
+ radTime64 end = radTimeGetMicroseconds64();
+ unsigned deleteTime = (unsigned) (end - start);
+
+// printf("WorldRenderLayer::DumpDynaLoad Delete time: %.3fms\n", deleteTime / 1000.0F);
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ //mLoadLists[i]->ClearAll();
+ //mLoadLists[i]->AllocateAll(mnLoadListRefs);
+ mLoadLists[i]->ClearAllUse();
+ mLoadLists.Remove(i);
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+ }
+ }
+}
diff --git a/game/code/render/RenderManager/WorldRenderLayer.h b/game/code/render/RenderManager/WorldRenderLayer.h
new file mode 100644
index 0000000..b7ea382
--- /dev/null
+++ b/game/code/render/RenderManager/WorldRenderLayer.h
@@ -0,0 +1,132 @@
+#ifndef __WorldRenderLayer_H__
+#define __WorldRenderLayer_H__
+
+//========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: WorldRenderLayer
+//
+// Description: The WorldRenderLayer does STUFF
+//
+// History: + Initial Implementation -- Devin [2002/04/23]
+//
+//========================================================================
+
+//=================================================
+// System Includes
+//=================================================
+
+//=================================================
+// Project Includes
+//=================================================
+#include <render/RenderManager/RenderLayer.h>
+#include <render/Culling/WorldScene.h>
+#include <render/DSG/DynaLoadListDSG.h>
+#include <raddebugwatch.hpp>
+
+//class tShadowGenerator; // VolShadows
+
+class ZoneAnimationController;
+
+//========================================================================
+//
+// Synopsis: The WorldRenderLayer; Synopsis by Inspection.
+//
+//========================================================================
+class WorldRenderLayer : public RenderLayer
+{
+public:
+ WorldRenderLayer();
+ ~WorldRenderLayer();
+
+ // Render Interface
+ virtual void Render();
+
+
+ // Resource Interface
+ virtual void AddGuts( IntersectDSG* ipIntersectDSG );
+ virtual void AddGuts( StaticEntityDSG* ipStaticEntityDSG );
+ virtual void AddGuts( StaticPhysDSG* ipStaticPhysDSG );
+ virtual void AddGuts( FenceEntityDSG* ipFenceEntityDSG );
+ virtual void AddGuts( SpatialTree* ipSpatialTree );
+ virtual void AddGuts( AnimCollisionEntityDSG* ipAnimCollDSG );
+ virtual void AddGuts( AnimEntityDSG* ipAnimDSG );
+ virtual void AddGuts( DynaPhysDSG* ipDynaPhysDSG );
+ virtual void AddGuts( TriggerVolume* ipTriggerVolume );
+ virtual void AddGuts( WorldSphereDSG* ipWorldSphere );
+ virtual void AddGuts( RoadSegment* ipRoadSegment );
+ virtual void AddGuts( PathSegment* ipPathSegment );
+ virtual void RemoveGuts( IEntityDSG* ipEDSG );
+
+
+ void ActivateWS(tUID iUID);
+ void DeactivateWS(tUID iUID);
+
+ //virtual void AddGuts( tDrawable* ipDrawable );
+ virtual void SetUpGuts();
+ virtual void NullifyGuts();
+
+ // Load Related interfaces
+ virtual void DoPreStaticLoad();
+ virtual void DoPostStaticLoad();
+ virtual void DumpAllDynaLoads( unsigned int start, SwapArray<tRefCounted*>& irEntityDeletionList);
+ virtual void DumpDynaLoad(tName& irGiveItAFuckinName, SwapArray<tRefCounted*>& irEntityDeletionList);
+ virtual bool DoPreDynaLoad(tName& irGiveItAFuckinName);
+ virtual void DoPostDynaLoad();
+
+ WorldScene* pWorldScene();
+
+ tName& GetCurSectionName();
+
+ enum DynaLoadState
+ {
+ msPreLoads,
+ msNoLoad,
+ msLoad,
+ msIgnoreLoad
+ };
+
+ DynaLoadState GetCurrentState() const { return mDynaLoadState; };
+
+
+ void SetMirror(bool enable, rmt::Matrix* matrix) { mMirror = enable; if(matrix) mMirrorMatrix = *matrix; }
+
+protected:
+
+ void DumpAllDynaLoads();
+ void DumpDynaLoad(tName& irGiveItAFuckinName);
+ virtual bool IsGutsSetup();
+ //Called by constructor
+ void OnWorldRenderLayerInit();
+
+ WorldScene* mpWorldScene;
+
+ ///////////////////////////////////////////////////////////////////////
+ // Dynamic Loading Control Stuctures
+ ///////////////////////////////////////////////////////////////////////
+ SwapArray<WorldSphereDSG*> mWorldSpheres;
+ SwapArray<DynaLoadListDSG> mStaticLoadLists;
+ SwapArray<DynaLoadListDSG*> mLoadLists;
+ int mnLoadListRefs;
+ int mCurLoadIndex;
+
+#ifdef DEBUGWATCH
+ unsigned int mDebugRenderTime, mDebugInnerRenderTime, mDebugGutsTime;
+#endif
+
+ DynaLoadState mDynaLoadState;
+ tUID mCurLoadUID;
+
+ //tShadowGenerator* mpShadowGenerator; // VolShadows
+
+
+ //These will handle deletions during a load
+ bool mQdDump;
+ unsigned int mQdDeletionStart;
+ SwapArray<tRefCounted*>* mpQdDeletionList;
+
+ bool mMirror;
+ rmt::Matrix mMirrorMatrix;
+};
+
+#endif
diff --git a/game/code/render/RenderManager/allrendermanager.cpp b/game/code/render/RenderManager/allrendermanager.cpp
new file mode 100644
index 0000000..c2cd757
--- /dev/null
+++ b/game/code/render/RenderManager/allrendermanager.cpp
@@ -0,0 +1,4 @@
+#include <render/RenderManager/FrontEndRenderLayer.cpp>
+#include <render/RenderManager/RenderLayer.cpp>
+#include <render/RenderManager/RenderManager.cpp>
+#include <render/RenderManager/WorldRenderLayer.cpp>
diff --git a/game/code/render/animentitydsgmanager/allanimentitydsgmanager.cpp b/game/code/render/animentitydsgmanager/allanimentitydsgmanager.cpp
new file mode 100644
index 0000000..ce69a72
--- /dev/null
+++ b/game/code/render/animentitydsgmanager/allanimentitydsgmanager.cpp
@@ -0,0 +1 @@
+#include <render/animentitydsgmanager/animentitydsgmanager.cpp> \ No newline at end of file
diff --git a/game/code/render/animentitydsgmanager/animentitydsgmanager.cpp b/game/code/render/animentitydsgmanager/animentitydsgmanager.cpp
new file mode 100644
index 0000000..f346eed
--- /dev/null
+++ b/game/code/render/animentitydsgmanager/animentitydsgmanager.cpp
@@ -0,0 +1,563 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: animentitydsgmanager
+//
+// Description: Single place for updating all loaded animentitydsg's in the world
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <render/animentitydsgmanager/animentitydsgmanager.h>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/utility.hpp>
+#include <memory/srrmemory.h>
+#include <meta/locatorevents.h>
+#include <events/eventmanager.h>
+#include <render/DSG/IEntityDSG.h>
+#include <render/DSG/animentitydsg.h>
+#include <render/DSG/animcollisionentitydsg.h>
+#include <render/DSG/StatePropDSG.h>
+#include <mission/gameplaymanager.h>
+#include <camera/supercammanager.h>
+#include <radtime.hpp>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+// Maximum number of animentitydsgs that can be inserted into the manager
+const int MAX_NUM_ANIM_ENTITY_DSGS = 100;
+// Maximum number of multicontrollers that can be inserted into the manager
+const int MAX_NUM_MULTICONTROLLERS = 50;
+// Maximum number of stateprops that can be inserted into the manager
+#ifdef RAD_WIN32
+const int MAX_NUM_STATEPROPS = 300;
+#else
+const int MAX_NUM_STATEPROPS = 250;
+#endif
+
+const char* PIGEON_IDLE_ANIM_DSG = "pidgeon_A_group";
+const char* PIGEON_FLY_ANIM_DSG = "pidgeon_B_group";
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+// Singleton instance pointer
+AnimEntityDSGManager* AnimEntityDSGManager::spInstance = NULL;
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//===========================================================================
+// AnimEntityDSGManager::CreateInstance
+//===========================================================================
+// Description:
+// Calls AnimEntityDSGManager ctor
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+AnimEntityDSGManager* AnimEntityDSGManager::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "AnimEntityDSGManager" );
+ rAssert( spInstance == NULL );
+
+ spInstance = new(GMA_PERSISTENT) AnimEntityDSGManager;
+ rAssert( spInstance != NULL );
+
+MEMTRACK_POP_GROUP( "AnimEntityDSGManager" );
+ return spInstance;
+}
+
+//===========================================================================
+// AnimEntityDSGManager::GetInstance
+//===========================================================================
+// Description:
+// Returns a non-const pointer to the singleton object
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+
+AnimEntityDSGManager* AnimEntityDSGManager::GetInstance()
+{
+ rAssert ( spInstance != NULL);
+
+ return spInstance;
+}
+//===========================================================================
+// AnimEntityDSGManager::DestroyInstance
+//===========================================================================
+// Description:
+// Calls the private manager singleton dtor
+// Constraints:
+//
+// Parameters:
+//
+// Return:
+//
+//===========================================================================
+void AnimEntityDSGManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+}
+//===========================================================================
+// AnimEntityDSGManager::AnimEntityDSGManager
+//===========================================================================
+// Description:
+// AnimEntityDSGManager ctor
+//
+// Constraints:
+// Allocates memory for a specific number of elements
+// if exceeded, assertion error
+//
+// Parameters:
+// None
+//
+// Return:
+//
+// None
+//===========================================================================
+
+AnimEntityDSGManager::AnimEntityDSGManager()
+{
+ // Allocate the array of pointers
+ // Don't worry too much about allocating too many since there it will
+ // be the only array in existence
+
+ mpFloatingRightWayArrows.Allocate(6);
+ mpFloatingWrongWayArrows.Allocate(6);
+
+ mEntityList.Allocate( MAX_NUM_ANIM_ENTITY_DSGS );
+ mCollEntityList.Allocate( MAX_NUM_ANIM_ENTITY_DSGS );
+ mMultiControllerlist.Allocate( MAX_NUM_MULTICONTROLLERS );
+ mStatePropList.Allocate( MAX_NUM_STATEPROPS );
+ GetEventManager()->AddListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::PARKED_BIRDS ) );
+}
+//===========================================================================
+// AnimEntityDSGManager::~AnimEntityDSGManager
+//===========================================================================
+// Description:
+// AnimEntityDSGManager dtor
+//
+// Constraints:
+//
+// Parameters:
+// None
+//
+// Return:
+// None
+//
+//===========================================================================
+AnimEntityDSGManager::~AnimEntityDSGManager()
+{
+ RemoveAll();
+ mEntityList.Clear();
+ mCollEntityList.Clear();
+ mMultiControllerlist.Clear();
+ mStatePropList.Clear();
+ mpFloatingRightWayArrows.Clear();
+ mpFloatingWrongWayArrows.Clear();
+
+ GetEventManager()->RemoveListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::PARKED_BIRDS ) );
+}
+//===========================================================================
+// AnimEntityDSGManager::Add
+//===========================================================================
+// Description:
+// Adds a new AnimCollisionEntityDSG to the manager
+//
+// Constraints:
+// Increases refcount
+//
+// Parameters:
+// AnimCollisionEntityDSG* pointer
+//
+// Return:
+// None.
+//
+//===========================================================================
+void AnimEntityDSGManager::Add( AnimCollisionEntityDSG* pDSG )
+{
+ rAssert( pDSG != NULL );
+ pDSG->AddRef();
+ mCollEntityList.Add( pDSG );
+}
+
+
+//===========================================================================
+// AnimEntityDSGManager::Add
+//===========================================================================
+// Description:
+// Adds a new AnimEntityDSG to the manager
+//
+// Constraints:
+// Increases refcount
+//
+// Parameters:
+// AnimEntityDSG* pointer
+//
+// Return:
+// None.
+//
+//===========================================================================
+void AnimEntityDSGManager::Add( AnimEntityDSG* pEntity )
+{
+ rAssert( pEntity != NULL );
+
+ if( pEntity->AddToUpdateList() )
+ {
+ pEntity->AddRef();
+ mEntityList.Add( pEntity );
+ return;
+ }
+ if( pEntity->TrackSeparately() )
+ {
+ if(pEntity->TrackSeparately()%2==0)
+ {
+ mpFloatingWrongWayArrows.Add(pEntity);//[pEntity->TrackSeparately()-1] = pEntity;
+ }
+ else
+ {
+ mpFloatingRightWayArrows.Add(pEntity);//[pEntity->TrackSeparately()-1] = pEntity;
+ }
+
+ pEntity->AddRef();
+ return;
+ }
+}
+
+void AnimEntityDSGManager::Add( StatePropDSG* prop )
+{
+ for (int i=0;i<mStatePropList.mUseSize;i++)
+ {
+ assert( mStatePropList[i] != prop );
+ }
+
+ mStatePropList.Add( prop );
+}
+
+
+//===========================================================================
+// AnimEntityDSGManager::Remove
+//===========================================================================
+// Description:
+//
+// Constraints:
+// O(N) time to remove. But array is so small who care?
+// No message if pointer not found
+//
+// Parameters:
+// AnimEntityDSG* indicating the object to remove
+//
+// Return:
+// None
+//
+//===========================================================================
+void AnimEntityDSGManager::Remove( AnimEntityDSG* pEntity )
+{
+ // Is it possible to insert multiple copies of the same pointer?
+ // Lets assume it is, and if it isn't we aren't really saving
+ // much by breaking early
+ for( int i = 0 ; i < mEntityList.mUseSize ; ++i)
+ {
+ if( mEntityList[i] == pEntity )
+ {
+ mEntityList[ i ]->Release();
+ mEntityList.Remove( i );
+ }
+ }
+}
+//===========================================================================
+// AnimEntityDSGManager::Remove
+//===========================================================================
+// Description:
+//
+// Constraints:
+// O(N) time to remove. But array is so small who care?
+// No message if pointer not found
+//
+// Parameters:
+// AnimCollisionEntityDSG* indicating the object to remove
+//
+// Return:
+// None
+//
+//===========================================================================
+void AnimEntityDSGManager::Remove( AnimCollisionEntityDSG* pEntity )
+{
+ // Is it possible to insert multiple copies of the same pointer?
+ // Lets assume it is, and if it isn't we aren't really saving
+ // much by breaking early
+ for( int i = 0 ; i < mCollEntityList.mUseSize ; ++i)
+ {
+ if( mCollEntityList[i] == pEntity )
+ {
+ mCollEntityList[ i ]->Release();
+ mCollEntityList.Remove( i );
+ }
+ }
+}
+
+void AnimEntityDSGManager::Remove( StatePropDSG* pProp )
+{
+ for ( int i = 0 ; i < mStatePropList.mUseSize ; i++ )
+ {
+ if ( mStatePropList[ i ] == pProp )
+ {
+ mStatePropList.Remove( i );
+ break;
+ }
+ }
+}
+
+//===========================================================================
+// AnimEntityDSGManager::RemoveAll
+//===========================================================================
+// Description:
+// Removes all dsg entitys from the manager
+//
+// Constraints:
+// Does not remove them from the scenegraph
+//
+// Parameters:
+// None
+//
+// Return:
+// None
+//
+//===========================================================================
+void AnimEntityDSGManager::RemoveAll()
+{
+ for(int i=mpFloatingRightWayArrows.mUseSize-1; i>-1; i--)
+ {
+ mpFloatingRightWayArrows[ i ]->Release();
+ }
+
+ for(int i=mpFloatingWrongWayArrows.mUseSize-1; i>-1; i--)
+ {
+ mpFloatingWrongWayArrows[ i ]->Release();
+ }
+
+ for (int i = 0 ; i < mEntityList.mUseSize ; ++i )
+ {
+ mEntityList[ i ]->Release();
+ }
+ for (int i = 0 ; i < mCollEntityList.mUseSize ; ++i )
+ {
+ mCollEntityList[ i ]->Release();
+ }
+ for (int i = 0 ; i < mMultiControllerlist.mUseSize ; ++i )
+ {
+ mMultiControllerlist[ i ]->Release();
+ }
+ mpFloatingRightWayArrows.ClearUse();
+ mpFloatingWrongWayArrows.ClearUse();
+ mEntityList.ClearUse();
+ mCollEntityList.ClearUse();
+ mMultiControllerlist.ClearUse();
+ mStatePropList.ClearUse();
+}
+//===========================================================================
+// AnimEntityDSGManager::Update
+//===========================================================================
+// Description:
+// Advances all animations
+//
+// Constraints:
+//
+// Parameters:
+// time to advance in milliseconds
+//
+// Return:
+// none.
+//
+//===========================================================================
+#ifdef BREAK_DOWN_PROFILE
+ #define ANIM_PROFILE_BEGIN(atime) atime = radTimeGetMicroseconds();
+ #define ANIM_PROFILE_END(atime, aname) atime = radTimeGetMicroseconds() - atime; if(atime>90) rReleasePrintf("Advance on %s takes %u us.\n", aname, atime);
+#else
+ #define ANIM_PROFILE_BEGIN(atime)
+ #define ANIM_PROFILE_END(atime, aname)
+#endif
+
+void AnimEntityDSGManager::Update( unsigned int elapsedTime )
+{
+ float fElapsedTime = static_cast<float>(elapsedTime);
+ float elapsedTimeSeconds = fElapsedTime * 0.001f;
+
+ #ifdef BREAK_DOWN_PROFILE
+ unsigned int updateTime;
+ #endif //BREAK_DOWN_PROFILE
+
+ int i;
+
+ for (i = 0 ; i < mEntityList.mUseSize ; ++i)
+ {
+ ANIM_PROFILE_BEGIN(updateTime)
+ mEntityList[ i ]->Update( elapsedTimeSeconds );
+ ANIM_PROFILE_END(updateTime, mEntityList[i]->GetName())
+ }
+ for (i = 0 ; i < mCollEntityList.mUseSize ; ++i)
+ {
+ ANIM_PROFILE_BEGIN(updateTime)
+ mCollEntityList[ i ]->AdvanceAnimation( elapsedTimeSeconds );
+ ANIM_PROFILE_END(updateTime, mCollEntityList[i]->GetName())
+ }
+ for (i = 0 ; i < mStatePropList.mUseSize ; ++i)
+ {
+ ANIM_PROFILE_BEGIN(updateTime)
+ mStatePropList[ i ]->AdvanceAnimation( fElapsedTime );
+ ANIM_PROFILE_END(updateTime, mStatePropList[i]->GetName())
+ }
+
+ for (i = 0 ; i < mMultiControllerlist.mUseSize ; ++i)
+ {
+ ANIM_PROFILE_BEGIN(updateTime)
+ mMultiControllerlist[ i ]->Advance( fElapsedTime );
+ ANIM_PROFILE_END(updateTime, mMultiControllerlist[i]->GetName())
+ }
+}
+
+//===========================================================================
+// AnimEntityDSGManager::Add
+//===========================================================================
+// Description:
+// Adds the given multicontroller to the list, but only if it is not present
+// already
+//
+// Constraints:
+//
+// Parameters:
+// tMultiController pointer
+//
+// Return:
+// none.
+//
+//===========================================================================
+void AnimEntityDSGManager::Add( tMultiController* pController )
+{
+ bool found = false;
+ for (int i = 0 ; i < mMultiControllerlist.mUseSize ; i++)
+ {
+ if ( mMultiControllerlist[ i ] == pController )
+ {
+ found = true;
+ break;
+ }
+ }
+
+
+ if ( found == false )
+ {
+ mMultiControllerlist.Add( pController );
+ pController->AddRef();
+ }
+}
+//===========================================================================
+// AnimEntityDSGManager::Remove
+//===========================================================================
+// Description:
+// Removes the given multicontroller from the list
+//
+// Constraints:
+//
+// Parameters:
+// tMultiController pointer
+//
+// Return:
+// none.
+//
+//===========================================================================
+void AnimEntityDSGManager::Remove( tMultiController* pController )
+{
+ for (int i = 0 ; i < mMultiControllerlist.mUseSize ; i++)
+ {
+ if ( mMultiControllerlist[ i ] == pController )
+ {
+ pController->Release();
+ mMultiControllerlist.Remove( i );
+ break;
+ }
+ }
+}
+//===========================================================================
+// AnimEntityDSGManager::HandleEvent
+//===========================================================================
+// Description:
+// Handles trigger volume locators being triggered
+//
+// Constraints:
+//
+// Parameters:
+// Event ID and pointer to user data
+//
+// Return:
+// none.
+//
+//===========================================================================
+
+void AnimEntityDSGManager::HandleEvent( EventEnum id, void* pEventData )
+{
+
+
+ switch( id )
+ {
+ case EVENT_LOCATOR + LocatorEvent::PARKED_BIRDS:
+ {
+ // The character has walked into the zone of range of the birds.
+ // We must make them fly away, but only if they are on the ground actually playing
+ // Stop the original idle animation. Remove it from the DSG.
+ // Find the second animation of the birds flying away. Insert it into the DSG and start it off
+
+ AnimEntityDSG* pIdleAnim = p3d::find< AnimEntityDSG >( PIGEON_IDLE_ANIM_DSG );
+ rAssertMsg( pIdleAnim != NULL, "Pigeon idle animation not found" );
+
+ //chuck:if the animation isn't loaded yet don't play it
+ if(pIdleAnim != NULL)
+ {
+ if ( pIdleAnim && pIdleAnim->GetVisibility() )
+ {
+
+ AnimEntityDSG* pTriggerAnim = p3d::find< AnimEntityDSG >( PIGEON_FLY_ANIM_DSG );
+ rAssertMsg( pTriggerAnim != NULL, "Pigeons flying animation not found" );
+
+ pIdleAnim->PlayAnimation( false );
+ pIdleAnim->SetVisibility( false );
+
+ pTriggerAnim->Reset();
+ pTriggerAnim->PlayAnimation( true );
+ pTriggerAnim->SetVisibility( true );
+
+ GetEventManager()->TriggerEvent( EVENT_PLAY_BIRD_SOUND );
+ }
+ }
+
+ }
+ break;
+
+ default:
+ rAssert(false);
+ break;
+ }
+}
+
diff --git a/game/code/render/animentitydsgmanager/animentitydsgmanager.h b/game/code/render/animentitydsgmanager/animentitydsgmanager.h
new file mode 100644
index 0000000..201b899
--- /dev/null
+++ b/game/code/render/animentitydsgmanager/animentitydsgmanager.h
@@ -0,0 +1,120 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: animentitydsgmanager
+//
+// Description: Single place for updating all loaded animentitydsg's in the world
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef ANIMENTITYDSGMANAGER_H
+#define ANIMENTITYDSGMANAGER_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <events/eventlistener.h>
+#include <meta/locatorevents.h>
+#include <render/Culling/swaparray.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class tMultiController;
+class AnimEntityDSG;
+class AnimCollisionEntityDSG;
+class StatePropDSG;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Manages animation control for AnimEntityDSGs as well as
+// InstAnimDynaPhysDSGs. Handles animations that are triggered by
+// trigger volumes as well (hence the EventListener inheritence)
+//
+//
+// Constraints:
+//
+//
+//===========================================================================
+
+
+class AnimEntityDSGManager : public EventListener
+{
+ public:
+
+ // Static Methods (for creating, destroying and acquiring an instance
+ // of the AnimEntityDSGManager)
+ static AnimEntityDSGManager* CreateInstance();
+ static AnimEntityDSGManager* GetInstance();
+ static void DestroyInstance();
+
+ void Add( AnimCollisionEntityDSG* );
+ void Add( AnimEntityDSG* );
+ void Add( StatePropDSG* );
+ // Add a multicontroller to the list of controllers that will get updated every frame
+ void Add( tMultiController* );
+ // Remove a multicontroller from the list of controllers
+ void Remove( tMultiController* );
+ void Remove( AnimEntityDSG* );
+ void Remove( AnimCollisionEntityDSG* );
+ void Remove( StatePropDSG* );
+
+ void RemoveAll();
+ void Update( unsigned int elapsedTime );
+
+ // Inherited from EventListener
+ // Used to detect when the character walks into a trigger volume
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ SwapArray<AnimEntityDSG*> mpFloatingRightWayArrows;
+ SwapArray<AnimEntityDSG*> mpFloatingWrongWayArrows;
+
+ protected:
+
+ private:
+
+ SwapArray< AnimEntityDSG* > mEntityList;
+ SwapArray< AnimCollisionEntityDSG* > mCollEntityList;
+ SwapArray< tMultiController* > mMultiControllerlist;
+ SwapArray< StatePropDSG* > mStatePropList;
+
+ // Singleton, prevent access to ctors
+
+ AnimEntityDSGManager();
+ virtual ~AnimEntityDSGManager();
+
+ AnimEntityDSGManager( const AnimEntityDSGManager& );
+ AnimEntityDSGManager& operator=( const AnimEntityDSGManager& );
+
+ static AnimEntityDSGManager* spInstance;
+};
+
+
+//===========================================================================
+// Inlines
+//===========================================================================
+
+
+// A little syntactic sugar for getting at this singleton.
+inline AnimEntityDSGManager* GetAnimEntityDSGManager()
+{
+ return( AnimEntityDSGManager::GetInstance() );
+}
+
+#endif \ No newline at end of file
diff --git a/game/code/render/breakables/allbreakables.cpp b/game/code/render/breakables/allbreakables.cpp
new file mode 100644
index 0000000..062dba8
--- /dev/null
+++ b/game/code/render/breakables/allbreakables.cpp
@@ -0,0 +1 @@
+#include <render/breakables/breakablesmanager.cpp>
diff --git a/game/code/render/breakables/breakablesmanager.cpp b/game/code/render/breakables/breakablesmanager.cpp
new file mode 100644
index 0000000..12167d3
--- /dev/null
+++ b/game/code/render/breakables/breakablesmanager.cpp
@@ -0,0 +1,649 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: breakablesmanager
+//
+// Description: Contains and manages all types of breakable animations in the game
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <render/particles/particlemanager.h>
+#include <render/Breakables/breakablesmanager.h>
+
+#include <render/RenderManager/RenderManager.h>
+#include <render/culling/worldscene.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <events/eventmanager.h>
+
+#include <memory/srrmemory.h>
+#include <algorithm>
+#include <worldsim/coins/coinmanager.h>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+// Static instance of our singleton pointer.
+BreakablesManager* BreakablesManager::spInstance = NULL;
+const char* BreakablesManager::sInventorySectionName = "Breakables Inventory Section";
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+//==============================================================================
+// BreakablesManager::CreateInstance
+//==============================================================================
+//
+// Description: Creates the BreakablesManager.
+//
+// Parameters: None.
+//
+// Return: Pointer to the BreakablesManager.
+//
+// Constraints: Multiple calls to CreateInstance without a DestroyInstance call in between
+// will result in an assertion (or lost memory if assertions not enabled)
+//
+//==============================================================================
+BreakablesManager* BreakablesManager::CreateInstance()
+{
+MEMTRACK_PUSH_GROUP( "BreakablesManager" );
+ rAssert( spInstance == NULL );
+
+ spInstance = new(GMA_PERSISTENT) BreakablesManager;
+ rAssert( spInstance != NULL );
+MEMTRACK_POP_GROUP( "BreakablesManager" );
+
+ return spInstance;
+}
+//==============================================================================
+// BreakablesManager::DestroyInstance
+//==============================================================================
+//
+// Description: Frees the BreakablesManager singleton.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: Assertion failure if CreateInstance was not called first.
+//
+//
+//==============================================================================
+void BreakablesManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+}
+//==============================================================================
+// BreakablesManager::GetInstance
+//==============================================================================
+//
+// Description: Returns a BreakablesManager singleton object.
+//
+// Parameters: None.
+//
+// Return: Pointer to a BreakablesManager object.
+//
+// Constraints: Assertion failure if CreateInstance was not called first.
+//
+//
+//==============================================================================
+BreakablesManager* BreakablesManager::GetInstance()
+{
+ if ( spInstance == NULL )
+ {
+ CreateInstance();
+ }
+ rAssert ( spInstance != NULL);
+
+ return spInstance;
+}
+
+//==============================================================================
+// BreakablesManager::BreakablesManager
+//==============================================================================
+//
+// Description: BreakablesManager constructor.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints:
+//
+//
+//==============================================================================
+BreakablesManager::BreakablesManager()
+: mInventorySectionUID( tName::MakeUID( GetInvSectionName() ) ),
+mBreakablesList( BreakablesEnum::eNumBreakables ),
+mZoneList( BreakablesEnum::eNumBreakables )
+{
+ GetEventManager()->AddListener( this, EVENT_DUMP_DYNA_SECTION );
+ mBreakableRemoveQueue.Allocate( BREAKABLE_QUEUE_SIZE );
+
+ unsigned int i;
+ mZoneList.AddUse( BreakablesEnum::eNumBreakables );
+ for ( i = 0; i < BreakablesEnum::eNumBreakables; ++i )
+ {
+ // mZoneList[i].AddUse( BreakablesEnum::eMaxBreakableNames );
+ mZoneList[i].Allocate( BreakablesEnum::eMaxBreakableNames );
+ }
+}
+//==============================================================================
+// BreakablesManager::~BreakablesManager
+//==============================================================================
+//
+// Description: BreakablesManager destructor
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints:
+//
+//
+//==============================================================================
+BreakablesManager::~BreakablesManager()
+{
+ mBreakablesList.clear();
+ p3d::inventory->DeleteSection( mInventorySectionUID );
+ GetEventManager()->RemoveListener( this, EVENT_DUMP_DYNA_SECTION );
+}
+//==============================================================================
+// BreakablesManager::Update
+//==============================================================================
+//
+// Description: Updates all breakables in the game
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints:
+//
+//
+//==============================================================================
+
+//==============================================================================
+// BreakablesManager::ManagedBreakable::ManagedBreakable
+//==============================================================================
+//
+// Description: ManagedBreakable ctor.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//
+//==============================================================================
+BreakablesManager::ManagedBreakable::ManagedBreakable()
+: mIsActive( false )
+{
+ mpBreakableDSG = new (GMA_LEVEL_OTHER) BreakableObjectDSG;
+ mpBreakableDSG->AddRef();
+}
+//==============================================================================
+// BreakablesManager::ManagedBreakable::~ManagedBreakable
+//==============================================================================
+//
+// Description: ManagedBreakable dtor.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//
+//==============================================================================
+BreakablesManager::ManagedBreakable::~ManagedBreakable()
+{
+ if ( mpBreakableDSG != NULL )
+ {
+ mpBreakableDSG->ReleaseVerified();
+ mpBreakableDSG = NULL;
+ }
+}
+void BreakablesManager::ManagedBreakable::AddToDSG()
+{
+ // Get the renderlayer that the breakable object is located in, store it in mLayer
+ mLayer = static_cast< RenderEnums::LayerEnum >( GetRenderManager()->rCurWorldRenderLayer() );
+ // Add the object
+ GetRenderManager()->pWorldRenderLayer()->pWorldScene()->Add( mpBreakableDSG );
+}
+
+void BreakablesManager::ManagedBreakable::RemoveFromDSG()
+{
+ // Get the renderlayer that the breakable object is located in
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mLayer ));
+ // Sanity check
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ // Remove the object
+ pWorldRenderLayer->pWorldScene()->Remove( mpBreakableDSG );
+
+}
+
+
+//==============================================================================
+// BreakablesManager::DebugRender
+//==============================================================================
+//
+// Description: Draw all breakable objects in one ugly loop.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: Get rid of this once Devin's Z sorting is finished.
+//
+//
+//==============================================================================
+void BreakablesManager::DebugRender()const
+{
+ return;
+ for( unsigned int i = 0 ; i < mBreakablesList.size() ; ++i )
+ {
+ for( unsigned int j = 0 ; j < mBreakablesList[ i ].size ; ++j)
+ {
+ if (mBreakablesList[ i ].list[ j ]->mIsActive)
+ mBreakablesList[ i ].list[ j ]->mpBreakableDSG->Display();
+ }
+ }
+}
+//==============================================================================
+// BreakablesManager::Update
+//==============================================================================
+//
+// Description: Advance all animations of currently playing breakable animations
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: Is float the correct parameter? Is int or unsigned int better?
+//
+//
+//==============================================================================
+void BreakablesManager::Update( unsigned int deltaTime )
+{
+ float fDeltaTime = static_cast< float >( deltaTime );
+
+ for( unsigned int i = 0 ; i < mBreakablesList.size() ; ++i )
+ {
+ for( unsigned int j = 0 ; j < mBreakablesList[ i ].size ; ++j)
+ {
+ if ( mBreakablesList[ i ].list[ j ]->mIsActive )
+ {
+ if (mBreakablesList[ i ].list[ j ]->mpBreakableDSG->LastFrameReached())
+ {
+ mBreakablesList[ i ].list[ j ]->mIsActive = false;
+ // Remove it from the DSG
+ mBreakablesList[ i ].list[ j ]->RemoveFromDSG();
+ // Lets check the type that we just finished playing
+ if ( BreakablesEnum::BreakableID(i) == BreakablesEnum::eCarExplosion )
+ {
+ // Trigger a car explosion event
+ GetEventManager()->TriggerEvent( EVENT_CAR_EXPLOSION_DONE, NULL );
+ }
+ }
+ else
+ {
+ mBreakablesList[ i ].list[ j ]->mpBreakableDSG->Update( fDeltaTime );
+ }
+ }
+ }
+ }
+ // Iterate through the list of broken objects (NOT the breakable animation, the original
+ // object that was broken and replaced by the breakable) and remove them from the DSG tree
+
+ for( int i = 0 ; i < mBreakableRemoveQueue.mUseSize ; ++i )
+ {
+ // Get the renderlayer that the broken object is located in
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( mBreakableRemoveQueue[ i ].layer ));
+ // Sanity check
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ // Remove the object
+ if ( mBreakableRemoveQueue[ i ].useRemoveGuts )
+ {
+ pWorldRenderLayer->RemoveGuts( mBreakableRemoveQueue[ i ].pDSG );
+ }
+ else
+ {
+ pWorldRenderLayer->pWorldScene()->Remove( mBreakableRemoveQueue[ i ].pDSG );
+ }
+ }
+ mBreakableRemoveQueue.ClearUse();
+
+}
+//==============================================================================
+// BreakablesManager::AllocateBreakables
+//==============================================================================
+//
+// Description: Allocate the breakable objects of a certain type
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//
+//==============================================================================
+void BreakablesManager::AllocateBreakables( BreakablesEnum::BreakableID type,
+ tAnimatedObjectFactory* pFactory,
+ tAnimatedObjectFrameController* pController,
+ int numInstances )
+{
+ rAssert( type >= 0 && type < (int)mBreakablesList.size());
+
+ // Set the tName of the zone that we are loading
+ // This could cause a brief alloc, which will go away when we go out of scope. So push GMA_TEMP here.
+ //
+ HeapMgr()->PushHeap(GMA_TEMP);
+ tName zoneBeingLoaded = GetRenderManager()->pWorldRenderLayer()->GetCurSectionName().GetUID();
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+ mZoneList[ type ].Add( zoneBeingLoaded );
+
+ if ( mBreakablesList[type].size == 0 )
+ {
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ #endif
+ mBreakablesList[ type ].size = numInstances;
+ mBreakablesList[ type ].list = new ManagedBreakable*[ numInstances ];
+ for( int i = 0 ; i < numInstances ; ++i )
+ {
+ mBreakablesList[ type ].list[ i ] = new ManagedBreakable;
+ mBreakablesList[ type ].list[ i ]->mpBreakableDSG->Init( pFactory, pController );
+ }
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ #endif
+ }
+}
+//==============================================================================
+// BreakablesManager::FreeBreakables
+//==============================================================================
+//
+// Description: Frees all breakables of the given type
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: Not refcount tested yet.
+//
+//
+//==============================================================================
+void BreakablesManager::FreeBreakables( BreakablesEnum::BreakableID type )
+{
+ RemoveFromDSG( type );
+
+ rAssert( type >= 0 && type < (int)mBreakablesList.size());
+
+ for (unsigned int i = 0 ; i < mBreakablesList[ type ].size ; ++i)
+ {
+ delete mBreakablesList[ type ].list[ i ];
+ }
+ delete[] mBreakablesList[ type ].list;
+ mBreakablesList[ type ].list = 0;
+ mBreakablesList[ type ].size = 0;
+
+ mZoneList[ type ].ClearUse();
+}
+//==============================================================================
+// BreakablesManager::RemoveAllFromDSG
+//==============================================================================
+//
+// Description: Removes all playing breakables from the DSG
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints:
+//
+//
+//==============================================================================
+void BreakablesManager::RemoveAllFromDSG()
+{
+ // For each type
+ // for each allocated instance
+ // if its in the DSG
+ // remove it and set active to false
+ for ( unsigned int i = 0 ; i < mBreakablesList.size() ; i++)
+ {
+ for ( unsigned int j = 0 ; j < mBreakablesList[ i ].size ; j++ )
+ {
+ if ( mBreakablesList[ i ].list[ j ]->mIsActive )
+ {
+ mBreakablesList[ i ].list[ j ]->mIsActive = false;
+ mBreakablesList[ i ].list[ j ]->RemoveFromDSG();
+ }
+ }
+ }
+}
+void BreakablesManager::RemoveFromDSG( BreakablesEnum::BreakableID type )
+{
+ for ( unsigned int j = 0 ; j < mBreakablesList[ type ].size ; j++ )
+ {
+ if ( mBreakablesList[ type ].list[ j ]->mIsActive )
+ {
+ mBreakablesList[ type ].list[ j ]->mIsActive = false;
+ mBreakablesList[ type ].list[ j ]->RemoveFromDSG();
+ }
+ }
+}
+
+
+//==============================================================================
+// BreakablesManager::FreeAllBreakables
+//==============================================================================
+//
+// Description: Frees all breakables
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//
+//==============================================================================
+void BreakablesManager::FreeAllBreakables()
+{
+ for ( unsigned int i = 0 ; i < mBreakablesList.size() ; ++i )
+ {
+ FreeBreakables( BreakablesEnum::BreakableID( i ) );
+ }
+}
+//==============================================================================
+// BreakablesManager::RemoveBrokenObjectFromWorld
+//==============================================================================
+//
+// Description: Adds a broken object to the manager's internal list of intact objects
+// that have to be removed since they got broken. We don't immediately remove the broken
+// object because they may still reside in internal collision states until collision
+// resolution is completed
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: Will not add duplicate entries. O(N) performance (but array is so small who cares?)
+//
+//
+//==============================================================================
+void BreakablesManager::RemoveBrokenObjectFromWorld( IEntityDSG* pObjectToBeRemoved, RenderEnums::LayerEnum layer, bool useRemoveGuts )
+{
+ bool wasDuplicateFound = false;
+ for( int i = 0 ; i < mBreakableRemoveQueue.mUseSize ; ++i)
+ {
+ if( mBreakableRemoveQueue[ i ].pDSG == pObjectToBeRemoved )
+ {
+ wasDuplicateFound = true;
+ break;
+ }
+ }
+ if( wasDuplicateFound == false )
+ {
+ BrokenObject brokenObject;
+ brokenObject.pDSG = pObjectToBeRemoved;
+ brokenObject.layer = layer;
+ brokenObject.useRemoveGuts = useRemoveGuts;
+
+ mBreakableRemoveQueue.Add( brokenObject );
+ }
+}
+
+//==============================================================================
+// BreakablesManager::Play
+//==============================================================================
+//
+// Description: Inserts a new breakable object into the DSG and starts animation playback
+// Animation stops when LastFrameReached() is true
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints:
+//
+//
+//==============================================================================
+void BreakablesManager::Play( BreakablesEnum::BreakableID type, const rmt::Matrix& transform )
+{
+ rAssert( type >= 0 && type < (int)mBreakablesList.size());
+
+ if ( mBreakablesList[ type ].size > 0 )
+ {
+ ManagedBreakable* pMB = mBreakablesList[ type ].Next();
+ if ( pMB->mIsActive )
+ {
+ pMB->RemoveFromDSG();
+ }
+ pMB->mIsActive = true;
+ pMB->mpBreakableDSG->Reset();
+ pMB->mpBreakableDSG->SetTransform( transform );
+ pMB->AddToDSG();
+
+ //passing pointer to local variable here!!
+ GetEventManager()->TriggerEvent( EVENT_HIT_BREAKABLE, (void*)&type );
+ }
+}
+
+void BreakablesManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ int i,j;
+ switch (id)
+ {
+ case EVENT_DUMP_DYNA_SECTION:
+ {
+ // This could cause a brief alloc, which will go away when we go out of scope. So push GMA_TEMP here.
+ //
+ HeapMgr()->PushHeap(GMA_TEMP);
+ tName zoneBeingDumped = *( static_cast< tName* >( pEventData ) );
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+ // Iterate through all the breakables and find those that have the
+ // same name as the one given
+ for ( i = 0 ; i < mZoneList.mUseSize ; i++)
+ {
+ //tNameList::iterator it = std::find( mZoneList[i].begin(), mZoneList[i].end(), zoneBeingDumped );
+ for ( j = 0 ; j < mZoneList[ i ].mUseSize ; j++)
+ {
+ if ( mZoneList[ i ][j] == zoneBeingDumped )
+ {
+ break;
+ }
+ }
+
+ if ( j != mZoneList[ i ].mUseSize )
+ {
+ mZoneList[i].Remove( j );
+ }
+ // Kill the zone if all zones that reference this breakable are gone
+ if ( mZoneList[i].mUseSize == 0 )
+ {
+ FreeBreakables( static_cast<BreakablesEnum::BreakableID>(i) );
+ }
+ }
+ }
+ break;
+ default:
+ rAssertMsg(true,"Unhandled case in Breakables Manager!");
+ break;
+ };
+}
+
+
+BreakablesManager::ManagedBreakable*
+BreakablesManager::BreakableInstances::Next()
+{
+ ++currElement;
+
+ if ( currElement >= size )
+ {
+ currElement = 0;
+ }
+ BreakablesManager::ManagedBreakable* pRetVal = list[ currElement ];
+ return pRetVal;
+}
+
+bool BreakablesManager::IsLoaded( BreakablesEnum::BreakableID type )
+{
+ bool isLoaded;
+ if ( type < 0 || type >= static_cast< int > ( mBreakablesList.size() ) )
+ {
+ isLoaded = false;
+ }
+ else if ( mBreakablesList[ type ].size == 0 )
+ {
+ isLoaded = false;
+ }
+ else
+ {
+ isLoaded = true;
+ }
+
+ return isLoaded;
+}
+
+void BreakablesManager::AddToZoneList( BreakablesEnum::BreakableID type )
+{
+ if ( type < 0 || type >= static_cast< int >( mBreakablesList.size() ))
+ return;
+
+ // Check to see that we aren't already adding it
+ tName zoneBeingLoaded = GetRenderManager()->pWorldRenderLayer()->GetCurSectionName().GetUID();
+ for ( int i = 0 ; i < mZoneList[ type ].mUseSize ; i++ )
+ {
+ if ( zoneBeingLoaded == mZoneList[ type ][ i ] )
+ return;
+ }
+
+ mZoneList[ type ].Add( zoneBeingLoaded );
+}
+
diff --git a/game/code/render/breakables/breakablesmanager.h b/game/code/render/breakables/breakablesmanager.h
new file mode 100644
index 0000000..341bf93
--- /dev/null
+++ b/game/code/render/breakables/breakablesmanager.h
@@ -0,0 +1,165 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: breakablesmanager
+//
+// Description: Singleton class that manages breakable objects in game
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef BREAKABLESMANAGER_H
+#define BREAKABLESMANAGER_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <memory\srrmemory.h> // Needed for my STL allocations to go on the right heap
+#include <render\DSG\breakableobjectdsg.h>
+#include <constants/breakablesenum.h>
+#include <render/culling/swaparray.h>
+#include <render/culling/reservearray.h>
+#include <memory/stlallocators.h>
+#include <events/eventlistener.h>
+#include <render/enums/renderenums.h>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+const int BREAKABLE_QUEUE_SIZE = 50;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Breakables manager is necessary because we want to have an number of
+// breakable objects (DSG objects) in the world in a ready pool. Possibly
+// less than the number of intact objects because it is unlikely the user
+// could smash all the fire hydrants in the world simultaneously. The user
+// could however smash more than one object at once and thus we need to
+// allocate and manage a pool of breakable objects.
+//
+//
+// Constraints:
+// Hasn't been memory leak tested yet.
+//
+//===========================================================================
+class BreakablesManager : public EventListener
+{
+ public:
+
+ // Static Methods (for creating, destroying and acquiring an instance
+ // of the ParticleManager)
+ static BreakablesManager* CreateInstance();
+ static BreakablesManager* GetInstance();
+ static void DestroyInstance();
+
+ // Time in milliseconds
+ void Update( unsigned int deltaTime );
+
+ void AllocateBreakables( BreakablesEnum::BreakableID, tAnimatedObjectFactory* pFactory, tAnimatedObjectFrameController* pController, int numInstances );
+ void FreeBreakables( BreakablesEnum::BreakableID );
+
+ // Calls FreeBreakables for every allocated breakable
+ // Killing all memory, also calls RemoveAllFromDSG to get rid of any that
+ // are still playing
+ void FreeAllBreakables();
+
+ // Removes breakables from the specified type from the DSG
+ void RemoveFromDSG( BreakablesEnum::BreakableID );
+
+ // Removes every breakable thats in the DSG
+ void RemoveAllFromDSG();
+
+ // Trigger a breakable object animation
+ void Play( BreakablesEnum::BreakableID, const rmt::Matrix& transform );
+
+ // Flags the given DSG object as breakable object that was broken.
+ // It will need to be removed from the world, but not right away.
+ // It might still be in use with the collision system
+ void RemoveBrokenObjectFromWorld( IEntityDSG*, RenderEnums::LayerEnum layer, bool useRemoveGuts = true );
+
+ void DebugRender()const;
+ bool IsLoaded( BreakablesEnum::BreakableID type );
+ void AddToZoneList( BreakablesEnum::BreakableID type );
+
+ // The section name associated with breakables
+ const char* GetInvSectionName()const { return sInventorySectionName; }
+ tUID GetInvSectionUID()const { return mInventorySectionUID; }
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ protected:
+
+ private:
+
+ static const char* sInventorySectionName;
+ const tUID mInventorySectionUID;
+
+ // Its a singleton, prevent access to its constructors
+
+ BreakablesManager();
+ ~BreakablesManager();
+ BreakablesManager( const BreakablesManager& );
+ BreakablesManager& operator=( const BreakablesManager& );
+
+ struct ManagedBreakable
+ {
+ ManagedBreakable();
+ ~ManagedBreakable();
+ RenderEnums::LayerEnum mLayer;
+
+ BreakableObjectDSG* mpBreakableDSG;
+ bool mIsActive;
+ void AddToDSG();
+ void RemoveFromDSG();
+ };
+ struct BreakableInstances
+ {
+ BreakableInstances() : list(0), currElement(0), size(0) {}
+
+ ManagedBreakable** list;
+ // Acts as a queue, gets the next item in the list and
+ // increments next
+ ManagedBreakable* Next();
+ unsigned int currElement;
+ unsigned int size;
+ };
+
+ std::vector< BreakableInstances, s2alloc<BreakableInstances> > mBreakablesList;
+
+ // each breakable type can be held in multiple zones
+ typedef SwapArray< tName > tNameList;
+ SwapArray< tNameList > mZoneList;
+
+ // A list of DSG objects that were broken and need to be removed at the end of the frame
+ struct BrokenObject
+ {
+ IEntityDSG* pDSG;
+ RenderEnums::LayerEnum layer;
+ bool useRemoveGuts;
+ };
+ SwapArray< BrokenObject > mBreakableRemoveQueue;
+
+ static BreakablesManager* spInstance;
+};
+
+
+// A little syntactic sugar for getting at this singleton.
+inline BreakablesManager* GetBreakablesManager()
+{
+ return( BreakablesManager::GetInstance() );
+}
+#endif \ No newline at end of file
diff --git a/game/code/roads/allroads.cpp b/game/code/roads/allroads.cpp
new file mode 100644
index 0000000..38a1c3f
--- /dev/null
+++ b/game/code/roads/allroads.cpp
@@ -0,0 +1,10 @@
+#include <roads/geometry.cpp>
+#include <roads/intersection.cpp>
+#include <roads/lane.cpp>
+#include <roads/road.cpp>
+#include <roads/roadmanager.cpp>
+#include <roads/roadrender.cpp>
+#include <roads/roadrendertest.cpp>
+#include <roads/roadsegment.cpp>
+#include <roads/roadsegmentdata.cpp>
+#include <roads/trafficcontrol.cpp>
diff --git a/game/code/roads/geometry.cpp b/game/code/roads/geometry.cpp
new file mode 100644
index 0000000..d6ed51d
--- /dev/null
+++ b/game/code/roads/geometry.cpp
@@ -0,0 +1,1024 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: geometry.cpp
+//
+// Description: Some linear algebra/geometry stuff mostly used in traffic
+// Also contains some useful structures.
+//
+// History: 09/09/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+
+
+#include <roads/geometry.h>
+#include <raddebug.hpp>
+
+/*
+//////////////////////////////////////////////////////////////////////////////
+// OLD OLD OLD STUFF
+//////////////////////////////////////////////////////////////////////////////
+
+bool fequals(float a, float b)
+{
+ return (rmt::Fabs(a-b)<=MYEPSILON)? true: false;
+}
+bool fequals(float a, float b, float epsilon)
+{
+ return (rmt::Fabs(a-b)<=epsilon)? true: false;
+}
+bool isVerticalLine( Line line )
+{
+ if( fequals(line.x1,line.x2) )
+ {
+ return true;
+ }
+ return false;
+}
+Line getLine( float x1, float y1, float x2, float y2, bool isInfinite )
+{
+ Line line;
+ line.x1 = x1;
+ line.y1 = y1;
+ line.x2 = x2;
+ line.y2 = y2;
+ line.isVertical = isVerticalLine( line );
+ if( !line.isVertical )
+ {
+ line.slope = (line.y2 - line.y1)/(line.x2 - line.x1);
+ line.b = line.y2 - line.slope * line.x2;
+ }
+ line.isInfinite = isInfinite;
+ line.isFinishLine = false;
+ return line;
+}
+bool isPointOnLine( Line line, Point p )
+{
+ // test first if point lies in line equation
+ if( !line.isVertical )
+ {
+ if( fequals(p.y, line.slope * p.x + line.b) )
+ {
+ // Ok, so we've verified that p lies on the line.
+ // now if the line is infinite, we're done
+ if( line.isInfinite )
+ {
+ return true;
+ }
+
+ // else test if point lies on line itself we always make sure
+ // x1 < x2 and y1 < y2, swapping values as needed.
+ if( line.x2 < line.x1 )
+ {
+ float temp = line.x2;
+ line.x2 = line.x1;
+ line.x1 = temp;
+ }
+ if( line.y2 < line.y1 )
+ {
+ float temp = line.y2;
+ line.y2 = line.y1;
+ line.y1 = temp;
+ }
+
+ // now check if p.x lies between acceptable values of x
+ // and if p.y lies between acceptable values of y
+ if( (line.x1 - MYEPSILON < p.x && p.x < line.x2 + MYEPSILON ) &&
+ (line.y1 - MYEPSILON < p.y && p.y < line.y2 + MYEPSILON ) )
+ {
+ return true;
+ }
+ }
+ }
+ else
+ { // if vertical line
+ if( fequals(p.x,line.x1) )
+ {
+ if( line.isInfinite )
+ {
+ return true;
+ }
+ if( line.y2 < line.y1 )
+ {
+ float temp = line.y2;
+ line.y2 = line.y1;
+ line.y1 = temp;
+ }
+
+ if( line.y1 - MYEPSILON < p.y && p.y < line.y2 + MYEPSILON )
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+//==============================================================================
+// Helper function: IntersectLines2D
+//===============================================================================
+// Description: Intersects two co-planar lines
+//
+// Parameters: (rmt::Vector) (rmt::Vector) (rmt::Vector) (rmt::Vector) (rmt::Vector&)
+//
+// Return: bool
+//
+//==============================================================================
+bool IntersectLines2D( rmt::Vector p1,
+ rmt::Vector dir1,
+ rmt::Vector p2,
+ rmt::Vector dir2,
+ rmt::Vector& p )
+{
+ // These will temporarily contain our intersection point coords
+ // Remember that Point and Line work on x-y plane, not x-z plane
+ // so let y = z
+ //
+ Point pTemp;
+
+ rmt::Vector q1 = p1 + dir1;
+ rmt::Vector q2 = p2 + dir2;
+
+ Line line1 = getLine( p1.x, p1.z, q1.x, q1.z, true );
+ Line line2 = getLine( p2.x, p2.z, q2.x, q2.z, true );
+
+
+ // treat vertical cases separately
+ if( line1.isVertical && line2.isVertical )
+ {
+ return false;
+ }
+ else if( line1.isVertical && !line2.isVertical )
+ {
+ // line1 is vertical and line2 is not, so pluck line1's
+ // x-value into line2's equation to find y coord of
+ // the potential intersection point.
+ pTemp.x = line1.x1;
+ pTemp.y = line2.slope * pTemp.x + line2.b;
+ if( isPointOnLine(line1,pTemp) && isPointOnLine(line2,pTemp) )
+ {
+ p.x = pTemp.x;
+ p.y = p1.y;
+ p.z = pTemp.y;
+ return true;
+ }
+ return false;
+ }
+ //else if( !isVerticalLine(line1) && isVerticalLine(line2) )
+ else if( !line1.isVertical && line2.isVertical )
+ {
+ // same as case above, but switch line1, line2
+ pTemp.x = line2.x1;
+ pTemp.y = line1.slope * pTemp.x + line1.b;
+ if( isPointOnLine(line1,pTemp) && isPointOnLine(line2,pTemp) )
+ {
+ p.x = pTemp.x;
+ p.y = p1.y;
+ p.z = pTemp.y;
+ return true;
+ }
+ return false;
+ }
+ else
+ { //both non vertical
+
+ if( line1.slope == line2.slope )
+ return false;
+
+ // find intersection point
+ pTemp.x = (line2.b-line1.b)/(line1.slope-line2.slope);
+ pTemp.y = line1.slope* pTemp.x + line1.b;
+
+ // make sure this point lies within the bounds of both lines
+ if( isPointOnLine(line1,pTemp) && isPointOnLine(line2,pTemp) )
+ {
+ p.x = pTemp.x;
+ p.y = p1.y;
+ p.z = pTemp.y;
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ return false; // just in case
+}
+*/
+
+
+//////////////////////////////////////////////////////////////////////////////////
+// CUBIC BEZIER SHEEYATSU
+//////////////////////////////////////////////////////////////////////////////////
+
+bool CubicBezier::sIsInitialized = false;
+float CubicBezier::B0[CubicBezier::MAX_CURVE_POINTS] = {0.0f};
+float CubicBezier::B1[CubicBezier::MAX_CURVE_POINTS] = {0.0f};
+float CubicBezier::B2[CubicBezier::MAX_CURVE_POINTS] = {0.0f};
+float CubicBezier::B3[CubicBezier::MAX_CURVE_POINTS] = {0.0f};
+
+
+void CubicBezier::InitOnceLUTs()
+{
+ if( sIsInitialized )
+ {
+ return;
+ }
+
+ float bias = 1.0f / (float)(MAX_CURVE_POINTS - 1);
+
+ int i = MAX_CURVE_POINTS - 1;
+ float t = 0.0f, t2, t3;
+ for( i = MAX_CURVE_POINTS-1; i>=0; i-- )
+ {
+ t2 = t*t;
+ t3 = t2*t;
+
+ // Fill the LUTs
+ B0[i] = t3;
+ B1[i] = 3.0f * t2 * (1.0f-t);
+ B2[i] = 3.0f * t * (1.0f-t)*(1.0f-t);
+ B3[i] = (1.0f-t)*(1.0f-t)*(1.0f-t);
+
+ t += bias;
+ }
+
+ rAssert( i == -1);
+
+ sIsInitialized = true;
+}
+
+
+
+CubicBezier::CubicBezier()
+{
+ if( !CubicBezier::sIsInitialized )
+ {
+ CubicBezier::InitOnceLUTs();
+ }
+
+ mCurveIsCreated = false;
+ mCurveIsCreated2D = false;
+ mNumControlPointsAdded = 0;
+
+ rmt::Vector dummy( 0.0f, 0.0f, 0.0f );
+ int i;
+ for( i=0; i<CubicBezier::MAX_CONTROL_POINTS; i++ )
+ {
+ AddControlPoint( dummy );
+ }
+}
+
+CubicBezier::~CubicBezier()
+{
+}
+
+void CubicBezier::GetCubicBezierCurve(rmt::Vector*& pts, int& nPts)
+{
+ if( !mCurveIsCreated )
+ {
+ CreateCubicBezierCurve();
+ }
+ pts = mCurve;
+ nPts = MAX_CURVE_POINTS;
+}
+
+void CubicBezier::GetCubicBezierCurve2D(rmt::Vector*& pts, int& nPts)
+{
+ if( !mCurveIsCreated2D )
+ {
+ CreateCubicBezierCurve2D();
+ }
+ pts = mCurve2D;
+ nPts = MAX_CURVE_POINTS;
+}
+
+void CubicBezier::SetControlPoint( const rmt::Vector &cp, const int index )
+{
+ rAssert( 0 <= index && index < mNumControlPointsAdded );
+
+ mCurveIsCreated = false;
+ mCurveIsCreated2D = false;
+ mControlPoints[index] = cp;
+}
+
+void CubicBezier::AddControlPoint( const rmt::Vector &cp )
+{
+ rAssert( 0 <= mNumControlPointsAdded && mNumControlPointsAdded < MAX_CONTROL_POINTS );
+
+ mCurveIsCreated = false;
+ mCurveIsCreated2D = false;
+ mControlPoints[mNumControlPointsAdded] = cp;
+ mNumControlPointsAdded++;
+}
+
+
+void CubicBezier::CreateCubicBezierCurve()
+{
+ rAssert( mNumControlPointsAdded == MAX_CONTROL_POINTS );
+
+ if( mCurveIsCreated )
+ {
+ return;
+ }
+
+ int i;
+ for( i=0; i<MAX_CURVE_POINTS; i++ )
+ {
+ mCurve[i] = mControlPoints[0] * B0[i] +
+ mControlPoints[1] * B1[i] +
+ mControlPoints[2] * B2[i] +
+ mControlPoints[3] * B3[i];
+ }
+ mCurveIsCreated = true;
+
+}
+
+void CubicBezier::CreateCubicBezierCurve2D()
+{
+ rAssert( mNumControlPointsAdded == MAX_CONTROL_POINTS );
+
+ if( mCurveIsCreated2D )
+ {
+ return;
+ }
+
+ float ep = 0.1f;
+
+ // **** ASSERT INFORMATION ***
+ // If you encounter this assert, it means that the road segments in a nearby
+ // intersection don't have same values of y where they hit the intersection
+ // (intersection isn't horizontal). This is due to BAD ROAD DATA ON WORLD BUILDER SIDE.
+ //
+ // Because we're using CubicBezier in 2D now, the intersection must be FLAT!
+ // Or cars will "hop" when they make the turn.
+ //
+ // Please notify Sheik to check the road data around nearby intersections.
+ rAssert( rmt::Epsilon( mControlPoints[0].y, mControlPoints[1].y, ep ) &&
+ rmt::Epsilon( mControlPoints[0].y, mControlPoints[2].y, ep ) &&
+ rmt::Epsilon( mControlPoints[0].y, mControlPoints[3].y, ep ) );
+
+ int i;
+ for( i=0; i<MAX_CURVE_POINTS; i++ )
+ {
+ mCurve2D[i].x = mControlPoints[0].x * B0[i] +
+ mControlPoints[1].x * B1[i] +
+ mControlPoints[2].x * B2[i] +
+ mControlPoints[3].x * B3[i];
+
+ mCurve2D[i].y = mControlPoints[0].y;
+
+ mCurve2D[i].z = mControlPoints[0].z * B0[i] +
+ mControlPoints[1].z * B1[i] +
+ mControlPoints[2].z * B2[i] +
+ mControlPoints[3].z * B3[i];
+ }
+ mCurveIsCreated2D = true;
+
+}
+
+/////////////////////////////////////////////////////////////////
+// DListArray Class Definition
+//////////////////////////////////////////////////////////////////
+
+DListArray::DListArray()
+{
+ this->Clear();
+}
+
+
+void DListArray::Clear()
+{
+ int i=0;
+ for( i ; i<(MAX_ELEMS-1) ; ++i )
+ {
+ mElems[i].data = NULL;
+ mElems[i].next = i+1;
+ mElems[i].prev = -1;
+ }
+ mElems[i].data = NULL;
+ mElems[i].next = -1;
+ mElems[i].prev = -1;
+
+ mHead = -1;
+ mTail = -1;
+ mFree = 0;
+
+ mnElems = 0;
+}
+
+// returns the index value of the newly added element
+// or -1 on error
+int DListArray::AddLast( void* data )
+{
+ assert( data != NULL );
+
+ if( mFree == -1 )
+ {
+ return -1;
+ }
+
+ mElems[mFree].data = data;
+ mElems[mFree].prev = mTail;
+
+ if( mnElems > 0 )
+ {
+ mElems[mTail].next = mFree;
+ }
+ else
+ {
+ mHead = mFree;
+ }
+
+ mTail = mFree;
+ mFree = mElems[mFree].next;
+ mElems[mTail].next = -1;
+
+ mnElems++;
+ return mTail;
+}
+
+// returns the index value of the newly added element
+// or -1 on error
+int DListArray::AddFirst( void* data )
+{
+ assert( data != NULL );
+
+ if( mFree == -1 )
+ {
+ return -1;
+ }
+
+ int newIndex = mFree;
+ mFree = mElems[mFree].next;
+
+ mElems[newIndex].data = data;
+ mElems[newIndex].next = mHead;
+
+ if( mnElems > 0 )
+ {
+ mElems[mHead].prev = newIndex;
+ }
+ else
+ {
+ mTail = newIndex;
+ }
+
+ mHead = newIndex;
+
+ mnElems++;
+ return mHead;
+}
+
+
+// returns index value of the newly inserted element
+// or -1 on error
+int DListArray::InsertAfter( void* data, int i )
+{
+ assert( mnElems >= 1 );
+ assert( data != NULL );
+ assert( 0 <= i && i < MAX_ELEMS );
+ assert( mElems[i].data != NULL );
+
+ if( mFree == -1 )
+ {
+ return -1;
+ }
+
+ int newIndex = mFree;
+ mFree = mElems[mFree].next;
+
+ mElems[newIndex].data = data;
+ mElems[newIndex].prev = i;
+ mElems[newIndex].next = mElems[i].next;
+
+ if( mElems[i].next != -1 )
+ {
+ mElems[ mElems[i].next ].prev = newIndex;
+ }
+ else
+ {
+ mTail = newIndex;
+ }
+
+ mElems[i].next = newIndex;
+
+ mnElems++;
+ return newIndex;
+}
+
+//
+bool DListArray::Remove( void* data )
+{
+ assert( data != NULL );
+
+ int i = 0;
+ bool res = false;
+ for( i ; i<mnElems ; i++ )
+ {
+ if( mElems[i].data == data )
+ {
+ res = true;
+ break;
+ }
+ }
+ if( res )
+ {
+ res = Remove(i);
+ }
+ return res;
+}
+
+bool DListArray::Remove( int i )
+{
+ assert( 0 <= i && i < MAX_ELEMS );
+ assert( mElems[i].data != NULL );
+
+ if( mnElems <= 0 )
+ {
+ return false;
+ }
+
+ if( mElems[i].next != -1 )
+ {
+ mElems[ mElems[i].next ].prev = mElems[i].prev;
+ }
+ else
+ {
+ mTail = mElems[i].prev;
+ }
+
+ if( mElems[i].prev != -1 )
+ {
+ mElems[ mElems[i].prev ].next = mElems[i].next;
+ }
+ else
+ {
+ mHead = mElems[i].next;
+ }
+
+ mElems[i].next = mFree;
+ mElems[i].prev = -1;
+ mElems[i].data = NULL;
+
+ mFree = i;
+
+ mnElems--;
+ return true;
+}
+
+int DListArray::Find( void* data )
+{
+ rAssert( data != NULL );
+
+ int i = 0;
+ for( i ; i<mnElems ; i++ )
+ {
+ if( mElems[i].data == data )
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// MISC
+//////////////////////////////////////////////////////////////////////////////
+
+// for Ray p1 to p2 and sphere 2
+// return number of intersection points and the intersection points
+// q1 and q2
+int IntersectLineSphere( const rmt::Vector& p1,
+ const rmt::Vector& p2,
+ const rmt::Sphere& s,
+ rmt::Vector* intPts )
+{
+
+ float X1 = (float)p1.x;
+ float X2 = (float)p2.x;
+ float X3 = (float)s.centre.x;
+ float Y1 = (float)p1.y;
+ float Y2 = (float)p2.y;
+ float Y3 = (float)s.centre.y;
+ float Z1 = (float)p1.z;
+ float Z2 = (float)p2.z;
+ float Z3 = (float)s.centre.z;
+ float Sr = (float)s.radius;
+
+ float A, B, C;
+ A = (X2 - X1)*(X2 - X1) + (Y2 - Y1)*(Y2 - Y1) + (Z2 - Z1)*(Z2 - Z1);
+ B = (X2 - X1)*(X1 - X3) + (Y2 - Y1)*(Y1 - Y3) + (Z2 - Z1)*(Z1 - Z3);
+ C = (X1 - X3)*(X1 - X3) + (Y1 - Y3)*(Y1 - Y3) + (Z1 - Z3)*(Z1 - Z3) - Sr*Sr;
+ //B = 2 * ((X2 - X1)*(X1 - X3) + (Y2 - Y1)*(Y1 - Y3) + (Z2 - Z1)*(Z1 - Z3));
+ //C = X3*X3 + Y3*Y3 + Z3*Z3 + X1*X1 + Y1*Y1 + Z1*Z1 - 2*(X3*X1 + Y3*Y1 + Z3*Z1) - Sr*Sr;
+
+ float discriminant = B*B - A*C;
+ //float discriminant = B*B - 4*A*C;
+ float t[2];
+
+
+ if( discriminant < 0.0f )
+ {
+ return 0;
+ }
+ else if( rmt::Epsilon(discriminant, 0.0f, 0.001f) )
+ {
+ //t[0] = (-1*B)/2*A;
+ t[0] = (-1*B)/A;
+
+ if( 0.0f <= t[0] && t[0] <= 1.0f )
+ {
+ intPts[0].Set(
+ (float)(X1+t[0]*(X2-X1)),
+ (float)(Y1+t[0]*(Y2-Y1)),
+ (float)(Z1+t[0]*(Z2-Z1)));
+ return 1;
+ }
+ return 0;
+ }
+ else
+ {
+ float sqrtDiscriminant = rmt::Sqrt(discriminant); // *** SQUARE ROOT! ***
+// t[0] = (-1*B - sqrtDiscriminant) / 2*A;
+// t[1] = (-1*B + sqrtDiscriminant) / 2*A;
+ t[0] = (-1*B - sqrtDiscriminant) / A;
+ t[1] = (-1*B + sqrtDiscriminant) / A;
+
+ rmt::Vector testVec;
+ int i=0, j=0;
+ for( i; i<2; i++)
+ {
+ if( 0.0f <= t[i] && t[i] <= 1.0f )
+ {
+ intPts[j].Set(
+ (float)(X1+t[i]*(X2-X1)),
+ (float)(Y1+t[i]*(Y2-Y1)),
+ (float)(Z1+t[i]*(Z2-Z1)));
+ j++;
+ }
+ }
+ return j;
+ }
+ return -1;
+}
+
+
+bool TestIntersectLineSphere( const rmt::Vector& lOrig,
+ const rmt::Vector& lDir,
+ const rmt::Sphere& s )
+{
+ rmt::Vector closestPtOnLine;
+ FindClosestPointOnLine( lOrig, lOrig+lDir, s.centre, closestPtOnLine );
+
+ float distSqr = (s.centre - closestPtOnLine).MagnitudeSqr();
+ bool res = distSqr <= (s.radius * s.radius);
+ return res;
+}
+
+
+
+// Test using (normalized) myHeading DOT vectorFromMyHeadingToTarget
+bool WillCollide( const rmt::Vector& myPos,
+ const rmt::Vector& myHeading, // Must be normalized
+ const rmt::Vector& mySide, // Must be normalized
+ float myRadius,
+ float myLookAheadDist,
+ const rmt::Vector& targetPos,
+ bool& targetOnMyRightSide )
+{
+ rmt::Vector toTarget = targetPos - myPos;
+ float myLookAheadDistSqr = myLookAheadDist * myLookAheadDist;
+
+ // if target lies within my look-ahead distance
+ if( toTarget.MagnitudeSqr() < myLookAheadDistSqr )
+ {
+ float frontOrBehindTest = myHeading.Dot( toTarget );
+ if( frontOrBehindTest > 0.0f )
+ {
+ // target is in front, which is a concern...
+ float lateralDist = mySide.Dot( toTarget );
+ if( lateralDist > 0.0f )
+ {
+ targetOnMyRightSide = true;
+ }
+ else
+ {
+ targetOnMyRightSide = false;
+ }
+ lateralDist = rmt::Fabs( lateralDist );
+
+ // if target is in our path
+ if( lateralDist <= myRadius )
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+rmt::Vector UpdateVUP( const rmt::Vector& position, const rmt::Vector& target )
+{
+ const float epsilon = 0.01f;
+ //Set the vUP by projecting the heading into the ZX plane and creating a
+ //crossproduct of a right angle to the projected heading along the X axis
+ //and the heading.
+ rmt::Vector X, Y, Z;
+ Z.Sub(target, position);
+ X.Set(Z.z, 0, -Z.x);
+
+ if ( rmt::Epsilon( X.x, 0, epsilon ) &&
+ rmt::Epsilon( X.y, 0, epsilon ) &&
+ rmt::Epsilon( X.z, 0, epsilon ) )
+ {
+ //Then the camera is looking straight down.
+ Y.Set( 0, 0, 1.0f ); //Up along the Z...
+ }
+ else
+ {
+ Y.CrossProduct(Z, X);
+ Y.Normalize(); // *** SQUARE ROOT! ***
+ }
+
+ return Y;
+}
+
+// Given points P1 and P2, and two points that define a line, A and B
+// P1 and P2 are on the same side of the line, if the Normals for BAxP1A
+// and BAxP2A are pointing on the same side of the plane (i.e.
+// N1-dot-N2 >= 0)
+
+bool PointsOnSameSideOfLine( const rmt::Vector& P1,
+ const rmt::Vector& P2,
+ const rmt::Vector& A,
+ const rmt::Vector& B )
+{
+ rmt::Vector BA, P1A, P2A, crossP1, crossP2;
+
+ BA.Sub( B, A );
+ P1A.Sub( P1, A );
+ P2A.Sub( P2, A );
+
+ crossP1.CrossProduct( BA, P1A );
+ crossP2.CrossProduct( BA, P2A );
+ if( crossP1.Dot(crossP2) >= 0 )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+
+// Given triangle with vertices v1, v2, v3 and a point p
+// p is inside triangle if it is on the same side of line v1v2 as v3
+// and on the same side of line v2v3 as v1,
+// and on the same side of line v1v3 as v2
+//
+// NOTE: Because we use Which-Side-of-Line solution, the point
+// that is "within" a triangle is not necessarily on the
+// same plane as the triangle (it could still be above or
+// below the actual triangle, as long as it lies within the infinite
+// planes formed by the 3 sides of the triangle.
+//
+bool PointLiesInTriangle ( const rmt::Vector& p,
+ const rmt::Vector& v1,
+ const rmt::Vector& v2,
+ const rmt::Vector& v3 )
+{
+ if( PointsOnSameSideOfLine( p, v1, v2, v3 ) &&
+ PointsOnSameSideOfLine( p, v2, v1, v3 ) &&
+ PointsOnSameSideOfLine( p, v3, v1, v2 ) )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+rmt::Vector GetProjectionVector( const rmt::Vector& source, const rmt::Vector& target )
+{
+ return target * ( target.Dot(source) / target.Dot(target) );
+}
+
+// In Lefthand coordinate system, turn vector to
+// the left (counter-clockwise) 90 degrees about Y axis & return new vector
+rmt::Vector Get90DegreeLeftTurn( const rmt::Vector& orig )
+{
+ rmt::Vector newVec;
+ newVec.Set( -1 * orig.z, orig.y, orig.x );
+ return newVec;
+}
+
+// In Lefthand coordinate system, turn vector to
+// the right (clockwise) 90 degrees about Y axis & return new vector
+rmt::Vector Get90DegreeRightTurn( const rmt::Vector& orig )
+{
+ rmt::Vector newVec;
+ newVec.Set( orig.z, orig.y, -1 * orig.x );
+ return newVec;
+}
+
+
+float GetRotationAboutY( float x, float z )
+{
+ float angle = 0.0f;
+ if( rmt::Epsilon(x, 0.0f) && rmt::Epsilon(z, 0.0f) )
+ {
+ angle = 0.0f;
+ }
+ else
+ {
+ // generate angle from x-z vector
+ // - assumes DEFAULT_FACING_VECTOR is (0,0,-1)
+ // - assumes UP VECTOR is (0,1,0)
+ angle = rmt::ATan2(x, z);
+ if( rmt::IsNan(angle) )
+ {
+ angle = 0.0f;
+ }
+
+ /*
+ angle = rmt::ATan2(-x, -z);
+
+ // wrap to [0, 2*pi)
+ if (angle < 0.0f)
+ {
+ angle += rmt::PI_2;
+ }
+ */
+ }
+ return angle;
+}
+
+
+bool PointToLineProjection2D( const rmt::Vector& inPt,
+ const rmt::Vector& linePt1,
+ const rmt::Vector& linePt2,
+ rmt::Vector& outPt )
+{
+ // The beauty of calling this 2D function is that we break it down into components
+ // so we don't do unnecessary float ops for the y values
+ float x1,x2,x3,z1,z2,z3;
+ x1 = linePt1.x;
+ x2 = linePt2.x;
+ x3 = inPt.x;
+ z1 = linePt1.z;
+ z2 = linePt2.z;
+ z3 = inPt.z;
+
+ // make sure we were given a line, not a point dammit!
+ rAssertMsg( !( rmt::Epsilon( x1,x2,0.0005f ) && rmt::Epsilon( z1,z2,0.0005f )),
+ "PointToLineProjection2D: The two points of the \"line\" are too close together.\n" );
+
+ // THEORY
+ // ======
+ // Let outPt be the point resulting from projecting inPt onto
+ // linesegment [linePt2 - linePt1]:
+ //
+ // 1) outPt = linePt1 + u * [linePt2 - linePt1];
+ //
+ // Since the projection is at Right Angle, we can say that the lines
+ // [linePt2 - linePt1] and [inPt - outPt] have zero-length dotproduct:
+ //
+ // 2) [inPt - outPt] dot [linePt2 - linePt1] = 0
+ //
+ // Substituting 1) into 2) gives:
+ //
+ // 3) [ inPt - [linePt1+u*[linePt2 - linePt1]] ] dot [linePt2 - linePt1] = 0
+ //
+ // Solving for u gives:
+ //
+ float x2MINUSx1 = x2-x1;
+ float z2MINUSz1 = z2-z1;
+ float u = ( (x3-x1)*x2MINUSx1 + (z3-z1)*z2MINUSz1 ) /
+ ( (x2MINUSx1*x2MINUSx1)+(z2MINUSz1*z2MINUSz1) );
+
+ // Populate the return point
+ outPt.Set( x1 + u*x2MINUSx1, inPt.y, z1 + u*z2MINUSz1 );
+
+ // If u is not on the line segment, outPt will not be in bounds
+ if( u < 0.0f || u > 1.0f )
+ {
+ return false;
+ }
+ return true;
+}
+
+
+bool PointOnLeftSideOfLine( const rmt::Vector& p,
+ const rmt::Vector& start,
+ const rmt::Vector& end )
+{
+ rmt::Vector start2p = p - start;
+ rmt::Vector start2end = end - start;
+ rmt::Vector leftVec = Get90DegreeLeftTurn( start2end );
+
+ float dp = leftVec.Dot( start2p );
+ if( dp > 0.0f ) // +ve means on the left
+ {
+ return true;
+ }
+ return false;
+}
+
+bool PointOnRightSideOfLine( const rmt::Vector& p,
+ const rmt::Vector& start,
+ const rmt::Vector& end )
+{
+ rmt::Vector start2p = p - start;
+ rmt::Vector start2end = end - start;
+ rmt::Vector rightVec = Get90DegreeRightTurn( start2end );
+
+ float dp = rightVec.Dot( start2p );
+ if( dp > 0.0f ) // +ve means on the right
+ {
+ return true;
+ }
+ return false;
+}
+
+float FindClosestPointOnLine( const rmt::Vector& start,
+ const rmt::Vector& end,
+ const rmt::Vector& p,
+ rmt::Vector& closestPt )
+{
+
+ rmt::Vector start2p = p - start;
+ rmt::Vector lDir = end - start;
+
+ float lDirMagSqr = lDir.Dot(lDir);
+
+ // if end and start are basically the same point,
+ // then lDir is zero. So the closest point returned
+ // is that point
+ //
+ if( rmt::Epsilon( lDirMagSqr, 0.0f, 0.001f ) )
+ {
+ closestPt = end;
+ return 0.0f;
+ }
+
+ float scale = lDir.Dot( start2p ) / lDirMagSqr;
+ if( scale > 1.0f )
+ {
+ closestPt = end;
+ }
+ else if( scale < 0.0f )
+ {
+ closestPt = start;
+ }
+ else
+ {
+ closestPt = start + lDir * scale;
+ }
+ return scale;
+}
+
+float GetLineSegmentT
+(
+ const rmt::Vector& segStart,
+ const rmt::Vector& segEnd,
+ const rmt::Vector& pt
+)
+{
+ float e = 0.0001f;
+
+#if defined( RAD_DEBUG ) || defined( RAD_TUNE )
+ // make sure this point is on the line
+ rmt::Vector closestPt;
+ FindClosestPointOnLine( segStart, segEnd, pt, closestPt );
+ rAssert( closestPt.Equals( pt, e ) );
+#endif
+
+ float segT = 0.0f;
+ if( rmt::Epsilon( segEnd.x, segStart.x, e ) )
+ {
+ if( rmt::Epsilon( segEnd.y, segStart.y, e ) )
+ {
+ if( rmt::Epsilon( segEnd.z, segStart.z, e ) )
+ {
+ segT = 0.0f;
+ }
+ else
+ {
+ segT = (pt.z - segStart.z) / (segEnd.z - segStart.z);
+ }
+ }
+ else
+ {
+ segT = (pt.y - segStart.y) / (segEnd.y - segStart.y);
+ }
+ }
+ else
+ {
+ segT = (pt.x - segStart.x) / (segEnd.x - segStart.x);
+ }
+ rAssert( 0.0f <= segT && segT <= 1.0f );
+ return segT;
+} \ No newline at end of file
diff --git a/game/code/roads/geometry.h b/game/code/roads/geometry.h
new file mode 100644
index 0000000..66620da
--- /dev/null
+++ b/game/code/roads/geometry.h
@@ -0,0 +1,448 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: geometry.h
+//
+// Description: Some linear algebra/geometry stuff mostly used in traffic
+// Also contains some useful structures.
+//
+// History: 09/09/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+
+
+#ifndef GEOMETRY_H
+#define GEOMETRY_H
+
+// *************************
+// 2D & 3D GEOMETRY HELPERS
+// *************************
+#include <radmath/radmath.hpp>
+#include <raddebug.hpp> // for rAssert & other debug print outs
+
+/*
+//////////////////////////////////////////////////////////////////////////////
+// OLD OLD OLD STUFF
+//////////////////////////////////////////////////////////////////////////////
+#ifndef PI_F
+#define PI_F 3.1415926535897932384626433832795f
+#endif
+#define MYEPSILON 0.001
+#ifndef NULL
+ #define NULL 0
+#endif
+struct Line
+{
+ float x1;
+ float y1;
+ float x2;
+ float y2;
+ float slope;
+ float b;
+ bool isVertical;
+ bool isInfinite;
+ bool isFinishLine;
+};
+
+struct Point
+{
+ float x;
+ float y;
+ int id;
+};
+bool fequals(float a, float b);
+bool fequals(float a, float b, float epsilon);
+bool isVerticalLine( Line line );
+Line getLine( float x1, float y1, float x2, float y2, bool isInfinite );
+bool isPointOnLine( Line line, Point p );
+bool IntersectLines2D( rmt::Vector p1,
+ rmt::Vector dir1,
+ rmt::Vector p2,
+ rmt::Vector dir2,
+ rmt::Vector& p );
+*/
+
+//////////////////////////////////////////////////////////////////////////////
+// CUBIC BEZIER SHEEYATSU
+//////////////////////////////////////////////////////////////////////////////
+
+class CubicBezier
+{
+public:
+ enum
+ {
+ MAX_CONTROL_POINTS = 4, // total control points (including start & end points)
+ MAX_CURVE_POINTS = 30 // total curve points including start & end points
+ };
+
+ static void InitOnceLUTs();
+
+ static bool sIsInitialized;
+ static float B0[MAX_CURVE_POINTS];
+ static float B1[MAX_CURVE_POINTS];
+ static float B2[MAX_CURVE_POINTS];
+ static float B3[MAX_CURVE_POINTS];
+
+ CubicBezier();
+ ~CubicBezier();
+
+ void GetCubicBezierCurve(rmt::Vector*& pts, int& nCurvePts);
+ void GetCubicBezierCurve2D(rmt::Vector*& pts, int& nCurvePts);
+ void AddControlPoint(const rmt::Vector& cp);
+ void SetControlPoint(const rmt::Vector& cp, int index);
+
+
+protected:
+
+ void CreateCubicBezierCurve();
+ void CreateCubicBezierCurve2D();
+
+ rmt::Vector mCurve[MAX_CURVE_POINTS];
+ rmt::Vector mCurve2D[MAX_CURVE_POINTS];
+ rmt::Vector mControlPoints[MAX_CONTROL_POINTS];
+ int mNumControlPointsAdded;
+ bool mCurveIsCreated;
+ bool mCurveIsCreated2D;
+
+ //Prevent wasteful constructor creation.
+ CubicBezier( const CubicBezier& CubicBezier );
+ CubicBezier& operator=( const CubicBezier& CubicBezier );
+
+};
+
+
+
+//////////////////////////////////////////////////////////////////////////////
+// DListArray
+//////////////////////////////////////////////////////////////////////////////
+
+class DListArray
+{
+
+public:
+
+ enum
+ {
+ MAX_ELEMS = 20
+ };
+
+ DListArray();
+ void Clear();
+
+ // returns index value of found element, -1 on error
+ int Find( void* data );
+
+ // returns the index value of the newly added element
+ // or -1 on error
+ int AddLast( void* data );
+
+ // returns the index value of the newly added element
+ // or -1 on error
+ int AddFirst( void* data );
+
+ // returns index value of the newly inserted element
+ // or -1 on error
+ int InsertAfter( void* data, int i );
+
+ // Note: this incurs a linear search
+ bool Remove( void* data );
+
+ bool Remove( int i );
+
+ int GetNumElems() const;
+
+ int GetFree() const;
+
+ void* GetDataAt( int i ) const;
+
+ int GetNextOf( int i ) const;
+
+ int GetPrevOf( int i ) const;
+
+ void* GetFirst() const;
+
+ void* GetLast() const;
+
+ int GetHead() const;
+
+ int GetTail() const;
+
+private:
+
+ struct DLAElem
+ {
+ void* data;
+ int next;
+ int prev;
+ };
+
+ DLAElem mElems[MAX_ELEMS];
+ int mnElems;
+ int mHead;
+ int mTail;
+ int mFree;
+
+};
+
+inline int DListArray::GetNumElems() const
+{
+ return mnElems;
+}
+inline int DListArray::GetFree() const
+{
+ return mFree;
+}
+inline void* DListArray::GetDataAt(int i) const
+{
+ return mElems[i].data;
+}
+
+inline void* DListArray::GetFirst() const
+{
+ if( mHead != -1 )
+ {
+ return mElems[mHead].data;
+ }
+ return NULL;
+}
+inline void* DListArray::GetLast() const
+{
+ if( mTail != -1 )
+ {
+ return mElems[mTail].data;
+ }
+ return NULL;
+}
+inline int DListArray::GetHead() const
+{
+ return mHead;
+}
+inline int DListArray::GetTail() const
+{
+ return mTail;
+}
+
+inline int DListArray::GetNextOf( int i ) const
+{
+ rAssert( 0 <= i && i < MAX_ELEMS );
+ return mElems[i].next;
+}
+
+inline int DListArray::GetPrevOf( int i ) const
+{
+ rAssert( 0 <= i && i < MAX_ELEMS );
+ return mElems[i].prev;
+}
+
+
+// history tracking
+template <class T, int HISTORY_SIZE> class History
+{
+public:
+ History() : mNextSpot(0) {}
+ ~History() {}
+
+ void Init( const T& t )
+ {
+ mNextSpot = 0;
+
+ for( int i=0; i< HISTORY_SIZE; i++ )
+ {
+ mHistory[i] = t;
+ }
+
+ mAverage = t;
+ }
+
+ void UpdateHistory( const T& t)
+ {
+ // first get the old value & recalculate our average
+ mAverage -= (mHistory[mNextSpot] - t) / (float)(HISTORY_SIZE);
+ mHistory[mNextSpot] = t;
+ mNextSpot = (mNextSpot + 1) % HISTORY_SIZE;
+ }
+
+ void GetAverage( T& t )
+ {
+ t = mAverage;
+ }
+
+ T GetEntry( int i )
+ {
+ rAssert( 0 <= i && i < HISTORY_SIZE );
+ return mHistory[i];
+ }
+
+ T GetLastEntry()
+ {
+ int i = mNextSpot - 1;
+ if( i == -1 )
+ {
+ i = HISTORY_SIZE - 1;
+ }
+ rAssert( 0 <= i && i < HISTORY_SIZE );
+ return mHistory[i];
+ }
+
+ int GetSize()
+ {
+ return HISTORY_SIZE;
+ }
+
+private:
+ int mNextSpot;
+ T mHistory[HISTORY_SIZE];
+ T mAverage;
+};
+
+template <int HISTORY_SIZE> class VectorHistory : public History<rmt::Vector, HISTORY_SIZE>
+{
+public:
+ VectorHistory() {}
+ ~VectorHistory() {}
+
+ void Init( const rmt::Vector& t )
+ {
+ History<rmt::Vector,HISTORY_SIZE>::Init(t);
+ GetAverage(mNormalizedAverage);
+ }
+
+ void GetNormalizedAverage( rmt::Vector& vec )
+ {
+ if( rmt::Epsilon( mNormalizedAverage.MagnitudeSqr(), 1.0f, 0.0005f ) )
+ {
+ vec = mNormalizedAverage;
+ }
+ else
+ {
+ mNormalizedAverage.NormalizeSafe();
+ vec = mNormalizedAverage;
+ }
+ }
+
+ void UpdateHistory( const rmt::Vector& vec )
+ {
+ History<rmt::Vector,HISTORY_SIZE>::UpdateHistory(vec);
+ GetAverage(mNormalizedAverage);
+ }
+
+protected:
+
+ rmt::Vector mNormalizedAverage;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+// MISC
+//////////////////////////////////////////////////////////////////////////////
+
+const float KPH_2_MPS = 1.0f/3.60f;
+
+// returns true if projection point is on line segment
+bool PointToLineProjection2D( const rmt::Vector& in,
+ const rmt::Vector& linePt1,
+ const rmt::Vector& linePt2,
+ rmt::Vector& out );
+
+rmt::Vector GetProjectionVector( const rmt::Vector& source,
+ const rmt::Vector& target );
+
+// MAYA: GAME:
+// Right-hand coord Left-hand coord
+//
+// +y (forefinger) +y (forefinger)
+// | |
+// | |
+// | |
+// /\ /\
+// / \ / \
+// / \ / \
+// +z (middle) +x (thumb) +x(thumb) +z(middle)
+//
+
+// In Lefthand coordinate system, turn vector to
+// the left (counter-clockwise) 90 degrees & return new vector
+rmt::Vector Get90DegreeLeftTurn( const rmt::Vector& orig );
+
+// In Lefthand coordinate system, turn vector to
+// the right (clockwise) 90 degrees & return new vector
+rmt::Vector Get90DegreeRightTurn( const rmt::Vector& orig );
+
+float GetRotationAboutY( float x, float z );
+
+// returns the number of intersections and po ints q1 & q2
+int IntersectLineSphere( const rmt::Vector& p1,
+ const rmt::Vector& p2,
+ const rmt::Sphere& s,
+ rmt::Vector* intPts);
+
+// just test if line segment intersects sphere... don't bother
+// finding the intersection point(s)
+bool TestIntersectLineSphere( const rmt::Vector& lOrig,
+ const rmt::Vector& lDir,
+ const rmt::Sphere& s );
+
+
+
+// Test using (normalized) myHeading DOT vectorFromMyHeadingToTarget
+bool WillCollide( const rmt::Vector& myPos,
+ const rmt::Vector& myHeading, // Must be normalized
+ const rmt::Vector& mySide, // Must be normalized
+ float myRadius,
+ float myLookAheadDist,
+ const rmt::Vector& targetPos,
+ bool& targetOnMyRightSide );
+
+rmt::Vector UpdateVUP( const rmt::Vector& position, const rmt::Vector& target );
+
+// Given points P1 and P2, and two points that define a line, A and B
+// P1 and P2 are on the same side of the line, if the Normals for BAxP1A
+// and BAxP2A are pointing on the same side of the plane (i.e.
+// N1-dot-N2 >= 0)
+
+bool PointsOnSameSideOfLine( const rmt::Vector& P1,
+ const rmt::Vector& P2,
+ const rmt::Vector& A,
+ const rmt::Vector& B );
+
+
+
+// Given triangle with vertices v1, v2, v3 and a point p
+// p is inside triangle if it is on the same side of line v1v2 as v3
+// and on the same side of line v2v3 as v1,
+// and on the same side of line v1v3 as v2
+//
+bool PointLiesInTriangle ( const rmt::Vector& p,
+ const rmt::Vector& v1,
+ const rmt::Vector& v2,
+ const rmt::Vector& v3 );
+
+
+// Given a point "p", and a line starting at point "start" and ending
+// at point "end", determine if p lies on the left side of the line
+// (assuming that the line is looking in the direction of start-to-end
+//
+bool PointOnLeftSideOfLine( const rmt::Vector& p,
+ const rmt::Vector& start,
+ const rmt::Vector& end );
+
+// Ditto.. but for the right side
+//
+bool PointOnRightSideOfLine( const rmt::Vector& p,
+ const rmt::Vector& start,
+ const rmt::Vector& end );
+
+// Given a line segment described by vector from start to end,
+// and an arbitrary point... return the point on the line segment
+// closest to this arbitrary point and the float parameter along
+// the line segment at which this closest point occurs
+float FindClosestPointOnLine( const rmt::Vector& start,
+ const rmt::Vector& end,
+ const rmt::Vector& p,
+ rmt::Vector& closestPt );
+
+float GetLineSegmentT( const rmt::Vector& segStart,
+ const rmt::Vector& segEnd,
+ const rmt::Vector& pt );
+
+#endif // GEOMETRY_H
diff --git a/game/code/roads/intersection.cpp b/game/code/roads/intersection.cpp
new file mode 100644
index 0000000..25f12f1
--- /dev/null
+++ b/game/code/roads/intersection.cpp
@@ -0,0 +1,1374 @@
+/*===========================================================================
+ Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+
+ Component: Intersection
+
+ Description:
+
+
+ Authors: Travis Brown-John
+
+ Revisions Date Author Revision
+ 2001/02/02 Tbrown-John Created
+
+===========================================================================*/
+
+#include <roads/intersection.h>
+#include <roads/road.h>
+#include <roads/roadsegment.h>
+#include <roads/lane.h>
+#include <roads/trafficcontrol.h>
+
+#include <meta/triggerlocator.h>
+#include <memory/srrmemory.h>
+
+// Math includes.
+//
+#ifdef TOOLS
+#include <choreo/utility.hpp>
+#else
+#include <choreo/utility.hpp>
+#endif
+
+
+Intersection::Intersection( void )
+:
+mBigIntersection( 0 ),
+mIndex( -1 ),
+mpTrafficControl( NULL ),
+mnRoadsIn( 0 ),
+mnRoadsOut( 0 ),
+mfRotation( 0.0f ),
+mfRadius( 15.0f )
+{
+ mpAnimEntityList.Allocate(2);
+ int i;
+
+ for ( i = 0; i < MAX_ROADS; i++ )
+ {
+ mRoadListIn[ i ] = 0;
+ mRoadListOut[ i ] = 0;
+ }
+
+ mLightControl.SetIntersection( this );
+ mNWayControl.SetIntersection( this );
+}
+
+Intersection::Intersection( TriggerLocator* pLocator )
+:
+mBigIntersection( 0 ),
+mIndex( -1 ),
+mpTrafficControl( NULL ),
+mnRoadsIn( 0 ),
+mnRoadsOut( 0 ),
+mfRotation( 0.0f ),
+mfRadius( 15.0f )
+{
+ mpAnimEntityList.Allocate(2);
+
+ int i;
+
+ for ( i = 0; i < MAX_ROADS; i++ )
+ {
+ mRoadListIn[ i ] = 0;
+ mRoadListOut[ i ] = 0;
+ }
+ mLightControl.SetIntersection( this );
+ mNWayControl.SetIntersection( this );
+}
+
+Intersection::~Intersection( void )
+{
+ mWaitingRoads.Clear();
+ mShortestRoadsToAdjacentIntersectionsWithMultiplier.Clear();
+ mShortestRoadsToAdjacentIntersectionsNoMultiplier.Clear();
+ mOutgoingShortcuts.Clear();
+ if( mBigIntersection )
+ {
+ mBigIntersection->routesWithMultiplier.Clear();
+ mBigIntersection->routesNoMultiplier.Clear();
+ delete mBigIntersection;
+ }
+}
+// Simulate the intersection when there are cars nearby.
+/*
+==============================================================================
+Intersection::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( unsigned int dt )
+
+Return: void
+
+=============================================================================
+*/
+void Intersection::Update( unsigned int dt )
+{
+ if( mpTrafficControl != NULL )
+ {
+ mpTrafficControl->Update( dt );
+ }
+}
+
+// advance the traffic in roads 'RoadMask' according to light state.
+/*
+==============================================================================
+Intersection::AdvanceTraffic
+==============================================================================
+Description: Comment
+
+Parameters: ( unsigned int RoadMask, unsigned int state )
+
+Return: void
+
+=============================================================================
+*/
+void Intersection::AdvanceTraffic( unsigned int RoadMask, unsigned int state ) const
+{
+ int i;
+
+ for ( i = 0; i < MAX_ROADS; i++ )
+ {
+ unsigned int mask = 1 << i;
+ if ( RoadMask & mask )
+ {
+ if ( mRoadListIn[ i ] )
+ {
+ unsigned int j;
+ for ( j = 0; j < mRoadListIn[i]->GetNumLanes(); j++ )
+ {
+ Lane* pLane = mRoadListIn[i]->GetLane( j );
+ if ( pLane )
+ {
+ pLane->NotifyWaitingTraffic( state );
+ }
+ }
+ }
+ }
+ }
+}
+
+
+void Intersection::AdvanceNextWaitingRoad()
+{
+ if( mWaitingRoads.mUseSize > 0 )
+ {
+ Road* road = mWaitingRoads[0];
+ for( unsigned int i=0; i<road->GetNumLanes(); i++ )
+ {
+ road->GetLane(i)->NotifyWaitingTraffic( TrafficControl::GREEN );
+ }
+ mWaitingRoads.RemoveKeepOrder( 0 );
+ }
+}
+
+// Is the intersection clear of all cars.
+/*
+==============================================================================
+Intersection::IsIntersectionClear
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+bool Intersection::IsIntersectionClear( void ) const
+{
+ // TODO: Implement this properly.
+ return true;
+}
+
+// add a road to the in list.
+/*
+==============================================================================
+Intersection::AddRoadIn
+==============================================================================
+Description: Comment
+
+Parameters: ( Road *pRoad )
+
+Return: void
+
+=============================================================================
+*/
+void Intersection::AddRoadIn( Road *pRoad )
+{
+ mRoadListIn[ mnRoadsIn ] = pRoad;
+ mnRoadsIn++;
+}
+// add a road to the out list.
+/*
+==============================================================================
+Intersection::AddRoadOut
+==============================================================================
+Description: Comment
+
+Parameters: ( Road *pRoad )
+
+Return: void
+
+=============================================================================
+*/
+void Intersection::AddRoadOut( Road *pRoad )
+{
+ mRoadListOut[ mnRoadsOut ] = pRoad;
+ mnRoadsOut++;
+}
+// Find the road pointer in the road list.
+/*
+==============================================================================
+Intersection::FindRoadIn
+==============================================================================
+Description: Comment
+
+Parameters: ( const Road* pRoad )
+
+Return: int
+
+=============================================================================
+*/
+int Intersection::FindRoadIn( const Road* pRoad ) const
+{
+ unsigned int i;
+ for ( i = 0; i < mnRoadsIn; i++ )
+ {
+ if ( pRoad == mRoadListIn[ i ] )
+ {
+ // found it.
+ return i;
+ }
+ }
+ // not found
+ return -1;
+}
+
+// Find the road pointer in the road list.
+/*
+==============================================================================
+Intersection::FindRoadOut
+==============================================================================
+Description: Comment
+
+Parameters: ( const Road* pRoad )
+
+Return: int
+
+=============================================================================
+*/
+int Intersection::FindRoadOut( const Road* pRoad ) const
+{
+ unsigned int i;
+ for ( i = 0; i < mnRoadsOut; i++ )
+ {
+ if ( pRoad == mRoadListOut[ i ] )
+ {
+ // found it.
+ return i;
+ }
+ }
+ // not found
+ return -1;
+}
+
+/*
+==============================================================================
+Intersection::GetLocation
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: const
+
+=============================================================================
+*/
+
+void Intersection::GetLocation( rmt::Vector& location ) const
+{
+ location = mLocation;
+}
+
+
+
+
+void Intersection::PopulateShortestRoads( SwapArray<RoadManager::ShortestRoad>& roadsToAdjacentInts, bool useMultiplier )
+{
+ ////////////
+ // First search through the IN roads, tallying up the shortest, unique IN roads
+ // that get us to distinct adjacent intersections
+
+ for( int i = 0; i< static_cast<int>(mnRoadsIn); i++ )
+ {
+ Road* road = mRoadListIn[i];
+ const Intersection* in = road->GetSourceIntersection();
+
+ // if the road loops back onto same intersection, forget it
+ if( in == this )
+ {
+ continue;
+ }
+
+ // if this is a shortcut road, forget it
+ if( road->GetShortCut() )
+ {
+ continue;
+ }
+
+ RoadManager::ShortestRoad sr;
+ sr.isOutRoad = false;
+ sr.road = road;
+
+ sr.cost = road->GetRoadLength();
+ sr.cost *= useMultiplier ? RoadManager::AGAINST_TRAFFIC_COST_MULTIPLIER : 1.0f;
+
+ // see if we already have this intersection listed
+ // If not, add it.
+ // If found, check if this road is shorter than road listed
+ RoadManager::ShortestRoad* testRoad = NULL;
+ const Intersection* testInt = NULL;
+
+ bool found = false;
+ for( int j=0; j<roadsToAdjacentInts.mUseSize; j++ )
+ {
+ testRoad = &(roadsToAdjacentInts[j]);
+ float testCost = testRoad->road->GetRoadLength();
+
+ if( testRoad->isOutRoad )
+ {
+ testInt = testRoad->road->GetDestinationIntersection();
+ }
+ else
+ {
+ testInt = testRoad->road->GetSourceIntersection();
+ testCost *= useMultiplier ? RoadManager::AGAINST_TRAFFIC_COST_MULTIPLIER : 1.0f;
+ }
+ if( testInt == in )
+ {
+ // ok, found "in" in our list...
+ // if the length of road is shorter, store this road
+ // instead of testRoad
+ found = true;
+
+ if( sr.cost < testCost )
+ {
+ roadsToAdjacentInts.Remove( j );
+ roadsToAdjacentInts.Add( sr );
+ }
+ // we need to break here since we modified the usesize.
+ break;
+ }
+ }
+ // if we never found it in any of our lists, add it to the list of IN roads
+ if( !found )
+ {
+ roadsToAdjacentInts.Add( sr );
+ }
+ }
+
+
+ /////////////////////
+ // Now do the same for the OUT roads
+
+ for( int i = 0; i<static_cast<int>(mnRoadsOut); i++ )
+ {
+ Road* road = mRoadListOut[i];
+ const Intersection* in = road->GetDestinationIntersection();
+
+ // if the road loops back onto same intersection, forget it
+ if( in == this )
+ {
+ continue;
+ }
+
+ // if this is a shortcut road, add it to the shortcut list,
+ // but skip adding it to adjacency list
+ if( road->GetShortCut() )
+ {
+ continue;
+ }
+
+
+ RoadManager::ShortestRoad sr;
+ sr.isOutRoad = true;
+ sr.road = road;
+ sr.cost = road->GetRoadLength();
+
+ // see if we already have this intersection listed
+ // If not, add it.
+ // If found, check if this road is shorter than road listed
+ RoadManager::ShortestRoad* testRoad = NULL;
+ const Intersection* testInt = NULL;
+
+ bool found = false;
+ for( int j=0; j<roadsToAdjacentInts.mUseSize; j++ )
+ {
+ testRoad = &(roadsToAdjacentInts[j]);
+ float testCost = testRoad->road->GetRoadLength();
+
+ if( testRoad->isOutRoad )
+ {
+ testInt = testRoad->road->GetDestinationIntersection();
+ }
+ else
+ {
+ testInt = testRoad->road->GetSourceIntersection();
+ testCost *= useMultiplier ? RoadManager::AGAINST_TRAFFIC_COST_MULTIPLIER : 1.0f;
+ }
+ if( testInt == in )
+ {
+ // ok, "in" already exists in our list...
+ // if the length of road is shorter, store this road
+ // instead of testRoad
+ found = true;
+ if( sr.cost < testCost )
+ {
+ roadsToAdjacentInts.Remove( j );
+ roadsToAdjacentInts.Add( sr );
+ }
+ // we need to break here since we modified the usesize.
+ break;
+ }
+ }
+ // if we never found it in any of our lists, add it to the list of IN roads
+ if( !found )
+ {
+ roadsToAdjacentInts.Add( sr );
+ }
+ }
+}
+
+
+
+
+
+/*
+==============================================================================
+Intersection::SortRoads
+==============================================================================
+Description: Sorts the roads into clockwise order.
+ Pick a road to start, add it to the head of the sorted list.
+ Find the first road to the right of that, add it to the sorted list.
+ Repeat until all roads have been added to the sorted list.
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Intersection::SortRoads( void )
+{
+ struct roadOrientation
+ {
+ Road* pRoad;
+ float fAngle;
+ };
+
+ // a copy of the list of the roads leading in to this intersection.
+ //
+ roadOrientation roadListIn[ MAX_ROADS ];
+
+ // a copy of the list of the roads leading out of this intersection.
+ //
+ roadOrientation roadListOut[ MAX_ROADS ];
+
+ for ( int i = 0; i < MAX_ROADS; i++ )
+ {
+ roadListIn[ i ].pRoad = mRoadListIn[ i ];
+ if ( mRoadListIn[ i ] )
+ {
+ rmt::Vector to, from;
+ // Get the facing vector by finding the two points that connect the road.
+ //
+ roadListIn[ i ].pRoad->GetSourceIntersection()->GetLocation( from );
+ this->GetLocation( to );
+ // Get the vector between and flip it.
+ // Choreo considers 0 0 -1 = 0 degrees.
+ //
+ to.Sub( from );
+ to.Scale( -1.0f );
+
+ roadListIn[ i ].fAngle = choreo::GetWorldAngle( to.x, to.z );
+ }
+
+ roadListOut[ i ].pRoad = mRoadListOut[ i ];
+ if ( mRoadListOut[ i ] )
+ {
+ rmt::Vector to, from;
+ // Get the facing vector by finding the two points that connect the road.
+ //
+ roadListOut[ i ].pRoad->GetDestinationIntersection()->GetLocation( from );
+ this->GetLocation( to );
+ // Get the vector between and flip it.
+ // Choreo considers 0 0 -1 = 0 degrees.
+ //
+ to.Sub( from );
+ to.Scale( -1.0f );
+
+ roadListOut[ i ].fAngle = choreo::GetWorldAngle( to.x, to.z );
+ }
+ }
+
+ for ( int i = 0; i < MAX_ROADS; i++ )
+ {
+ roadOrientation* pRoadOrientation = 0;
+ // A larger angle than would ever be stored.
+ //
+ float fMinAngle = rmt::PI * 4;
+
+ for ( int j = 0; j < MAX_ROADS; j++ )
+ {
+ // Take the smallest remaining angle each time through.
+ //
+ if ( roadListIn[ j ].pRoad && roadListIn[ j ].fAngle < fMinAngle )
+ {
+ // This is the best candidate so far.
+ //
+ fMinAngle = roadListIn[ j ].fAngle;
+ // So store it for later.
+ //
+ pRoadOrientation = &roadListIn[ j ];
+ }
+ }
+ if ( pRoadOrientation )
+ {
+ // Store this road in sorted order.
+ //
+ mRoadListIn[ i ] = pRoadOrientation->pRoad;
+ // Remove the road from consideration.
+ //
+ pRoadOrientation->pRoad = 0;
+ }
+ else
+ {
+ mRoadListIn[ i ] = 0;
+ }
+ }
+
+ for ( int i = 0; i < MAX_ROADS; i++ )
+ {
+ roadOrientation* pRoadOrientation = 0;
+ // A larger angle than would ever be stored.
+ //
+ float fMinAngle = rmt::PI * 4;
+
+ for ( int j = 0; j < MAX_ROADS; j++ )
+ {
+ // Take the smallest remaining angle each time through.
+ //
+ if ( roadListOut[ j ].pRoad && roadListOut[ j ].fAngle < fMinAngle )
+ {
+ // This is the best candidate so far.
+ //
+ fMinAngle = roadListOut[ j ].fAngle;
+ // So store it for later.
+ //
+ pRoadOrientation = &roadListOut[ j ];
+ }
+ }
+ if ( pRoadOrientation )
+ {
+ // Store this road in sorted order.
+ //
+ mRoadListOut[ i ] = pRoadOrientation->pRoad;
+ // Remove the road from consideration.
+ //
+ pRoadOrientation->pRoad = 0;
+ }
+ else
+ {
+ mRoadListOut[ i ] = 0;
+ }
+ }
+
+ //////////////////////////////////////////////////////////////////////////
+ // store shortcut roads going OUT of this intersection..
+ // Note, shortcuts can only be taken one way (e.g. a jump), so we
+ // only consider the OUTGOING shortcut roads...
+
+ SwapArray<Road*> outShortcutRoads;
+
+ HeapMgr()->PushHeap(GMA_TEMP);
+ outShortcutRoads.Allocate( MAX_ROADS );
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+ for( int i = 0; i<static_cast<int>(mnRoadsOut); i++ )
+ {
+ Road* road = mRoadListOut[i];
+ const Intersection* in = road->GetDestinationIntersection();
+
+ // if the road loops back onto same intersection, forget it
+ if( in == this )
+ {
+ continue;
+ }
+
+ // if this is a shortcut road, add it to the shortcut list,
+ // but skip adding it to adjacency list
+ if( road->GetShortCut() )
+ {
+ outShortcutRoads.Add( road );
+ continue;
+ }
+ }
+
+ // also, store away the list of shortcut roads
+ if( outShortcutRoads.mUseSize > 0 )
+ {
+ mOutgoingShortcuts.Allocate( outShortcutRoads.mUseSize );
+ for( int i=0; i<outShortcutRoads.mUseSize; i++ )
+ {
+ mOutgoingShortcuts.Add( outShortcutRoads[i] );
+ }
+ }
+ outShortcutRoads.Clear();
+
+ ////////////////////////////
+ // Determine shortest roads
+ // Store two versions: the one that uses AGAINST_TRAFFIC_COST_MULTIPLIER
+ // and the one that doesn't
+
+ SwapArray<RoadManager::ShortestRoad> roadsToAdjacentInts;
+
+ HeapMgr()->PushHeap(GMA_TEMP);
+ roadsToAdjacentInts.Allocate( MAX_ROADS*2 );
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+ PopulateShortestRoads( roadsToAdjacentInts, true );
+ if( roadsToAdjacentInts.mUseSize > 0 )
+ {
+ mShortestRoadsToAdjacentIntersectionsWithMultiplier.Allocate( roadsToAdjacentInts.mUseSize );
+ for( int i=0; i<roadsToAdjacentInts.mUseSize; i++ )
+ {
+ mShortestRoadsToAdjacentIntersectionsWithMultiplier.Add( roadsToAdjacentInts[i] );
+ }
+ }
+ roadsToAdjacentInts.ClearUse();
+
+ PopulateShortestRoads( roadsToAdjacentInts, false );
+ if( roadsToAdjacentInts.mUseSize > 0 )
+ {
+ mShortestRoadsToAdjacentIntersectionsNoMultiplier.Allocate( roadsToAdjacentInts.mUseSize );
+ for( int i=0; i<roadsToAdjacentInts.mUseSize; i++ )
+ {
+ mShortestRoadsToAdjacentIntersectionsNoMultiplier.Add( roadsToAdjacentInts[i] );
+ }
+ }
+ roadsToAdjacentInts.Clear();
+
+ // the choices of roads maybe different with or without multiplier, but
+ // the sizes of the two arrays should be the same because the number
+ // of reachable, neighboring intersections is the same
+ rAssert( mShortestRoadsToAdjacentIntersectionsWithMultiplier.mUseSize ==
+ mShortestRoadsToAdjacentIntersectionsNoMultiplier.mUseSize );
+
+ // see if we are a big intersection, doesn't matter which size we use here.
+ int numAdjacentInts = mShortestRoadsToAdjacentIntersectionsWithMultiplier.mUseSize;
+ if( numAdjacentInts > 2 )
+ {
+ mBigIntersection = new RoadManager::BigIntersection;
+ mBigIntersection->in = this;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // allocate space for waiting roads... only need to do it for IN roads
+ // because they're the ones approaching the intersection
+ if( mType != Intersection::NO_STOP )
+ {
+ if( mnRoadsIn > 0 )
+ {
+ mWaitingRoads.Allocate( mnRoadsIn );
+ }
+ }
+
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Do some checking here, shall we?
+ // Every intersection must have at least 1 neighbor (must be attached to rest of world)
+
+#if( RAD_TUNE || RAD_DEBUG )
+ if( numAdjacentInts <= 0 )
+ {
+ char msg[256];
+ sprintf( msg,
+ "Intersection at (%0.2f,%0.2f,%0.2f) is not joined to any other\n"
+ " intersection by any road!\n",
+ mLocation.x, mLocation.y, -mLocation.z );
+ rTuneAssertMsg( false, msg );
+ }
+#endif
+
+#ifdef RAD_DEBUG
+ for( int i = 0; i<mShortestRoadsToAdjacentIntersectionsWithMultiplier.mUseSize; i++ )
+ {
+ RoadManager::ShortestRoad* shortestRoad =
+ &(mShortestRoadsToAdjacentIntersectionsWithMultiplier[i]);
+
+ Intersection* testInt = NULL;
+ if( shortestRoad->isOutRoad )
+ {
+ testInt = (Intersection*)
+ shortestRoad->road->GetSourceIntersection();
+ }
+ else
+ {
+ testInt = (Intersection*)
+ shortestRoad->road->GetDestinationIntersection();
+ }
+ rAssert( testInt == this );
+ }
+ for( int i = 0; i<mShortestRoadsToAdjacentIntersectionsNoMultiplier.mUseSize; i++ )
+ {
+ RoadManager::ShortestRoad* shortestRoad =
+ &(mShortestRoadsToAdjacentIntersectionsNoMultiplier[i]);
+
+ Intersection* testInt = NULL;
+ if( shortestRoad->isOutRoad )
+ {
+ testInt = (Intersection*)
+ shortestRoad->road->GetSourceIntersection();
+ }
+ else
+ {
+ testInt = (Intersection*)
+ shortestRoad->road->GetDestinationIntersection();
+ }
+ rAssert( testInt == this );
+ }
+#endif
+
+#ifdef TOOLS
+// char baseMsg [1000];
+// char fullMsg [1100];
+
+ /*
+ // Every intersection should have at minimum 1 OUT road
+ if( mnRoadsOut <= 0 )
+ {
+ sprintf( fullMsg,
+ "You are doomed. Intersection at (%f,%f,%f)\n"
+ " does not have an out road.\n",
+ mLocation.x, mLocation.y, -1*mLocation.z );
+ rTuneAssertMsg( false, fullMsg );
+ }
+ */
+
+ /*
+ // Road segments coming in and going out of this intersection MUST have same values
+ // of y where they hit the intersection. If this is not so, it is due to BAD ROAD DATA.
+ // For the sake of traffic, the intersection must be FLAT (so Traffic only computes
+ // a spline in 2D rather than 3D).
+ //
+ // If the y values of any two segments do not match up, cars will "hop" when
+ // they transit through the intersection between these segments.
+ //
+ // Please notify Sheik to check the road data around nearby intersections.
+ //
+ sprintf( baseMsg,
+ "\nMismatching y-values at intersection (%f,%f,%f).\n"
+ " Check if y values are same for all IN & OUT road segments attached\n"
+ " to this intersection. Check hypergraph to see if the roadnodes leading\n"
+ " IN and OUT of this intersection contain all the proper roadsegments.\n"
+ " If you skip this, Traffic cars will \"hop\" when they transit through\n"
+ " the intersection between the road segments with mismatching y values.\n"
+ " Better to report error to me (Dusit) or Sheik.\n\n",
+ mLocation.x,
+ mLocation.y,
+ -1*mLocation.z );
+
+ float ep = 0.001f;
+ for( unsigned int m=0; m<mnRoadsIn; m++ )
+ {
+ // grab the last segment on the in road
+ RoadSegment* inSeg = mRoadListIn[m]->GetRoadSegment
+ ( mRoadListIn[m]->GetNumRoadSegments()-1 );
+ rTuneAssert( inSeg != NULL );
+
+ rmt::Vector p1, p2;
+ inSeg->GetCorner( 1, p1 );
+ inSeg->GetCorner( 2, p2 );
+
+ sprintf( fullMsg, "%s Offending road: IN \"%s\"\n",
+ baseMsg, mRoadListIn[m]->GetName() );
+ rTuneAssertMsg( rmt::Epsilon( p1.y, p2.y, ep ), fullMsg );
+
+ for( unsigned int n=0; n<mnRoadsOut; n++ )
+ {
+ // grab the first segment on the out road
+ RoadSegment* outSeg = mRoadListOut[n]->GetRoadSegment( 0 );
+ rTuneAssert( outSeg != NULL );
+
+ rmt::Vector q0, q3;
+ outSeg->GetCorner( 0, q0 );
+ outSeg->GetCorner( 3, q3 );
+
+ sprintf( fullMsg, "%s Offending roads: IN \"%s\", OUT \"%s\"\n",
+ baseMsg, mRoadListIn[m]->GetName(), mRoadListOut[n]->GetName() );
+ rTuneAssertMsg( rmt::Epsilon( p1.y, q0.y, ep ) &&
+ rmt::Epsilon( p1.y, q3.y, ep ), fullMsg );
+ }
+ }
+ */
+#endif
+}
+
+//==============================================================================
+// Intersection::GetType
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Intersection::Type
+//
+//==============================================================================
+Intersection::Type Intersection::GetType() const
+{
+ return mType;
+}
+
+
+//==============================================================================
+// Intersection::SetType
+//==============================================================================
+// Description: Comment
+//
+// Parameters: ( Type type )
+//
+// Return: void
+//
+//==============================================================================
+void Intersection::SetType( Type type )
+{
+ mType = type;
+ switch( mType )
+ {
+ case NO_STOP:
+ {
+ mpTrafficControl = NULL;
+ }
+ break;
+ case N_WAY:
+ {
+ mpTrafficControl = &mNWayControl;
+ }
+ break;
+ default:
+ {
+ mpTrafficControl = NULL;
+ }
+ break;
+ }
+}
+
+//=============================================================================
+// Intersection::IsPointInIntersection
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector& point )
+//
+// Return: bool
+//
+//=============================================================================
+bool Intersection::IsPointInIntersection( rmt::Vector& point ) const
+{
+ rmt::Vector vectorBetween;
+
+ vectorBetween.x = point.x - mLocation.x;
+ vectorBetween.y = 0.0f;
+ vectorBetween.z = point.z - mLocation.z;
+ vectorBetween.x *= vectorBetween.x;
+ vectorBetween.z *= vectorBetween.z;
+ vectorBetween.x += vectorBetween.z;
+
+ return ( vectorBetween.x <= rmt::Sqr( mfRadius ) );
+}
+
+
+void Intersection::FindGoodTrafficLane( const Road& road, unsigned int preferredLaneIndex, Lane*& outLane, unsigned int& outLaneIndex )
+{
+ Lane* lane = road.GetLane(preferredLaneIndex);
+ int numTrafficVehicles = 0;
+ if( lane != NULL )
+ {
+ numTrafficVehicles = lane->mTrafficVehicles.mUseSize;
+ rAssert( numTrafficVehicles >= 0 );
+
+ if( numTrafficVehicles < lane->GetDensity() )
+ {
+ outLane = lane;
+ outLaneIndex = preferredLaneIndex;
+ return;
+ }
+ }
+
+ // if can't use the preferred lane, gotta find the first lane that can support my vehicle...
+ for( unsigned int i=0; i<road.GetNumLanes(); i++ )
+ {
+ lane = road.GetLane(i);
+ rAssert( lane != NULL );
+
+ numTrafficVehicles = lane->mTrafficVehicles.mUseSize;
+ rAssert( numTrafficVehicles >= 0 );
+
+ if( numTrafficVehicles < lane->GetDensity() )
+ {
+ outLane = lane;
+ outLaneIndex = i;
+ return;
+ }
+ }
+
+ // finally, can't find any lane, return NULL...
+ outLane = NULL;
+ outLaneIndex = 0;
+ return;
+}
+
+
+void Intersection::GetLeftTurnForTraffic( const Road& inRoad,
+ unsigned int preferredLaneIndex,
+ Road*& outRoad,
+ Lane*& outLane,
+ unsigned int& outLaneIndex )
+{
+ outRoad = NULL;
+ outLane = NULL;
+ outLaneIndex = 0;
+
+ int index = FindRoadIn( &inRoad );
+ rAssert( index != -1 );
+ if( index == -1 )
+ {
+ return;
+ }
+
+ if( mnRoadsOut <= 0 )
+ {
+ return;
+ }
+
+ if( mnRoadsOut == 1 )
+ {
+ //No roads out. Or only a straight out.
+ rDebugString( "Intersection has only one OUT road!\n" );
+
+ outRoad = mRoadListOut[0];
+
+ // Pick an outLane & outLaneIndex
+ FindGoodTrafficLane( *outRoad, preferredLaneIndex, outLane, outLaneIndex );
+
+ if( outLane == NULL )
+ {
+ return;
+ }
+ }
+ else
+ {
+ // To find which turn will give a closest approximation to a "LEFT"
+ // turn, we turn our inDir by 90 degrees (to the left!) and
+ // iterate through the outDirs for one that's STRAIGHTEST along it.
+ //
+ rmt::Vector origInDir;
+ rmt::Vector inDir, outDir;
+
+ // Grab normal of trailing edge of the last segment of IN road (for comparison)
+ RoadSegment* segment;
+ unsigned int nSegments = inRoad.GetNumRoadSegments();
+ assert( nSegments > 0 );
+ segment = inRoad.GetRoadSegment( nSegments - 1 );
+
+ // TODO:
+ // Don't use Edge Normals, use Direction of Segment's LANE (but we'll have to
+ // pass in lane info)
+ segment->GetEdgeNormal( 2, origInDir );
+
+ // *** NOTE ***
+ // We rely on the assumption that the intersection is HORIZONTALLY FLAT
+ // Else, we'll have to project the In & Out vectors onto the plane of
+ // the intersection.
+ origInDir.y = 0.0f;
+ origInDir.Normalize(); // *** SQUARE ROOT! ***
+
+ // copy origInDir... we'll be modifying inDir to suit our needs
+ inDir = origInDir;
+ inDir.Scale(-1.0f);
+
+ // turn inDir CCW 90 degrees about y axis (the ideal left turn
+ // will be 180 angle from this vector)
+ rmt::Vector temp;
+ temp.Set( -1 * inDir.z, inDir.y, inDir.x );
+ inDir = temp;
+
+ float cosAlpha = 0.0f;
+
+ // Let bestCosAlpha be, initially, the WORST value possible
+ // cosAlpha of 1.0 = alphaBetwIn&OutRoads of 0 = 180 degree turn! BAD!
+ // As we iterate through the OUT roads, we want to find one with most
+ // alpha, or smallest cosAlpha
+ //
+ float bestCosAlpha = 1.0f;
+ bool stillLookingForFirstRoad = true;
+
+ outRoad = NULL;
+ outLane = NULL;
+ outLaneIndex = 0;
+
+ unsigned int i = 0;
+ for( i ; i<mnRoadsOut ; ++i )
+ {
+ // Grab normal of leading edge of the first segment of OUT road
+ segment = mRoadListOut[i]->GetRoadSegment(0);
+ segment->GetEdgeNormal(0, outDir);
+
+ // *** NOTE ***
+ // We rely on the assumption that the intersection is HORIZONTALLY FLAT
+ // Else, we'll have to project the In & Out vectors onto the plane of
+ // the intersection.
+ outDir.y = 0.0f;
+ outDir.Normalize(); // *** SQUARE ROOT! ***
+
+ // Skip this OUT road if it just goes back the way we came
+ // This prevents U-turn at an intersection when there are
+ // other roads to take, even if they don't turn the way
+ // we want. The decision is really up to traffic control or
+ // some other entity; for now, we hardcode it in the Intersection
+ if( outDir.Equals( origInDir * -1, 0.05f ) )
+ {
+ continue;
+ }
+
+ cosAlpha = inDir.Dot(outDir);
+
+ // *** NOTE ***
+ // If contention arises between a left and a right turn that
+ // both have bestCosAlpha, this inequality will favor the last
+ // best match.
+ if( stillLookingForFirstRoad || (cosAlpha <= bestCosAlpha) )
+ {
+ Lane* laneCandidate = NULL;
+ unsigned int laneIndexCandidate = 0;
+ FindGoodTrafficLane( *mRoadListOut[i], preferredLaneIndex, laneCandidate, laneIndexCandidate );
+
+ if( laneCandidate != NULL )
+ {
+ stillLookingForFirstRoad = false;
+ bestCosAlpha = cosAlpha;
+ outRoad = mRoadListOut[i];
+ outLane = laneCandidate;
+ outLaneIndex = laneIndexCandidate;
+ }
+ }
+ }// end of FOR LOOP
+
+ }// end of ELSE
+
+}
+
+
+
+void Intersection::GetStraightForTraffic( const Road& inRoad,
+ unsigned int preferredLaneIndex,
+ Road*& outRoad,
+ Lane*& outLane,
+ unsigned int& outLaneIndex )
+{
+ outRoad = NULL;
+ outLane = NULL;
+ outLaneIndex = 0;
+
+ int index = FindRoadIn( &inRoad );
+ rAssert( index != -1 );
+ if( index == -1 )
+ {
+ return;
+ }
+
+ if( mnRoadsOut <= 0 )
+ {
+ return;
+ }
+
+ if ( mnRoadsOut == 1 )
+ {
+ rDebugString( "Intersection has only one OUT road!\n" );
+
+ outRoad = mRoadListOut[0];
+
+ // Pick an outLane & outLaneIndex
+ FindGoodTrafficLane( *outRoad, preferredLaneIndex, outLane, outLaneIndex );
+
+ if( outLane == NULL )
+ {
+ return;
+ }
+ }
+ else
+ {
+ // Iterate through all the OUT roads to find which one gives us the angle closest to
+ // going STRAIGHT (180)
+ //
+ rmt::Vector origInDir;
+ rmt::Vector inDir, outDir;
+
+ // Grab normal of trailing edge of the last segment of IN road (for comparison)
+ RoadSegment* segment;
+ unsigned int nSegments = inRoad.GetNumRoadSegments();
+ assert( nSegments > 0 );
+ segment = inRoad.GetRoadSegment( nSegments - 1 );
+ segment->GetEdgeNormal( 2, origInDir );
+
+ // *** NOTE ***
+ // We rely on the assumption that the intersection is HORIZONTALLY FLAT
+ // Else, we'll have to project the In & Out vectors onto the plane of
+ // the intersection.
+ origInDir.y = 0.0f;
+ origInDir.Normalize(); // *** SQUARE ROOT! ***
+
+ inDir = origInDir;
+ inDir.Scale(-1.0f);
+
+ float cosAlpha = 0.0f;
+
+ // Let bestCosAlpha be, initially, the WORST value possible
+ // cosAlpha of 1.0 = alphaBetwIn&OutRoads of 0 = 180 degree turn! BAD!
+ // As we iterate through the OUT roads, we want to find one with most
+ // alpha, or smallest cosAlpha
+ //
+ float bestCosAlpha = 1.0f;
+ bool stillLookingForFirstRoad = true;
+
+ unsigned int i = 0;
+ for( i ; i<mnRoadsOut ; ++i )
+ {
+ // Grab normal of leading edge of the first segment of OUT road
+ segment = mRoadListOut[i]->GetRoadSegment(0);
+ segment->GetEdgeNormal(0, outDir);
+
+ // *** NOTE ***
+ // We rely on the assumption that the intersection is HORIZONTALLY FLAT
+ // Else, we'll have to project the In & Out vectors onto the plane of
+ // the intersection.
+ outDir.y = 0.0f;
+ outDir.Normalize(); // *** SQUARE ROOT! ***
+
+ // Skip this OUT road if it just goes back the way we came
+ // This prevents U-turn at an intersection when there are
+ // other roads to take, even if they don't turn the way
+ // we want. The decision is really up to traffic control or
+ // some other entity; for now, we hardcode it in the Intersection
+ if( outDir.Equals( origInDir * -1, 0.05f ) )
+ {
+ continue;
+ }
+
+ cosAlpha = inDir.Dot(outDir);
+
+ // *** NOTE ***
+ // This inequality will favor the last best match. (If another road
+ // has cosAlpha = current bestCosAlpha, we ignore it & go with the
+ // most recent one).
+ if( stillLookingForFirstRoad || (cosAlpha <= bestCosAlpha) )
+ {
+ Lane* laneCandidate = NULL;
+ unsigned int laneIndexCandidate = 0;
+ FindGoodTrafficLane( *mRoadListOut[i], preferredLaneIndex, laneCandidate, laneIndexCandidate );
+
+ if( laneCandidate != NULL )
+ {
+ stillLookingForFirstRoad = false;
+ bestCosAlpha = cosAlpha;
+ outRoad = mRoadListOut[i];
+ outLane = laneCandidate;
+ outLaneIndex = laneIndexCandidate;
+ }
+ }
+ }
+ }
+
+}
+
+// Given the current road, this should return a ref to the right turn road.
+void Intersection::GetRightTurnForTraffic( const Road& inRoad,
+ unsigned int preferredLaneIndex,
+ Road*& outRoad,
+ Lane*& outLane,
+ unsigned int& outLaneIndex )
+{
+ outRoad = NULL;
+ outLane = NULL;
+ outLaneIndex = 0;
+
+ int index = FindRoadIn( &inRoad );
+ rAssert( index != -1 );
+ if( index == -1 )
+ {
+ return;
+ }
+ if( mnRoadsOut <= 0 )
+ {
+ return;
+ }
+
+ if( mnRoadsOut == 1 )
+ {
+ //No roads out. Or only a straight out.
+ rDebugString( "Intersection has only one OUT road!\n" );
+
+ outRoad = mRoadListOut[0];
+
+ // Pick an outLane & outLaneIndex
+ FindGoodTrafficLane( *outRoad, preferredLaneIndex, outLane, outLaneIndex );
+
+ if( outLane == NULL )
+ {
+ return;
+ }
+ }
+ else
+ {
+ // To find which turn will give a closest approximation to a "LEFT"
+ // turn, we turn our inDir by 90 degrees (to the left!) and
+ // iterate through the outDirs for one that's STRAIGHTEST along it.
+ //
+ rmt::Vector origInDir;
+ rmt::Vector inDir, outDir;
+
+ // Grab normal of trailing edge of the last segment of IN road (for comparison)
+ RoadSegment* segment;
+ unsigned int nSegments = inRoad.GetNumRoadSegments();
+ assert( nSegments > 0 );
+ segment = inRoad.GetRoadSegment( nSegments - 1 );
+ segment->GetEdgeNormal( 2, origInDir );
+
+ // *** NOTE ***
+ // We rely on the assumption that the intersection is HORIZONTALLY FLAT
+ // Else, we'll have to project the In & Out vectors onto the plane of
+ // the intersection.
+ origInDir.y = 0.0f;
+ origInDir.Normalize(); // *** SQUARE ROOT! ***
+
+ // copy origInDir... we'll be modifying inDir to suit our needs
+ inDir = origInDir;
+ inDir.Scale(-1.0f);
+
+ // turn inDir CW 90 degrees about y axis (the ideal right turn
+ // is at 180 angle from this vector)
+ rmt::Vector temp;
+ temp.Set( inDir.z, inDir.y, -1 * inDir.x );
+ inDir = temp;
+
+ float cosAlpha = 0.0f;
+
+ // Let bestCosAlpha be, initially, the WORST value possible
+ // cosAlpha of 1.0 = alphaBetwIn&OutRoads of 0 = 180 degree turn! BAD!
+ // As we iterate through the OUT roads, we want to find one with most
+ // alpha, or smallest cosAlpha
+ //
+ float bestCosAlpha = 1.0f;
+ bool stillLookingForFirstRoad = true;
+
+ unsigned int i = 0;
+ for( i ; i<mnRoadsOut ; ++i )
+ {
+ // Grab normal of leading edge of the first segment of OUT road
+ segment = mRoadListOut[i]->GetRoadSegment(0);
+ segment->GetEdgeNormal(0, outDir);
+
+ // *** NOTE ***
+ // We rely on the assumption that the intersection is HORIZONTALLY FLAT
+ // Else, we'll have to project the In & Out vectors onto the plane of
+ // the intersection.
+ outDir.y = 0.0f;
+ outDir.Normalize(); // *** SQUARE ROOT! ***
+
+ // Skip this OUT road if it just goes back the way we came
+ // This prevents U-turn at an intersection when there are
+ // other roads to take, even if they don't turn the way
+ // we want. The decision is really up to traffic control or
+ // some other entity; for now, we hardcode it in the Intersection
+ if( outDir.Equals( origInDir * -1, 0.05f ) )
+ {
+ continue;
+ }
+
+ cosAlpha = inDir.Dot(outDir);
+
+ // *** NOTE ***
+ // If contention arises between a left and a right turn that
+ // both have bestCosAlpha, this inequality will favor the last
+ // best match.
+ if( stillLookingForFirstRoad || (cosAlpha <= bestCosAlpha) )
+ {
+ Lane* laneCandidate = NULL;
+ unsigned int laneIndexCandidate = 0;
+ FindGoodTrafficLane( *mRoadListOut[i], preferredLaneIndex, laneCandidate, laneIndexCandidate );
+
+ if( laneCandidate != NULL )
+ {
+ stillLookingForFirstRoad = false;
+ bestCosAlpha = cosAlpha;
+ outRoad = mRoadListOut[i];
+ outLane = laneCandidate;
+ outLaneIndex = laneIndexCandidate;
+ }
+ }
+ }
+ }
+}
+
+void Intersection::GetOtherIntersection
+(
+ bool useMultiplier,
+ Intersection* knownIntersection,
+ Intersection*& otherIntersection,
+ RoadManager::ShortestRoad*& road
+)
+{
+ rAssert( knownIntersection != NULL );
+
+ // works only for non-big intersections
+ if( mBigIntersection != NULL )
+ {
+ rAssert( false );
+ otherIntersection = NULL;
+ road = NULL;
+ return;
+ }
+
+ SwapArray<RoadManager::ShortestRoad>* array = useMultiplier ?
+ &mShortestRoadsToAdjacentIntersectionsWithMultiplier :
+ &mShortestRoadsToAdjacentIntersectionsNoMultiplier ;
+
+ // I'm a non-big intersection if I'm connected to either 1 or 2 other
+ // intersections
+ rAssert( 1 <= array->mUseSize && array->mUseSize <= 2 );
+
+ for( int i=0; i<array->mUseSize; i++ )
+ {
+ RoadManager::ShortestRoad* shortRoad = &((*array)[i]);
+ rAssert( shortRoad );
+
+ Intersection* otherIn = NULL;
+ if( shortRoad->isOutRoad )
+ {
+ otherIn = (Intersection*) shortRoad->road->GetDestinationIntersection();
+ }
+ else
+ {
+ otherIn = (Intersection*) shortRoad->road->GetSourceIntersection();
+ }
+ if( otherIn != knownIntersection )
+ {
+ road = shortRoad;
+ otherIntersection = otherIn;
+ return;
+ }
+ }
+}
diff --git a/game/code/roads/intersection.h b/game/code/roads/intersection.h
new file mode 100644
index 0000000..8dfc8d5
--- /dev/null
+++ b/game/code/roads/intersection.h
@@ -0,0 +1,312 @@
+/*===========================================================================
+ Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+
+ Component: Intersection
+
+ Description:
+
+
+ Authors: Travis Brown-John
+
+ Revisions Date Author Revision
+ 2001/02/02 Tbrown-John Created
+
+===========================================================================*/
+
+#ifndef INTERSECTION_H_
+#define INTERSECTION_H_
+
+#include <radmath/radmath.hpp>
+#include <p3d/p3dtypes.hpp>
+#include <p3d/entity.hpp>
+#include <roads/TrafficControl.h>
+#include <roads/lane.h>
+#include <roads/geometry.h>
+#include <roads/roadmanager.h>
+#include <render/culling/swaparray.h>
+
+class Road;
+class CNavVertex;
+class TriggerLocator;
+class AnimEntityDSG;
+
+// somewhat arbitrary limit of MAX_ROADS way intersection.
+const int MAX_ROADS = 8;
+/*
+ AI is going to want to know these things about moving along a road:
+ 1. Am I at an intersection?
+ 2. What is my position and orientation?
+ 3. Do I want to change lanes?
+
+ AI is going to want to know these things when it is at an intersection:
+ 1. Can I move through this intersection.
+
+ AI is going to want to know these things about moving through intersection.
+ 1. Can I turn left from this lane?
+ 2. Can I turn right from this lane?
+ 3. Can I go straight in this lane?
+
+*/
+#ifdef TOOLS
+#define IS_ENTITY : public tEntity
+#else
+#define IS_ENTITY
+#endif
+
+class Intersection IS_ENTITY
+{
+public:
+ Intersection( );
+ Intersection( TriggerLocator* pLocator );
+ ~Intersection( void );
+
+ enum Type
+ {
+ NO_STOP,
+ N_WAY
+ };
+
+ Type GetType() const;
+ void SetType( Type type );
+ // Get the count of Roads into the intersection.
+ //unsigned int SizeRoadsIn( void ) const { return mnRoadsIn; }
+
+ // Get the count of Roads out of the intersection.
+ //unsigned int SizeRoadsOut( void ) const { return mnRoadsOut; }
+
+ // Get the count of Roads into and out of the intersection.
+ //unsigned int SizeRoads( void ) const { return mnRoadsIn + mnRoadsOut; }
+
+ // add a road to the in list.
+ void AddRoadIn( Road *pRoad );
+
+ // add a road to the out list.
+ void AddRoadOut( Road *pRoad );
+
+ void FindGoodTrafficLane( const Road& road,
+ unsigned int preferredLaneIndex,
+ Lane*& outLane,
+ unsigned int& outLaneIndex );
+
+ void GetLeftTurnForTraffic( const Road& inRoad,
+ unsigned int preferredLaneIndex,
+ Road*& outRoad,
+ Lane*& outLane,
+ unsigned int& outLaneIndex );
+
+ void GetRightTurnForTraffic( const Road& inRoad,
+ unsigned int preferredLaneIndex,
+ Road*& outRoad,
+ Lane*& outLane,
+ unsigned int& outLaneIndex );
+
+ void GetStraightForTraffic( const Road& inRoad,
+ unsigned int preferredLaneIndex,
+ Road*& outRoad,
+ Lane*& outLane,
+ unsigned int& outLaneIndex );
+
+ // So easy, it's almost free!
+ //
+ const Road* GetRoadIn( unsigned int index ) const;
+ const Road* GetRoadOut( unsigned int index ) const;
+ unsigned int GetNumRoadsIn() const;
+ unsigned int GetNumRoadsOut() const;
+
+ // Is the intersection clear of all cars.
+ //
+ bool IsIntersectionClear( void ) const;
+
+ // Simulate the intersection when there are cars nearby.
+ //
+ void Update( unsigned int dt );
+
+ // advance the traffic in roads 'RoadMask' according to light state.
+ //
+ void AdvanceTraffic( unsigned int RoadMask, unsigned int state ) const;
+
+ // returns the Intersection location in world space.
+ //
+ void GetLocation( rmt::Vector& location ) const;
+ void SetLocation( rmt::Vector& location );
+
+ // Temp method to display.
+ //
+ void Render( void ) const;
+
+ void SetRadius( float radius );
+ float GetRadius( void ) const
+ { return mfRadius; }
+
+ // Sorts the roads into clockwise order.
+ //
+ void SortRoads( void );
+
+ void SetName( const char* name );
+ tUID GetNameUID() const;
+
+ bool IsPointInIntersection( rmt::Vector& point ) const;
+
+ TrafficControl* GetTrafficControl();
+ void AdvanceNextWaitingRoad();
+
+ // Works only for non-big intersections
+ //
+ // Given an intersection connected to myself, I'll search through
+ // my list of shortest roads to other intersections to return the
+ // other intersection adjacent to me.
+ //
+ // The "useMultiplier" flag tells us to use the list of shortestroads
+ // whose "shortest" descriptive is defined using the road cost augmented
+ // by AGAINST_TRAFFIC_COST_MULTIPLIER
+ //
+ void GetOtherIntersection(
+ bool useMultiplier,
+ Intersection* knownIntersection,
+ Intersection*& otherIntersection,
+ RoadManager::ShortestRoad*& road );
+
+ void LinkAnimEntity( AnimEntityDSG* ipAnimEntity, int iIndex )
+ {
+ int i = iIndex - mpAnimEntityList.mUseSize + 1;
+ if(i>0) mpAnimEntityList.AddUse(i);
+
+ mpAnimEntityList[iIndex] = ipAnimEntity;
+ }
+
+ AnimEntityDSG* AnimEntity(int iIndex)
+ {
+ return mpAnimEntityList[iIndex];
+ }
+
+public:
+ SwapArray<Road*> mWaitingRoads; // list of roads waiting for a turn to go
+ SwapArray<Road*> mOutgoingShortcuts;
+ SwapArray<RoadManager::ShortestRoad> mShortestRoadsToAdjacentIntersectionsWithMultiplier;
+ SwapArray<RoadManager::ShortestRoad> mShortestRoadsToAdjacentIntersectionsNoMultiplier;
+ RoadManager::BigIntersection* mBigIntersection;
+ int mIndex; // index to RoadManager::mIntersections[pool==0] list
+
+private:
+ SwapArray<AnimEntityDSG*> mpAnimEntityList;
+ // methods
+ //
+
+ // Find the road pointer in the road list.
+ //
+ int FindRoadIn( const Road* pRoad ) const;
+
+ // Find the road pointer in the road list.
+ //
+ int FindRoadOut( const Road* pRoad ) const;
+
+ void PopulateShortestRoads( SwapArray<RoadManager::ShortestRoad>& roadsToAdjacentInts, bool useMultiplier );
+
+private: // data
+ // Roads are Clockwise ordered.
+ //
+ // 0
+ // |
+ // |
+ //3 ____|____ 1
+ // |
+ // |
+ // |
+ // 2
+ //
+ // a list of the roads leading in to this intersection.
+ //
+ Road* mRoadListIn[ MAX_ROADS ];
+
+ // a list of the roads leading out of this intersection.
+ //
+ Road* mRoadListOut[ MAX_ROADS ];
+
+ //
+ // A pointer to the traffic control object for this intersection.
+ // & the different controls this intersection might have
+ //
+ TrafficControl* mpTrafficControl;
+ TrafficLight mLightControl;
+ NWayStop mNWayControl;
+
+
+ // the actual number of roads in. <= MAX_ROADS.
+ //
+ unsigned int mnRoadsIn;
+
+ // the actual number of roads out. <= MAX_ROADS.
+ //
+ unsigned int mnRoadsOut;
+
+ // the location of the intersection in world space.
+ //
+ rmt::Vector mLocation;
+
+ // the rotation around the y vector of the intersection.
+ //
+ float mfRotation;
+
+ float mfRadius;
+
+ Type mType;
+
+ tUID mNameUID;
+};
+
+
+
+inline void Intersection::SetName( const char* name )
+{
+#ifdef TOOLS
+ tEntity::SetName( name );
+#endif
+ mNameUID = tEntity::MakeUID( name );
+}
+inline tUID Intersection::GetNameUID() const
+{
+ return mNameUID;
+}
+inline void Intersection::SetLocation( rmt::Vector& location )
+{
+ mLocation = location;
+}
+inline void Intersection::SetRadius( float radius )
+{
+ mfRadius = radius;
+}
+inline const Road* Intersection::GetRoadIn( unsigned int index ) const
+{
+ if(index >= mnRoadsIn)
+ {
+ return NULL;
+ }
+
+ return mRoadListIn[ index ];
+}
+inline const Road* Intersection::GetRoadOut( unsigned int index ) const
+{
+ if(index >= mnRoadsOut)
+ {
+ return NULL;
+ }
+
+ return mRoadListOut[ index ];
+}
+inline unsigned int Intersection::GetNumRoadsIn() const
+{
+ return mnRoadsIn;
+}
+inline unsigned int Intersection::GetNumRoadsOut() const
+{
+ return mnRoadsOut;
+}
+inline TrafficControl* Intersection::GetTrafficControl()
+{
+ return mpTrafficControl;
+}
+
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/roads/lane.cpp b/game/code/roads/lane.cpp
new file mode 100644
index 0000000..4615e5a
--- /dev/null
+++ b/game/code/roads/lane.cpp
@@ -0,0 +1,187 @@
+/*===========================================================================
+ Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+
+ Component: Lane
+
+ Description:
+
+
+ Authors: Travis Brown-John
+
+ Revisions Date Author Revision
+ 2001/02/02 Tbrown-John Created
+
+===========================================================================*/
+
+#include <roads/lane.h>
+#include <roads/road.h>
+#include <roads/trafficcontrol.h>
+
+
+#ifndef TOOLS
+#include <memory/srrmemory.h>
+#else
+#define MEMTRACK_PUSH_GROUP(x)
+#define MEMTRACK_POP_GROUP()
+#endif
+
+#ifndef TOOLS
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/trafficlocomotion.h>
+#endif
+
+#include <raddebug.hpp>
+
+
+//=============================================================================
+// Lane::Lane
+//=============================================================================
+// Description: Constructor
+//
+// Parameters: None
+//
+//=============================================================================
+Lane::Lane() :
+#ifndef TOOLS
+ mWaitingVehicle( NULL ),
+#endif
+#if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ mDesiredDensity( 5 ),
+ mPoints( NULL ),
+ mNumPoints( 0 )
+#else
+ mDesiredDensity( 5 )
+#endif
+{
+}
+
+Lane::~Lane()
+{
+#if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ if ( mPoints )
+ {
+ delete[] mPoints;
+ mPoints = NULL;
+ }
+#endif
+ mTrafficVehicles.Clear();
+}
+
+void Lane::Create( int density, Road* parentRoad )
+{
+ // DUSIT [Oct21,2002]
+ // Took out this assert cuz we need to be able to set density to 0 to
+ // prevent traffic cars from going onto that lane. Why TBJ put this
+ // assert in there in the first place
+ //rAssertMsg( fDensity > 0.0f, "Desired Density must be greater than 0.0f" );
+
+ mDesiredDensity = density;
+ mpParentRoad = parentRoad;
+ if( density > 0 )
+ {
+ mTrafficVehicles.Allocate( density );
+ }
+}
+
+#if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ void Lane::GetStart( rmt::Vector &point ) const
+ {
+ GetPoint( 0, &point );
+ }
+ void Lane::GetEnd( rmt::Vector &point ) const
+ {
+ GetPoint( GetNumPoints() - 1, &point );
+ }
+#endif
+
+
+
+// Notify waiting traffic that traffic control signal has changed.
+void Lane::NotifyWaitingTraffic( unsigned int state )
+{
+#ifndef TOOLS
+ if( TrafficControl::GREEN == state )
+ {
+ if( mWaitingVehicle != NULL )
+ {
+ Vehicle* v = mWaitingVehicle->GetVehicle();
+ rAssert( v != NULL );
+
+ // If the vehicle was removed from traffic, don't
+ // bother with notification. This can happen if
+ // player moves away from intersection.
+ // Also, if the vehicle is no longer in the intersection,
+ // we shouldn't just transit it back to DRIVING (it
+ // could be in any other state by now!)
+ if( mWaitingVehicle->GetIsActive() && v->mVehicleType == VT_TRAFFIC )
+ {
+ if( this == v->mTrafficLocomotion->GetAILane() &&
+ v->mTrafficLocomotion->GetAI()->GetState() ==
+ TrafficAI::WAITING_AT_INTERSECTION )
+ {
+ v->mTrafficLocomotion->SetAIState( TrafficAI::DRIVING );
+ }
+ }
+ mWaitingVehicle = NULL;
+ }
+ }
+ /*
+ if ( TrafficLight::ADVANCE_GREEN == state && CanTurnLeft() )
+ {
+ // Notify all waiting traffic.
+ //
+ }
+ else if ( TrafficLight::GREEN == state && !CanTurnLeft() )
+ {
+ // Notify all waiting traffic.
+ //
+ }
+ */
+#endif
+}
+
+#if defined(RAD_DEBUG) || defined(RAD_TUNE)
+
+ void Lane::AllocatePoints( unsigned int numPoints )
+ {
+ MEMTRACK_PUSH_GROUP( "Lane" );
+ rAssert( mPoints == NULL );
+
+ //TODO: GET RID OF THIS.
+ #ifndef TOOLS
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ #endif
+ #endif
+ mPoints = new rmt::Vector[ numPoints ];
+ rAssert( mPoints );
+
+ mNumPoints = numPoints;
+
+ #ifndef TOOLS
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ #endif
+ #endif
+
+ MEMTRACK_POP_GROUP( "Lane" );
+ }
+
+ unsigned int Lane::GetNumPoints() const
+ {
+ return mNumPoints;
+ }
+
+ void Lane::SetPoint( unsigned int index, const rmt::Vector& point )
+ {
+ rAssert( index < mNumPoints );
+
+ mPoints[index] = point;
+ }
+
+#endif
+
diff --git a/game/code/roads/lane.h b/game/code/roads/lane.h
new file mode 100644
index 0000000..eb4fdd6
--- /dev/null
+++ b/game/code/roads/lane.h
@@ -0,0 +1,187 @@
+/*===========================================================================
+ Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+
+ Component: Lane
+
+ Description:
+
+
+ Authors: Travis Brown-John
+
+ Revisions Date Author Revision
+ 2001/02/02 Tbrown-John Created
+
+===========================================================================*/
+
+#ifndef LANE_HPP_
+#define LANE_HPP_
+
+#include <radmath/radmath.hpp>
+#include <vector>
+#include <list>
+#include <roads/geometry.h>
+#include <render/culling/swaparray.h>
+#include <worldsim/traffic/trafficvehicle.h>
+
+class Road;
+class Lane;
+
+// the read only interface to the lane.
+struct ILaneInformation
+{
+ // get the max allowable speed.
+ virtual float GetSpeedLimit( void ) const = 0;
+ virtual int GetDensity( void ) const = 0;
+ // returns the parent road of this lane.
+ virtual const Road *GetRoad( void ) const = 0;
+
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ // For Rendering
+ // point is assigned the world position of the start of the lane
+ virtual void GetStart( rmt::Vector &point ) const = 0;
+ // point is assigned the world position of the end of the lane
+ virtual void GetEnd( rmt::Vector &point ) const = 0;
+ // given a parametric time, updates the 'point.' returns true if at the end of the lane.
+ virtual bool GetPoint( float t, rmt::Vector& point ) const = 0;
+#endif
+
+};
+
+// the write only interface to the lane.
+struct ILaneControl
+{
+ // set the max allowable speed.
+ virtual void SetSpeedLimit( float metrespersecond ) = 0;
+ // set the max allowable num traffic cars.
+ virtual void SetDensity( int numCars ) = 0;
+};
+
+class Lane
+:
+public ILaneInformation,
+public ILaneControl
+{
+public:
+
+ // ctor
+ Lane( void );
+ ~Lane();
+
+ // grab the spline associated with this lane.
+ void Create( int density, Road* parentRoad );
+
+
+ //////////////////////////////////////////////////
+ // Implementing ILaneInformation
+ //
+ float GetSpeedLimit( void ) const;
+ int GetDensity( void ) const;
+ const Road* GetRoad( void ) const;
+
+#if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ // Rendering stuff
+ void GetStart( rmt::Vector &point ) const;
+ void GetEnd( rmt::Vector &point ) const;
+ bool GetPoint( float t, rmt::Vector& point ) const;
+ void GetPoint( unsigned int index, rmt::Vector* point ) const;
+#endif
+ //////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////
+ // Implementing ILaneControl
+ //
+ void SetSpeedLimit( float metrespersecond );
+ void SetDensity( int numCars );
+ void NotifyWaitingTraffic( unsigned int state );
+ ////////////////////////////////////////////////////
+
+
+ // Temp method to display.
+ void Render( void );
+
+#if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ void AllocatePoints( unsigned int numPoints );
+ unsigned int GetNumPoints( void ) const;
+ void SetPoint( unsigned int index, const rmt::Vector& point );
+#endif
+
+public:
+ // the vehicle at the front of all lane's traffic, waiting at the intersection.
+ TrafficVehicle* mWaitingVehicle;
+
+ // Add traffic to the lane's internal list to keep track of how many have been
+ // added to the lane (to conform to the lane's desired density).
+ SwapArray<TrafficVehicle*> mTrafficVehicles;
+
+
+private:
+ const Road *mpParentRoad; // the road that owns this lane.
+ float mfSpeedLimit; // the maximum allowable speed for this lane.
+ int mDesiredDensity; // the desired density ( in total cars )
+
+#if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ // Rendering stuff
+ rmt::Vector* mPoints;
+ unsigned int mNumPoints;
+#endif
+
+};
+
+//*****************************************************************************
+//
+// Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// Lane::SetSpeedLimit
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float metrespersecond )
+//
+// Return: void
+//
+//=============================================================================
+inline void Lane::SetSpeedLimit( float metrespersecond )
+{
+ mfSpeedLimit = metrespersecond;
+}
+
+
+#if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ inline bool Lane::GetPoint( float t, rmt::Vector& point ) const
+ {
+ //t will equal what segment 0 - mpParentRoad->GetNumRoadSegments()
+ // *plus* 0-0.999999... for it's position in the road.
+ return false;
+ }
+
+ inline void Lane::GetPoint( unsigned int index, rmt::Vector* point ) const
+ {
+ rAssert( index < mNumPoints );
+ *point = mPoints[index];
+ }
+#endif
+
+
+
+inline const Road* Lane::GetRoad( void ) const
+{
+ return mpParentRoad;
+}
+inline float Lane::GetSpeedLimit() const
+{
+ return mfSpeedLimit;
+}
+inline void Lane::SetDensity( int numCars )
+{
+ mDesiredDensity = numCars;
+}
+inline int Lane::GetDensity() const
+{
+ return mDesiredDensity;
+}
+
+
+#endif \ No newline at end of file
diff --git a/game/code/roads/road.cpp b/game/code/roads/road.cpp
new file mode 100644
index 0000000..542fc82
--- /dev/null
+++ b/game/code/roads/road.cpp
@@ -0,0 +1,830 @@
+/*===========================================================================
+ Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+
+ Component: Road
+
+ Description:
+
+
+ Authors: Travis Brown-John
+
+ Revisions Date Author Revision
+ 2001/02/02 Tbrown-John Created
+
+===========================================================================*/
+
+#include <roads/road.h>
+#include <roads/roadsegment.h>
+#include <roads/lane.h>
+#include <roads/geometry.h>
+#include <raddebug.hpp>
+
+#ifndef TOOLS
+#include <memory/srrmemory.h>
+#else
+#define MEMTRACK_PUSH_GROUP(x)
+#define MEMTRACK_POP_GROUP()
+#endif
+
+/*
+==============================================================================
+Road::Road
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: na
+
+=============================================================================
+*/
+Road::Road( void )
+:
+mpSourceIntersection( 0 ),
+mpDestinationIntersection( 0 ),
+mLaneList( 0 ),
+mnLanes( 0 ),
+mppRoadSegmentArray( 0 ),
+mnMaxRoadSegments( 0 ),
+mnRoadSegments( 0 ),
+mSpeed( 0 ),
+mDensity( 0 ),
+mDifficulty( 0 ),
+mIsShortCut( false )
+{
+}
+
+/*
+==============================================================================
+Road::~Road
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: na
+
+=============================================================================
+*/
+Road::~Road( void )
+{
+ if ( mLaneList )
+ {
+ delete [] mLaneList;
+ mLaneList = 0;
+ }
+
+ if ( mppRoadSegmentArray )
+ {
+ delete[] mppRoadSegmentArray;
+ mppRoadSegmentArray = NULL;
+ }
+}
+
+/*
+==============================================================================
+Road::AllocateSegments
+==============================================================================
+Description: Comment
+
+Parameters: ( unsigned int numSegments )
+
+Return: void
+
+=============================================================================
+*/
+void Road::AllocateSegments( unsigned int numSegments )
+{
+MEMTRACK_PUSH_GROUP( "Road" );
+#ifndef TOOLS
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+#endif
+#endif
+
+ rAssert( numSegments > 0 );
+ rAssert( 0 == mppRoadSegmentArray );
+ rAssert( 0 == mnRoadSegments );
+
+ mnMaxRoadSegments = numSegments;
+ mppRoadSegmentArray = new RoadSegment*[ mnMaxRoadSegments ];
+
+ unsigned int i = 0;
+ for ( i = 0; i < mnMaxRoadSegments; i++ )
+ {
+ mppRoadSegmentArray[ i ] = 0;
+ }
+
+#ifndef TOOLS
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ #endif
+#endif
+MEMTRACK_POP_GROUP( "Road" );
+}
+
+/*
+==============================================================================
+Road::AddRoadSegment
+==============================================================================
+Description: Add a road segment.
+
+Parameters: ( RoadSegment* pRoadSegment )
+
+Return: void
+
+=============================================================================
+*/
+void Road::AddRoadSegment( RoadSegment* pRoadSegment )
+{
+ mppRoadSegmentArray[ mnRoadSegments ] = pRoadSegment;
+
+ pRoadSegment->SetSegmentIndex( mnRoadSegments );
+
+ mnRoadSegments++;
+
+ unsigned int numVertices = 4;
+ rmt::Vector vertex;
+ unsigned int i = 0;
+ for ( i = 0; i < numVertices; i++ )
+ {
+ pRoadSegment->GetCorner( i, vertex );
+ if ( 1 == mnRoadSegments && 0 == i )
+ {
+ // This is the first time.
+ // Initialize to some value.
+ //
+ mBox.low = mBox.high = vertex;
+ }
+ else
+ {
+ if ( mBox.low.x > vertex.x )
+ {
+ mBox.low.x = vertex.x;
+ }
+ if ( mBox.low.y > vertex.y )
+ {
+ mBox.low.y = vertex.y;
+ }
+ if ( mBox.low.z > vertex.z )
+ {
+ mBox.low.z = vertex.z;
+ }
+
+ if ( mBox.high.x < vertex.x )
+ {
+ mBox.high.x = vertex.x;
+ }
+ if ( mBox.high.y < vertex.y )
+ {
+ mBox.high.y = vertex.y;
+ }
+ if ( mBox.high.z < vertex.z )
+ {
+ mBox.high.z = vertex.z;
+ }
+ }
+ }
+ // now we should have a bounding box.
+ //
+ // compute the bounding sphere.
+ //
+ // first the centre.
+ //
+ rmt::Vector vectorBetween;
+ vectorBetween.Sub( mBox.high, mBox.low );
+ vectorBetween.Scale( 0.5f );
+ mSphere.centre.Add( mBox.low, vectorBetween );
+ // Then the radius
+
+ mSphere.radius = vectorBetween.Magnitude( );
+}
+
+/*
+==============================================================================
+Road::CreateLanes
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Road::CreateLanes( void )
+{
+MEMTRACK_PUSH_GROUP( "Road" );
+ // Allocate the lanes.
+ //
+ //TODO: REMOVE!
+ #ifndef TOOLS
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ #endif
+ #endif
+
+ mLaneList = new Lane[ mnLanes ];
+
+ // Allocate the memory for the lane points.
+ //
+ unsigned int totalDensity = GetDensity();
+ unsigned int laneDensity = totalDensity / mnLanes;
+
+ unsigned int i = 0;
+ for ( i = 0; i < mnLanes; i++ )
+ {
+ // Do this first.
+ //
+ // if on last lane, just add whatever's left of totalDensity
+ unsigned int density = 0;
+ if( i == mnLanes - 1 )
+ {
+ density = totalDensity;
+ }
+ else
+ {
+ density = laneDensity;
+ }
+ mLaneList[ i ].Create( (int)density, this );
+ totalDensity -= laneDensity;
+ mLaneList[ i ].SetSpeedLimit( (float)GetSpeed() * KPH_2_MPS ); //convert from kph to mps
+
+#if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ // For each lane, allocate enough points to store all the roadSegment data plus one.
+ mLaneList[ i ].AllocatePoints( this->GetNumRoadSegments( ) + 1 );
+#endif
+
+ }
+ // Iterate through the segments.
+ //
+ for ( i = 0; i < mnRoadSegments; i++ )
+ {
+ RoadSegment* pSeg = mppRoadSegmentArray[ i ];
+ unsigned int j = 0;
+ for ( j = 0; j < mnLanes; j++ )
+ {
+ // For each lane in each segment, add a point to the lane.
+ //
+ rmt::Vector start, facing;
+ pSeg->GetLaneLocation( 0.0f, j, start, facing );
+
+ #if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ mLaneList[ j ].SetPoint( i, start );
+ #endif
+ }
+ }
+ // This is attached to the out intersection.
+ //
+ RoadSegment* pSeg = mppRoadSegmentArray[ mnRoadSegments - 1 ];
+ // Now add the last point.
+ //
+ for ( i = 0; i < mnLanes; i++ )
+ {
+ rmt::Vector end, facing;
+ pSeg->GetLaneLocation( 1.0f, i, end, facing );
+ #if defined(RAD_TUNE) || defined(RAD_DEBUG)
+ mLaneList[ i ].SetPoint( mnRoadSegments, end );
+ #endif
+ }
+
+ //TODO: REMOVE!
+ #ifndef TOOLS
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ #endif
+ #endif
+
+MEMTRACK_POP_GROUP( "Road" );
+}
+
+/*
+==============================================================================
+Road::GetLane
+==============================================================================
+Description: Comment
+
+Parameters: ( unsigned int LaneId )
+
+Return: Lane*
+
+=============================================================================
+*/
+Lane* Road::GetLane( unsigned int LaneId ) const
+{
+ if ( LaneId < mnLanes )
+ {
+ return &mLaneList[ LaneId ];
+ }
+ else
+ {
+ return 0;
+ }
+}
+RoadSegment* Road::GetRoadSegment( unsigned int index ) const
+{
+ rAssert( index < mnRoadSegments );
+
+ return mppRoadSegmentArray[ index ];
+}
+
+//
+//
+/*
+==============================================================================
+Road::GetDestinationIntersectionJoinPoint
+==============================================================================
+Description: Return the point where the road attaches to the incoming intersection.
+
+Parameters: ( rmt::Vector& out )
+
+Return: void
+
+=============================================================================
+*/
+void Road::GetDestinationIntersectionJoinPoint( rmt::Vector& out )
+{
+ RoadSegment* pRoadSegment = this->GetRoadSegment( 0 );
+
+ pRoadSegment->GetCorner( 0, out );
+}
+/*
+==============================================================================
+Road::GetSourceIntersectionJoinPoint
+==============================================================================
+Description: Return the point where the road attaches to the outgoing intersection.
+
+Parameters: ( rmt::Vector& out )
+
+Return: void
+
+=============================================================================
+*/
+void Road::GetSourceIntersectionJoinPoint( rmt::Vector& out )
+{
+ RoadSegment* pRoadSegment = this->GetRoadSegment( this->GetNumRoadSegments( ) - 1 );
+
+ pRoadSegment->GetCorner( 1, out );
+}
+
+/*
+==============================================================================
+Road::GetRoadSegmentAtPoint
+==============================================================================
+Description: returns a road segment, if the point is inside a segment on this road.
+
+Parameters: ( const rmt::Vector& point, RoadSegment& outRoadSegment, int hint )
+
+Return: int - index of road segment this point is in.
+
+=============================================================================
+*/
+int Road::GetRoadSegmentAtPoint( const rmt::Vector& point, RoadSegment** ppOutRoadSegment, float& in, float& lateral, int hint ) const
+{
+ rmt::Vector vectorBetween;
+ // Do the 2D Math Explicitly.
+ // My profile test show it is significantly faster.
+ //
+ vectorBetween.x = point.x - mSphere.centre.x;
+ vectorBetween.y = 0.0f;
+ vectorBetween.z = point.z - mSphere.centre.z;
+ vectorBetween.x *= vectorBetween.x;
+ vectorBetween.z *= vectorBetween.z;
+ vectorBetween.x += vectorBetween.z;
+
+ if ( vectorBetween.x <= rmt::Sqr( mSphere.radius ) )
+ {
+ // Now test against AABB.
+ //
+ if ( point.x <= mBox.high.x
+ && point.x >= mBox.low.x
+ && point.z <= mBox.high.z
+ && point.z >= mBox.low.z )
+ {
+ int i = hint;
+ // Check the hint.
+ //
+ if ( !IsPointInRoadSegment( i, point, in, lateral ) )
+ {
+ // Check the one ahead.
+ //
+ if ( !IsPointInRoadSegment( ++i, point, in, lateral ) )
+ {
+ // Check the one behind.
+ //
+ if ( !IsPointInRoadSegment( i -= 2, point, in, lateral ) )
+ {
+ // Scan the entire list.
+ //
+ for ( i = 0; i < static_cast< int >( GetNumRoadSegments( ) ); i++ )
+ {
+ // Skip the ones we've looked at.
+ //
+ if ( i != hint && i != hint + 1 && i != hint - 1 )
+ {
+ // Stop when we find one.
+ //
+ if ( IsPointInRoadSegment( i, point, in, lateral ) )
+ {
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ if ( i < static_cast< int >( GetNumRoadSegments( ) ) )
+ {
+ // We found one.
+ //
+ *ppOutRoadSegment = GetRoadSegment( i );
+ return i;
+ }
+ }
+ }
+ *ppOutRoadSegment = 0;
+ return -1;
+}
+
+/*
+==============================================================================
+Road::IsPointInRoadSegment
+==============================================================================
+Description: Tests the distance into and the lateral distance in a segment
+ if both lateral and in distance are between 0.0f - 1.0f
+ we return true.
+
+ Quick and Dirty performance number:
+ XBox 02/28/2002
+ 26.0 ms per 10000 calls point inside BBox
+ 1.0 ms per 10000 calls test BBox only.
+
+ 0.0025 / 10000
+
+ XBox 0.0025 ms in Debug for one call to IsPointInRoadSegment
+
+Parameters: ( int index )
+
+Return: bool - true if point is in road segment.
+
+=============================================================================
+*/
+bool Road::IsPointInRoadSegment( int index, const rmt::Vector& point, float& in, float& lateral ) const
+{
+ if ( index >= 0 && index < static_cast<int>( GetNumRoadSegments() ) )
+ {
+ RoadSegment* pRoadSegment = GetRoadSegment( index );
+ rmt::Vector vectorBetween;
+ rmt::Sphere sphere;
+ pRoadSegment->GetBoundingSphere( &sphere );
+
+ // Do the 2D Math Explicitly.
+ // My profile test show it is significantly faster.
+ //
+ vectorBetween.x = point.x - sphere.centre.x;
+ vectorBetween.y = 0.0f;
+ vectorBetween.z = point.z - sphere.centre.z;
+ vectorBetween.x *= vectorBetween.x;
+ vectorBetween.z *= vectorBetween.z;
+ vectorBetween.x += vectorBetween.z;
+
+ if ( vectorBetween.x <= rmt::Sqr( sphere.radius ) )
+ {
+ in = pRoadSegment->CalculateUnitDistIntoRoadSegment( point.x, point.z );
+ if ( in >= 0.0f && in <= 1.0f )
+ {
+ lateral = pRoadSegment->CalculateUnitHeightInRoadSegment( point.x, point.z );
+ if ( lateral >= 0.0f && lateral <= 1.0f )
+ {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+
+//=============================================================================
+// RoadManager::FindRoadSegmentAhead
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const Road* pRoad,
+// float& dist,
+// const float maxDist,
+// RoadSegment** segment )
+//
+// Return: bool
+//
+//=============================================================================
+bool Road::FindSegmentAhead( float& dist,
+ const float maxDist,
+ const int currentIndex,
+ RoadSegment** segment ) const
+{
+ rmt::Vector backleft;
+ rmt::Vector frontleft;
+ rmt::Vector frontright;
+ rmt::Vector backright;
+ rmt::Vector leftdir;
+ rmt::Vector rightdir;
+ rmt::Vector direction;
+ rmt::Vector destination;
+
+ rmt::Vector currentPos;
+
+ RoadSegment* pCurrentSegment = GetRoadSegment( currentIndex );
+
+ //
+ // Temporarily use pCurrentSegment to get data about where
+ // we're starting
+ //
+
+ pCurrentSegment->GetCorner( 0, backleft );
+ pCurrentSegment->GetCorner( 1, frontleft );
+ pCurrentSegment->GetCorner( 2, frontright );
+ pCurrentSegment->GetCorner( 3, backright );
+
+ //
+ // Find the front midpoint of the segment
+ //
+ currentPos.Add( frontleft, frontright );
+ currentPos.Scale( 0.5f, 0.5f, 0.5f );
+
+ //
+ // Find the average direction of the segment
+ //
+
+ leftdir.Sub( frontleft, backleft );
+ leftdir.Normalize();
+ rightdir.Sub( frontright, backright );
+ rightdir.Normalize();
+
+ //
+ // Take the average and scale by 0.1
+ //
+ direction.Add( leftdir, rightdir );
+ direction.Scale( 0.05f, 0.05f, 0.05f );
+
+ //
+ // This should put us within the next segment
+ //
+ currentPos.Add( direction );
+
+ RoadSegment* pNextSegment = NULL;
+ pCurrentSegment = NULL;
+
+ //float in, lateral = 0.0f;
+ int segmentIndex = currentIndex;
+
+ float currentDist = dist;
+ while( currentDist < maxDist )
+ {
+ //
+ // This search is not optimal
+ //
+ //segmentIndex = pRoad->GetRoadSegmentAtPoint( currentPos,
+ // &pNextSegment, in, lateral, segmentIndex + 1 );
+
+ //
+ // Much faster when the segments are sorted. Yay!
+ //
+ segmentIndex += 1;
+
+ if( segmentIndex >= static_cast<int>( GetNumRoadSegments() ) )
+ {
+ //
+ // Get as close as we can to the desired distance.
+ // Returns NULL if we didn't find anything at all.
+ //
+ (*segment) = pCurrentSegment;
+ dist = currentDist;
+ return false;
+ }
+
+ pNextSegment = GetRoadSegment( segmentIndex );
+ rAssert( pNextSegment != NULL );
+
+ //
+ // Find the midpoint of the front edge
+ //
+ pNextSegment->GetCorner( 1, frontleft );
+ pNextSegment->GetCorner( 2, frontright );
+
+ destination.Add( frontleft, frontright );
+ destination.Scale( 0.5f, 0.5f, 0.5f );
+
+ //
+ // Gives a vector along the centre line assuming currentPos
+ // is on the front edge midpoint of the previous segment
+ //
+ direction.Sub( destination, currentPos );
+
+ float segLength = direction.Magnitude();
+ currentDist += segLength;
+
+ //
+ // Normalize the direction and scale it by 0.1
+ //
+ // not necessary for the fast method
+ //float invLen = 0.1f / segLength;
+ //direction.Scale( invLen, invLen, invLen );
+
+ //currentPos.Add( destination, direction );
+
+ pCurrentSegment = pNextSegment;
+
+ }
+
+ //
+ // Got to the distance the person wanted
+ //
+ (*segment) = pCurrentSegment;
+ dist = currentDist;
+ return true;
+}
+
+//=============================================================================
+// RoadManager::FindSegmentBehind
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const Road* pRoad,
+// float& dist,
+// const float maxDist,
+// RoadSegment** segment )
+//
+// Return: bool
+//
+//=============================================================================
+bool Road::FindSegmentBehind( float& dist,
+ const float maxDist,
+ const int currentIndex,
+ RoadSegment** segment ) const
+{
+ rmt::Vector backleft;
+ rmt::Vector frontleft;
+ rmt::Vector frontright;
+ rmt::Vector backright;
+ rmt::Vector leftdir;
+ rmt::Vector rightdir;
+ rmt::Vector direction;
+ rmt::Vector destination;
+
+ rmt::Vector currentPos;
+
+ RoadSegment* pCurrentSegment = GetRoadSegment( currentIndex );
+
+ //
+ // Temporarily use pCurrentSegment to get data about where
+ // we're starting
+ //
+
+ pCurrentSegment->GetCorner( 0, backleft );
+ pCurrentSegment->GetCorner( 1, frontleft );
+ pCurrentSegment->GetCorner( 2, frontright );
+ pCurrentSegment->GetCorner( 3, backright );
+
+ //
+ // Find the rear midpoint of the segment
+ //
+ currentPos.Add( backleft, backright );
+ currentPos.Scale( 0.5f, 0.5f, 0.5f );
+
+ //
+ // Find the average direction of the segment
+ //
+
+ leftdir.Sub( backleft, frontleft );
+ leftdir.Normalize();
+ rightdir.Sub( backright, frontright );
+ rightdir.Normalize();
+
+ //
+ // Take the average and scale by 0.1
+ //
+ direction.Add( leftdir, rightdir );
+ direction.Scale( 0.05f, 0.05f, 0.05f );
+
+ //
+ // This should put us within the next segment
+ //
+ currentPos.Add( direction );
+
+ RoadSegment* pNextSegment = NULL;
+ //float in, lateral = 0.0f;
+ int segmentIndex = currentIndex;
+
+ float currentDist = dist;
+ while( currentDist < maxDist )
+ {
+ pCurrentSegment = pNextSegment;
+
+ //
+ // This search is not optimal
+ //
+ //segmentIndex = pRoad->GetRoadSegmentAtPoint( currentPos,
+ // &pNextSegment, in, lateral, segmentIndex + 1 );
+
+ //
+ // Much faster when the segments are sorted. Yay!
+ //
+ segmentIndex -= 1;
+
+ if( segmentIndex < 0 )
+ {
+ //
+ // Get as close as we can to the desired distance.
+ // Returns NULL if we didn't find anything at all.
+ //
+ (*segment) = pCurrentSegment;
+ dist = currentDist;
+ return false;
+ }
+
+ pNextSegment = GetRoadSegment( segmentIndex );
+ rAssert( pNextSegment != NULL );
+
+ //
+ // Find the midpoint of the front edge
+ //
+ pNextSegment->GetCorner( 0, backleft );
+ pNextSegment->GetCorner( 3, backright );
+
+ destination.Add( backleft, backright );
+ destination.Scale( 0.5f, 0.5f, 0.5f );
+
+ //
+ // Gives a vector along the centre line assuming currentPos
+ // is on the front edge midpoint of the previous segment
+ //
+ direction.Sub( destination, currentPos );
+
+ float segLength = direction.Magnitude();
+ currentDist += segLength;
+
+ //
+ // Normalize the direction and scale it by 0.1
+ //
+ // not necessary for the fast method
+ //float invLen = 0.1f / segLength;
+ //direction.Scale( invLen, invLen, invLen );
+
+ //currentPos.Add( destination, direction );
+ }
+
+ //
+ // Got to the distance the person wanted
+ //
+ (*segment) = pCurrentSegment;
+ dist = currentDist;
+ return true;
+}
+
+
+//=============================================================================
+// Road::SetDensity
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int density )
+//
+// Return: void
+//
+//=============================================================================
+void Road::SetDensity( unsigned int density )
+{
+ mDensity = density;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void Road::FindRoadSegmentAtDist( float iDist, RoadSegment** oppRoadSegment )
+{
+ if(iDist>GetRoadLength())
+ {
+ *oppRoadSegment = NULL;
+ return;
+ }
+
+ RoadSegment* pRoadSegment = NULL;
+
+ float distAccum = 0.0f;
+ for(int i=mnRoadSegments-1; i>-1 && distAccum<iDist; i--)
+ {
+ pRoadSegment = mppRoadSegmentArray[i];
+ distAccum += pRoadSegment->GetSegmentLength();
+ }
+
+ *oppRoadSegment = pRoadSegment;
+}
+
diff --git a/game/code/roads/road.h b/game/code/roads/road.h
new file mode 100644
index 0000000..2d20e3e
--- /dev/null
+++ b/game/code/roads/road.h
@@ -0,0 +1,249 @@
+/*===========================================================================
+ Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+
+ Component: Road
+
+ Description:
+
+
+ Authors: Travis Brown-John
+
+ Revisions Date Author Revision
+ 2001/02/02 Tbrown-John Created
+
+===========================================================================*/
+#ifndef ROAD_HPP_
+#define ROAD_HPP_
+
+#include <radmath/radmath.hpp>
+#include <p3d/p3dtypes.hpp>
+#include <string.h>
+
+#ifdef TOOLS
+#include <p3d/entity.hpp>
+#endif
+
+// forward declarations
+class Intersection;
+class Lane;
+class RoadSegment;
+
+// 3 lanes in one direction maximum.
+const unsigned int MAX_LANES = 3;
+
+
+#ifdef TOOLS
+#define IS_ENTITY : public tEntity
+#else
+#define IS_ENTITY
+#endif
+
+class Road IS_ENTITY
+{
+public: // methods
+
+ Road( void );
+ ~Road( void );
+
+ void SetNumLanes( int count );
+ unsigned int GetNumLanes( void ) const;
+
+ // Allocate memory for numSegments roadSegments.
+ //
+ void AllocateSegments( unsigned int numSegments );
+
+ // Return Lane at position 'laneId'. return NULL if lane doesn't exist.
+ //
+ Lane *GetLane( unsigned int laneId ) const;
+
+ // Add a road segment.
+ //
+ void AddRoadSegment( RoadSegment* pRoadSegment );
+
+ // Creates lanes based on RoadSegment list.
+ //
+ void CreateLanes( void );
+
+ // return the intersection at the beginning of this road.
+ //
+ const Intersection *GetSourceIntersection( void ) const
+ { return mpSourceIntersection; }
+
+ // return the intersection at the end of this road.
+ //
+ const Intersection *GetDestinationIntersection( void ) const
+ { return mpDestinationIntersection; }
+
+ // set the intersection at the beginning of this road.
+ //
+ void SetSourceIntersection( Intersection *pIntersection )
+ { mpSourceIntersection = pIntersection; }
+
+ // set the intersection at the end of this road.
+ //
+ void SetDestinationIntersection( Intersection *pIntersection )
+ { mpDestinationIntersection = pIntersection; }
+
+ // Return the point where the road attaches to the incoming intersection.
+ //
+ void GetSourceIntersectionJoinPoint( rmt::Vector& out );
+
+ // Return the point where the road attaches to the outgoing intersection.
+ //
+ void GetDestinationIntersectionJoinPoint( rmt::Vector& out );
+
+ unsigned int GetNumRoadSegments( void ) const
+ { return mnRoadSegments; }
+
+ unsigned int GetMaxRoadSegments( void ) const
+ { return mnMaxRoadSegments; }
+
+ RoadSegment* GetRoadSegment( unsigned int index ) const;
+
+ int GetRoadSegmentAtPoint( const rmt::Vector& point, RoadSegment** ppOutRoadSegment, float& in, float& lateral, int hint ) const;
+
+ bool IsPointInRoadSegment( int index, const rmt::Vector& point, float& in, float& lateral ) const;
+
+ void Render( const tColour& colour );
+
+ void SetSpeed( unsigned int speed );
+ unsigned int GetSpeed() const;
+
+ void SetDifficulty( unsigned int diff );
+ unsigned int GetDifficulty() const;
+
+ void SetShortCut( bool is );
+ bool GetShortCut() const;
+
+ void SetDensity( unsigned int density );
+ unsigned int GetDensity() const;
+
+ void FindRoadSegmentAtDist( float iDist, RoadSegment** ippRoadSegment );
+ //---------------Helpful methods
+ // returns true if got to maxdist on the current road
+ // false if got to the end of the road first
+ bool FindSegmentAhead( float& dist,
+ const float maxDist,
+ const int currentIndex,
+ RoadSegment** segment ) const;
+ bool FindSegmentBehind( float& dist,
+ const float maxDist,
+ const int currentIndex,
+ RoadSegment** segment ) const;
+
+#if !defined( RAD_RELEASE ) && !defined( TOOLS )
+ void SetName( const char* name ) { strcpy( mName, name ); };
+ const char* GetName() { return mName; }
+#endif
+
+ const rmt::Sphere& GetBoundingSphere() const { return mSphere; };
+
+ float GetRoadLength();
+ void SetRoadLength( float len );
+
+private: // data
+ // Intersection at the start of the road.
+ //
+ const Intersection *mpSourceIntersection;
+
+ // Intersection at the end of the road.
+ //
+ const Intersection *mpDestinationIntersection;
+
+ // List of Lanes in a left to right order.
+ //
+ Lane *mLaneList;
+
+ // how many lanes on this road.
+ //
+ unsigned int mnLanes;
+
+ // The pointer to the head of the RoadSegment Array.
+ //
+ RoadSegment** mppRoadSegmentArray;
+
+ // How many have we allocated.
+ //
+ unsigned int mnMaxRoadSegments;
+
+ // A count of road segments.
+ //
+ unsigned int mnRoadSegments;
+
+ /////////////////////////
+ // TODO: Get rid of this
+ // A bounding box in world space for the road.
+ //
+ rmt::Box3D mBox;
+
+ // A bounding sphere in world space for the road.
+ //
+ rmt::Sphere mSphere;
+
+ unsigned int mSpeed;
+ unsigned int mDensity;
+ unsigned int mDifficulty;
+ bool mIsShortCut;
+
+#ifndef RAD_RELEASE
+ char mName[256];
+#endif
+
+ float mLength;
+};
+
+
+
+inline void Road::SetSpeed( unsigned int speed )
+{
+ mSpeed = speed;
+}
+inline unsigned int Road::GetSpeed() const
+{
+ return mSpeed;
+}
+inline void Road::SetDifficulty( unsigned int diff )
+{
+ mDifficulty = diff;
+}
+inline unsigned int Road::GetDifficulty() const
+{
+ return mDifficulty;
+}
+inline void Road::SetShortCut( bool is )
+{
+ mIsShortCut = is;
+}
+inline bool Road::GetShortCut() const
+{
+ return mIsShortCut;
+}
+inline unsigned int Road::GetDensity() const
+{
+ return mDensity;
+}
+inline float Road::GetRoadLength()
+{
+ return mLength;
+}
+inline void Road::SetRoadLength( float len )
+{
+ rAssert( len >= 0.0f );
+ mLength = len;
+}
+inline void Road::SetNumLanes( int count )
+{
+ mnLanes = count;
+}
+inline unsigned int Road::GetNumLanes( void ) const
+{
+ return mnLanes;
+}
+/*
+inline float Road::GetWidth() const
+{
+ return mnLanes * mfLaneWidth;
+}
+*/
+
+#endif
diff --git a/game/code/roads/roadmanager.cpp b/game/code/roads/roadmanager.cpp
new file mode 100644
index 0000000..9b705ca
--- /dev/null
+++ b/game/code/roads/roadmanager.cpp
@@ -0,0 +1,2565 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: RoadManager.cpp
+//
+// Description: Implement RoadManager
+//
+// History: 26/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/entity.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/intersectmanager/intersectmanager.h>
+#include <roads/RoadManager.h>
+#include <roads/road.h>
+#include <roads/intersection.h>
+#include <roads/roadsegmentdata.h>
+#include <roads/roadsegment.h>
+#include <roads/roadrendertest.h>
+
+#ifndef TOOLS
+#include <memory/srrmemory.h>
+#else
+#define MEMTRACK_PUSH_GROUP(x)
+#define MEMTRACK_POP_GROUP()
+void* operator new[](size_t size)
+{
+ return malloc(size);
+}
+#endif
+
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/RenderLayer.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+RoadManager* RoadManager::mInstance = NULL;
+const float RoadManager::AGAINST_TRAFFIC_COST_MULTIPLIER = 1.30f;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+RoadManager* RoadManager::GetInstance()
+{
+MEMTRACK_PUSH_GROUP( "RoadManager" );
+ if ( !mInstance )
+ {
+#ifndef TOOLS
+ mInstance = new(GMA_LEVEL_OTHER) RoadManager();
+#else
+ mInstance = new RoadManager();
+#endif
+ //TODO: REMOVE
+ mInstance->Init( STARTUP );
+ }
+MEMTRACK_POP_GROUP( "RoadManager" );
+
+ return mInstance;
+}
+
+//=============================================================================
+// RoadManager::Destroy
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RoadManager::Destroy()
+{
+ delete mInstance;
+ mInstance = NULL;
+}
+
+
+//==============================================================================
+// RoadManager::RoadManager
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RoadManager::RoadManager()
+{
+ mRoads = NULL;
+ mNumRoads = 0;
+ mNumRoadsUsed = 0;
+ mIntersections = NULL;
+ mNumIntersections = 0;
+ mNumIntersections = 0;
+ mRoadSegmentData = NULL;
+ mNumRoadSegmentData = 0;
+ mNumRoadSegmentDataUsed = 0;
+ mRoadSegments = NULL;
+ mNumRoadSegments = 0;
+ mNumRoadSegmentsUsed = 0;
+
+#ifndef RAD_RELEASE
+#ifdef DEBUGWATCH
+ mRender = new(GMA_LEVEL_OTHER) RoadRenderTest;
+
+ RenderManager* rm = GetRenderManager();
+ RenderLayer* rl = rm->mpLayer( RenderEnums::LevelSlot );
+ rAssert( rl );
+
+ rl->AddGuts( mRender );
+#endif
+#endif
+}
+
+//==============================================================================
+// RoadManager::~RoadManager
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RoadManager::~RoadManager()
+{
+ Init( SHUTDOWN );
+}
+
+//=============================================================================
+// RoadManager::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RoadManager::Init( bool shutdown )
+{
+ if ( mRoads )
+ {
+ delete[] mRoads;
+ mRoads = NULL;
+ }
+ mNumRoads = 0;
+ mNumRoadsUsed = 0;
+
+ if ( mIntersections )
+ {
+ delete[] mIntersections;
+ mIntersections = NULL;
+ }
+ mNumIntersections = 0;
+ mNumIntersectionsUsed = 0;
+
+ if ( mRoadSegmentData )
+ {
+ delete[] mRoadSegmentData;
+ mRoadSegmentData = NULL;
+ }
+ mNumRoadSegmentData = 0;
+ mNumRoadSegmentDataUsed = 0;
+
+ if ( mRoadSegments )
+ {
+ for(unsigned int x = 0; x < mNumRoadSegments; x++)
+ {
+ mRoadSegments[x]->Release ();
+ }
+ delete[] mRoadSegments;
+ mRoadSegments = NULL;
+ }
+ mNumRoadSegments = 0;
+ mNumRoadSegmentsUsed = 0;
+
+ if( !shutdown )
+ {
+ InitializeRoadSegmentDataMemory( 1200 );
+ //TODO: base this on incoming chunk data.
+ InitializeRoadMemory( 150 );
+ InitializeIntersectionMemory( 60 );
+ InitializeRoadSegmentMemory( 1200 );
+ }
+}
+void RoadManager::InitializeRoadMemory( unsigned int numRoads )
+{
+
+#ifndef TOOLS
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+#endif
+#endif
+
+ if( mRoads )
+ {
+ delete[] mRoads;
+ }
+
+ mRoads = new Road[numRoads];
+ mNumRoads = numRoads;
+ mNumRoadsUsed = 0;
+
+#ifndef TOOLS
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ #endif
+#endif
+}
+
+
+void RoadManager::InitializeIntersectionMemory( unsigned int numIntersections )
+{
+#ifndef TOOLS
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+#endif
+#endif
+
+ if( mIntersections )
+ {
+ delete[] mIntersections;
+ }
+
+ mIntersections = new Intersection[numIntersections];
+ mNumIntersections = numIntersections;
+ mNumIntersectionsUsed = 0;
+
+#ifndef TOOLS
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ #endif
+#endif
+}
+
+
+void RoadManager::InitializeRoadSegmentDataMemory( unsigned int numSegments )
+{
+#ifndef TOOLS
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_TEMP );
+ #endif
+#endif
+
+ if( mRoadSegmentData )
+ {
+ delete[] mRoadSegmentData;
+ }
+
+ mRoadSegmentData = new RoadSegmentData[numSegments];
+ mNumRoadSegmentData = numSegments;
+ mNumRoadSegmentDataUsed = 0;
+
+#ifndef TOOLS
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_TEMP );
+ #endif
+#endif
+}
+
+void RoadManager::DumpRoadSegmentDataMemory()
+{
+ if( mRoadSegmentData )
+ {
+ delete[] mRoadSegmentData;
+ }
+ mRoadSegmentData = NULL;
+ mNumRoadSegmentData = 0;
+ mNumRoadSegmentDataUsed = 0;
+}
+
+
+void RoadManager::InitializeRoadSegmentMemory( unsigned int numRoadSegments )
+{
+#ifndef TOOLS
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+#endif
+#endif
+
+ if( mRoadSegments )
+ {
+ delete[] mRoadSegments;
+ }
+
+ mRoadSegments = new RoadSegment*[numRoadSegments];
+
+ unsigned int i = 0;
+ for( i ; i < numRoadSegments ; i++ )
+ {
+ mRoadSegments[i] = new RoadSegment;
+ mRoadSegments[i]->AddRef();
+ }
+ mNumRoadSegments = numRoadSegments;
+ mNumRoadSegmentsUsed = 0;
+
+#ifndef TOOLS
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ #endif
+#endif
+}
+
+
+
+Road* RoadManager::GetFreeRoadMemory()
+{
+ if( 0 <= mNumRoadsUsed && mNumRoadsUsed < mNumRoads )
+ {
+ Road* road;
+ road = &(mRoads[mNumRoadsUsed]);
+ return road;
+ }
+
+ return NULL;
+}
+
+Intersection* RoadManager::GetFreeIntersectionMemory()
+{
+ if( 0 <= mNumIntersectionsUsed && mNumIntersectionsUsed < mNumIntersections )
+ {
+ Intersection* intersection;
+ intersection = &(mIntersections[mNumIntersectionsUsed]);
+ return intersection;
+ }
+
+ return NULL;
+}
+
+RoadSegmentData* RoadManager::GetFreeRoadSegmentDataMemory()
+{
+ if( 0 <= mNumRoadSegmentsUsed && mNumRoadSegmentDataUsed < mNumRoadSegmentData )
+ {
+ RoadSegmentData* rsd;
+ rsd = &(mRoadSegmentData[mNumRoadSegmentDataUsed]);
+ return rsd;
+ }
+ else
+ {
+ rDebugPrintf( "NUMROADSUSED = %d, NUMROADS = %d\n",
+ mNumRoadSegmentDataUsed, mNumRoadSegmentData );
+ }
+
+ return NULL;
+}
+
+RoadSegment* RoadManager::GetFreeRoadSegmentMemory()
+{
+ if( 0 <= mNumRoadSegmentsUsed && mNumRoadSegmentsUsed < mNumRoadSegments )
+ {
+ return mRoadSegments[mNumRoadSegmentsUsed];
+ }
+ return NULL;
+}
+
+
+int RoadManager::GetMaxPathElements()
+{
+ // max elems we could ever have include:
+ // - sourceroad,
+ // - targetroad,
+ // - total number of intersections + their adjoining roads
+ return (1 + 1 + (GetNumIntersectionsUsed() * 2) - 1);
+}
+
+bool RoadManager::FindClosestPointOnRoad( const Road* pRoad,
+ const rmt::Vector& pos,
+ rmt::Vector& closestPos,
+ float& closestDistSqr,
+ int& segmentIndex )
+{
+ rAssert( pRoad );
+
+ rmt::Vector vec0, vec1, vec2, vec3;
+ rmt::Vector start, end;
+
+ // Take the destination intersection as the closest point so far
+ //
+ closestDistSqr = NEAR_INFINITY;
+ segmentIndex = -1;
+
+ for( int i = 0; i < (int)(pRoad->GetNumRoadSegments()); i++ )
+ {
+ RoadSegment* segment = pRoad->GetRoadSegment( i );
+ segment->GetCorner( 0, vec0 );
+ segment->GetCorner( 1, vec1 );
+ segment->GetCorner( 2, vec2 );
+ segment->GetCorner( 3, vec3 );
+
+ // Calc the midpoints at the start and end of the segment
+ //
+ start = (vec0+vec3) * 0.5f;
+ end = (vec1+vec2) * 0.5f;
+
+ // Calc the vector along the centre of the segment
+ //
+ rmt::Vector candidatePt;
+ FindClosestPointOnLine( start, end, pos, candidatePt );
+
+ // Calc vector between closest point on segment and target
+ //
+ float distSqr = (candidatePt - pos).MagnitudeSqr();
+ if( distSqr < closestDistSqr )
+ {
+ closestDistSqr = distSqr;
+ closestPos = candidatePt;
+ segmentIndex = i;
+ }
+ }
+
+ return true;
+}
+
+float RoadManager::DetermineRoadT( RoadSegment* seg, float segT )
+{
+ rAssert( seg );
+
+ unsigned int segIndex = seg->GetSegmentIndex();
+ Road* road = seg->GetRoad();
+ float roadT = 0.0f;
+
+ // determine progress "t" along the road
+ float segLen = 0.0f;
+ RoadSegment* prevSeg = NULL;
+ for( unsigned int j=0; j<segIndex; j++ )
+ {
+ prevSeg = road->GetRoadSegment( j );
+ segLen = prevSeg->GetSegmentLength();
+ roadT += segLen;
+ }
+ segLen = seg->GetSegmentLength();
+ roadT += segLen * segT;
+ roadT /= ((Road*)road)->GetRoadLength();
+
+ return roadT;
+}
+float RoadManager::DetermineSegmentT( const rmt::Vector& pos, RoadSegment* seg )
+{
+ rAssert( seg );
+
+ float segT = 0.0f;
+
+ // determine progress "t" along segment
+ rmt::Vector vec0, vec1, vec2, vec3;
+ rmt::Vector segStart, segEnd, closestPt;
+
+ seg->GetCorner( 0, vec0 );
+ seg->GetCorner( 1, vec1 );
+ seg->GetCorner( 2, vec2 );
+ seg->GetCorner( 3, vec3 );
+
+ segStart = (vec0 + vec3) * 0.5f;
+ segEnd = (vec1 + vec2) * 0.5f;
+
+ FindClosestPointOnLine( segStart, segEnd, pos, closestPt );
+
+ segT = GetLineSegmentT( segStart, segEnd, closestPt );
+ rAssert( 0.0f <= segT && segT <= 1.0f );
+ return segT;
+}
+
+
+//=============================================================================
+// RoadManager::CreateRoadNetwork
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void )
+//
+// Return: void
+//
+//=============================================================================
+void RoadManager::CreateRoadNetwork( void )
+{
+ for( unsigned int i = 0; i < mNumIntersectionsUsed; ++i )
+ {
+ // sort which ones are my IN & OUT roads, determine adjacency,
+ // determine if it's a big intersection (3 or more different adjacent
+ // intersections) and do some error checking
+ mIntersections[i].SortRoads();
+ mIntersections[i].mIndex = i;
+ }
+ PopulateConnectivityData( true, mIntersections, (int)(mNumIntersectionsUsed) );
+ PopulateConnectivityData( false, mIntersections, (int)(mNumIntersectionsUsed) );
+}
+
+
+void RoadManager::PopulateConnectivityData( bool useMultiplier, Intersection* intersections, int numInts )
+{
+ // nothing to do if there are no intersections
+ if( numInts <= 0 )
+ {
+ return;
+ }
+
+ //
+ // Create our temporary representation of each intersection:
+ // an array of Dijkstra nodes
+ //
+ SwapArray<DijkstraNode> nodes;
+
+ HeapMgr()->PushHeap(GMA_TEMP);
+ nodes.Allocate( numInts );
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+ nodes.mUseSize = numInts;
+
+ for( int i=0; i<numInts; i++ )
+ {
+ Intersection* in = &(intersections[i]);
+ nodes[i].in = in;
+ }
+
+ //
+ // Populate adjacency data for each Dijkstra node &
+ // store the cost for reachability
+ //
+ for( int i=0; i<numInts; i++ )
+ {
+ Intersection* in = nodes[i].in;
+ rAssert( in );
+
+ SwapArray<ShortestRoad>* shortestRoads = useMultiplier ?
+ &(in->mShortestRoadsToAdjacentIntersectionsWithMultiplier) :
+ &(in->mShortestRoadsToAdjacentIntersectionsNoMultiplier) ;
+
+ int numAdjacents = shortestRoads->mUseSize;
+ rAssert( numAdjacents > 0 );
+
+ HeapMgr()->PushHeap(GMA_TEMP);
+ nodes[i].adjacents.Allocate( numAdjacents );
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+ nodes[i].adjacents.mUseSize = numAdjacents;
+
+ int count = 0;
+
+ // for each adjacent intersection, fill in the corresponding
+ // dijkstra node pointer and distance
+ for( int j=0; j<shortestRoads->mUseSize; j++ )
+ {
+ ShortestRoad* sr = &((*shortestRoads)[j]);
+ rAssert( sr );
+ Road* road = sr->road;
+ rAssert( road );
+
+ Intersection* adjInt = NULL;
+ if( sr->isOutRoad )
+ {
+ adjInt = (Intersection*) road->GetDestinationIntersection();
+ }
+ else
+ {
+ adjInt = (Intersection*) road->GetSourceIntersection();
+ }
+ rAssert( adjInt );
+ rAssert( adjInt != in );
+
+ bool found = false;
+ for( int k=0; k<numInts; k++ )
+ {
+ if( adjInt == nodes[k].in )
+ {
+ found = true;
+ nodes[i].adjacents[count].adjacentNode = &(nodes[k]);
+ nodes[i].adjacents[count].shortestRoadThere = sr;
+ count++;
+ break;
+ }
+ }
+ rAssert( found );
+ }
+ rAssert( count == numAdjacents );
+ }
+
+ // foreach BIG intersection, populate its routing table
+ for( int i=0; i<numInts; i++ )
+ {
+ Intersection* src = &(intersections[i]);
+ if( !src->mBigIntersection )
+ {
+ continue;
+ }
+
+ SwapArray<NodeData>* routes = useMultiplier ?
+ &(src->mBigIntersection->routesWithMultiplier) :
+ &(src->mBigIntersection->routesNoMultiplier) ;
+
+ HeapMgr()->PushHeap(GMA_LEVEL_OTHER);
+ routes->Allocate( numInts );
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+
+ routes->mUseSize = numInts;
+
+ // initialize every Dijkstra node
+ for( int j=0; j<numInts; j++ )
+ {
+ nodes[j].Init( NEAR_INFINITY, NULL );
+ }
+ // initialize distance to source to zero
+ DijkstraNode* s = &(nodes[src->mIndex]);
+ s->distToSrc = 0.0f;
+
+ // determine best route to get from s to every other node
+ VisitAll( nodes );
+
+ //
+ // Populate routes:
+ // For each node pointer "a" at index "i" in array "nodes",
+ // follow predecessor pointers back to some node pointer "n"
+ // adjacent to src node, and set routes[i].destIn = n->in
+ // set routes[i].dist = a->distToSrc
+ //
+ for( int j=0; j<numInts; j++ )
+ {
+ DijkstraNode* a = &(nodes[j]);
+ rAssert( a->in->mIndex == j );
+ // if this node is the source node...
+ if( a == s )
+ {
+ (*routes)[j].destIn = NULL;
+ (*routes)[j].roadToIn = NULL;
+ (*routes)[j].roadJustBeforeIn = NULL;
+ }
+ else
+ {
+ SwapArray<ShortestRoad>* shortestRoads = NULL;
+
+ // find the last road just before reaching destIn
+ DijkstraNode* n = a;
+ rAssert( n );
+ rAssert( n->predecessor );
+
+ shortestRoads = useMultiplier ?
+ &(n->predecessor->in->mShortestRoadsToAdjacentIntersectionsWithMultiplier) :
+ &(n->predecessor->in->mShortestRoadsToAdjacentIntersectionsNoMultiplier) ;
+
+ ShortestRoad* roadToTake = NULL;
+ for( int k=0; k<shortestRoads->mUseSize; k++ )
+ {
+ ShortestRoad* candidate = &((*shortestRoads)[k]);
+ rAssert( candidate );
+
+ Intersection* candidateInt = NULL;
+ if( candidate->isOutRoad )
+ {
+ candidateInt = (Intersection*)
+ candidate->road->GetDestinationIntersection();
+ }
+ else
+ {
+ candidateInt = (Intersection*)
+ candidate->road->GetSourceIntersection();
+ }
+ if( candidateInt == n->in )
+ {
+ roadToTake = candidate;
+ break;
+ }
+ }
+ rAssert( roadToTake );
+ (*routes)[j].roadJustBeforeIn = roadToTake;
+
+ // find the road immediately leaving this big intersection
+ // that will put us on the correct path towards destIn
+
+ while( n->predecessor != s )
+ {
+ n = n->predecessor;
+ rAssert( n != NULL );
+ }
+ rAssert( n->in != NULL );
+ (*routes)[j].destIn = n->in;
+
+ // find the road that will take us to dest intersection
+ shortestRoads = useMultiplier ?
+ &(src->mShortestRoadsToAdjacentIntersectionsWithMultiplier) :
+ &(src->mShortestRoadsToAdjacentIntersectionsNoMultiplier) ;
+
+ roadToTake = NULL;
+ for( int k=0; k<shortestRoads->mUseSize; k++ )
+ {
+ ShortestRoad* candidate = &((*shortestRoads)[k]);
+ rAssert( candidate );
+
+ Intersection* candidateInt = NULL;
+ if( candidate->isOutRoad )
+ {
+ candidateInt = (Intersection*)
+ candidate->road->GetDestinationIntersection();
+ }
+ else
+ {
+ candidateInt = (Intersection*)
+ candidate->road->GetSourceIntersection();
+ }
+ if( candidateInt == n->in )
+ {
+ roadToTake = candidate;
+ break;
+ }
+ }
+ rAssert( roadToTake );
+ (*routes)[j].roadToIn = roadToTake;
+ }
+ (*routes)[j].dist = a->distToSrc;
+ }
+ }
+ nodes.Clear();
+}
+
+void RoadManager::VisitAll( SwapArray<DijkstraNode>& nodes )
+{
+ int numInts = nodes.mUseSize;
+ rAssert( numInts > 0 );
+
+ ///////////////////
+ // Dijkstra
+ ///////////////////
+
+ // Create "VS": a list of pointers to nodes that haven't been visited
+ SwapArray<DijkstraNode*> VS;
+
+ HeapMgr()->PushHeap(GMA_TEMP);
+ VS.Allocate( numInts );
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+ VS.mUseSize = numInts;
+ int i;
+ for( i=0; i<numInts; i++ )
+ {
+ VS[i] = &(nodes[i]);
+ }
+
+ // Create "S": a list of pointers to nodes that we have visited
+ // which is initially empty
+ SwapArray<DijkstraNode*> S;
+
+ HeapMgr()->PushHeap(GMA_TEMP);
+ S.Allocate( numInts );
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+ // Greedy-extract cheapest node one by one from VS and put in S
+ while( VS.mUseSize > 0 )
+ {
+ // Find the cheapest, remove from VS, put in S
+ int cheapest = -1;
+ float cheapestDist = NEAR_INFINITY;
+ DijkstraNode* u = NULL;
+
+ for( i=0; i<VS.mUseSize; i++ )
+ {
+ DijkstraNode* node = VS[i];
+ if( node->distToSrc < cheapestDist )
+ {
+ u = node;
+ cheapestDist = node->distToSrc;
+ cheapest = i;
+ }
+ }
+ rAssert( 0 <= cheapest && cheapest < VS.mUseSize );
+ S.Add( u ); // Dijkstra giveth
+ VS.Remove( cheapest ); // Dijkstra taketh away
+ u->addedToS = true;
+
+ // Relax the cost for all nodes adjacent to cheapestNode
+ for( int i=0; i<u->adjacents.mUseSize; i++ )
+ {
+ DijkstraNode* v = u->adjacents[i].adjacentNode;
+ rAssert( v );
+
+ if( v->addedToS ) // skip v is already in S
+ {
+ continue;
+ }
+
+ /////////////////////////////////////////////////////////////
+ // the cost is the length of the shortest road to the
+ // adjacent intersection PLUS the cost of traversal
+ // through intersection "u" from the road from our
+ // predecessor to this shortest road.
+
+ float adjacentDist = u->adjacents[i].shortestRoadThere->cost;
+ if( u->shortestRoadFromPred )
+ {
+ ShortestRoad* fromRoad = u->shortestRoadFromPred;
+ ShortestRoad* toRoad = u->adjacents[i].shortestRoadThere;
+ adjacentDist += GetTraversalDistance( fromRoad, toRoad );
+ }
+
+ if( v->distToSrc > (u->distToSrc + adjacentDist) )
+ {
+ v->distToSrc = u->distToSrc + adjacentDist;
+ v->predecessor = u;
+ v->shortestRoadFromPred = u->adjacents[i].shortestRoadThere;
+ }
+ }
+ }
+
+ VS.Clear();
+ S.Clear();
+}
+
+float RoadManager::FindPathElementsBetween(
+ bool useMultiplier,
+ PathElement& sourceElem,
+ float sourceT, // used only if sourceElem is a road
+ const rmt::Vector& sourcePos, // used only if sourceElem is an intersection
+ PathElement& targetElem,
+ float targetT, // used only if targetElem is a road
+ const rmt::Vector& targetPos, // used only if targetElem is an intersection
+ SwapArray<PathElement>& elems ) // accumulate roads
+{
+ rAssert( sourceElem.elem != NULL );
+ rAssert( targetElem.elem != NULL );
+ rAssert( 0.0f <= sourceT && sourceT <= 1.0f );
+ rAssert( elems.IsSetUp() );
+
+ float totalDist = 0.0f;
+
+ // for safety's sake... make this check...
+ // we need to make sure that the last element's type was
+ // not the same as sourceElem's type... this maintains the nice
+ // "...-int-road-int-road-int-..." series
+ if( elems.mUseSize > 0 )
+ {
+ rAssert( elems[ elems.mUseSize-1 ].type != sourceElem.type );
+ if( elems[ elems.mUseSize-1 ].type == sourceElem.type )
+ {
+ return totalDist;
+ }
+ }
+
+ //
+ // We will be returning a swaparray of PathElements that lie between
+ // the source and the target PathElements. A PathElement can be a
+ // shortest road or an intersection.
+ //
+
+ // the first thing we add should be the source road!
+ if( sourceElem.type == ET_NORMALROAD )
+ {
+ elems.Add( sourceElem );
+
+ // if it's a shortcut, and target road isn't this road,
+ // call this function again with the destination intersection..
+ Road* srcRd = (Road*) sourceElem.elem;
+ if( srcRd->GetShortCut() && sourceElem != targetElem )
+ {
+ Intersection* srcInt = (Intersection*) srcRd->GetDestinationIntersection();
+
+ rmt::Vector intPos;
+ srcInt->GetLocation( intPos );
+
+ float distToDestInt = (1.0f - sourceT) * srcRd->GetRoadLength();
+
+ rmt::Vector vec1,vec2,endOfRdPos;
+ RoadSegment* endOfRdSeg = srcRd->GetRoadSegment( srcRd->GetNumRoadSegments()-1 );
+ endOfRdSeg->GetCorner( 1, vec1 );
+ endOfRdSeg->GetCorner( 2, vec2 );
+ endOfRdPos = (vec1+vec2) * 0.5f;
+
+ distToDestInt += (endOfRdPos - intPos).Magnitude(); // *** SQUARE ROOT! ***
+
+
+ /*
+ // TODO:
+ // This is a big problem. We're CHANGING sourceElem here... which
+ // changes the value for whoever passed in sourceElem (cuz sourceElem
+ // was passed in by reference .. BIATCH!)... Somehow things have been
+ // miraculously working, I'm not sure why.. Basically the only case
+ // where this bug applies is when the somebody (e.g. the avater, the AI,
+ // the HUD map, light path, etc.) is on a shortcut and
+ // he's pathfinding to some target. The sourceElem will be set to the
+ // destination intersection of that shortcut, so the Avatar and Ai's
+ // mLastPathElement members will be inaccurate... Aiya!
+ // Too late to change this at this point. Just leave it unless the
+ // problems are OVERWHELMINGLY bad...
+ //
+ PathElement tmpElem;
+ tmpElem.type = ET_INTERSECTION;
+ tmpElem.elem = srcInt;
+ */
+ sourceElem.type = ET_INTERSECTION;
+ sourceElem.elem = srcInt;
+
+ SwapArray<PathElement> tmpElems;
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+ tmpElems.Allocate( elems.mSize );
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ totalDist += distToDestInt + FindPathElementsBetween( useMultiplier,
+ sourceElem, 0.0f, intPos, targetElem, targetT, targetPos, tmpElems );
+
+ for( int i=0; i<tmpElems.mUseSize; i++ )
+ {
+ elems.Add( tmpElems[i] );
+ }
+ tmpElems.Clear();
+
+ return totalDist;
+ }
+ }
+
+
+ // if source and target are the same, then return dist between them!
+ if( sourceElem == targetElem )
+ {
+ // if road, use T values to compute dist betw them
+ if( sourceElem.type == ET_NORMALROAD )
+ {
+ // TODO:
+ // Can't just happily return here... Not right to just
+ // compute the totalDist this way either...
+ // What if it's closer to go the other way (i.e. not along the
+ // given road, but via another road)? Hmm??
+ // Careful... Fixing this might change behavior below
+ // where some asserts assume that we have returned at this point...
+ //
+ // Here's the problem in detail:
+ //
+ // One of the other nuances with the pathfinding algo was quite
+ // evident in SuperSprint before we hacked the data to never produce
+ // the problem.. The bug still exists in code, however, but I haven't
+ // really seen it expressed anywhere else in the world.
+ //
+ // The algorithm "tries to be smart" and says, "Hey, if S is on the
+ // same road as T, then the distance between them must be just the
+ // distance along the road between S and T"... This is not necessarily
+ // the case.
+ //
+ // For example, take a particularly long road, with S and T near
+ // either ends of the road... But the road's either ends are connected
+ // via another road...like so (A and B are intersections):
+ //
+ // -<--S--A--<--B--T--<-
+ // | |
+ // | |
+ // ---->---------->-----
+ //
+ // In this case, it's actually closer to get from S to T via
+ // intersection A, then to B, then to T... but the algorithm returns
+ // the closest distance to be along the road from S to T
+ //
+
+ Road* someRoad = (Road*)sourceElem.elem;
+ float someRoadLen = someRoad->GetRoadLength();
+ float sourceProgress = someRoadLen * sourceT;
+ float targetProgress = someRoadLen * targetT;
+ totalDist += rmt::Fabs(sourceProgress - targetProgress);
+
+ return totalDist;
+ }
+ else // both in same intersection
+ {
+ totalDist += (sourcePos - targetPos).Length(); // *** SQUARE ROOT! ***
+ elems.Add( targetElem );
+ return totalDist;
+ }
+ }
+ else if( sourceElem.type == ET_NORMALROAD && targetElem.type == ET_NORMALROAD )
+ {
+ // figure out if the source is actually a road on the opposite lane..
+ // if so, ignore it if we're still close enough to the previous segment.
+ // This is to fix the problem where you're on the same physical road as
+ // your target, but you're on the incoming road and it's on the outgoing
+ // road, so you're actually doing unnecessary pathfinding around your
+ // own road to get to the correct road... stupid...
+ //
+ // Example:
+ // <--------------- you -----------------------------------
+ // -------AI ---------------------------- checkpoint ----->
+ //
+ // Here you are physically closer, but you're ranked second, behind the AI
+ // because you have to take extra pathfinding steps around your own road,
+ // to reach either intersections before getting on the correct road. Blegh.
+ //
+
+ Road* srcRoad = (Road*)sourceElem.elem;
+ Road* targRoad = (Road*)targetElem.elem;
+
+ rAssert( srcRoad != targRoad );
+
+
+ const Intersection* srcSrcInt = srcRoad->GetSourceIntersection();
+ const Intersection* srcDestInt = srcRoad->GetDestinationIntersection();
+ const Intersection* targSrcInt = targRoad->GetSourceIntersection();
+ const Intersection* targDestInt = targRoad->GetDestinationIntersection();
+
+ if( srcSrcInt == targDestInt && srcDestInt == targSrcInt )
+ {
+ // whoa, these roads are connecting the same intersections!
+ // figure out how far we really are from last road...
+
+ rmt::Vector closestPtOnSrc;
+ float distFromClosestPtOnSrcSqr = NEAR_INFINITY;
+ int closestSrcSegIndex = -1;
+
+ FindClosestPointOnRoad( srcRoad, sourcePos, closestPtOnSrc,
+ distFromClosestPtOnSrcSqr, closestSrcSegIndex );
+
+ rmt::Vector closestPtOnTarg;
+ float distFromClosestPtOnTargSqr = NEAR_INFINITY;
+ int closestTargSegIndex = -1;
+
+ FindClosestPointOnRoad( targRoad, closestPtOnSrc, closestPtOnTarg,
+ distFromClosestPtOnTargSqr, closestTargSegIndex );
+ /*
+ FindClosestPointOnRoad( targRoad, sourcePos, closestPtOnTarg,
+ distFromClosestPtOnTargSqr, closestTargSegIndex );
+ */
+
+ rAssert( closestTargSegIndex != -1 );
+
+ // if within 10 meters from road center, source and target roads
+ // must be part of the same road...
+ const float CLOSE_ENOUGH_TO_BE_ADJACENT_ROAD_SQR = 100.0f;
+
+ if( distFromClosestPtOnTargSqr < CLOSE_ENOUGH_TO_BE_ADJACENT_ROAD_SQR )
+ {
+ // TODO:
+ // Can't just happily return here... Not right to just
+ // compute the totalDist this way either...
+ // What if it's closer to go the other way (i.e. not along the
+ // given road, but via another road)? Hmm??
+ // Careful... Fixing this might change behavior below
+ // where some asserts assume that we have returned at this point...
+ //
+ // Here's the problem in detail:
+ //
+ // One of the other nuances with the pathfinding algo was quite
+ // evident in SuperSprint before we hacked the data to never produce
+ // the problem.. The bug still exists in code, however, but I haven't
+ // really seen it expressed anywhere else in the world.
+ //
+ // The algorithm "tries to be smart" and says, "Hey, if S is on the
+ // same road as T, then the distance between them must be just the
+ // distance along the road between S and T"... This is not necessarily
+ // the case.
+ //
+ // For example, take a particularly long road, with S and T near
+ // either ends of the road... But the road's either ends are connected
+ // via another road...like so (A and B are intersections):
+ //
+ // -<--S--A--<--B--T--<-
+ // | |
+ // | |
+ // ---->---------->-----
+ //
+ // In this case, it's actually closer to get from S to T via
+ // intersection A, then to B, then to T... but the algorithm returns
+ // the closest distance to be along the road from S to T
+ //
+ RoadSegment* closestTargSeg = (RoadSegment*) targRoad->GetRoadSegment(
+ (unsigned int)closestTargSegIndex );
+
+ float closestTargSegT = RoadManager::DetermineSegmentT( closestPtOnTarg, closestTargSeg );
+ float closestTargRoadT = RoadManager::DetermineRoadT( closestTargSeg, closestTargSegT );
+
+ float targRoadLen = targRoad->GetRoadLength();
+ float sourceProgress = targRoadLen * closestTargRoadT;
+ float targetProgress = targRoadLen * targetT;
+ totalDist += rmt::Fabs(sourceProgress - targetProgress);
+
+ elems.Remove( elems.mUseSize - 1 );
+ elems.Add( targetElem );
+
+ return totalDist;
+ }
+ }
+ }
+
+
+
+
+ // determine what we know about the target
+ float distFromTargetToInt = 0.0f;
+ float distFromTargetToOtherInt = 0.0f;
+ Intersection* targetInt = NULL; // the one we're heading towards
+ Intersection* targetOtherInt = NULL; // if targetElem is a road, it's the other intersection
+ ShortestRoad targetShortRoad;
+ targetShortRoad.cost = NEAR_INFINITY;
+
+ if( targetElem.type == ET_INTERSECTION )
+ {
+ targetInt = (Intersection*) targetElem.elem;
+ targetShortRoad.road = NULL;
+ }
+ else
+ {
+ rAssert( targetElem.type == ET_NORMALROAD );
+
+ Road* targetRoad = (Road*) targetElem.elem;
+ targetShortRoad.road = targetRoad;
+
+ targetInt = (Intersection*) targetRoad->GetSourceIntersection();
+ distFromTargetToInt = targetT * targetRoad->GetRoadLength();
+
+ if( !targetRoad->GetShortCut() )
+ {
+ targetOtherInt = (Intersection*) targetRoad->GetDestinationIntersection();
+ // if we're getting to target via dest int, we're going against traffic
+ // so use the multiplier
+ distFromTargetToOtherInt = (1.0f - targetT) * targetRoad->GetRoadLength() *
+ ((useMultiplier)? AGAINST_TRAFFIC_COST_MULTIPLIER : 1.0f);
+ }
+ else
+ {
+ targetOtherInt = NULL;
+ distFromTargetToOtherInt = NEAR_INFINITY;
+ }
+
+
+ }
+ // at least targetInt should exist...
+ rAssert( targetInt );
+
+ bool goingToTargetOther = false;
+
+ ShortestRoad* firstShortRoad = NULL;
+ ShortestRoad* lastShortRoad = NULL;
+ //////////////////////////////////////////////////////////////////////////////////
+
+
+ // deal with the simplest case first...
+ bool foundSimpleCase = false;
+ if( sourceElem.type == ET_INTERSECTION )
+ {
+ // if I'm a big intersection or if I only got one way to go...
+ // just call TraverseRoads...
+ Intersection* sourceInt = (Intersection*) sourceElem.elem;
+ SwapArray<ShortestRoad>* shortestRoads = useMultiplier ?
+ &(sourceInt->mShortestRoadsToAdjacentIntersectionsWithMultiplier) :
+ &(sourceInt->mShortestRoadsToAdjacentIntersectionsNoMultiplier) ;
+
+ if( sourceInt->mBigIntersection || shortestRoads->mUseSize == 1 )
+ {
+ foundSimpleCase = true;
+
+ SwapArray<PathElement> elemsToTargetInt, elemsToTargetOtherInt;
+
+ HeapMgr()->PushHeap(GMA_TEMP);
+ elemsToTargetInt.Allocate( elems.mSize );
+ elemsToTargetOtherInt.Allocate( elems.mSize );
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+ ErrorValue errVal;
+
+ float distViaTargetInt = NEAR_INFINITY;
+ float distViaTargetOtherInt = NEAR_INFINITY;
+
+ firstShortRoad = NULL;
+ lastShortRoad = NULL;
+
+ // PATH: sourceInt -> ... -> targetRoad's srcInt -> targetRoad
+ distViaTargetInt = FindDistToTargetInOneDirection( useMultiplier,
+ targetInt, sourceInt, sourceInt, NULL, elemsToTargetInt,
+ firstShortRoad, lastShortRoad, errVal ) +
+ distFromTargetToInt;
+ rAssert( errVal == FOUND_BIGINTERSECTION || errVal == FOUND_TARGET );
+ if( targetElem.type == ET_NORMALROAD )
+ {
+ if( lastShortRoad )
+ {
+ targetShortRoad.isOutRoad = true; // at target road's source intersection, the target road is an OUT road
+ distViaTargetInt += GetTraversalDistance( lastShortRoad, &targetShortRoad );
+ }
+ }
+
+ if( targetOtherInt )
+ {
+ firstShortRoad = NULL;
+ lastShortRoad = NULL;
+
+ // PATH: sourceInt -> ... -> targetRoad's destInt -> targetRoad
+ distViaTargetOtherInt = FindDistToTargetInOneDirection( useMultiplier,
+ targetOtherInt, sourceInt, sourceInt, NULL, elemsToTargetOtherInt,
+ firstShortRoad, lastShortRoad, errVal ) +
+ distFromTargetToOtherInt;
+ rAssert( errVal == FOUND_BIGINTERSECTION || errVal == FOUND_TARGET );
+ if( targetElem.type == ET_NORMALROAD )
+ {
+ if( lastShortRoad )
+ {
+ targetShortRoad.isOutRoad = false; // at target road's dest intersection, the target road is an OUT road
+ distViaTargetOtherInt += GetTraversalDistance( lastShortRoad, &targetShortRoad );
+ }
+ }
+
+ }
+
+ if( distViaTargetInt < distViaTargetOtherInt )
+ {
+ // don't need to copy over the roads, we're visiting sourceInt
+ // again
+ TraverseRoads( useMultiplier, targetInt, sourceInt, sourceInt, elems, errVal );
+ totalDist += distViaTargetInt;
+ }
+ else
+ {
+ rAssert( targetOtherInt );
+ // don't need to copy over the roads, we're visiting sourceInt
+ // again
+ goingToTargetOther = true;
+ TraverseRoads( useMultiplier, targetOtherInt, sourceInt, sourceInt, elems, errVal );
+ totalDist += distViaTargetOtherInt;
+ }
+ rAssert( errVal == FOUND_TARGET );
+ }
+ }
+
+ //
+ // Well we either got source being a road or a small intersection with
+ // 2 adjacent neighbors. Either way, we have to traverse in both directions...
+ //
+ if( !foundSimpleCase )
+ {
+ float distToTargetIntInOneDir = NEAR_INFINITY;
+ float distToTargetIntInOtherDir = NEAR_INFINITY;
+ float distToTargetOtherIntInOneDir = NEAR_INFINITY;
+ float distToTargetOtherIntInOtherDir = NEAR_INFINITY;
+
+ SwapArray<PathElement> elemsToTargetIntInOneDir;
+ SwapArray<PathElement> elemsToTargetIntInOtherDir;
+
+ HeapMgr()->PushHeap(GMA_TEMP);
+ elemsToTargetIntInOneDir.Allocate( elems.mSize );
+ elemsToTargetIntInOtherDir.Allocate( elems.mSize );
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+ SwapArray<PathElement> elemsToTargetOtherIntInOneDir;
+ SwapArray<PathElement> elemsToTargetOtherIntInOtherDir;
+ if( targetOtherInt )
+ {
+ HeapMgr()->PushHeap(GMA_TEMP);
+ elemsToTargetOtherIntInOneDir.Allocate( elems.mSize );
+ elemsToTargetOtherIntInOtherDir.Allocate( elems.mSize );
+ HeapMgr()->PopHeap(GMA_TEMP);
+ }
+ ErrorValue targetIntOneErr = DEAD_END;
+ ErrorValue targetIntOtherErr = DEAD_END;
+ ErrorValue targetOtherIntOneErr = DEAD_END;
+ ErrorValue targetOtherIntOtherErr = DEAD_END;
+
+ if( sourceElem.type == ET_NORMALROAD )
+ {
+ // Explore in both directions along our source road:
+ // - head toward the source intersection and keep going
+ // - head toward the dest intersection and keep going
+ //
+ Road* sourceRoad = (Road*) sourceElem.elem;
+
+ float distToSrcInt, distToDestInt;
+ distToSrcInt = sourceT * sourceRoad->GetRoadLength() *
+ ((useMultiplier)? AGAINST_TRAFFIC_COST_MULTIPLIER : 1.0f);
+ distToDestInt = (1.0f - sourceT) * sourceRoad->GetRoadLength();
+
+ Intersection* sourceSrcInt = (Intersection*) sourceRoad->GetSourceIntersection();
+ Intersection* sourceDestInt = (Intersection*) sourceRoad->GetDestinationIntersection();
+
+ ShortestRoad sourceShortRoad;
+ sourceShortRoad.road = sourceRoad;
+ sourceShortRoad.cost = NEAR_INFINITY;
+
+ if( !sourceRoad->GetShortCut() ) // shortcuts only go one way!!
+ {
+ firstShortRoad = NULL;
+ lastShortRoad = &sourceShortRoad;
+
+ // PATH: sourceRoad -> sourceRoad's srcInt -> ... -> targetRoad's srcInt -> targetRoad
+ sourceShortRoad.isOutRoad = false;
+ distToTargetIntInOneDir = FindDistToTargetInOneDirection( useMultiplier,
+ targetInt, sourceSrcInt, sourceDestInt, NULL, elemsToTargetIntInOneDir,
+ firstShortRoad, lastShortRoad, targetIntOneErr ) + distToSrcInt + distFromTargetToInt;
+ if( firstShortRoad && targetIntOneErr != DEAD_END )
+ {
+ distToTargetIntInOneDir += GetTraversalDistance( &sourceShortRoad, firstShortRoad );
+ }
+ if( targetElem.type == ET_NORMALROAD && targetIntOneErr != DEAD_END )
+ {
+ rAssert( lastShortRoad );
+ targetShortRoad.isOutRoad = true;
+ distToTargetIntInOneDir += GetTraversalDistance( lastShortRoad, &targetShortRoad );
+ }
+
+ }
+
+ firstShortRoad = NULL;
+ lastShortRoad = &sourceShortRoad;
+
+ // PATH: sourceRoad -> sourceRoad's destInt -> ... -> targetRoad's srcInt -> targetRoad
+ sourceShortRoad.isOutRoad = true;
+ distToTargetIntInOtherDir = FindDistToTargetInOneDirection( useMultiplier,
+ targetInt, sourceDestInt, sourceSrcInt, NULL, elemsToTargetIntInOtherDir,
+ firstShortRoad, lastShortRoad, targetIntOtherErr ) + distToDestInt + distFromTargetToInt;
+ if( firstShortRoad && targetIntOtherErr != DEAD_END )
+ {
+ distToTargetIntInOtherDir += GetTraversalDistance( &sourceShortRoad, firstShortRoad );
+ }
+ if( targetElem.type == ET_NORMALROAD && targetIntOtherErr != DEAD_END )
+ {
+ rAssert( lastShortRoad );
+ targetShortRoad.isOutRoad = true;
+ distToTargetIntInOtherDir += GetTraversalDistance( lastShortRoad, &targetShortRoad );
+ }
+
+ if( targetOtherInt )
+ {
+ if( !sourceRoad->GetShortCut() ) // shortcuts only go one way!!
+ {
+ firstShortRoad = NULL;
+ lastShortRoad = &sourceShortRoad;
+
+ // PATH: sourceRoad -> sourceRoad's srcInt -> ... -> targetRoad's destInt -> targetRoad
+ sourceShortRoad.isOutRoad = false;
+ distToTargetOtherIntInOneDir = FindDistToTargetInOneDirection( useMultiplier,
+ targetOtherInt, sourceSrcInt, sourceDestInt, NULL, elemsToTargetOtherIntInOneDir,
+ firstShortRoad, lastShortRoad, targetOtherIntOneErr ) +
+ distToSrcInt + distFromTargetToOtherInt;
+ if( firstShortRoad && targetOtherIntOneErr != DEAD_END )
+ {
+ distToTargetOtherIntInOneDir += GetTraversalDistance( &sourceShortRoad, firstShortRoad );
+ }
+ if( targetElem.type == ET_NORMALROAD && targetOtherIntOneErr != DEAD_END )
+ {
+ rAssert( lastShortRoad );
+ targetShortRoad.isOutRoad = false;
+ distToTargetOtherIntInOneDir += GetTraversalDistance( lastShortRoad, &targetShortRoad );
+ }
+ }
+
+ firstShortRoad = NULL;
+ lastShortRoad = &sourceShortRoad;
+
+ // PATH: sourceRoad -> sourceRoad's destInt -> ... -> targetRoad's destInt -> targetRoad
+ sourceShortRoad.isOutRoad = true;
+ distToTargetOtherIntInOtherDir = FindDistToTargetInOneDirection( useMultiplier,
+ targetOtherInt, sourceDestInt, sourceSrcInt, NULL, elemsToTargetOtherIntInOtherDir,
+ firstShortRoad, lastShortRoad, targetOtherIntOtherErr ) +
+ distToDestInt + distFromTargetToOtherInt;
+ if( firstShortRoad && targetOtherIntOtherErr != DEAD_END )
+ {
+ distToTargetOtherIntInOtherDir += GetTraversalDistance( &sourceShortRoad, firstShortRoad );
+ }
+ if( targetElem.type == ET_NORMALROAD && targetOtherIntOtherErr != DEAD_END )
+ {
+ rAssert( lastShortRoad );
+ targetShortRoad.isOutRoad = false;
+ distToTargetOtherIntInOtherDir += GetTraversalDistance( lastShortRoad, &targetShortRoad );
+ }
+ }
+ }
+ else
+ {
+ // I'm a small intersection with 2 adjacent neighbor intersections
+ // Visit both intersections on either sides of me...
+ rAssert( sourceElem.type == ET_INTERSECTION );
+
+ Intersection* sourceInt = (Intersection*) sourceElem.elem;
+ SwapArray<ShortestRoad>* shortestRoads = useMultiplier ?
+ &(sourceInt->mShortestRoadsToAdjacentIntersectionsWithMultiplier) :
+ &(sourceInt->mShortestRoadsToAdjacentIntersectionsNoMultiplier) ;
+
+ rAssert( shortestRoads->mUseSize == 2 );
+
+ // visit neighborB by feeding neighborA in as last intersection
+ Intersection* neighborA = NULL;
+ ShortestRoad* shortRoad = &((*shortestRoads)[0]);
+ if( shortRoad->isOutRoad )
+ {
+ neighborA = (Intersection*) shortRoad->road->GetDestinationIntersection();
+ }
+ else
+ {
+ neighborA = (Intersection*) shortRoad->road->GetSourceIntersection();
+ }
+
+ // now visit neighborA by feeding neighborB in as last intersection
+ Intersection* neighborB = NULL;
+ sourceInt->GetOtherIntersection( useMultiplier, neighborA, neighborB, shortRoad );
+
+ firstShortRoad = NULL;
+ lastShortRoad = NULL;
+
+ // PATH: sourceInt -> neighborB -> ... -> targetRoad's srcInt -> targetRoad
+ distToTargetIntInOneDir = FindDistToTargetInOneDirection( useMultiplier,
+ targetInt, sourceInt, neighborA, NULL, elemsToTargetIntInOneDir,
+ firstShortRoad, lastShortRoad, targetIntOneErr ) + distFromTargetToInt;
+ if( targetElem.type == ET_NORMALROAD && targetIntOneErr != DEAD_END )
+ {
+ if( lastShortRoad )
+ {
+ targetShortRoad.isOutRoad = true;
+ distToTargetIntInOneDir += GetTraversalDistance( lastShortRoad, &targetShortRoad );
+ }
+ }
+
+
+ firstShortRoad = NULL;
+ lastShortRoad = NULL;
+
+ // PATH: sourceInt -> neighborA -> ... -> targetRoad's srcInt -> targetRoad
+ distToTargetIntInOtherDir = FindDistToTargetInOneDirection( useMultiplier,
+ targetInt, sourceInt, neighborB, NULL, elemsToTargetIntInOtherDir,
+ firstShortRoad, lastShortRoad, targetIntOtherErr ) + distFromTargetToInt;
+ if( targetElem.type == ET_NORMALROAD && targetIntOtherErr != DEAD_END )
+ {
+ if( lastShortRoad )
+ {
+ targetShortRoad.isOutRoad = true;
+ distToTargetIntInOtherDir += GetTraversalDistance( lastShortRoad, &targetShortRoad );
+ }
+ }
+
+
+ // now do the other 2 alternatives...
+ if( targetOtherInt )
+ {
+ firstShortRoad = NULL;
+ lastShortRoad = NULL;
+
+ // PATH: sourceInt -> neighborB -> ... -> targetRoad's destInt -> targetRoad
+ distToTargetOtherIntInOneDir = FindDistToTargetInOneDirection( useMultiplier,
+ targetOtherInt, sourceInt, neighborA, NULL, elemsToTargetOtherIntInOneDir,
+ firstShortRoad, lastShortRoad, targetOtherIntOneErr ) + distFromTargetToOtherInt;
+ if( targetElem.type == ET_NORMALROAD && targetOtherIntOneErr != DEAD_END )
+ {
+ if( lastShortRoad )
+ {
+ targetShortRoad.isOutRoad = false;
+ distToTargetOtherIntInOneDir += GetTraversalDistance( lastShortRoad, &targetShortRoad );
+ }
+ }
+
+
+ firstShortRoad = NULL;
+ lastShortRoad = NULL;
+
+ // PATH: sourceInt -> neighborA -> ... -> targetRoad's destInt -> targetRoad
+ distToTargetOtherIntInOtherDir = FindDistToTargetInOneDirection( useMultiplier,
+ targetOtherInt, sourceInt, neighborB, NULL, elemsToTargetOtherIntInOtherDir,
+ firstShortRoad, lastShortRoad, targetOtherIntOtherErr ) + distFromTargetToOtherInt;
+ if( targetElem.type == ET_NORMALROAD && targetOtherIntOtherErr != DEAD_END )
+ {
+ if( lastShortRoad )
+ {
+ targetShortRoad.isOutRoad = false;
+ distToTargetOtherIntInOtherDir += GetTraversalDistance( lastShortRoad, &targetShortRoad );
+ }
+ }
+
+ }
+ }
+
+ //
+ // One or all of these ways should make it to either a big intersection
+ // (where we decide whether or not the total cost to the target road
+ // is less in this direction than if we were to go in the other
+ // directions) or the target road itself (where we consider total dist
+ // to the target in this dir versus total dist to target in other dirs)
+ //
+ // shouldn't have found dead end in all directions!
+ rAssert( targetIntOneErr != DEAD_END ||
+ targetIntOtherErr != DEAD_END ||
+ targetOtherIntOneErr != DEAD_END ||
+ targetOtherIntOtherErr != DEAD_END );
+
+ SwapArray<DistErrMap> mappings;
+
+ HeapMgr()->PushHeap(GMA_TEMP);
+ mappings.Allocate( 4 );
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+ mappings.mUseSize = 4;
+
+ mappings[0].dist = distToTargetIntInOneDir;
+ mappings[0].errVal = targetIntOneErr;
+ mappings[0].ID = 0;
+
+ mappings[1].dist = distToTargetIntInOtherDir;
+ mappings[1].errVal = targetIntOtherErr;
+ mappings[1].ID = 1;
+
+ mappings[2].dist = distToTargetOtherIntInOneDir;
+ mappings[2].errVal = targetOtherIntOneErr;
+ mappings[2].ID = 2;
+
+ mappings[3].dist = distToTargetOtherIntInOtherDir;
+ mappings[3].errVal = targetOtherIntOtherErr;
+ mappings[3].ID = 3;
+
+ int i;
+ // get rid of all the ones that never panned out
+ for( i=0; i<mappings.mUseSize; )
+ {
+ rAssert( mappings[i].errVal != STILL_LOOKING &&
+ mappings[i].errVal != UNEXPECTED );
+
+ if( mappings[i].errVal == DEAD_END )
+ {
+ mappings.Remove(i);
+ }
+ else
+ {
+ i++;
+ }
+ }
+ rAssert( mappings.mUseSize >= 1 );
+
+ // of whatever's left, we find the smallest dist...
+ int minIndex = -1;
+ float minDist = NEAR_INFINITY;
+ for( i=0; i<mappings.mUseSize; i++ )
+ {
+ if( mappings[i].dist < minDist )
+ {
+ minDist = mappings[i].dist;
+ minIndex = i;
+ }
+ }
+ rAssert( minIndex != -1 );
+ rAssert( minDist < NEAR_INFINITY );
+
+ ErrorValue errValToUse = mappings[minIndex].errVal;
+
+ totalDist += mappings[minIndex].dist;
+
+ switch( mappings[minIndex].ID )
+ {
+ case 0:
+ {
+ // copy over the roads
+ for( int i=0; i<elemsToTargetIntInOneDir.mUseSize; i++ )
+ {
+ elems.Add( elemsToTargetIntInOneDir[i] );
+ }
+ }
+ break;
+ case 1:
+ {
+ // copy over the roads
+ for( int i=0; i<elemsToTargetIntInOtherDir.mUseSize; i++ )
+ {
+ elems.Add( elemsToTargetIntInOtherDir[i] );
+ }
+ }
+ break;
+ case 2:
+ {
+ // copy over the roads
+ rAssert( elemsToTargetOtherIntInOneDir.IsSetUp() );
+ for( int i=0; i<elemsToTargetOtherIntInOneDir.mUseSize; i++ )
+ {
+ elems.Add( elemsToTargetOtherIntInOneDir[i] );
+ }
+ goingToTargetOther = true;
+ }
+ break;
+ case 3:
+ {
+ // copy over the roads
+ rAssert( elemsToTargetOtherIntInOtherDir.IsSetUp() );
+ for( int i=0; i<elemsToTargetOtherIntInOtherDir.mUseSize; i++ )
+ {
+ elems.Add( elemsToTargetOtherIntInOtherDir[i] );
+ }
+ goingToTargetOther = true;
+ }
+ break;
+ default:
+ {
+ rAssert( false );
+ }
+ break;
+ }
+
+ // clean out the temporary structures
+ elemsToTargetIntInOneDir.Clear();
+ elemsToTargetIntInOtherDir.Clear();
+ elemsToTargetOtherIntInOneDir.Clear();
+ elemsToTargetOtherIntInOtherDir.Clear();
+
+ // our choice in the error value to use (implies choice in direction of search)
+ // will take us to either the target or a big intersection, along the shortest
+ // path to the target
+ switch( errValToUse )
+ {
+ case FOUND_TARGET:
+ {
+ // We found targetInt before running into a big intersection.
+ // Just move on...
+ }
+ break;
+ case FOUND_BIGINTERSECTION:
+ {
+ rAssert( elems[elems.mUseSize-1].type == ET_INTERSECTION );
+
+ Intersection* bigInt = (Intersection*) elems[elems.mUseSize-1].elem;
+ rAssert( bigInt->mBigIntersection );
+
+ // pop it off the array, since TraverseRoads will add it again
+ // on first visit
+ elems.Remove( elems.mUseSize-1 );
+
+ // call TraverseRoads on it to fill the elems list with path elements
+ ErrorValue returnErr;
+ if( goingToTargetOther )
+ {
+ rAssert( targetOtherInt );
+ TraverseRoads( useMultiplier, targetOtherInt, bigInt, bigInt, elems, returnErr );
+ }
+ else
+ {
+ TraverseRoads( useMultiplier, targetInt, bigInt, bigInt, elems, returnErr );
+ }
+ rAssert( returnErr == FOUND_TARGET );
+ }
+ break;
+ case DEAD_END: // fall thru... the errVal we're using shouldn't lead to dead end
+ case STILL_LOOKING: // fall thru... only a tmp state for use before reaching end condition!
+ case UNEXPECTED: // fall thru
+ default:
+ {
+ // Shouldn't be here. We should have found either the target
+ // intersection (no more path finding necessary), or a big
+ // intersection.
+ rAssert( false );
+ }
+ break;
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////
+ //
+ // Deal with adding the target element, if necessary...
+ //
+ // Given that source and target are not identical, at the very least,
+ // sourceElem (first thing we added) and targetInt (the last thing
+ // we should have found before getting to this point) exist in the list.
+ // But they could be the same element!
+ //
+#ifdef RAD_DEBUG
+ rAssert( elems.mUseSize >= 1 );
+ rAssert( elems[0] == sourceElem );
+ rAssert( elems[elems.mUseSize-1].type == ET_INTERSECTION );
+ if( goingToTargetOther )
+ {
+ rAssert( targetOtherInt );
+ rAssert( ((Intersection*)elems[elems.mUseSize-1].elem) == targetOtherInt );
+ }
+ else
+ {
+ rAssert( ((Intersection*)elems[elems.mUseSize-1].elem) == targetInt );
+ }
+#endif
+
+
+ // If targetElem is an intersection, then we're already done, because
+ // targetInt is already the last element in the list
+ if( targetElem.type == ET_INTERSECTION )
+ {
+ // Because of our earlier RETURN for when src == target, we know:
+ // - we have at least 2 elems, targetElem (this intersection) and source
+ rAssert( elems.mUseSize >= 2 );
+ rAssert( targetElem == elems[elems.mUseSize-1] );
+ rAssert( elems[elems.mUseSize-1].type == ET_INTERSECTION );
+ rAssert( elems[elems.mUseSize-2].type == ET_NORMALROAD );
+
+ // the intersection
+ Intersection* targetElemInt = (Intersection*)targetElem.elem;
+
+ // add dist from endpoint of last road to targetPos
+ rmt::Vector lastRoadEndPos, vec0, vec1, vec2, vec3;
+ Road* lastRoad = (Road*)elems[elems.mUseSize-2].elem;
+ RoadSegment* lastRoadSeg = NULL;
+ if( lastRoad->GetDestinationIntersection() == targetElemInt )
+ {
+ lastRoadSeg = lastRoad->GetRoadSegment( lastRoad->GetNumRoadSegments()-1 );
+
+ lastRoadSeg->GetCorner( 1, vec1 );
+ lastRoadSeg->GetCorner( 2, vec2 );
+ lastRoadEndPos = (vec1 + vec2) * 0.5f;
+ }
+ else
+ {
+ rAssert( lastRoad->GetSourceIntersection() == targetElemInt );
+
+ lastRoadSeg = lastRoad->GetRoadSegment( 0 );
+
+ lastRoadSeg->GetCorner( 0, vec0 );
+ lastRoadSeg->GetCorner( 3, vec3 );
+ lastRoadEndPos = (vec0 + vec3) * 0.5f;
+ }
+ totalDist += (lastRoadEndPos - targetPos).Magnitude(); // *** SQUARE ROOT! ***
+ }
+ else
+ {
+ // ok, target is a road... gotta add it
+ #ifdef RAD_DEBUG
+ rAssert( targetElem.type == ET_NORMALROAD );
+ if( ((Road*)(targetElem.elem))->GetShortCut() )
+ {
+ // we don't consider approaching target from its destination intersection
+ // when the target is on a shortcut road because a shortcut is one-way.
+ rAssert( targetOtherInt == NULL );
+ }
+ else
+ {
+ // if not a shortcut, of course we have to consider approaching the
+ // target from the target road's destination intersection
+ rAssert( targetOtherInt );
+ }
+ #endif
+
+ bool needToAddTargetRoad = true;
+ // well do some more asserting...
+ if( elems.mUseSize > 1 )
+ {
+ rAssert( elems[elems.mUseSize-2].type == ET_NORMALROAD );
+
+ rAssert( elems[elems.mUseSize-2] != targetElem );
+ /*
+ if( elems[elems.mUseSize-2] == targetElem )
+ {
+ // we already added the target road!
+
+ // TODO:
+ // Subtract away the added cost of traversing the target
+ // road... Shouldn't have to do any more adding to totalDist
+ // since we already added the traversal cost in the process
+ // of getting to the target road, and we already added the
+ // cost of the target's actual distance from the intersection
+ totalDist -= elems[elems.mUseSize-2].cost;
+
+ // Now just pop the redundant target intersection
+ elems.Remove( elems.mUseSize-1 );
+
+ needToAddTargetRoad = false;
+ }
+ */
+
+ }
+
+ if( needToAddTargetRoad )
+ {
+ // add the target road
+ elems.Add( targetElem );
+ }
+ }
+
+
+
+ // If source element is an intersection, we have to take into account
+ // the dist from sourcePos to the first road element...
+ if( sourceElem.type == ET_INTERSECTION )
+ {
+ // if source != target, source is an intersection, and target has been added,
+ // then we have at least 2 elems
+ rAssert( elems.mUseSize >= 2 );
+ rAssert( elems[1].type == ET_NORMALROAD );
+
+ Intersection* sourceElemInt = (Intersection*) sourceElem.elem;
+
+ // now add the dist from source to the second element (which should be a road)
+ Road* firstRoad = (Road*) elems[1].elem;
+
+ rmt::Vector firstRoadEndPos, vec0, vec1, vec2, vec3;
+ RoadSegment* firstRoadSeg = NULL;
+ if( firstRoad->GetDestinationIntersection() == sourceElemInt )
+ {
+ firstRoadSeg = firstRoad->GetRoadSegment( firstRoad->GetNumRoadSegments()-1 );
+
+ firstRoadSeg->GetCorner( 1, vec1 );
+ firstRoadSeg->GetCorner( 2, vec2 );
+ firstRoadEndPos = (vec1 + vec2) * 0.5f;
+ }
+ else
+ {
+ rAssert( firstRoad->GetSourceIntersection() == sourceElemInt );
+
+ firstRoadSeg = firstRoad->GetRoadSegment( 0 );
+
+ firstRoadSeg->GetCorner( 0, vec0 );
+ firstRoadSeg->GetCorner( 3, vec3 );
+ firstRoadEndPos = (vec0 + vec3) * 0.5f;
+ }
+ totalDist += (firstRoadEndPos - sourcePos).Magnitude(); // *** SQUARE ROOT! ***
+ }
+
+
+
+ /* NOTE: The cases we postulated below never happen... Thank god...
+ //
+ // Ok, damn.. targetElem is a road...
+ // we really want the last thing in the list to be targetRoad... but we
+ // have to be careful that we haven't added it already in the process
+ // of getting to target int...
+ //
+ // So if the second last elem in the list exists (had to have been a road)
+ // and was targetRoad, then trim off the last elem (target Int)
+ //
+ // If it wasn't... then the road needs to be added to the list after
+ // target Int...
+ //
+
+ // if only one item in there, then it was the sourceElem/target Int (they
+ // are one and the same), so add targetElem (which is an adjacent road)
+ if( elems.mUseSize == 1 )
+ {
+ // add in the target
+ elems.Add( targetElem );
+
+ // NOTE:
+ // Don't need to augment totalDist here since the
+ // distance from target to targetInt road has already
+ // been accounted for
+ }
+ else
+ {
+ // the second last elem must be a road (only the sequence
+ // "...-int-road-int-road-..." is allowed)
+ rAssert( elems[elems.mUseSize-2].type == ET_NORMALROAD );
+
+ rAssert( elems[elems.mUseSize-2] != targetElem );
+
+ ////////////////////////////////////////////////////////////
+ // NOTE:
+ // This case should NEVER happen! It means we chose the
+ // wrong target intersection to head to in our earlier
+ // distance comparison.
+ //
+ // If we already added the target elem in the process of
+ // getting to the target Int, then pop the target Int
+ // so the last thing in elems is targetElem
+ if( elems[elems.mUseSize-2] == targetElem )
+ {
+ // Here, there should be at least 3 elems in our list, since target
+ // is a road and source is not target and the last item added
+ // was an intersection and we maintain the int-road-int
+ // sequence
+ rAssert( elems.mUseSize >= 3 );
+
+ // since we found targetelem to be our second last elem,
+ // and since we just pathfinded to targetInt (or targetOtherInt)
+ // the third last should have been the targetOtherInt (or targetInt)
+ float distToAdd = 0.0f;
+ float distToSubtract = 0.0f;
+ if( goingToTargetOther )
+ {
+ rAssert( (Intersection*)(elems[elems.mUseSize-3].elem) == targetInt );
+ distToAdd = distFromTargetToInt;
+ distToSubtract = distFromTargetToOtherInt;
+ }
+ else
+ {
+ rAssert( (Intersection*)(elems[elems.mUseSize-3].elem) == targetOtherInt );
+ distToAdd = distFromTargetToOtherInt;
+ distToSubtract = distFromTargetToInt;
+ }
+
+ // take out the unnecessary intersection
+ elems.Remove( elems.mUseSize-1 );
+
+ // Need to adjust totalDist...
+ // So far totalDist has overcounted:
+ // A) distance from source to one target intersection a,
+ // B) plus the length of the target road element all the way to
+ // the other target intersection b,
+ // C) plus the distance from intersection b back to target roadT
+ //
+ // Now that we're removing the last element (the wrong target int)
+ // we must take away B and C and add the dist from target roadT to
+ totalDist -= ((Road*)targetElem.elem)->GetRoadLength();
+ totalDist -= distToSubtract;
+ totalDist += distToAdd;
+ }
+
+ ////////////////////////////////////////////////////////////
+ // NOTE:
+ // We dont' need to do this ELSE IF case at all!
+ // Our algorithm already ensures that this case doesn't happen
+ // where going along the longer target road is more beneficial.
+ //
+ // otherwise, check if the second last elem joins the same
+ // two intersections (target Int and some third last
+ // intersection element "otherInt") as targetElem
+ else if( elems.mUseSize >= 3 )
+ {
+ rAssert( elems[elems.mUseSize-3].type == ET_INTERSECTION );
+
+ Intersection* intA = (Intersection*) elems[elems.mUseSize-3].elem;
+ Intersection* intB = (Intersection*) elems[elems.mUseSize-1].elem;
+
+ // if the road at elems[usesize-2] was performing the same function
+ // as targetElem road in joining the same intersections,
+ // then replace it with targetElem road...
+ Intersection* testIntB = (goingToTargetOther)? targetInt : targetOtherInt;
+ Intersection* testIntA = (goingToTargetOther)? targetOtherInt : targetInt;
+
+ if( testIntB == intB && testIntA == intA )
+ {
+ // remove target Int
+ elems.Remove( elems.mUseSize-1 );
+
+ // remove the shortestroad before targetInt because
+ // we'll be replacing it with targetRoad
+ elems.Remove( elems.mUseSize-1 );
+ }
+ // add the target element
+ elems.Add( targetElem );
+
+ // we made changes recently that means we have to reexamine the logic
+ // of this case should we have re-enabled this case.
+ rAssert( false );
+ }
+ else
+ {
+ // still need to add target
+ elems.Add( targetElem );
+
+ // NOTE:
+ // Don't need to augment totalDist here since the
+ // distance from target to targetInt road has already
+ // been accounted for
+ }
+ }
+ */
+
+#ifdef RAD_DEBUG
+ // Do some checking
+ rAssert( elems.mUseSize >= 1 );
+ rAssert( elems[0].elem == sourceElem.elem );
+ rAssert( elems[elems.mUseSize-1].elem == targetElem.elem );
+ for( int i=1; i<elems.mUseSize; i++ )
+ {
+ PathElement* lastElem = &(elems[i-1]);
+ PathElement* currElem = &(elems[i]);
+
+ Intersection* in = NULL;
+ Road* rd = NULL;
+
+ if( currElem->type == ET_INTERSECTION )
+ {
+ rAssert( lastElem->type == RoadManager::ET_NORMALROAD );
+
+ in = (Intersection*) currElem->elem;
+ rd = (Road*) lastElem->elem;
+ }
+ else if( currElem->type == ET_NORMALROAD )
+ {
+ rAssert( lastElem->type == ET_INTERSECTION );
+
+ in = (Intersection*) lastElem->elem;
+ rd = (Road*) currElem->elem;
+ }
+
+ // find currRoad in lastElem
+ bool found = false;
+ unsigned int j;
+ for( j=0; j<in->GetNumRoadsIn(); j++ )
+ {
+ Road* inRoad = (Road*) in->GetRoadIn( j );
+ rAssert( inRoad );
+
+ if( inRoad == rd )
+ {
+ rAssert( (Intersection*) rd->GetDestinationIntersection() == in );
+ found = true;
+ break;
+ }
+ }
+ for( j=0; j<in->GetNumRoadsOut(); j++ )
+ {
+ Road* outRoad = (Road*) in->GetRoadOut( j );
+ rAssert( outRoad );
+
+ if( outRoad == rd )
+ {
+ rAssert( (Intersection*) rd->GetSourceIntersection() == in );
+ found = true;
+ break;
+ }
+ }
+ rAssert( found );
+ }
+#endif
+
+ return totalDist;
+}
+
+float RoadManager::GetTraversalDistance( ShortestRoad* fromRoad, ShortestRoad* toRoad )
+{
+ rAssert( fromRoad );
+ rAssert( toRoad );
+
+ rmt::Vector vec0, vec1, vec2, vec3, start, end;
+ const Intersection* traversedInt = NULL;
+ RoadSegment* fromSeg = NULL;
+ RoadSegment* toSeg = NULL;
+ if( fromRoad->isOutRoad )
+ {
+ // fromRoad is an OUT road at lastInt, so it's an IN road at currInt, so
+ // its last segment is at currInt
+
+ traversedInt = fromRoad->road->GetDestinationIntersection();
+
+ fromSeg = fromRoad->road->GetRoadSegment( fromRoad->road->GetNumRoadSegments()-1 );
+ fromSeg->GetCorner( 1, vec1 );
+ fromSeg->GetCorner( 2, vec2 );
+ start = (vec1+vec2) * 0.5f;
+ }
+ else
+ {
+ // fromRoad is an IN road at lastInt, so its an OUT road at currInt and therefore the
+ // first segment is at currInt
+
+ traversedInt = fromRoad->road->GetSourceIntersection();
+
+ fromSeg = fromRoad->road->GetRoadSegment( 0 );
+ fromSeg->GetCorner( 0, vec0 );
+ fromSeg->GetCorner( 3, vec3 );
+ start = (vec0+vec3) * 0.5f;
+ }
+
+ if( toRoad->isOutRoad )
+ {
+ // toRoad is an OUT road at currInt, so its first segment is at currInt
+
+ // make sure this is an OUT road belonging to the traversedInt (our currInt)
+ rAssert( toRoad->road->GetSourceIntersection() == traversedInt );
+
+ toSeg = toRoad->road->GetRoadSegment( 0 );
+ toSeg->GetCorner( 0, vec0 );
+ toSeg->GetCorner( 3, vec3 );
+ end = (vec0+vec3) * 0.5f;
+ }
+ else
+ {
+ // toRoad is an IN road at currInt, so its last segment is at currInt
+
+ // make sure this is an IN road belonging to the traversedInt (our currInt)
+ rAssert( toRoad->road->GetDestinationIntersection() == traversedInt );
+
+ toSeg = toRoad->road->GetRoadSegment( toRoad->road->GetNumRoadSegments()-1 );
+ toSeg->GetCorner( 1, vec1 );
+ toSeg->GetCorner( 2, vec2 );
+ end = (vec1+vec2) * 0.5f;
+ }
+
+ // now the traversal distance can be either obtained more accurately
+ // by us building a spline... or just by linear straight line dist..
+ // let's try the linear dist first.
+ float traversalDist = (start - end).Length(); // *** SQUARE ROOT! ***
+
+ // If our from road is the same as our to road, we're being foolish...
+ // so we penalize the request by augmenting the distance by the
+ // intersection's radius...
+ if( fromRoad->road == toRoad->road )
+ {
+ traversalDist += traversedInt->GetRadius();
+ }
+
+ return traversalDist;
+}
+
+
+//
+// Follows current intersection in the given direction till we reach
+// a big intersection, where we ask how far it is to get to the target intersection.
+// Returns this distance.
+//
+float RoadManager::FindDistToTargetInOneDirection(
+ bool useMultiplier,
+ Intersection* targetInt,
+ Intersection* currInt,
+ Intersection* lastInt,
+ ShortestRoad* shortestRoadFromLastInt,
+ SwapArray<PathElement>& elems,
+ ShortestRoad*& firstShortRoad, // needed for traversal dist from src road to srcInt
+ ShortestRoad*& lastShortRoad, // needed for traversal dist from targetInt to target road
+ ErrorValue& errVal )
+{
+ rAssert( targetInt );
+ rAssert( currInt );
+ rAssert( lastInt );
+ rAssert( elems.IsSetUp() );
+
+ // add current intersection to list
+ PathElement intElem;
+ intElem.type = ET_INTERSECTION;
+ intElem.elem = currInt;
+ elems.Add( intElem );
+
+ float cost = 0.0f;
+
+ // found target
+ if( targetInt == currInt )
+ {
+ errVal = FOUND_TARGET;
+ return cost;
+ }
+ // Found "Big" Intersection
+ if( currInt->mBigIntersection )
+ {
+ NodeData* nodeData = useMultiplier ?
+ &(currInt->mBigIntersection->routesWithMultiplier[ targetInt->mIndex ]) :
+ &(currInt->mBigIntersection->routesNoMultiplier[ targetInt->mIndex ]) ;
+
+ errVal = FOUND_BIGINTERSECTION;
+
+ // First add the total dist from this BigInt to target intersection
+ cost = nodeData->dist;
+
+ // Now if there was a previous road...
+ // Compute the traversal cost through the currInt, which is the
+ // distance from the end of the shortest road from lastInt to the
+ // start of the shortest road to destInt (stored in nodeData)
+ if( shortestRoadFromLastInt )
+ {
+ ShortestRoad* fromRoad = shortestRoadFromLastInt;
+ ShortestRoad* toRoad = nodeData->roadToIn;
+ cost += GetTraversalDistance( fromRoad, toRoad );
+ }
+
+ // update the first and last shortroad data..
+ if( firstShortRoad == NULL )
+ {
+ firstShortRoad = nodeData->roadToIn;
+ }
+ lastShortRoad = nodeData->roadJustBeforeIn;
+
+ return cost;
+ }
+ // Found "linear" Intersection
+ else
+ {
+ // Last intersection becomes current, current becomes last
+ ShortestRoad* shortRoad = NULL;
+ Intersection* nextInt = NULL;
+
+ // given last int, get the nextInt and the shortestRoad to nextInt
+ currInt->GetOtherIntersection( useMultiplier, lastInt, nextInt, shortRoad );
+ if( nextInt == NULL )
+ {
+ // if deadend
+ errVal = DEAD_END;
+ return NEAR_INFINITY;
+ }
+
+ lastInt = currInt;
+ currInt = nextInt;
+
+ // add road to list
+ PathElement roadElem;
+ roadElem.type = ET_NORMALROAD;
+ roadElem.elem = shortRoad->road;
+ elems.Add( roadElem );
+
+ errVal = STILL_LOOKING;
+
+ // the cost begins with the cost of the road to nextInt
+ // and, if there was a previous road, is augmented by the
+ // traversal cost through currInt, from end of shortestRoadToLastInt
+ // to start of shortestRoad to nextInt)
+ //
+ cost = shortRoad->cost;
+ if( shortestRoadFromLastInt )
+ {
+ ShortestRoad* fromRoad = shortestRoadFromLastInt;
+ ShortestRoad* toRoad = shortRoad;
+ cost += GetTraversalDistance( fromRoad, toRoad );
+ }
+
+ // update the first and last shortroad data..
+ if( firstShortRoad == NULL )
+ {
+ firstShortRoad = shortRoad;
+ }
+ lastShortRoad = shortRoad;
+
+ return cost + FindDistToTargetInOneDirection( useMultiplier,
+ targetInt, currInt, lastInt, shortRoad, elems,
+ firstShortRoad, lastShortRoad, errVal );
+ }
+
+ // shouldn't be here
+ rAssert( false );
+ errVal = UNEXPECTED;
+ return NEAR_INFINITY;
+}
+
+
+void RoadManager::TraverseRoads(
+ bool useMultiplier,
+ Intersection* targetInt,
+ Intersection* currInt,
+ Intersection* lastInt,
+ SwapArray<PathElement>& elems,
+ ErrorValue& errVal )
+{
+ rAssert( targetInt );
+ rAssert( currInt );
+ rAssert( lastInt );
+ rAssert( elems.IsSetUp() );
+
+ // add current intersection to list
+ PathElement intElem;
+ intElem.type = ET_INTERSECTION;
+ intElem.elem = currInt;
+ elems.Add( intElem );
+
+ // found target
+ if( targetInt == currInt )
+ {
+ errVal = FOUND_TARGET;
+ return;
+ }
+
+ ShortestRoad* shortRoad = NULL;
+
+ // Found "Big" Intersection
+ if( currInt->mBigIntersection )
+ {
+ SwapArray<NodeData>* routes = useMultiplier ?
+ &(currInt->mBigIntersection->routesWithMultiplier) :
+ &(currInt->mBigIntersection->routesNoMultiplier) ;
+
+ // find the adjacent intersection that will take us to the target
+ // & the shortest road that will get us there
+ Intersection* adjacentInt = (*routes)[ targetInt->mIndex ].destIn;
+ shortRoad = (*routes)[ targetInt->mIndex ].roadToIn;
+
+ // Move current Int...
+ Intersection* tmp = currInt;
+ currInt = adjacentInt;
+ lastInt = tmp;
+
+ errVal = FOUND_BIGINTERSECTION;
+ }
+ else //Linear Intersection
+ {
+ // Last intersection becomes current, current becomes last
+ Intersection* nextInt = NULL;
+
+ // given last int
+ currInt->GetOtherIntersection( useMultiplier, lastInt, nextInt, shortRoad );
+ if( nextInt == NULL )
+ {
+ // DEAD END??? Can't be... we only call TraverseRoads when
+ // we are SURE it will take us to the target
+ rAssert( false );
+ errVal = DEAD_END;
+ return;
+ }
+
+ lastInt = currInt;
+ currInt = nextInt;
+
+ errVal = STILL_LOOKING;
+
+ }
+
+ rAssert( shortRoad );
+ rAssert( currInt );
+ rAssert( lastInt );
+ rAssert( errVal == STILL_LOOKING || errVal == FOUND_BIGINTERSECTION );
+
+ // add road to list
+ PathElement roadElem;
+ roadElem.type = ET_NORMALROAD;
+ roadElem.elem = shortRoad->road;
+ elems.Add( roadElem );
+
+ return TraverseRoads( useMultiplier, targetInt, currInt, lastInt, elems, errVal );
+
+}
+
+Intersection* RoadManager::FindIntersection( const char* name )
+{
+ return FindIntersection( tEntity::MakeUID( name ) );
+}
+
+Intersection* RoadManager::FindIntersection( tUID name )
+{
+ unsigned int i;
+ for ( i = 0; i < mNumIntersectionsUsed; ++i )
+ {
+ if ( mIntersections[i].GetNameUID() == name )
+ {
+ //Found it!
+ return &(mIntersections[i]);
+ }
+ }
+
+ return NULL;
+}
+
+Intersection* RoadManager::FindIntersection( rmt::Vector& point )
+{
+ unsigned int i;
+ for ( i = 0; i < mNumIntersectionsUsed; ++i )
+ {
+ if ( (mIntersections[i]).IsPointInIntersection( point ) )
+ {
+ return &(mIntersections[i]);
+ }
+ }
+
+ return NULL;
+}
+
+Intersection* RoadManager::FindIntersection( int iIndex )
+{
+ return &(mIntersections[iIndex]);
+}
+
+
+
+
+bool RoadManager::FindRoad( const rmt::Vector& point,
+ const Road** ppRoad,
+ RoadSegment** ppOutRoadSegment,
+ int& segmentIndex,
+ float& in,
+ float& lateral,
+ bool considerShortCuts ) const
+{
+ unsigned int i;
+ for ( i = 0; i < mNumRoadsUsed; i++ )
+ {
+ if( !considerShortCuts && mRoads[i].GetShortCut() )
+ {
+ continue;
+ }
+
+ segmentIndex = mRoads[i].GetRoadSegmentAtPoint( point, ppOutRoadSegment, in, lateral, 0 );
+ if ( segmentIndex >= 0 )
+ {
+ // We found our road.
+ //
+ *ppRoad = &(mRoads[i]);
+
+ return true;
+ break;
+ }
+ }
+ *ppRoad = 0;
+
+ return false;
+}
+
+
+RoadSegmentData* RoadManager::FindRoadSegmentData( const char* name )
+{
+ tUID nameUID = tEntity::MakeUID( name );
+ return FindRoadSegmentData( nameUID );
+}
+
+RoadSegmentData* RoadManager::FindRoadSegmentData( tUID name )
+{
+ unsigned int i;
+ for ( i = 0; i < mNumRoadSegmentDataUsed; ++i )
+ {
+ if ( mRoadSegmentData[i].GetNameUID() == name )
+ {
+ //This is it!
+ return &(mRoadSegmentData[i]);
+ }
+ }
+
+ return NULL;
+}
+
+void RoadManager::AddRoad( Road* pRoad )
+{
+ rAssert( mNumRoadsUsed < mNumRoads );
+
+ //Make sure this is the current road...
+ rAssert( pRoad == &(mRoads[mNumRoadsUsed]) );
+
+ ++mNumRoadsUsed;
+}
+
+void RoadManager::AddIntersection( Intersection* pIntersection )
+{
+ rAssert( mNumIntersectionsUsed < mNumIntersections );
+
+ //Make sure this is the current intersection...
+ rAssert( pIntersection == &(mIntersections[mNumIntersectionsUsed]) );
+
+ ++(mNumIntersectionsUsed);
+}
+
+void RoadManager::AddRoadSegmentData( RoadSegmentData* pRoadSegmentData )
+{
+ rAssert( mNumRoadSegmentDataUsed < mNumRoadSegmentData );
+
+ //Make sure this is the current intersection...
+ rAssert( pRoadSegmentData == &(mRoadSegmentData[mNumRoadSegmentDataUsed]) );
+
+ ++(mNumRoadSegmentDataUsed);
+}
+
+void RoadManager::AddRoadSegment( RoadSegment* pRoadSegment )
+{
+ rAssert( mNumRoadSegmentsUsed < mNumRoadSegments);
+
+ //Make sure this is the current intersection...
+ //rAssert( pRoadSegment == &(mRoadSegments[mNumRoadSegmentsUsed]) );
+ rAssert( pRoadSegment == mRoadSegments[mNumRoadSegmentsUsed] );
+
+ ++(mNumRoadSegmentsUsed);
+}
+
+
+// search roads and intersections for whichever's closest, given a position
+void RoadManager::FindClosestPathElement
+(
+ const rmt::Vector& pos, // IN: search center
+ float searchRadius, // IN: search radius
+ PathfindingOptions options, // IN: search options
+
+ PathElement& closestElem, // OUT: closest element (road or intersection)
+ RoadSegment*& closestRoadSeg, // OUT: if closest element is road, this is closest seg
+ float& closestRoadSegT, // OUT: if closest element is road, this is segment t value
+ float& closestRoadT // OUT: if closest element is road, this is road's t value
+)
+{
+ rAssert( 0 <= options && options <= NUM_POS );
+
+ // must search for SOMETHING... default to all
+ if( !(options & PO_SEARCH_ROADS) && !(options & PO_SEARCH_INTERSECTIONS) )
+ {
+ options |= PO_SEARCH_ROADS | PO_SEARCH_INTERSECTIONS;
+ }
+
+ // APPROACH
+ // ========
+ // Search all roads in given radius to find:
+ // the closest road segment, and
+ // the distance to this road segment
+ //
+ // Search all intersections in given radius to find:
+ // the closest intersection, and
+ // the distance to this intersection
+ //
+ // If distance to closest road segment <= distance to closest intersection
+ // use the road segment
+ // else
+ // use the intersection
+ //
+
+ // find closets road segment
+ RoadSegment* closestSeg = NULL;
+ float distSqrToClosestSeg = 100000000.0f;
+
+ if( options & PO_SEARCH_ROADS )
+ {
+ if( options & PO_SEARCH_SHORTCUTS )
+ {
+ GetIntersectManager()->FindClosestAnyRoad(
+ pos,
+ searchRadius,
+ closestSeg,
+ distSqrToClosestSeg );
+ }
+ else
+ {
+ GetIntersectManager()->FindClosestRoad(
+ pos,
+ searchRadius,
+ closestSeg,
+ distSqrToClosestSeg );
+ }
+ rAssert( closestSeg );
+ }
+
+
+ // find closest intersection
+ Intersection* closestInt = NULL;
+ float distSqrToClosestInt = 100000000.0f;
+
+ if( options & PO_SEARCH_INTERSECTIONS )
+ {
+ for( unsigned int i = 0; i < mNumIntersectionsUsed; ++i )
+ {
+ rmt::Vector intPos;
+ mIntersections[i].GetLocation( intPos );
+
+ float distSqr = (intPos - pos).MagnitudeSqr();
+ if( distSqr < distSqrToClosestInt )
+ {
+ closestInt = &mIntersections[i];
+ distSqrToClosestInt = distSqr;
+ }
+ }
+
+ rAssert( closestInt );
+ }
+
+ // Populate return values
+ if( distSqrToClosestSeg <= distSqrToClosestInt )
+ {
+ rAssert( closestSeg );
+
+ closestElem.type = ET_NORMALROAD;
+ closestElem.elem = closestSeg->GetRoad();
+ closestRoadSeg = closestSeg;
+ closestRoadSegT = DetermineSegmentT( pos, closestRoadSeg );
+ closestRoadT = DetermineRoadT( closestRoadSeg, closestRoadSegT );
+ }
+ else
+ {
+ rAssert( closestInt );
+
+ closestElem.type = ET_INTERSECTION;
+ closestElem.elem = closestInt;
+ closestRoadSeg = NULL;
+ closestRoadSegT = 0.0f;
+ closestRoadT = 0.0f;
+ }
+}
+
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/roads/roadmanager.h b/game/code/roads/roadmanager.h
new file mode 100644
index 0000000..a5b6a83
--- /dev/null
+++ b/game/code/roads/roadmanager.h
@@ -0,0 +1,318 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: roadmanager.h
+//
+// Description: Blahblahblah
+//
+// History: 26/06/2002 + Created -- Cary Brisebois (based on TBJ's work)
+//
+//=============================================================================
+
+#ifndef ROADMANAGER_H
+#define ROADMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <render/culling/swaparray.h>
+
+//========================================
+// Forward References
+//========================================
+class Road;
+class Intersection;
+class RoadSegmentData;
+class RoadSegment;
+class Lane;
+
+class RoadRenderTest;
+
+static const float NEAR_INFINITY = 100000.0f;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class RoadManager
+{
+public:
+ enum { STARTUP = false, SHUTDOWN = true };
+
+ static const float AGAINST_TRAFFIC_COST_MULTIPLIER;
+
+ struct ShortestRoad
+ {
+ Road* road;
+ bool isOutRoad;
+ float cost;
+ };
+ struct NodeData
+ {
+ Intersection* destIn;
+ ShortestRoad* roadToIn;
+ ShortestRoad* roadJustBeforeIn;
+ float dist; // total distance to the destination intersection
+ };
+ struct BigIntersection
+ {
+ Intersection* in;
+ SwapArray<NodeData> routesWithMultiplier;
+ SwapArray<NodeData> routesNoMultiplier;
+ };
+ enum ElementType
+ {
+ ET_INTERSECTION, // Intersection*
+ ET_NORMALROAD // Road*
+ };
+ struct PathElement
+ {
+ ElementType type;
+ void* elem;
+ bool operator==( const PathElement& right ) const
+ {
+ return(elem==right.elem);
+ }
+ bool operator!=( const PathElement& right ) const
+ {
+ return(elem!=right.elem);
+ }
+ };
+
+
+ static RoadManager* GetInstance();
+ static void Destroy();
+
+ //---------------Initialization
+ void Init( bool shutdown );
+
+ void InitializeRoadMemory( unsigned int numRoads );
+ void InitializeIntersectionMemory( unsigned int numIntersections );
+ void InitializeRoadSegmentDataMemory( unsigned int numSegments );
+ void InitializeRoadSegmentMemory( unsigned int numRoadSegments );
+
+ void DumpRoadSegmentDataMemory();
+
+ Road* GetFreeRoadMemory( );
+ Intersection* GetFreeIntersectionMemory( );
+ RoadSegmentData* GetFreeRoadSegmentDataMemory( );
+ RoadSegment* GetFreeRoadSegmentMemory( );
+
+ void AddRoad( Road* pRoad );
+ void AddIntersection( Intersection* pIntersection );
+ void AddRoadSegmentData( RoadSegmentData* pRoadSegmentData );
+ void AddRoadSegment( RoadSegment* pRoadSegment );
+
+ int GetMaxPathElements();
+
+ void CreateRoadNetwork( void );
+
+
+ //---------------Data Aquisition
+ Intersection* FindIntersection( const char* name );
+ Intersection* FindIntersection( tUID name );
+ Intersection* FindIntersection( rmt::Vector& point );
+
+ Intersection* FindIntersection( int iIndex );
+
+ int GetNumIntersectionsUsed( );
+ unsigned int GetNumRoads( );
+
+ bool FindRoad( const rmt::Vector& point,
+ const Road** ppRoad,
+ RoadSegment** ppOutRoadSegment,
+ int& segmentIndex,
+ float& in,
+ float& lateral,
+ bool considerShortCuts=true ) const;
+
+ static bool FindClosestPointOnRoad( const Road* pRoad,
+ const rmt::Vector& pos,
+ rmt::Vector& closestPos,
+ float& closestDist,
+ int& segmentIndex );
+
+ static float DetermineRoadT( RoadSegment* seg, float segT );
+ static float DetermineSegmentT( const rmt::Vector& pos, RoadSegment* seg );
+
+
+
+ RoadSegmentData* FindRoadSegmentData( const char* name );
+ RoadSegmentData* FindRoadSegmentData( tUID name );
+
+ RoadRenderTest* GetRoadRenderTest();
+
+ // fully pathfind from source to target, returning float distance
+ float FindPathElementsBetween(
+ bool useMultiplier, // IN: direction-biased?
+ PathElement& sourceElem, // IN: starting element
+ float sourceT, // IN: used only if sourceElem is a road
+ const rmt::Vector& sourcePos, // IN: used only if sourceElem is an intersection
+ PathElement& targetElem, // IN: terminating element
+ float targetT, // IN: used only if targetElem is a road
+ const rmt::Vector& targetPos, // IN: used only if targetElem is an intersection
+ SwapArray<PathElement>& elems ); // OUT: accumulate roads or intersections
+
+ enum PathfindingOption
+ {
+ PO_SEARCH_SHORTCUTS = 1 << 0, // whether or not to take into account shortcut roads
+ PO_SEARCH_ROADS = 1 << 1, // whether or not to search for roads
+ PO_SEARCH_INTERSECTIONS = 1 << 2, // whether or not to search for intersections
+
+ NUM_POS = ( PO_SEARCH_SHORTCUTS |
+ PO_SEARCH_ROADS |
+ PO_SEARCH_INTERSECTIONS )
+ };
+
+ typedef char PathfindingOptions;
+
+ // search roads and intersections for whichever's closest, given a position
+ void FindClosestPathElement(
+ const rmt::Vector& pos, // IN: search center
+ float searchRadius, // IN: search radius
+ PathfindingOptions options, // IN: search options
+ PathElement& closestElem, // OUT: closest element (road or intersection)
+ RoadSegment*& closestRoadSeg, // OUT: if closest element is road, this is closest seg
+ float& closestRoadSegT, // OUT: if closest element is road, this is segment t value
+ float& closestRoadT ); // OUT: if closest element is road, this is road's t value
+
+ enum ErrorValue
+ {
+ DEAD_END,
+ STILL_LOOKING,
+ FOUND_TARGET,
+ FOUND_BIGINTERSECTION,
+ UNEXPECTED
+ };
+ struct DistErrMap
+ {
+ int ID;
+ ErrorValue errVal;
+ float dist;
+ };
+
+ float FindDistToTargetInOneDirection(
+ bool useMultiplier,
+ Intersection* targetInt,
+ Intersection* currInt,
+ Intersection* lastInt,
+ ShortestRoad* shortestRoadFromLastInt,
+ SwapArray<PathElement>& elems,
+ ShortestRoad*& firstShortRoad, // needed for traversal dist from src road to srcInt
+ ShortestRoad*& lastShortRoad, // needed for traversal dist from targetInt to target road
+ ErrorValue& errVal );
+
+ void TraverseRoads(
+ bool useMultiplier,
+ Intersection* targetInt,
+ Intersection* currInt,
+ Intersection* lastInt,
+ SwapArray<PathElement>& elems,
+ ErrorValue& errVal );
+
+
+
+private:
+
+ static RoadManager* mInstance;
+
+ Road* mRoads;
+ unsigned int mNumRoads;
+ unsigned int mNumRoadsUsed;
+
+ Intersection* mIntersections;
+ unsigned int mNumIntersections;
+ unsigned int mNumIntersectionsUsed;
+
+ struct DijkstraNode
+ {
+ struct AdjacencyData
+ {
+ DijkstraNode* adjacentNode;
+ ShortestRoad* shortestRoadThere;
+ };
+
+ Intersection* in;
+ float distToSrc;
+ DijkstraNode* predecessor;
+ ShortestRoad* shortestRoadFromPred;
+ bool addedToS;
+ SwapArray<AdjacencyData> adjacents;
+
+ DijkstraNode()
+ {
+ in = NULL;
+
+ distToSrc = NEAR_INFINITY;
+ predecessor = NULL;
+ shortestRoadFromPred = NULL;
+ addedToS = false;
+ }
+
+ ~DijkstraNode()
+ {
+ in = NULL;
+ adjacents.Clear();
+
+ distToSrc = NEAR_INFINITY;
+ predecessor = NULL;
+ shortestRoadFromPred = NULL;
+ addedToS = false;
+ }
+
+ void Init( float dist, DijkstraNode* pred )
+ {
+ distToSrc = dist;
+ predecessor = pred;
+
+ shortestRoadFromPred = NULL;
+ addedToS = false;
+ }
+ };
+ SwapArray<BigIntersection*> mBigIntersections;
+ void PopulateConnectivityData( bool useMultiplier, Intersection* intersections, int numInts );
+ void VisitAll( SwapArray<DijkstraNode>& nodes );
+
+ RoadSegmentData* mRoadSegmentData;
+ unsigned int mNumRoadSegmentData;
+ unsigned int mNumRoadSegmentDataUsed;
+
+ RoadSegment** mRoadSegments; // dynamically allocated array of pointers to RoadSegments
+ unsigned int mNumRoadSegments;
+ unsigned int mNumRoadSegmentsUsed;
+ /*
+ TransformRoadSegment** mRoadSegments;
+ unsigned int mNumRoadSegments;
+ unsigned int mNumRoadSegmentsUsed;
+ */
+
+ RoadRenderTest* mRender;
+
+ float GetTraversalDistance( ShortestRoad* fromRoad, ShortestRoad* toRoad );
+
+
+ //Singleton
+ RoadManager();
+ virtual ~RoadManager();
+
+ //Prevent wasteful constructor creation.
+ RoadManager( const RoadManager& roadmanager );
+ RoadManager& operator=( const RoadManager& roadmanager );
+};
+
+inline unsigned int RoadManager::GetNumRoads()
+{
+ return mNumRoadsUsed;
+}
+inline int RoadManager::GetNumIntersectionsUsed()
+{
+ return mNumIntersectionsUsed;
+}
+inline RoadRenderTest* RoadManager::GetRoadRenderTest()
+{
+ return mRender;
+}
+
+#endif //ROADMANAGER_H
diff --git a/game/code/roads/roadrender.cpp b/game/code/roads/roadrender.cpp
new file mode 100644
index 0000000..c7c5ce5
--- /dev/null
+++ b/game/code/roads/roadrender.cpp
@@ -0,0 +1,401 @@
+#include <roads/intersection.h>
+#include <roads/road.h>
+#include <roads/roadsegment.h>
+#include <roads/lane.h>
+#include <p3d/camera.hpp>
+#include <p3d/shader.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/view.hpp>
+
+
+//#include "../../debugtools/debuginfo.h"
+
+// TODO: remove ugly hack for shader.
+//
+#include <roads/roadmanager.h>
+
+tShader* gpShader = NULL; //new tShader("simple");
+
+tShader* GetShader()
+{
+ if ( !gpShader )
+ {
+ gpShader = new tShader("simple");
+ }
+
+ return gpShader;
+}
+
+
+void DrawCircle( float radius, const rmt::Vector& center, int subDivisions, const tColour& colour )
+{
+ rmt::Vector a,b;
+ int i;
+ int n = subDivisions;
+ float r = radius;
+ pddiPrimStream* stream = p3d::pddi->BeginPrims( GetShader()->GetShader(), PDDI_PRIM_LINES, PDDI_V_C, n * 2 );
+ if ( stream )
+ {
+ for( i = 0; i < n; i++ )
+ {
+ a.x = center.x + r * rmt::Cos( i * rmt::PI_2 / n );
+ a.y = center.y;
+ a.z = center.z + r * rmt::Sin( i * rmt::PI_2 / n );
+ b.x = center.x + r * rmt::Cos( ( i + 1 ) * rmt::PI_2 / n );
+ b.y = center.y;
+ b.z = center.z + r * rmt::Sin( ( i + 1 ) * rmt::PI_2 / n );
+
+ stream->Vertex( ( ( pddiVector* )( &( a ) ) ), colour );
+ stream->Vertex( ( ( pddiVector* )( &( b ) ) ), colour );
+ }
+ }
+ p3d::pddi->EndPrims( stream );
+
+}
+
+void DrawBox( rmt::Box3D& box, const tColour& colour )
+{
+ // Render bounding box.
+ //
+ pddiPrimStream* stream = p3d::pddi->BeginPrims( GetShader()->GetShader(), PDDI_PRIM_LINES, PDDI_V_C, 2 );
+ if ( stream )
+ {
+ rmt::Vector start;
+ rmt::Vector end;
+ start.y = 0.0f;
+ end.y = 0.0f;
+
+ start.x = box.high.x;
+ start.z = box.high.z;
+ end.x = box.high.x;
+ end.z = box.low.z;
+ stream->Vertex( ( ( pddiVector* )( &( start ) ) ), colour );
+ stream->Vertex( ( ( pddiVector* )( &( end ) ) ), colour );
+
+ start = end;
+ end.x = box.low.x;
+ end.z = box.low.z;
+ stream->Vertex( ( ( pddiVector* )( &( start ) ) ), colour );
+ stream->Vertex( ( ( pddiVector* )( &( end ) ) ), colour );
+
+ start = end;
+ end.x = box.low.x;
+ end.z = box.high.z;
+ stream->Vertex( ( ( pddiVector* )( &( start ) ) ), colour );
+ stream->Vertex( ( ( pddiVector* )( &( end ) ) ), colour );
+
+ start = end;
+ end.x = box.high.x;
+ end.z = box.high.z;
+
+ stream->Vertex( ( ( pddiVector* )( &( start ) ) ), colour );
+ stream->Vertex( ( ( pddiVector* )( &( end ) ) ), colour );
+ }
+ p3d::pddi->EndPrims( stream );
+}
+
+
+/*
+==============================================================================
+Intersection::Render
+==============================================================================
+Description: Draw a circle representing the Intersection.
+ Call the road Render routine for each road.
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Intersection::Render( void ) const
+{
+ // Draw a circle representing the Intersection.
+ //
+ tColour intersectionColour;
+ if( mType == Intersection::N_WAY )
+ {
+ intersectionColour.Set( 0, 255, 0 );
+ }
+ else
+ {
+ intersectionColour.Set( 0, 0, 255 );
+ }
+
+ rmt::Vector center;
+ GetLocation( center );
+
+ if ( p3d::context->GetView()->GetCamera()->SphereVisible( center, 15.0f ) )
+ {
+ center.y += 1.0f;
+ DrawCircle( GetRadius( ), center, 8, intersectionColour );
+ }
+
+ unsigned int i = 0;
+ for ( i = 0; i < this->mnRoadsIn; i++ )
+ {
+ if ( mRoadListIn[ i ] )
+ {
+ mRoadListIn[ i ]->Render( tColour(255, 255, 255) );
+ }
+ }
+ for ( i = 0; i < this->mnRoadsOut; i++ )
+ {
+ if ( mRoadListOut[ i ] )
+ {
+ mRoadListOut[ i ]->Render( tColour(0, 0, 0) );
+ }
+ }
+}
+/*
+==============================================================================
+Road::Render
+==============================================================================
+Description: Draw a line along the inside edge of the road.
+ Call render for each roadsegment.
+ Call Render for each Lane.
+
+Parameters: ( const tColour& colour )
+
+Return: void
+
+=============================================================================
+*/
+void Road::Render( const tColour& colour )
+{
+ static tColour roadColour = colour;
+
+ if ( p3d::context->GetView()->GetCamera()->SphereVisible( mSphere.centre, mSphere.radius ) )
+ {
+ static bool bDrawRoads = true;
+ if ( bDrawRoads )
+ {
+ rmt::Vector start;
+ this->GetDestinationIntersection( )->GetLocation( start );
+ rmt::Vector end;
+ this->GetSourceIntersection( )->GetLocation( end );
+
+#ifdef TOOLS
+ DrawCircle( GetDestinationIntersection( )->GetRadius( ), start, 8, colour );
+ DrawCircle( GetSourceIntersection( )->GetRadius( ), end, 8, colour );
+
+#endif
+ // Render the roads.
+ //
+
+ // Draw a line from the start to the end.
+ //
+ pddiPrimStream* stream = p3d::pddi->BeginPrims( GetShader()->GetShader(), PDDI_PRIM_LINES, PDDI_V_C, 2 );
+ if ( stream )
+ {
+ stream->Vertex( ( ( pddiVector* )( &( start ) ) ), roadColour );
+ stream->Vertex( ( ( pddiVector* )( &( end ) ) ), roadColour );
+ }
+ p3d::pddi->EndPrims( stream );
+ }
+ static bool bDrawRoadBBox = false;
+// if ( BEGIN_DEBUGINFO_SECTION( "Draw Road BBox" ) )
+// {
+// ::DrawBox( mBox, roadColour );
+// }
+// END_DEBUGINFO_SECTION( "Draw Road BBox" );
+ static bool bDrawRoadBSphere = false;
+// if ( BEGIN_DEBUGINFO_SECTION( "Draw Road BSphere" ) )
+// {
+// // Render the roads bounding sphere.
+// //
+// ::DrawCircle( mSphere.radius, mSphere.centre, 8, roadColour );
+// }
+// END_DEBUGINFO_SECTION( "Draw Road BSphere" );
+ // Render the road Segments.
+ //
+ static bool bDrawRoadSegments = true;
+ if ( bDrawRoadSegments )
+ {
+ // Iterate through the segments.
+ //
+ unsigned int i = 0;
+
+ unsigned int colourStep = 255 / mnRoadSegments;
+
+ for ( i = 0; i < mnRoadSegments; i++ )
+ {
+ tColour colour = roadColour;
+ colour.SetRed( colourStep * i );
+ colour.SetGreen( colourStep * i );
+ colour.SetBlue( colourStep * i );
+ mppRoadSegmentArray[ i ]->Render( colour );
+ }
+ }
+
+ // Render the lanes.
+ //
+ static bool bDrawLanes = true;
+ if ( bDrawLanes )
+ {
+ unsigned int i = 0;
+ for ( i = 0; i < mnLanes; i++ )
+ {
+ this->mLaneList[ i ].Render( );
+ }
+ }
+ }
+}
+/*
+==============================================================================
+RoadSegment::Render
+==============================================================================
+Description: Outline the rectangular road segment.
+
+Parameters: ( const tColour& colour )
+
+Return: void
+
+=============================================================================
+*/
+void RoadSegment::Render( const tColour& colour )
+{
+ tColour roadSegmentColour = colour;
+
+ rmt::Sphere sphere;
+ GetBoundingSphere( &sphere );
+
+
+ if ( p3d::context->GetView()->GetCamera()->SphereVisible( sphere.centre, sphere.radius ) )
+ {
+ const int numPoints = 4;
+ rmt::Vector vertices[ numPoints ];
+ int i = 0;
+ for ( i = 0; i < numPoints; i++ )
+ {
+ GetCorner( i, vertices[ i ] );
+
+ vertices[i].y += 1.0f;
+ }
+
+ pddiPrimStream* stream = p3d::pddi->BeginPrims( GetShader()->GetShader(), PDDI_PRIM_LINES, PDDI_V_C, numPoints * 2 );
+
+
+ for ( i = 0; i < numPoints; i++ )
+ {
+ if ( stream )
+ {
+ stream->Vertex( ( ( pddiVector* )( &( vertices[ i ] ) ) ), roadSegmentColour );
+ stream->Vertex( ( ( pddiVector* )( &( vertices[ ( i + 1 ) % numPoints ] ) ) ), roadSegmentColour );
+ }
+ }
+ p3d::pddi->EndPrims( stream );
+
+ stream = p3d::pddi->BeginPrims( GetShader()->GetShader(), PDDI_PRIM_LINES, PDDI_V_C, GetNumLanes() * 2 );
+
+ unsigned int index;
+ for ( index = 0; index < GetNumLanes(); index++ )
+ {
+ if ( stream )
+ {
+ rmt::Vector pos, facing;
+ GetLaneLocation(0.0f, index, pos, facing);
+ pos.y += 1.0f;
+ stream->Vertex( ( ( pddiVector* )( &( pos ) ) ), tColour( 255, 0, 0 ) );
+
+ GetLaneLocation(1.0f, index, pos, facing);
+ pos.y += 1.0f;
+ stream->Vertex( ( ( pddiVector* )( &( pos ) ) ), tColour( 255, 0, 0 ) );
+ }
+ }
+ p3d::pddi->EndPrims( stream );
+ }
+}
+
+
+void RoadSegment::RenderAnywhere( const tColour& colour )
+{
+ tColour roadSegmentColour = colour;
+
+ rmt::Sphere sphere;
+ GetBoundingSphere( &sphere );
+
+ const int numPoints = 4;
+ rmt::Vector vertices[ numPoints ];
+ int i = 0;
+ for ( i = 0; i < numPoints; i++ )
+ {
+ GetCorner( i, vertices[ i ] );
+
+ vertices[i].y += 1.0f;
+ }
+
+ pddiPrimStream* stream = p3d::pddi->BeginPrims( GetShader()->GetShader(), PDDI_PRIM_LINES, PDDI_V_C, numPoints * 2 );
+
+
+ for ( i = 0; i < numPoints; i++ )
+ {
+ if ( stream )
+ {
+ stream->Vertex( ( ( pddiVector* )( &( vertices[ i ] ) ) ), roadSegmentColour );
+ stream->Vertex( ( ( pddiVector* )( &( vertices[ ( i + 1 ) % numPoints ] ) ) ), roadSegmentColour );
+ }
+ }
+ p3d::pddi->EndPrims( stream );
+
+ stream = p3d::pddi->BeginPrims( GetShader()->GetShader(), PDDI_PRIM_LINES, PDDI_V_C, GetNumLanes() * 2 );
+
+ unsigned int index;
+ for ( index = 0; index < GetNumLanes(); index++ )
+ {
+ if ( stream )
+ {
+ rmt::Vector pos, facing;
+ GetLaneLocation(0.0f, index, pos, facing);
+ pos.y += 1.0f;
+ stream->Vertex( ( ( pddiVector* )( &( pos ) ) ), tColour( 255, 0, 0 ) );
+
+ GetLaneLocation(1.0f, index, pos, facing);
+ pos.y += 1.0f;
+ stream->Vertex( ( ( pddiVector* )( &( pos ) ) ), tColour( 255, 0, 0 ) );
+ }
+ }
+ p3d::pddi->EndPrims( stream );
+
+}
+
+/*
+==============================================================================
+Lane::Render
+==============================================================================
+Description: Draw a line connecting the lane points.
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Lane::Render( void )
+{
+#if defined(RAD_DEBUG) || defined(RAD_TUNE)
+ static tColour laneColour( 255, 0, 0 );
+
+ rmt::Vector start, end;
+ GetStart( start );
+ GetEnd( end );
+
+ int numPoints = this->GetNumPoints( );
+ // Draw a line from the start to the end.
+ //
+ pddiPrimStream* stream = p3d::pddi->BeginPrims( GetShader()->GetShader(), PDDI_PRIM_LINESTRIP, PDDI_V_C, numPoints );
+
+ int i = 0;
+ for ( i = 0; i < numPoints; i++ )
+ {
+ if ( stream )
+ {
+ rmt::Vector point;
+ GetPoint( i, &point );
+ point.y += 1.0f;
+ stream->Vertex( ( ( pddiVector* )( &( point ) ) ), laneColour );
+ }
+ }
+ p3d::pddi->EndPrims( stream );
+#endif
+}
diff --git a/game/code/roads/roadrendertest.cpp b/game/code/roads/roadrendertest.cpp
new file mode 100644
index 0000000..d8619be
--- /dev/null
+++ b/game/code/roads/roadrendertest.cpp
@@ -0,0 +1,271 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: RoadRenderTest.cpp
+//
+// Description: Implement RoadRenderTest
+//
+// History: 27/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef RAD_RELEASE
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radmath/radmath.hpp>
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <roads/RoadRenderTest.h>
+#include <roads/roadmanager.h>
+#include <roads/road.h>
+#include <roads/roadsegment.h>
+#include <roads/intersection.h>
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/culling/spatialtree.h>
+#include <render/culling/worldscene.h>
+#include <render/dsg/intersectdsg.h>
+#include <contexts/bootupcontext.h>
+#include <debug/profiler.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// RoadRenderTest::RoadRenderTest
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RoadRenderTest::RoadRenderTest() :
+ mDisplay( false ),
+ mDisplaySpawnSegments( false ),
+ mDisplayTerrainTypes( false )
+{
+ radDbgWatchAddBoolean( &mDisplay, "Display", "Roads" );
+ radDbgWatchAddBoolean( &mDisplaySpawnSegments, "Display Spawn Segments", "Roads" );
+ radDbgWatchAddBoolean( &mDisplayTerrainTypes, "Display Terrain Types", "Roads" );
+}
+
+//==============================================================================
+// RoadRenderTest::~RoadRenderTest
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+RoadRenderTest::~RoadRenderTest()
+{
+ radDbgWatchDelete( &mDisplay );
+ radDbgWatchDelete( &mDisplaySpawnSegments );
+ radDbgWatchDelete( &mDisplayTerrainTypes );
+}
+
+//=============================================================================
+// RoadRenderTest::Display
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void RoadRenderTest::Display()
+{
+ DisplaySpawnSegments();
+ DisplayTerrainType();
+ if ( !mDisplay )
+ {
+ return;
+ }
+
+ BEGIN_PROFILE("RoadRenderTest");
+
+ Avatar* a = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( a );
+
+ rmt::Vector position;
+ a->GetPosition( position );
+
+ RoadManager* rm = RoadManager::GetInstance();
+
+ const Road* road = NULL;
+ RoadSegment* irs = NULL;
+ int index = -1;
+ float in = 0;
+ float lateral = 0;
+
+ if ( rm->FindRoad( position, &road, &irs, index, in, lateral, true ) )
+ {
+ irs->Render( tColour( 255, 255, 255 ) );
+ }
+ else
+ {
+ Intersection* intersection = rm->FindIntersection( position );
+ if ( intersection )
+ {
+ intersection->Render();
+ }
+ }
+
+ END_PROFILE("RoadRenderTest");
+}
+
+void RoadRenderTest::DisplaySpawnSegments()
+{
+ if ( !mDisplaySpawnSegments )
+ {
+ return;
+ }
+
+ if ( !mSegments.IsSetUp() )
+ {
+ return;
+ }
+
+ BEGIN_PROFILE("RoadRenderTest");
+
+
+ int i;
+ for( i=0; i<mSegments.mUseSize; i++ )
+ {
+ RoadSegment* segment;
+
+ segment = mSegments.mpData[i];
+ rAssert( segment != NULL );
+
+ segment->RenderAnywhere( tColour( 255, 255, 255 ) );
+ }
+
+
+ END_PROFILE("RoadRenderTest");
+}
+
+void RoadRenderTest::DisplayTerrainType( void )
+{
+ if( !mDisplayTerrainTypes )
+ {
+ return;
+ }
+
+ tColour terrainColours[ 9 ];
+ terrainColours[ 0 ].Set( 128, 128, 128 ); // Road.
+ terrainColours[ 1 ].Set( 0, 255, 0 ); // Grass.
+ terrainColours[ 2 ].Set( 255, 255, 0 ); // Sand.
+ terrainColours[ 3 ].Set( 32, 32, 32 ); // Gravel.
+ terrainColours[ 4 ].Set( 0, 0, 255 ); // Water.
+ terrainColours[ 5 ].Set( 250, 200, 150 ); // Wood.
+ terrainColours[ 6 ].Set( 200, 225, 250 ); // Metal.
+ terrainColours[ 7 ].Set( 64, 48, 32 ); // Dirt.
+ terrainColours[ 8 ].Set( 255, 0, 255 ); // Unknown.
+
+ Avatar* a = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( a );
+
+ rmt::Vector position;
+ a->GetPosition( position );
+ SpatialNode& rCurrentLeaf = GetRenderManager()->pWorldScene()->mStaticTreeWalker.rSeekLeaf( (Vector3f&)position );
+
+ pddiShader* testShader = BootupContext::GetInstance()->GetSharedShader();
+ testShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_NONE );
+ testShader->SetInt( PDDI_SP_ISLIT, 0 );
+ testShader->SetInt( PDDI_SP_ALPHATEST, 0 );
+ testShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_FLAT ); //PDDI_SHADE_GOURAUD
+ testShader->SetInt( PDDI_SP_TWOSIDED, 1 );
+
+ for( int i = rCurrentLeaf.mIntersectElems.mUseSize - 1; i > -1; --i )
+ {
+ for( int j = rCurrentLeaf.mIntersectElems[ i ]->nTris() - 1; j > -1; --j )
+ {
+ //rmt::Vector tmpVect, tmpVect2;
+ //float DistToPlane, ClosestDistToPlane = 20000.0f;
+ rmt::Vector triPts[ 3 ];
+ rmt::Vector triNorm;
+ rmt::Vector triCtr;
+ //float triRadius;
+ int terrainType;
+ bool interior;
+ //triRadius = rCurrentLeaf.mIntersectElems[ i ]->mTri( j, triPts, triNorm, triCtr, &terrainType);
+ terrainType = rCurrentLeaf.mIntersectElems[ i ]->mTri( j, triPts, triNorm );
+ interior = ( terrainType & 0x80 ) != 0;
+ terrainType &= ~0x80;
+ int colourIndex = rmt::Clamp( terrainType, 0, 8 );
+ tColour lineColour( terrainColours[ colourIndex ] );
+ if( interior )
+ {
+ lineColour.Set( lineColour.Red() >> 1, lineColour.Green() >> 1, lineColour.Blue() >> 1 );
+ }
+ tColour fillColour;
+ fillColour.Set( lineColour.Red() >> 1, lineColour.Green() >> 1, lineColour.Blue() >> 1 );
+
+ rmt::Vector center( triPts[ 0 ] );
+ center.Add( triPts[ 1 ] );
+ center.Add( triPts[ 2 ] );
+ center.Scale( 1.0f / 3.0f );
+ rmt::Vector toCenter;
+ triNorm.Scale( 0.1f );
+
+ for( int k = 0; k < 3; ++k )
+ {
+ toCenter.Sub( center, triPts[ k ] );
+ toCenter.Normalize();
+ toCenter.Scale( 0.1f );
+ triPts[ k ].Add( toCenter );
+ triPts[ k ].Add( triNorm );
+ }
+
+ pddiPrimStream* test = p3d::pddi->BeginPrims( testShader, PDDI_PRIM_TRIANGLES, PDDI_V_C, 3 );
+ test->Colour( fillColour );
+ test->Coord( triPts[ 0 ].x, triPts[ 0 ].y, triPts[ 0 ].z );
+ test->Colour( fillColour );
+ test->Coord( triPts[ 1 ].x, triPts[ 1 ].y, triPts[ 1 ].z );
+ test->Colour( fillColour );
+ test->Coord( triPts[ 2 ].x, triPts[ 2 ].y, triPts[ 2 ].z );
+ p3d::pddi->EndPrims( test );
+ test = p3d::pddi->BeginPrims( testShader, PDDI_PRIM_LINESTRIP, PDDI_V_C, 4 );
+ test->Colour( lineColour );
+ test->Coord( triPts[ 0 ].x, triPts[ 0 ].y, triPts[ 0 ].z );
+ test->Colour( lineColour );
+ test->Coord( triPts[ 1 ].x, triPts[ 1 ].y, triPts[ 1 ].z );
+ test->Colour( lineColour );
+ test->Coord( triPts[ 2 ].x, triPts[ 2 ].y, triPts[ 2 ].z );
+ test->Colour( lineColour );
+ test->Coord( triPts[ 0 ].x, triPts[ 0 ].y, triPts[ 0 ].z );
+ p3d::pddi->EndPrims( test );
+ }
+ }
+ testShader->SetInt( PDDI_SP_TWOSIDED, 0 );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+#endif \ No newline at end of file
diff --git a/game/code/roads/roadrendertest.h b/game/code/roads/roadrendertest.h
new file mode 100644
index 0000000..78a4e22
--- /dev/null
+++ b/game/code/roads/roadrendertest.h
@@ -0,0 +1,60 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: roadrendertest.h
+//
+// Description: Blahblahblah
+//
+// History: 27/06/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef ROADRENDERTEST_H
+#define ROADRENDERTEST_H
+
+#ifndef RAD_RELEASE
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+#include <roads/roadsegment.h>
+#include <render/Culling/ReserveArray.h>
+
+#include <p3d/drawable.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class RoadRenderTest : public tDrawable
+{
+public:
+ RoadRenderTest();
+ virtual ~RoadRenderTest();
+
+ void Display();
+ void DisplaySpawnSegments();
+ void DisplayTerrainType( void );
+
+ ReserveArray<RoadSegment*> mSegments;
+
+private:
+
+ bool mDisplay;
+ bool mDisplaySpawnSegments;
+ bool mDisplayTerrainTypes;
+
+ //Prevent wasteful constructor creation.
+ RoadRenderTest( const RoadRenderTest& roadrendertest );
+ RoadRenderTest& operator=( const RoadRenderTest& roadrendertest );
+};
+
+#endif
+
+#endif //ROADRENDERTEST_H
diff --git a/game/code/roads/roadsegment.cpp b/game/code/roads/roadsegment.cpp
new file mode 100644
index 0000000..e21c40a
--- /dev/null
+++ b/game/code/roads/roadsegment.cpp
@@ -0,0 +1,792 @@
+/*===========================================================================
+ Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+
+ Component: RoadSegment
+
+ Description:
+
+
+ Authors: Travis Brown-John
+
+ Revisions Date Author Revision
+ 2002/02/25 Tbrown-John Created
+
+===========================================================================*/
+
+#include <roads/road.h>
+#include <roads/roadsegment.h>
+#include <roads/roadsegmentdata.h>
+
+RoadSegment::RoadSegment()
+:
+mRoad( NULL ),
+mSegmentIndex( 0 )
+{
+}
+
+RoadSegment::~RoadSegment( void )
+{
+}
+
+/*
+void RoadSegment::Init( RoadSegmentData* rsd, rmt::Matrix& hierarchy, float scaleAlongFacing )
+{
+ ////////////////////////////////////////////////////////////////
+ // Transform segment data based on given matrix & scale-along-facing
+ //
+ rmt::Vector vector;
+
+ // First, do the corners and the edgenormals
+ for( int i=0; i<4; i++ )
+ {
+ // transform the corner
+ vector = rsd->GetCorner( i );
+ vector.z *= scaleAlongFacing;
+ vector.Transform( hierarchy );
+ mCorners[ i ] = vector;
+
+ // transform the edge normals
+ vector = rsd->GetEdgeNormal( i );
+ vector.Rotate( hierarchy );
+ mEdgeNormals[ i ] = vector;
+ }
+
+ // Now, transform the segment normal
+ vector = rsd->GetSegmentNormal();
+ vector.Rotate( hierarchy );
+ mNormal = vector;
+
+ // Now, calculate and store segment length
+ rmt::Vector segStart = (mCorners[0] + mCorners[3]) * 0.5f;
+ rmt::Vector segEnd = (mCorners[1] + mCorners[2]) * 0.5f;
+ mfSegmentLength = (segEnd - segStart).Length(); // *** SQUARE ROOT! ***
+
+ // Now, calculate and store the bounding sphere
+ rmt::Box3D box;
+ GetBoundingBox( &box ); // find the box on the fly (based on extents of corners)
+
+ // compute & store the bounding sphere based on bbox
+ rmt::Vector vectorBetween;
+ vectorBetween = ( box.high - box.low ) * 0.5f;
+ mSphere.centre = box.low + vectorBetween;
+ mSphere.radius = vectorBetween.Magnitude(); // *** SQUARE ROOT! ***
+
+
+
+ //////////////////////////////
+ // TODO:
+ // This stuff is dubious. It won't be accurate given that we can no
+ // longer assume interior and exterior edges are parallel.
+ //
+ // Now, Calculate the width of the leading edge of the segment.
+ float fWidth = segStart.Magnitude();
+ mfLaneWidth = fWidth / (float)rsd->GetNumLanes();
+
+ // Calculate a turn radius.
+ //
+ float fCosTheta = mEdgeNormals[0].DotProduct( mEdgeNormals[1] );
+ if ( fCosTheta < 0.0f )
+ {
+ fCosTheta = 0.0f - fCosTheta;
+ }
+ if ( fCosTheta < 0.001f ) //Clamp me.
+ {
+ fCosTheta = 0.0f;
+ }
+
+ rmt::Vector temp;
+ temp.Sub( mCorners[0], mCorners[1] );
+ float fInteriorEdgeLength = temp.Magnitude( );
+ temp.Sub( mCorners[2], mCorners[3] );
+ float fExteriorEdgeLength = temp.Magnitude( );
+
+ if ( fCosTheta != 0.0f )
+ {
+ // take the shortest length.
+ float length = ( fInteriorEdgeLength < fExteriorEdgeLength )? fInteriorEdgeLength : fExteriorEdgeLength;
+ length = length / 2.0f;
+ mfRadius = length / fCosTheta;
+ mfAngle = rmt::PI_BY2 - rmt::ACos( fCosTheta );
+ //rmt::RadianToDeg( mfAngle );
+ }
+ else
+ {
+ // Not a curved segment.
+ //
+ mfRadius = 0.0f;
+ mfAngle = 0.0f;
+ }
+}
+*/
+void RoadSegment::Init( RoadSegmentData* rsd, rmt::Matrix& hierarchy, float scaleAlongFacing )
+{
+ ////////////////////////////////////////////////////////////////
+ // Transform segment data based on given matrix & scale-along-facing
+ //
+ rmt::Vector vector;
+
+ // store the unmodified values
+ for( int i=0; i<4; i++ )
+ {
+ vector = rsd->GetCorner( i );
+ mCorners[ i ] = vector;
+
+ vector = rsd->GetEdgeNormal( i );
+ mEdgeNormals[ i ] = vector;
+ }
+
+
+ //////////////////////////////
+ // TODO:
+ // This stuff is dubious. It won't be accurate given that we can no
+ // longer assume interior and exterior edges are parallel. AND
+ // somehow it's able to work quite accurately from the corner
+ // and edgenormal values that have not yet been transformed. *shudder*
+ //
+ // Now, Calculate the width of the leading edge of the segment.
+ float fWidth = ((mCorners[0] + mCorners[3]) * 0.5f).Magnitude();
+ mfLaneWidth = fWidth / (float)rsd->GetNumLanes();
+
+ // Calculate a turn radius.
+ //
+ float fCosTheta = mEdgeNormals[0].DotProduct( mEdgeNormals[1] );
+ if ( fCosTheta < 0.0f )
+ {
+ fCosTheta = 0.0f - fCosTheta;
+ }
+ if ( fCosTheta < 0.001f ) //Clamp me.
+ {
+ fCosTheta = 0.0f;
+ }
+
+ rmt::Vector temp;
+ temp.Sub( mCorners[0], mCorners[1] );
+ float fInteriorEdgeLength = temp.Magnitude( );
+ temp.Sub( mCorners[2], mCorners[3] );
+ float fExteriorEdgeLength = temp.Magnitude( );
+
+ if ( fCosTheta != 0.0f )
+ {
+ // take the shortest length.
+ float length = ( fInteriorEdgeLength < fExteriorEdgeLength )? fInteriorEdgeLength : fExteriorEdgeLength;
+ length = length / 2.0f;
+ mfRadius = length / fCosTheta;
+ mfAngle = rmt::PI_BY2 - rmt::ACos( fCosTheta );
+ //rmt::RadianToDeg( mfAngle );
+ }
+ else
+ {
+ // Not a curved segment.
+ //
+ mfRadius = 0.0f;
+ mfAngle = 0.0f;
+ }
+ ///////////////////////////////////////////
+
+
+
+ // Ok, so first, transform the corners and the edgenormals
+ for( int i=0; i<4; i++ )
+ {
+ // transform the corner
+ vector = rsd->GetCorner( i );
+ vector.z *= scaleAlongFacing;
+ vector.Transform( hierarchy );
+ mCorners[ i ] = vector;
+
+ // transform the edge normals
+ vector = rsd->GetEdgeNormal( i );
+ vector.Rotate( hierarchy );
+ mEdgeNormals[ i ] = vector;
+ }
+
+ // Now, transform the segment normal
+ vector = rsd->GetSegmentNormal();
+ vector.Rotate( hierarchy );
+ mNormal = vector;
+
+ // Now, calculate and store segment length
+ rmt::Vector segStart = (mCorners[0] + mCorners[3]) * 0.5f;
+ rmt::Vector segEnd = (mCorners[1] + mCorners[2]) * 0.5f;
+ mfSegmentLength = (segEnd - segStart).Length(); // *** SQUARE ROOT! ***
+
+ // Now, calculate and store the bounding sphere
+ rmt::Box3D box;
+ GetBoundingBox( &box ); // find the box on the fly (based on extents of corners)
+
+ // compute & store the bounding sphere based on bbox
+ rmt::Vector vectorBetween;
+ vectorBetween = ( box.high - box.low ) * 0.5f;
+ mSphere.centre = box.low + vectorBetween;
+ mSphere.radius = vectorBetween.Magnitude(); // *** SQUARE ROOT! ***
+
+
+}
+
+
+void RoadSegment::GetBoundingBox(rmt::Box3D* box)
+{
+ // get axis-aligned bounding box from vertices...
+ unsigned int numVertices = 4;
+ rmt::Vector vertex;
+ unsigned int i = 0;
+ for ( i = 0; i < numVertices; i++ )
+ {
+ vertex = mCorners[i];
+ if ( 0 == i )
+ {
+ // This is the first time.
+ // Initialize to some value.
+ //
+ box->low = box->high = vertex;
+ }
+ else
+ {
+ if ( box->low.x > vertex.x )
+ {
+ box->low.x = vertex.x;
+ }
+ if ( box->low.y > vertex.y )
+ {
+ box->low.y = vertex.y;
+ }
+ if ( box->low.z > vertex.z )
+ {
+ box->low.z = vertex.z;
+ }
+
+ if ( box->high.x < vertex.x )
+ {
+ box->high.x = vertex.x;
+ }
+ if ( box->high.y < vertex.y )
+ {
+ box->high.y = vertex.y;
+ }
+ if ( box->high.z < vertex.z )
+ {
+ box->high.z = vertex.z;
+ }
+ }
+ }
+}
+
+void RoadSegment::GetBoundingSphere(rmt::Sphere* sphere)
+{
+ *sphere = mSphere;
+}
+
+
+
+
+/*
+==============================================================================
+RoadSegment::CalculateUnitDistIntoRoadSegment
+==============================================================================
+Description: Adapted from GameGems Article by Steven Ranck. pp412-pp420.
+ Implements a fast and simple algm for determing where a point
+ in between the edges of a 2D quad (RoadSegment). The result
+ is a unit floting point number, where 0 indicates that the point
+ lies on the leading edge, and where 1 indicates that the point
+ lies on the opposite edge. The RoadSegment may be any 4 sided
+ 2D convex shape.
+
+Constraints: The RoadSegment must be convex and have 4 sides.
+ The RoadSegment must have a non zero area.
+ The point must lie within the sector. ***** What if it doesn't?
+
+Parameters: ( float fPointX, float fPointZ )
+
+Return: A scalar from 0 to 1.
+ 0 if point lies on the leading edge.
+ 1 if point lies on the trailing edge.
+ Smoothly interpolated value for all points in between.
+
+=============================================================================
+*/
+float RoadSegment::CalculateUnitDistIntoRoadSegment( float fPointX, float fPointZ )
+{
+ rmt::Vector VLP, VTP;
+ float fDotL, fDotT;
+
+ // Get and cache the leading edge top corner
+ // and the trailing edge bottom corner.
+ //
+ rmt::Vector vertices[ 2 ];
+ GetCorner( 0, vertices[ 0 ] );
+ GetCorner( 2, vertices[ 1 ] );
+
+ // Get and cache the leading edge normal
+ // and the trailing edge normal.
+ //
+ rmt::Vector unitNormals[ 2 ];
+ GetEdgeNormal( 0, unitNormals[ 0 ] );
+ GetEdgeNormal( 2, unitNormals[ 1 ] );
+
+ //for this to work, the normals must both point into
+ //the volume...
+ //so, I need to reverse the second edge normal
+ unitNormals[1] *= -1;
+
+ // Compute vector from point on Leading Edge to P:
+ //
+ VLP.x = fPointX - vertices[0].x;
+ VLP.y = 0.0f;
+ VLP.z = fPointZ - vertices[0].z;
+
+ // Compute vector from point on Trailing Edge to P:
+ //
+ VTP.x = fPointX - vertices[1].x;
+ VTP.y = 0.0f;
+ VTP.z = fPointZ - vertices[1].z;
+
+ // Compute (VLP dot Leading Edge Normal):
+ //
+ fDotL = VLP.x*unitNormals[0].x + VLP.z*unitNormals[0].z;
+
+ // Compute (VTP dot Trailing Edge Normal):
+ //
+ fDotT = VTP.x*unitNormals[1].x + VTP.z*unitNormals[1].z;
+
+ // Compute unit distance into sector and return it:
+ //
+ return ( fDotL / (fDotL + fDotT) );
+}
+
+float RoadSegment::CalculateUnitHeightInRoadSegment( float fPointX, float fPointZ )
+{
+ rmt::Vector VLP, VTP;
+ float fDotL, fDotT;
+
+ // Get and cache the leading edge top corner
+ // and the trailing edge bottom corner.
+ //
+ rmt::Vector vertices[ 2 ];
+ GetCorner( 0, vertices[ 0 ] );
+ GetCorner( 2, vertices[ 1 ] );
+
+ // Get and cache the leading edge normal
+ // and the trailing edge normal.
+ //
+ rmt::Vector unitNormals[ 2 ];
+ GetEdgeNormal( 1, unitNormals[ 0 ] );
+ GetEdgeNormal( 3, unitNormals[ 1 ] );
+
+ // Compute vector from point on Leading Edge to P:
+ //
+ VLP.x = fPointX - vertices[0].x;
+ VLP.y = 0.0f;
+ VLP.z = fPointZ - vertices[0].z;
+
+ // Compute vector from point on Trailing Edge to P:
+ //
+ VTP.x = fPointX - vertices[1].x;
+ VTP.y = 0.0f;
+ VTP.z = fPointZ - vertices[1].z;
+
+ // Compute (VLP dot Leading Edge Normal):
+ //
+ fDotL = VLP.x*unitNormals[0].x + VLP.z*unitNormals[0].z;
+
+ // Compute (VTP dot Trailing Edge Normal):
+ //
+ fDotT = VTP.x*unitNormals[1].x + VTP.z*unitNormals[1].z;
+
+ // Compute unit distance into sector and return it:
+ //
+ return ( fDotL / (fDotL + fDotT) );
+}
+
+
+/*
+float RoadSegment::CalculateYHeight( float fPointX, float fPointZ )
+{
+ // Get and cache the leading edge top corner
+ // and the trailing edge bottom corner.
+ //
+ rmt::Vector vertices[ 2 ];
+ GetCorner( 0, vertices[ 0 ] );
+ GetCorner( 2, vertices[ 1 ] );
+
+ float fDistance = CalculateUnitDistIntoRoadSegment( fPointX, fPointZ );
+ float y = LERP( fDistance, vertices[ 0 ].y, vertices[ 1 ].y );
+ return y;
+}
+*/
+
+void RoadSegment::GetPosition( float t, float w, rmt::Vector* pos )
+{
+ // Get and cache the corners.
+ //
+ rmt::Vector vertices[ 4 ];
+ GetCorner( 0, vertices[ 0 ] );
+ GetCorner( 1, vertices[ 1 ] );
+ GetCorner( 2, vertices[ 2 ] );
+ GetCorner( 3, vertices[ 3 ] );
+
+ rmt::Vector position;
+
+ // Interpolate the Normal vector across the Segment.
+ //
+ rmt::Vector leadingEdge;
+ leadingEdge.Sub( vertices[ 3 ], vertices[ 0 ] );
+ rmt::Vector leadingPoint = leadingEdge;
+ leadingPoint.Scale( w );
+ leadingPoint.Add( vertices[ 0 ] );
+
+ rmt::Vector trailingEdge;
+ trailingEdge.Sub( vertices[ 2 ], vertices[ 1 ] );
+ rmt::Vector trailingPoint = trailingEdge;
+ trailingPoint.Scale( w );
+ trailingPoint.Add( vertices[ 1 ] );
+
+ position.Sub( trailingPoint, leadingPoint );
+ position.Scale( t );
+ position.Add( leadingPoint );
+ *pos = position;
+}
+
+
+void RoadSegment::GetLaneLocation( float t, int index, rmt::Vector& position, rmt::Vector& facing )
+{
+ //
+ // Get the world space point and facing at time 't'.
+ //
+ // Interpolate the facing.
+ //
+ rmt::Vector facingNormals[ 2 ];
+ GetEdgeNormal( 0, facingNormals[ 0 ] );
+ GetEdgeNormal( 2, facingNormals[ 1 ] );
+ facing.x = LERP( t, facingNormals[ 0 ].x, facingNormals[ 1 ].x );
+ facing.y = LERP( t, facingNormals[ 0 ].y, facingNormals[ 1 ].y );
+ facing.z = LERP( t, facingNormals[ 0 ].z, facingNormals[ 1 ].z );
+
+ // [Dusit: July 6th, 2003]
+ // NOTE:
+ // This is the CORRECT way to produce the lane length value when
+ // the width of the roadsegment isn't guaranteed across its length.
+ // The only thing that it assumes (and is always correct) is that
+ // each lane is as wide as the other lanes at any given point along
+ // the length of the segment
+ //
+ float edgeT = ((float)(index<<1) + 1.0f) / ((float)(GetNumLanes()<<1));
+
+ // find start & end points of the lane
+ rmt::Vector vec0, vec1, vec2, vec3;
+
+ GetCorner( 0, vec0 );
+ GetCorner( 1, vec1 );
+ GetCorner( 2, vec2 );
+ GetCorner( 3, vec3 );
+
+ // lane indices go from 0 to n, right to left ( n <=== 0 )
+ rmt::Vector bottomEdgeDir = vec0 - vec3; // points frm 3 to 0
+ rmt::Vector topEdgeDir = vec1 - vec2; // points frm 2 to 1
+
+ // now we figure out the starting point and ending point of the
+ // lane segment
+ rmt::Vector start = vec3 + bottomEdgeDir * edgeT;
+ rmt::Vector end = vec2 + topEdgeDir * edgeT;
+
+ // now find the t position along the lane
+ rmt::Vector laneDir = end - start;
+ position = start + laneDir * t;
+
+ /*
+
+ // Get and cache the corners.
+ //
+ rmt::Vector vertices[ 4 ];
+ GetCorner( 0, vertices[ 0 ] );
+ GetCorner( 1, vertices[ 1 ] );
+ GetCorner( 2, vertices[ 2 ] );
+ GetCorner( 3, vertices[ 3 ] );
+
+ //There is an assumption here that the road does not get wider or thinner
+ //across its length...
+ // Scale unnormalized vector by normalized center of desired lane.
+ // ( ( index * fLaneWidth ) + ( fLaneWidth / 2.0f ) ) / roadWidth;
+ //
+ float fCentreOfLane = ( index * mfLaneWidth ) + ( mfLaneWidth / 2.0f );
+
+ //I call this parametric variable w;
+ float w = fCentreOfLane / GetRoadWidth();
+
+ // Interpolate the Normal vector across the Segment.
+ //
+ rmt::Vector leadingEdge;
+ leadingEdge.Sub( vertices[ 0 ], vertices[ 3 ] );
+ rmt::Vector leadingPoint = leadingEdge;
+ leadingPoint.Scale( w );
+ leadingPoint.Add( vertices[ 3 ] );
+
+
+ rmt::Vector trailingEdge;
+ trailingEdge.Sub( vertices[ 1 ], vertices[ 2 ] );
+ rmt::Vector trailingPoint = trailingEdge;
+ trailingPoint.Scale( w );
+ trailingPoint.Add( vertices[ 2 ] );
+
+ //This gives the point between the leading and trailing point by parameter t.
+ position.x = LERP( t, leadingPoint.x, trailingPoint.x );
+ position.y = LERP( t, leadingPoint.y, trailingPoint.y );
+ position.z = LERP( t, leadingPoint.z, trailingPoint.z );
+ */
+
+
+}
+
+
+/*
+//==============================================================================
+//RoadSegment::GetJoinPoint
+//==============================================================================
+//Description: RoadSegmentData pieces are always joined at the left corner
+// of the trailing edge. The normal of the leading edge of the
+// new piece is always the inverse normal of the trailing edge
+// of the previous piece.
+//
+// This function returns the join vertex and facing in world space.
+//
+//Parameters: ( rmt::Vector& position, rmt::Vector& facing )
+//
+//Return: void
+//
+//=============================================================================
+void RoadSegment::GetJoinPoint( rmt::Vector& position, rmt::Vector& facing )
+{
+ // All segments are joined at the left top corner.
+ //
+ position = GetRoadSegmentData( )->GetCorner( 1 );
+
+ facing = GetRoadSegmentData( )->GetEdgeNormal( 2 );
+ facing.Scale( -1.0f );
+}
+*/
+
+void RoadSegment::GetCorner( int index, rmt::Vector& out )
+{
+ rAssert( 0 <= index && index < 4 );
+ out = mCorners[ index ];
+}
+void RoadSegment::GetEdgeNormal( int index, rmt::Vector& out )
+{
+ rAssert( 0 <= index && index < 4 );
+ out = mEdgeNormals[ index ];
+}
+void RoadSegment::GetSegmentNormal( rmt::Vector& out )
+{
+ out = mNormal;
+}
+unsigned int RoadSegment::GetNumLanes( void )
+{
+ return GetRoad()->GetNumLanes();
+}
+float RoadSegment::GetLaneLength( unsigned int lane )
+{
+ // [Dusit: July 6th, 2003]
+ // NOTE:
+ // This is the CORRECT way to produce the lane length value when
+ // the width of the roadsegment isn't guaranteed across its length.
+ // The only thing that it assumes (and is always correct) is that
+ // each lane is as wide as the other lanes at any given point along
+ // the length of the segment
+ //
+ // The problem with using this is that it requires a Sqrt (because
+ // it's too late for us to rearrange the data so that we can avoid
+ // this). So we continue using the old way because we're never off
+ // by more than 5 centimeters anyway.
+ //
+ float edgeT = ((float)(lane<<1) + 1.0f) / ((float)(GetNumLanes()<<1));
+
+ // find start & end points of the lane
+ rmt::Vector vec0, vec1, vec2, vec3;
+
+ GetCorner( 0, vec0 );
+ GetCorner( 1, vec1 );
+ GetCorner( 2, vec2 );
+ GetCorner( 3, vec3 );
+
+ // lane indices go from 0 to n, right to left ( n <=== 0 )
+ rmt::Vector bottomEdgeDir = vec0 - vec3; // points frm 3 to 0
+ rmt::Vector topEdgeDir = vec1 - vec2; // points frm 2 to 1
+
+ rmt::Vector start = vec3 + bottomEdgeDir * edgeT;
+ rmt::Vector end = vec2 + topEdgeDir * edgeT;
+
+ float expectedLength = (end - start).Magnitude();
+ return expectedLength;
+
+ /*
+ float computedLength = 0.0f;
+
+ // TODO:
+ // Because we can't assume that a lane is constant-width, this
+ // code is totally bogus...
+ if ( mfAngle > 0.0f )
+ {
+ // 2*PI*r for a circle.
+ // Theta*r for arc of theta degrees.
+ //
+ float fLaneOffset = mfLaneWidth * lane + mfLaneWidth;
+ computedLength = 2.0f * mfAngle * ( mfRadius + fLaneOffset );
+ }
+ else
+ {
+ // it's not a curve, both edge lengths will be equal.
+ //
+ computedLength = mfSegmentLength;
+ }
+
+ //rAssert( rmt::Epsilon( computedLength, expectedLength, 0.01f ) );
+ return computedLength;
+ */
+}
+
+float RoadSegment::GetRoadWidth()
+{
+ return mRoad->GetNumLanes() * mfLaneWidth;
+}
+
+
+/*
+
+/////////////////////////////////////////////////////////////////
+// TransformRoadSegment
+/////////////////////////////////////////////////////////////////
+//
+TransformRoadSegment::TransformRoadSegment() :
+ RoadSegment( NULL )
+{
+ mTransform.Identity();
+ mfScaleAlongFacing = 0.0f;
+}
+
+TransformRoadSegment::TransformRoadSegment(
+ const RoadSegmentData* pRoadSegmentData,
+ rmt::Matrix& transform )
+ :
+ RoadSegment( pRoadSegmentData )
+{
+ mTransform = transform;
+}
+
+TransformRoadSegment::TransformRoadSegment(
+ const RoadSegmentData* pRoadSegmentData,
+ rmt::Matrix& transform,
+ float fScaleAlongFacing )
+ :
+ RoadSegment( pRoadSegmentData ),
+ mfScaleAlongFacing( fScaleAlongFacing )
+{
+ mTransform = transform;
+}
+
+TransformRoadSegment::TransformRoadSegment(
+ const RoadSegmentData* pRoadSegmentData,
+ const rmt::Vector& facing,
+ const rmt::Vector& position )
+ :
+ RoadSegment( pRoadSegmentData )
+{
+ rmt::Vector sApproximateUp( 0.0f, 1.0f, 0.0f );
+
+ mTransform.Identity( );
+ mTransform.FillHeading( facing, sApproximateUp );
+ mTransform.FillTranslate( position );
+}
+
+TransformRoadSegment::TransformRoadSegment(
+ const RoadSegmentData* pRoadSegmentData,
+ const rmt::Vector& facing,
+ const rmt::Vector& position,
+ float fScaleAlongFacing )
+ :
+ RoadSegment( pRoadSegmentData ),
+ mfScaleAlongFacing( fScaleAlongFacing )
+{
+ rmt::Vector sApproximateUp( 0.0f, 1.0f, 0.0f );
+
+ mTransform.Identity( );
+ mTransform.FillHeading( facing, sApproximateUp );
+ mTransform.FillTranslate( position );
+}
+void TransformRoadSegment::GetCorner( int index, rmt::Vector& out ) const
+{
+ out = GetRoadSegmentData( )->GetCorner( index );
+ out.z *= mfScaleAlongFacing;
+ out.Transform( mTransform );
+}
+void TransformRoadSegment::GetEdgeNormal( int index, rmt::Vector& out ) const
+{
+ out = GetRoadSegmentData( )->GetEdgeNormal( index );
+ out.Rotate( mTransform );
+}
+void TransformRoadSegment::GetSegmentNormal( rmt::Vector& out ) const
+{
+ out = GetRoadSegmentData( )->GetSegmentNormal( );
+ out.Rotate( mTransform );
+}
+
+//==============================================================================
+//TransformRoadSegment::GetJoinPoint
+//==============================================================================
+//Description: RoadSegmentData pieces are always joined at the left corner
+// of the trailing edge. The normal of the leading edge of the
+// new piece is always the inverse normal of the trailing edge
+// of the previous piece.
+//
+// This function returns the join vertex and facing in world space.
+//
+//Parameters: ( rmt::Vector& position, rmt::Vector& facing )
+//
+//Return: void
+//
+//=============================================================================
+void TransformRoadSegment::GetJoinPoint( rmt::Vector& position, rmt::Vector& facing ) const
+{
+ // All segments are joined at the left top corner.
+ //
+ facing = GetRoadSegmentData( )->GetEdgeNormal( 2 );
+ facing.Scale( -1.0f );
+ facing.Rotate( mTransform );
+
+ position = GetRoadSegmentData( )->GetCorner( 1 );
+ position.z *= mfScaleAlongFacing;
+ position.Transform( mTransform );
+}
+void TransformRoadSegment::GetBoundingBox(rmt::Box3D* box)
+{
+ GetRoadSegmentData()->GetBoundingBox( *box );
+ (*box).high.z *= mfScaleAlongFacing;
+ (*box).high.Transform( mTransform );
+ (*box).low.Transform( mTransform );
+}
+void TransformRoadSegment::GetBoundingSphere(rmt::Sphere* sphere)
+{
+ GetRoadSegmentData( )->GetBoundingSphere( *sphere );
+ (*sphere).radius *= mfScaleAlongFacing;
+ (*sphere).centre.Transform( mTransform );
+}
+
+void TransformRoadSegment::GetBoundingBox( rmt::Box3D& out ) const
+{
+ GetRoadSegmentData( )->GetBoundingBox( out );
+ out.high.z *= mfScaleAlongFacing;
+ out.high.Transform( mTransform );
+ out.low.Transform( mTransform );
+}
+
+void TransformRoadSegment::GetBoundingSphere( rmt::Sphere& out ) const
+{
+ out = GetRoadSegmentData( )->GetBoundingSphere( );
+ out.radius *= mfScaleAlongFacing;
+ out.centre.Transform( mTransform );
+}
+
+void TransformRoadSegment::GetTransform( rmt::Matrix &out ) const
+{
+ out = mTransform;
+}
+
+*/ \ No newline at end of file
diff --git a/game/code/roads/roadsegment.h b/game/code/roads/roadsegment.h
new file mode 100644
index 0000000..9adbe7a
--- /dev/null
+++ b/game/code/roads/roadsegment.h
@@ -0,0 +1,267 @@
+#ifndef ROADSEGMENT_H_
+#define ROADSEGMENT_H_
+
+#ifdef WORLD_BUILDER
+#include "../render/DSG/IEntityDSG.h"
+#elif !defined(TOOLS)
+#include <render/DSG/IEntityDSG.h>
+#else
+#include <p3d/entity.hpp>
+#define IEntityDSG tEntity
+#endif
+
+#include <string.h>
+#include <radmath/radmath.hpp>
+
+class Road;
+class RoadSegmentData;
+
+
+////////////////////////////////////////////////////////////////////////
+// class RoadSegment adapted from GameGems article by Steven Ranck. pp 412-420.
+////////////////////////////////////////////////////////////////////////
+// Concrete Base = 8 bytes.
+//
+class RoadSegment :
+ public IEntityDSG
+{
+public:
+ RoadSegment();
+ virtual ~RoadSegment( void );
+
+ void Init( RoadSegmentData* rsd, rmt::Matrix& hierarchy, float scaleAlongFacing );
+
+ /////////////////////////////////////////////
+ // ACCESSORS
+ /////////////////////////////////////////////
+ void GetCorner( int index, rmt::Vector& out );
+ void GetEdgeNormal( int index, rmt::Vector& out );
+ void GetSegmentNormal( rmt::Vector& out );
+
+ void SetSegmentIndex( unsigned int index );
+ unsigned int GetSegmentIndex();
+
+ Road* GetRoad();
+ void SetRoad( Road* road );
+
+ float GetSegmentLength();
+
+ // for DEBUG
+ const char* GetName();
+ void SetName( const char* name );
+
+ float GetLaneWidth();
+ /////////////////////////////////////////////
+
+
+ float CalculateUnitDistIntoRoadSegment( float fPointX, float fPointZ );
+ float CalculateUnitHeightInRoadSegment( float fPointX, float fPointZ );
+ //float CalculateYHeight( float fPointX, float fPointZ );
+ void GetLaneLocation( float t, int index, rmt::Vector& position, rmt::Vector& facing );
+
+
+
+ unsigned int GetNumLanes( void ); // ask its parent road for the number of lanes
+ float GetRoadWidth(); // number of lanes (obtained from parent Road) times lane-width
+
+ //void GetJoinPoint( rmt::Vector& position, rmt::Vector& facing );
+ void GetPosition( float t, float w, rmt::Vector* pos );
+ float GetLaneLength( unsigned int lane );
+
+ // Temp method to display.
+ //
+ void Render( const tColour& colour );
+ void RenderAnywhere( const tColour& colour );
+
+
+ //////////////////////////////////////////////////////////////////////////
+ // IEntityDSG Interface
+ //////////////////////////////////////////////////////////////////////////
+ void DisplayBoundingBox(tColour colour = tColour(0,255,0));
+ void DisplayBoundingSphere(tColour colour = tColour(0,255,0));
+ void GetBoundingBox(rmt::Box3D* box);
+ void GetBoundingSphere(rmt::Sphere* sphere);
+ void Display();
+ rmt::Vector* pPosition();
+ const rmt::Vector& rPosition();
+ void GetPosition( rmt::Vector* ipPosn );
+ ///////////////////////////////////////////////////////////////////////////
+
+
+protected:
+ Road* mRoad;
+ unsigned int mSegmentIndex;
+
+private:
+
+#ifdef RAD_DEBUG
+ enum { MAX_NAME_LEN = 40 };
+ char mName[ MAX_NAME_LEN];
+#endif
+
+ rmt::Vector mCorners[ 4 ]; // The rectangle with extra vertices at the leading and trailing edge midpoints.
+ rmt::Vector mEdgeNormals[ 4 ]; // The inward pointing edge normals.
+ rmt::Vector mNormal; // The surface normal.
+
+ float mfSegmentLength;
+ float mfLaneWidth;
+ float mfRadius;
+ float mfAngle;
+ rmt::Sphere mSphere;
+
+};
+
+inline const char* RoadSegment::GetName()
+{
+#ifdef TOOLS
+ return tEntity::GetName();
+#endif
+
+#ifdef RAD_DEBUG
+ return mName;
+#else
+ return NULL;
+#endif
+}
+inline void RoadSegment::SetName( const char* name )
+{
+#ifdef TOOLS
+ tEntity::SetName( name );
+#endif
+#ifdef RAD_DEBUG
+ strncpy( mName, name, MAX_NAME_LEN > strlen(name) ? strlen(name) : MAX_NAME_LEN );
+ mName[strlen(name)] = '\0';
+#endif
+}
+
+inline Road* RoadSegment::GetRoad()
+{
+ return mRoad;
+}
+inline void RoadSegment::SetRoad( Road* road )
+{
+ mRoad = road;
+}
+inline void RoadSegment::SetSegmentIndex( unsigned int index )
+{
+ mSegmentIndex = index;
+}
+inline unsigned int RoadSegment::GetSegmentIndex()
+{
+ return mSegmentIndex;
+}
+inline void RoadSegment::DisplayBoundingBox(tColour colour)
+{
+ rAssert( false );
+}
+inline void RoadSegment::DisplayBoundingSphere(tColour colour)
+{
+ rAssert( false );
+}
+inline void RoadSegment::Display()
+{
+ rAssert( false ); // deprecated
+}
+inline rmt::Vector* RoadSegment::pPosition()
+{
+ rAssert( false ); // deprecated; result unreliable
+ return &(mSphere.centre);
+}
+inline const rmt::Vector& RoadSegment::rPosition()
+{
+ rAssert( false ); // deprecated; result unreliable
+ return mSphere.centre;
+}
+inline void RoadSegment::GetPosition( rmt::Vector* ipPosn )
+{
+ *ipPosn = mSphere.centre;
+}
+inline float RoadSegment::GetSegmentLength()
+{
+ return mfSegmentLength;
+}
+inline float RoadSegment::GetLaneWidth()
+{
+ return mfLaneWidth;
+}
+
+
+
+/*
+
+///////////////////////////////////////////////////////////////////////////
+// class TransformRoadSegment
+///////////////////////////////////////////////////////////////////////////
+// Derived class = Base + 16*4 bytes
+//
+class TransformRoadSegment
+:
+public RoadSegment
+{
+public:
+ TransformRoadSegment();
+
+ TransformRoadSegment( const RoadSegmentData* pRoadSegmentData,
+ rmt::Matrix& transform
+ );
+
+ TransformRoadSegment( const RoadSegmentData* pRoadSegmentData,
+ rmt::Matrix& transform,
+ float fScaleAlongFacing
+ );
+
+ TransformRoadSegment( const RoadSegmentData* pRoadSegmentData,
+ const rmt::Vector& facing,
+ const rmt::Vector& position
+ );
+
+ TransformRoadSegment( const RoadSegmentData* pRoadSegmentData,
+ const rmt::Vector& facing,
+ const rmt::Vector& position,
+ float fScaleAlongFacing
+ );
+
+ virtual void GetTransform( rmt::Matrix& out ) const;
+ void SetTransform( rmt::Matrix& transform );
+ void SetScaleAlongFacing( float scale );
+
+ /////////////////////////////////////////////////
+ // Implements RoadSegment.
+ //
+ void GetCorner( int index, rmt::Vector& out ) const;
+ void GetEdgeNormal( int index, rmt::Vector& out ) const;
+ void GetSegmentNormal( rmt::Vector& out ) const;
+ //void GetJoinPoint( rmt::Vector& position, rmt::Vector& facing ) const;
+ virtual void GetBoundingBox( rmt::Box3D& out ) const;
+ virtual void GetBoundingSphere( rmt::Sphere& out ) const;
+
+ //////////////////////////////////////////////////////////////////////////
+ // IEntityDSG Interface
+ //////////////////////////////////////////////////////////////////////////
+ virtual void GetBoundingBox(rmt::Box3D* box);
+ virtual void GetBoundingSphere(rmt::Sphere* sphere);
+ void GetPosition( rmt::Vector* ipPosn );
+
+private:
+ rmt::Matrix mTransform;
+
+ float mfScaleAlongFacing;
+};
+
+inline void TransformRoadSegment::SetTransform( rmt::Matrix& transform )
+{
+ mTransform = transform;
+}
+inline void TransformRoadSegment::SetScaleAlongFacing( float scale )
+{
+ mfScaleAlongFacing = scale;
+}
+inline void TransformRoadSegment::GetPosition( rmt::Vector* ipPosn )
+{
+ *ipPosn = GetRoadSegmentData()->GetBoundingSphere().centre;
+ ipPosn->Transform( mTransform );
+}
+*/
+
+
+#endif \ No newline at end of file
diff --git a/game/code/roads/roadsegmentdata.cpp b/game/code/roads/roadsegmentdata.cpp
new file mode 100644
index 0000000..ff27bca
--- /dev/null
+++ b/game/code/roads/roadsegmentdata.cpp
@@ -0,0 +1,211 @@
+/*===========================================================================
+ Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+
+ Component: RoadSegment
+
+ Description:
+
+
+ Authors: Travis Brown-John
+
+ Revisions Date Author Revision
+ 2002/02/25 Tbrown-John Created
+
+===========================================================================*/
+#include <memory/classsizetracker.h>
+#include <roads/roadsegmentdata.h>
+
+// System.
+//
+#include <raddebug.hpp>
+
+/*
+
+ Input parameters:
+
+ O----------> v0
+ | |
+ | |
+ | |
+ | |
+ v v
+ v2 v1
+
+ O = ( 0.0f, 0.0f, 0.0f ) LCS origin.
+
+ # of lanes.
+
+ create this:
+
+ |
+ N1 \ /
+ v0_________________________v1----> facing
+ | |
+ -> N0 | --> direction | -> N2
+ |_________________________|
+ v3 v2
+ N3 / \
+ |
+ N is UP
+ N0 is ->
+
+ N1 is |
+ \ /
+
+ N2 is ->
+
+ N3 is / \
+ |
+ */
+RoadSegmentData::RoadSegmentData() :
+ mnLanes( 0 )
+{
+ CLASSTRACKER_CREATE( RoadSegmentData );
+ unsigned int i;
+ for ( i = 0; i < 4; ++i )
+ {
+ mCorners[i].Set(0.0f, 0.0f, 0.0f);
+ mEdgeNormals[i].Set(0.0f, 0.0f, 0.0f);
+ }
+
+ mNormal.Set(0.0f, 0.0f, 0.0f);
+
+}
+
+RoadSegmentData::RoadSegmentData(
+ const rmt::Vector& v0,
+ const rmt::Vector& v1,
+ const rmt::Vector& v2,
+ unsigned int nLanes )
+{
+ CLASSTRACKER_CREATE( RoadSegmentData );
+ SetData( v0, v1, v2, nLanes );
+}
+
+RoadSegmentData::~RoadSegmentData()
+{
+ CLASSTRACKER_DESTROY( RoadSegmentData );
+}
+
+//=============================================================================
+// RoadSegmentData::SetData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& v0, const rmt::Vector& v1, const rmt::Vector& v2, unsigned int nLanes, bool hasShoulder )
+//
+// Return: void
+//
+//=============================================================================
+void RoadSegmentData::SetData( const rmt::Vector& v0,
+ const rmt::Vector& v1,
+ const rmt::Vector& v2,
+ unsigned int nLanes )
+{
+ mnLanes = nLanes;
+
+ // Calculate the up vector for this piece.
+ //
+ mNormal.CrossProduct( v0, v2 );
+ mNormal.Normalize( );
+
+ rmt::Vector origin( 0.0f, 0.0f, 0.0f );
+
+ mCorners[ 0 ] = origin;
+ mCorners[ 1 ] = v0;
+ mCorners[ 2 ] = v1;
+ mCorners[ 3 ] = v2;
+
+ rmt::Vector temp;
+
+ // Calculate the edge normals.
+ temp.Sub( mCorners[ 0 ], mCorners[ 3 ] );
+ mEdgeNormals[ 0 ].CrossProduct( mNormal, temp );
+ mEdgeNormals[ 0 ].Normalize( );
+
+ temp.Sub( mCorners[ 1 ], mCorners[ 0 ] );
+ mEdgeNormals[ 1 ].CrossProduct( mNormal, temp );
+ mEdgeNormals[ 1 ].Normalize( );
+
+ temp.Sub( mCorners[ 1 ], mCorners[ 2 ] );
+ mEdgeNormals[ 2 ].CrossProduct( mNormal, temp );
+ mEdgeNormals[ 2 ].Normalize( );
+
+ temp.Sub( mCorners[ 3 ], mCorners[ 2 ] );
+ mEdgeNormals[ 3 ].CrossProduct( mNormal, temp );
+ mEdgeNormals[ 3 ].Normalize( );
+
+
+}
+
+/*
+==============================================================================
+RoadSegmentData::GetCorner
+==============================================================================
+Description: Comment
+
+
+Parameters: ( int index ), which box corner are we interested in.
+
+Return: const rmt::Vector& - a copy of the vertex location in local space.
+
+=============================================================================
+*/
+const rmt::Vector& RoadSegmentData::GetCorner( int index ) const
+{
+ rAssert( index < 4 );
+ return mCorners[ index ];
+}
+/*
+==============================================================================
+RoadSegmentData::GetEdgeNormal
+==============================================================================
+Description: Return the normal to the edge. Define CW, starting
+ with leading edge. v4.Sub( v0 ) = leading edge.
+
+Parameters: ( int index )
+
+Return: const
+
+=============================================================================
+*/
+const rmt::Vector& RoadSegmentData::GetEdgeNormal( int index ) const
+{
+ rAssert( index < 4 );
+ return mEdgeNormals[ index ];
+}
+
+/*
+==============================================================================
+RoadSegmentData::GetSegmentNormal
+==============================================================================
+Description: Return the normal to the planar segment surface.
+
+Parameters: ( void )
+
+Return: const rmt::Vector&
+
+=============================================================================
+*/
+const rmt::Vector& RoadSegmentData::GetSegmentNormal( void ) const
+{
+ return mNormal;
+}
+
+/*
+==============================================================================
+RoadSegmentData::GetNumLanes
+==============================================================================
+Description: Return the number of lanes, on the left side if bIsLeft is true.
+ Else, the number of lanes on the right side.
+
+Parameters: ( void )
+
+Return: unsigned int, the number of lanes.
+
+=============================================================================
+*/
+unsigned int RoadSegmentData::GetNumLanes( void ) const
+{
+ return mnLanes;
+}
diff --git a/game/code/roads/roadsegmentdata.h b/game/code/roads/roadsegmentdata.h
new file mode 100644
index 0000000..0073748
--- /dev/null
+++ b/game/code/roads/roadsegmentdata.h
@@ -0,0 +1,61 @@
+#ifndef ROADSEGMENTDATA_H_
+#define ROADSEGMENTDATA_H_
+
+#include <radmath/radmath.hpp>
+#include <p3d/entity.hpp>
+
+#define LERP( fUnit, fV0, fV1 ) ( (1.0f-(fUnit))*fV0 + (fUnit)*fV1 )
+
+class RoadSegmentData
+{
+public:
+ RoadSegmentData();
+
+ RoadSegmentData( const rmt::Vector& v0,
+ const rmt::Vector& v1,
+ const rmt::Vector& v2,
+ unsigned int nLanes );
+
+ ~RoadSegmentData();
+ void SetData( const rmt::Vector& v0,
+ const rmt::Vector& v1,
+ const rmt::Vector& v2,
+ unsigned int nLanes );
+
+ const rmt::Vector& GetCorner( int index ) const;
+ const rmt::Vector& GetEdgeNormal( int index ) const;
+ const rmt::Vector& GetSegmentNormal( void ) const;
+ unsigned int GetNumLanes( void ) const;
+
+ void SetName( const char* name );
+ tUID GetNameUID() const;
+
+private:
+ // The rectangle with extra vertices at the leading and trailing
+ // edge midpoints.
+ //
+ rmt::Vector mCorners[ 4 ];
+
+ // The inward pointing edge normals.
+ //
+ rmt::Vector mEdgeNormals[ 4 ];
+
+ // The surface normal.
+ //
+ rmt::Vector mNormal;
+
+ unsigned int mnLanes;
+
+ tUID nameUID;
+};
+
+inline void RoadSegmentData::SetName( const char* name )
+{
+ nameUID = tEntity::MakeUID( name );
+}
+inline tUID RoadSegmentData::GetNameUID() const
+{
+ return nameUID;
+}
+
+#endif \ No newline at end of file
diff --git a/game/code/roads/trafficcontrol.cpp b/game/code/roads/trafficcontrol.cpp
new file mode 100644
index 0000000..8ef2908
--- /dev/null
+++ b/game/code/roads/trafficcontrol.cpp
@@ -0,0 +1,98 @@
+/*===========================================================================
+ Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+
+ Component: Traffic Control
+
+ Description:
+
+
+ Authors: Travis Brown-John
+
+ Revisions Date Author Revision
+ 2001/02/02 Tbrown-John Created
+
+===========================================================================*/
+
+#include <roads/trafficcontrol.h>
+#include <roads/intersection.h>
+
+void TrafficLight::Update( unsigned int dt )
+{
+ m_uiElapsedTime += dt;
+
+ if ( m_uiElapsedTime > m_uiSwitchTime )
+ {
+ m_uiSwitchTime = m_uiElapsedTime + TrafficLightSwitchTime;
+ SwitchControl();
+ }
+}
+
+void TrafficLight::SwitchControl( void )
+{
+ m_tState++;
+
+ m_tState = m_tState % NUM_STATES;
+
+ if ( ADVANCE_GREEN == m_tState )
+ {
+ // we must have rolled through all the other states, so change the direction.
+ // do a bitwise not.
+ m_tRoadsToGo = ~m_tRoadsToGo;
+ // mask out the high bits, for clarity.
+ m_tRoadsToGo = m_tRoadsToGo & 0x0000000F;
+ }
+ // notify the intersection of the control change.
+ m_pIntersection->AdvanceTraffic( m_tRoadsToGo, m_tState );
+}
+
+
+
+
+void NWayStop::Update( unsigned int dt )
+{
+ //
+ // After elapsed time, tell next road to go. The road should tell all
+ // of its lanes to go.
+ //
+ Intersection* intersection = (Intersection*)m_pIntersection;
+ if( intersection->mWaitingRoads.mUseSize > 0 )
+ {
+ if( mTurnTimeLeft < 0 )
+ {
+ mTurnTimeLeft = NWAY_TURN_MILLISECONDS;
+
+ // tell the road to go...
+ //rAssert( m_pIntersection->GetType() == Intersection::N_WAY );
+
+ // damn constants
+ ((Intersection*)m_pIntersection)->AdvanceNextWaitingRoad();
+ }
+ else
+ {
+ mTurnTimeLeft -= dt;
+ }
+ }
+ else
+ {
+ mTurnTimeLeft = NWAY_TURN_MILLISECONDS;
+ }
+ /*
+ if ( m_pIntersection->IsIntersectionClear() )
+ {
+ m_tState++;
+
+ m_tState = m_tState % NUM_STATES;
+
+ // do a bitwise not.
+ m_tRoadsToGo = ~m_tRoadsToGo;
+ // mask out the high bits, for clarity.
+ m_tRoadsToGo &= 0x0000000F;
+
+ m_pIntersection->AdvanceTraffic( m_tRoadsToGo, m_tState );
+ }
+ */
+}
+
+void CourtesyStop::Update( unsigned int dt )
+{
+} \ No newline at end of file
diff --git a/game/code/roads/trafficcontrol.h b/game/code/roads/trafficcontrol.h
new file mode 100644
index 0000000..93cc4b5
--- /dev/null
+++ b/game/code/roads/trafficcontrol.h
@@ -0,0 +1,106 @@
+/*===========================================================================
+ Copyright (C) 2000 Radical Entertainment Ltd. All rights reserved.
+
+ Component: Traffic Control
+
+ Description:
+
+
+ Authors: Travis Brown-John
+
+ Revisions Date Author Revision
+ 2001/02/02 Tbrown-John Created
+
+===========================================================================*/
+
+#ifndef TRAFFICCONTROL_HPP_
+#define TRAFFICCONTROL_HPP_
+
+class Intersection;
+
+// An Object that controls the flow of traffic through an intersection.
+class TrafficControl
+{
+public:
+ enum
+ {
+ RED,
+ YELLOW,
+ GREEN,
+ ADVANCE_GREEN,
+ NUM_STATES
+ };
+ enum
+ {
+ ROAD_ZERO = 1 << 0,
+ ROAD_ONE = 1 << 1,
+ ROAD_TWO = 1 << 2,
+ ROAD_THREE = 1 << 3
+ };
+ // constructor.
+ TrafficControl() : m_pIntersection( 0 ) {}
+ // destructor.
+ virtual ~TrafficControl() {}
+ // returns which intersection this traffic control belongs to.
+ const Intersection *GetIntersection( void ) const { return m_pIntersection; }
+ // sets which intersection this traffic control belongs to.
+ void SetIntersection( const Intersection *pIntersection ) { m_pIntersection = pIntersection; }
+
+ // update the traffic control state. Each one follows different logic.
+ virtual void Update( unsigned int dt ) = 0;
+protected:
+ // the intersection this traffic control belongs to.
+ const Intersection *m_pIntersection;
+ // the state of the traffic light in the green direction. All other directions STOP.
+ unsigned int m_tState;
+ // a bitmask representing which roads the traffic control advance commands applies to.
+ unsigned int m_tRoadsToGo;
+
+};
+
+// Allows flow of traffic through a 4 way intersection in 2 opposing, non crossing roads at a time.
+class TrafficLight
+:
+public TrafficControl
+{
+public:
+ TrafficLight() : TrafficControl(), m_uiElapsedTime( 0 ), m_uiSwitchTime( 0 ) {}
+ void Update( unsigned int dt );
+private:
+ void SwitchControl( void );
+ enum eTrafficLightSwitchTime
+ {
+ TrafficLightSwitchTime = 10000
+ };
+ // the elapsed time.
+ unsigned int m_uiElapsedTime;
+ // the control signal should switch when the m_uiElapsedTime reaches this value.
+ unsigned int m_uiSwitchTime;
+};
+
+// Allows flow of traffic through an N way intersection, 2 lanes at a time in a CW order.
+class NWayStop
+:
+public TrafficControl
+{
+public:
+ NWayStop() : TrafficControl(), mTurnTimeLeft( NWAY_TURN_MILLISECONDS ) {}
+ void Update( unsigned int dt );
+ enum eTrafficTurnTime
+ {
+ NWAY_TURN_MILLISECONDS = 3500
+ };
+private:
+ int mTurnTimeLeft;
+};
+
+// advances cars when intersection is clear. stopped cars are advanced in a CW order.
+class CourtesyStop
+:
+public TrafficControl
+{
+public:
+ void Update( unsigned int dt );
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/sound/allsound.cpp b/game/code/sound/allsound.cpp
new file mode 100644
index 0000000..9d10b15
--- /dev/null
+++ b/game/code/sound/allsound.cpp
@@ -0,0 +1,7 @@
+#include <sound/listener.cpp>
+#include <sound/simpsonssoundplayer.cpp>
+#include <sound/soundcluster.cpp>
+#include <sound/soundloader.cpp>
+#include <sound/soundmanager.cpp>
+#include <sound/soundrenderercallback.cpp>
+#include <sound/positionalsoundplayer.cpp>
diff --git a/game/code/sound/avatar/allsoundavatar.cpp b/game/code/sound/avatar/allsoundavatar.cpp
new file mode 100644
index 0000000..ff0df8f
--- /dev/null
+++ b/game/code/sound/avatar/allsoundavatar.cpp
@@ -0,0 +1,5 @@
+#include <sound/avatar/avatarsoundplayer.cpp>
+#include <sound/avatar/carsoundparameters.cpp>
+#include <sound/avatar/soundavatar.cpp>
+#include <sound/avatar/vehiclesoundplayer.cpp>
+#include <sound/avatar/vehiclesounddebugpage.cpp> \ No newline at end of file
diff --git a/game/code/sound/avatar/avatarsoundplayer.cpp b/game/code/sound/avatar/avatarsoundplayer.cpp
new file mode 100644
index 0000000..4f6df3a
--- /dev/null
+++ b/game/code/sound/avatar/avatarsoundplayer.cpp
@@ -0,0 +1,187 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: avatarsoundplayer.cpp
+//
+// Description: Implementation of AvatarSoundPlayer, which directs the
+// playing of avatar-related sounds
+//
+// History: 30/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <radfactory.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/avatar/avatarsoundplayer.h>
+
+#include <mission/gameplaymanager.h>
+#include <worldsim/avatarmanager.h>
+
+#include <sound/avatar/soundavatar.h>
+#include <sound/avatar/carsoundparameters.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// AvatarSoundPlayer::AvatarSoundPlayer
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AvatarSoundPlayer::AvatarSoundPlayer()
+{
+ int i;
+
+ for( i = 0; i < MAX_PLAYERS; i++ )
+ {
+ m_avatars[i] = NULL;
+ }
+}
+
+//==============================================================================
+// AvatarSoundPlayer::~AvatarSoundPlayer
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AvatarSoundPlayer::~AvatarSoundPlayer()
+{
+ int i;
+
+ for( i = 0; i < MAX_PLAYERS; i++ )
+ {
+ if( m_avatars[i] != NULL )
+ {
+ delete m_avatars[i];
+ }
+ }
+}
+
+//=============================================================================
+// AvatarSoundPlayer::Initialize
+//=============================================================================
+// Description: Do the initialization stuff for the avatar sound subsystem
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void AvatarSoundPlayer::Initialize()
+{
+ //
+ // Must register the carSoundParameters factory method with RadScript before
+ // the script creating these objects gets run
+ //
+ ::radFactoryRegister( "carSoundParameters", (radFactoryOutParamProc*) ::CarSoundParameterObjCreate );
+}
+
+//=============================================================================
+// AvatarSoundPlayer::UpdateOncePerFrame
+//=============================================================================
+// Description: Update function. Used for stuff that either doesn't need to
+// be called often and/or uses expensive math.
+//
+// Parameters: elapsedTime - time since last frame in msecs
+//
+// Return: void
+//
+//=============================================================================
+void AvatarSoundPlayer::UpdateOncePerFrame( unsigned int elapsedTime )
+{
+ int i;
+
+ for( i = 0; i < MAX_PLAYERS; i++ )
+ {
+ if( m_avatars[i] != NULL )
+ {
+ m_avatars[i]->UpdateOncePerFrame( elapsedTime );
+ }
+ }
+}
+
+//=============================================================================
+// AvatarSoundPlayer::OnBeginGameplay
+//=============================================================================
+// Description: Create SoundAvatar objects when gameplay begins
+//
+// Parameters: None
+//
+// Return: true if player 0 in car, false otherwise
+//
+//=============================================================================
+bool AvatarSoundPlayer::OnBeginGameplay()
+{
+ unsigned int i;
+ unsigned int numPlayers = GetGameplayManager()->GetNumPlayers();
+
+ for( i = 0; i < numPlayers; i++ )
+ {
+ rAssert( m_avatars[i] == NULL );
+
+ m_avatars[i] = new(GMA_LEVEL_AUDIO) SoundAvatar( GetAvatarManager()->GetAvatarForPlayer( i ) );
+ }
+
+ return( GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar() );
+}
+
+//=============================================================================
+// AvatarSoundPlayer::OnEndGameplay
+//=============================================================================
+// Description: Destroy SoundAvatar objects when gameplay ends
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void AvatarSoundPlayer::OnEndGameplay()
+{
+ int i;
+
+ //
+ // Destroy the avatar trackers, since we won't have avatars anymore until
+ // gameplay restarts
+ //
+ for( i = 0; i < MAX_PLAYERS; i++ )
+ {
+ if( m_avatars[i] != NULL )
+ {
+ delete m_avatars[i];
+ m_avatars[i] = NULL;
+ }
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/sound/avatar/avatarsoundplayer.h b/game/code/sound/avatar/avatarsoundplayer.h
new file mode 100644
index 0000000..ce8bacd
--- /dev/null
+++ b/game/code/sound/avatar/avatarsoundplayer.h
@@ -0,0 +1,63 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: avatarsoundplayer.h
+//
+// Description: Declaration of AvatarSoundPlayer class, which directs the
+// playing of avatar-related sounds
+//
+// History: 30/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef AVATARSOUNDPLAYER_H
+#define AVATARSOUNDPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <constants/maxplayers.h>
+
+//========================================
+// Forward References
+//========================================
+
+class SoundAvatar;
+
+//=============================================================================
+//
+// Synopsis: AvatarSoundPlayer
+//
+//=============================================================================
+
+class AvatarSoundPlayer
+{
+ public:
+ AvatarSoundPlayer();
+ virtual ~AvatarSoundPlayer();
+
+ void Initialize();
+
+ void UpdateOncePerFrame( unsigned int elapsedTime );
+
+ //
+ // Returns true if first player in car, false otherwise
+ //
+ bool OnBeginGameplay();
+
+ void OnEndGameplay();
+
+ private:
+ //Prevent wasteful constructor creation.
+ AvatarSoundPlayer( const AvatarSoundPlayer& original );
+ AvatarSoundPlayer& operator=( const AvatarSoundPlayer& rhs );
+
+ //
+ // One SoundAvatar to track activity of each Avatar object
+ //
+ SoundAvatar* m_avatars[MAX_PLAYERS];
+};
+
+
+#endif // AVATARSOUNDPLAYER_H
+
diff --git a/game/code/sound/avatar/carsoundparameters.cpp b/game/code/sound/avatar/carsoundparameters.cpp
new file mode 100644
index 0000000..870c13f
--- /dev/null
+++ b/game/code/sound/avatar/carsoundparameters.cpp
@@ -0,0 +1,1184 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: carsoundparameters.cpp
+//
+// Description: Implement carSoundParameters
+//
+// History: 01/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <string.h>
+
+#include <raddebugwatch.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/avatar/carsoundparameters.h>
+
+#include <memory/srrmemory.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Initialially the list is empty
+//
+carSoundParameters* radLinkedClass< carSoundParameters >::s_pLinkedClassHead = NULL;
+carSoundParameters* radLinkedClass< carSoundParameters >::s_pLinkedClassTail = NULL;
+
+static float s_maxPitchDefault = 5.0f;
+static float s_inAirIdleDefault = 0.2f;
+static float s_inAirThrottleDefault = 2.0f;
+static float s_powerslideMinDefault = 0.2f;
+static float s_powerslideMaxDefault = 2.0f;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// carSoundParameters::carSoundParameters
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+carSoundParameters::carSoundParameters() :
+ radRefCount( 0 ),
+ m_clipRPM( 3000 ),
+ m_engineClipName( NULL ),
+ m_idleClipName( NULL ),
+ m_damagedClipName( NULL ),
+ m_hornClipName( NULL ),
+ m_carOpenClipName( NULL ),
+ m_carCloseClipName( NULL ),
+ m_overlayClipName( NULL ),
+ m_roadSkidClipName( NULL ),
+ m_dirtSkidClipName( NULL ),
+ m_backupClipName( NULL ),
+ m_downshiftDamper( 0.05f ),
+ m_attackTime( 100 ),
+ m_delayTime( 50 ),
+ m_decayTime( 200 ),
+ m_decayFinishTrim( 0.75f ),
+ m_maxReverseKmh( 50.0f ),
+ m_minReversePitch( 1.5f ),
+ m_maxReversePitch( 3.0f ),
+ m_damageStartPcnt( 0.4f ),
+ m_damageVolumeRange( m_damageStartPcnt ),
+ m_damageStartTrim( 0.0f ),
+ m_damageMaxTrim( 1.0f ),
+ m_idleEnginePitch( 1.0f ),
+ m_inAirThrottlePitch( s_inAirThrottleDefault ),
+ m_inAirIdlePitch( s_inAirIdleDefault ),
+ m_inAirResponseMsecs( 500 ),
+ m_burnoutMinPitch( 0.5f ),
+ m_burnoutMaxPitch( 1.3f ),
+ m_powerslideMinPitch( s_powerslideMinDefault ),
+ m_powerslideMaxPitch( s_powerslideMaxDefault ),
+ m_msecsPerOctave( 500 )
+{
+ unsigned int i;
+
+ //
+ // Shift point defaults
+ //
+ m_shiftPoints[0] = 0.01f;
+ m_shiftPoints[1] = 0.30f;
+ m_shiftPoints[2] = 0.45f;
+ m_shiftPoints[3] = 0.70f;
+ m_shiftPoints[4] = 0.85f;
+ m_shiftPoints[5] = 0.95f;
+
+ m_shiftPoints[MAX_GEARS] = 1.0f;
+
+ //
+ // Pitch range and gear shift defaults
+ //
+ for( i = 0; i < MAX_GEARS; i++ )
+ {
+ m_minPitch[i] = 0.5f;
+ m_maxPitch[i] = s_maxPitchDefault;
+
+ m_gearShiftPitchDrop[i] = 0.2f;
+ }
+}
+
+//==============================================================================
+// carSoundParameters::~carSoundParameters
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+carSoundParameters::~carSoundParameters()
+{
+ if( m_engineClipName != NULL )
+ {
+ delete [] m_engineClipName;
+ }
+ if( m_idleClipName != NULL )
+ {
+ delete [] m_idleClipName;
+ }
+ if( m_damagedClipName != NULL )
+ {
+ delete [] m_damagedClipName;
+ }
+ if( m_hornClipName != NULL )
+ {
+ delete [] m_hornClipName;
+ }
+ if( m_carOpenClipName != NULL )
+ {
+ delete [] m_carOpenClipName;
+ }
+ if( m_carCloseClipName != NULL )
+ {
+ delete [] m_carCloseClipName;
+ }
+ if( m_overlayClipName != NULL )
+ {
+ delete [] m_overlayClipName;
+ }
+ if( m_roadSkidClipName != NULL )
+ {
+ delete [] m_roadSkidClipName;
+ }
+ if( m_dirtSkidClipName != NULL )
+ {
+ delete [] m_dirtSkidClipName;
+ }
+ if( m_backupClipName != NULL )
+ {
+ delete [] m_backupClipName;
+ }
+}
+
+//=============================================================================
+// carSoundParameters::SetWatcherName
+//=============================================================================
+// Description: Set the name of this object, since it's usually stored
+// in radScript's data structures. FOR DEBUGGING ONLY. We use
+// this to give the car a name for Watcher tuning.
+//
+// Parameters: name - name of car
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetWatcherName( const char* name )
+{
+#ifndef RAD_RELEASE
+ unsigned int i;
+ char label[50];
+ char watcherGroup[50];
+
+ sprintf( watcherGroup, "Sound::%s", name );
+
+ //
+ // Register members with Watcher for tweakage
+ //
+ for( i = 1; i <= MAX_GEARS; i++ )
+ {
+ sprintf( label, "Min pitch: gear %d", i );
+ radDbgWatchAddFloat( &m_minPitch[i-1], label, watcherGroup, NULL, NULL, 0.0f, 10.0f );
+
+ sprintf( label, "Max pitch: gear %d", i );
+ radDbgWatchAddFloat( &m_maxPitch[i-1], label, watcherGroup, NULL, NULL, 0.0f, 10.0f );
+
+ sprintf( label, "Pitch drop: gear %d", i );
+ radDbgWatchAddFloat( &m_gearShiftPitchDrop[i-1], label, watcherGroup, NULL, NULL, 0.0f, 10.0f );
+ }
+
+ for( i = 1; i <= MAX_GEARS + 1; i++ )
+ {
+ sprintf( label, "Shift point: gear %d", i );
+ radDbgWatchAddFloat( &m_shiftPoints[i-1], label, watcherGroup );
+ }
+
+ radDbgWatchAddFloat( &m_downshiftDamper, "Downshift damper", watcherGroup );
+ radDbgWatchAddFloat( &m_attackTime, "Attack time (msecs)", watcherGroup, NULL, NULL, 0, 2000.0f );
+ radDbgWatchAddUnsignedInt( &m_delayTime, "Delay time (msecs)", watcherGroup, NULL, NULL, 0, 2000 );
+ radDbgWatchAddFloat( &m_decayTime, "Decay time (msecs)", watcherGroup, NULL, NULL, 0, 2000.0f );
+ radDbgWatchAddFloat( &m_decayFinishTrim, "Decay finish trim", watcherGroup );
+ radDbgWatchAddFloat( &m_maxReverseKmh, "Max reverse kmh", watcherGroup, NULL, NULL, 0.0f, 200.0f );
+ radDbgWatchAddFloat( &m_minReversePitch, "Min reverse pitch", watcherGroup, NULL, NULL, 0.0f, 10.0f );
+ radDbgWatchAddFloat( &m_maxReversePitch, "Max reverse pitch", watcherGroup, NULL, NULL, 0.0f, 10.0f );
+ radDbgWatchAddFloat( &m_damageStartPcnt, "Damage start", watcherGroup );
+ radDbgWatchAddFloat( &m_damageVolumeRange, "Damage max volume life %", watcherGroup );
+ radDbgWatchAddFloat( &m_damageStartTrim, "Damage start trim", watcherGroup );
+ radDbgWatchAddFloat( &m_damageMaxTrim, "Damage max trim", watcherGroup );
+ radDbgWatchAddFloat( &m_idleEnginePitch, "Idle engine pitch", watcherGroup, NULL, NULL, 0.0f, 10.0f );
+ radDbgWatchAddFloat( &m_inAirThrottlePitch, "In air throttle pitch", watcherGroup, NULL, NULL, 0.0f, 10.0f );
+ radDbgWatchAddFloat( &m_inAirIdlePitch, "In air idle pitch", watcherGroup, NULL, NULL, 0.0f, 10.0f );
+ radDbgWatchAddUnsignedInt( &m_inAirResponseMsecs, "In air response time (msecs)", watcherGroup, NULL, NULL, 0, 2000 );
+#endif
+}
+
+//=============================================================================
+// carSoundParameters::SetShiftPoint
+//=============================================================================
+// Description: Set the percentage of top speed at which we shift to a
+// given gear
+//
+// Parameters: gear - gear to be shifted to
+// lowPercent - percentage of top speed at which we downshift
+// highPercent - percentage of top speed at which we upshift
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetShiftPoint( unsigned int gear, float percent )
+{
+ rAssert( gear > 0 );
+ rAssert( gear <= MAX_GEARS );
+ rAssert( percent > 0.0f );
+ rAssert( percent <= 1.0f );
+
+ m_shiftPoints[gear-1] = percent;
+}
+
+//=============================================================================
+// carSoundParameters::GetShiftPoint
+//=============================================================================
+// Description: Get the percentage of top speed at which we shift up from a
+// given gear
+//
+// Parameters: gear - gear we're currently in
+//
+// Return: shift point as percentage of top speed
+//
+//=============================================================================
+float carSoundParameters::GetShiftPoint( int gear )
+{
+ if( gear > 0 )
+ {
+ return( m_shiftPoints[gear-1] );
+ }
+ else
+ {
+ return( 0.0f );
+ }
+}
+
+//=============================================================================
+// carSoundParameters::SetAttackTimeMsecs
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float msecs )
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetAttackTimeMsecs( float msecs )
+{
+ m_attackTime = msecs;
+}
+
+//=============================================================================
+// carSoundParameters::SetDelayTimeMsecs
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int msecs )
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetDelayTimeMsecs( unsigned int msecs )
+{
+ m_delayTime = msecs;
+}
+
+//=============================================================================
+// carSoundParameters::SetDecayTimeMsecs
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float msecs )
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetDecayTimeMsecs( float msecs )
+{
+ m_decayTime = msecs;
+}
+
+//=============================================================================
+// carSoundParameters::SetDecayFinishTrim
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float trim )
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetDecayFinishTrim( float trim )
+{
+ m_decayFinishTrim = trim;
+}
+
+//=============================================================================
+// carSoundParameters::SetDownshiftDamperSize
+//=============================================================================
+// Description: Amount that we can drop below the shift point to avoid
+// downshifting
+//
+// Parameters: percent - percentage of top speed that acts as the buffer
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetDownshiftDamperSize( float percent )
+{
+ rAssert( percent >= 0.0f );
+ rAssert( percent <= 1.0f );
+
+ m_downshiftDamper = percent;
+}
+
+//=============================================================================
+// carSoundParameters::SetEngineClipName
+//=============================================================================
+// Description: Store the name of the sound resource we're using for engine
+// sounds
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetEngineClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ m_engineClipName = new char[strlen(clipName)+1];
+ strcpy( m_engineClipName, clipName );
+
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+}
+
+//=============================================================================
+// carSoundParameters::SetEngineIdleClipName
+//=============================================================================
+// Description: Store the name of the sound resource we're using for engine
+// idle sounds
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetEngineIdleClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ m_idleClipName = new char[strlen(clipName)+1];
+ strcpy( m_idleClipName, clipName );
+
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+}
+
+//=============================================================================
+// carSoundParameters::SetDamagedEngineClipName
+//=============================================================================
+// Description: Store the name of the sound resource we're using for damaged
+// engine sounds
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetDamagedEngineClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ m_damagedClipName = new char[strlen(clipName)+1];
+ strcpy( m_damagedClipName, clipName );
+
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+}
+
+//=============================================================================
+// carSoundParameters::SetHornClipName
+//=============================================================================
+// Description: Store the name of the sound resource we're using for horn
+// sounds
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetHornClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ m_hornClipName = new char[strlen(clipName)+1];
+ strcpy( m_hornClipName, clipName );
+
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+}
+
+//=============================================================================
+// carSoundParameters::SetCarDoorOpenClipName
+//=============================================================================
+// Description: Store the name of the sound resource we're using for car
+// door opening sounds
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetCarDoorOpenClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ m_carOpenClipName = new char[strlen(clipName)+1];
+ strcpy( m_carOpenClipName, clipName );
+
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+}
+
+//=============================================================================
+// carSoundParameters::SetCarDoorCloseClipName
+//=============================================================================
+// Description: Store the name of the sound resource we're using for car
+// door closing sounds
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetCarDoorCloseClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ m_carCloseClipName = new char[strlen(clipName)+1];
+ strcpy( m_carCloseClipName, clipName );
+
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+}
+
+//=============================================================================
+// carSoundParameters::SetOverlayClipName
+//=============================================================================
+// Description: Store the name of the sound resource we want to play along
+// with the specified engine sound. Is optional.
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetOverlayClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ m_overlayClipName = new char[strlen(clipName)+1];
+ strcpy( m_overlayClipName, clipName );
+
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+}
+
+//=============================================================================
+// carSoundParameters::SetRoadSkidClipName
+//=============================================================================
+// Description: Store the name of the sound resource we want to play when
+// the car skids on the road. Is optional.
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetRoadSkidClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ m_roadSkidClipName = new char[strlen(clipName)+1];
+ strcpy( m_roadSkidClipName, clipName );
+
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+}
+
+//=============================================================================
+// carSoundParameters::SetDirtSkidClipName
+//=============================================================================
+// Description: Store the name of the sound resource we want to play when
+// the car skids. Is optional.
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetDirtSkidClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ m_dirtSkidClipName = new char[strlen(clipName)+1];
+ strcpy( m_dirtSkidClipName, clipName );
+
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+}
+
+//=============================================================================
+// carSoundParameters::SetBackupClipName
+//=============================================================================
+// Description: Store the name of the sound resource we want to play when
+// the car goes into reverse. Is optional.
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetBackupClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+
+ m_backupClipName = new char[strlen(clipName)+1];
+ strcpy( m_backupClipName, clipName );
+
+ HeapMgr()->PopHeap(GMA_PERSISTENT);
+}
+
+//=============================================================================
+// carSoundParameters::SetGearPitchRange
+//=============================================================================
+// Description: Set pitch range for engine clip for given gear
+//
+// Parameters: gear - gear to apply range to
+// min - pitch at lowest gear RPMs
+// max - pitch at highest gear RPMs
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetGearPitchRange( unsigned int gear, float min, float max )
+{
+ rAssert( gear > 0 );
+ rAssert( gear <= MAX_GEARS );
+ rAssert( max >= min );
+
+ m_minPitch[gear-1] = min;
+ m_maxPitch[gear-1] = max;
+}
+
+//=============================================================================
+// carSoundParameters::SetNumberOfGears
+//=============================================================================
+// Description: Like the name says
+//
+// Parameters: gear - number of gears we want, must be <= MAX_GEARS
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetNumberOfGears( unsigned int gear )
+{
+ rAssert( gear <= MAX_GEARS );
+
+ //
+ // If we set the shift point for the given gear to 1.0f, we're
+ // effectively cutting the number of gears
+ //
+ m_shiftPoints[gear] = 1.0f;
+}
+
+//=============================================================================
+// carSoundParameters::SetReversePitchCapKmh
+//=============================================================================
+// Description: Set maximum reverse speed at which we'll increase engine
+// pitch
+//
+// Parameters: speed - max reverse speed
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetReversePitchCapKmh( float speed )
+{
+ rAssert( speed > 0.0f );
+
+ m_maxReverseKmh = speed;
+}
+
+//=============================================================================
+// carSoundParameters::SetReversePitchRange
+//=============================================================================
+// Description: Set range of engine pitch for reverse gear
+//
+// Parameters: min - pitch at rest
+// max - pitch at m_maxReverseKmh
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetReversePitchRange( float min, float max )
+{
+ rAssert( min < max );
+
+ m_minReversePitch = min;
+ m_maxReversePitch = max;
+}
+
+//=============================================================================
+// carSoundParameters::SetGearShiftPitchDrop
+//=============================================================================
+// Description: Set amount of engine clip pitch drop we'll do on gear shifts
+//
+// Parameters: gear - gear that we want to set drop for
+// drop - amount of pitch drop
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetGearShiftPitchDrop( unsigned int gear, float drop )
+{
+ rAssert( gear > 0 );
+ rAssert( gear <= MAX_GEARS );
+ rAssert( drop >= 0.0f );
+
+ m_gearShiftPitchDrop[gear-1] = drop;
+}
+
+//=============================================================================
+// carSoundParameters::GetGearShiftPitchDrop
+//=============================================================================
+// Description: Get amount of engine clip pitch drop we'll do on gear shifts
+//
+// Parameters: gear - gear that we want drop for
+//
+// Return: value of pitch drop
+//
+//=============================================================================
+float carSoundParameters::GetGearShiftPitchDrop( unsigned int gear )
+{
+ rAssert( gear > 0 );
+ rAssert( gear <= MAX_GEARS );
+
+ return( m_gearShiftPitchDrop[gear-1] );
+}
+
+//=============================================================================
+// carSoundParameters::SetDamageStartPcnt
+//=============================================================================
+// Description: Set vehicle life percentage at which we'll start playing
+// damage sounds
+//
+// Parameters: damagePercent - vehicle life percentage for damage sound
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetDamageStartPcnt( float damagePercent )
+{
+ rAssert( damagePercent >= 0.0f );
+ rAssert( damagePercent <= 1.0f );
+
+ // Also need to adjust damage volume range when start pcnt changes
+ m_damageVolumeRange += damagePercent - m_damageStartPcnt;
+
+ m_damageStartPcnt = damagePercent;
+}
+
+//=============================================================================
+// carSoundParameters::SetDamageMaxVolPcnt
+//=============================================================================
+// Description: Set vehicle life percentage at which the damage clip will
+// be played at maximum volume (linear interpolation between
+// m_damageStartPcnt and m_damageMaxVolPcnt).
+//
+// Parameters: percent - vehicle life percentage for maximum volume
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetDamageMaxVolPcnt( float percent )
+{
+ rAssert( percent >= 0.0f );
+ rAssert( percent <= 1.0f );
+
+ //
+ // We don't actually store the percentage, since the trim setting code
+ // only cares about the min/max range; might as well precalculate it
+ m_damageVolumeRange = m_damageStartPcnt - percent;
+}
+
+//=============================================================================
+// carSoundParameters::SetDamageStartTrim
+//=============================================================================
+// Description: Set volume for damage clip when vehicle life percentage
+// reaches damage start percentage
+//
+// Parameters: trim - initial volume
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetDamageStartTrim( float trim )
+{
+ rAssert( trim >= 0.0f );
+ rAssert( trim <= 1.0f );
+
+ m_damageStartTrim = trim;
+}
+
+//=============================================================================
+// carSoundParameters::SetDamageMaxTrim
+//=============================================================================
+// Description: Set volume for damage clip when vehicle life percentage
+// reaches damage max percentage
+//
+// Parameters: trim - initial volume
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetDamageMaxTrim( float trim )
+{
+ rAssert( trim >= 0.0f );
+ rAssert( trim <= 1.0f );
+
+ m_damageMaxTrim = trim;
+}
+
+//=============================================================================
+// carSoundParameters::SetIdleEnginePitch
+//=============================================================================
+// Description: Set pitch value for idle engine clip
+//
+// Parameters: pitch - obvious
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetIdleEnginePitch( float pitch )
+{
+ m_idleEnginePitch = pitch;
+}
+
+//=============================================================================
+// carSoundParameters::SetInAirThrottlePitch
+//=============================================================================
+// Description: Set pitch that engine goes to when gas applied in air
+//
+// Parameters: pitch - engine pitch
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetInAirThrottlePitch( float pitch )
+{
+ m_inAirThrottlePitch = pitch;
+}
+
+//=============================================================================
+// carSoundParameters::GetInAirThrottlePitch
+//=============================================================================
+// Description: Get pitch that engine goes to when gas applied in air
+//
+// Parameters: None
+//
+// Return: full-throttle pitch
+//
+//=============================================================================
+float carSoundParameters::GetInAirThrottlePitch()
+{
+ //
+ // POST-BETA HACK!!
+ //
+ if( m_inAirIdlePitch == s_inAirIdleDefault )
+ {
+ //
+ // Untuned, d'oh. Make something up.
+ //
+ m_inAirThrottlePitch = GetRevLimit();
+ }
+
+ return( m_inAirThrottlePitch );
+}
+
+//=============================================================================
+// carSoundParameters::SetInAirIdlePitch
+//=============================================================================
+// Description: Set pitch that engine goes to when engine idling in air
+//
+// Parameters: pitch - engine pitch
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetInAirIdlePitch( float pitch )
+{
+ m_inAirIdlePitch = pitch;
+}
+
+//=============================================================================
+// carSoundParameters::GetInAirIdlePitch
+//=============================================================================
+// Description: Get pitch that engine goes to when engine idling in air
+//
+// Parameters: None
+//
+// Return: idle pitch
+//
+//=============================================================================
+float carSoundParameters::GetInAirIdlePitch()
+{
+ //
+ // POST-BETA HACK!!
+ //
+ if( m_inAirIdlePitch == s_inAirIdleDefault )
+ {
+ //
+ // Untuned, d'oh. Make something up as percentage of rev limit
+ //
+ m_inAirIdlePitch = 0.5f * GetRevLimit();
+ }
+
+ return( m_inAirIdlePitch );
+}
+
+//=============================================================================
+// carSoundParameters::SetInAirThrottleResponseTimeMsecs
+//=============================================================================
+// Description: Set amount of time to go from throttle pitch to idle pitch
+// in midair and vice versa
+//
+// Parameters: msecs - time in milliseconds
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetInAirThrottleResponseTimeMsecs( unsigned int msecs )
+{
+ m_inAirResponseMsecs = msecs;
+}
+
+//=============================================================================
+// carSoundParameters::SetBurnoutMinPitch
+//=============================================================================
+// Description: Set pitch for engine when burnout factor is at minimum
+// value
+//
+// Parameters: pitch - pitch to apply to sound clip
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetBurnoutMinPitch( float pitch )
+{
+ m_burnoutMinPitch = pitch;
+}
+
+//=============================================================================
+// carSoundParameters::SetBurnoutMaxPitch
+//=============================================================================
+// Description: Set pitch for engine when burnout factor is at maximum
+// value
+//
+// Parameters: pitch - pitch to apply to sound clip
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetBurnoutMaxPitch( float pitch )
+{
+ m_burnoutMaxPitch = pitch;
+}
+
+//=============================================================================
+// carSoundParameters::SetPowerslideMinPitch
+//=============================================================================
+// Description: Set pitch for engine when powerslide factor is at minimum
+// value
+//
+// Parameters: pitch - pitch to apply to sound clip
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetPowerslideMinPitch( float pitch )
+{
+ m_powerslideMinPitch = pitch;
+}
+
+//=============================================================================
+// carSoundParameters::GetPowerslideMinPitch
+//=============================================================================
+// Description: Get pitch for engine when powerslide factor is at minimum
+// value
+//
+// Parameters: None
+//
+// Return: min pitch
+//
+//=============================================================================
+float carSoundParameters::GetPowerslideMinPitch()
+{
+ if( m_powerslideMinPitch == s_powerslideMinDefault )
+ {
+ m_powerslideMinPitch = 0.5f * GetRevLimit();
+ }
+
+ return( m_powerslideMinPitch );
+}
+
+//=============================================================================
+// carSoundParameters::SetPowerslideMaxPitch
+//=============================================================================
+// Description: Set pitch for engine when powerslide factor is at maximum
+// value
+//
+// Parameters: pitch - pitch to apply to sound clip
+//
+// Return: void
+//
+//=============================================================================
+void carSoundParameters::SetPowerslideMaxPitch( float pitch )
+{
+ m_powerslideMaxPitch = pitch;
+}
+
+//=============================================================================
+// carSoundParameters::GetPowerslideMaxPitch
+//=============================================================================
+// Description: Get pitch for engine when powerslide factor is at maximum
+// value
+//
+// Parameters: None
+//
+// Return: max pitch
+//
+//=============================================================================
+float carSoundParameters::GetPowerslideMaxPitch()
+{
+ if( m_powerslideMaxPitch == s_powerslideMaxDefault )
+ {
+ m_powerslideMaxPitch = 0.9f * GetRevLimit();
+ }
+
+ return( m_powerslideMaxPitch );
+}
+
+//=============================================================================
+// carSoundParameters::CalculateEnginePitch
+//=============================================================================
+// Description: Given some car data, figure out the pitch we should be playing
+// the clip at
+//
+// Parameters: gear - gear we're currently in
+// currentSpeed - how fast the car is travelling
+// topSpeed - top end for this car
+//
+// Return: pitch as a value from 0.0f to 1.0f
+//
+//=============================================================================
+float carSoundParameters::CalculateEnginePitch( int gear, float currentSpeed, float topSpeed )
+{
+ float enginePitch;
+ float percentageOfGear;
+ float bottomEndSpeed;
+
+ //rAssert( currentSpeed <= topSpeed );
+ rAssert( ( gear > 0 ) || ( gear == REVERSE_GEAR ) );
+ rAssert( gear <= static_cast<int>(MAX_GEARS) );
+
+ if( gear == REVERSE_GEAR )
+ {
+ percentageOfGear = currentSpeed / m_maxReverseKmh;
+ if( percentageOfGear > 1.0f )
+ {
+ percentageOfGear = 1.0f;
+ }
+
+ enginePitch = m_minReversePitch + ( percentageOfGear * ( m_maxReversePitch - m_minReversePitch ) );
+ }
+ else
+ {
+ bottomEndSpeed = m_shiftPoints[gear-1] * topSpeed;
+
+ //
+ // m_shiftPoints[gear+1] is allowed because we set m_shiftPoints[MAX_GEARS] to 1.0f
+ //
+ percentageOfGear = ( ( currentSpeed - bottomEndSpeed )
+ / ( ( m_shiftPoints[gear] * topSpeed ) - bottomEndSpeed ) );
+
+ //
+ // Based on our speed position within the range of speed used for this gear, apply that
+ // same percentage to the pitch range for our final pitch calculation
+ //
+ enginePitch = m_minPitch[gear-1] + ( ( m_maxPitch[gear-1] - m_minPitch[gear-1] ) * percentageOfGear );
+ }
+
+ return( enginePitch );
+}
+
+//=============================================================================
+// carSoundParameters::CalculateCurrentGear
+//=============================================================================
+// Description: Given our speed, figure out what gear we're in
+//
+// Parameters: currentSpeed - how fast the car is travelling
+// previousSpeed - how fast the car was going in the previous frame
+// (to see if we're upshifting or downshifting)
+// topSpeed - top end for this car
+// previousGear - which gear we were in before (used to cut down
+// on spurious gearshifts). If unknown, use -1.
+//
+// Return: 0 for idle, 1 to MAX_GEARS for forward gears
+//
+//=============================================================================
+int carSoundParameters::CalculateCurrentGear( float currentSpeed,
+ float previousSpeed,
+ float topSpeed,
+ int previousGear )
+{
+ unsigned int currentGear;
+ unsigned int uiPreviousGear;
+
+ //
+ // Find our current gear
+ //
+ for( currentGear = 0; currentGear < MAX_GEARS; currentGear++ )
+ {
+ if( currentSpeed <= ( topSpeed * m_shiftPoints[currentGear] ) )
+ {
+ break;
+ }
+ }
+
+ //
+ // Don't downshift if we're accelerating, and vice versa. If
+ // previousGear is -1, we'll accept the gearshift anyway
+ //
+ if( previousGear >= 0 )
+ {
+ uiPreviousGear = static_cast<unsigned int>(previousGear);
+
+ if( ( currentSpeed > previousSpeed )
+ && ( currentGear < uiPreviousGear ) )
+ {
+ currentGear = uiPreviousGear;
+ }
+ else if( ( currentSpeed <= previousSpeed )
+ && ( currentGear > uiPreviousGear ) )
+ {
+ currentGear = uiPreviousGear;
+ }
+ }
+
+ return( static_cast<int>(currentGear) );
+}
+
+//=============================================================================
+// carSoundParameters::GetRevLimit
+//=============================================================================
+// Description: Try to calculate a sensible maximum pitch
+//
+// Parameters: None
+//
+// Return: Rev limit as pitch value
+//
+//=============================================================================
+float carSoundParameters::GetRevLimit()
+{
+ unsigned int i;
+ float revLimit = 0.0f;
+
+ for( i = 0; i < MAX_GEARS; i++ )
+ {
+ //
+ // Ignore the default values of 2.0f, probably too high
+ //
+ if( ( m_maxPitch[i] > revLimit )
+ && ( m_maxPitch[i] < s_maxPitchDefault ) )
+ {
+ revLimit = m_maxPitch[i];
+ }
+ }
+
+ return( revLimit );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+
+//******************************************************************************
+// Factory functions
+//******************************************************************************
+
+//==============================================================================
+// CarSoundParameterObjCreate
+//==============================================================================
+// Description: Factory function for creating carSoundParameters objects.
+// Called by RadScript.
+//
+// Parameters: ppParametersObj - Address of ptr to new object
+// allocator - FTT pool to allocate object within
+//
+// Return: N/A.
+//
+//==============================================================================
+void CarSoundParameterObjCreate
+(
+ ICarSoundParameters** ppParametersObj,
+ radMemoryAllocator allocator
+)
+{
+ rAssert( ppParametersObj != NULL );
+ (*ppParametersObj) = new ( allocator ) carSoundParameters( );
+ (*ppParametersObj)->AddRef( );
+}
+
diff --git a/game/code/sound/avatar/carsoundparameters.h b/game/code/sound/avatar/carsoundparameters.h
new file mode 100644
index 0000000..d5c4ef1
--- /dev/null
+++ b/game/code/sound/avatar/carsoundparameters.h
@@ -0,0 +1,302 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: carsoundparameters.h
+//
+// Description: Declaration for RadScript-created class for tunable car sound
+// parameters
+//
+// History: 30/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef CARSOUNDPARAMETERS_H
+#define CARSOUNDPARAMETERS_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <radobject.hpp>
+#include <radlinkedclass.hpp>
+
+#include <sound/avatar/icarsoundparameters.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: carSoundParameters
+//
+//=============================================================================
+
+class carSoundParameters: public ICarSoundParameters,
+ public radLinkedClass< carSoundParameters >,
+ public radRefCount
+{
+ public:
+ IMPLEMENT_REFCOUNTED( "carSoundParameters" );
+
+ static const int REVERSE_GEAR = -1;
+
+ carSoundParameters();
+ virtual ~carSoundParameters();
+
+ void SetWatcherName( const char* name );
+
+ void SetClipRPM( float rpm ) { m_clipRPM = rpm; }
+ float GetClipRPM() { return( m_clipRPM ); }
+
+ void SetEngineClipName( const char* clipName );
+ const char* GetEngineClipName() { return( m_engineClipName ); }
+
+ void SetEngineIdleClipName( const char* clipName );
+ const char* GetEngineIdleClipName() { return( m_idleClipName ); }
+
+ void SetDamagedEngineClipName( const char* clipName );
+ const char* GetDamagedEngineClipName() { return( m_damagedClipName ); }
+
+ void SetHornClipName( const char* clipName );
+ const char* GetHornClipName() { return( m_hornClipName ); }
+
+ void SetCarDoorOpenClipName( const char* clipName );
+ const char* GetCarDoorOpenClipName() { return( m_carOpenClipName ); }
+
+ void SetCarDoorCloseClipName( const char* clipName );
+ const char* GetCarDoorCloseClipName() { return( m_carCloseClipName ); }
+
+ void SetOverlayClipName( const char* clipName );
+ const char* GetOverlayClipName() { return( m_overlayClipName ); }
+
+ void SetRoadSkidClipName( const char* clipName );
+ const char* GetRoadSkidClipName() { return( m_roadSkidClipName ); }
+
+ void SetDirtSkidClipName( const char* clipName );
+ const char* GetDirtSkidClipName() { return( m_dirtSkidClipName ); }
+
+ void SetBackupClipName( const char* clipName );
+ const char* GetBackupClipName() { return( m_backupClipName ); }
+
+ void SetShiftPoint( unsigned int gear, float percent );
+ float GetShiftPoint( int gear );
+
+ void SetDownshiftDamperSize( float percent );
+ float GetDownshiftDamperSize() { return( m_downshiftDamper ); }
+
+ void SetGearPitchRange( unsigned int gear, float min, float max );
+
+ void SetNumberOfGears( unsigned int gear );
+
+ float GetMsecsPerOctaveCap() { return( m_msecsPerOctave ); }
+
+ //
+ // Attack/delay/decay
+ //
+ void SetAttackTimeMsecs( float msecs );
+ float GetAttackTimeMsecs() { return( m_attackTime ); }
+
+ void SetDelayTimeMsecs( unsigned int msecs );
+ unsigned int GetDelayTimeMsecs() { return( m_delayTime ); }
+
+ void SetDecayTimeMsecs( float msecs );
+ float GetDecayTimeMsecs() { return( m_decayTime ); }
+
+ void SetDecayFinishTrim( float trim );
+ float GetDecayFinishTrim() { return( m_decayFinishTrim ); }
+
+ //
+ // Reverse
+ //
+ void SetReversePitchCapKmh( float speed );
+ float GetReversePitchCapKmh() { return( m_maxReverseKmh ); }
+
+ void SetReversePitchRange( float min, float max );
+ void GetReversePitchRange( float& min, float& max )
+ { min = m_minReversePitch; max = m_maxReversePitch; }
+
+ //
+ // Gear shifts
+ //
+ void SetGearShiftPitchDrop( unsigned int gear, float drop );
+ float GetGearShiftPitchDrop( unsigned int gear );
+
+ //
+ // Damage
+ //
+ void SetDamageStartPcnt( float damagePercent );
+ float GetDamageStartPcnt() { return( m_damageStartPcnt ); }
+
+ void SetDamageMaxVolPcnt( float percent );
+ float GetDamageVolumeRange() { return( m_damageVolumeRange ); }
+
+ void SetDamageStartTrim( float trim );
+ float GetDamageStartTrim() { return( m_damageStartTrim ); }
+ void SetDamageMaxTrim( float trim );
+ float GetDamageMaxTrim() { return( m_damageMaxTrim ); }
+
+ //
+ // Idle
+ //
+ void SetIdleEnginePitch( float pitch );
+ float GetIdleEnginePitch() { return( m_idleEnginePitch ); }
+
+ //
+ // In-air sound characteristics
+ //
+ void SetInAirThrottlePitch( float pitch );
+ float GetInAirThrottlePitch();
+
+ void SetInAirIdlePitch( float pitch );
+ float GetInAirIdlePitch();
+
+ void SetInAirThrottleResponseTimeMsecs( unsigned int msecs );
+ unsigned int GetInAirThrottleResponseTimeMsecs() { return( m_inAirResponseMsecs ); }
+
+ //
+ // Burnout sound characteristics
+ //
+ void SetBurnoutMinPitch( float pitch );
+ float GetBurnoutMinPitch() { return( m_burnoutMinPitch ); }
+
+ void SetBurnoutMaxPitch( float pitch );
+ float GetBurnoutMaxPitch() { return( m_burnoutMaxPitch ); }
+
+ //
+ // Powerslide sound characteristics
+ //
+ void SetPowerslideMinPitch( float pitch );
+ float GetPowerslideMinPitch();
+
+ void SetPowerslideMaxPitch( float pitch );
+ float GetPowerslideMaxPitch();
+
+ //
+ // Given a vehicle's current speed and max speed, figure out the
+ // gear it's in
+ //
+ int CalculateCurrentGear( float currentSpeed, float previousSpeed,
+ float topSpeed, int previousGear );
+
+ //
+ // Given a vehicle's current gear, speed, and max speed, figure out the
+ // pitch for the engine clip
+ //
+ float CalculateEnginePitch( int gear, float currentSpeed, float topSpeed );
+
+ float GetRevLimit();
+
+ private:
+ //Prevent wasteful constructor creation.
+ carSoundParameters( const carSoundParameters& original );
+ carSoundParameters& operator=( const carSoundParameters& rhs );
+
+ static const unsigned int MAX_GEARS = 6;
+
+ float m_clipRPM;
+
+ float m_minPitch[MAX_GEARS];
+ float m_maxPitch[MAX_GEARS];
+
+ char* m_engineClipName;
+ char* m_idleClipName;
+ char* m_damagedClipName;
+ char* m_hornClipName;
+ char* m_carOpenClipName;
+ char* m_carCloseClipName;
+ char* m_overlayClipName;
+ char* m_roadSkidClipName;
+ char* m_dirtSkidClipName;
+ char* m_backupClipName;
+
+ //
+ // Percentages of top speed at which we shift gears.
+ // E.g. m_shiftPoints[0] == 0.01f means that we shift from idle
+ // to first at 1% of top speed. m_shiftPoints[MAX_GEARS+1] is
+ // 1.0f to simplify math.
+ //
+
+ // Used for shifting
+ float m_shiftPoints[MAX_GEARS+1];
+ float m_downshiftDamper;
+
+ //
+ // Attack/delay/decay
+ //
+ float m_attackTime;
+ unsigned int m_delayTime;
+ float m_decayTime;
+
+ float m_decayFinishTrim;
+
+ //
+ // Reverse
+ //
+ float m_maxReverseKmh;
+ float m_minReversePitch;
+ float m_maxReversePitch;
+
+ //
+ // Gear shifts
+ //
+ float m_gearShiftPitchDrop[MAX_GEARS];
+ unsigned int m_gearShiftTimeMsecs[MAX_GEARS];
+
+ //
+ // Damage
+ //
+ float m_damageStartPcnt;
+ float m_damageVolumeRange;
+
+ float m_damageStartTrim;
+ float m_damageMaxTrim;
+
+ //
+ // Idle
+ //
+ float m_idleEnginePitch;
+
+ //
+ // Airborne
+ //
+ float m_inAirThrottlePitch;
+ float m_inAirIdlePitch;
+ unsigned int m_inAirResponseMsecs;
+
+ //
+ // Burnout
+ //
+ float m_burnoutMinPitch;
+ float m_burnoutMaxPitch;
+
+ //
+ // Powerslide
+ //
+ float m_powerslideMinPitch;
+ float m_powerslideMaxPitch;
+
+ //
+ // Makes transitions a little less abrupt
+ //
+ float m_msecsPerOctave;
+};
+
+//=============================================================================
+// Factory Functions
+//=============================================================================
+
+//
+// Create a CarSoundParameters object
+//
+void CarSoundParameterObjCreate
+(
+ ICarSoundParameters** ppSoundResource,
+ radMemoryAllocator allocator
+);
+
+
+
+
+#endif // CARSOUNDPARAMETERS_H
+
diff --git a/game/code/sound/avatar/icarsoundparameters.h b/game/code/sound/avatar/icarsoundparameters.h
new file mode 100644
index 0000000..6fb8f9a
--- /dev/null
+++ b/game/code/sound/avatar/icarsoundparameters.h
@@ -0,0 +1,168 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: icarsoundparameters.h
+//
+// Description: Interface declaration for RadScript-created class for
+// tunable car sound parameters
+//
+// History: 30/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef ICARSOUNDPARAMETERS_H
+#define ICARSOUNDPARAMETERS_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <radobject.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: ICarSoundParameters
+//
+//=============================================================================
+
+struct ICarSoundParameters : public IRefCount
+{
+ //
+ // Vehicle RPM at which the engine sound clip should be played unchanged
+ //
+ virtual void SetClipRPM( float rpm ) = 0;
+
+ //
+ // Name of clip to use for engine sound
+ //
+ virtual void SetEngineClipName( const char* clipName ) = 0;
+
+ //
+ // Name of clip to use for engine idle sound
+ //
+ virtual void SetEngineIdleClipName( const char* clipName ) = 0;
+
+ //
+ // Name of clip to use for damaged engine sound
+ //
+ virtual void SetDamagedEngineClipName( const char* clipName ) = 0;
+
+ //
+ // Horn sound
+ //
+ virtual void SetHornClipName( const char* clipName ) = 0;
+
+ //
+ // Skid sounds
+ //
+ virtual void SetRoadSkidClipName( const char* clipName ) = 0;
+ virtual void SetDirtSkidClipName( const char* clipName ) = 0;
+
+ //
+ // Backup sound
+ //
+ virtual void SetBackupClipName( const char* clipName ) = 0;
+
+ //
+ // Car door entry/exit sounds
+ //
+ virtual void SetCarDoorOpenClipName( const char* clipName ) = 0;
+ virtual void SetCarDoorCloseClipName( const char* clipName ) = 0;
+
+ //
+ // Percentages of top speed for shift points
+ //
+ virtual void SetShiftPoint( unsigned int gear, float percent ) = 0;
+
+ //
+ // Percentage of top speed that we'll use as a range for dropping
+ // below the shift point to avoid downshifting
+ //
+ virtual void SetDownshiftDamperSize( float percent ) = 0;
+
+ //
+ // Pitch range for a particular gear
+ //
+ virtual void SetGearPitchRange( unsigned int gear, float min, float max ) = 0;
+
+ //
+ // Number of gears
+ //
+ virtual void SetNumberOfGears( unsigned int gear ) = 0;
+
+ //
+ // Attack/delay/decay
+ //
+ virtual void SetAttackTimeMsecs( float msecs ) = 0;
+ virtual void SetDelayTimeMsecs( unsigned int msecs ) = 0;
+ virtual void SetDecayTimeMsecs( float msecs ) = 0;
+
+ virtual void SetDecayFinishTrim( float trim ) = 0;
+
+ //
+ // Top speed for which we'll increase pitch in reverse
+ //
+ virtual void SetReversePitchCapKmh( float speed ) = 0;
+
+ //
+ // Reverse gear pitch range
+ //
+ virtual void SetReversePitchRange( float min, float max ) = 0;
+
+ //
+ // Amount of pitch drop and time to apply in gear shifts
+ //
+ virtual void SetGearShiftPitchDrop( unsigned int gear, float drop ) = 0;
+
+ //
+ // Percentage damage when we play damage sound
+ //
+ virtual void SetDamageStartPcnt( float damagePercent ) = 0;
+ virtual void SetDamageMaxVolPcnt( float percent ) = 0;
+
+ //
+ // Volume control on damage sounds
+ //
+ virtual void SetDamageStartTrim( float trim ) = 0;
+ virtual void SetDamageMaxTrim( float trim ) = 0;
+
+ //
+ // Pitch for idle engine clip (separate from sound resource in case
+ // we want to share resource between cars. Strictly a convenience,
+ // since multiple sound resources with the same sound file could be
+ // created instead).
+ //
+ virtual void SetIdleEnginePitch( float pitch ) = 0;
+
+ //
+ // In-air sound characteristics
+ //
+ virtual void SetInAirThrottlePitch( float pitch ) = 0;
+ virtual void SetInAirIdlePitch( float pitch ) = 0;
+ virtual void SetInAirThrottleResponseTimeMsecs( unsigned int msecs ) = 0;
+
+ //
+ // Burnout sound characteristics
+ //
+ virtual void SetBurnoutMinPitch( float pitch ) = 0;
+ virtual void SetBurnoutMaxPitch( float pitch ) = 0;
+
+ //
+ // Powerslide sound characteristics
+ //
+ virtual void SetPowerslideMinPitch( float pitch ) = 0;
+ virtual void SetPowerslideMaxPitch( float pitch ) = 0;
+
+ //
+ // Playing an overlay clip
+ //
+ virtual void SetOverlayClipName( const char* clipName ) = 0;
+};
+
+
+#endif // ICARSOUNDPARAMETERS_H
+
diff --git a/game/code/sound/avatar/soundavatar.cpp b/game/code/sound/avatar/soundavatar.cpp
new file mode 100644
index 0000000..b1e827f
--- /dev/null
+++ b/game/code/sound/avatar/soundavatar.cpp
@@ -0,0 +1,245 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundavatar.cpp
+//
+// Description: Implements SoundAvatar, which maintains a reference
+// to an avatar for which sounds are to be created, and directs
+// the playing of sound as either vehicle- or character-based,
+// whichever is appropriate at the time.
+//
+// History: 30/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/avatar/soundavatar.h>
+
+#include <sound/avatar/vehiclesoundplayer.h>
+#include <worldsim/avatar.h>
+#include <worldsim/character/character.h>
+#include <events/eventmanager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundAvatar::SoundAvatar
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundAvatar::SoundAvatar( Avatar* avatarObj ) :
+ m_avatar( avatarObj ),
+ m_isInCar( false ),
+ m_turboTimer( 0 )
+{
+ rAssert( avatarObj != NULL );
+
+ //
+ // Register as an event listener
+ //
+ GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_MISSION_RESET );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED_SYNC_SOUND );
+ GetEventManager()->AddListener( this, EVENT_CHARACTER_POS_RESET );
+ GetEventManager()->AddListener( this, EVENT_CHASE_VEHICLE_SPAWNED );
+ GetEventManager()->AddListener( this, EVENT_CHASE_VEHICLE_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_TURBO_START );
+
+ syncCarSoundState();
+}
+
+//==============================================================================
+// SoundAvatar::~SoundAvatar
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundAvatar::~SoundAvatar()
+{
+ if( m_isInCar )
+ {
+ m_vehicleSoundPlayer.StopCarSounds();
+ }
+
+ //
+ // Deregister from EventManager
+ //
+ GetEventManager()->RemoveAll( this );
+}
+
+//=============================================================================
+// SoundAvatar::HandleEvent
+//=============================================================================
+// Description: Listen for events that tell us if we're swapping between
+// walking and driving
+//
+// Parameters: id - indicates which event has occurred
+// pEventData - ptr to Character class, must match to our avatar
+//
+// Return: void
+//
+//=============================================================================
+void SoundAvatar::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_GETINTOVEHICLE_END:
+ if( static_cast<Character*>(pEventData) == m_avatar->GetCharacter() )
+ {
+ m_isInCar = true;
+
+ rAssert( m_avatar->GetVehicle() );
+ m_vehicleSoundPlayer.StartCarSounds( m_avatar->GetVehicle() );
+ }
+ break;
+
+ case EVENT_GETOUTOFVEHICLE_END:
+ if( static_cast<Character*>(pEventData) == m_avatar->GetCharacter() )
+ {
+ m_isInCar = false;
+
+ m_vehicleSoundPlayer.StopCarSounds();
+ }
+ break;
+
+ case EVENT_MISSION_RESET:
+ case EVENT_CHARACTER_POS_RESET:
+ case EVENT_VEHICLE_DESTROYED_SYNC_SOUND:
+ syncCarSoundState();
+ break;
+
+ case EVENT_CHASE_VEHICLE_SPAWNED:
+ m_vehicleSoundPlayer.AddAIVehicleProximityTest( static_cast<Vehicle*>(pEventData) );
+ break;
+
+ case EVENT_CHASE_VEHICLE_DESTROYED:
+ m_vehicleSoundPlayer.DeleteAIVehicleProximityTest( static_cast<Vehicle*>(pEventData) );
+ break;
+
+ case EVENT_TURBO_START:
+ //
+ // If event applies to this character, start a timer. When it expires,
+ // say something funny
+ //
+ if( ( m_turboTimer == 0 ) &&
+ ( static_cast<Character*>(pEventData) == m_avatar->GetCharacter() ) )
+ {
+ m_turboTimer = 5000;
+ }
+
+ default:
+ break;
+ }
+}
+
+//=============================================================================
+// SoundAvatar::UpdateOncePerFrame
+//=============================================================================
+// Description: Update function. Used for stuff that either doesn't need to
+// be called often and/or uses expensive math.
+//
+// Parameters: elapsedTime - time since last frame in msecs
+//
+// Return: void
+//
+//=============================================================================
+void SoundAvatar::UpdateOncePerFrame( unsigned int elapsedTime )
+{
+ if( m_isInCar )
+ {
+ m_vehicleSoundPlayer.UpdateOncePerFrame( elapsedTime );
+ }
+
+ if( m_turboTimer > 0 )
+ {
+ if( !(m_avatar->GetCharacter()->IsTurbo()) )
+ {
+ //
+ // Player no longer sprinting, stop the countdown
+ //
+ m_turboTimer = 0;
+ }
+ else if( elapsedTime >= m_turboTimer )
+ {
+ //
+ // Timer expired. Time to make with the funny.
+ //
+ GetEventManager()->TriggerEvent( EVENT_CHARACTER_TIRED_NOW );
+
+ //
+ // Set up for another line, but if it's a repeat, let's make
+ // it take a little longer
+ //m_turboTimer = 10000;
+
+ //
+ // Okay, the repeating line is unpopular. Just play it once
+ // and be done.
+ //
+ m_turboTimer = 0;
+ }
+ else
+ {
+ m_turboTimer -= elapsedTime;
+ }
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// SoundAvatar::syncCarSoundState
+//=============================================================================
+// Description: Determine whether the avatar is in the car, and start or
+// stop engine sounds accordingly.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundAvatar::syncCarSoundState()
+{
+ m_isInCar = m_avatar->IsInCar();
+
+ if( m_isInCar )
+ {
+ rAssert( m_avatar->GetVehicle() );
+
+ m_vehicleSoundPlayer.StartCarSounds( m_avatar->GetVehicle() );
+ }
+ else
+ {
+ m_vehicleSoundPlayer.StopCarSounds();
+ }
+} \ No newline at end of file
diff --git a/game/code/sound/avatar/soundavatar.h b/game/code/sound/avatar/soundavatar.h
new file mode 100644
index 0000000..3ea676a
--- /dev/null
+++ b/game/code/sound/avatar/soundavatar.h
@@ -0,0 +1,78 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundavatar.h
+//
+// Description: Declaration of SoundAvatar class, which maintains a reference
+// to an avatar for which sounds are to be created, and directs
+// the playing of sound as either vehicle- or character-based,
+// whichever is appropriate at the time.
+//
+// History: 30/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDAVATAR_H
+#define SOUNDAVATAR_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <sound/avatar/vehiclesoundplayer.h>
+
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+
+class Avatar;
+
+//=============================================================================
+//
+// Synopsis: SoundAvatar
+//
+//=============================================================================
+
+class SoundAvatar : public EventListener
+{
+ public:
+ SoundAvatar( Avatar* avatarObj );
+ virtual ~SoundAvatar();
+
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ void UpdateOncePerFrame( unsigned int elapsedTime );
+
+ private:
+ //Prevent wasteful constructor creation.
+ SoundAvatar();
+ SoundAvatar( const SoundAvatar& original );
+ SoundAvatar& operator=( const SoundAvatar& rhs );
+
+ void syncCarSoundState();
+
+ //
+ // Avatar object that we're going to keep an eye on
+ //
+ Avatar* m_avatar;
+
+ //
+ // Is the player in the car?
+ //
+ bool m_isInCar;
+
+ //
+ // Object for playing vehicle sounds
+ VehicleSoundPlayer m_vehicleSoundPlayer;
+
+ //
+ // Timer for character sprinting
+ //
+ unsigned int m_turboTimer;
+};
+
+
+#endif // SOUNDAVATAR_H
+
diff --git a/game/code/sound/avatar/vehiclesounddebugpage.cpp b/game/code/sound/avatar/vehiclesounddebugpage.cpp
new file mode 100644
index 0000000..20102b3
--- /dev/null
+++ b/game/code/sound/avatar/vehiclesounddebugpage.cpp
@@ -0,0 +1,138 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehiclesounddebugpage.cpp
+//
+// Description: Definition for VehicleSoundDebugPage class, which displays
+// vehicle sound info through Watcher
+//
+// History: 11/22/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/avatar/vehiclesounddebugpage.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// VehicleSoundDebugPage::VehicleSoundDebugPage
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+VehicleSoundDebugPage::VehicleSoundDebugPage( unsigned int pageNum, SoundDebugDisplay* master ) :
+ SoundDebugPage( pageNum, master ),
+ m_shiftInProgress( false ),
+ m_currentGear( 0 ),
+ m_currentSpeed( 0.0f ),
+ m_shiftTime( 0 ),
+ m_downshiftSpeed( 0.0f ),
+ m_upshiftSpeed( 0.0f ),
+ m_currentPitch( 0.0f ),
+ m_isDamaged( false )
+{
+}
+
+//==============================================================================
+// VehicleSoundDebugPage::~VehicleSoundDebugPage
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+VehicleSoundDebugPage::~VehicleSoundDebugPage()
+{
+}
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+void VehicleSoundDebugPage::fillLineBuffer( int lineNum, char* buffer )
+{
+ switch( lineNum )
+ {
+ case 0:
+ sprintf( buffer, "Gear: %d Speed: %f", m_currentGear, m_currentSpeed );
+ break;
+
+ case 2:
+ sprintf( buffer, "Shift speeds: %f %f", m_downshiftSpeed, m_upshiftSpeed );
+ break;
+
+ case 4:
+ if( m_shiftInProgress )
+ {
+ sprintf( buffer, "SHIFTING - Time in msecs: %d", m_shiftTime );
+ }
+ else
+ {
+ buffer[0] = '\0';
+ }
+ break;
+
+ case 6:
+ sprintf( buffer, "Pitch: %f", m_currentPitch );
+ break;
+
+ case 8:
+ if( m_isDamaged )
+ {
+ sprintf( buffer, "Life: %f Threshold: %f CAR DAMAGE SOUND",
+ m_vehicleLife, m_damageThreshold );
+ }
+ else
+ {
+ sprintf( buffer, "Life: %f Threshold: %f",
+ m_vehicleLife, m_damageThreshold );
+ }
+ break;
+
+ default:
+ buffer[0] = '\0';
+ break;
+ }
+}
+
+//=============================================================================
+// VehicleSoundDebugPage::getNumLines
+//=============================================================================
+// Description: Returns number of lines that we'll display on screen
+//
+// Parameters: None
+//
+// Return: Line count
+//
+//=============================================================================
+int VehicleSoundDebugPage::getNumLines()
+{
+ return( 9 );
+}
diff --git a/game/code/sound/avatar/vehiclesounddebugpage.h b/game/code/sound/avatar/vehiclesounddebugpage.h
new file mode 100644
index 0000000..3259558
--- /dev/null
+++ b/game/code/sound/avatar/vehiclesounddebugpage.h
@@ -0,0 +1,75 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehiclesounddebugpage.h
+//
+// Description: Declaration for VehicleSoundDebugPage class, which displays
+// vehicle sound info through Watcher
+//
+// History: 11/22/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef VEHICLESOUNDDEBUGPAGE_H
+#define VEHICLESOUNDDEBUGPAGE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/sounddebug/sounddebugpage.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: VehicleSoundDebugPage
+//
+//=============================================================================
+
+class VehicleSoundDebugPage : public SoundDebugPage
+{
+ public:
+ VehicleSoundDebugPage( unsigned int pageNum, SoundDebugDisplay* master );
+ virtual ~VehicleSoundDebugPage();
+
+ void SetShiftInProgress( bool inProgress ) { m_shiftInProgress = inProgress; }
+ void SetCurrentGear( int gear ) { m_currentGear = gear; }
+ void SetCurrentSpeed( float speed ) { m_currentSpeed = speed; }
+ void SetShiftTime( unsigned int time ) { m_shiftTime = time; }
+ void SetDownshiftSpeed( float speed ) { m_downshiftSpeed = speed; }
+ void SetUpshiftSpeed( float speed ) { m_upshiftSpeed = speed; }
+ void SetCurrentPitch( float pitch ) { m_currentPitch = pitch; }
+ void SetDamageEnabled( bool isDamaged ) { m_isDamaged = isDamaged; }
+ void SetVehicleLife( float life ) { m_vehicleLife = life; }
+ void SetDamageThreshold( float threshold ) { m_damageThreshold = threshold; }
+
+ protected:
+ //
+ // Pure virtual functions from SoundDebugPage
+ //
+ void fillLineBuffer( int lineNum, char* buffer );
+ int getNumLines();
+
+ private:
+ //Prevent wasteful constructor creation.
+ VehicleSoundDebugPage();
+ VehicleSoundDebugPage( const VehicleSoundDebugPage& original );
+ VehicleSoundDebugPage& operator=( const VehicleSoundDebugPage& rhs );
+
+ bool m_shiftInProgress;
+ int m_currentGear;
+ float m_currentSpeed;
+ unsigned int m_shiftTime;
+ float m_downshiftSpeed;
+ float m_upshiftSpeed;
+ float m_currentPitch;
+ bool m_isDamaged;
+ float m_vehicleLife;
+ float m_damageThreshold;
+};
+
+
+#endif // VEHICLESOUNDDEBUGPAGE_H
+
diff --git a/game/code/sound/avatar/vehiclesoundplayer.cpp b/game/code/sound/avatar/vehiclesoundplayer.cpp
new file mode 100644
index 0000000..257d342
--- /dev/null
+++ b/game/code/sound/avatar/vehiclesoundplayer.cpp
@@ -0,0 +1,2499 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehiclesoundplayer.cpp
+//
+// Description: Implement VehicleSoundPlayer
+//
+// History: 30/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <float.h>
+
+#include <radnamespace.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/avatar/vehiclesoundplayer.h>
+
+#include <sound/avatar/carsoundparameters.h>
+#include <sound/tuning/globalsettings.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/idasoundresource.h>
+#include <sound/soundrenderer/soundresourcemanager.h>
+#include <sound/soundmanager.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h>
+
+#include <input/button.h>
+#include <events/eventmanager.h>
+#include <mission/gameplaymanager.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// For tuning
+//
+static bool s_resetEngineOnHonk = false;
+static bool s_resetEngineInitialized = false;
+
+static const char* s_skidResource = "skid";
+static const char* s_gearResource = "gearshift";
+
+static const float GAME_POWERSLIDE_TRIM_MIN = 0.6f;
+static const float GAME_POWERSLIDE_TRIM_MAX = 0.9f;
+
+static const float SUPERSPRINT_POWERSLIDE_TRIM_MIN = 0.4f;
+static const float SUPERSPRINT_POWERSLIDE_TRIM_MAX = 0.7f;
+
+//
+// MACROS FOR DEBUG INFO
+//
+#ifdef SOUND_DEBUG_INFO_ENABLED
+
+#define SET_SHIFT_IN_PROGRESS(inProgress) m_debugInfo->SetShiftInProgress(inProgress)
+#define SET_CURRENT_GEAR(gear) m_debugInfo->SetCurrentGear(gear)
+#define SET_CURRENT_SPEED(speed) m_debugInfo->SetCurrentSpeed(speed)
+#define SET_SHIFT_TIME_REMAINING(time) m_debugInfo->SetShiftTime(time)
+#define SET_DOWNSHIFT_SPEED(speed) m_debugInfo->SetDownshiftSpeed(speed)
+#define SET_UPSHIFT_SPEED(speed) m_debugInfo->SetUpshiftSpeed(speed)
+#define SET_CURRENT_PITCH(pitch) m_debugInfo->SetCurrentPitch(pitch)
+#define SET_IS_DAMAGED(isDamaged) m_debugInfo->SetDamageEnabled(isDamaged)
+#define SET_VEHICLE_LIFE(life) m_debugInfo->SetVehicleLife(life)
+#define SET_DAMAGE_THRESHOLD(threshold) m_debugInfo->SetDamageThreshold(threshold)
+
+//
+// This is annoying. Stinky bad design.
+//
+#define SET_LOCAL_SHIFT_IN_PROGRESS(inProgress) m_debugInfo.SetShiftInProgress(inProgress)
+#define SET_LOCAL_CURRENT_GEAR(gear) m_debugInfo.SetCurrentGear(gear)
+#define SET_LOCAL_CURRENT_SPEED(speed) m_debugInfo.SetCurrentSpeed(speed)
+#define SET_LOCAL_SHIFT_TIME_REMAINING(time) m_debugInfo.SetShiftTime(time)
+#define SET_LOCAL_DOWNSHIFT_SPEED(speed) m_debugInfo.SetDownshiftSpeed(speed)
+#define SET_LOCAL_UPSHIFT_SPEED(speed) m_debugInfo.SetUpshiftSpeed(speed)
+#define SET_LOCAL_CURRENT_PITCH(pitch) m_debugInfo.SetCurrentPitch(pitch)
+#define SET_LOCAL_IS_DAMAGED(isDamaged) m_debugInfo.SetDamageEnabled(isDamaged)
+#define SET_LOCAL_VEHICLE_LIFE(life) m_debugInfo.SetVehicleLife(life)
+#define SET_LOCAL_DAMAGE_THRESHOLD(threshold) m_debugInfo.SetDamageThreshold(threshold)
+
+#else
+
+#define SET_SHIFT_IN_PROGRESS(inProgress)
+#define SET_CURRENT_GEAR(gear)
+#define SET_CURRENT_SPEED(speed)
+#define SET_SHIFT_TIME_REMAINING(time)
+#define SET_DOWNSHIFT_SPEED(speed)
+#define SET_UPSHIFT_SPEED(speed)
+#define SET_CURRENT_PITCH(pitch)
+#define SET_IS_DAMAGED(isDamaged)
+#define SET_VEHICLE_LIFE(life)
+#define SET_DAMAGE_THRESHOLD(threshold)
+
+#define SET_LOCAL_SHIFT_IN_PROGRESS(inProgress)
+#define SET_LOCAL_CURRENT_GEAR(gear)
+#define SET_LOCAL_CURRENT_SPEED(speed)
+#define SET_LOCAL_SHIFT_TIME_REMAINING(time)
+#define SET_LOCAL_DOWNSHIFT_SPEED(speed)
+#define SET_LOCAL_UPSHIFT_SPEED(speed)
+#define SET_LOCAL_CURRENT_PITCH(pitch)
+#define SET_LOCAL_IS_DAMAGED(isDamaged)
+#define SET_LOCAL_VEHICLE_LIFE(life)
+#define SET_LOCAL_DAMAGE_THRESHOLD(threshold)
+
+#endif
+
+enum GearShiftResult
+{
+ GEARSHIFT_NONE,
+ GEARSHIFT_DAMPED,
+ GEARSHIFT_UP,
+ GEARSHIFT_DOWN
+};
+
+
+//
+// Classes for engine states. Each one represents a state
+// in which the engine will have different sound-speed characteristics
+// (e.g. car in air, gearshift in progress).
+//
+
+//
+// Base class for engine states
+//
+class EngineState
+{
+ public:
+ EngineState();
+ virtual ~EngineState();
+
+ void Initialize( Vehicle* vehicle, carSoundParameters* parameters,
+ SimpsonsSoundPlayer* player, radKey32 resourceKey,
+ VehicleSoundDebugPage* debugPtr );
+ bool StartNewState() { return( m_nextState != ENGINE_STATE_INVALID ); }
+ EngineStateEnum GetNewState() { return( m_nextState ); }
+ void Reset() { m_nextState = ENGINE_STATE_INVALID; }
+
+ virtual void SetDebugInfo();
+
+ virtual void Service( unsigned int elapsedTime ) = 0;
+
+ virtual float GetCurrentPitch();
+
+ bool IsActive() { return( m_isActive ); }
+ void SetActive( bool isActive, float prevPitch = 0.0f );
+
+ void StartFade( float initialTrim, bool stopAfterFade = true );
+ bool IsFading();
+ void ServiceFade( unsigned int elapsedTime );
+
+ void SetTrim( float trim );
+
+ protected:
+
+ virtual void startup( float prevPitch ) = 0;
+ bool isAtIdleSpeed();
+ bool isSkidding();
+
+ //
+ // True if this state is running and producing sound
+ //
+ bool m_isActive;
+
+ //
+ // When we want to kick off a new state, set this to something
+ // other than ENGINE_STATE_INVALID
+ //
+ EngineStateEnum m_nextState;
+
+ //
+ // Stuff for getting vehicle and sound parameter info
+ //
+ Vehicle* m_vehicle;
+ carSoundParameters* m_parameters;
+
+ //
+ // Sound player stuff
+ //
+ SimpsonsSoundPlayer* m_player;
+ radKey32 m_resourceKey;
+
+ //
+ // Rev limiter, calculated from pitch ranges
+ //
+ float m_revLimit;
+
+ //
+ // Debug stuff
+ //
+#ifdef SOUND_DEBUG_INFO_ENABLED
+ VehicleSoundDebugPage* m_debugInfo;
+#endif
+
+ private:
+ //Prevent wasteful constructor creation.
+ EngineState( const EngineState& original );
+ EngineState& operator=( const EngineState& rhs );
+
+ float m_fadeTrim;
+ bool m_stopAfterFade;
+};
+
+//
+// State for normal, on-ground, no-shift engine sound
+//
+class NormalEngineState : public EngineState
+{
+ public:
+ NormalEngineState();
+ virtual ~NormalEngineState();
+
+ void Service( unsigned int elapsedTime );
+
+ void SetDebugInfo();
+
+ float GetCurrentPitch();
+
+ protected:
+ void startup( float prevPitch );
+ private:
+ //Prevent wasteful constructor creation.
+ NormalEngineState( const NormalEngineState& original );
+ NormalEngineState& operator=( const NormalEngineState& rhs );
+
+ GearShiftResult updateCurrentGearFromVehicle( bool dampenShifts = false );
+
+ int m_currentGear;
+ float m_currentSpeed;
+ float m_currentPitch;
+ bool m_isAttacking;
+ float m_currentTrim;
+ unsigned int m_interpolateTime;
+};
+
+//
+// State for upshifts (e.g. 2nd to 3rd gear)
+//
+class UpshiftEngineState : public EngineState
+{
+ public:
+ UpshiftEngineState();
+ virtual ~UpshiftEngineState();
+
+ void Service( unsigned int elapsedTime );
+
+ void SetDebugInfo();
+
+ protected:
+ void startup( float prevPitch );
+ private:
+ //Prevent wasteful constructor creation.
+ UpshiftEngineState( const UpshiftEngineState& original );
+ UpshiftEngineState& operator=( const UpshiftEngineState& rhs );
+
+ float m_startPitch;
+ float m_pitchDrop;
+ float m_shiftTimeMsecs;
+ unsigned int m_remainingTime;
+};
+
+//
+// State for downshifts (e.g. 3rd to 2nd gear)
+//
+class DownshiftEngineState : public EngineState
+{
+ public:
+ DownshiftEngineState();
+ virtual ~DownshiftEngineState();
+
+ void Service( unsigned int elapsedTime );
+
+ void SetDebugInfo();
+
+ float GetCurrentPitch();
+
+ protected:
+ void startup( float prevPitch );
+ private:
+ //Prevent wasteful constructor creation.
+ DownshiftEngineState( const DownshiftEngineState& original );
+ DownshiftEngineState& operator=( const DownshiftEngineState& rhs );
+
+ float m_startSpeed;
+ float m_startPitch;
+ float m_lastSpeed;
+ float m_currentPitch;
+};
+
+//
+// State for car in mid-air
+//
+class InAirEngineState : public EngineState
+{
+ public:
+ InAirEngineState();
+ virtual ~InAirEngineState();
+
+ void Service( unsigned int elapsedTime );
+
+ void SetDebugInfo();
+
+ protected:
+ void startup( float prevPitch );
+ private:
+ //Prevent wasteful constructor creation.
+ InAirEngineState( const InAirEngineState& original );
+ InAirEngineState& operator=( const InAirEngineState& rhs );
+
+ float m_currentPitch;
+};
+
+//
+// State for car in reverse
+//
+class ReverseEngineState : public EngineState
+{
+ public:
+ ReverseEngineState( SimpsonsSoundPlayer* beepPlayer );
+ virtual ~ReverseEngineState();
+
+ void Service( unsigned int elapsedTime );
+
+ void SetDebugInfo();
+
+ protected:
+ void startup( float prevPitch );
+ private:
+ //Prevent wasteful constructor creation.
+ ReverseEngineState( const ReverseEngineState& original );
+ ReverseEngineState& operator=( const ReverseEngineState& rhs );
+
+ SimpsonsSoundPlayer* m_beepPlayer;
+};
+
+//
+// State for car in idle
+//
+class IdleEngineState : public EngineState
+{
+ public:
+ IdleEngineState();
+ virtual ~IdleEngineState();
+
+ void Service( unsigned int elapsedTime );
+
+ void SetDebugInfo();
+
+ float GetCurrentPitch();
+
+ protected:
+ void startup( float prevPitch );
+ private:
+ //Prevent wasteful constructor creation.
+ IdleEngineState( const IdleEngineState& original );
+ IdleEngineState& operator=( const IdleEngineState& rhs );
+
+ float m_currentPitch;
+};
+
+//
+// State for car when skidding
+//
+class SkidEngineState : public EngineState
+{
+ public:
+ SkidEngineState();
+ virtual ~SkidEngineState();
+
+ void Service( unsigned int elapsedTime );
+
+ void SetDebugInfo();
+
+ float GetCurrentPitch();
+
+ protected:
+ void startup( float prevPitch );
+
+ private:
+ //Prevent wasteful constructor creation.
+ SkidEngineState( const SkidEngineState& original );
+ SkidEngineState& operator=( const SkidEngineState& rhs );
+
+ float m_currentPitch;
+};
+
+//******************************************************************************
+//
+// EngineState
+//
+//******************************************************************************
+
+EngineState::EngineState() :
+ m_isActive( false ),
+ m_nextState( ENGINE_STATE_INVALID ),
+ m_vehicle( NULL ),
+ m_parameters( NULL ),
+ m_player( NULL ),
+ m_resourceKey( 0 ),
+ m_fadeTrim( 0.0f ),
+ m_stopAfterFade( true )
+{
+}
+
+EngineState::~EngineState()
+{
+}
+
+void EngineState::Initialize( Vehicle* vehicle, carSoundParameters* parameters,
+ SimpsonsSoundPlayer* player, radKey32 resourceKey,
+ VehicleSoundDebugPage* debugPtr )
+{
+ rAssert( vehicle != NULL );
+ rAssert( parameters != NULL );
+ rAssert( player != NULL );
+
+ m_vehicle = vehicle;
+ m_parameters = parameters;
+ m_player = player;
+ m_resourceKey = resourceKey;
+
+ //
+ // Figure out a sensible rev limit
+ //
+ m_revLimit = parameters->GetRevLimit();
+
+#ifdef SOUND_DEBUG_INFO_ENABLED
+ m_debugInfo = debugPtr;
+#endif
+}
+
+void EngineState::SetActive( bool isActive, float prevPitch )
+{
+ bool wasActive = m_isActive;
+
+ m_isActive = isActive;
+ if( m_isActive && !wasActive )
+ {
+ startup( prevPitch );
+ }
+}
+
+void EngineState::SetDebugInfo()
+{
+ // Do nothing by default
+}
+
+bool EngineState::isAtIdleSpeed()
+{
+ return( m_vehicle->GetSpeedKmh()
+ <= ( m_parameters->GetShiftPoint( 1 )
+ * m_vehicle->mDesignerParams.mDpTopSpeedKmh ) );
+}
+
+bool EngineState::isSkidding()
+{
+ return( m_vehicle->GetSkidLevel() > 0.0f );
+}
+
+float EngineState::GetCurrentPitch()
+{
+ //
+ // Not needed by all states, so return zero by default.
+ //
+ return( 0.0f );
+}
+
+bool EngineState::IsFading()
+{
+ return( m_fadeTrim > 0.0f );
+}
+
+void EngineState::StartFade( float initialTrim, bool stopAfterFade )
+{
+ m_fadeTrim = initialTrim;
+ m_stopAfterFade = stopAfterFade;
+
+ //
+ // Check whether we're already faded
+ //
+ if( ( m_fadeTrim <= 0.0f )
+ && stopAfterFade )
+ {
+ m_player->Stop();
+ }
+ else
+ {
+ //
+ // Set the trim, in case the parent hasn't done it already
+ //
+ m_player->SetTrim( initialTrim );
+ }
+}
+
+void EngineState::ServiceFade( unsigned int elapsedTime )
+{
+ //
+ // Fade from 1.0f to 0.0f in 1/10th of a second, time picked arbitrarily
+ //
+ m_fadeTrim -= static_cast<float>( elapsedTime ) / 100.0f;
+ if( m_fadeTrim < 0.0f )
+ {
+ m_fadeTrim = 0.0f;
+ }
+
+ m_player->SetTrim( m_fadeTrim );
+
+ if( ( m_fadeTrim == 0.0f )
+ && m_stopAfterFade )
+ {
+ m_player->Stop();
+ }
+}
+
+void EngineState::SetTrim( float trim )
+{
+ //
+ // Cancel fades in effect
+ //
+ m_fadeTrim = 0.0f;
+
+ m_player->SetTrim( trim );
+}
+
+//******************************************************************************
+//
+// NormalEngineState
+//
+//******************************************************************************
+
+NormalEngineState::NormalEngineState() :
+ m_currentGear( 0 ),
+ m_currentSpeed( 0.0f ),
+ m_isAttacking( false ),
+ m_currentTrim( 1.0f ),
+ m_interpolateTime( 0 )
+{
+}
+
+NormalEngineState::~NormalEngineState()
+{
+}
+
+void NormalEngineState::Service( unsigned int elapsedTime )
+{
+ GearShiftResult startedShift;
+ float newPitch;
+ float trimDiff;
+ float pitchDiff;
+ float maxDiff;
+ float speedPcnt;
+ float shiftPoint;
+ float shiftSpeed;
+ float topSpeed;
+
+ if( ( m_interpolateTime > 0 ) && !m_isAttacking )
+ {
+ //
+ // Delaying
+ //
+ if( elapsedTime > m_interpolateTime )
+ {
+ //
+ // Delay done, start attack
+ //
+ m_interpolateTime = static_cast<unsigned int>(m_parameters->GetAttackTimeMsecs());
+ m_isAttacking = true;
+ }
+ else
+ {
+ //
+ // Delay not done
+ //
+ m_interpolateTime -= elapsedTime;
+
+ if( m_interpolateTime <= m_parameters->GetDelayTimeMsecs() )
+ {
+ m_currentTrim = m_parameters->GetDecayFinishTrim();
+ m_player->SetPitch( 1.0f );
+ }
+
+ //
+ // Since we're delaying, don't do anything else
+ //
+ return;
+ }
+ }
+ else if( m_interpolateTime > 0 )
+ {
+ //
+ // Attacking
+ //
+ if( elapsedTime > m_interpolateTime )
+ {
+ m_currentTrim = 1.0f;
+ m_interpolateTime = 0;
+ m_isAttacking = false;
+ }
+ else
+ {
+ m_interpolateTime -= elapsedTime;
+ trimDiff = static_cast<float>(elapsedTime) / m_parameters->GetAttackTimeMsecs();
+ m_currentTrim += trimDiff * ( 1.0f - m_parameters->GetDecayFinishTrim() ); // Increase at half-speed because we start at 0.5 above
+ if( m_currentTrim > 1.0f )
+ {
+ //
+ // Interpolation error
+ //
+ m_currentTrim = 1.0f;
+ }
+ }
+ }
+ else
+ {
+ //
+ // Regular engine playback, make sure the attack is off and the trim is maxed.
+ //
+ m_isAttacking = false;
+
+ if( m_currentTrim != 1.0f )
+ {
+ m_currentTrim = 1.0f;
+ }
+ }
+
+ m_currentSpeed = m_vehicle->GetSpeedKmh();
+
+ if( m_vehicle->IsMovingBackward() )
+ {
+ m_nextState = ENGINE_STATE_REVERSE;
+ }
+ else if( m_vehicle->IsAirborn() )
+ {
+ m_nextState = ENGINE_STATE_IN_AIR;
+ }
+ else if( isSkidding() )
+ {
+ m_nextState = ENGINE_STATE_SKIDDING;
+ }
+ else
+ {
+ startedShift = updateCurrentGearFromVehicle();
+ if( ( startedShift == GEARSHIFT_UP )
+ || ( startedShift == GEARSHIFT_DOWN ) )
+ {
+ if( startedShift == GEARSHIFT_UP )
+ {
+ m_nextState = ENGINE_STATE_UPSHIFTING;
+ //
+ // Don't stop the player, start delay
+ //
+ m_interpolateTime = m_parameters->GetDelayTimeMsecs()
+ + static_cast<unsigned int>(m_parameters->GetDecayTimeMsecs());
+ if( m_interpolateTime > 0 )
+ {
+ m_currentTrim = 0.0f;
+ }
+ }
+ else if( m_currentGear > 0 )
+ {
+ m_nextState = ENGINE_STATE_DOWNSHIFTING;
+ }
+ else
+ {
+ m_nextState = ENGINE_STATE_IDLING;
+ }
+ }
+ else
+ {
+ if( startedShift == GEARSHIFT_DAMPED )
+ {
+ //
+ // If we're going slower than the speed appropriate for the gear,
+ // don't let the pitch drop too low
+ //
+ shiftPoint = m_parameters->GetShiftPoint( m_currentGear );
+ topSpeed = m_vehicle->mDesignerParams.mDpTopSpeedKmh;
+ shiftSpeed = shiftPoint * topSpeed;
+ speedPcnt = m_currentSpeed / shiftSpeed;
+ newPitch = m_parameters->CalculateEnginePitch( m_currentGear, shiftSpeed, topSpeed );
+ newPitch *= speedPcnt;
+ }
+ else
+ {
+ //
+ // No shift, just set the pitch to value appropriate to the vehicle speed
+ //
+ newPitch = m_parameters->CalculateEnginePitch( m_currentGear, m_currentSpeed,
+ m_vehicle->mDesignerParams.mDpTopSpeedKmh );
+ }
+
+ pitchDiff = newPitch - m_currentPitch;
+ maxDiff = static_cast<float>(elapsedTime) / m_parameters->GetMsecsPerOctaveCap();
+
+ //
+ // Ignore the calculated pitch if it means we're going to jump around
+ // too suddenly
+ //
+ if( pitchDiff >= 0.0f )
+ {
+ // Pitch rising
+ m_currentPitch += ( pitchDiff > maxDiff ) ? maxDiff : pitchDiff;
+ }
+ else
+ {
+ // Pitch dropping
+ m_currentPitch += ( pitchDiff < -maxDiff ) ? -maxDiff : pitchDiff;
+ }
+
+ if( m_currentPitch > m_revLimit )
+ {
+ m_currentPitch = m_revLimit;
+ }
+
+ m_player->SetPitch( m_currentPitch );
+ SET_CURRENT_PITCH( m_currentPitch );
+
+ if( !m_isAttacking )
+ {
+ m_currentTrim = 1.0f;
+ }
+ }
+ }
+
+ if( m_nextState != ENGINE_STATE_INVALID )
+ {
+ if( ( m_nextState == ENGINE_STATE_DOWNSHIFTING )
+ || ( m_nextState == ENGINE_STATE_UPSHIFTING ) )
+ {
+ StartFade( m_currentTrim, false );
+ }
+ else
+ {
+ SetTrim( m_currentTrim );
+ }
+
+ if( m_nextState != ENGINE_STATE_UPSHIFTING )
+ {
+ SetActive( false );
+ }
+ }
+ else
+ {
+ SetTrim( m_currentTrim );
+ }
+
+ rAssertMsg( m_currentPitch >= 0.0f, "If you're running a debugger, tell Esan about this" );
+}
+
+void NormalEngineState::SetDebugInfo()
+{
+ if( IsActive() )
+ {
+ SET_CURRENT_GEAR( m_currentGear );
+ SET_CURRENT_SPEED( m_currentSpeed );
+ }
+}
+
+float NormalEngineState::GetCurrentPitch()
+{
+ return( m_currentPitch );
+}
+
+void NormalEngineState::startup( float prevPitch )
+{
+ m_currentSpeed = m_vehicle->GetSpeedKmh();
+ m_currentGear = m_parameters->CalculateCurrentGear( m_vehicle->GetSpeedKmh(), m_currentSpeed,
+ m_vehicle->mDesignerParams.mDpTopSpeedKmh,
+ -1 );
+
+ //
+ // Start the sound
+ //
+ if( !(m_player->IsInUse()) )
+ {
+ m_player->PlaySound( m_resourceKey );
+ }
+
+ m_currentTrim = 1.0f;
+ m_interpolateTime = 0;
+ if( prevPitch > 0.0f )
+ {
+ m_currentPitch = prevPitch;
+ }
+ else
+ {
+ m_currentPitch = m_parameters->CalculateEnginePitch( m_currentGear, m_currentSpeed,
+ m_vehicle->mDesignerParams.mDpTopSpeedKmh );
+ }
+ SetTrim( m_currentTrim );
+
+ if( m_currentPitch > m_revLimit )
+ {
+ m_currentPitch = m_revLimit;
+ }
+ m_player->SetPitch( m_currentPitch );
+}
+
+//=============================================================================
+// NormalEngineState::updateCurrentGearFromVehicle
+//=============================================================================
+// Description: Update m_currentGear from the current state of m_vehicle
+//
+// Parameters: dampenShifts - indicates whether we want to eliminate unnecessary
+// gear shifts. Not needed when we get in the car.
+//
+// Return: enumeration indicating if we shifted and if so, which direction
+//
+//=============================================================================
+GearShiftResult NormalEngineState::updateCurrentGearFromVehicle( bool dampenShifts /* = false */ )
+{
+ int currentGear;
+ GearShiftResult shiftResult;
+ float damperSize;
+ float upperShiftSpeed;
+ int oldGear = m_currentGear;
+ float topSpeed = m_vehicle->mDesignerParams.mDpTopSpeedKmh;
+
+ if( dampenShifts )
+ {
+ currentGear = m_currentGear;
+ }
+ else
+ {
+ currentGear = -1;
+ }
+
+ m_currentGear = m_parameters->CalculateCurrentGear( m_vehicle->GetSpeedKmh(), m_currentSpeed,
+ topSpeed, -1 );
+
+ if( m_currentGear == oldGear )
+ {
+ shiftResult = GEARSHIFT_NONE;
+ }
+ else
+ {
+ if( m_currentGear > oldGear )
+ {
+ if( m_vehicle->IsSafeToUpShift() )
+ {
+ shiftResult = GEARSHIFT_UP;
+ }
+ else
+ {
+ //
+ // Don't upshift when we're climbing hills, because the engine
+ // tends to stay bogged down at low revs
+ //
+ m_currentGear = oldGear;
+ shiftResult = GEARSHIFT_NONE;
+ }
+ }
+ else if( m_currentGear == 0 )
+ {
+ //
+ // Don't damp going into idle
+ //
+ shiftResult = GEARSHIFT_DOWN;
+ }
+ else
+ {
+ //
+ // Apply some gearshift damping
+ //
+ damperSize = m_parameters->GetDownshiftDamperSize() * topSpeed;
+ upperShiftSpeed = m_parameters->GetShiftPoint( m_currentGear + 1 ) * topSpeed;
+ if( ( upperShiftSpeed - m_currentSpeed ) <= damperSize )
+ {
+ shiftResult = GEARSHIFT_DAMPED;
+
+ //
+ // Wah. I think there's a problem where having the top gear too narrow can
+ // cause us to bump up into a non-existent gear. Don't do that.
+ //
+ if( upperShiftSpeed < topSpeed )
+ {
+ ++m_currentGear;
+ }
+ //else
+ //{
+ // rDebugString("Gotcha!");
+ //}
+ }
+ else
+ {
+ shiftResult = GEARSHIFT_DOWN;
+ }
+ }
+ }
+
+ return( shiftResult );
+}
+
+//******************************************************************************
+//
+// UpshiftEngineState
+//
+//******************************************************************************
+
+UpshiftEngineState::UpshiftEngineState()
+{
+}
+
+UpshiftEngineState::~UpshiftEngineState()
+{
+}
+
+void UpshiftEngineState::Service( unsigned int elapsedTime )
+{
+ float newPitch;
+ float timePercent;
+ float newTrim;
+ float targetTrim;
+
+ if( elapsedTime > m_remainingTime )
+ {
+ //
+ // Upshift is done, return to normal engine sound
+ //
+
+ //
+ // No need for state change, normal doesn't shut off on
+ // upshifts anymore
+ //
+ //m_nextState = ENGINE_STATE_NORMAL;
+
+ SetTrim( 1.0f );
+ m_player->Stop();
+ SetActive( false );
+ }
+ else
+ {
+ //
+ // Calculate the amount we need to drop the pitch in this frame
+ //
+ m_remainingTime -= elapsedTime;
+ timePercent = static_cast<float>(m_remainingTime) / static_cast<float>(m_shiftTimeMsecs);
+ newPitch = m_startPitch - ( ( 1.0f - timePercent ) * m_pitchDrop );
+ targetTrim = m_parameters->GetDecayFinishTrim();
+ newTrim = targetTrim + ( ( 1.0f - targetTrim ) * timePercent );
+
+ m_player->SetPitch( newPitch );
+ SET_CURRENT_PITCH( newPitch );
+ SetTrim( newTrim );
+ }
+}
+
+void UpshiftEngineState::SetDebugInfo()
+{
+ if( IsActive() )
+ {
+ SET_SHIFT_IN_PROGRESS( true );
+ SET_SHIFT_TIME_REMAINING( m_remainingTime );
+ SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() );
+ }
+}
+
+void UpshiftEngineState::startup( float prevPitch )
+{
+ int currentGear;
+ float currentSpeed;
+
+ //
+ // Initialize the current pitch, the amount we're going to drop it by,
+ // and the length of time we'll take to drop it
+ //
+ currentSpeed = m_vehicle->GetSpeedKmh();
+ currentGear = m_parameters->CalculateCurrentGear( currentSpeed, currentSpeed,
+ m_vehicle->mDesignerParams.mDpTopSpeedKmh,
+ -1 );
+
+ //
+ // Don't forget to subtract one from the gear because the parameter calculation
+ // will already have signalled the upshift, and we still want the old gear's pitch
+ //
+ m_startPitch = m_parameters->CalculateEnginePitch( currentGear - 1, currentSpeed,
+ m_vehicle->mDesignerParams.mDpTopSpeedKmh );
+ m_pitchDrop = m_parameters->GetGearShiftPitchDrop( currentGear );
+ m_shiftTimeMsecs = m_parameters->GetDecayTimeMsecs();
+ m_remainingTime = static_cast<unsigned int>(m_shiftTimeMsecs);
+
+ //
+ // Start the player
+ //
+ if( !(m_player->IsInUse()) )
+ {
+ m_player->PlaySound( m_resourceKey );
+ }
+ m_player->SetPitch( m_startPitch );
+ SetTrim( 1.0f );
+}
+
+//******************************************************************************
+//
+// DownshiftEngineState
+//
+//******************************************************************************
+
+DownshiftEngineState::DownshiftEngineState()
+{
+}
+
+DownshiftEngineState::~DownshiftEngineState()
+{
+}
+
+void DownshiftEngineState::Service( unsigned int elapsedTime )
+{
+ float speedPercent;
+ float idlePitch;
+ float currentSpeed = m_vehicle->GetSpeedKmh();
+
+ if( isAtIdleSpeed() )
+ {
+ //
+ // Switch to idle sound
+ //
+ m_nextState = ENGINE_STATE_IDLING;
+
+ m_player->Stop();
+ SetActive( false );
+ }
+ else if( currentSpeed > m_lastSpeed )
+ {
+ //
+ // We've started accelerating again, switch to normal engine sound
+ //
+ m_nextState = ENGINE_STATE_NORMAL;
+
+ StartFade( 1.0f );
+ SetActive( false );
+ }
+ else
+ {
+ //
+ // Calculate new engine pitch
+ //
+ speedPercent = m_vehicle->GetSpeedKmh() / m_startSpeed;
+ // For now, use idle engine pitch for bottom of pitch range
+ idlePitch = m_parameters->GetIdleEnginePitch();
+ m_currentPitch = idlePitch + ( speedPercent * ( m_startPitch - idlePitch ) );
+
+ m_player->SetPitch( m_currentPitch );
+ SET_CURRENT_PITCH( m_currentPitch );
+
+ m_lastSpeed = currentSpeed;
+ }
+}
+
+void DownshiftEngineState::SetDebugInfo()
+{
+ if( IsActive() )
+ {
+ SET_SHIFT_IN_PROGRESS( true );
+ SET_SHIFT_TIME_REMAINING( 0 );
+ SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() );
+ }
+}
+
+float DownshiftEngineState::GetCurrentPitch()
+{
+ return( m_currentPitch );
+}
+
+void DownshiftEngineState::startup( float prevPitch )
+{
+ //int currentGear;
+
+ m_startSpeed = m_vehicle->GetSpeedKmh();
+ m_lastSpeed = m_startSpeed;
+
+ //
+ // Find the speed that we need to interpolate over
+ //
+
+ // "+ 1" is because we're already in the new gear, and we want to interpolate
+ // from the last one
+ //currentGear = m_parameters->CalculateCurrentGear( m_startSpeed, m_startSpeed,
+ // m_vehicle->mDesignerParams.mDpTopSpeedKmh,
+ // -1 ) + 1;
+
+ //m_startPitch = m_parameters->CalculateEnginePitch( currentGear, m_startSpeed,
+ // m_vehicle->mDesignerParams.mDpTopSpeedKmh );
+ m_startPitch = prevPitch;
+ m_currentPitch = m_startPitch;
+
+ //
+ // Start the sound
+ //
+ if( !(m_player->IsInUse()) )
+ {
+ m_player->PlaySound( m_resourceKey );
+ }
+ m_player->SetPitch( m_startPitch );
+}
+
+//******************************************************************************
+//
+// InAirEngineState
+//
+//******************************************************************************
+
+InAirEngineState::InAirEngineState()
+{
+}
+
+InAirEngineState::~InAirEngineState()
+{
+}
+
+void InAirEngineState::Service( unsigned int elapsedTime )
+{
+ float maxPitchChange;
+ float idlePitch;
+ float throttlePitch;
+ unsigned int responseTime;
+ float targetPitch;
+ float newPitch;
+
+ if( !(m_vehicle->IsAirborn()) )
+ {
+ if( isAtIdleSpeed() )
+ {
+ m_nextState = ENGINE_STATE_IDLING;
+ }
+ else if( isSkidding() )
+ {
+ m_nextState = ENGINE_STATE_SKIDDING;
+ }
+ else
+ {
+ m_nextState = ENGINE_STATE_NORMAL;
+ }
+
+ m_player->Stop();
+ SetActive( false );
+ }
+ else
+ {
+ //
+ // Calculate the maximum pitch change given the car's responsiveness
+ idlePitch = m_parameters->GetInAirIdlePitch();
+ throttlePitch = m_parameters->GetInAirThrottlePitch();
+ responseTime = m_parameters->GetInAirThrottleResponseTimeMsecs();
+ maxPitchChange = ( throttlePitch - idlePitch )
+ * ( static_cast<float>(elapsedTime) / static_cast<float>(responseTime) );
+
+ //
+ // Get target pitch
+ //
+ targetPitch = idlePitch + ( m_vehicle->mGas * ( throttlePitch - idlePitch ) );
+ if( targetPitch > m_currentPitch )
+ {
+ if( m_currentPitch + maxPitchChange > targetPitch )
+ {
+ newPitch = targetPitch;
+ }
+ else
+ {
+ newPitch = m_currentPitch + maxPitchChange;
+ }
+ }
+ else
+ {
+ if( m_currentPitch - maxPitchChange < targetPitch )
+ {
+ newPitch = targetPitch;
+ }
+ else
+ {
+ newPitch = m_currentPitch - maxPitchChange;
+ }
+ }
+
+ m_player->SetPitch( newPitch );
+ m_currentPitch = newPitch;
+ }
+}
+
+void InAirEngineState::SetDebugInfo()
+{
+ if( IsActive() )
+ {
+ SET_CURRENT_PITCH( m_currentPitch );
+ SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() );
+ }
+}
+
+void InAirEngineState::startup( float prevPitch )
+{
+ if( !(m_player->IsInUse()) )
+ {
+ m_player->PlaySound( m_resourceKey );
+ }
+
+ m_currentPitch = prevPitch;
+
+ m_player->SetPitch( m_currentPitch );
+}
+
+//******************************************************************************
+//
+// ReverseEngineState
+//
+//******************************************************************************
+
+ReverseEngineState::ReverseEngineState( SimpsonsSoundPlayer* beepPlayer ) :
+ m_beepPlayer( beepPlayer )
+{
+}
+
+ReverseEngineState::~ReverseEngineState()
+{
+}
+
+void ReverseEngineState::Service( unsigned int elapsedTime )
+{
+ float speed;
+ float maxReverseSpeed;
+ float minPitch;
+ float maxPitch;
+ float newPitch;
+
+ speed = m_vehicle->GetSpeedKmh();
+
+ if( !(m_vehicle->IsMovingBackward()) )
+ {
+ if( m_vehicle->IsAirborn() )
+ {
+ m_nextState = ENGINE_STATE_IN_AIR;
+ }
+ else if( isAtIdleSpeed() )
+ {
+ m_nextState = ENGINE_STATE_IDLING;
+ }
+ else if( isSkidding() )
+ {
+ m_nextState = ENGINE_STATE_SKIDDING;
+ }
+ else
+ {
+ m_nextState = ENGINE_STATE_NORMAL;
+ }
+
+ m_beepPlayer->Stop();
+ SetActive( false );
+ }
+ else
+ {
+ //
+ // Set pitch based on speed
+ //
+ maxReverseSpeed = m_parameters->GetReversePitchCapKmh();
+ m_parameters->GetReversePitchRange( minPitch, maxPitch );
+ if( speed > maxReverseSpeed )
+ {
+ newPitch = maxPitch;
+ }
+ else
+ {
+ newPitch = minPitch + ( ( speed / maxReverseSpeed ) * ( maxPitch - minPitch ) );
+ }
+
+ m_player->SetPitch( newPitch );
+ SET_CURRENT_PITCH( newPitch );
+ }
+}
+
+void ReverseEngineState::SetDebugInfo()
+{
+ if( IsActive() )
+ {
+ SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() );
+ SET_CURRENT_GEAR( -1 );
+ }
+}
+
+void ReverseEngineState::startup( float prevPitch )
+{
+ const char* backupClipName;
+
+ if( !(m_player->IsInUse()) )
+ {
+ m_player->PlaySound( m_resourceKey );
+ }
+
+ //
+ // Call Service() to set the pitch
+ //
+ Service( 0 );
+
+ //
+ // If we have a backup beep sound, play it
+ //
+ if( ( backupClipName = m_parameters->GetBackupClipName() ) != NULL )
+ {
+ m_beepPlayer->PlaySound( backupClipName );
+ }
+}
+
+//******************************************************************************
+//
+// IdleEngineState
+//
+//******************************************************************************
+
+IdleEngineState::IdleEngineState() :
+ m_currentPitch( 0.0f )
+{
+}
+
+IdleEngineState::~IdleEngineState()
+{
+}
+
+void IdleEngineState::Service( unsigned int elapsedTime )
+{
+ float idlePitch;
+ float pitchDiff;
+ float maxDiff;
+
+ //
+ // Check for car motion, indicating we're leaving the idle state
+ //
+ if( m_vehicle->IsMovingBackward() )
+ {
+ m_nextState = ENGINE_STATE_REVERSE;
+ }
+ else if( isSkidding() )
+ {
+ m_nextState = ENGINE_STATE_SKIDDING;
+ }
+ else if( !isAtIdleSpeed() )
+ {
+ m_nextState = ENGINE_STATE_NORMAL;
+ }
+
+ if( m_nextState != ENGINE_STATE_INVALID )
+ {
+ //
+ // Deactivate self
+ //
+ m_player->Stop();
+ SetActive( false );
+ }
+ else
+ {
+ //
+ // Keep playing idle. Interpolate to it if we're not playing
+ // the idle pitch already
+ //
+ idlePitch = m_parameters->GetIdleEnginePitch();
+ if( m_currentPitch != idlePitch )
+ {
+ pitchDiff = idlePitch - m_currentPitch;
+ maxDiff = static_cast<float>(elapsedTime) / m_parameters->GetMsecsPerOctaveCap();
+
+ //
+ // Ignore the calculated pitch if it means we're going to jump around
+ // too suddenly
+ //
+ if( pitchDiff >= 0.0f )
+ {
+ // Pitch rising
+ m_currentPitch += ( pitchDiff > maxDiff ) ? maxDiff : pitchDiff;
+ }
+ else
+ {
+ // Pitch dropping
+ m_currentPitch += ( pitchDiff < -maxDiff ) ? -maxDiff : pitchDiff;
+ }
+
+ m_player->SetPitch( m_currentPitch );
+ SET_CURRENT_PITCH( m_currentPitch );
+ }
+ }
+}
+
+void IdleEngineState::SetDebugInfo()
+{
+ if( IsActive() )
+ {
+ SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() );
+ SET_CURRENT_GEAR( 0 );
+ }
+}
+
+float IdleEngineState::GetCurrentPitch()
+{
+ return( m_currentPitch );
+}
+
+void IdleEngineState::startup( float prevPitch )
+{
+ rAssert( m_parameters != NULL );
+
+ if( !(m_player->IsInUse()) )
+ {
+ m_player->PlaySound( m_resourceKey );
+ }
+
+ m_currentPitch = m_parameters->GetIdleEnginePitch();
+ if( prevPitch != m_currentPitch )
+ {
+ m_currentPitch = prevPitch;
+ }
+
+ m_player->SetPitch( m_currentPitch );
+ SET_CURRENT_PITCH( m_currentPitch );
+}
+
+//******************************************************************************
+//
+// SkidEngineState
+//
+//******************************************************************************
+
+SkidEngineState::SkidEngineState() :
+ m_currentPitch( 0.0f )
+{
+}
+
+SkidEngineState::~SkidEngineState()
+{
+}
+
+void SkidEngineState::Service( unsigned int elapsedTime )
+{
+ float burnoutLevel;
+ float desiredPitch;
+ float minBurnoutPitch;
+ float minPowerslidePitch;
+ float pitchDiff;
+ float maxDiff;
+
+ //
+ // Get a desired pitch based on the burnout level that the
+ // vehicle object gives us
+ //
+ if( isSkidding() )
+ {
+ //
+ // Sometimes we're skidding, sometimes we're burning out. Try to figure out
+ // which value makes more sense.
+ //
+ if( m_vehicle->mBurnoutLevel > 0.0f )
+ {
+ burnoutLevel = m_vehicle->mBurnoutLevel;
+
+ minBurnoutPitch = m_parameters->GetBurnoutMinPitch();
+ desiredPitch = minBurnoutPitch +
+ ( ( m_parameters->GetBurnoutMaxPitch() - minBurnoutPitch )
+ * burnoutLevel );
+ }
+ else
+ {
+ //burnoutLevel = m_vehicle->GetSkidLevel();
+
+ //
+ // The skid level appears to be a constant value of 0.5. Try using the
+ // vehicle gas value instead, since the tires would be spinning kinda
+ // freely in the real world anyway.
+ //
+ // HACK: Since sliding skids seem to be quite different from burnouts,
+ // bump the burnout level beyond 1.0f at will to make this sound right.
+ // This should be a separate set of tuning parameters next time.
+ //
+ burnoutLevel = m_vehicle->mGas;
+
+ minPowerslidePitch = m_parameters->GetPowerslideMinPitch();
+ desiredPitch = minPowerslidePitch +
+ ( ( m_parameters->GetPowerslideMaxPitch() - minPowerslidePitch )
+ * burnoutLevel );
+ }
+
+ //
+ // Ease toward desired pitch
+ //
+ pitchDiff = desiredPitch - m_currentPitch;
+ maxDiff = static_cast<float>(elapsedTime) / ( m_parameters->GetMsecsPerOctaveCap() * 4 );
+
+ //
+ // Ignore the calculated pitch if it means we're going to jump around
+ // too suddenly
+ //
+ if( pitchDiff >= 0.0f )
+ {
+ // Pitch rising
+ m_currentPitch += ( pitchDiff > maxDiff ) ? maxDiff : pitchDiff;
+ }
+ else
+ {
+ // Pitch dropping
+ m_currentPitch += ( pitchDiff < -maxDiff ) ? -maxDiff : pitchDiff;
+ }
+
+ m_player->SetPitch( m_currentPitch );
+ SET_CURRENT_PITCH( m_currentPitch );
+ }
+ else
+ {
+ if( isAtIdleSpeed() )
+ {
+ m_nextState = ENGINE_STATE_IDLING;
+ }
+ else if( m_vehicle->IsMovingBackward() )
+ {
+ m_nextState = ENGINE_STATE_REVERSE;
+ }
+ else
+ {
+ m_nextState = ENGINE_STATE_NORMAL;
+ }
+
+ SetActive( false );
+ }
+}
+
+void SkidEngineState::SetDebugInfo()
+{
+ if( IsActive() )
+ {
+ SET_CURRENT_SPEED( m_vehicle->GetSpeedKmh() );
+ }
+}
+
+float SkidEngineState::GetCurrentPitch()
+{
+ return( m_currentPitch );
+}
+
+void SkidEngineState::startup( float prevPitch )
+{
+ rAssert( m_parameters != NULL );
+
+ if( !(m_player->IsInUse()) )
+ {
+ m_player->PlaySound( m_resourceKey );
+ }
+
+ m_currentPitch = prevPitch;
+
+ m_player->SetPitch( m_currentPitch );
+ SET_CURRENT_PITCH( m_currentPitch );
+}
+
+//******************************************************************************
+//
+// VehicleSoundPlayer
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// VehicleSoundPlayer::VehicleSoundPlayer
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+VehicleSoundPlayer::VehicleSoundPlayer() :
+#ifdef SOUND_DEBUG_INFO_ENABLED
+ m_debugInfo( 2, GetSoundManager()->GetDebugDisplay() ),
+#endif
+ m_vehicle( NULL ),
+ m_parameters( NULL ),
+ m_peeloutSettings( NULL ),
+ m_isSkidding( false ),
+ m_hornPlaying( false ),
+ m_oneTimeHorn( false ),
+ m_powerslideTrim( 0.0f ),
+ m_playingDamage( false ),
+ m_proximityAIVehicle( NULL ),
+ m_terrainType( TT_Road )
+{
+ m_engineStates[ENGINE_STATE_NORMAL] = new NormalEngineState();
+ m_engineStates[ENGINE_STATE_UPSHIFTING] = new UpshiftEngineState();
+ m_engineStates[ENGINE_STATE_DOWNSHIFTING] = new DownshiftEngineState();
+ m_engineStates[ENGINE_STATE_IN_AIR] = new InAirEngineState();
+ m_engineStates[ENGINE_STATE_REVERSE] = new ReverseEngineState( &(m_soundPlayers[CARPLAYER_BACKUP_BEEP]) );
+ m_engineStates[ENGINE_STATE_IDLING] = new IdleEngineState();
+ m_engineStates[ENGINE_STATE_SKIDDING] = new SkidEngineState();
+
+ if( !s_resetEngineInitialized )
+ {
+ radDbgWatchAddBoolean( &s_resetEngineOnHonk, "Reset engine on honk", "Sound Info" );
+ }
+}
+
+//==============================================================================
+// VehicleSoundPlayer::~VehicleSoundPlayer
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+VehicleSoundPlayer::~VehicleSoundPlayer()
+{
+ int i;
+
+ for( i = 0; i < NUM_ENGINE_STATES; i++ )
+ {
+ delete m_engineStates[i];
+ }
+}
+
+//=============================================================================
+// VehicleSoundPlayer::StartCarSounds
+//=============================================================================
+// Description: Begin playing car-related sounds
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::StartCarSounds( Vehicle* newVehicle )
+{
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ VehicleSoundDebugPage* debugPtr;
+ const char* overlayName;
+
+ rAssert( newVehicle != NULL );
+
+ GetEventManager()->TriggerEvent( EVENT_AVATAR_VEHICLE_TOGGLE, newVehicle );
+
+ //
+ // Don't bother switching sounds if we're already playing this car
+ //
+ if( ( newVehicle == m_vehicle ) && carSoundIsActive() )
+ {
+ return;
+ }
+
+ //
+ // Remember which vehicle we're playing
+ //
+ m_vehicle = newVehicle;
+
+ //
+ // Get the car sound parameter object for this vehicle.
+ //
+ // IMPORTANT: We assume that the object in the namespace has the same
+ // name as the one in the vehicle object, which comes from the loading
+ // script for that car, I think.
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+
+ nameSpaceObj = nameSpace->GetInstance( m_vehicle->GetName() );
+ if( nameSpaceObj != NULL )
+ {
+ m_parameters = reinterpret_cast<carSoundParameters*>( nameSpaceObj );
+
+ //
+ // Set up the Watcher tuning
+ //
+ m_parameters->SetWatcherName( m_vehicle->GetName() );
+
+ //
+ // Get global car values
+ //
+ nameSpaceObj = nameSpace->GetInstance( "tuner" );
+ rAssert( nameSpaceObj != NULL );
+ m_peeloutSettings = reinterpret_cast<globalSettings*>( nameSpaceObj );
+
+ const char* clipName = m_parameters->GetEngineClipName();
+
+ const char* idleClipName = m_parameters->GetEngineIdleClipName();
+ if( idleClipName == NULL )
+ {
+ //
+ // No idle clip, use the engine clip by default
+ //
+ idleClipName = clipName;
+ }
+
+ if( ( clipName != NULL ) && ( idleClipName != NULL ) )
+ {
+#ifdef SOUND_DEBUG_INFO_ENABLED
+ debugPtr = &m_debugInfo;
+#else
+ debugPtr = NULL;
+#endif
+
+ //
+ // Initialize the engine sound states
+ //
+ m_engineStates[ENGINE_STATE_NORMAL]->Initialize( newVehicle, m_parameters,
+ &m_soundPlayers[CARPLAYER_ENGINE],
+ ::radMakeKey32( clipName ),
+ debugPtr );
+ m_engineStates[ENGINE_STATE_UPSHIFTING]->Initialize( newVehicle, m_parameters,
+ &m_soundPlayers[CARPLAYER_SHIFT],
+ ::radMakeKey32( clipName ),
+ debugPtr );
+ m_engineStates[ENGINE_STATE_DOWNSHIFTING]->Initialize( newVehicle, m_parameters,
+ &m_soundPlayers[CARPLAYER_SHIFT],
+ ::radMakeKey32( clipName ),
+ debugPtr );
+ m_engineStates[ENGINE_STATE_IN_AIR]->Initialize( newVehicle, m_parameters,
+ &m_soundPlayers[CARPLAYER_ENGINE],
+ ::radMakeKey32( clipName ),
+ debugPtr );
+ m_engineStates[ENGINE_STATE_REVERSE]->Initialize( newVehicle, m_parameters,
+ &m_soundPlayers[CARPLAYER_ENGINE],
+ ::radMakeKey32( clipName ),
+ debugPtr );
+ m_engineStates[ENGINE_STATE_IDLING]->Initialize( newVehicle, m_parameters,
+ &m_soundPlayers[CARPLAYER_ENGINE],
+ ::radMakeKey32( idleClipName ),
+ debugPtr );
+ m_engineStates[ENGINE_STATE_SKIDDING]->Initialize( newVehicle, m_parameters,
+ &m_soundPlayers[CARPLAYER_ENGINE],
+ ::radMakeKey32( clipName ),
+ debugPtr );
+
+ //
+ // Assume that we're getting into an idle car
+ //
+ m_engineStates[ENGINE_STATE_IDLING]->SetActive( true );
+ }
+ else
+ {
+ //
+ // What the...? No engine clip? That's not right.
+ //
+ rTuneString( "WARNING: No engine clip found. No engine sounds will be played.\n" );
+ m_vehicle = NULL;
+ }
+
+ //
+ // If we have an overlay clip, start it
+ //
+ overlayName = m_parameters->GetOverlayClipName();
+ if( overlayName != NULL )
+ {
+ m_soundPlayers[CARPLAYER_OVERLAY].PlaySound( overlayName );
+ }
+ }
+ else
+ {
+ rTunePrintf( "Couldn't find carSoundParameters object named %s, no sound played\n",
+ m_vehicle->GetName() );
+ m_vehicle = NULL;
+ }
+}
+
+//=============================================================================
+// VehicleSoundPlayer::StopCarSounds
+//=============================================================================
+// Description: Stop playing car-related sounds
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::StopCarSounds()
+{
+ unsigned int i;
+
+ for( i = 0; i < CARPLAYER_NUMPLAYERS; i++ )
+ {
+ m_soundPlayers[i].Stop();
+ }
+
+ for( i = 0; i < NUM_ENGINE_STATES; i++ )
+ {
+ m_engineStates[i]->SetActive( false );
+ m_engineStates[i]->Reset();
+ }
+
+ m_proximityAIVehicle = NULL;
+
+ GetEventManager()->TriggerEvent( EVENT_AVATAR_VEHICLE_TOGGLE, m_vehicle );
+}
+
+//=============================================================================
+// VehicleSoundPlayer::UpdateSoundParameters
+//=============================================================================
+// Description: Look at the car sound parameter object, and make sure that
+// the engine sound is being played at a pitch appropriate for
+// the vehicle RPMs.
+//
+// Parameters: elapsedTime - time since last frame in msecs
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::UpdateSoundParameters( unsigned int elapsedTime )
+{
+ //float newPitch;
+ //unsigned int shiftTime;
+ int i;
+ static bool warningPrinted = false;
+ bool activateState[NUM_ENGINE_STATES];
+ float prevStatePitch[NUM_ENGINE_STATES];
+ EngineStateEnum newState;
+
+ //
+ // Before updating, check to make sure we have a vehicle. If we don't,
+ // then we got into a car that didn't have a carSoundParameters object
+ // associated with it. Handle it gracefully, but we really should fix it
+ //
+ if( m_vehicle != NULL )
+ {
+ for( i = 0; i < NUM_ENGINE_STATES; i++ )
+ {
+ activateState[i] = false;
+ prevStatePitch[i] = 0.0f;
+ }
+
+ //
+ // Service the active engine states
+ //
+ for( i = 0; i < NUM_ENGINE_STATES; i++ )
+ {
+ if( m_engineStates[i]->IsActive() )
+ {
+ m_engineStates[i]->Service( elapsedTime );
+ if( m_engineStates[i]->StartNewState() )
+ {
+ newState = m_engineStates[i]->GetNewState();
+ activateState[newState] = true;
+ prevStatePitch[newState] = m_engineStates[i]->GetCurrentPitch();
+ }
+ }
+ }
+
+ //
+ // Service fades
+ //
+ for( i = 0; i < NUM_ENGINE_STATES; i++ )
+ {
+ if( m_engineStates[i]->IsFading() )
+ {
+ m_engineStates[i]->ServiceFade( elapsedTime );
+ }
+ }
+
+ //
+ // Initialize debug info
+ //
+ SET_LOCAL_SHIFT_IN_PROGRESS( false );
+
+ //
+ // Start up any states that were marked for activation during servicing
+ //
+ for( i = 0; i < NUM_ENGINE_STATES; i++ )
+ {
+ if( activateState[i] )
+ {
+ m_engineStates[i]->SetActive( true, prevStatePitch[i] );
+ }
+
+ m_engineStates[i]->Reset();
+
+ m_engineStates[i]->SetDebugInfo();
+ }
+ }
+ else
+ {
+ if( !warningPrinted )
+ {
+ rTuneString("Warning: Vehicle doesn't have a sound associated with it (CarSoundParameters)\n");
+ warningPrinted = true;
+ }
+ }
+}
+
+//=============================================================================
+// VehicleSoundPlayer::UpdateOncePerFrame
+//=============================================================================
+// Description: Update routine, used to set the engine RPMs correctly and
+// check for skids, gear changes, and horns
+//
+// Parameters: elapsedTime - time since last frame in msecs
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::UpdateOncePerFrame( unsigned int elapsedTime )
+{
+ UpdateSoundParameters( elapsedTime );
+ checkDamage();
+ CheckForSkid( elapsedTime );
+ CheckHorn();
+ checkProximity();
+
+ //
+ // Debug stuff
+ //
+ if( ( m_parameters != NULL ) && ( m_vehicle != NULL ) )
+ {
+ /*SET_SHIFT_IN_PROGRESS( m_gearChangeInProgress );
+ SET_CURRENT_GEAR( m_currentGear );
+ SET_CURRENT_SPEED( m_currentSpeed );
+ SET_SHIFT_TIME_REMAINING( m_gearChangeTimeRemaining );
+ SET_DOWNSHIFT_SPEED( m_parameters->GetLowShiftPoint( m_currentGear ) * m_vehicle->mDesignerParams.mDpTopSpeedKmh );
+ SET_UPSHIFT_SPEED( m_parameters->GetHighShiftPoint( m_currentGear ) * m_vehicle->mDesignerParams.mDpTopSpeedKmh );*/
+ SET_LOCAL_IS_DAMAGED( m_playingDamage );
+ SET_LOCAL_VEHICLE_LIFE( m_vehicle->GetVehicleLifePercentage(m_vehicle->mHitPoints) );
+ SET_LOCAL_DAMAGE_THRESHOLD( m_parameters->GetDamageStartPcnt() );
+ }
+}
+
+//=============================================================================
+// VehicleSoundPlayer::CheckForSkid
+//=============================================================================
+// Description: Check the vehicle object to see if we're skidding, and make
+// the appropriate noise
+//
+// Parameters: elapsedTime - time elapsed in the last frame
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::CheckForSkid( unsigned int elapsedTime )
+{
+ bool skidPlaySuccessful;
+ float trim;
+ float skidLevel;
+ float peeloutMin;
+ float peeloutMax;
+ const char* skidName;
+ bool isSupersprint;
+ float trimMax;
+
+ //
+ // If we don't have a car, don't do anything
+ //
+ if( m_vehicle == NULL )
+ {
+ return;
+ }
+
+ //
+ // Find out if the car is skidding
+ //
+ skidLevel = m_vehicle->GetSkidLevel();
+ peeloutMin = m_peeloutSettings->GetPeeloutMin();
+ peeloutMax = m_peeloutSettings->GetPeeloutMax();
+ if( skidLevel > peeloutMin )
+ {
+ //
+ // Vehicle skidding. Start making tire squeal if we're not doing it yet
+ //
+ if( !m_isSkidding )
+ {
+ //
+ // Get the terrain type underneath the car so we know which sound
+ // resource to play
+ //
+ m_terrainType = m_vehicle->mTerrainType;
+ skidName = getSkidResourceForTerrain( m_terrainType );
+
+ //
+ // Squeal like a pig, boy
+ //
+ skidPlaySuccessful =
+ m_soundPlayers[CARPLAYER_SKID].PlaySound( skidName );
+ rAssert( skidPlaySuccessful );
+
+ m_isSkidding = true;
+ m_powerslideTrim = 0.0f;
+
+ //
+ // If we're starting from a slow speed, assume this is a burnout
+ //
+
+ // move the triggering, and triggering of the END of this event, to
+ // Vehicle since Cary wants it also, to play with the camera
+
+ //if( m_vehicle->GetSpeedKmh() < 10.0f )
+ //{
+ // GetEventManager()->TriggerEvent( EVENT_BURNOUT );
+ //}
+ }
+ else
+ {
+ //
+ // Check for whether we've changed terrain types
+ //
+ if( m_vehicle->mTerrainType != m_terrainType )
+ {
+ m_terrainType = m_vehicle->mTerrainType;
+ skidName = getSkidResourceForTerrain( m_terrainType );
+
+ m_soundPlayers[CARPLAYER_SKID].Stop();
+
+ skidPlaySuccessful =
+ m_soundPlayers[CARPLAYER_SKID].PlaySound( skidName );
+ rAssert( skidPlaySuccessful );
+ }
+ }
+
+ if( m_vehicle->mBurnoutLevel > 0.0f )
+ {
+ //
+ // Burnout
+ //
+ if( skidLevel >= peeloutMax )
+ {
+ trim = m_peeloutSettings->GetPeeloutMaxTrim();
+ }
+ else
+ {
+ trim = ( ( skidLevel - peeloutMin ) / ( peeloutMax - peeloutMin ) )
+ * m_peeloutSettings->GetPeeloutMaxTrim();
+ }
+ }
+ else
+ {
+ //
+ // Powerslide
+ //
+ if( ( GetGameplayManager() != NULL )
+ && ( GetGameplayManager()->IsSuperSprint() ) )
+ {
+ isSupersprint = true;
+ trimMax = SUPERSPRINT_POWERSLIDE_TRIM_MAX;
+ }
+ else
+ {
+ isSupersprint = false;
+ trimMax = GAME_POWERSLIDE_TRIM_MAX;
+ }
+
+ if( m_powerslideTrim <= 0.0f )
+ {
+ if( isSupersprint )
+ {
+ m_powerslideTrim = SUPERSPRINT_POWERSLIDE_TRIM_MIN;
+ }
+ else
+ {
+ m_powerslideTrim = GAME_POWERSLIDE_TRIM_MIN;
+ }
+ }
+ else
+ {
+ m_powerslideTrim += static_cast<float>(elapsedTime) * 0.0003f;
+ }
+
+ if( m_powerslideTrim > trimMax )
+ {
+ m_powerslideTrim = trimMax;
+ }
+
+ trim = m_powerslideTrim;
+ }
+ m_soundPlayers[CARPLAYER_SKID].SetTrim( trim );
+ }
+ else
+ {
+ //
+ // Not skidding. Stop the noise if it's playing
+ //
+ if( m_isSkidding )
+ {
+ m_soundPlayers[CARPLAYER_SKID].Stop();
+ m_isSkidding = false;
+ }
+ }
+}
+
+//=============================================================================
+// VehicleSoundPlayer::CheckHorn
+//=============================================================================
+// Description: Find the vehicle controller, and check to see whether the horn
+// button is being pressed and play/stop/do nothing with the
+// horn clip appropriately
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::CheckHorn()
+{
+ int vehicleId;
+ VehicleController* controller;
+ bool hornPlaySuccessful;
+ float buttonVal = 0.0f;
+ const char* hornClipName;
+ IDaSoundResource* resource;
+
+ if( m_vehicle == NULL )
+ {
+ return;
+ }
+
+ //
+ // Find the horn button for this vehicle and get its state
+ //
+ vehicleId = GetVehicleCentral()->GetVehicleId( m_vehicle, false );
+ controller = GetVehicleCentral()->GetVehicleController( vehicleId );
+
+ //
+ // Controller might already be gone, if we're playing car sound while
+ // the player is getting out of the car.
+ //
+ if( controller != NULL )
+ {
+ buttonVal = controller->GetHorn();
+ }
+
+ //
+ // Look for whether we need to start/stop a sound
+ //
+ if( buttonVal > 0.05f )
+ {
+ if( !m_hornPlaying )
+ {
+ //
+ // Horn button has just been pressed, find the correct clip and play it
+ //
+ rAssert( m_parameters != NULL );
+ hornClipName = m_parameters->GetHornClipName();
+
+ if( hornClipName == NULL )
+ {
+ hornClipName = "horn";
+ }
+
+ //
+ // Just in case we were still playing a non-looping horn sound
+ //
+ m_soundPlayers[CARPLAYER_HORNPLAYER].Stop();
+
+ hornPlaySuccessful =
+ m_soundPlayers[CARPLAYER_HORNPLAYER].PlaySound( hornClipName );
+ rAssert( hornPlaySuccessful );
+
+ m_hornPlaying = true;
+
+ //
+ // Find out if this is looping horn sound
+ //
+ resource = Sound::daSoundRenderingManagerGet()->GetResourceManager()->FindResource( hornClipName );
+ rAssert( resource != NULL );
+
+ // Sanity check
+ if( resource == NULL )
+ {
+ rTunePrintf( "No horn named %s found\n", hornClipName );
+ m_oneTimeHorn = true;
+ }
+ else
+ {
+ m_oneTimeHorn = !( resource->GetLooping() );
+ }
+
+#ifndef RAD_RELEASE
+ //
+ // Tuning hack. Stop and restart the engine to pick up any trim changes
+ //
+ if( s_resetEngineOnHonk )
+ {
+ Vehicle* theCar = m_vehicle;
+ StopCarSounds();
+ StartCarSounds( theCar );
+ }
+#endif
+ }
+ }
+ else
+ {
+ if( m_hornPlaying )
+ {
+ //
+ // Horn button has just been released, stop the clip
+ //
+ if( !m_oneTimeHorn )
+ {
+ m_soundPlayers[CARPLAYER_HORNPLAYER].Stop();
+ }
+ m_hornPlaying = false;
+ }
+ }
+}
+
+//=============================================================================
+// VehicleSoundPlayer::OnPlaybackComplete
+//=============================================================================
+// Description: SimpsonsSoundPlayer callback, used when gearshift is done
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::OnPlaybackComplete()
+{
+}
+
+//=============================================================================
+// VehicleSoundPlayer::OnSoundReady
+//=============================================================================
+// Description: Unused, required for SimpsonsSoundPlayerCallback interface
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::OnSoundReady()
+{
+}
+
+//=============================================================================
+// VehicleSoundPlayer::AddAIVehicleProximityTest
+//=============================================================================
+// Description: Add check for proximity to newly spawned AI vehicle
+//
+// Parameters: aiVehicle - vehicle to test
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::AddAIVehicleProximityTest( Vehicle* aiVehicle )
+{
+ if( m_proximityAIVehicle == NULL )
+ {
+ m_proximityAIVehicle = aiVehicle;
+ }
+}
+
+//=============================================================================
+// VehicleSoundPlayer::DeleteAIVehicleProximityTest
+//=============================================================================
+// Description: Stop proximity testing for given vehicle, if that's the one
+// we're currently testing.
+//
+// Parameters: aiVehicle - vehicle to stop testing for
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::DeleteAIVehicleProximityTest( Vehicle* aiVehicle )
+{
+ if( m_proximityAIVehicle == aiVehicle )
+ {
+ m_proximityAIVehicle = NULL;
+ }
+}
+
+//=============================================================================
+// VehicleSoundPlayer::PlayDoorOpen
+//=============================================================================
+// Description: Play a door opening sound
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::PlayDoorOpen()
+{
+}
+
+//=============================================================================
+// VehicleSoundPlayer::PlayDoorClose
+//=============================================================================
+// Description: Play a door closing sound
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::PlayDoorClose()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// VehicleSoundPlayer::checkDamage
+//=============================================================================
+// Description: Start or stop the damage sound for this vehicle as appropriate
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::checkDamage()
+{
+ float damagePercentage;
+ float lifePercentage;
+ float damageTrimPercentage;
+ float damageTrim;
+ float trimRange;
+ float minTrim;
+
+ if( m_vehicle == NULL )
+ {
+ return;
+ }
+
+ damagePercentage = m_parameters->GetDamageStartPcnt();
+ lifePercentage = m_vehicle->GetVehicleLifePercentage(m_vehicle->mHitPoints);
+ minTrim = m_parameters->GetDamageStartTrim();
+ trimRange = m_parameters->GetDamageMaxTrim() - m_parameters->GetDamageStartTrim();
+
+ if( ( lifePercentage <= damagePercentage ) && ( !m_playingDamage ) )
+ {
+ m_playingDamage = true;
+ m_soundPlayers[CARPLAYER_DAMAGE].PlaySound( m_parameters->GetDamagedEngineClipName() );
+
+ //
+ // Scale volume by amount of damage
+ //
+ damageTrimPercentage = ( damagePercentage - lifePercentage ) / m_parameters->GetDamageVolumeRange();
+ if( damageTrimPercentage > 1.0f )
+ {
+ damageTrimPercentage = 1.0f;
+ }
+ damageTrim = minTrim + ( damageTrimPercentage * trimRange );
+ m_soundPlayers[CARPLAYER_DAMAGE].SetTrim( damageTrim );
+ }
+ else if( m_playingDamage )
+ {
+ if( lifePercentage > damagePercentage )
+ {
+ //
+ // Must've reset or something
+ //
+ m_playingDamage = false;
+ m_soundPlayers[CARPLAYER_DAMAGE].Stop();
+ }
+ else
+ {
+ //
+ // Adjust volume
+ //
+ damageTrimPercentage = ( damagePercentage - lifePercentage ) / m_parameters->GetDamageVolumeRange();
+ if( damageTrimPercentage > 1.0f )
+ {
+ damageTrimPercentage = 1.0f;
+ }
+ damageTrim = minTrim + ( damageTrimPercentage * trimRange );
+ m_soundPlayers[CARPLAYER_DAMAGE].SetTrim( damageTrim );
+ }
+ }
+}
+
+//=============================================================================
+// VehicleSoundPlayer::carSoundIsActive
+//=============================================================================
+// Description: Indicate whether any of the engine sound states are going
+//
+// Parameters: None
+//
+// Return: true if a state is active, false otherwise
+//
+//=============================================================================
+bool VehicleSoundPlayer::carSoundIsActive()
+{
+ int i;
+ bool isActive = false;
+
+ for( i = 0; i < NUM_ENGINE_STATES; i++ )
+ {
+ if( m_engineStates[i]->IsActive() )
+ {
+ isActive = true;
+ break;
+ }
+ }
+
+ return( isActive );
+}
+
+//=============================================================================
+// VehicleSoundPlayer::checkProximity
+//=============================================================================
+// Description: Determine whether AI vehicle has gotten close enough to
+// trigger an event
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void VehicleSoundPlayer::checkProximity()
+{
+ rmt::Vector position1;
+ rmt::Vector position2;
+
+ //rAssert( m_vehicle != NULL );
+
+ if( m_proximityAIVehicle != NULL && m_vehicle != NULL )
+ {
+ m_vehicle->GetPosition( &position1 );
+ m_proximityAIVehicle->GetPosition( &position2 );
+
+ //
+ // Trigger event at 20 (use 20^2 for efficiency) metres
+ //
+ position1.Sub( position2 );
+ if( position1.MagnitudeSqr() < 400.0f )
+ {
+ GetEventManager()->TriggerEvent( EVENT_CHASE_VEHICLE_PROXIMITY );
+ m_proximityAIVehicle = NULL;
+ }
+ }
+}
+
+//=============================================================================
+// VehicleSoundPlayer::getSkidResourceForTerrain
+//=============================================================================
+// Description: Find the name of the sound resource for a skid appropriate
+// for the kind of ground we're driving over
+//
+// Parameters: terrain - type of terrain the car is over right now
+//
+// Return: name of sound resource to use
+//
+//=============================================================================
+const char* VehicleSoundPlayer::getSkidResourceForTerrain( eTerrainType terrain )
+{
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ globalSettings* clipNameObj;
+ const char* name;
+ bool terrainIsDirt = ( terrain == TT_Dirt )
+ || ( terrain == TT_Sand )
+ || ( terrain == TT_Gravel )
+ || ( terrain == TT_Grass );
+
+ //
+ // First, see if we have something specific for this car
+ //
+ rAssert( m_parameters != NULL );
+ if( terrainIsDirt )
+ {
+ name = m_parameters->GetDirtSkidClipName();
+ }
+ else
+ {
+ name = m_parameters->GetRoadSkidClipName();
+ }
+
+ if( name == NULL )
+ {
+ //
+ // No car-specific skid, get the global one from the globalSettings
+ // object
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ nameSpaceObj = nameSpace->GetInstance( "tuner" );
+ rAssert( nameSpaceObj != NULL );
+
+ clipNameObj = static_cast<globalSettings*>( nameSpaceObj );
+
+ if( terrainIsDirt )
+ {
+ name = clipNameObj->GetSkidDirtClipName();
+ }
+ else
+ {
+ name = clipNameObj->GetSkidRoadClipName();
+ }
+ }
+
+ rAssert( name != NULL );
+
+ return( name );
+}
diff --git a/game/code/sound/avatar/vehiclesoundplayer.h b/game/code/sound/avatar/vehiclesoundplayer.h
new file mode 100644
index 0000000..45e9665
--- /dev/null
+++ b/game/code/sound/avatar/vehiclesoundplayer.h
@@ -0,0 +1,162 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehiclesoundplayer.h
+//
+// Description: Declaration of the VehicleSoundPlayer class, which plays sounds
+// for user-controlled vehicles in game.
+//
+// History: 30/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef VEHICLESOUNDPLAYER_H
+#define VEHICLESOUNDPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <sound/simpsonssoundplayer.h>
+#include <sound/avatar/vehiclesounddebugpage.h>
+#include <render/IntersectManager/IntersectManager.h>
+
+//========================================
+// Forward References
+//========================================
+
+class Vehicle;
+class carSoundParameters;
+class globalSettings;
+class VehicleSoundPlayer;
+class EngineState;
+
+//
+// Enumerated list of temporary engine states
+//
+enum EngineStateEnum
+{
+ ENGINE_STATE_NORMAL,
+ ENGINE_STATE_UPSHIFTING,
+ ENGINE_STATE_DOWNSHIFTING,
+ ENGINE_STATE_IN_AIR,
+ ENGINE_STATE_REVERSE,
+ ENGINE_STATE_IDLING,
+ ENGINE_STATE_SKIDDING,
+
+ NUM_ENGINE_STATES,
+
+ ENGINE_STATE_INVALID
+};
+
+//=============================================================================
+//
+// Synopsis: VehicleSoundPlayer
+//
+//=============================================================================
+
+class VehicleSoundPlayer : public SimpsonsSoundPlayerCallback
+{
+ public:
+ VehicleSoundPlayer();
+ virtual ~VehicleSoundPlayer();
+
+ //
+ // Update routines
+ //
+ void UpdateOncePerFrame( unsigned int elapsedTime );
+
+ void UpdateSoundParameters( unsigned int elapsedTime );
+
+ //
+ // Check the vehicle to see if we should be playing a skid noise
+ //
+ void CheckForSkid( unsigned int elapsedTime );
+
+ //
+ // Check the vehicle controller to see if we should be playing
+ // a horn noise
+ //
+ void CheckHorn();
+
+ //
+ // Start and stop sound when the player gets in and out of the car
+ //
+ void StartCarSounds( Vehicle* newVehicle );
+ void StopCarSounds();
+
+ //
+ // Play door sounds
+ //
+ void PlayDoorOpen();
+ void PlayDoorClose();
+
+ //
+ // Called when gearshift is done playing
+ //
+ void OnPlaybackComplete();
+
+ //
+ // Needed to complete SimpsonsSoundPlayerCallback interface
+ //
+ void OnSoundReady();
+
+ //
+ // Proximity testing for AI vehicle dialog (better here than
+ // anywhere else)
+ //
+ void AddAIVehicleProximityTest( Vehicle* aiVehicle );
+ void DeleteAIVehicleProximityTest( Vehicle* aiVehicle );
+
+ private:
+ //Prevent wasteful constructor creation.
+ VehicleSoundPlayer( const VehicleSoundPlayer& original );
+ VehicleSoundPlayer& operator=( const VehicleSoundPlayer& rhs );
+
+ bool carSoundIsActive();
+
+ void checkDamage();
+ void checkProximity();
+
+ const char* getSkidResourceForTerrain( eTerrainType terrain );
+
+ enum CarSoundPlayers
+ {
+ CARPLAYER_ENGINE,
+ CARPLAYER_SHIFT,
+ CARPLAYER_SKID,
+ CARPLAYER_HORNPLAYER,
+ CARPLAYER_DAMAGE,
+ CARPLAYER_OVERLAY,
+ CARPLAYER_BACKUP_BEEP,
+ CARPLAYER_DOOR,
+
+ CARPLAYER_NUMPLAYERS
+ };
+
+#ifdef SOUND_DEBUG_INFO_ENABLED
+ VehicleSoundDebugPage m_debugInfo;
+#endif
+
+ SimpsonsSoundPlayer m_soundPlayers[CARPLAYER_NUMPLAYERS];
+ EngineState* m_engineStates[NUM_ENGINE_STATES];
+
+ Vehicle* m_vehicle;
+ carSoundParameters* m_parameters;
+ globalSettings* m_peeloutSettings;
+
+ bool m_isSkidding;
+ bool m_hornPlaying;
+ bool m_oneTimeHorn;
+ float m_powerslideTrim;
+
+ bool m_playingDamage;
+
+ Vehicle* m_proximityAIVehicle;
+
+ eTerrainType m_terrainType;
+};
+
+
+#endif // VEHICLESOUNDPLAYER_H
+
diff --git a/game/code/sound/dialog/alldialog.cpp b/game/code/sound/dialog/alldialog.cpp
new file mode 100644
index 0000000..b4681d5
--- /dev/null
+++ b/game/code/sound/dialog/alldialog.cpp
@@ -0,0 +1,11 @@
+#include <sound/dialog/conversation.cpp>
+#include <sound/dialog/conversationmatcher.cpp>
+#include <sound/dialog/dialogcoordinator.cpp>
+#include <sound/dialog/dialogline.cpp>
+#include <sound/dialog/dialoglist.cpp>
+#include <sound/dialog/dialogpriorityqueue.cpp>
+#include <sound/dialog/dialogqueueelement.cpp>
+#include <sound/dialog/dialogselectiongroup.cpp>
+#include <sound/dialog/playabledialog.cpp>
+#include <sound/dialog/selectabledialog.cpp>
+#include <sound/dialog/dialogsounddebugpage.cpp>
diff --git a/game/code/sound/dialog/conversation.cpp b/game/code/sound/dialog/conversation.cpp
new file mode 100644
index 0000000..4aac61b
--- /dev/null
+++ b/game/code/sound/dialog/conversation.cpp
@@ -0,0 +1,408 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: conversation.cpp
+//
+// Description: Conversation objects aggregate groups of DialogLine objects
+// which are intended to be played without interruption, like
+// a conversation. Hence the name.
+//
+// History: 02/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <stdio.h>
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/dialog/conversation.h>
+
+#include <sound/dialog/dialogline.h>
+#include <sound/dialog/dialogselectiongroup.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Conversation::Conversation
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Conversation::Conversation( DialogLine& line ) :
+ PlayableDialog( line.GetLevel(), line.GetMission(), line.GetEvent() ),
+ m_maxOrderNumber( line.GetConversationPosition() ),
+ m_dialogList( NULL ),
+ m_currentLine( NULL )
+{
+ line.AddToDialogList( reinterpret_cast<SelectableDialog**>(&m_dialogList) );
+}
+
+//==============================================================================
+// Conversation::~Conversation
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Conversation::~Conversation()
+{
+}
+
+//=============================================================================
+// Conversation::LineFits
+//=============================================================================
+// Description: Determine whether the given line matches this conversation
+//
+// Parameters: line - dialog line to test
+//
+// Return: true if it fits in this conversation, false otherwise
+//
+//=============================================================================
+bool Conversation::LineFits( DialogLine& line )
+{
+ bool matches;
+ DialogLine* existingLine = m_dialogList;
+
+ rAssert( existingLine != NULL );
+
+ //
+ // If the new line matches one dialog line in this conversation, it should
+ // match them all
+ //
+ matches = ( line.GetLevel() == existingLine->GetLevel() )
+ && ( line.GetMission() == existingLine->GetMission() )
+ && ( line.GetEvent() == existingLine->GetEvent() )
+ && ( line.GetConversationName() == existingLine->GetConversationName() );
+
+#ifdef RAD_DEBUG
+ if( matches )
+ {
+ rAssert( line.GetConversationPosition() != existingLine->GetConversationPosition() );
+ }
+#endif
+
+ return( matches );
+}
+
+//=============================================================================
+// Conversation::AddToConversation
+//=============================================================================
+// Description: Add the given dialog line to this conversation
+//
+// Parameters: line - line to be added
+//
+// Return: void
+//
+//=============================================================================
+void Conversation::AddToConversation( DialogLine& line )
+{
+ int position = line.GetConversationPosition();
+ DialogLine* listObj;
+ DialogLine* nextListObj;
+
+ if( ( m_dialogList == NULL )
+ || ( position < m_dialogList->GetConversationPosition() ) )
+ {
+ line.AddToDialogList( reinterpret_cast<SelectableDialog**>(&m_dialogList) );
+ }
+ else
+ {
+ listObj = m_dialogList;
+ // ARGH! Stinky downcast! I hate this!
+ nextListObj = reinterpret_cast<DialogLine*>(listObj->GetNextInList());
+ while( ( nextListObj != NULL )
+ && ( nextListObj->GetConversationPosition() < position ) )
+ {
+ listObj = nextListObj;
+ nextListObj = reinterpret_cast<DialogLine*>(listObj->GetNextInList());
+ }
+
+ line.AddToDialogList( listObj );
+ }
+}
+
+//=============================================================================
+// Conversation::IsComplete
+//=============================================================================
+// Description: Returns true if we have a set of consecutively-numbered
+// dialog lines, starting from 1.
+//
+// Parameters: None
+//
+// Return: true if conversation valid and complete, false otherwise
+//
+//=============================================================================
+bool Conversation::IsComplete()
+{
+ bool complete = true;
+ int nextPosition = 1;
+ DialogLine* nextDialogLine = m_dialogList;
+
+ while( nextDialogLine != NULL )
+ {
+ if( nextDialogLine->GetConversationPosition() != nextPosition )
+ {
+ complete = false;
+ break;
+ }
+
+ // Stinky downcast!
+ nextDialogLine = reinterpret_cast<DialogLine*>(nextDialogLine->GetNextInList());
+ ++nextPosition;
+ }
+
+ return( complete );
+}
+
+//=============================================================================
+// Conversation::PlayLine
+//=============================================================================
+// Description: Comment
+//
+// Parameters: lineIndex - index for the DialogLine we want to play
+// player - player to play it with
+// callback - callback to call when we're done
+//
+// Return: void
+//
+//=============================================================================
+void Conversation::PlayLine( unsigned int lineIndex,
+ SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback )
+{
+ DialogLine* currentDialog = findDialogLineByIndex( lineIndex );
+
+ currentDialog->PlayLine( 0, player, callback );
+}
+
+void Conversation::QueueLine( unsigned int lineIndex, SimpsonsSoundPlayer& player )
+{
+ m_currentLine = findDialogLineByIndex( lineIndex );
+
+ m_currentLine->QueueLine( 0, player );
+}
+
+void Conversation::PlayQueuedLine( SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback )
+{
+ m_currentLine->PlayQueuedLine( player, callback );
+}
+
+//=============================================================================
+// Conversation::GetNumDialogLines
+//=============================================================================
+// Description: Returns the number of DialogLine objects associated with
+// this conversation.
+//
+// Parameters: None
+//
+// Return: number of dialog lines
+//
+//=============================================================================
+unsigned int Conversation::GetNumDialogLines() const
+{
+ unsigned int lineCount = 0;
+ DialogLine* lineObj = m_dialogList;
+
+ while( lineObj != NULL )
+ {
+ ++lineCount;
+ lineObj = static_cast<DialogLine*>(lineObj->GetNextInList());
+ }
+
+ return( lineCount );
+}
+
+//=============================================================================
+// Conversation::UsesCharacter
+//=============================================================================
+// Description: Indicate if any of our dialog lines are from the given
+// character
+//
+// Parameters: characterObj - character to match
+//
+// Return: true if >=1 dialog line comes from the given character,
+// false otherwise
+//
+//=============================================================================
+bool Conversation::UsesCharacter( tUID characterUID )
+{
+ bool match = false;
+ DialogLine* lineObj = m_dialogList;
+
+ while( lineObj != NULL )
+ {
+ if( lineObj->UsesCharacter( characterUID ) )
+ {
+ match = true;
+ break;
+ }
+ lineObj = static_cast<DialogLine*>(lineObj->GetNextInList());
+ }
+
+ return( match );
+}
+
+//=============================================================================
+// Conversation::IsVillainLine
+//=============================================================================
+// Description: Indicate if this is a villain one-liner (answer: no)
+//
+// Parameters: None
+//
+// Return: false
+//
+//=============================================================================
+bool Conversation::IsVillainLine()
+{
+ //
+ // No conversations are villain one-liners
+ //
+ return( false );
+}
+
+//=============================================================================
+// Conversation::GetDialogLineCharacterUID
+//=============================================================================
+// Description: Indicate which character is speaking with the given line
+//
+// Parameters: lineNum - indicates which dialog line we want the UID for
+//
+// Return: tUID of character associated with dialog line
+//
+//=============================================================================
+tUID Conversation::GetDialogLineCharacterUID( unsigned int lineNum )
+{
+ unsigned int lineCount = lineNum;
+ DialogLine* lineObj = m_dialogList;
+
+ rAssert( lineNum > 0 );
+
+ while( ( --lineCount > 0 ) && ( lineObj != NULL ) )
+ {
+ lineObj = static_cast<DialogLine*>(lineObj->GetNextInList());
+ }
+
+ if( lineObj != NULL )
+ {
+ return( lineObj->GetCharacterUID() );
+ }
+ else
+ {
+ return( 0 );
+ }
+}
+
+//=============================================================================
+// Conversation::GetConversationName
+//=============================================================================
+// Description: I don't really expect this to be used, but that's no reason
+// to return garbage here. Find the name stored with one of the
+// individual lines.
+//
+// Parameters: None
+//
+// Return: radKey32 representing conversation name field
+//
+//=============================================================================
+radKey32 Conversation::GetConversationName()
+{
+ rAssert( m_dialogList != NULL );
+
+ return( m_dialogList->GetConversationName() );
+}
+
+//=============================================================================
+// Conversation::AddMatchingDialog
+//=============================================================================
+// Description: Remove self from the given list and add ourself and the new
+// dialog back in within a DialogSelectionGroup object
+//
+// Parameters: newDialog - new dialog with same characteristics as self
+// list - list to add to
+//
+// Return: void
+//
+//=============================================================================
+void Conversation::AddMatchingDialog( SelectableDialog& newDialog, SelectableDialogList& list )
+{
+ HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT );
+
+ DialogSelectionGroup* group = new DialogSelectionGroup( *this, newDialog );
+
+ rAssert( group != NULL );
+
+ list.remove( this );
+ list.push_back( group );
+
+ HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT );
+}
+
+//=============================================================================
+// Conversation::PrintDialogLineNames
+//=============================================================================
+// Description: For debugging. Print the names of the dialog lines.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void Conversation::PrintDialogLineNames()
+{
+#ifndef RAD_RELEASE
+ DialogLine* currentDialog = m_dialogList;
+
+ rDebugString( "Conversation contents:\n" );
+ while( currentDialog != NULL )
+ {
+ currentDialog->PrintResourceName();
+ currentDialog = static_cast<DialogLine*>(currentDialog->GetNextInList());
+ }
+#endif
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+DialogLine* Conversation::findDialogLineByIndex( unsigned int lineIndex )
+{
+ unsigned int lineCount = lineIndex;
+ DialogLine* currentDialog = m_dialogList;
+
+ rAssert( currentDialog != NULL );
+
+ while( lineCount > 0 )
+ {
+ currentDialog = static_cast<DialogLine*>(currentDialog->GetNextInList());
+ rAssert( currentDialog != NULL );
+ --lineCount;
+ }
+
+ return( currentDialog );
+} \ No newline at end of file
diff --git a/game/code/sound/dialog/conversation.h b/game/code/sound/dialog/conversation.h
new file mode 100644
index 0000000..29459f0
--- /dev/null
+++ b/game/code/sound/dialog/conversation.h
@@ -0,0 +1,79 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: conversation.h
+//
+// Description: Conversation objects aggregate groups of DialogLine objects
+// which are intended to be played without interruption, like
+// a conversation. Hence the name.
+//
+// History: 02/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef CONVERSATION_H
+#define CONVERSATION_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/dialog/playabledialog.h>
+
+//========================================
+// Forward References
+//========================================
+class DialogLine;
+
+//=============================================================================
+//
+// Synopsis: Conversation
+//
+//=============================================================================
+
+class Conversation : public PlayableDialog
+{
+ public:
+ Conversation( DialogLine& line );
+ virtual ~Conversation();
+
+ bool LineFits( DialogLine& line );
+ void AddToConversation( DialogLine& line );
+ bool IsComplete();
+
+ //
+ // Pure virtual functions from SelectableDialog
+ //
+ void PlayLine( unsigned int lineIndex,
+ SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback );
+ void QueueLine( unsigned int lineIndex,
+ SimpsonsSoundPlayer& player );
+ void PlayQueuedLine( SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback );
+
+ unsigned int GetNumDialogLines() const;
+ bool UsesCharacter( tUID characterUID );
+ tUID GetDialogLineCharacterUID( unsigned int lineNum );
+ radKey32 GetConversationName();
+ bool IsVillainLine();
+
+ void AddMatchingDialog( SelectableDialog& newDialog, SelectableDialogList& list );
+
+ void PrintDialogLineNames();
+
+ private:
+ //Prevent wasteful constructor creation.
+ Conversation();
+ Conversation( const Conversation& original );
+ Conversation& operator=( const Conversation& rhs );
+
+ DialogLine* findDialogLineByIndex( unsigned int lineIndex );
+
+ unsigned int m_maxOrderNumber;
+ DialogLine* m_dialogList;
+ DialogLine* m_currentLine;
+};
+
+
+#endif // CONVERSATION_H
+
diff --git a/game/code/sound/dialog/conversationmatcher.cpp b/game/code/sound/dialog/conversationmatcher.cpp
new file mode 100644
index 0000000..db49580
--- /dev/null
+++ b/game/code/sound/dialog/conversationmatcher.cpp
@@ -0,0 +1,237 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: conversationmatcher.cpp
+//
+// Description: Takes sound resource names which form individual dialog lines
+// and groups them into the intended conversations.
+//
+// History: 02/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/dialog/conversationmatcher.h>
+
+#include <sound/dialog/conversation.h>
+#include <sound/dialog/dialogline.h>
+#include <sound/dialog/dialoglist.h>
+
+#include <memory/srrmemory.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ConversationMatcher::ConversationMatcher
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ConversationMatcher::ConversationMatcher() :
+ m_conversationList( NULL )
+{
+}
+
+//==============================================================================
+// ConversationMatcher::~ConversationMatcher
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ConversationMatcher::~ConversationMatcher()
+{
+}
+
+//=============================================================================
+// ConversationMatcher::AddNewLine
+//=============================================================================
+// Description: Make a new DialogLine object for the resource and add it to
+// the list of conversations to be grouped together.
+//
+// Parameters: resource - conversation sound resource
+//
+// Return: void
+//
+//=============================================================================
+void ConversationMatcher::AddNewLine( IDaSoundResource* resource )
+{
+ DialogLine* line;
+ Conversation* conversationObj;
+ Conversation* newConversation;
+
+ //
+ // Create a DialogLine object
+ //
+#ifdef RAD_GAMECUBE
+ line = new( GMA_GC_VMM ) DialogLine( resource );
+#else
+ line = new( GMA_PERSISTENT ) DialogLine( resource );
+#endif
+ rAssert( line != NULL );
+
+ //
+ // Work through the conversation list, trying to find an existing
+ // conversation that matches this line
+ //
+ conversationObj = m_conversationList;
+
+ while( conversationObj != NULL )
+ {
+ if( conversationObj->LineFits( *line ) )
+ {
+ conversationObj->AddToConversation( *line );
+ break;
+ }
+
+ //
+ // Argh! This must go. Stinky downcast.
+ //
+ conversationObj = reinterpret_cast<Conversation*>(conversationObj->GetNextInList());
+ }
+
+ if( conversationObj == NULL )
+ {
+ //
+ // No conversation matched, create a new conversation
+ //
+#ifdef RAD_GAMECUBE
+ newConversation = new( GMA_GC_VMM ) Conversation( *line );
+#else
+ newConversation = new( GMA_PERSISTENT ) Conversation( *line );
+#endif
+ newConversation->AddToDialogList( reinterpret_cast<SelectableDialog**>(&m_conversationList) );
+ }
+}
+
+//=============================================================================
+// ConversationMatcher::AreAllConversationsComplete
+//=============================================================================
+// Description: Determine whether all the conversations have been filled out.
+// Sanity check function.
+//
+// Parameters: None
+//
+// Return: true if all conversations complete, false otherwise
+//
+//=============================================================================
+bool ConversationMatcher::AreAllConversationsComplete()
+{
+ Conversation* current;
+ bool allComplete = true;
+
+ current = m_conversationList;
+ while( current != NULL )
+ {
+ if( !(current->IsComplete()) )
+ {
+ allComplete = false;
+ current->PrintDialogLineNames();
+
+ //
+ // We know we're incomplete at this point, but keep going so that
+ // we get everything printed out in one go.
+ //
+ }
+
+ current = reinterpret_cast<Conversation*>(current->GetNextInList());
+ }
+
+ return( allComplete );
+}
+
+//=============================================================================
+// ConversationMatcher::AddConversationsToList
+//=============================================================================
+// Description: Transfer all of our conversations from our own list to the
+// list given that match the specification
+//
+// Parameters: level - level of conversations for list (NO_LEVEL if N/A)
+// mission - mission of conversations for list (NO_MISSION if N/A)
+// list - list to add to
+//
+// Return: void
+//
+//=============================================================================
+void ConversationMatcher::AddConversationsToList( unsigned int level,
+ unsigned int mission,
+ SelectableDialogList& list )
+{
+ HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT );
+
+ Conversation* conversationObj;
+ Conversation* nextConversation = NULL;
+ Conversation* temp;
+
+ //
+ // First, deal with the head of the list as a special case. This can
+ // probably be cleaned up
+ //
+ while( ( m_conversationList != NULL )
+ && ( m_conversationList->GetLevel() == level )
+ && ( m_conversationList->GetMission() == mission ) )
+ {
+ temp = m_conversationList;
+ m_conversationList = reinterpret_cast<Conversation*>( m_conversationList->GetNextInList() );
+ list.push_back( temp );
+ }
+
+ //
+ // Now, the first one in the list, if it exists, doesn't match the spec
+ //
+ conversationObj = m_conversationList;
+ if( conversationObj != NULL )
+ {
+ nextConversation = reinterpret_cast<Conversation*>( conversationObj->GetNextInList() );
+ }
+
+ while( ( conversationObj != NULL )
+ && ( nextConversation != NULL ) )
+ {
+ if( ( nextConversation->GetLevel() == level )
+ && ( nextConversation->GetMission() == mission ) )
+ {
+ //
+ // Remove conversation from our list and add it to the one supplied
+ //
+ conversationObj->RemoveNextFromList();
+ list.push_back( nextConversation );
+ }
+ else
+ {
+ conversationObj = nextConversation;
+ }
+
+ nextConversation = reinterpret_cast<Conversation*>( conversationObj->GetNextInList() );
+ }
+ HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/sound/dialog/conversationmatcher.h b/game/code/sound/dialog/conversationmatcher.h
new file mode 100644
index 0000000..18e9b15
--- /dev/null
+++ b/game/code/sound/dialog/conversationmatcher.h
@@ -0,0 +1,57 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: conversationmatcher.h
+//
+// Description: Takes sound resource names which form individual dialog lines
+// and groups them into the intended conversations.
+//
+// History: 02/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef CONVERSATIONMATCHER_H
+#define CONVERSATIONMATCHER_H
+
+//========================================
+// Nested Includes
+//========================================
+//
+// I'd like a forward declaration, but it's a little complicated with
+// STL lists
+//
+#include <sound/dialog/selectabledialoglist.h>
+
+//========================================
+// Forward References
+//========================================
+struct IDaSoundResource;
+class Conversation;
+
+//=============================================================================
+//
+// Synopsis: ConversationMatcher
+//
+//=============================================================================
+
+class ConversationMatcher
+{
+ public:
+ ConversationMatcher();
+ virtual ~ConversationMatcher();
+
+ void AddNewLine( IDaSoundResource* resource );
+ bool AreAllConversationsComplete();
+ void AddConversationsToList( unsigned int level, unsigned int mission, SelectableDialogList& list );
+
+ private:
+ //Prevent wasteful constructor creation.
+ ConversationMatcher( const ConversationMatcher& original );
+ ConversationMatcher& operator=( const ConversationMatcher& rhs );
+
+ Conversation* m_conversationList;
+};
+
+
+#endif // CONVERSATIONMATCHER_H
+
diff --git a/game/code/sound/dialog/dialogcoordinator.cpp b/game/code/sound/dialog/dialogcoordinator.cpp
new file mode 100644
index 0000000..ba6e269
--- /dev/null
+++ b/game/code/sound/dialog/dialogcoordinator.cpp
@@ -0,0 +1,971 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogcoordinator.cpp
+//
+// Description: The main interface class for the dialog system. It listens
+// for game events and cues the appropriate line of dialog.
+// Objects representing lines or sets of lines are obtained
+// from the SpeechCoordinator, and are passed to the
+// DialogPriorityQueue, which determines when playback is
+// ready to begin.
+//
+// History: 30/08/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/dialog/dialogcoordinator.h>
+
+#include <sound/dialog/dialoglist.h>
+#include <sound/dialog/dialogpriorityqueue.h>
+#include <sound/dialog/dialogline.h>
+
+#include <memory/srrmemory.h>
+#include <events/eventmanager.h>
+#include <events/eventdata.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/character/charactermanager.h>
+#include <mission/objectives/missionobjective.h>
+#include <mission/conditions/missioncondition.h>
+#include <gameflow/gameflow.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+struct TutorialConversationData
+{
+ const char* convName;
+ radKey32 convNameKey;
+ bool platformSpecificLine;
+};
+
+//
+// IMPORTANT: needs to line up with TutorialMode enumeration in tutorialmode.h.
+// Not ideal, but we're three days from alpha.
+//
+TutorialConversationData tutorialConvNames[] =
+{
+ { "camera", 0, false },
+ { "bonus", 0, false },
+ { "start", 0, true },
+ { "drive", 0, true },
+ { "traffic", 0, false },
+ { "gag", 0, false },
+ { "race", 0, false },
+ { "card", 0, false },
+ { "coin", 0, false },
+ { "reward", 0, true },
+ { "outcar", 0, true },
+ { "break", 0, true },
+ { "broken", 0, false },
+ { "interior", 0, true },
+ { "unknown", 0, false },
+ { "damaged", 0, false },
+ { "Gil", 0, false },
+ { "wrench", 0, false },
+ { "incar", 0, true },
+ { "unknown", 0, false },
+ { "timetrial", 0, false }
+};
+
+static unsigned int tutorialTableLength = sizeof( tutorialConvNames ) / sizeof( TutorialConversationData );
+
+static tUID genericTrafficUIDs[] =
+{
+ tEntity::MakeUID( "traffic1" ),
+ tEntity::MakeUID( "traffic2" ),
+ tEntity::MakeUID( "traffic3" ),
+ tEntity::MakeUID( "traffic4" )
+};
+
+static radKey32 s_failDialog1 = ::radMakeKey32( "fail1" );
+static radKey32 s_failDialog2 = ::radMakeKey32( "fail2" );
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// DialogCoordinator::DialogCoordinator
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DialogCoordinator::DialogCoordinator( IRadNameSpace* namespaceObj ) :
+ m_dialogNamespace( namespaceObj ),
+ m_dialogList( new( GMA_PERSISTENT ) DialogList() ),
+ m_playbackQueue( new( GMA_PERSISTENT ) DialogPriorityQueue() ),
+ m_dialogOn( true ),
+ m_phoneBoothRequestMade( false )
+{
+ unsigned int i;
+ char buffer[50];
+
+ rAssert( m_dialogList != NULL );
+ rAssert( m_dialogNamespace != NULL );
+ rAssert( m_playbackQueue != NULL );
+
+ //
+ // Initialize the tutorial dialog table
+ //
+ for( i = 0; i < tutorialTableLength; i++ )
+ {
+ if( tutorialConvNames[i].platformSpecificLine )
+ {
+#ifdef RAD_PS2
+ sprintf( buffer, "%sps2", tutorialConvNames[i].convName );
+#elif RAD_GAMECUBE
+ sprintf( buffer, "%sngc", tutorialConvNames[i].convName );
+#else
+ sprintf( buffer, "%sxbx", tutorialConvNames[i].convName );
+#endif
+
+ tutorialConvNames[i].convNameKey = ::radMakeKey32( buffer );
+ }
+ else
+ {
+ tutorialConvNames[i].convNameKey = ::radMakeKey32( tutorialConvNames[i].convName );
+ }
+ }
+}
+
+//==============================================================================
+// DialogCoordinator::~DialogCoordinator
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DialogCoordinator::~DialogCoordinator()
+{
+ GetEventManager()->RemoveAll( this );
+}
+
+//=============================================================================
+// DialogCoordinator::HandleEvent
+//=============================================================================
+// Description: Pass the event to the DialogList, which will find a suitable
+// SelectableDialog for us, and give it to the priority queue
+// for playback.
+//
+// Parameters: id - event ID
+// pEventData - user data, unused here
+//
+// Return: void
+//
+//=============================================================================
+void DialogCoordinator::HandleEvent( EventEnum id, void* pEventData )
+{
+ unsigned int tutorialIndex;
+ SelectableDialog* dialog;
+ DialogEventData* eventData;
+ DialogEventData raceEventData;
+ bool dialogWasPlaying;
+ AvatarManager* avatarMgr;
+ Vehicle* vehicleEventData = NULL;
+ Vehicle* minigameVehicle;
+ Vehicle* avatarVehicle;
+ tUID charUID1 = 0;
+ tUID charUID2 = 0;
+ int i;
+ int numPlayers;
+ const char* charName;
+ rmt::Vector dlgPosition;
+ rmt::Vector* dlgPositionPtr = NULL;
+ radKey32 convName = 0;
+ Character* character1 = NULL;
+ Character* character2 = NULL;
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ bool overridePositional = false;
+ int coinToss;
+
+ if( GetGameplayManager() != NULL )
+ {
+ //
+ // Filter out the crap we don't want in supersprint
+ //
+ if( GetGameplayManager()->IsSuperSprint()
+ && ( id != EVENT_BIG_VEHICLE_CRASH )
+ && ( id != EVENT_BIG_BOOM_SOUND )
+ && ( id != EVENT_DEATH_VOLUME_SOUND ) )
+ {
+ return;
+ }
+
+ //
+ // Filter out the crap we don't want in race missions
+ //
+ if( ( GetGameplayManager()->GetCurrentMission() != NULL )
+ && ( GetGameplayManager()->GetCurrentMission()->IsRaceMission() ) )
+ {
+ if( id == EVENT_MISSION_FAILURE )
+ {
+ const char* modelName;
+
+ //
+ // Hack!
+ // Turn this into a Patty/walker failure conversation.
+ // Randomly choose between fail1 and fail2.
+ //
+ rAssert( playerAvatar != NULL );
+ modelName = GetCharacterManager()->GetModelName( playerAvatar->GetCharacter() );
+ raceEventData.charUID1 = tEntity::MakeUID( modelName );
+ raceEventData.charUID2 = tEntity::MakeUID( "patty" );
+ if( rand() % 2 == 0 )
+ {
+ raceEventData.dialogName = s_failDialog1;
+ }
+ else
+ {
+ raceEventData.dialogName = s_failDialog2;
+ }
+
+ pEventData = &raceEventData;
+ id = EVENT_IN_GAMEPLAY_CONVERSATION;
+ }
+ else if( id == EVENT_MISSION_BRIEFING_ACCEPTED )
+ {
+ return;
+ }
+ }
+ }
+
+ //**********************************************************
+ //
+ // TODO: this is becoming a bit of an if-statement mess. We're getting close
+ // to alpha, so I'm not changing it now. In the sequel, pEventData should
+ // point to a parameter object which can be created in a variety of ways with
+ // a variety of data, and you should be able to ask it for a tUID or
+ // something when it gets here.
+ //
+ //**********************************************************
+
+ //
+ // Hack!!
+ //
+ if( id == EVENT_PHONE_BOOTH_RIDE_REQUEST )
+ {
+ m_phoneBoothRequestMade = true;
+ }
+ else if( ( id == EVENT_PHONE_BOOTH_NEW_VEHICLE_SELECTED )
+ || ( id == EVENT_PHONE_BOOTH_OLD_VEHICLE_RESELECTED ) )
+ {
+ if( !m_phoneBoothRequestMade )
+ {
+ //
+ // This isn't a phone booth car request, it's just a regular
+ // vehicle load. Throw the event out.
+ //
+ return;
+ }
+ else
+ {
+ m_phoneBoothRequestMade = false;
+ }
+ }
+ else if( id == EVENT_PHONE_BOOTH_CANCEL_RIDEREPLY_LINE )
+ {
+ m_phoneBoothRequestMade = false;
+ return;
+ }
+ else
+ {
+ //
+ // We expect those requests to be made back-to-back. Anything else,
+ // and we're done with the phone booth.
+ //
+ m_phoneBoothRequestMade = false;
+ }
+
+ //
+ // Filter out the reverse burnouts we don't want
+ //
+ if( id == EVENT_BURNOUT )
+ {
+ //
+ // Need to check the mBrake value, calling IsInReverse() or
+ // IsMovingBackward() filters out the small velocities we're
+ // getting at the start of the burnout
+ //
+ if( playerAvatar->IsInCar()
+ && ( playerAvatar->GetVehicle()->mBrake > 0.0f ) )
+ {
+ return;
+ }
+ }
+
+ //
+ // Turn wasp zaps into hit by car to play HitByC dialog lines
+ //
+ if( id == EVENT_WASP_BULLET_HIT_CHARACTER_STYLIZED_VIOLENCE_FOLLOWS )
+ {
+ id = EVENT_PLAYER_CAR_HIT_NPC;
+ overridePositional = true;
+ }
+
+ if( id == EVENT_DIALOG_SHUTUP )
+ {
+ m_playbackQueue->StopAllDialog();
+ }
+ else if( id == EVENT_CONVERSATION_SKIP )
+ {
+ //
+ // Stop anything that might be playing
+ //
+ dialogWasPlaying = m_playbackQueue->StopAllDialog();
+ if( dialogWasPlaying )
+ {
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_DONE );
+ }
+ }
+ else if( m_dialogOn )
+ {
+ if( id == EVENT_TUTORIAL_DIALOG_PLAY )
+ {
+ tutorialIndex = *(reinterpret_cast<unsigned int*>( pEventData ));
+ if( tutorialIndex < tutorialTableLength )
+ {
+ //
+ // It's always Bart. Specify him by UID (his Character
+ // object isn't loaded for these)
+ //
+ character1 = NULL;
+ character2 = NULL;
+ charUID1 = tEntity::MakeUID( "bart" );
+ charUID2 = tEntity::MakeUID( "homer" );
+ convName = tutorialConvNames[tutorialIndex].convNameKey;
+ }
+ else
+ {
+ //
+ // No line exists here, play nothing
+ //
+ rDebugString( "No tutorial dialog exists for event data\n" );
+ return;
+ }
+ }
+ else if( ( id == EVENT_CONVERSATION_INIT_DIALOG )
+ || ( id == EVENT_IN_GAMEPLAY_CONVERSATION ) )
+ {
+ //
+ // Get characters to match
+ //
+ eventData = static_cast<DialogEventData*>( pEventData );
+ character1 = eventData->char1;
+ character2 = eventData->char2;
+ charUID1 = eventData->charUID1;
+ charUID2 = eventData->charUID2;
+
+ //
+ // Assumption: if eventData->dialogNameis zero,
+ // that's an indication that no name is to be matched
+ //
+ convName = eventData->dialogName;
+ }
+ else if( id == EVENT_TRAFFIC_IMPEDED )
+ {
+ //
+ // Almost-final hack!
+ //
+ // Special case, of course. For this one, toss a coin between
+ // playing the avatar line and the traffic line. For the
+ // traffic, play one of the four generic vehicles.
+ //
+ if( playerAvatar->IsInCar() )
+ {
+ coinToss = rand() % 2;
+ }
+ else
+ {
+ coinToss = 1;
+ }
+
+ if( coinToss == 0 )
+ {
+ character1 = playerAvatar->GetCharacter();
+ overridePositional = true;
+ }
+ else
+ {
+ coinToss = rand() % 4;
+ charUID1 = genericTrafficUIDs[coinToss];
+ vehicleEventData = static_cast<Vehicle*>( pEventData );
+ }
+ }
+ else if( ( pEventData != NULL )
+ && ( id != EVENT_LOCATOR + LocatorEvent::BOUNCEPAD ) )
+ {
+ if( GetGameplayManager()->IsSuperSprint() )
+ {
+ minigameVehicle = static_cast<Vehicle*>(pEventData);
+
+ avatarMgr = GetAvatarManager();
+ numPlayers = GetGameplayManager()->GetNumPlayers();
+ for( i = 0; i < numPlayers; i++ )
+ {
+ playerAvatar = avatarMgr->GetAvatarForPlayer( i );
+ if( playerAvatar->GetVehicle() == minigameVehicle )
+ {
+ character1 = playerAvatar->GetCharacter();
+ break;
+ }
+ }
+
+ if( i >= numPlayers )
+ {
+ //
+ // Not the player, so use the driver name
+ //
+ charUID1 = tEntity::MakeUID( minigameVehicle->mDriverName );
+ }
+ }
+ else if( id == EVENT_WRONG_SIDE_DUMBASS )
+ {
+ //
+ // Stinky special case. pEventData is actually a Vehicle in this case, since
+ // we can't use the character, since drivers have different names
+ //
+ vehicleEventData = static_cast<Vehicle*>(pEventData);
+ dialog = m_dialogList->FindDialogForEvent( id, NULL, NULL,
+ tEntity::MakeUID( vehicleEventData->mDriverName ), 0, convName, false );
+ if( dialog == NULL )
+ {
+ rDebugPrintf( "No driver dialog found for event %d\n", static_cast<int>( id ) );
+ }
+ else
+ {
+ m_playbackQueue->AddDialogToQueue( *dialog );
+ }
+
+ return;
+ }
+ else if( id == EVENT_DING_DONG )
+ {
+ //
+ // Yet Another Special Case. Doorbells play dialog for unloaded characters.
+ // We're getting a string with the character name
+ //
+ charName = static_cast<char*>(pEventData);
+ dialog = m_dialogList->FindDialogForEvent( id, NULL, NULL, tEntity::MakeUID( charName ), 0, 0, false );
+
+ if( dialog == NULL )
+ {
+ rDebugPrintf( "No doorbell dialog found for character %s\n", charName );
+ }
+ else
+ {
+ m_playbackQueue->AddDialogToQueue( *dialog );
+ }
+
+ return;
+ }
+ else if( id == EVENT_BIG_BOOM_SOUND )
+ {
+ Vehicle* blowedUpCar = static_cast<Vehicle*>(pEventData);
+
+ //
+ // Can't handle below because we can get thrown out of the car before the
+ // explosion.
+ //
+ if( playerAvatar->IsInCar() && ( blowedUpCar != playerAvatar->GetVehicle() ) )
+ {
+ return;
+ }
+
+ character1 = playerAvatar->GetCharacter();
+ rAssert( character1 != NULL );
+
+ //
+ // Play funny driver line
+ //
+ if( blowedUpCar->GetDriver() != NULL )
+ {
+ dialog = m_dialogList->FindDialogForEvent( id, NULL, NULL,
+ tEntity::MakeUID(blowedUpCar->mDriverName), 0, 0, false );
+ if( dialog == NULL )
+ {
+ rDebugPrintf( "No driver dialog found for event %d\n", static_cast<int>( id ) );
+ }
+ else
+ {
+ m_playbackQueue->AddDialogToQueue( *dialog );
+ }
+ }
+ }
+ else if( eventHasVehicleData( id )
+ || ( id == EVENT_MISSION_FAILURE )
+ || ( id == EVENT_HIT_BREAKABLE )
+ || ( id == EVENT_DEATH_VOLUME_SOUND )
+ || ( id == EVENT_CARD_COLLECTED ) )
+ {
+ if( eventHasVehicleData( id )
+ && ( !(playerAvatar->IsInCar())
+ || ( static_cast<Vehicle*>(pEventData) != playerAvatar->GetVehicle() ) ) )
+ {
+ //
+ // We're only interested in our own vehicle
+ //
+ return;
+ }
+
+ //
+ // Another stinky special case. Event data is used in the call
+ // to queueVillainDialog below.
+ //
+ character1 = playerAvatar->GetCharacter();
+ rAssert( character1 != NULL );
+ }
+ else
+ {
+ //
+ // We expect a Character* as the event data,
+ //
+ character1 = static_cast<Character*>( pEventData );
+ }
+ }
+ else
+ {
+ //
+ // If nothing is supplied as event data, we assume that the character
+ // referred to is avatar 0
+ //
+ character1 = playerAvatar->GetCharacter();
+ rAssert( character1 != NULL );
+ }
+
+ //
+ // Check for events where we want to queue up a villain line before the walker/driver
+ //
+ queueVillainDialog( id, pEventData );
+
+ //
+ // Driver lines
+ //
+ if( ( playerAvatar != NULL )
+ && playerAvatar->IsInCar() )
+ {
+ avatarVehicle = playerAvatar->GetVehicle();
+ if( ( avatarVehicle->mpDriver != NULL ) && ( avatarVehicle->mpDriver != character1 ) )
+ {
+ tUID driverUID;
+
+ //
+ // Character is in car and not driving. Trigger driver dialog
+ // as well as character dialog below
+ //
+ driverUID = tEntity::MakeUID(avatarVehicle->mDriverName);
+
+ dialog = m_dialogList->FindDialogForEvent( id, NULL, NULL, driverUID, charUID2, convName, false );
+ if( dialog == NULL )
+ {
+ rDebugPrintf( "No driver dialog found for event %d\n", static_cast<int>( id ) );
+ }
+ else
+ {
+ m_playbackQueue->AddDialogToQueue( *dialog );
+ }
+ }
+ }
+
+ dialog = m_dialogList->FindDialogForEvent( id, character1, character2, charUID1, charUID2, convName, false );
+
+ if( dialog == NULL )
+ {
+ rDebugPrintf( "No dialog found for event %d\n", static_cast<int>( id ) );
+ }
+ else
+ {
+ if( playLinePositionally( id ) && !overridePositional )
+ {
+ if( character1 != NULL )
+ {
+ getCharacterPosition( character1, dlgPosition );
+ }
+ else
+ {
+ vehicleEventData->GetPosition( &dlgPosition );
+ }
+ dlgPositionPtr = &dlgPosition;
+ }
+ m_playbackQueue->AddDialogToQueue( *dialog, dlgPositionPtr );
+ }
+ }
+}
+
+//=============================================================================
+// DialogCoordinator::Initialize
+//=============================================================================
+// Description: Parse the namespace for dialog resources and register for
+// dialog-related events
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogCoordinator::Initialize()
+{
+ m_dialogList->OrganizeDialog( m_dialogNamespace );
+
+ registerDialogEvents();
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// DialogCoordinator::registerDialogEvents
+//=============================================================================
+// Description: Register as a listener for the dialog-related events with
+// the event manager.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogCoordinator::registerDialogEvents()
+{
+ unsigned int tableSize = DialogLine::GetEventTableSize();
+ unsigned int i;
+
+ for( i = 0; i < tableSize; i++ )
+ {
+ GetEventManager()->AddListener( this, DialogLine::GetEventTableEntry( i )->event );
+ }
+
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_SKIP );
+ GetEventManager()->AddListener( this, EVENT_TUTORIAL_DIALOG_PLAY );
+ GetEventManager()->AddListener( this, EVENT_DIALOG_SHUTUP );
+ GetEventManager()->AddListener( this, EVENT_WASP_BULLET_HIT_CHARACTER_STYLIZED_VIOLENCE_FOLLOWS );
+ GetEventManager()->AddListener( this, EVENT_PHONE_BOOTH_CANCEL_RIDEREPLY_LINE );
+}
+
+//=============================================================================
+// DialogCoordinator::queueVillainDialog
+//=============================================================================
+// Description: If this is a villain event, queue up some villain dialog
+//
+// Parameters: id - walker/driver event to be mapped to villain event
+// eventData - user data passed in with event
+//
+// Return: void
+//
+//=============================================================================
+void DialogCoordinator::queueVillainDialog( EventEnum id, void* eventData )
+{
+ EventEnum villainID = NUM_EVENTS; // Invalid placeholder value, shouldn't be used
+ Mission* currentMission;
+ MissionStage* currentStage;
+ MissionObjective* currentObjective;
+ MissionCondition* failureCondition;
+ Vehicle* eventCar;
+ Vehicle* AICar;
+ VehicleAI* AICarAI;
+ const char* villainName;
+ SelectableDialog* dialog;
+ GameplayManager* gameplayMgr;
+ rmt::Vector villainPosn;
+ rmt::Vector avatarPosn;
+ rmt::Vector diff;
+ bool noVillainLine = false;
+
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND )
+ {
+ //
+ // No villains in the FE
+ //
+ return;
+ }
+
+ gameplayMgr = GetGameplayManager();
+ if( ( gameplayMgr == NULL ) || ( gameplayMgr->IsSuperSprint() ) )
+ {
+ return;
+ }
+
+ //
+ // See if the given event ID is one that has corresponding villain dialog
+ //
+ switch( id )
+ {
+ case EVENT_TAIL_LOST_DIALOG:
+ villainID = EVENT_VILLAIN_TAIL_EVADE;
+ break;
+
+ case EVENT_MINOR_VEHICLE_CRASH:
+ case EVENT_BIG_VEHICLE_CRASH:
+ eventCar = static_cast<Vehicle*>(eventData);
+ if( ( eventCar != NULL )
+ && ( gameplayMgr->GetCurrentMission() != NULL )
+ && ( gameplayMgr->GetCurrentMission()->GetCurrentStage() != NULL )
+ && ( gameplayMgr->GetCurrentMission()->GetCurrentStage()->GetMainAIVehicleForThisStage() != NULL ) )
+ {
+ //
+ // Hack!
+ //
+ // Ideally, we should only play villain car crash dialogue if it's involved in the
+ // collision, apparently. Since we're trying to go final, just check to see if
+ // it's close by. Good enough.
+ //
+ AICar = gameplayMgr->GetCurrentMission()->GetCurrentStage()->GetMainAIVehicleForThisStage();
+ AICar->GetPosition( &villainPosn );
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( avatarPosn );
+ diff.Sub( villainPosn, avatarPosn );
+ if( diff.MagnitudeSqr() > 75.0f )
+ {
+ noVillainLine = true;
+ }
+ else
+ {
+ //
+ // Dig through the GameplayManager stuff to find out who is
+ // attacking who
+ //
+ currentMission = gameplayMgr->GetCurrentMission();
+ rAssert( currentMission != NULL );
+ currentStage = currentMission->GetCurrentStage();
+ if (currentStage != NULL)
+ {
+ currentObjective = currentStage->GetObjective();
+ rAssert( currentObjective != NULL );
+
+ if( currentObjective->GetObjectiveType() == MissionObjective::OBJ_DESTROY )
+ {
+ villainID = EVENT_BIG_CRASH;
+ }
+ else
+ {
+ villainID = EVENT_VILLAIN_CAR_HIT_PLAYER;
+ }
+ }
+ else
+ {
+ noVillainLine = true;
+ }
+ }
+ }
+ else
+ {
+ noVillainLine = true;
+ }
+ break;
+
+ case EVENT_MISSION_FAILURE:
+ failureCondition = static_cast<MissionCondition*>(eventData);
+
+ //
+ // We only play villain dialog on mission failure
+ // when the player blew a tail mission
+ //
+ if( failureCondition->GetType() == MissionCondition::COND_FOLLOW_DISTANCE )
+ {
+ villainID = EVENT_TAIL_LOST_DIALOG;
+ }
+ else if( failureCondition->IsChaseCondition() )
+ {
+ villainID = EVENT_MISSION_FAILURE;
+ }
+ else
+ {
+ //
+ // No villain involved
+ //
+ noVillainLine = true;
+ }
+ break;
+
+ case EVENT_MISSION_SUCCESS_DIALOG:
+ currentMission = gameplayMgr->GetCurrentMission();
+ rAssert( currentMission != NULL );
+ currentStage = currentMission->GetCurrentStage();
+ if( currentStage != NULL )
+ {
+ currentObjective = currentStage->GetObjective();
+ rAssert( currentObjective != NULL );
+
+ if( currentObjective->GetObjectiveType() == MissionObjective::OBJ_DESTROY )
+ {
+ villainID = EVENT_MISSION_SUCCESS_DIALOG;
+ }
+ else
+ {
+ noVillainLine = true;
+ }
+ }
+ else
+ {
+ noVillainLine = true;
+ }
+ break;
+
+ default:
+ //
+ // No villain line needed for this type of event
+ //
+ noVillainLine = true;
+ break;
+ }
+
+ if( noVillainLine )
+ {
+ return;
+ }
+
+ //
+ // Find out who the driver of the AI vehicle is and play the line
+ // if the dialog exists
+ //
+ // AICar = gameplayMgr->GetVehicleInSlot( GameplayManager::eAICar );
+
+ //Chuck: Esan use this method for getting the mainAI for a stage. It should work
+
+ AICar = gameplayMgr->GetCurrentMission()->GetCurrentStage()->GetMainAIVehicleForThisStage();
+
+ if( AICar != NULL )
+ {
+ if( ( id != EVENT_MISSION_FAILURE )
+ && ( id != EVENT_MISSION_SUCCESS_DIALOG ) )
+ {
+ // HACK:
+ // [Dusit Matthew Eakkachaichanvet: July 3rd, 2003]
+ // When a vehicle is destroyed and we ask for the AI, well,
+ // it wont' find that vehicle in the database... So.. we have some
+ // possible REAL fixes:
+ // - When a car is destroyed, I should send you an event and you should remember the new husk (or lack thereof).
+ // For this particular dialogue it doesn't matter. But y'know... maybe there are other bugs associated with this.
+ // - We check it here. I would prefer we STILL play the sound, but it's really your call.
+ if( !AICar->mVehicleDestroyed )
+ {
+ AICarAI = GetVehicleCentral()->GetVehicleAI( AICar );
+
+ // At the end of the mission, we don't seem to have AI anymore...
+ if( ( AICarAI == NULL )
+ || ( AICarAI->GetState() == VehicleAI::STATE_WAITING ) )
+ {
+ //
+ // Don't play dialogue for inactive AI vehicles
+ //
+ return;
+ }
+ }
+ }
+ villainName = AICar->GetDriverName();
+ rAssert( villainName != NULL );
+
+ //
+ // If the villain isn't there, then we're probably in a race mission or something
+ // where it isn't loaded
+ //
+ if( villainName != NULL )
+ {
+ dialog = m_dialogList->FindDialogForEvent( villainID, NULL, NULL, tEntity::MakeUID( villainName ), 0, 0, true );
+
+ if( dialog == NULL )
+ {
+ rDebugPrintf( "No dialog found for villain event %d\n", static_cast<int>( villainID ) );
+ }
+ else
+ {
+ m_playbackQueue->AddDialogToQueue( *dialog );
+ }
+ }
+ }
+}
+
+//=============================================================================
+// DialogCoordinator::playLinePositionally
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id )
+//
+// Return: bool
+//
+//=============================================================================
+bool DialogCoordinator::playLinePositionally( EventEnum id )
+{
+ return( ( id == EVENT_KICK_NPC_SOUND )
+ || ( id == EVENT_PEDESTRIAN_DODGE )
+ || ( id == EVENT_PLAYER_CAR_HIT_NPC )
+ || ( id == EVENT_TRAFFIC_IMPEDED ) );
+}
+
+//=============================================================================
+// DialogCoordinator::getCharacterPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: thePed - character to get position for.
+//
+// Return: position
+//
+//=============================================================================
+void DialogCoordinator::getCharacterPosition( Character* thePed, rmt::Vector& posn )
+{
+ rAssert( thePed != NULL );
+
+ //
+ // Get position from the character
+ //
+ thePed->GetPosition( posn );
+}
+
+//=============================================================================
+// DialogCoordinator::eventHasVehicleData
+//=============================================================================
+// Description: Indicate whether the given event has a vehicle as a data
+// parameter (stinky void*'s)
+//
+// Parameters: id - event ID to check
+//
+// Return: true if vehicle parameter expected, false otherwise
+//
+//=============================================================================
+bool DialogCoordinator::eventHasVehicleData( EventEnum id )
+{
+ bool retVal = false;
+
+ switch( id )
+ {
+ case EVENT_MINOR_VEHICLE_CRASH:
+ case EVENT_BIG_VEHICLE_CRASH:
+ case EVENT_WRONG_SIDE_DUMBASS:
+ case EVENT_TRAFFIC_IMPEDED:
+ case EVENT_BIG_BOOM_SOUND:
+ case EVENT_DEATH_VOLUME_SOUND:
+ retVal = true;
+ break;
+
+ default:
+ break;
+ }
+
+ return( retVal );
+}
diff --git a/game/code/sound/dialog/dialogcoordinator.h b/game/code/sound/dialog/dialogcoordinator.h
new file mode 100644
index 0000000..e162de4
--- /dev/null
+++ b/game/code/sound/dialog/dialogcoordinator.h
@@ -0,0 +1,84 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogcoordinator.h
+//
+// Description: The main interface class for the dialog system. It listens
+// for game events and cues the appropriate line of dialog.
+// Objects representing lines or sets of lines are obtained
+// from the SpeechCoordinator, and are passed to the
+// DialogPriorityQueue, which determines when playback is
+// ready to begin.
+//
+// History: 30/08/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef DIALOGCOORDINATOR_H
+#define DIALOGCOORDINATOR_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/dialog/dialogpriorityqueue.h>
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+
+struct IRadNameSpace;
+class DialogList;
+class Character;
+class Vehicle;
+
+//=============================================================================
+//
+// Synopsis: DialogCoordinator
+//
+//=============================================================================
+
+class DialogCoordinator : public EventListener
+{
+ public:
+ DialogCoordinator( IRadNameSpace* namespaceObj );
+ virtual ~DialogCoordinator();
+
+ void Initialize();
+ void OnGameplayEnd() { m_playbackQueue->StopAllDialog(); }
+
+ void OnPauseStart() { m_playbackQueue->PauseDialog(); }
+ void OnPauseEnd() { m_playbackQueue->UnpauseDialog(); }
+
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ void ServiceOncePerFrame() { m_playbackQueue->ServiceOncePerFrame(); }
+
+ private:
+ //Prevent wasteful constructor creation.
+ DialogCoordinator();
+ DialogCoordinator( const DialogCoordinator& original );
+ DialogCoordinator& operator=( const DialogCoordinator& rhs );
+
+ void registerDialogEvents();
+ void queueVillainDialog( EventEnum id, void* eventData );
+ bool playLinePositionally( EventEnum id );
+ void getCharacterPosition( Character* thePed, rmt::Vector& posn );
+ bool eventHasVehicleData( EventEnum id );
+
+ IRadNameSpace* m_dialogNamespace;
+
+ DialogList* m_dialogList;
+ DialogPriorityQueue* m_playbackQueue;
+
+ bool m_dialogOn;
+
+ //
+ // Hack!
+ //
+ bool m_phoneBoothRequestMade;
+};
+
+
+#endif // DIALOGCOORDINATOR_H
+
diff --git a/game/code/sound/dialog/dialogline.cpp b/game/code/sound/dialog/dialogline.cpp
new file mode 100644
index 0000000..d23c3f2
--- /dev/null
+++ b/game/code/sound/dialog/dialogline.cpp
@@ -0,0 +1,1018 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogline.cpp
+//
+// Description: Atomic unit of dialog. A DialogLine object represents a
+// complete line of dialog spoken by a single character.
+//
+// History: 02/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radkey.hpp>
+#include <radnamespace.hpp>
+#include <p3d/anim/skeleton.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/dialog/dialogline.h>
+
+#include <sound/dialog/dialogselectiongroup.h>
+
+#include <sound/simpsonssoundplayer.h>
+#include <sound/soundrenderer/idasoundresource.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+
+#include <mission/gameplaymanager.h>
+#include <worldsim/character/character.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+EventTableEntry eventTable[] =
+{
+ { "GIC", 0, EVENT_GETINTOVEHICLE_START, 2500 },
+ { "GOC", 0, EVENT_GETOUTOFVEHICLE_START, 2500 },
+ { "convinit", 0, EVENT_CONVERSATION_INIT_DIALOG, 2500 },
+ { "noboxconv", 0, EVENT_IN_GAMEPLAY_CONVERSATION, 2500 },
+ { "tutorial", 0, EVENT_TUTORIAL_DIALOG_PLAY, 2500 },
+ { "Mcrash", 0, EVENT_MINOR_VEHICLE_CRASH, 2500 },
+ { "Bcrash", 0, EVENT_BIG_VEHICLE_CRASH, 2500 },
+ { "Arrive", 0, EVENT_DESTINATION_REACHED, 2500 },
+ { "Air", 0, EVENT_BIG_AIR, 2500 },
+ { "Burn", 0, EVENT_BURNOUT, 2500 },
+ { "ObjectW", 0, EVENT_COLLECT_OBJECT, 2500 },
+ { "Break", 0, EVENT_HIT_BREAKABLE, 2500 },
+ { "Mfail", 0, EVENT_MISSION_FAILURE, 4000 },
+ { "Mvic", 0, EVENT_MISSION_SUCCESS_DIALOG, 4000 },
+ { "Tail", 0, EVENT_TAIL_LOST_DIALOG, 2500 },
+ { "NewAI", 0, EVENT_CHASE_VEHICLE_PROXIMITY, 2500 },
+ { "Time", 0, EVENT_TIME_RUNNING_OUT, 2500 },
+ { "Pass", 0, EVENT_RACE_PASSED_AI, 2500 },
+ { "Passed", 0, EVENT_RACE_GOT_PASSED_BY_AI, 2500 },
+ { "Activate", 0, EVENT_BIG_RED_SWITCH_PRESSED, 250 }, // Make this quite short
+ { "Fall", 0, EVENT_DEATH_VOLUME_SOUND, 2500 },
+ { "Char", 0, EVENT_PEDESTRIAN_SMACKDOWN, 2500 },
+ { "HitByW", 0, EVENT_KICK_NPC_SOUND, 2500 },
+ { "BreakCa", 0, EVENT_BREAK_CAMERA_OR_BOX, 2500 },
+ { "Turbo", 0, EVENT_CHARACTER_TIRED_NOW, 2500 },
+ { "Springboard", 0, static_cast<EventEnum>(EVENT_LOCATOR + LocatorEvent::BOUNCEPAD), 2500 },
+ { "NHitByC", 0, EVENT_PEDESTRIAN_DODGE, 2500 },
+ { "HitByC", 0, EVENT_PLAYER_CAR_HIT_NPC, 2500 },
+ { "HitP", 0, EVENT_PLAYER_MAKES_LIGHT_OF_CAR_HITTING_NPC, 2500 },
+ { "Damage", 0, EVENT_BIG_CRASH, 2500 },
+ { "Door", 0, EVENT_WRONG_SIDE_DUMBASS, 2500 },
+ { "CarWay", 0, EVENT_TRAFFIC_IMPEDED, 2500 },
+ { "Doorbell", 0, EVENT_DING_DONG, 2500 },
+ { "Askride", 0, EVENT_PHONE_BOOTH_RIDE_REQUEST, 0 },
+ { "Ridereply", 0, EVENT_PHONE_BOOTH_NEW_VEHICLE_SELECTED, 0 },
+ { "Answer", 0, EVENT_PHONE_BOOTH_OLD_VEHICLE_RESELECTED, 0 },
+ { "HitCar", 0, EVENT_VILLAIN_CAR_HIT_PLAYER, 2500 },
+ { "Greeting", 0, EVENT_AMBIENT_GREETING, 2500 },
+ { "Idlereply", 0, EVENT_AMBIENT_RESPONSE, 0 },
+ { "Askfood", 0, EVENT_AMBIENT_ASKFOOD, 2500 },
+ { "Foodreply", 0, EVENT_AMBIENT_FOODREPLY, 0 },
+ { "CarBuy", 0, EVENT_HAGGLING_WITH_GIL, 2500 },
+ { "Dcar", 0, EVENT_BIG_BOOM_SOUND, 5000 },
+ { "Card", 0, EVENT_CARD_COLLECTED, 2500 },
+ { "Mstart", 0, EVENT_MISSION_BRIEFING_ACCEPTED, 0 }
+};
+
+static unsigned int eventTableLength = sizeof( eventTable ) / sizeof( EventTableEntry );
+
+CharacterTableEntry characterTable[] =
+{
+ { 0, "Hom", 0, "homer" },
+ { 0, "Mrg", 0, "marge" },
+ { 0, "Brt", 0, "bart" },
+ { 0, "Lis", 0, "lisa" },
+ { 0, "Apu", 0, "apu" },
+ { 0, "Agn", 0, "askinner" },
+ { 0, "Brn", 0, "barney" },
+ { 0, "Bee", 0, "beeman" },
+ { 0, "Crl", 0, "carl" },
+ { 0, "Cbg", 0, "cbg" },
+ { 0, "Clt", 0, "cletus" },
+ { 0, "Dol", 0, "dolph" },
+ { 0, "Nic", 0, "nriviera" },
+ { 0, "Frk", 0, "frink" },
+ { 0, "Fla", 0, "ned" },
+ { 0, "By1", 0, "boy1" },
+ { 0, "By2", 0, "boy2" },
+ { 0, "Fm1", 0, "fem1" },
+ { 0, "Fm2", 0, "fem2" },
+ { 0, "Gil", 0, "gil" },
+ { 0, "Gr1", 0, "girl1" },
+ { 0, "Gr2", 0, "girl2" },
+ { 0, "Kan", 0, "kang" },
+ { 0, "Kod", 0, "kodos" },
+ { 0, "Mn1", 0, "male1" },
+ { 0, "Mn2", 0, "male2" },
+ { 0, "Grp", 0, "grandpa" },
+ { 0, "Mol", 0, "moleman" },
+ { 0, "Jas", 0, "jasper" },
+ { 0, "Jim", 0, "jimbo" },
+ { 0, "Kea", 0, "kearney" },
+ { 0, "Kru", 0, "krusty" },
+ { 0, "Len", 0, "lenny" },
+ { 0, "Loe", 0, "lou" },
+ { 0, "Lou", 0, "louie" },
+ { 0, "Mil", 0, "milhouse" },
+ { 0, "Moe", 0, "moe" },
+ { 0, "Nel", 0, "nelson" },
+ { 0, "Oto", 0, "otto" },
+ { 0, "Pat", 0, "patty" },
+ { 0, "Qim", 0, "quimby" },
+ { 0, "Ral", 0, "ralph" },
+ { 0, "Sea", 0, "captain" },
+ { 0, "Sel", 0, "selma" },
+ { 0, "Skn", 0, "skinner" },
+ { 0, "Smi", 0, "smithers" },
+ { 0, "Snk", 0, "snake" },
+ { 0, "Svt", 0, "teen" },
+ { 0, "Wig", 0, "wiggum" },
+ { 0, "Wil", 0, "willie" },
+ { 0, "Zom", 0, "zombie" },
+ { 0, "Zm1", 0, "zombie1" },
+ { 0, "Zm2", 0, "zombie2" },
+ { 0, "Zm3", 0, "zombie3" },
+ { 0, "Zm4", 0, "zombie4" },
+ { 0, "Hib", 0, "hibbert" },
+ { 0, "Bur", 0, "burns" },
+ { 0, "Rod", 0, "rod" },
+ { 0, "Todd", 0, "todd" },
+ { 0, "Bkm", 0, "brockman" },
+ { 0, "Hbn", 0, "homerbrain" },
+ { 0, "Vm1", 0, "traffic1" },
+ { 0, "Vm2", 0, "traffic2" },
+ { 0, "Vm3", 0, "traffic3" },
+ { 0, "Vm4", 0, "traffic4" }
+};
+
+static unsigned int characterTableLength = sizeof( characterTable ) / sizeof( CharacterTableEntry );
+
+//
+// The food guys
+//
+static unsigned int APU_INDEX = 4;
+static unsigned int TEEN_INDEX = 47;
+
+//
+// Arbitrary number, used in field name buffers
+//
+static const int FIELD_BUFFER_LEN = 64;
+static const int MY_FILENAME_BUFFER_LEN = 150;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// DialogLine::DialogLine
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DialogLine::DialogLine( IDaSoundResource* resource )
+{
+ m_resource = resource;
+ m_characterIndex = -1;
+ m_role = ROLE_NONE;
+ m_conversationPosition = NOT_CONVERSATION_LINE;
+ m_ConversationName = 0;
+
+ static bool tablesInitialized = false;
+
+ if( !tablesInitialized )
+ {
+ initializeTables();
+ tablesInitialized = true;
+ }
+
+ parseResourceFilename();
+}
+
+//==============================================================================
+// DialogLine::~DialogLine
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DialogLine::~DialogLine()
+{
+}
+
+//==============================================================================
+// DialogLine::PlayLine
+//==============================================================================
+// Description: Play the dialog line
+//
+// Parameters: lineIndex - ignored, only used for other SelectableDialog subclasses
+// player - what we use for the playing
+// callback - who we call when we're done
+//
+// Return: N/A.
+//
+//==============================================================================
+void DialogLine::PlayLine( unsigned int lineIndex,
+ SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback )
+{
+ player.PlayResource( m_resource, callback );
+}
+
+//=============================================================================
+// DialogLine::QueueLine
+//=============================================================================
+// Description: Buffer a line of dialog for playback
+//
+// Parameters: lineIndex - ignored, only used for other SelectableDialog subclasses
+// player - what we use for the queueing
+//
+// Return: void
+//
+//=============================================================================
+void DialogLine::QueueLine( unsigned int lineIndex, SimpsonsSoundPlayer& player )
+{
+ player.QueueSound( m_resource );
+}
+
+//=============================================================================
+// DialogLine::PlayQueuedLine
+//=============================================================================
+// Description: Play the sound we queued with QueueLine
+//
+// Parameters: player - what we use for the playing
+// callback - if non-NULL, object to notify when playback done
+//
+// Return: void
+//
+//=============================================================================
+void DialogLine::PlayQueuedLine( SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback )
+{
+ player.PlayQueuedSound( callback );
+}
+
+//=============================================================================
+// DialogLine::StripDirectoryCrud
+//=============================================================================
+// Description: Utility function for stripping directory prefix from filenames
+//
+// Parameters: filename - filename to strip
+// buffer - holds stripped filename
+// bufferLen - length of buffer
+//
+// Return: void
+//
+//=============================================================================
+void DialogLine::StripDirectoryCrud( const char* filename, char* buffer, int bufferLen )
+{
+ const char* charPtr;
+ int i, j;
+ int strippedLength = 0;
+
+ i = strlen( filename );
+ charPtr = &(filename[i]);
+ while( i > 0 )
+ {
+ if( ( *charPtr == '/' ) || ( *charPtr == '\\' ) )
+ {
+ break;
+ }
+ else
+ {
+ ++strippedLength;
+ --i;
+ --charPtr;
+ }
+ }
+
+ //
+ // Copy stripped filename to buffer
+ //
+ if( i > 0 )
+ {
+ ++charPtr;
+ }
+
+ if( strippedLength > bufferLen )
+ {
+ strippedLength = bufferLen;
+ }
+
+ for( j = 0; j < strippedLength; j++ )
+ {
+ buffer[j] = *charPtr++;
+ }
+}
+
+//=============================================================================
+// DialogLine::GetEventTableEntry
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index )
+//
+// Return: EventTableEntry
+//
+//=============================================================================
+const EventTableEntry* DialogLine::GetEventTableEntry( unsigned int index )
+{
+ if( index >= eventTableLength )
+ {
+ return( NULL );
+ }
+ else
+ {
+ return( &(eventTable[index]) );
+ }
+}
+
+//=============================================================================
+// DialogLine::GetEventTableSize
+//=============================================================================
+// Description: Return the number of entries in the event table
+//
+// Parameters: None
+//
+// Return: table size
+//
+//=============================================================================
+unsigned int DialogLine::GetEventTableSize()
+{
+ return( eventTableLength );
+}
+
+//=============================================================================
+// DialogLine::GetCharacterTableEntry
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int index )
+//
+// Return: CharacterTableEntry
+//
+//=============================================================================
+const CharacterTableEntry* DialogLine::GetCharacterTableEntry( unsigned int index )
+{
+ if( index >= characterTableLength )
+ {
+ return( NULL );
+ }
+ else
+ {
+ return( &(characterTable[index]) );
+ }
+}
+
+//=============================================================================
+// DialogLine::GetCharacterTableSize
+//=============================================================================
+// Description: Return the number of entries in the character table
+//
+// Parameters: None
+//
+// Return: table size
+//
+//=============================================================================
+unsigned int DialogLine::GetCharacterTableSize()
+{
+ return( characterTableLength );
+}
+
+//=============================================================================
+// DialogLine::UsesCharacter
+//=============================================================================
+// Description: Indicate whether this dialog line comes from
+// the character given
+//
+// Parameters: characterObj - character to match
+//
+// Return: true if character matches this line, false otherwise
+//
+//=============================================================================
+bool DialogLine::UsesCharacter( tUID characterUID )
+{
+ return( characterUID == GetCharacterUID( ) );
+}
+
+//=============================================================================
+// DialogLine::AddMatchingDialog
+//=============================================================================
+// Description: Request to a SelectableDialog object (this one) to add a new
+// DialogLine or such with the same characteristics. Since this
+// object represents a single line, we need to create a
+// DialogSelectionGroup and add self and the new dialog line to it,
+// and put it in our spot in the list.
+//
+// Parameters: newDialog - dialogLine that matches this one
+//
+// Return: void
+//
+//=============================================================================
+void DialogLine::AddMatchingDialog( SelectableDialog& newDialog, SelectableDialogList& list )
+{
+ HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT );
+
+ DialogSelectionGroup* group = new DialogSelectionGroup( *this, newDialog );
+
+ rAssert( group != NULL );
+
+ list.remove( this );
+ list.push_back( group );
+
+ HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT );
+}
+
+//=============================================================================
+// DialogLine::GetConversationName
+//=============================================================================
+// Description: Returns the name of the conversation this line belongs to,
+// if this is a conversation line. We'll calculate it on the
+// fly, no sense in wasting more space than we already are.
+//
+// Parameters: None
+//
+// Return: radKey32 representation of conversation name field, or 0
+// if this isn't a conversation line
+//
+//=============================================================================
+
+void DialogLine::parseConversationName( )
+{
+ bool fieldFound;
+ char fieldBuffer[FIELD_BUFFER_LEN];
+ char filenameBuffer[MY_FILENAME_BUFFER_LEN];
+ char tempBuffer[MY_FILENAME_BUFFER_LEN];
+
+ //
+ // Strip the directory crud
+ //
+ m_resource->GetFileNameAt( 0, tempBuffer, MY_FILENAME_BUFFER_LEN );
+ StripDirectoryCrud( tempBuffer, filenameBuffer, MY_FILENAME_BUFFER_LEN );
+
+ fieldFound = getNameField( filenameBuffer, 0, fieldBuffer, FIELD_BUFFER_LEN );
+ rAssert( fieldFound );
+
+ if( ( fieldBuffer[0] == 'C') && ( fieldBuffer[1] == '\0' ) )
+ {
+ //
+ // Conversation line
+ //
+ fieldFound = getNameField( filenameBuffer, 1, fieldBuffer, FIELD_BUFFER_LEN );
+ m_ConversationName = radMakeKey32( fieldBuffer );
+ }
+ else
+ {
+ //
+ // Not conversation line
+ //
+ rTuneAssert( false );
+ }
+}
+
+//=============================================================================
+// DialogLine::IsFoodCharacter
+//=============================================================================
+// Description: Determine whether this character gets Askfood lines or
+// Idlereply
+//
+// Parameters: theGuy - ambient character being talked to
+//
+// Return: true for Askfood, false for Idlereply
+//
+//=============================================================================
+bool DialogLine::IsFoodCharacter( Character* theGuy )
+{
+ tUID UID1;
+ bool retVal = false;
+
+ choreo::Puppet* puppet = theGuy->GetPuppet();
+ if( puppet != NULL )
+ {
+ UID1 = puppet->GetPose()->GetSkeleton()->GetUID();
+
+ //
+ // Hard-coded hack
+ //
+ rAssert( strcmp( "Apu", characterTable[APU_INDEX].characterString ) == 0 );
+ rAssert( strcmp( "Svt", characterTable[TEEN_INDEX].characterString ) == 0 );
+
+ if( ( UID1 == static_cast< tUID >( characterTable[APU_INDEX].realCharacterUID ) )
+ || ( UID1 == static_cast< tUID >( characterTable[TEEN_INDEX].realCharacterUID ) ) )
+ {
+ retVal = true;
+ }
+ }
+
+ return( retVal );
+}
+
+//=============================================================================
+// DialogLine::GetLifeInMsecsForEvent
+//=============================================================================
+// Description: Given an event, return the number of milliseconds that the
+// dialog for that event should be allowed to live in the queue
+//
+// Parameters: eventID - ID for event
+//
+// Return: lifetime in msecs, 0 for infinite lifetime
+//
+//=============================================================================
+unsigned int DialogLine::GetLifeInMsecsForEvent( EventEnum eventID )
+{
+ unsigned int i;
+ unsigned int lifetime = 2500; // 2.5 sec. default
+
+ for( i = 0; i < eventTableLength; i++ )
+ {
+ if( eventTable[i].event == eventID )
+ {
+ lifetime = eventTable[i].lifeInMsecs;
+ break;
+ }
+ }
+
+ return( lifetime );
+}
+
+//=============================================================================
+// DialogLine::FillCharacterName
+//=============================================================================
+// Description: Fill the given buffer with the text name of the character
+// matching the given UID
+//
+// Parameters: buffer - buffer to write name to
+// bufferSize - length of buffer
+// characterUID - UID of character to find name for
+//
+// Return: void
+//
+//=============================================================================
+void DialogLine::FillCharacterName( char* buffer, unsigned int bufferSize, tUID characterUID )
+{
+ unsigned int i;
+ unsigned int tableSize;
+ const CharacterTableEntry* charTableLine;
+
+ tableSize = GetCharacterTableSize();
+
+ for( i = 0; i < tableSize; i++ )
+ {
+ charTableLine = GetCharacterTableEntry( i );
+ if( charTableLine->realCharacterUID == characterUID )
+ {
+ break;
+ }
+ }
+
+ if( i < tableSize )
+ {
+ rAssert( strlen( charTableLine->characterString ) < bufferSize );
+ strcpy( buffer, charTableLine->characterString );
+ }
+ else
+ {
+ rAssert( bufferSize >= 4 );
+ strcpy( buffer, "???" );
+ }
+}
+
+//=============================================================================
+// DialogLine::FillEventName
+//=============================================================================
+// Description: Fill the given buffer with the text name of the event
+// matching the given ID
+//
+// Parameters: buffer - buffer to write name to
+// bufferSize - length of buffer
+// eventID - ID of event to find name for
+//
+// Return: void
+//
+//=============================================================================
+void DialogLine::FillEventName( char* buffer, unsigned int bufferSize, EventEnum eventID )
+{
+ unsigned int i;
+ unsigned int tableSize;
+ const EventTableEntry* eventTableLine;
+
+ tableSize = GetEventTableSize();
+
+ for( i = 0; i < tableSize; i++ )
+ {
+ eventTableLine = GetEventTableEntry( i );
+ if( eventTableLine->event == eventID )
+ {
+ break;
+ }
+ }
+
+ if( i < tableSize )
+ {
+ rAssert( strlen( eventTableLine->eventString ) < bufferSize );
+ strcpy( buffer, eventTableLine->eventString );
+ }
+ else
+ {
+ rAssert( bufferSize >= 4 );
+ strcpy( buffer, "???" );
+ }
+}
+
+//=============================================================================
+// DialogLine::PrintResourceName
+//=============================================================================
+// Description: Dump out the name of the sound resource. For debugging.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogLine::PrintResourceName()
+{
+#ifndef RAD_RELEASE
+ char buffer[256];
+
+ rAssert( m_resource != NULL );
+
+ m_resource->GetFileNameAt( 0, buffer, 256 );
+ rDebugPrintf( "DialogLine - %s\n", buffer );
+#endif
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// DialogLine::parseResourceFilename
+//=============================================================================
+// Description: Look at the resource filename and fill out the dialog line
+// attributes based on what we find
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogLine::parseResourceFilename()
+{
+ bool fieldFound;
+ char fieldBuffer[FIELD_BUFFER_LEN];
+ char filenameBuffer[MY_FILENAME_BUFFER_LEN];
+ char tempBuffer[MY_FILENAME_BUFFER_LEN];
+
+ //
+ // Strip the directory crud
+ //
+ m_resource->GetFileNameAt( 0, tempBuffer, MY_FILENAME_BUFFER_LEN );
+ StripDirectoryCrud( tempBuffer, filenameBuffer, MY_FILENAME_BUFFER_LEN );
+
+ fieldFound = getNameField( filenameBuffer, 0, fieldBuffer, FIELD_BUFFER_LEN );
+ rAssert( fieldFound );
+
+ if( ( fieldBuffer[0] == 'C') && ( fieldBuffer[1] == '\0' ) )
+ {
+ //
+ // Conversation line
+ //
+ parseConversationName( );
+ matchOrderField( filenameBuffer, 2 );
+ matchEventField( filenameBuffer, 3 );
+ matchCharacterField( filenameBuffer, 4 );
+ matchLevelField( filenameBuffer, 5 );
+ }
+ else
+ {
+ //
+ // One-liner
+ //
+ matchRoleField( filenameBuffer, 0 );
+ matchEventField( filenameBuffer, 1 );
+ matchCharacterField( filenameBuffer, 2 );
+ matchLevelField( filenameBuffer, 3 );
+ }
+}
+
+void DialogLine::matchRoleField( const char* filename, int field )
+{
+ char buffer[FIELD_BUFFER_LEN];
+ bool fieldFound;
+
+ fieldFound = getNameField( filename, field, buffer, FIELD_BUFFER_LEN );
+ rAssert( fieldFound );
+
+ switch( buffer[0] )
+ {
+ case 'W':
+ m_role = ROLE_WALKER;
+ break;
+ case 'D':
+ m_role = ROLE_DRIVER;
+ break;
+ case 'P':
+ m_role = ROLE_PEDESTRIAN;
+ break;
+ case 'V':
+ m_role = ROLE_VILLAIN;
+ break;
+ default:
+ rAssertMsg( false, "Unknown role field in dialog file\n" );
+ break;
+ }
+}
+
+//=============================================================================
+// DialogLine::matchEventField
+//=============================================================================
+// Description: Parse the field at the given position and store the eventEnum
+// that matches it
+//
+// Parameters: field - number of field to parse
+//
+// Return: void
+//
+//=============================================================================
+void DialogLine::matchEventField( const char* filename, int field )
+{
+ unsigned int i;
+ char buffer[FIELD_BUFFER_LEN];
+ radKey32 fieldKey;
+ bool fieldFound;
+
+ //
+ // Event name. Use a lookup table to translate to the event
+ // enumeration.
+ //
+ fieldFound = getNameField( filename, field, buffer, FIELD_BUFFER_LEN );
+ rAssert( fieldFound );
+
+ fieldKey = ::radMakeCaseInsensitiveKey32( buffer );
+ for( i = 0; i < eventTableLength; i++ )
+ {
+ if( fieldKey == eventTable[i].eventKey )
+ {
+ m_event = eventTable[i].event;
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// DialogLine::matchOrderField
+//=============================================================================
+// Description: Parse the field at the given position and store it as the value
+// for the conversation position
+//
+// Parameters: field - number of field to parse
+//
+// Return: void
+//
+//=============================================================================
+void DialogLine::matchOrderField( const char* filename, int field )
+{
+ char buffer[FIELD_BUFFER_LEN];
+ bool fieldFound;
+
+ //
+ // Event name. Use a lookup table to translate to the event
+ // enumeration.
+ //
+ fieldFound = getNameField( filename, field, buffer, FIELD_BUFFER_LEN );
+ rAssert( fieldFound );
+
+ rAssert( buffer[0] >= '0' );
+ rAssert( buffer[0] <= '9' );
+
+ m_conversationPosition = buffer[0] - '0';
+}
+
+//=============================================================================
+// DialogLine::matchCharacterField
+//=============================================================================
+// Description: Parse the field at the given position and store the characterEnum
+// that matches it
+//
+// Parameters: field - number of field to parse
+//
+// Return: void
+//
+//=============================================================================
+void DialogLine::matchCharacterField( const char* filename, int field )
+{
+ unsigned int i;
+ char buffer[FIELD_BUFFER_LEN];
+ radKey32 fieldKey;
+ bool fieldFound;
+
+ //
+ // Character. Use another lookup table.
+ //
+ fieldFound = getNameField( filename, field, buffer, FIELD_BUFFER_LEN );
+ rAssert( fieldFound );
+
+ fieldKey = ::radMakeCaseInsensitiveKey32( buffer );
+ for( i = 0; i < characterTableLength; i++ )
+ {
+ if( fieldKey == characterTable[i].characterKey )
+ {
+ m_characterIndex = i;
+ break;
+ }
+ }
+
+ rAssert( ( i < characterTableLength )
+ || ( filename[0] != 'C' ) );
+}
+
+//=============================================================================
+// DialogLine::matchLevelField
+//=============================================================================
+// Description: Parse the field at the given position and store the level
+// and mission numbers that match it
+//
+// Parameters: field - number of field to parse
+//
+// Return: void
+//
+//=============================================================================
+void DialogLine::matchLevelField( const char* filename, int field )
+{
+ char buffer[FIELD_BUFFER_LEN];
+ bool fieldFound;
+
+ //
+ // Level/Mission.
+ //
+ fieldFound = getNameField( filename, field, buffer, FIELD_BUFFER_LEN );
+
+ if( fieldFound )
+ {
+ if( ( buffer[0] == 'L' )
+ && ( buffer[1] >= '0' )
+ && ( buffer[1] <= '9' ) )
+ {
+ //
+ // Level number, start to string is "Lx"
+ //
+ m_levelNum = buffer[1] - '0';
+ rAssert( m_levelNum > 0 );
+ rAssert( m_levelNum < GameplayManager::MAX_LEVELS );
+
+ if( strlen( buffer ) >= 4 )
+ {
+ //
+ // Level and mission, expected string is "LxMy"
+ //
+ rAssert( ( buffer[2] == 'M' ) || ( buffer[2] == 'B' ) || ( buffer[2] == 'R' ) || ( buffer[2] == 'T' ) );
+ m_missionNum = buffer[3] - '0';
+ rAssert( m_missionNum >= 1 );
+ rAssert( m_missionNum <= 7 );
+
+ //
+ // Bonus == 8
+ // Races == 9-11
+ // Tutorial == 12
+ //
+ if( buffer[2] == 'B' )
+ {
+ rAssert( m_missionNum == 1 );
+ m_missionNum = BONUS_MISSION_NUMBER;
+ }
+ else if( buffer[2] == 'R' )
+ {
+ rAssert( m_missionNum >= 1 );
+ rAssert( m_missionNum <= 3 );
+ m_missionNum += FIRST_RACE_MISSION_NUMBER - 1;
+ }
+ else if( buffer[2] == 'T' )
+ {
+ rAssert( m_missionNum == 1 );
+ m_missionNum = TUTORIAL_MISSION_NUMBER;
+ }
+ }
+ else
+ {
+ m_missionNum = NO_MISSION;
+ }
+ }
+ }
+}
+
+//=============================================================================
+// DialogLine::getNameField
+//=============================================================================
+// Description: Parse the filename for a particular field, and return it in
+// the given buffer
+//
+// Parameters: filename -- name of file to parse
+// field -- number of field to find (counting from 0)
+// buffer -- buffer to copy field into
+// bufferLen -- length of buffer
+//
+// Return: true if requested field exists, false otherwise
+//
+//=============================================================================
+bool DialogLine::getNameField( const char* filename, int field, char* buffer, int bufferLen )
+{
+ const char* currentChar;
+ int i;
+ int fieldCount = field;
+ bool fieldFound = false;
+
+ currentChar = filename;
+ while( ( fieldCount > 0 ) && ( *currentChar != '\0' ) && ( *currentChar != '.' ) )
+ {
+ if( *currentChar == '_' )
+ {
+ --fieldCount;
+ }
+
+ ++currentChar;
+ }
+
+ if( ( *currentChar != '\0' ) && ( *currentChar != '.' ) )
+ {
+ //
+ // Field found, copy to buffer. Since GC has no string functions (argh),
+ // use hand-rolled strncpy
+ //
+ for( i = 0;
+ ( (i < bufferLen-1) && (currentChar[i] != '\0') && (currentChar[i] != '_') && (currentChar[i] != '.') );
+ i++ )
+ {
+ buffer[i] = currentChar[i];
+ }
+ buffer[i] = '\0';
+
+ fieldFound = true;
+ }
+
+ return( fieldFound );
+}
+
+//=============================================================================
+// DialogLine::initializeTables
+//=============================================================================
+// Description: Fill out the radKey32 entries in the event table.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogLine::initializeTables()
+{
+ unsigned int i;
+
+ for( i = 0; i < eventTableLength; i++ )
+ {
+ eventTable[i].eventKey = ::radMakeCaseInsensitiveKey32( eventTable[i].eventString );
+ }
+
+ for( i = 0; i < characterTableLength; i++ )
+ {
+ characterTable[i].characterKey = ::radMakeCaseInsensitiveKey32( characterTable[i].characterString );
+ characterTable[i].realCharacterUID = tEntity::MakeUID( characterTable[i].realCharacterName );
+ }
+}
+
+tUID DialogLine::GetCharacterUID( void )
+{
+ if ( -1 == m_characterIndex )
+ {
+ return 0;
+ }
+
+ tUID uid = characterTable[ m_characterIndex ].realCharacterUID;
+ return uid;
+}
+
+tUID DialogLine::GetDialogLineCharacterUID( unsigned int lineNum )
+{
+ return GetCharacterUID( );
+}
+ \ No newline at end of file
diff --git a/game/code/sound/dialog/dialogline.h b/game/code/sound/dialog/dialogline.h
new file mode 100644
index 0000000..1d72cdb
--- /dev/null
+++ b/game/code/sound/dialog/dialogline.h
@@ -0,0 +1,172 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogline.h
+//
+// Description: Atomic unit of dialog. A DialogLine object represents a
+// complete line of dialog spoken by a single character.
+//
+// History: 02/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef DIALOGLINE_H
+#define DIALOGLINE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/p3dtypes.hpp>
+
+#include <sound/dialog/playabledialog.h>
+#include <sound/dialog/selectabledialoglist.h>
+
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundrenderer/soundresource.h>
+//========================================
+// Forward References
+//========================================
+
+class Character;
+
+//
+// Table entry structure for mapping between event strings in filenames
+// and events responded to in dialog system
+//
+struct EventTableEntry
+{
+ const char* eventString;
+ radKey32 eventKey;
+ EventEnum event;
+ unsigned int lifeInMsecs;
+};
+
+//
+// Table entry structure for mapping between character strings in filenames
+// and tUIDs that we pull out of the Character objects.
+//
+struct CharacterTableEntry
+{
+ radInt64 realCharacterUID;
+ const char* characterString;
+ radKey32 characterKey;
+ const char* realCharacterName;
+};
+
+const char ROLE_NONE = 0;
+const char ROLE_WALKER = 1;
+const char ROLE_DRIVER = 2;
+const char ROLE_PEDESTRIAN = 3;
+const char ROLE_VILLAIN = 4;
+
+typedef char DialogRole;
+
+//=============================================================================
+//
+// Synopsis: DialogLine
+//
+//=============================================================================
+
+class DialogLine : public PlayableDialog
+{
+ public:
+ DialogLine( IDaSoundResource* resource );
+ virtual ~DialogLine();
+
+ static const int NOT_CONVERSATION_LINE = -1;
+
+ static const int BONUS_MISSION_NUMBER = 8;
+ static const int FIRST_RACE_MISSION_NUMBER = 9;
+ static const int TUTORIAL_MISSION_NUMBER = 12;
+
+ int GetConversationPosition() { return( m_conversationPosition ); }
+ tUID GetCharacterUID();
+ tUID GetDialogLineCharacterUID( unsigned int lineNum );
+ bool IsVillainLine() { return( m_role == ROLE_VILLAIN ); }
+
+ inline radKey32 GetConversationName( );
+
+ //
+ // Pure virtual functions from SelectableDialog
+ //
+ void PlayLine( unsigned int lineIndex,
+ SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback );
+ void QueueLine( unsigned int lineIndex,
+ SimpsonsSoundPlayer& player );
+ void PlayQueuedLine( SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback );
+
+ unsigned int GetNumDialogLines() const { return( 1 ); }
+ bool UsesCharacter( tUID characterUID );
+ void AddMatchingDialog( SelectableDialog& newDialog, SelectableDialogList& list );
+
+ static bool IsFoodCharacter( Character* theGuy );
+ static unsigned int GetLifeInMsecsForEvent( EventEnum eventID );
+
+ //
+ // Utility for stripping the directory crud off of filenames
+ //
+ static void StripDirectoryCrud( const char* filename, char* buffer, int bufferLen );
+
+ //
+ // Accessors for tables
+ //
+ static const EventTableEntry* GetEventTableEntry( unsigned int index );
+ static unsigned int GetEventTableSize();
+
+ static const CharacterTableEntry* GetCharacterTableEntry( unsigned int index );
+ static unsigned int GetCharacterTableSize();
+
+ static void FillEventName( char* buffer, unsigned int bufferSize, EventEnum eventID );
+ static void FillCharacterName( char* buffer, unsigned int bufferSize, tUID characterUID );
+
+ //
+ // For debugging
+ //
+ void PrintResourceName();
+
+ private:
+ //Prevent wasteful constructor creation.
+ DialogLine();
+ DialogLine( const DialogLine& original );
+ DialogLine& operator=( const DialogLine& rhs );
+
+ void parseResourceFilename();
+ bool getNameField( const char* filename, int field, char* buffer, int bufferLen );
+ void initializeTables();
+
+ void matchRoleField( const char* filename, int field );
+ void matchOrderField( const char* filename, int field );
+ void matchEventField( const char* filename, int field );
+ void matchCharacterField( const char* filename, int field );
+ void matchLevelField( const char* filename, int field );
+ void parseConversationName( );
+ //
+ // Sound resource to play
+ //
+ IDaSoundResource* m_resource;
+
+ radKey32 m_ConversationName;
+
+ DialogRole /* (char)*/ m_role;
+
+ //
+ // Position within conversation if this is a conversation line
+ //
+
+ char m_conversationPosition;
+
+ //
+ // Character that this line belongs to
+ //
+ char m_characterIndex;
+};
+
+inline radKey32 DialogLine::GetConversationName( )
+{
+ return m_ConversationName;
+}
+
+#endif // DIALOGLINE_H
+
diff --git a/game/code/sound/dialog/dialoglist.cpp b/game/code/sound/dialog/dialoglist.cpp
new file mode 100644
index 0000000..0dcab5e
--- /dev/null
+++ b/game/code/sound/dialog/dialoglist.cpp
@@ -0,0 +1,1259 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialoglist.cpp
+//
+// Description: Loads and maintains the list of dialog lines and conversations
+// (which group multiple dialog lines, and potentially link
+// conversations to other conversations that occur later).
+//
+// History: 01/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radnamespace.hpp>
+#include <raddebugwatch.hpp>
+
+#include <p3d/anim/skeleton.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/dialog/dialoglist.h>
+
+#include <sound/dialog/dialogline.h>
+#include <sound/dialog/conversationmatcher.h>
+#include <sound/soundrenderer/idasoundresource.h>
+
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <render/Enums/RenderEnums.h>
+#include <gameflow/gameflow.h>
+#include <worldsim/character/charactermanager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Arbitrary buffer length for filenames
+//
+static const int FILENAME_BUFFER_LEN = 100;
+
+//
+// Stinky hack support
+//
+radKey32 DialogList::s_introKey = 0;
+radKey32 DialogList::s_aztecKey = 0;
+tUID DialogList::s_milhouseKey = 0;
+tUID DialogList::s_nelsonKey = 0;
+tUID DialogList::s_raceZombie1 = 0;
+tUID DialogList::s_raceZombie2 = 0;
+
+enum pedDialogType
+{
+ PED_MALE1,
+ PED_MALE2,
+ PED_FEMALE1,
+ PED_FEMALE2,
+ PED_BOY1,
+ PED_BOY2,
+ PED_GIRL1,
+ PED_GIRL2,
+ PED_ZOMBIE1,
+ PED_ZOMBIE2,
+ PED_ZOMBIE3,
+ PED_ZOMBIE4,
+
+ PED_NUM_TYPES
+};
+
+enum skinDialogType
+{
+ SKIN_APU,
+ SKIN_BART,
+ SKIN_HOMER,
+ SKIN_LISA,
+ SKIN_MARGE,
+ SKIN_BARNEY,
+ SKIN_MILHOUSE,
+ SKIN_NELSON,
+ SKIN_RALPH,
+ SKIN_CLETUS,
+ SKIN_ZOMBIE1,
+ SKIN_ZOMBIE2,
+ SKIN_ZOMBIE3,
+ SKIN_ZOMBIE4,
+ SKIN_OTTO,
+ SKIN_WILLIE,
+ SKIN_KEARNEY,
+ SKIN_SKINNER,
+ SKIN_GRANDPA,
+ SKIN_CBG,
+ SKIN_FRINK,
+ SKIN_SNAKE,
+ SKIN_SMITHERS,
+
+ SKIN_NUM_TYPES
+};
+
+struct pedTypeInfo
+{
+ radInt64 pedUID;
+ const char* pedName;
+ pedDialogType dialogGroup;
+};
+
+static pedTypeInfo pedestrianNameTable[] =
+{
+ { 0, "boy", PED_BOY1 },
+ { 0, "boy2", PED_BOY2 },
+ { 0, "boy3", PED_BOY1 },
+ { 0, "bum", PED_MALE1 },
+ { 0, "busm1", PED_MALE2 },
+ { 0, "busm2", PED_MALE1 },
+ { 0, "busw1", PED_FEMALE1 },
+ { 0, "const1", PED_MALE2 },
+ { 0, "const2", PED_MALE1 },
+ { 0, "farmr1", PED_MALE2 },
+ { 0, "fem1", PED_FEMALE2 },
+ { 0, "fem2", PED_FEMALE1 },
+ { 0, "fem3", PED_FEMALE2 },
+ { 0, "fem4", PED_FEMALE1 },
+ { 0, "girl1", PED_GIRL1 },
+ { 0, "girl2", PED_GIRL2 },
+ { 0, "hooker", PED_FEMALE2 },
+ { 0, "joger1", PED_FEMALE1 },
+ { 0, "joger2", PED_MALE1 },
+ { 0, "male1", PED_MALE2 },
+ { 0, "male2", PED_MALE1 },
+ { 0, "male3", PED_MALE2 },
+ { 0, "male4", PED_MALE1 },
+ { 0, "male5", PED_MALE2 },
+ { 0, "male6", PED_MALE1 },
+ { 0, "mobstr", PED_MALE2 },
+ { 0, "nuclear", PED_MALE1 },
+ { 0, "olady1", PED_FEMALE2 },
+ { 0, "olady2", PED_FEMALE1 },
+ { 0, "olady3", PED_FEMALE2 },
+ { 0, "rednk1", PED_BOY2 },
+ { 0, "rednk2", PED_BOY1 },
+ { 0, "sail1", PED_MALE2 },
+ { 0, "sail2", PED_MALE1 },
+ { 0, "sail3", PED_MALE2 },
+ { 0, "sail4", PED_MALE1 },
+ { 0, "witch", PED_GIRL1 },
+ { 0, "frankenstein", PED_BOY2 },
+ { 0, "zfem1", PED_ZOMBIE3 },
+ { 0, "zfem5", PED_ZOMBIE3 },
+ { 0, "zmale1", PED_ZOMBIE1 },
+ { 0, "zmale3", PED_ZOMBIE2 },
+ { 0, "zmale4", PED_ZOMBIE4 }
+};
+
+static unsigned int pedestrianTableLength = sizeof( pedestrianNameTable ) / sizeof( pedTypeInfo );
+
+struct skinTypeInfo
+{
+ radInt64 skinUID;
+ const char* skinName;
+ skinDialogType dialogGroup;
+};
+
+static skinTypeInfo skinNameTable[] =
+{
+ { 0, "a_american", SKIN_APU },
+ { 0, "a_army", SKIN_APU },
+ { 0, "a_besharp", SKIN_APU },
+ { 0, "b_football", SKIN_BART },
+ { 0, "b_hugo", SKIN_BART },
+ { 0, "b_man", SKIN_BART },
+ { 0, "b_military", SKIN_BART },
+ { 0, "b_ninja", SKIN_BART },
+ { 0, "b_tall", SKIN_BART },
+ { 0, "h_donut", SKIN_HOMER },
+ { 0, "h_evil", SKIN_HOMER },
+ { 0, "h_fat", SKIN_HOMER },
+ { 0, "h_scuzzy", SKIN_HOMER },
+ { 0, "h_stcrobe", SKIN_HOMER },
+ { 0, "h_undrwr", SKIN_HOMER },
+ { 0, "reward_homer", SKIN_HOMER },
+ { 0, "l_cool", SKIN_LISA },
+ { 0, "l_florida", SKIN_LISA },
+ { 0, "l_jersey", SKIN_LISA },
+ { 0, "m_pink", SKIN_MARGE },
+ { 0, "m_police", SKIN_MARGE },
+ { 0, "m_prison", SKIN_MARGE },
+ { 0, "brn_unf", SKIN_BARNEY },
+ { 0, "reward_barney", SKIN_BARNEY },
+ { 0, "b_milhouse", SKIN_MILHOUSE },
+ { 0, "b_nelson", SKIN_NELSON },
+ { 0, "b_ralph", SKIN_RALPH },
+ { 0, "b_cletus", SKIN_CLETUS },
+ { 0, "b_zmale1", SKIN_ZOMBIE1 },
+ { 0, "b_zmale3", SKIN_ZOMBIE2 },
+ { 0, "b_zfem5", SKIN_ZOMBIE3 },
+ { 0, "b_zmale4", SKIN_ZOMBIE4 },
+ { 0, "b_zfem1", SKIN_ZOMBIE3 },
+ { 0, "b_skinner", SKIN_SKINNER },
+ { 0, "b_grandpa", SKIN_GRANDPA },
+ { 0, "b_cbg", SKIN_CBG },
+ { 0, "b_barney", SKIN_BARNEY },
+ { 0, "b_frink", SKIN_FRINK },
+ { 0, "b_snake", SKIN_SNAKE },
+ { 0, "b_smithers", SKIN_SMITHERS },
+ { 0, "reward_otto", SKIN_OTTO },
+ { 0, "reward_willie", SKIN_WILLIE },
+ { 0, "reward_kearney", SKIN_KEARNEY }
+};
+
+static unsigned int skinTableLength = sizeof( skinNameTable ) / sizeof( skinTypeInfo );
+
+struct pedDialogGroupInfo
+{
+ radInt64 pedUID;
+ const char* pedName;
+};
+
+//
+// Size must be PED_NUM_TYPES
+//
+static pedDialogGroupInfo dialogGroupTable[] =
+{
+ { 0, "male1" },
+ { 0, "male2" },
+ { 0, "fem1" },
+ { 0, "fem2" },
+ { 0, "boy1" },
+ { 0, "boy2" },
+ { 0, "girl1" },
+ { 0, "girl2" },
+ { 0, "zombie1" },
+ { 0, "zombie2" },
+ { 0, "zombie3" },
+ { 0, "zombie4" }
+};
+
+struct skinDialogGroupInfo
+{
+ radInt64 charUID;
+ const char* charName;
+};
+
+//
+// Size must be SKIN_NUM_TYPES
+//
+static skinDialogGroupInfo skinDialogGroupTable[] =
+{
+ { 0, "apu" },
+ { 0, "bart" },
+ { 0, "homer" },
+ { 0, "lisa" },
+ { 0, "marge" },
+ { 0, "barney" },
+ { 0, "milhouse" },
+ { 0, "nelson" },
+ { 0, "ralph" },
+ { 0, "cletus" },
+ { 0, "zombie1" },
+ { 0, "zombie2" },
+ { 0, "zombie3" },
+ { 0, "zombie4" },
+ { 0, "otto" },
+ { 0, "willie" },
+ { 0, "kearney" },
+ { 0, "skinner" },
+ { 0, "grandpa" },
+ { 0, "cbg" },
+ { 0, "frink" },
+ { 0, "snake" },
+ { 0, "smithers" }
+};
+
+//
+// Debug flag
+//
+bool DialogList::s_showDialogSpew = false;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// DialogList::DialogList
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DialogList::DialogList()
+{
+ unsigned int i;
+
+ //
+ // Lazy initialization
+ //
+ if( s_introKey == 0 )
+ {
+ s_introKey = ::radMakeKey32( "intro" );
+ s_aztecKey = ::radMakeKey32( "aztec" );
+ s_milhouseKey = tEntity::MakeUID( "milhouse" );
+ s_nelsonKey = tEntity::MakeUID( "nelson" );
+ s_raceZombie1 = tEntity::MakeUID( "zmale3" );
+ s_raceZombie2 = tEntity::MakeUID( "zfem1" );
+
+ //
+ // Also do the tables of UIDs we use to identify peds and skins
+ //
+ for( i = 0; i < pedestrianTableLength; i++ )
+ {
+ pedestrianNameTable[i].pedUID = tEntity::MakeUID( pedestrianNameTable[i].pedName );
+ }
+
+ for( i = 0; i < PED_NUM_TYPES; i++ )
+ {
+ dialogGroupTable[i].pedUID = tEntity::MakeUID( dialogGroupTable[i].pedName );
+ }
+
+ for( i = 0; i < skinTableLength; i++ )
+ {
+ skinNameTable[i].skinUID = tEntity::MakeUID( skinNameTable[i].skinName );
+ }
+
+ for( i = 0; i < SKIN_NUM_TYPES; i++ )
+ {
+ skinDialogGroupTable[i].charUID = tEntity::MakeUID( skinDialogGroupTable[i].charName );
+ }
+
+ //
+ // Debug spew
+ //
+ radDbgWatchAddBoolean( &s_showDialogSpew, "Show Dialog Spew", "Sound Info" );
+ radDbgWatchAddFunction( "Print Dialog Coverage", &dumpDialogCoverage, this, "Sound Info" );
+ }
+}
+
+//==============================================================================
+// DialogList::~DialogList
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DialogList::~DialogList()
+{
+}
+
+//=============================================================================
+// DialogList::OrganizeDialog
+//=============================================================================
+// Description: Go through the list of dialog resources, creating SelectableDialog
+// objects organized as directed by the naming conventions.
+//
+// Parameters: namespaceObj -- object containing the list of dialog resources
+// as read from the script file
+//
+// Return: void
+//
+//=============================================================================
+void DialogList::OrganizeDialog( IRadNameSpace* namespaceObj )
+{
+ int mission;
+ int level;
+ IDaSoundResource* resource;
+ DialogLine* newLine;
+ ConversationMatcher matcher;
+ SelectableDialogList* dialogList;
+ SelectableDialog* foundDialog;
+
+ //
+ // Go through the list of sound resources looking for dialog
+ //
+ resource = reinterpret_cast< IDaSoundResource* >( namespaceObj->GetFirst( NULL) );
+ while( resource != NULL )
+ {
+ if( isIndividualLine( resource ) )
+ {
+ //
+ // Resource is dialog but not conversation. Create a DialogLine object
+ // to hold the information about it and store it in the appropriate list.
+ //
+#ifdef RAD_GAMECUBE
+ newLine = new( GMA_GC_VMM ) DialogLine( resource );
+#else
+ newLine = new( GMA_PERSISTENT ) DialogLine( resource );
+#endif
+ if( newLine->IsLevelSpecific() )
+ {
+ dialogList = &(m_missionLists[newLine->GetLevel() - 1][newLine->GetMission()]);
+ }
+ else
+ {
+ dialogList = &m_genericDialogList;
+ }
+
+ //
+ // Search the list. If we've already got a dialog for the same situation, lump
+ // this one in with it, otherwise stash it straight into the list
+ //
+ foundDialog = searchDialogList( newLine->GetEvent(), newLine->GetCharacterUID(), 0,
+ *dialogList, 0, newLine->IsVillainLine(), false );
+ HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT );
+ if( foundDialog != NULL )
+ {
+ foundDialog->AddMatchingDialog( *newLine, *dialogList );
+ }
+ else
+ {
+ dialogList->push_back( newLine );
+ }
+ HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT );
+ }
+ else if( isConversationLine( resource ) )
+ {
+ //
+ // Resource is part of a conversation. Give it to the object
+ // responsible for matching the pieces together
+ //
+ matcher.AddNewLine( resource );
+ }
+ //
+ // Otherwise, this isn't dialog, so we don't have to do anything with it
+ //
+
+ //
+ // Next resource in the list
+ //
+ resource = reinterpret_cast< IDaSoundResource* >( namespaceObj->GetNext( NULL) );
+ }
+
+ //
+ // Do a sanity check on the conversations
+ //
+ rAssert( matcher.AreAllConversationsComplete() );
+
+ //
+ // Add the completed conversations to the appropriate lists
+ //
+ for( level = 0; level < GameplayManager::MAX_LEVELS; level++ )
+ {
+ for( mission = 0; mission < GameplayManager::MAX_MISSIONS; mission++ )
+ {
+ matcher.AddConversationsToList( level + 1, mission, m_missionLists[level][mission] );
+ }
+ }
+
+ matcher.AddConversationsToList( SelectableDialog::NO_LEVEL, SelectableDialog::NO_MISSION, m_genericDialogList );
+}
+
+//=============================================================================
+// DialogList::FindDialogForEvent
+//=============================================================================
+// Description: Search through the dialog lists to find something appropriate
+// for the given dialog event
+//
+// Parameters: id - Event ID that we need to find dialog for
+// character1 - character who will say the dialog
+// character2 - if conversation, second character in conversation.
+// NULL otherwise.
+// charUID1 - if we're searching by UID, this is non-zero and
+// character1 and character2 are NULL.
+// charUID2 - second character, used for searching by UID
+// convKey - name of conversation, 0 if not applicable
+//
+// Return: Const pointer to SelectableDialog object best matching the
+// event, or NULL if nothing found
+//
+//=============================================================================
+SelectableDialog* DialogList::FindDialogForEvent( EventEnum id,
+ Character* character1,
+ Character* character2,
+ tUID charUID1,
+ tUID charUID2,
+ radKey32 convKey,
+ bool isVillain )
+{
+ int mission;
+ tUID char1UID;
+ tUID char2UID;
+ char nameBuffer[20];
+ unsigned int aztecNumber;
+ GameplayManager* gameplayMgr = NULL;
+ // For indexing purposes, levels count from zero.
+ int level;
+ SelectableDialog* dialogMatch = NULL;
+ Mission* missionObj;
+
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND )
+ {
+ //
+ // Playing dialog in front end, so any level will do
+ //
+ level = 0;
+ missionObj = NULL;
+ }
+ else
+ {
+ gameplayMgr = GetGameplayManager();
+ rAssert( gameplayMgr != NULL );
+
+ level = gameplayMgr->GetCurrentLevelIndex() - RenderEnums::L1;
+ missionObj = gameplayMgr->GetCurrentMission();
+ }
+
+ //
+ // First, search the list for the current mission. If it's a conversation
+ // event, those only happen during missions even if we're in Sunday Drive
+ // (they're the conversations that start the missions) so just start looking there.
+ //
+ if( missionObj != NULL )
+ {
+ if( ( !(gameplayMgr->IsSuperSprint()) )
+ && ( (!(missionObj->IsSundayDrive()) )
+ || ( id == EVENT_CONVERSATION_INIT_DIALOG )
+ || ( id == EVENT_TUTORIAL_DIALOG_PLAY ) ) )
+ {
+ if( id == EVENT_TUTORIAL_DIALOG_PLAY )
+ {
+ mission = DialogLine::TUTORIAL_MISSION_NUMBER;
+ }
+ else if( missionObj->IsBonusMission() )
+ {
+ mission = DialogLine::BONUS_MISSION_NUMBER;
+ }
+ else if( missionObj->IsRaceMission() )
+ {
+ mission = DialogLine::FIRST_RACE_MISSION_NUMBER +
+ ( gameplayMgr->GetCurrentMissionNum() - GameplayManager::MAX_MISSIONS );
+ }
+ else
+ {
+ if( convKey == s_introKey )
+ {
+ //
+ // Stinky race missions. The "intro" conversation happens before we've
+ // started the race. I can't rename them to simple L1, since characters like Homer
+ // have multiple C_intro_*_L1.rsd lines, so there's a naming clash. Ugh.
+ // To make matters worse, since some of these conversations involve only Homer (or
+ // whoever the driver is), we need to check if either character is Milhouse, Nelson,
+ // Ralph, or their zombie counterparts
+ //
+ rAssert( character1 != NULL );
+ rAssert( character2 != NULL );
+
+ char1UID = getPuppetUID( character1 );
+ char2UID = getPuppetUID( character2 );
+ if( ( char1UID == s_milhouseKey )
+ || ( char2UID == s_milhouseKey )
+ || ( char1UID == s_raceZombie1 )
+ || ( char2UID == s_raceZombie1 ) )
+ {
+ mission = DialogLine::FIRST_RACE_MISSION_NUMBER;
+ }
+ else if( ( char1UID == s_nelsonKey )
+ || ( char2UID == s_nelsonKey )
+ || ( char1UID == s_raceZombie2 )
+ || ( char2UID == s_raceZombie2 ) )
+ {
+ mission = DialogLine::FIRST_RACE_MISSION_NUMBER + 1;
+ }
+ else
+ {
+ //
+ // This had better be Ralph or zombie Ralph
+ //
+ mission = DialogLine::FIRST_RACE_MISSION_NUMBER + 2;
+ }
+ }
+ else if( level == 0 )
+ {
+ //
+ // Stinky level 1, tutorial mission screws everything up
+ //
+ mission = gameplayMgr->GetCurrentMissionIndex();
+ }
+ else
+ {
+ mission = gameplayMgr->GetCurrentMissionIndex() + 1;
+
+#ifdef RAD_E3
+ //
+ // E3 hack. L2M5 is our only mission, and it's going to
+ // come back as mission 1. Hack it to 5.
+ //
+ mission = 5;
+#endif
+ }
+
+ if ( convKey == s_aztecKey )
+ {
+ //
+ // Another stinky hack. The teen at the Aztec needs randomized conversations.
+ // Conversations don't really randomize because the conversation builder assumes
+ // that identically-named conversations result from misnamed files. And the
+ // key isn't a straightforward randomization on the caller's end, since it's
+ // a scripted value. I'll handle it here.
+ //
+ aztecNumber = ( rand() % 4 ) + 1;
+ sprintf( nameBuffer, "aztec%d", aztecNumber );
+ convKey = ::radMakeKey32( nameBuffer );
+ }
+ }
+
+ if( s_showDialogSpew )
+ {
+ rTuneString( "Searching mission-specific dialog\n" );
+ }
+
+ if( character1 == NULL )
+ {
+ // Already have UIDs
+ dialogMatch = searchDialogList( id, charUID1, charUID2, m_missionLists[level][mission], convKey, isVillain );
+ }
+ else
+ {
+ // Take UID from character objects
+ dialogMatch = searchDialogList( id, character1, character2, m_missionLists[level][mission], convKey, isVillain );
+ }
+ }
+ }
+
+ if( dialogMatch == NULL )
+ {
+ //
+ // No mission-specific dialog, search the level-specific stuff
+ //
+ if( s_showDialogSpew )
+ {
+ rTuneString( "Searching level-specific dialog\n" );
+ }
+
+ if( character1 == NULL )
+ {
+ dialogMatch = searchDialogList( id, charUID1, charUID2, m_missionLists[level][0], convKey, isVillain );
+ }
+ else
+ {
+ dialogMatch = searchDialogList( id, character1, character2, m_missionLists[level][0], convKey, isVillain );
+ }
+
+ if( dialogMatch == NULL )
+ {
+ //
+ // No mission- or level-specific dialog, search the generic list
+ //
+ if( s_showDialogSpew )
+ {
+ rTuneString( "Searching generic dialog\n" );
+ }
+
+ if( character1 == NULL )
+ {
+ dialogMatch = searchDialogList( id, charUID1, charUID2, m_genericDialogList, convKey, isVillain );
+ }
+ else
+ {
+ dialogMatch = searchDialogList( id, character1, character2, m_genericDialogList, convKey, isVillain );
+ }
+ }
+ }
+
+ return( dialogMatch );
+}
+
+//=============================================================================
+// DialogList::GetStinkySkinPointer
+//=============================================================================
+// Description: Given a UID, see if we can dig up a character for it looking
+// through all the possible skins.
+//
+// Parameters: charUID - tUID of character
+//
+// Return: Character* if match found, NULL otherwise
+//
+//=============================================================================
+Character* DialogList::GetStinkySkinPointer( tUID charUID )
+{
+ int skinType;
+ unsigned int i;
+ Character* charPtr;
+
+ for( skinType = 0; skinType < SKIN_NUM_TYPES; skinType++ )
+ {
+ if( skinDialogGroupTable[skinType].charUID == charUID )
+ {
+ break;
+ }
+ }
+
+ if( skinType == SKIN_NUM_TYPES )
+ {
+ //
+ // No skin exists for given character
+ //
+ return( NULL );
+ }
+
+ //
+ // At this point, the character has skins. Look for a match.
+ //
+ for( i = 0; i < skinTableLength; i++ )
+ {
+ if( skinNameTable[i].dialogGroup == skinType )
+ {
+ charPtr = GetCharacterManager()->GetCharacterByName( skinNameTable[i].skinUID );
+ if( charPtr != NULL )
+ {
+ return( charPtr );
+ }
+ }
+ }
+
+ //
+ // No skins found
+ //
+ return( NULL );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// DialogList::hasOneLinerPrefix
+//=============================================================================
+// Description: Determines if resource name belongs to one-liner dialog. This
+// is deemed to be true if it starts with a valid role abbreviation.
+//
+// Parameters: name - name of resource
+//
+// Return: true if role field found, false otherwise
+//
+//=============================================================================
+bool DialogList::hasOneLinerPrefix( const char* name )
+{
+ return( ( name[1] == '_' )
+ && ( ( name[0] == 'W' )
+ || ( name[0] == 'D' )
+ || ( name[0] == 'P' )
+ || ( name[0] == 'V' ) ) );
+}
+
+//=============================================================================
+// DialogList::isIndividualLine
+//=============================================================================
+// Description: Test for whether given resource is a dialog one-liner
+//
+// Parameters: resource - sound resource to test
+//
+// Return: true if one-liner, false otherwise
+//
+//=============================================================================
+bool DialogList::isIndividualLine( IDaSoundResource* resource )
+{
+ char tempBuffer[FILENAME_BUFFER_LEN];
+ char buffer[FILENAME_BUFFER_LEN];
+
+ //
+ // Get the first filename belonging to the resource. Don't bother checking
+ // for >1 file---if they exist, then the names had better be interchangable.
+ //
+ tempBuffer[0] = '\0';
+ resource->GetFileNameAt( 0, tempBuffer, FILENAME_BUFFER_LEN );
+ rAssert( strlen( tempBuffer ) > 0 );
+
+ DialogLine::StripDirectoryCrud( tempBuffer, buffer, FILENAME_BUFFER_LEN );
+
+ //
+ // Simple test: we'll call it a line if it has at least two underscores
+ // and no "C_" prefix
+ //
+ return( ( !hasConversationPrefix( buffer ) )
+ && ( hasOneLinerPrefix( buffer ) )
+ && ( underscoreCount( buffer ) > 1 ) );
+}
+
+//=============================================================================
+// DialogList::isConversationLine
+//=============================================================================
+// Description: Test for whether given resource is part of a dialog
+// conversation
+//
+// Parameters: resource - sound resource to test
+//
+// Return: true if conversation line, false otherwise
+//
+//=============================================================================
+bool DialogList::isConversationLine( IDaSoundResource* resource )
+{
+ char tempBuffer[FILENAME_BUFFER_LEN];
+ char buffer[FILENAME_BUFFER_LEN];
+
+ //
+ // Get the first filename belonging to the resource. Don't bother checking
+ // for >1 file---if they exist, then the names had better be interchangable.
+ //
+ tempBuffer[0] = '\0';
+ resource->GetFileNameAt( 0, tempBuffer, FILENAME_BUFFER_LEN );
+ rAssert( strlen( tempBuffer ) > 0 );
+
+ DialogLine::StripDirectoryCrud( tempBuffer, buffer, FILENAME_BUFFER_LEN );
+
+ //
+ // Test: line belongs to conversation if it has at least three underscores
+ // and a "C_" prefix
+ //
+ return( hasConversationPrefix( buffer ) &&
+ ( underscoreCount( buffer ) > 3 ) );
+}
+
+//=============================================================================
+// DialogList::underscoreCount
+//=============================================================================
+// Description: Return number of underscores in the given string
+//
+// Parameters: name - string to count in
+//
+// Return: number of underscores found
+//
+//=============================================================================
+unsigned int DialogList::underscoreCount( const char* name )
+{
+ unsigned int i = 0;
+ unsigned int count = 0;
+
+
+ while( name[i] != '\0' )
+ {
+ if( name[i] == '_' )
+ {
+ ++count;
+ }
+
+ i++;
+ }
+
+ return( count );
+}
+
+//=============================================================================
+// DialogList::searchDialogList
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, tUID characterUID1, tUID characterUID2, SelectableDialog* list )
+//
+// Return: SelectableDialog
+//
+//=============================================================================
+SelectableDialog* DialogList::searchDialogList( EventEnum id, Character* character1,
+ Character* character2, SelectableDialogList& list,
+ radKey32 convName, bool isVillain )
+{
+ tUID UID1 = 0;
+ tUID UID2 = 0;
+
+ if( character1 == NULL )
+ {
+ UID1 = 0;
+ }
+ else
+ {
+ //
+ // Can't just get the character UID, since they're not guaranteed to be consistent
+ // with the model you see on the screen. Need the skeleton UID, it appears
+ //
+ UID1 = getPuppetUID( character1 );
+ }
+
+ if( character2 == NULL )
+ {
+ UID2 = 0;
+ }
+ else
+ {
+ UID2 = getPuppetUID( character2 );
+ }
+
+ return( searchDialogList( id, UID1, UID2, list, convName, isVillain, true ) );
+}
+
+//=============================================================================
+// DialogList::searchDialogList
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, tUID driverUID, SelectableDialogList& list )
+//
+// Return: SelectableDialog
+//
+//=============================================================================
+SelectableDialog* DialogList::searchDialogList( EventEnum id, tUID characterUID1,
+ tUID characterUID2,
+ SelectableDialogList& list,
+ radKey32 convName,
+ bool isVillain )
+{
+ return( searchDialogList( id, characterUID1, characterUID2, list, convName, isVillain, true ) );
+}
+
+//=============================================================================
+// DialogList::searchDialogList
+//=============================================================================
+// Description: Search the given list for a SelectableDialog object with the
+// given event ID.
+//
+// Parameters: id - event ID to find a match for
+// list - list to search
+//
+// Return: pointer to SelectableDialog object matching the event ID if
+// it exists, NULL otherwise
+//
+//=============================================================================
+SelectableDialog* DialogList::searchDialogList( EventEnum id, tUID characterUID1,
+ tUID characterUID2, SelectableDialogList& list,
+ radKey32 convName, bool isVillain, bool fuzzyPedMatch )
+{
+ char eventName[30];
+ char char1Name[30];
+ char char2Name[30];
+ char convBuffer[30];
+ char villain[3];
+ SelectableDialog* currentDialog;
+ SelectableDialogList::const_iterator iter = list.begin();
+ SelectableDialog* returnValue = NULL;
+
+ if( s_showDialogSpew )
+ {
+ //
+ // Print a message for the stuff we're trying to match with
+ //
+ DialogLine::FillEventName( eventName, 30, id );
+ DialogLine::FillCharacterName( char1Name, 30, characterUID1 );
+ DialogLine::FillCharacterName( char2Name, 30, characterUID2 );
+ if( convName != 0 )
+ {
+ sprintf( convBuffer, ", conv %d", convName );
+ }
+ else
+ {
+ convBuffer[0] = '\0';
+ }
+ if( isVillain )
+ {
+ villain[0] = 'V';
+ }
+ else
+ {
+ villain[0] = 'W';
+ }
+ villain[1] = '\0';
+ rTunePrintf( "Dialog: Looking for event %s, char1 %s, char2 %s %s %s\n",
+ eventName, char1Name, char2Name, villain, convBuffer );
+ }
+
+ for( ; iter != list.end(); ++iter )
+ {
+ currentDialog = *iter;
+
+ if( s_showDialogSpew )
+ {
+ //
+ // Print a message for the stuff we're currently looking at
+ //
+ DialogLine::FillEventName( eventName, 30, currentDialog->GetEvent() );
+ DialogLine::FillCharacterName( char1Name, 30, currentDialog->GetDialogLineCharacterUID( 1 ) );
+
+ if( currentDialog->GetNumDialogLines() > 1 )
+ {
+ DialogLine::FillCharacterName( char2Name, 30, currentDialog->GetDialogLineCharacterUID( 2 ) );
+ }
+ else
+ {
+ char2Name[0] = '-';
+ char2Name[1] = '\0';
+ }
+
+ if( convName != 0 )
+ {
+ sprintf( convBuffer, ", conv %d", currentDialog->GetConversationName() );
+ }
+ else
+ {
+ convBuffer[0] = '\0';
+ }
+
+ if( currentDialog->IsVillainLine() )
+ {
+ villain[0] = 'V';
+ }
+ else
+ {
+ villain[0] = 'W';
+ }
+ villain[1] = '\0';
+
+ rTunePrintf( "Dialog: Matching against event %s, char1 %s, char2 %s %s %s\n",
+ eventName, char1Name, char2Name, villain, convBuffer );
+ }
+
+ if( ( currentDialog->GetEvent() == id )
+ && ( currentDialog->IsVillainLine() == isVillain )
+ // If a conversation name is supplied, that has to match
+ && ( ( convName == 0 )
+ || ( currentDialog->GetConversationName() == convName ) ) )
+ {
+ if( currentDialog->GetNumDialogLines() == 1 )
+ {
+ //
+ // Match either character
+ //
+ if( characterMatches( characterUID1, currentDialog, fuzzyPedMatch )
+ || characterMatches( characterUID2, currentDialog, fuzzyPedMatch ) )
+ {
+ returnValue = currentDialog;
+ }
+ }
+ else
+ {
+ //
+ // Multi-line dialog. Match both.
+ //
+ if( characterMatches( characterUID1, currentDialog, fuzzyPedMatch )
+ && characterMatches( characterUID2, currentDialog, fuzzyPedMatch ) )
+ {
+ returnValue = currentDialog;
+ }
+ }
+
+ if( returnValue != NULL )
+ {
+ if( s_showDialogSpew )
+ {
+ rTunePrintf( "Dialog: Match found\n" );
+ }
+
+ //
+ // We're done
+ //
+ break;
+ }
+ }
+ }
+
+ return( returnValue );
+}
+
+//=============================================================================
+// DialogList::characterMatches
+//=============================================================================
+// Description: Determine whether the given dialog matches the character
+// given. UID of zero always matches.
+//
+// Parameters: characterObj - character to match
+// dialog - dialog to match to
+// fuzzyPedMatch - true if we want to fudge UIDs to group
+// pedestrians, false otherwise
+//
+// Return: true if match, false otherwise
+//
+//=============================================================================
+bool DialogList::characterMatches( tUID characterUID, SelectableDialog* dialog,
+ bool fuzzyPedMatch )
+{
+ unsigned int i;
+ tUID effectiveUID; // Unix humour. Nyuck!
+ pedDialogType dialogType;
+ bool switchMade = false;
+
+ if( characterUID == static_cast< tUID >( 0 ) )
+ {
+ return( false );
+ }
+
+ //
+ // Argh!! We have a whole bunch of pedestrian UIDs which need to be mapped
+ // to eight dialog characters. If this actually shows in a profiler, we'll
+ // need to mark the peds in the Character objects when they're spawned somehow
+ // to avoid this search
+ //
+
+ //
+ // Double argh!! Now we've got a bunch of character skins that are breaking
+ // the dialog system. We have to search for those as well.
+ //
+ effectiveUID = characterUID;
+
+ if( fuzzyPedMatch )
+ {
+ for( i = 0; i < pedestrianTableLength; i++ )
+ {
+ if( effectiveUID == static_cast< tUID >( pedestrianNameTable[i].pedUID ) )
+ {
+ //
+ // Is ped, map new UID
+ //
+
+ //
+ // Another hack: zombie1/2 and zombie3/4 should be randomly chosen.
+ // TODO: leave zombie3 as zombie3 for E3.
+ //
+ dialogType = pedestrianNameTable[i].dialogGroup;
+ if( ( dialogType == PED_ZOMBIE1 )
+ && ( dialog->GetEvent() != EVENT_CONVERSATION_INIT_DIALOG )
+ && ( dialog->GetEvent() != EVENT_IN_GAMEPLAY_CONVERSATION ) )
+ {
+ if( ( rand() % 2 ) == 0 )
+ {
+ dialogType = PED_ZOMBIE2;
+ }
+ }
+
+ effectiveUID = dialogGroupTable[dialogType].pedUID;
+ switchMade = true;
+ break;
+ }
+ }
+
+ if( !switchMade )
+ {
+ //
+ // Not a ped, check for skins
+ //
+ for( i = 0; i < skinTableLength; i++ )
+ {
+ if( effectiveUID == static_cast< tUID >( skinNameTable[i].skinUID ) )
+ {
+ //
+ // Is skin, map new UID
+ //
+ effectiveUID = skinDialogGroupTable[skinNameTable[i].dialogGroup].charUID;
+ break;
+ }
+ }
+ }
+ }
+
+ return( dialog->UsesCharacter( effectiveUID ) );
+}
+
+//=============================================================================
+// DialogList::getPuppetUID
+//=============================================================================
+// Description: Get UID for Choreo puppet for character
+//
+// Parameters: ( Character* characterPtr )
+//
+// Return: tUID if skeleton found, 0 otherwise
+//
+//=============================================================================
+tUID DialogList::getPuppetUID( Character* characterPtr )
+{
+ const char* modelName;
+ rAssert( characterPtr != NULL );
+
+ modelName = GetCharacterManager()->GetModelName( characterPtr );
+ if( modelName != NULL )
+ {
+ return( tEntity::MakeUID( modelName ) );
+ }
+ else
+ {
+ return( 0 );
+ }
+}
+
+void DialogList::dumpDialogCoverage( void* userData )
+{
+#ifndef RAD_RELEASE
+ SelectableDialogList::const_iterator iter;
+ int i, j;
+ SelectableDialog* currentDialog;
+ char eventName[30];
+ char char1Name[30];
+ char char2Name[30];
+ char convBuffer[30];
+ DialogList* listObj = static_cast<DialogList*>(userData);
+
+ for( i = 0; i < GameplayManager::MAX_LEVELS; i++ )
+ {
+ for( j = 0; j < GameplayManager::MAX_MISSIONS+1; j++ )
+ {
+ rTunePrintf( "\nDialogue for level %d mission %d list\n", i, j );
+
+ iter = listObj->m_missionLists[i][j].begin();
+ for( ; iter != listObj->m_missionLists[i][j].end(); ++iter )
+ {
+ currentDialog = *iter;
+ if( currentDialog != NULL )
+ {
+ DialogLine::FillEventName( eventName, 30, currentDialog->GetEvent() );
+ DialogLine::FillCharacterName( char1Name, 30, currentDialog->GetDialogLineCharacterUID( 1 ) );
+
+ if( currentDialog->GetNumDialogLines() > 1 )
+ {
+ DialogLine::FillCharacterName( char2Name, 30, currentDialog->GetDialogLineCharacterUID( 2 ) );
+ }
+ else
+ {
+ char2Name[0] = '-';
+ char2Name[1] = '\0';
+ }
+
+ sprintf( convBuffer, ", conv %d", currentDialog->GetConversationName() );
+
+ rTunePrintf( "Dialog: Event %s, char1 %s, char2 %s%s : ",
+ eventName, char1Name, char2Name, convBuffer );
+ currentDialog->PrintPlayedStatus();
+ }
+ }
+ }
+ }
+
+ rTuneString( "\nGeneric dialogue list:\n" );
+
+ iter = listObj->m_genericDialogList.begin();
+ for( ; iter != listObj->m_genericDialogList.end(); ++iter )
+ {
+ currentDialog = *iter;
+ if( currentDialog != NULL )
+ {
+ DialogLine::FillEventName( eventName, 30, currentDialog->GetEvent() );
+ DialogLine::FillCharacterName( char1Name, 30, currentDialog->GetDialogLineCharacterUID( 1 ) );
+
+ if( currentDialog->GetNumDialogLines() > 1 )
+ {
+ DialogLine::FillCharacterName( char2Name, 30, currentDialog->GetDialogLineCharacterUID( 2 ) );
+ }
+ else
+ {
+ char2Name[0] = '-';
+ char2Name[1] = '\0';
+ }
+
+ sprintf( convBuffer, ", conv %d", currentDialog->GetConversationName() );
+
+ rTunePrintf( "Dialog: Event %s, char1 %s, char2 %s%s : ",
+ eventName, char1Name, char2Name, convBuffer );
+ currentDialog->PrintPlayedStatus();
+ }
+ }
+
+#endif
+} \ No newline at end of file
diff --git a/game/code/sound/dialog/dialoglist.h b/game/code/sound/dialog/dialoglist.h
new file mode 100644
index 0000000..54692b0
--- /dev/null
+++ b/game/code/sound/dialog/dialoglist.h
@@ -0,0 +1,109 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialoglist.h
+//
+// Description: Loads and maintains the list of dialog lines and conversations
+// (which group multiple dialog lines, and potentially link
+// conversations to other conversations that occur later).
+//
+// History: 01/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef DIALOGLIST_H
+#define DIALOGLIST_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <mission/gameplaymanager.h>
+#include <events/eventenum.h>
+#include <sound/dialog/selectabledialoglist.h>
+
+//========================================
+// Forward References
+//========================================
+struct IRadNameSpace;
+struct IDaSoundResource;
+class SelectableDialog;
+class Character;
+
+//=============================================================================
+//
+// Synopsis: DialogList
+//
+//=============================================================================
+
+class DialogList
+{
+ public:
+ DialogList();
+ virtual ~DialogList();
+
+ void OrganizeDialog( IRadNameSpace* namespaceObj );
+
+ SelectableDialog* FindDialogForEvent( EventEnum id, Character* character1, Character* character2,
+ tUID charUID1, tUID charUID2, radKey32 convKey, bool isVillain );
+
+ static Character* GetStinkySkinPointer( tUID charUID );
+
+ private:
+ //Prevent wasteful constructor creation.
+ DialogList( const DialogList& original );
+ DialogList& operator=( const DialogList& rhs );
+
+ //
+ // Sound file naming convention tests
+ //
+ bool isIndividualLine( IDaSoundResource* resource );
+ bool isConversationLine( IDaSoundResource* resource );
+
+ bool hasConversationPrefix( const char* name )
+ { return( ( name[0] == 'C' ) && ( name[1] == '_' ) ); }
+ bool hasOneLinerPrefix( const char* name );
+ unsigned int underscoreCount( const char* name );
+
+ SelectableDialog* searchDialogList( EventEnum id, tUID characterUID1, tUID characterUID2,
+ SelectableDialogList& list, radKey32 convName, bool isVillain,
+ bool fuzzyPedMatch );
+
+ SelectableDialog* searchDialogList( EventEnum id, tUID characterUID1, tUID characterUID2,
+ SelectableDialogList& list, radKey32 convName, bool isVillain );
+
+ SelectableDialog* searchDialogList( EventEnum id, Character* character1,
+ Character* character2, SelectableDialogList& list,
+ radKey32 convName, bool isVillain );
+
+ bool characterMatches( tUID characterUID, SelectableDialog* dialog, bool fuzzyPedMatch );
+
+ tUID getPuppetUID( Character* characterPtr );
+
+ static void dumpDialogCoverage( void* userData );
+
+ //
+ // List of level/mission dialogs
+ //
+ SelectableDialogList m_missionLists[GameplayManager::MAX_LEVELS][GameplayManager::MAX_MISSIONS+1];
+
+ //
+ // Generic dialog list
+ //
+ SelectableDialogList m_genericDialogList;
+
+ static radKey32 s_introKey;
+ static radKey32 s_aztecKey;
+ static tUID s_milhouseKey;
+ static tUID s_nelsonKey;
+ static tUID s_raceZombie1;
+ static tUID s_raceZombie2;
+
+ //
+ // Debug flag
+ //
+ static bool s_showDialogSpew;
+};
+
+
+#endif // DIALOGLIST_H
+
diff --git a/game/code/sound/dialog/dialogpriorityqueue.cpp b/game/code/sound/dialog/dialogpriorityqueue.cpp
new file mode 100644
index 0000000..5a57510
--- /dev/null
+++ b/game/code/sound/dialog/dialogpriorityqueue.cpp
@@ -0,0 +1,536 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogpriorityqueue.cpp
+//
+// Description: Responsible for managing the outstanding dialog playback requests.
+// When the DialogCoordinator needs to play dialog, it hands the
+// PlayableDialog object off, and the DialogPriorityQueue determines
+// if it can be played, or if it should wait until some other dialog
+// completes. When a PlayableDialog is ready for playback, it gets
+// handed to the SimpsonsSoundPlayer.
+//
+// History: 04/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radtime.hpp>
+#include <radnamespace.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/dialog/dialogpriorityqueue.h>
+
+#include <sound/dialog/dialogqueueelement.h>
+#include <sound/dialog/selectabledialog.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/idasoundtuner.h>
+#include <sound/soundmanager.h>
+
+#include <memory/srrmemory.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Probability of optional play success as chance in 256
+//
+static const unsigned int OPL_PROB = 64;
+
+//#define DEBUG_QUEUE_REFCOUNT
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// DialogPriorityQueue::DialogPriorityQueue
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DialogPriorityQueue::DialogPriorityQueue() :
+#ifdef SOUND_DEBUG_INFO_ENABLED
+ m_debugPage( 3, GetSoundManager()->GetDebugDisplay() ),
+#endif
+ m_nowPlaying( NULL ),
+ m_permitQueueAdvance( true )
+{
+}
+
+//==============================================================================
+// DialogPriorityQueue::~DialogPriorityQueue
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DialogPriorityQueue::~DialogPriorityQueue()
+{
+}
+
+//=============================================================================
+// DialogPriorityQueue::AddDialogToQueue
+//=============================================================================
+// Description: Place dialog on queue and play if possible
+//
+// Parameters: dialog - dialog to add to queue
+//
+// Return: void
+//
+//=============================================================================
+void DialogPriorityQueue::AddDialogToQueue( SelectableDialog& dialog, rmt::Vector* posn )
+{
+ DialogPriority priority;
+ DialogQueueElement* queueElement;
+ unsigned int diceRoll;
+
+ //
+ // Check the priority on the dialog, and use it to find the appropriate
+ // spot in the queue.
+ //
+ priority = DialogQueueElement::CalculateDialogPriority( dialog );
+
+ if( priority == OccasionalPlayLine )
+ {
+ //
+ // Random play
+ //
+ diceRoll = ( rand() % 100 );
+ if( diceRoll >= DialogQueueElement::CalculateDialogProbability( dialog ) )
+ {
+ return;
+ }
+ }
+
+ //
+ // Ducking for dialog
+ //
+ if( m_nowPlaying == NULL )
+ {
+ Sound::daSoundRenderingManager::GetInstance()->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_DIALOG, NULL, false );
+ GetSoundManager()->MuteNISPlayers();
+ }
+
+ //
+ // Don't bother playing dialog if we're already playing the same thing.
+ // This can happen with collision events that get triggered zillions of
+ // times
+ //
+ if( ( m_nowPlaying == NULL )
+ || !( m_nowPlaying->DialogMatches( &dialog ) ) )
+ {
+ queueElement = new ( GMA_TEMP ) DialogQueueElement( &dialog );
+
+ if( priority == MustPlayImmediately )
+ {
+ //
+ // Special case. Place this guy at the head of the queue and kill
+ // whatever's playing right now.
+ //
+ if( m_nowPlaying )
+ {
+ //
+ // If we don't halt the dialog queue, the stop callback will advance
+ // it on us and we get two dialog lines
+ //
+ m_permitQueueAdvance = false;
+
+ m_nowPlaying->StopDialog();
+#ifdef DEBUG_QUEUE_REFCOUNT
+ rTunePrintf( "AddDialogToQueue %x: Count %d\n", m_nowPlaying, m_nowPlaying->GetRefCount() );
+#endif
+ //
+ // One more check. The StopDialog() above may already have made m_nowPlaying
+ // go away
+ //
+ if( m_nowPlaying != NULL )
+ {
+ m_nowPlaying->Release();
+ }
+
+ m_permitQueueAdvance = true;
+ }
+
+ m_nowPlaying = queueElement;
+ playDialog( posn );
+ }
+ else
+ {
+ //
+ // Stick it in the list
+ //
+ queueElement->AddToQueue( &m_dialogQueue, posn );
+ }
+ }
+
+#ifndef RAD_RELEASE
+ //
+ // Mark dialog as played for coverage testing purposes
+ //
+ dialog.MarkAsPlayed();
+#endif
+}
+
+//=============================================================================
+// DialogPriorityQueue::StopCurrentDialog
+//=============================================================================
+// Description: If we have something playing, stop it and yank it off the
+// queue
+//
+// Parameters: None
+//
+// Return: true if there was dialog to stop, false otherwise
+//
+//=============================================================================
+bool DialogPriorityQueue::StopCurrentDialog()
+{
+ bool dialogStopped = false;
+
+ if( m_nowPlaying )
+ {
+ //
+ // Stop it and let the callback do the rest
+ //
+ m_nowPlaying->StopDialog();
+ dialogStopped = true;
+ }
+
+ return( dialogStopped );
+}
+
+//=============================================================================
+// DialogPriorityQueue::StopAllDialog
+//=============================================================================
+// Description: Kill the queue. This'll most likely be done when gameplay
+// ends.
+//
+// Parameters: None
+//
+// Return: true if there was dialog to stop, false otherwise
+//
+//=============================================================================
+bool DialogPriorityQueue::StopAllDialog()
+{
+ DialogQueueElement* current;
+ bool dialogStopped = false;
+
+ //
+ // Just in case we're still in a paused state. Stopping paused dialogue doesn't
+ // seem to give us our stop callbacks
+ //
+ UnpauseDialog();
+
+ m_permitQueueAdvance = false;
+
+ while( !m_dialogQueue.empty() )
+ {
+ current = m_dialogQueue.front();
+ current->RemoveSelfFromList();
+ current->Release();
+ }
+
+ if( m_nowPlaying != NULL )
+ {
+ m_nowPlaying->StopDialog();
+
+ //
+ // Check again to make sure that the dialog didn't remove itself.
+ // Since the queue was emptied first, nothing else should replace
+ // the currently playing dialog afterward.
+ //
+ if( m_nowPlaying != NULL )
+ {
+#ifdef DEBUG_QUEUE_REFCOUNT
+ rTunePrintf( "StopAllDialog %x: Count %d\n", m_nowPlaying, m_nowPlaying->GetRefCount() );
+#endif
+ m_nowPlaying->Release();
+ m_nowPlaying = NULL;
+
+ Sound::daSoundRenderingManager::GetInstance()->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_DIALOG, NULL, true );
+ GetSoundManager()->UnmuteNISPlayers();
+ }
+
+ dialogStopped = true;
+ }
+
+ m_permitQueueAdvance = true;
+
+ //
+ // Since we don't seem to get the OnPlaybackComplete callback for the queue
+ // element, throw a couple of shutup events in case someone was mouth flapping
+ //
+ GetEventManager()->TriggerEvent( EVENT_PC_SHUTUP );
+ GetEventManager()->TriggerEvent( EVENT_NPC_SHUTUP );
+
+ return( dialogStopped );
+}
+
+//=============================================================================
+// DialogPriorityQueue::PauseDialog
+//=============================================================================
+// Description: Pause the dialog players
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogPriorityQueue::PauseDialog()
+{
+ m_player1.Pause();
+ m_player2.Pause();
+ m_positionalPlayer1.Pause();
+ m_positionalPlayer2.Pause();
+}
+
+//=============================================================================
+// DialogPriorityQueue::UnpauseDialog
+//=============================================================================
+// Description: Unpause the dialog players
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogPriorityQueue::UnpauseDialog()
+{
+ if( m_player1.IsPaused() )
+ {
+ m_player1.Continue();
+ }
+ if( m_player2.IsPaused() )
+ {
+ m_player2.Continue();
+ }
+ if( m_positionalPlayer1.IsPaused() )
+ {
+ m_positionalPlayer1.Continue();
+ }
+ if( m_positionalPlayer2.IsPaused() )
+ {
+ m_positionalPlayer2.Continue();
+ }
+}
+
+//=============================================================================
+// DialogPriorityQueue::OnDialogLineComplete
+//=============================================================================
+// Description: Called when a line of dialog in the currently playing
+// conversation is complete
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogPriorityQueue::OnDialogLineComplete()
+{
+ //
+ // TODO: Stop the mouth flapping and eye blinking
+ //
+}
+
+//=============================================================================
+// DialogPriorityQueue::OnDialogComplete
+//=============================================================================
+// Description: Called when the currently playing SelectableDialog is
+// complete
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogPriorityQueue::OnDialogComplete()
+{
+ //
+ // TODO: Stop the mouth flapping and eye blinking
+ //
+
+ //
+ // Get rid of the currently playing element
+ //
+#ifdef DEBUG_QUEUE_REFCOUNT
+ rTunePrintf( "OnDialogComplete %x: Count %d\n", m_nowPlaying, m_nowPlaying->GetRefCount() );
+#endif
+ m_nowPlaying->Release();
+ if( !m_dialogQueue.empty() && m_permitQueueAdvance )
+ {
+ m_nowPlaying = m_dialogQueue.front();
+ m_nowPlaying->RemoveSelfFromList();
+ playDialog( m_nowPlaying->GetPosition() );
+ }
+ else
+ {
+ m_nowPlaying = NULL;
+
+ Sound::daSoundRenderingManager::GetInstance()->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_DIALOG, NULL, true );
+ GetSoundManager()->UnmuteNISPlayers();
+ }
+}
+
+//=============================================================================
+// DialogPriorityQueue::Service
+//=============================================================================
+// Description: Do servicing stuff
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogPriorityQueue::ServiceOncePerFrame()
+{
+ DialogQueueElement::Service();
+
+ //
+ // Just to be safe, don't update the positional players here. The update
+ // function just does stuff for moving sounds and pausing out-of-range
+ // stuff, none of which applies here. It's theoretically safe to do the
+ // right thing and service these players, but we're way too close to final
+ // to change the status quo.
+ //
+ //m_positionalPlayer1.ServiceOncePerFrame();
+ //m_positionalPlayer2.ServiceOncePerFrame();
+
+ advanceQueue();
+
+ serviceDebugPage();
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// DialogPriorityQueue::advanceQueue
+//=============================================================================
+// Description: Advance the queue if non-empty and nothing's playing.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogPriorityQueue::advanceQueue()
+{
+ if( ( m_nowPlaying == NULL ) && ( !m_dialogQueue.empty() ) && m_permitQueueAdvance )
+ {
+ m_nowPlaying = m_dialogQueue.front();
+ m_nowPlaying->RemoveSelfFromList();
+ playDialog( m_nowPlaying->GetPosition() );
+ }
+}
+
+//=============================================================================
+// DialogPriorityQueue::playDialog
+//=============================================================================
+// Description: Start a dialog line playing with the correct players
+//
+// Parameters: posn - position of speaker for positional dialogue. NULL
+// if non-positional
+//
+// Return: void
+//
+//=============================================================================
+void DialogPriorityQueue::playDialog( rmt::Vector* posn )
+{
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ positionalSoundSettings* parameters;
+
+ if( posn != NULL )
+ {
+ //
+ // Before starting playback, set up the positional stuff
+ //
+ //
+ // Get the positionalSoundSettings object for the wasp sound
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ nameSpaceObj = nameSpace->GetInstance( ::radMakeKey32( "posn_dialog_settings" ) );
+ if( nameSpaceObj != NULL )
+ {
+ parameters = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj );
+ m_positionalPlayer1.SetParameters( parameters );
+
+ m_positionalPlayer1.SetPosition( posn->x, posn->y, posn->z );
+
+ m_nowPlaying->PlayDialog( m_positionalPlayer1, m_positionalPlayer2, this, this );
+ }
+ else
+ {
+ rTuneAssertMsg( false, "No min/max for positional dialogue? Bad, call Esan." );
+
+ //
+ // Handle gracefully in release
+ //
+ m_nowPlaying->PlayDialog( m_player1, m_player2, this, this );
+ }
+ }
+ else
+ {
+ m_nowPlaying->PlayDialog( m_player1, m_player2, this, this );
+ }
+}
+
+//=============================================================================
+// DialogPriorityQueue::serviceDebugPage
+//=============================================================================
+// Description: Update the SoundInfo data available in Watcher
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogPriorityQueue::serviceDebugPage()
+{
+#ifdef SOUND_DEBUG_INFO_ENABLED
+ int i;
+ int queueLength;
+ DialogQueueType::const_iterator iter;
+
+ if( m_nowPlaying != NULL )
+ {
+ //
+ // For the debug page, nowPlaying is part of the queue
+ //
+ queueLength = m_dialogQueue.size() + 1;
+ m_debugPage.SetQueueLength( queueLength );
+
+ m_nowPlaying->FillDebugInfo( m_debugPage, 0 );
+ i = 1;
+ for( iter = m_dialogQueue.begin(); iter != m_dialogQueue.end(); ++iter )
+ {
+ (*iter)->FillDebugInfo( m_debugPage, i++ );
+ }
+ }
+ else
+ {
+ m_debugPage.SetQueueLength( 0 );
+ }
+#endif
+}
diff --git a/game/code/sound/dialog/dialogpriorityqueue.h b/game/code/sound/dialog/dialogpriorityqueue.h
new file mode 100644
index 0000000..7ef5cfe
--- /dev/null
+++ b/game/code/sound/dialog/dialogpriorityqueue.h
@@ -0,0 +1,99 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogpriorityqueue.h
+//
+// Description: Responsible for managing the outstanding dialog playback requests.
+// When the DialogCoordinator needs to play dialog, it hands the
+// PlayableDialog object off, and the DialogPriorityQueue determines
+// if it can be played, or if it should wait until some other dialog
+// completes. When a PlayableDialog is ready for playback, it gets
+// handed to the SimpsonsSoundPlayer.
+//
+// History: 04/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef DIALOGPRIORITYQUEUE_H
+#define DIALOGPRIORITYQUEUE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/simpsonssoundplayer.h>
+#include <sound/positionalsoundplayer.h>
+#include <sound/dialog/dialogqueueelement.h>
+#include <sound/dialog/dialogqueuetype.h>
+#include <sound/dialog/dialogsounddebugpage.h>
+
+//========================================
+// Forward References
+//========================================
+class SelectableDialog;
+
+//=============================================================================
+//
+// Synopsis: DialogPriorityQueue
+//
+//=============================================================================
+
+class DialogPriorityQueue : public DialogLineCompleteCallback,
+ public DialogCompleteCallback
+{
+ public:
+ DialogPriorityQueue();
+ virtual ~DialogPriorityQueue();
+
+ void AddDialogToQueue( SelectableDialog& dialog, rmt::Vector* posn = NULL );
+
+ bool StopCurrentDialog();
+ bool StopAllDialog();
+
+ void PauseDialog();
+ void UnpauseDialog();
+
+ void OnDialogLineComplete();
+ void OnDialogComplete();
+
+ void ServiceOncePerFrame();
+
+ private:
+ //Prevent wasteful constructor creation.
+ DialogPriorityQueue( const DialogPriorityQueue& original );
+ DialogPriorityQueue& operator=( const DialogPriorityQueue& rhs );
+
+ void advanceQueue();
+ void serviceDebugPage();
+ void playDialog( rmt::Vector* posn );
+
+#ifdef SOUND_DEBUG_INFO_ENABLED
+ DialogSoundDebugPage m_debugPage;
+#endif
+
+ //
+ // Dialog sound players. Two needed so that we can queue up a second
+ // line while the first is playing.
+ //
+ SimpsonsSoundPlayer m_player1;
+ SimpsonsSoundPlayer m_player2;
+
+ PositionalSoundPlayer m_positionalPlayer1;
+ PositionalSoundPlayer m_positionalPlayer2;
+
+ //
+ // Points to whatever's playing right this instant. Stored separately
+ // from the waiting queue objects.
+ //
+ DialogQueueElement* m_nowPlaying;
+
+ //
+ // The queue
+ //
+ DialogQueueType m_dialogQueue;
+
+ bool m_permitQueueAdvance;
+};
+
+
+#endif // DIALOGPRIORITYQUEUE_H
+
diff --git a/game/code/sound/dialog/dialogqueueelement.cpp b/game/code/sound/dialog/dialogqueueelement.cpp
new file mode 100644
index 0000000..047b626
--- /dev/null
+++ b/game/code/sound/dialog/dialogqueueelement.cpp
@@ -0,0 +1,951 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogqueueelement.cpp
+//
+// Description: Implement DialogQueueElement
+//
+// History: 04/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radtime.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/dialog/dialogqueueelement.h>
+
+#include <sound/dialog/selectabledialog.h>
+#include <sound/dialog/dialogsounddebugpage.h>
+#include <sound/dialog/dialogline.h>
+#include <sound/dialog/dialoglist.h>
+
+#include <memory/srrmemory.h>
+#include <events/eventmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/character/charactermanager.h>
+
+//#define DEBUG_QUEUE_REFCOUNT
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+IRadTimerList* DialogQueueElement::s_timerList = NULL;
+bool DialogQueueElement::s_watcherInitialized = false;
+
+struct LinePriorityTableEntry
+{
+ EventEnum eventID;
+ DialogPriority priority;
+ unsigned int probability;
+#ifndef RAD_RELEASE
+ const char* eventName;
+#endif
+};
+
+LinePriorityTableEntry priorityTable[] =
+{
+ { EVENT_GETINTOVEHICLE_START, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "GIC"
+#endif
+ },
+ { EVENT_BURNOUT, OccasionalPlayLine, 15
+#ifndef RAD_RELEASE
+ , "Burn"
+#endif
+ },
+ { EVENT_DESTINATION_REACHED, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "Arrive"
+#endif
+ },
+ { EVENT_BIG_AIR, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "Air"
+#endif
+ },
+ { EVENT_BIG_CRASH, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "Damage"
+#endif
+ },
+ { EVENT_GETOUTOFVEHICLE_START, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "GOC"
+#endif
+ },
+ { EVENT_RACE_PASSED_AI, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "Pass"
+#endif
+ },
+ { EVENT_RACE_GOT_PASSED_BY_AI, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "Passed"
+#endif
+ },
+ { EVENT_KICK_NPC_SOUND, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "HitByW"
+#endif
+ },
+ { EVENT_PLAYER_CAR_HIT_NPC, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "HitByC"
+#endif
+ },
+ { EVENT_PEDESTRIAN_DODGE, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "NHitByC"
+#endif
+ },
+ { EVENT_PLAYER_MAKES_LIGHT_OF_CAR_HITTING_NPC, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "HitP"
+#endif
+ },
+ { EVENT_PEDESTRIAN_SMACKDOWN, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "Char"
+#endif
+ },
+ { EVENT_MINOR_VEHICLE_CRASH, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "Mcrash"
+#endif
+ },
+ { EVENT_TRAFFIC_IMPEDED, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "CarWay"
+#endif
+ },
+ { EVENT_HIT_BREAKABLE, OccasionalPlayLine, 20
+#ifndef RAD_RELEASE
+ , "Break"
+#endif
+ },
+ { EVENT_COLLECT_OBJECT, OccasionalPlayLine, 50
+#ifndef RAD_RELEASE
+ , "ObjectW"
+#endif
+ },
+ { EVENT_BIG_BOOM_SOUND, ShouldPlayLine, 100
+#ifndef RAD_RELEASE
+ , "Dcar"
+#endif
+ },
+ { EVENT_MISSION_FAILURE, ShouldPlayLine, 100
+#ifndef RAD_RELEASE
+ , "Mfail"
+#endif
+ },
+ { EVENT_TAIL_LOST_DIALOG, ShouldPlayLine, 100
+#ifndef RAD_RELEASE
+ , "Tail"
+#endif
+ },
+ { EVENT_MISSION_SUCCESS_DIALOG, ShouldPlayLine, 100
+#ifndef RAD_RELEASE
+ , "Mvic"
+#endif
+ },
+ { EVENT_CARD_COLLECTED, MustPlayLine, 100
+#ifndef RAD_RELEASE
+ , "Card"
+#endif
+ },
+ { EVENT_PHONE_BOOTH_RIDE_REQUEST, MustPlayLine, 100
+#ifndef RAD_RELEASE
+ , "Askride"
+#endif
+ },
+ { EVENT_PHONE_BOOTH_NEW_VEHICLE_SELECTED, MustPlayLine, 100
+#ifndef RAD_RELEASE
+ , "Ridereply"
+#endif
+ },
+ { EVENT_PHONE_BOOTH_OLD_VEHICLE_RESELECTED, MustPlayLine, 100
+#ifndef RAD_RELEASE
+ , "Answer"
+#endif
+ },
+ { EVENT_MISSION_BRIEFING_ACCEPTED, MustPlayLine, 100
+#ifndef RAD_RELEASE
+ , "Mstart"
+#endif
+ },
+ { EVENT_CONVERSATION_INIT_DIALOG, MustPlayImmediately, 100
+#ifndef RAD_RELEASE
+ , "EVENT_CONVERSATION_INIT_DIALOG"
+#endif
+ },
+ { EVENT_IN_GAMEPLAY_CONVERSATION, MustPlayImmediately, 100
+#ifndef RAD_RELEASE
+ , "EVENT_IN_GAMEPLAY_CONVERSATION"
+#endif
+ },
+ { EVENT_TUTORIAL_DIALOG_PLAY, MustPlayImmediately, 100
+#ifndef RAD_RELEASE
+ , "EVENT_TUTORIAL_DIALOG_PLAY"
+#endif
+ },
+ { EVENT_DING_DONG, MustPlayImmediately, 100
+#ifndef RAD_RELEASE
+ , "EVENT_DING_DONG"
+#endif
+ }
+};
+
+static unsigned int priorityTableLength = sizeof( priorityTable ) / sizeof( LinePriorityTableEntry );
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// DialogQueueElement::DialogQueueElement
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DialogQueueElement::DialogQueueElement( SelectableDialog* dialog ) :
+ m_dialog( dialog ),
+ m_player1( NULL ),
+ m_player2( NULL ),
+ m_lineDoneCallback( NULL ),
+ m_dialogDoneCallback( NULL ),
+ m_linesPlayed( 0 ),
+ m_queue( NULL ),
+ m_hasPosition( false )
+{
+ unsigned int lifeInMsecs;
+
+ rAssert( m_dialog != NULL );
+
+ if( s_timerList == NULL )
+ {
+ //
+ // We need enough timers for each element in the queue, plus one for
+ // currently playing dialog and one to create before we test for the
+ // queue being full. Actually, we don't really need one for current
+ // dialog, but we'll call that safety margin.
+ //
+ ::radTimeCreateList( &s_timerList, MAX_QUEUE_ELEMENTS + 2, GMA_AUDIO_PERSISTENT );
+ }
+
+ m_timer = NULL;
+
+ lifeInMsecs = DialogLine::GetLifeInMsecsForEvent( dialog->GetEvent() );
+
+ if( lifeInMsecs > 0 )
+ {
+ s_timerList->CreateTimer( &m_timer,
+ lifeInMsecs,
+ this,
+ NULL,
+ true,
+ IRadTimer::ResetModeOneShot );
+ rAssert( m_timer != NULL );
+ }
+}
+
+//==============================================================================
+// DialogQueueElement::~DialogQueueElement
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DialogQueueElement::~DialogQueueElement()
+{
+ if( m_timer )
+ {
+ m_timer->UnregisterCallback( this );
+#ifdef DEBUG_QUEUE_REFCOUNT
+ rTunePrintf( "~DialogQueueElement Timer: %d\n", m_timer->GetRefCount() );
+#endif
+ m_timer->Release();
+ }
+}
+
+//=============================================================================
+// DialogQueueElement::OnTimerDone
+//=============================================================================
+// Description: If the timer goes off and calls this function, then the
+// lifetime for the dialog has expired and we need to excuse
+// ourselves from the queue.
+//
+// Parameters: elapsedTime - ignored
+// pUserData - ignored
+//
+// Return: void
+//
+//=============================================================================
+void DialogQueueElement::OnTimerDone( unsigned int elapsedTime, void * pUserData )
+{
+ RemoveSelfFromList();
+
+ //
+ // We're now expendable. Delete self.
+ //
+#ifdef DEBUG_QUEUE_REFCOUNT
+ rTunePrintf( "OnTimerDone %x: Count %d\n", this, GetRefCount() );
+#endif
+ Release();
+}
+
+//=============================================================================
+// DialogQueueElement::AddToQueue
+//=============================================================================
+// Description: Add self to the given dialog queue
+//
+// Parameters: queue - queue to add self to
+// posn - position of dialog speaker, NULL if non-positional
+//
+// Return: none
+//
+//=============================================================================
+void DialogQueueElement::AddToQueue( DialogQueueType* queue, rmt::Vector* posn )
+{
+ DialogQueueElement* lowerPriorityElement;
+ bool duplicateFound = false;
+ DialogPriority priority = CalculateDialogPriority( *m_dialog );
+
+ rAssert( queue != NULL );
+ m_queue = queue;
+
+ if( posn != NULL )
+ {
+ m_hasPosition = true;
+ m_position = *posn;
+ }
+ else
+ {
+ m_hasPosition = false;
+ }
+
+ if( m_queue->empty() )
+ {
+ m_queue->push_front( this );
+ }
+ else
+ {
+ DialogQueueType::iterator iter = m_queue->begin();
+
+ //
+ // Search the rest of the list to see if we've already got this
+ // dialog in it. It couldn't have been passed over yet because
+ // all the earlier stuff has a higher priority
+ //
+ for( ; iter != m_queue->end(); ++iter )
+ {
+ if( (*iter)->DialogMatches( m_dialog ) )
+ {
+ duplicateFound = true;
+ break;
+ }
+ }
+
+ if( !duplicateFound )
+ {
+ if( m_queue->size() >= MAX_QUEUE_ELEMENTS )
+ {
+ //
+ // Dialog full, something has to go. Ditch the lowest priority
+ // one. If that's the one we're inserting, don't do it. If there's
+ // a lower-priority one in the queue, ditch that one instead.
+ //
+ if( iter == m_queue->end() )
+ {
+ //
+ // Nothing lower
+ //
+#ifdef DEBUG_QUEUE_REFCOUNT
+ rTunePrintf( "AddToQueue %x: Count %d\n", this, GetRefCount() );
+#endif
+ Release();
+ }
+ else
+ {
+ lowerPriorityElement = *iter;
+ m_queue->insert( iter, this );
+ m_queue->remove( lowerPriorityElement );
+ lowerPriorityElement->Release();
+ }
+ }
+ else
+ {
+ //
+ // Find spot to insert based on priority
+ //
+ iter = m_queue->begin();
+ while( ( iter != m_queue->end() )
+ && ( (*iter)->GetPriority() >= priority ) )
+ {
+ ++iter;
+ }
+
+ m_queue->insert( iter, this );
+ }
+ }
+ else
+ {
+ //
+ // No need to add this to the queue, and the caller is now relying
+ // on this object for self-management, so delete self
+ //
+#ifdef DEBUG_QUEUE_REFCOUNT
+ rTunePrintf( "AddToQueue %x: Count %d\n", this, GetRefCount() );
+#endif
+ Release();
+ }
+ }
+}
+
+//=============================================================================
+// DialogQueueElement::RemoveSelfFromList
+//=============================================================================
+// Description: Pull ourself from the queue element list
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogQueueElement::RemoveSelfFromList()
+{
+ rAssert( m_queue != NULL );
+
+ m_queue->remove( this );
+}
+
+//=============================================================================
+// DialogQueueElement::GetDialogPriority
+//=============================================================================
+// Description: Searches the priority table for a priority matching the event
+// ID for this bit of dialog.
+//
+// Parameters: dialog - dialog to find priority for
+//
+// Return: DialogPriority value for dialog if found in table,
+// UnknownPriority otherwise.
+//
+//=============================================================================
+DialogPriority DialogQueueElement::CalculateDialogPriority( const SelectableDialog& dialog )
+{
+ EventEnum eventID;
+ unsigned int i;
+ DialogPriority priority = UnknownPriority;
+
+ eventID = dialog.GetEvent();
+ for( i = 0; i < priorityTableLength; i++ )
+ {
+ if( priorityTable[i].eventID == eventID )
+ {
+ priority = priorityTable[i].priority;
+ break;
+ }
+ }
+
+ return( priority );
+}
+
+//=============================================================================
+// DialogQueueElement::CalculateDialogProbability
+//=============================================================================
+// Description: Searches the priority table for a probability matching the event
+// ID for this bit of dialog.
+//
+// Parameters: dialog - dialog to find priority for
+//
+// Return: Probability as % for dialog if found in table,
+// 100 otherwise.
+//
+//=============================================================================
+unsigned int DialogQueueElement::CalculateDialogProbability( const SelectableDialog& dialog )
+{
+ EventEnum eventID;
+ unsigned int i;
+ unsigned int probability = 100;
+
+ eventID = dialog.GetEvent();
+ for( i = 0; i < priorityTableLength; i++ )
+ {
+ if( ( priorityTable[i].priority == OccasionalPlayLine )
+ && ( priorityTable[i].eventID == eventID ) )
+ {
+ probability = priorityTable[i].probability;
+ break;
+ }
+ }
+
+ return( probability );
+}
+
+//=============================================================================
+// DialogQueueElement::OnPlaybackComplete
+//=============================================================================
+// Description: Callback function, triggered when currently playing dialog
+// is finished
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogQueueElement::OnPlaybackComplete()
+{
+ unsigned int numDialogLines = m_dialog->GetNumDialogLines();
+ SimpsonsSoundPlayer* player;
+ SimpsonsSoundPlayer* queuer; // I think I made up a word
+ Character* npcPtr;
+
+ //
+ // Reference self in case one of the callbacks we trigger
+ // here tries to delete this object
+ //
+ AddRef();
+
+ if( ++m_linesPlayed >= numDialogLines )
+ {
+ if( m_dialogDoneCallback )
+ {
+ if( isMouthFlappingEvent( m_dialog->GetEvent() ) )
+ {
+ //
+ // Coordinate the mouth flapping. Find out whether the PC
+ // or the NPC just finished talking, and send out the appropriate
+ // talk/shutup events.
+ //
+ if( dialogLineIsWalker( m_linesPlayed ) )
+ {
+ GetEventManager()->TriggerEvent( EVENT_PC_SHUTUP );
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_NPC_SHUTUP );
+ }
+ }
+
+ //
+ // Tell the rest of the world that we're done
+ //
+ if( m_dialog->GetEvent() == EVENT_CONVERSATION_INIT_DIALOG )
+ {
+/*
+ if( m_dialog->GetMission() == static_cast<unsigned int>( DialogLine::TUTORIAL_MISSION_NUMBER ) )
+ {
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_DONE );
+ }
+ else
+*/
+ {
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_DONE );
+ }
+ }
+ else if( m_dialog->GetEvent() == EVENT_TUTORIAL_DIALOG_PLAY )
+ {
+ GetEventManager()->TriggerEvent( EVENT_TUTORIAL_DIALOG_DONE );
+ }
+
+ m_dialogDoneCallback->OnDialogComplete();
+ }
+ }
+ else
+ {
+ if( m_lineDoneCallback )
+ {
+ m_lineDoneCallback->OnDialogLineComplete();
+ }
+
+ if( isMouthFlappingEvent( m_dialog->GetEvent() ) )
+ {
+ //
+ // Coordinate the mouth flapping. Find out whether the PC
+ // or the NPC just finished talking, and send out the appropriate
+ // talk/shutup events.
+ //
+ if( dialogLineIsWalker( m_linesPlayed ) )
+ {
+ GetEventManager()->TriggerEvent( EVENT_PC_SHUTUP );
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_NPC_SHUTUP );
+ }
+
+ //
+ // Start up the next line
+ //
+ if( dialogLineIsWalker( m_linesPlayed + 1 ) )
+ {
+ GetEventManager()->TriggerEvent( EVENT_PC_TALK );
+ }
+ else
+ {
+ npcPtr = dialogLineIsNPC( m_linesPlayed + 1 );
+ if( npcPtr != NULL )
+ {
+ GetEventManager()->TriggerEvent( EVENT_NPC_TALK, npcPtr );
+ }
+ }
+ }
+
+ //
+ // Even line numbers on player 1, odd on player 2
+ //
+ if( m_linesPlayed % 2 == 0 )
+ {
+ player = m_player1;
+ queuer = m_player2;
+ }
+ else
+ {
+ player = m_player2;
+ queuer = m_player1;
+ }
+ m_dialog->PlayQueuedLine( *player, this );
+
+ //
+ // Queue dialog if necessary
+ //
+ if( numDialogLines > m_linesPlayed + 1 )
+ {
+ m_dialog->QueueLine( m_linesPlayed + 1, *queuer );
+ }
+ }
+
+#ifdef DEBUG_QUEUE_REFCOUNT
+ rTunePrintf( "OnPlaybackComplete %x: Count %d\n", this, GetRefCount() );
+#endif
+ Release();
+}
+
+//=============================================================================
+// DialogQueueElement::OnSoundReady
+//=============================================================================
+// Description: Unused, required for SimpsonsSoundPlayerCallback interface
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogQueueElement::OnSoundReady()
+{
+}
+
+//=============================================================================
+// DialogQueueElement::PlayDialog
+//=============================================================================
+// Description: Comment
+//
+// Parameters: player - SimpsonsSoundPlayer to use for playback
+// lineCallback - callback for when an individual line of
+// dialog is done, but not the entire dialog
+// dialogCallback - callback for when the entire dialog is done
+//
+// Return: void
+//
+//=============================================================================
+void DialogQueueElement::PlayDialog( SimpsonsSoundPlayer& player1,
+ SimpsonsSoundPlayer& player2,
+ DialogLineCompleteCallback* lineCallback,
+ DialogCompleteCallback* dialogCallback )
+{
+ Character* npcPtr;
+
+ m_lineDoneCallback = lineCallback;
+ m_dialogDoneCallback = dialogCallback;
+
+ m_player1 = &player1;
+ m_player2 = &player2;
+ m_dialog->PlayLine( m_linesPlayed, *m_player1, this );
+ if( m_dialog->GetNumDialogLines() >= 2 )
+ {
+ //
+ // Queue up the next line
+ //
+ m_dialog->QueueLine( m_linesPlayed + 1, *m_player2 );
+ }
+
+ if( isMouthFlappingEvent( m_dialog->GetEvent() ) )
+ {
+ //
+ // Start the mouth flapping
+ //
+ if( dialogLineIsWalker( 1 ) )
+ {
+ GetEventManager()->TriggerEvent( EVENT_PC_TALK );
+ }
+ else
+ {
+ npcPtr = dialogLineIsNPC( 1 );
+ if( npcPtr != NULL )
+ {
+ GetEventManager()->TriggerEvent( EVENT_NPC_TALK, npcPtr );
+ }
+ }
+ }
+
+ if( m_timer )
+ {
+ m_timer->UnregisterCallback( this );
+#ifdef DEBUG_QUEUE_REFCOUNT
+ rTunePrintf( "PlayDialog Timer: %d\n", m_timer->GetRefCount() );
+#endif
+ m_timer->Release();
+ m_timer = NULL;
+ }
+}
+
+//=============================================================================
+// DialogQueueElement::StopDialog
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void DialogQueueElement::StopDialog()
+{
+ //
+ // Just in case of nasty callback action on the Stop() calls below
+ //
+ AddRef();
+
+ //
+ // Skip to the end of the dialog before triggering any stop callbacks
+ //
+ m_linesPlayed = m_dialog->GetNumDialogLines();
+
+ m_player1->Stop();
+ m_player2->Stop();
+
+ if( m_dialog != NULL )
+ {
+ if( isMouthFlappingEvent( m_dialog->GetEvent() ) )
+ {
+ GetEventManager()->TriggerEvent( EVENT_PC_SHUTUP );
+ GetEventManager()->TriggerEvent( EVENT_NPC_SHUTUP );
+ }
+ }
+
+#ifdef DEBUG_QUEUE_REFCOUNT
+ rTunePrintf( "StopDialog %x: Count %d\n", this, GetRefCount() );
+#endif
+ Release();
+}
+
+//=============================================================================
+// DialogQueueElement::Service
+//=============================================================================
+// Description: Service the timer list
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogQueueElement::Service()
+{
+ if( s_timerList != NULL )
+ {
+ s_timerList->Service();
+ }
+
+#ifndef RAD_RELEASE
+ if( !s_watcherInitialized )
+ {
+ //
+ // Register probability table in Watcher
+ //
+ unsigned int i;
+
+ for( i = 0; i < priorityTableLength; i++ )
+ {
+ if( priorityTable[i].priority == OccasionalPlayLine )
+ {
+ radDbgWatchAddUnsignedInt( &(priorityTable[i].probability),
+ priorityTable[i].eventName,
+ "Dialogue Tuning",
+ NULL,
+ NULL,
+ 0,
+ 100 );
+ }
+ }
+
+ s_watcherInitialized = true;
+ }
+#endif
+}
+
+//=============================================================================
+// DialogQueueElement::GetPosition
+//=============================================================================
+// Description: If this is a positional dialog, return a position
+//
+// Parameters: None
+//
+// Return: pointer to rmt::Vector with position if positional, NULL otherwise
+//
+//=============================================================================
+rmt::Vector* DialogQueueElement::GetPosition()
+{
+ rmt::Vector* retVal;
+
+ if( m_hasPosition )
+ {
+ retVal = &m_position;
+ }
+ else
+ {
+ retVal = NULL;
+ }
+
+ return( retVal );
+}
+
+void DialogQueueElement::FillDebugInfo( DialogSoundDebugPage& debugInfo, unsigned int lineNum )
+{
+ unsigned int msecs;
+
+ if( m_timer != NULL )
+ {
+ msecs = m_timer->GetTimeout();
+ }
+ else
+ {
+ msecs = 0;
+ }
+
+ debugInfo.SetQueueEntry( lineNum,
+ m_dialog->GetEvent(),
+ m_dialog->GetMission(),
+ m_dialog->GetLevel(),
+ m_dialog->GetDialogLineCharacterUID( 1 ),
+ CalculateDialogPriority( *m_dialog ),
+ msecs );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// DialogQueueElement::dialogLineIsWalker
+//=============================================================================
+// Description: Indicates whether the dialog line with the given number
+// belongs to the player character
+//
+// Parameters: lineNum - line number to check
+//
+// Return: true if player character, false otherwise
+//
+//=============================================================================
+bool DialogQueueElement::dialogLineIsWalker( unsigned int lineNum )
+{
+ tUID characterUID;
+ Character* walker;
+ tUID walkerUID;
+
+ characterUID = m_dialog->GetDialogLineCharacterUID( lineNum );
+ walker = GetAvatarManager()->GetAvatarForPlayer( 0 )->GetCharacter();
+ rAssert( walker != NULL );
+ walkerUID = walker->GetUID();
+
+ return( characterUID == walkerUID );
+}
+
+//=============================================================================
+// DialogQueueElement::dialogLineIsNPC
+//=============================================================================
+// Description: Indicates whether the dialog line with the given number
+// belongs to an NPC
+//
+// Parameters: lineNum - line number to check
+//
+// Return: pointer to character object if found, NULL otherwise
+//
+//=============================================================================
+Character* DialogQueueElement::dialogLineIsNPC( unsigned int lineNum )
+{
+ tUID speakerUID;
+ Character* npc;
+
+ speakerUID = m_dialog->GetDialogLineCharacterUID( lineNum );
+ npc = GetCharacterManager()->GetCharacterByName( speakerUID );
+
+ //
+ // If the character is an NPC, we'll get a pointer. If it's some
+ // character that doesn't exist in the world (e.g. Brockman in L7M1),
+ // then we get NULL.
+ //
+ if( npc != NULL )
+ {
+ return( npc );
+ }
+
+ //
+ // P.S. Not only do we have to check the speaker's UID, we have to
+ // check for stinky skins as well
+ //
+ return( DialogList::GetStinkySkinPointer( speakerUID ) );
+}
+
+//=============================================================================
+// DialogQueueElement::isMouthFlappingEvent
+//=============================================================================
+// Description: Indicate whether given dialogue event should trigger mouth
+// flapping
+//
+// Parameters: theEvent - event to scrutinize
+//
+// Return: true if mouth flapping event, false otherwise
+//
+//=============================================================================
+bool DialogQueueElement::isMouthFlappingEvent( EventEnum theEvent )
+{
+ bool retVal = false;
+
+ switch( theEvent )
+ {
+ case EVENT_CONVERSATION_INIT_DIALOG:
+ case EVENT_AMBIENT_ASKFOOD:
+ case EVENT_AMBIENT_FOODREPLY:
+ case EVENT_AMBIENT_GREETING:
+ case EVENT_AMBIENT_RESPONSE:
+ case EVENT_PEDESTRIAN_SMACKDOWN:
+ case EVENT_CHARACTER_TIRED_NOW:
+ retVal = true;
+ break;
+
+ default:
+ break;
+ }
+
+ return( retVal );
+} \ No newline at end of file
diff --git a/game/code/sound/dialog/dialogqueueelement.h b/game/code/sound/dialog/dialogqueueelement.h
new file mode 100644
index 0000000..54074b1
--- /dev/null
+++ b/game/code/sound/dialog/dialogqueueelement.h
@@ -0,0 +1,158 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogqueueelement.h
+//
+// Description: Wrapper for SelectableDialog objects in the dialog queue,
+// to associate them with a timer and to allow reception of
+// playback completion callbacks without having to bother
+// the queue class with partially-complete conversations.
+//
+// History: 04/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef DIALOGQUEUEELEMENT_H
+#define DIALOGQUEUEELEMENT_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radtime.hpp>
+
+#include <sound/simpsonssoundplayer.h>
+#include <sound/dialog/dialogqueuetype.h>
+#include <events/eventenum.h>
+
+//========================================
+// Forward References
+//========================================
+class SelectableDialog;
+class SimpsonsSoundPlayer;
+class DialogSoundDebugPage;
+class Character;
+
+//
+// Four levels of playback priority
+//
+enum DialogPriority
+{
+ UnknownPriority,
+
+ OccasionalPlayLine,
+ ShouldPlayLine,
+ MustPlayLine,
+ MustPlayImmediately
+};
+
+//=============================================================================
+//
+// Synopsis: DialogLineCompleteCallback - callback interface, triggered
+// when a line of dialog is finished playing, but more
+// dialog remains to be played
+//
+//=============================================================================
+
+class DialogLineCompleteCallback
+{
+ public:
+ virtual void OnDialogLineComplete() = 0;
+};
+
+//=============================================================================
+//
+// Synopsis: DialogCompleteCallback - callback interface, triggered
+// when all of the dialog has been played
+//
+//=============================================================================
+
+class DialogCompleteCallback
+{
+ public:
+ virtual void OnDialogComplete() = 0;
+};
+
+//=============================================================================
+//
+// Synopsis: DialogQueueElement
+//
+//=============================================================================
+
+class DialogQueueElement : public IRadTimerCallback,
+ public SimpsonsSoundPlayerCallback,
+ public radRefCount
+{
+ public:
+ IMPLEMENT_REFCOUNTED( "DialogQueueElement" );
+
+ DialogQueueElement( SelectableDialog* dialog );
+ virtual ~DialogQueueElement();
+
+ static void CreateTimerList();
+
+ void OnTimerDone( unsigned int elapsedTime, void * pUserData );
+
+ void AddToQueue( DialogQueueType* queue, rmt::Vector* posn );
+ void RemoveSelfFromList();
+
+ void PlayDialog( SimpsonsSoundPlayer& player1,
+ SimpsonsSoundPlayer& player2,
+ DialogLineCompleteCallback* lineCallback,
+ DialogCompleteCallback* dialogCallback );
+ void StopDialog();
+
+ DialogPriority GetPriority() { return( CalculateDialogPriority( *m_dialog ) ); }
+
+ bool DialogMatches( SelectableDialog* dialog ) { return( dialog == m_dialog ); }
+
+ rmt::Vector* GetPosition();
+
+ void FillDebugInfo( DialogSoundDebugPage& debugInfo, unsigned int lineNum );
+
+ //
+ // SimpsonsSoundPlayer callbacks
+ //
+ void OnPlaybackComplete();
+ void OnSoundReady();
+
+ static DialogPriority CalculateDialogPriority( const SelectableDialog& dialog );
+ static unsigned int CalculateDialogProbability( const SelectableDialog& dialog );
+ static void Service();
+
+ static const unsigned int MAX_QUEUE_ELEMENTS = 16;
+
+ private:
+ //Prevent wasteful constructor creation.
+ DialogQueueElement();
+ DialogQueueElement( const DialogQueueElement& original );
+ DialogQueueElement& operator=( const DialogQueueElement& rhs );
+
+ bool dialogLineIsWalker( unsigned int lineNum );
+ Character* dialogLineIsNPC( unsigned int lineNum );
+
+ bool isMouthFlappingEvent( EventEnum theEvent );
+
+ static IRadTimerList* s_timerList;
+
+ static bool s_watcherInitialized;
+
+ SelectableDialog* m_dialog;
+ IRadTimer* m_timer;
+
+ SimpsonsSoundPlayer* m_player1;
+ SimpsonsSoundPlayer* m_player2;
+
+ DialogLineCompleteCallback* m_lineDoneCallback;
+ DialogCompleteCallback* m_dialogDoneCallback;
+
+ unsigned int m_linesPlayed;
+
+ DialogQueueType* m_queue;
+
+ rmt::Vector m_position;
+ bool m_hasPosition;
+};
+
+
+#endif // DIALOGQUEUEELEMENT_H
+
diff --git a/game/code/sound/dialog/dialogqueuetype.h b/game/code/sound/dialog/dialogqueuetype.h
new file mode 100644
index 0000000..178396d
--- /dev/null
+++ b/game/code/sound/dialog/dialogqueuetype.h
@@ -0,0 +1,24 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogqueuetype.h
+//
+// Description: Typedef for STL list of dialog queue elements. Removed from
+// DialogPriorityQueue to avoid unnecessary dependency.
+//
+// History: 9/25/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef DIALOGQUEUETYPE_H
+#define DIALOGQUEUETYPE_H
+
+class DialogQueueElement;
+
+#include <list>
+#include <memory/stlallocators.h>
+
+typedef std::list< DialogQueueElement*, s2alloc<DialogQueueElement*> > DialogQueueType;
+
+#endif // DIALOGQUEUETYPE_H
+
diff --git a/game/code/sound/dialog/dialogselectiongroup.cpp b/game/code/sound/dialog/dialogselectiongroup.cpp
new file mode 100644
index 0000000..3b3b0f0
--- /dev/null
+++ b/game/code/sound/dialog/dialogselectiongroup.cpp
@@ -0,0 +1,376 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogselectiongroup.cpp
+//
+// Description: Represents a set of dialog lines or conversations that can
+// be randomly selected without affecting gameplay. For example,
+// if Homer had three generic lines for playback whenever he is
+// hit by a car, and all three can be substituted for each other
+// freely, then those lines would be grouped by a DialogSelectionGroup
+// object.
+//
+// History: 02/09/2002 + Created -- Darren
+//
+//=============================================================================
+#define DONTCHECKVECTORRESIZING
+//========================================
+// System Includes
+//========================================
+#include <radtime.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/dialog/dialogselectiongroup.h>
+
+#include <sound/simpsonssoundplayer.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Typical max number of elements in the vector, so as to not waste space
+//
+static const int TYPICAL_VECTOR_SIZE = 4;
+
+//
+// Value for in-use dialog index when nothing being played
+//
+static const short DIALOG_NOT_IN_USE = -1;
+
+static const short DIALOG_NOT_YET_USED = -1;
+
+
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// DialogSelectionGroup::DialogSelectionGroup
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DialogSelectionGroup::DialogSelectionGroup( SelectableDialog& dialog1,
+ SelectableDialog& dialog2 ) :
+ m_currentlyPlayingDialog( DIALOG_NOT_IN_USE ),
+ m_lastSelection( DIALOG_NOT_YET_USED )
+{
+ HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT );
+
+ m_dialogVector.reserve( TYPICAL_VECTOR_SIZE );
+
+ m_dialogVector.push_back( &dialog1 );
+ m_dialogVector.push_back( &dialog2 );
+
+ HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT );
+}
+
+//==============================================================================
+// DialogSelectionGroup::~DialogSelectionGroup
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+DialogSelectionGroup::~DialogSelectionGroup()
+{
+}
+
+//==============================================================================
+// DialogSelectionGroup::PlayLine
+//==============================================================================
+// Description: Select a dialog if we haven't done it yet and play it.
+//
+// Parameters: lineIndex - index of dialog line to play
+// player - sound player
+// callback - callback for when the dialog line is complete
+//
+// Return: N/A.
+//
+//==============================================================================
+void DialogSelectionGroup::PlayLine( unsigned int lineIndex,
+ SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback )
+{
+ if( lineIndex == 0 )
+ {
+ makeRandomSelection();
+ }
+ else
+ {
+ rAssert( m_currentlyPlayingDialog != DIALOG_NOT_IN_USE );
+ }
+
+ m_dialogVector[m_currentlyPlayingDialog]->PlayLine( lineIndex, player, callback );
+}
+
+//=============================================================================
+// DialogSelectionGroup::QueueLine
+//=============================================================================
+// Description: Select a dialog if we haven't done it yet and queue it for
+// playback
+//
+// Parameters: lineIndex - index of dialog line to play
+// player - sound player
+//
+// Return: void
+//
+//=============================================================================
+void DialogSelectionGroup::QueueLine( unsigned int lineIndex, SimpsonsSoundPlayer& player )
+{
+ if( lineIndex == 0 )
+ {
+ makeRandomSelection();
+ }
+ else
+ {
+ rAssert( m_currentlyPlayingDialog != DIALOG_NOT_IN_USE );
+ }
+
+ m_dialogVector[m_currentlyPlayingDialog]->QueueLine( lineIndex, player );
+}
+
+//=============================================================================
+// DialogSelectionGroup::PlayQueuedLine
+//=============================================================================
+// Description: Play line of dialog that we queued earlier
+//
+// Parameters: player - sound player
+// callback - callback for when the dialog line is complete
+//
+// Return: void
+//
+//=============================================================================
+void DialogSelectionGroup::PlayQueuedLine( SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback )
+{
+ m_dialogVector[m_currentlyPlayingDialog]->PlayQueuedLine( player, callback );
+}
+
+//=============================================================================
+// DialogSelectionGroup::GetNumDialogLines
+//=============================================================================
+// Description: Return the number of dialog lines in the currently selected
+// SelectableDialog object
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+unsigned int DialogSelectionGroup::GetNumDialogLines() const
+{
+ if( m_currentlyPlayingDialog == DIALOG_NOT_IN_USE )
+ {
+ //
+ // Assume shortest case
+ //
+ return( 1 );
+ }
+ else
+ {
+ return( m_dialogVector[m_currentlyPlayingDialog]->GetNumDialogLines() );
+ }
+}
+
+//=============================================================================
+// DialogSelectionGroup::UsesCharacter
+//=============================================================================
+// Description: Indicate if any of the dialogs use the given character
+//
+// Parameters: characterObj - character to match
+//
+// Return: true if character used, false otherwise
+//
+//=============================================================================
+bool DialogSelectionGroup::UsesCharacter( tUID characterUID )
+{
+ if( m_dialogVector.empty() )
+ {
+ return( false );
+ }
+ else
+ {
+ return( m_dialogVector[0]->UsesCharacter( characterUID ) );
+ }
+}
+
+//=============================================================================
+// DialogSelectionGroup::IsVillainLine
+//=============================================================================
+// Description: Indicate if the dialogs are villain
+//
+// Parameters: None
+//
+// Return: true if villain, false otherwise
+//
+//=============================================================================
+bool DialogSelectionGroup::IsVillainLine()
+{
+ if( m_dialogVector.empty() )
+ {
+ return( false );
+ }
+ else
+ {
+ return( m_dialogVector[0]->IsVillainLine() );
+ }
+}
+
+//=============================================================================
+// DialogSelectionGroup::GetDialogLineCharacterUID
+//=============================================================================
+// Description: Indicate which character is speaking with the given line
+//
+// Parameters: lineNum - indicates which dialog line we want the UID for
+//
+// Return: tUID of character associated with dialog line, zero if we
+// haven't selected a character
+//
+//=============================================================================
+tUID DialogSelectionGroup::GetDialogLineCharacterUID( unsigned int lineNum )
+{
+ if( m_currentlyPlayingDialog == DIALOG_NOT_IN_USE )
+ {
+ return( m_dialogVector[0]->GetDialogLineCharacterUID( lineNum ) );
+ }
+ else
+ {
+ return( m_dialogVector[m_currentlyPlayingDialog]->GetDialogLineCharacterUID( lineNum ) );
+ }
+}
+
+//=============================================================================
+// DialogSelectionGroup::AddMatchingDialog
+//=============================================================================
+// Description: Push given dialog onto list
+//
+// Parameters: newDialog - dialog to add
+//
+// Return: void
+//
+//=============================================================================
+void DialogSelectionGroup::AddMatchingDialog( SelectableDialog& newDialog,
+ SelectableDialogList& list )
+{
+ HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT );
+ m_dialogVector.push_back( &newDialog );
+ HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT );
+}
+
+//=============================================================================
+// DialogSelectionGroup::GetEvent
+//=============================================================================
+// Description: Returns event associated with this group, which we'll get
+// from a dialog in the list.
+//
+// Parameters: None
+//
+// Return: EventEnum representing event
+//
+//=============================================================================
+EventEnum DialogSelectionGroup::GetEvent() const
+{
+ rAssert( m_dialogVector.size() > 0 );
+
+ return( m_dialogVector[0]->GetEvent() );
+}
+
+//=============================================================================
+// DialogSelectionGroup::GetLevel
+//=============================================================================
+// Description: Returns level associated with this group, which we'll get
+// from a dialog in the list.
+//
+// Parameters: None
+//
+// Return: index of level if one exists, NO_LEVEL otherwise
+//
+//=============================================================================
+unsigned int DialogSelectionGroup::GetLevel() const
+{
+ rAssert( m_dialogVector.size() > 0 );
+
+ return( m_dialogVector[0]->GetLevel() );
+}
+
+//=============================================================================
+// DialogSelectionGroup::GetMission
+//=============================================================================
+// Description: Returns event associated with this group, which we'll get
+// from a dialog in the list.
+//
+// Parameters: None
+//
+// Return: index of mission if one exists, NO_MISSION otherwise
+//
+//=============================================================================
+unsigned int DialogSelectionGroup::GetMission() const
+{
+ rAssert( m_dialogVector.size() > 0 );
+
+ return( m_dialogVector[0]->GetMission() );
+}
+
+//=============================================================================
+// DialogSelectionGroup::GetConversationName
+//=============================================================================
+// Description: Not relevant in this class.
+//
+// Parameters: None
+//
+// Return: Zero, indicating we aren't a conversation
+//
+//=============================================================================
+radKey32 DialogSelectionGroup::GetConversationName()
+{
+ return( 0 );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// DialogSelectionGroup::makeRandomSelection
+//=============================================================================
+// Description: Select dialog to play
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void DialogSelectionGroup::makeRandomSelection()
+{
+ if( m_lastSelection == DIALOG_NOT_YET_USED )
+ {
+ //
+ // No real need to bother with true pseudo-randomization, just
+ // use the clock
+ //
+ m_lastSelection = static_cast<short>( radTimeGetMilliseconds() % m_dialogVector.size() );
+ }
+
+ m_currentlyPlayingDialog = m_lastSelection;
+ m_lastSelection = ( m_lastSelection + 1 ) % m_dialogVector.size();
+}
+
+#undef DONTCHECKVECTORRESIZING \ No newline at end of file
diff --git a/game/code/sound/dialog/dialogselectiongroup.h b/game/code/sound/dialog/dialogselectiongroup.h
new file mode 100644
index 0000000..6da0d17
--- /dev/null
+++ b/game/code/sound/dialog/dialogselectiongroup.h
@@ -0,0 +1,91 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogselectiongroup.h
+//
+// Description: Represents a set of dialog lines or conversations that can
+// be randomly selected without affecting gameplay. For example,
+// if Homer had three generic lines for playback whenever he is
+// hit by a car, and all three can be substituted for each other
+// freely, then those lines would be grouped by a DialogSelectionGroup
+// object.
+//
+// History: 02/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef DIALOGSELECTIONGROUP_H
+#define DIALOGSELECTIONGROUP_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <vector>
+
+#include <sound/dialog/selectabledialog.h>
+
+//========================================
+// Forward References
+//========================================
+class SimpsonsSoundPlayer;
+struct SimpsonsSoundPlayerCallback;
+
+//=============================================================================
+//
+// Synopsis: DialogSelectionGroup
+//
+//=============================================================================
+
+class DialogSelectionGroup : public SelectableDialog
+{
+ public:
+ DialogSelectionGroup( SelectableDialog& dialog1, SelectableDialog& dialog2 );
+ virtual ~DialogSelectionGroup();
+
+ void PlayLine( unsigned int lineIndex,
+ SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback );
+ void QueueLine( unsigned int lineIndex,
+ SimpsonsSoundPlayer& player );
+ void PlayQueuedLine( SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback );
+
+ unsigned int GetNumDialogLines() const;
+
+ bool UsesCharacter( tUID characterUID );
+ tUID GetDialogLineCharacterUID( unsigned int lineNum );
+ radKey32 GetConversationName();
+ bool IsVillainLine();
+
+ void AddMatchingDialog( SelectableDialog& newDialog, SelectableDialogList& list );
+
+ //
+ // Overriding SelectableDialog methods
+ //
+ unsigned int GetMission() const;
+ unsigned int GetLevel() const;
+ EventEnum GetEvent() const;
+
+ private:
+ //Prevent wasteful constructor creation.
+ DialogSelectionGroup();
+ DialogSelectionGroup( const DialogSelectionGroup& original );
+ DialogSelectionGroup& operator=( const DialogSelectionGroup& rhs );
+
+ void makeRandomSelection();
+
+ //
+ // List of SelectableDialog objects to choose from. Use std::vector
+ // for space conservation
+ //
+ typedef std::vector< SelectableDialog*, s2alloc<SelectableDialog*> > DialogVector;
+
+ DialogVector m_dialogVector;
+
+ short m_currentlyPlayingDialog;
+ short m_lastSelection;
+};
+
+
+#endif // DIALOGSELECTIONGROUP_H
+
diff --git a/game/code/sound/dialog/dialogsounddebugpage.cpp b/game/code/sound/dialog/dialogsounddebugpage.cpp
new file mode 100644
index 0000000..1823422
--- /dev/null
+++ b/game/code/sound/dialog/dialogsounddebugpage.cpp
@@ -0,0 +1,243 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogsounddebugpage.cpp
+//
+// Description: Implements a class for displaying dialog-related debug info
+// on the screen, triggered using Watcher
+//
+// History: 1/20/2003 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/dialog/dialogsounddebugpage.h>
+
+#include <sound/dialog/dialogline.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// DialogSoundDebugPage::DialogSoundDebugPage
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+DialogSoundDebugPage::DialogSoundDebugPage( unsigned int pageNum, SoundDebugDisplay* master ) :
+ SoundDebugPage( pageNum, master )
+{
+ unsigned int i;
+
+ //
+ // Mark queue entries as empty by setting event ID to NUM_EVENTS
+ //
+ for( i = 0; i < MAX_QUEUE_SIZE; i++ )
+ {
+ m_queueData[i].eventID = NUM_EVENTS;
+ }
+}
+
+//=============================================================================
+// DialogSoundDebugPage::~DialogSoundDebugPage
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+DialogSoundDebugPage::~DialogSoundDebugPage()
+{
+}
+
+//=============================================================================
+// DialogSoundDebugPage::SetQueueLength
+//=============================================================================
+// Description: Given queue length, clear out entries that are no longer in
+// the queue
+//
+// Parameters: size - current size of queue
+//
+// Return: void
+//
+//=============================================================================
+void DialogSoundDebugPage::SetQueueLength( unsigned int size )
+{
+ unsigned int i;
+
+ for( i = size; i < MAX_QUEUE_SIZE; i++ )
+ {
+ m_queueData[i].eventID = NUM_EVENTS;
+ }
+}
+
+//=============================================================================
+// DialogSoundDebugPage::SetQueueEntry
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int position, EventEnum event, unsigned int mission, unsigned int level, tUID theCharacter, DialogPriority priority, unsigned int msecsRemaining )
+//
+// Return: void
+//
+//=============================================================================
+void DialogSoundDebugPage::SetQueueEntry( unsigned int position,
+ EventEnum event,
+ unsigned int mission,
+ unsigned int level,
+ tUID theCharacter,
+ DialogPriority priority,
+ unsigned int msecsRemaining )
+{
+ QueueInfo* data;
+
+ if( position >= MAX_QUEUE_SIZE )
+ {
+ //
+ // Don't bother displaying too much stuff
+ //
+ return;
+ }
+
+ data = &m_queueData[position];
+ data->eventID = event;
+ data->level = level;
+ data->mission = mission;
+ data->theCharacter = theCharacter;
+ data->priority = priority;
+ data->msecsRemaining = msecsRemaining;
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// DialogSoundDebugPage::fillLineBuffer
+//=============================================================================
+// Description: Fill the given buffer with text to display on the screen
+// at the given line
+//
+// Parameters: lineNum - line number on screen where buffer will be displayed
+// buffer - to be filled in with text to display
+//
+// Return: void
+//
+//=============================================================================
+void DialogSoundDebugPage::fillLineBuffer( int lineNum, char* buffer )
+{
+ switch( lineNum )
+ {
+ case 0:
+ strcpy( buffer, "Now playing:" );
+ break;
+
+ case 1:
+ fillQueueText( buffer, 0 );
+ break;
+
+ case 3:
+ strcpy( buffer, "Queued:" );
+ break;
+
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ case 12:
+ fillQueueText( buffer, lineNum - 3 );
+ break;
+
+ default:
+ buffer[0] = '\0';
+ break;
+ }
+}
+
+//=============================================================================
+// DialogSoundDebugPage::getNumLines
+//=============================================================================
+// Description: Returns number of lines that we'll display on screen
+//
+// Parameters: None
+//
+// Return: Line count
+//
+//=============================================================================
+int DialogSoundDebugPage::getNumLines()
+{
+ return( 13 );
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// DialogSoundDebugPage::fillQueueText
+//=============================================================================
+// Description: Fill the text buffer with information about one particular
+// dialog queue entry
+//
+// Parameters: buffer - text buffer to fill
+// index - index of m_queueData entry to fill out with
+//
+// Return: void
+//
+//=============================================================================
+void DialogSoundDebugPage::fillQueueText( char* buffer, unsigned int index )
+{
+ QueueInfo* info;
+ char eventName[30];
+ char characterName[10];
+
+ rAssert( index >= 0 );
+ rAssert( index <= MAX_QUEUE_SIZE );
+ rAssert( buffer != NULL );
+
+ info = &m_queueData[index];
+ if( info->eventID != NUM_EVENTS )
+ {
+ DialogLine::FillEventName( eventName, 30, info->eventID );
+ DialogLine::FillCharacterName( characterName, 10, info->theCharacter );
+ sprintf( buffer, "Event: %s Char: %s Pri: %d L%dM%d",
+ eventName,
+ characterName,
+ info->priority,
+ info->level,
+ info->mission );
+ }
+ else
+ {
+ buffer[0] = '\0';
+ }
+}
diff --git a/game/code/sound/dialog/dialogsounddebugpage.h b/game/code/sound/dialog/dialogsounddebugpage.h
new file mode 100644
index 0000000..9fb0652
--- /dev/null
+++ b/game/code/sound/dialog/dialogsounddebugpage.h
@@ -0,0 +1,89 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dialogsounddebugpage.h
+//
+// Description: Declares a class for displaying dialog-related debug info
+// on the screen, triggered using Watcher
+//
+// History: 1/20/2003 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef DIALOGSOUNDDEBUGPAGE_H
+#define DIALOGSOUNDDEBUGPAGE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/sounddebug/sounddebugpage.h>
+
+#include <events/eventenum.h>
+#include <sound/dialog/dialogqueueelement.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: DialogSoundDebugPage
+//
+//=============================================================================
+
+class DialogSoundDebugPage : public SoundDebugPage
+{
+ public:
+ DialogSoundDebugPage( unsigned int pageNum, SoundDebugDisplay* master );
+ virtual ~DialogSoundDebugPage();
+
+ void SetQueueLength( unsigned int size );
+
+ void SetQueueEntry( unsigned int position,
+ EventEnum event,
+ unsigned int mission,
+ unsigned int level,
+ tUID theCharacter,
+ DialogPriority priority,
+ unsigned int msecsRemaining );
+
+ protected:
+ //
+ // Pure virtual functions from SoundDebugPage
+ //
+ void fillLineBuffer( int lineNum, char* buffer );
+ int getNumLines();
+
+ private:
+ //Prevent wasteful constructor creation.
+ DialogSoundDebugPage();
+ DialogSoundDebugPage( const DialogSoundDebugPage& dialogsounddebugpage );
+ DialogSoundDebugPage& operator=( const DialogSoundDebugPage& dialogsounddebugpage );
+
+ void fillQueueText( char* buffer, unsigned int index );
+
+ //
+ // Structure for holding queue information
+ //
+ struct QueueInfo
+ {
+ EventEnum eventID;
+ unsigned int mission;
+ unsigned int level;
+ tUID theCharacter;
+ DialogPriority priority;
+ unsigned int msecsRemaining;
+ };
+
+ static const unsigned int MAX_QUEUE_SIZE = 10;
+
+ QueueInfo m_queueData[MAX_QUEUE_SIZE];
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //DIALOGSOUNDDEBUGPAGE_H
diff --git a/game/code/sound/dialog/playabledialog.cpp b/game/code/sound/dialog/playabledialog.cpp
new file mode 100644
index 0000000..c251354
--- /dev/null
+++ b/game/code/sound/dialog/playabledialog.cpp
@@ -0,0 +1,87 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: playabledialog.cpp
+//
+// Description: Abstract base class for the basic unit of dialog playback.
+// A PlayableDialog object represents a piece of dialog which
+// is to be played without interruption (unless it gets booted
+// by a higher-priority PlayableDialog).
+//
+// History: 02/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/dialog/playabledialog.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// PlayableDialog::PlayableDialog
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PlayableDialog::PlayableDialog()
+{
+}
+
+//==============================================================================
+// PlayableDialog::PlayableDialog
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: mission - mission number for dialog
+// level - level number
+// event - event that this dialog is played in response to
+//
+// Return: N/A.
+//
+//==============================================================================
+PlayableDialog::PlayableDialog( unsigned int level, unsigned int mission, EventEnum event ) :
+ SelectableDialog( level, mission, event )
+{
+}
+
+//==============================================================================
+// PlayableDialog::~PlayableDialog
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PlayableDialog::~PlayableDialog()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/sound/dialog/playabledialog.h b/game/code/sound/dialog/playabledialog.h
new file mode 100644
index 0000000..5c7f16b
--- /dev/null
+++ b/game/code/sound/dialog/playabledialog.h
@@ -0,0 +1,48 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: playabledialog.h
+//
+// Description: Abstract base class for the basic unit of dialog playback.
+// A PlayableDialog object represents a piece of dialog which
+// is to be played without interruption (unless it gets booted
+// by a higher-priority PlayableDialog).
+//
+// History: 02/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef PLAYABLEDIALOG_H
+#define PLAYABLEDIALOG_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/dialog/selectabledialog.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: PlayableDialog
+//
+//=============================================================================
+
+class PlayableDialog : public SelectableDialog
+{
+ public:
+ PlayableDialog();
+ PlayableDialog( unsigned int level, unsigned int mission, EventEnum event );
+ virtual ~PlayableDialog();
+
+ private:
+ //Prevent wasteful constructor creation.
+ PlayableDialog( const PlayableDialog& original );
+ PlayableDialog& operator=( const PlayableDialog& rhs );
+};
+
+
+#endif // PLAYABLEDIALOG_H
+
diff --git a/game/code/sound/dialog/selectabledialog.cpp b/game/code/sound/dialog/selectabledialog.cpp
new file mode 100644
index 0000000..c71fe42
--- /dev/null
+++ b/game/code/sound/dialog/selectabledialog.cpp
@@ -0,0 +1,214 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: selectabledialog.cpp
+//
+// Description: Abstract base class for groups of one or more lines of dialog,
+// any of which can be selected randomly in response to a single
+// sound event.
+//
+// History: 02/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/dialog/selectabledialog.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SelectableDialog::SelectableDialog
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SelectableDialog::SelectableDialog() :
+ m_missionNum( NO_MISSION ),
+ m_levelNum( NO_LEVEL ),
+ m_event( NUM_EVENTS ),
+#ifndef RAD_RELEASE
+ m_hasBeenPlayed( false ),
+#endif
+ m_nextListObject( NULL )
+{
+}
+
+//==============================================================================
+// SelectableDialog::SelectableDialog
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: mission - mission number for dialog
+// level - level number
+// event - event that this dialog is played in response to
+//
+// Return: N/A.
+//
+//==============================================================================
+SelectableDialog::SelectableDialog( unsigned int level, unsigned int mission, EventEnum event ) :
+ m_missionNum( mission ),
+ m_levelNum( level ),
+ m_event( event ),
+#ifndef RAD_RELEASE
+ m_hasBeenPlayed( false ),
+#endif
+ m_nextListObject( NULL )
+{
+}
+
+//==============================================================================
+// SelectableDialog::~SelectableDialog
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SelectableDialog::~SelectableDialog()
+{
+}
+
+//=============================================================================
+// SelectableDialog::AddToDialogList
+//=============================================================================
+// Description: Place this object on the head of the list supplied
+//
+// Parameters: list - list to add to
+//
+// Return: void
+//
+//=============================================================================
+void SelectableDialog::AddToDialogList( SelectableDialog** list )
+{
+ m_nextListObject = *list;
+ *list = this;
+}
+
+//=============================================================================
+// SelectableDialog::AddToDialogList
+//=============================================================================
+// Description: Place this object in the list after the object supplied
+//
+// Parameters: listObj - object to add after
+//
+// Return: void
+//
+//=============================================================================
+void SelectableDialog::AddToDialogList( SelectableDialog* listObj )
+{
+ m_nextListObject = listObj->m_nextListObject;
+ listObj->m_nextListObject = this;
+}
+
+//=============================================================================
+// SelectableDialog::RemoveNextFromList
+//=============================================================================
+// Description: Pull out the object after this one in the list
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SelectableDialog::RemoveNextFromList()
+{
+ rAssert( m_nextListObject != NULL );
+ m_nextListObject = m_nextListObject->m_nextListObject;
+}
+
+//=============================================================================
+// SelectableDialog::GetMission
+//=============================================================================
+// Description: Returns mission number associated with this dialog
+//
+// Parameters: None
+//
+// Return: unsigned int with the mission number, NO_MISSION for no mission
+//
+//=============================================================================
+unsigned int SelectableDialog::GetMission() const
+{
+ return( m_missionNum );
+}
+
+//=============================================================================
+// SelectableDialog::GetLevel
+//=============================================================================
+// Description: Returns level number associated with this dialog
+//
+// Parameters: None
+//
+// Return: unsigned int with the level number, NO_LEVEL for no level
+//
+//=============================================================================
+unsigned int SelectableDialog::GetLevel() const
+{
+ return( m_levelNum );
+}
+
+//=============================================================================
+// SelectableDialog::GetEvent
+//=============================================================================
+// Description: Returns event associated with this dialog
+//
+// Parameters: None
+//
+// Return: EventEnum
+//
+//=============================================================================
+EventEnum SelectableDialog::GetEvent() const
+{
+ return( m_event );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+#ifndef RAD_RELEASE
+
+void SelectableDialog::MarkAsPlayed()
+{
+ m_hasBeenPlayed = true;
+}
+
+void SelectableDialog::PrintPlayedStatus()
+{
+ if( m_hasBeenPlayed )
+ {
+ rTuneString( "Played\n" );
+ }
+ else
+ {
+ rTuneString( "NOT PLAYED\n" );
+ }
+}
+
+#endif
diff --git a/game/code/sound/dialog/selectabledialog.h b/game/code/sound/dialog/selectabledialog.h
new file mode 100644
index 0000000..c26125c
--- /dev/null
+++ b/game/code/sound/dialog/selectabledialog.h
@@ -0,0 +1,118 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: selectabledialog.h
+//
+// Description: Abstract base class for groups of one or more lines of dialog,
+// any of which can be selected randomly in response to a single
+// sound event.
+//
+// History: 02/09/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SELECTABLEDIALOG_H
+#define SELECTABLEDIALOG_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <events/eventenum.h>
+#include <worldsim/character/character.h>
+#include <sound/dialog/selectabledialoglist.h>
+
+//========================================
+// Forward References
+//========================================
+class SimpsonsSoundPlayer;
+struct SimpsonsSoundPlayerCallback;
+
+//=============================================================================
+//
+// Synopsis: SelectableDialog
+//
+//=============================================================================
+
+class SelectableDialog
+{
+ public:
+ SelectableDialog();
+ SelectableDialog( unsigned int level, unsigned int mission, EventEnum event );
+ virtual ~SelectableDialog();
+
+ static const int NO_MISSION = 0;
+ static const int NO_LEVEL = 0;
+
+ bool IsLevelSpecific() { return( m_levelNum != NO_LEVEL ); }
+
+ virtual unsigned int GetMission() const;
+ virtual unsigned int GetLevel() const;
+ virtual EventEnum GetEvent() const;
+
+ virtual bool UsesCharacter( Character* characterObj )
+ { return( UsesCharacter( characterObj->GetUID() ) ); }
+ virtual bool UsesCharacter( tUID characterUID ) = 0;
+
+ virtual tUID GetDialogLineCharacterUID( unsigned int lineNum ) = 0;
+
+ virtual bool IsVillainLine() = 0;
+
+ void AddToDialogList( SelectableDialog** list );
+ void AddToDialogList( SelectableDialog* listObj );
+ SelectableDialog* GetNextInList() const { return( m_nextListObject ); }
+ void RemoveNextFromList();
+
+ virtual void AddMatchingDialog( SelectableDialog& newDialog, SelectableDialogList& list ) = 0;
+
+ virtual void PlayLine( unsigned int lineIndex,
+ SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback ) = 0;
+ virtual void QueueLine( unsigned int lineIndex,
+ SimpsonsSoundPlayer& player ) = 0;
+ virtual void PlayQueuedLine( SimpsonsSoundPlayer& player,
+ SimpsonsSoundPlayerCallback* callback ) = 0;
+
+ virtual unsigned int GetNumDialogLines() const = 0;
+
+ virtual radKey32 GetConversationName() = 0;
+
+#ifndef RAD_RELEASE
+ void MarkAsPlayed();
+ void PrintPlayedStatus();
+#endif
+
+ protected:
+ //
+ // Level, mission, and event values. Since these aren't really used
+ // in DialogSelectionGroup and Conversation objects, they should
+ // probably be separated out of this object someday if we want to
+ // save a few bytes and clean up the design a bit.
+ //
+
+ //
+ // Level and mission number for this line. Set to NO_MISSION or
+ // NO_LEVEL if not applicable.
+ //
+ int m_missionNum;
+ int m_levelNum;
+
+ //
+ // Event that this dialog is played in response to
+ //
+ EventEnum m_event;
+
+ private:
+ //Prevent wasteful constructor creation.
+ SelectableDialog( const SelectableDialog& original );
+ SelectableDialog& operator=( const SelectableDialog& rhs );
+
+#ifndef RAD_RELEASE
+ bool m_hasBeenPlayed;
+#endif
+
+ SelectableDialog* m_nextListObject;
+};
+
+
+#endif // SELECTABLEDIALOG_H
+
diff --git a/game/code/sound/dialog/selectabledialoglist.h b/game/code/sound/dialog/selectabledialoglist.h
new file mode 100644
index 0000000..ddd5572
--- /dev/null
+++ b/game/code/sound/dialog/selectabledialoglist.h
@@ -0,0 +1,24 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: selectabledialoglist.h
+//
+// Description: Typedef for STL list of selectable dialogs. Removed from
+// DialogList to avoid unnecessary dependency.
+//
+// History: 9/25/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SELECTABLEDIALOGLIST_H
+#define SELECTABLEDIALOGLIST_H
+
+class SelectableDialog;
+
+#include <list>
+#include <memory/stlallocators.h>
+
+typedef std::list< SelectableDialog*, s2alloc<SelectableDialog*> > SelectableDialogList;
+
+#endif // SELECTABLEDIALOGLIST_H
+
diff --git a/game/code/sound/listener.cpp b/game/code/sound/listener.cpp
new file mode 100644
index 0000000..559cb11
--- /dev/null
+++ b/game/code/sound/listener.cpp
@@ -0,0 +1,236 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: listener.cpp
+//
+// Description: Implement Listener class, which tracks the camera and
+// updates the RadSound listener position/orientation to match
+//
+// History: 02/08/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radmath/radmath.hpp>
+#include <radsoundmath.hpp>
+#include <radsound_hal.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/vectorcamera.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/listener.h>
+
+#include <sound/soundrenderer/soundrenderingmanager.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercam.h>
+#include <worldsim/avatarmanager.h>
+#include <mission/gameplaymanager.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+static const float MAX_LISTENER_DIST_FROM_AVATAR = 10.0f;
+static const float MAX_LISTENER_DIST_FROM_AVATAR_SQR = 100.0f;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Listener::Listener
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Listener::Listener() :
+ m_theListener( NULL ),
+ m_feCamera( NULL )
+{
+}
+
+//==============================================================================
+// Listener::~Listener
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Listener::~Listener()
+{
+}
+
+//=============================================================================
+// Listener::Initialize
+//=============================================================================
+// Description: Get a handle to the RadSound listener
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void Listener::Initialize( Sound::daSoundRenderingManager& renderMgr )
+{
+ m_theListener = renderMgr.GetTheListener();
+}
+
+//=============================================================================
+// Listener::Update
+//=============================================================================
+// Description: Update the listener's position/orientation
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void Listener::Update( ContextEnum context )
+{
+ SuperCamCentral* scc;
+ SuperCam* camera;
+ rmt::Vector position;
+ rmt::Vector velocity;
+ rmt::Vector heading;
+ rmt::Vector up;
+ radSoundVector soundVector1;
+ radSoundVector soundVector2;
+ rmt::Vector diff;
+ Avatar* theAvatar;
+ rmt::Vector avatarPosn;
+
+
+ //
+ // Don't do anything if we don't have a listener yet
+ //
+ if( m_theListener == NULL )
+ {
+ return;
+ }
+
+ //
+ // Get the camera positioning and pass it on to the listener.
+ // QUESTION: is it valid to assume that we'll strictly use player 1?
+ // H2H positional sound hasn't really been resolved...
+ //
+ if( context == CONTEXT_FRONTEND )
+ {
+ if( m_feCamera == NULL )
+ {
+ m_feCamera = p3d::find<tVectorCamera>( "FE_Camera" );
+ if( m_feCamera == NULL )
+ {
+ //
+ // Camera hasn't been loaded yet, I guess. Early exit.
+ //
+ return;
+ }
+ }
+
+ m_feCamera->GetPosition( &position );
+ m_feCamera->GetDirection( &heading );
+ m_feCamera->GetUpVector( &up );
+ //
+ // This class doesn't provide velocities, assume zero.
+ //
+ velocity.Clear();
+ }
+ else
+ {
+ m_feCamera = NULL;
+
+ scc = GetSuperCamManager()->GetSCC( 0 );
+ rAssert( scc != NULL );
+
+ camera = scc->GetActiveSuperCam();
+ if( camera == NULL )
+ {
+ //
+ // No camera, nothing to update to.
+ //
+ return;
+ }
+
+ camera->GetPosition( &position );
+ camera->GetVelocity( &velocity );
+ camera->GetHeading( &heading );
+ heading.NormalizeSafe();
+ //
+ // Sadly, the camera defaults allow for a nonsense zero-length
+ // heading vector. Test for this case, and make up a more
+ // lenient default if found
+ //
+ if( ( heading.x == 0.0f ) && ( heading.y == 0.0f ) && ( heading.z == 0.0f ) )
+ {
+ heading.x = 1.0f;
+ }
+ camera->GetCameraUp( &up );
+ up.NormalizeSafe();
+
+ //
+ // We don't want the position to get too far away from the character, so clamp
+ // the distance to some arbitrary maximum
+ //
+ theAvatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ if( ( theAvatar != NULL )
+ && ( GetGameplayManager() != NULL )
+ && ( !(GetGameplayManager()->IsSuperSprint()) ) )
+ {
+ theAvatar->GetPosition( avatarPosn );
+ diff = position - avatarPosn;
+ if( diff.MagnitudeSqr() > MAX_LISTENER_DIST_FROM_AVATAR_SQR )
+ {
+ diff.Normalize();
+ diff.Scale( MAX_LISTENER_DIST_FROM_AVATAR );
+ position.Add( avatarPosn, diff );
+ }
+ }
+ }
+
+ // Position
+ soundVector1.SetElements( position.x, position.y, position.z );
+ m_theListener->SetPosition( &soundVector1 );
+
+ // Velocity
+ soundVector1.SetElements( velocity.x, velocity.y, velocity.z );
+ m_theListener->SetVelocity( &soundVector1 );
+
+ // Orientation
+ soundVector1.SetElements( heading.x, heading.y, heading.z );
+ soundVector2.SetElements( up.x, up.y, up.z );
+
+ //
+ // Double-check the values, I'm not sure that DirectSound likes
+ // zero vectors
+ //
+ rAssert( ( heading.x != 0.0f )
+ || ( heading.y != 0.0f )
+ || ( heading.z != 0.0f ) );
+ rAssert( ( up.x != 0.0f ) || ( up.y != 0.0f ) || ( up.z != 0.0f ) );
+
+ m_theListener->SetOrientation( &soundVector1, &soundVector2 );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/sound/listener.h b/game/code/sound/listener.h
new file mode 100644
index 0000000..d41a86b
--- /dev/null
+++ b/game/code/sound/listener.h
@@ -0,0 +1,56 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: listener.h
+//
+// Description: Declaration of Listener class, which tracks the camera and
+// updates the RadSound listener position/orientation to match
+//
+// History: 02/08/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef LISTENER_H
+#define LISTENER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <contexts/contextenum.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+
+//========================================
+// Forward References
+//========================================
+
+struct IRadSoundHalListener;
+class tVectorCamera;
+
+//=============================================================================
+//
+// Synopsis: Listener
+//
+//=============================================================================
+
+class Listener
+{
+ public:
+ Listener();
+ virtual ~Listener();
+
+ void Initialize( Sound::daSoundRenderingManager& renderMgr );
+ void Update( ContextEnum context );
+
+ private:
+ //Prevent wasteful constructor creation.
+ Listener( const Listener& original );
+ Listener& operator=( const Listener& rhs );
+
+ IRadSoundHalListener* m_theListener;
+
+ tVectorCamera* m_feCamera;
+};
+
+
+#endif // LISTENER_H
+
diff --git a/game/code/sound/movingpositional/actorplayer.cpp b/game/code/sound/movingpositional/actorplayer.cpp
new file mode 100644
index 0000000..2ddac62
--- /dev/null
+++ b/game/code/sound/movingpositional/actorplayer.cpp
@@ -0,0 +1,194 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: actorplayer.cpp
+//
+// Description: Implement ActorPlayer
+//
+// History: 3/10/2003 + Created -- Esan
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/movingpositional/actorplayer.h>
+
+#include <ai/actor/actor.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ActorPlayer::ActorPlayer
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ActorPlayer::ActorPlayer() :
+ m_actor( NULL )
+{
+}
+
+//=============================================================================
+// ActorPlayer::~ActorPlayer
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ActorPlayer::~ActorPlayer()
+{
+}
+
+//=============================================================================
+// ActorPlayer::GetPosition
+//=============================================================================
+// Description: Get position of actor for moving positional sound
+//
+// Parameters: position - output parameter, to be filled with actor position
+//
+// Return: void
+//
+//=============================================================================
+void ActorPlayer::GetPosition( radSoundVector& position )
+{
+ rmt::Vector actorPosn;
+
+ m_actor->GetPosition( &actorPosn );
+ position.SetElements( actorPosn.x, actorPosn.y, actorPosn.z );
+}
+
+//=============================================================================
+// ActorPlayer::GetVelocity
+//=============================================================================
+// Description: Get velocity of actor for moving positional sound
+//
+// Parameters: velocity - output parameter, to be filled with actor velocity
+//
+// Return: void
+//
+//=============================================================================
+void ActorPlayer::GetVelocity( radSoundVector& velocity )
+{
+ //
+ // No doppler for actors, therefore make velocity zero
+ //
+ velocity.SetElements( 0.0f, 0.0f, 0.0f );
+}
+
+//=============================================================================
+// ActorPlayer::ServiceOncePerFrame
+//=============================================================================
+// Description: Service stuff
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void ActorPlayer::ServiceOncePerFrame()
+{
+ m_player.ServiceOncePerFrame();
+}
+
+//=============================================================================
+// ActorPlayer::OnPlaybackComplete
+//=============================================================================
+// Description: Sound player callback. Do nothing, leave for subclasses
+// to override
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void ActorPlayer::OnPlaybackComplete()
+{
+}
+
+//=============================================================================
+// ActorPlayer::OnSoundReady
+//=============================================================================
+// Description: Sound player callback. Do nothing, leave for subclasses
+// to override
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void ActorPlayer::OnSoundReady()
+{
+}
+
+//=============================================================================
+// ActorPlayer::playSound
+//=============================================================================
+// Description: Comment
+//
+// Parameters: settings - positional sound attributes
+// resourceName - name of sound resource to play
+// theActor - the moving object that this sound is associated with
+//
+// Return: void
+//
+//=============================================================================
+void ActorPlayer::playSound( positionalSoundSettings* settings,
+ const char* resourceName,
+ Actor* theActor )
+{
+ rmt::Vector posn;
+
+ m_actor = theActor;
+
+ m_player.SetPositionCarrier( *this );
+ m_player.SetParameters( settings );
+
+ theActor->GetPosition( &posn );
+ m_player.SetPosition( posn.x, posn.y, posn.z );
+
+ m_player.PlaySound( resourceName, this );
+}
+
+//=============================================================================
+// ActorPlayer::deactivate
+//=============================================================================
+// Description: Shut down the sound and disassociate from the actor
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void ActorPlayer::deactivate()
+{
+ m_actor = NULL;
+ m_player.Stop();
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/sound/movingpositional/actorplayer.h b/game/code/sound/movingpositional/actorplayer.h
new file mode 100644
index 0000000..2731d6c
--- /dev/null
+++ b/game/code/sound/movingpositional/actorplayer.h
@@ -0,0 +1,77 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: actorplayer.h
+//
+// Description: Abstract base class for playing sounds associated with moving
+// state props (e.g. wasps)
+//
+// History: 3/10/2003 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef ACTORPLAYER_H
+#define ACTORPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/positionalsoundplayer.h>
+
+//========================================
+// Forward References
+//========================================
+class Actor;
+
+//=============================================================================
+//
+// Synopsis: ActorPlayer
+//
+//=============================================================================
+
+class ActorPlayer : public PositionCarrier,
+ public SimpsonsSoundPlayerCallback
+{
+public:
+ ActorPlayer();
+ virtual ~ActorPlayer();
+
+ bool IsActive() { return( m_actor != NULL ); }
+ virtual void Activate( Actor* theActor ) = 0;
+
+ virtual void ServiceOncePerFrame();
+
+ //
+ // PositionCarrier functions
+ //
+ void GetPosition( radSoundVector& position );
+ void GetVelocity( radSoundVector& velocity );
+
+ //
+ // SimpsonsSoundPlayerCallback functions
+ //
+ virtual void OnSoundReady();
+ virtual void OnPlaybackComplete();
+
+protected:
+ void playSound( positionalSoundSettings* settings, const char* resourceName, Actor* theActor );
+
+ virtual void deactivate();
+
+ Actor* m_actor;
+
+ PositionalSoundPlayer m_player;
+
+private:
+ //Prevent wasteful constructor creation.
+ ActorPlayer( const ActorPlayer& actorplayer );
+ ActorPlayer& operator=( const ActorPlayer& actorplayer );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //ACTORPLAYER_H
diff --git a/game/code/sound/movingpositional/aivehiclesoundplayer.cpp b/game/code/sound/movingpositional/aivehiclesoundplayer.cpp
new file mode 100644
index 0000000..3fc866f
--- /dev/null
+++ b/game/code/sound/movingpositional/aivehiclesoundplayer.cpp
@@ -0,0 +1,99 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: aivehiclesoundplayer.cpp
+//
+// Description: Administers the playing of sound for an AI-controlled vehicle
+//
+// History: 1/4/2003 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/movingpositional/aivehiclesoundplayer.h>
+
+#include <worldsim/redbrick/vehicle.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// AIVehicleSoundPlayer::AIVehicleSoundPlayer
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+AIVehicleSoundPlayer::AIVehicleSoundPlayer()
+{
+}
+
+//=============================================================================
+// AIVehicleSoundPlayer::~AIVehicleSoundPlayer
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+AIVehicleSoundPlayer::~AIVehicleSoundPlayer()
+{
+}
+
+//=============================================================================
+// AIVehicleSoundPlayer::ServiceOncePerFrame
+//=============================================================================
+// Description: Adjust the pitch for this vehicle
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void AIVehicleSoundPlayer::ServiceOncePerFrame()
+{
+ float pitch;
+
+ VehiclePositionalSoundPlayer::ServiceOncePerFrame();
+
+ //
+ // Adjust pitch for vehicle speed if desired. I'll probably need to
+ // expose this for designer tuning later.
+ //
+ if( IsActive() && m_tiePitchToVelocity )
+ {
+ pitch = 0.3f + ( 0.7f * ( m_vehicle->GetSpeedKmh() / m_vehicle->mDesignerParams.mDpTopSpeedKmh ) );
+ if( pitch > 1.2f )
+ {
+ pitch = 1.2f;
+ }
+
+ m_player.SetPitch( pitch );
+ }
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/sound/movingpositional/aivehiclesoundplayer.h b/game/code/sound/movingpositional/aivehiclesoundplayer.h
new file mode 100644
index 0000000..36e55ba
--- /dev/null
+++ b/game/code/sound/movingpositional/aivehiclesoundplayer.h
@@ -0,0 +1,53 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: aivehiclesoundplayer.h
+//
+// Description: Administers the playing of sound for an AI-controlled vehicle
+//
+// History: 1/4/2003 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef AIVEHICLESOUNDPLAYER_H
+#define AIVEHICLESOUNDPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radtime.hpp>
+
+#include <sound/movingpositional/vehicleposnsoundplayer.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+
+//=============================================================================
+//
+// Synopsis: AIVehicleSoundPlayer
+//
+//=============================================================================
+
+class AIVehicleSoundPlayer : public VehiclePositionalSoundPlayer
+{
+ public:
+ AIVehicleSoundPlayer();
+ virtual ~AIVehicleSoundPlayer();
+
+ void ServiceOncePerFrame();
+
+ private:
+ //Prevent wasteful constructor creation.
+ AIVehicleSoundPlayer( const AIVehicleSoundPlayer& aivehiclesoundplayer );
+ AIVehicleSoundPlayer& operator=( const AIVehicleSoundPlayer& aivehiclesoundplayer );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //AIVEHICLESOUNDPLAYER_H
diff --git a/game/code/sound/movingpositional/allmovingposn.cpp b/game/code/sound/movingpositional/allmovingposn.cpp
new file mode 100644
index 0000000..5c0b72b
--- /dev/null
+++ b/game/code/sound/movingpositional/allmovingposn.cpp
@@ -0,0 +1,9 @@
+#include <sound/movingpositional/movingsoundmanager.cpp>
+#include <sound/movingpositional/vehicleposnsoundplayer.cpp>
+#include <sound/movingpositional/aivehiclesoundplayer.cpp>
+#include <sound/movingpositional/trafficsoundplayer.cpp>
+#include <sound/movingpositional/avatarvehicleposnplayer.cpp>
+#include <sound/movingpositional/actorplayer.cpp>
+#include <sound/movingpositional/waspsoundplayer.cpp>
+#include <sound/movingpositional/platformsoundplayer.cpp>
+#include <sound/movingpositional/animobjsoundplayer.cpp> \ No newline at end of file
diff --git a/game/code/sound/movingpositional/animobjsoundplayer.cpp b/game/code/sound/movingpositional/animobjsoundplayer.cpp
new file mode 100644
index 0000000..a65c662
--- /dev/null
+++ b/game/code/sound/movingpositional/animobjsoundplayer.cpp
@@ -0,0 +1,202 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: animobjsoundplayer.cpp
+//
+// Description: Plays sound for AnimCollisionEntityDSG objects
+//
+// History: 6/5/2003 + Created -- Esan (post-beta, yay!)
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radnamespace.hpp>
+#include <p3d/anim/pose.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/movingpositional/animobjsoundplayer.h>
+
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/soundallocatedresource.h>
+
+#include <events/eventdata.h>
+#include <render/DSG/animcollisionentitydsg.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// AnimObjSoundPlayer::AnimObjSoundPlayer
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+AnimObjSoundPlayer::AnimObjSoundPlayer() :
+ m_joint( NULL ),
+ m_identity( NULL )
+{
+}
+
+//=============================================================================
+// AnimObjSoundPlayer::~AnimObjSoundPlayer
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+AnimObjSoundPlayer::~AnimObjSoundPlayer()
+{
+ if( m_identity != NULL )
+ {
+ m_identity->Release();
+ }
+}
+
+//=============================================================================
+// AnimObjSoundPlayer::Activate
+//=============================================================================
+// Description: Start playing a sound for a particular platform
+//
+// Parameters: soundData - position and identify info for platform
+//
+// Return: void
+//
+//=============================================================================
+void AnimObjSoundPlayer::Activate( AnimSoundDSGData* soundData )
+{
+ rmt::Vector posn;
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ positionalSoundSettings* parameters;
+
+ //
+ // Get the positionalSoundSettings object for the platform sound
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ nameSpaceObj = nameSpace->GetInstance( ::radMakeKey32( soundData->positionalSettingName ) );
+ if( nameSpaceObj != NULL )
+ {
+ parameters = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj );
+
+ m_identity = soundData->soundObject;
+ rAssert( m_identity != NULL );
+
+ m_joint = soundData->animJoint;
+ rAssert( m_joint != NULL );
+
+ m_player.SetPositionCarrier( *this );
+ m_player.SetParameters( parameters );
+
+ //
+ // Get world position of the platform through this joint
+ //
+ posn = m_joint->worldMatrix.Row( 3 );
+ m_player.SetPosition( posn.x, posn.y, posn.z );
+
+ //
+ // Don't buffer, to save IOP
+ //
+
+ m_player.PlaySound( soundData->soundName, NULL );
+ }
+ else
+ {
+ rDebugString( "Couldn't play anim DSG platform sound, no matching settings found" );
+ }
+}
+
+//=============================================================================
+// AnimObjSoundPlayer::Deactivate
+//=============================================================================
+// Description: Stop playing platform sound
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void AnimObjSoundPlayer::Deactivate()
+{
+ m_player.Stop();
+
+ m_identity = NULL;
+}
+
+//=============================================================================
+// AnimObjSoundPlayer::ServiceOncePerFrame
+//=============================================================================
+// Description: Service the sound player
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void AnimObjSoundPlayer::ServiceOncePerFrame()
+{
+ m_player.ServiceOncePerFrame();
+}
+
+//=============================================================================
+// AnimObjSoundPlayer::GetPosition
+//=============================================================================
+// Description: Get the platform's current position
+//
+// Parameters: position - filled in with platform position
+//
+// Return: void
+//
+//=============================================================================
+void AnimObjSoundPlayer::GetPosition( radSoundVector& position )
+{
+ rmt::Vector posn;
+
+ rAssert( m_joint != NULL );
+ posn = m_joint->worldMatrix.Row( 3 );
+ position.SetElements( posn.x, posn.y, posn.z );
+}
+
+//=============================================================================
+// AnimObjSoundPlayer::GetVelocity
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( radSoundVector& velocity )
+//
+// Return: void
+//
+//=============================================================================
+void AnimObjSoundPlayer::GetVelocity( radSoundVector& velocity )
+{
+ //
+ // Doppler would be a big waste on those platforms anyway
+ //
+ velocity.SetElements( 0.0f, 0.0f, 0.0f );
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/sound/movingpositional/animobjsoundplayer.h b/game/code/sound/movingpositional/animobjsoundplayer.h
new file mode 100644
index 0000000..aa71fb1
--- /dev/null
+++ b/game/code/sound/movingpositional/animobjsoundplayer.h
@@ -0,0 +1,70 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: animobjsoundplayer.h
+//
+// Description: Plays sound for moving anim objs
+//
+// History: 6/5/2003 + Created -- Esan (post-beta, yay!)
+//
+//=============================================================================
+
+#ifndef ANIMOBJSOUNDPLAYER_H
+#define ANIMOBJSOUNDPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/positionalsoundplayer.h>
+#include <p3d/anim/pose.hpp>
+
+//========================================
+// Forward References
+//========================================
+struct AnimSoundDSGData;
+struct radSoundVector;
+class AnimCollisionEntityDSG;
+
+//=============================================================================
+//
+// Synopsis: AnimObjSoundPlayer
+//
+//=============================================================================
+
+class AnimObjSoundPlayer : public PositionCarrier
+{
+ public:
+ AnimObjSoundPlayer();
+ virtual ~AnimObjSoundPlayer();
+
+ bool IsActive() { return( m_identity != NULL ); }
+ bool UsesObject( AnimCollisionEntityDSG* soundObject ) { return( soundObject == m_identity ); }
+ void Activate( AnimSoundDSGData* soundData );
+ void Deactivate();
+
+ void ServiceOncePerFrame();
+
+ //
+ // PositionCarrier functions
+ //
+ void GetPosition( radSoundVector& position );
+ void GetVelocity( radSoundVector& velocity );
+
+ private:
+ //Prevent wasteful constructor creation.
+ AnimObjSoundPlayer( const AnimObjSoundPlayer& animobjsoundplayer );
+ AnimObjSoundPlayer& operator=( const AnimObjSoundPlayer& animobjsoundplayer );
+
+ tPose::Joint* m_joint;
+ AnimCollisionEntityDSG* m_identity;
+
+ PositionalSoundPlayer m_player;
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //ANIMOBJSOUNDPLAYER_H
diff --git a/game/code/sound/movingpositional/avatarvehicleposnplayer.cpp b/game/code/sound/movingpositional/avatarvehicleposnplayer.cpp
new file mode 100644
index 0000000..a1803b9
--- /dev/null
+++ b/game/code/sound/movingpositional/avatarvehicleposnplayer.cpp
@@ -0,0 +1,208 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: avatarvehicleposnplayer.cpp
+//
+// Description: Implement AvatarVehiclePosnPlayer
+//
+// History: 3/7/2003 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radnamespace.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/movingpositional/avatarvehicleposnplayer.h>
+
+#include <sound/avatar/carsoundparameters.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+
+#include <events/eventmanager.h>
+#include <mission/gameplaymanager.h>
+#include <worldsim/redbrick/vehicle.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// AvatarVehiclePosnPlayer::AvatarVehiclePosnPlayer
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+AvatarVehiclePosnPlayer::AvatarVehiclePosnPlayer()
+{
+ //
+ // Register events
+ //
+ GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_USER_VEHICLE_ADDED_TO_WORLD );
+ GetEventManager()->AddListener( this, EVENT_USER_VEHICLE_REMOVED_FROM_WORLD );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+}
+
+//=============================================================================
+// AvatarVehiclePosnPlayer::~AvatarVehiclePosnPlayer
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+AvatarVehiclePosnPlayer::~AvatarVehiclePosnPlayer()
+{
+ GetEventManager()->RemoveAll( this );
+}
+
+//=============================================================================
+// AvatarVehiclePosnPlayer::HandleEvent
+//=============================================================================
+// Description: Listen for events directing us to turn positional idle
+// sound on and off
+//
+// Parameters: id - identifier for event being listened for
+// pEventData - unused
+//
+// Return: void
+//
+//=============================================================================
+void AvatarVehiclePosnPlayer::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_GETOUTOFVEHICLE_END:
+ //Chuck: adding this for the stupid UFO beam, and all the wierdness that it generates.
+ if( GetGameplayManager()->GetCurrentVehicle()->IsVehicleDestroyed() == true)
+ {
+ Deactivate();
+ return;
+ }
+ StartPositionalIdle();
+ break;
+ case EVENT_VEHICLE_DESTROYED:
+ {
+ if(m_vehicle == static_cast<Vehicle*>(pEventData))
+ {
+ Deactivate();
+ }
+ break;
+ }
+
+
+ case EVENT_GETINTOVEHICLE_END:
+ Deactivate();
+ break;
+
+ case EVENT_USER_VEHICLE_ADDED_TO_WORLD:
+ StartPositionalIdle( static_cast<Vehicle*>(pEventData) );
+ break;
+
+ case EVENT_USER_VEHICLE_REMOVED_FROM_WORLD:
+ Deactivate();
+ break;
+
+ default:
+ rAssertMsg( false, "Unexpected event in AvatarVehiclePosnPlayer::HandleEvent\n" );
+ break;
+ }
+}
+
+//=============================================================================
+// AvatarVehiclePosnPlayer::StartPositionalIdle
+//=============================================================================
+// Description: Activate this player with an idle engine clip in the vehicle
+// position
+//
+// Parameters: carPtr - if vehicle is already known, this is non-NULL
+//
+// Return: void
+//
+//=============================================================================
+void AvatarVehiclePosnPlayer::StartPositionalIdle( Vehicle* carPtr /* = NULL */)
+{
+ Vehicle* theCar;
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ carSoundParameters* parameters;
+ positionalSoundSettings* soundSettings;
+
+ //
+ // Get name of idle sound clip
+ //
+ if( GetGameplayManager()->GetNumPlayers() == 1 )
+ {
+ if( carPtr != NULL )
+ {
+ theCar = carPtr;
+ }
+ else
+ {
+ theCar = GetGameplayManager()->GetCurrentVehicle();
+ rAssert( theCar != NULL );
+
+ if(!theCar)
+ {
+ rDebugString( "Couldn't find avatar vehicle\n" );
+ return;
+ }
+ }
+
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+
+ nameSpaceObj = nameSpace->GetInstance( theCar->GetName() );
+ if( nameSpaceObj != NULL )
+ {
+ parameters = reinterpret_cast<carSoundParameters*>( nameSpaceObj );
+
+ //
+ // Now find some positional sound settings
+ //
+ nameSpaceObj = nameSpace->GetInstance( "avatar_idle" );
+ if( nameSpaceObj != NULL )
+ {
+ soundSettings = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj );
+
+ Activate( soundSettings, parameters->GetEngineIdleClipName(), theCar );
+
+ m_player.SetPitch( parameters->GetIdleEnginePitch() );
+ }
+ else
+ {
+ rDebugString( "Couldn't find positional settings for avatar vehicle\n" );
+ }
+ }
+ else
+ {
+ rDebugString( "Couldn't find carSoundParameters for avatar vehicle\n" );
+ }
+ }
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
diff --git a/game/code/sound/movingpositional/avatarvehicleposnplayer.h b/game/code/sound/movingpositional/avatarvehicleposnplayer.h
new file mode 100644
index 0000000..c283d4d
--- /dev/null
+++ b/game/code/sound/movingpositional/avatarvehicleposnplayer.h
@@ -0,0 +1,58 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: avatarvehicleposnplayer.h
+//
+// Description: Plays positional idle for avatar vehicle
+//
+// History: 3/7/2003 + Created -- Esan
+//
+//=============================================================================
+
+#ifndef AVATARVEHICLEPOSNPLAYER_H
+#define AVATARVEHICLEPOSNPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/movingpositional/vehicleposnsoundplayer.h>
+
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: AvatarVehiclePosnPlayer
+//
+//=============================================================================
+
+class AvatarVehiclePosnPlayer : public VehiclePositionalSoundPlayer,
+ public EventListener
+{
+ public:
+ AvatarVehiclePosnPlayer();
+ virtual ~AvatarVehiclePosnPlayer();
+
+ //
+ // EventListener functions
+ //
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ void StartPositionalIdle( Vehicle* carPtr = NULL );
+
+ private:
+ //Prevent wasteful constructor creation.
+ AvatarVehiclePosnPlayer( const AvatarVehiclePosnPlayer& avatarvehicleposnplayer );
+ AvatarVehiclePosnPlayer& operator=( const AvatarVehiclePosnPlayer& avatarvehicleposnplayer );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //AVATARVEHICLEPOSNPLAYER_H
diff --git a/game/code/sound/movingpositional/movingsoundmanager.cpp b/game/code/sound/movingpositional/movingsoundmanager.cpp
new file mode 100644
index 0000000..af4f542
--- /dev/null
+++ b/game/code/sound/movingpositional/movingsoundmanager.cpp
@@ -0,0 +1,747 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: movingsoundmanager.cpp
+//
+// Description: Manages the playing of moving sounds
+//
+// History: 1/4/2003 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radnamespace.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/movingpositional/movingsoundmanager.h>
+
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/avatar/carsoundparameters.h>
+
+#include <events/eventmanager.h>
+#include <events/eventdata.h>
+#include <ai/actor/actor.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <constants/vehicleenum.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+tUID MovingSoundManager::s_waspUID = 0;
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// MovingSoundManager::MovingSoundManager
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+MovingSoundManager::MovingSoundManager()
+{
+ EventManager* eventMgr = GetEventManager();
+
+ //
+ // Start listening for events
+ //
+ eventMgr->AddListener( this, EVENT_CHASE_VEHICLE_SPAWNED );
+ eventMgr->AddListener( this, EVENT_CHASE_VEHICLE_DESTROYED );
+ eventMgr->AddListener( this, EVENT_TRAFFIC_SPAWN );
+ eventMgr->AddListener( this, EVENT_TRAFFIC_REMOVE );
+ eventMgr->AddListener( this, EVENT_ACTOR_CREATED );
+ eventMgr->AddListener( this, EVENT_TRAFFIC_GOT_HIT );
+ eventMgr->AddListener( this, EVENT_TRAFFIC_IMPEDED );
+ eventMgr->AddListener( this, EVENT_BIG_BOOM_SOUND );
+ eventMgr->AddListener( this, EVENT_MISSION_VEHICLE_CREATED );
+ eventMgr->AddListener( this, EVENT_MISSION_VEHICLE_RELEASED );
+ eventMgr->AddListener( this, EVENT_START_ANIMATION_SOUND );
+ eventMgr->AddListener( this, EVENT_STOP_ANIMATION_SOUND );
+ eventMgr->AddListener( this, EVENT_START_ANIM_ENTITY_DSG_SOUND );
+ eventMgr->AddListener( this, EVENT_STOP_ANIM_ENTITY_DSG_SOUND );
+ eventMgr->AddListener( this, static_cast<EventEnum>( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_ROOM_2 ) );
+ eventMgr->AddListener( this, EVENT_AVATAR_VEHICLE_TOGGLE );
+
+ if( s_waspUID == static_cast< tUID >( 0 ) )
+ {
+ s_waspUID = tEntity::MakeUID( "beecamera" );
+ }
+
+ TrafficSoundPlayer::InitializeClass( NUM_TRAFFIC_SOUND_PLAYERS );
+}
+
+//=============================================================================
+// MovingSoundManager::~MovingSoundManager
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+MovingSoundManager::~MovingSoundManager()
+{
+ GetEventManager()->RemoveAll( this );
+}
+
+void MovingSoundManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ AnimSoundData* soundData;
+ AnimSoundDSGData* soundDSGData;
+ Vehicle* theCar;
+
+ switch( id )
+ {
+ case EVENT_CHASE_VEHICLE_SPAWNED:
+ //
+ // New chase AI vehicle, play siren for it
+ //
+ addAISound( "siren", static_cast<Vehicle*>(pEventData), false );
+ break;
+
+ case EVENT_CHASE_VEHICLE_DESTROYED:
+ //
+ // Chase AI vehicle gone, stop its siren sound
+ //
+ stopAISound( static_cast<Vehicle*>(pEventData) );
+ break;
+
+ case EVENT_MISSION_VEHICLE_CREATED:
+ //
+ // New mission AI vehicle, play engine for it
+ //
+ theCar = static_cast<Vehicle*>(pEventData);
+ addAISound( getPositionalSettingName( theCar, true ), theCar, true );
+ break;
+
+ case EVENT_MISSION_VEHICLE_RELEASED:
+ //
+ // Mission AI vehicle gone, stop its engine
+ //
+ stopAISound( static_cast<Vehicle*>(pEventData) );
+ break;
+
+ case EVENT_TRAFFIC_SPAWN:
+ //
+ // New traffic vehicle, play engine sound for it
+ //
+ theCar = static_cast<Vehicle*>(pEventData);
+ addTrafficSound( getPositionalSettingName( theCar, false ), theCar, true );
+ break;
+
+ case EVENT_TRAFFIC_REMOVE:
+ //
+ // Traffic vehicle gone
+ //
+ stopTrafficSound( static_cast<Vehicle*>(pEventData) );
+ break;
+
+ case EVENT_AVATAR_VEHICLE_TOGGLE:
+ //
+ // Player avatar has gotten in or out of a vehicle. If it's a traffic
+ // vehicle with an overlay clip, we'll need to toggle it on or off.
+ //
+ toggleOverlayClip( static_cast<Vehicle*>(pEventData) );
+ break;
+
+ case EVENT_ACTOR_CREATED:
+ startWaspSound( static_cast<Actor*>(pEventData) );
+ break;
+
+ case EVENT_TRAFFIC_GOT_HIT:
+ case EVENT_TRAFFIC_IMPEDED:
+ handleTrafficHornEvent( static_cast<Vehicle*>(pEventData) );
+ break;
+
+ case EVENT_BIG_BOOM_SOUND:
+ makeCarGoBoom( static_cast<Vehicle*>(pEventData) );
+ break;
+
+ case EVENT_START_ANIMATION_SOUND:
+ soundData = static_cast<AnimSoundData*>(pEventData);
+ if( soundData->animJoint != NULL )
+ {
+ startPlatformSound( soundData );
+ }
+ break;
+
+ case EVENT_STOP_ANIMATION_SOUND:
+ stopPlatformSound( static_cast<ActionButton::AnimSwitch*>(pEventData) );
+ break;
+
+ case EVENT_START_ANIM_ENTITY_DSG_SOUND:
+ soundDSGData = static_cast<AnimSoundDSGData*>(pEventData);
+ startAnimObjSound( soundDSGData );
+ break;
+
+ case EVENT_STOP_ANIM_ENTITY_DSG_SOUND:
+ stopAnimObjSound( static_cast<AnimCollisionEntityDSG*>(pEventData) );
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_ROOM_2:
+ //
+ // If we're hitting this ambience boundary, then we're leaving the platform
+ // room, so kill all the platforms and save ourselves some streamers.
+ //
+ //stopAllPlatforms();
+ break;
+
+ default:
+ rAssertMsg( false, "Unexpected event in MovingSoundManager\n" );
+ break;
+ }
+}
+
+//=============================================================================
+// MovingSoundManager::ServiceOncePerFrame
+//=============================================================================
+// Description: Do once-per-frame update stuff
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::ServiceOncePerFrame()
+{
+ int i;
+
+ for( i = 0; i < NUM_TRAFFIC_SOUND_PLAYERS; i++ )
+ {
+ m_trafficPlayer[i].ServiceOncePerFrame();
+ }
+ for( i = 0; i < NUM_AI_SOUND_PLAYERS; i++ )
+ {
+ m_aiPlayer[i].ServiceOncePerFrame();
+ }
+ for( i = 0; i < NUM_PLATFORM_SOUND_PLAYERS; i++ )
+ {
+ m_platformPlayer[i].ServiceOncePerFrame();
+ }
+ for( i = 0; i < NUM_ANIM_OBJ_SOUND_PLAYERS; i++ )
+ {
+ m_animObjPlayer[i].ServiceOncePerFrame();
+ }
+ for( i = 0; i < NUM_WASP_SOUND_PLAYERS; i++ )
+ {
+ m_waspPlayer[i].ServiceOncePerFrame();
+ }
+
+ m_avatarVehiclePlayer.ServiceOncePerFrame();
+
+ TrafficSoundPlayer::ServiceTimerList();
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// MovingSoundManager::addTrafficSound
+//=============================================================================
+// Description: Find an inactive sound player and start it up with the given
+// sound and vehicle
+//
+// Parameters: soundName - name of sound resource to play for vehicle
+// vehiclePtr - vehicle whose position the sound will be
+// played from
+// tiePitchToVelocity - set to true if we want the sound pitch
+// to vary (i.e. engine sound)
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::addTrafficSound( const char* soundName, Vehicle* vehiclePtr, bool tiePitchToVelocity )
+{
+ int i;
+ carSoundParameters* carSettings;
+
+ for( i = 0; i < NUM_TRAFFIC_SOUND_PLAYERS; i++ )
+ {
+ if( !m_trafficPlayer[i].IsActive() )
+ {
+ m_trafficPlayer[i].ActivateByName( soundName, vehiclePtr );
+ m_trafficPlayer[i].TiePitchToVelocity( tiePitchToVelocity );
+
+ //
+ // Play overlay clips for traffic if they've got 'em (e.g. quimby truck)
+ //
+ if( hasOverlayClip( vehiclePtr, &carSettings ) )
+ {
+ m_trafficPlayer[i].AddOverlayClip( carSettings, soundName );
+ }
+ break;
+ }
+ }
+
+#ifdef RAD_DEBUG
+ if( i == NUM_TRAFFIC_SOUND_PLAYERS )
+ {
+ rDebugString( "AI Vehicle sound dropped" );
+ }
+#endif
+}
+
+//=============================================================================
+// MovingSoundManager::stopTrafficSound
+//=============================================================================
+// Description: Stop the sound coming from the given vehicle
+//
+// Parameters: vehiclePtr - vehicle whose sound is to be stopped.
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::stopTrafficSound( Vehicle* vehiclePtr )
+{
+ int i;
+
+ for( i = 0; i < NUM_TRAFFIC_SOUND_PLAYERS; i++ )
+ {
+ if( m_trafficPlayer[i].UsesVehicle( vehiclePtr ) )
+ {
+ m_trafficPlayer[i].Deactivate();
+ }
+ }
+}
+
+//=============================================================================
+// MovingSoundManager::addAISound
+//=============================================================================
+// Description: Find an inactive sound player and start it up with the given
+// sound and vehicle
+//
+// Parameters: soundName - name of sound resource to play for vehicle
+// vehiclePtr - vehicle whose position the sound will be
+// played from
+// tiePitchToVelocity - set to true if we want the sound pitch
+// to vary (i.e. engine sound)
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::addAISound( const char* soundName, Vehicle* vehiclePtr, bool tiePitchToVelocity )
+{
+ int i;
+
+ for( i = 0; i < NUM_AI_SOUND_PLAYERS; i++ )
+ {
+ if( !m_aiPlayer[i].IsActive() )
+ {
+ m_aiPlayer[i].ActivateByName( soundName, vehiclePtr );
+ m_aiPlayer[i].TiePitchToVelocity( tiePitchToVelocity );
+ break;
+ }
+ }
+
+#ifdef RAD_DEBUG
+ if( i == NUM_AI_SOUND_PLAYERS )
+ {
+ rDebugString( "AI Vehicle sound dropped" );
+ }
+#endif
+}
+
+//=============================================================================
+// MovingSoundManager::stopAISound
+//=============================================================================
+// Description: Stop the sound coming from the given vehicle
+//
+// Parameters: vehiclePtr - vehicle whose sound is to be stopped.
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::stopAISound( Vehicle* vehiclePtr )
+{
+ int i;
+
+ for( i = 0; i < NUM_AI_SOUND_PLAYERS; i++ )
+ {
+ if( m_aiPlayer[i].UsesVehicle( vehiclePtr ) )
+ {
+ m_aiPlayer[i].Deactivate();
+ }
+ }
+}
+
+//=============================================================================
+// MovingSoundManager::handleTrafficHornEvent
+//=============================================================================
+// Description: Horn the horn for the specified car
+//
+// Parameters: vehiclePtr - pointer to vehicle to honk
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::handleTrafficHornEvent( Vehicle* vehiclePtr )
+{
+ int i;
+
+ rAssert( vehiclePtr != NULL );
+
+ //
+ // Find the car matching the vehicle pointer
+ //
+ for( i = 0; i < NUM_TRAFFIC_SOUND_PLAYERS; i++ )
+ {
+ if( m_trafficPlayer[i].UsesVehicle( vehiclePtr ) )
+ {
+ m_trafficPlayer[i].HonkHorn();
+ }
+ }
+}
+
+//=============================================================================
+// MovingSoundManager::makeCarGoBoom
+//=============================================================================
+// Description: If the vehicle in the explosion event matches one of our
+// vehicles, play a positional explosion
+//
+// Parameters: vehiclePtr - pointer to exploding car
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::makeCarGoBoom( Vehicle* vehiclePtr )
+{
+ int i;
+
+ rAssert( vehiclePtr != NULL );
+
+ if( vehiclePtr->mVehicleType == VT_USER )
+ {
+ //
+ // User vehicles are handled by the sound effect system
+ //
+ return;
+ }
+
+ //
+ // Find the car matching the vehicle pointer
+ //
+ for( i = 0; i < NUM_TRAFFIC_SOUND_PLAYERS; i++ )
+ {
+ if( m_trafficPlayer[i].UsesVehicle( vehiclePtr ) )
+ {
+ m_trafficPlayer[i].BlowUp();
+ break;
+ }
+ }
+
+ if( i == NUM_TRAFFIC_SOUND_PLAYERS )
+ {
+ //
+ // It's not a traffic vehicle, try AI
+ //
+ for( i = 0; i < NUM_AI_SOUND_PLAYERS; i++ )
+ {
+ if( m_aiPlayer[i].UsesVehicle( vehiclePtr ) )
+ {
+ m_aiPlayer[i].BlowUp();
+ break;
+ }
+ }
+ }
+}
+
+//=============================================================================
+// MovingSoundManager::startPlatformSound
+//=============================================================================
+// Description: Find a player for the moving platform and hook it up
+//
+// Parameters: soundData - data we need to identify platform identity and
+// position
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::startPlatformSound( AnimSoundData* soundData )
+{
+ int i;
+
+ for( i = 0; i < NUM_PLATFORM_SOUND_PLAYERS; i++ )
+ {
+ if( !m_platformPlayer[i].IsActive() )
+ {
+ m_platformPlayer[i].Activate( soundData );
+ break;
+ }
+ }
+
+#ifdef RAD_DEBUG
+ if( i == NUM_PLATFORM_SOUND_PLAYERS )
+ {
+ rDebugString( "AI Vehicle sound dropped" );
+ }
+#endif
+}
+
+//=============================================================================
+// MovingSoundManager::stopPlatformSound
+//=============================================================================
+// Description: Find the player for this object and stop it
+//
+// Parameters: soundObject - platform identity
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::stopPlatformSound( ActionButton::AnimSwitch* soundObject )
+{
+ int i;
+
+ for( i = 0; i < NUM_PLATFORM_SOUND_PLAYERS; i++ )
+ {
+ if( m_platformPlayer[i].UsesObject( soundObject ) )
+ {
+ m_platformPlayer[i].Deactivate();
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// MovingSoundManager::stopAllPlatforms
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::stopAllPlatforms()
+{
+ int i;
+
+ for( i = 0; i < NUM_PLATFORM_SOUND_PLAYERS; i++ )
+ {
+ m_platformPlayer[i].Deactivate();
+ }
+}
+
+//=============================================================================
+// MovingSoundManager::startAnimObjSound
+//=============================================================================
+// Description: Find a player for the moving platform and hook it up
+//
+// Parameters: soundData - data we need to identify platform identity and
+// position
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::startAnimObjSound( AnimSoundDSGData* soundData )
+{
+ int i;
+
+ for( i = 0; i < NUM_ANIM_OBJ_SOUND_PLAYERS; i++ )
+ {
+ if( !m_animObjPlayer[i].IsActive() )
+ {
+ m_animObjPlayer[i].Activate( soundData );
+ break;
+ }
+ }
+
+#ifdef RAD_DEBUG
+ if( i == NUM_ANIM_OBJ_SOUND_PLAYERS )
+ {
+ rDebugString( "AnimObj sound dropped" );
+ }
+#endif
+}
+
+//=============================================================================
+// MovingSoundManager::stopAnimObjSound
+//=============================================================================
+// Description: Find the player for this object and stop it
+//
+// Parameters: soundObject - platform identity
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::stopAnimObjSound( AnimCollisionEntityDSG* soundObject )
+{
+ int i;
+
+ for( i = 0; i < NUM_ANIM_OBJ_SOUND_PLAYERS; i++ )
+ {
+ if( m_animObjPlayer[i].UsesObject( soundObject ) )
+ {
+ m_animObjPlayer[i].Deactivate();
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// MovingSoundManager::hasOverlayClip
+//=============================================================================
+// Description: Check whether the given vehicle has an overlay clip that
+// we want to move around with its engine sounds
+//
+// Parameters: vehiclePtr - vehicle we're finding a clip name for
+// parameters - pointer which is pointed at car sound settings
+// if clip exists, NULL otherwise
+//
+// Return: true if overlay clip found, false otherwise
+//
+//=============================================================================
+bool MovingSoundManager::hasOverlayClip( Vehicle* vehiclePtr, carSoundParameters** parameters )
+{
+ IRadNameSpace* nameSpace;
+ const char* clipName;
+ bool retVal = false;
+
+ rAssert( vehiclePtr != NULL );
+ rAssert( parameters != NULL );
+
+ //
+ // Ignore the husk
+ //
+ if( vehiclePtr->mVehicleID == VehicleEnum::HUSKA )
+ {
+ return( false );
+ }
+
+ //
+ // Find the settings for this positional sound first
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ *parameters = reinterpret_cast<carSoundParameters*>( nameSpace->GetInstance( vehiclePtr->GetName() ) );
+ if( *parameters != NULL )
+ {
+ clipName = (*parameters)->GetOverlayClipName();
+ if( clipName != NULL )
+ {
+ retVal = true;
+ }
+ else
+ {
+ *parameters = NULL;
+ }
+ }
+
+ return( retVal );
+}
+
+//=============================================================================
+// MovingSoundManager::toggleOverlayClip
+//=============================================================================
+// Description: Need to switch the overlay clip for the given vehicle
+// if it's a traffic vehicle, because the player is using it
+//
+// Parameters: vehiclePtr - vehicle being gotten in or out of
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::toggleOverlayClip( Vehicle* vehiclePtr )
+{
+ int i;
+ carSoundParameters* parameters;
+
+ for( i = 0; i < NUM_TRAFFIC_SOUND_PLAYERS; i++ )
+ {
+ if( m_trafficPlayer[i].UsesVehicle( vehiclePtr ) )
+ {
+ if( hasOverlayClip( vehiclePtr, &parameters ) )
+ {
+ m_trafficPlayer[i].ToggleOverlayClip( parameters, getPositionalSettingName( vehiclePtr, false ) );
+ }
+ }
+ }
+}
+
+//=============================================================================
+// MovingSoundManager::getPositionalSettingName
+//=============================================================================
+// Description: Figure out which positional values to use for traffic sounds
+// for the given vehicle.
+//
+// Parameters: vehiclePtr - traffic vehicle
+// isMissionVehicle - if true, use louder positional setting
+//
+// Return: name of positional settings object
+//
+//=============================================================================
+const char* MovingSoundManager::getPositionalSettingName( Vehicle* vehiclePtr, bool isMissionVehicle )
+{
+ VehicleEnum::VehicleID carID;
+ const char* settingName;
+
+ rAssert( vehiclePtr != NULL );
+
+ carID = vehiclePtr->mVehicleID;
+ if( carID == VehicleEnum::COFFIN )
+ {
+ settingName = "coffin_posn";
+ }
+ else if( carID == VehicleEnum::HALLO )
+ {
+ settingName = "hearse_posn";
+ }
+ else if( carID == VehicleEnum::CHEARS )
+ {
+ settingName = "chase_hearse_posn";
+ }
+ else if( isMissionVehicle )
+ {
+ settingName = "loud_ai_vehicle";
+ }
+ else
+ {
+ settingName = "generic_traffic";
+ }
+
+ return( settingName );
+}
+
+//=============================================================================
+// MovingSoundManager::startWaspSound
+//=============================================================================
+// Description: Start making waspy noises
+//
+// Parameters: wasp - wasp to start making noise
+//
+// Return: void
+//
+//=============================================================================
+void MovingSoundManager::startWaspSound( Actor* wasp )
+{
+ int i;
+
+ if( wasp->GetStatePropUID() == s_waspUID )
+ {
+ for( i = 0; i < NUM_WASP_SOUND_PLAYERS; i++ )
+ {
+ if( !m_waspPlayer[i].IsActive() )
+ {
+ m_waspPlayer[i].Activate( wasp );
+ break;
+ }
+ }
+ }
+}
diff --git a/game/code/sound/movingpositional/movingsoundmanager.h b/game/code/sound/movingpositional/movingsoundmanager.h
new file mode 100644
index 0000000..1e0d133
--- /dev/null
+++ b/game/code/sound/movingpositional/movingsoundmanager.h
@@ -0,0 +1,108 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: movingsoundmanager.h
+//
+// Description: Manages the playing of moving positional sounds
+//
+// History: 1/4/2003 + Created -- Esan
+//
+//=============================================================================
+
+#ifndef MOVINGSOUNDMANAGER_H
+#define MOVINGSOUNDMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/p3dtypes.hpp>
+
+#include <events/eventlistener.h>
+#include <sound/movingpositional/trafficsoundplayer.h>
+#include <sound/movingpositional/aivehiclesoundplayer.h>
+#include <sound/movingpositional/avatarvehicleposnplayer.h>
+#include <sound/movingpositional/waspsoundplayer.h>
+#include <sound/movingpositional/platformsoundplayer.h>
+#include <sound/movingpositional/animobjsoundplayer.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class carSoundParameters;
+struct AnimSoundData;
+struct AnimSoundDSGData;
+class AnimCollisionEntityDSG;
+
+namespace ActionButton
+{
+ class AnimSwitch;
+};
+
+//=============================================================================
+//
+// Synopsis: MovingSoundManager
+//
+//=============================================================================
+
+class MovingSoundManager : public EventListener
+{
+ public:
+ MovingSoundManager();
+ virtual ~MovingSoundManager();
+
+ void ServiceOncePerFrame();
+
+ //
+ // EventListener functions
+ //
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ private:
+ //Prevent wasteful constructor creation.
+ MovingSoundManager( const MovingSoundManager& original );
+ MovingSoundManager& operator=( const MovingSoundManager& rhs );
+
+ void addTrafficSound( const char* soundName, Vehicle* vehiclePtr, bool tiePitchToVelocity );
+ void stopTrafficSound( Vehicle* vehiclePtr );
+ void addAISound( const char* soundName, Vehicle* vehiclePtr, bool tiePitchToVelocity );
+ void stopAISound( Vehicle* vehiclePtr );
+ void handleTrafficHornEvent( Vehicle* vehiclePtr );
+ void makeCarGoBoom( Vehicle* vehiclePtr );
+ void startPlatformSound( AnimSoundData* soundData );
+ void stopPlatformSound( ActionButton::AnimSwitch* soundObject );
+ void stopAllPlatforms();
+ void startAnimObjSound( AnimSoundDSGData* soundData );
+ void stopAnimObjSound( AnimCollisionEntityDSG* soundObject );
+ void startWaspSound( Actor* wasp );
+ bool hasOverlayClip( Vehicle* vehiclePtr, carSoundParameters** parameters );
+ void toggleOverlayClip( Vehicle* vehiclePtr );
+ const char* getPositionalSettingName( Vehicle* vehiclePtr, bool isMissionVehicle );
+
+ const static int NUM_TRAFFIC_SOUND_PLAYERS = 5;
+ TrafficSoundPlayer m_trafficPlayer[NUM_TRAFFIC_SOUND_PLAYERS];
+
+ const static int NUM_AI_SOUND_PLAYERS = 5;
+ AIVehicleSoundPlayer m_aiPlayer[NUM_AI_SOUND_PLAYERS];
+
+ AvatarVehiclePosnPlayer m_avatarVehiclePlayer;
+
+ const static int NUM_WASP_SOUND_PLAYERS = 2;
+ WaspSoundPlayer m_waspPlayer[NUM_WASP_SOUND_PLAYERS];
+
+ const static int NUM_PLATFORM_SOUND_PLAYERS = 8;
+ PlatformSoundPlayer m_platformPlayer[NUM_PLATFORM_SOUND_PLAYERS];
+
+ const static int NUM_ANIM_OBJ_SOUND_PLAYERS = 5;
+ AnimObjSoundPlayer m_animObjPlayer[NUM_ANIM_OBJ_SOUND_PLAYERS];
+
+ static tUID s_waspUID;
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //MOVINGSOUNDMANAGER_H
diff --git a/game/code/sound/movingpositional/platformsoundplayer.cpp b/game/code/sound/movingpositional/platformsoundplayer.cpp
new file mode 100644
index 0000000..2803e8a
--- /dev/null
+++ b/game/code/sound/movingpositional/platformsoundplayer.cpp
@@ -0,0 +1,209 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: platformsoundplayer.cpp
+//
+// Description: Plays sound for moving platforms
+//
+// History: 5/29/2003 + Created -- Esan (two days to beta, yay!)
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radnamespace.hpp>
+#include <p3d/anim/pose.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/movingpositional/platformsoundplayer.h>
+
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/soundallocatedresource.h>
+
+#include <events/eventdata.h>
+#include <ai/actionbuttonhandler.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// PlatformSoundPlayer::PlatformSoundPlayer
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+PlatformSoundPlayer::PlatformSoundPlayer() :
+ m_joint( NULL ),
+ m_identity( NULL )
+{
+}
+
+//=============================================================================
+// PlatformSoundPlayer::~PlatformSoundPlayer
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+PlatformSoundPlayer::~PlatformSoundPlayer()
+{
+ if( m_identity != NULL )
+ {
+ //m_identity->Release();
+ }
+}
+
+//=============================================================================
+// PlatformSoundPlayer::Activate
+//=============================================================================
+// Description: Start playing a sound for a particular platform
+//
+// Parameters: soundData - position and identify info for platform
+//
+// Return: void
+//
+//=============================================================================
+void PlatformSoundPlayer::Activate( AnimSoundData* soundData )
+{
+ rmt::Vector posn;
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ positionalSoundSettings* parameters;
+
+ //
+ // Get the positionalSoundSettings object for the platform sound
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ nameSpaceObj = nameSpace->GetInstance( ::radMakeKey32( soundData->positionalSettingName ) );
+ if( nameSpaceObj != NULL )
+ {
+ parameters = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj );
+
+ m_joint = soundData->animJoint;
+ rAssert( m_joint != NULL );
+
+ m_identity = soundData->soundObject;
+ rAssert( m_identity != NULL );
+ //m_identity->AddRef();
+
+ m_player.SetPositionCarrier( *this );
+ m_player.SetParameters( parameters );
+
+ //
+ // Get world position of the platform through this joint
+ //
+ posn = m_joint->worldMatrix.Row( 3 );
+ m_player.SetPosition( posn.x, posn.y, posn.z );
+
+ //
+ // Don't buffer, to save IOP
+ //
+
+ m_player.PlaySound( soundData->soundName, NULL );
+ }
+ else
+ {
+ rDebugString( "Couldn't play platform sound, no matching settings found" );
+ }
+}
+
+//=============================================================================
+// PlatformSoundPlayer::Deactivate
+//=============================================================================
+// Description: Stop playing platform sound
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void PlatformSoundPlayer::Deactivate()
+{
+ m_player.Stop();
+
+ if( m_identity != NULL )
+ {
+ //m_identity->Release();
+ m_identity = NULL;
+ }
+
+ m_joint = NULL;
+}
+
+//=============================================================================
+// PlatformSoundPlayer::ServiceOncePerFrame
+//=============================================================================
+// Description: Service the sound player
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void PlatformSoundPlayer::ServiceOncePerFrame()
+{
+ m_player.ServiceOncePerFrame();
+}
+
+//=============================================================================
+// PlatformSoundPlayer::GetPosition
+//=============================================================================
+// Description: Get the platform's current position
+//
+// Parameters: position - filled in with platform position
+//
+// Return: void
+//
+//=============================================================================
+void PlatformSoundPlayer::GetPosition( radSoundVector& position )
+{
+ rmt::Vector posn;
+
+ rAssert( m_joint != NULL );
+ posn = m_joint->worldMatrix.Row( 3 );
+ position.SetElements( posn.x, posn.y, posn.z );
+}
+
+//=============================================================================
+// PlatformSoundPlayer::GetVelocity
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( radSoundVector& velocity )
+//
+// Return: void
+//
+//=============================================================================
+void PlatformSoundPlayer::GetVelocity( radSoundVector& velocity )
+{
+ //
+ // Doppler would be a big waste on those platforms anyway
+ //
+ velocity.SetElements( 0.0f, 0.0f, 0.0f );
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/sound/movingpositional/platformsoundplayer.h b/game/code/sound/movingpositional/platformsoundplayer.h
new file mode 100644
index 0000000..67bcdd1
--- /dev/null
+++ b/game/code/sound/movingpositional/platformsoundplayer.h
@@ -0,0 +1,74 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: platformsoundplayer.h
+//
+// Description: Plays sound for moving platforms
+//
+// History: 5/29/2003 + Created -- Esan (two days to beta, yay!)
+//
+//=============================================================================
+
+#ifndef PLATFORMSOUNDPLAYER_H
+#define PLATFORMSOUNDPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/positionalsoundplayer.h>
+#include <p3d/anim/pose.hpp>
+
+//========================================
+// Forward References
+//========================================
+struct AnimSoundData;
+struct radSoundVector;
+
+namespace ActionButton
+{
+ class AnimSwitch;
+};
+
+//=============================================================================
+//
+// Synopsis: PlatformSoundPlayer
+//
+//=============================================================================
+
+class PlatformSoundPlayer : public PositionCarrier
+{
+ public:
+ PlatformSoundPlayer();
+ virtual ~PlatformSoundPlayer();
+
+ bool IsActive() { return( m_identity != NULL ); }
+ bool UsesObject( ActionButton::AnimSwitch* soundObject ) { return( soundObject == m_identity ); }
+ void Activate( AnimSoundData* soundData );
+ void Deactivate();
+
+ void ServiceOncePerFrame();
+
+ //
+ // PositionCarrier functions
+ //
+ void GetPosition( radSoundVector& position );
+ void GetVelocity( radSoundVector& velocity );
+
+ private:
+ //Prevent wasteful constructor creation.
+ PlatformSoundPlayer( const PlatformSoundPlayer& platformsoundplayer );
+ PlatformSoundPlayer& operator=( const PlatformSoundPlayer& platformsoundplayer );
+
+ tPose::Joint* m_joint;
+ ActionButton::AnimSwitch* m_identity;
+
+ PositionalSoundPlayer m_player;
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //PLATFORMSOUNDPLAYER_H
diff --git a/game/code/sound/movingpositional/trafficsoundplayer.cpp b/game/code/sound/movingpositional/trafficsoundplayer.cpp
new file mode 100644
index 0000000..5013d74
--- /dev/null
+++ b/game/code/sound/movingpositional/trafficsoundplayer.cpp
@@ -0,0 +1,458 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trafficsoundplayer.cpp
+//
+// Description: Administers the playing of sound for a traffic vehicle
+//
+// History: 1/4/2003 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radnamespace.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/movingpositional/trafficsoundplayer.h>
+
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundfx/positionalsoundsettings.h>
+#include <sound/avatar/carsoundparameters.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/trafficlocomotion.h>
+#include <worldsim/traffic/trafficmanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+IRadTimerList* TrafficSoundPlayer::s_timerList = NULL;
+
+static const unsigned int s_minHonkShortMsecs = 250;
+static const unsigned int s_maxHonkShortMsecs = 500;
+static const unsigned int s_minHonkLongMsecs = 500;
+static const unsigned int s_maxHonkLongMsecs = 1000;
+static const unsigned int s_minHonkDelay = 250;
+static const unsigned int s_maxHonkDelay = 500;
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// TrafficSoundPlayer::TrafficSoundPlayer
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+TrafficSoundPlayer::TrafficSoundPlayer( ) :
+ m_hornTimer( NULL ),
+ m_vehicleParameters( NULL ),
+ m_honkCount( 0 ),
+ m_pitchMultiplier( 1.0f )
+{
+}
+
+//=============================================================================
+// TrafficSoundPlayer::~TrafficSoundPlayer
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+TrafficSoundPlayer::~TrafficSoundPlayer()
+{
+ if( m_hornTimer != NULL )
+ {
+ m_hornTimer->UnregisterCallback( this );
+ m_hornTimer->Release();
+ }
+}
+
+//=============================================================================
+// TrafficSoundPlayer::InitializeClass
+//=============================================================================
+// Description: Prep the timer list for tracking horn times
+//
+// Parameters: numVehicles - number of cars, and therefore timers we need
+//
+// Return: void
+//
+//=============================================================================
+void TrafficSoundPlayer::InitializeClass( unsigned int numVehicles )
+{
+ if( s_timerList == NULL )
+ {
+ ::radTimeCreateList( &s_timerList, numVehicles, GMA_AUDIO_PERSISTENT );
+ }
+}
+
+//=============================================================================
+// TrafficSoundPlayer::Activate
+//=============================================================================
+// Description: Before we start playing any sound, set a random pitch adjustment
+// to give this thing a little variety
+//
+// Parameters: soundSettings - positional stuff for traffic
+// resourceName - name of engine sound
+// theCar - pointer to traffic vehicle object
+//
+// Return: void
+//
+//=============================================================================
+void TrafficSoundPlayer::Activate( positionalSoundSettings* soundSettings,
+ const char* resourceName,
+ Vehicle* theCar )
+{
+ unsigned int randomNumber;
+
+ //
+ // Pick a random pitch multiplier from 0.8 to 1.2. Arbitrary numbers.
+ //
+ randomNumber = rand() % 401;
+ m_pitchMultiplier = 0.8f + ( static_cast<float>(randomNumber) / 1000.0f );
+
+ VehiclePositionalSoundPlayer::Activate( soundSettings, resourceName, theCar );
+
+ m_player.SetPitch( 0.5f );
+}
+
+//=============================================================================
+// TrafficSoundPlayer::Deactivate
+//=============================================================================
+// Description: Stop playing sound.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void TrafficSoundPlayer::Deactivate()
+{
+ VehiclePositionalSoundPlayer::Deactivate();
+
+ if( m_hornTimer != NULL )
+ {
+ m_hornTimer->UnregisterCallback( this );
+ m_hornTimer->Release();
+ m_hornTimer = NULL;
+ }
+
+ m_hornPlayer.Stop();
+
+ m_overlayPlayer.Stop();
+}
+
+//=============================================================================
+// TrafficSoundPlayer::ServiceOncePerFrame
+//=============================================================================
+// Description: Adjust the pitch for this vehicle
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void TrafficSoundPlayer::ServiceOncePerFrame()
+{
+ float pitch;
+
+ VehiclePositionalSoundPlayer::ServiceOncePerFrame();
+
+ //
+ // Adjust pitch for vehicle speed if desired. I'll probably need to
+ // expose this for designer tuning later.
+ //
+ if( IsActive() && m_tiePitchToVelocity )
+ {
+ pitch = 0.5f + ( 0.5f * ( m_vehicle->mTrafficLocomotion->mActualSpeed / TrafficManager::GetInstance()->GetDesiredTrafficSpeed() ) );
+ pitch *= m_pitchMultiplier;
+
+ //
+ // Arbitrary cap, just in case. Being paranoid.
+ //
+ if( pitch > 1.5f )
+ {
+ pitch = 1.5f;
+ }
+
+ m_player.SetPitch( pitch );
+ }
+
+ m_hornPlayer.ServiceOncePerFrame();
+ m_overlayPlayer.ServiceOncePerFrame();
+}
+
+//=============================================================================
+// TrafficSoundPlayer::ServiceTimerList
+//=============================================================================
+// Description: Service the timer list. Static function, since we only
+// need to service this static member once per frame, not once
+// per object per frame.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void TrafficSoundPlayer::ServiceTimerList()
+{
+ if( s_timerList != NULL )
+ {
+ s_timerList->Service();
+ }
+}
+
+//=============================================================================
+// TrafficSoundPlayer::OnTimerDone
+//=============================================================================
+// Description: Stop the traffic horn associated with this timer
+//
+// Parameters: elapsedTime - unused
+// pUserData - unused
+//
+// Return: void
+//
+//=============================================================================
+void TrafficSoundPlayer::OnTimerDone( unsigned int elapsedTime, void * pUserData )
+{
+ unsigned int timeout;
+ rmt::Vector position;
+ positionalSoundSettings* settings;
+ IRadNameSpace* nameSpace;
+
+ if( m_hornPlayer.IsInUse() )
+ {
+ m_hornPlayer.Stop();
+
+ if( m_honkCount == 0 )
+ {
+ //
+ // Last honk done
+ //
+ m_hornTimer->Release();
+ m_hornTimer = NULL;
+ }
+ else
+ {
+ //
+ // Random amount of silence before next honk
+ //
+ timeout = s_minHonkDelay + ( rand() % ( s_maxHonkDelay - s_minHonkDelay ) );
+ m_hornTimer->SetTimeout( timeout );
+ m_hornTimer->Start();
+ }
+ }
+ else
+ {
+ //
+ // Silence done, start another honk
+ //
+ rAssert( m_honkCount > 0 );
+
+ //
+ // Find the settings for this positional sound first
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ settings = reinterpret_cast<positionalSoundSettings*>( nameSpace->GetInstance( "traffic_horn" ) );
+ rAssert( settings != NULL );
+
+ m_vehicle->GetPosition( &position );
+ m_hornPlayer.SetPosition( position.x, position.y, position.z );
+ m_hornPlayer.SetPositionCarrier( *this );
+ m_hornPlayer.SetParameters( settings );
+ m_hornPlayer.PlaySound( "horn" );
+
+ --m_honkCount;
+
+ //
+ // Reset the timer
+ //
+ if( m_honkCount > 0 )
+ {
+ timeout = s_minHonkShortMsecs + ( rand() % ( s_maxHonkShortMsecs - s_minHonkShortMsecs ) );
+ }
+ else
+ {
+ timeout = s_minHonkLongMsecs + ( rand() % ( s_maxHonkLongMsecs - s_minHonkLongMsecs ) );
+ }
+
+ m_hornTimer->SetTimeout( timeout );
+ m_hornTimer->Start();
+ }
+}
+
+//=============================================================================
+// TrafficSoundPlayer::HonkHorn
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficSoundPlayer::HonkHorn()
+{
+ unsigned int hornTime;
+ rmt::Vector position;
+ positionalSoundSettings* settings;
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ float diceRoll;
+ float probability;
+
+ rAssert( m_vehicle != NULL );
+
+ if( m_hornTimer != NULL )
+ {
+ //
+ // Already honking
+ //
+ return;
+ }
+
+ //
+ // Find the settings for this positional sound first
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ nameSpaceObj = nameSpace->GetInstance( "traffic_horn" );
+ if( nameSpaceObj != NULL )
+ {
+ settings = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj );
+
+ probability = settings->GetPlaybackProbability();
+ if( probability < 1.0f )
+ {
+ //
+ // Random play
+ //
+ diceRoll = (static_cast<float>( rand() % 100 )) / 100.0f;
+ if( diceRoll >= probability )
+ {
+ return;
+ }
+ }
+
+ //
+ // Pick a random number of honks from 1 to 3. Pick a random time for
+ // the honk, giving the last one a better chance of being longer.
+ //
+ m_honkCount = rand() % 3;
+ if( m_honkCount > 0 )
+ {
+ hornTime = s_minHonkShortMsecs + ( rand() % ( s_maxHonkShortMsecs - s_minHonkShortMsecs ) );
+ }
+ else
+ {
+ hornTime = s_minHonkLongMsecs + ( rand() % ( s_maxHonkLongMsecs - s_minHonkLongMsecs ) );
+ }
+
+ s_timerList->CreateTimer( &m_hornTimer, hornTime, this, NULL, true,
+ IRadTimer::ResetModeOneShot );
+
+ //
+ // Start honking
+ //
+ m_vehicle->GetPosition( &position );
+ m_hornPlayer.SetPosition( position.x, position.y, position.z );
+ m_hornPlayer.SetPositionCarrier( *this );
+ m_hornPlayer.SetParameters( settings );
+ m_hornPlayer.PlaySound( "horn" );
+ }
+ else
+ {
+ rDebugString( "Couldn't find settings for traffic horn\n" );
+ }
+}
+
+//=============================================================================
+// TrafficSoundPlayer::AddOverlayClip
+//=============================================================================
+// Description: Play a positional clip along with the engine
+//
+// Parameters: parameters - car sound description for traffic vehicle
+// posnSettingsName - name of object with positional settings
+//
+// Return: void
+//
+//=============================================================================
+void TrafficSoundPlayer::AddOverlayClip( carSoundParameters* parameters, const char* posnSettingsName )
+{
+ const char* clipName;
+ positionalSoundSettings* settings;
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ rmt::Vector position;
+
+ rAssert( parameters != NULL );
+
+ m_vehicleParameters = parameters;
+ clipName = parameters->GetOverlayClipName();
+ if( clipName != NULL )
+ {
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ nameSpaceObj = nameSpace->GetInstance( posnSettingsName );
+ if( nameSpaceObj != NULL )
+ {
+ settings = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj );
+
+ m_vehicle->GetPosition( &position );
+ m_overlayPlayer.SetPosition( position.x, position.y, position.z );
+ m_overlayPlayer.SetPositionCarrier( *this );
+ m_overlayPlayer.SetParameters( settings );
+ m_overlayPlayer.PlaySound( clipName );
+ }
+ else
+ {
+ rTuneAssertMsg( false, "Huh? Positional settings for overlay clip disappeared" );
+ }
+ }
+}
+
+//=============================================================================
+// TrafficSoundPlayer::ToggleOverlayClip
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( carSoundParameters* parameters, const char* posnSettingsName )
+//
+// Return: void
+//
+//=============================================================================
+void TrafficSoundPlayer::ToggleOverlayClip( carSoundParameters* parameters, const char* posnSettingsName )
+{
+ if( m_overlayPlayer.IsInUse() )
+ {
+ m_overlayPlayer.Stop();
+ }
+ else
+ {
+ AddOverlayClip( parameters, posnSettingsName );
+ }
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/sound/movingpositional/trafficsoundplayer.h b/game/code/sound/movingpositional/trafficsoundplayer.h
new file mode 100644
index 0000000..1f5472e
--- /dev/null
+++ b/game/code/sound/movingpositional/trafficsoundplayer.h
@@ -0,0 +1,83 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trafficsoundplayer.h
+//
+// Description: Administers the playing of sound for a traffic vehicle
+//
+// History: 1/4/2003 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef TRAFFICSOUNDPLAYER_H
+#define TRAFFICSOUNDPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radtime.hpp>
+
+#include <sound/movingpositional/vehicleposnsoundplayer.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class carSoundParameters;
+
+//=============================================================================
+//
+// Synopsis: TrafficSoundPlayer
+//
+//=============================================================================
+
+class TrafficSoundPlayer : public VehiclePositionalSoundPlayer,
+ public IRadTimerCallback
+{
+ public:
+ TrafficSoundPlayer( );
+ virtual ~TrafficSoundPlayer();
+
+ static void InitializeClass( unsigned int numVehicles );
+
+ void Activate( positionalSoundSettings* soundSettings,
+ const char* resourceName,
+ Vehicle* theCar );
+ void Deactivate();
+
+ void ServiceOncePerFrame();
+ static void ServiceTimerList();
+
+ void HonkHorn();
+ void AddOverlayClip( carSoundParameters* parameters, const char* posnSettingsName );
+ void ToggleOverlayClip( carSoundParameters* parameters, const char* posnSettingsName );
+
+ //
+ // IRadTimerCallback
+ //
+ void OnTimerDone( unsigned int elapsedTime, void* pUserData );
+
+ private:
+ //Prevent wasteful constructor creation.
+ TrafficSoundPlayer( const TrafficSoundPlayer& trafficsoundplayer );
+ TrafficSoundPlayer& operator=( const TrafficSoundPlayer& trafficsoundplayer );
+
+ IRadTimer* m_hornTimer;
+ PositionalSoundPlayer m_hornPlayer;
+
+ carSoundParameters* m_vehicleParameters;
+ PositionalSoundPlayer m_overlayPlayer;
+
+ unsigned int m_honkCount;
+ float m_pitchMultiplier;
+
+ static IRadTimerList* s_timerList;
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //TRAFFICSOUNDPLAYER_H
diff --git a/game/code/sound/movingpositional/vehicleposnsoundplayer.cpp b/game/code/sound/movingpositional/vehicleposnsoundplayer.cpp
new file mode 100644
index 0000000..8fbb446
--- /dev/null
+++ b/game/code/sound/movingpositional/vehicleposnsoundplayer.cpp
@@ -0,0 +1,258 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehiclepositionalsoundplayer.cpp
+//
+// Description: Implement VehiclePositionalSoundPlayer
+//
+// History: 3/7/2003 + Created -- Esan
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radnamespace.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/movingpositional/vehicleposnsoundplayer.h>
+
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundfx/positionalsoundsettings.h>
+
+#include <worldsim/redbrick/vehicle.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// VehiclePositionalSoundPlayer::VehiclePositionalSoundPlayer
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+VehiclePositionalSoundPlayer::VehiclePositionalSoundPlayer( ) :
+ m_vehicle( NULL ),
+ m_tiePitchToVelocity( false )
+{
+}
+
+//=============================================================================
+// VehiclePositionalSoundPlayer::~VehiclePositionalSoundPlayer
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+VehiclePositionalSoundPlayer::~VehiclePositionalSoundPlayer()
+{
+}
+
+//=============================================================================
+// AIVehicleSoundPlayer::ActivateByName
+//=============================================================================
+// Description: Start playing given sound for given vehicle
+//
+// Parameters: soundName - name of positional sound setting object containing
+// data on sound to play
+// theCar - vehicle to associate sound with
+//
+// Return: void
+//
+//=============================================================================
+void VehiclePositionalSoundPlayer::ActivateByName( const char* soundName, Vehicle* theCar )
+{
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ positionalSoundSettings* parameters;
+
+ //
+ // Find the tunable sound settings that go with the name
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ nameSpaceObj = nameSpace->GetInstance( ::radMakeKey32( soundName ) );
+ if( nameSpaceObj != NULL )
+ {
+ parameters = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj );
+
+ Activate( parameters, parameters->GetClipName(), theCar );
+ }
+ else
+ {
+ rDebugString( "Couldn't play AI car sound, no matching settings found" );
+ }
+}
+
+//=============================================================================
+// AIVehicleSoundPlayer::Activate
+//=============================================================================
+// Description: Start playing given sound for given vehicle
+//
+// Parameters: soundSettings - positional sound setting object containing
+// data on sound to play
+// resourceName - name of sound resource to play positionally
+// theCar - vehicle to associate sound with
+//
+// Return: void
+//
+//=============================================================================
+void VehiclePositionalSoundPlayer::Activate( positionalSoundSettings* soundSettings,
+ const char* resourceName,
+ Vehicle* theCar )
+{
+ rmt::Vector posn;
+
+ m_vehicle = theCar;
+
+ m_player.SetPositionCarrier( *this );
+ m_player.SetParameters( soundSettings );
+
+ theCar->GetPosition( &posn );
+ m_player.SetPosition( posn.x, posn.y, posn.z );
+
+ m_player.PlaySound( resourceName, NULL );
+}
+
+//=============================================================================
+// VehiclePositionalSoundPlayer::Deactivate
+//=============================================================================
+// Description: Stop playing sound.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void VehiclePositionalSoundPlayer::Deactivate()
+{
+ m_vehicle = NULL;
+ m_tiePitchToVelocity = false;
+
+ m_player.Stop();
+}
+
+//=============================================================================
+// VehiclePositionalSoundPlayer::GetPosition
+//=============================================================================
+// Description: Return position of vehicle we're playing sound for
+//
+// Parameters: position - vector to be filled in with position
+//
+// Return: void
+//
+//=============================================================================
+void VehiclePositionalSoundPlayer::GetPosition( radSoundVector& position )
+{
+ rmt::Vector vehiclePosn;
+
+ m_vehicle->GetPosition( &vehiclePosn );
+ position.SetElements( vehiclePosn.x, vehiclePosn.y, vehiclePosn.z );
+}
+
+//=============================================================================
+// VehiclePositionalSoundPlayer::GetVelocity
+//=============================================================================
+// Description: Return velocity of vehicle we're playing sound for
+//
+// Parameters: velocity - vector to be filled in with velocity
+//
+// Return: void
+//
+//=============================================================================
+void VehiclePositionalSoundPlayer::GetVelocity( radSoundVector& velocity )
+{
+ rmt::Vector vehicleVel;
+
+ m_vehicle->GetVelocity( &vehicleVel );
+ velocity.SetElements( vehicleVel.x, vehicleVel.y, vehicleVel.z );
+}
+
+//=============================================================================
+// VehiclePositionalSoundPlayer::ServiceOncePerFrame
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehiclePositionalSoundPlayer::ServiceOncePerFrame()
+{
+ m_player.ServiceOncePerFrame();
+}
+
+//=============================================================================
+// VehiclePositionalSoundPlayer::UsesVehicle
+//=============================================================================
+// Description: Indicate whether we're currently playing sound for given
+// vehicle
+//
+// Parameters: car - vehicle to match against
+//
+// Return: true if we're playing sound for that car, false otherwise
+//
+//=============================================================================
+bool VehiclePositionalSoundPlayer::UsesVehicle( Vehicle* car )
+{
+ return( ( m_vehicle != NULL ) && ( car == m_vehicle ) );
+}
+
+//=============================================================================
+// VehiclePositionalSoundPlayer::BlowUp
+//=============================================================================
+// Description: Play an explosion sound. Yay!
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void VehiclePositionalSoundPlayer::BlowUp()
+{
+ IRadNameSpace* nameSpace;
+ positionalSoundSettings* settings;
+ rmt::Vector position;
+
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ settings = reinterpret_cast<positionalSoundSettings*>( nameSpace->GetInstance( "collision_sounds" ) );
+ if( settings != NULL )
+ {
+ m_vehicle->GetPosition( &position );
+ m_explosionPlayer.SetPosition( position.x, position.y, position.z );
+ m_explosionPlayer.SetPositionCarrier( *this );
+ m_explosionPlayer.SetParameters( settings );
+ m_explosionPlayer.PlaySound( "generic_car_explode" );
+ }
+ else
+ {
+ rDebugString( "Couldn't find positional explosion settings for AI vehicle" );
+ }
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/sound/movingpositional/vehicleposnsoundplayer.h b/game/code/sound/movingpositional/vehicleposnsoundplayer.h
new file mode 100644
index 0000000..dc47e2f
--- /dev/null
+++ b/game/code/sound/movingpositional/vehicleposnsoundplayer.h
@@ -0,0 +1,81 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehiclepositionalsoundplayer.h
+//
+// Description: Base class for moving vehicle sounds. This includes traffic
+// vehicles and positional idle on the user vehicle
+//
+// History: 3/7/2003 + Created -- Esan
+//
+//=============================================================================
+
+#ifndef VEHICLEPOSITIONALSOUNDPLAYER_H
+#define VEHICLEPOSITIONALSOUNDPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/positionalsoundplayer.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+
+//=============================================================================
+//
+// Synopsis: VehiclePositionalSoundPlayer
+//
+//=============================================================================
+
+class VehiclePositionalSoundPlayer : public PositionCarrier
+{
+ public:
+ VehiclePositionalSoundPlayer( );
+ virtual ~VehiclePositionalSoundPlayer();
+
+ bool IsActive() { return( m_vehicle != NULL ); }
+ void ActivateByName( const char* soundName, Vehicle* theCar );
+ virtual void Activate( positionalSoundSettings* soundSettings,
+ const char* resourceName,
+ Vehicle* theCar );
+ virtual void Deactivate();
+
+ bool UsesVehicle( Vehicle* car );
+
+ virtual void ServiceOncePerFrame();
+
+ void TiePitchToVelocity( bool flag ) { m_tiePitchToVelocity = flag; }
+
+ void BlowUp();
+
+ //
+ // PositionCarrier functions
+ //
+ void GetPosition( radSoundVector& position );
+ void GetVelocity( radSoundVector& velocity );
+
+ protected:
+ Vehicle* m_vehicle;
+
+ PositionalSoundPlayer m_player;
+
+ bool m_tiePitchToVelocity;
+
+
+ private:
+ //Prevent wasteful constructor creation.
+ VehiclePositionalSoundPlayer( const VehiclePositionalSoundPlayer& vehiclepositionalsoundplayer );
+ VehiclePositionalSoundPlayer& operator=( const VehiclePositionalSoundPlayer& vehiclepositionalsoundplayer );
+
+ PositionalSoundPlayer m_explosionPlayer;
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //VEHICLEPOSITIONALSOUNDPLAYER_H
diff --git a/game/code/sound/movingpositional/waspsoundplayer.cpp b/game/code/sound/movingpositional/waspsoundplayer.cpp
new file mode 100644
index 0000000..97eb98a
--- /dev/null
+++ b/game/code/sound/movingpositional/waspsoundplayer.cpp
@@ -0,0 +1,260 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: waspsoundplayer.cpp
+//
+// Description: Implement WaspSoundPlayer
+//
+// History: 3/10/2003 + Created -- Esan
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radnamespace.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/movingpositional/waspsoundplayer.h>
+
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <events/eventmanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// WaspSoundPlayer::WaspSoundPlayer
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+WaspSoundPlayer::WaspSoundPlayer() :
+ m_isFadingIn( false ),
+ m_attacking( false ),
+ m_blowingUp( false )
+{
+ EventManager* eventMgr = GetEventManager();
+
+ eventMgr->AddListener( this, EVENT_ACTOR_REMOVED );
+ eventMgr->AddListener( this, EVENT_WASP_CHARGING );
+ eventMgr->AddListener( this, EVENT_WASP_CHARGED );
+ eventMgr->AddListener( this, EVENT_WASP_ATTACKING );
+ eventMgr->AddListener( this, EVENT_WASP_BLOWED_UP );
+}
+
+//=============================================================================
+// WaspSoundPlayer::~WaspSoundPlayer
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+WaspSoundPlayer::~WaspSoundPlayer()
+{
+ GetEventManager()->RemoveAll( this );
+}
+
+//=============================================================================
+// WaspSoundPlayer::Activate
+//=============================================================================
+// Description: Start playing wasp noises
+//
+// Parameters: theActor - wasp object
+// soundName - name of sound resource to start playing for wasp
+//
+// Return: void
+//
+//=============================================================================
+void WaspSoundPlayer::Activate( Actor* theActor )
+{
+ //
+ // Play the fade-in sound
+ //
+ playWaspSound( "wasp_fade_in", theActor );
+ m_isFadingIn = true;
+}
+
+//=============================================================================
+// WaspSoundPlayer::HandleEvent
+//=============================================================================
+// Description: Look for actor destruction and disassociate when it happens
+//
+// Parameters: id - event ID
+// pEventData - event user data
+//
+// Return: void
+//
+//=============================================================================
+void WaspSoundPlayer::HandleEvent( EventEnum id, void* pEventData )
+{
+ //
+ // Make sure we've got the event for the right wasp
+ //
+ if( static_cast<Actor*>(pEventData) != m_actor )
+ {
+ return;
+ }
+
+ switch( id )
+ {
+ case EVENT_ACTOR_REMOVED:
+ deactivate();
+ break;
+
+ case EVENT_WASP_CHARGING:
+ playWaspSound( "wasp_charging", m_actor );
+ break;
+
+ case EVENT_WASP_CHARGED:
+ playWaspSound( "wasp_charged_idle", m_actor );
+ break;
+
+ case EVENT_WASP_ATTACKING:
+ playWaspSound( "wasp_attack", m_actor );
+ m_attacking = true;
+ break;
+
+ case EVENT_WASP_BLOWED_UP:
+ playWaspSound( "wasp_destroyed", m_actor );
+ m_blowingUp = true;
+ break;
+
+ default:
+ rAssertMsg( false, "Unexpected event in WaspSoundPlayer::HandleEvent\n" );
+ break;
+ }
+}
+
+//=============================================================================
+// WaspSoundPlayer::OnPlaybackComplete
+//=============================================================================
+// Description: Playback callback, so that we can make sound transitions
+// if necessary
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void WaspSoundPlayer::OnPlaybackComplete()
+{
+ if( m_isFadingIn || m_attacking )
+ {
+ m_isFadingIn = false;
+ m_attacking = false;
+
+ playWaspSound( "wasp_idle", m_actor );
+ }
+ else if( m_blowingUp )
+ {
+ //
+ // Wasp destroyed, deactivate player
+ //
+ m_blowingUp = false;
+ deactivate();
+ }
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// WaspSoundPlayer::deactivate
+//=============================================================================
+// Description: Stop sound playback and free player for next wasp
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void WaspSoundPlayer::deactivate()
+{
+ safeStop();
+
+ ActorPlayer::deactivate();
+}
+
+//=============================================================================
+// WaspSoundPlayer::safeStop
+//=============================================================================
+// Description: Stop sound without triggering playback callbacks
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void WaspSoundPlayer::safeStop()
+{
+ //
+ // Kill the callbacks before stopping, so we don't get sounds
+ // lingering around after we think we're stopped
+ //
+ m_isFadingIn = false;
+ m_attacking = false;
+
+ m_player.Stop();
+}
+
+//=============================================================================
+// WaspSoundPlayer::playWaspSound
+//=============================================================================
+// Description: Play a sound for the wasp
+//
+// Parameters: soundName - name of sound resource
+// theActor - wasp actor object
+//
+// Return: void
+//
+//=============================================================================
+void WaspSoundPlayer::playWaspSound( const char* soundName, Actor* theActor )
+{
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ positionalSoundSettings* parameters;
+
+ //
+ // Kill the old sound, disabling any callback action first.
+ //
+ safeStop();
+
+ //
+ // Get the positionalSoundSettings object for the wasp sound
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ nameSpaceObj = nameSpace->GetInstance( ::radMakeKey32( "wasp_settings" ) );
+ if( nameSpaceObj != NULL )
+ {
+ parameters = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj );
+
+ playSound( parameters, soundName, theActor );
+ }
+ else
+ {
+ rDebugString( "Couldn't play wasp sound, no matching settings found" );
+ }
+}
diff --git a/game/code/sound/movingpositional/waspsoundplayer.h b/game/code/sound/movingpositional/waspsoundplayer.h
new file mode 100644
index 0000000..f368b14
--- /dev/null
+++ b/game/code/sound/movingpositional/waspsoundplayer.h
@@ -0,0 +1,71 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: waspsoundplayer.h
+//
+// Description: Plays sound for those flying bugs
+//
+// History: 3/10/2003 + Created -- Esan
+//
+//=============================================================================
+
+#ifndef WASPSOUNDPLAYER_H
+#define WASPSOUNDPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/movingpositional/actorplayer.h>
+
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: WaspSoundPlayer
+//
+//=============================================================================
+
+class WaspSoundPlayer : public ActorPlayer,
+ public EventListener
+{
+ public:
+ WaspSoundPlayer();
+ virtual ~WaspSoundPlayer();
+
+ //
+ // ActorPlayer functions
+ //
+ void Activate( Actor* theActor );
+ void OnPlaybackComplete();
+
+ //
+ // EventListener functions
+ //
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ private:
+ //Prevent wasteful constructor creation.
+ WaspSoundPlayer( const WaspSoundPlayer& waspsoundplayer );
+ WaspSoundPlayer& operator=( const WaspSoundPlayer& waspsoundplayer );
+
+ void playWaspSound( const char* soundName, Actor* theActor );
+
+ void deactivate();
+ void safeStop();
+
+ bool m_isFadingIn;
+ bool m_attacking;
+ bool m_blowingUp;
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //WASPSOUNDPLAYER_H
diff --git a/game/code/sound/music/allmusic.cpp b/game/code/sound/music/allmusic.cpp
new file mode 100644
index 0000000..0b5fb3f
--- /dev/null
+++ b/game/code/sound/music/allmusic.cpp
@@ -0,0 +1 @@
+#include <sound/music/musicplayer.cpp>
diff --git a/game/code/sound/music/musicplayer.cpp b/game/code/sound/music/musicplayer.cpp
new file mode 100644
index 0000000..b03dfbb
--- /dev/null
+++ b/game/code/sound/music/musicplayer.cpp
@@ -0,0 +1,2284 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: musicplayer.cpp
+//
+// Description: Implement MusicPlayer class. Plays all music in
+// the game using RadMusic.
+//
+// History: 17/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/music/musicplayer.h>
+
+#include <sound/soundmanager.h>
+#include <sound/soundrenderer/idasoundtuner.h>
+#include <sound/soundrenderer/dasoundgroup.h>
+
+#include <main/commandlineoptions.h>
+#include <memory/srrmemory.h>
+#include <events/eventmanager.h>
+#include <gameflow/gameflow.h>
+#include <loading/loadingmanager.h>
+#include <loading/soundfilehandler.h>
+#include <mission/missionmanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/avatarmanager.h>
+#include <meta/eventlocator.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+
+#include <Render/Enums/RenderEnums.h>
+
+#include <interiors/interiormanager.h>
+
+#include <radload/radload.hpp>
+
+//========================================
+// System Includes
+//========================================
+#include <string.h>
+
+#include <radmusic/radmusic.hpp>
+#include <p3d/entity.hpp>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+static const char* AMBIENT_FILE_NAME = "sound\\ambience\\ambience.rms";
+static const char* LEVEL_FILE_NAME_PREFIX = "sound\\music\\L";
+static const char* LEVEL_FILE_NAME_SUFFIX = "_music.rms";
+static const char* MUSIC_SEARCH_PATH = "sound\\music\\";
+static const char* AMBIENCE_SEARCH_PATH = "sound\\ambience\\";
+
+struct InteriorIDEntry
+{
+ radInt64 id;
+ const char* name;
+ MusicEventList musicEvent;
+};
+
+static InteriorIDEntry interiorNameTable[] =
+{
+ { 0, "KwikEMart", MEVENT_INTERIOR_KWIK_E_MART },
+ { 0, "SpringfieldElementary", MEVENT_INTERIOR_SCHOOL },
+ { 0, "SimpsonsHouse", MEVENT_INTERIOR_HOUSE },
+ { 0, "Krustylu", MEVENT_INTERIOR_HOUSE },
+ { 0, "dmv", MEVENT_INTERIOR_DMV }
+};
+
+static int interiorTableLength = sizeof( interiorNameTable ) / sizeof( InteriorIDEntry );
+
+//
+// Tables for mapping music/ambience events to indices into radMusic scripts
+//
+
+struct MusicScriptTableEntry
+{
+ const char* name;
+ radKey32 nameKey;
+ int scriptIndex;
+};
+static const int NO_INDEX = -1;
+
+static MusicScriptTableEntry musicEventTable[] =
+{
+ { "movie", 0, 0 },
+ { "pause", 0, 0 },
+ { "unpause", 0, 0 },
+ { "FE", 0, 0 },
+ { "Loading_screen", 0, 0 },
+ { "Newspaper_Spin", 0, 0 },
+ { "SuperSprint", 0, 0 },
+ { "Win_SuperSprint", 0, 0 },
+ { "Lose_SuperSprint", 0, 0 },
+ { "Level_Intro", 0, 0 },
+ { "Sunday_Drive_start", 0, 0 },
+ { "Sunday_Drive_Get_out_of_Car", 0, 0 },
+ { "Store", 0, 0 },
+ { "Enter_StoneCutters_Tunnel", 0, 0 },
+ { "Exit_StoneCutters_Tunnel", 0, 0 },
+ { "OF_explore_mission", 0, 0 },
+ { "OF_found_card", 0, 0 },
+ { "OF_time_out", 0, 0 },
+ { "OF_Apu_Oasis", 0, 0 },
+ { "OF_House", 0, 0 },
+ { "OF_KiwkEMart", 0, 0 },
+ { "OF_School", 0, 0 },
+ { "OF_Moes", 0, 0 },
+ { "OF_DMV", 0, 0 },
+ { "OF_Android_Dungeon", 0, 0 },
+ { "OF_Observatory", 0, 0 },
+ { "Win_3Street_Races", 0, 0 },
+ { "Level_Completed", 0, 0 },
+ { "Destroy_Camera_Bonus", 0, 0 },
+ { "StoneCutters", 0, 0 },
+ { "Social_Club", 0, 0 },
+ { "DuffBeer", 0, 0 },
+ { "Hit_and_Run", 0, 0 },
+ { "Hit_and_Run_Caught", 0, 0 },
+ { "Wasp_Attack", 0, 0 },
+ { "Gated_Mission", 0, 0 },
+ { "ScaryMusic01", 0, 0 },
+ { "Credits", 0, 0 }
+};
+
+static unsigned int musicEventTableLength = sizeof( musicEventTable ) / sizeof( MusicScriptTableEntry );
+
+struct MissionNameScriptEntry
+{
+ radKey32 nameKey;
+ int scriptIndex;
+};
+static const unsigned int MISSION_TABLE_SIZE = 81;
+static MissionNameScriptEntry missionScriptTable[MISSION_TABLE_SIZE];
+static unsigned int missionScriptTableLength;
+
+static const unsigned int RACE_TABLE_SIZE = 12;
+static MissionNameScriptEntry raceScriptTable[RACE_TABLE_SIZE];
+static unsigned int raceScriptTableLength;
+
+struct MatrixStateScriptEntry
+{
+ radKey32 stateNameKey;
+ unsigned int stateIndex;
+ radKey32 stateValueNameKey;
+ unsigned int stateValueIndex;
+};
+static const unsigned int MATRIX_TABLE_SIZE = 6;
+static MatrixStateScriptEntry matrixStateTable[MATRIX_TABLE_SIZE];
+static unsigned int matrixStateTableLength;
+
+// Should change to whatever the global constant is
+static const int NUM_LEVELS = 7;
+static const int NUM_STARTING_MISSIONS = 8;
+
+static AmbientEventList startingAmbiences[NUM_LEVELS][NUM_STARTING_MISSIONS] =
+{
+ // L1
+ {
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_SUBURBS - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_SUBURBS - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_SUBURBS - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_SUBURBS - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_PP_ROOM_2 - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_SUBURBS - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_SUBURBS - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_COUNTRY_HIGHWAY - LocatorEvent::AMBIENT_SOUND_CITY ) )
+ },
+
+ // L2
+ {
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ AEVENT_INTERIOR_HOUSE // unused
+ },
+
+ // L3
+ {
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_LIGHT_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_LIGHT_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_LIGHT_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_FOREST_HIGHWAY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_LIGHT_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_QUAY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_QUAY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ AEVENT_INTERIOR_HOUSE // unused
+ },
+
+ // L4
+ {
+ AEVENT_INTERIOR_HOUSE,
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_COUNTRY_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_SUBURBS_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_SUBURBS_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_SUBURBS_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_SUBURBS_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_SUBURBS_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ AEVENT_INTERIOR_HOUSE // unused
+ },
+
+ // L5
+ {
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_CITY - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ AEVENT_INTERIOR_HOUSE // unused
+ },
+
+ // L6
+ {
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER10 - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER10 - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_SEASIDE_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_COUNTRY_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_SEASIDE_NIGHT - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER10 - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER10 - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ AEVENT_INTERIOR_HOUSE // unused
+ },
+
+ // L7
+ {
+ AEVENT_INTERIOR_HOUSE,
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER7 - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_HALLOWEEN1 - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ AEVENT_INTERIOR_HOUSE,
+ AEVENT_INTERIOR_HOUSE,
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER7 - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ static_cast<AmbientEventList>( AEVENT_TRIGGER_START +
+ ( LocatorEvent::AMBIENT_SOUND_PLACEHOLDER7 - LocatorEvent::AMBIENT_SOUND_CITY ) ),
+ AEVENT_INTERIOR_HOUSE // unused
+ }
+};
+
+static LocatorEvent::Event startingExteriorAmbiences[NUM_LEVELS] =
+{
+ LocatorEvent::AMBIENT_SOUND_SUBURBS,
+ LocatorEvent::AMBIENT_SOUND_CITY,
+ LocatorEvent::AMBIENT_SOUND_LIGHT_CITY,
+ LocatorEvent::AMBIENT_SOUND_SUBURBS_NIGHT,
+ LocatorEvent::AMBIENT_SOUND_CITY,
+ LocatorEvent::AMBIENT_SOUND_PLACEHOLDER10,
+ LocatorEvent::AMBIENT_SOUND_HALLOWEEN1
+};
+
+static int s_PostHitAndRunTimer = -99;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// MusicPlayer::MusicPlayer
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MusicPlayer::MusicPlayer( Sound::IDaSoundTuner& tuner ) :
+ m_lastServiceTime( ::radTimeGetMilliseconds( ) ),
+ m_isLoadingMusic( false ),
+ m_isInCar( false ),
+ m_radLoadRequest( NULL ),
+ m_musicPerformance( NULL ),
+ m_musicComposition( NULL ),
+ m_isLoadingAmbient( false ),
+ m_ambientPerformance( NULL ),
+ m_ambientComposition( NULL ),
+ m_currentAmbient( AEVENT_TRIGGER_START ),
+ m_ambiencePlaying( false ),
+ m_onApuRooftop( false ),
+ m_stoneCutterSong( false ),
+ m_LBCSong( false ),
+ m_delayedMusicStart( false ),
+ m_wasp( NULL )
+{
+ EventManager* eventMgr;
+ unsigned int event;
+
+ //
+ // Register as an event listener
+ //
+
+ eventMgr = GetEventManager();
+
+ eventMgr->AddListener( this, EVENT_GETINTOVEHICLE_END );
+ eventMgr->AddListener( this, EVENT_GETOUTOFVEHICLE_END );
+ eventMgr->AddListener( this, EVENT_INTERIOR_SWITCH );
+ eventMgr->AddListener( this, EVENT_MISSION_DRAMA );
+ eventMgr->AddListener( this, EVENT_MISSION_FAILURE );
+ eventMgr->AddListener( this, EVENT_MISSION_SUCCESS );
+ eventMgr->AddListener( this, EVENT_CARD_COLLECTED );
+ eventMgr->AddListener( this, EVENT_FE_START_GAME_SELECTED );
+ eventMgr->AddListener( this, EVENT_HIT_AND_RUN_START );
+ eventMgr->AddListener( this, EVENT_HIT_AND_RUN_CAUGHT );
+ eventMgr->AddListener( this, EVENT_HIT_AND_RUN_EVADED );
+
+ //
+ // Temporarily remove wasp music
+ //
+ //eventMgr->AddListener( this, EVENT_WASP_CHARGING );
+ //eventMgr->AddListener( this, EVENT_WASP_BLOWED_UP );
+
+ eventMgr->AddListener( this, EVENT_ACTOR_REMOVED );
+ eventMgr->AddListener( this, EVENT_CHANGE_MUSIC );
+ eventMgr->AddListener( this, EVENT_CHANGE_MUSIC_STATE );
+ eventMgr->AddListener( this, EVENT_STAGE_TRANSITION_FAILED );
+ eventMgr->AddListener( this, EVENT_COMPLETED_ALLSTREETRACES );
+ eventMgr->AddListener( this, EVENT_MISSION_RESET );
+ eventMgr->AddListener( this, EVENT_CHARACTER_POS_RESET );
+ eventMgr->AddListener( this, EVENT_PLAY_CREDITS );
+ eventMgr->AddListener( this, EVENT_PLAY_FE_MUSIC );
+ eventMgr->AddListener( this, EVENT_PLAY_MUZAK );
+ //eventMgr->AddListener( this, EVENT_PLAY_IDLE_MUSIC );
+ eventMgr->AddListener( this, EVENT_SUPERSPRINT_WIN );
+ eventMgr->AddListener( this, EVENT_SUPERSPRINT_LOSE );
+ eventMgr->AddListener( this, EVENT_STOP_THE_MUSIC );
+
+ //
+ // Register all of the ambience events
+ //
+ for( event = EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_CITY;
+ event <= EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER16;
+ ++event )
+ {
+ if( ( event < EVENT_LOCATOR + LocatorEvent::PARKED_BIRDS )
+ || ( ( event > EVENT_LOCATOR + LocatorEvent::FAR_PLANE )
+ && ( event < EVENT_LOCATOR + LocatorEvent::GOO_DAMAGE ) )
+ || ( event > EVENT_LOCATOR + LocatorEvent::TRAP ) )
+ {
+ eventMgr->AddListener( this, static_cast<EventEnum>(event) );
+ }
+ }
+
+ //
+ // Fill in the script tables with name keys
+ //
+ initializeTableNameKeys();
+}
+
+//==============================================================================
+// MusicPlayer::~MusicPlayer
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+MusicPlayer::~MusicPlayer()
+{
+ //
+ // Deregister from EventManager
+ //
+ GetEventManager()->RemoveAll( this );
+
+ if( m_musicPerformance != NULL )
+ {
+ radmusic::performance_stop( m_musicPerformance );
+ radmusic::performance_delete( & m_musicPerformance );
+ }
+
+ if ( m_musicComposition != NULL )
+ {
+ radmusic::composition_delete( & m_musicComposition );
+ }
+
+ if( m_ambientPerformance != NULL )
+ {
+ radmusic::performance_stop( m_ambientPerformance );
+ radmusic::performance_delete( & m_ambientPerformance );
+ }
+
+ if ( m_ambientComposition != NULL )
+ {
+ radmusic::composition_delete( & m_ambientComposition );
+ }
+}
+
+void MusicPlayer::TriggerMusicEvent( MusicEventList event )
+{
+ int scriptIndex;
+ int tableIndex;
+
+ if( !(CommandLineOptions::Get( CLO_NO_MUSIC )) )
+ {
+ //
+ // Much necessary hack: screen out music we're tired of hearing
+ //
+ if( CommandLineOptions::Get( CLO_NO_AVRIL ) )
+ {
+ if( ( event == MEVENT_FE )
+ || ( event == MEVENT_SUNDAY_DRIVE_START )
+ || ( event == MEVENT_STREETRACE_START ) )
+ {
+ // Silence
+ event = MEVENT_MOVIE;
+ }
+ }
+
+ //
+ // Get the script index for this event from the appropriate table
+ //
+ if( event < MEVENT_END_STANDARD_EVENTS )
+ {
+ scriptIndex = musicEventTable[event].scriptIndex;
+ }
+ else if( event < MEVENT_END_MISSION_EVENTS )
+ {
+ tableIndex = ( ( event - MEVENT_MISSION_START ) + calculateMissionIndex() );
+ scriptIndex = missionScriptTable[tableIndex].scriptIndex;
+ }
+ else
+ {
+ tableIndex = ( ( event - MEVENT_STREETRACE_START ) + calculateMissionIndex() );
+ scriptIndex = raceScriptTable[tableIndex].scriptIndex;
+ }
+
+ //
+ // TODO: Uncomment after all the missions are set up and ready to go
+ //
+ //rAssertMsg( ( scriptIndex != NO_INDEX ), "Music event not found in script? (E-mail Esan, then continue)" );
+
+ if( ( scriptIndex != NO_INDEX )
+ && ( m_musicPerformance != NULL ) )
+ {
+ radmusic::performance_trigger_event(
+ m_musicPerformance,
+ scriptIndex );
+ }
+ }
+}
+
+void MusicPlayer::TriggerAmbientEvent( unsigned int event )
+{
+ unsigned int scriptLength;
+
+ if( m_ambientPerformance == NULL )
+ {
+ return;
+ }
+
+ if( !(CommandLineOptions::Get( CLO_NO_MUSIC )) )
+ {
+ scriptLength = radmusic::performance_num_events( m_ambientPerformance );
+
+ if( event < scriptLength )
+ {
+ radmusic::performance_trigger_event( m_ambientPerformance, event );
+ }
+ else
+ {
+ //
+ // TODO: reinstate this after MS 17, get ambience for all interiors
+ //
+
+ //rAssertMsg( false, "Invalid ambient event received, ignored\n" );
+ }
+ }
+}
+
+//=============================================================================
+// MusicPlayer::HandleEvent
+//=============================================================================
+// Description: Listen for when the player gets in and out of cars
+//
+// Parameters: id - event ID
+// pEventData - user data, unused
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::HandleEvent( EventEnum id, void* pEventData )
+{
+ bool inCar;
+ int interiorIndex;
+ MusicStateData* musicState;
+ EventLocator* pLocator = (EventLocator*)pEventData;
+ Character* pCharacter = NULL;
+ int levelIndex;
+ int missionIndex;
+
+ static bool s_HitAndRun = false;
+ static int previousTime = 0, recentTime = 0;
+ static EventEnum previousEvent = EVENT_LOCATOR;
+ static EventEnum recentEvent = EVENT_LOCATOR;
+ static EventLocator* previousLocator = NULL;
+ static EventLocator* recentLocator = NULL;
+ static bool bSpecialCaseMusic = false;
+ static MusicEventList lastSpecialMusic = MEVENT_MOVIE; // default
+
+ // bmc: don't pay attention to music events when in hit and run mode
+ /*
+ if ( s_HitAndRun )
+ {
+ if ( id != EVENT_HIT_AND_RUN_CAUGHT && id != EVENT_HIT_AND_RUN_EVADED &&
+ id != EVENT_MISSION_FAILURE && id != EVENT_MISSION_SUCCESS && id != EVENT_MISSION_RESET )
+ return;
+ else
+ s_HitAndRun = false;
+ }
+ */
+
+ switch( id )
+ {
+ case EVENT_FE_START_GAME_SELECTED:
+ TriggerMusicEvent( MEVENT_NEWSPAPER_SPIN );
+ break;
+
+ case EVENT_GETINTOVEHICLE_END:
+ m_isInCar = true;
+
+ if ( !s_HitAndRun )
+ {
+ if ( bSpecialCaseMusic )
+ {
+ TriggerMusicEvent( lastSpecialMusic );
+ }
+ else
+ {
+ startMusic();
+ }
+ }
+ break;
+
+ case EVENT_GETOUTOFVEHICLE_END:
+ // bmc: have to validate that it's not an ai player getting out of the car...
+ pCharacter = static_cast<Character*>( pEventData );
+ if ( !pCharacter->IsNPC() )
+ {
+ m_isInCar = false;
+
+ // if playing special music then we don't want to mess with it...
+ if ( !bSpecialCaseMusic )
+ {
+ if ( !s_HitAndRun && !musicLockedOnForStage() )
+ {
+ if( currentMissionIsSundayDrive() )
+ {
+ TriggerMusicEvent( MEVENT_SUNDAY_DRIVE_GET_OUT_OF_CAR );
+ }
+ else if( currentMissionIsRace() )
+ {
+ TriggerMusicEvent( MEVENT_STREETRACE_GET_OUT_OF_CAR );
+ }
+ else
+ {
+ TriggerMusicEvent( MEVENT_GET_OUT_OF_CAR );
+ }
+ turnAmbienceOn( m_currentAmbient );
+ }
+ }
+ }
+ break;
+
+ case EVENT_MISSION_RESET:
+ {
+ s_HitAndRun = false;
+ bSpecialCaseMusic = false;
+ lastSpecialMusic = MEVENT_MOVIE;
+ // bmc: play music if just declined restarting of a mission
+ if ( GetGameplayManager()->IsSundayDrive() &&
+ GetInteriorManager()->GetInterior() == static_cast< tUID >( 0 ) && (int)pEventData == 0 )
+ {
+ startMusic();
+ }
+
+ inCar = GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar();
+ if( inCar != m_isInCar )
+ {
+ m_isInCar = inCar;
+ startMusic();
+ }
+ else if ( inCar )
+ {
+ // bmc: another hack -- if restarting a mission and we're in a car to start it then play the damn music
+ startMusic();
+ }
+
+#if defined( RAD_XBOX ) || defined( RAD_WIN32 )
+ // XBox seems to like this syntax better.
+ bool jumpStage = reinterpret_cast<bool>( pEventData );
+#else
+ bool jumpStage = (bool)( pEventData );
+#endif
+
+ if( jumpStage )
+ {
+ //
+ // Make sure we're playing the right ambience
+ //
+ levelIndex = calculateLevelIndex();
+ missionIndex = GetGameplayManager()->GetCurrentMissionIndex();
+
+ if( startingAmbiences[levelIndex][missionIndex] == AEVENT_INTERIOR_HOUSE )
+ {
+ m_currentAmbient = AEVENT_TRIGGER_START + ( startingExteriorAmbiences[calculateLevelIndex()] - LocatorEvent::AMBIENT_SOUND_CITY );
+ }
+ else
+ {
+ m_currentAmbient = startingAmbiences[levelIndex][missionIndex];
+ }
+ if( !inCar )
+ {
+ turnAmbienceOn( startingAmbiences[levelIndex][missionIndex] );
+ }
+ }
+ }
+ break;
+
+ case EVENT_CHARACTER_POS_RESET:
+ if ( pEventData )
+ pCharacter = static_cast<Character*>( pEventData );
+
+ if ( pCharacter && pCharacter->IsNPC() )
+ break;
+
+ inCar = GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar();
+ if( inCar != m_isInCar )
+ {
+ m_isInCar = inCar;
+ startMusic();
+ }
+
+ break;
+
+ case EVENT_INTERIOR_SWITCH:
+ //
+ // We sometimes get these events on loading screens. Filter them out.
+ //
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_GAMEPLAY )
+ {
+ break;
+ }
+
+ if(pEventData)
+ {
+ // switching to interior
+ interiorIndex = calculateInteriorIndex( GetInteriorManager()->GetInterior() );
+ turnAmbienceOn( AEVENT_INTERIOR_KWIK_E_MART + interiorIndex );
+ if( !musicLockedOnForStage() )
+ {
+ TriggerMusicEvent( interiorNameTable[interiorIndex].musicEvent );
+ }
+ }
+ else if( !m_isInCar )
+ {
+ // switching to exterior
+ turnAmbienceOn( m_currentAmbient );
+ if( !musicLockedOnForStage() )
+ {
+ TriggerMusicEvent( MEVENT_OF_EXPLORE_MISSION );
+ }
+ }
+ break;
+
+ case EVENT_MISSION_DRAMA:
+ TriggerMusicEvent( MEVENT_MISSION_DRAMA );
+ break;
+
+ case EVENT_CHANGE_MUSIC:
+ triggerMusicMissionEventByName( static_cast<radKey32*>( pEventData ) );
+ break;
+
+ case EVENT_CHANGE_MUSIC_STATE:
+ musicState = static_cast<MusicStateData*>( pEventData );
+ triggerMusicStateChange( musicState->stateKey, musicState->stateEventKey );
+ break;
+
+ case EVENT_MISSION_SUCCESS:
+ s_HitAndRun = false;
+ if( currentMissionIsRace() )
+ {
+ TriggerMusicEvent( MEVENT_STREETRACE_WIN );
+ }
+ else
+ {
+ TriggerMusicEvent( MEVENT_WIN_MISSION );
+ }
+ playPostMissionSounds();
+ break;
+
+ case EVENT_MISSION_FAILURE:
+ s_HitAndRun = false;
+ if( currentMissionIsRace() )
+ {
+ TriggerMusicEvent( MEVENT_STREETRACE_LOSE );
+ }
+ else
+ {
+ TriggerMusicEvent( MEVENT_LOSE_MISSION );
+ }
+
+ //playPostMissionSounds();
+ break;
+
+ case EVENT_STAGE_TRANSITION_FAILED:
+ TriggerMusicEvent( MEVENT_GATED_MISSION );
+ break;
+
+ case EVENT_CARD_COLLECTED:
+ //
+ // If we're in the car, don't interrupt the music, we'll play
+ // a sound effect instead
+ //
+ if( !m_isInCar )
+ {
+ TriggerMusicEvent( MEVENT_OF_FOUND_CARD );
+ }
+ break;
+
+ case EVENT_HIT_AND_RUN_START:
+ TriggerMusicEvent( MEVENT_HIT_AND_RUN_START );
+ s_HitAndRun = true;
+ // bmc: queueing this after the hit and run music seems to do the trick when hit and run ends...but only on evade...
+ //startMusic();
+ break;
+
+ case EVENT_HIT_AND_RUN_CAUGHT:
+ TriggerMusicEvent( MEVENT_HIT_AND_RUN_CAUGHT );
+ s_HitAndRun = false;
+ // begin counter -- (x) ticks until startMusic call...
+ s_PostHitAndRunTimer = 110;
+ break;
+
+ case EVENT_HIT_AND_RUN_EVADED:
+ // bmc: play nothing...
+ //TriggerMusicEvent( MEVENT_SUNDAY_DRIVE_START );
+ s_HitAndRun = false;
+ // begin counter -- (x) ticks until startMusic call...
+ s_PostHitAndRunTimer = 10;
+ break;
+
+ case EVENT_WASP_CHARGING:
+ if( !m_isInCar )
+ {
+ m_wasp = static_cast<Actor*>( pEventData );
+ TriggerMusicEvent( MEVENT_WASP_ATTACK );
+ }
+ break;
+
+ case EVENT_WASP_BLOWED_UP:
+ case EVENT_ACTOR_REMOVED:
+ if( static_cast<Actor*>( pEventData ) == m_wasp )
+ {
+ TriggerMusicEvent( MEVENT_OF_EXPLORE_MISSION );
+
+ m_wasp = NULL;
+ }
+ break;
+
+ case EVENT_COMPLETED_ALLSTREETRACES:
+ TriggerMusicEvent( MEVENT_WIN_3_RACES );
+ break;
+
+ case EVENT_DESTROYED_ALLCAMERAS:
+ TriggerMusicEvent( MEVENT_DESTROY_CAMERA_BONUS );
+ break;
+
+ case EVENT_SUPERSPRINT_WIN:
+ TriggerMusicEvent( MEVENT_SUPERSPRINT_WIN );
+ break;
+
+ case EVENT_SUPERSPRINT_LOSE:
+ TriggerMusicEvent( MEVENT_SUPERSPRINT_LOSE );
+ break;
+
+ case EVENT_PLAY_CREDITS:
+ TriggerMusicEvent( MEVENT_CREDITS );
+ break;
+
+ case EVENT_PLAY_FE_MUSIC:
+ TriggerMusicEvent( MEVENT_FE );
+ break;
+
+ case EVENT_PLAY_MUZAK:
+ TriggerMusicEvent( MEVENT_STORE );
+ break;
+
+ case EVENT_PLAY_IDLE_MUSIC:
+ // bmc: do nothing here -- it fucks things over anyway
+ // TriggerMusicEvent( MEVENT_OF_TIME_OUT );
+ break;
+
+ case EVENT_STOP_THE_MUSIC:
+ TriggerMusicEvent( MEVENT_MOVIE );
+ break;
+
+ default:
+ //
+ // This should be triggered with ambience events. Easier to handle here
+ // than writing dozens of cases above
+ //
+ rAssert( id >= EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_CITY );
+ rAssert( id <= EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER16 );
+ rAssert( ! ( ( id >= EVENT_LOCATOR + LocatorEvent::PARKED_BIRDS )
+ && ( id <= EVENT_LOCATOR + LocatorEvent::FAR_PLANE ) ) );
+ rAssert( ! ( ( id >= EVENT_LOCATOR + LocatorEvent::GOO_DAMAGE )
+ && ( id <= EVENT_LOCATOR + LocatorEvent::TRAP ) ) );
+
+ // don't play specific music if in hit and run
+ if ( s_HitAndRun || !pLocator->GetPlayerEntered() )
+ break;
+
+
+#ifdef RAD_DEBUG
+ char temp[256];
+
+ radmusic::performance_event_name( m_ambientPerformance,
+ AEVENT_TRIGGER_START + id - (EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_CITY),
+ temp,
+ 256 );
+
+ rDebugPrintf( "Hit ambient trigger for %s\n", temp );
+#endif
+
+ if( m_ambiencePlaying )
+ {
+ turnAmbienceOn( AEVENT_TRIGGER_START + id - (EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_CITY) );
+ }
+ else
+ {
+ m_currentAmbient = AEVENT_TRIGGER_START + id - (EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_CITY);
+ }
+
+ //
+ // Icky special case
+ //
+ unsigned int now = ::radTimeGetMilliseconds();
+
+ // bmc: thrashing of events when driving over transition volumes is quite hazardous...
+ // implementing a system with a time threshold on it, so for e.g. when thrashing quickly
+ // between two volumes ABBAABABABA etc. will only handle B. ...however still troublesome
+ // if the player continuously drives back and forth over the volumes...may become confused
+ // as to which one to actually choose and end up with the wrong one :(
+
+ if ( id == recentEvent )
+ {
+ // ignore
+ id = id;
+ }
+ else if ( id == previousEvent && ( ( now - previousTime ) < ( 500 ) ) )
+ {
+ rDebugPrintf( "Sound ignoring likely thrashing event id = %d with time delay %d\n", id, now-previousTime );
+ id = id;
+ }
+ else
+ {
+ if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER9 ) )
+ && ( GetGameplayManager()->IsSundayDrive() ) )
+ {
+ lastSpecialMusic = MEVENT_STONECUTTER_SONG;
+ TriggerMusicEvent( MEVENT_STONECUTTER_SONG );
+ bSpecialCaseMusic = true;
+ }
+ else if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER8 ) )
+ && ( GetGameplayManager()->IsSundayDrive() ) )
+ {
+ lastSpecialMusic = MEVENT_LBC_SONG;
+ TriggerMusicEvent( MEVENT_LBC_SONG );
+ bSpecialCaseMusic = true;
+ }
+ //else if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_STONE_CUTTER_TUNNEL ) )
+ // && ( GetGameplayManager()->IsSundayDrive() ) )
+ //{
+ // TriggerMusicEvent( MEVENT_STONECUTTER_SONG );
+ // bSpecialCaseMusic = true;
+ //}
+ else if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_STONE_CUTTER_HALL ) )
+ && ( GetGameplayManager()->IsSundayDrive() ) )
+ {
+ lastSpecialMusic = MEVENT_ENTER_STONECUTTER_TUNNEL;
+ TriggerMusicEvent( MEVENT_ENTER_STONECUTTER_TUNNEL );
+ bSpecialCaseMusic = true;
+ }
+ else if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_KWIK_E_MART_ROOFTOP ) ) ||
+ ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER3 ) )
+ && ( GetGameplayManager()->IsSundayDrive() ) )
+ {
+ lastSpecialMusic = MEVENT_APU_OASIS;
+ TriggerMusicEvent( MEVENT_APU_OASIS );
+ bSpecialCaseMusic = true;
+ }
+ else if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_HALLOWEEN2 ) )
+ && ( GetGameplayManager()->IsSundayDrive() ) )
+ {
+ lastSpecialMusic = MEVENT_SCARYMUSIC;
+ TriggerMusicEvent( MEVENT_SCARYMUSIC );
+ bSpecialCaseMusic = true;
+ }
+ else if( ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_DUFF_EXTERIOR ) ) ||
+ ( id == ( EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_BREWERY_NIGHT ) )
+ && ( GetGameplayManager()->IsSundayDrive() ) )
+ {
+ lastSpecialMusic = MEVENT_DUFF_SONG;
+ TriggerMusicEvent( MEVENT_DUFF_SONG );
+ bSpecialCaseMusic = true;
+ }
+ else if( bSpecialCaseMusic )
+ {
+ startMusic(); // bmc: test
+ bSpecialCaseMusic = false;
+ lastSpecialMusic = MEVENT_MOVIE; // default
+ }
+
+ previousEvent = recentEvent;
+ previousTime = recentTime;
+ previousLocator = recentLocator;
+
+ recentEvent = id;
+ recentTime = now;
+ recentLocator = pLocator;
+ }
+ break;
+
+ }
+}
+
+//=============================================================================
+// MusicPlayer::Service
+//=============================================================================
+// Description: Extract the composition from the radload request, create the
+// performance and hook up the composition.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+
+void MusicPlayer::SetUpPerformance(
+ radmusic::performance ** ppPerformance,
+ radmusic::composition ** ppComposition,
+ const char * searchPath )
+{
+ rAssert( NULL != ppPerformance );
+ rAssert( NULL != ppComposition );
+ rAssert( NULL == *ppPerformance );
+ rAssert( NULL == *ppComposition );
+
+ radmusic::radload_composition_adapter * compAdapter =
+ radLoadFind< radmusic::radload_composition_adapter >(
+ m_radLoadRequest->GetInventory( ), "my_composition" );
+
+ rAssert( NULL != compAdapter );
+
+ *ppComposition = compAdapter->p_composition;
+
+ rAssert( NULL != *ppComposition );
+
+ compAdapter->p_composition = NULL;
+
+ m_radLoadRequest->Release( );
+ m_radLoadRequest = NULL;
+
+ if ( CommandLineOptions::Get( CLO_FIREWIRE ) )
+ {
+ *ppPerformance = radmusic::performance_new( *ppComposition, searchPath, radMemorySpace_Local );
+ }
+ else
+ {
+ *ppPerformance = radmusic::performance_new( *ppComposition, searchPath );
+ }
+
+ rAssert( *ppPerformance != NULL );
+}
+
+//=============================================================================
+// MusicPlayer::Service
+//=============================================================================
+// Description: Service RadMusic, and check the composition loader if we're
+// still in a loading state
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::Service()
+{
+ HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT );
+
+ unsigned int now = ::radTimeGetMilliseconds( );
+ unsigned int dif = now - m_lastServiceTime;
+
+ if ( m_musicPerformance != NULL )
+ {
+ radmusic::performance_update( m_musicPerformance, dif );
+ }
+
+ if ( m_ambientPerformance != NULL )
+ {
+ radmusic::performance_update( m_ambientPerformance, dif );
+ }
+
+ m_lastServiceTime = now;
+
+ //
+ // According to Tim, this can be changed to a synchronous
+ // load on a thread using:
+ //
+ // radmusic::composition_new_from_file( const char * p_file_name );
+ //
+
+ if( m_isLoadingMusic && ( m_radLoadRequest->IsComplete( ) ) )
+ {
+ SetUpPerformance( & m_musicPerformance, & m_musicComposition, MUSIC_SEARCH_PATH );
+
+ m_isLoadingMusic = false;
+
+ //
+ // Construct the event lookup tables
+ //
+ buildEventTables();
+
+ m_loadCompleteCallback->LoadCompleted();
+ }
+ else if( m_isLoadingAmbient && m_radLoadRequest->IsComplete( ) )
+ {
+ SetUpPerformance( & m_ambientPerformance, & m_ambientComposition, AMBIENCE_SEARCH_PATH );
+
+ m_isLoadingAmbient = false;
+
+ rAssert( radmusic::performance_num_events( m_ambientPerformance ) == AEVENT_NUM_EVENTS );
+
+
+ m_loadCompleteCallback->LoadCompleted();
+ }
+
+ HeapMgr()->PopHeap( GMA_AUDIO_PERSISTENT );
+
+ // bmc: hit-and-run post music hack
+ if ( s_PostHitAndRunTimer > 0 )
+ {
+ if ( --s_PostHitAndRunTimer == 0 )
+ {
+ s_PostHitAndRunTimer = -99;
+ startMusic();
+ }
+ }
+
+ // My own music hack -- DE
+ if( m_delayedMusicStart )
+ {
+ if( m_musicPerformance == NULL )
+ {
+ m_delayedMusicStart = false;
+ }
+ else
+ {
+ //
+ // Check to see if the mission success/fail stinger is done yet
+ //
+ if( radmusic::performance_is_state_steady_idle( m_musicPerformance ) )
+ {
+ char regionName[ 64 ];
+ radmusic::debug_performance_current_region_name( m_musicPerformance, regionName, 64 );
+
+ if( strcmp( regionName, "stopped" ) == 0 )
+ {
+ m_delayedMusicStart = false;
+ startMusic();
+ }
+ }
+ }
+ }
+}
+
+//=============================================================================
+// MusicPlayer::QueueRadmusicScriptLoad
+//=============================================================================
+// Description: Queue the RadMusic composition script files in the loading
+// manager
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::QueueRadmusicScriptLoad()
+{
+ GetLoadingManager()->AddRequest( FILEHANDLER_SOUND, AMBIENT_FILE_NAME, GMA_LEVEL_AUDIO );
+
+ //
+ // For the front end, any music script will do right now
+ //
+ QueueMusicLevelLoad( RenderEnums::L1 );
+}
+
+//=============================================================================
+// MusicPlayer::QueueMusicLevelLoad
+//=============================================================================
+// Description: Queue the music script for the given level in the loading
+// manager
+//
+// Parameters: level - enumeration indicating which level we're loading
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::QueueMusicLevelLoad( RenderEnums::LevelEnum level )
+{
+ char musicScriptName[100];
+ int levelNum = level - RenderEnums::L1 + 1;
+
+ //TODO: Esan are we going to play special music here?
+ if ( levelNum > RenderEnums::numLevels )
+ {
+ levelNum -= RenderEnums::numLevels;
+ }
+
+ sprintf( musicScriptName, "%s%d%s", LEVEL_FILE_NAME_PREFIX, levelNum, LEVEL_FILE_NAME_SUFFIX );
+ GetLoadingManager()->AddRequest( FILEHANDLER_SOUND, musicScriptName, GMA_LEVEL_AUDIO );
+}
+
+//=============================================================================
+// MusicPlayer::LoadRadmusicScript
+//=============================================================================
+// Description: Load the RadMusic script
+//
+// Parameters: fileHandler - completion callback object
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::LoadRadmusicScript( const char* filename, SoundFileHandler* fileHandler )
+{
+ rAssert( NULL == m_radLoadRequest );
+
+ if( strcmp( filename, AMBIENT_FILE_NAME ) == 0 )
+ {
+ m_isLoadingAmbient = true;
+ }
+ else
+ {
+ UnloadRadmusicScript();
+ m_isLoadingMusic = true;
+ }
+
+ m_loadCompleteCallback = fileHandler;
+
+ // We have to keep the string around--problem with radload.
+
+ strncpy( n_currentLoadName, filename, 64 );
+
+ radLoadOptions options;
+ options.filename = n_currentLoadName;
+ options.allocator = GMA_MUSIC;
+
+ HeapMgr()->PushHeap(GMA_MUSIC);
+ radLoadInstance()->Load( & options, & m_radLoadRequest );
+ HeapMgr()->PopHeap(GMA_MUSIC);
+}
+
+//=============================================================================
+// MusicPlayer::UnloadRadmusicScript
+//=============================================================================
+// Description: Dump the music script, if it's loaded
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::UnloadRadmusicScript()
+{
+ if( NULL != m_musicPerformance )
+ {
+ radmusic::performance_stop( m_musicPerformance );
+ radmusic::performance_delete( & m_musicPerformance );
+
+ m_musicPerformance = NULL;
+ }
+
+ if ( NULL != m_musicComposition )
+ {
+ radmusic::composition_delete( & m_musicComposition );
+
+ m_musicComposition = NULL;
+ }
+}
+
+//=============================================================================
+// MusicPlayer::OnFrontEndStart
+//=============================================================================
+// Description: Notify RadMusic composition that we've started the front end
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::OnFrontEndStart()
+{
+ TriggerMusicEvent( MEVENT_FE );
+}
+
+//=============================================================================
+// MusicPlayer::OnFrontEndFinish
+//=============================================================================
+// Description: Notify RadMusic composition that we've finished the front end
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::OnFrontEndFinish()
+{
+ if( CommandLineOptions::Get( CLO_NO_MUSIC ) )
+ {
+ return;
+ }
+}
+
+//=============================================================================
+// MusicPlayer::OnGameplayStart
+//=============================================================================
+// Description: Notify RadMusic composition that we've started gameplay
+//
+// Parameters: playerInCar - should be true if player 0 is in the car, meaning
+// that we play music instead of ambient sound
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::OnGameplayStart( bool playerInCar )
+{
+ int levelIndex;
+ int missionIndex;
+
+ m_isInCar = playerInCar;
+
+ if( !(GetGameplayManager()->IsSuperSprint()) )
+ {
+ TriggerMusicEvent( MEVENT_LEVEL_INTRO );
+ }
+
+ //
+ // We want music playing for in-car characters. SuperSprint is a special
+ // case, since the characters can't be placed in cars until they select
+ // their cars, but we want music at gameplay start anyway
+ //
+ if( playerInCar || GetGameplayManager()->IsSuperSprint() )
+ {
+ startMusic();
+ }
+ else
+ {
+ levelIndex = calculateLevelIndex();
+ missionIndex = GetGameplayManager()->GetCurrentMissionIndex();
+
+ if( startingAmbiences[levelIndex][missionIndex] == AEVENT_INTERIOR_HOUSE )
+ {
+ m_currentAmbient = AEVENT_TRIGGER_START + ( startingExteriorAmbiences[calculateLevelIndex()] - LocatorEvent::AMBIENT_SOUND_CITY );
+ }
+ else
+ {
+ m_currentAmbient = startingAmbiences[levelIndex][missionIndex];
+ }
+ turnAmbienceOn( startingAmbiences[levelIndex][missionIndex] );
+ }
+}
+
+//=============================================================================
+// MusicPlayer::OnGameplayFinish
+//=============================================================================
+// Description: Notify RadMusic composition that we've finished gameplay
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::OnGameplayFinish()
+{
+ TriggerMusicEvent( MEVENT_LOADING_SCREEN );
+
+ turnAmbienceOff( AEVENT_FRONTEND );
+
+ m_delayedMusicStart = false;
+}
+
+//=============================================================================
+// MusicPlayer::OnPauseStart
+//=============================================================================
+// Description: Entering pause menu, pause the music
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::OnPauseStart()
+{
+ TriggerMusicEvent( MEVENT_PAUSE );
+ turnAmbienceOff( AEVENT_PAUSE );
+}
+
+//=============================================================================
+// MusicPlayer::OnPauseEnd
+//=============================================================================
+// Description: Leaving pause menu, restart the music
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::OnPauseEnd()
+{
+ TriggerMusicEvent( MEVENT_UNPAUSE );
+ turnAmbienceOn( AEVENT_UNPAUSE );
+}
+
+//=============================================================================
+// MusicPlayer::OnStoreStart
+//=============================================================================
+// Description: Triggered when we enter a reward screen, like the clothes
+// shop
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::OnStoreStart()
+{
+ TriggerMusicEvent( MEVENT_STORE );
+}
+
+//=============================================================================
+// MusicPlayer::OnStoreEnd
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::OnStoreEnd()
+{
+ //
+ // We actually reuse this for supersprint
+ //
+ if( !(GetGameplayManager()->IsSuperSprint()) )
+ {
+ if( GetInteriorManager()->GetInterior() == static_cast< tUID >( 0 ) )
+ {
+ TriggerMusicEvent( MEVENT_OF_EXPLORE_MISSION );
+ }
+ else
+ {
+ TriggerMusicEvent( interiorNameTable[calculateInteriorIndex( GetInteriorManager()->GetInterior() )].musicEvent );
+ }
+ }
+}
+
+//=============================================================================
+// MusicPlayer::StopForMovie
+//=============================================================================
+// Description: Stop the music for a movie
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::StopForMovie()
+{
+ TriggerMusicEvent( MEVENT_PAUSE );
+ turnAmbienceOff( AEVENT_PAUSE );
+}
+
+//=============================================================================
+// MusicPlayer::ResumeAfterMovie
+//=============================================================================
+// Description: Start the FE music again after an FMV
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::ResumeAfterMovie()
+{
+ TriggerMusicEvent( MEVENT_UNPAUSE );
+ turnAmbienceOn( AEVENT_UNPAUSE );
+}
+
+bool MusicPlayer::IsStoppedForMovie( void )
+{
+ char regionName[ 64 ];
+ if ( !m_musicPerformance )
+ {
+ return true;
+ }
+ radmusic::debug_performance_current_region_name( m_musicPerformance, regionName, 64 );
+
+ bool steadyIdle = radmusic::performance_is_state_steady_idle( m_musicPerformance );
+
+ bool paused = ( strcmp( regionName, "stopped" ) == 0 || strcmp( regionName, "pause_region" ) == 0 );
+
+ return paused && steadyIdle;
+}
+
+//=============================================================================
+// MusicPlayer::RestartSupersprintMusic
+//=============================================================================
+// Description: Give the supersprint music a kick
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::RestartSupersprintMusic()
+{
+ TriggerMusicEvent( MEVENT_SUPERSPRINT );
+}
+
+//=============================================================================
+// MusicPlayer::GetVolume
+//=============================================================================
+// Description: Get the volume. Duh.
+//
+// Parameters: None
+//
+// Return: Float value for volume
+//
+//=============================================================================
+float MusicPlayer::GetVolume()
+{
+ rAssert( NULL != m_musicPerformance );
+ if ( NULL == m_musicPerformance )
+ {
+ return 0.0f;
+ }
+ else
+ {
+ return( radmusic::performance_volume( m_musicPerformance ) );
+ }
+}
+
+//=============================================================================
+// MusicPlayer::SetVolume
+//=============================================================================
+// Description: Set the volume. Also duh.
+//
+// Parameters: volume - new volume setting
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::SetVolume( float volume )
+{
+ if ( NULL != m_musicPerformance )
+ {
+ radmusic::performance_volume( m_musicPerformance, volume );
+ }
+}
+
+//=============================================================================
+// MusicPlayer::GetAmbienceVolume
+//=============================================================================
+// Description: Get the ambience volume.
+//
+// Parameters: None
+//
+// Return: Float value for volume
+//
+//=============================================================================
+float MusicPlayer::GetAmbienceVolume()
+{
+ rAssert( NULL != m_ambientPerformance );
+ if ( NULL == m_ambientPerformance )
+ {
+ return 0.0f;
+ }
+ else
+ {
+ return( radmusic::performance_volume( m_ambientPerformance ) );
+ }
+}
+
+//=============================================================================
+// MusicPlayer::SetAmbienceVolume
+//=============================================================================
+// Description: Set the ambience volume.
+//
+// Parameters: volume - new volume setting
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::SetAmbienceVolume( float volume )
+{
+ rAssert( NULL != m_ambientPerformance );
+ if ( NULL != m_ambientPerformance )
+ {
+ radmusic::performance_volume( m_ambientPerformance, volume );
+ }
+}
+
+//=============================================================================
+// MusicPlayer::GetBeatValue
+//=============================================================================
+// Description: Pass on the beat from the music performance
+//
+// Parameters: None
+//
+// Return: float from 0.0f to 4.0f
+//
+//=============================================================================
+float MusicPlayer::GetBeatValue()
+{
+ float beat = 0.0f;
+
+ if( m_musicPerformance != NULL )
+ {
+ radmusic::debug_performance_current_beat( m_musicPerformance, &beat );
+ }
+
+ return( beat );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// MusicPlayer::calculateLevelIndex
+//=============================================================================
+// Description: Figure out an index for the current level (0 for L1, 1 for L2...)
+//
+// Parameters: None
+//
+// Return: Index
+//
+//=============================================================================
+int MusicPlayer::calculateLevelIndex()
+{
+ RenderEnums::LevelEnum level;
+
+ level = GetGameplayManager()->GetCurrentLevelIndex();
+
+ return( static_cast<int>( level - RenderEnums::L1 ) );
+}
+
+//=============================================================================
+// MusicPlayer::calculateMissionIndex
+//=============================================================================
+// Description: Figure out an index for the current mission (0 for M1, 1 for M2...).
+// Use -1 for Sunday Drive.
+//
+// Parameters: None
+//
+// Return: Index
+//
+//=============================================================================
+int MusicPlayer::calculateMissionIndex()
+{
+ int index;
+ int missionIndex;
+ Mission* currentMission;
+ GameplayManager* gameplayMgr = GetGameplayManager();
+
+ if( gameplayMgr->IsSundayDrive() )
+ {
+ index = -1;
+ }
+ else
+ {
+ currentMission = gameplayMgr->GetCurrentMission();
+ rAssert( currentMission != NULL );
+
+ if( currentMission->IsRaceMission() )
+ {
+ index = ( gameplayMgr->GetCurrentMissionNum() - GameplayManager::MAX_MISSIONS ) * ( MEVENT_END_RACE_EVENTS - MEVENT_STREETRACE_START );
+ }
+ else if( currentMission->IsBonusMission() )
+ {
+ index = 8 * ( MEVENT_END_MISSION_EVENTS - MEVENT_MISSION_START ); // 0-7 are regular missions, 8 is bonus
+ }
+ else
+ {
+ missionIndex = gameplayMgr->GetCurrentMissionIndex();
+
+ //
+ // Stupid dummy level 1 and its special-case training mission
+ //
+ if( gameplayMgr->GetCurrentLevelIndex() != RenderEnums::L1 )
+ {
+ missionIndex += 1;
+ }
+
+ index = missionIndex * ( MEVENT_END_MISSION_EVENTS - MEVENT_MISSION_START );
+ }
+ }
+
+ return( index );
+}
+
+//=============================================================================
+// MusicPlayer::calculateInteriorIndex
+//=============================================================================
+// Description: Figure out an index for the interior being entered
+//
+// Parameters: None
+//
+// Return: Index starting from 0, to be added to first interior event
+// in ambient list
+//
+//=============================================================================
+int MusicPlayer::calculateInteriorIndex( tUID interiorID )
+{
+ int i;
+ int retVal = 2; // Default to Simpsons house sound
+
+ //
+ // We should've be calculating this unless we're actually inside
+ //
+ rAssert( interiorID != static_cast< tUID >( 0 ) );
+
+ if( interiorNameTable[0].id == static_cast< tUID >( 0 ) )
+ {
+ //
+ // Initialize table
+ //
+ for( i = 0; i < interiorTableLength; i++ )
+ {
+ interiorNameTable[i].id = tName::MakeUID( interiorNameTable[i].name );
+ }
+ }
+
+ for( i = 0; i < interiorTableLength; i++ )
+ {
+ if( interiorID == static_cast< tUID >( interiorNameTable[i].id ) )
+ {
+ retVal = i;
+ break;
+ }
+ }
+
+ return( retVal );
+}
+
+//=============================================================================
+// MusicPlayer::musicLockedOnForStage
+//=============================================================================
+// Description: Checks the current mission stage to see if we need to keep
+// the music playing when we get out of the car
+//
+// Parameters: None
+//
+// Return: True if music stays on, false otherwise
+//
+//=============================================================================
+bool MusicPlayer::musicLockedOnForStage()
+{
+ Mission* mission;
+ MissionStage* stage;
+ bool musicLocked = false;
+
+ mission = GetGameplayManager()->GetCurrentMission();
+ if( mission != NULL )
+ {
+ stage = mission->GetCurrentStage();
+ if( stage != NULL )
+ {
+ musicLocked = stage->GetMusicAlwaysOnFlag();
+ }
+ }
+
+ return( musicLocked );
+}
+
+//=============================================================================
+// MusicPlayer::startMusic
+//=============================================================================
+// Description: Figure out which music track to play and play it
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::startMusic()
+{
+ // bmc: if calling startMusic then hit and run *must* be over...clean it up
+ s_PostHitAndRunTimer = -99;
+ // bmc
+
+ int mission;
+ bool onFoot = false;
+
+ mission = calculateMissionIndex();
+ if( mission >= 0 )
+ {
+ if( currentMissionIsRace() )
+ {
+ TriggerMusicEvent( MEVENT_STREETRACE_START );
+ }
+ else if ( m_isInCar )
+ {
+ TriggerMusicEvent( MEVENT_MISSION_START );
+ }
+ else
+ {
+ TriggerMusicEvent( MEVENT_OF_EXPLORE_MISSION );
+ onFoot = true;
+ }
+
+ }
+ else if( GetGameplayManager()->IsSuperSprint() )
+ {
+ //
+ // Play Fox's licensed music. Fox sucks.
+ //
+ TriggerMusicEvent( MEVENT_SUPERSPRINT );
+ }
+ else if ( m_isInCar )
+ {
+ TriggerMusicEvent( MEVENT_SUNDAY_DRIVE_START );
+ }
+ else
+ {
+ TriggerMusicEvent( MEVENT_OF_EXPLORE_MISSION );
+ onFoot = true;
+ }
+
+ if( !onFoot )
+ {
+ turnAmbienceOff( AEVENT_PAUSE );
+ }
+ else if( ( GetInteriorManager() != NULL )
+ && ( GetInteriorManager()->IsInside() ) )
+ {
+ triggerCurrentInteriorAmbience();
+ }
+ else
+ {
+ turnAmbienceOn( m_currentAmbient );
+ }
+}
+
+//=============================================================================
+// MusicPlayer::playPostMissionSounds
+//=============================================================================
+// Description: Figure out what to play when the mission ends
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::playPostMissionSounds()
+{
+ if( !m_isInCar )
+ {
+ if( ( GetInteriorManager() != NULL )
+ && ( GetInteriorManager()->IsInside() ) )
+ {
+ triggerCurrentInteriorAmbience();
+ }
+ else
+ {
+ turnAmbienceOn( m_currentAmbient );
+ }
+ }
+
+ m_delayedMusicStart = true;
+}
+
+//=============================================================================
+// MusicPlayer::turnAmbienceOn
+//=============================================================================
+// Description: Send the given ambience event and mark ambience as not
+// playing
+//
+// Parameters: event - event to give to ambient performance object (shouldn't
+// be pause or stop event)
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::turnAmbienceOn( unsigned int event )
+{
+ TriggerAmbientEvent( event );
+
+ if( ( event != AEVENT_UNPAUSE ) && ( event <= AEVENT_TRIGGER_END ) )
+ {
+ m_currentAmbient = event;
+ }
+ m_ambiencePlaying = true;
+}
+
+//=============================================================================
+// MusicPlayer::turnAmbienceOff
+//=============================================================================
+// Description: Send the given ambience event and mark ambience as playing
+//
+// Parameters: event - event to give to ambient performance object (should
+// be pause or stop event)
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::turnAmbienceOff( unsigned int event )
+{
+ rAssert( ( event == AEVENT_PAUSE ) || ( event == AEVENT_MOVIE )
+ || ( event == AEVENT_FRONTEND ) );
+
+ TriggerAmbientEvent( event );
+ m_ambiencePlaying = false;
+}
+
+//=============================================================================
+// MusicPlayer::currentMissionIsRace
+//=============================================================================
+// Description: As the name says
+//
+// Parameters: None
+//
+// Return: True if there's a current mission and it's a race, false
+// otherwise
+//
+//=============================================================================
+bool MusicPlayer::currentMissionIsRace()
+{
+ bool retVal = false;
+ Mission* theMission = GetGameplayManager()->GetCurrentMission();
+
+ if( ( theMission != NULL ) && ( theMission->IsRaceMission() ) )
+ {
+ retVal = true;
+ }
+
+ return( retVal );
+}
+
+//=============================================================================
+// MusicPlayer::currentMissionIsSundayDrive
+//=============================================================================
+// Description: As the name says
+//
+// Parameters: None
+//
+// Return: True if there's a current mission and it's Sunday Drive, false
+// otherwise
+//
+//=============================================================================
+bool MusicPlayer::currentMissionIsSundayDrive()
+{
+ bool retVal = false;
+ Mission* theMission = GetGameplayManager()->GetCurrentMission();
+
+ if( ( theMission != NULL ) && ( theMission->IsSundayDrive() ) )
+ {
+ retVal = true;
+ }
+
+ return( retVal );
+}
+
+//=============================================================================
+// MusicPlayer::initializeTableNameKeys
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::initializeTableNameKeys()
+{
+ unsigned int i;
+ char temp[256];
+ unsigned int tableIndex;
+ char prefix[10];
+
+ //
+ // We've already got strings for the musicEventTable, just make keys
+ //
+ for( i = 0; i < musicEventTableLength; i++ )
+ {
+ musicEventTable[i].nameKey = ::radMakeCaseInsensitiveKey32( musicEventTable[i].name );
+ }
+
+ //
+ // For the mission and race tables, generate strings and store the keys
+ //
+ tableIndex = 0;
+ for( i = 0; i < 9; i++ ) // min. 8 missions per level + bonus
+ {
+ if( i == 8 )
+ {
+ strcpy( prefix, "Bonus" );
+ }
+ else
+ {
+ sprintf( prefix, "M%d", i );
+ }
+
+ sprintf( temp, "%s_start", prefix );
+ missionScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp );
+
+ sprintf( temp, "%s_drama", prefix );
+ missionScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp );
+
+ sprintf( temp, "%s_win", prefix );
+ missionScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp );
+
+ sprintf( temp, "%s_lose", prefix );
+ missionScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp );
+
+ sprintf( temp, "%s_get_out_of_car", prefix );
+ missionScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp );
+
+ sprintf( temp, "%s_10sec_to_go", prefix );
+ missionScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp );
+ }
+ //
+ // Record the length of this table
+ //
+ rAssert( tableIndex <= MISSION_TABLE_SIZE );
+ missionScriptTableLength = tableIndex;
+
+ //
+ // Just to be safe
+ //
+ for( i = missionScriptTableLength; i < MISSION_TABLE_SIZE; i++ )
+ {
+ missionScriptTable[i].nameKey = 0;
+ missionScriptTable[i].scriptIndex = NO_INDEX;
+ }
+
+ tableIndex = 0;
+ for( i = 0; i < 3; i++ ) // 3 race missions
+ {
+ sprintf( temp, "StreetRace0%d_start", i+1 );
+ raceScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp );
+
+ sprintf( temp, "StreetRace0%d_win", i+1 );
+ raceScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp );
+
+ sprintf( temp, "StreetRace0%d_lose", i+1 );
+ raceScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp );
+
+ sprintf( temp, "StreetRace0%d_getoutofcar", i+1 );
+ raceScriptTable[tableIndex++].nameKey = ::radMakeCaseInsensitiveKey32( temp );
+ }
+ //
+ // Record the length of this table
+ //
+ rAssert( tableIndex <= RACE_TABLE_SIZE );
+ raceScriptTableLength = tableIndex;
+
+ //
+ // Just to be safe one more time
+ //
+ for( i = raceScriptTableLength; i < RACE_TABLE_SIZE; i++ )
+ {
+ raceScriptTable[i].nameKey = 0;
+ raceScriptTable[i].scriptIndex = NO_INDEX;
+ }
+}
+
+//=============================================================================
+// MusicPlayer::buildEventTables
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::buildEventTables()
+{
+ unsigned int i, j;
+ unsigned int scriptLength;
+ char temp[256];
+ radKey32 tempKey;
+ bool found;
+ unsigned int numStates;
+ unsigned int numStateEvents;
+ radKey32 stateKey;
+ radKey32 stateEventKey;
+ unsigned int currentTableLine;
+
+ //
+ // First, clear the old values out
+ //
+ for( i = 0; i < musicEventTableLength; i++ )
+ {
+ musicEventTable[i].scriptIndex = NO_INDEX;
+ }
+
+ for( i = 0; i < MISSION_TABLE_SIZE; i++ )
+ {
+ missionScriptTable[i].scriptIndex = NO_INDEX;
+ }
+
+ for( i = 0; i < RACE_TABLE_SIZE; i++ )
+ {
+ raceScriptTable[i].scriptIndex = NO_INDEX;
+ }
+
+ for( i = 0; i < MATRIX_TABLE_SIZE; i++ )
+ {
+ matrixStateTable[i].stateNameKey = 0;
+ matrixStateTable[i].stateIndex = NO_INDEX;
+ matrixStateTable[i].stateValueNameKey = 0;
+ matrixStateTable[i].stateValueIndex = NO_INDEX;
+ }
+
+ //
+ // Now, go through each event in the script and search for a match
+ // in the tables. If one is found, fill in its index
+ //
+ scriptLength = radmusic::performance_num_events( m_musicPerformance );
+ for( i = 0; i < scriptLength; i++ )
+ {
+ radmusic::performance_event_name( m_musicPerformance, i, temp, 256 );
+ tempKey = ::radMakeCaseInsensitiveKey32( temp );
+
+ found = false;
+
+ //
+ // Check standard music events first
+ //
+ for( j = 0; j < musicEventTableLength; j++ )
+ {
+ if( musicEventTable[j].nameKey == tempKey )
+ {
+ musicEventTable[j].scriptIndex = i;
+ found = true;
+ break;
+ }
+ }
+
+ //
+ // If not there, try mission events
+ //
+ if( !found )
+ {
+ for( j = 0; j < missionScriptTableLength; j++ )
+ {
+ if( missionScriptTable[j].nameKey == tempKey )
+ {
+ missionScriptTable[j].scriptIndex = i;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ //
+ // Finally, try race events
+ //
+ if( !found )
+ {
+ for( j = 0; j < raceScriptTableLength; j++ )
+ {
+ if( raceScriptTable[j].nameKey == tempKey )
+ {
+ raceScriptTable[j].scriptIndex = i;
+ found = true;
+ break;
+ }
+ }
+ }
+
+ //
+ // If we get here without a match, Marc's got an event we're not using
+ // anywhere yet. Assert on this at some point once the script appears
+ // stabilized.
+ //
+ //rAssert( found );
+ }
+
+ //
+ // Now, build the matrix event table. Go through all the states, and make
+ // a table of all the state/event pairs
+ //
+ currentTableLine = 0;
+
+ numStates = radmusic::performance_num_states( m_musicPerformance );
+ for( i = 0; i < numStates; i++ )
+ {
+ radmusic::performance_state_name( m_musicPerformance, i, temp, 256 );
+ stateKey = ::radMakeCaseInsensitiveKey32( temp );
+
+ numStateEvents = radmusic::performance_num_state_values( m_musicPerformance, i );
+ for( j = 0; j < numStateEvents; j++ )
+ {
+ radmusic::performance_state_value_name( m_musicPerformance, i, j, temp, 256 );
+ stateEventKey = ::radMakeCaseInsensitiveKey32( temp );
+
+ rAssert( currentTableLine < MATRIX_TABLE_SIZE );
+
+ matrixStateTable[currentTableLine].stateIndex = i;
+ matrixStateTable[currentTableLine].stateValueIndex = j;
+ matrixStateTable[currentTableLine].stateNameKey = stateKey;
+ matrixStateTable[currentTableLine++].stateValueNameKey = stateEventKey;
+ }
+ }
+
+ matrixStateTableLength = currentTableLine;
+}
+
+//=============================================================================
+// MusicPlayer::triggerMusicMissionEventByName
+//=============================================================================
+// Description: Trigger a music event by matching the key to the event names
+// in the mission event table
+//
+// Parameters: key - radKey for the name of the event to trigger
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::triggerMusicMissionEventByName( radKey32* key )
+{
+ unsigned int i;
+
+ rAssert( key != NULL );
+
+ if( !(CommandLineOptions::Get( CLO_NO_MUSIC )) )
+ {
+ for( i = 0; i < missionScriptTableLength; i++ )
+ {
+ if( missionScriptTable[i].nameKey == *key )
+ {
+ if( missionScriptTable[i].scriptIndex != NO_INDEX )
+ {
+ radmusic::performance_trigger_event( m_musicPerformance,
+ missionScriptTable[i].scriptIndex );
+ }
+ break;
+ }
+ }
+ }
+}
+
+//=============================================================================
+// MusicPlayer::triggerMusicStateChange
+//=============================================================================
+// Description: Change the radMusic state
+//
+// Parameters: stateKey - name of state
+// stateEventKey - name of event
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::triggerMusicStateChange( radKey32 stateKey, radKey32 stateEventKey )
+{
+ unsigned int i;
+
+ //
+ // Look for a match in the matrix table
+ //
+ for( i = 0; i < matrixStateTableLength; i++ )
+ {
+ if( ( matrixStateTable[i].stateNameKey == stateKey )
+ && ( matrixStateTable[i].stateValueNameKey == stateEventKey ) )
+ {
+ //
+ // Match, trigger the state change
+ //
+ radmusic::performance_state_value( m_musicPerformance,
+ matrixStateTable[i].stateIndex,
+ matrixStateTable[i].stateValueIndex );
+
+ //
+ // We need to trigger an event to prompt the change. Presumably
+ // we do this only for in-car stuff.
+ //
+ if( m_isInCar )
+ {
+ TriggerMusicEvent( MEVENT_MISSION_START );
+ }
+ break;
+ }
+ }
+
+ if( i == matrixStateTableLength )
+ {
+ rAssertMsg( false, "Mission script calling non-existent radMusic state" );
+ }
+}
+
+//=============================================================================
+// MusicPlayer::triggerCurrentInteriorAmbience
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void MusicPlayer::triggerCurrentInteriorAmbience()
+{
+ int interiorIndex;
+
+ interiorIndex = calculateInteriorIndex( GetInteriorManager()->GetInterior() );
+ turnAmbienceOn( AEVENT_INTERIOR_KWIK_E_MART + interiorIndex );
+} \ No newline at end of file
diff --git a/game/code/sound/music/musicplayer.h b/game/code/sound/music/musicplayer.h
new file mode 100644
index 0000000..046c0a3
--- /dev/null
+++ b/game/code/sound/music/musicplayer.h
@@ -0,0 +1,344 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: musicplayer.h
+//
+// Description: Declaration of the MusicPlayer class. Plays all music in
+// the game using RadMusic.
+//
+// History: 17/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef MUSICPLAYER_H
+#define MUSICPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/p3dtypes.hpp>
+
+#include <events/eventlistener.h>
+#include <render/Enums/RenderEnums.h>
+
+//========================================
+// Forward References
+//========================================
+
+namespace Sound
+{
+ struct IDaSoundTuner;
+}
+namespace radmusic
+{
+ struct performance;
+ struct composition;
+}
+
+class SoundFileHandler;
+class radLoadRequest;
+class Actor;
+
+//
+// Events for interactive music.
+//
+enum MusicEventList
+{
+ MEVENT_MOVIE,
+
+ MEVENT_PAUSE,
+ MEVENT_UNPAUSE,
+
+ MEVENT_FE,
+
+ MEVENT_LOADING_SCREEN,
+ MEVENT_NEWSPAPER_SPIN,
+
+ MEVENT_SUPERSPRINT,
+ MEVENT_SUPERSPRINT_WIN,
+ MEVENT_SUPERSPRINT_LOSE,
+
+ MEVENT_LEVEL_INTRO,
+
+ MEVENT_SUNDAY_DRIVE_START,
+ MEVENT_SUNDAY_DRIVE_GET_OUT_OF_CAR,
+
+ MEVENT_STORE,
+
+ MEVENT_ENTER_STONECUTTER_TUNNEL,
+ MEVENT_EXIT_STONECUTTER_TUNNEL,
+
+ MEVENT_OF_EXPLORE_MISSION,
+ MEVENT_OF_FOUND_CARD,
+ MEVENT_OF_TIME_OUT,
+
+ MEVENT_APU_OASIS,
+
+ MEVENT_INTERIOR_HOUSE,
+ MEVENT_INTERIOR_KWIK_E_MART,
+ MEVENT_INTERIOR_SCHOOL,
+ MEVENT_INTERIOR_MOES,
+ MEVENT_INTERIOR_DMV,
+ MEVENT_INTERIOR_ANDROID_DUNGEON,
+ MEVENT_INTERIOR_OBSERVATORY,
+
+ MEVENT_WIN_3_RACES,
+ MEVENT_LEVEL_COMPLETED,
+ MEVENT_DESTROY_CAMERA_BONUS,
+
+ MEVENT_STONECUTTER_SONG,
+ MEVENT_LBC_SONG,
+ MEVENT_DUFF_SONG,
+
+ MEVENT_HIT_AND_RUN_START,
+ MEVENT_HIT_AND_RUN_CAUGHT,
+
+ MEVENT_WASP_ATTACK,
+
+ MEVENT_GATED_MISSION,
+
+ MEVENT_SCARYMUSIC,
+
+ MEVENT_CREDITS,
+
+ MEVENT_END_STANDARD_EVENTS,
+
+ MEVENT_MISSION_START,
+ MEVENT_MISSION_DRAMA,
+ MEVENT_WIN_MISSION,
+ MEVENT_LOSE_MISSION,
+ MEVENT_GET_OUT_OF_CAR,
+ MEVENT_10_SEC_TO_GO,
+
+ MEVENT_END_MISSION_EVENTS,
+
+ MEVENT_STREETRACE_START,
+ MEVENT_STREETRACE_WIN,
+ MEVENT_STREETRACE_LOSE,
+ MEVENT_STREETRACE_GET_OUT_OF_CAR,
+
+ MEVENT_END_RACE_EVENTS,
+
+ MEVENT_NUM_EVENTS
+};
+
+//
+// Events for ambient sound script. Not actually stored anywhere, but it gives
+// me some identifiers to play with
+//
+enum AmbientEventList
+{
+ AEVENT_FRONTEND,
+
+ AEVENT_MOVIE,
+ AEVENT_PAUSE,
+ AEVENT_UNPAUSE,
+
+ AEVENT_TRIGGER_START,
+
+ AEVENT_TRIGGER_END = 73,
+
+ AEVENT_INTERIOR_KWIK_E_MART,
+ AEVENT_INTERIOR_SCHOOL,
+ AEVENT_INTERIOR_HOUSE,
+ AEVENT_INTERIOR_KRUSTYLU,
+ AEVENT_INTERIOR_DMV,
+
+ AEVENT_NUM_EVENTS
+};
+
+//=============================================================================
+//
+// Synopsis: MusicPlayer
+//
+//=============================================================================
+
+class MusicPlayer : public EventListener
+{
+ public:
+ MusicPlayer( Sound::IDaSoundTuner& tuner );
+ virtual ~MusicPlayer();
+
+ void Service();
+
+ void QueueRadmusicScriptLoad();
+ void QueueMusicLevelLoad( RenderEnums::LevelEnum level );
+
+ //
+ // Notify loading system when script file load complete
+ //
+ void LoadRadmusicScript( const char* filename, SoundFileHandler* fileHandler );
+ void UnloadRadmusicScript();
+
+ //
+ // Look for notification when the player gets in and out of the car
+ //
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ //
+ // Functions for notification of change in game state
+ //
+ void OnFrontEndStart();
+ void OnFrontEndFinish();
+
+ void OnGameplayStart( bool playerInCar );
+ void OnGameplayFinish();
+
+ void OnPauseStart();
+ void OnPauseEnd();
+
+ void OnStoreStart();
+ void OnStoreEnd();
+
+ void StopForMovie();
+ void ResumeAfterMovie();
+
+ bool IsStoppedForMovie();
+
+ void RestartSupersprintMusic();
+
+ //
+ // Volume controls
+ //
+ float GetVolume();
+ void SetVolume( float volume );
+ float GetAmbienceVolume();
+ void SetAmbienceVolume( float volume );
+
+ //
+ // Beat values
+ //
+ float GetBeatValue();
+
+ private:
+ //Prevent wasteful constructor creation.
+ MusicPlayer();
+ MusicPlayer( const MusicPlayer& original );
+ MusicPlayer& operator=( const MusicPlayer& rhs );
+
+ void SetUpPerformance(
+ radmusic::performance **,
+ radmusic::composition **,
+ const char * searchPath );
+
+ void TriggerMusicEvent( MusicEventList event );
+ void TriggerAmbientEvent( unsigned int event );
+ void triggerMusicMissionEventByName( radKey32* key );
+
+ unsigned int m_lastServiceTime;
+
+ // Needed for polling the composition loader
+ bool m_isLoadingMusic;
+
+ // True if player currently in car, false otherwise
+ bool m_isInCar;
+
+ // Composition loader
+
+ radLoadRequest* m_radLoadRequest;
+ char n_currentLoadName[ 64 ]; // hack for radload memory leak.
+
+ // Performance object
+ radmusic::performance * m_musicPerformance;
+ radmusic::composition * m_musicComposition;
+
+ //
+ // Ambient script stuff
+ //
+ bool m_isLoadingAmbient;
+
+ radmusic::performance * m_ambientPerformance;
+ radmusic::composition * m_ambientComposition;
+
+ // File load completion callback object
+ SoundFileHandler* m_loadCompleteCallback;
+
+ // Current ambient sound
+ unsigned int m_currentAmbient;
+ bool m_ambiencePlaying;
+
+ //
+ // Special music cases
+ //
+ bool m_onApuRooftop;
+ bool m_stoneCutterSong;
+ bool m_LBCSong;
+
+ //
+ // Delayed music start
+ //
+ bool m_delayedMusicStart;
+
+ //
+ // Wasp that triggered music
+ //
+ Actor* m_wasp;
+
+ //
+ // Calculate offset for current level
+ //
+ int calculateLevelIndex();
+
+ //
+ // Calculate offset for current mission
+ //
+ int calculateMissionIndex();
+
+ //
+ // Calculate offset for interior we're about to enter
+ //
+ int calculateInteriorIndex( tUID interiorID );
+
+ //
+ // Returns flag indicating whether we use ambient on-foot stuff for
+ // this stage
+ //
+ bool musicLockedOnForStage();
+
+ //
+ // Trigger the events to start music
+ //
+ void startMusic();
+
+ //
+ // Figure out what to do after the mission ends
+ //
+ void playPostMissionSounds();
+
+ //
+ // Turn ambient sound on and off
+ //
+ void turnAmbienceOn( unsigned int event );
+ void turnAmbienceOff( unsigned int event );
+
+ //
+ // True if current mission is a race, false otherwise
+ //
+ bool currentMissionIsRace();
+
+ //
+ // True if we're in Sunday Drive, false otherwise
+ //
+ bool currentMissionIsSundayDrive();
+
+ //
+ // Construct tables for faster lookup of music events in script
+ //
+ void initializeTableNameKeys();
+ void buildEventTables();
+
+ //
+ // Change the radMusic state
+ //
+ void triggerMusicStateChange( radKey32 stateKey, radKey32 stateEventKey );
+
+ int findStandardMusicEvent( MusicEventList event );
+ int findMissionEvent( MusicEventList event, int mission );
+ int findRaceEvent( MusicEventList event, int race );
+
+ void triggerCurrentInteriorAmbience();
+};
+
+
+#endif // MUSICPLAYER_H
+
diff --git a/game/code/sound/nis/allnissound.cpp b/game/code/sound/nis/allnissound.cpp
new file mode 100644
index 0000000..b943327
--- /dev/null
+++ b/game/code/sound/nis/allnissound.cpp
@@ -0,0 +1 @@
+#include <sound/nis/nissoundplayer.cpp>
diff --git a/game/code/sound/nis/nissoundplayer.cpp b/game/code/sound/nis/nissoundplayer.cpp
new file mode 100644
index 0000000..14d118e
--- /dev/null
+++ b/game/code/sound/nis/nissoundplayer.cpp
@@ -0,0 +1,388 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: nissoundplayer.cpp
+//
+// Description: Implement NISSoundPlayer, which interacts with the dialog
+// system to provide NIS sounds.
+//
+// History: 10/5/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/nis/nissoundplayer.h>
+
+#include <sound/soundmanager.h>
+
+#include <events/eventmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Table of NIS resources.
+//
+
+NISPlayerGroup::NISPlayerGroup() :
+ m_soundID( 0 ),
+ m_loadCallback( NULL ),
+ m_playCallback( NULL ),
+ m_soundQueued( false )
+{
+}
+
+NISPlayerGroup::~NISPlayerGroup()
+{
+}
+
+void NISPlayerGroup::LoadSound( radKey32 soundID, NISSoundLoadedCallback* callback )
+{
+ //
+ // For NIS playback, don't use buffered data sources. Hopefully we
+ // won't be play these in situations where skipping is likely. If it
+ // happens, then we can experiment with short buffers, I suppose.
+ // Stinky limited IOP memory.
+ //
+ m_soundQueued = m_player.QueueSound( soundID, this );
+
+ if( m_soundQueued )
+ {
+ m_loadCallback = callback;
+ m_soundID = soundID;
+ }
+}
+
+void NISPlayerGroup::PlaySound( rmt::Box3D* box, NISSoundPlaybackCompleteCallback* callback )
+{
+ radSoundVector position;
+ rmt::Vector midpoint;
+
+ if( m_soundQueued )
+ {
+ //
+ // Calculate position to play at
+ //
+ rAssert( box != NULL );
+ midpoint = box->Mid();
+ position.SetElements( midpoint.x, midpoint.y, midpoint.z );
+
+ m_playCallback = callback;
+ m_player.PlayQueuedSound( position, this );
+ }
+}
+
+void NISPlayerGroup::StopAndDumpSound()
+{
+ // Just in case.
+ m_loadCallback = NULL;
+ m_playCallback = NULL;
+ m_soundQueued = false;
+ m_soundID = 0;
+
+ m_player.Stop();
+}
+
+bool NISPlayerGroup::IsSoundIDLoaded( radKey32 soundID )
+{
+ return( m_soundID == soundID );
+}
+
+void NISPlayerGroup::Continue()
+{
+ if( m_player.IsPaused() )
+ {
+ m_player.Continue();
+ }
+}
+
+//=============================================================================
+// NISPlayerGroup::OnSoundReady
+//=============================================================================
+// Description: Called from m_player when sound is cued. Notify the
+// callback object
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void NISPlayerGroup::OnSoundReady()
+{
+ if( m_loadCallback )
+ {
+ m_loadCallback->NISSoundLoaded();
+ m_loadCallback = NULL;
+ }
+}
+
+//=============================================================================
+// NISPlayerGroup::OnPlaybackComplete
+//=============================================================================
+// Description: Called from m_player when playback is done. Notify the
+// callback object
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void NISPlayerGroup::OnPlaybackComplete()
+{
+ if( m_playCallback )
+ {
+ m_playCallback->NISSoundPlaybackComplete();
+ m_playCallback = NULL;
+ }
+
+ m_soundID = 0;
+}
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// NISSoundPlayer::NISSoundPlayer
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+NISSoundPlayer::NISSoundPlayer() :
+ m_currentFEGag( 0 )
+{
+ //
+ // Register as an event listener
+ //
+ GetEventManager()->AddListener( this, EVENT_FE_GAG_INIT );
+ GetEventManager()->AddListener( this, EVENT_FE_GAG_START );
+ GetEventManager()->AddListener( this, EVENT_FE_GAG_STOP );
+}
+
+//==============================================================================
+// NISSoundPlayer::~NISSoundPlayer
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+NISSoundPlayer::~NISSoundPlayer()
+{
+}
+
+//=============================================================================
+// NISSoundPlayer::LoadNISSound
+//=============================================================================
+// Description: Cue an NIS sound for playback
+//
+// Parameters: NISSound - sound to load
+// callback - object to notify when loading is complete
+//
+// Return: void
+//
+//=============================================================================
+void NISSoundPlayer::LoadNISSound( radKey32 NISSoundID, NISSoundLoadedCallback* callback )
+{
+ unsigned int i;
+
+ for( i = 0; i < NUM_NIS_PLAYERS; i++ )
+ {
+ if( m_NISPlayers[i].IsFree() )
+ {
+ m_NISPlayers[i].LoadSound( NISSoundID, callback );
+ break;
+ }
+ }
+
+ rAssert( i < NUM_NIS_PLAYERS );
+}
+
+//=============================================================================
+// NISSoundPlayer::PlayNISSound
+//=============================================================================
+// Description: Play the cued NIS sound
+//
+// Parameters: callback - object to notify when playback is complete
+//
+// Return: void
+//
+//=============================================================================
+void NISSoundPlayer::PlayNISSound( radKey32 NISSoundID, rmt::Box3D* box, NISSoundPlaybackCompleteCallback* callback )
+{
+ unsigned int i;
+
+ if( NISSoundID == 0 )
+ {
+ rDebugString( "Attempting to play NIS sound where no sound file specified. Ignored.\n" );
+ }
+ else
+ {
+ for( i = 0; i < NUM_NIS_PLAYERS; i++ )
+ {
+ if( m_NISPlayers[i].IsSoundIDLoaded( NISSoundID ) )
+ {
+ m_NISPlayers[i].PlaySound( box, callback );
+ break;
+ }
+ }
+
+ if( i >= NUM_NIS_PLAYERS )
+ {
+ rTuneString( "Attempting to play NIS sound which wasn't loaded. Tell Cory.\n" );
+ //rTuneAssert( false );
+ }
+ }
+}
+
+//=============================================================================
+// NISSoundPlayer::StopAndDumpNISSound
+//=============================================================================
+// Description: Stop the NIS sound if it's playing
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void NISSoundPlayer::StopAndDumpNISSound( radKey32 NISSoundID )
+{
+ unsigned int i;
+
+ for( i = 0; i < NUM_NIS_PLAYERS; i++ )
+ {
+ if( m_NISPlayers[i].IsSoundIDLoaded( NISSoundID ) )
+ {
+ m_NISPlayers[i].StopAndDumpSound();
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// NISSoundPlayer::PauseAllNISPlayers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NISSoundPlayer::PauseAllNISPlayers()
+{
+ unsigned int i;
+
+ for( i = 0; i < NUM_NIS_PLAYERS; i++ )
+ {
+ m_NISPlayers[i].Pause();
+ }
+}
+
+//=============================================================================
+// NISSoundPlayer::ContinueAllNISPlayers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void NISSoundPlayer::ContinueAllNISPlayers()
+{
+ unsigned int i;
+
+ for( i = 0; i < NUM_NIS_PLAYERS; i++ )
+ {
+ m_NISPlayers[i].Continue();
+ }
+}
+
+//=============================================================================
+// NISSoundPlayer::HandleEvent
+//=============================================================================
+// Description: Take care of NIS events thrown by the front end.
+//
+// Parameters: id - event ID
+// pEventData - FE gag name (radKey32) for EVENT_FE_GAG_INIT, unused for rest
+//
+// Return: void
+//
+//=============================================================================
+void NISSoundPlayer::HandleEvent( EventEnum id, void* pEventData )
+{
+ rmt::Box3D box;
+ rmt::Vector low;
+ rmt::Vector high;
+ radKey32 gagName = 0;
+
+ switch( id )
+ {
+ case EVENT_FE_GAG_INIT:
+ gagName = reinterpret_cast<radKey32>( pEventData );
+ loadFEGag( gagName );
+ break;
+
+ case EVENT_FE_GAG_START:
+ //
+ // Use a hardcoded position here for now. Not ideal, but the
+ // window isn't going anywhere soon.
+ //
+ low.Set( -2.04f, 1.9f, -0.2f );
+ high.Set( -2.0f, 1.96f, -0.1f );
+ box.Set( low, high );
+
+ PlayNISSound( m_currentFEGag, &box, NULL );
+ break;
+
+ case EVENT_FE_GAG_STOP:
+ StopAndDumpNISSound( m_currentFEGag );
+ break;
+
+ default:
+ rAssertMsg( false, "Huh? Shouldn't get an event here." );
+ break;
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// NISSoundPlayer::loadFEGag
+//=============================================================================
+// Description: Find the appropriate FE gag and prep it for playback
+//
+// Parameters: gagIndex - index for gag, sent from front end
+//
+// Return: void
+//
+//=============================================================================
+void NISSoundPlayer::loadFEGag( radKey32 gagKey )
+{
+ m_currentFEGag = gagKey;
+
+ LoadNISSound( gagKey, NULL );
+}
diff --git a/game/code/sound/nis/nissoundplayer.h b/game/code/sound/nis/nissoundplayer.h
new file mode 100644
index 0000000..e9475a6
--- /dev/null
+++ b/game/code/sound/nis/nissoundplayer.h
@@ -0,0 +1,115 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: nissoundplayer.h
+//
+// Description: Declare NISSoundPlayer, which interacts with the dialog
+// system to provide NIS sounds.
+//
+// History: 10/5/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef NISSOUNDPLAYER_H
+#define NISSOUNDPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+#include <sound/nisenum.h>
+#include <sound/positionalsoundplayer.h>
+
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+struct NISSoundLoadedCallback;
+struct NISSoundPlaybackCompleteCallback;
+
+//=============================================================================
+//
+// Synopsis: NISSoundPlayer
+//
+//=============================================================================
+
+class NISPlayerGroup : public SimpsonsSoundPlayerCallback
+{
+ public:
+ NISPlayerGroup();
+ virtual ~NISPlayerGroup();
+
+ void LoadSound( radKey32 soundID, NISSoundLoadedCallback* callback );
+ void PlaySound( rmt::Box3D* box, NISSoundPlaybackCompleteCallback* callback );
+ void StopAndDumpSound();
+ void Pause() { m_player.Pause(); }
+ void Continue();
+
+ bool IsSoundIDLoaded( radKey32 soundID );
+ bool IsFree() { return( m_soundID == 0 ); }
+
+ //
+ // SimpsonsSoundPlayerCallback interface functions
+ //
+ void OnSoundReady();
+ void OnPlaybackComplete();
+
+ protected:
+ private:
+ //Prevent wasteful constructor creation.
+ NISPlayerGroup( const NISPlayerGroup& original );
+ NISPlayerGroup& operator=( const NISPlayerGroup& rhs );
+
+ PositionalSoundPlayer m_player;
+
+ radKey32 m_soundID;
+
+ NISSoundLoadedCallback* m_loadCallback;
+ NISSoundPlaybackCompleteCallback* m_playCallback;
+
+ bool m_soundQueued;
+};
+
+//=============================================================================
+//
+// Synopsis: NISSoundPlayer
+//
+//=============================================================================
+
+class NISSoundPlayer : public EventListener
+{
+ public:
+ NISSoundPlayer();
+ virtual ~NISSoundPlayer();
+
+ void LoadNISSound( radKey32 NISSoundID, NISSoundLoadedCallback* callback );
+ void PlayNISSound( radKey32 NISSoundID, rmt::Box3D* box, NISSoundPlaybackCompleteCallback* callback );
+ void StopAndDumpNISSound( radKey32 NISSoundID );
+
+ void PauseAllNISPlayers();
+ void ContinueAllNISPlayers();
+
+ //
+ // EventListener functions
+ //
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ private:
+ //Prevent wasteful constructor creation.
+ NISSoundPlayer( const NISSoundPlayer& original );
+ NISSoundPlayer& operator=( const NISSoundPlayer& rhs );
+
+ void loadFEGag( radKey32 gagKey );
+
+ static const unsigned int NUM_NIS_PLAYERS = 6;
+
+ NISPlayerGroup m_NISPlayers[NUM_NIS_PLAYERS];
+
+ radKey32 m_currentFEGag;
+};
+
+
+#endif // NISSOUNDPLAYER_H
+
diff --git a/game/code/sound/nisenum.h b/game/code/sound/nisenum.h
new file mode 100644
index 0000000..f34949f
--- /dev/null
+++ b/game/code/sound/nisenum.h
@@ -0,0 +1,41 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: nisenum.h
+//
+// Description: Enumeration for the NIS sounds
+//
+// History: 10/5/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef NISENUM_H
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: nisenum.h
+//
+// Description: Enumeration of NIS sounds
+//
+// History: + Created -- Darren
+//
+//=============================================================================
+
+#define NISENUM_H
+
+enum NISSoundEnum
+{
+ MOLEMAN_GAG,
+ GRAMPA_GAG,
+ FRINK_GAG,
+ BARNEY_GAG,
+ MOE_GAG,
+ SNAKE_GAG,
+ SEA_CAPTAIN_GAG,
+ DR_NICK_GAG,
+
+ NIS_ENUM_COUNT
+};
+
+#endif // NISENUM_H
+
diff --git a/game/code/sound/positionalsoundplayer.cpp b/game/code/sound/positionalsoundplayer.cpp
new file mode 100644
index 0000000..f3693eb
--- /dev/null
+++ b/game/code/sound/positionalsoundplayer.cpp
@@ -0,0 +1,283 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: positionalsoundplayer.cpp
+//
+// Description: Implement PositionalSoundPlayer
+//
+// History: 12/18/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <sound/soundrenderer/soundplayer.h>
+//========================================
+// Project Includes
+//========================================
+#include <sound/positionalsoundplayer.h>
+
+#include <sound/soundfx/positionalsoundsettings.h>
+
+#include <memory/srrmemory.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+static const float POSITIONAL_PAUSE_BUFFER_DIST = 25.0f;
+
+//=============================================================================
+// PositionCarrier::PositionCarrier
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None
+//
+// Return: N/A
+//
+//=============================================================================
+PositionCarrier::PositionCarrier()
+{
+}
+
+//=============================================================================
+// PositionCarrier::~PositionCarrier
+//=============================================================================
+// Description: Destructor
+//
+// Parameters: None
+//
+// Return: N/A
+//
+//=============================================================================
+PositionCarrier::~PositionCarrier()
+{
+}
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// PositionalSoundPlayer::PositionalSoundPlayer
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+PositionalSoundPlayer::PositionalSoundPlayer( ) :
+ m_positionCarrier( NULL ),
+ m_positionalSettings( NULL ),
+ m_minDist( 3.0f ),
+ m_maxDist( 100.f ),
+ m_position( 0.0f, 0.0f, 0.0f ),
+ m_outOfRange( false )
+{
+ m_Type = Type_Positional;
+}
+
+//=============================================================================
+// PositionalSoundPlayer::~PositionalSoundPlayer
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+PositionalSoundPlayer::~PositionalSoundPlayer()
+{
+ delete m_positionCarrier;
+
+ if( m_positionalSettings != NULL )
+ {
+ m_positionalSettings->Release();
+ m_positionalSettings = NULL;
+ }
+}
+
+bool PositionalSoundPlayer::PlayResource( IDaSoundResource* resource,
+ SimpsonsSoundPlayerCallback* callback /* = NULL */)
+{
+ bool canPlay;
+
+ canPlay = QueueSound( resource, callback );
+ if( canPlay )
+ {
+ //
+ // m_position should have been set in an earlier call to SetPosition.
+ // If not, then it should be a moving sound and should therefore
+ // get set later by the service function.
+ //
+ PlayQueuedSound( m_position, callback );
+ }
+
+ return( canPlay );
+}
+
+//=============================================================================
+// PositionalSoundPlayer::PlayQueuedSound
+//=============================================================================
+// Description: Sets the position for the sound, then calls PlayQueuedSound
+// in the SimpsonsSoundPlayer base class
+//
+// Parameters: position - position that sound is to be played at
+// callback - object to inform when playback complete
+//
+// Return: void
+//
+//=============================================================================
+void PositionalSoundPlayer::PlayQueuedSound( radSoundVector& position,
+ SimpsonsSoundPlayerCallback* callback )
+{
+ //
+ // Before playing the sound, create a positional group and set the position.
+ // The group is reference counted, so the player will release it when
+ // the sound resource is released.
+ //
+
+ if ( m_playa )
+ {
+ radSoundVector velocity( 0.0f, 0.0f, 0.0f );
+
+ m_playa->SetPositionAndVelocity( & position, & velocity );
+ m_playa->SetMinMaxDistance( m_minDist, m_maxDist );
+ }
+
+ SimpsonsSoundPlayer::PlayQueuedSound( callback );
+}
+
+void PositionalSoundPlayer::ServiceOncePerFrame()
+{
+ radSoundVector position;
+ radSoundVector velocity;
+
+ radSoundVector distanceVector;
+ radSoundVector soundPosition;
+ radSoundVector listenerPosition;
+ float positionalMax;
+ float distance;
+
+ //
+ // Update the positional group with new vehicle position
+ //
+ if( ( m_positionCarrier != NULL ) )
+ {
+ m_positionCarrier->GetPosition( position );
+ m_positionCarrier->GetVelocity( velocity );
+
+ if ( m_playa )
+ {
+ m_playa->SetPositionAndVelocity( & position, & velocity );
+ }
+ }
+
+ //
+ // Do unpause/pause on players when they get in and out of range
+ //
+ if( ( m_playa != NULL ) && ( m_positionalSettings != NULL ) )
+ {
+ ::radSoundHalListenerGet()->GetPosition( &listenerPosition );
+ m_playa->GetPositionalGroup()->GetPosition( &soundPosition );
+ distanceVector = listenerPosition - soundPosition;
+ distance = distanceVector.GetLength();
+
+ positionalMax = m_positionalSettings->GetMaxDistance();
+
+ if( m_outOfRange && ( distance <= ( positionalMax + POSITIONAL_PAUSE_BUFFER_DIST ) ) )
+ {
+ if( IsPaused() )
+ {
+ Continue();
+ }
+ m_outOfRange = false;
+ }
+ else if( ( !m_outOfRange ) && ( distance > ( positionalMax + POSITIONAL_PAUSE_BUFFER_DIST ) ) )
+ {
+ Pause();
+ m_outOfRange = true;
+ }
+ }
+}
+
+void PositionalSoundPlayer::SetPositionCarrier( PositionCarrier& movingSound )
+{
+ m_positionCarrier = &movingSound;
+}
+
+//=============================================================================
+// PositionalSoundPlayer::SetParameters
+//=============================================================================
+// Description: Set the min/max and whatever else from the tunable
+// sound settings object
+//
+// Parameters: settings - object containing sound settings
+//
+// Return: void
+//
+//=============================================================================
+void PositionalSoundPlayer::SetParameters( positionalSoundSettings* settings )
+{
+ m_minDist = settings->GetMinDistance();
+ m_maxDist = settings->GetMaxDistance();
+
+ m_positionalSettings = settings;
+ m_positionalSettings->AddRef();
+
+ if( m_playa != NULL )
+ {
+ m_playa->SetMinMaxDistance( m_minDist, m_maxDist );
+ }
+}
+
+//=============================================================================
+// PositionalSoundPlayer::SetPosition
+//=============================================================================
+// Description: Set the position for the sound
+//
+// Parameters: x,y,z - position
+//
+// Return: void
+//
+//=============================================================================
+void PositionalSoundPlayer::SetPosition( float x, float y, float z )
+{
+ m_position.SetElements( x, y, z );
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+void PositionalSoundPlayer::dumpSoundPlayer()
+{
+ //
+ // Get rid of the positional group
+ //
+
+ if( m_positionalSettings != NULL )
+ {
+ m_positionalSettings->Release();
+ m_positionalSettings = NULL;
+ }
+
+ m_positionCarrier = NULL;
+
+ //
+ // Let the parent clean up now
+ //
+ SimpsonsSoundPlayer::dumpSoundPlayer();
+} \ No newline at end of file
diff --git a/game/code/sound/positionalsoundplayer.h b/game/code/sound/positionalsoundplayer.h
new file mode 100644
index 0000000..dbc1032
--- /dev/null
+++ b/game/code/sound/positionalsoundplayer.h
@@ -0,0 +1,109 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: positionalsoundplayer.h
+//
+// Description: Declaration of wrapper class for playing positional sounds
+//
+// History: 12/18/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef POSITIONALSOUNDPLAYER_H
+#define POSITIONALSOUNDPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radsoundmath.hpp>
+
+#include <sound/simpsonssoundplayer.h>
+
+//========================================
+// Forward References
+//========================================
+class positionalSoundSettings;
+struct IRadSoundHalPositionalGroup;
+
+//=============================================================================
+//
+// Synopsis: PositionCarrier
+//
+//=============================================================================
+
+class PositionCarrier
+{
+ public:
+ PositionCarrier();
+ virtual ~PositionCarrier();
+
+ virtual void GetPosition( radSoundVector& position ) = 0;
+ virtual void GetVelocity( radSoundVector& velocity ) = 0;
+
+ protected:
+ private:
+ //Prevent wasteful constructor creation.
+ PositionCarrier( const PositionCarrier& positioncarrier );
+ PositionCarrier& operator=( const PositionCarrier& positioncarrier );
+};
+
+//=============================================================================
+//
+// Synopsis: PositionalSoundPlayer
+//
+//=============================================================================
+
+class PositionalSoundPlayer : public SimpsonsSoundPlayer
+{
+ public:
+ PositionalSoundPlayer( );
+ virtual ~PositionalSoundPlayer();
+
+ bool PlayResource( IDaSoundResource* resource,
+ SimpsonsSoundPlayerCallback* callback = NULL );
+ void PlayQueuedSound( radSoundVector& position,
+ SimpsonsSoundPlayerCallback* callback = NULL );
+
+ void SetPositionCarrier( PositionCarrier& movingSound );
+ void UnsetPositionCarrier();
+
+ void SetParameters( positionalSoundSettings* settings );
+ positionalSoundSettings* GetParameters() { return( m_positionalSettings ); }
+
+ void ServiceOncePerFrame();
+
+ void SetPosition( float x, float y, float z );
+
+ protected:
+ //
+ // Called when we're done with the sound renderer player object
+ //
+ void dumpSoundPlayer();
+
+ private:
+ //Prevent wasteful constructor creation.
+ PositionalSoundPlayer( const PositionalSoundPlayer& positionalsoundplayer );
+ PositionalSoundPlayer& operator=( const PositionalSoundPlayer& positionalsoundplayer );
+
+ //
+ // Pointer to sound source, used only if source is moving
+ //
+ PositionCarrier* m_positionCarrier;
+
+ positionalSoundSettings* m_positionalSettings;
+
+ float m_minDist;
+ float m_maxDist;
+
+ radSoundVector m_position;
+
+ bool m_outOfRange;
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //POSITIONALSOUNDPLAYER_H
diff --git a/game/code/sound/simpsonssoundplayer.cpp b/game/code/sound/simpsonssoundplayer.cpp
new file mode 100644
index 0000000..a5eb0c7
--- /dev/null
+++ b/game/code/sound/simpsonssoundplayer.cpp
@@ -0,0 +1,539 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: simpsonssoundplayer.cpp
+//
+// Description: Implement SimpsonsSoundPlayer class, which interacts with
+// the sound renderer to play sounds, and tracks the player
+// resources in use.
+//
+// History: 29/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/simpsonssoundplayer.h>
+
+#include <sound/soundmanager.h>
+#include <sound/soundloader.h>
+#include <sound/soundrenderercallback.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/soundresourcemanager.h>
+#include <sound/soundrenderer/soundallocatedresource.h>
+#include <sound/soundrenderer/playermanager.h>
+#include <sound/soundrenderer/idasoundresource.h>
+
+#include <memory/srrmemory.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+unsigned int SimpsonsSoundPlayer::s_playersCreated = 0;
+unsigned int SimpsonsSoundPlayer::s_clipPlayersInUse = 0;
+unsigned int SimpsonsSoundPlayer::s_streamPlayersInUse = 0;
+Sound::daSoundResourceManager* SimpsonsSoundPlayer::s_resourceManager = NULL;
+Sound::daSoundPlayerManager* SimpsonsSoundPlayer::s_playerManager = NULL;
+SoundLoader* SimpsonsSoundPlayer::s_soundLoader = NULL;
+
+//
+// Limits on the number of players that can be playing clips/streams at once.
+// Numbers are arbitrary and subject to experimentation
+//
+static const unsigned int s_maxActiveClipPlayersAllowed = 25;
+static const unsigned int s_maxActiveStreamPlayersAllowed = 8;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SimpsonsSoundPlayer::SimpsonsSoundPlayer
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SimpsonsSoundPlayer::SimpsonsSoundPlayer() :
+ m_playa( NULL ),
+ m_callback( NULL )
+
+{
+ m_Type = Type_NonPositional;
+ //
+ // Get resource manager ptr and sound loader ptr if that hasn't been done yet
+ //
+ if( s_resourceManager == NULL )
+ {
+ s_resourceManager = Sound::daSoundRenderingManagerGet()->GetResourceManager();
+ }
+
+ if( s_playerManager == NULL )
+ {
+ s_playerManager = Sound::daSoundRenderingManagerGet()->GetPlayerManager();
+ }
+
+ if( s_soundLoader == NULL )
+ {
+ s_soundLoader = SoundManager::GetInstance()->GetSoundLoader();
+ }
+
+ //
+ // Update statistics
+ //
+ ++s_playersCreated;
+}
+
+//==============================================================================
+// SimpsonsSoundPlayer::~SimpsonsSoundPlayer
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SimpsonsSoundPlayer::~SimpsonsSoundPlayer()
+{
+ //
+ // Update statistics
+ //
+ --s_playersCreated;
+
+ if( m_callback != NULL )
+ {
+ m_callback->CancelGameCallbackAndRelease();
+ }
+
+ rAssert( m_playa == NULL );
+}
+
+//=============================================================================
+// SimpsonsSoundPlayer::PlaySound
+//=============================================================================
+// Description: Play the sound resource with the given name
+//
+// Parameters: resourceName - string with name of resource to play
+// callback - optional callback on playback completion
+//
+// Return: true if sound could be played, false otherwise
+//
+//=============================================================================
+bool SimpsonsSoundPlayer::PlaySound( const char* resourceName,
+ SimpsonsSoundPlayerCallback* callback )
+{
+ return( PlaySound( ::radMakeKey32( resourceName ), callback ) );
+}
+
+
+//=============================================================================
+// SimpsonsSoundPlayer::PlayResource
+//=============================================================================
+// Description: Play the sound resource
+//
+// Parameters: resource - resource to play
+// callback - object to notify when we're done
+//
+// Return: true if sound could be played, false otherwise
+//
+//=============================================================================
+bool SimpsonsSoundPlayer::PlayResource( IDaSoundResource* resource,
+ SimpsonsSoundPlayerCallback* callback /* = NULL */ )
+{
+ bool canPlay;
+
+ canPlay = QueueSound( resource, callback );
+ if( canPlay )
+ {
+ PlayQueuedSound();
+ }
+
+ return( canPlay );
+}
+
+//=============================================================================
+// SimpsonsSoundPlayer::PlaySound
+//=============================================================================
+// Description: Comment
+//
+// Parameters: resourceKey - hashed value of the name of the sound resource
+// to play
+//
+// Return: true if sound could be played, false otherwise
+//
+//=============================================================================
+bool SimpsonsSoundPlayer::PlaySound( Sound::daResourceKey resourceKey,
+ SimpsonsSoundPlayerCallback* callback )
+{
+ IDaSoundResource* resource;
+ bool retVal;
+
+ resource = s_resourceManager->FindResource( resourceKey );
+
+ if( resource != NULL )
+ {
+ retVal = PlayResource( resource, callback );
+ }
+ else
+ {
+ rDebugString( "Tried to play missing sound resource\n" );
+ retVal = false;
+ }
+
+ return( retVal );
+}
+
+//=============================================================================
+// SimpsonsSoundPlayer::QueueSound
+//=============================================================================
+// Description: Queue up a sound for playback, but don't play it. Useful
+// for streamed dialog
+//
+// Parameters: resourceName - name of sound resource to queue
+// callback - playback completion callback, unused if NULL
+// playUnbuffered - if streamer, don't use buffered data source
+//
+// Return: true if sound could be played, false otherwise
+//
+//=============================================================================
+bool SimpsonsSoundPlayer::QueueSound( radKey32 resourceID,
+ SimpsonsSoundPlayerCallback* callback )
+{
+ IDaSoundResource* resource = s_resourceManager->FindResource( resourceID );
+
+ if( resource != NULL )
+ {
+ return( QueueSound( resource, callback ) );
+ }
+ else
+ {
+ rDebugPrintf( "Couldn't play sound resource ID %d\n", resourceID );
+ return( false );
+ }
+}
+
+//=============================================================================
+// SimpsonsSoundPlayer::QueueSound
+//=============================================================================
+// Description: Queue up a sound for playback, but don't play it. Useful
+// for streamed dialog
+//
+// Parameters: resource - sound resource to queue
+// callback - playback completion callback, unused if NULL
+// playUnbuffered - if streamer, don't use buffered data source
+//
+// Return: true if sound could be played, false otherwise
+//
+//=============================================================================
+bool SimpsonsSoundPlayer::QueueSound( IDaSoundResource* resource,
+ SimpsonsSoundPlayerCallback* callback )
+{
+ if( m_playa != NULL )
+ {
+ rDebugString( "Dropped sound, player busy\n" );
+ return( false );
+ }
+
+ //
+ // Make sure we haven't maxed our limit on playback of this type of
+ // resource
+ //
+ if( resource->GetType() == IDaSoundResource::CLIP )
+ {
+ if( s_clipPlayersInUse < s_maxActiveClipPlayersAllowed )
+ {
+ ++s_clipPlayersInUse;
+ }
+ else
+ {
+ rAssertMsg( false, "Reached maximum allowable number of clip players\n" );
+ return( false );
+ }
+ }
+ else
+ {
+ rAssert( resource->GetType() == IDaSoundResource::STREAM );
+
+ if( s_streamPlayersInUse < s_maxActiveStreamPlayersAllowed )
+ {
+ ++s_streamPlayersInUse;
+ }
+ else
+ {
+ rAssertMsg( false, "Reached maximum allowable number of stream players\n" );
+ return( false );
+ }
+ }
+
+ s_playerManager->CaptureFreePlayer( &m_playa,
+ resource,
+ Type_Positional == m_Type );
+ rAssert( m_playa != NULL );
+
+ //
+ // Reset trim, just to be safe
+ //
+ m_playa->SetExternalTrim( 1.0f );
+
+ //
+ // Create a callback object and point it toward whoever wants the callback
+ //
+ if( ( callback != NULL ) && ( m_callback == NULL ) )
+ {
+ m_callback = new(GMA_TEMP) SoundRenderingPlayerCallback( *this, callback );
+ m_playa->RegisterSoundPlayerStateCallback( m_callback, NULL );
+ }
+
+ return( true );
+}
+
+//=============================================================================
+// SimpsonsSoundPlayer::PlayQueuedSound
+//=============================================================================
+// Description: Play the sound we've queued for playback
+//
+// Parameters: callback - callback to trigger on playback completion, ignored
+// if NULL
+//
+// Return: void
+//
+//=============================================================================
+void SimpsonsSoundPlayer::PlayQueuedSound( SimpsonsSoundPlayerCallback* callback )
+{
+ rAssert( m_playa != NULL );
+
+ //
+ // Play should not get stuck on pauses. If this causes problems,
+ // then I'll have to figure out how to make 100% sure that all
+ // paused streams (e.g. NIS) get unpaused in a proper fashion
+ //
+ if( m_playa->IsPaused() )
+ {
+ m_playa->UberContinue();
+ }
+
+ m_playa->Play();
+
+ //
+ // If requested, create a callback object and point it toward whoever
+ // wants the callback
+ //
+ if( m_callback == NULL )
+ {
+ m_callback = new(GMA_TEMP) SoundRenderingPlayerCallback( *this, callback );
+ m_playa->RegisterSoundPlayerStateCallback( m_callback, NULL );
+ }
+}
+
+//=============================================================================
+// SimpsonsSoundPlayer::OnPlaybackComplete
+//=============================================================================
+// Description: Callback from the sound renderer callback object when the
+// clip stops playback (not called for looping clips)
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SimpsonsSoundPlayer::OnPlaybackComplete()
+{
+ dumpSoundPlayer();
+}
+
+//=============================================================================
+// SimpsonsSoundPlayer::Stop
+//=============================================================================
+// Description: Stop playing sound.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SimpsonsSoundPlayer::Stop()
+{
+ if( m_playa != NULL )
+ {
+ //
+ // Stop() doesn't seem to play well with paused players. They tend
+ // to remain paused when you reuse them later.
+ //
+ if( m_playa->IsPaused() )
+ {
+ m_playa->Continue();
+ }
+
+ m_playa->Stop();
+
+ if( m_playa != NULL )
+ {
+ //
+ // The player didn't get released because we don't have
+ // a callback set (presumably), so we do it ourselves
+ //
+ dumpSoundPlayer();
+ }
+
+ rAssert( m_playa == NULL );
+ }
+}
+
+//=============================================================================
+// SimpsonsSoundPlayer::Pause
+//=============================================================================
+// Description: Pause the sound player
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SimpsonsSoundPlayer::Pause()
+{
+ if( m_playa != NULL )
+ {
+ m_playa->Pause();
+ }
+}
+
+//=============================================================================
+// SimpsonsSoundPlayer::Continue
+//=============================================================================
+// Description: Unpause the sound player
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SimpsonsSoundPlayer::Continue()
+{
+ if( m_playa != NULL )
+ {
+ m_playa->Continue();
+ }
+}
+
+//=============================================================================
+// SimpsonsSoundPlayer::IsPaused
+//=============================================================================
+// Description: Indicate whether the player is paused
+//
+// Parameters: None
+//
+// Return: true if paused, false if not or player unused
+//
+//=============================================================================
+bool SimpsonsSoundPlayer::IsPaused()
+{
+ if( m_playa != NULL )
+ {
+ return( m_playa->IsPaused() );
+ }
+ else
+ {
+ return( false );
+ }
+}
+
+//=============================================================================
+// SimpsonsSoundPlayer::SetPitch
+//=============================================================================
+// Description: Set the pitch of the currently playing clip
+//
+// Parameters: pitch - pitch setting to apply
+//
+// Return: void
+//
+//=============================================================================
+void SimpsonsSoundPlayer::SetPitch( float pitch )
+{
+ if( m_playa != NULL )
+ {
+ m_playa->SetPitch( pitch );
+ }
+ else
+ {
+ rDebugString( "Can't set pitch without associated player\n" );
+ }
+}
+
+//=============================================================================
+// SimpsonsSoundPlayer::SetTrim
+//=============================================================================
+// Description: Set the trim of the currently playing clip
+//
+// Parameters: trim - trim setting to apply
+//
+// Return: void
+//
+//=============================================================================
+void SimpsonsSoundPlayer::SetTrim( float trim )
+{
+ if( m_playa != NULL )
+ {
+ m_playa->SetExternalTrim( trim );
+ }
+ else
+ {
+ rDebugString( "Can't set trim without associated player\n" );
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// SimpsonsSoundPlayer::dumpSoundPlayer
+//=============================================================================
+// Description: To be called when we're done with the sound renderer's player
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SimpsonsSoundPlayer::dumpSoundPlayer()
+{
+ if( m_callback )
+ {
+ m_callback->CancelGameCallbackAndRelease();
+ m_playa->UnregisterSoundPlayerStateCallback( m_callback, NULL );
+ m_callback = NULL;
+ }
+
+ if( m_playa->GetPlayerType() == IDaSoundResource::CLIP )
+ {
+ --s_clipPlayersInUse;
+ }
+ else
+ {
+ rAssert( m_playa->GetPlayerType() != IDaSoundResource::UNKNOWN );
+ --s_streamPlayersInUse;
+ }
+
+ if( m_playa->IsCaptured() )
+ {
+ m_playa->UnCapture();
+ }
+ m_playa = NULL;
+} \ No newline at end of file
diff --git a/game/code/sound/simpsonssoundplayer.h b/game/code/sound/simpsonssoundplayer.h
new file mode 100644
index 0000000..98ad6b3
--- /dev/null
+++ b/game/code/sound/simpsonssoundplayer.h
@@ -0,0 +1,136 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: simpsonssoundplayer.h
+//
+// Description: Declaration for SimpsonsSoundPlayer class, which interacts with
+// the sound renderer to play sounds, and tracks the player
+// resources in use.
+//
+// History: 29/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SIMPSONSSOUNDPLAYER_H
+#define SIMPSONSSOUNDPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/soundrenderer/soundsystem.h>
+
+//========================================
+// Forward References
+//========================================
+namespace Sound
+{
+ class daSoundResourceManager;
+ class daSoundClipStreamPlayer;
+ class daSoundPlayerManager;
+}
+
+struct IDaSoundResource;
+class SoundLoader;
+class SoundRenderingPlayerCallback;
+struct IRadSoundHalPositionalGroup;
+
+//=============================================================================
+//
+// Synopsis: SimpsonsSoundPlayerCallback
+//
+//=============================================================================
+
+struct SimpsonsSoundPlayerCallback
+{
+ virtual void OnSoundReady() = 0;
+ virtual void OnPlaybackComplete() = 0;
+};
+
+//=============================================================================
+//
+// Synopsis: SimpsonsSoundPlayer
+//
+//=============================================================================
+
+class SimpsonsSoundPlayer
+{
+ public:
+ SimpsonsSoundPlayer();
+ virtual ~SimpsonsSoundPlayer();
+
+ bool PlaySound( const char* resourceName, SimpsonsSoundPlayerCallback* callback = NULL );
+
+ bool PlaySound( Sound::daResourceKey resourceKey, SimpsonsSoundPlayerCallback* callback = NULL );
+
+ virtual bool PlayResource( IDaSoundResource* resource,
+ SimpsonsSoundPlayerCallback* callback = NULL );
+
+ bool QueueSound( const char* resourceName,
+ SimpsonsSoundPlayerCallback* callback = NULL )
+ { return( QueueSound( ::radMakeKey32( resourceName ), callback ) ); }
+
+ bool QueueSound( radKey32 resourceKey,
+ SimpsonsSoundPlayerCallback* callback = NULL );
+
+ bool QueueSound( IDaSoundResource* resource,
+ SimpsonsSoundPlayerCallback* callback = NULL );
+
+ virtual void PlayQueuedSound( SimpsonsSoundPlayerCallback* callback = NULL );
+
+ void Stop();
+ void Pause();
+ void Continue();
+ bool IsPaused();
+
+ void OnPlaybackComplete();
+
+ bool IsInUse() { return( m_playa != NULL ); }
+
+ void SetPitch( float pitch );
+ void SetTrim( float trim );
+
+ protected:
+
+ //
+ // Sound renderer's player object
+ //
+ Sound::daSoundClipStreamPlayer* m_playa;
+
+ //
+ // Call when we're done with the sound renderer player object
+ //
+ virtual void dumpSoundPlayer();
+
+ protected:
+
+ enum Type { Type_Positional, Type_NonPositional } m_Type;
+
+ private:
+ //Prevent wasteful constructor creation.
+ SimpsonsSoundPlayer( const SimpsonsSoundPlayer& original );
+ SimpsonsSoundPlayer& operator=( const SimpsonsSoundPlayer& rhs );
+
+ //
+ // Sound renderer resource manager
+ //
+ static Sound::daSoundResourceManager* s_resourceManager;
+ static Sound::daSoundPlayerManager* s_playerManager;
+ static SoundLoader* s_soundLoader;
+
+ //
+ // Statistics on players in use
+ //
+ static unsigned int s_playersCreated;
+ static unsigned int s_clipPlayersInUse;
+ static unsigned int s_streamPlayersInUse;
+
+ //
+ // Callback object for playback completion
+ //
+ SoundRenderingPlayerCallback* m_callback;
+
+};
+
+
+#endif // SIMPSONSSOUNDPLAYER_H
+
diff --git a/game/code/sound/soundcluster.cpp b/game/code/sound/soundcluster.cpp
new file mode 100644
index 0000000..99e5d4f
--- /dev/null
+++ b/game/code/sound/soundcluster.cpp
@@ -0,0 +1,276 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundcluster.cpp
+//
+// Description: Implement SoundCluster
+//
+// History: 26/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radnamespace.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundcluster.h>
+
+#include <sound/soundrenderer/idasoundresource.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/soundresourcemanager.h>
+
+#include <loading/soundfilehandler.h>
+#include <memory/srrmemory.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+static const radKey32 NULL_SOUND_KEY = 0;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundCluster::SoundCluster
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundCluster::SoundCluster( int clusterIndex,
+ IRadNameSpace* soundNamespace ) :
+ m_isLoaded( false ),
+ m_namespace( soundNamespace ),
+ m_loadCompleteCallbackObj( NULL )
+{
+ int i;
+
+ //
+ // Initialize the sound list to zeroes, which is hopefully an unlikely radKey32 value
+ //
+ for( i = 0; i < MAX_RESOURCES; i++ )
+ {
+ m_soundList[i] = NULL_SOUND_KEY;
+ }
+
+#ifdef RAD_DEBUG
+ m_clusterIndex = clusterIndex;
+#endif
+}
+
+//==============================================================================
+// SoundCluster::~SoundCluster
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundCluster::~SoundCluster()
+{
+}
+
+//=============================================================================
+// SoundCluster::LoadSounds
+//=============================================================================
+// Description: Loads the sounds listed in m_soundList
+//
+// Parameters: none
+//
+// Return: void
+//
+//=============================================================================
+void SoundCluster::LoadSounds( SoundFileHandler* callbackObj /* = NULL */ )
+{
+ IDaSoundResource* resource;
+ int i;
+
+ MEMTRACK_PUSH_GROUP( "Sound" );
+
+ for( i = 0; i < MAX_RESOURCES; i++ )
+ {
+ if( m_soundList[i] != NULL_SOUND_KEY )
+ {
+ resource = reinterpret_cast< IDaSoundResource* >
+ (
+ m_namespace->GetInstance( m_soundList[i] )
+ );
+
+ if( resource != NULL )
+ {
+ resource->CaptureResource();
+ }
+#ifdef RAD_DEBUG
+ else
+ {
+ //
+ // Sound not found, spew debug message
+ //
+ rDebugPrintf( "Sound resource #%d couldn't be found in cluster #%d\n", i, m_clusterIndex );
+ }
+#endif
+ }
+ }
+
+ //
+ // Register for notification on load completion
+ //
+ Sound::daSoundRenderingManagerGet()->GetDynaLoadManager()->AddCompletionCallback( this, NULL );
+
+ m_loadCompleteCallbackObj = callbackObj;
+
+ //
+ // Tell the resource manager that we're using this namespace
+ //
+ Sound::daSoundRenderingManagerGet()->GetResourceManager()->SetActiveResource( m_namespace );
+
+ MEMTRACK_POP_GROUP( "Sound" );
+}
+
+//=============================================================================
+// SoundCluster::UnloadSounds
+//=============================================================================
+// Description: Unloads the sounds listed in m_soundList
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundCluster::UnloadSounds()
+{
+ IDaSoundResource* resource;
+ int i;
+
+ for( i = 0; i < MAX_RESOURCES; i++ )
+ {
+ if( m_soundList[i] != NULL_SOUND_KEY )
+ {
+ resource = reinterpret_cast< IDaSoundResource* >
+ (
+ m_namespace->GetInstance( m_soundList[i] )
+ );
+
+ if( resource != NULL )
+ {
+ resource->ReleaseResource();
+ }
+#ifdef RAD_DEBUG
+ else
+ {
+ //
+ // Sound not found, spew debug message
+ //
+ rDebugPrintf( "Tried to free unloaded sound resource #%d in cluster #%d\n", i, m_clusterIndex );
+ }
+#endif
+ }
+ }
+
+ //
+ // Tell the resource manager that we're no longer using this namespace
+ //
+ Sound::daSoundRenderingManagerGet()->GetResourceManager()->ReleaseActiveResource( m_namespace );
+
+
+ m_isLoaded = false;
+}
+
+//=============================================================================
+// SoundCluster::AddResource
+//=============================================================================
+// Description: Add given sound resource to the list of resources associated
+// with this cluster
+//
+// Parameters: resourceKey - radKey32 crunched from sound resource name
+//
+// Return: true if we could add it to the list, false otherwise
+//
+//=============================================================================
+bool SoundCluster::AddResource( Sound::daResourceKey resourceKey )
+{
+ int i;
+ bool added = false;
+
+ for( i = 0; i < MAX_RESOURCES; i++ )
+ {
+ if( m_soundList[i] == NULL_SOUND_KEY )
+ {
+ m_soundList[i] = resourceKey;
+ added = true;
+ break;
+ }
+ }
+
+ return( added );
+}
+
+//=============================================================================
+// SoundCluster::ContainsResource
+//=============================================================================
+// Description: Search the sound list and return a bool indicating whether
+// this sound cluster contains the indicated resource
+//
+// Parameters: resourceKey - hashed name of resource to search for
+//
+// Return: true if resource present, false otherwise
+//
+//=============================================================================
+bool SoundCluster::ContainsResource( Sound::daResourceKey resourceKey )
+{
+ int i;
+
+ rAssert( resourceKey != NULL_SOUND_KEY );
+
+ for( i = 0; i < MAX_RESOURCES; i++ )
+ {
+ if( m_soundList[i] == resourceKey )
+ {
+ return( true );
+ }
+ }
+
+ return( false );
+}
+
+//=============================================================================
+// SoundCluster::OnDynaLoadOperationsComplete
+//=============================================================================
+// Description: Called when the sound renderer is finished loading the cluster
+//
+// Parameters: pUserData - user data, unused
+//
+// Return: void
+//
+//=============================================================================
+void SoundCluster::OnDynaLoadOperationsComplete( void* pUserData )
+{
+ m_isLoaded = true;
+ if( m_loadCompleteCallbackObj != NULL )
+ {
+ m_loadCompleteCallbackObj->LoadCompleted();
+ m_loadCompleteCallbackObj = NULL;
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/sound/soundcluster.h b/game/code/sound/soundcluster.h
new file mode 100644
index 0000000..2abec0c
--- /dev/null
+++ b/game/code/sound/soundcluster.h
@@ -0,0 +1,103 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundcluster.h
+//
+// Description: Declaration of SoundCluster class. Used to allocate a group
+// of related sounds in sound memory. This is an abstract base
+// class, the subclasses are used to differentiate between clips
+// and streams.
+//
+// History: 26/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDCLUSTER_H
+#define SOUNDCLUSTER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundrenderer/sounddynaload.h>
+
+//========================================
+// Forward References
+//========================================
+
+class SoundFileHandler;
+struct IRadNameSpace;
+
+//=============================================================================
+//
+// Synopsis: SoundCluster
+//
+//=============================================================================
+
+class SoundCluster : public Sound::IDaSoundDynaLoadCompletionCallback,
+ public radRefCount
+{
+ public:
+ IMPLEMENT_REFCOUNTED( "SoundCluster" );
+
+ SoundCluster( int clusterIndex,
+ IRadNameSpace* soundNamespace );
+ virtual ~SoundCluster();
+
+ bool AddResource( const char* resourceName )
+ { return( AddResource( ::radMakeKey32( resourceName ) ) ); }
+ bool AddResource( Sound::daResourceKey resourceKey );
+
+ bool IsLoaded() const { return m_isLoaded; }
+
+ void LoadSounds( SoundFileHandler* callbackObj = NULL );
+ void UnloadSounds();
+
+ bool ContainsResource( const char* resourceName )
+ { return ContainsResource( ::radMakeKey32( resourceName ) ); }
+ bool ContainsResource( Sound::daResourceKey resourceKey );
+
+ IRadNameSpace* GetMyNamespace() { return( m_namespace ); }
+
+ //
+ // Interface for sound renderer load completion callback
+ //
+ void OnDynaLoadOperationsComplete( void* pUserData );
+
+ private:
+ //Prevent wasteful constructor creation.
+ SoundCluster();
+ SoundCluster( const SoundCluster& original );
+ SoundCluster& operator=( const SoundCluster& rhs );
+
+ static const int MAX_RESOURCES = 80;
+
+ //
+ // True if the sounds for this cluster are allocated in sound memory
+ //
+ bool m_isLoaded;
+
+ //
+ // List of sound clips
+ //
+ Sound::daResourceKey m_soundList[MAX_RESOURCES];
+
+ //
+ // Namespace containing these resources
+ //
+ IRadNameSpace* m_namespace;
+
+ //
+ // Callback object on load completion
+ //
+ SoundFileHandler* m_loadCompleteCallbackObj;
+
+#ifdef RAD_DEBUG
+ int m_clusterIndex;
+#endif
+};
+
+
+#endif // SOUNDCLUSTER_H
+
diff --git a/game/code/sound/soundclusternameenum.h b/game/code/sound/soundclusternameenum.h
new file mode 100644
index 0000000..daa6fcf
--- /dev/null
+++ b/game/code/sound/soundclusternameenum.h
@@ -0,0 +1,51 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundclusternameenum.h
+//
+// Description: Definition for SoundClusterName enum
+//
+// History: 11/18/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDCLUSTERNAMEENUM_H
+#define SOUNDCLUSTERNAMEENUM_H
+
+#include <constants/vehicleenum.h>
+
+enum SoundClusterName
+{
+ SC_ALWAYS_LOADED,
+ SC_FRONTEND,
+ SC_INGAME,
+
+ SC_LEVEL_SUBURBS,
+ SC_LEVEL_DOWNTOWN,
+ SC_LEVEL_SEASIDE,
+
+ SC_LEVEL1,
+ SC_LEVEL2,
+ SC_LEVEL3,
+ SC_LEVEL4,
+ SC_LEVEL5,
+ SC_LEVEL6,
+ SC_LEVEL7,
+ SC_MINIGAME,
+
+ SC_NEVER_LOADED,
+
+ SC_CHAR_APU,
+ SC_CHAR_BART,
+ SC_CHAR_HOMER,
+ SC_CHAR_LISA,
+ SC_CHAR_MARGE,
+
+ SC_CAR_BASE,
+
+ SC_MAX_CLUSTERS = SC_CAR_BASE + VehicleEnum::NUM_VEHICLES // SC_CAR_BASE + 53 cars
+};
+
+
+#endif // SOUNDCLUSTERNAMEENUM_H
+
diff --git a/game/code/sound/soundcollisiondata.h b/game/code/sound/soundcollisiondata.h
new file mode 100644
index 0000000..2a986bf
--- /dev/null
+++ b/game/code/sound/soundcollisiondata.h
@@ -0,0 +1,58 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundcollisiondata.h
+//
+// Description: Declaration for SoundCollisionData structure, which carries
+// collision information through the event manager to the sound
+// system
+//
+// History: 04/08/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDCOLLISIONDATA_H
+#define SOUNDCOLLISIONDATA_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <render/DSG/collisionentitydsg.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: SoundCollisionData
+//
+//=============================================================================
+
+class SoundCollisionData
+{
+ public:
+ SoundCollisionData( float intensity, CollisionEntityDSG* objA, CollisionEntityDSG* objB )
+ {
+ //impulse = vector;
+ mIntensity = intensity;
+ collObjA = objA;
+ collObjB = objB;
+ }
+ ~SoundCollisionData() {}
+
+ float mIntensity; // 0.0f - 1.0f
+ CollisionEntityDSG* collObjA;
+ CollisionEntityDSG* collObjB;
+
+ private:
+ //
+ // In this case, allow the shallow-copy assignment and copy constructors.
+ // We don't want the default constructor, though.
+ //
+ SoundCollisionData();
+};
+
+
+#endif // SOUNDCOLLISIONDATA_H
+
diff --git a/game/code/sound/sounddebug/allsounddebug.cpp b/game/code/sound/sounddebug/allsounddebug.cpp
new file mode 100644
index 0000000..de95350
--- /dev/null
+++ b/game/code/sound/sounddebug/allsounddebug.cpp
@@ -0,0 +1,2 @@
+#include <sound/sounddebug/sounddebugdisplay.cpp>
+#include <sound/sounddebug/sounddebugpage.cpp>
diff --git a/game/code/sound/sounddebug/sounddebugdisplay.cpp b/game/code/sound/sounddebug/sounddebugdisplay.cpp
new file mode 100644
index 0000000..b31a7e9
--- /dev/null
+++ b/game/code/sound/sounddebug/sounddebugdisplay.cpp
@@ -0,0 +1,438 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: sounddebugdisplay.cpp
+//
+// Description: Implement SoundDebugDisplay
+//
+// History: 10/26/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <pddi/pddi.hpp>
+#include <p3d/utility.hpp>
+
+#include <raddebugwatch.hpp>
+#include <radsound_hal.hpp>
+#include <radtypeinfo.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/sounddebug/sounddebugdisplay.h>
+
+#include <sound/sounddebug/sounddebugpage.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/playermanager.h>
+
+#include <render/IntersectManager/IntersectManager.h>
+#include <render/DSG/StaticPhysDSG.h>
+#include <render/DSG/DynaPhysDSG.h>
+#include <render/DSG/animcollisionentitydsg.h>
+#include <worldsim/avatarmanager.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+//
+// Watcher tunables
+//
+bool SoundDebugDisplay::s_isVisible = false;
+int SoundDebugDisplay::s_red = 255;
+int SoundDebugDisplay::s_green = 255;
+int SoundDebugDisplay::s_blue = 0;
+// Text position
+int SoundDebugDisplay::s_leftOffset = 0;
+int SoundDebugDisplay::s_topOffset = 0;
+// Displayed page
+unsigned int SoundDebugDisplay::s_page = 0;
+bool SoundDebugDisplay::s_dumpToWindow = false;
+// Name display radius
+float SoundDebugDisplay::s_radius = 2.0f;
+// Type info
+bool SoundDebugDisplay::s_dumpTypeInfoToWindow = false;
+
+static const int NUM_VISIBLE_LINES = 15;
+static const int NUM_ENTITIES = ( NUM_VISIBLE_LINES * SoundDebugDisplay::MAX_DEBUG_PAGES );
+
+static const int LEFT = 40;
+static const int TOP = 40;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundDebugDisplay::SoundDebugDisplay
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundDebugDisplay::SoundDebugDisplay( Sound::daSoundRenderingManager* soundMgr ) :
+ m_renderMgr( soundMgr )
+{
+ int i;
+ const char* sectionName = "Sound Info";
+
+ //
+ // Register the watcher variables
+ //
+ radDbgWatchAddBoolean( &s_isVisible, "Set Visibility", sectionName, 0, 0 );
+ radDbgWatchAddBoolean( &s_dumpToWindow, "Dump To Window", sectionName, 0, 0 );
+ radDbgWatchAddUnsignedInt( &s_page, "Select Page", sectionName, 0, 0, 0, MAX_DEBUG_PAGES - 1 );
+ radDbgWatchAddInt( &s_leftOffset, "Left Position", sectionName, 0, 0, -1000, 1000 );
+ radDbgWatchAddInt( &s_topOffset, "Top Position", sectionName, 0, 0, -1000, 1000 );
+ radDbgWatchAddInt( &s_red, "Text - Red", sectionName, 0, 0, 0, 255 );
+ radDbgWatchAddInt( &s_green, "Text - Green", sectionName, 0, 0, 0, 255 );
+ radDbgWatchAddInt( &s_blue, "Text - Blue", sectionName, 0, 0, 0, 255 );
+ radDbgWatchAddFloat( &s_radius, "Name Display Radius", sectionName, 0, 0, 1.0f, 10.0f );
+#ifdef RAD_DEBUG
+ // RadScript only provides this capability in debug
+ radDbgWatchAddBoolean( &s_dumpTypeInfoToWindow, "Dump Type Info To Window", sectionName );
+#endif
+
+ for( i = 0; i < MAX_DEBUG_PAGES; i++ )
+ {
+ m_debugPages[i] = NULL;
+ }
+}
+
+//==============================================================================
+// SoundDebugDisplay::~SoundDebugDisplay
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundDebugDisplay::~SoundDebugDisplay()
+{
+ radDbgWatchDelete( &s_isVisible );
+ radDbgWatchDelete( &s_dumpToWindow );
+ radDbgWatchDelete( &s_page );
+ radDbgWatchDelete( &s_leftOffset );
+ radDbgWatchDelete( &s_topOffset );
+ radDbgWatchDelete( &s_red );
+ radDbgWatchDelete( &s_green );
+ radDbgWatchDelete( &s_blue );
+ radDbgWatchDelete( &s_radius );
+#ifdef RAD_DEBUG
+ radDbgWatchDelete( &s_dumpTypeInfoToWindow );
+#endif
+}
+
+//=============================================================================
+// SoundDebugDisplay::RegisterPage
+//=============================================================================
+// Description: Register a debug page for display
+//
+// Parameters: page - page to register
+//
+// Return: void
+//
+//=============================================================================
+void SoundDebugDisplay::RegisterPage( SoundDebugPage* page )
+{
+ int i;
+
+ for( i = 0; i < MAX_DEBUG_PAGES; i++ )
+ {
+ if( m_debugPages[i] == NULL )
+ {
+ m_debugPages[i] = page;
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// SoundDebugDisplay::DeregisterPage
+//=============================================================================
+// Description: Deregister a debug page for display
+//
+// Parameters: page - page to deregister
+//
+// Return: void
+//
+//=============================================================================
+void SoundDebugDisplay::DeregisterPage( SoundDebugPage* page )
+{
+ int i;
+
+ for( i = 0; i < MAX_DEBUG_PAGES; i++ )
+ {
+ if( m_debugPages[i] == page )
+ {
+ m_debugPages[i] = NULL;
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// SoundDebugDisplay::Render
+//=============================================================================
+// Description: Draw the sound debug info
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundDebugDisplay::Render()
+{
+ if ( Sound::daSoundRenderingManager::GetInstance( ) )
+ {
+ Sound::daSoundRenderingManager::GetInstance( )->Render( );
+ }
+
+#ifndef RAD_RELEASE
+ Avatar* theAvatar;
+ rmt::Vector position;
+ rmt::Vector* positionPtr = NULL;
+ int i;
+ tColour stringColour;
+
+ if( s_dumpTypeInfoToWindow )
+ {
+ s_dumpTypeInfoToWindow = false;
+#ifdef RADSCRIPT_DEBUG
+ ::radTypeInfoSystemGet()->DebugDump();
+#endif
+ }
+
+ if( !s_isVisible )
+ {
+ return;
+ }
+
+ //
+ // The new, good way
+ //
+ if( s_page > 1 )
+ {
+ stringColour = tColour(s_red, s_green, s_blue);
+
+ for( i = 0; i < MAX_DEBUG_PAGES; i++ )
+ {
+ if( ( m_debugPages[i] != NULL )
+ && ( m_debugPages[i]->GetPage() == s_page ) )
+ {
+ m_debugPages[i]->Render( LEFT + s_leftOffset, TOP + s_topOffset,
+ stringColour, s_dumpToWindow );
+ break;
+ }
+ }
+ }
+ else
+ {
+ //
+ // The old, bad way
+ //
+
+ //
+ // Get the avatar position
+ //
+ theAvatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ if( theAvatar != NULL )
+ {
+ //
+ // Presumably we're in game if we get here
+ //
+ theAvatar->GetPosition( position );
+ positionPtr = &position;
+ }
+
+ if( s_page == 0 )
+ {
+ renderPositionAndHeapInfo( positionPtr );
+ }
+ else
+ {
+ renderNearbyObjectNames( positionPtr );
+ }
+ }
+#endif
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+void SoundDebugDisplay::renderPositionAndHeapInfo( rmt::Vector* position )
+{
+#ifndef RAD_RELEASE
+ char buffy[128];
+ tColour stringColour = tColour(s_red, s_green, s_blue);
+ IRadSoundHalSystem::Stats radSoundStats;
+
+ //
+ // Display the avatar position
+ //
+ if( position != NULL )
+ {
+ sprintf( buffy, "Avatar posn: %f %f %f", position->x, position->y, position->z );
+ renderTextLine( buffy, LEFT, TOP, stringColour );
+ }
+
+ //
+ // Display the radSound memory stats
+ //
+ strcpy( buffy, "RadSound stats:" );
+ renderTextLine( buffy, LEFT, TOP + 20, stringColour );
+
+ ::radSoundHalSystemGet()->GetStats( &radSoundStats );
+ sprintf( buffy, "Buffers: %d Voices: %d/%d PosVoices: %d/%d",
+ radSoundStats.m_NumBuffers,
+ radSoundStats.m_NumVoicesPlaying,
+ radSoundStats.m_NumVoices,
+ radSoundStats.m_NumPosVoicesPlaying,
+ radSoundStats.m_NumPosVoices );
+ renderTextLine( buffy, LEFT, TOP + 40, stringColour );
+
+ sprintf( buffy, "Buffer memory used: %d Effects memory used: %d",
+ radSoundStats.m_BufferMemoryUsed,
+ radSoundStats.m_EffectsMemoryUsed );
+ renderTextLine( buffy, LEFT, TOP + 60, stringColour );
+
+ sprintf( buffy, "Sound memory free: %d",
+ radSoundStats.m_TotalFreeSoundMemory );
+ renderTextLine( buffy, LEFT, TOP + 80, stringColour );
+
+ sprintf( buffy, "Sound renderer stats:" );
+ renderTextLine( buffy, LEFT, TOP + 120, stringColour );
+
+ sprintf( buffy, "Clip players used: %d", m_renderMgr->GetPlayerManager()->GetNumUsedClipPlayers() );
+ renderTextLine( buffy, LEFT, TOP + 140, stringColour );
+
+ sprintf( buffy, "Stream players used: %d", m_renderMgr->GetPlayerManager()->GetNumUsedStreamPlayers() );
+ renderTextLine( buffy, LEFT, TOP + 160, stringColour );
+
+ s_dumpToWindow = false;
+#endif
+}
+
+void SoundDebugDisplay::renderNearbyObjectNames( rmt::Vector* position )
+{
+#ifndef RAD_RELEASE
+ char buffy[128];
+ tColour stringColour = tColour(s_red, s_green, s_blue);
+ const char* noSound = "NO SOUND";
+ ReserveArray<StaticPhysDSG*> statics;
+ ReserveArray<DynaPhysDSG*> dynamics;
+ ReserveArray<AnimCollisionEntityDSG*> animatics;
+ CollisionEntityDSG* collEntityArray[NUM_ENTITIES];
+ int arrayIndex = 0;
+ int i;
+
+ if( position == NULL )
+ {
+ //
+ // No character to find nearby objects for, draw nothing
+ //
+ return;
+ }
+
+ //
+ // Get the intersect goodness
+ //
+ GetIntersectManager()->FindStaticPhysElems( *position, s_radius, statics );
+ GetIntersectManager()->FindDynaPhysElems( *position, s_radius, dynamics );
+ GetIntersectManager()->FindAnimPhysElems( *position, s_radius, animatics );
+
+ //
+ // Populate the entity array. Simplifies the code slightly
+ // to do it this way. This is debug stuff, doesn't need to be
+ // pretty.
+ //
+ for( i = 0; i < statics.mUseSize; i++ )
+ {
+ if( arrayIndex >= NUM_ENTITIES )
+ {
+ break;
+ }
+ collEntityArray[arrayIndex] = statics[i];
+ ++arrayIndex;
+ }
+ for( i = 0; i < dynamics.mUseSize; i++ )
+ {
+ if( arrayIndex >= NUM_ENTITIES )
+ {
+ break;
+ }
+ collEntityArray[arrayIndex] = dynamics[i];
+ ++arrayIndex;
+ }
+ for( i = 0; i < animatics.mUseSize; i++ )
+ {
+ if( arrayIndex >= NUM_ENTITIES )
+ {
+ break;
+ }
+ collEntityArray[arrayIndex] = animatics[i];
+ ++arrayIndex;
+ }
+
+ //
+ // Fill the rest of the array with NULLs
+ //
+ for( i = arrayIndex; i < NUM_ENTITIES; i++ )
+ {
+ collEntityArray[i] = NULL;
+ }
+
+ int startIndex = (s_page - 1) * NUM_VISIBLE_LINES;
+ int endIndex = startIndex + NUM_VISIBLE_LINES;
+
+ for( i = startIndex; i < endIndex; ++i )
+ {
+
+ if( collEntityArray[i] == NULL )
+ {
+ break;
+ }
+
+ if( collEntityArray[i]->GetCollisionAttributes() == NULL )
+ {
+ sprintf( buffy, "%-32s\t%-32s", collEntityArray[i]->GetName(), noSound );
+ }
+ else
+ {
+ sprintf( buffy, "%-32s\t%-32s", collEntityArray[i]->GetName(),
+ collEntityArray[i]->GetCollisionAttributes()->GetSound() );
+ }
+
+ renderTextLine( buffy, LEFT, TOP + ((i % NUM_VISIBLE_LINES)*20), stringColour );
+ }
+
+ s_dumpToWindow = false;
+#endif
+}
+
+void SoundDebugDisplay::renderTextLine( const char* text, int leftPosn,
+ int topPosn, tColour& colour )
+{
+ p3d::pddi->DrawString( text, leftPosn + s_leftOffset, topPosn + s_topOffset, colour );
+
+ if ( s_dumpToWindow )
+ {
+ rDebugString( text );
+ }
+}
diff --git a/game/code/sound/sounddebug/sounddebugdisplay.h b/game/code/sound/sounddebug/sounddebugdisplay.h
new file mode 100644
index 0000000..170f5f1
--- /dev/null
+++ b/game/code/sound/sounddebug/sounddebugdisplay.h
@@ -0,0 +1,86 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: sounddebugdisplay.h
+//
+// Description: Declaration for the SoundDebugDisplay class, used for
+// printing debugging/tuning information on screen
+//
+// History: 10/26/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDDEBUGDISPLAY_H
+#define SOUNDDEBUGDISPLAY_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+//========================================
+// Forward References
+//========================================
+class SoundDebugPage;
+
+namespace Sound
+{
+ class daSoundRenderingManager;
+}
+
+
+//=============================================================================
+//
+// Synopsis: SoundDebugDisplay
+//
+//=============================================================================
+
+class SoundDebugDisplay
+{
+ public:
+ SoundDebugDisplay( Sound::daSoundRenderingManager* renderMgr );
+ virtual ~SoundDebugDisplay();
+
+ void Render();
+
+ void RegisterPage( SoundDebugPage* page );
+ void DeregisterPage( SoundDebugPage* page );
+
+ static const int MAX_DEBUG_PAGES = 5;
+
+ //
+ // Watcher tunables
+ //
+ static bool s_isVisible;
+ static int s_red;
+ static int s_green;
+ static int s_blue;
+ // Text position
+ static int s_leftOffset;
+ static int s_topOffset;
+ // Displayed page
+ static unsigned int s_page;
+ static bool s_dumpToWindow;
+ // Name display radius
+ static float s_radius;
+ // Type info
+ static bool s_dumpTypeInfoToWindow;
+
+ private:
+ //Prevent wasteful constructor creation.
+ SoundDebugDisplay( const SoundDebugDisplay& original );
+ SoundDebugDisplay& operator=( const SoundDebugDisplay& rhs );
+
+ void renderPositionAndHeapInfo( rmt::Vector* position );
+ void renderNearbyObjectNames( rmt::Vector* position );
+
+ void renderTextLine( const char* text, int leftPosn, int topPosn, tColour& colour );
+
+ SoundDebugPage* m_debugPages[MAX_DEBUG_PAGES];
+
+ Sound::daSoundRenderingManager* m_renderMgr;
+};
+
+
+#endif // SOUNDDEBUGDISPLAY_H
+
diff --git a/game/code/sound/sounddebug/sounddebugpage.cpp b/game/code/sound/sounddebug/sounddebugpage.cpp
new file mode 100644
index 0000000..70aee13
--- /dev/null
+++ b/game/code/sound/sounddebug/sounddebugpage.cpp
@@ -0,0 +1,161 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: sounddebugpage.cpp
+//
+// Description: Implementation of SoundDebugPage class, which is used for
+// displaying a chunk of debug info in Watcher (or whatever
+// our display mechanism happens to be)
+//
+// History: 11/22/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/sounddebug/sounddebugpage.h>
+
+#include <sound/sounddebug/sounddebugdisplay.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundDebugPage::SoundDebugPage
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundDebugPage::SoundDebugPage() :
+ m_pageNum( 0 ),
+ m_displayMaster( NULL )
+{
+ //
+ // Presumably, we're saving full initialization for later.
+ //
+
+#ifndef SOUND_DEBUG_INFO_ENABLED
+ //
+ // Don't want to build these in release, so make it die horribly.
+ //
+ rReleaseAssertMsg( false, "Oops, shouldn't be storing sound debug info in release" );
+#endif
+}
+
+//==============================================================================
+// SoundDebugPage::SoundDebugPage
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: pageNum - page we'll display info on in Watcher's SoundInfo.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundDebugPage::SoundDebugPage( unsigned int pageNum, SoundDebugDisplay* master ) :
+m_pageNum( pageNum ),
+m_displayMaster( master )
+{
+#ifndef SOUND_DEBUG_INFO_ENABLED
+ //
+ // Don't want to build these in release, so make it die horribly.
+ //
+ rReleaseAssertMsg( false, "Oops, shouldn't be storing sound debug info in release" );
+#endif
+
+ //
+ // Tell debug display master about our existence
+ //
+ master->RegisterPage( this );
+}
+
+//==============================================================================
+// SoundDebugPage::~SoundDebugPage
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundDebugPage::~SoundDebugPage()
+{
+ m_displayMaster->DeregisterPage( this );
+}
+
+//=============================================================================
+// SoundDebugPage::LazyInitialization
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int pageNum, SoundDebugDisplay* master )
+//
+// Return: void
+//
+//=============================================================================
+void SoundDebugPage::LazyInitialization( unsigned int pageNum, SoundDebugDisplay* master )
+{
+ m_pageNum = pageNum;
+ m_displayMaster = master;
+
+ m_displayMaster->RegisterPage( this );
+}
+
+//=============================================================================
+// SoundDebugPage::Render
+//=============================================================================
+// Description: Draw our debug info. This relies on subclasses providing the
+// info and telling us how many lines to draw
+//
+// Parameters: leftPosn - leftmost horizontal position to write text to
+// topPosn - topmost vertical position to write text to
+// colour - colour of pddi text to use
+// dumpToWindow - also write text as debug string if true
+//
+// Return: void
+//
+//=============================================================================
+void SoundDebugPage::Render( int leftPosn, int topPosn, tColour& colour, bool dumpToWindow )
+{
+ int i;
+ char lineBuffer[255];
+ int numLines = getNumLines();
+
+ for( i = 0; i < numLines; i++ )
+ {
+ fillLineBuffer( i, lineBuffer );
+
+ p3d::pddi->DrawString( lineBuffer, leftPosn, topPosn + (i * 20), colour );
+
+ if ( dumpToWindow )
+ {
+ rDebugString( lineBuffer );
+ }
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/sound/sounddebug/sounddebugpage.h b/game/code/sound/sounddebug/sounddebugpage.h
new file mode 100644
index 0000000..088f14d
--- /dev/null
+++ b/game/code/sound/sounddebug/sounddebugpage.h
@@ -0,0 +1,67 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: sounddebugpage.h
+//
+// Description: Declaration of SoundDebugPage class, which is used for displaying
+// a chunk of debug info in Watcher (or whatever our display
+// mechanism happens to be)
+//
+// History: 11/22/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDDEBUGPAGE_H
+#define SOUNDDEBUGPAGE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/p3dtypes.hpp>
+
+//========================================
+// Forward References
+//========================================
+class SoundDebugDisplay;
+
+#ifndef RAD_RELEASE
+#define SOUND_DEBUG_INFO_ENABLED
+#endif
+
+//=============================================================================
+//
+// Synopsis: SoundDebugPage
+//
+//=============================================================================
+
+class SoundDebugPage
+{
+ public:
+ SoundDebugPage();
+ SoundDebugPage( unsigned int pageNum, SoundDebugDisplay* master );
+ virtual ~SoundDebugPage();
+
+ void LazyInitialization( unsigned int pageNum, SoundDebugDisplay* master );
+
+ unsigned int GetPage() { return( m_pageNum ); }
+
+ void Render( int leftPosn, int topPosn, tColour& colour, bool dumpToWindow );
+
+ protected:
+
+ virtual void fillLineBuffer( int lineNum, char* buffer ) = 0;
+ virtual int getNumLines() = 0;
+
+ private:
+ //Prevent wasteful constructor creation.
+ SoundDebugPage( const SoundDebugPage& original );
+ SoundDebugPage& operator=( const SoundDebugPage& rhs );
+
+ unsigned int m_pageNum;
+
+ SoundDebugDisplay* m_displayMaster;
+};
+
+
+#endif // SOUNDDEBUGPAGE_H
+
diff --git a/game/code/sound/soundfx/allsoundfx.cpp b/game/code/sound/soundfx/allsoundfx.cpp
new file mode 100644
index 0000000..4ee5e0b
--- /dev/null
+++ b/game/code/sound/soundfx/allsoundfx.cpp
@@ -0,0 +1,14 @@
+#include <sound/soundfx/soundeffectplayer.cpp>
+#include <sound/soundfx/soundfxfrontendlogic.cpp>
+#include <sound/soundfx/soundfxgameplaylogic.cpp>
+#include <sound/soundfx/soundfxlogic.cpp>
+#include <sound/soundfx/soundfxpauselogic.cpp>
+#include <sound/soundfx/reverbcontroller.cpp>
+#include <sound/soundfx/reverbsettings.cpp>
+#include <sound/soundfx/positionalsoundsettings.cpp>
+
+#ifdef RAD_PS2
+#include <sound/soundfx/ps2reverbcontroller.cpp>
+#else
+#include <sound/soundfx/gcreverbcontroller.cpp>
+#endif \ No newline at end of file
diff --git a/game/code/sound/soundfx/gcreverbcontroller.cpp b/game/code/sound/soundfx/gcreverbcontroller.cpp
new file mode 100644
index 0000000..4b6336a
--- /dev/null
+++ b/game/code/sound/soundfx/gcreverbcontroller.cpp
@@ -0,0 +1,108 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: gcreverbcontroller.cpp
+//
+// Description: Implementation for the GCReverbController class, which provides
+// the GC-specific reverb control
+//
+// History: 10/28/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radsound_gcn.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundfx/gcreverbcontroller.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// GCReverbController::GCReverbController
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GCReverbController::GCReverbController()
+{
+ m_reverbInterface = ::radSoundEffectStdReverbGcnCreate( GMA_PERSISTENT );
+
+ registerReverbEffect( m_reverbInterface );
+}
+
+//==============================================================================
+// GCReverbController::~GCReverbController
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+GCReverbController::~GCReverbController()
+{
+ m_reverbInterface->Release();
+ m_reverbInterface = NULL;
+}
+
+//=============================================================================
+// GCReverbController::SetReverbOn
+//=============================================================================
+// Description: Self-explanatory
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void GCReverbController::SetReverbOn( reverbSettings* settings )
+{
+ SetReverbGain( settings->GetGain() );
+ m_reverbInterface->SetPreDelay( settings->GetGCPreDelay() );
+ m_reverbInterface->SetReverbTime( settings->GetGCReverbTime() );
+ m_reverbInterface->SetColoration( settings->GetGCColoration() );
+ m_reverbInterface->SetDamping( settings->GetGCDamping() );
+}
+
+//=============================================================================
+// GCReverbController::SetReverbOff
+//=============================================================================
+// Description: Self-explanatory
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void GCReverbController::SetReverbOff()
+{
+ SetReverbGain( 0.0f );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/sound/soundfx/gcreverbcontroller.h b/game/code/sound/soundfx/gcreverbcontroller.h
new file mode 100644
index 0000000..3e1e101
--- /dev/null
+++ b/game/code/sound/soundfx/gcreverbcontroller.h
@@ -0,0 +1,54 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: gcreverbcontroller.h
+//
+// Description: Declaration for the GCReverbController class, which provides
+// the GC-specific reverb control
+//
+// History: 10/28/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef GCREVERBCONTROLLER_H
+#define GCREVERBCONTROLLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/soundfx/reverbcontroller.h>
+
+//========================================
+// Forward References
+//========================================
+struct IRadSoundEffectStdReverbGcn;
+
+//=============================================================================
+//
+// Synopsis: GCReverbController
+//
+//=============================================================================
+
+class GCReverbController : public ReverbController
+{
+ public:
+ GCReverbController();
+ virtual ~GCReverbController();
+
+ void SetReverbOn( reverbSettings* settings );
+ void SetReverbOff();
+
+ private:
+ //Prevent wasteful constructor creation.
+ GCReverbController( const GCReverbController& original );
+ GCReverbController& operator=( const GCReverbController& rhs );
+
+ //
+ // Radsound's GC reverb interface
+ //
+ IRadSoundEffectStdReverbGcn* m_reverbInterface;
+};
+
+
+#endif // GCREVERBCONTROLLER_H
+
diff --git a/game/code/sound/soundfx/ipositionalsoundsettings.h b/game/code/sound/soundfx/ipositionalsoundsettings.h
new file mode 100644
index 0000000..3989621
--- /dev/null
+++ b/game/code/sound/soundfx/ipositionalsoundsettings.h
@@ -0,0 +1,59 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ipositionalsoundsettings.h
+//
+// Description: RadScript interface for positional sound settings
+//
+// History: 12/20/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef IPOSITIONALSOUNDSETTINGS_H
+#define IPOSITIONALSOUNDSETTINGS_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radobject.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: IPositionalSoundSettings
+//
+//=============================================================================
+
+class IPositionalSoundSettings : public IRefCount
+{
+ //
+ // Name of sound resource to play
+ //
+ virtual void SetClipName( const char* clipName ) = 0;
+
+ //
+ // Distance at which volume is maxed
+ //
+ virtual void SetMinDistance( float min ) = 0;
+
+ //
+ // Distance at which volume reaches zero
+ //
+ virtual void SetMaxDistance( float max ) = 0;
+
+ //
+ // Probability of sound playing
+ //
+ virtual void SetPlaybackProbability( float prob ) = 0;
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //IPOSITIONALSOUNDSETTINGS_H
diff --git a/game/code/sound/soundfx/ireverbsettings.h b/game/code/sound/soundfx/ireverbsettings.h
new file mode 100644
index 0000000..c1288f3
--- /dev/null
+++ b/game/code/sound/soundfx/ireverbsettings.h
@@ -0,0 +1,73 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ireverbsettings.h
+//
+// Description: Declaration for IReverbSettings class, which is the interface
+// visible in RadTuner for creating and storing a set of reverb
+// parameters.
+//
+// History: 11/3/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef IREVERBSETTINGS_H
+#define IREVERBSETTINGS_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radobject.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: IReverbSettings
+//
+//=============================================================================
+
+struct IReverbSettings : public IRefCount
+{
+ virtual void SetGain( float gain ) = 0;
+ virtual void SetFadeInTime( float milliseconds ) = 0;
+ virtual void SetFadeOutTime( float milliseconds ) = 0;
+
+ //
+ // See radsound_<platform name>.hpp for details on this stuff
+ //
+ virtual void SetXboxRoom( int mBvalue ) = 0;
+ virtual void SetXboxRoomHF( int mBvalue ) = 0;
+ virtual void SetXboxRoomRolloffFactor( float value ) = 0;
+ virtual void SetXboxDecayTime( float value ) = 0;
+ virtual void SetXboxDecayHFRatio( float value ) = 0;
+ virtual void SetXboxReflections( int mBvalue ) = 0;
+ virtual void SetXboxReflectionsDelay( float value ) = 0;
+ virtual void SetXboxReverb( int mBvalue ) = 0;
+ virtual void SetXboxReverbDelay( float value ) = 0;
+ virtual void SetXboxDiffusion( float value ) = 0;
+ virtual void SetXboxDensity( float value ) = 0;
+ virtual void SetXboxHFReference( float value ) = 0;
+
+ // No RadTuner interface for enumerations as far as I know, so
+ // we'll have to cast whatever integer we get here
+ virtual void SetPS2ReverbMode( int mode ) = 0;
+
+ virtual void SetPS2Delay( float delayTime ) = 0;
+ virtual void SetPS2Feedback( float feedback ) = 0;
+
+ virtual void SetGCPreDelay( float milliseconds ) = 0;
+ virtual void SetGCReverbTime( float milliseconds ) = 0;
+ virtual void SetGCColoration( float coloration ) = 0;
+ virtual void SetGCDamping( float damping ) = 0;
+
+ // Must be defined for all platforms cause of the script.
+ virtual void SetWinEnvironmentDiffusion( float diffusion ) = 0;
+ virtual void SetWinAirAbsorptionHF( float value ) = 0;
+};
+
+
+#endif // IREVERBSETTINGS_H
+
diff --git a/game/code/sound/soundfx/positionalsoundsettings.cpp b/game/code/sound/soundfx/positionalsoundsettings.cpp
new file mode 100644
index 0000000..670bf9b
--- /dev/null
+++ b/game/code/sound/soundfx/positionalsoundsettings.cpp
@@ -0,0 +1,180 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: positionalsoundsettings.cpp
+//
+// Description: Implement positionalSoundSettings
+//
+// History: 12/20/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundfx/positionalsoundsettings.h>
+
+#include <memory/srrmemory.h>
+
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//
+// Initialially the list is empty
+//
+positionalSoundSettings* radLinkedClass< positionalSoundSettings >::s_pLinkedClassHead = NULL;
+positionalSoundSettings* radLinkedClass< positionalSoundSettings >::s_pLinkedClassTail = NULL;
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// positionalSoundSettings::positionalSoundSettings
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+positionalSoundSettings::positionalSoundSettings() :
+ radRefCount( 0 ),
+ m_clipName( NULL ),
+ m_minDist( 10.0f ),
+ m_maxDist( 100.0f ),
+ m_playProbability( 1.0f )
+{
+}
+
+//=============================================================================
+// positionalSoundSettings::~positionalSoundSettings
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+positionalSoundSettings::~positionalSoundSettings()
+{
+ delete m_clipName;
+}
+
+//=============================================================================
+// positionalSoundSettings::SetClipName
+//=============================================================================
+// Description: Set name of the sound resource to be played at this position
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void positionalSoundSettings::SetClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ m_clipName = new(GMA_PERSISTENT) char[strlen(clipName)+1];
+ strcpy( m_clipName, clipName );
+}
+
+//=============================================================================
+// positionalSoundSettings::SetMinDistance
+//=============================================================================
+// Description: Set distance at which volume is maxed (kinda counterintuitive,
+// but that seems to be the convention)
+//
+// Parameters: min - distance to set
+//
+// Return: void
+//
+//=============================================================================
+void positionalSoundSettings::SetMinDistance( float min )
+{
+ rAssert( min >= 0.0f );
+
+ m_minDist = min;
+}
+
+//=============================================================================
+// positionalSoundSettings::SetMaxDistance
+//=============================================================================
+// Description: Set the distance at which the volume reaches zero
+//
+// Parameters: max - distance to set
+//
+// Return: void
+//
+//=============================================================================
+void positionalSoundSettings::SetMaxDistance( float max )
+{
+ rAssert( max >= 0.0f );
+
+ m_maxDist = max;
+}
+
+//=============================================================================
+// positionalSoundSettings::SetPlaybackProbability
+//=============================================================================
+// Description: Set probability that sound will be played when we enter
+// the trigger volume
+//
+// Parameters: prob - probability on scale of 0.0 to 1.0
+//
+// Return: void
+//
+//=============================================================================
+void positionalSoundSettings::SetPlaybackProbability( float prob )
+{
+ rAssert( prob >= 0.0f );
+ rAssert( prob <= 1.0f );
+
+ m_playProbability = prob;
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//******************************************************************************
+// Factory functions
+//******************************************************************************
+
+//==============================================================================
+// PositionalSettingsObjCreate
+//==============================================================================
+// Description: Factory function for creating positionalSoundSettings objects.
+// Called by RadScript.
+//
+// Parameters: ppParametersObj - Address of ptr to new object
+// allocator - FTT pool to allocate object within
+//
+// Return: N/A.
+//
+//==============================================================================
+void PositionalSettingsObjCreate
+(
+ IPositionalSoundSettings** ppParametersObj,
+ radMemoryAllocator allocator
+)
+{
+ rAssert( ppParametersObj != NULL );
+ (*ppParametersObj) = new ( allocator ) positionalSoundSettings( );
+ (*ppParametersObj)->AddRef( );
+}
diff --git a/game/code/sound/soundfx/positionalsoundsettings.h b/game/code/sound/soundfx/positionalsoundsettings.h
new file mode 100644
index 0000000..8174598
--- /dev/null
+++ b/game/code/sound/soundfx/positionalsoundsettings.h
@@ -0,0 +1,80 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: positionalsoundsettings.h
+//
+// Description: Declaration of object encapsulating positional sound settings
+//
+// History: 12/20/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef POSITIONALSOUNDSETTINGS_H
+#define POSITIONALSOUNDSETTINGS_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radlinkedclass.hpp>
+
+#include <sound/soundfx/ipositionalsoundsettings.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: positionalSoundSettings
+//
+//=============================================================================
+
+class positionalSoundSettings: public IPositionalSoundSettings,
+ public radLinkedClass< positionalSoundSettings >,
+ public radRefCount
+{
+ public:
+ IMPLEMENT_REFCOUNTED( "positionalSoundSettings" );
+ positionalSoundSettings();
+ virtual ~positionalSoundSettings();
+
+ void SetClipName( const char* clipName );
+ const char* GetClipName() { return( m_clipName ); }
+
+ void SetMinDistance( float min );
+ float GetMinDistance() { return( m_minDist ); }
+
+ void SetMaxDistance( float max );
+ float GetMaxDistance() { return( m_maxDist ); }
+
+ void SetPlaybackProbability( float prob );
+ float GetPlaybackProbability() { return( m_playProbability ); }
+
+ private:
+ //Prevent wasteful constructor creation.
+ positionalSoundSettings( const positionalSoundSettings& positionalsoundsettings );
+ positionalSoundSettings& operator=( const positionalSoundSettings& positionalsoundsettings );
+
+ //
+ // Settings
+ //
+ char* m_clipName;
+ float m_minDist;
+ float m_maxDist;
+ float m_playProbability;
+};
+
+//=============================================================================
+// Factory Functions
+//=============================================================================
+
+//
+// Create a positionalSoundSettings object
+//
+void PositionalSettingsObjCreate
+(
+ IPositionalSoundSettings** ppSettings,
+ radMemoryAllocator allocator
+);
+
+#endif //POSITIONALSOUNDSETTINGS_H
diff --git a/game/code/sound/soundfx/ps2reverbcontroller.cpp b/game/code/sound/soundfx/ps2reverbcontroller.cpp
new file mode 100644
index 0000000..cf88160
--- /dev/null
+++ b/game/code/sound/soundfx/ps2reverbcontroller.cpp
@@ -0,0 +1,124 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ps2reverbcontroller.cpp
+//
+// Description: Implementation for the PS2ReverbController class, which provides
+// the PS2-specific reverb control
+//
+// History: 10/28/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radsound_ps2.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundfx/ps2reverbcontroller.h>
+
+#include <sound/soundfx/reverbsettings.h>
+
+#include <memory/srrmemory.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// PS2ReverbController::PS2ReverbController
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PS2ReverbController::PS2ReverbController()
+{
+ m_reverbInterface = ::radSoundCreateEffectPs2( GMA_PERSISTENT );
+
+ //
+ // Wheee!!!!
+ //
+ m_reverbInterface->SetMode( IRadSoundEffectPs2::Off );
+
+ registerReverbEffect( m_reverbInterface );
+}
+
+//==============================================================================
+// PS2ReverbController::~PS2ReverbController
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+PS2ReverbController::~PS2ReverbController()
+{
+ m_reverbInterface->Release();
+ m_reverbInterface = NULL;
+}
+
+//=============================================================================
+// PS2ReverbController::SetReverbOn
+//=============================================================================
+// Description: Self-explanatory
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void PS2ReverbController::SetReverbOn( reverbSettings* settings )
+{
+ if( settings != NULL )
+ {
+ m_reverbInterface->SetMode( static_cast<IRadSoundEffectPs2::Mode>(settings->GetPS2ReverbMode()) );
+ m_reverbInterface->SetGain( settings->GetGain() );
+ m_reverbInterface->SetDelay( settings->GetPS2Delay() );
+ m_reverbInterface->SetFeedback( settings->GetPS2Feedback() );
+
+ prepareFadeSettings( settings->GetGain(), settings->GetFadeInTime(),
+ settings->GetFadeOutTime() );
+ }
+ else
+ {
+ rReleaseString( "Settings is NULL\n" );
+ }
+}
+
+//=============================================================================
+// PS2ReverbController::SetReverbOff
+//=============================================================================
+// Description: Self-explanatory
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void PS2ReverbController::SetReverbOff()
+{
+ startFadeOut();
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/sound/soundfx/ps2reverbcontroller.h b/game/code/sound/soundfx/ps2reverbcontroller.h
new file mode 100644
index 0000000..d756bc2
--- /dev/null
+++ b/game/code/sound/soundfx/ps2reverbcontroller.h
@@ -0,0 +1,54 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: ps2reverbcontroller.h
+//
+// Description: Declaration for the PS2ReverbController class, which provides
+// the PS2-specific reverb control
+//
+// History: 10/28/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef PS2REVERBCONTROLLER_H
+#define PS2REVERBCONTROLLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/soundfx/reverbcontroller.h>
+
+//========================================
+// Forward References
+//========================================
+struct IRadSoundEffectPs2;
+
+//=============================================================================
+//
+// Synopsis: PS2ReverbController
+//
+//=============================================================================
+
+class PS2ReverbController : public ReverbController
+{
+ public:
+ PS2ReverbController();
+ virtual ~PS2ReverbController();
+
+ void SetReverbOn( reverbSettings* settings );
+ void SetReverbOff();
+
+ private:
+ //Prevent wasteful constructor creation.
+ PS2ReverbController( const PS2ReverbController& original );
+ PS2ReverbController& operator=( const PS2ReverbController& rhs );
+
+ //
+ // Radsound's PS2 reverb interface
+ //
+ IRadSoundEffectPs2* m_reverbInterface;
+};
+
+
+#endif // PS2REVERBCONTROLLER_H
+
diff --git a/game/code/sound/soundfx/reverbcontroller.cpp b/game/code/sound/soundfx/reverbcontroller.cpp
new file mode 100644
index 0000000..672d542
--- /dev/null
+++ b/game/code/sound/soundfx/reverbcontroller.cpp
@@ -0,0 +1,410 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: reverbcontroller.cpp
+//
+// Description: Implementation for ReverbController, which turns reverb on
+// and off and sets control values.
+//
+// History: 10/28/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radsound_hal.hpp>
+#include <radnamespace.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundfx/reverbcontroller.h>
+
+#include <sound/soundfx/reverbsettings.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+
+#include <events/eventmanager.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// ReverbController::ReverbController
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ReverbController::ReverbController() :
+ m_targetGain( 0.0f ),
+ m_currentGain( 0.0f ),
+ m_fadeInMultiplier( 1.0f ),
+ m_fadeOutMultiplier( 1.0f ),
+ m_lastReverb( NULL ),
+ m_queuedReverb( NULL )
+{
+ unsigned int event;
+ EventManager* eventMgr = GetEventManager();
+
+ rAssert( eventMgr != NULL );
+
+ //
+ // Register all of the ambience events
+ //
+ for( event = EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_CITY;
+ event < EVENT_LOCATOR + LocatorEvent::PARKED_BIRDS;
+ ++event )
+ {
+ eventMgr->AddListener( this, static_cast<EventEnum>(event) );
+ }
+ eventMgr->AddListener(this, EVENT_MISSION_RESET);
+ eventMgr->AddListener(this, EVENT_MISSION_CHARACTER_RESET);
+
+ // bmc -- add registering of extra ambient sound events (placeholder and otherwise)
+ for( event = EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_COUNTRY_NIGHT;
+ event <= EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER9;
+ ++event )
+ {
+ eventMgr->AddListener( this, static_cast<EventEnum>(event) );
+ }
+
+ for( event = EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_SEASIDE_NIGHT;
+ event <= EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PLACEHOLDER16;
+ ++event )
+ {
+ eventMgr->AddListener( this, static_cast<EventEnum>(event) );
+ }
+ // bmc
+
+
+}
+
+//==============================================================================
+// ReverbController::~ReverbController
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+ReverbController::~ReverbController()
+{
+ GetEventManager()->RemoveAll( this );
+}
+
+//=============================================================================
+// ReverbController::SetReverbGain
+//=============================================================================
+// Description: Set the gain on the entire reverb system
+//
+// Parameters: gain - 0 to 1 gain value
+//
+// Return: None
+//
+//=============================================================================
+void ReverbController::SetReverbGain( float gain )
+{
+ ::radSoundHalSystemGet()->SetAuxGain( REVERB_AUX_EFFECT_NUMBER, gain );
+}
+
+//=============================================================================
+// ReverbController::HandleEvent
+//=============================================================================
+// Description: Listen for events that will cause us to switch reverb modes
+// on and off
+//
+// Parameters: id - event ID
+// pEventData - user data, unused
+//
+// Return: void
+//
+//=============================================================================
+void ReverbController::HandleEvent( EventEnum id, void* pEventData )
+{
+ reverbSettings* settingsObj = NULL;
+ reverbSettings* oldSettings = m_lastReverb;
+
+ switch( id )
+ {
+ case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_ROOM_1:
+ settingsObj = getReverbSettings( "pproom1" );
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_ROOM_2:
+ settingsObj = getReverbSettings( "pproom2" );
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_ROOM_3:
+ settingsObj = getReverbSettings( "pproom3" );
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_TUNNEL_1:
+ settingsObj = getReverbSettings( "PP_tunnel_01" );
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_PP_TUNNEL_2:
+ settingsObj = getReverbSettings( "PP_tunnel_02" );
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_BURNS_TUNNEL:
+ settingsObj = getReverbSettings( "burns_tunnel" );
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_MANSION_INTERIOR:
+ settingsObj = getReverbSettings( "mansion_interior" );
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_SEWERS:
+ settingsObj = getReverbSettings( "sewers" );
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_STONE_CUTTER_TUNNEL:
+ settingsObj = getReverbSettings( "stonecuttertunnel" );
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_STONE_CUTTER_HALL:
+ settingsObj = getReverbSettings( "stonecutterhall" );
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_STADIUM_TUNNEL:
+ settingsObj = getReverbSettings( "stadiumtunnel" );
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::AMBIENT_SOUND_KRUSTYLU_EXTERIOR:
+ settingsObj = getReverbSettings( "krustylu" );
+ break;
+
+ default:
+ //
+ // This should be triggered by any ambient sound trigger that
+ // isn't mapped to reverb above
+ //
+ SetReverbOff();
+ m_lastReverb = NULL;
+ break;
+ }
+
+ if( settingsObj != NULL )
+ {
+ //
+ // No sense in switching to something we're already doing. That only
+ // seems to lead to unnecessary clicks on PS2 anyway.
+ //
+ if( settingsObj != oldSettings )
+ {
+ if( m_currentGain > 0.0f )
+ {
+ //
+ // We can't do a sudden switch without hearing popping. Queue up this
+ // switch to occur after we fade out
+ //
+ prepareFadeSettings( 0.0f, settingsObj->GetFadeInTime(), settingsObj->GetFadeOutTime() );
+ m_queuedReverb = m_lastReverb;
+ }
+ else
+ {
+ SetReverbOn( settingsObj );
+ }
+ }
+ }
+}
+
+//=============================================================================
+// ReverbController::ServiceOncePerFrame
+//=============================================================================
+// Description: Handle the fading in and fading out of reverb
+//
+// Parameters: elapsedTime - elapsed time since last update
+//
+// Return: void
+//
+//=============================================================================
+void ReverbController::ServiceOncePerFrame( unsigned int elapsedTime )
+{
+ if( m_currentGain == m_targetGain )
+ {
+ if( ( m_targetGain == 0.0f )
+ && ( m_queuedReverb != NULL ) )
+ {
+ //
+ // We're done fading out, now we can make the switch and fade
+ // back in
+ //
+ SetReverbOn( m_queuedReverb );
+ m_queuedReverb = NULL;
+ }
+
+ return;
+ }
+ else if( m_currentGain < m_targetGain )
+ {
+ m_currentGain += ( elapsedTime * m_fadeInMultiplier );
+ if( m_currentGain > m_targetGain )
+ {
+ m_currentGain = m_targetGain;
+ }
+ }
+ else
+ {
+ m_currentGain -= ( elapsedTime * m_fadeOutMultiplier );
+ if( m_currentGain < m_targetGain )
+ {
+ m_currentGain = m_targetGain;
+ }
+ }
+
+ SetReverbGain( m_currentGain );
+}
+
+//=============================================================================
+// ReverbController::PauseReverb
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ReverbController::PauseReverb()
+{
+ if( m_lastReverb != NULL )
+ {
+ SetReverbOff();
+ }
+}
+
+//=============================================================================
+// ReverbController::UnpauseReverb
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ReverbController::UnpauseReverb()
+{
+ if( m_lastReverb != NULL )
+ {
+ SetReverbOn( m_lastReverb );
+ }
+}
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+void ReverbController::registerReverbEffect( IRadSoundHalEffect* reverbEffect )
+{
+ ::radSoundHalSystemGet()->SetAuxEffect( REVERB_AUX_EFFECT_NUMBER, reverbEffect );
+ ::radSoundHalSystemGet()->SetAuxGain( REVERB_AUX_EFFECT_NUMBER, 0.0f );
+
+ Sound::daSoundRenderingManagerGet()->GetTheListener()->SetEnvironmentAuxSend( REVERB_AUX_EFFECT_NUMBER );
+ Sound::daSoundRenderingManagerGet()->GetTheListener()->SetEnvEffectsEnabled( true );
+}
+
+//=============================================================================
+// ReverbController::prepareFadeSettings
+//=============================================================================
+// Description: Set up the member values for fading reverb in and out
+//
+// Parameters: targetGain - gain value to gradually move to
+// fadeInTime - time in seconds for the fade, assuming we're
+// going from 0.0 to 1.0
+// fadeOutTime - similar to fadeInTime
+//
+// Return: void
+//
+//=============================================================================
+void ReverbController::prepareFadeSettings( float targetGain, float fadeInTime, float fadeOutTime )
+{
+ m_targetGain = targetGain;
+
+ m_fadeInMultiplier = fadeInTime;
+ if( m_fadeInMultiplier == 0.0f )
+ {
+ m_fadeInMultiplier = 1.0f;
+ }
+ else
+ {
+ m_fadeInMultiplier = 1.0f / m_fadeInMultiplier;
+ }
+ m_fadeOutMultiplier = fadeOutTime;
+ if( m_fadeOutMultiplier == 0.0f )
+ {
+ m_fadeOutMultiplier = 1.0f;
+ }
+ else
+ {
+ m_fadeOutMultiplier = 1.0f / m_fadeInMultiplier;
+ }
+}
+
+//=============================================================================
+// ReverbController::startFadeOut
+//=============================================================================
+// Description: Set up for the gradual reverb fadeout
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void ReverbController::startFadeOut()
+{
+ m_targetGain = 0.0f;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// ReverbController::getReverbSettings
+//=============================================================================
+// Description: Retrieve the object containing a group of reverb settings
+//
+// Parameters: objName - name of reverbSettings object to find
+//
+// Return: void
+//
+//=============================================================================
+reverbSettings* ReverbController::getReverbSettings( const char* objName )
+{
+ IRadNameSpace* nameSpace;
+
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+
+ if ( objName != NULL )
+ {
+ rTunePrintf( "\n\nAUDIO: Reverb Settings: [%s]\n\n\n", objName );
+ }
+
+ m_lastReverb = static_cast<reverbSettings*>(nameSpace->GetInstance( objName ));
+
+ return( m_lastReverb );
+} \ No newline at end of file
diff --git a/game/code/sound/soundfx/reverbcontroller.h b/game/code/sound/soundfx/reverbcontroller.h
new file mode 100644
index 0000000..d3bc037
--- /dev/null
+++ b/game/code/sound/soundfx/reverbcontroller.h
@@ -0,0 +1,83 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: reverbcontroller.h
+//
+// Description: Declaration for the ReverbController class, which turns reverb
+// on and off and applies the required control values.
+//
+// History: 10/28/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef REVERBCONTROLLER_H
+#define REVERBCONTROLLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+
+struct IRadSoundHalEffect;
+class reverbSettings;
+
+//=============================================================================
+//
+// Synopsis: ReverbController
+//
+//=============================================================================
+
+class ReverbController : public EventListener
+{
+ public:
+ ReverbController();
+ virtual ~ReverbController();
+
+ void SetReverbGain( float gain );
+
+ virtual void SetReverbOn( reverbSettings* settings ) = 0;
+ virtual void SetReverbOff() = 0;
+
+ virtual void PauseReverb();
+ virtual void UnpauseReverb();
+
+ //
+ // EventListener interface
+ //
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ void ServiceOncePerFrame( unsigned int elapsedTime );
+
+ protected:
+ static const int REVERB_AUX_EFFECT_NUMBER = 0;
+
+ void registerReverbEffect( IRadSoundHalEffect* reverbEffect );
+ void prepareFadeSettings( float targetGain, float fadeInTime, float fadeOutTime );
+ void startFadeOut();
+
+ private:
+ //Prevent wasteful constructor creation.
+ ReverbController( const ReverbController& original );
+ ReverbController& operator=( const ReverbController& rhs );
+
+ reverbSettings* getReverbSettings( const char* objName );
+
+ //
+ // Used for gradual changes in reverb
+ //
+ float m_targetGain;
+ float m_currentGain;
+ float m_fadeInMultiplier;
+ float m_fadeOutMultiplier;
+
+ reverbSettings* m_lastReverb;
+ reverbSettings* m_queuedReverb;
+};
+
+
+#endif // REVERBCONTROLLER_H
+
diff --git a/game/code/sound/soundfx/reverbsettings.cpp b/game/code/sound/soundfx/reverbsettings.cpp
new file mode 100644
index 0000000..d44c200
--- /dev/null
+++ b/game/code/sound/soundfx/reverbsettings.cpp
@@ -0,0 +1,133 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: reverbsettings.cpp
+//
+// Description: Definition for the reverbSettings class, which stores sets
+// of reverb parameters to be applied whenever we want that
+// reverby sound thing happening. NOTE: lower-case "r" needed
+// to make RadScript happy.
+//
+// History: 11/5/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundfx/reverbsettings.h>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Initialially the list is empty
+//
+reverbSettings* radLinkedClass< reverbSettings >::s_pLinkedClassHead = NULL;
+reverbSettings* radLinkedClass< reverbSettings >::s_pLinkedClassTail = NULL;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// reverbSettings::reverbSettings
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+reverbSettings::reverbSettings() :
+ radRefCount( 0 ),
+
+ m_gain( 0.0f ),
+ m_fadeInTime( 0.0f ),
+ m_fadeOutTime( 0.0f ),
+
+ m_xboxRoom( 0 ),
+ m_xboxRoomHF( 0 ),
+ m_xboxRoomRolloff( 0.0f ),
+ m_xboxDecay( 0.0f ),
+ m_xboxDecayHFRatio( 0.0f ),
+ m_xboxReflections( 0 ),
+ m_xboxReflectionsDelay( 0.0f ),
+ m_xboxReverb( 0 ),
+ m_xboxReverbDelay( 0.0f ),
+ m_xboxDiffusion( 0.0f ),
+ m_xboxDensity( 0.0f ),
+ m_xboxHFReference( 0.0f ),
+
+ m_ps2ReverbMode( 0 ),
+ m_ps2Delay( 0.0f ),
+ m_ps2Feedback( 0.0f ),
+
+ m_gcPreDelay( 0.0f ),
+ m_gcReverbTime( 0.0f ),
+ m_gcColoration( 0.0f ),
+ m_gcDamping( 0.0f ),
+
+ m_winEnvironmentDiffusion( 1.0f ),
+ m_winAirAbsorptionHF( -5.0f )
+{
+}
+
+//==============================================================================
+// reverbSettings::~reverbSettings
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+reverbSettings::~reverbSettings()
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//******************************************************************************
+// Factory functions
+//******************************************************************************
+
+//==============================================================================
+// ReverbSettingsObjCreate
+//==============================================================================
+// Description: Factory function for creating reverbSettings objects.
+// Called by RadScript.
+//
+// Parameters: ppSettings - Address of ptr to new object
+// allocator - FTT pool to allocate object within
+//
+// Return: N/A.
+//
+//==============================================================================
+void ReverbSettingsObjCreate
+(
+ IReverbSettings** ppSettings,
+ radMemoryAllocator allocator
+)
+{
+ rAssert( ppSettings != NULL );
+ (*ppSettings) = new ( allocator ) reverbSettings( );
+ (*ppSettings)->AddRef( );
+}
+
diff --git a/game/code/sound/soundfx/reverbsettings.h b/game/code/sound/soundfx/reverbsettings.h
new file mode 100644
index 0000000..8ea3bbe
--- /dev/null
+++ b/game/code/sound/soundfx/reverbsettings.h
@@ -0,0 +1,158 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: reverbsettings.h
+//
+// Description: Declaration for the reverbSettings class, which stores sets
+// of reverb parameters to be applied whenever we want that
+// reverby sound thing happening. NOTE: lower-case "r" needed
+// to make RadScript happy.
+//
+// History: 11/5/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef REVERBSETTINGS_H
+#define REVERBSETTINGS_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radobject.hpp>
+#include <radlinkedclass.hpp>
+
+#include <sound/soundfx/ireverbsettings.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: reverbSettings
+//
+//=============================================================================
+
+class reverbSettings : public IReverbSettings,
+ public radLinkedClass< reverbSettings >,
+ public radRefCount
+{
+ public:
+ IMPLEMENT_REFCOUNTED( "reverbSettings" );
+
+ reverbSettings();
+ virtual ~reverbSettings();
+
+ void SetGain( float gain ) { m_gain = gain; }
+ float GetGain() { return( m_gain ); }
+ void SetFadeInTime( float milliseconds ) { m_fadeInTime = milliseconds; }
+ float GetFadeInTime() { return( m_fadeInTime ); }
+ void SetFadeOutTime( float milliseconds ) { m_fadeOutTime = milliseconds; }
+ float GetFadeOutTime() { return( m_fadeOutTime ); }
+
+ //
+ // See radsound_<platform name>.hpp for details on this stuff
+ //
+ void SetXboxRoom( int mBvalue ) { m_xboxRoom = mBvalue; }
+ int GetXboxRoom() { return( m_xboxRoom ); }
+ void SetXboxRoomHF( int mBvalue ) { m_xboxRoomHF = mBvalue; }
+ int GetXboxRoomHF() { return( m_xboxRoomHF ); }
+ void SetXboxRoomRolloffFactor( float value ) { m_xboxRoomRolloff = value; }
+ float GetXboxRoomRolloffFactor() { return( m_xboxRoomRolloff ); }
+ void SetXboxDecayTime( float value ) { m_xboxDecay = value; }
+ float GetXboxDecayTime() { return( m_xboxDecay ); }
+ void SetXboxDecayHFRatio( float value ) { m_xboxDecayHFRatio = value; }
+ float GetXboxDecayHFRatio() { return( m_xboxDecayHFRatio ); }
+ void SetXboxReflections( int mBvalue ) { m_xboxReflections = mBvalue; }
+ int GetXboxReflections() { return( m_xboxReflections ); }
+ void SetXboxReflectionsDelay( float value ) { m_xboxReflectionsDelay = value; }
+ float GetXboxReflectionsDelay() { return( m_xboxReflectionsDelay ); }
+ void SetXboxReverb( int mBvalue ) { m_xboxReverb = mBvalue; }
+ int GetXboxReverb() { return( m_xboxReverb ); }
+ void SetXboxReverbDelay( float value ) { m_xboxReverbDelay = value; }
+ float GetXboxReverbDelay() { return( m_xboxReverbDelay ); }
+ void SetXboxDiffusion( float value ) { m_xboxDiffusion = value; }
+ float GetXboxDiffusion() { return( m_xboxDiffusion ); }
+ void SetXboxDensity( float value ) { m_xboxDensity = value; }
+ float GetXboxDensity() { return( m_xboxDensity ); }
+ void SetXboxHFReference( float value ) { m_xboxHFReference = value; }
+ float GetXboxHFReference() { return( m_xboxHFReference ); }
+
+ // No RadTuner interface for enumerations as far as I know, so
+ // we'll have to cast whatever integer we get here
+ void SetPS2ReverbMode( int mode ) { m_ps2ReverbMode = mode; }
+ int GetPS2ReverbMode() { return( m_ps2ReverbMode ); }
+
+ void SetPS2Delay( float delayTime ) { m_ps2Delay = delayTime; }
+ float GetPS2Delay() { return( m_ps2Delay ); }
+ void SetPS2Feedback( float feedback ) { m_ps2Feedback = feedback; }
+ float GetPS2Feedback() { return( m_ps2Feedback ); }
+
+ void SetGCPreDelay( float milliseconds ) { m_gcPreDelay = milliseconds; }
+ float GetGCPreDelay() { return( m_gcPreDelay ); }
+ void SetGCReverbTime( float milliseconds ) { m_gcReverbTime = milliseconds; }
+ float GetGCReverbTime() { return( m_gcReverbTime ); }
+ void SetGCColoration( float coloration ) { m_gcColoration = coloration; }
+ float GetGCColoration() { return( m_gcColoration ); }
+ void SetGCDamping( float damping ) { m_gcDamping = damping; }
+ float GetGCDamping() { return( m_gcDamping ); }
+
+ // Must be defined for all platforms cause of the script.
+ void SetWinEnvironmentDiffusion( float diffusion ) { m_winEnvironmentDiffusion = diffusion; }
+ float GetWinEnvironmentDiffusion() const { return m_winEnvironmentDiffusion; }
+ void SetWinAirAbsorptionHF( float value ) { m_winAirAbsorptionHF = value; }
+ float GetWinAirAbsorptionHF() const { return m_winAirAbsorptionHF; }
+
+ private:
+ //Prevent wasteful constructor creation.
+ reverbSettings( const reverbSettings& original );
+ reverbSettings& operator=( const reverbSettings& rhs );
+
+ //
+ // Reverb parameters
+ //
+ float m_gain;
+ float m_fadeInTime;
+ float m_fadeOutTime;
+
+ int m_xboxRoom;
+ int m_xboxRoomHF;
+ float m_xboxRoomRolloff;
+ float m_xboxDecay;
+ float m_xboxDecayHFRatio;
+ int m_xboxReflections;
+ float m_xboxReflectionsDelay;
+ int m_xboxReverb;
+ float m_xboxReverbDelay;
+ float m_xboxDiffusion;
+ float m_xboxDensity;
+ float m_xboxHFReference;
+
+ int m_ps2ReverbMode;
+ float m_ps2Delay;
+ float m_ps2Feedback;
+
+ float m_gcPreDelay;
+ float m_gcReverbTime;
+ float m_gcColoration;
+ float m_gcDamping;
+
+ float m_winEnvironmentDiffusion;
+ float m_winAirAbsorptionHF;
+};
+
+//=============================================================================
+// Factory Functions
+//=============================================================================
+
+//
+// Create a reverbSettings object
+//
+void ReverbSettingsObjCreate
+(
+ IReverbSettings** ppSettings,
+ radMemoryAllocator allocator
+);
+
+#endif // REVERBSETTINGS_H
+
diff --git a/game/code/sound/soundfx/soundeffectplayer.cpp b/game/code/sound/soundfx/soundeffectplayer.cpp
new file mode 100644
index 0000000..5847956
--- /dev/null
+++ b/game/code/sound/soundfx/soundeffectplayer.cpp
@@ -0,0 +1,249 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundeffectplayer.cpp
+//
+// Description: Implement SoundEffectPlayer, which switches between
+// the objects that direct the soundfx logic for various game
+// states
+//
+// History: 31/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundfx/soundeffectplayer.h>
+
+#include <sound/soundfx/soundfxfrontendlogic.h>
+#include <sound/soundfx/soundfxgameplaylogic.h>
+#include <sound/soundfx/soundfxpauselogic.h>
+
+#ifdef RAD_XBOX
+#include <sound/soundfx/xboxreverbcontroller.h>
+#elif RAD_WIN32
+#include <sound/soundfx/win32reverbcontroller.h>
+#elif RAD_PS2
+#include <sound/soundfx/ps2reverbcontroller.h>
+#else
+#include <sound/soundfx/gcreverbcontroller.h>
+#endif
+
+#include <memory/srrmemory.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundEffectPlayer::SoundEffectPlayer
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundEffectPlayer::SoundEffectPlayer() :
+ m_reverbController( NULL ),
+ m_currentState( FXSTATE_INVALID )
+{
+ unsigned int i;
+
+ for( i = 0; i < FXSTATE_MAX_STATES; i++ )
+ {
+ m_logicObjects[i] = NULL;
+ }
+
+ initialize();
+}
+
+//==============================================================================
+// SoundEffectPlayer::~SoundEffectPlayer
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundEffectPlayer::~SoundEffectPlayer()
+{
+ unsigned int i;
+
+ for( i = 0; i < FXSTATE_MAX_STATES; i++ )
+ {
+ if( m_logicObjects[i] != NULL )
+ {
+ delete m_logicObjects[i];
+ }
+ }
+
+ delete m_reverbController;
+}
+
+//=============================================================================
+// SoundEffectPlayer::ServiceOncePerFrame
+//=============================================================================
+// Description: Positional sound servicing
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundEffectPlayer::ServiceOncePerFrame( unsigned int elapsedTime )
+{
+ if( m_currentState != FXSTATE_INVALID )
+ {
+ m_logicObjects[m_currentState]->ServiceOncePerFrame( elapsedTime );
+ }
+
+ m_reverbController->ServiceOncePerFrame( elapsedTime );
+}
+
+void SoundEffectPlayer::OnPauseStart()
+{
+ m_reverbController->PauseReverb();
+}
+
+void SoundEffectPlayer::OnPauseEnd()
+{
+ m_reverbController->UnpauseReverb();
+}
+
+//=============================================================================
+// SoundEffectPlayer::PlayCarOptionStinger
+//=============================================================================
+// Description: The following four functions play stinger sounds for the
+// level settings in the options menu
+//
+// Parameters: trim - trim setting to play it at
+//
+// Return: void
+//
+//=============================================================================
+void SoundEffectPlayer::PlayCarOptionStinger( float trim )
+{
+ playStinger( "car_stinger", trim );
+}
+
+void SoundEffectPlayer::PlayDialogOptionStinger( float trim )
+{
+ playStinger( "dialog_stinger", trim );
+}
+
+void SoundEffectPlayer::PlayMusicOptionStinger( float trim )
+{
+ playStinger( "music_stinger", trim );
+}
+
+void SoundEffectPlayer::PlaySfxOptionStinger( float trim )
+{
+ playStinger( "sfx_stinger", trim );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// SoundEffectPlayer::initialize
+//=============================================================================
+// Description: Create the sound FX logic objects during initialization
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundEffectPlayer::initialize()
+{
+ m_logicObjects[FXSTATE_FRONTEND] = new(GMA_PERSISTENT) SoundFXFrontEndLogic();
+ m_logicObjects[FXSTATE_GAMEPLAY] = new(GMA_PERSISTENT) SoundFXGameplayLogic();
+ m_logicObjects[FXSTATE_PAUSED] = new(GMA_PERSISTENT) SoundFXPauseLogic();
+
+#ifdef RAD_PS2
+ m_reverbController = new(GMA_PERSISTENT) PS2ReverbController();
+#elif RAD_XBOX
+ m_reverbController = new(GMA_PERSISTENT) XboxReverbController();
+#elif RAD_WIN32
+ m_reverbController = new(GMA_PERSISTENT) Win32ReverbController();
+#else
+ m_reverbController = new(GMA_PERSISTENT) GCReverbController();
+#endif
+}
+
+//=============================================================================
+// SoundEffectPlayer::setSFXState
+//=============================================================================
+// Description: Switch to a new sound FX state
+//
+// Parameters: newState - state to be switched to
+//
+// Return: void
+//
+//=============================================================================
+void SoundEffectPlayer::setSFXState( SFXState newState )
+{
+ rAssert( newState < FXSTATE_MAX_STATES );
+
+ if( m_currentState != FXSTATE_INVALID )
+ {
+ m_logicObjects[m_currentState]->UnregisterEventListeners();
+ }
+
+ m_currentState = newState;
+ m_logicObjects[m_currentState]->RegisterEventListeners();
+}
+
+//=============================================================================
+// SoundEffectPlayer::doCleanup
+//=============================================================================
+// Description: Shut down anything that might be playing
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SoundEffectPlayer::doCleanup()
+{
+ m_logicObjects[m_currentState]->Cleanup();
+
+ m_reverbController->SetReverbOff();
+}
+
+//=============================================================================
+// SoundEffectPlayer::playStinger
+//=============================================================================
+// Description: Play the given stinger resource at the specified trim
+//
+// Parameters: stingerName - name of sound resource for stinger
+// trim - volume to play it at
+//
+// Return: void
+//
+//=============================================================================
+void SoundEffectPlayer::playStinger( const char* stingerName, float trim )
+{
+ m_stingerPlayer.PlaySound( stingerName );
+ m_stingerPlayer.SetTrim( trim );
+}
diff --git a/game/code/sound/soundfx/soundeffectplayer.h b/game/code/sound/soundfx/soundeffectplayer.h
new file mode 100644
index 0000000..3985a97
--- /dev/null
+++ b/game/code/sound/soundfx/soundeffectplayer.h
@@ -0,0 +1,122 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundeffectplayer.h
+//
+// Description: Declaration for SoundEffectPlayer class, which switches between
+// the objects that direct the soundfx logic for various game
+// states
+//
+// History: 31/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDEFFECTPLAYER_H
+#define SOUNDEFFECTPLAYER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <sound/simpsonssoundplayer.h>
+
+//========================================
+// Forward References
+//========================================
+
+class SoundFXLogic;
+class ReverbController;
+
+//=============================================================================
+//
+// Synopsis: SoundEffectPlayer
+//
+//=============================================================================
+
+class SoundEffectPlayer
+{
+ public:
+ SoundEffectPlayer();
+ virtual ~SoundEffectPlayer();
+
+ //
+ // Start playing front end sounds
+ //
+ void OnFrontEndStart() { setSFXState( FXSTATE_FRONTEND ); }
+
+ //
+ // Start playing gameplay sounds
+ //
+ void OnGameplayStart() { setSFXState( FXSTATE_GAMEPLAY ); }
+ void OnGameplayEnd() { doCleanup(); }
+
+ void OnPauseStart();
+ void OnPauseEnd();
+
+ void ServiceOncePerFrame( unsigned int elapsedTime );
+
+ void PlayCarOptionStinger( float trim );
+ void PlayDialogOptionStinger( float trim );
+ void PlayMusicOptionStinger( float trim );
+ void PlaySfxOptionStinger( float trim );
+
+ private:
+ //Prevent wasteful constructor creation.
+ SoundEffectPlayer( const SoundEffectPlayer& original );
+ SoundEffectPlayer& operator=( const SoundEffectPlayer& rhs );
+
+ void initialize();
+
+ //
+ // Game states in which sound effects are played
+ //
+ enum SFXState
+ {
+ FXSTATE_FRONTEND,
+ FXSTATE_GAMEPLAY,
+ FXSTATE_PAUSED,
+
+ FXSTATE_MAX_STATES,
+
+ FXSTATE_INVALID
+ };
+
+ //
+ // Set a new SFX state
+ //
+ void setSFXState( SFXState newState );
+
+ //
+ // Shut down anything that might still be playing
+ //
+ void doCleanup();
+
+ //
+ // Play a stinger (duh)
+ //
+ void playStinger( const char* stingerName, float trim );
+
+ //
+ // FX logic objects, one for each state
+ //
+ SoundFXLogic* m_logicObjects[FXSTATE_MAX_STATES];
+
+ //
+ // Reverb controller
+ //
+ ReverbController* m_reverbController;
+
+ //
+ // Current SFX state
+ //
+ SFXState m_currentState;
+
+ //
+ // Options menu stinger player
+ //
+ SimpsonsSoundPlayer m_stingerPlayer;
+};
+
+
+#endif // SOUNDEFFECTPLAYER_H
+
diff --git a/game/code/sound/soundfx/soundfxfrontendlogic.cpp b/game/code/sound/soundfx/soundfxfrontendlogic.cpp
new file mode 100644
index 0000000..d2a07aa
--- /dev/null
+++ b/game/code/sound/soundfx/soundfxfrontendlogic.cpp
@@ -0,0 +1,137 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundfxfrontendlogic.cpp
+//
+// Description: Implements the SoundFXFrontEndLogic class, which handles
+// the translation of events into sound effects for the front
+// end.
+//
+// History: 31/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundfx/soundfxfrontendlogic.h>
+
+#include <sound/soundmanager.h>
+
+#include <events/eventmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundFXFrontEndLogic::SoundFXFrontEndLogic
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundFXFrontEndLogic::SoundFXFrontEndLogic()
+{
+}
+
+//==============================================================================
+// SoundFXFrontEndLogic::~SoundFXFrontEndLogic
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundFXFrontEndLogic::~SoundFXFrontEndLogic()
+{
+}
+
+//=============================================================================
+// SoundFXFrontEndLogic::RegisterEventListeners
+//=============================================================================
+// Description: Register as listener of sound effect events with Event Manager
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXFrontEndLogic::RegisterEventListeners()
+{
+ GetEventManager()->AddListener( this, EVENT_FE_MENU_SELECT );
+ GetEventManager()->AddListener( this, EVENT_FE_MENU_BACK );
+ GetEventManager()->AddListener( this, EVENT_FE_MENU_UPORDOWN );
+ GetEventManager()->AddListener( this, EVENT_FE_CHEAT_SUCCESS );
+ GetEventManager()->AddListener( this, EVENT_FE_CHEAT_FAILURE );
+ GetEventManager()->AddListener( this, EVENT_FE_CREDITS_NEW_LINE );
+}
+
+//=============================================================================
+// SoundFXFrontEndLogic::HandleEvent
+//=============================================================================
+// Description: Play sound effects in response to events
+//
+// Parameters: id - Sound effect event identifier
+// pEventData - Currently unused
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXFrontEndLogic::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_FE_MENU_SELECT:
+ playSFXSound( "accept", true, false, NULL, GetSoundManager()->GetSfxVolume() );
+ break;
+
+ case EVENT_FE_MENU_BACK:
+ playSFXSound( "back", true, false, NULL, GetSoundManager()->GetSfxVolume() );
+ break;
+
+ case EVENT_FE_MENU_UPORDOWN:
+ playSFXSound( "scroll", true, false, NULL, GetSoundManager()->GetSfxVolume() );
+ break;
+
+ case EVENT_FE_CHEAT_SUCCESS:
+ playSFXSound( "cheat_success", true, false, NULL, GetSoundManager()->GetSfxVolume() );
+ break;
+
+ case EVENT_FE_CHEAT_FAILURE:
+ playSFXSound( "cheat_fail", true, false, NULL, GetSoundManager()->GetSfxVolume() );
+ break;
+
+ case EVENT_FE_CREDITS_NEW_LINE:
+ playCreditLine( reinterpret_cast<int>(pEventData) );
+ break;
+
+ default:
+ rAssertMsg( false, "Huh? Unexpected sound FX event\n" );
+ break;
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
diff --git a/game/code/sound/soundfx/soundfxfrontendlogic.h b/game/code/sound/soundfx/soundfxfrontendlogic.h
new file mode 100644
index 0000000..eebe139
--- /dev/null
+++ b/game/code/sound/soundfx/soundfxfrontendlogic.h
@@ -0,0 +1,50 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundfxfrontendlogic.h
+//
+// Description: Declaration for the SoundFXFrontEndLogic class, which handles
+// the translation of events into sound effects for the front
+// end.
+//
+// History: 31/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDFXFRONTENDLOGIC_H
+#define SOUNDFXFRONTENDLOGIC_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/soundfx/soundfxlogic.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: SoundFXFrontEndLogic
+//
+//=============================================================================
+
+class SoundFXFrontEndLogic : public SoundFXLogic
+{
+ public:
+ SoundFXFrontEndLogic();
+ virtual ~SoundFXFrontEndLogic();
+
+ void RegisterEventListeners();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ private:
+ //Prevent wasteful constructor creation.
+ SoundFXFrontEndLogic( const SoundFXFrontEndLogic& original );
+ SoundFXFrontEndLogic& operator=( const SoundFXFrontEndLogic& rhs );
+};
+
+
+#endif // SOUNDFXFRONTENDLOGIC_H
+
diff --git a/game/code/sound/soundfx/soundfxgameplaylogic.cpp b/game/code/sound/soundfx/soundfxgameplaylogic.cpp
new file mode 100644
index 0000000..20b995e
--- /dev/null
+++ b/game/code/sound/soundfx/soundfxgameplaylogic.cpp
@@ -0,0 +1,1212 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundfxgameplaylogic.cpp
+//
+// Description: Implements the SoundFXGameplayLogic class, which handles
+// the translation of events into sound effects for the game
+//
+// History: 31/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radnamespace.hpp>
+#include <radtime.hpp>
+#include <radsound_hal.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundfx/soundfxgameplaylogic.h>
+
+#include <sound/soundmanager.h>
+#include <sound/soundcollisiondata.h>
+#include <sound/soundfx/positionalsoundsettings.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/soundallocatedresource.h>
+#include <sound/avatar/carsoundparameters.h>
+#include <sound/tuning/globalsettings.h>
+
+#include <events/eventmanager.h>
+#include <meta/scriptlocator.h>
+#include <worldsim/physicsairef.h>
+#include <worldsim/character/character.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/avatarmanager.h>
+#include <mission/gameplaymanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// These are compared against the collision impulse value, which is 0 - 1.
+//
+
+static const float ARBITRARY_IMPULSE_THRESHOLD = 0.01f;
+
+// Above this value, send big crash event to dialog system
+static const float ARBITRARY_BIG_CRASH_THRESHOLD = 0.15f;
+
+// These values determine whether to play small, medium, or big crash
+// sounds for vehicles
+static const float ARBITRARY_VEHICLE_MEDIUM_CRASH_THRESHOLD = 0.15f;
+static const float ARBITRARY_VEHICLE_BIG_CRASH_THRESHOLD = 0.25f;
+
+static const char* s_hydrantSprayName = "hydrant_spray";
+
+//
+// Arbitrary value. If a collision occurs between the avatar vehicle
+// and an object, where the difference between their distances is greater
+// than this, then the avatar hit a wall or something, so don't average
+// the positions.
+//
+static const float POSITIONAL_COLLISION_MAX_DISTANCE_SQR = 100.0f;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundFXGameplayLogic::SoundFXGameplayLogic
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundFXGameplayLogic::SoundFXGameplayLogic() :
+ m_collisionMinMax( NULL ),
+ m_coinCounter( 0 ),
+ m_lastRonkTime( 0 ),
+ m_globalSettings( NULL )
+{
+}
+
+//==============================================================================
+// SoundFXGameplayLogic::~SoundFXGameplayLogic
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundFXGameplayLogic::~SoundFXGameplayLogic()
+{
+}
+
+//=============================================================================
+// SoundFXGameplayLogic::RegisterEventListeners
+//=============================================================================
+// Description: Register as listener of sound effect events with Event Manager
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXGameplayLogic::RegisterEventListeners()
+{
+ EventManager* eventMgr = GetEventManager();
+
+ eventMgr->AddListener( this, EVENT_COLLISION );
+ eventMgr->AddListener( this, EVENT_FOOTSTEP );
+ eventMgr->AddListener( this, EVENT_BIG_RED_SWITCH_PRESSED );
+ eventMgr->AddListener( this, EVENT_JUMP_TAKEOFF );
+ eventMgr->AddListener( this, EVENT_JUMP_LANDING );
+ eventMgr->AddListener( this, EVENT_POSITIONAL_SOUND_TRIGGER_HIT );
+ eventMgr->AddListener( this, EVENT_GETINTOVEHICLE_START );
+ eventMgr->AddListener( this, EVENT_GETOUTOFVEHICLE_END );
+ eventMgr->AddListener( this, EVENT_FE_MENU_SELECT );
+ eventMgr->AddListener( this, EVENT_FE_MENU_UPORDOWN );
+ eventMgr->AddListener( this, EVENT_FE_MENU_BACK );
+ eventMgr->AddListener( this, EVENT_COLLECTED_COINS );
+ eventMgr->AddListener( this, EVENT_LOST_COINS );
+ eventMgr->AddListener( this, EVENT_SPAWNED_COINS );
+ eventMgr->AddListener( this, EVENT_PHONE_BOOTH_BUSY );
+ eventMgr->AddListener( this, EVENT_PLAYER_CAR_HIT_NPC );
+ eventMgr->AddListener( this, EVENT_BIG_BOOM_SOUND );
+ eventMgr->AddListener( this, EVENT_CARD_COLLECTED );
+ eventMgr->AddListener( this, EVENT_COLLECTED_WRENCH );
+ eventMgr->AddListener( this, EVENT_VEHICLE_SUSPENSION_BOTTOMED_OUT );
+ eventMgr->AddListener( this, EVENT_MISSION_COLLECTIBLE_PICKED_UP );
+ eventMgr->AddListener( this, EVENT_KICK_NPC );
+ eventMgr->AddListener( this, EVENT_OBJECT_KICKED );
+ eventMgr->AddListener( this, EVENT_WASP_BULLET_FIRED );
+ eventMgr->AddListener( this, EVENT_WASP_BULLET_MISSED );
+ eventMgr->AddListener( this, EVENT_WASP_BULLET_HIT_CHARACTER_STYLIZED_VIOLENCE_FOLLOWS );
+ eventMgr->AddListener( this, EVENT_START_ANIMATION_SOUND );
+ eventMgr->AddListener( this, EVENT_MISSION_SUCCESS );
+ eventMgr->AddListener( this, EVENT_STAGE_COMPLETE );
+ eventMgr->AddListener( this, EVENT_HIT_AND_RUN_CAUGHT );
+ eventMgr->AddListener( this, EVENT_HIT_AND_RUN_METER_THROB );
+ eventMgr->AddListener( this, EVENT_SHOW_MISSION_OBJECTIVE );
+ eventMgr->AddListener( this, EVENT_PLAY_BIRD_SOUND );
+ eventMgr->AddListener( this, static_cast<EventEnum>(EVENT_LOCATOR + LocatorEvent::BOUNCEPAD) );
+ eventMgr->AddListener( this, EVENT_FE_PAUSE_MENU_END );
+ eventMgr->AddListener( this, EVENT_FE_PAUSE_MENU_START );
+ eventMgr->AddListener( this, EVENT_FE_CANCEL );
+ eventMgr->AddListener( this, EVENT_FE_CONTINUE );
+ eventMgr->AddListener( this, EVENT_FE_LOCKED_OUT );
+ eventMgr->AddListener( this, EVENT_BARREL_BLOWED_UP );
+ eventMgr->AddListener( this, EVENT_FE_CREDITS_NEW_LINE );
+}
+
+//=============================================================================
+// SoundFXGameplayLogic::HandleEvent
+//=============================================================================
+// Description: Play sound effects in response to events
+//
+// Parameters: id - Sound effect event identifier
+// pEventData - Currently unused
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXGameplayLogic::HandleEvent( EventEnum id, void* pEventData )
+{
+ Vehicle* vehiclePtr;
+ unsigned int ronkTime;
+ const char* soundName;
+ RenderEnums::LevelEnum level;
+ AnimSoundData* animData;
+
+ switch( id )
+ {
+ case EVENT_COLLISION:
+ handleCollisionEvent( static_cast<SoundCollisionData*>(pEventData) );
+ break;
+
+ case EVENT_FOOTSTEP:
+ handleFootstepEvent( static_cast<Character*>(pEventData) );
+ break;
+
+ case EVENT_JUMP_TAKEOFF:
+ playSFXSound( "jump", false );
+ break;
+
+ case EVENT_JUMP_LANDING:
+ //
+ // TEMPORARY: we need a surface-sensitive solution
+ //
+ playSFXSound( "feet_concrete_jump", false );
+ break;
+
+ case EVENT_BIG_RED_SWITCH_PRESSED:
+ handleSwitchEvent();
+ break;
+
+ case EVENT_POSITIONAL_SOUND_TRIGGER_HIT:
+ playPositionalSound( static_cast<ScriptLocator*>(pEventData) );
+ break;
+
+ case EVENT_GETINTOVEHICLE_START:
+ case EVENT_GETOUTOFVEHICLE_END:
+ playCarDoorSound( id, static_cast<Character*>(pEventData) );
+ break;
+
+ case EVENT_FE_MENU_SELECT:
+ playSFXSound( "accept", false, false, NULL, GetSoundManager()->GetSfxVolume() );
+ break;
+ case EVENT_FE_MENU_UPORDOWN:
+ playSFXSound( "scroll", false, false, NULL, GetSoundManager()->GetSfxVolume() );
+ break;
+ case EVENT_FE_MENU_BACK:
+ playSFXSound( "back", false, false, NULL, GetSoundManager()->GetSfxVolume() );
+ break;
+ case EVENT_FE_LOCKED_OUT:
+ playSFXSound( "locked_out", false );
+ break;
+
+ case EVENT_COLLECTED_COINS:
+ playCoinCollectSound();
+ break;
+
+ case EVENT_SPAWNED_COINS:
+ //
+ // Should this do anything?
+ //
+ break;
+
+ case EVENT_LOST_COINS:
+ playSFXSound( "coin_lose", true );
+ break;
+
+ case EVENT_PHONE_BOOTH_BUSY:
+ playSFXSound( "phonebusy", false );
+ break;
+
+ case EVENT_PLAYER_CAR_HIT_NPC:
+ {
+ //
+ // Set up special collision data for the collision code, because
+ // it won't be getting a collision event. I like saying "collision".
+ //
+ SoundCollisionData collData( 0.0f, static_cast<CollisionEntityDSG*>(pEventData), NULL );
+
+ handleCollisionEvent( &collData );
+ }
+ break;
+
+ case EVENT_KICK_NPC:
+ playSFXSound( "car_hit_pedestrian", true );
+ break;
+
+ case EVENT_OBJECT_KICKED:
+ handleObjectKick( static_cast<CollisionEntityDSG*>(pEventData) );
+ break;
+
+ case EVENT_BIG_BOOM_SOUND:
+ vehiclePtr = static_cast<Vehicle*>(pEventData);
+ rAssert( vehiclePtr != NULL );
+ if( vehiclePtr->mVehicleType == VT_USER )
+ {
+ playSFXSound( "generic_car_explode", false );
+ }
+ break;
+
+ case EVENT_BARREL_BLOWED_UP:
+ playSFXSound( "generic_car_explode", false );
+ break;
+
+ case EVENT_CARD_COLLECTED:
+ //
+ // Only play card sound effect in car, the music system
+ // handles it when on foot
+ //
+ if( GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar() )
+ {
+ playSFXSound( "card_collect", false );
+ }
+ break;
+
+ case EVENT_COLLECTED_WRENCH:
+ playSFXSound( "wrench_collect", false );
+ break;
+
+ case EVENT_VEHICLE_SUSPENSION_BOTTOMED_OUT:
+ //
+ // Play only if we haven't played the last one within a half-second.
+ // Use a simple timestamp to determine this.
+ //
+ ronkTime = ::radTimeGetMilliseconds();
+ if( ( ronkTime < m_lastRonkTime )
+ || ( ( ronkTime - m_lastRonkTime ) > 500 ) )
+ {
+ m_lastRonkTime = ronkTime;
+ playSFXSound( "suspension_ronks", true );
+ }
+ break;
+
+ case EVENT_MISSION_COLLECTIBLE_PICKED_UP:
+ handleCollection();
+ break;
+
+ case EVENT_WASP_BULLET_FIRED:
+ playSFXSound( "wasp_lasers", true );
+ break;
+
+ case EVENT_WASP_BULLET_MISSED:
+ playSFXSound( "laser_miss", true );
+ break;
+
+ case EVENT_WASP_BULLET_HIT_CHARACTER_STYLIZED_VIOLENCE_FOLLOWS:
+ playSFXSound( "character_zapped", true );
+ break;
+
+ case EVENT_START_ANIMATION_SOUND:
+ animData = static_cast<AnimSoundData*>( pEventData );
+ soundName = animData->soundName;
+ if( ( soundName != NULL ) && ( animData->animJoint == NULL ) )
+ {
+ playSFXSound( soundName, false );
+ }
+ break;
+
+ case EVENT_MISSION_SUCCESS:
+ playSFXSound( "mission_complete", false );
+ break;
+
+ case EVENT_STAGE_COMPLETE:
+ playSFXSound( "stage_complete", false );
+ break;
+
+ case EVENT_HIT_AND_RUN_CAUGHT:
+ playSFXSound( "busted", false );
+ break;
+
+ case EVENT_HIT_AND_RUN_METER_THROB:
+ playSFXSound( "rage_warning", false );
+ break;
+
+ case EVENT_SHOW_MISSION_OBJECTIVE:
+ playSFXSound( "pop_up", true );
+ break;
+
+ case EVENT_PLAY_BIRD_SOUND:
+ //
+ // Pick the correct bird effect for the level
+ //
+ level = GetGameplayManager()->GetCurrentLevelIndex();
+ if( ( level == RenderEnums::L2 )
+ || ( level == RenderEnums::L5 ) )
+ {
+ playSFXSound( "pigeon_takeoff", false );
+ }
+ else if( ( level == RenderEnums::L3 )
+ || ( level == RenderEnums::L6 ) )
+ {
+ playSFXSound( "gull_takeoff", false );
+ }
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::BOUNCEPAD :
+ playSFXSound( "trampoline", true );
+ break;
+
+ case EVENT_FE_PAUSE_MENU_START:
+ playSFXSound( "pause_on", true, false, NULL, GetSoundManager()->GetSfxVolume() );
+ break;
+
+ case EVENT_FE_PAUSE_MENU_END:
+ playSFXSound( "pause_off", true, false, NULL, GetSoundManager()->GetSfxVolume() );
+ break;
+
+ case EVENT_FE_CONTINUE:
+ playSFXSound( "continue", true, false, NULL, GetSoundManager()->GetSfxVolume() );
+ break;
+
+ case EVENT_FE_CANCEL:
+ playSFXSound( "cancel", true, false, NULL, GetSoundManager()->GetSfxVolume() );
+ break;
+
+ case EVENT_FE_CREDITS_NEW_LINE:
+ playCreditLine( reinterpret_cast<int>(pEventData) );
+ break;
+
+ default:
+ rAssertMsg( false, "Unexpected event received in SoundFXGameplayLogic\n" );
+ break;
+ }
+}
+
+//=============================================================================
+// SoundFXGameplayLogic::OnPlaybackComplete
+//=============================================================================
+// Description: No longer used, now that tutorial lines are played through
+// the dialog system
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXGameplayLogic::OnPlaybackComplete()
+{
+}
+
+//=============================================================================
+// SoundFXGameplayLogic::Cleanup
+//=============================================================================
+// Description: Called on gameplay exit. Use this to shut down any
+// positional sounds that might be playing.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXGameplayLogic::Cleanup()
+{
+ int i;
+
+ for( i = 0; i < s_numPositionalSounds; i++ )
+ {
+ if( m_positionalSounds[i].IsInUse() )
+ {
+ m_positionalSounds[i].Stop();
+ }
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// SoundFXGameplayLogic::getGlobalSettings
+//=============================================================================
+// Description: Initialize the member pointer to the global settings object
+// if not done yet, and return it
+//
+// Parameters: None
+//
+// Return: pointer to globalSettings object
+//
+//=============================================================================
+globalSettings* SoundFXGameplayLogic::getGlobalSettings()
+{
+ IRadNameSpace* nameSpace;
+
+ if( m_globalSettings == NULL )
+ {
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ m_globalSettings = static_cast<globalSettings*>(nameSpace->GetInstance( "tuner" ));
+ rAssert( m_globalSettings != NULL );
+ }
+
+ return( m_globalSettings );
+}
+
+//=============================================================================
+// SoundFXGameplayLogic::handleCollisionEvent
+//=============================================================================
+// Description: Play a collision sound appropriate for this event, unless we've
+// already received an event for this collision. A typical collision
+// throws a lot of duplicate events, apparently, so we gotta screen
+// 'em out.
+//
+// Parameters: collisionData - data we need pertaining to the collision, passed
+// through the event mechanism
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXGameplayLogic::handleCollisionEvent( SoundCollisionData* collisionData )
+{
+ int i;
+
+ //
+ // FOR NOW: filter out collisions that don't involve the user's car. Later,
+ // we might want to do AI-car collisions within a certain radius or
+ // something
+ //
+
+ //TODO (Cary): I think that this should search the avatar manager for each vehicle in the
+ //collision pair (or always take A so that vehicles don't get doubled events) and test to
+ //see if the vehicle is user controlled. The code as it is only sends events for player 0
+ //and won't work for multiplayer.
+ Vehicle* pVehicle = GetAvatarManager()->GetAvatarForPlayer( 0 )->GetVehicle();
+
+ if( !pVehicle )
+ {
+ return;
+ }
+
+ //
+ // See if we're already playing a sound for this collision, or if all collision
+ // players are in use. If so, exit
+ //
+ for( i = 0; i < s_numCollisionSounds; i++ )
+ {
+ if( m_collisionSounds[i].soundPlayer.IsInUse() )
+ {
+ if( collisionPairMatches( i, collisionData->collObjA, collisionData->collObjB ) )
+ {
+ // Collision sound already being played, do nothing
+ return;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if( i == s_numCollisionSounds )
+ {
+ //
+ // All players are being used
+ //
+ return;
+ }
+
+ CollisionAttributes* attributes;
+ CollisionEntityDSG* collEntities[2];
+ const char* vehicleCrashName = NULL;
+ const char* nonVehicleCrashName = NULL;
+ CollisionEntityDSG* hydrantObject = NULL;
+ bool hydrantHit = false;
+ EventEnum minorCrash = EVENT_MINOR_VEHICLE_CRASH;
+ EventEnum bigCrash = EVENT_BIG_VEHICLE_CRASH;
+ Vehicle* collisionVehicle = NULL;
+ CollisionEntityDSG* otherEntity = NULL;
+ rmt::Vector diffVector;
+ rmt::Vector avatarPosition;
+ rmt::Vector* vectorPtr = NULL;
+ bool avatarVehicleInvolved = false;
+
+ collEntities[0] = collisionData->collObjA;
+ collEntities[1] = collisionData->collObjB;
+
+ //
+ // NOTE: this code assumes that we have at most one vehicle and one non-vehicle
+ // in the collision pair. If we have two non-vehicles, one is lost. Eh.
+ //
+ for( i = 0; i < 2; i++ )
+ {
+ //
+ // If the DSG object is NULL, one of the objects is a fence or something.
+ // Ignore and move on.
+ //
+ if( collEntities[i] != NULL )
+ {
+ if( collEntities[i]->GetAIRef() == PhysicsAIRef::redBrickVehicle )
+ {
+ //
+ // Vehicle, play smashy sound
+ //
+ if( collisionData->mIntensity > ARBITRARY_VEHICLE_BIG_CRASH_THRESHOLD )
+ {
+ vehicleCrashName = "large_car_crash";
+ }
+ else if( collisionData->mIntensity > ARBITRARY_VEHICLE_MEDIUM_CRASH_THRESHOLD )
+ {
+ vehicleCrashName = "medium_car_crash";
+ }
+ else
+ {
+ vehicleCrashName = "small_car_crash";
+ }
+
+ collisionVehicle = static_cast<Vehicle*>( collEntities[i] );
+
+ if( collEntities[i] == pVehicle )
+ {
+ //
+ // Collision involves the user vehicle, store the other
+ // entity
+ //
+ if( i == 0 )
+ {
+ otherEntity = collEntities[1];
+ }
+ else
+ {
+ otherEntity = collEntities[0];
+ }
+
+ avatarVehicleInvolved = true;
+ }
+ }
+ else
+ {
+ //
+ // Non-vehicle, get attributes
+ //
+ attributes = collEntities[i]->GetCollisionAttributes();
+ if( attributes == NULL )
+ {
+ //
+ // No attributes. Character, I assume? It'd be nice if the
+ // Character class filled this in. Something to try.
+ //
+ nonVehicleCrashName = "car_hit_pedestrian";
+ }
+ else
+ {
+ nonVehicleCrashName = attributes->GetSound();
+
+ //
+ // Stinky special case
+ //
+ if( attributes->GetBreakable() == BreakablesEnum::eHydrantBreaking )
+ {
+ hydrantHit = true;
+ hydrantObject = collEntities[i];
+ }
+ }
+
+ //
+ // This eliminates vehicle-on-vehicle collision, so don't use that
+ // type of event for the dialog system
+ //
+ minorCrash = EVENT_MINOR_CRASH;
+ bigCrash = EVENT_BIG_CRASH;
+ }
+ }
+ else
+ {
+ //
+ // This eliminates vehicle-on-vehicle collision, so don't use that
+ // type of event for the dialog system
+ //
+ minorCrash = EVENT_MINOR_CRASH;
+ bigCrash = EVENT_BIG_CRASH;
+ }
+ }
+
+
+ //
+ // Based on how hard the crash is, pass on an event that the dialog
+ // system can use (Q: is this the best place for this? If other
+ // game components start using it, this should be moved).
+ //
+ if( avatarVehicleInvolved )
+ {
+ if( collisionData->mIntensity < ARBITRARY_BIG_CRASH_THRESHOLD )
+ {
+ if( collisionData->mIntensity >= ARBITRARY_IMPULSE_THRESHOLD )
+ {
+ if( minorCrash == EVENT_MINOR_CRASH )
+ {
+ //
+ // Can't be vehicle-on-vehicle
+ //
+ collisionVehicle = NULL;
+ }
+ GetEventManager()->TriggerEvent( minorCrash, collisionVehicle );
+ }
+ }
+ else
+ {
+ if( bigCrash == EVENT_BIG_CRASH )
+ {
+ //
+ // Isn't vehicle-on-vehicle
+ //
+ collisionVehicle = NULL;
+ }
+ GetEventManager()->TriggerEvent( bigCrash, collisionVehicle );
+ }
+ }
+
+ //
+ // Play the collision sound if we've got a player free
+ //
+ rAssert( collisionData->mIntensity >= 0.0f );
+ rAssert( collisionData->mIntensity <= 1.0f );
+
+ //
+ // Check whether the colliding entities are really far apart. If so,
+ // then the player has hit a wall or something, so don't bother averaging
+ // the distances between them, just play at the avatar position
+ //
+ if( otherEntity != NULL )
+ {
+ pVehicle->GetPosition( &avatarPosition );
+ otherEntity->GetPosition( &diffVector );
+ diffVector -= avatarPosition;
+
+ if( diffVector.MagnitudeSqr() > POSITIONAL_COLLISION_MAX_DISTANCE_SQR )
+ {
+ vectorPtr = &avatarPosition;
+ }
+ }
+
+ //
+ // Play the sounds in free players (or kill something
+ // if they're all in use)
+ //
+ if( vehicleCrashName != NULL )
+ {
+ startCollisionPlayer( vehicleCrashName, collisionData->collObjA, collisionData->collObjB, vectorPtr );
+ }
+ if( nonVehicleCrashName != NULL )
+ {
+ startCollisionPlayer( nonVehicleCrashName, collisionData->collObjA, collisionData->collObjB, vectorPtr );
+
+ //
+ // Stinky fire hydrant hack.
+ //
+ if( hydrantHit )
+ {
+ rAssert( hydrantObject != NULL );
+ startCollisionPlayer( s_hydrantSprayName, hydrantObject, NULL, NULL );
+ }
+ }
+}
+
+//=============================================================================
+// SoundFXGameplayLogic::handleObjectKick
+//=============================================================================
+// Description: Play a sound for an object that gets kicked
+//
+// Parameters: collObject - kicked object
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXGameplayLogic::handleObjectKick( CollisionEntityDSG* collObject )
+{
+ CollisionAttributes* attributes;
+ const char* soundName;
+
+ attributes = collObject->GetCollisionAttributes();
+ if( attributes != NULL )
+ {
+ soundName = attributes->GetSound();
+
+ //
+ // Cheat: don't play this positionally because I'd have to work around
+ // the double-play filter because of the stinky fire hydrant. Hope
+ // that's okay
+ //
+ playSFXSound( soundName, true );
+
+ //
+ // More hydrant hack
+ //
+ if( attributes->GetBreakable() == BreakablesEnum::eHydrantBreaking )
+ {
+ startCollisionPlayer( s_hydrantSprayName, collObject, NULL, NULL );
+ }
+ }
+}
+
+//=============================================================================
+// SoundFXGameplayLogic::collisionPairMatches
+//=============================================================================
+// Description: Check for match between parameters and collision pair at
+// given index
+//
+// Parameters: index - index into m_collisionSource that we're matching against
+// firstObj, secondObj - collision objects to match with
+//
+// Return: true if matching, false otherwise
+//
+//=============================================================================
+bool SoundFXGameplayLogic::collisionPairMatches( int index, void* firstObj, void* secondObj )
+{
+ PositionalSFXPlayer* storedPair;
+
+ rAssert( index < s_numCollisionSounds );
+
+ storedPair = &(m_collisionSounds[index]);
+
+ return( ( ( storedPair->collObjA == firstObj ) &&
+ ( storedPair->collObjB == secondObj ) )
+
+ ||
+
+ ( ( storedPair->collObjA == secondObj ) &&
+ ( storedPair->collObjB == firstObj ) ) );
+}
+
+//=============================================================================
+// SoundFXGameplayLogic::startCollisionPlayer
+//=============================================================================
+// Description: Find a free positional sound player (or stop one if necessary)
+// and play the sound and store the collision pair
+//
+// Parameters: soundName - name of sound resource to play
+// objA - first colliding object
+// objB - second colliding object
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXGameplayLogic::startCollisionPlayer( const char* soundName,
+ CollisionEntityDSG* objA,
+ CollisionEntityDSG* objB,
+ rmt::Vector* positionPtr )
+{
+ int index;
+ rmt::Vector posnA;
+ rmt::Vector posnB;
+ rmt::Vector average;
+ rmt::Vector vectorToListener;
+ radSoundVector rsListenerPosition;
+ rmt::Vector listenerPosition;
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ IRadSoundHalListener* theListener;
+
+
+ //
+ // Look for a free player first
+ //
+ for( index = 0; index < s_numCollisionSounds; index++ )
+ {
+ if( !(m_collisionSounds[index].soundPlayer.IsInUse()) )
+ {
+ break;
+ }
+ }
+
+ //
+ // If all are in use, stop one. Arbitrarily, use the one
+ // at the start of the list, since it's most likely to have
+ // been playing the longest. No need to get cute.
+ //
+ if( index >= s_numCollisionSounds )
+ {
+ index = 0;
+ m_collisionSounds[0].soundPlayer.Stop();
+
+ rWarningMsg( false, "Collision sound dropped for lack of players\n" );
+ }
+
+ //
+ // Store the collision pair
+ //
+ m_collisionSounds[index].collObjA = objA;
+ m_collisionSounds[index].collObjB = objB;
+
+ //
+ // Play the sound halfway between the positions of the
+ // colliding objects (if two objects are used)
+ //
+ if( positionPtr != NULL )
+ {
+ average = *positionPtr;
+ }
+ else if( objA == NULL )
+ {
+ objB->GetPosition( &average );
+ }
+ else if( objB == NULL )
+ {
+ objA->GetPosition( &average );
+ }
+ else
+ {
+ objA->GetPosition( &posnA );
+ objB->GetPosition( &posnB );
+ average.Add( posnA, posnB );
+ average *= 0.5f;
+ }
+
+ if( GetGameplayManager()->IsSuperSprint() )
+ {
+ //
+ // Hoo boy. Big stinky hack coming up. The problem is that sounds are
+ // positional, and the camera is a very long ways away in the bonus game.
+ // Fudge the position to make the collisions suitably close.
+ //
+ theListener = ::radSoundHalListenerGet( );
+ rAssert( theListener != NULL );
+
+ theListener->GetPosition( &rsListenerPosition );
+ listenerPosition.Set( rsListenerPosition.m_x, rsListenerPosition.m_y, rsListenerPosition.m_z );
+ vectorToListener = average - listenerPosition;
+ vectorToListener.Scale( 0.01f, 0.01f, 0.01f );
+ average = listenerPosition + vectorToListener;
+ }
+
+ if( m_collisionMinMax == NULL )
+ {
+ //
+ // Lazy initialization. Grab the positional characteristics for collision sounds
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ nameSpaceObj = nameSpace->GetInstance( "collision_sounds" );
+ rAssert( nameSpaceObj != NULL );
+ m_collisionMinMax = reinterpret_cast<positionalSoundSettings*>(nameSpaceObj);
+ }
+
+ m_collisionSounds[index].soundPlayer.SetPosition( average.x, average.y, average.z );
+ m_collisionSounds[index].soundPlayer.SetParameters( m_collisionMinMax );
+ m_collisionSounds[index].soundPlayer.PlaySound( soundName );
+ m_collisionSounds[index].soundPlayer.SetTrim( 1.0f );
+}
+
+//=============================================================================
+// SoundFXGameplayLogic::handleFootstepEvent
+//=============================================================================
+// Description: Plays appropriate footstep sound
+//
+// Parameters: walkingCharacter - character doing the walking
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXGameplayLogic::handleFootstepEvent( Character* walkingCharacter )
+{
+ eTerrainType terrain;
+ bool isInterior;
+ globalSettings* clipNameObj;
+ const char* name = NULL;
+
+ rAssert( walkingCharacter != NULL );
+ walkingCharacter->GetTerrainType( terrain, isInterior );
+
+ //
+ // Get the parameter object for this positional sound.
+ //
+ clipNameObj = getGlobalSettings();
+
+ if( isInterior )
+ {
+ //
+ // Use the road clip indoors
+ //
+ name = clipNameObj->GetFootstepRoadClipName();
+ }
+ else if( terrain == TT_Metal )
+ {
+ name = clipNameObj->GetFootstepMetalClipName();
+ }
+ else if( terrain == TT_Wood )
+ {
+ name = clipNameObj->GetFootstepWoodClipName();
+ }
+ else
+ {
+ name = clipNameObj->GetFootstepRoadClipName();
+ }
+
+ rAssert( name != NULL );
+
+ playSFXSound( name, true );
+}
+
+//=============================================================================
+// SoundFXGameplayLogic::handleSwitchEvent
+//=============================================================================
+// Description: Play clicking sound for big red switches
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXGameplayLogic::handleSwitchEvent()
+{
+ playSFXSound( "switch", false );
+}
+
+//=============================================================================
+// SoundFXGameplayLogic::handleCollection
+//=============================================================================
+// Description: Play the appropriate sound for picking up a collectible
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXGameplayLogic::handleCollection()
+{
+ RenderEnums::LevelEnum level;
+ int mission;
+ const char* soundName = NULL;
+
+ //
+ // We don't have a way to identify collectible types, so select a
+ // sound resource based on our level/mission
+ //
+ level = GetGameplayManager()->GetCurrentLevelIndex();
+ mission = GetGameplayManager()->GetCurrentMissionIndex();
+ switch( level )
+ {
+ case RenderEnums::L1 :
+ soundName = "level_1_pickup_sfx";
+ break;
+ case RenderEnums::L2 :
+ if( mission == 6 )
+ {
+ soundName = "monkey_collect";
+ }
+ else
+ {
+ soundName = "level_2_pickup_sfx";
+ }
+ break;
+ case RenderEnums::L3 :
+ soundName = "level_3_pickup_sfx";
+ break;
+ case RenderEnums::L4 :
+ soundName = "level_4_pickup_sfx";
+ break;
+ case RenderEnums::L5 :
+ soundName = "level_5_pickup_sfx";
+ break;
+ case RenderEnums::L6 :
+ soundName = "level_6_pickup_sfx";
+ break;
+ case RenderEnums::L7 :
+ soundName = "nuclear_waste_collect";
+ break;
+ default:
+ break;
+ }
+
+ rAssertMsg( ( soundName != NULL ), "Collection without sound effect, tell Esan\n" );
+
+ playSFXSound( soundName, true );
+}
+
+//=============================================================================
+// SoundFXGameplayLogic::playPositionalSound
+//=============================================================================
+// Description: Plays a positional sound in the world when a positional
+// (a.k.a. script) trigger is hit
+//
+// Parameters: locator - locator for the sound
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXGameplayLogic::playPositionalSound( ScriptLocator* locator )
+{
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ positionalSoundSettings* parameters;
+ float diceRoll;
+ float probability;
+ int i;
+ rmt::Vector locatorPosition;
+
+ rAssert( locator != NULL );
+
+ //
+ // Get the parameter object for this positional sound.
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ nameSpaceObj = nameSpace->GetInstance( locator->GetKey() );
+ if( nameSpaceObj != NULL )
+ {
+ parameters = reinterpret_cast<positionalSoundSettings*>( nameSpaceObj );
+
+ if( !(locator->GetPlayerEntered()) )
+ {
+ //
+ // Player is exiting volume, not entering. If we're playing a sound
+ // already, stop it
+ //
+ for( i = 0; i < s_numPositionalSounds; i++ )
+ {
+ if( m_positionalSounds[i].IsInUse()
+ && ( m_positionalSounds[i].GetParameters() == parameters ) )
+ {
+ m_positionalSounds[i].Stop();
+ break;
+ }
+ }
+ }
+ else
+ {
+ probability = parameters->GetPlaybackProbability();
+ if( probability < 1.0f )
+ {
+ //
+ // Random play
+ //
+ diceRoll = (static_cast<float>( rand() % 100 )) / 100.0f;
+ if( diceRoll >= probability )
+ {
+ return;
+ }
+ }
+
+ //
+ // Find a player and play
+ //
+ for( i = 0; i < s_numPositionalSounds; i++ )
+ {
+ if( !(m_positionalSounds[i].IsInUse()) )
+ {
+ locator->GetLocation( &locatorPosition );
+ m_positionalSounds[i].SetPosition( locatorPosition.x,
+ locatorPosition.y,
+ locatorPosition.z );
+ m_positionalSounds[i].SetParameters( parameters );
+
+ //
+ // Don't buffer, to save IOP
+ //
+
+ m_positionalSounds[i].PlaySound( parameters->GetClipName(), NULL );
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ rDebugString( "Couldn't play missing positional sound" );
+ }
+}
+
+void SoundFXGameplayLogic::playCarDoorSound( EventEnum eventType, Character* playerCharacter )
+{
+ Vehicle* car;
+ carSoundParameters* parameters;
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ const char* clipName;
+
+ //
+ // Get name of clip
+ //
+ car = playerCharacter->GetTargetVehicle();
+ if( car != NULL )
+ {
+ //
+ // Get the car sound parameter object for this vehicle.
+ //
+ // IMPORTANT: We assume that the object in the namespace has the same
+ // name as the one in the vehicle object, which comes from the loading
+ // script for that car, I think.
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+
+ nameSpaceObj = nameSpace->GetInstance( car->GetName() );
+ if( nameSpaceObj != NULL )
+ {
+ parameters = reinterpret_cast<carSoundParameters*>( nameSpaceObj );
+
+ if( eventType == EVENT_GETINTOVEHICLE_START )
+ {
+ clipName = parameters->GetCarDoorOpenClipName();
+ }
+ else
+ {
+ clipName = parameters->GetCarDoorCloseClipName();
+ }
+
+ if( clipName != NULL )
+ {
+ playSFXSound( clipName, false );
+ }
+ }
+ else
+ {
+ rDebugString( "Couldn't find car door sound\n" );
+ }
+ }
+}
+
+//=============================================================================
+// SoundFXGameplayLogic::playCoinCollectSound
+//=============================================================================
+// Description: Play next coin collect sound in sequence
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXGameplayLogic::playCoinCollectSound()
+{
+ float pitch;
+ unsigned int numPitches;
+ globalSettings* pitchSequence = getGlobalSettings();
+
+ pitch = pitchSequence->GetCoinPitch( m_coinCounter );
+ playSFXSound( "coin_collect_01", true, false, NULL, 1.0f, pitch );
+
+ numPitches = pitchSequence->GetNumCoinPitches();
+ if( numPitches > 0 )
+ {
+ m_coinCounter = ( m_coinCounter + 1 ) % numPitches;
+ }
+}
diff --git a/game/code/sound/soundfx/soundfxgameplaylogic.h b/game/code/sound/soundfx/soundfxgameplaylogic.h
new file mode 100644
index 0000000..cb1e5f9
--- /dev/null
+++ b/game/code/sound/soundfx/soundfxgameplaylogic.h
@@ -0,0 +1,130 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundfxgameplaylogic.h
+//
+// Description: Declaration for the SoundFXGameplayLogic class, which handles
+// the translation of events into sound effects for the game
+//
+// History: 31/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDFXGAMEPLAYLOGIC_H
+#define SOUNDFXGAMEPLAYLOGIC_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+
+#include <sound/soundfx/soundfxlogic.h>
+#include <sound/positionalsoundplayer.h>
+#include <events/eventenum.h>
+
+//========================================
+// Forward References
+//========================================
+
+class SoundCollisionData;
+class Character;
+class ScriptLocator;
+class CollisionEntityDSG;
+class globalSettings;
+
+//=============================================================================
+//
+// Synopsis: PositionalSFXPlayer
+//
+// Associated stuff required to play a positional collision sound
+//
+//=============================================================================
+struct PositionalSFXPlayer
+{
+ PositionalSoundPlayer soundPlayer;
+ void* collObjA;
+ void* collObjB;
+};
+
+//=============================================================================
+//
+// Synopsis: SoundFXGameplayLogic
+//
+//=============================================================================
+
+class SoundFXGameplayLogic : public SoundFXLogic
+{
+ public:
+ SoundFXGameplayLogic();
+ virtual ~SoundFXGameplayLogic();
+
+ void RegisterEventListeners();
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ //
+ // Override callback to trigger tutorial events
+ //
+ void OnPlaybackComplete();
+
+ //
+ // Virtual function to clean up positional sounds
+ //
+ void Cleanup();
+
+ private:
+ //Prevent wasteful constructor creation.
+ SoundFXGameplayLogic( const SoundFXGameplayLogic& original );
+ SoundFXGameplayLogic& operator=( const SoundFXGameplayLogic& rhs );
+
+ globalSettings* getGlobalSettings();
+
+ void handleCollisionEvent( SoundCollisionData* collisionData );
+ void handleFootstepEvent( Character* walkingCharacter );
+ void handleSwitchEvent();
+ void handleCollection();
+ void handleObjectKick( CollisionEntityDSG* collObject );
+ void playPositionalSound( ScriptLocator* locator );
+ void playCarDoorSound( EventEnum eventType, Character* playerCharacter );
+ void playCoinCollectSound();
+ void startCollisionPlayer( const char* soundName,
+ CollisionEntityDSG* objA,
+ CollisionEntityDSG* objB,
+ rmt::Vector* positionPtr );
+
+ bool collisionPairMatches( int index, void* firstObj, void* secondObj );
+
+ // Number of simultaneous positional sounds
+ static const int s_numPositionalSounds = 3;
+
+ //
+ // Positional sound objects
+ //
+ PositionalSoundPlayer m_positionalSounds[s_numPositionalSounds];
+
+ // Number of simultaneous collision sounds
+ static const int s_numCollisionSounds = 6;
+
+ //
+ // Positional collision sounds
+ //
+ PositionalSFXPlayer m_collisionSounds[s_numCollisionSounds];
+
+ positionalSoundSettings* m_collisionMinMax;
+
+ //
+ // Counter for cycling through ka-ching sounds
+ //
+ unsigned int m_coinCounter;
+
+ //
+ // Timing for suspension ronks
+ //
+ unsigned int m_lastRonkTime;
+
+ globalSettings* m_globalSettings;
+};
+
+
+#endif // SOUNDFXGAMEPLAYLOGIC_H
+
diff --git a/game/code/sound/soundfx/soundfxlogic.cpp b/game/code/sound/soundfx/soundfxlogic.cpp
new file mode 100644
index 0000000..798d061
--- /dev/null
+++ b/game/code/sound/soundfx/soundfxlogic.cpp
@@ -0,0 +1,322 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundfxlogic.cpp
+//
+// Description: Implement the SoundFXLogic class, which is an abstract
+// base class for objects that translate events into sound effects
+// in the different game states
+//
+// History: 31/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundfx/soundfxlogic.h>
+
+#include <events/eventmanager.h>
+#include <events/eventdata.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+struct CreditInfo
+{
+ int lineNumber;
+ radKey32 dialogName;
+};
+
+#ifdef PAL
+ const int PAL_OFFSET = +16; // due to additional VUG localization team
+#else
+ const int PAL_OFFSET = 0;
+#endif
+
+static CreditInfo s_creditDialogTable[] =
+{
+ { 7, ::radMakeKey32( "pubcredits" ) },
+ { 55, ::radMakeKey32( "foxcredits" ) },
+ { 178 + PAL_OFFSET, ::radMakeKey32( "radproducer" ) },
+ { 190 + PAL_OFFSET, ::radMakeKey32( "radlead" ) },
+ { 204 + PAL_OFFSET, ::radMakeKey32( "raddesign" ) },
+ { 221 + PAL_OFFSET, ::radMakeKey32( "radworld" ) },
+ { 230 + PAL_OFFSET, ::radMakeKey32( "radmodel" ) },
+ { 235 + PAL_OFFSET, ::radMakeKey32( "radfx" ) },
+ { 242 + PAL_OFFSET, ::radMakeKey32( "radfmvart" ) },
+ { 251 + PAL_OFFSET, ::radMakeKey32( "radfeart" ) },
+ { 260 + PAL_OFFSET, ::radMakeKey32( "radprog" ) },
+ { 279 + PAL_OFFSET, ::radMakeKey32( "radtest" ) },
+ { 289 + PAL_OFFSET, ::radMakeKey32( "radsound" ) }
+};
+
+static int s_creditDialogTableSize = sizeof( s_creditDialogTable ) / sizeof( CreditInfo );
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundFXLogic::SoundFXLogic
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundFXLogic::SoundFXLogic()
+{
+ unsigned int i;
+
+ for( i = 0; i < s_numSFXPlayers; i++ )
+ {
+ m_soundPlayers[i].isKillable = true;
+ }
+}
+
+//==============================================================================
+// SoundFXLogic::~SoundFXLogic
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundFXLogic::~SoundFXLogic()
+{
+}
+
+//=============================================================================
+// SoundFXLogic::UnregisterEventListeners
+//=============================================================================
+// Description: Unregister all events with the Event Manager
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXLogic::UnregisterEventListeners()
+{
+ GetEventManager()->RemoveAll( this );
+}
+
+//=============================================================================
+// SoundFXLogic::GetAvailableSFXPlayer
+//=============================================================================
+// Description: Find an unused SFXPlayer. Failing that, find a killable
+// SFXPlayer. Failing that, well, just fail.
+//
+// Parameters: index - Address of unsigned int, filled in with index of
+// SFXPlayer if one is available and address is non-NULL,
+// untouched otherwise
+//
+// Return: SFXPlayer pointer if one available, NULL otherwise
+//
+//=============================================================================
+SFXPlayer* SoundFXLogic::GetAvailableSFXPlayer( unsigned int* index )
+{
+ unsigned int i;
+ int lastKillable = -1;
+
+ //
+ // First, look for free players
+ //
+ for( i = 0; i < s_numSFXPlayers; i++ )
+ {
+ if( !m_soundPlayers[i].soundPlayer.IsInUse() )
+ {
+ if( index != NULL )
+ {
+ *index = i;
+ }
+ return( &(m_soundPlayers[i]) );
+ }
+ else if( m_soundPlayers[i].isKillable )
+ {
+ lastKillable = i;
+ }
+ }
+
+ //
+ // If we get this far, all players are in use. Kill a player if we can
+ //
+ if( lastKillable != -1 )
+ {
+ if( index != NULL )
+ {
+ *index = lastKillable;
+ }
+ return( &(m_soundPlayers[lastKillable]) );
+ }
+ else
+ {
+ return( NULL );
+ }
+}
+
+//=============================================================================
+// SoundFXLogic::playSFXSound
+//=============================================================================
+// Description: Searches through the list of SFXPlayers for one that's free.
+// If one isn't found, find one that's killable. If none of them
+// are killable, eh, no sound.
+//
+// Parameters: resource - name of sound resource to play
+// killable - true if sound can be killed prematurely, false otherwise
+// useCallback - true if we set callback on playback completion,
+// false otherwise
+// index - to be filled in with index of SFXPlayer used if sound
+// played and index is non-NULL
+// trim - volume to play sound at
+// pitch - pitch to play sound at
+//
+// Return: true if sound played, false otherwise
+//
+//=============================================================================
+bool SoundFXLogic::playSFXSound( const char* resource, bool killable,
+ bool useCallback, unsigned int* index,
+ float trim, float pitch )
+{
+ SFXPlayer* player;
+ bool success = false;
+ SimpsonsSoundPlayerCallback* callbackObj = NULL;
+
+ //
+ // Get a player if possible
+ //
+ player = GetAvailableSFXPlayer( index );
+
+ if( player != NULL )
+ {
+ if( player->soundPlayer.IsInUse() )
+ {
+ player->soundPlayer.Stop();
+ }
+
+ if( useCallback )
+ {
+ callbackObj = this;
+ }
+ player->soundPlayer.PlaySound( resource, callbackObj );
+ player->soundPlayer.SetTrim( trim );
+ player->soundPlayer.SetPitch( pitch );
+ player->isKillable = killable;
+
+ success = true;
+ }
+ else
+ {
+ rDebugString( "Dropped sound effect, no player available\n" );
+ }
+
+ return( success );
+}
+
+//=============================================================================
+// SoundFXLogic::ServiceOncePerFrame
+//=============================================================================
+// Description: Does nothing. Subclasses with servicing requirements need
+// to override this function
+//
+// Parameters: elapsedTime - time elapsed since last frame
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXLogic::ServiceOncePerFrame( unsigned int elapsedTime )
+{
+}
+
+//=============================================================================
+// SoundFXLogic::OnSoundReady
+//=============================================================================
+// Description: Does nothing. Needed to pull this in to get OnPlaybackComplete
+// from SimpsonsSoundPlayer.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXLogic::OnSoundReady()
+{
+}
+
+//=============================================================================
+// SoundFXLogic::OnPlaybackComplete
+//=============================================================================
+// Description: Does nothing. Subclasses with callback requirements need
+// to override this virtual function.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXLogic::OnPlaybackComplete()
+{
+}
+
+//=============================================================================
+// SoundFXLogic::Cleanup
+//=============================================================================
+// Description: Does nothing. Subclasses with stuff to clean up override
+// this virtual function.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXLogic::Cleanup()
+{
+}
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// SoundFXLogic::playCreditLine
+//=============================================================================
+// Description: Play a credits conversation
+//
+// Parameters: lineNumber - last scrolled line number in credits text
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXLogic::playCreditLine( int lineNumber )
+{
+ int i;
+ DialogEventData data;
+
+ for( i = 0; i < s_creditDialogTableSize; i++ )
+ {
+ if( lineNumber == s_creditDialogTable[i].lineNumber )
+ {
+ data.charUID1 = tEntity::MakeUID( "kang" );
+ data.charUID2 = tEntity::MakeUID( "kodos" );
+ data.dialogName = s_creditDialogTable[i].dialogName;
+
+ GetEventManager()->TriggerEvent( EVENT_IN_GAMEPLAY_CONVERSATION, static_cast<void*>(&data) );
+ }
+ }
+}
diff --git a/game/code/sound/soundfx/soundfxlogic.h b/game/code/sound/soundfx/soundfxlogic.h
new file mode 100644
index 0000000..f5735f6
--- /dev/null
+++ b/game/code/sound/soundfx/soundfxlogic.h
@@ -0,0 +1,91 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundfxlogic.h
+//
+// Description: Declaration for the SoundFXLogic class, which is an abstract
+// base class for objects that translate events into sound effects
+// in the different game states
+//
+// History: 31/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDFXLOGIC_H
+#define SOUNDFXLOGIC_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <events/eventlistener.h>
+#include <sound/simpsonssoundplayer.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: SFXPlayer
+//
+// Structure that maps the sound player to a flag indicating whether
+// we can kill the sound effect for something higher priority
+//
+//=============================================================================
+struct SFXPlayer
+{
+ SimpsonsSoundPlayer soundPlayer;
+ bool isKillable;
+};
+
+//=============================================================================
+//
+// Synopsis: SoundFXLogic
+//
+//=============================================================================
+
+class SoundFXLogic : public EventListener,
+ public SimpsonsSoundPlayerCallback
+{
+ public:
+ SoundFXLogic();
+ virtual ~SoundFXLogic();
+
+ virtual void RegisterEventListeners() = 0;
+ void UnregisterEventListeners();
+
+ SFXPlayer* GetAvailableSFXPlayer( unsigned int* index = NULL );
+
+ virtual void ServiceOncePerFrame( unsigned int elapsedTime );
+
+ virtual void Cleanup();
+
+ //
+ // SimpsonsSoundPlayerCallback functions
+ //
+ void OnSoundReady();
+ virtual void OnPlaybackComplete();
+
+ protected:
+ // Number of SFXPlayers
+ static const unsigned int s_numSFXPlayers = 6;
+
+ //
+ // Sound players
+ //
+ SFXPlayer m_soundPlayers[s_numSFXPlayers];
+
+ bool playSFXSound( const char* resource, bool killable, bool useCallback = false,
+ unsigned int* index = NULL, float trim = 1.0f, float pitch = 1.0f );
+
+ void playCreditLine( int lineNumber );
+
+ private:
+ //Prevent wasteful constructor creation.
+ SoundFXLogic( const SoundFXLogic& original );
+ SoundFXLogic& operator=( const SoundFXLogic& rhs );
+};
+
+
+#endif // SOUNDFXLOGIC_H
+
diff --git a/game/code/sound/soundfx/soundfxpauselogic.cpp b/game/code/sound/soundfx/soundfxpauselogic.cpp
new file mode 100644
index 0000000..961996a
--- /dev/null
+++ b/game/code/sound/soundfx/soundfxpauselogic.cpp
@@ -0,0 +1,98 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundfxpauselogic.cpp
+//
+// Description: Implements the SoundFXPauseLogic class, which handles
+// the translation of events into sound effects for the pause
+// menu.
+//
+// History: 31/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundfx/soundfxpauselogic.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundFXPauseLogic::SoundFXPauseLogic
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundFXPauseLogic::SoundFXPauseLogic()
+{
+}
+
+//==============================================================================
+// SoundFXPauseLogic::~SoundFXPauseLogic
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundFXPauseLogic::~SoundFXPauseLogic()
+{
+}
+
+//=============================================================================
+// SoundFXPauseLogic::RegisterEventListeners
+//=============================================================================
+// Description: Register as listener of sound effect events with Event Manager
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXPauseLogic::RegisterEventListeners()
+{
+}
+
+//=============================================================================
+// SoundFXPauseLogic::HandleEvent
+//=============================================================================
+// Description: Play sound effects in response to events
+//
+// Parameters: id - Sound effect event identifier
+// pEventData - Currently unused
+//
+// Return: void
+//
+//=============================================================================
+void SoundFXPauseLogic::HandleEvent( EventEnum id, void* pEventData )
+{
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/sound/soundfx/soundfxpauselogic.h b/game/code/sound/soundfx/soundfxpauselogic.h
new file mode 100644
index 0000000..5cc861f
--- /dev/null
+++ b/game/code/sound/soundfx/soundfxpauselogic.h
@@ -0,0 +1,50 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundfxpauselogic.h
+//
+// Description: Declaration for the SoundFXPauseLogic class, which handles
+// the translation of events into sound effects for the pause
+// menu.
+//
+// History: 31/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDFXPAUSELOGIC_H
+#define SOUNDFXPAUSELOGIC_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/soundfx/soundfxlogic.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: SoundFXPauseLogic
+//
+//=============================================================================
+
+class SoundFXPauseLogic : public SoundFXLogic
+{
+ public:
+ SoundFXPauseLogic();
+ virtual ~SoundFXPauseLogic();
+
+ void RegisterEventListeners();
+
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ private:
+ //Prevent wasteful constructor creation.
+ SoundFXPauseLogic( const SoundFXPauseLogic& original );
+ SoundFXPauseLogic& operator=( const SoundFXPauseLogic& rhs );
+};
+
+
+#endif // SOUNDFXPAUSELOGIC_H
+
diff --git a/game/code/sound/soundfx/win32reverbcontroller.cpp b/game/code/sound/soundfx/win32reverbcontroller.cpp
new file mode 100644
index 0000000..8876d0d
--- /dev/null
+++ b/game/code/sound/soundfx/win32reverbcontroller.cpp
@@ -0,0 +1,127 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: win32reverbcontroller.cpp
+//
+// Description: Implementation for the Win32ReverbController class, which provides
+// the Windows-specific reverb control
+//
+// History: 03/25/2003 + Created -- Ziemek
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radsound_win32.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundfx/win32reverbcontroller.h>
+
+#include <sound/soundfx/reverbsettings.h>
+
+#include <memory/srrmemory.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Win32ReverbController::Win32ReverbController
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Win32ReverbController::Win32ReverbController()
+{
+ m_reverbInterface = ::radSoundHalEffectEAX2ReverbCreate( GMA_PERSISTENT );
+ m_reverbInterface->AddRef();
+
+ registerReverbEffect( m_reverbInterface );
+}
+
+//==============================================================================
+// Win32ReverbController::~Win32ReverbController
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Win32ReverbController::~Win32ReverbController()
+{
+ m_reverbInterface->Release();
+ m_reverbInterface = NULL;
+}
+
+//=============================================================================
+// Win32ReverbController::SetReverbOn
+//=============================================================================
+// Description: Self-explanatory
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void Win32ReverbController::SetReverbOn( reverbSettings* settings )
+{
+ if( settings != NULL )
+ {
+ rReleaseString( "Settings not null\n" );
+ SetReverbGain( settings->GetGain() );
+ m_reverbInterface->SetRoom( settings->GetXboxRoom() );
+ m_reverbInterface->SetRoomHF( settings->GetXboxRoomHF() );
+ m_reverbInterface->SetRoomRolloffFactor( settings->GetXboxRoomRolloffFactor() );
+ m_reverbInterface->SetDecayTime( settings->GetXboxDecayTime() );
+ m_reverbInterface->SetDecayHFRatio( settings->GetXboxDecayHFRatio() );
+ m_reverbInterface->SetReflections( settings->GetXboxReflections() );
+ m_reverbInterface->SetReflectionsDelay( settings->GetXboxReflectionsDelay() );
+ m_reverbInterface->SetReverb( settings->GetXboxReverb() );
+ m_reverbInterface->SetReverbDelay( settings->GetXboxReverbDelay() );
+ m_reverbInterface->SetEnvironmentDiffusion( settings->GetWinEnvironmentDiffusion() );
+ m_reverbInterface->SetAirAbsorptionHF( settings->GetWinAirAbsorptionHF() );
+
+ prepareFadeSettings( settings->GetGain(), settings->GetFadeInTime(),
+ settings->GetFadeOutTime() );
+ }
+}
+
+//=============================================================================
+// Win32ReverbController::SetReverbOff
+//=============================================================================
+// Description: Self-explanatory
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void Win32ReverbController::SetReverbOff()
+{
+ startFadeOut();
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/sound/soundfx/win32reverbcontroller.h b/game/code/sound/soundfx/win32reverbcontroller.h
new file mode 100644
index 0000000..055ae84
--- /dev/null
+++ b/game/code/sound/soundfx/win32reverbcontroller.h
@@ -0,0 +1,54 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: win32reverbcontroller.h
+//
+// Description: Declaration for the Win32ReverbController class, which provides
+// the Windows-specific reverb control
+//
+// History: 03/25/2003 + Created -- Ziemek
+//
+//=============================================================================
+
+#ifndef WIN32REVERBCONTROLLER_H
+#define WIN32REVERBCONTROLLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/soundfx/reverbcontroller.h>
+
+//========================================
+// Forward References
+//========================================
+struct IRadSoundHalEffectEAX2Reverb;
+
+//=============================================================================
+//
+// Synopsis: Win32ReverbController
+//
+//=============================================================================
+
+class Win32ReverbController : public ReverbController
+{
+public:
+ Win32ReverbController();
+ virtual ~Win32ReverbController();
+
+ void SetReverbOn( reverbSettings* settings );
+ void SetReverbOff();
+
+private:
+ //Prevent wasteful constructor creation.
+ Win32ReverbController( const Win32ReverbController& original );
+ Win32ReverbController& operator=( const Win32ReverbController& rhs );
+
+ //
+ // Radsound's Win32 reverb interface
+ //
+ IRadSoundHalEffectEAX2Reverb* m_reverbInterface;
+};
+
+
+#endif // WIN32REVERBCONTROLLER_H
+
diff --git a/game/code/sound/soundfx/xboxreverbcontroller.cpp b/game/code/sound/soundfx/xboxreverbcontroller.cpp
new file mode 100644
index 0000000..599aeee
--- /dev/null
+++ b/game/code/sound/soundfx/xboxreverbcontroller.cpp
@@ -0,0 +1,127 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: xboxreverbcontroller.cpp
+//
+// Description: Implementation for the XboxReverbController class, which provides
+// the Xbox-specific reverb control
+//
+// History: 10/28/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radsound_xbox.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundfx/xboxreverbcontroller.h>
+
+#include <sound/soundfx/reverbsettings.h>
+
+#include <memory/srrmemory.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// XboxReverbController::XboxReverbController
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+XboxReverbController::XboxReverbController()
+{
+ m_reverbInterface = ::radSoundHalEffectI3DL2ReverbXBoxCreate( GMA_PERSISTENT );
+
+ registerReverbEffect( m_reverbInterface );
+}
+
+//==============================================================================
+// XboxReverbController::~XboxReverbController
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+XboxReverbController::~XboxReverbController()
+{
+ m_reverbInterface->Release();
+ m_reverbInterface = NULL;
+}
+
+//=============================================================================
+// XboxReverbController::SetReverbOn
+//=============================================================================
+// Description: Self-explanatory
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void XboxReverbController::SetReverbOn( reverbSettings* settings )
+{
+ if( settings != NULL )
+ {
+ rReleaseString( "Settings not null\n" );
+ SetReverbGain( settings->GetGain() );
+ m_reverbInterface->SetRoom( settings->GetXboxRoom() );
+ m_reverbInterface->SetRoomHF( settings->GetXboxRoomHF() );
+ m_reverbInterface->SetRoomRolloffFactor( settings->GetXboxRoomRolloffFactor() );
+ m_reverbInterface->SetDecayTime( settings->GetXboxDecayTime() );
+ m_reverbInterface->SetDecayHFRatio( settings->GetXboxDecayHFRatio() );
+ m_reverbInterface->SetReflections( settings->GetXboxReflections() );
+ m_reverbInterface->SetReflectionsDelay( settings->GetXboxReflectionsDelay() );
+ m_reverbInterface->SetReverb( settings->GetXboxReverb() );
+ m_reverbInterface->SetReverbDelay( settings->GetXboxReverbDelay() );
+ m_reverbInterface->SetDiffusion( settings->GetXboxDiffusion() );
+ m_reverbInterface->SetDensity( settings->GetXboxDensity() );
+ m_reverbInterface->SetHFReference( settings->GetXboxHFReference() );
+
+ prepareFadeSettings( settings->GetGain(), settings->GetFadeInTime(),
+ settings->GetFadeOutTime() );
+ }
+}
+
+//=============================================================================
+// XboxReverbController::SetReverbOff
+//=============================================================================
+// Description: Self-explanatory
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void XboxReverbController::SetReverbOff()
+{
+ startFadeOut();
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/sound/soundfx/xboxreverbcontroller.h b/game/code/sound/soundfx/xboxreverbcontroller.h
new file mode 100644
index 0000000..5ef87f3
--- /dev/null
+++ b/game/code/sound/soundfx/xboxreverbcontroller.h
@@ -0,0 +1,54 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: xboxreverbcontroller.h
+//
+// Description: Declaration for the XboxReverbController class, which provides
+// the Xbox-specific reverb control
+//
+// History: 10/28/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef XBOXREVERBCONTROLLER_H
+#define XBOXREVERBCONTROLLER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/soundfx/reverbcontroller.h>
+
+//========================================
+// Forward References
+//========================================
+struct IRadSoundHalEffectI3DL2ReverbXBox;
+
+//=============================================================================
+//
+// Synopsis: XboxReverbController
+//
+//=============================================================================
+
+class XboxReverbController : public ReverbController
+{
+ public:
+ XboxReverbController();
+ virtual ~XboxReverbController();
+
+ void SetReverbOn( reverbSettings* settings );
+ void SetReverbOff();
+
+ private:
+ //Prevent wasteful constructor creation.
+ XboxReverbController( const XboxReverbController& original );
+ XboxReverbController& operator=( const XboxReverbController& rhs );
+
+ //
+ // Radsound's Xbox reverb interface
+ //
+ IRadSoundHalEffectI3DL2ReverbXBox* m_reverbInterface;
+};
+
+
+#endif // XBOXREVERBCONTROLLER_H
+
diff --git a/game/code/sound/soundloader.cpp b/game/code/sound/soundloader.cpp
new file mode 100644
index 0000000..f0a2c64
--- /dev/null
+++ b/game/code/sound/soundloader.cpp
@@ -0,0 +1,601 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundloader.cpp
+//
+// Description: Implement SoundLoader class, which makes sure that sounds
+// required in the game are allocated and resident in sound memory
+//
+// History: 26/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <stdio.h>
+
+#include <sound/soundloader.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/soundresourcemanager.h>
+
+#include <memory/srrmemory.h>
+#include <loading/loadingmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <constants/vehicleenum.h>
+#include <mission/gameplaymanager.h>
+#include <events/eventmanager.h>
+#include <worldsim/character/character.h>
+
+#include <radscript.hpp>
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Names of sound clusters. Used so that the loading manager can tell us
+// when it's our turn to loading something. Should correspond to SoundClusterName
+// enumeration
+//
+static const char* s_clusterNames[] =
+{
+ "permanent",
+ "frontend",
+ "ingame",
+ "suburbs",
+ "downtown",
+ "seaside",
+ "level1",
+ "level2",
+ "level3",
+ "level4",
+ "level5",
+ "level6",
+ "level7",
+ "minigame",
+ "huh?",
+ "apu",
+ "bart",
+ "homer",
+ "lisa",
+ "marge",
+ "bart_v",
+ "apu_v",
+ "snake_v",
+ "homer_v",
+ "famil_v",
+ "gramp_v",
+ "cletu_v",
+ "wiggu_v",
+ "empty1",
+ "marge_v",
+ "empty2",
+ "empty3",
+ "smith_v",
+ "empty4",
+ "empty5",
+ "empty6",
+ "zombi_v",
+ "empty7",
+ "empty8",
+ "cVan",
+ "compactA",
+ "comic_v",
+ "skinn_v",
+ "cCola",
+ "cSedan",
+ "cPolice",
+ "cCellA",
+ "cCellB",
+ "cCellC",
+ "cCellD",
+ "minivanA_v",
+ "pickupA",
+ "taxiA_v",
+ "sportsA",
+ "sportsB",
+ "SUVA",
+ "wagonA",
+ "hbike_v",
+ "burns_v",
+ "honor_v",
+ "cArmor",
+ "cCurator",
+ "cHears",
+ "cKlimo",
+ "cLimo",
+ "cNerd",
+ "frink_v",
+ "cMilk",
+ "cDonut",
+ "bbman_v",
+ "bookb_v",
+ "carhom_v",
+ "elect_v",
+ "fone_v",
+ "gramR_v",
+ "moe_v",
+ "mrplo_v",
+ "otto_v",
+ "plowk_v",
+ "scorp_v",
+ "willi_v",
+ "sedanA",
+ "sedanB",
+ "cBlbart",
+ "cCube",
+ "cDuff",
+ "cNonup",
+ "lisa_v",
+ "krust_v",
+ "coffin",
+ "hallo",
+ "ship",
+ "witchcar",
+ "huska",
+ "atv_v",
+ "dune_v",
+ "hype_v",
+ "knigh_v",
+ "mono_v",
+ "oblit_v",
+ "rocke_v",
+ "ambul",
+ "burnsarm",
+ "fishtruc",
+ "garbage",
+ "icecream",
+ "istruck",
+ "nuctruck",
+ "pizza",
+ "schoolbu",
+ "votetruc",
+ "glastruc",
+ "cfire_v",
+ "cBone",
+ "redbrick"
+};
+
+static const int NumScriptNames = sizeof( s_clusterNames ) / sizeof( const char* );
+
+//
+// Indices of character namespaces for each level.
+//
+// TODO: I don't like these tables, there must be a more data-driven way to do
+// this.
+// 0 == Apu
+// 1 == Bart
+// 2 == Homer
+// 3 == Lisa
+// 4 == Marge
+//
+static unsigned int s_charNamespaceIndices[] = { 2, 1, 3, 4, 0, 1, 2, 0 };
+
+static VehicleEnum::VehicleID s_carIndices[] = { VehicleEnum::FAMIL_V,
+ VehicleEnum::HONOR_V,
+ VehicleEnum::LISA_V,
+ VehicleEnum::MARGE_V,
+ VehicleEnum::APU_V,
+ VehicleEnum::BART_V,
+ VehicleEnum::HOMER_V,
+ VehicleEnum::FAMIL_V,
+};
+
+static unsigned int s_levelIndices[] = { 0, 1, 2, 0, 1, 2, 0, 0 };
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundLoader::SoundLoader
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundLoader::SoundLoader() :
+ m_currentCluster( SC_ALWAYS_LOADED )
+{
+ unsigned int i;
+ SoundClusterName clusterIndex;
+ Sound::daSoundRenderingManager* renderingMgr = Sound::daSoundRenderingManagerGet();
+
+ for( i = 0; i < SC_MAX_CLUSTERS; i++ )
+ {
+ m_clusterList[i] = NULL;
+ }
+
+ for( clusterIndex = SC_ALWAYS_LOADED;
+ clusterIndex < SC_CHAR_APU;
+ clusterIndex = static_cast<SoundClusterName>( clusterIndex + 1 ) )
+ {
+ m_clusterList[clusterIndex] =
+ new(GMA_PERSISTENT) SoundCluster( clusterIndex,
+ renderingMgr->GetSoundNamespace() );
+ }
+
+ for( clusterIndex = SC_CHAR_APU;
+ clusterIndex < SC_CAR_BASE;
+ clusterIndex = static_cast<SoundClusterName>( clusterIndex + 1 ) )
+ {
+ m_clusterList[clusterIndex] =
+ new(GMA_PERSISTENT) SoundCluster( clusterIndex,
+ renderingMgr->GetCharacterNamespace( clusterIndex - SC_CHAR_APU ) );
+ }
+
+ for( clusterIndex = SC_CAR_BASE;
+ clusterIndex < SC_MAX_CLUSTERS;
+ clusterIndex = static_cast<SoundClusterName>( clusterIndex + 1 ) )
+ {
+ m_clusterList[clusterIndex] =
+ new(GMA_PERSISTENT) SoundCluster( clusterIndex,
+ renderingMgr->GetSoundNamespace() );
+ }
+
+ //
+ // Register event listeners
+ //
+ GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_START );
+}
+
+//==============================================================================
+// SoundLoader::~SoundLoader
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundLoader::~SoundLoader()
+{
+ unsigned int i;
+
+ for( i = 0; i < SC_MAX_CLUSTERS; i++ )
+ {
+ if( m_clusterList[i] != NULL )
+ {
+ m_clusterList[i]->Release();
+ }
+ }
+
+ GetEventManager()->RemoveAll( this );
+}
+
+//=============================================================================
+// SoundLoader::LevelLoad
+//=============================================================================
+// Description: Loads the sound cluster for a particular level
+//
+// Parameters: RenderEnums::LevelEnum level -
+// enumeration indicating the level whose sound cluster
+// is to be loaded
+//
+// Return: void
+//
+//=============================================================================
+void SoundLoader::LevelLoad( RenderEnums::LevelEnum level )
+{
+ IRadNameSpace* charNamespace;
+ unsigned int levelNum;
+
+ clusterUnload( SC_FRONTEND );
+
+ queueLoad( SC_INGAME );
+
+ if( GetGameplayManager()->IsSuperSprint() )
+ {
+ queueLoad( SC_MINIGAME );
+ }
+ else
+ {
+ rAssert( level <= RenderEnums::numLevels );
+ levelNum = static_cast<unsigned int>(level);
+
+ queueLoad( static_cast<SoundClusterName>( SC_LEVEL_SUBURBS + s_levelIndices[levelNum] ) );
+ queueLoad( static_cast<SoundClusterName>( SC_CHAR_APU + s_charNamespaceIndices[levelNum] ) );
+ queueLoad( static_cast<SoundClusterName>( SC_CAR_BASE + s_carIndices[levelNum] ) );
+ queueLoad( static_cast<SoundClusterName>( SC_LEVEL1 + levelNum ) );
+
+ charNamespace = m_clusterList[SC_CHAR_APU + s_charNamespaceIndices[level]]->GetMyNamespace();
+ rAssert( charNamespace != NULL );
+
+ //
+ // We need to do this for RadTuner, since we've got duplicate names and
+ // it won't necessarily find the correct character otherwise
+ //
+ charNamespace->MoveToFront();
+ }
+}
+
+//=============================================================================
+// SoundLoader::LevelUnload
+//=============================================================================
+// Description: Unloads the sound cluster for a particular level
+//
+// Parameters: RenderEnums::LevelEnum level -
+// enumeration indicating the level whose sound cluster
+// is to be unloaded
+//
+// Return: void
+//
+//=============================================================================
+void SoundLoader::LevelUnload( bool goingToFe )
+{
+ SoundClusterName clusterIndex;
+
+ //
+ // Unload everything that's not permanent
+ //
+ for( clusterIndex = SC_INGAME;
+ clusterIndex < SC_MAX_CLUSTERS;
+ clusterIndex = static_cast<SoundClusterName>( clusterIndex + 1 ) )
+ {
+ if( m_clusterList[clusterIndex]->IsLoaded() )
+ {
+ clusterUnload( clusterIndex );
+ }
+ }
+
+ if( goingToFe )
+ {
+ queueLoad( SC_FRONTEND );
+ }
+}
+
+//=============================================================================
+// SoundLoader::MissionLoad
+//=============================================================================
+// Description: Loads the sound cluster for a particular mission
+//
+// Parameters: RenderEnums::MissionEnum mission -
+// enumeration indicating the mission whose sound cluster
+// is to be loaded
+//
+// Return: void
+//
+//=============================================================================
+void SoundLoader::MissionLoad( RenderEnums::MissionEnum mission )
+{
+}
+
+//=============================================================================
+// SoundLoader::MissionUnload
+//=============================================================================
+// Description: Unloads the sound cluster for a particular mission
+//
+// Parameters: RenderEnums::MissionEnum mission -
+// enumeration indicating the mission whose sound cluster
+// is to be unloaded
+//
+// Return: void
+//
+//=============================================================================
+void SoundLoader::MissionUnload( RenderEnums::MissionEnum mission )
+{
+}
+
+void SoundLoader::LoadCarSound( Vehicle* theCar, bool unloadOtherCars )
+{
+ rAssert( theCar );
+
+ SoundClusterName clusterIndex;
+ SoundClusterName newCarCluster = static_cast<SoundClusterName>(SC_CAR_BASE + theCar->mVehicleID);
+ bool validCar = ( SC_CAR_BASE + theCar->mVehicleID ) < NumScriptNames;
+
+ rAssertMsg( validCar, "A new vehicle has been added that the sound system does not have a script for. Tell Esan.\n" );
+
+ //
+ // Have we loaded this car already?
+ //
+ if( !validCar || m_clusterList[newCarCluster]->IsLoaded() )
+ {
+ return;
+ }
+
+ //
+ // Unload the existing car sound
+ //
+ if( unloadOtherCars )
+ {
+ for( clusterIndex = SC_CAR_BASE;
+ clusterIndex < SC_MAX_CLUSTERS;
+ clusterIndex = static_cast<SoundClusterName>( clusterIndex + 1 ) )
+ {
+ if( m_clusterList[clusterIndex]->IsLoaded() )
+ {
+ clusterUnload( clusterIndex );
+ }
+ }
+ }
+
+ //
+ // Load the new car
+ //
+ queueLoad( newCarCluster );
+}
+
+//=============================================================================
+// SoundLoader::IsSoundLoaded
+//=============================================================================
+// Description: Indicate whether a particular sound resource has been loaded
+//
+// Parameters: soundKey - hashed name of the sound resource to look for
+//
+// Return: true if loaded, falsed otherwise
+//
+//=============================================================================
+bool SoundLoader::IsSoundLoaded( Sound::daResourceKey soundKey )
+{
+ unsigned int i;
+
+ for( i = 0; i < SC_MAX_CLUSTERS; i++ )
+ {
+ if( ( m_clusterList[i] != NULL ) && ( m_clusterList[i]->ContainsResource( soundKey ) ) )
+ {
+ return( m_clusterList[i]->IsLoaded() );
+ }
+ }
+
+ return( false );
+}
+
+//=============================================================================
+// SoundLoader::LoadClusterByName
+//=============================================================================
+// Description: Given a cluster name from the loading manager, load the
+// desired cluster
+//
+// Parameters: clusterName - text name for the cluster
+// callbackObj - loading file handler to notify on completion
+//
+// Return: true if cluster already loaded, false otherwise
+//
+//=============================================================================
+bool SoundLoader::LoadClusterByName( const char* clusterName, SoundFileHandler* callbackObj )
+{
+ const char* shortName;
+ unsigned int i;
+
+ // Strip out the "sound:" prefix
+ rAssert( strlen( clusterName ) > 6 );
+ shortName = &(clusterName[6]);
+
+ //
+ // Find the matching cluster name
+ //
+ for( i = 0; i < SC_MAX_CLUSTERS; i++ )
+ {
+ if( strcmp( shortName, s_clusterNames[i] ) == 0 )
+ {
+ return( clusterLoad( static_cast<SoundClusterName>( i ), callbackObj ) );
+ }
+ }
+
+ //
+ // If we get here, cluster not found
+ //
+ rAssert( false );
+ return( false );
+}
+
+//=============================================================================
+// SoundLoader::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void SoundLoader::HandleEvent( EventEnum id, void* pEventData )
+{
+ Character* theCharacter;
+ Vehicle* theCar;
+
+ switch( id )
+ {
+ case EVENT_GETINTOVEHICLE_START:
+ //
+ // Make sure we've got the correct car sound for this vehicle
+ //
+ theCharacter = static_cast<Character*>(pEventData);
+ rAssert( theCharacter != NULL );
+ theCar = theCharacter->GetTargetVehicle();
+ rAssert( theCar != NULL );
+
+ LoadCarSound( theCar, true );
+ break;
+
+ default:
+ rAssert( false );
+ break;
+ }
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// SoundLoader::queueLoad
+//=============================================================================
+// Description: Queue a cluster load with the loading manager
+//
+// Parameters: cluster - name of cluster to queue
+//
+// Return: void
+//
+//=============================================================================
+void SoundLoader::queueLoad( SoundClusterName cluster )
+{
+ char fakeFilename[50];
+
+ //
+ // Create a pseudo filename that we'll give to the loading manager.
+ // Content doesn't really matter, we'll just throw it out when the
+ // loading manager passes it back
+ //
+ if( cluster >= NumScriptNames )
+ {
+ //
+ // Just load Bart for now
+ //
+ cluster = SC_CAR_BASE;
+ }
+
+ sprintf( fakeFilename, "sound:%s", s_clusterNames[cluster] );
+ GetLoadingManager()->AddRequest( FILEHANDLER_SOUND, fakeFilename, GMA_LEVEL_AUDIO );
+}
+
+//=============================================================================
+// SoundLoader::clusterLoad
+//=============================================================================
+// Description: Find the specified sound cluster and direct it to load sounds
+//
+// Parameters: SoundClusterName name - specifies which cluster to load
+//
+// Return: false if cluster not yet loaded, true otherwise
+//
+//=============================================================================
+bool SoundLoader::clusterLoad( SoundClusterName name, SoundFileHandler* callbackObj )
+{
+ bool loaded;
+
+ rAssert( name < SC_MAX_CLUSTERS );
+
+ loaded = m_clusterList[name]->IsLoaded();
+ if( !loaded )
+ {
+ m_clusterList[name]->LoadSounds( callbackObj );
+ }
+
+ return( loaded );
+}
+
+void SoundLoader::clusterUnload( SoundClusterName name )
+{
+ rAssert( name < SC_MAX_CLUSTERS );
+ if( m_clusterList[name]->IsLoaded() )
+ {
+ m_clusterList[name]->UnloadSounds();
+ }
+} \ No newline at end of file
diff --git a/game/code/sound/soundloader.h b/game/code/sound/soundloader.h
new file mode 100644
index 0000000..fad45fe
--- /dev/null
+++ b/game/code/sound/soundloader.h
@@ -0,0 +1,98 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundloader.h
+//
+// Description: Declaration for the SoundLoader class, used to manage the loading
+// and unloading of sound clips and streams.
+//
+// History: 25/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDLOADER_H
+#define SOUNDLOADER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <render/Enums/RenderEnums.h>
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundclusternameenum.h>
+#include <sound/soundcluster.h>
+#include <events/eventlistener.h>
+
+//========================================
+// Forward References
+//========================================
+
+class SoundFileHandler;
+class Vehicle;
+
+//=============================================================================
+//
+// Synopsis: SoundLoader class declaration
+//
+//=============================================================================
+
+class SoundLoader : public EventListener
+{
+ public:
+ SoundLoader();
+ virtual ~SoundLoader();
+
+ bool LoadClusterByName( const char* clusterName, SoundFileHandler* callbackObj );
+
+ void LoadPermanentSounds() { queueLoad( SC_ALWAYS_LOADED ); }
+
+ void LevelLoad( RenderEnums::LevelEnum level );
+ void LevelUnload( bool goingToFe );
+
+ void MissionLoad( RenderEnums::MissionEnum mission );
+ void MissionUnload( RenderEnums::MissionEnum mission );
+
+ void LoadFrontEnd() { queueLoad( SC_FRONTEND ); }
+ void UnloadFrontEnd() { clusterUnload( SC_FRONTEND ); }
+
+ void LoadCarSound( Vehicle* theCar, bool unloadOtherCars );
+
+ bool IsSoundLoaded( Sound::daResourceKey soundKey );
+
+ void SetCurrentCluster( SoundClusterName cluster )
+ { rAssert( cluster != SC_MAX_CLUSTERS ); m_currentCluster = cluster; }
+ bool AddResourceToCurrentCluster( const char* resourceName )
+ { return( m_clusterList[m_currentCluster]->AddResource( resourceName ) ); }
+
+ //
+ // EventListener functions
+ //
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ private:
+ //Prevent wasteful constructor creation.
+ SoundLoader( const SoundLoader& original );
+ SoundLoader& operator=( const SoundLoader& rhs );
+
+ //
+ // Queue a load with the loading manager
+ //
+ void queueLoad( SoundClusterName cluster );
+
+ bool clusterLoad( SoundClusterName name, SoundFileHandler* callbackObj = NULL );
+ void clusterUnload( SoundClusterName name );
+
+ //
+ // List of clusters, each holding list of loadable sounds
+ //
+ SoundCluster* m_clusterList[SC_MAX_CLUSTERS];
+
+ //
+ // Cluster that any created sound resources will be added to
+ //
+ SoundClusterName m_currentCluster;
+};
+
+
+#endif // SOUNDLOADER_H
+
diff --git a/game/code/sound/soundmanager.cpp b/game/code/sound/soundmanager.cpp
new file mode 100644
index 0000000..5fea9cc
--- /dev/null
+++ b/game/code/sound/soundmanager.cpp
@@ -0,0 +1,2193 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundmanager.cpp
+//
+// Description: Manager interface that the other game components use to
+// interact with sound.
+//
+// History: 01/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <radfactory.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundmanager.h>
+
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/idasoundtuner.h>
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundrenderer/soundnucleus.hpp>
+
+#include <sound/music/musicplayer.h>
+#include <sound/dialog/dialogcoordinator.h>
+#include <sound/dialog/dialogline.h>
+#include <sound/tuning/globalsettings.h>
+#include <sound/soundfx/soundeffectplayer.h>
+#include <sound/soundfx/reverbsettings.h>
+#include <sound/soundfx/positionalsoundsettings.h>
+#include <sound/movingpositional/movingsoundmanager.h>
+#include <sound/sounddebug/sounddebugdisplay.h>
+
+#include <loading/loadingmanager.h>
+#include <loading/soundfilehandler.h>
+
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+#include <events/eventmanager.h>
+#include <data/gamedatamanager.h>
+#include <interiors/interiormanager.h>
+#include <gameflow/gameflow.h>
+#include <worldsim/avatarmanager.h>
+
+#include <string.h>
+
+#ifdef RAD_GAMECUBE
+#include <dolphin/os.h>
+#endif
+
+#ifdef RAD_WIN32
+#include <data/config/gameconfigmanager.h>
+#endif
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+// Static pointer to instance of singleton.
+SoundManager* SoundManager::spInstance = NULL;
+
+//
+// Sound file extensions
+//
+const char* RADSCRIPT_TYPE_INFO_FILE = "typ";
+const char* RADSCRIPT_SCRIPT_FILE = "spt";
+const char* RADMUSIC_SCRIPT_FILE = "rms";
+
+//
+// Sound mode flags, necessary because it's too late in the project
+// to turn the boolean used to save the sound mode into an enumeration.
+// Doh.
+//
+static const int SOUND_MODE_STEREO_FLAG = 1;
+static const int SOUND_MODE_SURROUND_FLAG = 1 << 1;
+
+static unsigned int gLastServiceTime;
+static unsigned int gLastServiceOncePerFrameTime;
+
+static const unsigned int BAD_SERVICE_TIME = 100;
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundManager::CreateInstance
+//==============================================================================
+//
+// Description: Creates the SoundManager.
+//
+// Parameters: None.
+//
+// Return: Pointer to the SoundManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+SoundManager* SoundManager::CreateInstance( bool muteSound, bool noMusic,
+ bool noEffects, bool noDialogue )
+{
+ MEMTRACK_PUSH_GROUP( "Sound" );
+
+ rAssert( spInstance == NULL );
+
+ spInstance = new(GMA_PERSISTENT) SoundManager( muteSound, noMusic, noEffects, noDialogue );
+ rAssert( spInstance );
+
+ spInstance->initialize();
+
+ MEMTRACK_POP_GROUP( "Sound" );
+
+ return spInstance;
+}
+
+//==============================================================================
+// SoundManager::GetInstance
+//==============================================================================
+//
+// Description: - Access point for the SoundManager singleton.
+//
+// Parameters: None.
+//
+// Return: Pointer to the SoundManager.
+//
+// Constraints: This is a singleton so only one instance is allowed.
+//
+//==============================================================================
+SoundManager* SoundManager::GetInstance()
+{
+ rAssert( spInstance != NULL );
+
+ return spInstance;
+}
+
+
+//==============================================================================
+// SoundManager::DestroyInstance
+//==============================================================================
+//
+// Description: Destroy the SoundManager.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void SoundManager::DestroyInstance()
+{
+ rAssert( spInstance != NULL );
+
+ delete( GMA_PERSISTENT, spInstance );
+ spInstance = NULL;
+}
+
+//==============================================================================
+// SoundManager::Update
+//==============================================================================
+//
+// Description: Update the sound renderer. This should be done as frequently
+// as possible.
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void SoundManager::Update()
+{
+ unsigned int now = radTimeGetMilliseconds( );
+ unsigned int dif = now - gLastServiceTime;
+
+ if ( dif > BAD_SERVICE_TIME )
+ {
+ #ifndef RAD_DEBUG
+ rReleasePrintf( "\nAUDIO: Detected Service Lag:[%d]ms -- this could cause skipping\n\n", dif );
+ #endif
+ }
+
+ gLastServiceTime = now;
+
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_musicPlayer->Service();
+
+ rAssert( m_pSoundRenderMgr != NULL );
+ m_pSoundRenderMgr->Service();
+}
+
+//==============================================================================
+// SoundManager::UpdateOncePerFrame
+//==============================================================================
+//
+// Description: Update the sound renderer's expensive, can-do-once-per-frame
+// stuff (e.g. positional sound info).
+//
+// Parameters: None.
+//
+// Return: None.
+//
+//==============================================================================
+void SoundManager::UpdateOncePerFrame( unsigned int elapsedTime, ContextEnum context, bool useContext, bool isPausedForErrors )
+{
+
+ unsigned int now = radTimeGetMilliseconds( );
+ unsigned int dif = now - gLastServiceOncePerFrameTime;
+
+ if ( dif > BAD_SERVICE_TIME )
+ {
+ #ifndef RAD_DEBUG
+ rReleasePrintf( "\nAUDIO: Detected ServiceOpf Lag:[%d]ms -- this could cause skipping\n\n", dif );
+ #endif
+ }
+
+ gLastServiceOncePerFrameTime = now;
+
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ rAssert( m_pSoundRenderMgr != NULL );
+ m_pSoundRenderMgr->ServiceOncePerFrame( elapsedTime );
+ m_soundFXPlayer->ServiceOncePerFrame( elapsedTime );
+ m_movingSoundManager->ServiceOncePerFrame();
+
+ if( !isPausedForErrors )
+ {
+ //
+ // If we've paused the players, don't update this thing because it could
+ // start up new sounds
+ //
+ m_avatarSoundPlayer.UpdateOncePerFrame( elapsedTime );
+ }
+
+ //
+ // No point in updating listener position more than once per frame
+ //
+ if( useContext )
+ {
+ m_listener.Update( context );
+ }
+
+ //
+ // The dialog queue timer list isn't that time-sensitive, once per frame
+ // should be fine
+ //
+ if( m_dialogCoordinator != NULL )
+ {
+ m_dialogCoordinator->ServiceOncePerFrame();
+ }
+}
+
+//=============================================================================
+// SoundManager::HandleEvent
+//=============================================================================
+// Description: Handle any events that the underlying systems won't do
+// during mute.
+//
+// Parameters: id - ID of event to be handled
+// pEventData - user data for event
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch( id )
+ {
+ case EVENT_CONVERSATION_SKIP:
+ //
+ // We only get this in mute mode. Nothing to stop, signal that we're done
+ //
+ GetEventManager()->TriggerEvent( EVENT_CONVERSATION_DONE );
+ break;
+ case EVENT_CONVERSATION_START:
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_LETTERBOX, NULL, false );
+ break;
+ case EVENT_CONVERSATION_DONE:
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_LETTERBOX, NULL, true );
+ break;
+ case EVENT_GETINTOVEHICLE_END:
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_ONFOOT, NULL, true );
+ break;
+ case EVENT_GETOUTOFVEHICLE_END:
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_ONFOOT, NULL, false );
+ break;
+ case EVENT_ENTER_INTERIOR_START:
+ case EVENT_EXIT_INTERIOR_START:
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_JUST_MUSIC, NULL, false );
+ break;
+ case EVENT_ENTER_INTERIOR_END:
+ case EVENT_EXIT_INTERIOR_END:
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_JUST_MUSIC, NULL, true );
+ break;
+
+ case EVENT_MISSION_RESET:
+ //
+ // D'oh! Problem: if you fail a mission just as you're trying to go into an interior, you never
+ // get the enter/exit_interior_end event. However, we will be getting a mission reset in this
+ // case, so bring up the volume just in case.
+ //
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_JUST_MUSIC, NULL, true );
+
+ //
+ // Fall through to case below
+ //
+
+ case EVENT_CHARACTER_POS_RESET:
+ case EVENT_VEHICLE_DESTROYED_SYNC_SOUND:
+ if( GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar() )
+ {
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_ONFOOT, NULL, true );
+ }
+ else
+ {
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_ONFOOT, NULL, false );
+ }
+ break;
+
+ case EVENT_FE_MENU_SELECT:
+ playStartupAcceptSound();
+ break;
+
+ case EVENT_FE_MENU_UPORDOWN:
+ playStartupScrollSound();
+ break;
+
+ default:
+ rAssertMsg( false, "Huh? SoundManager getting events it shouldn't\n" );
+ break;
+ }
+}
+
+//=============================================================================
+// SoundManager::OnBootupStart
+//=============================================================================
+// Description: Called when bootup context started
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::OnBootupStart()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ //
+ // Load the RadScript and RadMusic files
+ //
+ m_pSoundRenderMgr->QueueCementFileRegistration();
+ m_musicPlayer->QueueRadmusicScriptLoad();
+ m_pSoundRenderMgr->QueueRadscriptFileLoads();
+
+ //
+ // Load front end sounds
+ //
+ m_soundLoader->LoadFrontEnd();
+}
+
+//=============================================================================
+// SoundManager::OnBootupComplete
+//=============================================================================
+// Description: Initialize the dialog system once the scripts are known to
+// have been loaded
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::OnBootupComplete()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ dumpStartupSounds();
+
+ // Set up the tuner
+ m_pSoundRenderMgr->GetTuner()->PostScriptLoadInitialize();
+
+ //
+ // If the sound levels haven't been auto-loaded, set them now
+ //
+ if( !( GetGameDataManager()->IsGameLoaded() ) )
+ {
+ ResetData();
+ }
+ else
+ {
+ //
+ // Hack! If we've loaded sound volumes before we had the scripts loaded,
+ // then we wouldn't have been able to calculate the ambience volumes properly.
+ // Fix those up now.
+ //
+ SetAmbienceVolume( GetCalculatedAmbienceVolume() );
+ }
+}
+
+//=============================================================================
+// SoundManager::QueueLevelSoundLoads
+//=============================================================================
+// Description: Prepare to load level-related sound through the loading manager
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::QueueLevelSoundLoads()
+{
+ RenderEnums::LevelEnum level;
+
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ level = GetGameplayManager()->GetCurrentLevelIndex();
+ m_soundLoader->LevelLoad( level );
+ m_musicPlayer->QueueMusicLevelLoad( level );
+}
+
+//=============================================================================
+// SoundManager::ResetDucking
+//=============================================================================
+// Description: Bring all the volume levels back up
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::ResetDucking()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+ m_pSoundRenderMgr->GetTuner()->ResetDuck();
+}
+
+//=============================================================================
+// SoundManager::OnFrontEndStart
+//=============================================================================
+// Description: To be called when front end starts, so we can do initialization
+// stuff
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::OnFrontEndStart()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ //
+ // Start loading the permanent, always-in-memory stuff
+ //
+ m_soundLoader->LoadPermanentSounds();
+
+ m_musicPlayer->OnFrontEndStart();
+ m_soundFXPlayer->OnFrontEndStart();
+}
+
+//=============================================================================
+// SoundManager::OnFrontEndEnd
+//=============================================================================
+// Description: To be called when front end ends, so we can do destruction
+// stuff
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::OnFrontEndEnd()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_musicPlayer->OnFrontEndFinish();
+}
+
+//=============================================================================
+// SoundManager::OnGameplayStart
+//=============================================================================
+// Description: To be called when gameplay starts, so we can do initialization
+// stuff
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::OnGameplayStart()
+{
+ bool playerInCar;
+
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ //
+ // Pass on start notice
+ //
+ playerInCar = m_avatarSoundPlayer.OnBeginGameplay();
+ m_musicPlayer->OnGameplayStart( playerInCar );
+ m_soundFXPlayer->OnGameplayStart();
+
+ //
+ // Assume we're starting gameplay on foot, except for minigame. Start the ducking
+ //
+ if( GetGameplayManager()->IsSuperSprint() )
+ {
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_MINIGAME, NULL, false );
+ }
+ else
+ {
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_ONFOOT, NULL, false );
+ }
+}
+
+//=============================================================================
+// SoundManager::OnGameplayEnd
+//=============================================================================
+// Description: To be called when gameplay ends, so we can do destruction
+// stuff
+//
+// Parameters: goingToFE - indicates whether we're exiting gameplay to go
+// to the front end (as opposed to loading a different
+// level)
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::OnGameplayEnd( bool goingToFE )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_avatarSoundPlayer.OnEndGameplay();
+ m_musicPlayer->OnGameplayFinish();
+ m_dialogCoordinator->OnGameplayEnd();
+ m_soundFXPlayer->OnGameplayEnd();
+
+ m_soundLoader->LevelUnload( goingToFE );
+ m_musicPlayer->UnloadRadmusicScript();
+
+ //
+ // Set up for the front end
+ //
+ if( goingToFE )
+ {
+ m_musicPlayer->QueueMusicLevelLoad( RenderEnums::L3 );
+ }
+}
+
+//=============================================================================
+// SoundManager::OnPauseStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::OnPauseStart()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_PAUSE, NULL, false );
+
+ m_musicPlayer->OnPauseStart();
+ m_dialogCoordinator->OnPauseStart();
+ m_soundFXPlayer->OnPauseStart();
+ m_NISPlayer->PauseAllNISPlayers();
+
+ GetEventManager()->TriggerEvent( EVENT_FE_PAUSE_MENU_START );
+}
+
+//=============================================================================
+// SoundManager::OnPauseEnd
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::OnPauseEnd()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_PAUSE, NULL, true );
+
+ m_musicPlayer->OnPauseEnd();
+ m_dialogCoordinator->OnPauseEnd();
+ m_soundFXPlayer->OnPauseEnd();
+ m_NISPlayer->ContinueAllNISPlayers();
+
+ GetEventManager()->TriggerEvent( EVENT_FE_PAUSE_MENU_END );
+}
+
+//=============================================================================
+// SoundManager::OnStoreScreenStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::OnStoreScreenStart( bool playMusic )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_STORE, NULL, false );
+
+ if( playMusic )
+ {
+ m_musicPlayer->OnStoreStart();
+ }
+}
+
+//=============================================================================
+// SoundManager::OnStoreScreenEnd
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::OnStoreScreenEnd()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_STORE, NULL, true );
+
+ m_musicPlayer->OnStoreEnd();
+}
+
+//=============================================================================
+// SoundManager::DuckEverythingButMusicBegin
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::DuckEverythingButMusicBegin( bool playMuzak )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_JUST_MUSIC, NULL, false );
+
+ if( playMuzak )
+ {
+ m_musicPlayer->OnStoreStart();
+ }
+}
+
+//=============================================================================
+// SoundManager::DuckEverythingButMusicEnd
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::DuckEverythingButMusicEnd( bool playMuzak )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_JUST_MUSIC, NULL, true );
+
+ if( playMuzak )
+ {
+ m_musicPlayer->OnStoreEnd();
+ }
+}
+
+//=============================================================================
+// SoundManager::OnMissionBriefingStart
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::OnMissionBriefingStart()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_MISSION, NULL, false );
+ m_NISPlayer->PauseAllNISPlayers();
+}
+
+//=============================================================================
+// SoundManager::OnMissionBriefingEnd
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::OnMissionBriefingEnd()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_MISSION, NULL, true );
+ m_NISPlayer->ContinueAllNISPlayers();
+}
+
+//=============================================================================
+// SoundManager::DuckForInGameCredits
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::DuckForInGameCredits()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_pSoundRenderMgr->GetTuner()->ActivateSituationalDuck( NULL, Sound::DUCK_SIT_CREDITS, NULL, false );
+}
+
+//=============================================================================
+// SoundManager::LoadSoundFile
+//=============================================================================
+// Description: Pass on the file loading directive to the appropriate subsystem,
+// with callback object for the sake of the loading manager
+//
+// Parameters: filename - name of file to load
+// callbackObj - callback into loading system when complete
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::LoadSoundFile( const char* filename, SoundFileHandler* callbackObj )
+{
+ char fileExtension[4];
+ int length = strlen( filename );
+
+ //
+ // Determine the appropriate sound subsystem by the file extension
+ //
+ rAssert( length > 4 );
+ if( filename[length - 4] == '.' )
+ {
+ strcpy( fileExtension, &(filename[strlen(filename) - 3]) );
+
+ if( strcmp( fileExtension, RADSCRIPT_TYPE_INFO_FILE ) == 0 )
+ {
+ m_pSoundRenderMgr->LoadTypeInfoFile( filename, callbackObj );
+ }
+ else if( strcmp( fileExtension, RADSCRIPT_SCRIPT_FILE ) == 0 )
+ {
+ m_pSoundRenderMgr->LoadScriptFile( filename, callbackObj );
+ }
+ else if( strcmp( fileExtension, RADMUSIC_SCRIPT_FILE ) == 0 )
+ {
+ m_musicPlayer->LoadRadmusicScript( filename, callbackObj );
+ }
+ else
+ {
+ //
+ // Oops, don't recognize that type
+ //
+ rAssert( false );
+ }
+ }
+ else
+ {
+ //
+ // No file extension, must be a sound cluster name
+ //
+ if( m_soundLoader->LoadClusterByName( filename, callbackObj ) )
+ {
+ //
+ // LoadClusterByName indicated that the cluster was already loaded,
+ // so call the callback
+ //
+ callbackObj->LoadCompleted();
+ }
+ }
+}
+
+//=============================================================================
+// SoundManager::LoadCarSound
+//=============================================================================
+// Description: Load car sound. Duh.
+//
+// Parameters: theCar - vehicle whose sound is to be loaded
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::LoadCarSound( Vehicle* theCar, bool unloadOtherCars )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_soundLoader->LoadCarSound( theCar, unloadOtherCars );
+}
+
+//=============================================================================
+// SoundManager::GetMasterVolume
+//=============================================================================
+// Description: Get master volume
+//
+// Parameters: None
+//
+// Return: Volume setting from 0.0f to 1.0f
+//
+//=============================================================================
+float SoundManager::GetMasterVolume()
+{
+ if( m_isMuted )
+ {
+ return( 0.0f );
+ }
+
+ rAssert( m_pSoundRenderMgr != NULL );
+
+ return( m_pSoundRenderMgr->GetTuner()->GetMasterVolume() );
+}
+
+//=============================================================================
+// SoundManager::SetMasterVolume
+//=============================================================================
+// Description: Set master volume
+//
+// Parameters: volume - new master volume
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::SetMasterVolume( float volume )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ rAssert( m_pSoundRenderMgr != NULL );
+
+ m_pSoundRenderMgr->GetTuner()->SetMasterVolume( volume );
+
+ //
+ // Stinky special cases. This must be fixed.
+ //
+ /*if ( m_musicPlayer )
+ {
+ m_musicPlayer->SetVolume( volume * m_pSoundRenderMgr->GetTuner()->GetMusicVolume() );
+ m_musicPlayer->SetAmbienceVolume( volume * m_pSoundRenderMgr->GetTuner()->GetAmbienceVolume() );
+ }*/
+}
+
+//=============================================================================
+// SoundManager::GetSfxVolume
+//=============================================================================
+// Description: Get sound effect volume
+//
+// Parameters: None
+//
+// Return: Volume setting from 0.0f to 1.0f
+//
+//=============================================================================
+float SoundManager::GetSfxVolume()
+{
+ if( m_isMuted )
+ {
+ return( 0.0f );
+ }
+
+ rAssert( m_pSoundRenderMgr != NULL );
+
+ return( m_pSoundRenderMgr->GetTuner()->GetSfxVolume() );
+}
+
+//=============================================================================
+// SoundManager::SetSfxVolume
+//=============================================================================
+// Description: Set sound effect volume
+//
+// Parameters: volume - new sound effect volume
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::SetSfxVolume( float volume )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ rAssert( m_pSoundRenderMgr != NULL );
+
+ m_pSoundRenderMgr->GetTuner()->SetSfxVolume( volume );
+}
+
+//=============================================================================
+// SoundManager::GetCarVolume
+//=============================================================================
+// Description: Get sound effect volume
+//
+// Parameters: None
+//
+// Return: Volume setting from 0.0f to 1.0f
+//
+//=============================================================================
+float SoundManager::GetCarVolume()
+{
+ if( m_isMuted )
+ {
+ return( 0.0f );
+ }
+
+ rAssert( m_pSoundRenderMgr != NULL );
+
+ return( m_pSoundRenderMgr->GetTuner()->GetCarVolume() );
+}
+
+//=============================================================================
+// SoundManager::SetCarVolume
+//=============================================================================
+// Description: Set sound effect volume
+//
+// Parameters: volume - new sound effect volume
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::SetCarVolume( float volume )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ rAssert( m_pSoundRenderMgr != NULL );
+
+ m_pSoundRenderMgr->GetTuner()->SetCarVolume( volume );
+}
+
+//=============================================================================
+// SoundManager::GetMusicVolume
+//=============================================================================
+// Description: Get music volume
+//
+// Parameters: None
+//
+// Return: Volume setting from 0.0f to 1.0f
+//
+//=============================================================================
+float SoundManager::GetMusicVolume()
+{
+ if( m_isMuted )
+ {
+ return( 0.0f );
+ }
+
+ rAssert( m_pSoundRenderMgr != NULL );
+
+ //rAssert( m_musicPlayer->GetVolume() == m_pSoundRenderMgr->GetTuner()->GetMusicVolume() );
+
+ return( m_pSoundRenderMgr->GetTuner()->GetMusicVolume() );
+}
+
+//=============================================================================
+// SoundManager::SetMusicVolume
+//=============================================================================
+// Description: Set music volume
+//
+// Parameters: volume - new music volume
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::SetMusicVolume( float volume )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ rAssert( m_pSoundRenderMgr != NULL );
+
+ m_pSoundRenderMgr->GetTuner()->SetMusicVolume( volume );
+
+ //
+ // Sadly, we have to deal with the music player outside of the tuner. The
+ // daSound layer doesn't do our interactive music, so volume is controlled
+ // separately.
+ //
+ //rAssert( m_musicPlayer != NULL );
+ //m_musicPlayer->SetVolume( volume * m_pSoundRenderMgr->GetTuner()->GetMasterVolume() );
+}
+
+//=============================================================================
+// SoundManager::SetMusicVolumeWithoutTuner
+//=============================================================================
+// Description: Set music volume without affecting the tuner's internally
+// stored volume setting
+//
+// Parameters: volume - new music volume
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::SetMusicVolumeWithoutTuner( float volume )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ rAssert( m_musicPlayer != NULL );
+ m_musicPlayer->SetVolume( volume * m_pSoundRenderMgr->GetTuner()->GetMasterVolume() );
+}
+
+//=============================================================================
+// SoundManager::GetAmbienceVolume
+//=============================================================================
+// Description: Get ambience volume
+//
+// Parameters: None
+//
+// Return: Volume setting from 0.0f to 1.0f
+//
+//=============================================================================
+float SoundManager::GetAmbienceVolume()
+{
+ if( m_isMuted )
+ {
+ return( 0.0f );
+ }
+
+ rAssert( m_pSoundRenderMgr != NULL );
+
+#ifdef RAD_WIN32
+ m_musicPlayer->SetAmbienceVolume( m_pSoundRenderMgr->GetTuner()->GetAmbienceVolume() );
+#endif
+
+ rAssert( m_musicPlayer->GetAmbienceVolume() == m_pSoundRenderMgr->GetTuner()->GetAmbienceVolume() );
+
+ return( m_pSoundRenderMgr->GetTuner()->GetAmbienceVolume() );
+}
+
+//=============================================================================
+// SoundManager::SetAmbienceVolume
+//=============================================================================
+// Description: Set ambience volume
+//
+// Parameters: volume - new music volume
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::SetAmbienceVolume( float volume )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ rAssert( m_pSoundRenderMgr != NULL );
+
+ m_pSoundRenderMgr->GetTuner()->SetAmbienceVolume( volume );
+
+ //
+ // Sadly, we have to deal with the ambience player outside of the tuner. The
+ // daSound layer doesn't do our interactive music, so volume is controlled
+ // separately.
+ //
+ rAssert( m_musicPlayer != NULL );
+ m_musicPlayer->SetAmbienceVolume( volume * m_pSoundRenderMgr->GetTuner()->GetMasterVolume() );
+}
+
+//=============================================================================
+// SoundManager::SetAmbienceVolumeWithoutTuner
+//=============================================================================
+// Description: Set ambience volume without affecting the tuner's internally
+// stored volume setting
+//
+// Parameters: volume - new music volume
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::SetAmbienceVolumeWithoutTuner( float volume )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ rAssert( m_musicPlayer != NULL );
+ m_musicPlayer->SetAmbienceVolume( volume * m_pSoundRenderMgr->GetTuner()->GetMasterVolume() );
+}
+
+//=============================================================================
+// SoundManager::GetCalculatedAmbienceVolume
+//=============================================================================
+// Description: Hack!! Used to figure out what the ambience volume should
+// be based on the sfx volume, to tie it to the effect slider.
+//
+// Parameters: None
+//
+// Return: float
+//
+//=============================================================================
+float SoundManager::GetCalculatedAmbienceVolume()
+{
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ globalSettings* settings;
+ float ratio;
+ float newVolume;
+
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ if(!nameSpace)
+ {
+ return( -1.0f );
+ }
+ nameSpaceObj = nameSpace->GetInstance( "tuner" );
+ if(!nameSpaceObj)
+ {
+ return( -1.0f );
+ }
+
+ settings = static_cast<globalSettings*>( nameSpaceObj );
+ ratio = GetSfxVolume() / settings->GetSfxVolume();
+
+ newVolume = ratio * settings->GetAmbienceVolume();
+ if( newVolume < 0.0f )
+ {
+ newVolume = 0.0f;
+ }
+ else if( newVolume > 1.0f )
+ {
+ newVolume = 1.0f;
+ }
+
+ return( newVolume );
+}
+
+//=============================================================================
+// SoundManager::GetDialogueVolume
+//=============================================================================
+// Description: Get dialogue volume
+//
+// Parameters: None
+//
+// Return: Volume setting from 0.0f to 1.0f
+//
+//=============================================================================
+float SoundManager::GetDialogueVolume()
+{
+ if( m_isMuted )
+ {
+ return( 0.0f );
+ }
+
+ rAssert( m_pSoundRenderMgr != NULL );
+
+ return( m_pSoundRenderMgr->GetTuner()->GetDialogueVolume() );
+}
+
+//=============================================================================
+// SoundManager::DebugRender
+//=============================================================================
+// Description: Display the sound debug info
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::DebugRender()
+{
+ m_debugDisplay->Render();
+}
+
+//=============================================================================
+// SoundManager::SetDialogueVolume
+//=============================================================================
+// Description: Set dialogue volume
+//
+// Parameters: volume - new dialog volume
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::SetDialogueVolume( float volume )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ rAssert( m_pSoundRenderMgr != NULL );
+
+ m_pSoundRenderMgr->GetTuner()->SetDialogueVolume( volume );
+}
+
+//=============================================================================
+// SoundManager::PlayCarOptionMenuStinger
+//=============================================================================
+// Description: Play option menu sound for car slider
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::PlayCarOptionMenuStinger()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_soundFXPlayer->PlayCarOptionStinger( GetCarVolume() );
+}
+
+//=============================================================================
+// SoundManager::PlayDialogueOptionMenuStinger
+//=============================================================================
+// Description: Play option menu sound for dialogue slider
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::PlayDialogueOptionMenuStinger()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_soundFXPlayer->PlayDialogOptionStinger( GetDialogueVolume() );
+}
+
+//=============================================================================
+// SoundManager::PlayMusicOptionMenuStinger
+//=============================================================================
+// Description: Play option menu sound for music slider
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::PlayMusicOptionMenuStinger()
+{
+ //
+ // Music is special. We're already playing music in the front end,
+ // so we don't need to play a stinger there. We need it in game,
+ // though.
+ //
+ if( m_isMuted || ( GetGameFlow()->GetCurrentContext() == CONTEXT_FRONTEND ) )
+ {
+ return;
+ }
+
+ m_soundFXPlayer->PlayMusicOptionStinger( GetMusicVolume() );
+}
+
+//=============================================================================
+// SoundManager::PlaySfxOptionMenuStinger
+//=============================================================================
+// Description: Play option menu sound for sfx slider
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::PlaySfxOptionMenuStinger()
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_soundFXPlayer->PlaySfxOptionStinger( GetSfxVolume() );
+}
+
+void SoundManager::LoadNISSound( radKey32 NISSoundID, NISSoundLoadedCallback* callback )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_NISPlayer->LoadNISSound( NISSoundID, callback );
+}
+
+void SoundManager::PlayNISSound( radKey32 NISSoundID, rmt::Box3D* boundingBox, NISSoundPlaybackCompleteCallback* callback )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_NISPlayer->PlayNISSound( NISSoundID, boundingBox, callback );
+}
+
+void SoundManager::StopAndDumpNISSound( radKey32 NISSoundID )
+{
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_NISPlayer->StopAndDumpNISSound( NISSoundID );
+}
+
+//=============================================================================
+// SoundManager::StopForMovie
+//=============================================================================
+// Description: To be called when we want to stop the action for an FMV
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::StopForMovie()
+{
+ if ( !m_stoppedForMovie )
+ {
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ //
+ // Shut the works down
+ //
+ m_musicPlayer->StopForMovie();
+ GetInteriorManager()->UnloadGagSounds();
+ m_pSoundRenderMgr->GetPlayerManager()->PausePlayers();
+ m_stoppedForMovie = true;
+ }
+}
+
+//=============================================================================
+// SoundManager::ResumeAfterMovie
+//=============================================================================
+// Description: Call this to start everything up again after an FMV
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::ResumeAfterMovie()
+{
+ if ( m_stoppedForMovie )
+ {
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ m_musicPlayer->ResumeAfterMovie();
+ m_pSoundRenderMgr->GetPlayerManager()->ContinuePlayers();
+ m_stoppedForMovie = false;
+ }
+}
+
+//=============================================================================
+// SoundManager::IsStoppedForMovie
+//=============================================================================
+
+bool SoundManager::IsStoppedForMovie()
+{
+ if( m_isMuted )
+ {
+ return true;
+ }
+ else
+ {
+ return ( m_stoppedForMovie
+ && m_musicPlayer->IsStoppedForMovie()
+ && m_pSoundRenderMgr->GetPlayerManager()->AreAllPlayersStopped() );
+ }
+}
+
+//=============================================================================
+// SoundManager::MuteNISPlayers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::MuteNISPlayers()
+{
+ m_pSoundRenderMgr->GetTuner()->MuteNIS();
+}
+
+//=============================================================================
+// SoundManager::UnmuteNISPlayers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::UnmuteNISPlayers()
+{
+ m_pSoundRenderMgr->GetTuner()->UnmuteNIS();
+}
+
+//=============================================================================
+// SoundManager::RestartSupersprintMusic
+//=============================================================================
+// Description: Give the supersprint music a kick
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::RestartSupersprintMusic()
+{
+ m_musicPlayer->RestartSupersprintMusic();
+}
+
+//=============================================================================
+// SoundManager::GetBeatValue
+//=============================================================================
+// Description: Pass on the beat from the music player
+//
+// Parameters: None
+//
+// Return: float from 0.0f to 4.0f
+//
+//=============================================================================
+float SoundManager::GetBeatValue()
+{
+ rAssert( m_musicPlayer != NULL );
+
+ return( m_musicPlayer->GetBeatValue() );
+}
+
+//=============================================================================
+// SoundManager::IsFoodCharacter
+//=============================================================================
+// Description: Determine whether this character gets Askfood lines or
+// Idlereply
+//
+// Parameters: theGuy - ambient character being talked to
+//
+// Return: true for Askfood, false for Idlereply
+//
+//=============================================================================
+bool SoundManager::IsFoodCharacter( Character* theGuy )
+{
+ return( DialogLine::IsFoodCharacter( theGuy ) );
+}
+
+//=============================================================================
+// SoundManager::SetDialogueLanguage
+//=============================================================================
+// Description: Switch the current cement file for dialogue to match the
+// given language.
+//
+// Parameters: language - new language to use
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::SetDialogueLanguage( Scrooby::XLLanguage language )
+{
+ // TODO: Commented out to get PAL working for alpha. It's basically a no-op anyway until we get localized dialogue. --jdy
+ m_pSoundRenderMgr->SetLanguage( language );
+}
+
+//=============================================================================
+// SoundManager::LoadData
+//=============================================================================
+// Description: Load sound settings (from saved game file).
+//
+// Parameters:
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::LoadData( const GameDataByte* dataBuffer, unsigned int numBytes )
+{
+ SoundSettings soundSettings;
+ SoundMode loadedSoundMode;
+ float calculatedAmbienceVolume;
+
+#ifdef RAD_WIN32 // temp
+ return;
+#endif
+
+ memcpy( &soundSettings, dataBuffer, sizeof( soundSettings ) );
+
+ this->SetMusicVolume( soundSettings.musicVolume );
+ this->SetSfxVolume( soundSettings.sfxVolume );
+ this->SetCarVolume( soundSettings.carVolume );
+
+ calculatedAmbienceVolume = GetCalculatedAmbienceVolume();
+ //
+ // If the volume returned is less than zero, we haven't initialized
+ // the scripts yet. This will get taken care of in OnBootupComplete(),
+ // then.
+ //
+ if( calculatedAmbienceVolume >= 0.0f )
+ {
+ SetAmbienceVolume( GetCalculatedAmbienceVolume() );
+ }
+
+ //
+ // Hack! Hack! Hack! Hack!
+ //
+ // This is horrible. I'm not allowed to change the save structure, because we're
+ // too close to final. I can't cast that bool to an enumeration, because the PS2
+ // changes its size in release. The Xbox compiler won't let me set bit flags on
+ // booleans. So, I'm going to use the dialogue volume to signal stereo/mono by
+ // adding 100 to the volume to signal mono. I feel dirty.
+ //
+ if( soundSettings.dialogVolume >= 100.0f )
+ {
+ this->SetDialogueVolume( soundSettings.dialogVolume - 100.0f );
+ loadedSoundMode = SOUND_MONO;
+ }
+ else
+ {
+ this->SetDialogueVolume( soundSettings.dialogVolume );
+ if( soundSettings.isSurround )
+ {
+ loadedSoundMode = SOUND_SURROUND;
+ }
+ else
+ {
+ loadedSoundMode = SOUND_STEREO;
+ }
+ }
+
+#ifdef RAD_GAMECUBE
+ //
+ // GameCube's IPL needs to override the loaded settings, sadly
+ //
+ u32 GCSoundMode = OSGetSoundMode();
+ if( GCSoundMode == OS_SOUND_MODE_MONO )
+ {
+ //
+ // IPL says mono, we go mono
+ //
+ loadedSoundMode = SOUND_MONO;
+ }
+ else
+ {
+ if( loadedSoundMode == SOUND_MONO )
+ {
+ //
+ // IPL says stereo, we go stereo. Since the saved game had said
+ // mono, we need to choose a sub-sound mode. We'll go with ProLogic.
+ //
+ loadedSoundMode = SOUND_SURROUND;
+ }
+ }
+
+#endif
+
+ this->SetSoundMode( loadedSoundMode );
+}
+
+//=============================================================================
+// SoundManager::SaveData
+//=============================================================================
+// Description: Save sound settings (to saved game file).
+//
+// Parameters:
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::SaveData( GameDataByte* dataBuffer, unsigned int numBytes )
+{
+ SoundMode mode;
+ SoundSettings soundSettings;
+
+ soundSettings.musicVolume = this->GetMusicVolume();
+ soundSettings.sfxVolume = this->GetSfxVolume();
+ soundSettings.carVolume = this->GetCarVolume();
+ soundSettings.dialogVolume = this->GetDialogueVolume();
+
+ mode = this->GetSoundMode();
+ if( mode == SOUND_MONO )
+ {
+ //
+ // Horrible stinky hack explained in LoadData()
+ //
+ soundSettings.dialogVolume += 100.0f;
+ soundSettings.isSurround = false;
+ }
+ else if( mode == SOUND_STEREO )
+ {
+ soundSettings.isSurround = false;
+ }
+ else
+ {
+ soundSettings.isSurround = true;
+ }
+
+ memcpy( dataBuffer, &soundSettings, sizeof( soundSettings ) );
+}
+
+//=============================================================================
+// SoundManager::ResetData
+//=============================================================================
+// Description: Reset sound settings to defaults.
+//
+// Parameters:
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::ResetData()
+{
+ if( GetGameDataManager()->IsGameLoaded() )
+ {
+ // since sound settings can be changed in the FE, we should
+ // never reset to defaults if a game is already loaded
+ //
+ return;
+ }
+
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ globalSettings* settings;
+
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ nameSpaceObj = nameSpace->GetInstance( "tuner" );
+ rAssert( nameSpaceObj != NULL );
+
+ settings = static_cast<globalSettings*>( nameSpaceObj );
+
+ SetSfxVolume( settings->GetSfxVolume() );
+ SetMusicVolume( settings->GetMusicVolume() );
+ SetAmbienceVolume( settings->GetAmbienceVolume() );
+ SetDialogueVolume( settings->GetDialogueVolume() );
+ SetCarVolume( settings->GetCarVolume() );
+
+ //
+ // Sound mode. For GameCube, get it from the IPL (Initial Program
+ // Loader, the thingy you get when you start up a GameCube without
+ // a disc). For PS2 and Xbox, default to stereo (RadSound ignores
+ // this stuff for Xbox anyway, since these settings are supposed
+ // to be changed from the dashboard).
+ //
+#ifdef RAD_GAMECUBE
+ u32 GCSoundMode = OSGetSoundMode();
+ if( GCSoundMode == OS_SOUND_MODE_MONO )
+ {
+ SetSoundMode( SOUND_MONO );
+ }
+ else
+ {
+ SetSoundMode( SOUND_SURROUND );
+ }
+#else
+ SetSoundMode( SOUND_SURROUND );
+#endif
+}
+
+#ifdef RAD_WIN32
+//=============================================================================
+// SoundManager::GetConfigName
+//=============================================================================
+// Description: Returns the name of the sound manager's config
+//
+// Parameters: n/a
+//
+// Returns:
+//
+//=============================================================================
+
+const char* SoundManager::GetConfigName() const
+{
+ return "Sound";
+}
+
+//=============================================================================
+// SoundManager::GetNumProperties
+//=============================================================================
+// Description: Returns the number of config properties
+//
+// Parameters: n/a
+//
+// Returns:
+//
+//=============================================================================
+
+int SoundManager::GetNumProperties() const
+{
+ return 5;
+}
+
+//=============================================================================
+// SoundManager::LoadDefaults
+//=============================================================================
+// Description: Loads the default configuration for the sound manager.
+//
+// Parameters: n/a
+//
+// Returns:
+//
+//=============================================================================
+
+void SoundManager::LoadDefaults()
+{
+ IRadNameSpace* nameSpace;
+ IRefCount* nameSpaceObj;
+ globalSettings* settings;
+
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+ nameSpaceObj = nameSpace->GetInstance( "tuner" );
+ rAssert( nameSpaceObj != NULL );
+
+ settings = static_cast<globalSettings*>( nameSpaceObj );
+
+ SetSfxVolume( settings->GetSfxVolume() );
+ SetMusicVolume( settings->GetMusicVolume() );
+ SetAmbienceVolume( settings->GetAmbienceVolume() );
+ SetDialogueVolume( settings->GetDialogueVolume() );
+ SetCarVolume( settings->GetCarVolume() );
+}
+
+//=============================================================================
+// SoundManager::LoadConfig
+//=============================================================================
+// Description: Loads the manager's configuration
+//
+// Parameters: n/a
+//
+// Returns:
+//
+//=============================================================================
+
+void SoundManager::LoadConfig( ConfigString& config )
+{
+ char property[ ConfigString::MaxLength ];
+ char value[ ConfigString::MaxLength ];
+
+ while ( config.ReadProperty( property, value ) )
+ {
+ if( _stricmp( property, "sfx" ) == 0 )
+ {
+ float val = (float) atof( value );
+ if( val >= 0 && val <= 1 )
+ {
+ SetSfxVolume( val );
+ }
+ }
+ else if( _stricmp( property, "music" ) == 0 )
+ {
+ float val = (float) atof( value );
+ if( val >= 0 && val <= 1 )
+ {
+ SetMusicVolume( val );
+ }
+ }
+ else if( _stricmp( property, "ambience" ) == 0 )
+ {
+ float val = (float) atof( value );
+ if( val >= 0 && val <= 1 )
+ {
+ SetAmbienceVolume( val );
+ }
+ }
+ else if( _stricmp( property, "dialogue" ) == 0 )
+ {
+ float val = (float) atof( value );
+ if( val >= 0 && val <= 1 )
+ {
+ SetDialogueVolume( val );
+ }
+ }
+ else if( _stricmp( property, "car" ) == 0 )
+ {
+ float val = (float) atof( value );
+ if( val >= 0 && val <= 1 )
+ {
+ SetCarVolume( val );
+ }
+ }
+ }
+}
+
+//=============================================================================
+// SoundManager::SaveConfig
+//=============================================================================
+// Description: Saves the manager's configuration to the config string.
+//
+// Parameters: config string to save to
+//
+// Returns:
+//
+//=============================================================================
+
+void SoundManager::SaveConfig( ConfigString& config )
+{
+ char value[ 32 ];
+
+ sprintf( value, "%f", GetSfxVolume() );
+ config.WriteProperty( "sfx", value );
+ sprintf( value, "%f", GetMusicVolume() );
+ config.WriteProperty( "music", value );
+ sprintf( value, "%f", GetAmbienceVolume() );
+ config.WriteProperty( "ambience", value );
+ sprintf( value, "%f", GetDialogueVolume() );
+ config.WriteProperty( "dialogue", value );
+ sprintf( value, "%f", GetCarVolume() );
+ config.WriteProperty( "car", value );
+}
+#endif // RAD_WIN32
+
+void SoundManager::SetSoundMode( SoundMode mode )
+{
+ radSoundOutputMode radSoundMode;
+
+ m_soundMode = mode;
+
+ if( m_soundMode == SOUND_MONO )
+ {
+ radSoundMode = radSoundOutputMode_Mono;
+ }
+ else if( m_soundMode == SOUND_STEREO )
+ {
+ radSoundMode = radSoundOutputMode_Stereo;
+ }
+ else
+ {
+ radSoundMode = radSoundOutputMode_Surround;
+ }
+
+ ::radSoundHalSystemGet()->SetOutputMode( radSoundMode );
+}
+
+SoundMode SoundManager::GetSoundMode()
+{
+ return( m_soundMode );
+}
+
+//******************************************************************************
+//
+// Protected Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundManager::SoundManager
+//==============================================================================
+//
+// Description: Constructor.
+//
+// Parameters:
+//
+// Return: N/A
+//
+//==============================================================================
+SoundManager::SoundManager( bool noSound, bool noMusic,
+ bool noEffects, bool noDialogue ) :
+ m_soundLoader( NULL ),
+ m_musicPlayer( NULL ),
+ m_soundFXPlayer( NULL ),
+ m_NISPlayer( NULL ),
+ m_movingSoundManager( NULL ),
+ m_isMuted( noSound ),
+ m_noMusic( noMusic ),
+ m_noEffects( noEffects ),
+ m_noDialogue( noDialogue ),
+ m_stoppedForMovie( false ),
+ m_selectSoundClip( NULL ),
+ m_scrollSoundClip( NULL ),
+ m_selectSoundClipPlayer( NULL ),
+ m_scrollSoundClipPlayer( NULL ),
+ m_soundMode( SOUND_STEREO )
+{
+ m_dialogCoordinator = NULL;
+
+ Sound::daSoundRenderingManagerCreate( GMA_AUDIO_PERSISTENT );
+
+ if( !m_isMuted )
+ {
+ m_pSoundRenderMgr = Sound::daSoundRenderingManagerGet();
+ rAssert( m_pSoundRenderMgr != NULL );
+
+ m_pSoundRenderMgr->Initialize();
+ }
+
+ GetGameDataManager()->RegisterGameData( this, sizeof( SoundSettings ), "Sound Manager" );
+
+ prepareStartupSounds();
+}
+
+//==============================================================================
+// SoundManager::~SoundManager
+//==============================================================================
+//
+// Description: Destructor. first attempt.
+//
+// Parameters: N/A
+//
+// Return: N/A
+//
+//==============================================================================
+SoundManager::~SoundManager()
+{
+ GetEventManager()->RemoveAll( this );
+
+ if( m_isMuted )
+ {
+ return;
+ }
+
+ delete( GMA_PERSISTENT, m_soundFXPlayer);
+ delete( GMA_PERSISTENT, m_movingSoundManager);
+ delete( GMA_PERSISTENT, m_NISPlayer);
+ delete( GMA_PERSISTENT, m_dialogCoordinator);
+ delete( GMA_PERSISTENT, m_musicPlayer);
+ delete( GMA_PERSISTENT, m_soundLoader);
+
+ Sound::daSoundRenderingManagerTerminate();
+
+ delete( GMA_PERSISTENT, m_debugDisplay);
+}
+
+//=============================================================================
+// SoundManager::initialize
+//=============================================================================
+// Description: Do some class member initialization tasks that we can't really
+// do in the constructor.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundManager::initialize()
+{
+ if( m_isMuted )
+ {
+ //
+ // We need to intercept dialog skip events, since the dialog coordinator
+ // isn't going to do it if we're muted
+ //
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_SKIP );
+ return;
+ }
+ else
+ {
+ //
+ // Set up a few tuner-related events
+ //
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_START );
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_DONE );
+ GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_START );
+ GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_END );
+ GetEventManager()->AddListener( this, EVENT_EXIT_INTERIOR_START );
+ GetEventManager()->AddListener( this, EVENT_EXIT_INTERIOR_END );
+ GetEventManager()->AddListener( this, EVENT_MISSION_RESET );
+ GetEventManager()->AddListener( this, EVENT_CHARACTER_POS_RESET );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED_SYNC_SOUND );
+ }
+
+ m_debugDisplay = new( GMA_PERSISTENT ) SoundDebugDisplay( m_pSoundRenderMgr );
+ m_soundLoader = new( GMA_PERSISTENT ) SoundLoader();
+ m_musicPlayer = new( GMA_PERSISTENT ) MusicPlayer( *(m_pSoundRenderMgr->GetTuner()) );
+ m_dialogCoordinator = new( GMA_PERSISTENT ) DialogCoordinator( m_pSoundRenderMgr->GetSoundNamespace() );
+ m_NISPlayer = new( GMA_PERSISTENT ) NISSoundPlayer();
+ m_movingSoundManager = new( GMA_PERSISTENT ) MovingSoundManager();
+ m_soundFXPlayer = new( GMA_PERSISTENT ) SoundEffectPlayer();
+ m_avatarSoundPlayer.Initialize();
+ m_listener.Initialize( *m_pSoundRenderMgr );
+
+ //
+ // Apply the non-global mute options
+ //
+ if( m_noMusic )
+ {
+ m_pSoundRenderMgr->GetTuner()->SetMusicVolume( 0.0f );
+ }
+ if( m_noEffects )
+ {
+ m_pSoundRenderMgr->GetTuner()->SetSfxVolume( 0.0f );
+ }
+ if( m_noDialogue )
+ {
+ m_pSoundRenderMgr->GetTuner()->SetDialogueVolume( 0.0f );
+ }
+
+ //
+ // Register a factory for creating the global settings object
+ //
+ ::radFactoryRegister( "globalSettings", (radFactoryOutParamProc*) ::GlobalSettingsObjCreate );
+ ::radFactoryRegister( "reverbSettings", (radFactoryOutParamProc*) ::ReverbSettingsObjCreate );
+ ::radFactoryRegister( "positionalSoundSettings", (radFactoryOutParamProc*) ::PositionalSettingsObjCreate );
+
+#ifdef RAD_WIN32
+ //
+ // Register with the game config manager
+ //
+ GetGameConfigManager()->RegisterConfig( this );
+#endif
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+void SoundManager::prepareStartupSounds()
+{
+ //
+ // Go direct to RadSound to load two sounds that we can
+ // play for bootcheck screens
+ //
+ IRadSoundRsdFileDataSource* selectFileDataSource =
+ radSoundRsdFileDataSourceCreate( GMA_DEFAULT );
+ selectFileDataSource->AddRef();
+ selectFileDataSource->InitializeFromFileName( "sound/accept.rsd",
+ false,
+ 0,
+ IRadSoundHalAudioFormat::Frames,
+ Sound::SoundNucleusGetClipFileAudioFormat() );
+ m_selectSoundClip = radSoundClipCreate( GMA_DEFAULT );
+ m_selectSoundClip->AddRef();
+ m_selectSoundClip->Initialize(
+ selectFileDataSource,
+ ::radSoundHalSystemGet()->GetRootMemoryRegion(),
+ false,
+ "sound/accept.rsd" );
+ selectFileDataSource->Release( );
+
+ m_selectSoundClipPlayer = radSoundClipPlayerCreate( GMA_DEFAULT );
+ m_selectSoundClipPlayer->AddRef();
+
+ IRadSoundRsdFileDataSource* scrollFileDataSource =
+ radSoundRsdFileDataSourceCreate( GMA_DEFAULT );
+ scrollFileDataSource->AddRef();
+ scrollFileDataSource->InitializeFromFileName( "sound/scroll.rsd",
+ false,
+ 0,
+ IRadSoundHalAudioFormat::Frames,
+ Sound::SoundNucleusGetClipFileAudioFormat() );
+ m_scrollSoundClip = radSoundClipCreate( GMA_DEFAULT );
+ m_scrollSoundClip->AddRef();
+ m_scrollSoundClip->Initialize(
+ scrollFileDataSource,
+ ::radSoundHalSystemGet()->GetRootMemoryRegion(),
+ false,
+ "sound/scroll.rsd" );
+ scrollFileDataSource->Release( );
+
+ m_scrollSoundClipPlayer = radSoundClipPlayerCreate( GMA_DEFAULT );
+ m_scrollSoundClipPlayer->AddRef();
+
+ //
+ // Starting listening for the select/scroll events
+ //
+ GetEventManager()->AddListener( this, EVENT_FE_MENU_SELECT );
+ GetEventManager()->AddListener( this, EVENT_FE_MENU_UPORDOWN );
+}
+
+void SoundManager::dumpStartupSounds()
+{
+ m_selectSoundClip->Release();
+ m_selectSoundClip = NULL;
+
+ m_selectSoundClipPlayer->Release();
+ m_selectSoundClipPlayer = NULL;
+
+ m_scrollSoundClip->Release();
+ m_scrollSoundClip = NULL;
+
+ m_scrollSoundClipPlayer->Release();
+ m_scrollSoundClipPlayer = NULL;
+
+ GetEventManager()->RemoveListener( this, EVENT_FE_MENU_SELECT );
+ GetEventManager()->RemoveListener( this, EVENT_FE_MENU_UPORDOWN );
+}
+
+void SoundManager::playStartupAcceptSound()
+{
+ if( m_selectSoundClip->GetState() == IRadSoundClip::Initialized )
+ {
+ m_selectSoundClipPlayer->SetClip( m_selectSoundClip );
+ m_selectSoundClipPlayer->Play();
+ }
+}
+
+void SoundManager::playStartupScrollSound()
+{
+ if( m_scrollSoundClip->GetState() == IRadSoundClip::Initialized )
+ {
+ m_scrollSoundClipPlayer->SetClip( m_scrollSoundClip );
+ m_scrollSoundClipPlayer->Play();
+ }
+} \ No newline at end of file
diff --git a/game/code/sound/soundmanager.h b/game/code/sound/soundmanager.h
new file mode 100644
index 0000000..40f65da
--- /dev/null
+++ b/game/code/sound/soundmanager.h
@@ -0,0 +1,377 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundmanager.h
+//
+// Description: Manager interface that the other game components use to
+// interact with sound.
+//
+// History: 01/06/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef _SOUNDMANAGER_H
+#define _SOUNDMANAGER_H
+
+#ifndef RAD_RELEASE
+#define SOUNDDEBUG_ENABLED
+#endif
+
+
+//
+// Debug display macros
+//
+#ifndef SOUNDDEBUG_ENABLED
+
+#define SOUNDDEBUG_RENDER()
+
+#else
+
+#define SOUNDDEBUG_RENDER() GetSoundManager()->DebugRender()
+
+#endif
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <enums.h>
+
+#include <sound/soundloader.h>
+#include <sound/avatar/avatarsoundplayer.h>
+#include <sound/listener.h>
+#include <sound/nis/nissoundplayer.h>
+
+#include <data/gamedata.h>
+#include <events/eventlistener.h>
+#include <contexts/contextenum.h>
+
+#ifdef RAD_WIN32
+#include <data/config/gameconfig.h>
+#endif
+
+//========================================
+// Forward References
+//========================================
+
+class MusicPlayer;
+class DialogCoordinator;
+class SoundDebugDisplay;
+class MovingSoundManager;
+class Vehicle;
+class Character;
+class SoundEffectPlayer;
+struct IRadSoundClip;
+struct IRadSoundClipPlayer;
+
+//=============================================================================
+//
+// Synopsis: NIS loading and playback completion callbacks
+//
+//=============================================================================
+
+struct NISSoundLoadedCallback
+{
+ virtual void NISSoundLoaded() = 0;
+};
+
+struct NISSoundPlaybackCompleteCallback
+{
+ virtual void NISSoundPlaybackComplete() = 0;
+};
+
+enum SoundMode
+{
+ SOUND_MONO,
+ SOUND_STEREO,
+ SOUND_SURROUND
+};
+
+//=============================================================================
+//
+// Synopsis: SoundManager class declaration
+//
+//=============================================================================
+
+class SoundManager : public EventListener,
+ #ifdef RAD_WIN32
+ public GameConfigHandler, //ziemek: this is ugly..doh
+ #endif
+ public GameDataHandler
+{
+ public:
+ //
+ // Singleton accessors
+ //
+ static SoundManager* CreateInstance( bool muteSound, bool noMusic,
+ bool noEffects, bool noDialogue );
+ static SoundManager* GetInstance();
+ static void DestroyInstance();
+
+ //
+ // EventListener interface
+ //
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ //
+ // All-purpose sound file loading. Coordinates the sound system
+ // with the loading system
+ //
+ void LoadSoundFile( const char* filename, SoundFileHandler* callbackObj );
+
+ //
+ // Update routines.
+ //
+ void Update();
+ // This one is for expensive stuff like positional sound calculations
+ // that we can get away with doing once per frame
+ void UpdateOncePerFrame( unsigned int elapsedTime,
+ ContextEnum context,
+ bool useContext = true,
+ bool isPausedForErrors = false );
+
+ // Prepare to load level sounds
+ void QueueLevelSoundLoads();
+
+ // Load the sounds associated with a car
+ void LoadCarSound( Vehicle* theCar, bool unloadOtherCars );
+
+ // Called when bootup context starts and ends
+ void OnBootupStart();
+ void OnBootupComplete();
+
+ // Called when front end starts and ends
+ void OnFrontEndStart();
+ void OnFrontEndEnd();
+
+ // Called when gameplay starts and ends
+ void OnGameplayStart();
+ void OnGameplayEnd( bool goingToFE );
+
+ // Called when pause menu starts and ends (ends going back to gameplay,
+ // not loading)
+ void OnPauseStart();
+ void OnPauseEnd();
+
+ // Called for pseudo-pause stuff like clothing and phone booth
+ //
+ void OnStoreScreenStart( bool playMusic = true );
+ void OnStoreScreenEnd();
+
+ // Called when we want to kill everything but music (e.g. loading,
+ // minigame standings)
+ void DuckEverythingButMusicBegin( bool playMuzak = false );
+ void DuckEverythingButMusicEnd( bool playMuzak = false );
+
+ // Called when mission briefing screen starts and ends
+ void OnMissionBriefingStart();
+ void OnMissionBriefingEnd();
+
+ // Called when in-game credits start
+ //
+ void DuckForInGameCredits();
+
+ // Call these before and after FMVs
+ void StopForMovie();
+ void ResumeAfterMovie();
+ bool IsStoppedForMovie();
+
+ // Hack! Need to mute gags during conversations
+ void MuteNISPlayers();
+ void UnmuteNISPlayers();
+
+ //
+ // Supersprint
+ //
+ void RestartSupersprintMusic();
+
+ // Surround sound control
+ void SetSoundMode( SoundMode mode );
+ SoundMode GetSoundMode();
+
+ // Function for getting that funky beat. Values from 0.0f to 4.0f, assuming
+ // that Marc doesn't write any waltzes
+ float GetBeatValue();
+
+ // Special case dialog handling
+ static bool IsFoodCharacter( Character* theGuy );
+
+ //
+ // Volume controls. Values range from 0.0f to 1.0f
+ //
+ void SetMasterVolume( float volume );
+ float GetMasterVolume();
+
+ void SetSfxVolume( float volume );
+ float GetSfxVolume();
+
+ void SetCarVolume( float volume );
+ float GetCarVolume();
+
+ void SetMusicVolume( float volume );
+ float GetMusicVolume();
+
+ void SetAmbienceVolume( float volume );
+ float GetAmbienceVolume();
+
+ void SetDialogueVolume( float volume );
+ float GetDialogueVolume();
+
+ float GetCalculatedAmbienceVolume();
+
+ //
+ // Option menu stinger stuff
+ //
+ void PlayCarOptionMenuStinger();
+ void PlayDialogueOptionMenuStinger();
+ void PlayMusicOptionMenuStinger();
+ void PlaySfxOptionMenuStinger();
+
+ //
+ // Ducking stuff
+ //
+ void ResetDucking();
+
+ //
+ // NIS Interface
+ //
+ void LoadNISSound( radKey32 NISSoundID, NISSoundLoadedCallback* callback = NULL );
+ void PlayNISSound( radKey32 NISSoundID, rmt::Box3D* boundingBox, NISSoundPlaybackCompleteCallback* callback = NULL );
+ void StopAndDumpNISSound( radKey32 NISSoundID );
+
+ //
+ // Language interface
+ //
+ void SetDialogueLanguage( Scrooby::XLLanguage language );
+
+ //
+ // Sound debug functions
+ //
+ void DebugRender();
+
+ //
+ // TODO: move these functions, they're not intended for use outside
+ // of the sound system
+ //
+ SoundLoader* GetSoundLoader() { return( m_soundLoader ); }
+ SoundDebugDisplay* GetDebugDisplay() { return( m_debugDisplay ); }
+
+ //
+ // This should NOT be called outside the sound system. Unfortunately,
+ // to keep things clean, what I should do is split the MusicPlayer class
+ // and move a low-level controller into the soundrenderer layer. I don't
+ // have time for this. Things to do for the next round.
+ //
+ void SetMusicVolumeWithoutTuner( float volume );
+ void SetAmbienceVolumeWithoutTuner( float volume );
+
+ // Implements GameDataHandler
+ //
+ virtual void LoadData( const GameDataByte* dataBuffer, unsigned int numBytes );
+ virtual void SaveData( GameDataByte* dataBuffer, unsigned int numBytes );
+ virtual void ResetData();
+
+ #ifdef RAD_WIN32
+ // Implementation of the GameConfigHandler interface
+ virtual const char* GetConfigName() const;
+ virtual int GetNumProperties() const;
+ virtual void LoadDefaults();
+ virtual void LoadConfig( ConfigString& config );
+ virtual void SaveConfig( ConfigString& config );
+ #endif
+
+ DialogCoordinator* m_dialogCoordinator;
+
+ protected:
+ //
+ // Hide the SoundManager constructor and destructor so everyone
+ // is forced to use singleton accessors
+ //
+ SoundManager( bool noSound, bool noMusic, bool noEffects, bool noDialogue );
+ ~SoundManager();
+
+ void initialize();
+
+ private:
+ //Prevent wasteful constructor creation.
+ SoundManager( const SoundManager& original );
+ SoundManager& operator=( const SoundManager& rhs );
+
+ //
+ // Hack!
+ //
+ void prepareStartupSounds();
+ void playStartupAcceptSound();
+ void playStartupScrollSound();
+ void dumpStartupSounds();
+
+ // Pointer to the one and only instance of this singleton.
+ static SoundManager* spInstance;
+
+ struct SoundSettings
+ {
+ float musicVolume;
+ float sfxVolume;
+ float carVolume;
+ float dialogVolume;
+ bool isSurround;
+ };
+
+ // Sound loading subsystem
+ SoundLoader* m_soundLoader;
+
+ // Avatar sound subsystem
+ AvatarSoundPlayer m_avatarSoundPlayer;
+
+ // Music player subsystem
+ MusicPlayer* m_musicPlayer;
+
+ // Sound effect subsystem
+ SoundEffectPlayer* m_soundFXPlayer;
+
+ // Dialog subsystem
+
+
+ // NIS subsystem
+ NISSoundPlayer* m_NISPlayer;
+
+ // RadSound listener update
+ Listener m_listener;
+
+ // AI Vehicle sound subsystem
+ MovingSoundManager* m_movingSoundManager;
+
+ // Sound debug display subsystem
+ SoundDebugDisplay* m_debugDisplay;
+
+ // Mute options
+ bool m_isMuted;
+
+ bool m_noMusic;
+ bool m_noEffects;
+ bool m_noDialogue;
+
+ // Pointer to sound rendering interface
+ Sound::daSoundRenderingManager* m_pSoundRenderMgr;
+
+ // [ps] avoid hammering on pause.
+ bool m_stoppedForMovie;
+
+ //
+ // Hack for stinky pre-script-loading menu sounds
+ //
+ IRadSoundClip* m_selectSoundClip;
+ IRadSoundClip* m_scrollSoundClip;
+
+ IRadSoundClipPlayer* m_selectSoundClipPlayer;
+ IRadSoundClipPlayer* m_scrollSoundClipPlayer;
+
+
+ SoundMode m_soundMode;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline SoundManager* GetSoundManager() { return( SoundManager::GetInstance() ); }
+
+#endif //SOUNDMANAGER_H
+
diff --git a/game/code/sound/soundrenderer/allsoundrenderer.cpp b/game/code/sound/soundrenderer/allsoundrenderer.cpp
new file mode 100644
index 0000000..f2315f3
--- /dev/null
+++ b/game/code/sound/soundrenderer/allsoundrenderer.cpp
@@ -0,0 +1,14 @@
+#include <sound/soundrenderer/dasoundplayer.cpp>
+#include <sound/soundrenderer/fader.cpp>
+#include <sound/soundrenderer/musicsoundplayer.cpp>
+#include <sound/soundrenderer/soundnucleus.cpp>
+#include <sound/soundrenderer/playermanager.cpp>
+#include <sound/soundrenderer/soundallocatedresource.cpp>
+#include <sound/soundrenderer/sounddynaload.cpp>
+#include <sound/soundrenderer/soundrenderingmanager.cpp>
+#include <sound/soundrenderer/soundresource.cpp>
+#include <sound/soundrenderer/soundresourcemanager.cpp>
+#include <sound/soundrenderer/soundtuner.cpp>
+#include <sound/soundrenderer/wireplayers.cpp>
+#include <sound/soundrenderer/wiresystem.cpp>
+#include <sound/soundrenderer/tunerdebugpage.cpp>
diff --git a/game/code/sound/soundrenderer/dasoundgroup.h b/game/code/sound/soundrenderer/dasoundgroup.h
new file mode 100644
index 0000000..884b049
--- /dev/null
+++ b/game/code/sound/soundrenderer/dasoundgroup.h
@@ -0,0 +1,100 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dasoundgroup.hpp
+//
+// Subsystem: Dark Angel - Sound Grouping
+//
+// Description: Contains definitions, enumerations, and interfaces for a
+// Dark Angel sound group.
+//
+// Revisions:
+// + Created October 18, 2001 -- breimer
+//
+//=============================================================================
+
+#ifndef _DASOUNDGROUP_HPP
+#define _DASOUNDGROUP_HPP
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+//=============================================================================
+// Define Owning Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Typedefs and Enumerations
+//=============================================================================
+
+//
+// The list of possible sound groups
+//
+
+enum daSoundGroup {
+ // BASIC WIRING GROUPS
+ CARSOUND,
+ NIS,
+
+ NUM_BASIC_SOUND_GROUPS,
+
+ // SYSTEM WIRING GROUPS
+ // Warning: Do not modify
+ DIALOGUE = NUM_BASIC_SOUND_GROUPS,
+ DIALOGUE_TUNE,
+ DUCKABLE,
+ MASTER,
+ MASTER_TUNE,
+ MUSIC,
+ MUSIC_TUNE,
+ AMBIENCE,
+ AMBIENCE_TUNE,
+ SOUND_EFFECTS,
+ SOUND_EFFECTS_TUNE,
+ OPTIONS_MENU_STINGERS,
+
+ NUM_SOUND_GROUPS
+};
+
+enum DuckSituations
+{
+ DUCK_FULL_FADE,
+
+ DUCK_SIT_PAUSE,
+ DUCK_SIT_MISSION,
+ DUCK_SIT_LETTERBOX,
+ DUCK_SIT_DIALOG,
+ DUCK_SIT_STORE,
+ DUCK_SIT_ONFOOT,
+ DUCK_SIT_MINIGAME,
+ DUCK_SIT_JUST_MUSIC,
+ DUCK_SIT_CREDITS,
+
+ NUM_DUCK_SITUATIONS
+};
+
+enum DuckVolumes
+{
+ DUCK_SFX,
+ DUCK_CAR,
+ DUCK_MUSIC,
+ DUCK_DIALOG,
+ DUCK_AMBIENCE,
+
+ NUM_DUCK_VOLUMES
+};
+
+//
+// A structure for holding a collection of volumes
+//
+struct DuckVolumeSet
+{
+ float duckVolume[NUM_DUCK_VOLUMES];
+};
+
+} // Namespace
+#endif //_DASOUNDGROUP_HPP
+
diff --git a/game/code/sound/soundrenderer/dasoundplayer.cpp b/game/code/sound/soundrenderer/dasoundplayer.cpp
new file mode 100644
index 0000000..0220f47
--- /dev/null
+++ b/game/code/sound/soundrenderer/dasoundplayer.cpp
@@ -0,0 +1,1191 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dasoundplayer.cpp
+//
+// Subsystem: Dark Angel - Sound players
+//
+// Description: Implements the a Dark Angel sound player
+//
+// Revisions:
+// + Created October 16, 2001 -- breimer
+//
+//=============================================================================
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <raddebug.hpp>
+#include <radlinkedclass.hpp>
+
+#include <radsound.hpp>
+#include <radsoundmath.hpp>
+
+#include <sound/soundrenderer/soundsystem.h>
+
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/idasoundresource.h>
+#include <sound/soundrenderer/soundresourcemanager.h>
+#include <sound/soundrenderer/soundplayer.h>
+#include <sound/soundrenderer/idasoundtuner.h>
+#include <sound/soundrenderer/soundnucleus.hpp>
+
+#include <main/commandlineoptions.h>
+
+//=============================================================================
+// Static Variables (outside namespace)
+//=============================================================================
+
+
+//
+// Initialially the player list is empty
+//
+Sound::daSoundPlayerBase* radLinkedClass< Sound::daSoundPlayerBase >::s_pLinkedClassHead = NULL;
+Sound::daSoundPlayerBase* radLinkedClass< Sound::daSoundPlayerBase >::s_pLinkedClassTail = NULL;
+Sound::daSoundClipStreamPlayer* radLinkedClass< Sound::daSoundClipStreamPlayer >::s_pLinkedClassHead = NULL;
+Sound::daSoundClipStreamPlayer* radLinkedClass< Sound::daSoundClipStreamPlayer >::s_pLinkedClassTail = NULL;
+
+//=============================================================================
+// Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Debug Information
+//=============================================================================
+
+//
+// Use this if you want to debug the sound player
+//
+#ifndef FINAL
+#ifndef NDEBUG
+#define DASOUNDPLAYER_DEBUG
+#ifdef DASOUNDPLAYER_DEBUG
+
+// Show sound state changes
+static bool sg_ShowSoundStates = false;
+
+#endif //DASOUNDPLAYER_DEBUG
+#endif //NDEBUG
+#endif //FINAL
+
+//=============================================================================
+// Definitions Macros and Constants
+//=============================================================================
+
+//
+// These are the minimum ranges that the given variables must range
+// over to warrant a randomization calculation
+//
+const float DASOUND_MINFLOATVARYINGRANGE = 0.0001f;
+
+//=============================================================================
+// Local functions
+//=============================================================================
+
+//=============================================================================
+// Class Implementations
+//=============================================================================
+
+//=============================================================================
+// daSoundClipStreamPlayer Implementation
+//=============================================================================
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::daSoundClipStreamPlayer
+//=============================================================================
+// Description: Constructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundClipStreamPlayer::daSoundClipStreamPlayer( )
+ :
+ m_Trim( 1.0f ),
+ m_GroupTrim( 1.0f ),
+ m_FaderGroupTrim( 1.0f ),
+ m_MasterTrim( 1.0f ),
+ m_State( State_DeCued ),
+ m_CueingState( CueingState_Null ),
+ m_CaptureCount( 0 ),
+ m_PauseCount( 0 ),
+ m_pAllocatedResource( NULL ),
+ m_AllocResInstanceID( 0 ),
+ m_pResource( NULL ),
+ m_pStateChangeCallback( NULL ),
+ m_pStateChangeCallbackUserData( NULL ),
+ m_Type( IDaSoundResource::UNKNOWN )
+{
+ m_IsPositional = false;
+ m_pPositionalGroup = radSoundHalPositionalGroupCreate( GetThisAllocator( ) );
+ m_pPositionalGroup->AddRef( );
+
+ m_CurrentTrim = 0.0f;
+ m_StoppingTrim = 1.0f;
+ m_VaryingTrim = 1.0f;
+
+ m_Pitch = 1.0f;
+ m_VaryingPitch = 1.0f;
+ m_CurrentPitch = 1.0f;
+}
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::~daSoundClipStreamPlayer
+//=============================================================================
+// Description: Destructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundClipStreamPlayer::~daSoundClipStreamPlayer( )
+{
+ if ( IDaSoundResource::CLIP == m_Type )
+ {
+ m_ClipInfo.m_pClipPlayer->Stop( );
+ m_ClipInfo.m_pClipPlayer->SetClip( NULL );
+ m_ClipInfo.m_pClipPlayer->Release( );
+ }
+ else
+ {
+ if ( m_StreamInfo.m_pResources != NULL )
+ {
+ Sound::SoundNucleusUnCaptureStreamerResources( m_StreamInfo.m_pResources );
+ m_StreamInfo.m_pResources->m_pStitchedDataSource->SetStitchCallback( NULL, NULL );
+ }
+
+ if ( m_StreamInfo.m_pRsdFileDataSource != NULL )
+ {
+ m_StreamInfo.m_pRsdFileDataSource->Release( );
+ }
+ }
+
+ if ( m_pResource != NULL )
+ {
+ m_pResource->Release( );
+ }
+
+ if ( m_pPositionalGroup )
+ {
+ m_pPositionalGroup->Release( );
+ }
+
+ if ( m_pAllocatedResource )
+ {
+ m_pAllocatedResource->Release( );
+ }
+
+ if ( m_pResource != NULL )
+ {
+ m_pResource->Release( );
+ }
+}
+
+void daSoundClipStreamPlayer::UpdateClip( void )
+{
+ m_ClipInfo.m_pClipPlayer->SetTrim( m_CurrentTrim );
+ m_ClipInfo.m_pClipPlayer->SetPitch( m_CurrentPitch );
+
+ IRadSoundClipPlayer * pCp = m_ClipInfo.m_pClipPlayer;
+ IRadSoundClipPlayer::State state = pCp->GetState( );
+
+ switch( m_State )
+ {
+ case State_DeCued:
+ {
+ rAssert( IRadSoundClipPlayer::NoClip == state );
+ break;
+ }
+ case State_CuedPlay:
+ case State_Cueing:
+ {
+ switch( m_CueingState )
+ {
+ case CueingState_Null:
+ {
+ rAssert( false );
+ break;
+ }
+ case CueingState_Resource:
+ {
+ rAssert( IRadSoundClipPlayer::NoClip == state );
+
+ daSoundFileInstance* pFileInstance = m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID );
+ if ( pFileInstance->GetState( ) == daSoundFileInstance::Loaded )
+ {
+ HookUpAndCuePlayer( );
+ }
+
+ break;
+ }
+ case CueingState_Player:
+ {
+ m_CueingState = CueingState_Cued;
+ break;
+ }
+ case CueingState_Cued:
+ {
+ rAssert( IRadSoundClipPlayer::Stopped == state );
+
+ if ( State_CuedPlay == m_State )
+ {
+ if( 0 == m_PauseCount )
+ {
+ pCp->Play( );
+ }
+
+ m_CaptureCount++;
+ m_State = State_Playing;
+ }
+ else
+ {
+ m_State = State_Cued;
+ }
+
+ if ( m_pStateChangeCallback )
+ {
+ m_pStateChangeCallback->OnSoundReady( m_pStateChangeCallbackUserData );
+ }
+
+ m_CueingState = CueingState_Null;
+
+ break;
+ }
+ }
+ break;
+ }
+ case State_Cued:
+ {
+ switch( state )
+ {
+ case IRadSoundClipPlayer::Stopped:
+ break;
+ case IRadSoundClipPlayer::NoClip:
+ case IRadSoundClipPlayer::Queueing:
+ case IRadSoundClipPlayer::QueuedPlay:
+ case IRadSoundClipPlayer::Playing:
+ default:
+ rAssert( false );
+ }
+ break;
+ }
+ case State_Playing:
+ {
+ if ( m_StoppingTrim <= 1.0f )
+ {
+ m_StoppingTrim += radSoundVolumeChangeThreshold;
+
+ if ( m_StoppingTrim >= 1.0f )
+ {
+ m_StoppingTrim = 1.0f;
+ }
+ }
+ switch( state )
+ {
+ case IRadSoundClipPlayer::NoClip:
+ case IRadSoundClipPlayer::Queueing:
+ case IRadSoundClipPlayer::QueuedPlay:
+ rAssert( false );
+ break;
+ case IRadSoundClipPlayer::Playing:
+ rAssert( 0 == m_PauseCount );
+ break;
+ case IRadSoundClipPlayer::Stopped:
+ if( m_PauseCount == 0 )
+ {
+ m_State = State_Done;
+ UnCapture( );
+
+ if ( m_pStateChangeCallback )
+ {
+ m_pStateChangeCallback->OnSoundDone( m_pStateChangeCallbackUserData );
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+ case State_Stopping:
+ {
+ if ( m_CurrentTrim <= 0.0f )
+ {
+ m_ClipInfo.m_pClipPlayer->Stop( );
+ m_State = State_Cued;
+ m_StoppingTrim = 1.0f;
+ UnCapture( );
+ }
+ else
+ {
+ m_StoppingTrim -= radSoundVolumeChangeThreshold;
+
+ if ( m_StoppingTrim <= 0.0f )
+ {
+ m_StoppingTrim = 0.0f;
+ }
+ }
+
+ break;
+ }
+
+ case State_Done:
+ {
+ rAssert( IRadSoundClipPlayer::NoClip == state );
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ }
+ }
+}
+
+void daSoundClipStreamPlayer::UpdateStream( void )
+{
+ IRadSoundStreamPlayer::State state;
+
+ if ( NULL != m_StreamInfo.m_pResources )
+ {
+ m_StreamInfo.m_pResources->m_pStreamPlayer->SetTrim( m_CurrentTrim );
+ m_StreamInfo.m_pResources->m_pStreamPlayer->SetPitch( m_CurrentPitch );
+ state = m_StreamInfo.m_pResources->m_pStreamPlayer->GetState( );
+ }
+ else
+ {
+ state = IRadSoundStreamPlayer::NoSource;
+ }
+
+ switch( m_State )
+ {
+ case State_DeCued:
+ {
+ rAssert( IRadSoundStreamPlayer::NoSource == state );
+ break;
+ }
+ case State_CuedPlay:
+ case State_Cueing:
+ {
+ switch( m_CueingState )
+ {
+ case CueingState_Resource:
+ {
+ daSoundFileInstance* pFileInstance = m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID );
+
+ if ( pFileInstance->GetState( ) == daSoundFileInstance::Loaded )
+ {
+ if ( NULL == m_StreamInfo.m_pRsdFileDataSource )
+ {
+ m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID )->CreateFileDataSource( & m_StreamInfo.m_pRsdFileDataSource );
+ }
+ else if ( IRadSoundRsdFileDataSource::Initialized == m_StreamInfo.m_pRsdFileDataSource->GetState( ) )
+ {
+ /* rDebugPrintf( "DASOUND: Playing Streamed File: [%s], format: [%d], channels: [%d]\n",
+ pFileInstance->GetFileName( ),
+ m_StreamInfo.m_pRsdFileDataSource->GetFormat( )->GetEncoding( ),
+ m_StreamInfo.m_pRsdFileDataSource->GetFormat( )->GetNumberOfChannels( ) ); */
+
+ HookUpAndCuePlayer( );
+ }
+ }
+
+ break;
+ }
+ case CueingState_Player:
+ {
+ switch( state )
+ {
+ case IRadSoundStreamPlayer::Queueing:
+ {
+ break;
+ }
+ case IRadSoundStreamPlayer::Paused:
+ {
+
+ if ( m_pStateChangeCallback )
+ {
+ m_pStateChangeCallback->OnSoundReady( m_pStateChangeCallbackUserData );
+ }
+
+ m_CueingState = CueingState_Cued;
+ break;
+ }
+ case IRadSoundStreamPlayer::NoSource:
+ case IRadSoundStreamPlayer::Playing:
+ case IRadSoundStreamPlayer::QueuedPlay:
+ default:
+ rAssert( false );
+ }
+
+ break;
+ }
+ case CueingState_Cued:
+ {
+ if ( State_Cueing == m_State )
+ {
+ m_State = State_Cued;
+ }
+ else
+ {
+ if ( 0 == m_PauseCount )
+ {
+ m_StreamInfo.m_pResources->m_pStreamPlayer->Play( );
+ }
+ m_CaptureCount++;
+ m_State = State_Playing;
+ }
+
+ m_CueingState = CueingState_Null;
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ }
+ }
+ break;
+ }
+ case State_Cued:
+ {
+ rAssert( IRadSoundStreamPlayer::Paused == state );
+ break;
+ }
+ case State_Playing:
+ {
+ m_StoppingTrim += radSoundVolumeChangeThreshold;
+
+ if ( m_StoppingTrim >= 1.0f )
+ {
+ m_StoppingTrim = 1.0f;
+ }
+
+ switch( state )
+ {
+ case IRadSoundStreamPlayer::Queueing:
+ rAssert(false);
+ break;
+ case IRadSoundStreamPlayer::Paused:
+ rAssert( 0 < m_PauseCount );
+ break;
+ case IRadSoundStreamPlayer::NoSource:
+ m_State = State_Done;
+ UnCapture( ); // Internal
+ if ( m_pStateChangeCallback )
+ {
+ m_pStateChangeCallback->OnSoundDone( m_pStateChangeCallbackUserData );
+ }
+ break;
+ case IRadSoundStreamPlayer::Playing:
+ rAssert( 0 == m_PauseCount );
+ break;
+ case IRadSoundStreamPlayer::QueuedPlay:
+ default:
+ rAssert( false );
+ }
+
+ break;
+
+ }
+ case State_Stopping:
+ {
+ if ( m_CurrentTrim <= 0.0f )
+ {
+ m_StreamInfo.m_pResources->m_pStreamPlayer->Stop( );
+ m_State = State_Cued;
+ m_StoppingTrim = 1.0f;
+ UnCapture( );
+ }
+ else
+ {
+ m_StoppingTrim -= radSoundVolumeChangeThreshold;
+
+ if ( m_StoppingTrim <= 0.0f )
+ {
+ m_StoppingTrim = 0.0f;
+ }
+ }
+
+ break;
+ }
+ case State_Done:
+ {
+ rAssert( IRadSoundStreamPlayer::NoSource == state );
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ }
+ }
+
+}
+
+void daSoundClipStreamPlayer::ServiceOncePerFrame( void )
+{
+ //
+ // Update the state information. This now requires polling the player.
+ //
+
+ State prevState;
+ CueingState prevCueingState;
+
+ do
+ {
+
+ CalculateCurrentTrim( );
+ CalculateCurrentPitch( );
+
+ prevState = m_State;
+ prevCueingState = m_CueingState;
+
+ switch( m_Type )
+ {
+ case IDaSoundResource::CLIP:
+ UpdateClip( );
+ break;
+ case IDaSoundResource::STREAM:
+ UpdateStream( );
+ break;
+ default:
+ rAssert( 0 );
+ break;
+ }
+ }
+ while( prevState != m_State || prevCueingState != m_CueingState );
+}
+
+void daSoundClipStreamPlayer::InitializeAsClipPlayer( void )
+{
+ m_Type = IDaSoundResource::CLIP;
+
+ m_ClipInfo.m_pClipPlayer = radSoundClipPlayerCreate( GetThisAllocator( ) );
+ m_ClipInfo.m_pClipPlayer->AddRef( );
+
+#ifndef RAD_WIN32
+ // Prepare the player to listen for spacial effects
+ for( unsigned int i = 0; i < ::radSoundHalSystemGet( )->GetNumAuxSends( ); i++ )
+ {
+ m_ClipInfo.m_pClipPlayer->SetAuxMode( i, radSoundAuxMode_PostFader );
+ m_ClipInfo.m_pClipPlayer->SetAuxGain( i, 1.0f );
+ }
+#endif
+}
+
+void daSoundClipStreamPlayer::InitializeAsStreamPlayer( void )
+{
+ m_Type = IDaSoundResource::STREAM;
+
+ m_StreamInfo.m_pResources = NULL;
+ m_StreamInfo.m_pRsdFileDataSource = NULL;
+}
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::CapturePlayer
+//=============================================================================
+// Description: Capture the sound player
+//
+//-----------------------------------------------------------------------------
+
+void daSoundClipStreamPlayer::Capture(
+ IDaSoundResource* pResource,
+ bool isPositional )
+{
+ rAssert( pResource != NULL );
+ rAssert( State_DeCued == m_State );
+
+ m_IsPositional = isPositional;
+
+ ++m_CaptureCount;
+
+ // A bit of a hack, but the resource should be connected only at this time
+ rAssert( m_CaptureCount == 1 );
+
+ rAssert( m_pAllocatedResource == NULL );
+ rAssert( m_pResource == NULL );
+
+ m_pResource = pResource;
+ m_pResource->CaptureResource( );
+
+ //
+ // Get an allocated resource
+ //
+ m_pAllocatedResource = daSoundResourceManager::GetInstance( )->FindAllocatedResource( m_pResource );
+
+ rAssertMsg( m_pAllocatedResource != NULL, "Resource not properly allocated" );
+
+ m_pAllocatedResource->AddRef( );
+
+ // Choose an instance to play...
+ m_AllocResInstanceID = m_pAllocatedResource->ChooseNextInstance( );
+
+ //
+ // Check the type of resource
+ //
+
+ rAssert( m_Type == m_pResource->GetType( ) );
+
+ daSoundFileInstance* pFileInstance = m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID );
+ rAssert( pFileInstance != NULL );
+
+ if( pFileInstance->GetType( ) == IDaSoundResource::UNKNOWN )
+ {
+ rAssert( false); // when does this happen ? -Th
+ m_State = State_DeCued;
+ }
+ else
+ {
+ //
+ // Start initializing
+ //
+ m_State = State_Cueing;
+ m_CueingState = CueingState_Resource;
+ }
+
+ // Reset state.
+
+ m_Pitch = 1.0f;
+ m_Trim = 1.0f;
+
+ CalculateNewVaryingPitch( );
+ CalculateNewVaryingTrim( );
+}
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::IsCaptured
+//=============================================================================
+// Description: Returns true if the player is already captured
+//
+//-----------------------------------------------------------------------------
+
+bool daSoundClipStreamPlayer::IsCaptured( void )
+{
+ return( m_CaptureCount > 0 );
+}
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::ReleasePlayer
+//=============================================================================
+// Description: Releases a player. Automattically detatches any
+// resources when it is no longer captured.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundClipStreamPlayer::UnCapture( void )
+{
+ unsigned int i = 0;
+
+ //
+ // I don't think this assertion is valid anymore. If a player is cued but not
+ // playing, then Stop() will release the resource, but the player isn't
+ // captured yet and this call shouldn't do anything -- DE
+ //
+ //rAssert( IsCaptured( ) );
+ if( m_CaptureCount > 0 )
+ {
+ --m_CaptureCount;
+
+ if( 0 == m_CaptureCount )
+ {
+ rAssert( NULL == m_pStateChangeCallback );
+ rAssert( m_pResource != NULL );
+ rAssert( m_pAllocatedResource->GetResource( ) == m_pResource );
+ rAssert( m_Type == m_pResource->GetType( ) );
+
+ // Detatch the real player
+ switch( m_Type )
+ {
+ case IDaSoundResource::CLIP:
+ {
+ rAssert( m_ClipInfo.m_pClipPlayer != NULL );
+
+ // Detach the clip
+ m_ClipInfo.m_pClipPlayer->Stop( );
+ m_ClipInfo.m_pClipPlayer->SetClip( NULL );
+
+ break;
+ }
+ case IDaSoundResource::STREAM:
+ {
+ if ( m_StreamInfo.m_pResources != NULL )
+ {
+ m_StreamInfo.m_pResources->m_pStitchedDataSource->SetStitchCallback( NULL, NULL );
+
+ // Detach the stream
+ m_StreamInfo.m_pResources->m_pStreamPlayer->Stop( );
+ m_StreamInfo.m_pResources->m_pStreamPlayer->SetDataSource( NULL );
+
+ if ( m_StreamInfo.m_pResources->m_pBufferedDataSource != NULL )
+ {
+ m_StreamInfo.m_pResources->m_pBufferedDataSource->SetInputDataSource( NULL );
+ }
+
+ SoundNucleusUnCaptureStreamerResources( m_StreamInfo.m_pResources );
+ m_StreamInfo.m_pResources = NULL;
+ }
+
+ if ( m_StreamInfo.m_pRsdFileDataSource != NULL )
+ {
+ m_StreamInfo.m_pRsdFileDataSource->Release( );
+ m_StreamInfo.m_pRsdFileDataSource = NULL;
+ }
+
+ break;
+ }
+ default:
+ rAssert( 0 );
+ break;
+ }
+
+ // Release the allocated resource
+ m_pAllocatedResource->Release( );
+ m_pAllocatedResource = NULL;
+ m_pResource->ReleaseResource( );
+ m_pResource = NULL;
+
+ m_State = State_DeCued;
+ m_CueingState = CueingState_Null;
+ }
+ }
+}
+
+//=============================================================================
+// daSoundClipStreamPlayer::ChangeTrim
+//=============================================================================
+// Description: Change the trim setting for a sound group, or for the master
+// volume
+//
+// Parameters: groupName - name of group to change
+// newTrim - trim value
+//
+// Return: void
+//
+//=============================================================================
+void daSoundClipStreamPlayer::ChangeTrim( daSoundGroup groupName, float newTrim )
+{
+ daSoundGroup myGroup = GetSoundGroup();
+
+ if ( myGroup == groupName )
+ {
+ SetGroupTrim(newTrim);
+ }
+ else if ( groupName == MASTER )
+ {
+ SetMasterTrim(newTrim);
+ }
+ else if ( daSoundRenderingManagerGet()->GetTuner()->IsSlaveGroup( myGroup, groupName ) )
+ {
+ SetGroupTrim(newTrim);
+ }
+}
+
+//=============================================================================
+// daSoundClipStreamPlayer::ChangeFaderTrim
+//=============================================================================
+// Description: Change the trim setting for a sound group, or for the master
+// volume
+//
+// Parameters: groupName - name of group to change
+// newTrim - trim value
+//
+// Return: void
+//
+//=============================================================================
+void daSoundClipStreamPlayer::ChangeFaderTrim( daSoundGroup groupName, float newTrim )
+{
+ daSoundGroup myGroup = GetSoundGroup();
+
+ //
+ // ChangeTrim should be used for master volume settings
+ //
+ rAssert( groupName != MASTER );
+
+ if ( myGroup == groupName )
+ {
+ SetFaderGroupTrim(newTrim);
+ }
+ else if ( daSoundRenderingManagerGet()->GetTuner()->IsSlaveGroup( myGroup, groupName ) )
+ {
+ SetFaderGroupTrim(newTrim);
+ }
+}
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::GetSoundGroup
+//=============================================================================
+// Description: Get the sound group that this player belongs to. This is
+// the sound group that its currently active sound is a part of.
+//
+// Returns: Returns the appropriate sound group.
+//
+//-----------------------------------------------------------------------------
+
+daSoundGroup daSoundClipStreamPlayer::GetSoundGroup( void )
+{
+ daSoundGroup soundGroup = Sound::MASTER;
+ if( m_pAllocatedResource != NULL )
+ {
+ rAssert( m_pAllocatedResource->GetResource( ) != NULL );
+ soundGroup = m_pAllocatedResource->GetResource( )->GetSoundGroup( );
+ }
+ return soundGroup;
+}
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::RegisterSoundPlayerStateCallback
+//=============================================================================
+// Description: Register a sound player state callback
+//
+// Notes: Currently, only one callback is supported.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundClipStreamPlayer::RegisterSoundPlayerStateCallback
+(
+ IDaSoundPlayerState* pCallback,
+ void* pUserData
+)
+{
+ rAssertMsg( m_pStateChangeCallback == NULL, "Currently, only one callback is allowed" );
+ m_pStateChangeCallback = pCallback;
+ m_pStateChangeCallbackUserData = pUserData;
+ if( m_pStateChangeCallback != NULL )
+ {
+ m_pStateChangeCallback->AddRef( );
+ }
+}
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::UnregisterSoundPlayerStateCallback
+//=============================================================================
+// Description: Unregister a sound player state callback
+//
+// Notes: Currently, only one callback is supported.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundClipStreamPlayer::UnregisterSoundPlayerStateCallback
+(
+ IDaSoundPlayerState* pCallback,
+ void* pUserData
+)
+{
+ rAssert( pCallback == m_pStateChangeCallback );
+ if( m_pStateChangeCallback != NULL )
+ {
+ m_pStateChangeCallback->Release( );
+ m_pStateChangeCallback = NULL;
+ m_pStateChangeCallbackUserData = NULL;
+ }
+}
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::GetPlaybackTimeInSamples
+//=============================================================================
+// Description: Get the samples that have elapsed since playback started.
+//
+//-----------------------------------------------------------------------------
+
+unsigned int daSoundClipStreamPlayer::GetPlaybackTimeInSamples( void )
+{
+ // Is this synchronized with play / pause?
+ unsigned int elapsedtime = 0;
+ if( IsCaptured( ) )
+ {
+ // Update the positional information
+ IRadSoundPlayer* pPlayer = NULL;
+ switch( m_Type )
+ {
+ case IDaSoundResource::CLIP:
+ {
+ pPlayer = static_cast< IRadSoundPlayer* >
+ (
+ m_ClipInfo.m_pClipPlayer
+ );
+ break;
+ }
+ case IDaSoundResource::STREAM:
+ {
+ pPlayer = static_cast< IRadSoundPlayer* >
+ (
+ m_StreamInfo.m_pResources->m_pStreamPlayer
+ );
+ break;
+ }
+ default:
+ rAssert( 0 );
+ break;
+ }
+ if( pPlayer != NULL )
+ {
+ elapsedtime = pPlayer->GetPlaybackTimeInSamples( );
+ //elapsedtime = ::radSoundHalSystemGet( )->GetReferenceClock( );
+ }
+ }
+
+ // Return the time
+ return( elapsedtime );
+}
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::Play
+//=============================================================================
+// Description: Play the active sound resource.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundClipStreamPlayer::Play( void )
+{
+ daSoundFileInstance* pFileInstance = m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID );
+ rAssert( pFileInstance != NULL );
+
+ #ifndef RAD_RELEASE
+ if ( daSoundFileInstance::Loaded != pFileInstance->GetState( ) )
+ {
+ char fileName[ 256 ];
+ pFileInstance->GetFileName( fileName, 256 );
+
+ rTunePrintf(
+ "\nAUDIO: Controlling code is not paying attention to latency: [%s]\n\n", fileName );
+ }
+ #endif
+
+ switch ( m_State )
+ {
+ case State_Cueing:
+ {
+ m_State = State_CuedPlay;
+ break;
+ }
+ case State_Cued:
+ {
+ m_StoppingTrim = 1.0f;
+
+ // fall through
+ }
+ case State_Stopping:
+ {
+ m_State = State_CuedPlay;
+ m_CueingState = CueingState_Cued;
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ break;
+ }
+ }
+}
+
+
+void daSoundClipStreamPlayer::OnStitch( IRadSoundHalDataSource ** ppHds , unsigned int frameCount, void * pUserData )
+{
+ bool loop = m_pAllocatedResource->GetResource( )->GetLooping( );
+
+ if ( loop || NULL != m_StreamInfo.m_pRsdFileDataSource )
+ {
+ if ( NULL != m_StreamInfo.m_pRsdFileDataSource )
+ {
+ // pass on ref count
+
+ *ppHds = m_StreamInfo.m_pRsdFileDataSource;
+ m_StreamInfo.m_pRsdFileDataSource = NULL;
+ }
+ else
+ {
+ ref< IRadSoundRsdFileDataSource > refFds;
+ m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID )->CreateFileDataSource( & refFds );
+
+ *ppHds = refFds;
+ (*ppHds)->AddRef( );
+ }
+ //rDebugPrintf( "DASOUND: Player: [0x%x] Stitching: [%s]\n", this, refFds->GetName( ) );
+ }
+}
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::Pause
+//=============================================================================
+// Description: Pause the active sound resource.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundClipStreamPlayer::Pause( void )
+{
+ m_PauseCount++;
+
+ if ( State_Playing == m_State )
+ {
+ switch( m_Type )
+ {
+ case IDaSoundResource::CLIP:
+ {
+ m_ClipInfo.m_pClipPlayer->Stop( );
+ break;
+ }
+ case IDaSoundResource::STREAM:
+ {
+ m_StreamInfo.m_pResources->m_pStreamPlayer->Stop( );
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ }
+ }
+ }
+}
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::Continue
+//=============================================================================
+// Description: Continue the active sound resource.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundClipStreamPlayer::Continue( void )
+{
+ rAssert( m_PauseCount > 0 );
+
+ m_PauseCount--;
+
+ if ( 0 == m_PauseCount )
+ {
+ if ( State_Playing == m_State )
+ {
+ switch( m_Type )
+ {
+ case IDaSoundResource::CLIP:
+ {
+ m_ClipInfo.m_pClipPlayer->Play( );
+ break;
+ }
+ case IDaSoundResource::STREAM:
+ {
+ m_StreamInfo.m_pResources->m_pStreamPlayer->Play( );
+ break;
+ }
+ default:
+ {
+ rAssert( false );
+ }
+ }
+ }
+ }
+}
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::UberContinue
+//=============================================================================
+// Description: Continue the active sound resource, no matter how many times
+// it was previously paused
+//
+//-----------------------------------------------------------------------------
+
+void daSoundClipStreamPlayer::UberContinue( void )
+{
+ if( IsPaused() )
+ {
+ if( m_PauseCount > 1 )
+ {
+ m_PauseCount = 1;
+ }
+ Continue();
+ }
+}
+
+//=============================================================================
+// Function: daSoundClipStreamPlayer::Stop
+//=============================================================================
+// Description: Stop the active sound resource.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundClipStreamPlayer::Stop( void )
+{
+ if ( State_CuedPlay == m_State )
+ {
+ m_State = State_Cueing;
+ }
+ else if ( State_Playing == m_State )
+ {
+ m_State = State_Stopping;
+ }
+}
+
+void daSoundClipStreamPlayer::HookUpAndCuePlayer( void )
+{
+ rAssert( State_Cueing == m_State || State_CuedPlay == m_State );
+ rAssert( CueingState_Resource == m_CueingState );
+
+ daSoundFileInstance* pFileInstance = m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID );
+ rAssert( pFileInstance != NULL );
+
+ //
+ // Initialize the group trims
+ //
+ daSoundGroup myGroup = m_pResource->GetSoundGroup();
+ SetMasterTrim( daSoundRenderingManagerGet()->GetTuner()->GetMasterVolume() );
+ SetGroupTrim( daSoundRenderingManagerGet()->GetTuner()->GetGroupTrim( myGroup ) );
+ SetFaderGroupTrim( daSoundRenderingManagerGet()->GetTuner()->GetFaderGroupTrim( myGroup ) );
+
+ switch( m_Type )
+ {
+ case IDaSoundResource::CLIP:
+ {
+ IRadSoundClip * pClip = pFileInstance->GetSoundClip( );
+ rAssert( NULL != pClip );
+
+ // Attach the clip to the player
+ m_ClipInfo.m_pClipPlayer->SetClip( pClip );
+ m_ClipInfo.m_pClipPlayer->SetPositionalGroup( m_IsPositional ? m_pPositionalGroup : NULL );
+
+ break;
+ }
+
+ case IDaSoundResource::STREAM:
+ {
+ m_StreamInfo.m_pResources = SoundNucleusCaptureStreamerResources(
+ m_StreamInfo.m_pRsdFileDataSource->GetFormat( ) );
+
+ rAssert( m_StreamInfo.m_pResources != NULL );
+
+ m_StreamInfo.m_pResources->m_pStreamPlayer->SetPositionalGroup( m_IsPositional ? m_pPositionalGroup : NULL );
+
+ m_StreamInfo.m_pResources->m_pStitchedDataSource->SetStitchCallback( this, NULL );
+ m_StreamInfo.m_pResources->m_pStitchedDataSource->ResetAudioFormat( m_StreamInfo.m_pRsdFileDataSource->GetFormat( ) ); // GCN ADPCM HACK
+ m_StreamInfo.m_pResources->m_pStitchedDataSource->Reset( );
+
+ if ( NULL != m_StreamInfo.m_pResources->m_pBufferedDataSource )
+ {
+ m_StreamInfo.m_pResources->m_pBufferedDataSource->SetInputDataSource( m_StreamInfo.m_pResources->m_pStitchedDataSource );
+ m_StreamInfo.m_pResources->m_pStreamPlayer->SetDataSource( m_StreamInfo.m_pResources->m_pBufferedDataSource );
+ }
+ else
+ {
+ m_StreamInfo.m_pResources->m_pStreamPlayer->SetDataSource(
+ m_StreamInfo.m_pResources->m_pStitchedDataSource );
+ }
+
+
+ break;
+ }
+ default:
+ rAssert( 0 );
+ break;
+ }
+
+ m_CueingState = CueingState_Player;
+}
+
+const void daSoundClipStreamPlayer::GetFileName( char * pBuf, unsigned int max )
+{
+ rAssert( m_pAllocatedResource );
+
+ daSoundFileInstance* pFileInstance = m_pAllocatedResource->GetFileInstance( m_AllocResInstanceID );
+
+ return pFileInstance->GetFileName( pBuf, max );
+
+}
+
+} // Sound Namespace
+ \ No newline at end of file
diff --git a/game/code/sound/soundrenderer/fader.cpp b/game/code/sound/soundrenderer/fader.cpp
new file mode 100644
index 0000000..125fc9e
--- /dev/null
+++ b/game/code/sound/soundrenderer/fader.cpp
@@ -0,0 +1,483 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: fader.cpp
+//
+// Description: Implementation for Fader class which brings down volume on
+// associated multi-input knob. Replaces IRadSoundFade
+// objects from radsound/util.
+//
+// History: 13/08/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+#include <float.h>
+#include <radmath/radmath.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundrenderer/fader.h>
+
+#include <sound/soundrenderer/playermanager.h>
+#include <sound/soundrenderer/idasoundtuner.h>
+
+using namespace Sound;
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+Fader* Fader::s_faderUpdateList = NULL;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Fader::Fader
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Fader::Fader( globalSettings* duckSettings,
+ DuckSituations situation,
+ daSoundPlayerManager& playerMgr,
+ IDaSoundTuner& tuner ) :
+ m_duckSituation( situation ),
+ m_playerManager( playerMgr ),
+ m_tuner( tuner )
+{
+ m_Time = 750;
+ m_In = true;
+ m_State = FadedIn;
+ m_callback = NULL;
+ m_nextUpdatableFader = NULL;
+
+ ReinitializeFader( duckSettings );
+}
+
+//==============================================================================
+// Fader::~Fader
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Fader::~Fader()
+{
+ if( s_faderUpdateList != NULL )
+ {
+ removeFromUpdateList();
+ }
+}
+
+//========================================================================
+// Fader::SetTime
+//========================================================================
+
+void Fader::SetTime( unsigned int milliseconds )
+{
+ m_Time = milliseconds;
+}
+
+//========================================================================
+// Fader::GetTime
+//========================================================================
+
+unsigned int Fader::GetTime( void )
+{
+ return m_Time;
+}
+
+//========================================================================
+// Fader::BroadCast
+//========================================================================
+
+void Fader::BroadCast( void )
+{
+ m_playerManager.PlayerFaderVolumeChange( SOUND_EFFECTS, m_currentVolumes.duckVolume[DUCK_SFX] );
+ m_playerManager.PlayerFaderVolumeChange( CARSOUND, m_currentVolumes.duckVolume[DUCK_CAR] );
+ m_playerManager.PlayerFaderVolumeChange( DIALOGUE, m_currentVolumes.duckVolume[DUCK_DIALOG] );
+ m_playerManager.PlayerFaderVolumeChange( MUSIC, m_currentVolumes.duckVolume[DUCK_MUSIC] );
+ m_playerManager.PlayerFaderVolumeChange( AMBIENCE, m_currentVolumes.duckVolume[DUCK_AMBIENCE] );
+
+ m_tuner.SetFaderGroupTrim( DUCK_SFX, m_currentVolumes.duckVolume[DUCK_SFX] );
+ m_tuner.SetFaderGroupTrim( DUCK_CAR, m_currentVolumes.duckVolume[DUCK_CAR] );
+ m_tuner.SetFaderGroupTrim( DUCK_DIALOG, m_currentVolumes.duckVolume[DUCK_DIALOG] );
+ m_tuner.SetFaderGroupTrim( DUCK_MUSIC, m_currentVolumes.duckVolume[DUCK_MUSIC] );
+ m_tuner.SetFaderGroupTrim( DUCK_AMBIENCE, m_currentVolumes.duckVolume[DUCK_AMBIENCE] );
+}
+
+//========================================================================
+// Fader::Fade
+//========================================================================
+
+void Fader::Fade( bool in, DuckVolumeSet* initialVolumes, DuckVolumeSet* targetVolumes )
+{
+ unsigned int i;
+
+ m_In = in;
+
+ //
+ // Set current values to whatever we're fading from and target values
+ // to whatever we're fading to
+ //
+ if( initialVolumes != NULL )
+ {
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ m_currentVolumes.duckVolume[i] = initialVolumes->duckVolume[i];
+ }
+ }
+ else
+ {
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ if( m_In )
+ {
+ m_currentVolumes.duckVolume[i] = m_globalDuckSettings.duckVolume[i];
+ }
+ else
+ {
+ m_currentVolumes.duckVolume[i] = 1.0f;
+ }
+ }
+ }
+
+ if( targetVolumes != NULL )
+ {
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ m_targetVolumes.duckVolume[i] = targetVolumes->duckVolume[i];
+ }
+ }
+ else
+ {
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ if( m_In )
+ {
+ m_targetVolumes.duckVolume[i] = 1.0f;
+ }
+ else
+ {
+ m_targetVolumes.duckVolume[i] = m_globalDuckSettings.duckVolume[i];
+ }
+ }
+ }
+
+ //
+ // Now calculate the time steps
+ //
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ if ( m_Time == 0 )
+ {
+ // avoid divide by zero
+
+ m_stepValues[i] = FLT_MAX / 10.0f; // Some arbitrarily large number
+ }
+ else
+ {
+ m_stepValues[i] = ( rmt::Fabs( m_currentVolumes.duckVolume[i] - m_targetVolumes.duckVolume[i] ) ) / ( m_Time );
+ }
+ }
+
+ setState( );
+
+ addToUpdateList( );
+}
+
+//========================================================================
+// Fader::RegisterStateCallback
+//========================================================================
+
+void Fader::RegisterStateCallback( FaderStateChangeCallback* callback )
+{
+ //
+ // I'm assuming only one callback is set at a time
+ //
+ rAssert( m_callback == NULL );
+ m_callback = callback;
+}
+
+//========================================================================
+// Fader::UnRegisterStateCallback
+//========================================================================
+
+void Fader::UnRegisterStateCallback( FaderStateChangeCallback* callback )
+{
+ //
+ // Accept the callback as a parameter to test my assumption that
+ // we only set one at a time
+ //
+ rAssert( m_callback == callback );
+
+ m_callback = NULL;
+}
+
+//========================================================================
+// Fader::GetState
+//========================================================================
+
+Fader::State Fader::GetState( void )
+{
+ unsigned int i;
+ float targetValue;
+ State currentState = m_In ? FadedIn : FadedOut;
+
+ //
+ // Test each of the fading volumes. If one of them hasn't hit
+ // the target yet, we're still fading
+ //
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ targetValue = m_In ? 1.0f : m_globalDuckSettings.duckVolume[i];
+
+ if( m_currentVolumes.duckVolume[i] != targetValue )
+ {
+ currentState = m_In ? FadingIn : FadingOut;
+ break;
+ }
+ }
+
+ return( currentState );
+}
+
+//========================================================================
+// Fader::OnTimerDone
+//========================================================================
+
+void Fader::Update( unsigned int elapsed )
+{
+ unsigned int i;
+ float stepValue;
+ bool allTargetsHit = true;
+
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ stepValue = m_stepValues[i] * elapsed; // adjust for game chug.
+
+ if ( stepValue >= radSoundVolumeChangeThreshold )
+ {
+ stepValue = radSoundVolumeChangeThreshold;
+ }
+
+ if ( m_currentVolumes.duckVolume[i] < m_targetVolumes.duckVolume[i] )
+ {
+ m_currentVolumes.duckVolume[i] += stepValue;
+
+ if ( m_currentVolumes.duckVolume[i] >= m_targetVolumes.duckVolume[i] )
+ {
+ m_currentVolumes.duckVolume[i] = m_targetVolumes.duckVolume[i];
+ }
+ else
+ {
+ allTargetsHit = false;
+ }
+ }
+ else if ( m_currentVolumes.duckVolume[i] > m_targetVolumes.duckVolume[i] )
+ {
+ m_currentVolumes.duckVolume[i] -= stepValue;
+
+ if ( m_currentVolumes.duckVolume[i] <= m_targetVolumes.duckVolume[i] )
+ {
+ m_currentVolumes.duckVolume[i] = m_targetVolumes.duckVolume[i];
+ }
+ else
+ {
+ allTargetsHit = false;
+ }
+ }
+
+ }
+
+ BroadCast();
+
+ if ( allTargetsHit )
+ {
+ removeFromUpdateList( );
+
+ setState( );
+ }
+}
+
+//=============================================================================
+// Fader::UpdateAllFaders
+//=============================================================================
+// Description: Run through the fader list and update everything
+//
+// Parameters: elapsedTime - number of elapsed msecs since last call
+//
+// Return: void
+//
+//=============================================================================
+void Fader::UpdateAllFaders( unsigned int elapsedTime )
+{
+ Fader* currFader;
+
+ currFader = s_faderUpdateList;
+ while( currFader != NULL )
+ {
+ currFader->Update( elapsedTime );
+ currFader = currFader->m_nextUpdatableFader;
+ }
+}
+
+//=============================================================================
+// Fader::ReinitializeFader
+//=============================================================================
+// Description: Get the ducking parameters from the global settings object
+//
+// Parameters: ( globalSettings* settingObj )
+//
+// Return: void
+//
+//=============================================================================
+void Fader::ReinitializeFader( globalSettings* settingObj )
+{
+ unsigned int i;
+
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ if( settingObj == NULL )
+ {
+ m_globalDuckSettings.duckVolume[i] = 0.0f;
+ }
+ else
+ {
+ m_globalDuckSettings.duckVolume[i] =
+ settingObj->GetDuckVolume( m_duckSituation, static_cast<Sound::DuckVolumes>(i) );
+ }
+ }
+}
+
+//=============================================================================
+// Fader::Stop
+//=============================================================================
+// Description: Fader is being interrupted, get it out of the update list
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void Fader::Stop()
+{
+ removeFromUpdateList();
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//========================================================================
+// Fader::setState
+//========================================================================
+
+void Fader::setState( void )
+{
+
+ if ( GetState( ) != m_State )
+ {
+ m_State = GetState( );
+
+ if( m_callback != NULL )
+ {
+ m_callback->OnStateChange( m_State );
+ }
+ }
+}
+
+//========================================================================
+// Fader::addToUpdateList
+//========================================================================
+void Fader::addToUpdateList()
+{
+ if( !faderInUpdateList() )
+ {
+ //
+ // Order doesn't matter, add it to the head of the list
+ //
+ m_nextUpdatableFader = s_faderUpdateList;
+ s_faderUpdateList = this;
+ }
+}
+
+//========================================================================
+// Fader::removeFromUpdateList
+//========================================================================
+void Fader::removeFromUpdateList()
+{
+ Fader* currentFader;
+
+ //
+ // Search for position in list. The list is usually one or two faders,
+ // I think, so don't bother with double-linked lists.
+ //
+ if( s_faderUpdateList == this )
+ {
+ s_faderUpdateList = m_nextUpdatableFader;
+ }
+ else
+ {
+ currentFader = s_faderUpdateList;
+ while( ( currentFader != NULL )
+ && ( currentFader->m_nextUpdatableFader != this ) )
+ {
+ currentFader = currentFader->m_nextUpdatableFader;
+ }
+
+ if( currentFader != NULL )
+ {
+ currentFader->m_nextUpdatableFader = m_nextUpdatableFader;
+ }
+ }
+
+ m_nextUpdatableFader = NULL;
+}
+
+//=============================================================================
+// Fader::faderInUpdateList
+//=============================================================================
+// Description: Indicate whether this fader is currently in the update list
+//
+// Parameters: None
+//
+// Return: true if in list, false otherwise
+//
+//=============================================================================
+bool Fader::faderInUpdateList()
+{
+ Fader* currFader;
+
+ currFader = s_faderUpdateList;
+ while( currFader != NULL )
+ {
+ if( currFader == this )
+ {
+ return( true );
+ }
+
+ currFader = currFader->m_nextUpdatableFader;
+ }
+
+ return( false );
+}
diff --git a/game/code/sound/soundrenderer/fader.h b/game/code/sound/soundrenderer/fader.h
new file mode 100644
index 0000000..492080d
--- /dev/null
+++ b/game/code/sound/soundrenderer/fader.h
@@ -0,0 +1,146 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: fader.h
+//
+// Description: Declaration for Fader class which brings down volume on
+// associated multi-input knob. Replaces IRadSoundFade
+// objects from radsound/util.
+//
+// History: 13/08/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef FADER_H
+#define FADER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radobject.hpp>
+
+#include <sound/soundrenderer/dasoundgroup.h>
+#include <sound/tuning/globalsettings.h>
+
+//========================================
+// Forward References
+//========================================
+
+struct FaderStateChangeCallback;
+
+namespace Sound
+{
+ class daSoundPlayerManager;
+ struct IDaSoundTuner;
+}
+
+//=============================================================================
+//
+// Synopsis: Fader
+//
+//=============================================================================
+
+class Fader : public radRefCount
+{
+ public:
+ IMPLEMENT_REFCOUNTED( "Fader" );
+
+ Fader( globalSettings* duckSettings,
+ Sound::DuckSituations situation,
+ Sound::daSoundPlayerManager& playerMgr,
+ Sound::IDaSoundTuner& tuner );
+ ~Fader();
+
+ void Update( unsigned int elapsed );
+ static void UpdateAllFaders( unsigned int elapsedTime );
+
+ void Stop();
+
+ void SetTime( unsigned int milliseconds );
+ unsigned int GetTime( void );
+
+ void Fade( bool in, Sound::DuckVolumeSet* initialVolumes = NULL, Sound::DuckVolumeSet* targetVolumes = NULL );
+
+ float GetCurrentVolume( Sound::DuckVolumes volumeKnob ) { return( m_currentVolumes.duckVolume[volumeKnob] ); }
+ float GetTargetSettings( Sound::DuckVolumes volumeKnob ) { return( m_globalDuckSettings.duckVolume[volumeKnob] ); }
+
+ enum State { FadedIn, FadedOut, FadingIn, FadingOut };
+
+ void RegisterStateCallback( FaderStateChangeCallback* callback );
+ void UnRegisterStateCallback( FaderStateChangeCallback* callback );
+
+ State GetState( void );
+
+ void BroadCast( void );
+
+ Sound::DuckSituations GetSituation( void ) { return( m_duckSituation ); }
+
+ void ReinitializeFader( globalSettings* settingObj );
+
+ private:
+ //Prevent wasteful constructor creation.
+ Fader();
+ Fader( const Fader& original );
+ Fader& operator=( const Fader& rhs );
+
+ void setState();
+
+ //
+ // Add and remove fader from the update list. We should only update
+ // the fader if it's not on its target value yet.
+ //
+ void addToUpdateList();
+ void removeFromUpdateList();
+ bool faderInUpdateList();
+
+ unsigned int m_Time;
+ bool m_In;
+ State m_State;
+
+ Sound::DuckVolumeSet m_currentVolumes;
+ float m_stepValues[Sound::NUM_DUCK_VOLUMES];
+ Sound::DuckVolumeSet m_targetVolumes;
+ Sound::DuckVolumeSet m_globalDuckSettings;
+
+ Sound::DuckSituations m_duckSituation;
+
+ //
+ // Callback object for state change notification. Was a list of objects
+ // in radSoundFade
+ //
+ FaderStateChangeCallback* m_callback;
+
+ //
+ // Static fader list, used for updates
+ //
+ static Fader* s_faderUpdateList;
+
+ //
+ // Pointer used to hold place in update list
+ //
+ Fader* m_nextUpdatableFader;
+
+ //
+ // Player manager, used to actually do the fading
+ //
+ Sound::daSoundPlayerManager& m_playerManager;
+
+ //
+ // Tuner, stores the results
+ //
+ Sound::IDaSoundTuner& m_tuner;
+};
+
+//=============================================================================
+//
+// Synopsis: FaderStateChangeCallback
+//
+//=============================================================================
+struct FaderStateChangeCallback
+{
+ virtual void OnStateChange( Fader::State newState );
+};
+
+
+#endif // FADER_H
+
diff --git a/game/code/sound/soundrenderer/idasoundresource.h b/game/code/sound/soundrenderer/idasoundresource.h
new file mode 100644
index 0000000..f6564db
--- /dev/null
+++ b/game/code/sound/soundrenderer/idasoundresource.h
@@ -0,0 +1,172 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: idasoundresource.hpp
+//
+// Subsystem: Dark Angel - Sound resources
+//
+// Description: Defines the interface for a sound resource
+//
+// WARNING: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// IF THIS FILE IS MODIFIED THE TYPE INFO FILE MUST BE REGENERATED
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+//
+// Revisions:
+// + Created October 3, 2001 -- aking
+//
+//=============================================================================
+
+#ifndef _IDASOUNDRESOURCE_HPP
+#define _IDASOUNDRESOURCE_HPP
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+
+#include <sound/soundrenderer/dasoundgroup.h>
+
+//=============================================================================
+// Definitions and macros
+//=============================================================================
+
+//
+// This is the maximum number of file variations that can exist in a resource.
+//
+#define DASound_MaxNumSoundResourceFiles 7
+
+//=============================================================================
+// Prototypes
+//=============================================================================
+
+struct IDaSoundResource;
+
+//=============================================================================
+// Typedefs and enumerations
+//=============================================================================
+
+//
+// A procedure used to modify a sound resource
+//
+typedef void daSoundResourceProc( IDaSoundResource* pRes, void* pUserData );
+
+//=============================================================================
+// Interface Definitions
+//=============================================================================
+
+//
+// IDaSoundResourceData contains sound resource data.
+//
+struct IDaSoundResourceData : public IRefCount
+{
+ // SCRIPTED FEATURES THAT CAN BE TUNED IN REAL TIME
+
+ //
+ // Add files to the resource
+ //
+ virtual void AddFilename
+ (
+ const char* newFileName,
+ float trim
+ ) = 0;
+
+ //
+ // Set the pitch variation
+ //
+ virtual void SetPitchRange
+ (
+ float minPitch,
+ float maxPitch
+ ) = 0;
+
+ //
+ // Set the trim variation
+ //
+ virtual void SetTrimRange
+ (
+ float minTrim,
+ float maxTrim
+ ) = 0;
+
+ //
+ // Set the trim to one value
+ //
+ virtual void SetTrim( float trim ) = 0;
+
+ // SCRIPTED FEATURES THAT CAN NOT BE TUNED IN REAL TIME
+
+ // Is this a streaming sound resource?
+ virtual void SetStreaming( bool streaming ) = 0;
+
+ // Is this a looping sound resource?
+ virtual void SetLooping( bool looping ) = 0;
+
+ // SCRIPTED FEATURES AVAILABLE ONLY FOR TUNERS
+
+ // Play the resource
+ virtual void Play( void ) = 0;
+};
+
+//
+// IDaSoundResource is based on an IDaSoundResourceData. It adds functionality
+// to get parameters, and to capture/release resources.
+//
+struct IDaSoundResource : public IDaSoundResourceData
+{
+ // A resource file description
+ struct daSoundResourceFileDesc
+ {
+ char* m_Filename;
+ float m_Trim;
+ int m_TableIndex;
+ };
+
+ //
+ // Monitor files in the resource
+ //
+ virtual unsigned int GetNumFiles( void ) = 0;
+
+ virtual void GetFileNameAt( unsigned int index, char* buffer, unsigned int max ) = 0;
+ virtual void GetFileKeyAt( unsigned int index, char* buffer, unsigned int max ) = 0;
+ //
+ // Get parameters set in IDaSoundResourceData
+ //
+ virtual void GetPitchRange
+ (
+ float* pMinPitch,
+ float* pMaxPitch
+ ) = 0;
+ virtual void GetTrimRange
+ (
+ float* pMinTrim,
+ float* pMaxTrim
+ ) = 0;
+ virtual bool GetLooping( void ) = 0;
+
+ //
+ // Find out what kind of resource this is
+ //
+ enum Type {
+ UNKNOWN,
+ CLIP,
+ STREAM
+ };
+ virtual Type GetType( void ) = 0;
+
+ //
+ // Modify the sound group that this resource belongs to
+ //
+ virtual void SetSoundGroup( Sound::daSoundGroup soundGroup ) = 0;
+ virtual Sound::daSoundGroup GetSoundGroup( void ) = 0;
+
+ //
+ // Capture and release the resource
+ //
+ virtual void CaptureResource( void ) = 0;
+ virtual bool IsCaptured( void ) = 0;
+ virtual void ReleaseResource( void ) = 0;
+};
+
+//}; //Sound Namespace
+#endif // _IDASOUNDRESOURCE_HPP
diff --git a/game/code/sound/soundrenderer/idasoundtuner.h b/game/code/sound/soundrenderer/idasoundtuner.h
new file mode 100644
index 0000000..c537af2
--- /dev/null
+++ b/game/code/sound/soundrenderer/idasoundtuner.h
@@ -0,0 +1,205 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: idasoundtuner.hpp
+//
+// Subsystem: Dark Angel - Sound Tuner System
+//
+// Description: Description of the DA sound tuner interface
+//
+// Revisions:
+// + Created October 4, 2001 -- breimer
+//
+//=============================================================================
+
+#ifndef _IDASOUNDTUNER_HPP
+#define _IDASOUNDTUNER_HPP
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+
+#include <sound/soundrenderer/dasoundgroup.h>
+#include <sound/soundrenderer/soundsystem.h>
+
+#include <sound/tuning/globalsettings.h>
+
+//=============================================================================
+// Global namespace forward declarations
+//=============================================================================
+
+struct IRadSoundMultiInputKnob;
+class Fader;
+
+//=============================================================================
+// Define Owning Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Prototypes
+//=============================================================================
+
+struct IDaSoundWiring;
+struct IDaSoundTuner;
+struct IDaSoundFadeState;
+
+//=============================================================================
+// Interfaces
+//=============================================================================
+
+//
+// Wire together various sound groups that take the form of multi-input
+// knobs.
+//
+struct IDaSoundWiring : public IRefCount
+{
+ //
+ // Wire a sound group to a path
+ //
+ virtual void WirePath
+ (
+ daSoundGroup soundGroup,
+ const char* path
+ ) = 0;
+
+ //
+ // Initialize the sound groups
+ //
+ virtual void WireGroup( daSoundGroup slaveGroup, daSoundGroup masterGroup ) = 0;
+};
+
+//
+// The sound tuner is responsible for managing global settings and various
+// "wirings" of the sound system. It allows the generation of tuner instances
+// for controlling various game parameters, and it manages common global
+// sound settings (like ducking, master volume, pause/continue/cancel,
+// mono/stereo).
+//
+struct IDaSoundTuner : public IDaSoundWiring
+{
+ //
+ // Initialize and perform all wiring. This will lock down the resources
+ // if they have not been locked already.
+ //
+ virtual void Initialize( void ) = 0;
+
+ //
+ // Create faders after we've got scripts to latch them to
+ //
+ virtual void PostScriptLoadInitialize() = 0;
+
+ //
+ // Service the tuner
+ //
+ virtual void ServiceOncePerFrame( unsigned int elapsedTime ) = 0;
+
+ //
+ // Modify the sound output mode
+ //
+ enum SoundOutputMode
+ {
+ MONO,
+ STEREO,
+ SURROUND
+ };
+ virtual void SetSoundOutputMode
+ (
+ SoundOutputMode outputMode
+ ) = 0;
+ virtual SoundOutputMode GetSoundOutputMode( void ) = 0;
+
+ // SPECIALIZED SOUND SETTINGS FOR COMMON CONTROL
+
+ //
+ // Duck sounds
+ //
+ virtual void ActivateDuck( IDaSoundFadeState* pObject,
+ void* pUserData,
+ bool fadeIn ) = 0;
+
+ virtual void ActivateSituationalDuck( IDaSoundFadeState* pObject,
+ DuckSituations situation,
+ void* pUserData,
+ bool fadeIn ) = 0;
+ virtual void ResetDuck() = 0;
+
+ //
+ // Common volume controls
+ //
+ virtual void SetMasterVolume( daTrimValue volume ) = 0;
+ virtual daTrimValue GetMasterVolume( void ) = 0;
+
+ virtual void SetDialogueVolume( daTrimValue volume ) = 0;
+ virtual daTrimValue GetDialogueVolume( void ) = 0;
+
+ virtual void SetMusicVolume( daTrimValue volume ) = 0;
+ virtual daTrimValue GetMusicVolume( void ) = 0;
+
+ virtual void SetAmbienceVolume( daTrimValue volume ) = 0;
+ virtual daTrimValue GetAmbienceVolume( void ) = 0;
+
+ virtual void SetSfxVolume( daTrimValue volume ) = 0;
+ virtual daTrimValue GetSfxVolume( void ) = 0;
+
+ virtual void SetCarVolume( daTrimValue volume ) = 0;
+ virtual daTrimValue GetCarVolume( void ) = 0;
+
+ virtual daTrimValue GetGroupTrim( daSoundGroup group ) = 0;
+ virtual daTrimValue GetFaderGroupTrim( daSoundGroup group ) = 0;
+
+ virtual void MuteNIS() = 0;
+ virtual void UnmuteNIS() = 0;
+
+ virtual void SetFaderGroupTrim( Sound::DuckVolumes group, daTrimValue trim ) = 0;
+
+ // GENERAL SOUND SETTINGS
+
+ //
+ // Fade a sound group
+ //
+ virtual void FadeSounds( IDaSoundFadeState* pObject,
+ void* pUserData,
+ Fader* pFader,
+ bool fadeIn,
+ DuckVolumeSet* initialVolumes = NULL ) = 0;
+
+
+ //
+ // Sound group info
+ //
+ virtual bool IsSlaveGroup( daSoundGroup slave, daSoundGroup master ) = 0;
+
+};
+
+//=============================================================================
+// Public functions
+//=============================================================================
+
+//
+// Do the wiring of the sound system
+//
+void daSoundTunerWireSystem
+(
+ IDaSoundWiring* pWiring
+);
+
+//=============================================================================
+// Factory functions
+//=============================================================================
+
+//
+// Create the tuner manager
+//
+void daSoundTunerCreate
+(
+ IDaSoundTuner** ppTuner,
+ radMemoryAllocator allocator
+);
+
+} // Sound Namespace
+#endif //_IDASOUNDTUNER_HPP
+
diff --git a/game/code/sound/soundrenderer/musicsoundplayer.cpp b/game/code/sound/soundrenderer/musicsoundplayer.cpp
new file mode 100644
index 0000000..365b8ef
--- /dev/null
+++ b/game/code/sound/soundrenderer/musicsoundplayer.cpp
@@ -0,0 +1,300 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: musicsoundplayer.cpp
+//
+// Subsystem: Dark Angel - Sound players
+//
+// Description: Implements the a Dark Angel sound player
+//
+// Revisions:
+// + Created October 16, 2001 -- breimer
+//
+//=============================================================================
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <sound/soundrenderer/musicsoundplayer.h>
+
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/idasoundtuner.h>
+
+#include <sound/soundmanager.h>
+
+//=============================================================================
+// Static Variables (outside namespace)
+//=============================================================================
+
+//=============================================================================
+// Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Debug Information
+//=============================================================================
+
+//=============================================================================
+// Definitions Macros and Constants
+//=============================================================================
+
+//=============================================================================
+// Local functions
+//=============================================================================
+
+//=============================================================================
+// Class Implementations
+//=============================================================================
+
+//=============================================================================
+// MusicSoundPlayer Implementation
+//=============================================================================
+
+//=============================================================================
+// Function: MusicSoundPlayer::MusicSoundPlayer
+//=============================================================================
+// Description: Constructor
+//
+//-----------------------------------------------------------------------------
+
+MusicSoundPlayer::MusicSoundPlayer( )
+ :
+ m_isMusic( true ),
+ m_PlayerTrim( 1.0f ),
+ m_ResourceTrim( 1.0f ),
+ m_ExternalTrim( 1.0f ),
+ m_GroupTrim( 1.0f ),
+ m_FaderGroupTrim( 1.0f ),
+ m_MasterTrim( 1.0f )
+{
+}
+
+//=============================================================================
+// Function: MusicSoundPlayer::~MusicSoundPlayer
+//=============================================================================
+// Description: Destructor
+//
+//-----------------------------------------------------------------------------
+
+MusicSoundPlayer::~MusicSoundPlayer( )
+{
+}
+
+//=============================================================================
+// Function: MusicSoundPlayer::Initialize
+//=============================================================================
+// Description: Initialize the sound player to determine whether it will
+// set the trim for music or ambience
+//
+//-----------------------------------------------------------------------------
+void MusicSoundPlayer::Initialize( bool isMusic )
+{
+ m_isMusic = isMusic;
+
+ //
+ // Set the group trim
+ //
+ if( m_isMusic )
+ {
+ SetGroupTrim( daSoundRenderingManagerGet()->GetTuner()->GetGroupTrim( Sound::MUSIC ) );
+ }
+ else
+ {
+ SetGroupTrim( daSoundRenderingManagerGet()->GetTuner()->GetGroupTrim( Sound::AMBIENCE ) );
+ }
+}
+
+//=============================================================================
+// MusicSoundPlayer::GetSoundGroup
+//=============================================================================
+// Description: Unlike regular sound players, our group is determined by
+// the music/ambience specification from initialization, not
+// by an associated sound resource
+//
+// Parameters: None
+//
+// Return: sound group (either MUSIC or AMBIENCE)
+//
+//=============================================================================
+daSoundGroup MusicSoundPlayer::GetSoundGroup()
+{
+ if( m_isMusic )
+ {
+ return( Sound::MUSIC );
+ }
+ else
+ {
+ return( Sound::AMBIENCE );
+ }
+}
+
+//=============================================================================
+// MusicSoundPlayer::SetExternalTrim
+//=============================================================================
+// Description: Set arbitrary player trim. May not be needed.
+//
+// Parameters: newTrim - new trim value
+//
+// Return: void
+//
+//=============================================================================
+void MusicSoundPlayer::SetExternalTrim( float newTrim )
+{
+ m_ExternalTrim = newTrim;
+ m_PlayerTrim = m_ResourceTrim * m_ExternalTrim * m_GroupTrim * m_FaderGroupTrim * m_MasterTrim;
+
+ ResetMusicTrim();
+}
+
+//=============================================================================
+// MusicSoundPlayer::SetGroupTrim
+//=============================================================================
+// Description: Set sound group trim
+//
+// Parameters: newTrim - new trim setting
+//
+// Return: void
+//
+//=============================================================================
+void MusicSoundPlayer::SetGroupTrim( float newTrim )
+{
+ m_GroupTrim = newTrim;
+ m_PlayerTrim = m_ResourceTrim * m_ExternalTrim * m_GroupTrim * m_FaderGroupTrim * m_MasterTrim;
+
+ ResetMusicTrim();
+}
+
+//=============================================================================
+// MusicSoundPlayer::SetFaderGroupTrim
+//=============================================================================
+// Description: Set sound group fader trim
+//
+// Parameters: newTrim - new trim setting
+//
+// Return: void
+//
+//=============================================================================
+void MusicSoundPlayer::SetFaderGroupTrim( float newTrim )
+{
+ m_FaderGroupTrim = newTrim;
+ m_PlayerTrim = m_ResourceTrim * m_ExternalTrim * m_GroupTrim * m_FaderGroupTrim * m_MasterTrim;
+
+ ResetMusicTrim();
+}
+
+//=============================================================================
+// MusicSoundPlayer::SetMasterTrim
+//=============================================================================
+// Description: Set master trim. Duh.
+//
+// Parameters: newTrim - new trim setting
+//
+// Return: void
+//
+//=============================================================================
+void MusicSoundPlayer::SetMasterTrim( float newTrim )
+{
+ m_MasterTrim = newTrim;
+ m_PlayerTrim = m_ResourceTrim * m_ExternalTrim * m_GroupTrim * m_FaderGroupTrim * m_MasterTrim;
+
+ ResetMusicTrim();
+}
+
+//
+// TODO: revisit trim functions below. They're identical to the one in the base
+// class, should be virtual.
+//
+
+//=============================================================================
+// MusicSoundPlayer::ChangeTrim
+//=============================================================================
+// Description: Change the trim setting for a sound group, or for the master
+// volume
+//
+// Parameters: groupName - name of group to change
+// newTrim - trim value
+//
+// Return: void
+//
+//=============================================================================
+void MusicSoundPlayer::ChangeTrim( daSoundGroup groupName, float newTrim )
+{
+ daSoundGroup myGroup = GetSoundGroup();
+
+ if ( myGroup == groupName )
+ {
+ SetGroupTrim(newTrim);
+ }
+ else if ( groupName == MASTER )
+ {
+ SetMasterTrim(newTrim);
+ }
+ else if ( daSoundRenderingManagerGet()->GetTuner()->IsSlaveGroup( myGroup, groupName ) )
+ {
+ SetGroupTrim(newTrim);
+ }
+}
+
+//=============================================================================
+// MusicSoundPlayer::ChangeFaderTrim
+//=============================================================================
+// Description: Change the trim setting for a sound group, or for the master
+// volume
+//
+// Parameters: groupName - name of group to change
+// newTrim - trim value
+//
+// Return: void
+//
+//=============================================================================
+void MusicSoundPlayer::ChangeFaderTrim( daSoundGroup groupName, float newTrim )
+{
+ daSoundGroup myGroup = GetSoundGroup();
+
+ //
+ // Shouldn't change master volume here, use ChangeTrim
+ //
+ rAssert( groupName != MASTER );
+
+ if ( myGroup == groupName )
+ {
+ SetFaderGroupTrim(newTrim);
+ }
+ else if ( daSoundRenderingManagerGet()->GetTuner()->IsSlaveGroup( myGroup, groupName ) )
+ {
+ SetFaderGroupTrim(newTrim);
+ }
+}
+
+
+//=============================================================================
+// Private functions
+//=============================================================================
+
+//=============================================================================
+// MusicSoundPlayer::resetMusicTrim
+//=============================================================================
+// Description: Update the music player with the given trim setting
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void MusicSoundPlayer::ResetMusicTrim()
+{
+ if( m_isMusic )
+ {
+ GetSoundManager()->SetMusicVolumeWithoutTuner( m_PlayerTrim );
+ }
+ else
+ {
+ GetSoundManager()->SetAmbienceVolumeWithoutTuner( m_PlayerTrim );
+ }
+}
+
+} // Sound Namespace
+
diff --git a/game/code/sound/soundrenderer/musicsoundplayer.h b/game/code/sound/soundrenderer/musicsoundplayer.h
new file mode 100644
index 0000000..15cedb3
--- /dev/null
+++ b/game/code/sound/soundrenderer/musicsoundplayer.h
@@ -0,0 +1,112 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: musicsoundplayer.hpp
+//
+// Subsystem: Dark Angel - Sound players
+//
+// Description: Used to hack in volume control for RadMusic, even though
+// it doesn't go through the DA system
+//
+// Revisions:
+// + Hacked 5 Mar 03 -- Esan
+//
+//=============================================================================
+
+#ifndef _MUSICSOUNDPLAYER_HPP
+#define _MUSICSOUNDPLAYER_HPP
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <sound/soundrenderer/soundplayer.h>
+
+//=============================================================================
+// Define Owning Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Prototypes
+//=============================================================================
+
+//=============================================================================
+// Forward declarations
+//=============================================================================
+
+//=============================================================================
+// Typdefs and enumerations
+//=============================================================================
+
+//=============================================================================
+// Class Declarations
+//=============================================================================
+
+//
+// This contains a DA player instance.
+//
+class MusicSoundPlayer : public daSoundPlayerBase
+{
+public:
+ //
+ // Constructor and destructor
+ //
+ MusicSoundPlayer ( );
+ virtual ~MusicSoundPlayer ( );
+
+ void Initialize ( bool isMusic );
+
+ // Suspend the player if it should be playing, but no one can hear it
+ void Suspend ( void );
+
+ void SetExternalTrim( float newTrim );
+ void SetGroupTrim( float newTrim );
+ void SetFaderGroupTrim( float newTrim );
+ void SetMasterTrim( float newTrim );
+
+ daSoundGroup GetSoundGroup ( void );
+
+ unsigned int GetPlaybackTimeInSamples ( void );
+
+ // daSoundPlayerBase
+
+ virtual void ServiceOncePerFrame( void ) { }
+ virtual bool IsCaptured ( void ) { return true; }
+ virtual void Pause ( void ) { }
+ virtual bool IsPaused( void ) { return false; }
+ virtual void Continue ( void ) { }
+ virtual void UberContinue( void ) { }
+ virtual void Stop ( void ) { }
+ virtual void SetPitch( float pitch ) { }
+
+ virtual void ChangeTrim( daSoundGroup groupName, float newTrim );
+ virtual void ChangeFaderTrim( daSoundGroup groupName, float newTrim );
+
+
+private:
+ void ResetMusicTrim( );
+
+ bool m_isMusic;
+
+ //
+ // Trim values
+ //
+ float m_PlayerTrim;
+ float m_ResourceTrim;
+ float m_ExternalTrim;
+ float m_GroupTrim;
+ float m_FaderGroupTrim;
+ float m_MasterTrim;
+
+};
+
+
+//=============================================================================
+// Public Functions
+//=============================================================================
+
+} // Sound Namespace
+#endif //_MUSICSOUNDPLAYER_HPP
+
diff --git a/game/code/sound/soundrenderer/playermanager.cpp b/game/code/sound/soundrenderer/playermanager.cpp
new file mode 100644
index 0000000..9a92b77
--- /dev/null
+++ b/game/code/sound/soundrenderer/playermanager.cpp
@@ -0,0 +1,989 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: playermagaer.cpp
+//
+// Subsystem: Dark Angel - Player Manager System
+//
+// Description: Implementation of the DA sound player manager
+//
+// Revisions:
+// + Created October 16, 2001 -- breimer
+//
+//=============================================================================
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <radobjectlist.hpp>
+#include <raddebug.hpp>
+#include <radlinkedclass.hpp>
+#include <radnamespace.hpp>
+#include <radsound_hal.hpp>
+
+#include <memory/srrmemory.h>
+
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundrenderer/dasoundgroup.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/playermanager.h>
+#include <sound/soundrenderer/soundplayer.h>
+#include <sound/soundrenderer/musicsoundplayer.h>
+#include <sound/soundrenderer/idasoundtuner.h>
+#include <sound/soundrenderer/fader.h>
+#include <sound/soundrenderer/soundconstants.h>
+#include <sound/soundrenderer/soundnucleus.hpp>
+
+#include <pddi/pddi.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/p3dtypes.hpp>
+
+#include <radobjectbtree.hpp>
+
+
+//=============================================================================
+// Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Debug Information
+//=============================================================================
+
+daSoundPlayerManager * daSoundPlayerManager::s_pInstance = 0;
+
+//=============================================================================
+// Class Implementations
+//=============================================================================
+
+//=============================================================================
+// daSoundAsyncFadeCallback Implementation
+//=============================================================================
+
+//=============================================================================
+// Function: daSoundAsyncFadeCallback::daSoundAsyncFadeCallback
+//=============================================================================
+// Description: Constructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundAsyncFadeCallback::daSoundAsyncFadeCallback( )
+:
+radObject( ),
+m_Action( 0 ),
+m_pPlayerManager( NULL ),
+m_pCallback( NULL ),
+m_pUserData( NULL )
+{
+
+}
+
+//=============================================================================
+// Function: daSoundAsyncFadeCallback::~daSoundAsyncFadeCallback
+//=============================================================================
+// Description: Destructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundAsyncFadeCallback::~daSoundAsyncFadeCallback( )
+{
+
+ if( m_pCallback != NULL )
+ {
+ m_pCallback->Release( );
+ }
+ if( m_pPlayerManager != NULL )
+ {
+ m_pPlayerManager->Release( );
+ }
+}
+
+//=============================================================================
+// Function: daSoundAsyncFadeCallback::SetPlayerManager
+//=============================================================================
+// Description: Set the player manager
+//
+//-----------------------------------------------------------------------------
+
+void daSoundAsyncFadeCallback::SetPlayerManager
+(
+ daSoundPlayerManager* pPlayerManager
+)
+{
+ rAssert( m_pPlayerManager == NULL );
+ rAssert( pPlayerManager != NULL );
+
+ m_pPlayerManager = pPlayerManager;
+ m_pPlayerManager->AddRef( );
+}
+
+//=============================================================================
+// Function: daSoundAsyncFadeCallback::GetPlayerManager
+//=============================================================================
+// Description: Get the player manager
+//
+//-----------------------------------------------------------------------------
+
+daSoundPlayerManager* daSoundAsyncFadeCallback::GetPlayerManager( void )
+{
+ return m_pPlayerManager;
+}
+
+//=============================================================================
+// Function: daSoundAsyncFadeCallback::SetCallback
+//=============================================================================
+// Description: Set the fade callback
+//
+//-----------------------------------------------------------------------------
+
+void daSoundAsyncFadeCallback::SetCallback
+(
+ IDaSoundFadeState* pCallback,
+ void* pUserData
+)
+{
+ rAssert( m_pCallback == NULL );
+ m_pCallback = pCallback;
+ m_pUserData = pUserData;
+
+ if( m_pCallback != NULL )
+ {
+ m_pCallback->AddRef( );
+ }
+}
+
+//=============================================================================
+// Function: daSoundAsyncFadeCallback::GetCallback
+//=============================================================================
+// Description: Get the fade callback
+//
+//-----------------------------------------------------------------------------
+
+void daSoundAsyncFadeCallback::GetCallback( IDaSoundFadeState** ppCallback, void** ppUserData )
+{
+ rAssert( ppCallback != NULL );
+ rAssert( ppUserData != NULL );
+
+ *ppCallback = m_pCallback;
+ *ppUserData = m_pUserData;
+}
+
+
+//=============================================================================
+// daSoundPlayerManager Implementation
+//=============================================================================
+
+//=============================================================================
+// Function: daSoundPlayerManager::daSoundPlayerManager
+//=============================================================================
+// Description: Constructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundClipStreamPlayer * gClipPlayerArray[ SOUND_NUM_CLIP_PLAYERS ];
+daSoundClipStreamPlayer * gStreamPlayerArray [ SOUND_NUM_STREAM_PLAYERS ];
+
+daSoundPlayerManager::daSoundPlayerManager( )
+ :
+ radRefCount( 0 )
+{
+ m_pIngameFadeIn = NULL;
+ m_pIngameFadeOut = NULL;
+ m_pMusicPlayer = NULL;
+ m_pAmbiencePlayer = NULL;
+ m_Initialized = false;
+
+ s_pInstance = this;
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::~daSoundPlayerManager
+//=============================================================================
+// Description: Destructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundPlayerManager::~daSoundPlayerManager( )
+{
+ s_pInstance = NULL;
+ // Release our faders
+ if( m_pIngameFadeOut != NULL )
+ {
+ m_pIngameFadeOut->Release();
+ m_pIngameFadeOut = NULL;
+ }
+ if( m_pIngameFadeIn != NULL )
+ {
+ m_pIngameFadeIn->Release();
+ m_pIngameFadeIn = NULL;
+ }
+
+ // Release music players
+ if( m_pMusicPlayer != NULL )
+ {
+ m_pMusicPlayer->Release();
+ m_pMusicPlayer = NULL;
+ }
+ if( m_pAmbiencePlayer != NULL )
+ {
+ m_pAmbiencePlayer->Release();
+ m_pAmbiencePlayer = NULL;
+ }
+
+ for( unsigned int c = 0; c < SOUND_NUM_CLIP_PLAYERS; c ++ )
+ {
+ gClipPlayerArray[ c ]->Release( );
+ }
+
+ for( unsigned int s = 0; s < SOUND_NUM_STREAM_PLAYERS; s ++ )
+ {
+ gStreamPlayerArray[ s ]->Release( );
+ }
+}
+
+//=============================================================================
+// daSoundPlayerManager::UglyHackPostInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( IDaSoundTuner* pTuner )
+//
+// Return: void
+//
+//=============================================================================
+void daSoundPlayerManager::UglyHackPostInitialize( IDaSoundTuner* pTuner )
+{
+ // Ingame faders
+ m_pIngameFadeIn = new( GMA_PERSISTENT ) Fader( NULL, DUCK_FULL_FADE, *this, *pTuner );
+ rAssert( m_pIngameFadeIn != NULL );
+
+ m_pIngameFadeOut = new( GMA_PERSISTENT ) Fader( NULL, DUCK_FULL_FADE, *this, *pTuner );
+ rAssert( m_pIngameFadeOut != NULL );
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::GetObjectSize
+//=============================================================================
+// Description: Get the size of the sound player object
+//-----------------------------------------------------------------------------
+
+unsigned int daSoundPlayerManager::GetObjectSize( void )
+{
+ unsigned int thisSize = sizeof( *this );
+ return thisSize;
+}
+
+unsigned int daSoundPlayerManager::GetNumUsedClipPlayers()
+{
+ unsigned int playerCount = 0;
+
+ for( unsigned int c = 0; c < SOUND_NUM_CLIP_PLAYERS; c ++ )
+ {
+ if ( gClipPlayerArray[ c ]->IsCaptured( ) )
+ {
+ playerCount++;
+ }
+ }
+
+ return( playerCount );
+}
+
+unsigned int daSoundPlayerManager::GetNumUsedStreamPlayers()
+{
+ unsigned int playerCount = 0;
+
+ for( unsigned int s = 0; s < SOUND_NUM_STREAM_PLAYERS; s ++ )
+ {
+ if ( gStreamPlayerArray[ s ]->IsCaptured( ) )
+ {
+ playerCount++;
+ }
+ }
+
+ return playerCount;
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::Initialize
+//=============================================================================
+// Description: Initialize the player manager
+//
+//-----------------------------------------------------------------------------
+
+void daSoundPlayerManager::Initialize( void )
+{
+ // The stream players are tuned from associated radscripts
+
+ IRadSoundClipPlayer* pClipPlayer = NULL;
+ IRadSoundStreamPlayer* pStreamPlayer = NULL;
+ IRadSoundStitchedDataSource* pStitchedDataSource = NULL;
+
+ // CLIP PLAYERS ///////////////////////////////////////////////////////////
+
+ for( unsigned int c = 0; c < SOUND_NUM_CLIP_PLAYERS; c++ )
+ {
+ gClipPlayerArray[ c ] = new (GetThisAllocator( ) ) daSoundClipStreamPlayer( );
+ gClipPlayerArray[ c ]->AddRef( );
+ gClipPlayerArray[ c ]->InitializeAsClipPlayer( );
+ }
+
+ // STREAM PLAYERS /////////////////////////////////////////////////////////
+
+ for( unsigned int s = 0; s < SOUND_NUM_STREAM_PLAYERS; s++ )
+ {
+
+ gStreamPlayerArray[ s ] = new ( GetThisAllocator() ) daSoundClipStreamPlayer;
+ gStreamPlayerArray[ s ]->AddRef( );
+ gStreamPlayerArray[ s ]->InitializeAsStreamPlayer( );
+ }
+
+ //
+ // Make a couple of fake players for controlling music and
+ // ambience trim
+ //
+
+ // Create a music and an ambience sound player
+ m_pMusicPlayer = new( GetThisAllocator() ) MusicSoundPlayer;
+ m_pMusicPlayer->AddRef( );
+ m_pMusicPlayer->Initialize( true );
+
+ m_pAmbiencePlayer = new( GetThisAllocator() ) MusicSoundPlayer;
+ m_pAmbiencePlayer->AddRef( );
+ m_pAmbiencePlayer->Initialize( false );
+
+ m_Initialized = true;
+
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::ServiceOncePerFrame
+//=============================================================================
+// Description: Service once per frame
+//
+//-----------------------------------------------------------------------------
+
+void daSoundPlayerManager::ServiceOncePerFrame( void )
+{
+ daSoundPlayerBase* pPlayer = daSoundPlayerBase::GetLinkedClassHead( );
+ while( pPlayer != NULL )
+ {
+ pPlayer->ServiceOncePerFrame( );
+ pPlayer = pPlayer->GetLinkedClassNext( );
+ }
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::CaptureFreePlayer
+//=============================================================================
+// Description: This function finds and captures a sound player
+// and connects the given resource to it.
+//
+// Parameters: ppPlayer - (out) the captured player or NULL if one can't be
+// found
+// pResource - a pointer to a sound resource
+//
+//-----------------------------------------------------------------------------
+
+void daSoundPlayerManager::CaptureFreePlayer
+(
+ daSoundClipStreamPlayer** ppPlayer,
+ IDaSoundResource* pResource,
+ bool positional
+)
+{
+ rAssert( ppPlayer != NULL );
+ rAssert( pResource != NULL );
+
+ // Find out where to look
+ unsigned int i = 0;
+ bool playerFound = false;
+ daSoundGroup soundGroup = pResource->GetSoundGroup( );
+ IDaSoundResource::Type resourceType = pResource->GetType( );
+
+ switch( pResource->GetType( ) )
+ {
+ case IDaSoundResource::CLIP:
+ {
+ // Look in the clip player list
+ playerFound = FindFreeClipPlayer( ppPlayer, pResource );
+ break;
+ }
+ case IDaSoundResource::STREAM:
+ {
+ // Look in the stream player list
+ playerFound = FindFreeStreamPlayer( ppPlayer, pResource );
+ break;
+ }
+ default:
+ {
+ rAssert( 0 );
+ break;
+ }
+ }
+ if( playerFound )
+ {
+ // Capture it
+ (*ppPlayer)->Capture( pResource, positional );
+ }
+ else
+ {
+ (*ppPlayer) = NULL;
+ }
+
+}
+
+bool daSoundPlayerManager::FindFreePlayer(
+ daSoundClipStreamPlayer** ppPlayerArray,
+ unsigned int numPlayers,
+ daSoundClipStreamPlayer ** ppPlayer )
+{
+ *ppPlayer = 0;
+
+ for( unsigned int c = 0; c < numPlayers; c ++ )
+ {
+ if ( false == ppPlayerArray[ c ]->IsCaptured( ) )
+ {
+ *ppPlayer = ppPlayerArray[ c ];
+ break;
+ }
+ }
+
+ return NULL != *ppPlayer;
+}
+
+
+//=============================================================================
+// Function: daSoundPlayerManager::FindFreeClipPlayer
+//=============================================================================
+// Description: Find a free clip player
+//
+//-----------------------------------------------------------------------------
+
+bool daSoundPlayerManager::FindFreeClipPlayer(
+ daSoundClipStreamPlayer** ppPlayer,
+ IDaSoundResource* pResource
+)
+{
+ return FindFreePlayer( gClipPlayerArray, SOUND_NUM_CLIP_PLAYERS, ppPlayer );
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::FindFreeStreamPlayer
+//=============================================================================
+// Description: Find a free stream player
+//
+//-----------------------------------------------------------------------------
+
+bool daSoundPlayerManager::FindFreeStreamPlayer(
+ daSoundClipStreamPlayer** ppPlayer,
+ IDaSoundResource* pResource )
+{
+ return FindFreePlayer( gStreamPlayerArray, SOUND_NUM_STREAM_PLAYERS, ppPlayer );
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::PausePlayers
+//=============================================================================
+// Description: Pause the sound players
+//
+// Parameters: stackFrame - the stack frame that a player is associated with.
+// If NULL, all players pause.
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundPlayerManager::PausePlayers
+(
+)
+{
+ //
+ // Pause all players
+ //
+ daSoundPlayerBase* pPlayer = daSoundPlayerBase::GetLinkedClassHead( );
+ while( pPlayer != NULL )
+ {
+ pPlayer->Pause( );
+
+ // Move on
+ pPlayer = pPlayer->GetLinkedClassNext( );
+ }
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::PausePlayersWithFade
+//=============================================================================
+// Description: Fade out the players and then pause
+//
+// Parameters: see PausePlayers
+// pCallback - the asynchronous callback to call when done
+// pUserData - the user data to pass into the callback
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundPlayerManager::PausePlayersWithFade
+(
+ IDaSoundFadeState* pCallback,
+ void* pUserData
+)
+{
+ // Create the fade info
+ daSoundAsyncFadeCallback* pFadeInfo = new( GMA_TEMP ) daSoundAsyncFadeCallback( );
+ rAssert( pFadeInfo != NULL );
+ pFadeInfo->SetPlayerManager( this );
+ pFadeInfo->SetAction( OnFade_PausePlayers );
+ pFadeInfo->SetCallback( pCallback, pUserData );
+
+ // Start fading the sounds
+ rAssert( m_pIngameFadeOut != NULL );
+ Sound::daSoundRenderingManagerGet( )->GetTuner( )->FadeSounds
+ (
+ this,
+ pFadeInfo,
+ m_pIngameFadeOut,
+ false
+ );
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::ContinuePlayers
+//=============================================================================
+// Description: Continue the sound players
+//
+// Parameters: stackFrame - the stack frame that a player is associated with.
+// If NULL, all players continue.
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundPlayerManager::ContinuePlayers
+(
+)
+{
+ //
+ // Continue all players
+ //
+ daSoundPlayerBase* pPlayer = daSoundPlayerBase::GetLinkedClassHead( );
+ while( pPlayer != NULL )
+ {
+ if( pPlayer->IsPaused() )
+ {
+ pPlayer->Continue( );
+ }
+
+ // Move on
+ pPlayer = pPlayer->GetLinkedClassNext( );
+ }
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::ContinuePlayersWithFade
+//=============================================================================
+// Description: Continue the players and then fade in.
+//
+// Parameters: see PausePlayers
+// pCallback - the asynchronous callback to call when done
+// pUserData - the user data to pass into the callback
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundPlayerManager::ContinuePlayersWithFade
+(
+ IDaSoundFadeState* pCallback,
+ void* pUserData
+)
+{
+ // Create the fade info
+ daSoundAsyncFadeCallback* pFadeInfo =
+ new( GMA_TEMP ) daSoundAsyncFadeCallback( );
+ rAssert( pFadeInfo != NULL );
+ pFadeInfo->SetPlayerManager( this );
+ pFadeInfo->SetAction( OnFade_ContinuePlayers );
+ pFadeInfo->SetCallback( pCallback, pUserData );
+
+ // Continue the players
+ ContinuePlayers();
+
+ // Start fading the sounds
+ rAssert( m_pIngameFadeIn != NULL );
+ Sound::daSoundRenderingManagerGet( )->GetTuner( )->FadeSounds
+ (
+ this,
+ pFadeInfo,
+ m_pIngameFadeIn,
+ true
+ );
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::CancelPlayers
+//=============================================================================
+// Description: Stop the sound players
+//
+// Parameters: stackFrame - the stack frame that a player is associated with.
+// If NULL, all players stop.
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundPlayerManager::CancelPlayers
+(
+)
+{
+ //
+ // Stop all players
+ //
+ daSoundPlayerBase* pPlayer = daSoundPlayerBase::GetLinkedClassHead( );
+ while( pPlayer != NULL )
+ {
+ if( pPlayer->IsCaptured( ) )
+ {
+ // Stop the player
+ pPlayer->Stop( );
+ }
+
+ // Move on
+ pPlayer = pPlayer->GetLinkedClassNext( );
+ }
+
+ //
+ // Since this command may be called by the sound manager destructor,
+ // we must make sure that we service the sound system at least one
+ // more time so that we can actually stop all the sounds.
+ //
+ ::radSoundHalSystemGet( )->Service( );
+ ::radSoundHalSystemGet( )->ServiceOncePerFrame( );
+}
+
+//=============================================================================
+// daSoundPlayerManager::AreAllPlayersStopped
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool daSoundPlayerManager::AreAllPlayersStopped()
+{
+ unsigned int c;
+ daSoundClipStreamPlayer::State playerState;
+
+ for( c = 0; c < SOUND_NUM_CLIP_PLAYERS; c++ )
+ {
+ if( gClipPlayerArray[c] != NULL )
+ {
+ playerState = gClipPlayerArray[c]->GetState();
+
+ if( ( playerState == daSoundClipStreamPlayer::State_Cueing )
+ || ( playerState == daSoundClipStreamPlayer::State_CuedPlay )
+ || ( playerState == daSoundClipStreamPlayer::State_Playing ) )
+ {
+ if( !(gClipPlayerArray[c]->IsPaused()) )
+ {
+ return( false );
+ }
+ }
+ }
+ }
+
+ for( c = 0; c < SOUND_NUM_STREAM_PLAYERS; c ++ )
+ {
+ if( gStreamPlayerArray[c] != NULL )
+ {
+ playerState = gStreamPlayerArray[c]->GetState();
+
+ if( ( playerState == daSoundClipStreamPlayer::State_Cueing )
+ || ( playerState == daSoundClipStreamPlayer::State_CuedPlay )
+ || ( playerState == daSoundClipStreamPlayer::State_Playing )
+ || ( playerState == daSoundClipStreamPlayer::State_Stopping ) )
+ {
+ if( !(gStreamPlayerArray[c]->IsPaused()) )
+ {
+ return( false );
+ }
+ }
+ }
+ }
+
+ return( true );
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::PlayerVolumeChange
+//=============================================================================
+// Description: updates all of the players with the new volume value
+//
+//-----------------------------------------------------------------------------
+void daSoundPlayerManager::PlayerVolumeChange( daSoundGroup groupName, float trim )
+{
+ daSoundPlayerBase* pPlayer = daSoundPlayerBase::GetLinkedClassHead( );
+ while( pPlayer != NULL )
+ {
+ pPlayer->ChangeTrim(groupName,trim);
+
+ // Move on
+ pPlayer = pPlayer->GetLinkedClassNext( );
+ }
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::PlayerFaderVolumeChange
+//=============================================================================
+// Description: updates all of the players with the new fader volume value
+//
+//-----------------------------------------------------------------------------
+void daSoundPlayerManager::PlayerFaderVolumeChange( daSoundGroup groupName, float trim )
+{
+ daSoundPlayerBase* pPlayer = daSoundPlayerBase::GetLinkedClassHead( );
+ while( pPlayer != NULL )
+ {
+ pPlayer->ChangeFaderTrim(groupName,trim);
+
+ // Move on
+ pPlayer = pPlayer->GetLinkedClassNext( );
+ }
+}
+
+//=============================================================================
+// Function: daSoundPlayerManager::OnFadeDone
+//=============================================================================
+// Description: React when the fade is done
+//
+// Parameters: pUserData - a void* to a daSoundAsyncFadeCallback
+//
+//-----------------------------------------------------------------------------
+
+void daSoundPlayerManager::OnFadeDone( void* pUserData )
+{
+ daSoundAsyncFadeCallback* pFadeInfo =
+ reinterpret_cast< daSoundAsyncFadeCallback* >( pUserData );
+ rAssert( pFadeInfo != NULL );
+
+ // Perform the appropriate action
+ switch( pFadeInfo->GetAction( ) )
+ {
+ case OnFade_PausePlayers:
+ {
+ PausePlayers();
+ break;
+ }
+ case OnFade_ContinuePlayers:
+ {
+ break;
+ }
+ case OnFade_CancelPlayers:
+ {
+ CancelPlayers();
+ Sound::daSoundRenderingManagerGet( )->
+ GetTuner( )->
+ SetMasterVolume( 1.0f );
+ break;
+ }
+ default:
+ {
+ rAssert( 0 );
+ break;
+ }
+ }
+
+ // Call the callback
+ IDaSoundFadeState* pCallback = NULL;
+ void* pData = NULL;
+ pFadeInfo->GetCallback( &pCallback, &pData );
+
+ if( pCallback != NULL )
+ {
+ pCallback->OnFadeDone( pData );
+ }
+
+ // Delete the fade info
+ delete pFadeInfo;
+}
+
+void TrimFileName( char * pS, int len )
+{
+ int sl = strlen( pS );
+
+ char * pStart;
+ char * pEnd;
+
+ pEnd = pS + sl - 4;
+ pStart = pEnd - len;
+
+ if ( pStart < pS )
+ {
+ pStart = pS;
+ }
+
+ if ( pEnd < pStart )
+ {
+ pEnd = pS + 1;
+ }
+
+ int chars = pEnd - pStart;
+
+ ::memcpy( pS, pStart, chars);
+ pS[ chars ] = 0;
+}
+
+void RenderPlayer( daSoundClipStreamPlayer * pPlayer, int row, int col )
+{
+ char buf[ 256 ];
+
+ if ( false == pPlayer->IsCaptured( ) )
+ {
+ sprintf( buf, "free" );
+ }
+ else
+ {
+ float fDistToListener;
+ char sDistToListener[ 64 ];
+ char sFileName[ 64 ];
+ char sMaxDistance[ 64 ];
+
+ pPlayer->GetFileName( sFileName, 64 );
+
+ TrimFileName( sFileName, 8 );
+
+ IRadSoundHalPositionalGroup * pPosGroup = pPlayer->GetPositionalGroup( );
+
+ if ( pPosGroup )
+ {
+ radSoundVector listenerPos;
+ radSoundVector position;
+
+ float minDist;
+ float maxDist;
+
+ radSoundHalListenerGet( )->GetPosition( & listenerPos );
+
+ pPosGroup->GetPosition( & position );
+ pPosGroup->GetMinMaxDistance( & minDist, & maxDist );
+
+ fDistToListener = listenerPos.GetDistanceBetween( position );
+
+ sprintf( sDistToListener, "%.2f", fDistToListener );
+ sprintf( sMaxDistance, "%.2f", maxDist );
+ }
+ else
+ {
+ strcpy( sDistToListener, "--" );
+ strcpy( sMaxDistance, "--" );
+ }
+
+
+ // gClipPlayerArray[ c ]->
+ sprintf( buf, "[%s](%s)[%d][%s][%s]",
+ sFileName,
+ pPlayer->IsPaused( ) ? "-" : "*",
+ pPlayer->GetState( ),
+ sDistToListener,
+ sMaxDistance );
+ }
+
+ p3d::pddi->DrawString( buf, 40 + col * 320
+ , 36 + row * 16, pddiColour( 255, 255, 0 ) );
+
+}
+
+void daSoundPlayerManager::Render( void )
+{
+ if( m_Initialized )
+ {
+ int col = 0;
+ int row = 0;
+
+ unsigned int freeMem;
+ unsigned int numObjects;
+ unsigned int largestBlock;
+ unsigned int size;
+
+ radSoundHalSystemGet( )->GetRootMemoryRegion( )->GetStats(
+ & freeMem,
+ & numObjects,
+ & largestBlock,
+ true );
+
+ size = radSoundHalSystemGet( )->GetRootMemoryRegion( )->GetSize( );
+
+ char memStr[ 256 ];
+ sprintf(
+ memStr,
+ "Usd %dK Fre %dK Lrg %dK Objs: %d",
+ ( size - freeMem ) / 1024,
+ freeMem / 1024,
+ largestBlock / 1024,
+ numObjects );
+
+ p3d::pddi->DrawString( memStr, 40 + col * 320, 36 + row * 16, pddiColour( 255, 255, 0 ) );
+
+ row++;
+
+ unsigned int usedBTreeNodes = radObjectBTree::GetNumAllocatedNodes( );
+ unsigned int nodeSize = sizeof( radObjectBTreeNode );
+
+ sprintf( memStr,
+ "BTree Nodes: [0x%x], size: [0x%x]",
+ usedBTreeNodes,
+ nodeSize );
+
+ p3d::pddi->DrawString( memStr, 40 + col * 320, 36 + row * 16, pddiColour( 255, 255, 0 ) );
+
+ row++;
+
+ char listenerStr[ 128 ];
+ radSoundVector lp;
+ radSoundVector lv;
+ radSoundHalListenerGet( )->GetPosition( & lp );
+ radSoundHalListenerGet( )->GetVelocity( & lv );
+
+ sprintf( listenerStr, "Pos:[%.2f][%.2f][%.2f] Vel:[%.2f][%.2f][%.2f]\n",
+ lp.m_x, lp.m_y, lp.m_z, lv.m_x, lv.m_y, lv.m_z );
+
+ p3d::pddi->DrawString( listenerStr, 40 + col * 320, 36 + row * 16, pddiColour( 255, 255, 0 ) );
+
+ row++;
+
+ for( unsigned int c = 0; c < SOUND_NUM_CLIP_PLAYERS / 2; c ++ )
+ {
+ RenderPlayer( gClipPlayerArray[ c ], row, col );
+
+ col++;
+
+ if ( col >= 2 )
+ {
+ row++;
+ col = 0;
+ }
+ }
+
+ row++;
+
+ for( unsigned int c = 0; c < SOUND_NUM_STREAM_PLAYERS; c ++ )
+ {
+ RenderPlayer( gStreamPlayerArray[ c ], row, col );
+
+ col++;
+
+ if ( col >= 2 )
+ {
+ row++;
+ col = 0;
+ }
+ }
+ }
+}
+
+} // Sound Namespace
+
diff --git a/game/code/sound/soundrenderer/playermanager.h b/game/code/sound/soundrenderer/playermanager.h
new file mode 100644
index 0000000..09fa4eb
--- /dev/null
+++ b/game/code/sound/soundrenderer/playermanager.h
@@ -0,0 +1,185 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: playermanager.h
+//
+// Subsystem: Dark Angel - Player Manager System
+//
+// Description: Description of the DA sound player manager
+//
+// Revisions:
+// + Created October 16, 2001 -- breimer
+//
+//=============================================================================
+
+#ifndef _PLAYERMANAGER_HPP
+#define _PLAYERMANAGER_HPP
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundrenderer/idasoundtuner.h>
+#include <sound/soundrenderer/idasoundresource.h>
+#include <sound/soundrenderer/musicsoundplayer.h>
+#include <radsound.hpp>
+
+//=============================================================================
+// Global namespace forward declarations
+//=============================================================================
+
+struct IRadObjectList;
+
+class Fader;
+
+//=============================================================================
+// Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Prototypes
+//=============================================================================
+
+class daSoundPlayerGroupWiring;
+class daSoundPlayerManager;
+
+//=============================================================================
+// Forward declarations
+//=============================================================================
+
+class daSoundClipStreamPlayer;
+
+//=============================================================================
+// Class Declarations
+//=============================================================================
+
+//
+// This class is created for our asynchronous fades
+//
+
+class daSoundAsyncFadeCallback : public radObject
+{
+public:
+ IMPLEMENT_BASEOBJECT( "daSoundAsyncFadeCallback" )
+
+ daSoundAsyncFadeCallback( );
+ virtual ~daSoundAsyncFadeCallback( );
+
+ void SetAction( int action ) { m_Action = action; }
+ int GetAction( void ) { return m_Action; }
+
+ void SetPlayerManager( daSoundPlayerManager* pPlayerManager );
+ daSoundPlayerManager* GetPlayerManager( void );
+
+ void SetCallback( IDaSoundFadeState* pCallback, void* pUserData );
+ void GetCallback( IDaSoundFadeState** ppCallback, void** ppUserData );
+
+private:
+
+ int m_Action;
+ daSoundPlayerManager* m_pPlayerManager;
+ IDaSoundFadeState* m_pCallback;
+ void* m_pUserData;
+};
+
+//
+// The player manager is responsible for creating and managing daSoundClipStreamPlayer
+// objects. These objects allow the user to play sound resources.
+//
+class daSoundPlayerManager : public IDaSoundFadeState,
+ public radRefCount
+{
+public:
+ IMPLEMENT_REFCOUNTED_NOSIZE( "daSoundPlayerManager" );
+
+ //
+ // Constructor and destructor
+ //
+ daSoundPlayerManager( );
+ virtual ~daSoundPlayerManager( );
+
+ inline daSoundPlayerManager * GetInstance( void );
+
+ bool FindFreeClipPlayer(
+ daSoundClipStreamPlayer** ppPlayer,
+ IDaSoundResource* pResource );
+ bool FindFreeStreamPlayer(
+ daSoundClipStreamPlayer** ppPlayer,
+ IDaSoundResource* pResource );
+
+ unsigned int GetNumUsedClipPlayers();
+ unsigned int GetNumUsedStreamPlayers();
+ void Initialize( void );
+ void UglyHackPostInitialize( IDaSoundTuner* pTuner );
+ void ServiceOncePerFrame( void );
+ unsigned int GetObjectSize( void );
+ void CaptureFreePlayer(
+ daSoundClipStreamPlayer** ppPlayer,
+ IDaSoundResource* pResource,
+ bool positional );
+
+ void PausePlayers ( );
+ void PausePlayersWithFade(
+ IDaSoundFadeState* pCallback,
+ void* pUserData );
+
+ void ContinuePlayers ( );
+
+ void ContinuePlayersWithFade(
+ IDaSoundFadeState* pCallback,
+ void* pUserData );
+
+ void CancelPlayers ( );
+
+ bool AreAllPlayersStopped();
+
+ //
+ // Volume controls
+ //
+ void PlayerVolumeChange( daSoundGroup soundGroup, daTrimValue trim );
+ void PlayerFaderVolumeChange( daSoundGroup soundGroup, daTrimValue trim );
+
+ void Render( void );
+
+protected:
+ // When a fade is done go here and call our callback
+ enum FadeTypesEnum {
+ OnFade_PausePlayers,
+ OnFade_ContinuePlayers,
+ OnFade_CancelPlayers
+ };
+ void OnFadeDone( void* pUserData );
+
+private:
+
+ bool FindFreePlayer( daSoundClipStreamPlayer** ppPlayerArray, unsigned int numPlayers, daSoundClipStreamPlayer ** ppPlayer );
+
+ MusicSoundPlayer* m_pMusicPlayer;
+ MusicSoundPlayer* m_pAmbiencePlayer;
+
+ //
+ // The ingame faders
+ //
+ Fader* m_pIngameFadeIn;
+ Fader* m_pIngameFadeOut;
+
+ bool m_Initialized;
+
+ static daSoundPlayerManager * s_pInstance;
+
+};
+
+inline daSoundPlayerManager * daSoundPlayerManager::GetInstance( void )
+{
+ return s_pInstance;
+}
+
+} // Sound Namespace
+#endif //_PLAYERMANAGER_HPP
+
+
diff --git a/game/code/sound/soundrenderer/soundallocatedresource.cpp b/game/code/sound/soundrenderer/soundallocatedresource.cpp
new file mode 100644
index 0000000..e6678ca
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundallocatedresource.cpp
@@ -0,0 +1,429 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundallocatedresource.cpp
+//
+// Subsystem: Dark Angel - Sound Resource Management System
+//
+// Description: Implementation of an allocated sound resource
+//
+// Revisions:
+// + Created October 18, 2001 -- breimer
+//
+//=============================================================================
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <raddebug.hpp>
+#include <radsound.hpp>
+#include <raddebugwatch.hpp>
+
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/soundresourcemanager.h>
+#include <sound/soundrenderer/soundallocatedresource.h>
+#include <sound/soundrenderer/idasoundresource.h>
+#include <sound/soundrenderer/idasoundtuner.h>
+#include <sound/soundrenderer/soundnucleus.hpp>
+
+#include <memory/srrmemory.h>
+
+//=============================================================================
+// Static Variables (outside namespace)
+//=============================================================================
+
+//=============================================================================
+// Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Static Variables (inside namespace)
+//=============================================================================
+
+//=============================================================================
+// Constants
+//=============================================================================
+
+#ifndef RAD_RELEASE
+
+#ifdef RAD_DEBUG
+static bool s_displayMemoryInfo = true;
+#else
+static bool s_displayMemoryInfo = false;
+#endif
+
+static bool s_isInitialized = false;
+static int s_memoryRemaining = 0;
+
+#endif // RAD_RELEASE
+
+//=============================================================================
+// Class Implementations
+//=============================================================================
+
+//=============================================================================
+// daSoundAllocatedResource Implementation
+//=============================================================================
+
+//=============================================================================
+// Function: daSoundFileInstance::daSoundFileInstance
+//=============================================================================
+// Description: Constructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundFileInstance::daSoundFileInstance(
+ IDaSoundResource* pResource,
+ unsigned int fileIndex )
+{
+ rAssert( pResource != NULL );
+
+ m_RefCount = 0;
+ m_State = UnLoaded;
+ m_pSoundClip = NULL;
+
+ m_State = UnLoaded;
+
+ m_FileIndex = fileIndex;
+
+ m_pResource = pResource;
+ m_pResource->AddRef( );
+
+ // Set the type of resource
+
+ IDaSoundResource::Type type = pResource->GetType( );
+
+ if( type == IDaSoundResource::CLIP )
+ {
+ m_Type = IDaSoundResource::CLIP;
+ }
+ else
+ {
+ rAssert( type == IDaSoundResource::STREAM );
+ m_Type = IDaSoundResource::STREAM;
+ }
+
+#ifndef RAD_RELEASE
+ if( !s_isInitialized )
+ {
+ radDbgWatchAddBoolean( &s_displayMemoryInfo, "Show Loading Spew", "Sound Info", 0, 0 );
+
+ s_isInitialized = true;
+ }
+#endif
+}
+
+//=============================================================================
+// Function: daSoundFileInstance::~daSoundFileInstance
+//=============================================================================
+// Description: Destructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundFileInstance::~daSoundFileInstance( )
+{
+ m_pResource->Release( );
+
+ rAssert( m_State == UnLoaded );
+ rAssert( NULL == m_pSoundClip );
+
+}
+
+//=============================================================================
+// Function: daSoundFileInstance::CreateFileDataSource
+//=============================================================================
+// Description: Get the sound stream.
+//
+// Returns: Returns the sound stream if it is allocated for this resource,
+// or NULL if it is not.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundFileInstance::CreateFileDataSource(
+ IRadSoundRsdFileDataSource** ppFds )
+{
+ rAssert( GetType( ) == IDaSoundResource::STREAM );
+ rAssert( Loaded == m_State );
+
+ char fileName[ 256 ];
+ m_pResource->GetFileKeyAt( m_FileIndex, fileName, 256 );
+
+ *ppFds = radSoundRsdFileDataSourceCreate( GMA_AUDIO_PERSISTENT );
+ (*ppFds)->AddRef( );
+
+ (*ppFds)->InitializeFromFileName(
+ fileName,
+ true,
+ 0,
+ IRadSoundHalAudioFormat::Milliseconds,
+ SoundNucleusGetStreamFileAudioFormat( ) );
+
+}
+
+//=============================================================================
+// Function: daSoundFileInstance::OnDynaLoadObjectCreate
+//=============================================================================
+// Description: Called when this object is being created (by dynamic loading)
+//
+//-----------------------------------------------------------------------------
+
+void daSoundFileInstance::Load( IRadSoundHalMemoryRegion* pRegion )
+{
+ rAssert( m_State == UnLoaded );
+
+ rAssert( m_Type == IDaSoundResource::UNKNOWN || m_pResource != NULL );
+
+ // Create each type of object
+ if( m_Type == IDaSoundResource::CLIP )
+ {
+ char fileName[ 256 ];
+ m_pResource->GetFileKeyAt( m_FileIndex, fileName, 256 );
+
+ SoundNucleusLoadClip( fileName, m_pResource->GetLooping( ) );
+ }
+
+ m_State = Loading;
+}
+
+//=============================================================================
+// Function: daSoundFileInstance::OnDynaLoadObjectCreate
+//=============================================================================
+// Description: Is this object ready and stable?
+//
+//-----------------------------------------------------------------------------
+
+bool daSoundFileInstance::UpdateLoading( void )
+{
+ rAssert( Loading == m_State );
+
+ if( m_Type == IDaSoundResource::CLIP )
+ {
+ if ( SoundNucleusIsClipLoaded( ) )
+ {
+ SoundNucleusFinishClipLoad( & m_pSoundClip );
+ m_State = Loaded;
+ }
+ }
+ else
+ {
+ m_State = Loaded;
+ }
+
+ return Loaded == m_State;
+}
+
+//=============================================================================
+// Function: daSoundFileInstance::OnDynaLoadObjectDestroy
+//=============================================================================
+// Description: Destroy this dynamically loading object
+//
+//-----------------------------------------------------------------------------
+
+void daSoundFileInstance::UnLoad( void )
+{
+ rAssert( m_State == Loaded || Loading == m_State );
+
+ if ( m_Type == IDaSoundResource::CLIP )
+ {
+ if ( Loading == m_State )
+ {
+ SoundNucleusCancelClipLoad( );
+ }
+ else if ( Loaded == m_State )
+ {
+ rAssert( m_pSoundClip != NULL );
+ m_pSoundClip->Release( );
+ m_pSoundClip = NULL;
+ }
+ }
+
+ m_State = UnLoaded;
+}
+
+//=============================================================================
+// daSoundAllocatedResource Implementation
+//=============================================================================
+
+//=============================================================================
+// Function: daSoundAllocatedResource::daSoundAllocatedResource
+//=============================================================================
+// Description: Constructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundAllocatedResource::daSoundAllocatedResource( )
+ :
+ m_RefCount( 0 )
+{
+ unsigned int i = 0;
+
+ for( i = 0; i < DASound_MaxNumSoundResourceFiles; i++ )
+ {
+ m_pFileInstance[ i ] = NULL;
+ m_pDynaLoadRegion[ i ] = NULL;
+ }
+}
+
+//=============================================================================
+// Function: daSoundAllocatedResource::~daSoundAllocatedResource
+//=============================================================================
+// Description: Destructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundAllocatedResource::~daSoundAllocatedResource( void )
+{
+ if( GetResource( ) != NULL )
+ {
+ unsigned int i = 0;
+ unsigned int j = 0;
+ for( i = 0; i < DASound_MaxNumSoundResourceFiles; i++ )
+ {
+ // Release the dynamic loading region
+ if( m_pDynaLoadRegion[ i ] != NULL )
+ {
+ for( j = 0; j < m_pDynaLoadRegion[ i ]->GetNumSlots( ); j++ )
+ {
+ m_pDynaLoadRegion[ i ]->SwapInObject( j, NULL );
+ }
+ m_pDynaLoadRegion[ i ]->Release( );
+ m_pDynaLoadRegion[ i ] = NULL;
+ }
+
+ // Release file instance
+ if( m_pFileInstance[ i ] != NULL )
+ {
+ m_pFileInstance[ i ]->Release( );
+ m_pFileInstance[ i ] = NULL;
+ }
+ }
+
+ // Release the resource
+ m_pResource->Release( );
+ }
+
+}
+
+//=============================================================================
+// Function: daSoundAllocatedResource::Initialize
+//=============================================================================
+// Description: Intialize the allocated resource by giving it a normal
+// resource.
+//
+// Parameters: pResource - the resource for this instance to associate with
+// index - the index of the resource file to use
+//
+//-----------------------------------------------------------------------------
+
+void daSoundAllocatedResource::Initialize
+(
+ IDaSoundResource* pResource
+)
+{
+ rAssert( pResource != NULL );
+
+ m_pResource = pResource;
+ m_pResource->AddRef( );
+
+ // Look in the resource manager's tree of allocated resources
+ // If the file is already loaded, just addref the object
+ Sound::daSoundResourceManager* pResManager = Sound::daSoundResourceManager::GetInstance( );
+
+ // Load and initialize the files;
+
+ unsigned int numFiles = m_pResource->GetNumFiles( );
+
+ for( unsigned int i = 0; i < numFiles; i++ )
+ {
+
+#ifdef RAD_XBOX
+ daSoundFileInstance* pFileInstance = new( GMA_XBOX_SOUND_MEMORY ) daSoundFileInstance( m_pResource, i );
+#else
+ daSoundFileInstance* pFileInstance = new( GMA_AUDIO_PERSISTENT ) daSoundFileInstance( m_pResource, i );
+#endif
+ pFileInstance->AddRef( );
+
+ //
+ // Generate a dyna load region for this object
+ //
+
+ unsigned int size = 0;
+ daSoundDynaLoadRegion* pRegion = NULL;
+ m_pDynaLoadRegion[ i ] = Sound::daSoundRenderingManagerGet( )->
+ GetDynaLoadManager( )->
+ CreateRegion
+ (
+ ::radSoundHalSystemGet( )->GetRootMemoryRegion( ),
+ size,
+ 1
+ );
+ rAssert( m_pDynaLoadRegion[ i ] != NULL );
+ m_pDynaLoadRegion[ i ]->AddRef( );
+ m_pDynaLoadRegion[ i ]->SwapInObject( 0, pFileInstance );
+
+ // Set the file instance internally
+ // (The AddRef is just copied)
+
+ m_pFileInstance[ i ] = pFileInstance;
+ }
+}
+
+//=============================================================================
+// Function: daSoundAllocatedResource::ChooseNextInstance
+//=============================================================================
+// Description: Choose the next instance to play in this allocated resource
+//
+// Notes:
+// The resource automatically keeps track of what files
+// are being used. It will try not to let anything repeat
+// based on the following rules:
+// (1) IF ( numfiles <= DASound_NumLastPlayedFilesToRemember ) THEN
+// Choose a file randomly
+// (2) IF ( numfiles == DASound_NumLastPlayedFilesToRemember + 1 ) THEN
+// Choose randomly out of all files except the last one
+// used.
+// (2) ELSE
+// Don't choose one of the DASound_NumLastPlayedFilesToRemember
+// last files, but choose randomly out of the rest
+//
+//-----------------------------------------------------------------------------
+
+unsigned int daSoundAllocatedResource::ChooseNextInstance( void )
+{
+ rAssert( GetResource( ) != NULL );
+
+ // Get the number of resource files
+ unsigned int numResourceFiles = GetResource( )->GetNumFiles( );
+ rAssert( numResourceFiles > 0 );
+
+ // Return the chosen file
+ return( rand( ) % numResourceFiles );
+}
+
+//=============================================================================
+// Function: daSoundAllocatedResource::GetFileInstance
+//=============================================================================
+// Description: Get the file instance at the given index
+//
+// Returns: Returns a pointer to the file instance
+//
+//-----------------------------------------------------------------------------
+
+daSoundFileInstance* daSoundAllocatedResource::GetFileInstance
+(
+ unsigned int index
+)
+{
+ rAssert( GetResource( ) != NULL );
+ rAssert( index < GetResource( )->GetNumFiles( ) );
+
+ return m_pFileInstance[ index ];
+}
+
+} // Sound Namespace
+
diff --git a/game/code/sound/soundrenderer/soundallocatedresource.h b/game/code/sound/soundrenderer/soundallocatedresource.h
new file mode 100644
index 0000000..599e4ca
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundallocatedresource.h
@@ -0,0 +1,249 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundallocatedresource.hpp
+//
+// Subsystem: Dark Angel - Sound Resource Management System
+//
+// Description: Description of an allocated sound resource
+//
+// Revisions:
+// + Created October 19, 2001 -- breimer
+//
+//=============================================================================
+
+#ifndef _SOUNDALLOCATEDRESOURCE_HPP
+#define _SOUNDALLOCATEDRESOURCE_HPP
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <radlinkedclass.hpp>
+#include <radsound_hal.hpp>
+#include <radsound.hpp>
+
+#include <sound/soundrenderer/idasoundresource.h>
+#include <sound/soundrenderer/sounddynaload.h>
+
+//=============================================================================
+// Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Prototypes
+//=============================================================================
+
+class daSoundAllocatedResource;
+
+class daSoundFileInstance
+{
+public:
+
+ inline void * operator new ( size_t size, radMemoryAllocator allocator );
+ inline void operator delete( void * pMem );
+
+ inline void AddRef( void );
+ inline void Release( void );
+
+ // Constructor and destructor
+ daSoundFileInstance( IDaSoundResource* pResource, unsigned int fileIndex );
+ ~daSoundFileInstance( );
+
+ void Load( IRadSoundHalMemoryRegion* pRegion );
+ bool UpdateLoading( void );
+ void UnLoad( void );
+
+ inline IDaSoundResource::Type GetType( void );
+
+ inline IRadSoundClip* GetSoundClip( void );
+ void CreateFileDataSource( IRadSoundRsdFileDataSource** );
+
+ // Internal state
+ enum State { Loading, Loaded, UnLoaded };
+
+ inline State GetState( void );
+
+ inline void GetFileName( char * pFileName, unsigned int maxChars );
+
+protected:
+
+private:
+
+ unsigned int m_FileIndex;
+ unsigned int m_RefCount;
+
+ // Store the resource
+ IDaSoundResource * m_pResource;
+
+ // Store the actual allocated resource based on the attached
+ IDaSoundResource::Type m_Type;
+
+ IRadSoundClip* m_pSoundClip;
+
+ State m_State;
+};
+
+//
+// The resource library stores a giant array of resources, and provides helpfull
+// ways to get at the information. Notices that there is no
+// way to remove a resource file from this library.
+//
+class daSoundAllocatedResource : public IRefCount
+{
+
+public:
+
+ inline void AddRef( void );
+ inline void Release( void );
+
+ inline void * operator new ( size_t size, radMemoryAllocator allocator );
+ inline void operator delete( void * pMem );
+
+ //
+ // Constructor and destructor
+ //
+ daSoundAllocatedResource( );
+ ~daSoundAllocatedResource( );
+
+ //
+ // IDaSoundAllocatedResource
+ //
+ void Initialize( IDaSoundResource* pResource );
+ inline IDaSoundResource* GetResource( void );
+ unsigned int ChooseNextInstance( void );
+
+ daSoundFileInstance* GetFileInstance( unsigned int index );
+
+protected:
+ void ApplyResourceSettings_Internal( unsigned int index );
+
+private:
+ // Store the attached resource
+ IDaSoundResource* m_pResource;
+
+ unsigned int m_RefCount;
+
+ // Store the file instances
+ daSoundFileInstance* m_pFileInstance[ DASound_MaxNumSoundResourceFiles ];
+
+ // Dynamic loading region
+ daSoundDynaLoadRegion* m_pDynaLoadRegion[ DASound_MaxNumSoundResourceFiles ];
+};
+
+//=============================================================================
+// Function: daSoundFileInstance::GetType
+//=============================================================================
+// Description: Get the type of the allocated res
+//
+//-----------------------------------------------------------------------------
+
+inline IDaSoundResource::Type daSoundFileInstance::GetType( void )
+{
+ return m_Type;
+}
+
+
+//=============================================================================
+// Function: daSoundFileInstance::GetSoundClip
+//=============================================================================
+// Description: Get the sound clip.
+//
+// Returns: Returns the sound clip if it is allocated for this resource,
+// or NULL if it is not.
+//
+//-----------------------------------------------------------------------------
+
+inline IRadSoundClip* daSoundFileInstance::GetSoundClip( void )
+{
+ rAssert( IDaSoundResource::CLIP == GetType( ) );
+ rAssert( Loaded == m_State );
+
+ return m_pSoundClip;
+}
+
+
+//=============================================================================
+// Function: daSoundAllocatedResource::GetResource
+//=============================================================================
+// Description: Get the resource data from the allocated resource
+//
+//-----------------------------------------------------------------------------
+
+IDaSoundResource* daSoundAllocatedResource::GetResource( void )
+{
+ return m_pResource;
+}
+
+inline daSoundFileInstance::State daSoundFileInstance::GetState( void )
+{
+ return m_State;
+}
+
+inline void daSoundFileInstance::GetFileName( char * pBuffer, unsigned int max )
+{
+ m_pResource->GetFileKeyAt( m_FileIndex, pBuffer, max );
+}
+
+
+inline void daSoundFileInstance::AddRef( void )
+{
+ m_RefCount++;
+}
+
+inline void daSoundFileInstance::Release( void )
+{
+ rAssert( m_RefCount > 0 );
+
+ m_RefCount--;
+
+ if( 0 == m_RefCount )
+ {
+ delete this;
+ }
+}
+
+
+inline void * daSoundFileInstance::operator new ( size_t size, radMemoryAllocator allocator )
+{
+ return radMemoryAlloc( allocator, size );
+}
+
+inline void daSoundFileInstance::operator delete( void * pMem )
+{
+ return radMemoryFree( pMem );
+}
+
+inline void daSoundAllocatedResource::AddRef( void )
+{
+ m_RefCount++;
+}
+
+inline void daSoundAllocatedResource::Release( void )
+{
+ rAssert( m_RefCount > 0 );
+
+ m_RefCount--;
+
+ if( 0 == m_RefCount )
+ {
+ delete this;
+ }
+}
+
+inline void * daSoundAllocatedResource::operator new ( size_t size, radMemoryAllocator allocator )
+{
+ return radMemoryAlloc( allocator, size );
+}
+
+inline void daSoundAllocatedResource::operator delete( void * pMem )
+{
+ return radMemoryFree( pMem );
+}
+
+} // Sound Namespace
+#endif //_SOUNDALLOCATEDRESOURCE_HPP
+
diff --git a/game/code/sound/soundrenderer/soundconstants.h b/game/code/sound/soundrenderer/soundconstants.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundconstants.h
diff --git a/game/code/sound/soundrenderer/sounddynaload.cpp b/game/code/sound/soundrenderer/sounddynaload.cpp
new file mode 100644
index 0000000..8a5f7b4
--- /dev/null
+++ b/game/code/sound/soundrenderer/sounddynaload.cpp
@@ -0,0 +1,954 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: sounddynaload.cpp
+//
+// Subsystem: Dark Angel - Dynamic Loading System
+//
+// Description: Implementation of the DA Dynamic Sound Loading System
+//
+// Revisions:
+// + Created: Novemeber 22, 2001 -- breimer
+//
+//=============================================================================
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <raddebug.hpp>
+#include <radkey.hpp>
+#include <radsound.hpp>
+
+#include <sound/soundrenderer/sounddynaload.h>
+#include <sound/soundrenderer/soundallocatedresource.h>
+
+//=============================================================================
+// Static Variables (outside namespace)
+//=============================================================================
+
+Sound::daSoundDynaLoadRegion* radLinkedClass< Sound::daSoundDynaLoadRegion >::s_pLinkedClassHead = NULL;
+Sound::daSoundDynaLoadRegion* radLinkedClass< Sound::daSoundDynaLoadRegion >::s_pLinkedClassTail = NULL;
+
+//=============================================================================
+// Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Debug Information
+//=============================================================================
+
+//
+// Use this if you want to debug the sound player
+//
+#ifndef FINAL
+#ifndef NDEBUG
+#define DASOUNDDYNALOAD_DEBUG
+#ifdef DASOUNDDYNALOAD_DEBUG
+
+// Show the creation and destruction of dynamic loading regions
+static bool sg_ShowDynaLoadRegionCreation = false;
+
+#endif //DASOUNDDYNALOAD_DEBUG
+#endif //NDEBUG
+#endif //FINAL
+
+//=============================================================================
+// Static Variables
+//=============================================================================
+
+daSoundDynaLoadManager* daSoundDynaLoadManager::s_pSingleton = NULL;
+daSoundDynaLoadRegion* daSoundDynaLoadRegion::s_pActiveRegion = NULL;
+unsigned int daSoundDynaLoadRegion::s_ActiveSlot = 0;
+unsigned int daSoundDynaLoadRegion::s_GlobalPendingSwapCount = 0;
+
+//=============================================================================
+// Prototypes
+//=============================================================================
+
+class daSoundFileInstance;
+class daSoundDynaLoadRegion;
+
+//=============================================================================
+// Class Implementation
+//=============================================================================
+
+//=============================================================================
+// daSoundDynaLoadRegion Implementation
+//=============================================================================
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::daSoundDynaLoadRegion
+//=============================================================================
+// Description: Constructor.
+//
+//-----------------------------------------------------------------------------
+
+daSoundDynaLoadRegion::daSoundDynaLoadRegion( )
+:
+radRefCount( 0 ),
+m_IsInitialized( false ),
+m_NumSlots( 0 ),
+m_SlotSize( 0 ),
+m_ppSlot( NULL ),
+m_ppSlotObjects( NULL ),
+m_ppPendingSwapObjects( NULL ),
+m_PendingSwapCount( 0 )
+{
+ //
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::~daSoundDynaLoadRegion
+//=============================================================================
+// Description: Destructor.
+//
+//-----------------------------------------------------------------------------
+
+daSoundDynaLoadRegion::~daSoundDynaLoadRegion( )
+{
+ // Destroy everything
+ Destroy( );
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::~daSoundDynaLoadRegion
+//=============================================================================
+// Description: Create the region. If the size of slots is zero, do
+// not preallocate the region memory.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundDynaLoadRegion::Create
+(
+ IRadSoundHalMemoryRegion* pMemRegion,
+ unsigned int sizeofslots,
+ unsigned int numslots
+)
+{
+ rAssert( pMemRegion != NULL );
+ rAssert( !m_IsInitialized );
+ rAssert( !ArePendingSwapsRegistered( ) );
+
+ // Set the region information
+ m_SlotSize = sizeofslots;
+ m_NumSlots = numslots;
+
+ // Create the slots
+ m_ppSlot = reinterpret_cast< IRadSoundHalMemoryRegion** >
+ (
+ ::radMemoryAlloc
+ (
+ GetThisAllocator( ),
+ sizeof( IRadSoundHalMemoryRegion* ) * numslots
+ )
+ );
+ rAssert( m_ppSlot );
+
+ unsigned int i = 0;
+ for( i = 0; i < numslots; i++ )
+ {
+ // If the slot size is zero, just use the main memory
+ if( SharedMemoryRegions( ) )
+ {
+ m_ppSlot[ i ] = pMemRegion;
+ m_ppSlot[ i ]->AddRef( );
+ }
+ else
+ {
+ //
+ // ESAN TODO: Investigate the magic number 32 below...
+ //
+ m_ppSlot[ i ] = pMemRegion->CreateChildRegion( m_SlotSize, 32, "Sound Memory Region object" );
+ if( m_ppSlot[ i ] == NULL )
+ {
+ // If this occurs, there in the check for free space. This may
+ // occur if the slot size is not aligned in the same way as
+ // sound memory.
+ rDebugString( "Out of sound memory allocating region\n" );
+ rAssert( m_ppSlot[ i ] != NULL );
+ }
+ else
+ {
+ m_ppSlot[ i ]->AddRef( );
+ }
+ }
+ rAssert( m_ppSlot[ i ] != NULL );
+ rAssert( m_ppSlot[ i ]->GetSize( ) >= m_SlotSize );
+ }
+
+ // Create the slot objects and pending slot objects
+ m_ppSlotObjects = reinterpret_cast< daSoundFileInstance** >
+ (
+ ::radMemoryAlloc
+ (
+ GetThisAllocator( ),
+ sizeof( daSoundFileInstance* ) * numslots
+ )
+ );
+ m_ppPendingSwapObjects = reinterpret_cast< daSoundFileInstance** >
+ (
+ ::radMemoryAlloc
+ (
+ GetThisAllocator( ),
+ sizeof( daSoundFileInstance* ) * numslots
+ )
+ );
+ for( i = 0; i < numslots; i++ )
+ {
+ m_ppSlotObjects[ i ] = NULL;
+ m_ppPendingSwapObjects[ i ] = NULL;
+ }
+
+ m_IsInitialized = true;
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::Destroy
+//=============================================================================
+// Description: Destroy the region.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundDynaLoadRegion::Destroy( void )
+{
+ if( m_IsInitialized )
+ {
+ //
+ // Swap out all the objects
+ //
+ unsigned int i = 0;
+ for( i = 0; i < m_NumSlots; i++ )
+ {
+ SwapInObject( i, NULL );
+ }
+
+ // Destroy the pending swap objects
+ if( m_ppPendingSwapObjects != NULL )
+ {
+ ::radMemoryFree( GetThisAllocator( ), m_ppPendingSwapObjects );
+ m_ppPendingSwapObjects = NULL;
+ }
+
+ // Destroy the memory objects
+ if( m_ppSlotObjects != NULL )
+ {
+ ::radMemoryFree( GetThisAllocator( ), m_ppSlotObjects );
+ m_ppSlotObjects = NULL;
+ }
+
+ // Destroy the memory regions
+ if( m_ppSlot != NULL )
+ {
+ for( i = 0; i < m_NumSlots; i++ )
+ {
+ rAssert( m_ppSlot[ i ] != NULL );
+ m_ppSlot[ i ]->Release( );
+ m_ppSlot[ i ] = NULL;
+ }
+ ::radMemoryFree( GetThisAllocator( ), m_ppSlot );
+ m_ppSlot = NULL;
+ }
+
+ // Make sure we are not the active swap
+ ClearActiveSwap( );
+ }
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::ServiceOncePerFrame
+//=============================================================================
+// Description: Service the sound system once per frame.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundDynaLoadRegion::ServiceOncePerFrame( void )
+{
+ // Service any active swaps
+ if( s_pActiveRegion == this )
+ {
+ if( GetSlotState( s_ActiveSlot ) != Initializing )
+ {
+ ClearActiveSwap( );
+ }
+ }
+ else
+ {
+ // Make sure that any pending swaps take place
+ if( ArePendingSwapsRegistered( ) )
+ {
+ unsigned int i = 0;
+ for( i = 0; i < GetNumSlots( ); i++ )
+ {
+ daSoundFileInstance* pObject = GetPendingSwapObject( i );
+ if( pObject != NULL )
+ {
+ daSoundDynaLoadRegion::SlotState state = GetSlotState( i );
+ if( state == Empty )
+ {
+ PerformSwap( i );
+ }
+ }
+ }
+ }
+ }
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::SwapInObject
+//=============================================================================
+// Description: Swap in a sound object.
+//
+// Parameters: slot - the slot number to swap a sound into
+// pObject - a dynamic loading object
+//
+//-----------------------------------------------------------------------------
+
+void daSoundDynaLoadRegion::SwapInObject
+(
+ unsigned int slot,
+ daSoundFileInstance* pObject
+)
+{
+ // Destroy any pending swap object
+ daSoundFileInstance* pOldObject = GetPendingSwapObject( slot );
+ if( pOldObject != NULL )
+ {
+ SetPendingSwapObject( slot, NULL );
+ ClearActiveSwap( );
+ }
+
+ // Destroy the old object
+ pOldObject = GetSlotObject( slot );
+ if( pOldObject != NULL )
+ {
+ pOldObject->UnLoad( );
+ SetSlotObject( slot, NULL );
+ }
+
+ // Set the pending swap object
+ SetPendingSwapObject( slot, pObject );
+
+ // The swap will occur asynchronously in ServiceOncePerFrame( )
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::GetSlotState
+//=============================================================================
+// Description: Get the state of a slot
+//
+// Parameters: slot - the slot number to check
+//
+// Return: Returns the state of a slot
+//
+//-----------------------------------------------------------------------------
+
+daSoundDynaLoadRegion::SlotState daSoundDynaLoadRegion::GetSlotState
+(
+ unsigned int slot
+)
+{
+ rAssert( m_IsInitialized );
+ rAssert( m_ppSlot != NULL );
+ rAssert( slot < m_NumSlots );
+ rAssert( GetSlotMemoryRegion( slot ) != NULL );
+
+ bool slotHasObject = true;
+ bool slotHasAllocation = true;
+
+ unsigned int numobjs = 0;
+ GetSlotMemoryRegion( slot )->GetStats( NULL, &numobjs, NULL, false );
+ slotHasAllocation = ( ( numobjs != 0 ) && ( !SharedMemoryRegions( ) ) );
+
+ daSoundFileInstance* pObject = GetSlotObject( slot );
+ // If we do not have our own memory regions, we assume that the object has already disapeared.
+ slotHasObject = ( pObject != NULL );
+
+ // Determine the state
+ daSoundDynaLoadRegion::SlotState state = Empty;
+ if( slotHasObject )
+ {
+ rAssert( pObject != NULL );
+
+ if( pObject->UpdateLoading( ) )
+ {
+ state = Ready;
+ }
+ else
+ {
+ state = Initializing;
+ }
+ }
+ else
+ {
+ if( slotHasAllocation )
+ {
+ state = Destroying;
+ }
+ else
+ {
+ state = Empty;
+ }
+ }
+
+ return state;
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::GetNumSlots
+//=============================================================================
+// Description: Get the number of slots in this region
+//
+// Return: Returns the number of slots
+//
+//-----------------------------------------------------------------------------
+
+unsigned int daSoundDynaLoadRegion::GetNumSlots( void )
+{
+ rAssert( m_IsInitialized );
+ return m_NumSlots;
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::GetSlotSize
+//=============================================================================
+// Description: Get the size of the slots in this dynaload region
+//
+// Return: Returns the size of the slots
+//
+//-----------------------------------------------------------------------------
+
+unsigned int daSoundDynaLoadRegion::GetSlotSize( void )
+{
+ rAssert( m_IsInitialized );
+ return m_SlotSize;
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::SharedMemoryRegions
+//=============================================================================
+// Description: Are the memory regions shared, or does each slot have its own?
+//
+// Return: Returns true if the memory regions are shared.
+//
+//-----------------------------------------------------------------------------
+
+bool daSoundDynaLoadRegion::SharedMemoryRegions( void )
+{
+ return( m_SlotSize == 0 );
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::PerformSwap
+//=============================================================================
+// Description: Do an actual swap into a memory slot. The slot
+// should be already verified as empty.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundDynaLoadRegion::PerformSwap
+(
+ unsigned int slot
+)
+{
+ bool result = SetActiveSwap( slot );
+ if( result )
+ {
+ rAssert( GetSlotState( slot ) == Empty );
+
+ // Swap in the object
+ daSoundFileInstance* pObject = GetPendingSwapObject( slot );
+ rAssert( pObject != NULL );
+ pObject->AddRef( );
+ SetSlotObject( slot, pObject );
+ SetPendingSwapObject( slot, NULL );
+
+ // Tell it to create itself
+ IRadSoundHalMemoryRegion* pRegion = GetSlotMemoryRegion( slot );
+ rAssert( pRegion != NULL );
+ pObject->Load( pRegion );
+
+ pObject->Release( );
+ }
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::GetSlotMemoryRegion
+//=============================================================================
+// Description: Get the slot memory region
+//
+// Return: Returns the size of the slots
+//
+//-----------------------------------------------------------------------------
+
+IRadSoundHalMemoryRegion* daSoundDynaLoadRegion::GetSlotMemoryRegion
+(
+ unsigned int slot
+)
+{
+ rAssert( m_IsInitialized );
+ rAssert( m_ppSlot != NULL );
+ rAssert( slot < m_NumSlots );
+
+ return( m_ppSlot[ slot ] );
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::SetSlotObject
+//=============================================================================
+// Description: Set a slot's object
+//
+// Parameters: slot - the slot whose object is to be set
+// pObject - the object to place in the slot
+//
+//-----------------------------------------------------------------------------
+
+void daSoundDynaLoadRegion::SetSlotObject
+(
+ unsigned int slot,
+ daSoundFileInstance* pObject
+)
+{
+ SetObject_Internal( m_ppSlotObjects, slot, pObject );
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::GetSlotObject
+//=============================================================================
+// Description: Get a slot's object
+//
+// Return: Returns the sound object
+//
+//-----------------------------------------------------------------------------
+
+daSoundFileInstance* daSoundDynaLoadRegion::GetSlotObject
+(
+ unsigned int slot
+)
+{
+ return GetObject_Internal( m_ppSlotObjects, slot );
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::SetPendingSwapObject
+//=============================================================================
+// Description: Set a pending swap ovject for a given slot
+//
+// Parameters: slot - the slot whose object is to be set
+// pObject - the object to place in the slot
+//
+//-----------------------------------------------------------------------------
+
+void daSoundDynaLoadRegion::SetPendingSwapObject
+(
+ unsigned int slot,
+ daSoundFileInstance* pObject
+)
+{
+ if( GetPendingSwapObject( slot ) != NULL )
+ {
+ --m_PendingSwapCount;
+ --s_GlobalPendingSwapCount;
+ }
+ if( pObject != NULL )
+ {
+ ++m_PendingSwapCount;
+ ++s_GlobalPendingSwapCount;
+ }
+ SetObject_Internal( m_ppPendingSwapObjects, slot, pObject );
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::ArePendingSwapsRegistered
+//=============================================================================
+// Description: Returns true if there are still pending swaps registered
+//
+//-----------------------------------------------------------------------------
+
+bool daSoundDynaLoadRegion::ArePendingSwapsRegistered( void )
+{
+ return( m_PendingSwapCount > 0 );
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::ArePendingSwapsRegistered
+//=============================================================================
+// Description: Returns true if there are still pending swaps registered
+//
+//-----------------------------------------------------------------------------
+
+unsigned int daSoundDynaLoadRegion::GetNumPendingSwaps( void )
+{
+ return( s_GlobalPendingSwapCount );
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::GetPendingSwapObject
+//=============================================================================
+// Description: Get a pending swap object for a given slot
+//
+// Return: Returns the sound object
+//
+//-----------------------------------------------------------------------------
+
+daSoundFileInstance* daSoundDynaLoadRegion::GetPendingSwapObject
+(
+ unsigned int slot
+)
+{
+ return GetObject_Internal( m_ppPendingSwapObjects, slot );
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::SetObject_Internal
+//=============================================================================
+// Description: Set an object in an object array
+//
+// Parameters: ppObjects - the object array
+// slot - the slot whose object is to be set
+// pObject - the object to place in the slot
+//
+//-----------------------------------------------------------------------------
+
+void daSoundDynaLoadRegion::SetObject_Internal
+(
+ daSoundFileInstance** ppObjects,
+ unsigned int slot,
+ daSoundFileInstance* pObject
+)
+{
+ rAssert( m_IsInitialized );
+ rAssert( ppObjects != NULL );
+ rAssert( slot < m_NumSlots );
+
+ // Under the current usage of this class, we cannot set a slot object
+ // to anything but NULL if an object already exists.
+ daSoundFileInstance* pOldObject = GetObject_Internal
+ (
+ ppObjects,
+ slot
+ );
+ rAssert( (pObject == NULL ) || ( pOldObject == NULL ) );
+ if( pOldObject != pObject )
+ {
+ // Out with the old
+ if( pOldObject != NULL )
+ {
+ pOldObject->Release( );
+ pOldObject = NULL;
+ }
+
+ // In with the new
+ ppObjects[ slot ] = pObject;
+ rAssert( GetObject_Internal( ppObjects, slot ) == pObject );
+ if( ppObjects[ slot ] != NULL )
+ {
+ ppObjects[ slot ]->AddRef( );
+ }
+ }
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::GetObject_Internal
+//=============================================================================
+// Description: Get an object in an object array
+//
+// Return: Returns the sound object
+//
+//-----------------------------------------------------------------------------
+
+daSoundFileInstance* daSoundDynaLoadRegion::GetObject_Internal
+(
+ daSoundFileInstance** ppObjects,
+ unsigned int slot
+)
+{
+ rAssert( m_IsInitialized );
+ rAssert( ppObjects != NULL );
+ rAssert( slot < m_NumSlots );
+
+ return( ppObjects[ slot ] );
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::ClearActiveSwap
+//=============================================================================
+// Description: Clear the active swap region
+//
+//-----------------------------------------------------------------------------
+
+void daSoundDynaLoadRegion::ClearActiveSwap( void )
+{
+ if( s_pActiveRegion == this )
+ {
+ s_pActiveRegion = NULL;
+ }
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadRegion::SetActiveSwap
+//=============================================================================
+// Description: Set the active swap to this object. This is used
+// to help serialize dynamic loading.
+//
+// Returns: Returns true if the active swap region has been set to
+// this.
+//
+//-----------------------------------------------------------------------------
+
+bool daSoundDynaLoadRegion::SetActiveSwap( unsigned int slot )
+{
+ if( s_pActiveRegion == NULL )
+ {
+ s_pActiveRegion = this;
+ s_ActiveSlot = slot;
+
+ return true;
+ }
+ return false;
+}
+
+
+//=============================================================================
+// daSoundDynaLoadManager Implementation
+//=============================================================================
+
+//=============================================================================
+// Function: daSoundDynaLoadManager::daSoundDynaLoadManager
+//=============================================================================
+// Description: Constructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundDynaLoadManager::daSoundDynaLoadManager( )
+ :
+ radRefCount( 0 ),
+ m_pCompletionCallback( NULL ),
+ m_pCompletionUserData( NULL )
+{
+ // Set the singleton
+ rAssert( s_pSingleton == NULL );
+ s_pSingleton = this;
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadManager:~daSoundDynaLoadManager
+//=============================================================================
+// Description: Destructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundDynaLoadManager::~daSoundDynaLoadManager( )
+{
+ // Remove the singleton
+ rAssert( s_pSingleton != NULL );
+ s_pSingleton = NULL;
+
+ // Assert that there is no pending completion callback
+ rAssert( m_pCompletionCallback == NULL );
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadManager::GetInstance
+//=============================================================================
+// Description: Get the singleton instance of the load manager
+//
+//-----------------------------------------------------------------------------
+
+daSoundDynaLoadManager* daSoundDynaLoadManager::GetInstance( void )
+{
+ return s_pSingleton;
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadManager::ServiceOncePerFrame
+//=============================================================================
+// Description: Service the load manager.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundDynaLoadManager::ServiceOncePerFrame( void )
+{
+ IDaSoundDynaLoadCompletionCallback* callback;
+
+ // Service each of the regions
+ daSoundDynaLoadRegion* pDynaLoadRegion =
+ daSoundDynaLoadRegion::GetLinkedClassHead( );
+ while( pDynaLoadRegion != NULL )
+ {
+ pDynaLoadRegion->ServiceOncePerFrame( );
+ pDynaLoadRegion = pDynaLoadRegion->GetLinkedClassNext( );
+ }
+
+ // Call any completion callbacks
+ if
+ (
+ ( m_pCompletionCallback != NULL ) &&
+ ( daSoundDynaLoadRegion::GetNumPendingSwaps( ) == 0 )
+ )
+ {
+ //
+ // Store the callback separately before using, since the callback
+ // may lead to another sound load
+ //
+ callback = m_pCompletionCallback;
+ m_pCompletionCallback = NULL;
+ m_pCompletionUserData = NULL;
+
+ callback->OnDynaLoadOperationsComplete
+ (
+ m_pCompletionUserData
+ );
+
+ callback->Release();
+ }
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadManager::CreateRegion
+//=============================================================================
+// Description: Create a dynamic loading region
+//
+// Parameters: pMemRegion - the sound region to divide up so that
+// the given slots may be created.
+// sizeofslots - the size of the dynamic loading slots
+// numslots - the number of dynamic loading slots
+//
+//-----------------------------------------------------------------------------
+
+daSoundDynaLoadRegion* daSoundDynaLoadManager::CreateRegion
+(
+ IRadSoundHalMemoryRegion* pMemRegion,
+ unsigned int sizeofslots,
+ unsigned int numslots
+)
+{
+ rAssert( pMemRegion != NULL );
+#ifdef DASOUNDDYNALOAD_DEBUG
+ if( sg_ShowDynaLoadRegionCreation )
+ {
+ rReleasePrintf
+ (
+ "CreateRegion( %#x, sizeofslots=%u, numslots=%u )\n",
+ pMemRegion,
+ sizeofslots,
+ numslots,
+ 0
+ );
+ }
+#endif //DASOUNDDYNALOAD_DEBUG
+
+ // Make sure the new region fits into memory
+ unsigned int freeSpace = 0;
+ pMemRegion->GetStats( NULL, NULL, &freeSpace, false );
+ daSoundDynaLoadRegion* pDynaRegion = NULL;
+ if( freeSpace >= sizeofslots * numslots )
+ {
+ // Create the memory region
+ pDynaRegion = new( GetThisAllocator( ) ) daSoundDynaLoadRegion( );
+ rAssert( pDynaRegion != NULL );
+ pDynaRegion->Create( pMemRegion, sizeofslots, numslots );
+ }
+ else
+ {
+ // Out of memory!
+ // If this occurs during the game, your loading stratagies must be
+ // reconsidered. If it happens while setting up sounds for
+ // the game then the choices of resident sound files must
+ // be looked at, or more sound memory should be allocated.
+ rDebugString( "Out of sound memory trying to create sound region\n" );
+ rAssert( 0 );
+ }
+
+ return pDynaRegion;
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadManager::CreateRegionFromTotalSpace
+//=============================================================================
+// Description: Create a dynamic loading region when given a total amount of
+// space.
+//
+// Parameters: pMemRegion - the sound region to divide up so that
+// the given slots may be created.
+// sizeofslots - the size of the dynamic loading slots
+//
+// Note: The actual amount of space used will be aligned down by the
+// size of slots.
+//
+//-----------------------------------------------------------------------------
+
+daSoundDynaLoadRegion* daSoundDynaLoadManager::CreateRegionFromTotalSpace
+(
+ IRadSoundHalMemoryRegion* pMemRegion,
+ unsigned int sizeofslots
+)
+{
+ rAssert( pMemRegion != NULL );
+
+ // How much free space is there?
+ unsigned int freeSpace = 0;
+ pMemRegion->GetStats( NULL, NULL, &freeSpace, false );
+
+ // How many slots can we make
+ unsigned int numslots = freeSpace / sizeofslots;
+
+ // Make the slots
+ daSoundDynaLoadRegion* pDynaRegion = NULL;
+ if( numslots > 0 )
+ {
+ pDynaRegion = CreateRegion( pMemRegion, sizeofslots, numslots );
+ }
+
+ return pDynaRegion;
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadManager::AddCompletionCallback
+//=============================================================================
+// Description: Add a completion callback
+//
+//-----------------------------------------------------------------------------
+
+void daSoundDynaLoadManager::AddCompletionCallback
+(
+ IDaSoundDynaLoadCompletionCallback* pCallback,
+ void* pUserData
+)
+{
+ if( m_pCompletionCallback != NULL )
+ {
+ rDebugString( "Cannot add a completion callback while one is\n" );
+ rDebugString( "pending using current sounddynamic loading manager.\n" );
+ rAssert( 0 );
+
+ m_pCompletionCallback->Release( );
+ m_pCompletionCallback = NULL;
+ }
+
+ m_pCompletionCallback = pCallback;
+ m_pCompletionUserData = pUserData;
+
+ if( m_pCompletionCallback != NULL )
+ {
+ m_pCompletionCallback->AddRef( );
+ }
+}
+
+//=============================================================================
+// Function: daSoundDynaLoadManager::GetNumPendingSwaps
+//=============================================================================
+// Description: Returns the number of load regions in line to
+// be loaded with new data
+//
+//-----------------------------------------------------------------------------
+
+unsigned int daSoundDynaLoadManager::GetNumPendingSwaps( void )
+{
+ return daSoundDynaLoadRegion::GetNumPendingSwaps( );
+}
+
+} // Sound Namespace
diff --git a/game/code/sound/soundrenderer/sounddynaload.h b/game/code/sound/soundrenderer/sounddynaload.h
new file mode 100644
index 0000000..ef24357
--- /dev/null
+++ b/game/code/sound/soundrenderer/sounddynaload.h
@@ -0,0 +1,199 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: sounddynaload.hpp
+//
+// Subsystem: Dark Angel - Dynamic Loading System
+//
+// Description: Description of the DA Dynamic Sound Loading System
+//
+// Revisions:
+// + Created: Novemeber 22, 2001 -- breimer
+//
+//=============================================================================
+
+#ifndef _SOUNDDYNALOAD_HPP
+#define _SOUNDDYNALOAD_HPP
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <radlinkedclass.hpp>
+
+#include <radsound.hpp>
+
+//=============================================================================
+// Namespace
+//=============================================================================
+
+
+namespace Sound {
+
+//=============================================================================
+// Prototypes
+//=============================================================================
+
+class daSoundDynaLoadRegion;
+class daSoundDynaLoadManager;
+class daSoundFileInstance;
+
+//=============================================================================
+// Class Declarations
+//=============================================================================
+
+struct IDaSoundDynaLoadCompletionCallback : public IRefCount
+{
+ virtual void OnDynaLoadOperationsComplete( void* pUserData ) = 0;
+};
+
+//
+// A dynamic load region
+//
+class daSoundDynaLoadRegion : public radLinkedClass< daSoundDynaLoadRegion >,
+ public radRefCount
+{
+public:
+
+ enum SlotState {
+ Empty,
+ Initializing,
+ Ready,
+ Destroying
+ };
+
+ IMPLEMENT_REFCOUNTED( "daSoundDynaLoadRegion" );
+ daSoundDynaLoadRegion( );
+ virtual ~daSoundDynaLoadRegion( );
+
+ // Create and destroy the region
+ void Create
+ (
+ IRadSoundHalMemoryRegion* pMemRegion,
+ unsigned int sizeofslots,
+ unsigned int numslots
+ );
+ void Destroy( void );
+ void ServiceOncePerFrame( void );
+
+ // Track if there are any pending swaps registered.
+ static unsigned int GetNumPendingSwaps( void );
+
+ //
+ // daSoundDynaLoadRegion
+ //
+ void SwapInObject
+ (
+ unsigned int slot,
+ daSoundFileInstance* pObject
+ );
+ daSoundDynaLoadRegion::SlotState GetSlotState
+ (
+ unsigned int slot
+ );
+ unsigned int GetNumSlots( void );
+ unsigned int GetSlotSize( void );
+
+protected:
+ bool SharedMemoryRegions( void );
+
+ void PerformSwap( unsigned int slot );
+
+ IRadSoundHalMemoryRegion* GetSlotMemoryRegion( unsigned int slot );
+
+ void SetSlotObject( unsigned int slot, daSoundFileInstance* pObject );
+ daSoundFileInstance* GetSlotObject( unsigned int slot );
+
+ void SetPendingSwapObject( unsigned int slot, daSoundFileInstance * pObject );
+ bool ArePendingSwapsRegistered( void );
+ daSoundFileInstance* GetPendingSwapObject( unsigned int slot );
+
+ void SetObject_Internal
+ (
+ daSoundFileInstance** ppObjects,
+ unsigned int slot,
+ daSoundFileInstance* pObject
+ );
+ daSoundFileInstance* GetObject_Internal
+ (
+ daSoundFileInstance** ppObjects,
+ unsigned int slot
+ );
+
+ void ClearActiveSwap( void );
+ bool SetActiveSwap( unsigned int slot );
+
+private:
+ bool m_IsInitialized;
+
+ // The active swap
+ static daSoundDynaLoadRegion* s_pActiveRegion;
+ static unsigned int s_ActiveSlot;
+
+ // Region information
+ unsigned int m_NumSlots;
+ unsigned int m_SlotSize;
+
+ // Allocated regions
+ IRadSoundHalMemoryRegion** m_ppSlot;
+ daSoundFileInstance** m_ppSlotObjects;
+ daSoundFileInstance** m_ppPendingSwapObjects;
+
+ // Count of pending swap operations
+ unsigned int m_PendingSwapCount;
+ static unsigned int s_GlobalPendingSwapCount;
+};
+
+//
+// Dynamic loading system interface
+//
+class daSoundDynaLoadManager : public radRefCount
+{
+public:
+ IMPLEMENT_REFCOUNTED( "daSoundDynaLoadManager" );
+ daSoundDynaLoadManager( );
+ virtual ~daSoundDynaLoadManager( );
+
+ static daSoundDynaLoadManager* GetInstance( void );
+
+ //
+ // daSoundDynaLoadRegion
+ //
+ virtual void ServiceOncePerFrame( void );
+
+ daSoundDynaLoadRegion* CreateRegion
+ (
+ IRadSoundHalMemoryRegion* pMemRegion,
+ unsigned int sizeofslots,
+ unsigned int numslots
+ );
+ daSoundDynaLoadRegion* CreateRegionFromTotalSpace
+ (
+ IRadSoundHalMemoryRegion* pMemRegion,
+ unsigned int sizeofslots
+ );
+ void AddCompletionCallback
+ (
+ IDaSoundDynaLoadCompletionCallback* pCallback,
+ void* pUserData
+ );
+ unsigned int GetNumPendingSwaps( void );
+
+protected:
+private:
+ //
+ // Store this class as a singleton
+ //
+ static daSoundDynaLoadManager* s_pSingleton;
+
+ //
+ // Store a completion callback
+ //
+ IDaSoundDynaLoadCompletionCallback* m_pCompletionCallback;
+ void* m_pCompletionUserData;
+};
+
+} // Sound Namespace
+#endif //_SOUNDDYNALOAD_HPP
+
diff --git a/game/code/sound/soundrenderer/soundnucleus.cpp b/game/code/sound/soundrenderer/soundnucleus.cpp
new file mode 100644
index 0000000..2c56488
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundnucleus.cpp
@@ -0,0 +1,594 @@
+#include <sound/soundrenderer/soundnucleus.hpp>
+#include <main/commandlineoptions.h>
+#include <memory/srrmemory.h>
+#include <radmusic/radmusic.hpp>
+#include <memory/srrmemory.h>
+
+namespace Sound
+{
+
+const Encoding gPcmEncoding = { IRadSoundHalAudioFormat::PCM, 1, 1 };
+const Encoding gPcmBEncoding = { IRadSoundHalAudioFormat::PCM_BIGENDIAN, 1, 1 };
+const Encoding gVagEncoding = { IRadSoundHalAudioFormat::VAG, 2, 7 };
+const Encoding gGcAdpcmEncoding = { IRadSoundHalAudioFormat::GCNADPCM, 2, 7 };
+const Encoding gRadAdpcmEncoding = { IRadSoundHalAudioFormat::RadicalAdpcm, 5, 16 };
+const Encoding gXAdpcmEncoding = { IRadSoundHalAudioFormat::XBOXADPCM, 36, 128 };
+
+const unsigned int NUM_AUX_SENDS = 1;
+
+const unsigned int MUSIC_NUM_STREAM_PLAYERS = 4;
+const unsigned int MUSIC_NUM_CLIP_PLAYERS = 2;
+const unsigned int MUSIC_NUM_CHANNELS = 2;
+const unsigned int MUSIC_SAMPLING_RATE = 24000;
+
+const unsigned int TOTAL_PS2_FREE_UNCOMPRESSED_CLIP_BYTES = ( 1624 * 1024 * 7 ) / 2;
+
+#if defined RAD_GAMECUBE
+
+ const int ARAM_SILENT_BUFFER_SIZE = 1280;
+ const int ARAM_USER_START = 0x4000;
+ const int ARAM_RESERVED_BINK_MEMORY = 0x25800;
+ const int GAMECUBE_SOUND_MEMORY_AVAILABLE =
+ ( 1024 * 1024 * 10 ) - ARAM_SILENT_BUFFER_SIZE - ARAM_USER_START - ARAM_RESERVED_BINK_MEMORY;
+
+ const int PLAYBACK_RATE = 32000;
+
+ AudioFormat gCompressedStreamAudioFormat = { 1, & gGcAdpcmEncoding, 24000 };
+ AudioFormat gUnCompressedStreamAudioFormat = { 1, & gPcmBEncoding, 24000 };
+ AudioFormat gClipFileAudioFormat = { 1, & gPcmBEncoding, 24000 };
+ AudioFormat gMusicAudioFormat = { 2, & gPcmBEncoding, 24000 };
+
+ const unsigned int STREAM_BUFFER_SIZE_MS = 6000;
+ const bool STREAM_USE_BUFFERED_DATA_SOURCES = false;
+ const unsigned int STREAM_BUFFERED_DATA_SOURCE_SIZE_MS = 0;
+ const radMemorySpace STREAM_BUFFERED_DATA_SOURCE_MEMORY_SPACE = radMemorySpace_Local;
+
+ const unsigned int CLIP_BUFFERED_DATA_SOURCE_SIZE_MS = 0;
+ const radMemorySpace CLIP_BUFFERED_DATA_SOURCE_MEMORY_SPACE = radMemorySpace_Local;
+
+#elif defined RAD_XBOX || defined RAD_WIN32
+
+ const int PLAYBACK_RATE = 0;
+
+#ifdef PAL
+ AudioFormat gCompressedStreamAudioFormat = { 1, & gXAdpcmEncoding, 24000 };
+#else
+ AudioFormat gCompressedStreamAudioFormat = { 1, & gPcmEncoding, 24000 };
+#endif
+ AudioFormat gUnCompressedStreamAudioFormat = { 1, & gPcmEncoding, 24000 };
+ AudioFormat gClipFileAudioFormat = { 1, & gPcmEncoding, 24000 };
+ AudioFormat gMusicAudioFormat = { 2, & gPcmEncoding, 24000 };
+
+ const unsigned int STREAM_BUFFER_SIZE_MS = 6000;
+ const bool STREAM_USE_BUFFERED_DATA_SOURCES = false;
+ const unsigned int STREAM_BUFFERED_DATA_SOURCE_SIZE_MS = 0;
+ const radMemorySpace STREAM_BUFFERED_DATA_SOURCE_MEMORY_SPACE = radMemorySpace_Local;
+
+ const unsigned int CLIP_BUFFERED_DATA_SOURCE_SIZE_MS = 0;
+ const radMemorySpace CLIP_BUFFERED_DATA_SOURCE_MEMORY_SPACE = radMemorySpace_Local;
+
+
+#elif defined RAD_PS2
+
+ const int PLAYBACK_RATE = 0;
+
+ AudioFormat gCompressedStreamAudioFormat = { 1, & gVagEncoding, 24000 };
+ AudioFormat gUnCompressedStreamAudioFormat = { 1, & gVagEncoding, 24000 };
+ AudioFormat gClipFileAudioFormat = { 1, & gVagEncoding, 24000 };
+ AudioFormat gMusicAudioFormat = { 2, & gVagEncoding, 24000 };
+
+ const unsigned int STREAM_BUFFER_SIZE_MS = 1000;
+ const bool STREAM_USE_BUFFERED_DATA_SOURCES = true;
+ const unsigned int STREAM_BUFFERED_DATA_SOURCE_SIZE_MS = 4100;
+ const radMemorySpace STREAM_BUFFERED_DATA_SOURCE_MEMORY_SPACE = radMemorySpace_Iop;
+
+ const unsigned int CLIP_BUFFERED_DATA_SOURCE_SIZE_MS = 5000;
+ const radMemorySpace CLIP_BUFFERED_DATA_SOURCE_MEMORY_SPACE = radMemorySpace_Ee;
+
+#endif
+
+enum ClipLoadState
+{
+ ClipLoadState_Idle,
+ ClipLoadState_InitFile,
+ ClipLoadState_FillingBuffer,
+ ClipLoadState_LoadingClip,
+ ClipLoadState_Done
+};
+
+
+struct ClipLoadInfo
+{
+ IRadSoundRsdFileDataSource * pFds;
+ IRadSoundClip * pClip;
+ IRadSoundBufferedDataSource * pBds;
+ ClipLoadState state;
+ bool looping;
+} gClipLoadInfo;
+
+StreamerResources gStreamers[ SOUND_NUM_STREAM_PLAYERS ] =
+{
+ { NULL, NULL, NULL, & gCompressedStreamAudioFormat, false, 0 },
+ { NULL, NULL, NULL, & gCompressedStreamAudioFormat, false, 0 },
+ { NULL, NULL, NULL, & gCompressedStreamAudioFormat, false, 0 },
+ { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 },
+ { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 },
+ { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 },
+ { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 },
+ { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 },
+#ifdef RAD_XBOX
+ { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 },
+#endif
+ { NULL, NULL, NULL, & gUnCompressedStreamAudioFormat, false, 0 }
+};
+
+
+void CreateAudioFormat( AudioFormat * pAf, radMemoryAllocator alloc )
+{
+ pAf->m_pAudioFormat = ::radSoundHalAudioFormatCreate( alloc );
+ pAf->m_pAudioFormat->AddRef( );
+
+ pAf->m_pAudioFormat->Initialize(
+ pAf->m_pEncoding->m_Encoding,
+ NULL,
+ pAf->m_SamplingRate,
+ pAf->m_Channels,
+ 16 );
+}
+
+void DestroyAudioFormat( AudioFormat * pAf )
+{
+ pAf->m_pAudioFormat->Release( );
+ pAf->m_pAudioFormat = NULL;
+}
+
+void CreateStreamerResources( StreamerResources * pSi, radMemoryAllocator alloc )
+{
+ pSi->m_pStreamPlayer = ::radSoundStreamPlayerCreate( alloc );
+ pSi->m_pStreamPlayer->AddRef( );
+
+ pSi->m_pStreamPlayer->Initialize(
+ pSi->m_pAudioFormat->m_pAudioFormat,
+ STREAM_BUFFER_SIZE_MS,
+ IRadSoundHalAudioFormat::Milliseconds,
+ ::radSoundHalSystemGet( )->GetRootMemoryRegion( ),
+ "Sound Stream Player" );
+
+ pSi->m_pStitchedDataSource = ::radSoundStitchedDataSourceCreate( alloc );
+ pSi->m_pStitchedDataSource->AddRef( );
+ pSi->m_pStitchedDataSource->InitializeFromAudioFormat( pSi->m_pAudioFormat->m_pAudioFormat );
+
+ if ( STREAM_USE_BUFFERED_DATA_SOURCES && ( false == CommandLineOptions::Get( CLO_FIREWIRE ) ) )
+ {
+ pSi->m_pBufferedDataSource = radSoundBufferedDataSourceCreate( alloc );
+ pSi->m_pBufferedDataSource->AddRef( );
+
+ pSi->m_pBufferedDataSource->Initialize(
+ STREAM_BUFFERED_DATA_SOURCE_MEMORY_SPACE,
+ radMemorySpaceGetAllocator( STREAM_BUFFERED_DATA_SOURCE_MEMORY_SPACE, RADMEMORY_ALLOC_DEFAULT ), // This is IOP Memory
+ STREAM_BUFFERED_DATA_SOURCE_SIZE_MS,
+ IRadSoundHalAudioFormat::Milliseconds,
+ pSi->m_pAudioFormat->m_pAudioFormat,
+ "DaSound Buffered DataSource" );
+
+ }
+ else
+ {
+ pSi->m_pBufferedDataSource = NULL;
+ }
+}
+
+void DestroyStreamerResources( StreamerResources* pSi )
+{
+ // Its a stream player
+ pSi->m_pStreamPlayer->Stop( );
+ pSi->m_pStreamPlayer->SetDataSource( NULL );
+
+ if( pSi->m_pBufferedDataSource )
+ {
+ pSi->m_pBufferedDataSource->Release( );
+ pSi->m_pBufferedDataSource = NULL;
+ }
+
+ // Release the player
+ pSi->m_pStreamPlayer->Release( );
+ pSi->m_pStreamPlayer = NULL;
+
+
+ pSi->m_pStitchedDataSource->Release( );
+ pSi->m_pStitchedDataSource = NULL;
+}
+
+unsigned int CalculateStreamerSize( unsigned int ms, AudioFormat * pAf )
+{
+ unsigned int sizeInFrames = pAf->m_pAudioFormat->MillisecondsToFrames( ms );
+
+ unsigned int optimalFrameMultiple =
+ pAf->m_pAudioFormat->BytesToFrames( radSoundHalDataSourceReadMultipleGet( ) );
+
+ // Our buffer must be at least as big as two optimal reads.
+
+ sizeInFrames = radMemoryRoundUp( sizeInFrames, optimalFrameMultiple * 2 );
+
+ sizeInFrames = ::radSoundHalBufferCalculateMemorySize( IRadSoundHalAudioFormat::Frames,
+ sizeInFrames, IRadSoundHalAudioFormat::Frames, pAf->m_pAudioFormat );
+
+ unsigned int sizeInBytes = pAf->m_pAudioFormat->FramesToBytes( sizeInFrames );
+
+ return sizeInBytes;
+}
+
+void SoundNucleusInitialize( radMemoryAllocator alloc )
+{
+
+#if defined( RAD_PS2 ) || defined( RAD_GAMECUBE ) || defined( RAD_WIN32 )
+ ::radSoundHalSystemInitialize( alloc );
+#else
+ ::radSoundHalSystemInitialize( GMA_XBOX_SOUND_MEMORY );
+#endif
+
+ CreateAudioFormat( & gCompressedStreamAudioFormat, alloc );
+ CreateAudioFormat( & gUnCompressedStreamAudioFormat, alloc );
+ CreateAudioFormat( & gClipFileAudioFormat, alloc );
+ CreateAudioFormat( & gMusicAudioFormat, alloc );
+
+
+
+ //
+ // ESAN TODO: Investigate the magic number 150 below...
+ //
+
+ unsigned int totalStreamSoundMemoryNeeded = 0;
+ unsigned int totalStreamBufferMemoryNeeded = 0;
+ unsigned int totalClipBufferMemoryNeeded = 0;
+
+ if ( CLIP_BUFFERED_DATA_SOURCE_SIZE_MS > 0 )
+ {
+ gClipLoadInfo.pBds = radSoundBufferedDataSourceCreate( GMA_AUDIO_PERSISTENT );
+ gClipLoadInfo.pBds->AddRef( );
+ gClipLoadInfo.pBds->Initialize(
+ CLIP_BUFFERED_DATA_SOURCE_MEMORY_SPACE,
+ radMemorySpaceGetAllocator(
+ CLIP_BUFFERED_DATA_SOURCE_MEMORY_SPACE,
+ GMA_AUDIO_PERSISTENT ),
+ CLIP_BUFFERED_DATA_SOURCE_SIZE_MS,
+ IRadSoundHalAudioFormat::Milliseconds,
+ SoundNucleusGetClipFileAudioFormat( ),
+ "Clip Bds" );
+
+ gClipLoadInfo.pBds->SetLowWaterMark( 1.0f );
+
+ totalClipBufferMemoryNeeded =
+ SoundNucleusGetClipFileAudioFormat( )->MillisecondsToBytes(
+ CLIP_BUFFERED_DATA_SOURCE_SIZE_MS );
+ }
+
+
+ for( unsigned int i = 0; i < SOUND_NUM_STREAM_PLAYERS; i ++ )
+ {
+ StreamerResources* pSi = gStreamers + i;
+
+ unsigned int streamSoundMemoryNeeded = CalculateStreamerSize(
+ STREAM_BUFFER_SIZE_MS,
+ pSi->m_pAudioFormat );
+
+ totalStreamSoundMemoryNeeded += streamSoundMemoryNeeded;
+
+ unsigned int streamBufferMemoryNeeded = CalculateStreamerSize(
+ STREAM_BUFFERED_DATA_SOURCE_SIZE_MS,
+ pSi->m_pAudioFormat );
+
+ totalStreamBufferMemoryNeeded += streamBufferMemoryNeeded;
+
+ rTunePrintf( "AUDIO: Predicting SOUND streamer will allocate: Sound: [0x%x] Buffer:[0x%x]\n",
+ streamSoundMemoryNeeded,
+ streamBufferMemoryNeeded );
+ }
+
+ for( unsigned int i = 0; i < MUSIC_NUM_STREAM_PLAYERS; i ++ )
+ {
+ unsigned int streamSoundMemoryNeeded = CalculateStreamerSize(
+ STREAM_BUFFER_SIZE_MS,
+ & gMusicAudioFormat );
+
+ totalStreamSoundMemoryNeeded += streamSoundMemoryNeeded;
+
+ unsigned int streamBufferMemoryNeeded = CalculateStreamerSize(
+ STREAM_BUFFERED_DATA_SOURCE_SIZE_MS,
+ & gMusicAudioFormat );
+
+ totalStreamBufferMemoryNeeded += streamBufferMemoryNeeded;
+
+ rTunePrintf( "AUDIO: Predicting MUSIC streamer will allocate: Sound: [0x%x] Buffer:[0x%x]\n",
+ streamSoundMemoryNeeded,
+ streamBufferMemoryNeeded );
+
+ }
+
+ unsigned int totalClipMemoryNeeded =
+ ( TOTAL_PS2_FREE_UNCOMPRESSED_CLIP_BYTES * gClipFileAudioFormat.m_pEncoding->m_CompressionNumerator ) /
+ gClipFileAudioFormat.m_pEncoding->m_CompressionDenominator;
+
+ rTunePrintf(
+ "AUDIO: Sound Memory totals: Stream: [0x%x] Clip: [0x%x], Total: [0x%x]\n",
+ totalStreamSoundMemoryNeeded,
+ totalClipMemoryNeeded,
+ totalStreamSoundMemoryNeeded + totalClipMemoryNeeded );
+
+ #ifdef RAD_GAMECUBE
+ rAssert(
+ ( totalStreamSoundMemoryNeeded + totalClipMemoryNeeded ) <=
+ GAMECUBE_SOUND_MEMORY_AVAILABLE );
+ #endif
+
+ rTunePrintf(
+ "AUDIO: Sound Buffered Stream Memory Stream: [0x%x], Clip: [0x%x] Total:\n",
+ totalStreamBufferMemoryNeeded,
+ totalClipBufferMemoryNeeded,
+ totalStreamBufferMemoryNeeded + totalClipBufferMemoryNeeded );
+
+ IRadSoundHalSystem::SystemDescription desc;
+
+ desc.m_MaxRootAllocations = 170;
+ desc.m_NumAuxSends = NUM_AUX_SENDS;
+#ifdef RAD_WIN32
+ desc.m_SamplingRate = 24000;
+#endif
+
+#ifndef RAD_PS2
+ desc.m_ReservedSoundMemory = totalStreamSoundMemoryNeeded + totalClipMemoryNeeded;
+#endif
+
+#ifdef RAD_GAMECUBE
+ desc.m_EffectsAllocator = GMA_AUDIO_PERSISTENT;
+#endif
+
+ ::radSoundHalSystemGet( )->Initialize( desc );
+ //::radSoundHalSystemGet( )->SetOutputMode( radSoundOutputMode_Surround );
+
+ for( unsigned int i = 0; i < SOUND_NUM_STREAM_PLAYERS; i ++ )
+ {
+ gStreamers[ i ].index = i;
+ CreateStreamerResources( gStreamers + i, alloc );
+ }
+
+ radmusic::stream_graph_description sgDesc[ MUSIC_NUM_STREAM_PLAYERS ];
+
+ for( unsigned int i = 0; i < MUSIC_NUM_STREAM_PLAYERS; i ++ )
+ {
+ sgDesc[ i ].buffered_data_source_size_in_ms = STREAM_BUFFERED_DATA_SOURCE_SIZE_MS;
+ sgDesc[ i ].buffered_data_source_space = STREAM_BUFFERED_DATA_SOURCE_MEMORY_SPACE;
+ sgDesc[ i ].channels = MUSIC_NUM_CHANNELS;
+ sgDesc[ i ].sampling_rate = MUSIC_SAMPLING_RATE;
+ sgDesc[ i ].stream_buffer_size_in_ms = STREAM_BUFFER_SIZE_MS;
+ sgDesc[ i ].use_buffered_data_source = CommandLineOptions::Get( CLO_FIREWIRE ) ? false : STREAM_USE_BUFFERED_DATA_SOURCES;
+ }
+
+ radmusic::initialize( sgDesc, MUSIC_NUM_STREAM_PLAYERS, MUSIC_NUM_CLIP_PLAYERS, GMA_MUSIC );
+ radmusic::register_radload_loaders( );
+
+}
+
+void SoundNucleusTerminate( void )
+{
+ rAssert( ClipLoadState_Idle == gClipLoadInfo.state );
+
+ if ( gClipLoadInfo.pBds != NULL )
+ {
+ gClipLoadInfo.pBds->Release( );
+ }
+
+ for( unsigned int i = 0; i < SOUND_NUM_STREAM_PLAYERS; i ++ )
+ {
+ DestroyStreamerResources( gStreamers + i );
+ }
+
+ DestroyAudioFormat( & gCompressedStreamAudioFormat );
+ DestroyAudioFormat( & gUnCompressedStreamAudioFormat );
+ DestroyAudioFormat( & gClipFileAudioFormat );
+ DestroyAudioFormat( & gMusicAudioFormat );
+
+ radmusic::terminate( );
+
+ // Shutdown our related systems
+ ::radSoundHalSystemTerminate( );
+
+}
+
+IRadSoundHalAudioFormat * SoundNucleusGetStreamFileAudioFormat( void )
+{
+#if defined( RAD_GAMECUBE ) || ( defined( RAD_XBOX ) && defined( PAL ) )
+ return NULL;
+ #else
+ return gUnCompressedStreamAudioFormat.m_pAudioFormat;
+ #endif
+
+}
+
+IRadSoundHalAudioFormat * SoundNucleusGetClipFileAudioFormat( void )
+{
+ return gClipFileAudioFormat.m_pAudioFormat;
+}
+
+StreamerResources* SoundNucleusCaptureStreamerResources( IRadSoundHalAudioFormat * pAf )
+{
+ for( unsigned int i = 0; i < SOUND_NUM_STREAM_PLAYERS; i ++ )
+ {
+ StreamerResources * pSi =
+ gStreamers + i;
+
+ if ( ! pSi->m_IsCaptured )
+ {
+ if ( pSi->m_pAudioFormat->m_pAudioFormat->Matches( pAf ) )
+ {
+ pSi->m_IsCaptured = true;
+ return pSi;
+ }
+ }
+ }
+
+ rTuneAssertMsg( false, "Out of streamers of desired format" );
+
+ return NULL;
+}
+
+void SoundNucleusUnCaptureStreamerResources( StreamerResources * pSi )
+{
+ rAssert( gStreamers[ pSi->index ].m_IsCaptured == true );
+ gStreamers[ pSi->index ].m_IsCaptured = false;
+}
+
+void SoundNucleusLoadClip( const char * pFileName, bool looping )
+{
+ rAssert( ClipLoadState_Idle == gClipLoadInfo.state );
+
+ gClipLoadInfo.pFds = radSoundRsdFileDataSourceCreate( GMA_AUDIO_PERSISTENT );
+ gClipLoadInfo.pFds->AddRef( );
+ gClipLoadInfo.looping = looping;
+
+
+ // Initialize the file data source iwth our file
+ //
+ gClipLoadInfo.pFds->InitializeFromFileName(
+ pFileName,
+ false,
+ 0,
+ IRadSoundHalAudioFormat::Frames,
+ SoundNucleusGetClipFileAudioFormat( ) );
+
+ gClipLoadInfo.state = ClipLoadState_InitFile;
+}
+
+bool SoundNucleusIsClipLoaded( void )
+{
+ rAssert( ClipLoadState_Idle != gClipLoadInfo.state );
+
+ return ClipLoadState_Done == gClipLoadInfo.state;
+}
+
+void SoundNucleusFinishClipLoad( IRadSoundClip ** ppClip )
+{
+ rAssert( ClipLoadState_Done == gClipLoadInfo.state );
+
+ *ppClip = gClipLoadInfo.pClip;
+ gClipLoadInfo.pClip = NULL;
+
+ gClipLoadInfo.pFds->Release( );
+ gClipLoadInfo.pFds = NULL;
+
+ if ( NULL != gClipLoadInfo.pBds )
+ {
+ gClipLoadInfo.pBds->SetInputDataSource( 0 );
+ }
+
+ gClipLoadInfo.state = ClipLoadState_Idle;
+}
+
+void SoundNucleusCancelClipLoad( void )
+{
+ rAssert( ClipLoadState_Idle != gClipLoadInfo.state );
+
+ if( NULL != gClipLoadInfo.pClip )
+ {
+ gClipLoadInfo.pClip->Release( );
+ gClipLoadInfo.pClip = NULL;
+ }
+
+ if( NULL != gClipLoadInfo.pFds )
+ {
+ gClipLoadInfo.pFds->Release( );
+ gClipLoadInfo.pFds = NULL;
+ }
+
+ if ( NULL != gClipLoadInfo.pBds )
+ {
+ gClipLoadInfo.pBds->SetInputDataSource( 0 );
+ }
+
+ gClipLoadInfo.state = ClipLoadState_Idle;
+
+}
+
+
+void SoundNucleusServiceClipLoad( void )
+{
+ ClipLoadState oldState;
+
+ do
+ {
+ oldState = gClipLoadInfo.state;
+
+ switch ( gClipLoadInfo.state )
+ {
+ case ClipLoadState_Idle:
+ {
+ break;
+ }
+ case ClipLoadState_InitFile:
+ {
+ if ( IRadSoundHalDataSource::Initialized == gClipLoadInfo.pFds->GetState( ) )
+ {
+ if ( gClipLoadInfo.pBds != NULL )
+ {
+ unsigned int ms =
+ gClipLoadInfo.pBds->GetFormat( )->FramesToMilliseconds(
+ gClipLoadInfo.pFds->GetRemainingFrames( ) );
+
+ rTuneAssert( ms < CLIP_BUFFERED_DATA_SOURCE_SIZE_MS );
+
+ gClipLoadInfo.pBds->SetInputDataSource( gClipLoadInfo.pFds );
+
+ gClipLoadInfo.state = ClipLoadState_FillingBuffer;
+ }
+ else
+ {
+ gClipLoadInfo.pClip = radSoundClipCreate( GMA_AUDIO_PERSISTENT );
+ gClipLoadInfo.pClip->AddRef( );
+ gClipLoadInfo.pClip->Initialize(
+ gClipLoadInfo.pFds,
+ radSoundHalSystemGet( )->GetRootMemoryRegion( ),
+ gClipLoadInfo.looping,
+ "Clip" );
+
+ gClipLoadInfo.state = ClipLoadState_LoadingClip;
+ }
+ }
+
+ break;
+ }
+ case ClipLoadState_FillingBuffer:
+ {
+ if ( gClipLoadInfo.pBds->IsBufferFull( ) )
+ {
+ gClipLoadInfo.pClip = radSoundClipCreate( GMA_AUDIO_PERSISTENT );
+ gClipLoadInfo.pClip->AddRef( );
+ gClipLoadInfo.pClip->Initialize(
+ gClipLoadInfo.pBds,
+ radSoundHalSystemGet( )->GetRootMemoryRegion( ),
+ gClipLoadInfo.looping,
+ "Clip" );
+
+ gClipLoadInfo.state = ClipLoadState_LoadingClip;
+ }
+
+ break;
+ }
+ case ClipLoadState_LoadingClip:
+ {
+ if ( IRadSoundClip::Initialized == gClipLoadInfo.pClip->GetState( ) )
+ {
+ gClipLoadInfo.state = ClipLoadState_Done;
+ }
+
+ break;
+ }
+ case ClipLoadState_Done:
+ {
+ break;
+ }
+ }
+ }
+ while( oldState != gClipLoadInfo.state );
+}
+
+} \ No newline at end of file
diff --git a/game/code/sound/soundrenderer/soundnucleus.hpp b/game/code/sound/soundrenderer/soundnucleus.hpp
new file mode 100644
index 0000000..dc028e3
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundnucleus.hpp
@@ -0,0 +1,70 @@
+
+#ifndef SOUNDNUCLEUS_HPP
+#define SOUNDNUCLEUS_HPP
+
+#include <radmemory.hpp>
+#include <radsound.hpp>
+
+namespace Sound
+{
+#ifdef RAD_XBOX
+ const unsigned int SOUND_NUM_STREAM_PLAYERS = 10;
+#else
+ const unsigned int SOUND_NUM_STREAM_PLAYERS = 9;
+#endif
+ const unsigned int SOUND_NUM_CLIP_PLAYERS = 64;
+
+struct Encoding
+{
+ IRadSoundHalAudioFormat::Encoding m_Encoding;
+ unsigned int m_CompressionNumerator;
+ unsigned int m_CompressionDenominator;
+};
+
+struct AudioFormat
+{
+ unsigned int m_Channels;
+ const Encoding * m_pEncoding;
+ unsigned int m_SamplingRate;
+ IRadSoundHalAudioFormat * m_pAudioFormat;
+};
+
+struct StreamerResources
+{
+ IRadSoundStreamPlayer * m_pStreamPlayer;
+ IRadSoundBufferedDataSource * m_pBufferedDataSource;
+ IRadSoundStitchedDataSource * m_pStitchedDataSource;
+ AudioFormat * m_pAudioFormat;
+ bool m_IsCaptured;
+ unsigned int index;
+};
+
+void SoundNucleusInitialize( radMemoryAllocator alloc );
+void SoundNucleusTerminate( void );
+
+IRadSoundHalAudioFormat * SoundNucleusGetStreamFileAudioFormat( void );
+IRadSoundHalAudioFormat * SoundNucleusGetClipFileAudioFormat( void );
+
+StreamerResources * SoundNucleusCaptureStreamerResources( IRadSoundHalAudioFormat * pAf );
+void SoundNucleusUnCaptureStreamerResources( StreamerResources * pSi );
+
+void SoundNucleusLoadClip( const char * pFileName, bool looping );
+bool SoundNucleusIsClipLoaded( void );
+void SoundNucleusCancelClipLoad( void );
+void SoundNucleusFinishClipLoad( IRadSoundClip ** ppClip );
+void SoundNucleusServiceClipLoad( void );
+
+}
+
+#endif
+
+
+
+
+
+
+
+
+
+
+
diff --git a/game/code/sound/soundrenderer/soundplayer.h b/game/code/sound/soundrenderer/soundplayer.h
new file mode 100644
index 0000000..78538d3
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundplayer.h
@@ -0,0 +1,468 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: dasoundplayer.hpp
+//
+// Subsystem: Dark Angel - Sound players
+//
+// Description: Defines the a Dark Angel sound player
+//
+// Revisions:
+// + Created October 16, 2001 -- breimer
+//
+//=============================================================================
+
+#ifndef _DASOUNDPLAYER_HPP
+#define _DASOUNDPLAYER_HPP
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <radlinkedclass.hpp>
+
+#include <sound/soundrenderer/idasoundresource.h>
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundrenderer/soundallocatedresource.h>
+#include <radsound.hpp>
+
+//=============================================================================
+// Forward declarations
+//=============================================================================
+
+struct IRadObjectBTree;
+
+//=============================================================================
+// Define Owning Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Prototypes
+//=============================================================================
+
+class daSoundClipStreamPlayer;
+class daSoundTuner;
+struct IDaSoundPlayerState;
+struct StreamerResources;
+
+//=============================================================================
+// Forward declarations
+//=============================================================================
+
+class daSoundAllocatedResource;
+
+//=============================================================================
+// Typdefs and enumerations
+//=============================================================================
+
+const float radSoundPitchChangeThreshold = 0.1f;
+
+//=============================================================================
+// Class Declarations
+//=============================================================================
+
+class daSoundPlayerBase :
+ public IRefCount,
+ public radLinkedClass< daSoundPlayerBase >,
+ public radRefCount
+{
+ public:
+
+ inline daSoundPlayerBase( ) : radRefCount( 0 ) { }
+ virtual ~daSoundPlayerBase( ) { }
+
+ IMPLEMENT_REFCOUNTED( "daSoundPlayerBase" );
+
+ virtual void ServiceOncePerFrame ( void ) = 0;
+ virtual bool IsCaptured ( void ) = 0;
+ virtual void Pause ( void ) = 0;
+ virtual void Continue ( void ) = 0;
+ virtual void UberContinue( void ) = 0;
+ virtual void Stop ( void ) = 0;
+ virtual void SetPitch( float pitch ) = 0;
+ virtual bool IsPaused( void ) = 0;
+
+ virtual void ChangeTrim( daSoundGroup groupName, float newTrim ) = 0;
+ virtual void ChangeFaderTrim( daSoundGroup groupName, float newTrim ) = 0;
+};
+
+//
+// This contains a Dark Angel player instance.
+//
+class daSoundClipStreamPlayer
+ :
+ public daSoundPlayerBase,
+ public IRadSoundStitchCallback
+
+{
+public:
+
+ //
+ // Constructor and destructor
+ //
+ daSoundClipStreamPlayer( void );
+ virtual ~daSoundClipStreamPlayer ( void );
+
+ // daSoundPlayerBase
+
+ virtual void ServiceOncePerFrame ( void );
+ virtual bool IsCaptured ( void );
+ virtual void Pause ( void );
+ virtual void Continue ( void );
+ virtual void UberContinue( void );
+ virtual void Stop ( void );
+ virtual void SetPitch( float pitch );
+
+ virtual void ChangeTrim( daSoundGroup groupName, float newTrim );
+ virtual void ChangeFaderTrim( daSoundGroup groupName, float newTrim );
+
+ bool IsPaused( void );
+
+ //
+ // Sound player states
+ //
+
+ enum State {
+ State_DeCued,
+ State_Cueing,
+ State_Cued,
+ State_CuedPlay,
+ State_Playing,
+ State_Stopping,
+ State_Done
+ };
+
+ //
+ // Get and state this player's state
+ //
+
+ inline State GetState ( void );
+
+ //
+ // Initialize with some form of player
+ //
+ void InitializeAsClipPlayer( void );
+ void InitializeAsStreamPlayer( void );
+
+ void Capture(
+ IDaSoundResource* pResource,
+ bool isPositional );
+
+ inline IRadSoundHalPositionalGroup* GetPositionalGroup( void );
+
+ void Play( void );
+ void UnCapture ( void );
+
+ inline void SetPositionAndVelocity(
+ const radSoundVector * pPosition,
+ const radSoundVector * pVelocity );
+
+ inline void SetMinMaxDistance( float min, float max );
+
+ inline void SetExternalTrim( float newTrim );
+ inline void SetGroupTrim( float newTrim );
+ inline void SetFaderGroupTrim( float newTrim );
+ inline void SetMasterTrim( float newTrim );
+
+ inline IDaSoundResource::Type GetPlayerType ( void );
+ daSoundGroup GetSoundGroup ( void );
+
+ void RegisterSoundPlayerStateCallback
+ (
+ IDaSoundPlayerState* pCallback,
+ void* pUserData
+ );
+ void UnregisterSoundPlayerStateCallback
+ (
+ IDaSoundPlayerState* pCallback,
+ void* pUserData
+ );
+
+ unsigned int GetPlaybackTimeInSamples ( void );
+
+ void OnStitch( IRadSoundHalDataSource **, unsigned int frameCount, void * pUserData );
+
+ const void GetFileName( char * pBuf, unsigned int max );
+
+private:
+
+ enum CueingState
+ {
+ CueingState_Null,
+ CueingState_Resource,
+ CueingState_Player,
+ CueingState_Cued
+ };
+
+ inline void CalculateCurrentTrim( void );
+ inline void CalculateCurrentPitch( void );
+ void HookUpAndCuePlayer( void );
+
+ void UpdateClip( void );
+ void UpdateStream( void );
+
+ //
+ // Get the randomized trim and pitch settings based on a
+ // min/max variance of the current resource
+ //
+ inline void CalculateNewVaryingTrim ( void );
+ inline void CalculateNewVaryingPitch ( void );
+
+ //
+ // Trim values
+ //
+ float m_Trim;
+ float m_GroupTrim;
+ float m_FaderGroupTrim;
+ float m_MasterTrim;
+ float m_StoppingTrim;
+ float m_VaryingTrim;
+ float m_CurrentTrim; // floating target
+
+ float m_VaryingPitch;
+ float m_Pitch;
+ float m_CurrentPitch; // floating target
+
+ //
+ // Sound player state
+ //
+ State m_State;
+ CueingState m_CueingState;
+
+ //
+ // Hold a capture counter
+ //
+ unsigned int m_CaptureCount;
+
+ //
+ // Hold a pause counter
+ //
+ unsigned int m_PauseCount;
+
+ //
+ // Store the allocated and normal resource
+ //
+ daSoundAllocatedResource* m_pAllocatedResource;
+ unsigned int m_AllocResInstanceID;
+ IDaSoundResource* m_pResource;
+
+ //
+ // The positional group that this player uses
+ //
+ IRadSoundHalPositionalGroup * m_pPositionalGroup;
+ bool m_IsPositional;
+
+ //
+ // Currently, only one callback is allowed at a time
+ //
+ IDaSoundPlayerState* m_pStateChangeCallback;
+ void* m_pStateChangeCallbackUserData;
+
+ //
+ // Store the various types of players based on the connected resource
+ //
+ union
+ {
+ struct
+ {
+ IRadSoundClipPlayer* m_pClipPlayer;
+ } m_ClipInfo;
+
+ struct
+ {
+ StreamerResources* m_pResources;
+ IRadSoundRsdFileDataSource* m_pRsdFileDataSource;
+ } m_StreamInfo;
+ };
+
+ IDaSoundResource::Type m_Type;
+};
+
+
+inline IDaSoundResource::Type daSoundClipStreamPlayer::GetPlayerType( void )
+{
+ return m_Type;
+}
+
+inline void daSoundClipStreamPlayer::SetExternalTrim( float newTrim )
+{
+ radSoundVerifyAnalogVolume( newTrim );
+
+ m_Trim = newTrim;
+
+}
+
+inline void daSoundClipStreamPlayer::SetGroupTrim( float newTrim )
+{
+ radSoundVerifyAnalogVolume( newTrim );
+
+ m_GroupTrim = newTrim;
+
+}
+
+inline void daSoundClipStreamPlayer::SetFaderGroupTrim( float newTrim )
+{
+ radSoundVerifyAnalogVolume( newTrim );
+
+ m_FaderGroupTrim = newTrim;
+}
+
+inline void daSoundClipStreamPlayer::SetMasterTrim( float newTrim )
+{
+ radSoundVerifyAnalogVolume( newTrim );
+
+ m_MasterTrim = newTrim;
+}
+
+inline void daSoundClipStreamPlayer::CalculateCurrentTrim( void)
+{
+ float oldTrim = m_CurrentTrim;
+
+ float newTrim =
+ m_VaryingTrim *
+ m_StoppingTrim *
+ m_Trim *
+ m_GroupTrim *
+ m_FaderGroupTrim *
+ m_MasterTrim;
+
+ if ( State_Playing == m_State || State_Stopping == m_State )
+ {
+ if ( newTrim >= oldTrim )
+ {
+ float dif = newTrim - oldTrim;
+
+ if ( dif >= radSoundVolumeChangeThreshold )
+ {
+ newTrim = oldTrim + radSoundVolumeChangeThreshold;
+ }
+ }
+ else
+ {
+ float dif = oldTrim - newTrim;
+
+ if ( dif >= radSoundVolumeChangeThreshold )
+ {
+ newTrim = oldTrim - radSoundVolumeChangeThreshold;
+ }
+ }
+ }
+
+ m_CurrentTrim = newTrim;
+
+ radSoundVerifyAnalogVolume( m_CurrentTrim );
+}
+
+inline void daSoundClipStreamPlayer::CalculateCurrentPitch( void )
+{
+ float oldPitch = m_CurrentPitch;
+
+ float newPitch = m_VaryingPitch * m_Pitch;
+
+ if ( m_State == State_Playing || m_State == State_Stopping )
+ {
+ if ( newPitch > oldPitch )
+ {
+ float dif = newPitch - oldPitch;
+
+ if( dif > radSoundPitchChangeThreshold )
+ {
+ newPitch = oldPitch + radSoundVolumeChangeThreshold;
+ }
+ }
+ else
+ {
+ float dif = oldPitch - newPitch;
+
+ if( dif > radSoundPitchChangeThreshold )
+ {
+ newPitch = oldPitch - radSoundVolumeChangeThreshold;
+ }
+ }
+ }
+
+ m_CurrentPitch = newPitch;
+
+ radSoundVerifyAnalogPitch( m_CurrentPitch );
+}
+
+inline void daSoundClipStreamPlayer::SetPitch( float pitch )
+{
+ if ( pitch <= 0.0f )
+ {
+ rDebugPrintf( "AUDIO: Error, pitch set to: [%f]\n", pitch );
+
+ pitch = 0.0f;
+ }
+
+ // radSoundVerifyAnalogPitch( pitch );
+
+ m_Pitch = pitch;
+}
+
+inline void daSoundClipStreamPlayer::CalculateNewVaryingTrim( void )
+{
+ float minTrim;
+ float maxTrim;
+
+ m_pResource->GetTrimRange( &minTrim, &maxTrim );
+
+ m_VaryingTrim = ::radSoundRandMinMax( minTrim, maxTrim );
+
+ radSoundVerifyAnalogVolume( m_VaryingTrim );
+}
+
+
+inline void daSoundClipStreamPlayer::CalculateNewVaryingPitch( void )
+{
+ float minPitch;
+ float maxPitch;
+
+ m_pResource->GetPitchRange( &minPitch, &maxPitch );
+
+ m_VaryingPitch = ::radSoundRandMinMax( minPitch, maxPitch );
+
+ radSoundVerifyAnalogPitch( m_VaryingPitch );
+}
+
+inline daSoundClipStreamPlayer::State daSoundClipStreamPlayer::GetState( void )
+{
+ return m_State;
+}
+
+inline void daSoundClipStreamPlayer::SetPositionAndVelocity(
+ const radSoundVector * pPosition,
+ const radSoundVector * pVelocity )
+{
+ m_pPositionalGroup->SetPosition( (radSoundVector*) pPosition );
+ m_pPositionalGroup->SetVelocity( (radSoundVector*) pVelocity );
+}
+
+inline void daSoundClipStreamPlayer::SetMinMaxDistance( float min, float max )
+{
+ m_pPositionalGroup->SetMinMaxDistance( min, max );
+}
+
+inline bool daSoundClipStreamPlayer::IsPaused( void )
+{
+ return m_PauseCount != 0;
+}
+
+
+inline IRadSoundHalPositionalGroup * daSoundClipStreamPlayer::GetPositionalGroup( void )
+{
+ if ( m_IsPositional )
+ {
+ return m_pPositionalGroup;
+ }
+
+ return NULL;
+}
+
+
+} // Sound Namespace
+#endif //_DASOUNDPLAYER_HPP
+
diff --git a/game/code/sound/soundrenderer/soundrenderingmanager.cpp b/game/code/sound/soundrenderer/soundrenderingmanager.cpp
new file mode 100644
index 0000000..167e871
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundrenderingmanager.cpp
@@ -0,0 +1,1545 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundrenderingmanager.cpp
+//
+// Subsystem: Dark Angel - Sound Manager System
+//
+// Description: Implementation of the sound manager
+//
+// Revisions:
+// + Created October 2, 2001 -- breimer
+//
+//=============================================================================
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <raddebug.hpp>
+#include <radfile.hpp>
+
+
+#include <radmusic/radmusic.hpp>
+#include <radsound.hpp>
+
+#include <radobjectlist.hpp>
+#include <radscript.hpp>
+#include <radtypeinfo.hpp>
+#include <radtypeinfoutil.hpp>
+#include <radfactory.hpp>
+
+#include <memory/srrmemory.h>
+#include <loading/loadingmanager.h>
+#include <loading/soundfilehandler.h>
+
+#include <sound/soundrenderer/idasoundtuner.h>
+#include <sound/soundrenderer/soundresource.h>
+#include <sound/soundrenderer/soundresourcemanager.h>
+#include <sound/soundrenderer/playermanager.h>
+
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/soundallocatedresource.h>
+#include <sound/soundrenderer/soundconstants.h>
+#include <sound/soundclusternameenum.h>
+#include <sound/soundmanager.h>
+#include <sound/soundrenderer/soundnucleus.hpp>
+#include <sound/dialog/dialogcoordinator.h>
+#include <radobjectbtree.hpp>
+#include <radmemorymonitor.hpp>
+
+#include <pddi/pddi.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/p3dtypes.hpp>
+
+//=============================================================================
+// Namespace
+//=============================================================================
+
+//#define AUDIO_FILE_PERFORMANCE_LOG
+//#define AUDIO_FILE_PERFORMANCE_SPEW
+
+bool gDaSoundStats = false;
+bool gTuneSound = false;
+
+namespace Sound {
+
+const short MAX_BTREE_NODES = 5100;
+
+radObjectBTreeNode* gpBTreeNodePool = 0;
+
+//=============================================================================
+// Debug Information
+//=============================================================================
+
+//
+// Use this if you want to debug the sound tuner
+//
+#ifndef FINAL
+#ifndef NDEBUG
+
+// This is the type of debug information to output
+#define DASOUNDMANAGER_OUTPUTDEBUGINFO
+#ifdef DASOUNDMANAGER_OUTPUTDEBUGINFO
+//static DebugInfoType sg_DebugInfoType = DEBUG_RESOURCEINFO;
+#endif //DASOUNDMANAGER_OUTPUTDEBUGINFO
+
+#endif //NDEBUG
+#endif //FINAL
+
+
+//=============================================================================
+// Macros and Defines
+//=============================================================================
+
+//
+// THe sound memory size
+//
+
+//
+// Some directories
+//
+#define SOUND_ROOT_DIR "sound"
+#define SCRIPT_DIR SOUND_ROOT_DIR"\\scripts"
+#define TYPEINFO_DIR SOUND_ROOT_DIR"\\typ"
+
+unsigned int gTotalMicrosecondsWastedParsingScripts = 0;
+
+//=============================================================================
+// Constants
+//=============================================================================
+
+//
+// Custom memory management should not be handled by the sound system!
+//
+radMemoryAllocator DAMEMORY_ALLOC_SOUND = RADMEMORY_ALLOC_DEFAULT;
+
+//
+// The object list page size for the sound objects
+//
+static const unsigned int SoundObjectsListPageSize = 32;
+
+//
+// Flag used for file callback
+//
+static const bool LastScriptTrue = true;
+static const bool LastScriptFalse = false;
+
+//=============================================================================
+// Static Variables
+//=============================================================================
+
+//
+// The sound manager singleton class
+//
+daSoundRenderingManager* daSoundRenderingManager::s_Singleton = NULL;
+
+
+//=============================================================================
+// Local data
+//=============================================================================
+
+//
+// The name of our sound resource namespace
+//
+static const char s_SoundNamespace[ ] = "SoundGlobals";
+
+//
+// The name of our tuning object namespace
+//
+static const char s_TuningNamespace[] = "TuningGlobals";
+
+//
+// Base name for our character namespaces
+//
+static const char s_CharacterNamespace[] = "CharSounds";
+
+//
+// The name of the listener object
+//
+//static const char s_TheListenerName[ ] = "TheListener";
+
+//
+// Dialog cement file names
+//
+#if defined( RAD_XBOX )
+
+static const unsigned int s_NumDialogCementFiles = 3;
+
+static const char s_EnglishDialogue[] = "dialog0?.rcf";
+static const char s_FrenchDialogue[] = "dialogf0?.rcf";
+static const char s_GermanDialogue[] = "dialogg0?.rcf";
+static const char s_SpanishDialogue[] = "dialogs0?.rcf";
+static const char s_ItalianDialogue[] = "dialogi0?.rcf";
+
+#else
+
+static const unsigned int s_NumDialogCementFiles = 1;
+
+static const char s_EnglishDialogue[] = "dialog.rcf";
+static const char s_FrenchDialogue[] = "dialogf.rcf";
+static const char s_GermanDialogue[] = "dialogg.rcf";
+static const char s_SpanishDialogue[] = "dialogs.rcf";
+static const char s_ItalianDialogue[] = "dialogi.rcf";
+
+#endif
+
+//
+// Cement libraries associated with the Dark Angel sound system
+//
+struct DaSoundCementLibraryData
+{
+ const char* m_LibraryIDString;
+ const char* m_Filename;
+ unsigned int m_CacheSize;
+};
+
+//
+// Type info
+//
+struct DaSoundTypeinfoData
+{
+ const char* m_Filename;
+};
+static DaSoundTypeinfoData s_DaSoundTypeInfo =
+{
+ TYPEINFO_DIR"\\srrtypes.typ"
+};
+
+//
+// Radscripts associated wtih the DA sound system
+//
+struct DaSoundScriptData
+{
+ const char* m_Filename;
+};
+
+//
+// IMPORTANT CHANGE: The last script files listed will have its contents go
+// into the tuning namespace, not the sound namespace. This is to ensure
+// that everything in the sound namespace is a soundResourceData object
+// to allow me to search for dialog resources without an illegal downcast
+// (stinky downcasts, hate 'em).
+//
+static DaSoundScriptData s_DaSoundScripts[ ] =
+{
+ // Character sound scripts
+ { "\\Apu.spt" },
+ { "\\Bart.spt" },
+ { "\\Homer.spt" },
+ { "\\Lisa.spt" },
+ { "\\Marge.spt" },
+
+ // Level scripts
+ { "\\suburbs.spt" },
+ { "\\downtown.spt" },
+ { "\\seaside.spt" },
+ { "\\level1.spt" },
+ { "\\level2.spt" },
+ { "\\level3.spt" },
+ { "\\level4.spt" },
+ { "\\level5.spt" },
+ { "\\level6.spt" },
+ { "\\level7.spt" },
+ { "\\minigame.spt" },
+
+ // Sound effect resources
+ { "\\frontend.spt" },
+ { "\\collide.spt" },
+ { "\\carsound.spt" },
+ { "\\World.spt" },
+ { "\\positionalSounds.spt" },
+ { "\\interactive_props.spt" },
+
+ // Dialog
+ { "\\dialog.spt" },
+ { "\\nis.spt" },
+
+ // Cars
+ { "\\bart_v.spt" },
+ { "\\apu_v.spt" },
+ { "\\snake_v.spt" },
+ { "\\homer_v.spt" },
+ { "\\famil_v.spt" },
+ { "\\gramp_v.spt" },
+ { "\\cletu_v.spt" },
+ { "\\wiggu_v.spt" },
+ { "\\empty.spt" },
+ { "\\marge_v.spt" },
+ { "\\empty.spt" },
+ { "\\empty.spt" },
+ { "\\smith_v.spt" },
+ { "\\empty.spt" },
+ { "\\empty.spt" },
+ { "\\empty.spt" },
+ { "\\zombi_v.spt" },
+ { "\\empty.spt" },
+ { "\\empty.spt" },
+ { "\\cVan.spt" },
+ { "\\compactA.spt" },
+ { "\\comic_v.spt" },
+ { "\\skinn_v.spt" },
+ { "\\cCola.spt" },
+ { "\\cSedan.spt" },
+ { "\\cPolice.spt" },
+ { "\\cCellA.spt" },
+ { "\\cCellB.spt" },
+ { "\\cCellC.spt" },
+ { "\\cCellD.spt" },
+ { "\\minivanA_v.spt" },
+ { "\\pickupA.spt" },
+ { "\\taxiA_v.spt" },
+ { "\\sportsA.spt" },
+ { "\\sportsB.spt" },
+ { "\\SUVA.spt" },
+ { "\\wagonA.spt" },
+ { "\\hbike_v.spt" },
+ { "\\burns_v.spt" },
+ { "\\honor_v.spt" },
+ { "\\cArmor.spt" },
+ { "\\cCurator.spt" },
+ { "\\cHears.spt" },
+ { "\\cKlimo.spt" },
+ { "\\cLimo.spt" },
+ { "\\cNerd.spt" },
+ { "\\frink_v.spt" },
+ { "\\cMilk.spt" },
+ { "\\cDonut.spt" },
+ { "\\bbman_v.spt" },
+ { "\\bookb_v.spt" },
+ { "\\carhom_v.spt" },
+ { "\\elect_v.spt" },
+ { "\\fone_v.spt" },
+ { "\\gramR_v.spt" },
+ { "\\moe_v.spt" },
+ { "\\mrplo_v.spt" },
+ { "\\otto_v.spt" },
+ { "\\plowk_v.spt" },
+ { "\\scorp_v.spt" },
+ { "\\willi_v.spt" },
+ { "\\sedanA.spt" },
+ { "\\sedanB.spt" },
+ { "\\cBlbart.spt" },
+ { "\\cCube.spt" },
+ { "\\cDuff.spt" },
+ { "\\cNonup.spt" },
+ { "\\lisa_v.spt" },
+ { "\\krust_v.spt" },
+ { "\\coffin.spt" },
+ { "\\hallo.spt" },
+ { "\\ship.spt" },
+ { "\\witchcar.spt" },
+ { "\\huska.spt" },
+ { "\\atv_v.spt" },
+ { "\\dune_v.spt" },
+ { "\\hype_v.spt" },
+ { "\\knigh_v.spt" },
+ { "\\mono_v.spt" },
+ { "\\oblit_v.spt" },
+ { "\\rocke_v.spt" },
+ { "\\ambul.spt" },
+ { "\\burnsarm.spt" },
+ { "\\fishtruc.spt" },
+ { "\\garbage.spt" },
+ { "\\icecream.spt" },
+ { "\\istruck.spt" },
+ { "\\nuctruck.spt" },
+ { "\\pizza.spt" },
+ { "\\schoolbu.spt" },
+ { "\\votetruc.spt" },
+ { "\\glastruc.spt" },
+ { "\\cfire_v.spt" },
+ { "\\cBone.spt" },
+ { "\\redbrick.spt" },
+
+ // Tuning
+ { "\\car_tune.spt" },
+ { "\\positionalSettings.spt" },
+ { "\\global.spt" }
+};
+
+const unsigned int NumSoundScripts = sizeof( s_DaSoundScripts ) /
+ sizeof( DaSoundScriptData );
+
+const unsigned int NUM_TUNING_SCRIPTS = 3;
+const unsigned int NUM_CHARACTER_SCRIPTS = 5;
+const unsigned int INTERACTIVE_PROPS_SCRIPT_POSITION = 21;
+const unsigned int DIALOGUE_SCRIPT_POSITION = 22;
+const unsigned int NIS_SCRIPT_POSITION = 23;
+
+//
+// Array mapping scripts to sound clusters that their sounds should
+// be loaded/unloaded with.
+//
+// IMPORTANT: this array needs to be maintained to match the scripts
+// in s_DaSoundScripts up to the first car script. After that, we
+// do it programatically (I don't think that's actually a word).
+//
+static SoundClusterName s_ScriptClusters[] =
+{
+ SC_CHAR_APU,
+ SC_CHAR_BART,
+ SC_CHAR_HOMER,
+ SC_CHAR_LISA,
+ SC_CHAR_MARGE,
+ SC_LEVEL_SUBURBS,
+ SC_LEVEL_DOWNTOWN,
+ SC_LEVEL_SEASIDE,
+ SC_LEVEL1,
+ SC_LEVEL2,
+ SC_LEVEL3,
+ SC_LEVEL4,
+ SC_LEVEL5,
+ SC_LEVEL6,
+ SC_LEVEL7,
+ SC_MINIGAME,
+ SC_ALWAYS_LOADED,
+ SC_ALWAYS_LOADED,
+ SC_ALWAYS_LOADED,
+ SC_ALWAYS_LOADED,
+ SC_INGAME,
+ SC_INGAME,
+ SC_NEVER_LOADED,
+ SC_NEVER_LOADED,
+};
+const unsigned int NumClusterNames = sizeof( s_ScriptClusters ) /
+ sizeof( SoundClusterName );
+
+
+//=============================================================================
+// Class Implementations
+//=============================================================================
+
+//=============================================================================
+// daSoundRenderingManager Implementation
+//=============================================================================
+
+//=============================================================================
+// Function: daSoundRenderingManager::daSoundRenderingManager
+//=============================================================================
+// Description: Constructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundRenderingManager::daSoundRenderingManager( )
+ :
+ radRefCount( 0 ),
+ m_pScript( NULL ),
+ m_IsInitialized( false ),
+ m_pResourceNameSpace( NULL ),
+ m_pTuningNameSpace( NULL ),
+ m_pDynaLoadManager( NULL ),
+ m_pTuner( NULL ),
+ m_pResourceManager( NULL ),
+ m_pPlayerManager( NULL ),
+ m_scriptLoadCount( 0 ),
+ m_currentLanguage( DIALOGUE_LANGUAGE_ENGLISH ),
+ m_languageSelected( false )
+{
+ unsigned int i;
+
+ // Create the singleton
+ rAssert( s_Singleton == NULL );
+ s_Singleton = this;
+
+ for( i = 0; i < NUM_CHARACTER_NAMESPACES; i++ )
+ {
+ m_pCharacterNameSpace[i] = NULL;
+ }
+
+ for( i = 0; i < NUM_SOUND_CEMENT_FILES; i++ )
+ {
+ m_soundCementFileHandles[i] = 0;
+ }
+
+ m_LastPerformanceEventTime = radTimeGetMilliseconds( );
+ ::RadSoundSetFilePerformanceCallback( & daSoundRenderingManager::FilePerformanceEvent );
+
+ radDbgWatchAddBoolean(
+ & gDaSoundStats,
+ "DaSound Stats",
+ "Sound" );
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::~daSoundRenderingManager
+//=============================================================================
+// Description: Destructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundRenderingManager::~daSoundRenderingManager( )
+{
+
+ radDbgWatchDelete( & gDaSoundStats );
+ ::RadSoundSetFilePerformanceCallback( NULL );
+
+ rAssert( !m_IsInitialized );
+
+ // Clear the singleton
+ s_Singleton = NULL;
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::GetInstance
+//=============================================================================
+// Description: Get the singleton instance of this class
+//
+//-----------------------------------------------------------------------------
+
+daSoundRenderingManager* daSoundRenderingManager::GetInstance( void )
+{
+ return s_Singleton;
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::Initialize
+//=============================================================================
+// Description: Initialize the sound manager with the stuff that we don't
+// need to do if we're muted.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundRenderingManager::Initialize
+(
+ void
+)
+{
+ unsigned int i;
+ char nameBuffer[50];
+ int nameLength;
+
+ //
+ // Create namespaces
+ //
+ m_pResourceNameSpace = ::radNameSpaceCreate( GetThisAllocator( ) );
+ m_pResourceNameSpace->AddRef( );
+ m_pResourceNameSpace->SetName( s_SoundNamespace );
+
+ m_pTuningNameSpace = ::radNameSpaceCreate( GetThisAllocator( ) );
+ m_pTuningNameSpace->AddRef( );
+ m_pTuningNameSpace->SetName( s_TuningNamespace );
+
+ strcpy( nameBuffer, s_CharacterNamespace );
+ nameLength = strlen( s_CharacterNamespace );
+ for( i = 0; i < NUM_CHARACTER_NAMESPACES; i++ )
+ {
+ m_pCharacterNameSpace[i] = ::radNameSpaceCreate( GetThisAllocator() );
+ rAssert( m_pCharacterNameSpace[i] != NULL );
+
+ m_pCharacterNameSpace[i]->AddRef();
+
+ //
+ // Create a unique name
+ //
+ nameBuffer[nameLength] = static_cast<char>( '0' + i );
+ nameBuffer[nameLength+1] = '\0';
+ m_pCharacterNameSpace[i]->SetName( nameBuffer );
+ }
+
+ //
+ // Spawn other elements of the sound system (some of these depend on the namespace)
+ //
+ m_pDynaLoadManager = new ( GetThisAllocator( ) ) daSoundDynaLoadManager( );
+ m_pDynaLoadManager->AddRef( );
+ m_pResourceManager = new ( GetThisAllocator( ) ) daSoundResourceManager( );
+ m_pResourceManager->AddRef( );
+ m_pPlayerManager = new ( GetThisAllocator( ) ) daSoundPlayerManager( );
+ m_pPlayerManager->AddRef( );
+
+ Sound::daSoundTunerCreate ( &m_pTuner, GetThisAllocator( ) );
+
+ m_pPlayerManager->UglyHackPostInitialize( m_pTuner );
+
+ //
+ // Register some factories and some objects. Some of these object use
+ // the spawned systems.
+ //
+ ::radFactoryRegister(
+ "daSoundResourceData",
+ (radFactoryProc*) daSoundResourceManager::CreateResourceData );
+ rAssert( GetSoundNamespace( ) != NULL );
+ rAssert( GetTuningNamespace() != NULL );
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::IsInitialized
+//=============================================================================
+// Description: Use this to pull for the end of initialization
+//
+//-----------------------------------------------------------------------------
+
+bool daSoundRenderingManager::IsInitialized( void )
+{
+ return m_IsInitialized;
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::Terminate
+//=============================================================================
+// Description: Terminate the sound manager
+//
+//-----------------------------------------------------------------------------
+
+void daSoundRenderingManager::Terminate( void )
+{
+ unsigned int i;
+
+ // Stop all the sounds in the game
+ GetPlayerManager( )->CancelPlayers( );
+
+ // Release the namespaces, must do this before killing resource manager
+ // some of the reference counting is not per-object.
+
+ m_pResourceNameSpace->Release( );
+ m_pTuningNameSpace->Release();
+
+ for( i = 0; i < NUM_CHARACTER_NAMESPACES; i++ )
+ {
+ m_pCharacterNameSpace[i]->Release();
+ }
+
+ // Destroy the sound systems created by the initalize
+ if( IsInitialized( ) )
+ {
+ //
+ // Detach the various systems spawned on creation
+ //
+ if( m_pPlayerManager != NULL )
+ {
+ m_pPlayerManager->Release( );
+ m_pPlayerManager = NULL;
+ }
+ if( m_pResourceManager != NULL )
+ {
+ m_pResourceManager->Release( );
+ m_pResourceManager = NULL;
+ }
+ if( m_pTuner != NULL )
+ {
+ m_pTuner->Release( );
+ m_pTuner = NULL;
+ }
+ if( m_pDynaLoadManager != NULL )
+ {
+ m_pDynaLoadManager->Release( );
+ m_pDynaLoadManager = NULL;
+ }
+
+#ifndef FINAL
+ if ( gTuneSound )
+ {
+ ::radRemoteScriptTerminate( );
+ }
+#endif
+ rAssert( m_pScript == NULL );
+ ::radScriptTerminate( );
+
+ //
+ // Release the cement file
+ //
+ for( i = 0; i < NUM_SOUND_CEMENT_FILES; i++ )
+ {
+ GetLoadingManager()->UnregisterCementLibrary( m_soundCementFileHandles[i] );
+ }
+
+ // Uninitialize
+ m_IsInitialized = false;
+ }
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::Service
+//=============================================================================
+// Description: Service the Dark Angel sound system. This call should be made
+// as often as possible.
+//
+// Parameters: none
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundRenderingManager::Service( void )
+{
+ //
+ // Service the radsound system
+ //
+ ::radSoundHalSystemGet( )->Service( );
+ SoundNucleusServiceClipLoad( );
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::ServiceOncePerFrame
+//=============================================================================
+// Description: Service the Dark Angel sound system. This call should be made
+// no more than once per frame or else performance may suffer.
+//
+// Parameters: none
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundRenderingManager::ServiceOncePerFrame( unsigned int elapsedTime )
+{
+ //
+ // Service the radsound system
+ //
+ ::radSoundHalSystemGet( )->ServiceOncePerFrame( );
+
+ //
+ // Do nothing else until we're fully initialized
+ //
+ if( !IsInitialized( ) )
+ {
+ return;
+ }
+
+ // Service the dynamic loading system
+ if( GetDynaLoadManager( ) != NULL )
+ {
+ GetDynaLoadManager( )->ServiceOncePerFrame( );
+ }
+
+ // Service the tuner
+ if( GetTuner( ) != NULL )
+ {
+ GetTuner( )->ServiceOncePerFrame( elapsedTime );
+ }
+
+ // Service the player manager
+ if( GetPlayerManager( ) != NULL )
+ {
+ GetPlayerManager( )->ServiceOncePerFrame( );
+ }
+}
+
+//=============================================================================
+// daSoundRenderingManager::QueueCementFileRegistration
+//=============================================================================
+// Description: Set up the cement file registration with the loading manager
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void daSoundRenderingManager::QueueCementFileRegistration()
+{
+ HeapMgr()->PushHeap (GMA_AUDIO_PERSISTENT);
+
+ //
+ // Queue requests for sound cement file registration
+ //
+ int i = s_NumDialogCementFiles;
+
+ if( !m_languageSelected )
+ {
+ registerDialogueCementFiles( s_EnglishDialogue );
+ m_languageSelected = true;
+ }
+
+#ifdef RAD_XBOX
+ //
+ // Register the music rcfs -- no localization needed.
+ //
+ m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "music00.rcf" );
+ m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "music01.rcf" );
+ m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "music02.rcf" );
+ m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "music03.rcf" );
+#else
+ m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "music.rcf" );
+#endif
+
+ m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "carsound.rcf" );
+ m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "ambience.rcf" );
+ m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "nis.rcf" );
+ m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "soundfx.rcf" );
+ m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( "scripts.rcf" );
+
+ HeapMgr()->PopHeap (GMA_AUDIO_PERSISTENT);
+}
+
+//=============================================================================
+// daSoundRenderingManager::SwitchDialogueCementFile
+//=============================================================================
+// Description: Switch the current language to the given language.
+//
+// Parameters: language - new language to use
+//
+// Return: void
+//
+//=============================================================================
+void daSoundRenderingManager::SetLanguage( Scrooby::XLLanguage language )
+{
+ unsigned int i;
+ const char* cementFileName = s_EnglishDialogue;
+ DialogueLanguage oldLanguage = m_currentLanguage;
+
+ switch( language )
+ {
+ case Scrooby::XL_ENGLISH:
+ cementFileName = s_EnglishDialogue;
+ m_currentLanguage = DIALOGUE_LANGUAGE_ENGLISH;
+ break;
+
+ //**************************************************************************
+ // TEMPORARY: all dialog is English until we actually get localized dialogue
+ //**************************************************************************
+ case Scrooby::XL_FRENCH:
+ cementFileName = s_FrenchDialogue;
+ m_currentLanguage = DIALOGUE_LANGUAGE_FRENCH;
+ break;
+
+ case Scrooby::XL_GERMAN:
+ cementFileName = s_GermanDialogue;
+ m_currentLanguage = DIALOGUE_LANGUAGE_GERMAN;
+ break;
+
+ case Scrooby::XL_SPANISH:
+ cementFileName = s_SpanishDialogue;
+ m_currentLanguage = DIALOGUE_LANGUAGE_SPANISH;
+ break;
+
+ default:
+ rAssertMsg( false, "Language not supported by sound system" );
+ break;
+ }
+
+ if( m_currentLanguage == oldLanguage )
+ {
+ //
+ // Nothing needs to be done
+ //
+ return;
+ }
+
+ for( i = 0; i < s_NumDialogCementFiles; i++ )
+ {
+ GetLoadingManager()->UnregisterCementLibrary( m_soundCementFileHandles[i] );
+ }
+
+ registerDialogueCementFiles( cementFileName );
+
+ m_languageSelected = true;
+}
+
+//=============================================================================
+// daSoundRenderingManager::QueueRadscriptFileLoads
+//=============================================================================
+// Description: Set up the RadScript file loads with the loading manager
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void daSoundRenderingManager::QueueRadscriptFileLoads()
+{
+ HeapMgr()->PushHeap (GMA_AUDIO_PERSISTENT);
+
+ unsigned int i;
+ char filename[100];
+ const char* scriptName;
+
+ //
+ // Queue up the RadScript file loading requests
+ //
+ GetLoadingManager()->AddRequest( FILEHANDLER_SOUND, s_DaSoundTypeInfo.m_Filename, GMA_AUDIO_PERSISTENT );
+
+ for( i = 0; i < NumSoundScripts; i++ )
+ {
+ //
+ // Hack!
+ //
+ // Oops. Need to load the correct dialogue script depending on which language has been
+ // selected
+ //
+ if( i == DIALOGUE_SCRIPT_POSITION )
+ {
+ switch( m_currentLanguage )
+ {
+ case DIALOGUE_LANGUAGE_ENGLISH:
+ scriptName = s_DaSoundScripts[i].m_Filename;
+ break;
+
+ case DIALOGUE_LANGUAGE_FRENCH:
+ scriptName = "\\dialogfr.spt";
+ break;
+
+ case DIALOGUE_LANGUAGE_GERMAN:
+ scriptName = "\\dialogge.spt";
+ break;
+
+ case DIALOGUE_LANGUAGE_SPANISH:
+ scriptName = "\\dialogsp.spt";
+ break;
+
+ default:
+ rAssert( false );
+ scriptName = s_DaSoundScripts[i].m_Filename;
+ break;
+ }
+ }
+ else if( i == NIS_SCRIPT_POSITION )
+ {
+ switch( m_currentLanguage )
+ {
+ case DIALOGUE_LANGUAGE_ENGLISH:
+ scriptName = s_DaSoundScripts[i].m_Filename;
+ break;
+
+ case DIALOGUE_LANGUAGE_FRENCH:
+ scriptName = "\\nisfr.spt";
+ break;
+
+ case DIALOGUE_LANGUAGE_GERMAN:
+ scriptName = "\\nisge.spt";
+ break;
+
+ case DIALOGUE_LANGUAGE_SPANISH:
+ scriptName = "\\nissp.spt";
+ break;
+
+ default:
+ rAssert( false );
+ scriptName = s_DaSoundScripts[i].m_Filename;
+ break;
+ }
+ }
+ else if( i == INTERACTIVE_PROPS_SCRIPT_POSITION )
+ {
+ switch( m_currentLanguage )
+ {
+ case DIALOGUE_LANGUAGE_ENGLISH:
+ scriptName = s_DaSoundScripts[i].m_Filename;
+ break;
+
+ case DIALOGUE_LANGUAGE_FRENCH:
+ scriptName = "\\interactive_propsfr.spt";
+ break;
+
+ case DIALOGUE_LANGUAGE_GERMAN:
+ scriptName = "\\interactive_propsge.spt";
+ break;
+
+ case DIALOGUE_LANGUAGE_SPANISH:
+ scriptName = "\\interactive_propssp.spt";
+ break;
+
+ default:
+ rAssert( false );
+ scriptName = s_DaSoundScripts[i].m_Filename;
+ break;
+ }
+ }
+ else
+ {
+ scriptName = s_DaSoundScripts[i].m_Filename;
+ }
+ sprintf( filename, "%s%s", SCRIPT_DIR, scriptName );
+ GetLoadingManager()->AddRequest( FILEHANDLER_SOUND, filename, GMA_AUDIO_PERSISTENT );
+ }
+
+ HeapMgr()->PopHeap (GMA_AUDIO_PERSISTENT);
+}
+
+//=============================================================================
+// daSoundRenderingManager::LoadTypeInfoFile
+//=============================================================================
+// Description: Start the loading of the type info
+//
+// Parameters: filename - name of type info file
+// fileHandler - completion callback object
+//
+// Return: void
+//
+//=============================================================================
+void daSoundRenderingManager::LoadTypeInfoFile( const char* filename, SoundFileHandler* fileHandler )
+{
+ m_soundFileHandler = fileHandler;
+
+ IRadTypeInfoSystem* pTypeInfoSystem = ::radTypeInfoSystemGet( );
+ rAssert( pTypeInfoSystem != NULL );
+ pTypeInfoSystem->AddRef( );
+ pTypeInfoSystem->LoadTypeInfo
+ (
+ gTuneSound ? GMA_AUDIO_PERSISTENT : GMA_TEMP,
+ filename,
+ daSoundRenderingManager::TypeInfoComplete,
+ (void*)false
+ );
+ pTypeInfoSystem->Release( );
+}
+
+//=============================================================================
+// daSoundRenderingManager::LoadScriptFile
+//=============================================================================
+// Description: Start the loading of a RadScript script file
+//
+// Parameters: filename - name of script file
+// fileHandler - completion callback object
+//
+// Return: void
+//
+//=============================================================================
+void daSoundRenderingManager::LoadScriptFile( const char* filename, SoundFileHandler* fileHandler )
+{
+ const bool* lastScript;
+
+ m_soundFileHandler = fileHandler;
+
+ // Create the script object, if it hasn't been done yet
+ if( m_pScript == NULL )
+ {
+ m_pScript = ::radScriptCreateScript
+ (
+ RADMEMORY_ALLOC_TEMP
+ );
+ rAssert( m_pScript != NULL );
+ m_pScript->AddRef( );
+ }
+
+ m_pScript->SetAllocator( GetThisAllocator( ) );
+
+ //
+ // Pass a flag indicating if this is the last script as user info
+ //
+ if( m_scriptLoadCount < NUM_CHARACTER_SCRIPTS )
+ {
+ m_pScript->SetContext( GetCharacterNamespace( m_scriptLoadCount ) );
+ }
+ else if( m_scriptLoadCount >= NumSoundScripts - NUM_TUNING_SCRIPTS )
+ {
+ m_pScript->SetContext( GetTuningNamespace( ) );
+ }
+ else
+ {
+ m_pScript->SetContext( GetSoundNamespace( ) );
+ }
+
+ if( m_scriptLoadCount == NumSoundScripts - 1 )
+ {
+ lastScript = &LastScriptTrue;
+ }
+ else
+ {
+ lastScript = &LastScriptFalse;
+ }
+
+ // Load the script
+ m_pScript->Load
+ (
+ filename,
+ daSoundRenderingManager::ScriptComplete,
+ (void*)lastScript,
+ RADMEMORY_ALLOC_TEMP
+ );
+ }
+
+//=============================================================================
+// Function: daSoundRenderingManager::GetSoundNamespace
+//=============================================================================
+// Description: Get the sound resource namespace
+//
+// Parameters: none
+//
+// Returns: Returns a pointer to the sound resource namespace
+//
+//-----------------------------------------------------------------------------
+
+IRadNameSpace* daSoundRenderingManager::GetSoundNamespace( void )
+{
+ return m_pResourceNameSpace;
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::GetTuningNamespace
+//=============================================================================
+// Description: Get the sound tuning namespace
+//
+// Parameters: none
+//
+// Returns: Returns a pointer to the sound tuning namespace
+//
+//-----------------------------------------------------------------------------
+
+IRadNameSpace* daSoundRenderingManager::GetTuningNamespace( void )
+{
+ return m_pTuningNameSpace;
+}
+
+//=============================================================================
+// daSoundRenderingManager::GetCharacterNamespace
+//=============================================================================
+// Description: Get the specified character namespace
+//
+// Parameters: index - index into list of namespaces
+//
+// Return: Pointer to the desired namespace
+//
+//=============================================================================
+IRadNameSpace* daSoundRenderingManager::GetCharacterNamespace( unsigned int index )
+{
+ rAssert( index < NUM_CHARACTER_NAMESPACES );
+
+ return( m_pCharacterNameSpace[index] );
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::GetTheListener
+//=============================================================================
+// Description: Get the listener
+//
+// Parameters: none
+//
+// Returns: Returns a pointer to the listener
+//
+//-----------------------------------------------------------------------------
+
+IRadSoundHalListener* daSoundRenderingManager::GetTheListener( void )
+{
+ return ::radSoundHalListenerGet( );
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::GetDynaLoadManager
+//=============================================================================
+// Description: Get the dynamic loading manager
+//
+// Parameters: none
+//
+// Returns: Returns a pointer to the dynamic loading manager
+//
+//-----------------------------------------------------------------------------
+
+daSoundDynaLoadManager* daSoundRenderingManager::GetDynaLoadManager( void )
+{
+ return m_pDynaLoadManager;
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::GetDialogManager
+//=============================================================================
+// Description: Get the dialog manager
+//
+// Parameters: none
+//
+// Returns: Returns a pointer to the dialog manager
+//
+//-----------------------------------------------------------------------------
+
+/*IDaSoundDialogManager* daSoundRenderingManager::GetDialogManager( void )
+{
+ return m_pDialogManager;
+}*/
+
+//=============================================================================
+// Function: daSoundRenderingManager::GetTuner
+//=============================================================================
+// Description: Get the tuner
+//
+// Parameters: none
+//
+// Returns: Returns a pointer to the tuner
+//
+//-----------------------------------------------------------------------------
+
+IDaSoundTuner* daSoundRenderingManager::GetTuner( void )
+{
+ return m_pTuner;
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::GetObjectDataLibrary
+//=============================================================================
+// Description: Get the object data library
+//
+// Parameters: none
+//
+// Returns: Returns a pointer to the object data library
+//
+//-----------------------------------------------------------------------------
+
+/*IDaSoundObjectDataLibrary* daSoundRenderingManager::GetObjectDataLibrary( void )
+{
+ return m_pObjectDataLibrary;
+}*/
+
+//=============================================================================
+// Function: daSoundRenderingManager::GetResourceManager
+//=============================================================================
+// Description: Get the resource manager
+//
+// Parameters: none
+//
+// Returns: Returns a pointer to the resource manager
+//
+//-----------------------------------------------------------------------------
+
+daSoundResourceManager* daSoundRenderingManager::GetResourceManager( void )
+{
+ return m_pResourceManager;
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::GetPlayerManager
+//=============================================================================
+// Description: Get the player manager
+//
+// Parameters: none
+//
+// Returns: Returns a pointer to the player manager
+//
+//-----------------------------------------------------------------------------
+
+daSoundPlayerManager* daSoundRenderingManager::GetPlayerManager( void )
+{
+ return m_pPlayerManager;
+}
+
+//=============================================================================
+// Function: daSoundRenderingManager::TypeInfoComplete
+//=============================================================================
+// Description: Asynchronous file load complete callback. Redundant, but
+// RadScript requires a static function for callback
+//
+// Parameters: pUserData - some user data to pass on
+//
+// Note: This relies on the sound manager being a singleton
+//
+//-----------------------------------------------------------------------------
+void daSoundRenderingManager::TypeInfoComplete( void* pUserData )
+{
+ rAssert( s_Singleton != NULL );
+ s_Singleton->ProcessTypeInfo( pUserData );
+}
+
+//=============================================================================
+// daSoundRenderingManager::ScriptComplete
+//=============================================================================
+// Description: Asynchronous file load complete callback. Redundant, but
+// RadScript requires a static function for callback
+//
+// Parameters: pUserData - some user data to pass on
+//
+// Note: This relies on the sound manager being a singleton
+//
+//=============================================================================
+void daSoundRenderingManager::ScriptComplete( void* pUserData )
+{
+ rAssert( s_Singleton != NULL );
+ s_Singleton->ProcessScript( pUserData );
+}
+
+//=============================================================================
+// daSoundRenderingManager::SoundObjectCreated
+//=============================================================================
+// Description: RadScript callback function called when an object is created.
+// We use it to register the object for loading/unloading
+//
+// Parameters: objName - name of sound resource to load/unload
+//
+// Return: void
+//
+//=============================================================================
+void daSoundRenderingManager::SoundObjectCreated( const char* objName, IRefCount* obj )
+{
+ bool added;
+ rAssert( NULL != dynamic_cast< daSoundResourceData*>( obj ) );
+ daSoundResourceData* resourceObj = static_cast<daSoundResourceData*>( obj );
+
+ //
+ // We only need to preload clips
+ //
+ if( resourceObj->GetStreaming() == false )
+ {
+ added = GetSoundManager()->GetSoundLoader()->AddResourceToCurrentCluster( objName );
+ rAssert( added );
+ }
+ else
+ {
+ volatile int x = 4;
+ }
+}
+
+//=============================================================================
+// daSoundRenderingManager::ProcessTypeInfo
+//=============================================================================
+// Description: Asynchronous file load complete callback.
+//
+// Parameters: ( void* pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void daSoundRenderingManager::ProcessTypeInfo( void* pUserData )
+{
+ m_soundFileHandler->LoadCompleted();
+}
+
+//=============================================================================
+// daSoundRenderingManager::ScriptComplete
+//=============================================================================
+// Description: Asynchronous script load callback
+//
+// Parameters: pUserData - some user data to pass on
+//
+// Return: void
+//
+//=============================================================================
+void daSoundRenderingManager::ProcessScript( void* pUserData )
+{
+ ScriptObjCreateCallback* callbackPtr = NULL;
+ const bool* lastScript = reinterpret_cast<const bool*>( pUserData );
+
+ rAssert( m_pScript != NULL );
+
+ //
+ // Tell the sound manager which sound cluster we'll associate
+ // with the resources in this script
+ //
+ if( m_scriptLoadCount < NumSoundScripts - NUM_TUNING_SCRIPTS )
+ {
+ callbackPtr = SoundObjectCreated;
+
+ if( m_scriptLoadCount < NumClusterNames )
+ {
+ GetSoundManager()->GetSoundLoader()->SetCurrentCluster( s_ScriptClusters[m_scriptLoadCount] );
+ }
+ else
+ {
+ GetSoundManager()->GetSoundLoader()->SetCurrentCluster( static_cast<SoundClusterName>(SC_CAR_BASE + m_scriptLoadCount - NumClusterNames) );
+ }
+ }
+
+ // Execute script file
+
+ unsigned int start = radTimeGetMicroseconds( );
+ m_pScript->Run( callbackPtr );
+ unsigned int finished = radTimeGetMicroseconds( );
+ unsigned int dif = finished - start;
+
+ gTotalMicrosecondsWastedParsingScripts += dif;
+
+ if ( dif > 1000 )
+ {
+ //rTunePrintf( "\n\nAUDIO: Holy !@#$ script took [%d] ms to parse\n\n", dif / 1000 );
+ }
+
+ m_pScript->UnLoad( );
+
+ if( *lastScript )
+ {
+ rReleasePrintf( "\n\nAUDIO: Wasted [%d] ms of load time parsing scripts\n\n", gTotalMicrosecondsWastedParsingScripts / 1000);
+
+ // Free the script
+ rAssert( m_pScript != NULL );
+ m_pScript->Release( );
+ m_pScript = NULL;
+
+ //
+ // At this point the sound resources should be finalized, lets lock them down
+ // and then intialize some systems.
+ //
+
+ GetTuner( )->Initialize( );
+
+ // Initialize the dialog system
+
+ SoundManager::GetInstance( )->m_dialogCoordinator->Initialize( );
+
+ Sound::daSoundResourceManager::GetInstance( )->SetResourceLockdown( true );
+
+ m_pPlayerManager->Initialize( );
+
+ // We are now fully initialized
+ m_IsInitialized = true;
+
+ if ( ! gTuneSound )
+ {
+ radScriptUnLoadAllTypeInfo( );
+ }
+ }
+
+ ++m_scriptLoadCount;
+
+ m_soundFileHandler->LoadCompleted();
+}
+
+
+//=============================================================================
+// Public Functions
+//=============================================================================
+
+//=============================================================================
+// Function: ::daSoundRenderingManagerCreate
+//=============================================================================
+// Description: Create the sound manager
+//
+// Parameters: allocator - the memory allocator to use
+//
+// Preconditions:
+// In order to manage memory fragmentation issues, and to
+// allow a timely intialization of sound, this command should
+// be executed before any frontend or ingame structures have
+// been created.
+//
+// Postconditions:
+// This will generate any singleton classes related to the Dark
+// Angel sound system. It will register any appropriate
+// cement files to give access to sound, and it will parse
+// any sound scripts so that the sound system will be ready
+// to start recieving sound events.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundRenderingManagerCreate( radMemoryAllocator allocator )
+{
+ rAssert( daSoundRenderingManager::GetInstance( ) == NULL );
+
+ // Create the sound manager
+ daSoundRenderingManager* pSoundManager = new ( allocator ) daSoundRenderingManager( );
+ rAssert( pSoundManager != NULL );
+ pSoundManager->AddRef( );
+ rAssert( pSoundManager == daSoundRenderingManager::GetInstance( ) );
+
+ rDebugChannelInitialize( GMA_MUSIC );
+ //rDebugChannelEnable( radmusic::debug_channel );
+
+ //
+ // Initialize some usefull systems
+ //
+ ::radScriptInitialize( allocator );
+#ifndef FINAL
+ if( gTuneSound )
+ {
+ ::radRemoteScriptInitialize( allocator );
+ }
+#endif
+
+ gpBTreeNodePool = (radObjectBTreeNode*) radMemoryAlloc(
+ allocator,
+ MAX_BTREE_NODES * sizeof( radObjectBTreeNode ) );
+
+ ::memset( gpBTreeNodePool, 0, MAX_BTREE_NODES * sizeof( radObjectBTreeNode ) );
+
+ radMemoryMonitorIdentifyAllocation( gpBTreeNodePool, "Global BTree Node Pool" );
+
+ radObjectBTree::Initialize( gpBTreeNodePool, MAX_BTREE_NODES );
+ // Initialize
+
+ SoundNucleusInitialize( allocator );
+}
+
+//=============================================================================
+// Function: ::daSoundRenderingManagerGet
+//=============================================================================
+// Description: Get a pointer to the sound manager singleton class
+//
+//-----------------------------------------------------------------------------
+
+daSoundRenderingManager* daSoundRenderingManagerGet( void )
+{
+ rAssert( daSoundRenderingManager::GetInstance( ) != NULL );
+ return
+ (
+ static_cast< daSoundRenderingManager* >( daSoundRenderingManager::GetInstance( ) )
+ );
+}
+
+void daSoundRenderingManager::FilePerformanceEvent(
+ bool start,
+ const char * pFile,
+ unsigned int bytes )
+{
+ daSoundRenderingManager * pThis = daSoundRenderingManager::GetInstance( );
+
+ unsigned int now = radTimeGetMilliseconds( );
+
+ if ( start )
+ {
+ pThis->m_LastPerformanceEventTime = now;
+
+ if ( CommandLineOptions::Get( CLO_AUDIO_LOADING_SPEW ) )
+ {
+ rTunePrintf( "<<START>> Async Loading: (Audio) %s Bytes: 0x%x\n", pFile, bytes );
+ }
+ }
+ else
+ {
+ unsigned int dif = now - pThis->m_LastPerformanceEventTime;
+
+ if ( CommandLineOptions::Get( CLO_AUDIO_LOADING_SPEW ) )
+ {
+ rTunePrintf( "<< END >> Async Loading: (Audio) %s (%d msecs)\n", pFile, dif );
+ }
+ }
+}
+
+void daSoundRenderingManager::Render( void )
+{
+ if ( gDaSoundStats )
+ {
+ m_pPlayerManager->Render( );
+ }
+}
+
+//=============================================================================
+// Function: ::daSoundRenderingManagerTerminate
+//=============================================================================
+// Description: Terminate the sound system
+//
+//-----------------------------------------------------------------------------
+
+void daSoundRenderingManagerTerminate( void )
+{
+ rAssert( daSoundRenderingManager::GetInstance( ) != NULL );
+ daSoundRenderingManager::GetInstance( )->Terminate( );
+ daSoundRenderingManager::GetInstance( )->Release( );
+ rAssert( daSoundRenderingManager::GetInstance( ) == NULL );
+
+ SoundNucleusTerminate( );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// daSoundRenderingManager::registerDialogueCementFiles
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* cementFilename )
+//
+// Return: void
+//
+//=============================================================================
+void daSoundRenderingManager::registerDialogueCementFiles( const char* cementFilename )
+{
+#if defined( RAD_XBOX )
+ char dialogNameBuffer[ 16 ];
+ int i = 0;
+
+ strcpy( dialogNameBuffer, cementFilename );
+ char* numberPosition = strchr( dialogNameBuffer, '?' );
+ for ( unsigned int j = 0; j < s_NumDialogCementFiles; j++ )
+ {
+ *numberPosition = j + '0';
+ m_soundCementFileHandles[i++] = GetLoadingManager()->RegisterCementLibrary( dialogNameBuffer );
+ }
+#else
+ rAssert( s_NumDialogCementFiles == 1 );
+ m_soundCementFileHandles[0] = GetLoadingManager()->RegisterCementLibrary( cementFilename );
+#endif
+}
+
+} // Sound Namespace
+
diff --git a/game/code/sound/soundrenderer/soundrenderingmanager.h b/game/code/sound/soundrenderer/soundrenderingmanager.h
new file mode 100644
index 0000000..a77ac7c
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundrenderingmanager.h
@@ -0,0 +1,187 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundrenderingmanager.hpp
+//
+// Subsystem: Dark Angel - Sound Rendering Manager System
+//
+// Description: Description of the DA sound manager
+//
+// Revisions:
+// + Created October 2, 2001 -- breimer
+//
+//=============================================================================
+
+#ifndef _SOUNDRENDERINGMANAGER_HPP
+#define _SOUNDRENDERINGMANAGER_HPP
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <radfile.hpp>
+#include <enums.h>
+#include <radsound.hpp>
+#include <radscript.hpp>
+
+#include <sound/soundrenderer/idasoundtuner.h>
+#include <sound/soundrenderer/soundresourcemanager.h>
+#include <sound/soundrenderer/playermanager.h>
+
+//=============================================================================
+// Global namespace forward declarations
+//=============================================================================
+
+
+class SoundFileHandler;
+
+//=============================================================================
+// Define Owning Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Prototypes
+//=============================================================================
+
+class daSoundRenderingManager;
+class daSoundDynaLoadManager;
+
+//=============================================================================
+// Class Declarations
+//=============================================================================
+
+static const unsigned int NUM_CHARACTER_NAMESPACES = 5;
+
+//
+// Language enumeration
+//
+enum DialogueLanguage
+{
+ DIALOGUE_LANGUAGE_ENGLISH,
+ DIALOGUE_LANGUAGE_FRENCH,
+ DIALOGUE_LANGUAGE_GERMAN,
+ DIALOGUE_LANGUAGE_SPANISH
+};
+
+//
+// The sound manger
+//
+class daSoundRenderingManager : public radRefCount
+{
+public:
+ IMPLEMENT_REFCOUNTED( "daSoundManager" );
+
+ //
+ // Constructor and destructor
+ //
+ daSoundRenderingManager( );
+ virtual ~daSoundRenderingManager( );
+
+ //
+ // Get the singleton
+ //
+ static daSoundRenderingManager* GetInstance( void );
+
+ //
+ // Terminate
+ //
+ void Terminate( void );
+
+ void QueueCementFileRegistration();
+ void QueueRadscriptFileLoads();
+ void LoadTypeInfoFile( const char* filename, SoundFileHandler* fileHandler );
+ void LoadScriptFile( const char* filename, SoundFileHandler* fileHandler );
+
+ void SetLanguage( Scrooby::XLLanguage language );
+
+ void ProcessTypeInfo( void* pUserData );
+ void ProcessScript( void* pUserData );
+
+ //
+ // IDaSoundManager
+ //
+ void Initialize( void );
+ bool IsInitialized( void );
+ void Service( void );
+ void ServiceOncePerFrame( unsigned int elapsedTime );
+ void Render( void );
+
+ IRadNameSpace* GetSoundNamespace( void );
+ IRadNameSpace* GetTuningNamespace( void );
+ IRadNameSpace* GetCharacterNamespace( unsigned int index );
+ IRadSoundHalListener* GetTheListener( void );
+
+ daSoundDynaLoadManager* GetDynaLoadManager( void );
+ IDaSoundTuner* GetTuner( void );
+ daSoundResourceManager* GetResourceManager( void );
+ daSoundPlayerManager* GetPlayerManager( void );
+
+protected:
+
+ static void TypeInfoComplete( void* pUserData );
+ static void ScriptComplete( void* pUserData );
+ static void SoundObjectCreated( const char* objName, IRefCount* obj );
+
+private:
+
+ static void FilePerformanceEvent( bool start, const char * pFile, unsigned int bytes );
+
+ void registerDialogueCementFiles( const char* cementFilename );
+
+ // The singleton instance
+ static daSoundRenderingManager* s_Singleton;
+
+ IRadScript* m_pScript;
+ bool m_IsInitialized;
+
+ //
+ // Our namespaces
+ //
+ IRadNameSpace* m_pResourceNameSpace;
+ IRadNameSpace* m_pTuningNameSpace;
+
+ IRadNameSpace* m_pCharacterNameSpace[NUM_CHARACTER_NAMESPACES];
+
+ //
+ // Store the various related systems
+ //
+ daSoundDynaLoadManager* m_pDynaLoadManager;
+ IDaSoundTuner* m_pTuner;
+ daSoundResourceManager* m_pResourceManager;
+ daSoundPlayerManager* m_pPlayerManager;
+
+ //
+ // Loading system callback
+ //
+ SoundFileHandler* m_soundFileHandler;
+
+ //
+ // Cement file handles, in case we want to release them
+ //
+#ifdef RAD_XBOX
+ static const unsigned int NUM_SOUND_CEMENT_FILES = 12;
+#else
+ static const unsigned int NUM_SOUND_CEMENT_FILES = 7;
+#endif
+ unsigned int m_soundCementFileHandles[NUM_SOUND_CEMENT_FILES];
+
+ //
+ // Script loading count, so we can tell which namespace to put stuff in
+ //
+ unsigned int m_scriptLoadCount;
+
+ unsigned int m_LastPerformanceEventTime;
+
+ //
+ // Language
+ //
+ DialogueLanguage m_currentLanguage;
+ bool m_languageSelected;
+};
+
+} // Sound Namespace
+
+#endif //_SOUNDRENDERINGMANAGER_HPP
diff --git a/game/code/sound/soundrenderer/soundresource.cpp b/game/code/sound/soundrenderer/soundresource.cpp
new file mode 100644
index 0000000..ff39f81
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundresource.cpp
@@ -0,0 +1,555 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundresource.cpp
+//
+// Subsystem: Dark Angel - Sound resources
+//
+// Description: Implements sound resources
+//
+// Modification History:
+// + Created October 11, 2001 -- aking
+//
+//=============================================================================
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <raddebug.hpp>
+
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundrenderer/soundplayer.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/playermanager.h>
+
+#include <sound/soundrenderer/soundresourcemanager.h>
+
+#include <sound/soundrenderer/idasoundresource.h>
+#include <sound/soundrenderer/soundresource.h>
+
+#include <sound/soundmanager.h>
+#include <memory/srrmemory.h>
+
+//=============================================================================
+// Static Variables
+//=============================================================================
+
+//=============================================================================
+// daSoundResourceData Implementation
+//=============================================================================
+
+//=============================================================================
+// Function: daSoundResourceData::daSoundResourceData
+//=============================================================================
+// Description: Constructor
+//
+//-----------------------------------------------------------------------------
+
+
+inline unsigned char vol_f_to_c( float f )
+{
+ return (unsigned char) radSoundFloatToUInt( f * 255.0f );
+}
+
+inline float vol_c_to_f( unsigned char c )
+{
+ return radSoundUIntToFloat( (unsigned int) c ) / 255.0f;
+}
+
+inline unsigned short pitch_f_to_s( float f )
+{
+ return (unsigned short) radSoundFloatToUInt( f * 6553.0f );
+}
+
+inline float pitch_s_to_f( unsigned short c )
+{
+ return radSoundUIntToFloat( (unsigned short) c ) / 6553.0f;
+}
+
+const int FLAG_LOOPING = 1 << 0;
+const int FLAG_STREAMING = 1 << 1;
+
+daSoundResourceData::daSoundResourceData( )
+{
+ m_NumFiles = 0;
+
+ m_MinTrim = vol_f_to_c( 1.0f );
+ m_MaxTrim = vol_f_to_c( 1.0f );
+ m_MinPitch = pitch_f_to_s( 1.0f );
+ m_MaxPitch = pitch_f_to_s( 1.0f );
+
+ m_SoundGroup = Sound::MASTER;
+ m_CaptureCount = 0;
+
+ m_Flags = 0;
+
+ m_pFileIds = 0;
+}
+
+//=============================================================================
+// Function: daSoundResourceData::~daSoundResourceData
+//=============================================================================
+// Description: Destructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundResourceData::~daSoundResourceData( )
+{
+
+}
+
+//=============================================================================
+// Function: daSoundResourceData::AddFilename
+//=============================================================================
+// Description: Add a file to this resource
+//
+// Parameters: newFileName - the new file name
+// trim - the trim for this file
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceData::AddFilename
+(
+ const char* newFileName,
+ float trim
+)
+{
+ rAssert( false == Sound::daSoundResourceManager::GetInstance( )->GetResourceLockdown( ) );
+
+ // very lazy indeed.
+
+ if ( m_pFileIds == 0 )
+ {
+ m_pFileIds = (FileId*) radMemoryAlloc( GMA_DEFAULT, sizeof( FileId ) * DASound_MaxNumSoundResourceFiles );
+ }
+
+ rAssert( Sound::daSoundResourceManager::GetInstance( ) != NULL );
+ rAssert( m_NumFiles < DASound_MaxNumSoundResourceFiles );
+
+ m_pFileIds[ m_NumFiles ].m_pName = (char*) radMemoryAlloc( GMA_DEFAULT, strlen( newFileName ) + 1 );
+ strcpy( m_pFileIds[ m_NumFiles ].m_pName, newFileName );
+ m_NumFiles++;
+}
+
+
+//=============================================================================
+// Function: daSoundResourceData::GetFilenameAt
+//=============================================================================
+// Description: Get the resource file at the given index
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceData::GetFileNameAt( unsigned int index, char* buffer, unsigned int max )
+{
+ rTuneAssert( false == Sound::daSoundResourceManager::GetInstance( )->GetResourceLockdown( ) );
+ rTuneAssert( index < m_NumFiles );
+
+ strcpy( buffer, m_pFileIds[ index ].m_pName );
+}
+
+void daSoundResourceData::GetFileKeyAt( unsigned int index, char* buffer, unsigned int max )
+{
+ rTuneAssert( index < m_NumFiles );
+ KeyToStringKey32( buffer, m_pFileIds[ index ].m_Key );
+}
+
+//=============================================================================
+// Function: daSoundResourceData::SetPitchRange
+//=============================================================================
+// Description: Set the pitch range for this resource
+//
+// Parameters: minPitch - the minimum pitch value
+// maxPitch - the maximum pitch value
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceData::SetPitchRange
+(
+ Sound::daPitchValue minPitch,
+ Sound::daPitchValue maxPitch
+)
+{
+ m_MinPitch = pitch_f_to_s( minPitch );
+ m_MaxPitch = pitch_f_to_s( maxPitch );
+}
+
+//=============================================================================
+// Function: daSoundResourceData::GetPitchRange
+//=============================================================================
+// Description: Get the pitch range for this resource
+//
+// Parameters: *MinPitch - (out )the minimum pitch value
+// *MaxPitch - (out) the maximum pitch value
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceData::GetPitchRange
+(
+ Sound::daPitchValue* pMinPitch,
+ Sound::daPitchValue* pMaxPitch
+)
+{
+ if( pMinPitch != NULL )
+ {
+ *pMinPitch = pitch_s_to_f( m_MinPitch );
+ }
+ if( pMaxPitch != NULL )
+ {
+ *pMaxPitch = pitch_s_to_f( m_MaxPitch );
+ }
+}
+
+//=============================================================================
+// Function: daSoundResourceData::SetTrimRange
+//=============================================================================
+// Description: Set the trim range for this resource
+//
+// Parameters: minTrim - the minimum trim value
+// maxTrim - the maximum trim value
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceData::SetTrimRange
+(
+ float minTrim,
+ float maxTrim
+)
+{
+ m_MinTrim = vol_f_to_c( minTrim );
+ m_MaxTrim = vol_f_to_c( maxTrim );
+}
+
+//=============================================================================
+// Function: daSoundResourceData::SetTrim
+//=============================================================================
+// Description: Set the trim for this resource
+//
+// Parameters: trim - the trim value
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceData::SetTrim
+(
+ float trim
+)
+{
+ m_MinTrim = vol_f_to_c( trim );
+ m_MaxTrim = vol_f_to_c( trim );
+}
+
+//=============================================================================
+// Function: daSoundResourceData::GetTrimRange
+//=============================================================================
+// Description: Get the trim range for this resource
+//
+// Parameters: *MinTrim - (out )the minimum trim value
+// *MaxTrim - (out) the maximum trim value
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceData::GetTrimRange
+(
+ float* pMinTrim,
+ float* pMaxTrim
+)
+{
+ if( pMinTrim != NULL )
+ {
+ *pMinTrim = vol_c_to_f( m_MinTrim );
+ }
+ if( pMaxTrim != NULL )
+ {
+ *pMaxTrim = vol_c_to_f( m_MaxTrim );
+ }
+}
+
+//=============================================================================
+// Function: daSoundResourceData::SetStreaming
+//=============================================================================
+// Description: Set this as a streaming resource
+//
+// Parameters: streaming - should streaming be on or off
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceData::SetStreaming( bool streaming )
+{
+ if( streaming )
+ {
+ m_Flags |= FLAG_STREAMING;
+ }
+ else
+ {
+ m_Flags &= ~FLAG_STREAMING;
+ }
+
+ if( IsCaptured( ) )
+ {
+ rReleaseString
+ (
+ "Streaming will not immediately "
+ "affect a captured resource\n"
+ );
+ }
+}
+
+//=============================================================================
+// Function: daSoundResourceData::GetStreaming
+//=============================================================================
+// Description: Get whether or not this is a streaming resource
+//
+// Parameters: none
+//
+// Returns: Returns true if this resource streams, otherwise false
+//
+//-----------------------------------------------------------------------------
+
+bool daSoundResourceData::GetStreaming( void )
+{
+ return ( m_Flags & FLAG_STREAMING ) != 0;
+}
+
+//=============================================================================
+// Function: daSoundResourceData::SetLooping
+//=============================================================================
+// Description: Set this as a looping resource
+//
+// Parameters: looping - should looping be on or off
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceData::SetLooping( bool looping )
+{
+ if( looping )
+ {
+ m_Flags |= FLAG_LOOPING;
+ }
+ else
+ {
+ m_Flags &= ~FLAG_LOOPING;
+ }
+
+ if( IsCaptured( ) )
+ {
+ rReleaseString
+ (
+ "Looping will not immediately "
+ "affect a captured resource\n"
+ );
+ }
+}
+
+//=============================================================================
+// Function: daSoundResourceData::GetLooping
+//=============================================================================
+// Description: Get whether or not this is a looping resource
+//
+// Parameters: none
+//
+// Returns: Returns true if this resource loops, otherwise false
+//
+//-----------------------------------------------------------------------------
+
+bool daSoundResourceData::GetLooping( void )
+{
+ return ( m_Flags & FLAG_LOOPING ) != 0;
+}
+
+//=============================================================================
+// Function: daSoundResourceData::GetType
+//=============================================================================
+// Description: Find out what kind of resource this is
+//
+// Parameters: none
+//
+// Returns: Returns the type of resource
+//
+//-----------------------------------------------------------------------------
+
+IDaSoundResource::Type daSoundResourceData::GetType
+(
+ void
+)
+{
+ IDaSoundResource::Type resourceType = UNKNOWN;
+
+ if( GetStreaming( ) )
+ {
+ resourceType = STREAM;
+ }
+ else
+ {
+ resourceType = CLIP;
+ }
+
+ return resourceType;
+}
+
+//=============================================================================
+// Function: daSoundResourceData::SetSoundGroup
+//=============================================================================
+// Description: Set the sound group that this resource belongs to
+//
+// Parameters: soundGroup - the sound group to associate with this resource
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceData::SetSoundGroup
+(
+ Sound::daSoundGroup soundGroup
+)
+{
+ m_SoundGroup = soundGroup;
+}
+
+//=============================================================================
+// Function: daSoundResourceData::GetSoundGroup
+//=============================================================================
+// Description: Get the sound group that this resource belongs to
+//
+// Parameters: none
+//
+// Returns: Returns the sound group that this resource belongs to.
+//
+//-----------------------------------------------------------------------------
+
+Sound::daSoundGroup daSoundResourceData::GetSoundGroup( void )
+{
+ return static_cast<Sound::daSoundGroup>(m_SoundGroup);
+}
+
+//=============================================================================
+// Function: daSoundResourceData::CaptureResource
+//=============================================================================
+// Description: Capture this resource
+//
+// Parameters: none
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceData::CaptureResource( void )
+{
+ rAssert( Sound::daSoundResourceManager::GetInstance( ) != NULL );
+ bool wasCaptured = IsCaptured( );
+ ++m_CaptureCount;
+ if( !wasCaptured )
+ {
+ // Inform the resource manager
+ Sound::daSoundResourceManager::GetInstance( )->
+ AllocateResource( this );
+ }
+}
+
+//=============================================================================
+// Function: daSoundResourceData::IsCaptured
+//=============================================================================
+// Description: Is this resource captured?
+//
+// Parameters: none
+//
+// Returns: Returns true if the resource is captured, otherwise false.
+//
+//-----------------------------------------------------------------------------
+
+bool daSoundResourceData::IsCaptured( void )
+{
+ return( m_CaptureCount > 0 );
+}
+
+//=============================================================================
+// Function: daSoundResourceData::ReleaseResource
+//=============================================================================
+// Description: Release this resource
+//
+// Parameters: none
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceData::ReleaseResource( void )
+{
+ rAssertMsg( m_CaptureCount > 0, "Cannot release uncaptured resource" );
+ rAssert( Sound::daSoundResourceManager::GetInstance( ) != NULL );
+
+ --m_CaptureCount;
+ if( m_CaptureCount == 0 )
+ {
+ // Inform the resource manager
+ Sound::daSoundResourceManager::GetInstance( )->
+ DeallocateResource( this );
+ }
+}
+
+//=============================================================================
+// Function: daSoundResourceData::ReleaseResource
+//=============================================================================
+// Description: Plays this resource
+//
+// Notes: !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+// THIS IS FOR COMPOSERS ONLY. DO NOT USE IT.
+// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceData::Play( void )
+{
+ if( Sound::daSoundRenderingManagerGet( )->GetResourceManager( )->GetResourceLockdown( ) )
+ {
+ // Play it
+ // DO NOT USE THIS COMMAND IF YOU ARE NOT IN RADTUNER / SOUNDTUNER / etc.
+ Sound::daSoundClipStreamPlayer* pPlayer = NULL;
+ Sound::daSoundRenderingManagerGet( )->GetPlayerManager( )->CaptureFreePlayer
+ (
+ &pPlayer,
+ this,
+ false );
+
+ if( pPlayer != NULL )
+ {
+ pPlayer->Play( );
+ }
+ }
+ else
+ {
+ // You shouldn't have left play commands in the composer script
+ rDebugString( "Can't play sounds before resources are locked down.\n" );
+ }
+}
+
+//=============================================================================
+// Public functions
+//=============================================================================
+
+void daSoundResourceData::AddRef( void )
+{
+ //Sound::daSoundResourceManager::GetInstance( )->AddRef( );
+}
+
+void daSoundResourceData::Release( void )
+{
+ //Sound::daSoundResourceManager::GetInstance( )->Release( );
+}
+
+
+
diff --git a/game/code/sound/soundrenderer/soundresource.h b/game/code/sound/soundrenderer/soundresource.h
new file mode 100644
index 0000000..30abb0f
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundresource.h
@@ -0,0 +1,152 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundresource.hpp
+//
+// Subsystem: Dark Angel - Sound Resources
+//
+// Description: Defines sound resource
+//
+// Modification History:
+// + Created October 11, 2001 -- aking
+//
+//=============================================================================
+
+#ifndef _SOUNDRESOURCE_HPP
+#define _SOUNDRESOURCE_HPP
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <radlinkedclass.hpp>
+
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundrenderer/idasoundresource.h>
+
+//=============================================================================
+// Prototypes
+//=============================================================================
+
+class daSoundResourceData;
+
+//=============================================================================
+// Class Declarations
+//=============================================================================
+
+//
+// This contains the type of resource.
+//
+class daSoundResourceData
+ :
+ public IDaSoundResource
+{
+public:
+
+ virtual void AddRef( void );
+ virtual void Release( void );
+
+ //
+ // Constructor and destructor
+ //
+ daSoundResourceData( );
+ virtual ~daSoundResourceData( );
+
+ //
+ // IDaSoundResourceData and IDaSoundResource
+ //
+ virtual void AddFilename
+ (
+ const char* newFileName,
+ float trim
+ );
+
+ inline virtual unsigned int GetNumFiles( void );
+
+ virtual void GetFileNameAt( unsigned int index, char* buffer, unsigned int max );
+ virtual void GetFileKeyAt( unsigned int index, char * buffer, unsigned int max );
+
+ virtual void SetPitchRange
+ (
+ float minPitch,
+ float maxPitch
+ );
+ virtual void GetPitchRange
+ (
+ float* pMinPitch,
+ float* pMaxPitch
+ );
+
+ virtual void SetTrimRange
+ (
+ float minTrim,
+ float maxTrim
+ );
+ virtual void GetTrimRange
+ (
+ float* pMinTrim,
+ float* pMaxTrim
+ );
+
+ virtual void SetTrim( float trim );
+
+ virtual void SetStreaming( bool streaming );
+ virtual bool GetStreaming( void );
+
+ virtual void SetLooping( bool looping );
+ virtual bool GetLooping( void );
+
+ virtual Type GetType( void );
+ virtual void SetSoundGroup( Sound::daSoundGroup soundGroup );
+ virtual Sound::daSoundGroup GetSoundGroup( void );
+
+ virtual void CaptureResource( void );
+ virtual bool IsCaptured( void );
+ virtual void ReleaseResource( void );
+
+ // COMPOSERS ONLY!!
+ virtual void Play( void );
+
+ //
+ // The pitch variation
+ //
+ short m_MinPitch;
+ short m_MaxPitch;
+
+ //
+ // The sound files for this resource
+ //
+
+ union FileId {
+ char * m_pName;
+ radKey32 m_Key;
+ };
+
+ FileId * m_pFileIds;
+
+
+ //
+ // The trim variation
+ //
+ unsigned char m_MinTrim;
+ unsigned char m_MaxTrim;
+
+ unsigned char m_Flags;
+
+ unsigned char m_NumFiles;
+
+ //
+ // Hold a capture counter
+ //
+ unsigned char m_CaptureCount;
+ unsigned char m_SoundGroup;
+};
+
+inline unsigned int daSoundResourceData::GetNumFiles( void )
+{
+ return m_NumFiles;
+}
+
+#endif //_SOUNDRESOURCE_HPP
+
diff --git a/game/code/sound/soundrenderer/soundresourcemanager.cpp b/game/code/sound/soundrenderer/soundresourcemanager.cpp
new file mode 100644
index 0000000..5fdc5f6
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundresourcemanager.cpp
@@ -0,0 +1,527 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundresourcemanager.cpp
+//
+// Subsystem: Dark Angel - Sound Resource Manager
+//
+// Description: Implementation of the sound resource manager
+//
+// Revisions:
+// + Created October 11, 2001 -- breimer
+//
+//=============================================================================
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <raddebug.hpp>
+#include <radkey.hpp>
+#include <radobjectbtree.hpp>
+#include <radnamespace.hpp>
+#include <radsound.hpp>
+
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundrenderer/soundresource.h>
+#include <sound/soundrenderer/soundallocatedresource.h>
+#include <sound/soundrenderer/soundresourcemanager.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+
+#include <memory/srrmemory.h>
+
+//=============================================================================
+// Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Static Variables
+//=============================================================================
+
+daSoundResourceManager* daSoundResourceManager::s_pSingleton = NULL;
+
+//=============================================================================
+// Constants
+//=============================================================================
+
+//
+// The maximum number of sound resource files (not the resources, but the
+// the actual files)
+//
+const unsigned int MaxNumResourceFiles = 512;
+
+#if defined( RAD_XBOX ) || defined( RAD_WIN32 )
+ const IRadSoundHalAudioFormat::Encoding DEFAULT_ENCODING = IRadSoundHalAudioFormat::PCM;
+#elif defined RAD_PS2
+ const IRadSoundHalAudioFormat::Encoding DEFAULT_ENCODING = IRadSoundHalAudioFormat::VAG;
+#elif defined RAD_GAMECUBE
+ const IRadSoundHalAudioFormat::Encoding DEFAULT_ENCODING = IRadSoundHalAudioFormat::PCM_BIGENDIAN;
+#else
+ Uh oh.
+#endif
+
+//=============================================================================
+// Class Implementations
+//=============================================================================
+
+//=============================================================================
+// daSoundResourceManager Implementation
+//=============================================================================
+
+
+//=============================================================================
+// Function: daSoundResourceManager::daSoundResourceManager
+//=============================================================================
+// Description: Constructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundResourceManager::daSoundResourceManager( )
+ :
+ radRefCount( 0 ),
+ m_pSoundNamespace( NULL ),
+ m_ResourceLockdown( false ),
+ m_secondaryNamespace( NULL )
+
+{
+ m_pFileIdMemory = 0;
+
+ m_NumResourceDatas = 0;
+
+ // Create the singleton
+ rAssert( s_pSingleton == NULL );
+ s_pSingleton = this;
+
+ // Remember the sound namespace
+ m_pSoundNamespace = Sound::daSoundRenderingManagerGet( )->GetSoundNamespace( );
+ if( m_pSoundNamespace != NULL )
+ {
+ m_pSoundNamespace->AddRef( );
+ }
+
+ m_xIOL_AllocatedResources = new ( GetThisAllocator( )) radObjectBTree( );
+}
+
+//=============================================================================
+// Function: daSoundResourceManager::~daSoundResourceManager
+//=============================================================================
+// Description: Destructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundResourceManager::~daSoundResourceManager( )
+{
+ // Release the allocated sound resources
+ m_xIOL_AllocatedResources = NULL;
+
+ // Release the sound namespace
+ if( m_pSoundNamespace != NULL )
+ {
+ m_pSoundNamespace->Release( );
+ m_pSoundNamespace = NULL;
+ }
+
+ // Remove the singleton
+ rAssert( s_pSingleton != NULL );
+ s_pSingleton = NULL;
+}
+
+//=============================================================================
+// Function: daSoundResourceManager::AllocateResource
+//=============================================================================
+// Description: Allocate a resource
+//
+// Parameters: pResource - the resource to allocate
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceManager::AllocateResource
+(
+ IDaSoundResource* pResource
+)
+{
+ rAssert( pResource != NULL );
+
+ //
+ // Allocate this resource
+ //
+ radKey32 soundKey = (radKey32) pResource;
+
+ // Find out if it already exists...
+ daSoundAllocatedResource* pAllocatedResource =
+ FindAllocatedResource( pResource );
+ if( pAllocatedResource == NULL )
+ {
+ pAllocatedResource = new ( GetThisAllocator( ) ) daSoundAllocatedResource;
+ pAllocatedResource->AddRef( );
+ pAllocatedResource->Initialize( pResource );
+
+ // Add it to our allocated resource manager
+ m_xIOL_AllocatedResources->AddObject (
+ soundKey,
+ pAllocatedResource
+ );
+
+ // Release our version
+ pAllocatedResource->Release( );
+ }
+
+ rAssert( pAllocatedResource != NULL );
+}
+
+//=============================================================================
+// Function: daSoundResourceManager::DeallocateResource
+//=============================================================================
+// Description: Allocate a resource
+//
+// Parameters: pResource - the resource to allocate
+//
+//-----------------------------------------------------------------------------
+
+void daSoundResourceManager::DeallocateResource
+(
+ IDaSoundResource* pResource
+)
+{
+ rAssert( pResource != NULL );
+
+ //
+ // Deallocate the resource
+ //
+ radKey32 soundKey = (radKey32) pResource;
+
+ bool result = m_xIOL_AllocatedResources->RemoveObject( soundKey );
+ rAssertMsg( result, "Resource deallocated prematurely?" );
+}
+
+//=============================================================================
+// Function: daSoundResourceManager::FindResource
+//=============================================================================
+// Description: Find a resource.
+//
+// Parameters: resourceName - the name of the resource to find
+//
+// Returns: Returns a pointer to the appropriate resource or NULL if none
+// found.
+//
+//-----------------------------------------------------------------------------
+
+IDaSoundResource* daSoundResourceManager::FindResource
+(
+ daResourceName resourceName
+)
+{
+ IDaSoundResource* pResource;
+
+ pResource = reinterpret_cast< IDaSoundResource* >(
+ m_pSoundNamespace->GetInstance( resourceName ) );
+
+ if( ( pResource == NULL ) && ( m_secondaryNamespace != NULL ) )
+ {
+ // Search secondary namespace
+ pResource = reinterpret_cast< IDaSoundResource* >
+ (
+ m_secondaryNamespace->GetInstance( resourceName )
+ );
+ }
+
+ return pResource;
+}
+
+//=============================================================================
+// Function: daSoundResourceManager::FindResource
+//=============================================================================
+// Description: Find a resource by its key.
+//
+// Parameters: resourceKey - the key for the resource to find
+//
+// Returns: Returns a pointer to the appropriate resource or NULL if none
+// found.
+//
+//-----------------------------------------------------------------------------
+
+IDaSoundResource* daSoundResourceManager::FindResource
+(
+ daResourceKey resourceKey
+)
+{
+ IDaSoundResource* pResource;
+
+ pResource = reinterpret_cast< IDaSoundResource* >
+ (
+ m_pSoundNamespace->GetInstance( resourceKey )
+ );
+ if( ( pResource == NULL ) && ( m_secondaryNamespace != NULL ) )
+ {
+ // Search secondary namespace
+ pResource = reinterpret_cast< IDaSoundResource* >
+ (
+ m_secondaryNamespace->GetInstance( resourceKey )
+ );
+ }
+
+ return pResource;
+}
+
+//=============================================================================
+// Function: daSoundResourceManager::FindAllocatedResource
+//=============================================================================
+// Description: Find an allocated resource
+//
+// Parameters: pResource - the resource associated with an allocated resource
+//
+// Note: Calling this command on a given resource is not guarenteed
+// to always return the same allocated resource. In fact,
+// it randomly choose which of the possible file variations
+// it should use.
+//
+//-----------------------------------------------------------------------------
+
+daSoundAllocatedResource* daSoundResourceManager::FindAllocatedResource
+(
+ IDaSoundResource* pResource
+)
+{
+ rAssert( pResource != NULL );
+
+ // Generate a key based on the resource
+ radKey32 soundKey = (radKey32) pResource;
+
+ // Find the allocated resource for the resource
+ daSoundAllocatedResource* pAllocatedResource =
+ reinterpret_cast< daSoundAllocatedResource* >
+ (
+ m_xIOL_AllocatedResources->FindObject( soundKey )
+ );
+
+ return pAllocatedResource;
+}
+
+//=============================================================================
+// Function: daSoundResourceManager::SetResourceLockdown
+//=============================================================================
+// Description: Set the lockdown state for resources
+//
+// Notes: When the resources are locked down, no new resources
+// can be created. This is so that the tuner's wiring
+// of the sound system can successively map all available
+// resources to sound groups.
+//
+//-----------------------------------------------------------------------------
+
+void FlipSlashes( char * pString )
+{
+ while (*pString != 0 )
+ {
+ if ( *pString == '/' )
+ {
+ *pString = '\\';
+ }
+ pString++;
+ }
+}
+
+void daSoundResourceManager::SetResourceLockdown( bool lockdown )
+{
+ // Currently we may only lock down the resources
+ rAssert( lockdown == true );
+ m_ResourceLockdown = lockdown;
+
+ unsigned int totalFiles = 0;
+
+ for( unsigned int r = 0; r < m_NumResourceDatas; r ++ )
+ {
+ daSoundResourceData * pRd = this->m_ResourceData + r;
+
+ totalFiles += pRd->m_NumFiles;
+ }
+
+ m_pFileIdMemory = (radKey32*) radMemoryAlloc( GMA_PERSISTENT, sizeof( radKey32 ) * totalFiles );
+
+ radKey32* pCurrent = m_pFileIdMemory;
+
+ for( unsigned int r = 0; r < MAX_SOUND_DATA_RESOURCES; r ++ )
+ {
+ daSoundResourceData * pRd = this->m_ResourceData + r;
+
+ for( unsigned int f = 0; f < pRd->m_NumFiles; f ++ )
+ {
+ FlipSlashes( pRd->m_pFileIds[ f ].m_pName );
+
+ pCurrent[ f ] = ::radMakeCaseInsensitiveKey32( pRd->m_pFileIds[ f ].m_pName );
+ radMemoryFree( pRd->m_pFileIds[ f ].m_pName );
+ }
+
+ radMemoryFree( pRd->m_pFileIds );
+ pRd->m_pFileIds = (daSoundResourceData::FileId*)pCurrent;
+ pCurrent += pRd->m_NumFiles;
+ }
+}
+
+//=============================================================================
+// Function: daSoundResourceManager::GetResourceLockdown
+//=============================================================================
+// Description: Get the lockdown state for resources
+//-----------------------------------------------------------------------------
+
+bool daSoundResourceManager::GetResourceLockdown( void )
+{
+ return m_ResourceLockdown;
+}
+
+//=============================================================================
+// Function: daSoundResourceManager::GetLargestFileSize
+//=============================================================================
+// Description: Get the state of a resource
+//
+// Parameters: pResource - the resource to get the state of
+//
+// Returns: Returns the state of the resource
+//
+//-----------------------------------------------------------------------------
+
+unsigned int daSoundResourceManager::GetLargestFileSize
+(
+ IDaSoundResource* pResource
+)
+{
+ return 0;
+}
+
+//=============================================================================
+// Function: daSoundResourceManager::GetTotalSize
+//=============================================================================
+// Description: Get the state of a resource
+//
+// Parameters: pResource - the resource to get the state of
+//
+// Returns: Returns the state of the resource
+//
+//-----------------------------------------------------------------------------
+
+unsigned int daSoundResourceManager::GetTotalSize
+(
+ IDaSoundResource* pResource
+)
+{
+ return 0;
+}
+
+//=============================================================================
+// Function: daSoundResourceManager::GetSoundFileSize
+//=============================================================================
+// Description: Get debug information from resource manager
+//
+// Parameters: pFile - the file to find the size of
+//
+//-----------------------------------------------------------------------------
+
+unsigned int daSoundResourceManager::GetSoundFileSize
+(
+ const char* filename
+)
+{
+ // TODO : Don't use this unless it is not synchronous
+ rDebugString( "BAD SOUND FUNCTION BEING USED\n" );
+ unsigned int fileSize = 0;
+ IRadFile* pMyFile = NULL;
+ ::radFileOpen( &pMyFile, filename );
+ rAssert( pMyFile != NULL );
+ pMyFile->GetSizeAsync( &fileSize );
+ pMyFile->WaitForCompletion( );
+ pMyFile->Release( );
+ return fileSize;
+}
+
+//=============================================================================
+// Function: daSoundResourceManager::GetNumResources
+//=============================================================================
+// Description: Get the number of resources
+//
+//-----------------------------------------------------------------------------
+
+unsigned int daSoundResourceManager::GetNumResourceDatas( void )
+{
+ return m_NumResourceDatas;
+}
+
+//=============================================================================
+// Function: daSoundResourceManager::GetNumAllocatedResources
+//=============================================================================
+// Description: Get the number of allocated resources
+//
+//-----------------------------------------------------------------------------
+
+unsigned int daSoundResourceManager::GetNumAllocatedResources( void )
+{
+ return m_xIOL_AllocatedResources->GetSize( );
+}
+
+//=============================================================================
+// daSoundResourceManager::SetActiveResource
+//=============================================================================
+// Description: Set a secondary namespace for us to search for resources.
+// NOTE: a more robust implementation would have us storing a list
+// of these things, but I know for Simpsons that we'll only need
+// one and I want to keep the searches fast
+//
+// Parameters: activeNamespace - secondary namespace
+//
+// Return: void
+//
+//=============================================================================
+void daSoundResourceManager::SetActiveResource( IRadNameSpace* activeNamespace )
+{
+ if( activeNamespace != m_pSoundNamespace )
+ {
+ m_secondaryNamespace = activeNamespace;
+ }
+}
+
+//=============================================================================
+// daSoundResourceManager::ReleaseActiveResource
+//=============================================================================
+// Description: Lose our reference to the secondary namespace, probably because
+// the in-game resources are being released.
+//
+// Parameters: inactiveNamespace - only used to make sure we aren't releasing
+// the main sound namespace
+//
+// Return: void
+//
+//=============================================================================
+void daSoundResourceManager::ReleaseActiveResource( IRadNameSpace* inactiveNamespace )
+{
+ if( inactiveNamespace != m_pSoundNamespace )
+ {
+ m_secondaryNamespace = NULL;
+ }
+}
+
+daSoundResourceData * daSoundResourceManager::GetResourceDataAt( unsigned int i )
+{
+ rAssert( i < m_NumResourceDatas );
+
+ return m_ResourceData + i;
+}
+
+daSoundResourceData * daSoundResourceManager::CreateResourceData( void )
+{
+ daSoundResourceManager * pThis = daSoundResourceManager::GetInstance( );
+
+ rAssert( false == pThis->m_ResourceLockdown );
+
+ rAssert( pThis->m_NumResourceDatas < MAX_SOUND_DATA_RESOURCES );
+
+ daSoundResourceData * pRd = pThis->m_ResourceData + pThis->m_NumResourceDatas;
+
+ pThis->m_NumResourceDatas++;
+
+ return pRd;
+}
+
+} // Sound Namespace
+
diff --git a/game/code/sound/soundrenderer/soundresourcemanager.h b/game/code/sound/soundrenderer/soundresourcemanager.h
new file mode 100644
index 0000000..3a290c1
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundresourcemanager.h
@@ -0,0 +1,164 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundresourcemanager.hpp
+//
+// Subsystem: Dark Angel - Sound Resource Management System
+//
+// Description: Description of the DA sound resource manager
+//
+// Revisions:
+// + Created October 11, 2001 -- breimer
+//
+//=============================================================================
+
+#ifndef _SOUNDRESOURCEMANAGER_HPP
+#define _SOUNDRESOURCEMANAGER_HPP
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+
+#include <sound/soundrenderer/idasoundresource.h>
+#include <sound/soundrenderer/soundresource.h>
+
+//=============================================================================
+// Global namespace forward declarations
+//=============================================================================
+
+class radObjectBTree;
+
+//=============================================================================
+// Namespace
+//=============================================================================
+
+namespace Sound {
+
+const unsigned int MAX_SOUND_DATA_RESOURCES = 5000;
+
+//=============================================================================
+// Prototypes
+//=============================================================================
+
+class daSoundResourceManager;
+
+//=============================================================================
+// Forward declarations
+//=============================================================================
+
+class daSoundAllocatedResource;
+
+//=============================================================================
+// Class Declarations
+//=============================================================================
+
+//
+// The sound player manager passes information into the various sound
+// players. It also keeps track of these players, gives them to people
+// who ask for them, and makes sure they do not cause to much trouble :)
+//
+class daSoundResourceManager : public IRefCount,
+ public radRefCount
+{
+public:
+ IMPLEMENT_REFCOUNTED( "daSoundResourceManager" );
+
+ //
+ // Constructor and destructor
+ //
+ daSoundResourceManager( void );
+
+ virtual ~daSoundResourceManager( );
+
+ inline static daSoundResourceManager* GetInstance( void );
+
+ // Controlled by the resource data
+ void AllocateResource( IDaSoundResource* pResource );
+ void DeallocateResource( IDaSoundResource* pResource );
+
+ // Resource lockdown
+ void SetResourceLockdown( bool lockdown );
+
+ // Allocated resources
+ daSoundAllocatedResource* FindAllocatedResource
+ (
+ IDaSoundResource* pResource
+ );
+
+ // Get a sound file's size
+ unsigned int GetSoundFileSize
+ (
+ const char* filename
+ );
+
+ //
+ // IDaSoundResourceManager
+ //
+ IDaSoundResource* FindResource(
+ daResourceName resourceName );
+
+ IDaSoundResource* FindResource(
+ daResourceKey resourceKey );
+
+ unsigned int GetLargestFileSize( IDaSoundResource* pResource );
+ unsigned int GetTotalSize( IDaSoundResource* pResource );
+
+
+ bool GetResourceLockdown( void );
+
+ void SetActiveResource( IRadNameSpace* activeNamespace );
+ void ReleaseActiveResource( IRadNameSpace* inactiveNamespace );
+
+ unsigned int GetNumResourceDatas( void );
+ daSoundResourceData* GetResourceDataAt( unsigned int );
+ static daSoundResourceData* CreateResourceData( void );
+
+protected:
+ //
+ // Calculate some debug info
+ //
+
+ unsigned int GetNumAllocatedResources( );
+
+private:
+ // This is a singleton
+ static daSoundResourceManager* s_pSingleton;
+
+ //
+ // Store the sound namespace
+ //
+ IRadNameSpace* m_pSoundNamespace;
+
+ //
+ // Store all allocated resources (referenced by the resource's
+ // address cast to a radkey)
+ //
+ ref< radObjectBTree > m_xIOL_AllocatedResources;
+
+ //
+ // Are the resources locked down?
+ //
+ bool m_ResourceLockdown;
+
+ //
+ // Store active secondary namespace
+ //
+ IRadNameSpace* m_secondaryNamespace;
+
+ daSoundResourceData m_ResourceData[ MAX_SOUND_DATA_RESOURCES ];
+ unsigned int m_NumResourceDatas;
+
+ radKey32 * m_pFileIdMemory;
+};
+
+inline daSoundResourceManager* daSoundResourceManager::GetInstance( void )
+{
+ return s_pSingleton;
+}
+
+} // Sound Namespace
+#endif //_SOUNDRESOURCEMANAGER_HPP
+
+
diff --git a/game/code/sound/soundrenderer/soundsystem.h b/game/code/sound/soundrenderer/soundsystem.h
new file mode 100644
index 0000000..b0557ff
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundsystem.h
@@ -0,0 +1,118 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundsystem.hpp
+//
+// Subsystem: Dark Angel - Sound System
+//
+// Description: General declarations relating to the Dark Angel sound system
+//
+// Notes: This is the only file that may be loaded by non sound related
+// components of the game.
+//
+// Revisions: October 2, 2001 Creation BJR
+//
+//=============================================================================
+
+#ifndef _SOUNDSYSTEM_HPP
+#define _SOUNDSYSTEM_HPP
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <raddebug.hpp>
+#include <radkey.hpp>
+
+//=============================================================================
+// Define Owning Namespace
+//=============================================================================
+
+namespace Sound {
+
+class daSoundRenderingManager;
+
+//=============================================================================
+// Constants, Definitions and Macros
+//=============================================================================
+
+// The sound memory allocator
+extern radMemoryAllocator DAMEMORY_ALLOC_SOUND;
+
+// One aux send channels will be used for spacial effects
+#define DA_SPACIAL_EFFECT_AUX_SEND 0
+#define DA_MISC_AUX_SEND 1
+
+//=============================================================================
+// Prototypes
+//=============================================================================
+
+struct IDaSoundPlayerState;
+struct IDaSoundFadeState;
+struct IDaSoundSoundState;
+
+//=============================================================================
+// Typedefs and Enumerations
+//=============================================================================
+
+//
+// This is a sound resource name and key
+//
+typedef const char* daResourceName;
+typedef radKey32 daResourceKey;
+
+//
+// These are the sound values
+//
+typedef float daPitchValue;
+typedef float daTrimValue;
+
+//=============================================================================
+// Interfaces
+//=============================================================================
+
+//
+// Sound player state
+//
+struct IDaSoundPlayerState : public IRefCount
+{
+ virtual void OnSoundReady( void* pData ) = 0;
+ virtual void OnSoundDone( void* pData ) = 0;
+};
+
+//
+// Fade state
+//
+struct IDaSoundFadeState : public IRefCount
+{
+ virtual void OnFadeDone( void* pData ) = 0;
+};
+
+//
+// A sound object
+//
+struct IDaSoundObject : public IRefCount
+{
+ // Left intentionally blank
+};
+
+//=============================================================================
+// Public Functions
+//=============================================================================
+
+//
+// Set some global sound flags
+//
+void daSoundSetSoundOn( bool soundOn );
+
+//
+// Work indirectly with the sound manager
+//
+void daSoundRenderingManagerCreate( radMemoryAllocator allocator );
+daSoundRenderingManager* daSoundRenderingManagerGet( void );
+void daSoundRenderingManagerTerminate( void );
+
+} // Namespace
+#endif //_SOUNDSYSTEM_HPP
+
diff --git a/game/code/sound/soundrenderer/soundtuner.cpp b/game/code/sound/soundrenderer/soundtuner.cpp
new file mode 100644
index 0000000..7aaed29
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundtuner.cpp
@@ -0,0 +1,1241 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundtuner.cpp
+//
+// Subsystem: Dark Angel - Sound Tuner System
+//
+// Description: Implementation of the sound tuner
+//
+// Revisions:
+// + Created October 4, 2001 -- breimer
+//
+//=============================================================================
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <raddebug.hpp>
+#include <radnamespace.hpp>
+
+#include <radsound.hpp>
+#include <radsound_hal.hpp>
+
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundrenderer/idasoundresource.h>
+#include <sound/soundrenderer/soundrenderingmanager.h>
+#include <sound/soundrenderer/soundresourcemanager.h>
+#include <sound/soundrenderer/playermanager.h>
+#include <sound/soundrenderer/soundtuner.h>
+
+#include <sound/soundmanager.h>
+
+#include <memory/srrmemory.h>
+
+//=============================================================================
+// Static Variables (outside namespace)
+//=============================================================================
+
+short Sound::daSoundTuner::s_groupWirings[NUM_SOUND_GROUPS];
+
+//=============================================================================
+// Define Owning Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Class Implementations
+//=============================================================================
+
+//=============================================================================
+// daSoundTuner_ActiveFadeInfo Implementation
+//=============================================================================
+
+//=============================================================================
+// Function: daSoundTuner_ActiveFadeInfo::daSoundTuner_ActiveFadeInfo
+//=============================================================================
+// Description: Constructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundTuner_ActiveFadeInfo::daSoundTuner_ActiveFadeInfo
+(
+ Fader* pFader,
+ bool fadingIn,
+ IDaSoundFadeState* pDoneCallback,
+ void* pCallbackUserData,
+ DuckVolumeSet* initialVolumes,
+ DuckVolumeSet* targetVolumes
+)
+ :
+ m_pFader( pFader ),
+ m_FadingIn( fadingIn ),
+ m_pDoneCallback( pDoneCallback ),
+ m_pCallbackUserData( pCallbackUserData )
+{
+ // Reference count some items
+ rAssert( m_pFader != NULL );
+
+ m_pFader->AddRef( );
+ if( m_pDoneCallback != NULL )
+ {
+ m_pDoneCallback->AddRef( );
+ }
+
+ // Invoke the fader
+ m_pFader->Fade( m_FadingIn, initialVolumes, targetVolumes );
+}
+
+
+//=============================================================================
+// Function: daSoundTuner_ActiveFadeInfo::~daSoundTuner_ActiveFadeInfo
+//=============================================================================
+// Description: Destructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundTuner_ActiveFadeInfo::~daSoundTuner_ActiveFadeInfo( )
+{
+ // Release our objects
+ rAssert( m_pFader != NULL );
+
+ m_pFader->Stop();
+ m_pFader->Release( );
+ if( m_pDoneCallback != NULL )
+ {
+ m_pDoneCallback->Release( );
+ }
+}
+
+//=============================================================================
+// Function: daSoundTuner_ActiveFadeInfo::ProcessFader
+//=============================================================================
+// Description: Process a fader's state progress
+//
+// Returns: true if we're done with the fader and are ready for destruction,
+// false otherwise
+//
+//-----------------------------------------------------------------------------
+
+bool daSoundTuner_ActiveFadeInfo::ProcessFader()
+{
+ // Is the fader done fading yet?
+ bool doneFading = false;
+ switch( m_pFader->GetState( ) )
+ {
+ case Fader::FadedIn:
+ case Fader::FadedOut:
+ {
+ // Must be done (what ever it was)
+ doneFading = true;
+ break;
+ }
+ case Fader::FadingIn:
+ {
+ // If we're supposed to be fading out, we must be done
+ if( !m_FadingIn )
+ {
+ doneFading = true;
+ }
+ break;
+ }
+ case Fader::FadingOut:
+ {
+ // If we're supposed to be fading in, we must be done
+ if( m_FadingIn )
+ {
+ doneFading = true;
+ }
+ break;
+ }
+ default:
+ rAssert( 0 );
+ break;
+ }
+
+ // If done, disconnect it, destroy it, and call its callback
+ if( doneFading )
+ {
+ // Remember the callback
+ IDaSoundFadeState* pCallback = m_pDoneCallback;
+ void* pUserData = m_pCallbackUserData;
+ if( pCallback != NULL )
+ {
+ pCallback->AddRef( );
+ }
+
+ // Call the callback
+ if( pCallback != NULL )
+ {
+ pCallback->OnFadeDone( pUserData );
+ pCallback->Release( );
+ }
+ }
+
+ return( doneFading );
+}
+
+void daSoundTuner_ActiveFadeInfo::StoreCurrentVolumes( DuckVolumeSet& volumeSet )
+{
+ unsigned int i;
+
+ rAssert( m_pFader != NULL );
+
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ volumeSet.duckVolume[i] = m_pFader->GetCurrentVolume( static_cast<DuckVolumes>(i) );
+ }
+}
+
+void daSoundTuner_ActiveFadeInfo::StoreTargetSettings( DuckVolumeSet& volumeSet )
+{
+ unsigned int i;
+
+ rAssert( m_pFader != NULL );
+
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ volumeSet.duckVolume[i] = m_pFader->GetTargetSettings( static_cast<DuckVolumes>(i) );
+ }
+}
+
+//=============================================================================
+// daSoundTuner Implementation
+//=============================================================================
+
+//=============================================================================
+// Function: daSoundTuner::daSoundTuner
+//=============================================================================
+// Description: Constructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundTuner::daSoundTuner( )
+ :
+ radRefCount( 0 ),
+ m_pDuckFade( NULL ),
+ m_MasterVolume( 1.0f ),
+ m_activeFadeInfo( NULL ),
+ m_NISTrim( 1.0f )
+{
+ daSoundPlayerManager* playerMgr;
+ int i, j;
+
+ //
+ // The tuner makes use of several fader objects. These objects may be
+ // customized by scripts, so they must be added to the sound namespace.
+ //
+
+ // Duck fader
+ playerMgr = daSoundRenderingManagerGet()->GetPlayerManager();
+ rAssert( playerMgr != NULL );
+
+ m_pDuckFade = new( GetThisAllocator() ) Fader( NULL, DUCK_FULL_FADE, *playerMgr, *this );
+ rAssert( m_pDuckFade != NULL );
+
+ for( i = 0; i < NUM_DUCK_SITUATIONS; i++ )
+ {
+ m_situationFaders[i] = NULL;
+ }
+
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ m_userVolumes.duckVolume[i] = 1.0f;
+ m_finalDuckLevels.duckVolume[i] = 1.0f;
+ }
+
+ for( i = 0; i < NUM_DUCK_SITUATIONS; i++ )
+ {
+ for( j = 0; j < NUM_DUCK_VOLUMES; j++ )
+ {
+ m_duckLevels[i].duckVolume[j] = 1.0f;
+ }
+ }
+
+ //
+ // Initialize the sound group wirings (IMPORTANT: this assumes that
+ // daSoundTuner is a singleton, we only want to do this once)
+ //
+ for( i = 0; i < NUM_SOUND_GROUPS; i++ )
+ {
+ s_groupWirings[i] = ( 1 << i ) | ( 1 << MASTER );
+ }
+}
+
+//=============================================================================
+// Function: daSoundTuner::~daSoundTuner
+//=============================================================================
+// Description: Destructor
+//
+//-----------------------------------------------------------------------------
+
+daSoundTuner::~daSoundTuner( )
+{
+ int i;
+
+ // Release our duck faders
+ if( m_pDuckFade != NULL )
+ {
+ m_pDuckFade->Release( );
+ m_pDuckFade = NULL;
+ }
+
+ for( i = 0; i < NUM_DUCK_SITUATIONS; i++ )
+ {
+ if( m_situationFaders[i] != NULL )
+ {
+ m_situationFaders[i]->Release( );
+ m_situationFaders[i] = NULL;
+ }
+ }
+
+ if( m_activeFadeInfo != NULL )
+ {
+ delete m_activeFadeInfo;
+ }
+}
+
+//=============================================================================
+// Function: daSoundTuner::Initialize
+//=============================================================================
+// Description: Initialize the sound tuner. This can only be done once
+// resources have been locked down.
+//
+// Parameters: outputMode - the output mode desired
+//
+// Returns: n/a
+//
+// Notes: This class does not support re-initialization
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::Initialize( void )
+{
+ // Make sure resources are locked down
+ //rAssert( Sound::daSoundRenderingManagerGet( )->GetResourceManager( )->GetResourceLockdown( ) );
+
+ // Generate each of the wiring groups
+ Sound::daSoundTunerWireSystem( this );
+}
+
+//=============================================================================
+// daSoundTuner::PostScriptLoadInitialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void daSoundTuner::PostScriptLoadInitialize()
+{
+ IRadNameSpace* nameSpace;
+ globalSettings* settingsObj;
+ daSoundPlayerManager* playerMgr;
+ int i;
+
+ playerMgr = daSoundRenderingManagerGet()->GetPlayerManager();
+ rAssert( playerMgr != NULL );
+
+ //
+ // Get the globalSettings object for Fader use
+ //
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+
+ settingsObj = reinterpret_cast<globalSettings*>( nameSpace->GetInstance( "tuner" ) );
+ rAssert( settingsObj != NULL );
+
+ for( i = 0; i < NUM_DUCK_SITUATIONS; i++ )
+ {
+ m_situationFaders[i] = new( GetThisAllocator() ) Fader( settingsObj,
+ static_cast<DuckSituations>(i),
+ *playerMgr,
+ *this );
+ rAssert( m_situationFaders[i] != NULL );
+ }
+
+#ifdef SOUND_DEBUG_INFO_ENABLED
+ m_debugPage.LazyInitialization( 4, GetSoundManager()->GetDebugDisplay() );
+#endif
+}
+
+//=============================================================================
+// Function: daSoundTuner::ServiceOncePerFrame
+//=============================================================================
+// Description: Service the sound tuner. This should be done once per frame.
+//
+// Parameters: none
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::ServiceOncePerFrame( unsigned int elapsedTime )
+{
+ bool faderDone;
+
+ //
+ // Process the faders
+ //
+ Fader::UpdateAllFaders( elapsedTime );
+
+ //
+ // Process the fade info stuff that monitors the faders (hmmm, this is
+ // looking like a pretty lightweight class these days. Candidate for
+ // removal?)
+ //
+ if( m_activeFadeInfo != NULL )
+ {
+ faderDone = m_activeFadeInfo->daSoundTuner_ActiveFadeInfo::ProcessFader();
+
+ if( faderDone )
+ {
+ delete m_activeFadeInfo;
+ m_activeFadeInfo = NULL;
+ }
+ }
+
+ serviceDebugInfo();
+}
+
+//=============================================================================
+// Function: daSoundTuner::SetSoundOutputMode
+//=============================================================================
+// Description: Set the sound system output mode (stereo, mono, surround)
+//
+// Parameters: outputMode - the output mode desired
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::SetSoundOutputMode
+(
+ IDaSoundTuner::SoundOutputMode outputMode
+)
+{
+ radSoundOutputMode rsdOutputMode = radSoundOutputMode_Stereo;
+ if( outputMode == MONO )
+ {
+ rsdOutputMode = radSoundOutputMode_Mono;
+ }
+ else if( outputMode == STEREO )
+ {
+ rsdOutputMode = radSoundOutputMode_Stereo;
+ }
+ else if( outputMode == SURROUND )
+ {
+ rsdOutputMode = radSoundOutputMode_Surround;
+ }
+ else
+ {
+ rAssertMsg( 0, "Invalid sound output mode" );
+ }
+
+ ::radSoundHalSystemGet( )->SetOutputMode( rsdOutputMode );
+}
+
+//=============================================================================
+// Function: daSoundTuner::GetSoundOutputMode
+//=============================================================================
+// Description: Get the sound system output mode (stereo, mono, surround)
+//
+// Parameters: none
+//
+// Returns: Returns the current sound output mode
+//
+//-----------------------------------------------------------------------------
+
+IDaSoundTuner::SoundOutputMode daSoundTuner::GetSoundOutputMode
+(
+ void
+)
+{
+ IDaSoundTuner::SoundOutputMode outputMode = STEREO;
+ radSoundOutputMode rsdOutputMode =
+ ::radSoundHalSystemGet( )->GetOutputMode( );
+ if( rsdOutputMode == radSoundOutputMode_Mono )
+ {
+ outputMode = MONO;
+ }
+ else if( rsdOutputMode == radSoundOutputMode_Stereo )
+ {
+ outputMode = STEREO;
+ }
+ else if( rsdOutputMode == radSoundOutputMode_Surround )
+ {
+ outputMode = SURROUND;
+ }
+ else
+ {
+ rAssertMsg( 0, "Unrecognized sound output mode" );
+ }
+
+ return outputMode;
+}
+
+//=============================================================================
+// Function: daSoundTuner::ActivateDuck
+//=============================================================================
+// Description: Start/stop ducking of sounds
+//
+// Parameters: pObject - the object to receive fade state events
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::ActivateDuck
+(
+ IDaSoundFadeState* pObject,
+ void* pUserData,
+ bool fadeIn
+)
+{
+ activateDuckInternal( pObject, pUserData, fadeIn, m_pDuckFade );
+}
+
+//=============================================================================
+// Function: daSoundTuner::StartSituationalDuck
+//=============================================================================
+// Description: Start ducking of sounds
+//
+// Parameters: pObject - the object to receive fade state events
+//
+// Returns: n/a
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::ActivateSituationalDuck( IDaSoundFadeState* pObject,
+ DuckSituations situation,
+ void* pUserData,
+ bool fadeIn )
+{
+ activateDuckInternal( pObject, pUserData, fadeIn, m_situationFaders[situation] );
+}
+
+//=============================================================================
+// daSoundTuner::ResetDuck
+//=============================================================================
+// Description: Stop all ducking
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void daSoundTuner::ResetDuck()
+{
+ unsigned int i, j;
+
+ //
+ // Return all the duck values to max, then do a fade in
+ //
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ for( j = 0; j < NUM_DUCK_SITUATIONS; j++ )
+ {
+ m_duckLevels[j].duckVolume[i] = 1.0f;
+ }
+ }
+
+ activateDuckInternal( NULL, NULL, true, m_pDuckFade );
+}
+
+//=============================================================================
+// Function: daSoundTuner::SetMasterVolume
+//=============================================================================
+// Description: Set the master volume
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::SetMasterVolume
+(
+ daTrimValue volume
+)
+{
+ m_MasterVolume = volume;
+
+ daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( MASTER, m_MasterVolume );
+}
+
+//=============================================================================
+// Function: daSoundTuner::GetMasterVolume
+//=============================================================================
+// Description: Get the master volume
+//
+//-----------------------------------------------------------------------------
+
+daTrimValue daSoundTuner::GetMasterVolume( void )
+{
+ return( m_MasterVolume );
+}
+
+//=============================================================================
+// Function: daSoundTuner::SetDialogueVolume
+//=============================================================================
+// Description: Set the dialogue volume
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::SetDialogueVolume
+(
+ daTrimValue volume
+)
+{
+ m_userVolumes.duckVolume[DUCK_DIALOG] = volume;
+
+ daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( DIALOGUE, volume );
+}
+
+//=============================================================================
+// Function: daSoundTuner::GetDialogueVolume
+//=============================================================================
+// Description: Get the dialogue volume
+//
+//-----------------------------------------------------------------------------
+
+daTrimValue daSoundTuner::GetDialogueVolume( void )
+{
+ return( m_userVolumes.duckVolume[DUCK_DIALOG] );
+}
+
+//=============================================================================
+// Function: daSoundTuner::SetMusicVolume
+//=============================================================================
+// Description: Set the music volume
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::SetMusicVolume
+(
+ daTrimValue volume
+)
+{
+ m_userVolumes.duckVolume[DUCK_MUSIC] = volume;
+
+ daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( MUSIC, volume );
+}
+
+//=============================================================================
+// Function: daSoundTuner::GetMusicVolume
+//=============================================================================
+// Description: Get the music volume
+//
+//-----------------------------------------------------------------------------
+
+daTrimValue daSoundTuner::GetMusicVolume( void )
+{
+ return( m_userVolumes.duckVolume[DUCK_MUSIC] );
+}
+
+//=============================================================================
+// Function: daSoundTuner::SetAmbienceVolume
+//=============================================================================
+// Description: Set the music volume
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::SetAmbienceVolume
+(
+ daTrimValue volume
+)
+{
+ m_userVolumes.duckVolume[DUCK_AMBIENCE] = volume;
+
+ daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( AMBIENCE, volume );
+}
+
+//=============================================================================
+// Function: daSoundTuner::GetAmbienceVolume
+//=============================================================================
+// Description: Get the music volume
+//
+//-----------------------------------------------------------------------------
+
+daTrimValue daSoundTuner::GetAmbienceVolume( void )
+{
+ return( m_userVolumes.duckVolume[DUCK_AMBIENCE] );
+}
+
+//=============================================================================
+// Function: daSoundTuner::SetSfxVolume
+//=============================================================================
+// Description: Set the sound effects volume
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::SetSfxVolume
+(
+ daTrimValue volume
+)
+{
+ m_userVolumes.duckVolume[DUCK_SFX] = volume;
+
+ daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( SOUND_EFFECTS, volume );
+}
+
+//=============================================================================
+// Function: daSoundTuner::GetSfxVolume
+//=============================================================================
+// Description: Get the sound effects volume
+//
+//-----------------------------------------------------------------------------
+
+daTrimValue daSoundTuner::GetSfxVolume( void )
+{
+ return( m_userVolumes.duckVolume[DUCK_SFX] );
+}
+
+//=============================================================================
+// Function: daSoundTuner::SetCarVolume
+//=============================================================================
+// Description: Set the sound effects volume
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::SetCarVolume( daTrimValue volume )
+{
+ m_userVolumes.duckVolume[DUCK_CAR] = volume;
+
+ daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( CARSOUND, volume );
+}
+
+//=============================================================================
+// Function: daSoundTuner::GetCarVolume
+//=============================================================================
+// Description: Get the sound effects volume
+//
+//-----------------------------------------------------------------------------
+
+daTrimValue daSoundTuner::GetCarVolume( void )
+{
+ return( m_userVolumes.duckVolume[DUCK_CAR] );
+}
+
+//=============================================================================
+// Function: daSoundTuner::FadeSounds
+//=============================================================================
+// Description: Fade a particular group of sounds
+//
+// Parameters: pKnob - the knob to fade
+// pObject - the fade state change object
+// pUserData - user data for the state change callback
+// pFader - a pointer to an actual fader to use
+// fadeIn - true if we are fading in
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::FadeSounds
+(
+ IDaSoundFadeState* pObject,
+ void* pUserData,
+ Fader* pFader,
+ bool fadeIn,
+ DuckVolumeSet* initialVolumes
+)
+{
+ unsigned int i;
+ DuckVolumeSet currentVolumes;
+ DuckVolumeSet* initVolumePtr = initialVolumes;
+ DuckVolumeSet targetVolumes;
+
+ rAssert( m_activeFadeInfo == NULL );
+ rAssert( pFader != NULL );
+ if( pFader == NULL )
+ {
+ return;
+ }
+
+ HeapMgr()->PushHeap( static_cast<GameMemoryAllocator>(GetThisAllocator()) );
+
+ //
+ // Store the intended targets for this fader.
+ //
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ if( fadeIn )
+ {
+ m_duckLevels[pFader->GetSituation()].duckVolume[i] = 1.0f;
+ }
+ else
+ {
+ m_duckLevels[pFader->GetSituation()].duckVolume[i] =
+ pFader->GetTargetSettings( static_cast<Sound::DuckVolumes>(i) );
+ }
+ }
+
+ if( initVolumePtr == NULL )
+ {
+ //
+ // Initial volumes aren't supplied, so use the current settings
+ // as we've recorded them here
+ //
+ initVolumePtr = &currentVolumes;
+
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ currentVolumes.duckVolume[i] = m_userVolumes.duckVolume[i];
+ }
+ }
+
+ calculateDuckedVolumes( targetVolumes );
+
+ // Create the temporary fade object. It will destroy itself when done
+ m_activeFadeInfo = new daSoundTuner_ActiveFadeInfo
+ (
+ pFader,
+ fadeIn,
+ pObject,
+ pUserData,
+ initialVolumes,
+ &targetVolumes
+ );
+
+ HeapMgr()->PopHeap( static_cast<GameMemoryAllocator>( GetThisAllocator() ) );
+}
+
+
+//=============================================================================
+// Function: daSoundTuner::WireKnobToPathHelper
+//=============================================================================
+// Description: Helper function to wire a knob to a path for a resource.
+// This function is automattically called on every
+// sound resource. If the path matches that of a file
+// in the resource, the resource's sound group is automattically
+// wired to the sound group specified by the user data.
+//
+// Notes: Trying to put a resource in more than one group will not work!
+// If a resource contains files from more than one fundamental
+// path and its wiring must reflect this, then a new
+// allocation stratagy should be used.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::WireKnobToPathHelper
+(
+ IDaSoundResource* pRes,
+ void* pUserData
+)
+{
+ char filenameBuffer[256];
+
+ // Get the wiring info
+ WirePathInfo* pInfo = (WirePathInfo*)pUserData;
+
+ unsigned int i = 0;
+ for( i = 0; i < pRes->GetNumFiles( ); i++ )
+ {
+ pRes->GetFileNameAt( i, filenameBuffer, 256 );
+ unsigned int j = 0;
+ bool match = true;
+ for( j = 0; j < pInfo->m_PathLen; j++ )
+ {
+ char a = filenameBuffer[j];
+ char b = pInfo->m_Path[j];
+
+ if( a == '/' )
+ {
+ a = '\\';
+ }
+ if( b == '/' )
+ {
+ b = '\\';
+ }
+ if( a != b )
+ {
+ // Notice that this supports the case when the length
+ // of the filename is less than the test path because,
+ // in that case, the '\0' character won't match
+ match = false;
+ break;
+ }
+ }
+ if( match )
+ {
+ pRes->SetSoundGroup( pInfo->m_SoundGroup );
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// Function: daSoundTuner::WirePath
+//=============================================================================
+// Description: Wire a path to a particular sound group
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTuner::WirePath
+(
+ daSoundGroup soundGroup,
+ const char* path
+)
+{
+ // Find all resource that are in this path and relate them to this knob
+ WirePathInfo wirePathInfo;
+ wirePathInfo.m_Path = path;
+ wirePathInfo.m_PathLen = strlen( wirePathInfo.m_Path );
+ wirePathInfo.m_SoundGroup = soundGroup;
+
+ unsigned int numResources =
+ daSoundResourceManager::GetInstance( )->GetNumResourceDatas( );
+
+ for( unsigned int r = 0; r < numResources; r ++ )
+ {
+ WireKnobToPathHelper(
+ daSoundResourceManager::GetInstance( )->GetResourceDataAt( r ),
+ & wirePathInfo );
+ }
+}
+
+//=============================================================================
+// daSoundTuner::WireGroup
+//=============================================================================
+// Description: Mark a sound group as changing in sync with the master group
+//
+// Parameters: slaveGroup - slave that changes with master group
+// masterGroup - controller group
+//
+// Return: void
+//
+//=============================================================================
+void daSoundTuner::WireGroup( daSoundGroup slaveGroup, daSoundGroup masterGroup )
+{
+ unsigned int i;
+
+ for( i = 0; i < NUM_SOUND_GROUPS; i++ )
+ {
+ if( s_groupWirings[i] & ( 1 << slaveGroup ) )
+ {
+ s_groupWirings[i] |= 1 << masterGroup;
+ }
+ }
+}
+
+//=============================================================================
+// daSoundTuner::IsSlaveGroup
+//=============================================================================
+// Description: Indicates whether one group is slaved to another
+//
+// Parameters: slave - proposed slave sound group
+// master - proposed master sound group
+//
+// Return: True if slave is affected by master, false otherwise
+//
+//=============================================================================
+bool daSoundTuner::IsSlaveGroup( daSoundGroup slave, daSoundGroup master )
+{
+ if( s_groupWirings[slave] & ( 1 << master ) )
+ {
+ return( true );
+ }
+
+ return( false );
+}
+
+//=============================================================================
+// daSoundTuner::GetGroupTrim
+//=============================================================================
+// Description: Get the trim associated with a particular sound group.
+// Actually, our "group" is currently one of four settings,
+// even though we break it down more in daSoundGroup for
+// future expansion.
+//
+// Parameters: group - group that we want trim for
+//
+// Return: trim value for that group, or 1.0f (max) if it doesn't fit
+//
+//=============================================================================
+daTrimValue daSoundTuner::GetGroupTrim( daSoundGroup group )
+{
+ if( IsSlaveGroup( group, SOUND_EFFECTS ) )
+ {
+ return( m_userVolumes.duckVolume[DUCK_SFX] );
+ }
+ else if( IsSlaveGroup( group, MUSIC ) )
+ {
+ return( m_userVolumes.duckVolume[DUCK_MUSIC] );
+ }
+ else if( IsSlaveGroup( group, DIALOGUE ) )
+ {
+ return( m_userVolumes.duckVolume[DUCK_DIALOG] );
+ }
+ else if( IsSlaveGroup( group, AMBIENCE ) )
+ {
+ return( m_userVolumes.duckVolume[DUCK_AMBIENCE] );
+ }
+ else if( IsSlaveGroup( group, CARSOUND ) )
+ {
+ return( m_userVolumes.duckVolume[DUCK_CAR] );
+ }
+ else if( IsSlaveGroup( group, OPTIONS_MENU_STINGERS ) )
+ {
+ //
+ // Special group for options menu, not affected by ducking
+ //
+ return( 1.0f );
+ }
+ else
+ {
+ //
+ // None of the above. We shouldn't get here
+ //
+ rAssert( false );
+ return( 1.0f );
+ }
+}
+
+//=============================================================================
+// daSoundTuner::SetFaderGroupTrim
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Sound::DuckVolumes group, daTrimValue trim )
+//
+// Return: void
+//
+//=============================================================================
+void daSoundTuner::SetFaderGroupTrim( Sound::DuckVolumes group, daTrimValue trim )
+{
+ m_finalDuckLevels.duckVolume[group] = trim;
+}
+
+//=============================================================================
+// daSoundTuner::GetFaderGroupTrim
+//=============================================================================
+// Description: Get the fader trim associated with a particular sound group.
+// Actually, our "group" is currently one of four settings,
+// even though we break it down more in daSoundGroup for
+// future expansion.
+//
+// Parameters: group - group that we want trim for
+//
+// Return: trim value for that group, or 1.0f (max) if it doesn't fit
+//
+//=============================================================================
+daTrimValue daSoundTuner::GetFaderGroupTrim( daSoundGroup group )
+{
+ if( IsSlaveGroup( group, SOUND_EFFECTS ) )
+ {
+ return( m_finalDuckLevels.duckVolume[DUCK_SFX] );
+ }
+ else if( IsSlaveGroup( group, MUSIC ) )
+ {
+ return( m_finalDuckLevels.duckVolume[DUCK_MUSIC] );
+ }
+ else if( IsSlaveGroup( group, DIALOGUE ) )
+ {
+ return( m_finalDuckLevels.duckVolume[DUCK_DIALOG] );
+ }
+ else if( IsSlaveGroup( group, AMBIENCE ) )
+ {
+ return( m_finalDuckLevels.duckVolume[DUCK_AMBIENCE] );
+ }
+ else if( IsSlaveGroup( group, CARSOUND ) )
+ {
+ return( m_finalDuckLevels.duckVolume[DUCK_CAR] );
+ }
+ else if( IsSlaveGroup( group, OPTIONS_MENU_STINGERS ) )
+ {
+ //
+ // Special group for options menu, not affected by ducking
+ //
+ return( 1.0f );
+ }
+ else
+ {
+ //
+ // None of the above. We shouldn't get here
+ //
+ rAssert( false );
+ return( 1.0f );
+ }
+}
+
+void daSoundTuner::MuteNIS()
+{
+ m_NISTrim = GetGroupTrim( NIS );
+ daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( NIS, 0.0f );
+}
+
+void daSoundTuner::UnmuteNIS()
+{
+ daSoundRenderingManagerGet()->GetPlayerManager()->PlayerVolumeChange( NIS, m_NISTrim );
+}
+
+//=============================================================================
+// Private functions
+//=============================================================================
+
+void daSoundTuner::activateDuckInternal( IDaSoundFadeState* pObject,
+ void* pUserData,
+ bool fadeOut,
+ Fader* faderObj )
+{
+ DuckVolumeSet currentVolumes;
+
+#ifndef RAD_RELEASE
+ //
+ // Hack for fader tuning. The tuners usually only read the fader
+ // settings at startup, but we want them to pick up changes in radTuner
+ // on the fly. We'll make it refresh on each duck attempt in debug
+ // and tune builds.
+ //
+ refreshFaderSettings();
+#endif
+
+ if( m_activeFadeInfo != NULL )
+ {
+ //
+ // Get the current fader's settings and throw it on the stack
+ //
+ m_activeFadeInfo->StoreCurrentVolumes( currentVolumes );
+
+ //
+ // Now we can get rid of the old fader
+ //
+ delete m_activeFadeInfo;
+ m_activeFadeInfo = NULL;
+ }
+ else
+ {
+ calculateDuckedVolumes( currentVolumes );
+ }
+
+ // Use our duck fader...
+ FadeSounds( pObject,
+ pUserData,
+ faderObj,
+ fadeOut,
+ &currentVolumes );
+}
+
+//=============================================================================
+// daSoundTuner::calculateDuckedVolumes
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( DuckVolumeSet& volumes )
+//
+// Return: void
+//
+//=============================================================================
+void daSoundTuner::calculateDuckedVolumes( DuckVolumeSet& volumes )
+{
+ unsigned int i, j;
+
+ //
+ // Calculate target volumes
+ //
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ volumes.duckVolume[i] = 1.0f;
+ }
+
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ for( j = 0; j < NUM_DUCK_SITUATIONS; j++ )
+ {
+ if( m_duckLevels[j].duckVolume[i] < volumes.duckVolume[i] )
+ {
+ volumes.duckVolume[i] = m_duckLevels[j].duckVolume[i];
+ }
+ }
+ }
+}
+
+//=============================================================================
+// daSoundTuner::refreshFaderSettings
+//=============================================================================
+// Description: Reset the duck settings for each fader
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void daSoundTuner::refreshFaderSettings()
+{
+ IRadNameSpace* nameSpace;
+ globalSettings* settingsObj;
+ unsigned int i;
+
+ nameSpace = Sound::daSoundRenderingManagerGet()->GetTuningNamespace();
+ rAssert( nameSpace != NULL );
+
+ settingsObj = reinterpret_cast<globalSettings*>( nameSpace->GetInstance( "tuner" ) );
+ rAssert( settingsObj != NULL );
+
+ for( i = 0; i < NUM_DUCK_SITUATIONS; i++ )
+ {
+ m_situationFaders[i]->ReinitializeFader( settingsObj );
+ }
+}
+
+//=============================================================================
+// daSoundTuner::serviceDebugInfo
+//=============================================================================
+// Description: Send some debug stuff to be dumped on the screen
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void daSoundTuner::serviceDebugInfo()
+{
+#ifdef SOUND_DEBUG_INFO_ENABLED
+ unsigned int i;
+ unsigned int j;
+
+ for( i = 0; i < NUM_DUCK_SITUATIONS; i++ )
+ {
+ for( j = 0; j < NUM_DUCK_VOLUMES; j++ )
+ {
+ m_debugPage.SetDuckLevel( static_cast<Sound::DuckSituations>(i),
+ static_cast<Sound::DuckVolumes>(j),
+ m_duckLevels[i].duckVolume[j] );
+ }
+ }
+
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ m_debugPage.SetFinalDuckLevel( static_cast<Sound::DuckVolumes>(i), m_finalDuckLevels.duckVolume[i] );
+ }
+
+ for( i = 0; i < NUM_DUCK_VOLUMES; i++ )
+ {
+ m_debugPage.SetUserVolume( static_cast<Sound::DuckVolumes>(i), m_userVolumes.duckVolume[i] );
+ }
+#endif
+}
+
+//=============================================================================
+// Factory functions
+//=============================================================================
+
+//=============================================================================
+// Function: daSoundTunerCreate
+//=============================================================================
+// Description: Create the tuner
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTunerCreate
+(
+ IDaSoundTuner** ppTuner,
+ radMemoryAllocator allocator
+)
+{
+ rAssert( ppTuner != NULL );
+ (*ppTuner) = new ( allocator ) daSoundTuner( );
+ (*ppTuner)->AddRef( );
+}
+
+} // Sound Namespace
diff --git a/game/code/sound/soundrenderer/soundtuner.h b/game/code/sound/soundrenderer/soundtuner.h
new file mode 100644
index 0000000..93aa9f9
--- /dev/null
+++ b/game/code/sound/soundrenderer/soundtuner.h
@@ -0,0 +1,253 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundtuner.hpp
+//
+// Subsystem: Dark Angel - Sound Tuner System
+//
+// Description: Description of the DA sound tuner
+//
+// Revisions:
+// + Created October 4, 2001 -- breimer
+//
+//=============================================================================
+
+#ifndef _SOUNDTUNER_HPP
+#define _SOUNDTUNER_HPP
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <raddebug.hpp>
+#include <radlinkedclass.hpp>
+
+#include <sound/soundrenderer/dasoundgroup.h>
+
+#include <sound/soundrenderer/idasoundtuner.h>
+#include <sound/soundrenderer/fader.h>
+#include <sound/soundrenderer/tunerdebugpage.h>
+
+//=============================================================================
+// Global namespace forward declarations
+//=============================================================================
+
+struct IDaSoundResource;
+
+//=============================================================================
+// Define Owning Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Prototypes
+//=============================================================================
+
+class daSoundTuner;
+
+//=============================================================================
+// Forward declarations
+//=============================================================================
+
+//=============================================================================
+// Class Declarations
+//=============================================================================
+
+//
+// A fade state information structure stores necessary information about
+// an active fade.
+//
+class daSoundTuner_ActiveFadeInfo
+{
+public:
+
+ // Constructor and destructor
+ daSoundTuner_ActiveFadeInfo
+ (
+ Fader* pFader,
+ bool pFadingIn,
+ IDaSoundFadeState* pDoneCallback,
+ void* pCallbackUserData,
+ DuckVolumeSet* initialVolumes,
+ DuckVolumeSet* targetVolumes
+ );
+ virtual ~daSoundTuner_ActiveFadeInfo( );
+
+ // Process the fader
+ bool ProcessFader();
+
+ void StoreCurrentVolumes( DuckVolumeSet& volumeSet );
+ void StoreTargetSettings( DuckVolumeSet& volumeSet );
+
+ Sound::DuckSituations GetSituation() { return( m_pFader->GetSituation() ); }
+
+private:
+ // Store the raw data
+ Fader* m_pFader;
+ bool m_FadingIn;
+ IDaSoundFadeState* m_pDoneCallback;
+ void* m_pCallbackUserData;
+};
+
+//
+// The sound tuner. All controls associated with the tuner have
+// a scripted sister componenet available for composers to modify
+// using radtuner.
+//
+class daSoundTuner : public IDaSoundTuner,
+ public radRefCount
+{
+public:
+ IMPLEMENT_REFCOUNTED( "daSoundTuner" );
+
+ //
+ // Constructor and destructor
+ //
+ daSoundTuner( );
+ virtual ~daSoundTuner( );
+
+ //
+ // IDaSoundTuner
+ //
+ void Initialize( void );
+ void PostScriptLoadInitialize();
+ void ServiceOncePerFrame( unsigned int elapsedTime );
+
+ void SetSoundOutputMode
+ (
+ SoundOutputMode outputMode
+ );
+ SoundOutputMode GetSoundOutputMode( void );
+
+ void ActivateDuck
+ (
+ IDaSoundFadeState* pObject,
+ void* pUserData,
+ bool fadeIn
+ );
+
+ void ActivateSituationalDuck( IDaSoundFadeState* pObject,
+ DuckSituations situation,
+ void* pUserData,
+ bool fadeIn );
+
+ void ResetDuck();
+
+ void SetMasterVolume( daTrimValue volume );
+ daTrimValue GetMasterVolume( void );
+
+ void SetDialogueVolume( daTrimValue volume );
+ daTrimValue GetDialogueVolume( void );
+
+ void SetMusicVolume( daTrimValue volume );
+ daTrimValue GetMusicVolume( void );
+
+ void SetAmbienceVolume( daTrimValue volume );
+ daTrimValue GetAmbienceVolume( void );
+
+ void SetSfxVolume( daTrimValue volume );
+ daTrimValue GetSfxVolume( void );
+
+ void SetCarVolume( daTrimValue volume );
+ daTrimValue GetCarVolume( void );
+
+ daTrimValue GetGroupTrim( daSoundGroup group );
+ daTrimValue GetFaderGroupTrim( daSoundGroup group );
+
+ void MuteNIS();
+ void UnmuteNIS();
+
+ void SetFaderGroupTrim( Sound::DuckVolumes group, daTrimValue trim );
+
+ void FadeSounds( IDaSoundFadeState* pObject,
+ void* pUserData,
+ Fader* pFader,
+ bool fadeIn,
+ DuckVolumeSet* initialVolumes = NULL );
+
+ //
+ // IDaSoundWiring
+ //
+ void WirePath
+ (
+ daSoundGroup soundGroup,
+ const char* path
+ );
+
+ void WireGroup( daSoundGroup slaveGroup, daSoundGroup masterGroup );
+
+ //
+ // Sound group info
+ //
+ bool IsSlaveGroup( daSoundGroup slave, daSoundGroup master );
+
+protected:
+ //
+ // Helper function for wiring knob paths
+ //
+ struct WirePathInfo
+ {
+ const char* m_Path;
+ size_t m_PathLen;
+ daSoundGroup m_SoundGroup;
+ };
+ static void WireKnobToPathHelper
+ (
+ IDaSoundResource* pRes,
+ void* pUserData
+ );
+
+private:
+
+ void activateDuckInternal( IDaSoundFadeState* pObject,
+ void* pUserData,
+ bool fadeOut,
+ Fader* faderObj );
+ void calculateDuckedVolumes( DuckVolumeSet& volumes );
+
+ void refreshFaderSettings();
+
+ void serviceDebugInfo();
+
+#ifdef SOUND_DEBUG_INFO_ENABLED
+ TunerDebugPage m_debugPage;
+#endif
+
+ //
+ // How many ducks to we have in progress?
+ //
+ DuckVolumeSet m_duckLevels[NUM_DUCK_SITUATIONS];
+
+ DuckVolumeSet m_finalDuckLevels;
+
+ //
+ // Store our duck faders
+ //
+ Fader* m_pDuckFade;
+
+ Fader* m_situationFaders[NUM_DUCK_SITUATIONS];
+
+ //
+ // Group volume settings
+ //
+ float m_MasterVolume;
+
+ DuckVolumeSet m_userVolumes;
+
+ //
+ // Our static sound group wirings
+ //
+ static short s_groupWirings[NUM_SOUND_GROUPS];
+
+ daSoundTuner_ActiveFadeInfo* m_activeFadeInfo;
+
+ //
+ // NIS hack
+ //
+ float m_NISTrim;
+};
+
+} // Sound Namespace
+#endif //_SOUNDTUNER_HPP
+
diff --git a/game/code/sound/soundrenderer/tunerdebugpage.cpp b/game/code/sound/soundrenderer/tunerdebugpage.cpp
new file mode 100644
index 0000000..a792952
--- /dev/null
+++ b/game/code/sound/soundrenderer/tunerdebugpage.cpp
@@ -0,0 +1,231 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: tunerdebugpage.cpp
+//
+// Description: Displays debug info for the sound tuner
+//
+// History: 6/11/2003 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundrenderer/tunerdebugpage.h>
+
+const char* s_duckNames[Sound::NUM_DUCK_SITUATIONS] =
+{
+ "Full fade",
+ "Pause",
+ "Mission",
+ "Letterbox",
+ "Dialogue",
+ "Store",
+ "On foot",
+ "Minigame",
+ "Just Music",
+ "Credits"
+};
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// TunerDebugPage::TunerDebugPage
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+TunerDebugPage::TunerDebugPage()
+{
+ unsigned int i, j;
+
+ //
+ // Zero out the tuner values
+ //
+ for( i = 0; i < Sound::NUM_DUCK_SITUATIONS; i++ )
+ {
+ for( j = 0; j < Sound::NUM_DUCK_VOLUMES; j++ )
+ {
+ m_duckLevels[i].duckVolume[j] = 0.0f;
+ }
+ }
+
+ for( i = 0; i < Sound::NUM_DUCK_VOLUMES; i++ )
+ {
+ m_userVolumes.duckVolume[i] = 0.0f;
+ }
+}
+
+//=============================================================================
+// TunerDebugPage::~TunerDebugPage
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+TunerDebugPage::~TunerDebugPage()
+{
+}
+
+//=============================================================================
+// TunerDebugPage::SetDuckLevel
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( DuckSituations situation, DuckVolumes volumeType, float volume )
+//
+// Return: void
+//
+//=============================================================================
+void TunerDebugPage::SetDuckLevel( Sound::DuckSituations situation,
+ Sound::DuckVolumes volumeType,
+ float volume )
+{
+ m_duckLevels[situation].duckVolume[volumeType] = volume;
+}
+
+//=============================================================================
+// TunerDebugPage::SetFinalDuckLevel
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( DuckVolumes volumeType, float volume )
+//
+// Return: void
+//
+//=============================================================================
+void TunerDebugPage::SetFinalDuckLevel( Sound::DuckVolumes volumeType, float volume )
+{
+ m_finalDuckLevel.duckVolume[volumeType] = volume;
+}
+
+//=============================================================================
+// TunerDebugPage::SetUserVolume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( DuckVolumes volumeType, float volume )
+//
+// Return: void
+//
+//=============================================================================
+void TunerDebugPage::SetUserVolume( Sound::DuckVolumes volumeType, float volume )
+{
+ m_userVolumes.duckVolume[volumeType] = volume;
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// TunerDebugPage::fillLineBuffer
+//=============================================================================
+// Description: Fill the given buffer with text to display on the screen
+// at the given line
+//
+// Parameters: lineNum - line number on screen where buffer will be displayed
+// buffer - to be filled in with text to display
+//
+// Return: void
+//
+//=============================================================================
+void TunerDebugPage::fillLineBuffer( int lineNum, char* buffer )
+{
+ switch( lineNum )
+ {
+ case 0:
+ strcpy( buffer, "Ducking volumes:" );
+ break;
+
+ case 1:
+ strcpy( buffer, " SFX Car Music Dialog Ambience" );
+ break;
+
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ sprintf( buffer, "%s: %0.2f %0.2f %0.2f %0.2f %0.2f",
+ s_duckNames[lineNum-2],
+ m_duckLevels[lineNum-2].duckVolume[0],
+ m_duckLevels[lineNum-2].duckVolume[1],
+ m_duckLevels[lineNum-2].duckVolume[2],
+ m_duckLevels[lineNum-2].duckVolume[3],
+ m_duckLevels[lineNum-2].duckVolume[4] );
+ break;
+
+ case 13:
+ sprintf( buffer, "Final duck volumes: %0.2f %0.2f %0.2f %0.2f %0.2f",
+ m_finalDuckLevel.duckVolume[0],
+ m_finalDuckLevel.duckVolume[1],
+ m_finalDuckLevel.duckVolume[2],
+ m_finalDuckLevel.duckVolume[3],
+ m_finalDuckLevel.duckVolume[4] );
+ break;
+
+ case 15:
+ sprintf( buffer, "User volumes: %0.2f %0.2f %0.2f %0.2f %0.2f",
+ m_userVolumes.duckVolume[0],
+ m_userVolumes.duckVolume[1],
+ m_userVolumes.duckVolume[2],
+ m_userVolumes.duckVolume[3],
+ m_userVolumes.duckVolume[4] );
+ break;
+
+ default:
+ buffer[0] = '\0';
+ break;
+ }
+}
+
+//=============================================================================
+// DialogSoundDebugPage::getNumLines
+//=============================================================================
+// Description: Returns number of lines that we'll display on screen
+//
+// Parameters: None
+//
+// Return: Line count
+//
+//=============================================================================
+int TunerDebugPage::getNumLines()
+{
+ return( 16 );
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
diff --git a/game/code/sound/soundrenderer/tunerdebugpage.h b/game/code/sound/soundrenderer/tunerdebugpage.h
new file mode 100644
index 0000000..b48797c
--- /dev/null
+++ b/game/code/sound/soundrenderer/tunerdebugpage.h
@@ -0,0 +1,68 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: tunerdebugpage.h
+//
+// Description: Displays debug info for the sound tuner
+//
+// History: 6/11/2003 + Created -- Esan
+//
+//=============================================================================
+
+#ifndef TUNERDEBUGPAGE_H
+#define TUNERDEBUGPAGE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <sound/sounddebug/sounddebugpage.h>
+
+#include <sound/soundrenderer/dasoundgroup.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: TunerDebugPage
+//
+//=============================================================================
+
+class TunerDebugPage : public SoundDebugPage
+{
+ public:
+ TunerDebugPage();
+ virtual ~TunerDebugPage();
+
+ void SetDuckLevel( Sound::DuckSituations situation, Sound::DuckVolumes volumeType, float volume );
+ void SetFinalDuckLevel( Sound::DuckVolumes volumeType, float volume );
+ void SetUserVolume( Sound::DuckVolumes volumeType, float volume );
+
+ protected:
+ //
+ // Pure virtual functions from SoundDebugPage
+ //
+ void fillLineBuffer( int lineNum, char* buffer );
+ int getNumLines();
+
+ private:
+ //Prevent wasteful constructor creation.
+ TunerDebugPage( const TunerDebugPage& tunerdebugpage );
+ TunerDebugPage& operator=( const TunerDebugPage& tunerdebugpage );
+
+ //
+ // Ducking info
+ //
+ Sound::DuckVolumeSet m_duckLevels[Sound::NUM_DUCK_SITUATIONS];
+ Sound::DuckVolumeSet m_finalDuckLevel;
+ Sound::DuckVolumeSet m_userVolumes;
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //TUNERDEBUGPAGE_H
diff --git a/game/code/sound/soundrenderer/wireplayers.cpp b/game/code/sound/soundrenderer/wireplayers.cpp
new file mode 100644
index 0000000..c63b138
--- /dev/null
+++ b/game/code/sound/soundrenderer/wireplayers.cpp
@@ -0,0 +1 @@
+// This page made intentially empty. \ No newline at end of file
diff --git a/game/code/sound/soundrenderer/wiresystem.cpp b/game/code/sound/soundrenderer/wiresystem.cpp
new file mode 100644
index 0000000..6582a16
--- /dev/null
+++ b/game/code/sound/soundrenderer/wiresystem.cpp
@@ -0,0 +1,104 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: wiresystem.cpp
+//
+// Subsystem: Dark Angel - Sound Tuner System
+//
+// Description: Wire the tuner
+//
+// Revisions:
+// + Created October 24, 2001 -- breimer
+//
+//=============================================================================
+
+//=============================================================================
+// Included Files
+//=============================================================================
+
+#include <radobject.hpp>
+#include <raddebug.hpp>
+
+#include <radsound.hpp>
+#include <radsound_hal.hpp>
+
+#include <sound/soundrenderer/soundsystem.h>
+#include <sound/soundrenderer/idasoundtuner.h>
+#include <sound/soundrenderer/soundtuner.h>
+
+//=============================================================================
+// Define Owning Namespace
+//=============================================================================
+
+namespace Sound {
+
+//=============================================================================
+// Public functions
+//=============================================================================
+
+//=============================================================================
+// Function: ::daSoundTunerWireSystem
+//=============================================================================
+// Description: Wire the sound system
+//
+// NOTE: Music and ambience are ignored here, because they're controlled by
+// Radmusic and not us.
+//
+//-----------------------------------------------------------------------------
+
+void daSoundTunerWireSystem
+(
+ IDaSoundWiring* pWiring
+)
+{
+ // PATHS //////////////////////////////////////////////////////////////////
+
+ //
+ // Start by wiring up everything as dialogue by default. This is because
+ // we strip a little path info out of the dialogue files, so that we can
+ // easily switch between languages.
+ //
+ pWiring->WirePath( DIALOGUE, "" );
+
+ // Character
+ pWiring->WirePath
+ (
+ CARSOUND,
+ "sound\\carsound"
+ );
+
+ // Collision
+ pWiring->WirePath
+ (
+ NIS,
+ "sound\\nis"
+ );
+
+ pWiring->WirePath
+ (
+ SOUND_EFFECTS,
+ "sound\\soundfx"
+ );
+
+ pWiring->WirePath
+ (
+ OPTIONS_MENU_STINGERS,
+ "sound\\soundfx\\optionsmenu"
+ );
+
+ // SPECIAL GROUPS /////////////////////////////////////////////////////////
+
+ pWiring->WireGroup( NIS, DIALOGUE );
+
+ pWiring->WireGroup( DIALOGUE, DUCKABLE );
+ pWiring->WireGroup( SOUND_EFFECTS, DUCKABLE );
+ pWiring->WireGroup( CARSOUND, DUCKABLE );
+ pWiring->WireGroup( OPTIONS_MENU_STINGERS, DUCKABLE );
+
+ pWiring->WireGroup( DIALOGUE, DIALOGUE_TUNE );
+ pWiring->WireGroup( SOUND_EFFECTS, SOUND_EFFECTS_TUNE );
+ pWiring->WireGroup( MASTER, MASTER_TUNE );
+}
+
+
+} // Sound Namespace
diff --git a/game/code/sound/soundrenderercallback.cpp b/game/code/sound/soundrenderercallback.cpp
new file mode 100644
index 0000000..9f2cfb3
--- /dev/null
+++ b/game/code/sound/soundrenderercallback.cpp
@@ -0,0 +1,181 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundrenderercallback.cpp
+//
+// Description: Implement SoundRenderingPlayerCallback
+//
+// History: 06/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/soundrenderercallback.h>
+
+#include <sound/simpsonssoundplayer.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Initialially the list is empty
+//
+SoundRenderingPlayerCallback* radLinkedClass< SoundRenderingPlayerCallback >::s_pLinkedClassHead = NULL;
+SoundRenderingPlayerCallback* radLinkedClass< SoundRenderingPlayerCallback >::s_pLinkedClassTail = NULL;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// SoundRenderingPlayerCallback::SoundRenderingPlayerCallback
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundRenderingPlayerCallback::SoundRenderingPlayerCallback( SimpsonsSoundPlayer& playerObj,
+ SimpsonsSoundPlayerCallback* callbackObj ) :
+ m_callbackObj( callbackObj ),
+ m_playerObj( &playerObj )
+{
+}
+
+//==============================================================================
+// SoundRenderingPlayerCallback::~SoundRenderingPlayerCallback
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+SoundRenderingPlayerCallback::~SoundRenderingPlayerCallback()
+{
+}
+
+//=============================================================================
+// SoundRenderingPlayerCallback::CancelGameCallbackAndRelease
+//=============================================================================
+// Description: Called from the model layer when we're no longer interested
+// in callbacks, probably because the sound player is stopping.
+// Empty out the callback member and release.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundRenderingPlayerCallback::CancelGameCallbackAndRelease()
+{
+ m_callbackObj = NULL;
+ m_playerObj = NULL;
+ Release();
+}
+
+//=============================================================================
+// SoundRenderingPlayerCallback::OnSoundReady
+//=============================================================================
+// Description: Implements function for IDaSoundSoundState interface. Called
+// when the sound renderer player has a sound cued and ready to
+// play.
+//
+// Parameters: pData - user data
+//
+// Return: void
+//
+// Return: void
+//
+//=============================================================================
+void SoundRenderingPlayerCallback::OnSoundReady( void* pData )
+{
+ //
+ // Let the client know that the sound is ready for immediate playback
+ //
+ if( m_callbackObj != NULL )
+ {
+ m_callbackObj->OnSoundReady();
+ }
+}
+
+//=============================================================================
+// SoundRenderingPlayerCallback::OnSoundDone
+//=============================================================================
+// Description: Implements function for IDaSoundSoundState interface. Called
+// when the sound renderer player is finished playing something.
+//
+// Parameters: pData - user data
+//
+// Return: void
+//
+//=============================================================================
+void SoundRenderingPlayerCallback::OnSoundDone( void* pData )
+{
+ SimpsonsSoundPlayerCallback* callbackObj = m_callbackObj;
+
+ //
+ // AddRef so that none of the callback stuff below can wipe us out until
+ // we're done
+ //
+ AddRef();
+
+ //
+ // Trigger the player callback first so that it can clean itself up and
+ // make itself available for the client receiving the callback. Save
+ // a copy of the client callback, since the player might ask us to
+ // wipe it out
+ //
+ if( m_playerObj != NULL )
+ {
+ m_playerObj->OnPlaybackComplete();
+ }
+
+ //
+ // Now the client, who can reuse the same player now
+ //
+ if( callbackObj != NULL )
+ {
+ callbackObj->OnPlaybackComplete();
+ }
+
+ Release();
+}
+
+//=============================================================================
+// SoundRenderingPlayerCallback::CompletionCheck
+//=============================================================================
+// Description: A sanity check, usable by anyone who wants to ensure that all
+// sound players are closed and nobody is waiting around for
+// callbacks anymore. If any SoundRenderingPlayerCallback objects
+// are still kicking around, we fail.
+//
+// Parameters: None
+//
+// Return: void
+//
+//=============================================================================
+void SoundRenderingPlayerCallback::CompletionCheck()
+{
+ rAssertMsg( GetLinkedClassHead() == NULL, "GAAAAAK!! Sound renderer players are still running when they're not supposed to be!\n" );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/sound/soundrenderercallback.h b/game/code/sound/soundrenderercallback.h
new file mode 100644
index 0000000..e0a4744
--- /dev/null
+++ b/game/code/sound/soundrenderercallback.h
@@ -0,0 +1,70 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: soundrenderercallback.h
+//
+// Description: Declaration of SoundRenderingPlayerCallback class. Used from
+// sound renderer for player-related callbacks.
+//
+// History: 06/07/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef SOUNDRENDERERCALLBACK_H
+#define SOUNDRENDERERCALLBACK_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radobject.hpp>
+#include <radlinkedclass.hpp>
+
+#include <sound/soundrenderer/soundsystem.h>
+
+//========================================
+// Forward References
+//========================================
+
+struct SimpsonsSoundPlayerCallback;
+class SimpsonsSoundPlayer;
+
+//=============================================================================
+//
+// Synopsis: SoundRenderingPlayerCallback
+//
+//=============================================================================
+
+class SoundRenderingPlayerCallback : public Sound::IDaSoundPlayerState,
+ public radLinkedClass< SoundRenderingPlayerCallback >,
+ public radRefCount
+{
+ public:
+ IMPLEMENT_REFCOUNTED( "SoundRenderingPlayerCallback" );
+
+ SoundRenderingPlayerCallback( SimpsonsSoundPlayer& playerObj,
+ SimpsonsSoundPlayerCallback* callbackObj );
+ virtual ~SoundRenderingPlayerCallback();
+
+ void CancelGameCallbackAndRelease();
+
+ static void CompletionCheck();
+
+ // Currently unused
+ void OnSoundReady( void* pData );
+
+ // Called when sound renderer player has completed playback
+ void OnSoundDone( void* pData );
+
+ private:
+ //Prevent wasteful constructor creation.
+ SoundRenderingPlayerCallback();
+ SoundRenderingPlayerCallback( const SoundRenderingPlayerCallback& original );
+ SoundRenderingPlayerCallback& operator=( const SoundRenderingPlayerCallback& rhs );
+
+ SimpsonsSoundPlayerCallback* m_callbackObj;
+ SimpsonsSoundPlayer* m_playerObj;
+};
+
+
+#endif // SOUNDRENDERERCALLBACK_H
+
diff --git a/game/code/sound/tuning/allsoundtuning.cpp b/game/code/sound/tuning/allsoundtuning.cpp
new file mode 100644
index 0000000..325a1ca
--- /dev/null
+++ b/game/code/sound/tuning/allsoundtuning.cpp
@@ -0,0 +1 @@
+#include <sound/tuning/globalsettings.cpp>
diff --git a/game/code/sound/tuning/globalsettings.cpp b/game/code/sound/tuning/globalsettings.cpp
new file mode 100644
index 0000000..f6193fc
--- /dev/null
+++ b/game/code/sound/tuning/globalsettings.cpp
@@ -0,0 +1,467 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: globalsettings.cpp
+//
+// Description: Implementation of globalSettings, which sets global sound values
+// in the game (e.g. master volume, sound defaults). Created
+// using RadScript, hence the lower-case g.
+//
+// History: 07/08/2002 + Created -- Darren
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+//========================================
+// Project Includes
+//========================================
+#include <sound/tuning/globalsettings.h>
+
+#include <sound/soundmanager.h>
+#include <memory/srrmemory.h>
+
+using namespace Sound;
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// Initialially the list is empty
+//
+globalSettings* radLinkedClass< globalSettings >::s_pLinkedClassHead = NULL;
+globalSettings* radLinkedClass< globalSettings >::s_pLinkedClassTail = NULL;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// globalSettings::globalSettings
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+globalSettings::globalSettings() :
+ radRefCount( 0 ),
+ m_peeloutMin( 0.0f ),
+ m_peeloutMax( 1.0f ),
+ m_peeloutMaxTrim( 1.0f ),
+ m_roadSkidClip( NULL ),
+ m_dirtSkidClip( NULL ),
+ m_roadFootstepClip( NULL ),
+ m_metalFootstepClip( NULL ),
+ m_woodFootstepClip( NULL ),
+ m_coinPitchCount( 0 ),
+ m_ambienceVolume( 0.0f ),
+ m_musicVolume( 0.0f ),
+ m_sfxVolume( 0.0f ),
+ m_dialogueVolume( 0.0f ),
+ m_carVolume( 0.0f )
+{
+ unsigned int i, j;
+
+ for( i = 0; i < NUM_DUCK_SITUATIONS; i++ )
+ {
+ for( j = 0; j < NUM_DUCK_VOLUMES; j++ )
+ {
+ m_duckVolumes[i].duckVolume[j] = 0.0f;
+ }
+ }
+
+ for( i = 0; i < s_maxCoinPitches; i++ )
+ {
+ m_coinPitches[i] = 1.0f;
+ }
+}
+
+//==============================================================================
+// globalSettings::~globalSettings
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+globalSettings::~globalSettings()
+{
+ if( m_roadSkidClip != NULL )
+ {
+ delete( GMA_AUDIO_PERSISTENT, m_roadSkidClip );
+ }
+ if( m_dirtSkidClip != NULL )
+ {
+ delete( GMA_AUDIO_PERSISTENT, m_dirtSkidClip );
+ }
+
+ if( m_roadFootstepClip != NULL )
+ {
+ delete( GMA_AUDIO_PERSISTENT, m_roadFootstepClip );
+ }
+ if( m_metalFootstepClip != NULL )
+ {
+ delete( GMA_AUDIO_PERSISTENT, m_metalFootstepClip );
+ }
+ if( m_woodFootstepClip != NULL )
+ {
+ delete( GMA_AUDIO_PERSISTENT, m_woodFootstepClip );
+ }
+}
+
+//=============================================================================
+// globalSettings::SetMasterVolume
+//=============================================================================
+// Description: Sets master volume, obviously
+//
+// Parameters: volume - new volume level
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetMasterVolume( float volume )
+{
+ GetSoundManager()->SetMasterVolume( volume );
+}
+
+//=============================================================================
+// globalSettings::SetSfxVolume
+//=============================================================================
+// Description: Sets sfx volume, obviously
+//
+// Parameters: volume - new volume level
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetSfxVolume( float volume )
+{
+ m_sfxVolume = volume;
+}
+
+//=============================================================================
+// globalSettings::SetCarVolume
+//=============================================================================
+// Description: Sets car volume, obviously
+//
+// Parameters: volume - new volume level
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetCarVolume( float volume )
+{
+ m_carVolume = volume;
+}
+
+//=============================================================================
+// globalSettings::SetMusicVolume
+//=============================================================================
+// Description: Sets music volume, obviously
+//
+// Parameters: volume - new volume level
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetMusicVolume( float volume )
+{
+ m_musicVolume = volume;
+}
+
+//=============================================================================
+// globalSettings::SetDialogueVolume
+//=============================================================================
+// Description: Sets dialogue volume, obviously
+//
+// Parameters: volume - new volume level
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetDialogueVolume( float volume )
+{
+ m_dialogueVolume = volume;
+}
+
+//=============================================================================
+// globalSettings::SetAmbienceVolume
+//=============================================================================
+// Description: Sets ambience volume, obviously
+//
+// Parameters: volume - new volume level
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetAmbienceVolume( float volume )
+{
+ m_ambienceVolume = volume;
+}
+
+//=============================================================================
+// globalSettings::SetPeeloutMin
+//=============================================================================
+// Description: Set minimum peelout value at which we play sound
+//
+// Parameters: min - peelout value, 0-1
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetPeeloutMin( float min )
+{
+ rAssert( min >= 0.0f );
+ rAssert( min <= 1.0f );
+
+ m_peeloutMin = min;
+}
+
+//=============================================================================
+// globalSettings::SetPeeloutMax
+//=============================================================================
+// Description: Set peelout value at which sound is at maximum volume
+//
+// Parameters: max - peelout value, 0-1
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetPeeloutMax( float max )
+{
+ rAssert( max >= 0.0f );
+ rAssert( max <= 1.0f );
+
+ m_peeloutMax = max;
+}
+
+//=============================================================================
+// globalSettings::SetPeeloutMaxTrim
+//=============================================================================
+// Description: Set maximum trim applied to peelout sound
+//
+// Parameters: trim - max trim value
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetPeeloutMaxTrim( float trim )
+{
+ rAssert( trim >= 0.0f );
+
+ m_peeloutMaxTrim = trim;
+}
+
+//=============================================================================
+// globalSettings::SetSkidRoadClipName
+//=============================================================================
+// Description: Set name of sound resource for road skids
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetSkidRoadClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT );
+
+ m_roadSkidClip = new char[strlen(clipName)+1];
+ strcpy( m_roadSkidClip, clipName );
+
+ HeapMgr()->PopHeap(GMA_AUDIO_PERSISTENT);
+}
+
+//=============================================================================
+// globalSettings::SetSkidDirtClipName
+//=============================================================================
+// Description: Set name of sound resource for dirt skids
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetSkidDirtClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT );
+
+ m_dirtSkidClip = new char[strlen(clipName)+1];
+ strcpy( m_dirtSkidClip, clipName );
+
+ HeapMgr()->PopHeap(GMA_AUDIO_PERSISTENT);
+}
+
+//=============================================================================
+// globalSettings::SetFootstepRoadClipName
+//=============================================================================
+// Description: Set name of sound resource for footsteps on road surfaces
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetFootstepRoadClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT );
+
+ m_roadFootstepClip = new char[strlen(clipName)+1];
+ strcpy( m_roadFootstepClip, clipName );
+
+ HeapMgr()->PopHeap(GMA_AUDIO_PERSISTENT);
+}
+
+//=============================================================================
+// globalSettings::SetFootstepMetalClipName
+//=============================================================================
+// Description: Set name of sound resource for footsteps on metal surfaces
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetFootstepMetalClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT );
+
+ m_metalFootstepClip = new char[strlen(clipName)+1];
+ strcpy( m_metalFootstepClip, clipName );
+
+ HeapMgr()->PopHeap(GMA_AUDIO_PERSISTENT);
+}
+
+//=============================================================================
+// globalSettings::SetFootstepWoodClipName
+//=============================================================================
+// Description: Set name of sound resource for footsteps on wood surfaces
+//
+// Parameters: clipName - name of sound resource
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetFootstepWoodClipName( const char* clipName )
+{
+ rAssert( clipName != NULL );
+
+ HeapMgr()->PushHeap( GMA_AUDIO_PERSISTENT );
+
+ m_woodFootstepClip = new char[strlen(clipName)+1];
+ strcpy( m_woodFootstepClip, clipName );
+
+ HeapMgr()->PopHeap(GMA_AUDIO_PERSISTENT);
+}
+
+//=============================================================================
+// globalSettings::SetCoinPitch
+//=============================================================================
+// Description: Set pitch for coin pickup sound in sequence (so we can make
+// a tune out of the coin sounds or something)
+//
+// Parameters: pitch - pitch for next coin playback in sequence
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::SetCoinPitch( float pitch )
+{
+ if( m_coinPitchCount < s_maxCoinPitches )
+ {
+ m_coinPitches[m_coinPitchCount++] = pitch;
+ }
+ else
+ {
+ rDebugString( "Too many coin pitches specified in script\n" );
+ }
+}
+
+//=============================================================================
+// globalSettings::GetCoinPitch
+//=============================================================================
+// Description: Get a particular pitch within coin sequence
+//
+// Parameters: index - index into coin pitch sequence
+//
+// Return: the pitch
+//
+//=============================================================================
+float globalSettings::GetCoinPitch( unsigned int index )
+{
+ rAssert( index < s_maxCoinPitches );
+
+ return( m_coinPitches[index] );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+//=============================================================================
+// globalSettings::setDuckVolume
+//=============================================================================
+// Description: Store the ducking volume for a particular volume setting and
+// ducking situation
+//
+// Parameters: situation - ducking situation
+// volumeToSet - which volume to set within that situation
+// volume - volume value to set
+//
+// Return: void
+//
+//=============================================================================
+void globalSettings::setDuckVolume( DuckSituations situation, DuckVolumes volumeToSet, float volume )
+{
+ m_duckVolumes[situation].duckVolume[volumeToSet] = volume;
+}
+
+//******************************************************************************
+// Factory functions
+//******************************************************************************
+
+//==============================================================================
+// GlobalSettingsObjCreate
+//==============================================================================
+// Description: Factory function for creating globalSettings objects.
+// Called by RadScript.
+//
+// Parameters: ppParametersObj - Address of ptr to new object
+// allocator - FTT pool to allocate object within
+//
+// Return: N/A.
+//
+//==============================================================================
+void GlobalSettingsObjCreate
+(
+ IGlobalSettings** ppParametersObj,
+ radMemoryAllocator allocator
+)
+{
+ rAssert( ppParametersObj != NULL );
+ (*ppParametersObj) = new ( allocator ) globalSettings( );
+ (*ppParametersObj)->AddRef( );
+}
+
diff --git a/game/code/sound/tuning/globalsettings.h b/game/code/sound/tuning/globalsettings.h
new file mode 100644
index 0000000..13b8417
--- /dev/null
+++ b/game/code/sound/tuning/globalsettings.h
@@ -0,0 +1,224 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: globalsettings.h
+//
+// Description: Declaration of globalSettings, which sets global sound values
+// in the game (e.g. master volume, sound defaults). Created
+// using RadScript, hence the lower-case g.
+//
+// History: 07/08/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef GLOBALSETTINGS_H
+#define GLOBALSETTINGS_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radlinkedclass.hpp>
+
+#include <sound/tuning/iglobalsettings.h>
+#include <sound/soundrenderer/dasoundgroup.h>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: globalSettings
+//
+//=============================================================================
+
+class globalSettings : public IGlobalSettings,
+ public radLinkedClass< globalSettings >,
+ public radRefCount
+{
+ public:
+ IMPLEMENT_REFCOUNTED( "globalSettings" );
+
+ globalSettings();
+ virtual ~globalSettings();
+
+ //
+ // Volume controls
+ //
+ void SetMasterVolume( float volume );
+
+ void SetSfxVolume( float volume );
+ float GetSfxVolume() { return( m_sfxVolume ); }
+
+ void SetCarVolume( float volume );
+ float GetCarVolume() { return( m_carVolume ); }
+
+ void SetMusicVolume( float volume );
+ float GetMusicVolume() { return( m_musicVolume ); }
+
+ void SetDialogueVolume( float volume );
+ float GetDialogueVolume() { return( m_dialogueVolume ); }
+
+ void SetAmbienceVolume( float volume );
+ float GetAmbienceVolume() { return( m_ambienceVolume ); }
+
+ //
+ // Ducking controls
+ //
+ float GetDuckVolume( Sound::DuckSituations situation, Sound::DuckVolumes volume ) { return( m_duckVolumes[situation].duckVolume[volume] ); }
+
+ void SetPauseSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_PAUSE, Sound::DUCK_SFX, volume ); }
+ void SetPauseCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_PAUSE, Sound::DUCK_CAR, volume ); }
+ void SetPauseMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_PAUSE, Sound::DUCK_MUSIC, volume ); }
+ void SetPauseDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_PAUSE, Sound::DUCK_DIALOG, volume ); }
+ void SetPauseAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_PAUSE, Sound::DUCK_AMBIENCE, volume ); }
+
+ void SetMissionScreenSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MISSION, Sound::DUCK_SFX, volume ); }
+ void SetMissionScreenCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MISSION, Sound::DUCK_CAR, volume ); }
+ void SetMissionScreenMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MISSION, Sound::DUCK_MUSIC, volume ); }
+ void SetMissionScreenDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MISSION, Sound::DUCK_DIALOG, volume ); }
+ void SetMissionScreenAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MISSION, Sound::DUCK_AMBIENCE, volume ); }
+
+ void SetLetterboxSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_LETTERBOX, Sound::DUCK_SFX, volume ); }
+ void SetLetterboxCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_LETTERBOX, Sound::DUCK_CAR, volume ); }
+ void SetLetterboxMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_LETTERBOX, Sound::DUCK_MUSIC, volume ); }
+ void SetLetterboxDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_LETTERBOX, Sound::DUCK_DIALOG, volume ); }
+ void SetLetterboxAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_LETTERBOX, Sound::DUCK_AMBIENCE, volume ); }
+
+ void SetDialogueSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_DIALOG, Sound::DUCK_SFX, volume ); }
+ void SetDialogueCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_DIALOG, Sound::DUCK_CAR, volume ); }
+ void SetDialogueMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_DIALOG, Sound::DUCK_MUSIC, volume ); }
+ void SetDialogueDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_DIALOG, Sound::DUCK_DIALOG, volume ); }
+ void SetDialogueAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_DIALOG, Sound::DUCK_AMBIENCE, volume ); }
+
+ void SetStoreSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_STORE, Sound::DUCK_SFX, volume ); }
+ void SetStoreCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_STORE, Sound::DUCK_CAR, volume ); }
+ void SetStoreMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_STORE, Sound::DUCK_MUSIC, volume ); }
+ void SetStoreDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_STORE, Sound::DUCK_DIALOG, volume ); }
+ void SetStoreAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_STORE, Sound::DUCK_AMBIENCE, volume ); }
+
+ void SetOnFootSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_ONFOOT, Sound::DUCK_SFX, volume ); }
+ void SetOnFootCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_ONFOOT, Sound::DUCK_CAR, volume ); }
+ void SetOnFootMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_ONFOOT, Sound::DUCK_MUSIC, volume ); }
+ void SetOnFootDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_ONFOOT, Sound::DUCK_DIALOG, volume ); }
+ void SetOnFootAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_ONFOOT, Sound::DUCK_AMBIENCE, volume ); }
+
+ void SetMinigameSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MINIGAME, Sound::DUCK_SFX, volume ); }
+ void SetMinigameCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MINIGAME, Sound::DUCK_CAR, volume ); }
+ void SetMinigameMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MINIGAME, Sound::DUCK_MUSIC, volume ); }
+ void SetMinigameDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MINIGAME, Sound::DUCK_DIALOG, volume ); }
+ void SetMinigameAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_MINIGAME, Sound::DUCK_AMBIENCE, volume ); }
+
+ void SetJustMusicSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_JUST_MUSIC, Sound::DUCK_SFX, volume ); }
+ void SetJustMusicCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_JUST_MUSIC, Sound::DUCK_CAR, volume ); }
+ void SetJustMusicMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_JUST_MUSIC, Sound::DUCK_MUSIC, volume ); }
+ void SetJustMusicDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_JUST_MUSIC, Sound::DUCK_DIALOG, volume ); }
+ void SetJustMusicAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_JUST_MUSIC, Sound::DUCK_AMBIENCE, volume ); }
+
+ void SetCreditsSfxVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_CREDITS, Sound::DUCK_SFX, volume ); }
+ void SetCreditsCarVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_CREDITS, Sound::DUCK_CAR, volume ); }
+ void SetCreditsMusicVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_CREDITS, Sound::DUCK_MUSIC, volume ); }
+ void SetCreditsDialogueVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_CREDITS, Sound::DUCK_DIALOG, volume ); }
+ void SetCreditsAmbienceVolume( float volume ) { setDuckVolume( Sound::DUCK_SIT_CREDITS, Sound::DUCK_AMBIENCE, volume ); }
+
+ //
+ // Car controls
+ //
+ void SetPeeloutMin( float min );
+ float GetPeeloutMin() { return( m_peeloutMin ); }
+
+ void SetPeeloutMax( float max );
+ float GetPeeloutMax() { return( m_peeloutMax ); }
+
+ void SetPeeloutMaxTrim( float trim );
+ float GetPeeloutMaxTrim() { return( m_peeloutMaxTrim ); }
+
+ void SetSkidRoadClipName( const char* clipName );
+ const char* GetSkidRoadClipName() { return( m_roadSkidClip ); }
+
+ void SetSkidDirtClipName( const char* clipName );
+ const char* GetSkidDirtClipName() { return( m_dirtSkidClip ); }
+
+ //
+ // Footstep sounds
+ //
+ void SetFootstepRoadClipName( const char* clipName );
+ const char* GetFootstepRoadClipName() { return( m_roadFootstepClip ); }
+
+ void SetFootstepMetalClipName( const char* clipName );
+ const char* GetFootstepMetalClipName() { return( m_metalFootstepClip ); }
+
+ void SetFootstepWoodClipName( const char* clipName );
+ const char* GetFootstepWoodClipName() { return( m_woodFootstepClip ); }
+
+ //
+ // Coin pitches
+ //
+ void SetCoinPitch( float pitch );
+ float GetCoinPitch( unsigned int index );
+ unsigned int GetNumCoinPitches() { return( m_coinPitchCount ); }
+
+ private:
+
+ //Prevent wasteful constructor creation.
+ globalSettings( const globalSettings& original );
+ globalSettings& operator=( const globalSettings& rhs );
+
+ void setDuckVolume( Sound::DuckSituations situation, Sound::DuckVolumes volumeToSet, float volume );
+
+ //
+ // Ducking settings
+ //
+ Sound::DuckVolumeSet m_duckVolumes[Sound::NUM_DUCK_SITUATIONS];
+
+ //
+ // Car settings
+ //
+ float m_peeloutMin;
+ float m_peeloutMax;
+ float m_peeloutMaxTrim;
+
+ char* m_roadSkidClip;
+ char* m_dirtSkidClip;
+
+ //
+ // Footsteps
+ //
+ char* m_roadFootstepClip;
+ char* m_metalFootstepClip;
+ char* m_woodFootstepClip;
+
+ //
+ // Coin pitches
+ //
+ static const unsigned int s_maxCoinPitches = 10;
+ float m_coinPitches[s_maxCoinPitches];
+ unsigned int m_coinPitchCount;
+
+ //
+ // Hack!!
+ //
+ float m_ambienceVolume;
+ float m_musicVolume;
+ float m_sfxVolume;
+ float m_dialogueVolume;
+ float m_carVolume;
+};
+
+//=============================================================================
+// Factory Functions
+//=============================================================================
+
+//
+// Create a CarSoundParameters object
+//
+void GlobalSettingsObjCreate
+(
+ IGlobalSettings** ppSoundResource,
+ radMemoryAllocator allocator
+);
+
+
+
+#endif // GLOBALSETTINGS_H
+
diff --git a/game/code/sound/tuning/iglobalsettings.h b/game/code/sound/tuning/iglobalsettings.h
new file mode 100644
index 0000000..3fac89c
--- /dev/null
+++ b/game/code/sound/tuning/iglobalsettings.h
@@ -0,0 +1,128 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: iglobalsettings.h
+//
+// Description: Declaration of interface class IGlobalSettings, which sets
+// global sound values in the game (e.g. master volume,
+// sound defaults). Created using RadScript.
+//
+// History: 07/08/2002 + Created -- Darren
+//
+//=============================================================================
+
+#ifndef IGLOBALSETTINGS_H
+#define IGLOBALSETTINGS_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radobject.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: IGlobalSettings
+//
+//=============================================================================
+
+class IGlobalSettings : public IRefCount
+{
+ public:
+ //
+ // Volume controls
+ //
+ virtual void SetMasterVolume( float volume ) = 0;
+
+ virtual void SetSfxVolume( float volume ) = 0;
+ virtual void SetCarVolume( float volume ) = 0;
+ virtual void SetMusicVolume( float volume ) = 0;
+ virtual void SetDialogueVolume( float volume ) = 0;
+ virtual void SetAmbienceVolume( float volume ) = 0;
+
+ //
+ // Ducking controls
+ //
+ virtual void SetPauseSfxVolume( float volume ) = 0;
+ virtual void SetPauseCarVolume( float volume ) = 0;
+ virtual void SetPauseMusicVolume( float volume ) = 0;
+ virtual void SetPauseDialogueVolume( float volume ) = 0;
+ virtual void SetPauseAmbienceVolume( float volume ) = 0;
+
+ virtual void SetMissionScreenSfxVolume( float volume ) = 0;
+ virtual void SetMissionScreenCarVolume( float volume ) = 0;
+ virtual void SetMissionScreenMusicVolume( float volume ) = 0;
+ virtual void SetMissionScreenDialogueVolume( float volume ) = 0;
+ virtual void SetMissionScreenAmbienceVolume( float volume ) = 0;
+
+ virtual void SetLetterboxSfxVolume( float volume ) = 0;
+ virtual void SetLetterboxCarVolume( float volume ) = 0;
+ virtual void SetLetterboxMusicVolume( float volume ) = 0;
+ virtual void SetLetterboxDialogueVolume( float volume ) = 0;
+ virtual void SetLetterboxAmbienceVolume( float volume ) = 0;
+
+ virtual void SetDialogueSfxVolume( float volume ) = 0;
+ virtual void SetDialogueCarVolume( float volume ) = 0;
+ virtual void SetDialogueMusicVolume( float volume ) = 0;
+ virtual void SetDialogueDialogueVolume( float volume ) = 0;
+ virtual void SetDialogueAmbienceVolume( float volume ) = 0;
+
+ virtual void SetStoreSfxVolume( float volume ) = 0;
+ virtual void SetStoreCarVolume( float volume ) = 0;
+ virtual void SetStoreMusicVolume( float volume ) = 0;
+ virtual void SetStoreDialogueVolume( float volume ) = 0;
+ virtual void SetStoreAmbienceVolume( float volume ) = 0;
+
+ virtual void SetOnFootSfxVolume( float volume ) = 0;
+ virtual void SetOnFootCarVolume( float volume ) = 0;
+ virtual void SetOnFootMusicVolume( float volume ) = 0;
+ virtual void SetOnFootDialogueVolume( float volume ) = 0;
+ virtual void SetOnFootAmbienceVolume( float volume ) = 0;
+
+ virtual void SetMinigameSfxVolume( float volume ) = 0;
+ virtual void SetMinigameCarVolume( float volume ) = 0;
+ virtual void SetMinigameMusicVolume( float volume ) = 0;
+ virtual void SetMinigameDialogueVolume( float volume ) = 0;
+ virtual void SetMinigameAmbienceVolume( float volume ) = 0;
+
+ virtual void SetJustMusicSfxVolume( float volume ) = 0;
+ virtual void SetJustMusicCarVolume( float volume ) = 0;
+ virtual void SetJustMusicMusicVolume( float volume ) = 0;
+ virtual void SetJustMusicDialogueVolume( float volume ) = 0;
+ virtual void SetJustMusicAmbienceVolume( float volume ) = 0;
+
+ virtual void SetCreditsSfxVolume( float volume ) = 0;
+ virtual void SetCreditsCarVolume( float volume ) = 0;
+ virtual void SetCreditsMusicVolume( float volume ) = 0;
+ virtual void SetCreditsDialogueVolume( float volume ) = 0;
+ virtual void SetCreditsAmbienceVolume( float volume ) = 0;
+
+ //
+ // Car controls
+ //
+ virtual void SetPeeloutMin( float min ) = 0;
+ virtual void SetPeeloutMax( float max ) = 0;
+ virtual void SetPeeloutMaxTrim( float trim ) = 0;
+
+ virtual void SetSkidRoadClipName( const char* clipName ) = 0;
+ virtual void SetSkidDirtClipName( const char* clipName ) = 0;
+
+ //
+ // Footstep sounds
+ //
+ virtual void SetFootstepRoadClipName( const char* clipName ) = 0;
+ virtual void SetFootstepMetalClipName( const char* clipName ) = 0;
+ virtual void SetFootstepWoodClipName( const char* clipName ) = 0;
+
+ //
+ // Coin pitches
+ //
+ virtual void SetCoinPitch( float pitch ) = 0;
+};
+
+
+#endif // IGLOBALSETTINGS_H
+
diff --git a/game/code/stateprop/allstateprop.cpp b/game/code/stateprop/allstateprop.cpp
new file mode 100644
index 0000000..41a5bb1
--- /dev/null
+++ b/game/code/stateprop/allstateprop.cpp
@@ -0,0 +1,2 @@
+#include <stateprop/stateprop.cpp>
+#include <stateprop/statepropdata.cpp>
diff --git a/game/code/stateprop/stateprop.cpp b/game/code/stateprop/stateprop.cpp
new file mode 100644
index 0000000..42fe54f
--- /dev/null
+++ b/game/code/stateprop/stateprop.cpp
@@ -0,0 +1,361 @@
+#include <p3d/utility.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/anim/animatedobject.hpp>
+#include <p3d/anim/animate.hpp>
+
+#include "stateprop/stateprop.hpp"
+#include "stateprop/statepropdata.hpp"
+#include <ai/actor/actoranimation.h>
+
+using namespace StatePropDataTypes;
+
+CStateProp* CStateProp::CreateStateProp( CStatePropData* statePropData , unsigned int state, tPose* pose )
+{
+ tAnimatedObjectFactory* objectFactory = statePropData->GetAnimatedObjectFactory();
+
+ P3DASSERT( objectFactory );
+
+ if ( objectFactory )
+ {
+ tAnimatedObject* animatedObject = objectFactory->CreateObject(NULL, pose);
+ CStateProp* stateProp = new CStateProp( animatedObject , statePropData , state );
+ stateProp->SetName( statePropData->GetName() );
+ return stateProp;
+ }
+
+ return NULL;
+}
+
+CStateProp::CStateProp( tAnimatedObject* animatedObject , CStatePropData* statePropData , unsigned int state ) :
+ m_StatePropData( NULL ),
+ m_AnimatedObject( NULL ),
+ m_BaseFrameController( NULL ),
+ m_FastDisplayDrawable( NULL ),
+ m_CurrentState( state ),
+ m_NumStatePropListeners( 0 )
+{
+ //set state data
+ P3DASSERT( statePropData );
+ m_StatePropData = statePropData;
+ m_StatePropData->AddRef();
+
+ //set animated object
+ P3DASSERT( animatedObject );
+ m_AnimatedObject = animatedObject;
+ m_AnimatedObject->AddRef();
+
+ //base frame controller is run in milliseconds (ie the 1 is the timer)
+ m_BaseFrameController = new tMultiController(0 , 1000.f);
+ m_BaseFrameController->AddRef();
+ m_BaseFrameController->Reset();
+
+ //initialize the state
+ SetState( state );
+}
+
+CStateProp::~CStateProp()
+{
+ if ( m_BaseFrameController )
+ m_BaseFrameController->Release();
+ if ( m_StatePropData )
+ m_StatePropData->Release();
+ if ( m_AnimatedObject )
+ m_AnimatedObject->Release();
+
+ if ( m_FastDisplayDrawable )
+ m_FastDisplayDrawable->Release();
+}
+
+void CStateProp::Update( float dt )
+{
+ const TransitionData& tansdata = m_StatePropData->GetTransitionData( m_CurrentState );
+
+ unsigned int i;
+
+ float lastFrame = m_BaseFrameController->GetFrame();
+ m_BaseFrameController->Advance( dt );
+ float newFrame = m_BaseFrameController->GetFrame();
+
+ //Check out transition
+ if ( tansdata.autoTransition )
+ {
+ if (tansdata.onFrame >= lastFrame && tansdata.onFrame < newFrame)
+ {
+ SetState( tansdata.toState );
+ }
+ }
+
+ //Check callback events
+ if ( m_NumStatePropListeners > 0 )
+ {
+ for ( i = 0; i < m_StatePropData->GetNumberOfCallbacks( m_CurrentState ); i++ )
+ {
+ CallbackData callbackData = m_StatePropData->GetCallbackData( m_CurrentState , i );
+ if ( callbackData.onFrame >= lastFrame && callbackData.onFrame < newFrame)
+ {
+ unsigned int i;
+ for ( i = 0; i < m_NumStatePropListeners; i++ )
+ {
+ m_StatePropListener[i]->RecieveEvent( callbackData.callbackID , this );
+ }
+ }
+ }
+ }
+
+ if ( m_FastDisplayDrawable == NULL )
+ {
+ //update frame controllers
+ unsigned int numFrameControllers = GetNumberOfFrameControllers();
+ for ( i = 0; i < numFrameControllers; i++ )
+ {
+ FrameControllerData fcData = m_StatePropData->GetFrameControllerData( m_CurrentState , i );
+
+ //if we need to update
+ if ( fcData.minFrame != fcData.maxFrame )
+ {
+ tFrameController* fc = GetFrameControllerByIndex( i );
+
+ //if the min frame is greater than the max frame then reverse the update
+ if ( fcData.minFrame > fcData.maxFrame )
+ {
+ fc->Advance( -dt , false );
+ }
+ else
+ {
+ fc->Advance( dt , false );
+ }
+
+ if ( fcData.isCyclic && fcData.numberOfCycles > 0 )
+ {
+ unsigned int currentNumberOfCycles = fc->LastFrameReached();
+ if ( currentNumberOfCycles >= fcData.numberOfCycles )
+ {
+ fc->SetCycleMode( FORCE_NON_CYCLIC );
+ fc->SetFrame( fcData.maxFrame );
+ }
+ }
+ }
+ }
+ }
+}
+
+void CStateProp::UpdateFrameControllersForRender()
+{
+ unsigned int numFrameControllers = GetNumberOfFrameControllers();
+ for ( unsigned int i = 0; i < numFrameControllers; i++ )
+ {
+ GetFrameControllerByIndex( i )->Advance( 0.f , true );
+ }
+}
+
+unsigned int CStateProp::GetState()
+{
+ return m_CurrentState;
+}
+
+void CStateProp::NextState()
+{
+ if ( m_CurrentState + 1 >= m_StatePropData->GetNumberOfStates() )
+ {
+ SetState( 0 );
+ }
+ else
+ {
+ SetState( m_CurrentState + 1 );
+ }
+}
+
+void CStateProp::PrevState()
+{
+ if ( m_CurrentState - 1 < 0 )
+ {
+ SetState( m_StatePropData->GetNumberOfStates() - 1 );
+ }
+ else
+ {
+ SetState( m_CurrentState - 1 );
+ }
+}
+
+void CStateProp::SetState( unsigned int state )
+{
+ if ( state < m_StatePropData->GetNumberOfStates() && state >= 0 )
+ {
+ m_CurrentState = state;
+ }
+
+ m_BaseFrameController->SetFrame(0.f);
+
+ unsigned int i;
+
+ //reset the FC for new state
+ unsigned int numFrameControllers = GetNumberOfFrameControllers();
+ int numActiveControllers = 0;
+ for ( i = 0; i < numFrameControllers; i++ )
+ {
+ FrameControllerData frameControllerData = m_StatePropData->GetFrameControllerData( m_CurrentState , i );
+ tFrameController* fc = GetFrameControllerByIndex(i);
+
+ if ( frameControllerData.minFrame != frameControllerData.maxFrame )
+ numActiveControllers++;
+
+
+ //if we are holding a frame over from last state dont reset
+ if ( !frameControllerData.holdFrame )
+ {
+ //Reset() MUST come first or it will trounce frame ranges etc...
+ fc->Reset();
+ }
+ fc->SetRelativeSpeed( frameControllerData.relativeSpeed );
+ fc->SetCycleMode( (frameControllerData.isCyclic == 1) ? FORCE_CYCLIC : FORCE_NON_CYCLIC );
+ if ( frameControllerData.minFrame > frameControllerData.maxFrame )
+ {
+ fc->SetFrameRange( frameControllerData.maxFrame , frameControllerData.minFrame );
+ }
+ else
+ {
+ fc->SetFrameRange( frameControllerData.minFrame , frameControllerData.maxFrame );
+ }
+
+ if ( !frameControllerData.holdFrame )
+ {
+ fc->SetFrame( frameControllerData.minFrame );
+ }
+
+
+ }
+
+ //Set visibility for new state
+ unsigned int numElements = m_AnimatedObject->GetBaseObject()->GetNumDrawableElement();
+ int numDrawableElementsVisible = 0;
+ int visibleElement = 0;
+ for ( i = 0; i < numElements; i++ )
+ {
+ tCompositeDrawable::DrawableElement* de = GetDrawableElement(i);
+
+ de->SetLockVisibility(false);
+ VisibilityData visibilityData = m_StatePropData->GetVisibilityData( m_CurrentState , i );
+ bool visible = visibilityData.isVisible == 1;
+ de->SetVisibility( visible );
+
+ //lock visibility if visiblility if false
+ de->SetLockVisibility(!visible);
+
+ if ( visible )
+ {
+ numDrawableElementsVisible++;
+ visibleElement = i;
+ }
+ }
+
+ //notify listeners of a state change
+ for ( i = 0; i < m_NumStatePropListeners; i++ )
+ {
+ m_StatePropListener[i]->RecieveEvent( STATEPROP_CHANGE_STATE_EVENT , this );
+ }
+
+ // Determine if this state is a special case fast drawable
+ if ( m_FastDisplayDrawable )
+ {
+ m_FastDisplayDrawable->Release();
+ m_FastDisplayDrawable = NULL;
+ }
+
+ if ( numDrawableElementsVisible == 1 && numActiveControllers == 0 )
+ {
+ m_FastDisplayDrawable = GetDrawableElement( visibleElement )->GetDrawable();
+ m_FastDisplayDrawable->AddRef();
+ }
+
+}
+
+bool CStateProp::OnEvent( unsigned int eventID )
+{
+ bool wasEventHandled = false;
+ unsigned int i;
+ unsigned int numEvents = m_StatePropData->GetNumberOfEvents( m_CurrentState );
+ for ( i = 0; i < numEvents; i++ )
+ {
+ EventData eventData = m_StatePropData->GetEventData( m_CurrentState , i );
+ if ( eventData.eventID == eventID )
+ {
+ SetState( eventData.toState );
+ wasEventHandled = true;
+ }
+ }
+ return wasEventHandled;
+}
+
+
+void CStateProp::AddStatePropListener( CStatePropListener* statePropListener )
+{
+ if ( m_NumStatePropListeners < MAX_LISTENERS )
+ {
+ m_StatePropListener[m_NumStatePropListeners] = statePropListener;
+ m_NumStatePropListeners++;
+ }
+}
+void CStateProp::RemoveStatePropListener( CStatePropListener* statePropListener )
+{
+ unsigned int i = 0;
+ //find this entry
+ for ( i = 0; i < m_NumStatePropListeners; i++ )
+ {
+ if ( m_StatePropListener[i] == statePropListener )
+ {
+ m_NumStatePropListeners--;
+ break;
+ }
+ }
+
+ //copy other entries back over
+ for ( i; i < m_NumStatePropListeners; i++ )
+ {
+ m_StatePropListener[i] = m_StatePropListener[i+1];
+ }
+}
+
+tDrawable* CStateProp::GetDrawable()
+{
+ return m_AnimatedObject->GetBaseObject();
+}
+
+unsigned int CStateProp::GetNumberOfFrameControllers()
+{
+ return m_AnimatedObject->GetCurrentAnimation()->GetNumFrameControllers();
+}
+
+tFrameController* CStateProp::GetFrameControllerByIndex( unsigned int i )
+{
+ return m_AnimatedObject->GetCurrentAnimation()->GetFrameControllerByIndex(i);
+}
+
+unsigned int CStateProp::GetNumberOfDrawableElements()
+{
+ return m_AnimatedObject->GetBaseObject()->GetNumDrawableElement();
+}
+
+tCompositeDrawable::DrawableElement* CStateProp::GetDrawableElement( unsigned int i )
+{
+ return m_AnimatedObject->GetBaseObject()->GetDrawableElement( i );
+}
+
+void
+CStateProp::Display( StatePropDSGProcAnimator* ProcAnimator )
+{
+ if ( m_FastDisplayDrawable != NULL )
+ {
+ m_FastDisplayDrawable->Display();
+ }
+ else
+ {
+ UpdateFrameControllersForRender();
+ if( ProcAnimator )
+ {
+ ProcAnimator->UpdateForRender( static_cast<tCompositeDrawable*>( GetDrawable() ) );
+ }
+ GetDrawable()->Display();
+ }
+}
+
diff --git a/game/code/stateprop/stateprop.hpp b/game/code/stateprop/stateprop.hpp
new file mode 100644
index 0000000..7e44a62
--- /dev/null
+++ b/game/code/stateprop/stateprop.hpp
@@ -0,0 +1,94 @@
+#ifndef _STATEPROP_HPP_
+#define _STATEPROP_HPP_
+
+#include <radmath/radmath.hpp>
+
+#include <p3d/anim/compositedrawable.hpp>
+
+//=============================================================================
+// Forward Class/Struct Declarations
+//=============================================================================
+
+class tFrameController;
+class tAnimatedObject;
+class CStatePropData;
+class CStateProp;
+class StatePropDSGProcAnimator;
+class tPose;
+
+//=============================================================================
+// Class Declarations
+// PropListener
+//=============================================================================
+
+class CStatePropListener
+{
+public:
+ virtual void RecieveEvent( int callback , CStateProp* stateProp ) = 0;
+};
+
+//=============================================================================
+// Definitions
+//=============================================================================
+#define MAX_LISTENERS 10
+
+const int STATEPROP_CHANGE_STATE_EVENT = -1;
+
+//=============================================================================
+// Class Declarations
+// CStateProp
+//=============================================================================
+class CStateProp : public tEntity
+{
+public:
+
+ //Static function to create a new CStateProp instance
+ static CStateProp* CreateStateProp( CStatePropData* statePropData , unsigned int state = 0, tPose* pose = NULL );
+
+ CStateProp( tAnimatedObject* animatedObject , CStatePropData* statePropData , unsigned int state = 0 );
+ ~CStateProp();
+
+ //Per frame update
+ void Update( float dt );
+
+ //call before render
+ void UpdateFrameControllersForRender();
+
+ unsigned int GetState();
+ void SetState( unsigned int state );
+ void NextState();
+ void PrevState();
+
+ // MikeR - modified OnEvent to return boolean indicating whether or not the
+ // event was successfully handled by the stateprop
+ bool OnEvent( unsigned int eventID );
+
+ void AddStatePropListener( CStatePropListener* statePropListener );
+ void RemoveStatePropListener( CStatePropListener* statePropListener );
+
+ tDrawable* GetDrawable();
+
+ void Display( StatePropDSGProcAnimator* ProcAnimator = 0 );
+ bool UsingFastDisplay()const { return m_FastDisplayDrawable != NULL; }
+ CStatePropData* GetStatePropData()const { return m_StatePropData; }
+
+private:
+
+ //accessor helpers
+ unsigned int GetNumberOfFrameControllers();
+ tFrameController* GetFrameControllerByIndex( unsigned int i );
+ unsigned int GetNumberOfDrawableElements();
+ tCompositeDrawable::DrawableElement* GetDrawableElement( unsigned int i );
+
+ //Private members
+ CStatePropData* m_StatePropData;
+ tAnimatedObject* m_AnimatedObject;
+ tFrameController* m_BaseFrameController;
+ tDrawable* m_FastDisplayDrawable;
+ unsigned int m_CurrentState;
+
+ unsigned int m_NumStatePropListeners;
+ CStatePropListener* m_StatePropListener[MAX_LISTENERS];
+};
+
+#endif //_STATEPROP_HPP_ \ No newline at end of file
diff --git a/game/code/stateprop/statepropdata.cpp b/game/code/stateprop/statepropdata.cpp
new file mode 100644
index 0000000..0a3a74d
--- /dev/null
+++ b/game/code/stateprop/statepropdata.cpp
@@ -0,0 +1,251 @@
+#include <string.h>
+#include <p3d/utility.hpp>
+#include <p3d/anim/animatedobject.hpp>
+#include <constants/chunkids.hpp>
+#include <p3d/chunkfile.hpp>
+
+#include "stateprop/statepropdata.hpp"
+
+using namespace StatePropDataTypes;
+
+//=============================================================================
+// Class Declarations
+// Prop
+//=============================================================================
+CStatePropData::CStatePropData( ) :
+ m_ObjectFactory( NULL ),
+ m_NumStates(0),
+ m_StateData(NULL)
+{
+}
+
+CStatePropData::~CStatePropData()
+{
+ if (m_ObjectFactory)
+ {
+ m_ObjectFactory->Release();
+ }
+
+ unsigned int i = 0;
+ for( i; i < m_NumStates; i++ )
+ {
+ delete m_StateData[i];
+ }
+ delete m_StateData;
+}
+
+//=============================================================================
+//State Data
+//=============================================================================
+
+//Get
+unsigned int CStatePropData::GetNumberOfStates()
+{
+ return m_NumStates;
+}
+StateData CStatePropData::GetStateData( unsigned int state )
+{
+ return ( * (m_StateData[ state ]) ) ;
+}
+
+//=============================================================================
+//Transition Data
+//=============================================================================
+
+//Get
+TransitionData CStatePropData::GetTransitionData( int state )
+{
+ return m_StateData[state]->transitionData;
+}
+
+//=============================================================================
+//Visibility Data
+//=============================================================================
+
+//Get
+VisibilityData CStatePropData::GetVisibilityData( int state , int index)
+{
+ return ( m_StateData[state]->visibilityData[index] );
+}
+
+//=============================================================================
+//Frame controller data
+//=============================================================================
+
+//Get
+FrameControllerData CStatePropData::GetFrameControllerData( int state, int fc )
+{
+ return m_StateData[state]->frameControllerData[fc];
+}
+
+//=============================================================================
+//Event data
+//=============================================================================
+
+//Get
+unsigned int CStatePropData::GetNumberOfEvents( int state )
+{
+ return m_StateData[state]->numEvents;
+}
+EventData CStatePropData::GetEventData( int state , int eventindex )
+{
+ return m_StateData[state]->eventData[eventindex];
+}
+
+//=============================================================================
+//Callback Data
+//=============================================================================
+
+//Get
+unsigned int CStatePropData::GetNumberOfCallbacks( int state )
+{
+ return m_StateData[state]->numCallbacks;
+}
+CallbackData CStatePropData::GetCallbackData( int state , int eventindex )
+{
+ return m_StateData[state]->callbackData[eventindex];
+}
+
+//=============================================================================
+// Class Declarations
+// PropLoader
+//=============================================================================
+CStatePropDataLoader::CStatePropDataLoader() :
+ tSimpleChunkHandler(StateProp::STATEPROP)
+{
+}
+
+//-------------------------------------------------------------------------
+tEntity* CStatePropDataLoader::LoadObject(tChunkFile* f, tEntityStore* store)
+{
+
+ CStatePropData* statePropData = new CStatePropData();
+
+ unsigned int version = f->GetLong();
+
+ //get the name
+ char buf[256];
+ f->GetPString(buf);
+ statePropData->SetName(buf);
+
+ //get the object factory
+ f->GetPString(buf);
+ statePropData->m_ObjectFactory = p3d::find<tAnimatedObjectFactory>(store, buf);
+ if ( statePropData->m_ObjectFactory )
+ {
+ statePropData->m_ObjectFactory->AddRef();
+ }
+
+ //get the num states
+ statePropData->m_NumStates = f->GetLong();
+
+
+ statePropData->m_StateData = new StateData*[statePropData->m_NumStates];
+ memset(statePropData->m_StateData,0,statePropData->m_NumStates*sizeof(StateData*));
+
+ unsigned int currState;
+ unsigned int i;
+ for ( currState = 0; currState < statePropData->m_NumStates; currState++ )
+ {
+ f->BeginChunk();
+
+ //get name
+ f->GetPString(buf);
+
+ bool autoTransition = ( f->GetLong() != 0 );
+ unsigned int toState = f->GetLong();
+ unsigned int numDrawables = f->GetLong();
+ unsigned int numFrameControllers = f->GetLong();
+ unsigned int numEvents = f->GetLong();
+ unsigned int numCallbacks = f->GetLong();
+ float onFrame = f->GetFloat();
+
+ //give me memory dammit
+ int memsize = ( sizeof(StateData) ) +
+ ( sizeof(VisibilityData) * numDrawables ) +
+ ( sizeof(FrameControllerData) * numFrameControllers ) +
+ ( sizeof(EventData) * numEvents ) +
+ ( sizeof(CallbackData) * numCallbacks );
+
+ unsigned char* memptr = new unsigned char[ memsize ];
+
+ memset( memptr,0, memsize );
+
+ statePropData->m_StateData[currState] = (StateData*)memptr;
+
+ statePropData->m_StateData[currState]->numEvents = numEvents;
+ statePropData->m_StateData[currState]->numCallbacks = numCallbacks;
+
+ //Transition data
+ statePropData->m_StateData[currState]->transitionData.autoTransition = autoTransition;
+ statePropData->m_StateData[currState]->transitionData.onFrame = onFrame;
+ statePropData->m_StateData[currState]->transitionData.toState = toState;
+
+ //Visibility data
+ statePropData->m_StateData[currState]->visibilityData = (VisibilityData*) ( memptr + sizeof(StateData) );
+ for ( i=0; i < numDrawables; i++ )
+ {
+ f->BeginChunk();
+ f->GetPString(buf);
+ VisibilityData *p = &(statePropData->m_StateData[currState]->visibilityData[i]);
+ p->isVisible = (f->GetLong() != 0);
+ f->EndChunk();
+ }
+
+ //FrameController data
+ statePropData->m_StateData[currState]->frameControllerData = (FrameControllerData*)
+ ( memptr + sizeof(StateData) +
+ ( sizeof(VisibilityData) * numDrawables ) );
+
+ for ( i=0; i < numFrameControllers; i++ )
+ {
+ f->BeginChunk();
+ f->GetPString(buf);
+ FrameControllerData* p = &(statePropData->m_StateData[currState]->frameControllerData[i]);
+ p->isCyclic = ( f->GetLong() != 0 );
+ p->numberOfCycles = f->GetLong();
+ p->holdFrame = ( f->GetLong() != 0 );
+ p->minFrame = f->GetFloat();
+ p->maxFrame = f->GetFloat();
+ p->relativeSpeed = f->GetFloat();
+ f->EndChunk();
+ }
+
+ //Event data
+ statePropData->m_StateData[currState]->eventData = (EventData*)
+ ( memptr + sizeof(StateData) +
+ ( sizeof(VisibilityData) * numDrawables ) +
+ ( sizeof(FrameControllerData) * numFrameControllers ) );
+
+ for ( i=0; i < numEvents; i++ )
+ {
+ f->BeginChunk();
+ EventData* p = &(statePropData->m_StateData[currState]->eventData[i]);
+ memcpy( p->eventName , f->GetPString(buf) , 64 );
+ p->toState = f->GetLong();
+ p->eventID = f->GetLong();
+ f->EndChunk();
+ }
+
+ //Callback data
+ statePropData->m_StateData[currState]->callbackData = (CallbackData*)
+ ( memptr + sizeof(StateData) +
+ ( sizeof(VisibilityData) * numDrawables ) +
+ ( sizeof(FrameControllerData) * numFrameControllers ) +
+ ( sizeof(EventData) * numEvents ) );
+
+ for ( i=0; i < numCallbacks; i++ )
+ {
+ f->BeginChunk();
+ CallbackData* p = &(statePropData->m_StateData[currState]->callbackData[i]);
+ memcpy( p->callbackName , f->GetPString(buf) , 64 );
+ p->callbackID = f->GetLong();
+ p->onFrame = f->GetFloat();
+ f->EndChunk();
+ }
+
+ f->EndChunk();
+ }
+
+ return statePropData;
+}
diff --git a/game/code/stateprop/statepropdata.hpp b/game/code/stateprop/statepropdata.hpp
new file mode 100644
index 0000000..fea2f01
--- /dev/null
+++ b/game/code/stateprop/statepropdata.hpp
@@ -0,0 +1,99 @@
+#ifndef _STATEPROPDATA_HPP_
+#define _STATEPROPDATA_HPP_
+
+
+#include <radmath/radmath.hpp>
+#include <p3d/loadmanager.hpp>
+
+#include "stateprop/statepropdatatypes.hpp"
+
+//=============================================================================
+// Forward Class/Struct Declarations
+//=============================================================================
+
+class CStateProp;
+class tAnimatedObjectFactory;
+class CStatePropDataLoader;
+
+//=============================================================================
+// Definitions
+//=============================================================================
+
+//State data
+struct StateData
+{
+ StatePropDataTypes::TransitionData transitionData;
+ StatePropDataTypes::VisibilityData* visibilityData;
+ StatePropDataTypes::FrameControllerData* frameControllerData;
+ StatePropDataTypes::EventData* eventData;
+ StatePropDataTypes::CallbackData* callbackData;
+
+ unsigned int numEvents;
+ unsigned int numCallbacks;
+};
+
+//=============================================================================
+// Class Declarations
+// Prop
+//=============================================================================
+
+class CStatePropData : public tEntity
+{
+
+public:
+
+ friend class CStatePropDataLoader;
+
+ CStatePropData();
+ ~CStatePropData();
+
+ // State Data =====================================================================================
+ unsigned int GetNumberOfStates();
+ StateData GetStateData( unsigned int state );
+
+ //Transition Data ==================================================================================
+ StatePropDataTypes::TransitionData GetTransitionData( int state );
+
+ //Visibility Data ==================================================================================
+ StatePropDataTypes::VisibilityData GetVisibilityData( int state , int index);
+
+ //Frame Controller Data =============================================================================
+ StatePropDataTypes::FrameControllerData GetFrameControllerData( int state, int fc );
+
+ //Event Data ========================================================================================
+ unsigned int GetNumberOfEvents( int state );
+ StatePropDataTypes::EventData GetEventData( int state , int eventindex );
+
+ //Callback Data ======================================================================================
+ unsigned int GetNumberOfCallbacks( int state );
+ StatePropDataTypes::CallbackData GetCallbackData( int state , int eventindex );
+
+ tAnimatedObjectFactory* GetAnimatedObjectFactory() { return m_ObjectFactory; }
+
+private:
+
+ //animated object factory
+ char m_FactoryName[64];
+ tAnimatedObjectFactory* m_ObjectFactory;
+
+ //total number of states
+ unsigned int m_NumStates;
+ StateData** m_StateData;
+};
+
+
+//=============================================================================
+// Class Declarations
+// PropLoader
+//=============================================================================
+class CStatePropDataLoader : public tSimpleChunkHandler
+{
+public:
+ CStatePropDataLoader();
+ tEntity* LoadObject(tChunkFile* file, tEntityStore* store);
+
+protected:
+ ~CStatePropDataLoader() {};
+};
+
+#endif //_STATEPROPDATA_HPP_
diff --git a/game/code/stateprop/statepropdatatypes.hpp b/game/code/stateprop/statepropdatatypes.hpp
new file mode 100644
index 0000000..5983a07
--- /dev/null
+++ b/game/code/stateprop/statepropdatatypes.hpp
@@ -0,0 +1,50 @@
+#ifndef _STATEPROPDATATYPES_HPP_
+#define _STATEPROPDATATYPES_HPP_
+
+namespace StatePropDataTypes
+{
+
+//Transition data
+struct TransitionData
+{
+ unsigned int autoTransition;
+ unsigned int toState;
+ float onFrame;
+};
+
+//Visibility data
+struct VisibilityData
+{
+ unsigned int isVisible;
+};
+
+//Frame controller data
+struct FrameControllerData
+{
+ unsigned int isCyclic;
+ unsigned int numberOfCycles;
+ unsigned int holdFrame;
+ float minFrame;
+ float maxFrame;
+ float relativeSpeed;
+};
+
+//Event data
+struct EventData
+{
+ char eventName[64];
+ unsigned int eventID;
+ unsigned int toState;
+};
+
+//Callback data
+struct CallbackData
+{
+ char callbackName[64];
+ unsigned int callbackID;
+ float onFrame;
+};
+
+};
+
+#endif //_STATEPROPDATATYPES_HPP_
diff --git a/game/code/supersprint/allsupersprint.cpp b/game/code/supersprint/allsupersprint.cpp
new file mode 100644
index 0000000..d4354d1
--- /dev/null
+++ b/game/code/supersprint/allsupersprint.cpp
@@ -0,0 +1,3 @@
+#include <supersprint/supersprintmanager.cpp>
+#include <supersprint/supersprintdata.cpp>
+
diff --git a/game/code/supersprint/supersprintdata.cpp b/game/code/supersprint/supersprintdata.cpp
new file mode 100644
index 0000000..f691409
--- /dev/null
+++ b/game/code/supersprint/supersprintdata.cpp
@@ -0,0 +1,94 @@
+#include <supersprint/supersprintdata.h>
+
+const char* SuperSprintData::CHARACTER_NAMES[] =
+{
+ "lisa",
+ "bart",
+ "homer",
+ "marge",
+ "apu"
+};
+const unsigned int SuperSprintData::NUM_CHARACTER_NAMES = sizeof( SuperSprintData::CHARACTER_NAMES ) / sizeof( SuperSprintData::CHARACTER_NAMES[0] );
+
+const SuperSprintData::DisplayNames SuperSprintData::VEHICLE_NAMES[] =
+{
+ //
+ // SYNTAX:
+ // { "<vehicle name>", "<display name>" },
+ //
+ { "snake_v", "" },
+ { "bookb_v", "" },
+ { "marge_v", "" },
+ { "carhom_v", "" },
+ { "krust_v", "" },
+ { "bbman_v", "" },
+ { "elect_v", "" },
+ { "famil_v", "" },
+ { "bart_v", "" },
+ { "scorp_v", "" },
+ { "honor_v", "" },
+ { "hbike_v", "" },
+ { "frink_v", "" },
+ { "comic_v", "" },
+ { "lisa_v", "" },
+ { "smith_v", "" },
+ { "mrplo_v", "" },
+ { "fone_v", "" },
+ { "cletu_v", "" },
+ { "apu_v", "" },
+ { "plowk_v", "" },
+ { "wiggu_v", "" },
+ { "otto_v", "" },
+ { "moe_v", "" },
+ { "skinn_v", "Sedan (Skinner - Lvl 3)" },
+ { "homer_v", "" },
+ { "zombi_v", "" },
+ { "burns_v", "" },
+ { "willi_v", "" },
+ { "gramp_v", "" },
+ { "gramR_v", "" },
+
+ { "atv_v", "" },
+ { "knigh_v", "" },
+ { "mono_v", "" },
+ { "oblit_v", "" },
+ { "hype_v", "" },
+ { "rocke_v", "" },
+
+ { "cArmor", "" },
+ { "cCellA", "" },
+ { "cSedan", "" },
+ { "cCola", "" },
+ { "cCube", "" },
+ { "cCurator", "" },
+ { "cDonut", "" },
+ { "cDuff", "" },
+ { "cBlbart", "" },
+ { "cHears", "" },
+ { "cKlimo", "" },
+ { "cLimo", "" },
+ { "cMilk", "" },
+ { "cNerd", "" },
+ { "cNonup", "" },
+ { "cPolice", "" },
+ { "cVan", "" },
+ { "cBone", "" },
+
+// { "huskA", "Charred Husk (Traffic)" },
+ { "compactA", "" },
+ { "minivanA", "" },
+ { "pickupA", "" },
+ { "sedanA", "" },
+ { "sedanB", "" },
+ { "sportsA", "" },
+ { "sportsB", "" },
+ { "wagonA", "" },
+ { "SUVA", "" },
+ { "taxiA", "" },
+ { "coffin", "" },
+// { "ship", "Ghost Ship (Halloween Traffic)" },
+ { "hallo", "" },
+// { "witchcar", "Witch Broom (Halloween Traffic)" },
+
+};
+const unsigned int SuperSprintData::NUM_NAMES = sizeof( SuperSprintData::VEHICLE_NAMES ) / sizeof( SuperSprintData::VEHICLE_NAMES[0] );
diff --git a/game/code/supersprint/supersprintdata.h b/game/code/supersprint/supersprintdata.h
new file mode 100644
index 0000000..dc7e25b
--- /dev/null
+++ b/game/code/supersprint/supersprintdata.h
@@ -0,0 +1,151 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supersprintdata.h
+//
+// Description: Blahblahblah
+//
+// History: 2/8/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef SUPERSPRINTDATA_H
+#define SUPERSPRINTDATA_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <constants/maxplayers.h>
+
+#include <p3d/p3dtypes.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+class Vehicle;
+class WaypointAI;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+namespace SuperSprintData
+{
+ enum
+ {
+#ifdef RAD_PS2
+ NUM_PLAYERS = 4,
+#else
+ NUM_PLAYERS = 4,
+#endif
+ DEFAULT_TURBO_NUM = 3,
+ DEFAULT_NUM_LAPS = 3,
+ MIN_NUM_LAPS = 1,
+ MAX_NUM_LAPS = 5,
+ FLAG_TIMEOUT = 4000,
+ MAX_CHARACTER_NAME_LEN = 32
+ };
+
+ struct CarData
+ {
+ enum State
+ {
+ WAITING,
+ SELECTING,
+ SELECTED
+ };
+
+ CarData() :
+ mVehicle( NULL ),
+ mVehicleAI( NULL ),
+ mState( WAITING ),
+ mActiveListIndex( -1 ),
+ mIsHuman(false)
+ {
+ mCarName[ 0 ] = '\0';
+ };
+
+ Vehicle* mVehicle;
+ WaypointAI* mVehicleAI;
+ State mState;
+ char mCarName[ 16 ];
+ int mActiveListIndex;
+ bool mIsHuman;
+ };
+
+ struct PlayerData
+ {
+ PlayerData() :
+ mLapTime( 0 ),
+ mBestLap( 0xffffffff ),
+ mRaceTime( 0 ),
+ mNumLaps( 0 ),
+ mPosition( 0 ),
+ mPoints( 0 ),
+ mWins( 0 ),
+ mNextCheckPoint( 0 ),
+ mBestTimeEntry( -1 ),
+ mBestLapEntry( -1 ),
+ mRacing( false ),
+ mCheated( false ),
+ mCharacterIndex( -1 ),
+ mDistToCheckpoint( 10000000.0f ) { mCharacterName[0] = '\0'; };
+
+ unsigned int mLapTime;
+ unsigned int mBestLap;
+ unsigned int mRaceTime;
+ unsigned char mNumLaps;
+ unsigned char mPosition;
+ unsigned char mPoints;
+ unsigned char mWins;
+ char mNextCheckPoint;
+ int mBestTimeEntry;
+ int mBestLapEntry;
+ bool mRacing;
+ bool mCheated;
+ char mCharacterName[MAX_CHARACTER_NAME_LEN];
+ int mCharacterIndex;
+ float mDistToCheckpoint;
+ };
+
+ struct DisplayNames
+ {
+ const char* name;
+ const char* text;
+ };
+
+ extern const DisplayNames VEHICLE_NAMES[];
+ extern const unsigned int NUM_NAMES;
+
+ extern const char* CHARACTER_NAMES[];
+ extern const unsigned int NUM_CHARACTER_NAMES;
+
+ const tColour PLAYER_COLOURS[] =
+ {
+ tColour( 213, 74, 33 ),
+ tColour( 36, 232, 255 ),
+ tColour( 246, 255, 5 ),
+ tColour( 35, 209, 14 ),
+
+ tColour( 0, 0, 0 ) // dummy terminator
+ };
+
+ struct HighScore
+ {
+ char name[4];
+ unsigned int carNum;
+ unsigned int score;
+ enum { NUM_HIGH_SCORE = 10 };
+ };
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+#endif //SUPERSPRINTDATA_H
diff --git a/game/code/supersprint/supersprintdrawable.h b/game/code/supersprint/supersprintdrawable.h
new file mode 100644
index 0000000..9eeca00
--- /dev/null
+++ b/game/code/supersprint/supersprintdrawable.h
@@ -0,0 +1,524 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supersprintdrawable.h
+//
+// Description: Blahblahblah
+//
+// History: 2/8/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef SUPERSPRINTDRAWABLE_H
+#define SUPERSPRINTDRAWABLE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+#include <p3d/debugdraw.hpp>
+#include <p3d/drawable.hpp>
+#include <p3d/sprite.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/texture.hpp>
+#include <p3d/file.hpp>
+#include <p3d/shader.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/memory.hpp>
+#include <p3d/bmp.hpp>
+#include <p3d/png.hpp>
+#include <p3d/targa.hpp>
+#include <p3d/font.hpp>
+#include <p3d/texturefont.hpp>
+#include <p3d/unicode.hpp>
+#include <radfile.hpp>
+
+#include <string.h>
+
+#include <supersprint/supersprintdata.h>
+
+#include <mission/gameplaymanager.h>
+
+#include <camera/supercammanager.h>
+
+#include <debug/debuginfo.h>
+
+//========================================
+// Forward References
+//========================================
+
+extern unsigned char gFont[];
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class SuperSprintDrawable : public tDrawable
+{
+public:
+ SuperSprintDrawable();
+ virtual ~SuperSprintDrawable();
+
+ void SetCarData( const SuperSprintData::CarData* carData );
+ void SetPlayerData( const SuperSprintData::PlayerData* playerData );
+ void SetCountDownMSG( const char* text );
+ void SetTextScale( float scale );
+ void DoCountDownToo( bool enable );
+
+ void Display();
+
+ enum RenderState
+ {
+ CAR_DATA,
+ PLAYER_DATA,
+ COUNT_DOWN,
+ HIGH_SCORES,
+ NONE
+ };
+
+ void SetRenderState( RenderState state );
+
+private:
+
+ const SuperSprintData::CarData* mCarData;
+ const SuperSprintData::PlayerData* mPlayerData;
+
+ RenderState mRenderState;
+
+ tTextureFont* mFont;
+
+ const char* mCountDownText;
+
+ float mTextScale;
+
+ bool mCountDownToo;
+
+ bool mILoadedThefont;
+
+ inline void DisplayCarData();
+ inline void DisplayPlayerData();
+ inline void DisplayCountDown();
+ inline void DisplayHighScores();
+
+ //Prevent wasteful constructor creation.
+ SuperSprintDrawable( const SuperSprintDrawable& supersprintdrawable );
+ SuperSprintDrawable& operator=( const SuperSprintDrawable& supersprintdrawable );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperSprintDrawable::SuperSprintDrawable
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: none
+//
+//=============================================================================
+inline SuperSprintDrawable::SuperSprintDrawable() :
+ mCarData( NULL ),
+ mPlayerData( NULL ),
+ mRenderState( CAR_DATA ),
+ mFont( NULL ),
+ mCountDownText( NULL ),
+ mTextScale( 1.0f ),
+ mCountDownToo( false ),
+ mILoadedThefont( false )
+{
+ p3d::inventory->PushSection();
+
+ p3d::inventory->SelectSection( "Default" );
+ mFont = p3d::find<tTextureFont>("adlibn_20");
+
+ if ( mFont == NULL )
+ {
+ tFileMem* file = new tFileMem( gFont , 61075 ); //HACK
+ file->AddRef();
+ file->SetFilename("memfile.p3d");
+ p3d::loadManager->GetP3DHandler()->Load( file, p3d::inventory );
+ file->Release();
+
+ mILoadedThefont = true;
+ mFont = p3d::find<tTextureFont>("adlibn_20");
+ }
+
+ rAssert( mFont );
+
+ mFont->AddRef();
+ tShader* fontShader = mFont->GetShader();
+ fontShader->SetInt(PDDI_SP_BLENDMODE,PDDI_BLEND_ALPHA);
+ fontShader->SetInt(PDDI_SP_FILTER,PDDI_FILTER_BILINEAR);
+
+ // Make the missing letter into somthing I can see
+ //
+ mFont->SetMissingLetter(p3d::ConvertCharToUnicode('j'));
+
+ p3d::inventory->PopSection();
+}
+
+//=============================================================================
+// ::~SuperSprintDrawable
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: none
+//
+//=============================================================================
+inline SuperSprintDrawable::~SuperSprintDrawable()
+{
+ if ( mILoadedThefont )
+ {
+#ifndef DEBUGINFO_ENABLED
+ p3d::inventory->Remove( mFont ); //Debuginfo uses this too.
+#endif
+ mILoadedThefont = false;
+ }
+ mFont->Release();
+ mFont = NULL;
+}
+
+//=============================================================================
+// SuperSprintDrawable::SetCarData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const SuperSprintData::CarData* carData )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperSprintDrawable::SetCarData( const SuperSprintData::CarData* carData )
+{
+ mCarData = carData;
+}
+
+//=============================================================================
+// SuperSprintDrawable::SetPlayerData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const SuperSprintData::PlayerData* playerData )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperSprintDrawable::SetPlayerData( const SuperSprintData::PlayerData* playerData )
+{
+ mPlayerData = playerData;
+}
+
+//=============================================================================
+// SuperSprintDrawable::SetCountDownMSG
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* text )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperSprintDrawable::SetCountDownMSG( const char* text )
+{
+ mCountDownText = text;
+}
+
+//=============================================================================
+// SuperSprintDrawable::SetTextScale
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float scale )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperSprintDrawable::SetTextScale( float scale )
+{
+ mTextScale = scale;
+}
+
+//=============================================================================
+// SuperSprintDrawable::DoCountDownToo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool enable )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperSprintDrawable::DoCountDownToo( bool enable )
+{
+ mCountDownToo = enable;
+}
+
+//=============================================================================
+// SuperSprintDrawable::DisplayCarData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperSprintDrawable::DisplayCarData()
+{
+ char displayText[1024];
+
+ const char* seperator;
+ unsigned int position;
+ if ( GetGameplayManager()->GetNumPlayers() == 2 )
+ {
+ seperator = "\n\n";
+ position = 2;
+ }
+ else
+ {
+ seperator = "\n";
+ position = 1;
+ }
+
+ sprintf( displayText, seperator);
+
+ int i;
+ for ( i = 0; i < GetGameplayManager()->GetNumPlayers(); ++ i )
+ {
+ char text[256];
+ if ( mCarData[i].mState == SuperSprintData::CarData::WAITING )
+ {
+ sprintf( text, "Player %d\n%s%s", i + 1, "PRESS START", seperator );
+ }
+ else if ( mCarData[i].mState == SuperSprintData::CarData::SELECTING )
+ {
+ sprintf( text, "Player %d\n%s%s", i + 1, mCarData[ i ].mCarName, seperator );
+ }
+ else if ( mCarData[i].mState == SuperSprintData::CarData::SELECTED )
+ {
+ sprintf( text, "Player %d\n%s%s", i + 1, "READY", seperator );
+ }
+
+ strncpy( &displayText[position], text, strlen( text ) );
+ position += strlen( text );
+ }
+
+ P3D_UNICODE unicodeText[1024];
+ p3d::AsciiToUnicode( displayText, unicodeText, position + 1 );
+
+ mFont->DisplayText( unicodeText, 3 );
+}
+
+//=============================================================================
+// SuperSprintDrawable::DisplayCountDown
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperSprintDrawable::DisplayCountDown()
+{
+ P3D_UNICODE unicodeText[256];
+
+ p3d::AsciiToUnicode( mCountDownText, unicodeText, 256 );
+
+ mFont->DisplayText( unicodeText, 3 );
+}
+
+//=============================================================================
+// SuperSprintDrawable::DisplayPlayerData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperSprintDrawable::DisplayPlayerData()
+{
+ char displayText[1024];
+ const char* seperator = "\n";
+ sprintf( displayText, seperator);
+
+ unsigned int position = 1;
+
+ int i;
+ for ( i = 0; i < GetGameplayManager()->GetNumPlayers(); ++ i )
+ {
+ char text[256];
+
+ if( !mPlayerData[ i ].mRacing )
+ {
+ sprintf( text, "Player %d%s", i + 1, seperator );
+ }
+ else
+ {
+ if ( mPlayerData[ i ].mPosition == 0 )
+ {
+ sprintf( text, "Player %d %s%sDNF%sLOSER!%s", i + 1, "PRESS START", seperator, seperator, seperator );
+ }
+ else
+ {
+ sprintf( text, "Player %d %s%sRace Time: %.2f%sBest Lap: %.2f%s", i + 1, "PRESS START", seperator, rmt::LtoF(mPlayerData[ i ].mRaceTime) / 1000000.0f, seperator, rmt::LtoF(mPlayerData[ i ].mBestLap) / 1000000.0f, seperator );
+ }
+ }
+
+ strncpy( &displayText[position], text, strlen( text ) );
+ position += strlen( text );
+ }
+
+ P3D_UNICODE unicodeText[1024];
+ p3d::AsciiToUnicode( displayText, unicodeText, position + 1 );
+
+ mFont->DisplayText( unicodeText, 3 );
+}
+
+//=============================================================================
+// SuperSprintDrawable::DisplayHighScores
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+/*
+inline void SuperSprintDrawable::DisplayHighScores()
+{
+ char displayText[1024];
+ displayText[0] = '\0';
+
+ const char* seperator = "\n";
+
+ sprintf( displayText, seperator);
+
+ unsigned int position = 1;
+
+ char text[256];
+
+ sprintf( text, "BEST TIME BEST LAP%s=================++==========%s", seperator, seperator );
+ strncpy( &displayText[position], text, strlen( text ) );
+ position += strlen( text );
+
+ int j;
+ for ( j = 0; j < SuperSprintData::HighScore::NUM_HIGH_SCORE; ++j )
+ {
+ sprintf( text, "%8s %5s %.2f %8s %5s %.2f%s", SuperSprintData::BEST_TIME[j].name,
+ SuperSprintData::VEHICLE_NAMES[SuperSprintData::BEST_TIME[j].carNum].name,
+ SuperSprintData::BEST_TIME[j].score / 1000000.0f,
+ SuperSprintData::BEST_LAP[j].name,
+ SuperSprintData::VEHICLE_NAMES[SuperSprintData::BEST_LAP[j].carNum].name,
+ SuperSprintData::BEST_LAP[j].score / 1000000.0f,
+ seperator );
+
+ strncpy( &displayText[position], text, strlen( text ) );
+ position += strlen( text );
+ }
+
+ P3D_UNICODE unicodeText[1024];
+ p3d::AsciiToUnicode( displayText, unicodeText, position + 1 );
+
+ mFont->DisplayText( unicodeText, 3 );
+}
+*/
+
+//=============================================================================
+// SuperSprintDrawable::Display
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperSprintDrawable::Display()
+{
+ tColour colour( 240, 240, 0 );
+ colour.SetAlpha( 255 );
+
+ mFont->SetColour( colour );
+
+ p3d::pddi->PushState( PDDI_STATE_VIEW );
+ p3d::pddi->SetProjectionMode( PDDI_PROJECTION_ORTHOGRAPHIC );
+
+ p3d::stack->Push();
+ p3d::stack->LoadIdentity();
+
+ float textPosX, textPosY;
+ textPosX = textPosY = 0.0f;
+
+ tPointCamera* pPtCam = (tPointCamera*)GetSuperCamManager()->GetSCC(0)->GetCamera();
+
+ p3d::stack->Translate( textPosX, textPosY, pPtCam->GetNearPlane()+0.5f);
+ float scaleSize = 1.0f / 480.0f; //This is likely good for 528 also.
+
+ float fontScale = 0.5f * mTextScale;
+ p3d::stack->Scale(scaleSize * fontScale, scaleSize * fontScale , 1.0f);
+
+ switch ( mRenderState )
+ {
+ case CAR_DATA:
+ {
+ DisplayCarData();
+
+ if ( mCountDownToo )
+ {
+ DisplayCountDown();
+ }
+ break;
+ }
+ case PLAYER_DATA:
+ {
+// DisplayPlayerData();
+ break;
+ }
+ case COUNT_DOWN:
+ {
+ DisplayCountDown();
+ break;
+ }
+ case HIGH_SCORES:
+ {
+// DisplayHighScores();
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ p3d::stack->Pop();
+ p3d::pddi->PopState( PDDI_STATE_VIEW );
+}
+
+//=============================================================================
+// SuperSprintDrawable::SetRenderState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( RenderState state )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperSprintDrawable::SetRenderState( RenderState state )
+{
+ mRenderState = state;
+}
+#endif //SUPERSPRINTDRAWABLE_H
diff --git a/game/code/supersprint/supersprintmanager.cpp b/game/code/supersprint/supersprintmanager.cpp
new file mode 100644
index 0000000..d9c75b8
--- /dev/null
+++ b/game/code/supersprint/supersprintmanager.cpp
@@ -0,0 +1,2668 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supersprintmanager.cpp
+//
+// Description: Implement SuperSprintManager
+//
+// History: 2/3/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <radtime.hpp>
+#include <p3d/utility.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <memory/srrmemory.h>
+#include <loading/loadingmanager.h>
+#include <gameflow/gameflow.h>
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/animatedcam.h>
+#include <mission/missionscriptloader.h>
+#include <render/loaders/billboardwrappedloader.h>
+#include <render/dsg/animcollisionentitydsg.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <meta/carstartlocator.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/renderlayer.h>
+#include <input/inputmanager.h>
+#include <events/eventmanager.h>
+#include <meta/eventlocator.h>
+#include <contexts/supersprint/supersprintcontext.h>
+#include <gameflow/gameflow.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guimanager.h>
+#include <sound/soundmanager.h>
+
+#include <roads/roadmanager.h>
+#include <roads/roadsegment.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+#include <render/intersectmanager/intersectmanager.h>
+
+#include <supersprint/supersprintmanager.h>
+
+#include <mission/gameplaymanager.h>
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+const float MIN_SCALE = 0.01f;
+const float MAX_SCALE = 1.0f;
+const float MIN_DIST = 10.0f;
+const float MAX_DIST = 80.0f;
+
+SuperSprintManager* SuperSprintManager::spInstance = NULL;
+
+const int DNF_TIMEOUT_TIME = 10000;
+
+#define GET_TIME ::radTimeGetMicroseconds()
+
+static char CHEKPOINT_LIST_B01 [] =
+{
+ 2, 3, 4, 5, 6, 7, 8, 1
+};
+
+static char CHEKPOINT_LIST_B01_R [] =
+{
+ 8, 7, 6, 5, 4, 3, 2, 1
+};
+
+static char CHECKPOINT_LIST_B02 [] =
+{
+ 2, 3, 4, 5, 6, 3, 8, 9, 1
+};
+
+static char CHECKPOINT_LIST_B02_R [] =
+{
+ 9, 8, 3, 6, 5, 4, 3, 2, 1
+};
+
+static char CHECKPOINT_LIST_B03 [] =
+{
+ 2, 3, 4, 5, 6, 7, 1
+};
+
+static char CHECKPOINT_LIST_B03_R [] =
+{
+ 7, 6, 5, 4, 3, 2, 1
+};
+
+static char CHECKPOINT_LIST_B05 [] =
+{
+ 2, 3, 4, 5, 6, 7, 8, 9, 10, 7, 3, 11, 1
+};
+
+static char CHECKPOINT_LIST_B05_R [] =
+{
+ 11, 3, 7, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1
+};
+
+static char CHECKPOINT_LIST [] =
+{
+ 2, 3, 1
+};
+
+static char CHECKPOINT_LIST_R [] =
+{
+ 3, 2, 1
+};
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//From EventListener
+//=============================================================================
+// SuperSprintManager::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::HandleEvent( EventEnum id, void* pEventData )
+{
+#if defined( RAD_DEBUG ) || defined( RAD_TUNE )
+ char errMsg[128];
+#endif
+
+ if ( id == EVENT_LOCATOR + LocatorEvent::CHECK_POINT )
+ {
+ EventLocator* loc = static_cast<EventLocator*>(pEventData);
+
+ char checkNum = static_cast<char>( loc->GetData() );
+
+ if ( loc->GetPlayerEntered() && checkNum != 0 ) //Ignore the 0 ones.
+ {
+ //The idea here is to detect if the player is attempting to do the
+ //checkpoints out of order. If the checkpoint passed is not the one we want
+ //we test to see if the player has already hit a bad one and if not we decrement
+ //where they are going (send them back) and mark them as cheaters.
+ //Then, only when they pass the one they're supposed to do they get unmarked
+ //as cheaters and get to move on to the next check point. The first checkpoint is a
+ //bit tricky as the players start inside the last checkpoint and would thus be
+ //considered cheaters. We can ignore cheaters cheating on checkpoint 2.
+ //Indexes start at 1
+
+ int playerID = loc->GetPlayerID();
+
+#if defined( RAD_DEBUG ) || defined( RAD_TUNE )
+ sprintf( errMsg, "Something wrong with player %d", playerID+1 );
+ rTuneAssertMsg( 0 <= playerID && playerID < 4, errMsg );
+ rTuneAssertMsg( 0 <= mPlayers[ playerID ].mNextCheckPoint &&
+ mPlayers[ playerID ].mNextCheckPoint < GetNumCheckpoints(), errMsg );
+#endif
+
+ // find the one we're supposed to be at
+ const char nextCheck = mCheckPoints[ mPlayers[ playerID ].mNextCheckPoint ];
+
+ // find the one we were just at
+ char lastCheckIdx = mPlayers[ playerID ].mNextCheckPoint;
+ if ( lastCheckIdx == 0 )
+ {
+ lastCheckIdx = GetNumCheckpoints() - 1;
+ }
+ else
+ {
+ lastCheckIdx--;
+ }
+ rTuneAssert( 0 <= lastCheckIdx && lastCheckIdx < GetNumCheckpoints() );
+ const char prevCheck = mCheckPoints[ lastCheckIdx ];
+
+ if ( checkNum != nextCheck )
+ {
+ //This guy is possibly cheating
+ if ( !mPlayers[ playerID ].mCheated &&
+ checkNum != prevCheck )
+ {
+ //CHEATER!
+
+
+ // we want to knock him back to aim for the previous check
+ mPlayers[ playerID ].mNextCheckPoint = lastCheckIdx;
+ /*
+ if ( nextCheck == 1 )
+ {
+ mPlayers[ playerID ].mNextCheckPoint = GetNumCheckpoints() - 1;
+ }
+ else
+ {
+ mPlayers[ playerID ].mNextCheckPoint = nextCheck - 1;
+ }
+ */
+
+#if defined( RAD_DEBUG ) || defined( RAD_TUNE )
+ rTuneAssertMsg( 0 <= mPlayers[ playerID ].mNextCheckPoint &&
+ mPlayers[ playerID ].mNextCheckPoint < GetNumCheckpoints(), errMsg );
+#endif
+
+ mPlayers[ playerID ].mCheated = true;
+ }
+ }
+ else
+ {
+ //Unmark the cheater
+ bool previouslyCheated = mPlayers[ playerID ].mCheated;
+ mPlayers[ playerID ].mCheated = false;
+
+ if( nextCheck == 1 && !previouslyCheated ) //This is the finish line.
+ {
+ //This guy has completed a lap
+ mPlayers[ playerID ].mNumLaps++;
+
+ //Figure out his lap time.
+ unsigned int endTime = GET_TIME;
+ unsigned int time = endTime - mStartTime;
+
+ unsigned int lapTime = time - mPlayers[ playerID ].mRaceTime;
+
+ //Possibly set the best lap time
+ if ( lapTime < mPlayers[ playerID ].mBestLap )
+ {
+ mPlayers[ playerID ].mBestLap = lapTime;
+ }
+
+ //Set the lapTime
+ mPlayers[ playerID ].mLapTime = lapTime;
+
+ //Set the raceTime
+ mPlayers[ playerID ].mRaceTime += lapTime;
+
+ if ( static_cast<int>( mPlayers[ playerID ].mNumLaps ) == mNumLaps - 1 )
+ {
+ //Show the white flag for a few seconds.
+ mWFlag->ShouldRender( true );
+ mWFlagTimeout = SuperSprintData::FLAG_TIMEOUT;
+ }
+ else if ( static_cast<int>( mPlayers[ playerID ].mNumLaps ) == mNumLaps )
+ {
+ //Show the finished flag for a few seconds.
+ mFFlag->ShouldRender( true );
+ mFFlagTimeout = SuperSprintData::FLAG_TIMEOUT;
+
+ //This guy is finished.
+ if( !mVehicleSlots[ playerID ].mIsHuman )
+ {
+ mVehicleSlots[ playerID ].mVehicleAI->SetActive( false );
+ }
+ else
+ {
+ //Disable his controller
+ mNumHumansFinished++;
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( playerID );
+ if( controllerID != -1 )
+ {
+ GetInputManager()->GetController( controllerID )->SetGameState( Input::ACTIVE_SS_GAME );
+ }
+ }
+
+ mPlayers[ playerID ].mPosition = mCurrentPosition;
+
+ //Only one guy left, or all humans done, go to DNF
+ if ( mCurrentState != DNF_TIMEOUT &&
+ ( mCurrentPosition == mNumActivePlayers - 1 ||
+ mNumHumansFinished == mNumHumanPlayers ) )
+ {
+ mCountDownTime = DNF_TIMEOUT_TIME;
+ mDrawable->SetRenderState( SuperSprintDrawable::COUNT_DOWN );
+ mDrawable->SetTextScale( 2.0f );
+ sprintf( mTime, "%d", mCountDownTime / 1000 );
+ mDrawable->SetCountDownMSG( mTime );
+
+ SetState( DNF_TIMEOUT );
+ }
+ else if ( mCurrentPosition == mNumActivePlayers )
+ {
+ //We're done!
+ mCountDownTime = 0;
+ }
+
+ mCurrentPosition++;
+ }
+ }
+
+ mPlayers[ playerID ].mNextCheckPoint++;
+ if ( mPlayers[ playerID ].mNextCheckPoint == GetNumCheckpoints() )
+ {
+ mPlayers[ playerID ].mNextCheckPoint = 0; //Reset
+ }
+
+#if defined( RAD_DEBUG ) || defined( RAD_TUNE )
+ rTuneAssertMsg( 0 <= mPlayers[ playerID ].mNextCheckPoint &&
+ mPlayers[ playerID ].mNextCheckPoint < GetNumCheckpoints(), errMsg );
+#endif
+ }
+ }
+ }
+ else if ( id == EVENT_LOCATOR + LocatorEvent::TRAP )
+ {
+ EventLocator* loc = static_cast<EventLocator*>(pEventData);
+
+ if ( loc->GetPlayerEntered() )
+ {
+ unsigned int locID = loc->GetData();
+ if ( !mTrapTriggered &&
+ mPositions[ loc->GetPlayerID() ] == 1 &&
+ ((!mGoLeft && locID == 0) || (mGoLeft && locID == 1)) )
+ {
+ //This toggles on
+ mTrapController->SetAnimationDirection( 1.0f );
+ mTrapTriggered = true;
+ }
+ else if ( mTrapTriggered &&
+ mPositions[ loc->GetPlayerID() ] == 1 &&
+ ((!mGoLeft && locID == 1) || (mGoLeft && locID == 0)) )
+ {
+ //This toggles off
+ mTrapController->SetAnimationDirection( -1.0f );
+ mTrapTriggered = false;
+ }
+ }
+ }
+ else if ( id == EVENT_ANIMATED_CAM_SHUTDOWN )
+ {
+ DisableAllControllers();
+ }
+
+ GameplayManager::HandleEvent( id, pEventData );
+}
+
+
+//From LoadingManager::ProcessRequestsCallback
+//=============================================================================
+// SuperSprintManager::OnProcessRequestsComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::OnProcessRequestsComplete( void* pUserData )
+{
+ PositionCharacters();
+
+ SetupIcons();
+
+ GetGameFlow()->SetContext( CONTEXT_SUPERSPRINT );
+}
+
+//Local
+//=============================================================================
+// SuperSprintManager::GetInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: SuperSprintManager
+//
+//=============================================================================
+SuperSprintManager* SuperSprintManager::GetInstance()
+{
+ if ( spInstance == NULL )
+ {
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ spInstance = new SuperSprintManager();
+ spInstance->AddRef();
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ }
+
+ return spInstance;
+}
+
+//=============================================================================
+// SuperSprintManager::DestroyInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::DestroyInstance()
+{
+ if ( spInstance )
+ {
+ spInstance->Release();
+ }
+
+ spInstance = NULL;
+}
+
+//=============================================================================
+// SuperSprintManager::Initialize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::Initialize()
+{
+ GameplayManager::Initialize();
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ //Add the drawable to the views
+ mDrawable = new SuperSprintDrawable();
+ rAssert( mDrawable );
+
+ mDrawable->AddRef();
+
+ RenderManager* rm = GetRenderManager();
+ RenderLayer* rloutside = rm->mpLayer( RenderEnums::LevelSlot );
+ rAssert( rloutside );
+
+ rloutside->AddGuts( mDrawable );
+
+ mDrawable->SetCarData( mVehicleSlots );
+ mDrawable->SetPlayerData( mPlayers );
+
+ int i;
+ for( i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( i );
+
+ mVehicleSlots[ i ].mState = (controllerID != -1) ?
+ SuperSprintData::CarData::SELECTED :
+ SuperSprintData::CarData::WAITING;
+
+ mVehicleSlots[ i ].mIsHuman = (controllerID != -1);
+ mPlayers[ i ].mRacing = (controllerID != -1);
+ }
+
+ EnumerateControllers();
+
+ mNumCheckPoints = 0;
+
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_LOCATOR + LocatorEvent::CHECK_POINT) );
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_LOCATOR + LocatorEvent::TRAP) );
+ GetEventManager()->AddListener( this, EVENT_ANIMATED_CAM_SHUTDOWN );
+
+ mFFlag = new AnimatedIcon();
+ mWFlag = new AnimatedIcon();
+
+ int j;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ for ( j = 0; j < SuperSprintData::NUM_PLAYERS; ++j )
+ {
+ mPositionIcon[ i ][ j ] = new AnimatedIcon();
+ }
+
+ mPlayerID[ i ] = new AnimatedIcon();
+ mNitroEffect[ i ] = new AnimatedIcon();
+ }
+
+ for ( i = 0; i < MAX_AI_WAYPOINTS; ++i )
+ {
+ mPathData[ i ].closestElem.elem = NULL;
+ mPathData[ i ].roadT = 0.0f;
+ mPathData[ i ].seg = NULL;
+ mPathData[ i ].segT = 0.0f;
+ mPathData[ i ].loc = NULL;
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+}
+
+//=============================================================================
+// SuperSprintManager::Finalize
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::Finalize()
+{
+ //Remove the drawable from the views
+ RenderManager* rm = GetRenderManager();
+ RenderLayer* rloutside = rm->mpLayer( RenderEnums::LevelSlot );
+ rAssert( rloutside );
+
+ rloutside->RemoveGuts( mDrawable );
+
+ //Shut down the controllers.
+ InputManager* im = GetInputManager();
+
+ unsigned int i;
+ for ( i = 0; i < Input::MaxControllers; ++i )
+ {
+ im->UnregisterMappable( i, this );
+ }
+
+ GetEventManager()->RemoveAll( this );
+
+ mDrawable->Release();
+ mDrawable = NULL;
+
+ CleanUpCars();
+
+ delete mFFlag;
+ mFFlag = NULL;
+
+ delete mWFlag;
+ mWFlag = NULL;
+
+ int j;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ for ( j = 0; j < SuperSprintData::NUM_PLAYERS; ++j )
+ {
+ delete mPositionIcon[ i ][ j ];
+ mPositionIcon[ i ][ j ] = NULL;
+ }
+
+ delete mPlayerID[ i ];
+ delete mNitroEffect[ i ];
+ }
+
+ if ( mTrapController )
+ {
+ mTrapController->Release();
+ mTrapController = NULL;
+ }
+
+ for ( i = 0; i < mNumCheckPointLocators; ++i )
+ {
+ //mCheckPointLocators[ i ]->Release();
+ mCheckPointLocators[ i ] = NULL;
+ }
+
+ mNumCheckPointLocators = 0;
+
+ GameplayManager::Finalize();
+}
+
+//=============================================================================
+// SuperSprintManager::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::Update( unsigned int milliseconds )
+{
+ int oldTimeSecs;
+ int newTimeSecs;
+
+ //Test the flags
+ if ( mFFlagTimeout > 0 )
+ {
+ if ( mFFlagTimeout <= milliseconds )
+ {
+ mFFlag->ShouldRender( false );
+ mFFlagTimeout = 0;
+ }
+ else
+ {
+ mFFlagTimeout -= milliseconds;
+ mFFlag->Update( milliseconds );
+ }
+ }
+
+ if ( mWFlagTimeout > 0 )
+ {
+ if ( mWFlagTimeout <= milliseconds )
+ {
+ mWFlag->ShouldRender( false );
+ mWFlagTimeout = 0;
+ }
+ else
+ {
+ mWFlagTimeout -= milliseconds;
+ mWFlag->Update( milliseconds );
+ }
+ }
+
+ UpdatePositionIcons( milliseconds );
+
+ switch ( mCurrentState )
+ {
+ case COUNT_DOWN:
+ {
+ // 3 - 2 - 1 - GO!
+
+ oldTimeSecs = mCountDownTime / 1000;
+
+ if ( mCountDownTime - static_cast<int>(milliseconds) <= 0 )
+ {
+ mCountDownTime = 0;
+ }
+ else
+ {
+ mCountDownTime -= milliseconds;
+ }
+
+ if ( mCountDownTime > 2000 )
+ {
+ mDrawable->SetCountDownMSG( "3" );
+ }
+ else if ( mCountDownTime > 1000 )
+ {
+ mDrawable->SetCountDownMSG( "2" );
+ mDrawable->SetTextScale( 1.5f );
+ }
+ else if ( mCountDownTime > 0 )
+ {
+ mDrawable->SetCountDownMSG( "1" );
+ mDrawable->SetTextScale( 2.0f );
+ }
+ else
+ {
+ mDrawable->SetCountDownMSG( "GO!" );
+ mDrawable->SetTextScale( 2.5f );
+
+ //Leave the go up for a little while.
+ mCountDownTime = 500;
+ mCurrentPosition = 1;
+ mNumHumansFinished = 0;
+
+ // Temp structures for turbo-on-start hack
+ int aiIndices[ 3 ];
+ int numAIs = 0;
+
+ int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( i );
+ if( controllerID != -1 )
+ {
+ GetInputManager()->GetController( controllerID )->SetGameState( Input::ACTIVE_ALL );
+ GetInputManager()->GetController( controllerID )->SetRumble( GetInputManager()->IsRumbleEnabled() );
+ }
+ else
+ {
+ if( mVehicleSlots[ i ].mVehicleAI != NULL )
+ {
+ mVehicleSlots[ i ].mVehicleAI->SetActive( true );
+
+ // Remember this AI index for turbo-on-start hack just below..
+ aiIndices[ numAIs ] = i;
+ numAIs++;
+ }
+ }
+ }
+
+ /////////////////////////
+ // HACK:
+ // For each AI, it has a "m-in-n" chance to turbo at the start.
+ // This helps to avoid cluttering at the start, as well as adds
+ // life to the game.
+ //
+ const int MAX_AIS_DOING_TURBO = numAIs - 1;
+ int numAIsDoingTurbo = 0;
+
+ for( int j=0; j<numAIs; j++ )
+ {
+ if( numAIsDoingTurbo < MAX_AIS_DOING_TURBO )
+ {
+ int coinflip = rand() % 7;
+ if( coinflip == 0 || coinflip == 1 || coinflip == 2 )
+ {
+ rAssert( 0 <= aiIndices[j] && aiIndices[j] < SuperSprintData::NUM_PLAYERS );
+ mVehicleSlots[ aiIndices[j] ].mVehicleAI->UseTurbo();
+ numAIsDoingTurbo++;
+ }
+ }
+ else
+ {
+ break;
+ }
+ }
+ // If more than one AI, at least ONE AI will need to do turbo
+ // so there's no cluttering near the start...
+ if( numAIs > 1 && numAIsDoingTurbo == 0 )
+ {
+ int randAI = rand() % numAIs;
+ rAssert( 0 <= aiIndices[randAI] && aiIndices[randAI] < SuperSprintData::NUM_PLAYERS );
+ mVehicleSlots[ aiIndices[randAI] ].mVehicleAI->UseTurbo();
+ }
+ //////////////////////////////
+
+
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_SELECT );
+
+ //record the starting time
+ mStartTime = GET_TIME;
+ SetState( RACING );
+ }
+
+ //
+ // Reuse the menu select sound for a countdown sound
+ //
+ newTimeSecs = mCountDownTime / 1000;
+ if( oldTimeSecs != newTimeSecs )
+ {
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_SELECT );
+ }
+
+ break;
+ }
+ case RACING:
+ {
+ //Watch to see who crosses the finish line first!
+ if ( mCountDownTime - static_cast<int>(milliseconds) <= 0 )
+ {
+ mDrawable->SetRenderState( SuperSprintDrawable::NONE );
+ mDrawable->SetTextScale( 1.0f );
+ mCountDownTime = 0;
+ }
+ else
+ {
+ mCountDownTime -= milliseconds;
+ }
+
+ break;
+ }
+ case DNF_TIMEOUT:
+ {
+ oldTimeSecs = mCountDownTime / 1000;
+ if ( mCountDownTime - static_cast<int>(milliseconds) <= 0 )
+ {
+ //mDrawable->SetRenderState( SuperSprintDrawable::PLAYER_DATA );
+ //mDrawable->SetTextScale( 1.0f );
+
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_SELECT );
+
+ SetState( WINNER_CIRCLE );
+ mCountDownTime = 0;
+
+ mDrawable->SetRenderState( SuperSprintDrawable::NONE );
+
+ DisableAllAI();
+ DisableAllControllers();
+ }
+ else
+ {
+ mCountDownTime -= milliseconds;
+ }
+
+ sprintf( mTime, "%d", mCountDownTime / 1000 );
+
+ //
+ // Reuse the menu select sound for a countdown sound
+ //
+ newTimeSecs = mCountDownTime / 1000;
+ if( oldTimeSecs != newTimeSecs )
+ {
+ GetEventManager()->TriggerEvent( EVENT_FE_MENU_SELECT );
+ }
+
+ break;
+ }
+ case WINNER_CIRCLE:
+ {
+ //Display the winner!
+ GetInputManager()->SetGameState( Input::ACTIVE_SS_GAME );
+
+ // go to Race Summary screen
+ //
+ GetGameFlow()->GetContext( CONTEXT_SUPERSPRINT )->Suspend();
+
+ GetGuiSystem()->GotoScreen( CGuiWindow::GUI_SCREEN_ID_MINI_SUMMARY,
+ 0, 0, CLEAR_WINDOW_HISTORY );
+
+ //Calculate your score
+ int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ if ( mPositions[ i ] == 1 )
+ {
+ mPlayers[ i ].mWins++;
+
+ //
+ // We've found a winner. Send the win/lose sound events
+ //
+ if( mVehicleSlots[i].mIsHuman )
+ {
+ GetEventManager()->TriggerEvent( EVENT_SUPERSPRINT_WIN );
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_SUPERSPRINT_LOSE );
+ }
+ }
+
+ if ( mPlayers[ i ].mPosition == 0 )
+ {
+ mPlayers[ i ].mPoints += 0; //Joel made me set it to 0
+ }
+ else
+ {
+ mPlayers[ i ].mPoints += 5 - mPositions[ i ];
+ }
+ }
+
+ SetState( IDLE );
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::LoadScriptData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::LoadScriptData()
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER ); // I choose other because this stuff will exist longer than a single mission
+
+ char name[64];
+ sprintf( name, "scripts\\ssi.mfk" );
+
+ GetMissionScriptLoader()->SetFileHander( FILEHANDLER_LEVEL );
+ GetMissionScriptLoader()->LoadScriptAsync( name );
+
+ //Also load the carstarts and stuff.
+ sprintf( name, "art\\B0%ddata.p3d", GetCurrentLevelIndex() ); //Hackish
+ GetLoadingManager()->AddRequest( FILEHANDLER_LEVEL, name, GMA_LEVEL_OTHER );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+
+ //MOve this here so the car con files get loaded in time.
+ SetUpCars();
+
+ GetLoadingManager()->AddCallback( this );
+}
+
+//=============================================================================
+// SuperSprintManager::StartRace
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::StartRace()
+{
+ PositionCars();
+ PositionAI();
+ InitRaceData();
+ PlaceCharactersInCars(); //TODO: texas scramble
+
+ mCountDownTime = 5000;
+ mDrawable->SetRenderState( SuperSprintDrawable::COUNT_DOWN );
+ mDrawable->SetTextScale( 1.0f );
+
+ InitCamera();
+ SetupTraps(); //Only do this once.
+ PlayIntroCam();
+
+ SetState( COUNT_DOWN );
+}
+
+//=============================================================================
+// SuperSprintManager::SetCharacter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID, const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::SetCharacter( int playerID, int index )
+{
+ mPlayers[ playerID ].mCharacterIndex = index;
+}
+
+//=============================================================================
+// SuperSprintManager::SetVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int playerID, const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::SetVehicle( int playerID, const char* name )
+{
+ rAssert( playerID >= 0 && playerID < SuperSprintData::NUM_PLAYERS );
+
+ strncpy( mVehicleSlots[ playerID ].mCarName, name, sizeof( mVehicleSlots[ playerID ].mCarName ) );
+}
+
+//=============================================================================
+// SuperSprintManager::FindLeader
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: char
+//
+//=============================================================================
+char SuperSprintManager::FindLeader()
+{
+ char leader = -1;
+ unsigned char numLaps = 0;
+ char nextCheckpoint = -1;
+
+ //For now just figure out who's in the lead.
+ char i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ if ( mPlayers[ i ].mNumLaps > numLaps )
+ {
+ //This guy is in the lead.
+ leader = i;
+ numLaps = mPlayers[ i ].mNumLaps;
+ nextCheckpoint = mPlayers[ i ].mNextCheckPoint;
+ }
+ else if ( mPlayers[ i ].mNumLaps == numLaps )
+ {
+ if ( mPlayers[ i ].mNextCheckPoint > nextCheckpoint )
+ {
+ //This guys is in the lead.
+ leader = i;
+ numLaps = mPlayers[ i ].mNumLaps;
+ nextCheckpoint = mPlayers[ i ].mNextCheckPoint;
+ }
+ }
+ }
+
+ return leader;
+}
+
+//=============================================================================
+// SuperSprintManager::CalculatePositions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::CalculatePositions()
+{
+#if defined( RAD_DEBUG ) || defined( RAD_TUNE )
+ char errMsg[128];
+#endif
+
+ int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+ if ( mPlayers[ i ].mNumLaps == mNumLaps )
+ {
+ //No more updates for this guy.
+ continue;
+ }
+
+ if ( !mVehicleSlots[ i ].mIsHuman )
+ {
+ WaypointAI* ai = mVehicleSlots[ i ].mVehicleAI;
+
+ rmt::Vector aiPos;
+ ai->GetPosition( &aiPos );
+
+ /*
+ // reset as necessary.
+ rmt::Sphere aiSphere;
+ ai->GetVehicle()->GetBoundingSphere( &aiSphere );
+
+ SuperCam* superCam = GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ rAssert( superCam );
+
+ if( superCam->GetType() == SuperCam::SUPER_SPRINT_CAM &&
+ !superCam->GetCamera()->SphereVisible( aiPos, aiSphere.radius ) )
+ {
+ ai->GetVehicle()->ResetOnSpot( false );
+ }
+ */
+
+
+ //////////////////////////////////////////////////////
+ // Query the road manager to get the distance
+ RoadManager::PathElement aiElem;
+ aiElem.elem = NULL;
+ RoadSegment* aiSeg = NULL;
+ float aiSegT = 0.0f;
+ float aiRoadT = 0.0f;
+
+ ai->GetRacePathInfo( aiElem, aiSeg, aiSegT, aiRoadT );
+
+
+ // make sure the AI is on a path element
+#if defined( RAD_DEBUG ) || defined( RAD_TUNE )
+ sprintf( errMsg, "Something wrong with player %d!\n", i+1 );
+ rTuneAssertMsg( aiElem.elem != NULL, errMsg );
+ rTuneAssertMsg( 0 <= mPlayers[ i ].mNextCheckPoint &&
+ mPlayers[ i ].mNextCheckPoint < GetNumCheckpoints(), errMsg );
+#endif
+ char nextCheckpoint = mCheckPoints[ mPlayers[ i ].mNextCheckPoint ];
+
+ int path = GetPathDataWith( GetCheckpointWith( nextCheckpoint ) );
+ rAssert( path != -1 );
+
+ RoadManager::PathElement collectibleElem = mPathData[ path ].closestElem;
+ float collectibleRoadT = mPathData[ path ].roadT;
+
+ rmt::Vector collectiblePos;
+ mPathData[ path ].loc->GetLocation( &collectiblePos );
+
+ // make sure the collectible is on a path element
+ rAssert( collectibleElem.elem != NULL );
+
+ float aiDistToColl = NEAR_INFINITY;
+ if( aiElem.elem != NULL && collectibleElem.elem != NULL )
+ {
+ SwapArray<RoadManager::PathElement> dummy;
+
+ HeapMgr()->PushHeap(GMA_TEMP);
+ dummy.Allocate( RoadManager::GetInstance()->GetMaxPathElements() );
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+ aiDistToColl = RoadManager::GetInstance()->FindPathElementsBetween(
+ false,
+ aiElem, aiRoadT, aiPos,
+ collectibleElem, collectibleRoadT, collectiblePos,
+ dummy );
+ }
+
+ ai->SetDistToCurrentCollectible( aiDistToColl );
+ mPlayers[ i ].mDistToCheckpoint = aiDistToColl;
+ }
+ else
+ {
+ ////////////////////////////////////////////////////////
+ // Find out player's dist to mNextCollectible
+ //
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer( i );
+ rAssert( player );
+
+ rmt::Vector playerPos;
+ player->GetPosition( playerPos );
+
+ /*
+ // if player has gone out of bounds, reset to nearest segment
+ rmt::Sphere playerSphere;
+ player->GetVehicle()->GetBoundingSphere( &playerSphere );
+
+ SuperCam* superCam = GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ rAssert( superCam );
+
+ if( superCam->GetType() == SuperCam::SUPER_SPRINT_CAM &&
+ !superCam->GetCamera()->SphereVisible( playerPos, playerSphere.radius ) )
+ {
+ player->GetVehicle()->ResetOnSpot( false );
+ player->mHasBeenUpdatedThisFrame = false;
+ }
+ */
+
+
+ RoadManager::PathElement playerElem;
+ playerElem.elem = NULL;
+ RoadSegment* playerSeg = NULL;
+ float playerSegT = 0.0f;
+ float playerRoadT = 0.0f;
+
+ player->GetLastPathInfo( playerElem, playerSeg, playerSegT, playerRoadT );
+
+
+#if defined( RAD_DEBUG ) || defined( RAD_TUNE )
+ rTuneAssertMsg( playerElem.elem != NULL, errMsg );
+ rTuneAssertMsg( 0 <= mPlayers[ i ].mNextCheckPoint &&
+ mPlayers[ i ].mNextCheckPoint < GetNumCheckpoints(), errMsg );
+#endif
+
+ char nextCheckpoint = mCheckPoints[ mPlayers[ i ].mNextCheckPoint ];
+
+ int path = GetPathDataWith( GetCheckpointWith( nextCheckpoint ) );
+ rAssert( path != -1 );
+
+ RoadManager::PathElement collectibleElem = mPathData[ path ].closestElem;
+ float collectibleRoadT = mPathData[ path ].roadT;
+
+ rmt::Vector collectiblePos;
+ mPathData[ path ].loc->GetLocation( &collectiblePos );
+
+ // make sure the collectible is on a path element
+ rAssert( collectibleElem.elem != NULL );
+
+ float playerDistToCurrCollectible = NEAR_INFINITY;
+ if( playerElem.elem != NULL && collectibleElem.elem != NULL )
+ {
+ SwapArray<RoadManager::PathElement> dummy;
+
+ HeapMgr()->PushHeap(GMA_TEMP);
+ dummy.Allocate( RoadManager::GetInstance()->GetMaxPathElements() );
+ HeapMgr()->PopHeap(GMA_TEMP);
+
+ playerDistToCurrCollectible = RoadManager::GetInstance()->FindPathElementsBetween(
+ false,
+ playerElem, playerRoadT, playerPos,
+ collectibleElem, collectibleRoadT, collectiblePos,
+ dummy );
+ }
+
+ mPlayers[ i ].mDistToCheckpoint = playerDistToCurrCollectible;
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////
+ // Update my position
+ //
+
+ unsigned int j;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ if ( mPlayers[ i ].mNumLaps == mNumLaps )
+ {
+ //No more updates for this guy.
+ continue;
+ }
+
+ unsigned int numInFront = 0;
+ unsigned int vehiclesWithMe[ SuperSprintData::NUM_PLAYERS ];
+ unsigned int numWithMe = 0;
+ unsigned int numFinished = 0;
+
+ int whichOtherPlayer = (i + 1) % SuperSprintData::NUM_PLAYERS;
+
+ for( j = 0; j < SuperSprintData::NUM_PLAYERS - 1; ++j )
+ {
+ int numLapsCompleted = mPlayers[ whichOtherPlayer ].mNumLaps;
+
+ if ( numLapsCompleted == mNumLaps )
+ {
+ //They're finshed racing
+ numFinished++;
+ }
+ else if ( numLapsCompleted > mPlayers[ i ].mNumLaps )
+ {
+ //This guys finished more laps than I.
+ numInFront++;
+ }
+ else if ( numLapsCompleted == mPlayers[ i ].mNumLaps )
+ {
+ //We're racing the same lap number.
+ char currCollectible = mPlayers[ whichOtherPlayer ].mNextCheckPoint;
+ char playerCollectible = mPlayers[ i ].mNextCheckPoint;
+
+ if( currCollectible > playerCollectible )
+ {
+ numInFront++;
+ }
+ else if( currCollectible == playerCollectible )
+ {
+ vehiclesWithMe[ numWithMe ] = whichOtherPlayer;
+ numWithMe++;
+ }
+ }
+
+ whichOtherPlayer = ( whichOtherPlayer + 1 ) % SuperSprintData::NUM_PLAYERS;
+ }
+
+ mPositions[ i ] = 1 + numInFront + numFinished;
+
+ //Now, who is actually in front of me going to the same waypoint?
+ const Locator* waypointLoc = GetCheckpointWith( mCheckPoints[ mPlayers[ i ].mNextCheckPoint ] );
+ if ( !waypointLoc )
+ {
+ rAssert( false ); //Why? Because we've not started to race yet.
+ return;
+ }
+
+ ////////////////////////////////////////////////////////////
+ // The last set of vehicles to consider are the ones that
+ // are headed to the same collectible (race checkpoint) and
+ // are in the same lap as we are...
+ //
+ for( j = 0; j < numWithMe; ++j )
+ {
+ int index = vehiclesWithMe[ j ];
+
+ //This guy and I are racing for the same collectible
+ //Test the dist to the collectible.
+
+ float aiDistToCurrCollectible = mPlayers[ index ].mDistToCheckpoint;
+ float playerDistToCurrCollectible = mPlayers[ i ].mDistToCheckpoint;
+
+ if( aiDistToCurrCollectible < playerDistToCurrCollectible )
+ {
+ // blast! he's ahead of me... my pos is thus bumped even lower...
+ mPositions[ i ] = mPositions[ i ] + 1;
+ }
+ }
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::LoadLevelData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::LoadLevelData()
+{
+ char name[64];
+ sprintf( name, "scripts\\ss.mfk" );
+
+ BillboardWrappedLoader::OverrideLoader( true );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER ); // I choose other because this stuff will exist longer than a single mission
+
+ GetMissionScriptLoader()->SetFileHander( FILEHANDLER_LEVEL );
+ GetMissionScriptLoader()->LoadScriptAsync( name );
+
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+
+ BillboardWrappedLoader::OverrideLoader( false );
+}
+
+//=============================================================================
+// SuperSprintManager::Reset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::Reset()
+{
+ //Reset the same race with all the existing cars and stuff.
+ PositionCars();
+ PositionAI();
+ ResetRaceData();
+
+ mCountDownTime = 5000;
+ mDrawable->SetRenderState( SuperSprintDrawable::COUNT_DOWN );
+ mDrawable->SetTextScale( 1.0f );
+
+ InitCamera();
+ PlayIntroCam();
+
+ SetState( COUNT_DOWN );
+}
+
+//=============================================================================
+// SuperSprintManager::OnButton
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int controllerId, int id, const Button* pButton )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::OnButton( int controllerId, int id, const Button* pButton )
+{
+}
+
+//=============================================================================
+// SuperSprintManager::OnButtonUp
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int controllerId, int buttonId, const Button* pButton )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::OnButtonUp( int controllerId, int buttonId, const Button* pButton )
+{
+ if ( buttonId == ShowPositions )
+ {
+ int playerID = GetInputManager()->GetControllerPlayerIDforController( controllerId );
+
+ if ( playerID >= 0 && playerID < SuperSprintData::NUM_PLAYERS )
+ {
+ mTogglePosition[ playerID ] = false;
+ }
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::OnButtonDown
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int controllerId, int buttonId, const Button* pButton )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::OnButtonDown( int controllerId, int buttonId, const Button* pButton )
+{
+ switch ( mCurrentState )
+ {
+ case RACING:
+ {
+ int playerID = GetInputManager()->GetControllerPlayerIDforController( controllerId );
+ if ( playerID == -1 )
+ {
+ playerID = controllerId;
+
+ rAssert( playerID < SuperSprintData::NUM_PLAYERS );
+ if( playerID >= SuperSprintData::NUM_PLAYERS )
+ {
+ return;
+ }
+ }
+
+ int controllerIDPlayer = GetInputManager()->GetControllerIDforPlayer( playerID );
+ if ( buttonId == CamSelect && mVehicleSlots[ playerID ].mIsHuman && !GetGameFlow()->GetContext( CONTEXT_SUPERSPRINT )->IsSuspended())
+ {
+ unsigned int playerCount = 0;
+ int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ if ( mVehicleSlots[ i ].mIsHuman )
+ {
+ ++playerCount;
+ }
+ }
+
+ if ( playerCount == 1 )
+ {
+ GetSuperCamManager()->GetSCC( 0 )->SetTarget( mVehicleSlots[ playerID ].mVehicle );
+
+ if ( GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam()->GetType() == SuperCam::WRECKLESS_CAM )
+ {
+ //Skip the animated cams.
+ GetSuperCamManager()->GetSCC( 0 )->SelectSuperCam( (unsigned int)0 );
+ }
+ else
+ {
+ GetSuperCamManager()->GetSCC( 0 )->ToggleSuperCam( true );
+ }
+ }
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+
+ if ( buttonId == ShowPositions )
+ {
+ int playerID = GetInputManager()->GetControllerPlayerIDforController( controllerId );
+
+ if ( playerID >= 0 && playerID < SuperSprintData::NUM_PLAYERS )
+ {
+ mTogglePosition[ playerID ] = true;
+ }
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::LoadControllerMappings
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int controllerId )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::LoadControllerMappings( unsigned int controllerId )
+{
+ if ( GetInputManager()->GetController( controllerId )->IsConnected() )
+ {
+#ifdef RAD_XBOX
+ ClearMap(0);
+ Map( "Start", Start, 0, controllerId );
+ Map( "A", Select, 0, controllerId );
+ Map( "B", Back, 0, controllerId );
+ Map( "DPadRight", Right, 0, controllerId );
+ Map( "DPadLeft", Left, 0, controllerId );
+ Map( "LeftStickX", StickX, 0, controllerId );
+ Map( "LeftTrigger", L1, 0, controllerId );
+ Map( "Black", CamSelect, 0, controllerId );
+ Map( "Y", ShowPositions, 0, controllerId );
+#endif
+
+#ifdef RAD_PS2
+ ClearMap(0);
+ Map( "Start", Start, 0, controllerId );
+ Map( "X", Select, 0, controllerId );
+ Map( "Triangle", Back, 0, controllerId );
+ Map( "DPadRight", Right, 0, controllerId );
+ Map( "DPadLeft", Left, 0, controllerId );
+ Map( "LeftStickX", StickX, 0, controllerId );
+ Map( "L1", L1, 0, controllerId );
+ Map( "Select", CamSelect, 0, controllerId );
+ Map( "Triangle", ShowPositions, 0, controllerId );
+#endif
+
+#ifdef RAD_GAMECUBE
+ ClearMap(0);
+ Map( "Menu", Start, 0, controllerId );
+ Map( "A", Select, 0, controllerId );
+ Map( "B", Back, 0, controllerId );
+ Map( "DPadRight", Right, 0, controllerId );
+ Map( "DPadLeft", Left, 0, controllerId );
+ Map( "LeftStickX", StickX, 0, controllerId );
+ Map( "AnalogTriggerL", L1, 0, controllerId );
+ Map( "DPadDown", CamSelect, 0, controllerId );
+ Map( "Y", ShowPositions, 0, controllerId );
+#endif
+
+#ifdef RAD_WIN32
+ ClearMap(0);
+ Map( "Pause", Start, 0, controllerId );
+ Map( "Attack", Select, 0, controllerId );
+ Map( "Jump", Back, 0, controllerId );
+ Map( "MoveRight", Right, 0, controllerId );
+ Map( "MoveLeft", Left, 0, controllerId );
+ Map( "MoveX", StickX, 0, controllerId );
+ //Map( "LeftTrigger", L1, 0, controllerId );
+ Map( "CameraToggle", CamSelect, 0, controllerId );
+#endif
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::OnControllerConnect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int id )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::OnControllerConnect( int id )
+{
+ int playerID = GetInputManager()->GetControllerPlayerIDforController( id );
+
+ if ( playerID != -1 && mVehicleSlots[ playerID ].mState == SuperSprintData::CarData::SELECTED &&
+ static_cast<int>( mPlayers[ playerID ].mNumLaps ) < mNumLaps &&
+ mVehicleSlots[ playerID ].mIsHuman &&
+ (mCurrentState == RACING || mCurrentState == DNF_TIMEOUT) )
+ {
+ GetInputManager()->GetController( id )->SetGameState( Input::ACTIVE_ALL );
+ GetInputManager()->GetController( id )->SetRumble( GetInputManager()->IsRumbleEnabled() );
+ }
+ else
+ {
+ GetInputManager()->GetController( id )->SetGameState( Input::ACTIVE_SS_GAME );
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::OnControllerDisconnect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int id )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::OnControllerDisconnect( int id )
+{
+}
+
+//=============================================================================
+// SuperSprintManager::GetNumCheckpoints
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: char
+//
+//=============================================================================
+char SuperSprintManager::GetNumCheckpoints()
+{
+ if ( mNumCheckPoints == 0 )
+ {
+ if ( (RenderEnums::LevelEnum)(GetCurrentLevelIndex() + RenderEnums::B00) == RenderEnums::B02 ||
+ (RenderEnums::LevelEnum)(GetCurrentLevelIndex() + RenderEnums::B00) == RenderEnums::B07 )
+ {
+ if ( mGoLeft )
+ {
+ mCheckPoints = CHECKPOINT_LIST_B02_R;
+ }
+ else
+ {
+ mCheckPoints = CHECKPOINT_LIST_B02;
+ }
+
+ mNumCheckPoints = sizeof( CHECKPOINT_LIST_B02 ) / sizeof( char );
+ }
+ else if ( (RenderEnums::LevelEnum)(GetCurrentLevelIndex() + RenderEnums::B00) == RenderEnums::B05 )
+ {
+ if ( mGoLeft )
+ {
+ mCheckPoints = CHECKPOINT_LIST_B05_R;
+ }
+ else
+ {
+ mCheckPoints = CHECKPOINT_LIST_B05;
+ }
+
+ mNumCheckPoints = sizeof( CHECKPOINT_LIST_B05 ) / sizeof( char );
+ }
+ else if ( (RenderEnums::LevelEnum)(GetCurrentLevelIndex() + RenderEnums::B00) == RenderEnums::B01 )
+ {
+ if ( mGoLeft )
+ {
+ mCheckPoints = CHEKPOINT_LIST_B01_R;
+ }
+ else
+ {
+ mCheckPoints = CHEKPOINT_LIST_B01;
+ }
+
+ mNumCheckPoints = sizeof( CHEKPOINT_LIST_B01 ) / sizeof( char );
+ }
+ else if ( ((RenderEnums::LevelEnum)(GetCurrentLevelIndex() + RenderEnums::B00) == RenderEnums::B03 ) ||
+ ((RenderEnums::LevelEnum)(GetCurrentLevelIndex() + RenderEnums::B00) == RenderEnums::B04 )||
+ ((RenderEnums::LevelEnum)(GetCurrentLevelIndex() + RenderEnums::B00) == RenderEnums::B06 ) )
+ {
+ if ( mGoLeft )
+ {
+ mCheckPoints = CHECKPOINT_LIST_B03_R;
+ }
+ else
+ {
+ mCheckPoints = CHECKPOINT_LIST_B03;
+ }
+
+ mNumCheckPoints = sizeof( CHECKPOINT_LIST_B03 ) / sizeof( char );
+ }
+ else
+ {
+ if ( mGoLeft )
+ {
+ mCheckPoints = CHECKPOINT_LIST_R;
+ }
+ else
+ {
+ mCheckPoints = CHECKPOINT_LIST;
+ }
+
+ mNumCheckPoints = sizeof( CHECKPOINT_LIST ) / sizeof( char );
+ }
+ }
+
+ return mNumCheckPoints;
+}
+
+//*****************************************************************************
+//
+// Protected Member Functions
+//
+//*****************************************************************************
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// SuperSprintManager::SetUpCars
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::SetUpCars()
+{
+ unsigned int conFileIndex = 1;
+
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ if ( mVehicleSlots[ i ].mState == SuperSprintData::CarData::SELECTED )
+ {
+ mNumActivePlayers++;
+ char* carName = mVehicleSlots[i].mCarName; //Why does this have to be non-const?
+
+ if( mVehicleSlots[ i ].mIsHuman )
+ {
+ mNumHumanPlayers++;
+ mVehicleSlots[ i ].mVehicle = GetVehicleCentral()->InitVehicle( carName, true, NULL, VT_USER,
+ VehicleCentral::FORCE_NO_DRIVER,
+ false, // playercar - I think this is chucks thing for the character sheet
+ false); // one of the few rare cases where we start in the car
+ }
+ else
+ {
+ char scriptName[64];
+ sprintf( scriptName, "bonus/bg%d_%d.con", GetCurrentLevelIndex(), conFileIndex );
+ mVehicleSlots[ i ].mVehicle = GetVehicleCentral()->InitVehicle( carName, true, scriptName, VT_AI, VehicleCentral::FORCE_NO_DRIVER, false, false ); // see comments above
+ ++conFileIndex;
+ }
+
+ rAssert( mVehicleSlots[ i ].mVehicle );
+
+ mVehicleSlots[ i ].mVehicle->AddRef();
+
+ int added = GetVehicleCentral()->AddVehicleToActiveList( mVehicleSlots[ i ].mVehicle );
+ rAssert( added != -1 );
+
+ mVehicleSlots[ i ].mActiveListIndex = added;
+ }
+
+ if( mVehicleSlots[ i ].mVehicle != NULL )
+ {
+ GetSuperCamManager()->GetSCC( i )->SetTarget( mVehicleSlots[ i ].mVehicle );
+ GetSoundManager()->LoadCarSound( mVehicleSlots[ i ].mVehicle, false );
+ }
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::CleanUpCars
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::CleanUpCars()
+{
+ int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ if ( mVehicleSlots[ i ].mVehicle != NULL )
+ {
+ // drop character out of the car...
+ Character* playerCharacter = GetAvatarManager()->GetAvatarForPlayer( i )->GetCharacter();
+ //PlaceCharacterAtLocator( playerCharacter, this->m );
+ GetAvatarManager()->PutCharacterOnGround( playerCharacter, mVehicleSlots[ i ].mVehicle );
+
+ GetVehicleCentral()->RemoveVehicleFromActiveList( mVehicleSlots[ i ].mVehicle );
+
+ mVehicleSlots[ i ].mVehicle->Release();
+ mVehicleSlots[ i ].mVehicle = NULL;
+ }
+ if( mVehicleSlots[ i ].mVehicleAI != NULL )
+ {
+ rAssert( mVehicleSlots[ i ].mActiveListIndex != -1 );
+ GetVehicleCentral()->SetVehicleController( mVehicleSlots[ i ].mActiveListIndex, NULL );
+ mVehicleSlots[ i ].mVehicleAI->SetActive( false );
+ mVehicleSlots[ i ].mVehicleAI->Finalize();
+ mVehicleSlots[ i ].mVehicleAI->Release();
+ mVehicleSlots[ i ].mVehicleAI = NULL;
+ }
+ mVehicleSlots[ i ].mActiveListIndex = -1;
+
+ }
+
+ //Dump the car geo.
+ p3d::pddi->DrawSync();
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ p3d::inventory->RemoveSectionElements( mVehicleSlots[ i ].mCarName );
+ p3d::inventory->DeleteSection( mVehicleSlots[ i ].mCarName );
+ }
+
+ mNumActivePlayers = 0;
+ mNumHumanPlayers = 0;
+ mNumHumansFinished = 0;
+}
+
+//=============================================================================
+// SuperSprintManager::PositionCars
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::PositionCars()
+{
+ unsigned int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ // figure out the start position ...
+ char carlocator[32];
+ sprintf( carlocator, "car%d", i + 1 );
+
+ CarStartLocator* loc = p3d::find<CarStartLocator>(carlocator);
+ rAssert( loc );
+
+ if ( loc )
+ {
+ rmt::Vector pos;
+ float facing;
+ loc->GetLocation( &pos );
+ facing = loc->GetRotation();
+
+ if ( mGoLeft )
+ {
+ //We go Left.
+ facing += rmt::PI;
+ }
+
+ rAssert( mVehicleSlots[ i ].mVehicle );
+ mVehicleSlots[ i ].mVehicle->SetInitialPosition( &pos );
+ mVehicleSlots[ i ].mVehicle->SetResetFacingInRadians( facing );
+ mVehicleSlots[ i ].mVehicle->Reset();
+ }
+ }
+
+ DisableAllControllers();
+}
+
+//=============================================================================
+// SuperSprintManager::PositionAI
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::PositionAI()
+{
+ ///////////////////////////////////////////////////
+ // Go through all the waypoints, accumulating them...
+ //
+ Locator* waypoints[ MAX_AI_WAYPOINTS ];
+ int nWaypoints = 0;
+
+ // can either drive left around the track or right around the track
+ char waypointDir[4];
+ if( mGoLeft )
+ {
+ sprintf( waypointDir, "WPL" );
+ }
+ else
+ {
+ sprintf( waypointDir, "WPR" );
+ }
+
+ // iterate from 1 to MAX_AI_WAYPOINTS (inclusive)
+ char waypointName[8];
+ unsigned int i;
+ for( i = 1; i <= MAX_AI_WAYPOINTS; ++i )
+ {
+ if( i < 10 )
+ {
+ sprintf( waypointName, "%s0%d", waypointDir, i );
+ }
+ else
+ {
+ rAssert( i < 100 ); // keep it to 2 digits
+ sprintf( waypointName, "%s%d", waypointDir, i );
+ }
+
+ waypointName[5] = '\0';
+ Locator* wayloc = p3d::find<Locator>( waypointName );
+
+ if( wayloc == NULL )
+ {
+ // well... no more waypoints, just quit
+ break;
+ }
+
+ waypoints[ nWaypoints ] = wayloc;
+ nWaypoints++;
+ }
+
+ if( nWaypoints <= 0 )
+ {
+ rDebugPrintf( "SUPERSPRINT WARNING: Uh... We didn't see any waypoints"
+ "for AI. AI-controlled vehicles will not budge.\n" );
+ }
+
+ mNumCheckPointLocators = 0;
+
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ // Put in the AI here...
+ const float smallTriggerRadius = 7.0f;
+ if( !mVehicleSlots[ i ].mIsHuman )
+ {
+ if ( mVehicleSlots[ i ].mVehicleAI == NULL )
+ {
+ mVehicleSlots[ i ].mVehicleAI = new WaypointAI( mVehicleSlots[ i ].mVehicle, false, smallTriggerRadius, true ); // indicate false to segment optimization
+ mVehicleSlots[ i ].mVehicleAI->SetUseMultiplier( false ); // no traffic in supersprint... no need for multiplier
+ mVehicleSlots[ i ].mVehicleAI->AddRef(); // Corresponding call to Release() will already call delete if refcount<=1
+ }
+ else
+ {
+ //Cleanup to restart
+ mVehicleSlots[ i ].mVehicleAI->Finalize();
+
+ }
+
+ mVehicleSlots[ i ].mVehicleAI->Initialize();
+
+ // Just before this, we obtained a list of waypoints...
+ // Add them to the AIs here
+ for( int j=0; j<nWaypoints; j++ )
+ {
+ static_cast<WaypointAI*>(mVehicleSlots[ i ].mVehicleAI)->AddWaypoint( waypoints[j] );
+ }
+ }
+ }
+
+ DisableAllAI();
+
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( "Level" );
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+ tInventory::Iterator<EventLocator> it( p3d::inventory );
+
+ EventLocator* loc = it.First();
+
+ while( loc != NULL )
+ {
+ if ( loc->GetEventType() == LocatorEvent::CHECK_POINT && loc->GetData() > 0 )
+ {
+ mCheckPointLocators[ mNumCheckPointLocators ] = loc;
+ //mCheckPointLocators[ mNumCheckPointLocators ]->AddRef();
+ ++mNumCheckPointLocators;
+ }
+
+
+
+ loc = it.Next();
+ }
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+ p3d::inventory->PopSection();
+
+ for( i = 0; i < mNumCheckPointLocators; i++ )
+ {
+ rAssert( mCheckPointLocators[ i ] != NULL );
+
+ rmt::Vector locPos;
+ mCheckPointLocators[ i ]->GetPosition( &locPos );
+
+ //////////////////////////////////////////////////
+ // Get what the locator was placed on & other info
+ //
+ // NOTE: We assume that a collectible isn't going to move..
+ // and that it's always either on a road segment or
+ // an intersection... This is safe to do because
+ // the information below is only going to be used
+ // for race objectives (a subclass of collectibleobjective)
+ // which requires that its collectibles don't move around.
+ // It's not that bad to fix it if this changes... we
+ // will simply need to re-invoke FindClosestPathElement
+ // every frame, as the collectible moves around and
+ // remember the last valid (non-off-road) values.
+ //
+
+ RoadSegment* seg = NULL;
+ float segT = 0.0f;
+ float roadT = 0.0f;
+ RoadManager::PathElement closestElem;
+ closestElem.elem = NULL;
+
+ bool succeeded = VehicleAI::FindClosestPathElement( locPos, closestElem, seg, segT, roadT, true );
+ if( !succeeded )
+ {
+ char msg[512];
+ sprintf( msg, "Locator at (%0.1f,%0.1f,%0.1f) must either be placed on a roadsegment "
+ "or in an intersection! Woe be the designer who placed down this locator! "
+ "For now, the closest road segment will be chosen instead.\n",
+ locPos.x, locPos.y, -1 * locPos.z );
+ rAssertMsg( false, msg );
+
+ RoadSegment* closestSeg = NULL;
+ float dummy;
+ GetIntersectManager()->FindClosestRoad( locPos, 100.0f, closestSeg, dummy );
+
+ seg = (RoadSegment*) closestSeg;
+ segT = RoadManager::DetermineSegmentT( locPos, seg );
+ roadT = RoadManager::DetermineRoadT( seg, segT );
+ closestElem.elem = seg->GetRoad();
+ closestElem.type = RoadManager::ET_NORMALROAD;
+ }
+
+ mPathData[ i ].closestElem = closestElem;
+ mPathData[ i ].seg = seg;
+ mPathData[ i ].segT = segT;
+ mPathData[ i ].roadT = roadT;
+ mPathData[ i ].loc = mCheckPointLocators[ i ];
+
+ rAssert( mPathData[ i ].closestElem.elem != NULL );
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::DisableAllAI
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::DisableAllAI()
+{
+ unsigned int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ if ( !mVehicleSlots[ i ].mIsHuman )
+ {
+ mVehicleSlots[ i ].mVehicleAI->SetActive( false );
+ }
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::DisableAllControllers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::DisableAllControllers()
+{
+ GetInputManager()->SetGameState( Input::ACTIVE_SS_GAME );
+
+ unsigned int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; i++ )
+ {
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( i );
+ if( controllerID != -1 )
+ {
+ GetInputManager()->SetRumbleForDevice( controllerID, false );
+ }
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::InitRaceData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::InitRaceData()
+{
+ unsigned int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ //Reset the player race data.
+ mPlayers[ i ].mBestLap = 0xffffffff;
+ mPlayers[ i ].mBestLapEntry = 0;
+ mPlayers[ i ].mBestTimeEntry = 0;
+ mPlayers[ i ].mLapTime = 0;
+ mPlayers[ i ].mNextCheckPoint = 0;
+ mPlayers[ i ].mNumLaps = 0;
+ mPlayers[ i ].mPosition = 0;
+ mPlayers[ i ].mWins = 0;
+ mPlayers[ i ].mRaceTime = 0;
+ mPlayers[ i ].mPoints = 0;
+ mPlayers[ i ].mDistToCheckpoint = NEAR_INFINITY;
+
+ mVehicleSlots[ i ].mVehicle->mNumTurbos = SuperSprintData::DEFAULT_TURBO_NUM;
+
+ mPositions[ i ] = i;
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::ResetRaceData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::ResetRaceData()
+{
+ unsigned int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ //Reset the player race data.
+ mPlayers[ i ].mBestLap = 0xffffffff;
+ mPlayers[ i ].mBestLapEntry = 0;
+ mPlayers[ i ].mBestTimeEntry = 0;
+ mPlayers[ i ].mLapTime = 0;
+ mPlayers[ i ].mNextCheckPoint = 0;
+ mPlayers[ i ].mNumLaps = 0;
+ mPlayers[ i ].mPosition = 0;
+ mPlayers[ i ].mRaceTime = 0;
+ mPlayers[ i ].mDistToCheckpoint = NEAR_INFINITY;
+
+ mVehicleSlots[ i ].mVehicle->mNumTurbos = SuperSprintData::DEFAULT_TURBO_NUM;
+
+ mPositions[ i ] = i;
+ }
+}
+
+
+//=============================================================================
+// SuperSprintManager::PlaceCharactersInCars
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::PlaceCharactersInCars()
+{
+ int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ if ( mVehicleSlots[ i ].mState == SuperSprintData::CarData::SELECTED )
+ {
+ GetAvatarManager()->PutCharacterInCar( GetAvatarManager()->GetAvatarForPlayer( i )->GetCharacter(), mVehicleSlots[i].mVehicle );
+ if( !mVehicleSlots[ i ].mIsHuman )
+ {
+ GetVehicleCentral()->SetVehicleController(
+ mVehicleSlots[ i ].mActiveListIndex,
+ mVehicleSlots[ i ].mVehicleAI );
+ }
+ }
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::StartLoadingCars
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::LoadCars()
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ // Force unjoined positions to be AI-controlled
+ if( mVehicleSlots[ i ].mState == SuperSprintData::CarData::WAITING )
+ {
+ //Let's randomly pick a car.
+ unsigned int carNum = rand() % SuperSprintData::NUM_NAMES;
+
+ while ( strcmp( SuperSprintData::VEHICLE_NAMES[ carNum ].name, mVehicleSlots[ (i + 1) % SuperSprintData::NUM_PLAYERS ].mCarName ) == 0 ||
+ strcmp( SuperSprintData::VEHICLE_NAMES[ carNum ].name, mVehicleSlots[ (i + 2) % SuperSprintData::NUM_PLAYERS ].mCarName ) == 0 ||
+ strcmp( SuperSprintData::VEHICLE_NAMES[ carNum ].name, mVehicleSlots[ (i + 3) % SuperSprintData::NUM_PLAYERS ].mCarName ) == 0 )
+ {
+ carNum = rand() % SuperSprintData::NUM_NAMES;
+ }
+
+ SetVehicle( i, SuperSprintData::VEHICLE_NAMES[ carNum ].name );
+
+ // transit to SELECTED, so that our car will get loaded, below
+ mVehicleSlots[ i ].mState = SuperSprintData::CarData::SELECTED;
+ }
+
+ //Force undecided players to the current selection.
+ if ( mVehicleSlots[ i ].mState == SuperSprintData::CarData::SELECTING )
+ {
+ mVehicleSlots[ i ].mState = SuperSprintData::CarData::SELECTED;
+ }
+
+ if ( mVehicleSlots[ i ].mState == SuperSprintData::CarData::SELECTED )
+ {
+ char fileName[64];
+ sprintf( fileName, "art\\cars\\%s.p3d", mVehicleSlots[ i ].mCarName );
+ p3d::inventory->AddSection( mVehicleSlots[ i ].mCarName );
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_LEVEL,
+ fileName,
+ GMA_LEVEL_OTHER,
+ mVehicleSlots[ i ].mCarName,
+ mVehicleSlots[ i ].mCarName );
+ }
+ }
+
+ //When we get this callback, the loading of all cars is done.
+// GetLoadingManager()->AddCallback( this );
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+
+ mDrawable->DoCountDownToo( false );
+}
+
+//=============================================================================
+// SuperSprintManager::LoadCharacters
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::LoadCharacters()
+{
+ unsigned int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ if ( !mVehicleSlots[ i ].mIsHuman )
+ {
+ //We need to select a character for this AI.
+ unsigned int whichName = rand() % SuperSprintData::NUM_CHARACTER_NAMES;
+
+ unsigned int j;
+ for ( j = 0; j < SuperSprintData::NUM_CHARACTER_NAMES; ++j )
+ {
+ bool found = false;
+ unsigned int k;
+ for ( k = 0; k < SuperSprintData::NUM_PLAYERS && !found; ++k )
+ {
+ if ( SuperSprintData::CHARACTER_NAMES[ mPlayers[ k ].mCharacterIndex ] != '\0' &&
+ ( mPlayers[ k ].mCharacterIndex == static_cast<int>( whichName ) ) )
+ {
+ //This name is taken.
+ found = true;
+ break;
+ }
+ }
+
+ if ( found )
+ {
+ whichName = ( whichName + 1 ) % SuperSprintData::NUM_CHARACTER_NAMES;
+ }
+ else
+ {
+ //We have a winner!
+ break;
+ }
+ }
+
+ rAssert( j < SuperSprintData::NUM_CHARACTER_NAMES );
+
+ SetCharacter( i, whichName );
+ }
+
+ GetCharacterManager()->AddPCCharacter( SuperSprintData::CHARACTER_NAMES[ mPlayers[ i ].mCharacterIndex ], "npd" );
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::EnumerateControllers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::EnumerateControllers()
+{
+ //Set up the controllers.
+ InputManager* im = GetInputManager();
+
+ unsigned int i;
+
+ for ( i = 0; i < Input::MaxControllers; ++i )
+ {
+ im->UnregisterMappable( i, this );
+ }
+
+ for ( i = 0; i < Input::MaxControllers; ++i )
+ {
+ int handle = im->RegisterMappable( i, this );
+ rAssert( handle != -1 );
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::SetupIcons
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::SetupIcons()
+{
+ //Get and init the two flags.
+ mFFlag->Init( "fflag", rmt::Vector( 3.0f, 6.0f, -40.0f ), false, false );
+ mFFlag->ScaleByCameraDistance( 1.0f, 3.0f, 10.0f, 100.0f );
+ mWFlag->Init( "wflag", rmt::Vector( 1.8f, 6.0f, -40.0f ), false, false );
+ mWFlag->ScaleByCameraDistance( 1.0f, 3.0f, 10.0f, 100.0f );
+
+ int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ char name[64];
+ sprintf( name, "MiniArrow%dr", i + 1 ); //Start at 1
+ mPositionIcon[ 0 ][ i ]->Init( name, rmt::Vector( 0.0f, 0.0f, 0.0f ), true, false );
+ mPositionIcon[ 0 ][ i ]->ScaleByCameraDistance( MIN_SCALE, MAX_SCALE, MIN_DIST, MAX_DIST );
+ sprintf( name, "MiniArrow%db", i + 1 ); //Start at 1
+ mPositionIcon[ 1 ][ i ]->Init( name, rmt::Vector( 0.0f, 0.0f, 0.0f ), true, false );
+ mPositionIcon[ 1 ][ i ]->ScaleByCameraDistance( MIN_SCALE, MAX_SCALE, MIN_DIST, MAX_DIST );
+ sprintf( name, "MiniArrow%dy", i + 1 ); //Start at 1
+ mPositionIcon[ 2 ][ i ]->Init( name, rmt::Vector( 0.0f, 0.0f, 0.0f ), true, false );
+ mPositionIcon[ 2 ][ i ]->ScaleByCameraDistance( MIN_SCALE, MAX_SCALE, MIN_DIST, MAX_DIST );
+ sprintf( name, "MiniArrow%dg", i + 1 ); //Start at 1
+ mPositionIcon[ 3 ][ i ]->Init( name, rmt::Vector( 0.0f, 0.0f, 0.0f ), true, false );
+ mPositionIcon[ 3 ][ i ]->ScaleByCameraDistance( MIN_SCALE, MAX_SCALE, MIN_DIST, MAX_DIST );
+
+ sprintf( name, "MiniArrowP%d", i + 1 );
+ mPlayerID[ i ]->Init( name, rmt::Vector( 0.0f, 0.0f, 0.0f ), true, false );
+ mPlayerID[ i ]->ScaleByCameraDistance( MIN_SCALE, MAX_SCALE, MIN_DIST, MAX_DIST );
+ }
+
+ char name[64];
+ sprintf( name, "MiniArrow%drGlow", 1 );
+ mNitroEffect[ 0 ]->Init( name, rmt::Vector( 0.0f, 0.0f, 0.0f ), true, false );
+ mNitroEffect[ 0 ]->ScaleByCameraDistance( MIN_SCALE, MAX_SCALE, MIN_DIST, MAX_DIST );
+ sprintf( name, "MiniArrow%dbGlow", 2 );
+ mNitroEffect[ 1 ]->Init( name, rmt::Vector( 0.0f, 0.0f, 0.0f ), true, false );
+ mNitroEffect[ 1 ]->ScaleByCameraDistance( MIN_SCALE, MAX_SCALE, MIN_DIST, MAX_DIST );
+ sprintf( name, "MiniArrow%dyGlow", 3 );
+ mNitroEffect[ 2 ]->Init( name, rmt::Vector( 0.0f, 0.0f, 0.0f ), true, false );
+ mNitroEffect[ 2 ]->ScaleByCameraDistance( MIN_SCALE, MAX_SCALE, MIN_DIST, MAX_DIST );
+ sprintf( name, "MiniArrow%dgGlow", 4 );
+ mNitroEffect[ 3 ]->Init( name, rmt::Vector( 0.0f, 0.0f, 0.0f ), true, false );
+ mNitroEffect[ 3 ]->ScaleByCameraDistance( MIN_SCALE, MAX_SCALE, MIN_DIST, MAX_DIST );
+}
+
+//=============================================================================
+// SuperSprintManager::InitCamera
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::InitCamera()
+{
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC( i );
+ rAssert( scc );
+
+ scc->SelectSuperCam( SuperCam::SUPER_SPRINT_CAM, SuperCamCentral::CUT | SuperCamCentral::QUICK, 0 );
+ }
+}
+
+
+int SuperSprintManager::GetOnlyHumanPlayerID()
+{
+ int onlyHumanID = -1;
+ int humanCount = 0;
+ for( int i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ if( mVehicleSlots[ i ].mIsHuman )
+ {
+ onlyHumanID = i;
+ humanCount++;
+ }
+ }
+ if( humanCount == 1 ) // only return the ID of the one human player
+ {
+ return onlyHumanID;
+ }
+ return -1;
+}
+
+//=============================================================================
+// SuperSprintManager::RestoreControllerState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::RestoreControllerState()
+{
+ unsigned int i;
+ for ( i = 0; i < Input::MaxControllers; ++i )
+ {
+ OnControllerConnect( i );
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::SetupTraps
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::SetupTraps()
+{
+ //Get the spikes if we need them
+ if ( (RenderEnums::LevelEnum)(GetCurrentLevelIndex() + RenderEnums::B00) == RenderEnums::B07 )
+ {
+ //Find the trap multicontroller.
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( "Default" );
+
+ mTrapController = p3d::find<AnimCollisionEntityDSG>( "spikes" );
+ mTrapController->AddRef();
+
+ //Set them running backwards so that it doesn't animate.
+ mTrapController->SetAnimationDirection( -1.0f );
+
+
+ p3d::inventory->PopSection();
+
+ rAssert( mTrapController );
+ }
+ else if ( (RenderEnums::LevelEnum)(GetCurrentLevelIndex() + RenderEnums::B00) == RenderEnums::B04 )
+ {
+ //Find the trap multicontroller.
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( "Default" );
+
+ mTrapController = p3d::find<AnimCollisionEntityDSG>( "pistons" );
+ mTrapController->AddRef();
+
+ //Set them running backwards so that it doesn't animate.
+ mTrapController->SetAnimationDirection( -1.0f );
+
+
+ p3d::inventory->PopSection();
+
+ rAssert( mTrapController );
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::PlayIntroCam
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::PlayIntroCam()
+{
+ AnimatedCam::SetMulticontroller( "minigamecam" );
+ AnimatedCam::SetCamera( "minigamecamShape" );
+ AnimatedCam::CheckPendingCameraSwitch();
+ AnimatedCam::SetCameraTransitionFlags( SuperCamCentral::CUT | SuperCamCentral::FORCE );
+}
+
+//=============================================================================
+// SuperSprintManager::UpdatePositionIcons
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( unsigned int milliseconds )
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::UpdatePositionIcons( unsigned int milliseconds )
+{
+ CalculatePositions();
+
+ //Sort positions.
+ int i, j;
+
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ rmt::Vector carPos;
+ mVehicleSlots[ i ].mVehicle->GetPosition( &carPos );
+
+ rmt::Box3D bbox;
+ mVehicleSlots[ i ].mVehicle->GetBoundingBox( &bbox );
+ carPos.y = bbox.high.y;
+
+ for ( j = 0; j < SuperSprintData::NUM_PLAYERS; ++j )
+ {
+ mPositionIcon[ i ][ j ]->ShouldRender( false );
+ }
+
+ mPlayerID[ i ]->ShouldRender( false );
+ mNitroEffect[ i ]->ShouldRender( false );
+
+ if ( mNumHumanPlayers == 1 )
+ {
+ bool pressed = false;
+ unsigned int k;
+ for ( k = 0; k < SuperSprintData::NUM_PLAYERS; ++k )
+ {
+ if ( mTogglePosition[ k ] && mVehicleSlots[ k ].mIsHuman )
+ {
+ pressed = true;
+ break;
+ }
+ }
+
+ //If the toggle button is pressed, show all the positions
+ if ( pressed )
+ {
+ mPositionIcon[ i ][ mPositions[ i ] - 1 ]->Move( carPos );
+ mPositionIcon[ i ][ mPositions[ i ] - 1 ]->Update( milliseconds );
+ mPositionIcon[ i ][ mPositions[ i ] - 1 ]->ShouldRender( true );
+ }
+ else
+ {
+ //If the camera is in top-view render otherwise don't
+ if ( !mVehicleSlots[ i ].mIsHuman ||
+ GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam()->GetType() == SuperCam::SUPER_SPRINT_CAM ||
+ GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam()->GetType() == SuperCam::ANIMATED_CAM )
+ {
+ if ( mVehicleSlots[ i ].mVehicle->mNumTurbos > 0 )
+ {
+ mNitroEffect[ i ]->Move( carPos );
+ mNitroEffect[ i ]->Update( milliseconds );
+ mNitroEffect[ i ]->ShouldRender( true );
+ }
+ else
+ {
+ mPlayerID[ i ]->Move( carPos );
+ mPlayerID[ i ]->Update( milliseconds );
+ mPlayerID[ i ]->ShouldRender( true );
+ }
+ }
+ }
+ }
+ else
+ {
+ //If the toggle button is pressed, show all the positions
+ if ( mTogglePosition[ i ] && mVehicleSlots[ i ].mIsHuman )
+ {
+ mPositionIcon[ i ][ mPositions[ i ] - 1 ]->Move( carPos );
+ mPositionIcon[ i ][ mPositions[ i ] - 1 ]->Update( milliseconds );
+ mPositionIcon[ i ][ mPositions[ i ] - 1 ]->ShouldRender( true );
+ }
+ else
+ {
+ if ( mVehicleSlots[ i ].mVehicle->mNumTurbos > 0 )
+ {
+ mNitroEffect[ i ]->Move( carPos );
+ mNitroEffect[ i ]->Update( milliseconds );
+ mNitroEffect[ i ]->ShouldRender( true );
+ }
+ else
+ {
+ mPlayerID[ i ]->Move( carPos );
+ mPlayerID[ i ]->Update( milliseconds );
+ mPlayerID[ i ]->ShouldRender( true );
+ }
+ }
+ }
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::GetCheckpointWith
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( char data )
+//
+// Return: Locator
+//
+//=============================================================================
+Locator* SuperSprintManager::GetCheckpointWith( char data )
+{
+ Locator* returnLoc = NULL;
+ unsigned int i;
+ for ( i = 0; i < mNumCheckPointLocators; ++i )
+ {
+ if ( mCheckPointLocators[ i ]->GetData() == (unsigned int)data )
+ {
+ returnLoc = mCheckPointLocators[ i ];
+ break;
+ }
+ }
+
+
+ return returnLoc;
+}
+
+//=============================================================================
+// SuperSprintManager::GetPathDataWith
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Locator* loc )
+//
+// Return: int
+//
+//=============================================================================
+int SuperSprintManager::GetPathDataWith( Locator* loc )
+{
+ int i;
+ for ( i = 0; i < MAX_AI_WAYPOINTS; ++i )
+ {
+ if ( mPathData[ i ].loc == loc )
+ {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+
+//=============================================================================
+// SuperSprintManager::PositionCharacters
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void SuperSprintManager::PositionCharacters()
+{
+ int i;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ char carlocator[32];
+ sprintf( carlocator, "car%d", i + 1 );
+
+ CarStartLocator* loc = p3d::find<CarStartLocator>(carlocator);
+ rAssert( loc );
+
+ if ( loc )
+ {
+ Character* character = GetCharacterManager()->GetCharacterByName( tEntity::MakeUID( SuperSprintData::CHARACTER_NAMES[ mPlayers[ i ].mCharacterIndex ] ) );
+
+ rmt::Vector pos;
+ loc->GetLocation( &pos );
+ character->RelocateAndReset( pos, 0.0f, true );
+ }
+ }
+}
+
+
+//=============================================================================
+// SuperSprintManager::SuperSprintManager
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+SuperSprintManager::SuperSprintManager() :
+ Mappable( Input::ACTIVE_SS_GAME ),
+ mCurrentState( IDLE ),
+ mDrawable( NULL ),
+ mCountDownTime( 3000 ),
+ mCurrentPosition( 1 ),
+ mStartTime( 0 ),
+ mNumActivePlayers( 0 ),
+ mNumHumanPlayers( 0 ),
+ mNumHumansFinished( 0 ),
+ mNumLaps( 1 ),
+ mGoLeft( false ),
+ mNumCheckPoints( 0 ),
+ mCheckPoints( CHECKPOINT_LIST ),
+ mFFlag( NULL ),
+ mWFlag( NULL ),
+ mFFlagTimeout( 0 ),
+ mWFlagTimeout( 0 ),
+ mTrapController( NULL ),
+ mTrapTriggered( false )
+{
+ mGameType = GameplayManager::GT_SUPERSPRINT;
+ SetNumPlayers( SuperSprintData::NUM_PLAYERS );
+
+ int i, j;
+ for ( i = 0; i < SuperSprintData::NUM_PLAYERS; ++i )
+ {
+ for ( j = 0; j < SuperSprintData::NUM_PLAYERS; ++j )
+ {
+ mPositionIcon[ i ][ j ] = NULL;
+ }
+
+ mPlayerID[ i ] = NULL;
+ mTogglePosition[ i ] = false;
+ mNitroEffect[ i ] = NULL;
+ }
+}
+
+//=============================================================================
+// SuperSprintManager::~SuperSprintManager
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+SuperSprintManager::~SuperSprintManager()
+{
+}
diff --git a/game/code/supersprint/supersprintmanager.h b/game/code/supersprint/supersprintmanager.h
new file mode 100644
index 0000000..cbd3a11
--- /dev/null
+++ b/game/code/supersprint/supersprintmanager.h
@@ -0,0 +1,383 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: supersprintmanager.h
+//
+// Description: Blahblahblah
+//
+// History: 2/3/2003 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef SUPERSPRINTMANAGER_H
+#define SUPERSPRINTMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <events/eventlistener.h>
+#include <input/mappable.h>
+#include <mission/gameplaymanager.h>
+#include <mission/animatedicon.h>
+#include <loading/loadingmanager.h>
+#include <supersprint/supersprintdata.h>
+#include <supersprint/supersprintdrawable.h>
+
+#include <roads/roadmanager.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class AnimCollisionEntityDSG;
+class RoadSegment;
+class EventLocator;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class SuperSprintManager : public Mappable,
+ public GameplayManager,
+ public LoadingManager::ProcessRequestsCallback
+{
+public:
+
+ enum
+ {
+ MAX_AI_WAYPOINTS = 20,
+ MAX_CHECKPOINT_LOCATORS = 16
+ };
+
+ //------------- S T A T E
+
+ enum State
+ {
+ IDLE, //idle, doing nothing
+
+ COUNT_DOWN, //Start of race 3, 2, 1 countdown
+
+ RACING, //Racing until everyone crosses the finish line. Anyone who has crossed the finish
+ //line is disabled until the end of the race. The last player has 30 seconds from the
+ //previous player finishing or they are DNF
+
+ DNF_TIMEOUT, //When this starts, anyone who does not finish in the remaining time gets a DNF
+
+ WINNER_CIRCLE, //Race stats and display of the winner and their time to finish
+
+ PAUSED //Get it?
+
+ //
+ // Goes IDLE -> COUNT_DOWN -> RACING -> WINNER_CIRCLE
+ // -> DNF_TIMEOUT ->WINNER_CIRCLE
+ //
+ // At anytime we can quit back to the game through the pause menu.
+ //
+ };
+ //From EventListener
+ void HandleEvent( EventEnum id, void* pEventData );
+
+ //From LoadingManager::ProcessRequestsCallback
+ void OnProcessRequestsComplete( void* pUserData );
+
+ //From GamePlayManager
+ virtual void LoadLevelData();
+ virtual void InitLevelData() {};
+ virtual void CleanMissionData() {};
+ virtual bool IsSundayDrive() { return true; };
+ virtual bool IsSuperSprint();
+ virtual void PerformLoading() {};
+ virtual void Reset();
+
+ //From Mappable
+ virtual void OnButton( int controllerId, int id, const Button* pButton );
+ virtual void OnButtonUp( int controllerId, int buttonId, const Button* pButton );
+ virtual void OnButtonDown( int controllerId, int buttonId, const Button* pButton );
+ virtual void LoadControllerMappings( unsigned int controllerId );
+ virtual void OnControllerConnect( int id );
+ virtual void OnControllerDisconnect( int id );
+
+ char GetNumCheckpoints();
+
+ //Local
+ static SuperSprintManager* GetInstance();
+ static void DestroyInstance();
+
+ void Initialize();
+ void Finalize();
+ void Update( unsigned int milliseconds );
+
+ void LoadScriptData();
+ void LoadCars();
+ void LoadCharacters();
+ void StartRace();
+
+ void SetCurrentLevel( int level );
+ void SetNumLaps( int numLaps );
+ void SetTrackDirection( bool reverse );
+ bool IsTrackReversed() const;
+ void SetCharacter( int playerID, int index );
+ void SetVehicle( int playerID, const char* name );
+
+ char FindLeader(); //Returns who's in the lead
+
+ void CalculatePositions();
+
+ const SuperSprintData::CarData* GetVehicleData( int playerID ) const;
+ const SuperSprintData::PlayerData* GetPlayerData( int playerID ) const;
+
+ int GetOnlyHumanPlayerID();
+
+ State GetState() const {return mCurrentState;};
+
+ void RestoreControllerState();
+
+protected:
+ void LoadMission() {};
+
+private:
+
+
+
+ enum ButtonMap
+ {
+ Start,
+ Select,
+ Back,
+ Left,
+ Right,
+ StickX,
+ CamSelect,
+ L1,
+ ShowPositions
+ };
+
+ static SuperSprintManager* spInstance;
+
+ State mCurrentState;
+
+ void SetState( State state );
+
+
+ //------------- P L A Y E R C A R S
+
+ SuperSprintData::CarData mVehicleSlots[ SuperSprintData::NUM_PLAYERS ];
+
+
+ //------------- P L A Y E R D A T A
+
+ SuperSprintData::PlayerData mPlayers[ SuperSprintData::NUM_PLAYERS ];
+
+ //------------- D R A W A B L E
+
+ SuperSprintDrawable* mDrawable;
+
+ int mCountDownTime;
+ char mTime[32];
+
+ int mCurrentPosition;
+
+ unsigned int mStartTime;
+
+ int mNumActivePlayers;
+ int mNumHumanPlayers;
+ int mNumHumansFinished;
+
+ int mCurrentLevel;
+ int mNumLaps;
+
+ // can either drive left around the track or right around the track
+ // default to FALSE (i.e. going "left" is going backwards)
+ bool mGoLeft;
+
+ char mNumCheckPoints; //Use the accessor please.
+ char* mCheckPoints;
+
+ AnimatedIcon* mFFlag;
+ AnimatedIcon* mWFlag;
+ unsigned int mFFlagTimeout;
+ unsigned int mWFlagTimeout;
+
+ AnimCollisionEntityDSG* mTrapController;
+ bool mTrapTriggered;
+
+ char mPositions[ SuperSprintData::NUM_PLAYERS ]; //This is an array of player IDs.
+ AnimatedIcon* mPositionIcon[ SuperSprintData::NUM_PLAYERS ][ SuperSprintData::NUM_PLAYERS ];
+ AnimatedIcon* mPlayerID[ SuperSprintData::NUM_PLAYERS ];
+ bool mTogglePosition[ SuperSprintData::NUM_PLAYERS ];
+ AnimatedIcon* mNitroEffect[ SuperSprintData::NUM_PLAYERS ];
+
+ struct PathData
+ {
+ PathData() : seg( NULL ), segT( 0.0f ), roadT( 0.0f ), loc( NULL ) {};
+ RoadSegment* seg;
+ float segT;
+ float roadT;
+ RoadManager::PathElement closestElem;
+ EventLocator* loc;
+ };
+
+ PathData mPathData[ MAX_AI_WAYPOINTS ];
+ EventLocator* mCheckPointLocators[ MAX_CHECKPOINT_LOCATORS ];
+ unsigned int mNumCheckPointLocators;
+
+ void PositionCharacters();
+ void SetUpCars();
+ void CleanUpCars();
+ void PositionCars();
+ void PositionAI();
+ void DisableAllAI();
+ void DisableAllControllers();
+ void InitRaceData();
+ void ResetRaceData();
+ void PlaceCharactersInCars();
+ void EnumerateControllers();
+ void SetupIcons();
+ void InitCamera();
+ void SetupTraps();
+ void PlayIntroCam();
+ void UpdatePositionIcons( unsigned int milliseconds );
+ Locator* GetCheckpointWith( char data );
+ int GetPathDataWith( Locator* loc );
+
+ SuperSprintManager();
+ virtual ~SuperSprintManager();
+
+ //Prevent wasteful constructor creation.
+ SuperSprintManager( const SuperSprintManager& supersprintmanager );
+ SuperSprintManager& operator=( const SuperSprintManager& supersprintmanager );
+
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+
+inline SuperSprintManager* GetSSM() { return SuperSprintManager::GetInstance(); };
+
+//*****************************************************************************
+//
+//Inline Private Member Functions
+//
+//*****************************************************************************
+inline void SuperSprintManager::SetState( SuperSprintManager::State state )
+{
+ mCurrentState = state;
+}
+
+//=============================================================================
+// SuperSprintManager::IsSuperSprint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: inline
+//
+//=============================================================================
+inline bool SuperSprintManager::IsSuperSprint()
+{
+ return( true );
+}
+
+//=============================================================================
+// SuperSprintManager::SetCurrentLevel
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int level )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperSprintManager::SetCurrentLevel( int level )
+{
+ mCurrentLevel = level;
+}
+
+//=============================================================================
+// SuperSprintManager::SetNumLaps
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int level )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperSprintManager::SetNumLaps( int numLaps )
+{
+ mNumLaps = numLaps;
+}
+
+//=============================================================================
+// SuperSprintManager::SetTrackDirection
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int level )
+//
+// Return: void
+//
+//=============================================================================
+inline void SuperSprintManager::SetTrackDirection( bool reverse )
+{
+ mGoLeft = reverse;
+}
+
+//=============================================================================
+// SuperSprintManager::IsTrackReversed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+inline bool SuperSprintManager::IsTrackReversed() const
+{
+ return mGoLeft;
+}
+
+//=============================================================================
+// SuperSprintManager::GetPlayerData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return:
+//
+//=============================================================================
+inline const SuperSprintData::PlayerData*
+SuperSprintManager::GetPlayerData( int playerID ) const
+{
+ rAssert( playerID >= 0 && playerID < SuperSprintData::NUM_PLAYERS );
+
+ return &( mPlayers[ playerID ] );
+}
+
+//=============================================================================
+// SuperSprintManager::GetVehicleData
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return:
+//
+//=============================================================================
+inline const SuperSprintData::CarData*
+SuperSprintManager::GetVehicleData( int playerID ) const
+{
+ rAssert( playerID >= 0 && playerID < SuperSprintData::NUM_PLAYERS );
+
+ return &( mVehicleSlots[ playerID ] );
+}
+
+#endif //SUPERSPRINTMANAGER_H
diff --git a/game/code/worldsim/allworldsim.cpp b/game/code/worldsim/allworldsim.cpp
new file mode 100644
index 0000000..1bc292d
--- /dev/null
+++ b/game/code/worldsim/allworldsim.cpp
@@ -0,0 +1,9 @@
+#include <worldsim/avatar.cpp>
+#include <worldsim/avatarmanager.cpp>
+#include <worldsim/groundplanepool.cpp>
+#include <worldsim/hitnrunmanager.cpp>
+#include <worldsim/vehiclecentral.cpp>
+#include <worldsim/worldcollisionsolveragent.cpp>
+#include <worldsim/worldobject.cpp>
+#include <worldsim/worldphysicsmanager.cpp>
+#include <worldsim/huskpool.cpp>
diff --git a/game/code/worldsim/avatar.cpp b/game/code/worldsim/avatar.cpp
new file mode 100644
index 0000000..69e5f44
--- /dev/null
+++ b/game/code/worldsim/avatar.cpp
@@ -0,0 +1,1101 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implementation of class Avatar
+//
+// History: 4/3/2002 + Created -- TBJ
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <worldsim/avatar.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactertarget.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/charactermappable.h>
+#include <worldsim/character/charactercontroller.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h>
+#include <worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h>
+#include <worldsim/redbrick/vehiclecontroller/vehiclemappable.h>
+#include <input/inputmanager.h>
+
+#include <ai/vehicle/vehicleai.h>
+
+#include <roads/geometry.h>
+#include <roads/road.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+
+#include <roads/intersection.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+
+#include <gameflow/gameflow.h>
+#include <supersprint/supersprintmanager.h>
+#include <worldsim/traffic/trafficmanager.h>
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//
+// If true, display avatar coordinates on screen
+//
+bool Avatar::s_displayCoordinates = false;
+
+struct AvatarInputManager
+{
+ AvatarInputManager( void )
+ :
+ mVehicleMappableHandle( -1 ),
+#ifdef RAD_PS2
+ mVehicleMappableHandleWheel0( -1 ),
+ mVehicleMappableHandleWheel1( -1 ),
+#endif
+ mCharacterMappableHandle( -1 )
+ {
+ HeapMgr()->PushHeap (GMA_LEVEL_OTHER);
+
+ mpVehicleMappable = new VehicleMappable;
+ mpVehicleMappable->AddRef();
+
+#ifdef RAD_PS2
+ mpVehicleMappableUSB0 = new VehicleMappable;
+ mpVehicleMappableUSB0->AddRef();
+
+ mpVehicleMappableUSB1 = new VehicleMappable;
+ mpVehicleMappableUSB1->AddRef();
+#endif
+
+ mpHumanVehicleController = new HumanVehicleController;
+ mpHumanVehicleController->AddRef();
+
+ mpInCarCharacterMappable = new InCarCharacterMappable;
+ mpInCarCharacterMappable->AddRef();
+
+ mpBipedCharacterMappable = new BipedCharacterMappable;
+ mpBipedCharacterMappable->AddRef();
+
+ mpCameraRelativeCharacterController = new CameraRelativeCharacterController;
+ mpCameraRelativeCharacterController->AddRef();
+
+ HeapMgr()->PopHeap (GMA_LEVEL_OTHER);
+ }
+ ~AvatarInputManager( void )
+ {
+ mpVehicleMappable->ReleaseController();
+ mpVehicleMappable->Release();
+
+#ifdef RAD_PS2
+ mpVehicleMappableUSB0->Release();
+ mpVehicleMappableUSB1->Release();
+#endif
+
+ mpHumanVehicleController->ReleaseVehicleMappable();
+ mpHumanVehicleController->Release();
+
+ mpInCarCharacterMappable->SetCharacterController( NULL );
+ mpInCarCharacterMappable->Release();
+
+ mpBipedCharacterMappable->SetCharacterController( NULL );
+ mpBipedCharacterMappable->Release();
+
+ mpCameraRelativeCharacterController->SetCamera (0);
+ mpCameraRelativeCharacterController->Release();
+
+ int x =5;
+ }
+ VehicleMappable* mpVehicleMappable;
+ VehicleMappable* mpVehicleMappableUSB0;
+ VehicleMappable* mpVehicleMappableUSB1;
+ HumanVehicleController* mpHumanVehicleController;
+ InCarCharacterMappable* mpInCarCharacterMappable;
+ BipedCharacterMappable* mpBipedCharacterMappable;
+ CameraRelativeCharacterController* mpCameraRelativeCharacterController;
+ int mVehicleMappableHandle;
+ int mVehicleMappableHandleWheel0;
+ int mVehicleMappableHandleWheel1;
+ int mCharacterMappableHandle;
+};
+
+
+void UnRegisterMappableHandle( int controllerId, int& handle )
+{
+ if ( handle > -1 )
+ {
+ // Detach the mappable.
+ //
+ InputManager::GetInstance( )->UnregisterMappable( controllerId, handle );
+ handle = -1;
+ }
+}
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// Avatar::Avatar
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Avatar::Avatar()
+:
+mHasBeenUpdatedThisFrame( false ),
+mLastRoadSegment( NULL ),
+mLastRoadSegmentT( 0.0f ),
+mLastRoadT( 0.0f ),
+mControllerId( INVALID_CONTROLLER ),
+mPlayerId( 0 ),
+mpCharacter( 0 ),
+mpVehicle( 0 ),
+mpAvatarInputManager( 0 )
+{
+ mLastPathElement.elem = NULL;
+
+ mpAvatarInputManager = new AvatarInputManager;
+
+ GetCheatInputSystem()->RegisterCallback( this );
+
+ //
+ // Just in case the cheat is set before object creation
+ //
+ if( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_SHOW_AVATAR_POSITION ) )
+ {
+ s_displayCoordinates = true;
+ }
+}
+
+//==============================================================================
+// Avatar::~Avatar
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Avatar::~Avatar()
+{
+ Destroy( );
+
+ if ( mpAvatarInputManager )
+ {
+ delete mpAvatarInputManager;
+ mpAvatarInputManager = 0;
+ }
+
+ GetCheatInputSystem()->UnregisterCallback( this );
+}
+
+void Avatar::Destroy( void )
+{
+
+ if ( mpCharacter )
+ {
+ mpCharacter->SetController( NULL );
+ mpCharacter->Release( );
+ mpCharacter = 0;
+ }
+ if ( mpVehicle )
+ {
+ mpVehicle->Release( );
+ mpVehicle = 0;
+ }
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mCharacterMappableHandle );
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mVehicleMappableHandle );
+#ifdef RAD_PS2
+ if ( mpAvatarInputManager->mVehicleMappableHandleWheel0 != -1 )
+ {
+ UnRegisterMappableHandle( Input::USB0, mpAvatarInputManager->mVehicleMappableHandleWheel0 );
+ }
+
+ if ( mpAvatarInputManager->mVehicleMappableHandleWheel1 != -1 )
+ {
+ UnRegisterMappableHandle( Input::USB1, mpAvatarInputManager->mVehicleMappableHandleWheel1 );
+ }
+#endif
+ mControllerId = INVALID_CONTROLLER;
+}
+
+/*
+==============================================================================
+Avatar::GetControllerId
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: int
+
+=============================================================================
+*/
+int Avatar::GetControllerId( void ) const
+{
+ return mControllerId;
+}
+
+/*
+==============================================================================
+Avatar::SetControllerId
+==============================================================================
+Description: Comment
+
+Parameters: ( int id )
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::SetControllerId( int id )
+{
+ mControllerId = id;
+}
+/*
+==============================================================================
+Avatar::GetCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: const
+
+=============================================================================
+*/
+Character* Avatar::GetCharacter( void ) const
+{
+ return mpCharacter;
+}
+
+/*
+==============================================================================
+Avatar::SetCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::SetCharacter( Character* pCharacter )
+{
+ tRefCounted::Assign( mpCharacter, pCharacter );
+ if( mpCharacter )
+ {
+ tColour shadowColour = ::GetGameplayManager()->GetControllerColour( mControllerId );
+ mpCharacter->SetShadowColour( shadowColour );
+ }
+}
+
+/*
+==============================================================================
+Avatar::GetVehicle
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: const
+
+=============================================================================
+*/
+Vehicle* Avatar::GetVehicle( void ) const
+{
+ return mpVehicle;
+}
+
+/*
+==============================================================================
+Avatar::SetVehicle
+==============================================================================
+Description: Comment
+
+Parameters: ( Vehicle* pVehicle )
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::SetVehicle( Vehicle* pVehicle )
+{
+ tRefCounted::Assign( mpVehicle, pVehicle );
+
+ if( pVehicle )
+ {
+ int playerID = 0; // normal game defaults player to zero
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_SUPERSPRINT )
+ {
+ playerID = SuperSprintManager::GetInstance()->GetOnlyHumanPlayerID();
+ if( this->mPlayerId != playerID )
+ {
+ return;
+ }
+ }
+
+ // can be -1 if in supersprint and more than 1 human is participating...
+ if( playerID == -1 )
+ {
+ return;
+ }
+
+ SuperCam* sc = GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam();
+ if ( sc && sc->GetType() == SuperCam::BUMPER_CAM )
+ {
+ //Only stop rendering for bumpercams.
+ pVehicle->DrawVehicle( false );
+ }
+ else
+ {
+ pVehicle->DrawVehicle( true );
+ }
+ }
+}
+
+/*
+==============================================================================
+Avatar::GetIntoVehicle
+==============================================================================
+Description: Pseudo code.
+
+ 1. Get a "human" vehicle controller.
+ 2. Init HumanVehicleController with mControllerId.
+ 3. Register with ControllerSystem.
+ 4. Get pointer to Vehicle via VehicleManager using vehicleId.
+ 5. Set Avatar member data mpVehicle to Vehicle pointer from 4.
+ 6. Set Controller for Vehicle.
+
+
+Parameters: ( Vehicle* pVehicle)
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::SetInCarController( void )
+{
+ rAssert( mpVehicle );
+ mpVehicle->mGeometryVehicle->FadeRoof( true );
+ mpVehicle->mGeometryVehicle->EnableLights( true );
+ mpVehicle->SetUserDrivingCar( true );
+ mpVehicle->SetDisableGasAndBrake(false);
+
+ // in case we just got into a traffi car, make sure brake lights are off
+ mpVehicle->mGeometryVehicle->HideBrakeLights();
+ mpVehicle->mBrakeLightsOn = false;
+ mpVehicle->mGeometryVehicle->HideReverseLights();
+ mpVehicle->mReverseLightsOn = false;
+
+ int vehicleId = GetVehicleCentral( )->GetVehicleId( mpVehicle );
+ // Detach the vehicle controller.
+ //
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mVehicleMappableHandle );
+
+
+ HumanVehicleController* pVehicleController = mpAvatarInputManager->mpHumanVehicleController;
+
+ VehicleMappable* pVehicleMappable = mpAvatarInputManager->mpVehicleMappable;
+ pVehicleController->Create( mpVehicle, pVehicleMappable, mControllerId );
+ pVehicleMappable->SetController( pVehicleController );
+
+ mpAvatarInputManager->mVehicleMappableHandle = InputManager::GetInstance( )->RegisterMappable( mControllerId, pVehicleMappable );
+
+ bool wheelAttached = false;
+#ifdef RAD_PS2
+ if ( GetGameplayManager()->GetGameType() != GameplayManager::GT_SUPERSPRINT )
+ {
+ if ( mControllerId == Input::USB0 ||
+ ( mControllerId != Input::USB0 &&
+ mControllerId != Input::USB1 &&
+ GetInputManager()->IsControllerInPort( Input::USB0 )
+ )
+ )
+ {
+ mpAvatarInputManager->mVehicleMappableHandleWheel0 = InputManager::GetInstance( )->RegisterMappable( Input::USB0, mpAvatarInputManager->mpVehicleMappableUSB0 );
+ pVehicleController->SetWheel( mpAvatarInputManager->mpVehicleMappableUSB0, 0 );
+ mpAvatarInputManager->mpVehicleMappableUSB0->SetController( pVehicleController );
+ GetInputManager()->SetRumbleForDevice( Input::USB0, GetInputManager()->IsRumbleEnabled() );
+ wheelAttached = true;
+ }
+ else if ( mControllerId == Input::USB1 ||
+ ( mControllerId != Input::USB0 &&
+ mControllerId != Input::USB1 &&
+ GetInputManager()->IsControllerInPort( Input::USB1 )
+ )
+ )
+ {
+ mpAvatarInputManager->mVehicleMappableHandleWheel1 = InputManager::GetInstance( )->RegisterMappable( Input::USB1, mpAvatarInputManager->mpVehicleMappableUSB1 );
+ pVehicleController->SetWheel( mpAvatarInputManager->mpVehicleMappableUSB1, 1 );
+ mpAvatarInputManager->mpVehicleMappableUSB1->SetController( pVehicleController );
+ GetInputManager()->SetRumbleForDevice( Input::USB1, GetInputManager()->IsRumbleEnabled() );
+ wheelAttached = true;
+ }
+ }
+#endif
+
+ if ( !wheelAttached )
+ {
+ GetInputManager()->SetRumbleForDevice( mControllerId, GetInputManager()->IsRumbleEnabled() );
+ }
+
+ if ( !GetGameplayManager()->mIsDemo )
+ {
+ GetVehicleCentral( )->SetVehicleController( vehicleId, pVehicleController );
+ }
+
+ // First things, detach the in car character controller.
+ //
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mCharacterMappableHandle );
+
+ CharacterMappable* pCharacterMappable = mpAvatarInputManager->mpInCarCharacterMappable;
+ CameraRelativeCharacterController* pCharacterController = mpAvatarInputManager->mpCameraRelativeCharacterController;
+ pCharacterController->Create( mpCharacter, pCharacterMappable );
+
+ mpAvatarInputManager->mCharacterMappableHandle = InputManager::GetInstance()->RegisterMappable( mControllerId, pCharacterMappable );
+ mpCharacter->SetController( pCharacterController );
+
+ // new:
+ // for changing the physics/collision representation if applicabble
+ mpVehicle->SetInCarSimState();
+
+ mpVehicle->BeefUpHitPointsOnTrafficCarsWhenUserDriving();
+
+
+}
+
+void Avatar::SetCameraTargetToVehicle( bool cut )
+{
+ // Set the camera target.
+ //
+ GetSuperCamManager()->GetSCC( mPlayerId )->SetTarget( mpVehicle );
+
+ // Select the follow cam.
+ //
+ GetSuperCamManager()->GetSCC( mPlayerId )->SelectSuperCam( SuperCam::FOLLOW_CAM, cut ? (SuperCamCentral::CUT|SuperCamCentral::FORCE ) : 0, cut ? 0 : 7000 );
+}
+
+void Avatar::GetIntoVehicleStart( Vehicle* pVehicle)
+{
+ SetVehicle( pVehicle );
+
+ // check to see if the target vehicle is currently controlled by traffic
+ // if it is, we need to work a little harder to carjack it :-)
+ if(pVehicle->GetLocomotionType() == VL_TRAFFIC)
+ {
+ pVehicle->SetLocomotion(VL_PHYSICS);
+ pVehicle->mHijackedByUser = true;
+ }
+
+ if(GetSuperCamManager()->GetSCC( mPlayerId )->GetPreferredFollowCam() != SuperCam::BUMPER_CAM)
+ {
+ SetCameraTargetToVehicle( );
+ }
+
+ CGuiScreenMultiHud* currentHUD = GetCurrentHud();
+ if( currentHUD != NULL )
+ {
+ currentHUD->GetHudMap( mPlayerId )->UnregisterIcon( 0 );
+ currentHUD->GetHudMap( mPlayerId )->RegisterIcon( HudMapIcon::ICON_PLAYER,
+ rmt::Vector( 0, 0, 0 ),
+ pVehicle );
+ }
+
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mCharacterMappableHandle );
+
+
+}
+
+void Avatar::GetIntoVehicleEnd( Vehicle* pVehicle)
+{
+ SetCameraTargetToVehicle( );
+
+ SetInCarController( );
+ // this call is to record the rest seating positions of the driver and passenger
+ pVehicle->RecordRestSeatingPositionsOnEntry();
+}
+
+/*
+==============================================================================
+Avatar::GetOutOfVehicle
+==============================================================================
+Description: Pseudo code
+ 2. Set Controller for Vehicle to NULL ( or AI controller )
+ 3. Set Vehicle member data to NULL.
+ 4. Unregister controller with controller system.
+
+Parameters: ( Vehicle* pVehicle)
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::GetOutOfVehicleStart( Vehicle* pVehicle)
+{
+ // First things, detach the in car character controller.
+ //
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mCharacterMappableHandle );
+
+ // Detach the vehicle controller.
+ UnRegisterMappableHandle( mControllerId, mpAvatarInputManager->mVehicleMappableHandle );
+#ifdef RAD_PS2
+ if ( mpAvatarInputManager->mVehicleMappableHandleWheel0 != -1 )
+ {
+ UnRegisterMappableHandle( Input::USB0, mpAvatarInputManager->mVehicleMappableHandleWheel0 );
+ GetInputManager()->SetRumbleForDevice( Input::USB0, false );
+ }
+
+ if ( mpAvatarInputManager->mVehicleMappableHandleWheel1 != -1 )
+ {
+ UnRegisterMappableHandle( Input::USB1, mpAvatarInputManager->mVehicleMappableHandleWheel1 );
+ GetInputManager()->SetRumbleForDevice( Input::USB1, false );
+ }
+#endif
+
+ // attach the on-foot controller
+ // we do this early (don't wait for end of get out of car) so that we can abort \
+ // the door close if there is movement)
+ SetOutOfCarController();
+
+ int vehicleId = GetVehicleCentral()->GetVehicleId( pVehicle, false );
+
+ if ( !GetGameplayManager()->mIsDemo && (vehicleId != -1))
+ {
+ GetVehicleCentral( )->SetVehicleController( vehicleId, 0 );
+ }
+
+ if(GetSuperCamManager()->GetSCC( mPlayerId )->GetPreferredFollowCam() == SuperCam::BUMPER_CAM)
+ {
+ SetCameraTargetToCharacter();
+ }
+}
+
+void Avatar::SetOutOfCarController( void )
+{
+ if( GetVehicle() )
+ {
+ GetVehicle()->mGeometryVehicle->FadeRoof( false );
+
+ if( !TrafficManager::GetInstance()->IsVehicleTrafficVehicle( GetVehicle() ) )
+ {
+ GetVehicle()->mGeometryVehicle->EnableLights( false );
+ }
+
+ GetVehicle()->SetOutOfCarSimState();
+
+ GetVehicle()->SetUserDrivingCar( false );
+ }
+
+ // Then, make a new character controller.
+ //
+ CharacterMappable* pCharacterMappable = mpAvatarInputManager->mpBipedCharacterMappable;
+ CameraRelativeCharacterController* pCharacterController = mpAvatarInputManager->mpCameraRelativeCharacterController;
+ pCharacterController->Create( mpCharacter, pCharacterMappable );
+
+ // Register the new character controller.
+ //
+ mpAvatarInputManager->mCharacterMappableHandle = InputManager::GetInstance()->RegisterMappable( mControllerId, pCharacterMappable );
+ mpCharacter->SetController( pCharacterController );
+
+ pCharacterController->SetCamera( GetSuperCamManager()->GetSCC( mPlayerId )->GetCamera() );
+
+ GetInputManager()->SetRumbleForDevice( mControllerId, false );
+}
+
+void Avatar::SetCameraTargetToCharacter( bool cut )
+{
+ // Set the camera target.
+ //
+ GetSuperCamManager()->GetSCC( mPlayerId )->SetTarget( mpCharacter->GetTarget( ) );
+ // Select the follow cam.
+ //
+#ifdef RAD_WIN32
+ GetSuperCamManager()->GetSCC( mPlayerId )->SelectSuperCam( SuperCam::ON_FOOT_CAM, cut ? SuperCamCentral::CUT : 0 );
+#else
+ GetSuperCamManager()->GetSCC( mPlayerId )->SelectSuperCam( SuperCam::WALKER_CAM, cut ? SuperCamCentral::CUT : 0 );
+#endif
+}
+
+void Avatar::GetOutOfVehicleEnd( Vehicle* pVehicle)
+{
+ pVehicle->DrawVehicle(true); // just in case it was in bumper cam
+
+ SetCameraTargetToCharacter();
+
+ SetVehicle( 0 );
+
+ CGuiScreenMultiHud* currentHUD = GetCurrentHud();
+ if( currentHUD != NULL )
+ {
+ currentHUD->GetHudMap( mPlayerId )->UnregisterIcon( 0 );
+ currentHUD->GetHudMap( mPlayerId )->RegisterIcon( HudMapIcon::ICON_PLAYER,
+ rmt::Vector( 0, 0, 0 ),
+ mpCharacter->GetTarget() );
+ }
+}
+ /*
+==============================================================================
+Avatar::IsInCar
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+bool Avatar::IsInCar( void ) const
+{
+ rAssert( mpCharacter );
+ return mpCharacter->IsInCar( ) && (mpVehicle != NULL);
+}
+
+
+const void Avatar::GetPosition( rmt::Vector& pos ) const
+{
+ if( this->IsInCar() )
+ {
+ mpVehicle->GetPosition( &pos );
+ }
+ else
+ {
+ mpCharacter->GetPosition( pos );
+ }
+}
+
+const void Avatar::GetHeading( rmt::Vector& irHeading ) const
+{
+ if( this->IsInCar() )
+ {
+ mpVehicle->GetHeading( &irHeading );
+ }
+ else
+ {
+ mpCharacter->GetFacing( irHeading );
+ }
+}
+
+const void Avatar::GetNormalizedHeadingSafe( rmt::Vector& heading ) const
+{
+ if( this->IsInCar() )
+ {
+ assert( mpVehicle != NULL );
+ mpVehicle->GetVelocity( &heading );
+ float speedMps = mpVehicle->GetSpeedKmh() * KPH_2_MPS;
+ if( rmt::Fabs(speedMps) < 0.00001f )
+ {
+ heading.Set( 0.0f, 0.0f, 0.0f );
+ }
+ else
+ {
+ heading.Scale( 1.0f/speedMps );
+ }
+ }
+ else
+ {
+ // for characters, facing is same as heading (no skidding/sliding)
+ // I hope...
+ assert( mpCharacter != NULL );
+ mpCharacter->GetFacing( heading );
+ }
+ assert( rmt::Epsilon( heading.MagnitudeSqr(), 1.0f, 0.00001f ) ||
+ rmt::Epsilon( heading.MagnitudeSqr(), 0.0f, 0.00001f ) );
+}
+
+const void Avatar::GetVelocity( rmt::Vector& vel ) const
+{
+ if( this->IsInCar() )
+ {
+ assert( mpVehicle != NULL );
+ mpVehicle->GetVelocity( &vel );
+ }
+ else
+ {
+ // Not sure here if the Choreo puppet's velocity value
+ // (which is what Character::GetVelocity() retrieves)
+ // is a good one. So we just track our own.
+ assert( mpCharacter != NULL );
+ mpCharacter->GetVelocity( vel );
+ }
+}
+
+const float Avatar::GetSpeedMps() const
+{
+ if( this->IsInCar() )
+ {
+ assert( mpVehicle != NULL );
+ return mpVehicle->GetSpeedKmh() * KPH_2_MPS;
+ }
+ else
+ {
+ assert( mpCharacter != NULL );
+ return mpCharacter->GetSpeed();
+ }
+
+}
+
+void Avatar::OnCheatEntered( eCheatID cheatID, bool isEnabled )
+{
+ if( cheatID == CHEAT_ID_SHOW_AVATAR_POSITION )
+ {
+ s_displayCoordinates = isEnabled;
+ }
+}
+
+/*
+==============================================================================
+Avatar::Update
+==============================================================================
+Description: Comment
+
+Parameters: (float dt)
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::Update(float dt)
+{
+ mHasBeenUpdatedThisFrame = false;
+#ifndef FINAL
+ char buffy[256];
+ rmt::Vector posn;
+ const pddiColour YELLOW(255,255,0);
+
+ //
+ // Coordinate display cheat
+ //
+ if( s_displayCoordinates )
+ {
+ if( mpCharacter != NULL )
+ {
+ GetPosition( posn );
+ }
+ else
+ {
+ posn.Set( 0.0f, 0.0f, 0.0f );
+ }
+ sprintf( buffy, "Avatar %d position: %f %f %f", mPlayerId, posn.x, posn.y, posn.z );
+ p3d::pddi->DrawString( buffy, 40, 40 + ( mPlayerId * 20), YELLOW );
+ }
+#endif
+}
+
+void Avatar::GetLastPathInfo(
+ RoadManager::PathElement& oElem,
+ RoadSegment*& oSeg,
+ float& oSegT,
+ float& oRoadT )
+{
+ oElem.elem = NULL;
+ oSeg = NULL;
+ oSegT = 0.0f;
+ oRoadT = 0.0f;
+
+ // don't do it again if already done
+ if( !mHasBeenUpdatedThisFrame )
+ {
+ mHasBeenUpdatedThisFrame = true;
+ //////////////////////////////////////////////////////////////////////
+ // Update the Avatar's closest path entities in the world.
+
+ rmt::Vector myPos;
+ GetPosition( myPos );
+
+
+ RoadSegment* seg = NULL;
+ float segT = 0.0f;
+ float roadT = 0.0f;
+ RoadManager::PathElement elem;
+ elem.elem = NULL;
+
+ /*
+ RoadManager::PathfindingOptions options = RoadManager::PO_SEARCH_SHORTCUTS;
+ RoadManager::GetInstance()->FindClosestPathElement(
+ pos, 100.0f, options, tmpPathElem, tmpSeg, tmpSegT, tmpRoadT );
+ */
+
+ // Special initial case:
+ // If my last path info is completely unset (started off the road)
+ // then we fetch the closest path element using Devin's thing
+ if( mLastPathElement.elem == NULL && mLastRoadSegment == NULL )
+ {
+ RoadSegment* closestSeg = NULL;
+ float dummy;
+ GetIntersectManager()->FindClosestAnyRoad( myPos, 100.0f, closestSeg, dummy );
+
+ seg = (RoadSegment*) closestSeg;
+ segT = RoadManager::DetermineSegmentT( myPos, seg );
+ roadT = RoadManager::DetermineRoadT( seg, segT );
+ elem.elem = seg->GetRoad();
+ elem.type = RoadManager::ET_NORMALROAD;
+ }
+ else
+ {
+ VehicleAI::FindClosestPathElement( myPos, elem, seg, segT, roadT, true );
+ }
+
+ if( elem.elem == NULL )
+ {
+ GetEventManager()->TriggerEvent( EVENT_AVATAR_OFF_ROAD );
+
+ oElem = mLastPathElement;
+ oSeg = mLastRoadSegment;
+ oSegT = mLastRoadSegmentT;
+ oRoadT = mLastRoadT;
+ return;
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_AVATAR_ON_ROAD );
+ }
+
+ rAssert( elem.elem != NULL );
+
+ mLastPathElement = elem;
+ if( seg )
+ {
+ mLastRoadSegment = seg;
+ mLastRoadSegmentT = segT;
+ mLastRoadT = roadT;
+ }
+
+ /*
+ //
+ // true means not to ignore the path element we just found
+ // default is true because we could have lastpathelement == elem
+ // but we could be on different segments...
+ //
+ bool OK = true;
+
+ if( mLastPathElement != elem )
+ {
+ if( mLastPathElement.elem == NULL )
+ {
+ OK = true;
+ }
+ else
+ {
+ Road* lastRoad = NULL;
+ Intersection* lastInt = NULL;
+ Road* currRoad = NULL;
+ Intersection* currInt = NULL;
+
+ // temporary type: 0 = intersection, 1 = road, 2 = shortcut
+
+ // Figure out what type is LAST
+ int lastElemType = 0;
+ if( mLastPathElement.type == RoadManager::ET_NORMALROAD )
+ {
+ lastRoad = (Road*) mLastPathElement.elem;
+ lastElemType++;
+
+ if( lastRoad->GetShortCut() )
+ {
+ lastElemType++;
+ }
+ }
+ else
+ {
+ lastInt = (Intersection*) mLastPathElement.elem;
+ }
+
+ // Figure out what type is CURR
+ int currElemType = 0;
+ if( elem.type == RoadManager::ET_NORMALROAD )
+ {
+ currRoad = (Road*) elem.elem;
+ currElemType++;
+
+ if( currRoad->GetShortCut() )
+ {
+ currElemType++;
+ }
+ }
+ else
+ {
+ currInt = (Intersection*) elem.elem;
+ }
+
+ //
+ // Start resolving ...
+ ///////////////
+ // LAST: Intersection
+ if( lastElemType == 0 )
+ {
+ rAssert( lastInt );
+ if( currElemType == 2 )// CURR: shortcut
+ {
+ rAssert( currRoad );
+ if( lastInt == (Intersection*) currRoad->GetSourceIntersection() ||
+ lastInt == (Intersection*) currRoad->GetDestinationIntersection() )
+ {
+ OK = true;
+ }
+ else
+ {
+ OK = false;
+ }
+ }
+ else
+ {
+ OK = true;
+ }
+ }
+ /////////////////
+ // LAST: Normal road
+ else if( lastElemType == 1 )
+ {
+ rAssert( lastRoad );
+
+ if( currElemType == 0 ) // CURR: intersection
+ {
+ rAssert( currInt );
+ if( currInt == (Intersection*)lastRoad->GetSourceIntersection() ||
+ currInt == (Intersection*)lastRoad->GetDestinationIntersection() )
+ {
+ OK = true;
+ }
+ else
+ {
+ OK = false;
+ }
+ }
+ else if( currElemType == 1 ) // CURR: normal road
+ {
+ rAssert( currRoad );
+ OK = true;
+ }
+ else // CURR: shortcut
+ {
+ rAssert( currRoad );
+ OK = false;
+ }
+ }
+ ////////////////
+ // LAST: Shortcut
+ else
+ {
+ rAssert( lastRoad );
+
+ if( currElemType == 0 ) // CURR: intersection
+ {
+ rAssert( currInt );
+
+ // NOTE:
+ // This is the fix to Level 3 Mission 7 where the limo drives over the where the shortcuts
+ // and normal road segments merge near the squidport.... This was where its last good element
+ // was found... It then transitted onto the intersection, but rejected it because back then
+ // we only tested for the destination intersection... So it was stuck there for a long time
+ // but didn't realize it until it hit the next waypoint (wayy... over by the dam) and decided
+ // it needed to turn back.
+ if( currInt == (Intersection*)lastRoad->GetDestinationIntersection() ||
+ currInt == (Intersection*)lastRoad->GetSourceIntersection() )
+ {
+ OK = true;
+ }
+ else
+ {
+ OK = false;
+ }
+ }
+ else if( currElemType == 1 ) // CURR: normal road
+ {
+ rAssert( currRoad );
+ OK = false;
+ }
+ else // CURR: shortcut
+ {
+ rAssert( currRoad );
+ OK = false;
+ }
+ }
+ }
+ }
+
+ if( OK )
+ {
+ mLastPathElement = elem;
+ if( seg )
+ {
+ if( mLastRoadSegment != seg )
+ {
+ mLastRoadSegment = seg;
+ }
+ mLastRoadSegmentT = segT;
+ mLastRoadT = roadT;
+ }
+ }
+ */
+ }
+
+ oElem = mLastPathElement;
+ oSeg = (RoadSegment*) mLastRoadSegment;
+ oSegT = mLastRoadSegmentT;
+ oRoadT = mLastRoadT;
+}
+
+void Avatar::GetRaceInfo( float& distToCurrCollectible, int& currCollectible, int& numLapsCompleted )
+{
+ distToCurrCollectible = mDistToCurrentCollectible;
+ currCollectible = mCurrentCollectible;
+ numLapsCompleted = mNumLapsCompleted;
+}
+
+void Avatar::SetRaceInfo( float distToCurrCollectible, int currCollectible, int numLapsCompleted )
+{
+ mDistToCurrentCollectible = distToCurrCollectible;
+ mCurrentCollectible = currCollectible;
+ mNumLapsCompleted = numLapsCompleted;
+}
+
+/*
+==============================================================================
+Avatar::SetCamera
+==============================================================================
+Description: Controller needs to be aware of camera changes so that
+ direction can be normalized.
+
+Parameters: ( tCamera* pCamera )
+
+Return: void
+
+=============================================================================
+*/
+void Avatar::SetCamera( tCamera* pCamera )
+{
+ rAssert( pCamera != 0 );
+ mpAvatarInputManager->mpCameraRelativeCharacterController->SetCamera( pCamera );
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/worldsim/avatar.h b/game/code/worldsim/avatar.h
new file mode 100644
index 0000000..bf3fd05
--- /dev/null
+++ b/game/code/worldsim/avatar.h
@@ -0,0 +1,158 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: avatar.h
+//
+// Description: Avatar Class declaration.
+//
+// History: 4/3/2002 + Created -- TBJ
+//
+//=============================================================================
+
+#ifndef AVATAR_H
+#define AVATAR_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <radmath/radmath.hpp>
+#include <cheats/cheatinputsystem.h>
+#include <roads/roadmanager.h>
+
+//========================================
+// Forward References
+//========================================
+class Character;
+class Vehicle;
+class VehicleController;
+struct AvatarInputManager;
+class tCamera;
+
+//=============================================================================
+//
+// Synopsis: The Avatar
+//
+//=============================================================================
+
+class Avatar : public ICheatEnteredCallback
+{
+// METHODS:
+public:
+ Avatar();
+ ~Avatar();
+
+ void Destroy( void );
+
+ int GetControllerId( void ) const;
+ void SetControllerId ( int id );
+
+ void SetPlayerId( int id ) { mPlayerId = id; }
+ int GetPlayerId() const { return mPlayerId; }
+
+ Character* GetCharacter( void ) const;
+ void SetCharacter( Character* pCharacter );
+
+ Vehicle* GetVehicle( void ) const;
+ void SetVehicle( Vehicle* pVehicle );
+
+ void GetIntoVehicleStart( Vehicle* pVehicle );
+ void GetIntoVehicleEnd( Vehicle* pVehicle );
+ void GetOutOfVehicleStart( Vehicle* pVehicle );
+ void GetOutOfVehicleEnd( Vehicle* pVehicle );
+
+ void SetInCarController( void );
+ void SetOutOfCarController( void );
+ void SetCameraTargetToCharacter( bool cut = false );
+ void SetCameraTargetToVehicle( bool cut = false );
+
+ void SetCamera( tCamera* pCamera );
+
+ const void GetPosition( rmt::Vector& pos ) const;
+ const void GetHeading( rmt::Vector& irHeading ) const;
+
+ // *** //
+ const void GetNormalizedHeadingSafe( rmt::Vector& heading ) const;
+ const void GetVelocity( rmt::Vector& vel ) const;
+ const float GetSpeedMps() const;
+ // *** //
+
+ bool IsInCar( void ) const;
+
+ void PreSubstepUpdate();
+ void Update(float dt);
+ void PreCollisionPrep();
+
+ void OnCheatEntered( eCheatID cheatID, bool isEnabled );
+
+ void GetLastPathInfo(
+ RoadManager::PathElement& elem,
+ RoadSegment*& seg,
+ float& segT,
+ float& roadT );
+
+ void GetRaceInfo(
+ float& distToCurrCollectible,
+ int& currCollectible,
+ int& numLapsCompleted );
+
+ void SetRaceInfo(
+ float distToCurrCollectible,
+ int currCollectible,
+ int numLapsCompleted );
+
+//MEMBERS
+public:
+
+
+private:
+
+ ///////////////// PATH INFO /////////////////////
+ bool mHasBeenUpdatedThisFrame;
+ RoadManager::PathElement mLastPathElement;
+ RoadSegment* mLastRoadSegment;
+ float mLastRoadSegmentT;
+ float mLastRoadT;
+
+ ///////////////// RACE DATA ///////////////////
+ // we keep pieces of the race data in this class
+ // because catch-up logic will need to use them
+ float mDistToCurrentCollectible;
+ int mCurrentCollectible;
+ int mNumLapsCompleted;
+
+
+ //Prevent wasteful constructor creation.
+ //
+ Avatar( const Avatar& avatar );
+ Avatar& operator=( const Avatar& avatar );
+
+ // Data.
+ //
+ static const int INVALID_CONTROLLER = -1;
+ int mControllerId;
+
+ int mPlayerId;
+
+ Character* mpCharacter;
+
+ // NULL if not in any vehicle, when IsInCar, this is set to that car
+ Vehicle* mpVehicle;
+
+ AvatarInputManager* mpAvatarInputManager;
+
+ static bool s_displayCoordinates;
+
+ /*
+ // TRUE if neither directly on a NON-shortcut road segment nor
+ // in an intersection
+ bool mOffRoad;
+
+ // number of seconds mOffRoad has been true or false
+ float mSecondsOffOrOnRoad;
+ */
+};
+
+
+
+#endif //AVATAR_H
diff --git a/game/code/worldsim/avatarmanager.cpp b/game/code/worldsim/avatarmanager.cpp
new file mode 100644
index 0000000..3f8dcbe
--- /dev/null
+++ b/game/code/worldsim/avatarmanager.cpp
@@ -0,0 +1,511 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implementation of class AvatarManager
+//
+// History: 4/3/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <memory/srrmemory.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+#include <mission/gameplaymanager.h>
+#include <events/eventenum.h>
+#include <events/eventmanager.h>
+#include <meta/locatorevents.h>
+#include <meta/eventlocator.h>
+#include <presentation/gui/guisystem.h>
+#include <presentation/gui/guiwindow.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreeniriswipe.h>
+#include <input/inputmanager.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercam.h>
+
+#include <gameflow/gameflow.h>
+#include <contexts/context.h>
+
+#include <ai/sequencer/actioncontroller.h>
+#include <supersprint/supersprintmanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+AvatarManager* AvatarManager::spAvatarManager = 0;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+AvatarManager* AvatarManager::CreateInstance( void )
+{
+ rAssertMsg( spAvatarManager == 0, "AvatarManager already created.\n" );
+ spAvatarManager = new ( GMA_PERSISTENT ) AvatarManager;
+ return AvatarManager::GetInstance();
+}
+
+AvatarManager* AvatarManager::GetInstance( void )
+{
+ rAssertMsg( spAvatarManager != 0, "AvatarManager has not been created yet.\n" );
+ return spAvatarManager;
+}
+
+
+void AvatarManager::DestroyInstance( void )
+{
+ rAssertMsg( spAvatarManager != 0, "AvatarManager has not been created.\n" );
+ delete ( GMA_PERSISTENT, spAvatarManager );
+}
+//==============================================================================
+// AvatarManager::AvatarManager
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AvatarManager::AvatarManager()
+:
+mNumAvatars( 0 )
+{
+}
+
+//==============================================================================
+// AvatarManager::~AvatarManager
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+AvatarManager::~AvatarManager()
+{
+ Destroy( );
+}
+
+void AvatarManager::Destroy( void )
+{
+ int i;
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ mAvatarArray[ i ]->Destroy();
+ delete mAvatarArray[i];
+ }
+ mNumAvatars = 0;
+
+ GetEventManager()->RemoveAll( this );
+}
+
+
+/*
+==============================================================================
+AvatarManager::EnterGame
+==============================================================================
+Description: This will be called to create the avatars just before the
+ game session begins.
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void AvatarManager::EnterGame( void )
+{
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_START );
+ GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_START );
+ GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_CAMERA_CHANGE );
+
+ int i;
+ for (i = 0; i < MAX_AVATARS; i++)
+ {
+ mAvatarArray[i] = 0;
+ }
+ mNumAvatars = GetGameplayManager()->GetNumPlayers();
+
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ mAvatarArray[i] = new Avatar;
+
+ Vehicle* pVehicle = GetVehicleCentral()->GetVehicle( i );// GetNewVehicle();
+ int controllerID = GetInputManager()->GetControllerIDforPlayer( i );
+ rWarningMsg( controllerID != -1, "No controller ID registered for player avatar!" );
+
+ mAvatarArray[ i ]->SetControllerId( controllerID );
+ mAvatarArray[ i ]->SetPlayerId( i );
+ Character* pCharacter = GetCharacterManager( )->GetCharacter( i );
+ rAssert( pCharacter );
+ mAvatarArray[ i ]->SetCharacter( pCharacter );
+ rmt::Vector characterPosition;
+
+ // Single player set up.
+ //
+ pCharacter->SetInCar( false );
+ mAvatarArray[ i ]->SetVehicle( (Vehicle*)0 );
+ mAvatarArray[ i ]->SetCameraTargetToCharacter( true );
+ mAvatarArray[ i ]->SetOutOfCarController( );
+
+ //Chuck Adding support for loading of the last used skin for the level
+
+ if ( pVehicle )
+ {
+ // Hack. MS8.
+ // Position the character near the vehicle.
+ //
+ characterPosition.Set( 3.1f, 0.0f, 0.0f );
+
+ rmt::Matrix carToWorld = pVehicle->GetTransform();
+ characterPosition.Transform( carToWorld );
+ pCharacter->SetPosition( characterPosition );
+ pCharacter->UpdateTransformToLoco();
+ }
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+}
+
+void AvatarManager::ExitGame ()
+{
+ Destroy ();
+}
+
+void AvatarManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_GETINTOVEHICLE_START:
+ {
+ Character* pCharacter = static_cast<Character*>( pEventData );
+ Avatar* pAvatar = FindAvatarForCharacter( pCharacter );
+ if ( pAvatar )
+ {
+ Vehicle* pVehicle = pCharacter->GetTargetVehicle();
+ pAvatar->GetIntoVehicleStart( pVehicle );
+ }
+ break;
+ }
+ case EVENT_GETINTOVEHICLE_END:
+ {
+ Character* pCharacter = static_cast<Character*>( pEventData );
+ Avatar* pAvatar = FindAvatarForCharacter( pCharacter );
+ if ( pAvatar )
+ {
+ Vehicle* pVehicle = pCharacter->GetTargetVehicle();
+ pAvatar->GetIntoVehicleEnd( pVehicle );
+ }
+ break;
+ }
+ case EVENT_GETOUTOFVEHICLE_START:
+ {
+ Character* pCharacter = static_cast<Character*>( pEventData );
+ Avatar* pAvatar = FindAvatarForCharacter( pCharacter );
+ if ( pAvatar )
+ {
+ Vehicle* pVehicle = pAvatar->GetVehicle();
+ pAvatar->GetOutOfVehicleStart( pVehicle );
+ }
+ break;
+ }
+ case EVENT_GETOUTOFVEHICLE_END:
+ {
+ Character* pCharacter = static_cast<Character*>( pEventData );
+ Avatar* pAvatar = FindAvatarForCharacter( pCharacter );
+ if ( pAvatar )
+ {
+ Vehicle* pVehicle = pAvatar->GetVehicle();
+ pAvatar->GetOutOfVehicleEnd( pVehicle );
+ }
+ break;
+ }
+ case EVENT_CAMERA_CHANGE:
+ {
+ SuperCam* sc = static_cast<SuperCam*>(pEventData);
+
+ int playerID = 0; // normal game defaults player to zero
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_SUPERSPRINT ||
+ GetGameFlow()->GetCurrentContext() == CONTEXT_SUPERSPRINT )
+ {
+ playerID = SuperSprintManager::GetInstance()->GetOnlyHumanPlayerID();
+ /*
+ if( GetAvatarForPlayer( playerID )->GetPlayerId() != playerID )
+ {
+ break;
+ }
+ */
+ }
+
+ // can be -1 if in supersprint and more than 1 human is participating...
+ if( playerID == -1 )
+ {
+ break;
+ }
+
+ if ( sc == GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam() )
+ {
+ //Find the vehicle owned by this car.
+ //Vehicle* car = GetGameplayManager()->GetCurrentVehicle();
+ Vehicle* car = GetAvatarForPlayer( playerID )->GetVehicle();
+
+
+ if ( car )
+ {
+ if ( sc->GetType() == SuperCam::BUMPER_CAM )
+ {
+ //Only stop rendering for bumpercams.
+ car->DrawVehicle( false );
+ }
+ else
+ {
+ car->DrawVehicle( true );
+ }
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+Avatar* AvatarManager::GetAvatarForPlayer( int playerId )
+{
+ if( playerId < this->mNumAvatars )
+ {
+ return mAvatarArray[ playerId ];
+ }
+
+ return( NULL );
+}
+
+//=============================================================================
+// AvatarManager::PutCharacterInCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter, Vehicle* pVehicle )
+//
+// Return: void
+//
+//=============================================================================
+void AvatarManager::PutCharacterInCar( Character* pCharacter, Vehicle* pVehicle )
+{
+ rAssert( pCharacter != NULL );
+ rAssert( pVehicle != NULL );
+
+ int i;
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ if ( mAvatarArray[ i ]->GetCharacter( ) == pCharacter )
+ {
+ if(pCharacter->IsInCar() && (pCharacter->GetTargetVehicle() == pVehicle))
+ {
+ return;
+ }
+
+ pCharacter->GetActionController()->Clear();
+
+ pCharacter->SetInCar( true );
+ pCharacter->UpdateTransformToInCar( );
+ pCharacter->SetTargetVehicle( pVehicle );
+
+ // transit the vehicle into VL_PHYSICS!
+ if(pVehicle->GetLocomotionType() == VL_TRAFFIC)
+ {
+ pVehicle->SetLocomotion(VL_PHYSICS);
+ pVehicle->mHijackedByUser = true;
+ }
+
+ mAvatarArray[ i ]->SetVehicle( pVehicle );
+ mAvatarArray[ i ]->SetCameraTargetToVehicle( true ); //CUT the camera
+ mAvatarArray[ i ]->SetInCarController( );
+
+ pCharacter->UpdateTransformToInCar();
+ if(pCharacter->GetStateManager()->GetState() == CharacterAi::INCAR)
+ {
+ pCharacter->GetStateManager()->ResetState();
+ }
+ else
+ {
+ pCharacter->GetStateManager()->SetState<CharacterAi::InCar>();
+ //pCharacter->GetStateManager()->Update( 16 );
+ }
+
+ pCharacter->SetDesiredSpeed(0.0f);
+
+ if ( GetCurrentHud() )
+ {
+ GetCurrentHud()->GetHudMap( i )->UnregisterIcon( 0 );
+ GetCurrentHud()->GetHudMap( i )->RegisterIcon( HudMapIcon::ICON_PLAYER,
+ rmt::Vector( 0, 0, 0 ),
+ pVehicle );
+ }
+
+ return;
+ }
+ }
+}
+
+//=============================================================================
+// AvatarManager::PutCharacterOnGround
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* pCharacter, Vehicle* pVehicle )
+//
+// Return: void
+//
+//=============================================================================
+void AvatarManager::PutCharacterOnGround( Character* pCharacter, Vehicle* pVehicle )
+{
+ rAssert( pCharacter != NULL );
+ rAssert( pVehicle != NULL );
+
+ int i;
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ if ( mAvatarArray[ i ]->GetCharacter( ) == pCharacter )
+ {
+ mAvatarArray[ i ]->GetOutOfVehicleStart( pVehicle );
+ mAvatarArray[ i ]->GetOutOfVehicleEnd( pVehicle );
+
+ pCharacter->SetInCar( false );
+
+ pCharacter->UpdateTransformToLoco();
+ pCharacter->GetStateManager()->SetState<CharacterAi::Loco>();
+ pCharacter->GetStateManager()->Update( 16 );
+
+ return;
+ }
+ }
+}
+
+/*
+==============================================================================
+AvatarManager::Update
+==============================================================================
+Description: Comment
+
+Parameters: (float dt)
+
+Return: void
+
+=============================================================================
+*/
+void AvatarManager::Update(float dt)
+{
+ int i;
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ mAvatarArray[ i ]->Update( dt );
+ }
+}
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+Avatar* AvatarManager::FindAvatarForCharacter( Character* pCharacter )
+{
+ int i;
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ if ( mAvatarArray[ i ]->GetCharacter( ) == pCharacter )
+ {
+ return mAvatarArray[ i ];
+ }
+ }
+ return 0;
+}
+
+
+//=============================================================================
+// AvatarManager::GetAvatarForVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (Vehicle* vehicle)
+//
+// Return: Avatar
+//
+//=============================================================================
+Avatar* AvatarManager::GetAvatarForVehicle(Vehicle* vehicle)
+{
+ int i;
+ for ( i = 0; i < mNumAvatars; i++ )
+ {
+ if (mAvatarArray[i]->GetVehicle() == vehicle)
+ {
+ return mAvatarArray[i];
+ }
+ }
+ return 0;
+}
+
+//=============================================================================
+// AvatarManager::IsAvatarGettingInOrOutOfCar
+//=============================================================================
+// Description: returns true if the chacter for playerId is in a get into or get out of car transition state.
+//
+// Parameters: int playerId
+//
+// Return: bool
+//
+//=============================================================================
+bool AvatarManager::IsAvatarGettingInOrOutOfCar(int playerId)
+{
+ if (
+ GetAvatarForPlayer(0)->GetCharacter()->GetStateManager()->GetState() == CharacterAi::GET_IN
+ ||
+ GetAvatarForPlayer(0)->GetCharacter()->GetStateManager()->GetState() == CharacterAi::GET_OUT
+ )
+
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+
+
diff --git a/game/code/worldsim/avatarmanager.h b/game/code/worldsim/avatarmanager.h
new file mode 100644
index 0000000..c0230e7
--- /dev/null
+++ b/game/code/worldsim/avatarmanager.h
@@ -0,0 +1,87 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: avatarmanager.h
+//
+// Description: Blahblahblah
+//
+// History: 4/3/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef AVATARMANAGER_H
+#define AVATARMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <worldsim/avatar.h>
+#include <events/eventlistener.h>
+
+#include <constants/maxplayers.h>
+
+//========================================
+// Forward References
+//========================================
+
+class Character;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class AvatarManager
+:
+public EventListener
+{
+public:
+ AvatarManager();
+ ~AvatarManager();
+ void Destroy( void );
+
+ static AvatarManager* GetInstance( );
+ static AvatarManager* CreateInstance( );
+ static void DestroyInstance( );
+
+ void EnterGame( void );
+ void ExitGame( void );
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ Avatar* GetAvatarForPlayer( int playerId );
+ Avatar* GetAvatarForVehicleId( int vehicleId );
+
+ Avatar* GetAvatarForVehicle(Vehicle* vehicle);
+
+ Avatar* FindAvatarForCharacter( Character* pCharacter );
+
+ void PutCharacterInCar( Character* pCharacter, Vehicle* pVehicle );
+ void PutCharacterOnGround( Character* pCharacter, Vehicle* pVehicle );
+
+ //chuck returns a true if the players character is in get in car or get out of car transition state.
+ bool IsAvatarGettingInOrOutOfCar(int playerId);
+
+ void Update(float dt);
+
+
+private:
+
+ //Prevent wasteful constructor creation.
+ AvatarManager( const AvatarManager& avatarmanager );
+ AvatarManager& operator=( const AvatarManager& avatarmanager );
+
+ // Data.
+ //
+ static AvatarManager* spAvatarManager;
+
+ static const int MAX_AVATARS = MAX_PLAYERS;
+
+ Avatar* mAvatarArray[ MAX_AVATARS ];
+ int mNumAvatars;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline AvatarManager* GetAvatarManager() { return( AvatarManager::GetInstance() ); }
+
+#endif //AVATARMANAGER_H
diff --git a/game/code/worldsim/character/aicharactercontroller.cpp b/game/code/worldsim/character/aicharactercontroller.cpp
new file mode 100644
index 0000000..71c9cbb
--- /dev/null
+++ b/game/code/worldsim/character/aicharactercontroller.cpp
@@ -0,0 +1,112 @@
+#include <worldsim/character/aicharactercontroller.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <main/game.h>
+
+class Behaviour
+{
+public:
+ virtual rmt::Vector& Tick( Character& me ) = 0;
+};
+
+class Wander
+:
+public Behaviour
+{
+public:
+
+ Wander( void );
+ virtual ~Wander( void );
+
+ rmt::Vector& GetTargetPoint( void );
+ void SetTargetPoint( rmt::Vector& targetPoint );
+
+ rmt::Vector& GetPosition( void );
+ void SetPosition( rmt::Vector& position );
+
+ float GetTargetRadius( void ) const;
+ void SetTargetRadius( float radius );
+
+ float GetSteeringRadius( void ) const;
+ void SetSteeringRadius( float radius );
+
+ rmt::Vector& Tick( Character& me );
+private:
+ rmt::Vector mTargetPoint;
+ rmt::Vector mPosition;
+ float mfTargetCircleRadius;
+ float mfSteeringCircleRadius;
+};
+
+Wander::Wander( void )
+:
+mTargetPoint( 0.0f, 0.0f, -1.0f ),
+mPosition( 0.0f, 0.0f, -1.0f ),
+mfTargetCircleRadius( 1.0f ),
+mfSteeringCircleRadius( 0.3f )
+{
+ mTargetPoint.z = mfTargetCircleRadius;
+}
+
+Wander::~Wander( void )
+{
+}
+
+rmt::Vector& Wander::Tick( Character& me )
+{
+ static rmt::Randomizer r( Game::GetRandomSeed () );
+ static rmt::Vector point( 0.0f, 0.0f, 0.0f );
+
+ point.x = r.FloatSigned( );
+ point.y = 0.0f;
+ point.z = r.FloatSigned( );
+
+ // Make a point on the new circle.
+ //
+ point.Normalize( );
+ point.Scale( mfSteeringCircleRadius );
+
+ // Add to point on circle.
+ //
+ mTargetPoint.Add( point );
+ // Project back to unit circle.
+ //
+ mTargetPoint.Normalize( );
+ // Scale to actual Target Circle.
+ //
+ mTargetPoint.Scale( mfTargetCircleRadius );
+
+ return mTargetPoint;
+}
+
+AICharacterController::AICharacterController( Character* pCharacter, int index, tCamera* pCamera )
+:
+mpBehaviour( 0 )
+{
+}
+
+void AICharacterController::Update( float timeins )
+{
+ static Character* pCharacter = GetCharacterManager( )->GetCharacter( 0 );
+ if ( !mpBehaviour )
+ {
+ mpBehaviour = new Wander;
+ }
+
+ mDirection = mpBehaviour->Tick( *pCharacter );
+}
+
+void AICharacterController::GetDirection( rmt::Vector& outDirection ) const
+{
+ outDirection = mDirection;
+}
+
+float AICharacterController::GetValue( int buttonId ) const
+{
+ return 0.0f;
+}
+
+bool AICharacterController::IsButtonDown( int buttonId ) const
+{
+ return false;
+} \ No newline at end of file
diff --git a/game/code/worldsim/character/aicharactercontroller.h b/game/code/worldsim/character/aicharactercontroller.h
new file mode 100644
index 0000000..af73274
--- /dev/null
+++ b/game/code/worldsim/character/aicharactercontroller.h
@@ -0,0 +1,27 @@
+#ifndef AICHARACTERCONTROLLER_H_
+#define AICHARACTERCONTROLLER_H_
+
+#include <worldsim/character/charactercontroller.h>
+#include <radmath/radmath.hpp>
+
+class Behaviour;
+class Character;
+class tCamera;
+
+class AICharacterController
+:
+public CharacterController
+{
+public:
+ AICharacterController( Character* pCharacter, int index, tCamera* pCamera );
+ void Update( float timeins );
+ void GetDirection( rmt::Vector& outDirection ) const;
+ float GetValue( int buttonId ) const;
+ bool IsButtonDown( int buttonId ) const;
+protected:
+private:
+ rmt::Vector mDirection;
+ Behaviour* mpBehaviour;
+};
+
+#endif // AICHARACTERCONTROLLER_H_ \ No newline at end of file
diff --git a/game/code/worldsim/character/allcharacter.cpp b/game/code/worldsim/character/allcharacter.cpp
new file mode 100644
index 0000000..2fc662d
--- /dev/null
+++ b/game/code/worldsim/character/allcharacter.cpp
@@ -0,0 +1,7 @@
+#include <worldsim/character/aicharactercontroller.cpp>
+#include <worldsim/character/character.cpp>
+#include <worldsim/character/charactercontroller.cpp>
+#include <worldsim/character/charactermanager.cpp>
+#include <worldsim/character/charactermappable.cpp>
+#include <worldsim/character/characterrenderable.cpp>
+#include <worldsim/character/charactertarget.cpp>
diff --git a/game/code/worldsim/character/character.cpp b/game/code/worldsim/character/character.cpp
new file mode 100644
index 0000000..61f82ae
--- /dev/null
+++ b/game/code/worldsim/character/character.cpp
@@ -0,0 +1,5171 @@
+#include <worldsim/character/character.h>
+
+#include <choreo/animation.hpp>
+#include <p3d/anim/skeleton.hpp>
+#include <p3d/matrixstack.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <simcollision/collisionvolume.hpp>
+
+#include <worldsim/character/charactercontroller.h>
+#include <worldsim/character/charactermappable.h>
+#include <worldsim/character/aicharactercontroller.h>
+#include <worldsim/character/characterrenderable.h>
+#include <worldsim/character/charactertarget.h>
+#include <worldsim/character/charactermanager.h>
+
+#include <worldsim/coins/sparkle.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <roads/geometry.h>
+#include <memory/srrmemory.h>
+#include <ai/sequencer/actioncontroller.h>
+#include <ai/sequencer/action.h>
+#include <ai/statemanager.h>
+#include <ai/actionbuttonhandler.h>
+#include <ai/actionbuttonmanager.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/RenderManager/RenderManager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/trafficlocomotion.h>
+#include <worldsim/character/footprint/footprintmanager.h>
+#include <worldsim/harass/chasemanager.h>
+#include <ai/actor/intersectionlist.h>
+
+#include <render/DSG/StatePropDSG.h>
+#include <render/DSG/CollisionEntityDSG.h>
+
+#include <events/eventenum.h>
+#include <events/eventmanager.h>
+
+#include <render/IntersectManager/IntersectManager.h>
+
+#include <camera/supercammanager.h>
+
+#include <mission/gameplaymanager.h>
+
+#include <meta/locator.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/triggervolumetracker.h>
+#include <meta/eventlocator.h>
+
+#include <main/game.h>
+
+#include <sound/soundmanager.h>
+
+#include <interiors/interiormanager.h>
+
+// Sim includes.
+//
+#include <simcommon/simstate.hpp>
+#include <simcommon/physicsproperties.hpp>
+#include <simcommon/simulatedobject.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <simcollision/collisionvolume.hpp>
+#include <simcollision/collisionmanager.hpp>
+#include <simcollision/proximitydetection.hpp>
+#include <simcommon/simmath.hpp>
+
+#include <render/particles/particlemanager.h>
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/RenderLayer.h>
+#include <render/Culling/WorldScene.h>
+#include <render/DSG/StaticPhysDSG.h>
+
+//#ifdef RAD_DEBUG
+ #define DRAW_CHARACTER_COLLISION
+//#endif // RAD_DEBUG
+
+#ifdef DRAW_CHARACTER_COLLISION
+ #include <main/commandlineoptions.h>
+ #include <simcollision/collisiondisplay.hpp>
+ #include <simcommon/simutility.hpp>
+ #include <simcollision/collisionvolume.hpp>
+ #include <simcollision/collisionobject.hpp>
+#endif //DRAW_CHARACTER_COLLISION
+
+// NPC includes
+//
+#include <input/controller.h>
+#include <input/inputmanager.h>
+#include <worldsim/vehiclecentral.h>
+
+#include <debug/profiler.h>
+
+#include <presentation/presentation.h>
+#include <presentation/presentationanimator.h>
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+
+#ifdef RAD_WIN32
+#include <camera/pccam.h>
+#include <input/usercontrollerwin32.h>
+#include <input/mouse.h>
+#include <input/realcontroller.h>
+#include <camera/supercam.h>
+#include <camera/supercamcontroller.h>
+#endif
+
+const static rmt::Vector vUp( 0.0f, 1.0f, 0.0f );
+
+const float KICK_ANGLE = 45;
+
+static int s_IntersectFrame;
+
+class AmbientDialogueButton : public ActionButton::ButtonHandler
+{
+public:
+ AmbientDialogueButton(Character* c) : mpCharacter(c)
+ {
+ }
+
+ bool OnButtonPressed( Character* pCharacter, Sequencer* pSeq )
+ {
+ //
+ // Send a couple of sound events for dialog.
+ //
+ if( SoundManager::IsFoodCharacter( mpCharacter ) )
+ {
+ GetEventManager()->TriggerEvent( EVENT_AMBIENT_ASKFOOD );
+ GetEventManager()->TriggerEvent( EVENT_AMBIENT_FOODREPLY, mpCharacter );
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_AMBIENT_GREETING );
+ GetEventManager()->TriggerEvent( EVENT_AMBIENT_RESPONSE, mpCharacter );
+ }
+ return true;
+ }
+
+protected:
+ Character* mpCharacter;
+};
+
+class AmbientDialogueTrigger : public SphereTriggerVolume
+{
+public:
+
+ AmbientDialogueTrigger(Character* c, float radius) : SphereTriggerVolume(rmt::Vector(0,0,0), radius), mpCharacter(c)
+ {
+ SetLocator(new TriggerLocator);
+ GetLocator()->SetNumTriggers(1);
+ GetLocator()->AddTriggerVolume(this);
+ GetLocator()->AddRef();
+ mButton = new AmbientDialogueButton(c);
+ mButton->AddRef();
+ }
+
+ ~AmbientDialogueTrigger()
+ {
+ tRefCounted::Release(mButton);
+ }
+
+ void ClearLocator(void)
+ {
+ GetLocator()->Release();
+ }
+ void Trigger( unsigned int playerID, bool bActive )
+ {
+ if(bActive)
+ {
+ Character* character = GetAvatarManager()->GetAvatarForPlayer(playerID)->GetCharacter();
+ if ( character )
+ {
+ character->AddActionButtonHandler(mButton);
+ }
+ }
+ else
+ {
+ Character* character = GetAvatarManager()->GetAvatarForPlayer(playerID)->GetCharacter();
+ if ( character )
+ {
+ character->RemoveActionButtonHandler(mButton);
+ }
+ }
+ }
+
+protected:
+ Character* mpCharacter;
+ AmbientDialogueButton* mButton;
+};
+
+/*
+==============================================================================
+NPCharacter::NPCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: NPCharacter
+
+=============================================================================
+*/
+NPCharacter::NPCharacter( void )
+:
+Character( ),
+mMappableHandle( Input::INVALID_CONTROLLERID )
+{
+MEMTRACK_PUSH_GROUP( "NPCCharacter" );
+
+ mIsNPC = true;
+ SetInSubstep( false ); // initially false
+
+ /***
+ CharacterMappable* pCharacterMappable = new (GMA_PERSISTENT) BipedCharacterMappable;
+
+ PhysicalController* pController = new (GMA_PERSISTENT) PhysicalController;
+ this->SetController( pController );
+
+ pController->SetCharacterMappable( pCharacterMappable );
+ pCharacterMappable->SetCharacterController( pController );
+
+ mMappableHandle = InputManager::GetInstance()->RegisterMappable( 1, pCharacterMappable );
+ ***/
+ this->SetController( new(GMA_LEVEL_OTHER) NPCController );
+ this->GetController()->SetCharacter( this );
+MEMTRACK_POP_GROUP( "NPCCharacter" );
+}
+/*
+==============================================================================
+NPCharacter::~NPCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: NPCharacter
+
+=============================================================================
+*/
+NPCharacter::~NPCharacter( void )
+{
+ // if we don't clear this here, there may be actions cued that
+ // will screw up once we've been destructed
+ if(GetActionController())
+ {
+ GetActionController()->Clear();
+ }
+
+ /***
+ InputManager::GetInstance()->UnregisterMappable( 1, mMappableHandle );
+ ***/
+ mMappableHandle = Input::INVALID_CONTROLLERID;
+}
+
+#ifdef RAD_DEBUG
+ static float timeScale = 1.00f;
+#endif
+
+/*
+==============================================================================
+NPCharacter::OnUpdateRoot
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void NPCharacter::OnUpdateRoot( float timeins )
+{
+ // Intentionall left blank.
+ //
+}
+
+/*
+==============================================================================
+NPCharacter::OnPostSimUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void NPCharacter::OnPostSimUpdate( float timeins )
+{
+ UpdatePuppet( timeins );
+}
+/*
+==============================================================================
+Character::Character
+==============================================================================
+Description: Constructor
+
+Parameters: ( )
+
+Return: na
+
+=============================================================================
+*/
+sim::TArray< sim::RayIntersectionInfo > Character::msIntersectInfo( 64 ); //.ResizeArray( 2 );
+
+Character::Character( )
+:
+mbCollidedWithVehicle( false ),
+mbInAnyonesFrustrum( false ),
+mbSurfing(false),
+mbAllowUnload(true),
+mbIsPlayingIdleAnim(false),
+
+#ifdef RAD_WIN32
+mPCCamFacing( 0 ),
+#endif
+
+mIsNPC( false ),
+
+mGroundPlaneSimState( 0 ),
+mGroundPlaneWallVolume( 0 ),
+mCollisionAreaIndex( WorldPhysicsManager::INVALID_COLLISION_AREA ),
+
+mpController( 0 ),
+mpCharacterRenderable( 0 ),
+mpPuppet( 0 ),
+mfFacingDir( 0.0f ),
+mfDesiredDir( 0.0f ),
+mfSpeed( 0.0f ),
+mVelocity (0.0f, 0.0f, 0.0f),
+mfDesiredSpeed( 0.0f ),
+mbInCar( false ),
+mpCharacterTarget( 0 ),
+mpActionController( 0 ),
+mpCurrentActionButtonHandler( 0 ),
+mpTargetVehicle( 0 ),
+mTerrainType( TT_Road ),
+mInteriorTerrain( false ),
+mpStateManager( 0 ),
+mfRadius( 0.35f ),
+mbCollided( false ),
+mCurrentCollision( 0 ),
+mbIsStanding( true ),
+mpWalkerLocomotion( 0 ),
+mpJumpLocomotion( 0 ),
+mpStandingCollisionVolume( 0 ),
+mpStandingJoint( 0 ),
+mfGroundVerticalVelocity( 0.0f ),
+mfGroundVerticalPosition( 0.0f ),
+mbTurbo( false ),
+mbIsJump( false ),
+mbSolveCollisions( true ),
+mpPropHandler( 0 ),
+mPropJoint( -1 ),
+mVisible( false ),
+mpWorldScene( 0 ),
+m_IsSimpleShadow( true ),
+mYAdjust( 0.0f ),
+mbBusy(false),
+mbSimpleLoco( false ),
+m_TimeLeftToShock( 0 ),
+m_IsBeingShocked( false ),
+mDoKickwave(false),
+mKickwave(NULL),
+mKickwaveController(NULL),
+mAmbient(false),
+mAmbientLocator(0),
+mAmbientTrigger(NULL),
+mLastFramePos(0.0f,0.0f,0.0f),
+mbDoGroundIntersect(true),
+mIntersectFrame(s_IntersectFrame++),
+mAllowRockin(false),
+mHasBeenHit(false),
+mbSnapToGround(false),
+mSecondsSinceActionControllerUpdate( 0.0f ),
+mTooFarToUpdate(false),
+mSecondsSinceOnPostSimUpdate( 0.0f ),
+mRole(ROLE_UNKNOWN),
+mScale(1.0f),
+mIsInSubstep(true),
+mLean(0.0f, 1.0f, 0.0f),
+mIsLisa(false),
+mIsMarge(false),
+mManaged(false)
+{
+
+ mLastInteriorLoadCheck = radTimeGetMicroseconds64();
+
+ mPrevSimTransform.Identity();
+
+ mTranslucent = true;
+
+ mShadowColour.Set( 0, 0, 0 );
+
+ mGroundNormal.Set( 0.0f, 0.0f, 0.0f );
+ mRealGroundPos.Set( 0.0f, 0.0f, 0.0f );
+ mRealGroundNormal.Set( 0.0f, 0.0f, 0.0f );
+
+ mLastGoodPosOverStatic.Set( 0.0f, 0.0f, 0.0f );
+
+ unsigned int i;
+ for ( i = 0; i < MAX_ACTION_BUTTON_HANDLERS; ++i )
+ {
+ mpActionButtonHandlers[ i ] = NULL;
+ }
+
+}
+/*
+==============================================================================
+Character::Init
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::Init( void )
+{
+
+//#ifdef RAD_GAMECUBE
+// HeapMgr()->PushHeap(GMA_GC_VMM);
+//#else
+ HeapMgr()->PushHeap(GMA_LEVEL_OTHER);
+//#endif
+
+ mpCharacterTarget = new CharacterTarget( this );
+
+ mbWasFootPlanted.resize(2, false);
+
+ mpActionController = new ActionController;
+ mpActionController->Clear( );
+
+ mpStateManager = new CharacterAi::StateManager( this );
+
+ {
+ // Manually create the physics objects for now.
+ // Set inventory section to "Default" because the sim library will store stuff
+ //
+ p3d::inventory->PushSection( );
+ p3d::inventory->SelectSection( "Default" );
+
+ // Manually create the physics objects for now.
+ //
+ sim::SymMatrix identity;
+ identity.Identity();
+ rmt::Vector offset( 0.0f, 0.9f, -0.05f );
+
+ sim::CylinderVolume* pCollisionVolume = new sim::CylinderVolume( offset, vUp, 0.55f, mfRadius );
+ sim::CollisionObject* pCollisionObject = new sim::CollisionObject( pCollisionVolume );
+ sim::PhysicsProperties* pPhysicsProperties = sim::PhysicsProperties::HardWoodProperties(p3d::inventory);
+ sim::PhysicsObject* pSimulatedObject = new sim::PhysicsObject( pPhysicsProperties, offset, identity, 2.0f );
+ pSimulatedObject->SetSimEnvironment( GetWorldPhysicsManager()->mSimEnvironment );
+ sim::SimState* pSimState = sim::SimState::CreateSimState( pCollisionObject, pSimulatedObject );
+ rAssert( pSimState );
+ SetSimState( pSimState );
+
+ pCollisionObject->SetCollisionEnabled(false);
+ SetSolveCollisions(false);
+
+ p3d::inventory->PopSection( );
+ }
+
+ int i;
+ for ( i = 0; i < CollisionData::MAX_COLLISIONS; i++ )
+ {
+ mCollisionData[ i ].mCollisionNormal.Set( 0.0f, 0.0f, 0.0f );
+ mCollisionData[ i ].mCollisionDistance = 0.0f;
+ mCollisionData[ i ].mpCollisionVolume = (sim::CollisionVolume*)0;
+ }
+ mbCollidedWithVehicle = false;
+
+ mpWalkerLocomotion = new WalkerLocomotionAction( this );
+ mpWalkerLocomotion->AddRef();
+
+ mpJumpLocomotion = new JumpAction( this, "jump_idle", Character::GetJumpHeight() );
+ mpJumpLocomotion->AddRef();
+
+ rmt::Matrix mat;
+ mat.Identity( );
+ SetParentTransform( mat );
+
+/* mpPropHandler = new ActionButton::AttachProp;
+ mpPropHandler->AddRef( );
+ */
+ mPropJoint = 0;
+
+ if( !IsNPC() )
+ {
+ InitGroundPlane();
+ }
+ //AssignCollisionAreaIndex( );
+
+ //////////////////////////////
+ // Update Simstate transform
+ rmt::Vector position;
+ GetPosition( position );
+ rmt::Vector facing;
+ GetFacing( facing );
+
+ mat.Identity( );
+ mat.Row( 2 ) = facing;
+ mat.FillTranslate( position );
+
+ mpSimStateObj->SetTransform( mat );
+
+ if( !mIsNPC )
+ {
+ // If you're a player character
+ // Add ourself, our groundplane, and our self-groundplane pair
+ // to collision manager
+ AddToPhysics();
+ }
+
+ // Initialize position at origin
+ rmt::Vector zero( 0.0f, 0.0f, 0.0f );
+ SetPosition( zero );
+
+ UpdateTransformToLoco( );
+
+ // Dusit [Nov 13,2002]:
+ // Need to initialize out the garbage values. The problem is that
+ // the garbage values happen to be quite large... In a later call to
+ // UpdateSimState, we overwrite the currently quite nice simstate &
+ // collisionvolume transforms with the puppet's garbage value (still
+ // garbage because cloned NPCs such as pedestrians are not given a valid
+ // position by locator, but by arbitrary spawning). So now simstate &
+ // collisionvolume have large garbage transforms. Later on when
+ // this cloned NPC gets spawned (e.g. in Pedestrian::Activate()) with
+ // a valid position, the collisionvolume tries to update itself
+ // by calculating the difference between LARGE garbage value and a
+ // comparatively smaller position value. The floating point accuracy
+ // favors the large value and instead of moving to the new smaller
+ // position value, we move to (0,0,0). At this point, simstate's
+ // transform and simcollisionvolume's transform are out of synch.
+ //
+ SetPosition( position );
+
+//#ifdef RAD_GAMECUBE
+// HeapMgr()->PopHeap( GMA_GC_VMM );
+//#else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+//#endif
+}
+
+
+void Character::InitGroundPlane()
+{
+ MEMTRACK_PUSH_GROUP( "Character" );
+
+ HeapMgr()->PushHeap (GMA_LEVEL_OTHER);
+
+ rmt::Vector p(0.0f, 0.0f, 0.0f);
+ rmt::Vector n(0.0f, 1.0f, 0.0f);
+
+ mGroundPlaneWallVolume = new sim::WallVolume(p, n);
+ mGroundPlaneWallVolume->AddRef();
+
+ mGroundPlaneSimState = (sim::ManualSimState*)(
+ sim::SimState::CreateManualSimState(mGroundPlaneWallVolume));
+ mGroundPlaneSimState->AddRef();
+
+ mGroundPlaneSimState->GetCollisionObject()->SetManualUpdate(true);
+ mGroundPlaneSimState->GetCollisionObject()->SetAutoPair(false);
+ mGroundPlaneSimState->GetCollisionObject()->SetIsStatic(true);
+ mGroundPlaneSimState->GetCollisionObject()->SetCollisionEnabled(false);
+
+ char buffy[128];
+ sprintf( buffy, "player_character_groundplane" );
+ mGroundPlaneSimState->GetCollisionObject()->SetName( buffy );
+
+ mGroundPlaneSimState->mAIRefIndex = PhysicsAIRef::redBrickPhizMoveableGroundPlane;
+ mGroundPlaneSimState->mAIRefPointer = (void*)this;
+
+ /*
+ mGroundPlanePhysicsProperties = new PhysicsProperties;
+ mGroundPlanePhysicsProperties->AddRef();
+
+ mGroundPlanePhysicsProperties->SetFrictCoeffCGS(0.8f);
+ mGroundPlanePhysicsProperties->SetRestCoeffCGS(1.15f);
+ mGroundPlanePhysicsProperties->SetTangRestCoeffCGS(0.0f);
+
+ mGroundPlaneSimState->SetPhysicsProperties(mGroundPlanePhysicsProperties);
+ */
+
+ HeapMgr()->PopHeap (GMA_LEVEL_OTHER);
+ MEMTRACK_POP_GROUP( "Character" );
+}
+
+bool Character::IsInCarOrGettingInOut( void )
+{
+ if(mbInCar ||
+ GetStateManager()->GetState() == CharacterAi::GET_IN ||
+ GetStateManager()->GetState() == CharacterAi::GET_OUT)
+ {
+ return true;
+ }
+ return false;
+}
+
+/*
+==============================================================================
+Character::SetPuppet
+==============================================================================
+Description: Comment
+
+Parameters: ( choreo::Puppet* pPuppet )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SetPuppet( choreo::Puppet* pPuppet )
+{
+ rAssert( pPuppet );
+
+ rmt::Vector position;
+ float facing, desiredFacing;
+ GetPosition( position );
+ facing = GetFacingDir();
+ desiredFacing = GetDesiredDir();
+ tRefCounted::Assign( mpPuppet, pPuppet );
+ SetPosition( position );
+ SetFacingDir(facing);
+ SetDesiredDir(desiredFacing);
+
+ if(mpWalkerLocomotion)
+ {
+ mpWalkerLocomotion->SwitchLocomotion( );
+ }
+
+ mbWasFootPlanted.resize( pPuppet->GetLegCount(), false );
+/*
+ if ( pPuppet )
+ {
+ mPropJoint = pPuppet->GetP3DPose( )->FindJointIndex( "Wrist_R" );
+ }
+ if ( mPropJoint == -1 )
+ {
+ // Safe value.
+ //
+ mPropJoint = 0;
+ }
+*/
+ // The feet collider doesn't work as well as the ray intersection.
+ // It isn't any cheaper in the grand scheme of things either.
+ //
+ //mpFeetCollider = new CharacterFeetCollider( this );
+ int legs = mpPuppet->GetLegCount( );
+ for ( int i = 0; i < legs; i++ )
+ {
+ GetPuppet( )->SetIsLegIKEnabled( i, false );
+ }
+
+ if(GetActionController())
+ {
+ GetActionController()->Clear();
+ }
+
+ if(mpStateManager)
+ {
+ SetInCar(false);
+
+ if(mpStateManager->GetState() != CharacterAi::NOSTATE)
+ {
+ mpStateManager->ResetState();
+ }
+ }
+
+ pPuppet->GetEngine()->GetPoseEngine()->Begin(true);
+}
+
+
+void Character::SetYAdjust( float yOffset )
+{
+ mYAdjust = yOffset;
+}
+
+float Character::GetYAdjust()
+{
+ return mYAdjust;
+}
+
+/*
+==============================================================================
+Character::ResetSpeed
+==============================================================================
+*/
+void Character::ResetSpeed( void )
+{
+ mpSimStateObj->ResetVelocities( );
+ mpWalkerLocomotion->SetDesiredSpeed( 0.0f );
+}
+static rmt::Vector dstart,dend;
+void Character::Kick()
+{
+ if(GetInteriorManager()->IsInside())
+ {
+ return;
+ }
+
+ // Fetch the current character position
+ rmt::Vector position;
+ GetPosition( &position );
+
+ // Fetch character facing vector
+ rmt::Vector facing;
+ GetFacing( facing );
+
+
+ // We want to rotate the facing vector upwards by N degrees
+ // Find the vector thats orthogonal to the facing vector and the world up axis
+ // Lets make sure that the facing vector isn't parallel with the up vector
+ // first
+ rmt::Vector kickdir;
+ const rmt::Vector WORLD_UP( 0, 1.0f, 0 );
+ const float DOT_PRODUCT_PARALLEL_EPSILON = 0.99f;
+ if ( facing.Dot( WORLD_UP ) < DOT_PRODUCT_PARALLEL_EPSILON )
+ {
+ // Not parallel, take the crossproduct between world and up
+ rmt::Vector right;
+ right.CrossProduct( WORLD_UP, facing );
+ rmt::Matrix rotation;
+ rotation.Identity();
+ rotation.FillRotation( right, rmt::DegToRadian( KICK_ANGLE ));
+ kickdir.Rotate( facing, rotation );
+ rAssert( kickdir.y > 0 );
+ }
+ else
+ {
+ // They are parallel, just use the facing vector as the kicking direction
+ kickdir = facing;
+ }
+
+
+
+
+ const float KICK_EFFECT_RADIUS = 5.0f;
+ WorldPhysicsManager::NumObjectsHit numObjectsHit;
+
+ // Use the intersection list for querying and performing sim collision testing
+ IntersectionList intersectList;
+ intersectList.FillIntersectionListDynamics( position, KICK_EFFECT_RADIUS, true, this );
+ DynaPhysDSG* objectHit = NULL;
+ const float KICK_RAY_LEN = 2.0f;
+
+
+ rmt::Vector kickDest = position + kickdir;
+ rmt::Vector kickStart = position - kickdir;
+
+
+ rmt::Vector kickIntersect;
+ if ( intersectList.TestIntersectionDynamics( kickStart, kickDest, &kickIntersect, &objectHit ) )
+ {
+ switch ( objectHit->GetAIRef() )
+ {
+ case PhysicsAIRef::NPCharacter:
+ {
+ // We kicked an NPC. Send some events
+ NPCharacter* ch = (NPCharacter*)objectHit;
+ GetEventManager()->TriggerEvent( EVENT_KICK_NPC, ch );
+ GetEventManager()->TriggerEvent( EVENT_KICK_NPC_SOUND, ch );
+ GetEventManager()->TriggerEvent( EVENT_PEDESTRIAN_SMACKDOWN );
+ ch->SetHasBeenHit(true);
+
+ // Make the object that got hit fly
+ CharacterAi::CharacterState state = ch->GetStateManager()->GetState();
+ if( state == CharacterAi::LOCO )
+ {
+ // call a special kick that will reset the pastangular and pastlinear
+ // histories...
+ ch->ApplyKickForce( kickdir, CharacterTune::sfKickingForce );
+ }
+ else
+ {
+ ch->ApplyForce( kickdir, CharacterTune::sfKickingForce );
+ }
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, objectHit );
+ }
+ break;
+
+ case PhysicsAIRef::redBrickVehicle:
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, objectHit );
+ break;
+
+ case PhysicsAIRef::StateProp:
+ {
+ rAssert( dynamic_cast< StatePropDSG* >( objectHit ) != NULL );
+ StatePropDSG* stateprop = static_cast< StatePropDSG* >( objectHit );
+ // Lets not send EVENT_OBJECT_KICKED when the object has no collision volume
+ if ( stateprop->IsCollisionEnabled() )
+ {
+ objectHit->ApplyForce( kickdir, CharacterTune::sfKickingForce );
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, objectHit );
+ }
+ }
+ break;
+
+ default:
+ // Make the object that got hit fly
+ objectHit->ApplyForce( kickdir, CharacterTune::sfKickingForce );
+ InstDynaPhysDSG* toBreak = dynamic_cast<InstDynaPhysDSG*>(objectHit);
+
+ if(toBreak)
+ {
+ toBreak->Break();
+ }
+
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, objectHit );
+ break;
+ }
+ }
+ GetEventManager()->TriggerEvent( EVENT_KICK, this );
+
+}
+
+void Character::Slam()
+{
+ int i;
+ WorldPhysicsManager::CollisionEntityDSGList dsgList;
+
+ rmt::Vector position;
+ GetPosition( &position );
+
+ int numObjectsKicked = GetWorldPhysicsManager()->ApplyForceToDynamicsSpherical( mCollisionAreaIndex,
+ position,
+ 2,
+ CharacterTune::sfSlamForce,
+ &dsgList );
+
+ if ( numObjectsKicked > 0 )
+ {
+ for( i = 0; i < WorldPhysicsManager::CollisionEntityDSGList::NUM_COLLISION_LIST_ENTITIES; i++ )
+ {
+ CollisionEntityDSG* collObject = dsgList.collisionEntity[i];
+
+ if( collObject != NULL )
+ {
+ switch ( collObject->GetAIRef() )
+ {
+ case PhysicsAIRef::NPCharacter:
+ {
+ Character* ch = static_cast<Character*>(dsgList.collisionEntity[i]);
+ GetEventManager()->TriggerEvent( EVENT_KICK_NPC, ch );
+ GetEventManager()->TriggerEvent( EVENT_KICK_NPC_SOUND, ch );
+ GetEventManager()->TriggerEvent( EVENT_PEDESTRIAN_SMACKDOWN );
+ ch->SetHasBeenHit(true);
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, collObject );
+ }
+ break;
+ case PhysicsAIRef::StateProp:
+ {
+ rAssert( dynamic_cast< StatePropDSG* >( collObject ) != NULL );
+ StatePropDSG* stateprop = static_cast< StatePropDSG* >( collObject );
+ // Lets not send EVENT_OBJECT_KICKED when the object has no collision volume
+ if ( stateprop->IsCollisionEnabled() )
+ {
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, collObject );
+ }
+ break;
+ }
+ default:
+ GetEventManager()->TriggerEvent( EVENT_OBJECT_KICKED, collObject );
+ InstDynaPhysDSG* toBreak = dynamic_cast<InstDynaPhysDSG*>(collObject);
+
+ if(toBreak)
+ {
+ toBreak->Break();
+ }
+ break;
+ }
+
+ }
+ }
+ }
+ GetEventManager()->TriggerEvent( EVENT_STOMP, this );
+}
+
+void Character::RelocateAndReset( const rmt::Vector& position, float facing, bool resetMe, bool snapToGround )
+{
+ // TODO: Not done yet...
+ // What else need we do to relocate character & reset its states???
+ mLastFramePos = position;
+ mVelocity.Set(0.0f, 0.0f, 0.0f);
+
+ // Switch to AI control if we need to...
+ sim::SimState* simState = mpSimStateObj;
+ if( simState != NULL )
+ {
+ simState->SetControl( sim::simAICtrl );
+ }
+
+ // Update transforms for PoseJointZero, Puppet, and SimStateObj
+ SetPosition( position );
+
+ // update Puppet's root transform with facing
+ SetFacingDir( facing );
+ SetDesiredDir( facing );
+ ResetSpeed();
+
+ if( mpWalkerLocomotion != NULL )
+ {
+ // Gotta do this so facing is not overwritten by blend priorities
+ // when the player's character is involved...
+ // Without this call here, the player's character will gets its
+ // new facing value clobbered by old values stored in blend priorities
+ // when we go into UpdateTransformToLoco()
+ choreo::LocomotionDriver* locomod = mpWalkerLocomotion->GetDriver();
+ if( locomod != NULL )
+ {
+ locomod->SetActualFacingAngle( facing );
+ }
+ }
+
+ SetStandingJoint(NULL);
+
+ UpdateTransformToLoco();
+
+ // By this point, our position should be finalized and no UpdateBBox
+ // has been called (for moveinworldscene to do)
+
+ // Don't call UpdatePuppet() because it resets our puppet->engine->rootblender's
+ // and joints' transforms back to previous one due to blend priorities in UpdateRoot()
+ //UpdatePuppet( 0.0f );
+ choreo::Puppet* pPuppet = GetPuppet( );
+ rAssert( pPuppet );
+ pPuppet->UpdatePose();
+// pPuppet->UpdateEnd();
+
+ // update jump's root transform after Puppet's transform is set
+ if( mpJumpLocomotion != NULL )
+ {
+ // Copies transform from Puppet->Engine->RootBlender.
+ // If that is correct, so will this be.
+ mpJumpLocomotion->SetRootTransform();
+ }
+ if( mpWalkerLocomotion != NULL )
+ {
+ // Characters obtained a new locomotion driver in previous call to
+ // UpdateTransformToLoco, so we have to reset that driver's
+ // actual facing angle again...
+ // Without this call, NPCs won't face the correct way...
+ // Player character can get away with it cuz the driver gets updated
+ // again later.
+ choreo::LocomotionDriver* locomod = mpWalkerLocomotion->GetDriver();
+ if( locomod != NULL )
+ {
+ locomod->SetActualFacingAngle( facing );
+ }
+ }
+
+
+ ////////////////////////////////////////////////////////////////////
+ //
+ // Force NPC to submit statics and whatever's below them &
+ // update their terrain intersect info. We should actually do this
+ // for EVERY character, but dammit we can't afford to.
+ //
+ if( mIsNPC && mRole != ROLE_PEDESTRIAN )
+ {
+ // temporarily transit the controller to STOPPED state so we force submit statics
+ // to actually submit statics...
+ NPCController* npcController = (NPCController*) mpController;
+ NPCController::State oldState = npcController->GetState();
+ npcController->TransitToState( NPCController::STOPPED );
+
+ // temporarily obtain collision area index if we don't got one...
+ int oldArea = mCollisionAreaIndex;
+ if( oldArea == WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ AddToPhysics();
+ }
+
+ SubmitStatics();
+
+ rmt::Vector prevPosition = position;
+ rmt::Vector groundPosition = position;
+ rmt::Vector outnorm( 0.0f, 1.0f, 0.0f );
+ bool bFoundPlane = false;
+
+ mTerrainType = static_cast<eTerrainType>( GetIntersectManager()->FindIntersection(
+ groundPosition, // IN
+ bFoundPlane, // OUT
+ outnorm, // OUT
+ groundPosition ) ); // OUT
+ mInteriorTerrain = ( (int)mTerrainType & 0x80 ) == 0x80;
+ mTerrainType = static_cast<eTerrainType>( ( (int)mTerrainType & ~0x80 ) );
+
+ if( bFoundPlane )
+ {
+ mRealGroundPos = groundPosition;
+ mRealGroundNormal = outnorm;
+ float tooHigh = 100000.0f;
+ rAssert( -tooHigh <= mRealGroundNormal.x && mRealGroundNormal.x <= tooHigh );
+ rAssert( -tooHigh <= mRealGroundNormal.y && mRealGroundNormal.y <= tooHigh );
+ rAssert( -tooHigh <= mRealGroundNormal.z && mRealGroundNormal.z <= tooHigh );
+ }
+ else
+ {
+ //
+ // If this assert goes off for the player charater when he's hit by traffic
+ // it means that Physics has placed him at some location other than his
+ // present location. This is a bad thing... possibly related to
+ // collisions with traffic cars.
+ //rAssertMsg( false, "We're SOOO far from the ground, we're not intersecting" );
+ }
+ rmt::Vector collisionPosition;
+ rmt::Vector collisionNormal;
+
+ collisionNormal.Clear( );
+ collisionPosition.Clear( );
+
+ bool bOverStatic = GetCollisionHeight( prevPosition, position, collisionPosition, collisionNormal );
+ if ( bOverStatic )
+ {
+ if ( !bFoundPlane || collisionPosition.y > groundPosition.y )
+ {
+ mGroundY = collisionPosition.y;
+ mGroundNormal = collisionNormal;
+
+ mLastGoodPosOverStatic = position;
+ mLastGoodPosOverStatic.y = mGroundY;
+ }
+ else
+ {
+ rAssert( bFoundPlane );
+ mGroundY = groundPosition.y;
+ mGroundNormal = outnorm;
+ }
+ }
+ else if ( bFoundPlane )
+ {
+ mGroundY = groundPosition.y;
+ mGroundNormal = outnorm;
+ }
+ else
+ {
+ mGroundY = position.y;
+ mGroundNormal.Set( 0.0f, 1.0f, 0.0f );
+ }
+
+ if( snapToGround )
+ {
+ rmt::Vector groundPos, groundNormal;
+ GetTerrainIntersect( groundPos, groundNormal );
+ SetPosition( groundPos );
+ SetGroundPoint( groundPos );
+ mpJumpLocomotion->SetRootPosition( WorldToLocal(groundPos) );
+ snapToGround = false;
+ }
+
+ if( oldArea == WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ RemoveFromPhysics();
+ }
+ npcController->TransitToState( oldState );
+ }
+ //////////////////////////////////////////////////////////////////////////
+
+
+
+ MoveInWorldScene();
+ if( GetCharacterManager()->GetCharacter(0) == this )
+ GetTriggerVolumeTracker()->ResetDynaloadZones();
+
+ mbSnapToGround = snapToGround;
+ mTooFarToUpdate = false;
+ mSecondsSinceActionControllerUpdate = 0.0f;
+ mSecondsSinceOnPostSimUpdate = 0.0f;
+
+
+ if( resetMe )
+ {
+ // Reset collisions (otherwise the collisions carry on from last placement)
+ ResetCollisions();
+
+ // do SimState reset
+ if( mpSimStateObj != NULL )
+ {
+ mpSimStateObj->ResetVelocities();
+ }
+
+ // Reset action button so we don't get blinking "y" after reset
+ ClearAllActionButtonHandlers();
+
+ mbTurbo = false;
+
+ // Clear props??
+// mpPropHandler->Reset();
+
+ GetStateManager()->SetState<CharacterAi::Loco>();
+
+ mbIsJump = false;
+
+ mpActionController->Clear();
+
+ /*
+ // TODO:
+ // Do more resetting here... If it gets to be a lot, gotta put it in a
+ // separate ResetStates() function
+
+ // Clear actions & priorities??
+
+ // TODO:
+ // How do we halt the jump sequence & reset back to standing?
+ // Actually. We need a general "HaltAnimations" function that stops
+ // whatever you're doing and reset to standing & idle animation...
+ // This includes stopping: jumping, opening doors, turboing, dashing, etc...
+
+ if( mbIsJump )
+ {
+ mpJumpLocomotion->End();
+ mpJumpLocomotion->Done();
+ mbIsJump = false;
+ }
+
+ // TODO:
+ // DO we need to do these things?
+ if( mpLocomotion != NULL )
+ {
+ mpLocomotion->Clear();
+ }
+
+ if( mpActionController != NULL )
+ {
+ mpActionController->Clear();
+ }
+
+ if( mpController != NULL )
+ {
+ mpController->ClearIntention();
+ }
+
+ if( mpStateManager != NULL )
+ {
+ mpStateManager->ResetState( CharacterAi::LOCO, this );
+ }
+
+
+ //mbWasFootPlanted.clear();
+ mbIsStanding = true;
+ mbSolveCollisions = false;
+
+ */
+ }
+}
+
+
+
+void Character::AddToPhysics( void )
+{
+ if( !mManaged )
+ {
+ return;
+ }
+
+ if( mCollisionAreaIndex != WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ return;
+ }
+
+ sim::CollisionObject* myColObj = mpSimStateObj->GetCollisionObject();
+ sim::CollisionObject* groundPlaneColObj = mGroundPlaneSimState->GetCollisionObject();
+
+ AssignCollisionAreaIndex();
+
+ GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(
+ myColObj, mCollisionAreaIndex );
+ GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(
+ groundPlaneColObj, mCollisionAreaIndex);
+ GetWorldPhysicsManager()->mCollisionManager->AddPair(
+ groundPlaneColObj, myColObj, mCollisionAreaIndex);
+
+ myColObj->SetCollisionEnabled( true );
+
+ // disable groundplane collision till we need it
+ groundPlaneColObj->SetCollisionEnabled( false );
+
+ SetSolveCollisions( true );
+}
+
+void Character::RemoveFromPhysics( void )
+{
+ if( mCollisionAreaIndex == WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ return;
+ }
+
+ sim::CollisionObject* myColObj = mpSimStateObj->GetCollisionObject();
+ sim::CollisionObject* groundPlaneColObj = mGroundPlaneSimState->GetCollisionObject();
+
+ myColObj->SetCollisionEnabled( false );
+ groundPlaneColObj->SetCollisionEnabled( false );
+
+ // Freeing a collision area will also empty it out, so no remove calls
+ // necessary here.
+ GetWorldPhysicsManager()->FreeCollisionAreaIndex( mCollisionAreaIndex );
+
+ mCollisionAreaIndex = WorldPhysicsManager::INVALID_COLLISION_AREA;
+
+ SetSolveCollisions( false );
+}
+
+void NPCharacter::AddToPhysics( void )
+{
+ // NPCs don't keep track of their own groundplane... When they go into sim
+ // they are submitted by some other entity and get temp groundplanes
+ // assigned to them.
+ // So we only add ourselves to collisions
+
+ /*
+#ifdef RAD_DEBUG
+ rDebugPrintf( "+++++++++ Adding to Physics: %s, index %d\n", GetName(), mCollisionAreaIndex );
+#endif
+ */
+
+ if( mCollisionAreaIndex != WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ return;
+ }
+
+ sim::CollisionObject* myColObj = mpSimStateObj->GetCollisionObject();
+
+ AssignCollisionAreaIndex();
+
+ GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(
+ myColObj, mCollisionAreaIndex );
+
+ myColObj->SetCollisionEnabled( true );
+
+ SetSolveCollisions( true );
+}
+
+void NPCharacter::RemoveFromPhysics( void )
+{
+ /*
+#ifdef RAD_DEBUG
+ rDebugPrintf( "-------- Removing from Physics: %s, index %d\n", GetName(), mCollisionAreaIndex );
+#endif
+ */
+
+ if( mCollisionAreaIndex == WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ return;
+ }
+
+ sim::CollisionObject* myColObj = mpSimStateObj->GetCollisionObject();
+
+ myColObj->SetCollisionEnabled( false );
+
+ // Freeing a collision area will also empty it out, so no remove calls
+ // necessary here.
+
+ GetWorldPhysicsManager()->FreeCollisionAreaIndex( mCollisionAreaIndex );
+
+ mCollisionAreaIndex = WorldPhysicsManager::INVALID_COLLISION_AREA;
+
+ SetSolveCollisions( false );
+
+}
+
+
+
+
+/*
+==============================================================================
+Character::~Character
+==============================================================================
+Description: Destructor
+
+Parameters: ( void )
+
+Return: na
+
+=============================================================================
+*/
+Character::~Character( void )
+{
+ tRefCounted::Release(mpStandingCollisionVolume);
+
+ if(GetActionController())
+ {
+ GetActionController()->Clear();
+ }
+
+ tRefCounted::Release(mKickwave);
+ tRefCounted::Release(mKickwaveController);
+
+ if( mGroundPlaneSimState )
+ {
+ if(mCollisionAreaIndex != WorldPhysicsManager::INVALID_COLLISION_AREA)
+ {
+ GetWorldPhysicsManager()->mCollisionManager->RemoveCollisionObject(
+ mGroundPlaneSimState->GetCollisionObject(),
+ mCollisionAreaIndex);
+ }
+ mGroundPlaneSimState->Release();
+ mGroundPlaneSimState = NULL;
+ }
+ if( mGroundPlaneWallVolume )
+ {
+ mGroundPlaneWallVolume->Release();
+ mGroundPlaneWallVolume = NULL;
+ }
+
+ // NOTE:
+ // Do the same RemoveCollisionObject call for our simstate?
+ // No need. CharacterManager's destroy will call
+ // RemoveFromAllDynamicBlahblahblah for us.
+
+ if( WorldPhysicsManager::INVALID_COLLISION_AREA != mCollisionAreaIndex )
+ {
+ GetWorldPhysicsManager()->FreeCollisionAreaIndex( mCollisionAreaIndex );
+ }
+
+ if( mpController )
+ {
+ mpController->Release( );
+ mpController = 0;
+ }
+ if( mpCharacterRenderable )
+ {
+ delete mpCharacterRenderable;
+ }
+ if ( mpCharacterTarget )
+ {
+ delete mpCharacterTarget;
+ mpCharacterTarget = 0;
+ }
+ if ( mpActionController )
+ {
+ delete mpActionController;
+ mpActionController = 0;
+ }
+ if ( mpStateManager )
+ {
+ delete mpStateManager;
+ mpStateManager = 0;
+ }
+ if ( mpWalkerLocomotion )
+ {
+ mpWalkerLocomotion->Release( );
+ mpWalkerLocomotion = 0;
+ }
+ if ( mpJumpLocomotion )
+ {
+ mpJumpLocomotion->Release( );
+ mpJumpLocomotion = 0;
+ }
+ if ( mbWasFootPlanted.empty() == false )
+ {
+ mbWasFootPlanted.clear();
+ }
+
+ SetTargetVehicle(NULL);
+ ClearAllActionButtonHandlers();
+// tRefCounted::Release( mpPropHandler );
+ if(mAmbientTrigger)
+ {
+ mAmbientTrigger->ClearLocator();
+ }
+ tRefCounted::Release( mAmbientTrigger );
+
+ //
+ // Delete the puppet last. I moved this down to the bottom because of an occasional
+ // non-reproducible crash on gameplay exit where the ActionController destruction above
+ // ended up referencing the puppet which had already been cleaned up. Other desctructors
+ // above (e.g. locomotion objects) may also end up referencing the puppet. Putting it
+ // at the bottom should avoid that. -- jdy
+ //
+ if ( mpPuppet )
+ {
+ mpPuppet->ReleaseVerified();
+ mpPuppet = 0;
+ }
+}
+/*
+==============================================================================
+Character::UpdateParentTransform
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateParentTransform( float timeins )
+{
+ // Do this before UpdateRoot()
+ //
+
+ rmt::Matrix transform;
+ transform.Identity( );
+ if ( IsInCar() && GetTargetVehicle() )
+ {
+ transform = GetTargetVehicle()->GetTransform();
+ }
+ else if ( mpStandingJoint )
+ {
+ transform = mpStandingJoint->GetWorldMatrix( );
+ }
+ SetParentTransform( transform, timeins );
+
+}
+
+
+/*
+==============================================================================
+Character::SetParentTransform
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Matrix& mat, float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SetParentTransform( const rmt::Matrix& newTransform, float timeins )
+{
+ if ( !rmt::Epsilon( timeins, 0.0f ) )
+ {
+ float invDeltaTime = 1.0f / timeins;
+ mfGroundVerticalVelocity = newTransform.Row( 3 ).y - mfGroundVerticalPosition;
+ mfGroundVerticalVelocity *= invDeltaTime;
+ }
+ else
+ {
+ mfGroundVerticalVelocity = 0.0f;
+ }
+ mParentTransform = newTransform;
+ mfGroundVerticalPosition = mParentTransform.Row( 3 ).y;
+
+ //rAssertMsg( mParentTransform.IsOrthoNormal( ), "Your parent transform node is screwed!\n" );
+
+ if ( !IsInCar( ) )
+ {
+ // If we are not in the car, we always want to keep the character up
+ // at 0,1,0.
+ //
+ rmt::Vector facing = mParentTransform.Row( 2 );
+ mParentTransform.FillHeadingXZ( facing );
+
+ // We don't detect vertical motion with the transform.
+ // so we will zero it out.
+ //
+ mParentTransform.Row( 3 ).y = 0.0f;
+ }
+ mInvParentTransform.InvertOrtho( mParentTransform );
+ if ( mpPuppet )
+ {
+ poser::Transform transform;
+ transform.Identity( );
+ transform.SetMatrix( mParentTransform );
+ mpPuppet->SetParentTransform( transform );
+ }
+}
+
+
+void Character::UpdateGroundPlane( float timeins )
+{
+ // new approach with manual sim state
+ rAssert( mGroundPlaneWallVolume );
+
+ float tooHigh = 100000.0f;
+ rAssert( -tooHigh <= mRealGroundNormal.x && mRealGroundNormal.x <= tooHigh );
+ rAssert( -tooHigh <= mRealGroundNormal.y && mRealGroundNormal.y <= tooHigh );
+ rAssert( -tooHigh <= mRealGroundNormal.z && mRealGroundNormal.z <= tooHigh );
+
+ mGroundPlaneWallVolume->mPosition = mRealGroundPos; //p;
+ mGroundPlaneWallVolume->mNormal = mRealGroundNormal; //n;
+
+ rAssert( mGroundPlaneSimState );
+ sim::CollisionObject* co = mGroundPlaneSimState->GetCollisionObject();
+ co->PostManualUpdate();
+}
+
+
+
+/*
+==============================================================================
+Character::PreSimUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::PreSimUpdate( float timeins )
+{
+#ifdef RAD_DEBUG
+ timeins *= timeScale;
+#endif
+
+ if( !IsNPC() ) // ok, we're player character
+ {
+ UpdateGroundPlane( timeins );
+ }
+
+ /////////////////////////////////////////////////
+ // Test if we're too far from the camera for some updates
+ mTooFarToUpdate = false;
+
+ // if we're in First Person Cam, this is bad news... They can ZOOM!
+ // So treat it as though we're ALWAYS not too far to update.
+ if( GetInputManager()->GetGameState() != Input::ACTIVE_FIRST_PERSON )
+ {
+ rmt::Vector myPos;
+ GetPosition( myPos );
+
+ rmt::Vector testPos;
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( testPos );
+ /*
+ // NOTE:
+ // Can't use the camera pos because we could get quite far from the
+ // cam.. use the avatar's pos
+ GetSuperCamManager()->GetSCC(0)->GetCamera()->GetPosition( &testPos );
+ */
+ float fov, aspect;
+ GetSuperCamManager()->GetSCC(0)->GetCamera()->GetFOV(&fov, &aspect);
+ static float fovCutoff = 1.0f;
+
+ const float DIST_TOO_FAR_TO_UPDATE_SQR = 625.0f;
+
+ float distSqr = (myPos - testPos).MagnitudeSqr();
+ if( distSqr > DIST_TOO_FAR_TO_UPDATE_SQR && (fov > fovCutoff))
+ {
+ mTooFarToUpdate = true;
+ }
+ }
+
+ //////////////////////////////////////////////////////
+ // Sense and think.
+ //
+ rmt::Vector direction;
+ UpdateController( direction, timeins );
+ if ( IsMovementLocked() == false )
+ {
+ UpdateDesiredDirAndSpeed( direction );
+ }
+ else
+ {
+ UpdateDesiredDirAndSpeed( rmt::Vector(0,0,0) );
+ }
+
+
+
+ if( mpSimStateObj->GetControl() == sim::simSimulationCtrl &&
+ GetStateManager()->GetState() != CharacterAi::INSIM )
+ {
+ // this will cause flail & get-up anims to be sequenced
+ GetStateManager()->SetState<CharacterAi::InSim>();
+ }
+
+
+ ////////////////////////////////////////////////////////
+ // Selectively update action controller every n frames if:
+ // - not in view, or
+ // - too far away
+
+ unsigned int modulo = 0x7;
+ unsigned int frameCount = GetGame()->GetFrameCount();
+
+ mSecondsSinceActionControllerUpdate += timeins;
+
+ //chuck: changed the herusitic to also include drivers in cars
+ //since we dont want driver pop a mission restarts
+ if(
+ !mIsNPC
+ ||
+ (mbInAnyonesFrustrum)
+ ||
+ ((frameCount & modulo) == (mIntersectFrame & modulo)
+ ||
+ (mpTargetVehicle != NULL)
+
+ )
+
+
+
+ )
+ {
+ BEGIN_PROFILE("ActionController()->Update")
+ GetActionController()->Update( mSecondsSinceActionControllerUpdate );
+ END_PROFILE("ActionController()->Update")
+
+ // Execute intentions based on current state
+ mpStateManager->Update( mSecondsSinceActionControllerUpdate );
+
+ // Do simulation
+ GetActionController()->WakeUp( mSecondsSinceActionControllerUpdate );
+ GetActionController()->DoSimulation( mSecondsSinceActionControllerUpdate );
+
+ mSecondsSinceActionControllerUpdate = 0.0f;
+ }
+ ////////////////////////////////////////////////////////
+
+
+
+ // Reset last frame's collisions in preparation for new collision data
+ // (given unto me by WorldPhysMan's substep)
+ ResetCollisions( );
+
+ mpPuppet->UpdateBegin();
+
+ // Zero out the prophandler. If it is still valid it will get set
+ // during CollisioDetect.
+ //
+// mpPropHandler->SetProp( 0 );
+}
+
+/*
+==============================================================================
+Character::ResetCollisions
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::ResetCollisions( void )
+{
+ // If the thing has moved, then there will be new collision state info.
+ // If NOT then we don't reset our collision state info, we will reuse it.
+ //
+ // if ( mpSimStateObj->GetCollisionObject( )->HasMoved() )
+ {
+ // Reset everything after this code, and before PostSimUpdate
+ // is when we do the collision detection.
+ //
+ mbCollided = false;
+ int i;
+ for ( i = 0; i < CollisionData::MAX_COLLISIONS; i++ )
+ {
+ mCollisionData[ i ].mCollisionNormal.Set( 0.0f, 0.0f, 0.0f );
+ mCollisionData[ i ].mCollisionDistance = 0.0f;
+ mCollisionData[ i ].mpCollisionVolume = (sim::CollisionVolume*)0;
+ }
+ mCurrentCollision = 0;
+ }
+
+ mbCollidedWithVehicle = false;
+
+}
+/*
+==============================================================================
+Character::UpdateController
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& direction, float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateController( rmt::Vector& direction, float timeins )
+{
+
+ if ( GetController( ) )
+ {
+ GetController( )->Update( timeins );
+ GetController( )->GetDirection( direction );
+ }
+ else
+ {
+ direction.Set( 0.0f, 0.0f, 0.0f );
+ }
+}
+/*
+==============================================================================
+Character::UpdateDesiredDirAndSpeed
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& dir )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateDesiredDirAndSpeed( const rmt::Vector& dir )
+{
+ rmt::Vector direction = dir;
+
+#ifdef RAD_WIN32
+ SuperCam* cam = GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ PCCam* pPCCam = NULL;
+ if( !mIsNPC && cam->GetType() == SuperCam::PC_CAM )
+ {
+ pPCCam = static_cast<PCCam*>(cam);
+ }
+#endif
+
+#ifndef RAD_WIN32
+ if ( direction.DotProduct( direction ) > 0.001f )
+ {
+ SetDesiredDir( choreo::GetWorldAngle( direction.x, direction.z ) );
+ }
+#else
+ if( !mIsNPC && pPCCam )
+ {
+ pPCCam->SetPitchVelocity( direction.x );
+
+ // if input is given in either z-axis (forward and back) or x-axis (left and right)
+ //
+ rAssert( dynamic_cast<CameraRelativeCharacterController*>( GetController() ) );
+ CharacterMappable* map = ((CameraRelativeCharacterController*)this->GetController())->GetCharacterMappable();
+
+ float dirPadZ = map->GetValue( CharacterController::DPadUp ) - map->GetValue( CharacterController::DPadDown );
+ float dirAnalogZ = map->GetValue( CharacterController::LeftStickY );
+ float dirZ = rmt::Fabs( dirPadZ ) > rmt::Fabs( dirAnalogZ ) ? dirPadZ : dirAnalogZ;
+
+ float dirPadX = map->GetValue( CharacterController::DPadRight ) - map->GetValue( CharacterController::DPadLeft );
+ float dirAnalogX = map->GetValue( CharacterController::LeftStickX );
+ float dirX = rmt::Fabs( dirPadX ) > rmt::Fabs( dirAnalogX ) ? dirPadX : dirAnalogX;
+
+ /*
+ float currAngle = GetFacingDir();
+
+ if ( !rmt::Epsilon( dirX, 0, 0.01f ) || !rmt::Epsilon( dirZ, 0, 0.01f ) )
+ {
+ rmt::Vector camHeading;
+ cam->GetHeading( &camHeading );
+ camHeading.y = 0.0f;
+
+ rmt::Vector tmpHeading;
+
+ rmt::Matrix mat;
+ mat.Identity();
+ mat.FillHeading( camHeading, rmt::Vector( 0.0f, 1.0f, 0.0f ) );
+ tmpHeading.Set( dirX, 0.0f, dirZ );
+ tmpHeading.Transform( mat );
+
+ if( rmt::Epsilon( dirX, 0.0f ) )
+ {
+ if ( dirZ > 0.0f )
+ {
+ mPCCamFacing = 0;
+ }
+ else
+ {
+ mPCCamFacing = 1;
+ }
+ }
+ else
+ {
+ mPCCamFacing = 2;
+ }
+
+ mInvParentTransform.RotateVector( tmpHeading, &tmpHeading );
+ currAngle = choreo::GetWorldAngle( tmpHeading.x, tmpHeading.z );
+ SetFacingDir( currAngle );
+
+ direction.x = dirX;
+ }
+ else
+ {
+ // Upon no input, face the character in the direction of cam
+ //rmt::Vector camHeading;
+ //cam->GetHeading( &camHeading );
+ //camHeading.y = 0.0f;
+
+ // reset currAngle and modify the character's facing directly.
+ //currAngle = choreo::GetWorldAngle( camHeading.x, camHeading.z );
+ //SetFacingDir( currAngle );
+
+ //direction.Set( 0.0f, 0.0f, 0.0f );
+
+ //mPCCamFacing = 0;
+ }
+
+ static float X_SENSE_MOD = 0.01f;
+ float mouseSense = static_cast<Mouse*>(GetInputManager()->GetController( 0 )->GetRealController( MOUSE ))->getSensitivityX();
+
+ static float ROT_DIR = 4.0f;
+
+ float fPitchVelocity = pPCCam->GetPitchVelocity();
+ float fScaleFactor = 1.0f;
+ if( mbTurbo ) fScaleFactor *= mVelocity.Magnitude();
+
+ currAngle += ROT_DIR * mouseSense * X_SENSE_MOD * pPCCam->GetAngularSpeed() * pPCCam->GetPitchVelocity();
+
+ SetDesiredDir( currAngle );
+ */
+
+ if ( !rmt::Epsilon( dirX, 0, 0.01f ) || !rmt::Epsilon( dirZ, 0, 0.01f ) )
+ {
+ rmt::Vector camHeading;
+ cam->GetHeading( &camHeading );
+ camHeading.y = 0.0f;
+
+ rmt::Vector tmpHeading;
+
+ rmt::Matrix mat;
+ mat.Identity();
+ mat.FillHeading( camHeading, rmt::Vector( 0.0f, 1.0f, 0.0f ) );
+ tmpHeading.Set( dirX, 0.0f, dirZ );
+ tmpHeading.Transform( mat );
+
+ if( rmt::Epsilon( dirX, 0.0f ) )
+ {
+ if ( dirZ > 0.0f )
+ {
+ mPCCamFacing = 0;
+ }
+ else
+ {
+ mPCCamFacing = 1;
+ }
+ }
+ else
+ {
+ mPCCamFacing = 2;
+ }
+
+ mInvParentTransform.RotateVector( tmpHeading, &tmpHeading );
+ float currAngle = choreo::GetWorldAngle( tmpHeading.x, tmpHeading.z );
+ SetDesiredDir( currAngle );
+
+ direction.x = dirX;
+ }
+ }
+ else // We reached here cuz we're either an NPC or we're not in PC_CAM
+ {
+ if ( direction.DotProduct( direction ) > 0.001f )
+ {
+ SetDesiredDir( choreo::GetWorldAngle( direction.x, direction.z ) );
+ }
+ }
+#endif
+
+ float fDesiredSpeed = direction.Magnitude();
+
+ // apply non-linear response to input to Player Character only..
+ // the NPCs rely on desired speed to remain unaltered
+ // for the intended speed to be reached.
+ if( !mIsNPC )
+ {
+ fDesiredSpeed *= fDesiredSpeed;
+ }
+
+ // Now normalize the speed to a smaller range than 1.0, probably something like 0.84f
+ //
+ float fMaxScale = GetInputScale( );
+ fDesiredSpeed /= fMaxScale;
+ if ( fDesiredSpeed > 1.0f )
+ {
+ fDesiredSpeed = 1.0f;
+ }
+ SetDesiredSpeed( fDesiredSpeed * GetMaxSpeed( ) );
+}
+/*
+==============================================================================
+Character::UpdateRoot
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateRoot( float timeins )
+{
+ OnUpdateRoot( timeins );
+}
+
+/*
+==============================================================================
+Character::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::OnUpdateRoot( float timeins )
+{
+#ifdef RAD_DEBUG
+ timeins *= timeScale;
+#endif
+
+ UpdatePuppet( timeins );
+}
+/*
+==============================================================================
+Character::UpdatePuppet
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdatePuppet( float timeins )
+{
+ choreo::Puppet* pPuppet = GetPuppet( );
+ rAssert( pPuppet );
+ // post sim inserted here
+ UpdateParentTransform( timeins );
+
+ mbNeedChoreoUpdate = !(
+ (pPuppet->GetEngine()->GetRootBlender()->GetRootDriverCount() <= 1)
+ && (!mVisible || !mbInAnyonesFrustrum)
+ && mbSimpleLoco);
+
+ if(pPuppet->GetDriverCount() == 0)
+ {
+// rDebugPrintf("WARNING : Character '%s' has no pose drivers, holding last pose\n", GetName());
+ mbNeedChoreoUpdate = false;
+ }
+
+ if(!mbNeedChoreoUpdate && mbSimpleLoco)
+ {
+ pPuppet->GetEngine()->GetRootBlender()->ClearRootDrivers();
+
+ rmt::Vector position = pPuppet->GetPosition();
+ rAssert( mpWalkerLocomotion != NULL );
+ choreo::LocomotionDriver* locoDriver = mpWalkerLocomotion->GetDriver();
+ rAssert( locoDriver != NULL );
+ float velocity = locoDriver->GetDesiredVelocity();
+
+ rmt::Vector facing;
+ this->GetFacing(facing);
+
+ facing *= velocity * timeins;
+ pPuppet->SetPosition(position + facing);
+
+ pPuppet->UpdateRoot();
+ }
+ else
+ {
+// if(pPuppet->GetDriverCount() != 0)
+ {
+ // Update choreo.
+ //
+ // Push() all drivers before call to choreo::Puppet::Begin()
+ //
+ pPuppet->Advance( timeins );
+
+ // choreo::Puppet::UpdateRoot() will change choreo::Puppet Position and Orientation
+ //
+ pPuppet->UpdateRoot();
+ }
+ }
+}
+
+bool Character::TestInAnyonesFrustrum()
+{
+ ////////////////////////////////////////////////
+ // Test if we're in anyone's frustrum
+ //
+ mbInAnyonesFrustrum = false;
+ int playerCount = 0;
+ int numPlayers = ::GetGameplayManager()->GetNumPlayers();
+ while( !mbInAnyonesFrustrum && playerCount < numPlayers )
+ {
+ TestInFrustrumOfPlayer(playerCount);
+ playerCount++;
+ }
+ if( mpCharacterRenderable != NULL )
+ {
+ // characterrenderable doesn't have pointer to character, so
+ // store result there too...
+ mpCharacterRenderable->SetInAnyonesFrustrum( mbInAnyonesFrustrum );
+ }
+ return mbInAnyonesFrustrum;
+}
+
+
+/*
+==============================================================================
+Character::PostSimUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::PostSimUpdate( float timeins )
+{
+ TestInAnyonesFrustrum();
+
+#ifdef RAD_DEBUG
+ timeins *= timeScale;
+#endif
+
+ // determine how frequently we need to make some of the expensive calls
+ unsigned int modulo = 0x7;
+ unsigned int frameCount = GetGame()->GetFrameCount();
+ bool shouldUpdate = !mIsNPC || // update the PC at full rate
+ IsInCar() || // or parent transform will get screwed
+ (mbInAnyonesFrustrum) || // update if we are visible
+ ((frameCount & modulo) == (mIntersectFrame & modulo));
+
+BEGIN_PROFILE("OnPostSimUpdate")
+ mSecondsSinceOnPostSimUpdate += timeins;
+
+ if( shouldUpdate )
+ {
+ OnPostSimUpdate( mSecondsSinceOnPostSimUpdate );
+ mSecondsSinceOnPostSimUpdate = 0.0f;
+ }
+
+ mbNeedChoreoUpdate = mbNeedChoreoUpdate && shouldUpdate && (!mTooFarToUpdate || ((frameCount & modulo) == (mIntersectFrame & modulo)));
+
+END_PROFILE("OnPostSimUpdate")
+
+ // Call physics update.
+ // Needs to be called before UpdatePose in case we're in SimulationCtrl
+BEGIN_PROFILE("UpdateSimState")
+ UpdateSimState( timeins );
+END_PROFILE("UpdateSimState")
+
+ choreo::Puppet* pPuppet = GetPuppet( );
+ rAssert( pPuppet );
+
+ ResolveCollisions();
+
+ BEGIN_PROFILE("UpdateGroundHeight")
+ if ( !IsInCar( ) )
+ {
+ UpdateGroundHeight();
+ }
+END_PROFILE("UpdateGroundHeight")
+
+ // Update the puppet with the physics transform?
+ //
+/*
+BEGIN_PROFILE("UpdateProps")
+ UpdateProps( timeins );
+END_PROFILE("UpdateProps")
+*/
+
+ if ( GetController() )
+ {
+ GetController()->ClearIntention();
+ }
+
+ if(IsNPC())
+ {
+ if(mAmbientTrigger)
+ {
+ rmt::Vector pos;
+ GetPosition(pos);
+ mAmbientTrigger->SetPosition(pos);
+ }
+ }
+ else
+ {
+ /*
+ // Clear the action handler when we are set to handle a prop
+ // that has been previously cleared.
+ //
+BEGIN_PROFILE("GetActionButtonHandler")
+ if( mpPropHandler->GetProp() == 0 )
+ {
+ mpPropHandler->Exit( this );
+ RemoveActionButtonHandler( mpPropHandler );
+ }
+END_PROFILE("GetActionButtonHandler")
+ */
+
+BEGIN_PROFILE("UpdateFootPlant")
+ if( !IsInCar() )
+ {
+ if( radTimeGetMicroseconds64()-mLastInteriorLoadCheck > 3000000 ) //(amortise/3s) if it's been more than 3 sec's since we last checked for volumes
+ {
+ rmt::Vector posn;
+ GetPosition(&posn);
+ GetTriggerVolumeTracker()->ActivateNearestInteriorLoadZone(0, posn, 40.0f);
+ mLastInteriorLoadCheck = radTimeGetMicroseconds64();
+ }
+
+ UpdateFootPlant( );
+ }
+END_PROFILE("UpdateFootPlant")
+
+ UpdateShock( timeins );
+
+ if(mDoKickwave && mKickwaveController)
+ {
+ mKickwaveController->Advance(timeins * 1000.0f);
+ if(mKickwaveController->LastFrameReached())
+ {
+ mDoKickwave = false;
+ }
+ }
+ }
+
+ rmt::Vector tmp, tmpDir;
+ GetPosition(tmp);
+ tmpDir.Sub(tmp, mLastFramePos);
+ mVelocity = tmpDir / timeins;
+
+ // if movement is too great while not in car, reset velocity & reset position to last frame's pos
+ // this is to stop the player character from going out of world or warping too high in the sky,
+ // or too low below ground, etc...
+
+ const float DIFF_SQR_IN_POS_TOO_HIGH_RESET = 2500.0f;
+
+ CharacterAi::CharacterState state = GetStateManager()->GetState();
+
+ if( !mIsNPC &&
+ ( state == CharacterAi::LOCO || state == CharacterAi::INSIM )&&
+ ( tmpDir.MagnitudeSqr() > DIFF_SQR_IN_POS_TOO_HIGH_RESET ) )
+ {
+ //rAssertMsg( false, "Player character got moved REALLY far in one frame... Using last frame pos!\n" );
+ mVelocity.Set( 0.0f, 0.0f, 0.0f );
+ SetPosition( mLastFramePos );
+ mpJumpLocomotion->SetRootPosition( WorldToLocal( mLastFramePos ) );
+ this->SetGroundPoint( mLastFramePos );
+ }
+ else
+ {
+ if(mCollisionAreaIndex != -1)
+ {
+ if(GetWorldPhysicsManager()->FenceSanityCheck(mCollisionAreaIndex, mLastFramePos, tmp, &tmp))
+ {
+ SetPosition( tmp );
+ mpJumpLocomotion->SetRootPosition( WorldToLocal( tmp ) );
+ this->SetGroundPoint( tmp );
+ }
+ }
+
+ mLastFramePos = tmp;
+ }
+
+BEGIN_PROFILE("MoveInWorldScene")
+ MoveInWorldScene();
+END_PROFILE("MoveInWorldScene")
+}
+
+void Character::ResolveCollisions(void)
+{
+ mCollidedThisFrame = false;
+ sim::SimState* simState = mpSimStateObj; //GetSimState();
+ if( simState != NULL )
+ {
+ if( simState->GetControl() == sim::simAICtrl )
+ {
+ bool inSR1Or2 = false;
+ Mission* m = GetGameplayManager()->GetCurrentMission();
+ if( m )
+ {
+ inSR1Or2 = m->mIsStreetRace1Or2;
+ }
+ bool shouldSolveCollisions = !IsInCar() &&
+ (!mIsNPC || (mIsNPC && !inSR1Or2));
+
+
+ if( shouldSolveCollisions )
+ {
+ rmt::Vector desiredPos = LocalToWorld( mpPuppet->GetPosition( ) );
+ rmt::Vector outPos;
+BEGIN_PROFILE("SolveCollisionWithStatic")
+ Character::eCollisionType collisionType = SolveCollisionWithStatic( desiredPos, outPos );
+END_PROFILE("SolveCollisionWithStatic")
+
+ if( collisionType & Character::HitHead )
+ {
+ if(mpJumpLocomotion->IsJumpState(JumpAction::Jump))
+ {
+ GetEventManager()->TriggerEvent(EVENT_HIT_HEAD, this);
+
+ if(!mbIsStanding && (mVelocity.y > 0.0f))
+ {
+ mpJumpLocomotion->Reset(0.0f, true);
+
+ if(!IsNPC())
+ {
+ outPos.y -= 0.01f;
+ }
+ }
+ }
+ }
+
+ if ( collisionType & ( Character::HitWall | Character::HitHead) )
+ {
+ // Fix the character position.
+ //
+ SetPosition( outPos );
+ mpJumpLocomotion->SetRootPosition( WorldToLocal( outPos ) );
+ this->SetGroundPoint( outPos);
+ mCollidedThisFrame = true;
+ }
+
+ }
+ }
+ }
+}
+
+/*
+==============================================================================
+Character::UpdateFootPlant
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateFootPlant( void )
+{
+ int legs = mpPuppet->GetLegCount( );
+ for ( int i = 0; i < legs; i++ )
+ {
+ bool bFootPlanted = mpPuppet->IsFootPlanted( i );
+ if ( bFootPlanted )
+ {
+ if ( mbWasFootPlanted[ i ] == false )
+ {
+ // foot planted.
+ //
+
+ // If were are dashing around (turbo) and the foot just made
+ // contact with the ground, kick up a dust cloud
+ // also make sure the user isnt just holding in the turbo button while
+ // the character is just standing still
+ //
+
+
+ if ( GetDesiredSpeed() > 1.0f )
+ {
+ /* footprints
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( mpPuppet->GetFootPosition( i ) );
+
+ GetFootprintManager()->CreateFootprint( transform, FootprintManager::eSquishies );
+ */
+
+ // no puffs in interior
+ if(!GetInteriorManager()->IsInside())
+ {
+ rmt::Vector facing;
+ this->GetFacing( facing );
+ GetSparkleManager()->AddDash( mpPuppet->GetFootPosition( i ), facing, GetDesiredSpeed() * 0.125f );
+ }
+
+ if(this == GetCharacterManager()->GetCharacter(0))
+ {
+ GetEventManager()->TriggerEvent( EVENT_FOOTSTEP, this );
+ }
+ }
+ }
+ }
+ mbWasFootPlanted[ i ] = bFootPlanted;
+ }
+}
+/*
+==============================================================================
+Character::OnPostSimUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::OnPostSimUpdate( float timeins )
+{
+}
+
+/*
+==============================================================================
+Character::UpdateShock
+==============================================================================
+Description: Comment
+
+Parameters: ( float delta time in seconds )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateShock( float timeins )
+{
+ if ( m_IsBeingShocked )
+ {
+ // UpdateDesiredDirAndSpeed( rmt::Vector( 0,0,0 ) );
+ m_TimeLeftToShock -= timeins;
+
+ if ( IsNPC() == false )
+ {
+ float blur = m_TimeLeftToShock * 2.0f;
+ if ( blur > 1.0f ) blur = 1.0f;
+ GetRenderManager()->SetBlurAlpha( blur );
+ }
+ if ( m_TimeLeftToShock <= 0 )
+ {
+ mpCharacterRenderable->SetShocked( false );
+ m_IsBeingShocked = false;
+ }
+ }
+ else
+ {
+ if ( IsNPC() == false )
+ GetRenderManager()->SetBlurAlpha( 0 );
+ }
+}
+
+
+/*
+==============================================================================
+Character::AddToWorldScene
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::AddToWorldScene( void )
+{
+ if( !mVisible && mManaged )
+ {
+ UpdatePuppet( 0.0f );
+
+ choreo::Puppet* pPuppet = GetPuppet( );
+ rAssert( pPuppet );
+
+ pPuppet->UpdatePose();
+
+ if(mpCharacterRenderable->GetDrawable())
+ {
+ mpPuppet->GetP3DPose()->SetSkeleton(mpCharacterRenderable->GetDrawable()->GetSkeleton());
+ pPuppet->UpdateEnd();
+ }
+
+ UpdateSimState( 0.0f );
+
+ // Call updateBBox so the DSG knows where to put this guy at the start.
+ rmt::Box3D dummyBox;
+ UpdateBBox( dummyBox );
+
+ rAssert( mpWorldScene == 0 );
+ mpWorldScene = ((WorldRenderLayer*)GetRenderManager()->mpLayer(RenderEnums::LevelSlot))->pWorldScene();
+ mpWorldScene->Add( this );
+
+ //UpdateProps( 0.0f );
+
+ mVisible = true;
+ }
+}
+/*
+==============================================================================
+Character::RemoveFromWorldScene
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return:
+
+=============================================================================
+*/
+void Character::RemoveFromWorldScene( void )
+{
+ if( mVisible )
+ {
+ mpWorldScene->Remove( this );
+ mpWorldScene = 0;
+ mVisible = false;
+ }
+}
+
+
+
+void Character::MoveInWorldScene( void )
+{
+ if( mVisible )
+ {
+ rmt::Box3D oldBox;
+ UpdateBBox( oldBox );
+ // now move!
+ //
+ mpWorldScene->Move(oldBox, (IEntityDSG*)this);
+ }
+}
+
+/*
+==============================================================================
+Character::UpdateSimState
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateSimState( float timeins )
+{
+ sim::SimState* simState = mpSimStateObj; //GetSimState();
+ if( simState != 0 )
+ {
+ if( simState->GetControl() == sim::simAICtrl )
+ {
+ /*
+ poser::Transform poserTrans = mpPuppet->GetRootTransform();
+ rmt::Matrix matrix = poserTrans.GetMatrix();
+ simState->SetTransform( matrix, timeins );
+ */
+
+ rmt::Matrix matrix;
+ rmt::Vector facing;
+ rmt::Vector side;
+ rmt::Vector position;
+
+ GetFacing(facing);
+ GetPosition( position );
+
+ side.CrossProduct(facing, rmt::Vector(0.0f, 1.0f, 0.0f));
+
+ // leaning while in jump or when doing idle is *very* likely to push us through stuff
+ bool dontLean = GetJumpLocomotionAction()->IsInJump() || mbIsPlayingIdleAnim;
+
+ if(!dontLean)
+ {
+ dontLean = mLean.Dot(rmt::Vector(0.0f, 1.0f, 0.0f)) > 0.925;
+ }
+
+ matrix.Identity();
+ matrix.Row(0) = side;
+ matrix.Row(1) = dontLean ? rmt::Vector(0.0f, 1.0f, 0.0f) : mLean;
+ matrix.Row(2).CrossProduct(side, mLean);
+ matrix.Row(3) = position;
+
+ rmt::Matrix oldMat = simState->GetTransform( );
+ simState->SetTransform( matrix, timeins );
+ simState->GetCollisionObject()->Update();
+
+ // TODO:
+ // Do we really need to update velocity here??
+ rmt::Vector velXZ = simState->VelocityState().mLinear;
+ velXZ.y = 0.0f;
+ mfSpeed = velXZ.Magnitude( );
+ }
+ else if( simState->GetControl() == sim::simSimulationCtrl )
+ {
+ // If simstate velocities are too great, fudge them here. We don't want characters flying too far away...
+ rmt::Vector vel = simState->VelocityState().mLinear;
+ float limit = 15.0f;
+ rAssert( limit >= 0.0f );
+ float linearSpdMps = vel.Length(); // *** SQUARE ROOT! ***
+ if( linearSpdMps > limit )
+ {
+ simState->VelocityState().mLinear.Scale( limit/linearSpdMps );
+ }
+
+ // Update Character's speed
+ rmt::Vector velXZ = simState->VelocityState().mLinear;
+ velXZ.y = 0.0f;
+ mfSpeed = velXZ.Magnitude( );
+
+ // Update Character's facing and position
+ rmt::Matrix matrix = (rmt::Matrix) simState->GetTransform();
+ rmt::Vector negZed( 0.0f, 0.0f, -1.0f );
+
+ poser::Transform transform( matrix );
+ mpPuppet->SetRootTransform( transform );
+
+ poser::Pose* pPose = mpPuppet->GetPose( );
+ // stuff fixed up root transform into joint
+ poser::Joint* joint = pPose->GetJoint( 0 );
+ joint->SetWorldTransform( transform );
+ joint->SetObjectTransform( transform );
+ }
+ }
+}
+
+/*
+==============================================================================
+Character::UpdateBBox
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Box3D& oldBox )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateBBox( rmt::Box3D& oldBox )
+{
+ oldBox = mBBox;
+
+ GetPosition( mPosn );
+
+ mBBox.low = mPosn;
+ mBBox.high = mPosn;
+
+ mBBox.high += mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+ mBBox.low -= mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+
+ mSphere.centre.Sub(mBBox.high,mBBox.low);
+ mSphere.centre *= 0.5f;
+ mSphere.centre.Add(mBBox.low);
+ mSphere.radius = mpSimStateObj->GetCollisionObject()->GetCollisionVolume()->mSphereRadius;
+}
+// Implements CollisionEntityDSG
+//
+/*
+==============================================================================
+Character::PreReactToCollision
+==============================================================================
+Description: Comment
+
+Parameters: ( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+
+Return: bool
+
+=============================================================================
+*/
+sim::Solving_Answer Character::PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+{
+ if((inCollision.mCollisionVolumeA->Type() == sim::BBoxVolumeType) ||
+ (inCollision.mCollisionVolumeB->Type() == sim::BBoxVolumeType))
+ {
+ return sim::Solving_Continue;
+ }
+
+ ////////////////////////////////////////////
+ // Deal properly with ignoring targetvehicle pointer
+ //
+ if( this->mpTargetVehicle && (pCollidedObj->mAIRefPointer == this->mpTargetVehicle))
+ {
+ return sim::Solving_Aborted;
+ }
+
+ if( this->mpTargetVehicle && this->mpTargetVehicle->mVehicleDestroyed)
+ {
+ if(pCollidedObj->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ Vehicle* v = (Vehicle*)pCollidedObj->mAIRefPointer;
+ if(v->mVehicleID == VehicleEnum::HUSKA)
+ {
+ Vehicle* ov = GetVehicleCentral()->mHuskPool.FindOriginalVehicleGivenHusk(v);
+ if(ov && (ov == this->mpTargetVehicle))
+ {
+ return sim::Solving_Aborted;
+ }
+ }
+ }
+ }
+
+ if( this->mpTargetVehicle && this->mpTargetVehicle->GetDriver() &&
+ (pCollidedObj->mAIRefPointer == this->mpTargetVehicle->GetDriver()))
+ {
+ return sim::Solving_Aborted;
+ }
+ /////////////////////////////////////////
+
+ // Remember that we've legitimately collided with a vehicle
+ if( pCollidedObj->mAIRefIndex == PhysicsAIRef::redBrickVehicle )
+ {
+ mbCollidedWithVehicle = true;
+ }
+
+ // Don't store collision data if we're under simulation control
+ if( mpSimStateObj->GetControl() == sim::simSimulationCtrl )
+ {
+ // When a traffic vehicle hits an NPC (or vice versa) while in simulation control...
+ if( (pCollidedObj->mAIRefIndex == PhysicsAIRef::redBrickVehicle) &&
+ (mpSimStateObj->mAIRefIndex == PhysicsAIRef::NPCharacter) &&
+ (GetStateManager()->GetState() == CharacterAi::LOCO || GetStateManager()->GetState() == CharacterAi::INSIM) )
+ {
+
+ sim::Collision theCollision = inCollision;
+ sim::SimState* pSimState = theCollision.mCollisionObjectB->GetSimState();
+ if( pSimState->mAIRefPointer == this &&
+ (pSimState->mAIRefIndex == PhysicsAIRef::NPCharacter ||
+ pSimState->mAIRefIndex == PhysicsAIRef::PlayerCharacter) )
+ {
+ theCollision.mNormal.Scale( -1.0f );
+ if(theCollision.GetPositionB().y - mGroundY < 0.05f)
+ {
+ return sim::Solving_Continue;
+ }
+ }
+ else
+ {
+ if(theCollision.GetPositionA().y - mGroundY < 0.05f)
+ {
+ return sim::Solving_Continue;
+ }
+ }
+
+ // Grab vehicle's speed and if it's greater than threshold,
+ // transit to simulation control.
+ Vehicle* v = (Vehicle*) pCollidedObj->mAIRefPointer;
+ rAssert( v );
+
+ float vSpeedMps = v->mSpeed;
+ float speedThreshold = 1.0f;
+ if( vSpeedMps > speedThreshold && v->mVehicleType == VT_TRAFFIC )
+ {
+ /*
+ const int MAX_RAND_MODULUS = 3;
+ const float THEORETICAL_MAX_SPEED_MPS = 28.0f;
+
+ float speedRatio = v->mSpeed / THEORETICAL_MAX_SPEED_MPS;
+
+ const float BASE_SPEED_COMPONENT_MOD_MPS = 2.0f;
+
+ // Impart immediate vertical velocity! YA!
+ float randX, randY, randZ;
+ randX = ((float)(rand()%MAX_RAND_MODULUS) + BASE_SPEED_COMPONENT_MOD_MPS) * speedRatio;
+ randY = ((float)(rand()%MAX_RAND_MODULUS) + BASE_SPEED_COMPONENT_MOD_MPS) * speedRatio;
+ randZ = ((float)(rand()%MAX_RAND_MODULUS) + BASE_SPEED_COMPONENT_MOD_MPS) * speedRatio;
+
+ rmt::Vector impactLinearVel( 0.0f, randY, 0.0f );
+ rmt::Vector impactAngularVel( randX, randY, randZ);
+
+ impactLinearVel += v->GetFacing() + theCollision.mNormal;
+ */
+ rmt::Vector impactLinearVel = theCollision.mNormal * 1.4f;
+
+
+ rmt::Vector& linearVel = mpSimStateObj->GetLinearVelocity();
+ linearVel.Add( impactLinearVel );
+ //rmt::Vector& angularVel = mpSimStateObj->GetAngularVelocity();
+ //angularVel.Add( impactAngularVel );
+ }
+ }
+ return sim::Solving_Continue;
+ }
+
+ //rAssert( mCurrentCollision < CollisionData::MAX_COLLISIONS );
+ if( mCurrentCollision >= CollisionData::MAX_COLLISIONS )
+ {
+ return sim::Solving_Continue;//false;
+ }
+
+ if( //mpSimStateObj->mAIRefIndex == PhysicsAIRef::PlayerCharacter &&
+ pCollidedObj->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveableGroundPlane )
+ {
+ return sim::Solving_Aborted;
+ }
+
+ //
+ // store some value for when player character (me) collides with another character
+ //
+ if( mpSimStateObj->mAIRefIndex == PhysicsAIRef::PlayerCharacter &&
+ pCollidedObj->mAIRefIndex == PhysicsAIRef::NPCharacter )
+ {
+ GetEventManager()->TriggerEvent( EVENT_PC_NPC_COLLISION, pCollidedObj->mAIRefPointer );
+ }
+ static bool bTestNormals = true;
+ static float sfEdgeTune = 0.6f;
+ sim::Collision theCollision = inCollision;
+ // recall:
+ // mPositionA = mPositionB + mNormal * mDistance
+ // We want the normal to point from Object to Homer.
+ // Normals always point from B to A.
+ // So, if homer is B, then flip the normal.
+ //
+
+ sim::SimState* pSimState = theCollision.mCollisionObjectB->GetSimState();
+ if( pSimState->mAIRefPointer == this &&
+ (pSimState->mAIRefIndex == PhysicsAIRef::NPCharacter ||
+ pSimState->mAIRefIndex == PhysicsAIRef::PlayerCharacter) )
+ {
+ theCollision.mNormal.Scale( -1.0f );
+ if(theCollision.GetPositionB().y - mGroundY < 0.05f)
+ {
+ return sim::Solving_Continue;
+ }
+ }
+ else
+ {
+ if(theCollision.GetPositionA().y - mGroundY < 0.05f)
+ {
+ return sim::Solving_Continue;
+ }
+ }
+
+ if ( theCollision.mCollisionVolumeA == mpStandingCollisionVolume
+ || theCollision.mCollisionVolumeB == mpStandingCollisionVolume )
+ {
+ return sim::Solving_Continue;//false;
+ }
+
+ // When a vehicle hits us (or vice versa), transit to simulation control
+ // so physics take over if we are in locomiotion (for now, don't do this
+ // if we are not in loco (i.e. getting in or out of car) or state can get
+ // badly screwed
+ if( (pCollidedObj->mAIRefIndex == PhysicsAIRef::redBrickVehicle) &&
+ (GetStateManager()->GetState() == CharacterAi::LOCO ||
+ GetStateManager()->GetState() == CharacterAi::INSIM) )
+ {
+ // Grab vehicle's speed and if it's greater than threshold,
+ // transit to simulation control.
+ Vehicle* v = (Vehicle*) pCollidedObj->mAIRefPointer;
+ rAssert( v );
+
+ float vSpeedMps = v->mSpeed;
+
+ float speedThreshold = 1.0f;
+ if( vSpeedMps > speedThreshold )
+ {
+ if( mpSimStateObj->mAIRefIndex == PhysicsAIRef::NPCharacter )
+ {
+ if( v == GetAvatarManager()->GetAvatarForPlayer( 0 )->GetVehicle() )
+ {
+ GetEventManager()->TriggerEvent( EVENT_PLAYER_MAKES_LIGHT_OF_CAR_HITTING_NPC );
+ GetEventManager()->TriggerEvent( EVENT_PLAYER_CAR_HIT_NPC, this );
+ mHasBeenHit = true;
+ }
+
+ /////////////////////////////
+ // We're entering simulation
+ /////////////////////////////
+ mpSimStateObj->SetControl( sim::simSimulationCtrl );
+ GetWorldPhysicsManager()->EnableGroundPlaneCollision(mGroundPlaneIndex);
+
+
+ // If a traffic vehicle hit us, its simstate will have zero velocity, so
+ // it won't be imparting anything upon us... So we fake it...
+ if( v->mVehicleType == VT_TRAFFIC )
+ {
+
+ //const int MIN_RAND_MODULUS = 1;
+ const int MAX_RAND_MODULUS = 6;
+ const float THEORETICAL_MAX_SPEED_MPS = 28.0f;
+
+ float speedRatio = v->mSpeed / THEORETICAL_MAX_SPEED_MPS;
+
+ //int delta = MAX_RAND_MODULUS - MIN_RAND_MODULUS;
+ //int randMod = (int)(speedRatio * delta) + MIN_RAND_MODULUS;
+ //rAssert( randMod >= MIN_RAND_MODULUS );
+
+ const float BASE_SPEED_COMPONENT_MOD_MPS = 6.0f;
+
+ // Impart immediate vertical velocity! YA!
+ float randX, randY, randZ;
+ randX = ((float)(rand()%MAX_RAND_MODULUS) + BASE_SPEED_COMPONENT_MOD_MPS) * speedRatio;
+ randY = ((float)(rand()%MAX_RAND_MODULUS) + BASE_SPEED_COMPONENT_MOD_MPS) * speedRatio;
+ randZ = ((float)(rand()%MAX_RAND_MODULUS) + BASE_SPEED_COMPONENT_MOD_MPS) * speedRatio;
+
+ rmt::Vector impactLinearVel( 0.0f, randY, 0.0f );
+ rmt::Vector impactAngularVel( randX, randY, randZ);
+
+ impactLinearVel += v->GetFacing() + theCollision.mNormal;
+
+
+ rmt::Vector& linearVel = mpSimStateObj->GetLinearVelocity();
+ linearVel.Add( impactLinearVel );
+ rmt::Vector& angularVel = mpSimStateObj->GetAngularVelocity();
+ angularVel.Add( impactAngularVel );
+ }
+
+ }
+ else // it's the player character that's getting hit by a vehicle
+ {
+ // Ignore all other cars except VT_AI cars
+ // need to break out and keep solving though
+ if(((Vehicle*)pCollidedObj->mAIRefPointer)->mVehicleType != VT_AI )
+ {
+ goto KeepSolving;
+ }
+
+ if( !IsInCar() )
+ {
+
+ // ignore "up" collision normals (presumably we're standing on top
+ // of a traffic car)
+ const float TOP_OF_VEHICLE_COS_ALPHA = 0.9848077f; // approx 10 degrees
+ float dp = theCollision.mNormal.Dot( mRealGroundNormal );
+
+ // if deviate by > 10 degrees from ground normal, then we're
+ // probably not standing on it.
+ if( dp < TOP_OF_VEHICLE_COS_ALPHA )
+ {
+
+ /////////////////////////////
+ // We're entering simulation
+ /////////////////////////////
+
+ // Transit to Simulation control and set up its ground plane...
+ // Didn't need to be done for NPCs cuz they would have been
+ // submitted by other dynamics when hit (and would have obtained
+ // their ground plane at that point)
+ AddToSimulation();
+
+ rAssert( mGroundPlaneSimState );
+ mGroundPlaneSimState->GetCollisionObject()->SetCollisionEnabled( true );
+
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ if(v->mName)
+ {
+ if(chaseManager->IsModelRegistered(v->mName))
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_AND_RUN_CAUGHT, NULL );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+// return sim::Solving_Continue;
+ }
+ }
+
+KeepSolving:
+
+ if ( bTestNormals )
+ {
+ for ( int i = 0; i < mCurrentCollision; i++ )
+ {
+ // Test each collision normal to see if it is a duplicate of a collision we
+ // have already stored.
+ //
+ if( ( mCollisionData[ i ].mpCollisionVolume == theCollision.mCollisionVolumeA ||
+ mCollisionData[ i ].mpCollisionVolume == theCollision.mCollisionVolumeB ) )
+ {
+ rAssert( mbCollided );
+
+ rmt::Vector facing;
+ GetFacing(facing);
+
+ bool store = ( mCollisionData[ i ].mCollisionNormal.DotProduct(facing) > theCollision.mNormal.DotProduct(facing));
+
+ if(store)
+ {
+ mCollisionData[ i ].mCollisionDistance = theCollision.mDistance;
+ GetPosition( mCollisionData[ i ].mCollisionPosition );
+ mCollisionData[ i ].mCollisionNormal = theCollision.mNormal;
+ mCollisionData[ i ].mpCollisionVolume = theCollision.mCollisionVolumeA == mpSimStateObj->GetCollisionObject()->GetCollisionVolume() ? theCollision.mCollisionVolumeB : theCollision.mCollisionVolumeA;
+ }
+
+
+ return sim::Solving_Continue;//true;
+ }
+ }
+ }
+
+
+ // Tests to see if we should skip this collision.
+ //
+ mbCollided = true;
+
+ mCollisionData[ mCurrentCollision ].mCollisionDistance = theCollision.mDistance;
+ GetPosition( mCollisionData[ mCurrentCollision ].mCollisionPosition );
+ mCollisionData[ mCurrentCollision ].mCollisionNormal = theCollision.mNormal;
+ mCollisionData[ mCurrentCollision ].mpCollisionVolume = theCollision.mCollisionVolumeA == mpSimStateObj->GetCollisionObject()->GetCollisionVolume() ? theCollision.mCollisionVolumeB : theCollision.mCollisionVolumeA;
+
+ mCurrentCollision++;
+
+ return sim::Solving_Continue;//true;
+
+}
+
+
+//=============================================================================
+// Character::PostReactToCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+//
+// Return: sim
+//
+//=============================================================================
+sim::Solving_Answer Character::PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision)
+{
+ return sim::Solving_Continue;
+}
+
+
+/*
+==============================================================================
+Character::SolveCollisionWithStatic
+==============================================================================
+Description: desiredPos is the new position of the character after the
+ choreo::UpdateRoot( ) call.
+ the position of the character at the time of collision
+ was stored in the mCollisionData array.
+
+Parameters: ( const rmt::Vector& desiredPos )
+
+Return: rmt
+
+=============================================================================
+*/
+Character::eCollisionType Character::SolveCollisionWithStatic( const rmt::Vector& desiredPos, rmt::Vector& outPos )
+{
+ static bool sbOutput = false;
+
+ outPos = desiredPos; //.Set( 0.0f, 0.0f, 0.0f );
+ if ( !mbSolveCollisions )
+ {
+ return NoCollision;
+ }
+ eCollisionType collisionType = NoCollision;
+ int intCollisionType = collisionType;
+ int i = 0;
+
+ if(mCurrentCollision > 0)
+ {
+ if ( sbOutput ) rDebugPrintf( "solving %d\n", mCurrentCollision);
+ }
+
+ // unsightly hack to try and deal with multiple collisions trying
+ // to push you through walls
+ if(mCurrentCollision > 1)
+ {
+ bool lock = true;
+
+ if(mCurrentCollision == 2)
+ {
+ if( mCollisionData[ 0 ].mCollisionNormal.Dot(mCollisionData[ 1 ].mCollisionNormal) > 0.5f)
+ {
+ lock = false;
+ }
+ }
+
+ int nAway = 0;
+ rmt::Vector facing;
+ GetFacing(facing);
+
+ for(int i = 0; i < mCurrentCollision; i++)
+ {
+ if( mCollisionData[ i ].mCollisionNormal.Dot(facing) > 0.0f)
+ {
+ nAway++;
+ }
+ }
+
+ if(nAway == mCurrentCollision)
+ {
+ lock = false;
+ }
+
+ for(int i = 0; i < mCurrentCollision; i++)
+ {
+ if( mCollisionData[ i ].mCollisionNormal.y > 0.1f)
+ {
+ lock = false;
+ break;
+ }
+ }
+
+ if(lock)
+ {
+ if ( sbOutput ) rDebugPrintf( "locking\n", mCurrentCollision);
+ outPos = mLastFramePos;
+ if(this->GetJumpLocomotionAction()->IsInJump())
+ {
+ outPos.y = desiredPos.y;
+
+ for(int i = 0; i < mCurrentCollision; i++)
+ {
+ if ( mCollisionData[ i ].mCollisionNormal.y <= -0.5f )
+ {
+ return HitHead;
+ }
+ }
+ }
+ return HitWall;
+ }
+ }
+
+ int numSlides = 0;
+
+ for(i = 0; i < mCurrentCollision; i++)
+ {
+ if( mCollisionData[ i ].mCollisionNormal.y > 0.1f)
+ {
+ numSlides += 1;
+ }
+ }
+
+ for ( i = 0; i < mCurrentCollision; i++ )
+ {
+ if( !CanStandOnCollisionNormal( mCollisionData[ i ].mCollisionNormal ) )
+ {
+
+ // Desired motion vector.
+ //
+ rmt::Vector diffVect;
+
+ // outPos is the solved position. So it will change each time
+ // through the loop.
+ //
+ static bool sbUseOutpos = true;
+ rmt::Vector prevPos = mCollisionData[ i ].mCollisionPosition; //GetPuppet( )->GetPrevPosition( );
+ if ( sbUseOutpos )
+ diffVect.Sub( outPos, prevPos );
+ else
+ diffVect.Sub( desiredPos, prevPos );
+
+ // dampen the y a little, to avoid multiple sliding surfaces
+ // actually holding us in the air
+ if( mCollisionData[ i ].mCollisionNormal.y > 0.1f)
+ {
+ diffVect.y *= 1.0f / float(numSlides);
+ }
+
+ // Normalized desired motion vector.
+ //
+ rmt::Vector normalizeDiff = diffVect;
+
+ // The distance we want to travel this frame.
+ //
+ float dist = normalizeDiff.NormalizeSafe();
+
+ // handle the case when the character is not moving,
+ // but an animated collision volume has collided with it.
+ //
+ if ( dist == 0.0f )
+ {
+ // Possible solution. Take the collision normal,
+ // and use that for 'normalizeDiff'. ie assume the character
+ // is moving (relatively) in the direction of the normal.
+ //
+ normalizeDiff = mCollisionData[ i ].mCollisionNormal;
+ normalizeDiff.Scale(-1.0f);
+ dist = 0.0f;
+ }
+ // The dot will tell us if we are moving into ( dot < 0 )
+ // the collision or away from the collision (dot >= 0 )..
+ //
+ float dot = normalizeDiff.DotProduct( mCollisionData[ i ].mCollisionNormal );
+ rmt::Vector adjPos = mCollisionData[ i ].mCollisionNormal;
+ bool bSolve = true;
+ if ( bSolve )
+ {
+ // Scale the distance against the collision normal.
+ //
+ float tempDist = dist;
+ tempDist *= -dot;
+
+ // We only want to add the collision distance to
+ // tempDist (the distance we will scale the motion vector)
+ // when the collDist is gt zero, ie NOT interpentrating.
+ // If we are interpenetrating, NOT adding the collDist
+ // will snap us to the surface of the collision.
+ //
+ static bool sbPositive = true;
+ static bool sbNegative = true;
+ if ( mCollisionData[ i ].mCollisionDistance < 0.0f )
+ {
+ if ( sbNegative )
+ {
+ tempDist -= mCollisionData[ i ].mCollisionDistance;
+ if ( sbOutput ) rDebugPrintf( "collision %.4f (%.2f, %.2f, %.2f) %s\n",
+ mCollisionData[ i ].mCollisionDistance,
+ mCollisionData[ i ].mCollisionNormal.x,
+ mCollisionData[ i ].mCollisionNormal.y,
+ mCollisionData[ i ].mCollisionNormal.z,
+ mCollisionData[ i ].mpCollisionVolume->GetCollisionObject( )->GetName( ) );
+ }
+ }
+ else if ( mCollisionData[ i ].mCollisionDistance > 0.0f )
+ {
+ if( sbPositive )
+ {
+ tempDist += mCollisionData[ i ].mCollisionDistance;
+ if ( sbOutput ) rDebugPrintf( "collision %.4f (%.2f, %.2f, %.2f) %s\n",
+ mCollisionData[ i ].mCollisionDistance,
+ mCollisionData[ i ].mCollisionNormal.x,
+ mCollisionData[ i ].mCollisionNormal.y,
+ mCollisionData[ i ].mCollisionNormal.z,
+ mCollisionData[ i ].mpCollisionVolume->GetCollisionObject( )->GetName( ) );
+ }
+ }
+ adjPos.Scale( tempDist );
+
+ rmt::Vector tmpOutPos = outPos;
+ outPos.Add( adjPos );
+
+ // if the collisionNormal is straight down.
+ //
+ if ( mCollisionData[ i ].mCollisionNormal.y <= -0.5f )
+ {
+ // Ouch, you hit your head!
+ //
+ intCollisionType |= HitHead;
+ if(!IsNPC())
+ {
+ outPos.y -= 0.1f;
+ }
+ }
+ else
+ {
+
+ // D'oh. You ran into a wall.
+ //
+ // if we're surfing and the wall belongs to the car we're
+ // on, then we can ignore it. Physics gives us weird collisions
+ // from time to time, causing us to get shoved off the car
+ // we're surfing on.
+ //
+ bool ignoreThisOne = false;
+
+ if( mbSurfing && (this->GetLocoVelocity().MagnitudeSqr() < 0.001f) && mpStandingCollisionVolume)
+ {
+ sim::SimState* surfSimState = mpStandingCollisionVolume->GetCollisionObject( )->GetSimState();
+
+ rAssert( surfSimState->mAIRefIndex == PhysicsAIRef::redBrickVehicle );
+
+ sim::SimState* objSimState = mCollisionData[ i ].mpCollisionVolume->GetCollisionObject()->GetSimState();
+
+ if( objSimState->mAIRefIndex == PhysicsAIRef::redBrickVehicle &&
+ (Vehicle*)(objSimState->mAIRefPointer) == (Vehicle*)(surfSimState->mAIRefPointer) )
+ {
+ ignoreThisOne = true;
+ }
+ }
+
+ if( ignoreThisOne )
+ {
+ outPos = tmpOutPos;
+ }
+ else
+ {
+ intCollisionType |= HitWall;
+ }
+ }
+
+ if( intCollisionType != NoCollision )
+ {
+ // if we hit a static or a fence piece
+ sim::SimState* simState = mCollisionData[ i ].mpCollisionVolume->
+ GetCollisionObject()->GetSimState();
+
+ if( mIsNPC &&
+ (simState->mAIRefIndex == PhysicsAIRef::redBrickPhizFence ||
+ simState->mAIRefIndex == PhysicsAIRef::redBrickPhizStatic) )
+ {
+ // if we're not panicking, this call will have no effect
+ ((NPCController*)GetController())->QuellPanic();
+ }
+
+ }
+ }
+ else
+ {
+ if ( sbOutput ) rDebugPrintf( "Dot product reject. dot = %.2f, dist = %.6f, %s\n",
+ dot,
+ mCollisionData[ i ].mCollisionDistance,
+ mCollisionData[ i ].mpCollisionVolume->GetCollisionObject( )->GetName( )
+ );
+ }
+ }
+ else
+ {
+ if ( sbOutput ) rDebugPrintf( "CollisionNormal reject: %.2f, %.2f, %.2f, %s\n",
+ mCollisionData[ i ].mCollisionNormal.x,
+ mCollisionData[ i ].mCollisionNormal.y,
+ mCollisionData[ i ].mCollisionNormal.z,
+ mCollisionData[ i ].mpCollisionVolume->GetCollisionObject( )->GetName( )
+ );
+ }
+ }
+ collisionType = (eCollisionType)intCollisionType;
+ return collisionType;
+}
+/*
+==============================================================================
+Character::GetMaxSpeed
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+float Character::GetMaxSpeed( void ) const
+{
+ float fTurbo = 0.0f;
+ if ( IsTurbo( ) )
+ {
+ fTurbo = CharacterTune::sfDashBurstMax;
+ }
+ return CharacterTune::sfMaxSpeed + fTurbo;
+}
+
+/*
+==============================================================================
+Character::GetTerrainIntersect
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& pos, rmt::Vector& normal )
+
+Return: void
+
+=============================================================================
+*/
+void Character::GetTerrainIntersect( rmt::Vector& pos, rmt::Vector& normal ) const
+{
+ GetPosition( pos );
+ pos.y = mGroundY;
+ normal = mGroundNormal;
+}
+
+void Character::GetTerrainType( eTerrainType& TerrainType, bool& Interior ) const
+{
+ TerrainType = mTerrainType;
+ Interior = mInteriorTerrain;
+}
+
+void Character::SnapToGround(void)
+{
+ mbSnapToGround = true;
+ UpdateGroundHeight();
+}
+
+void Character::UpdateGroundHeight( void )
+{
+ unsigned int modulo = mbInAnyonesFrustrum ? 0x3 : 0x7;
+ if( !mbSnapToGround && IsNPC() &&
+ ((GetGame()->GetFrameCount() & modulo) != (mIntersectFrame & modulo)))
+ {
+ return;
+ }
+
+BEGIN_PROFILE("Character::UpdateGroundHeight")
+
+ choreo::Puppet* pPuppet = GetPuppet( );
+ rAssert( pPuppet );
+
+ // Before
+ //
+ rmt::Vector prevPosition = mLastFramePos;//LocalToWorld( pPuppet->GetPrevPosition( ) );
+
+ // And after!
+ //
+ rmt::Vector position;
+ GetPosition( position );
+ // Updates the cached value.
+ //
+// UpdateGroundHeight( prevPosition, position, mGroundY, mGroundNormal );
+
+ rmt::Vector groundPosition = position;
+ rmt::Vector outnorm( 0.0f, 1.0f, 0.0f );
+
+ // I don't understand these ones.
+ //
+ rmt::Vector intersectPos = position;
+ rmt::Vector intersectNorm( 0.0f, 1.0f, 0.0f );
+ bool bFoundIntersect = false;
+ bool bFoundPlane = false;
+
+ mTerrainType = static_cast<eTerrainType>( GetIntersectManager()->FindIntersection(
+ groundPosition, // IN
+ bFoundPlane, // OUT
+ outnorm, // OUT
+ groundPosition ) ); // OUT
+ mInteriorTerrain = ( (int)mTerrainType & 0x80 ) == 0x80;
+ mTerrainType = static_cast<eTerrainType>( ( (int)mTerrainType & ~0x80 ) );
+
+ if( bFoundPlane )
+ {
+ mRealGroundPos = groundPosition;
+ mRealGroundNormal = outnorm;
+ float tooHigh = 100000.0f;
+ rAssert( -tooHigh <= mRealGroundNormal.x && mRealGroundNormal.x <= tooHigh );
+ rAssert( -tooHigh <= mRealGroundNormal.y && mRealGroundNormal.y <= tooHigh );
+ rAssert( -tooHigh <= mRealGroundNormal.z && mRealGroundNormal.z <= tooHigh );
+ }
+ else
+ {
+ //
+ // If this assert goes off for the player charater when he's hit by traffic
+ // it means that Physics has placed him at some location other than his
+ // present location. This is a bad thing... possibly related to
+ // collisions with traffic cars.
+ //rAssertMsg( false, "We're SOOO far from the ground, we're not intersecting" );
+ }
+ rmt::Vector collisionPosition;
+ rmt::Vector collisionNormal;
+
+ collisionNormal.Clear( );
+ collisionPosition.Clear( );
+
+
+ rmt::Vector playerPos;
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetPosition( playerPos );
+
+ rmt::Vector distVecToPlayer = playerPos - position;
+ distVecToPlayer.y = 0.0f;
+ float distSqrFromPlayer = distVecToPlayer.MagnitudeSqr();
+
+ float delta = position.y - groundPosition.y;
+
+ const float TOO_FAR_FROM_GROUND_DIST = 2.0f; // 2 meters? Maybe that's good enough
+ const float VISIBLE_TO_PLAYER_DIST_SQR = 90000.0f;
+ const float CLOSE_ENOUGH_TO_PLAYER_DIST_SQR = 400.0f; // always check if within 20 meters
+
+ bool bOverStatic = false;
+ if(IsNPC())
+ {
+ // discretionally ray-test against objects
+ if( delta > TOO_FAR_FROM_GROUND_DIST ||
+ (!bFoundPlane && distSqrFromPlayer <= VISIBLE_TO_PLAYER_DIST_SQR) ||
+ distSqrFromPlayer < CLOSE_ENOUGH_TO_PLAYER_DIST_SQR )
+ {
+ //rDebugPrintf( "**** NPC %s calling getCollisionHeight *****\n", GetName() );
+ bOverStatic = GetCollisionHeight( prevPosition, position, collisionPosition, collisionNormal );
+ }
+ /*
+ else if( distSqrFromPlayer >= CLOSE_ENOUGH_TO_PLAYER_DIST_SQR )
+ {
+ if( strcmp( GetName(), "b_ralph" ) == 0 )
+ {
+ rDebugPrintf( "%s not within 20 meters of player\n", GetName() );
+ }
+ }
+ */
+ }
+ else
+ {
+ bOverStatic = GetCollisionHeight( prevPosition, position, collisionPosition, collisionNormal );
+ }
+
+ bool bNpcShouldNotMove = false;
+
+ if ( bOverStatic )
+ {
+ if ( !bFoundPlane || collisionPosition.y > groundPosition.y )
+ {
+ mGroundY = collisionPosition.y;
+ mGroundNormal = collisionNormal;
+
+ mLastGoodPosOverStatic = position;
+ mLastGoodPosOverStatic.y = mGroundY;
+ }
+ else
+ {
+ rAssert( bFoundPlane );
+ mGroundY = groundPosition.y;
+ mGroundNormal = outnorm;
+ }
+ }
+ else if ( bFoundPlane )
+ {
+ if( IsNPC() && mLastGoodPosOverStatic.Equals( position, 0.5f ) )
+ {
+ if( ((NPCController*)GetController())->GetState() == NPCController::TALKING_WITH_PLAYER )
+ {
+ bNpcShouldNotMove = false; // don't transit out of TALKING_WITH_PLAYER state
+ }
+ else
+ {
+ bNpcShouldNotMove = true;
+ }
+ mGroundY = position.y;
+ mGroundNormal.Set( 0.0f, 1.0f, 0.0f );
+ }
+ else
+ {
+ mGroundY = groundPosition.y;
+ mGroundNormal = outnorm;
+ }
+ }
+ else
+ {
+ bNpcShouldNotMove = true;
+ mGroundY = position.y;
+ mGroundNormal.Set( 0.0f, 1.0f, 0.0f );
+ }
+
+ if(mpSimStateObj->GetControl() == sim::simSimulationCtrl)
+ {
+ return;
+ }
+
+ if( mIsNPC )
+ {
+ // if we're dropping more than N meters, we transit to sim,
+ // but only if we have a valid ground plane (otherwise we could
+ // fall through the world!)
+ delta = position.y - mGroundY;
+ if( delta > TOO_FAR_FROM_GROUND_DIST &&
+ distSqrFromPlayer <= VISIBLE_TO_PLAYER_DIST_SQR &&
+ mGroundPlaneIndex != WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ // transit to sim here so we "fall" to the ground
+ mpSimStateObj->SetControl( sim::simSimulationCtrl );
+ GetWorldPhysicsManager()->EnableGroundPlaneCollision(mGroundPlaneIndex);
+ }
+ }
+
+
+ static float sfFallingTolerance = 0.5f;
+
+ rmt::Vector pos;
+ GetPosition( pos );
+
+ rmt::Vector dummy, groundPos;
+ GetTerrainIntersect( groundPos, dummy );
+
+ delta = pos.y - groundPos.y;
+
+ JumpAction* pJumpAction = GetJumpLocomotionAction();
+ bool inJump = pJumpAction->IsInJump();
+
+ if (( mVelocity.y <= mfGroundVerticalVelocity && (delta < 0.0f )) ||
+ ( !inJump && (delta < sfFallingTolerance)) ||
+ ( IsNPC() && mpSimStateObj->GetControl() == sim::simAICtrl ) ||
+ mbSnapToGround)
+ {
+ mbIsStanding = true;
+
+ if(mbDoGroundIntersect || mbSnapToGround)
+ {
+ mbSnapToGround = false;
+
+ if( IsNPC() )
+ {
+ NPCController* npcController = (NPCController*) GetController();
+ if( bNpcShouldNotMove )
+ {
+ npcController->TransitToState( NPCController::NONE );
+ }
+ else
+ {
+ if( npcController->GetState() == NPCController::NONE )
+ {
+ npcController->TransitToState( NPCController::FOLLOWING_PATH );
+ }
+ }
+ }
+ SetPosition(groundPos);
+ this->SetGroundPoint(groundPos);
+ mpJumpLocomotion->SetRootPosition( WorldToLocal(groundPos) );
+ }
+ }
+ else
+ {
+ mbIsStanding = false;
+
+ if ( !mbIsStanding && mbDoGroundIntersect && !inJump && (this->mpSimStateObj->GetControl() == sim::simAICtrl))
+ {
+ //falling.
+ //
+ pJumpAction->Reset( 0.0f, true );
+ GetActionController()->Clear();;
+ Sequencer* seq = GetActionController()->GetNextSequencer();
+ seq->BeginSequence();
+ seq->AddAction(pJumpAction);
+ seq->EndSequence();
+ }
+ }
+
+END_PROFILE("Character::UpdateGroundHeight")
+}
+/*
+==============================================================================
+Character::pPosition
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: rmt
+
+=============================================================================
+*/
+rmt::Vector* Character::pPosition()
+{
+ rAssert( 0 );
+ return (Vector*)0;
+}
+
+/*
+==============================================================================
+Character::rPosition
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: const
+
+=============================================================================
+*/
+const rmt::Vector& Character::rPosition()
+{
+ GetPosition(lameAssPosition);
+ return lameAssPosition;
+}
+
+/*
+==============================================================================
+Character::GetPosition
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector* ipPosn )
+
+Return: void
+
+=============================================================================
+*/
+void Character::GetPosition( rmt::Vector* ipPosn )
+{
+ GetPosition(*ipPosn);
+}
+
+//////////////////////////////////////////////////////////////////////////
+
+void Character::SetFadeAlpha( int fadeAlpha )
+{
+ if( mpCharacterRenderable != NULL )
+ {
+ mpCharacterRenderable->SetFadeAlpha( fadeAlpha );
+ }
+}
+
+int Character::CastsShadow()
+{
+ return mpCharacterRenderable->CastsShadow();
+}
+/*
+==============================================================================
+Character::DisplayShadow
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: void
+
+=============================================================================
+*/
+void Character::DisplayShadow()
+{
+ /*
+ if( !IsInCar() && !IsSimpleShadow() )
+ {
+ mpCharacterRenderable->DisplayShadow( mpPuppet->GetP3DPose() );
+ }
+ */
+}
+
+/*
+==============================================================================
+Character::DisplaySimpleShadow
+==============================================================================
+Description: Draw the simple shadow during the simple shadow pass.
+
+Parameters: ()
+
+Return: void
+
+=============================================================================
+*/
+void Character::DisplaySimpleShadow( void )
+{
+}
+
+
+//=============================================================================
+//Character::DisplaySimpleShadow
+//=============================================================================
+//Description: Draw the simple shadow during the simple shadow pass.
+//
+//Parameters: ()
+//
+//Return: void
+//=============================================================================
+bool Character::CanPlayAnimation( const tName& name ) const
+{
+ choreo::Bank* bank = mpPuppet->GetBank();
+ choreo::Animation* anim = choreo::find<choreo::Animation>( bank, name.GetUID() );
+ if( anim == NULL )
+ {
+ PrintAnimations();
+ }
+ return ( anim != NULL );
+}
+
+/*
+==============================================================================
+Character::CanStandOnCollisionVolume
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+bool Character::CanStandOnCollisionVolume( void ) const
+{
+ int i;
+
+ for ( i = 0; i < mCurrentCollision; i++ )
+ {
+ bool bCanStand = CanStandOnCollisionNormal( mCollisionData[ i ].mCollisionNormal );
+ if ( bCanStand )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+/*
+==============================================================================
+Character::CanStandOnCollisionNormal
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& normal )
+
+Return: bool
+
+=============================================================================
+*/
+bool Character::CanStandOnCollisionNormal( const rmt::Vector& normal ) const
+{
+ float dot = normal.DotProduct( vUp );
+ const float cos30 = 0.86602540378443864676372317075294f;
+ static float sfStandTolerance = cos30;
+ if ( dot > sfStandTolerance )
+ {
+ return true;
+ }
+ return false;
+}
+
+/*
+==============================================================================
+Character::CanStaggerCollision
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+bool Character::CanStaggerCollision( void ) const
+{
+ int i;
+
+ for ( i = 0; i < mCurrentCollision; i++ )
+ {
+ if ( this->mpStandingCollisionVolume != mCollisionData[ i ].mpCollisionVolume )
+ {
+ bool bCanStagger = CanStaggerCollisionNormal( mCollisionData[ i ].mCollisionNormal );
+ if ( bCanStagger )
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+/*
+==============================================================================
+Character::CanStaggerCollisionNormal
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& normal )
+
+Return: bool
+
+=============================================================================
+*/
+bool Character::CanStaggerCollisionNormal( const rmt::Vector& normal ) const
+{
+
+ static rmt::Vector facing;
+ GetFacing( facing );
+
+ float dot = normal.DotProduct( facing );
+ static float sfStaggerTolerance = -0.9f;
+ if ( dot < sfStaggerTolerance )
+ {
+ return true;
+ }
+ return false;
+}
+/*
+==============================================================================
+Character::FindStandingVolume
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& inPos, sim::CollisionVolume* inVolume, rmt::Vector& outNormal, float& outDist)
+
+Return: sim
+
+=============================================================================
+*/
+sim::CollisionVolume* Character::FindStandingVolume( const rmt::Vector& inPos, sim::CollisionVolume* inVolume, rmt::Vector& outNormal, float& outDist )
+{
+ sim::CollisionVolume* pOutVolume = 0;
+
+ switch (inVolume->Type())
+ {
+ case sim::SphereVolumeType:
+ case sim::CylinderVolumeType:
+ case sim::OBBoxVolumeType:
+ case sim::WallVolumeType:
+ {
+ float currentDist = VERY_LARGE;
+ rmt::Vector currentNormal;
+
+ // trivial reject stuff that is actually no where near us (probably a subvolume
+ // of a large volume we did intersect with)
+ if((rmt::Fabs(inVolume->mPosition.x - inPos.x) > (inVolume->mBoxSize.x + 1.0f)) ||
+ (rmt::Fabs(inVolume->mPosition.z - inPos.z) > (inVolume->mBoxSize.z + 1.0f)))
+ {
+ break;
+ }
+
+ sim::CollisionVolume* pOutSubVolume = sim::FindClosestPointOnVolume( inPos, inVolume, currentNormal, currentDist);
+ if ( pOutSubVolume && pOutSubVolume->GetCollisionObject()->GetCollisionEnabled())
+ {
+ if ( CanStandOnCollisionNormal( currentNormal ) )
+ {
+ // We must be above the object.
+ //
+ if ( currentDist >= 0.0f )
+ {
+ pOutVolume = pOutSubVolume;
+ outDist = currentDist;
+ outNormal = currentNormal;
+ }
+ }
+ }
+ break;
+ }
+ case sim::BBoxVolumeType:
+ {
+ float currentDist = VERY_LARGE;
+ rmt::Vector currentNormal;
+ for (int i=0; i<inVolume->SubVolumeList()->GetSize(); i++)
+ {
+ sim::CollisionVolume* pOutSubVolume = FindStandingVolume(inPos, inVolume->SubVolumeList()->GetAt(i), currentNormal, currentDist );
+ if ( pOutSubVolume && pOutSubVolume->GetCollisionObject()->GetCollisionEnabled())
+ {
+ // For each volume returned, keep the closest one only.
+ //
+ if ( currentDist < outDist )
+ {
+ outDist = currentDist;
+ outNormal = currentNormal;
+ pOutVolume = pOutSubVolume;
+ }
+ }
+ }
+ }
+ break;
+ case sim::MaxCollisionVolumeEnum:
+ case sim::CollisionVolumeType:
+ break;
+ }
+ return pOutVolume;
+}
+/*
+==============================================================================
+Character::GetCollisionHeight
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& prevPosition, const rmt::Vector& position, rmt::Vector& outPosition, rmt::Vector& collisionNormal )
+
+Return: bool
+
+=============================================================================
+*/
+bool Character::GetCollisionHeight( const rmt::Vector& prevPosition, const rmt::Vector& position, rmt::Vector& outPosition, rmt::Vector& collisionNormal )
+{
+BEGIN_PROFILE( "GetCollisionHeight" );
+
+ msIntersectInfo.Clear();
+
+ float fOldRayThickness = sim::RayIntersectionInfo::sRayThickness;
+ static float sfCharacterRayThickness = 0.3f;
+ sim::RayIntersectionInfo::sRayThickness = sfCharacterRayThickness;
+ const poser::Joint* pStandingJoint = 0;
+ bool bFoundIntersect = false;
+ sim::CollisionVolume* oldStanding = NULL;
+ tRefCounted::Assign(oldStanding, mpStandingCollisionVolume);
+ tRefCounted::Release(mpStandingCollisionVolume);
+
+ // Had to increase the fudge when I moved character::update calls outside
+ // of the physics substep.
+ //
+ static float sfFudge = 0.6f;
+ rmt::Vector testPosition;
+ testPosition = position;
+ testPosition.y = prevPosition.y;
+ testPosition.y += sfFudge;
+
+ float outDist = VERY_LARGE;
+ float currentDist = outDist;
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+
+ // adds in the list the collision object interfering with the ray and ordered according to their distance to the source.
+ // use sim::RayIntersectionInfo::SetMethod(method) to set the method
+ // use sim::RayIntersectionInfo::SetReturnClosestOnly(true/false) if you need only the closest object
+ // nb. if SetReturnClosestOnly(true) is used, the previous returned list can be used as a cache.
+ sim::RayIntersectionInfo::SetReturnClosestOnly( false );
+ sim::RayIntersectionInfo::SetMethod( sim::RayIntersectionBBox );
+ rmt::Vector rayOrg( 0.0f, 0.0f, 0.0f );
+ rmt::Vector rayEnd( 0.0f, -100.0f, 0.0f );
+ rayOrg.Add( testPosition );
+ rayEnd.Add( testPosition );
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+
+ // Do not allow ray test against the character collision object
+ // Otherwise, rayintersection will detect against player volume.
+ //
+ bool bRayRestore = mpSimStateObj->GetCollisionObject()->GetRayCastingEnabled( );
+ bool bCollRestore = mpSimStateObj->GetCollisionObject()->GetCollisionEnabled( );
+ mpSimStateObj->GetCollisionObject( )->SetRayCastingEnabled( false );
+ mpSimStateObj->GetCollisionObject( )->SetCollisionEnabled( false );
+
+ bool bGroundPlaneRestore = false;
+ if( mGroundPlaneSimState )
+ {
+ bGroundPlaneRestore = mGroundPlaneSimState->GetCollisionObject( )->GetRayCastingEnabled( );
+ mGroundPlaneSimState->GetCollisionObject( )->SetRayCastingEnabled( false );
+ }
+
+ // Test ray against remaining collision objects.
+ //
+ GetWorldPhysicsManager()->mCollisionManager->DetectRayIntersection(msIntersectInfo, rayOrg, rayEnd, false, this->GetCollisionAreaIndex() );
+ //GetWorldPhysicsManager()->mCollisionManager->DetectRayIntersection(msIntersectInfo, rayOrg, rayEnd, false,
+ // GetAvatarManager()->GetAvatarForPlayer(0)->GetCharacter()->GetCollisionAreaIndex() );
+
+ // Restore the state.
+ //
+ mpSimStateObj->GetCollisionObject( )->SetRayCastingEnabled( bRayRestore );
+ mpSimStateObj->GetCollisionObject( )->SetCollisionEnabled( bCollRestore );
+ if( mGroundPlaneSimState )
+ {
+ mGroundPlaneSimState->GetCollisionObject( )->SetRayCastingEnabled( bGroundPlaneRestore );
+ }
+
+
+ // Iterate through the entire list because of way DetectRayIntersection works.
+ // It checks the top level hierarchy of an object, so it will return bad values
+ // if the hierarchy is large, and you are standing on top of something in a different
+ // (smaller) hierarchy. see Level 9, duffTruck BV vs L9 BV.
+ //
+ int i;
+ for ( i = 0; i < msIntersectInfo.GetSize( ); i++ )
+ {
+ if ( msIntersectInfo.GetSize() > 0 && msIntersectInfo[ i ].mCollisionVolume )
+ {
+ rmt::Vector outNormal;
+ float prevOut = outDist;
+ sim::CollisionVolume* pOutVolume = FindStandingVolume( testPosition, msIntersectInfo[ i ].mCollisionVolume, outNormal, outDist );
+
+ bool vehicleIgnore = false;
+ if(this->mpTargetVehicle && pOutVolume)
+ {
+ if(pOutVolume->GetCollisionObject()->GetSimState()->mAIRefPointer == this->mpTargetVehicle)
+ {
+ if(oldStanding != pOutVolume)
+ {
+ vehicleIgnore = true;
+ outDist = prevOut;
+ }
+ }
+ }
+
+ if ( pOutVolume &&
+ pOutVolume->GetCollisionObject()->GetCollisionEnabled() &&
+ (pOutVolume->GetCollisionObject()->GetSimState()->mAIRefIndex != PhysicsAIRef::redBrickPhizVehicleGroundPlane) &&
+ (pOutVolume->GetCollisionObject()->GetSimState()->mAIRefIndex != PhysicsAIRef::redBrickPhizMoveableGroundPlane) &&
+ (!vehicleIgnore))
+ {
+ if ( outDist < currentDist )
+ {
+ currentDist = outDist;
+ tRefCounted::Assign(mpStandingCollisionVolume, pOutVolume);
+ collisionNormal = outNormal;
+ }
+ }
+ }
+ }
+
+ mbSurfing = false;
+
+ if ( mpStandingCollisionVolume )
+ {
+ rmt::Vector newPos = collisionNormal;
+ newPos.Scale( -currentDist );
+ testPosition.Add( newPos );
+ outPosition = testPosition;
+
+ // Test to see if we are standing on the collision volume.
+ //
+ if ( position.y - outPosition.y <= 0.1f )
+ {
+ // We are standing.
+ //
+ // If this is an animated object, find the transform to parent the character.
+ //
+ sim::SimState* pSimState = mpStandingCollisionVolume->GetCollisionObject( )->GetSimState();
+
+ if(pSimState->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ mbSurfing = true;
+ }
+
+ if( !( PhysicsAIRef::redBrickPhizStatic & pSimState->mAIRefIndex ) )
+ {
+ if ( pSimState->IsArticulated( ) )
+ {
+ // Find joint based on mpStandingCollisionVolume->ObjRefIndex().
+ //
+ sim::SimStateArticulated* pSimStateArticulated = static_cast<sim::SimStateArticulated*>( pSimState );
+
+ // Michael - this will assert on articulated objects that were converted to rigid bodies
+ // (these always have their volume::objrefindex set to -1)
+ // commenting out, it doesn't appear to have any problems
+
+ // rAssert(mpStandingCollisionVolume->ObjRefIndex() != -1);
+ if(mpStandingCollisionVolume->ObjRefIndex() != -1)
+ {
+ const poser::Pose* pPose = pSimStateArticulated->GetPose( );
+ rAssert( pPose );
+ pStandingJoint = pPose->GetJoint( mpStandingCollisionVolume->ObjRefIndex() );
+ rAssert( pStandingJoint );
+ }
+ }
+
+ }
+ }
+ bFoundIntersect = true;
+ }
+
+ SetStandingJoint( pStandingJoint );
+ sim::RayIntersectionInfo::sRayThickness = fOldRayThickness;
+END_PROFILE( "GetCollisionHeight" );
+ tRefCounted::Release(oldStanding);
+ return bFoundIntersect;
+}
+
+#ifdef RAD_DEBUG
+
+void Character::PrintAnimations() const
+{
+ choreo::Bank* bank = mpPuppet->GetBank();
+ choreo::BaseBank::RawIterator* it = bank->NewRawIterator();
+ it->AddRef();
+
+ IRefCount* obj = it->First();
+ while( obj != NULL )
+ {
+ choreo::Animation* anim = dynamic_cast< choreo::Animation* >( obj );
+ if( anim != NULL )
+ {
+ tAnimation* tAnim = anim->GetP3DAnimation();
+ const char* name = tAnim->GetName();
+ rDebugPrintf( "animationName '%s'\n", name );
+ }
+ obj = it->Next();
+ }
+ it->Release();
+}
+#endif
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
+void Character::SetStandingJoint( const poser::Joint* pJoint )
+{
+ if ( pJoint != mpStandingJoint )
+ {
+ rmt::Vector position;
+ GetPosition( position );
+
+ rmt::Vector desiredFacing;
+ GetDesiredFacing( desiredFacing );
+ rmt::Vector facingVector = mpPuppet->GetFacingVector( );
+
+ mParentTransform.RotateVector( desiredFacing, &desiredFacing );
+ mParentTransform.RotateVector( facingVector, &facingVector );
+
+ rmt::Matrix transform;
+ if(pJoint)
+ {
+ transform = pJoint->GetWorldMatrix( );
+ }
+ else
+ {
+ transform.Identity();
+ }
+
+ SetParentTransform( transform );
+
+ SetPosition( position );
+ mInvParentTransform.RotateVector( desiredFacing, &desiredFacing );
+ mInvParentTransform.RotateVector( facingVector, &facingVector );
+ mpPuppet->SetFacingVector( facingVector );
+ SetDesiredDir( choreo::GetWorldAngle( desiredFacing.x, desiredFacing.z ) );
+ mpJumpLocomotion->SetRootTransform( );
+ mpStandingJoint = pJoint;
+ }
+}
+
+/*
+==============================================================================
+Character::SetGroundPoint
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& groundPoint )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SetGroundPoint( const rmt::Vector& groundPoint )
+{
+ //choreo::Puppet* pPuppet = GetPuppet( );
+ //if ( pPuppet )
+ //{
+ // pPuppet->SetGroundPoint( groundPoint );
+ //}
+ //SetPosition( groundPoint );
+ if ( mpPuppet )
+ {
+ // Transform from world to object space.
+ //
+ rmt::Vector transformedPos = groundPoint;
+ transformedPos.Transform( mInvParentTransform );
+
+ mpPuppet->SetPosition( transformedPos );
+
+ poser::Pose* pPose = mpPuppet->GetPose( );
+ // stuff fixed up root transform into joint
+ poser::Joint* joint = pPose->GetJoint( 0 );
+ joint->SetObjectTranslation( groundPoint );
+ joint->SetWorldTranslation( groundPoint );
+ }
+}
+//=============================================================================
+// Character::UpdateTransformToLoco
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void )
+//
+// Return: void
+//
+//=============================================================================
+void Character::UpdateTransformToLoco( void )
+{
+ // This will get us the world space position and facing.
+ //
+ rmt::Vector position;
+ GetPosition( position );
+ rmt::Vector facing;
+ GetFacing( facing );
+ mParentTransform.RotateVector( facing, &facing );
+
+ tRefCounted::Release(mpStandingCollisionVolume);
+ mbSurfing = false;
+
+ // Go from the car space back to world space.
+ //
+ UpdateParentTransform( 0.0f );
+
+ SetDesiredDir( choreo::GetWorldAngle( facing.x, facing.z ) );
+ SetFacingDir( choreo::GetWorldAngle( facing.x, facing.z ) );
+ SetPosition( position );
+
+ SetInCar(false);
+
+ //AssignCollisionAreaIndex( );
+ //mpSimStateObj->GetCollisionObject()->SetCollisionEnabled( true );
+}
+/*
+==============================================================================
+Character::AssignCollisionAreaIndex
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::AssignCollisionAreaIndex( void )
+{
+ if ( WorldPhysicsManager::INVALID_COLLISION_AREA == mCollisionAreaIndex )
+ {
+ mCollisionAreaIndex = GetWorldPhysicsManager()->GetCharacterCollisionAreaIndex();
+ }
+ rTuneAssert( mCollisionAreaIndex != -1 );
+}
+//=============================================================================
+// Character::UpdateTransformToInCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void )
+//
+// Return: void
+//
+//=============================================================================
+void Character::UpdateTransformToInCar( void )
+{
+ if( mbIsJump )
+ {
+ mpJumpLocomotion->Done();
+ mbIsJump = false;
+ }
+
+ rmt::Vector pos;
+ GetPosition(pos);
+
+ // Update the character parent transform to vehicleToWorld.
+ //
+ UpdateParentTransform( 0.0f );
+
+ SetStandingJoint(NULL);
+ tRefCounted::Release(mpStandingCollisionVolume);
+ mbSurfing = false;
+
+ SetPosition(pos);
+ SetDesiredDir( rmt::PI );
+ SetFacingDir( rmt::PI );
+}
+
+/*
+==============================================================================
+Character::SetInCar
+==============================================================================
+Description: Comment
+
+Parameters: ( bool bInCar )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SetInCar( bool bInCar )
+{
+ mbInCar = bInCar;
+
+ mTranslucent = !mbInCar;
+
+ if(this == GetCharacterManager()->GetCharacter(0))
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( !mbInCar )
+ {
+ if( radTimeGetMicroseconds64()-mLastInteriorLoadCheck > 3000000 ) //(amortise/3s) if it's been more than 3 sec's since we last checked for volumes
+ {
+ rmt::Vector posn;
+ GetPosition(&posn);
+ GetTriggerVolumeTracker()->ActivateNearestInteriorLoadZone(0, posn, 40.0f);
+ mLastInteriorLoadCheck = radTimeGetMicroseconds64();
+ }
+
+ if(mpCurrentActionButtonHandler)
+ {
+ if ( mpCurrentActionButtonHandler->IsInstanceEnabled() )
+ {
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+ }
+ }
+ else if(mbInCar && mpCurrentActionButtonHandler)
+ {
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+ }
+}
+
+/*
+==============================================================================
+Character::GetActionButtonHandler
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ActionButton
+
+=============================================================================
+*/
+ActionButton::ButtonHandler* Character::GetActionButtonHandler( void ) const
+{
+ return mpCurrentActionButtonHandler;
+}
+
+/*
+==============================================================================
+Character::AddActionButtonHandler
+==============================================================================
+Description: Comment
+
+Parameters: ( ActionButton::ButtonHandler* pActionButtonHandler )
+
+Return: void
+
+=============================================================================
+*/
+void Character::AddActionButtonHandler( ActionButton::ButtonHandler* pActionButtonHandler )
+{
+ if ( !IsNPC() )
+ {
+ unsigned int i;
+#ifdef RAD_DEBUG
+ //Make sure this is only added once.
+ for ( i = 0; i < MAX_ACTION_BUTTON_HANDLERS; ++i )
+ {
+ rAssert( mpActionButtonHandlers[ i ] != pActionButtonHandler );
+ }
+#endif
+ for ( i = 0; i < MAX_ACTION_BUTTON_HANDLERS; ++i )
+ {
+ if ( mpActionButtonHandlers[ i ] == NULL )
+ {
+ //Add here.
+ mpActionButtonHandlers[ i ] = pActionButtonHandler;
+ mpActionButtonHandlers[ i ]->AddRef();
+ break;
+ }
+ }
+
+ rAssertMsg( i < MAX_ACTION_BUTTON_HANDLERS, "Need to increase the size of MAX_ACTION_BUTTON_HANDLERS" );
+ if ( i == MAX_ACTION_BUTTON_HANDLERS )
+ {
+ return;
+ }
+
+ ActionButton::ButtonHandler* newButton = TestPriority( pActionButtonHandler, mpCurrentActionButtonHandler );
+
+ if ( newButton != mpCurrentActionButtonHandler )
+ {
+ //This is a new action button of highest priority.
+
+ mpCurrentActionButtonHandler = pActionButtonHandler;
+
+ if ( !IsInCar() )
+ {
+ if ( mpCurrentActionButtonHandler->IsInstanceEnabled() )
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+ }
+ }
+ }
+}
+
+//=============================================================================
+// Character::RemoveActionButtonHandler
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ActionButtonHandler::ButtonHandler* pActionButtonHandler )
+//
+// Return: void
+//
+//=============================================================================
+void Character::RemoveActionButtonHandler( ActionButton::ButtonHandler* pActionButtonHandler )
+{
+ if ( !IsNPC() )
+ {
+ unsigned int i;
+ for ( i = 0; i < MAX_ACTION_BUTTON_HANDLERS; ++i )
+ {
+ if ( mpActionButtonHandlers[ i ] == pActionButtonHandler )
+ {
+ //This is the one to remove.
+ mpActionButtonHandlers[ i ]->Release();
+ mpActionButtonHandlers[ i ] = NULL;
+
+ if ( mpCurrentActionButtonHandler == pActionButtonHandler )
+ {
+ mpCurrentActionButtonHandler = NULL;
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+
+ break;
+ }
+ }
+
+ if ( mpCurrentActionButtonHandler == NULL )
+ {
+ //Find a new one.
+ for ( i = 0; i < MAX_ACTION_BUTTON_HANDLERS; ++i )
+ {
+ if ( mpActionButtonHandlers[ i ] != NULL )
+ {
+ mpCurrentActionButtonHandler = TestPriority( mpCurrentActionButtonHandler, mpActionButtonHandlers[ i ] );
+ }
+ }
+
+ if ( mpCurrentActionButtonHandler != NULL )
+ {
+ if ( mpCurrentActionButtonHandler->IsInstanceEnabled() )
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+ }
+ }
+ }
+}
+
+//=============================================================================
+// Character::TestPriority
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( ActionButton::ButtonHandler* bA, ActionButton::ButtonHandler* bB )
+//
+// Return: ActionButton
+//
+//=============================================================================
+ActionButton::ButtonHandler* Character::TestPriority( ActionButton::ButtonHandler* bA, ActionButton::ButtonHandler* bB )
+{
+ if ( NULL == bA )
+ {
+ return bB;
+ }
+ else if ( NULL == bB )
+ {
+ return bA;
+ }
+
+ ActionButton::ButtonHandler::Type bAType = bA->GetType();
+ ActionButton::ButtonHandler::Type bBType = bB->GetType();
+
+ if ( bAType == ActionButton::ButtonHandler::GET_IN_CAR || bBType == ActionButton::ButtonHandler::GET_IN_CAR )
+ {
+ //Is this the players car?
+ Avatar* myAvatar = GetAvatarManager()->FindAvatarForCharacter( this );
+
+ if ( myAvatar )
+ {
+ int id = myAvatar->GetPlayerId();
+ rAssert( id == 0 ); //ONly works in single player.
+
+ Vehicle* myVehicle = GetGameplayManager()->GetCurrentVehicle();
+ if ( myVehicle )
+ {
+ int actionId = (int)myVehicle->mpEventLocator->GetData( );
+ ActionButton::ButtonHandler* pActionButtonHandler = GetActionButtonManager()->GetActionByIndex( actionId );
+ rAssert( pActionButtonHandler );
+
+ if ( bA == pActionButtonHandler )
+ {
+ bAType = ActionButton::ButtonHandler::GET_IN_USER_CAR;
+ }
+
+ if ( bB == pActionButtonHandler )
+ {
+ bBType = ActionButton::ButtonHandler::GET_IN_USER_CAR;
+ }
+ }
+ }
+ }
+
+ return bAType < bBType ? bA : bB;
+}
+
+//=============================================================================
+// Character::ClearAllActionButtonHandlers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Character::ClearAllActionButtonHandlers()
+{
+ unsigned int i;
+ for ( i = 0; i < MAX_ACTION_BUTTON_HANDLERS; ++i )
+ {
+ if ( mpActionButtonHandlers[ i ] != NULL )
+ {
+ mpActionButtonHandlers[ i ]->Exit( this );
+ mpActionButtonHandlers[ i ]->Release();
+ mpActionButtonHandlers[ i ] = NULL;
+ }
+ }
+
+ mpCurrentActionButtonHandler = NULL;
+ if(this == GetCharacterManager()->GetCharacter(0))
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+}
+
+tSkeleton* Character::Prop::spSkeleton = 0;
+int Character::Prop::sSkelRefs = 0;
+
+Character::Prop::Prop( void )
+ :
+mpProp( 0 ),
+mpPose( 0 )
+{
+ if ( !spSkeleton )
+ {
+ spSkeleton = new tSkeleton( 1 );
+ rmt::Matrix mat;
+ mat.Identity( );
+ spSkeleton->GetJoint( 0 )->worldMatrix = spSkeleton->GetJoint( 0 )->restPose = mat;
+ spSkeleton->GetJoint( 0 )->inverseWorldMatrix = spSkeleton->GetJoint( 0 )->worldMatrix;
+ spSkeleton->GetJoint( 0 )->inverseWorldMatrix.InvertOrtho( );
+ }
+ spSkeleton->AddRef( );
+ sSkelRefs++;
+ tRefCounted::Assign( mpPose, spSkeleton->NewPose( ) );
+}
+
+Character::Prop::~Prop( void )
+{
+ tRefCounted::Release( mpProp );
+ tRefCounted::Release( mpPose );
+ spSkeleton->Release ();
+ sSkelRefs--;
+ if (sSkelRefs < 1)
+ {
+ spSkeleton = 0;
+ }
+}
+/*
+==============================================================================
+Character::TouchProp
+==============================================================================
+Description: Comment
+
+Parameters: ( InstDynaPhysDSG* pProp )
+
+Return: void
+
+=============================================================================
+*/
+void Character::TouchProp( InstDynaPhysDSG* pProp )
+{
+ /*
+ bool sbPickUp = false;
+ if ( sbPickUp )
+ {
+ mpPropHandler->SetProp( pProp );
+ AddActionButtonHandler( mpPropHandler );
+ mpPropHandler->Enter( this );
+ }
+ */
+}
+/*
+==============================================================================
+Character::AttachProp
+==============================================================================
+Description: Comment
+
+Parameters: ( InstDynaPhysDSG* pProp )
+
+Return: void
+
+=============================================================================
+*/
+void Character::AttachProp( InstDynaPhysDSG* pProp )
+{
+ /*
+ int i;
+ for ( i = 0; i < MAX_PROPS; i++ )
+ {
+ if ( mPropList[ i ].mpProp == 0 )
+ {
+ tRefCounted::Assign( mPropList[ i ].mpProp, pProp );
+ GetPuppet( )->AttachProp( mPropJoint, mPropList[ i ].mpPose );
+
+ mPropList[ i ].mpProp->GetSimState()->GetCollisionObject()->SetCollisionEnabled( false );
+ mPropList[ i ].mpProp->GetSimState()->SetControl( sim::simAICtrl );
+ break;
+ }
+ }
+ */
+}
+/*
+==============================================================================
+Character::RemoveProp
+==============================================================================
+Description: Comment
+
+Parameters: ( InstDynaPhysDSG* pProp )
+
+Return: void
+
+=============================================================================
+*/
+void Character::RemoveProp( InstDynaPhysDSG* pProp )
+{
+ /*
+ int i;
+ for ( i = 0; i < MAX_PROPS; i++ )
+ {
+ if ( mPropList[ i ].mpProp == pProp )
+ {
+ tRefCounted::Release( mPropList[ i ].mpProp );
+ GetPuppet( )->RemoveAttachedProp( mPropList[ i ].mpPose );
+ break;
+ }
+ }
+ */
+}
+/*
+==============================================================================
+Character::UpdateProps
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void Character::UpdateProps( float timeins )
+{
+ /*
+ int i;
+ for ( i = 0; i < MAX_PROPS; i++ )
+ {
+ if ( mPropList[ i ].mpProp != 0 )
+ {
+ mPropList[ i ].mpProp->GetSimState()->SetTransform( mPropList[ i ].mpPose->GetJoint( 0 )->worldMatrix, timeins );
+ mPropList[ i ].mpProp->Update( timeins );
+
+ CharacterController::eIntention theIntention = GetController()->GetIntention();
+ if( CharacterController::DoAction == theIntention )
+ {
+ // Throw the sum'bitch.
+ //
+ sim::SimState* pSimState = mPropList[ i ].mpProp->GetSimState();
+ rAssert( pSimState );
+ mPropList[ i ].mpProp->AddToSimulation();
+ GetFacing( pSimState->VelocityState( ).mLinear );
+ static float sfUpVelocity = 3.0f;
+ pSimState->VelocityState( ).mLinear.y = sfUpVelocity;
+ pSimState->GetCollisionObject()->SetCollisionEnabled( true );
+
+ // Not the most efficient.
+ //
+ RemoveProp( mPropList[ i ].mpProp );
+ }
+ }
+ }
+ */
+}
+
+
+
+void Character::UpdatePhysicsObjects( float timeins, int area )
+{
+ rAssert( area != -1 );
+ RestTest();
+ GetWorldPhysicsManager()->UpdateDynamicObjects(timeins, area );
+ GetWorldPhysicsManager()->UpdateAnimCollisions(timeins, area );
+}
+
+/*
+==============================================================================
+Character::SubmitStatics
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SubmitStatics( void )
+{
+ BEGIN_PROFILE( "Per Character Submit STatics" );
+ if ( GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer() )
+ {
+ // Always test because we need to dump stuff while we are in the car.
+ // Dynaimc loading rides again
+ //
+ // TBJ [8/14/2002]
+ //
+ if( true ) //!(IsInCar()))
+ {
+ int collisionAreaIndex = GetCollisionAreaIndex();
+ if ( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ rmt::Vector position;
+ GetPosition( position );
+ static float sfCollisionRadius = 5.0f;
+ GetWorldPhysicsManager()->SubmitStaticsPseudoCallback(position, sfCollisionRadius, collisionAreaIndex, GetSimState(), true);
+
+ GetWorldPhysicsManager()->SubmitFencePiecesPseudoCallback(position, sfCollisionRadius, collisionAreaIndex, GetSimState(), true);
+ }
+ }
+ }
+ END_PROFILE( "Per Character Submit STatics" );
+}
+
+/*
+==============================================================================
+Character::SubmitAnimCollisions
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SubmitAnimCollisions( void )
+{
+ BEGIN_PROFILE( "Per Character Submit Anims" );
+ if ( GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer() )
+ {
+ // Always test because we need to dump stuff while we are in the car.
+ // Dynaimc loading rides again
+ //
+ // This is also nice because objects will animate while we are in the car.
+ //
+ // TBJ [8/14/2002]
+ //
+ if( true ) //!(IsInCar()))
+ {
+ int collisionAreaIndex = GetCollisionAreaIndex();
+ if ( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ rmt::Vector position;
+ GetPosition( position );
+
+ static float sfCollisionRadius = 1.5f;
+ GetWorldPhysicsManager()->SubmitAnimCollisionsPseudoCallback(position, sfCollisionRadius, collisionAreaIndex, GetSimState());
+ static float sfUpdateRadius = 30.0f;//0.0f;
+ GetWorldPhysicsManager()->SubmitAnimCollisionsForUpdateOnly( position, sfUpdateRadius, collisionAreaIndex );
+ }
+ }
+ }
+ END_PROFILE( "Per Character Submit Anims" );
+}
+
+/*
+==============================================================================
+Character::SubmitDynamics
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void Character::SubmitDynamics( void )
+{
+ BEGIN_PROFILE( "Per Character Submit Dyn" );
+ if ( GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer() )
+ {
+ // Always test because we need to dump stuff while we are in the car.
+ // Dynaimc loading rides again
+ //
+ // This is also nice because objects will animate while we are in the car.
+ //
+ // TBJ [8/14/2002]
+ //
+ if( true ) //!(IsInCar()))
+ {
+ int collisionAreaIndex = GetCollisionAreaIndex();
+ if ( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ rmt::Vector position;
+ GetPosition( position );
+
+ static float sfCollisionRadius = 10.5f;
+ GetWorldPhysicsManager()->SubmitDynamicsPseudoCallback(position, sfCollisionRadius, collisionAreaIndex, GetSimState(), true);
+ }
+ }
+ }
+ END_PROFILE( "Per Character Submit Dyn" );
+}
+
+bool Character::PosInFrustrumOfPlayer( const rmt::Vector& pos, int playerID )
+{
+ return GetGameplayManager()->TestPosInFrustrumOfPlayer( pos, playerID );
+}
+
+void Character::TestInFrustrumOfPlayer( int playerID )
+{
+ if( PosInFrustrumOfPlayer( mSphere.centre, playerID ) )
+ {
+ mbInAnyonesFrustrum = true;
+ }
+}
+
+void Character::SetShadowColour( tColour shadowColour )
+{
+ mShadowColour = shadowColour;
+ if( mpCharacterRenderable )
+ {
+ mpCharacterRenderable->SetShadowColour( shadowColour );
+ }
+}
+
+tColour Character::GetShadowColour()
+{
+ return mShadowColour;
+}
+
+void Character::SetSwatch( int swatchNum )
+{
+ rAssert( mpCharacterRenderable != NULL );
+ mpCharacterRenderable->SetSwatch( swatchNum );
+}
+
+void Character::SetDrawable( CharacterRenderable* pDrawable )
+{
+ if(mpCharacterRenderable)
+ {
+ delete mpCharacterRenderable;
+ }
+ mpCharacterRenderable = pDrawable;
+}
+
+void Character::Shock( float timeInSeconds )
+{
+ if ( mpCharacterRenderable )
+ {
+ m_TimeLeftToShock = timeInSeconds;
+ m_IsBeingShocked = true;
+
+ mpCharacterRenderable->SetShocked( true );
+
+ GetEventManager()->TriggerEvent( EVENT_WASP_BULLET_HIT_CHARACTER_STYLIZED_VIOLENCE_FOLLOWS );
+
+ //
+ // Throw KICK_NPC_SOUND to trigger the HitByW dialogue that Chris and Cory want -- Esan
+ //
+ GetEventManager()->TriggerEvent( EVENT_KICK_NPC_SOUND, this );
+ }
+}
+
+void Character::DoKickwave(void)
+{
+ if(!mKickwave)
+ {
+ tRefCounted::Assign(mKickwave, p3d::find<tDrawable>("kickwave"));
+ tRefCounted::Assign(mKickwaveController, p3d::find<tFrameController>("kickwave"));
+ }
+
+ if(mKickwave)
+ {
+ mDoKickwave = true;
+ mKickwaveController->SetFrame(0);
+ }
+}
+
+void Character::OnTransitToAICtrl()
+{
+ mPrevSimTransform = GetSimState()->GetTransform();
+ mGroundPlaneSimState->GetCollisionObject()->SetCollisionEnabled( false );
+
+ // get "up" out of sim control
+ rmt::Vector up, right, forward;
+ //right = mpCharacter->mPrevSimTransform.Row(0);
+ up = mPrevSimTransform.Row(1);
+ //forward = mpCharacter->mPrevSimTransform.Row(2);
+
+ float dir = choreo::GetWorldAngle( up.x, up.z );
+ RelocateAndReset( mPrevSimTransform.Row(3), dir );
+}
+
+void Character::Display(void)
+{
+ if(IS_DRAW_LONG) return;
+ DSG_BEGIN_PROFILE(" Character::Display")
+ if(!mpCharacterRenderable->GetDrawable())
+ {
+ return;
+ }
+
+ if( mbNeedChoreoUpdate)
+ {
+ mpPuppet->UpdatePose( );
+ mbNeedChoreoUpdate = false;
+ }
+
+ if(IsMarge() &&
+ ((GetStateManager()->GetState() == CharacterAi::INCAR) ||
+ (GetStateManager()->GetState() == CharacterAi::GET_IN) ||
+ (GetStateManager()->GetState() == CharacterAi::GET_OUT)) &&
+ GetTargetVehicle() &&
+ GetTargetVehicle()->mHighRoof)
+ {
+ poser::Pose* p = mpPuppet->GetPose();
+ int numJoints = p->GetJointCount();
+ if( numJoints >= 36 )
+ {
+ mpPuppet->GetPose()->GetJoint(33)->SetObjectMatrix(mpCharacterRenderable->GetDrawable()->GetSkeleton()->GetJoint(33)->restPose);
+ mpPuppet->GetPose()->GetJoint(34)->SetObjectMatrix(mpCharacterRenderable->GetDrawable()->GetSkeleton()->GetJoint(34)->restPose);
+ mpPuppet->GetPose()->GetJoint(35)->SetObjectMatrix(mpCharacterRenderable->GetDrawable()->GetSkeleton()->GetJoint(35)->restPose);
+ }
+ }
+
+ mpPuppet->GetP3DPose()->SetSkeleton(mpCharacterRenderable->GetDrawable()->GetSkeleton());
+ mpPuppet->UpdateEnd( );
+
+ static float JumpRatio = 0.0f;
+ if( JumpRatio == 0.0f && ( GetJumpHeight() != 0.0f ) )
+ {
+ JumpRatio = 0.5f / GetJumpHeight();
+ }
+ if( !IsInCar() && IsSimpleShadow() )
+ {
+ rmt::Vector groundPos;
+ rmt::Vector groundNormal;
+ rmt::Vector characterFacing;
+ GetTerrainIntersect( groundPos, groundNormal );
+ GetFacing( characterFacing );
+ struct BlobShadowParams p( groundPos, groundNormal, characterFacing );
+ const rmt::Vector& pos = rPosition();
+ p.ShadowScale = 1.0f - ( ( pos.y - groundPos.y ) * JumpRatio );
+ p.ShadowAlpha = p.ShadowScale * ( mInteriorTerrain ? 0.5f : 1.0f );
+ p3d::pddi->SetZWrite(false);
+ mpCharacterRenderable->DisplayShadow( mpPuppet->GetP3DPose(), &p );
+ p3d::pddi->SetZWrite( true );
+ }
+
+ tPose* pose = mpPuppet->GetP3DPose();
+
+ mLean = pose->GetJoint(17)->worldMatrix.Row(3);
+ mLean.Sub(pose->GetJoint(0)->worldMatrix.Row(3));
+ mLean.NormalizeSafe();
+
+ rmt::Matrix backToTheOrigin;
+
+ backToTheOrigin.Identity();
+ backToTheOrigin.Row(3) = pose->GetJoint(0)->worldMatrix.Row(3);
+ backToTheOrigin.InvertOrtho();
+
+ rmt::Vector rootPos = pose->GetJoint(0)->worldMatrix.Row(3);
+
+ bool shouldScale = mScale != 1.0f;
+
+ for(int i = 0; i < pose->GetNumJoint(); i++)
+ {
+ rmt::Matrix tmp;
+ tmp.Mult(pose->GetJoint(i)->worldMatrix, backToTheOrigin);
+ if(shouldScale)
+ {
+ rmt::Matrix scale;
+ scale.Identity();
+ scale.FillScale(mScale, mScale, mScale);
+ pose->GetJoint(i)->worldMatrix.Mult(tmp, scale);
+ }
+ else
+ {
+ pose->GetJoint(i)->worldMatrix = tmp;
+ }
+ }
+
+ p3d::stack->Push();
+ p3d::stack->Translate(0.0f, mYAdjust, 0.0f);
+
+ p3d::stack->Push();
+ p3d::stack->Translate(rootPos);
+
+ mpCharacterRenderable->Display( mSphere.centre, pose );
+
+ p3d::stack->Pop();
+ p3d::stack->Pop();
+
+ if(mDoKickwave && mKickwave)
+ {
+ p3d::stack->Push();
+ p3d::stack->Translate(rootPos);
+ p3d::stack->Multiply(pose->GetJoint(0)->worldMatrix);
+ mKickwave->Display();
+ p3d::stack->Pop();
+ }
+#ifdef DRAW_CHARACTER_COLLISION
+#ifdef RAD_RELEASE
+ if ( CommandLineOptions::Get( CLO_DEBUGBV ) )
+#else
+ if ( !mpCharacterRenderable->GetDrawable() || CommandLineOptions::Get( CLO_DEBUGBV ) )
+#endif
+ {
+ // The sim library allocates shaders and stuff for this, so we should ensure they're on the temp heap
+ //
+ HeapMgr()->PushHeap (GMA_TEMP);
+
+ sim::CollisionVolume* pVolume = GetSimState( )->GetCollisionObject()->GetCollisionVolume( );
+ sim::DrawCollisionVolume( pVolume );
+ if ( mpStandingCollisionVolume )
+ sim::DrawCollisionVolume( mpStandingCollisionVolume );
+ int i;
+ for ( i = 0; i < mCurrentCollision; i++ )
+ {
+ sim::CollisionVolume* pVolume = mCollisionData[ i ].mpCollisionVolume;
+ if ( pVolume )
+ sim::DrawCollisionVolume(pVolume);
+ }
+
+ HeapMgr()->PopHeap( GMA_TEMP );
+ }
+#endif // DRAW_CHARACTER_COLLISION
+ DSG_END_PROFILE(" Character::Display")
+}
+
+void Character::SetAmbient(const char* location, float radius)
+{
+ mAmbient = true;
+ mAmbientLocator = tEntity::MakeUID(location);
+
+ if((mRole != ROLE_REWARD) && (radius != 0.0f))
+ {
+ tRefCounted::Assign(mAmbientTrigger, new AmbientDialogueTrigger(this, radius));
+ EnableAmbientDialogue(true);
+ }
+}
+
+void Character::EnableAmbientDialogue(bool e)
+{
+ if(!mAmbientTrigger)
+ return;
+
+ if(e)
+ {
+ GetTriggerVolumeTracker()->AddTrigger(mAmbientTrigger);
+ }
+ else
+ {
+ GetTriggerVolumeTracker()->RemoveTrigger(mAmbientTrigger);
+ }
+}
+
+void Character::ResetAmbientPosition(void)
+{
+ Locator* l = p3d::find<Locator>(mAmbientLocator);
+ if(l)
+ {
+ rmt::Vector pos;
+ l->GetPosition(&pos);
+ RelocateAndReset(pos, 0.0f);
+ }
+ AddToWorldScene();
+
+ static_cast<NPCController*>(GetController())->ClearTempWaypoint();
+}
+
+/////////////////////////// NPC STUFF ////////////////////////////////
+
+void NPCharacter::UpdatePhysicsObjects( float timeins, int area )
+{
+ // NPCs shouldn't be submitting to physics stuff around it.
+ //
+ RestTest();
+}
+
+void NPCharacter::AssignCollisionAreaIndex( void )
+{
+ Character::AssignCollisionAreaIndex();
+}
+
+
+void NPCharacter::SubmitStatics( void )
+{
+ BEGIN_PROFILE( "Per NPCharacter Submit Statics" );
+
+ // DUSIT [Oct 29,2002]:
+ // HACK:
+ // When should we submit statics around ourselves?
+ // characters too far away from player shouldn't be submitting statics
+ // characters standing still shouldn't submit.
+ // characters not "off path" shouldn't submit.
+ // characters in street races 1 & 2 shouldn't submit (or they'll pop out side
+ // the race props onto the race track).
+
+ NPCController* npcController = (NPCController*) GetController();
+ if( npcController != NULL )
+ {
+ NPCController::State state = npcController->GetState();
+ bool npcStateNeedsToSubmit =
+ (GetStateManager()->GetState() == CharacterAi::INSIM) ||
+ (
+ (GetStateManager()->GetState() != CharacterAi::INSIM) &&
+ (
+ (npcController->mOffPath && state == NPCController::FOLLOWING_PATH) ||
+ (state == NPCController::STOPPED ) ||
+ (state == NPCController::DODGING) ||
+ (state == NPCController::PANICKING) ||
+ (state == NPCController::TALKING_WITH_PLAYER)
+ )
+ );
+
+ if( npcStateNeedsToSubmit )
+ {
+ // We COULD do this to prevent code duplication. But a virtual function call
+ // is quite expensive.
+ //Character::SubmitStatics();
+
+ if ( GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer() )
+ {
+ // Always test because we need to dump stuff while we are in the car.
+ // Dynaimc loading rides again
+ //
+ // TBJ [8/14/2002]
+ //
+ int collisionAreaIndex = GetCollisionAreaIndex();
+ if( collisionAreaIndex == WorldPhysicsManager::INVALID_COLLISION_AREA &&
+ GetRole() != ROLE_PEDESTRIAN )
+ {
+ if(!IsInCar())
+ {
+ AddToPhysics();
+ collisionAreaIndex = GetCollisionAreaIndex();
+ }
+ }
+
+ if( collisionAreaIndex != WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ if ( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ rmt::Vector position;
+ GetPosition( position );
+ static float sfCollisionRadius = 1.0f;
+ GetWorldPhysicsManager()->SubmitStaticsPseudoCallback(position, sfCollisionRadius, collisionAreaIndex, GetSimState());
+
+ GetWorldPhysicsManager()->SubmitFencePiecesPseudoCallback(position, sfCollisionRadius, collisionAreaIndex, GetSimState());
+ }
+ }
+ }
+ }
+ else
+ {
+ int collisionAreaIndex = GetCollisionAreaIndex();
+ if( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ //
+ // Empty the collision area index of all submissions...
+ // unfortunately, this takes US and our GROUNDPLANE out of the list too
+ // so we gotta re-add and re-pair.
+ //
+ GetWorldPhysicsManager()->EmptyCollisionAreaIndex( collisionAreaIndex );
+ GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(
+ mpSimStateObj->GetCollisionObject(), mCollisionAreaIndex );
+ //AddToPhysics();
+ }
+ }
+ }
+
+
+ END_PROFILE( "Per NPCharacter Submit Statics" );
+}
+
+void NPCharacter::SubmitDynamics( void )
+{
+}
+
+void NPCharacter::OnTransitToAICtrl()
+{
+ // do here what we need to do at the very moment we transit
+ // back from simulation control to AI control
+ mPrevSimTransform = GetSimState()->GetTransform();
+
+}
+
+void NPCharacter::ApplyForce( const rmt::Vector& direction, float force )
+{
+ if(!IsInCar())
+ {
+ DynaPhysDSG::ApplyForce( direction, force );
+ }
+}
+
+
+void NPCharacter::ApplyKickForce( const rmt::Vector& direction, float force )
+{
+ if(!IsInCar())
+ {
+ DynaPhysDSG::ApplyForce( direction, force );
+
+ /*
+ rmt::Vector& rAngular = mpSimStateObj->GetAngularVelocity();
+ float deltaV = force / 100.0f;
+ rAngular += (direction * deltaV);
+ */
+
+ rmt::Vector linVel = mpSimStateObj->GetLinearVelocity();
+ mPastLinear.SetAverage( linVel.Magnitude() );
+
+ rmt::Vector angVel = mpSimStateObj->GetAngularVelocity();
+ mPastAngular.SetAverage( angVel.Magnitude() );
+
+ }
+}
+
+
diff --git a/game/code/worldsim/character/character.h b/game/code/worldsim/character/character.h
new file mode 100644
index 0000000..b87dcf5
--- /dev/null
+++ b/game/code/worldsim/character/character.h
@@ -0,0 +1,1428 @@
+#ifndef CHARACTER_H_
+#define CHARACTER_H_
+
+#include <radmath/radmath.hpp>
+#include <radtime.hpp>
+
+#include <render/DSG/InstDynaPhysDSG.h>
+#include <ai/state.h>
+#include <ai/statemanager.h>
+//#include <ai/sequencer/action.h>
+#include <memory/memorypool.h>
+#include <p3d/memory.hpp>
+
+#include <ai/sequencer/task.h>
+#include <p3d/p3dtypes.hpp>
+#include <events/eventenum.h>
+#include <events/eventmanager.h>
+#include <radmath/radmath.hpp>
+#include <worldsim/redbrick/vehicle.h>
+
+#include <worldsim/character/charactercontroller.h>
+
+#include <choreo/puppet.hpp>
+#include <simcollision/proximitydetection.hpp>
+
+class tDrawablePose;
+class tCamera;
+class CharacterTarget;
+class ActionController;
+
+namespace ActionButton
+{
+ class ButtonHandler;
+ class AttachProp;
+}
+class Vehicle;
+
+namespace CharacterAi
+{
+ class StateManager;
+};
+
+namespace sim
+{
+ class CollisionVolume;
+};
+
+namespace poser
+{
+ class Joint;
+ class Transform;
+};
+
+struct CharacterTune
+{
+ static float sfLocoRotateRate;
+ static float sfLocoAcceleration;
+ static float sfLocoDecceleration;
+ static bool bLocoTest;
+ static float sfAirRotateRate;
+ static float sfAirAccelScale;
+ static float sfAirGravity;
+ static float sfStompGravityScale;
+ static float sfDashBurstMax;
+ static float sfDashAcceleration;
+ static float sfDashDeceleration;
+ static float sfJumpHeight;
+ static float sfDoubleJumpHeight;
+ static float sfDoubleJumpAllowUp;
+ static float sfDoubleJumpAllowDown;
+ static float sfHighJumpHeight;
+ static float sfMaxSpeed;
+
+ static rmt::Vector sGetInPosition;
+ static float sGetInHeightThreshold;
+ static float sGetInOpenDelay;
+ static float sGetInOpenSpeed;
+ static float sGetInCloseDelay;
+ static float sGetInCloseSpeed;
+ static float sGetOutOpenDelay;
+ static float sGetOutOpenSpeed;
+ static float sGetOutCloseDelay;
+ static float sGetOutCloseSpeed;
+
+ static float sfTurboRotateRate;
+
+ static float sfGetInOutOfCarAnimSpeed;
+ static float sfKickingForce;
+ static float sfSlamForce;
+ static float sfShockTime;
+};
+class tPose;
+class tSkeleton;
+class InstDynaPhysDSG;
+class WorldScene;
+class AmbientDialogueTrigger;
+class CharacterRenderable;
+class JumpAction;
+class WalkerLocomotionAction;
+
+class Character
+:
+public DynaPhysDSG
+{
+public:
+ enum Role {
+ ROLE_UNKNOWN,
+ ROLE_DRIVER,
+ ROLE_REWARD,
+ ROLE_ACTIVE_BONUS,
+ ROLE_COMPLETED_BONUS,
+ ROLE_PEDESTRIAN,
+ ROLE_MISSION
+ };
+
+public: // MEMBERS
+ Character( void );
+ virtual ~Character( void );
+ void Init( void );
+
+ virtual void OnTransitToAICtrl();
+ virtual int GetAIRef() { return PhysicsAIRef::PlayerCharacter;}
+
+ float GetFacingDir( void ) const;
+ void SetFacingDir( float fFacingDir );
+
+ float GetDesiredDir( void ) const;
+ void GetDesiredFacing( rmt::Vector& facing ) const;
+ //ICameraTarget.
+ //
+ void GetFacing( rmt::Vector& facing ) const;
+ void SetFacing( rmt::Vector& facing );
+
+ // Call this to place character at a particular place in the world
+ // and perhaps reset its states.
+ void RelocateAndReset( const rmt::Vector& position, float facing, bool resetMe=true, bool snapToGround = true );
+
+ void SetPosition( const rmt::Vector& position );
+ void GetPosition( rmt::Vector& position ) const;
+
+ void SetVelocity( rmt::Vector& velocity );
+ void GetVelocity( rmt::Vector& velocity ) const;
+ const rmt::Vector& GetLocoVelocity() const;
+
+ float GetSpeed( void ) const;
+ void SetSpeed( float fSpeed );
+ void ResetSpeed( void );
+ float GetDesiredSpeed( void ) const;
+
+
+ void StickReleased( void );
+ void StickPressed( void );
+
+ void SetPuppet( choreo::Puppet* pPuppet );
+ choreo::Puppet* GetPuppet( void ) const;
+
+ void SetDrawable( CharacterRenderable* pDrawablePose );
+
+ virtual void PreSimUpdate(float timeins);
+ virtual void UpdateRoot( float timeins );
+ virtual void ResolveCollisions(void);
+ virtual void PostSimUpdate(float timeins);
+
+ // Just use this to set a flag.
+ virtual void Update( float timeins ) {};
+
+ void UpdateSimState( float timeins );
+ void UpdateBBox( rmt::Box3D& oldBox );
+
+ bool IsInCar( void ) const;
+ void SetInCar( bool bInCar );
+ bool IsInCarOrGettingInOut( void );
+
+ CharacterTarget* GetTarget( void ) const;
+
+ void SetController( CharacterController* pController );
+ CharacterController* GetController( void ) const;
+
+ void SetDesiredDir( float fDesiredDir );
+ void SetDesiredSpeed( float fDesiredSpeed );
+
+ ActionButton::ButtonHandler* GetActionButtonHandler( void ) const;
+ void AddActionButtonHandler( ActionButton::ButtonHandler* pActionButtonHandler );
+ void RemoveActionButtonHandler( ActionButton::ButtonHandler* pActionButtonHandler );
+ void ClearAllActionButtonHandlers();
+
+ void SetTargetVehicle( Vehicle* pVehicle );
+ Vehicle* GetTargetVehicle( ) const;
+ float GetMaxSpeed( void ) const;
+
+ CharacterAi::StateManager* GetStateManager( void ) const;
+
+ ActionController* GetActionController( void ) const;
+
+ static float GetJumpHeight( void );
+ void GetTerrainIntersect( rmt::Vector& pos, rmt::Vector& normal ) const;
+ void GetTerrainType( eTerrainType& TerrainType, bool& Interior ) const;
+
+ virtual void SubmitStatics( void );
+ virtual void SubmitAnimCollisions( void );
+ virtual void SubmitDynamics( void );
+
+ // Implements CollisionEntityDSG
+ //
+ virtual void Display(void);
+ virtual rmt::Vector* pPosition() ;
+ virtual const rmt::Vector& rPosition() ;
+ virtual void GetPosition( rmt::Vector* ipPosn ) ;
+
+ void DisplayShadow();
+ void DisplaySimpleShadow( void );
+ int CastsShadow();
+ bool IsSimpleShadow( void );
+ void SetSimpleShadow( bool IsSimpleShadow );
+
+ virtual sim::Solving_Answer PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision );
+ virtual sim::Solving_Answer PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision);
+
+
+ bool IsInCollision( void ) const;
+ void ResetCollisions( void );
+ bool CanStandOnCollisionVolume( void ) const;
+ bool CanStaggerCollision( void ) const;
+
+ enum eCollisionType
+ {
+ NoCollision = 0,
+ HitWall = 1 << 0,
+ HitHead = 1 << 1,
+ DangerousMultiple = 1 << 3
+ };
+
+ eCollisionType SolveCollisionWithStatic( const rmt::Vector& desiredPos, rmt::Vector& outPos );
+
+ bool IsStanding( void ) const;
+ void SetGroundPoint( const rmt::Vector& groundPoint );
+
+ WalkerLocomotionAction* GetWalkerLocomotionAction( void ) const;
+ JumpAction* GetJumpLocomotionAction( void ) const;
+
+ void UpdateParentTransform( float timeins );
+ void GetRootTransform( poser::Transform& out ) const;
+ void SetStandingJoint( const poser::Joint* pJoint );
+ const rmt::Matrix& GetParentTransform( void ) const;
+ const rmt::Matrix& GetInverseParentTransform( void ) const;
+ float GetGroundVerticalVelocity( void ) const;
+
+ void AddToWorldScene( void );
+ void RemoveFromWorldScene( void );
+ void MoveInWorldScene( void );
+ virtual void InitGroundPlane( void );
+ virtual void AddToPhysics( void );
+ virtual void RemoveFromPhysics( void );
+
+ int GetCollisionAreaIndex( void ) const;
+
+ rmt::Vector WorldToLocal( const rmt::Vector& world ) const;
+ rmt::Vector LocalToWorld( const rmt::Vector& local ) const;
+ void UpdateTransformToLoco( void );
+ void UpdateTransformToInCar( void );
+
+ bool IsJumping( void ) const;
+ void SetJumping( bool bIsJumping );
+
+ void SetTurbo( bool bTurbo );
+ bool IsTurbo( void ) const;
+
+ bool IsVisible( void ) const { return mVisible;}
+
+ void SetSolveCollisions( bool bCollide )
+ {
+ mbSolveCollisions = bCollide;
+ }
+
+ bool GetSolveCollisions( void ) const
+ {
+ return mbSolveCollisions;
+ }
+
+ struct Prop
+ {
+ Prop( void );
+ ~Prop( void );
+ InstDynaPhysDSG* mpProp;
+ tPose* mpPose;
+ static tSkeleton* spSkeleton;
+ static int sSkelRefs;
+ static int mPropCount;
+ };
+ void TouchProp( InstDynaPhysDSG* pProp );
+ void AttachProp( InstDynaPhysDSG* pProp );
+ void RemoveProp( InstDynaPhysDSG* pProp );
+ void UpdateProps( float timeins );
+
+ virtual void UpdatePhysicsObjects( float timeins, int area );
+ bool CanPlayAnimation( const tName& name ) const;
+ #ifdef RAD_DEBUG
+ void PrintAnimations() const;
+ #else
+ void PrintAnimations() const{};
+ #endif
+
+ void SetSwatch( int swatchNum );
+ bool PosInFrustrumOfPlayer( const rmt::Vector& pos, int playerID );
+
+ void SetYAdjust( float yOffset );
+ float GetYAdjust();
+
+ void Kick();
+ void Slam();
+
+ void SetFadeAlpha( int fadeAlpha );
+
+ bool IsBusy(void) {return mbBusy;}
+ void SetBusy(bool b) {mbBusy = b;}
+
+ bool IsSimpleLoco(void) {return mbSimpleLoco;}
+ void SetSimpleLoco(bool b) {mbSimpleLoco = b;}
+
+ void SetShadowColour( tColour shadowColour );
+ tColour GetShadowColour();
+
+ void Shock( float timeInSeconds );
+
+ void DoKickwave(void);
+
+ // Don't apply any velocity change to the player character
+ // There appear to be bugs when this happens ( character getting stuck in the ground )
+ virtual void ApplyForce( const rmt::Vector& direction, float force ){}
+
+ void SetAmbient(const char* location, float radius);
+ bool IsAmbient(void) { return mAmbient;}
+ void EnableAmbientDialogue(bool);
+ void ResetAmbientPosition(void);
+
+ bool IsNPC();
+
+ enum { MAX_ACTION_BUTTON_HANDLERS = 5 };
+
+ void DoGroundIntersect(bool b) { mbDoGroundIntersect = b;}
+
+ bool GetRockinIdle(void) { return mAllowRockin; }
+ void SetRockinIdle(bool b) { mAllowRockin = b; }
+
+ bool HasBeenHit() { return mHasBeenHit; }
+ void SetHasBeenHit( bool tf ) { mHasBeenHit = tf; }
+
+ void SetRole(Role r) { mRole = r;}
+ Role GetRole(void) { return mRole;}
+
+ void SetScale(float f) { mScale = f;}
+
+ bool CollidedThisFrame(void) { return mCollidedThisFrame; }
+
+ bool IsInSubstep() { return mIsInSubstep; }
+ void SetInSubstep( bool in ) { mIsInSubstep = in; }
+
+ bool TestInAnyonesFrustrum();
+
+ const rmt::Vector& GetLean(void) { return mLean; }
+
+ bool IsLisa(void) { return mIsLisa; }
+ bool IsMarge(void) { return mIsMarge; }
+ void SetIsLisa(bool b) { mIsLisa = b; }
+ void SetIsMarge(bool b) { mIsMarge = b; }
+
+ void SnapToGround(void);
+
+ unsigned GetActiveFrame(void) { return mIntersectFrame; }
+
+ void SetManaged(bool b) { mManaged = b;}
+ bool IsManaged(void) { return mManaged; }
+
+public: // MEMBERS
+ bool mbCollidedWithVehicle;
+ bool mbInAnyonesFrustrum; //if we have array of MAX_PLAYERS, we can distinguish betw each player's frustrum
+ bool mbSurfing;
+ bool mbAllowUnload;
+ bool mbIsPlayingIdleAnim; // shuffling feet, scratching bum, etc.
+#ifdef RAD_WIN32
+ int mPCCamFacing; // 0 = cam direction, 1 = cam's right, 2 = facing cam, 3 = cam's left
+#endif
+
+
+ rmt::Matrix mPrevSimTransform;
+
+protected: // METHODS
+ void TestInFrustrumOfPlayer( int playerID );
+
+ void UpdateGroundHeight(void);
+
+ void SetParentTransform( const rmt::Matrix& mat, float timeins = 0.0f );
+ sim::CollisionVolume* FindStandingVolume( const rmt::Vector& inPos, sim::CollisionVolume* inVolume, rmt::Vector& outNormal, float& outDist );
+ bool GetCollisionHeight( const rmt::Vector& prevPosition, const rmt::Vector& position, rmt::Vector& outPosition, rmt::Vector& collisionNormal ) ;
+ bool CanStandOnCollisionNormal( const rmt::Vector& normal ) const;
+ bool CanStaggerCollisionNormal( const rmt::Vector& normal ) const;
+
+ void UpdateController( rmt::Vector& direction, float timeins );
+ virtual void UpdateDesiredDirAndSpeed( const rmt::Vector& dir );
+ virtual void UpdateFootPlant( void );
+ void UpdatePuppet( float timeins );
+ void UpdateGroundPlane( float timeins );
+ // Is the character allowed to move?
+ bool IsMovementLocked() { return m_IsBeingShocked; }
+
+
+ virtual void AssignCollisionAreaIndex( void );
+ virtual float GetInputScale( void )
+ {
+ // Number arrived at via experimentation.
+ //
+ return 0.69f;
+ }
+
+protected: // MEMBERS
+ bool mIsNPC;
+
+ // ground plane stuff for player character only
+ sim::ManualSimState* mGroundPlaneSimState;
+ sim::WallVolume* mGroundPlaneWallVolume;
+
+ int mCollisionAreaIndex;
+
+ static sim::TArray<sim::RayIntersectionInfo> msIntersectInfo;
+
+ radTime64 mLastInteriorLoadCheck;
+
+private: // METHODS
+ virtual void OnUpdateRoot( float timeins );
+ virtual void OnPostSimUpdate(float timeins);
+ void UpdateShock( float timeins );
+ ActionButton::ButtonHandler* TestPriority( ActionButton::ButtonHandler* bA, ActionButton::ButtonHandler* bB );
+
+
+private: // MEMBERS
+ // The abstract controller object.
+ //
+ CharacterController* mpController;
+
+ // A pointer to the renderable object.
+ //
+ CharacterRenderable* mpCharacterRenderable;
+
+ // A pointer to the choreo::Puppet
+ //
+ choreo::Puppet* mpPuppet;
+
+ // The facing angle, in radians. 0 = ( 0, 0, -1 )
+ //
+ float mfFacingDir;
+
+ // The desired facing angle. The character will attempt to converge on this angle.
+ //
+ float mfDesiredDir;
+
+ // Scalar velocity along the facing angle.
+ //
+ float mfSpeed;
+
+ rmt::Vector mVelocity;
+
+ // The desired speed. The character will accel or decel towards this speed.
+ //
+ float mfDesiredSpeed;
+
+ // Is in car.
+ //
+ bool mbInCar;
+
+ // Whether the characters feet were planted last update
+ //
+ std::vector< bool, s2alloc<bool> > mbWasFootPlanted;
+
+ // For camera tracking.
+ //
+ CharacterTarget* mpCharacterTarget;
+
+ // For ai and animation.
+ //
+ ActionController* mpActionController;
+
+
+ //To control action button priority, we do the following.
+ ActionButton::ButtonHandler* mpActionButtonHandlers[ MAX_ACTION_BUTTON_HANDLERS ];
+ ActionButton::ButtonHandler* mpCurrentActionButtonHandler;
+
+ Vehicle* mpTargetVehicle;
+
+ static float sfMaxSpeed;
+
+ // Terrain positioning.
+ //
+ float mGroundY;
+ rmt::Vector mGroundNormal;
+ eTerrainType mTerrainType; // What type of terrain is the character on.
+ bool mInteriorTerrain; // The terrain is covered/inside. Such as a tunnel or building interior.
+
+ rmt::Vector mRealGroundPos; // ground, excluding statics we may be on.
+ rmt::Vector mRealGroundNormal; // ground, excluding statics we may be on.
+
+ CharacterAi::StateManager* mpStateManager;
+
+ float mfRadius;
+
+// Hack.
+ friend class CharacterRenderable;
+ // Collision detection and response.
+ //
+ bool mbCollided;
+ int mCurrentCollision;
+ struct CollisionData
+ {
+ CollisionData( void )
+ :
+ mCollisionDistance( 0.0f ),
+ mpCollisionVolume( 0 )
+ {
+ }
+ static const int MAX_COLLISIONS = 8;
+ rmt::Vector mCollisionPosition;
+ rmt::Vector mCollisionNormal;
+ float mCollisionDistance;
+ sim::CollisionVolume* mpCollisionVolume;
+ };
+ CollisionData mCollisionData[ CollisionData::MAX_COLLISIONS ];
+
+ bool mbIsStanding;
+ WalkerLocomotionAction* mpWalkerLocomotion;
+ JumpAction* mpJumpLocomotion;
+
+
+ sim::CollisionVolume* mpStandingCollisionVolume;
+ const poser::Joint* mpStandingJoint;
+ rmt::Matrix mParentTransform;
+ rmt::Matrix mInvParentTransform;
+ float mfGroundVerticalVelocity;
+ float mfGroundVerticalPosition;
+
+ bool mbTurbo;
+ bool mbIsJump;
+ bool mbSolveCollisions;
+
+ static const int MAX_PROPS = 1;
+
+ Prop mPropList[ MAX_PROPS ];
+
+ ActionButton::AttachProp* mpPropHandler;
+ int mPropJoint;
+
+ bool mVisible;
+
+ WorldScene* mpWorldScene;
+ bool m_IsSimpleShadow;
+
+ // when we apply choreo puppet skeleton over top of differently
+ // scaled character model, this value is to adjust the difference between
+ // the character model root and the npd skeleton root. Can be +ve or -ve
+ float mYAdjust;
+
+ bool mbBusy;
+
+ bool mbSimpleLoco;
+ bool mbNeedChoreoUpdate;
+
+ tColour mShadowColour;
+
+ float m_TimeLeftToShock;
+ bool m_IsBeingShocked;
+
+ bool mDoKickwave;
+ tDrawable* mKickwave;
+ tFrameController* mKickwaveController;
+
+ bool mAmbient;
+ tUID mAmbientLocator;
+ AmbientDialogueTrigger* mAmbientTrigger;
+
+ rmt::Vector mLastFramePos;
+
+ bool mbDoGroundIntersect;
+
+ unsigned mIntersectFrame;
+
+ bool mAllowRockin;
+
+ bool mHasBeenHit;
+
+ bool mbSnapToGround;
+
+ float mSecondsSinceActionControllerUpdate;
+
+ bool mTooFarToUpdate;
+
+ float mSecondsSinceOnPostSimUpdate;
+
+ Role mRole;
+
+ float mScale;
+
+ bool mCollidedThisFrame;
+
+ bool mIsInSubstep;
+
+ rmt::Vector mLean;
+
+ bool mIsLisa;
+ bool mIsMarge;
+
+ rmt::Vector mLastGoodPosOverStatic;
+
+ rmt::Vector lameAssPosition;
+
+ bool mManaged;
+};
+
+/*
+==============================================================================
+Character::GetFacing
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& facingVector )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::GetFacing( rmt::Vector& facingVector ) const
+{
+ if ( mpPuppet )
+ {
+ // Transform from object to world space.
+ //
+ facingVector = mpPuppet->GetFacingVector( );
+ // Transform from object to world space.
+ //
+ //mInvParentTransform.RotateVector( facingVector, &facingVector );
+ return;
+ }
+ facingVector.Set( 0.0f, 0.0f, -1.0f );
+}
+/*
+==============================================================================
+Character::GetFacingDir
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+inline float Character::GetFacingDir( void ) const
+{
+ rmt::Vector facing;
+ GetFacing( facing );
+ return choreo::GetWorldAngle( facing.x, facing.z );
+}
+
+/*
+==============================================================================
+Character::SetFacing
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& facingVector )
+
+Return: inline
+
+=============================================================================
+*/
+inline void Character::SetFacing( rmt::Vector& facingVector )
+{
+ if ( mpPuppet )
+ {
+ mpPuppet->SetFacingVector( facingVector );
+ }
+}
+/*
+==============================================================================
+Character::SetFacingDir
+==============================================================================
+Description: Comment
+
+Parameters: ( float fDir )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetFacingDir( float fDir )
+{
+ rmt::Vector facing;
+ facing = choreo::DEFAULT_FACING_VECTOR;
+ choreo::RotateYVector( fDir, facing );
+ SetFacing( facing );
+}
+/*
+==============================================================================
+Character::GetDesiredFacing
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& facingVector )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::GetDesiredFacing( rmt::Vector& facingVector ) const
+{
+ facingVector = choreo::DEFAULT_FACING_VECTOR;
+ choreo::RotateYVector( mfDesiredDir, facingVector );
+ // Transform from object to world space.
+ //
+ //mInvParentTransform.RotateVector( facingVector, &facingVector );
+}
+/*
+==============================================================================
+Character::GetDesiredDir
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+inline float Character::GetDesiredDir( void ) const
+{
+ rmt::Vector facing;
+ GetDesiredFacing( facing );
+ return choreo::GetWorldAngle( facing.x, facing.z );
+}
+
+/*
+==============================================================================
+Character::SetPosition
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& position )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetPosition( const rmt::Vector& position )
+{
+ if ( mpPuppet )
+ {
+ // Transform from world to object space.
+ //
+ rmt::Vector transformedPos = position;
+ transformedPos.Transform( mInvParentTransform );
+
+ mpPuppet->SetPosition( transformedPos );
+ }
+
+ sim::SimState* simState = mpSimStateObj; //GetSimState();
+ if( simState != NULL && simState->GetControl() == sim::simAICtrl )
+ {
+ UpdateSimState( 0.0f );
+ }
+}
+
+/*
+==============================================================================
+Character::GetPosition
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& position )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::GetPosition( rmt::Vector& position ) const
+{
+ if ( mpPuppet )
+ {
+ position = mpPuppet->GetPosition( );
+ position.Transform( mParentTransform );
+ }
+ else
+ {
+ position.Set(0.0f, 0.0f, 0.0f);
+ }
+}
+/*
+==============================================================================
+Character::GetVelocity
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& velocity )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::GetVelocity( rmt::Vector& velocity ) const
+{
+ velocity = mVelocity;
+}
+/*
+==============================================================================
+Character::GetLocoVelocity
+==============================================================================
+Description: How fast is the character running/walking (locomoting)
+
+Parameters: ()
+
+Return: inline
+
+=============================================================================
+*/
+inline const rmt::Vector& Character::GetLocoVelocity() const
+{
+ return mpPuppet->GetVelocity( );
+}
+/*
+==============================================================================
+Character::GetSpeed
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+inline float Character::GetSpeed( void ) const
+{
+ return mfSpeed;
+}
+/*
+==============================================================================
+Character::SetSpeed
+==============================================================================
+Description: Comment
+
+Parameters: ( float fSpeed )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetSpeed( float fSpeed )
+{
+ rmt::Vector facing;
+ GetFacing( facing );
+ facing.Scale( fSpeed );
+ mParentTransform.RotateVector( facing, &facing );
+ mpSimStateObj->VelocityState( ).mLinear = facing;
+}
+/*
+==============================================================================
+Character::GetDesiredSpeed
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+inline float Character::GetDesiredSpeed( void ) const
+{
+ return mfDesiredSpeed;
+}
+/*
+
+/*
+==============================================================================
+Character::SetDesiredDir
+==============================================================================
+Description: Comment
+
+Parameters: ( float fDesiredDir )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetDesiredDir( float fDesiredDir )
+{
+ mfDesiredDir = fDesiredDir;
+}
+/*
+==============================================================================
+Character::SetDesiredSpeed
+==============================================================================
+Description: Comment
+
+Parameters: ( float fDesiredSpeed )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetDesiredSpeed( float fDesiredSpeed )
+{
+ if ( fDesiredSpeed > GetMaxSpeed( ) )
+ {
+ fDesiredSpeed = GetMaxSpeed( );
+ }
+ mfDesiredSpeed = fDesiredSpeed;
+}
+
+/*
+==============================================================================
+Character::GetPuppet
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: choreo
+
+=============================================================================
+*/
+inline choreo::Puppet* Character::GetPuppet( void ) const
+{
+ return mpPuppet;
+}
+
+/*
+==============================================================================
+Character::IsInCar
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool Character::IsInCar( void ) const
+{
+ return mbInCar;
+}
+
+
+/*
+==============================================================================
+Character::SetController
+==============================================================================
+Description: Comment
+
+Parameters: ( CharacterController* pController )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetController( CharacterController* pController )
+{
+ tRefCounted::Assign( mpController, pController );
+}
+/*
+==============================================================================
+Character::GetController
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterController
+
+=============================================================================
+*/
+inline CharacterController* Character::GetController( void ) const
+{
+ return mpController;
+}
+
+/*
+==============================================================================
+Character::GetTarget
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterTarget
+
+=============================================================================
+*/
+inline CharacterTarget* Character::GetTarget( void ) const
+{
+ rAssert( mpCharacterTarget );
+ return mpCharacterTarget;
+}
+
+/*
+==============================================================================
+Character::SetTargetVehicle
+==============================================================================
+Description: Comment
+
+Parameters: ( Vehicle* pVehicle )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetTargetVehicle( Vehicle* pVehicle )
+{
+ mpTargetVehicle = pVehicle;
+}
+/*
+==============================================================================
+Character::GetTargetVehicle
+==============================================================================
+Description: Comment
+
+Parameters: ( )
+
+Return: Vehicle
+
+=============================================================================
+*/
+inline Vehicle* Character::GetTargetVehicle( ) const
+{
+ return mpTargetVehicle;
+}
+/*
+==============================================================================
+Character::GetStateManager
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterAi
+
+=============================================================================
+*/
+inline CharacterAi::StateManager* Character::GetStateManager( void ) const
+{
+ return mpStateManager;
+}
+
+/*
+==============================================================================
+Character::GetActionController
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: ActionController
+
+=============================================================================
+*/
+inline ActionController* Character::GetActionController( void ) const
+{
+ return mpActionController;
+}
+/*
+==============================================================================
+Character::GetJumpHeight
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+inline float Character::GetJumpHeight( void )
+{
+ return CharacterTune::sfJumpHeight;
+}
+/*
+==============================================================================
+Character::GetRootTransform
+==============================================================================
+Description: Comment
+
+Parameters: ( poser::Transform& out )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::GetRootTransform( poser::Transform& out ) const
+{
+ if ( mpPuppet )
+ {
+ out = mpPuppet->GetRootTransform( );
+ poser::Transform parentTransform;
+ parentTransform.SetMatrix( mParentTransform );
+ out.Mult( parentTransform );
+ }
+ else
+ {
+ out.Identity( );
+ }
+}
+/*
+==============================================================================
+Character::WorldToLocal
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& world )
+
+Return: rmt
+
+=============================================================================
+*/
+inline rmt::Vector Character::WorldToLocal( const rmt::Vector& world ) const
+{
+ rmt::Vector local = world;
+ local.Transform( mInvParentTransform );
+ return local;
+}
+/*
+==============================================================================
+Character::LocalToWorld
+==============================================================================
+Description: Comment
+
+Parameters: ( const rmt::Vector& local )
+
+Return: rmt
+
+=============================================================================
+*/
+inline rmt::Vector Character::LocalToWorld( const rmt::Vector& local ) const
+{
+ rmt::Vector world = local;
+ world.Transform( mParentTransform );
+ return world;
+}
+/*
+==============================================================================
+Character::GetCollisionAreaIndex
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: int
+
+=============================================================================
+*/
+inline int Character::GetCollisionAreaIndex( void ) const
+{
+ return mCollisionAreaIndex;
+}
+/*
+==============================================================================
+Character::IsInCollision
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool Character::IsInCollision( void ) const
+{
+ return mbCollided;
+}
+/*
+==============================================================================
+Character::IsStanding
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool Character::IsStanding( void ) const
+{
+ return mbIsStanding;
+}
+/*
+==============================================================================
+Character::GetWalkerLocomotionAction
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: WalkerLocomotionAction
+
+=============================================================================
+*/
+inline WalkerLocomotionAction* Character::GetWalkerLocomotionAction( void ) const
+{
+ return mpWalkerLocomotion;
+}
+/*
+==============================================================================
+Character::GetJumpLocomotionAction
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: JumpAction
+
+=============================================================================
+*/
+inline JumpAction* Character::GetJumpLocomotionAction( void ) const
+{
+ return mpJumpLocomotion;
+}
+/*
+==============================================================================
+Character::GetParentTransform
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: const
+
+=============================================================================
+*/
+inline const rmt::Matrix& Character::GetParentTransform( void ) const
+{
+ return mParentTransform;
+}
+/*
+==============================================================================
+Character::GetInverseParentTransform
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: const
+
+=============================================================================
+*/
+inline const rmt::Matrix& Character::GetInverseParentTransform( void ) const
+{
+ return mInvParentTransform;
+}
+/*
+==============================================================================
+Character::GetGroundVerticalVelocity
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: float
+
+=============================================================================
+*/
+inline float Character::GetGroundVerticalVelocity( void ) const
+{
+ return mfGroundVerticalVelocity;
+}
+
+/*
+==============================================================================
+Character::IsJumping
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool Character::IsJumping( void ) const
+{
+ return mbIsJump;
+}
+/*
+==============================================================================
+Character::SetJumping
+==============================================================================
+Description: Comment
+
+Parameters: ( bool bIsJumping )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetJumping( bool bIsJumping )
+{
+ mbIsJump = bIsJumping;
+}
+
+/*
+==============================================================================
+Character::SetTurbo
+==============================================================================
+Description: Comment
+
+Parameters: ( bool bTurbo )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetTurbo( bool bTurbo )
+{
+ if( bTurbo != mbTurbo )
+ {
+ if( bTurbo )
+ {
+ GetEventManager()->TriggerEvent( EVENT_TURBO_START, this );
+ }
+ mbTurbo = bTurbo;
+ }
+}
+/*
+==============================================================================
+Character::IsTurbo
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool Character::IsTurbo( void ) const
+{
+ return mbTurbo;
+}
+
+/*
+==============================================================================
+Character::IsSimpleShadow
+==============================================================================
+Description: Is the shadow a blobby shadow or a volume shadow?
+
+Parameters: ( void )
+
+Return: bool
+
+=============================================================================
+*/
+inline bool Character::IsSimpleShadow( void )
+{
+ return m_IsSimpleShadow;
+}
+/*
+==============================================================================
+Character::SetSimpleShadow
+==============================================================================
+Description: Set if the character's shadow is a blobby, in apposed to the
+ volumetric.
+
+Parameters: ( bool )
+
+Return: void
+
+=============================================================================
+*/
+inline void Character::SetSimpleShadow( bool IsSimpleShadow )
+{
+ m_IsSimpleShadow = IsSimpleShadow;
+}
+
+inline bool Character::IsNPC()
+{
+ return mIsNPC;
+}
+
+class NPCharacter
+:
+public Character
+{
+public:
+ NPCharacter( void );
+ ~NPCharacter( void );
+
+ virtual int GetAIRef() { return PhysicsAIRef::NPCharacter;}
+
+ virtual void AddToPhysics( void );
+ virtual void RemoveFromPhysics( void );
+
+ virtual void SubmitStatics( void );
+ virtual void SubmitAnimCollisions( void ) {};
+ virtual void SubmitDynamics( void );
+
+ virtual void UpdatePhysicsObjects( float timeins, int area );
+ virtual void OnTransitToAICtrl();
+
+ virtual void ApplyKickForce( const rmt::Vector& direction, float force );
+ virtual void ApplyForce( const rmt::Vector& direction, float force );
+protected:
+
+ virtual void AssignCollisionAreaIndex( void );
+ virtual float GetInputScale( void )
+ {
+ // Scale 1.0f does nothing.
+ return 1.0f;
+ }
+private:
+ virtual void OnUpdateRoot( float timeins );
+ virtual void OnPostSimUpdate(float timeins);
+ virtual void UpdateFootPlant( void )
+ {
+ }
+ int mMappableHandle;
+};
+
+
+
+#endif // CHARACTER_H_
+
diff --git a/game/code/worldsim/character/charactercontroller.cpp b/game/code/worldsim/character/charactercontroller.cpp
new file mode 100644
index 0000000..0b41b77
--- /dev/null
+++ b/game/code/worldsim/character/charactercontroller.cpp
@@ -0,0 +1,1668 @@
+#include <ai/sequencer/actioncontroller.h>
+#include <worldsim/character/charactercontroller.h>
+#include <worldsim/character/controllereventhandler.h>
+#include <worldsim/character/charactermappable.h>
+#include <worldsim/character/character.h>
+#include <input/inputmanager.h>
+#include <mission/gameplaymanager.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercam.h>
+
+#include <p3d/camera.hpp>
+#include <p3d/pointcamera.hpp>
+
+#include <events/eventmanager.h>
+
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+
+#include <roads/geometry.h>
+
+#include <worldsim/character/charactermanager.h>
+
+#include <ai/sequencer/action.h>
+#include <presentation/mouthflapper.h>
+#include <interiors/interiormanager.h>
+//#include <presentation/presentation.h>
+//#include <presentation/presentationanimator.h>
+
+/*
+==============================================================================
+CharacterController::CharacterController
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterController
+
+=============================================================================
+*/
+CharacterController::CharacterController( void )
+:
+mpCharacter( 0 ),
+mIntention( NONE ),
+mPreserveIntention( NONE ),
+mActive( true )
+{
+}
+/*
+==============================================================================
+CharacterController::~CharacterControlller
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterController
+
+=============================================================================
+*/
+CharacterController::~CharacterController( void )
+{
+ if ( mpCharacter )
+ {
+ mpCharacter = 0;
+ }
+}
+/*
+==============================================================================
+CharacterController::GetCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: Character
+
+=============================================================================
+*/
+Character* CharacterController::GetCharacter( void ) const
+{
+ return mpCharacter;
+}
+/*
+==============================================================================
+CharacterController::SetCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterController::SetCharacter( Character* pCharacter )
+{
+ mpCharacter = pCharacter;
+}
+/*
+==============================================================================
+PhysicalController::PhysicalController
+==============================================================================
+Description: Constructor
+
+Parameters: ( void )
+
+Return: n/a
+
+=============================================================================
+*/
+PhysicalController::PhysicalController( void )
+:
+mpCharacterMappable( 0 )
+{
+}
+/*
+==============================================================================
+PhysicalController::~PhysicalController
+==============================================================================
+Description: Destructor
+
+Parameters: ( void )
+
+Return: n/a
+
+=============================================================================
+*/
+PhysicalController::~PhysicalController( void )
+{
+ // This should release and set mpCharacterMappable = 0;
+ //
+ mpCharacterMappable->Release( );
+ mpCharacterMappable = 0;
+}
+/*
+==============================================================================
+PhysicalController::SetCharacterMappable
+==============================================================================
+Description: Comment
+
+Parameters: ( CharacterMappable* pMappable )
+
+Return: void
+
+=============================================================================
+*/
+void PhysicalController::SetCharacterMappable( CharacterMappable* pMappable )
+{
+ tRefCounted::Assign( mpCharacterMappable, pMappable );
+}
+/*
+==============================================================================
+PhysicalController::GetDirection
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector& outDirection )
+
+Return: void
+
+=============================================================================
+*/
+void PhysicalController::GetDirection( rmt::Vector& outDirection )
+{
+ if(mActive)
+ {
+ mpCharacterMappable->GetDirection( outDirection );
+ }
+ else
+ {
+ outDirection.Set(0.0f, 0.0f, 0.0f);
+ }
+}
+/*
+==============================================================================
+PhysicalController::GetValue
+==============================================================================
+Description: Comment
+
+Parameters: ( int buttonId )
+
+Return: float
+
+=============================================================================
+*/
+float PhysicalController::GetValue( int buttonId ) const
+{
+ if(mActive)
+ {
+ return GetCharacterMappable( )->GetValue( buttonId );
+ }
+ else
+ {
+ return 0.0f;
+ }
+}
+/*
+==============================================================================
+PhysicalController::IsButtonDown
+==============================================================================
+Description: Comment
+
+Parameters: ( int buttonId )
+
+Return: bool
+
+=============================================================================
+*/
+bool PhysicalController::IsButtonDown( int buttonId ) const
+{
+ if(mActive)
+ {
+ return GetCharacterMappable( )->IsButtonDown( buttonId ) || GetIntention( ) == (eIntention)buttonId;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+int PhysicalController::TimeSinceChange( int buttonId ) const
+{
+ return IsButtonDown(buttonId) ? 0 : GetCharacterMappable( )->GetButton( buttonId )->TimeSinceChange();
+};
+
+/*
+==============================================================================
+PhysicalController::GetCharacterMappable
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterMappable
+
+=============================================================================
+*/
+CharacterMappable* PhysicalController::GetCharacterMappable( void ) const
+{
+ return mpCharacterMappable;
+}
+/*
+==============================================================================
+CameraRelativeCharacterController::CameraRelativeCharacterController
+==============================================================================
+Description: Constructor
+
+Parameters: ( int index, tCamera* pCamera )
+
+Return: n/a
+
+=============================================================================
+*/
+CameraRelativeCharacterController::CameraRelativeCharacterController( void )
+:
+mpCamera( 0 ),
+mbCameraChange( false )
+{
+ mpEventHandler = new CameraRelativeCharacterControllerEventHandler( this );
+ GetEventManager()->AddListener( mpEventHandler, EVENT_CAMERA_CHANGE );
+}
+void CameraRelativeCharacterController::Create( Character* pCharacter,
+ CharacterMappable* pCharacterMappable )
+{
+MEMTRACK_PUSH_GROUP( "Character Controller" );
+ SetCharacter( pCharacter );
+ SetCharacterMappable( pCharacterMappable );
+ pCharacterMappable->SetCharacterController( this );
+MEMTRACK_POP_GROUP( "Character Controller" );
+}
+/*
+==============================================================================
+CameraRelativeCharacterController::~CameraRelativeCharacterController
+==============================================================================
+Description: Destructor
+
+Parameters: ( void )
+
+Return: n/a
+
+=============================================================================
+*/
+CameraRelativeCharacterController::~CameraRelativeCharacterController( void )
+{
+ if ( mpCamera )
+ {
+ mpCamera->Release( );
+ mpCamera = 0;
+ }
+ if ( mpEventHandler )
+ {
+ GetEventManager()->RemoveListener( mpEventHandler, EVENT_CAMERA_CHANGE );
+ delete mpEventHandler;
+ mpEventHandler = 0;
+ }
+}
+
+
+void CameraRelativeCharacterController::SetIntention( eIntention intention )
+{
+ if(!mActive)
+ {
+ return;
+ }
+
+ // ignore input if we're not in locomotion state
+ if( mpCharacter->GetStateManager()->GetState() == CharacterAi::INSIM )
+ {
+ // TODO:
+ // Should clear the intention here? or maybe just let the
+ // old intention linger? Hmm...
+ mIntention = CharacterController::NONE;
+ }
+ else
+ {
+ mIntention = intention;
+ }
+
+}
+
+
+/*
+==============================================================================
+CameraRelativeCharacterController::GetDirection
+==============================================================================
+Description: Transforms Controller relative inputs into camera relative inputs.
+ The character will carry on moving the same world direction independent
+ of camera position until the controller direction changes.
+
+Parameters: ( rmt::Vector& outDirection )
+
+Return: void
+
+=============================================================================
+*/
+void CameraRelativeCharacterController::GetDirection( rmt::Vector& outDirection )
+{
+ if(!mActive)
+ {
+ outDirection.Set(0.0f,0.0f,0.0f);
+ return;
+ }
+
+ if( mpCharacter->GetStateManager()->GetState() == CharacterAi::INSIM )
+ {
+ outDirection.Set( 0.0f, 0.0f, 0.0f ); // feed zero, so no walk anim
+ return;
+ }
+
+ GetCharacterMappable( )->GetDirection( outDirection );
+
+#ifdef RAD_WIN32
+ SuperCam* cam = GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam();
+ if( cam->GetType() == SuperCam::PC_CAM )
+ {
+ //We don't do anything to modify the direction.
+ return;
+ }
+
+#endif
+
+ if ( mbCameraChange )
+ {
+ float cos15 = 0.9659258262890682867497431997289f;
+ static float sfAngleTolerance = cos15;
+
+ // Compare the old direction with the new direction transformed
+ // by the old matrix.
+ //
+ outDirection.Transform( mLastCameraMatrix );
+
+ rmt::Vector normDir = outDirection;
+ normDir.Normalize();
+
+ rmt::Vector normLast = mLastDirection;
+ normLast.Normalize();
+
+ float dot = normDir.DotProduct( normLast );
+
+ if ( dot > sfAngleTolerance )
+ {
+ // Direction is the "same" within given tolerance.
+ //
+ outDirection = mLastDirection;
+ }
+ else
+ {
+ mbCameraChange = false;
+ }
+ }
+ else if ( mpCamera != (tCamera*)0 )
+ {
+ rmt::Matrix mat = mpCamera->GetCameraToWorldMatrix();
+ mat.IdentityTranslation( );
+ outDirection.Transform( mat );
+ }
+
+ // Rotate by the inv transform of whatever the character is
+ // standing on.
+ //
+ rmt::Matrix invMat = mpCharacter->GetInverseParentTransform();
+ invMat.IdentityTranslation( );
+ outDirection.Transform( invMat );
+
+ // This is designed to address analog stick hysteresis.
+ // If you hold the stick in a direction and release it, often it
+ // will snap back and momentarily read a value >90 deg from the intended direction.
+ // this will address those momentary values.
+ //
+ Character* pCharacter = GetCharacter();
+ rmt::Vector facing;
+ rmt::Vector normDir = outDirection;
+ normDir.Normalize();
+ pCharacter->GetFacing( facing );
+ float dot = facing.DotProduct( normDir );
+ static float sfDot = 0.000001f;
+ if ( dot <= -sfDot )
+ {
+ //rReleasePrintf( "%f dot\n", dot );
+ // gt 90 deg.
+ //
+ static float toleranceSqr = rmt::Sqr( 0.60f );
+ //rReleasePrintf( "%f MagnitudeSqr\n", outDirection.MagnitudeSqr( ) );
+ if ( outDirection.MagnitudeSqr( ) < toleranceSqr )
+ {
+ // Small value.
+ //
+ static float sfScale = 0.01f;
+ outDirection.Scale( sfScale );
+ }
+ }
+
+}
+/*
+==============================================================================
+CameraRelativeCharacterController::SetCamera
+==============================================================================
+Description: Comment
+
+Parameters: ( tCamera* pCamera )
+
+Return: void
+
+=============================================================================
+*/
+void CameraRelativeCharacterController::SetCamera( tCamera* pCamera )
+{
+ tRefCounted::Assign( mpCamera, pCamera );
+}
+
+/*
+==============================================================================
+CameraRelativeCharacterController::HandleEvent
+==============================================================================
+Description: Implements Devil May Cry style controls when the camera view switches.
+ The character will carry on moving the same world direction independent
+ of camera position until the controller direction changes.
+
+Parameters: ( EventEnum id, void* pEventData )
+
+Return: void
+
+=============================================================================
+*/
+void CameraRelativeCharacterController::HandleEvent( int id, void* pEventData )
+{
+ EventEnum eventId = (EventEnum)id;
+
+ if ( EVENT_CAMERA_CHANGE == eventId )
+ {
+ bool interior = GetInteriorManager()->IsEntering() || GetInteriorManager()->IsExiting() || GetInteriorManager()->IsInside();
+
+ if (mpCamera && !mbCameraChange && !interior)
+ {
+ // Get absolute direction we'll transform it down below.
+ //
+ GetCharacterMappable( )->GetDirection( mLastDirection );
+
+ // Get the camera matrix to store while in transition.
+ //
+ mLastCameraMatrix = mpCamera->GetCameraToWorldMatrix();
+ mLastCameraMatrix.IdentityTranslation( );
+
+ // Now transform mLastDirection by the camera matrix.
+ // We store it to maintain the CameraRelative controls to the previous
+ // camera.
+ //
+ // NB: We don't just call CameraRelativeCharacterController::GetDirection()
+ // because that will take into account the parentTransform as well.
+ //
+ mLastDirection.Transform( mLastCameraMatrix );
+
+ // Tell the controller that we want to hold onto mLastDirection.
+ //
+ mbCameraChange = true;
+
+ // Update the new camera.
+ //
+ SuperCam* pSuperCam = static_cast<SuperCam*>( pEventData );
+ tCamera* pCamera = pSuperCam->GetCamera();
+ SetCamera( pCamera );
+ }
+ }
+ else
+ {
+ rAssert( false );
+ }
+}
+
+////////////////////////////////////////////////// NPC CONTROLLER ///////////////////////////////////////////
+
+static const float SECONDS_BACK_TO_FOLLOW_PATH = 3.0f;
+static const float SECONDS_TRACKING_PLAYER = 0.3f;
+static const float NPC_FOLLOW_PATH_SPEED_MPS = 1.0f;
+
+static const float SECONDS_BETW_PANIC_CHANGE_DIRECTION = 2.0f;
+static const float NPC_PANIC_RUN_SPEED_MPS = 3.0f;
+
+static const int TALK_TIME_MILLISECONDS = 3000;
+static const int STOP_TALK_CHECK_INTERVAL_MILLISECONDS = 1000;
+
+static const float SECONDS_BETW_PLAYING_TURN_ANIMS = 0.5f;
+
+NPCController::NPCController()
+:
+mOffPath( false ),
+mMillisecondsInTalk( 0 ),
+mTalkTarget( NULL ),
+mListening( true ),
+mState( FOLLOWING_PATH ),
+mSecondsInStopped( 0.0f ),
+mNumNPCWaypoints( 0 ),
+mCurrNPCWaypoint( -1 ),
+mStartPanicking( false ),
+mSecondsChangePanicDir( 0.0f ),
+mSecondsSinceLastTurnAnim( SECONDS_BETW_PLAYING_TURN_ANIMS ),
+mUseTempWaypoint(false),
+mTempWaypoint(0.0f, 0.0f , 0.0f)
+{
+ mSpeedMps = GetFollowPathSpeedMps();
+ mDirection.Set( 0.0f, 0.0f, 1.0f );
+ mNormalizedDodgeDir.Set( 0.0f, 0.0f, 1.0f );
+ mMouthFlapper = new MouthFlapper();
+ mMouthFlapper->AddRef();
+ mMouthFlapper->SetIsEnabled( false );
+
+
+ mNormalizedPanicDir.Set( 0.0f, 0.0f, 1.0f );
+}
+
+
+NPCController::~NPCController( void )
+{
+ if( mMouthFlapper != NULL )
+ {
+ mMouthFlapper->Release();
+ }
+}
+
+void NPCController::SetCharacter( Character *pCharacter )
+{
+ CharacterController::SetCharacter( pCharacter );
+}
+
+bool NPCController::AddNPCWaypoint( const rmt::Vector& pt )
+{
+ // can't add another point
+ if( mNumNPCWaypoints >= MAX_NPC_WAYPOINTS )
+ {
+ return false;
+ }
+
+ if( mNumNPCWaypoints == 0 )
+ {
+ // set our sights on the first waypoint
+ mCurrNPCWaypoint = 0;
+ }
+ mNPCWaypoints[ mNumNPCWaypoints ] = pt;
+ mNumNPCWaypoints++;
+
+ return true;
+}
+void NPCController::IncitePanic()
+{
+ // he's gonna run, so let him RUN .... into things...
+ //GetCharacter()->SetSolveCollisions( true );
+
+ // flag this guy to start panicking, if he can't panick right now..
+ mStartPanicking = true;
+
+ // don't panick if already panicking or in limbo (deliberately set there)
+ if( mState == NPCController::PANICKING || mState == NPCController::NONE )
+ {
+ return;
+ }
+ /*
+ // if not in an ideal state for panicking...
+ if( mState != NPCController::FOLLOWING_PATH &&
+ mState != NPCController::STANDING &&
+ mState != NPCController::STOPPED )
+ {
+ return;
+ }
+ */
+
+ TransitToState( NPCController::PANICKING );
+
+}
+void NPCController::QuellPanic()
+{
+ mStartPanicking = false;
+ TransitToState( NPCController::STOPPED );
+}
+
+
+
+void NPCController::Update( float seconds )
+{
+
+ if( mpCharacter == NULL )
+ {
+ return;
+ }
+
+ // if character in simulation, no point
+ if( mpCharacter->GetSimState()->GetControl() == sim::simSimulationCtrl )
+ {
+ return;
+ }
+
+ // if character is not in locomotion state, no point...
+ if( mpCharacter->GetStateManager()->GetState() != CharacterAi::LOCO )
+ {
+ return;
+ }
+
+ //BEGIN_PROFILE( "NPCController::Update" );
+
+ // Grab the simstate control. we'll be checking this sporadically.
+ int control = mpCharacter->GetSimState()->GetControl();
+
+ if( mStartPanicking )
+ {
+ IncitePanic();
+ }
+
+ switch( mState )
+ {
+ case FOLLOWING_PATH:
+ {
+ //rAssert( control == sim::simAICtrl );
+ FollowPath( seconds );
+ DetectAndDodge( seconds );
+ }
+ break;
+ case STOPPED:
+ {
+ //rAssert( control == sim::simAICtrl );
+ mSecondsInStopped += seconds;
+ mSecondsSinceLastTurnAnim += seconds;
+
+ // set to 1.5f and they'll follow you terminator style!
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+
+ //
+ // Track player for awhile then we can start deciding
+ // whether or not we want/need to dodge...
+ //
+ rmt::Vector playerPos, myPos, myFacing;
+
+ Avatar* avatar = ::GetAvatarManager()->GetAvatarForPlayer(0);
+ avatar->GetPosition( playerPos );
+
+ mpCharacter->GetPosition( &myPos );
+
+ mpCharacter->GetFacing( myFacing );
+ //rAssert( rmt::Epsilon( myFacing.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ rmt::Vector toPlayer = playerPos - myPos;
+ toPlayer.NormalizeSafe();
+ rAssert( rmt::Epsilon( toPlayer.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ //cosN for N being > half the animation turn angle
+ const float COSALPHA_TURN_TOLERANCE = 0.8387f;
+
+ // cosN, so anything > than N we just set the direction directly rather than play anim
+ const float COSALPHA_QUICKTURN_TOLERANCE = 0.7071f;
+
+ float cosAlpha = myFacing.Dot( toPlayer );
+ if( cosAlpha < COSALPHA_QUICKTURN_TOLERANCE )
+ {
+ // just turn (no anim)
+ SetDirection( toPlayer );
+ float dir = choreo::GetWorldAngle( toPlayer.x, toPlayer.z );
+ mpCharacter->SetDesiredDir( dir );
+
+ // reset so we can do turn anim right away after this
+ mSecondsSinceLastTurnAnim = SECONDS_BETW_PLAYING_TURN_ANIMS;
+ }
+ else if( COSALPHA_QUICKTURN_TOLERANCE <= cosAlpha && cosAlpha <= COSALPHA_TURN_TOLERANCE )
+ {
+ if( mSecondsSinceLastTurnAnim >= SECONDS_BETW_PLAYING_TURN_ANIMS )
+ {
+ // figure out whether to turn left or right
+ rmt::Vector myRight = Get90DegreeRightTurn( myFacing );
+ if( myRight.Dot( toPlayer ) > 0.0f )
+ {
+ // player's on my right, turn right
+ SetIntention( TurnRight );
+ }
+ else
+ {
+ // player's on my left, turn left
+ SetIntention( TurnLeft );
+ }
+ mSecondsSinceLastTurnAnim = 0.0f; // back to zero!
+ }
+ }
+
+ if( mSecondsInStopped > SECONDS_BACK_TO_FOLLOW_PATH )
+ {
+
+ // if we are going to be too close to other characters, transit
+ // to stopped here.
+ CharacterManager* cm = GetCharacterManager();
+
+ const float TOO_CLOSE_TO_NPC_DIST_SQR = 2.0f;
+
+ //
+ // if I'm a ped, I can ignore other peds (in THIS check only) cuz I already
+ // deal with them safely elsewhere... If I'm a non-ped NPC, I would
+ // want to check for peds, so I don't walk through them.
+ //
+ bool ignorePeds = (mpCharacter->GetRole() == Character::ROLE_PEDESTRIAN)? true : false;
+
+ int maxChars = cm->GetMaxCharacters();
+
+ bool gonnaHit = false;
+
+ for( int i=0; i<maxChars; i++ )
+ {
+ Character* c = cm->GetCharacter( i );
+ if( c && c != mpCharacter )
+ {
+ if( ignorePeds && c->GetRole() == Character::ROLE_PEDESTRIAN )
+ {
+ continue;
+ }
+ rmt::Vector cPos;
+ c->GetPosition( cPos );
+
+ float distSqr = (cPos - myPos).MagnitudeSqr();
+ if( distSqr <= TOO_CLOSE_TO_NPC_DIST_SQR )
+ {
+ gonnaHit = true;
+ break;
+ }
+ }
+ }
+
+ if( gonnaHit )
+ {
+ mSecondsInStopped = 0.0f;
+ }
+ else
+ {
+ TransitToState(FOLLOWING_PATH);
+ }
+ }
+ //
+ // If we've been tracking player long enough,
+ // time to dodge!!!
+ //
+ if( mSecondsInStopped > SECONDS_TRACKING_PLAYER )
+ {
+ DetectAndDodge( seconds );
+ }
+
+ }
+ break;
+ case DODGING:
+ {
+ //rAssert( control == sim::simAICtrl );
+ // so NPCs don't dodge into someplace indeterminate
+ //mpCharacter->SetSolveCollisions( true );
+
+ // sets NeedsUpdate flag for character (so it doens't pause
+ // in midmotion if player moves out of physics simming radius)
+ mpCharacter->Update( seconds );
+ }
+ break;
+ case CRINGING:
+ {
+ //rAssert( control == sim::simAICtrl );
+ }
+ break;
+ case TALKING:
+ {
+ rAssert( mTalkTarget != NULL );
+ //rAssert( control == sim::simAICtrl );
+
+ int coinflip = 0;
+
+ // Direction is set for us when we transited to this state
+ // it never needs changing.
+ //SetDirection( );
+
+ // if target is no longer in TALKING state, we stop too...
+ if( mTalkTarget->GetState() != TALKING )
+ {
+ TransitToState(FOLLOWING_PATH);
+ break;
+ }
+
+ if( mListening )
+ {
+ rAssert( mTalkTarget != NULL );
+ rAssert( !mMouthFlapper->IsEnabled() );
+ rAssert( !mTalkTarget->mListening ); // one of us needs to do the talking! geez...
+ rAssert( mTalkTarget->mMouthFlapper->IsEnabled() );
+
+ mMillisecondsInTalk = 0;
+ }
+ else
+ {
+ rAssert( mTalkTarget != NULL );
+ rAssert( mMouthFlapper->IsEnabled() );
+ rAssert( mTalkTarget->mListening ); // only one of us can talk at once! geez...
+ rAssert( !mTalkTarget->mMouthFlapper->IsEnabled() );
+
+ mMillisecondsInTalk += (int)(seconds*1000);
+ if( mMillisecondsInTalk >= TALK_TIME_MILLISECONDS )
+ {
+ // ok, by now we've talked for longer than alotted time,
+ // so every check interval, we have a chance to
+ // stop talking and let the other person start
+ // talking.
+
+ if( (mMillisecondsInTalk - TALK_TIME_MILLISECONDS) >=
+ STOP_TALK_CHECK_INTERVAL_MILLISECONDS )
+ {
+ mMillisecondsInTalk = TALK_TIME_MILLISECONDS;
+
+ coinflip = rand() % 3;
+ // 2 in 3 chance in favor of stopping our yammer
+ if( coinflip != 2 )
+ {
+ StopTalking();
+ mTalkTarget->StartTalking();
+ }
+ }
+ }
+ }
+
+ /*
+ // there's a good chance we won't bother to detect cars while talking... uh oh!
+ coinflip = rand() % 1000;
+ if( coinflip == 0 )
+ {
+ DetectAndDodge( seconds );
+ }
+ */
+ }
+ break;
+ case STANDING:
+ {
+ //rAssert( control == sim::simAICtrl );
+ mSecondsInStopped += seconds;
+ if( mSecondsInStopped > SECONDS_BACK_TO_FOLLOW_PATH )
+ {
+ TransitToState(FOLLOWING_PATH);
+ break;
+ }
+ DetectAndDodge( seconds );
+ }
+ break;
+ case PANICKING:
+ {
+ //rAssert( control == sim::simAICtrl );
+
+ rmt::Vector myPos;
+ mpCharacter->GetPosition( myPos );
+
+ mSecondsChangePanicDir += seconds;
+ if( mSecondsChangePanicDir > SECONDS_BETW_PANIC_CHANGE_DIRECTION )
+ {
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( player );
+
+ rmt::Vector playerPos;
+ player->GetPosition( playerPos );
+
+ rmt::Vector awayFromPlayer = myPos - playerPos;
+
+ // vary direction of panic run by modifying at random .x & .z
+ // of the awayFromPlayer vector...
+
+ float randomXVariance = 0.0f;
+ float randomZVariance = 0.0f;
+ const float FAR_ENOUGH_TO_CHANGE_DIR_SQR = 36.0f;
+ if( awayFromPlayer.MagnitudeSqr() > FAR_ENOUGH_TO_CHANGE_DIR_SQR )
+ {
+ randomXVariance = (float)(rand()%500) / 100.0f;
+ randomXVariance *= (rand() % 2)? 1.0f : -1.0f;
+ randomZVariance = (float)(rand()%500) / 100.0f;
+ randomZVariance *= (rand() % 2)? 1.0f : -1.0f;
+ }
+ awayFromPlayer.x += randomXVariance;
+ awayFromPlayer.z += randomZVariance;
+
+ awayFromPlayer.NormalizeSafe(); // *** SQUARE ROOT! ***
+
+ mNormalizedPanicDir = awayFromPlayer;
+ mSecondsChangePanicDir = 0.0f;
+ }
+ SetDirection( mNormalizedPanicDir );
+
+ }
+ break;
+ case TALKING_WITH_PLAYER: // fall thru
+ case NONE:
+ break; // do nothing
+ default:
+ {
+ // bad state
+ rAssert( false );
+ }
+ }
+ //END_PROFILE( "NPCController::Update" );
+}
+
+float NPCController::GetFollowPathSpeedMps() const
+{
+ if( mOffPath )
+ {
+ return NPC_FOLLOW_PATH_SPEED_MPS;
+ }
+ if( mNumNPCWaypoints > 1 )
+ {
+ return NPC_FOLLOW_PATH_SPEED_MPS;
+ }
+ return 0.0f;
+}
+
+
+void NPCController::FollowPath( float seconds )
+{
+ // if we're in collision, we should transit to STOPPED.
+ if( mpCharacter->mbCollidedWithVehicle )
+ {
+ TransitToState( STOPPED );
+ return;
+ }
+
+ // get our pos
+ rmt::Vector currPos;
+ mpCharacter->GetPosition( &currPos );
+
+
+ // if we are going to be too close to other characters, transit
+ // to stopped here.
+ CharacterManager* cm = GetCharacterManager();
+
+ const float TOO_CLOSE_TO_NPC_DIST_SQR = 2.0f;
+
+ //
+ // if I'm a ped, I can ignore other peds (in THIS check only) cuz I already
+ // deal with them safely elsewhere... If I'm a non-ped NPC, I would
+ // want to check for peds, so I don't walk through them.
+ //
+ bool ignorePeds = (mpCharacter->GetRole() == Character::ROLE_PEDESTRIAN)? true : false;
+
+ int maxChars = cm->GetMaxCharacters();
+ for( int i=0; i<maxChars; i++ )
+ {
+ Character* c = cm->GetCharacter( i );
+ if( c && c != mpCharacter )
+ {
+ if( ignorePeds && c->GetRole() == Character::ROLE_PEDESTRIAN )
+ {
+ continue;
+ }
+ rmt::Vector cPos;
+ c->GetPosition( cPos );
+
+ float distSqr = (cPos - currPos).MagnitudeSqr();
+ if( distSqr <= TOO_CLOSE_TO_NPC_DIST_SQR )
+ {
+ TransitToState( STOPPED );
+ return;
+ }
+ }
+ }
+
+
+ //mpCharacter->SetSolveCollisions( true );
+
+ mSpeedMps = GetFollowPathSpeedMps();
+ mpCharacter->SetSpeed( mSpeedMps );
+
+ // If we're off our path, pathfind way back on path
+ rmt::Vector lastPos;
+
+ //rAssert( 1 <= mNumNPCWaypoints && mNumNPCWaypoints < MAX_NPC_WAYPOINTS );
+ //rAssert( 0 <= mCurrNPCWaypoint && mCurrNPCWaypoint < mNumNPCWaypoints );
+
+ // Find the good pos on path that we want to be at, or the point
+ // itself if no waypoints, ok?
+ if(mUseTempWaypoint)
+ {
+ lastPos = mTempWaypoint;
+ if((lastPos - mNPCWaypoints[0]).Magnitude() < 20.0f) // were close to our path again, clear the temp waypoint
+ {
+ lastPos = mNPCWaypoints[0];
+ mUseTempWaypoint = false;
+ }
+ }
+ else if( mNumNPCWaypoints == 1 )
+ {
+ lastPos = mNPCWaypoints[0]; // only one waypoint, so this is it.
+ }
+ else if( mNumNPCWaypoints > 1 )
+ {
+ rmt::Vector start, end;
+ end = mNPCWaypoints[ mCurrNPCWaypoint ];
+
+ int lastPt = mCurrNPCWaypoint - 1;
+ if( lastPt < 0 )
+ {
+ lastPt = mNumNPCWaypoints - 1;
+ }
+ rAssert( 0 <= lastPt && lastPt < mNumNPCWaypoints );
+ start = mNPCWaypoints[ lastPt ];
+
+ end.y = start.y = currPos.y; // ignore heights
+
+ FindClosestPointOnLine( start, end, currPos, lastPos );
+ }
+ else
+ {
+ // do nothing!
+ lastPos = currPos;
+ }
+
+ rmt::Vector epsilon;
+ GetAllowedPathOffset( epsilon );
+
+ bool same = false;
+ if( rmt::Epsilon( currPos.x, lastPos.x, epsilon.x ) &&
+ //rmt::Epsilon( currPos.y, lastPos.y, epsilon.y ) && // IGNORE y VALUE
+ rmt::Epsilon( currPos.z, lastPos.z, epsilon.z ) )
+ {
+ same = true;
+ }
+
+ if( !mOffPath && same )
+ {
+ // we were on path, and we're still on path
+ return;
+ }
+ else if( mOffPath && same )
+ {
+ // we were off path, now we're on path
+ mOffPath = false;
+ return;
+ }
+ else if( !same )
+ {
+ // we are not on path...
+ mOffPath = true;
+
+ bool tooFar = (lastPos - currPos).Magnitude() > 100.0f;
+
+ // If no one is looking, might as well warp baby!
+ if( !mpCharacter->mbInAnyonesFrustrum || tooFar)
+ {
+ // Make sure where we're warping to is also not in anyone's frustrum...
+ bool bLastPosInAnyonesFrustrum = false;
+ for( int i=0; i<GetGameplayManager()->GetNumPlayers(); i++ )
+ {
+ if( mpCharacter->PosInFrustrumOfPlayer( lastPos, i ) )
+ {
+ bLastPosInAnyonesFrustrum = true;
+ break;
+ }
+ }
+ if( !bLastPosInAnyonesFrustrum || tooFar)
+ {
+ float facing = mpCharacter->GetFacingDir();
+ mpCharacter->RelocateAndReset( lastPos, facing, false );
+ mOffPath = false;
+ return;
+ }
+ }
+
+ //mpCharacter->SetSolveCollisions( true );
+ OnOffPath( lastPos, currPos );
+ }
+}
+
+void NPCController::TeleportToPath( void )
+{
+ float facing = mpCharacter->GetFacingDir();
+ mpCharacter->RelocateAndReset( mNPCWaypoints[0], facing, false );
+ mOffPath = false;
+ ClearTempWaypoint();
+}
+
+void NPCController::DetectAndDodge( float seconds )
+{
+ // if we're in a street race, don't do any detecting or dodging
+ // cuz it could put us outside the race props boundaries.
+ Mission* m = GetGameplayManager()->GetCurrentMission();
+ if( m && m->mIsStreetRace1Or2 )
+ {
+ rmt::Vector dir( 0.0f, 0.0f, 0.0f );
+ SetDirection( dir );
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+ return;
+ }
+
+
+ float hisVel = 0.0f;
+ float epsilon = 0.001f;
+
+ bool willGetHit = Detect( seconds, hisVel );
+
+ if( willGetHit )
+ {
+ //If we haven't tried stopping...
+ if( mState != STOPPED )
+ {
+ TransitToState( STOPPED );
+ }
+ // ok, we're stopped, but are they're still coming?
+ else
+ {
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar != NULL );
+
+ if( hisVel > epsilon ) // we're already stopped, but he's still coming
+ {
+ // Well if they're not in a vehicle, who cares!
+ if( !avatar->IsInCar() )
+ {
+ return;
+ }
+
+ int coinflip = rand() % 2;
+ if( coinflip == 0 )
+ {
+ //TransitToState(CRINGING);
+ PerformCringe();
+ return;
+ }
+ else
+ {
+ //TransitToState(DODGING);
+ PerformDodge( );
+ return;
+ }
+ }
+ }
+ }
+
+ //
+ // The reason we do this after is that we may have needed to transit
+ // back to STOPPED (in the code above) after we've just transitted
+ // from there to FOLLOWING_PATH because we might hit the player.
+ // So if the state is still FOLLOWING_PATH despite detecting and
+ // dodging, we'll go ahead and traverse the pedpathsegments
+ //
+ if( mState == FOLLOWING_PATH )
+ {
+ TraversePath( seconds );
+ }
+}
+
+
+void NPCController::TraversePath( float seconds )
+{
+ rmt::Vector dir( 0.0f, 0.0f, 0.0f );
+ if( mOffPath )
+ {
+ // allow pathfinding back (don't set speed to zero or set direction to zero)
+ return;
+ }
+
+ if( mCurrNPCWaypoint == -1 )
+ {
+ SetDirection( dir );
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+ return;
+ }
+
+ if( (mNumNPCWaypoints < 2) || mUseTempWaypoint)
+ {
+ SetDirection( dir );
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+ return;
+ }
+
+ // loop back to the first waypoint
+ if( mCurrNPCWaypoint >= mNumNPCWaypoints )
+ {
+ mCurrNPCWaypoint = 0;
+ }
+
+ rAssert( 0 <= mCurrNPCWaypoint && mCurrNPCWaypoint < mNumNPCWaypoints );
+ rmt::Vector wayPos = mNPCWaypoints[ mCurrNPCWaypoint ];
+
+ rmt::Vector myPos;
+ GetCharacter()->GetPosition( myPos );
+
+ // if player not close enough, don't bother to do this...
+ rmt::Vector playerPos;
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( playerPos );
+
+ const float DIST_PLAYER_TOO_FAR_TO_BOTHER_SQR = 22500.0f;
+ if( (playerPos-myPos).MagnitudeSqr() >= DIST_PLAYER_TOO_FAR_TO_BOTHER_SQR )
+ {
+ SetDirection( dir );
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+ return;
+ }
+
+ // ignore height differences
+ wayPos.y = myPos.y;
+ dir = wayPos - myPos;
+
+ float distToWaypoint = dir.Magnitude(); // *** SQUARE ROOT! ***
+ const float DIST_CLOSE_ENOUGH_TO_NPC_WAYPOINT = 0.5f;
+ if( distToWaypoint < DIST_CLOSE_ENOUGH_TO_NPC_WAYPOINT )
+ {
+ // increment curr waypoint
+ mCurrNPCWaypoint++;
+ if( mCurrNPCWaypoint >= mNumNPCWaypoints )
+ {
+ mCurrNPCWaypoint = 0; // increment for next round
+ }
+
+ // Non-ped NPCs will want to do some idle anims... Peds
+ // will just keep on going...
+ OnReachedWaypoint();
+ }
+
+ if( distToWaypoint > 0.0f )
+ {
+ SetDirection( dir / distToWaypoint );
+ mSpeedMps = GetFollowPathSpeedMps();
+ mpCharacter->SetSpeed( mSpeedMps );
+ }
+}
+
+void NPCController::OnReachedWaypoint()
+{
+ /*
+ // Reached a waypoint, so do some anim here if we're in the player's
+ // frustrum
+ if( mpCharacter->IsVisible() && mpCharacter->mbInAnyonesFrustrum )
+ {
+ PresentationAnimator* npcAnimator = GetPresentationManager()->GetAnimatorNpc();
+ Character* oldCharacter = npcAnimator->GetCharacter();
+ const bool oldRandomSelection = npcAnimator->GetRandomSelection();
+
+ npcAnimator->SetCharacter( mpCharacter );
+ npcAnimator->SetRandomSelection( true );
+ npcAnimator->PlaySpecialAmbientAnimation();
+ npcAnimator->SetCharacter( oldCharacter );
+ npcAnimator->SetRandomSelection( oldRandomSelection );
+ }
+ */
+}
+
+
+
+static const float DODGE_SPEED_MPS = 1.0f; // doesn't seem to matter as long as > 0.0f
+static const float CRINGE_SPEED_MPS = 0.0f;
+
+static const float VEHICLE_SPAN_METERS = 1.5f;
+static const float CHARACTER_SPAN_METERS = 0.5f;
+static const float COLLISION_LOOK_AHEAD_SECONDS = 1.0f;
+static const float COLLISION_DISTANCE_SQUARED = 400.0f; // must be within this to calc collisions
+
+bool NPCController::Detect( float seconds, float& hisVel )
+{
+ hisVel = 0.0f; // init return value to something
+
+ // Clear out previous use of mDodgeInfo
+ mDodgeInfo.wasSetThisFrame = false;
+
+
+ // Things we use over and over again in this function
+ rmt::Vector playerPos, playerHeading, playerSide, playerUp, playerVel;
+ rmt::Vector myPos, myHeading, mySide, myUp, myVel;
+
+
+ //////////////////////////////////////
+ // Information about player
+ //////////////////////////////////////
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar != NULL );
+ avatar->GetPosition( playerPos );
+ float playerCollisionRadius =
+ (avatar->IsInCar())? VEHICLE_SPAN_METERS : CHARACTER_SPAN_METERS;
+
+
+ //////////////////////////////////////
+ // Information about self
+ //////////////////////////////////////
+ mpCharacter->GetPosition( myPos );
+ float myCollisionRadius = CHARACTER_SPAN_METERS;
+
+
+ //////////////////////////////////////
+ // Detect if player will hit us
+ //////////////////////////////////////
+
+ //
+ // CRUDE TEST
+ // ==========
+ // Test against hard-coded distanceSquared to see if we're
+ // close enough to player to even bother to proceed...
+ //
+ rmt::Vector myPosToPlayerPos = playerPos - myPos;
+ if( myPosToPlayerPos.MagnitudeSqr() > COLLISION_DISTANCE_SQUARED )
+ {
+ return false;
+ }
+
+ //
+ // MORE DETAILED TEST
+ // ===================
+ // Treat everything only on x-z plane. In other words, we won't
+ // detect a vehicle coming at us from above or below.
+ //
+
+ // Get more info about player and self
+ float epsilon = 0.005f;
+ bool gonnaHit = false;
+ float spanDistSum = playerCollisionRadius + myCollisionRadius;
+
+ myPos.y = 0.0f;
+ mpCharacter->GetFacing( myHeading );
+ rAssert( rmt::Epsilon(myHeading.MagnitudeSqr(), 1.0f, epsilon) );
+ myVel = myHeading * GetSpeedMps();
+ myVel.y = 0.0f;
+ myUp.Set( 0.0f, 1.0f, 0.0f );
+ float mySpeedMps = myVel.Length(); // *** SQUARE ROOT! ***
+
+
+ playerPos.y = 0.0f;
+ avatar->GetVelocity( playerVel );
+ playerVel.y = 0.0f;
+ playerUp.Set( 0.0f, 1.0f, 0.0f );
+ float playerSpeedMps = playerVel.Length(); // *** SQUARE ROOT! ***
+
+ hisVel = playerSpeedMps;
+
+ /////////////////////////////////////////////////////////
+ // If player is stationary
+ //
+ if( playerSpeedMps <= epsilon || avatar->GetCharacter()->mbIsPlayingIdleAnim )
+ {
+ mySide.CrossProduct( myUp, myHeading );
+ mySide.Normalize();
+
+ // sanity check lengths to 1.0f. myHeading should not be 0.0f since
+ // we're in the case where mySpeedMps > 0.0f. mySide shouldn't be 0.0f
+ // since we made myUp and myHeading orthonormal
+
+ rAssert( rmt::Epsilon( myHeading.MagnitudeSqr(), 1.0f, epsilon ) );
+ rAssert( rmt::Epsilon( mySide.MagnitudeSqr(), 1.0f, epsilon ) );
+
+ float lookAheadDist = GetFollowPathSpeedMps() * COLLISION_LOOK_AHEAD_SECONDS + spanDistSum;
+
+ //float lookAheadDist = mySpeedMps * COLLISION_LOOK_AHEAD_SECONDS + spanDistSum;
+ bool playerPosLiesOnMyRight;
+
+ gonnaHit = WillCollide( myPos,
+ myHeading, // Must be normalized to 1
+ mySide, // Must be normalized to 1
+ spanDistSum,
+ lookAheadDist,
+ playerPos,
+ playerPosLiesOnMyRight );
+ }
+ //////////////////////////////////////////////////////////////
+ // If player is not stationary, but I am...
+ //
+ else if( playerSpeedMps > epsilon && mySpeedMps <= epsilon )
+ {
+ playerHeading = playerVel / playerSpeedMps;
+ playerSide.CrossProduct( playerUp, playerHeading );
+ playerSide.Normalize();
+
+ rAssert( rmt::Epsilon( playerHeading.MagnitudeSqr(), 1.0f, epsilon ) );
+ rAssert( rmt::Epsilon( playerSide.MagnitudeSqr(), 1.0f, epsilon ) );
+
+ float lookAheadDist = playerSpeedMps * COLLISION_LOOK_AHEAD_SECONDS + spanDistSum;
+ bool myPosLiesOnPlayersRight;
+
+ gonnaHit = WillCollide( playerPos,
+ playerHeading, // Must be normalized to 1
+ playerSide, // Must be normalized to 1
+ spanDistSum,
+ lookAheadDist,
+ myPos,
+ myPosLiesOnPlayersRight );
+
+ //
+ // Store Dodge info to be used in PerformDodge
+ // because this should be the only control flow that allows you
+ // to call PerformDodge() later.
+ //
+ if( gonnaHit )
+ {
+ mDodgeInfo.wasSetThisFrame = true;
+ mDodgeInfo.myPosIsOnPlayersRightSide = myPosLiesOnPlayersRight;
+ mDodgeInfo.playerRightSide = playerSide;
+ }
+ }
+ /////////////////////////////////////////////////////////////
+ // If both moving, gotta do full collision test
+ //
+ else
+ {
+ rAssert( playerSpeedMps > epsilon && mySpeedMps > epsilon );
+ //
+ // Easiest, Crudest way to go about this is to do sphere-sphere
+ // collision on the 2 motion vectors. We don't need sophisticated
+ // check because this only provides an early warning about nearby
+ // activities...
+ //
+
+ //
+ // Compute future positions
+ //
+ rmt::Vector playerPos2, myPos2;
+ playerPos2 = playerPos + playerVel * COLLISION_LOOK_AHEAD_SECONDS;
+ myPos2 = myPos + myVel * COLLISION_LOOK_AHEAD_SECONDS;
+
+ //
+ // Find midpoints & radii
+ //
+ rmt::Vector playerMid, myMid;
+ playerMid = ( playerPos2 + playerPos ) * 0.5f;
+ myMid = (myPos2 + myPos ) * 0.5f;
+
+ float playerMotionRadius, myMotionRadius;
+ playerMotionRadius = playerSpeedMps * COLLISION_LOOK_AHEAD_SECONDS * 0.5f;
+ myMotionRadius = mySpeedMps * COLLISION_LOOK_AHEAD_SECONDS * 0.5f;
+
+ //
+ // Test for sphere-sphere collision
+ //
+ float minDist = myMotionRadius + playerMotionRadius;
+ float minDistSqr = minDist * minDist;
+ float actualDistSqr = (playerMid - myMid).MagnitudeSqr();
+
+ if( actualDistSqr < minDistSqr )
+ {
+ gonnaHit = true;
+ }
+ else
+ {
+ gonnaHit = false;
+ }
+
+ }
+ return gonnaHit;
+
+}
+
+
+void NPCController::PerformCringe( )
+{
+ SetIntention( Cringe );
+ SetSpeedMps( CRINGE_SPEED_MPS );
+
+ // Determine direction...
+ rmt::Vector myPos, playerPos;
+ mpCharacter->GetPosition( myPos );
+
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar != NULL );
+ avatar->GetPosition( playerPos );
+
+ rmt::Vector toPlayer = playerPos - myPos;
+ toPlayer.Normalize(); // *** SQUARE ROOT! ***
+
+ SetDirection( toPlayer );
+}
+
+void NPCController::PerformDodge( )
+{
+ if( !mDodgeInfo.wasSetThisFrame )
+ {
+ return;
+ }
+
+ // NOTE:
+ // I wouldn't be here unless I transited from STOPPED state,
+ // meaning my speed was 0.0f when Detect() was called just before
+ // this method was called, meaning that mDodgeInfo should
+ // have been set by the control flow through Detect() that
+ // happens when I am stationary and player is not.
+
+ // Set Controller information
+ SetIntention( Dodge );
+ SetSpeedMps( DODGE_SPEED_MPS );
+
+ // Determine which way to dodge...
+ if( mDodgeInfo.myPosIsOnPlayersRightSide )
+ {
+ mNormalizedDodgeDir = mDodgeInfo.playerRightSide;
+ }
+ else
+ {
+ mNormalizedDodgeDir = mDodgeInfo.playerRightSide * -1;
+ }
+ SetDirection( mNormalizedDodgeDir );
+
+ //
+ // Say funny ha-ha line
+ //
+ GetEventManager()->TriggerEvent( EVENT_PEDESTRIAN_DODGE, mpCharacter );
+}
+
+void NPCController::GetDirection( rmt::Vector& outDirection )
+{
+ // mDirection initialized and kept at 0,0,0
+ rAssert( mpCharacter->GetMaxSpeed() > 0.0f );
+ outDirection = mDirection * (mSpeedMps/mpCharacter->GetMaxSpeed());
+}
+
+void NPCController::TransitToState( State state )
+{
+ // If in TALKING but transiting to state other than TALKING,
+ // must StopTalking().
+ if( mState == TALKING && state != TALKING )
+ {
+ StopTalking();
+ mTalkTarget = NULL;
+ }
+
+ mpCharacter->SetInSubstep( false );
+
+ switch( state )
+ {
+ case TALKING:
+ {
+ // initially, tell anyone who transits to TALKING to start
+ // by listening. It's up to the function that sets them TALKING
+ // to call StartTalking() on one of the parties...
+ mListening = true;
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+ }
+ break;
+ case TALKING_WITH_PLAYER: // fall thru
+ case STOPPED: // fall thru
+ case STANDING: // fall thru
+ case NONE:
+ {
+ mSpeedMps = 0.0f;
+ mpCharacter->SetSpeed( mSpeedMps );
+ mSecondsInStopped = 0.0f;
+ mSecondsSinceLastTurnAnim = 0.0f;
+ }
+ break;
+ case PANICKING:
+ {
+ // test to see if we were ever told to transit to panic
+ // while already in panic. This isn't anything fatal, it's just
+ // more work than necessary
+ rAssert( mState != PANICKING );
+
+ mSpeedMps = NPC_PANIC_RUN_SPEED_MPS;
+ mpCharacter->SetSpeed( mSpeedMps );
+ mSecondsChangePanicDir = 0.0f;
+
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( player );
+
+ rmt::Vector myPos, playerPos;
+ mpCharacter->GetPosition( myPos );
+ player->GetPosition( playerPos );
+
+ rmt::Vector awayFromPlayer = myPos - playerPos;
+ awayFromPlayer.NormalizeSafe(); // *** SQUARE ROOT! ***
+
+ mNormalizedPanicDir = awayFromPlayer;
+ }
+ break;
+ case DODGING:
+ {
+ mpCharacter->SetInSubstep( true );
+ }
+ break;
+ default:
+ {
+ //nothing
+ }
+ // Put other cases here if they need specific operations...
+ }
+
+ mState = state;
+}
+void NPCController::GetAllowedPathOffset( rmt::Vector& offset )
+{
+ offset.Set( 1.0f, 1.0f, 1.0f );
+}
+
+void NPCController::OnOffPath( rmt::Vector lastPos, rmt::Vector currPos )
+{
+ rAssert( mOffPath );
+
+ // set direction...
+ rmt::Vector dirBackToPath = lastPos - currPos;
+ dirBackToPath.y = 0.0f;
+
+ dirBackToPath.Normalize(); // *** SQUARE ROOT! ***
+ SetDirection( dirBackToPath );
+}
+
+
+void NPCController::StartTalking()
+{
+ if( mListening && mState == TALKING )
+ {
+ mMouthFlapper->SetCharacter( mpCharacter );
+ mMillisecondsInTalk = 0;
+ mListening = false;
+ mpCharacter->GetPuppet()->GetEngine()->GetPoseEngine()->AddPoseDriver( 2, mMouthFlapper );
+ mMouthFlapper->SetIsEnabled( true );
+ }
+}
+void NPCController::StopTalking()
+{
+ if( !mListening && mState == TALKING )
+ {
+ mMillisecondsInTalk = 0;
+ mListening = true;
+ mMouthFlapper->SetIsEnabled( false );
+ mpCharacter->GetPuppet()->GetEngine()->GetPoseEngine()->RemovePoseDriver( 2, mMouthFlapper );
+ }
+}
+
+void NPCController::SetTempWaypont(const rmt::Vector& w)
+{
+ mTempWaypoint = w;
+ mUseTempWaypoint = true;
+}
+
+void NPCController::ClearTempWaypoint(void)
+{
+ mUseTempWaypoint = false;
+}
diff --git a/game/code/worldsim/character/charactercontroller.h b/game/code/worldsim/character/charactercontroller.h
new file mode 100644
index 0000000..85431a8
--- /dev/null
+++ b/game/code/worldsim/character/charactercontroller.h
@@ -0,0 +1,308 @@
+#ifndef CHARACTERCONTROLLER_H_
+#define CHARACTERCONTROLLER_H_
+
+#include <p3d/refcounted.hpp>
+#include <radmath/radmath.hpp>
+
+//////// FORWARD DECLARATIONS /////////
+class Character;
+class MouthFlapper;
+///////////////////////////////////////
+
+class CharacterController
+:
+public tRefCounted
+{
+public:
+ enum eIntention
+ {
+ NONE,
+ LeftStickX,
+ LeftStickY,
+ DoAction, // DUSIT [Nov 6,2002]: "Action" conflicts with class name
+ Jump,
+ Dash,
+ Attack,
+ DPadUp,
+ DPadDown,
+ DPadLeft,
+ DPadRight,
+#ifdef RAD_WIN32
+ GetOutCar,
+ MouseLookLeft,
+ MouseLookRight,
+#endif
+ NUM_INPUTS, // "real" inputs (stuff with buttons) goes
+ // before this, things that are only triggerd by AI go after
+ Dodge,
+ Cringe,
+ TurnRight,
+ TurnLeft,
+ CelebrateSmall,
+ CelebrateBig,
+ WaveHello,
+ WaveGoodbye
+ };
+
+ CharacterController( void );
+ virtual ~CharacterController( void );
+
+ virtual void Update( float timeins ) {};
+ virtual void GetDirection( rmt::Vector& outDirection ) = 0;
+ virtual float GetValue( int buttonId ) const = 0;
+ virtual bool IsButtonDown( int buttonId ) const = 0;
+ virtual int TimeSinceChange( int buttonId ) const { return IsButtonDown(buttonId) ? 0 : 5000; };
+
+ Character* GetCharacter( ) const;
+ virtual void SetCharacter( Character* pCharacter );
+
+ virtual void SetIntention( eIntention intention )
+ {
+ if( mActive )
+ {
+ mIntention = intention;
+ }
+ }
+ eIntention GetIntention( void ) const
+ {
+ return mIntention;
+ }
+
+ void PreserveIntention( eIntention intention )
+ {
+ mPreserveIntention = intention;
+ }
+
+ void ClearIntention( void )
+ {
+ mIntention = mPreserveIntention;
+ mPreserveIntention = NONE;
+ }
+
+ void SetActive( bool active ) { mActive = active; }
+
+protected:
+ Character* mpCharacter;
+ eIntention mIntention;
+ eIntention mPreserveIntention;
+ bool mActive;
+};
+
+
+////////////////////////////////////////////////////////////
+// Dusit
+////////////////////////////////////////////////////////////
+
+class NPCController
+:
+public CharacterController
+{
+public: // METHODS
+ NPCController( void );
+ virtual ~NPCController( void );
+
+ virtual void Update( float seconds );
+ virtual float GetSpeedMps() const;
+ virtual void SetSpeedMps( float fSpeedMps );
+ virtual void SetDirection( const rmt::Vector& inDirection );
+ virtual void GetDirection( rmt::Vector& outDirection );
+ virtual float GetValue( int buttonId ) const;
+ virtual bool IsButtonDown( int buttonId ) const;
+
+ void SetCharacter( Character *pCharacter );
+
+ enum State
+ {
+ // We enumerate here, all the different states specific to
+ // NPC behavior controller... which are essentially substates
+ // of the character's locomotion state (in character's statemanager)
+
+ FOLLOWING_PATH, // following predescribed path
+ STOPPED, // not moving, turns to track player, will resume following path
+ DODGING, // uh... dodging
+ CRINGING, // if we're not dodging, we're cringing...
+ TALKING, // when peds want to talk to one another!
+ STANDING, // just not moving, will resume following path
+ PANICKING, // running in random direction, more or less away from player
+ TALKING_WITH_PLAYER, // just for conversations..
+ NONE, // limbo... do nothing... stand there... don't resume following path
+ NUM_STATES
+ };
+ virtual State GetState() const
+ {
+ return mState;
+ }
+ void TransitToState( State state );
+
+ bool AddNPCWaypoint( const rmt::Vector& pt );
+ void ClearNPCWaypoints();
+
+ void StartTalking();
+ void StopTalking();
+ void IncitePanic();
+ void QuellPanic();
+
+ void TeleportToPath(void);
+ void SetTempWaypont(const rmt::Vector&);
+ void ClearTempWaypoint(void);
+
+public: // MEMBERS
+
+ enum { MAX_NPC_WAYPOINTS = 32 };
+
+ bool mOffPath;
+ MouthFlapper* mMouthFlapper;
+ int mMillisecondsInTalk;
+ NPCController* mTalkTarget;
+ bool mListening; // if in talking state but mouth not flapping
+
+protected: // METHODS
+
+ virtual void OnOffPath( rmt::Vector lastPos, rmt::Vector currPos );
+ virtual void GetAllowedPathOffset( rmt::Vector& offset );
+ virtual float GetFollowPathSpeedMps() const;
+ virtual void FollowPath( float seconds );
+ virtual void OnReachedWaypoint();
+
+ // detect collision with player vehicle and set dodge intention.
+ // returns true if need to dodge, false otherwise...
+ virtual void DetectAndDodge( float seconds );
+ virtual void TraversePath( float seconds );
+
+ bool Detect( float seconds, float& hisVel );
+ void PerformDodge();
+ void PerformCringe();
+
+protected: // MEMBERS
+
+ // This struct is filled in the Detect call before PerformDodge is called
+ // and used from within PerformDodge to determine direction of dodge.
+ struct DodgeInfo
+ {
+ bool wasSetThisFrame;
+ rmt::Vector playerRightSide;
+ bool myPosIsOnPlayersRightSide;
+ };
+ DodgeInfo mDodgeInfo;
+
+ State mState;
+ float mSecondsInStopped;
+ float mSpeedMps;
+ rmt::Vector mNormalizedDodgeDir; // Normalized
+
+ rmt::Vector mNPCWaypoints[ MAX_NPC_WAYPOINTS ];
+ int mNumNPCWaypoints;
+ int mCurrNPCWaypoint;
+
+ bool mStartPanicking;
+ float mSecondsChangePanicDir;
+ rmt::Vector mNormalizedPanicDir;
+
+ float mSecondsSinceLastTurnAnim; // while in STOPPED state, we sometimes play turn animations
+
+ bool mUseTempWaypoint;
+ rmt::Vector mTempWaypoint;
+
+private: // METHODS
+
+private: // MEMBERS
+ rmt::Vector mDirection;
+};
+
+inline void NPCController::ClearNPCWaypoints()
+{
+ mNumNPCWaypoints = 0;
+ mCurrNPCWaypoint = -1;
+}
+
+inline float NPCController::GetSpeedMps() const
+{
+ return mSpeedMps;
+}
+inline void NPCController::SetSpeedMps( float fSpeedMps )
+{
+ mSpeedMps = fSpeedMps;
+}
+inline void NPCController::SetDirection( const rmt::Vector& inDirection )
+{
+ mDirection = inDirection;
+}
+inline float NPCController::GetValue( int buttonId ) const
+{
+ return 0.0f;
+}
+inline bool NPCController::IsButtonDown( int buttonId ) const
+{
+ return false;
+}
+
+/////////////////////////////////////////////////////////////
+
+
+
+
+class CharacterMappable;
+class tCamera;
+
+class PhysicalController
+:
+public CharacterController
+{
+public:
+ PhysicalController( void );
+ virtual ~PhysicalController( void );
+
+ virtual void GetDirection( rmt::Vector& outDirection );
+ virtual float GetValue( int buttonId ) const;
+ virtual bool IsButtonDown( int buttonId ) const;
+ virtual int TimeSinceChange( int buttonId ) const;
+ virtual void SetIntention( eIntention intention ) {};
+
+ CharacterMappable* GetCharacterMappable( void ) const;
+ void SetCharacterMappable( CharacterMappable* pMappable );
+private:
+ CharacterMappable* mpCharacterMappable;
+};
+
+//////////////////////////////////////////////////////////////////////////
+//
+//
+//
+//
+// TBJ [8/9/2002]
+//
+//////////////////////////////////////////////////////////////////////////
+
+class CameraRelativeCharacterControllerEventHandler;
+
+class CameraRelativeCharacterController
+:
+public PhysicalController
+{
+public:
+
+ CameraRelativeCharacterController( void );
+ virtual ~CameraRelativeCharacterController( void );
+
+ void Create( Character* pCharacter, CharacterMappable* pCharacterMappable );
+ void GetDirection( rmt::Vector& outDirection );
+ virtual void SetIntention( eIntention intention );
+
+ void SetCamera( tCamera* pCamera );
+
+
+protected:
+ friend class CameraRelativeCharacterControllerEventHandler;
+ void HandleEvent( int id, void* pEventData );
+private:
+
+ // Don't much like this pointer here.
+ //
+ tCamera* mpCamera;
+ CameraRelativeCharacterControllerEventHandler* mpEventHandler;
+ bool mbCameraChange;
+ rmt::Vector mLastDirection;
+ rmt::Matrix mLastCameraMatrix;
+};
+
+#endif // CHARACTERCONTROLLER_H_
diff --git a/game/code/worldsim/character/charactermanager.cpp b/game/code/worldsim/character/charactermanager.cpp
new file mode 100644
index 0000000..e5f003b
--- /dev/null
+++ b/game/code/worldsim/character/charactermanager.cpp
@@ -0,0 +1,2488 @@
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/characterrenderable.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/vehicle.h>
+
+#include <ai/actionbuttonmanager.h>
+#include <ai/actionbuttonhandler.h>
+#include <events/eventenum.h>
+#include <events/eventmanager.h>
+#include <meta/locatorevents.h>
+#include <meta/eventlocator.h>
+#include <meta/triggervolume.h>
+#include <meta/carstartlocator.h>
+
+#include <camera/supercamcentral.h>
+#include <camera/supercammanager.h>
+
+#include <memory/srrmemory.h>
+
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <p3d/anim/drawablepose.hpp>
+#include <p3d/shader.hpp>
+#include <p3d/shadow.hpp>
+
+// Choreo includes.
+//
+#include <p3d/anim/pose.hpp>
+#include <choreo/bank.hpp>
+#include <choreo/load.hpp>
+#include <choreo/puppet.hpp>
+#include <choreo/utility.hpp>
+#include <simcommon/simstate.hpp>
+#include <simcommon/simulatedobject.hpp>
+#include <simcollision/collisionobject.hpp>
+
+#include <console/console.h>
+
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/RenderLayer.h>
+// Temp hack to get NPCs in.
+// TBJ [9/3/2002]
+//
+#include <mission/gameplaymanager.h>
+#include <constants/maxplayers.h>
+
+#include <stdlib.h>
+#include <debug/profiler.h>
+#include <worldsim/avatarmanager.h>
+
+#include <radtime.hpp>
+#include <p3d/anim/skeleton.hpp>
+
+#include <ai/sequencer/actioncontroller.h>
+#include <ai/sequencer/action.h>
+#include <ai/sequencer/sequencer.h>
+#include <presentation/blinker.h>
+
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+CharacterManager* CharacterManager::spCharacterManager = 0;
+
+/************ OUTPUT_TIMES FOR DLOP **********/
+//#define OUTPUT_TIMES
+/**********************************************/
+
+// Init the character tunables.
+//
+// Statics.
+//
+float CharacterTune::sfLocoRotateRate;
+float CharacterTune::sfLocoAcceleration;
+float CharacterTune::sfLocoDecceleration;
+bool CharacterTune::bLocoTest;
+float CharacterTune::sfAirRotateRate;
+float CharacterTune::sfAirAccelScale;
+float CharacterTune::sfAirGravity;
+float CharacterTune::sfStompGravityScale;
+float CharacterTune::sfDashBurstMax;
+float CharacterTune::sfDashAcceleration;
+float CharacterTune::sfDashDeceleration;
+float CharacterTune::sfJumpHeight;
+float CharacterTune::sfDoubleJumpHeight;
+float CharacterTune::sfDoubleJumpAllowUp;
+float CharacterTune::sfDoubleJumpAllowDown;
+
+float CharacterTune::sfHighJumpHeight;
+float CharacterTune::sfMaxSpeed;
+float CharacterTune::sfTurboRotateRate;
+
+float CharacterTune::sfGetInOutOfCarAnimSpeed;
+rmt::Vector CharacterTune::sGetInPosition;
+float CharacterTune::sGetInHeightThreshold;
+float CharacterTune::sGetInOpenDelay;
+float CharacterTune::sGetInOpenSpeed;
+float CharacterTune::sGetInCloseDelay;
+float CharacterTune::sGetInCloseSpeed;
+float CharacterTune::sGetOutOpenDelay;
+float CharacterTune::sGetOutOpenSpeed;
+float CharacterTune::sGetOutCloseDelay;
+float CharacterTune::sGetOutCloseSpeed;
+float CharacterTune::sfKickingForce;
+float CharacterTune::sfSlamForce;
+// How long in seconds that the character will get shocked for
+float CharacterTune::sfShockTime;
+
+bool CharacterManager::sbFixedSimRate;
+
+static const unsigned INVALID_LOAD = 0xffffffff;
+
+Blinker g_Blinkers[ 64 ]; //should match max_characters
+
+/*
+==============================================================================
+CharacterManager::CreateInstance
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterManager
+
+=============================================================================
+*/
+CharacterManager* CharacterManager::CreateInstance( void )
+{
+ rAssertMsg( spCharacterManager == 0, "CharacterManager already created.\n" );
+#ifdef RAD_GAMECUBE
+ spCharacterManager = new ( GMA_GC_VMM ) CharacterManager;
+#else
+ spCharacterManager = new ( GMA_PERSISTENT ) CharacterManager;
+#endif
+
+ return( spCharacterManager );
+}
+/*
+==============================================================================
+CharacterManager::GetInstance
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterManager
+
+=============================================================================
+*/
+CharacterManager* CharacterManager::GetInstance( void )
+{
+ rAssertMsg( spCharacterManager != 0, "CharacterManager has not been created yet.\n" );
+ return spCharacterManager;
+}
+/*
+==============================================================================
+CharacterManager::DestroyInstance
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::DestroyInstance( void )
+{
+ rAssertMsg( spCharacterManager != 0, "CharacterManager has not been created.\n" );
+ delete ( GMA_PERSISTENT, spCharacterManager );
+}
+
+/*
+==============================================================================
+CharacterManager::CharacterManager
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterManager
+
+=============================================================================
+*/
+CharacterManager::CharacterManager( void )
+{
+ int i;
+ for ( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ mpCharacter[ i ] = 0;
+ mGarbage[i] = false;
+ }
+
+ GetEventManager()->AddListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::CAR_DOOR ) );
+ GetEventManager()->AddListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::BOUNCEPAD ) );
+ GetEventManager()->AddListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::GENERIC_BUTTON_HANDLER_EVENT ) );
+ GetEventManager()->AddListener( this, EVENT_DEATH_VOLUME_SCREEN_BLANK );
+ GetEventManager()->AddListener( this, EVENT_STAGE_COMPLETE );
+ GetEventManager()->AddListener( this, EVENT_MISSION_SUCCESS );
+ GetEventManager()->AddListener( this, EVENT_CARD_COLLECTED );
+ GetEventManager()->AddListener( this, EVENT_MISSION_CHARACTER_RESET );
+ GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_START );
+ GetEventManager()->AddListener( this, EVENT_GETOUTOFVEHICLE_END );
+ GetEventManager()->AddListener( this, EVENT_TOGGLE_FIRSTPERSON );
+
+ CharacterTune::sfLocoRotateRate = 10.0f;
+ CharacterTune::sfLocoAcceleration = 20.0f;
+ CharacterTune::sfLocoDecceleration = -10.0f;
+
+ CharacterTune::bLocoTest = false;
+
+ CharacterTune::sfAirRotateRate = 4.0f;
+ CharacterTune::sfAirAccelScale = 0.078f; //was .1
+ CharacterTune::sfAirGravity = -25.0f; //was -30
+ CharacterTune::sfStompGravityScale = 3.22f;
+ CharacterTune::sfJumpHeight = 1.9f; //was 1.65
+ CharacterTune::sfDoubleJumpHeight = 1.0f;
+ CharacterTune::sfDoubleJumpAllowUp = 2.0f;
+ CharacterTune::sfDoubleJumpAllowDown = 12.0f; //was 6
+ CharacterTune::sfHighJumpHeight = 1.9f;
+
+ CharacterTune::sfDashBurstMax = 4.0f; // 7.0
+ CharacterTune::sfDashAcceleration = 200.0f;
+ CharacterTune::sfDashDeceleration = 200.0f;
+ CharacterTune::sfMaxSpeed = 4.0f;
+ CharacterTune::sGetInPosition.Set( 1.05f, 0.0f, -0.86f );
+ CharacterTune::sGetInHeightThreshold = 0.1f;
+ CharacterTune::sGetInOpenDelay = 0.0f;
+ CharacterTune::sGetInOpenSpeed = 0.2f;
+ CharacterTune::sGetInCloseDelay = 0.0f;
+ CharacterTune::sGetInCloseSpeed = 0.2f;
+ CharacterTune::sGetOutOpenDelay = 0.0f;
+ CharacterTune::sGetOutOpenSpeed = 0.2f;
+ CharacterTune::sGetOutCloseDelay = 0.0f;
+ CharacterTune::sGetOutCloseSpeed = 0.2f;
+
+ CharacterTune::sfTurboRotateRate = 2.0f; // 1.2
+
+ CharacterTune::sfGetInOutOfCarAnimSpeed = 30.0f;
+ CharacterTune::sfKickingForce = 400.0f;
+ CharacterTune::sfSlamForce = 800.0f;
+
+ CharacterManager::sbFixedSimRate = false;
+
+ Console* pConsole = GetConsole( );
+ rAssert( pConsole );
+ if ( pConsole )
+ {
+ pConsole->AddFunction( "SetCharacterPosition", CharacterManager::SetCharacterPosition, "Sets the character position", 3, 3 );
+ pConsole->AddFunction( "ResetCharacter", CharacterManager::ResetCharacter, "Sets the character to the named locator", 2, 2 );
+ pConsole->AddFunction( "AddTeleportDest", AddTeleportDest, "Set a valid location for a teleport", 3, 5 );
+
+ pConsole->AddFunction( "SetInitialWalk", SetInitialWalk, "Set locator to walk to on startup", 1, 1 );
+ }
+#ifdef DEBUGWATCH
+ const int MAX_LEN = 64;
+ char debugName[ MAX_LEN ];
+ int len = sprintf( debugName, "Character" );
+ rAssert( len < MAX_LEN );
+
+ radDbgWatchAddBoolean(&CharacterTune::bLocoTest, "Steer Facing", debugName, NULL, 0 );
+ radDbgWatchAddFloat(&CharacterTune::sfLocoRotateRate, "Loco Turning Rate", debugName, NULL, 0, 0.1f, 50.0f);
+ radDbgWatchAddFloat(&CharacterTune::sfLocoAcceleration, "Loco Acceleration", debugName, NULL, 0, 0.1f, 50.0f);
+ radDbgWatchAddFloat(&CharacterTune::sfLocoDecceleration, "Loco Deceleration", debugName, NULL, 0, -50.0f, -0.1f);
+
+ radDbgWatchAddFloat(&CharacterTune::sfAirRotateRate, "Air Turning Rate", debugName, NULL, 0, 0.0f, 20.0f);
+ radDbgWatchAddFloat(&CharacterTune::sfAirAccelScale, "Air Acceleration", debugName, NULL, 0, 0.0f, 1.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfAirGravity, "Air Gravity", debugName, NULL, 0, -100.0f, 0.1f );
+ radDbgWatchAddFloat(&CharacterTune::sfStompGravityScale, "Stomp Gravity Scale", debugName, NULL, 0, 0.1f, 4.0f);
+ radDbgWatchAddFloat(&CharacterTune::sfJumpHeight, "Jump Height (Tap)", debugName, NULL, 0, 0.0f, 100.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfDoubleJumpHeight, "Jump Height (Double Jump)", debugName, NULL, 0, 0.0f, 100.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfDoubleJumpAllowUp, "Double Jump Sweet Spot (Up)", debugName, NULL, 0, 0.0f, 30.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfDoubleJumpAllowDown, "Double Jump Sweet Spot (Down)", debugName, NULL, 0, 0.0f, 30.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfHighJumpHeight, "Jump Height (Full Press)", debugName, NULL, 0, 0.0f, 100.0f );
+
+ radDbgWatchAddFloat(&CharacterTune::sfDashAcceleration, "Dash Accel", debugName, NULL, 0, 0.0f, 200.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfDashDeceleration, "Dash Decel", debugName, NULL, 0, 0.0f, 200.0f );
+
+ radDbgWatchAddFloat(&CharacterTune::sfMaxSpeed, "Max Speed", debugName, NULL, 0, 0.0f, 8.0f );
+
+ radDbgWatchAddFloat((float*)&CharacterTune::sGetInPosition.x, "Get In Offset X", "Get In/Get Out", NULL, 0, 0.0f, 3.0f );
+ radDbgWatchAddFloat((float*)&CharacterTune::sGetInPosition.z, "Get In Offset Z", "Get In/Get Out", NULL, 0, -1.5f, 1.5f );
+ radDbgWatchAddFloat(&CharacterTune::sGetInHeightThreshold, "Height Threshold", "Get In/Get Out", NULL, 0, -2.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfGetInOutOfCarAnimSpeed, "Speed", "Get In/Get Out", NULL, 0, 15.0f, 240.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetInOpenDelay, "Get In Open Delay", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetInOpenSpeed, "Get In Open Speed", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetInCloseDelay, "Get In Close Delay", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetInCloseSpeed, "Get In Close Speed", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetOutOpenDelay, "Get Out Open Delay", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetOutOpenSpeed, "Get Out Open Speed", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetOutCloseDelay, "Get Out Close Delay", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&CharacterTune::sGetOutCloseSpeed, "Get Out Close Speed", "Get In/Get Out", NULL, 0, 0.0f, 2.0f );
+
+ radDbgWatchAddBoolean( &CharacterManager::sbFixedSimRate, "Fixed sim rate", debugName, NULL, 0 );
+
+ radDbgWatchAddFloat(&CharacterTune::sfDashBurstMax, "Turbo Speed", debugName, NULL, 0, 0.0f, 100.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfTurboRotateRate, "Turbo Rotate Rate", debugName, NULL, 0, 0.0f, 10.0f );
+
+ radDbgWatchAddString(sCharacterToSpawn, 64, "NPC to Spawn", "CharacterManager");
+ radDbgWatchAddFunction( "Spawn NPC", &Spawn, NULL, "CharacterManager");
+
+ radDbgWatchAddFunction( "Next Skin", &NextSkin, NULL, "CharacterManager");
+ radDbgWatchAddFloat(&CharacterTune::sfKickingForce, "Kicking Force", debugName, NULL, 0, 0.0f, 2000.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfSlamForce, "Stomping Force", debugName, NULL, 0, 0.0f, 4000.0f );
+ radDbgWatchAddFloat(&CharacterTune::sfShockTime, "Time(sec) to be shocked",debugName , NULL, NULL ,0 , 10.0f);
+
+#ifdef RAD_XBOX
+ extern float g_XBoxMipmapBias;
+ radDbgWatchAddFloat(&g_XBoxMipmapBias, "Mipmap Bias", "XBox", NULL, NULL , -10.0f , 10.0f);
+#endif
+
+#endif //DEBUGWATCH
+
+ strcpy(mDummyLoadData.modelName, "npd");
+ strcpy(mDummyLoadData.animName, "npd");
+ FillLoadData( mDummyLoadData, "npd", "npd");
+
+ mGarbageCollect = false;
+
+ mUniversalPose = new tPose(64);
+ mUniversalPose->AddRef();
+
+ mNumCharactersAdded = 0;
+}
+
+CharacterManager::~CharacterManager( void )
+{
+ Destroy(true);
+ tRefCounted::Release(mUniversalPose);
+ GetEventManager()->RemoveAll( this );
+}
+
+void CharacterManager::PreLoad( void )
+{
+ // Load the global textures
+ char fname[128];
+ sprintf(fname, "art\\chars\\global.p3d");
+ char section[128];
+ sprintf(section, "Eakkachaichanvet");
+
+ p3d::inventory->AddSection(section);
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, fname, GMA_LEVEL_OTHER, section );
+
+ // Load in a dummy, lightweight model
+ LoadModel("nps");
+ LoadModel("ndr");
+ LoadModel("npd");
+
+ // these animation banks are loaded all the time
+ LoadAnimation("nps");
+ LoadAnimation("ndr");
+ LoadAnimation("npd");
+}
+
+
+void CharacterManager::Destroy(bool permenant)
+{
+ mUniversalPose->SetSkeleton(NULL);
+
+ ClearTeleportDests();
+
+ if(permenant)
+ {
+ p3d::inventory->RemoveSectionElements( "Eakkachaichanvet" );
+ p3d::inventory->DeleteSection( "Eakkachaichanvet" );
+ }
+
+ int i;
+ for ( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ g_Blinkers[ i ].SetCharacter( NULL );
+ if ( mpCharacter[ i ] != 0 )
+ {
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(mpCharacter[i]);
+ mpCharacter[ i ]->RemoveFromWorldScene( );
+ mpCharacter[ i ]->ClearAllActionButtonHandlers();
+ mpCharacter[ i ]->SetManaged(false);
+ mpCharacter[ i ]->Release();
+ mpCharacter[ i ] = 0;
+ }
+
+ // Clean out the load data names, to make sure we clean up all memory in use
+ //
+ mCharacterLoadData[ i ].modelSection.SetText (0);
+ mCharacterLoadData[ i ].animSection.SetText (0);
+ mCharacterLoadData[ i ].animModelSection.SetText (0);
+ mCharacterLoadData[ i ].mModelHigh.SetText (0);
+ mCharacterLoadData[ i ].mModelMedium.SetText (0);
+ mCharacterLoadData[ i ].mModelLow.SetText (0);
+ mCharacterLoadData[ i ].mChoreoName.SetText (0);
+ }
+
+ for ( i = permenant ? 0 : 3; i < MAX_LOADS; i++ )
+ {
+ rAssert(mModelData[i].state != LOADING);
+ rAssert(mAnimData[i].state != LOADING);
+
+ if(mModelData[i].state == LOADED || mModelData[i].state == GARBAGE )
+ {
+ p3d::inventory->RemoveSectionElements(mModelData[i].section);
+ p3d::inventory->DeleteSection(mModelData[i].section);
+ }
+
+ if(mAnimData[i].state == LOADED || mAnimData[i].state == GARBAGE )
+ {
+ p3d::inventory->RemoveSectionElements(mAnimData[i].section);
+ p3d::inventory->DeleteSection(mAnimData[i].section);
+ }
+
+ mModelData[i].state = NOT_LOADED;
+ mAnimData[i].state = NOT_LOADED;
+ }
+
+ mNumCharactersAdded = 0;
+}
+
+void CharacterManager::RemoveCharacter( Character* c)
+{
+ rAssert(c);
+ if(c)
+ {
+ for ( int i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] == c )
+ {
+ if(mpCharacter[ i ]->GetRole() == Character::ROLE_COMPLETED_BONUS)
+ {
+ GetVehicleCentral()->RemoveSuppressedDriver(this->mRealModelNames[i]);
+ }
+
+ // if you get this assert, please leave it running and go get Nigel or Dusit
+ rAssert(mpCharacter[ i ]->GetRole() != Character::ROLE_PEDESTRIAN);
+
+ g_Blinkers[ i ].SetCharacter( NULL );
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(mpCharacter[i]);
+ mpCharacter[ i ]->RemoveFromWorldScene( );
+ GetRenderManager()->mEntityDeletionList.Add((tRefCounted*&)mpCharacter[ i ]);
+ mpCharacter[ i ]->SetManaged(false);
+ mpCharacter[ i ] = 0;
+
+ GarbageCollectModel(mCharacterModelData[i]);
+ GarbageCollectAnim(mCharacterAnimData[i]);
+ return;
+ }
+ }
+ }
+}
+
+
+void CharacterManager::SetGarbage(Character* c, bool garbage)
+{
+ for( int i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] == c)
+ {
+ mGarbage[i] = garbage;
+ }
+ }
+}
+
+void CharacterManager::GarbageCollectModel(unsigned modelIndex)
+{
+ bool modelUsed = (modelIndex < 3); // first three animation are npd, ndr, nps, don't garbage colelct
+
+ for ( int j = 0; (j < MAX_CHARACTERS) && !modelUsed; j++ )
+ {
+ if(mpCharacter[j])
+ {
+ modelUsed |= modelIndex == mCharacterModelData[j];
+ }
+ }
+
+ if(!modelUsed)
+ {
+ if(mModelData[modelIndex].state == LOADING)
+ {
+ mModelData[modelIndex].state = LOADING_GARBAGE;
+ mModelData[modelIndex].gracePeriod = 3.0f;
+ }
+ else
+ {
+ mModelData[modelIndex].state = GARBAGE;
+ mModelData[modelIndex].gracePeriod = 3.0f;
+ }
+ }
+}
+
+void CharacterManager::GarbageCollectAnim(unsigned animIndex)
+{
+ bool animUsed = (animIndex < 3); // first three animation are npd, ndr, nps, don't garbage colelct
+
+ for ( int j = 0; (j < MAX_CHARACTERS) && !animUsed; j++ )
+ {
+ if(mpCharacter[j])
+ {
+ animUsed |= animIndex == mCharacterAnimData[j];
+ }
+ }
+
+ if(!animUsed)
+ {
+ if(mAnimData[animIndex].state == LOADING)
+ {
+ mAnimData[animIndex].state = LOADING_GARBAGE;
+ }
+ else
+ {
+ mAnimData[animIndex].state = GARBAGE;
+ }
+
+ }
+}
+
+bool CharacterManager::IsModelLoaded(const char* name)
+{
+ unsigned index = FindLoad(mModelData, tEntity::MakeUID(name));
+ if( index != INVALID_LOAD && mModelData[index].state == LOADED )
+ {
+ return true;
+ }
+ return false;
+
+ //return FindLoad(mModelData, tEntity::MakeUID(name)) != INVALID_LOAD;
+}
+
+bool CharacterManager::IsAnimLoaded(const char* name)
+{
+ unsigned index = FindLoad(mAnimData, tEntity::MakeUID(name));
+ if( index != INVALID_LOAD && mAnimData[index].state == LOADED )
+ {
+ return true;
+ }
+ return false;
+
+ //return FindLoad(mAnimData, tEntity::MakeUID(name)) != INVALID_LOAD;
+}
+
+unsigned CharacterManager::LoadModel(const char* model, LoadingManager::ProcessRequestsCallback* callback, void* userData)
+{
+ tUID uid = tEntity::MakeUID(model);
+ unsigned loadIndex = AllocLoad(mModelData, uid);
+
+
+ if(mModelData[loadIndex].state == GARBAGE)
+ {
+ mModelData[loadIndex].state = LOADED;
+ }
+
+ if(mModelData[loadIndex].state == LOADING_GARBAGE)
+ {
+ mModelData[loadIndex].state = LOADING;
+ }
+
+ if((mModelData[loadIndex].state == LOADED) && callback)
+ {
+ callback->OnProcessRequestsComplete(userData);
+ }
+ else
+ {
+ mModelData[loadIndex].callback = callback;
+ mModelData[loadIndex].userData = userData;
+ }
+
+ if(mModelData[loadIndex].state == NOT_LOADED)
+ {
+ char truncModel[ 32 ];
+
+ // filenames are truncated at 6 characters
+ strcpy(truncModel, model);
+ truncModel[6] = 0;
+
+ char modelName[128];
+ sprintf(modelName, "art\\chars\\%s_m.p3d", truncModel);
+ char modelSection[128];
+ sprintf(modelSection, "%s_m", model);
+
+ mModelData[loadIndex].name = uid;
+ mModelData[loadIndex].section = tEntity::MakeUID(modelSection);
+ mModelData[loadIndex].state = LOADING;
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ p3d::inventory->AddSection(modelSection);
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, modelName, GMA_CHARS_AND_GAGS, modelSection);
+ GetLoadingManager()->AddCallback(this, (void*)loadIndex);
+ }
+
+ return loadIndex;
+}
+
+unsigned CharacterManager::LoadAnimation(const char* anim)
+{
+ tUID uid = tEntity::MakeUID(anim);
+ unsigned loadIndex = AllocLoad(mAnimData, uid);
+
+ if(mAnimData[loadIndex].state == GARBAGE)
+ {
+ mAnimData[loadIndex].state = LOADED;
+ }
+
+ if(mAnimData[loadIndex].state == LOADING_GARBAGE)
+ {
+ mAnimData[loadIndex].state = LOADING;
+ }
+
+ if(mAnimData[loadIndex].state == NOT_LOADED)
+ {
+ char truncAnim[ 32 ];
+
+ // filenames are truncated at 6 characters
+ strcpy(truncAnim, anim);
+ truncAnim[6] = 0;
+
+ char animName[128];
+ char choreoName[128];
+ sprintf(animName, "art\\chars\\%s_a.p3d", truncAnim);
+ sprintf(choreoName, "art\\chars\\%s.cho", anim);
+ char animSection[128];
+ sprintf(animSection, "%s_a", anim);
+
+ mAnimData[loadIndex].name = uid;
+ mAnimData[loadIndex].section = tEntity::MakeUID(animSection);
+ mAnimData[loadIndex].state = LOADING;
+
+ HeapMgr()->PushHeap( GMA_TEMP );
+ p3d::inventory->AddSection(animSection);
+ HeapMgr()->PopHeap( GMA_TEMP );
+ GetLoadingManager()->AddRequest( FILEHANDLER_PURE3D, animName, GMA_LEVEL_OTHER, animSection);
+ GetLoadingManager()->AddRequest( FILEHANDLER_CHOREO, choreoName, GMA_LEVEL_OTHER, animSection);
+ GetLoadingManager()->AddCallback(this, (void*)(0x10000000 | loadIndex));
+ }
+
+ return loadIndex;
+}
+
+unsigned CharacterManager::FindLoad(Load* loads, tUID name)
+{
+ for(int i = 0; i < MAX_LOADS; i++)
+ {
+ if(loads[i].name == name)
+ {
+ return i;
+ }
+ }
+
+ return INVALID_LOAD;
+}
+
+unsigned CharacterManager::AllocLoad(Load* loads, tUID name)
+{
+ unsigned index = FindLoad(loads, name);
+
+ if(index == INVALID_LOAD)
+ {
+ for(int i = 0; i < MAX_LOADS; i++)
+ {
+ if(loads[i].state == NOT_LOADED)
+ {
+ loads[i].name = name;
+ return i;
+ }
+ }
+ }
+ else
+ {
+ return index;
+ }
+
+ rAssertMsg(0, "Can't load any more character data");
+ return INVALID_LOAD;
+}
+
+CharacterManager::LoadState CharacterManager::GetState(Load* loads, tUID name)
+{
+ unsigned index = FindLoad(loads, name);
+ if(index != INVALID_LOAD)
+ {
+ return loads[index].state;
+ }
+ return NOT_LOADED;
+}
+
+void CharacterManager::FillLoadData( CharacterLoadData& data, const char* useModel, const char* useAnim)
+{
+ const int MAX_LEN = 32;
+ char modelName[ MAX_LEN ];
+
+ int len = sprintf( modelName, "%s_h", useModel);
+ rAssert( len < MAX_LEN );
+ data.mModelHigh.SetText( modelName );
+
+ len = sprintf( modelName, "%s_m", useModel );
+ rAssert( len < MAX_LEN );
+ data.mModelMedium.SetText( modelName );
+
+ len = sprintf( modelName, "%s_l", useModel );
+ rAssert( len < MAX_LEN );
+ data.mModelLow.SetText( modelName );
+
+ data.mChoreoName.SetText( useAnim );
+
+ char sec[256];
+ sprintf(sec, "%s_m", useModel);
+ data.modelSection.SetText(sec);
+ sprintf(sec, "%s_a", useAnim);
+ data.animSection.SetText(sec);
+ sprintf(sec, "%s_m", useAnim);
+ data.animModelSection.SetText(sec);
+}
+
+Character* CharacterManager::AddCharacter( CharacterType type, const char* characterName, const char* modelName, const char* choreoPuppet, const char* location)
+{
+ unsigned int addCharTime = radTimeGetMicroseconds();
+
+
+ unsigned int MakeUIDTime = radTimeGetMicroseconds();
+ tUID modelUID = tEntity::MakeUID(modelName);
+ tUID animUID = tEntity::MakeUID(choreoPuppet);
+ MakeUIDTime = radTimeGetMicroseconds() - MakeUIDTime;
+
+ Character* character;
+
+
+// #ifdef RAD_GAMECUBE
+// HeapMgr()->PushHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+// #endif
+
+ unsigned int NewTime = radTimeGetMicroseconds();
+ if(type == PC)
+ {
+ if( (int)mNumCharactersAdded >= GetGameplayManager()->GetNumPlayers() )
+ {
+ rReleasePrintf("Tried to add too many PCs, not supported right now. Check level scrips for multiple AddCharacter calls.\n");
+// #ifdef RAD_GAMECUBE
+// HeapMgr()->PopHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+// #endif
+ return NULL;
+ }
+ MEMTRACK_PUSH_GROUP( "CharacterManager - Add PC" );
+ character = new Character;
+ character->mbAllowUnload = false;
+ //character->SetSimpleShadow( false );
+ }
+ else
+ {
+ rAssert( mNumCharactersAdded == (unsigned int)GetGameplayManager()->GetNumPlayers() );
+
+ MEMTRACK_PUSH_GROUP( "CharacterManager - Add NPC" );
+ character = new NPCharacter;
+ }
+
+ if((strcmp(modelName, "lisa") == 0) || (strncmp(modelName, "l_", 2) == 0))
+ {
+ character->SetIsLisa(true);
+ }
+
+ if((strcmp(modelName, "marge") == 0) || (strncmp(modelName, "m_", 2) == 0))
+ {
+ character->SetIsMarge(true);
+ }
+
+ rAssert( character );
+ NewTime = radTimeGetMicroseconds() - NewTime;
+
+// #ifdef RAD_GAMECUBE
+// HeapMgr()->PopHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+// #endif
+
+ unsigned int SetNameTime = radTimeGetMicroseconds();
+ character->SetName( characterName );
+ SetNameTime = radTimeGetMicroseconds() - SetNameTime;
+
+
+ unsigned int AddCharacterTime = radTimeGetMicroseconds();
+
+ int characterIndex = AddCharacter( character, type );
+ strcpy(mRealModelNames[ characterIndex ], modelName);
+ strcpy(mRealAnimNames[ characterIndex ], choreoPuppet);
+ mLoaded[characterIndex] = true;
+ mGarbage[characterIndex] = false;
+
+ if ( type == PC )
+ {
+ rAssert( characterIndex == (int)mNumCharactersAdded );
+ ++mNumCharactersAdded;
+ }
+
+ AddCharacterTime = radTimeGetMicroseconds() - AddCharacterTime;
+
+ unsigned int FillLoadTime = radTimeGetMicroseconds();
+ strcpy(mCharacterLoadData[characterIndex].modelName, modelName);
+ strcpy(mCharacterLoadData[characterIndex].animName, choreoPuppet);
+
+ FillLoadData( mCharacterLoadData[characterIndex], modelName, choreoPuppet);
+ FillLoadTime = radTimeGetMicroseconds() - FillLoadTime;
+
+ unsigned int LoadModelTime = 0;
+ unsigned int SetupCharacterTime;
+
+ LoadModelTime = radTimeGetMicroseconds();
+ mCharacterModelData[characterIndex] = LoadModel(modelName);
+ mCharacterAnimData[characterIndex] = LoadAnimation(choreoPuppet);
+ LoadModelTime = radTimeGetMicroseconds() - LoadModelTime;
+
+ if( (GetState(mAnimData, animUID) == LOADED) && (GetState(mModelData, modelUID) == LOADED) )
+ {
+ SetupCharacterTime = radTimeGetMicroseconds();
+ SetupCharacter( mCharacterLoadData[characterIndex], character );
+ SetupCharacterTime = radTimeGetMicroseconds() - SetupCharacterTime;
+ }
+ else
+ {
+ SetupCharacterTime = radTimeGetMicroseconds();
+ SetupCharacter( mDummyLoadData, character );
+ SetupCharacterTime = radTimeGetMicroseconds() - SetupCharacterTime;
+ }
+
+ unsigned int InitTime = radTimeGetMicroseconds();
+ character->Init();
+ InitTime = radTimeGetMicroseconds() - InitTime;
+
+ if(type == NPC)
+ {
+ // Dusit [Oct 22, 2002]:
+ // Don't solve collisions for NPCs.. They just sit there...
+ // We'll enable solve collisions as needed...
+ character->SetSolveCollisions( false );
+ }
+ else
+ {
+ // PCs get immediatly added to scene, NPCs might not be visible (i.e pedestrians)
+ character->AddToWorldScene();
+ }
+
+ if(location)
+ {
+ //Set the initial character position.
+ Locator* loc = p3d::find<Locator>( location );
+ CarStartLocator* cloc = dynamic_cast<CarStartLocator*>( loc );
+
+ rmt::Vector position;
+ if ( cloc )
+ {
+ cloc->GetLocation( &position );
+ character->RelocateAndReset( position, cloc->GetRotation() );
+ }
+ else if ( loc )
+ {
+ rmt::Vector position;
+ loc->GetLocation( &position );
+ character->RelocateAndReset( position, 0 );
+ }
+ else
+ {
+ rDebugPrintf( "Couldn't find locator \"%s\" for %s\n", location, characterName );
+ }
+
+ // if NPC, add the locator as the first waypoint of this character
+ if( loc && type == NPC )
+ {
+ // the controller should have been created in NPC constructor
+ NPCController* npcController = (NPCController*) character->GetController();
+ rAssert( npcController );
+
+ bool res = npcController->AddNPCWaypoint( position );
+ rAssert( res );
+ }
+ }
+ addCharTime = radTimeGetMicroseconds() - addCharTime;
+
+#ifdef OUTPUT_TIMES
+ rReleasePrintf("AddCharacter %s Time %d\n",characterName, addCharTime);
+ rReleasePrintf(" AddCharacter MakeUIDTime %s Time %d\n",characterName, MakeUIDTime);
+ rReleasePrintf(" AddCharacter NewTime %s Time %d\n",characterName, NewTime);
+ rReleasePrintf(" AddCharacter SetNameTime %s Time %d\n",characterName, SetNameTime);
+ rReleasePrintf(" AddCharacter AddCharacterTime %s Time %d\n",characterName, AddCharacterTime);
+ rReleasePrintf(" AddCharacter FillLoadTime %s Time %d\n",characterName, FillLoadTime);
+ rReleasePrintf(" AddCharacter LoadModelTime %s Time %d\n",characterName, LoadModelTime);
+ rReleasePrintf(" AddCharacter SetupCharacterTime %s Time %d\n",characterName, SetupCharacterTime);
+ rReleasePrintf(" AddCharacter InitTime %s Time %d\n",characterName, InitTime);
+#endif
+ if(type == PC)
+ {
+ MEMTRACK_POP_GROUP( "CharacterManager - Add PC" );
+ }
+ else
+ {
+ MEMTRACK_POP_GROUP( "CharacterManager - Add NPC" );
+ }
+
+ if(type == PC && GetGameplayManager()->IsSuperSprint() == false && GetGameplayManager()->mIsDemo == false)
+ {
+ //check to see if there is a valid skin that is not called NULL
+ if(strcmp(GetCharacterSheetManager()->QueryCurrentSkin(GetGameplayManager()->GetCurrentLevelIndex()),"NULL") != 0)
+ {
+ SwapData(character, GetCharacterSheetManager()->QueryCurrentSkin(GetGameplayManager()->GetCurrentLevelIndex()), GetAnimName(character));
+ }
+ }
+
+ return character;
+}
+
+Character* CharacterManager::AddCharacterDeferedLoad( CharacterType type, const char* characterName, const char* modelName, const char* choreoPuppet, const char* location)
+{
+ Character* c = AddCharacter(type, characterName, "npd", "npd", location);
+
+ unsigned characterIndex = INVALID_LOAD;
+
+ for ( int i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] == c )
+ {
+ characterIndex = i;
+ break;
+ }
+ }
+
+ rAssert(characterIndex != INVALID_LOAD);
+
+ strcpy(mRealModelNames[ characterIndex ], modelName);
+ strcpy(mRealAnimNames[ characterIndex ], choreoPuppet);
+ mLoaded[characterIndex] = false;
+
+ return c;
+}
+
+void CharacterManager::SwapData(Character* character, const char* modelName, const char* choreoPuppet)
+{
+ unsigned index = InternalSwapData(character, modelName, choreoPuppet);
+ rAssert(index != INVALID_LOAD);
+ strcpy(mRealModelNames[ index ], modelName);
+ strcpy(mRealAnimNames[ index ], choreoPuppet);
+}
+
+
+unsigned CharacterManager::InternalSwapData(Character* character, const char* modelName, const char* choreoPuppet)
+{
+ unsigned int SwapDataTime = radTimeGetMicroseconds();
+
+
+ unsigned characterIndex = INVALID_LOAD;
+
+ for ( int i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] == character )
+ {
+ characterIndex = i;
+ break;
+ }
+ }
+
+ rAssert(characterIndex != INVALID_LOAD);
+
+ tUID modelUID;
+ tUID animUID;
+
+ unsigned modelIndex = mCharacterModelData[characterIndex];
+ unsigned animIndex = mCharacterAnimData[characterIndex];
+
+ if(modelName)
+ {
+ modelUID = tEntity::MakeUID(modelName);
+ strcpy(mCharacterLoadData[characterIndex].modelName, modelName);
+ }
+ else
+ {
+ modelUID = tEntity::MakeUID(mCharacterLoadData[characterIndex].modelName);
+ }
+
+ if(choreoPuppet)
+ {
+ animUID = tEntity::MakeUID(choreoPuppet);
+ strcpy(mCharacterLoadData[characterIndex].animName, choreoPuppet);
+ }
+ else
+ {
+ animUID = tEntity::MakeUID(mCharacterLoadData[characterIndex].animName);
+ }
+
+
+ unsigned int FillLoadTime = radTimeGetMicroseconds();
+ FillLoadData( mCharacterLoadData[characterIndex], mCharacterLoadData[characterIndex].modelName, mCharacterLoadData[characterIndex].animName);
+ FillLoadTime = radTimeGetMicroseconds() - FillLoadTime;
+
+ unsigned int LoadModelTime = 0;
+ unsigned int SetupCharacterTime = 0;
+ unsigned int CreatePuppetTime = 0;
+
+ LoadModelTime = radTimeGetMicroseconds();
+ mCharacterModelData[characterIndex] = LoadModel(mCharacterLoadData[characterIndex].modelName);
+ mCharacterAnimData[characterIndex] = LoadAnimation(mCharacterLoadData[characterIndex].animName);
+ LoadModelTime = radTimeGetMicroseconds() - LoadModelTime;
+
+ if((GetState(mAnimData, animUID) == LOADED) && (GetState(mModelData, modelUID) == LOADED))
+ {
+ SetupCharacterTime = radTimeGetMicroseconds();
+ CreatePuppetTime = SetupCharacter( mCharacterLoadData[characterIndex], character );
+ SetupCharacterTime = radTimeGetMicroseconds() - SetupCharacterTime;
+ }
+ else
+ {
+ SetupCharacterTime = radTimeGetMicroseconds();
+ CreatePuppetTime = SetupCharacter( mDummyLoadData, character );
+ SetupCharacterTime = radTimeGetMicroseconds() - SetupCharacterTime;
+ }
+
+ unsigned int GarbageCollectTime = radTimeGetMicroseconds();
+ GarbageCollectModel(modelIndex);
+ GarbageCollectAnim(animIndex);
+ GarbageCollectTime = radTimeGetMicroseconds() - GarbageCollectTime;
+
+ SwapDataTime = radTimeGetMicroseconds() - SwapDataTime;
+
+#ifdef OUTPUT_TIMES
+ rReleasePrintf("SwapDataTime %s Time %d\n",modelName, SwapDataTime);
+ rReleasePrintf(" FillLoadTime %s Time %d\n",modelName, FillLoadTime);
+ if( LoadModelTime != 0 )
+ rReleasePrintf(" LoadModelTime %s Time %d\n",modelName, LoadModelTime);
+ rReleasePrintf(" GarbageCollectTime %s Time %d\n",modelName, GarbageCollectTime );
+ rReleasePrintf(" SetupCharacterTime %s Time %d\n",modelName, SetupCharacterTime );
+ rReleasePrintf(" CreatePuppetTime %s Time %d\n\n",modelName, CreatePuppetTime );
+#endif
+
+ return characterIndex;
+}
+
+//=============================================================================
+// CharacterManager::SetupCharacter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( CharacterLoadData& data, Character* pCharacter, bool isNPC )
+//
+// Return: void
+//
+//=============================================================================
+unsigned int CharacterManager::SetupCharacter( CharacterLoadData& data, Character* pCharacter)
+{
+ p3d::inventory->PushSection();
+
+ bool currOnly = p3d::inventory->GetCurrentSectionOnly();
+ tName curr = p3d::inventory->GetCurrentSection()->GetName();
+ p3d::inventory->SetCurrentSectionOnly(true);
+
+// #ifdef RAD_GAEMCUBE
+// HeapMgr()->PushHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+// #endif
+
+MEMTRACK_PUSH_GROUP( "CharacterManager - CharacterRenderable" );
+ p3d::inventory->SelectSection(data.modelSection.GetUID());
+
+ CharacterRenderable* pCharacterRenderable;
+ if(data.modelSection.GetUID() != tEntity::MakeUID("npd_m"))
+ {
+ pCharacterRenderable = new CharacterRenderable(
+ p3d::find<tDrawablePose>( data.mModelHigh.GetUID() ),
+ p3d::find<tDrawablePose>( data.mModelMedium.GetUID() ),
+ p3d::find<tDrawablePose>( data.mModelLow.GetUID() ));
+ pCharacterRenderable->SetSwatchShader( p3d::find<tShader>( "char_swatches_lit_m" ) );
+ }
+ else
+ {
+ pCharacterRenderable = new CharacterRenderable(NULL, NULL, NULL);
+ pCharacterRenderable->SetSwatchShader( NULL );
+ }
+
+ pCharacterRenderable->SetShadowColour( pCharacter->GetShadowColour() );
+
+ // Lets find the electrocution effect
+ p3d::inventory->SelectSection( P3D_DEFAULT_INV_SECTION );
+ p3d::inventory->SetCurrentSectionOnly( false );
+ pCharacterRenderable->SetShockEffect( p3d::find< tDrawable >( "electrocuted" ) );
+ p3d::inventory->SetCurrentSectionOnly( true );
+
+
+ p3d::inventory->SelectSection( "Eakkachaichanvet" );
+ pCharacterRenderable->SetSwatchTexture( 0, p3d::find<tTexture>( "char_swatches_lit.bmp" ) );
+ pCharacterRenderable->SetSwatchTexture( 1, p3d::find<tTexture>( "char_swatches1.bmp" ) );
+ pCharacterRenderable->SetSwatchTexture( 2, p3d::find<tTexture>( "char_swatches2.bmp" ) );
+ pCharacterRenderable->SetSwatchTexture( 3, p3d::find<tTexture>( "char_swatches3.bmp" ) );
+ pCharacterRenderable->SetSwatchTexture( 4, p3d::find<tTexture>( "char_swatches4.bmp" ) );
+ pCharacterRenderable->SetSwatch( 0 );
+ p3d::inventory->SelectSection( data.modelSection.GetUID() );
+
+ pCharacter->SetDrawable( pCharacterRenderable );
+MEMTRACK_POP_GROUP("CharacterManager - CharacterRenderable");
+
+ tDrawablePose* pDrawablePose = pCharacterRenderable->GetDrawable();
+ tSkeleton* pSkeleton = NULL;
+
+ if(pDrawablePose)
+ {
+ pSkeleton = pDrawablePose->GetSkeleton();
+ }
+ else
+ {
+ p3d::inventory->SelectSection(data.animModelSection.GetUID());
+ pSkeleton = p3d::find<tSkeleton>( data.mChoreoName.GetUID() );
+ }
+
+ if(!pSkeleton)
+ {
+ tUID uid = tEntity::MakeUID("npd");
+ p3d::inventory->SelectSection(uid);
+ pSkeleton = p3d::find<tSkeleton>(uid);
+ }
+
+ rAssert( pSkeleton);
+
+ p3d::inventory->SelectSection(data.animSection.GetUID());
+ choreo::Bank* bank = p3d::find<choreo::Bank>( data.mChoreoName.GetUID() );
+ rAssert( bank != 0 );
+
+MEMTRACK_PUSH_GROUP( "CharacterManager - Puppet" );
+ unsigned int PuppetTime = radTimeGetMicroseconds();
+ // Make sure the skeleton to be remapped are equivalent.
+ //
+ choreo::Rig* rig = bank->GetRig();
+ rAssert( rig != 0 );
+ tSkeleton* rigSkeleton = rig->GetSkeleton( );
+ int i = 0;
+ if ( rigSkeleton->GetNumJoint() != pSkeleton->GetNumJoint() )
+ {
+ rDebugPrintf( "%s skeleton is not the same as choreo::Rig skeleton %s. Animations will be hooped.\n",
+ rigSkeleton->GetName( ), pSkeleton->GetName( ) );
+ }
+
+ mUniversalPose->SetSkeleton(pSkeleton);
+ mUniversalPose->ResetToRestPose();
+
+ pCharacter->SetInCar(false);
+ pCharacter->SetPuppet( new choreo::Puppet( mUniversalPose, bank, false, 0x20, 5 ) );
+
+ choreo::Puppet* pPuppet = pCharacter->GetPuppet();
+
+ PuppetTime = radTimeGetMicroseconds() - PuppetTime;
+
+// #ifdef RAD_GAEMCUBE
+// HeapMgr()->PopHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+// #endif
+
+MEMTRACK_POP_GROUP( "CharacterManager - Puppet" );
+
+
+ //
+ // Determine & store y offset needed for this character
+ //
+
+ float yAdjustDrawable = 0.0f, yAdjustPuppet = 0.0f;
+ if( pCharacterRenderable->GetDrawable() &&
+ pCharacterRenderable->GetDrawable()->GetSkeleton() )
+ {
+ yAdjustDrawable = pCharacterRenderable->GetDrawable()->GetSkeleton()->
+ FindJoint( tEntity::MakeUID("Balance_Root") )->worldMatrix.Row(3).y;
+ }
+ if( pPuppet && pPuppet->GetSkeleton() )
+ {
+ yAdjustPuppet = pPuppet->GetSkeleton()->
+ FindJoint( tEntity::MakeUID("Balance_Root") )->worldMatrix.Row(3).y;
+ }
+
+ pCharacter->SetYAdjust((yAdjustDrawable - yAdjustPuppet) * 0.30f);
+
+ //
+ // Setup the blinker for this character
+ //
+ unsigned int characterIndex = GetCharacterIndex( pCharacter );
+ g_Blinkers[ characterIndex ].SetCharacter( pCharacter );
+
+ p3d::inventory->SetCurrentSectionOnly(currOnly);
+ p3d::inventory->SelectSection( curr );
+
+ p3d::inventory->PopSection();
+
+ return PuppetTime;
+
+}
+
+void CharacterManager::GarbageCollect(bool ignoreDist)
+{
+ int i;
+
+ const float GARBAGE_COLLECT_DISTANCE = 100.0f;
+
+ // garbage collect
+ if(mGarbageCollect)
+ {
+ for(i = MAX_PLAYERS; i < MAX_CHARACTERS; i++)
+ {
+ if(mpCharacter[i])
+ {
+ rmt::Vector charPos;
+ rmt::Vector camPos;
+ rmt::Vector distance;
+
+ mpCharacter[i]->GetPosition(charPos);
+// camPos = GetSuperCamManager()->GetSCC(0)->GetCamera()->GetPosition();
+ GetCharacter(0)->GetPosition(camPos);
+ distance.Sub(camPos, charPos);
+
+ float fDist = rmt::Abs(distance.Magnitude());
+
+ if(fDist > 10000)
+ {
+ continue;
+ }
+
+ bool suppressLoad = false;
+
+ if(mpCharacter[i]->GetRole() == Character::ROLE_ACTIVE_BONUS)
+ {
+ if(!GetGameplayManager()->GetCurrentMission()->IsBonusMission() &&
+ !GetGameplayManager()->GetCurrentMission()->IsRaceMission() &&
+ !GetGameplayManager()->GetCurrentMission()->IsWagerMission() &&
+ !GetGameplayManager()->GetCurrentMission()->IsSundayDrive())
+ {
+ suppressLoad = true;
+ }
+ }
+
+ if(mGarbage[i])
+ {
+ if(fDist > GARBAGE_COLLECT_DISTANCE || GetGameplayManager()->IsIrisClosed() || ignoreDist )
+ {
+ if(mpCharacter[i]->GetRole() != Character::ROLE_ACTIVE_BONUS)
+ {
+ if(mpCharacter[i]->IsAmbient())
+ {
+ mGarbage[i] = false;
+ mpCharacter[i]->EnableAmbientDialogue(true);
+ mpCharacter[i]->ResetAmbientPosition();
+ }
+ else
+ {
+ RemoveCharacter(mpCharacter[i]);
+ }
+ }
+ else
+ {
+ mGarbage[i] = false;
+ }
+ }
+ }
+ else if (mLoaded[i] && (fDist > 150) && mpCharacter[i]->mbAllowUnload)
+ {
+ InternalSwapData(mpCharacter[i], "npd", "npd");
+ mLoaded[i] = false;
+ mpCharacter[i]->RemoveFromPhysics();
+ }
+ else if (!mLoaded[i] && (fDist < 150) && !suppressLoad)
+ {
+ if( !CommandLineOptions::Get( CLO_NO_PEDS ) )
+ InternalSwapData(mpCharacter[i], mRealModelNames[i], mRealAnimNames[i]);
+ mLoaded[i] = true;
+ if(!mpCharacter[i]->IsInCar())
+ {
+ mpCharacter[i]->AddToPhysics();
+ }
+ }
+ }
+
+ }
+
+ for(i = 1; i < MAX_LOADS; i++)
+ {
+ if(mModelData[i].state == GARBAGE)
+ {
+ if(mModelData[i].gracePeriod < 0.0f)
+ {
+ p3d::inventory->RemoveSectionElements(mModelData[i].section);
+ p3d::inventory->DeleteSection(mModelData[i].section);
+ mModelData[i].state = NOT_LOADED;
+ }
+ }
+
+ if(mAnimData[i].state == GARBAGE)
+ {
+ if(mAnimData[i].gracePeriod < 0.0f)
+ {
+ p3d::inventory->RemoveSectionElements(mAnimData[i].section);
+ p3d::inventory->DeleteSection(mAnimData[i].section);
+ mAnimData[i].state = NOT_LOADED;
+ }
+ }
+ }
+ }
+}
+
+/*
+==============================================================================
+CharacterManager::PreSimUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::PreSimUpdate( float timeins )
+{
+ int i;
+
+ for ( i = 1; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] && (mpCharacter[ i ]->GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer()) )
+ {
+BEGIN_PROFILE("Character::PreSimUpdate")
+ mpCharacter[ i ]->PreSimUpdate( timeins );
+END_PROFILE("Character::PreSimUpdate")
+ }
+ }
+
+ for(i = 1; i < MAX_LOADS; i++)
+ {
+ if(mModelData[i].state == GARBAGE)
+ {
+ mModelData[i].gracePeriod -= timeins;
+ }
+
+ if(mAnimData[i].state == GARBAGE)
+ {
+ mAnimData[i].gracePeriod -= timeins;
+ }
+ }
+
+}
+
+/*
+==============================================================================
+CharacterManager::PostSimUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::PostSimUpdate( float timeins )
+{
+ int i;
+ for ( i = 1; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] && (mpCharacter[ i ]->GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer()) )
+ {
+BEGIN_PROFILE("Character::PostSimUpdate")
+ mpCharacter[ i ]->PostSimUpdate( timeins );
+END_PROFILE("Character::PostSimUpdate")
+ }
+ }
+}
+
+
+/*
+==============================================================================
+CharacterManager::Update
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::Update( float timeins )
+{
+ int i;
+
+ for( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ Character* character = mpCharacter[i];
+ if( character && (character->GetRenderLayer() == GetRenderManager()->rCurWorldRenderLayer()) )
+ {
+ g_Blinkers[ i ].Update( static_cast< int >( timeins * 1000 ) );
+
+ int collisionAreaIndex = character->GetCollisionAreaIndex();
+ if( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA )
+ {
+ BEGIN_PROFILE("Character::UpdatePhObjects");
+ character->UpdatePhysicsObjects( timeins, collisionAreaIndex );
+ END_PROFILE("Character::UpdatePhObjects");
+ }
+
+ character->UpdateRoot( timeins );
+
+ sim::SimState* simState = character->GetSimState();
+ if( simState != NULL && simState->GetControl() == sim::simSimulationCtrl )
+ {
+ simState->GetSimulatedObject()->Update( timeins );
+ character->UpdateSimState( timeins );
+
+ sim::CollisionObject* collObj = simState->GetCollisionObject();
+ collObj->Update();
+
+ }
+ }
+ }
+
+}
+
+/*
+==============================================================================
+CharacterManager::PreSubstepUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::PreSubstepUpdate( float timeins )
+{
+ for( int i=0; i<MAX_CHARACTERS; i++ )
+ {
+ if( mpCharacter[ i ] && mpCharacter[ i ]->IsInSubstep() )
+ {
+ BEGIN_PROFILE("Character::PreSimUpdate")
+ mpCharacter[ i ]->PreSimUpdate( timeins );
+ END_PROFILE("Character::PreSimUpdate")
+ }
+ }
+}
+
+/*
+==============================================================================
+CharacterManager::PostSubstepUpdate
+==============================================================================
+Description: Comment
+
+Parameters: ( float timeins )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::PostSubstepUpdate( float timeins )
+{
+ if ( mpCharacter[ 0 ] )
+ {
+BEGIN_PROFILE("Character::PostSimUpdate")
+ mpCharacter[ 0 ]->PostSimUpdate( timeins );
+END_PROFILE("Character::PostSimUpdate")
+ }
+}
+
+
+void CharacterManager::ClearTargetVehicle(Vehicle* v)
+{
+ for (int i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( NULL != mpCharacter[ i ] )
+ {
+ if(mpCharacter[ i ]->GetTargetVehicle() == v)
+ {
+ mpCharacter[ i ]->SetTargetVehicle(NULL);
+ }
+ }
+ }
+}
+
+void CharacterManager::ResetBonusCharacters(void)
+{
+ for (int i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( NULL != mpCharacter[ i ] )
+ {
+ if(mpCharacter[ i ]->GetRole() == Character::ROLE_ACTIVE_BONUS)
+ {
+ ((NPCController*)(mpCharacter[ i ]->GetController()))->TeleportToPath();
+ }
+ }
+ }
+}
+
+/*
+==============================================================================
+CharacterManager::AddCharacter
+==============================================================================
+Description: Add a character to the array. Returns true if succesfully added.
+
+Parameters: ( Character* pCharacter, int& number, bool isNPC )
+
+Return: bool
+
+=============================================================================
+*/
+int CharacterManager::AddCharacter( Character* pCharacter, CharacterType type )
+{
+ int i = 0;
+
+ // if it's an NPC, skip the first couple of entries (reserved for players)
+ if ( type == NPC )
+ {
+ i = MAX_PLAYERS;
+ }
+
+ // find an empty slot
+ for (; i < MAX_CHARACTERS; i++ )
+ {
+ if ( 0 == mpCharacter[ i ] )
+ {
+ break;
+ }
+ }
+
+ rAssertMsg(( i < MAX_CHARACTERS ), "Couldn't add character, no space in character array");
+
+ tRefCounted::Assign( mpCharacter[ i ], pCharacter );
+ mpCharacter[ i ]->SetManaged(true);
+ mGarbage[i] = false;
+ return i;
+}
+
+void CharacterManager::OnProcessRequestsComplete( void* pUserData )
+{
+ unsigned index = (unsigned)pUserData;
+
+
+ if(index & 0x10000000)
+ {
+ index &= 0x0fffffff;
+
+ if(mAnimData[index].state == LOADING)
+ {
+ mAnimData[index].state = LOADED;
+
+ for(int i = 0; i < MAX_CHARACTERS; i++)
+ {
+ if(mpCharacter[i] && (mCharacterAnimData[i] == index))
+ {
+ if(mModelData[mCharacterModelData[i]].state == LOADED)
+ {
+ unsigned int PuppetTime = SetupCharacter(mCharacterLoadData[i], mpCharacter[i] );
+ #ifdef OUTPUT_TIMES
+ rReleasePrintf( "A) Calling SetupCharacter from OnProcessRequestsComplete for %s took %d\n",
+ mCharacterLoadData[i].modelName, PuppetTime );
+ #endif
+ }
+ }
+ }
+ } else if(mAnimData[index].state == LOADING_GARBAGE)
+ {
+ mAnimData[index].state = GARBAGE;
+ }
+ else
+ {
+ rAssert(0);
+ }
+ }
+ else
+ {
+ if(mModelData[index].state == LOADING)
+ {
+ mModelData[index].state = LOADED;
+
+ for(int i = 0; i < MAX_CHARACTERS; i++)
+ {
+ if(mpCharacter[i] && (mCharacterModelData[i] == index))
+ {
+ if(mAnimData[mCharacterAnimData[i]].state == LOADED)
+ {
+ unsigned int PuppetTime = SetupCharacter(mCharacterLoadData[i], mpCharacter[i] );
+ #ifdef OUTPUT_TIMES
+ rReleasePrintf( "B) Calling SetupCharacter from OnProcessRequestsComplete for %s took %d\n",
+ mCharacterLoadData[i].modelName, PuppetTime );
+ #endif
+ }
+ }
+ }
+
+ if(mModelData[index].callback)
+ {
+ mModelData[index].callback->OnProcessRequestsComplete(mModelData[index].userData);
+ mModelData[index].callback = NULL;
+ }
+ } else if(mModelData[index].state == LOADING_GARBAGE)
+ {
+ mModelData[index].state = GARBAGE;
+ }
+ else
+ {
+ rAssert(0);
+ }
+
+ }
+}
+
+//=============================================================================
+Character* CharacterManager::GetCharacterByName( const char* name ) const
+{
+ return GetCharacterByName( tEntity::MakeUID( name ) );
+}
+
+/*
+==============================================================================
+CharacterManager::GetCharacterByName
+==============================================================================
+Description: Comment
+
+Parameters: ( const tUID uid )
+
+Return: Character
+
+=============================================================================
+*/
+Character* CharacterManager::GetCharacterByName( const tUID uid ) const
+{
+ int i;
+ for( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] != 0 && mpCharacter[ i ]->GetUID() == uid )
+ {
+ return mpCharacter[ i ];
+ }
+ }
+ return NULL;
+
+}
+
+//=============================================================================
+Character* CharacterManager::GetMissionCharacter( const char* name ) const
+{
+ Character* c = NULL;
+
+ // pick an appropriate name based on if we are a bonus mission or not
+ char n[64];
+ if(GetGameplayManager()->GetCurrentMission()->IsBonusMission())
+ {
+ sprintf(n, "b_%s", name);
+ c = GetCharacterManager()->GetCharacterByName( n );
+ }
+
+ if(!c)
+ {
+ sprintf(n, "reward_%s", name);
+ c = GetCharacterManager()->GetCharacterByName( n );
+ }
+
+ if(!c)
+ {
+ c = GetCharacterManager()->GetCharacterByName( name );
+ }
+
+ return c;
+}
+
+/*
+==============================================================================
+CharacterManager::GetCharacter
+==============================================================================
+Description: Return a pointer to the character.
+
+Parameters: ( int i )
+
+Return: Character
+
+=============================================================================
+*/
+Character* CharacterManager::GetCharacter( int i ) const
+{
+ if ( i < MAX_CHARACTERS )
+ {
+ return mpCharacter[ i ];
+ }
+ else
+ {
+ return NULL;
+ }
+}
+
+/*
+==============================================================================
+CharacterManager::HandleEvent
+==============================================================================
+Description: Comment
+
+Parameters: ( EventEnum id, void* pEventData )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_GETINTOVEHICLE_START :
+ {
+ Character* c = (Character*)pEventData;
+ if(c->GetTargetVehicle())
+ {
+ if(c->GetTargetVehicle()->mpDriver && (c->GetTargetVehicle()->mpDriver != c))
+ {
+ c->GetTargetVehicle()->mpDriver->GetController()->SetIntention(CharacterController::WaveHello);
+ }
+ }
+ }
+ break;
+ case EVENT_GETOUTOFVEHICLE_END :
+ {
+ Character* c = (Character*)pEventData;
+ if(c->GetTargetVehicle())
+ {
+ if(c->GetTargetVehicle()->mpDriver && (c->GetTargetVehicle()->mpDriver != c))
+ {
+ c->GetTargetVehicle()->mpDriver->GetController()->SetIntention(CharacterController::WaveGoodbye);
+ }
+ }
+ }
+ break;
+ case EVENT_TOGGLE_FIRSTPERSON :
+ {
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if(pEventData == 0)
+ {
+ if(GetCharacter(0)->GetActionButtonHandler())
+ {
+ if ( GetCharacter(0)->GetActionButtonHandler()->IsInstanceEnabled() )
+ {
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_SHOW_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+ }
+ }
+ else
+ {
+ if( currentHud != NULL )
+ {
+ currentHud->HandleMessage( GUI_MSG_HIDE_HUD_OVERLAY, HUD_ACTION_BUTTON );
+ }
+ }
+ }
+ break;
+
+ case EVENT_STAGE_COMPLETE :
+ {
+ if(pEventData)
+ {
+ Character* ch = GetCharacter(0);
+ ch->GetController()->SetIntention(CharacterController::CelebrateSmall);
+ }
+ }
+ break;
+
+ case EVENT_CARD_COLLECTED :
+ case EVENT_MISSION_SUCCESS :
+ {
+ Character* ch = GetCharacter(0);
+ ch->GetController()->SetIntention(CharacterController::CelebrateBig);
+ }
+ break;
+
+ case EVENT_MISSION_CHARACTER_RESET :
+ {
+ if(pEventData && (sInitialWalkLocator[0] != 0))
+ {
+ rmt::Vector dest;
+ Locator* l = p3d::find<Locator>(sInitialWalkLocator);
+
+ if(l)
+ {
+ l->GetLocation(&dest);
+
+ Character* c = GetCharacterManager()->GetCharacter(0);
+
+ if(c->GetWalkerLocomotionAction()->GetStatus() != RUNNING)
+ {
+ c->GetWalkerLocomotionAction()->SetStatus(SLEEPING);
+ }
+
+ Sequencer* pSeq = c->GetActionController()->GetNextSequencer();
+ pSeq->BeginSequence();
+ pSeq->AddAction( new Arrive( c, dest) );
+ pSeq->AddAction( c->GetWalkerLocomotionAction() );
+ pSeq->EndSequence( );
+
+ }
+ }
+ sInitialWalkLocator[0] = 0;
+ }
+ break;
+
+ case EVENT_LOCATOR + LocatorEvent::CAR_DOOR:
+ {
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ rAssert( pLocator );
+ unsigned int playerId = pLocator->GetPlayerID();
+ Character* pCharacter = GetCharacter( playerId );
+
+ unsigned int actionId = pLocator->GetData( );
+ rAssert( actionId >= 0 );
+
+ ActionButton::GetInCar* pGetInCarAction = static_cast<ActionButton::GetInCar* >( GetActionButtonManager()->GetActionByIndex( actionId ) );
+ rAssert( dynamic_cast<ActionButton::GetInCar*>( pGetInCarAction ) );
+ rAssert( pGetInCarAction );
+
+ if ( pLocator->GetPlayerEntered() )
+ {
+
+ // Entered a door volume.
+ //
+
+#ifdef RAD_DEBUG
+ int vehicleId = pGetInCarAction->GetVehicleId();
+ Vehicle* pVehicle = GetVehicleCentral()->GetVehicle( vehicleId );
+ rAssert( pVehicle );
+#endif
+
+ pCharacter->AddActionButtonHandler( pGetInCarAction );
+ pGetInCarAction->Enter( pCharacter );
+
+ // TODO: Think about what will happen when the character is in two vols.
+ // at the same time.
+ //
+
+ }
+ else if ( !pLocator->GetPlayerEntered() )
+ {
+ // Exited a door volume.
+ //
+ pGetInCarAction->Exit( pCharacter );
+ pCharacter->RemoveActionButtonHandler( pGetInCarAction );
+ }
+ break;
+ }
+ case EVENT_LOCATOR + LocatorEvent::BOUNCEPAD:
+ {
+
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ if ( pLocator->GetPlayerEntered() )
+ {
+ rAssert( pLocator );
+ unsigned int playerId = pLocator->GetPlayerID();
+ Character* pCharacter = GetCharacter( playerId );
+
+ ActionButton::Bounce::OnEnter( pCharacter, pLocator );
+ }
+ break;
+ }
+ case EVENT_LOCATOR + LocatorEvent::GENERIC_BUTTON_HANDLER_EVENT:
+ {
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ rAssert( pLocator );
+
+ if ( pLocator->GetPlayerEntered() )
+ {
+ unsigned int playerId = pLocator->GetPlayerID();
+ Character* pCharacter = GetCharacter( playerId );
+
+ unsigned int actionId = pLocator->GetData( );
+ rAssert( actionId >= 0 );
+
+ ActionButton::GenericEventButtonHandler* pTED = static_cast<ActionButton::GenericEventButtonHandler* >( GetActionButtonManager()->GetActionByIndex( actionId ) );
+ rAssert( dynamic_cast< ActionButton::GenericEventButtonHandler* > (pTED) );
+ rAssert( pTED );
+
+ if ( pCharacter != static_cast<Character*>(pTED->GetEventData()) )
+ {
+ pCharacter->AddActionButtonHandler( pTED );
+ pTED->Enter( pCharacter );
+ }
+ }
+ else
+ {
+ unsigned int playerId = pLocator->GetPlayerID();
+ Character* pCharacter = GetCharacter( playerId );
+
+ unsigned int actionId = pLocator->GetData( );
+ rAssert( actionId >= 0 );
+
+ ActionButton::GenericEventButtonHandler* pTED = static_cast<ActionButton::GenericEventButtonHandler* >( GetActionButtonManager()->GetActionByIndex( actionId ) );
+
+ if ( pTED && (pCharacter != static_cast<Character*>(pTED->GetEventData())) )
+ {
+ rAssert( dynamic_cast<ActionButton::GenericEventButtonHandler* >( pTED ) != NULL );
+ pTED->Exit( pCharacter );
+ pCharacter->RemoveActionButtonHandler( pTED );
+ }
+ }
+
+ break;
+ }
+ case EVENT_DEATH_VOLUME_SCREEN_BLANK:
+ {
+ //TODO: Make the blends go away.
+ //Also, cut the camera properly...
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ if( pLocator == NULL )
+ {
+ break;
+ }
+
+ if ( pLocator->GetPlayerID() >= static_cast<unsigned int>(MAX_PLAYERS) )
+ {
+ //Ignore this!
+ break;
+ }
+
+ //Ignore when player leaves.
+ if ( pLocator->GetPlayerEntered() )
+ {
+ unsigned int playerId = pLocator->GetPlayerID();
+ Character* pCharacter = GetCharacter( playerId );
+
+ //Only do this if he's on foot.
+ if ( pCharacter->GetStateManager()->GetState() == CharacterAi::GET_IN)
+ {
+ GetAvatarManager()->PutCharacterInCar(pCharacter, pCharacter->GetTargetVehicle());
+ GetEventManager()->TriggerEvent(EVENT_GETINTOVEHICLE_END, pCharacter);
+ }
+ else
+ {
+ if ( pCharacter->GetStateManager()->GetState() == CharacterAi::GET_OUT )
+ {
+ GetAvatarManager()->PutCharacterOnGround( pCharacter, pCharacter->GetTargetVehicle() );
+ }
+ else if ( pCharacter->IsInCar() )
+ {
+ return;
+ }
+
+ rmt::Matrix mat = pLocator->GetMatrix();
+ rmt::Vector facing = mat.Row(2);
+ rmt::Vector pos;
+ pLocator->GetLocation( &pos );
+
+ float fDesiredDir = choreo::GetWorldAngle( facing.x, facing.z );
+ pCharacter->RelocateAndReset( pos, fDesiredDir, true, false );
+
+ GetSuperCamManager()->GetSCC( 0 )->DoCameraCut();
+ }
+ }
+
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+
+const char* CharacterManager::GetModelName(Character* c)
+{
+ for(int i = 0; i < MAX_CHARACTERS; i++)
+ {
+ if(c == mpCharacter[i])
+ {
+ return mRealModelNames[i];
+ }
+ }
+ return NULL;
+}
+
+const char* CharacterManager::GetAnimName(Character* c)
+{
+ for(int i = 0; i < MAX_CHARACTERS; i++)
+ {
+ if(c == mpCharacter[i])
+ {
+ return mRealAnimNames[i];
+ }
+ }
+ return NULL;
+}
+
+/*
+==============================================================================
+CharacterManager::SubmitStatics
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::SubmitStatics( void )
+{
+ int i;
+ for ( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ Character* character = mpCharacter[i];
+ if( character != NULL )
+ {
+ character->SubmitStatics();
+ }
+ }
+}
+
+/*
+==============================================================================
+CharacterManager::SubmitAnimCollisions
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::SubmitAnimCollisions( void )
+{
+ int i;
+ for ( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ if ( mpCharacter[ i ] )
+ {
+ mpCharacter[ i ]->SubmitAnimCollisions();
+ }
+ }
+}
+
+ /*
+==============================================================================
+CharacterManager::SubmitDynamics
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::SubmitDynamics( void )
+{
+ int i;
+ for ( i = 0; i < MAX_CHARACTERS; i++ )
+ {
+ Character* character = mpCharacter[i];
+ if( character != NULL )
+ {
+ character->SubmitDynamics();
+ }
+ }
+}
+
+
+/*
+==============================================================================
+CharacterManager::SetCharacterPosition
+==============================================================================
+Description: Comment
+
+Parameters: ( int argc, char** argv )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::SetCharacterPosition( int argc, char** argv )
+{
+ int index = ::atoi( argv[ 1 ] );
+ Character* pCharacter = GetCharacterManager()->GetCharacter( index );
+ if ( pCharacter )
+ {
+ rmt::Vector pos;
+ pos.x = static_cast<float>( atoi( argv[ 2 ] ) );
+ pos.y = 0.0f;
+ pos.z = static_cast<float>( atoi( argv[ 3 ] ) );
+ pCharacter->SetPosition( pos );
+ }
+}
+/*
+==============================================================================
+CharacterManager::ResetCharacter
+==============================================================================
+Description: Comment
+
+Parameters: ( int argc, char** argv )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterManager::ResetCharacter( int argc, char** argv )
+{
+ int index = ::atoi( argv[ 1 ] );
+ Character* pCharacter = GetCharacterManager()->GetCharacter( index );
+ Locator* loc = p3d::find<Locator>( argv[2] );
+ if ( pCharacter && loc )
+ {
+ rmt::Vector pos;
+ loc->GetLocation( &pos );
+ pCharacter->RelocateAndReset(pos, pCharacter->GetDesiredDir());
+ }
+}
+
+
+char CharacterManager::sInitialWalkLocator[64] = "";
+
+void CharacterManager::SetInitialWalk(int argc, char** argv)
+{
+ strcpy(sInitialWalkLocator, argv[1]);
+}
+
+char CharacterManager::sCharacterToSpawn[64] = "homer";
+Character* CharacterManager::sSpawnedCharacter = NULL;
+
+void CharacterManager::Spawn(void*)
+{
+ if(sSpawnedCharacter)
+ {
+ GetCharacterManager()->RemoveCharacter(sSpawnedCharacter);
+ sSpawnedCharacter = NULL;
+ }
+
+ sSpawnedCharacter = GetCharacterManager()->AddCharacter(NPC, "dummy", sCharacterToSpawn, "npd", "next_to_pc");
+
+ rmt::Vector position;
+ rmt::Vector facing;
+ GetCharacterManager()->GetCharacter(0)->GetPosition( &position );
+ GetCharacterManager()->GetCharacter(0)->GetFacing( facing );
+ position += facing;
+ position += facing;
+
+ sSpawnedCharacter->RelocateAndReset( position, 0);
+
+ if(sSpawnedCharacter)
+ sSpawnedCharacter->AddToWorldScene();
+}
+
+
+static const int sNumBartSkins = 7;
+static int sCurBartSkin = 1;
+static char sBartSkins [sNumBartSkins][16] =
+{
+ "bart",
+ "b_man",
+ "b_military",
+ "b_ninja",
+ "b_football",
+ "b_tall",
+ "b_hugo"
+};
+
+static const int sNumHomerSkins = 7;
+static int sCurHomerSkin = 1;
+static char sHomerSkins [sNumHomerSkins][16] =
+{
+ "homer",
+ "h_fat",
+ "h_donut",
+ "h_stcrobe",
+ "h_undrwr",
+ "h_scuzzy",
+ "h_evil"
+};
+
+static const int sNumLisaSkins = 4;
+static int sCurLisaSkin = 1;
+static char sLisaSkins [sNumLisaSkins][16] =
+{
+ "lisa",
+ "l_cool",
+ "l_florida",
+ "l_jersey"
+};
+
+static const int sNumMargeSkins = 4;
+static int sCurMargeSkin = 1;
+static char sMargeSkins [sNumMargeSkins][16] =
+{
+ "marge",
+ "m_police",
+ "m_pink",
+ "m_prison"
+};
+
+
+static const int sNumApuSkins = 4;
+static int sCurApuSkin = 1;
+static char sApuSkins [sNumApuSkins][16] =
+{
+ "apu",
+ "a_american",
+ "a_besharp",
+ "a_army"
+};
+
+void CharacterManager::NextSkin( void* )
+{
+ tUID bart = tEntity::MakeUID("bart");
+ tUID homer = tEntity::MakeUID("homer");
+ tUID lisa = tEntity::MakeUID("lisa");
+ tUID marge = tEntity::MakeUID("marge");
+ tUID apu = tEntity::MakeUID("apu");
+
+ Character* pc = GetCharacterManager()->GetCharacter(0);
+
+ if(pc->GetUID() == bart)
+ {
+ GetCharacterManager()->SwapData(pc, sBartSkins[sCurBartSkin], "bart");
+ sCurBartSkin = (sCurBartSkin + 1) % sNumBartSkins;
+ }
+
+ if(pc->GetUID() == homer)
+ {
+ GetCharacterManager()->SwapData(pc, sHomerSkins[sCurHomerSkin], "homer");
+ sCurHomerSkin = (sCurHomerSkin + 1) % sNumHomerSkins;
+ }
+
+ if(pc->GetUID() == lisa)
+ {
+ GetCharacterManager()->SwapData(pc, sLisaSkins[sCurLisaSkin], "lisa");
+ sCurLisaSkin = (sCurLisaSkin + 1) % sNumLisaSkins;
+ }
+
+ if(pc->GetUID() == marge)
+ {
+ GetCharacterManager()->SwapData(pc, sMargeSkins[sCurMargeSkin], "marge");
+ sCurMargeSkin = (sCurMargeSkin + 1) % sNumMargeSkins;
+ }
+
+ if(pc->GetUID() == apu)
+ {
+ GetCharacterManager()->SwapData(pc, sApuSkins[sCurApuSkin], "apu");
+ sCurApuSkin = (sCurApuSkin + 1) % sNumApuSkins;
+ }
+
+}
+
+static const int s_numCheatModels = 107;
+
+static char s_CheatModels[s_numCheatModels][107] =
+{
+ "apu",
+ "askinner",
+ "a_american",
+ "a_army",
+ "a_besharp",
+ "barney",
+ "bart",
+ "beeman",
+ "brn_unf",
+ "burns",
+ "b_football",
+ "b_hugo",
+ "b_man",
+ "b_military",
+ "b_ninja",
+ "b_tall",
+ "captain",
+ "carl",
+ "cbg",
+ "cletus",
+ "dolph",
+ "eddie",
+ "frink",
+ "gil",
+ "grandpa",
+ "hibbert",
+ "homer",
+ "h_donut",
+ "h_evil",
+ "h_fat",
+ "h_scuzzy",
+ "h_stcrobe",
+ "h_undrwr",
+ "jasper",
+ "jimbo",
+ "kearney",
+ "krusty",
+ "lenny",
+ "lisa",
+ "lou",
+ "louie",
+ "l_cool",
+ "l_florida",
+ "l_jersey",
+ "marge",
+ "milhouse",
+ "moe",
+ "moleman",
+ "m_pink",
+ "m_police",
+ "m_prison",
+ "ned",
+ "nelson",
+ "nriviera",
+ "otto",
+ "patty",
+ "ralph",
+ "selma",
+ "skinner",
+ "smithers",
+ "snake",
+ "teen",
+ "wiggum",
+ "willie",
+ "boy1",
+ "boy2",
+ "boy3",
+ "bum",
+ "busm1",
+ "busm2",
+ "busw1",
+ "const1",
+ "const2",
+ "farmr1",
+ "fem1",
+ "fem2",
+ "fem3",
+ "fem4",
+ "girl1",
+ "girl2",
+ "hooker",
+ "joger1",
+ "joger2",
+ "male1",
+ "male2",
+ "male3",
+ "male4",
+ "male5",
+ "male6",
+ "mobstr",
+ "nuclear",
+ "olady1",
+ "olady2",
+ "olady3",
+ "rednk1",
+ "rednk2",
+ "sail1",
+ "sail2",
+ "sail3",
+ "sail4",
+ "zfem1",
+ "zfem5",
+ "zmale1",
+ "zmale3",
+ "zmale4",
+ "frankenstein",
+ "witch"
+};
+
+static int s_curCheatModel = 0;
+
+void CharacterManager::NextCheatModel(void)
+{
+ if( CommandLineOptions::Get( CLO_NO_PEDS ) )
+ return;
+
+ Character* pc = GetCharacterManager()->GetCharacter(0);
+ GetCharacterManager()->SwapData(pc, s_CheatModels[s_curCheatModel], GetCharacterManager()->GetAnimName(pc));
+ s_curCheatModel = (s_curCheatModel + 1) % s_numCheatModels;
+}
+
+#include <mission/gameplaymanager.h>
+#include <meta/zoneeventlocator.h>
+
+static const unsigned MAX_TELEPORT_DESTS = 64;
+static ZoneEventLocator* s_dynaloadLoc = NULL;
+static unsigned s_numTeleportDests = 0;
+struct TeleportDest
+{
+ char name[32];
+ bool useLoc;
+ char loc[32];
+ rmt::Vector pos;
+ char zone[64];
+} s_teleportDests[MAX_TELEPORT_DESTS];
+
+unsigned CharacterManager::GetNumTeleportDests(void)
+{
+ return s_numTeleportDests;
+}
+
+const char* CharacterManager::GetTeleportDest(unsigned i)
+{
+ static char dummy[] = "Invalid Teleport Destination";
+
+ if(i < s_numTeleportDests)
+ {
+ return s_teleportDests[i].name;
+ }
+
+ return dummy;
+}
+
+void CharacterManager::ClearTeleportDests(void)
+{
+ for(unsigned i = 0; i < s_numTeleportDests; i++)
+ {
+ radDbgWatchDelete( &DoTeleport );
+ }
+ tRefCounted::Release(s_dynaloadLoc);
+ s_numTeleportDests = 0;
+}
+
+void CharacterManager::AddTeleportDest(int argc, char** argv)
+{
+ rAssert(argc != 5);
+ strcpy(s_teleportDests[s_numTeleportDests].name, argv[1]);
+
+ if(argc == 4)
+ {
+ strcpy(s_teleportDests[s_numTeleportDests].loc, argv[2]);
+ strcpy(s_teleportDests[s_numTeleportDests].zone, argv[3]);
+ s_teleportDests[s_numTeleportDests].useLoc = true;
+ }
+ else
+ {
+ s_teleportDests[s_numTeleportDests].pos.Set((float)atof(argv[2]), (float)atof(argv[3]), (float)atof(argv[4]));
+ strcpy(s_teleportDests[s_numTeleportDests].zone, argv[5]);
+ s_teleportDests[s_numTeleportDests].useLoc = false;
+ }
+ radDbgWatchAddFunction( argv[1], &DoTeleport, (void*)s_numTeleportDests, "Teleport");
+ s_numTeleportDests++;
+}
+
+void CharacterManager::DoTeleport(void* data)
+{
+ int which = (int)data;
+ GetRenderManager()->mpLayer( RenderEnums::LevelSlot )->DumpAllDynaLoads(1, GetRenderManager()->mEntityDeletionList );
+
+ tRefCounted::Assign(s_dynaloadLoc,new(GetGameplayManager()->GetCurrentMissionHeap()) ZoneEventLocator);
+ s_dynaloadLoc->SetZoneSize( strlen(s_teleportDests[which].zone) + 1 );
+ s_dynaloadLoc->SetZone( s_teleportDests[which].zone );
+ s_dynaloadLoc->SetPlayerEntered();
+ GetEventManager()->TriggerEvent( EVENT_FIRST_DYNAMIC_ZONE_START, s_dynaloadLoc );
+
+ rmt::Vector pos = s_teleportDests[which].pos;
+ if(s_teleportDests[which].useLoc)
+ {
+ Locator* l = p3d::find<Locator>(s_teleportDests[which].loc);
+ l->GetLocation(&pos);
+ }
+
+ if(GetCharacterManager()->GetCharacter(0)->IsInCar())
+ {
+ GetCharacterManager()->GetCharacter(0)->GetTargetVehicle()->SetPosition(&pos);
+ }
+ else
+ {
+ GetCharacterManager()->GetCharacter(0)->RelocateAndReset(pos, 0);
+ }
+}
+
+
+//=============================================================================
+// CharacterManager::ResetCharacter
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int argc, char** argv )
+//
+// Return: void
+//
+//=============================================================================
+unsigned int CharacterManager::GetCharacterIndex( const Character* character ) const
+{
+ unsigned int i;
+ unsigned int size = MAX_CHARACTERS;
+ for( i = 0; i < size; ++i )
+ {
+ if( mpCharacter[ i ] == character )
+ {
+ return i;
+ }
+ }
+ rAssertMsg( false, "Searched for a chraracter that does not exist" );
+ return 0;
+} \ No newline at end of file
diff --git a/game/code/worldsim/character/charactermanager.h b/game/code/worldsim/character/charactermanager.h
new file mode 100644
index 0000000..8f5f87b
--- /dev/null
+++ b/game/code/worldsim/character/charactermanager.h
@@ -0,0 +1,240 @@
+#ifndef CHARACTERMANAGER_H_
+#define CHARACTERMANAGER_H_
+
+#include <events/eventlistener.h>
+#include <loading/loadingmanager.h>
+#include <worldsim/ped/pedestrianmanager.h>
+// Forward declarations.
+//
+class Character;
+class CharacterRenderable;
+class ActionButtonHandler;
+class tSkeleton;
+class tPose;
+class Vehicle;
+
+namespace choreo
+{
+ class Puppet;
+};
+
+class CharacterManager
+:
+public EventListener,
+public LoadingManager::ProcessRequestsCallback
+{
+public:
+ // Are we a PC or NPC
+ //
+ enum CharacterType
+ {
+ PC,
+ NPC
+ };
+
+ // Singleton stuff
+ //
+ static CharacterManager* GetInstance( );
+ static CharacterManager* CreateInstance( );
+ static void DestroyInstance( );
+
+ // Called before loading begins to preload a little neccesary data
+ //
+ void PreLoad( void );
+
+ // Called when exiting gameplay, cleans up everything
+ //
+ void Destroy( bool permenant = false);
+
+ // Create a new character and add it to the manager
+ //
+ Character* AddCharacter( CharacterType, const char* characterName, const char* modelName, const char* choreoPuppet, const char* location = NULL);
+ Character* AddCharacterDeferedLoad( CharacterType, const char* characterName, const char* modelName, const char* choreoPuppet, const char* location = NULL);
+
+ // preload data for a character
+ //
+ void PreloadCharacter(const char* name, const char* anim, LoadingManager::ProcessRequestsCallback* callback = NULL, void* userData = NULL) { LoadAnimation(anim); LoadModel(name, callback, userData); }
+ bool IsModelLoaded(const char* name);
+ bool IsAnimLoaded(const char* name);
+
+ // legacy add functions (just map to AddCharacter now)
+ //
+ Character* AddPCCharacter( const char* characterName, const char* choreoPuppet ) { return AddCharacter(PC, characterName, characterName, choreoPuppet); }
+
+ // Get character by name
+ //
+ Character* GetCharacterByName( const char* name ) const;
+ Character* GetCharacterByName( const tUID uid ) const;
+ Character* GetMissionCharacter( const char* name ) const;
+
+ // swap character data
+ void SwapData(Character*, const char* mesh, const char* anim);
+
+ // Return a pointer to the character.
+ //
+ Character* GetCharacter( int i ) const;
+
+ // remove a character
+ //
+ void RemoveCharacter( Character* ) ;
+
+ // Character garbage collection
+ void AllowGarbageCollection(bool allow) { mGarbageCollect = allow;}
+ void SetGarbage(Character*, bool garbage);
+
+ // event handling (mainly when characters touch various locators)
+ //
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void GarbageCollect(bool ignoreDist = false);
+
+ const char* GetModelName(Character*);
+ const char* GetAnimName(Character*);
+
+ void ClearTargetVehicle(Vehicle*);
+ void ResetBonusCharacters(void);
+
+ // Various simulation stuff
+ //
+ void PreSimUpdate(float timeins);
+ void PostSimUpdate(float timeins);
+ void PreSubstepUpdate(float timeins);
+ void PostSubstepUpdate(float timeins);
+ void Update( float timeins );
+ void SubmitStatics( void );
+ void SubmitAnimCollisions( void );
+ void SubmitDynamics( void );
+
+ // Scripter hooks
+ //
+ static void SetCharacterPosition( int argc, char** argv );
+ static void ResetCharacter( int argc, char** argv );
+
+ // ???
+ //
+ static bool sbFixedSimRate;
+
+ static float sfKickingForce;
+ static float sfSlamForce;
+
+ enum LoadState
+ {
+ NOT_LOADED,
+ GARBAGE,
+ LOADING,
+ LOADING_GARBAGE,
+ LOADED
+ };
+
+ static void NextCheatModel(void);
+
+ static int GetMaxCharacters();
+
+ static void Teleport(unsigned i) { DoTeleport((void*)i); }
+ static unsigned GetNumTeleportDests(void);
+ static const char* GetTeleportDest(unsigned i);
+
+ void ClearInitialWalk(void) {sInitialWalkLocator[0] = 0;}
+
+protected:
+ unsigned int GetCharacterIndex( const Character* character ) const;
+private:
+ static const int MAX_NPCS = 8;
+ static const int MAX_PEDESTRIANS = PedestrianManager::MAX_PEDESTRIANS;
+ static const int MAX_CHARACTERS = 64; //MAX_PLAYERS + MAX_PEDESTRIANS + MAX_NPCS;
+ static const int MAX_LOADS = 40;
+
+ // Data we need for a character load.
+ //
+ struct CharacterLoadData
+ {
+ char modelName[64];
+ char animName[64];
+
+ tName modelSection;
+ tName animSection;
+ tName animModelSection;
+
+ tName mModelHigh;
+ tName mModelMedium;
+ tName mModelLow;
+ tName mChoreoName;
+ };
+
+ struct Load
+ {
+ Load() { state = NOT_LOADED; callback = NULL; }
+ tUID name;
+ tUID section;
+ LoadState state;
+ float gracePeriod;
+ LoadingManager::ProcessRequestsCallback* callback;
+ void* userData;
+ };
+
+ CharacterManager( void );
+ ~CharacterManager( void );
+
+ int AddCharacter( Character* pCharacter, CharacterType );
+
+ // [Dusit: Nov 26,2002]
+ // Hacked setupcharacter to return the radTime for OUTPUT_TIMES debugging
+ // Uncomment #define OUTPUT_TIMES in .cpp to have rReleasePrintfs show time taken
+ // in dynaload calls for peds & other characters
+ unsigned int SetupCharacter( CharacterLoadData& data, Character* pCharacter);
+
+ void FillLoadData( CharacterLoadData& data, const char* model, const char* anim);
+
+ unsigned LoadModel(const char* model, LoadingManager::ProcessRequestsCallback* callback = NULL, void* userData = NULL);
+ unsigned LoadAnimation(const char* anim);
+ unsigned FindLoad(Load* loads, tUID name);
+ unsigned AllocLoad(Load* loads, tUID name);
+ LoadState GetState(Load* loads, tUID name);
+
+ unsigned InternalSwapData(Character*, const char* mesh, const char* anim);
+
+ void GarbageCollectModel(unsigned index);
+ void GarbageCollectAnim(unsigned index);
+
+ void OnProcessRequestsComplete( void* pUserData );
+
+ static CharacterManager* spCharacterManager;
+
+ CharacterLoadData mDummyLoadData;
+
+ Character* mpCharacter[ MAX_CHARACTERS ];
+ char mRealModelNames[ MAX_CHARACTERS ][64];
+ char mRealAnimNames[ MAX_CHARACTERS ][64];
+ bool mLoaded[MAX_CHARACTERS];
+ CharacterLoadData mCharacterLoadData[ MAX_CHARACTERS ];
+ unsigned mCharacterModelData[MAX_CHARACTERS];
+ unsigned mCharacterAnimData[MAX_CHARACTERS];
+ Load mModelData[MAX_LOADS];
+ Load mAnimData[MAX_LOADS];
+
+ bool mGarbageCollect;
+ bool mGarbage[MAX_CHARACTERS];
+
+ tPose* mUniversalPose;
+
+ static char sInitialWalkLocator[64];
+ static void SetInitialWalk(int argc, char** argv);
+
+ static char sCharacterToSpawn[64];
+ static Character* sSpawnedCharacter;
+ static void Spawn(void*);
+ static void NextSkin( void* );
+
+ static void DoTeleport(void*);
+ static void ClearTeleportDests(void);
+ static void AddTeleportDest(int argc, char** argv);
+
+ unsigned int mNumCharactersAdded;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline CharacterManager* GetCharacterManager() { return( CharacterManager::GetInstance() ); }
+
+inline int CharacterManager::GetMaxCharacters() { return CharacterManager::MAX_CHARACTERS; }
+
+#endif //CHARACTERMANAGER_H_
diff --git a/game/code/worldsim/character/charactermappable.cpp b/game/code/worldsim/character/charactermappable.cpp
new file mode 100644
index 0000000..b798761
--- /dev/null
+++ b/game/code/worldsim/character/charactermappable.cpp
@@ -0,0 +1,281 @@
+#include <worldsim/character/charactermappable.h>
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactercontroller.h>
+#include <p3d/camera.hpp>
+
+#include <input/inputmanager.h>
+#include <input/usercontrollerwin32.h>
+
+//
+// Temp
+#include <worldsim/avatarmanager.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercam.h>
+
+
+// Constructor.
+//
+/*
+==============================================================================
+CharacterMappable::CharacterMappable
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterMappable
+
+=============================================================================
+*/
+CharacterMappable::CharacterMappable( void )
+:
+Mappable(Input::ACTIVE_GAMEPLAY),
+mpCharacterController( 0 )
+{
+}
+
+// Destructor
+//
+/*
+==============================================================================
+CharacterMappable::~CharacterMappable
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterMappable
+
+=============================================================================
+*/
+CharacterMappable::~CharacterMappable( void )
+{
+ if ( mpCharacterController )
+ {
+ mpCharacterController->Release( );
+ mpCharacterController = 0;
+ }
+}
+/*
+==============================================================================
+CharacterMappable::GetCharacterController
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: CharacterController
+
+=============================================================================
+*/
+CharacterController* CharacterMappable::GetCharacterController() const
+{
+ return mpCharacterController;
+}
+
+void CharacterMappable::SetCharacterController( CharacterController* pCharacterController )
+{
+ tRefCounted::Assign( mpCharacterController, pCharacterController );
+}
+// This method is called when ever a button state changes.
+//void
+void CharacterMappable::OnButton( int controllerId, int id, const IButton* pButton )
+{
+}
+
+// This method is called when a button changes state from "Pressed" to "Released".
+//
+void CharacterMappable::OnButtonUp( int controllerId, int buttonId, const IButton* pButton )
+{
+}
+
+// This method is called when a button changes state from "Released" to "Pressed".
+//
+void CharacterMappable::OnButtonDown( int controllerId, int buttonId, const IButton* pButton )
+{
+}
+
+// This is how we create our controller device mappings to logical game mappings.
+// The mappings set up in this method are platform specific.
+//
+// The basic format of the calls is to "Map" a input, to a enumerated output id.
+// The output of the specified input will be contained in the Button[] array.
+// This id will also be sent as a the second parameter in the OnButton... messages.
+//
+void CharacterMappable::LoadControllerMappings( unsigned int controllerId )
+{
+#ifdef RAD_XBOX
+ ClearMap( 0 );
+ Map( "LeftStickX", CharacterController::LeftStickX, 0, controllerId );
+ Map( "LeftStickY", CharacterController::LeftStickY, 0, controllerId );
+ Map( "DPadUp", CharacterController::DPadUp, 0, controllerId );
+ Map( "DPadDown", CharacterController::DPadDown, 0, controllerId );
+ Map( "DPadLeft", CharacterController::DPadLeft, 0, controllerId );
+ Map( "DPadRight", CharacterController::DPadRight, 0, controllerId );
+ Map( "Y", CharacterController::DoAction, 0, controllerId );
+ Map( "A", CharacterController::Jump, 0, controllerId );
+ Map( "B", CharacterController::Dash, 0, controllerId );
+ Map( "X", CharacterController::Attack, 0, controllerId );
+#endif
+#ifdef RAD_PS2
+ ClearMap( 0 );
+ Map( "LeftStickX", CharacterController::LeftStickX, 0, controllerId );
+ Map( "LeftStickY", CharacterController::LeftStickY, 0, controllerId );
+ Map( "DPadUp", CharacterController::DPadUp, 0, controllerId );
+ Map( "DPadDown", CharacterController::DPadDown, 0, controllerId );
+ Map( "DPadLeft", CharacterController::DPadLeft, 0, controllerId );
+ Map( "DPadRight", CharacterController::DPadRight, 0, controllerId );
+ Map( "Triangle", CharacterController::DoAction, 0, controllerId );
+ Map( "X", CharacterController::Jump, 0, controllerId );
+ Map( "Circle", CharacterController::Dash, 0, controllerId );
+ Map( "Square", CharacterController::Attack, 0, controllerId );
+#endif
+#ifdef RAD_GAMECUBE
+ ClearMap( 0 );
+ Map( "LeftStickX", CharacterController::LeftStickX, 0, controllerId );
+ Map( "LeftStickY", CharacterController::LeftStickY, 0, controllerId );
+ Map( "DPadUp", CharacterController::DPadUp, 0, controllerId );
+ Map( "DPadDown", CharacterController::DPadDown, 0, controllerId );
+ Map( "DPadLeft", CharacterController::DPadLeft, 0, controllerId );
+ Map( "DPadRight", CharacterController::DPadRight, 0, controllerId );
+ Map( "Y", CharacterController::DoAction, 0, controllerId );
+ Map( "A", CharacterController::Jump, 0, controllerId );
+ Map( "X", CharacterController::Dash, 0, controllerId );
+ Map( "B", CharacterController::Attack, 0, controllerId );
+#endif
+#ifdef RAD_WIN32
+ ClearMap( 0 );
+ Map( "MoveUp", CharacterController::DPadUp, 0, controllerId );
+ Map( "MoveDown", CharacterController::DPadDown, 0, controllerId );
+ Map( "MoveLeft", CharacterController::DPadLeft, 0, controllerId );
+ Map( "MoveRight", CharacterController::DPadRight, 0, controllerId );
+ Map( "DoAction", CharacterController::DoAction, 0, controllerId );
+ Map( "GetOutCar", CharacterController::GetOutCar, 0, controllerId );
+ Map( "Jump", CharacterController::Jump, 0, controllerId );
+ Map( "Sprint", CharacterController::Dash, 0, controllerId );
+ Map( "Attack", CharacterController::Attack, 0, controllerId );
+ Map( "feMouseRight", CharacterController::MouseLookRight, 0, controllerId );
+ Map( "feMouseLeft", CharacterController::MouseLookLeft, 0, controllerId );
+#endif
+}
+
+void CharacterMappable::GetDirection( rmt::Vector& outDirection ) const
+{
+}
+
+BipedCharacterMappable::BipedCharacterMappable( void )
+:
+CharacterMappable( )
+{
+}
+
+BipedCharacterMappable::~BipedCharacterMappable( )
+{
+}
+void BipedCharacterMappable::OnButtonDown( int controllerId, int buttonId, const IButton* pButton )
+{
+ CharacterMappable::OnButtonDown( controllerId, buttonId, pButton );
+
+ switch ( buttonId )
+ {
+ case CharacterController::DoAction:
+ {
+ GetCharacterController()->SetIntention( CharacterController::DoAction );
+ break;
+ }
+ case CharacterController::Jump:
+ {
+ GetCharacterController()->SetIntention( CharacterController::Jump );
+ break;
+ }
+ case CharacterController::Dash:
+ {
+ GetCharacterController()->SetIntention( CharacterController::Dash );
+ break;
+ }
+ case CharacterController::Attack:
+ {
+ GetCharacterController()->SetIntention( CharacterController::Attack );
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+}
+void BipedCharacterMappable::GetDirection( rmt::Vector& outDirection ) const
+{
+#ifdef RAD_WIN32
+ if ( GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCam()->GetType() == SuperCam::PC_CAM ) //Mouse look enabled
+ {
+ float right = GetValue( CharacterController::MouseLookRight );
+ float left = GetValue( CharacterController::MouseLookLeft );
+ outDirection.x = ( right > left ) ? right : -left;
+
+ float dirPad = GetValue( CharacterController::DPadUp ) - GetValue( CharacterController::DPadDown );
+ float dirAnalog = GetValue( CharacterController::LeftStickY );
+
+ outDirection.z = rmt::Fabs( dirPad ) > rmt::Fabs( dirAnalog ) ? dirPad : dirAnalog;
+ }
+ else
+ {
+#endif
+ rmt::Vector tempDir;
+ tempDir.x = GetValue( CharacterController::LeftStickX );
+ tempDir.y = 0.0f;
+ tempDir.z = GetValue( CharacterController::LeftStickY );
+
+ rmt::Vector tempDir2;
+ tempDir2.x = GetValue( CharacterController::DPadRight ) - GetValue( CharacterController::DPadLeft );
+ tempDir2.y = 0.0f;
+ tempDir2.z = GetValue( CharacterController::DPadUp ) - GetValue( CharacterController::DPadDown );
+
+ //The DPad overrides the analog stick.
+ outDirection = tempDir2.MagnitudeSqr() != 0.0f ? tempDir2 : tempDir;
+#ifdef RAD_WIN32
+ }
+#endif
+}
+
+InCarCharacterMappable::InCarCharacterMappable( void )
+:
+CharacterMappable( )
+{
+}
+
+InCarCharacterMappable::~InCarCharacterMappable( )
+{
+}
+
+void InCarCharacterMappable::OnButtonDown( int controllerId, int buttonId, const IButton* pButton )
+{
+ CharacterMappable::OnButtonDown( controllerId, buttonId, pButton );
+
+ switch ( buttonId )
+ {
+ case CharacterController::DoAction:
+ {
+ GetCharacterController()->SetIntention( CharacterController::DoAction );
+ break;
+ }
+#ifdef RAD_WIN32
+ case CharacterController::GetOutCar:
+ {
+ GetCharacterController()->SetIntention( CharacterController::GetOutCar );
+ break;
+ }
+#endif
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void InCarCharacterMappable::GetDirection( rmt::Vector& outDirection ) const
+{
+ outDirection.Set( 0.0f, 0.0f, 0.0f );
+}
diff --git a/game/code/worldsim/character/charactermappable.h b/game/code/worldsim/character/charactermappable.h
new file mode 100644
index 0000000..6c67f5f
--- /dev/null
+++ b/game/code/worldsim/character/charactermappable.h
@@ -0,0 +1,126 @@
+#ifndef BIPEDCHARACTERMAPPABLE_H_
+#define BIPEDCHARACTERMAPPABLE_H_
+
+#include <input/mappable.h>
+#include <radmath/radmath.hpp>
+
+
+class Character;
+class CharacterController;
+class tCamera;
+
+class CharacterMappable
+:
+public Mappable
+{
+public:
+
+ CharacterMappable( void );
+ virtual ~CharacterMappable( void );
+
+ // This method is called when ever a button state changes.
+ //
+ virtual void OnButton( int controllerId, int id, const IButton* pButton );
+
+ // This method is called when a button changes state from "Pressed" to "Released".
+ //
+ virtual void OnButtonUp( int controllerId, int buttonId, const IButton* pButton );
+
+ // This method is called when a button changes state from "Released" to "Pressed".
+ //
+ virtual void OnButtonDown( int controllerId, int buttonId, const IButton* pButton );
+
+ // This is how we create our controller device mappings to logical game mappings.
+ // The mappings set up in this method are platform specific.
+ //
+ // The basic format of the calls is to "Map" a input, to a enumerated output id.
+ // The output of the specified input will be contained in the Button[] array.
+ // This id will also be sent as a the second parameter in the OnButton... messages.
+ //
+ virtual void LoadControllerMappings( unsigned int controllerId );
+
+ // Return the controller input direction transformed from camera to world space.
+ //
+ virtual void GetDirection( rmt::Vector& outDirection ) const;
+
+ CharacterController* GetCharacterController( void ) const;
+ void SetCharacterController( CharacterController* pCharacterController );
+private:
+ CharacterController* mpCharacterController;
+};
+
+class BipedCharacterMappable
+:
+public CharacterMappable
+{
+public:
+
+ BipedCharacterMappable( void );
+ virtual ~BipedCharacterMappable( void );
+
+ // This method is called when ever a button state changes.
+ //
+ //virtual void OnButton( int controllerId, int id, const IButton* pButton );
+
+ // This method is called when a button changes state from "Pressed" to "Released".
+ //
+ //virtual void OnButtonUp( int controllerId, int buttonId, const IButton* pButton );
+
+ // This method is called when a button changes state from "Released" to "Pressed".
+ //
+ virtual void OnButtonDown( int controllerId, int buttonId, const IButton* pButton );
+
+ // This is how we create our controller device mappings to logical game mappings.
+ // The mappings set up in this method are platform specific.
+ //
+ // The basic format of the calls is to "Map" a input, to a enumerated output id.
+ // The output of the specified input will be contained in the Button[] array.
+ // This id will also be sent as a the second parameter in the OnButton... messages.
+ //
+ //virtual void LoadControllerMappings( unsigned int controllerId );
+
+ // Return the controller input direction transformed from camera to world space.
+ //
+ virtual void GetDirection( rmt::Vector& outDirection ) const;
+
+private:
+};
+
+
+class InCarCharacterMappable
+:
+public CharacterMappable
+{
+public:
+
+ InCarCharacterMappable( void );
+ virtual ~InCarCharacterMappable( void );
+
+ // This method is called when ever a button state changes.
+ //
+ //virtual void OnButton( int controllerId, int id, const IButton* pButton );
+
+ // This method is called when a button changes state from "Pressed" to "Released".
+ //
+ //void OnButtonUp( int controllerId, int buttonId, const IButton* pButton );
+
+ // This method is called when a button changes state from "Released" to "Pressed".
+ //
+ virtual void OnButtonDown( int controllerId, int buttonId, const IButton* pButton );
+
+ // This is how we create our controller device mappings to logical game mappings.
+ // The mappings set up in this method are platform specific.
+ //
+ // The basic format of the calls is to "Map" a input, to a enumerated output id.
+ // The output of the specified input will be contained in the Button[] array.
+ // This id will also be sent as a the second parameter in the OnButton... messages.
+ //
+ //virtual void LoadControllerMappings( unsigned int controllerId );
+
+ // Return the controller input direction transformed from camera to world space.
+ //
+ virtual void GetDirection( rmt::Vector& outDirection ) const;
+
+private:
+};
+#endif // BIPEDCHARACTERMAPPABLE_H_ \ No newline at end of file
diff --git a/game/code/worldsim/character/characterrenderable.cpp b/game/code/worldsim/character/characterrenderable.cpp
new file mode 100644
index 0000000..eb94e36
--- /dev/null
+++ b/game/code/worldsim/character/characterrenderable.cpp
@@ -0,0 +1,581 @@
+#include <worldsim/character/characterrenderable.h>
+#include <mission/gameplaymanager.h>
+#include <p3d/texture.hpp>
+#include <p3d/shader.hpp>
+#include <p3d/anim/drawablepose.hpp>
+#include <p3d/anim/pose.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/shadow.hpp>
+#include <p3d/view.hpp>
+#include <pddi/pddi.hpp>
+#include <camera/supercammanager.h>
+#include <contexts/bootupcontext.h>
+
+// Hack.
+// [6/27/2002]
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+
+/*
+==============================================================================
+CharacterRenderable::CharacterRenderable
+==============================================================================
+Description: Comment
+
+Parameters: (
+ tDrawablePose* pDrawablePoseHigh,
+ tDrawablePose* pDrawablePoseMedium,
+ tDrawablePose* pDrawablePoseLow,
+ tPose* pPose )
+
+Return: CharacterRenderable
+
+=============================================================================
+*/
+CharacterRenderable::CharacterRenderable(
+ tDrawablePose* pDrawablePoseHigh,
+ tDrawablePose* pDrawablePoseMedium,
+ tDrawablePose* pDrawablePoseLow)
+:
+mCurrentLOD( High ),
+mbInAnyonesFrustrum( false ),
+mpSwatchTexture( NULL ),
+mpSwatchShader( NULL ),
+mHaveShadowJoints( false ),
+mFadeAlpha( 255 ),
+mpShockedDrawable( NULL ),
+mIsShocked( false ),
+mDisplayingSkeletonShock( false )
+{
+ int i;
+ for ( i = 0; i < MAX_LOD; i++ )
+ {
+ mpDrawableList[ i ] = 0;
+ }
+ tRefCounted::Assign( mpDrawableList[ High ], pDrawablePoseHigh );
+ tRefCounted::Assign( mpDrawableList[ Medium ], pDrawablePoseMedium );
+ tRefCounted::Assign( mpDrawableList[ Low ], pDrawablePoseLow );
+
+ if(pDrawablePoseHigh)
+ {
+ pDrawablePoseHigh->SetPose(NULL);
+ }
+
+ if(pDrawablePoseMedium)
+ {
+ pDrawablePoseMedium->SetPose(NULL);
+ }
+
+ if(pDrawablePoseLow)
+ {
+ pDrawablePoseLow->SetPose(NULL);
+ }
+
+ // null these out for now... CharacterManager::SetupCharacter will be
+ // setting these swatch textures...
+ for( i=0; i<CharacterRenderable::NUM_CHARACTER_SWATCHES; i++ )
+ {
+ mpSwatchTextures[i] = NULL;
+ }
+
+ mShadowColour.Set( 0, 0, 0 );
+}
+
+/*
+==============================================================================
+CharacterRenderable::~CharacterRenderable
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterRenderable
+
+=============================================================================
+*/
+CharacterRenderable::~CharacterRenderable( void )
+{
+ int i;
+ for ( i = 0; i < MAX_LOD; i++ )
+ {
+ if ( mpDrawableList[ i ] )
+ {
+ mpDrawableList[ i ]->Release();
+ mpDrawableList[ i ] = 0;
+ }
+ }
+
+ if( mpSwatchTexture )
+ {
+ mpSwatchTexture->Release();
+ mpSwatchTexture = NULL;
+ }
+
+ for( i=0; i<CharacterRenderable::NUM_CHARACTER_SWATCHES; i++ )
+ {
+ if( mpSwatchTextures[i] != NULL )
+ {
+ mpSwatchTextures[i]->Release();
+ mpSwatchTextures[i] = NULL;
+ }
+ }
+ if( mpSwatchShader )
+ {
+ mpSwatchShader->Release();
+ mpSwatchShader = NULL;
+ }
+
+ if ( mpShockedDrawable != NULL )
+ {
+ mpShockedDrawable->Release();
+ mpShockedDrawable = NULL;
+ }
+}
+/*
+==============================================================================
+CharacterRenderable::Display
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterRenderable::Display( rmt::Vector iPosn, tPose* pose )
+{
+BEGIN_PROFILE("CharRender Cull")
+ tPointCamera* pCam = (tPointCamera*)GetSuperCamManager()->GetSCC(0)->GetCamera();
+ rmt::Vector camPosn;
+ pCam->GetPosition( &camPosn );
+ iPosn.Sub( iPosn, camPosn );
+ float sqrDistFromCam = iPosn.MagnitudeSqr();
+END_PROFILE("CharRender Cull")
+
+ float dist = (pCam->GetNearPlane() + 1.5f);
+ dist *= dist;
+
+ if ( sqrDistFromCam < dist )
+ {
+ return;
+ }
+
+ if( mbInAnyonesFrustrum )
+ {
+
+ if(sqrDistFromCam > 900.0f )
+ {
+ if(sqrDistFromCam > 6400.0f ) //>80m
+ {
+ SetLOD(Low);
+ }
+ else // <= 80m
+ {
+ SetLOD(Medium);
+ }
+ }
+ else // <= 30m
+ {
+ SetLOD(High);
+ }
+
+ BEGIN_PROFILE("CharRender Display")
+ if ( mIsShocked == false )
+ {
+ DisplayModel( pose );
+ }
+ else
+ {
+ DisplayShocked( pose );
+ }
+
+ END_PROFILE("CharRender Display")
+ }
+}
+//////////////////////////////////////////////////////////////////////////
+int CharacterRenderable::CastsShadow()
+{
+ return 1;
+}
+
+/*
+==============================================================================
+CharacterRenderable::DisplayShadow
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: void
+
+=============================================================================
+*/
+void CharacterRenderable::DisplayShadow( tPose* pose, const BlobShadowParams* BlobParams )
+{
+ if( BlobParams )
+ {
+ BEGIN_PROFILE("Char Blobby Shadow");
+ if( !mHaveShadowJoints )
+ {
+ mHaveShadowJoints = true;
+ mShadowJoints[ 0 ] = pose->FindJoint( "Elbow_L" );
+ mShadowJoints[ 1 ] = pose->FindJoint( "Ankle_L" );
+ mShadowJoints[ 2 ] = pose->FindJoint( "Elbow_R" );
+ mShadowJoints[ 3 ] = pose->FindJoint( "Ankle_R" );
+ }
+
+ tColour OutsideColour;
+ tColour insideColour;
+
+ // Hack for brightly colored shadows for special game mode
+ if( ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ OutsideColour.Set( 0, 0, 0, 0 ); // black outline
+ //OutsideColour = mShadowColour;
+ insideColour = mShadowColour;
+ }
+ else
+ {
+ OutsideColour.Set( 255, 255, 255, 255 );
+ const int Inside = 128;
+ float shadowAlpha = BlobParams->ShadowAlpha;
+ if( mFadeAlpha != 255 )
+ {
+ shadowAlpha = mFadeAlpha / 255.0f;
+ }
+ int c = rmt::Clamp( int( Inside + ( 255 - Inside ) * ( 1.0f - shadowAlpha ) ), 0, 255 );
+ insideColour.Set( c, c, c, c );
+ }
+
+
+ const float ArticulatedBlobbyShadowLODThreshold = 15.0f;
+ const float MinShadowSize = 0.15f;
+ const float NonArticulatedScale = 0.2f;
+ const int NumShadowJoints = 4;
+
+
+ const float BlobRadius = 1.0f;
+ const float BlobFadeOff = 0.2f;
+ const int NumBlobSlices = 16; // Keep the number even or you're in trouble.
+ const int HalfCircle = NumBlobSlices >> 1; // We only keep half the circle and then mirror it. Hence the reason to keep the number of points even.
+ static float BlobPoints[ HalfCircle ][ 2 ];
+ static float FadePoints[ HalfCircle ][ 2 ];
+ float pointScales[ NumBlobSlices ]; // How much to move the points to envolope the character.
+ bool articulated = false;
+
+ static float DoOnce = true;
+ if( DoOnce )
+ {
+ DoOnce = false;
+ const float angleStep = rmt::PI_2 / ((float) NumBlobSlices );
+ const float angleHalfStep = angleStep * 0.5f;
+ float angle = rmt::PI_2;
+ for( int j = 0; j < HalfCircle; ++j )
+ {
+ float sa, ca;
+ rmt::SinCos( angle, &sa, &ca);
+ BlobPoints[ j ][ 0 ] = sa * BlobRadius;
+ BlobPoints[ j ][ 1 ] = ca * BlobRadius;
+ rmt::SinCos( angle - angleHalfStep, &sa, &ca );
+ FadePoints[ j ][ 0 ] = sa * BlobFadeOff;
+ FadePoints[ j ][ 1 ] = ca * BlobFadeOff;
+ angle -= angleStep;
+ }
+ }
+
+ // Translate the shadow towards the camera slightly, instead of moving it off the
+ //ground in the direction of the the ground normal. Hopefully this will cause less distortion of the shadow.
+ rmt::Vector camPos;
+ tCamera* camera = p3d::context->GetView()->GetCamera();
+ rAssert( camera );
+ camera->GetWorldPosition( &camPos );
+ camPos.Sub( BlobParams->GroundPos );
+ float camDirDot = camPos.DotProduct( camera->GetCameraToWorldMatrix().Row( 2 ) );
+ // Note that the camPos vector is towards the camera so the camDirDot will be negative when the camera is facing
+ //the object. So if it's positive then we can assume it's behind the camera.
+ if( camDirDot > 0.0f )
+ {
+ // Please excuse the early return.
+ END_PROFILE( "Char Blobby Shadow" );
+ return;
+ }
+ float cameraToShadow = camPos.Magnitude();
+ camPos.Normalize();
+ camPos.Scale( 0.25f );
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( BlobParams->GroundPos );
+ transform.FillHeading( BlobParams->GroundNormal, BlobParams->ShadowFacing );
+ transform.Row( 3 ).Add( camPos );
+ p3d::stack->PushMultiply( transform );
+
+ if( ( mHaveShadowJoints ) && ( cameraToShadow < ArticulatedBlobbyShadowLODThreshold ) )
+ {
+ articulated = true;
+ // Find the position of the joints to deform the shadow by the character's movement.
+ float jointPos[ NumShadowJoints ][ 2 ];
+/*
+ Uncomment this if you want to weight the joint position by how close
+ they line up to the blob point position.
+ float jointNor[ NumShadowJoints ][ 2 ];
+*/
+ rmt::Vector tempPos;
+ rmt::Matrix m = pose->GetJoint( 0 )->worldMatrix;
+ m.Invert();
+ for( int i = 0; i < NumShadowJoints; ++i )
+ {
+ m.Transform( mShadowJoints[ i ]->worldMatrix.Row( 3 ), &tempPos );
+ jointPos[ i ][ 0 ] = tempPos.x;
+ jointPos[ i ][ 1 ] = -tempPos.z;
+/*
+ Uncomment this if you want to weight the joint positions by how close
+ they line up to the blob point position.
+ float mag;
+ mag = rmt::Sqrt( ( tempPos.x * tempPos.x ) + ( tempPos.z * tempPos.z ) );
+ mag = ( mag != 0.0f ) ? 1 / mag : 0.0f;
+ jointNor[ i ][ 0 ] = jointPos[ i ][ 0 ] * mag;
+ jointNor[ i ][ 1 ] = jointPos[ i ][ 1 ] * mag;
+*/
+ }
+
+ for( int i = 0; i < HalfCircle; ++i )
+ {
+ pointScales[ i ] = MinShadowSize;
+ pointScales[ i + HalfCircle ] = -MinShadowSize;
+
+ for( int j = 0; j < NumShadowJoints; ++j )
+ {
+ float dot;
+ // Project joint position onto blob point position.
+ dot = ( BlobPoints[ i ][ 0 ] * jointPos[ j ][ 0 ] ) + ( BlobPoints[ i ][ 1 ] * jointPos[ j ][ 1 ] );
+/*
+ Uncomment this if you want to weight the joint position by how close
+ they line up to the blob point position.
+ dot *= rmt::Abs( ( BlobPoints[ i ][ 0 ] * jointNor[ j ][ 0 ] ) + ( BlobPoints[ i ][ 1 ] * jointNor[ j ][ 1 ] ) );
+*/
+ pointScales[ i ] = rmt::Max( pointScales[ i ], dot );
+ pointScales[ i + HalfCircle ] = rmt::Min( pointScales[ i + HalfCircle ], dot );
+ }
+ }
+ // Since we'll be mirroring the circle, we'll conviently keep the second half of the scales array
+ //as negative numbers so that it will automatically mirror our points for us.
+ }
+
+ pddiPrimStream* blob = 0;
+
+ pddiShader* blobShader = BootupContext::GetInstance()->GetSharedShader();
+ rAssert( blobShader != NULL );
+
+ if( ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ blobShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_NONE );
+ }
+ else
+ {
+ blobShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_MODULATE );
+ }
+ blobShader->SetInt( PDDI_SP_ISLIT, 0 );
+ blobShader->SetInt( PDDI_SP_ALPHATEST, 0 );
+ blobShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_GOURAUD );
+
+ blob = p3d::pddi->BeginPrims( blobShader, PDDI_PRIM_TRIANGLES, PDDI_V_C, 9 * NumBlobSlices );
+ for(int i=0; i < NumBlobSlices; i++)
+ {
+ float x0, y0, x1, y1, x2, y2, x3, y3;
+ int index = i % HalfCircle;
+ int nextIndex = ( i + 1 ) % HalfCircle;
+ int nextScaleIndex = ( i + 1 ) % NumBlobSlices;
+ float fadeScale = ( i < HalfCircle ) ? BlobParams->ShadowScale : -BlobParams->ShadowScale;
+ float nextFadeScale = ( nextScaleIndex < HalfCircle ) ? BlobParams->ShadowScale : -BlobParams->ShadowScale;
+
+ // Draw inside.
+ blob->Colour( insideColour );
+ blob->Coord( 0.0f, 0.0f, 0.0f );
+
+ blob->Colour( insideColour );
+ x0 = BlobPoints[ index ][ 0 ];
+ x0 *= BlobParams->ShadowScale;
+ x0 *= articulated ? pointScales[ i ] : ( index == i ) ? NonArticulatedScale : -NonArticulatedScale;
+ y0 = BlobPoints[ index ][ 1 ];
+ y0 *= BlobParams->ShadowScale;
+ y0 *= articulated ? pointScales[ i ] : ( index == i ) ? NonArticulatedScale : -NonArticulatedScale;
+ blob->Coord( x0, y0, 0.0f );
+
+ blob->Colour( insideColour );
+ x1 = BlobPoints[ nextIndex ][ 0 ];
+ x1 *= BlobParams->ShadowScale;
+ x1 *= articulated ? pointScales[ nextScaleIndex ] : ( nextIndex == nextScaleIndex ) ? NonArticulatedScale : -NonArticulatedScale;
+ y1 = BlobPoints[ nextIndex ][ 1 ];
+ y1 *= BlobParams->ShadowScale;
+ y1 *= articulated ? pointScales[ nextScaleIndex ] : ( nextIndex == nextScaleIndex ) ? NonArticulatedScale : -NonArticulatedScale;
+ blob->Coord( x1, y1, 0.0f );
+
+ // Draw first part of outside.
+ blob->Colour( insideColour );
+ blob->Coord( x0, y0, 0.0f );
+
+ blob->Colour( OutsideColour );
+ x2 = x1 + ( FadePoints[ nextIndex ][ 0 ] * nextFadeScale );
+ y2 = y1 + ( FadePoints[ nextIndex ][ 1 ] * nextFadeScale );
+ blob->Coord( x2, y2, 0.0f );
+
+ blob->Colour( insideColour );
+ blob->Coord( x1, y1, 0.0f );
+
+ // Draw second part of outside.
+ blob->Colour( insideColour );
+ blob->Coord( x0, y0, 0.0f );
+
+ blob->Colour( OutsideColour );
+ x3 = x0 + ( FadePoints[ index ][ 0 ] * fadeScale );
+ y3 = y0 + ( FadePoints[ index ][ 1 ] * fadeScale );
+ blob->Coord( x3, y3, 0.0f );
+
+ blob->Colour( OutsideColour );
+ blob->Coord( x2, y2, 0.0f );
+ }
+ p3d::pddi->EndPrims( blob );
+
+ p3d::stack->Pop();
+ END_PROFILE( "Char Blobby Shadow" );
+ }
+}
+/*
+==============================================================================
+CharacterRenderable::GetDrawable
+==============================================================================
+Description: Comment
+
+Parameters: ( )
+
+Return: tDrawablePose
+
+=============================================================================
+*/
+tDrawablePose* CharacterRenderable::GetDrawable( ) const
+{
+ for(int lod = (int)mCurrentLOD; lod >= (int)Low; lod--)
+ {
+ if(mpDrawableList[ lod ] )
+ {
+ return mpDrawableList[ lod ];
+ }
+ }
+ return NULL;
+}
+
+/*
+==============================================================================
+CharacterRenderable::GetLOD
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: CharacterRenderable
+
+=============================================================================
+*/
+int CharacterRenderable::GetLOD( void ) const
+{
+ return mCurrentLOD;
+}
+/*
+==============================================================================
+CharacterRenderable::SetLOD
+==============================================================================
+Description: Comment
+
+Parameters: ( CharacterLOD LOD )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterRenderable::SetLOD( int LOD )
+{
+ mCurrentLOD = (LOD > High) ? LOD : High;
+}
+
+void CharacterRenderable::SetSwatch( int index )
+{
+ rAssert( 0 <= index && index < NUM_CHARACTER_SWATCHES );
+ if( mpSwatchTextures[index] != NULL && mpSwatchTexture != mpSwatchTextures[index] )
+ {
+ // TODO:
+ // Assign here? or just set mpSwatchTexture = mpSwatchTextures[index]?
+ tRefCounted::Assign(mpSwatchTexture, mpSwatchTextures[index]);
+ }
+}
+
+void CharacterRenderable::SetSwatchTexture( int index, tTexture* pTexture )
+{
+ rAssert( 0 <= index && index < NUM_CHARACTER_SWATCHES );
+ tRefCounted::Assign(mpSwatchTextures[index], pTexture);
+}
+
+void CharacterRenderable::SetShockEffect( tDrawable* pDrawable )
+{
+ tRefCounted::Assign(mpShockedDrawable, pDrawable);
+}
+
+
+void CharacterRenderable::SetSwatchShader( tShader* pShader )
+{
+ tRefCounted::Assign(mpSwatchShader, pShader);
+}
+
+void CharacterRenderable::SetFadeAlpha( int fadeAlpha )
+{
+ mFadeAlpha = rmt::Clamp( fadeAlpha, 0, 255 );
+}
+
+void CharacterRenderable::SetShadowColour( tColour colour )
+{
+ mShadowColour = colour;
+}
+
+void CharacterRenderable::DisplayModel( tPose* pose )
+{
+ tDrawablePose* draw = GetDrawable();
+ if(draw)
+ {
+ if( mpSwatchShader != NULL )
+ {
+ if( mpSwatchTexture != NULL )
+ {
+ mpSwatchShader->SetTexture( PDDI_SP_BASETEX, mpSwatchTexture );
+ }
+ }
+ tShaderIntBroadcast blendAlpha( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ draw->ProcessShaders( blendAlpha );
+ tShaderIntBroadcast emissiveFade( PDDI_SP_EMISSIVEALPHA, mFadeAlpha );
+ draw->ProcessShaders( emissiveFade );
+ draw->Display( pose );
+ }
+}
+
+
+void CharacterRenderable::DisplayShocked( tPose* pose )
+{
+ // Alternate displaying the skeleton and displaying the model
+ if ( mDisplayingSkeletonShock )
+ {
+ if ( mpShockedDrawable != NULL )
+ {
+ p3d::stack->PushMultiply( pose->GetJoint(0)->worldMatrix );
+ mpShockedDrawable->Display();
+ p3d::stack->Pop();
+ }
+ mDisplayingSkeletonShock = false;
+ }
+ else
+ {
+
+ //pose->GetSkeleton();
+ //DisplayModel( pose );
+ mDisplayingSkeletonShock = true;
+ }
+}
+
diff --git a/game/code/worldsim/character/characterrenderable.h b/game/code/worldsim/character/characterrenderable.h
new file mode 100644
index 0000000..be5a1ab
--- /dev/null
+++ b/game/code/worldsim/character/characterrenderable.h
@@ -0,0 +1,100 @@
+#ifndef CHARACTERRENDERABLE_H_
+#define CHARACTERRENDERABLE_H_
+
+#include <radmath/vector.hpp>
+#include <p3d/anim/pose.hpp>
+
+class tDrawablePose;
+class tShadowSkin;
+class tShader;
+class tTexture;
+class tDrawable;
+
+struct BlobShadowParams
+{
+ BlobShadowParams( const rmt::Vector& Pos, const rmt::Vector& Normal, const rmt::Vector& Facing ) :
+ GroundPos( Pos ), GroundNormal( Normal ), ShadowFacing( Facing ), ShadowAlpha( 1.0f ), ShadowScale( 1.0f ) {}
+ const rmt::Vector& GroundPos;
+ const rmt::Vector& GroundNormal;
+ const rmt::Vector& ShadowFacing;
+ float ShadowAlpha;
+ float ShadowScale;
+};
+
+class CharacterRenderable
+{
+public:
+ static const int NUM_CHARACTER_SWATCHES = 5;
+
+public:
+ // Constructor
+ CharacterRenderable( tDrawablePose* pDrawablePoseHigh,
+ tDrawablePose* pDrawablePoseMedium,
+ tDrawablePose* pDrawablePoseLow);
+
+ ~CharacterRenderable( void );
+
+ void Display( rmt::Vector iPosn, tPose* pose );
+
+ enum CharacterLOD
+ {
+ Low,
+ Medium,
+ High,
+ MAX_LOD
+ };
+
+ int GetLOD( void ) const;
+ void SetLOD( int LOD );
+
+ tDrawablePose* GetDrawable( ) const;
+
+ int CastsShadow();
+
+ // If GroundPos/GroundNormal are null then it's a volumetric shadow, otherwise it's a simple shadow.
+ void DisplayShadow( tPose* pose, const BlobShadowParams* BlobParams );
+
+ void SetInAnyonesFrustrum( bool inFrustrum ) {mbInAnyonesFrustrum = inFrustrum;}
+
+ void SetSwatch( int index );
+ void SetSwatchShader( tShader* pShader );
+ void SetSwatchTexture( int index, tTexture* pTexture );
+ void SetShockEffect( tDrawable* pDrawable );
+
+ void SetFadeAlpha( int fadeAlpha );
+
+ void SetShadowColour( tColour colour );
+
+ void SetShocked( bool isShocked ){ mIsShocked = isShocked; }
+ bool GetIsShocked()const{ return mIsShocked; }
+
+private:
+ // No default construction.
+ CharacterRenderable( void );
+ CharacterRenderable( const CharacterRenderable& characterRenderable );
+
+ void DisplayModel( tPose* pose );
+ void DisplayShocked( tPose* pose );
+
+ tDrawablePose* mpDrawableList[ MAX_LOD ];
+ int mCurrentLOD;
+
+ bool mbInAnyonesFrustrum;
+
+ tTexture* mpSwatchTexture;
+ tTexture* mpSwatchTextures[NUM_CHARACTER_SWATCHES];
+ tShader* mpSwatchShader;
+
+ tPose::Joint* mShadowJoints[ 4 ];
+ bool mHaveShadowJoints;
+
+ int mFadeAlpha;
+
+ tColour mShadowColour;
+ tDrawable* mpShockedDrawable;
+ bool mIsShocked;
+ bool mDisplayingSkeletonShock;
+};
+
+
+#endif // CHARACTERRENDERABLE_H_ \ No newline at end of file
diff --git a/game/code/worldsim/character/charactertarget.cpp b/game/code/worldsim/character/charactertarget.cpp
new file mode 100644
index 0000000..39c7f45
--- /dev/null
+++ b/game/code/worldsim/character/charactertarget.cpp
@@ -0,0 +1,312 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implementation of class CharacterTarget
+//
+// History: 5/13/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <worldsim/character/charactertarget.h>
+#include <worldsim/character/character.h>
+
+
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+/*
+==============================================================================
+CharacterTarget::CharacterTarget
+==============================================================================
+Description: Comment
+
+Parameters: ( Character* pCharacter )
+
+Return: CharacterTarget
+
+=============================================================================
+*/
+CharacterTarget::CharacterTarget( Character* pCharacter ) :
+ mWalkerID( CharacterEnum::INVALID )
+{
+ mpCharacter = pCharacter;
+}
+/*
+==============================================================================
+CharacterTarget::~CharacterTarget
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: CharacterTarget
+
+=============================================================================
+*/
+CharacterTarget::~CharacterTarget()
+{
+}
+
+/*
+==============================================================================
+CharacterTarget::GetPosition
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector* position )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterTarget::GetPosition( rmt::Vector* position )
+{
+ mpCharacter->GetPosition( *position );
+}
+/*
+==============================================================================
+CharacterTarget::GetHeading
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector* heading )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterTarget::GetHeading( rmt::Vector* heading )
+{
+ mpCharacter->GetFacing( *heading );
+}
+/*
+==============================================================================
+CharacterTarget::GetVUP
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector* vup )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterTarget::GetVUP( rmt::Vector* vup )
+{
+ vup->Set( 0.0f, 1.0f, 0.0f );
+}
+/*
+==============================================================================
+CharacterTarget::GetVelocity
+==============================================================================
+Description: Comment
+
+Parameters: ( rmt::Vector* velocity )
+
+Return: void
+
+=============================================================================
+*/
+void CharacterTarget::GetVelocity( rmt::Vector* velocity )
+{
+ mpCharacter->GetVelocity( *velocity );
+}
+
+//=============================================================================
+// CharacterTarget::GetID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+unsigned int CharacterTarget::GetID()
+{
+ return mWalkerID;
+}
+
+/*
+==============================================================================
+CharacterTarget::IsCar
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: bool
+
+=============================================================================
+*/
+bool CharacterTarget::IsCar() const
+{
+ return false;
+}
+/*
+==============================================================================
+CharacterTarget::IsAirborn
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: bool
+
+=============================================================================
+*/
+bool CharacterTarget::IsAirborn()
+{
+ return !mpCharacter->IsStanding();
+}
+/*
+==============================================================================
+CharacterTarget::IsUnstable
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: bool
+
+=============================================================================
+*/
+bool CharacterTarget::IsUnstable()
+{
+ return false;
+ /*
+ return( !mpCharacter->IsNPC() &&
+ mpCharacter->GetSimState()->GetControl() == sim::simSimulationCtrl );
+ */
+}
+/*
+==============================================================================
+ CharacterTarget::IsQuickTurn
+ ==============================================================================
+ Description: Comment
+
+Parameters: ()
+
+Return: bool
+
+=============================================================================
+*/
+bool CharacterTarget::IsQuickTurn()
+{
+ return false;
+}
+
+//=============================================================================
+// CharacterTarget::GetFirstPersonPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* position )
+//
+// Return: void
+//
+//=============================================================================
+void CharacterTarget::GetFirstPersonPosition( rmt::Vector* position )
+{
+ poser::Pose* pose = mpCharacter->GetPuppet()->GetPose();
+
+ rmt::Vector pos;
+ pos = pose->GetJoint(17)->GetWorldMatrix().Row(3); // 17 is the head (should be looking it up by name, but what the hell)
+ pos -= pose->GetJoint(0)->GetWorldMatrix().Row(3); // 0 is the motion root
+ pos.y += 0.05f; // bump it up a little, the head joint actually a little too low
+
+ //Find the eye spot on the character and return that position.
+ position->Set( 0.0f, pos.y, 0.0f );
+}
+
+
+/*
+==============================================================================
+CharacterTarget::GetName
+==============================================================================
+Description: This is only for debugging, so in the implementation go ahead and
+ make this return NULL in release.
+
+Parameters: ()
+
+Return: const
+
+=============================================================================
+*/
+const char* const CharacterTarget::GetName()
+{
+ return 0;
+}
+
+//=============================================================================
+// CharacterTarget::SetID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( CharacterEnum::WalkerID id )
+//
+// Return: void
+//
+//=============================================================================
+void CharacterTarget::SetID( CharacterEnum::WalkerID id )
+{
+ mWalkerID = id;
+}
+
+//=============================================================================
+// CharacterTarget::IsInReverse
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool CharacterTarget::IsInReverse()
+{
+ return false;
+}
+
+//=============================================================================
+// CharacterTarget::GetTerrainIntersect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector& pos, rmt::Vector& normal )
+//
+// Return: void
+//
+//=============================================================================
+void CharacterTarget::GetTerrainIntersect( rmt::Vector& pos, rmt::Vector& normal ) const
+{
+ mpCharacter->GetTerrainIntersect( pos, normal );
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/worldsim/character/charactertarget.h b/game/code/worldsim/character/charactertarget.h
new file mode 100644
index 0000000..0ad5595
--- /dev/null
+++ b/game/code/worldsim/character/charactertarget.h
@@ -0,0 +1,72 @@
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: charactertarget.h
+//
+// Description: Blahblahblah
+//
+// History: 5/13/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef CHARACTERTARGET_H
+#define CHARACTERTARGET_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/radmath.hpp>
+#include <camera/isupercamtarget.h>
+#include <constants/characterenum.h>
+#include <presentation/gui/utility/hudmap.h>
+
+//========================================
+// Forward References
+//========================================
+class Character;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class CharacterTarget
+:
+public ISuperCamTarget, public IHudMapIconLocator
+{
+public:
+ CharacterTarget( Character* pCharacter );
+ ~CharacterTarget();
+
+ virtual void GetPosition( rmt::Vector* position );
+ virtual void GetHeading( rmt::Vector* heading );
+ virtual void GetVUP( rmt::Vector* vup );
+ virtual void GetVelocity( rmt::Vector* velocity );
+ virtual unsigned int GetID();
+ virtual bool IsCar() const;
+ virtual bool IsAirborn();
+ virtual bool IsUnstable();
+ virtual bool IsQuickTurn();
+ virtual bool IsInReverse();
+ virtual void GetFirstPersonPosition( rmt::Vector* position );
+ virtual void GetTerrainIntersect( rmt::Vector& pos, rmt::Vector& normal ) const;
+
+
+
+ //This is only for debugging, so in the implementation go ahead and
+ //make this return NULL in release.
+ virtual const char* const GetName();
+
+ void SetID( CharacterEnum::WalkerID id );
+private:
+
+ //Prevent wasteful constructor creation.
+ CharacterTarget( void );
+ CharacterTarget( const CharacterTarget& charactertarget );
+ CharacterTarget& operator=( const CharacterTarget& charactertarget );
+
+ Character* mpCharacter;
+ CharacterEnum::WalkerID mWalkerID;
+};
+
+#endif //CHARACTERTARGET_H \ No newline at end of file
diff --git a/game/code/worldsim/character/controllereventhandler.h b/game/code/worldsim/character/controllereventhandler.h
new file mode 100644
index 0000000..7d3c024
--- /dev/null
+++ b/game/code/worldsim/character/controllereventhandler.h
@@ -0,0 +1,54 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: controllereventhandler.h
+//
+// Description: Blahblahblah
+//
+// History: 09/08/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef CONTROLLEREVENTHANDLER_H_
+#define CONTROLLEREVENTHANDLER_H_
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+#include <events/eventlistener.h>
+
+class CameraRelativeCharacterController;
+
+class CameraRelativeCharacterControllerEventHandler
+:
+public EventListener
+{
+public:
+ CameraRelativeCharacterControllerEventHandler( CameraRelativeCharacterController* pParent )
+ :
+ mpParent( pParent )
+ {
+ }
+ // Derived classes must implement this method to receive
+ // event notification.
+ virtual void HandleEvent( EventEnum id, void* pEventData )
+ {
+ mpParent->HandleEvent( id, pEventData );
+ }
+protected:
+private:
+ CameraRelativeCharacterControllerEventHandler( void );
+ CameraRelativeCharacterController* mpParent;
+};
+
+#endif //CONTROLLEREVENTHANDLER_H_ \ No newline at end of file
diff --git a/game/code/worldsim/character/footprint/allfootprint.cpp b/game/code/worldsim/character/footprint/allfootprint.cpp
new file mode 100644
index 0000000..88e1a6f
--- /dev/null
+++ b/game/code/worldsim/character/footprint/allfootprint.cpp
@@ -0,0 +1 @@
+#include <worldsim\character\footprint\footprintmanager.cpp> \ No newline at end of file
diff --git a/game/code/worldsim/character/footprint/footprint.cpp b/game/code/worldsim/character/footprint/footprint.cpp
new file mode 100644
index 0000000..7b019ab
--- /dev/null
+++ b/game/code/worldsim/character/footprint/footprint.cpp
@@ -0,0 +1,49 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: footprintmanager
+//
+// Description: Holds a list of all footprints, and fades them out over time
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <worldsim/character/footprint/footprint.h>
+
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+Footprint::Footprint()
+{
+
+}
+
+Footprint::~Footprint()
+{
+
+}
+
+void
+Footprint::Display()
+{
+
+}
+
diff --git a/game/code/worldsim/character/footprint/footprint.h b/game/code/worldsim/character/footprint/footprint.h
new file mode 100644
index 0000000..3e299a0
--- /dev/null
+++ b/game/code/worldsim/character/footprint/footprint.h
@@ -0,0 +1,73 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: footprint
+//
+// Description: Simple class inherited from tDrawable. Draws a foot print
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef FOOTPRINT_H
+#define FOOTPRINT_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <p3d\drawable.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+// Describe here what you require of the client which uses or
+// has this class - essentially, the converse of the description
+// above. A constraint is an expression of some semantic
+// condition that must be preserved, an invariant of a class or
+// relationship that must be preserved while the system is in a
+// steady state.
+//
+//===========================================================================
+class Footprint : public tDrawable
+{
+ public:
+ Footprint();
+ virtual ~Footprint();
+
+ virtual void Display();
+
+
+ protected:
+
+ private:
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow Footprint from being copied and assigned.
+ Footprint( const Footprint& );
+ Footprint& operator=( const Footprint& );
+
+};
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/character/footprint/footprintmanager.cpp b/game/code/worldsim/character/footprint/footprintmanager.cpp
new file mode 100644
index 0000000..7a9a313
--- /dev/null
+++ b/game/code/worldsim/character/footprint/footprintmanager.cpp
@@ -0,0 +1,252 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: footprintmanager
+//
+// Description: Holds a list of all footprints, and fades them out over time
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <worldsim/character/footprint/footprintmanager.h>
+#include <memory/srrmemory.h>
+#include <p3d/utility.hpp>
+#include <contexts/bootupcontext.h>
+#include <p3d/texture.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/shader.hpp>
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+const float FOOTPRINT_LIVE_TIME = 0.5f;
+const float FOOTPRINT_FADE_PER_MS = 1 / 2000.0f;
+
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+FootprintManager* FootprintManager::spFootprintManager = 0;
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+FootprintManager::Footprint::Footprint():
+alpha( 0 )
+{
+ const float FOOTPRINT_WIDTH = 1.0f;
+ const float FOOTPRINT_HEIGHT = 1.0f;
+ const float HALF_FOOTPRINT_WIDTH = FOOTPRINT_WIDTH / 2.0f;
+ const float HALF_FOOTPRINT_HEIGHT = FOOTPRINT_HEIGHT / 2.0f;
+
+ rmt::Vector topLeft( -HALF_FOOTPRINT_WIDTH, 0, HALF_FOOTPRINT_HEIGHT );
+ rmt::Vector topRight( HALF_FOOTPRINT_WIDTH, 0 , HALF_FOOTPRINT_HEIGHT );
+ rmt::Vector bottomLeft( -HALF_FOOTPRINT_WIDTH,0 ,-HALF_FOOTPRINT_HEIGHT );
+ rmt::Vector bottomRight( HALF_FOOTPRINT_WIDTH,0, -HALF_FOOTPRINT_HEIGHT );
+
+ points[0] = bottomLeft;
+ points[1] = bottomRight;
+ points[2] = topLeft;
+ points[3] = topRight;
+}
+
+FootprintManager::FootprintManager()
+{
+ for ( int i = 0 ; i < eNumFootprintTypes ; i++ )
+ {
+ m_ActiveFootprints[ i ].Allocate( MAX_NUM_FOOTPRINTS );
+ mpTextures[ i ] = NULL;
+ }
+}
+
+FootprintManager::~FootprintManager()
+{
+ FreeTextures();
+}
+
+FootprintManager* FootprintManager::CreateInstance( void )
+{
+ rAssertMsg( spFootprintManager == 0, "FootprintManager already created.\n" );
+ HeapManager::GetInstance()->PushHeap( GMA_PERSISTENT );
+ spFootprintManager = new FootprintManager;
+ rAssert( spFootprintManager );
+ HeapManager::GetInstance()->PopHeap(GMA_PERSISTENT);
+ return FootprintManager::GetInstance();
+}
+
+FootprintManager* FootprintManager::GetInstance( void )
+{
+ rAssertMsg( spFootprintManager != 0, "FootprintManager has not been created yet.\n" );
+ return spFootprintManager;
+}
+
+void FootprintManager::DestroyInstance( void )
+{
+ rAssertMsg( spFootprintManager != 0, "FootprintManager has not been created.\n" );
+ delete spFootprintManager;
+ spFootprintManager = 0;
+}
+
+bool FootprintManager::CreateFootprint( const rmt::Matrix& transform, TYPE type )
+{
+
+ bool footprintCreated;
+
+ if ( mpTextures[ type ] == NULL )
+ return false;
+
+ Footprint footprint;
+ footprint.alpha = 1.0f;
+ for ( int i = 0 ; i < 4 ; i++ )
+ {
+ transform.Transform( footprint.points[i], &footprint.points[i] );
+ }
+
+ if ( m_ActiveFootprints[ type ].mSize > m_ActiveFootprints[ type ].mUseSize )
+ {
+ m_ActiveFootprints[ type ].Add( footprint );
+ footprintCreated = true;
+ }
+ else
+ {
+ footprintCreated = false;
+ }
+ return footprintCreated;
+}
+
+void FootprintManager::Render()
+{
+ return;
+ int i,j;
+
+
+ SetTexture( FootprintManager::eSquishies, "footprint.bmp" );
+
+ bool oldZWrite = p3d::pddi->GetZWrite();
+ pddiCompareMode oldZComp = p3d::pddi->GetZCompare();
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( false );
+ }
+ if( oldZComp != PDDI_COMPARE_LESS )
+ {
+ p3d::pddi->SetZCompare( PDDI_COMPARE_LESS );
+ }
+ pddiCullMode cm = p3d::pddi->GetCullMode();
+ p3d::pddi->SetCullMode(PDDI_CULL_NONE);
+
+ for ( i = 0 ; i < eNumFootprintTypes ;i++)
+ {
+ if ( m_ActiveFootprints[i].mUseSize == 0 )
+ continue;
+
+ if ( mpTextures[i] == NULL )
+ continue;
+
+ pddiShader* pShader = GetBootupContext()->GetSharedShader();
+ pShader->SetInt( PDDI_SP_ISLIT, 0 );
+ pShader->SetInt( PDDI_SP_ALPHATEST, 0 );
+ pShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_FLAT );
+ pShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ pShader->SetTexture( PDDI_SP_BASETEX, mpTextures[ i ]->GetTexture() );
+
+ pddiPrimStream* stream = p3d::pddi->BeginPrims( pShader, PDDI_PRIM_TRIANGLES, PDDI_V_CT, m_ActiveFootprints[i].mUseSize * 6 );
+
+ for ( j = 0 ; j < m_ActiveFootprints[i].mUseSize ; j++ )
+ {
+ Footprint& footprint = m_ActiveFootprints[i][j];
+ tColour colour;
+ colour.Set( 255,255,255, static_cast< int >( footprint.alpha * 255.0f ));
+
+ // 2 triangles for every foot step
+ // Now draw the triangle.
+ // bottom left tri.
+ stream->Colour( colour );
+ stream->UV( 0.0f, 0.0f );
+ stream->Coord( footprint.points[0].x, footprint.points[0].y, footprint.points[0].z );
+
+ stream->Colour( colour );
+ stream->UV( 1.0f, 0.0f );
+ stream->Coord( footprint.points[1].x, footprint.points[1].y, footprint.points[1].z );
+
+ stream->Colour( colour );
+ stream->UV( 0.0f, 1.0f );
+ stream->Coord( footprint.points[2].x, footprint.points[2].y, footprint.points[2].z );
+
+ // Top right tri.
+ stream->Colour( colour );
+ stream->UV( 1.0f, 1.0f );
+ stream->Coord( footprint.points[3].x, footprint.points[3].y, footprint.points[3].z );
+
+ stream->Colour( colour );
+ stream->UV( 0.0f, 1.0f );
+ stream->Coord( footprint.points[2].x, footprint.points[2].y, footprint.points[2].z );
+
+ stream->Colour( colour );
+ stream->UV( 1.0f, 0.0f );
+ stream->Coord( footprint.points[1].x, footprint.points[1].y, footprint.points[1].z );
+
+ }
+ p3d::pddi->EndPrims( stream );
+ pShader->SetTexture( PDDI_SP_BASETEX, 0 );
+
+ }
+
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( true );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( oldZComp );
+ }
+ p3d::pddi->SetCullMode(cm);
+
+
+}
+
+void FootprintManager::Update( unsigned int timeInMS )
+{
+ for ( int i = 0 ; i < eNumFootprintTypes ; i++ )
+ {
+ for ( int j = 0 ; j < m_ActiveFootprints[ i ].mUseSize ; j++ )
+ {
+ m_ActiveFootprints[i][j].alpha -= static_cast< float >( timeInMS )* FOOTPRINT_FADE_PER_MS;
+ if ( m_ActiveFootprints[i][j].alpha < 0 )
+ {
+ m_ActiveFootprints[i].Remove( j );
+ }
+ }
+ }
+}
+
+void FootprintManager::SetTexture( TYPE type, const char* textureName )
+{
+ tTexture* pTexture = p3d::find< tTexture >( textureName );
+ if ( pTexture != NULL )
+ {
+ tRefCounted::Assign( mpTextures[type], pTexture );
+ }
+}
+
+void FootprintManager::FreeTextures()
+{
+ for ( int i = 0 ; i < eNumFootprintTypes ; i++)
+ {
+ if ( mpTextures[i] != NULL )
+ {
+ mpTextures[ i ]->Release();
+ mpTextures[ i ] = NULL;
+ }
+ }
+}
+
+
diff --git a/game/code/worldsim/character/footprint/footprintmanager.h b/game/code/worldsim/character/footprint/footprintmanager.h
new file mode 100644
index 0000000..12f83d2
--- /dev/null
+++ b/game/code/worldsim/character/footprint/footprintmanager.h
@@ -0,0 +1,105 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: footprintmanager
+//
+// Description: Holds a list of all footprints, and fades them out over time
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef FOOTPRINTMANAGER_H
+#define FOOTPRINTMANAGER_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <worldsim\character\footprint\footprintmanager.h>
+#include <render\culling\swaparray.h>
+#include <p3d\utility.hpp>
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class tShader;
+class tTexture;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Footprint manager - creates and renders footprints
+//
+// Constraints:
+//
+//
+//===========================================================================
+class FootprintManager
+{
+ public:
+ FootprintManager();
+ ~FootprintManager();
+
+ static FootprintManager* GetInstance( void );
+ static FootprintManager* CreateInstance( void );
+ static void DestroyInstance( void );
+
+ enum { MAX_NUM_FOOTPRINTS = 1 };
+ enum TYPE { eSquishies, eNumFootprintTypes };
+
+ bool CreateFootprint( const rmt::Matrix& transform, TYPE type );
+ void Update( unsigned int timeInMS );
+ void FreeTextures();
+
+ void SetTexture( TYPE, const char* texture );
+ void ClearAllFootPrints();
+ void Render();
+
+ protected:
+
+ private:
+
+ static FootprintManager* spFootprintManager;
+
+ struct Footprint
+ {
+ Footprint();
+
+ rmt::Vector points[4];
+ float alpha;
+ };
+
+ typedef SwapArray< Footprint > ListOfFootprints;
+
+ ListOfFootprints m_ActiveFootprints[eNumFootprintTypes];
+
+ tTexture* mpTextures[ eNumFootprintTypes ];
+
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow FootprintManager from being copied and assigned.
+ FootprintManager( const FootprintManager& );
+ FootprintManager& operator=( const FootprintManager& );
+
+ // A little syntactic sugar for getting at this singleton.
+};
+
+inline FootprintManager* GetFootprintManager() { return( FootprintManager::GetInstance() ); }
+
+
+
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/coins/allcoins.cpp b/game/code/worldsim/coins/allcoins.cpp
new file mode 100644
index 0000000..b8ea98e
--- /dev/null
+++ b/game/code/worldsim/coins/allcoins.cpp
@@ -0,0 +1,2 @@
+#include <worldsim/coins/coinmanager.cpp>
+#include <worldsim/coins/sparkle.cpp> \ No newline at end of file
diff --git a/game/code/worldsim/coins/coinmanager.cpp b/game/code/worldsim/coins/coinmanager.cpp
new file mode 100644
index 0000000..0f7d50d
--- /dev/null
+++ b/game/code/worldsim/coins/coinmanager.cpp
@@ -0,0 +1,1132 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: CoinManager.cpp
+//
+// Description: Implementation of class CoinManager
+//
+// History: 29/1/2003 + Created -- James Harrison
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <radmath/matrix.hpp>
+#include <radmath/random.hpp>
+#include <p3d/entity.hpp>
+#include <p3d/view.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/matrixstack.hpp>
+#include <pddi/pddi.hpp>
+
+//#include <math.h> //need this if we want extra accurate coin paths.
+
+//========================================
+// Project Includes
+//========================================
+#include <gameflow/gameflow.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/coins/sparkle.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <memory/srrmemory.h>
+#include <events/eventmanager.h>
+#include <render/intersectmanager/intersectmanager.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <data/persistentworldmanager.h>
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+
+/* Watcher stuff */
+#ifndef RAD_RELEASE
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+static float COIN_HOVER = 0.5f; // How far off the ground will the coin hover.
+#if !defined( RAD_PS2 ) && defined( RAD_RELEASE )
+static const float FRAME_RATIO = 1.0f / 60.0f;
+//static float COIN_DRAG = 0.9848f; // .4 to the power of 1/60 of a second.
+#elif defined( RAD_RELEASE )
+static const float FRAME_RATIO = 1.0f / 30.0f;
+//static float COIN_DRAG = 0.9700f; // .4 to the power of 1/30 of a second.
+#else
+static const float FRAME_RATIO = 1.0f / 15.0f;
+//static float COIN_DRAG = 0.9407f; // .4 to the power of 1/15 of a second.
+#endif
+static float COIN_SPAWN_VELOCITY = 9.5f;// * FRAME_RATIO;
+static float COIN_EXTRA_VERTICAL = 1.1f;// * FRAME_RATIO;
+// Use these values if you want accurate coins.
+static float GRAVITY = 21.0f; // * FRAME_RATIO;
+static float COIN_DRAG = 0.1f;
+
+// Use these if you want faster (but less accurate coins.
+//static float GRAVIYT = 0.9f * FRAME_RATIO;
+
+static float LIFE_TIME = 5.0f; // How many seconds the coin lives for.
+static float DECAY_TIME = 5.0f; // How long does it take to disappear.
+#define I_DECAY_TIME ( 1.0f / DECAY_TIME ) // Instead of doing a divide.
+static float DECAY_EXTRA_SPIN = rmt::PI * 8.0f; // How much extra spin per frame do we want.
+static float SPIN_MULTIPLIER = 3.0f;
+static float RANGE = 2.25f; // Coin collection radius.
+#define INNER_RANGE ( RANGE * ( 4.0f / 3.0f ) )
+static float COLLECT_TIME = 1.0f; // How long does it take a coin to suck into the player.
+#define I_COLLECT_TIME ( 1.0f / COLLECT_TIME )
+static float POP_SCALE = 0.4f; // How much pop up does the coin have when it is collected.
+static float BOUNCE_DAMPENING = 0.6f; // How much energy does the coin retain when it hits the ground.
+
+static float FLYING_TIME = 0.25f; // How long does it take to fly up to the HUD.
+#define I_FLYING_TIME ( 1.0f / FLYING_TIME )
+static float MAX_VISIBLITY = 60.0f; // How far away is the coin.
+static float COUNTER_X = 0.45f; // Where is the destination we are flying the coin up to.
+static float COUNTER_Y = 0.45f; // Where is the destination we are flying the coin up to.
+#ifdef RAD_XBOX
+static float SPARKLE_FLOAT = 0.025f; // How much vertical velocity for the sparkles per frame.
+#else
+static float SPARKLE_FLOAT = 0.05f;
+#endif
+static float IN_CAR_RANGE_MULTIPLIER = 2.25f;
+static int dbg_CoinsDrawn = 0;
+static int dbg_MaxCoins = 0;
+
+#else /* Same as the variables available to the watcher, but const so the compiler can optimize them. */
+const static float COIN_HOVER = 0.5f; // How far off the ground will the coin hover.
+// Use this for more accurate coins.
+//static const float COIN_SPAWN_VELOCITY = 4.0f;
+//static const float COIN_EXTRA_VERTICAL = 2.0f
+#if !defined( RAD_PS2 ) && defined ( RAD_RELEASE )
+static const float FRAME_RATIO = 1.0f / 60.0f;
+//static const float COIN_DRAG = 0.9848f; // .4 to the power of 1/60 of a second.
+#elif defined( RAD_RELEASE )
+static const float FRAME_RATIO = 1.0f / 30.0f;
+//static const float COIN_DRAG = 0.9700f; // .4 to the power of 1/30 of a second.
+#else
+static const float FRAME_RATIO = 1.0f / 15.0f;
+//static const float COIN_DRAG = 0.9407f; // .4 to the power of 1/15 of a second.
+#endif
+static const float COIN_SPAWN_VELOCITY = 9.5f;// * FRAME_RATIO;
+static const float COIN_EXTRA_VERTICAL = 1.1f;// * FRAME_RATIO;
+static const float GRAVITY = 21.0f;
+static const float COIN_DRAG = 0.1f;
+//static const float GRAVITY = 0.9f * FRAME_RATIO;
+static const float LIFE_TIME = 11.0f; // How many seconds the coin lives for.
+static const float DECAY_TIME = 5.0f; // How long does it take to disappear.
+static const float I_DECAY_TIME = 1.0f / DECAY_TIME; // Instead of doing a divide.
+static const float DECAY_EXTRA_SPIN = rmt::PI * 8.0f; // How much extra spin per frame do we want.
+static const float SPIN_MULTIPLIER = 3.0f;
+static const float RANGE = 2.25f; // Coin collection radius.
+static const float INNER_RANGE = RANGE * ( 4.0f / 3.0f );
+static const float COLLECT_TIME = 1.0f; // How long does it take a coin to suck into the player.
+static const float I_COLLECT_TIME = 1.0f / COLLECT_TIME;
+static const float POP_SCALE = 0.4f; // How much pop up does the coin have when it is collected.
+// Use these values if you want accurate coins.
+/*
+static const float COIN_DRAG = 0.5f;
+static const float GRAVITY = 0.9f;
+*/
+static const float BOUNCE_DAMPENING = 0.6f; // How much energy does the coin retain when it hits the ground.
+static const float FLYING_TIME = 0.25f; // How long does it take to fly up to the HUD.
+static const float I_FLYING_TIME = 1.0f / FLYING_TIME;
+static const float MAX_VISIBLITY = 60.0f; // How far away is the coin.
+static const float COUNTER_X = 0.25f; // Where is the destination we are flying the coin up to.
+static const float COUNTER_Y = 0.4f; // Where is the destination we are flying the coin up to.
+#ifdef RAD_XBOX
+static const float SPARKLE_FLOAT = 0.025f;
+#else
+static const float SPARKLE_FLOAT = 0.05f;
+#endif
+static const float IN_CAR_RANGE_MULTIPLIER = 2.25f;
+#endif
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+CoinManager* CoinManager::spCoinManager = 0;
+
+static const int NUM_COINS = 200;
+static const int NUM_SPARKLES = 120; // Although we seldom use all these sparkles we do when the vehicle explodes.
+#ifdef RAD_XBOX
+static int Sparkle_Rate = 6;
+static const int Glint_Rate = 4;
+static const int SPARKLE_RATE_TOGGLE = Sparkle_Rate ^ ( Sparkle_Rate + 2 );
+static const int HUD_SPARKLE_RATE = 60;
+#else
+static int Sparkle_Rate = 3;
+static const int Glint_Rate = 2;
+static const int SPARKLE_RATE_TOGGLE = Sparkle_Rate ^ ( Sparkle_Rate + 1 );
+static const int HUD_SPARKLE_RATE = 30;
+#endif
+static int DoSparkle = 0;
+static int DoGlint = 0;
+static int GlintDelay = 0;
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+CoinManager* CoinManager::CreateInstance( void )
+{
+ rAssertMsg( spCoinManager == 0, "CoinManager already created.\n" );
+ HeapManager::GetInstance()->PushHeap( GMA_PERSISTENT );
+ spCoinManager = new CoinManager;
+ rAssert( spCoinManager );
+ HeapManager::GetInstance()->PopHeap( GMA_PERSISTENT );
+ return CoinManager::GetInstance();
+}
+
+CoinManager* CoinManager::GetInstance( void )
+{
+ rAssertMsg( spCoinManager != 0, "CoinManager has not been created yet.\n" );
+ return spCoinManager;
+}
+
+void CoinManager::DestroyInstance( void )
+{
+ rAssertMsg( spCoinManager != 0, "CoinManager has not been created.\n" );
+ delete spCoinManager;
+ spCoinManager = 0;
+}
+
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CoinManager::CoinManager() :
+ m_pCoinDrawable( 0 ),
+ mActiveCoins( NULL),
+ mNumActiveCoins( 0 ),
+ mNextInactiveCoin( 0 ),
+ mNumHUDFlying( 0 ),
+ mHUDSparkle( 0 ),
+ mHUDCoinX( -10.0f ),
+ mHUDCoinY( -10.0f ),
+ mHUDCoinAngle( 0.0f ),
+ mDrawAfterGui(false)
+{
+#ifndef RAD_RELEASE
+ radDbgWatchAddFloat( &COIN_SPAWN_VELOCITY, "Spawn - Initial velocity", "Coins", 0, 0, 0.01f, 5.0f );
+ radDbgWatchAddFloat( &COIN_EXTRA_VERTICAL, "Spawn - Initial extra vertical", "Coins", 0, 0, 0.0f, 2.5f );
+ radDbgWatchAddFloat( &COIN_DRAG, "Spawn - Movement drag", "Coins", 0, 0, 0.1f, 1.0f );
+ radDbgWatchAddFloat( &GRAVITY, "Spawn - Gravity", "Coins", 0, 0, 0.1f, 2.0f );
+ radDbgWatchAddFloat( &BOUNCE_DAMPENING, "Spawn - Bounce energy dampening", "Coins", 0, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat( &COIN_HOVER, "Spawn - Ground hover", "Coins", 0, 0, 0.0f, 2.0f );
+ radDbgWatchAddFloat( &LIFE_TIME, "Duration - Life", "Coins", 0, 0, 1.0f, 30.0f );
+ radDbgWatchAddFloat( &DECAY_TIME, "Duration - Decay", "Coins", 0, 0, 1.0f, 10.0f );
+ radDbgWatchAddFloat( &RANGE, "Collection - Radius", "Coins", 0, 0, 0.5f, 10.0f );
+ radDbgWatchAddFloat( &IN_CAR_RANGE_MULTIPLIER, "Collection - In car multiplier", "Coins", 0, 0, 0.0f, 5.0f );
+ radDbgWatchAddFloat( &DECAY_EXTRA_SPIN, "Animation - Extra Decay spin", "Coins", 0, 0, 0.0f, 50.0f );
+ radDbgWatchAddFloat( &SPIN_MULTIPLIER, "Animation - Spin multiplier", "Coins", 0, 0, 0.01f, 5.0f );
+ radDbgWatchAddFloat( &COLLECT_TIME, "Animation - Collection duration", "Coins", 0, 0, 0.1f, 5.0f );
+ radDbgWatchAddFloat( &POP_SCALE, "Animation - Collection pop up", "Coins", 0, 0, 0.1f, 1.0f );
+ radDbgWatchAddFloat( &FLYING_TIME, "Animation - Fly to HUD duration", "Coins", 0, 0, 0.1f, 1.0f );
+ radDbgWatchAddFloat( &COUNTER_X, "Animation - HUD X position", "Coins", 0, 0, -0.7f, 0.7f );
+ radDbgWatchAddFloat( &COUNTER_Y, "Animation - HUD Y position", "Coins", 0, 0, -0.7f, 0.7f );
+ radDbgWatchAddFloat( &SPARKLE_FLOAT, "Animation - Sparkle float", "Coins", 0, 0, 0.0f, 0.5f );
+ radDbgWatchAddFloat( &MAX_VISIBLITY, "Visibility culling distance", "Coins", 0, 0, 10.0f, 100.0f );
+ radDbgWatchAddShort( &mNumActiveCoins, "Active coin count", "Coins", 0, 0, 0, NUM_COINS, true );
+ radDbgWatchAddInt( &dbg_CoinsDrawn, "Drawn coin count", "Coins", 0, 0, 0, NUM_COINS, true );
+ radDbgWatchAddInt( &dbg_MaxCoins, "Max active coins", "Coins", 0, 0, 0, NUM_COINS, true );
+#endif
+}
+
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+CoinManager::~CoinManager()
+{
+#ifndef RAD_RELEASE
+ radDbgWatchDelete( &COIN_SPAWN_VELOCITY );
+ radDbgWatchDelete( &COIN_EXTRA_VERTICAL );
+ radDbgWatchDelete( &COIN_DRAG );
+ radDbgWatchDelete( &GRAVITY );
+ radDbgWatchDelete( &BOUNCE_DAMPENING );
+ radDbgWatchDelete( &COIN_HOVER );
+ radDbgWatchDelete( &LIFE_TIME );
+ radDbgWatchDelete( &DECAY_TIME );
+ radDbgWatchDelete( &RANGE );
+ radDbgWatchDelete( &DECAY_EXTRA_SPIN );
+ radDbgWatchDelete( &SPIN_MULTIPLIER );
+ radDbgWatchDelete( &COLLECT_TIME );
+ radDbgWatchDelete( &POP_SCALE );
+ radDbgWatchDelete( &FLYING_TIME );
+ radDbgWatchDelete( &COUNTER_X );
+ radDbgWatchDelete( &COUNTER_Y );
+ radDbgWatchDelete( &SPARKLE_FLOAT );
+ radDbgWatchDelete( &MAX_VISIBLITY );
+ radDbgWatchDelete( &mNumActiveCoins );
+ radDbgWatchDelete( &dbg_CoinsDrawn );
+ radDbgWatchDelete( &dbg_MaxCoins );
+#endif
+ tRefCounted::Release( m_pCoinDrawable );
+ Destroy( );
+}
+
+//==============================================================================
+// Description: Destruction method for all transient data.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void CoinManager::Destroy( void )
+{
+ SetCoinDrawable( NULL );
+
+ if( mActiveCoins != NULL )
+ {
+ GetCheatInputSystem()->UnregisterCallback( this );
+
+ GetEventManager()->RemoveAll( this );
+
+ delete [] mActiveCoins;
+ mActiveCoins = NULL;
+ }
+ mNumHUDFlying = 0;
+ mNumActiveCoins = 0;
+}
+
+/*==============================================================================
+Description: This will be called set up as the game session begins.
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================*/
+void CoinManager::Init( void )
+{
+ #ifdef RAD_GAMECUBE
+ HeapManager::GetInstance()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapManager::GetInstance()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+ mActiveCoins = new ActiveCoin[ NUM_COINS ];
+ rAssert( mActiveCoins );
+ #ifdef RAD_GAMECUBE
+ HeapManager::GetInstance()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapManager::GetInstance()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+ mNextInactiveCoin = 0;
+ mNumActiveCoins = 0;
+ mNumHUDFlying = 0;
+ DoSparkle = 0;
+ DoGlint = 0;
+#ifndef RAD_RELEASE
+ dbg_MaxCoins = 0;
+#endif
+
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_DUMP_DYNA_SECTION );
+
+ GetCheatInputSystem()->RegisterCallback( this );
+}
+
+void CoinManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_VEHICLE_DESTROYED:
+ {
+ Vehicle* vehicle = reinterpret_cast<Vehicle*>( pEventData );
+ OnVehicleDestroyed( vehicle );
+ }
+ break;
+ case EVENT_DUMP_DYNA_SECTION:
+ {
+ RemoveWorldCoins( reinterpret_cast<tName*>( pEventData )->GetUID() );
+ }
+ break;
+ default:
+ {
+ break;
+ }
+ }
+}
+
+/*=============================================================================
+Go nosing through he active coin array looking for an inactive coin. Update
+the next inactive coin index while we are at it to speed up the search.
+=============================================================================*/
+CoinManager::ActiveCoin* CoinManager::GetInactiveCoin( void )
+{
+ if( mNumActiveCoins >= NUM_COINS )
+ {
+ return 0;
+ }
+ for( int i = 0; i < NUM_COINS; ++i )
+ {
+ ActiveCoin* c = &(mActiveCoins[ mNextInactiveCoin ]);
+ mNextInactiveCoin = ( mNextInactiveCoin + 1 ) % NUM_COINS;
+ if( c->State == CS_Inactive )
+ {
+ c->HeadingCos = 0.0f;
+ c->HeadingSin = 1.0f;
+ return c;
+ }
+ }
+ return 0;
+}
+
+/*=============================================================================
+Spawn count number of coins at position. You can optionally override the
+groundY position (handy for exploding wasps), or it takes the Y value form
+the position.
+=============================================================================*/
+void CoinManager::SpawnCoins( int Count, const rmt::Vector& Position, const rmt::Vector* Direction, bool Force )
+{
+ SpawnCoins( Count, Position, Position.y, Direction, Force );
+}
+
+void CoinManager::SpawnCoins( int Count, const rmt::Vector& Position, float GroundY, const rmt::Vector* Direction, bool Force )
+{
+ rAssert( Count >= 0 );
+ if( Count <= 0 )
+ {
+ return;
+ }
+
+
+ // Check for a valid avatar pointer
+ rTuneAssert( GetAvatarManager() != NULL );
+ rTuneAssert( GetAvatarManager()->GetAvatarForPlayer(0) != NULL );
+ bool instaCollect = !Force && GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar();
+
+ for( int i = 0; i < Count; ++i )
+ {
+ ActiveCoin* c = GetInactiveCoin();
+ if( !c )
+ {
+ CollectCoins( Count - i );
+ break;
+ }
+ SpawnCoin( *c, Position, GroundY + COIN_HOVER, Direction, instaCollect );
+ }
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+ GetEventManager()->TriggerEvent( EVENT_SPAWNED_COINS, reinterpret_cast<void*>( Count ) );
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+
+}
+
+/*=============================================================================
+This spawns a coin which is immediately collected by the player. Used for giving
+the player a coin reward and you want to make sure they don't miss it.
+=============================================================================*/
+void CoinManager::SpawnInstantCoins(int Count, const rmt::Vector& Position)
+{
+ rAssert( Count >= 0 );
+ if( Count <= 0 )
+ {
+ return;
+ }
+
+ // Check for a valid avatar pointer
+ rTuneAssert( GetAvatarManager() != NULL );
+ rTuneAssert( GetAvatarManager()->GetAvatarForPlayer(0) != NULL );
+
+ for( int i = 0; i < Count; ++i )
+ {
+ ActiveCoin* c = GetInactiveCoin();
+ if( !c )
+ {
+ CollectCoins( Count - i );
+ break;
+ }
+ SpawnCoin(*c, Position, Position.y + COIN_HOVER, 0, true);
+ }
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+ GetEventManager()->TriggerEvent( EVENT_SPAWNED_COINS, reinterpret_cast<void*>( Count ) );
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+}
+
+/*=============================================================================
+Spawn a single coin.
+=============================================================================*/
+void CoinManager::SpawnCoin( ActiveCoin& Coin, const rmt::Vector& Start, float Ground, const rmt::Vector* Direction, bool InstaCollect )
+{
+ Coin.Position = Start;
+ Coin.Position.y = Ground + COIN_HOVER;
+ Coin.State = InstaCollect ? CS_SpawnToCollect : CS_InitialSpawning;
+ Coin.Age = Sparkle::sRandom.Float() * rmt::PI;
+ Coin.Ground = Ground;
+ Coin.Velocity.x = Sparkle::sRandom.FloatSigned();
+ Coin.Velocity.y = Sparkle::sRandom.Float();
+ Coin.Velocity.z = Sparkle::sRandom.FloatSigned();
+ Coin.Velocity.Normalize();
+ Coin.Velocity.y += COIN_EXTRA_VERTICAL;
+ if( Direction )
+ {
+ if( Direction->Dot( Coin.Velocity ) < 0 )
+ {
+ Coin.Velocity.x *= -1.0f;
+ Coin.Velocity.z *= -1.0f;
+ }
+ }
+ Coin.Velocity.Scale(COIN_SPAWN_VELOCITY);
+ ++mNumActiveCoins;
+#ifndef RAD_RELEASE
+ dbg_MaxCoins = rmt::Max( (int)mNumActiveCoins, dbg_MaxCoins );
+#endif
+}
+
+/*=============================================================================
+Add a HUD coin at the coin counter position and have it fly down to the middle
+of the screen. Used when the player's coin amount decrements so that they can
+see that it's going down a little easier.
+=============================================================================*/
+void CoinManager::AddFlyDownCoin(void)
+{
+ return;
+ // They didn't like it. Method is now a nop...until they decide they want it again.
+/* ActiveCoin* c = GetInactiveCoin();
+ if( !c )
+ {
+ return;
+ }
+ c->Position.x = 0.0f;
+ c->Position.y = 0.0f;
+ c->Position.z = 0.0f;
+ c->State = CS_FlyingFromHUD;
+ c->Age = 0.0f;
+ c->Velocity.Set(-0.5f, -0.5f, 0.0f);
+ c->Velocity.Scale(FLYING_TIME * 2.0f);
+ ++mNumActiveCoins;
+ ++mNumHUDFlying;
+#ifndef RAD_RELEASE
+ dbg_MaxCoins = rmt::Max( (int)mNumActiveCoins, dbg_MaxCoins );
+#endif
+*/
+}
+
+/*=============================================================================
+Update the position/animation of all the coins.
+=============================================================================*/
+void CoinManager::Update( int ElapsedMS )
+{
+ float deltaSeconds = (float)ElapsedMS * 0.001f;
+ mHUDCoinAngle += deltaSeconds;
+#ifndef RAD_RELEASE
+ dbg_CoinsDrawn = 0;
+#endif
+ if(GetGameFlow()->GetCurrentContext() == CONTEXT_PAUSE)
+ {
+ return;
+ }
+ rmt::Vector avatarPos;
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( avatarPos );
+ bool isInCar = GetAvatarManager()->GetAvatarForPlayer( 0 )->IsInCar();
+ for( int i = 0; i < NUM_COINS; ++i )
+ {
+ ActiveCoin& c = mActiveCoins[ i ];
+ if( c.State != CS_Inactive )
+ {
+ float decaySpin = 0.0f;
+ if( c.State != CS_InitialSpawning && c.State != CS_SpawnToCollect && c.State != CS_Collecting && c.State != CS_Collected )
+ {
+ CheckCollection( c, avatarPos, isInCar ? IN_CAR_RANGE_MULTIPLIER : 1.0f );
+ }
+ if( c.State == CS_Collecting )
+ {
+ UpdateCollecting( c, avatarPos );
+ }
+ else if((c.State == CS_FlyingToHUD) || (c.State == CS_FlyingFromHUD))
+ {
+ UpdateHUDFlying( c );
+ }
+ else if( c.State != CS_RestingIndefinitely && c.State != CS_Collecting && c.State != CS_Collected )
+ {
+ if( c.Age > LIFE_TIME + DECAY_TIME )
+ {
+ // Coin has expired.
+ c.State = CS_Inactive;
+ --mNumActiveCoins;
+ GetSparkleManager()->AddSparkle( rmt::Vector( c.Position.x, c.Position.y + mCoinBounding.radius, c.Position.z ), 0.25f, 0.5f, rmt::Vector( 0.0f, 0.01f, 0.0f ), Sparkle::SE_Vanish );
+ }
+ else if( c.Age > LIFE_TIME )
+ {
+ c.State = CS_Decaying;
+ decaySpin = ( c.Age - LIFE_TIME ) * I_DECAY_TIME;
+ decaySpin *= decaySpin;
+ }
+ if( c.State == CS_Spawning || c.State == CS_InitialSpawning || c.State == CS_SpawnToCollect )
+ {
+ UpdateSpawning(c, avatarPos, deltaSeconds);
+ }
+ }
+ c.Age += deltaSeconds;
+ // Rotate the coin based on age.
+ float coinSin, coinCos;
+ rmt::SinCos( ( c.Age * SPIN_MULTIPLIER ) + ( decaySpin * DECAY_EXTRA_SPIN ), &coinSin, &coinCos );
+ c.HeadingCos = coinCos;
+ c.HeadingSin = coinSin;
+ }
+ }
+}
+
+/*=============================================================================
+Damaging out the player's vehicle causes their bank to go down.
+=============================================================================*/
+void CoinManager::OnVehicleDestroyed( Vehicle* DestroyedVehicle )
+{
+ if( DestroyedVehicle->mVehicleType == VT_USER )
+ {
+ int lose = rmt::Min( 20, GetBankValue() );
+ LoseCoins( lose, &( DestroyedVehicle->GetPosition() ) );
+ }
+}
+
+/*=============================================================================
+Whatever drawable is set here will be used for all the coin drawables. Note
+that there isn't any support for shadows or animation. It's just geometry.
+=============================================================================*/
+void CoinManager::SetCoinDrawable( tDrawable* Drawable )
+{
+ tRefCounted::Assign( m_pCoinDrawable, Drawable );
+ if( m_pCoinDrawable )
+ {
+ m_pCoinDrawable->GetBoundingSphere( &mCoinBounding );
+ }
+ else
+ {
+ mCoinBounding.centre.Set( 0.0f, 0.0f, 0.0f );
+ mCoinBounding.radius = 0.0f;
+ }
+}
+
+/*=============================================================================
+Check if a coin is within range to do the collection animation to the player.
+If it is we set the state to Collecting.
+=============================================================================*/
+void CoinManager::CheckCollection( ActiveCoin& Coin, const rmt::Vector& AvatarPos, float RangeMultiplier )
+{
+ float x = rmt::Abs( Coin.Position.x - AvatarPos.x );
+ float z = rmt::Abs( Coin.Position.z - AvatarPos.z );
+ float y = rmt::Abs( Coin.Position.y - AvatarPos.y );
+ // Note, no multipler for Y.
+ if( ( x < ( RANGE * RangeMultiplier ) ) && ( z < ( RANGE * RangeMultiplier ) ) && ( y < RANGE ) && ( x + z < ( INNER_RANGE * RangeMultiplier ) ) )
+ {
+ if( Coin.State == CS_RestingIndefinitely )
+ {
+ GetPersistentWorldManager()->OnObjectBreak( Coin.PersistentObjectID );
+ }
+ Coin.State = CS_Collecting;
+ Coin.Age = 0.0f;
+ }
+}
+
+/*=============================================================================
+Player has picked up a coin. This will increase the player's bank value, fire
+a coin collected event so that we can play a sound
+=============================================================================*/
+void CoinManager::CollectCoins( int Count )
+{
+ AdjustBankValue( Count );
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+ GetEventManager()->TriggerEvent( EVENT_COLLECTED_COINS, reinterpret_cast<void*>( Count ) );
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+}
+
+/*=============================================================================
+Player loses coins for whatever reason. We play a sound and HUD animation.
+If Position isn't null then we respawn the coins at the specified location.
+=============================================================================*/
+void CoinManager::LoseCoins( int Count, const rmt::Vector* Position )
+{
+ int count = rmt::Min( Count, GetBankValue() );
+ rAssert( count >= 0 );
+ AdjustBankValue( -count );
+ if( Position )
+ {
+ // Because we are losing the coins when we are shot (or car explodes) the position tends to be lower
+ //then for a crate. So we raise the position a tiny bit so that the coin's travel is the same as
+ //the designers tune for when kicking the crates around.
+ SpawnCoins( count, rmt::Vector(Position->x, Position->y + 0.25f, Position->z), Position->y, 0, true );
+ }
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+ GetEventManager()->TriggerEvent( EVENT_LOST_COINS, reinterpret_cast<void*>( count ) );
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+}
+
+int
+CoinManager::GetBankValue() const
+{
+ RenderEnums::LevelEnum currentLevel = GetGameplayManager()->GetCurrentLevelIndex();
+ int bankValue = GetCharacterSheetManager()->GetNumberOfTokens( currentLevel );
+
+ return bankValue;
+}
+
+/*=============================================================================
+Modify the player's bank value without any special effects.
+=============================================================================*/
+void CoinManager::AdjustBankValue( int DeltaCoins )
+{
+/*
+ mBankValue += DeltaCoins;
+ mBankValue = rmt::Max( mBankValue, 0 );
+*/
+ // tell the character sheet manager
+ //
+ GetCharacterSheetManager()->AddTokens( GetGameplayManager()->GetCurrentLevelIndex(),
+ DeltaCoins );
+
+ if( DeltaCoins > 0 )
+ {
+ GetEventManager()->TriggerEvent( EVENT_COLLECTED_COINS );
+ }
+ else if( DeltaCoins < 0 )
+ {
+ GetEventManager()->TriggerEvent( EVENT_LOST_COINS );
+ }
+}
+
+/*
+/*=============================================================================
+Force bank to a specific value. No special effects.
+=============================================================================/
+void CoinManager::SetBankValue( int Coins )
+{
+ rAssert( Coins >= 0 );
+ mBankValue = Coins;
+
+ // update character sheet
+ //
+ GetCharacterSheetManager()->SetNumberOfTokens( GetGameplayManager()->GetCurrentLevelIndex(),
+ mBankValue );
+}
+*/
+
+/*=============================================================================
+World coins just sit around until their zone unloads. They take up space in the
+active coin array so don't use them lightly.
+=============================================================================*/
+void CoinManager::AddWorldCoin( const rmt::Vector& Position, tUID Sector )
+{
+ if( !mActiveCoins )
+ {
+ return;
+ }
+
+ short persistentID = GetPersistentWorldManager()->GetPersistentObjectID( Sector );
+ if( persistentID < -1 )
+ {
+ return;
+ }
+
+ ActiveCoin* c = GetInactiveCoin();
+ if( !c )
+ {
+ return;
+ }
+
+ c->Position = Position;
+ c->State = CS_RestingIndefinitely;
+ c->Age = Sparkle::sRandom.Float() * rmt::PI;
+ c->Sector = Sector;
+ c->PersistentObjectID = persistentID;
+ ++mNumActiveCoins;
+#ifndef RAD_RELEASE
+ dbg_MaxCoins = rmt::Max( (int)mNumActiveCoins, dbg_MaxCoins );
+#endif
+}
+
+/*=============================================================================
+Remove any coins resting in the world which are in the sector which was just
+unloaded. Only remove the coins which are in CS_RestingIndefinite state since
+others could be spawning or collecting, etc (that's kind of unlikely however).
+=============================================================================*/
+void CoinManager::RemoveWorldCoins( tUID Sector )
+{
+ for( int i = 0; i < NUM_COINS; ++i )
+ {
+ ActiveCoin& c = mActiveCoins[ i ];
+ if( ( c.State == CS_RestingIndefinitely ) && ( c.Sector == Sector ) )
+ {
+ c.State = CS_Inactive;
+ --mNumActiveCoins;
+ }
+ }
+}
+
+/*=============================================================================
+Update the animation for the coins when they are collected.
+=============================================================================*/
+void CoinManager::UpdateCollecting( ActiveCoin& Coin, const rmt::Vector& AvatarPos )
+{
+ if( Coin.Age > ( COLLECT_TIME * 0.4f ) )
+ {
+ Coin.State = CS_Collected;
+ Coin.Age = 0.0f;
+ Coin.HeadingCos = 1;
+ CollectCoins( 1 );
+ }
+ else
+ {
+ rmt::Vector diff;
+ diff.Sub( AvatarPos, Coin.Position );
+ diff.Scale( Coin.Age * I_COLLECT_TIME );
+ float pop = ( COLLECT_TIME - Coin.Age ) * I_COLLECT_TIME;
+ diff.y += pop * POP_SCALE;
+ Coin.Position.Add( diff );
+ --DoSparkle;
+ if( DoSparkle < 0 )
+ {
+ DoSparkle = Sparkle_Rate;
+ Sparkle_Rate ^= SPARKLE_RATE_TOGGLE;
+ rmt::Vector vel;
+ vel.Scale(FRAME_RATIO * 0.1f, Coin.Velocity);
+ vel.y += SPARKLE_FLOAT;
+ GetSparkleManager()->AddSparkle( Coin.Position, 0.5f, 1.0f, vel, Sparkle::SE_Trail );
+ }
+ }
+}
+
+/*=============================================================================
+Update the spawning animation for the coins.
+=============================================================================*/
+void CoinManager::UpdateSpawning(ActiveCoin& Coin, const rmt::Vector& AvatarPos, float DeltaSeconds)
+{
+ rmt::Vector vel;
+ vel.Scale(DeltaSeconds, Coin.Velocity);
+ Coin.Velocity.Scale( (float)pow(COIN_DRAG, DeltaSeconds) );
+ Coin.Velocity.y -= GRAVITY * DeltaSeconds;
+ Coin.Position.Add( vel );
+ // Use this if we want faster (but less accurate) coins.
+ /*
+ Coin.Velocity.Scale( COIN_DRAG );
+ Coin.Velocity.y -= GRAVITY;
+ Coin.Position.Add( Coin.Velocity );
+ */
+ if( Coin.State == CS_InitialSpawning )
+ {
+ --DoSparkle;
+ if( DoSparkle < 0 )
+ {
+ DoSparkle = Sparkle_Rate;
+ Sparkle_Rate ^= SPARKLE_RATE_TOGGLE;
+ GetSparkleManager()->AddSparkle( Coin.Position, 0.5f, 1.0f, rmt::Vector( Coin.Velocity.x * 0.1f, ( Coin.Velocity.y * 0.1f ) + SPARKLE_FLOAT, Coin.Velocity.z * 0.1f ), Sparkle::SE_Trail );
+ }
+ }
+ if( Coin.Position.y < Coin.Ground )
+ {
+ // Bounce.
+ Coin.Velocity.y = rmt::Abs( Coin.Velocity.y * BOUNCE_DAMPENING );
+ if( Coin.State == CS_SpawnToCollect )
+ {
+ Coin.State = CS_Collecting;
+ Coin.Age = 0.0f;
+ }
+ else
+ {
+ Coin.State = CS_Spawning;
+ if( Coin.Velocity.y < 0.01f )
+ {
+ Coin.State = CS_Resting;
+ }
+ }
+ Coin.Position.y = Coin.Ground;
+ }
+}
+
+void CoinManager::AddGlint( ActiveCoin& Coin, const rmt::Vector& ToCamera, const rmt::Vector& CoinAxis, const rmt::Vector& CameraRight )
+{
+ rmt::Vector pos;
+ pos.Add( Coin.Position, mCoinBounding.centre );
+ float dot = ToCamera.DotProduct( CoinAxis );
+ float radius = ( dot < 0.0f ) ? -mCoinBounding.radius : mCoinBounding.radius;
+ pos.ScaleAdd( radius, CoinAxis );
+ rmt::Vector left;
+ left.Scale( -0.0025f, CameraRight );
+ GetSparkleManager()->AddSparkle( pos, 1.0f, 0.25f, left, Sparkle::SE_Glint );
+}
+
+/*=============================================================================
+Animation of the coin zipping up to the HUD counter.
+=============================================================================*/
+void CoinManager::UpdateHUDFlying( ActiveCoin& Coin )
+{
+ // Do fly to HUD animation.
+ if(((Coin.State == CS_FlyingToHUD) && (Coin.Age > FLYING_TIME )) ||
+ ((Coin.State == CS_FlyingFromHUD) && (Coin.Age > 0.5f)))
+ {
+ Coin.State = CS_Inactive;
+ --mNumActiveCoins;
+ --mNumHUDFlying;
+ }
+}
+
+void CoinManager::ClearHUDCoins(void)
+{
+ if(mNumHUDFlying == 0)
+ {
+ return;
+ }
+ for(int i = 0; i < NUM_COINS; ++i)
+ {
+ ActiveCoin& c = mActiveCoins[i];
+ if((c.State == CS_FlyingToHUD) || (c.State == CS_FlyingFromHUD))
+ {
+ c.State = CS_Inactive;
+ --mNumActiveCoins;
+ --mNumHUDFlying;
+ }
+ }
+}
+
+/*=============================================================================
+Render pass called form within the opaque rendering stage of world render. It
+does a simple distance and camera culling check to determine which coins to
+draw. We don't add the coins to the DSG since the overhead of moving/adding/
+removing them is too much. Also, the DSG nodes are hard coded to only allow a
+certain number of extra entities over the amount preallocated in the pipeline.
+It's very possible that we'll end up with more then then that value.
+=============================================================================*/
+void CoinManager::Render( void )
+{
+ if( ( mNumActiveCoins - mNumHUDFlying ) > 0 )
+ {
+ tCamera* camera = p3d::context->GetView()->GetCamera();
+ rAssert( camera );
+ const rmt::Matrix& camTrans = camera->GetCameraToWorldMatrix();
+
+ for( int i = 0; i < NUM_COINS; ++i )
+ {
+ ActiveCoin& c = mActiveCoins[ i ];
+ if( c.State != CS_Inactive )
+ {
+ if( c.State == CS_Collected )
+ {
+ camera->WorldToView( c.Position, &c.Position );
+ c.Velocity.Sub( rmt::Vector( COUNTER_X, COUNTER_Y, 0.0f ), c.Position );
+ c.State = CS_FlyingToHUD;
+ ++mNumHUDFlying;
+ continue;
+ }
+ if( !m_pCoinDrawable )
+ {
+ continue;
+ }
+ // Visiblity test.
+ rmt::Vector diff;
+ diff.Sub( camTrans.Row( 3 ), c.Position );
+ float camDirDot = diff.DotProduct( camTrans.Row( 2 ) );
+ // Note that the diff vector is towards the camera so the camDirDot will be negative when the camera is facing
+ //the object. So if it's positive then we can assume it's behind the camera.
+ if( camDirDot > 0.0f )
+ {
+ continue;
+ }
+ // Distance check.
+ float distance = diff.Magnitude();
+ if( distance > MAX_VISIBLITY )
+ {
+ continue;
+ }
+#ifndef RAD_RELEASE
+ ++dbg_CoinsDrawn;
+#endif
+ if( ( GlintDelay <= 0 ) && ( DoGlint == i ) )
+ {
+ if( c.State != CS_InitialSpawning && c.State != CS_Collecting && c.State != CS_Collected )
+ {
+ if(GetGameFlow()->GetCurrentContext() != CONTEXT_PAUSE)
+ {
+ AddGlint( c, diff, rmt::Vector( c.HeadingCos, 0.0f, -c.HeadingSin ), camTrans.Row( 0 ) );
+ }
+ }
+ }
+ // Down the columns, across the rows.
+ rmt::Matrix transform( c.HeadingCos, 0.0f, -c.HeadingSin, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ c.HeadingSin, 0.0f, c.HeadingCos, 0.0f,
+ c.Position.x, c.Position.y, c.Position.z, 1.0f );
+
+ p3d::pddi->PushMultMatrix( PDDI_MATRIX_MODELVIEW, &transform );
+ m_pCoinDrawable->Display();
+ p3d::pddi->PopMatrix( PDDI_MATRIX_MODELVIEW );
+ }
+ }
+ }
+ --GlintDelay;
+ if( GlintDelay < 0 )
+ {
+ GlintDelay = Glint_Rate;
+ DoGlint = Sparkle::sRandom.IntRanged( 0, NUM_COINS );
+ }
+}
+
+void CoinManager::SetHUDCoin( int X, int Y, bool IsShowing )
+{
+ const static float ScreenWidthRatio = 1.0f / 640.0f;
+ const static float ScreenHeightRatio = 1.0f / 480.0f;
+ const static float ScreenAspect = 4.0f / 3.0f;
+ if( IsShowing )
+ {
+ mHUDCoinX = ( ( ( (float)X * ScreenWidthRatio ) ) - 0.5f ) * ScreenAspect * ScreenAspect;
+ mHUDCoinY = ( ( ( (float)Y * ScreenHeightRatio ) ) - 0.5f ) * ScreenAspect;
+ }
+ else
+ {
+ mHUDCoinX = -10.f;
+ }
+}
+
+void CoinManager::OnCheatEntered( eCheatID cheatID, bool isEnabled )
+{
+ if( cheatID == CHEAT_ID_EXTRA_COINS )
+ {
+ this->AdjustBankValue( +100 ); // add 100 coins to bank value
+ }
+}
+
+/*=============================================================================
+Rendering pass for the HUD. Used for when the coin flies up to the HUD counter.
+We set the state once and do all the rendering.
+=============================================================================*/
+void CoinManager::HUDRender( void )
+{
+ if( !m_pCoinDrawable )
+ {
+ return;
+ }
+ bool renderHUDCoin = false;
+ if( mHUDCoinX > -1.0f && mHUDCoinX < 1.0f && mHUDCoinY > -1.0f && mHUDCoinY < 1.0f )
+ {
+ renderHUDCoin = true;
+ }
+ if( ( mNumHUDFlying > 0 ) || renderHUDCoin )
+ {
+ p3d::stack->Push();
+ bool oldZWrite = p3d::pddi->GetZWrite();
+ pddiCompareMode oldZComp = p3d::pddi->GetZCompare();
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( false );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( PDDI_COMPARE_ALWAYS );
+ }
+ p3d::pddi->SetProjectionMode( PDDI_PROJECTION_ORTHOGRAPHIC );
+ pddiColour oldAmbient = p3d::pddi->GetAmbientLight();
+ p3d::pddi->SetAmbientLight( pddiColour( 255, 255, 255 ) );
+
+ rmt::Vector pos;
+ if( renderHUDCoin )
+ {
+ p3d::stack->LoadIdentity();
+ p3d::stack->Scale( 0.1f, 0.1f, 1.0f );
+ float coinSin, coinCos;
+ rmt::SinCos( ( mHUDCoinAngle * SPIN_MULTIPLIER ), &coinSin, &coinCos );
+ rmt::Matrix transform( coinCos, 0.0f, -coinSin, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ coinSin, 0.0f, coinCos, 0.0f,
+ mHUDCoinX * 5.0f, mHUDCoinY * 5.0f, 5.0f, 1.0f );
+#ifndef RAD_RELEASE
+ ++dbg_CoinsDrawn;
+#endif
+ if( mHUDSparkle <= 0 )
+ {
+ rmt::Vector pos;
+ pos.ScaleAdd( rmt::Vector( mHUDCoinX, mHUDCoinY, 5.0f ), 0.2f, mCoinBounding.centre );
+ float radius = 0.2f * ( ( coinCos < 0.0f ) ? mCoinBounding.radius : -mCoinBounding.radius );
+ pos.Add( rmt::Vector( coinCos * radius, 0.0f, coinSin * radius ) );
+ GetSparkleManager()->AddSparkle( pos, 1.0f, 0.25f, rmt::Vector( 0.0f, 0.0f, 0.0f ), Sparkle::SE_HUDGlint );
+ mHUDSparkle = Sparkle::sRandom.IntRanged( HUD_SPARKLE_RATE, HUD_SPARKLE_RATE * 3 );
+ }
+ --mHUDSparkle;
+ p3d::pddi->PushMultMatrix( PDDI_MATRIX_MODELVIEW, &transform );
+ m_pCoinDrawable->Display();
+ p3d::pddi->PopMatrix( PDDI_MATRIX_MODELVIEW );
+ }
+ p3d::stack->LoadIdentity();
+ p3d::stack->Scale( 0.05f, 0.05f, 1.0f);
+
+ for( int i = 0; i < NUM_COINS; ++i )
+ {
+ ActiveCoin& c = mActiveCoins[ i ];
+ if((c.State == CS_FlyingToHUD) || (c.State == CS_FlyingFromHUD))
+ {
+ if(c.State == CS_FlyingToHUD)
+ {
+ float a = c.Age * I_FLYING_TIME;
+ pos.ScaleAdd( c.Position, a * a, c.Velocity );
+ }
+ else
+ {
+ pos.ScaleAdd(c.Position, c.Age * I_FLYING_TIME, c.Velocity);
+ }
+ // Down the columns, across the rows.
+ rmt::Matrix transform( 1.0f, 0.0f, 0.0f, 0.0f,
+ 0.0f, 1.0f, 0.0f, 0.0f,
+ 0.0f, 0.0f, 1.0f, 0.0f,
+ pos.x * 10.0f, pos.y * 10.0f, 5.0f, 1.0f );
+ #ifndef RAD_RELEASE
+ ++dbg_CoinsDrawn;
+ #endif
+ p3d::pddi->PushMultMatrix( PDDI_MATRIX_MODELVIEW, &transform );
+ m_pCoinDrawable->Display();
+ p3d::pddi->PopMatrix( PDDI_MATRIX_MODELVIEW );
+ }
+ }
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_PERSPECTIVE);
+ p3d::pddi->SetAmbientLight( oldAmbient );
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( true );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( oldZComp );
+ }
+ p3d::stack->Pop();
+ }
+} \ No newline at end of file
diff --git a/game/code/worldsim/coins/coinmanager.h b/game/code/worldsim/coins/coinmanager.h
new file mode 100644
index 0000000..28faee2
--- /dev/null
+++ b/game/code/worldsim/coins/coinmanager.h
@@ -0,0 +1,168 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: coinmanager.h
+//
+// Description: Coin manager looks after everything to do with the collectable coins
+//
+// History: 29/01/2003 + Created -- James Harrison
+//
+//=============================================================================
+
+#ifndef COINMANAGER_H
+#define COINMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/vector.hpp>
+#include <radmath/geometry.hpp>
+#include <events/eventlistener.h>
+#include <constants/breakablesenum.h>
+#include <cheats/cheatinputsystem.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class tDrawable;
+class tTexture;
+
+//=============================================================================
+//
+// Synopsis: Creating and placing coins in the world, picking up coins,
+// losing coins,
+//
+//=============================================================================
+class CoinManager : public EventListener,
+ public ICheatEnteredCallback
+{
+public:
+ CoinManager();
+ ~CoinManager();
+
+ static CoinManager* GetInstance( void );
+ static CoinManager* CreateInstance( void );
+ static void DestroyInstance( void );
+
+ void Init( void );
+ void Destroy( void );
+ void SetCoinDrawable( tDrawable* Drawable );
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void Update( int ElapsedMS ); // Elapsed milliseconds.
+ void Render( void ); // Draw coins. Do simple frustum and distance check on them.
+ void HUDRender( void ); // Draw any HUD animations.
+
+ int GetBankValue( void ) const; // How much is in the bank.
+ void AdjustBankValue( int DeltaCoins ); // Adjust the bank value without any visual SFX.
+ void CollectCoins( int Count ); // Collect coins with sound SFX.
+ void LoseCoins( int Count, const rmt::Vector* Position = 0 ); // decrease bank value with visual SFX.
+ // spawn coins from this position. If in car, the coin actually just go to the player, unless the Force
+ //parameter is true.
+ void SpawnCoins( int Count, const rmt::Vector& Position, const rmt::Vector* Direction = 0, bool Force = false );
+ void SpawnCoins( int Count, const rmt::Vector& Position, float GroundY, const rmt::Vector* Direction = 0, bool Force = false ); // spawn coins and force specific ground value.
+ void SpawnInstantCoins(int Count, const rmt::Vector& Position);
+
+ void AddWorldCoin( const rmt::Vector& Position, tUID Sector ); // Place a coin to sit happily in a zone until the zone unloads.
+
+ void SetHUDCoin( int X, int Y, bool IsShowing = true );
+ void AddFlyDownCoin(void); // Create an animation of a single coin zipping down from the HUD counter.
+ void ClearHUDCoins(void); // Remove all HUD coins.
+
+ bool DrawAfterGui() const { return mDrawAfterGui;}
+ void SetDrawAfterGui(bool d) { mDrawAfterGui = d;}
+
+ enum eCoinState
+ {
+ CS_Inactive,
+ CS_InitialSpawning, // Coins aren't collectable during this time.
+ CS_SpawnToCollect, // Coins spawn and then are collected on first bounce.
+ CS_Spawning, // Still moving, but can be collected.
+ CS_Resting, // Sitting happily.
+ CS_RestingIndefinitely, // Sitting happily in the world. I doesn't decay.
+ CS_Decaying, // Spinning away into nothing.
+ CS_Collecting, // Reverse spawning. Attracted to the player.
+ CS_Collected, // Special state held for one frame before the coin heads up into the HUD.
+ CS_FlyingToHUD, // Coin is doing the little fly up animation.
+ CS_FlyingFromHUD, // Coin is doing the little fly down animation.
+ CS_NUM_STATES
+ };
+
+ tDrawable* GetCoinDrawable( void ) const { return m_pCoinDrawable; }
+ const rmt::Sphere& GetCoinBounding( void ) const { return mCoinBounding; }
+
+ virtual void OnCheatEntered( eCheatID cheatID, bool isEnabled );
+
+protected:
+ //Prevent wasteful constructor creation.
+ CoinManager( const CoinManager& That );
+ CoinManager& operator=( const CoinManager& That );
+
+ void OnVehicleDestroyed( Vehicle* DestroyedVehicle );
+ struct ActiveCoin;
+ void SpawnCoin( ActiveCoin& Coin, const rmt::Vector& Start, float Ground, const rmt::Vector* Direction = 0, bool InstaCollect = false );
+ void CheckCollection( ActiveCoin& Coin, const rmt::Vector& AvatarPos, float RangeMultiplier = 1.0f );
+ void RemoveWorldCoins( tUID Sector );
+ void AddGlint( ActiveCoin& Coin, const rmt::Vector& ToCamera, const rmt::Vector& CoinAxis, const rmt::Vector& CameraRight );
+
+ void UpdateCollecting( ActiveCoin& Coin, const rmt::Vector& AvatarPos );
+ void UpdateSpawning(ActiveCoin& Coin, const rmt::Vector& AvatarPos, float DeltaSeconds);
+ void UpdateHUDFlying( ActiveCoin& Coin );
+
+ static CoinManager* spCoinManager;
+
+ tDrawable* m_pCoinDrawable;
+ rmt::Sphere mCoinBounding;
+
+ struct ActiveCoin
+ {
+ ActiveCoin() : State( CS_Inactive ) {};
+ union
+ {
+#ifdef RAD_GAMECUBE
+ rmt::Vector Velocity;
+ tUID Sector;
+#else
+ struct
+ {
+ rmt::Vector Velocity;
+ };
+
+ struct
+ {
+ tUID Sector;
+ short PersistentObjectID;
+ };
+#endif
+ };
+#ifdef RAD_GAMECUBE
+ short PersistentObjectID;
+#endif
+ rmt::Vector Position;
+ float HeadingCos;
+ float HeadingSin;
+ float Age;
+ float Ground;
+ eCoinState State;
+ };
+
+ ActiveCoin* GetInactiveCoin( void );
+
+ ActiveCoin* mActiveCoins;
+ short mNumActiveCoins;
+ short mNextInactiveCoin;
+ short mNumHUDFlying; // So we can early out of the HUD render.
+ short mHUDSparkle;
+
+ float mHUDCoinX;
+ float mHUDCoinY;
+ float mHUDCoinAngle;
+
+ bool mDrawAfterGui;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline CoinManager* GetCoinManager() { return( CoinManager::GetInstance() ); }
+
+#endif //COINMANAGER_H
diff --git a/game/code/worldsim/coins/sparkle.cpp b/game/code/worldsim/coins/sparkle.cpp
new file mode 100644
index 0000000..77004b8
--- /dev/null
+++ b/game/code/worldsim/coins/sparkle.cpp
@@ -0,0 +1,1222 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: Sparkle.cpp
+//
+// Description: Implementation of class Sparkle
+//
+// History: 18/2/2003 + Created -- James Harrison
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <radmath/matrix.hpp>
+#include <p3d/entity.hpp>
+#include <p3d/view.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/texture.hpp>
+#include <pddi/pddi.hpp>
+#ifndef RAD_RELEASE
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#endif
+
+//========================================
+// Project Includes
+//========================================
+#include <contexts/bootupcontext.h>
+#include <worldsim/coins/sparkle.h>
+#include <memory/srrmemory.h>
+#include <main/game.h>
+#include <camera/supercam.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercammanager.h>
+#include <gameflow/gameflow.h>
+#include <mission/gameplaymanager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+#if defined( RAD_XBOX ) || defined( RAD_WIN32 )
+ #ifdef RAD_RELEASE
+ static const float FRAME_RATE = 60.0f;
+ #elif RAD_TUNE
+ static const float FRAME_RATE = 30.0f;
+ #else
+ static const float FRAME_RATE = 15.0f;
+ #endif
+ static const float FRAME_RATIO = 1.0f / FRAME_RATE;
+#endif
+
+static const float DRIFT_DRAG = 0.99f; // How much horizontal inertia is lost per frame.
+static const int NUM_SPARK_PARTICLES = 10;
+static const float SPARK_RADIUS = 1.8f;
+static const float SPARK_SIZE = 0.25f;
+static const float SPARK_VERTICAL_SCALE = 0.25f;
+static const float SPARK_DURATION = 0.3f;
+static const float SPARK_DURATION_RATIO = 1.0f / SPARK_DURATION;
+static const float SPARK_VELOCITY_TO_SIZE_RATIO = 1.0f / ( SPARK_RADIUS * SPARK_DURATION_RATIO * FRAME_RATIO );
+static const float DASH_SIZE = 0.2f;
+static const float DASH_SIZE_INCREASE = 0.2f;
+static const float DASH_DURATION_RATIO = 2.0f;
+static const float LANDING_SIZE = 0.5f;
+static const float LANDING_NUM = 16;
+static const float LANDING_DURATION_RATIO = 1.5f;
+static const int NUM_PAINT_CHIPS = 10;
+static const float PAINT_CHIP_SIZE = 0.05f;
+static const float SHOCK_RING_RADIUS = 3.0f;
+static const float SHOCK_RING_SPEED = 1.75f;
+static const int NUM_STAR_PARTICLES = 5;
+static const float STAR_DURATION_RATIO = 1.0f / 0.5f;
+static const float STAR_RADIUS = 10.0f;
+static const float STAR_SIZE = 0.25f;
+static const float STAR_DUST_RADIUS = 2.0f;
+static const float STAR_DUST_SIZE = 1.0f;
+static const float STAR_DUST_DURATION_RATIO = 1.0f;
+static const int NUM_BOTTOMOUT_SPARKS = 30;
+static const float BOTTOMOUT_RADIUS = 2.5f;
+static const int NUM_SMOKE_PARTICLES = 2; // Maximum number of particles per call for the smoke.
+static const float SMOKE_SIZE = 0.3f;
+static const int NUM_GAG_SPARKLES = 1;
+static const float GAG_SPARKLE_ELEVATION = 1.0f;
+static const float GAG_SPARKLE_EMIT_RADIUS = 0.75f;
+static const float GAG_SPARKLE_DISTANCE = 0.15f;
+static const float GAG_SPARKLE_DURATION_RATIO = 1.0f / 1.6f;
+static const float GAG_SPARKLE_SIZE = 0.45f;
+static const tColour GAG_SPARKLE_COLOUR(32, 128, 192);
+static const tColour STAR_COLOUR( 255, 240, 100, 255 );
+static const tColour STAR_DUST_COLOUR( 150, 140, 110, 128 );
+static const tColour TRAIL_COLOUR( 0xFF, 0xFF, 0x7F );
+static const tColour SPARK_COLOUR( 0xFF, 0xC6, 0x00 );
+static const tColour DASH_COLOUR( 220, 210, 180, 32 );
+static const tColour LANDING_COLOUR( 220, 210, 180, 128 );
+static const tColour RING_COLOUR( 0xFF, 0xFF, 0xFF );
+static const tColour LEAF_COLOUR( 120, 200, 100 );
+static const tColour LIGHT_SMOKE_COLOUR( 0xC0, 0xD0, 0xE0, 0x10 );
+static const tColour DARK_SMOKE_COLOUR( 0x40, 0x40, 0x40, 0xA0 );
+#ifndef RAD_RELEASE
+static int dbg_MaxSparkles = 0;
+#endif
+
+rmt::Randomizer Sparkle::sRandom( 0 ); // Set in the constructor.
+bool Sparkle::sRandomSeeded = false;
+
+Sparkle* Sparkle::spInstance = 0;
+
+Sparkle* Sparkle::CreateInstance( unsigned char NumTextures, unsigned short NumSparkles )
+{
+ rAssertMsg( spInstance == 0, "SparkleManager already created.\n" );
+ HeapManager::GetInstance()->PushHeap( GMA_PERSISTENT );
+ spInstance = new Sparkle( NumTextures, NumSparkles );
+ rAssert( spInstance );
+ HeapManager::GetInstance()->PopHeap( GMA_PERSISTENT );
+ return Sparkle::GetInstance();
+}
+
+Sparkle* Sparkle::GetInstance( void )
+{
+ rAssertMsg( spInstance != 0, "SparkleManager has not been created yet.\n" );
+ return spInstance;
+}
+
+void Sparkle::DestroyInstance( void )
+{
+ rAssertMsg( spInstance != 0, "SparkleManager has not been created.\n" );
+ delete spInstance;
+ spInstance = 0;
+}
+
+
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Sparkle::Sparkle( unsigned char NumTextures, unsigned short NumSparkles ) :
+ mActiveSparkles( 0 ),
+ mpTextures( 0 ),
+ mNumSparkles( NumSparkles ),
+ mNextInactiveSparkle( 0 ),
+ mNumActiveSparkles( 0 ),
+ mNumHUDSparkles( 0 ),
+ mNumTextures( NumTextures )
+{
+ rAssert( mNumSparkles <= 65535 ); // We're using an unsigned short for our counters!
+ rAssert( mNumTextures <= ST_NUM_TEXTURES );
+
+ if (!sRandomSeeded)
+ {
+ sRandom.Seed (Game::GetRandomSeed ());
+ sRandomSeeded = true;
+ }
+}
+
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+Sparkle::~Sparkle()
+{
+ Destroy();
+#ifndef RAD_RELEASE
+ radDbgWatchDelete( &dbg_MaxSparkles );
+#endif
+}
+
+void Sparkle::Init( void )
+{
+ #ifdef RAD_GAMECUBE
+ HeapManager::GetInstance()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapManager::GetInstance()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+ mActiveSparkles = new ActiveSparkle[ mNumSparkles ];
+ rAssert( mActiveSparkles );
+ mpTextures = new tTexture*[ mNumTextures ];
+ rAssert( mpTextures );
+ mNumActiveSparkles = new unsigned short[ mNumTextures << 1 ];
+ rAssert( mNumActiveSparkles );
+ mNumHUDSparkles = new unsigned short[ mNumTextures ];
+ rAssert( mNumHUDSparkles );
+ #ifdef RAD_GAMECUBE
+ HeapManager::GetInstance()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapManager::GetInstance()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+
+ for( int i = 0; i < mNumTextures; ++i )
+ {
+ mpTextures[ i ] = 0;
+ mNumActiveSparkles[ i ] = 0;
+ mNumActiveSparkles[ i + ST_NUM_TEXTURES ] = 0;
+ mNumHUDSparkles[ i ] = 0;
+ }
+}
+
+void Sparkle::Destroy( void )
+{
+ if( mpTextures )
+ {
+ for( int i = 0; i < mNumTextures; ++i )
+ {
+ tRefCounted::Release( mpTextures[ i ] );
+ }
+ delete [] mpTextures;
+ mpTextures = 0;
+ }
+ delete [] mNumActiveSparkles;
+ mNumActiveSparkles = 0;
+ delete [] mNumHUDSparkles;
+ mNumHUDSparkles = 0;
+ delete [] mActiveSparkles;
+ mActiveSparkles = 0;
+}
+
+/*=============================================================================
+Set the texture the sparkles will be drawn with. Perhaps in the future we'll
+have some method of doing multiple textures.
+=============================================================================*/
+void Sparkle::SetTexture( unsigned char Index, tTexture* Texture )
+{
+ rAssert( Index < mNumTextures );
+ tRefCounted::Assign( mpTextures[ Index ], Texture );
+}
+
+/*=============================================================================
+Find an unused sparkle in our pre-allocated array of sparkles.
+=============================================================================*/
+Sparkle::ActiveSparkle* Sparkle::GetInactiveSparkle( void )
+{
+ for( int i = 0; i < mNumSparkles; ++i )
+ {
+ ActiveSparkle* s = &(mActiveSparkles[ mNextInactiveSparkle ]);
+ mNextInactiveSparkle = ( mNextInactiveSparkle + 1 ) % mNumSparkles;
+ if( !s->Active )
+ {
+ s->Active = 1;
+ s->HUD = 0;
+ return s;
+ }
+ }
+#ifndef RAD_RELEASE
+ // rTunePrintf( "WARNING: Ran out of sparkles!" );
+#endif
+ return 0;
+}
+
+/*=============================================================================
+Add a sparkle to the world. This is used for collection/spawn trails.
+=============================================================================*/
+void Sparkle::AddSparkle( const rmt::Vector& Position, float Size, float Duration, const rmt::Vector& Velocity, eSparkleEffect Effect )
+{
+ rAssert( Duration > 0.0f ); // No infinite druation sparkles.
+ if( Duration <= 0.0f )
+ {
+ return;
+ }
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Texture = ST_Sparkle;
+ if( Effect == SE_Trail )
+ {
+ s->Align = SA_None;
+ s->Scale = SS_LinearDown;
+ s->ColourAnim = SC_LinearRedDownAtHalf;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Colour = TRAIL_COLOUR;
+ }
+ else if( Effect == SE_Glint )
+ {
+ s->Align = SA_None;
+ s->Scale = SS_LinearDown;
+ s->ColourAnim = SC_LinearDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Colour = TRAIL_COLOUR;
+ }
+ else if( Effect == SE_Vanish )
+ {
+ s->Align = SA_None;
+ s->Scale = SS_BulgeDown;
+ s->ColourAnim = SC_LinearRedDownAtHalf;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Colour = TRAIL_COLOUR;
+ }
+ else
+ {
+ s->HUD = 1;
+ s->Align = SA_None;
+ s->Scale = SS_LinearDown;
+ s->ColourAnim = SC_LinearDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Colour = TRAIL_COLOUR;
+ ++( mNumHUDSparkles[ s->Texture ] );
+ }
+ s->Size = Size;
+ s->DurationRatio = 1.0f / Duration;
+ s->Life = 1.0f;
+ s->Position = Position;
+ s->Velocity = Velocity;
+ ++( mNumActiveSparkles[ s->Texture ] );
+#ifndef RAD_RELEASE
+// dbg_MaxSparkles = rmt::Max( mNumActiveSparkles, dbg_MaxSparkles );
+#endif
+}
+
+/*=============================================================================
+Add the blue gag sparkle to the world. Size is used to scale the particles down
+for indoors. A 1 is full sized, .5 is half, etc. The Strength value is used to
+fade the sparkle out for distance culling.
+Okay, so the problem is we don't want sparkles shooting out every frame for all
+those gags. It's too many particles and it looks bad. We'd either have to store
+a state with each gag, some sort of counter, or store a state here to only
+activate every few calls. The problem with storing a state with each gag is an
+extra byte for anything using this.
+Currently only gags uses the sparkles, but it could be others (doorbells, NPCs
+you talk to). The problem with only activating every few calls, is if there is
+exactly the number of gags as the number we wait, then only one gag gets a
+sparkle. I perfer to hold the state here anyway, since it's less memory. So we
+have two solutions, randomize the amount we skip before we spit out another
+sparkle, or hold the state of the objects here. If we held the state here we
+could create a hash table and then group the calling objects by their hash
+value (we'd have to pass in a this pointer so we know who called us). So if we
+took bits 4 to 7 (i.e. the high order bits of the first byte of the address
+(remember since the address is aligned we don't want to take the first bits of
+the first bytes) we'd end up with a number between 0 and 15 which we could use
+as an index into some bits we are using to hold a counter controling when to
+spit out a sparkle. I think I'll try something along those lines.
+=============================================================================*/
+void Sparkle::AddGagSparkle(const rmt::Vector& Position, float Size, float Strength, unsigned int Caller)
+{
+ static unsigned int actionFlag = 0;
+ if(GetGameFlow()->GetCurrentContext() == CONTEXT_PAUSE)
+ {
+ return;
+ }
+
+ if((Size <= 0.0f) || (Strength <= 0.0f) || (int(255 * Strength) <= 0))
+ {
+ return;
+ }
+ int bitMask = 1 << ((Caller >> 4) & 31);
+ actionFlag ^= bitMask;
+ if(actionFlag & bitMask)
+ {
+ return;
+ }
+ ActiveSparkle* s = GetInactiveSparkle();
+ if(!s)
+ {
+ return;
+ }
+ s->Align = SA_None;
+ s->Scale = SS_Constant;
+ s->ColourAnim = SC_LinearDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Texture = ST_Sparkle;
+ s->Position = Position;
+ s->Position.y += GAG_SPARKLE_ELEVATION * Size;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.FloatSigned();
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Position.ScaleAdd(GAG_SPARKLE_EMIT_RADIUS * Size, s->Velocity);
+ s->Velocity.Normalize();
+ s->Velocity.Scale( GAG_SPARKLE_DISTANCE * GAG_SPARKLE_DURATION_RATIO * FRAME_RATIO * Size);
+ s->Size = GAG_SPARKLE_SIZE * Size;
+ s->Life = 1.0f + ( sRandom.FloatSigned() * 0.1f );
+ s->DurationRatio = GAG_SPARKLE_DURATION_RATIO;
+ int red = rmt::Clamp(int(GAG_SPARKLE_COLOUR.Red() * Strength), 0, 255);
+ int green = rmt::Clamp(int(GAG_SPARKLE_COLOUR.Green() * Strength), 0, 255);
+ int blue = rmt::Clamp(int(GAG_SPARKLE_COLOUR.Blue() * Strength), 0, 255);
+ s->Colour.Set(red, green, blue);
+ ++(mNumActiveSparkles[ s->Texture ]);
+}
+
+/*=============================================================================
+Add spark to the world. A spark is like a spherical explosion.
+=============================================================================*/
+void Sparkle::AddSparks( const rmt::Vector& Position, const rmt::Vector& Velocity, float Strength )
+{
+ int numParticles = int( sRandom.IntRanged( NUM_SPARK_PARTICLES, NUM_SPARK_PARTICLES * 2 ) * Strength ) + 1;
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_MotionStreak;
+ s->Scale = SS_Constant;
+ s->ColourAnim = SC_LinearDownAtHalf;
+ s->CornerFade = 0;
+ s->Motion = SM_Decelerate;
+ s->Texture = ST_Spark;
+ s->Position = Position;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.FloatSigned();
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( SPARK_RADIUS * SPARK_DURATION_RATIO );
+ s->Velocity.ScaleAdd(SPARK_DURATION_RATIO, Velocity );
+ s->Velocity.Scale( FRAME_RATIO );
+ s->Size = s->Velocity.Magnitude() * SPARK_VELOCITY_TO_SIZE_RATIO * SPARK_SIZE;
+ s->Life = 1.0f + ( sRandom.FloatSigned() * 0.1f );
+ s->DurationRatio = SPARK_DURATION_RATIO;
+ s->Colour = SPARK_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+
+/*=============================================================================
+This is basically the same as the AddSparks, except it's radial sparks at the
+position and more pronounced.
+=============================================================================*/
+void Sparkle::AddBottomOut( const rmt::Vector& Position )
+{
+ for( int i = 0; i < NUM_BOTTOMOUT_SPARKS; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_MotionStreak;
+ s->Scale = SS_Constant;
+ s->ColourAnim = SC_LinearDownAtHalf;
+ s->CornerFade = 0;
+ s->Motion = SM_Decelerate;
+ s->Texture = ST_Spark;
+ s->Position = Position;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.Float() * 0.1f;
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( BOTTOMOUT_RADIUS * SPARK_DURATION_RATIO );
+ s->Velocity.Scale( FRAME_RATIO );
+ s->Size = SPARK_SIZE;
+ s->Life = 1.0f + ( sRandom.FloatSigned() * 0.1f );
+ s->DurationRatio = SPARK_DURATION_RATIO;
+ s->Colour = SPARK_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+
+/*=============================================================================
+Add the dash cloud. Note that the velocity is the travel of the character,
+not the direction you want the dash cloud to travel.
+=============================================================================*/
+void Sparkle::AddDash( const rmt::Vector& Position, const rmt::Vector& Velocity, float Strength )
+{
+ int alpha = int(DASH_COLOUR.Alpha() * (Strength*Strength+Strength)*0.5f);
+ if(alpha <= 0)
+ {
+ return;
+ }
+ int numParticles = sRandom.IntRanged( NUM_SPARK_PARTICLES >> 2, NUM_SPARK_PARTICLES ) + 1;
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_Rotation;
+ s->Scale = SS_LinearGrow;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Texture = ST_Puff;
+ s->Position = Position;
+ s->Position.y += 0.05f;
+ s->Position.ScaleAdd(-0.05f, Velocity);
+ s->Velocity.x = -Velocity.x * sRandom.FloatSigned() * 2.0f;
+ s->Velocity.y = ( sRandom.Float() * 0.5f ) + 0.5f;
+ s->Velocity.z = -Velocity.z * sRandom.FloatSigned() * 2.0f;
+ s->Velocity.Normalize();
+ s->Velocity.Scale( 0.2f + ( 0.2f * sRandom.Float() ) );
+ s->Velocity.Scale( FRAME_RATIO );
+ s->Size = DASH_SIZE;
+ s->Life = 1.0f + ( sRandom.FloatSigned() * 0.5f );
+ s->DurationRatio = DASH_DURATION_RATIO;
+ s->Colour = DASH_COLOUR;
+ s->Colour.SetAlpha(int(DASH_COLOUR.Alpha() * (Strength*Strength+Strength)*0.5f));
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+
+/*=============================================================================
+Add the landing puff. It's a radial explosion of puffs used in the dash.
+The strength is used to control the look over a jump landing (~0.5), a jump-
+jump landing (~0.7), and a stomp (1.0).
+=============================================================================*/
+void Sparkle::AddLanding( const rmt::Vector& Position, float Strength )
+{
+ if( Strength <= 0.0f )
+ {
+ return;
+ }
+ int numParticles = int( LANDING_NUM + ( LANDING_NUM * Strength ) );
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_Rotation;
+ s->Scale = SS_LinearUp;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Texture = ST_Puff;
+ s->Position = Position;
+ s->Position.y += 0.25f * Strength;
+
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.Float() * Strength * 0.25f;
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( 1.0f + Strength );
+ s->Velocity.Scale( FRAME_RATIO );
+
+ s->Size = LANDING_SIZE * Strength;
+ s->Life = ( sRandom.Float() * 0.2f ) + Strength;
+ s->DurationRatio = LANDING_DURATION_RATIO;
+ s->Colour = DASH_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+/*=============================================================================
+Add a shockwave ring, which is basically a billboard with a texture which
+expands from the center and then disappears. Really easy. Handy for things like
+the jump-jump-stomp.
+=============================================================================*/
+void Sparkle::AddShockRing( const rmt::Vector& Position, float Strength )
+{
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_Flat;
+ s->Scale = SS_LinearUp;
+ s->ColourAnim = SC_LinearDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Texture = ST_Ring;
+ s->Position = Position;
+ s->Position.y += 0.25f;
+ s->Velocity.Set( 0.0f, 0.0f, 0.0f );
+ s->Size = SHOCK_RING_RADIUS * Strength;
+ s->Life = 1.0f;
+ s->DurationRatio = SHOCK_RING_SPEED;
+ s->Colour = RING_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+}
+
+/*=============================================================================
+Add the leaves spray when the car drives over a hedge. The animation is like a
+coins spawning.
+=============================================================================*/
+void Sparkle::AddLeaves( const rmt::Vector& Position, const rmt::Vector& Velocity, float Strength )
+{
+ int numParticles = int( sRandom.IntRanged( NUM_SPARK_PARTICLES, NUM_SPARK_PARTICLES * 2 ) * Strength ) + 1;
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_Rotation;
+ s->Scale = SS_Constant;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Gravity;
+ s->Texture = ST_Leaf;
+ s->Position = Position;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.FloatSigned();
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( SPARK_RADIUS * SPARK_DURATION_RATIO );
+ s->Velocity.ScaleAdd(SPARK_DURATION_RATIO, Velocity );
+ s->Velocity.Scale( FRAME_RATIO );
+ s->Size = s->Velocity.Magnitude() * SPARK_VELOCITY_TO_SIZE_RATIO;
+ s->Life = 1.0f + ( sRandom.FloatSigned() * 0.1f );
+ s->DurationRatio = SPARK_DURATION_RATIO;
+ s->Colour = LEAF_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+
+/*=============================================================================
+Add paint chips when upon vehicle-vehicle collision. The colour depends
+upon the colour of the vehicle getting hit.
+=============================================================================*/
+void Sparkle::AddPaintChips( const rmt::Vector& Position, const rmt::Vector& Velocity, pddiColour Colour, float Strength )
+{
+ return;
+
+ int numParticles = int( sRandom.IntRanged( NUM_PAINT_CHIPS, NUM_PAINT_CHIPS * 2 ) * Strength ) + 1;
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+
+ const float VELOCITY_DAMPENING = 0.2f;
+
+ s->Align = SA_Rotation;
+ s->Scale = SS_Constant;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Gravity;
+ s->Texture = ST_Paint;
+ s->Position = Position;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.FloatSigned();
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( VELOCITY_DAMPENING );
+
+ s->Size = PAINT_CHIP_SIZE;
+ s->Life = 1.0f + ( sRandom.FloatSigned() * 0.1f );
+ s->DurationRatio = SPARK_DURATION_RATIO;
+ const float COLOUR_DAMPENING = 0.5f;
+ int red = static_cast< int >( static_cast< float >(Colour.Red()) * COLOUR_DAMPENING );
+ int green = static_cast< int >( static_cast< float >(Colour.Green()) * COLOUR_DAMPENING );
+ int blue = static_cast< int >( static_cast< float >(Colour.Blue()) * COLOUR_DAMPENING );
+ s->Colour.Set( red, green, blue );
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+/*=============================================================================
+Explosion of stars and smoke. Useful when the car hits a static.
+=============================================================================*/
+void Sparkle::AddStars( const rmt::Vector& Position, float Strength )
+{
+ int numParticles = NUM_STAR_PARTICLES + int( NUM_STAR_PARTICLES * Strength );
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_MotionStreak;
+ s->Scale = SS_LinearDown;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Linear;
+ s->Position = Position;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.FloatSigned();
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( FRAME_RATIO * STAR_RADIUS );
+ s->DurationRatio = STAR_DURATION_RATIO;
+ s->Size = s->Velocity.Magnitude() * SPARK_VELOCITY_TO_SIZE_RATIO * SPARK_SIZE;
+ s->Life = 1.0f;
+ s->Texture = ST_Spark;
+ s->Colour = STAR_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_Rotation;
+ s->Scale = SS_LinearGrow;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 1;
+ s->Motion = SM_Linear;
+ s->Position = Position;
+ s->Velocity.x = sRandom.FloatSigned();
+ s->Velocity.y = sRandom.FloatSigned();
+ s->Velocity.z = sRandom.FloatSigned();
+ s->Velocity.Normalize();
+ s->Velocity.Scale( FRAME_RATIO * STAR_DUST_RADIUS );
+ s->DurationRatio = STAR_DUST_DURATION_RATIO;
+ s->Size = STAR_DUST_SIZE;
+ s->Life = 1.0f;
+ s->Texture = ST_Puff;
+ s->Colour = STAR_DUST_COLOUR;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+
+/*=============================================================================
+Upward spray of smoke. Used for car damage smoke.
+Transform is the position of the vehicle and the offset is the position of the
+emission of the smoke _relative_ to the vehicle. The velocity is the vehicle's
+movement per frame.
+=============================================================================*/
+void Sparkle::AddSmoke( const rmt::Matrix& Transform, const rmt::Vector& Offset, const rmt::Vector& Velocity, float Strength )
+{
+ if( Strength <= 0.0f )
+ {
+ return;
+ }
+ rmt::Vector pos;
+ Transform.Transform( Offset, &pos );
+ // Do some distance type LODing. The further from the camera, the fewer particles.
+ rmt::Vector camPos;
+ GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam()->GetPosition( &camPos );
+ camPos.Sub( pos );
+ int d = 0;
+ if( GetGameplayManager()->GetGameType() != GameplayManager::GT_SUPERSPRINT )
+ {
+ d = int( rmt::Max( rmt::Abs( camPos.x ), rmt::Abs( camPos.z ) ) * 0.02f );
+ }
+ int numParticles = int( NUM_SMOKE_PARTICLES * Strength ) + 1 - d;
+ if( numParticles <= 0 )
+ {
+ return;
+ }
+ for( int i = 0; i < numParticles; ++i )
+ {
+ ActiveSparkle* s = GetInactiveSparkle();
+ if( !s )
+ {
+ return;
+ }
+ s->Align = SA_None;
+ s->Scale = SS_LinearTriple;
+ s->ColourAnim = SC_LinearAlphaDown;
+ s->CornerFade = 0;
+ s->Motion = SM_Drift;
+ s->Position = pos;
+ s->Position.x += sRandom.FloatSigned() * 0.15f;
+ s->Position.z += sRandom.FloatSigned() * 0.15f;
+ s->Position.y += sRandom.FloatSigned() * 0.15f;
+ s->Velocity.x = sRandom.FloatSigned() * 2.0f;
+ s->Velocity.z = sRandom.FloatSigned() * 2.0f;
+ s->Velocity.y = 2.0f + ( sRandom.Float() * 2.0f );
+ s->Velocity.Normalize();
+ s->Velocity.Scale( FRAME_RATIO * ( 2.0f + ( 1.0f * Strength ) ) );
+ //s->Velocity.ScaleAdd( s->Velocity, FRAME_RATIO, Velocity );
+ s->Velocity.Add(Velocity);
+ s->DurationRatio = 0.2f / 1.0f + Strength + sRandom.Float();
+ s->Size = SMOKE_SIZE; // + ( ( Strength + sRandom.Float() ) * 0.1f );
+ s->Life = 1.0f + ( Strength * 0.5f );
+ int red = rmt::Clamp( int( ( LIGHT_SMOKE_COLOUR.Red() * ( 1.0f - Strength ) ) + ( DARK_SMOKE_COLOUR.Red() * Strength ) ), 0, 255 );
+ int green = rmt::Clamp( int( ( LIGHT_SMOKE_COLOUR.Green() * ( 1.0f - Strength ) ) + ( DARK_SMOKE_COLOUR.Green() * Strength ) ), 0, 255 );
+ int blue = rmt::Clamp( int( ( LIGHT_SMOKE_COLOUR.Blue() * ( 1.0f - Strength ) ) + ( DARK_SMOKE_COLOUR.Blue() * Strength ) ), 0, 255 );
+ int alpha = rmt::Clamp( int( ( LIGHT_SMOKE_COLOUR.Alpha() * ( 1.0f - Strength ) ) + ( DARK_SMOKE_COLOUR.Alpha() * Strength ) ), 0, 255 );
+ s->Colour.Set( red, green, blue, alpha );
+ s->Texture = ST_SortedPuff;
+ ++( mNumActiveSparkles[ s->Texture ] );
+ }
+}
+
+/*=============================================================================
+Update the position/animation of all the coins.
+=============================================================================*/
+void Sparkle::Update( unsigned int Elapsedms )
+{
+ float deltaSeconds = (float)Elapsedms * 0.001f;
+ bool HUDOnly = GetGameFlow()->GetCurrentContext() == CONTEXT_PAUSE;
+
+ for( int i = 0; i < mNumSparkles; ++i )
+ {
+ ActiveSparkle& s = mActiveSparkles[ i ];
+ if(s.Active)
+ {
+ if(HUDOnly && s.HUD != 1)
+ {
+ continue;
+ }
+ if( s.Life < 0.0f )
+ {
+ if(s.HUD)
+ {
+ s.HUD = 0;
+ --( mNumHUDSparkles[ s.Texture ] );
+ }
+ s.Active = 0;
+ --(mNumActiveSparkles[ s.Texture ]);
+ }
+ if( s.Motion == SM_Decelerate )
+ {
+ s.Position.ScaleAdd( 2.0f - s.Life, s.Velocity );
+ }
+ else if ( s.Motion == SM_Gravity )
+ {
+ s.Position.Add( s.Velocity );
+ const float GRAVITY = 0.02f;
+ s.Velocity.y -= static_cast<float>(Elapsedms) * GRAVITY;
+ }
+ else if( s.Motion == SM_Drift )
+ {
+ s.Position.Add( s.Velocity );
+ s.Velocity.x *= DRIFT_DRAG;
+ s.Velocity.z *= DRIFT_DRAG;
+ }
+ else
+ {
+ s.Position.Add( s.Velocity );
+ }
+ s.Life -= deltaSeconds * s.DurationRatio;
+ }
+ }
+}
+
+Sparkle::ActiveSparkle::ActiveSparkle() : Active( 0 ), HUD( 0 )
+{}
+
+
+/*=============================================================================
+Render pass for the world sparkles. They are aligned to face the camera.
+=============================================================================*/
+void Sparkle::Render( eSparkleRenderMode Mode )
+{
+ unsigned short numWorldSparkles[ ST_NUM_TEXTURES ];
+ int totalSparkles = 0;
+ int ti;
+ for( ti = 0; ti < mNumTextures; ++ti )
+ {
+ if( mpTextures[ ti ] )
+ {
+ if( Mode == SRM_IncludeSorted )
+ {
+ numWorldSparkles[ ti ] = mNumActiveSparkles[ ti ] + mNumActiveSparkles[ ti + ST_NUM_TEXTURES ] - mNumHUDSparkles[ ti ];
+ }
+ else if( Mode == SRM_ExcludeSorted )
+ {
+ numWorldSparkles[ ti ] = mNumActiveSparkles[ ti ] - mNumHUDSparkles[ ti ];
+ }
+ else
+ {
+ numWorldSparkles[ ti ] = mNumActiveSparkles[ ti + ST_NUM_TEXTURES ];
+ }
+ }
+ else
+ {
+ numWorldSparkles[ ti ] = 0;
+ }
+ totalSparkles += numWorldSparkles[ ti ];
+ }
+ if( totalSparkles <= 0 )
+ {
+ return;
+ }
+
+ tCamera* camera = p3d::context->GetView()->GetCamera();
+ rAssert( camera );
+ const rmt::Matrix& camTransform = camera->GetCameraToWorldMatrix();
+ rmt::Vector cameraDiagR = camTransform.Row( 1 );
+ rmt::Vector cameraDiagL = cameraDiagR;
+ cameraDiagR.Add( camTransform.Row( 0 ) );
+ cameraDiagL.ScaleAdd( -1.0f, camTransform.Row( 0 ) );
+ rmt::Vector offset;
+ offset.Scale( -0.1f, camTransform.Row( 2 ) );
+
+ bool oldZWrite = p3d::pddi->GetZWrite();
+ pddiCompareMode oldZComp = p3d::pddi->GetZCompare();
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( false );
+ }
+
+ pddiPrimStream* sprite = 0;
+
+ pddiShader* spriteShader = BootupContext::GetInstance()->GetSharedShader();
+ rAssert( spriteShader );
+
+ spriteShader->SetInt( PDDI_SP_ISLIT, 0 );
+ spriteShader->SetInt( PDDI_SP_ALPHATEST, 1 );
+ spriteShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_FLAT );
+ spriteShader->SetInt( PDDI_SP_FILTER, PDDI_FILTER_BILINEAR );
+ for( ti = 0; ti < mNumTextures; ++ti )
+ {
+ if( numWorldSparkles[ ti ] <= 0 )
+ {
+ continue;
+ }
+ spriteShader->SetTexture( PDDI_SP_BASETEX, mpTextures[ ti ]->GetTexture() );
+ if((ti == ST_Puff) || (ti == ST_Leaf))
+ {
+ spriteShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ spriteShader->SetInt( PDDI_SP_ALPHATEST, 0 );
+ }
+ else
+ {
+ spriteShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ }
+
+
+ sprite = p3d::pddi->BeginPrims( spriteShader, PDDI_PRIM_TRIANGLES, PDDI_V_CT , numWorldSparkles[ ti ] * 6 );
+ for( int i = 0; i < mNumSparkles; ++i )
+ {
+ ActiveSparkle& s = mActiveSparkles[ i ];
+ if( ( s.Active == 0 ) || ( s.HUD == 1 ) )
+ {
+ continue;
+ }
+ unsigned short matchTexture = s.Texture;
+ if( Mode == SRM_IncludeSorted )
+ {
+ matchTexture %= ST_NUM_TEXTURES;
+ }
+ else if( Mode == SRM_SortedOnly )
+ {
+ matchTexture -= ST_NUM_TEXTURES;
+ }
+ if( ( matchTexture != ti ) )
+ {
+ continue;
+ }
+ float life = rmt::Max( s.Life, 0.0f );
+ float scale;
+ // Triangle points:
+ // 2 - 3
+ // | \ |
+ // 0 - 1
+ tColour colour[ 4 ];
+ rmt::Vector corners[ 4 ];
+
+ // Calculate the scale first.
+ if( s.Scale == SS_LinearDown )
+ {
+ scale = s.Size * life;
+ }
+ else if( s.Scale == SS_BulgeDown )
+ {
+ scale = s.Size * ( ( 1.0f - ( rmt::Abs( ( life * life ) - 0.5f ) * 2.0f ) ) + life );
+ }
+ else if( s.Scale == SS_LinearGrow )
+ {
+ scale = s.Size + ( s.Size * ( 1.0f - life ) * DASH_SIZE_INCREASE );
+ }
+ else if( s.Scale == SS_LinearTriple )
+ {
+ scale = s.Size + ( s.Size * 3.0f * ( 1.0f - rmt::Min( life, 1.0f ) ) );
+ }
+ else if( s.Scale == SS_LinearUp )
+ {
+ scale = s.Size * ( 1.0f - life );
+ }
+ else
+ {
+ scale = s.Size;
+ }
+ // Now do colour.
+ if( s.ColourAnim == SC_LinearRedDownAtHalf )
+ {
+ float l = rmt::Clamp( life * 2.0f, 0.0f, 1.0f );
+ float l_half = rmt::Clamp( life - 0.5f, 0.0f, 1.0f );
+ colour[ 0 ].Set( rmt::FtoL( s.Colour.Red() * l ),
+ rmt::FtoL( s.Colour.Green() * life ),
+ rmt::FtoL( s.Colour.Blue() * l_half ) );
+ }
+ else if( s.ColourAnim == SC_LinearDownAtHalf )
+ {
+ float l = rmt::Clamp( life * 2.0f, 0.0f, 1.0f );
+ colour[ 0 ].Set( rmt::FtoL( s.Colour.Red() * l ),
+ rmt::FtoL( s.Colour.Green() * l),
+ rmt::FtoL( s.Colour.Blue() * l ) );
+ }
+ else if( s.ColourAnim == SC_LinearAlphaDown )
+ {
+ float l = rmt::Clamp( life, 0.0f, 1.0f );
+ colour[ 0 ].Set( s.Colour.Red(),
+ s.Colour.Green(),
+ s.Colour.Blue(),
+ rmt::FtoL( s.Colour.Alpha() * l ) );
+ }
+ else
+ {
+ float l = rmt::Clamp( life, 0.0f, 1.0f );
+ colour[ 0 ].Set( rmt::FtoL( s.Colour.Red() * l ),
+ rmt::FtoL( s.Colour.Green() * l ),
+ rmt::FtoL( s.Colour.Blue() * l ) );
+ }
+ if( s.CornerFade == 1 )
+ {
+ colour[ 1 ] = colour[ 0 ];
+ colour[ 1 ].SetAlpha( int( colour[ 0 ].Alpha() * 0.75f ) );
+ colour[ 2 ] = colour[ 1 ];
+ colour[ 3 ] = colour[ 0 ];
+ colour[ 3 ].SetAlpha( colour[ 0 ].Alpha() >> 1 );
+ }
+ else
+ {
+ for( int i = 1; i < 4; ++i )
+ {
+ colour[ i ] = colour[ 0 ];
+ }
+ }
+
+ // Now do corners two corners.
+ if( s.Align == SA_MotionStreak )
+ {
+ // Align to motion.
+ // Project travel onto plane of the camera.
+ rmt::Vector travel = s.Velocity;
+ travel.Normalize();
+ float dot = travel.DotProduct( camTransform.Row( 2 ) );
+ travel.ScaleAdd( -dot, camTransform.Row( 2 ) );
+ dot = 1.0f - rmt::Abs( dot );
+ corners[ 2 ].Set( -scale * dot, scale * SPARK_VERTICAL_SCALE, 0.0f );
+ corners[ 3 ].Set( scale * dot, scale * SPARK_VERTICAL_SCALE, 0.0f );
+
+ travel.Normalize();
+ // Because textures like to be wider then they are taller
+ //on the PS2, the spark textures is aligned down the X axis instead of Y.
+ //so travel becomes the right in the matrix, the camera direction becomes facing,
+ //and the up is a cross of those two.
+ rmt::Vector cross;
+ cross.CrossProduct( camTransform.Row( 2 ), travel );
+ rmt::Matrix r( travel.x, travel.y, travel.z, 0.0f,
+ cross.x, cross.y, cross.z, 0.0f,
+ camTransform.Row( 2 ).x, camTransform.Row( 2 ).y, camTransform.Row( 2 ).z, 0.0f,
+ 0.0f, 0.0f, 0.0f, 1.0f );
+ r.Transform( 2, &( corners[ 2 ] ), &( corners[ 2 ] ) );
+ }
+ else if( s.Align == SA_Rotation )
+ {
+ rmt::Vector diagR;
+ diagR.Scale( scale, cameraDiagR );
+ rmt::Vector diagL;
+ diagL.Scale( scale, cameraDiagL );
+ corners[ 2 ] = diagL;
+ corners[ 3 ] = diagR;
+ rmt::Matrix r;
+ r.Identity();
+ r.FillRotation( camTransform.Row( 2 ), rmt::PI_BY2 * life );
+ r.Transform( 2, &( corners[ 2 ] ), &( corners[ 2 ] ) );
+ }
+ else if( s.Align == SA_Flat )
+ {
+ corners[ 2 ].Set( scale, 0.0f, -scale );
+ corners[ 3 ].Set( scale, 0.0f, scale );
+ }
+ else
+ {
+ rmt::Vector diagR;
+ diagR.Scale( scale, cameraDiagR );
+ rmt::Vector diagL;
+ diagL.Scale( scale, cameraDiagL );
+ corners[ 2 ] = diagL;
+ corners[ 3 ] = diagR;
+ }
+
+ // Now mirror those two corners.
+ corners[ 0 ].Scale( -1.0f, corners[ 3 ] );
+ corners[ 1 ].Scale( -1.0f, corners[ 2 ] );
+
+ // Now move corners into world position.
+ rmt::Vector pos;
+ pos.Add( s.Position, offset );
+ for( int i = 0; i < 4; ++i )
+ {
+ corners[ i ].Add( pos );
+ }
+
+ // Now draw the triangle.
+ // bottom left tri.
+ sprite->Colour( colour[ 0 ] );
+ sprite->UV( 0.0f, 0.0f );
+ sprite->Coord( corners[ 0 ].x, corners[ 0 ].y, corners[ 0 ].z );
+ sprite->Colour( colour[ 1 ] );
+ sprite->UV( 1.0f, 0.0f );
+ sprite->Coord( corners[ 1 ].x, corners[ 1 ].y, corners[ 1 ].z );
+ sprite->Colour( colour[ 2 ] );
+ sprite->UV( 0.0f, 1.0f );
+ sprite->Coord( corners[ 2 ].x, corners[ 2 ].y, corners[ 2 ].z );
+
+ // Top right tri.
+ sprite->Colour( colour[ 3 ] );
+ sprite->UV( 1.0f, 1.0f );
+ sprite->Coord( corners[ 3 ].x, corners[ 3 ].y, corners[ 3 ].z );
+ sprite->Colour( colour[ 2 ] );
+ sprite->UV( 0.0f, 1.0f );
+ sprite->Coord( corners[ 2 ].x, corners[ 2 ].y, corners[ 2 ].z );
+ sprite->Colour( colour[ 1 ] );
+ sprite->UV( 1.0f, 0.0f );
+ sprite->Coord( corners[ 1 ].x, corners[ 1 ].y, corners[ 1 ].z );
+ }
+ p3d::pddi->EndPrims( sprite );
+ }
+ spriteShader->SetTexture( PDDI_SP_BASETEX, 0 );
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( true );
+ }
+}
+
+/*=============================================================================
+Rendering pass for the HUD. Used for when the coin flies up to the HUD counter.
+We set the state once and do all the rendering.
+=============================================================================*/
+void Sparkle::HUDRender( void )
+{
+ if( !mpTextures )
+ {
+ return;
+ }
+ unsigned char totalSparkles = 0;
+ unsigned int ti;
+ for( ti = 0; ti < mNumTextures; ++ti )
+ {
+ totalSparkles += mNumHUDSparkles[ ti ];
+ }
+ if( totalSparkles <= 0 )
+ {
+ return;
+ }
+ p3d::stack->Push();
+ bool oldZWrite = p3d::pddi->GetZWrite();
+ pddiCompareMode oldZComp = p3d::pddi->GetZCompare();
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( false );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( PDDI_COMPARE_ALWAYS );
+ }
+ p3d::stack->LoadIdentity();
+ p3d::stack->Scale( 0.1f, 0.1f, 1.0f);
+ p3d::pddi->SetProjectionMode( PDDI_PROJECTION_ORTHOGRAPHIC );
+ pddiColour oldAmbient = p3d::pddi->GetAmbientLight();
+ p3d::pddi->SetAmbientLight( pddiColour( 255, 255, 255 ) );
+
+ pddiPrimStream* sprite = 0;
+
+ pddiShader* spriteShader = BootupContext::GetInstance()->GetSharedShader();
+ rAssert( spriteShader );
+
+ spriteShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ spriteShader->SetInt( PDDI_SP_ISLIT, 0 );
+ spriteShader->SetInt( PDDI_SP_ALPHATEST, 1 );
+ spriteShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_FLAT );
+
+ for( ti = 0; ti < mNumTextures; ++ti )
+ {
+ if( mNumHUDSparkles[ ti ] <= 0 )
+ {
+ continue;
+ }
+ spriteShader->SetTexture( PDDI_SP_BASETEX, mpTextures[ ti ]->GetTexture() );
+
+ sprite = p3d::pddi->BeginPrims( spriteShader, PDDI_PRIM_TRIANGLES, PDDI_V_CT, mNumHUDSparkles[ ti ] * 6 );
+
+ for( int i = 0; i < mNumSparkles; ++i )
+ {
+ ActiveSparkle& s = mActiveSparkles[ i ];
+ if( s.Active == 0 || s.HUD == 0 || ( s.Texture != ti ) )
+ {
+ continue;
+ }
+ float life = s.Life;
+ tColour colour;
+ colour.Set( rmt::Clamp( int( 255 * life ), 0, 255 ),
+ rmt::Clamp( int( 255 * life ), 0, 255 ),
+ rmt::Clamp( int( ( 255 * life ) - 128 ), 0, 255 ) );
+ float scale;
+ scale = s.Size * life;
+ rmt::Vector pos = s.Position;
+ pos.Scale( 5.0f );
+
+ // bottom left tri.
+ sprite->Colour( colour );
+ sprite->UV( 0.0f, 0.0f );
+ sprite->Coord( pos.x - scale, pos.y - scale, pos.z );
+ sprite->Colour( colour );
+ sprite->UV( 1.0f, 0.0f );
+ sprite->Coord( pos.x + scale, pos.y - scale, pos.z );
+ sprite->Colour( colour );
+ sprite->UV( 0.0f, 1.0f );
+ sprite->Coord( pos.x - scale, pos.y + scale, pos.z );
+
+ // Top right tri.
+ sprite->Colour( colour );
+ sprite->UV( 1.0f, 1.0f );
+ sprite->Coord( pos.x + scale, pos.y + scale, pos.z );
+ sprite->Colour( colour );
+ sprite->UV( 0.0f, 1.0f );
+ sprite->Coord( pos.x - scale, pos.y + scale, pos.z );
+ sprite->Colour( colour );
+ sprite->UV( 1.0f, 0.0f );
+ sprite->Coord( pos.x + scale, pos.y - scale, pos.z );
+ }
+ p3d::pddi->EndPrims( sprite );
+ }
+ spriteShader->SetTexture( PDDI_SP_BASETEX, 0 );
+
+ p3d::pddi->SetProjectionMode(PDDI_PROJECTION_PERSPECTIVE);
+ p3d::pddi->SetAmbientLight( oldAmbient );
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( true );
+ }
+ if( oldZComp != PDDI_COMPARE_ALWAYS )
+ {
+ p3d::pddi->SetZCompare( oldZComp );
+ }
+ p3d::stack->Pop();
+} \ No newline at end of file
diff --git a/game/code/worldsim/coins/sparkle.h b/game/code/worldsim/coins/sparkle.h
new file mode 100644
index 0000000..aa977f7
--- /dev/null
+++ b/game/code/worldsim/coins/sparkle.h
@@ -0,0 +1,174 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: coinmanager.h
+//
+// Description: Sparkle effect looks after drawing sparkle effects. It bundles
+// them together under one set up call to PDDI.
+//
+// History: 29/01/2003 + Created -- James Harrison
+//
+//=============================================================================
+
+#ifndef SPARKLE_H
+#define SPARKLE_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <radmath/vector.hpp>
+#include <radmath/matrix.hpp>
+#include <radmath/random.hpp>
+#include <render/enums/renderenums.h>
+
+//========================================
+// Forward References
+//========================================
+class tDrawable;
+class tTexture;
+
+//=============================================================================
+//
+// Synopsis: Creating, place, animate sparkles in the world. Handy for coin
+// collection trails, etc.
+//
+//=============================================================================
+static const int DEFAULT_NUM_SPARKLES = 150;
+
+class Sparkle
+{
+public:
+ enum eSparkleTextures
+ {
+ ST_Sparkle, // 0
+ ST_Spark, // 1
+ ST_Puff, // 2
+ ST_Leaf, // 3
+ ST_BlueSparkle, // 4 - used for gags.
+ ST_Paint, // 5
+ ST_Ring, // 6
+ ST_NUM_TEXTURES,
+ ST_SortedSparkle,
+ ST_SortedSpark,
+ ST_SortedPuff,
+ ST_SortedLeaf,
+ ST_SortedBlueSparkle,
+ ST_SortedPaint,
+ ST_SortedRing
+ };
+
+ Sparkle( unsigned char NumTextures = 1, unsigned short NumSparkles = DEFAULT_NUM_SPARKLES );
+ ~Sparkle();
+
+ static Sparkle* GetInstance( void );
+ static Sparkle* CreateInstance( unsigned char NumTextures = ST_NUM_TEXTURES, unsigned short NumSparkles = DEFAULT_NUM_SPARKLES );
+ static void DestroyInstance( void );
+
+ void Init( void );
+ void Destroy( void );
+
+ enum eSparkleEffect
+ {
+ SE_Trail,
+ SE_Glint,
+ SE_HUDGlint,
+ SE_Vanish
+ };
+
+ void AddSparkle( const rmt::Vector& Position, float Size, float Duration, const rmt::Vector& Velocity, eSparkleEffect Effect );
+ void AddGagSparkle(const rmt::Vector& Position, float Size, float Strength, unsigned int Caller); // See notes for information on the Caller.
+ void AddSparks( const rmt::Vector& Position, const rmt::Vector& Velocity, float Strength );
+ void AddDash( const rmt::Vector& Position, const rmt::Vector& Velocity, float Strength ); // NOTE: Velocity is direction of character travel.
+ void AddLanding( const rmt::Vector& Position, float Strength );
+ void AddLeaves( const rmt::Vector& Position, const rmt::Vector& Velocity, float Strength );
+ void AddPaintChips( const rmt::Vector& Position, const rmt::Vector& Velocity, pddiColour colour, float Strength );
+ void AddShockRing( const rmt::Vector& Position, float Strength );
+ void AddStars( const rmt::Vector& Position, float Strength );
+ void AddBottomOut( const rmt::Vector& Position );
+ void AddSmoke( const rmt::Matrix& Transform, const rmt::Vector& Offset, const rmt::Vector& Velocity, float Strength );
+
+ void Update( unsigned int Elapsedms ); // Elapsed milliseconds.
+ enum eSparkleRenderMode
+ {
+ SRM_IncludeSorted,
+ SRM_ExcludeSorted,
+ SRM_SortedOnly
+ };
+ // Note that the RenderSorted Draws all the particles tagged as being sorted. This is a pretty crude implementation and designed just the fix the car smoke.
+ void Render( eSparkleRenderMode Mode = SRM_IncludeSorted );
+ void HUDRender( void ); // Draw any HUD sparkles.
+
+ void SetTexture( unsigned char Index, tTexture* Texture );
+
+ static rmt::Randomizer sRandom;
+ static bool sRandomSeeded;
+
+protected:
+ //Prevent wasteful constructor creation.
+ Sparkle( const Sparkle& That );
+ Sparkle& operator=( const Sparkle& That );
+
+ struct ActiveSparkle
+ {
+ ActiveSparkle();
+ rmt::Vector Position;
+ rmt::Vector Velocity;
+ float Size;
+ float DurationRatio;
+ float Life;
+ pddiColour Colour;
+ unsigned int Active : 1;
+ unsigned int HUD : 1;
+ unsigned int Texture : 4;
+ unsigned int Align : 2;
+ unsigned int Scale : 3;
+ unsigned int ColourAnim : 3;
+ unsigned int CornerFade : 1;
+ unsigned int Motion : 2;
+ };
+ enum eSparkleMotion
+ {
+ SM_Linear,
+ SM_Decelerate,
+ SM_Gravity,
+ SM_Drift
+ };
+ enum eSparkleColour
+ {
+ SC_LinearDown,
+ SC_LinearDownAtHalf,
+ SC_LinearAlphaDown,
+ SC_LinearRedDownAtHalf,
+ SC_BulgeDown
+ };
+ enum eSparkleScale
+ {
+ SS_Constant,
+ SS_LinearDown,
+ SS_BulgeDown,
+ SS_LinearUp,
+ SS_LinearGrow,
+ SS_LinearTriple
+ };
+ enum eSparkleAlign
+ {
+ SA_None,
+ SA_MotionStreak,
+ SA_Flat,
+ SA_Rotation
+ };
+ ActiveSparkle* GetInactiveSparkle( void );
+ static Sparkle* spInstance;
+ ActiveSparkle* mActiveSparkles;
+ tTexture** mpTextures;
+ unsigned short mNumSparkles;
+ unsigned short mNextInactiveSparkle;
+ unsigned short* mNumActiveSparkles;
+ unsigned short* mNumHUDSparkles;
+ unsigned char mNumTextures;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline Sparkle* GetSparkleManager() { return( Sparkle::GetInstance() ); }
+
+#endif // #ifndef SPARKLE_H
diff --git a/game/code/worldsim/groundplanepool.cpp b/game/code/worldsim/groundplanepool.cpp
new file mode 100644
index 0000000..6a8bba8
--- /dev/null
+++ b/game/code/worldsim/groundplanepool.cpp
@@ -0,0 +1,326 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: groundplanepool.cpp
+//
+// Description: manage pool of ground planes for dynamic physics objects
+//
+// History: July 31, 2002 - created, gmayer
+//
+//=============================================================================
+
+
+//========================================
+// System Includes
+//========================================
+
+#include <simcommon/simstate.hpp>
+
+
+#include <simcommon/simenvironment.hpp>
+#include <simcollision/collisionmanager.hpp>
+
+#include <simcollision/collisionvolume.hpp>
+#include <simcollision/collisiondisplay.hpp>
+
+#include <simcommon/physicsproperties.hpp>
+
+#include <raddebug.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+
+#include <worldsim/groundplanepool.h>
+#include <worldsim/physicsairef.h>
+
+#include <memory/srrmemory.h>
+
+
+//=============================================================================
+// GroundPlanePool::GroundPlanePool
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: GroundPlanePool
+//
+//=============================================================================
+GroundPlanePool::GroundPlanePool(int num)
+{
+MEMTRACK_PUSH_GROUP( "GroundPlanePool" );
+ mTotalNum = num;
+
+// #ifdef RAD_GAMECUBE
+// HeapMgr()->PushHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+// #endif
+
+ mPool = new sim::ManualSimState*[mTotalNum];
+ //mPool = new sim::SimState*[mTotalNum];
+ mInUse = new bool[mTotalNum];
+
+ mSimStateOwners = new sim::SimState*[mTotalNum];
+
+ mGroundPlanePhysicsProperties = new sim::PhysicsProperties;
+
+ int i;
+ for(i = 0; i < mTotalNum; i++)
+ {
+ rmt::Vector p(0.0f, 0.0f, 0.0f);
+ rmt::Vector n(0.0f, 1.0f, 0.0f);
+
+ sim::WallVolume* tempwall = new sim::WallVolume(p, n);
+
+ // TODO - are the temp volumes getting deleted by the sim state?
+ mPool[i] = (sim::ManualSimState*)(sim::SimState::CreateManualSimState(tempwall));
+
+ //static SimState* CreateSimState(CollisionVolume* inCollisionVolume, char* inName = NULL, tEntityStore* inStore = NULL);
+
+ //mPool[i] = sim::SimState::CreateSimState(tempwall);
+
+ mPool[i]->AddRef();
+
+ mPool[i]->GetCollisionObject()->SetManualUpdate(true);
+ mPool[i]->GetCollisionObject()->SetAutoPair(false);
+ mPool[i]->GetCollisionObject()->SetIsStatic(true);
+
+ //mPool[i]->GetCollisionObject()->SetPhysicsProperties(mGroundPlanePhysicsProperties);
+ mPool[i]->SetPhysicsProperties(this->mGroundPlanePhysicsProperties);
+
+ // give a reasonable name for debugging purposes...
+ char buffy[128];
+ sprintf(buffy, "groundplanepool_id%d", i);
+ mPool[i]->GetCollisionObject()->SetName(buffy);
+
+ mInUse[i] = false;
+
+ mPool[i]->GetCollisionObject()->SetCollisionEnabled(false);
+
+ mPool[i]->mAIRefIndex = PhysicsAIRef::redBrickPhizMoveableGroundPlane;
+ mPool[i]->mAIRefPointer = 0; // only set if object is derived from CollisionEntityDSG
+
+
+ mSimStateOwners[i] = 0;
+
+
+
+ }
+// #ifdef RAD_GAMECUBE
+// HeapMgr()->PopHeap( GMA_GC_VMM );
+// #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+// #endif
+MEMTRACK_POP_GROUP( "GroundPlanePool" );
+}
+
+//=============================================================================
+// GroundPlanePool::~GroundPlanePool
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: GroundPlanePool
+//
+//=============================================================================
+GroundPlanePool::~GroundPlanePool()
+{
+ int i;
+ for(i = 0; i < mTotalNum; i++)
+ {
+ mPool[i]->Release();
+ }
+ delete mPool;
+ delete mInUse;
+
+ delete mSimStateOwners;
+
+}
+
+
+//=============================================================================
+// GroundPlanePool::GetNewGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+int GroundPlanePool::GetNewGroundPlane(sim::SimState* simStateOwner)
+{
+ // find first one that's not in use
+ int i;
+ for(i = 0; i < mTotalNum; i++)
+ {
+ if(mInUse[i] == false)
+ {
+ mInUse[i] = true;
+
+ mSimStateOwners[i] = simStateOwner;
+
+ return i;
+ }
+ }
+
+ // failure
+ return -1;
+}
+
+//=============================================================================
+// GroundPlanePool::UpdateGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void GroundPlanePool::UpdateGroundPlane(int index, rmt::Vector& position, rmt::Vector& normal)
+{
+ rAssert( 0 <= index && index < mTotalNum );
+
+ if(index > -1 && index < mTotalNum) // just in case
+ {
+ rAssert(mInUse[index]);
+
+ // for convenience:
+ sim::CollisionObject* co = mPool[index]->GetCollisionObject();
+ sim::WallVolume* wall = (sim::WallVolume*)(co->GetCollisionVolume());
+
+ wall->mPosition = position;
+ wall->mNormal = normal;
+
+ co->PostManualUpdate();
+
+ //co->Relocated();
+ //obbox->UpdateBBox();
+
+ // only need to do this once - TODO .. ? only enable when 'owner' object get's hit? -
+ //co->SetCollisionEnabled(true);
+ }
+}
+
+
+//=============================================================================
+// GroundPlanePool::EnableCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void GroundPlanePool::EnableCollision(int index)
+{
+ rAssert( 0 <= index && index < mTotalNum );
+ rAssert( mInUse[index] );
+
+ sim::CollisionObject* co = mPool[index]->GetCollisionObject();
+ co->SetCollisionEnabled(true);
+}
+
+
+//=============================================================================
+// GroundPlanePool::DisableCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void GroundPlanePool::DisableCollision(int index)
+{
+ rAssert( 0 <= index && index < mTotalNum );
+ rAssert( mInUse[index] );
+
+ sim::CollisionObject* co = mPool[index]->GetCollisionObject();
+ co->SetCollisionEnabled(false);
+}
+
+
+//=============================================================================
+// GroundPlanePool::FreeGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void GroundPlanePool::FreeGroundPlane(int index)
+{
+ // make this safe to call with any value.
+ //
+ // note: we are not responsible for taking it out of any collision or anything like that.
+ if(index < 0 || index >= mTotalNum)
+ {
+ return;
+ }
+ mInUse[index] = false;
+ mSimStateOwners[index] = 0;
+}
+
+
+
+//=============================================================================
+// GroundPlanePool::FreeAllGroundPlanes
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool GroundPlanePool::FreeAllGroundPlanes() // returns false if there was a problem...
+{
+ // called when quitting game or a level
+ bool ok = true;
+ int i;
+ for(i = 0; i < mTotalNum; i++)
+ {
+ if(mInUse[i])
+ {
+ ok = false;
+ rAssert(false);
+ }
+ mInUse[i] = false;
+ mSimStateOwners[i] = 0;
+ }
+ return ok;
+
+}
+
+
+
+//=============================================================================
+// GroundPlanePool::GetSimState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: sim
+//
+//=============================================================================
+sim::ManualSimState* GroundPlanePool::GetSimState(int index)
+//sim::SimState* GroundPlanePool::GetSimState(int index)
+{
+ rAssert( 0 <= index && index < mTotalNum );
+ rAssert(mInUse[index]);
+
+ return mPool[index];
+}
+
+
diff --git a/game/code/worldsim/groundplanepool.h b/game/code/worldsim/groundplanepool.h
new file mode 100644
index 0000000..ca93e33
--- /dev/null
+++ b/game/code/worldsim/groundplanepool.h
@@ -0,0 +1,71 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: groundplanepool.h
+//
+// Description: manage pool of ground planes for dynamic physics objects
+//
+// History: July 31, 2002 - created, gmayer
+//
+//=============================================================================
+
+#ifndef GROUNDPLANEPOOL_H
+#define GROUNDPLANEPOOL_H
+
+//========================================
+// Nested Includes
+//========================================
+
+#include <simcommon/simstate.hpp>
+
+//========================================
+// Forward References
+//========================================
+
+
+// note to self: maybe vehicles should use this?
+
+class GroundPlanePool
+{
+public:
+
+ GroundPlanePool(int num); // how big the pool should be
+ ~GroundPlanePool();
+
+ //sim::ManualSimState* GetNewGroundPlane();
+
+ //int GetNewGroundPlane(); // user refers to it with the returned index
+
+ int GetNewGroundPlane(sim::SimState* simStateOwner);
+
+
+ void UpdateGroundPlane(int index, rmt::Vector& position, rmt::Vector& normal);
+ void FreeGroundPlane(int index);
+ bool FreeAllGroundPlanes(); // returns false if there was a problem...
+
+ sim::ManualSimState* GetSimState(int index);
+ //sim::SimState* GetSimState(int index);
+
+ void EnableCollision(int index);
+ void DisableCollision(int index);
+
+
+private:
+
+ int mTotalNum;
+ sim::ManualSimState** mPool;
+ //sim::SimState** mPool;
+
+ bool* mInUse;
+
+
+ sim::SimState** mSimStateOwners;
+
+ // to save memory, and potentially have more control over the values
+ sim::PhysicsProperties* mGroundPlanePhysicsProperties;
+
+
+};
+
+
+#endif //GROUNDPLANEPOOL_H
diff --git a/game/code/worldsim/harass/allharass.cpp b/game/code/worldsim/harass/allharass.cpp
new file mode 100644
index 0000000..d587474
--- /dev/null
+++ b/game/code/worldsim/harass/allharass.cpp
@@ -0,0 +1 @@
+#include <worldsim/harass/chasemanager.cpp>
diff --git a/game/code/worldsim/harass/chasemanager.cpp b/game/code/worldsim/harass/chasemanager.cpp
new file mode 100644
index 0000000..1aa9d0e
--- /dev/null
+++ b/game/code/worldsim/harass/chasemanager.cpp
@@ -0,0 +1,911 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: chasemanager.cpp
+//
+// Description: ChaseManager Class Implementation
+//
+// History: 11/5/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#include <worldsim/harass/chasemanager.h>
+#include <stdlib.h>
+
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+/*
+#include <camera/supercammanager.h>
+#include <camera/supercam.h>
+*/
+#include <ai/vehicle/chaseai.h>
+#include <roads/roadsegment.h>
+#include <roads/geometry.h>
+#include <render/Culling/ReserveArray.h>
+#include <render/IntersectManager/IntersectManager.h>
+#include <events/EventManager.h>
+
+#include <mission/gameplaymanager.h>
+
+////////////////////////////////////
+// Initialize Statics
+//ChaseManager* spChaseManager = NULL;
+static const float HARASS_BEELINE_DIST = 60.0f;
+
+/////////////////////////////////////
+
+// Constructors/Destructors
+ChaseManager::ChaseManager()
+:
+mNumRegisteredModels( 0 ),
+mTotalSpawnFrequencies( 0 ),
+mMaxVehicles( MAX_CHASE_VEHICLES )
+{
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ //GetEventManager()->AddListener( this, EVENT_REPAIR_CAR ); // Chase cars won't be repairing.
+
+ for( int i=0; i<MAX_MODELS; i++ )
+ {
+ mModels[i].spawnFreq = 0;
+ }
+
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ mVehicles[i].v = NULL;
+ mVehicles[i].husk = NULL;
+ mVehicles[i].vAI = NULL;
+ mVehicles[i].isActive = false;
+ //mVehicles[i].activeListIndex = -1;
+ mVehicles[i].isOutOfSight = true;
+ mVehicles[i].markedForDeletion = false;
+ mVehicles[i].secondsOutOfSight = 0.0f;
+ }
+
+ // Gotta set stuff belonging to superclass to prevent a check in superclass from failing
+ mSpawnRadius = CHASE_SPAWN_RADIUS;
+ mRemoveRadius = CHASE_REMOVE_RADIUS;
+}
+
+ChaseManager::~ChaseManager()
+{
+ GetEventManager()->RemoveAll( this );
+ DeactivateAllVehicles();
+}
+
+bool ChaseManager::IsChaseVehicle( Vehicle* v )
+{
+ ChaseVehicle* cv = FindChaseVehicle( v );
+ return( cv != NULL );
+}
+
+
+
+ChaseManager::ChaseVehicle* ChaseManager::FindChaseVehicle( Vehicle* v )
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( v == mVehicles[i].v )
+ {
+ return &mVehicles[i];
+ }
+ }
+
+ return NULL;
+}
+
+
+void ChaseManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ if( id == EVENT_VEHICLE_DESTROYED )
+ {
+ Vehicle* v = (Vehicle*) pEventData;
+ rAssert( v );
+
+ ChaseVehicle* cv = FindChaseVehicle( v );
+ if( cv == NULL )
+ {
+ return;
+ }
+
+ rAssert( cv->isActive );
+
+ DeactivateVehicle( cv );
+ /***
+ WELL now they don't want husks for specifically for chase/harass cars
+ and so we go round in circles.... Initially, they wanted ALL to have husks
+ "for consistency"... Now, not so much.
+
+ // obtain info from the vehicle
+ rmt::Vector initPos, initDir;
+ v->GetPosition( &initPos );
+ initDir = v->GetFacing();
+ rAssert( rmt::Epsilon( initDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // Remove original from VehicleCentral's ActiveList
+ VehicleCentral* vc = ::GetVehicleCentral();
+ rAssert( vc != NULL );
+
+ vc->RemoveVehicleFromActiveList( cv->v );
+ GetEventManager()->TriggerEvent( EVENT_CHASE_VEHICLE_DESTROYED, cv->v );
+
+ // Update chase AI
+ vc->SetVehicleController( cv->activeListIndex, NULL );
+ cv->activeListIndex = -1;
+ cv->vAI->SetActive( false );
+ cv->vAI->Finalize();
+
+ //
+ // Now we grab husk and put it in place of the original vehicle
+ //
+ Vehicle* husk = GetVehicleCentral()->mHuskPool.RequestHusk( VT_AI, v );
+ if( husk == NULL )
+ {
+ return;
+ }
+ int res = GetVehicleCentral()->AddVehicleToActiveList( husk );
+ if( res == -1 )
+ {
+ // not supposed to happen since the list can't be full!!!
+ // we TOOK something out of the list before adding something in
+ // If this assert happens, it is both fatal and strange
+ rAssert( false );
+ return;
+ }
+ cv->activeListIndex = res;
+
+ husk->AddRef();
+ husk->SetInitialPosition( &initPos );
+ float angle = GetRotationAboutY( initDir.x, initDir.z );
+ husk->SetResetFacingInRadians( angle );
+ husk->Reset();
+ husk->SetLocomotion( VL_PHYSICS );
+
+ cv->husk = husk;
+ */
+ }
+}
+
+
+
+void ChaseManager::Init()
+{
+ SpawnManager::Init();
+
+ ////////////////////////////////////////////////////////////////
+ // CLEAN UP PREVIOUS RUN
+ //
+ DeactivateAllVehicles();
+
+ ////////////////////////////////////////////////////////////////
+ // INITIALIZE FOR NEW RUN
+ //
+
+ int count = 0;
+
+ // create a temp string array of exactly this many elems or "buckets"
+ char** names = new char* [mTotalSpawnFrequencies];
+ for( int i=0; i<MAX_MODELS; i++ )
+ {
+ for( int j=count; j<(count+mModels[i].spawnFreq); j++ )
+ {
+ names[j] = new char[160];
+ strcpy( names[j], mModels[i].name );
+ }
+ count += mModels[i].spawnFreq;
+ }
+ rAssert( count == mTotalSpawnFrequencies );
+
+ // pick a bucket random and spawn a car by that name
+ VehicleCentral* vc = ::GetVehicleCentral();
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ int coinflip = rand() % mTotalSpawnFrequencies;
+
+
+
+ Vehicle* v = vc->InitVehicle( names[coinflip], false, mConfileName, VT_AI,
+ VehicleCentral::ALLOW_DRIVER,
+ false, // not a playercar
+ false); // start with in car bv representation immediately
+ rAssert( v != NULL );
+
+ mVehicles[i].v = v;
+ mVehicles[i].husk = NULL;
+ mVehicles[i].v->AddRef();
+ mVehicles[i].v->SetLocomotion( VL_PHYSICS );
+ mVehicles[i].vAI = new (GMA_LEVEL_OTHER) ChaseAI( v, HARASS_BEELINE_DIST );
+ mVehicles[i].vAI->AddRef(); // Corresponding call to Release() will already call delete if refcount<=1
+ mVehicles[i].isActive = false;
+ mVehicles[i].isOutOfSight = true;
+ mVehicles[i].markedForDeletion = false;
+ mVehicles[i].secondsOutOfSight = 0.0f;
+ }
+
+ // clean up our temporary name buckets...
+ for( int i=0; i<mTotalSpawnFrequencies; i++ )
+ {
+ delete[] names[i];
+ }
+ delete[] names;
+}
+
+
+
+void ChaseManager::ClearAllObjects()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ DeactivateVehicle( &mVehicles[i] );
+ }
+ }
+}
+
+int ChaseManager::GetNumActiveVehicles()
+{
+ int numActive = 0;
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ numActive++;
+ }
+ }
+ return numActive;
+}
+
+void ChaseManager::ClearOutOfSightVehicles()
+{
+ static const float SECONDS_OUT_OF_SIGHT_BEFORE_REMOVAL = 5.0f;
+
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive &&
+ mVehicles[i].isOutOfSight &&
+ ( mVehicles[i].husk != NULL || mVehicles[i].markedForDeletion == true ) &&
+ mVehicles[i].secondsOutOfSight > SECONDS_OUT_OF_SIGHT_BEFORE_REMOVAL )
+ {
+ DeactivateVehicle( &mVehicles[i] );
+ }
+ }
+}
+
+void ChaseManager::ClearObjectsInsideRadius( rmt::Vector center, float radius )
+{
+ int nObjectsRemoved = 0;
+ float minDistSqr = radius * radius;
+
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ Vehicle* v = mVehicles[i].v;
+ rAssert( v != NULL );
+
+ rmt::Vector vPos;
+ v->GetPosition( &vPos );
+
+ rmt::Vector toSphere = center - vPos;
+
+ if( toSphere.MagnitudeSqr() <= minDistSqr )
+ {
+ DeactivateVehicle( &mVehicles[i] );
+ }
+ }
+ }
+}
+
+
+void ChaseManager::ClearObjectsOutsideRadius( rmt::Vector center, float radius )
+{
+ float minDistSqr = radius * radius;
+
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ Vehicle* v = mVehicles[i].v;
+ rAssert( v != NULL );
+
+ rmt::Vector vPos;
+ v->GetPosition( &vPos );
+
+ rmt::Vector toSphere = center - vPos;
+
+ if( toSphere.MagnitudeSqr() > minDistSqr )
+ {
+ DeactivateVehicle( &mVehicles[i] );
+ }
+ }
+ }
+}
+
+bool ChaseManager::RegisterModel( const char* name, int spawnFreq )
+{
+ rAssert( name != NULL );
+ rAssert( spawnFreq > 0 );
+
+ // search existing SPARSE list for name
+ int freeIndex = -1;
+ for( int i=0; i<MAX_MODELS; i++ )
+ {
+ // use spawnFreq = 0 to mark empty spots
+ if( mModels[i].spawnFreq == 0 )
+ {
+ freeIndex = i;
+ continue;
+ }
+
+ // found an existing model registered under same name, so overwrite w/ new values
+ if( strcmp(mModels[i].name, name)==0 )
+ {
+ mTotalSpawnFrequencies += spawnFreq - mModels[i].spawnFreq;
+ mModels[i].spawnFreq = spawnFreq;
+ return true;
+ }
+ }
+
+ if( 0 <= freeIndex && freeIndex < MAX_MODELS )
+ {
+ int len = strlen( name );
+ rAssert( len <= MAX_STRING_LEN );
+ strncpy( mModels[freeIndex].name, name, MAX_STRING_LEN );
+ mModels[freeIndex].name[MAX_STRING_LEN] = '\0';
+
+ mModels[freeIndex].spawnFreq = spawnFreq;
+ mTotalSpawnFrequencies += spawnFreq;
+ mNumRegisteredModels++;
+ return true;
+ }
+ return false;
+}
+
+bool ChaseManager::IsModelRegistered( const char* name )
+{
+ rAssert( name != NULL );
+
+ // search existing SPARSE list for name
+ for( int i=0; i<MAX_MODELS; i++ )
+ {
+ // found an existing model registered under this name
+ if( strcmp(mModels[i].name, name)==0 )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ChaseManager::UnregisterModel( const char* name )
+{
+ rAssert( name != NULL );
+
+ // search existing SPARSE list for name
+ for( int i=0; i<MAX_MODELS; i++ )
+ {
+ if( mModels[i].spawnFreq == 0 )
+ {
+ continue;
+ }
+ int nameLen = strlen( name );
+ int modelNameLen = strlen( mModels[i].name );
+ if( (nameLen == modelNameLen) &&
+ (strncmp(mModels[i].name, name, nameLen)==0) )
+ {
+ mTotalSpawnFrequencies -= mModels[i].spawnFreq;
+ mNumRegisteredModels--;
+ mModels[i].spawnFreq = 0;
+ return true;
+ }
+ }
+ return false;
+}
+
+void ChaseManager::SetConfileName( const char* name )
+{
+ int len = strlen( name );
+ rAssert( len <= MAX_STRING_LEN );
+ strncpy( mConfileName, name, MAX_STRING_LEN );
+ mConfileName[MAX_STRING_LEN] = '\0';
+}
+
+
+void ChaseManager::DisableAllActiveVehicleAIs()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ //mVehicles[i].vAI->SetActive( false );
+ mVehicles[i].vAI->EnterLimbo();
+ }
+ }
+}
+
+void ChaseManager::EnableAllActiveVehicleAIs()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ //mVehicles[i].vAI->SetActive( true );
+ mVehicles[i].vAI->ExitLimbo();
+ }
+ }
+}
+
+void ChaseManager::AddObjects( float seconds )
+{
+ rAssert( seconds >= 0.0f );
+
+ // Get VehicleCentral. We'll need it lots.
+ VehicleCentral* vc = ::GetVehicleCentral();
+ rAssert( vc != NULL );
+
+ // Get Player info.
+ rmt::Vector playerPos, playerVel;
+
+ /*
+ SuperCam* superCam = ::GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ rAssert( superCam != NULL );
+ superCam->GetPosition( &playerPos );
+ superCam->GetVelocity( &playerVel );
+ */
+ Avatar* avatar = ::GetAvatarManager()->GetAvatarForPlayer(0);
+ rAssert( avatar != NULL );
+ avatar->GetPosition( playerPos );
+ avatar->GetVelocity(playerVel);
+
+
+ float spawnRadius = GetSpawnRadius();
+
+
+ // Do FindRoadElems to get the road segments that intersect our sphere.
+ // For each road segment's lane, tally a list of all intersection points (max of 2)
+ // For each intersection point,
+ // if mNumObjects < mMaxObjects,
+ // ActivateVehicle()
+ // Initialize vehicle
+
+ ReserveArray<RoadSegment*> orList;
+ ::GetIntersectManager()->FindRoadSegmentElems( playerPos, spawnRadius, orList );
+
+ // This is the vehicle from our list of inactive chase vehicles that we'll spawn
+ // Abort early if there's no more vehicle available to spawn...
+ ChaseVehicle* cv = GetInactiveVehicle();
+ if( cv == NULL )
+ {
+ return;
+ }
+
+ for( int i=0; i<orList.mUseSize; i++ )
+ {
+ RoadSegment* segment = orList.mpData[i];
+ rAssert( segment != NULL );
+
+ // loop through all the lanes for this segment
+ for( unsigned int j=0; j<segment->GetNumLanes(); j++ )
+ {
+ // Find places where lane intersects with spawn radius (max 2 locations)
+ rmt::Vector startPos, startDir, endPos, endDir;
+ segment->GetLaneLocation( 0.0f, j, startPos, startDir );
+ segment->GetLaneLocation( 1.0f, j, endPos, endDir );
+
+ rmt::Vector intPts[2];
+ rmt::Sphere playerSphere( playerPos, spawnRadius );
+ int numIntersections = IntersectLineSphere( startPos, endPos, playerSphere, intPts );
+ if(numIntersections <= 0)
+ {
+ continue; // doesn't intersect our spawn sphere; skip this lane
+ }
+
+ // for each intersection point found, plant a vehicle
+ for( int k=0; k<numIntersections; k++ )
+ {
+ if( mNumObjects < mMaxVehicles )
+ {
+ rmt::Vector vPos = intPts[k];
+
+ if( cv == NULL )
+ {
+ return; // no more inactive vehicles available for spawning
+ }
+ Vehicle* v = cv->v;
+ rAssert( v != NULL );
+
+ //
+ // Detect if we're placing this car on top of another car
+ // by looping through active vehicles list. If there is car beneath us,
+ // don't spawn here and try next point.
+ //
+ rmt::Sphere vSphere;
+ v->GetBoundingSphere( &vSphere );
+ vSphere.centre = vPos;
+ if( SpawningOnTopOfAnotherVehicle(vSphere) )
+ {
+ continue; // gonna spawn on a car; try next spawn point
+ }
+
+ //
+ // Check if in the next n seconds, vehicle will still be in
+ // player's spawn zone (so we didn't add it in vain).
+ //
+
+ float timeToLiveInSeconds = 1.0f; //GetSecondsBetwAdds();
+
+ //
+ // Predict player pos after timeToLiveInSeconds seconds
+ //
+ rmt::Vector playerPos2 = playerPos + playerVel * timeToLiveInSeconds;
+
+ //
+ // Predict v pos after timeToLiveInSeconds seconds
+ //
+ rmt::Vector vDir = endPos - startPos;
+ vDir.Normalize(); // *** SQUARE ROOT! ***
+
+ float vAccel = v->mDesignerParams.mDpGasScale *
+ v->mSlipGasModifier *
+ 1.0f; //v->mGas;
+
+ float vDistWillTravel = 0.5f * vAccel * timeToLiveInSeconds * timeToLiveInSeconds;
+
+ rmt::Vector vPos2 = vPos + vDir * vDistWillTravel;
+
+ //
+ // Make sure vPos2 still inside playerPos2's radius before we spawn it
+ //
+ float minDistSqr = spawnRadius * spawnRadius;
+ float distSqr = (vPos2 - playerPos2).MagnitudeSqr();
+ if( distSqr < minDistSqr )
+ {
+ // want the vehicle to spawn facing the player
+ rmt::Vector initFacing = playerPos - vPos;
+
+ // initialize vehicle
+ v->SetInitialPosition( &vPos );
+ float angle = GetRotationAboutY( initFacing.x, initFacing.z );
+ v->SetResetFacingInRadians( angle );
+ v->Reset();
+
+ bool succeeded = ActivateVehicle( cv );
+ if( !succeeded )
+ {
+ // vehiclecentral's activelist is full..
+ // no point trying to add anymore
+ return;
+ }
+
+ // just used the inactive vehicle; grab a new inactive vehicle
+ cv = GetInactiveVehicle();
+
+ } // end-of-if vehicle's next pos lies in radius of player's next spawn sphere
+ } // end-of-if mNumObjects < mMaxObjects
+ } // end-of-loop through all intersection points on spawn radius for this lane
+ } // end-of-loop through all lanes belonging to one segment
+ }// end-of-loop through all segments returned by DSGFind
+}
+
+void ChaseManager::RemoveObjects( float seconds )
+{
+ // Get Player info
+
+ rmt::Vector playerPos;
+ /*
+ SuperCam* superCam = ::GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ rAssert( superCam != NULL );
+ superCam->GetPosition( &playerPos );
+ */
+ Avatar* avatar = ::GetAvatarManager()->GetAvatarForPlayer(0);
+ rAssert( avatar != NULL );
+ avatar->GetPosition( playerPos );
+
+ float radius = GetRemoveRadius();
+
+ // Remove!
+ ClearObjectsOutsideRadius( playerPos, radius );
+
+ ClearOutOfSightVehicles();
+}
+
+void ChaseManager::UpdateObjects( float seconds )
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ if( mVehicles[i].v->mVehicleDestroyed &&
+ mVehicles[i].vAI->GetState() != VehicleAI::STATE_LIMBO )
+ {
+ mVehicles[i].vAI->EnterLimbo();
+ // NOTE:
+ // Never set mVehicles[i].isActive to false here because
+ // it means something entirely different (it means that it's not
+ // within your radius). Just set the AI's active flag to false
+ //mVehicles[i].isActive = false;
+ }
+
+ if( mVehicles[i].husk || mVehicles[i].markedForDeletion )
+ {
+ Vehicle* vehicle = mVehicles[i].v;
+ if(mVehicles[i].husk)
+ {
+ vehicle = mVehicles[i].husk;
+ }
+
+ // Test for out of sight of player 0... If so, increment timer, if not reset timer
+ rmt::Vector pos;
+
+ vehicle->GetPosition( &pos );
+ mVehicles[i].isOutOfSight = !GetGameplayManager()->TestPosInFrustrumOfPlayer( pos, 0 );
+
+ mVehicles[i].secondsOutOfSight += seconds;
+ if( !mVehicles[i].isOutOfSight )
+ {
+ mVehicles[i].secondsOutOfSight = 0.0f;
+ }
+ }
+ }
+ }
+
+ /*
+ // VehicleCentral automatically updates all vehicles & their AI
+ // controllers if they're under Physics Locomotion (which they are)
+ // so we need do nothing here.
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ mVehicles[i].v->Update( seconds );
+ }
+ }
+ */
+}
+
+void ChaseManager::SuspendAllVehicles()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].v != NULL && mVehicles[i].isActive)
+ {
+ mVehicles[i].v->CarDisplay( false );
+ GetVehicleCentral()->RemoveVehicleFromActiveList( mVehicles[i].v );
+ }
+ }
+}
+
+void ChaseManager::ResumeAllVehicles()
+{
+ VehicleCentral* vc = ::GetVehicleCentral();
+ rAssert( vc != NULL );
+
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].v != NULL && mVehicles[i].isActive)
+ {
+ mVehicles[i].v->CarDisplay( true );
+ // add to vehiclecentral's activelist
+ if( vc->ActiveVehicleListIsFull() )
+ {
+ rAssert(false);
+ return;
+ }
+ int res = vc->AddVehicleToActiveList( mVehicles[i].v );
+ if( res == -1 )
+ {
+ // not supposed to happen since we already eliminated the
+ // safe failure condition (activelistisfull).. So something else
+ // went wrong...
+ rAssert( false );
+ }
+
+ // update chase AI
+ vc->SetVehicleController( res, mVehicles[i].vAI );
+ mVehicles[i].vAI->Initialize();
+ mVehicles[i].vAI->SetActive( true );
+ }
+ }
+}
+
+void ChaseManager::DeactivateVehicle( ChaseVehicle* cv )
+{
+ rAssert( cv->v != NULL );
+ rAssert( cv->vAI != NULL );
+ rAssert( cv->isActive );
+
+ // Remove from VehicleCentral's ActiveList
+ VehicleCentral* vc = ::GetVehicleCentral();
+ rAssert( vc != NULL );
+
+ if( cv->husk == NULL )
+ {
+ vc->RemoveVehicleFromActiveList( cv->v );
+ GetEventManager()->TriggerEvent( EVENT_CHASE_VEHICLE_DESTROYED, cv->v );
+ }
+ else
+ {
+ bool succeeded = vc->RemoveVehicleFromActiveList( cv->husk );
+ rAssert( succeeded );
+
+ ::GetVehicleCentral()->mHuskPool.FreeHusk( cv->husk );
+ cv->husk->Release(); // don't verify destruction cuz huskpool has final ownership
+ cv->husk = NULL;
+ }
+
+ //if( cv->activeListIndex != -1 )
+ //{
+ // vc->SetVehicleController( cv->activeListIndex, NULL );
+ //}
+ cv->vAI->SetActive( false );
+ cv->vAI->Finalize();
+ //cv->activeListIndex = -1;
+ cv->isActive = false;
+ cv->isOutOfSight = true;
+ cv->markedForDeletion = false;
+ cv->secondsOutOfSight = 0.0f;
+ mNumObjects--;
+}
+
+bool ChaseManager::ActivateVehicle( ChaseVehicle* cv )
+{
+ rAssert( cv->v != NULL );
+ rAssert( cv->vAI != NULL );
+ //rAssert( cv->activeListIndex == -1 );
+ rAssert( !cv->isActive );
+
+ VehicleCentral* vc = ::GetVehicleCentral();
+ rAssert( vc != NULL );
+
+ // add to vehiclecentral's activelist
+ if( vc->ActiveVehicleListIsFull() )
+ {
+ return false;
+ }
+ int res = vc->AddVehicleToActiveList( cv->v );
+ if( res == -1 )
+ {
+ // not supposed to happen since we already eliminated the
+ // safe failure condition (activelistisfull).. So something else
+ // went wrong...
+ rAssert( false );
+ }
+
+ // update chase AI
+ vc->SetVehicleController( res, cv->vAI );
+ cv->vAI->Initialize();
+ cv->vAI->SetActive( true );
+
+ // update our variables
+ //cv->activeListIndex = res;
+ cv->isActive = true;
+ rAssert( cv->husk == NULL );
+ cv->isOutOfSight = true;
+ cv->markedForDeletion = false;
+ cv->secondsOutOfSight = 0.0f;
+ mNumObjects++;
+
+ // tell sound
+ GetEventManager()->TriggerEvent( EVENT_CHASE_VEHICLE_SPAWNED, cv->v );
+
+ return true;
+}
+
+void ChaseManager::DeactivateAllVehicles()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ DeactivateVehicle( &mVehicles[i] );
+ }
+ if( mVehicles[i].v != NULL )
+ {
+ mVehicles[i].v->Release();
+ mVehicles[i].v = NULL;
+ }
+ if( mVehicles[i].vAI != NULL )
+ {
+ mVehicles[i].vAI->Release();
+ mVehicles[i].vAI = NULL;
+ }
+ }
+ rAssert( mNumObjects == 0 );
+}
+
+void ChaseManager::MarkAllVehiclesForDeletion()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].v != NULL )
+ {
+ mVehicles[i].markedForDeletion = true;
+ }
+ }
+}
+
+ChaseManager::ChaseVehicle* ChaseManager::GetInactiveVehicle()
+{
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( !mVehicles[i].isActive )
+ {
+ return &mVehicles[i];
+ }
+ }
+ return NULL;
+}
+
+bool ChaseManager::SpawningOnTopOfAnotherVehicle( const rmt::Sphere& s )
+{
+ VehicleCentral* vc = ::GetVehicleCentral();
+
+ int nActiveVehicles = 0;
+ Vehicle** activeVehicles = NULL;
+ vc->GetActiveVehicleList( activeVehicles, nActiveVehicles );
+
+ Vehicle* aCar;
+ rmt::Vector aPos;
+ rmt::Sphere aSphere;
+
+ //
+ // Because VehicleCentral's ActiveList is a SPARSE array, we have to loop
+ // through the MAX array size. But since we know how many actual vehicles
+ // there are in there, we can quit after we've reached that number. So keep
+ // a counter.
+ //
+ int vCount = 0;
+ for( int i=0; i<vc->GetMaxActiveVehicles(); i++ )
+ {
+ if( vCount >= nActiveVehicles )
+ {
+ break;
+ }
+
+ aCar = activeVehicles[i];
+ if( aCar == NULL )
+ {
+ continue;
+ }
+ vCount++;
+ aCar->GetPosition( &aPos );
+ aCar->GetBoundingSphere( &aSphere );
+
+ float distSqr = (aPos - s.centre).MagnitudeSqr();
+ float minDist = aSphere.radius + s.radius;
+ float minDistSqr = minDist * minDist;
+
+ // if we're colliding with another car
+ if( distSqr < minDistSqr )
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
+float ChaseManager::GetClosestCarPosition(rmt::Vector* toPos, rmt::Vector* carPos)
+{
+ float distSqr = 999999999.0f;
+
+ for( int i=0; i<MAX_CHASE_VEHICLES; i++ )
+ {
+ if( mVehicles[i].isActive )
+ {
+ if( mVehicles[i].v != NULL )
+ {
+ rmt::Vector currPos;
+ mVehicles[i].v->GetPosition(&currPos);
+ float currDistSqr = (*toPos - currPos).MagnitudeSqr();
+ if(currDistSqr < distSqr)
+ {
+ distSqr = currDistSqr;
+ *carPos = currPos;
+ }
+ }
+ }
+ }
+ return distSqr;
+}
+
diff --git a/game/code/worldsim/harass/chasemanager.h b/game/code/worldsim/harass/chasemanager.h
new file mode 100644
index 0000000..50fdc31
--- /dev/null
+++ b/game/code/worldsim/harass/chasemanager.h
@@ -0,0 +1,188 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: chasemanager.h
+//
+// Description: ChaseManager Class declaration.
+//
+// History: 11/5/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#ifndef CHASEMANAGER_H
+#define CHASEMANAGER_H
+
+#include <worldsim/spawn/spawnmanager.h>
+#include <events/eventlistener.h>
+
+class Vehicle;
+class VehicleAI;
+
+static const float CHASE_SPAWN_RADIUS = 100.0f;
+static const float CHASE_REMOVE_RADIUS = 110.0f;
+static const float SECONDS_BETW_CHASE_ADDS = 0.7f;
+static const float SECONDS_BETW_CHASE_REMOVES = 0.5f;
+static const float SECONDS_BETW_CHASE_UPDATES = 0.0f;
+
+class ChaseManager
+: public SpawnManager, EventListener
+{
+public:
+
+ static const int MAX_CHASE_VEHICLES = 5;
+ static const int MAX_MODELS = 1;
+ static const int MAX_STRING_LEN = 64;
+
+ // Dusit [Nov 4,2002]:
+ // Don't make ChaseManager static. Make it possible for there
+ // to be 1 ChaseManager to spawn the 1 cSedan that needs to always
+ // be present, and another ChaseManager for the other set of chase cars.
+ ///////////////////////////////////////////////////////////////
+ // Statics
+ //static ChaseManager* GetInstance();
+ //static ChaseManager* DestroyInstance();
+
+ ChaseManager();
+ virtual ~ChaseManager();
+
+ ///////////////////////////////////////////////////////////////
+ // Implementing SpawnManager Stuff
+ void Init();
+ void ClearAllObjects();
+ void ClearObjectsInsideRadius( rmt::Vector center, float radius );
+ void ClearObjectsOutsideRadius( rmt::Vector center, float radius );
+ int GetAbsoluteMaxObjects() const;
+ int GetMaxObjects() const;
+ void SetMaxObjects( int maxObjects );
+ int GetMaxModels() const;
+ int GetNumRegisteredModels() const;
+ bool RegisterModel( const char* name, int spawnFreq );
+ bool IsModelRegistered( const char* name );
+ bool UnregisterModel( const char* name );
+ void SetConfileName( const char* name );
+ ///////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////
+ // EventListener
+ void HandleEvent( EventEnum id, void* pEventData );
+ ///////////////////////////////////////////////////////////////
+
+ bool IsChaseVehicle( Vehicle* v );
+ void DisableAllActiveVehicleAIs();
+ void EnableAllActiveVehicleAIs();
+ void DeactivateAllVehicles();
+ int GetNumActiveVehicles();
+ void MarkAllVehiclesForDeletion();
+
+ void SuspendAllVehicles();
+ void ResumeAllVehicles();
+
+ float GetClosestCarPosition(rmt::Vector* toPos, rmt::Vector* carPos);
+
+protected:
+ ///////////////////////////////////////////////////////////////
+ // Implementing SpawnManager Stuff
+ void AddObjects( float seconds );
+ void RemoveObjects( float seconds );
+ void UpdateObjects( float seconds );
+ float GetSecondsBetwAdds() const;
+ float GetSecondsBetwRemoves() const;
+ float GetSecondsBetwUpdates() const;
+
+
+private:
+
+ //static ChaseManager* spChaseManager;
+
+ struct Model
+ {
+ char name[MAX_STRING_LEN+1];
+ int spawnFreq;
+ };
+ Model mModels[MAX_MODELS];
+ int mNumRegisteredModels;
+ int mTotalSpawnFrequencies;
+
+ struct ChaseVehicle
+ {
+ Vehicle* v;
+ Vehicle* husk;
+ VehicleAI* vAI;
+ //int activeListIndex;
+ bool isActive;
+ bool isOutOfSight;
+ bool markedForDeletion;
+ float secondsOutOfSight;
+ };
+
+ int mMaxVehicles; // betw 0 and AbsoluteMax
+
+ ChaseVehicle mVehicles[MAX_CHASE_VEHICLES];
+
+ char mConfileName[MAX_STRING_LEN+1];
+
+ void ClearOutOfSightVehicles();
+
+ void DeactivateVehicle( ChaseVehicle* cv );
+ bool ActivateVehicle( ChaseVehicle* cv );
+
+ ChaseVehicle* FindChaseVehicle( Vehicle* v );
+
+ ChaseVehicle* GetInactiveVehicle();
+ bool SpawningOnTopOfAnotherVehicle( const rmt::Sphere& s );
+};
+
+////////////////////////////// INLINES /////////////////////////////////
+
+// etc, etc, etc...
+inline int ChaseManager::GetAbsoluteMaxObjects() const
+{
+ rAssert( MAX_CHASE_VEHICLES >= 0 );
+ return MAX_CHASE_VEHICLES;
+}
+inline int ChaseManager::GetMaxObjects() const
+{
+ return mMaxVehicles;
+}
+inline void ChaseManager::SetMaxObjects( int maxObjects )
+{
+ int absoluteMax = GetAbsoluteMaxObjects();
+ rAssert( absoluteMax >= 0 );
+
+ if( maxObjects < 0 )
+ {
+ maxObjects = 0;
+ }
+ else if( maxObjects > absoluteMax )
+ {
+ maxObjects = absoluteMax;
+ }
+
+ mMaxVehicles = maxObjects;
+}
+
+inline int ChaseManager::GetMaxModels() const
+{
+ return MAX_MODELS;
+}
+inline int ChaseManager::GetNumRegisteredModels() const
+{
+ return mNumRegisteredModels;
+}
+inline float ChaseManager::GetSecondsBetwAdds() const
+{
+ rAssert( SECONDS_BETW_CHASE_ADDS >= 0.0f );
+ return SECONDS_BETW_CHASE_ADDS;
+}
+inline float ChaseManager::GetSecondsBetwRemoves() const
+{
+ rAssert( SECONDS_BETW_CHASE_REMOVES >= 0.0f );
+ return SECONDS_BETW_CHASE_REMOVES;
+}
+inline float ChaseManager::GetSecondsBetwUpdates() const
+{
+ rAssert( SECONDS_BETW_CHASE_UPDATES >= 0.0f );
+ return SECONDS_BETW_CHASE_UPDATES;
+}
+
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/hitnrunmanager.cpp b/game/code/worldsim/hitnrunmanager.cpp
new file mode 100644
index 0000000..070178b
--- /dev/null
+++ b/game/code/worldsim/hitnrunmanager.cpp
@@ -0,0 +1,1118 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: HitnRunManager.cpp
+//
+// Description: Implementation of class HitnRunManager
+//
+// History: 26/03/2003 + Created -- Jesse Cluff
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <radmath/matrix.hpp>
+#include <radmath/random.hpp>
+
+//#include <math.h> //need this if we want extra accurate coin paths.
+
+//========================================
+// Project Includes
+//========================================
+#include <gameflow/gameflow.h>
+
+#include <worldsim/hitnrunmanager.h>
+#include <worldsim/coins/coinmanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/coins/coinmanager.h>
+#include <memory/srrmemory.h>
+#include <events/eventmanager.h>
+#include <render/intersectmanager/intersectmanager.h>
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+#include <render/dsg/statepropdsg.h>
+#include <data/persistentworldmanager.h>
+#include <mission/gameplaymanager.h>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <interiors/interiormanager.h>
+#include <constants/breakablesenum.h>
+#include <constants/vehicleenum.h>
+#include <worldsim/harass/chasemanager.h>
+#include <presentation\gui\ingame\guimanageringame.h>
+#include <presentation\gui\ingame\guiscreenhud.h>
+#include <presentation\gui\guisystem.h>
+#include <ai\statemanager.h>
+#include <contexts/context.h>
+
+/* Watcher stuff */
+#ifndef RAD_RELEASE
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#endif
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+static const float MAX_HITNRUN = 100.0f;
+static const float MIN_HITNRUN = 0.0f;
+static const float FADE_TIME = 750.0f;
+static float STOP_HITNRUN = 5.0f; //was 78
+static const float ALLOW_THROB = 60.0f;
+
+HitnRunManager* HitnRunManager::smpHitnRunManager = NULL;
+
+static const char* OBJECTS_THAT_NEVER_GIVE_COINS[] =
+{
+ "l1_hedgeunit",
+ "l7_hedgeunit"
+};
+
+static const int NUM_OBJECTS_THAT_NEVER_GIVE_COINS = sizeof(OBJECTS_THAT_NEVER_GIVE_COINS)/sizeof(OBJECTS_THAT_NEVER_GIVE_COINS[0]);
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+HitnRunManager* HitnRunManager::CreateInstance( void )
+{
+ rAssertMsg( smpHitnRunManager == NULL, "HitnRunManager already created.\n" );
+ HeapManager::GetInstance()->PushHeap( GMA_PERSISTENT );
+ smpHitnRunManager = new HitnRunManager;
+ rAssert( smpHitnRunManager );
+ HeapManager::GetInstance()->PopHeap( GMA_PERSISTENT );
+ return HitnRunManager::GetInstance();
+}
+
+HitnRunManager* HitnRunManager::GetInstance( void )
+{
+ rAssertMsg( smpHitnRunManager != NULL, "HitnRunManager has not been created yet.\n" );
+ return smpHitnRunManager;
+}
+
+void HitnRunManager::DestroyInstance( void )
+{
+ rAssertMsg( smpHitnRunManager != NULL, "HitnRunManager has not been created.\n" );
+ delete smpHitnRunManager;
+ smpHitnRunManager = NULL;
+}
+
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+HitnRunManager::HitnRunManager() :
+ mCurrHitnRun(0.0f),
+ mDecayRatePerSecond(1.0f),
+ mDecayRateWhileSpawning(6.0f),
+ mDecayRateInsidePerSecond(10.0f),
+ mVehicleDestroyedDelta(25.0f),
+ mVehicleDestroyedCoins( 10 ),
+ mVehicleHitDelta(5.0f),
+ mHitBreakableDelta(7.5f),
+ mHitBreakableCoins( 1 ),
+ mHitMoveableDelta(7.5f),
+ mHitMoveableCoins( 1 ),
+ mHitKrustyGlassDelta(0.0f),
+ mHitKrustyGlassCoins( 5 ),
+ mColaPropDestroyedDelta(0.0f),
+ mColaPropDestroyedCoins( 10 ),
+ mKickNPCDelta(10.0f),
+ mKickNPCCoins( 0 ), //no coins on peds
+ mPlayerCarHitNPCDelta(13.0f),
+ mPlayerCarHitNPCCoins( 0 ), //no coins on peds
+ mBustedCoins( 50 ),
+ mSwitchSkinDelta(-100.0f),
+ mChangeVehicleDelta(-100.0f),
+ mPrevVehicleID(VehicleEnum::INVALID),
+// mPrevVehicleHit(NULL),
+// mPrevMoveableHit(NULL),
+ mLeastRecentlyHit(0),
+ mChaseOn(false),
+ mSpawnOn(false),
+ mDecayDelayMS(0.0f),
+ mDecayDelay(3000.0f),
+ mDecayDelayWhileSpawning(0.0f), // was 3000
+ mNumChaseCars(1),
+ mHitnRunDisabled(false),
+ mDecayDisabled(false),
+ mCopTicketDistance(10.0f),
+ mCopTicketDistanceOnFoot(10.0f),
+ mCopTicketSpeedThreshold(30.0f),
+ mCopTicketTimeThreshold(0.75f), // was 0.1; trying something to get rid of "bump = busted"
+ mTicketTimer(0.0f),
+ mLastUpdateValue(0.0f),
+ mLastHitnRunValue(0.0f),
+ mVehicleReset(true),
+ mVehicleResetTimer(0.0f),
+ mUnresponsiveTime(1500.0f),
+ mFadeDone(true),
+ mFadeTimer(0.0f),
+ mImmunityVehicle(NULL),
+ mWarningThreshold(0.78f),
+ mAllowThrob(true)
+{
+ for(int i = 0; i < 16; i++)
+ {
+ mPrevHits[i] = NULL;
+ }
+ // We don't want to do string compares when determining if an object that we just hit
+ // is to not give a coin
+ // So build a list of tUIDs in the ctor
+ mObjectsThatNeverGiveCoins = new tUID[ NUM_OBJECTS_THAT_NEVER_GIVE_COINS ];
+ for(int i = 0 ; i < NUM_OBJECTS_THAT_NEVER_GIVE_COINS ; i++ )
+ {
+ mObjectsThatNeverGiveCoins[ i ] = tName::MakeUID( OBJECTS_THAT_NEVER_GIVE_COINS[i] );
+ }
+
+#ifndef RAD_RELEASE
+ radDbgWatchAddFloat( &mCurrHitnRun, "Current HitnRun Value", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mDecayRatePerSecond, "Decay Rate", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mDecayRateWhileSpawning, "Decay Rate while spawning", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mDecayRateInsidePerSecond, "Interior Decay Rate", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mVehicleDestroyedDelta, "Vehicle Destroyed Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mVehicleDestroyedCoins, "Vehicle Destroyed Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddFloat( &mHitBreakableDelta, "Hit Breakable Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mVehicleHitDelta, "Vehicle Hit Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mHitBreakableCoins, "Hit Breakable Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddFloat( &mHitMoveableDelta, "Hit Moveable Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mHitMoveableCoins, "Hit Moveable Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddFloat( &mHitKrustyGlassDelta, "Hit Krusty Glass Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mHitKrustyGlassCoins, "Hit Krusty Glass Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddFloat( &mColaPropDestroyedDelta, "Cola Prop Destroyed Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mColaPropDestroyedCoins, "Cola Prop Destroyed Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddFloat( &mKickNPCDelta, "Kick NPC Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mKickNPCCoins, "Kick NPC Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddFloat( &mPlayerCarHitNPCDelta, "Vehicle Hit NPC Delta", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mPlayerCarHitNPCCoins, "Vehicle Hit NPC Coins", "HitnRun", NULL, NULL, 0, 100, false );
+ radDbgWatchAddInt( &mBustedCoins, "Coins lost when busted", "HitnRun", 0, 0, 0, 100, false );
+ radDbgWatchAddFloat( &mSwitchSkinDelta, "Switch Skin Delta", "HitnRun", NULL, NULL, 0.0f, -100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mChangeVehicleDelta, "Change Vehicle Delta", "HitnRun", NULL, NULL, 0.0f, -100.0f, false ); //false for read and write
+ radDbgWatchAddInt( &mNumChaseCars, "Number of Chase Vehicles", "HitnRun", NULL, NULL, 0, 5, false );
+ radDbgWatchAddFloat( &mDecayDelay, "Delay until meter decay", "HitnRun", NULL, NULL, 0.0f, 5000.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mDecayDelayWhileSpawning, "Decay delay while spawning", "HitnRun", NULL, NULL, 0.0f, 5000.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &STOP_HITNRUN, "Spawning Stop Percentage", "HitnRun", NULL, NULL, 0.0f, 100.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mCopTicketDistance, "Cop Ticket Distance", "HitnRun", NULL, NULL, 0.0f, 20.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mCopTicketDistanceOnFoot, "Cop Ticket Distance On Foot", "HitnRun", NULL, NULL, 0.0f, 20.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mCopTicketSpeedThreshold, "Cop Ticket Speed Threshold", "HitnRun", NULL, NULL, 0.0f, 30.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mCopTicketTimeThreshold, "Cop Ticket Time Threshold", "HitnRun", NULL, NULL, 0.0f, 10.0f, false ); //false for read and write
+ radDbgWatchAddFloat( &mUnresponsiveTime, "Busted Unresponsive Time", "HitnRun", NULL, NULL, 0.0f, 10000.0f, false ); //false for read and write
+#endif
+}
+
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+HitnRunManager::~HitnRunManager()
+{
+#ifndef RAD_RELEASE
+ radDbgWatchDelete( &mCurrHitnRun );
+ radDbgWatchDelete( &mDecayRatePerSecond );
+ radDbgWatchDelete( &mDecayRateWhileSpawning );
+ radDbgWatchDelete( &mDecayRateInsidePerSecond );
+ radDbgWatchDelete( &mVehicleDestroyedDelta );
+ radDbgWatchDelete( &mVehicleDestroyedCoins );
+ radDbgWatchDelete( &mHitBreakableDelta );
+ radDbgWatchDelete( &mVehicleHitDelta );
+ radDbgWatchDelete( &mHitBreakableCoins );
+ radDbgWatchDelete( &mHitMoveableDelta );
+ radDbgWatchDelete( &mHitMoveableCoins );
+ radDbgWatchDelete( &mHitKrustyGlassDelta );
+ radDbgWatchDelete( &mHitKrustyGlassCoins );
+ radDbgWatchDelete( &mColaPropDestroyedDelta );
+ radDbgWatchDelete( &mColaPropDestroyedCoins );
+ radDbgWatchDelete( &mKickNPCDelta );
+ radDbgWatchDelete( &mKickNPCCoins );
+ radDbgWatchDelete( &mPlayerCarHitNPCDelta );
+ radDbgWatchDelete( &mPlayerCarHitNPCCoins );
+ radDbgWatchDelete( &mBustedCoins );
+ radDbgWatchDelete( &mSwitchSkinDelta );
+ radDbgWatchDelete( &mChangeVehicleDelta );
+ radDbgWatchDelete( &mNumChaseCars );
+ radDbgWatchDelete( &mDecayDelay );
+ radDbgWatchDelete( &mDecayDelayWhileSpawning );
+ radDbgWatchDelete( &STOP_HITNRUN );
+ radDbgWatchDelete( &mCopTicketDistance );
+ radDbgWatchDelete( &mCopTicketDistanceOnFoot );
+ radDbgWatchDelete( &mCopTicketSpeedThreshold );
+ radDbgWatchDelete( &mCopTicketTimeThreshold );
+ radDbgWatchDelete( &mUnresponsiveTime );
+#endif
+ Destroy( );
+ delete [] mObjectsThatNeverGiveCoins;
+ mObjectsThatNeverGiveCoins = NULL;
+}
+
+//==============================================================================
+// Description: Destruction method for all transient data.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+void HitnRunManager::Destroy( void )
+{
+ GetEventManager()->RemoveAll( this );
+}
+
+/*==============================================================================
+Description: This will be called set up as the game session begins.
+
+Parameters: ( void )
+
+Return: void
+
+=============================================================================*/
+void HitnRunManager::Init( void )
+{
+ #ifdef RAD_GAMECUBE
+ HeapManager::GetInstance()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapManager::GetInstance()->PushHeap( GMA_LEVEL_ZONE );
+ #endif
+
+ //do some allocations here
+
+ #ifdef RAD_GAMECUBE
+ HeapManager::GetInstance()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapManager::GetInstance()->PopHeap( GMA_LEVEL_ZONE );
+ #endif
+
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED_BY_USER );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_VEHICLE_COLLISION );
+ //GetEventManager()->AddListener( this, EVENT_HIT_BREAKABLE ); //currently get multiple events - don't use
+ GetEventManager()->AddListener( this, EVENT_HIT_MOVEABLE );
+ GetEventManager()->AddListener( this, EVENT_OBJECT_KICKED );
+ GetEventManager()->AddListener( this, EVENT_COLAPROP_DESTROYED ); //event waiting for mike r
+ GetEventManager()->AddListener( this, EVENT_KICK_NPC ); //add trigger - complicated
+ GetEventManager()->AddListener( this, EVENT_PLAYER_CAR_HIT_NPC );
+
+ GetEventManager()->AddListener( this, EVENT_SWITCH_SKIN );
+ //GetEventManager()->AddListener( this, EVENT_GETINTOVEHICLE_END ); //doesn't work for traffic cars
+ GetEventManager()->AddListener( this, EVENT_ENTERING_PLAYER_CAR );
+ GetEventManager()->AddListener( this, EVENT_ENTERING_TRAFFIC_CAR );
+ GetEventManager()->AddListener( this, EVENT_LEVEL_START );
+
+ GetEventManager()->AddListener( this, EVENT_HIT_AND_RUN_CAUGHT );
+ GetEventManager()->AddListener( this, EVENT_CHASE_VEHICLE_SPAWNED );
+
+ GetEventManager()->AddListener( this, EVENT_MISSION_START );
+ GetEventManager()->AddListener( this, EVENT_MISSION_SUCCESS );
+ GetEventManager()->AddListener( this, EVENT_MISSION_FAILURE );
+ GetEventManager()->AddListener( this, EVENT_MISSION_RESET );
+
+ GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_START );
+ GetEventManager()->AddListener( this, EVENT_ENTER_INTERIOR_END );
+ GetEventManager()->AddListener( this, EVENT_EXIT_INTERIOR_START );
+ GetEventManager()->AddListener( this, EVENT_EXIT_INTERIOR_END );
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_START );
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_DONE );
+ GetEventManager()->AddListener( this, EVENT_CONVERSATION_SKIP );
+}
+
+void HitnRunManager::ResetState( void )
+{
+ mCurrHitnRun = MIN_HITNRUN;
+ mSpawnOn = false;
+ mChaseOn = false;
+ mDecayDisabled = false;
+ mHitnRunDisabled = false;
+ mAllowThrob = true;
+ mVehicleResetTimer = 0.0f;
+ mFadeTimer = 0.0f;
+ mFadeDone = true;
+ mVehicleReset = true;
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ //chaseManager->DisableAllActiveVehicleAIs();
+ //chaseManager->MarkAllVehiclesForDeletion();
+ chaseManager->ClearAllObjects();
+ }
+ }
+}
+
+void HitnRunManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_ENTER_INTERIOR_START:
+ case EVENT_EXIT_INTERIOR_START:
+ case EVENT_CONVERSATION_START:
+ {
+ mHitnRunDisabled = true;
+
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->DisableAllActiveVehicleAIs();
+ chaseManager->SuspendAllVehicles();
+ }
+ }
+ }
+ break;
+ case EVENT_ENTER_INTERIOR_END:
+ case EVENT_EXIT_INTERIOR_END:
+ case EVENT_CONVERSATION_DONE:
+ case EVENT_CONVERSATION_SKIP:
+ {
+ mHitnRunDisabled = false;
+
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->EnableAllActiveVehicleAIs();
+ chaseManager->ResumeAllVehicles();
+ }
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ if(!mHitnRunDisabled && !GetInteriorManager()->IsInside())
+ {
+ float decayDelay = mDecayDelay;
+ if(mSpawnOn)
+ {
+ decayDelay = mDecayDelayWhileSpawning;
+ }
+
+ switch ( id )
+ {
+ case EVENT_VEHICLE_DESTROYED_BY_USER:
+ {
+ Vehicle* vehicle = static_cast<Vehicle*>( pEventData );
+ bool isChaseVehicle = false;
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ if(vehicle->mName)
+ {
+ isChaseVehicle = chaseManager->IsModelRegistered(vehicle->mName);
+ }
+ }
+ }
+ if( !isChaseVehicle && ( vehicle->mVehicleType == VT_TRAFFIC || vehicle->mVehicleType == VT_AI ) && vehicle != mImmunityVehicle)
+ {
+ mCurrHitnRun += mVehicleDestroyedDelta;
+ mDecayDelayMS = decayDelay;
+ GetCoinManager()->SpawnCoins( mVehicleDestroyedCoins, vehicle->GetPosition(), vehicle->GetGroundY() );
+ }
+ }
+ break;
+ case EVENT_VEHICLE_VEHICLE_COLLISION:
+ {
+ CarOnCarCollisionEventData* data = static_cast<CarOnCarCollisionEventData*>( pEventData );
+ Vehicle* vehicle = data->vehicle;
+ bool isChaseVehicle = false;
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ if(vehicle->mName)
+ {
+ isChaseVehicle = chaseManager->IsModelRegistered(vehicle->mName);
+ }
+ }
+ }
+ if( !isChaseVehicle && ( vehicle->mVehicleType == VT_TRAFFIC || vehicle->mVehicleType == VT_AI ) && vehicle != mImmunityVehicle)
+ {
+ if(mDecayDelayMS <= decayDelay/2)
+ {
+ mCurrHitnRun += mVehicleHitDelta;
+ mDecayDelayMS = decayDelay;
+ }
+ }
+ }
+ break;
+ case EVENT_HIT_MOVEABLE:
+ //case EVENT_HIT_BREAKABLE:
+ {
+ //currently this assumes hitting a breakable breaks it ;)
+ sim::SimState* simState = static_cast<sim::SimState*>(pEventData);
+ //if(simState && simState->GetControl() == sim::simAICtrl)
+ if(simState)
+ {
+ sim::SimControlEnum con = simState->GetControl();
+ if(con == sim::simAICtrl)
+ {
+ CollisionEntityDSG* cedsg = static_cast< CollisionEntityDSG*>(simState->mAIRefPointer);
+ CollisionAttributes* collAttribs = cedsg->GetCollisionAttributes();
+ if(collAttribs)
+ {
+ if(HasntBeenHit((void*)cedsg))
+ {
+ BreakablesEnum::BreakableID id = collAttribs->GetBreakable();
+ if(id == BreakablesEnum::eKrustyGlassBreaking)
+ {
+ mCurrHitnRun += mHitKrustyGlassDelta;
+ GetCoinManager()->SpawnCoins( mHitKrustyGlassCoins, simState->GetPosition() );
+ }
+ else if(id == BreakablesEnum::eNull)
+ {
+ // Lets check if this thing that we impacted is actually allowed to give
+ // coins. (e.g. hedges are stateprops that unlike most
+ // others, should NEVER give coins and are thus in this list
+ if ( DoesObjectGiveCoins( cedsg ) )
+ {
+ mCurrHitnRun += mHitMoveableDelta;
+ GetCoinManager()->SpawnCoins( mHitMoveableCoins, simState->GetPosition() );
+ }
+ }
+ else
+ {
+ mCurrHitnRun += mHitBreakableDelta;
+ GetCoinManager()->SpawnCoins( mHitBreakableCoins, simState->GetPosition() );
+ }
+ RegisterHit((void*)cedsg);
+ mDecayDelayMS = decayDelay;
+ }
+ }
+ }
+ }
+ }
+ break;
+ case EVENT_OBJECT_KICKED:
+ {
+ CollisionEntityDSG* cedsg = static_cast< CollisionEntityDSG*>(pEventData);
+ CollisionAttributes* collAttribs = cedsg->GetCollisionAttributes();
+ if(collAttribs)
+ {
+ if(HasntBeenHit((void*)cedsg))
+ {
+
+ StatePropDSG* spdsg = dynamic_cast<StatePropDSG*>(cedsg);
+ if(spdsg)
+ {
+ if(spdsg->GetStatePropUID() == tEntity::MakeUID("waspray"))
+ {
+ break;
+ }
+ }
+
+ rmt::Vector pos;
+ cedsg->GetPosition(&pos);
+
+ BreakablesEnum::BreakableID id = collAttribs->GetBreakable();
+ if(id == BreakablesEnum::eKrustyGlassBreaking)
+ {
+ mCurrHitnRun += mHitKrustyGlassDelta;
+ GetCoinManager()->SpawnCoins( mHitKrustyGlassCoins, pos );
+ }
+ else if(id == BreakablesEnum::eNull)
+ {
+ mCurrHitnRun += mHitMoveableDelta;
+ GetCoinManager()->SpawnCoins( mHitMoveableCoins, pos );
+ }
+ else
+ {
+ mCurrHitnRun += mHitBreakableDelta;
+ GetCoinManager()->SpawnCoins( mHitBreakableCoins, pos );
+ }
+ RegisterHit((void*)cedsg);
+ mDecayDelayMS = decayDelay;
+ }
+ }
+ }
+ break;
+ case EVENT_COLAPROP_DESTROYED:
+ {
+ //handles cola state props being destroyed (krustyglass is handled as a breakable)
+ mCurrHitnRun += mColaPropDestroyedDelta;
+ mDecayDelayMS = decayDelay;
+ StatePropDSG* prop = static_cast<StatePropDSG*>( pEventData );
+ GetCoinManager()->SpawnCoins( mColaPropDestroyedCoins, prop->rPosition() );
+ }
+ break;
+ case EVENT_KICK_NPC:
+ {
+ Character* ch = static_cast<Character*>(pEventData);
+ mCurrHitnRun += mKickNPCDelta;
+ mDecayDelayMS = decayDelay;
+ if( !ch->HasBeenHit() )
+ {
+ rmt::Vector pos;
+ ch->GetPosition( pos );
+ GetCoinManager()->SpawnCoins( mKickNPCCoins, pos );
+ }
+ }
+ break;
+ case EVENT_PLAYER_CAR_HIT_NPC:
+ {
+ Character* ch = static_cast<Character*>(pEventData);
+ if(ch->IsNPC())
+ {
+ // TODO:
+ // Make sure this is correct by verifying with Jesse.
+ // We don't use NPCController::FLAILING state anymore..
+ // Need a diff mechanism
+ if( ch->GetStateManager()->GetState() != CharacterAi::INSIM )
+ {
+ mCurrHitnRun += mPlayerCarHitNPCDelta;
+ mDecayDelayMS = decayDelay;
+ }
+ /*
+ NPCharacter* npc = static_cast<NPCharacter*>(ch);
+ NPCController* npcCont = static_cast<NPCController*>(npc->GetController());
+ if(npcCont->GetState() != NPCController::FLAILING)
+ {
+ mCurrHitnRun += mPlayerCarHitNPCDelta;
+ mDecayDelayMS = decayDelay;
+ }
+ */
+ }
+ if( !ch->HasBeenHit() )
+ {
+ rmt::Vector pos;
+ ch->GetPosition( pos );
+ GetCoinManager()->SpawnCoins( mPlayerCarHitNPCCoins, pos );
+ }
+ }
+ break;
+ case EVENT_SWITCH_SKIN:
+ {
+ //switching to same skin not handled currently
+ mCurrHitnRun += mSwitchSkinDelta;
+ mDecayDelayMS = decayDelay;
+ }
+ break;
+ //case EVENT_GETINTOVEHICLE_END:
+ case EVENT_ENTERING_PLAYER_CAR:
+ case EVENT_ENTERING_TRAFFIC_CAR:
+ {
+ //have to track vehicle type to prevent getting in and out of same car
+ //Character* character = static_cast<Character*>(pEventData);
+ //Vehicle* vehicle = character->GetTargetVehicle();
+ Vehicle* vehicle = static_cast<Vehicle*>(pEventData);
+ if(mPrevVehicleID != vehicle->mVehicleID)
+ {
+ mCurrHitnRun += mChangeVehicleDelta;
+ mDecayDelayMS = decayDelay;
+ }
+ mPrevVehicleID = vehicle->mVehicleID;
+ }
+ break;
+ case EVENT_HIT_AND_RUN_CAUGHT: //triggered via collision system
+ {
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL && mChaseOn )
+ {
+ chaseManager->DisableAllActiveVehicleAIs();
+ chaseManager->MarkAllVehiclesForDeletion();
+ mCurrHitnRun = MIN_HITNRUN;
+ mSpawnOn = false;
+ mChaseOn = false;
+ GetCoinManager()->LoseCoins( mBustedCoins );
+
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer(0);
+ rAssert( player );
+ Character* ch = player->GetCharacter();
+
+ mVehicleResetTimer = mUnresponsiveTime;
+ mVehicleReset = false;
+
+ if(ch->GetStateManager()->GetState() == CharacterAi::INCAR)
+ {
+ Vehicle* playerVehicle = player->GetVehicle();
+ //make car unresponsive
+ playerVehicle->SetDisableGasAndBrake(true);
+ }
+ }
+ }
+ }
+ break;
+ case EVENT_CHASE_VEHICLE_SPAWNED:
+ {
+ mChaseOn = true;
+ }
+ break;
+ default:
+ {
+ }
+ break;
+ }
+ }
+
+ switch ( id ) //do a second switch because these events are handled whether h&r is disabled or not
+ {
+ //case EVENT_MISSION_START:
+ // {
+ // mLastHitnRunValue = mCurrHitnRun;
+ // }
+ // break;
+ //case EVENT_MISSION_RESET: //commented out because it happens all the time before EVENT_MISSION_START
+ // {
+ // if(mLastHitnRunValue < mCurrHitnRun)
+ // {
+ // mCurrHitnRun = mLastHitnRunValue;
+ // }
+ // mSpawnOn = false;
+ // mChaseOn = false;
+ // }
+ // break;
+ case EVENT_MISSION_START:
+ {
+ mCurrHitnRun = MIN_HITNRUN;
+ mSpawnOn = false;
+ mChaseOn = false;
+ mAllowThrob = true;
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->DisableAllActiveVehicleAIs();
+ chaseManager->MarkAllVehiclesForDeletion();
+ }
+ }
+ }
+ break;
+ case EVENT_MISSION_SUCCESS:
+ {
+ mCurrHitnRun = MIN_HITNRUN;
+ mSpawnOn = false;
+ mChaseOn = false;
+ mDecayDisabled = false;
+ mHitnRunDisabled = false;
+ mAllowThrob = true;
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->DisableAllActiveVehicleAIs();
+ chaseManager->MarkAllVehiclesForDeletion();
+ }
+ }
+ }
+ break;
+ //case EVENT_MISSION_FAILURE:
+ // {
+ // //if(mLastHitnRunValue < mCurrHitnRun)
+ // //{
+ // // mCurrHitnRun = mLastHitnRunValue;
+ // //}
+ // mCurrHitnRun = MIN_HITNRUN;
+ // mDecayDisabled = false;
+ // mHitnRunDisabled = false;
+ // mSpawnOn = false;
+ // mChaseOn = false;
+ // }
+ // break;
+ case EVENT_GUI_FADE_OUT_DONE:
+ {
+ mFadeTimer = FADE_TIME;
+ mFadeDone = false;
+ GetEventManager()->RemoveListener( this, EVENT_GUI_FADE_OUT_DONE );
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->ClearAllObjects();
+ }
+ }
+ }
+ break;
+ default:
+ {
+ }
+ break;
+
+ }
+ if(mCurrHitnRun < MIN_HITNRUN)
+ {
+ mCurrHitnRun = MIN_HITNRUN;
+ }
+ else if(mCurrHitnRun > MAX_HITNRUN)
+ {
+ mCurrHitnRun = MAX_HITNRUN;
+ }
+
+}
+
+
+
+
+
+/*=============================================================================
+Update the position/animation of all the coins.
+=============================================================================*/
+void HitnRunManager::Update( int elapsedMS )
+{
+ if(mHitnRunDisabled)
+ return;
+
+ if(mCurrHitnRun < MAX_HITNRUN * (STOP_HITNRUN/100.0f))
+ {
+ //do chase stuff
+ mSpawnOn = false;
+ }
+
+ if(mCurrHitnRun < ALLOW_THROB)
+ {
+ mAllowThrob = true;
+ }
+ else if(mAllowThrob && (mCurrHitnRun > (mWarningThreshold * 100.0f)))
+ {
+ mAllowThrob = false;
+ GetEventManager()->TriggerEvent( EVENT_HIT_AND_RUN_METER_THROB, NULL );
+ }
+
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ if(!mSpawnOn)
+ {
+ if(mCurrHitnRun >= MAX_HITNRUN)
+ {
+ //do chase stuff
+ mSpawnOn = true;
+ chaseManager->SetMaxObjects(mNumChaseCars);
+ chaseManager->SetActive(true);
+ chaseManager->EnableAdd( true );
+ chaseManager->EnableRemove( true );
+ chaseManager->EnableUpdate( true );
+ GetEventManager()->TriggerEvent( EVENT_HIT_AND_RUN_START, NULL );
+ }
+ else
+ {
+ chaseManager->SetMaxObjects(0); //!!!!!!!!!!!!!!!!!!!!!!!!!!!every frame!
+ //chaseManager->SetActive( true );
+ chaseManager->EnableAdd( false );
+ chaseManager->EnableRemove( true );
+ chaseManager->EnableUpdate( true );
+ }
+ }
+
+ if(mChaseOn)
+ {
+ int cars = chaseManager->GetNumActiveVehicles();
+ if(cars <= 0)
+ {
+ if(!mSpawnOn)
+ {
+ mChaseOn = false;
+ GetEventManager()->TriggerEvent( EVENT_HIT_AND_RUN_EVADED, NULL );
+ }
+ }
+ else
+ {
+ //If the player's vehicle speed is < CopTicketSpeedThreshold FOR CopTicketTimeThreshold seconds
+ //WHILE the distance from the nearest cop is < CopTicketDistance, you get ticketed
+
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer(0);
+ rAssert( player );
+
+ Vehicle* playerVehicle = player->GetVehicle();
+ if( playerVehicle != NULL )
+ {
+ float currSpeed = playerVehicle->mSpeedKmh;
+ float copTicketDistSqr = mCopTicketDistance * mCopTicketDistance;
+
+ // figure rough distance to player
+ rmt::Vector copPos, playerPos;
+ playerVehicle->GetPosition( &playerPos );
+ float distToPlayerSqr = chaseManager->GetClosestCarPosition( &playerPos, &copPos );
+
+ if( currSpeed < mCopTicketSpeedThreshold && distToPlayerSqr < copTicketDistSqr)
+ {
+ mTicketTimer += elapsedMS;
+ }
+ else
+ {
+ mTicketTimer = 0.0f;
+ }
+ //rTunePrintf("dist: %f, timer: %f\n", distToPlayerSqr, mTicketTimer);
+
+ if( mTicketTimer >= (mCopTicketTimeThreshold*1000.0f)) //convert from seconds to MS
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_AND_RUN_CAUGHT, NULL );
+ mTicketTimer = 0.0f;
+ }
+ }
+ else
+ {
+ float copTicketDistSqr = (mCopTicketDistanceOnFoot) * (mCopTicketDistanceOnFoot);
+
+ // figure rough distance to player
+ rmt::Vector copPos, playerPos;
+ player->GetPosition( playerPos );
+ float distToPlayerSqr = chaseManager->GetClosestCarPosition( &playerPos, &copPos );
+
+ if( distToPlayerSqr < copTicketDistSqr)
+ {
+ mTicketTimer += elapsedMS;
+ }
+ else
+ {
+ mTicketTimer = 0.0f;
+ }
+ //rTunePrintf("dist: %f, timer: %f\n", distToPlayerSqr, mTicketTimer);
+
+ if( mTicketTimer >= (mCopTicketTimeThreshold*1000.0f*2)) //convert from seconds to MS
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_AND_RUN_CAUGHT, NULL );
+ mTicketTimer = 0.0f;
+ }
+
+ }
+ }
+ }
+ else //if(!mChaseOn)
+ {
+ mTicketTimer = 0.0f;
+ }
+ }
+ }
+
+ if( fabsf(mLastUpdateValue - mCurrHitnRun) > 0.5f)
+ {
+ //update hud (before decreasing value)
+ CGuiScreenHud* hud = GetCurrentHud();
+ if(hud)
+ {
+ hud->SetHitAndRunMeter( mCurrHitnRun / MAX_HITNRUN );
+ mLastUpdateValue = mCurrHitnRun;
+ }
+ }
+
+ if(mDecayDelayMS <= 0.0f)
+ {
+ if( !mDecayDisabled )
+ {
+ if(mCurrHitnRun > MIN_HITNRUN)
+ {
+ float decayRatePerSecond = mDecayRatePerSecond;
+
+ if( GetInteriorManager()->IsInside() )
+ {
+ decayRatePerSecond = mDecayRateInsidePerSecond;
+ }
+ else if(mSpawnOn)
+ {
+ decayRatePerSecond = mDecayRateWhileSpawning;
+ }
+
+
+ float delta = decayRatePerSecond * elapsedMS * (-1.0f / 1000.0f);
+ mCurrHitnRun += delta;
+ if(mCurrHitnRun <= MIN_HITNRUN)
+ {
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ if(chaseManager->GetNumActiveVehicles() > 0)
+ {
+ chaseManager->MarkAllVehiclesForDeletion();
+ }
+ }
+ }
+ mCurrHitnRun = MIN_HITNRUN;
+ }
+ }
+ }
+ }
+ else
+ {
+ mDecayDelayMS -= elapsedMS;
+ }
+
+ if(!mVehicleReset)
+ {
+ if(mVehicleResetTimer <= 0.0f)
+ {
+ //reset vehicle
+ mVehicleReset = true;
+
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer(0);
+ rAssert( player );
+
+ if( player->IsInCar() )
+ {
+ Vehicle* playerVehicle = player->GetVehicle();
+ playerVehicle->SetDisableGasAndBrake(false);
+ GetGuiSystem()->HandleMessage( GUI_MSG_MANUAL_RESET, reinterpret_cast< unsigned int >(playerVehicle) );
+ GameplayManager* gameplayManager = GetGameplayManager();
+ if ( gameplayManager != NULL )
+ {
+ ChaseManager* chaseManager = gameplayManager->GetChaseManager(0);
+ if ( chaseManager != NULL )
+ {
+ chaseManager->ClearAllObjects();
+ }
+ }
+ }
+ else
+ {
+ GetEventManager()->AddListener( this, EVENT_GUI_FADE_OUT_DONE );
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Suspend();
+ GetGuiSystem()->HandleMessage( GUI_MSG_INGAME_FADE_OUT );
+ }
+
+ }
+ else
+ {
+ mVehicleResetTimer -= elapsedMS;
+ }
+ }
+
+ if(!mFadeDone)
+ {
+ if(mFadeTimer <= 0.0f)
+ {
+ //reset vehicle
+ mFadeDone = true;
+ GetGuiSystem()->HandleMessage( GUI_MSG_INGAME_FADE_IN );
+ GetGameFlow()->GetContext( CONTEXT_GAMEPLAY )->Resume();
+ }
+ else
+ {
+ mFadeTimer -= elapsedMS;
+ }
+ }
+
+}
+
+float HitnRunManager::GetHitnRunValue() const
+{
+ return mCurrHitnRun;
+}
+
+/*=============================================================================
+Modify the player's Hit and Run meter
+=============================================================================*/
+void HitnRunManager::AdjustHitnRunValue( float delta )
+{
+ mCurrHitnRun += delta;
+ if(mSpawnOn)
+ {
+ mDecayDelayMS = mDecayDelayWhileSpawning;
+ }
+ else
+ {
+ mDecayDelayMS = mDecayDelay;
+ }
+
+ if(mCurrHitnRun < MIN_HITNRUN)
+ {
+ mCurrHitnRun = MIN_HITNRUN;
+ }
+ else if(mCurrHitnRun > MAX_HITNRUN)
+ {
+ mCurrHitnRun = MAX_HITNRUN;
+ }
+
+}
+
+
+/*=============================================================================
+Force the player's Hit and Run meter to a specific value
+=============================================================================*/
+void HitnRunManager::SetHitnRunValue( float value )
+{
+ rAssert(value <= MAX_HITNRUN && value >= MIN_HITNRUN);
+ mCurrHitnRun = value;
+}
+
+void HitnRunManager::MaxHitnRunValue()
+{
+ mCurrHitnRun = MAX_HITNRUN;
+}
+
+bool HitnRunManager::HasntBeenHit( void* ptr )
+{
+ for(int i = 0; i < 16; i++)
+ {
+ if( mPrevHits[i] == ptr )
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+void HitnRunManager::RegisterHit( void* ptr )
+{
+ mPrevHits[mLeastRecentlyHit] = ptr;
+ mLeastRecentlyHit++;
+ if(mLeastRecentlyHit >= 16)
+ {
+ mLeastRecentlyHit = 0;
+ }
+}
+
+
+bool HitnRunManager::DoesObjectGiveCoins( CollisionEntityDSG* cedsg )
+{
+ bool givesCoins = true;
+
+ // We want to get the type name, not the instance name. Get the type name from a
+ // stateprop from its embedded CStateProp object
+ if ( cedsg->GetAIRef() == PhysicsAIRef::StateProp )
+ {
+ const StatePropDSG* spdsg = static_cast< const StatePropDSG* >( cedsg );
+ rAssert( dynamic_cast< StatePropDSG* >( cedsg ) != NULL );
+ tUID typeName = spdsg->GetStatePropUID();
+ for ( int i = 0 ; i < NUM_OBJECTS_THAT_NEVER_GIVE_COINS ; i++ )
+ {
+ if ( typeName == mObjectsThatNeverGiveCoins[i] )
+ {
+ // Its a match, this object does not give coins
+ givesCoins = false;
+ break;
+ }
+ }
+ }
+ return givesCoins;
+}
+
+void HitnRunManager::RegisterVehicleImmunity( Vehicle* v )
+{
+ mImmunityVehicle = v;
+}
diff --git a/game/code/worldsim/hitnrunmanager.h b/game/code/worldsim/hitnrunmanager.h
new file mode 100644
index 0000000..3fa4b5c
--- /dev/null
+++ b/game/code/worldsim/hitnrunmanager.h
@@ -0,0 +1,150 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: hitnrunmanager.h
+//
+// Description: Hit & Run manager looks after everything to do with the hit & run system
+//
+// History: 26/03/2003 + Created -- Jesse Cluff
+//
+//=============================================================================
+
+#ifndef HITNRUNMANAGER_H
+#define HITNRUNMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <events/eventlistener.h>
+#include <constants/breakablesenum.h>
+#include <constants/vehicleenum.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class CollisionEntityDSG;
+
+//=============================================================================
+//
+// Synopsis: Handling events that cause the hit & run value to change and spawning
+// chase vehicles when value exceeds a given threshold
+//
+//=============================================================================
+class HitnRunManager : public EventListener
+{
+public:
+ static HitnRunManager* GetInstance( void );
+ static HitnRunManager* CreateInstance( void );
+ static void DestroyInstance( void );
+
+ void Init( void );
+ void Destroy( void );
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ void Update( int elapsedMS ); // spawns chase vehicles if hit & run value is too high.
+ void HUDRender( void ); // Renders HUD data.
+
+ float GetHitnRunValue( void ) const; // What is the current Hit & Run value.
+ void AdjustHitnRunValue( float delta ); // Adjust the Hit & Run value.
+ void SetHitnRunValue( float value );
+ void MaxHitnRunValue(); //sets hitnrun meter to maximum value
+
+ void DisableMeterDecay() { mDecayDisabled = true; }
+ void EnableMeterDecay() { mDecayDisabled = false ; }
+
+ bool IsHitnRunActive(){ return mChaseOn; }
+ bool IsHitnRunDisabled(){ return mHitnRunDisabled; }
+
+ void ResetState();
+
+ void EnableHitnRun() { ResetState(); mHitnRunDisabled = false; }
+ void DisableHitnRun() { ResetState(); mHitnRunDisabled = true; }
+
+ void SetNumChaseCars( int num ) { mNumChaseCars = num; }
+ void SetDecayRate( float rate ) { mDecayRatePerSecond = rate; }
+ float GetDecayRate() {return mDecayRatePerSecond;}
+ void SetDecayRateInside( float rate ) { mDecayRateInsidePerSecond = rate; }
+ float GetDecayRateInside() {return mDecayRateInsidePerSecond;}
+
+ bool IsWaitingForReset() { return !mVehicleReset; }
+
+ bool HasntBeenHit( void* ptr );
+ void RegisterHit( void* ptr );
+
+ void RegisterVehicleImmunity( Vehicle* v );
+
+ float GetWarningThreshold() { return mWarningThreshold; }
+
+ bool BustingPlayer() { return ( mVehicleResetTimer > 0.0f || mFadeTimer > 0.0f ); }
+
+protected:
+ //Prevent wasteful constructor creation.
+ HitnRunManager( const HitnRunManager& That );
+ HitnRunManager& operator=( const HitnRunManager& That );
+
+ static HitnRunManager* smpHitnRunManager;
+
+private:
+ HitnRunManager();
+ ~HitnRunManager();
+
+ float mCurrHitnRun;
+ float mDecayRatePerSecond;
+ float mDecayRateWhileSpawning;
+ float mDecayRateInsidePerSecond;
+ float mVehicleDestroyedDelta;
+ int mVehicleDestroyedCoins;
+ float mVehicleHitDelta;
+ float mHitBreakableDelta;
+ int mHitBreakableCoins;
+ float mHitMoveableDelta;
+ int mHitMoveableCoins;
+ float mHitKrustyGlassDelta;
+ int mHitKrustyGlassCoins;
+ float mColaPropDestroyedDelta;
+ int mColaPropDestroyedCoins;
+ float mKickNPCDelta;
+ int mKickNPCCoins;
+ float mPlayerCarHitNPCDelta;
+ int mPlayerCarHitNPCCoins;
+ int mBustedCoins;
+ float mSwitchSkinDelta;
+ float mChangeVehicleDelta;
+ VehicleEnum::VehicleID mPrevVehicleID;
+ //Vehicle* mPrevVehicleHit;
+ //CollisionEntityDSG* mPrevMoveableHit;
+ void* mPrevHits[16];
+ int mLeastRecentlyHit;
+ bool mChaseOn;
+ bool mSpawnOn;
+ float mDecayDelayMS;
+ float mDecayDelay;
+ float mDecayDelayWhileSpawning;
+ int mNumChaseCars;
+ bool mHitnRunDisabled;
+ bool mDecayDisabled;
+ float mCopTicketDistance;
+ float mCopTicketDistanceOnFoot;
+ float mCopTicketSpeedThreshold;
+ float mCopTicketTimeThreshold;
+ float mTicketTimer;
+ float mLastUpdateValue;
+ float mLastHitnRunValue;
+ bool mVehicleReset;
+ float mVehicleResetTimer;
+ float mUnresponsiveTime;
+ bool mFadeDone;
+ float mFadeTimer;
+
+ tUID* mObjectsThatNeverGiveCoins;
+ bool DoesObjectGiveCoins( CollisionEntityDSG* ); //const
+
+ Vehicle* mImmunityVehicle;
+ float mWarningThreshold;
+ bool mAllowThrob;
+};
+
+inline HitnRunManager* GetHitnRunManager() { return( HitnRunManager::GetInstance() ); }
+
+#endif //HITNRUNMANAGER_H
diff --git a/game/code/worldsim/huskpool.cpp b/game/code/worldsim/huskpool.cpp
new file mode 100644
index 0000000..4a7cdd1
--- /dev/null
+++ b/game/code/worldsim/huskpool.cpp
@@ -0,0 +1,309 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: huskpool.cpp
+//
+// Description:
+//
+// History: Mar 27, 2003 - created, gmayer
+//
+//=============================================================================
+
+
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/huskpool.h>
+#include <worldsim/redbrick/trafficlocomotion.h>
+
+#include <memory/srrmemory.h>
+
+#include <mission/gameplaymanager.h>
+
+
+//=============================================================================
+// HuskPool::HuskPool
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int num)
+//
+// Return: HuskPool
+//
+//=============================================================================
+HuskPool::HuskPool()
+{
+ mTotalNum = 0;
+}
+
+
+//=============================================================================
+// ::~HuskPool
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: HuskPool
+//
+//=============================================================================
+HuskPool::~HuskPool()
+{
+
+}
+
+
+//=============================================================================
+// HuskPool::Init
+//=============================================================================
+// Description: Comment
+//
+// this has to be called from GameplayContext and DemoContext OnStart
+
+//
+// Parameters: (int num)
+//
+// Return: void
+//
+//=============================================================================
+void HuskPool::Init(int num)
+{
+
+MEMTRACK_PUSH_GROUP( "HuskPool Init" );
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ HeapMgr()->PushHeap (gma);
+
+ mTotalNum = num;
+
+ mHuskArray = new HuskData[num];
+
+ for( int i=0; i<num; i++)
+ {
+ // initially make all husks VT_TRAFFIC
+ // set the husk type to whatever the vehicle that just got destroyed is...
+
+ Vehicle* huskVehicle = GetVehicleCentral()->InitVehicle("huskA", false, 0, VT_TRAFFIC);
+ rAssert( huskVehicle );
+
+ huskVehicle->AddRef();
+ huskVehicle->SetLocomotion( VL_PHYSICS );
+#ifdef DEBUGWATCH
+ // HACK:
+ // Automatically take it out of vehicleAIRender, so we don't
+ // clutter up the render slots
+ huskVehicle->mTrafficLocomotion->GetAI()->UnregisterAI();
+#endif
+
+ mHuskArray[i].huskVehicle = huskVehicle;
+ mHuskArray[i].originalVehicle = NULL;
+ mHuskArray[i].inUse = false;
+ }
+ HeapMgr()->PopHeap (gma);
+
+MEMTRACK_PUSH_GROUP( "HuskPool Init" );
+
+}
+
+
+//=============================================================================
+// HuskPool::Empty
+//=============================================================================
+// Description: Comment
+//
+//
+// called from PauseContext and DemoContext OnStop
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void HuskPool::Empty()
+{
+ // called when you're leaving the level
+ int i;
+ for(i = 0; i < mTotalNum; i++)
+ {
+ // TODO:
+ // Should do this just in case?
+ //GetVehicleCentral()->RemoveVehicleFromActiveList( mHuskArray[i].huskVehicle );
+
+ mHuskArray[i].huskVehicle->ReleaseVerified();
+ mHuskArray[i].huskVehicle = NULL;
+
+ if( mHuskArray[i].originalVehicle )
+ {
+ mHuskArray[i].originalVehicle->Release();
+ mHuskArray[i].originalVehicle = NULL;
+ }
+ }
+
+ mTotalNum = 0;
+ delete[] mHuskArray;
+}
+
+
+//=============================================================================
+// HuskPool::RequestHusk
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* HuskPool::RequestHusk( VehicleType vt, Vehicle* originalVehicle )
+{
+ rAssert( originalVehicle );
+ if( originalVehicle == NULL )
+ {
+ return NULL;
+ }
+
+ if( !WillMakeConvincingHusk( originalVehicle ) )
+ {
+ return NULL;
+ }
+
+ for( int i = 0; i < mTotalNum; i++ )
+ {
+ if(mHuskArray[i].inUse == false)
+ {
+ // give 'em this one
+ mHuskArray[i].inUse = true;
+ mHuskArray[i].huskVehicle->mVehicleType = vt; // I'm pretty sure this is safe to set on the fly
+ mHuskArray[i].huskVehicle->mGeometryVehicle->SetFadeAlpha( 255 ); // restore vehicle fade alpha
+
+ // store away the original vehicle
+ rAssert( mHuskArray[i].originalVehicle == NULL );
+ mHuskArray[i].originalVehicle = originalVehicle;
+ mHuskArray[i].originalVehicle->AddRef();
+
+ return mHuskArray[i].huskVehicle;
+ }
+ }
+
+ //rReleaseAssertMsg(0, "Not enough husks to go around!");
+ return NULL;
+}
+
+
+//=============================================================================
+// HuskPool::FreeHusk
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (Vehicle* husk)
+//
+// Return: void
+//
+//=============================================================================
+void HuskPool::FreeHusk(Vehicle* husk)
+{
+ if( husk == NULL )
+ {
+ return;
+ }
+
+ for( int i = 0; i < mTotalNum; i++)
+ {
+ if( mHuskArray[i].huskVehicle == husk &&
+ mHuskArray[i].inUse )
+ {
+ mHuskArray[i].inUse = false;
+
+ rAssert( mHuskArray[i].originalVehicle );
+ mHuskArray[i].originalVehicle->Release();
+ mHuskArray[i].originalVehicle = NULL;
+
+ return;
+ }
+ }
+}
+
+bool HuskPool::IsHuskType( VehicleEnum::VehicleID id )
+{
+ // NOTE:
+ // If we start using other Husk models other than "huskA",
+ // we add the ID checks here...
+ if( id == VehicleEnum::HUSKA )
+ {
+ return true;
+ }
+ return false;
+}
+
+
+Vehicle* HuskPool::FindOriginalVehicleGivenHusk( Vehicle* husk )
+{
+ if( husk == NULL )
+ {
+ return NULL;
+ }
+
+ for( int i = 0; i < mTotalNum; i++)
+ {
+ if(mHuskArray[i].huskVehicle == husk)
+ {
+ rAssert( mHuskArray[i].inUse );
+ rAssert( mHuskArray[i].originalVehicle );
+ return mHuskArray[i].originalVehicle;
+ }
+ }
+
+ return NULL;
+}
+
+Vehicle* HuskPool::FindHuskGivenOriginalVehicle( Vehicle* v )
+{
+ if( v == NULL )
+ {
+ return NULL;
+ }
+
+ for( int i = 0; i < mTotalNum; i++)
+ {
+ if(mHuskArray[i].originalVehicle == v)
+ {
+ rAssert( mHuskArray[i].inUse );
+ rAssert( mHuskArray[i].huskVehicle );
+ return mHuskArray[i].huskVehicle;
+ }
+ }
+
+ return NULL;
+
+}
+
+
+bool HuskPool::WillMakeConvincingHusk( Vehicle* origV )
+{
+ // these cars don't look anything remotely like
+ // HuskA, so we blow them up and leave nothing behind
+
+ if( origV->mVehicleID == VehicleEnum::HONOR_V ||
+ origV->mVehicleID == VehicleEnum::SCHOOLBU ||
+ origV->mVehicleID == VehicleEnum::WILLI_V ||
+ origV->mVehicleID == VehicleEnum::HALLO ||
+ origV->mVehicleID == VehicleEnum::OBLIT_V ||
+
+ origV->mVehicleID == VehicleEnum::MONO_V ||
+ origV->mVehicleID == VehicleEnum::KNIGH_V ||
+ origV->mVehicleID == VehicleEnum::CFIRE_V ||
+ origV->mVehicleID == VehicleEnum::CCOLA ||
+ origV->mVehicleID == VehicleEnum::HBIKE_V ||
+
+ origV->mVehicleID == VehicleEnum::ROCKE_V ||
+ origV->mVehicleID == VehicleEnum::ATV_V ||
+ origV->mVehicleID == VehicleEnum::DUNE_V ||
+ origV->mVehicleID == VehicleEnum::COFFIN ||
+ origV->mVehicleID == VehicleEnum::SHIP ||
+
+ origV->mVehicleID == VehicleEnum::WITCHCAR )
+ {
+ return false;
+ }
+ return true;
+} \ No newline at end of file
diff --git a/game/code/worldsim/huskpool.h b/game/code/worldsim/huskpool.h
new file mode 100644
index 0000000..7c582ac
--- /dev/null
+++ b/game/code/worldsim/huskpool.h
@@ -0,0 +1,56 @@
+//=============================================================================
+// Copyright (C) 2003 Radical Entertainment Ltd. All rights reserved.
+//
+// File: huskpool.h
+//
+// Description:
+//
+// History: Mar 27, 2003 - created, gmayer
+//
+//=============================================================================
+
+#ifndef HUSKPOOL_H
+#define HUSKPOOL_H
+
+
+
+#include <worldsim/redbrick/vehicle.h>
+
+// vehicle central can own this
+class HuskPool
+{
+public:
+
+ HuskPool();
+ ~HuskPool();
+
+ void Init(int num); // how big the pool should be - the max number of simultaneous husks we ever want to have
+ void Empty(); // empty lists - for the OnStop..
+
+ Vehicle* RequestHusk( VehicleType vt, Vehicle* originalVehicle );
+ void FreeHusk( Vehicle* husk );
+
+ Vehicle* FindOriginalVehicleGivenHusk( Vehicle* husk );
+ Vehicle* FindHuskGivenOriginalVehicle( Vehicle* v );
+
+ bool IsHuskType( VehicleEnum::VehicleID id );
+
+private:
+ bool WillMakeConvincingHusk( Vehicle* origV );
+
+private:
+ int mTotalNum;
+
+ struct HuskData
+ {
+ Vehicle* originalVehicle;
+ Vehicle* huskVehicle;
+ bool inUse;
+ };
+
+ HuskData* mHuskArray;
+
+};
+
+
+#endif // HUSKPOOL_H
diff --git a/game/code/worldsim/parkedcars/allparkedcars.cpp b/game/code/worldsim/parkedcars/allparkedcars.cpp
new file mode 100644
index 0000000..1318e1e
--- /dev/null
+++ b/game/code/worldsim/parkedcars/allparkedcars.cpp
@@ -0,0 +1 @@
+#include <worldsim/parkedcars/parkedcarmanager.cpp> \ No newline at end of file
diff --git a/game/code/worldsim/parkedcars/parkedcarmanager.cpp b/game/code/worldsim/parkedcars/parkedcarmanager.cpp
new file mode 100644
index 0000000..6d959d0
--- /dev/null
+++ b/game/code/worldsim/parkedcars/parkedcarmanager.cpp
@@ -0,0 +1,943 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: parkedcarmanager.cpp
+//
+// Description: Implement ParkedCarManager
+//
+// History: 2/6/2003 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <radmath/radmath.hpp>
+#include <raddebug.hpp>
+#include <string.h>
+#include <stdlib.h>
+#include <p3d/utility.hpp>
+#include <pddi/pdditype.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <worldsim/parkedcars/parkedcarmanager.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+
+#include <memory/srrmemory.h>
+
+#include <events/eventmanager.h>
+
+#include <render/rendermanager/rendermanager.h>
+#include <render/rendermanager/worldrenderlayer.h>
+
+#include <meta/carstartlocator.h>
+
+#include <main/commandlineoptions.h>
+
+#include <mission/gameplaymanager.h>
+
+#include <cheats/cheatinputsystem.h>
+
+#include <presentation/gui/ingame/guimanageringame.h>
+#include <presentation/gui/ingame/guiscreenhud.h>
+#include <presentation/gui/utility/hudmap.h>
+
+static const char FREE_CAR_SECTION[] = "FreeCar";
+
+//TODO: SHOULD I USE VT_USER?
+
+//*****************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//*****************************************************************************
+
+ParkedCarManager* ParkedCarManager::spInstance = NULL;
+
+//*****************************************************************************
+//
+// Public Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ParkedCarManager::GetInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: ParkedCarManager
+//
+//=============================================================================
+ParkedCarManager& ParkedCarManager::GetInstance()
+{
+ if ( spInstance == NULL )
+ {
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ spInstance = new ParkedCarManager();
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+ }
+
+ return *spInstance;
+}
+
+//=============================================================================
+// ParkedCarManager::DestroyInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::DestroyInstance()
+{
+ if ( spInstance != NULL )
+ {
+ delete spInstance;
+ spInstance = NULL;
+ }
+}
+
+//=============================================================================
+// ParkedCarManager::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ switch ( id )
+ {
+ case EVENT_DYNAMIC_ZONE_LOAD_ENDED:
+ {
+ //Now we've finished loading locators (maybe)
+ //Let's choose some for this zone and reset the data.
+ if ( mNumLocators > 0 )
+ {
+ //Choose a locator or two.
+ unsigned int max = mNumCarTypes / MAX_ZONES;
+ bool isOdd = mNumCarTypes % 2 == 1;
+ if ( isOdd || mNumCarTypes < MAX_ZONES ) //This is an odd number
+ {
+ max += 1;
+ }
+
+ //Allocate the max num of parked cars among these locators.
+ unsigned int i;
+ unsigned int allocated = 0;
+ for ( i = 0; i < mNumLocators && i < max && allocated < mNumCarTypes - mNumParkedCars; ++i )
+ {
+ unsigned int which = rand() % mNumLocators;
+ if ( !(mLocators[ which ]->GetFlag( Locator::ACTIVE )) )
+ {
+ //Find another one, this one is used.
+ unsigned int j;
+ for ( j = (which + 1) % mNumLocators; j != which; j = (j + 1) % mNumLocators )
+ {
+ if ( mLocators[ j ]->GetFlag( Locator::ACTIVE ) )
+ {
+ which = j;
+ break;
+ }
+ }
+ }
+
+ if ( CommandLineOptions::Get( CLO_PARKED_CAR_TEST ) || rand() % mNumCarTypes <= 2 ) //Weight it to placing the car.
+ {
+ //Mark the locator as used.
+
+ //Park this one!
+ Vehicle* car = NULL;
+ ParkedCarInfo* info = NULL;
+
+ //Find an available car
+ unsigned int j;
+ for ( j = 0; j < mNumCarTypes; ++j )
+ {
+ bool inUseByPlayer = false;
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ inUseByPlayer = ( avatar &&
+ avatar->GetCharacter()->IsInCar() &&
+ avatar->GetVehicle() == mParkedCars[ j ].mCar );
+
+ if ( mParkedCars[ j ].mLoadedZoneUID == static_cast< tUID >( 0 ) && !inUseByPlayer )
+ {
+ //We found one!
+ info = &mParkedCars[ j ];
+ car = mParkedCars[ j ].mCar;
+ mParkedCars[ j ].mLoadedZoneUID = mLoadingZoneUID;
+ break;
+ }
+ }
+
+ //rAssert( car ); //If this fails then mNumParkedCars is invalid or there's mem stompage
+
+ if ( car )
+ {
+ //Assign a new random colour
+ pddiColour colour;
+ TrafficManager::GetInstance()->GenerateRandomColour( colour );
+ car->mGeometryVehicle->SetTrafficBodyColour( colour );
+
+ int added = GetVehicleCentral()->AddVehicleToActiveList( car );
+ rAssert( added != -1 );
+ //info->mActiveListIndex = added;
+
+ //Set the position and facing and reset.
+ rmt::Vector location;
+ mLocators[ which ]->GetLocation( &location );
+ car->SetInitialPositionGroundOffsetAutoAdjust( &location );
+
+ car->SetResetFacingInRadians( mLocators[ which ]->GetRotation() );
+
+ car->Reset();
+
+ car->GetSimState()->SetControl( sim::simSimulationCtrl );
+
+ //Turn off the headlights
+ car->mGeometryVehicle->EnableLights( false );
+
+ ++mNumParkedCars;
+ ++allocated;
+ }
+ }
+ }
+
+ //Remove all the locators
+ for ( i = 0; i < mNumLocators; ++i )
+ {
+ p3d::inventory->Remove( mLocators[ i ] );
+ }
+
+ //Reset the data.
+ mLoadingZoneUID = 0;
+ mNumLocators = 0;
+ }
+ break;
+ }
+ case EVENT_DUMP_DYNA_SECTION:
+ {
+ //Make all the parked cars in the dynazone available to the new zone.
+ tUID sectionNameUID = static_cast<tName*>(pEventData)->GetUID();
+ unsigned int i;
+ for ( i = 0; i < mNumCarTypes; ++i )
+ {
+ if ( mParkedCars[ i ].mLoadedZoneUID == sectionNameUID )
+ {
+ //Make this puppy available.
+ mParkedCars[ i ].mLoadedZoneUID = 0;
+ mNumParkedCars--;
+
+ if( mParkedCars[ i ].mHusk )
+ {
+ // remove husk
+ bool succeeded = GetVehicleCentral()->RemoveVehicleFromActiveList( mParkedCars[ i ].mHusk );
+ rAssert( succeeded );
+
+ ::GetVehicleCentral()->mHuskPool.FreeHusk( mParkedCars[ i ].mHusk );
+ mParkedCars[ i ].mHusk->Release();
+ mParkedCars[ i ].mHusk = NULL;
+ }
+ }
+ }
+
+ //Now test to see if we should dump the free car too.
+ if (
+ (sectionNameUID == mFreeCar.mLoadedZoneUID || mFreeCar.mLoadedZoneUID == static_cast< tUID > ( 0 ) )
+ &&
+ (mFreeCar.mCar != NULL)
+ )
+
+ {
+ //Can we dump this car?
+ bool dump = true;
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar );
+
+ if ( ( GetGameplayManager()->GetCurrentVehicle() == mFreeCar.mCar ) ||
+ ( GetVehicleCentral()->IsCarUnderConstruction(mFreeCar.mCar) == true ) )
+ {
+ dump = false;
+ }
+ else
+ {
+ //Either the guy's not in the car or he's in another car.
+ rmt::Vector charToCar;
+ rmt::Vector carPos, charPos;
+ avatar->GetPosition( charPos );
+ rTuneAssertMsg(mFreeCar.mCar != NULL,"Attempting to calculate distance from Null free car to Character, Crash Imminent\n");
+ mFreeCar.mCar->GetPosition( &carPos );
+
+ charToCar.Sub( carPos, charPos );
+
+ const float minDist = 200.0f * 200.0f;
+ if ( charToCar.MagnitudeSqr() > minDist )
+ {
+ dump = true;
+ }
+ }
+
+ if ( dump )
+ {
+ RemoveFreeCar();
+ p3d::inventory->RemoveSectionElements( FREE_CAR_SECTION );
+ }
+
+ //Reset this as it is either a message that we can try to dump the car,
+ //or the car is already gone.
+ mFreeCar.mLoadedZoneUID = 0;
+
+ if ( mFreeCarLocator )
+ {
+ mFreeCarLocator->Release();
+ mFreeCarLocator = NULL;
+ }
+ }
+ break;
+ }
+
+ case EVENT_VEHICLE_DESTROYED:
+ {
+ Vehicle* v = (Vehicle*) pEventData;
+ rAssert( v );
+
+ // the destroyed vehicle could be a free "moment" car or
+ // a normal parked traffic car. We must search for both
+
+ ParkedCarInfo* info = NULL;
+ bool isFreeCar = false;
+ FindParkedCarInfo( v, info, isFreeCar );
+
+ // if we never found it, this event doesn't concern us
+ if( info == NULL )
+ {
+ break;
+ }
+ // otherwise, it sure does...
+
+ // First, grab some information about the car
+ rmt::Vector initPos, initDir;
+ v->GetPosition( &initPos );
+ initDir = v->GetFacing();
+ rAssert( rmt::Epsilon( initDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // Now remove the old car from the activelist..
+ bool succeeded = GetVehicleCentral()->RemoveVehicleFromActiveList( v );
+ rAssert( succeeded );
+ //GetVehicleCentral()->SetVehicleController( info->mActiveListIndex, NULL );
+ //info->mActiveListIndex = -1;
+
+ tUID zoneUID = info->mLoadedZoneUID;
+ info->mLoadedZoneUID = 0;
+ if( !isFreeCar )
+ {
+ mNumParkedCars--;
+ }
+
+ // Now grab a husk (if available), add it to the activelist,
+ // and place it exactly where theother car is...
+
+ Vehicle* husk = NULL;
+
+ // since we are just now destroying this car, it shouldn't have
+ // a husk yet.
+ rAssert( info->mHusk == NULL );
+ if( info->mHusk )
+ {
+ // weird! we shouldn't be here.. but we'll handle it gracefully
+ // otherwise we'll leak.
+ husk = info->mHusk;
+ }
+ else
+ {
+ husk = GetVehicleCentral()->mHuskPool.RequestHusk( VT_TRAFFIC, v );
+ if( husk )
+ {
+ husk->AddRef();
+ }
+ }
+
+ if( husk )
+ {
+ int res = GetVehicleCentral()->AddVehicleToActiveList( husk );
+ if( res == -1 )
+ {
+ // not supposed to happen since the list can't be full!!!
+ // we TOOK something out of the list before adding something in
+ // If this assert happens, it is both fatal and strange
+ rAssert( false );
+ break;
+ }
+ //info->mActiveListIndex = res;
+
+ husk->SetInitialPosition( &initPos );
+ float angle = GetRotationAboutY( initDir.x, initDir.z );
+ husk->SetResetFacingInRadians( angle );
+ husk->Reset();
+ husk->SetLocomotion( VL_PHYSICS );
+
+ info->mHusk = husk;
+ info->mLoadedZoneUID = zoneUID;
+
+ // add back the numparkedcars
+ mNumParkedCars++;
+ }
+ }
+ break;
+
+ case EVENT_REPAIR_CAR:
+ {
+ // NOTE: When we want to support other cars than the user car
+ // picking up Wrench/repair icons in the main game, we should pass in
+ // the vehicle pointer when we trigger this event.
+ //
+ Vehicle* v = GetGameplayManager()->GetCurrentVehicle();
+ if( v == NULL )
+ {
+ return; // no current vehicle to repair.. oh well...
+ }
+
+ ParkedCarInfo* info = NULL;
+ bool isFreeCar = false;
+ FindParkedCarInfo( v, info, isFreeCar );
+
+ if( info == NULL )
+ {
+ return;
+ }
+
+ // if the vehicle is a husk.. gotta replace husk with original vehicle
+ if( v->mVehicleDestroyed )
+ {
+ if( info->mHusk )
+ {
+ // Destroyed, but has a husk
+ // Husk had to have existed to get to this point
+ Vehicle* husk = info->mHusk;
+
+ // obtain info from the husk
+ rmt::Vector initPos, initDir;
+ husk->GetPosition( &initPos );
+ initDir = husk->GetFacing();
+ rAssert( rmt::Epsilon( initDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // remove husk
+ bool succeeded = GetVehicleCentral()->RemoveVehicleFromActiveList( husk );
+ rAssert( succeeded );
+ //GetVehicleCentral()->SetVehicleController( info->mActiveListIndex, NULL );
+ //info->mActiveListIndex = -1;
+
+ ::GetVehicleCentral()->mHuskPool.FreeHusk( husk );
+ husk->Release();
+ husk = NULL;
+ info->mHusk = NULL;
+
+ v->SetInitialPosition( &initPos );
+ float angle = GetRotationAboutY( initDir.x, initDir.z );
+ v->SetResetFacingInRadians( angle );
+ v->Reset();
+
+ }
+
+ // put in original vehicle
+ int res = GetVehicleCentral()->AddVehicleToActiveList( v );
+ if( res == -1 )
+ {
+ // not supposed to happen since the list can't be full!!!
+ // we TOOK something out of the list before adding something in
+ // If this assert happens, it is both fatal and strange
+ rAssert( false );
+ return;
+ }
+ //info->mActiveListIndex = res;
+
+ // if the avatar is inside a vehicle the vehicle
+ // is probably a husk, update this vehicle to be the original
+ // vehicle and place the character in this new vehicle
+ //
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar );
+ if( avatar->IsInCar() )
+ {
+ rAssert( avatar->GetVehicle() );
+ rAssert( GetVehicleCentral()->mHuskPool.IsHuskType( avatar->GetVehicle()->mVehicleID ) );
+
+ avatar->SetVehicle( v );
+
+ Character* character = avatar->GetCharacter();
+ GetAvatarManager()->PutCharacterInCar( character, v );
+ }
+
+ // fire off event so Esan can know when we switch the vehicle on him.
+ GetEventManager()->TriggerEvent(
+ EVENT_VEHICLE_DESTROYED_SYNC_SOUND, v );
+
+ }
+ // repair any damage to original vehicle
+ bool resetDamage = true;
+ v->ResetFlagsOnly( resetDamage );
+ }
+ break;
+
+ default:
+ {
+ rAssert( false ); //Why?
+ break;
+ }
+ }
+}
+
+void ParkedCarManager::FindParkedCarInfo( Vehicle* v, ParkedCarInfo*& info, bool& isFreeCar )
+{
+ info = NULL;
+ isFreeCar = false;
+
+ if( mFreeCar.mCar == v )
+ {
+ info = &mFreeCar;
+ isFreeCar = true;
+ }
+ if( info == NULL )
+ {
+ for( unsigned int i=0; i<mNumCarTypes; i++ )
+ {
+ if( mParkedCars[i].mCar == v )
+ {
+ // found it
+ info = &(mParkedCars[i]);
+ break;
+ }
+ }
+ }
+}
+
+
+
+//=============================================================================
+// ParkedCarManager::OnProcessRequestsComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::OnProcessRequestsComplete( void* pUserData )
+{
+ if ( mFreeCar.mLoadedZoneUID != static_cast< tUID >( 0 ) )
+ {
+ //Create the car.
+ CreateFreeCar();
+ }
+ else
+ {
+ //Throw this data away...
+ rAssert( false );
+
+ //Fragment.
+ p3d::inventory->RemoveSectionElements( FREE_CAR_SECTION );
+ }
+}
+
+
+//=============================================================================
+// ParkedCarManager::AddCarType
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::AddCarType( const char* name )
+{
+ //return;
+
+#ifdef RAD_DEBUG
+ if ( CommandLineOptions::Get( CLO_PARKED_CAR_TEST ) )
+ {
+ rAssert( mNumCarTypes < NUM_TEST_PARKED_CARS );
+ }
+ else
+ {
+ rAssert( mNumCarTypes < MAX_DIFFERENT_CARS );
+ }
+#endif
+
+ rAssert( strlen( name ) < MAX_CAR_NAME_LEN );
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+
+ unsigned int i;
+ unsigned int iterations = CommandLineOptions::Get( CLO_PARKED_CAR_TEST ) ? NUM_TEST_PARKED_CARS / MAX_DIFFERENT_CARS : 1;
+ for ( i = 0; i < iterations; ++i )
+ {
+ //Set the name.
+ unsigned int nameLen = strlen( name ) > MAX_CAR_NAME_LEN ? MAX_CAR_NAME_LEN : strlen( name );
+ strncpy( mParkedCars[ mNumCarTypes ].mName, name, nameLen );
+ mParkedCars[ mNumCarTypes ].mName[ nameLen ] = '\0';
+
+ mParkedCars[ mNumCarTypes ].mLoadedZoneUID = 0;
+
+ Vehicle* car = GetVehicleCentral()->InitVehicle( mParkedCars[ mNumCarTypes ].mName, false, NULL, VT_TRAFFIC ); //TODO: Should I change the NULL to a con file?
+ rAssert( car );
+ car->mCreatedByParkedCarManager = true;
+
+ car->AddRef();
+
+ if ( car )
+ {
+ mParkedCars[ mNumCarTypes ].mCar = car;
+ ++mNumCarTypes;
+ }
+ }
+
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+}
+
+//=============================================================================
+// ParkedCarManager::AddLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( CarStartLocator* loc )
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::AddLocator( CarStartLocator* loc )
+{
+ if(!mParkedCarsEnabled)
+ {
+ return;
+ }
+
+ //return;
+ rAssert( mNumLocators < MAX_LOCATORS_PER_ZONE );
+
+ //Adding locators from the same dyna zone.
+ if ( mNumLocators < MAX_LOCATORS_PER_ZONE )
+ {
+ mLocators[ mNumLocators ] = loc;
+ ++mNumLocators;
+ }
+
+ if ( mLoadingZoneUID == static_cast< tUID >( 0 ) )
+ {
+ //Set the zone ID
+ mLoadingZoneUID = GetRenderManager()->pWorldRenderLayer()->GetCurSectionName().GetUID();
+ }
+}
+
+//=============================================================================
+// ParkedCarManager::AddFreeCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name, CarStartLocator* loc )
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::AddFreeCar( const char* name, CarStartLocator* loc )
+{
+ if ( GetCheatInputSystem()->IsCheatEnabled( CHEAT_ID_REDBRICK ) )
+ {
+ //This Bud's for you.
+ name = "redbrick";
+ }
+
+ if(!mParkedCarsEnabled)
+ {
+ return;
+ }
+
+ if ( mFreeCar.mCar == NULL )
+ {
+ unsigned int nameLen = strlen( name ) > MAX_CAR_NAME_LEN ? MAX_CAR_NAME_LEN : strlen( name );
+ strncpy( mFreeCar.mName, name, nameLen );
+ mFreeCar.mName[ nameLen ] = '\0';
+
+ mFreeCar.mLoadedZoneUID = GetRenderManager()->pWorldRenderLayer()->GetCurSectionName().GetUID();
+ rAssert( mFreeCar.mLoadedZoneUID != static_cast< tUID >( 0 ) );
+
+ tRefCounted::Assign( mFreeCarLocator, loc );
+
+ //Try to load the car
+ char fileName[MAX_CAR_NAME_LEN + 5 + 32];
+ sprintf( fileName, "art\\cars\\%s.p3d", name );
+ GetLoadingManager()->AddRequest( FILEHANDLER_LEVEL, fileName, GMA_LEVEL_OTHER, FREE_CAR_SECTION, FREE_CAR_SECTION, this );
+ }
+}
+
+//=============================================================================
+// ParkedCarManager::RemoveFreeCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::RemoveFreeCar()
+{
+ //TODO: Do I need this? What if I make sure the car is not in the view frustrum?
+ //p3d::pddi->DrawSync();
+ if ( mFreeCar.mCar != NULL )
+ {
+ if( mFreeCar.mHusk == NULL )
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList( mFreeCar.mCar );
+ }
+ else
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList( mFreeCar.mHusk );
+
+ GetVehicleCentral()->mHuskPool.FreeHusk( mFreeCar.mHusk );
+ mFreeCar.mHusk->Release();
+ mFreeCar.mHusk = NULL;
+ }
+ /*
+ if( mFreeCar.mActiveListIndex != -1 )
+ {
+ GetVehicleCentral()->SetVehicleController( mFreeCar.mActiveListIndex, NULL );
+ }
+ mFreeCar.mActiveListIndex = -1;
+ */
+
+ CGuiScreenHud* currentHud = GetCurrentHud();
+ if( currentHud != NULL )
+ {
+ HudMapIcon* playerCarIcon = currentHud->GetHudMap( 0 )->FindIcon( HudMapIcon::ICON_PLAYER_CAR );
+ if( playerCarIcon != NULL && playerCarIcon->m_dynamicLocator == mFreeCar.mCar )
+ {
+ GetGameplayManager()->UnregisterVehicleHUDIcon();
+ }
+ }
+
+ mFreeCar.mCar->Release();
+ mFreeCar.mCar = NULL;
+ mFreeCar.mLoadedZoneUID = 0;
+ }
+
+ if ( mFreeCarLocator )
+ {
+ mFreeCarLocator->Release();
+ mFreeCarLocator = NULL;
+ }
+}
+
+void ParkedCarManager::RemoveFreeCarIfClose( const rmt::Vector& position )
+{
+ // where is the free car?
+ rmt::Vector freeCarPosition;
+ if ( mFreeCar.mCar != NULL )
+ {
+ if(mFreeCar.mCar == GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle())
+ {
+ return;
+ }
+
+ mFreeCar.mCar->GetPosition( &freeCarPosition );
+ }
+ if( ( position - freeCarPosition ).MagnitudeSqr() < 10.0 * 10.0 )
+ {
+ RemoveFreeCar();
+ }
+}
+
+//*****************************************************************************
+//
+// Private Member Functions
+//
+//*****************************************************************************
+
+//=============================================================================
+// ParkedCarManager::ParkedCarManager
+//=============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ParkedCarManager::ParkedCarManager() :
+ mNumCarTypes( 0 ),
+ mNumParkedCars( 0 ),
+ mNumLocators( 0 ),
+ mLoadingZoneUID( 0 ),
+ mFreeCarLocator( NULL )
+{
+ GetEventManager()->AddListener( this, EVENT_DUMP_DYNA_SECTION );
+ GetEventManager()->AddListener( this, EVENT_DYNAMIC_ZONE_LOAD_ENDED );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_REPAIR_CAR );
+
+ if ( CommandLineOptions::Get( CLO_PARKED_CAR_TEST ) )
+ {
+ mParkedCars = new ParkedCarInfo[ NUM_TEST_PARKED_CARS ];
+ }
+ else
+ {
+ mParkedCars = new ParkedCarInfo[ MAX_DIFFERENT_CARS ];
+ }
+
+ //We want an inventory section for the car
+ p3d::inventory->AddSection( FREE_CAR_SECTION );
+
+ mParkedCarsEnabled = true;
+
+}
+
+//=============================================================================
+// ParkedCarManager::~ParkedCarManager
+//=============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//=============================================================================
+ParkedCarManager::~ParkedCarManager()
+{
+ GetEventManager()->RemoveAll( this );
+
+ //Remove all the car types
+ MDKParkedCars();
+ delete[] mParkedCars;
+
+ //Clear out leftover locators.
+ for( unsigned int i = 0; i < mNumLocators; ++i )
+ {
+ p3d::inventory->Remove( mLocators[ i ] );
+ }
+
+ RemoveFreeCar();
+
+ if ( mFreeCarLocator )
+ {
+ p3d::inventory->Remove( mFreeCarLocator );
+
+ mFreeCarLocator->Release();
+ mFreeCarLocator = NULL;
+ }
+
+ p3d::inventory->RemoveSectionElements( FREE_CAR_SECTION );
+ p3d::inventory->DeleteSection( FREE_CAR_SECTION );
+
+}
+
+//=============================================================================
+// ParkedCarManager::CreateFreeCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void ParkedCarManager::CreateFreeCar()
+{
+ rAssert( mFreeCarLocator );
+
+ Vehicle* car = GetVehicleCentral()->InitVehicle( mFreeCar.mName, true, NULL, VT_TRAFFIC ); //TODO: Should I change the NULL to a con file?
+ rAssert( car );
+ car->mCreatedByParkedCarManager = true;
+
+ car->AddRef();
+
+
+ //Set the position and facing and reset.
+ rmt::Vector location;
+ mFreeCarLocator->GetLocation( &location );
+ car->SetInitialPositionGroundOffsetAutoAdjust( &location );
+
+ car->SetResetFacingInRadians( mFreeCarLocator->GetRotation() );
+
+ car->Reset();
+
+
+ int added = GetVehicleCentral()->AddVehicleToActiveList( car );
+ rAssert( added != -1 );
+ //mFreeCar.mActiveListIndex = added;
+
+
+ //Turn off the lights.
+ car->mGeometryVehicle->EnableLights( false );
+
+ mFreeCar.mCar = car;
+
+ //Throw away the locator since we'll not need it again and we'll prevent the little memory block.
+ mFreeCarLocator->Release();
+ mFreeCarLocator = NULL;
+}
+
+//=============================================================================
+// ParkedCarManager::MDKParkCar
+//=============================================================================
+// Description: Deletes the park car instances , so that the art can be removed from the inventory.
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+
+
+void ParkedCarManager::MDKParkedCars()
+{
+ //Remove all the car types
+ for( unsigned int i = 0; i < mNumCarTypes; ++i )
+ {
+ rAssert( mParkedCars[ i ].mCar != NULL );
+
+ if( mParkedCars[ i ].mHusk == NULL )
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList( mParkedCars[ i ].mCar );
+ }
+ else
+ {
+ GetVehicleCentral()->RemoveVehicleFromActiveList( mParkedCars[ i ].mHusk );
+
+ GetVehicleCentral()->mHuskPool.FreeHusk( mParkedCars[ i ].mHusk );
+ mParkedCars[ i ].mHusk->Release();
+ mParkedCars[ i ].mHusk = NULL;
+
+ }
+ /*
+ if( mParkedCars[ i ].mActiveListIndex != -1 )
+ {
+ GetVehicleCentral()->SetVehicleController( mParkedCars[ i ].mActiveListIndex, NULL );
+ }
+ mParkedCars[ i ].mActiveListIndex = -1;
+ */
+ mParkedCars[ i ].mCar->Release();
+ mParkedCars[ i ].mCar = NULL;
+ mParkedCars[ i ].mLoadedZoneUID = 0;
+ }
+
+ mNumParkedCars = 0;
+
+}
diff --git a/game/code/worldsim/parkedcars/parkedcarmanager.h b/game/code/worldsim/parkedcars/parkedcarmanager.h
new file mode 100644
index 0000000..35d0096
--- /dev/null
+++ b/game/code/worldsim/parkedcars/parkedcarmanager.h
@@ -0,0 +1,127 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: parkedcarmanager.h
+//
+// Description: Blahblahblah
+//
+// History: 2/6/2003 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef PARKEDCARMANAGER_H
+#define PARKEDCARMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/p3dtypes.hpp>
+
+#include <events/eventlistener.h>
+
+#include <worldsim/vehiclecentral.h>
+
+#include <loading/loadingmanager.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class CarStartLocator;
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class ParkedCarManager : public EventListener, public LoadingManager::ProcessRequestsCallback
+{
+public:
+ enum
+ {
+ MAX_DIFFERENT_CARS = 5,
+ MAX_CAR_NAME_LEN = 32,
+ MAX_LOCATORS_PER_ZONE = 50,
+ MAX_ZONES = 3,
+ NUM_TEST_PARKED_CARS = VehicleCentral::MAX_ACTIVE_VEHICLES - 6
+ };
+
+ static ParkedCarManager& GetInstance();
+ static void DestroyInstance();
+
+ //From EventListener
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+ //From LoadingManager
+ virtual void OnProcessRequestsComplete( void* pUserData );
+
+ //Local
+ void AddCarType( const char* name );
+ void AddLocator( CarStartLocator* loc );
+
+ void AddFreeCar( const char* name, CarStartLocator* loc );
+ void RemoveFreeCar();
+ void RemoveFreeCarIfClose( const rmt::Vector& position );
+
+ //Chuck: release the parkcar instances so Gameplaymanager can free up the art
+ void MDKParkedCars();
+
+ void EnableParkedCars() {mParkedCarsEnabled = true;}
+ void DisableParkedCars() {mParkedCarsEnabled = false;}
+
+private:
+ static ParkedCarManager* spInstance;
+
+ struct ParkedCarInfo
+ {
+ ParkedCarInfo() :
+ mCar( NULL ),
+ mHusk( NULL ),
+ mLoadedZoneUID( 0 )
+ //mActiveListIndex( -1 )
+ { mName[0] ='\0'; };
+
+ char mName[ MAX_CAR_NAME_LEN + 1];
+ Vehicle* mCar;
+ Vehicle* mHusk;
+ tUID mLoadedZoneUID;
+ //int mActiveListIndex;
+ };
+
+ //Where all the parked cars are
+ ParkedCarInfo* mParkedCars;
+ unsigned int mNumCarTypes;
+ unsigned int mNumParkedCars;
+
+ //For loading locators, we store all the locators loaded while a given UID is active.
+ CarStartLocator* mLocators[ MAX_LOCATORS_PER_ZONE ];
+ unsigned int mNumLocators;
+ tUID mLoadingZoneUID;
+
+ //Free "Moment" cars
+ ParkedCarInfo mFreeCar;
+ CarStartLocator* mFreeCarLocator;
+
+ ParkedCarManager();
+ virtual ~ParkedCarManager();
+
+ void CreateFreeCar();
+
+ void FindParkedCarInfo( Vehicle* v, ParkedCarInfo*& info, bool& isFreeCar );
+
+ bool mParkedCarsEnabled; // little bool to disable these during street races
+
+ //Prevent wasteful constructor creation.
+ ParkedCarManager( const ParkedCarManager& parkedcarmanager );
+ ParkedCarManager& operator=( const ParkedCarManager& parkedcarmanager );
+};
+
+//*****************************************************************************
+//
+//Inline Public Member Functions
+//
+//*****************************************************************************
+inline ParkedCarManager& GetPCM() { return ParkedCarManager::GetInstance(); };
+
+#endif //PARKEDCARMANAGER_H
diff --git a/game/code/worldsim/ped/allped.cpp b/game/code/worldsim/ped/allped.cpp
new file mode 100644
index 0000000..62dbc20
--- /dev/null
+++ b/game/code/worldsim/ped/allped.cpp
@@ -0,0 +1,2 @@
+#include <worldsim/ped/pedestrianmanager.cpp>
+#include <worldsim/ped/pedestrian.cpp>
diff --git a/game/code/worldsim/ped/pedestrian.cpp b/game/code/worldsim/ped/pedestrian.cpp
new file mode 100644
index 0000000..b6ac4a9
--- /dev/null
+++ b/game/code/worldsim/ped/pedestrian.cpp
@@ -0,0 +1,448 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pedestrian.cpp
+//
+// Description: Implements Pedestrian class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+#include <string.h>
+#include <raddebug.hpp> // for rAssert & other debug print outs
+#include <radmath/radmath.hpp> // for rmt::Vector
+
+#include <worldsim/ped/pedestrian.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercam.h>
+
+#include <pedpaths/path.h>
+#include <pedpaths/pathsegment.h>
+
+#include <render/RenderManager/RenderManager.h>
+#include <render/Culling/WorldScene.h>
+#include <render/IntersectManager/IntersectManager.h>
+
+#include <ai/sequencer/action.h>
+#include <input/inputmanager.h>
+
+#include <mission/gameplaymanager.h>
+
+static const float SECONDS_BETW_ACTIVATE_POLLS = 0.0f;//0.5f;
+static const float PEDESTRIAN_PATHFIND_BACK_SPEED_MPS = 1.0f;
+static const float PEDESTRIAN_WALK_SPEED_MPS = 1.0f;
+static const float PEDESTRIAN_BRISK_WALK_SPEED_MPS = 1.5f;
+static const float PEDESTRIAN_RUN_SPEED_MPS = 3.0f;
+
+Pedestrian::Pedestrian() :
+ mMillisecondsOutOfSight( 0 ),
+ mIsActive( false ),
+ mPath( NULL ),
+ mFollowPathSpeedMps( 0.0f ),
+ mMarkedForActivation( false ),
+ mSecondsTillActivateSelf( 0.0f )
+{
+}
+
+Pedestrian::~Pedestrian()
+{
+}
+
+
+/////////////////////////////////////////////////////////////////////////
+// CharacterController
+/////////////////////////////////////////////////////////////////////////
+void Pedestrian::Update( float seconds )
+{
+ if( mMarkedForActivation )
+ {
+ rAssert( !mIsActive );
+
+
+ //mSecondsTillActivateSelf -= seconds;
+ //if( mSecondsTillActivateSelf < 0.0f )
+ //{
+ // mSecondsTillActivateSelf = SECONDS_BETW_ACTIVATE_POLLS;
+ if( ::GetCharacterManager()->IsModelLoaded( mModelName ) )
+ {
+ ActivateSelf();
+ }
+ //}
+ else
+ {
+ // don't update if marked for activation but not yet activated
+ return;
+ }
+ }
+
+ // don't update if not active!
+ if( !mIsActive )
+ {
+ return;
+ }
+ BEGIN_PROFILE( "Pedestrian::Update" );
+
+ if( mState == NPCController::FOLLOWING_PATH )
+ {
+
+ // Detect if another ped is near me
+ // if so, deal with "walk through one another" scenario if we're
+ // close enough to the player
+
+ rmt::Vector myPos;
+ GetCharacter()->GetPosition( &myPos );
+
+ rmt::Vector playerPos;
+ GetAvatarManager()->GetAvatarForPlayer( 0 )->GetPosition( playerPos );
+
+ const float MIN_DIST_SQR_FROM_PLAYER = 1225.0f;
+ if( (myPos - playerPos).MagnitudeSqr() <= MIN_DIST_SQR_FROM_PLAYER ||
+ GetInputManager()->GetGameState() == Input::ACTIVE_FIRST_PERSON )
+ {
+
+ int i = PedestrianManager::GetInstance()->mActivePeds.GetHead();
+ while( 0 <= i && i < DListArray::MAX_ELEMS )
+ {
+ PedestrianManager::PedestrianStruct* pedStr = (PedestrianManager::PedestrianStruct*)
+ PedestrianManager::GetInstance()->mActivePeds.GetDataAt(i);
+ rAssert( pedStr );
+
+ Pedestrian* ped = pedStr->ped;
+ rAssert( ped );
+
+ if( !ped->GetIsActive() )
+ {
+ i = PedestrianManager::GetInstance()->mActivePeds.GetNextOf( i );
+ continue;
+ }
+
+ if( ped != this )
+ {
+ rmt::Vector pedPos;
+ ped->GetCharacter()->GetPosition( &pedPos );
+
+ rmt::Vector toPed = pedPos - myPos;
+
+ static const float MINDISTSQR_FROM_ANOTHER_PED_SMALL = 2.25f;
+ static const float MINDISTSQR_FROM_ANOTHER_PED_LARGE = 4.00f;
+
+ float distToleranceFromAnotherPedSqr = (mFollowPathSpeedMps > 1.0f) ?
+ MINDISTSQR_FROM_ANOTHER_PED_SMALL :
+ MINDISTSQR_FROM_ANOTHER_PED_LARGE;
+
+ if( toPed.MagnitudeSqr() < distToleranceFromAnotherPedSqr )
+ {
+ // here, we're too close to another ped on same path...
+ // If he's just following path as well, engage him in conversation!
+ if( ped->GetState() == NPCController::FOLLOWING_PATH )
+ {
+ // determine direction
+ toPed.Normalize(); // *** SQUARE ROOT! ***
+
+ // tell myself to transit to talking
+ TransitToState( NPCController::TALKING );
+ mTalkTarget = ped;
+ SetDirection( toPed );
+ mpCharacter->SetDesiredDir( choreo::GetWorldAngle(toPed.x,toPed.z) );
+
+ // tell him to transit to talking
+ toPed.Scale( -1.0f );
+ ped->TransitToState( NPCController::TALKING );
+ ped->mTalkTarget = this;
+ ped->SetDirection( toPed );
+ ped->GetCharacter()->SetDesiredDir( choreo::GetWorldAngle(toPed.x,toPed.z) );
+
+ StartTalking();
+
+ break;
+ }
+ else if( ped->GetState() == NPCController::PANICKING )
+ {
+ // He's... um.. panicking... We'd better panick too
+ IncitePanic();
+ break;
+ }
+ else
+ {
+ // he's not available for talking, but he's still nearby
+ // check if he's in front of us. If so, STAND and wait.
+
+ rmt::Vector forward, right, up;
+ up.Set( 0.0f, 1.0f, 0.0f );
+
+ mpCharacter->GetFacing( forward );
+ rAssert( rmt::Epsilon(forward.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ right.CrossProduct( up, forward );
+ bool pedOnMyRight = false;
+ bool gonnaHit = WillCollide( myPos,
+ forward, // heading (forward)
+ right, // right vector
+ 1.0f, 3.0f, pedPos, pedOnMyRight );
+
+ if( gonnaHit )
+ {
+ TransitToState( STANDING );
+ break;
+ }
+ }
+ }
+ }
+ i = PedestrianManager::GetInstance()->mActivePeds.GetNextOf( i );
+ } // end of WHILE
+ }
+ }
+ NPCController::Update( seconds );
+
+ END_PROFILE( "Pedestrian::Update" );
+
+}
+
+float Pedestrian::GetValue( int buttonId ) const
+{
+ return 0.0f;
+}
+
+bool Pedestrian::IsButtonDown( int buttonId ) const
+{
+ return false;
+}
+/////////////////////////////////////////////////////////////////////////
+
+
+
+void Pedestrian::Activate(
+ Path* path,
+ PathSegment* pathSegment,
+ rmt::Vector spawnPos,
+ const char* modelName
+ )
+
+{
+ rAssert( path );
+ rAssert( pathSegment );
+ rAssert( modelName );
+ rAssert( path->IsClosed() );
+
+ mStartPanicking = false;
+
+ // set where I am on the path
+ mPath = path;
+
+ /////////////////////////////////////////////////////////
+ // Add NPC Waypoints based on the path given...
+ int numSegs = path->GetNumPathSegments();
+ int numPts = numSegs; // a closed path has same number of points as segments
+
+ rAssert( 1 < numPts && numPts <= MAX_NPC_WAYPOINTS );
+
+ int currSegIndex = pathSegment->GetIndexToParentPath();
+
+ mNumNPCWaypoints = 0;
+ while( mNumNPCWaypoints < numPts )
+ {
+ rmt::Vector segEndPos;
+ pathSegment->GetEndPos( segEndPos );
+
+ AddNPCWaypoint( segEndPos );
+
+ // next segment!
+ currSegIndex = ( currSegIndex + 1 ) % numSegs;
+ rAssert( 0 <= currSegIndex && currSegIndex < numSegs );
+ pathSegment = path->GetPathSegmentByIndex( currSegIndex );
+ }
+ mCurrNPCWaypoint = 0;
+
+
+ //////////////////////////////////////////////////////////
+ rAssert( modelName != NULL );
+ int modelNameLen = strlen(modelName);
+ rAssert( modelNameLen <= Pedestrian::MAX_STRING_LEN );
+ strncpy( mModelName, modelName, Pedestrian::MAX_STRING_LEN );
+ mModelName[ modelNameLen ] = '\0';
+
+ mMarkedForActivation = true;
+ mSecondsTillActivateSelf = SECONDS_BETW_ACTIVATE_POLLS;
+
+ // set the Character's new spawn position (so whenever we do
+ // a Character::GetPosition, we get the correct position)
+ GetCharacter()->RelocateAndReset(spawnPos, 0.0f);
+
+ // diff between spawnPos & actual character position at this point
+ // is the ground fix up... spawnpos is the actual ON SEGMENT
+ // location itself..
+ mSpawnPos = spawnPos;
+}
+
+void Pedestrian::ActivateSelf()
+{
+ mMillisecondsOutOfSight = 0;
+ mMarkedForActivation = false;
+
+ // Test to see how close we are to the player... if too close
+ // just deactivate...
+ rmt::Vector myPos;
+ mpCharacter->GetPosition( myPos );
+
+ rmt::Vector playerPos, playerVel;
+
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar != NULL );
+ avatar->GetPosition( playerPos );
+
+ /*
+ rmt::Vector camTarget;
+ SuperCam* pCam = GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ pCam->GetHeadingNormalized( &camTarget );
+ rAssert( rmt::Epsilon(camTarget.MagnitudeSqr(), 1.0f, 0.0005f) );
+
+ rmt::Vector center = playerPos + camTarget * PedestrianManager::CENTER_OFFSET;
+
+ float distSqr = (myPos - center).MagnitudeSqr();
+ */
+ float distSqr = (myPos - playerPos).MagnitudeSqr();
+ float removeDistSqr = PedestrianManager::GetInstance()->FADE_RADIUS;
+ removeDistSqr *= removeDistSqr;
+
+ if( GetGameplayManager()->TestPosInFrustrumOfPlayer( myPos, 0 ) &&
+ distSqr < removeDistSqr )
+ {
+ PedestrianManager::GetInstance()->RemovePed( mpCharacter );
+ return;
+ }
+
+ // make sure we don't activate too near another ped...
+ const float TOO_CLOSE_TO_ANOTHER_PED_SQR = 2.25f;
+ rmt::Vector pedPos;
+ int i = PedestrianManager::GetInstance()->mActivePeds.GetHead();
+ while( 0 <= i && i < DListArray::MAX_ELEMS )
+ {
+ PedestrianManager::PedestrianStruct* pedStr = (PedestrianManager::PedestrianStruct*)
+ PedestrianManager::GetInstance()->mActivePeds.GetDataAt(i);
+ rAssert( pedStr != NULL );
+
+ if( pedStr->ped == this )
+ {
+ i = PedestrianManager::GetInstance()->mActivePeds.GetNextOf( i );
+ continue; // I'm this ped! Ignore...
+ }
+
+ if( !pedStr->ped->GetIsActive() )
+ {
+ i = PedestrianManager::GetInstance()->mActivePeds.GetNextOf( i );
+ continue; // ped is waiting to self-activate, ignore it
+ }
+
+ pedStr->ped->GetCharacter()->GetPosition( &pedPos );
+ pedPos.y = myPos.y; // ignore diff in y
+
+ if( (pedPos-myPos).MagnitudeSqr() < TOO_CLOSE_TO_ANOTHER_PED_SQR )
+ {
+ PedestrianManager::GetInstance()->RemovePed( mpCharacter );
+ return;
+ }
+ i = PedestrianManager::GetInstance()->mActivePeds.GetNextOf( i );
+ }
+
+
+ /////////////////////////////////
+ // Otherwise, we're free to activate self
+
+ mIsActive = true;
+
+ // set my state
+ TransitToState(FOLLOWING_PATH);
+ mOffPath = false;
+ DetermineFollowPathSpeed();
+
+ mpCharacter->ResetCollisions();
+ mpCharacter->AddToWorldScene( );
+ mpCharacter->SetHasBeenHit(false);
+
+ mpCharacter->AddToPhysics();
+}
+
+void Pedestrian::InitZero()
+{
+ mPath = NULL;
+
+ mMillisecondsOutOfSight = 0;
+ mMarkedForActivation = false;
+ mIsActive = false;
+
+ TransitToState(STOPPED);
+ mOffPath = false;
+}
+
+void Pedestrian::Deactivate()
+{
+ InitZero();
+
+ // transit back to locomotion state
+ if( mpCharacter->GetStateManager()->GetState() != CharacterAi::LOCO )
+ {
+ mpCharacter->GetStateManager()->SetState<CharacterAi::Loco>();
+ }
+
+ // transit back to AI control...
+ sim::SimState* simState = mpCharacter->GetSimState();
+ if( simState != NULL )
+ {
+ simState->SetControl( sim::simAICtrl );
+ }
+
+ //mpCharacter->SetSolveCollisions( false );
+ mpCharacter->RemoveFromPhysics();
+ mpCharacter->RemoveFromWorldScene( );
+}
+
+void Pedestrian::OnReachedWaypoint()
+{
+ // do nothing
+}
+
+
+float Pedestrian::GetFollowPathSpeedMps() const
+{
+ // when offpath, don't just RUN BACK on the path
+ // this can make us thrash back and forth (Oh we're off path!
+ // Oh now we're off path on the other side! Oh we're off path! So on...)
+ if( mOffPath )
+ {
+ return PEDESTRIAN_PATHFIND_BACK_SPEED_MPS;
+ }
+ return mFollowPathSpeedMps;
+}
+
+void Pedestrian::GetBoundingSphere( rmt::Sphere& s )
+{
+ rmt::Sphere sphere;
+ mpCharacter->GetBoundingSphere( &sphere );
+
+ // make sure the bounding sphere has same position as character!!!
+ s = sphere;
+}
+
+
+void Pedestrian::DetermineFollowPathSpeed()
+{
+ int coinflip = rand() % 10;
+ if( 0 <= coinflip && coinflip < 4 )
+ {
+ mFollowPathSpeedMps = PEDESTRIAN_WALK_SPEED_MPS;
+ }
+ else if( 4 <= coinflip && coinflip < 8 )
+ {
+ mFollowPathSpeedMps = PEDESTRIAN_BRISK_WALK_SPEED_MPS;
+ }
+ else
+ {
+ mFollowPathSpeedMps = PEDESTRIAN_RUN_SPEED_MPS;
+ }
+}
+
diff --git a/game/code/worldsim/ped/pedestrian.h b/game/code/worldsim/ped/pedestrian.h
new file mode 100644
index 0000000..88e9d01
--- /dev/null
+++ b/game/code/worldsim/ped/pedestrian.h
@@ -0,0 +1,128 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pedestrian.h
+//
+// Description: Defines Pedestrian class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#ifndef PEDESTRIAN_H // 80 character mark
+#define PEDESTRIAN_H
+
+#include <worldsim/character/charactercontroller.h>
+#include <roads/geometry.h>
+
+// because we're only storing pointers to these
+// objects (all pointers are same size), we can
+// use forward declarations here.
+class Character;
+class Path;
+class PathSegment;
+
+
+class Pedestrian
+: public NPCController
+{
+// METHODS
+public:
+ Pedestrian();
+ virtual ~Pedestrian();
+
+ void GetBoundingSphere( rmt::Sphere& s );
+
+ /////////////////////////////////////////////////////////////////////////
+ // CharacterController
+ /////////////////////////////////////////////////////////////////////////
+ virtual void Update( float seconds );
+ virtual float GetValue( int buttonId ) const;
+ virtual bool IsButtonDown( int buttonId ) const;
+ /////////////////////////////////////////////////////////////////////////
+
+ // a magic all-in-one call to place ped in the world & make it active
+ // This only marks it for activation while waiting for CharaterManager
+ // to load our data... won't put in world till model is done loading...
+ void Activate(
+ Path* path,
+ PathSegment* pathSegment,
+ rmt::Vector spawnPos,
+ const char* modelName );
+
+ // This immediately removes model from the world and makes it inactive
+ void Deactivate();
+
+ // ACCESSORS
+ bool GetIsActive() const;
+ void SetIsActive( bool isActive );
+
+ Path* GetPath();
+ void SetPath( Path* path );
+
+ void InitZero();
+
+// MEMBERS
+public:
+ unsigned int mMillisecondsOutOfSight;
+
+protected:
+ float GetFollowPathSpeedMps() const;
+ void OnReachedWaypoint();
+
+// METHODS
+private:
+ void DetermineFollowPathSpeed();
+ void ActivateSelf();
+
+ //Prevent wasteful constructor creation.
+ Pedestrian( const Pedestrian& ped );
+ Pedestrian& operator=( const Pedestrian& ped );
+
+// MEMBERS
+private:
+
+ /////////////////////////////////////////////////////////////////////////
+ // CharacterController
+ /////////////////////////////////////////////////////////////////////////
+ rmt::Vector mNormalizedDir;
+ /////////////////////////////////////////////////////////////////////////
+
+ bool mIsActive;
+
+ Path* mPath; // path I'm on
+
+ float mFollowPathSpeedMps;
+ bool mMarkedForActivation;
+
+ enum {
+ MAX_STRING_LEN = 64
+ };
+
+ char mModelName [MAX_STRING_LEN+1];
+ rmt::Vector mSpawnPos;
+ float mSecondsTillActivateSelf;
+
+};
+
+
+// **************************** INLINES ******************************
+
+inline bool Pedestrian::GetIsActive() const
+{
+ return mIsActive;
+}
+inline void Pedestrian::SetIsActive( bool isActive )
+{
+ mIsActive = isActive;
+}
+inline Path* Pedestrian::GetPath()
+{
+ return mPath;
+}
+inline void Pedestrian::SetPath( Path* path )
+{
+ mPath = path;
+}
+
+#endif // PEDESTRIAN_H \ No newline at end of file
diff --git a/game/code/worldsim/ped/pedestrianmanager.cpp b/game/code/worldsim/ped/pedestrianmanager.cpp
new file mode 100644
index 0000000..f4e9011
--- /dev/null
+++ b/game/code/worldsim/ped/pedestrianmanager.cpp
@@ -0,0 +1,1593 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pedestrianmanager.cpp
+//
+// Description: Implements Pedestrian Manager class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+#define USEAVATARPOS
+
+#include <raddebug.hpp> // for rAssert & other debug print outs
+#include <stdlib.h>
+#include <string.h>
+
+#include <debug/profiler.h> // for the Profiler
+#include <radmath/radmath.hpp> // for rmt::Vector
+#include <memory/srrmemory.h>
+
+#include <meta/locatorevents.h>
+#include <meta/pedgrouplocator.h>
+
+#include <worldsim/ped/pedestrianmanager.h>
+
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+
+/*
+#include <camera/supercammanager.h>
+#include <camera/supercam.h>
+*/
+
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/characterrenderable.h>
+
+#include <render/Culling/ReserveArray.h>
+#include <render/IntersectManager/IntersectManager.h>
+
+#include <roads/geometry.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+#include <camera/supercam.h>
+
+#include <pedpaths/path.h>
+#include <pedpaths/pathsegment.h>
+
+#include <mission/gameplaymanager.h>
+
+#include <worldsim/vehiclecentral.h>
+
+// in meters, how much to translate spawn & remove radii forward
+// in the direction of the camera lookat
+
+//#define PED_TEST
+#ifdef PED_TEST
+ const float PedestrianManager::FADE_RADIUS = 5.0f; // in meters
+ const float PedestrianManager::CENTER_OFFSET = 5.0f; // in meters
+ const float PedestrianManager::ADD_RADIUS = 10.0f; // in meters
+ const float PedestrianManager::REMOVE_RADIUS = 15.0f; // in meters
+ const float PedestrianManager::INITIAL_ADD_RADIUS = 10.0f; // in meters
+#else
+ const float PedestrianManager::FADE_RADIUS = 60.0f; // in meters
+ const float PedestrianManager::CENTER_OFFSET = 30.0f; // in meters
+ const float PedestrianManager::ADD_RADIUS = 45.0f; // in meters
+ const float PedestrianManager::REMOVE_RADIUS = 50.0f; // in meters
+ const float PedestrianManager::INITIAL_ADD_RADIUS = 90.0f; // in meters
+#endif
+const unsigned int PedestrianManager::MILLISECONDS_PER_GROUND_INTERSECT = 10;
+const unsigned int PedestrianManager::MILLISECONDS_BETW_ADDS = 1;
+const unsigned int PedestrianManager::MILLISECONDS_BETW_REMOVES = 1000;
+const unsigned int PedestrianManager::MILLISECONDS_POPULATE_WORLD = 3000;
+
+int PedestrianManager::mDefaultModelGroup = 0;
+
+// *********************** STATICS ***********************
+
+PedestrianManager* PedestrianManager::mInstance = NULL;
+
+PedestrianManager* PedestrianManager::GetInstance()
+{
+ if( mInstance == NULL )
+ {
+ mInstance = new(GMA_LEVEL_OTHER) PedestrianManager();
+ }
+ return mInstance;
+}
+
+void PedestrianManager::DestroyInstance()
+{
+ if( mInstance != NULL )
+ {
+ mInstance->mFreePeds.Clear();
+ mInstance->mActivePeds.Clear();
+
+ // Clean up Pedestrians.
+ int i = 0;
+ for( i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
+ {
+ PedestrianStruct* pedStr = &(mInstance->mPeds[i]);
+ rAssert( pedStr != NULL );
+
+ Pedestrian* ped = pedStr->ped;
+ rAssert( ped != NULL );
+
+ if( ped->GetIsActive() )
+ {
+ ped->Deactivate();
+ }
+ }
+ // Delete self
+ delete mInstance;
+ }
+ mInstance = NULL;
+}
+
+
+// ******************** PUBLICS *************************
+
+// call this in loading context
+void PedestrianManager::InitDefaultModelGroups()
+{
+ // initialize all model groups with a default group
+ // This is why we put PedManager::Init in LoadingContext, after
+ // CharacterManager::PreLoad and GamePlayManager::LoadBlahBlah,
+ // cuz GamePlayManager will want to parse the level script which
+ // will register the modelgroups...
+ for( int i=0; i<PedestrianManager::MAX_MODEL_GROUPS; i++ )
+ {
+ mModelGroups[i].numModels = 4;
+ mModelGroups[i].models[0].Init( "male1", 5 );
+ mModelGroups[i].models[1].Init( "fem1", 5 );
+ mModelGroups[i].models[2].Init( "boy1", 5 );
+ mModelGroups[i].models[3].Init( "girl1", 5 );
+ for( int j=4; j<PedestrianManager::MAX_MODELS; j++ )
+ {
+ mModelGroups[i].models[j].InitZero();
+ }
+ }
+}
+
+void PedestrianManager::SetDefaultModelGroup( int toGroupID )
+{
+ mDefaultModelGroup = toGroupID;
+}
+
+void PedestrianManager::Init()
+{
+ mAllowAdd = true;
+ mMillisecondsTillRemove = PedestrianManager::MILLISECONDS_BETW_REMOVES;
+ mMillisecondsTillAdd = PedestrianManager::MILLISECONDS_BETW_ADDS;
+
+ mMillisecondsPopulateWorld = PedestrianManager::MILLISECONDS_POPULATE_WORLD;
+
+ mFreePeds.Clear();
+ mActivePeds.Clear();
+
+ for( int i=0; i<PedestrianManager::MAX_MODELS_IN_USE; i++ )
+ {
+ mModelsInUse[i].uid.SetText( NULL );
+ mModelsInUse[i].currCount = 0;
+ }
+ mNumActiveModels = 0;
+
+ // Recall that at this point, MissionManager has parsed the level script
+ // which may or may not have populated our model groups. Recall also that
+ // before that happened, we init all our model groups with a default group
+ // Therefore, we can safely switch our current model group to group 0
+ // (it should exist one way or another).
+
+ // switch to our group zero
+ UnregisterAllModels();
+
+ mCurrGroupID = -1;
+ SwitchModelGroup( mDefaultModelGroup ); // will set mCurrGroupID to 0
+
+ for( int i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
+ {
+ PedestrianStruct* pedStr = &(mPeds[i]);
+ rAssert( pedStr != NULL );
+
+ Pedestrian* ped = pedStr->ped;
+ rAssert( ped != NULL );
+
+ char name[5];
+ sprintf( name, "ped%d", i );
+ name[4] = '\0';
+
+ // call AddCharacter with an initial, lightweight dummy model so
+ // we can init all the sim objects, the animation drivers, etc.
+ Character* character = ::GetCharacterManager()->AddCharacter(
+ CharacterManager::NPC, name, "npd", "npd" );
+
+ character->SetRole(Character::ROLE_PEDESTRIAN);
+
+ rAssert( character != NULL );
+ character->AddRef();
+ character->SetController( pedStr->ped );
+ character->mbAllowUnload = false;
+ pedStr->ped->SetCharacter( character );
+
+ int nameLen = strlen("npd");
+ rAssert( nameLen <= PedestrianManager::MAX_STRING_LEN );
+ strncpy( pedStr->modelName, "npd", PedestrianManager::MAX_STRING_LEN );
+ pedStr->modelName[nameLen] = '\0';
+
+ ped->InitZero();
+
+ // add ped to list of free peds
+ pedStr->listIndex = mFreePeds.AddLast( pedStr );
+ rAssert( pedStr->listIndex != -1 );
+
+ }
+}
+
+
+
+
+void PedestrianManager::Update( unsigned int milliseconds )
+{
+BEGIN_PROFILE( "Pedestrian Manager" );
+
+ //rTunePrintf( "<<<<<<<<<<<<<<<<<<<<<<< NUM ACTIVE PEDS %d >>>>>>>>>>>>>>>>>>>>>>\n", mActivePeds.GetNumElems() );
+
+ PedestrianStruct* pedStr = NULL;
+ int i = 0;
+
+ //
+ // ================================
+ // Get information about the player
+ // ================================
+ //
+ rmt::Vector pPos, pVel;//, pDir;
+ float pSpeed;
+
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar != NULL );
+
+ // Avatar::GetPosition() will return position of vehicle if avatar inside
+ // vehicle. Same deal with Avatar::GetVelocity() & GetSpeedMps()
+ // Remember that we should use VELOCITY rather than facing because
+ // the player can face one way and travel in reverse.
+ //
+ avatar->GetPosition(pPos);
+ avatar->GetVelocity(pVel);
+ pSpeed = avatar->GetSpeedMps();
+ /*
+ SuperCam* superCam = ::GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ rAssert( superCam != NULL );
+
+ superCam->GetPosition( &pPos );
+ superCam->GetVelocity( &pVel );
+ pSpeed = pVel.Magnitude(); // *** SQUARE ROOT! ***
+ */
+
+ int debugCount, debugExpCount;
+
+ // Get the camera for Player 1.
+ // It's what we need to apply the center offset to our spawn & remove radii
+ SuperCam* pCam = GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+
+ rmt::Vector camTarget;
+ pCam->GetHeadingNormalized( &camTarget );
+ rAssert( rmt::Epsilon(camTarget.MagnitudeSqr(), 1.0f, 0.0005f) );
+
+ rmt::Vector center;
+ if( mMillisecondsPopulateWorld > 0 )
+ {
+ center = pPos;
+ }
+ else
+ {
+ center = pPos + camTarget * PedestrianManager::CENTER_OFFSET;
+ }
+
+
+
+ // ====================================
+ // Update PedestrianManager properties
+ // ======================================
+
+ if( mMillisecondsPopulateWorld > milliseconds )
+ {
+ mMillisecondsPopulateWorld -= milliseconds;
+ }
+ else
+ {
+ mMillisecondsPopulateWorld = 0;
+ }
+
+
+ static bool remove = true;
+ remove = !remove;
+
+ //
+ // ==================================================
+ // Remove active pedestrians that are outside radius
+ // ==================================================
+ //
+ if( remove && (mMillisecondsPopulateWorld == 0))
+ {
+ mMillisecondsTillRemove = PedestrianManager::MILLISECONDS_BETW_REMOVES;
+
+ debugCount = 0;
+ debugExpCount = mActivePeds.GetNumElems();
+
+ int j = mActivePeds.GetHead();
+ while( 0 <= j && j < DListArray::MAX_ELEMS )
+ {
+ debugCount++;
+
+ pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( j );
+ rAssert( pedStr != NULL );
+
+ // Do radius test on character's bounding sphere
+ // versus pedestrian radius...
+ //
+ bool remove = false;
+
+ rmt::Vector pedPos;
+ pedStr->ped->GetCharacter()->GetPosition( pedPos );
+
+ float distSqr = (center - pedPos).MagnitudeSqr();
+ float zoneDistSqr = PedestrianManager::REMOVE_RADIUS * PedestrianManager::REMOVE_RADIUS;
+ if( distSqr >= zoneDistSqr )
+ {
+ remove = true;
+ }
+
+ // Do frustrum test too... if outside our frustrum, remove it within n seconds
+
+ const float MAX_OUT_OF_SIGHT_MILLISECONDS = 2000;
+
+ if( !pedStr->ped->GetCharacter()->mbInAnyonesFrustrum &&
+ pedStr->ped->mMillisecondsOutOfSight > MAX_OUT_OF_SIGHT_MILLISECONDS )
+ {
+ remove = true;
+ pedStr->ped->mMillisecondsOutOfSight = 0;
+ }
+
+ j = mActivePeds.GetNextOf( j );
+
+ if( remove )
+ {
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ DeactivatePed( pedStr );
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ break;
+ }
+ }
+// rAssert( debugCount == debugExpCount );
+ }
+ else
+ {
+ //
+ // ====================
+ // Add Pedestrians
+ // ====================
+ //
+ if( mMillisecondsPopulateWorld > 0 )
+ {
+ AddPeds( center, PedestrianManager::INITIAL_ADD_RADIUS );
+ }
+ else
+ {
+ AddPeds( center, PedestrianManager::ADD_RADIUS );
+ }
+ }
+
+
+ //
+ // =================================
+ // Update active pedestrians:
+ // - fade them as necessary
+ // =================================
+ //
+ i = mActivePeds.GetHead();
+ while( 0 <= i && i < DListArray::MAX_ELEMS )
+ {
+ pedStr = (PedestrianStruct*) mActivePeds.GetDataAt(i);
+ rAssert( pedStr != NULL );
+
+ if( pedStr->ped->GetIsActive() )
+ {
+ ////////////////////////////////////////////////////
+ // Update out of sight stuff
+ if( !pedStr->ped->GetCharacter()->mbInAnyonesFrustrum )
+ {
+ pedStr->ped->mMillisecondsOutOfSight += milliseconds;
+ }
+ else
+ {
+ pedStr->ped->mMillisecondsOutOfSight = 0;
+ }
+
+
+ ////////////////////////////////////////////////////
+ // Set fade if getting near fringe...
+ //
+ rmt::Vector myPos;
+ pedStr->ped->GetCharacter()->GetPosition( myPos );
+
+ float dist2Player = (pPos - myPos).Length(); // *** SQUARE ROOT! ***
+
+ float fadeMinLimit = PedestrianManager::FADE_RADIUS;
+ float fadeMaxLimit = PedestrianManager::ADD_RADIUS + PedestrianManager::CENTER_OFFSET;
+ int fadeAlpha = 255;
+ if( fadeMinLimit <= dist2Player && dist2Player <= fadeMaxLimit )
+ {
+ // if we're in the fading zone, gotta change fade alpha
+ float fadeRatio = (dist2Player - fadeMinLimit)/(fadeMaxLimit - fadeMinLimit);
+ fadeAlpha = (int) (255.0f * (1.0f - fadeRatio));
+ }
+ else if( dist2Player > fadeMaxLimit )
+ {
+ fadeAlpha = 0;
+ }
+ pedStr->ped->GetCharacter()->SetFadeAlpha( fadeAlpha );
+ }
+ i = mActivePeds.GetNextOf( i );
+ }
+
+
+
+END_PROFILE( "Pedestrian Manager" );
+
+}
+
+void PedestrianManager::RemovePed( Character* character )
+{
+ if( character == NULL )
+ {
+ return;
+ }
+ int j = mActivePeds.GetHead();
+ while( 0 <= j && j < DListArray::MAX_ELEMS )
+ {
+ PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( j );
+ rAssert( pedStr != NULL );
+
+ if( pedStr->ped->GetCharacter() == character )
+ {
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ DeactivatePed( pedStr );
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ return;
+ }
+ j = mActivePeds.GetNextOf( j );
+ }
+}
+
+void PedestrianManager::RemoveAllPeds()
+{
+ int j = mActivePeds.GetHead();
+ while( 0 <= j && j < DListArray::MAX_ELEMS )
+ {
+ PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( j );
+ rAssert( pedStr != NULL );
+
+ j = mActivePeds.GetNextOf( j );
+
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ DeactivatePed( pedStr );
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ }
+}
+
+void PedestrianManager::AllowAddingPeds( bool allowed )
+{
+ mAllowAdd = allowed;
+}
+
+
+// ************************ PRIVATES *************************
+
+PedestrianManager::PedestrianManager() :
+ mMillisecondsTillRemove( 0 ),
+ mMillisecondsTillAdd( 0 ),
+ mMillisecondsPopulateWorld( 0 ),
+ mNumRegisteredModels( 0 ),
+ mCurrGroupID( -1 ),
+ mNumActiveModels( 0 ),
+ mAllowAdd( true )
+{
+ // check some stuff once per compile..
+ rAssert( PedestrianManager::ADD_RADIUS > 0 );
+ rAssert( PedestrianManager::REMOVE_RADIUS > 0 );
+ rAssert( PedestrianManager::MAX_STRING_LEN >= 6 );
+ rAssert( 1 <= PedestrianManager::MAX_MODELS );
+ rAssert( 0 <= PedestrianManager::MAX_PEDESTRIANS &&
+ PedestrianManager::MAX_PEDESTRIANS <= DListArray::MAX_ELEMS );
+
+ // listen to appropriate events
+ GetEventManager()->AddListener( this, (EventEnum)( EVENT_LOCATOR + LocatorEvent::LOAD_PED_MODEL_GROUP ) );
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_PLAYER_CAR_HIT_NPC );
+ GetEventManager()->AddListener( this, EVENT_OBJECT_KICKED );
+ GetEventManager()->AddListener( this, EVENT_PLAYER_VEHICLE_HORN );
+
+ /*** DEBUG ***
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_GETOUTOFVEHICLE_END) );
+ *** DEBUG ***/
+
+ for( int i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
+ {
+ mPeds[i].ped = new (GMA_LEVEL_OTHER) Pedestrian();
+ mPeds[i].ped->AddRef();
+ mPeds[i].listIndex = -1;
+ }
+}
+
+PedestrianManager::~PedestrianManager()
+{
+ GetEventManager()->RemoveAll( this );
+
+ int i;
+ for( i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
+ {
+ if( mPeds[i].ped != NULL )
+ {
+ Character* character = mPeds[i].ped->GetCharacter();
+ if( character != NULL )
+ {
+ // Just release the pointer; don't bother calling
+ // RemoveCharacter(). The character gets cleaned up
+ // later by CharacterManager's destructor
+ character->Release();
+ }
+ mPeds[i].ped->Release();
+ }
+ }
+}
+
+void PedestrianManager::DeactivatePed( PedestrianStruct* pedStr )
+{
+ rAssert( pedStr != NULL );
+ rAssert( pedStr->ped != NULL );
+
+ rAssert( 1 <= mNumActiveModels && mNumActiveModels <= PedestrianManager::MAX_MODELS_IN_USE );
+
+ //rDebugPrintf( "*** Removing %s\n", pedStr->modelName );
+
+ // remove ped from path
+ Path* path = pedStr->ped->GetPath();
+ rAssert( path != NULL );
+ bool res = path->RemovePedestrian();
+ rAssert( res );
+
+ // reset ped parameters
+ pedStr->ped->Deactivate();
+
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+ // If we can find the model in list of currently registered models,
+ // decrement count. The reason we need to search is that the model
+ // can be unregistered and re-registered in a different location.
+ // If we can't find it anywhere, then it must have been unregistered.
+ int modelID = GetModelIDByName( pedStr->modelName );
+ if( modelID != -1 )
+ {
+ mModels[modelID].currCount--;
+ }
+ else
+ {
+ rDebugPrintf( " Not found in model group, so I'll swap in npd!\n" );
+
+ #ifdef RAD_DEBUG
+ SanityCheck();
+ #endif
+ // we're swapping out the model for npd... better update the modelsinuse list
+ FindModelInUseAndRemove( pedStr->modelName );
+
+ // since this model has been unregistered, it's not in the current
+ // model group, so keeping it around will only waste memory...
+ // we need to swap in "npd" in its place.
+ Character* character = pedStr->ped->GetCharacter();
+ rAssert( character != NULL );
+ ::GetCharacterManager()->SwapData( character, "npd", "npd" );
+ sprintf( pedStr->modelName, "npd" );
+
+ #ifdef RAD_DEBUG
+ SanityCheck();
+ #endif
+ }
+
+ // manage the ped manager's active/free lists
+ res = mActivePeds.Remove( pedStr->listIndex );
+ rAssert( res );
+ pedStr->listIndex = mFreePeds.AddLast( pedStr );
+ rAssert( pedStr->listIndex > -1 );
+
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+
+
+}
+
+float PedestrianManager::GetRandomMinDistSqr()
+{
+ static const float MINDISTSQR_FROM_PED_ON_SAME_PATH_A = 16.0f;
+ static const float MINDISTSQR_FROM_PED_ON_SAME_PATH_B = 49.0f;
+ static const float MINDISTSQR_FROM_PED_ON_SAME_PATH_C = 144.0f;
+
+ int coinflip = rand() % 3;
+ float mindistsqr = 0.0f;
+ switch( coinflip )
+ {
+ case 0:
+ {
+ mindistsqr = MINDISTSQR_FROM_PED_ON_SAME_PATH_A;
+ }
+ break;
+ case 1:
+ {
+ mindistsqr = MINDISTSQR_FROM_PED_ON_SAME_PATH_B;
+ }
+ break;
+ case 2:
+ {
+ mindistsqr = MINDISTSQR_FROM_PED_ON_SAME_PATH_C;
+ }
+ break;
+ default:
+ {
+ mindistsqr = MINDISTSQR_FROM_PED_ON_SAME_PATH_A;
+ }
+ break;
+ }
+
+ return mindistsqr;
+}
+
+bool PedestrianManager::ActivatePed( PathSegment* segment, rmt::Vector spawnPos )
+{
+ rAssert( segment != NULL );
+
+ PedestrianStruct* pedStr = NULL;
+ Pedestrian* ped = NULL;
+
+ // get the path for this pedestrian & try to add ped to it &
+ // store index
+ Path* path = segment->GetParentPath();
+ rAssert( path != NULL );
+
+ // Check to make sure it isn't already at max ped limit
+ if( path->IsFull() )
+ {
+ return false;
+ }
+
+ /*
+ // Make sure we're not too close to another ped...
+ float minDistSqr = GetRandomMinDistSqr();
+ rmt::Vector pedPos;
+ int i = mActivePeds.GetHead();
+ while( 0 <= i && i < DListArray::MAX_ELEMS )
+ {
+ pedStr = (PedestrianStruct*) mActivePeds.GetDataAt(i);
+ rAssert( pedStr != NULL );
+
+ pedStr->ped->GetCharacter()->GetPosition( &pedPos );
+ pedPos.y = spawnPos.y; // ignore diff in y
+
+ if( (pedPos-spawnPos).MagnitudeSqr() < minDistSqr )
+ {
+ return false;
+ }
+ i = mActivePeds.GetNextOf( i );
+ }
+ */
+
+ // make sure we're not spawning too close to another character
+ // or vehicle
+ float minDistSqr = GetRandomMinDistSqr();
+ float distSqr = 100000.0f;
+ rmt::Vector hisPos;
+
+ CharacterManager* cm = GetCharacterManager();
+ int maxChars = cm->GetMaxCharacters();
+ for( int i=0; i<maxChars; i++ )
+ {
+ Character* c = cm->GetCharacter(i);
+ if( c )
+ {
+ c->GetPosition( hisPos );
+ hisPos.y = spawnPos.y; // ignore diff in y
+
+ distSqr = (hisPos - spawnPos).MagnitudeSqr();
+ if( distSqr < minDistSqr )
+ {
+ return false;
+ }
+ }
+ }
+
+ VehicleCentral* vc = GetVehicleCentral();
+ for( int i=0; i<VehicleCentral::MAX_ACTIVE_VEHICLES; i++ )
+ {
+ Vehicle* v = vc->GetVehicle(i);
+ if( v )
+ {
+ v->GetPosition( &hisPos );
+ hisPos.y = spawnPos.y; // ignore diff in y
+
+ rmt::Sphere s;
+ v->GetBoundingSphere( &s );
+
+ distSqr = (hisPos - spawnPos).MagnitudeSqr();
+
+ minDistSqr = s.radius + 1.0f;
+ minDistSqr *= minDistSqr;
+
+ if( distSqr < minDistSqr )
+ {
+ return false;
+ }
+ }
+ }
+
+
+
+ // Choose a model for our new pedestrian
+ int modelID = GetRandomModel();
+ if( modelID <= -1 )
+ {
+ return false;
+ }
+
+ //*** Test ***
+ //rDebugPrintf( "Model randomly chosen: %d, %s\n", modelID, mModels[modelID].name );
+ //*** Test ***
+
+
+ // if reached maxcount for this model, go to next model and use it instead
+ bool foundUsable = false;
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ if( !mModels[modelID].free &&
+ mModels[modelID].currCount < mModels[modelID].maxCount )
+ {
+ foundUsable = true;
+ break;
+ }
+ // go to next model
+ modelID = (modelID+1) % PedestrianManager::MAX_MODELS;
+ }
+ // if we never found a model with currCount < maxCount, then
+ // we can't actually do any spawning...
+ if( !foundUsable )
+ {
+ rTunePrintf( "Can't spawn a new ped. Not enough Ped model "
+ "instances allowed in leveli.mfk. See Dusit or Sheik.\n" );
+ return false;
+ }
+
+ PedestrianStruct* freeNonNPDNotInCurrModelGroup = NULL;
+ PedestrianStruct* pedStrFoundInFreeList = NULL;
+
+ // See if this model is already available in our freepeds list...
+ // If so, we don't need to call SwapData (save some processing time)
+ bool foundInFreeList = false;
+ int j = mFreePeds.GetHead();
+ while( 0 <= j && j < DListArray::MAX_ELEMS )
+ {
+ pedStr = (PedestrianStruct*) mFreePeds.GetDataAt(j);
+ rAssert( pedStr != NULL );
+ if( !foundInFreeList && strcmp(pedStr->modelName, mModels[modelID].name)==0 )
+ {
+ foundInFreeList = true;
+ pedStrFoundInFreeList = pedStr;
+ //rDebugPrintf( "*** Yes! We found an existing model %s in the freelist!\n", pedStr->modelName );
+ //break;
+ }
+ else
+ {
+ if( strcmp(pedStr->modelName, "npd") != 0 &&
+ GetModelIDByName( pedStr->modelName ) == -1 )
+ {
+ freeNonNPDNotInCurrModelGroup = pedStr;
+ }
+ }
+ j = mFreePeds.GetNextOf( j );
+ }
+
+ // if none of the freepeds has the model we want, we need
+ // to swap out one of them and put in the model we want.
+ if( !foundInFreeList )
+ {
+ //
+ // Since we never found the model we want in the list of free peds,
+ // we should try to find a "free" model (npd, or a model not
+ // currently in our model group, so we don't clobber another real
+ // model that might be wanted later)..
+ //
+ // We could achieve this by searching for models not in current group in free list...
+ // However, if we never found one, then we have to just pick one, preferrably an "npd"
+ // Since we always add the most recently removed ped to the END of the freepeds
+ // list, we should take from the FRONT of the freepeds list... sort of a mini-scheduling
+ // algorithm, ya know...
+ //
+ if( freeNonNPDNotInCurrModelGroup )
+ {
+ pedStr = freeNonNPDNotInCurrModelGroup;
+ }
+ else
+ {
+ pedStr = (PedestrianStruct*) mFreePeds.GetFirst();
+ }
+ rAssert( pedStr != NULL );
+ bool res = SwapModels( modelID, pedStr );
+ if( !res )
+ {
+ return false;
+ }
+ }
+ else
+ {
+ pedStr = pedStrFoundInFreeList;
+ }
+
+ //////////////////////////////////////////////////////////
+ // NOTE:
+ // At this point we're ready to do the actual activating (no more bailing out)
+
+ //*** Test ***
+ //rDebugPrintf( "*** Adding %s\n", mModels[modelID].name );
+ //*** Test ***
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+
+ mModels[modelID].currCount++;
+
+ rAssert( strcmp( pedStr->modelName, mModels[modelID].name ) == 0 );
+
+ rAssert( pedStr->ped != NULL );
+
+ // choose a random swatch
+ int swatch = rand() % CharacterRenderable::NUM_CHARACTER_SWATCHES;
+ pedStr->ped->GetCharacter()->SetSwatch( swatch );
+
+ // add ped to path
+ bool res = path->AddPedestrian( );
+ rAssert( res );
+
+ /*
+ // get the segment's index (to the parent path's list of segments)
+ // this will be needed by Pedestrian::Update() when locomoting.
+ int pathSegmentIndex = segment->GetIndexToParentPath();
+ rAssert( 0 <= pathSegmentIndex && pathSegmentIndex < path->GetNumPathSegments() );
+ */
+
+ // set the ped's parameters
+ pedStr->ped->Activate( path, segment, spawnPos, pedStr->modelName );
+
+ // manage the ped manager's active/free list
+ res = mFreePeds.Remove( pedStr->listIndex );
+ rAssert( res );
+
+ pedStr->listIndex = mActivePeds.AddLast( pedStr );
+ rAssert( pedStr->listIndex != -1 );
+
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+ return true;
+}
+
+bool PedestrianManager::SwapModels( int modelID, PedestrianStruct* pedStr )
+{
+ rAssert( pedStr != NULL );
+ rTuneAssert( 0 <= modelID && modelID < PedestrianManager::MAX_MODELS );
+
+ Character* character = NULL;
+
+#ifdef RAD_DEBUG
+ SanityCheck();
+#endif
+ FindModelInUseAndRemove( pedStr->modelName );
+ bool res = FindModelInUseAndAdd( mModels[modelID].name );
+ if( res == false )
+ {
+ // we already removed pedStr->modelName from list, but haven't really
+ // swapped him out...
+ // If he's in the list of currently registered models, we don't want
+ // to really swap him out anyway (so call FindModelInUseAndAdd(pedStr->modelName))
+ // But if he's not in the list of currently registered models, we DO
+ // want to swap him out.. so call SwapData passing in "npd"
+ if( strcmp( pedStr->modelName, "npd" ) != 0 )
+ {
+ int index = GetModelIDByName( pedStr->modelName );
+ if( index == -1 )
+ {
+ character = pedStr->ped->GetCharacter();
+ rAssert( character != NULL );
+ ::GetCharacterManager()->SwapData( character, "npd", "npd" );
+ sprintf( pedStr->modelName, "npd" );
+ }
+ else
+ {
+ res = FindModelInUseAndAdd( pedStr->modelName );
+ rAssert( res );
+ }
+ }
+ return false;
+ }
+
+ character = pedStr->ped->GetCharacter();
+ rAssert( character != NULL );
+ ::GetCharacterManager()->SwapData( character, mModels[modelID].name, "npd" );
+
+ int nameLen = strlen( mModels[modelID].name );
+ rAssert( nameLen <= PedestrianManager::MAX_STRING_LEN );
+ strncpy( pedStr->modelName, mModels[modelID].name, PedestrianManager::MAX_STRING_LEN );
+ pedStr->modelName[nameLen] = '\0';
+
+#ifdef RAD_DEBUG
+ SanityCheck();
+#endif
+
+
+ return true;
+}
+
+int PedestrianManager::GetRandomModel()
+{
+ if( mNumRegisteredModels < 1 )
+ {
+ rTunePrintf( "PedestrianManager: You haven't registered a pedestrian model!\n" );
+ return -1;
+ }
+
+ // generate a number between 1 and mNumRegisteredModels
+ int num = (rand() % mNumRegisteredModels) + 1;
+ rAssert( 1 <= num && num <= mNumRegisteredModels );
+
+ // loop through models array counting till we get to num & return this modelID
+ int count = 0;
+ for(int i=0; i<PedestrianManager::MAX_MODELS; i++)
+ {
+ if( !mModels[i].free )
+ {
+ count++;
+ }
+ if( count==num )
+ {
+ return i;
+ }
+ }
+ rAssert( false ); // shouldn't get here
+ return -1;
+}
+
+bool PedestrianManager::RegisterModel( const char* name, int maxCount )
+{
+ rTuneAssert( name != NULL );
+ rTuneAssert( maxCount > 0 );
+
+ // search existing SPARSE list for name
+ int freeIndex = -1;
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ if( mModels[i].free )
+ {
+ freeIndex = i;
+ continue;
+ }
+
+ // found an existing model registered under same name, so overwrite w/ new values
+ if( strcmp(mModels[i].name, name)==0 )
+ {
+ mModels[i].maxCount = maxCount;
+ // currCount should be the same as there are active instances
+ #ifdef RAD_DEBUG
+ CheckModelCounts();
+ #endif
+ return true;
+ }
+ }
+
+ // If we haven't found an existing model, then we're adding one.
+ // See if we can...
+ if( mNumRegisteredModels >= PedestrianManager::MAX_MODELS )
+ {
+ return false;
+ }
+
+ if( 0 <= freeIndex && freeIndex < PedestrianManager::MAX_MODELS )
+ {
+ // loop through activelist looking for this model and counting the
+ // number of instances... init it with this value
+ int count = 0;
+ int n = mActivePeds.GetHead();
+ while( 0 <= n && n < DListArray::MAX_ELEMS )
+ {
+ PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt(n);
+ rAssert( pedStr );
+
+ if( strcmp( pedStr->modelName, name )==0 )
+ {
+ count++;
+ }
+
+ n = mActivePeds.GetNextOf( n );
+ }
+
+ mModels[freeIndex].Init( false, name, maxCount, count );
+ mNumRegisteredModels++;
+ return true;
+ }
+
+ return false;
+}
+
+bool PedestrianManager::UnregisterModel( const char* name )
+{
+ rAssert( name != NULL );
+
+ if( mNumRegisteredModels <= 0 )
+ {
+ return false;
+ }
+
+ // search existing SPARSE list for name
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ if( mModels[i].free )
+ {
+ continue;
+ }
+ int nameLen = strlen( name );
+ int modelNameLen = strlen( mModels[i].name );
+ if( (nameLen == modelNameLen) &&
+ (strncmp(mModels[i].name, name, nameLen)==0) )
+ {
+ mNumRegisteredModels--;
+ mModels[i].InitZero();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool PedestrianManager::UnregisterModel( int modelID )
+{
+ rAssert( 0 <= modelID && modelID < PedestrianManager::MAX_MODELS );
+
+ if( mNumRegisteredModels <= 0 )
+ {
+ return false;
+ }
+ if( mModels[modelID].free )
+ {
+ return false;
+ }
+ mNumRegisteredModels--;
+ mModels[modelID].InitZero();
+ return true;
+}
+
+void PedestrianManager::UnregisterAllModels()
+{
+ mNumRegisteredModels = 0;
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ mModels[i].InitZero();
+ }
+}
+
+void PedestrianManager::SwitchModelGroup( int toGroupID )
+{
+ rAssert( 0 <= toGroupID && toGroupID < PedestrianManager::MAX_MODEL_GROUPS );
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+
+ // if already in this group, forget it
+ if( mCurrGroupID == toGroupID )
+ {
+ return;
+ }
+
+ // NOTE:
+ // Don't want to UnregisterAllModels here because if say male1 exists in new group as
+ // well as in old group, we'll be wiping its currcount. So just go around resetting
+ // maxcount to zero and let RegisterModel overwrite the ones that are pertinent.
+ // In the end, unregister everything that doesn't have maxcount > 0
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ mModels[i].maxCount = 0;
+ }
+ for( int i=0; i<mModelGroups[toGroupID].numModels; i++ )
+ {
+ RegisterModel(
+ mModelGroups[toGroupID].models[i].name,
+ mModelGroups[toGroupID].models[i].maxCount );
+ }
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ if( mModels[i].maxCount <= 0 )
+ {
+ UnregisterModel( i );
+ }
+ }
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+ mCurrGroupID = toGroupID;
+
+#ifdef RAD_DEBUG
+ char msg[256];
+ sprintf( msg, "*** SWITCHING TO NEW MODEL GROUP: " );
+ for( int i=0; i<mModelGroups[toGroupID].numModels; i++ )
+ {
+ sprintf( msg, "%s%s ", msg, mModelGroups[toGroupID].models[i].name );
+ }
+ sprintf( msg, "%s\n", msg );
+ //rDebugPrintf( msg );
+#endif
+
+
+}
+
+int PedestrianManager::GetModelIDByName( const char* name )
+{
+ for( int i=0; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ if( strcmp( mModels[i].name, name )==0 )
+ {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void PedestrianManager::SetModelGroup( int groupID, const ModelGroup& group )
+{
+ rAssert( 0 <= groupID && groupID < PedestrianManager::MAX_MODEL_GROUPS );
+ rAssert( 0 <= group.numModels && group.numModels <= PedestrianManager::MAX_MODELS );
+
+ mModelGroups[groupID].numModels = group.numModels;
+
+ for( int i=0; i<group.numModels; i++ )
+ {
+ mModelGroups[groupID].models[i].Init(
+ group.models[i].name, group.models[i].maxCount );
+ }
+
+ for( int i=group.numModels ; i<PedestrianManager::MAX_MODELS; i++ )
+ {
+ mModelGroups[groupID].models[i].InitZero();
+ }
+}
+
+//=============================================================================
+// PedestrianManager::IsPed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Character* character )
+//
+// Return: bool
+//
+//=============================================================================
+bool PedestrianManager::IsPed( Character* character )
+{
+ int i;
+ for (i = 0; i < MAX_PEDESTRIANS; ++i )
+ {
+ if ( mPeds[ i ].ped->GetCharacter() == character )
+ {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+
+void PedestrianManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ /*** DEBUG ***
+ switch( id )
+ {
+ case EVENT_GETOUTOFVEHICLE_END:
+ {
+ static int count = 0;
+ SwitchModelGroup( count % 2 );
+ count++;
+ }
+ break;
+ }
+ *** DEBUG ***/
+
+ // Handle the events appropriately:
+ // - determine which model group we want to switch to and call SwitchToGroup on it
+ switch ( id )
+ {
+ case EVENT_LOCATOR + LocatorEvent::LOAD_PED_MODEL_GROUP:
+ {
+ PedGroupLocator* pLocator = (PedGroupLocator*)pEventData;
+ rAssert( pLocator != NULL );
+
+ unsigned int groupID = pLocator->GetGroupNum();
+ if( pLocator->GetPlayerEntered() )
+ {
+ SwitchModelGroup( (int)groupID );
+ }
+ break;
+ }
+ case EVENT_VEHICLE_DESTROYED:
+ {
+ // spawn a pedestrian in th driver's seat when a traffic vehicle is destroyed
+ Vehicle* vehicle = (Vehicle*)pEventData;
+ if( (GetGameplayManager()->GetCurrentLevelIndex() != RenderEnums::L7) &&
+ (strcmp(vehicle->GetDriverName(), "phantom") == 0) &&
+ mActivePeds.GetNumElems() > 0 )
+ {
+ int randPedNum = rand() % mActivePeds.GetNumElems();
+
+ Character* character = NULL;
+
+ int count = 0;
+ int ind = mActivePeds.GetHead();
+ while( 0 <= ind && ind <= MAX_PEDESTRIANS )
+ {
+ if( count == randPedNum )
+ {
+ PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( ind );
+ character = pedStr->ped->GetCharacter();
+ break;
+ }
+ count++;
+ ind = mActivePeds.GetNextOf( ind );
+ }
+
+ //Character* character = mPeds[rand() % MAX_PEDESTRIANS].ped->GetCharacter();
+
+ if(character)
+ {
+ vehicle->SetDriver(character);
+
+ character->SetFadeAlpha( 255 ); // make sure we're not faded.
+ character->SetTargetVehicle( vehicle );
+ character->SetInCar( true );
+ character->UpdateTransformToInCar();
+ character->GetStateManager()->SetState<CharacterAi::InCar>();
+ }
+ }
+ }
+ break;
+ case EVENT_PLAYER_VEHICLE_HORN: // fall thru
+ case EVENT_PLAYER_CAR_HIT_NPC: // fall thru
+ case EVENT_OBJECT_KICKED:
+ {
+ static const float TRIGGER_PANIC_RADIUS_SQR = 144.0f;
+
+ // Get player data
+ Avatar* player = GetAvatarManager()->GetAvatarForPlayer(0);
+ rmt::Vector playerPos;
+ player->GetPosition( playerPos );
+
+ // for any ped nearby, make him run away...
+ int i = mActivePeds.GetHead();
+ while( 0 <= i && i <= DListArray::MAX_ELEMS )
+ {
+ PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt( i );
+ rAssert( pedStr );
+
+ if( pedStr->ped->GetIsActive() )
+ {
+ rmt::Vector pedPos;
+ pedStr->ped->GetCharacter()->GetPosition( pedPos );
+ float distSqr = (playerPos - pedPos).MagnitudeSqr();
+
+ if( distSqr < TRIGGER_PANIC_RADIUS_SQR )
+ {
+ pedStr->ped->IncitePanic();
+ }
+ }
+ i = mActivePeds.GetNextOf( i );
+ }
+ }
+ break;
+
+ default:
+ {
+ break;
+ }
+ }
+}
+
+
+void PedestrianManager::AddPeds( rmt::Vector center, float addRadius )
+{
+ if( CommandLineOptions::Get( CLO_NO_PEDS ) )
+ {
+ return;
+ }
+
+ if( !mAllowAdd )
+ {
+ return;
+ }
+
+ // if no more free pedestrians...
+ if( mFreePeds.GetNumElems() <= 0 )
+ {
+ return;
+ }
+
+ // Invoke search on DSG for path segments that intersect with
+ // our radius
+ ReserveArray<PathSegment*> orList;
+ ::GetIntersectManager()->FindPathSegmentElems( center, addRadius, orList );
+
+ //
+ // For each segment we "intersect" with (in the loose sense), we
+ // a) break if we have no more pedestrians to spawn
+ // b) break if segment (or path?) already has a pedestrian
+ // c) put pedestrian on the segment at the intersection point.
+ // d) set pedestrian's facing/velocity/position, whatever's necessary
+ //
+ if(orList.mUseSize == 0)
+ return;
+
+ int which = rand() % orList.mUseSize;
+
+ PathSegment* segment = NULL;
+
+ segment = orList.mpData[which];
+ rAssert( segment != NULL );
+
+ // determine intersection point of the path segment & ped radius
+ // at normalized value "t"
+
+ rmt::Sphere s;
+ s.Set( center, addRadius );
+
+ rmt::Vector start, end;
+ segment->GetStartPos( start );
+ segment->GetEndPos( end );
+
+ rmt::Vector intPts[2];
+ int numIntersections = IntersectLineSphere( start, end, s, intPts );
+
+ // if no intersection, a mild surprise.
+ if( numIntersections <= 0 )
+ {
+ return;
+ }
+
+ // Just use the first intersection point
+ rmt::Vector spawnPos = intPts[0];
+ float t = GetLineSegmentT( start, end, spawnPos );
+
+ // activate the pedestrian
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+
+ bool succeeded = ActivatePed( segment, spawnPos );
+#ifdef RAD_DEBUG
+ CheckModelCounts();
+#endif
+}
+
+void PedestrianManager::FindModelInUseAndRemove( const char* name )
+{
+ if( tName::MakeUID(name) == tName::MakeUID("npd") )
+ {
+ return;
+ }
+ // should be able to find it in the list of models being used...
+ tUID uid = tEntity::MakeUID( name );
+ bool foundUid = false;
+
+ rAssert( 0 <= mNumActiveModels && mNumActiveModels <= PedestrianManager::MAX_MODELS_IN_USE );
+ for( int i=0; i<mNumActiveModels; i++ )
+ {
+ if( uid == mModelsInUse[i].uid.GetUID() )
+ {
+ // count for this ped should be > 0 if it was found in this list
+ rAssert( mModelsInUse[i].currCount > 0 );
+ mModelsInUse[i].currCount--;
+
+ // if currCount is zero, remove it from list of models currently in use.
+ if( mModelsInUse[i].currCount == 0 )
+ {
+ // swap last entry into this position
+ //rDebugPrintf( "*** Removing from ModelsInUse %s\n", name );
+ mModelsInUse[i].currCount = mModelsInUse[mNumActiveModels-1].currCount;
+ mModelsInUse[i].uid = mModelsInUse[mNumActiveModels-1].uid;
+ mNumActiveModels--;
+ }
+ foundUid = true;
+ break; // NOTE: MUST break here since we may have modified mNumActiveModels
+ }
+ }
+ if( !foundUid )
+ {
+ // should have found it! Something wrong with our code...
+ rAssert( false );
+ }
+}
+
+bool PedestrianManager::FindModelInUseAndAdd( const char* name )
+{
+ if( strcmp( name, "npd" ) == 0 )
+ {
+ return true;
+ }
+
+ // PS2 memory can only support a few (4) models in use at all times...
+ // see if we have room for this model
+ bool founduid = false;
+ tUID uid = tEntity::MakeUID( name );
+
+ rAssert( 0 <= mNumActiveModels && mNumActiveModels <= PedestrianManager::MAX_MODELS_IN_USE );
+ for( int i=0; i<mNumActiveModels; i++ )
+ {
+ if( uid == mModelsInUse[i].uid.GetUID() )
+ {
+ mModelsInUse[i].currCount++;
+ founduid = true;
+ break;
+ }
+ }
+ if( !founduid )
+ {
+ // couldn't find it in list of models currently in use, so try to add it
+ // to the list. If we can't, we don't spawn this model
+ if( mNumActiveModels == PedestrianManager::MAX_MODELS_IN_USE )
+ {
+ return false;
+ }
+ //rDebugPrintf( "*** Adding to ModelsInUse %s\n", name );
+ mModelsInUse[mNumActiveModels].uid.SetText( name );
+ mModelsInUse[mNumActiveModels].currCount = 1;
+ mNumActiveModels++;
+ }
+ return true;
+}
+
+void PedestrianManager::SanityCheck()
+{
+#ifdef RAD_DEBUG
+ // make sure every non-"npd" model in mPeds exists in ModelsInUse
+ char msg[256];
+ for( int i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
+ {
+ if( strcmp( mPeds[i].modelName, "npd" ) == 0 )
+ {
+ continue;
+ }
+
+ bool found = false;
+ for( int j=0; j<mNumActiveModels; j++ )
+ {
+ tUID test = tEntity::MakeUID( mPeds[i].modelName );
+ if( test == mModelsInUse[j].uid.GetUID() )
+ {
+ sprintf( msg, "%s already came up once in mModelsInUse list\n",
+ mPeds[i].modelName );
+ rAssertMsg( !found, msg);
+
+ sprintf( msg, "UIDs equal but names are not! %s vs %s\n",
+ mPeds[i].modelName, mModelsInUse[j].uid.GetText() );
+ rAssertMsg( strcmp( mPeds[i].modelName, mModelsInUse[j].uid.GetText() ) == 0, msg );
+ found = true;
+ }
+ }
+ sprintf( msg, "%s in mPeds could not be found in mModelsInUse\n",
+ mPeds[i].modelName );
+ rAssertMsg( found, msg );
+ }
+
+ // make sure every model in ModelsInUse is non-"npd" and exists in mPeds, with correct count
+ for( int i=0; i<mNumActiveModels; i++ )
+ {
+ rAssertMsg( strcmp( mModelsInUse[i].uid.GetText(), "npd" ) != 0,
+ "Found npd in mModelsInUse\n" );
+
+ bool found = false;
+ int count = 0;
+ for( int j=0; j<PedestrianManager::MAX_PEDESTRIANS; j++ )
+ {
+ tUID test = tEntity::MakeUID( mPeds[j].modelName );
+ if( test == mModelsInUse[i].uid.GetUID() )
+ {
+ count++;
+ sprintf( msg, "UIDs equal but names are not! %s vs %s\n",
+ mPeds[j].modelName, mModelsInUse[i].uid.GetText() );
+ rAssertMsg( strcmp( mPeds[j].modelName, mModelsInUse[i].uid.GetText() ) == 0, msg );
+ found = true;
+ }
+ }
+ sprintf( msg, "%s in mModelsInUse could not be found in mPeds\n",
+ mModelsInUse[i].uid.GetText() );
+ rAssertMsg( found, msg );
+
+ sprintf( msg, "Not equals numbers of %s found in mPeds (%d) and mModelsInUse (%d)\n",
+ mModelsInUse[i].uid.GetText(), count, mModelsInUse[i].currCount );
+ rAssertMsg( count == mModelsInUse[i].currCount, msg );
+ }
+#endif
+}
+
+
+void PedestrianManager::CheckModelCounts()
+{
+ // *** DEBUG ***
+ // do some checking here to make sure mModels' currcount is
+ // correct
+ for( int m=0; m<PedestrianManager::MAX_MODELS; m++ )
+ {
+ if( mModels[m].free )
+ {
+ continue;
+ }
+ int expectedCount = mModels[m].currCount;
+
+ int count = 0;
+ int n = mActivePeds.GetHead();
+ while( 0 <= n && n < DListArray::MAX_ELEMS )
+ {
+ PedestrianStruct* pedStr = (PedestrianStruct*) mActivePeds.GetDataAt(n);
+ rAssert( pedStr );
+
+ if( strcmp( pedStr->modelName, mModels[m].name )==0 )
+ {
+ count++;
+ }
+
+ n = mActivePeds.GetNextOf( n );
+ }
+ rAssert( count == expectedCount );
+ }
+ // *** DEBUG ***
+}
+
+void PedestrianManager::DumpAllPedModels()
+{
+ // deactivate all current peds
+ RemoveAllPeds();
+
+ // Swap "npd" in for the other models
+ for( int i=0; i<PedestrianManager::MAX_PEDESTRIANS; i++ )
+ {
+ PedestrianStruct* pedStr = &(mPeds[i]);
+ rAssert( pedStr );
+
+ if( tName::MakeUID( pedStr->modelName ) == tName::MakeUID( "npd" ) )
+ {
+ continue;
+ }
+
+ #ifdef RAD_DEBUG
+ SanityCheck();
+ #endif
+ // we're swapping out the model for npd... better update the modelsinuse list
+ FindModelInUseAndRemove( pedStr->modelName );
+
+ // since this model has been unregistered, it's not in the current
+ // model group, so keeping it around will only waste memory...
+ // we need to swap in "npd" in its place.
+ Character* character = pedStr->ped->GetCharacter();
+ rAssert( character != NULL );
+ ::GetCharacterManager()->SwapData( character, "npd", "npd" );
+ sprintf( pedStr->modelName, "npd" );
+
+ #ifdef RAD_DEBUG
+ SanityCheck();
+ #endif
+ }
+
+} \ No newline at end of file
diff --git a/game/code/worldsim/ped/pedestrianmanager.h b/game/code/worldsim/ped/pedestrianmanager.h
new file mode 100644
index 0000000..41829e5
--- /dev/null
+++ b/game/code/worldsim/ped/pedestrianmanager.h
@@ -0,0 +1,241 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: pedestrianmanager.h
+//
+// Description: Defines Pedestrian Manager class
+//
+// History: 09/18/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+
+#ifndef PEDESTRIANMANAGER_H
+#define PEDESTRIANMANAGER_H
+
+
+#include <worldsim/ped/pedestrian.h>
+#include <roads/geometry.h>
+#include <events/eventlistener.h>
+#include <p3d/entity.hpp>
+#include <string.h>
+
+class PathSegment;
+
+// Hey look! I'm a singleton!
+class PedestrianManager :
+public EventListener
+{
+
+// MEMBERS
+public:
+ // STATICS
+ static const int MAX_PEDESTRIANS = 7; // should be <= DListArray::MAX_ELEMS;
+ static const int MAX_MODELS_IN_USE = 4; // number of distinct models we can current support at a time
+ static const int MAX_MODELS = 10; // num distinct models for 2 groups (while one takes over the other)
+ static const int MAX_STRING_LEN = 64;
+ static const int MAX_MODEL_GROUPS = 10; // number of different groups we allow to be defined
+
+ static PedestrianManager* mInstance;
+ static const float FADE_RADIUS;
+ static const float CENTER_OFFSET;
+ static const float ADD_RADIUS;
+ static const float INITIAL_ADD_RADIUS;
+ static const float REMOVE_RADIUS;
+ static const unsigned int MILLISECONDS_PER_GROUND_INTERSECT;
+ static const unsigned int MILLISECONDS_BETW_ADDS;
+ static const unsigned int MILLISECONDS_BETW_REMOVES;
+ static const unsigned int MILLISECONDS_POPULATE_WORLD;
+
+ // NON-STATICS
+ struct ModelData
+ {
+ ModelData() : maxCount( 0 ) {};
+ char name[MAX_STRING_LEN+1];
+ int maxCount;
+ void Init( const char* modelName, int max )
+ {
+ rAssert( modelName != NULL );
+ rAssert( max > 0 );
+
+ int nameLen = strlen( modelName );
+ rAssert( nameLen <= PedestrianManager::MAX_STRING_LEN );
+ strncpy( name, modelName, PedestrianManager::MAX_STRING_LEN );
+ name[ nameLen ] = '\0';
+
+ maxCount = max;
+ }
+ void InitZero()
+ {
+ name[0] = '\0';
+ maxCount = 0;
+ }
+
+ };
+ struct ModelGroup
+ {
+ ModelGroup() : numModels( 0 ) {};
+ int numModels;
+ ModelData models[MAX_MODELS];
+ };
+
+ // Need lists to keep track of inactive peds & active peds
+ //
+ // Whenever we set mPeds[i] to active, remove it from free list
+ // and add it to active list:
+ //
+ // mFreePeds.Remove(mPeds[i].listIndex);
+ // mPeds[i].listIndex = mActivePeds.AddLast( &mPeds[i] );
+ //
+ // Whenever we set mPeds[i] to inactive, add it to free list &
+ // remove it from active list:
+ //
+ // mActivePeds.Remove(mPeds[i].listIndex);
+ // mPeds[i].listIndex = mFreePeds.AddLast(&mPeds[i]);
+ //
+ DListArray mFreePeds;
+ DListArray mActivePeds;
+
+ // Pedestrian alone is not sufficient for our purposes...
+ struct PedestrianStruct
+ {
+ PedestrianStruct() : ped( NULL ), listIndex( -1 ) {};
+ Pedestrian* ped;
+ int listIndex; // index to mActivePeds or mFreePeds depending on whether ped is active
+ char modelName [MAX_STRING_LEN+1]; // name of model
+ };
+
+
+// METHODS
+public:
+
+ // STATICS
+ static PedestrianManager* GetInstance();
+ static void DestroyInstance();
+ static int GetMaxPedestrians( void )
+ {
+ return MAX_PEDESTRIANS;
+ }
+
+ // NON-STATICS
+
+ void Init(); // Initializes pedmanager itself; called from GamePlayContext::OnStart()
+ void Update( unsigned int milliseconds ); // called once per frame by GamePlayContext::Update()
+
+ void RemovePed( Character* character );
+ void RemoveAllPeds();
+ void AllowAddingPeds( bool allowed );
+
+ void DumpAllPedModels(); // will remove all peds and dump their models (so revert all to "npd")
+
+ ////////// EVENT LISTENER STUFF /////////////
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ /////////////////////////////////////////////
+
+ // Makes all model groups default to:
+ // numModels = 4
+ // model 0 = male1, 2
+ // model 1 = fem1, 1
+ // model 2 = boy1, 1
+ // model 3 = girl1, 1
+ // called from LoadingContext::OnStart()
+ void InitDefaultModelGroups();
+
+ void SetModelGroup( int groupID, const ModelGroup& group );
+
+ bool IsPed( Character* character );
+
+ void SwitchModelGroup( int toGroupID );
+
+ static void SetDefaultModelGroup( int toGroupID );
+
+// MEMBERS
+private:
+ static int mDefaultModelGroup;
+
+ unsigned int mMillisecondsTillRemove;
+ unsigned int mMillisecondsTillAdd;
+
+ // all peds we'll ever use/need
+ PedestrianStruct mPeds[ MAX_PEDESTRIANS ];
+
+ unsigned int mMillisecondsPopulateWorld;
+
+ struct Model
+ {
+ Model() : free( true ), maxCount( 0 ), currCount( 0 ) {};
+
+ bool free; // whether or not this model slot has been registered
+ char name[MAX_STRING_LEN+1]; // name of the model (duh)
+ int maxCount; // max ped instances of this model
+ int currCount; // current num instances of this model
+ void InitZero()
+ {
+ free = true;
+ name[0] = '\0';
+ maxCount = 0;
+ currCount = 0;
+ }
+ void Init( bool isFree, const char* modelName, int max, int curr )
+ {
+ rAssert( modelName != NULL );
+ rAssert( max > 0 );
+ rAssert( curr >= 0 );
+
+ free = isFree;
+
+ int nameLen = strlen( modelName );
+ rAssert( nameLen <= PedestrianManager::MAX_STRING_LEN );
+ strncpy( name, modelName, PedestrianManager::MAX_STRING_LEN );
+ name[ nameLen ] = '\0';
+
+ maxCount = max;
+ currCount = curr;
+ }
+ };
+ Model mModels[MAX_MODELS]; // array of currently registered models (of current group)
+ int mNumRegisteredModels; // number of currently registerd models
+
+ ModelGroup mModelGroups[MAX_MODEL_GROUPS]; // groups of models for swapping
+ int mCurrGroupID;
+
+ struct UsedModel
+ {
+ UsedModel() : uid( 0 ), currCount( 0 ) {};
+ tName uid;
+ int currCount;
+ };
+ UsedModel mModelsInUse[ MAX_MODELS_IN_USE ];
+ int mNumActiveModels;
+
+ bool mAllowAdd;
+
+// METHODS
+private:
+ PedestrianManager();
+ virtual ~PedestrianManager();
+
+ void AddPeds( rmt::Vector center, float addRadius );
+ void DeactivatePed( PedestrianStruct* ped );
+ bool ActivatePed( PathSegment* segment, rmt::Vector spawnPos );
+
+ float GetRandomMinDistSqr();
+
+ int GetRandomModel();
+ bool SwapModels( int modelID, PedestrianStruct* pedStr );
+
+ bool RegisterModel( const char* name, int maxCount );
+ bool UnregisterModel( const char* name );
+ bool UnregisterModel( int modelID );
+ void UnregisterAllModels();
+ int GetModelIDByName( const char* name );
+
+ void FindModelInUseAndRemove( const char* name );
+ bool FindModelInUseAndAdd( const char* name );
+
+ void SanityCheck();
+ void CheckModelCounts();
+
+};
+
+#endif //PEDESTRIANMANAGER_H
diff --git a/game/code/worldsim/physicsairef.h b/game/code/worldsim/physicsairef.h
new file mode 100644
index 0000000..bff6d06
--- /dev/null
+++ b/game/code/worldsim/physicsairef.h
@@ -0,0 +1,51 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: physicsairef.h
+//
+// Description: Blahblahblah
+//
+// History: 6/17/2002 + Created -- TBJ
+//
+//=============================================================================
+
+#ifndef PHYSICSAIREF_H
+#define PHYSICSAIREF_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+namespace PhysicsAIRef
+{
+ enum
+ {
+ redBrickPhizDefault = 0,
+ redBrickVehicle,
+ redBrickPhizVehicleGroundPlane,
+ redBrickPhizMoveableGroundPlane, // might not need to differnetiate here!
+ redBrickPhizFence, // might not need to differnetiate here!
+ redBrickPhizStatic = 1 << 16,
+ redBrickPhizMoveable = 1 << 17, // don't think we actually need to differentiate between moveable and moveableinstance
+ PlayerCharacter,
+ NPCharacter,
+ redBrickPhizMoveableAnim = 1 << 18,
+ CameraSphere,
+ StateProp,
+ ActorStateProp,
+ redBrickPhizLast
+ }; // phizGround, phizStatic, phizMoveableAnim, phizMoveable, phizCamera, phizLast };
+};
+
+
+#endif //PHYSICSAIREF_H
diff --git a/game/code/worldsim/redbrick/allredbrick.cpp b/game/code/worldsim/redbrick/allredbrick.cpp
new file mode 100644
index 0000000..27f9cca
--- /dev/null
+++ b/game/code/worldsim/redbrick/allredbrick.cpp
@@ -0,0 +1,13 @@
+#include <worldsim/redbrick/geometryvehicle.cpp>
+#include <worldsim/redbrick/physicslocomotion.cpp>
+#include <worldsim/redbrick/physicslocomotioncontrollerforces.cpp>
+#include <worldsim/redbrick/redbrickcollisionsolveragent.cpp>
+#include <worldsim/redbrick/rootmatrixdriver.cpp>
+#include <worldsim/redbrick/suspensionjointdriver.cpp>
+#include <worldsim/redbrick/trafficlocomotion.cpp>
+#include <worldsim/redbrick/vehicle.cpp>
+#include <worldsim/redbrick/vehicleinit.cpp>
+#include <worldsim/redbrick/vehiclelocomotion.cpp>
+#include <worldsim/redbrick/wheel.cpp>
+#include <worldsim/redbrick/vehicleeventlistener.cpp>
+#include <worldsim/redbrick/trafficbodydrawable.cpp>
diff --git a/game/code/worldsim/redbrick/geometryvehicle.cpp b/game/code/worldsim/redbrick/geometryvehicle.cpp
new file mode 100644
index 0000000..8b7472b
--- /dev/null
+++ b/game/code/worldsim/redbrick/geometryvehicle.cpp
@@ -0,0 +1,3384 @@
+/*===========================================================================
+ geometryvehicle.cpp
+
+ created Dec 7, 2001
+ by Greg Mayer
+
+ Copyright (c) 2001 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+#include <camera/supercammanager.h>
+#include <contexts/bootupcontext.h>
+#include <worldsim/character/characterrenderable.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/wheel.h>
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/vehiclecentral.h>
+#include <render/RenderManager/RenderManager.h>
+
+#include <p3d/anim/compositedrawable.hpp>
+#include <p3d/billboardobject.hpp>
+#include <p3d/anim/multicontroller.hpp>
+#include <p3d/anim/poseanimation.hpp>
+#include <p3d/camera.hpp>
+#include <p3d/directionallight.hpp>
+#include <p3d/light.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/shadow.hpp>
+#include <p3d/view.hpp>
+#include <p3d/effects/particlesystem.hpp>
+#include <pddi/pddi.hpp>
+#include <p3d/utility.hpp>
+#include <typeinfo>
+#include <p3d/anim/visibilityanimation.hpp>
+
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <mission/statepropcollectible.h>
+#include <memory/srrmemory.h>
+
+#include <mission/gameplaymanager.h>
+#include <render/particles/particlemanager.h>
+#include <radmath/util.hpp>
+#include <debug/debuginfo.h>
+#include <worldsim/redbrick/trafficbodydrawable.h>
+#include <worldsim/coins/sparkle.h>
+
+//#define SHADOW_EDITTING
+
+const int TYPICAL_NUMBER_OF_FRAME_CONTROLLERS = 16;
+
+// this is by artist design... the value is between 0 and 255
+static const int INCAR_ROOF_ALPHA = 100;
+// The velocity at which variable emission particle systems
+// reach their peak (13.5 meters/second ~ 50 kph)
+static const float PARTICLE_SYSTEM_MAX_VELOCITY = 13.5f;
+static const float MAX_Y_OFFSET = 0.25f; // Max distance the shadow will raise to avoid Z Chew.
+
+// Listing of special frame controllers
+// This list might be best moved into some sort of script file or data chunk
+// rather than being hardcoded if many more get added
+// Currently its got framecontrollers for the flame effects on the hoverbike
+static const GeometryVehicle::VehicleFrameController FRAME_CONTROLLER_DATA[] =
+{
+ // These are reversable framecontrollers
+ { "BQG_flame4Shape", NULL, true, eNone, 0, 1.0f, 0, 0 },
+ { "BQG_flame3Shape", NULL, true, eNone, 0, 1.0f, 0, 0 },
+ { "BQG_flame2Shape", NULL, true, eNone, 1.0f, 0, 0, 0 },
+ { "BQG_flame1Shape", NULL, true, eNone, 1.0f, 0, 0, 0 },
+ { "BQG_flame5Shape", NULL, true, eNone, 1.0f, 0, 0, 0 },
+ { "BQG_flame6Shape", NULL, true, eNone, 1.0f, 0, 0, 0 },
+ { "EFX_backfireSystShape", NULL, false, eBackfire, 0,0,0, 0 },
+ { "BQG_backfireflashGroupShape", NULL, false, eBackfire, 0,0,0 },
+ { "PTRN_ship", NULL, false, eNone, 0, 0, 1.0f/25.0f, 0 },
+ { "PTRN_redbrick", NULL, false, eNone, 0, 0, 0.08f, 5.0f }
+ //,
+// { "PTRN_ship", NULL, false, eNone, 2.0f,-2.0f,0,0 }
+};
+
+const rmt::Vector BARRACUDA_COLLECTIBLE_POS( 0, 0.4f, -3.0f );
+const rmt::Vector JEEP_COLLECTIBLE_POS( -0.237f, 0.216f, -2.243f );
+const rmt::Vector DUNE_COLLECTIBLE_ROT( rmt::DegToRadian( -90 ), rmt::DegToRadian( 0 ), rmt::DegToRadian( 0 ));
+const rmt::Vector DUNE_COLLECTIBLE_POS( 0, 0.714f, 0);
+
+const float BARREL_RADIUS = 0.351f;
+
+const rmt::Vector WITCH_COLLECTIBLE_POS( 0, 0.4f, -1.31f - BARREL_RADIUS );
+const rmt::Vector COFFIN_COLLECTIBLE_POS( 0, 0.4f, -1.59f - BARREL_RADIUS );
+const rmt::Vector HALLO_COLLECTIBLE_POS( 0, 0.4f, -3.467f - BARREL_RADIUS );
+
+
+const rmt::Matrix DEFAULT_COLLECTIBLE_TRANSFORM( 0.9219f, -0.3875f, 0.0f, 0.0f,
+ 0.3797f, 0.9034f, -0.1994f, 0.0f,
+ 0.0773f, 0.1838f, 0.9799f, 0.0f,
+ 0.0f, 0.1270f, -2.6610f, 1.0f );
+
+
+static const int NUM_FRAME_CONTROLLERS = sizeof( FRAME_CONTROLLER_DATA ) / sizeof( FRAME_CONTROLLER_DATA[0] );
+static bool sbDrawVehicle = true;
+static float refractiveIndex = 10.0f;
+
+//------------------------------------------------------------------------
+GeometryVehicle::GeometryVehicle():
+ m_Collectible( NULL ),
+ m_EnvRef( 0x40 )
+{
+ mCompositeDrawable = 0;
+
+ mChassisGeometry = 0;
+
+
+ mAnim = 0;
+
+ mAnimRevPerSecondBase = 1.0f;
+
+ mVehicleOwner = 0;
+
+ //mRevMult = 0.5f;
+ //mRevMult = 0.4f;
+ mRevMult = 0.1f; // good for frink - anyone else need this?
+
+
+ mHoodShader = 0;
+ mTrunkShader = 0;
+ mDoorPShader = 0;
+ mDoorDShader = 0;
+
+ mHoodTextureDam = 0;
+ mTrunkTextureDam = 0;
+ mDoorPTextureDam = 0;
+ mDoorDTextureDam = 0;
+
+ mHoodTextureNorm = 0;
+ mTrunkTextureNorm = 0;
+ mDoorPTextureNorm = 0;
+ mDoorDTextureNorm = 0;
+
+
+ mChassisShader = 0;
+ mChassisTextureNorm = 0;
+ mChassisTextureDam = 0;
+
+ mParticleEmitter = 0;
+ mVariableEmissionParticleSystem = 0;
+ mEngineParticleAttr.mType = ParticleEnum::eNull;
+ mLeftWheelParticleAttr.mType = ParticleEnum::eNull;
+ mRightWheelParticleAttr.mType = ParticleEnum::eNull;
+ mTailPipeParticleAttr.mType = ParticleEnum::eNull;
+
+ mSkidMarkGenerator = 0;
+
+ mSpecialEffect = ParticleEnum::eNull;
+
+ mLastPosition = rmt::Vector (0.0f, 0.0f, 0.0f);
+ mCurEnvMapRotation = 0.0f;
+
+ mBrakeLightJoints[0] = -1;
+ mBrakeLightJoints[1] = -1;
+ mBrakeLightJoints[2] = -1;
+ mBrakeLightJoints[3] = -1;
+
+ mReverseLightJoints[0] = -1;
+ mReverseLightJoints[1] = -1;
+ mReverseLightJoints[2] = -1;
+ mReverseLightJoints[3] = -1;
+
+ mTrafficBodyDrawable = NULL;
+ mTrafficDoorDrawable = NULL;
+
+ for( int i = 0; i < 4; ++i )
+ {
+ mShadowPointAdjustments[ i ][ 0 ] = 0.0f;
+ mShadowPointAdjustments[ i ][ 1 ] = 0.0f;
+ }
+
+ mFadeAlpha = 255;
+
+ for( int i=0; i<NUM_BRAKELIGHT_BBQS; i++ )
+ {
+ mBrakeLights[i] = NULL;
+ }
+
+#ifdef RAD_WIN32
+ mFrinkArc = NULL;
+#endif
+
+ mUsingTrafficModel = false;
+
+ /*
+ for( int i=0; i<NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ mHeadLights[i] = NULL;
+ }
+ */
+
+ mHasGhostGlow = false;
+ for( int i=0; i<NUM_GHOSTGLOW_BBQGS; i++ )
+ {
+ mGhostGlows[i] = NULL;
+ }
+
+ mHasNukeGlow = false;
+ for( int i=0; i<NUM_NUKEGLOW_BBQGS; i++ )
+ {
+ mNukeGlows[i] = NULL;
+ }
+
+ mBrakeLightsOn = false;
+ mBrakeLightScale = 0.0f;
+ mHeadLightScale = 0.0f;
+ mEnableLights = true; // sets visibility on headlights and brakelights
+ mLightsOffDueToDamage = false;
+
+ mRoofShader = NULL;
+ mRoofAlpha = 255;
+ mRoofTargetAlpha = 255;
+
+ mRoofOpacShape = NULL;
+ mRoofAlphaShape = NULL;
+
+ for( int i = 0; i < MAX_REFRACTION_SHADERS; ++i )
+ {
+ mRefractionShader[ i ] = NULL;
+ }
+ // Lets make a collectible transform
+ // Hard code it for now. Later use car specific ones from Kevin
+ m_CollectibleTransform = DEFAULT_COLLECTIBLE_TRANSFORM;
+
+ mFrameControllers.reserve( TYPICAL_NUMBER_OF_FRAME_CONTROLLERS );
+
+ //
+ // Add refractive index to the watcher
+ //
+ static bool refractiveIndexAdded = false;
+ if( !refractiveIndexAdded )
+ {
+ radDbgWatchAddFloat( &refractiveIndex, "RefractionShader", "RefraciveIndex", 0, 0, -20.0f, 20.0f );
+ refractiveIndexAdded = true;
+ }
+}
+
+
+//------------------------------------------------------------------------
+GeometryVehicle::~GeometryVehicle()
+{
+#ifdef SHADOW_EDITTING
+ for( int i = 0; i < 4; ++i )
+ {
+ radDbgWatchDelete( &(mShadowPointAdjustments[ i ][ 0 ]) );
+ radDbgWatchDelete( &(mShadowPointAdjustments[ i ][ 1 ]) );
+ }
+#endif
+ if(mCompositeDrawable)
+ {
+ GetRenderManager()->mEntityDeletionList.Add((tRefCounted*&)mCompositeDrawable);
+ mCompositeDrawable = 0;
+ }
+
+ if(mVariableEmissionParticleSystem)
+ {
+ GetRenderManager()->mEntityDeletionList.Add((tRefCounted*&)mVariableEmissionParticleSystem);
+ mVariableEmissionParticleSystem = 0;
+ }
+
+ if(mChassisGeometry)
+ {
+ mChassisGeometry->Release();
+ mChassisGeometry = 0;
+ }
+
+ if(mAnim)
+ {
+ mAnim->Release();
+ mAnim = 0;
+ }
+
+
+
+ for( int i = 0; i < MAX_REFRACTION_SHADERS; ++i )
+ {
+ tRefCounted::Release( mRefractionShader[ i ] );
+ mRefractionShader[ i ] = 0;
+ }
+
+ // damage state crapola
+
+ if(mHoodShader) mHoodShader->Release();
+ if(mTrunkShader) mTrunkShader->Release();
+ if(mDoorPShader) mDoorPShader->Release();
+ if(mDoorDShader) mDoorDShader->Release();
+
+ if(mHoodTextureNorm) mHoodTextureNorm->Release();
+ if(mTrunkTextureNorm) mTrunkTextureNorm->Release();
+ if(mDoorPTextureNorm) mDoorPTextureNorm->Release();
+ if(mDoorDTextureNorm) mDoorDTextureNorm->Release();
+
+ if(mHoodTextureDam) mHoodTextureDam->Release();
+ if(mTrunkTextureDam) mTrunkTextureDam->Release();
+ if(mDoorPTextureDam) mDoorPTextureDam->Release();
+ if(mDoorDTextureDam) mDoorDTextureDam->Release();
+
+
+ if(mChassisShader) mChassisShader->Release();
+ if(mChassisTextureNorm) mChassisTextureNorm->Release();
+ if(mChassisTextureDam) mChassisTextureDam->Release();
+
+ delete mParticleEmitter;
+ if ( mSpecialEffect != ParticleEnum::eNull )
+ {
+ GetParticleManager()->DeleteSystem( mSpecialEffect );
+ }
+
+ if ( mVariableEmissionParticleSystem != 0 )
+ {
+ mVariableEmissionParticleSystem->ReleaseParticles();
+ mVariableEmissionParticleSystem = 0;
+ }
+
+ if(mSkidMarkGenerator)
+ {
+ delete mSkidMarkGenerator;
+ }
+
+ if( mTrafficBodyDrawable )
+ {
+ mTrafficBodyDrawable->Release();
+ mTrafficBodyDrawable = NULL;
+ }
+
+ if( mTrafficDoorDrawable )
+ {
+ mTrafficDoorDrawable->Release();
+ mTrafficDoorDrawable = NULL;
+ }
+
+ for( int i=0; i<NUM_BRAKELIGHT_BBQS; i++ )
+ {
+ if( mBrakeLights[i] )
+ {
+ mBrakeLights[i]->Release();
+ mBrakeLights[i] = NULL;
+ }
+ }
+
+#ifdef RAD_WIN32
+ if( mFrinkArc )
+ {
+ mFrinkArc->Release();
+ mFrinkArc = NULL;
+ }
+#endif
+ /*
+ for( int i=0; i<NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ if( mHeadLights[i] )
+ {
+ mHeadLights[i]->Release();
+ mHeadLights[i] = NULL;
+ }
+ }
+ */
+ for( int i=0; i<NUM_GHOSTGLOW_BBQGS; i++ )
+ {
+ if( mGhostGlows[i] )
+ {
+ mGhostGlows[i]->Release();
+ mGhostGlows[i] = NULL;
+ }
+ }
+ for( int i=0; i<NUM_NUKEGLOW_BBQGS; i++ )
+ {
+ if( mNukeGlows[i] )
+ {
+ mNukeGlows[i]->Release();
+ mNukeGlows[i] = NULL;
+ }
+ }
+ if( mRoofShader )
+ {
+ mRoofShader->Release();
+ mRoofShader = NULL;
+ }
+ if( mRoofOpacShape )
+ {
+ mRoofOpacShape = NULL;
+ }
+ if( mRoofAlphaShape )
+ {
+ mRoofAlphaShape = NULL;
+ }
+
+
+ for ( unsigned int fc = 0 ; fc < mFrameControllers.size() ; fc++ )
+ {
+ mFrameControllers[fc].frameController->Release();
+ }
+
+ if ( m_Collectible != NULL )
+ {
+ m_Collectible->Release();
+ m_Collectible = NULL;
+ }
+}
+
+
+//------------------------------------------------------------------------
+bool GeometryVehicle::Init( const char* name, Vehicle* owner, int num)
+{
+#ifdef SHADOW_EDITTING
+ for( int i = 0; i < 4; ++i )
+ {
+ mShadowPointAdjustments[ i ][ 0 ] = 0.0f;
+ mShadowPointAdjustments[ i ][ 1 ] = 0.0f;
+ char text[ 128 ];
+ sprintf( text, "%s Shadow point %d X", name, i );
+ radDbgWatchAddFloat( &(mShadowPointAdjustments[ i ][ 0 ]), text, "VehicleShadow", 0, 0, -5.0f, 5.0f );
+ sprintf( text, "%s Shadow point %d Y", name, i );
+ radDbgWatchAddFloat( &(mShadowPointAdjustments[ i ][ 1 ]), text, "VehicleShadow", 0, 0, -5.0f, 5.0f );
+ }
+#endif
+ mVehicleOwner = owner;
+
+ mCurEnvMapRotation = 0.0f;
+
+ return GetArt(name);
+}
+
+
+//=============================================================================
+// GeometryVehicle::InitSkidMarks
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::InitSkidMarks()
+{
+ // called from vehicle Init
+ // only for user vehicles
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mSkidMarkGenerator = new(gma)SkidMarkGenerator;
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ // initial value
+
+ rmt::Vector offset = mVehicleOwner->mSuspensionRestPoints[i];
+ offset.y -= mVehicleOwner->mWheels[i]->mRadius;
+
+ mSkidMarkGenerator->SetWheelOffset(i, offset); // need to do this everyframe
+
+ float length = mVehicleOwner->mWheels[i]->mRadius * 0.6f;
+ //float width = mVehicleOwner->mWheels[i]->mRadius * 0.5f; //??
+ float width = mVehicleOwner->mWheels[i]->mRadius * 0.8f; //??
+
+ mSkidMarkGenerator->SetWheelDimensions(i, width, length);
+
+ //void GenerateSkid( int wheel, const SkidData& );
+
+ //void Update();
+
+ }
+
+
+}
+
+
+//=============================================================================
+// GeometryVehicle::InitParticles
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::InitParticles()
+{
+MEMTRACK_PUSH_GROUP( "GeometryVehicle" );
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mParticleEmitter = new(gma)VehicleParticleEmitter;
+
+ rmt::Vector smokeOffset = mVehicleOwner->GetSmokeOffset();
+
+ mParticleEmitter->SetPartLocation(VehicleParticleEmitter::eEngine, smokeOffset);
+
+ rmt::Vector wheelOffset = mVehicleOwner->GetWheel0Offset();
+ mParticleEmitter->SetPartLocation(VehicleParticleEmitter::eRightBackTire, wheelOffset);
+
+ wheelOffset = mVehicleOwner->GetWheel1Offset();
+ mParticleEmitter->SetPartLocation(VehicleParticleEmitter::eLeftBackTire, wheelOffset);
+MEMTRACK_POP_GROUP("GeometryVehicle");
+}
+
+
+
+//=============================================================================
+// GeometryVehicle::GetHeadlightScale
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float GeometryVehicle::GetHeadlightScale()
+{
+ return mHeadLightScale;
+}
+
+
+//=============================================================================
+// GeometryVehicle::SetHeadlightScale
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float scale )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetHeadlightScale( float scale )
+{
+ rAssert( 0.0f <= scale );
+ this->mHeadLightScale = scale;
+}
+
+
+//=============================================================================
+// GeometryVehicle::AttachCollectible
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( StatePropCollectible* drawable )
+//
+// Return: bool
+//
+//=============================================================================
+bool GeometryVehicle::AttachCollectible( StatePropCollectible* drawable )
+{
+ bool wasAttached;
+ // We only have one slot for a collectible per vehicle
+ // check that this vehicle's slot is currently empty
+ if ( m_Collectible == NULL )
+ {
+ tRefCounted::Assign( m_Collectible, drawable );
+ rmt::Matrix identity;
+ identity.Identity();
+ drawable->SetTransform( identity );
+ m_Collectible->EnableCollisionVolume( false );
+ m_Collectible->SetState( 1 );
+ m_Collectible->RemoveFromDSG();
+ m_Collectible->EnableHudIcon( false );
+ m_Collectible->EnableCollisionTesting( false );
+ wasAttached = true;
+ }
+ else
+ {
+ wasAttached = false;
+ }
+
+ return wasAttached;
+}
+
+
+//=============================================================================
+// GeometryVehicle::GetAttachedCollectible
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: StatePropCollectible
+//
+//=============================================================================
+StatePropCollectible* GeometryVehicle::GetAttachedCollectible()
+{
+ return m_Collectible;
+}
+
+
+//=============================================================================
+// GeometryVehicle::DetachCollectible
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& velocity, bool explode )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::DetachCollectible( const rmt::Vector& velocity, bool explode )
+{
+ if ( m_Collectible )
+ {
+ m_Collectible->EnableHudIcon( false );
+ m_Collectible->EnableCollisionVolume( false );
+ m_Collectible->SetState( 0 );
+ sim::SimState* sim = m_Collectible->GetSimState();
+ if ( sim )
+ {
+ sim->ResetVelocities();
+ }
+ m_Collectible->AddToDSG();
+ // Lets set the transform to have no rotation, but position remains the same
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( m_CollectibleTransform.Row(3) + mVehicleOwner->GetPosition() );
+ m_Collectible->SetTransform(transform);
+
+ m_Collectible->Release();
+ if ( explode )
+ m_Collectible->Explode();
+
+ m_Collectible = NULL;
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::Display
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::Display()
+{
+BEGIN_PROFILE("GeometryVehicle::Display SetUp")
+ rmt::Vector pos = mVehicleOwner->GetPosition ();
+ rmt::Vector movement = pos - mLastPosition;
+ bool smokeFirst = true; // Used to fix draw order problem with the smoke and a vehicle's windshield.
+ tCamera* camera = p3d::context->GetView()->GetCamera();
+ rAssert( camera );
+ const rmt::Matrix& camTrans = camera->GetCameraToWorldMatrix();
+
+ if( mVehicleOwner->mVehicleType == VT_USER )
+ {
+ // Set the rotation vector on all the car's shaders.
+ // Base the rotation on how far the car has moved in the last known direction,
+ // so that the reflection map moves across the car when the car moves. -- jdy
+ //
+
+ bool fucked = false;
+ if ( mLastPosition.MagnitudeSqr() != 0.0f &&
+ ( rmt::Fabs( movement.x ) > 1000.0f ||
+ rmt::Fabs( movement.y ) > 1000.0f ||
+ rmt::Fabs( movement.z ) > 1000.0f ) )
+ {
+ //TODO: What the hell is causing this?
+ //mVehicleOwner position and facing are fucked.
+ //We've moved too far. Clamp.
+ pos = mLastPosition;
+ fucked = true;
+ }
+
+ if ( !fucked )
+ {
+ float distance = movement.DotProduct( mVehicleOwner->GetFacing() );
+ rAssert( !rmt::IsNan( distance ) );
+ if( rmt::IsNan( distance ) )
+ {
+ distance = 0.0f;
+ }
+ if( rmt::Fabs( distance ) > 0.01f && rmt::Fabs( distance ) < 50.0f ) // Car must move at least 1 cm for an adjustment to be made
+ {
+ // Find the normal of the plane defined by the direction of movement and the camera facing.
+ //This will be the axis we rotate around.
+ // Note that the reflection map is relative to the object so we need to transform everything
+ //into the object's space.
+ rmt::Matrix carOri = mVehicleOwner->GetTransform();
+ carOri.Invert();
+ carOri.FillTranslate( rmt::Vector( 0.0f, 0.0f, 0.0f ) );
+ rmt::Vector camRelCar;
+ carOri.Transform( camTrans.Row( 2 ), &camRelCar );
+ rmt::Vector carRelMovement;
+ carOri.Transform( movement, &carRelMovement );
+ rmt::Vector axis;
+ float camMovementFacing = carRelMovement.Dot( camRelCar );
+ if( camMovementFacing < 0.0f )
+ {
+ axis.CrossProduct(camRelCar, carRelMovement);
+ }
+ else
+ {
+ axis.CrossProduct(carRelMovement, camRelCar);
+ }
+ // Check if the car is moving towards or away from the camera.
+ mCurEnvMapRotation += distance * 0.05f;
+ while( mCurEnvMapRotation >= rmt::PI_2 )
+ {
+ mCurEnvMapRotation -= rmt::PI_2;
+ }
+ while( mCurEnvMapRotation < 0.0f )
+ {
+ mCurEnvMapRotation += rmt::PI_2;
+ }
+ float mag;
+ float angle;
+ CartesianToPolar( axis.x, axis.y, &mag, &angle );
+
+ tShaderVectorBroadcast cbPos( PDDI_SP_ROTVEC, rmt::Vector( mCurEnvMapRotation, 0.0f, -angle ) );
+ int count = mCompositeDrawable->GetNumDrawableElement ();
+ for (int i = 0; i < count; i++)
+ {
+ tCompositeDrawable::DrawableElement* pElement = mCompositeDrawable->GetDrawableElement( i );
+ tDrawable* pDrawable = pElement->GetDrawable();
+ pDrawable->ProcessShaders( cbPos );
+ }
+ }
+ // Remember the position of the car for next time
+ //
+ mLastPosition = pos;
+ }
+ }
+ else
+ {
+ mLastPosition = pos;
+ }
+
+ rmt::Vector vehicleVelocity;
+ mVehicleOwner->GetVelocity( &vehicleVelocity );
+
+ if(mEngineParticleAttr.mType != ParticleEnum::eNull)
+ {
+ // Figure out which to draw first, the smoke or the car.
+ //We do this by transforming the SmokeOffset into world space and then dot product with the
+ //the camera's facing vector. If it is towards the camera we draw the smoke after the car.
+ //Crude, but quick and hopefully there won't be too many visual artifacts.
+ rmt::Matrix facing = mVehicleOwner->mTransform;
+ facing.FillTranslate( rmt::Vector( 0.0f, 0.0f, 0.0f ) );
+ rmt::Vector smoke = mVehicleOwner->GetSmokeOffset();
+ facing.Transform( smoke, &smoke );
+ float dot = smoke.DotProduct( camTrans.Row( 2 ) );
+ smokeFirst = dot > 0.6f;
+
+ if( ( mEngineParticleAttr.mType == ParticleEnum::eEngineSmokeLight ) ||
+ ( mEngineParticleAttr.mType == ParticleEnum::eEngineSmokeMedium ) )
+ {
+ GetSparkleManager()->AddSmoke( mVehicleOwner->mTransform, mVehicleOwner->GetSmokeOffset(), movement, ( ( 0.4f - mVehicleOwner->GetVehicleLifePercentage( mVehicleOwner->mHitPoints ) ) * 2.5f ) );
+ }
+ else
+ {
+ // draw some particles
+ //mEngineParticleAttr.mVelocity = vehicleVelocity / 45.0f;
+ mParticleEmitter->Generate( VehicleParticleEmitter::eEngine,
+ mEngineParticleAttr, mVehicleOwner->mTransform);
+
+
+ // TODO - HOW ABOUT TRAFFIC - WITH THE ONE-TIME POOF!! DON'T DRAW CONTINUOUSLY
+ }
+ }
+ if ( mSpecialEffect != ParticleEnum::eNull )
+ {
+ ParticleAttributes attr;
+ attr.mType = mSpecialEffect;
+
+ mParticleEmitter->Generate( VehicleParticleEmitter::eSpecialEmitter,
+ attr, mVehicleOwner->mTransform);
+
+ }
+ // If this vehicle has tailpipe particles, generate them, always
+
+ // 31.25 (trails too long)
+ //mTailPipeParticleAttr.mVelocity = vehicleVelocity / 29.0f;
+
+ if ( mTailPipeParticleAttr.mType != ParticleEnum::eNull )
+ {
+ mParticleEmitter->Generate( VehicleParticleEmitter::eRightTailPipe,
+ mTailPipeParticleAttr, mVehicleOwner->mTransform );
+
+ mParticleEmitter->Generate( VehicleParticleEmitter::eLeftTailPipe,
+ mTailPipeParticleAttr, mVehicleOwner->mTransform );
+ }
+
+ if(mLeftWheelParticleAttr.mType != ParticleEnum::eNull)
+ {
+ mParticleEmitter->Generate( VehicleParticleEmitter::eLeftBackTire,
+ mLeftWheelParticleAttr, mVehicleOwner->mTransform);
+ }
+ if(mRightWheelParticleAttr.mType != ParticleEnum::eNull)
+ {
+ mParticleEmitter->Generate( VehicleParticleEmitter::eRightBackTire,
+ mRightWheelParticleAttr, mVehicleOwner->mTransform);
+ }
+
+// if( mVehicleOwner->mVehicleType == VT_USER )
+ {
+ // Change the vehicle environment reflection
+ //depending on if it is interior or damaged.
+ unsigned char blend;
+ if( !mVehicleOwner->mInterior )
+ {
+ blend = m_EnvRef;
+ }
+ else
+ {
+ blend = m_EnvRef >> 2; // Quarter value for interior.
+ }
+ float damage = mVehicleOwner->GetVehicleLifePercentage(mVehicleOwner->mHitPoints);
+ damage = rmt::Clamp( ( damage * 0.75f ) + 0.25f, 0.0f, 1.0f );
+ blend = (unsigned char)( blend * damage );
+ tShaderColourBroadcast envBlend( PDDI_SP_ENVBLEND, tColour( blend, blend, blend ) );
+ int count = mCompositeDrawable->GetNumDrawableElement ();
+ for (int i = 0; i < count; i++)
+ {
+ tCompositeDrawable::DrawableElement* pElement = mCompositeDrawable->GetDrawableElement (i);
+ tDrawable* pDrawable = pElement->GetDrawable ();
+ pDrawable->ProcessShaders( envBlend );
+ }
+ }
+
+END_PROFILE("GeometryVehicle::Display SetUp")
+BEGIN_PROFILE("GeometryVehicle::Display Render")
+
+ if( mUsingTrafficModel )
+ {
+ if( mTrafficBodyDrawable != NULL )
+ {
+ if( mFadeAlpha != 255 )
+ {
+ mTrafficBodyDrawable->mFading = true;
+ }
+ else
+ {
+ mTrafficBodyDrawable->mFading = false;
+ }
+ mTrafficBodyDrawable->mFadeAlpha = mFadeAlpha;
+ }
+ if( mTrafficDoorDrawable != NULL )
+ {
+ if( mFadeAlpha != 255 )
+ {
+ mTrafficDoorDrawable->mFading = true;
+ }
+ else
+ {
+ mTrafficDoorDrawable->mFading = false;
+ }
+ mTrafficDoorDrawable->mFadeAlpha = mFadeAlpha;
+ }
+
+
+ // NOTE:
+ // This is a big cause for major grievances. It will force the blend mode of
+ // all the shaders of the compositedrawable's elements to be BLEND_ALPHA...
+ // bad news for billboards that are BLEND_NONE or BLEND_ADD...
+ //
+ tShaderIntBroadcast blendModeAlpha( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ mCompositeDrawable->ProcessShaders( blendModeAlpha );
+
+ tShaderIntBroadcast emissiveAlpha( PDDI_SP_EMISSIVEALPHA, mFadeAlpha );
+ mCompositeDrawable->ProcessShaders( emissiveAlpha );
+ }
+
+
+#ifdef RAD_WIN32
+ const tName& name = mVehicleOwner->GetNameObject();
+ if( name == "frink_v" )
+ {
+ float topSpeed = mVehicleOwner->GetTopSpeed();
+ float percentOfTopSpeed = vehicleVelocity.Magnitude() / topSpeed;
+ float refraction = percentOfTopSpeed;
+ refraction = rmt::Clamp( refraction, 0.0f, 1.0f );
+
+ int emissiveAlpha = (int)( 255.0f * (1.0f - refraction) );
+
+ tShaderIntBroadcast blendMode( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ tShaderIntBroadcast emissive( PDDI_SP_EMISSIVEALPHA, emissiveAlpha );
+ int count = mCompositeDrawable->GetNumDrawableElement ();
+ for (int j = 0; j < count; j++)
+ {
+ tCompositeDrawable::DrawableElement* pElement = mCompositeDrawable->GetDrawableElement( j );
+ tDrawable* pDrawable = pElement->GetDrawable();
+ pDrawable->ProcessShaders( blendMode );
+ pDrawable->ProcessShaders( emissive );
+ }
+
+ // so since we just made EVERY SHADER alpha blend, we need to take
+ // care of the BBQGs (that need to be additive blended)
+ if( mFrinkArc )
+ {
+ tColour arcColour;
+ for( int j=0; j< NUM_FRINKARC_BBQS; j++ )
+ {
+ tBillboardQuad* bbq = mFrinkArc->GetQuad(j);
+ rAssert( bbq );
+
+ arcColour.Set(
+ rmt::Clamp( (int)(mOriginalFrinkArcColour[j].Red()*(1.0f - refraction)), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalFrinkArcColour[j].Green()*(1.0f - refraction)), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalFrinkArcColour[j].Blue()*(1.0f - refraction)), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalFrinkArcColour[j].Alpha()*(1.0f - refraction)), 0, 255 )
+ );
+ bbq->SetColour( arcColour );
+ }
+ mFrinkArc->GetShader()->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ }
+ }
+#endif
+
+
+
+
+
+ tColour brakecolour;
+
+ // if not turning on brakelights, we need to take into account the daytime
+ // running lights settings
+ float dayReduce = 2.5f; // by default, if braking is on, we magnify this value
+ if( !mBrakeLightsOn )
+ {
+ dayReduce = mBrakeLightScale;
+ }
+ float fadeFactor = mFadeAlpha / 255.0f;
+BEGIN_PROFILE("GeometryVehicle::Breaklights")
+ for( int i=0; i<NUM_BRAKELIGHT_BBQS; i++ )
+ {
+ if( mBrakeLights[i] != NULL )
+ {
+ brakecolour.Set(
+ rmt::Clamp( (int)(mOriginalBrakeLightColours[i].Red()*fadeFactor*dayReduce), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalBrakeLightColours[i].Green()*fadeFactor*dayReduce), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalBrakeLightColours[i].Blue()*fadeFactor*dayReduce), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalBrakeLightColours[i].Alpha()*fadeFactor*dayReduce), 0, 255 )
+ );
+ for( int j=0; j<mBrakeLights[i]->GetNumQuads(); j++ )
+ {
+ tBillboardQuad* bbq = mBrakeLights[i]->GetQuad( j );
+ rAssert( bbq != NULL );
+ bbq->SetColour( brakecolour );
+ bbq->SetVisibility( mEnableLights );
+ }
+
+ // ProcessShaders screwed us by causing us to lose the additive blend for
+ // brakelights... We set it back.
+ mBrakeLights[i]->GetShader()->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ }
+ }
+END_PROFILE("GeometryVehicle::Breaklights")
+
+BEGIN_PROFILE("GeometryVehicle::Headlights")
+ tColour headlightcolour;
+ int count = 0;
+ for( int i=0; i<VehicleCentral::NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ if( GetVehicleCentral()->mHeadLights[i] != NULL )
+ {
+ for( int j=0; j<GetVehicleCentral()->mHeadLights[i]->GetNumQuads(); j++ )
+ {
+ headlightcolour.Set(
+ rmt::Clamp( (int)(GetVehicleCentral()->mOriginalHeadLightColours[count].Red()*fadeFactor*mHeadLightScale), 0, 255 ),
+ rmt::Clamp( (int)(GetVehicleCentral()->mOriginalHeadLightColours[count].Green()*fadeFactor*mHeadLightScale), 0, 255 ),
+ rmt::Clamp( (int)(GetVehicleCentral()->mOriginalHeadLightColours[count].Blue()*fadeFactor*mHeadLightScale), 0, 255 ),
+ rmt::Clamp( (int)(GetVehicleCentral()->mOriginalHeadLightColours[count].Alpha()*fadeFactor*mHeadLightScale), 0, 255 )
+ );
+ tBillboardQuad* bbq = GetVehicleCentral()->mHeadLights[i]->GetQuad( j );
+ rAssert( bbq != NULL );
+ bbq->SetColour( headlightcolour );
+ bbq->SetVisibility( mEnableLights );
+ count++;
+ }
+
+ // ProcessShaders screwed us by causing us to lose the additive blend
+ // We set it back.
+ GetVehicleCentral()->mHeadLights[i]->GetShader()->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ }
+ }
+END_PROFILE("GeometryVehicle::Headlights")
+
+BEGIN_PROFILE("GeometryVehicle::Ghost")
+ // this means we're the ghost pirate ship... The glow must be ADDITIVE as well...
+ if( mHasGhostGlow )
+ {
+ count = 0;
+ tColour glowcolour;
+ for( int i=0; i<NUM_GHOSTGLOW_BBQGS; i++ )
+ {
+ if( mGhostGlows[i] != NULL )
+ {
+ for( int j=0; j<mGhostGlows[i]->GetNumQuads(); j++ )
+ {
+ glowcolour.Set(
+ rmt::Clamp( (int)(mOriginalGhostGlowColours[count].Red()*fadeFactor), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalGhostGlowColours[count].Green()*fadeFactor), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalGhostGlowColours[count].Blue()*fadeFactor), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalGhostGlowColours[count].Alpha()*fadeFactor), 0, 255 )
+ );
+ tBillboardQuad* bbq = mGhostGlows[i]->GetQuad( j );
+ rAssert( bbq != NULL );
+ bbq->SetColour( glowcolour );
+ count++;
+ }
+
+ // ProcessShaders screwed us by causing us to lose the additive blend
+ // We set it back.
+ mGhostGlows[i]->GetShader()->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ }
+ }
+ }
+END_PROFILE("GeometryVehicle::Ghost")
+
+ // this means we're the ghost pirate ship... The glow must be ADDITIVE as well...
+ if( mHasNukeGlow )
+ {
+ count = 0;
+ tColour glowcolour;
+ for( int i=0; i<NUM_NUKEGLOW_BBQGS; i++ )
+ {
+ if( mNukeGlows[i] != NULL )
+ {
+ for( int j=0; j<mNukeGlows[i]->GetNumQuads(); j++ )
+ {
+ glowcolour.Set(
+ rmt::Clamp( (int)(mOriginalNukeGlowColours[count].Red()*fadeFactor), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalNukeGlowColours[count].Green()*fadeFactor), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalNukeGlowColours[count].Blue()*fadeFactor), 0, 255 ),
+ rmt::Clamp( (int)(mOriginalNukeGlowColours[count].Alpha()*fadeFactor), 0, 255 )
+ );
+ tBillboardQuad* bbq = mNukeGlows[i]->GetQuad( j );
+ rAssert( bbq != NULL );
+ bbq->SetColour( glowcolour );
+ count++;
+ }
+
+ // ProcessShaders screwed us by causing us to lose the additive blend
+ // We set it back.
+ mNukeGlows[i]->GetShader()->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ }
+ }
+ }
+
+ // fade roof...
+ if( mRoofShader )
+ {
+ if( mRoofAlpha > mRoofTargetAlpha )
+ {
+ mRoofAlpha--;
+ }
+ else if( mRoofAlpha < mRoofTargetAlpha )
+ {
+ mRoofAlpha++;
+ }
+ //mRoofShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ mRoofShader->SetInt( PDDI_SP_EMISSIVEALPHA, mRoofAlpha );
+ if( mRoofOpacShape && mRoofAlphaShape )
+ {
+ if( mRoofAlpha == 255 )
+ {
+ mRoofOpacShape->SetVisibility( true );
+ mRoofAlphaShape->SetVisibility( false );
+ }
+ else
+ {
+ mRoofOpacShape->SetVisibility( false );
+ mRoofAlphaShape->SetVisibility( true );
+ }
+ }
+ }
+
+BEGIN_PROFILE("GeometryVehicle::CompDraw->Disp")
+ if( smokeFirst )
+ {
+ GetSparkleManager()->Render( Sparkle::SRM_SortedOnly );
+ }
+ if( sbDrawVehicle )
+ {
+ mCompositeDrawable->Display();
+ }
+ if( !smokeFirst )
+ {
+ GetSparkleManager()->Render( Sparkle::SRM_SortedOnly );
+ }
+ if( m_Collectible )
+ {
+ rmt::Matrix transform = mVehicleOwner->GetTransform();
+
+ p3d::stack->PushMultiply( transform );
+ p3d::stack->PushMultiply( m_CollectibleTransform );
+ m_Collectible->Display();
+ p3d::stack->Pop();
+ p3d::stack->Pop();
+ }
+
+END_PROFILE("GeometryVehicle::CompDraw->Disp")
+END_PROFILE("GeometryVehicle::Display Render")
+}
+
+
+//=============================================================================
+// GeometryVehicle::DisplaySkids
+//=============================================================================
+// Description: Comment
+//
+// Parameters: void GeometryVehicle::DisplaySkids(int wheel
+
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetSkidValues(int wheel, float intensity, rmt::Vector& groundPlaneNormal, eTerrainType terrainType )
+{
+ // if this got called, intensity is > 0.0
+
+ if(mSkidMarkGenerator)// && mDrawSkids) // test here though this should only be getting called for vehicles that have it... ie. VT_USER
+ {
+ /*
+ struct SkidData
+ {
+ // Direction that the vehicle is moving
+ rmt::Vector velocityDirection;
+ // Orientation and position of the vehicle
+ rmt::Matrix transform;
+ // Other data?????
+ float intensity; // 1 most intense, 0 nonexistent skid
+ };
+ */
+
+ rmt::Vector offset = mVehicleOwner->mSuspensionRestPoints[wheel];
+ offset.y -= mVehicleOwner->mWheels[wheel]->mRadius;
+ offset.y += mVehicleOwner->mWheels[wheel]->mYOffset; // hmmm....
+
+ //if(mVehicleOwner->mWheels[wheel]->mYOffset != 0.0f)
+ //{
+ // int stophere = 1;
+ //}
+
+ mSkidMarkGenerator->SetWheelOffset(wheel, offset); // need to do this everyframe
+
+ SkidMarkGenerator::SkidData sd;
+ sd.intensity = intensity;
+ sd.transform = mVehicleOwner->mTransform;
+ sd.terrainType = terrainType;
+
+ sd.groundPlaneNormal = groundPlaneNormal;
+
+
+ rmt::Vector velocityDir = mVehicleOwner->mVelocityCM;
+ velocityDir.NormalizeSafe();
+
+ // TODO
+ // Michael - would this blow up if I passed in 0,0,0 ?
+ sd.velocityDirection = velocityDir;
+
+ mSkidMarkGenerator->GenerateSkid(wheel, sd);
+
+ }
+
+}
+
+//=============================================================================
+// GeometryVehicle::DisplaySkids
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::UpdateSkids()
+{
+ if(mSkidMarkGenerator)
+ {
+ mSkidMarkGenerator->Update();
+ }
+}
+
+
+//////////////////////////////////////////////////////////////////////////
+int GeometryVehicle::CastsShadow()
+{
+ return 2;
+}
+//------------------------------------------------------------------------
+void GeometryVehicle::DisplayShadow( BlobShadowParams* BlobParams )
+{
+ BEGIN_PROFILE("GeometryVehicle::DisplayShadow")
+ rAssert(BlobParams);
+ tColour OutsideColour, insideColour;
+
+ // Hack for brightly colored shadows for special game mode
+ rmt::Vector camPos;
+ tCamera* camera = p3d::context->GetView()->GetCamera();
+ rAssert(camera);
+ camera->GetWorldPosition( &camPos );
+ camPos.Sub( BlobParams->GroundPos );
+ float yOffset = 0.0f; // Move the shadow up a bit with distance to avoid Z chew.
+ if( ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ rAssert( mVehicleOwner );
+ Avatar* avatar = ::GetAvatarManager()->GetAvatarForVehicle( mVehicleOwner );
+
+ OutsideColour.Set( 0, 0, 0, 0 ); // black outline
+ insideColour.Set( 0, 0, 0 ); // default black if no Avatar
+ if( avatar )
+ {
+ int id = avatar->GetPlayerId();
+
+ if ( this->mVehicleOwner->mNumTurbos > 0 )
+ {
+ insideColour = ::GetGameplayManager()->GetControllerColour( id );
+ }
+ else
+ {
+ insideColour = tColour( 0, 0, 0 );
+ }
+ }
+ }
+ else
+ {
+ // Camera culling.
+ float camDirDot = camPos.DotProduct( camera->GetCameraToWorldMatrix().Row( 2 ) );
+ // Note that the camPos vector is towards the camera so the camDirDot will be negative when the camera is facing
+ //the object. So if it's positive then we can assume it's behind the camera.
+ if( camDirDot > 0.0f )
+ {
+ END_PROFILE("GeometryVehicle::DisplayShadow")
+ return;
+ }
+
+ // Blobby shadow for vehicle.
+ OutsideColour.Set( 255, 255, 255, 255 );
+ const int Inside = 128;
+
+ float fadeFactor = 1.0f;
+ if( mVehicleOwner->mVehicleType == VT_TRAFFIC &&
+ mFadeAlpha != 255 )
+ {
+ fadeFactor = 1.0f - (mFadeAlpha/255.0f);
+ }
+ else
+ {
+ fadeFactor = 1.0f - BlobParams->ShadowAlpha;
+ }
+ // DistanceAlpha: over 30 is 1, under 10 is 0, linear between.
+ float distanceAlpha = rmt::Clamp((camPos.Magnitude() - 10.0f) * 0.05f, 0.0f, 1.0f);
+
+ fadeFactor *= 1.0f - distanceAlpha;
+
+ int c = rmt::Clamp( int( Inside + ( 255 - Inside ) * fadeFactor ), 0, 255 );
+ if( c == 255)
+ {
+ return;
+ }
+ insideColour.Set( c, c, c, c );
+ yOffset = MAX_Y_OFFSET * distanceAlpha;
+ }
+ const int NumPoints = 6;
+ const float BlobLength = 2.2f;
+ const float BlobWidth = 1.2f;
+ const float BlobCant = 0.2f;
+ const float BlobFade = 0.4f;
+ static float Points[ NumPoints ][ 2 ];
+ static float Fades[ NumPoints ][ 2 ];
+
+ static bool DoOnce = true;
+ if( DoOnce )
+ {
+ DoOnce = false;
+ /* Here are the points we'll use for the car:
+
+ ....----0
+ . 1
+ . |
+ . |
+ . + 2
+ . 3
+ . |
+ . 4
+ ....----5
+ We'll mirror the blob along the Z axis.
+ The shadow point adjusts work like this:
+ 0 - affects points 0 and 1, the top corner.
+ 1 - affects point 2, the position for the top division.
+ 2 - affects point 3, the position for the bottom division.
+ 3 - affects point 4 and 5, the bottom corner.
+ */
+ Points[ 0 ][ 0 ] = BlobWidth - BlobCant;
+ Points[ 0 ][ 1 ] = BlobLength;
+ Points[ 1 ][ 0 ] = BlobWidth;
+ Points[ 1 ][ 1 ] = BlobLength - BlobCant;
+ Points[ 2 ][ 0 ] = BlobWidth;
+ Points[ 2 ][ 1 ] = BlobCant;
+ Points[ 3 ][ 0 ] = BlobWidth;
+ Points[ 3 ][ 1 ] = -BlobCant;
+ Points[ 4 ][ 0 ] = BlobWidth;
+ Points[ 4 ][ 1 ] = -BlobLength + BlobCant;
+ Points[ 5 ][ 0 ] = BlobWidth - BlobCant;
+ Points[ 5 ][ 1 ] = -BlobLength;
+ Fades[ 0 ][ 0 ] = 0.5f * BlobFade; // Sin( 30 )
+ Fades[ 0 ][ 1 ] = 0.866f * BlobFade; // Cos( 30 )
+ Fades[ 1 ][ 0 ] = 0.866f * BlobFade; // Sin( 60 )
+ Fades[ 1 ][ 1 ] = 0.5f * BlobFade; // Cos( 60 )
+ Fades[ 2 ][ 0 ] = 0.866f * BlobFade; // Sin( 60 )
+ Fades[ 2 ][ 1 ] = 0.5f * BlobFade; // Cos( 60 )
+ Fades[ 3 ][ 0 ] = 0.866f * BlobFade; // Sin( 60 )
+ Fades[ 3 ][ 1 ] = 0.5f * BlobFade; // Cos( 60 )
+ Fades[ 4 ][ 0 ] = 0.866f * BlobFade; // Sin( 120 )
+ Fades[ 4 ][ 1 ] = -0.5f * BlobFade; // Cos( 120 )
+ Fades[ 5 ][ 0 ] = 0.5f * BlobFade; // Sin( 150 )
+ Fades[ 5 ][ 1 ] = -0.866f * BlobFade; // Cos( 150 )
+ }
+
+ camPos.Normalize();
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( BlobParams->GroundPos );
+ transform.FillHeading( BlobParams->GroundNormal, BlobParams->ShadowFacing );
+ transform.Row( 3 ).Add( camPos );
+ transform.Row(3).y += yOffset;
+ p3d::stack->PushMultiply( transform );
+
+ pddiShader* blobShader = BootupContext::GetInstance()->GetSharedShader();
+
+ if( ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ blobShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_NONE );
+ }
+ else
+ {
+ blobShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_MODULATE );
+ }
+ blobShader->SetInt( PDDI_SP_ISLIT, 0 );
+ blobShader->SetInt( PDDI_SP_ALPHATEST, 0 );
+ blobShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_GOURAUD );
+ pddiPrimStream* blob = p3d::pddi->BeginPrims( blobShader, PDDI_PRIM_TRIANGLES, PDDI_V_C, 6 * NumPoints );
+ for( int i = 0; i < NumPoints * 2; ++i )
+ {
+ int index = i;
+ int nextIndex = ( i + 1 ) % ( NumPoints * 2 );
+ int mirrorX = int( ( index * ( 1.0f / NumPoints ) ) );
+ int nextMirrorX = int( ( nextIndex * ( 1.0f / NumPoints ) ) );
+ index %= NumPoints;
+ nextIndex %= NumPoints;
+ index = rmt::Abs( ( mirrorX * 5 ) - index );
+ nextIndex = rmt::Abs( ( nextMirrorX * 5 ) - nextIndex );
+ mirrorX = 1 - ( mirrorX * 2 );
+ nextMirrorX = 1 - ( nextMirrorX * 2 );
+ int adjust = rmt::Clamp( index - 1, 0, NumPoints - 3 );
+ int nextAdjust = rmt::Clamp( nextIndex - 1, 0, NumPoints - 3 );
+
+ float x, y;
+ blob->Colour( insideColour );
+ blob->Coord( 0.0f, 0.0f, 0.0f );
+ x = ( Points[ nextIndex ][ 0 ] + mShadowPointAdjustments[ nextAdjust ][ 0 ] ) * BlobParams->ShadowScale;
+ y = ( Points[ nextIndex ][ 1 ] + mShadowPointAdjustments[ nextAdjust ][ 1 ] ) * BlobParams->ShadowScale;
+ blob->Colour( insideColour );
+ blob->Coord( x * nextMirrorX, y, 0.0f );
+ x = ( Points[ index ][ 0 ] + mShadowPointAdjustments[ adjust ][ 0 ] ) * BlobParams->ShadowScale;
+ y = ( Points[ index ][ 1 ] + mShadowPointAdjustments[ adjust ][ 1 ] ) * BlobParams->ShadowScale;
+ blob->Colour( insideColour );
+ blob->Coord( x * mirrorX, y, 0.0f );
+ }
+ p3d::pddi->EndPrims( blob );
+ // How the soft edge.
+ blob = p3d::pddi->BeginPrims( blobShader, PDDI_PRIM_TRISTRIP, PDDI_V_C, ( ( 2 * NumPoints ) + 1 ) * 2 );
+ for( int i = 0; i <= NumPoints * 2; ++i )
+ {
+ int index = i;
+ int mirrorX = int( ( index * ( 1.0f / NumPoints ) ) ) % 2;
+ index %= NumPoints;
+ index = rmt::Abs( ( mirrorX * 5 ) - index );
+ mirrorX = 1 - ( mirrorX * 2 );
+ int adjust = rmt::Clamp( index - 1, 0, NumPoints - 3 );
+
+ float inX, inY;
+ float outX, outY;
+ inX = ( Points[ index ][ 0 ] + mShadowPointAdjustments[ adjust ][ 0 ] ) * BlobParams->ShadowScale;
+ inY = ( Points[ index ][ 1 ] + mShadowPointAdjustments[ adjust ][ 1 ] ) * BlobParams->ShadowScale;
+ inX *= mirrorX;
+ outX = inX + ( Fades[ index ][ 0 ] * mirrorX * BlobParams->ShadowScale );
+ outY = inY + ( Fades[ index ][ 1 ] * BlobParams->ShadowScale );
+ blob->Colour( OutsideColour );
+ blob->Coord( outX, outY, 0.0f );
+ blob->Colour( insideColour );
+ blob->Coord( inX, inY, 0.0f );
+ }
+ p3d::pddi->EndPrims( blob );
+ p3d::stack->Pop();
+ END_PROFILE("GeometryVehicle::DisplayShadow")
+}
+
+//=============================================================================
+// GeometryVehicle::DisplayLights
+//=============================================================================
+// Description: Comment
+//
+// Parameters: rmt::Vector& GroundPos - Position of car.
+// rmt::Vector& GroundNormal - Ground normal at car position.
+// rmt::Vector& GroundUp - Up direction at ground position
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::DisplayLights( const HeadLightParams& LightParams )
+{
+ BEGIN_PROFILE("GeometryVehicle::DisplayLights")
+ const int NumPoints = 20;
+ const float LightXSize = 1.0f;
+ const float LightZSize = 0.125f;
+ const float FadeSize = 0.3f;
+ const float LightLength = NumPoints * LightZSize;
+ static bool DoOnce = true;
+ static float PointsX[ NumPoints ];
+ static tColour Colours[ NumPoints ];
+ static float FadePoints[ NumPoints ][ 2 ];
+ const tColour Colour( 65, 70, 46 );
+ const tColour Black( 0, 0, 0 );
+
+ if( DoOnce )
+ {
+ DoOnce = false;
+ // The light cone is made of of two tri strips one is the inner bright parabula
+ // which fades out with distance, and the other is the soft edge around it.
+ for( int i = 0; i < NumPoints; ++i )
+ {
+ float z = ( i + 1 ) * LightZSize;
+ float x = rmt::Sqrt( ( i + 1 ) * 0.5f );
+ PointsX[ i ] = x;
+ z -= LightLength;
+ float mag = rmt::Sqrt( ( x * x ) + ( z * z ) );
+ mag = ( ( mag != 0.0f ) ? 1.0f / mag : 0.0f ) * FadeSize;
+ x *= mag;
+ z *= mag;
+ FadePoints[ i ][ 0 ] = x;
+ FadePoints[ i ][ 1 ] = z;
+ float colourScale = ( 1.0f - ( (float)i / (float)NumPoints ) );
+ int red = rmt::Clamp( int( Colour.Red() * colourScale ), 0, 255 );
+ int green = rmt::Clamp( int( Colour.Green() * colourScale ), 0, 255 );
+ int blue = rmt::Clamp( int( Colour.Blue() * colourScale ), 0, 255 );
+ Colours[ i ].Set( red, green, blue );
+ }
+ // Now calculate the offset for the fade points.
+ }
+
+ float xScale = 1.0f;
+ float zScale = 1.0f;
+ float scale = 1.0f;
+
+ scale = LightParams.LightReach;
+ float offset = ( 1.0f - LightParams.LightReach ) * LightLength;
+
+ rmt::Vector camPos;
+ rmt::Vector groundPos = LightParams.GroundPos;
+ groundPos.ScaleAdd( offset, LightParams.VehicleHeading );
+ tCamera* camera = p3d::context->GetView()->GetCamera();
+ rAssert( camera );
+ camera->GetWorldPosition( &camPos );
+ camPos.Sub( groundPos );
+ float cameraToLight = camPos.Magnitude();
+ camPos.Normalize();
+ rmt::Matrix toCamera;
+ toCamera.Identity();
+ toCamera.FillTranslate( camPos );
+ rmt::Matrix transform;
+ transform.Identity();
+ transform.FillTranslate( groundPos );
+ transform.FillHeading( LightParams.GroundNormal, LightParams.VehicleHeading );
+ transform.Mult( toCamera );
+ p3d::stack->PushMultiply( transform );
+
+ float dist = LightParams.VehicleHeading.Dot( LightParams.GroundNormal );
+ rmt::Vector groundByVehicle( LightParams.GroundNormal );
+ if( dist != 0.0f )
+ {
+ groundByVehicle.ScaleAdd( -dist, LightParams.VehicleHeading );
+ groundByVehicle.Normalize();
+ }
+ float vehicleRoll = groundByVehicle.Dot( LightParams.VehicleUp );
+ vehicleRoll *= vehicleRoll;
+#ifdef RAD_DEBUG
+ if( this->mVehicleOwner->mVehicleType != VT_TRAFFIC )
+ {
+ char text[128];
+ sprintf( text, "Ground to vehicle ratio: %.4f", vehicleRoll );
+ DebugInfo::GetInstance()->Push( "Vehicle lights" );
+ DebugInfo::GetInstance()->AddScreenText( text );
+ DebugInfo::GetInstance()->Pop();
+ }
+#endif
+
+ xScale *= scale * LightXSize * vehicleRoll;
+ zScale *= scale * LightZSize;
+
+ pddiShader* lightShader = BootupContext::GetInstance()->GetSharedShader();
+ lightShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+ lightShader->SetInt( PDDI_SP_ISLIT, 0 );
+ lightShader->SetInt( PDDI_SP_ALPHATEST, 0 );
+ lightShader->SetInt( PDDI_SP_SHADEMODE, PDDI_SHADE_GOURAUD );
+
+ // Center light.
+ pddiPrimStream* light = p3d::pddi->BeginPrims( lightShader, PDDI_PRIM_TRISTRIP, PDDI_V_C, ( NumPoints << 1 ) + 1 );
+ light->Colour( Colour );
+ light->Coord( 0.0f, 0.0f, 0.0f );
+ for( int i = 0; i < NumPoints; ++i )
+ {
+ float inX = PointsX[ i ] * xScale;
+ float inZ = ( i + 1 ) * zScale;
+ light->Colour( Colours[ i ] );
+ light->Coord( inX, inZ, 0.0f );
+ light->Colour( Colours[ i ] );
+ light->Coord( -inX, inZ, 0.0f );
+ }
+ p3d::pddi->EndPrims( light );
+ // Fade on the negative side.
+ pddiPrimStream* nFade = p3d::pddi->BeginPrims( lightShader, PDDI_PRIM_TRISTRIP, PDDI_V_C, ( NumPoints + 1 ) << 1 );
+ nFade->Colour( Black );
+ nFade->Coord( 0.0f, -FadeSize, 0.0f );
+ nFade->Colour( Colour);
+ nFade->Coord( 0.0f, 0.0f, 0.0f );
+ for( int i = 0; i < NumPoints; ++i )
+ {
+ float inX = -PointsX[ i ] * xScale;
+ float inZ = ( i + 1 ) * zScale;
+ float outX = inX - FadePoints[ i ][ 0 ];
+ float outZ = inZ + FadePoints[ i ][ 1 ];
+ nFade->Colour( Black );
+ nFade->Coord( outX, outZ, 0.0f );
+ nFade->Colour( Colours[ i ] );
+ nFade->Coord( inX, inZ, 0.0f );
+ }
+ p3d::pddi->EndPrims( nFade );
+
+ // Fade on the positive side.
+ pddiPrimStream* pFade = p3d::pddi->BeginPrims( lightShader, PDDI_PRIM_TRISTRIP, PDDI_V_C, ( NumPoints + 1 ) << 1 );
+ pFade->Colour( Colour);
+ pFade->Coord( 0.0f, 0.0f, 0.0f );
+ pFade->Colour( Black );
+ pFade->Coord( 0.0f, -FadeSize, 0.0f );
+ for( int i = 0; i < NumPoints; ++i )
+ {
+ float inX = PointsX[ i ] * xScale;
+ float inZ = ( i + 1 ) * zScale;
+ float outX = inX + FadePoints[ i ][ 0 ];
+ float outZ = inZ + FadePoints[ i ][ 1 ];
+ pFade->Colour( Colours[ i ] );
+ pFade->Coord( inX, inZ, 0.0f );
+ pFade->Colour( Black );
+ pFade->Coord( outX, outZ, 0.0f );
+ }
+ p3d::pddi->EndPrims( pFade );
+
+ p3d::stack->Pop();
+ END_PROFILE("GeometryVehicle::DisplayLights")
+}
+
+//=============================================================================
+// GeometryVehicle::FindAndTurnOffWheels
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindAndTurnOffWheels()
+{
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ tPose* p3dPose = mCompositeDrawable->GetPose();
+
+ char buffy[128];
+ sprintf(buffy, "w%d", i);
+ int jointIndex = p3dPose->FindJointIndex(buffy);
+
+ int j;
+ for(j = 0; j < mCompositeDrawable->GetNumDrawableElement(); j++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+ if(prop && prop->GetPoseIndex() == jointIndex)
+ {
+ prop->SetVisibility(false);
+ }
+ }
+
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindAndTurnOffFrontWheelsOnly
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindAndTurnOffFrontWheelsOnly()
+{
+ int i;
+ for(i = 2; i < 4; i++)
+ {
+ tPose* p3dPose = mCompositeDrawable->GetPose();
+
+ char buffy[128];
+ sprintf(buffy, "w%d", i);
+ int jointIndex = p3dPose->FindJointIndex(buffy);
+
+ int j;
+ for(j = 0; j < mCompositeDrawable->GetNumDrawableElement(); j++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+ if(prop && prop->GetPoseIndex() == jointIndex)
+ {
+ prop->SetVisibility(false);
+ }
+ }
+
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindHeadLightBillboardJoints
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindHeadLightBillboardJoints()
+{
+ // grab the pose
+ tPose* p3dPose = mCompositeDrawable->GetPose();
+
+ // grab the headlight bbqgs from common section
+
+ // put in left headlight
+ char buffy[128];
+ sprintf( buffy, "hll" );
+ int left = p3dPose->FindJointIndex( buffy );
+ if( left >= 0 )
+ {
+ for( int i=0; i<VehicleCentral::NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ rAssert( GetVehicleCentral()->mHeadLights[i] );
+ mCompositeDrawable->AddProp( GetVehicleCentral()->mHeadLights[i], left );
+ }
+ }
+
+ // now the right side
+ sprintf( buffy, "hlr" );
+ int right = p3dPose->FindJointIndex( buffy );
+ if( right >= 0 )
+ {
+ for( int i=0; i<VehicleCentral::NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ rAssert( GetVehicleCentral()->mHeadLights[i] );
+ mCompositeDrawable->AddProp( GetVehicleCentral()->mHeadLights[i], right );
+ }
+ }
+
+}
+//=============================================================================
+// GeometryVehicle::FindBrakeLightBillboardJoints
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindBrakeLightBillboardJoints()
+{
+ int i;
+ for(i = 1; i < 5; i++) // 'cause Kevin starts counting at 1, not 0
+ {
+ tPose* p3dPose = mCompositeDrawable->GetPose();
+
+ char buffy[128];
+ sprintf(buffy, "brake%d", i);
+ int jointIndex = p3dPose->FindJointIndex(buffy);
+
+ int j;
+ for(j = 0; j < mCompositeDrawable->GetNumDrawableElement(); j++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+ if(prop && prop->GetPoseIndex() == jointIndex)
+ {
+ //mBrakeLights[i - 1] = (tBillboardQuadGroup*) prop->GetDrawable();
+ tBillboardQuadGroup* bbqg = dynamic_cast<tBillboardQuadGroup*>( prop->GetDrawable() );
+ if( bbqg != NULL )
+ {
+ tRefCounted::Assign( mBrakeLights[i - 1], bbqg );
+
+ // the structure for storing the original colors only supports 1 quad per quadgroup
+ rAssert( mBrakeLights[i-1]->GetNumQuads() == 1 );
+ tBillboardQuad* quad = mBrakeLights[i-1]->GetQuad( 0 );
+ mOriginalBrakeLightColours[i-1] = quad->GetColour();
+ mBrakeLightJoints[i - 1] = jointIndex;
+ }
+ }
+ }
+
+ }
+
+ for(i = 1; i < 5; i++) // 'cause Kevin starts counting at 1, not 0
+ {
+ tPose* p3dPose = mCompositeDrawable->GetPose();
+
+ char buffy[128];
+ sprintf(buffy, "rev%d", i);
+ int jointIndex = p3dPose->FindJointIndex(buffy);
+
+ int j;
+ for(j = 0; j < mCompositeDrawable->GetNumDrawableElement(); j++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+ if(prop && prop->GetPoseIndex() == jointIndex)
+ {
+ prop->SetVisibility(false);
+ mReverseLightJoints[i - 1] = jointIndex;
+ }
+ }
+
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindGhostGlowBillboards
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* model )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindGhostGlowBillboards( const char* model )
+{
+ int count = 0;
+ for( int i = 0; i<NUM_GHOSTGLOW_BBQGS; i++ )
+ {
+ char name[64];
+ sprintf( name, "%sGlow%dShape", model, i+1 );
+ tUID test = tEntity::MakeUID( name );
+
+ for( int j=0; j<mCompositeDrawable->GetNumDrawableElement(); j++ )
+ {
+ tCompositeDrawable::DrawablePropElement* prop =
+ (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+
+ if( prop && prop->GetUID() == test )
+ {
+ tBillboardQuadGroup* bbqg = dynamic_cast<tBillboardQuadGroup*>( prop->GetDrawable() );
+ if( bbqg != NULL )
+ {
+ tRefCounted::Assign( mGhostGlows[i], bbqg );
+
+ for( int j=0; j<mGhostGlows[i]->GetNumQuads(); j++ )
+ {
+ tBillboardQuad* quad = mGhostGlows[i]->GetQuad( j );
+ rAssert( quad );
+ mOriginalGhostGlowColours[count] = quad->GetColour();
+ count++;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindNukeGlowBillboards
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* model )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindNukeGlowBillboards( const char* model )
+{
+ int count = 0;
+ for( int i = 0; i<NUM_NUKEGLOW_BBQGS; i++ )
+ {
+ char name[64];
+ sprintf( name, "nucGlowGroupShape" );
+ tUID test = tEntity::MakeUID( name );
+
+ for( int j=0; j<mCompositeDrawable->GetNumDrawableElement(); j++ )
+ {
+ tCompositeDrawable::DrawablePropElement* prop =
+ (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+
+ if( prop && prop->GetUID() == test )
+ {
+ tBillboardQuadGroup* bbqg = dynamic_cast<tBillboardQuadGroup*>( prop->GetDrawable() );
+ if( bbqg != NULL )
+ {
+ tRefCounted::Assign( mNukeGlows[i], bbqg );
+
+ for( int j=0; j<mNukeGlows[i]->GetNumQuads(); j++ )
+ {
+ tBillboardQuad* quad = mNukeGlows[i]->GetQuad( j );
+ rAssert( quad );
+ mOriginalNukeGlowColours[count] = quad->GetColour();
+ count++;
+ }
+ }
+ }
+ }
+ }
+}
+
+
+
+//=============================================================================
+// GeometryVehicle::SetCollectibleHardpointPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const rmt::Vector& position )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetCollectibleHardpointPosition( const rmt::Vector& position )
+{
+ // Use the default orientation, but replace the translational compoonent
+ m_CollectibleTransform = DEFAULT_COLLECTIBLE_TRANSFORM;
+ m_CollectibleTransform.FillTranslate( position );
+
+}
+
+
+//=============================================================================
+// GeometryVehicle::SetCollectibleHardpointTransform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float rotx ,float roty ,float rotz, const rmt::Vector& position )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetCollectibleHardpointTransform( float rotx ,float roty ,float rotz,
+ const rmt::Vector& position )
+{
+ m_CollectibleTransform.Identity();
+ m_CollectibleTransform.FillRotateXYZ( rotx, roty, rotz );
+ m_CollectibleTransform.FillTranslate( position );
+}
+
+
+//=============================================================================
+// GeometryVehicle::ShowBrakeLights
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::ShowBrakeLights()
+{
+ mBrakeLightsOn = true;
+ /*
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ if(mBrakeLightJoints[i] != -1)
+ {
+ this->HideFlappingPiece(mBrakeLightJoints[i], false);
+ }
+ }
+ */
+}
+
+//=============================================================================
+// GeometryVehicle::HideBrakeLights
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::HideBrakeLights()
+{
+ mBrakeLightsOn = false;
+ /*
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ if(mBrakeLightJoints[i] != -1)
+ {
+ this->HideFlappingPiece(mBrakeLightJoints[i], true);
+ }
+ }
+ */
+}
+
+
+//=============================================================================
+// GeometryVehicle::ShowReverseLights
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::ShowReverseLights()
+{
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ if(mReverseLightJoints[i] != -1)
+ {
+ this->HideFlappingPiece(mReverseLightJoints[i], false);
+ }
+ }
+
+}
+
+
+//=============================================================================
+// GeometryVehicle::HideReverseLights
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::HideReverseLights()
+{
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ if(mReverseLightJoints[i] != -1)
+ {
+ this->HideFlappingPiece(mReverseLightJoints[i], true);
+ }
+ }
+
+}
+
+
+//=============================================================================
+// GeometryVehicle::GetArt
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (char* name)
+//
+// Return: bool
+//
+//=============================================================================
+bool GeometryVehicle::GetArt( const char* name)
+{
+ // expect a composite drawable named "name" in the file.
+
+ tCompositeDrawable* master = p3d::find<tCompositeDrawable>(name);
+ rTuneAssert( master != NULL );
+
+#ifndef FINAL
+ if (master == NULL)
+ {
+ char buffer [255];
+ sprintf(buffer,"Script ERROR: Can't Find Vehicle:%s Make sure you loaded it!\n",name);
+ rTuneAssertMsg(0,buffer);
+ }
+#endif
+
+ mCompositeDrawable = master->Clone();
+
+ //mCompositeDrawable = p3d::find<tCompositeDrawable>(name);
+ rAssert(mCompositeDrawable);
+ mCompositeDrawable->AddRef();
+
+ {
+ char controllerName[255];
+ strcpy( controllerName, "EFX_" );
+ strcat( controllerName, name );
+ strcat( controllerName, "velocpartSystShape" );
+
+ tEffectController* particleSysController = p3d::find < tEffectController >( controllerName );
+ if ( particleSysController != NULL )
+ {
+ tEffect* effect = particleSysController->GetEffect();
+ rAssert( dynamic_cast< tParticleSystem* >( effect ) != NULL );
+ tParticleSystem* particleSystem = static_cast< tParticleSystem* > ( effect );
+ mVariableEmissionParticleSystem = particleSystem;
+ mVariableEmissionParticleSystem->AddRef();
+ }
+ }
+
+ /////////////////////////////////////////////////////////////////////////////////////
+ // Traffic "swatches" (in a sense)
+
+ // Any car using traffic model (regardless of whether they are in fact traffic cars)
+ // Must contain a trafficbodydrawable so that it will render correctly (due to fading
+ // and swatch tricks done for traffic models).
+
+ if( IsTrafficModel() )
+ {
+ mUsingTrafficModel = true;
+
+ // Grab the shader to be used for swatches
+ char shadername[64];
+ sprintf( shadername, "%s_m", name );
+ tShader* bodyShade = p3d::find<tShader>( shadername );
+
+ // Find the body prop in compositedrawable's list of elements by name
+ // e.g. compactAShape
+ char propname[64];
+ sprintf( propname, "%sShape", name );
+ tUID uid = tEntity::MakeUID(propname);
+
+ int poseIndex = -1;
+ tCompositeDrawable::DrawablePropElement* body = NULL;
+
+ for( int i=0; i<mCompositeDrawable->GetNumDrawableElement(); i++ )
+ {
+ body = (tCompositeDrawable::DrawablePropElement*) (mCompositeDrawable->GetDrawableElement(i));
+ if( body != NULL )
+ {
+ if( body->GetDrawable()->GetUID() == uid )
+ {
+ poseIndex = i;
+ break;
+ }
+ }
+ }
+
+ if( poseIndex > -1 && body != NULL )
+ {
+ // create a trafficbodydrawable
+ tRefCounted::Assign( mTrafficBodyDrawable, new TrafficBodyDrawable() );
+ rAssert( mTrafficBodyDrawable );
+
+ // store away the body prop's old drawable
+ rAssert( body->GetDrawable() != NULL );
+ mTrafficBodyDrawable->SetBodyPropDrawable( body->GetDrawable() );
+
+ // Grab the shader for the vehicle chassis and store it too
+ if( bodyShade == NULL )
+ {
+ rDebugPrintf( "Warning: Couldn't find shader \"%s\" for traffic vehicle %s. Chassis color randomization will not apply to this vehicle.\n",
+ shadername, name );
+ }
+ mTrafficBodyDrawable->SetBodyShader( bodyShade );
+
+ // make the new traffic body drawable the drawable for the body prop
+ body->SetDrawable( mTrafficBodyDrawable );
+ }
+ else
+ {
+ rDebugPrintf( "Warning: Couldn't find tCompositeDrawable prop \"%s\" for traffic vehicle %s. Chassis color randomization will not apply to this vehicle.\n",
+ propname, name );
+ }
+
+ // Find the door prop in compositedrawable's list of elements by name
+ // e.g. compactAShape
+ sprintf( propname, "doorPRotShape" );
+ uid = tEntity::MakeUID(propname);
+
+ poseIndex = -1;
+ body = NULL;
+ for( int i=0; i<mCompositeDrawable->GetNumDrawableElement(); i++ )
+ {
+ body = (tCompositeDrawable::DrawablePropElement*)
+ (mCompositeDrawable->GetDrawableElement(i));
+ if( body != NULL )
+ {
+ if( body->GetDrawable()->GetUID() == uid )
+ {
+ poseIndex = i;
+ break;
+ }
+ }
+ }
+
+ if( poseIndex > -1 && body != NULL )
+ {
+ // create a trafficbodydrawable
+ tRefCounted::Assign( mTrafficDoorDrawable, new TrafficBodyDrawable() );
+ rAssert( mTrafficDoorDrawable );
+
+ // store away the body prop's old drawable
+ rAssert( body->GetDrawable() != NULL );
+ mTrafficDoorDrawable->SetBodyPropDrawable( body->GetDrawable() );
+
+ // Grab the shader for the vehicle chassis and store it too
+ if( bodyShade == NULL )
+ {
+ rDebugPrintf( "Warning: Couldn't find shader \"%s\" for traffic vehicle %s. Chassis color randomization will not apply to this vehicle.\n",
+ shadername, name );
+ }
+ mTrafficDoorDrawable->SetBodyShader( bodyShade );
+
+ // make the new traffic body drawable the drawable for the body prop
+ body->SetDrawable( mTrafficDoorDrawable );
+ }
+ else
+ {
+ rDebugPrintf( "Warning: Couldn't find tCompositeDrawable prop \"%s\" for traffic vehicle %s. Chassis color randomization will not apply to this vehicle.\n",
+ propname, name );
+ }
+
+ }
+ // is this ok for release?
+ // I think so
+
+ // little hack just for frink
+ // no wheels
+ if( strcmp(name, "frink_v") == 0 || strcmp(name, "honor_v") == 0 || strcmp(name, "hbike_v") == 0 ||
+ strcmp(name, "witchcar") == 0 || strcmp(name, "ship") == 0 || strcmp(name, "mono_v") == 0 )
+ {
+ FindAndTurnOffWheels();
+ }
+
+ if( strcmp( name, "frink_v" ) == 0 )
+ {
+ int count = 0;
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "special_m" ) ); //must be first
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vInt_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vTrim_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vDoorPNorm_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vExtras_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vHoodNorm_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vVent_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vcoil_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "BottomA_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vDoorDNorm_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "engine_m" ) );
+ tRefCounted::Assign( mRefractionShader[ count++ ], p3d::find< tShader >( "frink_vBackNorm_m" ) );
+
+ int size = count;
+ int i;
+ for( i = 0; i < size; ++i )
+ {
+ rAssert( mRefractionShader[ i ] != NULL );
+ }
+
+#ifdef RAD_WIN32
+ tPose* p3dPose = mCompositeDrawable->GetPose();
+
+ char buffy[128];
+ sprintf(buffy, "frinkArcGroup", i);
+ int jointIndex = p3dPose->FindJointIndex(buffy);
+
+ for(int j = 0; j < mCompositeDrawable->GetNumDrawableElement(); j++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(j));
+ if(prop && prop->GetPoseIndex() == jointIndex)
+ {
+ tBillboardQuadGroup* bbqg = dynamic_cast<tBillboardQuadGroup*>( prop->GetDrawable() );
+ if( bbqg )
+ {
+ tRefCounted::Assign( mFrinkArc, bbqg );
+ for( int k = 0; k < NUM_FRINKARC_BBQS; k++ )
+ {
+ tBillboardQuad* quad = mFrinkArc->GetQuad( k );
+ mOriginalFrinkArcColour[k] = quad->GetColour();
+ }
+ }
+ }
+ }
+#endif
+ }
+
+ if(strcmp(name, "rocke_v") == 0)
+ {
+ this->FindAndTurnOffFrontWheelsOnly();
+ }
+
+ // store brake light joint indices
+ FindBrakeLightBillboardJoints();
+ FindHeadLightBillboardJoints();
+
+ if( GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ mHeadLightScale = 1.0f;
+ mBrakeLightScale = 0.4f;
+ if( mVehicleOwner->mVehicleType == VT_USER || mVehicleOwner->mVehicleType == VT_AI )
+ {
+ EnableLights( true );
+ }
+ }
+ else
+ {
+ // if we are on a level other than 4 or 7, we want to turn the headlights off
+ if( GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L1 || // BROAD DAYLIGHT
+ GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L2 )
+ {
+ // Which values should we use for headlights and brakelights
+ // when we just have running lights (when not braking)
+ mHeadLightScale = 0.0f;
+ mBrakeLightScale = 0.0f;//0.4f;
+ }
+ else if( GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L3 ) // SUNSET
+ {
+ mHeadLightScale = 0.2f;
+ mBrakeLightScale = 0.3f;
+ }
+ else if( GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L5 ) // DUSK
+ {
+ mHeadLightScale = 0.4f;
+ mBrakeLightScale = 0.4f;
+ }
+ else if( GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L6 ) // TWILIGHT
+ {
+ mHeadLightScale = 0.4f;
+ mBrakeLightScale = 0.4f;
+ }
+ else if( GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L4 || // NIGHT
+ GetGameplayManager()->GetCurrentLevelIndex() == RenderEnums::L7 )
+ {
+ // full headlights on night levels
+ mHeadLightScale = 0.6f;
+ mBrakeLightScale = 0.4f;
+ }
+ if( mVehicleOwner->mVehicleType == VT_USER )
+ {
+ EnableLights( false );
+ }
+ }
+
+
+
+ // hack for ghost ship
+ if( strcmp(name, "ship") == 0 )
+ {
+ mHasGhostGlow = true;
+ FindGhostGlowBillboards( name );
+ }
+
+ if( strcmp( name, "nuctruck" ) == 0 )
+ {
+ mHasNukeGlow = true;
+ FindNukeGlowBillboards( name );
+ }
+
+
+ // TODO - might need to clone this too
+// mAnimController = p3d::find<tPoseAnimationController>(buffy);
+
+ // We will advance the multicontroller, not just the posecontroller
+
+ FindAnimationControllers( name );
+
+ char buffy[128];
+ sprintf(buffy, "PTRN_%s", name);
+
+ tPoseAnimationController* poseController = p3d::find<tPoseAnimationController>(buffy);
+
+ // only play an anim if it's there:
+
+ if( poseController )
+ {
+ poseController->SetPose(mCompositeDrawable->GetPose());
+
+ // TODO
+ // cast here might be unsafe in the general case
+ //mAnim = (tPoseAnimation*)(poseController->GetAnimation());
+ mAnim = poseController->GetAnimation();
+ mAnim->AddRef(); // ? already addref'd by controller?
+ mAnim->SetCyclic(true);
+
+ float numFrames = mAnim->GetNumFrames();
+ float speed = mAnim->GetSpeed();
+
+ rAssert( numFrames != 0 );
+
+ mAnimRevPerSecondBase = speed / (float)numFrames;
+ }
+
+ char roofShaderName[64];
+ sprintf( roofShaderName, "%sRoof_m", name );
+ tRefCounted::Assign( mRoofShader, p3d::find<tShader>( tEntity::MakeUID( roofShaderName ) ) );
+
+ FindRoofGeometry( name );
+ if( mRoofOpacShape && mRoofAlphaShape )
+ {
+ mRoofOpacShape->SetVisibility( true );
+ mRoofAlphaShape->SetVisibility( false );
+ }
+
+ if ( mVehicleOwner->GetNameObject() == "gramR_v" )
+ {
+ SetCollectibleHardpointPosition( JEEP_COLLECTIBLE_POS );
+ }
+ else if ( mVehicleOwner->GetNameObject() == "homer_v" )
+ {
+ SetCollectibleHardpointPosition( BARRACUDA_COLLECTIBLE_POS );
+ }
+ else if ( mVehicleOwner->GetNameObject() == "dune_v" )
+ {
+ SetCollectibleHardpointTransform( DUNE_COLLECTIBLE_ROT.x,
+ DUNE_COLLECTIBLE_ROT.y,
+ DUNE_COLLECTIBLE_ROT.z,
+ DUNE_COLLECTIBLE_POS);
+ }
+ else if ( mVehicleOwner->GetNameObject() == "hallo" )
+ {
+ SetCollectibleHardpointPosition( HALLO_COLLECTIBLE_POS );
+ }
+ else if ( mVehicleOwner->GetNameObject() == "coffin" )
+ {
+ SetCollectibleHardpointPosition( COFFIN_COLLECTIBLE_POS );
+ }
+ else if ( mVehicleOwner->GetNameObject() == "witchcar" )
+ {
+ SetCollectibleHardpointPosition( WITCH_COLLECTIBLE_POS );
+ }
+
+ if(mVehicleOwner->mVehicleType == VT_USER)
+ {
+ tCompositeDrawable::DrawableElement* agnes = mCompositeDrawable->FindNode("agnusGeoShape");
+ if(agnes)
+ {
+ if(GetVehicleCentral()->IsDriverSuppressed("skinner"))
+ {
+ agnes->SetVisibility(false);
+ }
+ }
+ }
+
+ //THERE'S AN UNTRACKED ALLOCATION HERE!
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+
+
+ return FindDamageShadersAndTextures(name);
+ //return true;
+
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindDamageShadersAndTextures
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (char* name)
+//
+// Return: void
+//
+//=============================================================================
+bool GeometryVehicle::FindDamageShadersAndTextures( const char* carname)
+{
+ // names we expect to have:
+ //
+ // for shaders
+ // <carname>DoorDNorm_m
+ // <carname>DoorPNorm_m
+ // <carname>HoodNorm_m
+ // <carname>BackNorm_m
+
+
+ // for normal textures:
+ // <carname>DoorDNorm.bmp
+ // <carname>DoorPNorm.bmp
+ // <carname>HoodNorm.bmp
+ // <carname>BackNorm.bmp current: BarTrunk.bmp
+
+
+ // for damage textures:
+ // <carname>DoorPDam.bmp current: BarracudaDoorPDam.bmp
+ // <carname>HoodDam.bmp current: BarracudaHoodDam.bmp
+ // <carname>DoorDDam.bmp current: BarracudaDoorDDam.bmp
+ // <carname>BackDam.bmp current: BarracudaBackDam.bmp
+
+ // !!
+ // TODO - search a particular inventory section only?
+
+ char buffy[128];
+
+
+ // change logic here - if we find at least one set of shader, normal texture, damage texture
+ // return true.
+
+
+
+ bool result = false;
+
+ bool doorD = true;
+ bool doorP = true;
+ bool hood = true;
+ bool trunk = true;
+
+
+ //-------
+ //shaders
+ //-------
+
+ sprintf(buffy, "%sDoorDNorm_m", carname);
+ mDoorDShader = p3d::find<tShader>(buffy);
+ if(mDoorDShader)
+ {
+ mDoorDShader->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ doorD = false;
+ }
+
+ sprintf(buffy, "%sDoorPNorm_m", carname);
+ mDoorPShader = p3d::find<tShader>(buffy);
+ if(mDoorPShader)
+ {
+ mDoorPShader->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ doorP = false;
+ }
+
+
+ sprintf(buffy, "%sHoodNorm_m", carname);
+ mHoodShader = p3d::find<tShader>(buffy);
+ if(mHoodShader)
+ {
+ mHoodShader->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ hood = false;
+ }
+
+
+ sprintf(buffy, "%sBackNorm_m", carname);
+ mTrunkShader = p3d::find<tShader>(buffy);
+ if(mTrunkShader)
+ {
+ mTrunkShader->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ trunk = false;
+ }
+
+
+
+ //----------------
+ // normal textures
+ //----------------
+
+ sprintf(buffy, "%sDoorDNorm.bmp", carname);
+ mDoorDTextureNorm = p3d::find<tTexture>(buffy);
+ if(mDoorDTextureNorm)
+ {
+ mDoorDTextureNorm->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ doorD = false;
+ }
+
+
+ sprintf(buffy, "%sDoorPNorm.bmp", carname);
+ mDoorPTextureNorm = p3d::find<tTexture>(buffy);
+ if(mDoorPTextureNorm)
+ {
+ mDoorPTextureNorm->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ doorP = false;
+ }
+
+
+ sprintf(buffy, "%sHoodNorm.bmp", carname);
+ mHoodTextureNorm = p3d::find<tTexture>(buffy);
+ if(mHoodTextureNorm)
+ {
+ mHoodTextureNorm->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ hood = false;
+ }
+
+
+ sprintf(buffy, "%sBackNorm.bmp", carname);
+ mTrunkTextureNorm = p3d::find<tTexture>(buffy);
+ if(mTrunkTextureNorm)
+ {
+ mTrunkTextureNorm->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ trunk = false;
+ }
+
+
+
+ //----------------
+ // damage textures
+ //----------------
+
+ sprintf(buffy, "%sDoorDDam.bmp", carname);
+ mDoorDTextureDam = p3d::find<tTexture>(buffy);
+ if(mDoorDTextureDam)
+ {
+ mDoorDTextureDam->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ doorD = false;
+ }
+
+
+ sprintf(buffy, "%sDoorPDam.bmp", carname);
+ mDoorPTextureDam = p3d::find<tTexture>(buffy);
+ if(mDoorPTextureDam)
+ {
+ mDoorPTextureDam->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ doorP = false;
+ }
+
+
+ sprintf(buffy, "%sHoodDam.bmp", carname);
+ mHoodTextureDam = p3d::find<tTexture>(buffy);
+ if(mHoodTextureDam)
+ {
+ mHoodTextureDam->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ hood = false;
+ }
+
+
+ sprintf(buffy, "%sBackDam.bmp", carname);
+ mTrunkTextureDam = p3d::find<tTexture>(buffy);
+ if(mTrunkTextureDam)
+ {
+ mTrunkTextureDam->AddRef();
+ }
+ else
+ {
+ rTunePrintf("vehicle damage: cant find %s\n", buffy);
+ //result = false;
+ trunk = false;
+ }
+
+
+ // now if any of doorD, doorP, hood, or trunk are true - return true
+
+ result = doorD | doorP | hood | trunk;
+
+
+
+ //----------------------------------------------------
+ // all of the above was if we are a type 1 or 2 damage
+ //
+ // for type 3 we just expect the following:
+ //----------------------------------------------------
+
+ //-------
+ // shader
+ //-------
+ /*
+
+
+ // ancient fucking history
+
+
+ sprintf(buffy, "%sChassisNorm_m", carname);
+ mChassisShader = p3d::find<tShader>(buffy);
+ if(mChassisShader)
+ {
+ mChassisShader->AddRef();
+ }
+
+ //---------------
+ // normal texture
+ //---------------
+
+ sprintf(buffy, "%sChassisNorm.bmp", carname);
+ mChassisTextureNorm = p3d::find<tTexture>(buffy);
+ if(mChassisTextureNorm)
+ {
+ mChassisTextureNorm->AddRef();
+ }
+
+ //---------------
+ // damage texture
+ //---------------
+
+ sprintf(buffy, "%sChassisDam.bmp", carname);
+ mChassisTextureDam = p3d::find<tTexture>(buffy);
+ if(mChassisTextureDam)
+ {
+ mChassisTextureDam->AddRef();
+ }
+ */
+
+
+ return result;
+
+}
+
+
+
+//=============================================================================
+// GeometryVehicle::SetSmoke
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (ParticleEnum::ParticleID pid)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetEngineSmoke(ParticleEnum::ParticleID pid)
+{
+ mEngineParticleAttr.mType = pid;
+}
+
+
+/*
+ParticleEnum::ParticleID mType;
+
+ // only one bias so far, the emission bias
+ // other biases would be simple to put in
+ // NumParticles/Life/Speed/Weight/Gravity/Drag/Size/Spin
+ float mEmissionBias;
+*/
+
+
+//=============================================================================
+// GeometryVehicle::SetWheelSmoke
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (ParticleEnum::ParticleID pid, float bias)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetWheelSmoke( int wheel, ParticleEnum::ParticleID pid, float bias)
+{
+ switch( wheel )
+ {
+ case 0:
+ mRightWheelParticleAttr.mType = pid;
+ mRightWheelParticleAttr.mEmissionBias = bias;
+ break;
+ case 1:
+ mLeftWheelParticleAttr.mType = pid;
+ mLeftWheelParticleAttr.mEmissionBias = bias;
+ break;
+ default:
+ mRightWheelParticleAttr.mType = pid;
+ mRightWheelParticleAttr.mEmissionBias = bias;
+
+ mLeftWheelParticleAttr.mType = pid;
+ mLeftWheelParticleAttr.mEmissionBias = bias;
+ break;
+ };
+}
+
+
+//=============================================================================
+// GeometryVehicle::DamageTextureChassis
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (bool on)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::DamageTextureChassis(bool on)
+{
+ rAssert(0); // this is fucking history
+ if(mChassisShader)
+ {
+ if(on)
+ {
+ mChassisShader->SetTexture(PDDI_SP_BASETEX, mChassisTextureDam);
+ }
+ else
+ {
+ mChassisShader->SetTexture(PDDI_SP_BASETEX, mChassisTextureNorm);
+ }
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::DamageTextureTrunk
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (bool on)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::DamageTextureTrunk(bool on)
+{
+ if(mTrunkShader && mTrunkTextureDam && mTrunkTextureNorm)
+ {
+ if(on)
+ {
+ mTrunkShader->SetTexture(PDDI_SP_BASETEX, mTrunkTextureDam);
+ }
+ else
+ {
+ mTrunkShader->SetTexture(PDDI_SP_BASETEX, mTrunkTextureNorm);
+ }
+ }
+}
+
+//=============================================================================
+// GeometryVehicle::DamageTextureHood
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (bool on)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::DamageTextureHood(bool on)
+{
+ if(mHoodShader && mHoodTextureDam && mHoodTextureNorm)
+ {
+ if(on)
+ {
+ mHoodShader->SetTexture(PDDI_SP_BASETEX, mHoodTextureDam);
+ }
+ else
+ {
+ mHoodShader->SetTexture(PDDI_SP_BASETEX, mHoodTextureNorm);
+ }
+ }
+}
+
+//=============================================================================
+// GeometryVehicle::DamageTextureDoorP
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (bool on)
+//
+// Return:
+//
+//=============================================================================
+void GeometryVehicle::DamageTextureDoorP(bool on)
+{
+ if(mDoorPShader && mDoorPTextureDam && mDoorPTextureNorm)
+ {
+ if(on)
+ {
+ mDoorPShader->SetTexture(PDDI_SP_BASETEX, mDoorPTextureDam);
+ }
+ else
+ {
+ mDoorPShader->SetTexture(PDDI_SP_BASETEX, mDoorPTextureNorm);
+ }
+ }
+}
+
+//=============================================================================
+// GeometryVehicle::DamageTextureDoorD
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (bool on)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::DamageTextureDoorD(bool on)
+{
+ if(mDoorDShader && mDoorDTextureDam && mDoorDTextureNorm)
+ {
+ if(on)
+ {
+ mDoorDShader->SetTexture(PDDI_SP_BASETEX, mDoorDTextureDam);
+ }
+ else
+ {
+ mDoorDShader->SetTexture(PDDI_SP_BASETEX, mDoorDTextureNorm);
+ }
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::HideFlappingPiece
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int jointindex)
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::HideFlappingPiece(int jointindex, bool doit)
+{
+
+ //tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(jointindex));
+
+ int i;
+ for(i = 0; i < mCompositeDrawable->GetNumDrawableElement(); i++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop = (tCompositeDrawable::DrawablePropElement*)(mCompositeDrawable->GetDrawableElement(i));
+ if(prop && prop->GetPoseIndex() == jointindex)
+ {
+ //prop->SetVisibility(false);
+ prop->SetVisibility(!doit);
+ }
+ }
+
+}
+
+
+//------------------------------------------------------------------------
+void GeometryVehicle::Update(float dt)
+{
+ AdvanceAnimationControllers( dt * 1000.0f );
+
+
+
+ rmt::Vector vehicleVelocity;
+ mVehicleOwner->GetVelocity( &vehicleVelocity );
+ // Certain animations and particle systems are scaled by the velocity
+ // Calculate what % the scalar should be
+ float animationSpeedBias = vehicleVelocity.Magnitude() / PARTICLE_SYSTEM_MAX_VELOCITY;
+ animationSpeedBias = animationSpeedBias > 1.0f ? 1.0f : animationSpeedBias;
+ animationSpeedBias = animationSpeedBias < 0.0f ? 0.0f : animationSpeedBias;
+
+ if ( mVariableEmissionParticleSystem != NULL )
+ {
+ mVariableEmissionParticleSystem->SetBias( p3dParticleSystemConstants::EmitterBias::EMISSION, animationSpeedBias );
+ }
+
+
+ //
+ // Vary prof frink's refraction shader every frame
+ //
+
+ const tName& name = mVehicleOwner->GetNameObject();
+ if( name == "frink_v" )
+ {
+ float topSpeed = mVehicleOwner->GetTopSpeed();
+ float percentOfTopSpeed = vehicleVelocity.Magnitude() / topSpeed;
+ float refraction = percentOfTopSpeed;
+ refraction = rmt::Clamp( refraction, 0.0f, 1.0f );
+
+ /*
+ //we adjust the refractive index based on distance from the camera
+ rmt::Vector pos = mVehicleOwner->GetPosition ();
+ rmt::Vector cameraPosition;
+ unsigned int index = GetSuperCamManager()->GetSCC( 0 )->GetActiveSuperCamIndex();
+ SuperCam* camera = GetSuperCamManager()->GetSCC( 0 )->GetSuperCam( index );
+ camera->GetPosition( &cameraPosition );
+ float distance = ( pos - cameraPosition ).Magnitude();
+
+ float refractionAmount = refractiveIndex;
+ //if( distance > 20 )
+ {
+ refractionAmount *= 5;
+ refractionAmount /= distance;
+ }
+ */
+
+
+ //
+ // special case for the windshield shader
+ //
+ if( mRefractionShader[ 0 ] != NULL )
+ {
+ #ifdef RAD_GAMECUBE
+ float adjustedRefraction = refraction;
+ #else
+ float adjustedRefraction = 0.5f + (0.5f * refraction);
+ #endif
+
+ #ifndef RAD_WIN32
+ mRefractionShader[ 0 ]->SetFloat( PDDI_SP_REFRACTBLEND, adjustedRefraction );
+ mRefractionShader[ 0 ]->SetFloat( PDDI_SP_REFRACTINDEX, refractiveIndex );
+ #endif
+ }
+
+ //
+ // all other shaders
+ //
+ int i;
+ for( i = 1; i < MAX_REFRACTION_SHADERS; ++i )
+ {
+ if( mRefractionShader[ i ] != NULL )
+ {
+ #ifndef RAD_WIN32
+ mRefractionShader[ i ]->SetFloat( PDDI_SP_REFRACTBLEND, refraction );
+ mRefractionShader[ i ]->SetFloat( PDDI_SP_REFRACTINDEX, refractiveIndex );
+ #endif
+ }
+ }
+ }
+
+ // Update the collectible's position
+ if ( m_Collectible != NULL )
+ {
+ rmt::Matrix collectibleTransform;
+ collectibleTransform.Mult( m_CollectibleTransform, mVehicleOwner->mTransform );
+ m_Collectible->SetTransform( collectibleTransform );
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::SetTrafficBodyColour
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( pddiColour colour )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetTrafficBodyColour( pddiColour colour )
+{
+ if( mVehicleOwner->mVehicleType == VT_TRAFFIC || mVehicleOwner->mVehicleType == VT_USER )
+ {
+ if( mTrafficBodyDrawable != NULL )
+ {
+ mTrafficBodyDrawable->SetDesiredColour( colour );
+ }
+ if( mTrafficDoorDrawable != NULL )
+ {
+ mTrafficDoorDrawable->SetDesiredColour( colour );
+ }
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::GetP3DPose
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: tPose
+//
+//=============================================================================
+tPose* GeometryVehicle::GetP3DPose()
+{
+ if(mCompositeDrawable)
+ {
+ return mCompositeDrawable->GetPose();
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::SetShadowAdjustments
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float Adjustments[ 4 ][ 2 ] )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetShadowAdjustments( float Adjustments[ 4 ][ 2 ] )
+{
+ for( int i = 0; i < 4; ++i )
+ {
+ mShadowPointAdjustments[ i ][ 0 ] = Adjustments[ i ][ 0 ];
+ mShadowPointAdjustments[ i ][ 1 ] = Adjustments[ i ][ 1 ];
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::SetFadeAlpha
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int fade )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::SetFadeAlpha( int fade )
+{
+ rAssert( 0 <= fade && fade <= 255 );
+ mFadeAlpha = fade;
+}
+
+// Hack:
+// We hard code the traffic models so we can tell which vehicle
+// needs special treatment in terms of fading... Be sure to add
+// any new traffic models here or they won't fade properly.
+//
+// The problem is that the intbroadcast on all the shaders in
+// this composite drawable is going to reset everyone's blend
+// mode to BLEND_ALPHA... Brakelights, however, need BLEND_ADDITIVE
+// and some cars (cCells, cPolice) have special light effects that
+// have similar problems. Setting these all to BLEND_ALPHA will
+// cause the billboards to not display properly, so we make sure
+// we do an intbroadcast (using ProcessShaders()) only for cars
+// that are using traffic models (including non-traffic cars such
+// as mission AI cars).
+//
+// The alternative to this is to avoid using ProcessShaders() and
+// set the appropriate shader for each composite drawable's element
+// that need to fade independently... Could be messy...
+// This hard-coded list is not so bad..
+//
+//=============================================================================
+// GeometryVehicle::IsTrafficModel
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool GeometryVehicle::IsTrafficModel()
+{
+ rAssert( mVehicleOwner );
+
+ VehicleEnum::VehicleID ID = mVehicleOwner->mVehicleID;
+
+ if( ID == VehicleEnum::HUSKA ||
+
+ ID == VehicleEnum::COMPACTA ||
+ ID == VehicleEnum::PICKUPA ||
+ ID == VehicleEnum::MINIVANA ||
+ ID == VehicleEnum::SUVA ||
+ ID == VehicleEnum::SPORTSA ||
+
+ ID == VehicleEnum::SPORTSB ||
+ ID == VehicleEnum::SEDANA ||
+ ID == VehicleEnum::SEDANB ||
+ ID == VehicleEnum::TAXIA ||
+ ID == VehicleEnum::WAGONA ||
+
+ ID == VehicleEnum::COFFIN ||
+ ID == VehicleEnum::HALLO ||
+ ID == VehicleEnum::WITCHCAR ||
+ ID == VehicleEnum::SHIP ||
+ ID == VehicleEnum::AMBUL ||
+
+ ID == VehicleEnum::BURNSARM ||
+ ID == VehicleEnum::FISHTRUC ||
+ ID == VehicleEnum::GARBAGE ||
+ ID == VehicleEnum::ICECREAM ||
+ ID == VehicleEnum::ISTRUCK ||
+
+ ID == VehicleEnum::NUCTRUCK ||
+ ID == VehicleEnum::PIZZA ||
+ ID == VehicleEnum::SCHOOLBU ||
+ ID == VehicleEnum::VOTETRUC ||
+ ID == VehicleEnum::GLASTRUC )
+
+ {
+ return true;
+ }
+ return false;
+}
+
+
+//=============================================================================
+// GeometryVehicle::FadeRoof
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool fade )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FadeRoof( bool fade )
+{
+ rAssert( 0 <= INCAR_ROOF_ALPHA && INCAR_ROOF_ALPHA <= 255 );
+ mRoofTargetAlpha = (fade)? INCAR_ROOF_ALPHA : 255;
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindRoofGeometry
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* model )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindRoofGeometry( const char* model )
+{
+ char opac[64];
+ sprintf(opac, "%sRoofOpacShape", model );
+ tUID opacID = tEntity::MakeUID( opac );
+
+ char alpha[64];
+ sprintf(alpha, "%sRoofAlphaShape", model );
+ tUID alphaID = tEntity::MakeUID( alpha );
+
+ for( int j = 0; j < mCompositeDrawable->GetNumDrawableElement(); j++)
+ {
+ tCompositeDrawable::DrawablePropElement* prop =
+ (tCompositeDrawable::DrawablePropElement*)
+ (mCompositeDrawable->GetDrawableElement(j));
+
+ if( prop )
+ {
+ tUID uid = prop->GetUID();
+ if( uid == opacID )
+ {
+ mRoofOpacShape = prop;
+ }
+ else if( uid == alphaID )
+ {
+ mRoofAlphaShape = prop;
+ }
+ }
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::EnableLights
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( bool enable )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::EnableLights( bool enable )
+{
+ mEnableLights = enable;
+}
+
+
+//=============================================================================
+// GeometryVehicle::GetVehicleColour
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: tColour
+//
+//=============================================================================
+tColour GeometryVehicle::GetVehicleColour()const
+{
+ if ( mTrafficBodyDrawable != NULL )
+ {
+ return mTrafficBodyDrawable->GetDesiredColour();
+ }
+ else
+ {
+ const tColour DEFAULT_COLOUR( 128,128,128 );
+ return DEFAULT_COLOUR;
+ }
+}
+
+
+//=============================================================================
+// GeometryVehicle::FindAnimationControllers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* multicontrollername )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::FindAnimationControllers( const char* multicontrollername )
+{
+
+ tMultiController* mc = p3d::find< tMultiController >( multicontrollername );
+ if( mc == NULL )
+ return;
+
+ HeapMgr()->PushHeap(GMA_LEVEL_OTHER);
+
+ // Iterate through the frame controllers and add them to our own list
+ for ( unsigned int i = 0 ; i < mc->GetNumTracks() ; i++ )
+ {
+ tFrameController* fc = mc->GetTrack( i )->Clone();
+
+ VehicleFrameController vfc;
+
+ vfc.trigger = eNone;
+ vfc.brakeBias = vfc.gasBias = vfc.speedBias = 0;
+ vfc.timeBias = 1.0f;
+ vfc.frameSetter = false;
+
+ GetSpecialController( fc->GetUID(), &vfc );
+ vfc.frameController = fc;
+ vfc.frameController->AddRef();
+ vfc.frameController->Reset();
+ vfc.frameController->SetCycleMode( FORCE_CYCLIC );
+
+ // What kind of animation controller are we dealing with?
+ // Possibly a pose controller?
+ tPoseAnimationController* poseController = dynamic_cast< tPoseAnimationController* >( vfc.frameController );
+ if ( poseController )
+ {
+ // The compositeDrawble::clone function allocates a brand new pose
+ // set the frame controller that this pose is the one to update upon
+ // Advance()
+ poseController->SetPose( mCompositeDrawable->GetPose() );
+
+ }
+
+ mFrameControllers.push_back( vfc );
+ }
+ HeapMgr()->PopHeap(GMA_LEVEL_OTHER);
+}
+
+
+//=============================================================================
+// GeometryVehicle::GetSpecialController
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( tUID name, VehicleFrameController* outSpecialController )
+//
+// Return: bool
+//
+//=============================================================================
+bool GeometryVehicle::GetSpecialController( tUID name, VehicleFrameController* outSpecialController )
+{
+ bool wasFound = false;
+ // iterate through the preset "special" controllers and find any with the same name
+ for ( int i = 0 ; i < NUM_FRAME_CONTROLLERS ; i++ )
+ {
+ if ( name == tName::MakeUID( FRAME_CONTROLLER_DATA[i].name ) )
+ {
+ *outSpecialController = FRAME_CONTROLLER_DATA[i];
+ wasFound = true;
+ break;
+ }
+ }
+ return wasFound;
+}
+
+
+//=============================================================================
+// GeometryVehicle::AdvanceAnimationControllers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float deltaTime )
+//
+// Return: void
+//
+//=============================================================================
+void GeometryVehicle::AdvanceAnimationControllers( float deltaTime )
+{
+ int numFCs = mFrameControllers.size();
+
+ float gas = mVehicleOwner->GetGas();
+ float speed = mVehicleOwner->GetSpeedKmh();
+ float brake = mVehicleOwner->GetBrake();
+
+ if ( mVehicleOwner->IsMovingBackward() )
+ speed *= -1;
+
+ FCTrigger trigger = eNone;
+
+ if ( mVehicleOwner->GetDeltaGas() > 0.001f )
+ {
+ trigger = eBackfire;
+ }
+ for ( int i = 0 ; i < numFCs ; i++ )
+ {
+ VehicleFrameController& vfc = mFrameControllers[i];
+ if ( vfc.frameSetter )
+ {
+ // This thing doesn't ever run linearly, it only sets the frame
+ // it uses specifically.
+ // Figure out which frame to set based upon vehicle attributes
+ float relativeframe;
+ float numFrames = vfc.frameController->GetNumFrames();
+ relativeframe = vfc.brakeBias * brake + vfc.gasBias * gas +
+ vfc.speedBias * speed;
+
+ vfc.frameController->SetFrame( relativeframe * numFrames );
+ vfc.frameController->Advance( 0.0f );
+ }
+ else
+ {
+ // Is the animation triggered by something?
+ // Check to see if it is or isn't
+ if ( trigger != eNone && trigger == vfc.trigger )
+ {
+ // Its a triggered animation thats been triggered
+ // If the animation isnt playing already then restart it
+ if ( vfc.frameController->GetRelativeSpeed() < 0.001f )
+ {
+ vfc.frameController->Reset();
+ vfc.frameController->SetRelativeSpeed( 1.0f );
+ }
+ if ( vfc.frameController->LastFrameReached() )
+ {
+ vfc.frameController->SetRelativeSpeed( 0 );
+ }
+ }
+ // Standard animation, apply biases and play
+ float advanceTime = vfc.timeBias * deltaTime;
+ if ( advanceTime < 0 ) advanceTime = 0;
+
+ float advanceGas = gas * vfc.gasBias * deltaTime;
+ if ( advanceGas < 0 ) advanceGas = 0;
+
+ float advanceBrake = brake * vfc.brakeBias * deltaTime;
+ if ( advanceBrake < 0 ) advanceBrake = 0;
+
+ float advanceSpeed = speed * vfc.speedBias * deltaTime;
+ if ( advanceSpeed < 0 ) advanceSpeed = 0;
+
+ float totalAdvanceTime = advanceTime + advanceGas - advanceBrake + advanceSpeed;
+
+ vfc.frameController->Advance( totalAdvanceTime );
+ }
+ }
+}
+
diff --git a/game/code/worldsim/redbrick/geometryvehicle.h b/game/code/worldsim/redbrick/geometryvehicle.h
new file mode 100644
index 0000000..4da0a8b
--- /dev/null
+++ b/game/code/worldsim/redbrick/geometryvehicle.h
@@ -0,0 +1,322 @@
+/*===========================================================================
+ geometryvehicle.h
+
+ created Dec 7, 2001
+ by Greg Mayer
+
+ Copyright (c) 2001 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#ifndef _GEOMETRYVEHICLE_HPP
+#define _GEOMETRYVEHICLE_HPP
+
+class tPose;
+class tGeometry;
+class tTexture;
+class tShader;
+class tPoseAnimationController;
+class tParticleSystem;
+class tAnimation;
+class Vehicle;
+class TrafficBodyDrawable;
+class tBillboardQuadGroup;
+class tFrameController;
+class SkidmarkGenerator;
+class tMultiController;
+class StatePropCollectible;
+
+#include <p3d/anim/compositedrawable.hpp>
+#include <render/particles/vehicleparticleemitter.h>
+#include <worldsim/SkidMarks/SkidMarkGenerator.h>
+#include <constants/particleenum.h>
+#include <vector>
+#include <memory/stlallocators.h>
+
+#define MAX_REFRACTION_SHADERS 16
+
+struct BlobShadowParams;
+
+struct HeadLightParams
+{
+ HeadLightParams( const rmt::Vector& Pos, const rmt::Vector& Normal, const rmt::Vector& Forward, const rmt::Vector& Up ) :
+ GroundPos( Pos ), GroundNormal( Normal ), VehicleHeading( Forward ), VehicleUp( Up ) {};
+ const rmt::Vector& GroundPos;
+ const rmt::Vector& GroundNormal;
+ const rmt::Vector& VehicleHeading;
+ const rmt::Vector& VehicleUp;
+ float LightReach; // A value of 1 is the light right at the car (i.e. car on the ground), a zero is light max reach from the car (i.e. car high in the air)
+};
+
+enum FCTrigger
+{
+ eNone,
+ eBackfire
+};
+
+class GeometryVehicle
+{
+public:
+
+ GeometryVehicle();
+ ~GeometryVehicle();
+
+ bool Init( const char* name, Vehicle* owner, int i);
+
+ void Display();
+ //void DisplaySkids();
+ void UpdateSkids();
+ void SetSkidValues(int wheel, float intensity, rmt::Vector& normal, eTerrainType terrainType );
+
+
+ void Update(float dt);
+
+ tPose* GetP3DPose();
+
+ int CastsShadow();
+ void DisplayShadow( BlobShadowParams* BlobParams = 0 );
+
+ // The additive lights. Headlights, etc.
+ void DisplayLights( const HeadLightParams& LightParams );
+
+ void ShowBrakeLights();
+ void HideBrakeLights();
+
+ void ShowReverseLights();
+ void HideReverseLights();
+
+ void SetTrafficBodyColour( pddiColour colour );
+ void SetShadowAdjustments( float Adjustments[ 4 ][ 2 ] );
+ void SetShininess( unsigned char EnvRef ) { m_EnvRef = EnvRef; }
+
+ void SetFadeAlpha( int fade );
+
+ bool IsTrafficModel();
+
+ void FadeRoof( bool fade );
+
+ void EnableLights( bool enable );
+ void SetLightsOffDueToDamage(bool lightsOffDueToDamage) {mLightsOffDueToDamage = lightsOffDueToDamage;}
+ // Get the vehicle body colour, currently only valid for traffic vehicles
+ tColour GetVehicleColour()const;
+ bool HasVehicleColour()const { return (mTrafficBodyDrawable != NULL); }
+
+ // Structure that holds data on how to manipulate frame controllers
+ // that are tied to vehicle specific things like gas and velocity
+ // The animation frame of such an object is determined by
+ // frame = clamp( gasBias * currGas * brakeBias * brake + speedBias * currSpeed )
+ // controller->SetFrame( frame );
+ // note that brake values act as reverse gas and are in the range [0,1] like gas is
+
+
+ struct VehicleFrameController
+ {
+ const char* name;
+ tFrameController* frameController;
+ bool frameSetter;
+ FCTrigger trigger;
+ float gasBias;
+ float brakeBias;
+ float speedBias;
+ float timeBias;
+ };
+
+ float GetHeadlightScale();
+ void SetHeadlightScale( float scale );
+
+ // Attach a tDrawable collectible to the vehicle
+ // returns true if attached or false if not ( false happens
+ // when a collectible is already attached to the vehicle)
+ bool AttachCollectible( StatePropCollectible* );
+ StatePropCollectible* GetAttachedCollectible();
+ void DetachCollectible( const rmt::Vector& velocity, bool explode = true );
+
+private:
+
+ // TODO - keep like this?
+ friend class Vehicle;
+
+ Vehicle* mVehicleOwner;
+
+ bool GetArt( const char* name); // a bit obscure, but use return value to say true for localized damage textures and shaders present
+ void FindAndTurnOffWheels();
+ void FindAndTurnOffFrontWheelsOnly(); // for rocket car
+ void FindBrakeLightBillboardJoints();
+ void FindHeadLightBillboardJoints();
+ void FindGhostGlowBillboards( const char* model );
+ void FindRoofGeometry( const char* model );
+ void FindNukeGlowBillboards( const char* model );
+
+ void SetCollectibleHardpointPosition( const rmt::Vector& position );
+ void SetCollectibleHardpointTransform( float rotx ,float roty ,float rotz, const rmt::Vector& position );
+
+ tCompositeDrawable* mCompositeDrawable;
+
+ // some debug shit to make the car change colour when you bottom out
+ tGeometry* mChassisGeometry;
+
+
+ tShader* mRefractionShader[ MAX_REFRACTION_SHADERS ];
+
+ //-------------------------------------------------------
+ // damage texture stuff
+ //
+ // get specific test working then make a general solution
+ //
+ //-------------------------------------------------------
+
+ // need pointers to the tShaders so we can swap the texture they use
+ // need pointers to the textures to swap in
+ // need pointers to the textures to swap back?
+
+ // we should find these for type 1 and 2 damage
+
+
+ tShader* mHoodShader;
+ tShader* mTrunkShader;
+ tShader* mDoorPShader;
+ tShader* mDoorDShader;
+
+ tTexture* mHoodTextureDam;
+ tTexture* mTrunkTextureDam;
+ tTexture* mDoorPTextureDam;
+ tTexture* mDoorDTextureDam;
+
+ tTexture* mHoodTextureNorm;
+ tTexture* mTrunkTextureNorm;
+ tTexture* mDoorPTextureNorm;
+ tTexture* mDoorDTextureNorm;
+
+ bool FindDamageShadersAndTextures( const char* name); // only returns true if localized damage textures and shaders are present
+ //void TriggerDamage();
+
+ void DamageTextureDoorD(bool on);
+ void DamageTextureDoorP(bool on);
+ void DamageTextureHood(bool on);
+ void DamageTextureTrunk(bool on);
+
+ void HideFlappingPiece(int jointindex, bool doit);
+
+ void InitParticles();
+
+ void SetEngineSmoke(ParticleEnum::ParticleID pid);
+ void SetWheelSmoke( int wheel, ParticleEnum::ParticleID pid, float bias);
+
+ tShader* mChassisShader;
+ tTexture* mChassisTextureNorm;
+ tTexture* mChassisTextureDam;
+ void DamageTextureChassis(bool on);
+
+
+ VehicleParticleEmitter* mParticleEmitter;
+
+ ParticleAttributes mEngineParticleAttr; // eNull
+ ParticleAttributes mLeftWheelParticleAttr;
+ ParticleAttributes mRightWheelParticleAttr;
+ ParticleAttributes mTailPipeParticleAttr; // eNull
+
+ //eEngineSmokeLight,
+ //eEngineSmokeHeavy,
+
+ // Particle system with variable emission rate depending on speed
+ tParticleSystem* mVariableEmissionParticleSystem;
+
+
+ SkidMarkGenerator* mSkidMarkGenerator;
+ void InitSkidMarks();
+
+ // ?
+ // tPoseAnimationController* mAnimController;
+
+ //tPoseAnimation* mAnim;
+
+
+ //tPoseAnimationController* mAnimController;
+ tAnimation* mAnim;
+
+
+ float mAnimRevPerSecondBase;
+
+ float mRevMult;
+
+ // Special particle effects (only present on a few vehicles, like Frink or
+ // the zombie car)
+ ParticleEnum::ParticleID mSpecialEffect;
+
+ // Cache the last position of the vehicle for
+ // spherical environment map rotation based on distance travelled
+ //
+ rmt::Vector mLastPosition;
+ float mCurEnvMapRotation;
+
+ int mBrakeLightJoints[4];
+ int mReverseLightJoints[4];
+
+ TrafficBodyDrawable* mTrafficBodyDrawable;
+ TrafficBodyDrawable* mTrafficDoorDrawable;
+
+ float mShadowPointAdjustments[ 4 ][ 2 ]; // Nudge around the shadow points. See notes in the shadow display.
+
+ int mFadeAlpha;
+
+ enum {
+ NUM_BRAKELIGHT_BBQS = 4,
+#ifdef RAD_WIN32
+ NUM_FRINKARC_BBQS = 3,
+#endif
+ NUM_GHOSTGLOW_BBQGS = 6,
+ NUM_GHOSTGLOW_BBQS = 6,
+ NUM_NUKEGLOW_BBQGS = 1,
+ NUM_NUKEGLOW_BBQS = 3
+ };
+
+#ifdef RAD_WIN32
+ tBillboardQuadGroup* mFrinkArc;
+ tColour mOriginalFrinkArcColour[ NUM_FRINKARC_BBQS ];
+#endif
+
+ tBillboardQuadGroup* mBrakeLights[NUM_BRAKELIGHT_BBQS];
+ // This relies on each quadgroup containing only one quad
+ // & will store color of that quad
+ tColour mOriginalBrakeLightColours[NUM_BRAKELIGHT_BBQS];
+
+ bool mUsingTrafficModel;
+
+ bool mHasGhostGlow;
+ tBillboardQuadGroup* mGhostGlows[NUM_GHOSTGLOW_BBQGS];
+ tColour mOriginalGhostGlowColours[NUM_GHOSTGLOW_BBQS];
+
+ bool mHasNukeGlow;
+ tBillboardQuadGroup* mNukeGlows[NUM_NUKEGLOW_BBQGS];
+ tColour mOriginalNukeGlowColours[NUM_NUKEGLOW_BBQS];
+
+
+ bool mBrakeLightsOn;
+ float mBrakeLightScale;
+ float mHeadLightScale;
+ bool mEnableLights;
+ bool mLightsOffDueToDamage;
+
+ tCompositeDrawable::DrawablePropElement* mRoofOpacShape;
+ tCompositeDrawable::DrawablePropElement* mRoofAlphaShape;
+ tShader* mRoofShader;
+ int mRoofAlpha;
+ int mRoofTargetAlpha;
+
+
+ std::vector< VehicleFrameController, s2alloc< VehicleFrameController > > mFrameControllers;
+ void FindAnimationControllers( const char* multicontrollername );
+ bool GetSpecialController( tUID name, VehicleFrameController* outSpecialController );
+ void AdvanceAnimationControllers( float deltaTime );
+
+ // A collectible that can be attached to the car
+ // Used for l7m5 mission
+ StatePropCollectible* m_Collectible;
+ rmt::Matrix m_CollectibleTransform;
+
+ unsigned char m_EnvRef;
+};
+
+#endif // _GEOMETRYVEHICLE_HPP \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/physicslocomotion.cpp b/game/code/worldsim/redbrick/physicslocomotion.cpp
new file mode 100644
index 0000000..1ab2899
--- /dev/null
+++ b/game/code/worldsim/redbrick/physicslocomotion.cpp
@@ -0,0 +1,2069 @@
+/*===========================================================================
+ physicslocomotion.cpp
+
+ created April 24, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#include <simcommon/simstatearticulated.hpp>
+#include <simphysics/articulatedphysicsobject.hpp>
+#include <worldsim/redbrick/physicslocomotion.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/wheel.h>
+#include <worldsim/worldphysicsmanager.h>
+
+
+#include <poser/pose.hpp>
+#include <poser/poseengine.hpp>
+#include <poser/posedriver.hpp>
+
+#include <render/IntersectManager/IntersectManager.h>
+
+#include <cheats/cheatinputsystem.h>
+
+using namespace sim;
+
+/*
+ data that should move from Vehicle to here...
+
+ struct TerrainIntersectCache
+ {
+ rmt::Vector closestTriPosn;
+ rmt::Vector closestTriNormal;
+ rmt::Vector planePosn;
+ rmt::Vector planeNorm;
+ };
+
+ TerrainIntersectCache mTerrainIntersectCache[4]; // is this wasting too much memory?
+
+
+???
+ float mRollingFrictionForce;
+ float mTireLateralResistance;
+ float mSlipGasModifier;
+???
+
+
+
+
+
+*/
+
+
+//------------------------------------------------------------------------
+PhysicsLocomotion::PhysicsLocomotion(Vehicle* vehicle) : VehicleLocomotion(vehicle)
+{
+ mVehicle = vehicle;
+ //
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ mForceApplicationPoints[i].Clear();
+ mSuspensionPointVelocities[i].Clear();
+
+ mCachedSuspensionForceResults[i] = 0.0f;
+
+ // TODO - initialize better?
+ // everytime control is switched to VL_PHYSICS
+ //mWheelTerrainCollisionFixDepth[i] = 0.0f;
+
+ //mWheelTerrainCollisionNormals[i].x = 0.0f;
+ //mWheelTerrainCollisionNormals[i].y = 1.0f;
+ //mWheelTerrainCollisionNormals[i].z = 0.0f;
+
+ //mWheelTerrainCollisionPoints[i].Clear();
+
+ //mGoodWheelTerrainCollisionValue[i] = false;
+ }
+
+ mCurrentSteeringForce = mVehicle->mDesignerParams.mDpTireLateralResistanceNormal;
+
+}
+
+
+
+//------------------------------------------------------------------------
+PhysicsLocomotion::~PhysicsLocomotion()
+{
+ //
+}
+
+
+
+//=============================================================================
+// PhysicsLocomotion::SetTerrainIntersectCachePointsForNewTransform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::SetTerrainIntersectCachePointsForNewTransform()
+{
+ rmt::Vector tempVec;
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ tempVec = mVehicle->mSuspensionWorldSpacePoints[i];
+ tempVec.y -= 2.0f * mVehicle->mWheels[i]->mRadius;
+
+ mTerrainIntersectCache[i].closestTriPosn = tempVec;
+ mTerrainIntersectCache[i].closestTriNormal.Set(0.0f, 1.0f, 0.0f);
+
+ mTerrainIntersectCache[i].planePosn = tempVec;
+ mTerrainIntersectCache[i].planeNorm.Set(0.0f, 1.0f, 0.0f);
+
+ mTerrainIntersectCache[i].mTerrainType = TT_Road;
+ mTerrainIntersectCache[i].mInteriorTerrain = false;
+
+ mIntersectNormalUsed[i].Set(0.0f, 1.0f, 0.0f);
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::MoveWheelsToBottomOfSuspension
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::MoveWheelsToBottomOfSuspension()
+{
+
+ poser::Pose* pose = mVehicle->mPoseEngine->GetPose();
+
+ // want to call false here because we want the movement of the hierarchy based on animation to also be available for collision
+ // TODO - make sure this is done in the traffic locomotion case as well.
+ mVehicle->mPoseEngine->Begin(false); //Cary Here - I've moved it.
+ //mVehicle->mPoseEngine->Begin(true); //Cary Here - I've moved it.
+ // TODO - even need to do this?
+
+
+ // TODO
+ // note:
+ // are we making the assumption that the animation doesn't move the suspension points around?
+ // for now, yes.
+
+
+ // TODO - only do this if wheelInCollision?
+
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ Wheel* wheel = mVehicle->mWheels[i];
+
+ poser::Joint* joint = pose->GetJoint(mVehicle->mWheelToJointIndexMapping[i]);
+ rmt::Vector trans = joint->GetObjectTranslation();
+
+ //trans.y -= mVehicle->mWheels[i]->mLimit;
+ // TODO - verify that the -= is the thing to do here
+ trans.y -= wheel->mLimit;
+
+ trans.y += mVehicle->mDesignerParams.mDpSuspensionYOffset;
+
+ joint->SetObjectTranslation(trans);
+
+
+
+ // TODO - if this method actually works, make nicer interface with wheel to do this
+
+ //
+ // remember, mYOffset is the offset (upwards) from the bottom of the suspension limit, to fix wheel out of collision (or just barely in collision)
+ wheel->mYOffset = -1.0f * wheel->mLimit;
+
+ wheel->mWheelInCollision = false;
+ wheel->mWheelBottomedOutThisFrame = false;
+
+
+ }
+
+ // just in case
+ //
+ // TODO - review why the hell you're doing this? - this one's pretty important I think
+ CollisionObject* collObj = mVehicle->mSimStateArticulated->GetCollisionObject();
+ collObj->SetHasMoved(true);
+
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::UpdateVehicleGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::UpdateVehicleGroundPlane()
+{
+ // create a new transform for the ground plane sim state and set it.
+
+ rmt::Vector p, n;
+ p.Set(0.0f, 0.0f, 0.0f);
+ n.Set(0.0f, 0.0f, 0.0f);
+
+ /*
+ int i;
+ int count = 0;
+ for(i = 0; i < 4; i++)
+ {
+ //if(mFoundPlane[i])
+
+ // try doing this with whatever's in there
+ // old or new
+ //
+ // TODO - is this safe?
+
+ {
+ p.Add(mVehicle->mTerrainIntersectCache[i].planePosn);
+ n.Add(mVehicle->mTerrainIntersectCache[i].planeNorm);
+ }
+
+
+ }
+
+ p.Scale(0.25f);
+ n.Scale(0.25f);
+ */
+
+ // new idea for ground plane
+ // pick the most upright normal, and the lowest height point
+ // ??
+
+ p = mTerrainIntersectCache[0].planePosn;
+ n = mTerrainIntersectCache[0].planeNorm;
+
+ int i;
+ for(i = 1; i < 4; i++)
+ {
+ if(mTerrainIntersectCache[i].planePosn.y < p.y)
+ {
+ p = mTerrainIntersectCache[i].planePosn;
+ }
+
+ if( GetWorldPhysicsManager()->mWorldUp.DotProduct(mTerrainIntersectCache[i].planeNorm) >
+ GetWorldPhysicsManager()->mWorldUp.DotProduct(n) )
+ {
+ n = mTerrainIntersectCache[i].planeNorm;
+ }
+
+ }
+
+
+ // new approach with manual sim state
+
+ mVehicle->mGroundPlaneWallVolume->mPosition = p;
+ mVehicle->mGroundPlaneWallVolume->mNormal = n;
+
+
+ sim::CollisionObject* co = mVehicle->mGroundPlaneSimState->GetCollisionObject();
+ co->PostManualUpdate();
+
+
+
+
+ /*
+ rmt::Matrix groundPlaneTransform;
+ groundPlaneTransform.Identity();
+
+ // TODO - optimize this call since we know what the heading is all the time
+ //rmt::Vector heading(0.0f, 0.0f, 1.0f);
+ rmt::Vector up(0.0f, 0.0f, 1.0f);
+
+ // note:
+ //!!!
+ //
+ // new approach - use our n as heading
+
+ //groundPlaneTransform.FillHeading(heading, n);
+ groundPlaneTransform.FillHeading(n, up);
+
+ groundPlaneTransform.FillTranslate(p);
+
+ //PushSimState(bool inReset=true) = 0; // better name for this would be Sync
+ // ? mSimStateArticulated->SetControl(simAICtrl);
+
+ mVehicle->mGroundPlaneSimState->SetTransform(groundPlaneTransform);
+
+
+ // ? mSimStateArticulated->SetControl(simSimulationCtrl);
+ // TODO - need to do this?
+ // double check with martin!!
+ //mVehicle->mGroundPlaneSimState->GetCollisionObject()->Update(); - don't seem to need this?
+ */
+
+}
+
+//------------------------------------------------------------------------
+void PhysicsLocomotion::PreCollisionPrep(bool firstSubstep)
+{
+
+ BEGIN_PROFILE("MoveWheelsToBottomOfSuspension")
+ MoveWheelsToBottomOfSuspension();
+ END_PROFILE("MoveWheelsToBottomOfSuspension")
+
+ // this is in substep, so comment out if we don't want it to happen
+ if(firstSubstep) // only do this once per update, but it must be inside the loop the first time
+ {
+
+ if(mVehicle->mSimStateArticulated->GetControl() != sim::simAICtrl)
+ {
+
+ BEGIN_PROFILE("FetchWheelTerrainCollisionInfo")
+ FetchWheelTerrainCollisionInfo();
+ END_PROFILE("FetchWheelTerrainCollisionInfo")
+ }
+ }
+ else
+ {
+ int stophere = 1;
+ }
+
+ BEGIN_PROFILE("UpdateVehicleGroundPlane")
+ UpdateVehicleGroundPlane();
+ END_PROFILE("UpdateVehicleGroundPlane")
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::CompareNormalAndHeight
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::CompareNormalAndHeight(int index, rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint)
+{
+
+ // important!
+ //
+ // assume this is being called from the collision solver agent _after_ we've already fetched new wheel terrain collision info
+
+ // so....
+ // how to decide?
+
+ // first try?
+ // y value of contact points?
+ rAssert(index >= 0 && index < 4);
+
+
+ if(mFoundPlane[index])
+ {
+
+ if(groundContactPoint.y > mTerrainIntersectCache[index].planePosn.y)
+ {
+
+ //mTerrainIntersectCache[index].planePosn = groundContactPoint;
+ //mTerrainIntersectCache[index].planeNorm = normalPointingAtCar;
+
+ // note this used to be in here??
+ mIntersectNormalUsed[index] = normalPointingAtCar;
+
+ }
+ }
+ else
+ {
+
+ // take the more uprigth normal
+ if(GetWorldPhysicsManager()->mWorldUp.DotProduct(normalPointingAtCar) >
+ GetWorldPhysicsManager()->mWorldUp.DotProduct(mIntersectNormalUsed[index]) )
+
+ {
+ mIntersectNormalUsed[index] = normalPointingAtCar;
+
+
+ }
+
+ }
+/*
+ struct TerrainIntersectCache
+ {
+ rmt::Vector closestTriPosn;
+ rmt::Vector closestTriNormal;
+ rmt::Vector planePosn;
+ rmt::Vector planeNorm;
+ };
+
+ TerrainIntersectCache mTerrainIntersectCache[4]; // is this wasting too much memory?
+
+ // need this because if we're driving on a static collision volume the terrain normal could be way off!
+ rmt::Vector mIntersectNormalUsed[4];
+*/
+
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::PreSubstepUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (Vehicle* vehicle)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::PreSubstepUpdate()
+{
+
+ //MoveWheelsToBottomOfSuspension();
+
+ //FetchWheelTerrainCollisionInfo();
+
+ //UpdateVehicleGroundPlane();
+
+}
+
+
+//------------------------------------------------------------------------
+void PhysicsLocomotion::SetForceApplicationPoints()
+{
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ // TODO - clean up
+ // for force application points should also add in wheel - nope, tried that, not so good
+
+ // TODO
+ // why do I have copies of thsi stuff here
+ mForceApplicationPoints[i] = mVehicle->mSuspensionWorldSpacePoints[i];
+
+ // try something new:
+
+ if(mVehicle->mVehicleType == VT_USER && (mVehicle->mVehicleState == VS_SLIP || mVehicle->mVehicleState == VS_EBRAKE_SLIP))
+ {
+
+ rmt::Vector tireFix = mVehicle->mVehicleUp;
+
+
+ // new test - decrease depending on % of top speed
+ //tireFix.Scale(mVehicle->mWheels[i]->mRadius * -1.0f * (1.0f - (mVehicle->mPercentOfTopSpeed * 0.5f)));
+
+ //tireFix.Scale(mVehicle->mWheels[i]->mRadius * -1.0f * (1.0f - (mVehicle->mPercentOfTopSpeed * 0.75f)));
+
+
+ //tireFix.Scale(mVehicle->mWheels[i]->mRadius * -1.0f * (1.0f - (mVehicle->mPercentOfTopSpeed * mVehicle->mPercentOfTopSpeed)));
+ tireFix.Scale(mVehicle->mWheels[i]->mRadius * mVehicle->mPercentOfTopSpeed);// * 0.5f);
+
+
+ //mForceApplicationPoints[i].y -= mVehicle->mWheels[i]->mRadius;
+
+
+ mForceApplicationPoints[i].Add(tireFix);
+ }
+
+ mSuspensionPointVelocities[i] = mVehicle->mSuspensionPointVelocities[i];
+
+ }
+
+}
+
+
+//------------------------------------------------------------------------
+void PhysicsLocomotion::ApplySuspensionForces(float dt, bool atrest)
+{
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ //rmt::Vector force = mVehicle->mVehicleUp;
+ //rmt::Vector force = mTerrainIntersectCache[i].planeNorm;
+ rmt::Vector force;
+ if(atrest)
+ {
+ force = mVehicle->mVehicleUp;
+ }
+ else
+ {
+ force = mIntersectNormalUsed[i];
+ }
+
+ //??
+ /*
+ // not helping
+ if( GetWorldPhysicsManager()->mWorldUp.DotProduct(mVehicle->mVehicleUp) >
+ GetWorldPhysicsManager()->mWorldUp.DotProduct(force) && mVehicle->mDoingJumpBoost)
+
+ {
+
+ force = mVehicle->mVehicleUp;
+ }
+ */
+
+ // debug
+ //float cos30 = 0.866f;
+ //float cos40 = 0.766f;
+ //if(GetWorldPhysicsManager()->mWorldUp.DotProduct(force) < cos30)
+
+
+ float yVelocityAlongSuspensionAxis = force.DotProduct(mSuspensionPointVelocities[i]);
+
+ //mCachedSuspensionForceResults[i] = mWheels[i]->CalculateSuspensionForce(mSuspensionPointVelocities[i].y, dt);
+ Wheel* wheel = mVehicle->mWheels[i];
+
+ float forceToUse = 0.0f;
+ mCachedSuspensionForceResults[i] = CalculateSuspensionForce(wheel, yVelocityAlongSuspensionAxis, dt, forceToUse);
+
+
+ force.Scale(forceToUse);
+
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->AddForce(force, &(mForceApplicationPoints[i]));
+ }
+
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::CalculateSuspensionForce
+//=============================================================================
+// Description: Comment
+//
+// note - moving thsi functionality from Wheel class to here because wheel
+// shouldn't do physics calculations, and this class shouldn't hold the
+// info about wheels - like k and c
+//
+//
+// Parameters: (Wheel* wheel, float suspensionPointYVelocity, float dt)
+//
+// Return: float
+//
+//=============================================================================
+float PhysicsLocomotion::CalculateSuspensionForce(Wheel* wheel, float suspensionPointYVelocity, float dt, float& forceToUse)
+{
+
+ // crazy shit goin' on here!
+ // the value we return we'll cachce for slip calculations and stuff
+ //
+ // the value we stuff in forceToUse is the one to make the car bank.
+ //
+ // the one we cache should not include the quadSpringForce
+
+
+ float force = 0.0f;
+
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = mVehicle->mVehicleUp.DotProduct(up);
+
+ float cos85 = 0.087f;
+ float cos80 = 0.17365f;
+ float cos65 = 0.4226f;
+
+ if(wheel->mWheelInCollision && tip > cos65) // minor TODO - should this number match any others?
+ {
+ // test
+ //
+ // make the spring also only push up
+
+ //float springForce = wheel->mk * wheel->mYOffset;
+ float springForce = 0.0f;
+ //if(wheel->mYOffset > 0.0f)
+
+ if(!(mVehicle->mDoingJumpBoost && wheel->mYOffset < 0.0f))
+ {
+ springForce = wheel->mk * wheel->mYOffset;
+ }
+
+
+
+ float quadSpringForce = 0.0f;
+ if(wheel->mYOffset > 0.0f)
+ {
+ quadSpringForce = wheel->mqk * wheel->mYOffset * wheel->mYOffset;
+ //springForce += quadSpringForce;
+ }
+
+
+ //
+ // or... topping out!
+ //
+ if((wheel->mLimit - rmt::Fabs(wheel->mYOffset)) < wheel->mLimit * 0.25f)
+ {
+ //springForce *= 3.0f;
+ }
+
+ // for now, only add damping if chassis is trying to compress suspension - ie. y velocity is down, -ve
+ float damperForce = 0.0f;
+
+
+ if(suspensionPointYVelocity > 0.0f)// this would make the dampe pull down
+ {
+ if((mVehicle->mDamperShouldNotPullDown[wheel->mWheelNum]) || mVehicle->mDoingJumpBoost)
+ {
+ // no suspension force down
+ }
+ else
+ {
+ // regular
+ damperForce = wheel->mc * -1.0f * suspensionPointYVelocity;
+ }
+
+ }
+ else
+ {
+ // no worries
+ damperForce = wheel->mc * -1.0f * suspensionPointYVelocity;
+ }
+
+
+
+ force = springForce + damperForce;
+ forceToUse = springForce + damperForce + quadSpringForce;
+ }
+ else
+ {
+ // need to relax spring somehow
+
+ // this is essentially useless....
+
+ float relaxDistance = wheel->mLimit / wheel->mSpringRelaxRate * dt;
+ if(wheel->mYOffset >= relaxDistance)
+ {
+ wheel->mYOffset -= relaxDistance;
+ }
+ else if(wheel->mYOffset <= -relaxDistance)
+ {
+ wheel->mYOffset += relaxDistance;
+ }
+
+ }
+
+ return force;
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::ApplyDragForce
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::ApplyDragForce()
+{
+ if(mVehicle->mSpeed > 1.0f)
+ {
+ rmt::Vector force = mVehicle->mVelocityCM;
+
+ // new test -
+ //force.y = 0.0f;
+
+ force.NormalizeSafe();
+
+ float mag = mVehicle->mSpeed * mVehicle->mSpeed * 0.5f * mVehicle->mDragCoeff;
+
+ // new
+ // car slows down too much when you let off gas
+ if(mVehicle->mGas == 0.0f && mVehicle->mSpeedKmh > 50.0f && mVehicle->mBrake == 0.0f)
+ {
+ mag *= 0.25f;
+ }
+
+
+ force.Scale(-1.0f * mag);
+
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->AddForce(force);
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::LowSpeedTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::LowSpeedTest()
+{
+
+ //static float test = 0.8f;
+ static float test = 0.8f;
+
+ static float linearWhittle = 0.6f;
+ static float angularWhittle = 0.1f;
+
+ //const float lowspeed = 1.0f;
+ //static float lowspeed = 2.0f;
+ float lowspeed = 2.0f;
+ float reallylowspeed = 0.05f;
+
+ // only apply if at least 2 wheels in contact with the ground:
+
+ int i;
+ int count = 0;
+ for(i = 0; i < 4; i++)
+ {
+ if(mVehicle->mWheels[i]->mWheelInCollision)
+ {
+ count++;
+ }
+ }
+
+ if(count > 1)
+ {
+
+ if(mVehicle->mSpeed < lowspeed && mVehicle->mGas < 0.1f /*&& mVehicle->mReverse < 0.1f*/ && mVehicle->mBrake < 0.1f) // new... brake
+ {
+ // whittle away speed, or just reset?
+ //mVehicle->mSimStateArticulated->ResetVelocities();
+ // I knew this was a bad idea
+
+ if(mVehicle->mSpeed < reallylowspeed)
+ {
+ mVehicle->mSimStateArticulated->ResetVelocities();
+ }
+ else
+ {
+
+ rmt::Vector& linearVel = mVehicle->mSimStateArticulated->GetLinearVelocity();
+ rmt::Vector& angularVel = mVehicle->mSimStateArticulated->GetAngularVelocity();
+
+ linearVel.Scale(linearWhittle);
+ angularVel.Scale(angularWhittle);
+ }
+ }
+ }
+
+
+}
+
+
+
+//=============================================================================
+// PhysicsLocomotion::ApplyAngularDrag
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::ApplyAngularDrag()
+{
+ if(mVehicle->mPercentOfTopSpeed > 0.01f)
+ {
+ const float magicshit = 3.0f;
+
+ rmt::Vector angularDragForce = mVehicle->mSimStateArticulated->GetAngularVelocity();
+ angularDragForce.Scale(-1.0f * magicshit * mVehicle->mDesignerParams.mDpMass);
+
+ sim::PhysicsObject* phobj = (sim::PhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject());
+ rAssert(phobj);
+
+ phobj->AddTorque(angularDragForce);
+ }
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::ApplyRollingFriction
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::ApplyRollingFriction()
+{
+ // to start with simplest possible model - constant?
+ //static float test = 1.0f;
+ //static float test2 = 2000.0f;
+
+ //static float test = 0.9f;
+ //static float test = 0.8f;
+ static float test = 0.8f;
+
+ //const float lowspeed = 1.0f;
+ //static float lowspeed = 2.0f;
+ static float lowspeed = 2.0f;
+ static float reallylowspeed = 0.05f;
+
+ static float torquemod = 0.001f;
+
+ // only apply if at least 2 wheels in contact with the ground:
+
+ int i;
+ int count = 0;
+ for(i = 0; i < 4; i++)
+ {
+ if(mVehicle->mWheels[i]->mWheelInCollision)
+ {
+ count++;
+ }
+ }
+
+ if(count > 1)
+ {
+
+ if(mVehicle->mSpeed > reallylowspeed && mVehicle->mGas == 0.0f)
+ {
+ rmt::Vector force = mVehicle->mVelocityCM;
+ force.Normalize();
+
+ force.Scale(-1.0f * mVehicle->mRollingFrictionForce * mVehicle->mDesignerParams.mDpMass);
+
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->AddForce(force);
+
+
+ }
+
+ else if(0)//mVehicle->mGas < 0.1f && mVehicle->mReverse < 0.1f && mVehicle->mBrake < 0.1f) // new... brake
+ {
+ // whittle away speed, or just reset?
+ //mVehicle->mSimStateArticulated->ResetVelocities();
+ // I knew this was a bad idea
+
+ //if(1)//mVehicle->mSpeed < reallylowspeed)
+ /*
+ if(mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->IsAtRest())
+ {
+
+ mVehicle->mSimStateArticulated->ResetVelocities();
+ mVehicle->mSimStateArticulated->SetControl(sim::simAICtrl);
+
+ //mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->ResetAppliedForces();
+
+ }
+ else
+ {
+ //mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->ResetAppliedForces();
+
+
+ //rmt::Vector& linearVel = mVehicle->mSimStateArticulated->GetLinearVelocity();
+ //rmt::Vector& angularVel = mVehicle->mSimStateArticulated->GetAngularVelocity();
+
+ //linearVel.Scale(test);
+ //angularVel.Scale(test);
+ }
+ */
+
+ }
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::HighSpeedInstabilityTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::HighSpeedInstabilityTest()
+{
+ const float threshold = 0.5f;
+
+ // from threshold to 100%, increase y cm offset
+
+ if(mVehicle->mPercentOfTopSpeed > threshold)
+ {
+ // should do ...
+ if(mVehicle->mInstabilityOffsetOn)
+ {
+ // already set
+ //
+ // nothing to do here
+ }
+ else
+ {
+ // set it
+
+ rmt::Vector unstableAdjust = mVehicle->mCMOffset;
+
+ //static float weeblemagic = 1.0f;
+ /*static*/ float magic = 1.0f;
+
+ // scale by point in range
+ // set range of 0 to 1 for start to limit
+ /*
+ float range = 1.0f - ((tip - end) / (start - end));
+
+ if(range < 0.0f)
+ {
+ range = 0.0f;
+ //rAssert(0);
+ }
+ if(range > 1.0f)
+ {
+ range = 1.0f;
+ }
+
+ float modifier = weeblemagic * range;
+ */
+ //weeble.y -= modifier;
+
+ unstableAdjust.y += magic;
+
+ //weeble.y -= 3.0f;
+
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(unstableAdjust);
+
+ mVehicle->mInstabilityOffsetOn = true;
+ }
+ }
+ else
+ {
+ // if it's on, shut it off
+ if(mVehicle->mInstabilityOffsetOn)
+ {
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mCMOffset);
+
+ mVehicle->mInstabilityOffsetOn = false;
+ }
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::Weeble
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::Weeble()
+{
+ bool doit = false;
+
+ // only do this for sideways tipping
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = mVehicle->mVehicleUp.DotProduct(up);
+
+ float cos35 = 0.8192f;
+ float cos45 = 0.7071f;
+ float cos55 = 0.5736f;
+ float cos40 = 0.766f;
+
+ float start = cos40;
+ float end = cos55;
+
+
+ // new test -
+ // if we are completely airborn - set cmoffset to 0,0,0
+
+ //if(mVehicle->mAirBorn)
+ //{
+ // if(!(mVehicle->mCMOffsetSetToOriginal))
+ // {
+ // //mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mCMOffset);
+ // mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mOriginalCMOffset);
+ // mVehicle->mCMOffsetSetToOriginal = true;
+ // }
+ //}
+ //else
+ {
+ /*
+ if(mVehicle->mCMOffsetSetToOriginal)
+ {
+ //mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mOriginalCMOffset);
+ mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mCMOffset);
+ mVehicle->mCMOffsetSetToOriginal = false;
+ }
+ */
+
+ //if(!mVehicle->mAirBorn && tip < start)
+ if(1)//tip < start)
+ {
+ bool slowdowntipping = false;
+ if( !(mVehicle->mWheels[0]->mWheelInCollision) && !(mVehicle->mWheels[3]->mWheelInCollision) )
+ {
+ doit = true;
+
+ if(mVehicle->mWheels[1]->mWheelInCollision && mVehicle->mWheels[2]->mWheelInCollision)
+ {
+ slowdowntipping = true;
+ }
+ }
+
+
+ if( !(mVehicle->mWheels[1]->mWheelInCollision) && !(mVehicle->mWheels[2]->mWheelInCollision) )
+ {
+ doit = true;
+
+ if(mVehicle->mWheels[0]->mWheelInCollision && mVehicle->mWheels[3]->mWheelInCollision)
+ {
+ slowdowntipping = true;
+ }
+ }
+
+ if(0)//(slowdowntipping)
+ {
+ // only resist tipping UP!
+
+
+
+ //static float magicshit = 3.0f;
+ const float magicshit = 2.5f;
+
+ float facingAngVel = (mVehicle->mSimStateArticulated->GetAngularVelocity()).DotProduct(mVehicle->mVehicleFacing);
+
+ // a positive value means tipping left.
+ //
+ // so.......
+ // we want to apply the torque if the transverse.dot.worldup is positive
+
+ // or... if tipping right and transverse.dot.worldup is negative
+
+ if( (facingAngVel > 0.0f && mVehicle->mVehicleTransverse.DotProduct(up) > 0.0f) ||
+ (facingAngVel < 0.0f && mVehicle->mVehicleTransverse.DotProduct(up) < 0.0f) )
+ {
+ rmt::Vector torque = mVehicle->mVehicleFacing;
+
+
+ torque.Scale(-1.0f * magicshit * mVehicle->mDesignerParams.mDpMass * facingAngVel);
+
+ sim::PhysicsObject* phobj = (sim::PhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject());
+ rAssert(phobj);
+
+ phobj->AddTorque(torque);
+ }
+ }
+
+ }
+ if(mVehicle->mAirBorn)
+ {
+ doit = true;
+ }
+
+
+ if(doit)
+ {
+
+ if(!(mVehicle->mWeebleOn))
+ {
+
+ // weeble
+ rmt::Vector weeble = mVehicle->mCMOffset;
+
+ //static float weeblemagic = 1.0f;
+ float weeblemagic = 1.0f;
+
+ // scale by point in range
+ // set range of 0 to 1 for start to limit
+ /*
+ float range = 1.0f - ((tip - end) / (start - end));
+
+ if(range < 0.0f)
+ {
+ range = 0.0f;
+ //rAssert(0);
+ }
+ if(range > 1.0f)
+ {
+ range = 1.0f;
+ }
+
+ float modifier = weeblemagic * range;
+ */
+ //weeble.y -= modifier;
+
+ //weeble.y -= weeblemagic;
+ weeble.y += mVehicle->mDesignerParams.mDpWeebleOffset;
+
+ //weeble.y -= 3.0f;
+
+ rmt::Vector current = ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->GetExternalCMOffset();
+
+ if(current.Equals(weeble))
+ {
+ int stophere = 1;
+ }
+ else
+ {
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(weeble);
+ }
+
+ mVehicle->mWeebleOn = true;
+ }
+
+ }
+ else
+ {
+ if(mVehicle->mWeebleOn)
+ {
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mCMOffset);
+ }
+ mVehicle->mWeebleOn = false;
+ }
+
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::PreUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::PreUpdate()
+{
+ //mVehicle->CalculateSuspensionLocationAndVelocity();
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::Update(float dt)
+{
+
+
+ // recall
+ // this is the set of function calls to physicsvehicle from vehicle in the old
+ // system
+
+ UseWheelTerrainCollisionInfo(dt);
+
+ CorrectWheelYPositions(); // old name: PreUpdate(dt);
+
+ mVehicle->CalculateSuspensionLocationAndVelocity();
+
+
+
+ //mVehicle->mSimStateArticulated->SetControl(sim::simSimulationCtrl); -- move this into SelfRestTest
+
+
+ //bool rest = mVehicle->SelfRestTest();
+
+
+ bool rest = false;
+ rest = mVehicle->SelfRestTest();
+
+
+ if(mVehicle->mSimStateArticulated->GetControl() != sim::simAICtrl)
+ {
+
+
+ // TODO - this call, and UpdateJointState, should be in the same place
+ // mVehicle->mSimStateArticulated->StoreJointState(dt);
+
+
+ // the update guts from physicsvehicle
+ //
+ // kind of a fucking mess
+ //mVehicle->CalculateSuspensionLocationAndVelocity();
+
+ SetForceApplicationPoints();
+
+
+ ApplySuspensionForces(dt, rest);
+
+ if(!(mVehicle->mAirBorn) && !rest)
+ {
+ ApplyControllerForces2(dt);
+ }
+ else
+ {
+ // at least do this
+ mVehicle->mBurnoutLevel = 0.0f;
+
+ }
+
+ if(GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_NO_TOP_SPEED) && mVehicle->mVehicleType == VT_USER)
+ {
+ // no drag for you!
+ }
+ else
+ {
+ if(!(mVehicle->mSteeringWheelsOutOfContact) && !(mVehicle->mDoSpeedBurst) && !(mVehicle->mDoingJumpBoost))
+ {
+ ApplyDragForce();
+ }
+ }
+
+ if(!(mVehicle->mDoingJumpBoost))
+ {
+ ApplyRollingFriction(); // move low speed portion of this into a separate function to follow update...
+ }
+
+ //TipTest();
+
+ if(mVehicle->mAirBorn && mVehicle->mVehicleType == VT_USER )
+ {
+ ApplyAngularDrag();
+ }
+
+ Weeble();
+
+ if(mVehicle->mVehicleType == VT_USER)
+ {
+ //HighSpeedInstabilityTest();
+ }
+
+ if(mVehicle->mVehicleType == VT_USER)
+ {
+ StuckOnSideTest(dt);
+ }
+
+
+ if(mVehicle->mAirBorn)// && mVehicle->mVehicleType == VT_USER)
+ {
+ DurangoStyleStabilizer(dt);
+ }
+
+
+
+ //
+ // ****
+ //
+ // THE Update
+ //
+ // ****
+
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->Update(dt);
+
+ //LowSpeedTest();
+
+ //rest = mVehicle->SelfRestTest();
+ }
+
+
+
+ //Update(dt);
+ //PostUpdate(dt);
+
+ // maybe for first pass implementation, implement methods with the same name?
+
+
+ // ???????
+ //
+ // do the pose driver updates here!!!! or back in vehicle <- for some reason
+ // the second way seems much nicer to me.
+
+
+
+ // the net effect on Vehicle result when this function returns is to have
+ // modified the joint-space y values in the wheel joints to correct
+ // the wheel positions, and to have applied forces and torques to the
+ // simstate and had it update itself so there is a new world space transform
+ // AND make sure the world space transform of joint 0 is in sync with this
+
+ // also - make sure wheels have enough info for suspensionjointdriver to
+ // do it's thing
+ //
+ // ie - cumulative rot
+ // wheel turn angle
+ // that's about it?
+
+ // are we gonna have to make physicslocomotion a friend of the Vehicle?
+ // is that the correct thing to do - make all the locomotions friends??
+
+
+
+}
+
+
+
+//=============================================================================
+// PhysicsLocomotion::DurangoStyleStabilizer
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::DurangoStyleStabilizer(float dt)
+{
+ // assume airborn
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = mVehicle->mVehicleUp.DotProduct(up);
+
+ // we want a fix torque proportional to 1.0 - tip.
+
+ // 'direction' of the toruqe is vehicleup crossed into world up
+ float cos10 = 0.9848f;
+ if(tip < cos10)
+ {
+
+
+ rmt::Vector fixTorque;
+ fixTorque.CrossProduct(mVehicle->mVehicleUp, up);
+
+ // need this?
+ fixTorque.NormalizeSafe();
+
+ const float hackmagicshit = 200.0f;
+
+ float fixscale = 1.0f - tip;
+
+ fixTorque.Scale(hackmagicshit * mVehicle->mDesignerParams.mDpMass * fixscale);
+
+ sim::PhysicsObject* phobj = (sim::PhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject());
+ rAssert(phobj);
+
+ phobj->AddTorque(fixTorque);
+
+
+ // try some damping too
+ const float dampingShit = 7.0f;
+
+ rmt::Vector angularDragForce = mVehicle->mSimStateArticulated->GetAngularVelocity();
+ angularDragForce.Scale(-1.0f * dampingShit * mVehicle->mDesignerParams.mDpMass);
+
+ phobj->AddTorque(angularDragForce);
+
+
+ }
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::StuckOnSideTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::StuckOnSideTest(float dt)
+{
+ // only for user vehicle in physics locomotion
+
+ const float lowspeed = 1.0f;
+ if(mVehicle->mSpeed < lowspeed)
+ {
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = mVehicle->mVehicleUp.DotProduct(up);
+
+ //float cos85 = 0.0872f;
+ float test = 0.2f;
+ if(tip < test)
+ {
+ //static float magicshit = 1.0f;
+ const float magicshit = 130.0f;
+
+ float side = up.DotProduct(mVehicle->mVehicleTransverse);
+ if(side > 0.0f)
+ {
+ // lying on it's left side - right side pointint to sky
+ //if(mVehicle->mUnmodifiedInputWheelTurnAngle > 0.0f)
+ if(mVehicle->mUnmodifiedInputWheelTurnAngle < 0.0f)
+ {
+ // apply some torque
+ rmt::Vector torque = mVehicle->mVehicleFacing;
+ //torque.Scale(mVehicle->mUnmodifiedInputWheelTurnAngle * -1.0f * mVehicle->mDesignerParams.mDpMass * magicshit);
+ torque.Scale(mVehicle->mUnmodifiedInputWheelTurnAngle * 1.0f * mVehicle->mDesignerParams.mDpMass * magicshit);
+
+ sim::PhysicsObject* phobj = (sim::PhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject());
+ rAssert(phobj);
+
+ phobj->AddTorque(torque);
+
+ }
+
+
+ }
+ else
+ {
+ // lying on it's right side - left side pointint to sky
+ //if(mVehicle->mUnmodifiedInputWheelTurnAngle < 0.0f)
+ if(mVehicle->mUnmodifiedInputWheelTurnAngle > 0.0f)
+ {
+ // apply some torque
+ rmt::Vector torque = mVehicle->mVehicleFacing;
+ //torque.Scale(mVehicle->mUnmodifiedInputWheelTurnAngle * -1.0f * mVehicle->mDesignerParams.mDpMass * magicshit);
+ torque.Scale(mVehicle->mUnmodifiedInputWheelTurnAngle * 1.0f * mVehicle->mDesignerParams.mDpMass * magicshit);
+
+ sim::PhysicsObject* phobj = (sim::PhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject());
+ rAssert(phobj);
+
+ phobj->AddTorque(torque);
+
+ }
+
+
+ }
+
+ }
+
+ }
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::TipTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::TipTest()
+{
+
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = mVehicle->mVehicleUp.DotProduct(up);
+
+ float cos45 = 0.7071f;
+ float cos80 = 0.17365f;
+ float cos65 = 0.4226f;
+ // need to figure out if the angular velocity is trying to tip us over more, or right us
+
+ float start = cos45;
+ float end = cos65;
+
+ //static float tipRecoverPercentage = 75.0f;
+ float tipRecoverPercentage = 75.0f;
+
+ if(tip < start && tip > end)
+ {
+ // set range of 0 to 1 for start to limit
+ float range = 1.0f - ((tip - end) / (start - end));
+
+
+ float lean = mVehicle->mVehicleTransverse.DotProduct(up);
+
+ if(lean > cos45)
+ {
+ // tipping left
+
+ rmt::Vector& angularVel = mVehicle->mSimStateArticulated->GetAngularVelocity();
+
+ float proj = angularVel.DotProduct(mVehicle->mVehicleFacing);
+
+ //if(proj > 0.0f)
+ if(proj > 1.0f)
+ {
+ // the velocity is trying to tip us more
+
+ //rmt::Vector recover = mVehicle->mVehicleFacing;
+ //recover.Scale(-1.0f * proj * tipRecoverPercentage);
+
+ //angularVel.Add(recover);
+
+ //angularVel.Scale(tipRecoverPercentage);
+ //angularVel.Scale(1.0f - range);
+ angularVel.Scale(0.0f);
+
+ //rmt::Vector recover = mVehicle->mVehicleFacing;
+ //recover.Scale(-1.0f * proj * tipRecoverPercentage * mVehicle->mDesignerParams.mDpMass * range);
+
+ //mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->AddTorque(recover);
+
+
+
+ }
+ }
+ else if(lean < -cos45)
+ {
+
+ //rmt::Vector& linearVel = mVehicle->mSimStateArticulated->GetLinearVelocity();
+ rmt::Vector& angularVel = mVehicle->mSimStateArticulated->GetAngularVelocity();
+
+ float proj = angularVel.DotProduct(mVehicle->mVehicleFacing);
+
+
+ // tipping over right
+ //if(proj < 0.0f)
+ if(proj < -1.0f)
+ {
+
+ //rmt::Vector recover = mVehicle->mVehicleFacing;
+ //recover.Scale(-1.0f * proj * tipRecoverPercentage);
+
+ //angularVel.Add(recover);
+
+ //angularVel.Scale(tipRecoverPercentage);
+ //angularVel.Scale(1.0f - range);
+ angularVel.Scale(0.0f);
+
+ //rmt::Vector recover = mVehicle->mVehicleFacing;
+ //recover.Scale(-1.0f * proj * tipRecoverPercentage * mVehicle->mDesignerParams.mDpMass * range);
+
+ //mVehicle->((ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1)))->AddTorque(recover);
+
+ }
+
+ }
+
+ // else don't do shit
+
+
+
+
+
+
+ // what is our tendency to hold and recover from tipping?
+
+
+ }
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::PostUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::PostUpdate()
+{
+ // this whole method is kind of annyoing
+ //
+ // TODO
+ //??? shoudl be in Vehicle
+
+ mVehicle->mTransform = mVehicle->mSimStateArticulated->GetTransform(-1);
+
+
+ mVehicle->mVelocityCM = mVehicle->mSimStateArticulated->GetLinearVelocity();
+ mVehicle->mSpeed = mVehicle->mVelocityCM.Magnitude();
+ mVehicle->mPercentOfTopSpeed = mVehicle->mSpeed / (mVehicle->mDesignerParams.mDpTopSpeedKmh / 3.6f);
+ if(mVehicle->mPercentOfTopSpeed > 1.0f)
+ {
+ mVehicle->mPercentOfTopSpeed = 1.0f;
+ }
+
+ // Roll up the terrain type and interior flag;
+ // As soon as we get two tires or more of a terrain type, that's the type we'll consider the car on.
+ // This isn't very exact since the rear two tires will have precidence over the other tires.
+ int terrainCounts = 0;
+ int interiorCount = 0;
+ eTerrainType newType = TT_Road;
+ for( int i = 0; i < 4; i++ )
+ {
+ int index = i ^ 2; // We do this to change i from 0, 1, 2, 3 into 2, 3, 0, 1 so that the rear wheels are processed last.
+ interiorCount += mTerrainIntersectCache[ index ].mInteriorTerrain ? 1 : 0;
+//? Do we want to mask out roads? int mask = ( 1 << (int)mTerrainIntersectCache[ index ].mTerrainType ) & ~(int)TT_Road; // Mask out roads so everything else takes precidence over it.
+ int mask = 1 << (int)mTerrainIntersectCache[ index ].mTerrainType;
+ if( ( terrainCounts & mask ) )
+ {
+ // We've already got one, so this is number two.
+ newType = mTerrainIntersectCache[ index ].mTerrainType;
+ }
+ else
+ {
+ terrainCounts |= mask;
+ }
+ }
+ mVehicle->mTerrainType = newType;
+ mVehicle->mInterior = ( interiorCount >= 2 );
+
+}
+
+//------------------------------------------------------------------------
+void PhysicsLocomotion::CorrectWheelYPositions()
+{
+
+
+ poser::Pose* pose = mVehicle->mPoseEngine->GetPose();
+
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ // we don't want yOffset, we want the correction value!!
+
+ // now just use mYOffset
+ //float yOffset = mWheels[i]->mYOffset; fuck no!!!
+ Wheel* wheel = mVehicle->mWheels[i];
+
+ float yCorrectionValue = wheel->GetYCorrectionValue();
+
+ poser::Joint* joint = pose->GetJoint(mVehicle->mWheelToJointIndexMapping[i]);
+ rmt::Vector trans = joint->GetObjectTranslation();
+
+ // these trans.y should still be at the bottom of their suspension range
+
+ // new test so wheels don't hang so low
+ if(wheel->mWheelInCollision)
+ {
+ trans.y += yCorrectionValue;
+ }
+ else if(mVehicle->mAirBorn) // add this here to remove the snap/bounce when turning hard and two wheels come out of contact
+ {
+ trans.y += wheel->mLimit;
+ }
+
+ // test
+ //trans.y += mVehicle->mDesignerParams.mDpSuspensionYOffset;
+
+ joint->SetObjectTranslation(trans);
+
+ wheel->ResolveOffset();
+
+ }
+
+
+}
+
+
+//------------------------------------------------------------------------
+// temp...
+//------------------------------------------------------------------------
+inline void TempGetTerrainIntersects( rmt::Vector& trans,
+ float radius,
+ bool& foundtri,
+ rmt::Vector& closestTriNormal,
+ rmt::Vector& closestTriPosn,
+ bool& foundplane,
+ rmt::Vector& planeNormal,
+ rmt::Vector& planePosn)
+{
+ const float y = 3.0f;
+
+
+ foundtri = false;
+ foundplane = true;
+ planeNormal.Set(0.0f, 1.0f, 0.0f);
+ planePosn.x = trans.x;
+ planePosn.y = y;
+ planePosn.z = trans.z;
+
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::FetchWheelTerrainCollisionInfo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::FetchWheelTerrainCollisionInfo()
+{
+ poser::Pose* pose = mVehicle->mPoseEngine->GetPose();
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ BEGIN_PROFILE("PreInter")
+ Wheel* wheel = mVehicle->mWheels[i];
+
+ // for each wheel, based on the world xz, get a y and a normal, ..... and a ground type
+
+ //? could also do based on suspension points, no?
+ // I guess this is more accurate
+
+ poser::Joint* joint = pose->GetJoint(mVehicle->mWheelToJointIndexMapping[i]);
+ rmt::Vector trans = joint->GetWorldTranslation();
+
+
+ rmt::Vector closestTriNormal, closestTriPosn;
+ rmt::Vector planeNormal, planePosn;
+ int terrainType;
+ //bool bFoundTri, bFoundPlane;
+
+ mFoundTri[i] = false;
+ mFoundPlane[i] = false;
+ END_PROFILE("PreInter")
+
+ BEGIN_PROFILE("GetIntersectManager()->FindIntersection")
+
+ terrainType = GetIntersectManager()->FindIntersection( trans,
+ mFoundPlane[i],
+ planeNormal,
+ planePosn );
+
+
+ /*
+ TempGetTerrainIntersects( trans,
+ wheel->mRadius,
+ mFoundTri[i],
+ closestTriNormal,
+ closestTriPosn,
+ mFoundPlane[i],
+ planeNormal,
+ planePosn);
+ */
+
+ END_PROFILE("GetIntersectManager()->FindIntersection")
+
+
+ BEGIN_PROFILE("PostInter")
+ // fill in pieces of cache
+ if(mFoundPlane[i])
+ {
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ if(up.DotProduct(planeNormal) < 0.0f)
+ {
+ //rAssert(0);
+ }
+
+
+ mTerrainIntersectCache[i].planePosn = planePosn;
+ mTerrainIntersectCache[i].planeNorm = planeNormal;
+
+ mIntersectNormalUsed[i] = mTerrainIntersectCache[i].planeNorm;
+ }
+ else
+ {
+ // no plane
+ //rAssert(0);
+ }
+
+
+
+
+ //Devin says this is not used. Cleaning up the warnings.
+// if(mFoundTri[i])
+// {
+// mTerrainIntersectCache[i].closestTriPosn = closestTriPosn;
+// mTerrainIntersectCache[i].closestTriNormal = closestTriNormal;
+// }
+
+ mTerrainIntersectCache[ i ].mTerrainType = (eTerrainType)( terrainType & ~0x80 );
+ mTerrainIntersectCache[ i ].mInteriorTerrain = ( terrainType & 0x80 ) == 0x80;
+
+ // stuff in value for later comparison
+ //mIntersectNormalUsed[i] = mTerrainIntersectCache[i].planeNorm;
+ END_PROFILE("PostInter")
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::UseWheelTerrainCollisionInfo
+//=============================================================================
+// Description: Comment
+//
+// per-wheel, calculate and call Wheel::SetYOffsetFromCurrentPosition
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::UseWheelTerrainCollisionInfo(float dt)
+{
+
+ poser::Pose* pose = mVehicle->mPoseEngine->GetPose();
+
+ // take the highest value for this from all 4 wheels
+ float bottomOutFix = 0.0f;
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+
+ // make copies 'cause we may want to modify local result, but not cached value
+ bool foundPlane = mFoundPlane[i];
+ bool foundTri = mFoundTri[i];
+
+ float fixHeight = 0.0f;
+ bool thisWheelInCollision = false;
+ rmt::Vector fixHeightNormal(0.0f, 1.0f, 0.0f);
+
+ float fixAlongSuspensionAxis = 0.0f;
+
+ Wheel* wheel = mVehicle->mWheels[i];
+ poser::Joint* joint = pose->GetJoint(mVehicle->mWheelToJointIndexMapping[i]);
+ rmt::Vector trans = joint->GetWorldTranslation();
+
+
+ if(!mFoundPlane[i] && !mFoundTri[i])
+ {
+ // we didn't get any good data back, so we need
+ // to use last cached value
+ //rAssert(0); // curious to see if we hit this - yes, a lot
+
+ // just use plane y
+ foundPlane = true;
+ }
+
+ if(foundPlane)
+ {
+ float y = mTerrainIntersectCache[i].planePosn.y;
+
+ // first check
+ // the pathologically bad case:
+ if(mVehicle->mSuspensionWorldSpacePoints[i].y < y)
+ {
+ // this means the suspension rest point on the car is below the terrain
+ // this is fucking bad.
+
+ // TODO: take this out?
+ //rAssert(0);
+
+ // ?? actually need to do anything here?
+
+ // TODO - bump car up?
+
+ }
+
+ //else
+
+ {
+ // hopefully sane case
+
+ // in planePosn, we only really care about the y
+
+ float penetratingDepth = y - (trans.y - wheel->mRadius);
+
+ if(penetratingDepth > 0.0f)
+ {
+ thisWheelInCollision = true;
+ fixHeight = penetratingDepth;
+
+ // fixHeight is going pure up
+ fixHeightNormal.Set(0.0f, 1.0f, 0.0f);
+
+ fixAlongSuspensionAxis = fixHeight / rmt::Fabs( (mVehicle->mVehicleUp).DotProduct(fixHeightNormal));
+
+ }
+ }
+
+ }
+
+
+ // TODO! revisit this!
+
+ if(0)//foundTri)
+ {
+ // calculate fixAlongSuspensionAxis based on this also, and if it's greater than above, use it instead
+
+ rmt::Vector penetrationVector;
+ //penetrationVector.Sub(trans, mWheelTerrainCollisionPoints[i]);
+
+ penetrationVector = trans;
+ penetrationVector.Sub(mTerrainIntersectCache[i].closestTriPosn);
+
+ // TODO - is this safe?
+ float penMag = penetrationVector.Magnitude();
+
+ bool checkBump = false;
+
+ if(trans.y < mTerrainIntersectCache[i].closestTriPosn.y)
+ {
+ // definately need to fix!
+ fixHeight = wheel->mRadius + penMag;
+ thisWheelInCollision = true;
+ fixHeightNormal = mTerrainIntersectCache[i].closestTriNormal;
+
+ float tempFixAlongSuspensionAxis = fixHeight / rmt::Fabs( (mVehicle->mVehicleUp).DotProduct(fixHeightNormal));
+
+ if(tempFixAlongSuspensionAxis > fixAlongSuspensionAxis)
+ {
+ fixAlongSuspensionAxis = tempFixAlongSuspensionAxis;
+ }
+
+ checkBump = true;
+
+ }
+ else if(penMag < wheel->mRadius) // normal case
+ {
+ fixHeight = wheel->mRadius - penMag;
+ thisWheelInCollision = true;
+ fixHeightNormal = mTerrainIntersectCache[i].closestTriNormal;
+
+ float tempFixAlongSuspensionAxis = fixHeight / rmt::Fabs( (mVehicle->mVehicleUp).DotProduct(fixHeightNormal));
+
+ if(tempFixAlongSuspensionAxis > fixAlongSuspensionAxis)
+ {
+ fixAlongSuspensionAxis = tempFixAlongSuspensionAxis;
+ }
+
+ checkBump = true;
+
+ }
+
+ if(checkBump)
+ {
+
+ // if this is too horizontal, we need to deal with it specially
+ float sin10 = 0.1736f;
+ float sin20 = 0.342f;
+ float sin30 = 0.5f;
+
+ // TODO - what angle?
+ if(rmt::Fabs(fixHeightNormal.DotProduct(mVehicle->mVehicleUp)) < sin20)// && fixHeight > wheel->mRadius)
+ {
+ // just bump the whole car back by penMag?
+
+ rmt::Vector bump = fixHeightNormal;
+ bump.NormalizeSafe();
+ bump.Scale(fixHeight);
+
+ // TODO - true or false or tapered down or what?
+ //mVehicle->mSimStateArticulated->DynamicPositionAdjustment(bump, dt, true);
+ //mVehicle->mSimStateArticulated->DynamicPositionAdjustment(bump, dt, false);
+
+ mVehicle->mBottomedOutThisFrame = true;
+
+ continue;
+
+
+ }
+ }
+ }
+
+
+ if(thisWheelInCollision)
+ {
+
+ // perhaps this method should be renamed
+ // it sets the wheelInCollision flag to true!
+ float bottomedOut = wheel->SetYOffsetFromCurrentPosition(fixAlongSuspensionAxis);
+
+ if(bottomedOut > bottomOutFix)
+ {
+ bottomOutFix = bottomedOut;
+ wheel->mWheelBottomedOutThisFrame = true; // not sure if we'll use yet.
+
+ //char buffy[128];
+ //sprintf(buffy, "wheel %d bottomed out this frame\n", i);
+ //rDebugPrintf(buffy);
+ }
+
+ }
+
+ } // end for(i
+
+
+ if(bottomOutFix > 0.0f)
+ {
+
+ // this is the amount the suspension couldn't deal with.
+ //
+ // what to do?
+ // in srr1 we just bumped the whole chassis up by this amount - seemed to work fairly well actually.
+ //rmt::Vector inDeltaPos = mVehicle->mVehicleUp;
+
+ // Note!
+ // we hav to be careful how to deal with this 'cause we can get some weird ass results that make
+ // the car go flying after seemingly minor crashes
+
+ // hack, but might work well - only correct the vehicle straight up!
+
+ //rmt::Vector inDeltaPos = fixHeightNormal;
+
+ rmt::Vector inDeltaPos;
+
+ inDeltaPos = GetWorldPhysicsManager()->mWorldUp;
+
+
+ // sanity test?
+ if(bottomOutFix > (mVehicle->mWheels[0]->mRadius * 0.5f))
+ {
+ bottomOutFix = (mVehicle->mWheels[0]->mRadius * 0.5f);
+ }
+
+
+
+
+ if(inDeltaPos.y > 0.0f) // this test is pointless right now
+ {
+
+ inDeltaPos.Scale(bottomOutFix);
+
+ // TODO - revisit this
+
+
+
+///you are here
+//take this out - see what f1 car does
+
+// no this was not the problem.
+
+// get kevin to raise the bv
+
+ mVehicle->mSimStateArticulated->DynamicPositionAdjustment(inDeltaPos, dt, false);
+
+
+
+ // ?
+ // how 'bout just applying some impulse to the sucker?
+
+ /*
+ if(0)//mVehicle->mOkToCrashLand)
+ {
+ rmt::Matrix groundPlaneMatrix = mVehicle->mGroundPlaneSimState->GetTransform(-1);
+ rmt::Vector groundPlaneUp = groundPlaneMatrix.Row(2); // recall - ground plane is oriented weird
+
+ rmt::Vector& linearVel = mVehicle->mSimStateArticulated->GetLinearVelocity();
+
+ static float test = 10.0f;
+ groundPlaneUp.Scale(test);
+
+ linearVel.Add(groundPlaneUp);
+
+ mVehicle->mOkToCrashLand = false;
+
+ }
+ */
+
+ }
+
+ mVehicle->mBottomedOutThisFrame = true;
+ }
+
+
+
+}
+
+
+
diff --git a/game/code/worldsim/redbrick/physicslocomotion.h b/game/code/worldsim/redbrick/physicslocomotion.h
new file mode 100644
index 0000000..446e42a
--- /dev/null
+++ b/game/code/worldsim/redbrick/physicslocomotion.h
@@ -0,0 +1,135 @@
+/*===========================================================================
+ physicslocomotion.h
+
+ created April 24, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#ifndef _PHYSICSLOCOMOTION_H
+#define _PHYSICSLOCOMOTION_H
+
+#include <worldsim/redbrick/vehiclelocomotion.h>
+#include <render/intersectmanager/intersectmanager.h> // For the terrain type enumeration.
+#include <radmath/radmath.hpp>
+
+class Vehicle;
+class Wheel;
+
+class PhysicsLocomotion : public VehicleLocomotion
+{
+public:
+
+ PhysicsLocomotion(Vehicle* vehicle);
+ ~PhysicsLocomotion();
+
+ virtual void PreSubstepUpdate();
+ virtual void PreCollisionPrep(bool firstSubstep);
+
+ virtual void UpdateVehicleGroundPlane();
+
+ virtual void PreUpdate();
+ virtual void Update(float dt);
+ virtual void PostUpdate();
+
+
+ // call this from Vehicle::SetTransform
+ void SetTerrainIntersectCachePointsForNewTransform();
+
+ virtual void CompareNormalAndHeight(int index, rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint);
+
+//private: for the purposes of debug printout
+
+ void MoveWheelsToBottomOfSuspension();
+
+ void FetchWheelTerrainCollisionInfo();
+ void UseWheelTerrainCollisionInfo(float dt);
+
+ void TipTest();
+ void ApplyAngularDrag();
+
+ void StuckOnSideTest(float dt);
+
+ void DurangoStyleStabilizer(float dt);
+
+
+ // delete all this crap soon
+
+ //float mWheelTerrainCollisionFixDepth[4];
+ //rmt::Vector mWheelTerrainCollisionPoints[4];
+ //rmt::Vector mWheelTerrainCollisionNormals[4];
+ //bool mGoodWheelTerrainCollisionValue[4];
+
+ bool mFoundTri[4];
+ bool mFoundPlane[4];
+
+
+ void CorrectWheelYPositions(); // used to be preupdate
+
+ void SetForceApplicationPoints();
+ rmt::Vector mForceApplicationPoints[4];
+ rmt::Vector mSuspensionPointVelocities[4];
+
+ void ApplySuspensionForces(float dt, bool atrest);
+ float mCachedSuspensionForceResults[4];
+ float CalculateSuspensionForce(Wheel* wheel, float suspensionPointYVelocity, float dt, float& forceToUse);
+
+ void ApplyControllerForces2(float dt);
+ void GasForce2(rmt::Vector& forceResult, int wheelNum, float dt);
+ void BrakeForce2(rmt::Vector& forceResult, int wheelNum, float dt);
+ void ReverseForce2(rmt::Vector& forceResult, int wheelNum);
+ void SteeringForce2(rmt::Vector& forceResult, int wheelNum, float dt);
+ //void LimitTireForce(rmt::Vector& totalForce, int wheelNum);
+ void TestControllerForces(rmt::Vector* totalForce);
+ void SetSkidLevel();
+
+ void UpdateSteeringForce(float dt);
+ float mCurrentSteeringForce;
+
+ void ApplyTerrainFriction(rmt::Vector* totalForce);
+
+
+ void TestSteeringForce(rmt::Vector& force, int index);
+
+ void EBrakeEffect(rmt::Vector& forceResult, int wheelNum);
+ void DoughnutTest();
+
+ void ApplyDragForce();
+ void Weeble();
+
+ void HighSpeedInstabilityTest();
+
+ void ApplyRollingFriction();
+
+ void LowSpeedTest();
+
+ // just for convenience in internal methods
+ Vehicle* mVehicle;
+
+ struct TerrainIntersectCache
+ {
+ rmt::Vector closestTriPosn;
+ rmt::Vector closestTriNormal;
+ rmt::Vector planePosn;
+ rmt::Vector planeNorm;
+ eTerrainType mTerrainType;
+ bool mInteriorTerrain;
+ };
+
+ TerrainIntersectCache mTerrainIntersectCache[4]; // is this wasting too much memory?
+
+ // need this because if we're driving on a static collision volume the terrain normal could be way off!
+ rmt::Vector mIntersectNormalUsed[4];
+
+ friend class Vehicle;
+
+
+
+};
+
+
+#endif // _PHYSICSLOCOMOTION_H
diff --git a/game/code/worldsim/redbrick/physicslocomotioncontrollerforces.cpp b/game/code/worldsim/redbrick/physicslocomotioncontrollerforces.cpp
new file mode 100644
index 0000000..00ae0aa
--- /dev/null
+++ b/game/code/worldsim/redbrick/physicslocomotioncontrollerforces.cpp
@@ -0,0 +1,1686 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: physicslocomotioncontrollerforces.cpp
+//
+// Description: moved some methods into a different cpp file 'cause it was getting
+// too big
+//
+// History: May 13, 2001 + Created -- gmayer
+//
+//=============================================================================
+
+
+//========================================
+// System Includes
+//========================================
+
+#include <simcommon/simstate.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <simcommon/simulatedobject.hpp>
+#include <simphysics/articulatedphysicsobject.hpp>
+#include <simphysics/physicsobject.hpp>
+#include <simcommon/simenvironment.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <simcollision/collisionvolume.hpp>
+
+#include <raddebug.hpp>
+
+#include <gameflow/gameflow.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/avatarmanager.h>
+
+
+#include <cheats/cheatinputsystem.h>
+
+#include <poser/pose.hpp>
+#include <poser/poseengine.hpp>
+#include <poser/posedriver.hpp>
+
+
+//========================================
+// Project Includes
+//========================================
+
+#include <worldsim/redbrick/physicslocomotion.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/wheel.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+
+#ifdef WORKING_ON_GREG_PHIZTEST_RIGHT_NOW
+#include "../../../../users/greg/phiztest/code/worldsim/redbrick/phizsim.h" // just for a temp hack using world up
+#else
+#include <worldsim/worldphysicsmanager.h>
+#endif
+
+#include <render/IntersectManager/IntersectManager.h>
+
+using namespace sim;
+
+
+// test of terrain friction
+ // enum value
+const float TF_Road = 1.0f; // 0
+const float TF_Grass = 0.7f; // 1
+const float TF_Sand = 0.6f; // 2
+const float TF_Gravel = 0.6f; // 3
+const float TF_Water = 0.6f; // 4
+const float TF_Wood = 1.0f; // 5
+const float TF_Metal = 1.0f; // 6
+const float TF_Dirt = 0.6f; // 7
+
+
+
+
+
+
+//=============================================================================
+// PhysicsLocomotion::ApplyControllerForces2
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::ApplyControllerForces2(float dt)
+{
+ // for each wheel, calculate the total requested force, and then
+ // chop it based on the force circle?
+
+ mVehicle->mBurnoutLevel = 0.0f;
+
+ int i;
+ rmt::Vector totalForce[4];
+ for(i = 0; i < 4; i++)
+ {
+ totalForce[i].Clear();
+
+
+ if(/*mVehicle->mWheels[i]->mWheelInCollision &&*/ !(mVehicle->mWheels[i]->mWheelBottomedOutThisFrame))
+ {
+
+ // TODO
+ // only do loop if wheel in collision?
+ // else reset some shit like the wheel state
+
+ // TODO
+ // how about wheel bottomed out this frame?
+ // only affects steering? or all?
+
+ //rmt::Vector totalForce(0.0f, 0.0f, 0.0f);
+
+ GasForce2(totalForce[i], i, dt);
+ // TODO - make sure you set skid level and renderspinuprate and shit like that.
+
+ BrakeForce2(totalForce[i], i, dt);
+ // TODO - somewhere else add code to bring car to a standstill
+
+ ReverseForce2(totalForce[i], i);
+
+ EBrakeEffect(totalForce[i], i);
+
+ SteeringForce2(totalForce[i], i, dt);
+
+
+
+ }
+ //else
+ {
+ // reset wheel state?
+ }
+
+ }
+
+ // DoughnutTest(); I think we need a state for this.
+
+
+ // test if we should change vehicle state
+ // TODO - test for airborn etc.. before calling this?
+
+ TestControllerForces(totalForce); // <- mVehicleState only changed in here!
+
+ // new - Mar 12, 2003
+ //
+ // use state to set "target" steering force.
+ //
+ // move towards target based on time rate
+ //
+ //
+
+ // next step - make let-off gas get you out of slip state
+
+ // how does gasreducinggrip thing fit into all of this?
+
+ // next step - make skid level 0 to 1 based on have we gotten to our target level yet?
+
+ //UpdateSteeringForce(dt);
+
+
+ // not doing this anymore - only step is really to set good skid level
+
+
+
+ SetSkidLevel();
+
+ // TODO - actually limit locomotive and other forces??????
+
+ // cheap and easy test of terrain friction:
+ //ApplyTerrainFriction(totalForce);
+
+
+ for(i = 0; i < 4; i++)
+ {
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->AddForce(totalForce[i], &(mForceApplicationPoints[i]));
+ }
+}
+
+
+
+//=============================================================================
+// PhysicsLocomotion::UpdateSteeringForce
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+/*
+
+ // I don't think plum wants this now
+
+void PhysicsLocomotion::UpdateSteeringForce(float dt)
+{
+ // default changerate: 50 units / second
+ //static float changeRate = 10.0f;
+ //static float changeRate = 5.0f;
+ static float changeRate = 1.0f;
+
+ float changethisframe = changeRate * dt;
+
+ float target = 80.0f; // just some default - should never use
+
+
+ // if we are not VT_USER, switch immediately.
+
+
+ if(mVehicle->mVehicleState == VS_NORMAL)
+ {
+ // "target" - mVehicle->mDesignerParams.mDpTireLateralResistanceNormal;
+
+ target = mVehicle->mDesignerParams.mDpTireLateralResistanceNormal;
+
+ }
+ else if(mVehicle->mVehicleState == VS_EBRAKE_SLIP)
+ {
+ // "target" - mVehicle->mDesignerParams.mDpTireLateralResistanceSlip
+
+ target = mVehicle->mDesignerParams.mDpTireLateralResistanceSlip;
+
+ }
+ else
+ {
+ // regular slip
+ // "target" - mVehicle->mDesignerParams.mDpTireLateralResistanceSlipNoEBrake
+
+ target = mVehicle->mDesignerParams.mDpTireLateralResistanceSlipNoEBrake;
+
+ }
+
+ if(mCurrentSteeringForce > target)
+ {
+ if( (mCurrentSteeringForce - changethisframe) > target)
+ {
+ mCurrentSteeringForce -= changethisframe;
+ }
+ else
+ {
+ mCurrentSteeringForce = target;
+ }
+ }
+ else if(mCurrentSteeringForce < target)
+ {
+ if((mCurrentSteeringForce + changethisframe) < target)
+ {
+ mCurrentSteeringForce += changethisframe;
+ }
+ else
+ {
+ mCurrentSteeringForce = target;
+ }
+ }
+
+
+
+
+}
+
+*/
+
+//=============================================================================
+// PhysicsLocomotion::ApplyTerrainFriction
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector* totalForce)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::ApplyTerrainFriction(rmt::Vector* totalForce)
+{
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ switch (mTerrainIntersectCache[i].mTerrainType)
+ {
+
+ case TT_Road:
+ totalForce[i].Scale(TF_Road);
+ break;
+
+ case TT_Grass:
+ totalForce[i].Scale(TF_Grass);
+ break;
+
+ case TT_Dirt:
+ totalForce[i].Scale(TF_Dirt);
+ break;
+
+ case TT_Water:
+ totalForce[i].Scale(TF_Water);
+ break;
+
+ case TT_Gravel:
+ totalForce[i].Scale(TF_Gravel);
+ break;
+
+ case TT_Wood:
+ totalForce[i].Scale(TF_Wood);
+ break;
+
+ case TT_Sand:
+ totalForce[i].Scale(TF_Sand);
+ break;
+
+ case TT_Metal:
+ totalForce[i].Scale(TF_Metal);
+ break;
+
+ default:
+ break;
+
+ }
+
+ }
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::GasForce2
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& forceResult, float dt, int wheelNum)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::GasForce2(rmt::Vector& forceResult, int wheelNum, float dt)
+{
+ if(mVehicle->mWheels[wheelNum]->mDriveWheel)
+ {
+ float desiredForceMag = 0.0f;
+
+ // burnout check
+ //
+ // TODO
+ // only high powered vehicles should do this? or do for longer?
+ //if(mVehicle->mDesignerParams.mDpGasScale >
+
+ // maybe the % topspeed for which you do it is greater for high powered vehicles
+
+ // try mapping 5.0 (average value) to %20 topspeed
+ //static float burnouttunebullshit = 0.04f;
+ const float burnouttunebullshit = 0.03f;
+
+ //static float gasdecrease = 0.5f;
+ const float gasdecrease = 0.75f;
+
+ //static float burnoutsound = 0.7f;
+ const float gasvalue = 0.99f;
+
+ //float burnoutRange = mVehicle->mDesignerParams.mDpGasScale * burnouttunebullshit;
+
+ float burnoutRange = mVehicle->mDesignerParams.mDpBurnoutRange;
+
+ // need new designer param for this percent of top speed
+ //
+ // use same value for doing doughnut?
+
+ // !! TODO - get hard/soft press on ps2!
+ //
+ // this is not gonna happen
+ // if you put the ps2 button into analog mode, you gotta mash it to get _anything_!
+
+ if(mVehicle->mPercentOfTopSpeed < burnoutRange && mVehicle->mGas > gasvalue && mVehicle->mVehicleType == VT_USER)
+ {
+ // burnout
+ //
+ // skid, smoke, spin and non loco
+
+ // the regular calculation would give this:
+ //desiredForceMag = mVehicle->mDesignerParams.mDpGasScale * mVehicle->mSlipGasModifier * mVehicle->mGas * mVehicle->mDesignerParams.mDpMass;
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();
+ float gravFactor = gravity.y * -1.0f / 9.81f;
+
+ //desiredForceMag = mVehicle->mDesignerParams.mDpGasScale * mVehicle->mSlipGasModifier * /*mVehicle->mGas*/ 1.0f * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+ desiredForceMag = mVehicle->mDesignerParams.mDpGasScale * mVehicle->mSlipGasModifier * mVehicle->mGas * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+
+ desiredForceMag *= gasdecrease;
+
+ //mVehicle->mBurnoutLevel = burnoutsound;
+ mVehicle->mBurnoutLevel = (1.0f - (0.5f * (mVehicle->mPercentOfTopSpeed / burnoutRange)));
+ if(mVehicle->mBurnoutLevel < 0.0f)
+ {
+ mVehicle->mBurnoutLevel = 0.0f;
+ }
+
+
+
+ // additional test for donut?
+ // here?
+ if(rmt::Fabs(mVehicle->mUnmodifiedInputWheelTurnAngle) > 0.6)
+ {
+ //const float magicshit = 4.0f;
+ //float magicshit = 9.0f;
+ float magicshit = mVehicle->mDesignerParams.mDpDonutTorque;
+
+ rmt::Vector etorque = mVehicle->mVehicleUp;
+ float dir = 0.0f;
+ if(mVehicle->mWheelTurnAngle > 0.0f)
+ {
+ dir = 1.0f;
+ }
+ if(mVehicle->mWheelTurnAngle < 0.0f)
+ {
+ dir = -1.0f;
+ }
+
+ //float speedeffect = mVehicle->mPercentOfTopSpeed * 2.0f;
+ //if(speedeffect > 1.0f)
+ //{
+ // speedeffect = 1.0f;
+ //}
+
+ float speedmodifier = 1.0f - mVehicle->mPercentOfTopSpeed;
+
+ etorque.Scale(magicshit * dir * mVehicle->mDesignerParams.mDpMass * speedmodifier);
+
+ //mVehicle->mPhObj->AddTorque(etorque);
+
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->AddTorque(etorque);
+
+ }
+ else
+ {
+ // speed burst build up here....
+
+ if(mVehicle->mEBrake > 0.5f)
+ {
+ mVehicle->mSpeedBurstTimer += dt;
+ mVehicle->mBuildingUpSpeedBurst = true;
+ mVehicle->mDoSpeedBurst = false;
+
+ //if(mVehicle->mSpeedBurstTimer > 3.0f)
+ if(mVehicle->mSpeedBurstTimer > mVehicle->mDesignerParams.mDpMaxSpeedBurstTime)
+ {
+ mVehicle->mSpeedBurstTimer = mVehicle->mDesignerParams.mDpMaxSpeedBurstTime;
+ }
+ }
+ else
+ {
+ if(mVehicle->mBuildingUpSpeedBurst)
+ {
+ rAssert(mVehicle->mDoSpeedBurst == false);
+ // just let off the button
+ mVehicle->mBuildingUpSpeedBurst = false;
+ mVehicle->mDoSpeedBurst = true;
+
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC(0);
+ SuperCam* sc = scc->GetActiveSuperCam();
+
+ mVehicle->mFOVToRestore = sc->GetFOV();
+
+ mVehicle->mSpeedBurstTimerHalf = mVehicle->mSpeedBurstTimer * 0.5f;
+
+
+ }
+ }
+
+ }
+
+
+ }
+ else
+ {
+
+ //!
+ // TODO
+ //
+ // take analog gas usage out of here?
+
+ if(mVehicle->mGas > 0.0f)
+ {
+
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();
+ float gravFactor = gravity.y * -1.0f / 9.81f;
+
+
+ if(mVehicle->mVehicleState == VS_NORMAL)
+ {
+ if(mVehicle->mVehicleType == VT_USER && mVehicle->mPercentOfTopSpeed > mVehicle->mDesignerParams.mDpGasScaleSpeedThreshold)
+ {
+ // use high speed gas scale
+ desiredForceMag = mVehicle->mDesignerParams.mDpHighSpeedGasScale * mVehicle->mSlipGasModifier * mVehicle->mGas * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+
+ }
+ else
+ {
+ desiredForceMag = mVehicle->mDesignerParams.mDpGasScale * mVehicle->mSlipGasModifier * mVehicle->mGas * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+
+ }
+
+ }
+ else
+ {
+ // slip
+
+ desiredForceMag = mVehicle->mDesignerParams.mDpSlipGasScale * mVehicle->mSlipGasModifier * mVehicle->mGas* mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+
+ }
+ }
+
+ // if we get out of speedburst range we should probably null out
+
+
+ //mVehicle->mBuildingUpSpeedBurst = false;
+ //mVehicle->mSpeedBurstTimer = 0.0f;
+ //mVehicle->mDoSpeedBurst = false;
+
+ }
+
+
+ // regardless of burnout or normal
+ // test for wheelie down here
+ if(mVehicle->mPercentOfTopSpeed < mVehicle->mDesignerParams.mDpWheelieRange && mVehicle->mGas > gasvalue && mVehicle->mVehicleType == VT_USER)
+ {
+ // set wheelie offsets
+
+ if(!(mVehicle->mDoingWheelie) && !(mVehicle->mWeebleOn))
+ {
+ rmt::Vector wheelie = mVehicle->mCMOffset;
+
+ wheelie.y += mVehicle->mDesignerParams.mDpWheelieYOffset;
+ wheelie.z += mVehicle->mDesignerParams.mDpWheelieZOffset;
+
+ //mVehicle->mPhObj->SetExternalCMOffset(wheelie);
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(wheelie);
+
+ mVehicle->mDoingWheelie = true;
+ }
+
+
+ }
+ else if(mVehicle->mDoingWheelie)
+ {
+ //mVehicle->mPhObj->SetExternalCMOffset(mVehicle->mCMOffset);
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->SetExternalCMOffset(mVehicle->mCMOffset);
+ mVehicle->mDoingWheelie = false;
+ }
+
+
+
+ rmt::Vector force = mVehicle->mVehicleTransverse;
+ //force.CrossProduct(mTerrainIntersectCache[wheelNum].planeNorm);
+ force.CrossProduct(mIntersectNormalUsed[wheelNum]);
+
+ // just in case
+ force.NormalizeSafe();
+
+
+ if(mVehicle->mDoSpeedBurst)
+ {
+ desiredForceMag *= 2.0f;
+ mVehicle->mSpeedBurstTimer -= dt;
+
+ SuperCamCentral* scc = GetSuperCamManager()->GetSCC(0);
+ SuperCam* sc = scc->GetActiveSuperCam();
+
+
+ if(mVehicle->mSpeedBurstTimer <= 0.0f)
+ {
+ mVehicle->mSpeedBurstTimer = 0.0f;
+ mVehicle->mDoSpeedBurst = false;
+
+ //sc->SetFOV(mVehicle->mFOVToRestore); - this shoudl all be moved to super cam
+ }
+ else
+ {
+ //static float camhack = 1.0f;
+ //static float camhack = 0.015f;
+ float camhack = 0.015f;
+ float fov;
+
+ if(mVehicle->mSpeedBurstTimer > (mVehicle->mSpeedBurstTimerHalf * 1.5f))
+ {
+ fov = sc->GetFOV();
+ fov = fov + camhack;
+ //sc->SetFOV(fov); this shoudl all be moved to super cam
+ }
+ else if(mVehicle->mSpeedBurstTimer < (mVehicle->mSpeedBurstTimerHalf * 0.5f))
+ {
+ fov = sc->GetFOV();
+ fov = fov - camhack;
+ //sc->SetFOV(fov); this shoudl all be moved to super cam
+ }
+ // else do nothing in the mid-range
+ }
+
+ }
+
+ if(mVehicle->mDoingJumpBoost)
+ {
+ // want to boost the jump
+ // proportioal to the cars power, and current speed, and should have gas held down
+ if(mVehicle->mGas > 0.5f && mVehicle->mPercentOfTopSpeed > 0.3f && mVehicle->GetSpeedKmh() < 140.0f)
+ {
+ //static float gasscaleboosteffect = 0.1f;
+ //const float gasscaleboosteffect = 0.08f;
+ float boostEffect = 0.0f;
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_SUPERSPRINT )
+ {
+ // do special boost only for AI in supersprint
+ rAssert( mVehicle->mVehicleType == VT_AI );
+ boostEffect = 1.4f;
+ }
+ else
+ {
+ boostEffect = 0.07f;
+ }
+ const float gasscaleboosteffect = boostEffect;
+
+ //float multiplier = 1.0f + /* mVehicle->mPercentOfTopSpeed */ + (mVehicle->mDesignerParams.mDpGasScale * gasscaleboosteffect);
+ const float avgGasScale = 7.0f;
+
+ float multiplier = 1.0f + /* mVehicle->mPercentOfTopSpeed */ + (avgGasScale * gasscaleboosteffect);
+
+ desiredForceMag *= multiplier;
+ }
+
+ }
+
+
+ //rmt::Vector force = mVehicle->mVehicleFacing;
+
+ // just in case
+ // you only get speed burst of buildup if you hold down gas
+ if(mVehicle->mGas == 0.0f && (mVehicle->mDoSpeedBurst || mVehicle->mSpeedBurstTimer > 0.0f))
+ {
+ mVehicle->mSpeedBurstTimer = 0.0f;
+ mVehicle->mDoSpeedBurst = false;
+ }
+
+ if(GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle() == mVehicle)
+ {
+ if(GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_HIGH_ACCELERATION))
+ {
+ desiredForceMag *= 2.0f;
+ }
+ }
+
+
+
+ force.Scale(desiredForceMag);
+
+ forceResult.Add(force);
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::DoughnutTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int wheelNum)
+//
+// Return: float
+//
+//=============================================================================
+void PhysicsLocomotion::DoughnutTest()
+{
+ // already know this is a drive wheel
+/*
+ static float test1 = 0.3f;
+ static float test2 = 0.8f;
+ static float gastest = 0.9f;
+ static float amount = 18.0f;
+
+ if( mVehicle->mPercentOfTopSpeed < test1 && rmt::Fabs(mVehicle->mUnmodifiedInputWheelTurnAngle) > test2 && mVehicle->mGas > gastest)
+ {
+ rmt::Vector doughnutTorque = mVehicle->mVehicleUp;
+
+ // nasty hardcoded logic
+ // -ve is left turn
+
+ if(mVehicle->mUnmodifiedInputWheelTurnAngle < 0.0f)
+ {
+ doughnutTorque.Scale(-amount * mVehicle->mDesignerParams.mDpMass);
+ }
+ else
+ {
+ doughnutTorque.Scale(amount* mVehicle->mDesignerParams.mDpMass);
+ }
+
+ mVehicle->mPhObj->AddTorque(doughnutTorque);
+
+ }
+*/
+}
+
+//=============================================================================
+// PhysicsLocomotion::BrakeForce2
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& forceResult, int wheelNum)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::BrakeForce2(rmt::Vector& forceResult, int wheelNum, float dt)
+{
+
+ //mBrakeTimer = 0.0f;
+ //mBrakeActingAsReverse = false;
+
+ // this is exactly reverse force
+
+ if(mVehicle->mWheels[wheelNum]->mDriveWheel)
+ {
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();
+ float gravFactor = gravity.y * -1.0f / 9.81f;
+
+ // if we're coming to a stop we want to use brakescale
+ // if we're going backwards want to use gasscale
+
+ float desiredForceMag = 0.0f;
+
+
+ float proj = mVehicle->mVelocityCM.DotProduct(mVehicle->mVehicleFacing);
+
+ // don't want just any slight backwards motion to trigger
+ const float cos120 = -0.5f;
+
+ //if(proj < cos120)
+ if(proj < 0.0f)
+ {
+ // going backwards
+
+ // same accel and top speed reverse as forward
+
+ if(mVehicle->mVehicleType == VT_USER && mVehicle->mPercentOfTopSpeed > mVehicle->mDesignerParams.mDpGasScaleSpeedThreshold)
+ {
+ desiredForceMag = mVehicle->mDesignerParams.mDpHighSpeedGasScale * mVehicle->mBrake * mVehicle->mDesignerParams.mDpMass;
+ }
+ else
+ {
+ desiredForceMag = mVehicle->mDesignerParams.mDpGasScale * mVehicle->mBrake * mVehicle->mDesignerParams.mDpMass;
+ }
+
+ if(mVehicle->mSpeedKmh < 50.0f)
+ {
+ mVehicle->mBurnoutLevel = mVehicle->mBrake;
+ }
+ }
+ else
+ {
+ if(mVehicle->mGas > 0.1f)
+ {
+ desiredForceMag = (mVehicle->mDesignerParams.mDpGasScale) * mVehicle->mBrake * mVehicle->mDesignerParams.mDpMass * 0.75f;// magic number is gasdecrease when doing burnout
+ }
+ else
+ {
+ desiredForceMag = (mVehicle->mDesignerParams.mDpBrakeScale + 3.0f) * mVehicle->mBrake * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+ }
+ }
+
+
+
+
+
+
+
+
+
+ rmt::Vector force = mVehicle->mVehicleFacing;
+ force.Scale(-1.0f * desiredForceMag);
+
+ // TODO - make this a state?
+ // so that we can reduce the grip on the rear wheels and totally up the front?
+
+ forceResult.Add(force);
+
+ // if we are actually going in reverse
+ //if(mVehicle->mVehicleFacing.DotProduct(mVehicle->mVelocityCM) < 0.0f)
+ //{
+ // mVehicle->mBurnoutLevel = mVehicle->mBrake;
+ //}
+
+ }
+
+
+
+ /*
+ //static float test = 0.8f;
+ const float test = 0.8f;
+
+ //const float lowspeed = 1.0f;
+ const float lowspeed = 1.0f;
+
+
+
+
+ // copy of test from rolling friction -
+ // holding brake down should hold car perfectly still....
+ if(mVehicle->mBrakeActingAsReverse)
+ {
+ if(mVehicle->mSpeed > lowspeed)
+ {
+ float desiredForceMag = mVehicle->mBrake * mVehicle->mDesignerParams.mDpBrakeScale * mVehicle->mDesignerParams.mDpMass;
+
+ rmt::Vector force = mSuspensionPointVelocities[wheelNum];
+
+ force.NormalizeSafe();
+ force.Scale(-1.0f * desiredForceMag);
+
+ forceResult.Add(force);
+ }
+ else if(mVehicle->mBrake > 0.7f)
+ {
+ // TODO - this may have to change with brakestands!
+
+ // whittle away speed
+ rmt::Vector& linearVel = mVehicle->mSimStateArticulated->GetLinearVelocity();
+ rmt::Vector& angularVel = mVehicle->mSimStateArticulated->GetAngularVelocity();
+
+ linearVel.Scale(test);
+ angularVel.Scale(test);
+
+
+ }
+ }
+ */
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::ReverseForce2
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& forceResult, int wheelNum)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::ReverseForce2(rmt::Vector& forceResult, int wheelNum)
+{
+ if(mVehicle->mWheels[wheelNum]->mDriveWheel)
+ {
+ float desiredForceMag = mVehicle->mDesignerParams.mDpGasScale * mVehicle->mReverse * mVehicle->mDesignerParams.mDpMass;
+
+ rmt::Vector force = mVehicle->mVehicleFacing;
+ force.Scale(-1.0f * desiredForceMag);
+
+ // TODO - make this a state?
+ // so that we can reduce the grip on the rear wheels and totally up the front?
+
+ forceResult.Add(force);
+ }
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::EBrakeEffect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::EBrakeEffect(rmt::Vector& forceResult, int wheelNum)
+{
+ // in addition to reducing the rear grip, we want to apply (weak?) brakes too?
+
+ // since people are using the handbrake like a parking brake, I guess I better acknowledge...
+ static const float reallylowspeed = 0.5f;
+ if(mVehicle->mSpeed < reallylowspeed && mVehicle->mEBrake == 1.0f)
+ {
+ // someone's trying to use it to hold the car in place, so just let the rest test do it's thing
+ return;
+ }
+
+ if(mVehicle->mWheels[wheelNum]->mDriveWheel && mVehicle->mWheels[wheelNum]->mWheelTurnAngle == 0.0f)
+ {
+ // just lock up steer/drive wheels?????
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();
+ float gravFactor = gravity.y * -1.0f / 9.81f;
+
+ //float desiredForceMag = mVehicle->mEBrake * mVehicle->mDesignerParams.mDpBrakeScale * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+ const float defaultebrakebrakescalethatplumishappywith = 3.0f;
+ float desiredForceMag = mVehicle->mEBrake * defaultebrakebrakescalethatplumishappywith /*mVehicle->mDesignerParams.mDpBrakeScale*/ * mVehicle->mDesignerParams.mDpMass;// * gravFactor;
+
+ rmt::Vector force = mSuspensionPointVelocities[wheelNum];
+
+ force.NormalizeSafe();
+ force.Scale(-1.0f * desiredForceMag);
+
+ forceResult.Add(force);
+
+ }
+
+
+
+
+ //mVehicle->mVehicleState = VS_SLIP;
+
+ // instead of adding another state variable, just put this calculation right in steering code?
+ //mVehicle->mTireLateralResistance = mVehicle->mDesignerParams.mDpTireLateralResistanceSlip * (1.0f - mVehicle->mEBrake);
+
+}
+
+//=============================================================================
+// PhysicsLocomotion::SteeringForce2
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& forceResult, int wheelNum)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::SteeringForce2(rmt::Vector& forceResult, int wheelNum, float dt)
+{
+ rmt::Vector lateralDirection = mVehicle->mVehicleTransverse;
+
+
+ if(mVehicle->mWheels[wheelNum]->mSteerWheel)
+ {
+ rmt::Matrix steeringMatrix; // TODO - make this a class member?
+ steeringMatrix.Identity();
+ steeringMatrix.FillRotateY(mVehicle->mWheels[wheelNum]->mWheelTurnAngle);
+
+ lateralDirection.Transform(steeringMatrix);
+ }
+
+ //float lateralVelocityProjection = lateralDirection.DotProduct(mSuspensionPointVelocities[wheelNum]);
+
+
+
+ rmt::Vector temp = mSuspensionPointVelocities[wheelNum];
+
+ //rmt::Vector temp = mVehicle->mVelocityCM;
+
+
+ float desiredLateralResistanceForce = 0.0f;
+
+ //const float threshold = 0.15f;
+ //static float threshold = 0.15f;
+ const float threshold = 0.15f;
+ if(mVehicle->mPercentOfTopSpeed > threshold)
+ {
+ //-------
+ // normal
+ //-------
+
+ // this is the normal thing to do for regular and high-speed driving
+ temp.NormalizeSafe();
+
+ float lateralVelocityProjection = lateralDirection.DotProduct(temp);
+ // the higher this is the more we're asking this tire to do for us!
+
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();
+ float gravFactor = gravity.y * -1.0f / 9.81f;
+
+ if(mVehicle->mVehicleState == VS_NORMAL)
+ {
+ desiredLateralResistanceForce = (mVehicle->mDesignerParams.mDpMass * lateralVelocityProjection *
+ mVehicle->mDesignerParams.mDpTireLateralResistanceNormal) /*mCurrentSteeringForce)*/ * 0.25f;
+ }
+ else if(mVehicle->mVehicleState == VS_EBRAKE_SLIP)
+ {
+ // slip
+ {
+
+ desiredLateralResistanceForce = (mVehicle->mDesignerParams.mDpMass * lateralVelocityProjection *
+ mVehicle->mDesignerParams.mDpTireLateralResistanceSlip) /*mCurrentSteeringForce)*/ * 0.25f;
+
+
+
+ }
+ }
+ else // just VS_SLIP
+ {
+ // slip without ebrake!
+ desiredLateralResistanceForce = (mVehicle->mDesignerParams.mDpMass * lateralVelocityProjection *
+ mVehicle->mDesignerParams.mDpTireLateralResistanceSlipNoEBrake /*mCurrentSteeringForce*/) * 0.25f;
+
+
+ if(mVehicle->mWheels[wheelNum]->mSteerWheel)
+ {
+ desiredLateralResistanceForce *= (1.0f + mVehicle->mDesignerParams.mDpSlipEffectNoEBrake);
+
+ }
+ else
+ {
+ desiredLateralResistanceForce *= (1.0f - mVehicle->mDesignerParams.mDpSlipEffectNoEBrake);
+ }
+
+ }
+
+
+/*
+
+ // put ebrake effect outside of any state
+
+ if(mVehicle->mEBrake > 0.1) // note: we can be in this state without always holding the button down
+ {
+ if(mVehicle->mWheels[wheelNum]->mSteerWheel)
+ {
+ // increase resistance on steer wheels
+ //desiredLateralResistanceForce *= (1.0f + mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+ desiredLateralResistanceForce *= (1.0f + mVehicle->mDesignerParams.mDpEBrakeEffect);
+
+ }
+ else
+ {
+ // decrease resistance on rear
+ //desiredLateralResistanceForce *= (1.0f - mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+ desiredLateralResistanceForce *= (1.0f - mVehicle->mDesignerParams.mDpEBrakeEffect);
+ }
+
+ }
+
+*/
+
+
+
+ // rockford...?
+
+
+ // this is the condition for beginning a rockford
+ if( mVehicle->mVehicleFacing.DotProduct(mVehicle->mVelocityCM) < 0.0f && mVehicle->mEBrake < 0.1 && rmt::Fabs(mVehicle->mUnmodifiedInputWheelTurnAngle) > 0.5f && mVehicle->mGas == 0.0f )//||
+ //mVehicle->mDoingRockford )
+ {
+
+ mVehicle->mDoingRockford = true;
+ // rockford in proportion to ebrake setting
+
+ // note - the + and - are inverted from regular ebrake effect
+
+
+ //static float magicshit = 20.0f;
+ //static float mDpRockfordEffect = 0.5f;
+ float mDpRockfordEffect = 0.5f;
+
+
+
+ if(mVehicle->mWheels[wheelNum]->mSteerWheel)
+ {
+ // increase resistance on steer wheels
+ //desiredLateralResistanceForce *= (1.0f + mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+ desiredLateralResistanceForce *= (1.0f - mDpRockfordEffect);
+ }
+ else
+ {
+ // decrease resistance on rear
+ //desiredLateralResistanceForce *= (1.0f - mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+ desiredLateralResistanceForce *= (1.0f + mDpRockfordEffect);
+ }
+
+
+ float hackbullshit = 10.0f;
+ float magicshit = hackbullshit * mDpRockfordEffect;
+
+ rmt::Vector rockfordtorque = mVehicle->mVehicleUp;
+ float dir = 0.0f;
+ if(mVehicle->mWheelTurnAngle > 0.0f)
+ {
+ dir = -1.0f;
+ }
+ if(mVehicle->mWheelTurnAngle < 0.0f)
+ {
+ dir = 1.0f;
+ }
+
+
+ float speedeffect = mVehicle->mPercentOfTopSpeed;
+
+ rockfordtorque.Scale(magicshit * dir * mVehicle->mDesignerParams.mDpMass * speedeffect);
+
+ //mVehicle->mPhObj->AddTorque(rockfordtorque);
+ ((ArticulatedPhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject(-1)))->AddTorque(rockfordtorque);
+
+
+ mVehicle->mBurnoutLevel = 1.0f;
+
+
+
+
+ //hmmm....
+ //static float resdrop = 0.001f;
+ float resdrop = 0.001f;
+ //desiredLateralResistanceForce *= resdrop;
+
+ // new place to set this
+ mVehicle->mCollisionLateralResistanceDropFactor = resdrop;
+
+ }
+
+
+ // new - reduce traction on drive wheels if givin 'er gas
+ const float gasmodifierthreshold = 0.7f;
+ const float absoluteSpeedThreshold = 90.0f;
+ if( mVehicle->mWheels[wheelNum]->mDriveWheel && mVehicle->mVehicleType == VT_USER && mVehicle->mGas > 0.0f &&
+ mVehicle->mPercentOfTopSpeed > gasmodifierthreshold && rmt::Fabs(mVehicle->mWheelTurnAngleInputValue) > 0.1f &&
+ mVehicle->mSpeedKmh > absoluteSpeedThreshold)
+ {
+ const float maxPenalty = 0.2f;
+ float modifier = 1.0f - (maxPenalty * mVehicle->mGas); // ? also include speed??
+
+ desiredLateralResistanceForce *= modifier;
+
+ mVehicle->mDrawWireFrame = true;
+ mVehicle->mLosingTractionDueToAccel = true; // for plum
+ //mVehicle->mBurnoutLevel = 1.0f;
+
+
+ }
+
+
+
+
+ }
+ else
+ {
+ //----------
+ // low speed
+ //----------
+
+ temp.NormalizeSafe();
+
+ float lateralVelocityProjection = lateralDirection.DotProduct(temp);
+
+ // at low speeds we want to account for the (small) magnitude of the velocity
+ lateralVelocityProjection *= (mVehicle->mPercentOfTopSpeed / threshold);
+
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();
+ float gravFactor = gravity.y * -1.0f / 9.81f;
+
+ if(mVehicle->mVehicleState == VS_NORMAL)
+ {
+ desiredLateralResistanceForce = (mVehicle->mDesignerParams.mDpMass * lateralVelocityProjection * mVehicle->mDesignerParams.mDpTireLateralResistanceNormal) * 0.25f;// * gravFactor;
+ }
+ else
+ {
+ // slip
+ desiredLateralResistanceForce = (mVehicle->mDesignerParams.mDpMass * lateralVelocityProjection * mVehicle->mDesignerParams.mDpTireLateralResistanceSlip) * 0.25f;// * gravFactor;
+
+ /*
+ if(mVehicle->mEBrake > 0.0)
+ {
+ if(mVehicle->mWheels[wheelNum]->mSteerWheel)
+ {
+ // increase resistance on steer wheels
+ desiredLateralResistanceForce *= (1.0f + mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+
+ }
+ else
+ {
+ // decrease resistance on rear
+ desiredLateralResistanceForce *= (1.0f - mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+
+ }
+
+ }
+ */
+
+
+ }
+
+ }
+
+
+
+ // put ebrake effect outside of any state
+ if(mVehicle->mPercentOfTopSpeed > 0.02f)
+ {
+ if(mVehicle->mEBrake > 0.1) // note: we can be in this state without always holding the button down
+ {
+ if(mVehicle->mWheels[wheelNum]->mSteerWheel)
+ {
+ // increase resistance on steer wheels
+ //desiredLateralResistanceForce *= (1.0f + mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+ desiredLateralResistanceForce *= (1.0f + mVehicle->mDesignerParams.mDpEBrakeEffect);
+
+ }
+ else
+ {
+ // decrease resistance on rear
+ //desiredLateralResistanceForce *= (1.0f - mVehicle->mEBrake * mVehicle->mDesignerParams.mDpEBrakeEffect);
+ desiredLateralResistanceForce *= (1.0f - mVehicle->mDesignerParams.mDpEBrakeEffect);
+ }
+
+ }
+ }
+
+
+ //rmt::Vector force = lateralDirection;
+
+ // assumign these are good?
+
+ //mTerrainIntersectCache[i].planePosn = planePosn;
+ //mTerrainIntersectCache[i].planeNorm = planeNormal;
+
+
+ //rmt::Vector force = mTerrainIntersectCache[wheelNum].planeNorm;
+
+ rmt::Vector force = mIntersectNormalUsed[wheelNum];
+ force.CrossProduct(mVehicle->mVehicleFacing);
+
+ // just in case
+ force.NormalizeSafe();
+
+ //rmt::Vector force = mVehicle->mVehicleTransverse; // I think this is better!
+
+
+
+
+
+ /// new quick terrain based friction test
+ // only affect steering
+
+ switch (mTerrainIntersectCache[wheelNum].mTerrainType)
+ {
+ case TT_Road:
+ desiredLateralResistanceForce *= TF_Road;
+ break;
+
+ case TT_Grass:
+ desiredLateralResistanceForce *= TF_Grass;
+ break;
+
+ case TT_Dirt:
+ desiredLateralResistanceForce *= TF_Dirt;
+ break;
+
+ case TT_Water:
+ desiredLateralResistanceForce *= TF_Water;
+ break;
+
+ case TT_Gravel:
+ desiredLateralResistanceForce *= TF_Gravel;
+ break;
+
+ case TT_Wood:
+ desiredLateralResistanceForce *= TF_Wood;
+ break;
+
+ case TT_Sand:
+ desiredLateralResistanceForce *= TF_Sand;
+ break;
+
+ case TT_Metal:
+ desiredLateralResistanceForce *= TF_Metal;
+ break;
+
+ default:
+ break;
+
+ }
+
+
+ force.Scale(-1.0f * desiredLateralResistanceForce);
+
+
+ if(1)//mVehicle->mBurnoutLevel == 0.0f)
+ {
+ // test
+ // scale lateral resistance way down if we have just had a collision
+ force.Scale(mVehicle->mCollisionLateralResistanceDropFactor);
+ }
+
+
+ // test before or after the above drop?
+ //TestSteeringForce(force, wheelNum);
+
+
+
+ forceResult.Add(force);
+
+
+
+ // new test
+ // faked high speed instability
+ /*
+ const float highSpeedInstabilityThreshold = 0.6f;
+ if(mVehicle->mPercentOfTopSpeed > highSpeedInstabilityThreshold)
+ {
+ // add some magic torque depending on the input wheel turn angle.
+ if(mVehicle->mWheelTurnAngleInputValue != 0.0f && !(mVehicle->mAirBorn))
+ {
+ static float magicshit = 10.0f;
+
+ rmt::Vector instabilityTorque = mVehicle->mVehicleFacing;
+ instabilityTorque.Scale(magicshit * mVehicle->mDesignerParams.mDpMass * mVehicle->mPercentOfTopSpeed * mVehicle->mWheelTurnAngleInputValue);
+
+ sim::PhysicsObject* phobj = (sim::PhysicsObject*)(mVehicle->mSimStateArticulated->GetSimulatedObject());
+ rAssert(phobj);
+
+ phobj->AddTorque(instabilityTorque);
+
+
+ }
+ }
+ */
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::TestSteeringForce
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& force)
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::TestSteeringForce(rmt::Vector& force, int index)
+{
+ //mVehicleState not changed here - this is just used to limit the lateral force
+
+ //static float forceCap = 1.0f;
+
+
+ rAssert(0);
+
+
+/*
+ static float rearWheelMult = 1.0f;
+
+ if(mVehicle->mVehicleState == VS_SLIP)
+ {
+
+ float suspensionEffect = mCachedSuspensionForceResults[index];
+
+ float suspensionMinEffect = mVehicle->mSuspensionRestValue;
+
+ if(suspensionEffect < suspensionMinEffect)
+ {
+ suspensionEffect = suspensionMinEffect;
+ }
+
+ float maxTireForce = mVehicle->mDesignerParams.mDpTireLateralStaticGrip * suspensionEffect;
+
+
+ // cap all forces
+ int j;
+ for(j = 0; j < 4; j++)
+ {
+
+ //if(totalForce[j].Magnitude() > forceCap)
+ //{
+ // totalForce[j].NormalizeSafe();
+ // totalForce[j].Scale(forceCap);
+ //}
+
+ if(j < 2)
+ {
+
+ if(force.Magnitude() > maxTireForce * rearWheelMult)
+ {
+ force.NormalizeSafe();
+ force.Scale(maxTireForce * rearWheelMult);
+ }
+ }
+ else
+ {
+ if(force.Magnitude() > maxTireForce)
+ {
+ force.NormalizeSafe();
+ force.Scale(maxTireForce);
+ }
+
+ }
+
+ }
+
+ }
+
+*/
+}
+
+//=============================================================================
+// PhysicsLocomotion::TestControllerForces
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector* totalForce) -- points to array of 4
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::TestControllerForces(rmt::Vector* totalForce)
+{
+
+ // mVehicleState only changed in here!
+
+
+ // find wheel with highest down force
+ int i;
+ int index = 0;
+ for(i = 1; i < 4; i++)
+ {
+ if(mCachedSuspensionForceResults[i] > mCachedSuspensionForceResults[index])
+ {
+ index = i;
+ }
+ }
+
+ // TODO - fix this for airborn!
+ /*
+ float suspensionEffect = mCachedSuspensionForceResults[index];
+
+ float suspensionMinEffect = mVehicle->mSuspensionRestValue;
+
+ if(suspensionEffect < suspensionMinEffect)
+ {
+ suspensionEffect = suspensionMinEffect;
+ }
+ */
+
+ // new test
+ float suspensionEffect = mVehicle->mSuspensionRestValue;
+
+
+ float maxTireForce = mVehicle->mDesignerParams.mDpTireLateralStaticGrip * suspensionEffect;
+
+ /*
+ // new Mar 12, 2003
+ // affect the maxTireForce calculation by gas
+ const float gasmodifierthreshold = 0.7f;
+ const float absoluteSpeedThreshold = 90.0f;
+
+ if(mVehicle->mVehicleType == VT_USER && mVehicle->mGas > 0.0f &&
+ mVehicle->mPercentOfTopSpeed > gasmodifierthreshold && rmt::Fabs(mVehicle->mWheelTurnAngleInputValue) > 0.1f &&
+ mVehicle->mSpeedKmh > absoluteSpeedThreshold)
+ {
+ //const float maxPenalty = 0.2f;
+ static float maxPenalty = 0.4f;
+ float modifier = 1.0f - (maxPenalty * mVehicle->mGas); // ? also include speed??
+
+ // *** desiredLateralResistanceForce *= modifier;
+ maxTireForce *= modifier;
+
+ mVehicle->mDrawWireFrame = true;
+ mVehicle->mLosingTractionDueToAccel = true; // for plum
+ //mVehicle->mBurnoutLevel = 1.0f;
+
+
+ }
+ */
+
+
+ if(mVehicle->mVehicleState == VS_NORMAL)
+ {
+ // if the wheel with the greatest down force is slipping
+ // make whole car slip
+
+ if(mVehicle->mEBrake > 0.1f && mVehicle->mPercentOfTopSpeed > 0.05f)
+ {
+ // change state to controlled skid
+ mVehicle->mVehicleState = VS_EBRAKE_SLIP;
+ }
+ else
+ {
+ // now if wheel index is slipping, whole fucking car is slipping
+ float totalForceMag = totalForce[index].Magnitude();
+ //float maxTireForce = mVehicle->mDesignerParams.mDpTireLateralStaticGrip * suspensionEffect;
+ if(totalForceMag > maxTireForce)
+ {
+ // fucker is sliding
+ mVehicle->mVehicleState = VS_SLIP;
+ //mVehicle->mTireLateralResistance = mVehicle->mDesignerParams.mDpTireLateralResistanceSlip;
+
+ }
+ }
+
+ }
+ else
+ {
+ if( !mVehicle->mOutOfControl )
+ {
+ // need heruistic to go back to normal
+ rmt::Vector velDir = mVehicle->mVelocityCM;
+ velDir.NormalizeSafe();
+
+ float cos5 = 0.9962f;
+ float cos10 = 0.9848f;
+ float cos20 = 0.9397f;
+ float cos15 = 0.9659f;
+
+ // in addition to the alignment test, should not go into VS_SLIP if you're going very slow,
+ // ebrake or not.
+
+ if( (velDir.DotProduct(mVehicle->mVehicleFacing) > cos15 && rmt::Fabs(mVehicle->mWheelTurnAngleInputValue) < 0.1f) ||
+ mVehicle->mSpeed < 1.0f ||
+ mVehicle->mGas == 0.0f)
+ {
+ mVehicle->mVehicleState = VS_NORMAL;
+ //mVehicle->mTireLateralResistance = mVehicle->mDesignerParams.mDpTireLateralResistanceNormal;
+ }
+
+ /*
+ // duplicate this here in case we only got in here cause the ebrake is held down.
+ if(mVehicle->mEBrake > 0.1f && mVehicle->mSpeed > 1.0f)
+ {
+ mVehicle->mVehicleState = VS_SLIP;
+ //mVehicle->mTireLateralResistance = mVehicle->mDesignerParams.mDpTireLateralResistanceSlip;
+ // return;
+ }
+ */
+ }
+ }
+
+
+
+
+
+}
+
+
+//=============================================================================
+// PhysicsLocomotion::SetSkidLevel
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void PhysicsLocomotion::SetSkidLevel()
+{
+ mVehicle->mSkidLevel = 0.0f;
+ mVehicle->mSlipGasModifier = 1.0f;
+
+ //static float hack = 0.7f;
+ //mVehicle->mSkidLevel = hack;
+
+
+ float skid = 0.0f;
+ float speedfactor = mVehicle->mPercentOfTopSpeed;
+
+
+
+ rmt::Vector velDir = mVehicle->mVelocityCM;
+ velDir.NormalizeSafe();
+
+
+ float projectionOnly = rmt::Fabs(velDir.DotProduct(mVehicle->mVehicleTransverse));
+
+
+ if(mVehicle->mVehicleState == VS_SLIP || mVehicle->mVehicleState == VS_EBRAKE_SLIP)
+ {
+ // static float hack = 0.7f;
+ // mVehicle->mSkidLevel = hack;
+
+
+ if(1)//projectionOnly > 0.7f)
+ {
+ // this actually works quite well
+ skid = projectionOnly;
+ }
+ else
+ {
+ skid = projectionOnly * speedfactor;
+ }
+
+
+ // if they're using the ebrake, we want whichever is loudest:
+ if(mVehicle->mEBrake * 0.5f > skid)
+ {
+ skid = mVehicle->mEBrake * 0.5f;
+ }
+
+ }
+
+ if(mVehicle->mBrake > 0.05f && speedfactor > 0.07f)
+ {
+ /*
+ rmt::Vector velDir = mVehicle->mVelocityCM;
+ velDir.NormalizeSafe();
+
+ float proj = velDir.DotProduct(mVehicle->mVehicleFacing);
+
+ // hmm..
+ if(mVehicle->mBrake * 0.5f > skid && proj > 0.0f)
+ {
+ skid = mVehicle->mBrake * 0.5f;
+ }
+ */
+
+ // not sure if values lower than 0.5 are working...
+ //
+ //
+ float forward = velDir.DotProduct(mVehicle->mVehicleFacing);
+ if(forward > 0.0f && speedfactor < 0.5f)
+ {
+ skid = 1.0f;
+ }
+
+
+ }
+
+ if(mVehicle->mEBrake > 0.85f && speedfactor > 0.07f)// && mVehicle->mWheelTurnAngle == 0.0f)
+ {
+ // hmm..
+ if(mVehicle->mEBrake * 0.5f > skid)
+ {
+ skid = mVehicle->mEBrake * 0.5f;
+ }
+ }
+
+
+ // compare to burnout level
+ if(mVehicle->mBurnoutLevel > skid)
+ {
+ mVehicle->mSkidLevel = mVehicle->mBurnoutLevel;
+ }
+ else
+ {
+ mVehicle->mSkidLevel = skid;
+ }
+
+
+ // still too quiet
+ if(mVehicle->mSkidLevel > 0.0f)
+ {
+ if(mVehicle->mSkidLevel < 0.5f)
+ {
+ mVehicle->mSkidLevel = 0.5f;
+ }
+ }
+
+
+/*
+
+ // override any of the above shit if wheels are not in contact with ground
+ int i;
+ if(mBurnoutLevel > 0.0f)
+ {
+ if(mWheels[0]->mWheelInCollision
+
+
+ for(i = 0; i < 2; i++)
+ {
+ if(mWheels[i]->mWheelInCollision) // will this value still be valid here?
+ {
+ mGeometryVehicle->SetSkidValues(i, mSkidLevel);
+ }
+ }
+ }
+ else
+ {
+ for(i = 0; i < 4; i++)
+ {
+ if(mWheels[i]->mWheelInCollision) // will this value still be valid here?
+ {
+ mGeometryVehicle->SetSkidValues(i, mSkidLevel);
+ }
+ }
+ }
+
+
+*/
+
+
+}
+
+
+
+
+
+
+
+
+
diff --git a/game/code/worldsim/redbrick/redbrickcollisionsolveragent.cpp b/game/code/worldsim/redbrick/redbrickcollisionsolveragent.cpp
new file mode 100644
index 0000000..4543079
--- /dev/null
+++ b/game/code/worldsim/redbrick/redbrickcollisionsolveragent.cpp
@@ -0,0 +1,927 @@
+/*===========================================================================
+ redbrickcollisionsolveragent.cpp
+
+ created Jan 28, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+===========================================================================*/
+#include <simcommon/simstatearticulated.hpp>
+#include <simcommon/simulatedobject.hpp>
+#include <worldsim/redbrick/redbrickcollisionsolveragent.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/wheel.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <mission/gameplaymanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+
+#include <events/eventmanager.h>
+#include <sound/soundcollisiondata.h>
+
+#ifdef RAD_XBOX
+#include <float.h>
+#endif
+
+
+//------------------------------------------------------------------------
+RedBrickCollisionSolverAgent::RedBrickCollisionSolverAgent()
+{
+ //
+ mBottomedOut = false;
+
+ //mBottomedOutScale = 1.0e-5f;
+ //mBottomedOutScale = 1.0f;
+ //mBottomedOutScale = 0.0002f;
+ mBottomedOutScale = 0.0003f;
+
+
+ mWheelSidewaysHit = false;
+
+ mWheelSidewaysHitScale = 0.002f;
+
+}
+
+
+//------------------------------------------------------------------------
+RedBrickCollisionSolverAgent::~RedBrickCollisionSolverAgent()
+{
+ //
+}
+
+
+//------------------------------------------------------------------------------------------------
+void RedBrickCollisionSolverAgent::ResetCollisionFlags()
+{
+ // TODO - how to properly tie this to rest of vehicle module????
+
+ // TODO - need these flags on a car by car basis
+
+ mBottomedOut = false;
+ mWheelSidewaysHit = false;
+
+}
+
+
+//------------------------------------------------------------------------------------------------
+Solving_Answer RedBrickCollisionSolverAgent::TestImpulse(rmt::Vector& impulse, Collision& inCollision)
+{
+
+ // currently this method does nothing...
+ //
+ // test some collision results...
+
+
+
+
+
+ // TODO -
+ // something more efficient here so we don't have to test this shit every time
+
+ CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ SimState* simStateA = collObjA->GetSimState();
+ SimState* simStateB = collObjB->GetSimState();
+
+ //
+ // SOUND EVENT HERE?
+ //
+
+ // need to convert the impulse to a nice friendly float between 0.0 and 1.0
+ //
+ // just looking at some empirical data, the range of impulse magnitude seems to be from around
+ // 0.0 to 100000.0 - at the high end would be a heavy vehicle (3000 kg) hitting a static wall at 122kmh
+ // a 2000kg car hitting a wall at 150kmh gave 78706.7
+
+
+
+
+
+#ifdef RAD_DEBUG
+ if(rmt::IsNan(impulse.x) || rmt::IsNan(impulse.y) || rmt::IsNan(impulse.z))
+ {
+ rAssert(0);
+ }
+#endif
+
+
+
+
+
+ float impulseMagnitude = impulse.Magnitude();
+
+ if(impulseMagnitude > 100000.0f)
+ {
+ int stophere = 1;
+ }
+
+
+
+ const float maxIntensity = 100000.0f;
+
+ float soundScale = impulseMagnitude / maxIntensity;
+ if(soundScale > 1.0f)
+ {
+ soundScale = 1.0f;
+ }
+ if(soundScale < 0.0f)
+ {
+ rAssert(0);
+ }
+
+// SoundCollisionData soundData( soundScale, simStateA->mAIRefPointer, simStateB->mAIRefPointer );
+// GetEventManager()->TriggerEvent( EVENT_COLLISION, &soundData );
+ // up to this point is pretty generic for sound stuff - ie. the guts of PostReactToCollision might look a lot like this?
+
+
+ // here is more vehicle intensive
+
+ //if(simStateA->mAIRefIndex == Vehicle::GetAIRef() && simStateB->mAIRefIndex == Vehicle::GetAIRef())
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle && simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ // car on car
+
+ //static float magic = 1.0f;
+
+ // fun test
+ //impulse.Scale(magic);
+
+ //int stophere = 1;
+
+ if( ((Vehicle*)(simStateA->mAIRefPointer))->mVehicleType == VT_TRAFFIC )
+ {
+
+ }
+
+ }
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle || simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ //PhysicsVehicle* physicsVehicle;
+ Vehicle* vehicle;
+
+ float test = impulse.Magnitude();
+
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle )
+ {
+ vehicle = (Vehicle*)(simStateA->mAIRefPointer);
+ }
+ else
+ {
+ vehicle = (Vehicle*)(simStateB->mAIRefPointer);
+ }
+
+
+ // TODO - more detailed test...
+
+ // TODO - I'm not sure I like this system...
+
+ if(mBottomedOut) // todo - more detailed test for if we are dealing with a wheel
+ {
+ //mImpulse.Scale(mBottomedOutScale);
+ }
+ if(mWheelSidewaysHit)
+ {
+ //mImpulse.Scale(mWheelSidewaysHitScale);
+
+ //mImpulse.y = 0.0f; // TODO - expand to below later...
+
+ }
+
+ }
+
+ return Solving_Continue;
+}
+
+//=============================================================================
+// RedBrickCollisionSolverAgent::CarOnCarAction
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (Collision& inCollision)
+//
+// Return: Solving_Answer
+//
+//=============================================================================
+Solving_Answer RedBrickCollisionSolverAgent::CarOnCarPreTest(Collision& inCollision, int inPass)
+{
+ CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ SimState* simStateA = collObjA->GetSimState();
+ SimState* simStateB = collObjB->GetSimState();
+
+ Vehicle* vehicleA = (Vehicle*)(simStateA->mAIRefPointer);
+ Vehicle* vehicleB = (Vehicle*)(simStateB->mAIRefPointer);
+
+ int jointIndexA = inCollision.mIndexA;
+ int jointIndexB = inCollision.mIndexB;
+
+
+
+ //vehicleA->TriggerDamage();
+ //vehicleB->TriggerDamage(); - move the damage triggering to Vehicle::PostReactToCollision
+
+// vehicleA->SetHitJoint(jointIndexA);
+// vehicleB->SetHitJoint(jointIndexB);
+
+
+ // for now...
+ if(1)//vehicleA->mUsingInCarPhysics)
+ {
+ if(vehicleA->IsJointAWheel(jointIndexA) || vehicleB->IsJointAWheel(jointIndexB))
+ {
+ return Solving_Aborted;
+ }
+
+ if(vehicleA->IsAFlappingJoint(jointIndexA) || vehicleB->IsAFlappingJoint(jointIndexB))
+ {
+ // TODO - try taking this out?
+ return Solving_Aborted;
+ }
+ }
+
+ //hmmm
+ // is this the place to switch loco?
+ if(vehicleA->GetLocomotionType() == VL_TRAFFIC)
+ {
+ vehicleA->SetLocomotion(VL_PHYSICS);
+ }
+
+ if(vehicleB->GetLocomotionType() == VL_TRAFFIC)
+ {
+ vehicleB->SetLocomotion(VL_PHYSICS);
+ }
+
+ // just in case it has come to rest....
+
+ if(simStateA->GetControl() == sim::simAICtrl)
+ {
+ simStateA->SetControl(sim::simSimulationCtrl);
+ }
+
+ if(simStateB->GetControl() == sim::simAICtrl)
+ {
+ simStateB->SetControl(sim::simSimulationCtrl);
+ }
+
+
+ return CollisionSolverAgent::PreCollisionEvent(inCollision, inPass);
+}
+
+//=============================================================================
+// RedBrickCollisionSolverAgent::PreCollisionEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (Collision& inCollision, int inPass)
+//
+// Return: Solving_Answer
+//
+//=============================================================================
+Solving_Answer RedBrickCollisionSolverAgent::PreCollisionEvent(Collision& inCollision, int inPass)
+{
+ // have a look at objects A and B and see if we care about them:
+
+ // TODO
+ // also, depending how steeply we've hit something, we also want to let the solver do it's thing...
+ // ? mabye cancel out the y component?
+
+ // also may skip this depending how tipped we are
+
+
+ CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ SimState* simStateA = collObjA->GetSimState();
+ SimState* simStateB = collObjB->GetSimState();
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle || simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ //--------------------------------------
+ // deal specially with car on car action
+ //--------------------------------------
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle && simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ // test for hitting a wheel or a flapping joint in here
+ //
+ return CarOnCarPreTest(inCollision, inPass);
+ }
+
+ Vehicle* vehicle;
+
+ int jointIndex;
+
+ rmt::Vector groundContactPoint;
+ rmt::Vector normalPointingAtCar;
+
+ bool carisA = true;
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ vehicle = (Vehicle*)(simStateA->mAIRefPointer);
+ jointIndex = inCollision.mIndexA;
+
+ groundContactPoint = inCollision.GetPositionB();
+ normalPointingAtCar = inCollision.mNormal;
+
+ }
+ else
+ {
+ vehicle = (Vehicle*)(simStateB->mAIRefPointer);
+ jointIndex = inCollision.mIndexB;
+
+ groundContactPoint = inCollision.GetPositionA();
+ normalPointingAtCar = inCollision.mNormal;
+ normalPointingAtCar.Scale(-1.0f);
+ carisA = false;
+ }
+
+
+ // temp hack cause I think we're hitting our driver every frame:
+/*
+ if(carisA)
+ {
+ if(simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveable)
+ {
+ return Solving_Aborted;
+ }
+ }
+ else
+ {
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveable)
+ {
+ return Solving_Aborted;
+ }
+
+ }
+*/
+
+
+ /*
+
+ this now encompassed below...
+
+ if(carisA)
+ {
+ if(simStateB->mAIRefIndex == PhysicsAIRef::StateProp)
+ {
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if(playerAvatar->GetVehicle() == vehicle)
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_MOVEABLE, (void*)simStateB );
+ }
+ }
+ }
+ else
+ {
+ if(simStateA->mAIRefIndex == PhysicsAIRef::StateProp)
+ {
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if(playerAvatar->GetVehicle() == vehicle)
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_MOVEABLE, (void*)simStateA );
+ }
+ }
+ }
+ */
+
+ if(carisA)
+ {
+ if(simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveable || simStateB->mAIRefIndex == PhysicsAIRef::StateProp)
+ {
+ sim::SimControlEnum control = simStateB->GetControl();
+ bool ishit = ((DynaPhysDSG*)(simStateB->mAIRefPointer))->mIsHit;
+ enClasstypeID classid = (enClasstypeID)((DynaPhysDSG*)(simStateB->mAIRefPointer))->GetCollisionAttributes()->GetClasstypeid();
+
+
+ if(simStateB->GetControl() == sim::simAICtrl && ((DynaPhysDSG*)(simStateB->mAIRefPointer))->mIsHit &&
+ ((DynaPhysDSG*)(simStateB->mAIRefPointer))->GetCollisionAttributes()->GetClasstypeid() == PROP_ONETIME_MOVEABLE)
+ {
+ return Solving_Aborted;
+ }
+ else
+ {
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if(playerAvatar->GetVehicle() == vehicle)
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_MOVEABLE, (void*)simStateB );
+ }
+ }
+ }
+ }
+ else
+ {
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveable || simStateA->mAIRefIndex == PhysicsAIRef::StateProp)
+ {
+
+ sim::SimControlEnum control = simStateA->GetControl();
+ bool ishit = ((DynaPhysDSG*)(simStateA->mAIRefPointer))->mIsHit;
+ enClasstypeID classid = (enClasstypeID)((DynaPhysDSG*)(simStateA->mAIRefPointer))->GetCollisionAttributes()->GetClasstypeid();
+
+
+
+ if(simStateA->GetControl() == sim::simAICtrl && ((DynaPhysDSG*)(simStateA->mAIRefPointer))->mIsHit &&
+ ((DynaPhysDSG*)(simStateA->mAIRefPointer))->GetCollisionAttributes()->GetClasstypeid() == PROP_ONETIME_MOVEABLE) // safe cast?
+ {
+ return Solving_Aborted;
+ }
+ else
+ {
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if(playerAvatar->GetVehicle() == vehicle)
+ {
+ GetEventManager()->TriggerEvent( EVENT_HIT_MOVEABLE, (void*)simStateA );
+ }
+ }
+
+ }
+
+ }
+
+ if(vehicle->mUsingInCarPhysics)
+ {
+ if(vehicle->IsAFlappingJoint(jointIndex))
+ {
+
+ // vehicle->SetHitJoint(jointIndex);
+
+ return Solving_Aborted;
+ }
+ }
+
+
+
+ if(vehicle->IsJointAWheel(jointIndex))
+ {
+
+ if(carisA)
+ {
+ if(simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizFence)
+ {
+ int stophere = 1;
+
+ // the old tip test
+ rmt::Vector worldUp = GetWorldPhysicsManager()->mWorldUp;
+
+ float cos10 = 0.9848f;
+ //if(worldUp.DotProduct(vehicle->mVehicleUp) < cos10)
+ {
+ // will this work??
+ inCollision.mIndexA = 0;
+ return Solving_Continue;
+
+ }
+
+ }
+
+
+
+ if(simStateB->mAIRefIndex != PhysicsAIRef::redBrickPhizStatic) // redBrickPhizMoveableAnim? -> don't think we need to
+ {
+ if(vehicle->mUsingInCarPhysics)
+ {
+ return Solving_Aborted;
+ }
+ else
+ {
+ return Solving_Continue;
+ }
+ }
+
+
+ }
+ else
+ {
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizFence)
+ {
+ // the old tip test
+ rmt::Vector worldUp = GetWorldPhysicsManager()->mWorldUp;
+
+ float cos10 = 0.9848f;
+ //if(worldUp.DotProduct(vehicle->mVehicleUp) < cos10)
+ {
+ // will this work??
+ inCollision.mIndexB = 0;
+ return Solving_Continue;
+
+ }
+
+ int stophere = 1;
+ }
+
+
+ if(simStateA->mAIRefIndex != PhysicsAIRef::redBrickPhizStatic)
+ {
+ if(vehicle->mUsingInCarPhysics)
+ {
+ return Solving_Aborted;
+ }
+ else
+ {
+ return Solving_Continue;
+ }
+ }
+ }
+
+
+ /*
+ // if this is a wheel, but the thing we hit is a vehicle ground plane, then we want to ignore it.
+ if(carisA)
+ {
+ if(simStateB->mAIRefIndex == vehicle->GetGroundPlaneAIRef() ||
+ simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveable) // new - do we want to keep this?
+ {
+ return Solving_Aborted;
+ }
+ }
+ else
+ {
+ if(simStateA->mAIRefIndex == vehicle->GetGroundPlaneAIRef() ||
+ simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveable) // new - do we want to keep this?
+ {
+ return Solving_Aborted;
+ }
+
+ }
+ */
+
+ // recall:
+ // mPositionA = mPositionB + mNormal * mDistance
+
+ // SG::phizSim.mCollisionDistanceCGS;
+
+ // amount we want to keep in collision
+ // TODO - even want to do this?
+ //float slightlyColliding = (SG::phizSim.mCollisionDistanceCGS * 0.1f) * 0.01f; // last factor to make sure we're in meters...
+
+ //float slightlyColliding = (SG::phizSim.mCollisionDistanceCGS * 1.0f) * 0.01f; // last factor to make sure we're in meters...
+
+ // TODO
+ // hack to fix for no phizsim
+ float collisionDistanceCGS = 2.0f;
+
+ //float slightlyColliding = (SG::phizSim.mCollisionDistanceCGS * 1.0f) * 0.01f; // last factor to make sure we're in meters...
+ float slightlyColliding = (collisionDistanceCGS * 1.0f) * 0.01f; // last factor to make sure we're in meters...
+
+ // TODO - choose right value here based on coefficient of restituion blah blah
+ // initial random guessof 0.1 was so low it was never used since the impulses
+ // kept shit apart.
+ //
+ // hmmmm... maybe want to use that? but then how does suspension compress?
+ //
+
+
+ // if we hit something sideways with the wheel, just apply impulse
+ float cos80 = 0.1736f;
+ float cos70 = 0.342f;
+
+ // TODO - this isn't nearly adequate!
+
+ // TODO
+ // temp fix
+
+ rmt::Vector worldUp = GetWorldPhysicsManager()->mWorldUp;
+
+ // new test
+ //if(vehicle->mPercentOfTopSpeed > 0.3f)
+ if(vehicle->mPercentOfTopSpeed > 0.1f)
+ {
+ int wheelIndex = vehicle->mJointIndexToWheelMapping[jointIndex];
+ rAssert(wheelIndex >= 0);
+ rAssert(wheelIndex < 5);
+
+ rmt::Vector normVel = vehicle->mSuspensionPointVelocities[wheelIndex];
+
+ normVel.NormalizeSafe();
+ float cos45 = 0.7071f;
+ if(normalPointingAtCar.DotProduct(normVel) > cos45)
+ //if(normalPointingAtCar.DotProduct(normVel) > cos70)
+ {
+ // the collision is pointing the way we're going so fuck it
+ //char buffy[128];
+ //sprintf(buffy, "aborting wheel-static collision fix - pointing too much along velocity\n");
+ //rDebugPrintf(buffy);
+
+
+ vehicle->SetNoDamperDownFlagOnWheel(wheelIndex);
+
+ return Solving_Aborted;
+ }
+ }
+
+
+
+ float cos55 = 0.5736f;
+
+ //if( rmt::Fabs((inCollision.mNormal).DotProduct(vehicle->mVehicleUp)) < cos70 || // recall - this is the new line
+ if( rmt::Fabs((inCollision.mNormal).DotProduct(vehicle->mVehicleUp)) < cos55 || // recall - this is the new line
+ worldUp.DotProduct(vehicle->mVehicleUp) < cos80) // leave this on at 80
+ {
+
+ //char buffy[128];
+ //sprintf(buffy, "aborting wheel-static collision fix - too horizontal\n");
+ //rDebugPrintf(buffy);
+
+ int wheelIndex = vehicle->mJointIndexToWheelMapping[jointIndex];
+ rAssert(wheelIndex >= 0);
+ rAssert(wheelIndex < 5);
+
+ vehicle->SetNoDamperDownFlagOnWheel(wheelIndex);
+
+ // temp
+ // TODO - something?
+ return Solving_Aborted;
+
+ // let collision solver just apply some impulse
+ //
+ // TODO - scale down impulse??
+
+ mWheelSidewaysHit = true;
+
+ if(carisA)
+ {
+ inCollision.mIndexA = 0;
+ }
+ else
+ {
+ inCollision.mIndexB = 0;
+ }
+
+ return CollisionSolverAgent::PreCollisionEvent(inCollision, inPass);
+
+ }
+
+
+
+
+ if(inCollision.mDistance < slightlyColliding)
+ {
+ // need to move along normal by:
+ float fixAlongCollisionNormal = slightlyColliding - inCollision.mDistance;
+
+ float fixAlongSuspensionAxis = fixAlongCollisionNormal / rmt::Fabs( (vehicle->mVehicleUp).DotProduct(inCollision.mNormal) );
+
+ // now fixAlongSuspensionAxis is the object space y offset that we should correct to
+
+ // this will return true for bottom out
+ if(vehicle->SetWheelCorrectionOffset(jointIndex, fixAlongSuspensionAxis, normalPointingAtCar, groundContactPoint))
+ //if(fixAlongSuspensionAxis > 2.0f * physicsVehicle->GetWheelByJoint(jointIndex)->GetSuspensionLimit() )
+ {
+ // we've bottomed out
+ //
+ // so also let solver apply impulse
+ mBottomedOut = true; // TODO - should also store the joint! - ...later...why?
+
+ // if we've bottomed out, we only want to transfer impulse to the chassis if
+ // it's more or less up
+
+ const float cos20 = 0.9397f;
+
+ //if( rmt::Fabs((inCollision.mNormal).DotProduct(vehicle->mVehicleUp)) > cos20 )
+ if(normalPointingAtCar.DotProduct(vehicle->mVehicleUp) > cos20)
+ {
+ /*
+ if(carisA)
+ {
+ inCollision.mIndexA = 0;
+ }
+ else
+ {
+ inCollision.mIndexB = 0;
+ }
+
+ return CollisionSolverAgent::PreCollisionEvent(inCollision, inPass);
+
+
+ april 11, 2003
+ this makes an absolute world of difference in jumps!!!!
+
+
+ */
+
+ return Solving_Aborted;
+ }
+ else
+ {
+ return Solving_Aborted;
+ }
+
+ }
+
+ return Solving_Aborted;
+
+ }
+
+
+
+ } // end of the IsJointAWheel block...
+
+
+ // if we got here we are the chassis hitting a static or our own groudn plane
+ if(carisA)
+ {
+ if(simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizVehicleGroundPlane)
+ {
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = vehicle->mVehicleUp.DotProduct(up);
+
+ //float cos10 = 0.9848f;
+ float cos16 = 0.9613f;
+
+ if(vehicle->mAirBorn && tip > cos16)
+ {
+ //int stophere = 1;
+ return Solving_Aborted;
+ }
+
+
+ //vehicle->mChassisHitGroundPlaneThisFrame = true;
+ }
+ }
+ else
+ {
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizVehicleGroundPlane)
+ {
+
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = vehicle->mVehicleUp.DotProduct(up);
+
+ //float cos10 = 0.9848f;
+ float cos16 = 0.9613f;
+
+ if(vehicle->mAirBorn && tip > cos16)
+ {
+ //int stophere = 1;
+ return Solving_Aborted;
+ }
+
+
+
+ //vehicle->mChassisHitGroundPlaneThisFrame = true;
+ }
+
+ }
+
+
+
+
+ if(inCollision.mDistance < -0.5f)
+ //if(inCollision.mDistance < -0.25f)
+ //if(inCollision.mDistance < -0.1f)
+ {
+ //if(vehicle->mVehicleID == VehicleEnum::FAMIL_V)
+ //{
+ // int stophere = 1; // motherfucking goddamn data conditions on breakpoints not working!!
+ //}
+
+ // greg
+ // jan 15, 2003
+
+ // did you know, that to penetrate something 0.5m in 32ms, you only have to be going 56 kmh ?
+
+ // live and learn
+
+ //return Solving_Aborted;
+ //inCollision.mDistance = -0.1f; // fuck
+ }
+
+ // this is just chassis hitting something here...
+
+ // test - if all wheels are out of collision, transffer impulse to root...?
+
+
+ }
+ return Solving_Continue;
+}
+
+
+
+Solving_Answer RedBrickCollisionSolverAgent::EndObjectCollision(SimState* inSimState, int inIndex)
+{
+
+ if(inSimState->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ Vehicle* vehicle = (Vehicle*)(inSimState->mAIRefPointer);
+
+ if(vehicle->IsAFlappingJoint(inIndex))
+ {
+ //vehicle->mSimStateArticulated->GetSimulatedObject()->ResetCache();
+ }
+ }
+
+ return CollisionSolverAgent::EndObjectCollision(inSimState, inIndex);
+}
+
+
+
+Solving_Answer RedBrickCollisionSolverAgent::TestCache(SimState* inSimState, int inIndex)
+{
+ // this is called everytime an impulse is added on an object. Getting the cache give information
+
+/*
+ if(inSimState->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ // car on car
+
+ //static float magic = 1.0f;
+
+ // fun test
+ //impulse.Scale(magic);
+
+ //int stophere = 1;
+
+ if( ((Vehicle*)(inSimState->mAIRefPointer))->mVehicleType == VT_TRAFFIC )
+ {
+ static float magic = 5.0f;
+
+ // fun test
+ impulse.Scale(magic);
+
+ SimulatedObject* simobj = inSimState->GetSimulatedObject(-1);
+
+ rmt::Vector cachev, cachew;
+ simobj->GetCollisionCache(cachev, cachew, -1); //retrieve the collision cache of the physical object.
+
+
+ if (cachev.MagnitudeSqr() > maxDv2)
+ {
+ bool fuck = true;
+ }
+
+
+ }
+ }
+
+*/
+
+
+#if 0
+ SimulatedObject* simobj = inSimState->GetSimulatedObject(-1);
+
+
+ if ( simobj && (simobj->Type() == RigidObjectType || simobj->Type() == ArticulatedObjectType) )
+ {
+ /*
+ static float thresholdfactor1 = 100.0f;
+ Vector currentv, vcmv, cachev, cachew;
+ currentv = inSimState->GetLinearVelocity(); //current speed of the object
+ if (inSimState->GetVirtualCM()==NULL)
+ vcmv.Clear();
+ else
+ vcmv = inSimState->GetVirtualCM()->GetVelocity(); //The current speed of the vcm of the object
+ simobj->GetCollisionCache(cachev, cachew, -1); //retrieve the collision cache of the physical object.
+ cachev.Add(vcmv); //The combined vcm's speed and the cached speed.
+ float testSpeed = Max(currentv.DotProduct(currentv),Sqr(simobj->GetMinimumLinSpeed()));
+ if ( cachev.DotProduct(cachev) > thresholdfactor1*testSpeed )
+ {
+ //inSimState->SetControl(simSimulationCtrl);
+ return Solving_Aborted;
+ }
+ */
+ Vector cachev, cachew;
+ simobj->GetCollisionCache(cachev, cachew, -1); //retrieve the collision cache of the physical object.
+
+ static float maxDv2 = 27.0f*27.0f;
+ if (cachev.MagnitudeSqr() > maxDv2)
+ {
+ bool fuck = true;
+ }
+ }
+#endif
+
+ /*
+ if (simobj && simobj->Type() == ArticulatedObjectType)
+ {
+ //Looks if there is a vcm installed on this joint:
+ SimStateArticulated *simStateArt = (SimStateArticulated*)inSimState;
+ JointVCMpArray *vcma = &simStateArt->mVirtualCMList;
+ for (int i=0 ; i<vcma->GetSize() ; i++)
+ {
+ JointVirtualCM *vcm = vcma->GetAt(i);
+ if (vcm->GetIndex() == inIndex)
+ {
+ static float thresholdfactor2=100.0f;
+ Vector currentv, vcmv, cachev, cachew;
+ simStateArt->GetVelocity(vcm->GetPosition(), currentv, inIndex);
+ vcmv = vcm->GetVelocity();
+ simobj->GetCollisionCache(cachev, cachew, inIndex);
+ if ( cachev.DotProduct(cachev) > thresholdfactor2*currentv.DotProduct(currentv) )
+ {
+ simStateArt->SetControl(simSimulationCtrl);
+ return Solving_Aborted;
+ }
+ }
+ }
+ }
+ */
+ return Solving_Continue;
+}
+
diff --git a/game/code/worldsim/redbrick/redbrickcollisionsolveragent.h b/game/code/worldsim/redbrick/redbrickcollisionsolveragent.h
new file mode 100644
index 0000000..ed88531
--- /dev/null
+++ b/game/code/worldsim/redbrick/redbrickcollisionsolveragent.h
@@ -0,0 +1,57 @@
+/*===========================================================================
+ redbrickcollisionsolveragent.h
+
+ created Jan 28, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+===========================================================================*/
+
+#ifndef _REDBRICKCOLLISIONSOLVERAGENT_H
+#define _REDBRICKCOLLISIONSOLVERAGENT_H
+
+#include <worldsim/worldcollisionsolveragent.h>
+
+#include <simcommon/tlist.hpp>
+#include <simcollision/collisionanalyser.hpp>
+#include <simcollision/collisionanalyserdata.hpp>
+
+using namespace sim;
+
+// TODO - looking for the best place to put these
+// a necessary part of the RedBrick interface
+
+// TBJ moved 'em to <worldsim/physicsairef.h>
+//
+//enum RedBrickPhizAITypes { redBrickPhizDefault = 0, redBrickVehicle, redBrickPhizVehicleGroundPlane, redBrickPhizStatic, redBrickPhizMoveable, redBrickPhizMoveableAnim, redBrickPhizLast }; // phizGround, phizStatic, phizMoveableAnim, phizMoveable, phizCamera, phizLast };
+
+class RedBrickCollisionSolverAgent : public WorldCollisionSolverAgent
+{
+public:
+
+ RedBrickCollisionSolverAgent();
+ ~RedBrickCollisionSolverAgent();
+
+ // the key method to override
+ Solving_Answer PreCollisionEvent(Collision& inCollision, int inPass);
+ Solving_Answer TestImpulse(rmt::Vector& mImpulse, Collision& inCollision);
+ Solving_Answer TestCache(SimState* inSimState, int inIndex);
+
+ Solving_Answer EndObjectCollision(SimState* inSimState, int inIndex);
+
+ Solving_Answer CarOnCarPreTest(Collision& inCollision, int inPass);
+
+ virtual void ResetCollisionFlags();
+
+ bool mBottomedOut;
+ float mBottomedOutScale;
+
+ bool mWheelSidewaysHit;
+ float mWheelSidewaysHitScale;
+
+
+};
+
+#endif // _REDBRICKCOLLISIONSOLVERAGENT_H
diff --git a/game/code/worldsim/redbrick/rootmatrixdriver.cpp b/game/code/worldsim/redbrick/rootmatrixdriver.cpp
new file mode 100644
index 0000000..ccaf57e
--- /dev/null
+++ b/game/code/worldsim/redbrick/rootmatrixdriver.cpp
@@ -0,0 +1,29 @@
+/*===========================================================================
+ rootmatrixdriver.cpp
+
+ created Jan 24, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+
+#include <poser/pose.hpp>
+#include <poser/poseengine.hpp>
+#include <poser/posedriver.hpp>
+#include <poser/joint.hpp>
+
+#include <worldsim/redbrick/rootmatrixdriver.h>
+
+//------------------------------------------------------------------------
+void RootMatrixDriver::Update(poser::Pose* pose)
+{
+ poser::Joint* j = pose->GetJoint(0);
+ //rmt::Matrix m = j->GetObjectMatrix();
+ //m.Mult(*mRootMatrix);
+ j->SetWorldMatrix(*mRootMatrix); // recall, mRootMatrix is a pointer to mTransform of the physics vehicle
+}
+
diff --git a/game/code/worldsim/redbrick/rootmatrixdriver.h b/game/code/worldsim/redbrick/rootmatrixdriver.h
new file mode 100644
index 0000000..bbebab8
--- /dev/null
+++ b/game/code/worldsim/redbrick/rootmatrixdriver.h
@@ -0,0 +1,77 @@
+/*===========================================================================
+ rootmatrixdriver.h
+
+ created Jan 24, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#ifndef _ROOTMATRIXDRIVER_H
+#define _ROOTMATRIXDRIVER_H
+
+
+#include <poser/pose.hpp>
+#include <poser/poseengine.hpp>
+#include <poser/posedriver.hpp>
+
+/*
+class PoseDriver: public tEntity
+{
+public:
+
+ PoseDriver();
+
+ virtual int GetMinimumJointIndex() const
+ { return 0; }
+ virtual int GetPriority() const
+ { return PRIORITY_DEFAULT; }
+
+ bool IsEnabled() const
+ { return m_IsEnabled; }
+ void SetIsEnabled(bool isEnabled)
+ { m_IsEnabled = isEnabled; }
+
+ virtual void Advance(float dt) = 0;
+ virtual void Update(Pose* pose) = 0;
+
+protected:
+
+ virtual ~PoseDriver();
+
+private:
+
+ bool m_IsEnabled;
+};
+*/
+
+
+class RootMatrixDriver : public poser::PoseDriver
+{
+public:
+ RootMatrixDriver(rmt::Matrix* inRootMatrix) : mRootMatrix(inRootMatrix) {}
+ virtual int GetMinimumJointIndex() const { return 0; }
+ virtual int GetPriority() const { return 0; }
+ virtual void Advance(float dt) {}
+ virtual void Update(poser::Pose* pose);
+
+ // move definition to cpp file
+ /*
+ {
+ poser::Joint* j = pose->GetJoint(0);
+ rmt::Matrix m = j->GetObjectMatrix();
+ m.Mult(*mRootMatrix);
+ j->SetWorldMatrix(m);
+ }
+ */
+
+private:
+ rmt::Matrix* mRootMatrix;
+};
+
+#endif // #ifndef _ROOTMATRIXDRIVER_H
+
+
diff --git a/game/code/worldsim/redbrick/suspensionjointdriver.cpp b/game/code/worldsim/redbrick/suspensionjointdriver.cpp
new file mode 100644
index 0000000..d9bca77
--- /dev/null
+++ b/game/code/worldsim/redbrick/suspensionjointdriver.cpp
@@ -0,0 +1,130 @@
+/*===========================================================================
+ suspensionjointdriver.cpp
+
+ created Feb 1, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#include <poser/pose.hpp>
+#include <poser/poseengine.hpp>
+#include <poser/posedriver.hpp>
+#include <poser/joint.hpp>
+
+#include <worldsim/redbrick/suspensionjointdriver.h>
+#include <worldsim/redbrick/wheel.h>
+
+
+
+//------------------------------------------------------------------------
+SuspensionJointDriver::SuspensionJointDriver(Wheel* wheel, int jointIndex)
+{
+ mWheel = wheel;
+ mJointIndex = jointIndex;
+
+}
+
+
+//------------------------------------------------------------------------
+SuspensionJointDriver::~SuspensionJointDriver()
+{
+ //
+}
+
+
+
+//------------------------------------------------------------------------
+void SuspensionJointDriver::Update(poser::Pose* pose)
+{
+ // process results of last time's collision detection and solving...
+
+
+ //mWheel->ResolveOffset(); // I think this call is redundant - bug
+
+ // now just use mYOffset
+ //float yOffset = mWheel->mYOffset; // need to reapply this because we called PoseEngine::Begin and overwrote the suspension results...
+
+
+ //!
+ // BUT
+ //
+ // this mYOffset is from the bottomed out location set in precollisionprep, not the neutral point
+ //
+ // try not calling PoseEngine::Begin in PostUpdate
+
+
+
+
+
+
+
+ poser::Joint* joint = pose->GetJoint(mJointIndex);
+
+ // TODO - is this call inefficient?
+ // I'm gonna have to make a copy of the matrix at some point so I can change some values....
+ rmt::Matrix matrix = joint->GetObjectMatrix();
+
+
+
+
+//Matrix& FillRotateXYZ(float anglex, float angley, float anglez);
+
+ /*
+
+
+ rmt::Matrix rot;
+ rot.Identity();
+ rot.FillRotateX(mWheel->mRotAngle * 100.0f);
+
+ matrix.Mult(rot);
+
+
+ */
+
+
+ //matrix.m[3][1] += yOffset;
+
+ // !! TODO - safe to just apply this?
+ //matrix.m[3][1] += mWheel->mPhysicsVehicleOwner->mGravitySettleYCorrection; // this is insignificant
+
+ if(mWheel->mSteerWheel)
+ {
+ // this is one of the two front wheels, so turn it
+ float angle = mWheel->mWheelTurnAngle;
+
+ // should this be done before the addition of the yOffset?
+ // shouldn't matter too much....
+ matrix.FillRotateXYZ(mWheel->mCumulativeRot, mWheel->mWheelTurnAngle, 0.0f);
+
+ }
+ else
+ {
+ matrix.FillRotateX(mWheel->mCumulativeRot);
+ }
+
+ joint->SetObjectMatrix(matrix);
+
+ /*
+
+ rmt::Vector trans = joint->GetObjectTranslation();
+
+ trans.y += yOffset;
+
+ joint->SetObjectTranslation(trans);
+
+ if(mWheel->GetNum() < 1)
+ {
+ // this is one of the two front wheels, so rotate it
+
+ const rmt::Matrix& GetObjectMatrix() const
+ { return GetObjectTransform().GetMatrix(); }
+ void SetObjectMatrix(const rmt::Matrix& matrix);
+
+
+ }
+ */
+}
diff --git a/game/code/worldsim/redbrick/suspensionjointdriver.h b/game/code/worldsim/redbrick/suspensionjointdriver.h
new file mode 100644
index 0000000..43d0902
--- /dev/null
+++ b/game/code/worldsim/redbrick/suspensionjointdriver.h
@@ -0,0 +1,71 @@
+/*===========================================================================
+ suspensionjointdriver.h
+
+ created Feb 1, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#ifndef _SUSPENSIONJOINTDRIVER_H
+#define _SUSPENSIONJOINTDRIVER_H
+
+
+#include <poser/pose.hpp>
+#include <poser/poseengine.hpp>
+#include <poser/posedriver.hpp>
+
+class Wheel;
+
+/*
+class PoseDriver: public tEntity
+{
+public:
+
+ PoseDriver();
+
+ virtual int GetMinimumJointIndex() const
+ { return 0; }
+ virtual int GetPriority() const
+ { return PRIORITY_DEFAULT; }
+
+ bool IsEnabled() const
+ { return m_IsEnabled; }
+ void SetIsEnabled(bool isEnabled)
+ { m_IsEnabled = isEnabled; }
+
+ virtual void Advance(float dt) = 0;
+ virtual void Update(Pose* pose) = 0;
+
+protected:
+
+ virtual ~PoseDriver();
+
+private:
+
+ bool m_IsEnabled;
+};
+*/
+
+
+class SuspensionJointDriver : public poser::PoseDriver
+{
+public:
+ SuspensionJointDriver(Wheel* wheel, int jointIndex);
+ ~SuspensionJointDriver();
+
+ virtual void Advance(float dt) {}
+ virtual void Update(poser::Pose* pose);
+
+private:
+ Wheel* mWheel;
+ int mJointIndex;
+
+};
+
+#endif // #ifndef _SUSPENSIONJOINTDRIVER_H
+
+
diff --git a/game/code/worldsim/redbrick/trafficbodydrawable.cpp b/game/code/worldsim/redbrick/trafficbodydrawable.cpp
new file mode 100644
index 0000000..56725ef
--- /dev/null
+++ b/game/code/worldsim/redbrick/trafficbodydrawable.cpp
@@ -0,0 +1,82 @@
+#include <worldsim/redbrick/trafficbodydrawable.h>
+#include <p3d/shader.hpp>
+#include <debug/profiler.h>
+
+TrafficBodyDrawable::TrafficBodyDrawable()
+{
+ mBodyPropDrawable = NULL;
+ mBodyShader = NULL;
+ mDesiredColour.Set( 255, 255, 255, 255 );
+ mFading = false;
+}
+
+TrafficBodyDrawable::~TrafficBodyDrawable()
+{
+ if( mBodyPropDrawable != NULL )
+ {
+ mBodyPropDrawable->Release();//delete mBodyPropDrawable;
+ mBodyPropDrawable = NULL;
+ }
+ if( mBodyShader != NULL )
+ {
+ mBodyShader->Release();
+ mBodyShader = NULL;
+ }
+}
+void TrafficBodyDrawable::SetBodyPropDrawable( tDrawable* drawable )
+{
+ tRefCounted::Assign( mBodyPropDrawable, drawable );
+}
+void TrafficBodyDrawable::SetBodyShader( tShader* shader )
+{
+ tRefCounted::Assign( mBodyShader, shader );
+}
+
+///////////////////////////////////////////////////
+// Implementing tDrawable
+void TrafficBodyDrawable::Display()
+{
+ BEGIN_PROFILE("TrafficBodyDrawable::Display")
+ rAssert( mBodyPropDrawable != NULL );
+ if( mBodyPropDrawable != NULL )
+ {
+ if( mBodyShader != NULL )
+ {
+ // display with desired colour first, then we'll go over it with a gloss
+ // put the old settings back
+ if(!mFading)
+ {
+ mBodyShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_NONE );
+ }
+ else
+ {
+ mBodyShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ }
+ mBodyShader->SetColour( PDDI_SP_DIFFUSE, mDesiredColour );
+ mBodyShader->SetInt( PDDI_SP_EMISSIVEALPHA, mFadeAlpha );
+ mBodyPropDrawable->Display();
+
+ pddiColour white( 255,255,255,255 );
+ mBodyShader->SetColour( PDDI_SP_DIFFUSE, white );
+ mBodyShader->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ALPHA );
+ mBodyShader->SetInt( PDDI_SP_EMISSIVEALPHA, mFadeAlpha );
+ mBodyShader->SetInt( PDDI_SP_ALPHATEST, 1 );
+ mBodyShader->SetFloat( PDDI_SP_ALPHACOMPARE_THRESHOLD, (250.0f * (float(mFadeAlpha) / 255.0f)) / 255.0f );
+ mBodyPropDrawable->Display();
+
+
+ mBodyShader->SetInt( PDDI_SP_ALPHATEST, 0 );
+ }
+ else
+ {
+ mBodyPropDrawable->Display();
+ }
+ }
+ END_PROFILE("TrafficBodyDrawable::Display")
+}
+
+void TrafficBodyDrawable::ProcessShaders(ShaderCallback& callback)
+{
+ rAssert( mBodyPropDrawable != NULL );
+ mBodyPropDrawable->ProcessShaders(callback);
+}
diff --git a/game/code/worldsim/redbrick/trafficbodydrawable.h b/game/code/worldsim/redbrick/trafficbodydrawable.h
new file mode 100644
index 0000000..4ee7594
--- /dev/null
+++ b/game/code/worldsim/redbrick/trafficbodydrawable.h
@@ -0,0 +1,39 @@
+#ifndef TRAFFICBODYDRAWABLE_H
+#define TRAFFICBODYDRAWABLE_H
+
+#include <p3d/drawable.hpp>
+
+class tShader;
+
+class TrafficBodyDrawable :
+ public tDrawable
+{
+public:
+ TrafficBodyDrawable();
+ ~TrafficBodyDrawable();
+
+ void SetBodyPropDrawable( tDrawable* drawable );
+ void SetBodyShader( tShader* shader );
+ void SetDesiredColour( pddiColour colour )
+ {
+ mDesiredColour = colour;
+ }
+ pddiColour GetDesiredColour() const { return mDesiredColour; }
+
+ int mFadeAlpha;
+ bool mFading;
+
+public:
+ ///////////////////////////////////////////////////
+ // Implementing tDrawable
+ virtual void Display();
+ virtual void ProcessShaders(ShaderCallback&);
+
+private:
+ tDrawable* mBodyPropDrawable;
+ tShader* mBodyShader;
+ pddiColour mDesiredColour;
+
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/trafficlocomotion.cpp b/game/code/worldsim/redbrick/trafficlocomotion.cpp
new file mode 100644
index 0000000..20bd77c
--- /dev/null
+++ b/game/code/worldsim/redbrick/trafficlocomotion.cpp
@@ -0,0 +1,1421 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trafficlocomotion.cpp
+//
+// Description: Blahblahblah
+//
+// History: 09/09/2002 + Locomote through intersections -- Dusit Eakkachaichanvet
+// 04/24/2002 + Created -- Greg Mayer
+//
+//=============================================================================
+#include <poser/poseengine.hpp>
+#include <raddebugwatch.hpp>
+#include <radtime.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <worldsim/redbrick/trafficlocomotion.h>
+#include <worldsim/redbrick/vehicle.h>
+
+#include <debug/profiler.h>
+#include <worldsim/worldphysicsmanager.h>
+
+#include <roads/roadmanager.h>
+#include <roads/road.h>
+#include <roads/roadsegment.h>
+#include <roads/roadsegmentdata.h>
+#include <roads/lane.h>
+#include <roads/intersection.h>
+#include <roads/geometry.h>
+
+const float TrafficLocomotion::SECONDS_BETW_HISTORY_UPDATES = 0.005f;
+static const float SECONDS_BETW_CHECKS_FOR_FREE_LANE = 5.0f;
+static const float TRAFFIC_WHEEL_GAP_HACK = 0.1f;
+
+#define FACING_HISTORY
+// Need to use pos history to smooth out motion along curve
+// Fortunately, this also causes the actual (apparent) position
+// to be located further back from the calculated position, so
+// turning looks better.
+// Unfortunately, averaging means we can't stop on a dime,
+// like we want to ...
+#define POS_HISTORY
+
+//------------------------------------------------------------------------
+TrafficLocomotion::TrafficLocomotion(Vehicle* vehicle) :
+ VehicleLocomotion(vehicle),
+ mMyAI( NULL )
+{
+
+ mMyAI = new TrafficAI( vehicle );
+ mMyAI->AddRef();
+
+#ifdef DEBUGWATCH
+ if( vehicle && vehicle->mVehicleType == VT_TRAFFIC )
+ {
+ mMyAI->RegisterAI();
+ }
+#endif
+
+
+ mVehicle = vehicle;
+ mIsInIntersection = false;
+ mCurrWay = -1;
+ mCurrPathLength = 0.0f;
+ mCurrPathLocation = 0.0f;
+ mActualSpeed = 0.0f;
+ mWays = NULL;
+ mNumWays = 0;
+ mSecondsTillCheckForFreeLane = SECONDS_BETW_CHECKS_FOR_FREE_LANE;
+
+ ////////////////////////////////////////////////
+ // HISTORY stuff
+ ////////////////////////////////////////////////
+#ifdef FACING_HISTORY
+ rmt::Vector heading( 0.0f, 0.0f, 0.0f );
+ /*
+ if( mVehicle != NULL )
+ {
+ mVehicle->GetHeading( &heading );
+ }
+ */
+ mFacingHistory.Init( heading );
+#endif
+
+#ifdef POS_HISTORY
+ rmt::Vector pos( 0.0f, 0.0f, 0.0f );
+ mPosHistory.Init( pos );
+#endif
+ ////////////////////////////////////////////////
+
+ mLaneChangeProgress = 0.0f;
+ mLaneChangeDist = 0.0f;
+ mLaneChangingFrom = 0;
+ mOutLaneT = 0.0f;
+
+ mSecondsSinceLastAddToHistory = 0.0f;
+}
+
+
+//------------------------------------------------------------------------
+TrafficLocomotion::~TrafficLocomotion()
+{
+ mMyAI->ReleaseVerified();
+}
+
+void TrafficLocomotion::InitPos( const rmt::Vector& pos )
+{
+#ifdef POS_HISTORY
+ mPosHistory.Init( pos );
+#endif
+
+ mSecondsSinceLastAddToHistory = 0.0f;
+
+ /////////////////////////////////////////////
+ // Adjust ground height (unfortunately we need to do this
+ // because we use pos averaging (so our y value is off)
+
+ mPrevPos = pos;
+
+ rmt::Vector groundPosition, outnorm;
+ bool bFoundPlane = false;
+
+ groundPosition = pos;
+ outnorm.Set( 0.0f, 1.0f, 0.0f );
+
+ GetIntersectManager()->FindIntersection(
+ groundPosition, // IN
+ bFoundPlane, // OUT
+ outnorm, // OUT
+ groundPosition // OUT
+ );
+
+ if( bFoundPlane )
+ {
+ mPrevPos.y = groundPosition.y;
+ }
+ ///////////////////////////////////////////////
+
+}
+
+
+void TrafficLocomotion::InitFacing( const rmt::Vector& facing )
+{
+ rAssert( rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) );
+#ifdef FACING_HISTORY
+ mFacingHistory.Init( facing );
+#endif
+ mSecondsSinceLastAddToHistory = 0.0f;
+
+}
+//=============================================================================
+// TrafficLocomotion::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::Init()
+{
+ mMyAI->Init();
+}
+
+//=============================================================================
+// TrafficLocomotion::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( TrafficAI::Behaviour behaviour,
+// unsigned int behaviourModifiers,
+// Vehicle* vehicle,
+// Lane* lane,
+// unsigned int laneIndex,
+// RoadSegment* segment,
+// unsigned int segmentIndex,
+// float t,
+// float kmh )
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::Init( Vehicle* vehicle,
+ Lane* lane,
+ unsigned int laneIndex,
+ RoadSegment* segment,
+ unsigned int segmentIndex,
+ float t,
+ float mps )
+{
+ mMyAI->Init( mVehicle, lane, laneIndex, segment, segmentIndex, t, mps );
+}
+
+//=============================================================================
+// TrafficLocomotion::InitVehicleAI
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Behaviour behaviour, unsigned int behaviourModifiers, Vehicle* vehicle )
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::InitVehicleAI( Vehicle* vehicle )
+{
+ mMyAI->SetVehicle( vehicle );
+}
+
+//=============================================================================
+// TrafficLocomotion::InitLane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Lane* lane, unsigned int laneIndex, float mps )
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::InitLane( Lane* lane, unsigned int laneIndex, float mps )
+{
+ mMyAI->SetLane( lane );
+ mMyAI->SetLaneIndex( laneIndex );
+ mMyAI->SetAISpeed( mps );
+}
+
+//=============================================================================
+// TrafficLocomotion::InitSegment
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( RoadSegment* segment, unsigned int segmentIndex, float t )
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::InitSegment( RoadSegment* segment, unsigned int segmentIndex, float t )
+{
+ mMyAI->SetSegment( segment );
+ mMyAI->SetSegmentIndex( segmentIndex );
+ mMyAI->SetLanePosition( t );
+ mMyAI->SetIsInIntersection( false );
+ mIsInIntersection = false;
+}
+
+
+//=============================================================================
+// TrafficLocomotion::UpdateVehicleGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::UpdateVehicleGroundPlane()
+{
+ rmt::Vector p, n;
+ p.Set(0.0f, 0.0f, 0.0f);
+ n.Set(0.0f, 1.0f, 0.0f);
+
+ p = mVehicle->GetPosition();
+ p.y -= 5.0f; // just to be safe
+
+
+ mVehicle->mGroundPlaneWallVolume->mPosition = p;
+ mVehicle->mGroundPlaneWallVolume->mNormal = n;
+
+
+ sim::CollisionObject* co = mVehicle->mGroundPlaneSimState->GetCollisionObject();
+ co->PostManualUpdate();
+
+
+}
+
+
+
+//------------------------------------------------------------------------
+void TrafficLocomotion::PreCollisionPrep(bool firstSubstep)
+{
+ UpdateVehicleGroundPlane();
+}
+
+//=============================================================================
+// TrafficLocomotion::PreSubstepUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (mVehicle* mVehicle)
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::PreSubstepUpdate()
+{
+ if( !mMyAI->mIsActive )
+ {
+ return;
+ }
+
+ // perform whatever update here
+}
+
+
+//=============================================================================
+// TrafficLocomotion::PreUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::PreUpdate()
+{
+ poser::Pose* pose = mVehicle->mPoseEngine->GetPose();
+
+ mVehicle->mPoseEngine->Begin( false );
+ //mVehicle->mPoseEngine->Begin(true);
+
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ Wheel* wheel = mVehicle->mWheels[i];
+
+ poser::Joint* joint = pose->GetJoint(mVehicle->mWheelToJointIndexMapping[i]);
+ rmt::Vector trans = joint->GetObjectTranslation();
+
+ //trans.y -= mVehicle->mWheels[i]->mLimit;
+ // TODO - verify that the -= is the thing to do here
+ //trans.y -= wheel->mLimit;
+ trans.y += TRAFFIC_WHEEL_GAP_HACK;
+
+ joint->SetObjectTranslation(trans);
+
+ }
+
+
+}
+
+
+//=============================================================================
+// TrafficLocomotion::UpdateAI
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::UpdateAI(unsigned int ms)
+{
+ // TrafficAI::Update takes timestep in seconds
+ mMyAI->Update(float(ms)/1000.0f);
+}
+
+
+
+void TrafficLocomotion::StopSuddenly( rmt::Vector& pos, rmt::Vector& facing )
+{
+#ifdef POS_HISTORY
+ // re-init history with pos
+ mPosHistory.GetAverage( pos );
+ mPosHistory.Init( pos );
+#else
+ pos = mPrevPos;
+#endif
+
+#ifdef FACING_HISTORY
+ mFacingHistory.GetNormalizedAverage( facing );
+ mFacingHistory.Init( facing );
+#endif
+
+ mSecondsSinceLastAddToHistory = 0.0f;
+
+ // TODO:
+ // When we stop suddenly, our t value is slightly ahead of
+ // us (because we've clobbered our pos history with the
+ // vehicle's current position). When we accelerate again,
+ // we'll dump the t position into the history, causing
+ // the vehicle to lurch forward inertialessly, throwing off
+ // the average.
+ //
+ // So we reset our t value at this point to where we are.
+ // This is rather tricky to do: we could be anywhere...
+ // - If we're on a road segment, that's perfect.
+ // - If we're not on a road segment, but we're in
+ // an intersection, there's no way to guarantee that
+ // our t value is still on the spline (we could already
+ // be in the OUT lane). We need to create a new spline
+ // that still takes us from one road to another, but
+ // we didn't store all this info!! Arrgh...
+ //
+ // A BETTER design (from the ground up), would have
+ // just stored splines from the beginning (even if you're
+ // traversing roads), making sure that this spline is
+ // long enough to contain our vehicle's actual position.
+ // As our t value crawls along a roadsegment, we add it to
+ // our spline. This way when we need to recompute t, we
+ // can just search on our spline for the closest segment
+ // to our current position. The other plus side to this
+ // is that we don't traverse segments differently from
+ // intersections. Everything gets stored in our spline.
+ //
+
+}
+
+
+
+//=============================================================================
+// TrafficLocomotion::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float seconds)
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::Update(float seconds)
+{
+ if( !mMyAI->mIsActive )
+ {
+ return;
+ }
+
+ mSecondsSinceLastAddToHistory += seconds;
+
+BEGIN_PROFILE( "Traffic Locomotion" );
+
+ //mVehicle->mPoseEngine->Begin( false );
+
+ rmt::Vector pos, facing;
+
+ TrafficAI::State state = mMyAI->GetState();
+
+ switch ( state )
+ {
+ case TrafficAI::DEAD:
+ {
+ pos = mPrevPos;
+ #ifdef FACING_HISTORY
+ mFacingHistory.GetNormalizedAverage( facing );
+ #endif
+ }
+ break;
+ case TrafficAI::DRIVING: // fall through
+ case TrafficAI::WAITING_AT_INTERSECTION:
+ {
+ if( mMyAI->mNeedToSuddenlyStop )
+ {
+ StopSuddenly( pos, facing );
+ break;
+ }
+
+ bool stayDoggyStay = false;
+
+ //================================================
+ // DETERMINE t AND CURRENT WAYPOINT OR ROAD SEGMENT
+ // (whichever's applicable)
+ //================================================
+ float t = 0.0f;
+ float pathLength = 0.0f;
+
+ if( !mIsInIntersection )
+ {
+ pathLength = mMyAI->GetLaneLength();
+ t = mMyAI->GetLanePosition();
+ }
+ else
+ {
+ pathLength = mCurrPathLength;
+ t = mCurrPathLocation;
+ }
+
+ float adjustedDt = (GetAISpeed() * seconds) / pathLength;
+ t += adjustedDt;
+
+ while( t > 1.0f )
+ {
+ t -= 1.0f;
+
+ if( !mIsInIntersection ) // we're not inside an intersection
+ {
+ const Road* road = mMyAI->GetLane()->GetRoad();
+ unsigned int segmentIndex = mMyAI->GetSegmentIndex();
+
+ // If there are more segments on this road
+ if ( segmentIndex < (road->GetNumRoadSegments()-1) )
+ {
+ // we have to move ahead a segment
+ segmentIndex++;
+ mMyAI->SetSegmentIndex( segmentIndex );
+ float newLength = mMyAI->GetLaneLength();
+ t *= pathLength / newLength;
+ mMyAI->SetLanePosition(t);
+ pathLength = newLength;
+
+ // ========================
+ // We just updated mMyAI:
+ // - segment & segment index updated to look at next segment
+ // - lane & lane index not updated
+ // - lane position updated with the new segment's t
+ // ========================
+ }
+ // Else we must be at an intersection
+ else
+ {
+ // set up cubic spline
+ bool succeeded = EnterIntersection();
+ // ========================
+ // We just updated mMyAI:
+ // - segment & segment index updated to look at the OUT segment
+ // - lane & lane index updated to look at the OUT lane
+ // - lane position not updated
+ // ========================
+
+ // Set info of first path on spline
+ if( succeeded )
+ {
+ rAssert( mWays != NULL );
+
+ mMyAI->SetIsInIntersection( true );
+ mIsInIntersection = true;
+
+ mCurrWay = 0;
+ mCurrPath.Sub( mWays[mCurrWay+1], mWays[mCurrWay] );
+ mCurrPathLength = mCurrPath.Length(); // *** SQUARE ROOT! ***
+ t *= pathLength / mCurrPathLength;
+ pathLength = mCurrPathLength;
+ }
+ else
+ {
+ stayDoggyStay = true;
+ break;
+ }
+
+ }
+ }
+ else // we ARE inside an intersection
+ {
+ // Since we never spawn in an intersection, we never
+ // get into this case unless we entered via EnterIntersection(),
+ // and t is > 1.0f...
+ // Meaning that at this point, mWays has been populated and
+ // mCurrWay, mCurrPath, and mCurrPathLength are set.
+
+ rAssert( mCurrWay < mNumWays );
+ rAssert( mCurrWay >= 0 );
+
+ // if we still got waypoints to traverse in intersection
+ if( mCurrWay < (mNumWays - 2) )
+ {
+ mCurrWay++;
+
+ rAssert( mWays != NULL );
+
+ mCurrPath.Sub( mWays[mCurrWay+1], mWays[mCurrWay] );
+ float newLength = mCurrPath.Length(); // *** SQUARE ROOT! ***
+ t *= pathLength / newLength;
+ pathLength = newLength;
+ mCurrPathLength = newLength;
+ }
+ else // Else going out of the intersection now
+ {
+ mIsInIntersection = false;
+ mMyAI->SetIsInIntersection( false );
+
+ // mMyAI information has already been set to look
+ // at the out lane/segment/etc.
+ // Must move within the lane...
+ float newLength = mMyAI->GetLaneLength();
+ t *= pathLength / newLength;
+ pathLength = newLength;
+ }
+ }
+ }
+
+ // if t is not a valid number, it means that we encountered
+ // an error inside EnterIntersection()
+ if( stayDoggyStay )
+ {
+ pos = mPrevPos;
+ #ifdef FACING_HISTORY
+ mFacingHistory.GetNormalizedAverage( facing );
+ #endif
+ break; // exit this case
+ }
+
+
+ //================================================
+ // KNOWING t, DETERMINE pos AND facing
+ //================================================
+ rAssert( 0 <= t && t <= 1.0f );
+
+ // Depending on whether we're inside an intersection,
+ // we have different methods for determining pos & facing
+ if( !mIsInIntersection )
+ {
+ mMyAI->SetLanePosition( t );
+ RoadSegment* segment = mMyAI->GetSegment();
+ rAssert( segment );
+
+ segment->GetLaneLocation( mMyAI->GetLanePosition(), mMyAI->GetLaneIndex(), pos, facing );
+ if( !rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) )
+ {
+ facing.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) );
+ }
+ else
+ {
+ mCurrPathLocation = t;
+ rmt::Vector temp = facing = mCurrPath;
+ facing.Scale( 1.0f / mCurrPathLength );
+ rAssert( rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) );
+ temp.Scale(t);
+
+ rAssert( mWays != NULL );
+ pos.Add( mWays[mCurrWay], temp );
+ }
+ }
+ break;
+
+ case TrafficAI::WAITING_FOR_FREE_LANE:
+ {
+ mSecondsTillCheckForFreeLane -= seconds;
+ if( mSecondsTillCheckForFreeLane < seconds )
+ {
+ mSecondsTillCheckForFreeLane = SECONDS_BETW_CHECKS_FOR_FREE_LANE;
+
+ // Transit back to driving next frame, so we check again for
+ // lane availability
+ mMyAI->SetState( TrafficAI::DRIVING );
+ }
+
+ // keep old heading & facing for now (complete and instantaneous stop)
+ pos = mPrevPos;
+ #ifdef FACING_HISTORY
+ mFacingHistory.GetNormalizedAverage( facing );
+ #endif
+ }
+ break;
+
+ case TrafficAI::SPLINING: // fall thru
+ case TrafficAI::LANE_CHANGING:
+ {
+ rAssert( !mIsInIntersection );
+
+ // In TrafficAI, if we're SPLINING, we can be told to stop for something.
+ // (when LANE_CHANGING we don't stop for anything... why? Not sure...)
+ // What happens if we need to suddenly stop here?
+ if( mMyAI->mNeedToSuddenlyStop )
+ {
+ StopSuddenly( pos, facing );
+ break;
+ }
+
+
+ //================================================
+ // DETERMINE t AND CURRENT WAYPOINT OR ROAD SEGMENT
+ // (whichever's applicable)
+ //================================================
+ float pathLength = mCurrPathLength;
+ float t = mCurrPathLocation;
+
+ float adjustedDt = (GetAISpeed() * seconds) / pathLength;
+ t += adjustedDt;
+
+ while( t > 1.0f )
+ {
+ t -= 1.0f;
+
+ // In case we got fed a long "seconds" value and go WAY over
+ // the lane change spline. Then we're not lane changing
+ // anymore (we're on normal road now)
+ if( mMyAI->GetState() != state )
+ {
+ const Road* road = mMyAI->GetLane()->GetRoad();
+ unsigned int segmentIndex = mMyAI->GetSegmentIndex();
+
+ if( segmentIndex < (road->GetNumRoadSegments()-1) )
+ {
+ // we have to move ahead a segment
+ segmentIndex++;
+ mMyAI->SetSegmentIndex( segmentIndex );
+ float newLength = mMyAI->GetLaneLength();
+ t *= pathLength / newLength;
+ mMyAI->SetLanePosition(t);
+ pathLength = newLength;
+
+ // ========================
+ // We just updated mMyAI:
+ // - segment & segment index updated to look at next segment
+ // - lane & lane index not updated
+ // - lane position updated with the new segment's t
+ // ========================
+ }
+ else // if we're out of segments.. we are at an intersection...
+ {
+ // TODO:
+ // we should include intersection code here and deal with
+ // the t-overflow properly... but I'm thinking that's a lot
+ // of code repeat... so I'll get around to it later...
+ t = 0.999f;
+ mMyAI->SetLanePosition(t);
+ }
+ }
+ else // else we are in middle of lane changing...
+ {
+ // At this point, mWays has been populated and
+ // mCurrWay, mCurrPath, and mCurrPathLength are set.
+
+ rAssert( mCurrWay < mNumWays );
+ rAssert( mCurrWay >= 0 );
+
+ // if we still got waypoints to traverse...
+ if( mCurrWay < (mNumWays - 2) )
+ {
+ mCurrWay++;
+
+ rAssert( mWays != NULL );
+
+ mCurrPath.Sub( mWays[mCurrWay+1], mWays[mCurrWay] );
+ float newLength = mCurrPath.Length(); // *** SQUARE ROOT! ***
+ t *= pathLength / newLength;
+ pathLength = newLength;
+ mCurrPathLength = newLength;
+ }
+ else // Else done lane changing now...
+ {
+ mMyAI->SetState( TrafficAI::DRIVING );
+
+ // mMyAI information has already been set to look
+ // at the target lane/segment/etc.
+ // Must move within the lane...
+ float newLength = mMyAI->GetLaneLength();
+ t *= pathLength / newLength;
+ t += mOutLaneT;
+ pathLength = newLength;
+ }
+ }
+ }
+
+ //================================================
+ // KNOWING t, DETERMINE pos AND facing
+ //================================================
+ rAssert( 0 <= t && t <= 1.0f );
+
+ // Depending on whether we're still lane changing...
+ // we have different methods for determining pos & facing
+ if( mMyAI->GetState() != state )
+ {
+ mMyAI->SetLanePosition( t );
+ RoadSegment* segment = mMyAI->GetSegment();
+ rAssert( segment );
+
+ segment->GetLaneLocation( mMyAI->GetLanePosition(), mMyAI->GetLaneIndex(), pos, facing );
+ if( !rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) )
+ {
+ facing.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) );
+ }
+ else
+ {
+ mCurrPathLocation = t;
+ rmt::Vector temp = facing = mCurrPath;
+ facing.Scale( 1.0f / mCurrPathLength );
+ rAssert( rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) );
+ temp.Scale(t);
+
+ rAssert( mWays != NULL );
+ pos.Add( mWays[mCurrWay], temp );
+ }
+ }
+ break;
+
+ case TrafficAI::SWERVING:
+ {
+ // we should be updating TrafficLocomotion if we're swerving because
+ // we should be in PhysicsLocomotion
+ rAssert( false );
+ }
+ default:
+ {
+ rAssert( false );
+ }
+ break;
+ }
+
+
+
+ //================================================
+ // KNOWING pos AND facing, UPDATE VEHICLE
+ //================================================
+
+ // only need mPrevPos for the transition into Intersection
+ // since we can't rely on Vehicle->GetPosition to return us
+ // the RIGHT-ON-THE-GROUND values (Vehicle class does its
+ // own adjustments to bring the car up from ground level)
+ rAssert( rmt::Epsilon( facing.MagnitudeSqr(), 1.0f, 0.005f ) );
+
+#if defined(FACING_HISTORY) || defined(POS_HISTORY)
+
+ // we need to add this many entries
+ int maxCount = (int)(mSecondsSinceLastAddToHistory / SECONDS_BETW_HISTORY_UPDATES);
+
+ if( maxCount > 0 )
+ {
+ float secondsConsumed = (float)(maxCount)*SECONDS_BETW_HISTORY_UPDATES;
+ float tmpT = (secondsConsumed/mSecondsSinceLastAddToHistory);
+
+ // figure out the starting and ending elements to interpolate between
+ #ifdef POS_HISTORY
+ rmt::Vector posStart = mPosHistory.GetLastEntry();
+ rmt::Vector posDir = pos - posStart;
+ rmt::Vector posEnd = posStart + posDir * tmpT;
+ posDir = posEnd - posStart;
+ #endif
+ #ifdef FACING_HISTORY
+ rmt::Vector faceStart = mFacingHistory.GetLastEntry();
+ rmt::Vector faceDir = facing - faceStart;
+ rmt::Vector faceEnd = faceStart + faceDir * tmpT;
+ faceDir = faceEnd - faceStart;
+ #endif
+
+
+ float tIncrement = 1.0f / (float)(maxCount);
+ tmpT = 0.0f;
+ for( int count = 1; count <= maxCount; count++ )
+ {
+ tmpT += tIncrement;
+ #ifdef POS_HISTORY
+ mPosHistory.UpdateHistory( posStart + posDir * tmpT );
+ #endif
+ #ifdef FACING_HISTORY
+ mFacingHistory.UpdateHistory( faceStart + faceDir * tmpT );
+ #endif
+
+ }
+ mSecondsSinceLastAddToHistory -= secondsConsumed;
+
+ }
+
+#endif
+
+ // Histories are updated, now we get the average out of them.
+
+#ifdef FACING_HISTORY
+ mFacingHistory.GetNormalizedAverage( facing );
+#endif
+#ifdef POS_HISTORY
+ mPosHistory.GetAverage( pos );
+#endif
+
+ /////////////////////////////////////////////
+ // Adjust ground height (unfortunately we need to do this
+ // because we use pos averaging (so our y value is off)
+ rmt::Vector groundPosition, outnorm;
+ bool bFoundPlane = false;
+
+ groundPosition = pos;
+ outnorm.Set( 0.0f, 1.0f, 0.0f );
+
+ GetIntersectManager()->FindIntersection(
+ groundPosition, // IN
+ bFoundPlane, // OUT
+ outnorm, // OUT
+ groundPosition // OUT
+ );
+
+ if( bFoundPlane )
+ {
+ pos.y = groundPosition.y;
+ }
+ ///////////////////////////////////////////////
+
+ // compute actual speed
+ rmt::Vector oldPos;
+ oldPos = mPrevPos;
+ mActualSpeed = (pos - oldPos).Magnitude() / seconds;
+
+ // Update vehicle transform...
+ rmt::Matrix newTransform;
+ newTransform.Identity();
+
+ rmt::Vector target;
+ target = pos;
+ target.Add( facing );
+ rmt::Vector up = UpdateVUP( pos, target );
+ newTransform.FillTranslate(pos);
+ newTransform.FillHeading(facing, up);
+ mVehicle->TrafficSetTransform(newTransform);
+
+ // Pivot the front wheels...
+ PivotFrontWheels( facing );
+
+ mPrevPos = pos;
+
+END_PROFILE( "Traffic Locomotion" );
+}
+
+//=============================================================================
+// TrafficLocomotion::PostUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void TrafficLocomotion::PostUpdate()
+{
+ if( !mMyAI->mIsActive )
+ {
+ return;
+ }
+ // make sure values are set for wheel rendering info...
+
+ // set 'artificial suspension point velocities
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ rmt::Vector velocity = mVehicle->mVehicleFacing;
+ velocity.Scale( mActualSpeed );
+ mVehicle->mSuspensionPointVelocities[i] = velocity;
+ }
+ mVehicle->mVelocityCM = mVehicle->mVehicleFacing;
+ mVehicle->mVelocityCM.Scale( mActualSpeed );
+
+ // update mSimStateArticulated speed here?
+ // hmmm...
+ rmt::Vector& linearVelocity = mVehicle->mSimStateArticulated->GetLinearVelocity();
+ linearVelocity = mVehicle->mVelocityCM;
+
+ mVehicle->mSpeed = mActualSpeed;
+
+
+}
+
+//////////////////////////////////////////////////////////////////////
+//////////////////////////// PRIVATES ////////////////////////////////
+
+void TrafficLocomotion::FindOutLane (
+ const Lane* inLane,
+ unsigned int inLaneIndex,
+ Lane*& outLane,
+ unsigned int& outLaneIndex )
+{
+ rAssert( inLane != NULL );
+
+ const Road* inRoad = inLane->GetRoad();
+ rAssert( inRoad != NULL );
+
+ Intersection* intersection =
+ (Intersection*) inRoad->GetDestinationIntersection();
+ rAssert( intersection );
+
+ Road* outRoad = NULL;
+ outLane = NULL;
+ outLaneIndex = 0;
+
+ switch( mMyAI->DecideTurn() )
+ {
+ case TrafficAI::LEFT:
+ {
+ intersection->GetLeftTurnForTraffic(
+ *inRoad, inLaneIndex, outRoad, outLane, outLaneIndex );
+ }
+ break;
+ case TrafficAI::RIGHT:
+ {
+ intersection->GetRightTurnForTraffic(
+ *inRoad, inLaneIndex, outRoad, outLane, outLaneIndex );
+ }
+ break;
+ case TrafficAI::STRAIGHT:
+ {
+ intersection->GetStraightForTraffic(
+ *inRoad, inLaneIndex, outRoad, outLane, outLaneIndex );
+ }
+ break;
+ default:
+ {
+ rAssert( false );
+ }
+ }
+}
+
+
+void TrafficLocomotion::BuildCurve (
+ RoadSegment* inSegment,
+ unsigned int inLaneIndex,
+ RoadSegment* outSegment,
+ unsigned int outLaneIndex )
+{
+ rAssert( inSegment != NULL );
+ rAssert( outSegment != NULL );
+
+ // get the start pos & dir
+ rmt::Vector startPos, startDir;
+ inSegment->GetLaneLocation( 1.0f, inLaneIndex, startPos, startDir );
+ //startDir.y = 0.0f;
+ if( !rmt::Epsilon( startDir.MagnitudeSqr(), 1.0f, 0.001f ) )
+ {
+ startDir.Normalize(); // *** SQUARE ROOT! ***
+ }
+
+ // get end pos & dir
+ rmt::Vector endPos, endDir;
+ outSegment->GetLaneLocation( 0.0f, outLaneIndex, endPos, endDir );
+ //endDir.y = 0.0f;
+ if( !rmt::Epsilon( endDir.MagnitudeSqr(), 1.0f, 0.001f ) )
+ {
+ endDir.Normalize(); // *** SQUARE ROOT! ***
+ }
+
+ // get intermediate points
+ rmt::Vector startEndTemp, p2, p4;
+ startEndTemp.Sub( endPos, startPos );
+ float distFromStartOrEnd = startEndTemp.Length() / 3.0f; // *** SQUARE ROOT! ***
+ p2 = startPos + startDir * distFromStartOrEnd;
+ p4 = endPos - endDir * distFromStartOrEnd;
+
+ // Now we are ready to gather together our points and send it
+ // to our "curve" finder.
+ rmt::Vector pts [4];
+ pts[0] = startPos;
+ pts[1] = p2;
+ pts[2] = p4;
+ pts[3] = endPos;
+
+ mSplineMaker.SetControlPoint( pts[0], 0 );
+ mSplineMaker.SetControlPoint( pts[1], 1 );
+ mSplineMaker.SetControlPoint( pts[2], 2 );
+ mSplineMaker.SetControlPoint( pts[3], 3 );
+
+/*
+#if defined(RAD_TUNE) || defined(RAD_DEBUG)
+
+ const Road* inRoad = inSegment->GetRoad();
+ rAssert( inRoad );
+
+ Intersection* intersection =
+ (Intersection*) inRoad->GetDestinationIntersection();
+ rAssert( intersection );
+
+ // NOTE:
+ // If you hate this assert, you can comment it out locally... FOR NOW...
+ // Put it back in once all y-value mismatch errors are gone.
+ // In other words, once hell freezes over.
+ rmt::Vector intersectionLoc;
+ intersection->GetLocation( intersectionLoc );
+ char baseMsg[1000];
+ sprintf( baseMsg,
+ "\nMismatching y-values at intersection (%f,%f,%f).\n"
+ " Check if y values are same for all IN & OUT road segments attached\n"
+ " to this intersection. Check hypergraph to see if the roadnodes leading\n"
+ " IN and OUT of this intersection contain all the proper roadsegments.\n"
+ " If you skip this, Traffic cars will \"hop\" when they transit through\n"
+ " the intersection between the road segments with mismatching y values.\n"
+ " Better to report error to me (Dusit) or Sheik. Preferrably Sheik.\n\n",
+ intersectionLoc.x,
+ intersectionLoc.y,
+ -1*intersectionLoc.z );
+ rAssert( strlen(baseMsg) < 1000 );
+
+ float ep = 0.001f;
+ if( !( rmt::Epsilon( pts[0].y, pts[1].y, ep ) &&
+ rmt::Epsilon( pts[0].y, pts[2].y, ep ) &&
+ rmt::Epsilon( pts[0].y, pts[3].y, ep ) ) )
+ {
+ rTunePrintf( baseMsg );
+ }
+#endif
+*/
+
+ mSplineMaker.GetCubicBezierCurve( mWays, mNumWays );
+ rAssert( mWays != NULL );
+ rAssert( mNumWays == CubicBezier::MAX_CURVE_POINTS );
+}
+
+
+
+bool TrafficLocomotion::EnterIntersection()
+{
+ const Lane* inLane = mMyAI->GetLane();
+ rAssert( inLane != NULL );
+ RoadSegment* inSegment = mMyAI->GetSegment();
+ rAssert( inSegment != NULL );
+ const unsigned int inLaneIndex = mMyAI->GetLaneIndex();
+
+ // ==============================================
+ // Find OUT LANE and OUT LANE INDEX
+ // ==============================================
+
+ Lane* outLane = NULL;
+ unsigned int outLaneIndex = 0;
+
+ FindOutLane( inLane, inLaneIndex, outLane, outLaneIndex );
+
+ // if absolutely can't find a road, transit to waiting state
+ if( outLane == NULL )
+ {
+ // TODO: Should we slow down?
+ // Right now we have complete and instantaneous stop,
+ // which is needed because we are transiting to a diff state
+ // and must maintain old position & facing for when we transit
+ // back...
+ mMyAI->SetState( TrafficAI::WAITING_FOR_FREE_LANE );
+
+ return false;
+ }
+
+ rAssert( outLane != NULL );
+
+
+ // ================================================
+ // Update IN & OUT lanes' list of traffic vehicles
+ // ================================================
+ UpdateLanes( mVehicle->mTrafficVehicle, (Lane*)inLane, outLane );
+
+
+ // ==============================================
+ // Build Cubic Spline to navigate intersection
+ // ==============================================
+ const Road* outRoad = outLane->GetRoad();
+ rAssert( outRoad != NULL );
+ unsigned int outSegmentIndex = 0;
+ RoadSegment* outSegment = outRoad->GetRoadSegment(outSegmentIndex);
+ rAssert( outSegment != NULL );
+
+ // create control points, then the spline... store all in mWays
+ BuildCurve( inSegment, inLaneIndex, outSegment, outLaneIndex );
+
+
+ // ==================================================
+ // Update mMyAI:
+ // - to look at the OUT segment & segment index
+ // - to look at the OUT lane & lane index
+ // - lane position not updated
+ // ===================================================
+ mMyAI->SetLane( outLane );
+ mMyAI->SetLaneIndex( outLaneIndex );
+ mMyAI->SetSegment( outSegment );
+ mMyAI->SetSegmentIndex( outSegmentIndex );
+
+ return true;
+}
+
+void TrafficLocomotion::PivotFrontWheels( rmt::Vector facing )
+{
+ float cosAlpha = 0.0f;
+ rmt::Vector outPos, outFacing;
+ if( mIsInIntersection )
+ {
+ // when we're in the intersection, the lane is the OUTLANE, so get the t=0.0f
+ mMyAI->GetSegment()->GetLaneLocation( 0.0f, (int)mMyAI->GetLaneIndex(), outPos, outFacing );
+ outFacing.y = 0.0f;
+ outFacing.Normalize(); // *** SQUARE ROOT! ***
+ facing.y = 0.0f;
+ facing.Normalize(); // *** SQUARE ROOT! ***
+ cosAlpha = facing.Dot( outFacing );
+ }
+ else
+ {
+ // fake wheel turning for lane change...
+ if( mMyAI->GetState() == TrafficAI::LANE_CHANGING )
+ {
+ float progress = mLaneChangeProgress / mLaneChangeDist;
+ if( progress < 0.65f )
+ {
+ cosAlpha = 0.9396926f; // cos20
+ }
+ else
+ {
+ cosAlpha = -0.9396926f; // cos20
+ }
+ }
+ else
+ {
+ // when we're not in the intersection, the lane is the
+ // current lane, so get t=1.0f
+ mMyAI->GetSegment()->GetLaneLocation( 1.0f,
+ (int)mMyAI->GetLaneIndex(), outPos, outFacing );
+ outFacing.y = 0.0f;
+ outFacing.Normalize(); // *** SQUARE ROOT! ***
+ facing.y = 0.0f;
+ facing.Normalize(); // *** SQUARE ROOT! ***
+ cosAlpha = facing.Dot( outFacing );
+
+ }
+ }
+
+ // ensure we are in bounds
+ if( cosAlpha < -1.0f )
+ {
+ cosAlpha = -1.0f;
+ }
+ else if( cosAlpha > 1.0f )
+ {
+ cosAlpha = 1.0f;
+ }
+ float alpha = rmt::ACos( cosAlpha ); // *** COSINE! ***
+ rAssert( !rmt::IsNan( alpha ) );
+
+ // pivot left or right
+ if( mMyAI->GetState() != TrafficAI::LANE_CHANGING )
+ {
+ rmt::Vector rightOfFacing = Get90DegreeRightTurn( facing );
+ if( rightOfFacing.Dot( outFacing ) < 0.0f )
+ {
+ alpha *= -1.0f;
+ }
+ }
+ // Who am I? I'm Spiderman! No, I'm Dusit.
+ mVehicle->SetWheelTurnAngleDirectlyInRadiansForDusitOnly( alpha );
+
+}
+
+
+
+void TrafficLocomotion::UpdateLanes( TrafficVehicle* tv, Lane* oldLane, Lane* newLane )
+{
+ rAssert( tv != NULL );
+ rAssert( tv->GetIsActive() );
+ rAssert( oldLane != NULL );
+ rAssert( newLane != NULL );
+
+ // Remove vehicle from IN lane
+ bool found = false;
+ for( int i=0; i<oldLane->mTrafficVehicles.mUseSize; i++ )
+ {
+ if( tv == oldLane->mTrafficVehicles[i] )
+ {
+ oldLane->mTrafficVehicles.Remove( i );
+ found = true;
+ break;
+ }
+ }
+ //rAssert( found );
+
+ // Add vehicle to OUT lane.
+ // What happens if AddLast returns -1 because you can't add
+ // another vehicle to that lane? It WON'T! Because in our
+ // Intersection::Get<blah>ForTraffic, we make sure we either
+ // return a lane that can take this car or NULL. If NULL, then
+ // we shouldn't be at this point in the code.
+ //
+ rAssert( newLane->mTrafficVehicles.mUseSize < newLane->mTrafficVehicles.mSize );
+ newLane->mTrafficVehicles.Add( tv );
+ tv->SetLane( newLane );
+}
+
+Lane* TrafficLocomotion::GetAIPrevLane()
+{
+ return mMyAI->mPrevLane;
+}
+
+// will return if there's not enough room to lanechange
+bool TrafficLocomotion::BuildLaneChangeCurve(
+ RoadSegment* oldSegment,
+ const float oldT,
+ unsigned int oldLaneIndex,
+ unsigned int newLaneIndex,
+ const float dist)
+{
+ rAssert( oldSegment != NULL );
+
+ // We have to create control points
+ rmt::Vector pts[4];
+
+ // First figure out entry point
+ rmt::Vector entryFacing;
+ oldSegment->GetLaneLocation( oldT, oldLaneIndex, pts[0], entryFacing );
+ if( !rmt::Epsilon( entryFacing.MagnitudeSqr(), 1.0f, 0.0001f ) )
+ {
+ entryFacing.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( entryFacing.MagnitudeSqr(), 1.0f, 0.0001f ) );
+
+ // Second, figure out the next point in line with facing,
+ // some dist ahead (percentage of lanechange dist)
+ float entryOffset = 0.2f * dist;
+ pts[1] = pts[0] + entryFacing * entryOffset;
+
+ // Third, figure out the exit point.
+ // we'll have to follow the road for the given lanechange distance
+ RoadSegment* endSegment = oldSegment;
+
+ float oldLaneLength = oldSegment->GetLaneLength( oldLaneIndex );
+ float t = oldT;
+ float adjustedDt = dist / oldLaneLength;
+ t += adjustedDt;
+
+ unsigned int endSegIndex = endSegment->GetSegmentIndex();
+ while( t > 1.0f )
+ {
+ t -= 1.0f;
+ // move on to next segment (there must be one)
+ unsigned int numSegs = endSegment->GetRoad()->GetNumRoadSegments();
+ endSegIndex++;
+
+ // if we're out of bounds, it means that there weren't enough segments
+ // to complete lane-change. Abort!
+ if( endSegIndex < 0 || endSegIndex >= numSegs )
+ {
+ return false;
+ }
+
+ endSegment = endSegment->GetRoad()->GetRoadSegment( endSegIndex );
+ float nextSegLen = endSegment->GetLaneLength( oldLaneIndex );
+
+ t *= oldLaneLength / nextSegLen;
+
+ oldLaneLength = nextSegLen;
+ }
+
+ rAssert( t <= 1.0f );
+
+ // since the last point of spline won't be the START of the
+ // segment when we come out of LANE_CHANGING state, we need to
+ // store away the t so we add it later...
+ mOutLaneT = t;
+
+ rmt::Vector exitFacing;
+ endSegment->GetLaneLocation( t, newLaneIndex, pts[3], exitFacing );
+ if( !rmt::Epsilon( exitFacing.MagnitudeSqr(), 1.0f, 0.0001f ) )
+ {
+ exitFacing.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( exitFacing.MagnitudeSqr(), 1.0f, 0.0001f ) );
+ exitFacing.Scale( -1.0f );
+
+ // Lastly, figure out the previous point, in line with facing,
+ // some dist ahead (percentage of lanechange dist)
+ float exitOffset = dist * 0.5f;//0.3f;
+ pts[2] = pts[3] + exitFacing * exitOffset;
+
+ //
+ // update AI's segment info: segment, segment index, lane length
+ //
+ mMyAI->SetSegmentIndex( endSegment->GetSegmentIndex() );
+
+ mSplineMaker.SetControlPoint( pts[0], 0 );
+ mSplineMaker.SetControlPoint( pts[1], 1 );
+ mSplineMaker.SetControlPoint( pts[2], 2 );
+ mSplineMaker.SetControlPoint( pts[3], 3 );
+
+ // use mWays to store the spline
+ // (since we're not in an intersection anyway)
+ mSplineMaker.GetCubicBezierCurve( mWays, mNumWays );
+ rAssert( mWays != NULL );
+ rAssert( mNumWays == CubicBezier::MAX_CURVE_POINTS );
+
+ // update the currway stuff needed to work with mWays
+ mCurrWay = 0;
+ mCurrPath.Sub( mWays[mCurrWay+1], mWays[mCurrWay] );
+ mCurrPathLength = mCurrPath.Length(); // *** SQUARE ROOT! ***
+ mCurrPathLocation = 0.0f;
+
+ return true;
+}
+
+bool TrafficLocomotion::BuildArbitraryCurve(
+ const rmt::Vector& startPos,
+ const rmt::Vector& startDir, // is normalized to 1
+ const rmt::Vector& endPos,
+ const rmt::Vector& endDir )// is normalized to 1
+{
+ rAssert( rmt::Epsilon( startDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+ rAssert( rmt::Epsilon( endDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ float distScale = (startPos - endPos).Length(); // *** SQUARE ROOT! ***
+ distScale *= 0.3f;
+
+ rmt::Vector pts[4];
+ pts[0] = startPos;
+ pts[1] = startPos + startDir * distScale;
+ pts[2] = endPos + endDir * -1 * distScale;
+ pts[3] = endPos;
+
+ mSplineMaker.SetControlPoint( pts[0], 0 );
+ mSplineMaker.SetControlPoint( pts[1], 1 );
+ mSplineMaker.SetControlPoint( pts[2], 2 );
+ mSplineMaker.SetControlPoint( pts[3], 3 );
+
+ // use mWays to store the spline
+ mSplineMaker.GetCubicBezierCurve( mWays, mNumWays );
+ rAssert( mWays != NULL );
+ rAssert( mNumWays == CubicBezier::MAX_CURVE_POINTS );
+
+ // update the currway stuff needed to work with mWays
+ mCurrWay = 0;
+ mCurrPath.Sub( mWays[mCurrWay+1], mWays[mCurrWay] );
+ mCurrPathLength = mCurrPath.Length(); // *** SQUARE ROOT! ***
+ mCurrPathLocation = 0.0f;
+
+ return true;
+}
+
+void TrafficLocomotion::GetSplineCurve( rmt::Vector*& ways, int& npts, int& currWay )
+{
+ if( mWays == NULL )
+ {
+ ways = NULL;
+ npts = 0;
+ currWay = -1;
+ }
+ else
+ {
+ ways = mWays;
+ npts = mNumWays;
+ currWay = mCurrWay;
+ }
+}
diff --git a/game/code/worldsim/redbrick/trafficlocomotion.h b/game/code/worldsim/redbrick/trafficlocomotion.h
new file mode 100644
index 0000000..350f5d0
--- /dev/null
+++ b/game/code/worldsim/redbrick/trafficlocomotion.h
@@ -0,0 +1,270 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trafficlocomotion.h
+//
+// Description: Blahblahblah
+//
+// History: 09/09/2002 + Locomote through intersections -- Dusit Eakkachaichanvet
+// 07/08/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef TRAFFICLOCOMOTION_H
+#define TRAFFICLOCOMOTION_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <worldsim/redbrick/vehiclelocomotion.h>
+#include <radmath/radmath.hpp>
+#include <ai/vehicle/trafficai.h>
+#include <roads/geometry.h>
+
+//========================================
+// Forward References
+//========================================
+class Lane;
+class RoadSegment;
+
+//
+// A lightweight statically allocated, singly-linked list
+//
+
+const int MAX_ITEMS = 30;
+
+struct Item
+{
+ rmt::Vector p;
+ int next;
+};
+
+class StaticList
+{
+public:
+ Item mItems[MAX_ITEMS];
+
+ void Clear()
+ {
+ int i=0;
+ for(i; i<MAX_ITEMS; i++)
+ {
+ mItems[i].next = -1;
+ }
+ mFirstFree = 0;
+ }
+
+ StaticList()
+ {
+ Clear();
+ }
+
+ int GetNumItems()
+ {
+ return mFirstFree;
+ }
+
+ Item* InsertItem( rmt::Vector p, Item* prevItem )
+ {
+ if( mFirstFree >= MAX_ITEMS )
+ {
+ return NULL;
+ }
+ int freeIndex = mFirstFree;
+ mItems[freeIndex].p = p;
+ mItems[freeIndex].next = (prevItem != NULL)? prevItem->next : -1;
+
+ if( prevItem != NULL )
+ {
+ prevItem->next = freeIndex;
+ }
+ else if( freeIndex > 0 )
+ {
+ mItems[freeIndex-1].next = freeIndex;
+ }
+ mFirstFree++;
+ return &(mItems[freeIndex]);
+ }
+
+private:
+
+ int mFirstFree;
+};
+
+
+
+
+
+
+
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class TrafficLocomotion : public VehicleLocomotion
+{
+public:
+
+ enum {
+ ON_ROAD_HISTORY_SIZE = 66
+ };
+
+ static const float SECONDS_BETW_HISTORY_UPDATES;
+
+ TrafficLocomotion(Vehicle* vehicle);
+ virtual ~TrafficLocomotion();
+
+ void Init();
+ void Init( Vehicle* vehicle,
+ Lane* lane,
+ unsigned int laneIndex, //0 is curbside
+ RoadSegment* segment,
+ unsigned int segmentIndex,
+ float t,
+ float kmh );
+
+ void InitPos( const rmt::Vector& pos );
+ void InitFacing( const rmt::Vector& facing );
+ void InitVehicleAI( Vehicle* vehicle );
+ void InitLane( Lane* lane, unsigned int laneIndex, float mps );
+ void InitSegment( RoadSegment* segment, unsigned int segmentIndex, float t );
+
+ //////////////////// VEHICLELOCOMOTION STUFF ////////////////////
+ virtual void PreSubstepUpdate();
+ virtual void PreCollisionPrep(bool firstSubstep);
+ virtual void UpdateVehicleGroundPlane();
+ virtual void PreUpdate();
+ virtual void Update(float seconds);
+ virtual void PostUpdate();
+ virtual void CompareNormalAndHeight(int index, rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint) {};
+ ///////////////////////////////////////////////////////////////////
+
+ TrafficAI* GetAI();
+ Lane* GetAILane();
+ Lane* GetAIPrevLane();
+ void SetAISpeed( float mps );
+ float GetAISpeed();
+ bool IsInIntersection() const;
+ void UpdateAI(unsigned int ms);
+ void SetActive( bool isActive );
+ bool GetActive() const;
+ void SetAIState( enum TrafficAI::State state );
+
+ void UpdateLanes( TrafficVehicle* tv, Lane* oldLane, Lane* newLane );
+
+ bool BuildLaneChangeCurve(
+ RoadSegment* oldSegment,
+ const float oldT,
+ unsigned int oldLaneIndex,
+ unsigned int newLaneIndex,
+ const float dist);
+
+ bool BuildArbitraryCurve(
+ const rmt::Vector& startPos,
+ const rmt::Vector& startDir,
+ const rmt::Vector& endPos,
+ const rmt::Vector& endDir );
+
+ void GetSplineCurve( rmt::Vector*& ways, int& npts, int& currWay );
+
+public:
+ float mLaneChangeProgress; // progress in meters while in lane change state
+ float mLaneChangeDist; // total distance over which we want to lane change
+ unsigned int mLaneChangingFrom; // index of lane we are lane changing FROM
+ float mOutLaneT;
+
+ float mCurrPathLocation;
+
+ float mActualSpeed;
+
+private:
+ Vehicle* mVehicle;
+ TrafficAI* mMyAI;
+
+ ///// For building splines at intersections //////
+ CubicBezier mSplineMaker;
+ rmt::Vector* mWays;
+ int mNumWays;
+ //////////////////////////////////////////////////
+
+ rmt::Vector mCurrPath;
+ float mCurrPathLength;
+ int mCurrWay;
+
+ bool mIsInIntersection;
+
+ rmt::Vector mPrevPos;
+
+ float mSecondsTillCheckForFreeLane;
+
+ ////////////// History ///////////////
+ VectorHistory<ON_ROAD_HISTORY_SIZE> mFacingHistory;
+ VectorHistory<ON_ROAD_HISTORY_SIZE> mPosHistory;
+ /////////////////////////////////////////////
+
+ float mSecondsSinceLastAddToHistory;
+
+private:
+ // Helpers
+ void FindOutLane( const Lane* inLane,
+ unsigned int inLaneIndex,
+ Lane*& outLane,
+ unsigned int& outLaneIndex );
+
+ void BuildCurve ( RoadSegment* inSegment,
+ unsigned int inLaneIndex,
+ RoadSegment* outSegment,
+ unsigned int outLaneIndex );
+
+ bool EnterIntersection();
+
+ void PivotFrontWheels( rmt::Vector facing );
+
+ void StopSuddenly( rmt::Vector& pos, rmt::Vector& facing );
+
+ ////////////// WASTEFUL CONSTRUCTORS ////////////////////
+ TrafficLocomotion();
+ TrafficLocomotion( const TrafficLocomotion& trafficlocomotion );
+ TrafficLocomotion& operator=( const TrafficLocomotion& trafficlocomotion );
+ ////////////////////////////////////////////////////////////
+};
+
+inline bool TrafficLocomotion::IsInIntersection() const
+{
+ return mIsInIntersection;
+}
+inline bool TrafficLocomotion::GetActive() const
+{
+ return mMyAI->mIsActive;
+}
+inline void TrafficLocomotion::SetActive( bool isActive )
+{
+ mMyAI->mIsActive = isActive;
+}
+inline void TrafficLocomotion::SetAISpeed( float mps )
+{
+ mMyAI->SetAISpeed( mps );
+}
+inline float TrafficLocomotion::GetAISpeed()
+{
+ return mMyAI->GetAISpeed();
+ }
+inline Lane* TrafficLocomotion::GetAILane()
+{
+ return mMyAI->GetLane();
+}
+inline void TrafficLocomotion::SetAIState( enum TrafficAI::State state )
+{
+ mMyAI->SetState(state);
+}
+inline TrafficAI* TrafficLocomotion::GetAI()
+{
+ return mMyAI;
+}
+
+#endif //TRAFFICLOCOMOTION_H
+
+
diff --git a/game/code/worldsim/redbrick/vehicle.cpp b/game/code/worldsim/redbrick/vehicle.cpp
new file mode 100644
index 0000000..5abc941
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehicle.cpp
@@ -0,0 +1,6483 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehicle.cpp
+//
+// Description: the car
+//
+// History: Nov 16, 2001 + Created -- gmayer
+//
+//=============================================================================
+
+
+//========================================
+// System Includes
+//========================================
+#include <p3d/anim/skeleton.hpp>
+#include <p3d/matrixstack.hpp>
+#include <simcommon/simstate.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <simcommon/simulatedobject.hpp>
+#include <simcommon/simenvironment.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <simcollision/collisionvolume.hpp>
+#include <simcollision/collisionmanager.hpp>
+#include <simphysics/articulatedphysicsobject.hpp>
+#include <simphysics/physicsobject.hpp>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <simcollision/collisiondisplay.hpp>
+#include <render/DSG/StatePropDSG.h>
+#include <worldsim/character/characterrenderable.h>
+#include <mission/statepropcollectible.h>
+#include <mission/objectives/missionobjective.h>
+
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <worldsim/redbrick/vehicle.h>
+
+#include <worldsim/worldphysicsmanager.h>
+
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/redbrick/wheel.h>
+#include <worldsim/redbrick/redbrickcollisionsolveragent.h>
+
+#include <worldsim/redbrick/physicslocomotion.h>
+#include <worldsim/redbrick/trafficlocomotion.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+#include <worldsim/character/character.h>
+
+
+#include <choreo/puppet.hpp>
+
+
+#include <events/eventmanager.h>
+#include <events/eventdata.h>
+
+#include <roads/roadsegment.h>
+#include <roads/geometry.h>
+
+
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+
+#include <meta/carstartlocator.h>
+
+#include <debug/debuginfo.h>
+
+
+#include <ai/actionbuttonhandler.h>
+#include <ai/actionbuttonmanager.h>
+
+#include <meta/eventlocator.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/recttriggervolume.h>
+
+
+#include <render/RenderManager/RenderManager.h>
+#include <render/Culling/WorldScene.h>
+#include <render/IntersectManager/IntersectManager.h>
+#include <render/breakables/breakablesmanager.h>
+
+#include <sound/soundcollisiondata.h>
+
+#include <radmath/radmath.hpp>
+#include <worldsim/coins/sparkle.h>
+
+#include <cheats/cheatinputsystem.h>
+
+#include <mission/gameplaymanager.h>
+#include <supersprint/supersprintmanager.h>
+#include <p3d/billboardobject.hpp>
+#include <meta/triggervolumetracker.h>
+
+#include <camera/supercammanager.h>
+#include <camera/supercamcentral.h>
+
+#include <presentation/gui/guisystem.h>
+
+using namespace sim;
+
+// note - methods that are only called once at initialization, moved to vehicleinit
+
+// CONSTANTS
+// In what radius the vehicle explosion will affect objects
+const float EXPLOSION_EFFECT_RADIUS = 20.0f;
+// How much force to apply to objects within this radius
+const float EXPLOSION_FORCE = 20000.0f;
+// Set the center of the explosion so be below that car position so that objects get hurled upwards
+const float EXPLOSION_Y_OFFSET = -5.0f;
+
+const float HITPOINTS_REMOVED_FROM_VEHICLE_EXPLOSION = 0.5;
+const float HITPOINTS_REMOVED_FROM_VEHICLE_EXPLOSION_PLAYER = 0.5;
+
+static const float REST_LINEAR_TOL = 0.05f; // linear velocity tolerance
+static const float REST_ANGULAR_TOL = 0.3f; // angular velocity tolerance
+
+// Initialize static variables
+float Vehicle::s_DamageFromExplosion = HITPOINTS_REMOVED_FROM_VEHICLE_EXPLOSION;
+float Vehicle::s_DamageFromExplosionPlayer = HITPOINTS_REMOVED_FROM_VEHICLE_EXPLOSION_PLAYER;
+
+bool Vehicle::sDoBounce = false;
+
+void Vehicle::ActivateTriggers( bool activate )
+{
+ if(activate == mTriggerActive)
+ {
+ return;
+ }
+
+ if( mVehicleType == VT_AI && activate )
+ {
+ // NOT OK to add triggers for AI car doors, but OK to remove...
+ // AI triggers remain as they are (so you can't get into the cars)
+ //rAssert( false, "FALSE, Chuck, FALSE!" );
+ return;
+ }
+
+ if(mVehicleDestroyed && activate)
+ {
+ return;
+ }
+
+ if(!GetVehicleCentral()->GetVehicleTriggersActive() && activate)
+ {
+ return;
+ }
+
+ if( mpEventLocator == NULL )
+ {
+ return;
+ }
+
+ mTriggerActive = activate;
+
+ if( activate )
+ {
+ for( unsigned j = 0; j < mpEventLocator->GetNumTriggers(); j++ )
+ {
+ GetTriggerVolumeTracker()->AddTrigger( mpEventLocator->GetTriggerVolume( j ) );
+ }
+ }
+ else
+ {
+ for( unsigned j = 0; j < mpEventLocator->GetNumTriggers(); j++ )
+ {
+ GetTriggerVolumeTracker()->RemoveTrigger( mpEventLocator->GetTriggerVolume( j ) );
+ }
+ }
+}
+
+void Vehicle::SetUserDrivingCar( bool b )
+{
+ // chooka set's this when a character get's in or out.
+ mUserDrivingCar = b;
+ if( mVehicleType != VT_AI && mUserDrivingCar )
+ {
+ mVehicleType = VT_USER;
+ }
+}
+
+
+void Vehicle::TransitToAI()
+{
+ mVehicleType = VT_AI;
+ ActivateTriggers( false );
+ GetEventManager()->TriggerEvent( EVENT_USER_VEHICLE_REMOVED_FROM_WORLD, this );
+}
+
+//=============================================================================
+//
+//=============================================================================
+// Description: Comment
+//
+// Parameters:
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::EnteringJumpBoostVolume()
+{
+ mDoingJumpBoost = true;
+}
+
+/*
+void Vehicle::AddRef()
+{
+ if( mVehicleType == VT_TRAFFIC && TrafficManager::GetInstance()->IsVehicleTrafficVehicle(this) )
+ {
+ rDebugPrintf( "Booya!\n" );
+ }
+ tRefCounted::AddRef();
+}
+
+void Vehicle::Release()
+{
+ if( mVehicleType == VT_TRAFFIC && TrafficManager::GetInstance()->IsVehicleTrafficVehicle(this) )
+ {
+ rDebugPrintf( "Yaaaboo!\n" );
+ }
+ tRefCounted::Release();
+}
+*/
+
+//=============================================================================
+// Vehicle::
+//=============================================================================
+// Description: Comment
+//
+// Parameters:
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::ExitingJumpBoostVolume()
+{
+ if(mDoingJumpBoost)
+ {
+ // if we were doing one, now might be a good time for this event:
+ if(mVehicleType == VT_USER)
+ {
+ GetEventManager()->TriggerEvent(EVENT_BIG_AIR, (void*)(this->mpDriver));
+ }
+
+ }
+
+ mDoingJumpBoost = false;
+}
+
+int Vehicle::CastsShadow()
+{
+ int retVal;
+ // Casts a shadow in the 2nd pass, if this vehicle has one (witch and ship don't)
+ if ( m_IsSimpleShadow )
+ {
+ retVal = 989;
+ }
+ else
+ {
+ retVal = 0;
+ }
+ return retVal;
+}
+//=============================================================================
+// Vehicle::DisplayShadow
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( )
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::DisplayShadow()
+{
+ if( !mOkToDrawSelf || !mDrawVehicle )
+ {
+ return;
+ }
+
+ BEGIN_PROFILE("Vehicle::DisplayShadow")
+ if( !IsSimpleShadow() )
+ {
+ p3d::stack->Push();
+ p3d::stack->Multiply(mTransform);
+
+ mGeometryVehicle->DisplayShadow();
+
+ p3d::stack->Pop();
+ }
+ END_PROFILE("Vehicle::DisplayShadow")
+}
+
+void Vehicle::DisplaySimpleShadow( void )
+{
+ if( !mOkToDrawSelf || !mDrawVehicle )
+ {
+ return;
+ }
+
+ BEGIN_PROFILE("Vehicle::DisplaySimpleShadow")
+ p3d::pddi->SetZWrite(false);
+ if( IsSimpleShadow() )
+ {
+ const float HeightRatio = 1.0f / 4.0f;
+ rmt::Vector pos = mTransform.Row( 3 );
+ float carY = pos.y;
+ pos.y = GetGroundY();
+ rmt::Vector norm;
+ rmt::Vector forward;
+ forward = mTransform.Row( 2 );
+
+ if( GetLocomotionType() == VL_TRAFFIC )
+ {
+ // We'll assume the traffic doesn't go jumping through the air...although they
+ //could theorically get knocked through the air I guess. When that happens however
+ //they are under physics control and Greg says they aren't VL_TRAFFIC any more.
+ norm = mTransform.Row( 1 );
+ }
+ else
+ {
+ //const rmt::Matrix& groundTrans = mGroundPlaneSimState->GetTransform();
+ //norm = groundTrans.Row( 2 );
+
+ norm = this->mGroundPlaneWallVolume->mNormal;
+ }
+
+ BlobShadowParams p( pos, norm, forward );
+ p.ShadowScale = rmt::Clamp( 1.0f - ( ( carY - ( pos.y + GetRestHeightAboveGround() ) ) * HeightRatio ), 0.0f, 1.0f );
+ p.ShadowAlpha = p.ShadowScale * ( mInterior ? 0.5f : 1.0f );
+ mGeometryVehicle->DisplayShadow( &p );
+ }
+ p3d::pddi->SetZWrite(true);
+ END_PROFILE("Vehicle::DisplaySimpleShadow")
+}
+
+
+
+//=============================================================================
+// Vehicle::SetInCarSimState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetInCarSimState()
+{
+ if(mSimStateArticulatedOutOfCar)
+ {
+ if(!mUsingInCarPhysics)
+ {
+ if(mCollisionAreaIndex != -1)
+ {
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(this);
+
+ RemoveSelfFromCollisionManager(); // deals with our own index
+ GetWorldPhysicsManager()->EmptyCollisionAreaIndex(this->mCollisionAreaIndex);
+
+ }
+
+ mSimStateArticulatedOutOfCar->ResetVelocities();
+ mSimStateArticulatedInCar->ResetVelocities();
+
+ rmt::Matrix transform = mSimStateArticulatedOutOfCar->GetTransform();
+
+ mSimStateArticulatedInCar->SetControl(sim::simAICtrl);
+ mSimStateArticulatedInCar->SetTransform(transform);
+ mSimStateArticulatedInCar->SetControl(sim::simSimulationCtrl);
+
+
+
+ mSimStateArticulated = mSimStateArticulatedInCar;
+
+ mUsingInCarPhysics = true;
+
+ if(mCollisionAreaIndex != -1)
+ {
+ AddSelfToCollisionManager();
+ }
+ }
+
+ }
+ else
+ {
+ // please God let this be the last fucking hack in this game...
+ mSimStateArticulated->ResetVelocities();
+
+ rmt::Matrix transform = mSimStateArticulated->GetTransform();
+
+ mSimStateArticulated->SetControl(sim::simAICtrl);
+ mSimStateArticulated->SetTransform(transform);
+ mSimStateArticulated->SetControl(sim::simSimulationCtrl);
+
+ CalculateSuspensionLocationAndVelocity(); // just in case
+
+
+
+ }
+
+
+}
+
+
+//=============================================================================
+// Vehicle::SetOutOfCarSimState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetOutOfCarSimState()
+{
+ if(mSimStateArticulatedOutOfCar)
+ {
+ if(mUsingInCarPhysics)
+ {
+ if(mCollisionAreaIndex != -1)
+ {
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(this);
+
+ RemoveSelfFromCollisionManager(); // deals with our own index
+
+ GetWorldPhysicsManager()->EmptyCollisionAreaIndex(this->mCollisionAreaIndex);
+
+ // hmmm
+ // this is probably at init.
+ //
+ // we need to do something to try and make the normal physics update loop run once
+ // so that the cars settles into place with suspensionYOffset set by designers...
+
+ // try this:
+ //this->PreSubstepUpdate();
+ //this->PreCollisionPrep(0.016f, true);
+ //this->Update(0.016f);
+ //this->PostSubstepUpdate();
+
+
+ }
+
+ mSimStateArticulatedOutOfCar->ResetVelocities();
+ mSimStateArticulatedInCar->ResetVelocities();
+
+
+ rmt::Matrix transform = mSimStateArticulatedInCar->GetTransform();
+
+ mSimStateArticulatedOutOfCar->SetControl(sim::simAICtrl);
+ mSimStateArticulatedOutOfCar->SetTransform(transform);
+ mSimStateArticulatedOutOfCar->SetControl(sim::simSimulationCtrl);
+
+ mSimStateArticulated = mSimStateArticulatedOutOfCar;
+
+ CalculateSuspensionLocationAndVelocity(); // just in case
+
+ mUsingInCarPhysics = false;
+
+ if(mCollisionAreaIndex != -1)
+ {
+ AddSelfToCollisionManager();
+ }
+ }
+
+ }
+ else
+ {
+ // please God let this be the last fucking hack in this game...
+ mSimStateArticulated->ResetVelocities();
+
+ rmt::Matrix transform = mSimStateArticulated->GetTransform();
+
+ mSimStateArticulated->SetControl(sim::simAICtrl);
+ mSimStateArticulated->SetTransform(transform);
+ mSimStateArticulated->SetControl(sim::simSimulationCtrl);
+
+ CalculateSuspensionLocationAndVelocity(); // just in case
+
+
+
+ }
+
+}
+
+
+
+
+
+//=============================================================================
+// Vehicle::SetLocomotion
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( VehicleLocomotionType loco )
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetLocomotion( VehicleLocomotionType loco )
+{
+
+ switch(loco)
+ {
+ case VL_PHYSICS:
+
+ if(mLoco == VL_TRAFFIC)
+ {
+ // this is a traffic car that has just been hit
+ // deactivate the AI so that when we get to a rest state
+ // and return to AI control the car won't move
+ ::GetEventManager()->TriggerEvent( EVENT_TRAFFIC_GOT_HIT, this );
+ TrafficManager::GetInstance()->Deactivate( this );
+ }
+
+ mVehicleLocomotion = mPhysicsLocomotion;
+
+ //mSimStateArticulated->ResetVelocities(); //hmmmmmmmmmmmmmmmmmmmmmmmmmm
+ // looks like we have to do this
+
+ //mSimStateArticulated->StoreJointState(0.016f);
+ mSimStateArticulated->SetControl(simSimulationCtrl);
+
+ mLocoSwitchedToPhysicsThisFrame = true;
+
+ // hmm....
+ // safe to set velocity of vehicle to mVelocityCM?
+ //mSimStateArticulated->ResetVelocities();
+
+ break;
+
+ case VL_TRAFFIC:
+
+ mVehicleLocomotion = mTrafficLocomotion;
+ mSimStateArticulated->SetControl(simAICtrl);
+
+ mSimStateArticulated->ResetVelocities();
+
+ break;
+
+ default:
+ rAssert(0);
+ }
+
+ mLoco = loco;
+}
+
+
+
+//=============================================================================
+// Vehicle::IsAFlappingJoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: bool
+//
+//=============================================================================
+bool Vehicle::IsAFlappingJoint(int index)
+{
+ if( index == mDoorDJoint ||
+ index == mDoorPJoint ||
+ index == mHoodJoint ||
+ index == mTrunkJoint)
+ {
+ return true;
+ }
+
+ return false;
+
+}
+
+
+//=============================================================================
+// Vehicle::CalculateDragCoeffBasedOnTopSpeed
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::CalculateDragCoeffBasedOnTopSpeed()
+{
+ // this will also have to be called again whenever the desginer params are reloaded
+
+
+ // dragforce = 0.5 * coeff * speed * speed;
+ //
+ // so, we need a coeff that causes dragforce to match engine force at that speed
+ //
+ // for now we know the max engine force - 2 * 1.0f * mDpGasScale
+
+ float topspeedmps = mDesignerParams.mDpTopSpeedKmh / 3.6f;
+
+ // * 2 because this is what we apply at each wheel
+ // also need to subtract rolling friction for this to be accurate
+ //float terminalForce = 2.0f * mDesignerParams.mDpGasScale * mDesignerParams.mDpMass - mRollingFrictionForce;
+
+ float terminalForce = 0.0f;
+ if(mDesignerParams.mDpGasScaleSpeedThreshold == 1.0f)
+ {
+ terminalForce = 2.0f * mDesignerParams.mDpGasScale * mDesignerParams.mDpMass; //) - (mRollingFrictionForce * mDesignerParams.mDpMass);
+ }
+ else
+ {
+ // else threhold is < 1.0 so it is used
+
+ terminalForce = 2.0f * mDesignerParams.mDpHighSpeedGasScale * mDesignerParams.mDpMass; //) - (mRollingFrictionForce * mDesignerParams.mDpMass);
+ }
+
+
+ mDragCoeff = 2.0f* terminalForce / (topspeedmps * topspeedmps);
+
+
+}
+
+
+
+//=============================================================================
+// Vehicle::IsJointAWheel
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int jointIndex)
+//
+// Return: bool
+//
+//=============================================================================
+bool Vehicle::IsJointAWheel(int jointIndex)
+{
+ if(mJointIndexToWheelMapping[jointIndex] >= 0)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+//=============================================================================
+// Vehicle::SetWheelCorrectionOffset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int jointNum, float objectSpaceYOffsetFromCurrentPosition)
+//
+// Return: bool
+//
+//=============================================================================
+bool Vehicle::SetWheelCorrectionOffset(int jointNum, float objectSpaceYOffsetFromCurrentPosition, rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint)
+{
+ //bool bottomedOut = mWheels[mJointIndexToWheelMapping[jointNum]]->SetYOffsetFromCurrentPosition(objectSpaceYOffsetFromCurrentPosition);
+ float bottomedOut = mWheels[mJointIndexToWheelMapping[jointNum]]->SetYOffsetFromCurrentPosition(objectSpaceYOffsetFromCurrentPosition);
+
+ if(mLoco == VL_PHYSICS) // is this method even called if it's not?
+ {
+ mVehicleLocomotion->CompareNormalAndHeight(mJointIndexToWheelMapping[jointNum], normalPointingAtCar, groundContactPoint);
+ }
+
+
+ if(bottomedOut > 0.0f)
+ {
+ // if any wheel bottoms out we want to set this
+
+ // currently this is just used by the parent Vehicle to do some debug rendering
+ mBottomedOutThisFrame = true;
+
+ // TODO ?
+ // what to do with this value
+
+ return true;
+ }
+
+ //return bottomedOut;
+ return false;
+}
+
+
+
+//=============================================================================
+// Vehicle::SetVehicleSimEnvironment
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (sim::SimEnvironment* se)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetVehicleSimEnvironment(sim::SimEnvironment* se)
+{
+ mPhObj->SetSimEnvironment(se);
+}
+
+
+//=============================================================================
+// Vehicle::GetCollisionAreaIndexAndAddSelf
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::GetCollisionAreaIndexAndAddSelf()
+{
+ mCollisionAreaIndex = GetWorldPhysicsManager()->GetVehicleCollisionAreaIndex();
+ rAssert(mCollisionAreaIndex != -1);
+
+ AddSelfToCollisionManager();
+
+}
+
+
+//=============================================================================
+// Vehicle::RemoveSelfAndFreeCollisionAreaIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::RemoveSelfAndFreeCollisionAreaIndex()
+{
+ RemoveSelfFromCollisionManager();
+
+ GetWorldPhysicsManager()->FreeCollisionAreaIndex(mCollisionAreaIndex);
+ mCollisionAreaIndex = -1;
+
+}
+
+//=============================================================================
+// Vehicle::AddSelfToCollisionManager
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::AddSelfToCollisionManager()
+{
+ GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(mSimStateArticulated->GetCollisionObject(), mCollisionAreaIndex);
+ GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(mGroundPlaneSimState->GetCollisionObject(), mCollisionAreaIndex);
+ GetWorldPhysicsManager()->mCollisionManager->AddPair(mGroundPlaneSimState->GetCollisionObject(), mSimStateArticulated->GetCollisionObject(), mCollisionAreaIndex);
+}
+
+
+//=============================================================================
+// Vehicle::RemoveSelfFromCollisionManager
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::RemoveSelfFromCollisionManager()
+{
+ rAssert(mCollisionAreaIndex != -1);
+ GetWorldPhysicsManager()->mCollisionManager->RemoveCollisionObject(mSimStateArticulated->GetCollisionObject(), mCollisionAreaIndex);
+ GetWorldPhysicsManager()->mCollisionManager->RemoveCollisionObject(mGroundPlaneSimState->GetCollisionObject(), mCollisionAreaIndex);
+}
+
+//=============================================================================
+// Vehicle::AddToOtherCollisionArea
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::AddToOtherCollisionArea(int index)
+{
+ GetWorldPhysicsManager()->mCollisionManager->AddCollisionObject(mSimStateArticulated->GetCollisionObject(), index);
+}
+
+//=============================================================================
+// Vehicle::ReLoadDesignerParams
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::CalculateValuesBasedOnDesignerParams()
+{
+ // in the future, reload from file or something?
+ //
+ // right now, this method can just be called when a gamepad button is hit, to
+ // pass down new settings (using the Marting-method in msdev) and force
+ // recalculation in the physicsvehicle and wheel as necessary
+
+ int stophere = 1; // break, and change values in msdev, then continue
+
+ //
+ //mPhysicsVehicle->SetDesignerParams(&mDesignerParams);
+
+ if(GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_HIGH_ACCELERATION))
+ {
+ //mDesignerParams.mDpGasScale *= 3.0f;
+ }
+
+
+ // TODO - reimplement as necessary here
+
+ // account for new mass
+ SetupPhysicsProperties();
+
+ CalculateDragCoeffBasedOnTopSpeed();
+
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ mSuspensionRestPoints[i] = mSuspensionRestPointsFromFile[i];
+ mSuspensionRestPoints[i].y += mDesignerParams.mDpSuspensionYOffset;
+
+ mWheels[i]->SetDesignerParams(&mDesignerParams);
+ }
+
+ // reset hitpoints to full
+ mHitPoints = mDesignerParams.mHitPoints;
+
+ float health = GetCharacterSheetManager()->GetCarHealth( mCharacterSheetCarIndex );
+ if( ( health >= 0.0f ) && ( health < 1.0f ) )
+ {
+ mHitPoints *= health;
+ SyncVisualDamage( health );
+ }
+}
+
+
+//=============================================================================
+// Vehicle::TrafficSetTransform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Matrix &m)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::TrafficSetTransform(rmt::Matrix &m)
+{
+ // should already be under simAICtrl
+ rAssert(mSimStateArticulated->GetControl() == simAICtrl);
+
+ // the position in the incoming matrix is on the road surface
+ //
+ // need to figure out how much to bump this up along vup
+
+ // ( I think the wheels settling a bit on the suspension will have to be
+ // handled elsewhere.. eg. suspensionjointdriver)
+
+ float restHeightAboveGround = GetRestHeightAboveGround();
+ //restHeightAboveGround = 2.0f;
+ rmt::Vector fudge = mVehicleUp;
+
+ const float hacktest = 0.1f;
+
+ fudge.Scale(restHeightAboveGround - hacktest);
+
+ rmt::Vector currentTrans = m.Row(3);
+ currentTrans.Add(fudge);
+
+ m.FillTranslate(currentTrans);
+
+
+ mSimStateArticulated->SetTransform(m);
+ mTransform = m;
+
+
+}
+
+
+//=============================================================================
+// Vehicle::GetRestHeightAboveGround
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float Vehicle::GetRestHeightAboveGround()
+{
+ // ie. height above ground for skeleton in rest pose
+ //
+ // assume all the wheels have same radius and same suspension point y
+
+ // TODO - is this correct? adequate?
+ float suspensionPointToCenter = -1.0f * mSuspensionRestPoints[0].y;
+ float wheelRadius = mWheels[0]->mRadius;
+
+ return suspensionPointToCenter + wheelRadius;
+}
+
+
+//=============================================================================
+// Vehicle::SetTransform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Matrix* m )
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetTransform( rmt::Matrix &m )
+{
+ if(mSimStateArticulated->GetControl() == simAICtrl)
+ {
+ mSimStateArticulated->SetTransform(m);
+
+ // TODO
+ // MS7 HACK
+ //mSimStateArticulated->SetControl(simSimulationCtrl);
+ }
+ else
+ {
+ mSimStateArticulated->SetControl(simAICtrl);
+ mSimStateArticulated->SetTransform(m);
+ mSimStateArticulated->SetControl(simSimulationCtrl);
+ }
+
+ mSimStateArticulated->ResetVelocities();
+
+ mTransform = mSimStateArticulated->GetTransform(-1);
+
+ mPoseEngine->Begin(true); // TODO - should this be true or false
+
+ int i;
+ for (i = 0; i < mPoseEngine->GetPassCount(); i++)
+ {
+ mPoseEngine->Advance(i, 0);
+ mPoseEngine->Update(i);
+ }
+ mPoseEngine->End();
+ mSimStateArticulated->UpdateJointState(0);
+
+ // TODO - reset other state variables
+ // TODO - need to touch pose engine here?
+
+
+ mVelocityCM.Set(0.0f, 0.0f, 0.0f);
+ mSpeed = 0.0f;
+ mSpeedKmh = 0.0f;
+ mPercentOfTopSpeed = 0.0f;
+
+ mBrakeTimer = 0.0f;
+ mBrakeActingAsReverse = false;
+
+ mVehicleFacing = mTransform.Row(2);
+ mVehicleUp = mTransform.Row(1);
+ mVehicleTransverse = mTransform.Row(0);
+
+
+ mGear = 0; // neutral?
+
+ // redundant, but just in case
+ for(i = 0; i < 4; i++)
+ {
+ mWheels[i]->Reset();
+ }
+
+
+
+ CalculateSuspensionLocationAndVelocity();
+
+ mPhysicsLocomotion->SetTerrainIntersectCachePointsForNewTransform();
+
+ //Only do this if the car is actually IN the DSG
+ if ( mVehicleCentralIndex > -1 )
+ {
+ DSGUpdateAndMove();
+ }
+}
+
+//=============================================================================
+// Vehicle::SetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector* newPos)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetPosition(rmt::Vector* newPos)
+{
+ rmt::Matrix m;
+ m.Identity();
+
+ m.FillTranslate(*newPos);
+
+ SetTransform( m );
+}
+
+//
+// Dumbledore here,
+// This method is to be used for vehicles that get spawned at a locator position that
+// has been SNAPPED TO GROUND. Since Vehicle will assume initial position belongs
+// to the car's center of mass, the car will start half-sunken into the ground..
+// that is unless we can do the auto adjustment here...
+//
+void Vehicle::SetInitialPositionGroundOffsetAutoAdjust( rmt::Vector* newPos )
+{
+ mInitialPosition = *newPos;
+ mInitialPosition.y += GetRestHeightAboveGround();
+}
+
+//=============================================================================
+// Vehicle::SetInitialPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* newPos )
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetInitialPosition( rmt::Vector* newPos )
+{
+ mInitialPosition = *newPos;
+}
+
+//=============================================================================
+// Vehicle::SetResetFacingInRadians
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float rotation )
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetResetFacingInRadians( float rotation )
+{
+ mResetFacingRadians = rotation;
+}
+
+//=============================================================================
+// Vehicle::Reset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::Reset( bool resetDamage, bool clearTraffic )
+{
+ //SetPosition(&mInitialPosition);
+
+ rmt::Matrix m;
+
+ m.Identity();
+ m.FillRotateXYZ( 0.0f, mResetFacingRadians, 0.0f );
+ m.FillTranslate( mInitialPosition );
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ mWheels[i]->Reset();
+ }
+
+ SetTransform( m );
+
+ /*
+ // FORCE UPDATE THE AVATAR'S LAST PATH INFO
+ Avatar* avatar = GetAvatarManager()->GetAvatarForVehicle( this );
+ if( avatar )
+ {
+ RoadManager::PathElement elem;
+ RoadSegment* seg;
+ float segT, roadT;
+ avatar->GetLastPathInfo( elem, seg, segT, roadT );
+ }
+ */
+
+ // if clearTraffic is set to true, we want to use the functionality of ResetOnSpot
+ // but use the position that was filled into mInitialPosition
+ if(clearTraffic)
+ {
+ this->ResetOnSpot(resetDamage, false);
+ }
+ else
+ {
+
+
+
+ ResetFlagsOnly( resetDamage );
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForVehicle( this );
+ if ( playerAvatar )
+ {
+ GetSuperCamManager()->GetSCC( playerAvatar->GetPlayerId() )->DoCameraCut();
+ }
+ }
+}
+
+
+//=============================================================================
+// Vehicle::ResetFlagsOnly
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::ResetFlagsOnly(bool resetDamage)
+{
+ mOkToDrawSelf = true;
+
+ mDoingBurnout = false;
+ //mOkToCrashLand = false;
+
+ mDoingRockford = false;
+
+ mSpeedBurstTimer = 0.0f;
+ mEBrakeTimer = 0.0f;
+ mBuildingUpSpeedBurst = false;
+ mDoSpeedBurst = false;
+ mDoingJumpBoost = false;
+ mBrakeLightsOn = false;
+ mReverseLightsOn = false;
+ mCMOffsetSetToOriginal = false;
+ mStuckOnSideTimer = 0.0f;
+
+ if( resetDamage )
+ {
+ mVehicleDestroyed = false;
+ ResetDamageState();
+ mAlreadyPlayedExplosion = false;
+
+ }
+
+
+ mDesiredDoorPosition[0] = mDesiredDoorPosition[1] = 0.0f;
+ mDesiredDoorAction[0] = mDesiredDoorAction[1] = DOORACTION_NONE;
+
+ mDrawWireFrame = false;
+
+ mWasAirborn = false;
+ mWasAirbornTimer = 0.0f;
+
+ mBottomOutSpeedMaintenance = 0.0f;
+
+ // reset the AI pathfinding information, if we're an AI vehicle
+ if( mVehicleType == VT_AI && mVehicleCentralIndex != -1 )
+ {
+ // if we're an AI car, dump our pathfinding data
+ VehicleAI* vAI = dynamic_cast<VehicleAI*>( GetVehicleCentral()->GetVehicleController( mVehicleCentralIndex ) );
+ if( vAI )
+ {
+ vAI->Reset();
+ }
+ }
+}
+
+//=============================================================================
+// Vehicle::ResetOnSpot
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::ResetOnSpot( bool resetDamage /*=true*/ , bool moveCarOntoRoad)
+{
+
+ // find road segment and put them on it
+
+ // one branch or the other will fill these up
+ rmt::Vector newVehiclePosition(0.0f, 0.0f, 0.0f);
+ float ang = 0.0f;
+
+
+ rmt::Vector currentVehiclePosition = this->rPosition();
+ float radius = 100.0f; // this is the radius of query for nearest road
+
+ RoadSegment* roadSeg = NULL;
+ float dummy;
+
+ bool useIntersection = false;
+ Intersection* in = NULL;
+
+
+ // We need to do something special for street races because they have race props,
+ // meaning that resetting onto the "nearest road" can take you outside the race
+ // bounds. For normal race missions, we just find the closest road.
+ //
+ if( GetGameplayManager()->GetCurrentMission() != NULL &&
+ GetGameplayManager()->GetCurrentMission()->IsRaceMission() )
+ {
+ RoadManager::PathElement elem;
+ RoadSegment* seg = 0;
+ float segT;
+ float roadT;
+
+ if( mVehicleType == VT_USER )
+ {
+ //// Use my UBER-search algorithm only when desperate times call for it.
+ //RoadManager::PathfindingOptions options = RoadManager::PO_SEARCH_INTERSECTIONS | RoadManager::PO_SEARCH_ROADS;
+ //RoadManager::GetInstance()->FindClosestPathElement( currentVehiclePosition, radius, options, elem, roadSeg, segT, roadT );
+
+ GetAvatarManager()->GetAvatarForPlayer(0)->GetLastPathInfo(elem, roadSeg, segT, roadT);
+ if( elem.type == RoadManager::ET_INTERSECTION )
+ {
+ useIntersection = true;
+ in = (Intersection*) elem.elem;
+ }
+ }
+ else
+ {
+ rAssert( mVehicleType == VT_AI );
+ VehicleAI* vAI = GetVehicleCentral()->GetVehicleAI( this );
+ rAssert( vAI );
+
+ vAI->GetLastPathInfo( elem, roadSeg, segT, roadT );
+ }
+ }
+ else
+ {
+ GetIntersectManager()->FindClosestRoad( currentVehiclePosition, radius, roadSeg, dummy );
+ }
+
+
+ if( useIntersection )
+ {
+ rAssert( in );
+ in->GetLocation( newVehiclePosition );
+
+ // bump up position
+ float h = this->GetRestHeightAboveGround();
+ h += 1.0f;
+ newVehiclePosition.y += h;
+
+ ang = FacingIntoRad(mVehicleFacing);
+
+ }
+ else
+ {
+ if(roadSeg)
+ {
+ // from the road
+ // want two points - centre of baseline and center of top
+
+ rmt::Vector corner0, corner1, corner2, corner3;
+
+ roadSeg->GetCorner(0, corner0);
+ roadSeg->GetCorner(1, corner1);
+ roadSeg->GetCorner(2, corner2);
+ roadSeg->GetCorner(3, corner3);
+
+ rmt::Vector base = corner0;
+ base.Add(corner3);
+ base.Scale(0.5f);
+
+ rmt::Vector top = corner1;
+ top.Add(corner2);
+ top.Scale(0.5f);
+
+ rmt::Vector centerline = top;
+ centerline.Sub(base);
+
+ // we want the vehicle's new orientation to be along this vector, and the position
+ // to be the projection (shortest distance) of vehicle's position onto this line
+
+ rmt::Vector centerlineDir = centerline;
+
+ rAssert(centerline.Magnitude() > 0.0f);
+
+ centerlineDir.NormalizeSafe();
+
+ rmt::Vector newVehicleFacing = centerlineDir;
+
+ //If this is NOT SuperSprint, do this test. Otherwise the car will
+ //always face along the road.
+ if ( !GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ if(mVehicleFacing.DotProduct(centerlineDir) > 0.0f)
+ {
+ // leave as is
+ }
+ else
+ {
+ newVehicleFacing.Scale(-1.0f);
+ }
+ }
+
+ ang = FacingIntoRad(newVehicleFacing);
+
+
+ // for position, we just use Dusit's magical function
+
+ // the line segment at which this closest point occurs
+ //float FindClosestPointOnLine( const rmt::Vector& start,
+ // const rmt::Vector& end,
+ // const rmt::Vector& p,
+ // rmt::Vector& closestPt );
+
+
+ FindClosestPointOnLine(base, top, currentVehiclePosition, newVehiclePosition);
+
+
+
+
+ // make sure there's no traffic in the way at that point
+ rmt::Sphere s;
+ s.centre = newVehiclePosition;
+ //s.radius = 10.0f;
+ s.radius = this->mWheelBase * 2.0f;
+
+ TrafficManager::GetInstance()->ClearTrafficInSphere(s);
+
+
+
+
+ // bump up position
+ float h = this->GetRestHeightAboveGround();
+ h += 1.0f;
+
+ newVehiclePosition.y += h;
+
+
+ //GetVehicleCentral()->ClearSpot(newVehiclePosition, this->mWheelBase * 2.0f, this);
+
+ }
+ else
+ {
+
+ // this is the old debug version
+ //
+ // literally resets on spot.
+ newVehiclePosition = currentVehiclePosition;
+ newVehiclePosition.y += 1.0f;
+
+ ang = FacingIntoRad(mVehicleFacing);
+ }
+ }
+
+ if(moveCarOntoRoad)
+ {
+
+ rmt::Matrix m;
+
+ m.Identity();
+
+ if ( GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ if ( GetSSM()->IsTrackReversed() )
+ {
+ ang += rmt::PI; //Turn, turn around
+ }
+ }
+
+ //m.FillRotateXYZ( 0.0f, mResetFacingRadians, 0.0f );
+ m.FillRotateXYZ( 0.0f, ang, 0.0f );
+
+ m.FillTranslate(newVehiclePosition);
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ mWheels[i]->Reset();
+ }
+
+ SetTransform( m );
+ }
+
+ ResetFlagsOnly(resetDamage);
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForVehicle( this );
+ if ( playerAvatar )
+ {
+ GetSuperCamManager()->GetSCC( playerAvatar->GetPlayerId() )->DoCameraCut();
+ }
+
+ mAlreadyCalledAutoResetOnSpot = false;
+}
+
+
+//=============================================================================
+// Vehicle::GetFacingInRadians
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float Vehicle::GetFacingInRadians()
+{
+ return FacingIntoRad(mVehicleFacing);
+}
+
+
+//=============================================================================
+// Vehicle::FacingIntoRad
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float Vehicle::FacingIntoRad(rmt::Vector facing)
+{
+ float test = rmt::ATan2(facing.x, facing.z);
+
+ if(rmt::IsNan(test))
+ {
+ return 0.0f;
+ }
+
+ return test;
+ //return 0.0f;
+}
+
+//=============================================================================
+// Vehicle::SetGas
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float gas)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetGas(float gas)
+{
+
+ if(!mVehicleDestroyed && !mGasBrakeDisabled)
+ {
+ mGas = gas;
+ //if(mGas > 0.8f)
+ //{
+ // mGas = 1.0f;
+ //}
+ }
+ else
+ {
+ mGas = 0.0f;
+ }
+}
+
+
+//=============================================================================
+// Vehicle::SetBrake
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float brake)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetBrake(float brake)
+{
+
+ if(!mVehicleDestroyed && !mGasBrakeDisabled)
+ {
+ // normal setting of value
+
+ // the max we will set brake to is inverse of percentage of top speed
+
+ float proj = mVelocityCM.DotProduct(mVehicleFacing);
+ if(proj > 0.0f)
+ {
+ // only do all this crap if going forward, and using brake to slow down, not go in reverse
+
+
+ float maxbrake = 1.0f - this->mPercentOfTopSpeed;
+ if(maxbrake < 0.0f)
+ {
+ maxbrake = 0.0f;
+ }
+
+ if(brake > maxbrake)
+ {
+ brake = maxbrake;
+ }
+
+ if(mGas > 0.1f)
+ {
+ if(brake > mGas)
+ {
+ brake = mGas - 0.1f;
+ }
+ }
+ if(brake < 0.0f)
+ {
+ brake = 0.0f;
+ }
+ }
+
+
+ mBrake = brake;
+ }
+ else
+ {
+ mBrake = 0.0f;
+ }
+
+ /*
+ if(mBrake > 0.0f && !mBrakeLightsOn)
+ {
+ mGeometryVehicle->ShowBrakeLights();
+ mBrakeLightsOn = true;
+ }
+
+ if(mBrake == 0.0f && mBrakeLightsOn)
+ {
+ mGeometryVehicle->HideBrakeLights();
+ mBrakeLightsOn = false;
+ }
+ */
+
+ if(mBrake > 0.0f && !mDontShowBrakeLights)
+ {
+ if(IsInReverse())
+ {
+ if(!mReverseLightsOn)
+ {
+ mGeometryVehicle->ShowReverseLights();
+ mReverseLightsOn = true;
+ }
+ if(mBrakeLightsOn)
+ {
+ mGeometryVehicle->HideBrakeLights();
+ mBrakeLightsOn = false;
+ }
+
+ }
+ else
+ {
+ if(mReverseLightsOn)
+ {
+ mGeometryVehicle->HideReverseLights();
+ mReverseLightsOn = false;
+ }
+
+ if(!mBrakeLightsOn)
+ {
+ mGeometryVehicle->ShowBrakeLights();
+ mBrakeLightsOn = true;
+ }
+ }
+
+ }
+
+ if(mBrake == 0.0f || mDontShowBrakeLights)
+ {
+ if(mBrakeLightsOn)
+ {
+ mGeometryVehicle->HideBrakeLights();
+ mBrakeLightsOn = false;
+ }
+ if(mReverseLightsOn)
+ {
+ mGeometryVehicle->HideReverseLights();
+ mReverseLightsOn = false;
+ }
+ }
+
+}
+
+
+//=============================================================================
+// Vehicle::SetEBrake
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float ebrake)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetEBrake(float ebrake, float dt)
+{
+ const float magicEBrakeTimerLimit = 0.5f; // seconds
+
+ if(ebrake > 0.0f)
+ {
+ //mEBrake = ebrake;
+
+ if(mEBrake == 0.0f)
+ {
+ // ie. the button just went down, reset the timer
+ mEBrakeTimer = 0.0f;
+ }
+
+ // hack for digital
+ mEBrake = 1.0f;
+
+
+ }
+ else if (mEBrakeTimer < magicEBrakeTimerLimit)
+ {
+ // leave EBrake at last set value?
+ }
+ else
+ {
+ mEBrake = 0.0f;
+ mEBrakeTimer = 0.0f;
+
+ }
+
+
+
+ mEBrakeTimer += dt;
+
+
+ // mEBrake = ebrake; old implementation of this method
+
+
+
+}
+
+
+//=============================================================================
+// Vehicle::SetWheelTurnAngle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float wheelTurnAngle, bool doNotModifyInputValue)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetWheelTurnAngle(float wheelTurnAngle, bool doNotModifyInputValue, float dt)
+{
+
+ mUnmodifiedInputWheelTurnAngle = wheelTurnAngle; // just store for later use - things like doughnuts...
+
+ // modify the h/w input to be nicer
+
+ //char dbinfo[64];
+ //sprintf(dbinfo, "input wheel turn angle: %.2f\n", wheelTurnAngle);
+ //DEBUGINFO_ADDSCREENTEXT( dbinfo );
+
+ //doNotModifyInputValue = true; //debug test
+ if(doNotModifyInputValue) // please ignore the name of the param :)
+ {
+ // this is a wheel
+ // scale up input value
+ //const float magicWheelScaleUp = 1.2f;
+ //const float magicWheelScaleUp = 10.2f;
+ const float magicWheelScaleUp = 2.75f;
+ //const float magicWheelScaleUp = 1.5f;
+
+ wheelTurnAngle *= magicWheelScaleUp;
+
+ if(wheelTurnAngle < -1.0f)
+ {
+ wheelTurnAngle = -1.0f;
+ }
+ if(wheelTurnAngle > 1.0f)
+ {
+ wheelTurnAngle = 1.0f;
+ }
+
+
+
+
+
+ }
+ if (1) // plum wants high speed steering drop always
+ {
+ /*
+ if(rmt::Fabs(wheelTurnAngle) < mSteeringInputThreshold)
+ {
+ wheelTurnAngle *= mSteeringPreSlope;
+ }
+ else
+ {
+ float steeringPostSlope = (1.0f - (mSteeringInputThreshold * mSteeringPreSlope)) / (1.0f - mSteeringInputThreshold);
+
+ float pieceBelow = mSteeringInputThreshold * mSteeringPreSlope;
+ float pieceAbove = (rmt::Fabs(wheelTurnAngle) - mSteeringInputThreshold) * steeringPostSlope;
+
+ float wheelTurnAngleMag = pieceBelow + pieceAbove;
+ wheelTurnAngle = wheelTurnAngleMag * rmt::Sign(wheelTurnAngle);
+ }
+ */
+
+ // factor in sensitivity drop as speed increases
+ //
+ // simple slope-intercept formula
+ float slope = mDesignerParams.mDpHighSpeedSteeringDrop - 1.0f; // divided by 1.0 implied
+ float yintercept = 1.0f;
+ // input x is mPercentOfTopSpeed
+
+ float value = slope * mPercentOfTopSpeed + yintercept;
+
+ // this should still be a positive number
+ // use to scale the angle (ie. formulat initialy setup for input value of 1.0)
+
+ if(value < 0.0f)
+ {
+ value = 0.0f; // should never hit this
+ }
+
+ wheelTurnAngle *= value;
+ }
+
+
+
+ //else
+ //{
+ // float maxWheelTurnInRadians = rmt::DegToRadian(mDesignerParams.mDpMaxWheelTurnAngle);
+ // mWheelTurnAngle = wheelTurnAngle * maxWheelTurnInRadians;
+ //}
+
+
+ // if we're below some low speed.....??? 60 kmh... have a time lag to reach the desired angle to reduce twichyness
+ const float thresholdLowSpeed = 90.0f; // kmh
+ const float timeAtZero = 0.3f;
+ if(/*this->mSpeedKmh < thresholdLowSpeed && */ this->mVehicleType == VT_USER && !doNotModifyInputValue)
+ {
+ // closer to zero, the longer the lag.
+
+ // at 60 it would take no time.
+
+ // standard linear drop:
+ //float secondsToChangeOneUnitAtCurrentSpeed = (1.0f - mSpeedKmh / thresholdLowSpeed) * timeAtZero;
+
+ // parabolic drop:
+ float secondsToChangeOneUnitAtCurrentSpeed = ((1.0f - rmt::Sqr(mSpeedKmh / thresholdLowSpeed)) * timeAtZero);
+
+ // new cap for plum
+ if(secondsToChangeOneUnitAtCurrentSpeed < 0.15f)
+ {
+ secondsToChangeOneUnitAtCurrentSpeed = 0.15f;
+ }
+
+ rAssert(secondsToChangeOneUnitAtCurrentSpeed > 0.0f);
+ if(secondsToChangeOneUnitAtCurrentSpeed > 0.0f) // 2 lines of defense
+ {
+ float unitsPerSecond = 1.0f / secondsToChangeOneUnitAtCurrentSpeed;
+
+ // this is the max amount we can change in this frame
+ float unitsInThisFrame = unitsPerSecond * dt;
+
+ // at this point wheelTurnAngle is still our 'target', between 0 and 1
+
+ // we can move a maximum of 'unitsInThisFrame' from mWheelTurnAngleInputValue (last value) towards our target, wheelTurnAngle
+
+ if(wheelTurnAngle > mWheelTurnAngleInputValue)
+ {
+ if(wheelTurnAngle > (mWheelTurnAngleInputValue + unitsInThisFrame))
+ {
+ mWheelTurnAngleInputValue += unitsInThisFrame;
+ }
+ else
+ {
+ mWheelTurnAngleInputValue = wheelTurnAngle;
+ }
+
+
+ }
+ else if(wheelTurnAngle < mWheelTurnAngleInputValue)
+ {
+ if(wheelTurnAngle < (mWheelTurnAngleInputValue - unitsInThisFrame))
+ {
+ mWheelTurnAngleInputValue -= unitsInThisFrame;
+ }
+ else
+ {
+ mWheelTurnAngleInputValue = wheelTurnAngle;
+ }
+
+ }
+ // else if they're == do nothing
+
+ }
+
+ }
+ else
+ {
+ mWheelTurnAngleInputValue = wheelTurnAngle;
+ }
+
+
+ // low speed uturn thingy
+ if(this->mSpeedKmh < 50.0f && this->mVehicleType == VT_USER)
+ {
+ // allow the max wheel turn angel to be higher
+ // ?? try current + 10?
+
+ // normal case - use limit set by designers
+ float maxWheelTurnInRadians = rmt::DegToRadian(mDesignerParams.mDpMaxWheelTurnAngle + 10.0f);
+ //mWheelTurnAngle = wheelTurnAngle * maxWheelTurnInRadians;
+ mWheelTurnAngle = mWheelTurnAngleInputValue * maxWheelTurnInRadians;
+
+
+ }
+ else
+ {
+ // normal case - use limit set by designers
+ float maxWheelTurnInRadians = rmt::DegToRadian(mDesignerParams.mDpMaxWheelTurnAngle);
+ //mWheelTurnAngle = wheelTurnAngle * maxWheelTurnInRadians;
+ mWheelTurnAngle = mWheelTurnAngleInputValue * maxWheelTurnInRadians;
+
+ }
+
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ if(mWheels[i]->mSteerWheel)
+ {
+ mWheels[i]->mWheelTurnAngle = mWheelTurnAngle;
+ }
+ }
+
+ //sprintf(dbinfo, "modified wheel turn angle: %.2f\n", mWheelTurnAngle);
+
+ //DEBUGINFO_ADDSCREENTEXT( dbinfo );
+
+}
+
+
+//=============================================================================
+// Vehicle::SetWheelTurnAngleDirectlyInRadiansForDusitOnly
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float rad)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetWheelTurnAngleDirectlyInRadiansForDusitOnly(float rad)
+{
+ float maxWheelTurnInRadians = rmt::DegToRadian(mDesignerParams.mDpMaxWheelTurnAngle);
+
+ mWheelTurnAngle = rad;
+
+ if(rad > maxWheelTurnInRadians)
+ {
+ mWheelTurnAngle = maxWheelTurnInRadians;
+ }
+ if(rad < -maxWheelTurnInRadians)
+ {
+ mWheelTurnAngle = -maxWheelTurnInRadians;
+ }
+
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ if(mWheels[i]->mSteerWheel)
+ {
+ mWheels[i]->mWheelTurnAngle = mWheelTurnAngle;
+ }
+ }
+
+
+}
+
+//=============================================================================
+// Vehicle::SetReverse
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float reverse)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetReverse(float reverse)
+{
+ mReverse = reverse;
+ if(mReverse > 0.0f)
+ {
+ //rAssertMsg(0, "see greg");
+ //int stophere = 1;
+ }
+}
+
+
+
+//=============================================================================
+// Vehicle::PreSubstepUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::PreSubstepUpdate(float dt)
+{
+ float dirSpeedKmh = mSpeedKmh * ( (mVelocityCM.DotProduct(mVehicleFacing) > 0.0f) ? 1.0f : -1.0f);
+ float deltaKmh = dirSpeedKmh - mLastSpeedKmh;
+ mAccelMss = (deltaKmh / 3600.0f) / (dt / 1000.0f);
+ mLastSpeedKmh = mSpeedKmh;
+
+ mBottomedOutThisFrame = false;
+
+ /*
+ if(mWaitingToSwitchToOutOfCar && mCollisionAreaIndex != -1)
+ {
+ mOutOfCarSwitchTimer += dt;
+ if(mOutOfCarSwitchTimer > 2.0f)
+ {
+ mOutOfCarSwitchTimer = 0.0f;
+ mWaitingToSwitchToOutOfCar = false;
+
+ SetOutOfCarSimState();
+
+ }
+
+ }
+ */
+
+ mVelocityCMLag = mVelocityCM;
+ mPositionCMLag = this->rPosition();
+
+ mVehicleLocomotion->PreSubstepUpdate();
+}
+
+
+
+//=============================================================================
+// Vehicle::PreCollisionPrep
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::PreCollisionPrep(float dt, bool firstSubstep)
+{
+ BEGIN_PROFILE("mGeometryVehicle->Update")
+ mGeometryVehicle->Update(dt); // seems to work fine before
+ END_PROFILE("mGeometryVehicle->Update")
+
+ // new
+ // TODO - how to improve the interfaces
+ BEGIN_PROFILE("mVehicleLocomotion->PreCollisionPrep")
+ mVehicleLocomotion->PreCollisionPrep(firstSubstep);
+ END_PROFILE("mVehicleLocomotion->PreCollisionPrep")
+
+ // used by physicslocomotion in suspension force calculation
+ // but set by redbrickcollisionsolveragent - yuck
+ mDamperShouldNotPullDown[0] = false;
+ mDamperShouldNotPullDown[1] = false;
+ mDamperShouldNotPullDown[2] = false;
+ mDamperShouldNotPullDown[3] = false;
+
+ // reset this flag
+ //mHitJoint = -1;
+
+}
+
+
+//=============================================================================
+// Vehicle::SetNoDamperDownFlagOnWheel
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int wheelIndex)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetNoDamperDownFlagOnWheel(int wheelIndex)
+{
+ mDamperShouldNotPullDown[wheelIndex] = true; // just for this frame
+}
+
+
+//=============================================================================
+// Vehicle::CalculateSuspensionLocationAndVelocity
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::CalculateSuspensionLocationAndVelocity()
+{
+ // TODO - is this ok to actually change what we call world space suspension point for this frame if we're airborn???
+
+ // do airborn calculation here?
+ // only look at wheels 2,3
+
+ // recall - right now this is getting called from physicslocomotion::update, after collision detection
+
+
+ //if( !(mWheels[2]->mWheelInCollision) && !(mWheels[3]->mWheelInCollision))
+ if( !(mWheels[2]->mWheelInCollision) || !(mWheels[3]->mWheelInCollision))
+ {
+ // consider this airbor
+ // even if we're just cresting hill.
+ mSteeringWheelsOutOfContact = true;
+ }
+ else
+ {
+ mSteeringWheelsOutOfContact = false;
+ }
+
+
+ // air born test
+ int i;
+ int count = 0;
+ for(i = 0; i < 4; i++)
+ {
+ if(!(mWheels[i]->mWheelInCollision))
+ {
+ count++;
+ }
+ }
+
+ if(count > 3)
+ {
+ mAirBorn = true;
+ mWasAirborn = true;
+ mWasAirbornTimer = 0.0f;
+ }
+ else
+ {
+ mAirBorn = false;
+ }
+
+
+
+
+
+
+ // TODO - how 'bout flipped?
+
+
+ for(i = 0; i < 4; i++)
+ {
+
+ mSuspensionWorldSpacePoints[i] = mSuspensionRestPoints[i];
+
+
+ //if(1)//mSteeringWheelsOutOfContact)
+ if(mSteeringWheelsOutOfContact)// || mVehicleState == VS_NORMAL)
+ {
+ // TODO - revisit this - I don't like it
+
+ // ok, this was a serious fuck up
+ // revisit during the tipping likelihood thing
+
+ //mSuspensionWorldSpacePoints[i].y = mCMOffset.y;
+ }
+
+ mSuspensionWorldSpacePoints[i].Transform(mTransform);
+
+ mSimStateArticulated->GetVelocity(mSuspensionWorldSpacePoints[i], mSuspensionPointVelocities[i]);
+
+ }
+
+
+ // note:
+ // mSteeringWheelsOutOfContact is a bit of a misnomer
+
+ // do a test here for real airborn, all 4 wheels off, and vehicle upright
+ //
+ // this is the only place where mOkToCrashLand will get set to true
+ //
+ /*
+ if( !(mWheels[0]->mWheelInCollision) && !(mWheels[1]->mWheelInCollision) &&
+ !(mWheels[2]->mWheelInCollision) && !(mWheels[3]->mWheelInCollision) &&
+ mVehicleUp.y > 0.0f)
+ {
+ mOkToCrashLand = true;
+ }
+ */
+
+ // TODO - around here somewhere is the place to calculate and set a big-air event
+
+}
+
+
+
+//=============================================================================
+// Vehicle::JumpOnHorn
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float test)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::JumpOnHorn(float test)
+{
+ if(!(this->mAirBorn))
+ {
+
+ if(GetSimState()->GetControl() == sim::simAICtrl)
+ {
+ GetSimState()->SetControl(sim::simSimulationCtrl);
+ }
+
+ rmt::Vector boost = mVehicleFacing;
+ boost.y += 2.0f;
+
+ static float hack = 5.0f;
+ //const float hack = 2.0f;
+
+ boost.Scale(test * hack);
+
+ rmt::Vector& linearVel = mSimStateArticulated->GetLinearVelocity();
+ linearVel.Add(boost);
+
+ if(GetSimState()->GetControl() == sim::simAICtrl)
+ {
+ GetSimState()->SetControl(sim::simSimulationCtrl);
+ }
+ }
+}
+
+
+void Vehicle::TurboOnHorn()
+{
+ /*static*/ const float SECONDS_TURBO_RECHARGE = 0.5f;
+ /*static*/ const float TURBO_SPEED_MPS = 10.0f;
+
+ if( mSecondsTillNextTurbo <= 0.0f )
+ {
+ if( mNumTurbos > 0 )
+ {
+ // apply turbo speed
+ if(GetSimState()->GetControl() == sim::simAICtrl)
+ {
+ GetSimState()->SetControl(sim::simSimulationCtrl);
+ }
+ rmt::Vector turbo = mVehicleFacing * TURBO_SPEED_MPS;
+ rmt::Vector& linearVel = mSimStateArticulated->GetLinearVelocity();
+ linearVel.Add( turbo );
+
+ /*
+ // TODO:
+ // Maybe play a sound effect? Pass in the vehicle pointer? Ask Esan
+ ::GetEventManager()->TriggerEvent( EVENT_USE_NITRO, this );
+ */
+
+ // decrement number of turbos
+ mNumTurbos--;
+
+ // reset mSecondsTillNextTurbo
+ mSecondsTillNextTurbo = SECONDS_TURBO_RECHARGE;
+ }
+ }
+}
+
+
+//=============================================================================
+// Vehicle::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::Update(float dt)
+{
+ // Update gas, if the change in gas is significant
+
+ if(mLocoSwitchedToPhysicsThisFrame)
+ {
+ this->PreCollisionPrep(dt, true);
+ }
+ mLocoSwitchedToPhysicsThisFrame = false;
+
+
+
+ mDeltaGas = (mGas - mLastGas) / dt;
+ mLastGas = mGas;
+
+ // update turbo wind down time
+ if( mSecondsTillNextTurbo > 0.0f )
+ {
+ mSecondsTillNextTurbo -= dt;
+ }
+
+ mNoDamageTimer -= dt;
+ if(mNoDamageTimer < 0.0f)
+ {
+ mNoDamageTimer = 0.0f;
+ }
+
+ // TODO - before or after physics?
+ //mGeometryVehicle->Update(dt); // seems to work fine before
+
+ // don't need this if traffic!
+ //
+ // actually, need a different one if this is traffic
+ //CalculateSuspensionLocationAndVelocity(); // TODO - ? shouldn't this also be done before FetchingWheelTerrainCollisionInfo
+ // moved to PhysicsLocomotion::PreUpdate
+
+ // break up of work into pre and post update, outside of update, is a bit arbitrary
+ // I suppose
+
+ mSimStateArticulated->StoreJointState(dt);
+
+ mVehicleLocomotion->PreUpdate();
+
+ if(mVehicleType != VT_AI && GetGameplayManager()->GetGameType() != GameplayManager::GT_SUPERSPRINT)
+ {
+ if(mCollisionLateralResistanceDropFactor < 1.0f)
+ {
+ mCollisionLateralResistanceDropFactor += dt;
+ if(mCollisionLateralResistanceDropFactor > 1.0f)
+ {
+ mCollisionLateralResistanceDropFactor = 1.0f;
+ }
+ }
+ }
+ else
+ {
+ mCollisionLateralResistanceDropFactor = 1.0f;
+ }
+
+ mVehicleLocomotion->Update(dt);
+ // basically - the only thing that is different after this is the mTransform,
+ // whether traffic or physics
+ //
+ // ?? maybe traffic should just set mTransform directly
+ // also set mSpeed???
+
+
+ mVehicleLocomotion->PostUpdate();
+
+
+ // TODO - trust these are normalized?
+ mVehicleFacing = mTransform.Row(2);
+ mVehicleUp = mTransform.Row(1);
+ mVehicleTransverse = mTransform.Row(0);
+
+ // is this gonna hurt framrate?
+ mVehicleFacing.Normalize();
+ mVehicleUp.Normalize();
+ mVehicleTransverse.Normalize();
+
+
+ // this is good
+ // can leave as is for both traffic and physics
+ //
+ // just have to make sure that mSuspensionPointVelocities are correct
+ // and it will use mSteeringWheelAngle if set.
+ UpdateWheelRenderingInfo(dt);
+
+ SetGeometryVehicleWheelSmokeLevel();
+
+ if(mBurnoutLevel > 0.0f)
+ {
+ if ( !mDoingBurnout )
+ {
+ GetEventManager()->TriggerEvent( EVENT_BURNOUT );
+ mDoingBurnout = true;
+ }
+ }
+ else
+ {
+ if ( mDoingBurnout )
+ {
+ GetEventManager()->TriggerEvent( EVENT_BURNOUT_END );
+ mDoingBurnout = false;
+ }
+ }
+
+
+
+ mSpeedKmh = mSpeed * 3.6f; // for watcher...
+
+
+ int i;
+ for (i = 0; i < mPoseEngine->GetPassCount(); i++)
+ {
+ mPoseEngine->Advance(i, dt);
+ mPoseEngine->Update(i);
+ }
+
+ // calculate forced door opening as a result of character getting into/out of car
+ // TODO : this should probably be doen in the poseengine , but I don't yet understand
+ // enough about what poseengine/posedriver are actually doing to do that
+ CalcDoors();
+
+ // cap on huge impulses
+ const float hugeSpeed = 200.0f; // that's about 720 kmh
+ if(this->mSpeed > hugeSpeed)
+ {
+ // this is really fucking bad
+ //rAssertMsg(0,"tell greg how you did this");
+ mSimStateArticulated->ResetVelocities();
+ mSimStateArticulated->GetSimulatedObject()->ResetCache();
+
+ this->ResetOnSpot(false);
+ // mVehicleLocomotion->PostUpdate();
+
+
+ }
+
+ if(1)//mLoco == VL_PHYSICS)
+ {
+ mSimStateArticulated->UpdateJointState(dt);
+ }
+
+
+ // for debugging:
+ // we want the collision objects to draw in the correct places
+ // TODO - want to call this here! every frame!!
+ CollisionObject* collObj = mSimStateArticulated->GetCollisionObject();
+ collObj->Update();
+
+ DSGUpdateAndMove();
+
+ //--------------------------
+ // do some gear and rpm shit
+ //--------------------------
+
+ UpdateGearAndRPM();
+
+ // Update the trigger volume.
+ mpTriggerVolume->SetTransform( mTransform);
+
+ mWasAirbornTimer += dt;
+ if(mWasAirbornTimer > 1.0f)
+ {
+ mWasAirborn = false;
+ }
+
+ if(mBottomedOutThisFrame && mWasAirborn)
+ {
+ // try this here for Esan
+ mWasAirborn = false;
+ if( mTerrainType == TT_Road || mTerrainType == TT_Metal || mTerrainType == TT_Gravel )
+ {
+ GetSparkleManager()->AddBottomOut( GetPosition() );
+ }
+ GetEventManager()->TriggerEvent(EVENT_VEHICLE_SUSPENSION_BOTTOMED_OUT, (void*)this);
+ //rDebugPrintf("vehicle bottomed out this frame \n");
+
+
+
+ }
+
+}
+
+
+//=============================================================================
+// Vehicle::GetGroundY
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float Vehicle::GetGroundY()
+{
+ // for James, for the shadows...
+
+ if(this->mLoco == VL_PHYSICS)
+ {
+ // need this value for either case
+
+ float avg = 0.0f;
+ avg = mPhysicsLocomotion->mTerrainIntersectCache[0].planePosn.y +
+ mPhysicsLocomotion->mTerrainIntersectCache[1].planePosn.y +
+ mPhysicsLocomotion->mTerrainIntersectCache[2].planePosn.y +
+ mPhysicsLocomotion->mTerrainIntersectCache[3].planePosn.y;
+
+ avg *= 0.25;
+
+ if(mAirBorn) // all four wheels out of contact
+ {
+ // in this situation, give the average of the terrain intersect caches?
+ return avg;
+ }
+ else
+ {
+ // here at least one wheel is in collision
+ // so... give the average y of all the wheels that are in collision
+ float wheelavg = 0.0f;
+ float count = 0.0f;
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ if(mWheels[i]->mWheelInCollision)
+ {
+ count = count + 1.0f;
+
+ poser::Pose* pose = mPoseEngine->GetPose();
+
+ poser::Joint* joint = pose->GetJoint(mWheelToJointIndexMapping[i]);
+ rmt::Vector trans = joint->GetWorldTranslation();
+
+ wheelavg += ( trans.y - mWheels[ i ]->mRadius );
+ }
+ }
+
+ if(count > 0.0f)
+ {
+ return wheelavg / count;
+ }
+ return avg;
+
+ }
+ }
+ else
+ {
+ float y = mTransform.m[3][1];
+ float diff = GetRestHeightAboveGround();
+
+ y -= diff;
+
+ return y;
+ }
+
+ // should never get here
+ return 0.0f;
+}
+
+
+// I'm hiding. I don't have a comment header. I'm a fugitive. I am hunted.
+//
+// that's ok Dusit, here you go:
+
+//=============================================================================
+// Vehicle::PostSubstepUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::PostSubstepUpdate(float dt)
+{
+ //-------------------
+ // reset input values
+ //-------------------
+// mGas = 0.0f; // for tony, for hud
+// mBrake = 0.0f; ? safe to do this - want the IsInReverse method to work afterall...
+ //mWheelTurnAngle = 0.0f;
+ mReverse = 0.0f;
+ //mEBrake = 0.0f;
+
+ // skid setting
+
+ if( mVehicleID == VehicleEnum::HUSKA )
+ {
+ rmt::Vector vel = mVelocityCM;
+ if( ( rmt::Abs( vel.x ) + rmt::Abs( vel.z ) > 0.5f ) && ( mPhysicsLocomotion ) )
+ {
+ vel.y = 0.0f;
+ vel.Normalize();
+ // Make a sound effects call here.
+ for( int wi = 0; wi < 2; ++wi )
+ {
+ eTerrainType tt = mPhysicsLocomotion->mTerrainIntersectCache[ wi ].mTerrainType;
+ if( tt == TT_Road || tt == TT_Metal || tt == TT_Gravel )
+ {
+ rmt::Vector pos = mSuspensionRestPoints[ wi ];
+ pos.y -= mWheels[ wi ]->mRadius;
+ pos.y += mWheels[ wi ]->mYOffset;
+ GetTransform().Transform( pos, &pos );
+ GetSparkleManager()->AddSparks( pos, vel, 0.1f );
+ }
+ }
+ }
+ }
+
+ if(!mNoSkid)
+ {
+ //if(mSkidLevel > 0.0f)// && mVehicleType == VT_USER)
+
+ if(mLoco == VL_PHYSICS)
+ {
+ int i;
+ if(mBurnoutLevel > 0.0f)
+ {
+ for(i = 0; i < 2; i++)
+ {
+ if(mWheels[i]->mWheelInCollision) // will this value still be valid here?
+ { // need to fetch ground plane normal also
+ rAssert(mPhysicsLocomotion);
+ rmt::Vector normal = mPhysicsLocomotion->mIntersectNormalUsed[i];
+
+ mGeometryVehicle->SetSkidValues(i, mSkidLevel, normal, mPhysicsLocomotion->mTerrainIntersectCache[i].mTerrainType );
+
+ }
+ }
+ }
+ else
+ {
+ int numWheels = mNoFrontSkid ? 2 : 4; // We want to determine how many wheels
+ // generate skids, taking advantage of the fact that wheels 0 and 1 are always the
+ // back wheels.
+ for(i = 0; i < numWheels; i++)
+ {
+ if(mWheels[i]->mWheelInCollision) // will this value still be valid here?
+ {
+ rAssert(mPhysicsLocomotion);
+ rmt::Vector normal = mPhysicsLocomotion->mIntersectNormalUsed[i];
+
+ mGeometryVehicle->SetSkidValues(i, mSkidLevel, normal, mPhysicsLocomotion->mTerrainIntersectCache[i].mTerrainType );
+ }
+ }
+ }
+ }
+
+
+ mGeometryVehicle->UpdateSkids();
+
+ }
+
+ int i;
+ int count = 0;
+ for(i = 0; i < 4; i++)
+ {
+ if(mWheels[i]->mWheelInCollision)
+ {
+ count++;
+ }
+ }
+
+
+ // only want to do the following two tests if we are the avatar's car
+ //
+ // unless, we are supersprint, then we want to do it for all cars, with no fade to black...
+ if(GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle() == this ||
+ GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT)
+ {
+
+ if(count == 0 && GetWorldPhysicsManager()->mWorldUp.DotProduct(mVehicleUp) < 0.0f)
+ {
+ float linear = mPastLinear.Smooth(GetSimState()->GetLinearVelocity().Magnitude());
+ float angular = mPastAngular.Smooth(GetSimState()->GetAngularVelocity().Magnitude());
+
+ // flipped
+ if(rmt::Fabs(linear) < REST_LINEAR_TOL * 3.0f && rmt::Fabs(angular) < REST_ANGULAR_TOL && !mAlreadyCalledAutoResetOnSpot)
+ {
+ // put it at rest
+ // reset
+
+ if(GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT)
+ {
+ this->ResetOnSpot(false);
+ }
+ else
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_MANUAL_RESET, reinterpret_cast< unsigned int >(this) );
+ }
+
+ mAlreadyCalledAutoResetOnSpot = true;
+
+ }
+
+ }
+
+ // other on side reset test:
+ const float lowspeed = 1.0f;
+ if(mSpeed < lowspeed)
+ {
+
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = mVehicleUp.DotProduct(up);
+
+ //float cos85 = 0.0872f;
+ float test = 0.2f;
+ if(tip < test)
+ {
+
+ // anytime we're in here we should incrememnt the timer
+ mStuckOnSideTimer += dt;
+ if(mStuckOnSideTimer > 1.0f && !mAlreadyCalledAutoResetOnSpot) // yeah for magic numbers!
+ {
+ mStuckOnSideTimer = 0.0f; /// this is done in the reset anyway, but easier to see like this
+
+ if(GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT)
+ {
+ this->ResetOnSpot(false);
+ }
+ else
+ {
+ GetGuiSystem()->HandleMessage( GUI_MSG_MANUAL_RESET, reinterpret_cast< unsigned int >(this) );
+ }
+
+ mAlreadyCalledAutoResetOnSpot = true;
+
+ }
+ }
+ else
+ {
+ // just in case
+ mStuckOnSideTimer = 0.0f;
+ }
+ }
+ else
+ {
+ // not satisfied the on-side test so reset stuckonsidetimer
+ mStuckOnSideTimer = 0.0f;
+ }
+
+ }
+
+
+ // revisit this
+ //
+ // with new husk system all you have to do here for all types of cars is play an explosion
+ //
+ // that should probably move to where the event is fired - so that we don't swap out the car and replace with husk before this?
+
+ if(mVehicleID != VehicleEnum::HUSKA)
+ {
+ if( mVehicleDestroyed && !mAlreadyPlayedExplosion )
+ {
+ // Lets detach any objects from this vehicle on explosion
+ DetachCollectible( rmt::Vector( 0, 0, 0 ), true );
+ if( this->mVehicleType == VT_USER || GetGameplayManager()->mIsDemo )
+ {
+ mDamageOutResetTimer += dt;
+ if(mDamageOutResetTimer > 3.0f)
+ {
+ // kaboom
+ PlayExplosionEffect();
+
+ mAlreadyPlayedExplosion = true; // this will get reset immediately in ResetOnSpot
+
+ if ( GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ ResetOnSpot( true );
+ }
+ else
+ {
+ // appropriate manager should catch this and swap in husk
+ GetEventManager()->TriggerEvent(EVENT_VEHICLE_DESTROYED, (void*)this);
+ }
+ }
+
+ }
+ else
+ {
+ mDamageOutResetTimer += dt;
+ if( GetGameplayManager()->GetGameType() == GameplayManager::GT_NORMAL ||
+ mDamageOutResetTimer > 3.0f )
+ {
+ // kaboom
+ PlayExplosionEffect();
+ mAlreadyPlayedExplosion = true; // this will get reset immediately in ResetOnSpot
+
+ if ( GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ ResetOnSpot( true );
+ }
+ else
+ {
+ // appropriate manager should catch this and swap in husk
+ GetEventManager()->TriggerEvent(EVENT_VEHICLE_DESTROYED, (void*)this);
+ if(mWasHitByVehicleType != VT_AI)
+ {
+ GetEventManager()->TriggerEvent(EVENT_VEHICLE_DESTROYED_BY_USER, (void*)this);
+ }
+
+ }
+ }
+ }
+ }
+
+ }
+
+ /*
+ if(mDamageType == VDT_AI && mVehicleDestroyed && !mAlreadyPlayedExplosion)
+ {
+ // just kaboom and it sits there dead...... for now
+ PlayExplosionEffect();
+ mAlreadyPlayedExplosion = true;
+
+ }
+
+ if(mDamageType == VDT_TRAFFIC && mVehicleDestroyed && !mAlreadyPlayedExplosion && this->mVehicleID != VehicleEnum::HUSKA)
+ {
+ PlayExplosionEffect();
+ mAlreadyPlayedExplosion = true;
+
+ // Dusit here...
+ // It seems that the way we set mDamageType to VDT_TRAFFIC doesn't necessarily
+ // imply that this is a traffic car. SwapInTrafficHusk is only safe to call if
+ // the car is actually a traffic car (owned and initialized by TrafficManager)
+ // So... if it's not a traffic car, we won't call SwapInTrafficHusk... It will
+ // just sit there after explosions... for now... just like what's done for VDT_AI
+ //
+ if( this->mVehicleType == VT_TRAFFIC )
+ {
+ TrafficManager::GetInstance()->SwapInTrafficHusk(this);
+ }
+ }
+ */
+
+
+ // hmm... this might be the place to try moving the characters?
+ BounceCharacters(dt);
+
+
+
+}
+
+
+//=============================================================================
+// Vehicle::BounceCharacters
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::BounceCharacters(float dt)
+{
+
+ if(mVehicleType == VT_USER)
+ {
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if(sDoBounce && (playerAvatar->GetCharacter()->GetStateManager()->GetState() == CharacterAi::INCAR) && playerAvatar->GetVehicle() == this)
+ {
+ mYAccelForSeatingOffset = (mVelocityCM.y - mVelocityCMLag.y) / dt;
+ // seems to hover around -1.0f to 1.0f a lot - goes up to 8 or 10 occassionally
+
+ if(rmt::Fabs(mYAccelForSeatingOffset) > mBounceAccelThreshold)
+ {
+ // want to displace it opposite the accel dir, proportional to the amount?
+ //static float magicShitScale = 0.25f;
+ const float magicShitScale = 0.003f;
+ float amountToDisplace = mYAccelForSeatingOffset * magicShitScale * -1.0f;
+ if(rmt::Fabs(amountToDisplace) > mMaxBounceDisplacementPerSecond * dt)
+ {
+ amountToDisplace = mMaxBounceDisplacementPerSecond * dt * rmt::Sign(amountToDisplace);
+ }
+
+ ApplyDisplacementToCharacters(amountToDisplace);
+
+ }
+ else
+ {
+ // want to move back to the middle
+ MoveCharactersTowardsRestPosition(dt);
+
+ }
+
+ }
+
+ }
+
+}
+
+
+//=============================================================================
+// Vehicle::MoveCharactersTowardsRestPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::MoveCharactersTowardsRestPosition(float dt)
+{
+ // let's say, for first try, move at half max diplacment speed
+ float rate = 0.5f;
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if( mpDriver && mpDriver != playerAvatar->GetCharacter() )
+ {
+ // there is both a driver and us
+ //
+ // sort of clunky to set every frame but whatever...
+ mNPCRestSeatingPosition = mDriverLocation;
+ mOurRestSeatingPosition = mPassengerLocation;
+
+ // hackey, hackey - need to slide lisa up a little because of dress poking through car
+ if(mpDriver->IsLisa())
+ {
+ mNPCRestSeatingPosition.y += 0.12f;
+ }
+
+ if(playerAvatar->GetCharacter()->IsLisa())
+ {
+ mOurRestSeatingPosition.y += 0.12f;
+ }
+
+ // us
+ Character* us = playerAvatar->GetCharacter();
+ choreo::Puppet* ourPuppet = us->GetPuppet();
+ const rmt::Vector& ourPuppetPosition = ourPuppet->GetPosition();
+ rmt::Vector newOurPuppetPosition = ourPuppetPosition;
+
+ // compare rest pos
+ float diff = newOurPuppetPosition.y - mOurRestSeatingPosition.y;
+ float maxchange = rate * dt * mMaxBounceDisplacementPerSecond;
+ if(rmt::Fabs(diff) <= maxchange)
+ {
+ // easy - back at rest
+ newOurPuppetPosition.y = mOurRestSeatingPosition.y;
+ ourPuppet->SetPosition(newOurPuppetPosition);
+ }
+ else if(diff < 0.0f)
+ {
+ // add to it
+ newOurPuppetPosition.y += maxchange;
+ ourPuppet->SetPosition(newOurPuppetPosition);
+
+ }
+ else
+ {
+ // subtract
+ newOurPuppetPosition.y -= maxchange;
+ ourPuppet->SetPosition(newOurPuppetPosition);
+
+ }
+
+
+ // driver
+ choreo::Puppet* npcPuppet = mpDriver->GetPuppet();
+ const rmt::Vector& npcPuppetPosition = npcPuppet->GetPosition();
+ rmt::Vector newNPCPuppetPosition = npcPuppetPosition;
+
+
+ diff = newNPCPuppetPosition.y - mNPCRestSeatingPosition.y;
+ //maxchange = rate * dt * mMaxBounceDisplacementPerSecond;
+ if(rmt::Fabs(diff) <= maxchange)
+ {
+ // easy - back at rest
+ newNPCPuppetPosition.y = mNPCRestSeatingPosition.y;
+ npcPuppet->SetPosition(newNPCPuppetPosition);
+ }
+ else if(diff < 0.0f)
+ {
+ // add to it
+ newNPCPuppetPosition.y += maxchange;
+ npcPuppet->SetPosition(newNPCPuppetPosition);
+
+ }
+ else
+ {
+ // subtract
+ newNPCPuppetPosition.y -= maxchange;
+ npcPuppet->SetPosition(newNPCPuppetPosition);
+
+ }
+ }
+ else
+ {
+ // if we're the driver... or if no driver (so we must be the driver)
+
+ mOurRestSeatingPosition = mDriverLocation;
+
+ if(playerAvatar->GetCharacter()->IsLisa())
+ {
+ mOurRestSeatingPosition.y += 0.12f;
+ }
+
+ // just us drivign?
+ Character* us = playerAvatar->GetCharacter();
+ choreo::Puppet* ourPuppet = us->GetPuppet();
+ const rmt::Vector& ourPuppetPosition = ourPuppet->GetPosition();
+ rmt::Vector newOurPuppetPosition = ourPuppetPosition;
+
+ // compare rest pos
+ float diff = newOurPuppetPosition.y - mOurRestSeatingPosition.y;
+ float maxchange = rate * dt * mMaxBounceDisplacementPerSecond;
+ if(rmt::Fabs(diff) <= maxchange)
+ {
+ // easy - back at rest
+ newOurPuppetPosition.y = mOurRestSeatingPosition.y;
+ ourPuppet->SetPosition(newOurPuppetPosition);
+ }
+ else if(diff < 0.0f)
+ {
+ // add to it
+ newOurPuppetPosition.y += maxchange;
+ ourPuppet->SetPosition(newOurPuppetPosition);
+
+ }
+ else
+ {
+ // subtract
+ newOurPuppetPosition.y -= maxchange;
+ ourPuppet->SetPosition(newOurPuppetPosition);
+
+ }
+ }
+}
+
+
+//=============================================================================
+// Vehicle::ApplyDisplacementToCharacters
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float displacement)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::ApplyDisplacementToCharacters(float displacement)
+{
+ // now either we're driving alone or we're a passenger and there's a driver
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if( mpDriver && mpDriver != playerAvatar->GetCharacter() )
+ {
+ // sort of clunky to set every frame but whatever...
+ mNPCRestSeatingPosition = mDriverLocation;
+ mOurRestSeatingPosition = mPassengerLocation;
+
+
+ // there is both a driver and us
+ Character* us = playerAvatar->GetCharacter();
+ choreo::Puppet* ourPuppet = us->GetPuppet();
+ const rmt::Vector& ourPuppetPosition = ourPuppet->GetPosition();
+ rmt::Vector newOurPuppetPosition = ourPuppetPosition;
+
+ //newOurPuppetPosition.y += 0.001f;
+ newOurPuppetPosition.y += displacement;
+
+ // test against limit
+ if(newOurPuppetPosition.y > mOurRestSeatingPosition.y + mBounceLimit)
+ {
+ newOurPuppetPosition.y = mOurRestSeatingPosition.y + mBounceLimit;
+ }
+ if(newOurPuppetPosition.y < mOurRestSeatingPosition.y - mBounceLimit)
+ {
+ newOurPuppetPosition.y = mOurRestSeatingPosition.y - mBounceLimit;
+ }
+
+ ourPuppet->SetPosition(newOurPuppetPosition);
+
+
+ choreo::Puppet* npcPuppet = mpDriver->GetPuppet();
+ const rmt::Vector& npcPuppetPosition = npcPuppet->GetPosition();
+ rmt::Vector newNPCPuppetPosition = npcPuppetPosition;
+
+ newNPCPuppetPosition.y += displacement;
+
+ // test against limit
+ if(newNPCPuppetPosition.y > mNPCRestSeatingPosition.y + mBounceLimit)
+ {
+ newNPCPuppetPosition.y = mNPCRestSeatingPosition.y + mBounceLimit;
+ }
+ if(newNPCPuppetPosition.y < mNPCRestSeatingPosition.y - mBounceLimit)
+ {
+ newNPCPuppetPosition.y = mNPCRestSeatingPosition.y - mBounceLimit;
+ }
+
+ npcPuppet->SetPosition(newNPCPuppetPosition);
+
+ }
+ else
+ {
+ // just us drivign?
+ mOurRestSeatingPosition = mDriverLocation;
+
+ Character* us = playerAvatar->GetCharacter();
+ choreo::Puppet* ourPuppet = us->GetPuppet();
+ const rmt::Vector& ourPuppetPosition = ourPuppet->GetPosition();
+ rmt::Vector newOurPuppetPosition = ourPuppetPosition;
+
+ //newOurPuppetPosition.y += 0.001f;
+ newOurPuppetPosition.y += displacement;
+
+
+ if(newOurPuppetPosition.y > mOurRestSeatingPosition.y + mBounceLimit)
+ {
+ newOurPuppetPosition.y = mOurRestSeatingPosition.y + mBounceLimit;
+ }
+ if(newOurPuppetPosition.y < mOurRestSeatingPosition.y - mBounceLimit)
+ {
+ newOurPuppetPosition.y = mOurRestSeatingPosition.y - mBounceLimit;
+ }
+ ourPuppet->SetPosition(newOurPuppetPosition);
+ }
+}
+
+
+//=============================================================================
+// Vehicle::RecordRestSeatingPositionsOnEntry
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::RecordRestSeatingPositionsOnEntry()
+{
+ // same logic as BounceCharacters to decide which of these we need
+
+ return;
+
+ if(mVehicleType == VT_USER)
+ {
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if(playerAvatar->IsInCar() && playerAvatar->GetVehicle() == this)
+ {
+ // now either we're driving alone or we're a passenger and there's a driver
+ if(this->mpDriver)
+ {
+ // there is a driver
+ if(this->mpDriver == playerAvatar->GetCharacter())
+ {
+ // don't think we should ever hit this - the mpDriver is an NPC driver
+ int stophere = 1;
+ }
+ else
+ {
+ // there is both a driver and us
+ Character* us = playerAvatar->GetCharacter();
+ choreo::Puppet* ourPuppet = us->GetPuppet();
+ const rmt::Vector& ourPuppetPosition = ourPuppet->GetPosition();
+ mOurRestSeatingPosition = ourPuppetPosition;
+
+ choreo::Puppet* npcPuppet = mpDriver->GetPuppet();
+ const rmt::Vector& npcPuppetPosition = npcPuppet->GetPosition();
+ mNPCRestSeatingPosition = npcPuppetPosition;
+
+
+ }
+ }
+ else
+ {
+ // just us drivign?
+ Character* us = playerAvatar->GetCharacter();
+ choreo::Puppet* ourPuppet = us->GetPuppet();
+ const rmt::Vector& ourPuppetPosition = ourPuppet->GetPosition();
+ mOurRestSeatingPosition = ourPuppetPosition;
+
+
+ }
+
+ }
+
+ }
+}
+
+
+//=============================================================================
+// Vehicle::RestTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (void)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::RestTest(void)
+{
+ // only traffic rests
+ if(mVehicleType != VT_TRAFFIC)
+ {
+ return;
+ }
+
+ if(!GetSimState() || GetSimState()->GetControl() == sim::simAICtrl)
+ {
+ // already at rest
+ return;
+ }
+
+ // check if smoothed velocities are below tolerance
+ if((mPastLinear.Smooth(GetSimState()->GetLinearVelocity().Magnitude()) < REST_LINEAR_TOL) &&
+ (mPastAngular.Smooth(GetSimState()->GetAngularVelocity().Magnitude()) < REST_ANGULAR_TOL))
+ {
+ // put it at rest
+ // this is done inside the loco-switch
+ //GetSimState()->SetControl(sim::simAICtrl);
+ //GetSimState()->ResetVelocities();
+
+ }
+}
+
+
+
+//=============================================================================
+// Vehicle::SelfRestTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (void)
+//
+// Return: void
+//
+//=============================================================================
+bool Vehicle::SelfRestTest(void)
+{
+ // new version of this for user and ai cars to call on themselves
+ // during physicslocomotion updates so that at very low speeds they come to a complete stop
+
+ if(!GetSimState())
+ {
+ rAssert(0);
+ return false;
+ }
+
+
+ int i;
+ int count = 0;
+ for(i = 0; i < 4; i++)
+ {
+ if(mWheels[i]->mWheelInCollision)
+ {
+ count++;
+ }
+ }
+
+ /*
+ if(count == 0 && GetWorldPhysicsManager()->mWorldUp.DotProduct(mVehicleUp) < 0.0f)
+ {
+ // flipped
+ if((mPastLinear.Smooth(GetSimState()->GetLinearVelocity().Magnitude()) < REST_LINEAR_TOL) &&
+ (mPastAngular.Smooth(GetSimState()->GetAngularVelocity().Magnitude()) < REST_ANGULAR_TOL))
+ {
+ // put it at rest
+ // reset
+
+ this->ResetOnSpot(false);
+
+ }
+
+ }
+ */
+ Vehicle* playerVehicle = GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle();
+ //Vehicle* playerVehicle = GetGameplayManager()->GetCurrentVehicle();
+
+ if(mGas < 0.1f && mBrake < 0.2f && count > 2)
+ {
+
+ float dot = this->mVehicleUp.DotProduct(GetWorldPhysicsManager()->mWorldUp);
+
+ float linear = mPastLinear.Smooth(GetSimState()->GetLinearVelocity().Magnitude());
+ float angular = mPastAngular.Smooth(GetSimState()->GetAngularVelocity().Magnitude());
+
+ float ebrakefactor = 1.0f;
+ if(this->mEBrake == 1.0f)
+ {
+ //ebrakefactor = 2.0f;
+ }
+
+ if(mUsingInCarPhysics)
+ {
+ if(1)//dot < 0.995f)
+ {
+ // steep, so more aggresive test
+ //if( linear < 2.0f * REST_LINEAR_TOL * ebrakefactor && angular < 2.0f * REST_ANGULAR_TOL * ebrakefactor)
+ if( linear < 3.0f * REST_LINEAR_TOL * ebrakefactor && angular < 3.0f * REST_ANGULAR_TOL * ebrakefactor)
+ {
+ //GetSimState()->SetControl(sim::simAICtrl);
+ //GetSimState()->ResetVelocities();
+
+
+ if(this->mVehicleType == VT_TRAFFIC && this != playerVehicle && !mCreatedByParkedCarManager)
+ {
+ GetSimState()->SetControl(sim::simAICtrl);
+ GetSimState()->ResetVelocities();
+ }
+ else
+ {
+ this->ZeroOutXZVelocity();
+ }
+
+
+ mAtRestAsFarAsTriggersAreConcerned = true;
+ return true;
+ }
+
+ }
+ else if( linear < REST_LINEAR_TOL * ebrakefactor && angular < REST_ANGULAR_TOL * ebrakefactor)
+ {
+ // put it at rest
+ //GetSimState()->SetControl(sim::simAICtrl);
+ //GetSimState()->ResetVelocities();
+ //this->ZeroOutXZVelocity();
+
+
+ if(this->mVehicleType == VT_TRAFFIC && this != playerVehicle && !mCreatedByParkedCarManager)
+ {
+ GetSimState()->SetControl(sim::simAICtrl);
+ GetSimState()->ResetVelocities();
+ }
+ else
+ {
+ this->ZeroOutXZVelocity();
+ }
+
+
+
+
+ mAtRestAsFarAsTriggersAreConcerned = true;
+ return true;
+ }
+ }
+ else
+ {
+ // more aggressive test
+ if( linear < 10.0f * REST_LINEAR_TOL * ebrakefactor && angular < 10.0f * REST_ANGULAR_TOL * ebrakefactor)
+ {
+ //GetSimState()->SetControl(sim::simAICtrl);
+ //GetSimState()->ResetVelocities();
+
+ //this->ZeroOutXZVelocity();
+
+
+ if(this->mVehicleType == VT_TRAFFIC && this != playerVehicle && !mCreatedByParkedCarManager)
+ {
+ GetSimState()->SetControl(sim::simAICtrl);
+ GetSimState()->ResetVelocities();
+ }
+ else
+ {
+ this->ZeroOutXZVelocity();
+ }
+
+
+
+
+ mAtRestAsFarAsTriggersAreConcerned = true;
+ return true;
+ }
+
+ }
+ }
+ else
+ {
+ if(0)//this->mVehicleType == VT_TRAFFIC)
+ {
+ GetSimState()->SetControl(sim::simSimulationCtrl);
+ }
+ }
+ mAtRestAsFarAsTriggersAreConcerned = false;
+ return false;
+}
+
+
+
+//=============================================================================
+// Vehicle::ZeroOutXZVelocity
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::ZeroOutXZVelocity()
+{
+ rmt::Vector& linear = mSimStateArticulated->GetLinearVelocity();
+
+ rmt::Vector& angular = mSimStateArticulated->GetAngularVelocity();
+
+ rmt::Vector newlinear = this->mVehicleUp;
+ float linearProj = linear.DotProduct(newlinear);
+
+ newlinear.Scale(linearProj);
+
+ linear = newlinear;
+
+
+ angular.Set(0.0f, 0.0f, 0.0f);
+}
+
+
+//=============================================================================
+// Vehicle::SetGeometryVehicleWheelSmokeLevel
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetGeometryVehicleWheelSmokeLevel()
+{
+ // just based on burnout level for now
+
+ if(mBurnoutLevel > 0.0f)
+ {
+ if(this->mSpeedBurstTimer >= 2.5f)
+ {
+ // TODO
+ // replace this with wheel flame
+ // MKR - replaced. Greg, does this work ok?
+ // -1 indicates apply to all wheel
+ mGeometryVehicle->SetWheelSmoke( -1, ParticleEnum::eFireSpray, mBurnoutLevel);
+ }
+ else
+ {
+ // TODO - if anyone notices or complains, switch this to
+ // set on a wheel by wheel basis
+
+ // for now, just base on James's overall value
+
+ //TT_Road, // Default road terrain. Also used for sidewalk. This is default. If not set, it's this.
+ //TT_Grass, // Grass type terrain most everything else which isn't road or sidewalk.
+ //TT_Sand, // Sand type terrain.
+ //TT_Gravel, // Loose gravel type terrain.
+ //TT_Water, // Water on surface type terrain.
+ //TT_Wood, // Boardwalks, docks type terrain.
+ //TT_Metal, // Powerplant and other structures.
+ //TT_Dirt, // Dirt type terrain.
+ //TT_NumTerrainTypes
+
+ for ( int i = 0 ; i < 2 ; i++)
+ {
+
+ switch( mPhysicsLocomotion->mTerrainIntersectCache[i].mTerrainType )
+ {
+ case TT_Grass:
+ mGeometryVehicle->SetWheelSmoke( i, ParticleEnum::eGrassSpray, mBurnoutLevel);
+ break;
+
+ case TT_Gravel:
+ case TT_Dirt:
+ case TT_Sand:
+ mGeometryVehicle->SetWheelSmoke( i, ParticleEnum::eDirtSpray, mBurnoutLevel);
+ break;
+
+ case TT_Water:
+ mGeometryVehicle->SetWheelSmoke( i, ParticleEnum::eWaterSpray, mBurnoutLevel);
+ break;
+
+ default:
+ mGeometryVehicle->SetWheelSmoke( i, ParticleEnum::eSmokeSpray, mBurnoutLevel);
+ break;
+
+ }
+ }
+ }
+ }
+ else
+ {
+ mGeometryVehicle->SetWheelSmoke( -1, ParticleEnum::eNull, 0.0f);
+ }
+
+}
+
+
+
+//=============================================================================
+// Vehicle::DSGUpdateAndMove
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::DSGUpdateAndMove()
+{
+ /*
+ rmt::Box3D mBBox;
+ rmt::Sphere mSphere;
+ rmt::Vector mPosn;
+ sim::SimState* mpSimStateObj; // TODO - ever care about this?
+ */
+
+ rmt::Box3D oldBox = mBBox;
+
+ mPosn = *((rmt::Vector*)mTransform.m[3]);
+
+ mBBox.low = mPosn;
+ mBBox.high = mPosn;
+
+ // ?
+ // I think the same code should work for vehicle...
+ mBBox.high += mSimStateArticulated->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+ mBBox.low -= mSimStateArticulated->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+
+ mSphere.centre.Sub(mBBox.high,mBBox.low);
+ mSphere.centre *= 0.5f;
+ mSphere.centre.Add(mBBox.low);
+ mSphere.radius = mSimStateArticulated->GetCollisionObject()->GetCollisionVolume()->mSphereRadius;
+
+
+ // now move!
+ GetRenderManager()->pWorldScene()->Move(oldBox, (IEntityDSG*)this);
+
+
+}
+
+
+
+//=============================================================================
+// Vehicle::UpdateGearAndRPM
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::UpdateGearAndRPM()
+{
+ // make sure this method can work on both
+ // physics and traffic updated vehicles.
+
+
+ float speedAlongFacing;
+
+ speedAlongFacing = mVehicleFacing.DotProduct(mVelocityCM);
+
+ // just use wheel 0
+
+ float linearDistancePerRev = rmt::PI * mWheels[0]->mRadius * 2.0f;
+
+ float revPerTime = speedAlongFacing / linearDistancePerRev;
+ revPerTime = rmt::Fabs(revPerTime);
+
+ // now we go backwards from this to figure out what the rpm is,
+ // and if necessary, shift gears
+
+ // !!! TODO - deal with reverse!
+
+ // first see if we're in neutral and just sitting there
+ //static float smallRevPerTime = 0.1f;
+ float smallRevPerTime = 0.1f;
+ if(mGas == 0.0f && revPerTime < smallRevPerTime)
+ {
+ mGear = 0;
+ mRPM = mBaseRPM;
+ return;
+ }
+
+ // otherwise let's move!
+ if(mGear == 0)
+ {
+ mGear = 1;
+ }
+
+ if((mVehicleState == VS_NORMAL || mGas == 0) && mBurnoutLevel == 0.0f && !mDoSpeedBurst)
+ {
+ //--------------------------
+ // determine tentative value
+ //--------------------------
+ float targetRPM = mGearRatios[mGear - 1] * mFinalDriveRatio * revPerTime * 60.0f; // 60.0f to get to minutes instead of seconds
+
+
+ //------------------------
+ // only change by set rate
+ //------------------------
+ if(targetRPM > mRPM)
+ {
+ // shouldn't be able to make arbitrary changes in rpm
+ mRPM += mRPMUpRate;
+ if(mRPM > targetRPM)
+ {
+ mRPM = targetRPM;
+ }
+ }
+ if(targetRPM < mRPM)
+ {
+ // shouldn't be able to make arbitrary changes in rpm
+ mRPM -= mRPMDownRate;
+ if(mRPM < targetRPM)
+ {
+ mRPM = targetRPM;
+ }
+ }
+
+ //--------------------------
+ // change gears if necessary
+ //--------------------------
+ if(mRPM > mShiftPointHigh)
+ {
+ // change gears and recalculate
+ mGear++;
+ if(mGear > mNumGears)
+ {
+ mGear = mNumGears;
+ }
+
+ }
+ else if(mRPM < mShiftPointLow)
+ {
+ mGear--;
+ if(mGear < 1)
+ {
+ mGear = 1; // TODO - again - deal with reverse!
+ }
+
+ }
+
+ //-----------------------------------------
+ // recalc target due to gear change - maybe
+ //-----------------------------------------
+ targetRPM = mGearRatios[mGear - 1] * mFinalDriveRatio * revPerTime * 60.0f; // 60.0f to get to minutes instead of seconds
+
+
+ //------------------------
+ // only change by set rate
+ //------------------------
+ if(targetRPM > mRPM)
+ {
+ // shouldn't be able to make arbitrary changes in rpm
+ mRPM += mRPMUpRate;
+ if(mRPM > targetRPM)
+ {
+ mRPM = targetRPM;
+ }
+ }
+ if(targetRPM < mRPM)
+ {
+ // shouldn't be able to make arbitrary changes in rpm
+ mRPM -= mRPMDownRate;
+ if(mRPM < targetRPM)
+ {
+ mRPM = targetRPM;
+ }
+ }
+
+ //------------------------
+ // lock no lower than base
+ //------------------------
+
+ if(mRPM < mBaseRPM)
+ {
+ mRPM = mBaseRPM;
+ }
+ }
+ else // VS_SLIP
+ {
+ if(mGas > 0.0f)
+ {
+ const float hack = 0.1f;
+ //mRPM *= 1.0f + (hack * mGas);
+
+ const float hack2 = 3.1f;
+ if(mBurnoutLevel > 0.0f)
+ {
+ mRPM += mGas * mRPMUpRate * hack2;
+ }
+ else
+ {
+ mRPM += mGas * mRPMUpRate * hack;
+ }
+
+ // gear same
+ }
+ if(mRPM > mShiftPointHigh)
+ {
+ mRPM = mShiftPointHigh;
+ }
+ }
+
+
+}
+
+
+//=============================================================================
+// Vehicle::UpdateWheelRenderingInfo
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::UpdateWheelRenderingInfo(float dt)
+{
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ // setup this method such that it can be called to get the right info
+ // cahced into the Wheel after either a physics or traffic update
+ //
+ // maybe this can be the single calculation-type method in
+ // Wheel.
+ mWheels[i]->CalculateRotAngle(dt);
+ }
+}
+
+
+//=============================================================================
+// Vehicle::GetTransform
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+const rmt::Matrix& Vehicle::GetTransform()
+{
+ return mTransform;
+}
+
+
+
+//========================================================================
+// vehicle::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+rmt::Vector* Vehicle::pPosition()
+{
+ return (rmt::Vector*)mTransform.m[3];
+}
+//========================================================================
+// vehicle::
+//========================================================================
+//
+// Description:
+//
+// Parameters: None.
+//
+// Return: None.
+//
+// Constraints: None.
+//
+//========================================================================
+const rmt::Vector& Vehicle::rPosition()
+{
+// rAssert(false);
+// return NULL;
+ return *((rmt::Vector*)mTransform.m[3]);
+}
+
+//------------------------------------------------------------------------
+//=============================================================================
+// Vehicle::Display
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::Display()
+{
+ ActivateTriggers(!(IsUnstable() || IsAirborn()) || (GetLocomotionType() == VL_TRAFFIC) || mAtRestAsFarAsTriggersAreConcerned);
+
+ if(IS_DRAW_LONG) return;
+
+ // not sure if these belong here?
+ //p3d::stack->Push();
+ //p3d::stack->Multiply(mTransform);
+
+ //return;
+
+
+ //DebugDisplay();
+ //return;
+
+ if( !mOkToDrawSelf || !mDrawVehicle )
+ {
+ return;
+ }
+
+ DSG_BEGIN_PROFILE(" Vehicle::Display")
+ BillboardQuadManager::Enable();
+ //DisplaySimpleShadow();
+ // TODO - how to set this up cleaner?
+
+ // little debug test
+ //
+ //if(!(mWheels[2]->mWheelInCollision) || !(mWheels[3]->mWheelInCollision))
+ if(0)//mWeebleOn)
+ //if(mDrawWireFrame)
+ //if(this->mAirBorn)
+ {
+ p3d::pddi->SetFillMode(PDDI_FILL_WIRE);
+ }
+
+
+ // temp!!
+
+ //p3d::pddi->SetFillMode(PDDI_FILL_WIRE);
+
+
+ mPoseEngine->End(); // copy over what we're gonna render
+ mGeometryVehicle->Display();
+
+
+
+ //p3d::stack->Pop();
+
+ //if(!(mWheels[2]->mWheelInCollision) || !(mWheels[3]->mWheelInCollision))
+ if(0)//mWeebleOn)
+ //if(mDrawWireFrame)
+ //if(this->mAirBorn)
+ {
+ p3d::pddi->SetFillMode(PDDI_FILL_SOLID);
+ }
+ // ugly, but just for debugging shit
+ mDrawWireFrame = false;
+ this->mLosingTractionDueToAccel = false;
+
+
+ BillboardQuadManager::Disable();
+ DSG_END_PROFILE(" Vehicle::Display")
+}
+
+
+//=============================================================================
+// Vehicle::DebugDisplay
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::DebugDisplay()
+{
+ mSimStateArticulated->DebugDisplay(2); // this one draws some sort of virtual centre of mass or something
+ sim::DrawCollisionObject(mSimStateArticulated->GetCollisionObject());
+}
+
+
+//=============================================================================
+// Vehicle::CarDisplay
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (bool doit)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::CarDisplay(bool doit)
+{
+ mOkToDrawSelf = doit;
+}
+
+
+//=============================================================================
+// Vehicle::GetSpeedKmh
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float Vehicle::GetSpeedKmh()
+{
+ //return mSpeed * 3.6f;
+ return mSpeedKmh;
+}
+
+
+float Vehicle::GetAccelMss()
+{
+ return mAccelMss;
+}
+
+//=============================================================================
+// Vehicle::GetRPM
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float Vehicle::GetRPM()
+{
+ return mRPM;
+}
+
+
+//=============================================================================
+// Vehicle::GetSkidLevel
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float Vehicle::GetSkidLevel()
+{
+ return mSkidLevel;
+}
+
+
+//=============================================================================
+// Vehicle::GetGear
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+int Vehicle::GetGear()
+{
+ // TODO - how to do the gearshift interface?
+
+ return mGear;
+}
+
+
+//----------------------
+// camera inteface stuff
+//----------------------
+
+//=============================================================================
+// Vehicle::GetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* position )
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::GetPosition( rmt::Vector* position )
+{
+ *position = *((rmt::Vector*)mTransform.m[3]);
+}
+
+
+//=============================================================================
+// Vehicle::GetHeading
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* heading )
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::GetHeading( rmt::Vector* heading )
+{
+ *heading = mVehicleFacing;
+}
+
+
+//=============================================================================
+// Vehicle::GetVUP
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* vup )
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::GetVUP( rmt::Vector* vup )
+{
+ *vup = mVehicleUp;
+}
+
+
+//=============================================================================
+// Vehicle::GetVelocity
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector* velocity )
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::GetVelocity( rmt::Vector* velocity )
+{
+ // TODO - make sure this holds the right thing when
+ // we are being traffic locomoted
+ *velocity = mVelocityCM;
+}
+
+//=============================================================================
+// Vehicle::GetID
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: unsigned
+//
+//=============================================================================
+unsigned int Vehicle::GetID()
+{
+ return mVehicleID;
+}
+
+//=============================================================================
+// Vehicle::IsCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool Vehicle::IsCar() const
+{
+ return true;
+}
+
+
+//=============================================================================
+// Vehicle::IsAirborn
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool Vehicle::IsAirborn()
+{
+ //return mSteeringWheelsOutOfContact;
+ return mAirBorn;
+}
+
+
+//=============================================================================
+// Vehicle::IsUnstable
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool Vehicle::IsUnstable()
+{
+ // see how much it's tipped...
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = mVehicleUp.DotProduct(up);
+
+ //float cos10 = 0.9848f;
+ float cos30 = 0.866f;
+ if(tip < cos30)
+ {
+ return true;
+ }
+
+ if(mAirBorn)
+ {
+ return true;
+ }
+
+ return false;
+
+ // quick test
+ //return mWeebleOn;
+}
+
+
+//=============================================================================
+// Vehicle::IsSafeToUpShift
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool Vehicle::IsSafeToUpShift()
+{
+ // let's say if at least 1 wheel in contact with ground (ie. !airborn)
+ // and the tip angle is less than 15 degrees, then ok.
+
+ if(!mAirBorn)
+ {
+ rmt::Vector up = GetWorldPhysicsManager()->mWorldUp;
+ float tip = mVehicleUp.DotProduct(up);
+
+ //float cos15 = 0.9659f;
+ float cos10 = 0.9848f;
+
+ if( (tip > cos10) || (mVehicleFacing.DotProduct(up) <= 0.0f) )
+ {
+ return true;
+ }
+
+ }
+
+ return false;
+}
+
+
+//=============================================================================
+// Vehicle::IsQuickTurn
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool Vehicle::IsQuickTurn()
+{
+ // TODO
+ return false;
+}
+
+//=============================================================================
+// Vehicle::IsInReverse
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool Vehicle::IsInReverse()
+{
+ //return false;
+
+ // first heuristic
+ //
+ // if the brake button is down and we're travelling backwards
+ float proj = mVelocityCM.DotProduct(mVehicleFacing);
+
+ // don't want just any slight backwards motion to trigger
+ const float cos120 = -0.5f;
+
+ if(mBrake > 0.1f && proj < cos120)
+ {
+ return true;
+ }
+
+ return false;
+}
+
+//=============================================================================
+// Vehicle::GetTerrainIntersect
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector& pos, rmt::Vector& normal )
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::GetTerrainIntersect( rmt::Vector& pos, rmt::Vector& normal ) const
+{
+ //THIS IS A LIE!
+ pos = mTransform.Row(3);
+ normal.Set( 0.0f, 1.0f, 0.0f );
+}
+
+//=============================================================================
+// Vehicle::IsMovingBackward
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: bool
+//
+//=============================================================================
+bool Vehicle::IsMovingBackward()
+{
+ // this one does not require the brake input to be held down
+
+ if(this->mPercentOfTopSpeed > 0.05f)
+ {
+ float proj = mVelocityCM.DotProduct(mVehicleFacing);
+
+ const float cos120 = -0.5f;
+
+ if(proj < cos120)
+ {
+ return true;
+ }
+ }
+
+ return false;
+
+
+}
+
+//=============================================================================
+// Vehicle::GetName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: const
+//
+//=============================================================================
+const char* const Vehicle::GetName()
+{
+ return (const char*)mName;
+}
+
+const rmt::Vector& Vehicle::GetPassengerLocation( void ) const
+{
+ return mPassengerLocation;
+}
+
+const rmt::Vector& Vehicle::GetDriverLocation( void ) const
+{
+ return mDriverLocation;
+}
+//=============================================================================
+// Vehicle::PreReactToCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+//
+// Return: bool
+//
+//=============================================================================
+sim::Solving_Answer Vehicle::PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+{
+ // this is called from the generic worldcollisionsolveragentmanager
+
+ // don't abort
+ return sim::Solving_Continue;
+}
+
+
+
+//=============================================================================
+// Vehicle::SetHitJoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int hj)
+//
+// Return: void
+//
+//=============================================================================
+/*
+void Vehicle::SetHitJoint(int hj)
+{
+ // want to try and use the most "interesting" value
+
+ if(IsAFlappingJoint(hj))
+ {
+ // set for sure
+ mHitJoint = hj;
+ return;
+ }
+
+ if(IsAFlappingJoint(mHitJoint))
+ {
+ // we already have a flapping joint set and the incoming one is not
+ return;
+ }
+
+ // ok we'll use it.
+ mHitJoint = hj;
+
+
+}
+*/
+
+
+
+//=============================================================================
+// Vehicle::ResetDamageState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::ResetDamageState()
+{
+ mVehicleDestroyed = false;
+ mDontShowBrakeLights = false;
+ mGeometryVehicle->SetLightsOffDueToDamage(false);
+ mDamageOutResetTimer = 0.0f;
+ mHitPoints = mDesignerParams.mHitPoints;
+
+ mDesiredDoorPosition[0] = mDesiredDoorPosition[1] = 0.0f;
+ mDesiredDoorAction[0] = mDesiredDoorAction[1] = DOORACTION_NONE;
+
+ ActivateTriggers(true);
+
+ if (mbPlayerCar ==true)
+ {
+ GetCharacterSheetManager()->UpdateCarHealth( mCharacterSheetCarIndex, 1.0f );
+ }
+
+ switch(mDamageType)
+ {
+ //case 1:
+ case VDT_USER:
+ {
+ // reset flapping joints
+
+ if(mHoodJoint != -1)
+ {
+ int index = mJointIndexToInertialJointDriverMapping[mHoodJoint];
+ if(index != -1)
+ {
+ mPhObj->GetJoint(mHoodJoint)->SetInvStiffness(0.0f);
+ mPhObj->GetJoint(mHoodJoint)->ResetDeformation();
+ mInertialJointDrivers[index]->SetIsEnabled(false);
+ }
+ }
+
+
+ if(mTrunkJoint != -1)
+ {
+ int index = mJointIndexToInertialJointDriverMapping[mTrunkJoint];
+ if(index != -1)
+ {
+ mPhObj->GetJoint(mTrunkJoint)->SetInvStiffness(0.0f);
+ mPhObj->GetJoint(mTrunkJoint)->ResetDeformation();
+ mInertialJointDrivers[index]->SetIsEnabled(false);
+ }
+ }
+
+ if(mDoorDJoint != -1)
+ {
+ int index = mJointIndexToInertialJointDriverMapping[mDoorDJoint];
+ if(index != -1)
+ {
+ mPhObj->GetJoint(mDoorDJoint)->SetInvStiffness(0.0f);
+ mPhObj->GetJoint(mDoorDJoint)->ResetDeformation();
+ mInertialJointDrivers[index]->SetIsEnabled(false);
+ }
+ }
+
+
+ if(mDoorPJoint != -1)
+ {
+ int index = mJointIndexToInertialJointDriverMapping[mDoorPJoint];
+ if(index != -1)
+ {
+ mPhObj->GetJoint(mDoorPJoint)->SetInvStiffness(0.0f);
+ mPhObj->GetJoint(mDoorPJoint)->ResetDeformation();
+ mInertialJointDrivers[index]->SetIsEnabled(false);
+ }
+ }
+
+ mGeometryVehicle->DamageTextureHood(false);
+ mGeometryVehicle->DamageTextureTrunk(false);
+ mGeometryVehicle->DamageTextureDoorD(false);
+ mGeometryVehicle->DamageTextureDoorP(false);
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eNull);
+
+ mGeometryVehicle->HideFlappingPiece(mHoodJoint, false);
+
+ }
+ break;
+
+ //case 2:
+ case VDT_AI:
+ {
+ mGeometryVehicle->DamageTextureHood(false);
+ mGeometryVehicle->DamageTextureTrunk(false);
+ mGeometryVehicle->DamageTextureDoorD(false);
+ mGeometryVehicle->DamageTextureDoorP(false);
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eNull);
+ }
+ break;
+
+ //case 3:
+ case VDT_TRAFFIC:
+ {
+ //mGeometryVehicle->DamageTextureChassis(false);
+ //mGeometryVehicle->SetEngineSmoke(ParticleEnum::eNull);
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eNull);
+ }
+ break;
+
+ default:
+ rAssertMsg(0, "what are you doing here?");
+
+ }
+
+}
+
+//=============================================================================
+// Vehicle::DebugInflictDamageHood
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::DebugInflictDamageHood()
+{
+ //int stophere = 1;
+
+
+
+ //float perc = GetVehicleLifePercentage();
+ float perc = TriggerDamage(0.15f);
+
+ switch(mDamageType)
+ {
+ //case 1: // user level - flaping joints etc...
+ case VDT_USER:
+ {
+ VisualDamageType1(1.0f - perc, dl_hood);
+ }
+ break;
+
+ //case 2: // ai - localized damage textures but no flapping
+ case VDT_AI:
+ {
+ VisualDamageType2(1.0f - perc, dl_hood);
+ }
+ break;
+
+ //case 3: // traffic - one big poof and texture
+ case VDT_TRAFFIC:
+ {
+ VisualDamageType3(1.0f - perc);
+ }
+ break;
+
+ default:
+ rAssertMsg(0, "what are you doing here?");
+ }
+
+
+}
+
+//=============================================================================
+// Vehicle::DebugInflictDamageBack
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::DebugInflictDamageBack()
+{
+
+ //TriggerDamage(0.15f);
+ //float perc = GetVehicleLifePercentage();
+
+
+ //float perc = GetVehicleLifePercentage();
+ float perc = TriggerDamage(0.15f);
+
+
+ switch(mDamageType)
+ {
+ //case 1: // user level - flaping joints etc...
+ case VDT_USER:
+ {
+ VisualDamageType1(1.0f - perc, dl_trunk);
+ }
+ break;
+
+ //case 2: // ai - localized damage textures but no flapping
+ case VDT_AI:
+ {
+ VisualDamageType2(1.0f - perc, dl_trunk);
+ }
+ break;
+
+ //case 3: // traffic - one big poof and texture
+ case VDT_TRAFFIC:
+ {
+ VisualDamageType3(1.0f - perc);
+ }
+ break;
+
+ default:
+ rAssertMsg(0, "what are you doing here?");
+ }
+}
+
+//=============================================================================
+// Vehicle::DebugInflictDamageDriverSide
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::DebugInflictDamageDriverSide()
+{
+
+ //TriggerDamage(0.15f);
+ //float perc = GetVehicleLifePercentage();
+
+
+ //float perc = GetVehicleLifePercentage();
+ float perc = TriggerDamage(0.15f);
+
+
+ switch(mDamageType)
+ {
+ //Case 1: // user level - flaping joints etc...
+ case VDT_USER:
+ {
+ VisualDamageType1(1.0f - perc, dl_driverside);
+ }
+ break;
+
+ //case 2: // ai - localized damage textures but no flapping
+ case VDT_AI:
+ {
+ VisualDamageType2(1.0f - perc, dl_driverside);
+ }
+ break;
+
+ //case 3: // traffic - one big poof and texture
+ case VDT_TRAFFIC:
+ {
+ VisualDamageType3(1.0f - perc);
+ }
+ break;
+
+ default:
+ rAssertMsg(0, "what are you doing here?");
+ }
+
+}
+
+//=============================================================================
+// Vehicle::DebugInflictDamagePassengerSide
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::DebugInflictDamagePassengerSide()
+{
+ //TriggerDamage(0.15f);
+
+ //float perc = GetVehicleLifePercentage();
+
+
+ //float perc = GetVehicleLifePercentage();
+ float perc = TriggerDamage(0.15f);
+
+
+ switch(mDamageType)
+ {
+ //case 1: // user level - flaping joints etc...
+ case VDT_USER:
+ {
+ VisualDamageType1(1.0f - perc, dl_passengerside);
+ }
+ break;
+
+ //case 2: // ai - localized damage textures but no flapping
+ case VDT_AI:
+ {
+ VisualDamageType2(1.0f - perc, dl_passengerside);
+ }
+ break;
+
+ //case 3: // traffic - one big poof and texture
+ case VDT_TRAFFIC:
+ {
+ VisualDamageType3(1.0f - perc);
+ }
+ break;
+
+ default:
+ rAssertMsg(0, "what are you doing here?");
+ }
+
+}
+
+//=============================================================================
+// Vehicle::PostReactToCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( sim::SimState* pCollidedObj, sim::Collision& inCollision )
+//
+// Return: sim
+//
+//=============================================================================
+sim::Solving_Answer Vehicle::PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision)
+{
+
+ // this is called from the generic worldcollisionsolveragentmanager
+
+ // don't abort
+
+ sim::CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ sim::CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ sim::SimState* simStateA = collObjA->GetSimState();
+ sim::SimState* simStateB = collObjB->GetSimState();
+
+
+ float impulseMagnitude = impulse.Magnitude();
+
+ const float maxIntensity = 100000.0f; // empircally determined
+
+ // Check to see if the impact was strong enough to detach any attached collectibles.
+ if ( impulseMagnitude > mForceToDetachCollectible )
+ {
+ rmt::Vector velocity = GetSimState()->GetLinearVelocity();
+ DetachCollectible( velocity );
+ }
+
+ float normalizedMagnitude = impulseMagnitude / maxIntensity;
+
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle && simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ // stricter test
+ if(normalizedMagnitude > 0.8f)
+ {
+ return sim::Solving_Aborted;
+ }
+ else if( ( ((Vehicle*)(simStateA->mAIRefPointer))->mVehicleID == VehicleEnum::BART_V && ((Vehicle*)(simStateB->mAIRefPointer))->mVehicleID == VehicleEnum::CKLIMO) ||
+ ( ((Vehicle*)(simStateA->mAIRefPointer))->mVehicleID == VehicleEnum::CKLIMO && ((Vehicle*)(simStateB->mAIRefPointer))->mVehicleID == VehicleEnum::BART_V) )
+
+ {
+ if(normalizedMagnitude > 0.5f)
+ {
+ return sim::Solving_Aborted;
+ }
+ }
+ }
+
+ if(normalizedMagnitude > 1.0f)
+ {
+ rDebugPrintf("Yikes!!! Enormous impulse!\n");
+ //impulse.x /= normalizedMagnitude * 2;
+ //impulse.y /= normalizedMagnitude * 2;
+ //impulse.z /= normalizedMagnitude * 2;
+ if(normalizedMagnitude > 2.0f)
+ {
+ return sim::Solving_Aborted;
+ }
+
+
+ normalizedMagnitude = 1.0f;
+ }
+ if(normalizedMagnitude < 0.0f)
+ {
+ rAssert(0);
+ }
+
+
+ if(mVehicleID == VehicleEnum::HUSKA)
+ {
+ normalizedMagnitude = 0.2f;
+ }
+
+ if(this->mVehicleID == VehicleEnum::DUNE_V)
+ {
+ if( simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizFence ||
+ simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizFence)
+
+ {
+ // r/c car hitting a fence
+ //? scale up impulse?
+
+ impulse.Scale(2.0f);
+ }
+ }
+
+
+ //Rumble the controller
+ RumbleCollision rc;
+ rc.normalizedForce = normalizedMagnitude;
+ rc.vehicle = this;
+ rc.point = inCollision.GetPositionA(); //This could be either.
+
+ GetEventManager()->TriggerEvent( EVENT_RUMBLE_COLLISION, &rc );
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle && simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ TestWhoHitWhom( simStateA, simStateB, normalizedMagnitude, inCollision );
+ if(mVehicleType != VT_USER)
+ {
+ DusitsStunTest(normalizedMagnitude);
+ }
+ // Vehicle - vehicle collision
+ // we want to emit paint flicks of the appropriate colour
+ if ( mGeometryVehicle->HasVehicleColour() )
+ {
+ tColour vehicleColour = mGeometryVehicle->GetVehicleColour();
+ float strength = impulseMagnitude * ( 1.0f / 5000.0f );
+ float velocityScale = strength;
+ const rmt::Vector& position = inCollision.GetPositionA();
+ rmt::Vector velocity = mVelocityCM;
+ if ( velocity.MagnitudeSqr() > 0.01f )
+ {
+ velocity.y = 0.0f;
+ velocity.Normalize();
+ velocity.Scale( velocityScale );
+ }
+ else
+ {
+ velocity = rmt::Vector(0,0,0);
+ }
+
+ GetSparkleManager()->AddPaintChips( position, velocity, vehicleColour, strength );
+ }
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ if(playerAvatar->GetVehicle() == this)
+ {
+ CarOnCarCollisionEventData data;
+
+ //data.vehicle shall be the other vehicle.
+ if ( simStateA->mAIRefPointer == this )
+ {
+ data.vehicle = ((Vehicle*)(simStateB->mAIRefPointer));
+ }
+ else
+ {
+ data.vehicle = ((Vehicle*)(simStateA->mAIRefPointer));
+ }
+ data.force = normalizedMagnitude;
+ data.collision = &inCollision;
+ GetEventManager()->TriggerEvent( EVENT_VEHICLE_VEHICLE_COLLISION, &data );
+ }
+ }
+
+ if(mVehicleType == VT_USER)
+ {
+ SparksTest(impulseMagnitude, inCollision); // should this also be wrapped in mUserDrivingCar??
+ }
+
+ //which one are we?
+ bool thisIsA = true;
+
+ if(simStateA->mAIRefPointer == this)
+ {
+ // ok
+ }
+ else
+ {
+ thisIsA = false;
+ }
+
+ // try movign this down so that husks hitting cars and statics will still make sound
+ //if(mVehicleID == VehicleEnum::HUSKA)
+ //{
+ // return sim::Solving_Continue;
+ //}
+
+ // new debug test just for traffic collision
+ bool oneTapTrafficDeath = false;
+
+
+ if( GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_ONE_TAP_TRAFFIC_DEATH) )
+ {
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle && simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ if(thisIsA)
+ {
+ if( (((Vehicle*)(simStateA->mAIRefPointer))->mVehicleType == VT_TRAFFIC || ((Vehicle*)(simStateA->mAIRefPointer))->mVehicleType == VT_AI) &&
+ ((Vehicle*)(simStateB->mAIRefPointer))->mVehicleType == VT_USER)
+ {
+ int stophere = 1;
+ oneTapTrafficDeath = true;
+
+ }
+ }
+ else
+ {
+ if( (((Vehicle*)(simStateB->mAIRefPointer))->mVehicleType == VT_TRAFFIC || ((Vehicle*)(simStateB->mAIRefPointer))->mVehicleType == VT_AI) &&
+ ((Vehicle*)(simStateA->mAIRefPointer))->mVehicleType == VT_USER)
+
+ {
+ int stophere = 1;
+ oneTapTrafficDeath = true;
+ }
+ }
+ }
+ }
+
+ // This is the "minimum damage threshold" for applying ANY damage
+ // - think of the number as a percentage, i.e., any hit less than .1 (10 &) intensity
+ // won't cause any damage at all
+ const float minDamageThreshold = 0.06f;
+
+
+
+ if(!oneTapTrafficDeath)
+ {
+ if(normalizedMagnitude < minDamageThreshold)
+ {
+ //#ifdef RAD_DEBUG
+ //char buffy[128];
+ //sprintf(buffy, "normalizedMagnitude %.4f\n", normalizedMagnitude);
+ //rDebugPrintf(buffy);
+ //#endif
+
+ return sim::Solving_Continue;
+ }
+ }
+
+
+
+
+
+ //Greg says to do this. Won't work in 2-player.
+ if (mUserDrivingCar)
+ {
+ CameraShakeTest(impulseMagnitude, inCollision); // pass struct, by reference, for convenience
+ // pass in impulseMagnitude because we don't want to compute it twice.
+ }
+
+
+
+
+ SoundCollisionData soundData( normalizedMagnitude,
+ static_cast<CollisionEntityDSG*>(simStateA->mAIRefPointer),
+ static_cast<CollisionEntityDSG*>(simStateB->mAIRefPointer) );
+ GetEventManager()->TriggerEvent( EVENT_COLLISION, &soundData );
+
+
+ if(mVehicleID == VehicleEnum::HUSKA)
+ {
+ return sim::Solving_Continue;
+ }
+
+
+
+ // before further testing, see if this is a car that hit it's own ground plane
+ // if so, abort
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+
+ if(GetGameplayManager()->GetGameType() != GameplayManager::GT_SUPERSPRINT && playerAvatar->GetVehicle() == this)
+ {
+ const float percentageOfSpeedToMaintain = 0.85f; // plum, change this number
+
+ if(thisIsA)
+ {
+ if(simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizVehicleGroundPlane && this->mGas > 0.0f)
+ {
+ if(mBottomOutSpeedMaintenance == 0.0f)
+ {
+ // not set
+ mBottomOutSpeedMaintenance = this->mSpeed * percentageOfSpeedToMaintain;
+ }
+ else
+ {
+ // it is set
+ //
+ // so.... maintain it!
+ if(mBottomOutSpeedMaintenance > (this->mDesignerParams.mDpTopSpeedKmh / 3.6f) * 0.6f)
+ {
+ rmt::Vector& linearVel = mSimStateArticulated->GetLinearVelocity();
+
+ float proj = linearVel.DotProduct(this->mVehicleFacing);
+ if(proj < mBottomOutSpeedMaintenance)
+ {
+ float diff = mBottomOutSpeedMaintenance - proj;
+ rmt::Vector boost = mVehicleFacing;
+ boost.Scale(diff);
+
+ linearVel.Add(boost);
+
+ }
+
+
+ }
+ }
+
+ return sim::Solving_Continue;
+ }
+ else
+ {
+ // didn't hit our ground plane this frame so make sure the maintenance speed is reset
+ mBottomOutSpeedMaintenance = 0.0f;
+ }
+ }
+ else
+ {
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizVehicleGroundPlane && this->mGas > 0.0f)
+ {
+
+ if(mBottomOutSpeedMaintenance == 0.0f)
+ {
+ // not set
+ mBottomOutSpeedMaintenance = this->mSpeed * percentageOfSpeedToMaintain;
+ }
+ else
+ {
+ // it is set
+ //
+ // so.... maintain it!
+
+
+ if(mBottomOutSpeedMaintenance > (this->mDesignerParams.mDpTopSpeedKmh / 3.6f) * 0.6f)
+ {
+ rmt::Vector& linearVel = mSimStateArticulated->GetLinearVelocity();
+
+ float proj = linearVel.DotProduct(this->mVehicleFacing);
+ if(proj < mBottomOutSpeedMaintenance)
+ {
+ float diff = mBottomOutSpeedMaintenance - proj;
+ rmt::Vector boost = mVehicleFacing;
+ boost.Scale(diff);
+
+ linearVel.Add(boost);
+
+ }
+
+
+ }
+
+
+ }
+
+
+ return sim::Solving_Continue;
+ }
+ else
+ {
+ // didn't hit our ground plane this frame so make sure the maintenance speed is reset
+ mBottomOutSpeedMaintenance = 0.0f;
+ }
+
+ }
+ }
+
+
+
+
+
+
+ bool inflictDamage = true;
+
+
+ if(simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle && simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+
+ // lateral resistance drop
+
+ if(mVehicleType != VT_USER)
+ {
+ rmt::Vector otherFacing;
+ if(thisIsA)
+ {
+ otherFacing = ((Vehicle*)(simStateB->mAIRefPointer))->mVehicleFacing;
+ }
+ else
+ {
+ otherFacing = ((Vehicle*)(simStateA->mAIRefPointer))->mVehicleFacing;
+ }
+
+ float dp = mVehicleFacing.DotProduct(otherFacing);
+ if(dp < 0.1f)
+ {
+ dp = 0.1f;
+ }
+
+ mCollisionLateralResistanceDropFactor = dp;
+
+
+ // exaggerated hit results - note the test for ! VT_USER ain't gonna help here
+ /*
+ rmt::Vector funImpulse = impulse;
+ funImpulse.NormalizeSafe();
+ funImpulse.y += 0.7f;
+
+ funImpulse.Scale(impulseMagnitude);
+
+ impulse = funImpulse;
+ */
+
+ }
+
+
+ bool carOnCarDamage = CarOnCarDamageLogic(thisIsA, simStateA, simStateB);
+
+ inflictDamage &= carOnCarDamage;
+
+
+ }
+
+
+
+
+ if(GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_INVINCIBLE_CAR))
+ {
+ if(GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle() == this)
+ {
+ return sim::Solving_Continue;
+ }
+ }
+
+ // time for damage inc.
+
+ // debug cheat for the testers...
+
+ if(GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_FULL_DAMAGE_TO_CAR) && mVehicleType == VT_USER)
+ {
+ // debug cheat
+
+ // note - don't actually decrememtn hitpoints, just draw all damaged.
+
+ switch(mDamageType)
+ {
+ //case 1: // user level - flaping joints etc...
+ case VDT_USER:
+ {
+ DamageLocation dl = TranslateCollisionIntoLocation(inCollision);
+ VisualDamageType1(0.995f, dl); // any point in this actually taking in a parameter
+
+
+
+ }
+ break;
+
+ //case 2: // ai - localized damage textures but no flapping
+ case VDT_AI:
+ {
+ DamageLocation dl = TranslateCollisionIntoLocation(inCollision);
+ VisualDamageType2(0.995f, dl); // any point in this actually taking in a parameter
+
+ }
+ break;
+
+ //case 3: // traffic - one big poof and texture
+ case VDT_TRAFFIC:
+ {
+ VisualDamageType3(0.995f); // any point in this actually taking in a parameter
+ }
+ break;
+
+ default:
+ rAssertMsg(0, "what are you doing here?");
+
+ }
+
+
+ }
+ // normal case
+ else if((mVehicleCanSustainDamage && inflictDamage) || oneTapTrafficDeath)// && this->mVehicleType != VT_TRAFFIC) // todo - definately need to reassess the traffic test here
+ {
+ if(oneTapTrafficDeath)
+ {
+ this->TriggerDamage(100.0f);
+ }
+ else if(mNoDamageTimer == 0.0f)
+ {
+ if( simStateA->mAIRefIndex == PhysicsAIRef::redBrickVehicle &&
+ simStateB->mAIRefIndex == PhysicsAIRef::redBrickVehicle )
+ {
+ // The hitter sustains less damage, the hit sustains more
+ if( !mWasHitByVehicle )
+ {
+ normalizedMagnitude *= 0.5f; // I was the hitter! Yes!
+ }
+ }
+ SwitchOnDamageTypeAndApply(normalizedMagnitude, inCollision);
+ }
+ }
+
+
+ // test
+ //static float fun = 2.0f;
+ //impulse.Scale(fun);
+
+ return sim::Solving_Continue;
+
+}
+
+//=============================================================================
+// Vehicle::DusitsStunTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float normalizedMagnitude)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::DusitsStunTest(float normalizedMagnitude)
+{
+ mCollidedWithVehicle = true;
+ /*
+ const float AI_VEHICLE_STUNNED_IMPACT_THRESHOLD = 0.01f;
+ if( normalizedMagnitude > AI_VEHICLE_STUNNED_IMPACT_THRESHOLD )
+ {
+ mCollidedWithVehicle = true;
+ }
+ */
+}
+void Vehicle::TestWhoHitWhom( sim::SimState* simA, sim::SimState* simB, float normalizedMagnitude, const sim::Collision& inCollision )
+{
+ rAssert( simA->mAIRefIndex == PhysicsAIRef::redBrickVehicle &&
+ simB->mAIRefIndex == PhysicsAIRef::redBrickVehicle );
+
+ sim::Collision simCollision = inCollision;
+ rAssert( rmt::Epsilon( simCollision.mNormal.MagnitudeSqr(),1.0f,0.0005f ) );
+
+ // Want to determine which one I am.
+ // We also want collision normal to point from him to me..
+ // But since collision normal for all sim::Collision always points from B to A,
+ // if he is simState A, then invert the collision normal.
+
+ Vehicle* him = NULL;
+ if( simA->mAIRefPointer == this )
+ {
+ him = (Vehicle*)simB->mAIRefPointer;
+ }
+ else
+ {
+ him = (Vehicle*)simA->mAIRefPointer;
+ simCollision.mNormal.Scale( -1.0f );
+ }
+
+ rmt::Vector myVel;
+ GetVelocity( &myVel );
+
+ bool gotHitByHim = false;
+ float velThreshold = 0.005f;
+
+ // If one of the speeds is near zero, it's obvious who hit whom...
+ if( mSpeed < velThreshold )
+ {
+ gotHitByHim = true;
+ }
+ else if( him->mSpeed < velThreshold )
+ {
+ gotHitByHim = false;
+ }
+ else
+ {
+ // Deal with the ambiguous case of who hit whom, when
+ // both velocities are > zero. Sooo...
+ // The collision normal is always pointing from him to me and is
+ // at this point NORMALIZED. We steal this and invert the vector
+ // to get the heading vector from US to HIM.
+ rmt::Vector toHim = simCollision.mNormal * -1.0f;
+ rAssert( rmt::Epsilon( toHim.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // Now... if my velocity vector is NOT pointing in "more or less"
+ // the same direction as my vector to him, then I'm not the hitter,
+ // so I got hit by him...
+ rmt::Vector myNormalizedVel = myVel / mSpeed;
+ rAssert( rmt::Epsilon( myNormalizedVel.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ const float cosAlphaTest = 0.8660254f; //approx cos30 (in either directions)
+ if( myNormalizedVel.Dot( toHim ) < cosAlphaTest ) // angle greater than cosalpha
+ {
+ gotHitByHim = true; // we're not the hitter, so we're the hit
+ }
+ else
+ {
+ // ok, we're the hitter... BUT there's a special case for when
+ // we're in a head-on collision with the other car. If we detect
+ // this case we don't want to set both as the hitter... we want
+ // each party to take full damage....
+
+ rmt::Vector hisVel;
+ him->GetVelocity( &hisVel );
+ rmt::Vector hisNormalizedVel = hisVel / him->mSpeed;
+ rAssert( rmt::Epsilon( hisNormalizedVel.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ rmt::Vector toMe = toHim * -1.0f;
+ rAssert( rmt::Epsilon( toMe.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ if( hisNormalizedVel.Dot( toMe ) < cosAlphaTest )
+ {
+ // angle greater than tolerance, so he's not headed at me
+ // therefore not a head-on collision. So do the setting
+ // normally.
+ gotHitByHim = false; // we're the hitter
+ }
+ else
+ {
+ // we're the hit (because he's coming straight at us with some
+ // tangible velocity.)
+ gotHitByHim = true;
+ }
+ }
+
+ }
+
+ // ok we figured out that ya we got hit, so store the appropriate data
+ if( gotHitByHim )
+ {
+ // Do special transit to slip for all vehicles
+ // NOTE: If don't want player vehicle to transit to slip from impact,
+ // just gotta put in a check here that we're not player vehicle
+ if( normalizedMagnitude > 0.035f )
+ {
+ mVehicleState = VS_SLIP;
+ }
+ mWasHitByVehicle = true;
+ mWasHitByVehicleType = him->mVehicleType;
+ mNormalizedMagnitudeOfVehicleHit = normalizedMagnitude;
+ mSwerveNormal = simCollision.mNormal;
+ }
+}
+
+
+//=============================================================================
+// Vehicle::SwitchOnDamageTypeAndApply
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float normalizedMagnitude)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SwitchOnDamageTypeAndApply(float normalizedMagnitude, sim::Collision& inCollision)
+{
+
+ // first just decrement the hit points
+ //TriggerDamage(normalizedMagnitude);
+
+ // make sure appropriate effects level is playing
+
+ //float perc = GetVehicleLifePercentage();
+ float perc = TriggerDamage(normalizedMagnitude);
+
+ switch(mDamageType)
+ {
+ //case 1: // user level - flaping joints etc...
+ case VDT_USER:
+ {
+ DamageLocation dl = TranslateCollisionIntoLocation(inCollision);
+ VisualDamageType1(1.0f - perc, dl); // any point in this actually taking in a parameter
+
+
+
+ }
+ break;
+
+ //case 2: // ai - localized damage textures but no flapping
+ case VDT_AI:
+ {
+ DamageLocation dl = TranslateCollisionIntoLocation(inCollision);
+ VisualDamageType2(1.0f - perc, dl); // any point in this actually taking in a parameter
+
+ }
+ break;
+
+ case VDT_TRAFFIC:
+ {
+ VisualDamageType3(1.0f - perc); // any point in this actually taking in a parameter
+ }
+ break;
+
+ default:
+ rAssertMsg(0, "what are you doing here?");
+
+ }
+
+
+
+}
+
+
+//=============================================================================
+// Vehicle::CarOnCarDamageLogic
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( sim::SimState* simStateA, sim::SimState* simStateB)
+//
+// Return: bool
+//
+//=============================================================================
+bool Vehicle::CarOnCarDamageLogic(bool thisIsA, sim::SimState* simStateA, sim::SimState* simStateB)
+{
+
+ if(this->mIsADestroyObjective)
+ {
+ if(thisIsA)
+ {
+ if( ((Vehicle*)(simStateB->mAIRefPointer))->mVehicleType == VT_USER)
+ {
+ // destroy objective hit by user car so it should take damage!
+ return true;
+ }
+ else
+ {
+ //inflictDamage = false;
+ return false;
+ }
+ }
+ else
+ {
+ if( ((Vehicle*)(simStateA->mAIRefPointer))->mVehicleType == VT_USER)
+ {
+ // destroy objective hit by user car so it should take damage
+ return true;
+ }
+ else
+ {
+ //inflictDamage = false;
+ return false;
+ }
+ }
+
+
+ }
+ else if(this->mVehicleType == VT_USER)
+ {
+
+ // I want to know if this is a dump mission - if so, I shouldn't take damage....
+
+
+
+ if(thisIsA)
+ {
+ if( ((Vehicle*)(simStateB->mAIRefPointer))->mIsADestroyObjective)
+ {
+ // user car hitting a destroy objective so it should not take damage
+ //inflictDamage = false;
+
+ //return false;
+
+ // test - April 8, 2003
+ // even during a destroy mission, the user will take damage when hitting the destory objective
+ // makes stronger cars more important
+ return true;
+
+ }
+ }
+ else
+ {
+ if( ((Vehicle*)(simStateA->mAIRefPointer))->mIsADestroyObjective)
+ {
+ // destroy objective hit by user car so it should take damage
+ //inflictDamage = false;
+
+
+ //return false;
+ // see note above
+ return true;
+ }
+ }
+
+ }
+
+ return true;
+}
+
+
+//=============================================================================
+// Vehicle::CameraShakeTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float impulseMagnitude, sim::Collision& inCollision)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::CameraShakeTest(float impulseMagnitude, sim::Collision& inCollision)
+{
+
+ //TODO: Greg can you make this more reliable? Should I take into account
+ //the mass of the vehicle?
+ //Fudge some magnitude calc to send to the shaker.
+
+ // greg responds: the mass is taken into account in the size of the impulse - I think...?
+
+ const float cameraShakeThreshold = 19000.0f;
+ const float maxIntensity = 100000.0f; // copied from PostReactToCollision, but doesn't have to be the same
+
+ if(impulseMagnitude > cameraShakeThreshold)
+ {
+ ShakeEventData shakeData;
+ shakeData.playerID = 0; //Hack...
+ shakeData.force = impulseMagnitude / maxIntensity;
+
+ rmt::Vector pointOnOtherObject;
+
+ sim::CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ sim::CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ sim::SimState* simStateA = collObjA->GetSimState();
+ sim::SimState* simStateB = collObjB->GetSimState();
+
+ if(simStateA == mSimStateArticulated)
+ {
+ pointOnOtherObject = inCollision.GetPositionB();
+ }
+ else if(simStateB == mSimStateArticulated)
+ {
+ pointOnOtherObject = inCollision.GetPositionA();
+ }
+ else
+ {
+ rAssertMsg(0, "Ask Greg what went wrong!");
+ }
+
+ rmt::Vector contactPointToCenter;
+ contactPointToCenter.Sub(this->rPosition(), pointOnOtherObject);
+ contactPointToCenter.NormalizeSafe();
+
+ shakeData.direction = contactPointToCenter;
+
+ GetEventManager()->TriggerEvent( EVENT_CAMERA_SHAKE, (void*)(&shakeData) );
+ }
+
+
+}
+
+//=============================================================================
+// Vehicle::SparksTest
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float impulseMagnitude, sim::Collision& inCollision)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SparksTest(float impulseMagnitude, sim::Collision& inCollision) // should this also be wrapped in mUserDrivingCar??
+{
+ sim::CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ sim::CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ sim::SimState* simStateA = collObjA->GetSimState(); // A is the vehicle.
+ sim::SimState* simStateB;
+ if( simStateA->mAIRefPointer != this )
+ {
+ simStateB = simStateA;
+ simStateA = collObjB->GetSimState();
+ }
+ else
+ {
+ simStateB = collObjB->GetSimState();
+ }
+
+ // Lets add some vehicle specific particle effects upon collision
+ // If the other collision object also a vehicle, lets create some sparks!
+ // Better place for this constant?
+ const float MIN_IMPULSE_VEHICLE_VEHICLE_SPARKS = 0.01f;
+ float velocityScale = 0.0f;
+
+ bool doStars = false;
+ switch( simStateB->mAIRefIndex )
+ {
+ case PhysicsAIRef::redBrickPhizFence:
+ impulseMagnitude *= ( 1.0f / 25000.0f );
+ doStars = true;
+ break;
+ case PhysicsAIRef::redBrickVehicle:
+ impulseMagnitude *= ( 1.0f / 5000.0f );
+ velocityScale = impulseMagnitude;
+ break;
+ case PhysicsAIRef::redBrickPhizMoveable:
+ impulseMagnitude *= ( 1.0f / 5000.0f );
+ velocityScale = impulseMagnitude * 0.5f;
+ break;
+ case PhysicsAIRef::redBrickPhizStatic:
+ impulseMagnitude *= ( 1.0f / 25000.0f );
+ impulseMagnitude = rmt::Min( 1.0f, impulseMagnitude * 2.0f );
+ doStars = true;
+ default:
+ return;
+ }
+
+ if( impulseMagnitude > 10.0f )
+ {
+ impulseMagnitude *= 0.1f;
+ }
+
+ if ( impulseMagnitude > MIN_IMPULSE_VEHICLE_VEHICLE_SPARKS )
+ {
+ const rmt::Vector& pos = inCollision.GetPositionA();
+ if( doStars )
+ {
+ GetSparkleManager()->AddStars( pos, impulseMagnitude );
+ }
+ else
+ {
+ rmt::Vector vel = mVelocityCM;
+ vel.y = 0.0f;
+ vel.Normalize();
+ vel.Scale( velocityScale );
+ GetSparkleManager()->AddSparks( pos, vel, impulseMagnitude );
+ }
+ }
+}
+
+//=============================================================================
+// Vehicle::VisualDamageType1
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float percentageDamage)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::VisualDamageType1(float percentageDamage, DamageLocation dl)
+{
+/*
+ // new design, new range
+
+ 0 normal
+ 10 flap
+ 30 texture
+ 60 white smoke
+ 99 grey smoke (the one-hit indicator) - this will have been clamped to exactly this value
+ 100 black smoke and flame - car will blow in X seconds
+
+
+
+*/
+
+ // get the joint in question:
+ int joint = -1;
+ switch(dl)
+ {
+ case dl_hood:
+ joint = mHoodJoint;
+ break;
+
+ case dl_trunk:
+ joint = mTrunkJoint;
+ break;
+
+ case dl_driverside:
+ joint = mDoorDJoint;
+ break;
+
+ case dl_passengerside:
+ joint = mDoorPJoint;
+ break;
+
+ default:
+ rAssert(0);
+ }
+
+ if(percentageDamage > 0.1f)
+ {
+ // only join that got hit should flap
+ if(joint != -1)
+ {
+ int index = mJointIndexToInertialJointDriverMapping[joint];
+ if(index != -1)
+ {
+ mPhObj->GetJoint(joint)->SetInvStiffness(1.0f);
+ mInertialJointDrivers[index]->SetIsEnabled(true);
+ }
+ }
+ }
+
+
+ if(percentageDamage > 0.3f)
+ {
+ // only the joint that got hit should have texture on it.
+
+ switch(dl)
+ {
+ case dl_hood:
+ mGeometryVehicle->DamageTextureHood(true);
+ break;
+
+ case dl_trunk:
+ mGeometryVehicle->DamageTextureTrunk(true);
+ mDontShowBrakeLights = true;
+ break;
+
+ case dl_driverside:
+ mGeometryVehicle->DamageTextureDoorD(true);
+ break;
+
+ case dl_passengerside:
+ mGeometryVehicle->DamageTextureDoorP(true);
+ break;
+
+ default:
+ rAssert(0);
+ }
+
+ // but all should flap
+ // all sides flapping ?
+ int index;
+
+ if(mHoodJoint != -1)
+ {
+ index = mJointIndexToInertialJointDriverMapping[mHoodJoint];
+ if(index != -1)
+ {
+ mPhObj->GetJoint(mHoodJoint)->SetInvStiffness(1.0f);
+ mInertialJointDrivers[index]->SetIsEnabled(true);
+ }
+ }
+
+ if(mTrunkJoint != -1)
+ {
+ index = mJointIndexToInertialJointDriverMapping[mTrunkJoint];
+ if(index != -1)
+ {
+ mPhObj->GetJoint(mTrunkJoint)->SetInvStiffness(1.0f);
+ mInertialJointDrivers[index]->SetIsEnabled(true);
+ }
+ }
+
+ if(mDoorDJoint != -1)
+ {
+ index = mJointIndexToInertialJointDriverMapping[mDoorDJoint];
+ if(index != -1)
+ {
+ mPhObj->GetJoint(mDoorDJoint)->SetInvStiffness(1.0f);
+ mInertialJointDrivers[index]->SetIsEnabled(true);
+ }
+ }
+
+ if(mDoorPJoint != -1)
+ {
+ index = mJointIndexToInertialJointDriverMapping[mDoorPJoint];
+ if(index != -1)
+ {
+ mPhObj->GetJoint(mDoorPJoint)->SetInvStiffness(1.0f);
+ mInertialJointDrivers[index]->SetIsEnabled(true);
+ }
+ }
+
+
+
+ }
+
+
+ if(percentageDamage > 0.6f)
+ {
+ // for now, just try taking off the hood before we make smoke pour out there.
+ //int index;
+
+ if(mHoodJoint != -1)
+ {
+ mGeometryVehicle->HideFlappingPiece(mHoodJoint, true);
+ }
+
+ // all texture
+
+ mGeometryVehicle->DamageTextureHood(true);
+ mGeometryVehicle->DamageTextureTrunk(true);
+ mGeometryVehicle->DamageTextureDoorD(true);
+ mGeometryVehicle->DamageTextureDoorP(true);
+
+ // white smoke
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeLight);
+
+ mDontShowBrakeLights = true;
+ mGeometryVehicle->SetLightsOffDueToDamage(true);
+
+ // at this point lights and brake lights should stop working
+
+
+ }
+
+ if(percentageDamage >= 0.98f) // this is the one-more-hit warning
+ {
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeMedium);
+
+ }
+
+ if(percentageDamage > 0.99f) // about to blow
+ {
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeHeavy);
+ }
+
+}
+
+
+
+//=============================================================================
+// Vehicle::VisualDamageType2
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float percentageDamage, DamageLocation dl)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::VisualDamageType2(float percentageDamage, DamageLocation dl)
+{
+/*
+ try this range
+
+ 0 normal
+ 10 texture damage (localized front, back, driver side, passenger side)
+
+ 40 smoke level 1
+ 60 smoke level 2
+ 80 smoke level 3
+ 100 disabled
+
+
+*/
+
+
+ // note:
+ // joint might still be -1, if the is a type 1 car but it doesn't have all four
+ // flapping joints?
+
+ // this is bulky, but fuck it, gotta get this shit working like yesterday
+
+ if(percentageDamage > 0.1f)
+ {
+ // only the joint that got hit should have texture on it.
+
+ switch(dl)
+ {
+ case dl_hood:
+ mGeometryVehicle->DamageTextureHood(true);
+ break;
+
+ case dl_trunk:
+ mGeometryVehicle->DamageTextureTrunk(true);
+ mDontShowBrakeLights = true;
+ break;
+
+ case dl_driverside:
+ mGeometryVehicle->DamageTextureDoorD(true);
+ break;
+
+ case dl_passengerside:
+ mGeometryVehicle->DamageTextureDoorP(true);
+ break;
+
+ default:
+ rAssert(0);
+ }
+
+
+
+ }
+
+
+ if(percentageDamage > 0.4f)
+ {
+ // smoke 1
+ //
+ // ? all sides textured?
+
+ mGeometryVehicle->DamageTextureHood(true);
+ mGeometryVehicle->DamageTextureTrunk(true);
+ mGeometryVehicle->DamageTextureDoorD(true);
+ mGeometryVehicle->DamageTextureDoorP(true);
+
+ //mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeLight);
+ mGeometryVehicle->SetLightsOffDueToDamage(true);
+
+ }
+
+ if(percentageDamage > 0.6f)
+ {
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeLight);
+ //mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeMedium);
+ }
+
+ if(percentageDamage >= 0.98f)
+ {
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeMedium);
+ }
+
+ if(percentageDamage > 0.99f)
+ {
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeHeavy);
+ }
+
+}
+
+
+
+//=============================================================================
+// Vehicle::VisualDamageType3
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float percentageDamage)
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::VisualDamageType3(float percentageDamage)
+{
+
+ if(percentageDamage > 0.6f)
+ {
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeLight);
+ //mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeMedium);
+ }
+
+ if(percentageDamage >= 0.98f)
+ {
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeMedium);
+ }
+
+ if(percentageDamage > 0.99f)
+ {
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeHeavy);
+ }
+
+}
+
+
+//=============================================================================
+// Vehicle::SyncVisualDamage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float Health )
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SyncVisualDamage( float Health )
+{
+ if( Health >= 1.0f )
+ {
+ return;
+ }
+ else if( Health <= 0.0f )
+ {
+ // Husk.
+ mHitPoints = 0.0f;
+ mVehicleDestroyed = true;
+ ActivateTriggers(false);
+ }
+ else
+ {
+ // this should use the same visual logic functions as the normal damage system
+ // just need to make up a DamageLocation
+ // how about the hood? :)
+ switch(mDamageType)
+ {
+ //case 1: // user level - flaping joints etc...
+ case VDT_USER:
+ {
+ VisualDamageType1(1.0f - Health, dl_hood); // any point in this actually taking in a parameter
+ }
+ break;
+
+ //case 2: // ai - localized damage textures but no flapping
+ case VDT_AI:
+ {
+ VisualDamageType2(1.0f - Health, dl_hood); // any point in this actually taking in a parameter
+ }
+ break;
+
+ case VDT_TRAFFIC:
+ {
+ VisualDamageType3(1.0f - Health); // any point in this actually taking in a parameter
+ }
+ break;
+
+ default:
+ rAssertMsg(0, "what are you doing here?");
+
+ }
+
+
+
+
+
+
+
+ /*
+ if( Health <= 0.8f)
+ {
+ mGeometryVehicle->DamageTextureHood(true);
+ mGeometryVehicle->DamageTextureTrunk(true);
+ mGeometryVehicle->DamageTextureDoorD(true);
+ mGeometryVehicle->DamageTextureDoorP(true);
+ }
+ if( Health <= 0.6f )
+ {
+ mGeometryVehicle->SetLightsOffDueToDamage(true);
+ if( Health <= 0.01f )
+ {
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeHeavy);
+ }
+ else if( Health <= 0.4f )
+ {
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeMedium);
+ }
+ else
+ {
+ mGeometryVehicle->SetEngineSmoke(ParticleEnum::eEngineSmokeLight);
+ }
+ }
+
+ */
+ }
+}
+
+//=============================================================================
+// Vehicle::TranslateCollisionIntoLocation
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (sim::Collision& inCollision)
+//
+// Return:
+//
+//=============================================================================
+Vehicle::DamageLocation Vehicle::TranslateCollisionIntoLocation(sim::Collision& inCollision)
+{
+
+ sim::CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ sim::CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ sim::SimState* simStateA = collObjA->GetSimState();
+ sim::SimState* simStateB = collObjB->GetSimState();
+
+ rmt::Vector pointOnOtherCar;
+
+ if(simStateA == mSimStateArticulated)
+ {
+ pointOnOtherCar = inCollision.GetPositionB();
+ }
+ else if(simStateB == mSimStateArticulated)
+ {
+ pointOnOtherCar = inCollision.GetPositionA();
+ }
+ else
+ {
+ rAssertMsg(0, "problem in Vehicle::TranslateCollisionIntoLocation");
+ }
+
+ rmt::Vector centreToContactPoint;
+ centreToContactPoint.Sub(pointOnOtherCar, this->rPosition());
+ centreToContactPoint.NormalizeSafe();
+
+ float dot = mVehicleFacing.DotProduct(centreToContactPoint);
+
+ // first pass:
+ // if no more than 45 degrees from facing, use trunk or hood
+ //
+ // otherwise, project on transverse and see
+
+ const float cos45 = 0.7071f;
+
+ int joint = 0;
+
+ if(dot > cos45)
+ {
+ // hood
+ //return mHoodJoint;
+ //joint = mHoodJoint;
+
+ return dl_hood;
+ }
+ else if(dot < -cos45)
+ {
+ // trunk
+ //return mTrunkJoint;
+ //joint = mTrunkJoint;
+
+ return dl_trunk;
+ }
+ else
+ {
+ float dot2 = mVehicleTransverse.DotProduct(centreToContactPoint);
+
+ if(dot2 > cos45)
+ {
+ // passenger side
+ //return mDoorPJoint;
+ //joint = mDoorPJoint;
+
+ return dl_driverside;
+ }
+ else
+ {
+ // driver side
+ //return mDoorDJoint;
+ //joint = mDoorDJoint;
+
+ return dl_passengerside;
+ }
+
+ }
+
+ rAssert(0); // shouldn't really ever get here.
+ return dl_hood;
+}
+
+
+
+//=============================================================================
+// Vehicle::BeefUpHitPointsOnTrafficCarsWhenUserDriving
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::BeefUpHitPointsOnTrafficCarsWhenUserDriving()
+{
+ // can't just use VT_TRAFFIC since we want the effect if we've gone to a phone booth
+ if( this->mVehicleID == VehicleEnum::TAXIA || // plus all others Jeff gives me
+ this->mVehicleID == VehicleEnum::COMPACTA ||
+ this->mVehicleID == VehicleEnum::MINIVANA ||
+ this->mVehicleID == VehicleEnum::PICKUPA ||
+ this->mVehicleID == VehicleEnum::SEDANA ||
+ this->mVehicleID == VehicleEnum::SEDANB ||
+ this->mVehicleID == VehicleEnum::SPORTSA ||
+ this->mVehicleID == VehicleEnum::SPORTSB ||
+ this->mVehicleID == VehicleEnum::SUVA ||
+ this->mVehicleID == VehicleEnum::WAGONA ||
+ this->mVehicleID == VehicleEnum::COFFIN ||
+ this->mVehicleID == VehicleEnum::HALLO ||
+ this->mVehicleID == VehicleEnum::SHIP ||
+ this->mVehicleID == VehicleEnum::WITCHCAR ||
+ this->mVehicleID == VehicleEnum::AMBUL ||
+ this->mVehicleID == VehicleEnum::BURNSARM ||
+ this->mVehicleID == VehicleEnum::FISHTRUC ||
+ this->mVehicleID == VehicleEnum::GARBAGE ||
+ this->mVehicleID == VehicleEnum::GLASTRUC ||
+ this->mVehicleID == VehicleEnum::ICECREAM ||
+ this->mVehicleID == VehicleEnum::ISTRUCK ||
+ this->mVehicleID == VehicleEnum::NUCTRUCK ||
+ this->mVehicleID == VehicleEnum::PIZZA ||
+ this->mVehicleID == VehicleEnum::SCHOOLBU ||
+ this->mVehicleID == VehicleEnum::VOTETRUC ||
+ this->mVehicleID == VehicleEnum::CBONE )
+
+ {
+
+ // only add to an undamaged car, so that you can't get in and out to repair
+ if(this->GetVehicleLifePercentage(this->mHitPoints) == 1.0f)
+ {
+
+ if(this->mDesignerParams.mHitPoints <= 1.0f)
+ {
+ this->mDesignerParams.mHitPoints += 1.0f;
+ this->mHitPoints = mDesignerParams.mHitPoints;
+ }
+ }
+ }
+
+}
+
+
+//=============================================================================
+// Vehicle::GetVehicleDamagePercentage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: float
+//
+//=============================================================================
+float Vehicle::GetVehicleLifePercentage(float testvalue)
+{
+ float temp = testvalue / mDesignerParams.mHitPoints;
+ if(temp < 0.0f)
+ {
+ // shouldn't ever really be here
+ // going below 0 should be caught somewhere else
+ temp = 0.0f;
+ }
+ rAssert(temp <= 1.0f);
+ rAssert(temp >= 0.0f);
+
+ return temp;
+
+}
+
+
+//=============================================================================
+// Vehicle::TriggerDamage
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+float Vehicle::TriggerDamage(float amount, bool clamp)
+{
+
+ // TODO - disable some updating stuff - like locomotive shit, if
+ // we are destroyed
+ if(mVehicleDestroyed)
+ {
+ // already destroyed
+ // do nothing
+
+ return 0.0f;
+ }
+
+ // try the clamp to 99% for last hit on all car types
+ if(clamp)//mDamageType == VDT_USER || mDamageType == VDT_AI)
+ {
+
+ float currentPerc = GetVehicleLifePercentage(mHitPoints);
+
+ if(currentPerc > 0.02f)
+ {
+ // we want to make sure we clamp this hit at 1% life left
+
+ float testvalue = mHitPoints;
+ testvalue -= amount;
+
+ if(testvalue < 0.0f)
+ {
+ testvalue = 0.0f;
+ }
+
+ float perc = GetVehicleLifePercentage(testvalue);
+
+ if(perc < 0.02f)
+ {
+ // the clamp case:
+
+ mHitPoints = 0.001f; // just some token amount
+
+ //only update the charactersheet if this car belongs to the player
+ if (mbPlayerCar == true)
+ {
+ GetCharacterSheetManager()->UpdateCarHealth( mCharacterSheetCarIndex, perc);
+ }
+
+ GetEventManager()->TriggerEvent(EVENT_VEHICLE_DAMAGED, (void*)this);
+ mNoDamageTimer = 1.0f; // set countdown
+ return 0.02f; // return fixed amount
+ }
+ else
+ {
+ // just normal hit
+ mHitPoints = testvalue; // fall through and continue
+ }
+
+ }
+ else
+ {
+ // this is the hit that takes us out
+ mHitPoints = 0.0f;
+ }
+
+
+ }
+ else
+ {
+ mHitPoints -= amount;
+ }
+
+ if(mHitPoints <= 0.0f)
+ {
+ // we've been destroyed
+ mHitPoints = 0.0f;
+ mVehicleDestroyed = true;
+
+ ActivateTriggers(false);
+ //GetEventManager()->TriggerEvent(EVENT_VEHICLE_DESTROYED_BY_USER, (void*)this);
+ // move the triggering of this event to PostSubstepUpdate
+ //
+ // user car will pause for timer, other cars will send out immediately
+ mDamageOutResetTimer = 0.0f;
+
+ /*
+ // if this is a traffic, then just give'r right here
+ if(mDamageType == VDT_TRAFFIC)
+ {
+
+ ParticleAttributes pa;
+ pa.mType = ParticleEnum::eCarExplosion;
+ rmt::Vector pos = this->rPosition();
+
+ GetParticleManager()->Add(pa, pos);
+
+ TrafficManager::GetInstance()->SwapInTrafficHusk(this);
+
+ }
+ */
+
+ }
+
+ // make sure the damaged event fired off either way...
+ GetEventManager()->TriggerEvent(EVENT_VEHICLE_DAMAGED, (void*)this);
+
+ float health = GetVehicleLifePercentage(mHitPoints);
+
+ //only update the charactersheet if this car belongs to the player
+ if (mbPlayerCar == true)
+ {
+ GetCharacterSheetManager()->UpdateCarHealth( mCharacterSheetCarIndex, health );
+ }
+ return health;
+}
+
+
+
+//=============================================================================
+// Vehicle door handling
+//=============================================================================
+//
+// Figure out the final position of the doors, based on current state,
+// and whether someone is opening/closing them
+//
+
+bool Vehicle::NeedToOpenDoor(Door door)
+{
+ if(!mHasDoors)
+ {
+ return false;
+ }
+
+ if(mDesiredDoorPosition[door] < 0.8f)
+ {
+ return true;
+ }
+
+ if(!HasActiveDoor(door))
+ {
+ return false;
+ }
+
+ return false;
+}
+
+bool Vehicle::NeedToCloseDoor(Door door)
+{
+ // TODO : there are cases where we might want to not close it
+ // (physics, missing door, door left open, etc)
+ if(!mHasDoors)
+ {
+ return false;
+ }
+
+ // check if door was left open when we exited
+ if(mDesiredDoorPosition[door] > 0.0f)
+ {
+ return true;
+ }
+
+ if(!HasActiveDoor(door))
+ {
+ return false;
+ }
+
+ return false;
+}
+
+bool Vehicle::HasActiveDoor(Door door)
+{
+ if(!mHasDoors)
+ {
+ return false;
+ }
+
+ switch(door)
+ {
+ case DOOR_DRIVER:
+ if(mDoorDJoint != -1)
+ {
+ int inertialJointIndex = mJointIndexToInertialJointDriverMapping[ mDoorDJoint ];
+ if( inertialJointIndex == -1 )
+ {
+ rWarningMsg( false, "why is there no inertial joint? doesn't this car have a door?" );
+ return false;
+ }
+ sim::PhysicsJointInertialEffector* inertialJoint = mInertialJointDrivers[ inertialJointIndex ];
+ if( inertialJoint == NULL )
+ {
+ return false;
+ }
+ if( inertialJoint->IsEnabled() )
+ {
+ return false;
+ }
+ }
+
+ break;
+
+ case DOOR_PASSENGER:
+ if(mDoorPJoint != -1)
+ {
+ if(mInertialJointDrivers[mJointIndexToInertialJointDriverMapping[mDoorPJoint]]->IsEnabled())
+ return false;
+ }
+ break;
+ }
+ return true;
+}
+
+void Vehicle::UpdateDoorState(void)
+{
+ if(mDesiredDoorPosition[DOOR_DRIVER] == 0.0f)
+ {
+ if((mDoorDJoint != -1) && (mPhObj->GetJoint(mDoorDJoint)))
+ {
+ mDesiredDoorPosition[DOOR_DRIVER] = mPhObj->GetJoint(mDoorDJoint)->Deformation();
+ }
+ }
+
+ if(mDesiredDoorPosition[DOOR_PASSENGER] == 0.0f)
+ {
+ if((mDoorPJoint != -1) && (mPhObj->GetJoint(mDoorPJoint)))
+ {
+ mDesiredDoorPosition[DOOR_PASSENGER] = mPhObj->GetJoint(mDoorPJoint)->Deformation();
+ }
+ }
+}
+
+void Vehicle::ReleaseDoors(void)
+{
+ mDesiredDoorPosition[DOOR_DRIVER] = 0.0f;
+ mDesiredDoorPosition[DOOR_PASSENGER] = 0.0f;
+}
+
+void Vehicle::PlayExplosionEffect()
+{
+
+ //rmt::Vector explosionCenter = rPosition();
+ // explosionCenter.y += EXPLOSION_Y_OFFSET;
+ GetWorldPhysicsManager()->ApplyForceToDynamicsSpherical( mCollisionAreaIndex, rPosition(), EXPLOSION_EFFECT_RADIUS, EXPLOSION_FORCE );
+
+ // Lets get the explosion position as the center of the wheels
+ rmt::Vector explosionCenter(0,0,0);
+ for ( int i = 0 ; i < 4 ; i++ )
+ {
+ explosionCenter += mSuspensionWorldSpacePoints[i];
+ }
+ explosionCenter *= 0.25f;
+ rmt::Matrix explosionTransform = GetTransform();
+ explosionTransform.FillTranslate( explosionCenter );
+
+ GetBreakablesManager()->Play( BreakablesEnum::eCarExplosion, explosionTransform );
+
+ GetEventManager()->TriggerEvent( EVENT_BIG_BOOM_SOUND, this );
+}
+
+
+void Vehicle::AddToSimulation()
+{
+ //DynaPhysDSG::AddToSimulation(); // greg
+ // jan 31, 2003
+ // I don't think any vehicle should do this - just use it's own ground plane
+ SetLocomotion( VL_PHYSICS );
+}
+
+void Vehicle::ApplyForce( const rmt::Vector& direction, float force )
+{
+ SetLocomotion( VL_PHYSICS );
+ rmt::Vector& rVelocity = GetSimState()->GetLinearVelocity();
+ float deltaV = force / GetMass();
+ // Apply delta velocity
+ rVelocity += (direction * deltaV);
+ // Make it interact with the world
+ AddToSimulation();
+
+ // Damage the vehicle
+ if ( mUserDrivingCar )
+ {
+ TriggerDamage( force * s_DamageFromExplosionPlayer / EXPLOSION_FORCE, false );
+ }
+ else
+ {
+ TriggerDamage( force * s_DamageFromExplosion / EXPLOSION_FORCE, false );
+ }
+}
+
+
+bool Vehicle::AttachCollectible( StatePropCollectible* drawable )
+{
+ if ( drawable->GetState() != 0 )
+ return false;
+
+ bool wasAttached = mGeometryVehicle->AttachCollectible( drawable );
+ if ( wasAttached )
+ {
+ GetEventManager()->TriggerEvent( EVENT_VEHICLE_COLLECTED_PROP, this );
+ }
+ return wasAttached;
+}
+
+StatePropCollectible* Vehicle::GetAttachedCollectible()
+{
+ return mGeometryVehicle->GetAttachedCollectible();
+}
+
+void Vehicle::DetachCollectible( const rmt::Vector& velocity, bool explode )
+{
+ mGeometryVehicle->DetachCollectible(velocity, explode);
+}
+
+
+// move the door to the specified location (called by the character AI during get in/out of car)
+void Vehicle::MoveDoor(Door door, DoorAction action, float position)
+{
+ rAssert(door < 2);
+
+ mDesiredDoorPosition[door] = position;
+ mDesiredDoorAction[door] = action;
+}
+
+// calculate the position of a single door
+void Vehicle::CalcDoor(unsigned doorIndex, unsigned joint, float doorOpen)
+{
+ // clamp desired door position to 0 -> 1
+ if(mDesiredDoorPosition[doorIndex] > rmt::Abs(doorOpen))
+ mDesiredDoorPosition[doorIndex] = rmt::Abs(doorOpen);
+
+ if(mDesiredDoorPosition[doorIndex] < 0.0f)
+ mDesiredDoorPosition[doorIndex] = 0.0f;
+
+ // grab the rest pose
+ poser::Pose* pose = mPoseEngine->GetPose();
+ rmt::Matrix matrix = pose->GetSkeleton()->GetJoint(joint)->restPose;
+ rmt::Matrix tmp;
+
+ // rotate by the scaled desired position
+ tmp.Identity();
+ tmp.FillRotateY(mDesiredDoorPosition[doorIndex] * doorOpen);
+ tmp.Mult(matrix);
+
+ // set the new joint position
+ pose->GetJoint(joint)->SetObjectMatrix(tmp);
+
+ // if we just closed the door all the way, turn off future door activity
+ if((mDesiredDoorAction[doorIndex] == DOORACTION_CLOSE) && (mDesiredDoorPosition[doorIndex] == 0.0f))
+ {
+ // if neccesary, reset physics state
+ int index = mJointIndexToInertialJointDriverMapping[joint];
+ if( index != -1 )
+ {
+ if(mInertialJointDrivers[index]->IsEnabled())
+ {
+ mPhObj->GetJoint(joint)->ResetDeformation();
+ }
+ }
+ mDesiredDoorAction[doorIndex] = DOORACTION_NONE;
+ }
+
+}
+
+void Vehicle::CalcDoors(void)
+{
+ // angle in radians that represents a fully open door
+ // TODO : could be tweakable
+ const float doorOpen = 1.0f;
+
+ // update each door, if neccesary
+ if((mDesiredDoorAction[0] != DOORACTION_NONE) || (mDesiredDoorAction[1] != DOORACTION_NONE))
+ {
+ if((mDesiredDoorAction[0] != DOORACTION_NONE) && mDoorDJoint != -1)
+ {
+ CalcDoor(0, mDoorDJoint, doorOpen);
+ }
+
+ if((mDesiredDoorAction[1] != DOORACTION_NONE) && mDoorPJoint != -1)
+ {
+ // passenger door is backwards, so we do negative rotation
+ CalcDoor(1, mDoorPJoint, -1.0f * doorOpen);
+ }
+ }
+}
+
+void Vehicle::SetDriverName(const char* name)
+{
+ if(!name)
+ {
+ mDriverName[0] = 0;
+ return;
+ }
+
+ rAssert(strlen(name) < 32);
+ strcpy(mDriverName, name);
+}
+
+const char* Vehicle::GetDriverName(void)
+{
+ return mDriverName;
+}
+
+
+void Vehicle::SetDriver(Character* d)
+{
+ tRefCounted::Assign(mpDriver, d);
+}
+
+void Vehicle::SetShadowAdjustments( float Adjustments[ 4 ][ 2 ] )
+{
+ mGeometryVehicle->SetShadowAdjustments( Adjustments );
+}
+
+void Vehicle::SetShininess( unsigned char EnvRef )
+{
+ mGeometryVehicle->SetShininess( EnvRef );
+}
+
+//=============================================================================
+// Vehicle::GetTopSpeedKmh
+//=============================================================================
+// Description: returns the top speed of which this vehicle is capable in world
+// units, not KPH
+//
+// Parameters: NONE
+//
+// Return: float
+//
+//=============================================================================
+float Vehicle::GetTopSpeed() const
+{
+ return mDesignerParams.mDpTopSpeedKmh * ( 1000.0f / 3600.0f );
+} \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehicle.h b/game/code/worldsim/redbrick/vehicle.h
new file mode 100644
index 0000000..928e639
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehicle.h
@@ -0,0 +1,989 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehicle.h
+//
+// Description: the car
+//
+// History: Nov 16, 2001 + Created, gmayer
+//
+//=============================================================================
+
+
+#ifndef _VEHICLE_H
+#define _VEHICLE_H
+
+
+//========================================
+// Nested System Includes
+//========================================
+
+#include <camera/isupercamtarget.h>
+#include <constants/vehicleenum.h>
+#include <presentation/gui/utility/hudmap.h>
+#include <radmath/radmath.hpp>
+#include <render/DSG/InstDynaPhysDSG.h>
+#include <render/intersectmanager/intersectmanager.h> // For terrain type enumeration.
+
+//========================================
+// Forward References
+//========================================
+
+class Character;
+class EventLocator;
+class GeometryVehicle;
+class PhysicsLocomotion;
+class RectTriggerVolume;
+class RootMatrixDriver;
+class StatePropCollectible;
+class SuspensionJointDriver;
+class TrafficLocomotion;
+class TrafficVehicle;
+class VehicleEventListener;
+class VehicleLocomotion;
+class Wheel;
+
+namespace poser
+{
+ class PoseEngine;
+}
+
+namespace sim
+{
+ class PhysicsJointInertialEffector;
+ class PhysicsJointMatrixModifier;
+ class SimStateArticulated;
+ class ArticulatedPhysicsObject;
+ class PhysicsProperties;
+}
+
+enum VehicleLocomotionType {VL_PHYSICS, VL_TRAFFIC};
+enum VehicleState {VS_NORMAL, VS_SLIP, VS_EBRAKE_SLIP};
+
+enum VehicleType {VT_USER, VT_AI, VT_TRAFFIC, VT_LAST};
+
+//=============================================================================
+//
+// The Vehicle.
+//
+//=============================================================================
+class Vehicle : public DynaPhysDSG, public ISuperCamTarget, public IHudMapIconLocator
+{
+public:
+
+ //-----
+ // core
+ //-----
+
+ Vehicle();
+ virtual ~Vehicle();
+
+ /*
+ void AddRef();
+ void Release();
+ */
+
+ bool Init( const char* name, sim::SimEnvironment* se, VehicleLocomotionType loco, VehicleType vt = VT_USER, bool startoutofcar = true);
+ void Reset( bool ResetDamage = true, bool clearTraffic = false );
+ void ResetOnSpot( bool resetDamage=true, bool moveCarOntoRoad = true );
+
+ // make sure, for gui reasons, we don't call repeatedly
+ bool mAlreadyCalledAutoResetOnSpot;
+
+ void SetPosition(rmt::Vector* newPos);
+
+ void TransitToAI();
+
+ // utility
+ float FacingIntoRad(rmt::Vector facing);
+
+ // reset has come to be the one that uses the initial position & facing
+ // make a different reset that will just reset the flags and state - set transform can be called in
+ // conjunction with this
+ void ResetFlagsOnly(bool resetDamage);
+
+ VehicleType mVehicleType;
+
+ int mVehicleCentralIndex;
+
+ //
+ // Dlong: Proposed new methods (works better!)
+ //
+ void SetInitialPositionGroundOffsetAutoAdjust( rmt::Vector* newPos );
+ void SetInitialPosition( rmt::Vector* newPos );
+ void SetResetFacingInRadians( float rotation );
+ float GetFacingInRadians();
+
+ void SetTransform( rmt::Matrix &m );
+ void TrafficSetTransform(rmt::Matrix &m);
+
+ // for debugging and a cheat
+ void JumpOnHorn(float test);
+
+ void TurboOnHorn();
+ float mSecondsTillNextTurbo;
+ int mNumTurbos;
+
+ // Implements CollisionEntityDSG
+ //
+ virtual sim::Solving_Answer PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision );
+ virtual sim::Solving_Answer PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision);
+
+ // drawable vehicle encapsulation:
+ GeometryVehicle* mGeometryVehicle;
+
+ rmt::Matrix mTransform;
+
+ // TODO - this is temporary?
+ const rmt::Vector& GetPosition() { return *((rmt::Vector*)mTransform.m[3]); }
+ const rmt::Vector& GetFacing() { return mVehicleFacing; }
+ const rmt::Matrix& GetTransform();
+ const rmt::Vector& GetExtents() { return mExtents; }
+
+ char* mName;
+ rmt::Vector mInitialPosition;
+ float mResetFacingRadians;
+
+ //This is which type of vehicle it is.
+ VehicleEnum::VehicleID mVehicleID;
+ void AssignEnumBasedOnName();
+
+
+ // vehicle event stuff
+ VehicleEventListener* mVehicleEventListener;
+
+ void EnteringJumpBoostVolume();
+ void ExitingJumpBoostVolume();
+
+ bool mDoingJumpBoost;
+
+
+ void ActivateTriggers( bool activate );
+ /*
+
+ in pure3d lhc, top-down view of vehicle:
+
+
+
+ ^
+ |
+ |
+ |
+ +Z
+ |
+ |
+ front
+ _____________
+ wheel 2 | | wheel 3
+ | |
+ | |
+ | |
+ | |
+ | |
+ | | ---+X----->
+ | |
+ | |
+ | |
+ | |
+ | |
+ wheel 1 ------------- wheel 0
+ rear
+
+
+
+ */
+
+ rmt::Vector mVehicleFacing;
+ rmt::Vector mVehicleUp;
+ rmt::Vector mVehicleTransverse;
+
+
+ rmt::Vector mVelocityCM;
+ float mSpeed;
+ float mPercentOfTopSpeed;
+ float mSpeedKmh;
+ float mLastSpeedKmh;
+ float mAccelMss;
+
+ rmt::Vector mOriginalCMOffset;
+ rmt::Vector mCMOffset; // this one modified by designer input
+
+ //----------------
+ // Shadow stuff
+ //----------------
+
+ virtual int CastsShadow();
+ bool IsSimpleShadow( void ) { return m_IsSimpleShadow; }
+ void SetSimpleShadow( bool IsSimpleShadow ) { m_IsSimpleShadow = IsSimpleShadow; }
+ void SetShadow( tShadowMesh* pShadowMesh ){};
+ void DisplayShadow();
+ void DisplaySimpleShadow( void );
+
+ //----------------
+ // Terrain type stuff
+ //----------------
+
+ // This is a 'rollup' from the four wheels.
+ eTerrainType mTerrainType;
+ bool mInterior;
+
+
+ float GetGroundY();
+
+
+ //----------------
+ // locomotion shit
+ //----------------
+
+ VehicleState mVehicleState; //{VS_NORMAL, VS_SLIP};
+
+ VehicleLocomotionType GetLocomotionType() {return mLoco;}
+
+ VehicleLocomotion* mVehicleLocomotion;
+ VehicleLocomotionType mLoco;
+
+
+ PhysicsLocomotion* mPhysicsLocomotion;
+
+
+ // *** For Traffic *** //
+ TrafficVehicle* mTrafficVehicle;
+
+ TrafficLocomotion* mTrafficLocomotion;
+
+ void CreateLocomotions();
+ void SetLocomotion( VehicleLocomotionType loco );
+
+
+ friend class PhysicsLocomotion;
+ friend class TrafficLocomotion;
+
+ bool mLocoSwitchedToPhysicsThisFrame;
+
+ //---------------------------
+ // world simulation interface
+ //---------------------------
+
+ void PreSubstepUpdate(float dt); // only thing this does right now is reset a flag
+ void PostSubstepUpdate(float dt);
+ void PreCollisionPrep(float dt, bool firstSubstep);
+ void Update(float dt);
+
+ void SetVehicleSimEnvironment(sim::SimEnvironment* se); // don't actually need this anymore
+
+ void GetCollisionAreaIndexAndAddSelf();
+ void RemoveSelfAndFreeCollisionAreaIndex();
+
+ void AddSelfToCollisionManager();
+ void AddToOtherCollisionArea(int index);
+ void RemoveSelfFromCollisionManager();
+
+ int mCollisionAreaIndex; // index into the collision area this instance will use.
+ RenderEnums::LayerEnum mRenderLayerEnum;
+ void SetRenderLayerEnum(RenderEnums::LayerEnum renderLayerEnum) {mRenderLayerEnum = renderLayerEnum;}
+ RenderEnums::LayerEnum GetRenderLayerEnum() {return mRenderLayerEnum;}
+
+ void DebugDisplay();
+ void CarDisplay(bool doit);
+ bool mOkToDrawSelf;
+
+ void DrawVehicle( bool draw ) { mDrawVehicle = draw; };
+ bool mDrawVehicle; //This is the same thing as above, but Greg uses the above for debugging.
+
+ //---------------------
+ // controller interface
+ //---------------------
+
+ void SetGas(float gas);
+ void SetBrake(float brake);
+ void SetWheelTurnAngle(float wheelTurnAngle, bool doNotModifyInputValue, float dt);
+ void SetReverse(float reverse);
+ void SetEBrake(float ebrake, float dt); // new timing thing for plum
+
+ void SetWheelTurnAngleDirectlyInRadiansForDusitOnly(float rad);
+
+ // Vehicle's cached values
+ float mGas;
+ float mLastGas;
+ float mDeltaGas;
+ float mBrake;
+ float mWheelTurnAngle;
+ float mWheelTurnAngleInputValue; // for Plum's low speed lag
+ float mReverse;
+ float mEBrake;
+ float mEBrakeTimer;
+ bool mBrakeLightsOn;
+ bool mReverseLightsOn;
+
+ float mBrakeTimer;
+ bool mBrakeActingAsReverse;
+
+
+ float mSteeringInputThreshold; // my own values to modify the h/w input value
+ float mSteeringPreSlope;
+
+ // the designers will have their own value to max the dropoff
+
+ float mUnmodifiedInputWheelTurnAngle; // just for ease of looking at the pure input
+
+
+ bool mDoingRockford;
+
+ float mSpeedBurstTimer;
+ bool mBuildingUpSpeedBurst;
+ bool mDoSpeedBurst;
+ float mFOVToRestore;
+ float mSpeedBurstTimerHalf;
+
+ //to disable player control of vehicle
+ void SetDisableGasAndBrake(bool tf) { mGasBrakeDisabled = tf; }
+ bool mGasBrakeDisabled;
+
+ //----------------------------------
+ // RenderManager/EntityDSG Interface
+ //----------------------------------
+ void Display();
+ rmt::Vector* pPosition();
+ const rmt::Vector& rPosition();
+
+ sim::SimState* GetSimState() const {return (sim::SimState*)mSimStateArticulated;}
+ sim::SimState* mpSimState() const { return GetSimState(); }
+
+ void DSGUpdateAndMove();
+
+ // just in case
+ virtual void OnSetSimState( sim::SimState* ipSimState ) {};
+
+ // stub out?
+ //virtual int FetchGroundPlane();
+ //virtual void FreeGroundPlane();
+
+ //virtual bool IsAtRest();
+
+ //-----------------
+ // camera interface
+ //-----------------
+
+ virtual void GetPosition( rmt::Vector* position );
+ virtual void GetHeading( rmt::Vector* heading );
+ virtual void GetVUP( rmt::Vector* vup );
+ virtual void GetVelocity( rmt::Vector* velocity );
+ virtual unsigned int GetID();
+ virtual bool IsCar() const;
+ virtual bool IsAirborn();
+ virtual bool IsUnstable();
+ virtual bool IsQuickTurn();
+ virtual bool IsInReverse();
+ virtual void GetFirstPersonPosition( rmt::Vector* position ) {};
+ virtual void GetTerrainIntersect( rmt::Vector& pos, rmt::Vector& normal ) const;
+
+
+ void CameraShakeTest(float impulseMagnitude, sim::Collision& inCollision);
+
+ bool IsMovingBackward(); // for Darren - don't have to have 'brake' input held down
+
+ bool mSteeringWheelsOutOfContact; // note:
+ // use for adjusting locomotive force appliction points when cresting hills, so turn this on
+ // when wheels 2,3 leave ground
+
+ bool mAirBorn;
+
+ bool mWeebleOn;
+ bool mCMOffsetSetToOriginal;
+
+ bool mInstabilityOffsetOn;
+
+ // ------------
+ // ai interface
+ // ------------
+ void SetDriverName(const char*);
+ Character* GetDriver()const{ return mpDriver; }
+ const char* GetDriverName(void);
+
+ bool HasDriver( void ) const // note! - this only determines if the car has a character model in the driver seat
+ { // nothing to do with whether or not there is a 'user' driving the car
+ return (mpDriver != NULL) || mPhantomDriver;
+ }
+
+ void SetDriver(Character*);
+
+ void SetPhantomDriver(bool b) { mPhantomDriver = b;}
+
+ char mDriverName[32];
+ Character* mpDriver;
+ bool mPhantomDriver;
+
+ void BounceCharacters(float dt);
+ void RecordRestSeatingPositionsOnEntry();
+ rmt::Vector mOurRestSeatingPosition;
+ rmt::Vector mNPCRestSeatingPosition;
+ static bool sDoBounce;
+
+ float mYAccelForSeatingOffset;
+
+ float mMaxBounceDisplacementPerSecond;
+ float mBounceAccelThreshold; // below this value just try and move back to rest
+ // if accel is above this value then we move opposite accel direction
+
+ rmt::Vector mVelocityCMLag;
+ rmt::Vector mPositionCMLag;
+
+ float mBounceLimit;
+ void ApplyDisplacementToCharacters(float displacement);
+ void MoveCharactersTowardsRestPosition(float dt);
+
+ virtual const char* const GetName();
+
+
+ bool mUserDrivingCar;
+ bool IsUserDrivingCar()const { return mUserDrivingCar; } // Sorry, I just really like to use accessor functions.
+ void SetUserDrivingCar(bool b);
+
+
+ virtual int GetAIRef() {return PhysicsAIRef::redBrickVehicle;}
+ /*
+ static int GetAIRef( void )
+ {
+ return PhysicsAIRef::redBrickVehicle;
+ }
+ */
+
+ virtual int GetGroundPlaneAIRef() {return PhysicsAIRef::redBrickPhizVehicleGroundPlane;}
+
+ /*
+ static int GetGroundPlaneAIRef( void )
+ {
+ return PhysicsAIRef::redBrickPhizVehicleGroundPlane;
+ }
+ */
+
+ const rmt::Vector& GetPassengerLocation( void ) const;
+ const rmt::Vector& GetDriverLocation( void ) const;
+
+ //--------------
+ // sfx interface
+ //--------------
+
+ float GetSpeedKmh();
+ float GetAccelMss();
+ float GetRPM();
+ float GetGas()const { return mGas; }
+ float GetDeltaGas()const { return mDeltaGas; }
+ float GetBrake()const { return mBrake; }
+ float GetSkidLevel(); // overall amount amongst the (currently) 4 wheels
+ // both the skidding - tire locked
+ // and slip - tire rotating really fast.
+ int GetGear(); // -1 is reverse, 0 neutral
+ // TODO - how to do the gearshift interface?
+
+ bool IsSafeToUpShift();
+
+ // gearbox stuff
+ float mRPM;
+ float mRPMUpRate;
+ float mRPMDownRate;
+
+ float mBaseRPM;
+ float mMaxRpm;
+ float mShiftPointHigh;
+ float mShiftPointLow;
+ int mGear; // -1 for reverse, 0 neutral
+
+ float mSkidLevel;
+ float mBurnoutLevel; // use for sound and smoke level?
+ bool mDoingBurnout;
+ void SetGeometryVehicleWheelSmokeLevel();
+
+ bool mNoSkid; // just to flag frinks hovering vehilces...
+ bool mNoFrontSkid; // For the rocket car and other vehicles that can only skid using
+ // the back two wheels
+
+ bool mDoingWheelie; //need this?
+
+ int mNumGears;
+ float* mGearRatios;
+ float mFinalDriveRatio;
+ void InitGears();
+
+ void UpdateGearAndRPM();
+
+ void SparksTest(float impulseMagnitude, sim::Collision& inCollision); // should this also be wrapped in mUserDrivingCar??
+
+ //--------------------
+ // the designer params
+ //--------------------
+
+ struct DesignerParams
+ {
+ // from physicsvehicle
+
+ float mDpGasScale; // proportional to mass
+ float mDpSlipGasScale;
+ float mDpHighSpeedGasScale;
+ float mDpGasScaleSpeedThreshold;
+ float mDpBrakeScale; // proportional to mass
+ float mDpTopSpeedKmh;
+
+ float mDpMass; // think of it as kg
+
+ float mDpMaxWheelTurnAngle; // in degrees
+ float mDpHighSpeedSteeringDrop; // 0.0 to 1.0
+
+ float mDpTireLateralStaticGrip;
+ float mDpTireLateralResistanceNormal;
+ float mDpTireLateralResistanceSlip;
+
+ float mDpTireLateralResistanceSlipNoEBrake; // this one's for more out of control driving
+ float mDpSlipEffectNoEBrake;
+
+ float mDpEBrakeEffect;
+
+ float mDpSuspensionLimit;
+ float mDpSpringk;
+ float mDpDamperc;
+
+ float mDpSuspensionYOffset;
+
+ float mHitPoints;
+
+ float mDpBurnoutRange;
+
+
+ float mDpWheelieRange;
+ float mDpWheelieYOffset;
+ float mDpWheelieZOffset;
+
+ float mDpMaxSpeedBurstTime;
+
+ float mDpDonutTorque;
+
+ // new tunable value for plum
+ float mDpWeebleOffset;
+
+ float mDpGamblingOdds;
+
+ rmt::Vector mDpCMOffset;
+
+ };
+
+ DesignerParams mDesignerParams;
+
+ // simple accessors that script functions call
+ //
+ // note - you have to call CalculateValuesBasedOnDesignerParams after using these.
+ //
+ // make sure to name script methods based on watcher names
+ // these names can match the data.
+
+ //Chuck: Added these so cars now have odds for gambling races.
+ void SetGamblingOdds(float odds) {mDesignerParams.mDpGamblingOdds = odds;}
+ float GetGamblingOdds() {return mDesignerParams.mDpGamblingOdds;}
+
+ //Chuck:using this flag for forced car mission, since forced cars dont belong to the player,but the NPC
+ //Forced cars should respawn and do not check the check to or update the charactersheet. If Flag is true then this
+ //car is owned by the player, if false then its owned by the NPC
+ bool mbPlayerCar;
+
+ void SetGasScale(float gs) {mDesignerParams.mDpGasScale = gs;}
+ void SetSlipGasScale(float value) {mDesignerParams.mDpSlipGasScale = value;}
+
+ void SetHighSpeedGasScale(float gs) {mDesignerParams.mDpHighSpeedGasScale = gs;}
+ void SetGasScaleSpeedThreshold(float t) {mDesignerParams.mDpGasScaleSpeedThreshold = t;}
+
+ void SetBrakeScale(float bs) {mDesignerParams.mDpBrakeScale = bs;}
+ void SetTopSpeedKmh(float ts) {mDesignerParams.mDpTopSpeedKmh = ts;}
+ float GetTopSpeed() const;
+ void SetMass(float m) {mDesignerParams.mDpMass = m;}
+ void SetMaxWheelTurnAngle(float mwta) {mDesignerParams.mDpMaxWheelTurnAngle = mwta;}
+ void SetHighSpeedSteeringDrop(float value) {mDesignerParams.mDpHighSpeedSteeringDrop = value;}
+ void SetTireLateralStaticGrip(float g) {mDesignerParams.mDpTireLateralStaticGrip = g;}
+ void SetTireLateralResistanceNormal(float n) {mDesignerParams.mDpTireLateralResistanceNormal = n;}
+ void SetTireLateralResistanceSlip(float s) {mDesignerParams.mDpTireLateralResistanceSlip = s;}
+ void SetEBrakeEffect(float s) {mDesignerParams.mDpEBrakeEffect = s;}
+
+ void SetTireLateralResistanceSlipWithoutEBrake(float s) {mDesignerParams.mDpTireLateralResistanceSlipNoEBrake = s;}
+ void SetSlipEffectWithoutEBrake(float s) {mDesignerParams.mDpSlipEffectNoEBrake = s;}
+
+ void SetCMOffsetX(float s) {mDesignerParams.mDpCMOffset.x = s;}
+ void SetCMOffsetY(float s) {mDesignerParams.mDpCMOffset.y = s;}
+ void SetCMOffsetZ(float s) {mDesignerParams.mDpCMOffset.z = s;}
+
+ void SetSuspensionLimit(float s) {mDesignerParams.mDpSuspensionLimit = s;}
+ void SetSuspensionSpringK(float s) {mDesignerParams.mDpSpringk = s;}
+ void SetSuspensionDamperC(float s) {mDesignerParams.mDpDamperc = s;}
+
+ void SetSuspensionYOffset(float s) {mDesignerParams.mDpSuspensionYOffset = s;}
+
+ void SetHitPoints(float h) {mDesignerParams.mHitPoints = h;}
+ void SetBurnoutRange(float h) {mDesignerParams.mDpBurnoutRange = h;}
+ void SetMaxSpeedBurstTime(float h) {mDesignerParams.mDpMaxSpeedBurstTime = h;}
+ void SetDonutTorque(float h) {mDesignerParams.mDpDonutTorque = h;}
+
+ void SetWeebleOffset(float w) {mDesignerParams.mDpWeebleOffset = w;}
+
+ void SetWheelieRange(float w) {mDesignerParams.mDpWheelieRange = w;}
+ void SetWheelieYOffset(float y) {mDesignerParams.mDpWheelieYOffset = y;}
+ void SetWheelieZOffset(float z) {mDesignerParams.mDpWheelieZOffset = z;}
+
+ void SetShadowAdjustments( float Adjustments[ 4 ][ 2 ] );
+ void SetShininess( unsigned char EnvRef );
+
+ void CalculateValuesBasedOnDesignerParams();
+
+
+ //------------
+ // wheel stuff
+ //------------
+
+ void UpdateWheelRenderingInfo(float dt);
+ bool IsJointAWheel(int jointIndex);
+ //bool SetWheelCorrectionOffset(int jointNum, float objectSpaceYOffsetFromCurrentPosition);
+ bool SetWheelCorrectionOffset(int jointNum, float objectSpaceYOffsetFromCurrentPosition, rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint);
+
+ void SetNoDamperDownFlagOnWheel(int jointIndex); // joint corresponds to a wheel
+ bool mDamperShouldNotPullDown[4];
+
+ SuspensionJointDriver* mSuspensionJointDrivers[4];
+
+ int mWheelToJointIndexMapping[4];
+ int* mJointIndexToWheelMapping;
+
+ rmt::Vector mSuspensionRestPointsFromFile[4];
+ rmt::Vector mSuspensionRestPoints[4];
+ float mSuspensionRestValue;
+ float mSuspensionMaxValue;
+
+ void CalculateSuspensionLocationAndVelocity();
+ rmt::Vector mSuspensionWorldSpacePoints[4];
+ rmt::Vector mSuspensionPointVelocities[4];
+
+ float mWheelBase;
+ float GetWheelBase() {return mWheelBase;}
+
+ float GetMass()const { return mDesignerParams.mDpMass; }
+
+ float GetRestHeightAboveGround();
+
+ void FetchWheelMapping();
+
+ Wheel* mWheels[4];
+ void InitWheelsAndLinkSuspensionJointDrivers();
+
+ friend class Wheel;
+
+
+ //--------------
+ // damage states
+ //--------------
+
+/*
+
+ here is what damage states have evolved too:
+
+ there will be 3 types of damage states on vehicles:
+
+ 1.
+ On "user" level vehicles there will be:
+
+ normal
+ texture damage (localized front, back, driver side, passenger side)
+ flapping joints
+ [flapping joints breaking off] <--- this might not be done for this milestone (or ever); I see it as very low priority right now
+ smoke level 1
+ smoke level 2
+ smoke level 3
+ disabled
+
+
+ 2.
+ On "ai" level vehicles there will be
+
+ normal
+ texture damage (localized front, back, driver side, passenger side)
+ smoke level 1
+ smoke level 2
+ smoke level 3
+ disabled
+
+
+ 3.
+ On "traffic" there will be
+
+ normal
+ simultaneous BRIEF paricle system Poof! and overall damage texture - disabled.
+
+
+
+
+
+
+*/
+
+ enum VehicleDamageType { VDT_UNSET, VDT_USER, VDT_AI, VDT_TRAFFIC };
+ //int mDamageType; // 1, 2, or 3 - see above.
+ VehicleDamageType mDamageType;
+
+
+ bool IsAFlappingJoint(int index);
+ bool InitFlappingJoints(); // returns true if least 1 flapping joint
+ bool AddFlappingJoint(const char* transJointName, const char* rotJointName, rmt::Vector& axis, rmt::Vector& rotAxis, int count, int* collisionJointIndex);
+
+ sim::PhysicsJointInertialEffector* mInertialJointDrivers[4];
+ int* mJointIndexToInertialJointDriverMapping;
+ // just for easier delete
+ sim::PhysicsJointMatrixModifier* mPhysicsJointMatrixModifiers[4];
+
+ int mDoorDJoint; // recall set to -1 if not present
+ int mDoorPJoint;
+ int mHoodJoint;
+ int mTrunkJoint;
+
+ // logical state of pieces:
+ // 0 undamaged
+ // 1 textured
+ // 2 flapping
+ // 3 gone
+
+ int mDoorDDamageLevel;
+ int mDoorPDamageLevel;
+ int mHoodDamageLevel;
+ int mTrunkDamageLevel;
+
+
+
+ enum DamageLocation{ dl_hood, dl_trunk, dl_driverside, dl_passengerside };
+
+ DamageLocation TranslateCollisionIntoLocation(sim::Collision& inCollision);
+
+ //void DamageJoint(int joint, int damlevel);
+ //void DamageAllJoints();
+
+ //void BreakOffFlappingPiece(int index);
+
+ bool CarOnCarDamageLogic(bool thisIsA, sim::SimState* simStateA, sim::SimState* simStateB);
+ void SwitchOnDamageTypeAndApply(float normalizedMagnitude, sim::Collision& inCollision);
+
+ // =========================================================
+ bool mCollidedWithVehicle; // when we are involved in collision with a vehicle
+ void DusitsStunTest( float normalizedMagnitude );
+
+ // store data about the last thing that hit me
+ bool mOutOfControl;
+ float mNormalizedMagnitudeOfVehicleHit;
+ bool mWasHitByVehicle; // when we try to distinguish who hit who, if somebody hit us, this is true
+ VehicleType mWasHitByVehicleType;
+ rmt::Vector mSwerveNormal; // if somebody hit us, this tells us direction of swerve
+ void TestWhoHitWhom( sim::SimState* simA, sim::SimState* simB, float normalizedMagnitude, const sim::Collision & inCollision );
+ // =========================================================
+
+
+ void VisualDamageType1(float percentageDamage, DamageLocation dl);
+ void VisualDamageType2(float percentageDamage, DamageLocation dl);
+ void VisualDamageType3(float percentageDamage);
+ void SyncVisualDamage( float Health ); // Note that this is the health of the car 0.0f -husk, 1.0f -nice and new.
+
+ rmt::Vector mSmokeOffset;
+ rmt::Vector& GetSmokeOffset() {return mSmokeOffset;}
+ rmt::Vector GetWheel0Offset();
+ rmt::Vector GetWheel1Offset();
+
+ void DebugInflictDamageHood();
+ void DebugInflictDamageBack();
+ void DebugInflictDamageDriverSide();
+ void DebugInflictDamagePassengerSide();
+
+
+ //void TriggerDamage(float amount, int hitJoint);
+ float TriggerDamage(float amount, bool clamp = true); // returns new percentage
+ bool IsVehicleDestroyed()const { return mVehicleDestroyed; }
+
+ bool mVehicleDestroyed;
+ bool mDontShowBrakeLights;
+
+ bool mAlreadyPlayedExplosion;
+
+ float mDamageOutResetTimer;
+
+ float mNoDamageTimer; // to make a clean distinction between the second last and the last hit
+
+
+ float mHitPoints;
+ bool mVehicleCanSustainDamage; // defaults to false
+ void SetVehicleCanSustainDamage(bool trueOrFalse) {mVehicleCanSustainDamage = trueOrFalse;}
+ float GetVehicleLifePercentage(float testvalue);
+
+ bool mIsADestroyObjective;
+ void VehicleIsADestroyObjective(bool b) {mIsADestroyObjective = b;}
+
+
+ void ResetDamageState();
+
+ void BeefUpHitPointsOnTrafficCarsWhenUserDriving();
+
+ //----------
+ // sim model
+ //----------
+
+ sim::SimStateArticulated* mSimStateArticulated;
+ sim::ArticulatedPhysicsObject* mPhObj;
+ sim::PhysicsProperties* mPhysicsProperties;
+
+ // new - swap in model for character to jump on top of
+ sim::SimStateArticulated* mSimStateArticulatedOutOfCar;
+
+ sim::SimStateArticulated* mSimStateArticulatedInCar;
+
+ bool mUsingInCarPhysics;
+ void SetInCarSimState();
+ void SetOutOfCarSimState();
+
+ //bool mWaitingToSwitchToOutOfCar;
+ //float mOutOfCarSwitchTimer; // brutal fucking hack
+
+ void InitSimState(sim::SimEnvironment* se);
+ void SetupPhysicsProperties();
+
+ void InitGroundPlane();
+ sim::ManualSimState* mGroundPlaneSimState;
+ sim::WallVolume* mGroundPlaneWallVolume;
+ sim::PhysicsProperties* mGroundPlanePhysicsProperties;
+
+
+ void RestTest(void);
+ bool SelfRestTest(void);
+ bool mAtRestAsFarAsTriggersAreConcerned;
+ void ZeroOutXZVelocity();
+
+ bool mCreatedByParkedCarManager;
+
+ void CreatePoseEngine();
+ poser::PoseEngine* mPoseEngine;
+ RootMatrixDriver* mRootMatrixDriver;
+
+ //void CreatePoseEngineOutOfCar();
+ //poser::PoseEngine* mPoseEngineOutOfCar;
+ //RootMatrixDriver* mRootMatrixDriverOutOfCar;
+
+
+ float mDragCoeff; // TODO - do we want regular, quadratic drag?
+ void CalculateDragCoeffBasedOnTopSpeed();
+
+ float mRollingFrictionForce;
+ float mTireLateralResistance;
+ float mSlipGasModifier;
+
+ float mCollisionLateralResistanceDropFactor;
+
+ //bool mOkToCrashLand;
+ //-----------
+ // other crap
+ //-----------
+
+ bool mBottomedOutThisFrame; // for debug rendering
+ bool mWasAirborn; // used to help decide when to fire a bottomed out event for esan
+ float mWasAirbornTimer;
+
+ float mBottomOutSpeedMaintenance;
+
+ float mStuckOnSideTimer;
+
+ bool mDrawWireFrame; // also debug rendering
+ bool mLosingTractionDueToAccel; // for plum
+
+ void SetupRadDebugWatchStuff();
+
+ // unfortunately vehicle will now have a dependency on this shyte, but
+ // I still think this is the cleanest way to do it.
+ RectTriggerVolume* mpTriggerVolume;
+ EventLocator* mpEventLocator;
+ bool mTriggerActive;
+ void InitEventLocator();
+
+ int mDriverInit;
+
+ bool mHijackedByUser;
+
+ // Attach a tDrawable collectible to the vehicle
+ // returns true if attached or false if not ( false happens
+ // when a collectible is already attached to the vehicle)
+ bool AttachCollectible( StatePropCollectible* );
+ StatePropCollectible* GetAttachedCollectible();
+ // The collectible is detached and free to move around the world under physics control
+ void DetachCollectible( const rmt::Vector& velocity, bool explode = true );
+ float mForceToDetachCollectible;
+
+ int mCharacterSheetCarIndex;
+
+ //----------------------
+ // door opening/closing
+ //----------------------
+public:
+ enum Door
+ {
+ DOOR_DRIVER,
+ DOOR_PASSENGER
+ };
+
+ enum DoorAction
+ {
+ DOORACTION_NONE,
+ DOORACTION_OPEN,
+ DOORACTION_CLOSE
+ };
+
+ // move the door to the given "position'
+ // positions are in 0 to 1, DoorAction should be open or close, not none
+ void MoveDoor(Door, DoorAction, float position);
+
+ // do we actually need to open or close the door (test's physics state, current position, etc)
+ bool NeedToOpenDoor(Door); // returns false if door is already open
+ bool NeedToCloseDoor(Door); // returns false if door is already closed
+ bool HasActiveDoor(Door); // is there and active door (i.e. exists, and isn't flapping)
+
+ void UpdateDoorState(void);
+ void ReleaseDoors(void);
+
+ void PlayExplosionEffect();
+
+ virtual void AddToSimulation();
+ virtual void ApplyForce( const rmt::Vector& direction, float force );
+
+ bool mHasDoors : 1;
+ bool mVisibleCharacters : 1;
+ bool mIrisTransition : 1;
+ bool mAllowSlide : 1;
+ bool mHighRoof : 1;
+ float mCharacterScale;
+
+ // Calculates the s_ForceToDamage variable when given
+ // the number of hitpoints that should be removed when a vehicle explodes
+ static void SetPercentDamageFromExplosion( float percent );
+ // Calculates the s_ForceToDamagePlayer variable when given
+ // the number of hitpoints that should be removed when a vehicle explodes
+ static void SetPercentDamageFromExplosionPlayer( float percent );
+
+private:
+
+ float mDesiredDoorPosition[2];
+ DoorAction mDesiredDoorAction[2];
+
+ void CalcDoor(unsigned index, unsigned joint, float scale);
+ void CalcDoors(void);
+
+ //----------------------
+
+private:
+ rmt::Vector mPassengerLocation;
+ rmt::Vector mDriverLocation;
+ rmt::Vector mExtents;
+ bool m_IsSimpleShadow;
+
+
+ // The number of hitpoints that are removed (via TriggerDamage)
+ //
+ static float s_DamageFromExplosion;
+ static float s_DamageFromExplosionPlayer;
+};
+
+
+#endif // _VEHICLE_H
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.cpp b/game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.cpp
new file mode 100644
index 0000000..d452954
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.cpp
@@ -0,0 +1,167 @@
+#include <worldsim/redbrick/vehiclecontroller/aivehiclecontroller.h>
+#include <main/game.h>
+#include <memory/classsizetracker.h>
+#include <radmath/radmath.hpp>
+
+class Behaviour
+{
+public:
+ virtual rmt::Vector Tick( Vehicle* pVehicle ) = 0;
+};
+
+class VehicleWander
+:
+public Behaviour
+{
+public:
+
+ VehicleWander( void );
+ virtual ~VehicleWander( void );
+
+ rmt::Vector& GetTargetPoint( void );
+ void SetTargetPoint( rmt::Vector& targetPoint );
+
+ rmt::Vector& GetPosition( void );
+ void SetPosition( rmt::Vector& position );
+
+ float GetTargetRadius( void ) const;
+ void SetTargetRadius( float radius );
+
+ float GetSteeringRadius( void ) const;
+ void SetSteeringRadius( float radius );
+
+ rmt::Vector Tick( Vehicle* pVehicle );
+private:
+ rmt::Vector mTargetPoint;
+ rmt::Vector mPosition;
+ float mfTargetCircleRadius;
+ float mfSteeringCircleRadius;
+};
+
+VehicleWander::VehicleWander( void )
+:
+mTargetPoint( 0.0f, 0.0f, 1.0f ),
+mPosition( 0.0f, 0.0f, 0.1f ),
+mfTargetCircleRadius( 1.0f ),
+mfSteeringCircleRadius( 0.003f )
+{
+ mTargetPoint.z = mfTargetCircleRadius;
+}
+
+VehicleWander::~VehicleWander( void )
+{
+}
+
+rmt::Vector VehicleWander::Tick( Vehicle* pVehicle )
+{
+ static rmt::Randomizer r( Game::GetRandomSeed () );
+ static rmt::Vector point( 0.0f, 0.0f, 0.0f );
+
+ point.x = r.FloatSigned( );
+ point.y = 0.0f;
+ point.z = r.FloatSigned( );
+
+ // Make a point on the new circle.
+ //
+ point.Normalize( );
+ point.Scale( mfSteeringCircleRadius );
+
+ // Add to point on circle.
+ //
+ mTargetPoint.Add( point );
+ // Project back to unit circle.
+ //
+ mTargetPoint.Normalize( );
+ // Scale to actual Target Circle.
+ //
+ mTargetPoint.Scale( mfTargetCircleRadius );
+
+ rmt::Vector retVector;
+ retVector.Add( mPosition, mTargetPoint );
+ retVector.Normalize( );
+
+ return retVector;
+}
+
+AiVehicleController::AiVehicleController( Vehicle* pVehicle )
+{
+ //CLASSTRACKER_CREATE( AiVehicleController );
+ SetVehicle( pVehicle );
+}
+
+AiVehicleController::~AiVehicleController( void )
+{
+ //CLASSTRACKER_DESTROY( AiVehicleController );
+}
+
+void AiVehicleController::Update( float timeins )
+{
+ /*
+ static Behaviour* spBehaviour = 0;
+ if ( !spBehaviour )
+ {
+ spBehaviour = new VehicleWander;
+ }
+ rmt::Vector direction = spBehaviour->Tick( GetVehicle( ) );
+ mSteering.SetValue( direction.x );
+ mGas.SetValue( direction.z );
+ */
+ mHandBrake.SetValue( 1.0f );
+
+ mGas.SetValue( 0.0f );
+ mSteering.SetValue( 0.0f );
+ mBrake.SetValue( 0.0f );
+ mReverse.SetValue( 0.0f );
+ mHorn.SetValue( 0.0f );
+
+}
+
+float AiVehicleController::GetGas( void ) const
+{
+ // ai shoudln't really use this?
+ return mGas.GetValue();
+}
+
+float AiVehicleController::GetThrottle( void ) const
+{
+ return mGas.GetValue();
+}
+
+float AiVehicleController::GetBrake( void ) const
+{
+ return mBrake.GetValue();
+}
+
+float AiVehicleController::GetSteering( bool& isWheel ) const
+{
+ isWheel = false;
+ return mSteering.GetValue();
+}
+
+
+// AI shouldn't really call these
+float AiVehicleController::GetSteerLeft( void ) const
+{
+ return 0.0f;
+}
+
+
+float AiVehicleController::GetSteerRight( void ) const
+{
+ return 0.0f;
+}
+
+float AiVehicleController::GetHandBrake( void ) const
+{
+ return mHandBrake.GetValue();
+}
+
+float AiVehicleController::GetReverse( void ) const
+{
+ return mReverse.GetValue();
+}
+
+float AiVehicleController::GetHorn( void ) const
+{
+ return mHorn.GetValue();
+} \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.h b/game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.h
new file mode 100644
index 0000000..2f2a463
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/aivehiclecontroller.h
@@ -0,0 +1,40 @@
+#ifndef AIVEHICLECONTROLLER_H_
+#define AIVEHICLECONTROLLER_H_
+
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h>
+#include <input/button.h>
+
+class Vehicle;
+
+class AiVehicleController
+:
+public VehicleController
+{
+public:
+ AiVehicleController( Vehicle* pVehicle );
+ virtual ~AiVehicleController( void );
+
+ virtual void Update( float timeins );
+
+ virtual float GetGas( void ) const;
+ virtual float GetThrottle( void ) const;
+ virtual float GetBrake( void ) const;
+ virtual float GetSteering( bool& isWheel ) const;
+
+ virtual float GetSteerLeft( void ) const;
+ virtual float GetSteerRight( void ) const;
+
+ virtual float GetHandBrake( void ) const;
+ virtual float GetReverse( void ) const;
+ virtual float GetHorn( void ) const;
+protected:
+ Button mGas;
+ Button mBrake;
+ Button mSteering;
+ Button mHandBrake;
+ Button mReverse;
+ Button mHorn;
+private:
+};
+
+#endif //AIVEHICLECONTROLLER_H_ \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/allvehiclecontroller.cpp b/game/code/worldsim/redbrick/vehiclecontroller/allvehiclecontroller.cpp
new file mode 100644
index 0000000..517da35
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/allvehiclecontroller.cpp
@@ -0,0 +1,4 @@
+#include <worldsim/redbrick/vehiclecontroller/aivehiclecontroller.cpp>
+#include <worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.cpp>
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.cpp>
+#include <worldsim/redbrick/vehiclecontroller/vehiclemappable.cpp>
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.cpp b/game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.cpp
new file mode 100644
index 0000000..3e9ae01
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.cpp
@@ -0,0 +1,807 @@
+#include <worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h>
+#include <worldsim/redbrick/vehiclecontroller/vehiclemappable.h>
+#include <worldsim/redbrick/vehiclecontroller/../vehicle.h>
+#include <presentation/gui/guisystem.h>
+#include <raddebugwatch.hpp>
+
+#include <input/inputmanager.h>
+#include <events/eventmanager.h>
+#include <events/eventdata.h>
+
+#include <main/commandlineoptions.h>
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+#include <input/basedamper.h>
+#include <input/steeringspring.h>
+#include <input/constanteffect.h>
+#include <input/wheelrumble.h>
+#endif
+
+#ifdef RAD_WIN32
+const short DEFAULT_SPRING_COEFF = 3000;
+#else
+const short DEFAULT_SPRING_COEFF = 50;
+#endif
+const char DEFAULT_SPRING_OFFSET = 0;
+const unsigned char DEFAULT_SPRING_DEADBAND = 0;
+const unsigned char DEFAULT_SPRING_SAT = 0;
+
+const char DEFAULT_DAMPER_OFFSET = 0;
+const unsigned char DEFAULT_DAMPER_DEADBAND = 5;
+const unsigned char DEFAULT_DAMPER_SAT = 0;
+const short DEFAULT_DAMPER_COEFF = 0;
+
+const short DEFAULT_CONSTANT_MAGNITUDE = 0;
+const unsigned short DEFAULT_CONSTANT_DIRECTION = 0;
+
+#ifdef DEBUGWATCH
+short gDamperSlip = -100;
+short gDamperMax = 100;
+float gDamperSpeed = 200.0f;
+
+unsigned char gSpringSlip = 20;
+unsigned char gSpringMax = 200;
+float gSpringSpeed = 80.0f;
+#else
+const float gDamperSpeed = 200.0f;
+
+#ifdef RAD_WIN32
+const u16 gSpringMax = 1000;
+const unsigned char gSpringSlip = 20;
+const short gDamperMax = 100;
+const short gDamperSlip = -600;
+#else
+const unsigned char gSpringMax = 200;
+const unsigned char gSpringSlip = 20;
+const short gDamperMax = 100;
+const short gDamperSlip = -100;
+#endif
+
+const float gSpringSpeed = 80.0f;
+
+const short gConstantMag = 0;
+const unsigned short gConstantDirection = 0;
+
+#endif
+
+#ifdef RAD_GAMECUBE
+const float GC_STEERING_FUDGE = 1.25f;
+#endif
+
+HumanVehicleController::HumanVehicleController( void )
+:
+mpMappable( 0 ),
+mControllerId( -1 ),
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+mSpring( NULL ),
+mDamper( NULL ),
+mConstantEffect( NULL ),
+mWheelRumble( NULL ),
+mHeavyWheelRumble( NULL ),
+#endif
+mDisableReset( false )
+{
+#ifdef RAD_PS2
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ mpWheel[ i ] = NULL;
+ }
+#endif
+}
+
+void HumanVehicleController::Create( Vehicle* pVehicle, VehicleMappable* pMappable, int controllerId )
+{
+ SetVehicle( pVehicle );
+ tRefCounted::Assign( mpMappable, pMappable );
+ mControllerId = controllerId;
+}
+
+#ifdef RAD_PS2
+void HumanVehicleController::SetWheel( VehicleMappable* pMappable, unsigned int wheelNum )
+{
+ rAssert( wheelNum < Input::MaxUSB );
+
+ tRefCounted::Assign( mpWheel[ wheelNum ], pMappable );
+}
+#endif
+
+HumanVehicleController::~HumanVehicleController( void )
+{
+ ReleaseVehicleMappable();
+
+#ifdef RAD_PS2
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ if ( mpWheel[ i ] )
+ {
+ mpWheel[ i ]->Release();
+ mpWheel[ i ] = NULL;
+ }
+ }
+#endif
+}
+
+void HumanVehicleController::ReleaseVehicleMappable( void )
+{
+ if ( mpMappable )
+ {
+ mpMappable->Release();
+ mpMappable = 0;
+ }
+}
+
+
+float HumanVehicleController::GetGas( void ) const
+{
+#ifdef RAD_PS2
+ float value = mpMappable->GetButton( VehicleMappable::Gas )->GetValue();
+
+ if ( mControllerId >= Input::USB0 )
+ {
+ return value;
+ }
+
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ if ( mpWheel[ i ] )
+ {
+ float tempVal = mpWheel[ i ]->GetButton( VehicleMappable::Gas )->GetValue();
+ if ( rmt::Fabs( tempVal ) > rmt::Fabs( value ) )
+ {
+ value = tempVal;
+ break;
+ }
+ }
+ }
+
+ return value;
+#else
+ return mpMappable->GetButton( VehicleMappable::Gas )->GetValue();
+#endif
+}
+
+float HumanVehicleController::GetThrottle( void ) const
+{
+ //No throttle on the wheels.
+ return mpMappable->GetButton( VehicleMappable::Throttle )->GetValue();
+}
+
+
+float HumanVehicleController::GetBrake( void ) const
+{
+#ifdef RAD_PS2
+ float value = mpMappable->GetButton( VehicleMappable::Brake )->GetValue();
+
+ if ( mControllerId >= Input::USB0 )
+ {
+ return value;
+ }
+
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ if ( mpWheel[ i ] )
+ {
+ float tempVal = mpWheel[ i ]->GetButton( VehicleMappable::Brake )->GetValue();
+ if ( rmt::Fabs( tempVal ) > rmt::Fabs( value ) )
+ {
+ value = tempVal;
+ break;
+ }
+ }
+ }
+
+ return value;
+#else
+ return mpMappable->GetButton( VehicleMappable::Brake )->GetValue();
+#endif
+}
+
+float HumanVehicleController::GetSteering( bool& isWheel ) const
+{
+#ifdef RAD_PS2
+ isWheel = false;
+
+ float value = mpMappable->GetButton( VehicleMappable::Steer )->GetValue();
+
+ if ( mControllerId >= Input::USB0 )
+ {
+ isWheel = true;
+ return value;
+ }
+
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ if ( mpWheel[ i ] )
+ {
+ float tempVal = mpWheel[ i ]->GetButton( VehicleMappable::Steer )->GetValue();
+ if ( rmt::Fabs( tempVal ) > rmt::Fabs( value ) )
+ {
+ value = tempVal;
+ isWheel = true;
+ break;
+ }
+ }
+ }
+
+ return value;
+#else
+ //How do I tell if this is from a wheel or a stick?
+#ifdef RAD_GAMECUBE
+ //This is cheating.
+ isWheel = mpMappable->IsWheel();
+ return rmt::Clamp( mpMappable->GetButton( VehicleMappable::Steer )->GetValue() * GC_STEERING_FUDGE, -1.0f, 1.0f );
+#else
+#ifdef RAD_WIN32
+ isWheel = GetInputManager()->GetController(mControllerId)->IsWheel();
+#endif
+ return mpMappable->GetButton( VehicleMappable::Steer )->GetValue();
+#endif
+
+#endif
+}
+
+float HumanVehicleController::GetSteerLeft( void ) const
+{
+ //No steerleft on the wheels
+ return mpMappable->GetButton( VehicleMappable::SteerLeft )->GetValue();
+}
+
+float HumanVehicleController::GetSteerRight( void ) const
+{
+ //No steer right on the wheels
+ return mpMappable->GetButton( VehicleMappable::SteerRight )->GetValue();
+}
+
+float HumanVehicleController::GetReverse( void ) const
+{
+ //No reverse mapped.
+ return 0.0f;
+}
+
+
+float HumanVehicleController::GetHandBrake( void ) const
+{
+#ifdef RAD_PS2
+ float value = mpMappable->GetButton( VehicleMappable::HandBrake )->GetValue();
+
+ if ( mControllerId >= Input::USB0 )
+ {
+ return value;
+ }
+
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ if ( mpWheel[ i ] )
+ {
+ float tempVal = mpWheel[ i ]->GetButton( VehicleMappable::HandBrake )->GetValue();
+ if ( rmt::Fabs( tempVal ) > rmt::Fabs( value ) )
+ {
+ value = tempVal;
+ break;
+ }
+ }
+ }
+
+ return value;
+#else
+ return mpMappable->GetButton( VehicleMappable::HandBrake )->GetValue();
+#endif
+}
+
+float HumanVehicleController::GetHorn( void ) const
+{
+#ifdef RAD_PS2
+ float value = mpMappable->GetButton( VehicleMappable::Horn )->GetValue();
+
+ if ( mControllerId >= Input::USB0 )
+ {
+ return value;
+ }
+
+ unsigned int i;
+ for ( i = 0; i < Input::MaxUSB; ++i )
+ {
+ if ( mpWheel[ i ] )
+ {
+ float tempVal = mpWheel[ i ]->GetButton( VehicleMappable::Horn )->GetValue();
+ if ( rmt::Fabs( tempVal ) > rmt::Fabs( value ) )
+ {
+ value = tempVal;
+ break;
+ }
+ }
+ }
+
+ return value;
+#else
+ return mpMappable->GetButton( VehicleMappable::Horn )->GetValue();
+#endif
+}
+
+void HumanVehicleController::Reset( void )
+{
+ if( !mDisableReset )
+ {
+ Vehicle* vehicle = GetVehicle();
+ GetGuiSystem()->HandleMessage( GUI_MSG_MANUAL_RESET, reinterpret_cast< unsigned int >( vehicle ) );
+ }
+}
+
+/*
+==============================================================================
+HumanVehicleController::GetMappable
+==============================================================================
+Description: Comment
+
+Parameters: ( void )
+
+Return: VehicleMappable
+
+=============================================================================
+*/
+VehicleMappable *HumanVehicleController::GetMappable( void ) const
+{
+ return mpMappable;
+}
+
+//=============================================================================
+// HumanVehicleController::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( float timeins )
+//
+// Return: void
+//
+//=============================================================================
+void HumanVehicleController::Update( float timeins )
+{
+ //This is where the output points will be set.
+
+ Vehicle* vehicle = GetVehicle();
+ float speed = vehicle->GetSpeedKmh();
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ UserController* uc = NULL;
+
+ //Set up the output points to default settings.
+#ifdef RAD_PS2
+ //TODO: Make this only set up the active wheel.
+ if ( mpWheel[ 0 ] != NULL )
+ {
+ uc = GetInputManager()->GetController( Input::USB0 );
+ }
+ else if ( mpWheel[ 1 ] != NULL )
+ {
+ uc = GetInputManager()->GetController( Input::USB1 );
+ }
+ else
+ {
+ uc = GetInputManager()->GetController( mControllerId );
+ }
+#elif defined RAD_GAMECUBE || defined(RAD_WIN32)
+ uc = GetInputManager()->GetController( mControllerId );
+#endif
+
+ SetupRumbleFeatures( uc );
+
+ if ( mSpring || mDamper || mConstantEffect )
+ {
+ rAssert( mSpring && mDamper );
+ //Update these effects.
+
+ if ( vehicle->IsAirborn() ||
+ vehicle->mVehicleState == VS_SLIP ||
+ vehicle->mVehicleState == VS_EBRAKE_SLIP )
+ {
+#ifdef RAD_WIN32
+ if( vehicle->IsAirborn() )
+ {
+ mSpring->SetSpringStrength( 0 );
+ mSpring->SetSpringCoefficient( 0 );
+
+ mDamper->SetDamperCoefficient( 0 );
+ mDamper->SetDamperStrength( 0 );
+ }
+ else
+ {
+ mDamper->SetDamperCoefficient( gDamperSlip );
+ mDamper->SetDamperStrength( 255 );
+
+ mSpring->SetSpringStrength( static_cast<u16>(50*speed) );
+ mSpring->SetSpringCoefficient( gDamperSlip*20 );
+
+ mConstantEffect->SetDirection( static_cast<u16>(90*speed) );
+ mConstantEffect->SetMagnitude( static_cast<s16>(-200*speed) );
+ }
+#else
+ //Kill the damper
+ mDamper->SetDamperCoefficient( gDamperSlip );
+ mDamper->SetDamperStrength( 0 );
+
+
+ mSpring->SetSpringStrength( gSpringSlip );
+ mSpring->SetSpringCoefficient( gDamperSlip );
+#endif
+ }
+ else
+ {
+
+ if ( speed > gSpringSpeed )
+ {
+ speed = gSpringSpeed;
+ }
+
+#ifdef RAD_WIN32
+ u16 springSat = static_cast<u16>(rmt::FtoL(gSpringMax * ( speed / gSpringSpeed )));
+ if( speed < 5.0f )
+ {
+ if( rmt::Epsilon( 0.0f, speed, 0.05f ) ) springSat = gSpringMax;
+ else springSat = static_cast<u16>((gSpringMax)/(speed));
+ }
+ springSat*=50;
+
+#else
+ unsigned char springSat = static_cast<unsigned char>(rmt::FtoL(gSpringMax * ( speed / gSpringSpeed )));
+#endif
+ mSpring->SetSpringStrength( springSat );
+ mSpring->SetSpringCoefficient( DEFAULT_SPRING_COEFF );
+
+ if ( speed > gDamperSpeed )
+ {
+ speed = gDamperSpeed;
+ }
+
+ short dampercoeff = gDamperMax - static_cast<short>(rmt::FtoL(rmt::LtoF(gDamperMax) * ( speed / gDamperSpeed )));
+ mDamper->SetDamperCoefficient( dampercoeff );
+ mDamper->SetDamperStrength( DEFAULT_DAMPER_SAT );
+ }
+ }
+#endif
+ //Update the rumble effect.
+ switch ( vehicle->mTerrainType )
+ {
+ case TT_Grass: // Grass type terrain most everything else which isn't road or sidewalk.
+ case TT_Dirt: // Dirt type terrain.
+ {
+ if ( speed > 40.0f ) //Hmmmm... TODO: allow this to be modified
+ {
+ GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::GROUND2, 250 );
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ if ( mWheelRumble )
+ {
+// mWheelRumble->SetMagDir( 200, 90 );
+// mWheelRumble->SetPPO( 120 - (80 * (speed / 80.0f)), 0, 0 );
+ }
+#endif
+ }
+ }
+ case TT_Water: // Water on surface type terrain.
+ case TT_Gravel: // Loose gravel type terrain.
+ {
+ if ( speed > 40.0f ) //Hmmmm... TODO: allow this to be modified
+ {
+ GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::GROUND4, 250 );
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ if ( mWheelRumble )
+ {
+// mWheelRumble->SetMagDir( 200, 90 );
+// mWheelRumble->SetPPO( 120 - (60 * (speed / 80.0f)), 0, 0 );
+ }
+#endif
+ }
+ }
+ case TT_Sand: // Sand type terrain.
+ {
+ if ( speed > 40.0f ) //Hmmmm... TODO: allow this to be modified
+ {
+ GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::GROUND3, 250 );
+ }
+ }
+ case TT_Metal: // Powerplant and other structurs.
+ {
+ if ( speed > 40.0f ) //Hmmmm... TODO: allow this to be modified
+ {
+ GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::GROUND2, 250 );
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ if ( mWheelRumble )
+ {
+// mWheelRumble->SetMagDir( 255, 90 );
+// mWheelRumble->SetPPO( 120 - (60 * (speed / 80.0f)), 0, 0 );
+ }
+#endif
+ }
+ }
+ case TT_Road:
+ case TT_Wood:
+ default:
+ {
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) //|| defined(RAD_WIN32)
+ if ( mWheelRumble )
+ {
+ mWheelRumble->SetMagDir( 0, 0 );
+ mWheelRumble->SetPPO( 0, 0, 0 );
+ }
+#endif
+ break;
+ }
+ }
+
+// if ( vehicle->mSkidLevel > 0.0f )
+// {
+// GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::PEELOUT, 250 );
+// }
+}
+
+//=============================================================================
+// HumanVehicleController::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void HumanVehicleController::Init()
+{
+ if ( mControllerId == -1 )
+ {
+ return;
+ }
+
+ UserController* uc = NULL;
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ //Set up the output points to default settings.
+#ifdef RAD_PS2
+ //TODO: Make this only set up the active wheel.
+ if ( mpWheel[ 0 ] != NULL )
+ {
+ uc = GetInputManager()->GetController( Input::USB0 );
+ }
+ else if ( mpWheel[ 1 ] != NULL )
+ {
+ uc = GetInputManager()->GetController( Input::USB1 );
+ }
+ else
+ {
+ uc = GetInputManager()->GetController( mControllerId );
+ }
+#elif defined RAD_GAMECUBE || defined(RAD_WIN32)
+ uc = GetInputManager()->GetController( mControllerId );
+#endif
+
+ if ( uc )
+ {
+ SetupRumbleFeatures( uc );
+ }
+#endif
+
+ GetEventManager()->AddListener( this, EVENT_BIG_CRASH );
+ GetEventManager()->AddListener( this, EVENT_BIG_VEHICLE_CRASH );
+ GetEventManager()->AddListener( this, EVENT_MINOR_CRASH );
+ GetEventManager()->AddListener( this, EVENT_MINOR_VEHICLE_CRASH );
+ GetEventManager()->AddListener( this, EVENT_RUMBLE_COLLISION );
+}
+
+//=============================================================================
+// HumanVehicleController::Shutdown
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void HumanVehicleController::Shutdown()
+{
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ //Stop the vehicle output point settings
+
+ if ( mSpring )
+ {
+ mSpring = NULL;
+ }
+
+ if ( mDamper )
+ {
+ mDamper = NULL;
+ }
+
+ if ( mConstantEffect )
+ {
+ mConstantEffect = NULL;
+ }
+
+#endif
+
+ GetEventManager()->RemoveAll( this );
+}
+
+//=============================================================================
+// HumanVehicleController::HandleEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( EventEnum id, void* pEventData )
+//
+// Return: void
+//
+//=============================================================================
+void HumanVehicleController::HandleEvent( EventEnum id, void* pEventData )
+{
+ Vehicle* vehicle = GetVehicle();
+ float speed = vehicle->GetSpeedKmh();
+ switch ( id )
+ {
+ case EVENT_BIG_CRASH:
+ case EVENT_BIG_VEHICLE_CRASH:
+ {
+#if defined(RAD_PS2) || defined(RAD_GAMECUBE) || defined(RAD_WIN32)
+ if ( mHeavyWheelRumble )
+ {
+#ifdef RAD_WIN32
+ mHeavyWheelRumble->SetMagDir( static_cast<u16>(speed*20), 90 );
+ mHeavyWheelRumble->SetPPO( 20, 0, 0 );
+ if( mWheelRumble )
+ {
+ mWheelRumble->SetMagDir( static_cast<u16>(speed*20), 90 );
+ mWheelRumble->SetPPO( 20, 0, 0 );
+ }
+#else
+ mHeavyWheelRumble->SetMagDir( 180, 90 );
+ mHeavyWheelRumble->SetPPO( 20, 0, 0 );
+#endif
+ }
+#endif
+// GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::HARD1, 250 );
+// GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::HARD2, 250 );
+ break;
+ }
+ case EVENT_MINOR_CRASH:
+ case EVENT_MINOR_VEHICLE_CRASH:
+ {
+#ifdef RAD_WIN32
+ if ( mWheelRumble )
+ {
+ mWheelRumble->SetMagDir( 100, 90 );
+ mWheelRumble->SetPPO( 20, 0, 0 );
+ }
+#endif
+// GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::LIGHT, 250 );
+// GetInputManager()->GetController( mControllerId )->ApplyEffect( RumbleEffect::HARD2, 250 );
+ break;
+ }
+ case EVENT_RUMBLE_COLLISION:
+ {
+ RumbleCollision* rc = static_cast<RumbleCollision*>(pEventData);
+ if ( rc->vehicle == GetVehicle() && mControllerId != -1 && rc->vehicle->mSpeedKmh > 1.0f )
+ {
+ GetInputManager()->GetController( mControllerId )->ApplyDynaEffect( RumbleEffect::COLLISION1, 333, rc->normalizedForce );
+ GetInputManager()->GetController( mControllerId )->ApplyDynaEffect( RumbleEffect::COLLISION2, 333, rc->normalizedForce );
+
+#if defined(RAD_PS2) || defined(RAD_GAMECUBE) || defined(RAD_WIN32)
+ if ( mConstantEffect )
+ {
+ if ( mWheelRumble && rc->normalizedForce > 0.02f )
+ {
+ rmt::Vector thisPosition;
+ GetVehicle()->GetPosition( &thisPosition );
+
+ rmt::Vector X( 1.0f, 0.0f, 0.0f );
+ rmt::Matrix mat = GetVehicle()->GetTransform();
+ mat.IdentityTranslation();
+
+ X.Transform( mat );
+
+ rmt::Vector carToPoint;
+ carToPoint.Sub( rc->point, thisPosition );
+ carToPoint.NormalizeSafe();
+
+ float dot = carToPoint.DotProduct( X );
+ bool left = ( dot < 0.0f );
+ u16 direction = rmt::FtoL( rmt::Fabs(dot) * 90.0f );
+ if ( left )
+ {
+ direction = (90 + 180) + ( 90 - (direction) );
+ }
+
+ mConstantEffect->SetDirection( direction );
+ mConstantEffect->SetMagnitude( 200 );
+ }
+ }
+
+ if ( mWheelRumble && rc->normalizedForce > 0.008f )
+ {
+ mWheelRumble->SetMagDir( 180, 90 );
+ mWheelRumble->SetPPO( static_cast<u16>( rmt::FtoL(rmt::Clamp( rc->normalizedForce, 0.5f, 1.0f ) * 200.0f ) ),
+ 0, 0 );
+ }
+#endif
+ }
+ }
+ default:
+ break;
+ }
+}
+
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+//=============================================================================
+// HumanVehicleController::SetupRumbleFeatures
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( UserController* uc )
+//
+// Return: void
+//
+//=============================================================================
+void HumanVehicleController::SetupRumbleFeatures( UserController* uc )
+{
+ if ( !mSpring )
+ {
+ mSpring = uc->GetSpring();
+ if ( mSpring )
+ {
+ //Set default settings for the spring
+ mSpring->SetCenterPoint( DEFAULT_SPRING_OFFSET, DEFAULT_SPRING_DEADBAND );
+ mSpring->SetSpringCoefficient( DEFAULT_SPRING_COEFF );
+ mSpring->SetSpringStrength( DEFAULT_SPRING_SAT );
+ }
+ }
+
+ if ( !mDamper )
+ {
+ mDamper = uc->GetDamper();
+ if ( mDamper )
+ {
+ //Set default settings for the damper
+ mDamper->SetCenterPoint( DEFAULT_SPRING_OFFSET, DEFAULT_SPRING_DEADBAND );
+ mDamper->SetDamperCoefficient( DEFAULT_SPRING_COEFF );
+ mDamper->SetDamperStrength( DEFAULT_SPRING_SAT );
+ }
+ }
+
+ if ( !mConstantEffect )
+ {
+ mConstantEffect = uc->GetConstantEffect();
+ if ( mConstantEffect )
+ {
+ //Default
+ mConstantEffect->SetMagnitude( DEFAULT_CONSTANT_MAGNITUDE );
+ mConstantEffect->SetDirection( DEFAULT_CONSTANT_DIRECTION );
+ }
+ }
+
+ if ( !mWheelRumble )
+ {
+ mWheelRumble = uc->GetWheelRumble();
+ if ( mWheelRumble )
+ {
+ mWheelRumble->SetMagDir( 0, 0 );
+ mWheelRumble->SetPPO( 0, 0, 0 );
+ }
+ }
+
+ if ( !mHeavyWheelRumble )
+ {
+ mHeavyWheelRumble = uc->GetHeavyWheelRumble();
+ if ( mHeavyWheelRumble )
+ {
+ mHeavyWheelRumble->SetMagDir( 0, 0 );
+ mHeavyWheelRumble->SetPPO( 0, 0, 0 );
+ }
+ }
+}
+#endif
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h b/game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h
new file mode 100644
index 0000000..8e7feb8
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h
@@ -0,0 +1,78 @@
+#ifndef HUMANVEHICLECONTROLLER_H_
+#define HUMANVEHICLECONTROLLER_H_
+
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h>
+
+#include <input/controller.h>
+#include <events/eventlistener.h>
+
+class VehicleMappable;
+class BaseDamper;
+class SteeringSpring;
+class ConstantEffect;
+class WheelRumble;
+class UserController;
+
+class HumanVehicleController
+:
+public VehicleController,
+public EventListener
+{
+public:
+ HumanVehicleController( void );
+ virtual ~HumanVehicleController( void );
+
+ void ReleaseVehicleMappable( void );
+
+ void Create( Vehicle* pVehicle, VehicleMappable* pMappable, int controllerId );
+#ifdef RAD_PS2
+ void SetWheel( VehicleMappable* pMappable, unsigned int wheelNum );
+#endif
+ virtual float GetGas( void ) const;
+ virtual float GetThrottle( void ) const;
+ virtual float GetBrake( void ) const;
+ virtual float GetSteering( bool& isWheel ) const;
+
+ // for dpad
+ virtual float GetSteerLeft( void ) const;
+ virtual float GetSteerRight( void ) const;
+
+ virtual float GetHandBrake( void ) const;
+ virtual float GetReverse( void ) const;
+ virtual float GetHorn( void ) const;
+
+ void Reset( void );
+ void SetDisableReset( bool tf ) { mDisableReset = tf; }
+
+ VehicleMappable* GetMappable( void ) const;
+
+ virtual void Update( float timeins );
+ virtual void Init();
+ virtual void Shutdown();
+
+ void HandleEvent( EventEnum id, void* pEventData );
+
+protected:
+private:
+
+ VehicleMappable* mpMappable;
+
+#ifdef RAD_PS2
+ VehicleMappable* mpWheel[ Input::MaxUSB ];
+#endif
+
+ int mControllerId;
+
+#if defined(RAD_GAMECUBE) || defined(RAD_PS2) || defined(RAD_WIN32)
+ SteeringSpring* mSpring;
+ BaseDamper* mDamper;
+ ConstantEffect* mConstantEffect;
+ WheelRumble* mWheelRumble;
+ WheelRumble* mHeavyWheelRumble;
+
+ void SetupRumbleFeatures( UserController* uc );
+#endif
+ bool mDisableReset;
+};
+
+#endif //HUMANVEHICLECONTROLLER_H_ \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.cpp b/game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.cpp
new file mode 100644
index 0000000..87942e2
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.cpp
@@ -0,0 +1,16 @@
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h>
+
+VehicleController::VehicleController( void )
+:
+mpVehicle( 0 )
+{
+}
+
+VehicleController::~VehicleController( void )
+{
+}
+
+Vehicle* VehicleController::GetVehicle( void ) const
+{
+ return mpVehicle;
+}
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.h b/game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.h
new file mode 100644
index 0000000..9a7ce5f
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/vehiclecontroller.h
@@ -0,0 +1,49 @@
+#ifndef VEHICLECONTROLER_H_
+#define VEHICLECONTROLER_H_
+
+#include <p3d/refcounted.hpp>
+#include <input/button.h>
+
+class Vehicle;
+
+/*
+// Skeleton class for example.
+//
+struct IButton
+{
+ virtual float GetValue( void ) const = 0;
+ virtual float GetHeldTime( void ) const = 0;
+};
+*/
+class VehicleController
+:
+public tRefCounted
+{
+public:
+ VehicleController( void );
+ virtual ~VehicleController( void );
+
+ virtual void Update( float timeins ) {};
+ virtual void Init() {};
+ virtual void Shutdown() {};
+
+ void SetVehicle( Vehicle* pVehicle ) { mpVehicle = pVehicle; }
+
+ virtual float GetGas( void ) const = 0;
+ virtual float GetThrottle( void ) const = 0;
+
+ virtual float GetBrake( void ) const = 0;
+ virtual float GetSteering( bool& isWheel ) const = 0;
+ virtual float GetSteerLeft( void ) const = 0;
+ virtual float GetSteerRight( void ) const = 0;
+
+ virtual float GetHandBrake( void ) const = 0;
+ virtual float GetReverse( void ) const = 0;
+ virtual float GetHorn( void ) const = 0;
+
+ Vehicle* GetVehicle( void ) const;
+private:
+ Vehicle* mpVehicle;
+};
+
+#endif // VEHICLECONTROLER_H_ \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.cpp b/game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.cpp
new file mode 100644
index 0000000..94c7d87
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.cpp
@@ -0,0 +1,263 @@
+#include <worldsim/redbrick/vehiclecontroller/vehiclemappable.h>
+#include <worldsim/redbrick/vehiclecontroller/humanvehiclecontroller.h>
+
+#include <worldsim/worldphysicsmanager.h>
+
+// Temp.
+//
+#include <worldsim/avatarmanager.h>
+
+#ifdef RAD_WIN32
+#include <gameflow/gameflow.h>
+#include <contexts/context.h>
+#endif
+
+// Constructor.
+//
+VehicleMappable::VehicleMappable( void )
+:
+Mappable(Input::ACTIVE_GAMEPLAY),
+mpHumanVehicleController( 0 ),
+mIsWheel( false ),
+mIsWheelA( false )
+{
+}
+
+// Destructor
+//
+VehicleMappable::~VehicleMappable( void )
+{
+ ReleaseController();
+}
+
+void VehicleMappable::ReleaseController( void )
+{
+ if ( mpHumanVehicleController )
+ {
+ mpHumanVehicleController->Release();
+ mpHumanVehicleController = 0;
+ }
+}
+
+
+
+// This method is called when ever a button state changes.
+//void
+void VehicleMappable::OnButton( int controllerId, int id, const IButton* pButton )
+{
+}
+
+// This method is called when a button changes state from "Pressed" to "Released".
+//
+void VehicleMappable::OnButtonUp( int controllerId, int buttonId, const IButton* pButton )
+{
+}
+
+// This method is called when a button changes state from "Released" to "Pressed".
+//
+void VehicleMappable::OnButtonDown( int controllerId, int buttonId, const IButton* pButton )
+{
+ switch ( buttonId )
+ {
+ case Reset:
+ {
+#ifdef RAD_GAMECUBE
+ if ( pButton->GetValue() > 0.5f )
+ {
+ GetController( )->Reset();
+ }
+#else
+ GetController( )->Reset();
+#endif
+ break;
+ }
+ case Horn:
+ {
+ GetWorldPhysicsManager()->ToggleTimerState();
+ }
+ default:
+ {
+ // Do nothing. That's cool.
+ //
+ break;
+ }
+ }
+}
+
+// This is how we create our controller device mappings to logical game mappings.
+// The mappings set up in this method are platform specific.
+//
+// The basic format of the calls is to "Map" a input, to a enumerated output id.
+// The output of the specified input will be contained in the Button[] array.
+// This id will also be sent as a the second parameter in the OnButton... messages.
+//
+void VehicleMappable::LoadControllerMappings( unsigned int controllerId )
+{
+ // The names used in parameter 1 of the Map method are
+ // Platform Specific.
+ //
+
+ // Configuration 1.
+ //
+
+ // MS7
+ // #ifdef this for different platforms
+ #ifdef RAD_XBOX
+
+ ClearMap(0);
+ Map( "LeftStickX", Steer, 0, controllerId );
+ Map( "DPadLeft", SteerLeft, 0, controllerId );
+ Map( "DPadRight", SteerRight, 0, controllerId );
+
+ // The misnamed "Throttle" controls gas (up) and brake (down).
+ //
+ //Map( "RightStickY", Throttle, 0, controllerId ); Not on XBOX, please
+
+ Map( "RightTrigger", Gas, 0, controllerId );
+ Map( "A", Gas, 0, controllerId );
+
+ Map( "B", Brake, 0, controllerId );
+ Map( "LeftTrigger", Brake, 0, controllerId );
+
+ Map( "X", HandBrake, 0, controllerId );
+
+ Map( "White", Horn, 0, controllerId );
+ Map( "LeftThumb", Horn, 0, controllerId );
+
+ Map( "Back", Reset, 0, controllerId );
+ #endif
+
+
+ #ifdef RAD_PS2
+
+ ClearMap(0);
+
+ if ( controllerId >= Input::MaxControllers - Input::MaxUSB )
+ {
+ mIsWheel = true;
+
+ //This is a wheel!
+ //And for the wheels!
+ Map( "Wheel", Steer, 0, controllerId );
+ Map( "Gas", Gas, 0, controllerId );
+ Map( "Brake", Brake, 0, controllerId );
+
+ if ( !Map( "R2", Gas, 0, controllerId ) )
+ {
+ mIsWheelA = true;
+
+ //This is the GT wheel.
+ Map( "LGR1", Gas, 0, controllerId );
+ Map( "LGA", Brake, 0, controllerId );
+ Map( "LGL1", HandBrake, 0, controllerId );
+ Map( "LGX", Horn, 0, controllerId );
+ Map( "LGB", Reset, 0, controllerId );
+ }
+ else
+ {
+ //This is the DPad Wheel
+ Map( "X", Gas, 0, controllerId );
+ Map( "R1", Brake, 0, controllerId );
+ Map( "Circle", Brake, 0, controllerId );
+ Map( "Square", HandBrake, 0, controllerId );
+ Map( "L1", HandBrake, 0, controllerId );
+ Map( "DPadUp", Horn, 0, controllerId );
+ Map( "DPadDown", Horn, 0, controllerId );
+ Map( "DPadLeft", Horn, 0, controllerId );
+ Map( "DPadRight", Horn, 0, controllerId );
+ Map( "Select", Reset, 0, controllerId );
+ }
+ }
+ else
+ {
+ //This is a DUALSHOCK!
+ Map( "LeftStickX", Steer, 0, controllerId );
+ Map( "DPadLeft", SteerLeft, 0, controllerId );
+ Map( "DPadRight", SteerRight, 0, controllerId );
+
+ // The misnamed "Throttle" controls gas (up) and brake (down).
+ //
+ Map( "RightStickY", Throttle, 0, controllerId );
+
+ Map( "X", Gas, 0, controllerId );
+
+ Map( "Circle", Brake, 0, controllerId );
+
+ Map( "R1", HandBrake, 0, controllerId );
+ Map( "Square", HandBrake, 0, controllerId );
+
+ Map( "L3", Horn, 0, controllerId );
+
+ Map( "Select", Reset, 0, controllerId );
+ }
+
+ #endif
+
+ #ifdef RAD_GAMECUBE
+
+ ClearMap(0);
+ Map( "LeftStickX", Steer, 0, controllerId );
+// Map( "DPadLeft", SteerLeft, 0, controllerId );
+// Map( "DPadRight", SteerRight, 0, controllerId );
+
+ Map( "AnalogTriggerR", Gas, 0, controllerId );
+ Map( "A", Gas, 0, controllerId );
+
+ Map( "X", Brake, 0, controllerId );
+ Map( "AnalogTriggerL", Brake, 0, controllerId );
+
+ Map( "B", HandBrake, 0, controllerId );
+
+ Map( "TriggerZ", Horn, 0, controllerId );
+ Map( "DPadUp", Reset, 0, controllerId );
+ Map( "DPadDown", Reset, 0, controllerId );
+ Map( "DPadLeft", Reset, 0, controllerId );
+ Map( "DPadRight", Reset, 0, controllerId );
+
+ if ( Map( "Wheel", Steer, 0, controllerId ) )
+ {
+ mIsWheel = true;
+ //This is a wheel!
+ Map( "Gas", Gas, 0, controllerId );
+ Map( "Brake", Brake, 0, controllerId );
+ }
+ else
+ {
+//
+ }
+
+ #endif
+
+ #ifdef RAD_WIN32
+
+ ClearMap(0);
+ if ( GetGameFlow()->GetCurrentContext() == CONTEXT_LOADING_SUPERSPRINT &&
+ controllerId == 3 )
+ {
+ Map( "P1_KBD_Left", SteerLeft, 0, controllerId );
+ Map( "P1_KBD_Right", SteerRight, 0, controllerId );
+ Map( "P1_KBD_Gas", Gas, 0, controllerId );
+ Map( "P1_KBD_Brake", Brake, 0, controllerId );
+ Map( "P1_KBD_EBrake", HandBrake, 0, controllerId );
+ Map( "P1_KBD_Nitro", Horn, 0, controllerId );
+ }
+
+ Map( "SteerLeft", SteerLeft, 0, controllerId );
+ Map( "SteerRight", SteerRight, 0, controllerId );
+
+ Map( "Accelerate", Gas, 0, controllerId );
+ Map( "Reverse", Brake, 0, controllerId );
+
+ Map( "HandBrake", HandBrake, 0, controllerId );
+ Map( "Horn", Horn, 0, controllerId );
+
+ Map( "ResetCar", Reset, 0, controllerId );
+
+ #endif
+
+}
+
+void VehicleMappable::SetController( HumanVehicleController* pHumanVehicleController )
+{
+ tRefCounted::Assign( mpHumanVehicleController, pHumanVehicleController );
+}
diff --git a/game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.h b/game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.h
new file mode 100644
index 0000000..e63d6e0
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclecontroller/vehiclemappable.h
@@ -0,0 +1,60 @@
+#ifndef VEHICLEMAPPABLE_H_
+#define VEHICLEMAPPABLE_H_
+
+#include <input/mappable.h>
+
+class HumanVehicleController;
+
+class VehicleMappable
+:
+public Mappable
+{
+public:
+ VehicleMappable( void );
+ ~VehicleMappable( void );
+
+ void ReleaseController( void );
+
+ void OnButton( int controllerId, int id, const IButton* pButton );
+ void OnButtonUp( int controllerId, int buttonId, const IButton* pButton );
+ void OnButtonDown( int controllerId, int buttonId, const IButton* pButton );
+ void LoadControllerMappings( unsigned int controllerId );
+
+ void SetController( HumanVehicleController* pHumanVehicleController );
+
+ bool IsWheel() const { return mIsWheel; };
+ bool IsWheelA() const { return mIsWheelA; };
+
+ enum
+ {
+ Steer = 0,
+ SteerLeft,
+ SteerRight,
+ Throttle,
+ Gas,
+ Brake,
+ Horn,
+ Reset,
+ Pause,
+ Reverse,
+ HandBrake,
+ Burnout,
+ GetOutofCar,
+ LookBack,
+ Spring,
+ Damper,
+ Rumble,
+ RightStickX,
+ RightStickY
+ };
+protected:
+ HumanVehicleController* GetController( void ) const
+ { return mpHumanVehicleController; }
+private:
+ HumanVehicleController* mpHumanVehicleController;
+
+ bool mIsWheel;
+ bool mIsWheelA;
+};
+
+#endif //VEHICLEMAPPABLE_H_ \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehicleeventlistener.cpp b/game/code/worldsim/redbrick/vehicleeventlistener.cpp
new file mode 100644
index 0000000..34838aa
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehicleeventlistener.cpp
@@ -0,0 +1,304 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehicleeventlistener.cpp
+//
+// Description:
+//
+// History: created, Nov 29, 2002, gmayer
+//
+//=============================================================================
+
+
+#include <worldsim/redbrick/vehicleeventlistener.h>
+
+#include <events/eventmanager.h>
+#include <events/eventenum.h>
+
+#include <meta/locatorevents.h>
+#include <meta/eventlocator.h>
+#include <meta/triggervolumetracker.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/vehiclecentral.h>
+#include <worldsim/traffic/trafficmanager.h>
+#include <worldsim/parkedcars/parkedcarmanager.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/avatar.h>
+
+#include <gameflow/gameflow.h>
+
+#include <mission/objectives/missionobjective.h>
+#include <mission/objectives/destroyobjective.h>
+
+#include <mission/gameplaymanager.h>
+#include <supersprint/supersprintmanager.h>
+
+//-----------------------------------------------------------------------------
+VehicleEventListener::VehicleEventListener()
+{
+ mVehicleOwner = 0;
+}
+
+
+//-----------------------------------------------------------------------------
+VehicleEventListener::VehicleEventListener(Vehicle* owner)
+{
+ mVehicleOwner = owner;
+ GetEventManager()->AddListener( this, EVENT_DEATH_VOLUME_SCREEN_BLANK );
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_LOCATOR + LocatorEvent::WHACKY_GRAVITY));
+ GetEventManager()->AddListener( this, (EventEnum)(EVENT_LOCATOR + LocatorEvent::DEATH) );
+}
+
+
+//-----------------------------------------------------------------------------
+VehicleEventListener::~VehicleEventListener()
+{
+ GetEventManager()->RemoveAll( this );
+}
+
+
+//-----------------------------------------------------------------------------
+void VehicleEventListener::HandleEvent( EventEnum id, void* pEventData )
+{
+ ////////////////////////////////////////////////////////////////
+ // WHACKY GRAVITY (FOR JUMP VOLUMES)
+
+ if( id == EVENT_LOCATOR + LocatorEvent::WHACKY_GRAVITY )
+ {
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ rAssert( pLocator );
+
+ Vehicle* testVehicle = NULL;
+ if ( pLocator->GetPlayerID() < static_cast<unsigned int>(MAX_PLAYERS) )
+ {
+ //This is a player vehicle
+ testVehicle = GetAvatarManager()->GetAvatarForPlayer( pLocator->GetPlayerID() )->GetVehicle();
+ }
+ else
+ {
+ testVehicle = GetTriggerVolumeTracker()->GetAI( pLocator->GetPlayerID() );
+ }
+
+ if ( testVehicle != mVehicleOwner )
+ {
+ //If this isn't for me, ignore it.
+ return;
+ }
+
+ // if in supersprint, ignore jump boost for player
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_SUPERSPRINT &&
+ mVehicleOwner->mVehicleType == VT_USER )
+ {
+ return;
+ }
+
+ if ( pLocator->GetPlayerEntered() )
+ {
+ int stophere = 1;
+ // only apply this if we are moderately aligned to it
+
+ rmt::Matrix mat = pLocator->GetMatrix();
+ rmt::Vector facing = mat.Row(2);
+
+ if(mVehicleOwner->mVehicleFacing.DotProduct(facing) > 0.0f)
+ {
+ mVehicleOwner->EnteringJumpBoostVolume();
+ }
+ }
+ else
+ {
+ // should be leaving
+ int stophere = 1;
+ mVehicleOwner->ExitingJumpBoostVolume();
+ }
+ }
+
+
+ ////////////////////////////////////////////////////////////////
+ // DEATH VOLUMES
+
+ else if( id == EVENT_LOCATOR + LocatorEvent::DEATH )
+ {
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ rAssert( pLocator );
+ if( pLocator == NULL )
+ {
+ return;
+ }
+
+ Vehicle* testVehicle = NULL;
+ if ( pLocator->GetPlayerID() < static_cast<unsigned int>(MAX_PLAYERS) )
+ {
+ //This is a player vehicle
+ testVehicle = GetAvatarManager()->GetAvatarForPlayer( pLocator->GetPlayerID() )->GetVehicle();
+ }
+ else
+ {
+ testVehicle = GetTriggerVolumeTracker()->GetAI( pLocator->GetPlayerID() );
+ }
+
+ if ( testVehicle != mVehicleOwner )
+ {
+ //If this isn't for me, ignore it.
+ return;
+ }
+
+ // Ok, it is for me...
+
+ GameplayManager::GameTypeEnum gameType = GetGameplayManager()->GetGameType();
+
+ //
+ // In supersprint, we want to handle DEATH event for AI & player vehicles,
+ // but if in other contexts, we handle DEATH event only for AI (player vehicles
+ // will need to listen to the fade to DEATH_VOLUME_SCREEN_BLANK event because
+ // we don't want death to be instantaneous)
+ //
+ VehicleType type = mVehicleOwner->mVehicleType;
+ if( ( gameType != GameplayManager::GT_SUPERSPRINT ) &&
+ ( type != VT_AI ) )
+ {
+ // there's some stuff that we want to do right away
+ if ( pLocator->GetPlayerEntered() )
+ {
+ GetEventManager()->TriggerEvent( EVENT_DEATH_VOLUME_SOUND, mVehicleOwner );
+ }
+ return;
+ }
+
+ /*
+ //
+ // If this is a destroy objective, we want to kill the target AI vehicle
+ // (thus triggering the destroyed condition, completing the mission...
+ //
+ if( GetGameFlow()->GetCurrentContext() != CONTEXT_SUPERSPRINT )
+ {
+ MissionStage* ms = GetGameplayManager()->GetCurrentMission()->GetCurrentStage();
+ if( ms )
+ {
+ MissionObjective* mobj = ms->GetObjective();
+ if( mobj->GetObjectiveType() == MissionObjective::OBJ_DESTROY )
+ {
+ DestroyObjective* dobj = (DestroyObjective*) mobj;
+ if( dobj->GetTargetVehicle() == mVehicleOwner )
+ {
+ mVehicleOwner->mHitPoints = 0.0f;
+ mVehicleOwner->mVehicleDestroyed = true;
+ return;
+ }
+ }
+ }
+ }
+ */
+
+ rAssert( mVehicleOwner->mVehicleCentralIndex != -1 );
+
+ // Only do something upon entry
+ if ( pLocator->GetPlayerEntered() )
+ {
+ // We're here if we're a car that the player is driving
+ // or if we're an AI car...
+
+ rmt::Matrix mat = pLocator->GetMatrix();
+
+ //Let's turn the car around in specific cases.
+ if ( GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ if ( GetSSM()->IsTrackReversed() )
+ {
+ rmt::Vector heading;
+ mat.GetHeading( heading );
+ heading *= -1.0f;
+ mat.FillHeadingXZ( heading ); //Turn, turn around
+ }
+
+ //
+ // Dusit said I should do this here
+ //
+ GetEventManager()->TriggerEvent( EVENT_DEATH_VOLUME_SOUND, mVehicleOwner );
+ }
+
+ // John McLean here,
+ // locator is snapped to ground, so we need to spawn at some rest
+ // height above ground. Yippe ka yay!
+ rmt::Vector loc;
+ pLocator->GetLocation(&loc);
+ float yAug = mVehicleOwner->GetRestHeightAboveGround();
+ loc.y += yAug;
+ mat.Row(3).y = loc.y;
+
+ mVehicleOwner->SetTransform(mat);
+
+ // do reset flags, do reset AI info (if an AI car)
+ // don't reset pos (already done manually be setting transform),
+ // don't reset damage states
+ mVehicleOwner->ResetFlagsOnly(false);
+ }
+ }
+ else if( id == EVENT_DEATH_VOLUME_SCREEN_BLANK )
+ {
+ // Only handle the player vehicle triggering this. This includes:
+ // - player default vehicle (could be under AI control in Demo) in Demo or Normal modes
+ // - any traffic car or parked car or husk that the player is driving
+
+ EventLocator* pLocator = static_cast<EventLocator*>( pEventData );
+ rAssert( pLocator );
+ if( pLocator == NULL )
+ {
+ return;
+ }
+
+ rAssert( pLocator->GetPlayerID() < static_cast<unsigned int>(MAX_PLAYERS) );
+
+ //This is a player vehicle
+ Vehicle* testVehicle = GetAvatarManager()->GetAvatarForPlayer(
+ pLocator->GetPlayerID() )->GetVehicle();
+
+ if( testVehicle != mVehicleOwner )
+ {
+ //If this isn't for me, ignore it.
+ return;
+ }
+
+ // Ok, it is for me...
+ rAssert( mVehicleOwner->mVehicleCentralIndex != -1 );
+ rAssert( GetGameplayManager()->GetGameType() != GameplayManager::GT_SUPERSPRINT );
+
+ // Only do something upon entry
+ if ( pLocator->GetPlayerEntered() )
+ {
+ // We're here if we're a car that the player is driving
+ // or if we're an AI-controller player car (in Demo mode)
+
+ rmt::Matrix mat = pLocator->GetMatrix();
+
+ // John McLean here,
+ // locator is snapped to ground, so we need to spawn at some rest
+ // height above ground. Yippe ka yay!
+ rmt::Vector loc;
+ pLocator->GetLocation(&loc);
+ float yAug = mVehicleOwner->GetRestHeightAboveGround();
+ loc.y += yAug;
+ mat.Row(3).y = loc.y;
+
+ //
+ // what if there is already a car at the location we want to put htis car
+ //
+ const rmt::Vector& position = mat.Row( 3 );
+ rmt::Sphere s;
+ s.centre = position;
+ s.radius = 4.0;
+ TrafficManager::GetInstance()->ClearTrafficInSphere( s );
+ GetGameplayManager()->DumpCurrentCarIfInSphere( s );
+ GetPCM().RemoveFreeCarIfClose( position );
+
+ mVehicleOwner->SetTransform(mat);
+
+ // do reset flags, do reset AI info (if an AI car)
+ // don't reset pos (already done manually be setting transform),
+ // don't reset damage states
+ mVehicleOwner->ResetFlagsOnly(false);
+ }
+ }
+}
+
diff --git a/game/code/worldsim/redbrick/vehicleeventlistener.h b/game/code/worldsim/redbrick/vehicleeventlistener.h
new file mode 100644
index 0000000..30f0668
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehicleeventlistener.h
@@ -0,0 +1,45 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehicleeventlistener.h
+//
+// Description:
+//
+// History: created, Nov 29, 2002, gmayer
+//
+//=============================================================================
+
+
+
+#ifndef VEHICLEEVENTLISTENER_H
+#define VEHICLEEVENTLISTENER_H
+
+#include <events/eventlistener.h>
+
+class Vehicle;
+
+class VehicleEventListener : public EventListener
+{
+public:
+
+ VehicleEventListener();
+ VehicleEventListener(Vehicle* owner);
+
+ virtual ~VehicleEventListener();
+
+
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+
+private:
+
+ // Declared but not defined to prevent copying and assignment.
+ VehicleEventListener( const VehicleEventListener& );
+ VehicleEventListener& operator=( const VehicleEventListener& );
+
+ Vehicle* mVehicleOwner;
+};
+
+
+
+
+#endif // VEHICLEEVENTLISTENER_H
diff --git a/game/code/worldsim/redbrick/vehicleinit.cpp b/game/code/worldsim/redbrick/vehicleinit.cpp
new file mode 100644
index 0000000..79ee9fe
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehicleinit.cpp
@@ -0,0 +1,2309 @@
+//=============================================================================
+// Copyright (C) 2001 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehicleinit.cpp
+//
+// Description: init stuff for the vehicle, that is only called once!
+//
+// History: Aug 2, 2002 + Created -- gmayer
+//
+//=============================================================================
+
+
+//========================================
+// System Includes
+//========================================
+
+#include <simcommon/skeletoninfo.hpp>
+#include <simcommon/simstate.hpp>
+#include <simcommon/simstatearticulated.hpp>
+#include <simcommon/simulatedobject.hpp>
+#include <simphysics/articulatedphysicsobject.hpp>
+#include <simphysics/physicsjoint.hpp>
+#include <simphysics/physicsobject.hpp>
+#include <simcommon/simenvironment.hpp>
+#include <simcollision/collisionobject.hpp>
+#include <simcollision/collisionvolume.hpp>
+#include <mission/charactersheet/charactersheetmanager.h>
+#include <simcollision/collisiondisplay.hpp>
+
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#include <p3d/anim/drawablepose.hpp>
+#include <p3d/shadow.hpp>
+
+#include <string.h>
+
+//========================================
+// Project Includes
+//========================================
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/worldobject.h>
+
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/redbrick/rootmatrixdriver.h>
+#include <worldsim/redbrick/suspensionjointdriver.h>
+#include <worldsim/redbrick/vehicleeventlistener.h>
+#include <worldsim/redbrick/wheel.h>
+#include <worldsim/redbrick/redbrickcollisionsolveragent.h>
+
+#include <worldsim/redbrick/physicslocomotion.h>
+#include <worldsim/redbrick/trafficlocomotion.h>
+
+#include <worldsim/character/charactermanager.h>
+
+
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+
+#include <meta/carstartlocator.h>
+
+#include <debug/debuginfo.h>
+
+
+#include <ai/actionbuttonhandler.h>
+#include <ai/actionbuttonmanager.h>
+
+#include <meta/eventlocator.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/recttriggervolume.h>
+
+#include <worldsim/character/character.h>
+
+#include <gameflow/gameflow.h>
+
+using namespace sim;
+
+// The force required to knock a collectible off
+const float MIN_FORCE_DETACH_COLLECTIBLES = 30000.0f;
+
+
+//=============================================================================
+// Vehicle::Vehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle::Vehicle() :
+ mGeometryVehicle( NULL ),
+ mName( NULL ),
+ mVehicleID( VehicleEnum::INVALID ),
+ mTerrainType( TT_Road),
+ mInterior( false ),
+ mpEventLocator( NULL ),
+ mCharacterSheetCarIndex( -1 ),
+ m_IsSimpleShadow( true )
+{
+ mTranslucent = true;
+ mDrawVehicle = true;
+
+ mTransform.Identity();
+
+ mVehicleCentralIndex = -1;
+
+ // wtf?
+ mInitialPosition.Set(0.0f, 8.0f, 20.0f);
+ mResetFacingRadians = 0.0f;
+
+ mVehicleFacing.Set(0.0f, 0.0f, 1.0f);
+ mVehicleUp.Set(0.0f, 1.0f, 0.0f);
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+#else
+ GameMemoryAllocator allocator = GetGameplayManager()->GetCurrentMissionHeap();
+ HeapMgr()->PushHeap( allocator );
+#endif
+ mGeometryVehicle = new GeometryVehicle;
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PopHeap( allocator );
+#endif
+
+
+ //mPhysicsVehicle = new PhysicsVehicle;
+ //mPhysicsVehicle = 0; test this motherfucker
+
+ // should this be here or in Init?
+ // shouldn't really be in this class at all
+ // TODO - take this shit out
+ //mVehicleRender = new VehicleRender(this);
+
+
+ mSimStateArticulated = 0;
+ mPoseEngine = 0;
+ mRootMatrixDriver = 0;
+
+ //mPoseEngineOutOfCar = 0;
+ //mRootMatrixDriverOutOfCar = 0;
+
+
+ mSimStateArticulatedOutOfCar = 0;
+ mSimStateArticulatedInCar = 0;
+
+ mGas = 0.0f;
+ mLastGas = 0.0f;
+ mDeltaGas = 0.0f;
+ mBrake = 0.0f;
+ mWheelTurnAngle = 0.0f;
+ mReverse = 0.0f;
+ mEBrake = 0.0f;
+
+ mBrakeTimer = 0.0f;
+ mBrakeActingAsReverse = false;
+
+ mGasBrakeDisabled = false;
+
+ mUnmodifiedInputWheelTurnAngle = 0.0f;
+
+ mVelocityCM.Set(0.0f, 0.0f, 0.0f);
+ mSpeed = 0.0f;
+ mSpeedKmh = 0.0f;
+ mLastSpeedKmh = 0.0f;
+ mAccelMss = 0.0f;
+ mPercentOfTopSpeed = 0.0f;
+
+ mRollingFrictionForce = 2.0f; // need to tune? I don't think so
+ //mRollingFrictionForce = 1.5f; // need to tune? I don't think so
+
+ mRPM = 0.0f;
+ mBaseRPM = 500.0f;
+ mMaxRpm = 6500.0f;
+
+ mRPMUpRate = 10.0f;
+ mRPMDownRate = 50.0f;
+ //mShiftPointHigh = 3500.0f;
+ //mShiftPointLow = 1000.0f;
+
+ //mShiftPointHigh = 4000.0f;
+ mShiftPointHigh = 4500.0f;
+ //mShiftPointLow = 1500.0f;
+ mShiftPointLow = 2000.0f;
+
+
+ mGear = 0; // neutral?
+
+ mNumGears = -1; // unset
+ mGearRatios = 0;
+ mFinalDriveRatio = -1.0f; // unset
+
+ mSkidLevel = 0.0f;
+ mBurnoutLevel = 0.0f;
+ mSlipGasModifier = 1.0f;
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ mInertialJointDrivers[i] = 0;
+ mPhysicsJointMatrixModifiers[i] = 0;
+ }
+ mJointIndexToInertialJointDriverMapping = 0;
+
+ mCollisionAreaIndex = -1;
+
+ mDoorDJoint = -1;
+ mDoorPJoint = -1;
+ mHoodJoint = -1;
+ mTrunkJoint = -1;
+
+ mDoorDDamageLevel = 0;
+ mDoorPDamageLevel = 0;
+ mHoodDamageLevel = 0;
+ mTrunkDamageLevel = 0;
+
+ mOkToDrawSelf = true;
+ mSteeringWheelsOutOfContact = false;
+ mAirBorn = false;
+ mWasAirborn = false;
+ mWasAirbornTimer = 0.0f;
+ mWeebleOn = false;
+ mInstabilityOffsetOn = false;
+
+ mSpeedBurstTimer = 0.0f;
+ mBuildingUpSpeedBurst = false;
+ mDoSpeedBurst = false;
+ mFOVToRestore = 1.57f;
+ mSpeedBurstTimerHalf = 0.0f;
+
+ //mDamageType = 0; // 0 is unset - normally 1, 2, or 3
+ mDamageType = VDT_UNSET;
+
+ mVehicleDestroyed = false;
+ mVehicleCanSustainDamage = true;
+ //mVehicleCanSustainDamage = false;
+
+ mIsADestroyObjective = false;
+
+ mHitPoints = 2.0f;
+
+ mDamageOutResetTimer = 0.0f;
+
+ mSmokeOffset.Set(0.0f, -0.5f, 1.5f);
+
+// mHitJoint = 0;
+
+ mTrafficVehicle = NULL;
+
+ mCollisionLateralResistanceDropFactor = 1.0f;
+
+ mUserDrivingCar = false;
+
+ mDesiredDoorPosition[0] = mDesiredDoorPosition[1] = 0.0f;
+ mDesiredDoorAction[0] = mDesiredDoorAction[1] = DOORACTION_NONE;
+
+ mpTriggerVolume = NULL;
+
+ mpDriver = NULL;
+
+ mDoingRockford = false;
+ mDoingWheelie = false;
+
+
+ strcpy(mDriverName,"phantom");
+// mDriverName[0] = 0;
+ mPhantomDriver = false;
+
+
+ // vehicle event stuff
+ mVehicleEventListener = 0;
+
+ mDoingJumpBoost = false;
+
+ mNoSkid = false; // only true for frink's hovering vehicles (also the ghost ship)
+ mNoFrontSkid = false; // only true for the rocket car
+
+ mBrakeLightsOn = false;
+ mReverseLightsOn = false;
+ mDontShowBrakeLights = false;
+
+ mCMOffsetSetToOriginal = false;
+
+ mOutOfControl = false;
+ mCollidedWithVehicle = false;
+
+ mNormalizedMagnitudeOfVehicleHit = 0.0f;
+ mWasHitByVehicle = false;
+ mWasHitByVehicleType = VT_LAST;
+
+ mDamperShouldNotPullDown[0] = false;
+ mDamperShouldNotPullDown[1] = false;
+ mDamperShouldNotPullDown[2] = false;
+ mDamperShouldNotPullDown[3] = false;
+
+
+ mNumTurbos = 3; // default to 3 for now
+ mSecondsTillNextTurbo = 0.0f;
+
+ mUsingInCarPhysics = true;
+
+ //mWaitingToSwitchToOutOfCar = false;
+ //mOutOfCarSwitchTimer = 0.0f; // brutal fucking hack
+
+ mNoDamageTimer = 0.0f;
+
+ mAlreadyPlayedExplosion = false;
+
+ mEBrakeTimer = 0.0f;
+
+ mWheelTurnAngleInputValue = 0.0f;
+
+ mDrawWireFrame = false;
+ mLosingTractionDueToAccel = false; // for plum - just debug output
+
+ mHijackedByUser = false;
+
+
+
+ mDesignerParams.mDpGamblingOdds= 1.0f; //Chuck: set the Gambling Odds to 1.0 or 1:1 by default.
+ mbPlayerCar = false;
+
+ mOurRestSeatingPosition.Set(0.0f, 0.0f, 0.0f);
+ mNPCRestSeatingPosition.Set(0.0f, 0.0f, 0.0f);
+ mYAccelForSeatingOffset = 0.0f;
+
+ mVelocityCMLag.Set(0.0f, 0.0f, 0.0f);
+ mPositionCMLag.Set(0.0f, 0.0f, 0.0f);
+
+ // value... fresh from my ass:
+ //mBounceLimit = 0.5f;
+ //mBounceLimit = 0.25f;
+ mBounceLimit = 0.11f;
+
+
+ //mMaxBounceDisplacementPerSecond = 1.0f;
+ mMaxBounceDisplacementPerSecond = 2.0f;
+
+ mBounceAccelThreshold = 1.0f; // below this value just try and move back to rest
+ //mBounceAccelThreshold = 2.0f; // below this value just try and move back to rest
+
+
+ mForceToDetachCollectible = MIN_FORCE_DETACH_COLLECTIBLES;
+
+ mHasDoors = true;
+ mVisibleCharacters = true;
+ mIrisTransition = false;
+ mAllowSlide = true;
+ mHighRoof = false;
+ mCharacterScale = 1.0f;
+
+
+ // Lets set the default to inflict half damage on this vehicle when they explode
+ s_DamageFromExplosion = 0.5f;
+ // And no damage on player vehicles
+ s_DamageFromExplosionPlayer = 0;
+
+ mStuckOnSideTimer = 0.0f;
+ mAlreadyCalledAutoResetOnSpot = false;
+
+
+ mOriginalCMOffset.x = 0.0f;
+ mOriginalCMOffset.y = 0.0f;
+ mOriginalCMOffset.z = 0.0f;
+
+ mBottomOutSpeedMaintenance = 0.0f;
+
+ mTriggerActive = false;
+
+ mAtRestAsFarAsTriggersAreConcerned = true;
+
+ mLocoSwitchedToPhysicsThisFrame = false;
+
+ mCreatedByParkedCarManager = false;
+}
+
+
+//=============================================================================
+// Vehicle::Init
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (char* name, int num, SimEnvironment* se, VehicleLocomotionType loco)
+//
+// Return: bool
+//
+//=============================================================================
+bool Vehicle::Init( const char* name, SimEnvironment* se, VehicleLocomotionType loco, VehicleType vt, bool startoutofcar)
+{
+ if(mbPlayerCar ==true)
+ {
+ mCharacterSheetCarIndex = GetCharacterSheetManager()->GetCarIndex( name );
+ }
+ else
+ {
+ mCharacterSheetCarIndex = -1;
+ }
+
+ mDrawVehicle = true;
+
+ mVehicleType = vt;
+
+ rAssert( mName == NULL );
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mName = new(gma)char[strlen(name)+1];
+ strcpy( mName, name );
+
+ CollisionEntityDSG::SetName(mName);
+
+ mLoco = loco;
+
+ AssignEnumBasedOnName();
+
+ //++++++++++++++++++++
+ // the designer params
+ //++++++++++++++++++++
+
+
+ //mDesignerParams.mDpGasScale = 8.0f; // proportional to mass
+ //mDesignerParams.mDpGasScale = 6.0f; // proportional to mass
+ //mDesignerParams.mDpGasScale = 3.0f; // proportional to mass
+ mDesignerParams.mDpGasScale = 4.0f; // proportional to mass
+
+ mDesignerParams.mDpSlipGasScale = 4.0f;
+
+ mDesignerParams.mDpHighSpeedGasScale = 2.0f;
+ //mDesignerParams.mDpGasScaleSpeedThreshold = 0.5f;
+ mDesignerParams.mDpGasScaleSpeedThreshold = 1.0f; // make this the default so it is not used for the time being
+
+
+ mDesignerParams.mDpBrakeScale = 3.0f; // proportional to mass
+
+ //mDesignerParams.mDpTopSpeedKmh = 300.0f;
+ //mDesignerParams.mDpTopSpeedKmh = 250.0f;
+ mDesignerParams.mDpTopSpeedKmh = 220.0f;
+
+ mDesignerParams.mDpMass = 1000.0f; // think of it as kg
+
+ mDesignerParams.mDpMaxWheelTurnAngle = 35.0f; // in degrees
+ //mDesignerParams.mDpMaxWheelTurnAngle = 30.0f; // in degrees
+ //mDesignerParams.mDpMaxWheelTurnAngle = 24.0f; // in degrees
+ //mDesignerParams.mDpHighSpeedSteeringDrop = 0.25f; // 0.0 to 1.0
+ mDesignerParams.mDpHighSpeedSteeringDrop = 0.0f; // 0.0 to 1.0
+
+ // for wheel
+ //mDesignerParams.mDpTireLateralStaticGrip = 2.5f;
+ //mDesignerParams.mDpTireLateralStaticGrip = 5.0f;
+ mDesignerParams.mDpTireLateralStaticGrip = 4.0f;
+
+
+ mDesignerParams.mDpTireLateralResistanceNormal = 110.0f;
+ mDesignerParams.mDpTireLateralResistanceSlip = 35.0f;
+
+ mDesignerParams.mDpEBrakeEffect = 0.5f;
+
+ //mDesignerParams.mDpTireLateralResistanceSlipNoEBrake = 20.0f; // this one's for more out of control driving
+ mDesignerParams.mDpTireLateralResistanceSlipNoEBrake = 50.0f; // this one's for more out of control driving
+ mDesignerParams.mDpSlipEffectNoEBrake = 0.1f;
+
+ mDesignerParams.mDpCMOffset.x = 0.0f;
+ mDesignerParams.mDpCMOffset.y = 0.0f;
+ mDesignerParams.mDpCMOffset.z = 0.0f;
+
+ // don't think they need to touch these for now
+ mDesignerParams.mDpSuspensionLimit = 0.4f;
+ mDesignerParams.mDpSpringk = 0.5f;
+ mDesignerParams.mDpDamperc = 0.2f;
+
+ mDesignerParams.mDpSuspensionYOffset = 0.0f;
+
+ mDesignerParams.mHitPoints = 2.0f;
+ //mDesignerParams.mHitPoints = 0.5f;
+
+ //mDesignerParams.mDpBurnoutRange = 0.2f;
+ mDesignerParams.mDpBurnoutRange = 0.3f;
+
+ mDesignerParams.mDpWheelieRange = 0.0f;
+ mDesignerParams.mDpWheelieYOffset = 0.0f;
+ mDesignerParams.mDpWheelieZOffset = 0.0f;
+
+ mDesignerParams.mDpMaxSpeedBurstTime = 1.0f;
+ mDesignerParams.mDpDonutTorque = 10.0f;
+
+ mDesignerParams.mDpWeebleOffset = -1.0f;
+
+
+ mVehicleState = VS_NORMAL;
+ mTireLateralResistance = mDesignerParams.mDpTireLateralResistanceNormal;
+
+ mSteeringInputThreshold = 0.7f;
+ mSteeringPreSlope = 0.5f;
+
+
+ bool localizedDamage;
+ //localizedDamage = mGeometryVehicle->Init(name, this, num);
+ localizedDamage = mGeometryVehicle->Init(name, this, 0);
+
+ // change the logic so that localizedDamage is true if there as at
+ // least one localized damage texture
+
+
+ if(!localizedDamage)
+ {
+ //mDamageType = 3; // traffic
+ mDamageType = VDT_TRAFFIC;
+ }
+
+ InitSimState(se);
+
+ InitGroundPlane();
+
+ FetchWheelMapping();
+ InitWheelsAndLinkSuspensionJointDrivers();
+
+
+ mGeometryVehicle->InitParticles();
+ //if(mVehicleType == VT_USER)
+ if(1)// mVehicleType == VT_USER || mVehicleType == VT_AI) change this to init for all, only draw for USER - that way it is totally safe to switch vehicle type on the fly
+ {
+ mGeometryVehicle->InitSkidMarks();
+ }
+
+
+
+ bool flappingJointPresent = InitFlappingJoints();
+ if(flappingJointPresent)
+ {
+ //if(mDamageType == 3)
+ if(mDamageType == VDT_TRAFFIC)
+ {
+ //rAssertMsg(0, "Fink, what are you doing?");
+
+ }
+
+ // else
+
+ //mDamageType = 1;
+ mDamageType = VDT_USER;
+
+ }
+ else if(mDamageType == VDT_UNSET) // still unset
+ {
+ mDamageType = VDT_AI;
+ }
+
+ // should be set for sure at this point
+ rAssertMsg(mDamageType != VDT_UNSET, "damage type not set?");
+
+ InitGears();
+
+ //CalculateDragCoeffBasedOnTopSpeed();
+
+ CalculateValuesBasedOnDesignerParams();
+ // does this:
+ // SetupPhysicsProperties();
+ // CalculatedDragCoeffBasedOnTopSpeed
+ // SetDesignerParams on Wheels
+
+
+ CreateLocomotions();
+ mLocoSwitchedToPhysicsThisFrame = false;
+
+
+
+ // need this call here?
+ Reset( false );
+
+ SetupRadDebugWatchStuff();
+
+ // moved here from VehicleCentral
+ InitEventLocator();
+
+
+ // vehicle event stuff
+ mVehicleEventListener = new VehicleEventListener(this);
+
+
+
+
+ //mUsingInCarPhysics
+
+ // default at this point in creation is to be using the incar physics.
+ //
+ // perhaps there should be a paramter to say which, passed into
+
+
+ if(startoutofcar)
+ {
+ SetOutOfCarSimState();
+ }
+
+ /*
+ if( GetGameFlow()->GetCurrentContext() == CONTEXT_DEMO )
+ {
+ mWaitingToSwitchToOutOfCar = true;
+ }
+ else
+ {
+ mWaitingToSwitchToOutOfCar = false;
+ }
+ */
+
+ // actually any use in this result?
+ // might as well just assert.
+ //return result;
+ return true;
+}
+
+
+
+//=============================================================================
+// Vehicle::InitEventLocator
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::InitEventLocator()
+{
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+
+ mpEventLocator = new(gma)EventLocator();
+ mpEventLocator->SetName( mName );
+ mpEventLocator->AddRef();
+ mpEventLocator->SetEventType( LocatorEvent::CAR_DOOR );
+ int id = -1;
+
+ ActionButton::GetInCar* pABHandler = static_cast<ActionButton::GetInCar*>( ActionButton::GetInCar::NewAction( mpEventLocator ) );
+ rAssert( dynamic_cast<ActionButton::GetInCar*>( pABHandler ) != NULL );
+ rAssert( pABHandler );
+ bool bResult = GetActionButtonManager()->AddAction( pABHandler, id );
+ rAssert( bResult );
+
+ // this part is done in VehicleCentral::AddVehicleToActiveList
+ //pABHandler->SetVehicleId( mNumActiveVehicles );
+
+ mpEventLocator->SetData( id );
+
+ // grab the car bounding box
+ mExtents = GetSimState()->GetCollisionObject()->GetCollisionVolume()->mBoxSize;
+
+ // slap down a trigger volume for getting intop car
+ const float edge = 1.0f; // how big? TODO : tunable?
+ mpTriggerVolume = new(gma) RectTriggerVolume( mTransform.Row(3),
+ mTransform.Row(0),
+ mTransform.Row(1),
+ mTransform.Row(2),
+ mExtents.x + edge,
+ mExtents.y + edge,
+ mExtents.z + edge);
+
+ // add volume to event trigger
+ mpEventLocator->SetNumTriggers( 1, gma );
+ mpEventLocator->AddTriggerVolume( mpTriggerVolume );
+ mpTriggerVolume->SetLocator( mpEventLocator );
+ mpTriggerVolume->SetName( "get_in" );
+
+}
+
+//=============================================================================
+// Vehicle::AssignEnumBasedOnName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::AssignEnumBasedOnName()
+{
+ /*
+ enum VehicleID
+ {
+ BART_V = 0, // bart_v 0
+ APU_V, // apu_v 1
+ SNAKE_V, // snake_v 2
+ HOMER_V, // homer_v 3
+ FAMIL_V, // famil_v 4
+ GRAMP_V, // gramp_v 5
+ CLETU_V, // cletu_v 6
+ WIGGU_V, // wiggu_v 7
+ KRUSTYKAR_NOTYETIN, //
+ MARGE_V, // marge_v 9
+ OTTOBUS_NOTYETIN,
+ MOESDUFFTRUCK_NOTYETIN,
+ SMITH_V, // smith_v 12
+ FLANDERSMOTORHOME_NOTYETIN,
+ MCBAINHUMMER_NOTYETIN,
+ ALIEN_NOTYETIN,
+ ZOMBI_V, // zombi_v 16
+ MUNSTERS_NOTYETIN,
+ HUMMER2_NOTYETIN,
+
+ CVAN, // cVan 19
+ COMPACTA, // compactA 20
+
+ COMIC_V, // comic_v 21
+ SKINN_V, // skinn_v 22
+
+
+
+ // some more new ai cars
+ CCOLA, // 23
+ CSEDAN,
+ CPOLICE,
+ CCELLA,
+ CCELLB,
+ CCELLC,
+ CCELLD,
+
+ // some more new traffic cars
+ MINIVANA, // 30
+ PICKUPA,
+ TAXIA,
+ SPORTSA,
+ SPORTSB,
+ SUVA,
+ WAGONA, // 36
+
+ // some more new cars: nov 12, 2002
+ HBIKE_V, // 37
+ BURNS_V, // 38
+ HONOR_V, // 39
+
+ CARMOR, // 40
+ CCURATOR, // 41
+ CHEARS, // 42
+ CKLIMO, // 43
+ CLIMO, // 44
+ CNERD, // 45
+
+ FRINK_V, // 46
+
+ // some more new cars: dec 18, 2002
+ CMILK, // 47
+ CDONUT, // 48
+ BBMAN_V, // 49
+ BOOKB_V, // 50
+ CARHOM_V, // 51
+ ELECT_V, // 52
+ FONE_V, // 53
+ GRAMR_V, // 54
+ MOE_V, // 55
+ MRPLO_V, // 56
+ OTTO_V, // 57
+ PLOWK_V, // 58
+ SCORP_V, // 59
+ WILLI_V, // 60
+
+ // new traffic cars: Jan 11, 2003
+ SEDANA, // 61
+ SEDANB, // 62
+
+ // new Chase cars: Jan 11, 2003
+ CBLBART, // 63
+ CCUBE, // 64
+ CDUFF, // 65
+ CNONUP, // 66
+
+ // new Driver cars: Jan 11, 2003
+ LISA_V, // 67
+ KRUST_V, // 68
+
+ // new Traffic cars: Jan 13, 2003
+ COFFIN, // 69
+ HALLO, // 70
+ SHIP, // 71
+ WITCHCAR, // 72
+
+ // new Traffic husk: Feb 10, 2003
+ HUSKA, // 73
+
+ // new Driver cars: Feb 11, 2003
+ ATV_V, // 74
+ DUNE_V, // 75
+ HYPE_V, // 76
+ KNIGH_V, // 77
+ MONO_V, // 78
+ OBLIT_V, // 79
+ ROCKE_V, // 80
+
+ // new Traffic cars: Feb 24, 2003
+ AMBUL, // 81
+ BURNSARM, // 82
+ FISHTRUC, // 83
+ GARBAGE, // 84
+ ICECREAM, // 85
+ ISTRUCK, // 86
+ NUCTRUCK, // 87
+ PIZZA, // 88
+ SCHOOLBU, // 89
+ VOTETRUC, // 90
+
+ // new traffic cars: April 2, 2003
+ GLASTRUC, // 91
+ CFIRE_V, // 92
+
+ CBONE,
+ REDBRICK, // 94
+
+ NUM_VEHICLES,
+ INVALID
+
+ };
+
+ */
+
+ if(strcmp(mName, "bart_v") == 0)
+ {
+ mVehicleID = VehicleEnum::BART_V;
+ }
+
+ if(strcmp(mName, "apu_v") == 0)
+ {
+ mVehicleID = VehicleEnum::APU_V;
+ }
+
+ if(strcmp(mName, "snake_v") == 0)
+ {
+ mVehicleID = VehicleEnum::SNAKE_V;
+ }
+
+ if(strcmp(mName, "homer_v") == 0)
+ {
+ mVehicleID = VehicleEnum::HOMER_V;
+ }
+
+ if(strcmp(mName, "famil_v") == 0)
+ {
+ mVehicleID = VehicleEnum::FAMIL_V;
+ }
+
+ if(strcmp(mName, "gramp_v") == 0)
+ {
+ mVehicleID = VehicleEnum::GRAMP_V;
+ }
+
+ if(strcmp(mName, "cletu_v") == 0)
+ {
+ mVehicleID = VehicleEnum::CLETU_V;
+ }
+
+ if(strcmp(mName, "wiggu_v") == 0)
+ {
+ mVehicleID = VehicleEnum::WIGGU_V;
+ }
+
+ if(strcmp(mName, "marge_v") == 0)
+ {
+ mVehicleID = VehicleEnum::MARGE_V;
+ }
+
+ if(strcmp(mName, "smith_v") == 0)
+ {
+ mVehicleID = VehicleEnum::SMITH_V;
+ }
+
+ if(strcmp(mName, "zombi_v") == 0)
+ {
+ mVehicleID = VehicleEnum::ZOMBI_V;
+ }
+
+ if(strcmp(mName, "cVan") == 0)
+ {
+ mVehicleID = VehicleEnum::CVAN;
+ }
+
+ if(strcmp(mName, "compactA") == 0)
+ {
+ mVehicleID = VehicleEnum::COMPACTA;
+ }
+
+ if(strcmp(mName, "comic_v") == 0)
+ {
+ mVehicleID = VehicleEnum::COMIC_V;
+ }
+
+ if(strcmp(mName, "skinn_v") == 0)
+ {
+ mVehicleID = VehicleEnum::SKINN_V;
+ }
+
+ if(strcmp(mName, "cCola") == 0)
+ {
+ mVehicleID = VehicleEnum::CCOLA;
+ }
+
+ if(strcmp(mName, "cSedan") == 0)
+ {
+ mVehicleID = VehicleEnum::CSEDAN;
+ }
+
+ if(strcmp(mName, "cPolice") == 0)
+ {
+ mVehicleID = VehicleEnum::CPOLICE;
+ }
+
+ if(strcmp(mName, "cCellA") == 0)
+ {
+ mVehicleID = VehicleEnum::CCELLA;
+ }
+
+ if(strcmp(mName, "cCellB") == 0)
+ {
+ mVehicleID = VehicleEnum::CCELLB;
+ }
+
+ if(strcmp(mName, "cCellC") == 0)
+ {
+ mVehicleID = VehicleEnum::CCELLC;
+ }
+
+ if(strcmp(mName, "cCellD") == 0)
+ {
+ mVehicleID = VehicleEnum::CCELLD;
+ }
+
+ if(strcmp(mName, "minivanA") == 0)
+ {
+ mVehicleID = VehicleEnum::MINIVANA;
+ }
+
+ if(strcmp(mName, "pickupA") == 0)
+ {
+ mVehicleID = VehicleEnum::PICKUPA;
+ }
+
+ if(strcmp(mName, "taxiA") == 0)
+ {
+ mVehicleID = VehicleEnum::TAXIA;
+ }
+
+ if(strcmp(mName, "sportsA") == 0)
+ {
+ mVehicleID = VehicleEnum::SPORTSA;
+ }
+
+ if(strcmp(mName, "sportsB") == 0)
+ {
+ mVehicleID = VehicleEnum::SPORTSB;
+ }
+
+ if(strcmp(mName, "SUVA") == 0)
+ {
+ mVehicleID = VehicleEnum::SUVA;
+ }
+
+ if(strcmp(mName, "wagonA") == 0)
+ {
+ mVehicleID = VehicleEnum::WAGONA;
+ }
+
+ if(strcmp(mName, "hbike_v") == 0)
+ {
+ mVehicleID = VehicleEnum::HBIKE_V;
+ mNoSkid = true;
+ }
+
+
+ if(strcmp(mName, "burns_v") == 0)
+ {
+ mVehicleID = VehicleEnum::BURNS_V;
+ }
+
+ if(strcmp(mName, "honor_v") == 0)
+ {
+ mVehicleID = VehicleEnum::HONOR_V;
+ mNoFrontSkid = true;
+ }
+
+ if(strcmp(mName, "cArmor") == 0)
+ {
+ mVehicleID = VehicleEnum::CARMOR;
+ }
+
+ if(strcmp(mName, "cCurator") == 0)
+ {
+ mVehicleID = VehicleEnum::CCURATOR;
+ }
+
+ if(strcmp(mName, "cHears") == 0)
+ {
+ mVehicleID = VehicleEnum::CHEARS;
+ }
+
+ if(strcmp(mName, "cKlimo") == 0)
+ {
+ mVehicleID = VehicleEnum::CKLIMO;
+ }
+
+ if(strcmp(mName, "cLimo") == 0)
+ {
+ mVehicleID = VehicleEnum::CLIMO;
+ }
+
+ if(strcmp(mName, "cNerd") == 0)
+ {
+ mVehicleID = VehicleEnum::CNERD;
+ }
+
+ if(strcmp(mName, "frink_v") == 0)
+ {
+ mVehicleID = VehicleEnum::FRINK_V;
+ mNoSkid = true;
+ }
+
+ if(strcmp(mName, "cMilk") == 0)
+ {
+ mVehicleID = VehicleEnum::CMILK;
+ }
+
+ if(strcmp(mName, "cDonut") == 0)
+ {
+ mVehicleID = VehicleEnum::CDONUT;
+ }
+
+ if(strcmp(mName, "bbman_v") == 0)
+ {
+ mVehicleID = VehicleEnum::BBMAN_V;
+ }
+
+ if(strcmp(mName, "bookb_v") == 0)
+ {
+ mVehicleID = VehicleEnum::BOOKB_V;
+ }
+
+ if(strcmp(mName, "carhom_v") == 0)
+ {
+ mVehicleID = VehicleEnum::CARHOM_V;
+ }
+
+ if(strcmp(mName, "elect_v") == 0)
+ {
+ mVehicleID = VehicleEnum::ELECT_V;
+ }
+
+ if(strcmp(mName, "fone_v") == 0)
+ {
+ mVehicleID = VehicleEnum::FONE_V;
+ }
+
+ if(strcmp(mName, "gramR_v") == 0)
+ {
+ mVehicleID = VehicleEnum::GRAMR_V;
+ }
+
+ if(strcmp(mName, "moe_v") == 0)
+ {
+ mVehicleID = VehicleEnum::MOE_V;
+ }
+
+ if(strcmp(mName, "mrplo_v") == 0)
+ {
+ mVehicleID = VehicleEnum::MRPLO_V;
+ }
+
+ if(strcmp(mName, "otto_v") == 0)
+ {
+ mVehicleID = VehicleEnum::OTTO_V;
+ }
+
+ if(strcmp(mName, "plowk_v") == 0)
+ {
+ mVehicleID = VehicleEnum::PLOWK_V;
+ }
+
+ if(strcmp(mName, "scorp_v") == 0)
+ {
+ mVehicleID = VehicleEnum::SCORP_V;
+ }
+
+ if(strcmp(mName, "willi_v") == 0)
+ {
+ mVehicleID = VehicleEnum::WILLI_V;
+ }
+
+ if(strcmp(mName, "sedanA") == 0)
+ {
+ mVehicleID = VehicleEnum::SEDANA;
+ }
+
+ if(strcmp(mName, "sedanB") == 0)
+ {
+ mVehicleID = VehicleEnum::SEDANB;
+ }
+
+ if(strcmp(mName, "cBlbart") == 0)
+ {
+ mVehicleID = VehicleEnum::CBLBART;
+ }
+
+ if(strcmp(mName, "cCube") == 0)
+ {
+ mVehicleID = VehicleEnum::CCUBE;
+ }
+
+ if(strcmp(mName, "cDuff") == 0)
+ {
+ mVehicleID = VehicleEnum::CDUFF;
+ }
+
+ if(strcmp(mName, "cNonup") == 0)
+ {
+ mVehicleID = VehicleEnum::CNONUP;
+ }
+
+ if(strcmp(mName, "lisa_v") == 0)
+ {
+ mVehicleID = VehicleEnum::LISA_V;
+ }
+
+ if(strcmp(mName, "krust_v") == 0)
+ {
+ mVehicleID = VehicleEnum::KRUST_V;
+ }
+
+ if(strcmp(mName, "coffin") == 0)
+ {
+ mVehicleID = VehicleEnum::COFFIN;
+ }
+
+ if(strcmp(mName, "hallo") == 0)
+ {
+ mVehicleID = VehicleEnum::HALLO;
+ }
+
+ if(strcmp(mName, "ship") == 0)
+ {
+ mVehicleID = VehicleEnum::SHIP;
+ m_IsSimpleShadow = false;
+ mNoSkid = true;
+ }
+
+ if(strcmp(mName, "witchcar") == 0)
+ {
+ mVehicleID = VehicleEnum::WITCHCAR;
+ mNoSkid = true;
+ }
+
+ if(strcmp(mName, "huskA") == 0)
+ {
+ mVehicleID = VehicleEnum::HUSKA;
+ }
+
+ if(strcmp(mName, "atv_v") == 0)
+ {
+ mVehicleID = VehicleEnum::ATV_V;
+ }
+
+ if(strcmp(mName, "dune_v") == 0)
+ {
+ mVehicleID = VehicleEnum::DUNE_V;
+ }
+
+ if(strcmp(mName, "hype_v") == 0)
+ {
+ mVehicleID = VehicleEnum::HYPE_V;
+ }
+
+ if(strcmp(mName, "knigh_v") == 0)
+ {
+ mVehicleID = VehicleEnum::KNIGH_V;
+ }
+
+ if(strcmp(mName, "mono_v") == 0)
+ {
+ mVehicleID = VehicleEnum::MONO_V;
+ mNoSkid = true;
+ }
+
+ if(strcmp(mName, "oblit_v") == 0)
+ {
+ mVehicleID = VehicleEnum::OBLIT_V;
+ }
+
+ if(strcmp(mName, "rocke_v") == 0)
+ {
+ mVehicleID = VehicleEnum::ROCKE_V;
+ mNoFrontSkid = true;
+ }
+
+ if(strcmp(mName, "ambul") == 0)
+ {
+ mVehicleID = VehicleEnum::AMBUL;
+ }
+
+ if(strcmp(mName, "burnsarm") == 0)
+ {
+ mVehicleID = VehicleEnum::BURNSARM;
+ }
+
+ if(strcmp(mName, "fishtruc") == 0)
+ {
+ mVehicleID = VehicleEnum::FISHTRUC;
+ }
+
+ if(strcmp(mName, "garbage") == 0)
+ {
+ mVehicleID = VehicleEnum::GARBAGE;
+ }
+
+ if(strcmp(mName, "icecream") == 0)
+ {
+ mVehicleID = VehicleEnum::ICECREAM;
+ }
+
+ if(strcmp(mName, "IStruck") == 0)
+ {
+ mVehicleID = VehicleEnum::ISTRUCK;
+ }
+
+ if(strcmp(mName, "nuctruck") == 0)
+ {
+ mVehicleID = VehicleEnum::NUCTRUCK;
+ }
+
+ if(strcmp(mName, "pizza") == 0)
+ {
+ mVehicleID = VehicleEnum::PIZZA;
+ }
+
+ if(strcmp(mName, "schoolbu") == 0)
+ {
+ mVehicleID = VehicleEnum::SCHOOLBU;
+ }
+
+ if(strcmp(mName, "votetruc") == 0)
+ {
+ mVehicleID = VehicleEnum::VOTETRUC;
+ }
+
+ if(strcmp(mName, "glastruc") == 0)
+ {
+ mVehicleID = VehicleEnum::GLASTRUC;
+ }
+
+ if(strcmp(mName, "cFire_v") == 0)
+ {
+ mVehicleID = VehicleEnum::CFIRE_V;
+ }
+
+ if(strcmp(mName, "cBone") == 0)
+ {
+ mVehicleID = VehicleEnum::CBONE;
+ }
+
+
+ // the best for last!
+ if(strcmp(mName, "redbrick") == 0)
+ {
+ mVehicleID = VehicleEnum::REDBRICK;
+ }
+
+
+}
+
+
+
+// call back for debug watcher:
+//typedef void (*RADDEBUGWATCH_CALLBACK)( void* userData );
+void DebugWatchVehicleTuningCallback(void* userData)
+{
+ ((Vehicle*)userData)->CalculateValuesBasedOnDesignerParams();
+}
+
+
+void InflictDamageHoodCallback(void* userData)
+{
+ ((Vehicle*)userData)->DebugInflictDamageHood();
+}
+
+void InflictDamageBackCallback(void* userData)
+{
+ ((Vehicle*)userData)->DebugInflictDamageBack();
+}
+
+void InflictDamageDriverSideCallback(void* userData)
+{
+ ((Vehicle*)userData)->DebugInflictDamageDriverSide();
+}
+
+void InflictDamagePassengerSideCallback(void* userData)
+{
+ ((Vehicle*)userData)->DebugInflictDamagePassengerSide();
+}
+
+
+//=============================================================================
+// Vehicle::SetupRadDebugWatchStuff
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetupRadDebugWatchStuff()
+{
+ radDbgWatchAddFloat(&mSpeedKmh, "speed kmh", mName, NULL, (void*)this, 0.0f, 1000.0f); // just want to observe this
+ radDbgWatchAddFloat(&mForceToDetachCollectible, "Detach Collectible Force", mName, DebugWatchVehicleTuningCallback, (void*)this, 5000.0f, 50000.0f );
+
+ // Add the static explosion damage variables to the watcher. Make sure to only add them once
+ // under group "Vehicle"
+ static bool staticVariablesInitialized = false;
+ if ( !staticVariablesInitialized)
+ {
+ radDbgWatchAddFloat(&Vehicle::s_DamageFromExplosion, "Damage from Explosion (other)", "Vehicle", NULL, NULL, 0, 10.0f );
+ radDbgWatchAddFloat(&Vehicle::s_DamageFromExplosionPlayer, "Damage from Explosion (player)", "Vehicle", NULL, NULL, 0, 10.0f );
+ staticVariablesInitialized = true;
+ }
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpMass), "mass", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 10000.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpGasScale), "gas scale", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 30.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpSlipGasScale), "slip gas scale", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 30.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpHighSpeedGasScale), "high speed gas scale", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 30.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpGasScaleSpeedThreshold), "gas scale threshold", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpBrakeScale), "brake scale", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 30.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpTopSpeedKmh), "top speed kmh", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 300.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpMaxWheelTurnAngle), "max wheel turn angle", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 55.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpHighSpeedSteeringDrop), "high speed steering drop", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpTireLateralStaticGrip), "tire grip", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 15.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpTireLateralResistanceNormal), "normal steering", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 500.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpTireLateralResistanceSlip), "slip steering", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 500.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpEBrakeEffect), "ebrake effect", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpTireLateralResistanceSlipNoEBrake), "slip steering without ebrake", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 500.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpSlipEffectNoEBrake), "slip effect without ebrake", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpCMOffset.x), "cmoffset x", mName, DebugWatchVehicleTuningCallback, (void*)this, -1.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpCMOffset.y), "cmoffset y", mName, DebugWatchVehicleTuningCallback, (void*)this, -1.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpCMOffset.z), "cmoffset z", mName, DebugWatchVehicleTuningCallback, (void*)this, -1.0f, 1.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpSuspensionLimit), "suspension limit", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpSpringk), "spring k", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpDamperc), "damper c", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpSuspensionYOffset), "suspension Y Offset", mName, DebugWatchVehicleTuningCallback, (void*)this, -1.0f, 1.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpBurnoutRange), "burnout range", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpMaxSpeedBurstTime), "max speed burst time", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 10.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpDonutTorque), "donut torque", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 20.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpWeebleOffset), "weeble offset", mName, DebugWatchVehicleTuningCallback, (void*)this, -3.0f, 3.0f );
+
+ radDbgWatchAddFloat(&(mDesignerParams.mDpWheelieRange), "wheelie range", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 1.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpWheelieYOffset), "wheelie Y offset", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, 2.0f );
+ radDbgWatchAddFloat(&(mDesignerParams.mDpWheelieZOffset), "wheelie Z offset", mName, DebugWatchVehicleTuningCallback, (void*)this, 0.0f, -2.0f );
+
+ radDbgWatchAddFunction( "Inflict Damage Hood", &InflictDamageHoodCallback, (void*)this, mName );
+ radDbgWatchAddFunction( "Inflict Damage Back", &InflictDamageBackCallback, (void*)this, mName );
+ radDbgWatchAddFunction( "Inflict Damage Driver Side", &InflictDamageDriverSideCallback, (void*)this, mName );
+ radDbgWatchAddFunction( "Inflict Damage Passenger Side", &InflictDamagePassengerSideCallback, (void*)this, mName );
+
+}
+
+//=============================================================================
+// Vehicle::~Vehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle::~Vehicle()
+{
+ if(GetGameplayManager()->GetCurrentVehicle() == this)
+ {
+ GetGameplayManager()->UnregisterVehicleHUDIcon();
+ }
+
+ delete mGeometryVehicle;
+
+ delete mPhysicsLocomotion;
+ delete mTrafficLocomotion;
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ delete mWheels[i];
+ mSuspensionJointDrivers[i]->Release();
+
+ // TODO - need to wrap these somehow in case they don't exist?
+ if(mInertialJointDrivers[i])
+ {
+ mInertialJointDrivers[i]->Release();
+ }
+ if(mPhysicsJointMatrixModifiers[i])
+ {
+ mPhysicsJointMatrixModifiers[i]->Release();
+ }
+
+ }
+
+ delete[] mGearRatios;
+ delete[] mJointIndexToWheelMapping;
+ if(mJointIndexToInertialJointDriverMapping)
+ {
+ delete[] mJointIndexToInertialJointDriverMapping;
+ }
+
+ // these moved to a method that can be called from VehicleCentral::RemoveVehicleFromActiveList
+ //RemoveSelfFromCollisionManager();
+ //GetWorldPhysicsManager()->FreeCollisionAreaIndex(mCollisionAreaIndex);
+ //mCollisionAreaIndex = -1;
+
+ mGroundPlaneWallVolume->Release();
+ mGroundPlanePhysicsProperties->Release();
+ mPhysicsProperties->Release();
+
+ if(mRootMatrixDriver)
+ {
+ mRootMatrixDriver->Release();
+ }
+
+ mPoseEngine->Release();
+
+ mGroundPlaneSimState->Release();
+
+ //p3d::inventory->SelectSection("Level");
+ //p3d::inventory->Remove(mSimStateArticulated->GetCollisionObject());
+ //p3d::inventory->Remove(mSimStateArticulated->GetSimulatedObject());
+
+
+ tRefCounted::Release(mSimStateArticulatedInCar);
+ tRefCounted::Release(mSimStateArticulatedOutOfCar);
+
+
+ int id = mpEventLocator->GetData();
+ GetActionButtonManager()->RemoveActionByIndex( id );
+
+
+ mpEventLocator->Release();
+
+ if(mpDriver)
+ {
+ // DO NOT remove a pedestrian manually!
+ // We need them for recycling within the level.
+ if( mpDriver->GetRole() != Character::ROLE_PEDESTRIAN )
+ {
+ GetCharacterManager()->RemoveCharacter(mpDriver);
+ }
+ tRefCounted::Release(mpDriver);
+ }
+
+ GetCharacterManager()->ClearTargetVehicle(this);
+
+ rAssert( mName != NULL );
+ delete[] mName;
+
+ if(mVehicleEventListener)
+ {
+ delete mVehicleEventListener;
+ }
+}
+
+
+
+//=============================================================================
+// Vehicle::CreateLocomotions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::CreateLocomotions()
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+ // is this vehicle being setup for A or B?
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mPhysicsLocomotion = new(gma)PhysicsLocomotion(this);
+ mTrafficLocomotion = new(gma)TrafficLocomotion(this);
+
+ //?
+ SetLocomotion( mLoco );
+MEMTRACK_POP_GROUP( "Vehicle" );
+}
+
+
+
+
+//=============================================================================
+// Vehicle::InitWheelsAndLinkSuspensionJointDrivers
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::InitWheelsAndLinkSuspensionJointDrivers()
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+ CollisionObject* collObj = mSimStateArticulated->GetCollisionObject();
+ CollisionVolume* collVol = collObj->GetCollisionVolume();
+ rAssert(collVol);
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ CollisionVolume* sub = collVol->GetSubCollisionVolume(mWheelToJointIndexMapping[i], true); // true because we only care about the local sub volume
+
+ // sub should be of type SphereVolumeType
+ rAssert(sub->Type() == SphereVolumeType);
+
+ float radius = ((SphereVolume*)sub)->GetRadius();
+
+ mWheels[i] = new(gma)Wheel;
+
+ // now we can init the goddamn wheel
+ if(i < 2) // todo - way to NOT hardcode this for 4 wheels????
+ {
+ mWheels[i]->Init(this, i, radius, false, true);
+ }
+ else
+ {
+ mWheels[i]->Init(this, i, radius, true, false);
+ // try all wheel drive
+ //mWheels[i]->Init(this, i, radius, true, true);
+ }
+
+ mSuspensionJointDrivers[i] = new(gma)SuspensionJointDriver(mWheels[i], mWheelToJointIndexMapping[i]);
+
+ mSuspensionJointDrivers[i]->AddRef();
+ mPoseEngine->AddPoseDriver(1, mSuspensionJointDrivers[i]);
+ }
+MEMTRACK_POP_GROUP("Vehicle");
+}
+
+
+//=============================================================================
+// Vehicle::InitFlappingJoints
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+bool Vehicle::InitFlappingJoints()
+{
+
+ tPose* p3dPose = mGeometryVehicle->GetP3DPose();
+ rAssert(p3dPose);
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mJointIndexToInertialJointDriverMapping = new(gma) int[p3dPose->GetNumJoint()];
+
+ // TODO ?
+ // ok to use memset?
+ memset(mJointIndexToInertialJointDriverMapping, -1, (sizeof(int) * p3dPose->GetNumJoint()) );
+
+
+ int count = 0;
+
+ rmt::Vector axis;
+ rmt::Vector rotAxis;
+
+ bool atLeastOneFlappingJointPresent = false;
+
+
+ //-----------------
+ // driver-side door
+ //-----------------
+ axis.Set(0.0f, 0.0f, -1.0f);
+ rotAxis.Set(0.0f, 1.0f, 0.0f);
+ if(AddFlappingJoint("DoorDTrans", "DoorDRot", axis, rotAxis, count, &mDoorDJoint))
+ {
+ // override default settings
+
+ mInertialJointDrivers[count]->SetSpeedRate(15.0f);
+ mInertialJointDrivers[count]->SetAccelRate(180.0f);
+ mInertialJointDrivers[count]->SetGravityRate(0.5f);
+ mInertialJointDrivers[count]->SetCentrifugalRate(2.0f);
+
+ atLeastOneFlappingJointPresent = true;
+ }
+
+ //--------------------
+ // passenger-side door
+ //--------------------
+ count++;
+ rotAxis.Set(0.0f, -1.0f, 0.0f);
+ if(AddFlappingJoint("DoorPTrans", "DoorPRot", axis, rotAxis, count, &mDoorPJoint))
+ {
+ mInertialJointDrivers[count]->SetSpeedRate(15.0f);
+ mInertialJointDrivers[count]->SetAccelRate(180.0f);
+ mInertialJointDrivers[count]->SetGravityRate(0.5f);
+ mInertialJointDrivers[count]->SetCentrifugalRate(2.0f);
+
+ atLeastOneFlappingJointPresent = true;
+ }
+
+ //-----
+ // hood
+ //-----
+ count++;
+ axis.Set(0.0f, 0.0f, 1.0f);
+ rotAxis.Set(-1.0f, 0.0f, 0.0f);
+ if(AddFlappingJoint("HoodTrans", "HoodRot", axis, rotAxis, count, &mHoodJoint))
+ {
+ mInertialJointDrivers[count]->SetSpeedRate(3.0f);
+ mInertialJointDrivers[count]->SetAccelRate(300.0f);
+ mInertialJointDrivers[count]->SetGravityRate(1.0f);
+ mInertialJointDrivers[count]->SetCentrifugalRate(1.0f);
+
+ atLeastOneFlappingJointPresent = true;
+ }
+
+ //------
+ // trunk
+ //------
+ count++;
+ axis.Set(0.0f, 0.0f, -1.0f);
+ rotAxis.Set(1.0f, 0.0f, 0.0f);
+ if(AddFlappingJoint("TrunkTrans", "TrunkRot", axis, rotAxis, count, &mTrunkJoint))
+ {
+ mInertialJointDrivers[count]->SetSpeedRate(12.0f);
+ mInertialJointDrivers[count]->SetAccelRate(180.0f);
+ mInertialJointDrivers[count]->SetGravityRate(1.0f);
+ mInertialJointDrivers[count]->SetCentrifugalRate(1.0f);
+
+ atLeastOneFlappingJointPresent = true;
+ }
+
+ return atLeastOneFlappingJointPresent;
+
+}
+
+
+//=============================================================================
+// Vehicle::AddFlappingJoint
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (const char* transJointName, const char* rotJointName, rmt::Vector& axis, rmt::Vector& rotAxis, int count)
+//
+// Return: void
+//
+//=============================================================================
+bool Vehicle::AddFlappingJoint(const char* transJointName, const char* rotJointName, rmt::Vector& axis, rmt::Vector& rotAxis, int count, int* collisionJointIndex)
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+
+ //SkeletonInfo* skelInfo = p3d::find<SkeletonInfo>(mName);
+ SkeletonInfo* skelInfo = mPhObj->GetSkeletonInfo ();
+ rAssert(skelInfo);
+
+ // release debugging:
+ if(skelInfo == 0)
+ {
+ char buffy[64];
+ sprintf(buffy, "can't find skelInfo for %s\n", mName);
+ rReleaseString(buffy);
+ }
+
+
+ tPose* p3dPose = mGeometryVehicle->GetP3DPose();
+ rAssert(p3dPose);
+
+
+ int indexTrans;
+ int indexRot;
+
+ indexTrans = p3dPose->FindJointIndex(transJointName);
+ indexRot = p3dPose->FindJointIndex(rotJointName);
+
+ *collisionJointIndex = indexRot;
+
+ if(indexTrans == -1 || indexRot == -1)
+ {
+ MEMTRACK_POP_GROUP( "Vehicle" );
+ return false;
+ }
+
+ #ifdef RAD_DEBUG // wrap in define?
+ tPose::Joint* transJoint = p3dPose->GetJoint(indexTrans);
+ tPose::Joint* rotJoint = p3dPose->GetJoint(indexRot);
+ rAssert(rotJoint->parent == transJoint);
+ #endif
+
+ skelInfo->SetJointAxis(indexTrans, axis, 1.0f);
+ skelInfo->SetJointAxis(indexRot, axis, 1.0f);
+
+ skelInfo->SetJointRotAxis(indexRot, rotAxis);
+
+ // add new driver...
+ mJointIndexToInertialJointDriverMapping[indexRot] = count;
+
+ PhysicsJoint* phizJoint = mPhObj->GetJoint(indexRot);
+ rAssert(phizJoint);
+
+ // test
+ //phizJoint->SetInvStiffness(0.5f);
+
+
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mInertialJointDrivers[count] = new(gma) PhysicsJointInertialEffector(phizJoint);
+
+ mInertialJointDrivers[count]->AddRef();
+
+ // defaults
+ // override shortly after this...
+
+ mInertialJointDrivers[count]->SetSpeedRate(15.0f);
+ //mInertialJointDrivers[count]->SetAccelRate(20.0f);
+ mInertialJointDrivers[count]->SetAccelRate(180.0f);
+ mInertialJointDrivers[count]->SetGravityRate(1.0f);
+ mInertialJointDrivers[count]->SetCentrifugalRate(2.0f);
+
+ // new
+ // start out disabled
+
+ mPhObj->GetJoint(indexRot)->SetInvStiffness(0.0f);
+ mPhObj->GetJoint(indexRot)->ResetDeformation();
+
+ mInertialJointDrivers[count]->SetIsEnabled(false);
+
+ // debug
+ //mInertialJointDrivers[count]->SetIsEnabled(true);
+
+
+ /*
+ mInertialJointDrivers[count]->SetSpeedRate(0.0f);
+ //mInertialJointDrivers[count]->SetAccelRate(20.0f);
+ mInertialJointDrivers[count]->SetAccelRate(0.0f);
+ mInertialJointDrivers[count]->SetGravityRate(0.0f);
+ mInertialJointDrivers[count]->SetCentrifugalRate(2.0f);
+ */
+
+
+ // TODO - which pose engine pass?
+ mPoseEngine->AddPoseDriver(2, mInertialJointDrivers[count]);
+
+ // also need this other jointmatrixmodifier thing...
+ mPhysicsJointMatrixModifiers[count] = new(gma) PhysicsJointMatrixModifier(phizJoint);
+
+ mPhysicsJointMatrixModifiers[count]->AddRef();
+
+ mPoseEngine->AddPoseDriver(2, mPhysicsJointMatrixModifiers[count]);
+
+ MEMTRACK_POP_GROUP("Vehicle");
+ return true;
+}
+
+
+
+//=============================================================================
+// Vehicle::InitGears
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::InitGears()
+{
+ mNumGears = 6; // TODO - not sure we actually want designers to worry about this?
+ // maybe, they can just choose number of gears and there will be pre-defined ratios??
+ //
+ // recall: all this gear shit is just for sound and does not affect performance
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mGearRatios = new(gma) float[mNumGears];
+
+ mFinalDriveRatio = 3.42f;
+
+ mGearRatios[0] = 2.97f; // 1st
+ //mGearRatios[1] = 2.07f; // 2nd
+ mGearRatios[1] = 1.8f; // 2nd
+ mGearRatios[2] = 1.43f; // 3rd
+ mGearRatios[3] = 1.00f; // 4th
+ mGearRatios[4] = 0.84f; // 5th
+ mGearRatios[5] = 0.56f; // 6th
+
+
+
+/* some stuff fromt trav for a corvette
+
+ Transmission..........6-speed manual
+ Final-drive ratio..........3.42:1, limited slip
+ Gear..........Ratio..........Mph/1000 rpm..........Max. test speed
+ I..........2.97..........7.4..........48 mph (6500 rpm)
+ II..........2.07..........10.6..........69 mph (6500 rpm)
+ III..........1.43..........15.3..........100 mph (6500 rpm)
+ IV..........1.00..........21.9..........143 mph (6500 rpm)
+ V..........0.84..........26.1..........168 mph (6450 rpm)
+ VI..........0.56..........39.2..........155 mph (4000 rpm)
+
+*/
+
+
+}
+
+
+//=============================================================================
+// Vehicle::SetupPhysicsProperties
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::SetupPhysicsProperties()
+{
+ // called when we first create the sim state, as well as when
+ // we set new designer paramters.
+
+ //float volume = mPhObj->GetVolume();
+
+ float volume = ((ArticulatedPhysicsObject*)(mSimStateArticulatedInCar->GetSimulatedObject(-1)))->GetVolume();
+
+ // TODO - verify this is coming back in the correct units
+
+ // can't set mass directly
+ float density = mDesignerParams.mDpMass / volume;
+
+ // TODO - which of these do we actually want to allow designers to modify?
+ //mPhysicsProperties->SetFrictCoeffCGS(0.8f);
+ //mPhysicsProperties->SetRestCoeffCGS(1.05f);
+ //mPhysicsProperties->SetTangRestCoeffCGS(0.0f);
+
+ // these values slide you along walls a little nicer
+ mPhysicsProperties->SetFrictCoeffCGS(0.3f);
+ mPhysicsProperties->SetRestCoeffCGS(1.15f);
+ mPhysicsProperties->SetTangRestCoeffCGS(-0.6f);
+
+
+ //mPhysicsProperties->SetDensityCGS(density / 1000000.0f); // our density is in g / m^3
+ mPhysicsProperties->SetDensityCGS(density); // our density is in g / m^3
+
+ //mSimStateArticulated->SetPhysicsProperties(mPhysicsProperties);
+
+ mSimStateArticulatedInCar->SetPhysicsProperties(mPhysicsProperties);
+ if(mSimStateArticulatedOutOfCar)
+ {
+ mSimStateArticulatedOutOfCar->SetPhysicsProperties(mPhysicsProperties);
+ }
+
+ mPhObj->SetInvTWDissip(0);
+ mPhObj->SetDissipationInternalRate(0);
+ mPhObj->SetDissipationDeformationRate(0); //no good Martin
+
+
+ // TODO - where to put this!
+ // should i even use it?
+
+ // TODO
+ // temp hack to fix lack of phizsim
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();// this or CGS ?
+
+ //float gravity_y = 9.81f;
+ float gravity_y = -1.0f * gravity.y;
+
+ //rmt::Vector gravity = SG::phizSim.mSimEnvironment->Gravity();
+ //mSuspensionRestValue = mDesignerParams.mDpMass * rmt::Fabs(gravity.y) * 0.25f;
+ mSuspensionRestValue = mDesignerParams.mDpMass * gravity_y * 0.25f;
+
+ // new thing aimed at minimizing strage whipping effects on the car when the wheels bottom out
+ // and you get insane values calculated for suspension force
+ mSuspensionMaxValue = mDesignerParams.mDpMass * gravity_y * 2.5f; // TODO - find the right value
+
+
+ mCMOffset = mOriginalCMOffset;
+
+ mCMOffset.Add(mDesignerParams.mDpCMOffset);
+
+ ((ArticulatedPhysicsObject*)(mSimStateArticulatedInCar->GetSimulatedObject(-1)))->SetExternalCMOffset(mCMOffset);
+
+ //mPhObj->SetExternalCMOffset(mCMOffset);
+
+
+}
+
+
+//=============================================================================
+// Vehicle::InitGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::InitGroundPlane()
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+
+ rmt::Vector p(0.0f, 0.0f, 0.0f);
+ rmt::Vector n(0.0f, 1.0f, 0.0f);
+
+ HeapMgr()->PushHeap (gma);
+
+ mGroundPlaneWallVolume = new WallVolume(p, n);
+ mGroundPlaneWallVolume->AddRef();
+
+ mGroundPlaneSimState = (sim::ManualSimState*)(SimState::CreateManualSimState(mGroundPlaneWallVolume));
+ mGroundPlaneSimState->AddRef();
+
+ mGroundPlaneSimState->GetCollisionObject()->SetManualUpdate(true);
+ mGroundPlaneSimState->GetCollisionObject()->SetAutoPair(false);
+ mGroundPlaneSimState->GetCollisionObject()->SetIsStatic(true);
+
+ char buffy[128];
+ sprintf(buffy, "%s_groundplane", mName);
+
+ mGroundPlaneSimState->GetCollisionObject()->SetName(buffy);
+
+ mGroundPlaneSimState->mAIRefIndex = this->GetGroundPlaneAIRef();
+ mGroundPlaneSimState->mAIRefPointer = (void*)this;
+
+ mGroundPlanePhysicsProperties = new PhysicsProperties;
+ mGroundPlanePhysicsProperties->AddRef();
+
+ mGroundPlanePhysicsProperties->SetFrictCoeffCGS(0.8f);
+ mGroundPlanePhysicsProperties->SetRestCoeffCGS(1.15f);
+ mGroundPlanePhysicsProperties->SetTangRestCoeffCGS(0.0f);
+
+ mGroundPlaneSimState->SetPhysicsProperties(mGroundPlanePhysicsProperties);
+
+ HeapMgr()->PopHeap (gma);
+
+/*
+
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+
+ rmt::Vector p, n;
+ p.Set(0.0f, 0.0f, 0.0f);
+ //n.Set(0.0f, 1.0f, 0.0f);
+ n.Set(0.0f, 0.0f, 1.0f);
+
+ mGroundPlaneWallVolume = new(gma) WallVolume(p, n);
+
+ mGroundPlaneWallVolume->AddRef();
+
+ mGroundPlaneSimState = SimState::CreateSimState(mGroundPlaneWallVolume);
+ mGroundPlaneSimState->AddRef();
+ //mGroundPlaneSimState->GetCollisionObject()->SetIsStatic(true);
+ mGroundPlaneSimState->GetCollisionObject()->SetAutoPair(false); // TODO - does not work yet
+ // TODO - name?
+
+ char buffy[128];
+ sprintf(buffy, "%s_groundplane", mName);
+
+ mGroundPlaneSimState->GetCollisionObject()->SetName(buffy);
+
+ mGroundPlaneSimState->mAIRefIndex = this->GetGroundPlaneAIRef();
+ mGroundPlaneSimState->mAIRefPointer = (void*)this;
+
+ mGroundPlanePhysicsProperties = new(gma) PhysicsProperties;
+ mGroundPlanePhysicsProperties->AddRef();
+
+ mGroundPlanePhysicsProperties->SetFrictCoeffCGS(0.8f);
+ mGroundPlanePhysicsProperties->SetRestCoeffCGS(1.05f);
+ mGroundPlanePhysicsProperties->SetTangRestCoeffCGS(0.0f);
+
+ mGroundPlaneSimState->SetPhysicsProperties(mGroundPlanePhysicsProperties);
+
+
+*/
+
+
+
+MEMTRACK_POP_GROUP("Vehicle");
+}
+
+
+
+
+//=============================================================================
+// Vehicle::FetchWheelMapping
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::FetchWheelMapping()
+{
+ // should have already done these asserts, but just in case things
+ // get moved around a bit...
+
+ rAssert(mGeometryVehicle);
+
+ tPose* p3dPose = mGeometryVehicle->GetP3DPose();
+ rAssert(p3dPose);
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mJointIndexToWheelMapping = new(gma) int[p3dPose->GetNumJoint()];
+
+ memset(mJointIndexToWheelMapping, -1, (sizeof(int) * p3dPose->GetNumJoint()) );
+
+ //
+ // naming convention so far:
+ //
+ // should have joints named:
+ //
+ // w0, w1, w2, w3
+
+ char buffy[8];
+ memset(buffy, 0, sizeof(buffy));
+
+
+ int i;
+ for(i = 0; i < 4; i++)
+ {
+ // TODO!
+ // safe to use sprintf ??
+ sprintf(buffy, "w%d", i);
+ mWheelToJointIndexMapping[i] = p3dPose->FindJointIndex(buffy);
+
+ // for convenience we can look up either way....
+ mJointIndexToWheelMapping[mWheelToJointIndexMapping[i]] = i;
+
+ tPose::Joint* joint = p3dPose->GetJoint(mWheelToJointIndexMapping[i]);
+
+ // fill mSuspensionRestPoints[4] with the transform row of the joint object space
+ // matrix
+
+ mSuspensionRestPointsFromFile[i] = joint->objectMatrix.Row(3);
+
+ mSuspensionRestPoints[i] = mSuspensionRestPointsFromFile[i];
+
+ mSuspensionRestPoints[i].y += mDesignerParams.mDpSuspensionYOffset;
+
+ // used to be other stuff in here for radius, and Wheels, and suspensionjointdrivers
+
+ }
+
+ // passenger/driver locations
+
+ int passengerIndex = p3dPose->FindJointIndex( "pl" );
+ int driverIndex = p3dPose->FindJointIndex( "dl" );
+
+ if(passengerIndex != -1)
+ {
+ tPose::Joint* joint = p3dPose->GetJoint( passengerIndex );
+ mPassengerLocation = joint->objectMatrix.Row(3);
+ }
+ else
+ {
+ mPassengerLocation.Set( 0.5f, -0.6f, 0.01f );
+ }
+
+ if(driverIndex != -1)
+ {
+ tPose::Joint* joint = p3dPose->GetJoint( driverIndex );
+ mDriverLocation = joint->objectMatrix.Row(3);
+ }
+ else
+ {
+ mDriverLocation.Set( -0.5f, -0.6f, 0.01f );
+ }
+
+
+ mWheelBase = mSuspensionRestPoints[3].z - mSuspensionRestPoints[1].z;
+
+ // smoke location
+
+ //int smokeIndex = p3dPose->FindJointIndex("smoke");
+ int smokeIndex = p3dPose->FindJointIndex("sl");
+ tPose::Joint* joint = p3dPose->GetJoint(smokeIndex);
+ if (joint)
+ {
+ mSmokeOffset = joint->objectMatrix.Row(3);
+ }
+
+}
+
+
+//=============================================================================
+// Vehicle::GetWheel0Offset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: rmt
+//
+//=============================================================================
+rmt::Vector Vehicle::GetWheel0Offset()
+{
+ // this is not accurate as wheel is lurching around but should be close enough for smoke effects?
+ rmt::Vector temp = mSuspensionRestPoints[0];
+ temp.y -= mWheels[0]->mRadius;
+ return temp;
+}
+
+
+//=============================================================================
+// Vehicle::GetWheel1Offset
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: rmt
+//
+//=============================================================================
+rmt::Vector Vehicle::GetWheel1Offset()
+{
+ rmt::Vector temp = mSuspensionRestPoints[1];
+ temp.y -= mWheels[1]->mRadius;
+ return temp;
+}
+
+//=============================================================================
+// Vehicle::InitSimState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::InitSimState(SimEnvironment* se)
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+ CreatePoseEngine();
+ rAssert(mPoseEngine);
+
+ // The section stuff here is a hack.
+ // Tracked to ATG as bug 1259.
+ //
+ p3d::inventory->PushSection ();
+ p3d::inventory->AddSection (SKELCACHE);
+ p3d::inventory->SelectSection (SKELCACHE);
+ //mSimStateArticulated = SimStateArticulated::CreateSimStateArticulated(mName, mPoseEngine->GetPose(), SimStateAttribute_Default, NULL);
+
+ SimStateArticulated* newSimState = SimStateArticulated::CreateSimStateArticulated(mName, mPoseEngine->GetPose(), SimStateAttribute_Default, NULL);
+ rAssert( mSimStateArticulatedInCar == NULL );
+ tRefCounted::Assign( mSimStateArticulatedInCar, newSimState );
+ rAssert( mSimStateArticulatedInCar != NULL );
+
+
+ mSimStateArticulatedInCar->mAIRefIndex = PhysicsAIRef::redBrickVehicle;
+
+ mSimStateArticulatedInCar->mAIRefPointer = (void*)this;
+
+
+ ((ArticulatedPhysicsObject*)(mSimStateArticulatedInCar->GetSimulatedObject(-1)))->SetSimEnvironment(se);
+
+
+
+
+ // new test:
+ char buffy[128];
+ sprintf(buffy, "%sBV", mName);
+ rAssert( mSimStateArticulatedOutOfCar == NULL );
+ mSimStateArticulatedOutOfCar = SimStateArticulated::CreateSimStateArticulated(buffy, mPoseEngine->GetPose(), SimStateAttribute_Default, NULL);
+
+ if(mSimStateArticulatedOutOfCar)
+ {
+
+ mSimStateArticulatedOutOfCar->AddRef();
+ mSimStateArticulatedOutOfCar->mAIRefIndex = PhysicsAIRef::redBrickVehicle;
+
+ mSimStateArticulatedOutOfCar->mAIRefPointer = (void*)this;
+ ((ArticulatedPhysicsObject*)(mSimStateArticulatedOutOfCar->GetSimulatedObject(-1)))->SetSimEnvironment(se);
+
+ }
+
+
+ p3d::inventory->PopSection ();
+
+ // finish initialization with this... then switch back
+ mSimStateArticulated = mSimStateArticulatedInCar;
+
+
+
+
+
+
+ mPhObj = (ArticulatedPhysicsObject*)(mSimStateArticulated->GetSimulatedObject(-1));
+
+ // fetch once only ever
+ // seven fucking days
+ //mOriginalCMOffset = mPhObj->GetExternalCMOffset();
+
+ mOriginalCMOffset.x = 0.0f;
+ mOriginalCMOffset.y = 0.0f;
+ mOriginalCMOffset.z = 0.0f;
+
+
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mPhysicsProperties = new(gma) PhysicsProperties; // TODO - who owns - ie. who is responsible to delete
+
+ mPhysicsProperties->AddRef();
+
+/*
+ for (int i=0; i<mPhObj->NumSimJoints(); i++)
+ {
+ PhysicsJoint* joint = mPhObj->GetSimJoint(i);
+ P3DASSERT(joint);
+ mPoseEngine->AddPoseDriver(2, new PhysicsJointMatrixModifier(joint));
+ }
+
+ sim::PhysicsJointMatrixModifier mPhysicsJointMatrixModifiers[4];
+*/
+MEMTRACK_POP_GROUP( "Vehicle" );
+}
+
+
+//=============================================================================
+// Vehicle::CreatePoseEngineOutOfCar
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+/*
+void Vehicle::CreatePoseEngineOutOfCar()
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+ // TODO - wtf?
+ //const int PoseEngineSimPass = 1;
+ const int PoseEngineSimPass = 2;
+
+ rAssert(mGeometryVehicle);
+
+
+
+ tPose* p3dPose = mGeometryVehicle->GetP3DPose();
+ rAssert(p3dPose);
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mPoseEngine = new(gma) poser::PoseEngine(p3dPose, PoseEngineSimPass+1, p3dPose->GetNumJoint());
+ mRootMatrixDriver = new(gma) RootMatrixDriver(&mTransform);
+
+ mPoseEngine->AddRef();
+ mRootMatrixDriver->AddRef();
+ mPoseEngine->AddPoseDriver(1, mRootMatrixDriver); // 0 is the pass
+
+MEMTRACK_POP_GROUP();
+
+}
+*/
+//=============================================================================
+// Vehicle::CreatePoseEngine
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void Vehicle::CreatePoseEngine()
+{
+MEMTRACK_PUSH_GROUP( "Vehicle" );
+ // TODO - wtf?
+ //const int PoseEngineSimPass = 1;
+ const int PoseEngineSimPass = 2;
+
+ rAssert(mGeometryVehicle);
+
+
+
+ tPose* p3dPose = mGeometryVehicle->GetP3DPose();
+ rAssert(p3dPose);
+
+ GameMemoryAllocator gma = GetGameplayManager()->GetCurrentMissionHeap();
+ mPoseEngine = new(gma) poser::PoseEngine(p3dPose, PoseEngineSimPass+1, p3dPose->GetNumJoint());
+ mRootMatrixDriver = new(gma) RootMatrixDriver(&mTransform);
+
+ mPoseEngine->AddRef();
+ mRootMatrixDriver->AddRef();
+ //mPoseEngine->AddPoseDriver(1, mRootMatrixDriver); // 0 is the pass
+ mPoseEngine->AddPoseDriver(0, mRootMatrixDriver); // 0 is the pass
+
+MEMTRACK_POP_GROUP("Vehicle");
+}
diff --git a/game/code/worldsim/redbrick/vehiclelocomotion.cpp b/game/code/worldsim/redbrick/vehiclelocomotion.cpp
new file mode 100644
index 0000000..4cf7f2b
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclelocomotion.cpp
@@ -0,0 +1,46 @@
+/*===========================================================================
+ vehiclelocomotion.cpp
+
+ created April 24, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+
+#include <worldsim/redbrick/vehiclelocomotion.h>
+
+
+
+/*
+//------------------------------------------------------------------------
+VehicleLocomotion::VehicleLocomotion()
+{
+ //
+}
+
+
+
+//------------------------------------------------------------------------
+VehicleLocomotion::~VehicleLocomotion()
+{
+ //
+}
+
+
+//------------------------------------------------------------------------
+void VehicleLocomotion::PreCollisionPrep(Vehicle* vehicle)
+{
+
+}
+
+
+//------------------------------------------------------------------------
+void VehicleLocomotion::Update(Vehicle* vehicle, float dt)
+{
+
+}
+*/ \ No newline at end of file
diff --git a/game/code/worldsim/redbrick/vehiclelocomotion.h b/game/code/worldsim/redbrick/vehiclelocomotion.h
new file mode 100644
index 0000000..a700dc6
--- /dev/null
+++ b/game/code/worldsim/redbrick/vehiclelocomotion.h
@@ -0,0 +1,41 @@
+/*===========================================================================
+ vehiclelocomotion.h
+
+ created April 24, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#ifndef _VEHICLELOCOMOTION_H
+#define _VEHICLELOCOMOTION_H
+
+#include <radmath/radmath.hpp>
+
+class Vehicle;
+
+class VehicleLocomotion
+{
+public:
+
+ VehicleLocomotion(Vehicle* vehicle) {};
+ virtual ~VehicleLocomotion() {};
+
+ virtual void PreSubstepUpdate() = 0;
+
+ virtual void PreCollisionPrep(bool firstSubstep) = 0;
+ virtual void UpdateVehicleGroundPlane() = 0;
+
+ virtual void PreUpdate() = 0;
+ virtual void Update(float dt) = 0;
+ virtual void PostUpdate() = 0;
+
+ virtual void CompareNormalAndHeight(int index, rmt::Vector& normalPointingAtCar, rmt::Vector& groundContactPoint) = 0;
+
+};
+
+
+#endif // _VEHICLELOCOMOTION_H
diff --git a/game/code/worldsim/redbrick/wheel.cpp b/game/code/worldsim/redbrick/wheel.cpp
new file mode 100644
index 0000000..f889909
--- /dev/null
+++ b/game/code/worldsim/redbrick/wheel.cpp
@@ -0,0 +1,533 @@
+/*===========================================================================
+ wheel.cpp
+
+ created Jan 29, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#include <worldsim/redbrick/wheel.h>
+#include <radmath/radmath.hpp>
+#include <worldsim/worldphysicsmanager.h>
+
+
+//------------------------------------------------------------------------
+Wheel::Wheel()
+{
+ mRadius = 0.0f;
+ mObjectSpaceYOffsetFromCurrentPosition = 0.0f;
+
+ mWheelNum = -1; // unset flag
+ mYOffset = 0.0f;
+
+ mLimit = 0.0f;
+ mk = 0.0f;
+ mc = 0.0f;
+
+ mWheelInCollision = false; // TODO - is this method adequate!?
+ mWheelBottomedOutThisFrame = false;
+
+ mSpringRelaxRate = 0.5f; // second to move from limit to 0 when not in collision
+
+ mWheelTurnAngle = 0.0f;
+
+ mRotAngle = 0.0f;
+ mCumulativeRot = 0.0f;
+ mTotalTime = 0.0f;
+
+ mWheelRenderSpeedRadPerSecond = 0.0f;
+ //mSpinUpRate = 10.0f; //? - I think this is in (rad/s)/s
+
+ //mRenderingSpinUpRateBase = 0.1f; // this is actually in units of rad/s -
+ // it's a direct setting of the increment amount, for now
+
+ mRenderingSpinUpRateBase = 100.0f; // back to rad/s
+
+ mRenderingSpinUpRate = 0.0f; // this one is scaled by the gas input
+
+ // % of top speed at which mode goes back to NORMAL, if we were slipping 'cause we floored it initially
+ mDpRenderingSpinUpSpeedThresholdPercentage = 0.3f;
+ //mDpRenderingSpinUpSpeedThresholdPercentage = 0.2f;
+
+
+ mSteerWheel = false;
+ mDriveWheel = false;
+
+
+ mVehicle = 0;
+
+ //----------------
+ // designer values
+ //----------------
+
+ mWheelState = WS_NORMAL;
+
+
+ //---------
+ // redbrick normal
+ //---------
+
+ // NOTE: these are all overwritten shortly after init
+ // these are just default values that are ok for the ferrari
+
+ mDpSuspensionLimit = 0.4f; // try this for ferrari
+
+ mDpSpringk = 0.5f; // prett good value for redbrick
+ mDpDamperc = 0.2f; // scale on critical damping temp for debugging
+
+
+}
+
+
+//------------------------------------------------------------------------
+Wheel::~Wheel()
+{
+ //
+}
+
+
+//------------------------------------------------------------------------
+void Wheel::Init(Vehicle* vehicle, int wheelNum, float radius, bool steer, bool drive)
+{
+ mVehicle = vehicle;
+
+ mWheelNum = wheelNum;
+ mRadius = radius;
+
+ mSteerWheel = steer;
+ mDriveWheel = drive;
+
+ //
+ // calculate 'real' values from designer params
+ //
+
+ mLimit = mDpSuspensionLimit * mRadius;
+
+ // TODO
+ // temp hack for lack of phizsim
+ //float gravity_y = 9.81f;
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();// this or CGS ?
+
+ //float gravity_y = 9.81f;
+ float gravity_y = -1.0f * gravity.y;
+
+ //rmt::Vector gravity = SG::phizSim.mSimEnvironment->Gravity();
+ //float force = mVehicle->mDesignerParams.mDpMass * rmt::Fabs(gravity.y);
+ float force = mVehicle->mDesignerParams.mDpMass * gravity_y;
+ mk = force / mLimit;
+ mk *= mDpSpringk;
+
+ //mqk = rmt::Sqrt(mk) * 2.0f; // first guess at quad spring
+ mqk = mk * 5.0f; // first guess at quad spring
+ //mqk = mk * 10.0f; // first guess at quad spring
+
+ float criticalDamping = 2.0f * (float)rmt::Sqrt(mk * mVehicle->mDesignerParams.mDpMass);
+ mc = criticalDamping * mDpDamperc;
+
+
+ // these will get recalculated wheh SetDesignParams is called
+
+}
+
+//------------------------------------------------------------------------
+void Wheel::SetDesignerParams(Vehicle::DesignerParams* dp)
+{
+
+ mDpSuspensionLimit = dp->mDpSuspensionLimit;
+
+ mDpSpringk = dp->mDpSpringk;
+ mDpDamperc = dp->mDpDamperc;
+
+
+ //----------
+
+ mLimit = mDpSuspensionLimit * mRadius;
+
+ // TODO
+ // temp hack for lack of phizsim
+ //float gravity_y = 9.81f;
+
+ const rmt::Vector& gravity = GetWorldPhysicsManager()->mSimEnvironment->Gravity();// this or CGS ?
+
+ //float gravity_y = 9.81f;
+ float gravity_y = -1.0f * gravity.y;
+
+ //rmt::Vector gravity = SG::phizSim.mSimEnvironment->Gravity();
+ //float force = mPhysicsVehicleOwner->mDpMass * rmt::Fabs(gravity.y);
+ float force = mVehicle->mDesignerParams.mDpMass * gravity_y;
+ mk = force / mLimit;
+ mk *= mDpSpringk;
+
+ //mqk = rmt::Sqrt(mk) * 2.0f; // first guess at quad spring
+ mqk = mk * 5.0f; // first guess at quad spring
+ //mqk = mk * 10.0f; // first guess at quad spring
+
+
+ float criticalDamping = 2.0f * (float)rmt::Sqrt(mk * mVehicle->mDesignerParams.mDpMass);
+ mc = criticalDamping * mDpDamperc;
+
+
+ CalculateRenderingSpinUpRateBase(dp->mDpTopSpeedKmh);
+
+}
+
+//------------------------------------------------------------------------
+float Wheel::CalculateSuspensionForce(float suspensionPointYVelocity, float dt)
+{
+
+ rAssert(0);
+
+ float force = 0.0f;
+
+ if(mWheelInCollision)
+ {
+
+ float springForce = mk * mYOffset;
+
+ // apply extra force if we're close to bottoming out
+ //
+ // TODO
+ // hmmmm..... revisit this...
+ // replace with quadratic
+
+ //
+ // or... topping out!
+ //
+ if((mLimit - rmt::Fabs(mYOffset)) < mLimit * 0.25f)
+ {
+ springForce *= 3.0f;
+ }
+
+ // for now, only add damping if chassis is trying to compress suspension - ie. y velocity is down, -ve
+ float damperForce = 0.0f;
+
+ if(suspensionPointYVelocity < 0.0f)
+ {
+ damperForce = mc * -1.0f * suspensionPointYVelocity;
+ }
+ else
+ {
+ // like the havok guys, only let damper pull down at about 1/3 of push up
+ damperForce = mc * -1.0f * suspensionPointYVelocity * 0.3f;
+
+ }
+
+ force = springForce + damperForce;
+ }
+ else
+ {
+ // need to relax spring somehow
+ float relaxDistance = mLimit / mSpringRelaxRate * dt;
+ if(mYOffset >= relaxDistance)
+ {
+ mYOffset -= relaxDistance;
+ }
+ else if(mYOffset <= -relaxDistance)
+ {
+ mYOffset += relaxDistance;
+ }
+
+ }
+ return force;
+
+
+}
+
+
+
+//------------------------------------------------------------------------------------------------
+void Wheel::CalculateRenderingSpinUpRateBase(float topSpeedKmh)
+{
+ // mDpRenderingSpinUpSpeedThreshold;
+
+ // at what rad/s is the wheel going % of top speed
+
+ float topspeedmps = topSpeedKmh / 3.6f;
+
+ float thresholdmps = topspeedmps * mDpRenderingSpinUpSpeedThresholdPercentage;
+
+ float linearDistancePerRev = rmt::PI * mRadius * 2.0f;
+
+ // rev/s = rev/dist * dist/s
+
+ // ! fuck you idiot
+ // we need rads not revs
+ float linearDistancePerRad = linearDistancePerRev / (2.0f * rmt::PI);
+
+
+ mRenderingSpinUpRateBase = (1.0f / linearDistancePerRad) * thresholdmps;
+
+
+
+}
+
+
+//------------------------------------------------------------------------
+void Wheel::CalculateRotAngle(float dt)
+{
+ if(mVehicle->mEBrake > 0.0f && mWheelInCollision)
+ {
+ // now, if no gas, don't rotate
+ if(mVehicle->mGas == 0.0f && this->mDriveWheel == true)
+ {
+ mRotAngle = 0.0f;
+ return;
+ }
+
+ }
+
+
+ float speedAlongFacing;
+
+ rmt::Vector wheelDirection = mVehicle->mVehicleFacing;
+
+ if(mSteerWheel)
+ {
+ rmt::Matrix steeringMatrix; // TODO - make this a class member?
+ steeringMatrix.Identity();
+ steeringMatrix.FillRotateY(mWheelTurnAngle);
+
+ wheelDirection.Transform(steeringMatrix);
+ }
+
+ speedAlongFacing = wheelDirection.DotProduct(mVehicle->mSuspensionPointVelocities[mWheelNum]);
+
+ //float linearDistance = speedAlongFacing * dt;
+
+ float linearDistancePerRev = rmt::PI * mRadius * 2.0f;
+
+ float revPerTime = speedAlongFacing / linearDistancePerRev;
+
+ float radiansPerTime = revPerTime * 2.0f * rmt::PI;
+
+ // ?
+ // see if vehicle's doin' a burnout
+ if(mVehicle->mBurnoutLevel > 0.0f && mDriveWheel)
+ {
+ if(mVehicle->mGas > mVehicle->mBrake)
+ {
+ radiansPerTime = mRenderingSpinUpRateBase;
+ }
+ else
+ {
+ radiansPerTime = -1.0f * mRenderingSpinUpRateBase;
+ }
+ }
+
+
+ /*
+ if(mWheelState == WS_LOCOMOTIVE_SLIDE || mWheelState == WS_LOCOMOTIVE_FREE_SPIN)
+ {
+ if(radiansPerTime >= mRenderingSpinUpRate)
+ {
+ // kick out of thid mode and use the calculated value
+ //
+ // TODO - how to then easily tune the mRenderingSpinUpRateBase?
+ //
+ // should be able to calculate based on some % of top speed
+ // yeah - no fucking timer needed!
+
+ mWheelState = WS_NORMAL;
+
+ }
+ else
+ {
+ //mRotAngle *= 1.5f;
+ //otAngle = mRenderingSpinUpRate;
+ radiansPerTime = mRenderingSpinUpRate;
+ }
+
+ }
+ */
+
+ mRotAngle = radiansPerTime * dt;
+
+ //if(rmt::Fabs(radiansPerTime - (2.0f * rmt::PI)) < 0.1f)
+ //{
+ // int stophere = 1;
+ // }
+
+
+ // fucking hack to (temporarily?) fix wheel rot on ps2
+ /*
+ mTotalTime += dt;
+
+ if(mTotalTime > 30.0f) // value is in seconds
+ {
+ mTotalTime = 0.0f;
+ mCumulativeRot = 0.0f;
+ }
+ */
+
+ mCumulativeRot += mRotAngle;
+
+ if(mCumulativeRot > rmt::PI_2)
+ {
+ mCumulativeRot -= rmt::PI_2;
+
+ //safeguard - higly unlikely we'd get here...
+ if(mCumulativeRot > rmt::PI_2)
+ {
+ // still?
+ // something strange must have happened
+ mCumulativeRot = 0.0f;
+ }
+
+ }
+
+
+
+
+ // TODO
+ // what we might want to try here is always calculate the wheel rotation to render based on desired forces
+ // not the actual forces we apply.
+ //
+ // add booleans to the Cap methods?
+ //
+ // also need to throw in some consideration for braking - draw wheels locked.
+ //
+ // furthermore, this should be done before the vehicle PostUpdate where the pose drivers actually change the joint matrices
+
+
+}
+
+//=============================================================================
+// Wheel::SetYOffsetFromCurrentPosition
+//=============================================================================
+// Description:
+//
+// Parameters: (float yoffset)
+//
+// Return: float
+//
+// note - if we've bottomed out, this will return the overflow
+// amount that the suspension could not absorb
+// if we didn't bottom out it will return 0.0f;
+//
+//
+//=============================================================================
+float Wheel::SetYOffsetFromCurrentPosition(float yoffset)
+{
+
+ // the parameter mObjectSpaceYOffsetFromCurrentPosition is a bit verbose at this
+ // point since before doing wheel collision we set the wheels to the bottom of the
+ // suspension limit, so at this poitn the currentposition is always the same
+
+ if(yoffset > mObjectSpaceYOffsetFromCurrentPosition)
+ {
+ mObjectSpaceYOffsetFromCurrentPosition = yoffset;
+ }
+ // TODO - make sure this gets reset to 0.0 at the right place
+
+
+ // this call got propagated to us from the collision solver, so I think this is the place to set the flag
+ mWheelInCollision = true;
+
+ // temp
+ // TODO - remove
+ //return 0.0f;
+
+
+ if(mObjectSpaceYOffsetFromCurrentPosition > 2.0f * mLimit)
+ {
+ // suspension has bottomed out
+ //
+ // TODO - rename this method?
+ //
+ // TODO - double check this
+
+ float overflow = mObjectSpaceYOffsetFromCurrentPosition - (2.0f * mLimit);
+ mObjectSpaceYOffsetFromCurrentPosition = 2.0f * mLimit;
+
+ return overflow;
+ //return true;
+ }
+
+ // ??
+ // TODO
+ // is this ok?
+ // should be if we always started out at the bottom of the suspension limit
+ if(mObjectSpaceYOffsetFromCurrentPosition < 0.0f)
+ {
+ rAssert(0);
+ mObjectSpaceYOffsetFromCurrentPosition = 0.0f;
+ }
+
+ return 0.0f;
+
+}
+
+
+//------------------------------------------------------------------------
+void Wheel::ResolveOffset()
+{
+ // we assume at this point anyone who wants to has called SetYOffsetFromCurrentPosition
+ mYOffset += mObjectSpaceYOffsetFromCurrentPosition;
+
+
+ // temp
+ // TODO - remove
+ //mObjectSpaceYOffsetFromCurrentPosition = 0.0f;
+ //return;
+
+ if(mYOffset > mLimit)
+ {
+ mYOffset = mLimit;
+ }
+ if(mYOffset < -mLimit)
+ {
+ mYOffset = -mLimit;
+ }
+
+ // !
+ //
+ // mYOffset is only of use internally to this class
+ //
+ // this is NOT the value other shit should use to move suspension points around and what have you.
+
+
+
+ mObjectSpaceYOffsetFromCurrentPosition = 0.0f;
+ //mWheelInCollision = false; // TODO - is this method adequate!?
+
+}
+
+
+//------------------------------------------------------------------------
+float Wheel::GetYCorrectionValue()
+{
+ return mObjectSpaceYOffsetFromCurrentPosition;
+}
+
+
+
+
+//------------------------------------------------------------------------
+void Wheel::Reset()
+{
+ mYOffset = 0.0f;
+ mObjectSpaceYOffsetFromCurrentPosition = 0.0f;
+
+ mRotAngle = 0.0f;
+
+ mCumulativeRot = 0.0f;
+ mTotalTime = 0.0f;
+
+ mWheelInCollision = false;
+ mWheelBottomedOutThisFrame = false;
+
+ mWheelState = WS_NORMAL;
+
+ mWheelRenderSpeedRadPerSecond = 0.0f;
+
+}
+
+
+
+
+
diff --git a/game/code/worldsim/redbrick/wheel.h b/game/code/worldsim/redbrick/wheel.h
new file mode 100644
index 0000000..832a052
--- /dev/null
+++ b/game/code/worldsim/redbrick/wheel.h
@@ -0,0 +1,107 @@
+/*===========================================================================
+ wheel.h
+
+ created Jan 29, 2002
+ by Greg Mayer
+
+ Copyright (c) 2002 Radical Entertainment, Inc.
+ All rights reserved.
+
+
+===========================================================================*/
+
+#ifndef _WHEEL_H
+#define _WHEEL_H
+
+#include <radmath/radmath.hpp>
+
+#include <worldsim/redbrick/vehicle.h>
+
+class SuspensionJointDriver;
+
+enum WheelState{ WS_NORMAL, WS_SLIP, WS_LOCOMOTIVE_SLIDE, WS_LOCOMOTIVE_FREE_SPIN };
+
+class Wheel
+{
+private:
+
+ friend class SuspensionJointDriver;
+ friend class Vehicle;
+ friend class PhysicsLocomotion;
+ friend class GeometryVehicle; // everyone's friends with the Wheel :)
+ friend class RedBrickCollisionSolverAgent;
+
+
+ Wheel();
+ ~Wheel();
+
+ Vehicle* mVehicle;
+
+ // TODO - initialize k and c from vehicle too...easier to tune?
+ void Init(Vehicle* vehicle, int wheelNum, float radius, bool steer, bool drive);
+ void Reset();
+
+ void SetDesignerParams(Vehicle::DesignerParams* dp);
+
+ // this will check the one we're passing in against the one stored
+ float SetYOffsetFromCurrentPosition(float yoffset);
+ void ResolveOffset(); // modify mYOffset by the final thing left in mObjectSpaceYOffsetFromCurrentPosition
+
+ float GetYCorrectionValue();
+
+ float CalculateSuspensionForce(float suspensionPointYVelocity, float dt);
+ void CalculateRenderingSpinUpRateBase(float topSpeedKmh);
+
+ bool mSteerWheel;
+ bool mDriveWheel;
+
+ int mWheelNum;
+ float mRadius;
+ float mObjectSpaceYOffsetFromCurrentPosition; // set frame to frame by collision agent
+
+
+ float mYOffset; // object space offset from suspension rest point
+
+ void CalculateRotAngle(float dt);
+ float mRotAngle;
+ float mCumulativeRot;
+ float mTotalTime;
+
+ float mLimit;
+ float mk; // spring constant
+ float mc; // damper constant
+
+ float mqk; // new test - quadratic spring
+
+ bool mWheelInCollision;
+ bool mWheelBottomedOutThisFrame;
+
+ float mSpringRelaxRate;
+
+ float mWheelTurnAngle;
+
+
+
+ WheelState mWheelState;
+
+ float mWheelRenderSpeedRadPerSecond;
+
+ float mRenderingSpinUpRate;
+ float mRenderingSpinUpRateBase;
+
+
+ //---------------------
+ // designer parameters:
+ //---------------------
+
+ float mDpSuspensionLimit; // multiplier on radius
+ float mDpSpringk;
+ float mDpDamperc; // normalized values
+
+ float mDpRenderingSpinUpSpeedThresholdPercentage;
+
+
+};
+
+
+#endif // _WHEEL_H \ No newline at end of file
diff --git a/game/code/worldsim/skidmarks/SkidMarkGenerator.cpp b/game/code/worldsim/skidmarks/SkidMarkGenerator.cpp
new file mode 100644
index 0000000..df7d860
--- /dev/null
+++ b/game/code/worldsim/skidmarks/SkidMarkGenerator.cpp
@@ -0,0 +1,272 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SkidMarkGenerator
+//
+// Description: A per-vehicle class that generates skidmarks
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+#include <stdlib.h>
+
+#include <worldsim/SkidMarks/SkidMarkGenerator.h>
+#include <worldsim/SkidMarks/skidmarkmanager.h>
+#include <worldsim/SkidMarks/skidmark.h>
+
+#include <radmath/util.hpp>
+#include <float.h>
+#include <debug/profiler.h>
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+
+#ifdef RAD_RELEASE
+const float SKID_INTENSITY_DAMPENING = 0.5f;
+#else
+static float SKID_INTENSITY_DAMPENING = 0.5f;
+#endif
+
+
+SkidMarkGenerator::SkidMarkGenerator()
+{
+#ifndef RAD_RELEASE
+ static bool s_AddedDebugging = false;
+ if ( s_AddedDebugging == false )
+ {
+ radDbgWatchAddFloat( &SKID_INTENSITY_DAMPENING, "Skid mark dampening", "Skidmarks" );
+ s_AddedDebugging = true;
+ }
+#endif
+}
+
+SkidMarkGenerator::~SkidMarkGenerator()
+{
+ for(int i = 0; i < 4; i++)
+ {
+ if(m_CurrentSkidData[i].currentSkid)
+ {
+ GetSkidmarkManager()->ReturnUsedSkidmark( m_CurrentSkidData[i].currentSkid );
+ }
+ m_CurrentSkidData[i].currentSkid = NULL;
+ }
+}
+
+// Height in meters to raise the skids above the ground to avoid z fighting
+
+// Keep pointers to the tShaders
+static tShader* spPavementShader = NULL;
+static tShader* spDirtShader = NULL;
+static tShader* spGrassShader = NULL;
+
+// The shader names for use in searching for skid mark shaders in the inventory
+const char* PAVEMENT_SKIDMARK_SHADERNAME = "skid_m";
+const char* DIRT_SKIDMARK_SHADERNAME = "dirt_skidmark_shader";
+const char* GRASS_SKIDMARK_SHADERNAME = "grass_skidmark_shader";
+
+// enables z test, disables z test, required for correct
+// skid mark appearance
+static void SetShaderSkidSettings( tShader* pShader )
+{
+ pShader->SetInt(PDDI_SP_ALPHAOP1, PDDI_BLEND_ALPHA);
+ pShader->SetInt(PDDI_SP_TEXTUREOP1, PDDI_TEXBLEND_MODULATE);
+ pShader->SetInt(PDDI_SP_COLOUROP1, PDDI_BLEND_SUBTRACT);
+}
+
+// Find the shaders in the inventory, set zwrite/ztest (since the artists can't
+// export these shader settings directly and addref them
+// SkidMarkGenerator::ReleaseShaders() must be called afterwards
+void SkidMarkGenerator::InitShaders()
+{
+
+ // Just so that we never do an accidental double addref
+ ReleaseShaders();
+
+ spPavementShader = p3d::find< tShader >( PAVEMENT_SKIDMARK_SHADERNAME );
+ if ( spPavementShader != NULL )
+ {
+ spPavementShader->AddRef();
+ SetShaderSkidSettings( spPavementShader );
+ }
+ rTuneWarningMsg( spPavementShader != NULL, "Pavement skid shader not found in inventory! Some skids will not appear!");
+
+ spDirtShader = p3d::find< tShader >( DIRT_SKIDMARK_SHADERNAME );
+ if ( spDirtShader != NULL )
+ {
+ spDirtShader->AddRef();
+ SetShaderSkidSettings( spDirtShader );
+ }
+ rTuneWarningMsg( spDirtShader != NULL, "Dirt skid shader not found in inventory! Some skids will not appear!");
+
+ spGrassShader = p3d::find< tShader >( GRASS_SKIDMARK_SHADERNAME );
+ if ( spGrassShader != NULL )
+ {
+ spGrassShader->AddRef();
+ SetShaderSkidSettings( spGrassShader );
+ }
+
+ rTuneWarningMsg( spGrassShader != NULL, "Grass skid shader not found in inventory! Some skids will not appear!");
+
+
+}
+// Fetch the appropriate shader for the terrain type
+tShader* SkidMarkGenerator::GetShader( eTerrainType terrainType )
+{
+ tShader* shader = NULL;
+
+ switch (terrainType)
+ {
+ case TT_Dirt:
+ shader = spDirtShader;
+ break;
+ case TT_Grass:
+ shader = spGrassShader;
+ break;
+ case TT_Road:
+ default:
+ shader = spPavementShader;
+ break;
+ };
+ return shader;
+}
+
+
+// Releases all the shaders that were addrefed in InitShaders()
+void SkidMarkGenerator::ReleaseShaders()
+{
+ if ( spPavementShader != NULL )
+ {
+ spPavementShader->Release();
+ spPavementShader = NULL;
+ }
+ if ( spDirtShader != NULL )
+ {
+ spDirtShader->Release();
+ spDirtShader = NULL;
+ }
+ if ( spGrassShader != NULL )
+ {
+ spGrassShader->Release();
+ spGrassShader = NULL;
+ }
+}
+
+
+
+
+void SkidMarkGenerator::GenerateSkid( int wheel, const SkidMarkGenerator::SkidData& skidData )
+{
+ if ( skidData.velocityDirection.MagnitudeSqr() < 0.01f ||
+ skidData.intensity < 0.01f )
+ return;
+
+
+ float dampenedIntensity = skidData.intensity * SKID_INTENSITY_DAMPENING;
+
+
+ rAssert( wheel >=0 && wheel < 4 );
+ TireGeneratorData& generatorData = m_CurrentSkidData[ wheel ];
+ // Is the tire currently skidding?
+ // if so, continue it
+ // otherwise start a new skid
+
+ rmt::Vector alignedOffset;
+ skidData.transform.RotateVector( generatorData.offset, &alignedOffset );
+ rmt::Vector skidCenter = skidData.transform.Row(3) + alignedOffset;
+
+ rmt::Vector skidDirection = skidData.velocityDirection;
+ skidDirection.Normalize();
+
+ // Check to see if the terrain type changed
+ if ( generatorData.currentSkid != NULL )
+ {
+ const float ANGLE_TOO_SHARP = 0.7f;
+ bool angleTooSharp = generatorData.currentSkid->GetCurrentDirection().Dot( skidDirection ) < ANGLE_TOO_SHARP;
+ bool terrainTypeChanged = generatorData.terrainType != skidData.terrainType;
+
+ if ( angleTooSharp || terrainTypeChanged )
+ {
+ // Finish off the current skid and start a new one
+ generatorData.currentSkid->FadeOutTrailingVertices();
+ GetSkidmarkManager()->ReturnUsedSkidmark( generatorData.currentSkid );
+ generatorData.currentSkid = NULL;
+ GenerateSkid( wheel, skidData );
+ }
+ }
+ // Check to see if
+
+ if ( generatorData.currentSkid != NULL )
+ {
+ // Is there space left for more vertices?
+ if ( generatorData.currentSkid->SpaceLeft() )
+ {
+ generatorData.currentSkid->Extend( skidCenter, skidDirection, skidData.groundPlaneNormal, generatorData.halfWidth, dampenedIntensity );
+ generatorData.skidExtendedThisFrame = true;
+ }
+ else
+ {
+ // No space left. We must continue this skid with a new skidmark
+ Skidmark* skidMark = GetSkidmarkManager()->GetUnusedSkidmark();
+ if ( skidMark != NULL )
+ {
+ generatorData.currentSkid->ContinueSkidmark( skidMark );
+ GetSkidmarkManager()->ReturnUsedSkidmark( generatorData.currentSkid );
+ generatorData.currentSkid = skidMark;
+ generatorData.currentSkid->Extend( skidCenter, skidDirection, skidData.groundPlaneNormal, generatorData.halfWidth, dampenedIntensity );
+ generatorData.skidExtendedThisFrame = true;
+ }
+ else
+ {
+ // No space available!
+ // Tell the current skid to fade out, we are done. Pretend the skid is finished
+ generatorData.currentSkid->FadeOutTrailingVertices();
+ GetSkidmarkManager()->ReturnUsedSkidmark( generatorData.currentSkid );
+ generatorData.currentSkid = NULL;
+ generatorData.skidExtendedThisFrame = false;
+ }
+ }
+ }
+ else
+ {
+ // Fetch a skidmark thats not being used right now
+ generatorData.currentSkid = GetSkidmarkManager()->GetUnusedSkidmark();
+ if ( generatorData.currentSkid != NULL )
+ {
+ generatorData.currentSkid->Extend( skidCenter, skidDirection, skidData.groundPlaneNormal, generatorData.halfWidth, dampenedIntensity );
+ generatorData.skidExtendedThisFrame = true;
+ generatorData.terrainType = skidData.terrainType;
+ generatorData.currentSkid->SetShader( GetShader( skidData.terrainType ) );
+ generatorData.currentSkid->FadeInVertices();
+ }
+ }
+}
+
+
+
+void
+SkidMarkGenerator::Update( )
+{
+ for ( int i = 0 ; i < 4 ; i++ )
+ {
+ if ( m_CurrentSkidData[i].skidExtendedThisFrame == false)
+ {
+ if ( m_CurrentSkidData[i].currentSkid != NULL )
+ {
+ m_CurrentSkidData[i].currentSkid->FadeOutTrailingVertices();
+ GetSkidmarkManager()->ReturnUsedSkidmark( m_CurrentSkidData[i].currentSkid );
+ m_CurrentSkidData[i].currentSkid = NULL;
+ }
+ }
+ else
+ {
+ m_CurrentSkidData[i].skidExtendedThisFrame = false;
+ }
+ }
+}
+
+
+
+
diff --git a/game/code/worldsim/skidmarks/SkidMarkGenerator.h b/game/code/worldsim/skidmarks/SkidMarkGenerator.h
new file mode 100644
index 0000000..ebea931
--- /dev/null
+++ b/game/code/worldsim/skidmarks/SkidMarkGenerator.h
@@ -0,0 +1,187 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SkidMarkGenerator
+//
+// Description: A per-vehicle class that generates skidmarks
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef SKIDMARKGENERATOR_H
+#define SKIDMARKGENERATOR_H
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+#include <render/IntersectManager/IntersectManager.h>
+
+
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class Skidmark;
+class tShader;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+//
+//
+//
+//
+//
+// Constraints:
+//
+//===========================================================================
+
+
+
+class SkidMarkGenerator
+{
+ public:
+ // Tries and finds the shaders the skidmarks in the inventory, retrieves
+ // them so that skidmarkgenerator doesn't need to call p3d::find<>
+ // and also sets the zwrite/ztest properties
+ static void InitShaders();
+ // Releases the shaders that were addrefed in initshaders
+ static void ReleaseShaders();
+
+ public:
+ SkidMarkGenerator();
+ ~SkidMarkGenerator();
+
+ enum Constants
+ {
+ MAX_NUM_WHEELS = 4
+ };
+
+ // Data for a single tire
+ struct TireData
+ {
+ // Tire dimensions as they would look when pressed evenly flat against the road
+ // A tank tread, or a snowboard, for instance would be much longer than it is wide
+ float width;
+ float length;
+ // offset of the tire, relative to the vehicle center
+ rmt::Vector offset;
+ };
+
+ struct SkidData
+ {
+ SkidData() : groundPlaneNormal( 0 , 1 ,0 ) { }
+
+ // Direction that the vehicle is moving
+ rmt::Vector velocityDirection;
+ // Orientation and position of the vehicle
+ rmt::Matrix transform;
+ float intensity; // 1 most intense, 0 nonexistent skid
+ // Ground plane normal, used to raise the skid mark over the ground
+ // to avoid Z figthing.
+ // Constraint : this variable must be normalized before use
+ rmt::Vector groundPlaneNormal;
+
+ // Terrain type underneath the tire. Used to determine which texture to use.
+ eTerrainType terrainType;
+ };
+
+ // Generate a skidmark, starts a new strip if necessary
+
+
+ /*
+ ^
+ |
+ |
+ |
+ +Z
+ |
+ |
+ front
+ _____________
+ wheel 2 | | wheel 3
+ | |
+ | |
+ | |
+ | |
+ | |
+ | | ---+X----->
+ | |
+ | |
+ | |
+ | |
+ | |
+ wheel 1 ------------- wheel 0
+ rear
+
+ */
+
+ // Sets the translation offset for the specified wheel relative to the
+ // vehicle's origin
+ void SetWheelOffset( int wheel, const rmt::Vector& offset )
+ {
+ m_CurrentSkidData[ wheel ].offset = offset;
+ }
+
+ // Sets the length and width of the wheel as it presses against the ground
+ // Determines the dimensions of the skid
+ void SetWheelDimensions( int wheel, float width, float length )
+ {
+ m_CurrentSkidData[ wheel ].halfWidth = width / 2.0f;
+ }
+ void GenerateSkid( int wheel, const SkidData& );
+
+ void Update( );
+
+ protected:
+
+ private:
+
+ tShader* GetShader( eTerrainType terrainType );
+ void LayDownVertices( int wheel, const SkidMarkGenerator::SkidData& skidData );
+
+ struct TireGeneratorData
+ {
+ TireGeneratorData():
+ halfWidth( 0.25f ),
+ currentSkid( NULL ),
+ skidExtendedThisFrame( false ),
+ offset( 0,0,0 ),
+ wasLastVertexLaidTemporary( false ),
+ terrainType( TT_NumTerrainTypes )
+ {
+
+ }
+ float halfWidth;
+ Skidmark* currentSkid;
+ bool skidExtendedThisFrame;
+ rmt::Vector offset;
+ bool wasLastVertexLaidTemporary;
+ eTerrainType terrainType;
+ };
+
+ TireGeneratorData m_CurrentSkidData[4];
+
+
+
+ // These methods defined as private and not implemented ensure that
+ // clients will not be able to use them. For example, we will
+ // disallow SkidMarkGenerator from being copied and assigned.
+ SkidMarkGenerator( const SkidMarkGenerator& );
+ SkidMarkGenerator& operator=( const SkidMarkGenerator& );
+
+};
+
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/skidmarks/allskidmarks.cpp b/game/code/worldsim/skidmarks/allskidmarks.cpp
new file mode 100644
index 0000000..e178a4a
--- /dev/null
+++ b/game/code/worldsim/skidmarks/allskidmarks.cpp
@@ -0,0 +1,3 @@
+#include <worldsim/SkidMarks/SkidMarkGenerator.cpp>
+#include <worldsim/SkidMarks/SkidMarkmanager.cpp>
+#include <worldsim/skidmarks/skidmark.cpp> \ No newline at end of file
diff --git a/game/code/worldsim/skidmarks/skidmark.cpp b/game/code/worldsim/skidmarks/skidmark.cpp
new file mode 100644
index 0000000..8cfd239
--- /dev/null
+++ b/game/code/worldsim/skidmarks/skidmark.cpp
@@ -0,0 +1,442 @@
+#include <worldsim/SkidMarks/skidmark.h>
+#include <camera/supercammanager.h>
+#include <debug/profiler.h>
+
+const float METERS_PER_TEX_UNIT = 0.5f;
+const float TEX_UNITS_PER_METER = 1.0f / METERS_PER_TEX_UNIT;
+
+
+
+Skidmark::Skidmark():
+m_NumSegments( 0 ),
+mpShader( NULL ),
+m_IsInDSG( false ),
+m_LastU( 0.0f ),
+m_TextureDirection( 1.0f ),
+m_FadedOut( false ),
+m_FadingIn( false )
+{
+ // Skidmarks are always translucent
+ mTranslucent = true;
+ SetName( "Skidmark" );
+}
+
+Skidmark::~Skidmark()
+{
+ if ( mpShader != NULL )
+ {
+ mpShader->Release();
+ mpShader = NULL;
+ }
+ if ( m_IsInDSG )
+ {
+ RemoveFromDSG();
+ m_IsInDSG = false;
+ }
+}
+
+bool Skidmark::IsVisible()const
+{
+ if ( m_NumSegments < 2 || m_FadedOut )
+ return false;
+
+ rmt::Sphere boundingSphere = m_BoundingBox.GetBoundingSphere();
+
+ if (GetSuperCamManager()->GetSCC( 0 )->GetCamera()->SphereVisible( boundingSphere.centre, boundingSphere.radius ) )
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+void Skidmark::Display()
+{
+ if ( m_NumSegments < 2 || mpShader == NULL )
+ return;
+
+ BEGIN_PROFILE("Skidmark::Display")
+ bool oldZWrite = p3d::pddi->GetZWrite();
+ pddiCompareMode oldZComp = p3d::pddi->GetZCompare();
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( false );
+ }
+
+ pddiPrimStream* pStream = p3d::pddi->BeginPrims( mpShader->GetShader(), PDDI_PRIM_TRISTRIP, PDDI_V_CT, m_NumSegments * 2 );
+ for ( int i = 0 ; i < m_NumSegments ; i++ )
+ {
+ tColour colour( m_Segments[i].intensity, m_Segments[i].intensity, m_Segments[i].intensity, m_Segments[i].intensity );
+ pStream->Vertex( &m_Segments[ i ].left.position, colour, &m_Segments[ i ].left.uv );
+ pStream->Vertex( &m_Segments[ i ].right.position, colour, &m_Segments[ i ].right.uv );
+ }
+ p3d::pddi->EndPrims( pStream );
+
+ if( oldZWrite )
+ {
+ p3d::pddi->SetZWrite( true );
+ }
+ END_PROFILE("Skidmark::Display")
+}
+
+void Skidmark::Extend( const rmt::Vector& position, const rmt::Vector& forward, const rmt::Vector groundNormal, float halfWidth, float intensity )
+{
+ const float Z_FIGHTING_OFFSET = 0.1f;
+
+ rAssert( SpaceLeft () );
+ rmt::Vector elevatedPosition = position + (groundNormal * Z_FIGHTING_OFFSET);
+
+
+
+ rmt::Box3D oldBoundingBox;
+ GetBoundingBox( &oldBoundingBox );
+
+ m_CurrentDirection = forward;
+
+ unsigned char ucIntensity = static_cast< unsigned char >( intensity * 255.0f );
+
+ // if vertices have been laid already
+ // find texture break position
+ // it is on the straight line vector from the last position to the current one
+ // extend the last two vertices laid to the texture break position
+ // the texture break position is recorded as the last position written
+ // lay down two more at the current (given position)
+ if ( m_NumSegments > 0 )
+ {
+ // Check to see that we have travelled the minimum distance from the last
+ // point that we laid down a segment
+ if ( ( elevatedPosition - m_LastPositionWritten ).MagnitudeSqr() < 0.05f )
+ return;
+
+ rmt::Vector toPosition = elevatedPosition - m_LastPositionWritten;
+ toPosition.Normalize();
+ rmt::Vector right;
+ right.CrossProduct( toPosition, groundNormal );
+ right.Normalize();
+
+ rmt::Vector textureBreakPosition;
+ float breakPointU;
+ float distanceToTextureBreakPoint;
+ float distanceToGivenPoint;
+ if ( FindTextureBreakPosition( elevatedPosition, &textureBreakPosition, &breakPointU, &distanceToTextureBreakPoint, &distanceToGivenPoint ) )
+ {
+ float distanceFromStart = distanceToTextureBreakPoint + m_Segments[ m_NumSegments - 1].distanceFromStart;
+ // ExtendVertices( textureBreakPosition, right, halfWidth, ucIntensity, breakPointU, distanceFromStart );
+ m_LastPositionWritten = textureBreakPosition;
+ // Reverse texture direction
+ m_TextureDirection *= -1.0f;
+ // Calculate the texture coordinate of the elevated position
+ // u = distance( tbp, ep ) * TEX_UNITS_PER_METER * texturedirection
+ //float distance = (textureBreakPosition - elevatedPosition).Magnitude();
+ float currentU = breakPointU + distanceToTextureBreakPoint * TEX_UNITS_PER_METER * m_TextureDirection;
+ if ( currentU > 1.0f )
+ currentU = 1.0f;
+ else if ( currentU < 0 )
+ currentU = 0;
+ m_LastU = currentU;
+
+ distanceFromStart = distanceToGivenPoint + m_Segments[ m_NumSegments - 1].distanceFromStart;
+ WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, currentU, distanceFromStart );
+ WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, currentU, distanceFromStart );
+ m_LastPositionWritten = elevatedPosition;
+ }
+ else
+ {
+ // We haven't crossed a texture break point boundary
+ // extend the last two vertices written to the current position
+ // then write two more vertices, also at the current position for use in
+ // the next extension
+ float distance = (m_LastPositionWritten - elevatedPosition).Magnitude();
+
+ float currentU = m_LastU + distance * TEX_UNITS_PER_METER * m_TextureDirection;
+
+ float distanceFromStart = m_Segments[ m_NumSegments - 1 ].distanceFromStart + distance;
+
+ ExtendVertices( elevatedPosition, right, halfWidth, ucIntensity, currentU, distanceFromStart );
+ WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, currentU, distanceFromStart );
+ m_LastPositionWritten = elevatedPosition;
+ m_LastU = currentU;
+ }
+
+ }
+ else
+ {
+ rmt::Vector right;
+ right.CrossProduct( forward, groundNormal );
+ right.Normalize();
+
+ // Brand new strip
+ // write 4 vertices, first two are fixed, 2nd two are temporaries
+ WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, 0, 0 );
+ WriteVertices( elevatedPosition, right, halfWidth, ucIntensity, 0, 0 );
+ m_TextureDirection = 1.0f;
+ m_LastU = 0;
+ m_LastPositionWritten = elevatedPosition;
+ }
+ // Toss it in the dsg if its not there already
+ if ( m_IsInDSG == false )
+ {
+ AddToDSG( RenderEnums::LevelSlot );
+ }
+ else
+ {
+ MoveInDSG( oldBoundingBox );
+ }
+
+}
+
+void Skidmark::ClearVertices()
+{
+ // Reset the bounding box
+
+ // Which direction are we laying our coordinates, either 1.0 or -1.0
+ m_LastU = 0.0f;
+
+ RemoveFromDSG();
+ m_NumSegments = 0;
+ m_FadedOut = false;
+ m_FadingIn = false;
+
+ m_BoundingBox.low = rmt::Vector(FLT_MAX,FLT_MAX,FLT_MAX);
+ m_BoundingBox.high = rmt::Vector(-FLT_MAX,-FLT_MAX,-FLT_MAX);
+
+}
+
+void Skidmark::ContinueSkidmark( Skidmark* newSkidmark )
+{
+ tRefCounted::Assign( newSkidmark->mpShader, mpShader );
+ newSkidmark->ClearVertices();
+ newSkidmark->m_LastPositionWritten = m_LastPositionWritten;
+ newSkidmark->m_Segments[ 0 ] = m_Segments[ m_NumSegments - 1 ];
+ newSkidmark->m_Segments[ 1 ] = m_Segments[ m_NumSegments - 2 ];
+ newSkidmark->m_NumSegments = 2;
+ newSkidmark->m_LastU = m_LastU;
+ newSkidmark->m_TextureDirection = m_TextureDirection;
+ newSkidmark->m_CurrentDirection = m_CurrentDirection;
+ rmt::Box3D oldBox;
+ GetBoundingBox( &oldBox );
+
+ newSkidmark->m_BoundingBox.Expand( newSkidmark->m_Segments[ 0 ].left.position );
+ newSkidmark->m_BoundingBox.Expand( newSkidmark->m_Segments[ 0 ].right.position );
+ newSkidmark->m_BoundingBox.Expand( newSkidmark->m_Segments[ 1 ].left.position );
+ newSkidmark->m_BoundingBox.Expand( newSkidmark->m_Segments[ 1 ].right.position );
+
+ m_NumSegments--;
+
+}
+
+void Skidmark::FadeInVertices()
+{
+ const float DISTANCE_TO_FADE_IN = 1.0f;
+
+ // We want to fade in the last few vertices, but we don't want to make the fade greater in intensity than
+ // existing ones, so check before writing new values
+
+ if ( m_NumSegments < 1 )
+ return;
+
+ for ( int i = 0 ; i < m_NumSegments ; i++ )
+ {
+ int intensity = static_cast< int >( m_Segments[ i ].distanceFromStart / DISTANCE_TO_FADE_IN * 255.0f );
+ if ( m_Segments[ i ].intensity > intensity )
+ m_Segments[ i ].intensity = intensity;
+ }
+
+}
+
+void Skidmark::FadeOutTrailingVertices()
+{
+ const float DISTANCE_TO_FADE_OUT = 1.0f;
+
+ rAssert(m_NumSegments != 0); // happened to me once, probably bad, you can click
+ // through okay now, but we might want to fix it at some point
+ // nbrooke, apr 11, 2003
+ if(m_NumSegments == 0)
+ return;
+
+ // We want to fade out the last few vertices, but we don't want to make the fade greater in intensity than
+ // existing ones, so check before writing new values
+
+ // Last segment is zero
+ // next to last is -1.dist - this.dist
+
+ float distFadedOutSoFar = 0;
+ m_Segments[ m_NumSegments - 1 ].intensity = 0;
+ int intensity = 0;
+
+ for ( int i = m_NumSegments - 2 ; i >= 0 ; i-- )
+ {
+ distFadedOutSoFar += m_Segments[ i + 1 ].distanceFromStart - m_Segments[ i ].distanceFromStart;
+ intensity = static_cast< int >( distFadedOutSoFar / DISTANCE_TO_FADE_OUT * 255.0f );
+ if ( m_Segments[ i ].intensity > intensity )
+ m_Segments[ i ].intensity = intensity;
+ }
+
+}
+
+void Skidmark::FadeOut( float deltaAlpha )
+{
+ int iDeltaAlpha = static_cast< unsigned char > ( deltaAlpha * 255.0f );
+
+ bool stillVisible = false;
+ for ( int i = 0 ; i < m_NumSegments ; i++ )
+ {
+ int newAlpha = m_Segments[i].intensity - iDeltaAlpha;
+ if ( newAlpha <= 0 )
+ {
+ newAlpha = 0;
+ }
+ else
+ {
+ stillVisible = true;
+ }
+ m_Segments[ i ].intensity = newAlpha;
+ }
+ if ( stillVisible == false )
+ m_FadedOut = true;
+}
+
+void Skidmark::AddToDSG( RenderEnums::LayerEnum renderLayer )
+{
+ if ( m_IsInDSG )
+ RemoveFromDSG();
+
+ m_RenderLayer = renderLayer;
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( m_RenderLayer ));
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Add( this );
+ m_IsInDSG = true;
+
+}
+
+void Skidmark::RemoveFromDSG()
+{
+
+ if ( m_IsInDSG )
+ {
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( m_RenderLayer ));
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Remove( this );
+ m_IsInDSG = false;
+ }
+}
+
+void Skidmark::SetShader( tShader* pShader )
+{
+ tRefCounted::Assign( mpShader, pShader );
+}
+
+void Skidmark::WriteVertices( const rmt::Vector& elevatedPosition,
+ const rmt::Vector& right,
+ float halfWidth,
+ unsigned char intensity,
+ float u,
+ float distanceFromStart )
+{
+
+
+ rmt::Vector leftVertex = elevatedPosition - (right * halfWidth);
+ rmt::Vector rightVertex = elevatedPosition + (right * halfWidth);
+
+ // Increase bounding box
+ m_BoundingBox.Expand( rightVertex );
+ m_BoundingBox.Expand( leftVertex );
+
+
+ // Set position data
+ m_Segments[ m_NumSegments ].left.position = leftVertex;
+ m_Segments[ m_NumSegments ].right.position = rightVertex;
+
+
+ // Intensity
+ m_Segments[ m_NumSegments ].intensity = intensity;
+
+ // Tex coordinates
+ m_Segments[ m_NumSegments ].left.uv = pddiVector2( 0.0f, u );
+ m_Segments[ m_NumSegments ].right.uv = pddiVector2( 1.0f, u );
+
+ m_Segments[ m_NumSegments ].distanceFromStart = distanceFromStart;
+
+ m_NumSegments++;
+}
+
+void Skidmark::ExtendVertices( const rmt::Vector& elevatedPosition,
+ const rmt::Vector& right,
+ float halfWidth,
+ unsigned char intensity,
+ float u,
+ float distance )
+{
+
+ rmt::Vector leftVertex = elevatedPosition - (right * halfWidth);
+ rmt::Vector rightVertex = elevatedPosition + (right * halfWidth);
+
+ // Increase bounding box
+ m_BoundingBox.Expand( rightVertex );
+ m_BoundingBox.Expand( leftVertex );
+
+ // Set position data
+ m_Segments[ m_NumSegments - 1 ].left.position = leftVertex;
+ m_Segments[ m_NumSegments - 1 ].right.position = rightVertex;
+
+ // Intensity
+ m_Segments[ m_NumSegments - 1 ].intensity = intensity;
+
+ // Tex coordinates
+ m_Segments[ m_NumSegments -1 ].left.uv = pddiVector2( 0.0f, u );
+ m_Segments[ m_NumSegments -1 ].right.uv = pddiVector2( 1.0f, u );
+
+ m_Segments[ m_NumSegments - 1].distanceFromStart = distance;
+}
+
+bool Skidmark::FindTextureBreakPosition( const rmt::Vector& position, rmt::Vector* texBreakPosition, float* u, float* distanceToTextureBreakPoint, float* distanceToGivenPoint )
+{
+ // Calculate the point on the vector from the last vertex written
+ rmt::Vector toPosition = position - m_LastPositionWritten;
+ toPosition.Normalize();
+ float deltaU;
+ float breakTexCoordinate;
+ if ( m_TextureDirection > 0 )
+ {
+ deltaU = 1.0f - m_LastU;
+ breakTexCoordinate = 1.0f;
+ }
+ else
+ {
+ deltaU = m_LastU - 0.0f;
+ breakTexCoordinate = 0.0f;
+ }
+
+ float distance = METERS_PER_TEX_UNIT * deltaU;
+ // Check to see that the break position is actually between the last position and
+ // the given position
+ *texBreakPosition = m_LastPositionWritten + toPosition * distance;
+ *u = breakTexCoordinate;
+
+ float distanceToBreakPointSqr = ( *texBreakPosition - m_LastPositionWritten ).MagnitudeSqr();
+ float distanceToGivenPointSqr = ( position - m_LastPositionWritten ).MagnitudeSqr();
+ if ( distanceToGivenPointSqr > distanceToBreakPointSqr )
+ {
+ *distanceToTextureBreakPoint = rmt::Sqrt ( distanceToBreakPointSqr );
+ *distanceToGivenPoint = rmt::Sqrt ( distanceToGivenPointSqr );
+ return true;
+ }
+ else
+ return false;
+
+}
+
+void Skidmark::MoveInDSG( rmt::Box3D& oldBox )
+{
+ if ( m_IsInDSG )
+ {
+ WorldRenderLayer* pWorldRenderLayer = static_cast< WorldRenderLayer* > (GetRenderManager()->mpLayer( m_RenderLayer ));
+ rAssert( dynamic_cast<WorldRenderLayer*>(pWorldRenderLayer) != NULL );
+ pWorldRenderLayer->pWorldScene()->Move( oldBox, this );
+ }
+}
+
+
diff --git a/game/code/worldsim/skidmarks/skidmark.h b/game/code/worldsim/skidmarks/skidmark.h
new file mode 100644
index 0000000..5803913
--- /dev/null
+++ b/game/code/worldsim/skidmarks/skidmark.h
@@ -0,0 +1,85 @@
+#ifndef SKIDMARK_H
+#define SKIDMARK_H
+
+#include <render/DSG/StaticEntityDSG.h>
+#include <render/RenderManager/WorldRenderLayer.h>
+#include <render/RenderManager/RenderManager.h>
+
+
+// A contigous skidmark
+class Skidmark : public StaticEntityDSG
+{
+public:
+ Skidmark();
+ ~Skidmark();
+ virtual void Display();
+ virtual void GetBoundingBox( rmt::Box3D* box ){ *box = m_BoundingBox; }
+ virtual void GetBoundingSphere( rmt::Sphere* sphere ) { *sphere = m_BoundingBox.GetBoundingSphere(); }
+ bool SpaceLeft()const { return (NUM_SEGMENTS - m_NumSegments) >= 3; }
+ bool IsVisible()const;
+ void Extend( const rmt::Vector& position, const rmt::Vector& forward, const rmt::Vector groundNormal, float halfWidth, float intensity );
+ void ClearVertices();
+ // Continue a skidmark by copying the last two vertices + texture information to the new
+ // skidmark
+ void ContinueSkidmark( Skidmark* newSkidmark );
+ void FadeInVertices();
+ void FadeOutTrailingVertices();
+ void FadeOut( float deltaAlpha );
+ void AddToDSG( RenderEnums::LayerEnum renderLayer );
+ void RemoveFromDSG();
+ bool IsInDSG()const{ return m_IsInDSG; }
+ void SetShader( tShader* pShader );
+ void SetFadeIn( bool useFadeIn ){ m_FadingIn = useFadeIn; }
+ const rmt::Vector& GetCurrentDirection()const { return m_CurrentDirection; }
+
+private:
+
+ void WriteVertices( const rmt::Vector& elevatedPosition,
+ const rmt::Vector& rightVector,
+ float halfWidth,
+ unsigned char intensity,
+ float u,
+ float distance );
+
+ void ExtendVertices( const rmt::Vector& elevatedPosition,
+ const rmt::Vector& rightVector,
+ float halfWidth,
+ unsigned char intensity,
+ float u,
+ float distance );
+ void MoveInDSG( rmt::Box3D& oldBox );
+
+
+ bool FindTextureBreakPosition( const rmt::Vector& position, rmt::Vector* texBreakPosition, float* u, float* distanceToTBP, float* distanceToGivenPoint );
+
+ struct Vertex
+ {
+ pddiVector position;
+ pddiVector2 uv;
+ };
+
+ struct Segment
+ {
+ Vertex left,right;
+ unsigned char intensity;
+ float distanceFromStart;
+ };
+ enum { NUM_SEGMENTS = 33 };
+ int m_NumSegments;
+ Segment m_Segments[ NUM_SEGMENTS ];
+
+ tShader* mpShader;
+ rmt::Box3D m_BoundingBox;
+ bool m_IsInDSG;
+ RenderEnums::LayerEnum m_RenderLayer;
+ // The midpoint of the last position written, needed for texture coordinate generation
+ rmt::Vector m_LastPositionWritten;
+ float m_LastU;
+ float m_TextureDirection;
+ bool m_FadedOut;
+ // Is this the start of the skid? If so, fade it in
+ bool m_FadingIn;
+ rmt::Vector m_CurrentDirection;
+};
+
+#endif
diff --git a/game/code/worldsim/skidmarks/skidmarkmanager.cpp b/game/code/worldsim/skidmarks/skidmarkmanager.cpp
new file mode 100644
index 0000000..d1e5391
--- /dev/null
+++ b/game/code/worldsim/skidmarks/skidmarkmanager.cpp
@@ -0,0 +1,178 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SkidmarkManager
+//
+// Description:
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+//---------------------------------------------------------------------------
+// Includes
+//===========================================================================
+
+#include <worldsim/SkidMarks/skidmarkmanager.h>
+#include <memory/srrmemory.h>
+#include <worldsim/SkidMarks/skidmark.h>
+#include <radtime.hpp>
+
+//===========================================================================
+// Local Constants, Typedefs, and Macros
+//===========================================================================
+
+const float ALPHA_FADE_PER_MILLISECOND = 0.001f;
+
+//===========================================================================
+// Global Data, Local Data, Local Classes
+//===========================================================================
+
+SkidmarkManager* SkidmarkManager::spInstance = NULL;
+
+//===========================================================================
+// Member Functions
+//===========================================================================
+
+SkidmarkManager::ManagedSkidmark::ManagedSkidmark():
+age( 0 ),
+inUse( false )
+{
+ skidmark = new Skidmark();
+ skidmark->AddRef();
+}
+
+SkidmarkManager::ManagedSkidmark::~ManagedSkidmark()
+{
+ skidmark->RemoveFromDSG();
+ skidmark->Release();
+}
+
+
+SkidmarkManager::SkidmarkManager():
+m_SkidMarks( NULL ),
+m_UseTimedFadeouts( false )
+{
+
+}
+
+SkidmarkManager::~SkidmarkManager()
+{
+ Destroy();
+}
+
+
+
+SkidmarkManager* SkidmarkManager::CreateInstance()
+{
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ rAssert( spInstance == NULL );
+ spInstance = new SkidmarkManager();
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+
+ return spInstance;
+}
+
+SkidmarkManager* SkidmarkManager::GetInstance()
+{
+ return spInstance;
+}
+
+void SkidmarkManager::DestroyInstance()
+{
+ delete spInstance;
+}
+
+void SkidmarkManager::Update( unsigned int timeInMS )
+{
+
+ const unsigned int TIME_TO_WAIT_BEFORE_FADE = 5000;
+ unsigned int currentTime = radTimeGetMilliseconds();
+ if ( m_UseTimedFadeouts )
+ {
+
+ float deltaAlpha = static_cast< float >( timeInMS ) * ALPHA_FADE_PER_MILLISECOND;
+ // Iterate through all skidmarks, fading out the old ones that aren't in use
+ for ( int i = 0 ; i < m_NumSkidMarks ; i++ )
+ {
+ if ( m_SkidMarks[i]->age + TIME_TO_WAIT_BEFORE_FADE < currentTime )
+ {
+ if ( m_SkidMarks[i]->skidmark->IsInDSG() && m_SkidMarks[i]->inUse == false )
+ {
+ m_SkidMarks[i]->skidmark->FadeOut( deltaAlpha );
+ }
+ }
+ }
+ }
+}
+
+Skidmark* SkidmarkManager::GetUnusedSkidmark()
+{
+
+ Skidmark* availSkidmark = NULL;
+ for ( int i = 0 ; i < m_NumSkidMarks ; i++ )
+ {
+ if ( m_SkidMarks[ i ]->inUse == false && m_SkidMarks[ i ]->skidmark->IsVisible() == false )
+ {
+ availSkidmark = m_SkidMarks[i]->skidmark;
+ availSkidmark->ClearVertices();
+ m_SkidMarks[ i ]->inUse = true;
+
+ break;
+ }
+ }
+ return availSkidmark;
+}
+
+void SkidmarkManager::ReturnUsedSkidmark( Skidmark* skidmark )
+{
+ rAssert(m_SkidMarks);
+ if(!m_SkidMarks)
+ {
+ return;
+ }
+
+ for ( int i = 0 ; i < m_NumSkidMarks ; i++ )
+ {
+ if ( m_SkidMarks[ i ]->skidmark == skidmark )
+ {
+ rAssert( m_SkidMarks[i]->inUse == true );
+ m_SkidMarks[ i ]->inUse = false;
+ m_SkidMarks[ i ]->age = radTimeGetMilliseconds();
+ break;
+ }
+ }
+}
+
+void SkidmarkManager::Init( int numSkids )
+{
+
+ HeapMgr()->PushHeap( GMA_LEVEL_OTHER );
+ rAssert( m_SkidMarks == NULL );
+ m_NumSkidMarks = numSkids;
+ rAssert( m_SkidMarks == NULL );
+ m_SkidMarks = new ManagedSkidmark*[ numSkids ];
+
+ for ( int i = 0 ; i < m_NumSkidMarks ; i++)
+ {
+ m_SkidMarks[i] = new ManagedSkidmark();
+ }
+ HeapMgr()->PopHeap( GMA_LEVEL_OTHER );
+}
+
+void SkidmarkManager::Destroy()
+{
+
+ if ( m_SkidMarks != NULL )
+ {
+ for ( int i = 0 ; i < m_NumSkidMarks ; i++)
+ {
+ delete m_SkidMarks[i];
+ m_SkidMarks[i] = NULL;
+ }
+ delete [] m_SkidMarks;
+ m_SkidMarks = NULL;
+ }
+
+}
+
diff --git a/game/code/worldsim/skidmarks/skidmarkmanager.h b/game/code/worldsim/skidmarks/skidmarkmanager.h
new file mode 100644
index 0000000..057be54
--- /dev/null
+++ b/game/code/worldsim/skidmarks/skidmarkmanager.h
@@ -0,0 +1,94 @@
+//===========================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// Component: SkidmarkManager
+//
+// Description:
+//
+// Authors: Michael Riegger
+//
+//===========================================================================
+
+// Recompilation protection flag.
+#ifndef SKIDMARKMANAGER_H
+#define SKIDMARKMANAGER_H
+
+
+//===========================================================================
+// Nested Includes
+//===========================================================================
+
+//===========================================================================
+// Forward References
+//===========================================================================
+
+class Skidmark;
+
+//===========================================================================
+// Constants, Typedefs, and Macro Definitions (needed by external clients)
+//===========================================================================
+
+const int DEFAULT_NUM_SKID_MARKS = 30;
+
+//===========================================================================
+// Interface Definitions
+//===========================================================================
+
+//===========================================================================
+//
+// Description:
+// Generally, describe what behaviour this class possesses that
+// clients can rely on, and the actions that this service
+// guarantees to clients.
+//
+// Constraints:
+//
+//===========================================================================
+class SkidmarkManager
+{
+ public:
+
+ // Static Methods (for creating, destroying and acquiring an instance
+ // of the TriStripDSGManager)
+ static SkidmarkManager* CreateInstance();
+ static SkidmarkManager* GetInstance();
+ static void DestroyInstance();
+ static SkidmarkManager* spInstance;
+
+ void Update( unsigned int timeInMS );
+ Skidmark* GetUnusedSkidmark();
+ void ReturnUsedSkidmark( Skidmark* );
+
+ void SetTimedFadeouts( bool useFadeouts ) { m_UseTimedFadeouts = useFadeouts; }
+ void Init( int numSkids = DEFAULT_NUM_SKID_MARKS );
+ void Destroy();
+
+ private:
+ SkidmarkManager();
+ ~SkidmarkManager();
+ SkidmarkManager( const SkidmarkManager& );
+ SkidmarkManager& operator=( const SkidmarkManager& );
+
+ struct ManagedSkidmark
+ {
+ ManagedSkidmark();
+ ~ManagedSkidmark();
+
+ Skidmark* skidmark;
+ unsigned int age;
+ bool inUse;
+ };
+
+ ManagedSkidmark** m_SkidMarks;
+ // Returns a preallocated skidmark thats not currently in use at the moment
+ bool m_UseTimedFadeouts;
+ int m_NumSkidMarks;
+
+};
+// A little syntactic sugar for getting at this singleton.
+inline SkidmarkManager* GetSkidmarkManager()
+{
+ return( SkidmarkManager::GetInstance() );
+}
+
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/spawn/allspawn.cpp b/game/code/worldsim/spawn/allspawn.cpp
new file mode 100644
index 0000000..443b1d7
--- /dev/null
+++ b/game/code/worldsim/spawn/allspawn.cpp
@@ -0,0 +1 @@
+#include <worldsim/spawn/spawnmanager.cpp>
diff --git a/game/code/worldsim/spawn/spawnmanager.cpp b/game/code/worldsim/spawn/spawnmanager.cpp
new file mode 100644
index 0000000..357a791
--- /dev/null
+++ b/game/code/worldsim/spawn/spawnmanager.cpp
@@ -0,0 +1,107 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: spawnmanager.cpp
+//
+// Description: SpawnManager Class Implementation
+//
+// History: 11/5/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#include <worldsim/spawn/spawnmanager.h>
+
+void SpawnManager::Init()
+{
+ //////////////////////////////////////////////////////////////////
+ // Sanity check subclass implementations
+ rAssert( GetAbsoluteMaxObjects() >= 0 );
+ rAssert( GetMaxModels() >= 0 );
+ rAssert( GetSecondsBetwAdds() >= 0.0f );
+ rAssert( GetSecondsBetwRemoves() >= 0.0f );
+ rAssert( GetSecondsBetwUpdates() >= 0.0f );
+
+ mSecondsSinceLastAdd = 0.0f;
+ mSecondsSinceLastRemove = 0.0f;
+ mSecondsSinceLastUpdate = 0.0f;
+
+ mAddEnabled = true;
+ mRemoveEnabled = true;
+ mUpdateEnabled = true;
+}
+
+
+void SpawnManager::Update( float seconds )
+{
+ rAssert( seconds >= 0.0f );
+
+ if( !IsActive() )
+ {
+ return;
+ }
+
+ ///////////////////////////////////////////////////////
+ // Do some local sanity checks
+ rAssert( mSpawnRadius >= 0.0f );
+ rAssert( mRemoveRadius >= 0.0f );
+
+ ///////////////////////////////////////////////////////
+ // Do some enforcement of subclass implementations
+ rAssert( 0 <= mNumObjects && mNumObjects <= GetAbsoluteMaxObjects() );
+ rAssert( 0 <= GetMaxObjects() && GetMaxObjects() <= GetAbsoluteMaxObjects() );
+ rAssert( 0 < GetNumRegisteredModels() && GetNumRegisteredModels() <= GetMaxModels() );
+
+ ///////////////////////////////////////////////////////
+ // Do things in this order:
+ // - Remove everything that's outside the radius
+ // - Add at the fringes of the radius
+ // - Update as necessary.
+
+ /////////////////
+ // REMOVE PHASE
+ float secondsBetwRemoves = GetSecondsBetwRemoves();
+ rAssert( secondsBetwRemoves >= 0.0f );
+ if( mRemoveEnabled && mSecondsSinceLastRemove >= secondsBetwRemoves )
+ {
+ mSecondsSinceLastRemove = 0.0f;
+ RemoveObjects( seconds );
+ }
+ else
+ {
+ mSecondsSinceLastRemove += seconds;
+ }
+ rAssert( 0 <= mNumObjects && mNumObjects <= GetAbsoluteMaxObjects() );
+ /////////////////
+
+ ////////////////
+ // ADD PHASE
+ float secondsBetwAdds = GetSecondsBetwAdds();
+ rAssert( secondsBetwAdds >= 0.0f );
+ if( mAddEnabled && mSecondsSinceLastAdd >= secondsBetwAdds )
+ {
+ mSecondsSinceLastAdd = 0.0f;
+ AddObjects( seconds );
+ }
+ else
+ {
+ mSecondsSinceLastAdd += seconds;
+ }
+ rAssert( 0 <= mNumObjects && mNumObjects <= GetAbsoluteMaxObjects() );
+ ////////////////
+
+ ////////////////
+ // UPDATE PHASE
+ float secondsBetwUpdates = GetSecondsBetwUpdates();
+ rAssert( secondsBetwUpdates >= 0.0f );
+ if( mUpdateEnabled && mSecondsSinceLastUpdate >= secondsBetwUpdates )
+ {
+ mSecondsSinceLastUpdate = 0.0f;
+ UpdateObjects( seconds );
+ }
+ else
+ {
+ mSecondsSinceLastUpdate += seconds;
+ }
+ rAssert( (0 <= mNumObjects) && (mNumObjects <= GetAbsoluteMaxObjects() ) );
+ ////////////////
+}
diff --git a/game/code/worldsim/spawn/spawnmanager.h b/game/code/worldsim/spawn/spawnmanager.h
new file mode 100644
index 0000000..4a9ccc4
--- /dev/null
+++ b/game/code/worldsim/spawn/spawnmanager.h
@@ -0,0 +1,154 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: spawnmanager.h
+//
+// Description: SpawnManager Class Declaration
+//
+// History: 11/5/2002 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#ifndef SPAWNMANAGER_H
+#define SPAWNMANAGER_H
+
+#include <radmath/radmath.hpp>
+#include <raddebug.hpp>
+
+class SpawnManager
+{
+public:
+
+ SpawnManager();
+ virtual ~SpawnManager();
+ void Update( float seconds ); // don't allow overwriting of Update, which enforces structure
+
+ // PURE VIRTUALS
+ virtual void Init();
+ virtual void ClearAllObjects() = 0;
+ virtual void ClearObjectsInsideRadius( rmt::Vector center, float radius ) = 0;
+ virtual void ClearObjectsOutsideRadius( rmt::Vector center, float radius ) = 0;
+ virtual int GetAbsoluteMaxObjects() const = 0;
+ virtual int GetMaxObjects() const = 0;
+ virtual void SetMaxObjects( int maxObjects ) = 0;
+ virtual int GetMaxModels() const = 0;
+ virtual int GetNumRegisteredModels() const = 0;
+ virtual bool RegisterModel( const char* name, int spawnFreq ) = 0;
+ virtual bool UnregisterModel( const char* name ) = 0;
+
+ // ACCESSORS
+ bool IsActive() const;
+
+ // turning on will make use of current enable flags (all default to true)
+ // turning off will not doing anything (no adding, removing, updating whatsoever)
+ void SetActive( bool activeFlag );
+
+ void EnableAdd( bool enable );
+ void EnableRemove( bool enable );
+ void EnableUpdate( bool enable );
+
+ float GetSpawnRadius() const;
+ void SetSpawnRadius( float radius );
+ float GetRemoveRadius() const;
+ void SetRemoveRadius( float radius );
+
+ int GetNumObjects() const;
+
+
+protected:
+
+ // PURE VIRTUALS
+ // return # objects added/removed
+ virtual void AddObjects( float seconds ) = 0;
+ virtual void RemoveObjects( float seconds ) = 0;
+ virtual void UpdateObjects( float seconds ) = 0;
+
+ // subclass accessors
+ virtual float GetSecondsBetwAdds() const = 0;
+ virtual float GetSecondsBetwRemoves() const = 0;
+ virtual float GetSecondsBetwUpdates() const = 0;
+
+
+ bool mIsActive;
+ float mSpawnRadius; // in meters
+ float mRemoveRadius; // in meters
+ // Betw 0 and AbsoluteMax (defined by subclass)...
+ // NOTE: Not betw 0 and mMaxObjects because mMaxObjects can be changed
+ // to a lower number while there are mNumObjects > mMaxObjects
+ // still within radius (they don't get removed till an explicit
+ // call to ClearObjectsOutsideRadius takes place)
+ int mNumObjects;
+
+ float mSecondsSinceLastAdd;
+ float mSecondsSinceLastRemove;
+ float mSecondsSinceLastUpdate;
+
+ bool mAddEnabled;
+ bool mRemoveEnabled;
+ bool mUpdateEnabled;
+private:
+
+};
+
+inline SpawnManager::SpawnManager() :
+mIsActive( true ),
+mSpawnRadius( -1.0f ),
+mRemoveRadius( -1.0f ),
+mNumObjects( 0 ),
+mSecondsSinceLastAdd( 0.0f ),
+mSecondsSinceLastRemove( 0.0f ),
+mSecondsSinceLastUpdate( 0.0f ),
+mAddEnabled( true ),
+mRemoveEnabled( true ),
+mUpdateEnabled( true )
+{
+}
+
+inline SpawnManager::~SpawnManager()
+{
+}
+
+inline bool SpawnManager::IsActive() const
+{
+ return mIsActive;
+}
+inline void SpawnManager::SetActive( bool activeFlag )
+{
+ mIsActive = activeFlag;
+}
+inline float SpawnManager::GetSpawnRadius() const
+{
+ return mSpawnRadius;
+}
+inline void SpawnManager::SetSpawnRadius( float radius )
+{
+ rAssert( radius >= 0.0f );
+ mSpawnRadius = radius;
+}
+inline float SpawnManager::GetRemoveRadius() const
+{
+ return mRemoveRadius;
+}
+inline void SpawnManager::SetRemoveRadius( float radius )
+{
+ rAssert( radius >= 0.0f );
+ mRemoveRadius = radius;
+}
+inline int SpawnManager::GetNumObjects() const
+{
+ return mNumObjects;
+}
+inline void SpawnManager::EnableAdd( bool enable )
+{
+ mAddEnabled = enable;
+}
+inline void SpawnManager::EnableRemove( bool enable )
+{
+ mRemoveEnabled = enable;
+}
+inline void SpawnManager::EnableUpdate( bool enable )
+{
+ mUpdateEnabled = enable;
+}
+
+#endif
diff --git a/game/code/worldsim/traffic/alltraffic.cpp b/game/code/worldsim/traffic/alltraffic.cpp
new file mode 100644
index 0000000..f75d366
--- /dev/null
+++ b/game/code/worldsim/traffic/alltraffic.cpp
@@ -0,0 +1 @@
+#include <worldsim/traffic/trafficmanager.cpp>
diff --git a/game/code/worldsim/traffic/trafficmanager.cpp b/game/code/worldsim/traffic/trafficmanager.cpp
new file mode 100644
index 0000000..45ae929
--- /dev/null
+++ b/game/code/worldsim/traffic/trafficmanager.cpp
@@ -0,0 +1,2169 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: TrafficManager.cpp
+//
+// Description: Implement TrafficManager
+//
+// History: 09/09/2002 + Spawning/Removing vehicles -- Dusit Eakkachaichanvet
+// 04/07/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <raddebugwatch.hpp>
+#include <stdlib.h>
+
+//========================================
+// Project Includes
+//========================================
+#include <mission/gameplaymanager.h>
+
+#include <camera/supercammanager.h>
+#include <worldsim/traffic/TrafficManager.h>
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/trafficlocomotion.h>
+#include <memory/srrmemory.h>
+#include <debug/profiler.h>
+#include <worldsim/character/character.h>
+#include <worldsim/avatar.h>
+#include <worldsim/avatarmanager.h>
+#include <worldsim/redbrick/geometryvehicle.h>
+#include <worldsim/vehiclecentral.h>
+
+#include <ai/vehicle/trafficai.h>
+
+#include <roads/roadmanager.h>
+#include <roads/roadmanager.h>
+#include <roads/road.h>
+#include <roads/roadsegment.h>
+#include <roads/roadsegmentdata.h>
+#include <roads/lane.h>
+#include <roads/intersection.h>
+#include <roads/roadrendertest.h>
+
+#include <render/Culling/ReserveArray.h>
+#include <render/IntersectManager/IntersectManager.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+TrafficManager* TrafficManager::mInstance = NULL;
+
+//#define TRAFFIC_TEST
+#ifdef TRAFFIC_TEST
+ const float TrafficManager::FADE_RADIUS = 10.0f;
+ const float TrafficManager::ADD_RADIUS = 30.0f;
+ const float TrafficManager::CENTER_OFFSET = 20.0f;
+ const float TrafficManager::REMOVE_RADIUS = 35.0f;
+ const float TrafficManager::INITIAL_ADD_RADIUS = 20.0f;
+#else
+ const float TrafficManager::FADE_RADIUS = 85.0;
+ const float TrafficManager::CENTER_OFFSET = 40.0f;
+ const float TrafficManager::ADD_RADIUS = 65.0f;
+ const float TrafficManager::REMOVE_RADIUS = 75.0f;
+ const float TrafficManager::INITIAL_ADD_RADIUS = 100.0f;
+#endif
+
+const unsigned int TrafficManager::MILLISECONDS_BETWEEN_REMOVE = 200;
+const unsigned int TrafficManager::MILLISECONDS_BETWEEN_ADD = 200;
+const unsigned int TrafficManager::MILLISECONDS_POPULATE_WORLD = 3000;
+
+const unsigned int MILLISECONDS_STUNNED_AFTER_DEACTIVATED = 3000;
+
+const int MAX_TRAFFIC = 5;
+
+// Define all the swatch colours here...
+TrafficManager::SwatchColour TrafficManager::sSwatchColours[] =
+{
+ {148, 45, 12},
+ {133, 124, 4},
+ {110, 84, 145},
+ {170, 43, 74},
+ {48, 11, 102},
+
+ {110, 30, 32},
+ {140, 125, 207},
+ {195, 98, 31},
+ {122, 50, 69},
+ {148, 191, 229},
+
+ {0, 159, 123},
+ {0, 75, 132},
+ {43, 177, 166},
+ {250, 166, 23},
+ {172, 81, 127},
+
+ {185, 162, 232},
+ {229, 222, 33},
+ {163, 203, 60},
+ {213, 142, 210},
+ {255, 239, 158},
+
+ {122, 43, 103},
+ {181, 133, 70},
+ {68, 106, 171},
+ {59, 149, 36},
+ {205, 94, 0}
+};
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+void TrafficManager::InitDefaultModelGroups()
+{
+ for( int i=0; i<TrafficManager::MAX_TRAFFIC_MODEL_GROUPS; i++ )
+ {
+ // WORKING ONES
+ //mTrafficModelGroups[i].AddTrafficModel( "minivanA", 5 );
+ /*
+ mTrafficModelGroups[i].AddTrafficModel( "compactA", 1 );
+ mTrafficModelGroups[i].AddTrafficModel( "pickupA", 1 );
+ mTrafficModelGroups[i].AddTrafficModel( "sportsA", 2 );
+ mTrafficModelGroups[i].AddTrafficModel( "SUVA", 1 );
+ */
+ /*
+ mTrafficModelGroups[i].AddTrafficModel( "coffin", 2 );
+ mTrafficModelGroups[i].AddTrafficModel( "hallo",1 );
+ mTrafficModelGroups[i].AddTrafficModel( "ship", 1 );
+ mTrafficModelGroups[i].AddTrafficModel( "witchcar", 1 );
+ */
+
+ /*
+ mTrafficModelGroups[i].AddTrafficModel( "sportsB", 1 );
+ mTrafficModelGroups[i].AddTrafficModel( "wagonA", 1 );
+ mTrafficModelGroups[i].AddTrafficModel( "minivanA", 1 );
+ mTrafficModelGroups[i].AddTrafficModel( "taxiA", 2 );
+ */
+ /*
+ mTrafficModelGroups[i].AddTrafficModel( "sedanA", 3 );
+ mTrafficModelGroups[i].AddTrafficModel( "sedanB", 2 );
+ */
+ //mTrafficModelGroups[i].AddTrafficModel( "ambul", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "burnsarm", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "fishtruc", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "garbage", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "icecream", 5 );
+
+ //mTrafficModelGroups[i].AddTrafficModel( "IStruck", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "nuctruck", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "pizza", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "schoolbu", 5 );
+ //mTrafficModelGroups[i].AddTrafficModel( "votetruc", 5 );
+
+ // NOT TESTED
+ //mTrafficModelGroups[i].AddTrafficModel( "compactA", 5 );
+
+ // DEFAULTS
+ mTrafficModelGroups[i].AddTrafficModel( "compactA", 2 );
+ mTrafficModelGroups[i].AddTrafficModel( "pickupA", 2 );
+ mTrafficModelGroups[i].AddTrafficModel( "sportsA", 2 );
+ mTrafficModelGroups[i].AddTrafficModel( "SUVA", 2 );
+ }
+ mCurrTrafficModelGroup = 0;
+}
+
+
+ITrafficSpawnController* TrafficManager::GetSpawnController()
+{
+ return (ITrafficSpawnController*) TrafficManager::GetInstance();
+}
+
+
+TrafficManager* TrafficManager::GetInstance()
+{
+ MEMTRACK_PUSH_GROUP( "Traffic Manager" );
+ if ( !mInstance )
+ {
+ mInstance = new(GMA_LEVEL_OTHER) TrafficManager();
+ }
+ MEMTRACK_POP_GROUP( "Traffic Manager" );
+ return mInstance;
+}
+
+void TrafficManager::DestroyInstance()
+{
+ delete mInstance;
+ mInstance = NULL;
+}
+
+void TrafficManager::HandleEvent( EventEnum id, void* pEventData )
+{
+ if( id == EVENT_VEHICLE_DESTROYED )
+ {
+ // ignore it if it's not one of our vehicles...
+ Vehicle* v = (Vehicle*) pEventData;
+ SwapInTrafficHusk( v );
+ }
+
+ else if( id == EVENT_REPAIR_CAR )
+ {
+ //
+ // NOTE: When we want to support other cars than the user car
+ // picking up Wrench/repair icons in the main game, we should pass in
+ // the vehicle pointer when we trigger this event.
+ //
+ Vehicle* currVehicle = GetGameplayManager()->GetCurrentVehicle();
+ if( currVehicle == NULL )
+ {
+ return; // no current vehicle to repair.. oh well...
+ }
+ TrafficVehicle* tv = FindTrafficVehicle( currVehicle );
+ if( tv == NULL )
+ {
+ return; // not one of ours, don't worry...
+ }
+
+
+ // repair this vehicle..
+ if( currVehicle->mVehicleDestroyed )
+ {
+ bool usingHusk = false;
+ Vehicle* husk = tv->GetHusk();
+ if( husk )
+ {
+ usingHusk = true;
+ // damage is extensive... gotta replace husk with original vehicle
+
+ // obtain info from the husk
+ rmt::Vector initPos, initDir;
+ husk->GetPosition( &initPos );
+ initDir = husk->GetFacing();
+ rAssert( rmt::Epsilon( initDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // remove husk
+ bool succeeded = GetVehicleCentral()->RemoveVehicleFromActiveList( husk );
+ rAssert( succeeded );
+ /*
+ GetVehicleCentral()->SetVehicleController( tv->mActiveListIndex, NULL );
+ tv->mActiveListIndex = -1;
+ */
+
+ ::GetVehicleCentral()->mHuskPool.FreeHusk( husk );
+ // restore fade alpha if we set it, so other vehicles don't get confused
+ husk->mGeometryVehicle->SetFadeAlpha( 255 );
+ husk->Release();
+ husk = NULL;
+ tv->SetHusk( NULL );
+ tv->SetHasHusk( false );
+
+ currVehicle->SetInitialPosition( &initPos );
+ float angle = GetRotationAboutY( initDir.x, initDir.z );
+ currVehicle->SetResetFacingInRadians( angle );
+ currVehicle->Reset();
+ //currVehicle->SetLocomotion( VL_PHYSICS );
+
+ }
+
+ // put in original vehicle
+ int res = GetVehicleCentral()->AddVehicleToActiveList( currVehicle );
+ if( res == -1 )
+ {
+ // not supposed to happen since the list can't be full!!!
+ // we TOOK something out of the list before adding something in
+ // If this assert happens, it is both fatal and strange
+ rAssert( false );
+ return;
+ }
+ //tv->mActiveListIndex = res;
+ //GetEventManager()->TriggerEvent( EVENT_TRAFFIC_SPAWN, currVehicle ); // tell sound
+
+ if( usingHusk )
+ {
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar );
+
+ // if the avatar is inside a vehicle the vehicle
+ // is probably a husk, update this vehicle to be the original
+ // vehicle and place the character in this new vehicle
+ //
+ if( avatar->IsInCar() )
+ {
+ rAssert( avatar->GetVehicle() );
+ rAssert( GetVehicleCentral()->mHuskPool.IsHuskType( avatar->GetVehicle()->mVehicleID ) );
+
+ avatar->SetVehicle( currVehicle );
+
+ Character* character = avatar->GetCharacter();
+ GetAvatarManager()->PutCharacterInCar( character, currVehicle );
+ }
+ }
+
+ // fire off event so Esan can know when we switch the vehicle on him.
+ GetEventManager()->TriggerEvent( EVENT_VEHICLE_DESTROYED_SYNC_SOUND, currVehicle );
+
+ }
+ // repair any damage to original vehicle
+ bool resetDamage = true;
+ currVehicle->ResetFlagsOnly( resetDamage );
+ }
+
+ // If the player honks his horn, we trigger horn honking too for nearby traffic
+ else if( id == EVENT_PLAYER_VEHICLE_HORN )
+ {
+ // if queue for the last time we honked horn hasn't been cleared yet,
+ // don't do more...
+ if( mQueuedTrafficHorns.mUseSize > 0 )
+ {
+ return;
+ }
+
+ Vehicle* playerVehicle = (Vehicle*) pEventData;
+ rAssert( playerVehicle );
+
+ rmt::Vector playerPos;
+ playerVehicle->GetPosition( &playerPos );
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* traffV = &mVehicles[i];
+ if( !traffV->GetIsActive() )
+ {
+ continue;
+ }
+
+ if( mQueuedTrafficHorns.mUseSize >= MAX_QUEUED_TRAFFIC_HORNS )
+ {
+ break;
+ }
+
+ rmt::Vector traffPos;
+ traffV->GetVehicle()->GetPosition( &traffPos );
+
+ // within n meters; make sure n is less than traffic remove radius
+ const float CLOSE_ENOUGH_TO_HONK_BACK_SQR = 625.0f;
+
+ float distSqr = (traffPos - playerPos).MagnitudeSqr();
+ if( distSqr < CLOSE_ENOUGH_TO_HONK_BACK_SQR )
+ {
+ int coinflip = rand() % 10; // 1 in n chance of honking back
+ if( coinflip == 0 )
+ {
+ int delayInMilliseconds = rand() % 300 + 500;
+
+ TrafficHornQueue tmp;
+ tmp.delayInMilliseconds = static_cast<unsigned int>( delayInMilliseconds );
+ tmp.vehicle = traffV->GetVehicle();
+
+ mQueuedTrafficHorns.Add( tmp );
+ }
+ }
+ }
+
+ }
+}
+
+
+
+void TrafficManager::Init()
+{
+ //////////////////////////////////////////
+ // Do normal cleaning up...
+ Cleanup();
+
+ //////////////////////////////////////////
+ // Initialize some members
+ mNumTraffic = 0;
+ mMillisecondsBetweenRemove = TrafficManager::MILLISECONDS_BETWEEN_REMOVE;
+ mMillisecondsBetweenAdd = 0;
+ mMillisecondsPopulateWorld = TrafficManager::MILLISECONDS_POPULATE_WORLD;
+ mDesiredTrafficSpeedKph = DetermineDesiredSpeedKph();
+
+ Vehicle* newV = NULL; // vehicle
+ Vehicle* newH = NULL; // vehicle husk
+
+ //////////////////////////////////////////
+ // Initialize Traffic cars
+ for( int i=0 ; i<MAX_TRAFFIC ; i++ )
+ {
+ TrafficVehicle* tv = &mVehicles[i];
+ rAssert( tv != NULL );
+
+ newV = InitRandomVehicle();
+ rAssert( newV != NULL );
+ newV->AddRef();
+ newV->SetLocomotion( VL_TRAFFIC );
+ newV->mTrafficVehicle = tv;
+
+ tv->SetVehicle( newV );
+ tv->SetHusk( NULL );
+ }
+
+#ifdef DEBUGWATCH
+ char nameSpace[64];
+ sprintf( nameSpace, "Traffic Manager" );
+
+ radDbgWatchAddFloat( &mDesiredTrafficSpeedKph,
+ "Global Traffic Target Speed",
+ nameSpace,
+ NULL,
+ NULL,
+ 10.0f,
+ 200.0f );
+#endif
+
+}
+
+
+void TrafficManager::ClearOutOfSightTraffic()
+{
+ static const unsigned int MAX_MILLISECONDS_OUT_OF_SIGHT_BEFORE_REMOVAL = 5000;
+
+ Vehicle* playerVehicle = GetGameplayManager()->GetCurrentVehicle();
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* traffV = &mVehicles[i];
+ if( !traffV->GetIsActive() )
+ {
+ continue;
+ }
+
+ // if the player is using this vehicle, goddammit don't remove the thing
+ if( traffV->GetVehicle() == playerVehicle )
+ {
+ continue;
+ }
+
+ if( traffV->mOutOfSight &&
+ traffV->mMillisecondsOutOfSight > MAX_MILLISECONDS_OUT_OF_SIGHT_BEFORE_REMOVAL )
+ {
+ RemoveTraffic( i ); // remove this car
+ }
+ }
+}
+
+
+void TrafficManager::UpdateQueuedTrafficHorns( unsigned int milliseconds )
+{
+ // update our queue of traffic horns
+ int i=0;
+ while( i < mQueuedTrafficHorns.mUseSize )
+ {
+ if( mQueuedTrafficHorns[i].delayInMilliseconds <= milliseconds )
+ {
+ // trigger the event already, sheesh!
+ GetEventManager()->TriggerEvent( EVENT_TRAFFIC_HORN,
+ mQueuedTrafficHorns[i].vehicle );
+ mQueuedTrafficHorns.Remove( i );
+ }
+ else
+ {
+ mQueuedTrafficHorns[i].delayInMilliseconds -= milliseconds;
+ i++;
+ }
+ }
+}
+
+
+
+// ******************************
+//
+// BIG UPDATE METHOD
+//
+// ******************************
+
+void TrafficManager::Update( unsigned int milliseconds )
+{
+ rAssert( 0 <= mNumTraffic && mNumTraffic <= MAX_TRAFFIC );
+
+BEGIN_PROFILE( "Traffic Man" );
+
+ if( mMillisecondsPopulateWorld > milliseconds )
+ {
+ mMillisecondsPopulateWorld -= milliseconds;
+ }
+ else
+ {
+ mMillisecondsPopulateWorld = 0;
+ }
+
+ // play the queued up traffic horn sounds, if the time is right
+ UpdateQueuedTrafficHorns( milliseconds );
+
+ //
+ // ====================================
+ // Getting information about the player
+ // ====================================
+ //
+
+ Vehicle* v = NULL;
+
+ rmt::Vector pPos, pVel;//, pDir;
+ float pSpeed;
+
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ rAssert( avatar != NULL );
+ // Avatar::GetPosition() will return position of vehicle if avatar inside
+ // vehicle. Same deal with Avatar::GetVelocity() & GetSpeedMps()
+ // Remember that we should use VELOCITY rather than facing because
+ // the player can face one way and travel in reverse.
+ //
+ avatar->GetPosition(pPos);
+ avatar->GetVelocity(pVel);
+ pSpeed = avatar->GetSpeedMps();
+
+ // Get the camera for Player 1.
+ // It's what we need to apply the center offset to our spawn & remove radii
+ SuperCam* pCam = GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ rmt::Vector camTarget;
+ pCam->GetHeadingNormalized( &camTarget );
+ rAssert( rmt::Epsilon(camTarget.MagnitudeSqr(), 1.0f, 0.0005f) );
+
+ rmt::Vector center;
+ if( mMillisecondsPopulateWorld > 0 )
+ {
+ center = pPos;
+ }
+ else
+ {
+ center = pPos + camTarget * TrafficManager::CENTER_OFFSET;
+ }
+
+ /*
+ SuperCam* superCam = ::GetSuperCamManager()->GetSCC(0)->GetActiveSuperCam();
+ rAssert( superCam != NULL );
+ superCam->GetPosition( &pPos );
+ superCam->GetVelocity( &pVel );
+ pSpeed = pVel.Magnitude(); // *** SQUARE ROOT! ***
+ */
+ /*
+ if( pSpeed > 0.001f )
+ {
+ pDir = pVel;
+ pDir.Scale(1.0f / pSpeed);
+ }
+ else
+ {
+ avatar->GetHeading( pDir );
+ }
+ rAssert( rmt::Epsilon( pDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+ */
+
+ // for sphere intersection/containment testing
+ float minDistSqr = 0.0f;
+ rmt::Vector distVec;
+
+
+BEGIN_PROFILE( "Traffic Man: Remove" );
+
+ if( mMillisecondsBetweenRemove < milliseconds )
+ {
+ mMillisecondsBetweenRemove = MILLISECONDS_BETWEEN_REMOVE;
+
+ // don't remove if populating world
+ if( mMillisecondsPopulateWorld == 0 )
+ {
+ //
+ // ==================================
+ // Remove vehicles
+ // ===================================
+ // "Remove" from consideration vehicles that:
+ // - are no longer in the player's Traffic Radius, and/or
+ // - haven't been in player's view for some time
+ //
+ rmt::Sphere traffSphere;
+ traffSphere.Set( center, TrafficManager::REMOVE_RADIUS );
+ ClearTrafficOutsideSphere( traffSphere );
+
+ ClearOutOfSightTraffic();
+ }
+ }
+ else
+ {
+ mMillisecondsBetweenRemove -= milliseconds;
+ }
+
+END_PROFILE( "Traffic Man: Remove" );
+
+
+BEGIN_PROFILE( "Traffic Man: Add" );
+
+ //
+ // ==============================
+ // Adding cars to lanes as needed
+ // ==============================
+ // Imagine two overlapping circles. Circle1 is the traffic radius of your
+ // current position (center, radius), and Circle2 (center2, radius)
+ // is the traffic radius of your future position (based on velocity and
+ // assumption that it'll be just as long to reach the next loop as it took
+ // to reach the current loop).
+ //
+ if( mTrafficEnabled )
+ {
+
+ if( mMillisecondsBetweenAdd < milliseconds )
+ {
+ mMillisecondsBetweenAdd = MILLISECONDS_BETWEEN_ADD;
+
+ float addRadius = TrafficManager::ADD_RADIUS;
+ if( mMillisecondsPopulateWorld > 0 )
+ {
+ addRadius = TrafficManager::INITIAL_ADD_RADIUS;
+ }
+
+ rmt::Sphere pSphere( center, addRadius );
+
+ // FindRoadSegmentElems returns to us a list of RoadSegments whose bounding spheres
+ // come in contact with the player's traffic radius. These contact points give us
+ // the segments on the FRINGE of the traffic radius, where can we add cars.
+ //
+ // For each point of intersection, we only place down a car IF:
+ // a) the candidate car's predicted position in the NEXT frame lies within Circle 2
+ // (player's traffic zone in the NEXT frame)
+ // AND
+ // b) the lane containing this segment has fewer cars on it than the desired
+ // density value.
+ //
+ // The reason for FindRoadSegmentElems (which consequently forced us to build DSG
+ // Tree out of RoadSegments, involving a lot of work) is that a lane isn't a
+ // straight line. It's comprised of meandering segments. A lane can enter and exit
+ // the player's Traffic Radius as many times as it desires.
+ //
+ ReserveArray<RoadSegment*> orList;
+ ::GetIntersectManager()->FindRoadSegmentElems( center, addRadius, orList );
+
+ bool noMoreFreeTrafficVehicles = false;
+
+ for( int i=0; i<orList.mUseSize; i++ )
+ {
+ RoadSegment* segment;
+ unsigned int numLanes;
+
+ segment = orList.mpData[i];
+ rAssert( segment != NULL );
+
+ numLanes = segment->GetNumLanes();
+ rAssert( numLanes >= 1 );
+
+ // loop through all the lanes for this segment
+ for( unsigned int j=0; j<numLanes; j++ )
+ {
+
+ Road* road;
+ Lane* lane;
+ unsigned int nDesiredDensity;
+
+ road = segment->GetRoad();
+ rAssert( road != NULL );
+
+ lane = road->GetLane( j );
+ nDesiredDensity = lane->GetDensity();
+
+ // HACK:
+ // We only have 5 traffic cars man c'mon...
+ if(nDesiredDensity > 2 )
+ {
+ nDesiredDensity = 2;
+ }
+
+ // we add vehicle only if number on the lane < expected density
+ // AND if projected next position of the car is in the projected
+ // next position of the player's Traffic Radius
+
+ if( lane->mTrafficVehicles.mUseSize < (int)(nDesiredDensity) )
+ {
+ // Here we're determining where in the world to place the car.
+ // We try to place it at the lane location where it intersects
+ // with our traffic zone (pSphere).
+
+ rmt::Vector startPos, startDir, endPos, endDir;
+ segment->GetLaneLocation( 0.0f, j, startPos, startDir );
+ segment->GetLaneLocation( 1.0f, j, endPos, endDir );
+
+ rmt::Vector intPts[2];
+ int numIntersections = IntersectLineSphere( startPos, endPos, pSphere, intPts );
+ if(numIntersections <= 0)
+ {
+ continue;
+ }
+
+ rmt::Vector cPos, cDir;
+ cDir.Sub( endPos, startPos );
+ cDir.Normalize(); // *** SQUARE ROOT! ***
+
+ // for each intersection point found, plant a vehicle
+ for( int k=0; k<numIntersections; k++ )
+ {
+ // HACK:
+ // Designers want maxtraffic to be always 5 if you're on foot
+ // unless mMaxTraffic is actually 0, in which case, we leave at
+ // zero.
+ int maxTrafficToUse = mMaxTraffic;
+ if( mMaxTraffic > 0 && !avatar->IsInCar())
+ {
+ maxTrafficToUse = MAX_TRAFFIC_MODEL_GROUPS;
+ }
+
+ if( mNumTraffic < maxTrafficToUse )
+ {
+ // set cPos;
+ cPos = intPts[k];
+
+ // see if we got any more vehicles...
+ TrafficVehicle* tv = this->GetFreeTrafficVehicle();
+ if( tv == NULL )
+ {
+ noMoreFreeTrafficVehicles = true;
+ break;
+ }
+ v = tv->GetVehicle();
+ rAssert( v != NULL );
+
+
+ //
+ // We should detect if we're placing this car on top of another car
+ // We need to search the entire ActiveVehicles list, not just the
+ // traffic cars in our lane, to take into account vehicles that
+ // are lying around on the road in VL_PHYSICS
+ //
+ int nActiveVehicles = 0;
+ Vehicle** activeVehicles = NULL;
+
+ VehicleCentral* vc;
+ vc = ::GetVehicleCentral();
+ vc->GetActiveVehicleList( activeVehicles, nActiveVehicles );
+
+
+ rmt::Sphere cSphere;
+ v->GetBoundingSphere( &cSphere );
+
+
+ bool tryNextPoint = false;
+ int vCount = 0;
+ for( int i=0; i<vc->GetMaxActiveVehicles(); i++ )
+ {
+ if( vCount >= nActiveVehicles )
+ {
+ break;
+ }
+
+ Vehicle* aCar = activeVehicles[i];
+ if( aCar == NULL )
+ {
+ continue;
+ }
+ vCount++;
+
+ rmt::Vector aPos;
+ rmt::Sphere aSphere;
+ aCar->GetPosition( &aPos );
+ aCar->GetBoundingSphere( &aSphere );
+
+ float distSqr = (aPos - cPos).MagnitudeSqr();
+
+ // if same as our lane, make sure they're at least some
+ // lookahead distance away...
+ float minDist = 5.0f; // initial buffer...
+ if( aCar->mTrafficLocomotion->GetAILane() == lane )
+ {
+ /*
+ float lookAhead = aCar->mTrafficLocomotion->GetAISpeed() *
+ TrafficAI::SECONDS_LOOKAHEAD;
+ */
+ float lookAhead = aCar->mTrafficLocomotion->mActualSpeed *
+ TrafficAI::SECONDS_LOOKAHEAD;
+ if( lookAhead < TrafficAI::LOOKAHEAD_MIN )
+ {
+ lookAhead = TrafficAI::LOOKAHEAD_MIN;
+ }
+ minDist += lookAhead;
+ }
+ else
+ {
+ minDist += aSphere.radius + cSphere.radius;
+ }
+
+ float minDistSqr = minDist * minDist;
+ if( distSqr < minDistSqr )
+ {
+ // if we're too near another car, don't spawn here...
+ tryNextPoint = true;
+ break;
+ }
+ }
+ if( tryNextPoint )
+ {
+ continue;
+ }
+
+
+ //
+ // Check if in NEXT few seconds, vehicle will still be in
+ // player's traffic zone.
+ //
+ const float SECONDS_LOOK_AHEAD = 1.5f;
+
+ // next frame player pos & traffic zone
+ rmt::Vector center2 = center + pVel * SECONDS_LOOK_AHEAD;
+
+ float speedLimit = lane->GetSpeedLimit();
+ rmt::Vector cVel = cDir * speedLimit;
+ rmt::Vector cPos2 = cPos + cVel * SECONDS_LOOK_AHEAD;
+
+ minDistSqr = TrafficManager::REMOVE_RADIUS * TrafficManager::REMOVE_RADIUS;
+ distVec.Sub( cPos2, center2 );
+
+
+ if( distVec.MagnitudeSqr() < minDistSqr )
+ {
+
+ bool succeeded = this->AddTraffic(lane, tv);
+ if( !succeeded )
+ {
+ // can't add more traffic to this lane, do next lane
+ continue;
+ }
+
+
+ // Determine which point on the segment we are at...
+ // Since we know that cPos is on the line between startPos & endPos
+ // of the segment, we only need to test one coordinate (that didn't
+ // remain the same, of course)... This should give us a good enough
+ // approximation (within 0.00001)
+ //
+ float segmentT = GetLineSegmentT( startPos, endPos, cPos );
+ rAssert( 0.0f <= segmentT && segmentT <= 1.0f );
+
+ // INITIALIZE
+
+ // Get a random color for this vehicle
+ pddiColour randomColour;
+ GenerateRandomColour( randomColour );
+ v->mGeometryVehicle->SetTrafficBodyColour( randomColour );
+
+ // initialize vehicle's position & facing
+ v->SetInitialPosition( &cPos );
+ float angle = GetRotationAboutY( cDir.x, cDir.z );
+ v->SetResetFacingInRadians( angle );
+ v->Reset();
+ v->mHijackedByUser = false;
+
+ // set up TrafficLocomotion/TrafficAI info
+ v->mTrafficLocomotion->Init();
+ v->mTrafficLocomotion->InitPos( cPos );
+ v->mTrafficLocomotion->InitFacing( cDir );
+ v->mTrafficLocomotion->InitVehicleAI( v );
+ v->mTrafficLocomotion->InitLane( lane, j, lane->GetSpeedLimit() );
+ v->mTrafficLocomotion->InitSegment( segment, segment->GetSegmentIndex(), segmentT );
+ v->mTrafficLocomotion->SetActive( true );
+
+ // determine initial speed for traffic.
+ // if far from road's end, go at desired speed
+ float speed = 0.0f;
+ rmt::Vector segEnd, segFacing;
+ RoadSegment* lastSeg = road->GetRoadSegment( road->GetNumRoadSegments()-1 );
+ lastSeg->GetLaneLocation( 1.0f, 0, segEnd, segFacing );
+ const float MIN_DIST_FROM_ROAD_END_SQR = 100.0f;
+ if( (cPos-segEnd).MagnitudeSqr() > MIN_DIST_FROM_ROAD_END_SQR )
+ {
+ speed = GetDesiredTrafficSpeed();
+ }
+ v->mTrafficLocomotion->SetAISpeed( speed );
+
+ v->mVehicleType = VT_TRAFFIC;
+ v->SetLocomotion(VL_TRAFFIC);
+
+ } // end if vehicle's next pos lies in radius of player's next traffic zone
+ }
+ }
+ if( noMoreFreeTrafficVehicles )
+ {
+ break;
+ }
+ } // end if(lane density < desired density)
+
+ } // end for-loop through all the lanes in a particular segment
+
+ if( noMoreFreeTrafficVehicles )
+ {
+ break;
+ }
+
+ } // end for-loop through all segments returned by DSGFind
+ }
+ else
+ {
+ mMillisecondsBetweenAdd -= milliseconds;
+ }
+ }
+
+END_PROFILE( "Traffic Man: Add" );
+
+BEGIN_PROFILE( "Traffic Man: Update AI & Intersections" );
+
+ //
+ // ============================================================================
+ // Go through the vehicles, updating
+ // ============================================================================
+ //
+
+ // at first, no intersection has been updated in this update call.
+ for( int i=0; i<MAX_INTERSECTIONS; i++ )
+ {
+ mpIntersections[i] = NULL;
+ }
+ int nIntersectionsUpdated = 0;
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* traffV = &mVehicles[i];
+ if( !traffV->GetIsActive() )
+ {
+ continue;
+ }
+
+ if( traffV->HasHusk() )
+ {
+ v = traffV->GetHusk();
+ }
+ else
+ {
+ v = traffV->GetVehicle();
+ }
+ rAssert( v );
+
+ // Test for out of sight of player 0... If so, increment timer, if not reset timer
+ rmt::Vector vPos;
+ v->GetPosition( &vPos );
+ traffV->mOutOfSight = !GetGameplayManager()->TestPosInFrustrumOfPlayer( vPos, 0 );
+
+ traffV->mMillisecondsOutOfSight += milliseconds;
+ if( !traffV->mOutOfSight )
+ {
+ traffV->mMillisecondsOutOfSight = 0;
+ }
+
+ // Determine fade alpha.. All active (in the world) traffic vehicles
+ // fade. Whether or not they're in VL_TRAFFIC or VL_PHYSICS is irrelevant
+ SetVehicleFadeAlpha( v, pPos );
+
+ // figure out if we should allow entering the traffic vehicle
+ const float GETIN_SPEED_THRESHOLD_MPS = 4.5f; // this is as fast as character can run
+ if( !avatar->IsInCar() && !v->mHijackedByUser )
+ {
+ rAssert( v->mVehicleType != VT_AI );
+ if( v->mSpeed > GETIN_SPEED_THRESHOLD_MPS )
+ {
+ v->ActivateTriggers( false );
+ }
+ else
+ {
+ v->ActivateTriggers( true );
+ }
+ }
+
+ if( v->GetLocomotionType() == VL_PHYSICS && !v->mHijackedByUser )
+ {
+ traffV->mMillisecondsDeactivated += milliseconds;
+
+ // Check through vehicles VL_PHYSICS to see if we can bring any of them
+ // back to life:
+ // - check health
+ // - check distance from last segment
+ // - check if we've been deactivated long enough
+ AttemptResurrection( traffV );
+ }
+
+ if( v->GetLocomotionType() == VL_TRAFFIC )
+ {
+ // Update Vehicle AI
+ //v->mTrafficLocomotion->UpdateAI(milliseconds);
+
+ // Update intersection
+ UpdateIntersection( milliseconds, v, nIntersectionsUpdated );
+ }
+ }
+END_PROFILE( "Traffic Man: Update AI & Intersections" );
+
+
+
+END_PROFILE( "Traffic Man" );
+}
+
+void TrafficManager::ClearTrafficOutsideSphere( const rmt::Sphere& s )
+{
+ rmt::Vector distVec;
+ rmt::Vector vPos;
+ rmt::Sphere vSphere;
+ float minDistSqr = 0.0f;
+
+ Vehicle* playerVehicle = GetGameplayManager()->GetCurrentVehicle();//GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle();
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* traffV = &mVehicles[i];
+ rAssert( traffV != NULL );
+
+ if( !traffV->GetIsActive() )
+ {
+ continue;
+ }
+
+ Vehicle* v = NULL;
+ if( traffV->HasHusk() )
+ {
+ v = traffV->GetHusk();
+ }
+ else
+ {
+ v = traffV->GetVehicle();
+ }
+ rAssert( v != NULL );
+
+ // if the player is driving this traffic vehicle, don't take it away from him!
+ if( traffV->GetVehicle() == playerVehicle ) // get original vehicle to compare, not husk
+ {
+ continue;
+ }
+
+ v->GetPosition( &vPos );
+
+ v->GetBoundingSphere( &vSphere );
+ vSphere.centre = vPos;
+
+ minDistSqr = s.radius * s.radius;
+ distVec.Sub( vSphere.centre, s.centre );
+ if( distVec.MagnitudeSqr() < minDistSqr )
+ {
+ // BAH! vehicle still in traffic zone... leave it alone
+ continue;
+ }
+
+ // At this point, we want to kill the vehicle
+ // because it is now outside our traffic zone
+ RemoveTraffic( i );
+ }
+}
+
+void TrafficManager::ClearTrafficInSphere( const rmt::Sphere& s )
+{
+ rmt::Vector distVec;
+ rmt::Vector vPos;
+ rmt::Sphere vSphere;
+ float minDistSqr = 0.0f;
+
+ Vehicle* playerVehicle = GetGameplayManager()->GetCurrentVehicle();//GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle();
+
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* traffV = &mVehicles[i];
+ rAssert( traffV != NULL );
+
+ if( !traffV->GetIsActive() )
+ {
+ continue;
+ }
+
+ Vehicle* v = NULL;
+ if( traffV->HasHusk() )
+ {
+ v = traffV->GetHusk();
+ }
+ else
+ {
+ v = traffV->GetVehicle();
+ }
+ rAssert( v != NULL );
+
+ // if the player is driving this traffic vehicle, don't take it away from him!
+ if( traffV->GetVehicle() == playerVehicle )
+ {
+ continue;
+ }
+
+ v->GetPosition( &vPos );
+
+ v->GetBoundingSphere( &vSphere );
+ vSphere.centre = vPos;
+
+ minDistSqr = s.radius * s.radius;
+ distVec.Sub( vSphere.centre, s.centre );
+
+ // If vehicle still in sphere... kill it
+ if( distVec.MagnitudeSqr() < minDistSqr )
+ {
+ // At this point, we want to kill the vehicle
+ // because it is now outside our traffic zone
+ RemoveTraffic( i );
+ }
+ }
+}
+
+
+void TrafficManager::RemoveTraffic( Vehicle* vehicle )
+{
+ rAssert( vehicle != NULL );
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* tv = &mVehicles[i];
+ rAssert( tv != NULL );
+
+ Vehicle* v = NULL;
+ if( GetVehicleCentral()->mHuskPool.IsHuskType( vehicle->mVehicleID ) )
+ {
+ v = tv->GetHusk();
+ }
+ else
+ {
+ v = tv->GetVehicle();
+ }
+
+ // found it.
+ if( vehicle == v )
+ {
+ RemoveTrafficVehicle( tv );
+ }
+ }
+}
+
+
+void TrafficManager::EnableTraffic()
+{
+ if( !CommandLineOptions::Get(CLO_NO_TRAFFIC) && !DISABLETRAFFIC )
+ {
+ mTrafficEnabled = true;
+ }
+}
+
+void TrafficManager::DisableTraffic()
+{
+ mTrafficEnabled = false;
+}
+
+void TrafficManager::AddCharacterToStopFor( Character* character )
+{
+ rAssert( character != NULL );
+
+ bool assigned = false;
+
+ for( int i=0; i<MAX_CHARS_TO_STOP_FOR; i++ )
+ {
+ if( mCharactersToStopFor[i] == character )
+ {
+ mCharactersToStopFor[i]->Release();
+ mCharactersToStopFor[i] = NULL;
+ }
+ if( !assigned && mCharactersToStopFor[i] == NULL )
+ {
+ tRefCounted::Assign( mCharactersToStopFor[i], character );
+ assigned = true;
+ mNumCharsToStopFor++;
+ }
+ }
+}
+
+void TrafficManager::RemoveCharacterToStopFor( Character* character )
+{
+ rAssert( character != NULL );
+
+ for( int i=0; i<MAX_CHARS_TO_STOP_FOR; i++ )
+ {
+ if( mCharactersToStopFor[i] == character )
+ {
+ mCharactersToStopFor[i]->Release();
+ mCharactersToStopFor[i] = NULL;
+ mNumCharsToStopFor--;
+ }
+ }
+}
+
+void TrafficManager::GenerateRandomColour( pddiColour& colour )
+{
+ int alpha = 255;
+ int red, green, blue;
+
+ int index = rand() % TrafficManager::NUM_SWATCH_COLOURS;
+ red = sSwatchColours[index].red;
+ green = sSwatchColours[index].green;
+ blue = sSwatchColours[index].blue;
+ /*
+ red = rand() % 256;
+ green = rand() % 256;
+ blue = rand() % 256;
+ */
+ colour.Set( red, green, blue, alpha );
+}
+
+void TrafficManager::Deactivate( Vehicle* vehicle )
+{
+ rAssert( vehicle != NULL );
+
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* tv = &mVehicles[i];
+ rAssert( tv != NULL );
+
+ Vehicle* v = tv->GetVehicle();
+
+ // found it.
+ if( vehicle == v )
+ {
+ rAssert( v != NULL );
+ v->mTrafficLocomotion->SetActive( false );
+ //
+ // Remove the vehicle from the lane
+ Lane* lane = tv->GetLane();
+ if( lane )
+ {
+ for( int j=0; j<lane->mTrafficVehicles.mUseSize; j++ )
+ {
+ if( tv == lane->mTrafficVehicles[j] )
+ {
+ lane->mTrafficVehicles.Remove(j);
+ break;
+ }
+ }
+ }
+
+ // NOTE:
+ // hold off NULLing out the lane variable.
+ // We could use it in case we want to resurrect it
+ //tv->SetLane( NULL );
+
+ // NOTE:
+ // DO NOT SET TrafficVehicle::mIsActive TO FALSE HERE,
+ // We want traffic manager to consider it as being
+ // active still (in the sense that it's still in the world and is
+ // subject to removal). We just want to remove it from the lane and
+ // deactivate its AI...
+ //tv->SetIsActive( false );
+
+ // start the deactivation timer
+ tv->mMillisecondsDeactivated = 0;
+ tv->mCanBeResurrected = true;
+ }
+ }
+
+}
+
+void TrafficManager::SwapInTrafficHusk( Vehicle* vehicle )
+{
+ rAssert( vehicle );
+
+ TrafficVehicle* tv = FindTrafficVehicle( vehicle );
+ if( tv == NULL )
+ {
+ return;
+ }
+
+ // obtain info from the vehicle
+ rmt::Vector initPos, initDir;
+ vehicle->GetPosition( &initPos );
+ initDir = vehicle->GetFacing();
+ rAssert( rmt::Epsilon( initDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ //
+ // Deactivate traffic vehicle, take it out of the world, swap in a free husk...
+ // that sort of thing
+ //
+ Deactivate( vehicle );
+ bool succeeded = ::GetVehicleCentral()->RemoveVehicleFromActiveList( vehicle );
+ rAssert( succeeded );
+ //GetVehicleCentral()->SetVehicleController( tv->mActiveListIndex, NULL );
+ GetEventManager()->TriggerEvent( EVENT_TRAFFIC_REMOVE, vehicle );
+
+ //
+ // Now we grab husk and put it in place of the original vehicle
+ //
+ Vehicle* husk = InitRandomHusk( vehicle );
+ if( husk == NULL )
+ {
+ // by returning here, we have removed the traffic car from the
+ // manager, the lane, and world scene... But not entirely! (RemoveTrafficVehicle
+ // will be called on it when it goes out of range later...) Good job!
+ return;
+ }
+ int res = ::GetVehicleCentral()->AddVehicleToActiveList( husk );
+ if( res == -1 )
+ {
+ // not supposed to happen since the list can't be full!!!
+ // we TOOK something out of the list before adding something in
+ // If this assert happens, it is both fatal and strange
+ rAssert( false );
+ return;
+ }
+ //tv->mActiveListIndex = res;
+ GetVehicleCentral()->SetVehicleController( res, husk->mTrafficLocomotion->GetAI() );
+
+
+ husk->AddRef();
+ husk->SetInitialPosition( &initPos );
+ float angle = GetRotationAboutY( initDir.x, initDir.z );
+ husk->SetResetFacingInRadians( angle );
+ husk->Reset();
+ husk->SetLocomotion( VL_PHYSICS );
+
+ /*
+ // tell the avatar that it's now in a husk
+ Avatar* avatar = GetAvatarManager()->GetAvatarForPlayer( 0 );
+ avatar->SetVehicle(husk);
+ */
+
+ tv->SetHusk( husk );
+
+ // tell the TrafficVehicle that it is destroyed
+ tv->SetHasHusk( true );
+
+}
+
+void TrafficManager::SetMaxTraffic( int n )
+{
+ if( n < 0 )
+ {
+ n = 0;
+ }
+ else if( n > MAX_TRAFFIC )
+ {
+ n = MAX_TRAFFIC;
+ }
+
+ mMaxTraffic = n;
+}
+
+int TrafficManager::GetMaxTraffic()
+{
+ return mMaxTraffic;
+}
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
+
+TrafficManager::TrafficManager()
+{
+ // start listening for events
+ GetEventManager()->AddListener( this, EVENT_VEHICLE_DESTROYED );
+ GetEventManager()->AddListener( this, EVENT_REPAIR_CAR );
+ GetEventManager()->AddListener( this, EVENT_PLAYER_VEHICLE_HORN );
+
+ mVehicles = new TrafficVehicle[ MAX_TRAFFIC ];
+
+ mCurrTrafficModelGroup = -1;
+ mMaxTraffic = MAX_TRAFFIC;
+
+ // initialize NULL list.
+ mNumCharsToStopFor = 0;
+ for( int i=0; i<MAX_CHARS_TO_STOP_FOR; i++ )
+ {
+ mCharactersToStopFor[i] = NULL;
+ }
+
+ mTrafficEnabled = !DISABLETRAFFIC;
+ if( CommandLineOptions::Get(CLO_NO_TRAFFIC) )
+ {
+ mTrafficEnabled = false;
+ }
+
+ mQueuedTrafficHorns.Allocate( MAX_QUEUED_TRAFFIC_HORNS );
+
+}
+TrafficManager::~TrafficManager()
+{
+ GetEventManager()->RemoveAll( this );
+ Cleanup();
+
+ mQueuedTrafficHorns.Clear();
+ delete[] mVehicles;
+}
+
+Vehicle* TrafficManager::InitRandomVehicle()
+{
+ rAssert( mCurrTrafficModelGroup >= 0 );
+
+ Vehicle* newV = NULL;
+
+ // randomly choose a model index
+ int numModels = mTrafficModelGroups[mCurrTrafficModelGroup].GetNumModels();
+ int modelIndex = rand() % numModels;
+
+ TrafficModel* tm = NULL;
+
+ // if there are already too many instances of traffic cars under this model
+ // we go to the next model... and so on until we're out of models.
+ int count = 0;
+ while( count < numModels )
+ {
+ tm = mTrafficModelGroups[mCurrTrafficModelGroup].GetTrafficModel( modelIndex );
+ if( tm->mNumInstances >= tm->mMaxInstances )
+ {
+ modelIndex = (modelIndex+1) % numModels;
+ count++;
+ continue;
+ }
+ else
+ {
+ break;
+ }
+ }
+
+ if( count >= numModels )
+ {
+ char message[256];
+ sprintf( message, "DOH! Can't initialize a traffic car \"%s\" because\n"
+ "we already have %d of max %d instances allowed.\n"
+ "See leveli.mfk to increase the max allowed for this model\n",
+ tm->mModelName, tm->mNumInstances, tm->mMaxInstances );
+
+ rTuneAssertMsg( false, message );
+ return NULL;
+ }
+
+ newV = ::GetVehicleCentral()->InitVehicle( tm->mModelName, false, 0, VT_TRAFFIC );
+ rTuneAssert( newV != NULL );
+ tm->mNumInstances++;
+
+ return newV;
+}
+
+void TrafficManager::Cleanup()
+{
+ // Clean up list of characters to stop for...
+ //
+ for( int i=0; i<MAX_CHARS_TO_STOP_FOR; i++ )
+ {
+ if( mCharactersToStopFor[i] != NULL )
+ {
+ mCharactersToStopFor[i]->Release();
+ mCharactersToStopFor[i] = NULL;
+ mNumCharsToStopFor--;
+ }
+ }
+ rAssert( mNumCharsToStopFor == 0 );
+
+ // Clean up swap array
+ //
+ mQueuedTrafficHorns.ClearUse();
+
+ // Clean up traffic vehicles
+ //
+ for( int i=0 ; i<MAX_TRAFFIC ; i++ )
+ {
+ TrafficVehicle* tv = &mVehicles[i];
+ rAssert( tv != NULL );
+
+ Vehicle* v = tv->GetVehicle();
+ if( tv->GetIsActive() )
+ {
+ // tell trafficlocomotion to be inactive,
+ // clear out traffic vehicle from a lane,
+ // reset trafficvehicle members (except vehicle pointer member) to indeterminate values,
+ // decrement numtraffic count by 1,
+ // remove from activelist
+ RemoveTraffic( i );
+ }
+
+ rAssert( tv->GetHusk() == NULL );
+
+ if( v != NULL )
+ {
+ v->ReleaseVerified();
+ tv->SetVehicle( NULL );
+ }
+ }
+
+ // clean up intersections
+ for( int j=0; j<MAX_INTERSECTIONS; j++ )
+ {
+ mpIntersections[j] = NULL;
+ }
+
+#ifdef DEBUGWATCH
+ radDbgWatchDelete( &mDesiredTrafficSpeedKph );
+#endif
+
+}
+
+bool TrafficManager::AddTraffic(Lane* lane, TrafficVehicle* tv)
+{
+ rAssert( tv != NULL );
+ rAssert( tv->GetIsActive() == false );
+ rAssert( tv->GetLane() == NULL );
+
+ Vehicle* v = tv->GetVehicle();
+ rAssert( v != NULL );
+ rAssert( v->mTrafficLocomotion->GetActive() == false );
+
+ // add vehicle to the lane
+ // if there was some error in adding (say if list was full)
+ // then just abort
+ if( lane->mTrafficVehicles.mUseSize >= lane->mTrafficVehicles.mSize )
+ {
+ return false;
+ }
+
+ // add vehicle to ActiveList
+ if( ::GetVehicleCentral()->ActiveVehicleListIsFull() )
+ {
+ return false;
+ }
+
+ v->mVehicleType = VT_TRAFFIC;
+
+ int res = ::GetVehicleCentral()->AddVehicleToActiveList( v );
+ if( res == -1 )
+ {
+ // not supposed to happen since we already eliminated the
+ // safe failure condition (activelistisfull)
+ rAssert( false );
+ return false;
+ }
+
+ /////// NO MORE BAILING OUT AFTER THIS POINT /////////
+
+ lane->mTrafficVehicles.Add( tv );
+
+ // now add the AI to active vehicle controller list
+ GetVehicleCentral()->SetVehicleController( res, v->mTrafficLocomotion->GetAI() );
+
+ // set trafficVehicle fields
+ tv->SetLane( lane );
+ tv->SetIsActive( true );
+ tv->SetHasHusk( false );
+ //tv->mActiveListIndex = res;
+ tv->mMillisecondsDeactivated = 0;
+ tv->mMillisecondsOutOfSight = 0;
+ tv->mOutOfSight = true;
+
+ ::GetEventManager()->TriggerEvent( EVENT_TRAFFIC_SPAWN, v );
+
+ mNumTraffic++;
+ return true;
+}
+
+void TrafficManager::RemoveTraffic( int vIndex )
+{
+ int i = vIndex;
+ TrafficVehicle* tv = &mVehicles[i];
+ RemoveTrafficVehicle( tv );
+}
+
+void TrafficManager::RemoveTrafficVehicle( TrafficVehicle* tv )
+{
+ rAssert( tv != NULL );
+ rAssert( tv->GetIsActive() == true );
+
+ Vehicle* v = tv->GetVehicle();
+ rAssert( v != NULL );
+ v->mTrafficLocomotion->SetActive( false );
+
+ // Remove the vehicle from the lane, if it hasn't been removed
+ // already (if the car was Deactivated, it will have already been
+ // removed from the lane.. the lane pointer will not be NULL cuz
+ // it's needed later for resurrection, but the traffic index will
+ // be -1.)
+ Lane* lane = tv->GetLane();
+ if( lane != NULL )
+ {
+ for( int i=0; i<lane->mTrafficVehicles.mUseSize; i++ )
+ {
+ if( tv == lane->mTrafficVehicles[i] )
+ {
+ lane->mTrafficVehicles.Remove(i);
+ break;
+ }
+ }
+ }
+
+ // remove Vehicle (or its husk if it's destroyed) from ActiveList
+ if( tv->HasHusk() )
+ {
+ Vehicle* husk = tv->GetHusk();
+ rAssert( husk );
+
+ bool succeeded = ::GetVehicleCentral()->RemoveVehicleFromActiveList( husk );
+ rAssert( succeeded );
+
+ ::GetVehicleCentral()->mHuskPool.FreeHusk( husk );
+ // restore fade alpha if we set it, so other vehicles don't get confused
+ husk->mGeometryVehicle->SetFadeAlpha( 255 );
+ husk->Release(); // don't verify destruction cuz huskpool has final ownership
+ tv->SetHusk( NULL );
+ }
+ else
+ {
+ Vehicle* v = tv->GetVehicle();
+ rAssert( v );
+ rAssert( tv->GetHusk() == NULL );
+
+ GetVehicleCentral()->RemoveVehicleFromActiveList( v );
+ GetEventManager()->TriggerEvent( EVENT_TRAFFIC_REMOVE, v );
+ }
+
+ // remove from our traffic system
+ //GetVehicleCentral()->SetVehicleController( tv->mActiveListIndex, NULL );
+ tv->SetLane( NULL );
+ tv->SetIsActive( false );
+ tv->SetHasHusk( false );
+ //tv->mActiveListIndex = -1;
+ tv->mMillisecondsDeactivated = 0;
+ tv->mMillisecondsOutOfSight = 0;
+ tv->mOutOfSight = true;
+
+ mNumTraffic--;
+
+ // search through queued traffic horns list and remove self...
+ Vehicle* traffVehicle = tv->GetVehicle();
+ for( int i=0; i<mQueuedTrafficHorns.mUseSize; i++ )
+ {
+ if( mQueuedTrafficHorns[i].vehicle == traffVehicle )
+ {
+ mQueuedTrafficHorns.Remove( i );
+ break;
+ }
+ }
+}
+
+TrafficVehicle* TrafficManager::GetFreeTrafficVehicle()
+{
+ // gotta start at some random index, so we don't recycle
+ // the same car (and thus same car model) over and over again
+ // when maxtraffic is set to a small number
+
+ int randIndex = rand() % MAX_TRAFFIC;
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ if( !mVehicles[randIndex].GetIsActive() )
+ {
+ return &mVehicles[randIndex];
+ }
+ randIndex = (randIndex + 1) % MAX_TRAFFIC;
+ }
+ return NULL;
+}
+
+float TrafficManager::DetermineDesiredSpeedKph()
+{
+ float speedKph = 0.0f;
+ switch( GetGameplayManager()->GetCurrentLevelIndex() )
+ {
+ case RenderEnums::L1:
+ speedKph = 60.0f;
+ break;
+ case RenderEnums::L2:
+ speedKph = 60.0f;
+ break;
+ case RenderEnums::L3:
+ speedKph = 60.0f;
+ break;
+ case RenderEnums::L4:
+ speedKph = 60.0f;
+ break;
+ case RenderEnums::L5:
+ speedKph = 60.0f;
+ break;
+ case RenderEnums::L6:
+ speedKph = 60.0f;
+ break;
+ case RenderEnums::L7:
+ speedKph = 60.0f;
+ break;
+ default:
+ rAssert( false );
+ break;
+ }
+ return speedKph;
+}
+
+TrafficVehicle* TrafficManager::FindTrafficVehicle( Vehicle* vehicle )
+{
+ for( int i=0; i<MAX_TRAFFIC; ++i )
+ {
+ TrafficVehicle* tv = &mVehicles[i];
+ rAssert( tv != NULL );
+
+ Vehicle* v = tv->GetVehicle();
+ if( vehicle == v )
+ {
+ return tv;
+ }
+ }
+ return NULL;
+}
+
+bool TrafficManager::IsVehicleTrafficVehicle( Vehicle* vehicle )
+{
+ return FindTrafficVehicle( vehicle ) != NULL;
+}
+
+
+Vehicle* TrafficManager::InitRandomHusk( Vehicle* v )
+{
+ Vehicle* husk = ::GetVehicleCentral()->mHuskPool.RequestHusk( VT_TRAFFIC, v );
+ return husk;
+}
+
+void TrafficManager::UpdateIntersection( unsigned int milliseconds, Vehicle* v, int& nIntersectionsUpdated )
+{
+ rAssert( v->GetLocomotionType() == VL_TRAFFIC );
+ rAssert( v != NULL );
+
+ Intersection* intersection = NULL;
+ if( !v->mTrafficLocomotion->IsInIntersection() )
+ {
+ intersection = (Intersection*) v->mTrafficLocomotion->
+ GetAILane()->GetRoad()->GetDestinationIntersection();
+ }
+ else
+ {
+ intersection = (Intersection*) v->mTrafficLocomotion->
+ GetAILane()->GetRoad()->GetSourceIntersection();
+ }
+
+ // update the intersection if it hasn't already been updated this frame
+ bool foundIntersection = false;
+ for( int i=0; i<nIntersectionsUpdated; i++ )
+ {
+ if( mpIntersections[i] == intersection )
+ {
+ foundIntersection = true;
+ break;
+ }
+ }
+ if( !foundIntersection )
+ {
+ if( nIntersectionsUpdated < MAX_INTERSECTIONS )
+ {
+ mpIntersections[nIntersectionsUpdated] = intersection;
+ nIntersectionsUpdated++;
+ intersection->Update(milliseconds);
+ }
+ }
+}
+
+void TrafficManager::SetVehicleFadeAlpha( Vehicle* v, const rmt::Vector& pPos )
+{
+ rAssert( v );
+
+ rmt::Vector vPos;
+ v->GetPosition( &vPos );
+ float distFromPlayer = (pPos - vPos).Length(); // *** SQUARE ROOT! ***
+
+ float fadeMinLimit = TrafficManager::FADE_RADIUS;
+ float fadeMaxLimit = TrafficManager::ADD_RADIUS + TrafficManager::CENTER_OFFSET;
+ int fadeAlpha = 255;
+ if( fadeMinLimit <= distFromPlayer && distFromPlayer <= fadeMaxLimit )
+ {
+ // if we're in the fading zone, gotta change fade alpha
+ float fadeRatio = (distFromPlayer - fadeMinLimit)/(fadeMaxLimit - fadeMinLimit);
+ fadeAlpha = (int) (255.0f * (1.0f - fadeRatio));
+ }
+ else if( distFromPlayer > fadeMaxLimit )
+ {
+ fadeAlpha = 0;
+ }
+
+ v->mGeometryVehicle->SetFadeAlpha( fadeAlpha );
+}
+
+bool TrafficManager::AttemptResurrection( TrafficVehicle* tv )
+{
+ rAssert( tv );
+ rAssert( tv->GetIsActive() );
+
+ Vehicle* v = tv->GetVehicle();
+ rAssert( v );
+ rAssert( v->mTrafficLocomotion );
+ rAssert( v->mTrafficLocomotion->GetActive() == false );
+
+ // if has been hijacked by user, don't resurrect
+ if( v->mHijackedByUser )
+ {
+ tv->mCanBeResurrected = false;
+ return false;
+ }
+
+ // if we have determined that it can't be resurrect, no point trying
+ if( !tv->mCanBeResurrected )
+ {
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ // don't allow resurrection right away... cool down... cool down... cool down...
+ if( tv->mMillisecondsDeactivated < MILLISECONDS_STUNNED_AFTER_DEACTIVATED )
+ {
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ if( v->mVehicleDestroyed )
+ {
+ // reset it to 0 so we don't check again every frame
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ // Check through vehicles VL_PHYSICS to see if we can bring any of them
+ // back to life:
+ // - check health
+ // - check distance from last segment
+ // - check if we've been deactivated long enough
+
+ // if not at rest yet, don't resurrect...
+ if( !v->IsAtRest() )
+ {
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ // If the lane won't support us anymore, wait for awhile
+ Lane* oldLane = tv->GetLane();
+ rAssert( oldLane != NULL );
+ if( oldLane->mTrafficVehicles.mUseSize >= oldLane->GetDensity() )
+ {
+ // reset it to 0 so there's a lull before we check again..
+ tv->mMillisecondsDeactivated = 0;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ //
+ // check if there's anything ahead ...
+ //
+ TrafficAI::ObstacleType foundSOMETHING = TrafficAI::OT_NOTHING;
+ float distFromSOMETHINGSqr = 100000.0f;
+ void* SOMETHING = NULL;
+ bool SOMETHINGOnMyRight = false;
+
+ v->mTrafficLocomotion->GetAI()->CheckForObstacles(
+ foundSOMETHING, distFromSOMETHINGSqr, SOMETHING, SOMETHINGOnMyRight );
+
+ if( foundSOMETHING != TrafficAI::OT_NOTHING )
+ {
+ // reset it to 0 so there's a lull before we check again..
+ tv->mMillisecondsDeactivated = 0;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+
+
+ rmt::Vector vPos, vDir, vUp;
+ v->GetPosition( &vPos );
+
+ vDir = v->GetFacing();
+ rAssert( rmt::Epsilon( vDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ v->GetVUP( &vUp );
+ rAssert( rmt::Epsilon( vUp.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // ACTION:
+ // - if we were in an intersection, just build spline straight to the
+ // target segment (no problem!). Remain in DRIVING state.
+ // - else
+ // - Make sure we're not too far from closest point on last segment
+ // - Make sure the line from us to closest point on last segment does not
+ // cross over the segment (meaning that we were outside the segment...
+ // e.g. on the sidewalk... in which case we don't recover)
+ // - Build spline to a position 5 meters ahead of the closestPt on segment
+ // flowing onto a new segment as necessary... do not flow over into an
+ // intersection. Store out segment's t value and transit to RECOVERING
+ // state. When done RECOVERING state, add t value to the t.
+
+ TrafficAI* ai = v->mTrafficLocomotion->GetAI();
+ rAssert( ai );
+
+ RoadSegment* seg = ai->GetSegment();
+ rAssert( seg );
+
+ int laneIndex = (int)(ai->GetLaneIndex());
+
+ const float TOLERANCE_DIST_SQR = 64.0f;
+ const float UP_COSALPHA = 0.9848077f;
+ const float FACING_RESURRECT_COSAPLHA = 0.0f;
+
+ rmt::Vector targetPos, targetDir;
+
+ if( v->mTrafficLocomotion->IsInIntersection() ||
+ ai->GetState() == TrafficAI::LANE_CHANGING )
+ {
+ // TODO:
+ // The assumption that the intersection's UP vector is simply 0,1,0 is
+ // no longer valid since we don't enforce the intersection to be completely
+ // horizontal anymore. So... we have to determine the up vector ourselves...
+ // *shudder*....
+
+ // If vehicle's up vector isn't anywhere close to the horizontal up vector
+ // (0,1,0), don't resurrect (it might be tipped over or laying on its side)
+ rmt::Vector testUp( 0.0f, 1.0f, 0.0f );
+ if( vUp.Dot( testUp ) < UP_COSALPHA )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ // Just build a spline to the end of the intersection, if we're
+ // close enough to the original intersection spline
+ rmt::Vector* ways;
+ int nPts, currPt;
+ v->mTrafficLocomotion->GetSplineCurve( ways, nPts, currPt );
+
+ rAssert( ways ); // we should have mWays populated since we're in the intersection
+
+ rmt::Vector tmpStart, tmpEnd, tmpClosestPt;
+ tmpStart = ways[0];
+ tmpEnd = ways[nPts-1];
+ FindClosestPointOnLine( tmpStart, tmpEnd, vPos, tmpClosestPt );
+
+ const float CLOSE_ENOUGH_TO_ORIG_SPLINE_SQR = 16.0f;
+ float distSqr = (vPos - tmpClosestPt).MagnitudeSqr();
+ if( distSqr > CLOSE_ENOUGH_TO_ORIG_SPLINE_SQR )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ // now it's time to get the target of our new spline...
+ seg->GetLaneLocation( 0.0f, laneIndex, targetPos, targetDir );
+
+ // if our pos is too close to the end, don't do it;
+ // it will look too strange...
+ const float US_FAR_ENOUGH_FROM_TARGET_POS_SQR = 25.0f;
+ distSqr = (vPos - targetPos).MagnitudeSqr();
+ if( distSqr < US_FAR_ENOUGH_FROM_TARGET_POS_SQR )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ // if the closest point on original spline is too close to
+ // the end, don't do it; it will look too strange...
+ const float PROJ_FAR_ENOUGH_FROM_TARGET_POS_SQR = 25.0f;
+ distSqr = (tmpClosestPt - targetPos).MagnitudeSqr();
+ if( distSqr < PROJ_FAR_ENOUGH_FROM_TARGET_POS_SQR )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ if( !rmt::Epsilon( targetDir.MagnitudeSqr(), 1.0f, 0.001f ) )
+ {
+ targetDir.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( targetDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // If vehicle's facing is at too great an angle from target dir,
+ // and our lateral distance is not that far, don't do it...
+ // (it will look too strange)
+ if( targetDir.Dot( vDir ) < FACING_RESURRECT_COSAPLHA )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ ////////////////////// OK, NO ABORTING NOW //////////////////////
+ if( ai->GetState() == TrafficAI::LANE_CHANGING )
+ {
+ ai->SetState( TrafficAI::SPLINING );
+ }
+ }
+ else
+ {
+ // Here we're neither lane-changing nor in an intersection
+
+ // Find the closest seg along road (rather than use the last seg we were on)
+ const Road* road = seg->GetRoad();
+ rAssert( road );
+
+ float closestDistSqr = TOLERANCE_DIST_SQR;
+ RoadSegment* closestSeg = NULL;
+ rmt::Vector closestPt, start, end, closestPtOnSeg;
+
+ unsigned int numSegs = road->GetNumRoadSegments();
+ for( unsigned int i=0; i<numSegs; i++ )
+ {
+ RoadSegment* aSeg = road->GetRoadSegment( i );
+ rAssert( aSeg );
+
+ rmt::Vector tmpStart, tmpEnd, tmpDir;
+ aSeg->GetLaneLocation( 0.0f, laneIndex, tmpStart, tmpDir );
+ aSeg->GetLaneLocation( 1.0f, laneIndex, tmpEnd, tmpDir );
+
+ FindClosestPointOnLine( tmpStart, tmpEnd, vPos, closestPtOnSeg );
+
+ float distSqr = (vPos - closestPtOnSeg).MagnitudeSqr();
+ if( distSqr < closestDistSqr )
+ {
+ closestDistSqr = distSqr;
+ closestSeg = aSeg;
+ closestPt = closestPtOnSeg;
+ start = tmpStart;
+ end = tmpEnd;
+ }
+ }
+ if( closestSeg == NULL )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ seg = closestSeg;
+
+ // If vehicle's up vector isn't anywhere close to the segment's up vector,
+ // don't resurrect (it might be tipped over or laying on its side)
+ rmt::Vector testUp;
+ seg->GetSegmentNormal( testUp );
+ rAssert( rmt::Epsilon( testUp.MagnitudeSqr(), 1.0f, 0.001f ) );
+ if( vUp.Dot( testUp ) < UP_COSALPHA )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+
+ // determine starting t
+ float startT = GetLineSegmentT( start, end, closestPt );
+ rAssert( 0.0f <= startT && startT <= 1.0f );
+
+ // TODO:
+ // Maybe make sure line from us to closestPt doesn't cross over a segment bound
+ // (test against line segments---v0,v1 and v2,v3---to make sure that
+ // closestPt and vPos are both on the left of v2,v3 and right of v0,v1)
+
+ // find a new lane position, 5 meters ahead...
+ unsigned int segmentIndex = seg->GetSegmentIndex();
+ float pathLength = seg->GetLaneLength( laneIndex );
+ float t = startT;
+ float distAhead = 10.0f;
+ t += distAhead / pathLength;
+ while( t > 1.0f )
+ {
+ t -= 1.0f;
+
+ if( segmentIndex < (numSegs-1) )
+ {
+ // move ahead a segment
+ segmentIndex++;
+ seg = road->GetRoadSegment( segmentIndex );
+ float newLength = seg->GetLaneLength( laneIndex );
+ t *= pathLength / newLength;
+ pathLength = newLength;
+ }
+ else // if we're out of segments.. we are at an intersection...
+ {
+ // TODO:
+ // Aborting here keeps us from being able to resurrect properly if
+ // we're closestDistSqr meters or less from the intersection (on approach).
+ // We need to be able to deal effectively with this case..
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+ }
+
+ rAssert( 0.0f <= t && t <= 1.0f );
+
+ seg->GetLaneLocation( t, laneIndex, targetPos, targetDir );
+ if( !rmt::Epsilon( targetDir.MagnitudeSqr(), 1.0f, 0.001f ) )
+ {
+ targetDir.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( targetDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ // If vehicle's facing is at too great an angle from target dir,
+ // and our lateral distance is not that far, don't do it...
+ // (it will look too strange)
+ if( targetDir.Dot( vDir ) < FACING_RESURRECT_COSAPLHA )
+ {
+ tv->mCanBeResurrected = false;
+ tv->GetVehicle()->GetSimState()->SetControl( sim::simSimulationCtrl );
+ return false;
+ }
+
+ //////////////////////////// OK NO ABORTING NOW ////////////////////////
+ // Update TrafficAI to look at the new segment, segIndex, and outT
+ ai->SetSegmentIndex( segmentIndex );
+ ai->SetState( TrafficAI::SPLINING );
+
+ v->mTrafficLocomotion->mOutLaneT = t;
+ }
+
+ float restHeightAboveGround = v->GetRestHeightAboveGround();
+
+ /////////////////////////////////////////////
+ // Adjust ground height (unfortunately we need to do this
+ // because we use pos averaging (so our y value is off)
+ rmt::Vector groundPosition, outnorm;
+ bool bFoundPlane = false;
+
+ groundPosition = vPos;
+ outnorm.Set( 0.0f, 1.0f, 0.0f );
+
+ GetIntersectManager()->FindIntersection(
+ groundPosition, // IN
+ bFoundPlane, // OUT
+ outnorm, // OUT
+ groundPosition // OUT
+ );
+
+ if( bFoundPlane )
+ {
+ vPos.y = groundPosition.y + restHeightAboveGround;
+ }
+
+ ///////////////////////////////////
+ // build spline
+ vPos.y -= restHeightAboveGround;
+ //vDir.y = 0.0f;
+ if( !rmt::Epsilon( vDir.MagnitudeSqr(), 1.0f, 0.001f ) )
+ {
+ vDir.NormalizeSafe(); // *** SQUARE ROOT! ***
+ }
+ rAssert( rmt::Epsilon( vDir.MagnitudeSqr(), 1.0f, 0.001f ) );
+
+ v->mTrafficLocomotion->BuildArbitraryCurve( vPos, vDir, targetPos, targetDir );
+
+
+ // bring vehicle back into traffic:
+ // - set loco back to VL_TRAFFIC
+ // - put it back in the same lane, on the closest point on the closestSeg
+ // - TrafficLomotion::SetIsActive(true)
+
+ /* NOTE:
+ Don't need to do this sheeyatsu... the vehicle is already where
+ we want it to be and the simstate is up to date.
+ v->SetInitialPosition( &vPos );
+ float angle = GetRotationAboutY( vDir.x, vDir.z );
+ v->SetResetFacingInRadians( angle );
+ v->Reset();
+ */
+
+ // Just a test to see if it's ever anything else... if it is, this could be
+ // a problem.. Please notify Dusit.
+ rAssert( v->mVehicleType == VT_TRAFFIC );
+
+ v->mVehicleType = VT_TRAFFIC;
+ v->SetLocomotion( VL_TRAFFIC );
+ v->mTrafficLocomotion->SetActive( true );
+ v->mTrafficLocomotion->SetAISpeed( 0.0f );
+ v->mTrafficLocomotion->InitPos( vPos );
+ v->mTrafficLocomotion->InitFacing( vDir );
+
+ // Readd the vehicle to the lane
+ oldLane->mTrafficVehicles.Add( tv );
+
+ return true;
+}
+
+
diff --git a/game/code/worldsim/traffic/trafficmanager.h b/game/code/worldsim/traffic/trafficmanager.h
new file mode 100644
index 0000000..0979e87
--- /dev/null
+++ b/game/code/worldsim/traffic/trafficmanager.h
@@ -0,0 +1,312 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trafficmanager.h
+//
+// Description: Blahblahblah
+//
+// History: 09/09/2002 + Spawning/Removing vehicles -- Dusit Eakkachaichanvet
+// 04/07/2002 + Created -- Cary Brisebois
+//
+//=============================================================================
+
+#ifndef TRAFFICMANAGER_H
+#define TRAFFICMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <worldsim/traffic/trafficvehicle.h>
+#include <worldsim/traffic/trafficmodelgroup.h>
+#include <ai/vehicle/trafficai.h>
+#include <events/eventlistener.h>
+#include <roads/intersection.h>
+
+#include <render/culling/swaparray.h>
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class Lane;
+class Character;
+
+
+//********************************************
+// USED TO CONTROL WHETHER OR NOT TRAFFIC CARS GET ADDED TO THE WORLD
+// FOR THE PURPOSES OF TESTING.
+//
+const bool DISABLETRAFFIC = false;
+//
+//********************************************
+
+
+struct ITrafficSpawnController
+{
+ virtual void SetMaxTraffic( int n ) = 0;
+ virtual void EnableTraffic() = 0;
+ virtual void DisableTraffic() = 0;
+ virtual void ClearTrafficInSphere( const rmt::Sphere& s ) = 0;
+};
+
+/*
+These are the constraints Traffic lives by.
+
+Road Segment
+============
+- Try not to make angles of the edge normals deviate too greatly from
+ the segment's "direction". i.e Avoid this:
+
+ |
+ / |
+ / |
+ / |
+ / |
+ / |
+ | |
+ |__________|
+
+- a single road segment is a flat polygon (of coplanar vertices).
+- road world builder data must conform to the art/physics.
+- road segments should end BEFORE they cross over the (art-side)
+ crosswalks. Otherwise traffic cars will run over people crossing the street.
+- no road segment is overlapping another
+- the road segments that belong to the same road must lie within half a meter
+ apart from one another to be considered attached to that road.
+
+
+Intersections
+=========
+- plane of the intersection (formed by in & out road segments) must be
+ FLAT FLAT FLAT... This means completely X-Z horizontal.
+
+- For each intersection "x", for every IN road "y" belonging to "x", if "y"
+ has more than 1 OUT road ("z"?) other than a U-turn OUT road going back
+ the way it came ("w"?), then it needs to be type N_WAY (type value of 1
+ for the world builders). Otherwise its type is NO_STOP (type value 0). This
+ goes for all intersections, including the "fake" ones we use to join
+ Zones/Rails.
+
+- The road data for IN & OUT segments of "fake" intersections (used to
+ join zones/rails) need to leave a small (1 meter or less) "gap" between
+ one another. Otherwise the traffic cars will appear to "flip". More
+ generally: no 2 roads going in or out of any intersection are
+ touching/overlapping one another.
+
+- the centre of any intersection (hand-placed) is more or less at the
+ physical centre of the intersection.
+
+- There is an appreciable/reasonable distance between one intersection and
+ another. This translates to something like 10 meters or more.
+
+General
+======
+- The world is not round or overlapping. In other words, if I were to extend
+ an infinite line, parallel to y-axis down through the world, it will only
+ cut through only one single road segment or intersection plane. This is a
+ precautionary step only. Not sure what drastic effects failing to conform
+ to this constraint will produce, but I imagine that most of the code
+ assumes we are in 2.5D, not 3D.
+
+
+
+Might have missed some.. but there you are.
+*/
+
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class TrafficManager :
+ ITrafficSpawnController,
+ public EventListener
+{
+public:
+
+ static TrafficManager* mInstance;
+
+ static const float FADE_RADIUS;
+ static const float ADD_RADIUS;
+ static const float CENTER_OFFSET;
+ static const float INITIAL_ADD_RADIUS;
+ static const float REMOVE_RADIUS;
+ static const unsigned int MILLISECONDS_BETWEEN_REMOVE;
+ static const unsigned int MILLISECONDS_BETWEEN_ADD;
+ static const unsigned int MILLISECONDS_POPULATE_WORLD;
+
+ enum {
+ MAX_CHARS_TO_STOP_FOR = 10 // Max # characters traffic will brake for
+ };
+
+ static ITrafficSpawnController* GetSpawnController();
+ static TrafficManager* GetInstance();
+ static void DestroyInstance();
+
+ ///////////////////////////////////
+ // EVENTLISTENTER STUFF
+ virtual void HandleEvent( EventEnum id, void* pEventData );
+ ///////////////////////////////////
+
+ void Init();
+ void InitDefaultModelGroups();
+ void Update( unsigned int milliseconds );
+
+ void Cleanup();
+
+ void AddCharacterToStopFor( Character* character );
+ void RemoveCharacterToStopFor( Character* character );
+ int GetNumCharsToStopFor();
+ Character* GetCharacterToStopFor( int i ) const;
+
+ // Given its pointer, find it in our static list of TrafficVehicles and
+ // take it out of lane traffic and out of active list. Also invalidate
+ // TrafficVehicle-specific fields.
+ void RemoveTraffic( Vehicle* vehicle ); //Take this out of traffic.
+ void Deactivate( Vehicle* v );
+
+ void SetMaxTraffic( int n );
+ int GetMaxTraffic();
+
+ void EnableTraffic();
+ void DisableTraffic();
+ void ClearTrafficInSphere( const rmt::Sphere& s );
+ void ClearTrafficOutsideSphere( const rmt::Sphere& s );
+
+ void ClearOutOfSightTraffic();
+
+ TrafficModelGroup* GetTrafficModelGroup( int i );
+ void SetCurrTrafficModelGroup( int i );
+
+ float GetDesiredTrafficSpeed();
+ void GenerateRandomColour( pddiColour& colour );
+ void SwapInTrafficHusk( Vehicle* vehicle ); // Remove traffic vehicle, swap in a husk...
+
+ bool IsVehicleTrafficVehicle( Vehicle* vehicle );
+
+//MEMBERS
+private:
+ float mDesiredTrafficSpeedKph;
+
+ enum {
+ MAX_TRAFFIC_MODEL_GROUPS = 10, // Max # different groups of traffic models
+ MAX_INTERSECTIONS = 5, // Max # intersections we'll be keeping track of (calling Update to)
+ NUM_SWATCH_COLOURS = 25, // # artist-defined swatch colours
+ MAX_QUEUED_TRAFFIC_HORNS = 3
+ };
+ int mMaxTraffic; // value is betw 0 and MAX_TRAFFIC
+ int mNumTraffic; // value changes between 0 and MAX_TRAFFIC as vehicles are added/removed
+
+ // keep repository of all the vehicles we'll ever need.
+ TrafficVehicle* mVehicles;
+
+ Character* mCharactersToStopFor[ MAX_CHARS_TO_STOP_FOR ];
+ int mNumCharsToStopFor;
+
+ // keep list of intersections that will need to be updated in this loop
+ Intersection* mpIntersections[ MAX_INTERSECTIONS ];
+
+ unsigned int mMillisecondsBetweenRemove;
+ unsigned int mMillisecondsBetweenAdd;
+ unsigned int mMillisecondsPopulateWorld;
+
+ bool mTrafficEnabled;
+
+ TrafficModelGroup mTrafficModelGroups[ MAX_TRAFFIC_MODEL_GROUPS ];
+ int mCurrTrafficModelGroup;
+
+ struct SwatchColour
+ {
+ int red;
+ int green;
+ int blue;
+ };
+ static SwatchColour sSwatchColours[ NUM_SWATCH_COLOURS ];
+
+ struct TrafficHornQueue
+ {
+ Vehicle* vehicle;
+ unsigned int delayInMilliseconds;
+ };
+ SwapArray<TrafficHornQueue> mQueuedTrafficHorns;
+ int mNumQueuedTrafficHorns;
+
+//METHODS
+private:
+
+ float DetermineDesiredSpeedKph();
+
+ // Add a traffic vehicle to the lane and to active list and Initialize
+ // some TrafficVehicle-specific fields
+ bool AddTraffic( Lane* lane, TrafficVehicle* tv );
+
+ // Given its index in our static list of TrafficVehicles, take vehicle out
+ // of lane traffic and out of active list. Also invalidate TrafficVehicle-
+ // specific fields.
+ void RemoveTraffic( int vIndex );
+
+ void RemoveTrafficVehicle( TrafficVehicle* tv );
+
+ TrafficVehicle* FindTrafficVehicle( Vehicle* vehicle );
+ TrafficVehicle* GetFreeTrafficVehicle();
+ Vehicle* InitRandomVehicle();
+ Vehicle* InitRandomHusk( Vehicle* v );
+
+
+ // update the intersections to let cars go in turn (if NWAY)
+ void UpdateIntersection(
+ unsigned int milliseconds,
+ Vehicle* v,
+ int& nIntersectionsUpdated );
+
+ void SetVehicleFadeAlpha( Vehicle* v, const rmt::Vector& pPos );
+
+ bool AttemptResurrection( TrafficVehicle* tv );
+
+ void UpdateQueuedTrafficHorns( unsigned int milliseconds );
+
+ // constructors/destructors we wish to hide so we can implement singleton
+ TrafficManager();
+ virtual ~TrafficManager();
+
+ //Prevent wasteful constructor creation.
+ TrafficManager( const TrafficManager& trafficmanager );
+ TrafficManager& operator=( const TrafficManager& trafficmanager );
+};
+
+
+
+
+// ************************************ INLINES *******************************************
+
+
+inline int TrafficManager::GetNumCharsToStopFor()
+{
+ return mNumCharsToStopFor;
+}
+inline Character* TrafficManager::GetCharacterToStopFor( int i ) const
+{
+ rAssert( 0 <= i && i < MAX_CHARS_TO_STOP_FOR );
+ return mCharactersToStopFor[i];
+}
+inline TrafficModelGroup* TrafficManager::GetTrafficModelGroup( int i )
+{
+ rTuneAssert( 0 <= i && i < MAX_TRAFFIC_MODEL_GROUPS );
+ return &mTrafficModelGroups[i];
+}
+inline void TrafficManager::SetCurrTrafficModelGroup( int i )
+{
+ rTuneAssert( 0 <= i && i < MAX_TRAFFIC_MODEL_GROUPS );
+ mCurrTrafficModelGroup = i;
+}
+inline float TrafficManager::GetDesiredTrafficSpeed()
+{
+ return mDesiredTrafficSpeedKph * KPH_2_MPS;
+}
+#endif //TRAFFICMANAGER_H
+
+
+
+
diff --git a/game/code/worldsim/traffic/trafficmodelgroup.h b/game/code/worldsim/traffic/trafficmodelgroup.h
new file mode 100644
index 0000000..01c1559
--- /dev/null
+++ b/game/code/worldsim/traffic/trafficmodelgroup.h
@@ -0,0 +1,145 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trafficvehicle.h
+//
+// Description: Blahblahblah
+//
+// History: 02/01/2003 + Created -- Dusit Eakkachaichanvet
+//
+//=============================================================================
+
+#ifndef TRAFFICMODELGROUP_H
+#define TRAFFICMODELGROUP_H
+
+#include <string.h>
+#include <raddebug.hpp>
+
+//******************************************************************************
+//
+// TRAFFIC MODEL
+//
+//******************************************************************************
+
+class TrafficModel
+{
+public:
+ enum
+ {
+ MAX_STRING_LEN = 64
+ };
+
+ // TRACKING
+ int mNumInstances;
+
+ // DATA
+ char mModelName[MAX_STRING_LEN+1];
+ int mMaxInstances;
+
+public:
+ TrafficModel();
+ void ClearData();
+ void Init( const char* name, int num );
+
+private:
+
+};
+
+inline void TrafficModel::ClearData()
+{
+ mModelName[0] = '\0';
+ mMaxInstances = 0;
+ mNumInstances = 0;
+ // TODO:
+ // We may not need to clear mNumInstances if we're going to initialize traffic vehicles
+ // on the fly. Imagine loading a new model group that shares this same model as
+ // the previous group. We don't want to lose the count.
+}
+
+inline TrafficModel::TrafficModel()
+{
+ ClearData();
+}
+
+inline void TrafficModel::Init( const char* name, int num )
+{
+ rTuneAssert( name != NULL );
+ rTuneAssert( num > 0 );
+
+ int nameLen = strlen( name );
+
+ rTuneAssert( nameLen <= TrafficModel::MAX_STRING_LEN );
+
+ strncpy( mModelName, name, TrafficModel::MAX_STRING_LEN );
+ mModelName[ nameLen ] = '\0';
+ mMaxInstances = num;
+}
+
+//******************************************************************************
+//
+// TRAFFIC MODEL GROUP
+//
+//******************************************************************************
+
+class TrafficModelGroup
+{
+public:
+ TrafficModelGroup();
+ void ClearGroup();
+ void AddTrafficModel( const char* name, int num );
+ TrafficModel* GetTrafficModel( int i );
+ int GetNumModels();
+
+private:
+ enum
+ {
+ MAX_TRAFFIC_MODELS = 5
+ };
+ TrafficModel mTrafficModels[MAX_TRAFFIC_MODELS];
+ int mNumModels;
+};
+
+inline TrafficModelGroup::TrafficModelGroup()
+{
+ mNumModels = 0;
+}
+inline void TrafficModelGroup::ClearGroup()
+{
+ for( int i=0; i<mNumModels; i++ )
+ {
+ mTrafficModels[i].ClearData();
+ }
+ mNumModels = 0;
+}
+inline void TrafficModelGroup::AddTrafficModel( const char* name, int num )
+{
+ rTuneAssert( name != NULL );
+ rTuneAssert( strlen(name) <= TrafficModel::MAX_STRING_LEN );
+ rTuneAssert( num > 0 );
+
+ bool found = false;
+ for( int i=0; i<mNumModels; i++ )
+ {
+ if( strcmp( mTrafficModels[i].mModelName, name )==0 )
+ {
+ found = true;
+ mTrafficModels[i].mNumInstances = num;
+ }
+ }
+
+ if( !found )
+ {
+ mTrafficModels[mNumModels].Init( name, num );
+ mNumModels++;
+ }
+}
+inline TrafficModel* TrafficModelGroup::GetTrafficModel( int i )
+{
+ rTuneAssert( 0 <= i && i < TrafficModelGroup::MAX_TRAFFIC_MODELS );
+ return &mTrafficModels[i];
+}
+inline int TrafficModelGroup::GetNumModels()
+{
+ return mNumModels;
+}
+#endif \ No newline at end of file
diff --git a/game/code/worldsim/traffic/trafficvehicle.h b/game/code/worldsim/traffic/trafficvehicle.h
new file mode 100644
index 0000000..e7259d4
--- /dev/null
+++ b/game/code/worldsim/traffic/trafficvehicle.h
@@ -0,0 +1,135 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: trafficvehicle.h
+//
+// Description: Blahblahblah
+//
+// History: 09/09/2002 + Added members that aid Traffic management -- Dusit Eakkachaichanvet
+// 04/07/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef TRAFFICVEHICLE_H
+#define TRAFFICVEHICLE_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+class Vehicle;
+class Lane;
+
+
+//=============================================================================
+//
+// TrafficVehicle
+//
+//=============================================================================
+
+class TrafficVehicle
+{
+public:
+ TrafficVehicle();
+ virtual ~TrafficVehicle();
+
+ Vehicle* GetVehicle() const;
+ void SetVehicle( Vehicle* vehicle );
+
+ Lane* GetLane() const;
+ void SetLane( Lane* lane );
+
+ bool GetIsActive() const;
+ void SetIsActive( bool active );
+
+ bool HasHusk() const;
+ void SetHasHusk( bool yes );
+
+ Vehicle* GetHusk();
+ void SetHusk( Vehicle* husk );
+
+ //int mActiveListIndex;
+ unsigned int mMillisecondsDeactivated;
+ bool mCanBeResurrected;
+
+ unsigned int mMillisecondsOutOfSight;
+ bool mOutOfSight;
+
+
+
+private:
+
+ Vehicle* mVehicle;
+ Vehicle* mHusk;
+
+ bool mIsActive; // Active = being used as traffic car
+ Lane* mLane; // Pointer to Lane to whose list of vehicles this
+ // traffic vehicle has been added.
+ bool mHasHusk;
+
+private:
+ //Prevent wasteful constructor creation.
+ TrafficVehicle( const TrafficVehicle& trafficvehicle );
+ TrafficVehicle& operator=( const TrafficVehicle& trafficvehicle );
+};
+inline TrafficVehicle::TrafficVehicle() :
+ //mActiveListIndex( -1 ),
+ mMillisecondsDeactivated( 0 ),
+ mCanBeResurrected( true ),
+ mMillisecondsOutOfSight( 0 ),
+ mOutOfSight( true ),
+ mVehicle( NULL ),
+ mHusk( NULL ),
+ mIsActive( false ),
+ mLane( NULL ),
+ mHasHusk( false )
+{
+}
+inline TrafficVehicle::~TrafficVehicle()
+{
+}
+inline Vehicle* TrafficVehicle::GetVehicle() const
+{
+ return mVehicle;
+}
+inline void TrafficVehicle::SetVehicle( Vehicle* vehicle )
+{
+ mVehicle = vehicle;
+}
+inline Lane* TrafficVehicle::GetLane() const
+{
+ return mLane;
+}
+inline void TrafficVehicle::SetLane( Lane* lane )
+{
+ mLane = lane;
+}
+inline bool TrafficVehicle::GetIsActive() const
+{
+ return mIsActive;
+}
+inline void TrafficVehicle::SetIsActive( bool active )
+{
+ mIsActive = active;
+}
+inline bool TrafficVehicle::HasHusk() const
+{
+ return mHasHusk;
+}
+inline void TrafficVehicle::SetHasHusk( bool yes )
+{
+ mHasHusk = yes;
+}
+inline Vehicle* TrafficVehicle::GetHusk()
+{
+ return mHusk;
+}
+inline void TrafficVehicle::SetHusk( Vehicle* husk )
+{
+ mHusk = husk;
+}
+
+#endif //TRAFFICVEHICLE_H
diff --git a/game/code/worldsim/vehiclecentral.cpp b/game/code/worldsim/vehiclecentral.cpp
new file mode 100644
index 0000000..f9dc05f
--- /dev/null
+++ b/game/code/worldsim/vehiclecentral.cpp
@@ -0,0 +1,2278 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehiclecentral.cpp
+//
+// Description: bleek
+//
+// History: May 1, 2002 - created, gmayer
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <raddebug.hpp>
+
+#include <stdlib.h> // for atof
+
+//========================================
+// Project Includes
+//========================================
+
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/vehiclecentral.h>
+
+#include <worldsim/redbrick/vehiclecontroller/vehiclecontroller.h>
+
+#include <worldsim/character/character.h>
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/avatarmanager.h>
+
+#include <ai/actionbuttonhandler.h>
+#include <ai/actionbuttonmanager.h>
+
+#include <memory/srrmemory.h>
+#include <mission/gameplaymanager.h>
+
+#include <meta/eventlocator.h>
+#include <meta/spheretriggervolume.h>
+#include <meta/triggervolumetracker.h>
+
+#include <debug/debuginfo.h>
+
+#include <mission/gameplaymanager.h>
+#include <mission/missionscriptloader.h>
+
+#include <render/RenderManager/RenderManager.h>
+#include <render/Culling/WorldScene.h>
+
+#include <gameflow/gameflow.h>
+#include <console/console.h>
+#include <cheats/cheatinputsystem.h>
+
+#include <p3d/billboardobject.hpp>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+VehicleCentral* VehicleCentral::spInstance = 0;
+AiVehicleController* VehicleCentral::spGenericAI = NULL;
+
+//
+// Dusit here:
+// First, keep MAX_HUSKS defined in the .cpp so we can tweak without
+// recompiling the whole game...
+// Now, how many husks can we possibly need in the WORST case??
+// traffic 5
+// parked cars 5
+// harass 5
+// player 1
+// mission ai 4
+// main ai 1
+// free (moment) cars 1-2 (?)
+// This is too many (23). At 17KB per Vehicle instance + 35KB Husk model,
+// it would total between 420 and 500 KB... Unacceptable.
+// So we have forced the other systems to deal safely with not having
+// husks available when they need it (RequestHusk may return NULL)
+//
+const int MAX_HUSKS = 5;
+
+
+//=============================================================================
+// VehicleCentral::VehicleCentral
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: VehicleCentral
+//
+//=============================================================================
+VehicleCentral::VehicleCentral() :
+ mCurrentVehicleUnderContructionHead( 0 ),
+ mCurrentVehicleUnderConstructionTail( 0 ),
+ mbVehicleTriggersActive(true),
+ mSuppressedDriverCount(0)
+{
+ spGenericAI = new AiVehicleController( NULL );
+ spGenericAI->AddRef();
+
+ int i;
+ for(i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ mActiveVehicleList[i] = 0;
+
+ //mActiveVehicleControllerList[ i ] = NULL;
+ mActiveVehicleControllerList[ i ] = spGenericAI;
+ mActiveVehicleControllerList[ i ]->AddRef();
+
+ mVehicleUnderConstruction[ i ] = NULL;
+ //mDoorTriggerList[ i ] = 0;
+ }
+
+ mNumActiveVehicles = 0;
+
+ // perhaps this would be a good place to setup the console functions for the vehicle
+ SetupConsoleFunctionsForVehicleTuning();
+
+ for( i=0; i<NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ mHeadLights[i] = NULL;
+ }
+}
+
+//==============
+// console hooks
+//==============
+
+static void ConsoleHookSetGasScale(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetGasScale(value);
+}
+
+static void ConsoleHookSetSlipGasScale(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetSlipGasScale(value);
+}
+
+static void ConsoleHookSetHighSpeedGasScale(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetHighSpeedGasScale(value);
+}
+
+
+static void ConsoleHookSetGasScaleSpeedThreshold(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetGasScaleSpeedThreshold(value);
+}
+
+
+static void ConsoleHookSetBrakeScale(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetBrakeScale(value);
+}
+
+static void ConsoleHookSetTopSpeedKmh(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetTopSpeedKmh(value);
+}
+
+static void ConsoleHookSetMass(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetMass(value);
+}
+
+static void ConsoleHookSetMaxWheelTurnAngle(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetMaxWheelTurnAngle(value);
+}
+
+
+static void ConsoleHookSetHighSpeedSteeringDrop(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetHighSpeedSteeringDrop(value);
+}
+
+
+static void ConsoleHookSetTireLateralStaticGrip(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetTireLateralStaticGrip(value);
+}
+
+
+static void ConsoleHookSetTireLateralResistanceNormal(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetTireLateralResistanceNormal(value);
+}
+
+
+static void ConsoleHookSetTireLateralResistanceSlip(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetTireLateralResistanceSlip(value);
+}
+
+
+static void ConsoleHookSetEBrakeEffect(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetEBrakeEffect(value);
+}
+
+
+static void ConsoleHookSetCMOffsetX(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetCMOffsetX(value);
+}
+
+
+static void ConsoleHookSetCMOffsetY(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetCMOffsetY(value);
+}
+
+
+static void ConsoleHookSetCMOffsetZ(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetCMOffsetZ(value);
+}
+
+
+static void ConsoleHookSetSuspensionLimit(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetSuspensionLimit(value);
+}
+
+
+static void ConsoleHookSetSpringK(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetSuspensionSpringK(value);
+}
+
+
+static void ConsoleHookSetDamperC(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetSuspensionDamperC(value);
+}
+
+
+static void ConsoleHookSetHitPoints(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetHitPoints(value);
+}
+
+
+static void ConsoleHookSetSuspensionYOffset(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetSuspensionYOffset(value);
+}
+
+static void ConsoleHookSetBurnoutRange(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetBurnoutRange(value);
+}
+
+
+static void ConsoleHookSetMaxSpeedBurstTime(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetMaxSpeedBurstTime(value);
+}
+
+
+static void ConsoleHookSetDonutTorque(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetDonutTorque(value);
+}
+
+
+static void ConsoleHookSetSlipSteeringNoEBrake(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetTireLateralResistanceSlipWithoutEBrake(value);
+}
+
+static void ConsoleHookSetSlipEffectNoEBrake(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetSlipEffectWithoutEBrake(value);
+}
+
+
+static void ConsoleHookSetWeebleOffset(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetWeebleOffset(value);
+}
+
+
+static void ConsoleHookSetWheelieRange(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetWheelieRange(value);
+}
+
+
+static void ConsoleHookSetWheelieOffsetY(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetWheelieYOffset(value);
+}
+
+static void ConsoleHookSetWheelieOffsetZ(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetWheelieZOffset(value);
+}
+
+static void ConsoleHookSetShininess( int argc, char** argv )
+{
+ if( argc != 2 )
+ {
+ return;
+ }
+ float shininess = static_cast<float>( atof( argv[ 1 ] ) );
+ unsigned char ref = rmt::Clamp( int( 0xff * shininess ), 0, 0xff );
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetShininess( ref );
+}
+
+static void ConsoleHookSetShadowAdjustments( int argc, char** argv )
+{
+ if( argc != 9 )
+ {
+ return;
+ }
+ float Adjustments[ 4 ][ 2 ];
+ for( int i = 0; i < 4; ++i )
+ {
+ Adjustments[ i ][ 0 ] = static_cast<float>( atof( argv[ 1 + ( i * 2 ) ] ) );
+ Adjustments[ i ][ 1 ] = static_cast<float>( atof( argv[ 2 + ( i * 2 ) ] ) );
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetShadowAdjustments( Adjustments );
+}
+
+static void ConsoleHookSetDriverName(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetDriverName(argv[1]);
+}
+
+static void ConsoleHookSetHasDoors(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->mHasDoors = (atoi(argv[1]) != 0);
+}
+
+static void ConsoleHookSetCharactersVisible(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->mVisibleCharacters = (atoi(argv[1]) != 0);
+}
+
+static void ConsoleHookSetIrisTransition(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->mIrisTransition = (atoi(argv[1]) != 0);
+}
+
+static void ConsoleHookSetAllowSlide(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->mAllowSlide = (atoi(argv[1]) != 0);
+}
+
+static void ConsoleHookSetHighRoof(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->mHighRoof = (atoi(argv[1]) != 0);
+}
+
+static void ConsoleHookSetCharacterScale(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->mCharacterScale = static_cast< float >( atof(argv[1]) );
+}
+
+//Chuck: added this for Gambling Races.
+static void ConsoleHookSetGamblingOdds(int argc, char** argv)
+{
+ if(argc != 2)
+ {
+ return;
+ }
+ float value = static_cast<float>(atof(argv[1]));
+ GetVehicleCentral()->GetCurrentVehicleUnderConstruction()->SetGamblingOdds(value);
+}
+
+static void ConsoleHookSuppressDriver(int argc, char** argv)
+{
+ GetVehicleCentral()->AddSuppressedDriver(argv[1]);
+}
+
+
+//=============================================================================
+// VehicleCentral::SetupConsoleFunctionsForVehicleTuning
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::SetupConsoleFunctionsForVehicleTuning()
+{
+
+ GetConsole()->AddFunction("SetMass", ConsoleHookSetMass, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetGasScale", ConsoleHookSetGasScale, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetHighSpeedGasScale", ConsoleHookSetHighSpeedGasScale, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetGasScaleSpeedThreshold", ConsoleHookSetGasScaleSpeedThreshold, "help your goddamn self", 1, 1);
+
+
+ GetConsole()->AddFunction("SetSlipGasScale", ConsoleHookSetSlipGasScale, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetBrakeScale", ConsoleHookSetBrakeScale, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetTopSpeedKmh", ConsoleHookSetTopSpeedKmh, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetMaxWheelTurnAngle", ConsoleHookSetMaxWheelTurnAngle, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetHighSpeedSteeringDrop", ConsoleHookSetHighSpeedSteeringDrop, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetTireGrip", ConsoleHookSetTireLateralStaticGrip, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetNormalSteering", ConsoleHookSetTireLateralResistanceNormal, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetSlipSteering", ConsoleHookSetTireLateralResistanceSlip, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetEBrakeEffect", ConsoleHookSetEBrakeEffect, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetCMOffsetX", ConsoleHookSetCMOffsetX, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetCMOffsetY", ConsoleHookSetCMOffsetY, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetCMOffsetZ", ConsoleHookSetCMOffsetZ, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetSuspensionLimit", ConsoleHookSetSuspensionLimit, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetSpringK", ConsoleHookSetSpringK, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetDamperC", ConsoleHookSetDamperC, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetSuspensionYOffset", ConsoleHookSetSuspensionYOffset, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetHitPoints", ConsoleHookSetHitPoints, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetBurnoutRange", ConsoleHookSetBurnoutRange, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetMaxSpeedBurstTime", ConsoleHookSetMaxSpeedBurstTime, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetDonutTorque", ConsoleHookSetDonutTorque, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetSlipSteeringNoEBrake", ConsoleHookSetSlipSteeringNoEBrake, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetSlipEffectNoEBrake", ConsoleHookSetSlipEffectNoEBrake, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetWeebleOffset", ConsoleHookSetWeebleOffset, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SetWheelieRange", ConsoleHookSetWheelieRange, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetWheelieOffsetY", ConsoleHookSetWheelieOffsetY, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetWheelieOffsetZ", ConsoleHookSetWheelieOffsetZ, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction( "SetShadowAdjustments", ConsoleHookSetShadowAdjustments, "Move the shadow points around", 8, 8 );
+ GetConsole()->AddFunction( "SetShininess", ConsoleHookSetShininess, "Set the environmental reflection 0 to 1", 1, 1 );
+
+ GetConsole()->AddFunction("SetDriver", ConsoleHookSetDriverName, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetGamblingOdds",ConsoleHookSetGamblingOdds,"Set Gambling Odds",1,1);
+
+ GetConsole()->AddFunction("SetHasDoors", ConsoleHookSetHasDoors, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetCharactersVisible", ConsoleHookSetCharactersVisible, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetIrisTransition", ConsoleHookSetIrisTransition, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetAllowSeatSlide", ConsoleHookSetAllowSlide, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetHighRoof", ConsoleHookSetHighRoof, "help your goddamn self", 1, 1);
+ GetConsole()->AddFunction("SetCharacterScale", ConsoleHookSetCharacterScale, "help your goddamn self", 1, 1);
+
+ GetConsole()->AddFunction("SuppressDriver", ConsoleHookSuppressDriver, "help your goddamn self", 1, 1);
+}
+
+//=============================================================================
+// VehicleCentral::~VehicleCentral
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: VehicleCentral
+//
+//=============================================================================
+VehicleCentral::~VehicleCentral()
+{
+ ClearSuppressedDrivers();
+
+
+ int i;
+ for(i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ if(mActiveVehicleList[i])
+ {
+ // TODO - will the mission manager delete these instead??
+ //
+ // for now, only delete the ones that were left in there...
+ delete mActiveVehicleList[i];
+ }
+
+ if ( this->mActiveVehicleControllerList[ i ] )
+ {
+ mActiveVehicleControllerList[ i ]->Release();
+ mActiveVehicleControllerList[ i ] = NULL;
+ }
+/*
+ if ( mDoorTriggerList[ i ] )
+ {
+ mDoorTriggerList[ i ]->Release( );
+ mDoorTriggerList[ i ] = 0;
+ }
+*/
+ }
+
+ // create a static vehicle AI and put it in limbo...
+ if( spGenericAI )
+ {
+ spGenericAI->ReleaseVerified();
+ }
+
+ for( i=0; i<NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ if( mHeadLights[i] )
+ {
+ mHeadLights[i]->Release();
+ mHeadLights[i] = NULL;
+ }
+ }
+
+}
+
+
+//=============================================================================
+// VehicleCentral::GetInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: VehicleCentral
+//
+//=============================================================================
+VehicleCentral* VehicleCentral::GetInstance()
+{
+ rAssert(spInstance);
+ return spInstance;
+}
+
+
+//=============================================================================
+// VehicleCentral::CreateInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: VehicleCentral
+//
+//=============================================================================
+VehicleCentral* VehicleCentral::CreateInstance()
+{
+ rAssert(spInstance == 0);
+
+ spInstance = new(GMA_PERSISTENT) VehicleCentral;
+ rAssert(spInstance);
+
+ return spInstance;
+
+}
+
+
+//=============================================================================
+// VehicleCentral::DestroyInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::DestroyInstance()
+{
+ rAssert(spInstance);
+
+ delete(GMA_PERSISTENT, spInstance);
+ spInstance = NULL;
+
+
+}
+
+
+//=============================================================================
+// VehicleCentral::InitHuskPool
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::InitHuskPool()
+{
+}
+
+
+//=============================================================================
+// VehicleCentral::FreeHuskPool
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::FreeHuskPool()
+{
+
+
+}
+
+
+//=============================================================================
+// VehicleCentral::PreLoad
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::PreLoad()
+{
+ // bunch of actual loading moved to frontend startup to reduce load times - nbrooke 3/7/2003
+
+ this->mHuskPool.Init(MAX_HUSKS);
+
+ rAssert( mHeadLights[0] == NULL && mHeadLights[1] == NULL && mHeadLights[2] == NULL );
+
+ // also might as well load the vehicle commons here..
+ p3d::inventory->PushSection();
+ p3d::inventory->SelectSection( "Global" );
+ bool oldCurrSectionOnly = p3d::inventory->GetCurrentSectionOnly();
+ p3d::inventory->SetCurrentSectionOnly( true );
+
+ tRefCounted::Assign( mHeadLights[0], p3d::find<tBillboardQuadGroup>(tEntity::MakeUID("headlightShape8")) );
+ tRefCounted::Assign( mHeadLights[1], p3d::find<tBillboardQuadGroup>(tEntity::MakeUID("headlight2Shape")) );
+ tRefCounted::Assign( mHeadLights[2], p3d::find<tBillboardQuadGroup>(tEntity::MakeUID("glowGroupShape2")) );
+
+ p3d::inventory->SetCurrentSectionOnly( oldCurrSectionOnly );
+ p3d::inventory->PopSection();
+
+ // grab the colours from the bbqgs
+ int count = 0;
+ for( int i=0; i<NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ rAssert( mHeadLights[i] );
+ for( int j=0; j<mHeadLights[i]->GetNumQuads(); j++ )
+ {
+ tBillboardQuad* quad = mHeadLights[i]->GetQuad( j );
+ rAssert( quad );
+ mOriginalHeadLightColours[count] = quad->GetColour();
+ count++;
+ }
+ }
+ rAssert( mHeadLights[0] && mHeadLights[1] && mHeadLights[2] );
+
+}
+
+
+
+//=============================================================================
+// VehicleCentral::Unload
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::Unload()
+{
+ // empty out husk manager here also
+ this->mHuskPool.Empty();
+
+ ClearSuppressedDrivers();
+
+ int count = 0;
+
+ // restore prev states and release all preloaded headlights bbqgs
+ for( int i=0; i<NUM_HEADLIGHT_BBQGS; i++ )
+ {
+ rAssert( mHeadLights[i] );
+ for( int j=0; j<mHeadLights[i]->GetNumQuads(); j++ )
+ {
+ tBillboardQuad* quad = mHeadLights[i]->GetQuad( j );
+ rAssert( quad );
+ quad->SetColour( mOriginalHeadLightColours[count] );
+ count++;
+ }
+ // ProcessShaders screwed us by causing us to lose the additive blend
+ // We set it back.
+ mHeadLights[i]->GetShader()->SetInt( PDDI_SP_BLENDMODE, PDDI_BLEND_ADD );
+
+ mHeadLights[i]->Release();
+ mHeadLights[i] = NULL;
+ }
+
+ rAssert(mNumActiveVehicles == 0);
+
+}
+
+//=============================================================================
+// VehicleCentral::InitVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: void
+//
+//=============================================================================
+Vehicle* VehicleCentral::InitVehicle( const char* name, bool addToActiveVehicleList, char* confile, VehicleType vt, DriverInit driver, bool playercar, bool startoutofcar)
+{
+ HeapMgr()->PushHeap( GetGameplayManager()->GetCurrentMissionHeap() );
+
+ MEMTRACK_PUSH_GROUP( "VehicleCentral" );
+//#ifdef RAD_GAMECUBE
+// Vehicle* vehicle = new( GMA_GC_VMM ) Vehicle;
+//#else
+ Vehicle* vehicle = new(GetGameplayManager()->GetCurrentMissionHeap()) Vehicle;
+//#endif
+
+ //if this car is owned by the player then set the flag.
+ if(playercar ==true)
+ {
+ vehicle->mbPlayerCar=true;
+ }
+
+ // note: moved the car open-door locator to the vehicle init
+ bool ok = vehicle->Init( name, GetWorldPhysicsManager()->mSimEnvironment, VL_PHYSICS, vt, startoutofcar);
+ rAssert(ok);
+
+ // so the script hooks can get at it
+ rAssert( mVehicleUnderConstruction[mCurrentVehicleUnderConstructionTail] == NULL );
+ mVehicleUnderConstruction[mCurrentVehicleUnderConstructionTail] = vehicle;
+ mCurrentVehicleUnderConstructionTail = (mCurrentVehicleUnderConstructionTail + 1) % MAX_ACTIVE_VEHICLES;
+
+ //If this happens, we're in big shit. We'll need to up the number of active vehicles or look closer at the loading.
+ rAssert( mCurrentVehicleUnderContructionHead != mCurrentVehicleUnderConstructionTail );
+
+ // see TODO in comment blocks above
+ // DL: now loads the car based on name
+
+ char scriptname[64];
+ strcpy( scriptname, "scripts/cars/" );
+
+ if(confile != NULL && confile[0] != '\0')
+ {
+ strcat( scriptname, confile );
+
+ }
+ else
+ {
+ strcat( scriptname, name );
+ strcat( scriptname, ".con" );
+ }
+
+ vehicle->mDriverInit = driver;
+ GetMissionScriptLoader()->LoadScriptAsync( scriptname, this );
+
+ //vehicle->CalculateValuesBasedOnDesignerParams();
+
+ if(addToActiveVehicleList)
+ {
+ // return slot ignored here...
+ int dummy = AddVehicleToActiveList(vehicle);
+ }
+
+ MEMTRACK_POP_GROUP( "VehicleCentral" );
+
+ HeapMgr()->PopHeap( GetGameplayManager()->GetCurrentMissionHeap() );
+
+ return vehicle;
+}
+
+
+//=============================================================================
+// VehicleCentral::AddVehicleToActiveList
+//=============================================================================
+// Description: Comment
+//
+// returns index if successful, otherwise -1
+//
+// Parameters: VehicleCentral::AddVehicleToActiveList
+//
+// Return: int
+//
+//=============================================================================
+int VehicleCentral::AddVehicleToActiveList(Vehicle* vehicle)
+{
+
+
+ //==============================================================================
+ //
+ // let's be clear here:
+ //
+ // an 'active' vehicle is one that will be added to the dsg
+ // it will also request a collision area index and put itself in there,
+ // but it won't do it's own query to fill that area unless it's under VL_PHYSICS
+ //
+ //==============================================================================
+
+ // find this vehicle a slot
+ int slot = -1;
+
+ int i;
+ for(i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ // debug test
+ if(mActiveVehicleList[i])
+ {
+ if(mActiveVehicleList[i] == vehicle)
+ {
+ // this is already in our list!!!!
+ //rAssertMsg( false, "This vehicle is already in the active list\n");
+ return i;
+
+ //chuck: draw the driver
+ if (vehicle->GetDriver() !=NULL)
+ {
+ vehicle->GetDriver()->AddToWorldScene();
+ }
+
+ }
+ }
+
+
+ }
+
+ for(i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ if(mActiveVehicleList[i] == 0)
+ {
+ // got it.
+ slot = i;
+ break;
+ }
+ }
+
+
+ if(slot == -1)
+ {
+ // couldn't find room for it.
+ //?
+ rAssertMsg(0,"Trying to add too many active vehicles!");
+ return -1;
+ }
+
+ //----------
+ // add it in
+ //----------
+
+ mActiveVehicleList[slot] = vehicle;
+
+ if (vehicle->GetDriver() !=NULL)
+ {
+ vehicle->GetDriver()->AddToWorldScene();
+ }
+
+
+
+ // this is perhaps totally redundant right now
+ mNumActiveVehicles++;
+
+ if(mNumActiveVehicles > MAX_ACTIVE_VEHICLES)
+ {
+ rAssertMsg(0,"Too many active vehicles!");
+ return -1;
+ }
+
+
+ //--------------------
+ // trigger volume shit
+ //--------------------
+
+ if( vehicle->mVehicleType == VT_AI &&
+ ::GetGameFlow()->GetCurrentContext() != CONTEXT_SUPERSPRINT &&
+ !GetGameplayManager()->mIsDemo )
+ {
+ GetTriggerVolumeTracker()->RegisterAI( vehicle );
+ }
+
+
+
+ int id = vehicle->mpEventLocator->GetData();
+ ActionButton::GetInCar* pABHandler = static_cast<ActionButton::GetInCar*>( GetActionButtonManager()->GetActionByIndex(id) );
+ rAssert( dynamic_cast<ActionButton::GetInCar*>( pABHandler ) != NULL );
+ rAssert( pABHandler );
+ pABHandler->SetVehicleId(slot);
+
+
+ // leave AI untouched! Their triggers remain OFF!
+ vehicle->ActivateTriggers(true);
+
+ //-----------
+ // add to dsg
+ //-----------
+ GetRenderManager()->pWorldScene()->Add((DynaPhysDSG*)vehicle);
+
+ vehicle->GetCollisionAreaIndexAndAddSelf();
+
+ int curWorldRenderLayer = GetRenderManager()->rCurWorldRenderLayer();
+ vehicle->SetRenderLayerEnum((RenderEnums::LayerEnum)curWorldRenderLayer);
+
+
+ // temp - for car on car collisions
+ // TODO - remove
+
+ // until the vehicles are moving around in the scenegraph, we need to make sure the cars have a collision pair for each other
+
+ /*
+ for(i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ if(mActiveVehicleList[i] != 0 && i != slot)
+ {
+ vehicle->AddToOtherCollisionArea(mActiveVehicleList[i]->mCollisionAreaIndex);
+ }
+ }
+ */
+
+ //
+ // Notify the world about new user vehicles
+ //
+ if( vehicle->mVehicleType == VT_USER )
+ {
+ GetEventManager()->TriggerEvent( EVENT_USER_VEHICLE_ADDED_TO_WORLD, vehicle );
+ }
+
+ // april 22, 2003
+ // new
+ // vehicle will hold it's slot.
+
+ vehicle->mVehicleCentralIndex = slot;
+
+ vehicle->AddRef();
+
+ return slot;
+
+}
+
+
+
+//=============================================================================
+// VehicleCentral::RemoveVehicleFromActiveList
+//=============================================================================
+// Description: Comment
+//
+// returns whether or not it was even there in the first place
+//
+//
+// Parameters: (Vehicle* vehicle)
+//
+// Return: bool
+//
+//=============================================================================
+bool VehicleCentral::RemoveVehicleFromActiveList(Vehicle* vehicle)
+{
+ int i;
+ for(i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ if(mActiveVehicleList[i] == vehicle)
+ {
+
+ //Chuck: If car getting dumped was the last car used by the player
+ //set the mCurrentVehicle to NULL
+
+
+ // greg
+ // jan 4, 2003
+ //
+ // TODO
+ // not sure if we want this code
+ // mCurrentVehicle should be updated elsewhere
+
+ // PHONEBOOTH CAR SHOULD REPLACE DEFAULT VEHICLE.
+ // this change is going in rsn
+
+ //if (GetGameplayManager()->GetCurrentVehicle() == vehicle)
+ //{
+ // Vehicle* p_vehicle = GetGameplayManager()->GetVehicle(GetGameplayManager()->mDefaultVehicle);
+ // GetGameplayManager()->SetCurrentVehicle(p_vehicle);
+ //}
+
+ // later in the same day
+ //
+ // GameplayManager::DumpCurrentCar ()
+ // now sets mCurrentVehicle to NULL as the name would imply
+
+
+ mActiveVehicleList[i] = 0;
+ mNumActiveVehicles--;
+
+ // make sure controller slot doesn't have junk left lying around in it.
+ if( mActiveVehicleControllerList[i] )
+ {
+ mActiveVehicleControllerList[i]->Release();
+ }
+ //mActiveVehicleControllerList[i] = NULL;
+ mActiveVehicleControllerList[i] = spGenericAI;
+ mActiveVehicleControllerList[i]->AddRef();
+
+ // !! need to remove this from any other vehicles mCurrentDynamics list
+ //
+ // what an ugly pain in the ass:
+ GetWorldPhysicsManager()->RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(vehicle);
+
+ vehicle->RemoveSelfAndFreeCollisionAreaIndex();
+
+
+ //----------------
+ // remove from DSG
+ //----------------
+
+ int& curRenderLayer = GetRenderManager()->rCurWorldRenderLayer();
+ int orgRenderLayer = curRenderLayer;
+
+ // swtich to the vehicle's render layer
+ curRenderLayer = vehicle->GetRenderLayerEnum();
+
+ //Chuck: if we're removing the car lets check if there is a AI NPC driver in that car
+ if(vehicle->GetDriver() != NULL)
+ {
+ vehicle->GetDriver()->RemoveFromWorldScene();
+ }
+ GetRenderManager()->pWorldScene()->Remove(/*(DynaPhysDSG*)*/ vehicle);
+
+
+ // restore the orignal render layer
+ curRenderLayer = orgRenderLayer;
+
+ // hack
+ //vehicle->Release();
+
+ // leave AI untouched! Their triggers remain OFF!
+ vehicle->ActivateTriggers(false);
+
+ //
+ // Notify the world about deleted user vehicles
+ //
+ if( vehicle->mVehicleType == VT_USER )
+ {
+ GetEventManager()->TriggerEvent( EVENT_USER_VEHICLE_REMOVED_FROM_WORLD, vehicle );
+ }
+
+ // stop tracking AI...
+ if( GetGameFlow()->GetCurrentContext() != CONTEXT_SUPERSPRINT )
+ {
+ GetTriggerVolumeTracker()->UnregisterAI( vehicle );
+ }
+
+ vehicle->mVehicleCentralIndex = -1;
+
+ vehicle->Release();
+
+ return true;
+ }
+ /*
+ else if(mActiveVehicleList[i])
+ {
+ // this isn't super-efficient but it's only temporary and should stop Darryl's whining
+
+ // try and remove from all other collision areas just in case it's in there...
+ GetWorldPhysicsManager()->mCollisionManager->
+ RemoveCollisionObject(vehicle->mSimStateArticulated->GetCollisionObject(), mActiveVehicleList[i]->mCollisionAreaIndex);
+
+
+ }
+ */
+
+ }
+
+
+ return false;
+}
+
+
+//=============================================================================
+// VehicleCentral::KillEmAll
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::KillEmAll()
+{
+ // not sure if I should keep this method around, because whoever creates the
+ // vehicle should destroy it
+ //
+ // but the method name is so fucking cool....
+
+
+}
+
+/*
+//=============================================================================
+// VehicleCentral::Suspend
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::Suspend()
+{
+ mSuspended = true;
+}
+
+
+//=============================================================================
+// VehicleCentral::Resume
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::Resume()
+{
+ mSuspended = false;
+}
+*/
+
+//=============================================================================
+// VehicleCentral::GetVehicle
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int id )
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* VehicleCentral::GetVehicle( int id ) const
+{
+ //rAssert( id < this->mNumActiveVehicles );
+ return this->mActiveVehicleList[ id ];
+}
+
+
+//=============================================================================
+// VehicleCentral::SetVehicleController
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int id, VehicleController* pVehicleController )
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::SetVehicleController( int id, VehicleController* pVehicleController )
+{
+
+ if ( id >= 0 && id < MAX_ACTIVE_VEHICLES )
+ {
+ //rAssert( id < this->mNumActiveVehicles ); // don't think this is necessary anymore
+ if ( pVehicleController )
+ {
+ pVehicleController->Init();
+ }
+ else
+ {
+ if ( mActiveVehicleControllerList[ id ] )
+ {
+ mActiveVehicleControllerList[ id ]->Shutdown();
+ }
+ pVehicleController = spGenericAI;
+ }
+
+ tRefCounted::Assign( mActiveVehicleControllerList[ id ], pVehicleController );
+ }
+ else
+ {
+ rAssertMsg( 0, "SetVehicleController - id out of range" );
+ }
+}
+
+//Triage hack, only for demo mode, or until Greg actually
+//addrefs and releases --dm 12/01/02
+VehicleController* VehicleCentral::RemoveVehicleController( int mAIIndex )
+{
+ if ( mActiveVehicleControllerList[ mAIIndex ] )
+ {
+ VehicleController* FoundVehicleController = mActiveVehicleControllerList[ mAIIndex ];
+ mActiveVehicleControllerList[ mAIIndex ] = 0;
+ return FoundVehicleController;
+ }
+ return NULL;
+}
+//=============================================================================
+// VehicleCentral::GetVehicleController
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int id )
+//
+// Return: VehicleController
+//
+//=============================================================================
+VehicleController* VehicleCentral::GetVehicleController( int id ) const
+{
+ if( 0 <= id && id < MAX_ACTIVE_VEHICLES )
+ {
+ if( mActiveVehicleControllerList[ id ] == spGenericAI )
+ {
+ return NULL;
+ }
+ return mActiveVehicleControllerList[ id ];
+ }
+ return NULL;
+}
+
+
+//=============================================================================
+// VehicleCentral::GetVehicleId
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( Vehicle* pVehicle )
+//
+// Return: int
+//
+//=============================================================================
+int VehicleCentral::GetVehicleId( Vehicle* pVehicle, bool checkStrict ) const
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if(pVehicle == mActiveVehicleList[i])
+ {
+ return i;
+ }
+ }
+
+ if(checkStrict)
+ {
+ rAssertMsg( false, "Vehicle not found in Vehicle Database!\n" );
+ }
+
+ return (int)-1;
+}
+
+//=============================================================================
+// VehicleCentral::PreSubstepUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( )
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::PreSubstepUpdate( float dt )
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ Vehicle* vehicle = mActiveVehicleList[i];
+ if( vehicle ) // TODO - wrap with render layer test
+ {
+ vehicle->PreSubstepUpdate(dt);
+
+ // Update controller
+ VehicleController* vController = mActiveVehicleControllerList[i];
+ if( vController != 0 )
+ {
+ vController->Update( dt );
+
+ float test = vController->GetGas();
+ //?
+ // safe to just apply these as-is to vehicle?
+ vehicle->SetGas(test);
+
+
+ test = vController->GetBrake( );
+ vehicle->SetBrake( test );
+
+
+ test = vController->GetThrottle();
+ if(test > 0.1f)
+ {
+ vehicle->SetGas( test );
+ }
+ if(test < -0.1f)
+ {
+ vehicle->SetBrake( -test );
+ }
+
+
+ //This sucks.
+ bool isWheel = false;
+ test = vController->GetSteering( isWheel );
+
+
+ // test stick and dpad and set highest fabs
+ float left = 0.0f;
+ if( vController->GetSteerLeft() )
+ {
+ left = vController->GetSteerLeft( );
+ }
+
+ float right = 0.0f;
+ if( vController->GetSteerRight() )
+ {
+ right = vController->GetSteerRight( );
+ }
+
+ if(rmt::Fabs(test) > left && rmt::Fabs(test) > right)
+ {
+ // use stick value
+ vehicle->SetWheelTurnAngle(test, isWheel, dt);
+
+ }
+ else if(right > left)
+ {
+ vehicle->SetWheelTurnAngle(right, false, dt);
+ }
+ else
+ {
+ vehicle->SetWheelTurnAngle(-left, false, dt);
+ }
+
+
+
+ /*
+ // only human controlled vehicles will return a pointer here
+ if(vController->GetSteerLeft())
+ {
+ float left = vController->GetSteerLeft( );
+ if(test <= 0.0f && left > rmt::Fabs(test))
+ {
+ vehicle->SetWheelTurnAngle(-left, false, dt);
+ }
+ }
+ if(vController->GetSteerRight())
+ {
+ float right = vController->GetSteerRight( );
+ if(test >= 0.0f && right > test)
+ {
+ vehicle->SetWheelTurnAngle(right, false, dt);
+ }
+ }
+ */
+
+ test = vController->GetReverse( );
+ vehicle->SetReverse(test);
+
+ test = vController->GetHandBrake();
+ vehicle->SetEBrake(test, dt);
+
+ if( GetCheatInputSystem()->IsCheatEnabled(CHEAT_ID_CAR_JUMP_ON_HORN) &&
+ vehicle == GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle() ) // vehicle->mVehicleType == VT_USER )
+ {
+ test = vController->GetHorn( );
+ if(test > 0.1f)
+ {
+ vehicle->JumpOnHorn(test);
+ }
+ }
+
+ if( vController->GetHorn() > 0.0f )
+ {
+ // Only the player vehicle's controller uses Set/GetHorn.
+ // AI cars never at any time set the Horn value.
+ if( vehicle == GetAvatarManager()->GetAvatarForPlayer(0)->GetVehicle() ||
+ ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ // if in a certain game type, use the horn button for speedboost
+ if( ::GetGameplayManager()->GetGameType() == GameplayManager::GT_SUPERSPRINT )
+ {
+ // may or may not do turbo depending on if turbo is available..
+ vehicle->TurboOnHorn();
+ }
+ else
+ {
+ // outside of Supersprint context, we want to fire off an event
+ // so that traffic can honk back and peds can panick.
+ GetEventManager()->TriggerEvent( EVENT_PLAYER_VEHICLE_HORN, vehicle );
+ }
+ }
+ }
+
+
+ }
+ else
+ {
+ vehicle->SetGas(0.0f);
+ vehicle->SetBrake(0.0f);
+ vehicle->SetWheelTurnAngle(0.0f, false, dt);
+ vehicle->SetReverse(0.0f);
+ vehicle->SetEBrake(0.0f, dt);
+ }
+
+ //
+ // [Dusit Matthew Eakkachaichanvet: Dec 10, 2002]
+ // ok, the controller has been updated and knows of
+ // last frame's collision with a vehicle, so
+ // we can clear this value now.
+ //
+ vehicle->mCollidedWithVehicle = false;
+ vehicle->mNormalizedMagnitudeOfVehicleHit = 0.0f;
+ vehicle->mWasHitByVehicle = false;
+
+ }
+ }
+}
+
+//=============================================================================
+// VehicleCentral::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::Update(float dt)
+{
+
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ //rAssert( mActiveVehicleList[ i ] );
+ if( mActiveVehicleList[i] &&
+ (GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum()))
+ {
+ BEGIN_PROFILE("ActiveVehicle->Update")
+ mActiveVehicleList[i]->Update( dt );
+ END_PROFILE("ActiveVehicle->Update")
+
+ //rAssertMsg( mActiveVehicleList[i] != NULL, "VehicleCentral::Update - trying to add a null vehicle* pointer!" );
+
+ if ( mActiveVehicleList[i] != NULL )
+ {
+ if(mActiveVehicleList[i]->GetLocomotionType() == VL_PHYSICS)
+ {
+ BEGIN_PROFILE("WorldPhysManager()->UpdateDyna")
+ GetWorldPhysicsManager()->UpdateDynamicObjects(dt, mActiveVehicleList[i]->mCollisionAreaIndex);
+ GetWorldPhysicsManager()->UpdateAnimCollisions(dt, mActiveVehicleList[i]->mCollisionAreaIndex);
+ END_PROFILE("WorldPhysManager()->UpdateDyna")
+ // Temp removed this because objects were being updated twice.
+ // more important (for me) to have the character update anim objects.
+ //
+ // TBJ [8/27/2002]
+ //
+ //GetWorldPhysicsManager()->UpdateAnimCollisions(dt, mActiveVehicleList[i]->mCollisionAreaIndex);
+
+ }
+ }
+ }
+ }
+}
+
+
+//=============================================================================
+// VehicleCentral::ClearSpot
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& point, float radius)
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::ClearSpot(rmt::Vector& point, float radius, Vehicle* skipCar)
+{
+ rAssert(0); // don't call this
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum() &&
+ mActiveVehicleList[i] != skipCar)
+ {
+
+ rmt::Vector carPosition = mActiveVehicleList[i]->rPosition();
+
+ rmt::Vector vectorToCar = carPosition;
+ vectorToCar.Sub(point);
+
+ float carsWheelBase = mActiveVehicleList[i]->GetWheelBase();
+
+ float dist = vectorToCar.Magnitude();
+ if(dist - carsWheelBase < radius)
+ {
+ // this car has to move back
+
+ float amount = radius - (dist - carsWheelBase);
+
+ // first test
+ // blast it back by some ratio of amount?
+
+ rmt::Vector fix = vectorToCar;
+ fix.NormalizeSafe();
+ fix.Scale(amount);
+
+ carPosition.Add(fix);
+
+ // quick test for fun and safety
+ carPosition.y += 2.0f;
+
+ float ang = mActiveVehicleList[i]->GetFacingInRadians();
+
+ rmt::Matrix m;
+ m.Identity();
+ m.FillRotateXYZ( 0.0f, ang, 0.0f );
+ m.FillTranslate(carPosition);
+
+ mActiveVehicleList[i]->SetTransform(m);
+
+
+ /*
+ rmt::Vector& linearVelocity = mActiveVehicleList[i]->mSimStateArticulated->GetLinearVelocity();
+
+ vectorToCar.NormalizeSafe();
+
+ static float magicshit = 1.0f;
+
+ vectorToCar.Scale(amount * magicshit);
+
+ linearVelocity = vectorToCar;
+ */
+
+ }
+
+
+
+ }
+ }
+
+
+}
+
+
+
+//=============================================================================
+// VehicleCentral::PostSubstepUpdate
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::PostSubstepUpdate(float dt)
+{
+
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ //rAssert( mActiveVehicleList[ i ] );
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum())
+ {
+ mActiveVehicleList[i]->PostSubstepUpdate(dt);
+ }
+ }
+}
+
+
+//=============================================================================
+// VehicleCentral::PreCollisionPrep
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( )
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::PreCollisionPrep(float dt, bool firstSubstep)
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum())
+ {
+ mActiveVehicleList[ i ]->PreCollisionPrep(dt, firstSubstep);
+ }
+ }
+}
+
+
+//=============================================================================
+// VehicleCentral::SubmitStatics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::SubmitStatics()
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum())
+ {
+ if(1)
+ //if(mActiveVehicleList[i]->GetLocomotionType() == VL_PHYSICS)
+ {
+ rmt::Vector position = mActiveVehicleList[i]->GetPosition();
+
+ // at 200kmh, we cover 55.5 m/s
+ // a long frame would be 50ms...
+ //
+ // at that rate we'd cover 2.775 m
+
+ // hmm.... ferrini wheel base is approx 3m
+
+ //float radius = mActiveVehicleList[i]->GetWheelBase() * 3.0f; // TODO - what magic number?
+ float radius = mActiveVehicleList[i]->GetWheelBase() * 2.0f; // TODO - what magic number?
+
+ int collisionAreaIndex = mActiveVehicleList[i]->mCollisionAreaIndex;
+ rAssert(collisionAreaIndex != -1);
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ Vehicle* playerVehicle = 0;
+ if(playerAvatar)
+ {
+ playerVehicle = playerAvatar->GetVehicle();
+ }
+
+ if(playerVehicle && mActiveVehicleList[i] == playerVehicle)
+ {
+ GetWorldPhysicsManager()->SubmitStaticsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), true);
+ GetWorldPhysicsManager()->SubmitFencePiecesPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), true);
+ }
+ else
+ {
+ GetWorldPhysicsManager()->SubmitStaticsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), false);
+ GetWorldPhysicsManager()->SubmitFencePiecesPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState());
+ }
+
+ // TODO - should probably only do this one if it's a human driver or AI ... ie. not traffic bouncing around
+ //GetWorldPhysicsManager()->SubmitFencePiecesPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState());
+ }
+ else
+ {
+ /*
+ // an active vehicle, but not VL_PHYSICS
+ // so we want to do a DSG query and cleanup of our collision area if necessary
+
+ rmt::Vector position = mActiveVehicleList[i]->GetPosition();
+
+ float radius = mActiveVehicleList[i]->GetWheelBase() * 2.0f; // TODO - what magic number?
+
+ int collisionAreaIndex = mActiveVehicleList[i]->mCollisionAreaIndex;
+ rAssert(collisionAreaIndex != -1);
+
+ GetWorldPhysicsManager()->CleanOnlyStaticsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState());
+
+ // TODO - should probably only do this one if it's a human driver or AI ... ie. not traffic bouncing around
+ GetWorldPhysicsManager()->SubmitFencePiecesPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState());
+ */
+
+ }
+
+ }
+ }
+
+}
+
+
+
+//=============================================================================
+// VehicleCentral::SubmitDynamics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::SubmitDynamics()
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum())
+ {
+ // new
+ // call this for all (one) VT_USER cars first
+ //if(mActiveVehicleList[i]->GetLocomotionType() == VL_PHYSICS && mActiveVehicleList[i]->mVehicleType == VT_USER)
+ if(mActiveVehicleList[i]->mVehicleType == VT_USER)
+ {
+ rmt::Vector position = mActiveVehicleList[i]->GetPosition();
+
+ //float radius = mActiveVehicleList[i]->GetWheelBase() * 3.0f; // TODO - what magic number?
+ float radius = mActiveVehicleList[i]->GetWheelBase() * 2.0f; // TODO - what magic number?
+
+ int collisionAreaIndex = mActiveVehicleList[i]->mCollisionAreaIndex;
+ rAssert(collisionAreaIndex != -1);
+
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ Vehicle* playerVehicle = 0;
+ if(playerAvatar)
+ {
+ playerVehicle = playerAvatar->GetVehicle();
+ }
+
+ if(playerVehicle && mActiveVehicleList[i] == playerVehicle)
+ {
+ GetWorldPhysicsManager()->SubmitDynamicsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), true);
+ }
+ else
+ {
+ GetWorldPhysicsManager()->SubmitDynamicsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), false);
+ }
+ }
+ }
+ }
+
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum())
+ {
+ //if(mActiveVehicleList[i]->GetLocomotionType() == VL_PHYSICS && mActiveVehicleList[i]->mVehicleType != VT_USER)
+ if(mActiveVehicleList[i]->mVehicleType != VT_USER)
+ {
+ rmt::Vector position = mActiveVehicleList[i]->GetPosition();
+
+ //float radius = mActiveVehicleList[i]->GetWheelBase() * 3.0f; // TODO - what magic number?
+ float radius = mActiveVehicleList[i]->GetWheelBase() * 2.0f; // TODO - what magic number?
+
+ int collisionAreaIndex = mActiveVehicleList[i]->mCollisionAreaIndex;
+ rAssert(collisionAreaIndex != -1);
+
+
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+ Vehicle* playerVehicle = 0;
+ if(playerAvatar)
+ {
+ playerVehicle = playerAvatar->GetVehicle();
+ }
+
+ if(playerVehicle && mActiveVehicleList[i] == playerVehicle)
+ {
+ GetWorldPhysicsManager()->SubmitDynamicsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), true);
+ }
+ else
+ {
+ GetWorldPhysicsManager()->SubmitDynamicsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), false);
+ }
+
+ //GetWorldPhysicsManager()->SubmitDynamicsPseudoCallback(position, radius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState(), false);
+ }
+ }
+ }
+
+}
+
+
+//=============================================================================
+// VehicleCentral::SubmitAnimCollisions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::SubmitAnimCollisions()
+{
+ int i;
+ for ( i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if( mActiveVehicleList[i] &&
+ GetRenderManager()->rCurWorldRenderLayer() == mActiveVehicleList[i]->GetRenderLayerEnum())
+ {
+ if(1)
+ //if(mActiveVehicleList[i]->GetLocomotionType() == VL_PHYSICS)
+ {
+ rmt::Vector position = mActiveVehicleList[i]->GetPosition();
+
+ //float collradius = mActiveVehicleList[i]->GetWheelBase() * 3.0f; // TODO - what magic number?
+ float collradius = mActiveVehicleList[i]->GetWheelBase() * 2.0f; // TODO - what magic number?
+
+ int collisionAreaIndex = mActiveVehicleList[i]->mCollisionAreaIndex;
+ rAssert(collisionAreaIndex != -1);
+
+ GetWorldPhysicsManager()->SubmitAnimCollisionsPseudoCallback(position, collradius, collisionAreaIndex, mActiveVehicleList[i]->GetSimState());
+
+ float updateradius = collradius * 1.0f; //? TODO - what magic number
+ GetWorldPhysicsManager()->SubmitAnimCollisionsForUpdateOnly(position, updateradius, collisionAreaIndex);
+ }
+ }
+ }
+
+
+
+}
+
+//=============================================================================
+// VehicleCentral::GetVehicleByName
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( const char* name )
+//
+// Return: Vehicle
+//
+//=============================================================================
+Vehicle* VehicleCentral::GetVehicleByName( const char* name ) const
+{
+ return GetVehicleByUID( tEntity::MakeUID( name ) );
+}
+
+/*
+==============================================================================
+VehicleCentral::GetVehicleByUID
+==============================================================================
+Description: Comment
+
+Parameters: ( tUID uid )
+
+Return: Vehicle
+
+=============================================================================
+*/
+Vehicle* VehicleCentral::GetVehicleByUID( tUID uid ) const
+{
+ Vehicle* vehicle = 0;
+ for( int i = 0; i < MAX_ACTIVE_VEHICLES; i++ )
+ {
+ if ( mActiveVehicleList[ i ] != 0 )
+ {
+ if( uid == tEntity::MakeUID( mActiveVehicleList[ i ]->GetName() ) )
+ {
+ vehicle = mActiveVehicleList[ i ];
+ break;
+ }
+ }
+ }
+ return vehicle;
+}
+
+
+//=============================================================================
+// VehicleCentral::OnProcessRequestsComplete
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( void* pUserData )
+//
+// Return: void
+//
+//=============================================================================
+void VehicleCentral::OnProcessRequestsComplete( void* pUserData )
+{
+ Character* theDriver;
+
+ Vehicle* vehicle = GetCurrentVehicleUnderConstruction();
+ rAssert( vehicle );
+
+ vehicle->CalculateValuesBasedOnDesignerParams();
+
+ SetupDriver(vehicle);
+
+ theDriver = vehicle->GetDriver();
+ if( theDriver != NULL )
+ {
+ //
+ // Despite the name, send this for every vehicle load. The dialogue
+ // system keeps a flag to determine which ones come from phone booths,
+ // and throws the rest of 'em out. Not pretty, but we're going final.
+ // -- Esan
+ //
+ GetEventManager()->TriggerEvent( EVENT_PHONE_BOOTH_NEW_VEHICLE_SELECTED, theDriver );
+ }
+ else
+ {
+ GetEventManager()->TriggerEvent( EVENT_PHONE_BOOTH_CANCEL_RIDEREPLY_LINE );
+ }
+
+ //Clear the head.
+ mVehicleUnderConstruction[mCurrentVehicleUnderContructionHead] = NULL;
+ mCurrentVehicleUnderContructionHead = (mCurrentVehicleUnderContructionHead + 1) % MAX_ACTIVE_VEHICLES;
+
+ if ( mCurrentVehicleUnderContructionHead == mCurrentVehicleUnderConstructionTail )
+ {
+ rAssert( mVehicleUnderConstruction[mCurrentVehicleUnderContructionHead] == NULL );
+ }
+}
+
+void VehicleCentral::ActivateVehicleTriggers(bool active)
+{
+ if(active == mbVehicleTriggersActive)
+ {
+ return;
+ }
+
+ mbVehicleTriggersActive = active;
+
+ Vehicle* v = NULL;
+
+ if( mbVehicleTriggersActive )
+ {
+ for(int i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ v = mActiveVehicleList[i];
+ if(v)
+ {
+ v->ActivateTriggers(true);
+ }
+ }
+ }
+ else
+ {
+ for(int i = 0; i < MAX_ACTIVE_VEHICLES; i++)
+ {
+ v = mActiveVehicleList[i];
+ if( v )
+ {
+ v->ActivateTriggers(false);
+ }
+ }
+ }
+
+}
+
+void VehicleCentral::ClearSuppressedDrivers(void)
+{
+ for(unsigned i = 0; i < mSuppressedDriverCount; i++)
+ {
+ mSuppressedDrivers[i].SetText(NULL);
+ }
+
+ mSuppressedDriverCount = 0;
+}
+
+void VehicleCentral::AddSuppressedDriver(const char* name)
+{
+ rAssert(mSuppressedDriverCount < MAX_SuppressED_DRIVERS);
+ mSuppressedDrivers[mSuppressedDriverCount++].SetText(name);
+}
+
+void VehicleCentral::RemoveSuppressedDriver(const char* name)
+{
+ tUID uid = tEntity::MakeUID(name);
+ for(unsigned i = 0; i < mSuppressedDriverCount; i++)
+ {
+ if(mSuppressedDrivers[i].GetUID() == uid)
+ {
+ mSuppressedDrivers[i].SetUID((tUID)0);
+ return;
+ }
+ }
+}
+
+bool VehicleCentral::IsDriverSuppressed(const char* name)
+{
+ tUID uid = tEntity::MakeUID(name);
+ for(unsigned i = 0; i < mSuppressedDriverCount; i++)
+ {
+ if(mSuppressedDrivers[i].GetUID() == uid)
+ return true;
+ }
+ return false;
+}
+
+void VehicleCentral::SetupDriver(Vehicle* vehicle)
+{
+ if(vehicle->GetDriver())
+ {
+ if((vehicle->GetDriver() != GetCharacterManager()->GetCharacter(0)) && (vehicle->GetDriver()->GetRole() != Character::ROLE_PEDESTRIAN))
+ {
+ GetCharacterManager()->RemoveCharacter(vehicle->GetDriver());
+ vehicle->SetDriver(NULL);
+ }
+ }
+
+ if(vehicle->mDriverInit != FORCE_NO_DRIVER)
+ {
+ if((vehicle->GetDriverName()[0] != 0))
+ {
+ if(strcmp(vehicle->GetDriverName(),"phantom") == 0)
+ {
+ vehicle->SetPhantomDriver(true);
+ }
+ else
+ {
+ if((strcmp(vehicle->GetDriverName(),"none") != 0) &&
+ ((vehicle->mDriverInit == FORCE_DRIVER) || !IsDriverSuppressed(vehicle->GetDriverName())))
+ {
+ char uniqueName[16];
+ sprintf(uniqueName, "d_%s", vehicle->GetDriverName());
+
+ Character* character = NULL;
+ character = GetCharacterManager()->GetCharacterByName(uniqueName);
+ if(character && ((character->GetTargetVehicle() == vehicle) || (character->GetTargetVehicle() == NULL)))
+ {
+ GetCharacterManager()->RemoveCharacter(character);
+ }
+
+ character = GetCharacterManager()->AddCharacter(CharacterManager::NPC, uniqueName, vehicle->GetDriverName(), "npd", "");
+
+ static_cast<NPCController*>(character->GetController())->TransitToState(NPCController::NONE);
+ character->SetTargetVehicle( vehicle );
+ character->AddToWorldScene();
+ character->GetStateManager()->SetState<CharacterAi::InCar>();
+
+ vehicle->SetDriver(character);
+
+ character->SetRole(Character::ROLE_DRIVER);
+ }
+ }
+ }
+ }
+}
+
+
+//chuck adding this method so we can determine if car is still underconstruction.
+
+bool VehicleCentral::IsCarUnderConstruction(const char* name)
+{
+ //unsigned int pListPtr = mCurrentVehicleUnderContructionHead;
+
+ for(int i=0;i<MAX_ACTIVE_VEHICLES;i++)
+ {
+ if (mVehicleUnderConstruction[i] != NULL)
+ {
+ if( strcmp(mVehicleUnderConstruction[i]->GetName(),name) == 0)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool VehicleCentral::IsCarUnderConstruction(const Vehicle* vehicle)
+{
+ for(int i=0;i<MAX_ACTIVE_VEHICLES;i++)
+ {
+ if (mVehicleUnderConstruction[i] != NULL)
+ {
+ if( mVehicleUnderConstruction[i] == vehicle )
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+VehicleAI* VehicleCentral::GetVehicleAI( Vehicle* vehicle )
+{
+ if( vehicle )
+ {
+ //////////////////////////
+ // if Demo context, it's ok to fetch AI for
+ // non VT_AI types. Otherwise, we must boot!!
+ //
+ // Note:
+ // Supersprint doesn't worry about these, the AI cars should
+ // be VT_AI...
+ //
+ if( GetGameFlow()->GetCurrentContext() != CONTEXT_DEMO &&
+ vehicle->mVehicleType != VT_AI )
+ {
+ return NULL;
+ }
+
+ int id = GetVehicleCentral()->GetVehicleId( vehicle );
+ VehicleController* controller = GetVehicleCentral()->GetVehicleController( id );
+ if( controller )
+ {
+ rAssert( dynamic_cast<VehicleAI*>( controller ) );
+ return static_cast<VehicleAI*>( controller );
+ }
+ }
+ return NULL;
+}
+
+
+void VehicleCentral::DetachAllCollectibles()
+{
+ for ( int i = 0 ; i < GetNumVehicles() ; i++ )
+ {
+ Vehicle* v = GetVehicle( i );
+ if ( v != NULL )
+ {
+ v->DetachCollectible( rmt::Vector(0,0,0), false );
+ }
+ }
+}
diff --git a/game/code/worldsim/vehiclecentral.h b/game/code/worldsim/vehiclecentral.h
new file mode 100644
index 0000000..488846f
--- /dev/null
+++ b/game/code/worldsim/vehiclecentral.h
@@ -0,0 +1,205 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: vehiclecentral.h
+//
+// Description: holds a list of all the vehicles in the game and provides
+// easy access for the rest of the game objects that care
+//
+// History: May 1, 2002 - created, gmayer
+//
+//=============================================================================
+
+#ifndef VEHICLECENTRAL_H
+#define VEHICLECENTRAL_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <p3d/p3dtypes.hpp>
+
+#include <loading/loadingmanager.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/huskpool.h>
+
+
+
+//========================================
+// Forward References
+//========================================
+class VehicleAI;
+class AiVehicleController;
+class VehicleController;
+class EventLocator;
+class tBillboardQuadGroup;
+
+//=============================================================================
+//
+// Synopsis: they lived happily ever after....
+//
+//=============================================================================
+
+
+
+class VehicleCentral : public LoadingManager::ProcessRequestsCallback
+{
+ public:
+ enum DriverInit
+ {
+ ALLOW_DRIVER,
+ FORCE_NO_DRIVER,
+ FORCE_DRIVER
+ };
+
+ // Static Methods for accessing this singleton.
+ static VehicleCentral* GetInstance();
+ static VehicleCentral* CreateInstance();
+ static void DestroyInstance();
+
+ void PreLoad();
+ void Unload();
+
+ Vehicle* InitVehicle( const char* name, bool addToActiveVehicleList = true, char* confile = 0, VehicleType vt = VT_USER,
+ DriverInit s = ALLOW_DRIVER, bool playercar = false, bool startoutofcar = true);
+
+ // returns index if successful, otherwise -1
+ int AddVehicleToActiveList(Vehicle* vehicle);
+ // also make this put vehicles in the dsg.
+ //
+ // this is also where the vehicle should ask for a collision index, and insert itself
+
+ // returns whether or not it was even there in the first place
+ bool RemoveVehicleFromActiveList(Vehicle* vehicle);
+ // remove from dsg
+
+ //Triage hack, only for demo mode, or until Greg actually
+ //addrefs and releases --dm 12/01/02
+ VehicleController* RemoveVehicleController( int mAIIndex );
+
+ void KillEmAll();
+
+ void ClearSpot(rmt::Vector& point, float radius, Vehicle* skipCar);
+
+ //void Suspend(); // temporarily freeze all action - leave vehicles in the active list
+ //void Resume(); // unfreeze
+
+ int GetNumVehicles() const { return mNumActiveVehicles; }
+ Vehicle* GetVehicle( int id ) const;
+
+ void SetupConsoleFunctionsForVehicleTuning();
+
+ void SetVehicleController( int id, VehicleController* pVehicleController );
+ VehicleController* GetVehicleController( int id ) const;
+
+ int GetVehicleId( Vehicle* pVehicle, bool checkStrict = true ) const;
+
+ void SubmitStatics();
+ void SubmitDynamics();
+ void SubmitAnimCollisions();
+
+
+ void PreSubstepUpdate(float dt);
+ void PostSubstepUpdate(float dt);
+ void Update(float dt);
+ void PreCollisionPrep(float dt, bool firstSubstep);
+
+ enum { MAX_ACTIVE_VEHICLES = 30 };
+
+ static int GetMaxActiveVehicles() {return MAX_ACTIVE_VEHICLES;}
+
+ // *** //
+ void GetActiveVehicleList(Vehicle** &vList, int& nVehicles);
+ bool ActiveVehicleListIsFull() const;
+ // *** //
+
+ // hmmm... is there a nicer way to do this?
+ // need this so we have object to call script hooks on
+ Vehicle* mVehicleUnderConstruction[MAX_ACTIVE_VEHICLES];
+ unsigned int mCurrentVehicleUnderContructionHead;
+ unsigned int mCurrentVehicleUnderConstructionTail;
+ Vehicle* GetCurrentVehicleUnderConstruction() { return mVehicleUnderConstruction[ mCurrentVehicleUnderContructionHead ]; };
+ void OnProcessRequestsComplete( void* pUserData );
+
+
+ Vehicle* GetVehicleByName( const char* name ) const;
+ Vehicle* GetVehicleByUID( tUID uid ) const;
+
+ void ActivateVehicleTriggers(bool);
+
+ HuskPool mHuskPool; // just use default constructor
+
+ void InitHuskPool();
+ void FreeHuskPool();
+
+ void ClearSuppressedDrivers(void);
+ void AddSuppressedDriver(const char* name);
+ void RemoveSuppressedDriver(const char* name);
+ bool IsDriverSuppressed(const char* name);
+
+ void SetupDriver(Vehicle*);
+
+ //
+ bool IsCarUnderConstruction(const char* name);
+ bool IsCarUnderConstruction(const Vehicle* vehicle);
+
+ bool GetVehicleTriggersActive(void) {return mbVehicleTriggersActive;}
+
+ VehicleAI* GetVehicleAI( Vehicle* vehicle );
+
+ // Removes all collectibles attached to various vehicles
+ void DetachAllCollectibles();
+
+ // store the headlights here!
+ enum
+ {
+ NUM_HEADLIGHT_BBQGS = 3,
+ NUM_HEADLIGHT_BBQS = 7 // combined total of BBQs of all headlight BBQGs
+ };
+ tBillboardQuadGroup* mHeadLights[NUM_HEADLIGHT_BBQGS];
+ tColour mOriginalHeadLightColours[NUM_HEADLIGHT_BBQS];
+
+ private:
+
+ // No public access to these, use singleton interface.
+ VehicleCentral();
+ ~VehicleCentral();
+
+
+ // pointer to the single instance
+ static VehicleCentral* spInstance;
+
+ Vehicle* mActiveVehicleList[MAX_ACTIVE_VEHICLES];
+ VehicleController* mActiveVehicleControllerList[ MAX_ACTIVE_VEHICLES ];
+ //EventLocator* mDoorTriggerList[ MAX_ACTIVE_VEHICLES ];
+ int mNumActiveVehicles;
+
+ //bool mSuspended;
+
+ bool mbVehicleTriggersActive;
+
+ static const unsigned int MAX_SuppressED_DRIVERS = 32;
+ unsigned int mSuppressedDriverCount;
+ tName mSuppressedDrivers[MAX_SuppressED_DRIVERS];
+
+ static AiVehicleController* spGenericAI;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline VehicleCentral* GetVehicleCentral() { return( VehicleCentral::GetInstance() ); }
+
+
+
+// *** //
+inline void VehicleCentral::GetActiveVehicleList(Vehicle** &vList, int& nVehicles)
+{
+ vList = mActiveVehicleList;
+ nVehicles = mNumActiveVehicles;
+}
+inline bool VehicleCentral::ActiveVehicleListIsFull() const
+{
+ return (mNumActiveVehicles>=MAX_ACTIVE_VEHICLES);
+}
+// *** //
+
+#endif //VEHICLECENTRAL_H
diff --git a/game/code/worldsim/worldcollisionsolveragent.cpp b/game/code/worldsim/worldcollisionsolveragent.cpp
new file mode 100644
index 0000000..3362c34
--- /dev/null
+++ b/game/code/worldsim/worldcollisionsolveragent.cpp
@@ -0,0 +1,365 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File:
+//
+// Description: Implementation of class WorldCollisionSolverAgentManager
+//
+// History: 6/14/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <worldsim/worldcollisionsolveragent.h>
+#include <worldsim/redbrick/redbrickcollisionsolveragent.h>
+#include <worldsim/physicsairef.h>
+#include <memory/srrmemory.h>
+
+#include <camera/supercamcentral.h>
+
+#include <render/DSG/collisionentitydsg.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// WorldCollisionSolverAgentManager::WorldCollisionSolverAgentManager
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WorldCollisionSolverAgentManager::WorldCollisionSolverAgentManager()
+{
+ mpCollisionSolverAgentArray[ 0 ] = new(GMA_PERSISTENT) RedBrickCollisionSolverAgent;
+ mpCollisionSolverAgentArray[ 0 ]->AddRef();
+}
+
+//==============================================================================
+// WorldCollisionSolverAgentManager::~WorldCollisionSolverAgentManager
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WorldCollisionSolverAgentManager::~WorldCollisionSolverAgentManager()
+{
+ int i;
+ for ( i = 0; i < NUM_SOLVERS; i++ )
+ {
+ if ( mpCollisionSolverAgentArray[ i ] )
+ {
+ mpCollisionSolverAgentArray[ i ]->Release();
+ mpCollisionSolverAgentArray[ i ] = 0;
+ }
+ }
+}
+
+
+
+
+//=============================================================================
+// WorldCollisionSolverAgentManager::CollisionEvent
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (SimState* inSimStateA, int indexA, SimState* inSimStateB, int indexB, const rmt::Vector& inPos, float inDvN, float inDvT, SimulatedObject** simA, SimulatedObject** simB)
+//
+// Return: Solving_Answer
+//
+//=============================================================================
+Solving_Answer WorldCollisionSolverAgentManager::CollisionEvent(SimState* inSimStateA, int indexA, SimState* inSimStateB, int indexB, const rmt::Vector& inPos, float inDvN, float inDvT, SimulatedObject** simA, SimulatedObject** simB)
+{
+ return Solving_Continue;
+}
+
+
+/*
+==============================================================================
+WorldCollisionSolverAgentManager::PreCollisionEvent
+==============================================================================
+Description: Comment
+
+Parameters: (Collision& inCollision, int inPass)
+
+Return: Solving_Answer
+
+=============================================================================
+*/
+Solving_Answer WorldCollisionSolverAgentManager::PreCollisionEvent(Collision& inCollision, int inPass)
+{
+ CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ SimState* simStateA = collObjA->GetSimState();
+ SimState* simStateB = collObjB->GetSimState();
+
+
+ if(inCollision.mCollisionVolumeA->Type() == sim::BBoxVolumeType || inCollision.mCollisionVolumeB->Type() == sim::BBoxVolumeType)
+ {
+ return Solving_Aborted;
+ }
+
+
+
+
+ int i;
+ for ( i = 0; i < NUM_SOLVERS; i++ )
+ {
+ rAssert( mpCollisionSolverAgentArray[ i ] != (CollisionSolverAgent*)0 );
+ if ( Solving_Aborted == mpCollisionSolverAgentArray[ i ]->PreCollisionEvent( inCollision, inPass ) )
+ {
+ return Solving_Aborted;
+ }
+ }
+
+
+
+ // this is a bit messy, but I don't want to have to derive supercamcentral from CollisionEntityDSG
+ if(simStateA && simStateA->mAIRefIndex == PhysicsAIRef::CameraSphere)
+ {
+ rmt::Vector fixOffset = inCollision.mNormal;
+ float dist = inCollision.mDistance;
+
+ if(dist < 0.0f)
+ {
+ dist *= -1.0f;
+ }
+
+ fixOffset.Scale(dist);
+
+ SuperCamCentral* scc = (SuperCamCentral*)(simStateA->mAIRefPointer);
+ scc->AddCameraCollisionOffset(fixOffset);
+
+ return Solving_Aborted;
+
+ }
+ if(simStateB && simStateB->mAIRefIndex == PhysicsAIRef::CameraSphere)
+ {
+ // normal always points B to A
+ // distance -ve means interpenetration
+
+ rmt::Vector fixOffset = inCollision.mNormal;
+
+
+ float dist = inCollision.mDistance;
+
+ if(dist < 0.0f)
+ {
+ dist *= -1.0f;
+ }
+
+ fixOffset.Scale(-1.0f * dist);
+
+ SuperCamCentral* scc = (SuperCamCentral*)(simStateB->mAIRefPointer);
+ scc->AddCameraCollisionOffset(fixOffset);
+
+ return Solving_Aborted;
+
+ }
+
+
+
+ /*
+ if( simStateA && simStateB && ( simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveableGroundPlane ||
+ simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveableGroundPlane) )
+ {
+ //char buffy[128];
+ rReleaseString("something collided with a moveableobject groundplane\n");
+
+ }
+ */
+
+ CollisionEntityDSG* pObjA = static_cast<CollisionEntityDSG*>( simStateA->mAIRefPointer );
+ CollisionEntityDSG* pObjB = static_cast<CollisionEntityDSG*>( simStateB->mAIRefPointer );
+
+ //bool bSolveA = false;
+ //bool bSolveB = false;
+ Solving_Answer answerA = Solving_Continue;
+ Solving_Answer answerB = Solving_Continue;
+
+ if ( pObjA )
+ {
+ //bSolveA = pObjA->ReactToCollision( simStateB, inCollision );
+ answerA = pObjA->PreReactToCollision( simStateB, inCollision );
+ }
+ if ( pObjB )
+ {
+ //bSolveB = pObjB->ReactToCollision( simStateA, inCollision );
+ answerB = pObjB->PreReactToCollision( simStateA, inCollision );
+ }
+ //if ( bSolveA || bSolveB )
+ if(answerA == Solving_Aborted || answerB == Solving_Aborted)
+ {
+ return Solving_Aborted;
+ }
+ return CollisionSolverAgent::PreCollisionEvent(inCollision, inPass);
+}
+
+/*
+==============================================================================
+WorldCollisionSolverAgentManager::TestImpulse
+==============================================================================
+Description: Comment
+
+Parameters: (rmt::Vector& mImpulse, Collision& inCollision)
+
+Return: Solving_Answer
+
+=============================================================================
+*/
+Solving_Answer WorldCollisionSolverAgentManager::TestImpulse(rmt::Vector& impulse, Collision& inCollision)
+{
+ int i;
+
+ // vehicle is special and will be dealt with in the redbrick collision solver agent
+
+ // other ordinary shit will get the PostReactToCollision call here.
+
+ for ( i = 0; i < NUM_SOLVERS; i++ )
+ {
+ rAssert( mpCollisionSolverAgentArray[ i ] != (CollisionSolverAgent*)0 );
+ if ( Solving_Aborted == mpCollisionSolverAgentArray[ i ]->TestImpulse( impulse, inCollision ) )
+ {
+ return Solving_Aborted;
+ }
+ }
+
+ CollisionObject* collObjA = inCollision.mCollisionObjectA;
+ CollisionObject* collObjB = inCollision.mCollisionObjectB;
+
+ SimState* simStateA = collObjA->GetSimState();
+ SimState* simStateB = collObjB->GetSimState();
+
+ CollisionEntityDSG* pObjA = static_cast<CollisionEntityDSG*>( simStateA->mAIRefPointer );
+ CollisionEntityDSG* pObjB = static_cast<CollisionEntityDSG*>( simStateB->mAIRefPointer );
+
+ Solving_Answer answerA = Solving_Continue;
+ Solving_Answer answerB = Solving_Continue;
+
+ if(pObjA)
+ {
+ answerA = pObjA->PostReactToCollision(impulse, inCollision);
+ }
+ if(pObjB)
+ {
+ answerB = pObjB->PostReactToCollision(impulse, inCollision);
+ }
+
+ if(answerA == Solving_Aborted || answerB == Solving_Aborted)
+ {
+ return Solving_Aborted;
+ }
+
+ /*
+ if( simStateA && simStateB && ( simStateA->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveableGroundPlane ||
+ simStateB->mAIRefIndex == PhysicsAIRef::redBrickPhizMoveableGroundPlane) )
+ {
+ //char buffy[128];
+ rReleaseString("something is testing impulse with a moveableobject groundplane\n");
+
+ }
+ */
+
+
+ return CollisionSolverAgent::TestImpulse(impulse, inCollision);
+
+
+}
+
+/*
+==============================================================================
+WorldCollisionSolverAgentManager::TestCache
+==============================================================================
+Description: Comment
+
+Parameters: (SimState* inSimState, int inIndex)
+
+Return: Solving_Answer
+
+=============================================================================
+*/
+Solving_Answer WorldCollisionSolverAgentManager::TestCache(SimState* inSimState, int inIndex)
+{
+ int i;
+ for ( i = 0; i < NUM_SOLVERS; i++ )
+ {
+ rAssert( mpCollisionSolverAgentArray[ i ] != (CollisionSolverAgent*)0 );
+ if ( Solving_Aborted == mpCollisionSolverAgentArray[ i ]->TestCache( inSimState, inIndex ) )
+ {
+ return Solving_Aborted;
+ }
+ }
+ return Solving_Continue;
+}
+
+
+
+
+Solving_Answer WorldCollisionSolverAgentManager::EndObjectCollision(SimState* inSimState, int inIndex)
+{
+
+ int i;
+ for ( i = 0; i < NUM_SOLVERS; i++ )
+ {
+ rAssert( mpCollisionSolverAgentArray[ i ] != (CollisionSolverAgent*)0 );
+ if ( Solving_Aborted == mpCollisionSolverAgentArray[ i ]->EndObjectCollision(inSimState, inIndex) )
+ {
+ return Solving_Aborted;
+ }
+ }
+ return Solving_Continue;
+
+}
+
+
+
+/*
+==============================================================================
+WorldCollisionSolverAgentManager::ResetCollisionFlags
+==============================================================================
+Description: Comment
+
+Parameters: ()
+
+Return: void
+
+=============================================================================
+*/
+void WorldCollisionSolverAgentManager::ResetCollisionFlags()
+{
+ int i;
+ for ( i = 0; i < NUM_SOLVERS; i++ )
+ {
+ rAssert( mpCollisionSolverAgentArray[ i ] != (CollisionSolverAgent*)0 );
+ mpCollisionSolverAgentArray[ i ]->ResetCollisionFlags();
+ }
+}
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/worldsim/worldcollisionsolveragent.h b/game/code/worldsim/worldcollisionsolveragent.h
new file mode 100644
index 0000000..c624e01
--- /dev/null
+++ b/game/code/worldsim/worldcollisionsolveragent.h
@@ -0,0 +1,80 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: worldcollisionsolveragent.h
+//
+// Description: Blahblahblah
+//
+// History: 6/14/2002 + Created -- TBJ
+//
+//=============================================================================
+
+#ifndef WORLDCOLLISIONSOLVERAGENT_H
+#define WORLDCOLLISIONSOLVERAGENT_H
+
+//========================================
+// Nested Includes
+//========================================
+#include <simcollision/impulsebasedcollisionsolver.hpp>
+
+//========================================
+// Forward References
+//========================================
+class RedBrickCollisionSolverAgent;
+
+using namespace sim;
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+class WorldCollisionSolverAgent
+:
+public CollisionSolverAgent
+{
+public:
+ virtual void ResetCollisionFlags() {};
+};
+
+class WorldCollisionSolverAgentManager
+:
+public CollisionSolverAgent
+{
+public:
+ WorldCollisionSolverAgentManager();
+ ~WorldCollisionSolverAgentManager();
+
+ // the key method to override
+ Solving_Answer PreCollisionEvent(Collision& inCollision, int inPass);
+ Solving_Answer TestImpulse(rmt::Vector& mImpulse, Collision& inCollision);
+ Solving_Answer TestCache(SimState* inSimState, int inIndex);
+
+
+ Solving_Answer EndObjectCollision(SimState* inSimState, int inIndex);
+
+
+ // need to override this so that the sim library version doesn't automatically
+ // switch from ai to sim ctrl when objects are hit
+
+ // this method allows to trigger sounds, animation and modify the objects state.
+ Solving_Answer CollisionEvent( SimState* inSimStateA, int indexA,
+ SimState* inSimStateB, int indexB,
+ const rmt::Vector& inPos, float inDvN, float inDvT,
+ SimulatedObject** simA, SimulatedObject** simB);
+
+
+
+
+ void ResetCollisionFlags();
+private:
+
+ //Prevent wasteful constructor creation.
+ WorldCollisionSolverAgentManager( const WorldCollisionSolverAgent& worldcollisionsolveragent );
+ WorldCollisionSolverAgentManager& operator=( const WorldCollisionSolverAgentManager& worldcollisionsolveragent );
+
+ static const int NUM_SOLVERS = 1;
+ WorldCollisionSolverAgent* mpCollisionSolverAgentArray[ NUM_SOLVERS ];
+};
+
+
+#endif //WORLDCOLLISIONSOLVERAGENT_H
diff --git a/game/code/worldsim/worldobject.cpp b/game/code/worldsim/worldobject.cpp
new file mode 100644
index 0000000..e5726f6
--- /dev/null
+++ b/game/code/worldsim/worldobject.cpp
@@ -0,0 +1,125 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: worldobject.cpp
+//
+// Description: Implement WorldObject
+//
+// History: 14/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+// Foundation Tech
+#include <raddebug.hpp>
+#include <p3d/matrixstack.hpp>
+#include <p3d/utility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <worldsim/worldobject.h>
+#include <debug/profiler.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+//******************************************************************************
+//
+// Public Member Functions
+//
+//******************************************************************************
+
+//==============================================================================
+// WorldObject::WorldObject
+//==============================================================================
+// Description: Constructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WorldObject::WorldObject( tDrawable* drawable )
+{
+ if( drawable != NULL )
+ {
+ mDrawable = drawable;
+ mDrawable->AddRef();
+ }
+ else
+ {
+ mDrawable = NULL;
+ }
+
+ mTransform.Identity();
+}
+
+//==============================================================================
+// WorldObject::~WorldObject
+//==============================================================================
+// Description: Destructor.
+//
+// Parameters: None.
+//
+// Return: N/A.
+//
+//==============================================================================
+WorldObject::~WorldObject()
+{
+ if( mDrawable != NULL )
+ {
+ mDrawable->Release();
+ }
+}
+
+//=============================================================================
+// WorldObject::SetPosition
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( rmt::Vector &position )
+//
+// Return: void
+//
+//=============================================================================
+void WorldObject::SetPosition( rmt::Vector &position )
+{
+ mTransform.FillTranslate( position );
+}
+
+//=============================================================================
+// WorldObject::Display
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldObject::Display()
+{
+ BEGIN_PROFILE("WorldObject::Display")
+ if( mDrawable != NULL )
+ {
+ p3d::stack->PushMultiply( mTransform );
+
+ mDrawable->Display();
+
+ p3d::stack->Pop();
+ }
+ END_PROFILE("WorldObject::Display")
+}
+
+
+//******************************************************************************
+//
+// Private Member Functions
+//
+//******************************************************************************
diff --git a/game/code/worldsim/worldobject.h b/game/code/worldsim/worldobject.h
new file mode 100644
index 0000000..3cbc3a7
--- /dev/null
+++ b/game/code/worldsim/worldobject.h
@@ -0,0 +1,60 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: .h
+//
+// Description: Blahblahblah
+//
+// History: 14/05/2002 + Created -- NAME
+//
+//=============================================================================
+
+#ifndef WORLDOBJECT_H
+#define WORLDOBJECT_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+
+#include <radmath/radmath.hpp>
+#include <p3d/drawable.hpp>
+
+//=============================================================================
+//
+// Synopsis: Blahblahblah
+//
+//=============================================================================
+
+class WorldObject : public tDrawable
+{
+ public:
+ WorldObject( tDrawable* drawable );
+ virtual ~WorldObject();
+
+ void SetPosition( rmt::Vector &position );
+
+ virtual void Display();
+
+ tDrawable* GetDrawable() { return( mDrawable ); }
+ private:
+ //Prevent wasteful constructor creation.
+ WorldObject( const WorldObject& worldObject );
+ WorldObject& operator=( const WorldObject& worldObject );
+
+ tDrawable* mDrawable;
+ rmt::Matrix mTransform;
+};
+
+
+// Hack name for inventory section to hold sim library caches of SkeletonInfo.
+// Tracked to ATG as bug 1259.
+//
+const char SKELCACHE[] = "SkeletonInfoCache";
+
+
+#endif // WORLDOBJECT_H
+
diff --git a/game/code/worldsim/worldphysicsmanager.cpp b/game/code/worldsim/worldphysicsmanager.cpp
new file mode 100644
index 0000000..9a3eb24
--- /dev/null
+++ b/game/code/worldsim/worldphysicsmanager.cpp
@@ -0,0 +1,2921 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: worldphysicsmanager.cpp
+//
+// Description: bleek
+//
+// History: May 1, 2002 - created, gmayer
+//
+//=============================================================================
+
+//========================================
+// System Includes
+//========================================
+
+#include <raddebug.hpp>
+
+#include <simcommon/simstatearticulated.hpp>
+#include <simcommon/simenvironment.hpp>
+#include <simcollision/collisionmanager.hpp>
+#include <simcommon/physicsproperties.hpp>
+
+#include <simcollision/collisionvolume.hpp>
+#include <simcollision/collisiondisplay.hpp>
+#include <simcommon/simutility.hpp>
+
+//========================================
+// Project Includes
+//========================================
+#include <render/DSG/animcollisionentitydsg.h>
+#include <worldsim/worldphysicsmanager.h>
+#include <worldsim/worldcollisionsolveragent.h>
+
+#include <worldsim/groundplanepool.h>
+
+#include <worldsim/avatarmanager.h>
+#include <worldsim/vehiclecentral.h>
+
+#include <memory/srrmemory.h>
+
+#include <worldsim/redbrick/vehicle.h>
+#include <worldsim/redbrick/physicslocomotion.h> // just for debug prinout
+
+#include <camera/supercammanager.h>
+
+#include <debug/profiler.h>
+#include <debug/debuginfo.h>
+
+
+#include <render/Culling/ReserveArray.h>
+#include <render/DSG/StaticPhysDSG.h>
+#include <render/DSG/StatePropDSG.h>
+#include <render/DSG/FenceEntityDSG.h>
+#include <render/IntersectManager/IntersectManager.h>
+
+
+#include <worldsim/character/charactermanager.h>
+#include <worldsim/character/character.h>
+#include <ai/actionbuttonmanager.h>
+
+#include <constants/maxplayers.h>
+#include <constants/maxnpccharacters.h>
+
+#include <sound/soundmanager.h>
+
+#include <mission/gameplaymanager.h>
+
+// TODO
+// can remove
+// just here to hack in a light so I can see the texture damage states better
+#include <render/RenderManager/RenderManager.h>
+#include <render/RenderManager/RenderLayer.h>
+
+//******************************************************************************
+//
+// Global Data, Local Data, Local Classes
+//
+//******************************************************************************
+
+WorldPhysicsManager* WorldPhysicsManager::spInstance = 0;
+
+//=============================================================================
+// WorldPhysicsManager::WorldPhysicsManager
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: WorldPhysicsManager
+//
+//=============================================================================
+WorldPhysicsManager::WorldPhysicsManager()
+{
+ mCollisionDistanceCGS = 2.0f;
+ mWorldUp.Set(0.0f, 1.0f, 0.0f);
+ mTotalTime = 0.0f;
+ updateFrame = 0;
+ mLoopTime = 0.0f;
+
+ sim::InitializeSimulation(MetersUnits);
+
+ //SetSimUnits();
+
+ // move to bootupcontext onstart
+ //Init();
+ mLastTime = 30;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::~WorldPhysicsManager
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: WorldPhysicsManager
+//
+//=============================================================================
+WorldPhysicsManager::~WorldPhysicsManager()
+{
+ mSimEnvironment = 0;
+
+ int i, j;
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ for( j = 0; j < mMaxFencePerArea; j++ )
+ {
+ mFences[i][j].mFenceSimState->Release();
+ }
+
+ delete [] mCurrentStatics[i];
+ delete [] mCurrentAnimCollisions[i];
+ delete [] mCurrentUpdateAnimCollisions[i];
+ delete [] mFences[i];
+ delete [] mCurrentDynamics[i];
+ }
+ delete [] mCurrentStatics;
+ delete [] mCurrentAnimCollisions;
+ delete [] mCurrentUpdateAnimCollisions;
+ delete [] mFences;
+ delete [] mFencesInEachArea;
+ delete [] mCurrentDynamics;
+ delete mGroundPlanePool;
+
+ for( i = 0; i < mMaxFencePerArea; i++ )
+ {
+ mFenceDSGResults[i]->Release();
+ }
+ delete[] mFenceDSGResults;
+
+ mFencePhysicsProperties->Release();
+
+ mCollisionManager->Release();
+
+ mpWorldCollisionSolverAgentManager->Release();
+
+ sim::CleanupLineDrawing ();
+
+ delete [] mCollisionAreaAllocated;
+ delete [] mCollisionAreaActive;
+
+ sim::ResetSimulation ();
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::GetInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: WorldPhysicsManager
+//
+//=============================================================================
+WorldPhysicsManager* WorldPhysicsManager::GetInstance()
+{
+ rAssert(spInstance);
+ return spInstance;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::CreateInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: WorldPhysicsManager
+//
+//=============================================================================
+WorldPhysicsManager* WorldPhysicsManager::CreateInstance()
+{
+ rAssert(spInstance == 0);
+
+#ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ spInstance = new WorldPhysicsManager;
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+#else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ spInstance = new WorldPhysicsManager;
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+#endif
+ rAssert(spInstance);
+
+ return spInstance;
+
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::Init
+//=============================================================================
+// Description:
+// TODO - need this? - where to call it from?
+// ok to call from CreateSingletons
+//
+// no - it would be called from the a context OnStart if necessary
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::Init()
+{
+ mSimEnvironment = sim::SimEnvironment::GetDefaultSimEnvironment();
+
+ rAssert(mSimEnvironment);
+ //mSimEnvironment->AddRef(); // TODO
+ mSimEnvironment->SetCollisionDistanceCGS(mCollisionDistanceCGS);
+
+ //mSimEnvironment->SetGravityCGS(0.0f, -981.0f, 0.0f);
+ //mSimEnvironment->SetGravityCGS(0.0f, -2000.0f, 0.0f);
+ mSimEnvironment->SetGravityCGS(0.0f, -1600.0f, 0.0f); // plum's setting
+ //mSimEnvironment->SetGravityCGS(0.0f, -2500.0f, 0.0f);
+ mTimerTime = 0.0f;
+ mTimerOn = false;
+
+ InitCollisionManager();
+
+ sim::SetupLineDrawing ();
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::StartTimer
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::StartTimer()
+{
+ mTimerOn = true;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::StopTimer
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::StopTimer()
+{
+ mTimerOn = false;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::ResetTimer
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::ResetTimer()
+{
+ mTimerTime = 0.0f;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::ToggleTimerState
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::ToggleTimerState()
+{
+ if(mTimerOn)
+ {
+ mTimerOn = false;
+ }
+ else if(mTimerTime > 0.0f)
+ {
+ mTimerTime = 0.0f;
+ }
+ else
+ {
+ mTimerOn = true;
+ }
+}
+
+int WorldPhysicsManager::ApplyForceToDynamics( int collisionAreaIndex,
+ const rmt::Vector& position,
+ float radius,
+ const rmt::Vector& direction,
+ float force,
+ WorldPhysicsManager::NumObjectsHit* pObjectsHit,
+ CollisionEntityDSGList* pCollisionEntityList )
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ int i;
+ int numObjectsAffected = 0;
+ float radiusSqr = radius * radius;
+
+ if( pCollisionEntityList != NULL )
+ {
+ //
+ // Initialize entity list
+ //
+ for( i = 0; i < CollisionEntityDSGList::NUM_COLLISION_LIST_ENTITIES; i++ )
+ {
+ pCollisionEntityList->collisionEntity[i] = NULL;
+ }
+ }
+
+ for (i = 0 ; i < mMaxDynamics ; i++)
+ {
+ DynaPhysDSG* pDSG = mCurrentDynamics[collisionAreaIndex][i].mDynamicPhysDSG;
+ if ( pDSG != NULL )
+ {
+ rmt::Vector objPosition;
+ rmt::Box3D boundingBox;
+ pDSG->GetBoundingBox( &boundingBox );
+ rmt::Sphere boundingSphere( position, radius );
+ pDSG->GetPosition(&objPosition);
+ if ( boundingBox.Intersects( boundingSphere ) )
+ {
+ if ( pDSG->IsCollisionEnabled() )
+ {
+ pDSG->ApplyForce( direction, force );
+
+ if( ( pCollisionEntityList != NULL )
+ && ( numObjectsAffected < CollisionEntityDSGList::NUM_COLLISION_LIST_ENTITIES ) )
+ {
+ pCollisionEntityList->collisionEntity[numObjectsAffected] = pDSG;
+ }
+ ++numObjectsAffected;
+ }
+ }
+ }
+ }
+ return numObjectsAffected;
+}
+
+int WorldPhysicsManager::ApplyForceToDynamicsSpherical( int collisionAreaIndex,
+ const rmt::Vector& position,
+ float radius,
+ float force,
+ CollisionEntityDSGList* pCollisionEntityList )
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ int i;
+ int numObjectsAffected = 0;
+ float radiusSqr = radius * radius;
+
+ if( pCollisionEntityList != NULL )
+ {
+ //
+ // Initialize entity list
+ //
+ for( i = 0; i < CollisionEntityDSGList::NUM_COLLISION_LIST_ENTITIES; i++ )
+ {
+ pCollisionEntityList->collisionEntity[i] = NULL;
+ }
+ }
+
+ for (i = 0 ; i < mMaxDynamics ; i++)
+ {
+ DynaPhysDSG* pDSG = mCurrentDynamics[collisionAreaIndex][i].mDynamicPhysDSG;
+ if ( pDSG != NULL )
+ {
+ rmt::Vector objPosition;
+ rmt::Box3D boundingBox;
+ pDSG->GetBoundingBox( &boundingBox );
+ rmt::Sphere boundingSphere( position, radius );
+ pDSG->GetPosition(&objPosition);
+ if ( boundingBox.Intersects( boundingSphere ) )
+ {
+ if ( pDSG->IsCollisionEnabled() )
+ {
+ // Calculate the vector from the position to the object
+ rmt::Vector direction = boundingBox.Mid() - position;
+ direction.Normalize();
+ pDSG->ApplyForce( direction, force );
+ if( ( pCollisionEntityList != NULL )
+ && ( numObjectsAffected < CollisionEntityDSGList::NUM_COLLISION_LIST_ENTITIES ) )
+ {
+ pCollisionEntityList->collisionEntity[numObjectsAffected] = pDSG;
+ }
+
+ ++numObjectsAffected;
+ }
+ }
+ }
+ }
+ return numObjectsAffected;
+
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::InitCollisionManager
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::InitCollisionManager()
+{
+MEMTRACK_PUSH_GROUP( "WorldPhysicsManager" );
+
+ mCollisionManager = CollisionManager::GetInstance();
+ mCollisionManager->AddRef();
+
+ //mCollisionManager->SetCollisionDistanceCGS(mCollisionDistanceCGS);
+
+ //mCollisionManager->SetCollisionManagerAttributes(CM_DetectIfMoving); // TODO - which attribute should this be?
+ mCollisionManager->SetCollisionManagerAttributes(CM_DetectAll); // TODO - should be able to use IfMoving
+
+
+
+ //mReservedCollisionAreas = 2;
+ mReservedCollisionAreas = 0;
+
+ // setup areas:
+ //
+ // total number of areas = max allowed number of active vehicles
+ // + max active (human) characters {always 1 I think}
+ // + cameras for active cars or characters
+ //
+ // (new) + initial reserved slots
+
+ //mMaxVehicles = GetVehicleCentral()->GetMaxActiveVehicles();
+
+ //mMaxChars = GetCharacterManager()->GetMaxCharacters();
+
+ // new
+ // jan 12, 2003
+ // test
+ // values for vehicles and especially characters are now way too high
+ //
+ // only need slots for shit that is in existence simultaneously
+
+ // 10 is probably enough
+ mMaxVehicles = 15;
+
+ // 12 - 15 would probably be enough
+ mMaxChars = 18;
+
+
+
+ mMaxCameras = MAX_PLAYERS;
+
+ // this total is the number of collision areas we need + reserved
+
+ // following indexing standard will be used into the collision areas array:
+
+ /*
+
+ reserved:
+
+ 0
+ 1
+ 2
+ .
+ .
+ .
+ mReservedCollisionAreas - 1
+
+ vehicles:
+
+ mReservedCollisionAreas
+ .
+ .
+ .
+ .
+ .
+ mReservedCollisionAreas + maxVehicle - 1
+
+
+ characters:
+
+ mReservedCollisionAreas + maxVehicle
+ .
+ .
+ mReservedCollisionAreas+ maxVehicle + maxChars - 1
+
+ cameras:
+
+ mReservedCollisionAreas + maxVehicle + maxChars
+ .
+ .
+ mReservedCollisionAreas + maxVehicle + maxChars + maxCameras - 1
+
+
+ */
+
+ mNumCollisionAreas = mReservedCollisionAreas + mMaxVehicles + mMaxChars + mMaxCameras;
+ mCollisionManager->SetNbArea(mNumCollisionAreas);
+
+ // goddamn, goddamn!!
+ mCollisionManager->SetUseExclusiveAutoPair(true);
+
+ mCollisionManager->ActivateAllAreas();
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PushHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PushHeap( GMA_PERSISTENT );
+ #endif
+
+ mCollisionAreaAllocated = new bool[mNumCollisionAreas];
+ mCollisionAreaActive = new bool[mNumCollisionAreas];
+
+ //--------
+ // statics
+ //--------
+
+ // TODO - find the right number here
+ // TODO - different depending on thing?
+ mMaxStatics = 30;
+
+ mCurrentStatics = new StaticsInCollisionDetection*[mNumCollisionAreas];
+
+ int i,j;
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ mCollisionAreaAllocated[i] = false;
+ mCollisionAreaActive[i] = false;
+
+ mCurrentStatics[i] = new StaticsInCollisionDetection[mMaxStatics];
+
+ for(j = 0; j < mMaxStatics; j++)
+ {
+ mCurrentStatics[i][j].mStaticPhysDSG = 0;
+ mCurrentStatics[i][j].clean = false;
+ }
+ }
+
+
+ //----------------
+ // anim collisions
+ //----------------
+
+ // Todo: TBJ - make this value smaller.
+ // It has to be larger because of the large radius set in CharacterManager.
+ // When anim objects move in the DSG, we can reduce this to a smaller number.
+ //
+ mMaxAnimCollisions = 20;
+
+ mCurrentAnimCollisions = new AnimCollisionInCollisionDetection*[mNumCollisionAreas];
+
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ mCurrentAnimCollisions[i] = new AnimCollisionInCollisionDetection[mMaxAnimCollisions];
+
+ for(j = 0; j < mMaxAnimCollisions; j++)
+ {
+ mCurrentAnimCollisions[i][j].mAnimCollisionEntityDSG = 0;
+ mCurrentAnimCollisions[i][j].clean = false;
+ }
+ }
+
+
+ mMaxUpdateAnimCollisions = 64;
+
+ mCurrentUpdateAnimCollisions = new AnimCollisionInCollisionDetection*[mNumCollisionAreas];
+
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ mCurrentUpdateAnimCollisions[i] = new AnimCollisionInCollisionDetection[mMaxUpdateAnimCollisions];
+
+ for(j = 0; j < mMaxUpdateAnimCollisions; j++)
+ {
+ mCurrentUpdateAnimCollisions[i][j].mAnimCollisionEntityDSG = 0;
+ mCurrentUpdateAnimCollisions[i][j].clean = false;
+ }
+ }
+
+ //-------
+ // fences
+ //-------
+
+ // TODO - find the right number here!
+ mMaxFencePerArea = 8;
+
+ mFences = new FencePieces*[mNumCollisionAreas];
+
+ mFencesInEachArea = new int[mNumCollisionAreas];
+ // keep a total for efficient submission/removal
+
+
+ // new - to save memory
+ // May 7, 2003
+ //
+ // make all fences and ground planes in the pool use the same physics properties
+ // (perhaps later change so that one for fences and one for ground planes)
+
+ mFencePhysicsProperties = new sim::PhysicsProperties;
+ mFencePhysicsProperties->AddRef();
+
+
+
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ mFences[i] = new FencePieces[mMaxFencePerArea];
+
+ for(j = 0; j < mMaxFencePerArea; j++)
+ {
+ // need to make collision volumes here!
+ rmt::Vector center(0.0f, 0.0f, 0.0f);
+ rmt::Vector o0(1.0f, 0.0f, 0.0f);
+ rmt::Vector o1(0.0f, 1.0f, 0.0f);
+ rmt::Vector o2(0.0f, 0.0f, 1.0f);
+
+ sim::OBBoxVolume* tempOBBox = new OBBoxVolume(center, o0, o1, o2, 1.0f, 1.0f, 1.0f);
+
+
+ mFences[i][j].mFenceSimState = (sim::ManualSimState*)(SimState::CreateManualSimState(tempOBBox));
+ mFences[i][j].mFenceSimState->AddRef();
+
+ mFences[i][j].mFenceSimState->GetCollisionObject()->SetManualUpdate(true);
+ //mFences[i][j].mFenceSimState->GetCollisionObject()->SetAutoPair(true);
+ mFences[i][j].mFenceSimState->GetCollisionObject()->SetAutoPair(false);
+
+ // setting this should free the unnecessarly allocated default one:
+ //mFences[i][j].mFenceSimState->GetCollisionObject()->SetPhysicsProperties(mFencePhysicsProperties);
+ mFences[i][j].mFenceSimState->SetPhysicsProperties(this->mFencePhysicsProperties);
+
+
+ // give this thing a reasonable name for debug purposes
+ char buffy[128];
+ sprintf(buffy, "fence_a%d_n%d", i, j);
+
+ mFences[i][j].mFenceSimState->GetCollisionObject()->SetName(buffy);
+
+ mFences[i][j].mFenceSimState->mAIRefIndex = PhysicsAIRef::redBrickPhizFence;
+ mFences[i][j].mFenceSimState->mAIRefPointer = 0; // only set if object is derived from CollisionEntityDSG
+
+
+ mFences[i][j].mInCollision = false;
+ mFences[i][j].mClean = false;
+
+ }
+
+ mFencesInEachArea[i] = 0;
+
+ }
+
+ // debug drawing
+ mNumDebugFences = 0;
+
+ mFenceDSGResults = new FenceEntityDSG*[mMaxFencePerArea];
+ for(i = 0; i < mMaxFencePerArea; i++)
+ {
+ mFenceDSGResults[i] = new FenceEntityDSG;
+ mFenceDSGResults[i]->AddRef();
+ }
+
+ //---------
+ // dynamics
+ //---------
+
+ mMaxDynamics = 20;
+
+ mCurrentDynamics = new DynamicsInCollisionDetection*[mNumCollisionAreas];
+
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ mCurrentDynamics[i] = new DynamicsInCollisionDetection[mMaxDynamics];
+
+ for(j = 0; j < mMaxDynamics; j++)
+ {
+ mCurrentDynamics[i][j].mDynamicPhysDSG = 0;
+ mCurrentDynamics[i][j].clean = false;
+ }
+ }
+
+ //--------------
+ // ground planes
+ //--------------
+
+ mGroundPlanePool = new GroundPlanePool(mMaxDynamics * 2); // TODO - ????? how many
+
+
+ //-----------------------
+ // collision solver agent
+ //-----------------------
+ mpWorldCollisionSolverAgentManager = new WorldCollisionSolverAgentManager;
+ mpWorldCollisionSolverAgentManager->AddRef();
+
+ mCollisionManager->GetImpulseBasedCollisionSolver()->SetCollisionSolverAgent(mpWorldCollisionSolverAgentManager);
+
+
+ //enum DrawVolumeMethod {DrawVolumeOutline=0, DrawVolumeShape};
+ //sim::SetDrawVolumeMethod(DrawVolumeOutline);
+ sim::SetDrawVolumeMethod(DrawVolumeShape);
+
+ mInInterior = false;
+
+ #ifdef RAD_GAMECUBE
+ HeapMgr()->PopHeap( GMA_GC_VMM );
+ #else
+ HeapMgr()->PopHeap( GMA_PERSISTENT );
+ #endif
+MEMTRACK_POP_GROUP("WorldPhysicsManager");
+}
+
+//=============================================================================
+// WorldPhysicsManager::SuspendForInterior
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::SuspendForInterior()
+{
+ mInInterior = true;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::ResumeForOutside
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::ResumeForOutside()
+{
+ mInInterior = false;
+}
+
+
+
+//=============================================================================
+// WorldPhysicsManager::EmptyCollisionAreaIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::EmptyCollisionAreaIndex(int index)
+{
+ rAssert( index > WorldPhysicsManager::INVALID_COLLISION_AREA );
+ rAssert(mCollisionAreaAllocated[index]); // should not be trying to empty one that is not in use
+
+ mCollisionManager->ResetArea(index); // this will take everything out of that area!
+
+ int i;
+ for(i = 0; i < mMaxStatics; i++)
+ {
+ tRefCounted::Release( mCurrentStatics[index][i].mStaticPhysDSG );
+ mCurrentStatics[index][i].mStaticPhysDSG = 0;
+ mCurrentStatics[index][i].clean = false;
+ }
+
+ for(i = 0; i < mMaxAnimCollisions; i++)
+ {
+ tRefCounted::Release( mCurrentAnimCollisions[index][i].mAnimCollisionEntityDSG );
+ mCurrentAnimCollisions[index][i].mAnimCollisionEntityDSG = 0;
+ mCurrentAnimCollisions[index][i].clean = false;
+ }
+
+ for(i = 0; i < mMaxUpdateAnimCollisions; i++)
+ {
+ tRefCounted::Release( mCurrentUpdateAnimCollisions[index][i].mAnimCollisionEntityDSG );
+ mCurrentUpdateAnimCollisions[index][i].mAnimCollisionEntityDSG = 0;
+ mCurrentUpdateAnimCollisions[index][i].clean = false;
+ }
+
+ for(i = 0; i < mMaxFencePerArea; i++)
+ {
+ // don't need to add ref and release these 'cause they're only internal to the fences.
+ mFences[index][i].mInCollision = false;
+ }
+ //mFencesInEachArea[index] = 0;
+
+
+ for(i = 0; i < mMaxDynamics; i++)
+ {
+ if(mCurrentDynamics[index][i].mDynamicPhysDSG)
+ {
+ //TODO
+ if(mCurrentDynamics[index][i].mDynamicPhysDSG->GetAIRef() != PhysicsAIRef::redBrickVehicle)
+ {
+ mCurrentDynamics[index][i].mDynamicPhysDSG->FreeGroundPlane(); // in case it still has one
+
+ // this is also where the ground plane collision object will be disabled if
+ // there are no more refs
+ }
+
+
+
+
+
+ tRefCounted::Release( mCurrentDynamics[index][i].mDynamicPhysDSG );
+ mCurrentDynamics[index][i].mDynamicPhysDSG = 0;
+ mCurrentDynamics[index][i].clean = false;
+ }
+
+ }
+
+ // TODO - setup some sort of enable/disable interface also?
+}
+
+//=============================================================================
+// WorldPhysicsManager::FreeCollisionAreaIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::FreeCollisionAreaIndex(int index)
+{
+ rAssert( index > WorldPhysicsManager::INVALID_COLLISION_AREA );
+ rAssert(mCollisionAreaAllocated[index]); // should not be trying to free up one that is not in use
+
+ EmptyCollisionAreaIndex( index );
+
+ mCollisionAreaAllocated[index] = false;
+}
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+void WorldPhysicsManager::FreeAllCollisionAreaIndicies()
+{
+ int index = 0;
+ for(; index<mNumCollisionAreas; index++)
+ {
+ if(mCollisionAreaAllocated[index]) // should not be trying to free up one that is not in use
+ {
+ FreeCollisionAreaIndex(index);
+ }
+ }
+}
+
+//=============================================================================
+// WorldPhysicsManager::RemoveVehicleFromAnyOtherCurrentDynamicsList
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (Vehicle* vehicle)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(DynaPhysDSG* obj)
+{
+ int i;
+
+ // Vehicle* vehicle = dynamic_cast<Vehicle*>(obj);
+ // Character* character= dynamic_cast<Character*>(obj);
+ Vehicle* vehicle = NULL;
+ Character* character = NULL;
+
+ if ( obj->GetAIRef() == PhysicsAIRef::NPCharacter ||
+ obj->GetAIRef() == PhysicsAIRef::PlayerCharacter )
+ {
+ rAssert( dynamic_cast< Character* >( obj ) );
+ character = static_cast< Character*>( obj ) ;
+ }
+ else if ( obj->GetAIRef() == PhysicsAIRef::redBrickVehicle )
+ {
+ rAssert( dynamic_cast< Vehicle* >( obj ));
+ vehicle = static_cast< Vehicle*>( obj );
+ }
+ else
+ {
+ rAssert( dynamic_cast< Character* >( obj )==false);
+ rAssert( dynamic_cast< Vehicle* >( obj )==false);
+ }
+
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ if(vehicle && (i == vehicle->mCollisionAreaIndex))
+ {
+ continue;
+ }
+
+ if(character && (i == character->GetCollisionAreaIndex()))
+ {
+ continue;
+ }
+
+ if(!mCollisionAreaAllocated[i])
+ {
+ continue;
+ }
+
+ int j;
+ for(j = 0; j < mMaxDynamics; j++)
+ {
+ if(mCurrentDynamics[i][j].mDynamicPhysDSG == obj)
+ {
+ mCollisionManager->RemoveCollisionObject(obj->GetSimState()->GetCollisionObject(), i);
+
+
+ // only deal with ground plane here if the incoming object is not a car
+ if(!(vehicle))
+ {
+ int groundPlaneIndex = mCurrentDynamics[i][j].mDynamicPhysDSG->GetGroundPlaneIndex();
+ if(groundPlaneIndex != -1) // should never be false...
+ {
+ sim::CollisionObject* gpCollObj = mGroundPlanePool->GetSimState(groundPlaneIndex)->GetCollisionObject();
+
+ mCollisionManager->RemoveCollisionObject(gpCollObj, i);
+
+ mCurrentDynamics[i][j].mDynamicPhysDSG->FreeGroundPlane(); // this decrements the groundplaneref, but we still need to take it out of
+ }
+ }
+
+ mCurrentDynamics[i][j].mDynamicPhysDSG = 0;
+ mCurrentDynamics[i][j].clean = false;
+
+ obj->Release();
+
+
+ }
+
+ }
+ }
+}
+
+//=============================================================================
+// WorldPhysicsManager::OnQuitLevel
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::OnQuitLevel()
+{
+ // make sure ground plane pool has been properly free'd up
+ if(!(mGroundPlanePool->FreeAllGroundPlanes()))
+ {
+ // there was some ground planes that were not yet free'd when this was called!
+ rAssertMsg(false, "not all the ground planes were free'd by their dynamic object owners! bad dynamic objects!\n");
+ }
+
+}
+
+//=============================================================================
+// WorldPhysicsManager::GetCameraCollisionAreaIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+int WorldPhysicsManager::GetCameraCollisionAreaIndex()
+{
+ // moved to member data
+ //int maxVehicles = GetVehicleCentral()->GetMaxActiveVehicles();
+ //int maxChars = MAX_PLAYERS;
+ //int maxCameras = MAX_PLAYERS;
+
+ int start = mReservedCollisionAreas + mMaxVehicles + mMaxChars;
+ int end = mReservedCollisionAreas + mMaxVehicles + mMaxChars + mMaxCameras - 1;
+
+ int i;
+ for(i = start; i <= end; i++)
+ {
+ // look for first free index
+ if(mCollisionAreaAllocated[i])
+ {
+ continue;
+ }
+ else
+ {
+ mCollisionAreaAllocated[i] = true;
+ return i;
+ }
+ }
+
+ rReleaseAssertMsg( 0, "not enough camera collision indices\n" );
+ return WorldPhysicsManager::INVALID_COLLISION_AREA;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::GetVehicleCollisionAreaIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+int WorldPhysicsManager::GetVehicleCollisionAreaIndex()
+{
+ //int maxVehicles = GetVehicleCentral()->GetMaxActiveVehicles();
+
+ int start = mReservedCollisionAreas;
+ int end = mReservedCollisionAreas + mMaxVehicles - 1;
+
+ int i;
+ for(i = start; i <= end; i++)
+ {
+ // look for first free index
+ if(mCollisionAreaAllocated[i])
+ {
+ continue;
+ }
+ else
+ {
+ mCollisionAreaAllocated[i] = true;
+ return i;
+ }
+ }
+
+ rReleaseAssertMsg( 0, "not enough vehicle collision indices\n" );
+ return WorldPhysicsManager::INVALID_COLLISION_AREA;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::GetCharacterCollisionAreaIndex
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+int WorldPhysicsManager::GetCharacterCollisionAreaIndex()
+{
+ //int maxVehicles = GetVehicleCentral()->GetMaxActiveVehicles();
+ //int maxChars = MAX_PLAYERS;
+
+ int start = mReservedCollisionAreas + mMaxVehicles;
+ int end = mReservedCollisionAreas + mMaxVehicles + mMaxChars - 1;
+
+ int i;
+ for(i = start; i <= end; i++)
+ {
+ // look for first free index
+ if(mCollisionAreaAllocated[i])
+ {
+ continue;
+ }
+ else
+ {
+ mCollisionAreaAllocated[i] = true;
+ return i;
+ }
+ }
+
+ rReleaseAssertMsg( 0, "not enough character collision indices\n" );
+ return WorldPhysicsManager::INVALID_COLLISION_AREA;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::DestroyInstance
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::DestroyInstance()
+{
+ rAssert(spInstance);
+
+ // TODO - Release() collision manager
+
+ delete(GMA_PERSISTENT, spInstance);
+ spInstance = NULL;
+
+
+
+}
+
+
+
+//=============================================================================
+// WorldPhysicsManager::SetSimUnits
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+/*
+void WorldPhysicsManager::SetSimUnits()
+{
+ float LSCALE = 0.01f;
+ //float MSCALE = 0.001f;
+ float MSCALE = 1.0f;
+ float TSCALE = 1.0f;
+ SimUnitsManager um;
+ um.SetUnits(LSCALE, MSCALE, TSCALE);
+}
+*/
+
+//=============================================================================
+// WorldPhysicsManager::Update
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (unsigned int timeDeltaSeconds)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::Update(unsigned int timeDeltaMilliSeconds)
+{
+
+
+ BEGIN_PROFILE("WorldPhysicsManager::Update")
+
+
+ BEGIN_PROFILE("Vehicle Submits")
+ GetVehicleCentral()->SubmitStatics();
+ GetVehicleCentral()->SubmitAnimCollisions();
+ GetVehicleCentral()->SubmitDynamics();
+ END_PROFILE("Vehicle Submits")
+
+
+ BEGIN_PROFILE("Character Submits")
+ GetCharacterManager()->SubmitStatics();
+ GetCharacterManager()->SubmitAnimCollisions(); // both collision and update I assume
+ GetCharacterManager()->SubmitDynamics();
+ END_PROFILE("Character Submits")
+
+ BEGIN_PROFILE("Cam Submits")
+ GetSuperCamManager()->SubmitStatics();
+ END_PROFILE("Cam Submits")
+
+
+ BEGIN_PROFILE("Update Ground")
+ UpdateSimluatingDynamicObjectGroundPlanes();
+ END_PROFILE("Update Ground")
+
+ // new substep logic - nbrooke 12/7/03
+ //
+ // we want to try to minimize it ticking over into two substep mode unless it absolutly neccesary
+ // and try to run a 30fps on the ps2 all the time therefore I have
+ // 1) upped the threshold for going to two substeps to 35 ms from 32ms
+ // 2) only go into multiple substeps if we get two consecutive frames over the threshold or
+ // one frame WAY over the threshold
+ unsigned numSubsteps = 1;
+
+ if(((mLastTime > 35) && (timeDeltaMilliSeconds > 35)) || (timeDeltaMilliSeconds > 50))
+ {
+ numSubsteps = (timeDeltaMilliSeconds + 34) / 35;
+ }
+
+ mLastTime = timeDeltaMilliSeconds;
+
+ float substep = (float(timeDeltaMilliSeconds) * 0.001f) / float(numSubsteps);
+ float dt = static_cast<float>(timeDeltaMilliSeconds) * 0.001f;
+
+ // cap
+ if(numSubsteps > 10)
+ {
+ rReleasePrintf("\nhit 10 substeps!\n");
+ numSubsteps = 10;
+ }
+
+
+ mLoopTime = dt;
+
+ if(mTimerOn)
+ {
+ mTimerTime += dt;
+ }
+
+ BEGIN_PROFILE("WorldPhysicsManager::PreSubstepUpdate")
+
+ GetVehicleCentral()->PreSubstepUpdate(dt);
+
+ END_PROFILE("WorldPhysicsManager::PreSubstepUpdate")
+
+ BEGIN_PROFILE("PreSim")
+ GetCharacterManager( )->PreSimUpdate( dt );
+ END_PROFILE("PreSim")
+
+ bool firstSubstep = true;
+ //while(countDown > timestep)// * 1.5f)
+ for(unsigned i = 0; i < numSubsteps; i++)
+ {
+ // do shit
+ WorldSimSubstepGuts(substep, firstSubstep);
+ firstSubstep = false;
+ }
+
+ BEGIN_PROFILE("VehiclePost")
+ GetVehicleCentral()->PostSubstepUpdate(dt);
+ END_PROFILE("VehiclePost")
+
+ BEGIN_PROFILE("CharPost")
+ GetCharacterManager()->PostSimUpdate( dt );
+ END_PROFILE("CharPost")
+
+ END_PROFILE("WorldPhysicsManager::Update")
+
+ BEGIN_PROFILE("Phys DebugInfo")
+ DebugInfoDisplay();
+ END_PROFILE("Phys DebugInfo")
+
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::DebugInfoDisplay
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::DebugInfoDisplay()
+{
+ // maybe "Display" is a bit deceiving.
+
+ #ifdef DEBUGINFO_ENABLED
+
+ DEBUGINFO_PUSH_SECTION( "Vehicle Shit" );
+
+ // just display the vehicles position
+ // whichever vehicle player is driving
+
+ Avatar* playerAvatar = GetAvatarManager()->GetAvatarForPlayer(0);
+
+ Vehicle* playerVehicle = 0;
+
+ if(playerAvatar)
+ {
+ playerVehicle = playerAvatar->GetVehicle();
+
+ if(playerVehicle)
+ {
+ float x = 0.0f;
+ float y = 0.0f;
+ float z = 0.0f;
+
+ rmt::Vector pos = playerVehicle->GetPosition();
+ x = pos.x;
+ y = pos.y;
+ z = pos.z;
+
+
+ char buffy[128];
+ sprintf( buffy, "position - x: %.2f y: %.2f z: %.2f", x, y, z);
+
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+ float life = playerVehicle->GetVehicleLifePercentage(playerVehicle->mHitPoints);
+ sprintf( buffy, "damage inc. - %.3f", 1.0f - life);
+
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+ if(playerVehicle->mVehicleState == VS_NORMAL)
+ {
+ sprintf( buffy, "VS_NORMAL");
+ }
+ else if(playerVehicle->mVehicleState == VS_EBRAKE_SLIP)
+ {
+ sprintf( buffy, "VS_EBRAKE_SLIP");
+ }
+ else
+ {
+ sprintf( buffy, "VS_SLIP");
+ }
+
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+ /*
+ if(playerVehicle->mLosingTractionDueToAccel)
+ {
+ sprintf(buffy, "LOSING TRACTION DUE TO ACCELERATION!!");
+ }
+ else
+ {
+ sprintf(buffy, "unmodified traction");
+ }
+
+
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+ if(playerVehicle->mLoco == VL_PHYSICS) // I think this will always be the case
+ {
+ sprintf(buffy, "currentSteeringForce: %.2f", playerVehicle->mPhysicsLocomotion->mCurrentSteeringForce);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+ }
+ */
+
+
+ sprintf(buffy, "wheel turn angle unmodified input: %.2f", playerVehicle->mUnmodifiedInputWheelTurnAngle);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+ sprintf(buffy, "wheel turn angle normalized: %.2f", playerVehicle->mWheelTurnAngleInputValue);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+ sprintf(buffy, "wheel turn angle: %.2f", playerVehicle->mWheelTurnAngle);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+
+ sprintf(buffy, "kmh: %.2f", playerVehicle->mSpeedKmh);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+ sprintf(buffy, "ebrake: %.2f", playerVehicle->mEBrake);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+ sprintf(buffy, "gas: %.2f", playerVehicle->mGas);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+
+
+ sprintf(buffy, "timer: %.3f", mTimerTime);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+ /*
+
+ sprintf(buffy, "speed burst timer: %.3f", playerVehicle->mSpeedBurstTimer);
+ DEBUGINFO_ADDSCREENTEXT( buffy );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+ if(playerVehicle->mDoSpeedBurst)
+ {
+ sprintf(buffy, "doing speed burst!");
+ }
+ else
+ {
+ sprintf(buffy, "not doing speed burst");
+ }
+ */
+
+
+ }
+
+ }
+
+
+ DEBUGINFO_POP_SECTION();
+
+
+
+
+ if(playerVehicle)
+ {
+ char temp[128];
+ DebugInfo::GetInstance()->Push( "Vehicle Terrain Type" );
+
+
+ sprintf( temp, "average for vehicle: %d. %sside.", playerVehicle->mTerrainType, playerVehicle->mInterior ? "In" : "Out" );
+ DebugInfo::GetInstance()->AddScreenText( temp, tColour( 25, 150, 125 ) );
+
+ sprintf( temp, "wheel 0: %d", playerVehicle->mPhysicsLocomotion->mTerrainIntersectCache[0].mTerrainType);
+ DebugInfo::GetInstance()->AddScreenText( temp, tColour( 25, 150, 125 ) );
+
+ sprintf( temp, "wheel 1: %d", playerVehicle->mPhysicsLocomotion->mTerrainIntersectCache[1].mTerrainType);
+ DebugInfo::GetInstance()->AddScreenText( temp, tColour( 25, 150, 125 ) );
+
+ sprintf( temp, "wheel 2: %d", playerVehicle->mPhysicsLocomotion->mTerrainIntersectCache[2].mTerrainType);
+ DebugInfo::GetInstance()->AddScreenText( temp, tColour( 25, 150, 125 ) );
+
+ sprintf( temp, "wheel 3: %d", playerVehicle->mPhysicsLocomotion->mTerrainIntersectCache[3].mTerrainType);
+ DebugInfo::GetInstance()->AddScreenText( temp, tColour( 25, 150, 125 ) );
+
+
+
+ DebugInfo::GetInstance()->Pop();
+
+
+ /*
+ DebugInfo::GetInstance()->Push( "Player Vehicle Collision Info" );
+
+ int colindex = playerVehicle->mCollisionAreaIndex;
+ sprintf( temp, "number of collision pairs in player collision area: %d", this->mCollisionManager->GetCollisionObjectPairList(colindex)->ArraySize());
+
+ DEBUGINFO_ADDSCREENTEXT( temp );
+ DEBUGINFO_ADDSCREENTEXT( "" ); //?
+
+ DebugInfo::GetInstance()->Pop();
+ */
+
+
+ }
+
+
+
+ #endif // DEBUGINFO_ENABLED
+
+
+}
+
+//=============================================================================
+// WorldPhysicsManager::WorldSimSubstepGuts
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::WorldSimSubstepGuts(float dt, bool firstSubstep)
+{
+ BEGIN_PROFILE("WorldSimSubstepGuts")
+
+ mTotalTime += dt;
+ updateFrame++;
+
+ BEGIN_PROFILE("VC::PreCollisionPrep")
+ if(!mInInterior)
+ {
+ GetVehicleCentral()->PreCollisionPrep(dt, firstSubstep);
+ }
+ END_PROFILE("VC::PreCollisionPrep")
+
+ BEGIN_PROFILE("CharMgr::PreSimUpdate")
+ GetCharacterManager()->PreSubstepUpdate( dt );
+ END_PROFILE("CharMgr::PreSimUpdate")
+
+ BEGIN_PROFILE("ABMgr::Update")
+ GetActionButtonManager( )->Update( dt );
+ END_PROFILE("ABMgr::Update")
+
+// BEGIN_PROFILE("SuperCamMgr::PreCollPrep")
+// GetSuperCamManager()->PreCollisionPrep();
+// END_PROFILE("SuperCamMgr::PreCollPrep")
+
+ BEGIN_PROFILE("ResetCollisionFlags")
+ mpWorldCollisionSolverAgentManager->ResetCollisionFlags();
+ END_PROFILE("ResetCollisionFlags")
+
+
+
+BEGIN_PROFILE("DetectCollisions")
+ bool printOut = false;
+ mCollisionManager->ClearCollisions();
+ mCollisionManager->DetectCollision(dt, mTotalTime, printOut );
+ mCollisionManager->SolveCollision(dt, mTotalTime);
+
+END_PROFILE("DetectCollisions")
+
+
+ BEGIN_PROFILE("GetVehicleCentral()->Update")
+ GetVehicleCentral()->Update(dt);
+ END_PROFILE("GetVehicleCentral()->Update")
+ BEGIN_PROFILE("GetCharacterManager()->Update")
+ GetCharacterManager()->Update( dt );
+ END_PROFILE("GetCharacterManager()->Update")
+ BEGIN_PROFILE("GetCharacterManager()->PostSimUpdate")
+ GetCharacterManager()->PostSubstepUpdate(dt);
+ END_PROFILE("GetCharacterManager()->PostSimUpdate")
+
+ //Update the position of the super camers
+ BEGIN_PROFILE("GetSuperCamManager()->Update")
+ GetSuperCamManager()->Update( rmt::FtoL(dt * 1000.0f), firstSubstep );
+ END_PROFILE("GetSuperCamManager()->Update")
+
+ //Detect collisions on the new position of the super camera
+ int area = GetSuperCamManager()->GetSCC( 0 )->mCollisionAreaIndex;
+ mCollisionManager->ClearCollisions();
+ BEGIN_PROFILE("SuperCamMgr::PreCollPrep")
+ GetSuperCamManager()->PreCollisionPrep();
+ END_PROFILE("SuperCamMgr::PreCollPrep")
+
+ mCollisionManager->DetectCollision( area, dt, mTotalTime, printOut );
+ mCollisionManager->SolveCollision( area, dt, mTotalTime );
+
+ //Correct the position of the super camera
+ GetSuperCamManager()->GetSCC( 0 )->UpdateForPhysics( rmt::FtoL(dt * 1000.0f) );
+
+ END_PROFILE("WorldSimSubstepGuts")
+}
+
+
+
+//=============================================================================
+// WorldPhysicsManager::DisplayCollisionObjectsExceptVehicleInArea
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int area)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::DisplayFencesInArea(int area)
+{
+ rAssert( area > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ int i;
+ for(i = 0; i < mMaxFencePerArea; i++)
+ {
+ if(mFences[area][i].mInCollision)
+ {
+ sim::ManualSimState* mss = mFences[area][i].mFenceSimState;
+ sim::DrawCollisionObject(mss->GetCollisionObject());
+ }
+
+ }
+ for(i = 0; i < mNumDebugFences; i++)
+ {
+ // debug draw it!
+ mFenceDSGResults[i]->Display();
+ }
+
+}
+
+//=============================================================================
+// WorldPhysicsManager::SubmitStaticsPseudoCallback
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& position, float radius, int collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::SubmitStaticsPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState, bool allowAutoPairing)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ BEGIN_PROFILE( "SubmitStaticsPseudoCallback" );
+
+ ReserveArray<StaticPhysDSG*> staticPhysDSGList;
+
+ int i;
+ for(i = 0; i < mMaxStatics; i++)
+ {
+ mCurrentStatics[collisionAreaIndex][i].clean = false; // probably slower to check if there's
+ // even something pointed to than just
+ // to do this
+ }
+
+ BEGIN_PROFILE( "FindStaticPhysElems" );
+ GetIntersectManager()->FindStaticPhysElems(position, radius, staticPhysDSGList);
+ END_PROFILE( "FindStaticPhysElems" );
+
+ int numResults = staticPhysDSGList.mUseSize;
+
+ // !!!!
+ // temp
+ //
+ // TODO
+ // hmmm... what to do here?
+ // pick different max or try and prioritize further?
+ //
+ // also TODO - need different maxes for different lists
+
+ if(numResults > mMaxStatics)
+ {
+ numResults = mMaxStatics;
+ rDebugPrintf("\n!!! too many statics !!!\n");
+ }
+
+
+ for(i = 0; i < numResults; i++)
+ {
+ // go through results and submit to collision.
+
+ // first see if already in our list
+ StaticPhysDSG* curr = staticPhysDSGList[i];
+
+ int j;
+ bool alreadyIn = false;
+ for(j = 0; j < mMaxStatics; j++)
+ {
+ if(mCurrentStatics[collisionAreaIndex][j].mStaticPhysDSG == curr)
+ {
+ // this one already in list
+ mCurrentStatics[collisionAreaIndex][j].clean = true;
+ alreadyIn = true;
+ break;
+
+ }
+ }
+
+ if(!alreadyIn)
+ {
+ // add to our list and to collision manager
+ //
+ // look for first available slot
+ int k;
+ for(k = 0; k < mMaxStatics; k++)
+ {
+ if(mCurrentStatics[collisionAreaIndex][k].mStaticPhysDSG == 0)
+ {
+ // here is a slot
+ mCurrentStatics[collisionAreaIndex][k].mStaticPhysDSG = curr;
+ mCurrentStatics[collisionAreaIndex][k].mStaticPhysDSG->AddRef();
+ mCurrentStatics[collisionAreaIndex][k].clean = true;
+
+ sim::CollisionObject* currCollObj = curr->mpSimState()->GetCollisionObject();
+
+
+ BEGIN_PROFILE( "AddCollisionObject" );
+
+ if(1)//allowAutoPairing)
+ {
+ currCollObj->SetAutoPair(true);
+ }
+ else
+ {
+ currCollObj->SetAutoPair(false);
+ }
+
+ mCollisionManager->AddCollisionObject(currCollObj, collisionAreaIndex);
+
+ //if(!allowAutoPairing)
+ {
+ mCollisionManager->AddPair(currCollObj, callerSimState->GetCollisionObject(), collisionAreaIndex);
+ }
+
+
+
+
+ END_PROFILE( "AddCollisionObject" );
+ break;
+ }
+ }
+
+ rAssert(k < mMaxStatics);
+ // TODO - deal with no available slots....
+ }
+ }
+
+ // now look through remaining list and remove unclean
+ for(i = 0; i < mMaxStatics; i++)
+ {
+ if(mCurrentStatics[collisionAreaIndex][i].mStaticPhysDSG != 0 &&
+ mCurrentStatics[collisionAreaIndex][i].clean == false)
+ {
+ BEGIN_PROFILE( "RemoveCollisionObject" );
+ mCollisionManager->RemoveCollisionObject(mCurrentStatics[collisionAreaIndex][i].mStaticPhysDSG->mpSimState()->GetCollisionObject(), collisionAreaIndex);
+ END_PROFILE( "RemoveCollisionObject" );
+ mCurrentStatics[collisionAreaIndex][i].mStaticPhysDSG->Release();
+ mCurrentStatics[collisionAreaIndex][i].mStaticPhysDSG = 0;
+ mCurrentStatics[collisionAreaIndex][i].clean = false; // this is redundant
+ }
+ }
+ END_PROFILE( "SubmitStaticsPseudoCallback" );
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::SubmitAnimCollisionsPseudoCallback
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& position, float radius, int collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::SubmitAnimCollisionsPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ ReserveArray<AnimCollisionEntityDSG*> animCollisionEntityDSGList;
+
+ int i;
+ for(i = 0; i < mMaxAnimCollisions; i++)
+ {
+ mCurrentAnimCollisions[collisionAreaIndex][i].clean = false;
+ }
+
+ GetIntersectManager()->FindAnimPhysElems(position, radius, animCollisionEntityDSGList);
+
+ int numResults = animCollisionEntityDSGList.mUseSize;
+
+ if(numResults > mMaxAnimCollisions)
+ {
+ numResults = mMaxAnimCollisions;
+ rDebugPrintf("\n!!! too many animating collision objects - goddamn !!!\n");
+ }
+
+
+ for(i = 0; i < numResults; i++)
+ {
+ // go through results and submit to collision.
+
+ // first see if already in our list
+ AnimCollisionEntityDSG* curr = animCollisionEntityDSGList[i];
+
+ int j;
+ bool alreadyIn = false;
+ for(j = 0; j < mMaxAnimCollisions; j++)
+ {
+ if(mCurrentAnimCollisions[collisionAreaIndex][j].mAnimCollisionEntityDSG == curr)
+ {
+ // this one already in list
+ mCurrentAnimCollisions[collisionAreaIndex][j].clean = true;
+ alreadyIn = true;
+ break;
+
+ }
+ }
+
+ if(!alreadyIn)
+ {
+ // add to our list and to collision manager
+ //
+ // look for first available slot
+ int k;
+ for(k = 0; k < mMaxAnimCollisions; k++)
+ {
+ if(mCurrentAnimCollisions[collisionAreaIndex][k].mAnimCollisionEntityDSG == 0)
+ {
+ // here is a slot
+ mCurrentAnimCollisions[collisionAreaIndex][k].mAnimCollisionEntityDSG = curr;
+ mCurrentAnimCollisions[collisionAreaIndex][k].mAnimCollisionEntityDSG->AddRef( );
+ mCurrentAnimCollisions[collisionAreaIndex][k].clean = true;
+
+ sim::CollisionObject* currCollObj = curr->GetSimState()->GetCollisionObject();
+
+ currCollObj->SetAutoPair(false);
+ mCollisionManager->AddCollisionObject(currCollObj, collisionAreaIndex);
+ mCollisionManager->AddPair(currCollObj, callerSimState->GetCollisionObject(), collisionAreaIndex);
+
+ break;
+ }
+ }
+
+ rAssert(k < mMaxAnimCollisions);
+ // TODO - deal with no available slots....
+ }
+ }
+
+ // now look through remaining list and remove unclean
+ for(i = 0; i < mMaxAnimCollisions; i++)
+ {
+ if(mCurrentAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG != 0 &&
+ mCurrentAnimCollisions[collisionAreaIndex][i].clean == false)
+ {
+ mCollisionManager->RemoveCollisionObject(mCurrentAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG->GetSimState()->GetCollisionObject(), collisionAreaIndex);
+ mCurrentAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG->Release( );
+ mCurrentAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG = 0;
+ mCurrentAnimCollisions[collisionAreaIndex][i].clean = false; // this is redundant
+ }
+ }
+
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::SubmitAnimCollisionsForUpdateOnly
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& position, float radius, int collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::SubmitAnimCollisionsForUpdateOnly(rmt::Vector& position, float radius, int collisionAreaIndex)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ ReserveArray<AnimCollisionEntityDSG*> animCollisionEntityDSGList;
+
+ int i;
+ for(i = 0; i < mMaxUpdateAnimCollisions; i++)
+ {
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][i].clean = false;
+ }
+
+ GetIntersectManager()->FindAnimPhysElems(position, radius, animCollisionEntityDSGList);
+
+ int numResults = animCollisionEntityDSGList.mUseSize;
+
+ if(numResults > mMaxUpdateAnimCollisions)
+ {
+ numResults = mMaxUpdateAnimCollisions;
+ rDebugPrintf("\n!!! too many animating collision objects - goddamn !!!\n");
+ }
+
+
+ for(i = 0; i < numResults; i++)
+ {
+ // go through results and submit to collision.
+
+ // first see if already in our list
+ AnimCollisionEntityDSG* curr = animCollisionEntityDSGList[i];
+
+ int j;
+ bool alreadyIn = false;
+ for(j = 0; j < mMaxUpdateAnimCollisions; j++)
+ {
+ if(mCurrentUpdateAnimCollisions[collisionAreaIndex][j].mAnimCollisionEntityDSG == curr)
+ {
+ // this one already in list
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][j].clean = true;
+ alreadyIn = true;
+ break;
+
+ }
+ }
+
+ if(!alreadyIn)
+ {
+ // add to our list and to collision manager
+ //
+ // look for first available slot
+ int k;
+ for(k = 0; k < mMaxUpdateAnimCollisions; k++)
+ {
+ if(mCurrentUpdateAnimCollisions[collisionAreaIndex][k].mAnimCollisionEntityDSG == 0)
+ {
+ // here is a slot
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][k].mAnimCollisionEntityDSG = curr;
+ // Hang on to this object.
+ //
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][k].mAnimCollisionEntityDSG->AddRef( );
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][k].clean = true;
+
+ break;
+ }
+ }
+
+ rAssert(k < mMaxUpdateAnimCollisions);
+ // TODO - deal with no available slots....
+ }
+ }
+
+ // now look through remaining list and remove unclean
+ for(i = 0; i < mMaxUpdateAnimCollisions; i++)
+ {
+ if(mCurrentUpdateAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG != 0 &&
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][i].clean == false)
+ {
+ // Let this thing go.
+ //
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG->Release( );
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][i].mAnimCollisionEntityDSG = 0;
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][i].clean = false; // this is redundant
+ }
+ }
+}
+
+//=============================================================================
+// WorldPhysicsManager::SubmitDynamicsPseudoCallback
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (rmt::Vector& position, float radius, int collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::SubmitDynamicsPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState, bool allowAutoPairing)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ // hmmm...
+ // safe to make this an InstDynaPhysDSG list?? - doesn't compile
+ ReserveArray<DynaPhysDSG*> dynamicsPhysDSGList;
+
+ int i;
+ for(i = 0; i < mMaxDynamics; i++)
+ {
+ mCurrentDynamics[collisionAreaIndex][i].clean = false; // probably slower to check if there's
+ // even something pointed to than just
+ // to do this
+ }
+
+ // this is just commented out until it exists
+ GetIntersectManager()->FindDynaPhysElems(position, radius, dynamicsPhysDSGList);
+
+ int numResults = dynamicsPhysDSGList.mUseSize;
+
+ if(numResults > 1)
+ {
+ int stophere = 1;
+ }
+
+
+ // TODO
+ // hmmm... what to do here?
+ if(numResults > mMaxDynamics)
+ {
+ numResults = mMaxDynamics;
+ rDebugPrintf("\n!!! too many dynamics returned from query !!!\n");
+ }
+
+ for(i = 0; i < numResults; i++)
+ {
+ // go through results and submit to collision.
+
+ // first see if already in our list
+ DynaPhysDSG* curr = dynamicsPhysDSGList[i];
+
+ // temp
+ //
+ // this actually might be the way to do it?
+ //if(curr == caller)
+ if((curr->mpSimState() == callerSimState) || (curr->mpSimState() == NULL))
+ {
+ continue;
+ }
+
+ // new test
+ // just for Cary
+ if(callerSimState->mAIRefIndex == PhysicsAIRef::CameraSphere)
+ {
+ if( curr->mpSimState()->mAIRefIndex != PhysicsAIRef::redBrickVehicle &&
+ curr->mpSimState()->mAIRefIndex != PhysicsAIRef::NPCharacter &&
+ curr->mpSimState()->mAIRefIndex != PhysicsAIRef::PlayerCharacter )
+ {
+ continue;
+ }
+
+ }
+
+
+ // not optimization further below
+ //
+ // only gonna pair "submitters" with ourselves
+ //
+ // this logic here is to see if the current "submitter" is trying to pair itself against another "submitter" vehicle we already called earlier, that
+ // already set up this pair
+ if(callerSimState->mAIRefIndex == PhysicsAIRef::redBrickVehicle && curr->mpSimState()->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+
+ // April 22, 2003
+ // problem when cars that are calling this are very close to each other
+ //
+ // many duplicate pairs put in more than one collision area
+ //
+ // the fastest, most optimal check we can do to minimize this is:
+ // (first re-order the vehicle calls to this to make sure VT_USER called first)
+ // then, if we are a car, and return a car, and either it's a VT_USER or it's and AI with
+ // a lower vehicle central index than us, then skipt it - they've already submitted us, and autopaird all round!
+ Vehicle* submittingVehicle = (Vehicle*)(callerSimState->mAIRefPointer);
+ Vehicle* submittedVehicle = (Vehicle*)(curr->mpSimState()->mAIRefPointer);
+
+ rTuneAssert(submittingVehicle->mVehicleCentralIndex != -1);
+ rTuneAssert(submittedVehicle->mVehicleCentralIndex != -1);
+
+ if(submittingVehicle->mVehicleType == VT_USER)
+ {
+ if(submittedVehicle->mVehicleType == VT_USER)
+ {
+ if(submittingVehicle->mVehicleCentralIndex > submittedVehicle->mVehicleCentralIndex)
+ {
+ // both user vehicles, but...
+ // the submitted vehicle has been called first, so it wins
+ continue;
+ }
+ }
+ }
+ else
+ {
+ // not a user vehicle
+ if(submittedVehicle->mVehicleType == VT_USER)
+ {
+ // it has already added us
+ continue;
+ }
+ else if(submittingVehicle->mVehicleType == VT_AI)
+ {
+ if(submittingVehicle->mVehicleCentralIndex > submittedVehicle->mVehicleCentralIndex)
+ {
+ // the submitted vehicle has been called first, so it wins
+ continue;
+ }
+ }
+ }
+
+ }
+
+
+ int j;
+ bool alreadyIn = false;
+ for(j = 0; j < mMaxDynamics; j++)
+ {
+ if(mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG == curr)
+ {
+ // this one already in list
+ mCurrentDynamics[collisionAreaIndex][j].clean = true;
+ alreadyIn = true;
+ break;
+
+ }
+ }
+
+ if(!alreadyIn)
+ {
+ // add to our list and to collision manager
+ //
+ // look for first available slot
+ int k;
+ for(k = 0; k < mMaxDynamics; k++)
+ {
+ if(mCurrentDynamics[collisionAreaIndex][k].mDynamicPhysDSG == 0)
+ {
+ // here is a slot
+ mCurrentDynamics[collisionAreaIndex][k].mDynamicPhysDSG = curr;
+ mCurrentDynamics[collisionAreaIndex][k].mDynamicPhysDSG->AddRef( );
+ mCurrentDynamics[collisionAreaIndex][k].clean = true;
+
+ sim::CollisionObject* currCollObj = curr->mpSimState()->GetCollisionObject();
+
+ // final, simplest possible optimization
+ //
+ // don't autopair dynamics period
+ // any dynamic we submit only pairs with us!
+ //
+ // who knows what else this thing will do in it's own area (if it has one), so just in case turn
+ // turn flag back on after
+
+
+ // only pair with us
+ //
+
+ if(allowAutoPairing && curr->mpSimState()->mAIRefIndex != PhysicsAIRef::redBrickVehicle)
+ {
+ currCollObj->SetAutoPair(true);
+ }
+ else
+ {
+ currCollObj->SetAutoPair(false);
+ }
+
+ mCollisionManager->AddCollisionObject(currCollObj, collisionAreaIndex);
+
+ //if(!allowAutoPairing || curr->mpSimState()->mAIRefIndex == PhysicsAIRef::redBrickVehicle)
+ {
+ mCollisionManager->AddPair(currCollObj, callerSimState->GetCollisionObject(), collisionAreaIndex);
+ }
+
+
+
+ /*
+ // new??
+ // is this gonna work, and work well?
+
+ //!!
+ // only do this for the player character!
+
+
+ int l;
+ for(l = 0; l < this->mMaxStatics; l++)
+ {
+ if(mCurrentStatics[collisionAreaIndex][l].mStaticPhysDSG)
+ {
+ // there's one there...
+ // make sure we are paired with it
+ mCollisionManager->AddPair(currCollObj,
+ mCurrentStatics[collisionAreaIndex][l].mStaticPhysDSG->mpSimState()->GetCollisionObject(),
+ collisionAreaIndex);
+ }
+ }
+ for(l = 0; l < this->mMaxFencePerArea; l++)
+ {
+ if(mFences[collisionAreaIndex][l].mInCollision)
+ {
+ mCollisionManager->AddPair(currCollObj,
+ mFences[collisionAreaIndex][l].mFenceSimState->GetCollisionObject(),
+ collisionAreaIndex);
+
+ }
+
+ }
+ */
+
+
+ // all vehicles and player character have their own groundplanes already
+ if( curr->GetAIRef() != PhysicsAIRef::redBrickVehicle && curr->GetAIRef() != PhysicsAIRef::PlayerCharacter )
+ {
+ int groundPlaneIndex = mCurrentDynamics[collisionAreaIndex][k].mDynamicPhysDSG->FetchGroundPlane();
+
+ if(groundPlaneIndex != -1) // just in case, though this should never be -1
+ {
+ sim::CollisionObject* gpCollObj = mGroundPlanePool->GetSimState(groundPlaneIndex)->GetCollisionObject();
+ gpCollObj->SetRayCastingEnabled( false );
+
+
+
+ // recall, collision is disabled on the ground plane until it is set on by the object using it
+ mCollisionManager->AddCollisionObject( gpCollObj, collisionAreaIndex );
+
+ mCollisionManager->AddPair(currCollObj, gpCollObj, collisionAreaIndex);
+ }
+
+ }
+
+
+ break;
+ }
+ }
+
+ //rAssert(k < mMaxDynamics);
+ // TODO - deal with no available slots....
+ }
+ }
+
+ // now look through remaining list and remove unclean
+ //
+ // at this stage, we can only remove the ones that are unclean and at rest.
+
+ PurgeDynamics( collisionAreaIndex );
+
+}
+
+
+//=============================================================================
+// static bool IsBreakable
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( DynaPhysDSG* pObject )
+//
+// Return: static
+//
+//=============================================================================
+static bool IsBreakable( DynaPhysDSG* pObject )
+{
+ bool isBreakable;
+ CollisionAttributes* attr = pObject->GetCollisionAttributes();
+ if ( pObject->GetAIRef() == PhysicsAIRef::StateProp ||
+ pObject->GetAIRef() == PhysicsAIRef::ActorStateProp )
+ {
+ // *some* stateprops can break, but not ones flagged as PROP_ONE_TIME_MOVEABLE
+ if ( pObject->GetCollisionAttributes()->GetClasstypeid() == PROP_ONETIME_MOVEABLE )
+ isBreakable = false;
+ else
+ isBreakable = true;
+ }
+ else if ( attr != NULL )
+ {
+ if ( attr->GetClasstypeid() == PROP_BREAKABLE )
+ {
+ isBreakable = true;
+ }
+ else
+ {
+ isBreakable = false;
+ }
+ }
+ else
+ {
+ isBreakable = false;
+ }
+
+ return isBreakable;
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::PurgeDynamics
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ( int collisionAreaIndex )
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::PurgeDynamics( int collisionAreaIndex )
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ for(int i = 0; i < mMaxDynamics; i++)
+ {
+ // TODO !
+ // test for vehicles must be different;
+ // they use they're own ground plane!
+
+ if( mCurrentDynamics[collisionAreaIndex][i].mDynamicPhysDSG != 0 &&
+ mCurrentDynamics[collisionAreaIndex][i].clean == false )
+ {
+ DynaPhysDSG* pObject = mCurrentDynamics[collisionAreaIndex][i].mDynamicPhysDSG;
+
+ // need to keep updating when "at rest" if it's a onetime movable that has been hit
+ // so that it can be removed when it goes out of view
+ bool atRest = pObject->IsAtRest();
+
+ if(atRest && pObject->GetCollisionAttributes())
+ {
+ atRest = atRest && !((pObject->GetCollisionAttributes()->GetClasstypeid() == PROP_ONETIME_MOVEABLE)
+ && pObject->mIsHit);
+ }
+
+ if ( atRest || IsBreakable( pObject ) )
+ {
+ mCollisionManager->RemoveCollisionObject(pObject->mpSimState()->GetCollisionObject(), collisionAreaIndex);
+
+ // don't do fuck if we are a car - temp TODO!!
+ // this test doesn't seem to work
+ if(pObject->GetAIRef() != PhysicsAIRef::redBrickVehicle)
+ {
+ int groundPlaneIndex = pObject->GetGroundPlaneIndex();
+ if(groundPlaneIndex != -1) // should never be false...
+ {
+ sim::CollisionObject* gpCollObj = mGroundPlanePool->GetSimState(groundPlaneIndex)->GetCollisionObject();
+
+ mCollisionManager->RemoveCollisionObject(gpCollObj, collisionAreaIndex);
+
+ pObject->FreeGroundPlane(); // this decrements the groundplaneref, but we still need to take it out of
+ // there are no more refs
+ }
+ }
+ pObject->Release( );
+ mCurrentDynamics[collisionAreaIndex][i].mDynamicPhysDSG = 0;
+ mCurrentDynamics[collisionAreaIndex][i].clean = false; // this is redundant
+ }
+ }
+ }
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::UpdateSimluatingDynamicObjectGroundPlanes
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::UpdateSimluatingDynamicObjectGroundPlanes()
+{
+ int i;
+ for(i = 0; i < mNumCollisionAreas; i++)
+ {
+ if(!mCollisionAreaAllocated[i])
+ {
+ continue;
+ }
+
+
+ int j;
+ for(j = 0; j < mMaxDynamics; j++)
+ {
+ if( mCurrentDynamics[i][j].mDynamicPhysDSG &&
+ mCurrentDynamics[i][j].mDynamicPhysDSG->GetSimState()->GetControl() == sim::simSimulationCtrl &&
+ mCurrentDynamics[i][j].mDynamicPhysDSG->GetSimState()->mAIRefIndex != PhysicsAIRef::redBrickVehicle &&
+ mCurrentDynamics[i][j].mDynamicPhysDSG->GetSimState()->mAIRefIndex != PhysicsAIRef::PlayerCharacter ) // TODO - vehicles use also?
+
+ // TODO - rest test??? or is that covered under sim control?
+
+ {
+ // need to update this ground plane
+ // TODO
+ // is the position of the DSG object being updated correctly???????
+ //
+ // no implementatino of that for dynamics yet!
+
+ rmt::Vector inPosition = mCurrentDynamics[i][j].mDynamicPhysDSG->rPosition();
+ float inRadius = 1.0f;
+
+ // ignore the tri values
+ rmt::Vector triNormal;
+ rmt::Vector triPosition;
+
+ bool foundPlane;
+ rmt::Vector planeNormal;
+ rmt::Vector planePosition;
+
+
+ GetIntersectManager()->FindIntersection(inPosition,
+ foundPlane,
+ planeNormal,
+ planePosition);
+
+
+ if(foundPlane)
+ {
+ mGroundPlanePool->UpdateGroundPlane(mCurrentDynamics[i][j].mDynamicPhysDSG->GetGroundPlaneIndex(), planePosition, planeNormal);
+ }
+ else
+ {
+ //rAssert(0);
+ int stophere = 1; // hopefully not.
+ }
+
+ }
+
+ }
+ }
+
+
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::GetNewGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: ()
+//
+// Return: int
+//
+//=============================================================================
+int WorldPhysicsManager::GetNewGroundPlane(sim::SimState* simStateOwner)
+{
+ return mGroundPlanePool->GetNewGroundPlane(simStateOwner);
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::FreeGroundPlane
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::FreeGroundPlane(int index)
+{
+ mGroundPlanePool->FreeGroundPlane(index);
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::EnableGroundPlaneCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::EnableGroundPlaneCollision(int index)
+{
+ //char buffy[128];
+ //sprintf(buffy, "enabling ground plane index %d\n", index);
+
+ //rReleaseString(buffy);
+
+ mGroundPlanePool->EnableCollision(index);
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::DisableGroundPlaneCollision
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (int index)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::DisableGroundPlaneCollision(int index)
+{
+ //char buffy[128];
+ //sprintf(buffy, "disabling ground plane index %d\n", index);
+
+ //rReleaseString(buffy);
+
+ mGroundPlanePool->DisableCollision(index);
+}
+
+//=============================================================================
+// WorldPhysicsManager::UpdateDynamicObjects
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt, int collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::UpdateDynamicObjects(float dt, int collisionAreaIndex)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+ rAssert( mCollisionAreaAllocated[collisionAreaIndex] );
+
+ int j;
+ for(j = 0; j < mMaxDynamics; j++)
+ {
+ if(mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG)
+ {
+ mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->RestTest();
+
+ bool atRest = mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->IsAtRest();
+
+ // need to keep updating when "at rest" if it's a onetime movable that has been hit
+ if(atRest && mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->GetCollisionAttributes())
+ {
+ atRest = atRest && !((mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->GetCollisionAttributes()->GetClasstypeid() == PROP_ONETIME_MOVEABLE)
+ && mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->mIsHit);
+ }
+
+ // don't update vehicles or characters (they are updated elsewhere),
+ // stuff at rest, or stuff that has already been updated.
+ if((mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->GetSimState()->mAIRefIndex != PhysicsAIRef::redBrickVehicle) &&
+ (mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->GetSimState()->mAIRefIndex != PhysicsAIRef::PlayerCharacter ) &&
+ (!atRest) &&
+ (mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->GetLastUpdate() != updateFrame))
+ {
+ // set the update tick so we wont update this again
+ mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->SetLastUpdate(updateFrame);
+ mCurrentDynamics[collisionAreaIndex][j].mDynamicPhysDSG->Update(dt);
+ }
+ }
+ }
+
+}
+
+//=============================================================================
+// WorldPhysicsManager::UpdateAnimCollisions
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (float dt, int collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::UpdateAnimCollisions(float dt, int collisionAreaIndex)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+ rAssert(mCollisionAreaAllocated[collisionAreaIndex]);
+
+ int j;
+ for(j = 0; j < mMaxUpdateAnimCollisions; j++)
+ {
+ if(mCurrentUpdateAnimCollisions[collisionAreaIndex][j].mAnimCollisionEntityDSG)
+ {
+ if(mCurrentUpdateAnimCollisions[collisionAreaIndex][j].mAnimCollisionEntityDSG->GetLastUpdate() != updateFrame)
+ {
+ // set the update tick so we wont update this again
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][j].mAnimCollisionEntityDSG->SetLastUpdate(updateFrame);
+ mCurrentUpdateAnimCollisions[collisionAreaIndex][j].mAnimCollisionEntityDSG->Update(dt);
+ }
+ }
+
+ }
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::SubmitFencePiecesPseudoCallback
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (position, radius, collisionAreaIndex)
+//
+// Return: void
+//
+//=============================================================================
+void WorldPhysicsManager::SubmitFencePiecesPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState, bool allowAutoPairing)
+{
+ rAssert( collisionAreaIndex > WorldPhysicsManager::INVALID_COLLISION_AREA );
+
+ ReserveArray<FenceEntityDSG*> fenceDSGList;
+
+ BEGIN_PROFILE( "FindFenceElems" );
+ GetIntersectManager()->FindFenceElems(position, radius, fenceDSGList);
+ END_PROFILE( "FindFenceElems" );
+
+ int numResults = fenceDSGList.mUseSize;
+
+ //int numResults = 1;
+ if(numResults > 0)
+ {
+ int stophere = 1;
+ }
+
+ if(numResults > mMaxFencePerArea)
+ {
+ numResults = mMaxFencePerArea;
+ }
+
+ // fill in debug rendering list:
+ int i;
+
+ for(i = 0; i < numResults; i++)
+ {
+ mFenceDSGResults[i]->mStartPoint = fenceDSGList[i]->mStartPoint;
+ mFenceDSGResults[i]->mEndPoint = fenceDSGList[i]->mEndPoint;
+ mFenceDSGResults[i]->mNormal = fenceDSGList[i]->mNormal;
+
+ mFenceDSGResults[i]->mStartPoint.y = position.y;
+ mFenceDSGResults[i]->mEndPoint.y = position.y;
+
+
+ }
+ mNumDebugFences = numResults;
+
+ // reset list
+ for(i = 0; i < mMaxFencePerArea; i++)
+ {
+ mFences[collisionAreaIndex][i].mClean = false;
+ }
+
+ for(i = 0; i < numResults; i++)
+ {
+
+ // i is candidate for submission
+
+ // just in case, do this in function, not here
+ //fenceDSGList[i]->mStartPoint.y = position.y;
+ //fenceDSGList[i]->mEndPoint.y = position.y;
+
+ mFences[collisionAreaIndex][i].start = fenceDSGList[i]->mStartPoint;
+ mFences[collisionAreaIndex][i].end = fenceDSGList[i]->mEndPoint;
+
+ if(UpdateFencePiece(position, mFences[collisionAreaIndex][i].mFenceSimState,
+ fenceDSGList[i]->mStartPoint, fenceDSGList[i]->mEndPoint,
+ fenceDSGList[i]->mNormal,
+ callerSimState))
+ {
+ // ok for this one to be in collision
+ //
+
+ if(mFences[collisionAreaIndex][i].mInCollision == false)
+ {
+ // we need to add this into the collision manager
+ if(allowAutoPairing)
+ {
+ mFences[collisionAreaIndex][i].mFenceSimState->GetCollisionObject()->SetAutoPair(true);
+ }
+ else
+ {
+ mFences[collisionAreaIndex][i].mFenceSimState->GetCollisionObject()->SetAutoPair(false);
+ }
+
+ mCollisionManager->AddCollisionObject(mFences[collisionAreaIndex][i].mFenceSimState->GetCollisionObject(), collisionAreaIndex);
+ mCollisionManager->AddPair(callerSimState->GetCollisionObject(), mFences[collisionAreaIndex][i].mFenceSimState->GetCollisionObject(), collisionAreaIndex);
+ mFences[collisionAreaIndex][i].mInCollision = true;
+
+ }
+ mFences[collisionAreaIndex][i].mClean = true;
+
+ }
+ }
+
+ // update list
+ for(i = 0; i < mMaxFencePerArea; i++)
+ {
+ if(mFences[collisionAreaIndex][i].mInCollision == true &&
+ mFences[collisionAreaIndex][i].mClean == false)
+ {
+ mCollisionManager->RemoveCollisionObject(mFences[collisionAreaIndex][i].mFenceSimState->GetCollisionObject(), collisionAreaIndex);
+ mFences[collisionAreaIndex][i].mInCollision = false;
+ }
+
+
+ }
+
+}
+
+
+//=============================================================================
+// WorldPhysicsManager::UpdateFencePiece
+//=============================================================================
+// Description: Comment
+//
+// Parameters: (SimState* fencePiece, rmt::Vector* end0, rmt::Vector* end1, rmt::Vector* normal)
+//
+// Return: void
+//
+//=============================================================================
+bool WorldPhysicsManager::UpdateFencePiece(rmt::Vector& callerPosition, sim::ManualSimState* fencePiece, rmt::Vector& end0,
+ rmt::Vector& end1, rmt::Vector& normal, sim::SimState* callerSimState)
+{
+
+ // on the collision volume
+ // set position,
+ // mAxis
+ // mLength
+ rmt::Vector o0, o1, o2;
+ float l0, l1, l2;
+
+ // pain in the ass - just get it to fucking work
+ rmt::Vector localEnd0 = end0;
+ rmt::Vector localEnd1 = end1;
+
+ localEnd0.y = callerPosition.y;
+ localEnd1.y = callerPosition.y;
+
+ o1.Set(0.0f, 1.0f, 0.0f); // y is always straight up
+
+ o2 = normal; // safe to assume this is normalized?
+
+ o0.CrossProduct(o1, o2);
+
+ //l1 = 10000.0f; // magic big ass number
+ l1 = 10.0f; // use a smaller number for now so the debug drawn volumes are easier to see
+
+ rmt::Vector segment;
+ segment.Sub(localEnd1, localEnd0);
+
+ float segmentLength = segment.Magnitude();
+
+ l0 = segmentLength * 0.5f;
+
+
+ l2 = 10.0f; // ? whatever? TODO - is this value ok?
+
+ if(callerSimState->mAIRefIndex == PhysicsAIRef::CameraSphere)
+ {
+ // Cary!
+ // change this number!
+
+ // this is half the length of the fence piece obbox
+
+ l2 = 2.0f;
+ //l2 = 10.0f;
+ }
+
+ rmt::Vector midpoint;
+ midpoint.Add(localEnd0, localEnd1);
+ midpoint.Scale(0.5f);
+
+
+ // before doing anything else, test if we are pointed at the fucker
+ // TODO - maybe this test should be moved up to the submit part?
+ // maybe not
+ rmt::Vector test;
+ test.Sub(callerPosition, midpoint);
+
+ if(test.DotProduct(normal) < 0.0f && callerSimState->mAIRefIndex != PhysicsAIRef::CameraSphere)
+ {
+
+ fencePiece->GetCollisionObject()->SetCollisionEnabled(false);
+ return false;
+ }
+
+ rmt::Vector position = midpoint;
+ rmt::Vector centerAdjust = o2;
+ centerAdjust.Scale(l2);
+ position.Sub(centerAdjust);
+
+
+ // for convenience:
+ sim::CollisionObject* co = fencePiece->GetCollisionObject();
+ sim::OBBoxVolume* obbox = (OBBoxVolume*)(co->GetCollisionVolume());
+
+ obbox->Set(position, o0, o1, o2, l0, l1, l2);
+
+ co->PostManualUpdate();
+
+ //co->Relocated();
+ //obbox->UpdateBBox();
+
+ // only need to do this once
+ co->SetCollisionEnabled(true);
+
+
+ // on the Object call Relocated
+ //
+ //virtual void UpdateBBox() {}
+ // make sure mUpdated is true
+ // what about dP??
+ //
+ // what about OptimizeAxis
+ // what about SetRotation?
+
+ // GetCollisionObject()->SetCollisionEnabled(true);
+
+
+ //#ifdef RAD_DEBUG
+
+ //sim::DrawCollisionObject(CollisionObject* inObject);
+ //sim::DrawCollisionVolume(obbox);
+
+ //#endif
+
+ return true;
+
+
+}
+
+//----------------------------------------------------------------------------
+// Segment intersection, stolen, as usual, from www.magic-software.com
+//----------------------------------------------------------------------------
+
+// even though we're using 3d vectors (cause that's what we have) this is only a 2d intersection
+// in x & z
+static bool Find (const rmt::Vector& rkP0, const rmt::Vector& rkD0,
+ const rmt::Vector& rkP1, const rmt::Vector& rkD1, rmt::Vector& rkDiff,
+ float& rfD0SqrLen, int& riQuantity, float afT[2])
+{
+ // Intersection is a solution to P0+s*D0 = P1+t*D1. Rewrite as
+ // s*D0 - t*D1 = P1 - P0, a 2x2 system of equations. If D0 = (x0,y0)
+ // and D1 = (x1,y1) and P1 - P0 = (c0,c1), then the system is
+ // x0*s - x1*t = c0 and y0*s - y1*t = c1. The error tests are relative
+ // to the size of the direction vectors, |Cross(D0,D1)| >= e*|D0|*|D1|
+ // rather than absolute tests |Cross(D0,D1)| >= e. The quantities
+ // P1-P0, |D0|^2, and |D1|^2 are returned for use by calling functions.
+
+ float fDet = rkD1.x*rkD0.z - rkD1.z*rkD0.x;
+ rkDiff = rkP1 - rkP0;
+ rfD0SqrLen = rkD0.MagnitudeSqr();
+
+ const float fEpsilon = 1e-06f;
+ if ( fDet*fDet > fEpsilon*rfD0SqrLen*rkD1.MagnitudeSqr() )
+ {
+ // Lines intersect in a single point. Return both s and t values for
+ // use by calling functions.
+ float fInvDet = 1.0f/fDet;
+ riQuantity = 1;
+ afT[0] = (rkD1.x*rkDiff.z - rkD1.z*rkDiff.x)*fInvDet;
+ afT[1] = (rkD0.x*rkDiff.z - rkD0.z*rkDiff.x)*fInvDet;
+ }
+ else
+ {
+ // lines are parallel
+ fDet = rkD0.x*rkDiff.z - rkD0.z*rkDiff.x;
+ if ( fDet*fDet > fEpsilon*rfD0SqrLen*rkDiff.MagnitudeSqr() )
+ {
+ // lines are disjoint
+ riQuantity = 0;
+ }
+ else
+ {
+ // lines are the same
+ riQuantity = 2;
+ }
+ }
+
+ return riQuantity != 0;
+}
+
+//----------------------------------------------------------------------------
+static bool FindIntersection (const rmt::Vector& origin0, const rmt::Vector& direction0,
+ const rmt::Vector& origin1, const rmt::Vector& direction1,
+ int& riQuantity)
+{
+ float afT[2];
+
+ rmt::Vector kDiff;
+ float fD0SqrLen;
+ bool bIntersects = Find(origin0, direction0,
+ origin1,direction1,kDiff,fD0SqrLen,
+ riQuantity,afT);
+
+ if ( bIntersects )
+ {
+ if ( riQuantity == 1 )
+ {
+ if ( afT[0] < 0.0f || afT[0] > 1.0f
+ || afT[1] < 0.0f || afT[1] > 1.0f )
+ {
+ // lines intersect, but segments do not
+ riQuantity = 0;
+ }
+ }
+ else
+ {
+ // segments are on the same line
+ float fDotRS = direction0.Dot(direction1);
+ float fDot0, fDot1;
+ if ( fDotRS > 0.0f )
+
+ {
+ fDot0 = kDiff.Dot(direction0);
+ fDot1 = fDot0 + fDotRS;
+ }
+ else
+ {
+ fDot1 = kDiff.Dot(direction0);
+ fDot0 = fDot1 + fDotRS;
+ }
+
+ // compute intersection of [t0,t1] and [0,1]
+ if ( fDot1 < 0.0f || fDot0 > fD0SqrLen )
+ {
+ riQuantity = 0;
+ }
+ else if ( fDot1 > 0.0f )
+ {
+ if ( fDot0 < fD0SqrLen )
+ {
+ float fInvLen = 1.0f/fD0SqrLen;
+ riQuantity = 2;
+ afT[0] = ( fDot0 < 0.0f ? 0.0f : fDot0*fInvLen );
+ afT[1] = ( fDot1 > fD0SqrLen ? 1.0f : fDot1*fInvLen );
+ }
+ else // fT0 == 1
+ {
+ riQuantity = 1;
+ afT[0] = 1.0f;
+ }
+ }
+ else // fT1 == 0
+ {
+ riQuantity = 1;
+ afT[0] = 0.0f;
+ }
+ }
+ }
+
+ return riQuantity != 0;
+}
+
+bool WorldPhysicsManager::FenceSanityCheck(unsigned collisionAreaIndex, const rmt::Vector lastFrame, const rmt::Vector thisFrame, rmt::Vector* fixPos)
+{
+ rmt::Vector dir, dirTweak;
+ dir.Sub(thisFrame, lastFrame);
+
+ if(dir.MagnitudeSqr() == 0.0f)
+ {
+ return false;
+ }
+
+ dirTweak = dir;
+ dirTweak.Normalize();
+ dirTweak.Scale(0.07f);
+ dir += dirTweak;
+
+ for(int i = 0; i < mMaxFencePerArea; i++)
+ {
+ if(mFences[collisionAreaIndex][i].mClean)
+ {
+ rmt::Vector fenceDir, fenceTweak, fenceStart;
+ fenceDir.Sub(mFences[collisionAreaIndex][i].end, mFences[collisionAreaIndex][i].start);
+
+ fenceTweak = fenceDir;
+ fenceTweak.Normalize();
+ fenceTweak.Scale(0.07f);
+
+ fenceDir += fenceTweak;
+ fenceDir += fenceTweak;
+
+ fenceStart = mFences[collisionAreaIndex][i].start;
+ fenceStart -= fenceTweak;
+
+ int quantity;
+
+ if(FindIntersection(lastFrame, dir, fenceStart, fenceDir, quantity))
+ {
+ if(quantity == 1)
+ {
+ *fixPos = lastFrame;
+ fixPos->y = thisFrame.y;
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+float WorldPhysicsManager::GetLoopTime()
+{
+ return mLoopTime;
+}
diff --git a/game/code/worldsim/worldphysicsmanager.h b/game/code/worldsim/worldphysicsmanager.h
new file mode 100644
index 0000000..e8509ce
--- /dev/null
+++ b/game/code/worldsim/worldphysicsmanager.h
@@ -0,0 +1,296 @@
+//=============================================================================
+// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
+//
+// File: worldphysicsmanager.h
+//
+// Description: manage the world physics
+//
+// History: May 1, 2002 - created, gmayer
+//
+//=============================================================================
+
+#ifndef WORLDPHYSICSMANAGER_H
+#define WORLDPHYSICSMANAGER_H
+
+//========================================
+// Nested Includes
+//========================================
+
+//========================================
+// Forward References
+//========================================
+class AnimCollisionEntityDSG;
+class CollisionEntityDSG;
+class DynaPhysDSG;
+class WorldCollisionSolverAgentManager;
+class StaticPhysDSG;
+class GroundPlanePool;
+class FenceEntityDSG;
+class Vehicle;
+namespace sim
+{
+ class CollisionManager;
+ class ManualSimState;
+ class SimEnvironment;
+ class SimState;
+ class PhysicsProperties;
+}
+
+//=============================================================================
+//
+// Synopsis: this is the key tie-in point to the gameplay context to
+// run the simulation of the game world
+//
+//=============================================================================
+class WorldPhysicsManager
+{
+ public:
+
+ // Structure thats filled out upon call to ApplyForceToDynamics
+ // indicating the counts for the various types of objects that got hit
+ struct NumObjectsHit
+ {
+ NumObjectsHit() : numNPCsHit(0) {}
+ int numNPCsHit;
+ };
+
+ struct CollisionEntityDSGList
+ {
+ static const int NUM_COLLISION_LIST_ENTITIES = 3;
+
+ CollisionEntityDSG* collisionEntity[NUM_COLLISION_LIST_ENTITIES];
+ };
+
+ // Static Methods for accessing this singleton.
+ static WorldPhysicsManager* GetInstance();
+ static WorldPhysicsManager* CreateInstance();
+ static void DestroyInstance();
+
+ // Important!
+ // this method must be called before we load _anything_!
+ //static void SetSimUnits();
+
+ void Init(); // TODO - need this?
+
+ void Update(unsigned int timeDeltaMilliSeconds);
+
+ // kind of a hack?
+ void SuspendForInterior();
+ void ResumeForOutside();
+
+ void OnQuitLevel();
+
+ sim::SimEnvironment* mSimEnvironment;
+ float mCollisionDistanceCGS;
+ rmt::Vector mWorldUp;
+
+ sim::CollisionManager* mCollisionManager;
+
+ WorldCollisionSolverAgentManager* mpWorldCollisionSolverAgentManager;
+
+ int GetCameraCollisionAreaIndex(); // return the next free one
+ int GetVehicleCollisionAreaIndex();
+ int GetCharacterCollisionAreaIndex();
+
+ void FreeCollisionAreaIndex(int index);
+ void FreeAllCollisionAreaIndicies();
+ void EmptyCollisionAreaIndex(int index);
+ static const int INVALID_COLLISION_AREA = -1;
+ void SubmitStaticsPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState, bool allowAutoPairing = false);
+ void SubmitFencePiecesPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState, bool allowAutoPairing = false);
+ //void SubmitFencePiecesPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex);
+ void SubmitDynamicsPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* caller, bool allowAutoPairing = false);
+
+ //bool WorldPhysicsManager::IsASubmitter(sim::SimState* testSimState);
+
+ void SubmitAnimCollisionsPseudoCallback(rmt::Vector& position, float radius, int collisionAreaIndex, sim::SimState* callerSimState);
+
+ void SubmitAnimCollisionsForUpdateOnly(rmt::Vector& position, float radius, int collisionAreaIndex);
+
+ void UpdateAnimCollisions(float dt, int collisionAreaIndex);
+ void UpdateDynamicObjects(float dt, int collisionAreaIndex);
+
+
+ void DisplayFencesInArea(int area);
+
+ // need to do this when we remove a vehicle from an active collision area index...
+ void RemoveFromAnyOtherCurrentDynamicsListAndCollisionArea(DynaPhysDSG* vehicle);
+
+ // allow access to groundplanepool
+ int GetNewGroundPlane(sim::SimState* simStateOwner); // user refers to it with the returned index
+ void FreeGroundPlane(int index);
+
+ // add pair to collision
+ // called by object
+ //void SubmitGroundPlaneToCollisionManager(sim::SimState* simStateToPair, int groundPlaneIndex);
+ //void RemoveGroundPlaneFromCollisionManager(int groundPlaneIndex);
+
+ void EnableGroundPlaneCollision(int index);
+ void DisableGroundPlaneCollision(int index);
+
+ void StartTimer();
+ void StopTimer();
+ void ResetTimer();
+
+ void ToggleTimerState();
+
+ // Apply the given force to nearby objects. Returns the number of objects affected
+ int ApplyForceToDynamics( int collisionAreaIndex,
+ const rmt::Vector& position,
+ float radius,
+ const rmt::Vector& direction,
+ float force,
+ WorldPhysicsManager::NumObjectsHit* pObjectsHit = NULL,
+ CollisionEntityDSGList* pCollisionEntityList = NULL );
+
+ int ApplyForceToDynamicsSpherical( int collisionAreaIndex,
+ const rmt::Vector& position,
+ float radius,
+ float force,
+ CollisionEntityDSGList* pCollisionEntityList = NULL );
+ float GetLoopTime();
+
+ //--------
+ // statics
+ //--------
+ struct StaticsInCollisionDetection
+ {
+ StaticPhysDSG* mStaticPhysDSG;
+ bool clean;
+ };
+ int mMaxStatics; // per area
+
+ // for each collision area, need the 'clean' list
+ StaticsInCollisionDetection** mCurrentStatics;
+
+ void PurgeDynamics( int collisionAreaIndex );
+
+ bool FenceSanityCheck(unsigned collisionAreaIndex, const rmt::Vector lastFrame, const rmt::Vector thisFrame, rmt::Vector* fixPos);
+
+ private:
+
+ // No public access to these, use singleton interface.
+ WorldPhysicsManager();
+ ~WorldPhysicsManager();
+
+ void InitCollisionManager();
+
+
+ void WorldSimSubstepGuts(float dt, bool firstSubstep);
+
+ // pointer to the single instance
+ static WorldPhysicsManager* spInstance;
+
+ float mTotalTime;
+ float mTimerTime;
+ bool mTimerOn;
+
+
+ void DebugInfoDisplay();
+
+ //----------------------
+ // anim collision entity
+ //----------------------
+
+ // these are for collision detection only
+ struct AnimCollisionInCollisionDetection
+ {
+ AnimCollisionEntityDSG* mAnimCollisionEntityDSG;
+ bool clean;
+ };
+ int mMaxAnimCollisions;
+
+ AnimCollisionInCollisionDetection** mCurrentAnimCollisions;
+
+ // these ones are for update only - we can and want to update stuff that is too far away to collide with, for appearance sake
+ AnimCollisionInCollisionDetection** mCurrentUpdateAnimCollisions;
+ int mMaxUpdateAnimCollisions;
+
+ //---------
+ // dynamics
+ //---------
+ struct DynamicsInCollisionDetection
+ {
+ //InstDynaPhysDSG* mDynamicPhysDSG;
+ DynaPhysDSG* mDynamicPhysDSG;
+ bool clean;
+ //int groundPlaneIndex; have to hold this in the InstDynaPhysDSG class since object could be in more than one update list
+ };
+ int mMaxDynamics; // per area
+
+ // the 'clean' list
+ //
+ DynamicsInCollisionDetection** mCurrentDynamics;
+
+ // this list will hold all dynamics that should be tested for collision against.
+ // some will be under ai ctrl, and one's that have been collided with will be under
+ // simulation ctrl
+ //
+ // only the simulation ctrl ones need a physics update and ground plane and a rest test
+ //
+ // for now... don't remove from the list until object has come to rest
+
+ GroundPlanePool* mGroundPlanePool;
+ void UpdateSimluatingDynamicObjectGroundPlanes();
+
+ bool* mCollisionAreaAllocated; // has been requested for use by a vehicle, character, or camera
+ bool* mCollisionAreaActive; // for intermediate, temporary disabling
+ int mNumCollisionAreas;
+
+
+ // for debugging?
+ int mReservedCollisionAreas; // reserve 0...mReservedCollisionAreas - 1
+
+ int mMaxVehicles;
+ int mMaxChars;
+ int mMaxCameras;
+
+ //-------------
+ // fence pieces
+ //-------------
+
+ sim::PhysicsProperties* mFencePhysicsProperties;
+
+ // have a pool of these
+
+ struct FencePieces
+ {
+ sim::ManualSimState* mFenceSimState;
+ bool mInCollision;
+ bool mClean;
+ rmt::Vector start, end;
+ };
+
+ FencePieces** mFences;
+
+ // debug drawing:
+ // note this only really works for one thing calling submit fence pieces!
+ int mNumDebugFences;
+ FenceEntityDSG** mFenceDSGResults;
+
+
+ //sim::ManualSimState*** mFenceSimStates; // a 2D array of pointers
+
+ int mMaxFencePerArea;
+ int* mFencesInEachArea; // ? still need this
+
+ bool UpdateFencePiece(rmt::Vector& callerPosition, sim::ManualSimState* fencePiece, rmt::Vector& end0, rmt::Vector& end1, rmt::Vector& normal, sim::SimState* callerSimState);
+
+
+ // ? bit of a hack here for player character going into interiors
+ bool mInInterior;
+
+ // current physics "frame" (for detecting duplicate updating
+ // when an object is in multip[le lists
+ unsigned updateFrame;
+
+ float mLoopTime;
+ unsigned mLastTime;
+};
+
+// A little syntactic sugar for getting at this singleton.
+inline WorldPhysicsManager* GetWorldPhysicsManager() { return( WorldPhysicsManager::GetInstance() ); }
+
+
+
+#endif //WORLDPHYSICSMANAGER_H